productive-eslint 3.3.2 → 4.0.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/AGENTS.md ADDED
@@ -0,0 +1,35 @@
1
+ ## Project overview
2
+ `productive-eslint` — opinionated ESLint flat config preset (npm package).
3
+ Inspired by eslint-config-hardcore. Targets ESLint 10+, TypeScript 5.9+, Node 24+.
4
+
5
+ ## Tech stack
6
+ - Language: TypeScript (strict, `exactOptionalPropertyTypes`)
7
+ - Package manager: pnpm
8
+ - Bundler: tsdown
9
+ - Module system: ESM (`"type": "module"`)
10
+ - Entry point: `src/index.config.ts` → `dist/index.config.js`
11
+ - Supported consumer setup: TypeScript-only ESM codebases with `eslint.config.ts` or `eslint.config.mts`
12
+
13
+ ## Project structure
14
+ - `src/index.config.ts` — main factory `createConfig(options)`, composes all rule configs
15
+ - `src/*.config.ts` — per-plugin rule configs (javascript, typescript, unicorn, vue, etc.)
16
+ - `src/utils/presets.ts` — `Preset` enum (`autoFixable`/`recommended`) and `mergePresetConfigs`
17
+ - `src/utils/globs.ts` — file glob constants
18
+ - `src/plugins/productive.plugin.ts` — custom ESLint plugin with custom rules
19
+ - `scripts/` — utility scripts (dump/compare ESLint rules), run via `jiti`
20
+
21
+ ## Key patterns
22
+ - Each rule config file exports a `TPresetMap` — an object with `autoFixable` and `recommended` keys
23
+ - `mergePresetConfigs(map, preset)` merges `autoFixable` and, when requested, `recommended`
24
+ - Vue and RxJS support is auto-detected (via `local-pkg`) or explicitly enabled via options
25
+ - The config uses `FlatConfigComposer` from `eslint-flat-config-utils` for composability
26
+
27
+ ## Scripts
28
+ - `pnpm lint` — typecheck (`tsc`) + lint with autofix (`eslint --fix`)
29
+ - `pnpm build` — build with tsdown (output to `dist/`)
30
+ - `pnpm release` — build + publish
31
+ - `pnpm inspect` — open ESLint config inspector
32
+ - `pnpm rules:dump` / `pnpm rules:compare` — utility scripts for rule management
33
+
34
+ ## Linting
35
+ Repository-wide lint rules are intended to stay mechanical. Nuanced checks should move to focused on-demand diagnostics.
package/README.md CHANGED
@@ -1,9 +1,17 @@
1
- # A ESLint config for practical code analysis
2
- This is a config preset aimed at providing a fast and efficient way to distribute our team's desired ESLint configuration.
1
+ # An ESLint config for practical code analysis
2
+ This package provides an AI-friendly ESLint flat config for repository-wide mechanical checks.
3
3
  It is heavily inspired by Evgeny Orekhov's [eslint-config-hardcore](https://github.com/EvgenyOrekhov/eslint-config-hardcore).
4
4
 
5
- This config is extremely opinionated, so if certain sets of rules don't suit you - you are free to extend and override any
6
- given rules.
5
+ The default preset is intentionally limited to rules that are safe as permanent lint noise: autofixable rules plus trivial
6
+ non-autofixable checks that do not require architecture, API, or product decisions.
7
+
8
+ `productive-eslint` targets modern TypeScript-only ESM codebases. Project configs must use `eslint.config.ts` or `eslint.config.mts`.
9
+
10
+ Documentation:
11
+
12
+ - [Configuration Model](./docs/configuration.md)
13
+ - [CLI Diagnostics](./docs/cli-diagnostics.md)
14
+ - [Analyzer Runtime](./docs/analyzer-runtime.md)
7
15
 
8
16
  ---
9
17
 
@@ -22,11 +30,11 @@ npm i -D productive-eslint eslint typescript prettier prettier-plugin-jsdoc
22
30
  - TypeScript 5.9+ (required)
23
31
  - Prettier 3.6+
24
32
 
25
- 2. Create eslint.config.ts in project root:
33
+ 2. Create `eslint.config.ts` or `eslint.config.mts` in project root:
26
34
  ````typescript
27
- import productiveEslint from 'productive-eslint'
35
+ import { createConfig } from 'productive-eslint'
28
36
 
29
- export default productiveEslint()
37
+ export default createConfig()
30
38
  ````
31
39
 
32
40
  3. Add scripts to package.json:
@@ -41,17 +49,39 @@ npm i -D productive-eslint eslint typescript prettier prettier-plugin-jsdoc
41
49
 
42
50
  ### Options
43
51
 
44
- `productiveEslint` accepts an options object:
52
+ `createConfig` accepts an options object:
45
53
 
46
54
  | Option | Type | Default | Description |
47
55
  |---|---|---|---|
48
- | `strictness` | `'easy' \| 'medium' \| 'hard'` | `'hard'` | Rule strictness preset |
56
+ | `preset` | `Preset.AUTO_FIXABLE \| Preset.RECOMMENDED` | `Preset.RECOMMENDED` | Rule preset |
49
57
  | `ignores` | `string[]` | `[]` | Additional glob patterns to ignore |
50
58
  | `vue` | `boolean` | auto-detect | Enable Vue/Nuxt rules |
51
59
  | `rxjs` | `boolean` | auto-detect | Enable RxJS rules |
52
60
 
53
61
  By default, `vue` and `rxjs` are auto-detected based on installed packages.
54
62
 
63
+ ### Presets
64
+
65
+ ```ts
66
+ import { createConfig, Preset } from 'productive-eslint'
67
+ ```
68
+
69
+ `Preset.AUTO_FIXABLE` enables only rules with reliable ESLint autofix support.
70
+
71
+ ```ts
72
+ export default createConfig({
73
+ preset: Preset.AUTO_FIXABLE,
74
+ })
75
+ ```
76
+
77
+ `Preset.RECOMMENDED` is the default permanent baseline. It includes `AUTO_FIXABLE` plus mechanical non-autofixable rules.
78
+
79
+ ```ts
80
+ export default createConfig({
81
+ preset: Preset.RECOMMENDED,
82
+ })
83
+ ```
84
+
55
85
  ---
56
86
 
57
87
  ### Monorepo setup
@@ -59,9 +89,9 @@ By default, `vue` and `rxjs` are auto-detected based on installed packages.
59
89
  When the ESLint config lives at the **workspace root**, auto-detection may not find packages installed only in sub-packages. In this case, enable framework configs explicitly:
60
90
 
61
91
  ```ts
62
- import productiveEslint from 'productive-eslint'
92
+ import { createConfig } from 'productive-eslint'
63
93
 
64
- export default productiveEslint({
94
+ export default createConfig({
65
95
  rxjs: true,
66
96
  vue: true,
67
97
  })
@@ -69,17 +99,64 @@ export default productiveEslint({
69
99
 
70
100
  ---
71
101
 
72
- ### AI Agent Integration
73
- This package ships a `FIXES.md` file describing how to fix every rule that `eslint --fix` cannot resolve automatically.
102
+ ### On-Demand Code Review Diagnostics
103
+
104
+ Repository-wide ESLint is kept mechanical on purpose. Nuanced checks such as type-safety debt, architecture boundaries,
105
+ async correctness, migration tails, framework lifecycle risk, and complexity should be handled by focused diagnostics
106
+ instead of being enabled as permanent lint noise.
107
+
108
+ These diagnostics are intended for explicit review/audit requests, not as an always-on agent tool loop during ordinary
109
+ coding tasks.
110
+
111
+ Available CLI diagnostics:
112
+
113
+ ```bash
114
+ productive-eslint analyze types
115
+ productive-eslint analyze architecture
116
+ productive-eslint analyze complexity
117
+ productive-eslint analyze async
118
+ productive-eslint analyze suppressions
119
+ productive-eslint analyze dead-code
120
+ productive-eslint analyze imports
121
+ productive-eslint analyze api
122
+ productive-eslint analyze vue
123
+ productive-eslint analyze rxjs
124
+ productive-eslint analyze migrations
125
+ productive-eslint analyze risk
126
+ ```
127
+
128
+ Run one diagnostic at a time from an explicit project root:
74
129
 
75
- Add the following instruction to your `CLAUDE.md`, `.cursorrules`, or similar AI agent config:
130
+ ```bash
131
+ productive-eslint analyze types --cwd /path/to/project
132
+ productive-eslint analyze complexity --cwd . --top 20
133
+ productive-eslint analyze suppressions --cwd . --include "src/**/*.ts" --exclude "**/*.test.ts"
134
+ ```
76
135
 
136
+ Recommended first audit pass:
137
+
138
+ ```bash
139
+ productive-eslint analyze risk --cwd .
140
+ productive-eslint analyze types --cwd .
141
+ productive-eslint analyze suppressions --cwd .
142
+ productive-eslint analyze async --cwd .
143
+ productive-eslint analyze architecture --cwd .
144
+ productive-eslint analyze complexity --cwd . --top 20
77
145
  ```
78
- When fixing ESLint errors that `eslint --fix` cannot resolve,
79
- look up the rule in node_modules/productive-eslint/FIXES.md.
146
+
147
+ Use framework-specific diagnostics only when the target project enables the
148
+ matching preset support:
149
+
150
+ ```bash
151
+ productive-eslint analyze vue --cwd .
152
+ productive-eslint analyze rxjs --cwd .
153
+ productive-eslint analyze migrations --cwd .
80
154
  ```
81
155
 
156
+ The CLI requires the target project to export a marked `productive-eslint`
157
+ composer from `eslint.config.ts` or `eslint.config.mts`.
158
+
82
159
  ---
83
160
 
84
161
  📄 License: MIT © Bogdan Binitskiy
85
- 💻 Contributor: [Roman Nikitin](https://github.com/Stelsovich1)
162
+ 💻 Contributor: [Roman Nikitin](https://github.com/Stelsovich1)
package/dist/cli.d.ts ADDED
@@ -0,0 +1 @@
1
+ export { };
package/dist/cli.js ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env node
2
+ import{_ as e,h as t,i as n,n as r,r as i,t as a,u as o}from"./model-DGP73Lve.js";import s from"node:path";import c from"node:process";import{createJiti as l}from"jiti";import{access as u,readFile as d}from"node:fs/promises";import f from"@typescript-eslint/eslint-plugin";import p from"@typescript-eslint/parser";import m from"eslint-plugin-import";import{ESLint as h}from"eslint";import g from"typescript";import _ from"@smarttools/eslint-plugin-rxjs";const v=e=>{let t=[`# ${e.title}`,``,`## Summary`,``,`- Files scanned: \`${e.fileCount}\``,`- Findings: \`${e.findingsCount}\``,`- Hotspots shown: \`${e.summaries.length}\``];if(e.summaryLines&&e.summaryLines.length>0)for(let n of e.summaryLines)t.push(`- ${n}`);if(t.push(``),e.summaries.length>0){if(e.topGroups&&e.topGroups.length>0){t.push(`## Top Directions`,``);for(let n of e.topGroups)t.push(`- ${n.label}: \`${n.count}\``);t.push(``)}t.push(`## Highest Risk`,``);for(let n of e.summaries){if(t.push(`### \`${n.file}\``,``),t.push(`- Score: \`${n.score}\``),n.labels&&n.labels.length>0&&t.push(`- Labels: ${n.labels.map(e=>`\`${e}\``).join(`, `)}`),n.reasons&&n.reasons.length>0){t.push(`- Why this file is prioritized:`);for(let e of n.reasons)t.push(` - ${e}`)}let e=n.findings.slice(0,10),r=n.findings.length-e.length;for(let r of e){let e=r.symbol?`${n.file} :: ${r.symbol}`:n.file,i=r.line?`${e}:${r.line}${r.column?`:${r.column}`:``}`:e;t.push(`- ${r.ruleId??`unknown-rule`} (${r.severity}, score ${r.score}) at \`${i}\``),r.labels&&r.labels.length>0&&t.push(` - Labels: ${r.labels.map(e=>`\`${e}\``).join(`, `)}`);for(let e of r.reasons)t.push(` - ${e}`)}r>0&&t.push(`- ${r} more findings in this hotspot are omitted from the Markdown report.`),t.push(``)}}return e.suggestedOrder.length>0&&(t.push(`## Suggested Order`,``),e.suggestedOrder.forEach((e,n)=>{t.push(`${n+1}. \`${e}\``)}),t.push(``)),t.push(`## Next Step`,``,e.nextStep),`${t.join(`
3
+ `).trim()}\n`};var y=class extends Error{constructor(e){super(e),this.name=`CliError`}};const ee=[`eslint.config.ts`,`eslint.config.mts`],te=async e=>{for(let t of ee){let n=s.join(e,t);try{return await u(n),n}catch{continue}}return null},ne=t=>{if(!e(t))throw new y(`The selected project does not export a supported productive-eslint composer pipeline.`);return t},re=e=>!e||typeof e!=`object`||!(`default`in e)?e:e.default??e,ie=async e=>{let t=await te(e);if(!t)throw new y(`No supported eslint.config.ts or eslint.config.mts file was found in "${e}".`);let n=l(import.meta.url,{fsCache:!1,moduleCache:!1}),r=c.cwd();try{return c.chdir(e),{composer:ne(re(n(t))),configPath:t}}finally{c.chdir(r)}},b=async({config:e,context:t,ruleIds:n})=>{let r=new h({cwd:t.cwd,errorOnUnmatchedPattern:!1,ignore:!0,ignorePatterns:t.exclude,overrideConfig:e,overrideConfigFile:!0,passOnNoPatterns:!0,ruleFilter:({ruleId:e})=>n.includes(e)}),i=t.include.length>0?t.include:[`.`];return(await r.lintFiles(i)).map(e=>({...e,filePath:s.relative(t.cwd,e.filePath)||e.filePath}))},ae=e=>e.labels?.includes(`test-only`)??!1,oe=e=>{let t=e.reduce((e,t)=>e+t.score,0);return e.length>0&&e.every(ae)?Math.min(t,10):t},x=(e,t)=>{let n=new Map;for(let t of e){let e=n.get(t.file)??[];e.push(t),n.set(t.file,e)}return[...n.entries()].map(([e,t])=>({file:e,findings:t.sort((e,t)=>t.score-e.score),labels:[...new Set(t.flatMap(e=>e.labels??[]))],reasons:[...new Set(t.flatMap(e=>e.reasons.slice(0,1)))],score:oe(t)})).sort((e,t)=>t.score-e.score||e.file.localeCompare(t.file)).slice(0,t)},S={"@typescript-eslint/explicit-module-boundary-types":`error`,"@typescript-eslint/no-explicit-any":`error`,"@typescript-eslint/no-unsafe-return":`error`,"import/no-mutable-exports":`error`,"no-restricted-syntax":[`error`,{message:`Default exports make public API naming less explicit and harder to refactor.`,selector:`ExportDefaultDeclaration`}]},se=m,ce=f,C=Object.keys(S),le={"@typescript-eslint/explicit-module-boundary-types":{category:`public-signature`,labels:[`public-signature`,`missing-return-type`],reason:`An exported boundary should make its return contract explicit.`,score:5,severity:`medium`},"@typescript-eslint/no-explicit-any":{category:`public-type-safety`,labels:[`public-type-safety`,`explicit-any`],reason:`Explicit any in public-facing code weakens downstream contracts.`,score:6,severity:`high`},"@typescript-eslint/no-unsafe-return":{category:`public-type-safety`,labels:[`public-type-safety`,`unsafe-return`],reason:`Unsafe returns can leak unknown runtime shape through an API boundary.`,score:6,severity:`high`},"import/no-mutable-exports":{category:`public-mutability`,labels:[`public-mutability`,`mutable-export`],reason:`Mutable exports make public module state difficult to reason about.`,score:5,severity:`medium`},"no-restricted-syntax":{category:`export-policy`,labels:[`export-policy`,`default-export`],reason:`Default exports make public API naming less explicit and harder to refactor.`,score:2,severity:`low`}},ue=/(^|\/)(index|public-api)\.(c|m)?[jt]sx?$/u,de=/(^|\/)(api|public|shared|lib|components)\//u,fe=/\.vue$/u,pe=(e,t)=>{let n=[],r=[],i=t;return ue.test(e)&&(n.push(`public-entry`),r.push(`This file looks like a public entrypoint.`),i+=3),de.test(e)&&(n.push(`shared-surface`),r.push(`The file sits in a shared or API-facing surface.`),i+=2),fe.test(e)&&(n.push(`vue-component-contract`),r.push(`A Vue component file can expose props, emits, slots, or bindings.`),i+=2),{labels:n,reasons:r,score:i}},me=e=>e.clone().append({files:[t],languageOptions:{parserOptions:{extraFileExtensions:[`.vue`],parser:p,projectService:!0,sourceType:`module`}},name:`productive-eslint/analyze-api-vue-parser`}).append({files:[o,t],name:`productive-eslint/analyze-api`,plugins:{"@typescript-eslint":ce,import:se},rules:S,settings:{"import/resolver":{typescript:!0}}}),he=(e,t)=>{if(!t.ruleId||!C.includes(t.ruleId))return null;let n=le[t.ruleId];if(!n)return null;let r=pe(e,n.score);return{category:n.category,column:t.column,confidence:`high`,file:e,labels:[...n.labels,...r.labels],line:t.line,reasons:[n.reason,t.message,...r.reasons],ruleId:t.ruleId,score:r.score,severity:n.severity}},w=async(e,t)=>{if(t.top<1)throw new y(`--top must be at least 1.`);let n=await b({config:await me(e).toConfigs(),context:t,ruleIds:C}),r=n.flatMap(e=>e.messages.flatMap(t=>{let n=he(e.filePath,t);return n?[n]:[]})),i=x(r,t.top),a=i[0],o=r.filter(e=>e.category===`public-type-safety`).length,s=r.filter(e=>e.category===`public-signature`).length,c=r.filter(e=>e.category===`export-policy`).length;return{fileCount:n.length,findingsCount:r.length,nextStep:a?`Start with \`${a.file}\` and make exported contracts explicit before changing internal implementation details.`:`No public API contract findings were reported by the current analyzer ruleset.`,suggestedOrder:i.map(e=>e.file),summaries:i,summaryLines:[`Public type-safety findings: \`${o}\``,`Public signature findings: \`${s}\``,`Export policy findings: \`${c}\``],title:`API Analysis`}},T=[`boundaries/dependencies`],ge=/\b(?:import|export)\b[\s\S]*?\bfrom\s*['"]([^'"]+)['"]|import\s*\(\s*['"]([^'"]+)['"]\s*\)/u,_e=(e,n)=>e.clone().append({files:[o,t],name:`productive-eslint/analyze-architecture`,rules:{"boundaries/dependencies":a},settings:{"boundaries/root-path":n}}),ve=e=>e.some(e=>e.plugins?.boundaries?e.files?Array.isArray(e.files)&&e.files.includes(`**/*.vue`):!0:!1),E=e=>{let t=e.match(ge);return t?.[1]??t?.[2]??null},D=(e,t,n)=>{if(n.startsWith(`.`)){let r=s.resolve(s.dirname(s.join(e,t)),n);return s.relative(e,r)}return n},ye=async(e,t)=>{let r=n(t.file);if(!r)return{labels:[`unknown-source`],reason:`Could not map the source file to a known architecture element.`,scoreDelta:0};if(t.category===`private-entry`){let a=E((await d(s.join(e,t.file),`utf8`)).split(`
4
+ `)[Math.max(0,(t.line??1)-1)]??``);if(!a)return{category:`private-entry`,labels:[`private-entry`],reason:`A private entry import was reported, but the exact target import could not be recovered.`,scoreDelta:4};let o=n(D(e,t.file,a));return{category:`private-entry`,labels:[`private-entry`],reason:`This import bypasses the public entry point of the target architecture element.`,scoreDelta:4,...o?{directionLabel:i(r,o)}:{}}}let a=E((await d(s.join(e,t.file),`utf8`)).split(`
5
+ `)[Math.max(0,(t.line??1)-1)]??``);if(!a)return{category:`layer-direction`,labels:[`layer-direction`],reason:`An architecture direction violation was reported, but the target dependency could not be recovered.`,scoreDelta:3};let o=n(D(e,t.file,a));return o?{category:r.type===o.type?`slice-direction`:`layer-direction`,directionLabel:i(r,o),labels:[r.type===o.type?`slice-direction`:`layer-direction`],reason:r.type===o.type?`This import crosses into another slice of the same architecture layer.`:`This import violates the allowed dependency direction between architecture layers.`,scoreDelta:3}:{category:`layer-direction`,labels:[`layer-direction`],reason:`The target dependency could not be mapped to a known architecture element.`,scoreDelta:3}},be=(e,t)=>!t.ruleId||!T.includes(t.ruleId)?null:{category:t.message.includes(`private-entry`)?`private-entry`:`layer-direction`,column:t.column,confidence:`high`,file:e.filePath,labels:[],line:t.line,reasons:[t.message],ruleId:t.ruleId,score:t.message.includes(`private-entry`)?5:4,severity:`high`},xe=e=>{let t=new Map;for(let n of e){let e=n.category===`private-entry`?`private entry imports`:n.labels?.find(e=>e.includes(`->`));e&&t.set(e,(t.get(e)??0)+1)}return[...t.entries()].map(([e,t])=>({count:t,label:e})).sort((e,t)=>t.count-e.count||e.label.localeCompare(t.label)).slice(0,5)},O=async(e,n)=>{if(n.top<1)throw new y(`--top must be at least 1.`);let i=await _e(e,n.cwd).toConfigs();ve(i)||i.push({files:[t],name:`productive-eslint/analyze-architecture-vue-plugin`,plugins:r.plugins});let a=await b({config:i,context:n,ruleIds:[...T]}),o=[];for(let e of a)for(let t of e.messages){let r=be(e,t);if(!r)continue;let i=await ye(n.cwd,r);o.push({...r,...i.category?{category:i.category}:{},labels:[...new Set([...r.labels??[],...i.labels,...i.directionLabel?[i.directionLabel]:[]])],reasons:[...r.reasons,i.reason],score:r.score+i.scoreDelta})}let s=x(o,n.top),c=s[0],l=xe(o),u=o.filter(e=>e.category===`private-entry`).length,d=o.filter(e=>e.category===`layer-direction`).length,f=o.filter(e=>e.category===`slice-direction`).length;return{fileCount:a.length,findingsCount:o.length,nextStep:c?`Start with \`${c.file}\` and remove the repeated architecture violations there first.`:`No architecture boundary violations were reported by the current analyzer ruleset.`,suggestedOrder:s.map(e=>e.file),summaries:s,summaryLines:[`Layer-direction violations: \`${d}\``,`Slice-direction violations: \`${f}\``,`Private-entry violations: \`${u}\``],title:`Architecture Analysis`,topGroups:l}},k={"@typescript-eslint/await-thenable":`error`,"@typescript-eslint/no-floating-promises":`error`,"@typescript-eslint/no-misused-promises":`error`,"@typescript-eslint/require-await":`error`,"@typescript-eslint/return-await":[`error`,`error-handling-correctness-only`],"no-async-promise-executor":`error`,"promise/always-return":`error`,"promise/catch-or-return":`error`,"promise/no-multiple-resolved":`error`,"promise/no-return-in-finally":`error`,"promise/valid-params":`error`},Se=f,A=Object.keys(k),Ce={"@typescript-eslint/await-thenable":{category:`likely-bug`,labels:[`likely-bug`,`await-non-thenable`],reason:`Awaiting a non-thenable value usually means async intent drifted.`,score:5,severity:`high`},"@typescript-eslint/no-floating-promises":{category:`likely-bug`,labels:[`likely-bug`,`floating-promise`],reason:`A promise can reject without any handling path.`,score:6,severity:`high`},"@typescript-eslint/no-misused-promises":{category:`likely-bug`,labels:[`likely-bug`,`misused-promise`],reason:`Async behavior is flowing through an API shape that is usually not promise-safe.`,score:6,severity:`high`},"@typescript-eslint/require-await":{category:`suspicious-flow`,labels:[`suspicious-flow`,`unneeded-async`],reason:`An async function without await can make callers assume async work or error boundaries that are not present.`,score:3,severity:`medium`},"@typescript-eslint/return-await":{category:`suspicious-flow`,labels:[`suspicious-flow`,`return-await-error-boundary`],reason:`Returning a promise without await can bypass the local async error boundary.`,score:4,severity:`medium`},"no-async-promise-executor":{category:`likely-bug`,labels:[`likely-bug`,`async-executor`],reason:`Async promise executors often hide rejected work and double-resolution hazards.`,score:5,severity:`high`},"promise/always-return":{category:`suspicious-flow`,labels:[`suspicious-flow`,`incomplete-chain`],reason:`A promise callback does not consistently return its async result.`,score:3,severity:`medium`},"promise/catch-or-return":{category:`suspicious-flow`,labels:[`suspicious-flow`,`missing-catch`],reason:`The promise chain does not make error handling explicit.`,score:4,severity:`medium`},"promise/no-multiple-resolved":{category:`lifecycle-hazard`,labels:[`lifecycle-hazard`,`multiple-resolve`],reason:`The same promise can resolve or reject more than once.`,score:5,severity:`high`},"promise/no-return-in-finally":{category:`lifecycle-hazard`,labels:[`lifecycle-hazard`,`finally-control-flow`],reason:`Returning from finally can suppress or replace earlier async outcomes.`,score:4,severity:`high`},"promise/valid-params":{category:`likely-bug`,labels:[`likely-bug`,`invalid-promise-api`],reason:`Promise APIs are being called with invalid argument shapes.`,score:4,severity:`medium`}},we=/(^|\/)(test|tests|__tests__|__mocks__)\//u,Te=/\.(spec|test)\.(c|m)?[jt]sx?$/u,Ee=/(^|\/)(shared|lib|libs|utils|helpers|api)\//u,De=e=>we.test(e)||Te.test(e),Oe=e=>Ee.test(e),ke=e=>e.clone().append({files:[t],languageOptions:{parserOptions:{extraFileExtensions:[`.vue`],parser:p,projectService:!0,sourceType:`module`}},name:`productive-eslint/analyze-async-vue-parser`}).append({files:[o,t],name:`productive-eslint/analyze-async`,plugins:{"@typescript-eslint":Se},rules:k}),Ae=(e,t)=>{if(!t.ruleId||!A.includes(t.ruleId))return null;let n=Ce[t.ruleId];if(!n)return null;let r=De(e),i=!r&&Oe(e),a=Math.max(1,n.score+(i?2:0)-(r?2:0)),o=[...n.labels,...i?[`shared-surface`]:[],...r?[`test-only`]:[]],s=[n.reason,t.message,...i?[`The finding sits in a shared async surface that can affect multiple callers.`]:[],...r?[`The finding is test-only, so urgency is usually lower than production code.`]:[]];return{category:n.category,column:t.column,confidence:`high`,file:e,labels:o,line:t.line,reasons:s,ruleId:t.ruleId,score:a,severity:n.severity}},j=async(e,t)=>{if(t.top<1)throw new y(`--top must be at least 1.`);let n=await b({config:await ke(e).toConfigs(),context:t,ruleIds:A}),r=n.flatMap(e=>e.messages.flatMap(t=>{let n=Ae(e.filePath,t);return n?[n]:[]})),i=x(r,t.top),a=i[0],o=r.filter(e=>e.category===`likely-bug`).length,s=r.filter(e=>e.category===`suspicious-flow`).length,c=r.filter(e=>e.category===`lifecycle-hazard`).length;return{fileCount:n.length,findingsCount:r.length,nextStep:a?`Start with \`${a.file}\` and make the async control flow explicit there first.`:`No async reliability findings were reported by the current analyzer ruleset.`,suggestedOrder:i.map(e=>e.file),summaries:i,summaryLines:[`Likely bug findings: \`${o}\``,`Suspicious flow findings: \`${s}\``,`Lifecycle hazard findings: \`${c}\``],title:`Async Analysis`}},M={complexity:[`error`,10],"max-depth":[`error`,3],"no-nested-ternary":`error`,"productive/no-abusive-nested-if":[`error`,3],"sonarjs/cognitive-complexity":[`error`,15],"sonarjs/no-all-duplicated-branches":`error`,"sonarjs/no-duplicated-branches":`error`,"sonarjs/no-gratuitous-expressions":`error`,"sonarjs/no-identical-conditions":`error`,"sonarjs/no-identical-expressions":`error`,"sonarjs/no-invariant-returns":`error`,"sonarjs/no-nested-conditional":`error`},N=Object.keys(M),je={complexity:3,"max-depth":3,"no-nested-ternary":2,"productive/no-abusive-nested-if":2,"sonarjs/cognitive-complexity":4,"sonarjs/no-all-duplicated-branches":3,"sonarjs/no-duplicated-branches":3,"sonarjs/no-gratuitous-expressions":2,"sonarjs/no-identical-conditions":3,"sonarjs/no-identical-expressions":3,"sonarjs/no-invariant-returns":3,"sonarjs/no-nested-conditional":2},Me={complexity:`Cyclomatic complexity already crossed the analyzer threshold.`,"max-depth":`Branch nesting is above the safe editing threshold.`,"no-nested-ternary":`Nested ternaries make branch intent harder to preserve.`,"productive/no-abusive-nested-if":`Nested if chains are dense enough to deserve structural attention.`,"sonarjs/cognitive-complexity":`Cognitive complexity is already high before additional risk scoring.`,"sonarjs/no-all-duplicated-branches":`Every branch has the same implementation, so the control flow is likely misleading.`,"sonarjs/no-duplicated-branches":`Duplicated branch implementations make it harder to infer the intended behavior.`,"sonarjs/no-gratuitous-expressions":`A condition or expression is structurally unnecessary and can obscure intent.`,"sonarjs/no-identical-conditions":`Repeated branch conditions usually point to stale or unreachable logic.`,"sonarjs/no-identical-expressions":`Identical expressions in a comparison can turn branching into dead intent.`,"sonarjs/no-invariant-returns":`A function returns the same value through different branches, making the branching suspect.`,"sonarjs/no-nested-conditional":`Nested conditionals compress multiple branches into a hard-to-edit expression.`},Ne=e=>e.endsWith(`.tsx`)?g.ScriptKind.TSX:g.ScriptKind.TS,P=e=>g.isArrowFunction(e)||g.isConstructorDeclaration(e)||g.isFunctionDeclaration(e)||g.isFunctionExpression(e)||g.isGetAccessor(e)||g.isMethodDeclaration(e)||g.isSetAccessor(e),Pe=e=>{if(g.isConstructorDeclaration(e))return`constructor`;if(`name`in e&&e.name)return g.isIdentifier(e.name)||g.isStringLiteral(e.name)?e.name.text:e.name.getText();let t=e.parent;return t&&g.isVariableDeclaration(t)&&g.isIdentifier(t.name)||t&&g.isPropertyAssignment(t)&&(g.isIdentifier(t.name)||g.isStringLiteral(t.name)||g.isNumericLiteral(t.name))?t.name.text:t&&g.isBinaryExpression(t)&&t.operatorToken.kind===g.SyntaxKind.EqualsToken?t.left.getText():`<anonymous>`},Fe=e=>e?g.isObjectLiteralExpression(e)?`object`:g.isArrayLiteralExpression(e)?`array`:g.isStringLiteralLike(e)||g.isNumericLiteral(e)?`literal`:e.kind===g.SyntaxKind.TrueKeyword||e.kind===g.SyntaxKind.FalseKeyword?`boolean`:g.isIdentifier(e)?`identifier`:g.isCallExpression(e)?`call`:g.isAwaitExpression(e)?`await`:g.isConditionalExpression(e)?`conditional`:g.SyntaxKind[e.kind]??`other`:`void`,F=e=>{let t=0,n=e=>{g.isBinaryExpression(e)&&(e.operatorToken.kind===g.SyntaxKind.AmpersandAmpersandToken||e.operatorToken.kind===g.SyntaxKind.BarBarToken)&&(t+=1),e.forEachChild(n)};return n(e),t},I=e=>g.isIdentifier(e)?e.text:e.getText(),Ie=e=>e===g.SyntaxKind.EqualsToken||e===g.SyntaxKind.PlusEqualsToken||e===g.SyntaxKind.MinusEqualsToken||e===g.SyntaxKind.AsteriskEqualsToken||e===g.SyntaxKind.AsteriskAsteriskEqualsToken||e===g.SyntaxKind.SlashEqualsToken||e===g.SyntaxKind.PercentEqualsToken||e===g.SyntaxKind.AmpersandEqualsToken||e===g.SyntaxKind.BarEqualsToken||e===g.SyntaxKind.CaretEqualsToken||e===g.SyntaxKind.LessThanLessThanEqualsToken||e===g.SyntaxKind.GreaterThanGreaterThanEqualsToken||e===g.SyntaxKind.GreaterThanGreaterThanGreaterThanEqualsToken||e===g.SyntaxKind.AmpersandAmpersandEqualsToken||e===g.SyntaxKind.BarBarEqualsToken||e===g.SyntaxKind.QuestionQuestionEqualsToken,Le=(e,t)=>{let n=0,r=[],i=new Map,a=new Set,o=new Set,s=new Set,c=0,l=0,u=0,d=0,f=e=>{let t=n;n+=1,r.push(t),c=Math.max(c,r.length),h(e),r.pop()},p=e=>{let t=r.at(-1);if(t===void 0)return;let n=i.get(t)??new Set;n.add(e),i.set(t,n),a.add(t)},m=()=>{let e=r.at(-1);e!==void 0&&o.add(e)},h=t=>{if(!(t!==e&&P(t))){if(g.isIfStatement(t)){d+=F(t.expression),f(t.thenStatement),t.elseStatement&&f(t.elseStatement);return}if(g.isForStatement(t)||g.isForInStatement(t)||g.isForOfStatement(t)||g.isWhileStatement(t)||g.isDoStatement(t)){`expression`in t&&t.expression&&(d+=F(t.expression)),f(t.statement);return}if(g.isSwitchStatement(t)){for(let e of t.caseBlock.clauses)f(e);return}if(g.isTryStatement(t)&&r.length>0&&(u+=1),g.isAwaitExpression(t)&&r.length>0&&(l+=1),g.isBinaryExpression(t)&&Ie(t.operatorToken.kind)&&(g.isIdentifier(t.left)||g.isPropertyAccessExpression(t.left)||g.isElementAccessExpression(t.left)||g.isArrayLiteralExpression(t.left)||g.isObjectLiteralExpression(t.left))&&p(I(t.left)),(g.isPrefixUnaryExpression(t)||g.isPostfixUnaryExpression(t))&&(t.operator===g.SyntaxKind.PlusPlusToken||t.operator===g.SyntaxKind.MinusMinusToken)){let e=t.operand;(g.isIdentifier(e)||g.isPropertyAccessExpression(e)||g.isElementAccessExpression(e))&&p(I(e))}(g.isCallExpression(t)||g.isNewExpression(t))&&r.length>0&&m(),g.isReturnStatement(t)&&s.add(Fe(t.expression)),t.forEachChild(h)}};e.body&&h(e.body);let _=new Map;for(let e of i.values())for(let t of e)_.set(t,(_.get(t)??0)+1);let v=t.getLineAndCharacterOfPosition(e.getStart(t)).line+1,y=t.getLineAndCharacterOfPosition(e.end).line+1;return{asyncInsideBranches:l,booleanGuardComplexity:d,branchMutations:a.size,functionLines:y-v+1,maxDepth:c,mixedReturnShapes:s.size>1?s.size:0,sharedMutableAcrossBranches:[..._.values()].filter(e=>e>1).length,sideEffectBranches:o.size,tryCatchInsideBranches:u}},Re=(e,t)=>{let n=[],r=i=>{if(P(i)){let r=t.getLineAndCharacterOfPosition(i.getStart(t)).line+1,a=t.getLineAndCharacterOfPosition(i.end).line+1;n.push({displayName:Pe(i),endLine:a,file:e,metrics:Le(i,t),startLine:r})}i.forEachChild(r)};return r(t),n},L=(e,t)=>t===void 0?null:e.filter(e=>t>=e.startLine&&t<=e.endLine).sort((e,t)=>e.endLine-e.startLine-(t.endLine-t.startLine))[0]??null,ze=(e,t,n)=>{if(n&&n.displayName!==`<anonymous>`)return n;let r=e.filter(e=>e.displayName!==`<anonymous>`);for(let e of t){let t=L(r,e.line);if(t)return t}return n},Be=e=>Math.max(0,e.maxDepth-2)+e.branchMutations*2+e.asyncInsideBranches*2+e.tryCatchInsideBranches*2+(e.mixedReturnShapes>0?2:0)+e.sharedMutableAcrossBranches*3+Math.max(0,e.sideEffectBranches-1)*3+ +(e.functionLines>40)+e.booleanGuardComplexity,Ve=e=>{let t=[];return e.maxDepth>2&&t.push(`Nested branch depth reaches ${e.maxDepth}.`),e.branchMutations>0&&t.push(`Branches mutate state in ${e.branchMutations} places.`),e.sharedMutableAcrossBranches>0&&t.push(`Mutable state is shared across ${e.sharedMutableAcrossBranches} branch paths.`),e.asyncInsideBranches>0&&t.push(`Async work appears inside ${e.asyncInsideBranches} branch paths.`),e.sideEffectBranches>1&&t.push(`Side effects appear in ${e.sideEffectBranches} different branches.`),e.tryCatchInsideBranches>0&&t.push(`Try/catch handling is nested inside branching logic.`),e.mixedReturnShapes>0&&t.push(`The function returns more than one structural shape.`),e.functionLines>40&&t.push(`Function spans ${e.functionLines} lines.`),e.booleanGuardComplexity>0&&t.push(`Boolean guards contain multiple logical operators.`),t},He=e=>e>=12?`high`:e>=7?`medium`:`low`,Ue=e=>e.clone().append({files:[o],name:`productive-eslint/analyze-complexity`,rules:M}),We=(e,t)=>e.sort((e,t)=>t.score-e.score||e.file.localeCompare(t.file)).slice(0,t).map(e=>({file:e.file,findings:[e],...e.labels?{labels:e.labels}:{},reasons:e.reasons.slice(0,3),score:e.score})),R=e=>e.symbol?`${e.file} :: ${e.symbol}`:e.file,z=async(e,t)=>{if(t.top<1)throw new y(`--top must be at least 1.`);let n=await b({config:await Ue(e).toConfigs(),context:t,ruleIds:N}),r=[];for(let e of n){if(e.messages.length===0)continue;let n=s.join(t.cwd,e.filePath),i=await d(n,`utf8`),a=g.createSourceFile(n,i,g.ScriptTarget.Latest,!0,Ne(n)),o=Re(e.filePath,a),c=new Map;for(let t of e.messages){if(!t.ruleId||!N.includes(t.ruleId))continue;let n=L(o,t.line),r=n?`${e.filePath} :: ${n.displayName}:${n.startLine}`:`${e.filePath} :: <module>:0`,i=c.get(r)??{context:n,hits:[]};i.hits.push({line:t.line,message:t.message,ruleId:t.ruleId}),c.set(r,i)}for(let[,t]of c){let n=ze(o,t.hits,t.context),i=n?.metrics??{asyncInsideBranches:0,booleanGuardComplexity:0,branchMutations:0,functionLines:0,maxDepth:0,mixedReturnShapes:0,sharedMutableAcrossBranches:0,sideEffectBranches:0,tryCatchInsideBranches:0},a=t.hits.reduce((e,t)=>e+(je[t.ruleId]??1),0)+Be(i),s=He(a),c=n?.displayName??`<module>`,l=[...t.hits.map(e=>`${Me[e.ruleId]??e.message} (${e.ruleId} at line ${e.line})`),...Ve(i)];r.push({category:`semantic-change-risk`,confidence:`medium`,file:e.filePath,labels:[s,...n?[`function:${c}`]:[`module-scope`]],...n?{line:n.startLine}:{},reasons:l,ruleId:`complexity/hotspot`,score:a,severity:s,symbol:c})}}let i=We(r,t.top),a=r.filter(e=>e.severity===`high`).length,o=r.filter(e=>e.severity===`medium`).length,c=r.filter(e=>e.severity===`low`).length,l=i[0],u=l?.findings[0];return{fileCount:n.length,findingsCount:r.length,nextStep:l?`Start with \`${u?R(u):l.file}\` and reduce the branch and side-effect risk there first.`:`No complexity hotspots were reported by the current analyzer ruleset.`,suggestedOrder:i.flatMap(e=>e.findings.map(R)),summaries:i,summaryLines:[`High-risk hotspots: \`${a}\``,`Medium-risk hotspots: \`${o}\``,`Low-risk hotspots: \`${c}\``],title:`Complexity Analysis`}},Ge={"@typescript-eslint/no-unused-private-class-members":`error`,"@typescript-eslint/no-unused-vars":[`error`,{args:`after-used`,argsIgnorePattern:`^_`,caughtErrors:`all`,caughtErrorsIgnorePattern:`^_`,ignoreRestSiblings:!0,varsIgnorePattern:`^_`}],"@typescript-eslint/no-useless-constructor":`error`,"no-unreachable":`error`,"no-useless-catch":`error`,"unicorn/no-empty-file":`error`,"unicorn/no-static-only-class":`error`,"unicorn/no-useless-undefined":`error`,"unused-imports/no-unused-imports":`error`},Ke=f,B=Object.keys(Ge),qe={"@typescript-eslint/no-unused-private-class-members":{category:`safe-delete-candidate`,labels:[`safe-delete-candidate`,`private-member`],reason:`A private class member appears unused and is usually safe to delete.`,score:4,severity:`medium`},"@typescript-eslint/no-unused-vars":{category:`safe-delete-candidate`,labels:[`safe-delete-candidate`,`unused-symbol`],reason:`An unused local symbol adds maintenance noise.`,score:3,severity:`medium`},"@typescript-eslint/no-useless-constructor":{category:`safe-delete-candidate`,labels:[`safe-delete-candidate`,`useless-constructor`],reason:`A constructor does not add behavior beyond the inherited default.`,score:3,severity:`low`},"no-unreachable":{category:`likely-bug-or-delete`,labels:[`likely-bug-or-delete`,`unreachable`],reason:`Unreachable code is either dead or hides a control-flow mistake.`,score:6,severity:`high`},"no-useless-catch":{category:`safe-delete-candidate`,labels:[`safe-delete-candidate`,`useless-catch`],reason:`A catch block only rethrows and can usually be removed.`,score:3,severity:`low`},"unicorn/no-empty-file":{category:`safe-delete-candidate`,labels:[`safe-delete-candidate`,`empty-file`],reason:`An empty source file is usually leftover migration or scaffolding debt.`,score:4,severity:`medium`},"unicorn/no-static-only-class":{category:`requires-judgment`,labels:[`requires-judgment`,`static-only-class`],reason:`A static-only class may be a namespace-shaped leftover rather than useful API.`,score:2,severity:`low`},"unicorn/no-useless-undefined":{category:`safe-delete-candidate`,labels:[`safe-delete-candidate`,`redundant-undefined`],reason:`A redundant undefined usually has no semantic value.`,score:1,severity:`low`},"unused-imports/no-unused-imports":{category:`mechanical-delete`,labels:[`mechanical-delete`,`unused-import`],reason:`An unused import can be removed mechanically.`,score:2,severity:`low`}},Je=/(^|\/)(test|tests|__tests__|__mocks__)\//u,Ye=/\.(spec|test)\.(c|m)?[jt]sx?$/u,Xe=e=>Je.test(e)||Ye.test(e),Ze=e=>e.clone().append({files:[t],languageOptions:{parserOptions:{extraFileExtensions:[`.vue`],parser:p,projectService:!0,sourceType:`module`}},name:`productive-eslint/analyze-dead-code-vue-parser`}).append({files:[o,t],name:`productive-eslint/analyze-dead-code`,plugins:{"@typescript-eslint":Ke},rules:Ge}),Qe=(e,t)=>{if(!t.ruleId||!B.includes(t.ruleId))return null;let n=qe[t.ruleId];if(!n)return null;let r=Xe(e);return{category:r?`test-only`:n.category,column:t.column,confidence:n.category===`requires-judgment`?`medium`:`high`,file:e,labels:[...n.labels,...r?[`test-only`]:[]],line:t.line,reasons:[n.reason,t.message,...r?[`The finding is test-only, so deletion priority is usually lower.`]:[]],ruleId:t.ruleId,score:Math.max(1,n.score-(r?2:0)),severity:r?`low`:n.severity}},V=async(e,t)=>{if(t.top<1)throw new y(`--top must be at least 1.`);let n=await b({config:await Ze(e).toConfigs(),context:t,ruleIds:B}),r=n.flatMap(e=>e.messages.flatMap(t=>{let n=Qe(e.filePath,t);return n?[n]:[]})),i=x(r,t.top),a=i[0],o=r.filter(e=>e.labels?.includes(`mechanical-delete`)).length,s=r.filter(e=>e.labels?.includes(`safe-delete-candidate`)).length,c=r.filter(e=>e.category===`requires-judgment`).length;return{fileCount:n.length,findingsCount:r.length,nextStep:a?`Start with \`${a.file}\` and remove the mechanical dead-code findings before touching judgment-heavy cleanup.`:`No dead-code findings were reported by the current analyzer ruleset.`,suggestedOrder:i.map(e=>e.file),summaries:i,summaryLines:[`Mechanical deletion findings: \`${o}\``,`Safe-delete candidates: \`${s}\``,`Requires judgment findings: \`${c}\``],title:`Dead Code Analysis`}},H={"@typescript-eslint/consistent-type-imports":[`error`,{disallowTypeAnnotations:!1,fixStyle:`separate-type-imports`,prefer:`type-imports`}],"import/consistent-type-specifier-style":[`error`,`prefer-top-level`],"import/no-cycle":[`error`,{ignoreExternal:!0,maxDepth:10}],"import/no-duplicates":`error`,"import/no-mutable-exports":`error`,"import/no-namespace":`error`,"import/no-relative-packages":`error`,"import/no-self-import":`error`,"import/no-useless-path-segments":`error`},$e=m,et=f,U=Object.keys(H),tt={"@typescript-eslint/consistent-type-imports":{category:`type-value-boundary`,labels:[`type-value-boundary`,`mechanical-fix`],reason:`Mixed type and value imports make dependency intent harder to scan.`,score:2,severity:`low`},"import/consistent-type-specifier-style":{category:`type-value-boundary`,labels:[`type-value-boundary`,`mechanical-fix`],reason:`Inline type specifiers make import shape less consistent.`,score:1,severity:`low`},"import/no-cycle":{category:`dependency-graph`,labels:[`dependency-graph`,`cycle`],reason:`Import cycles increase coupling and can create initialization hazards.`,score:7,severity:`high`},"import/no-duplicates":{category:`local-import-hygiene`,labels:[`local-import-hygiene`,`mechanical-fix`],reason:`Duplicate imports are local dependency noise and are usually mechanical to merge.`,score:2,severity:`low`},"import/no-mutable-exports":{category:`public-contract-risk`,labels:[`public-contract-risk`,`mutable-export`],reason:`Mutable exports make module contracts harder to reason about.`,score:5,severity:`medium`},"import/no-namespace":{category:`dependency-shape`,labels:[`dependency-shape`,`namespace-import`],reason:`Namespace imports can hide which dependency surface is actually used.`,score:2,severity:`low`},"import/no-relative-packages":{category:`package-boundary`,labels:[`package-boundary`,`relative-package-import`],reason:`Relative package imports bypass package boundaries and published entrypoints.`,score:6,severity:`high`},"import/no-self-import":{category:`dependency-graph`,labels:[`dependency-graph`,`self-import`],reason:`A module importing itself indicates broken dependency shape.`,score:6,severity:`high`},"import/no-useless-path-segments":{category:`local-import-hygiene`,labels:[`local-import-hygiene`,`mechanical-fix`],reason:`Useless path segments make imports harder to compare and maintain.`,score:1,severity:`low`}},nt=e=>e.clone().append({files:[o,t],name:`productive-eslint/analyze-imports`,plugins:{"@typescript-eslint":et,import:$e},rules:H,settings:{"import/resolver":{typescript:!0}}}),rt=(e,t)=>{if(!t.ruleId||!U.includes(t.ruleId))return null;let n=tt[t.ruleId];return n?{category:n.category,column:t.column,confidence:`high`,file:e,labels:n.labels,line:t.line,reasons:[n.reason,t.message],ruleId:t.ruleId,score:n.score,severity:n.severity}:null},it=e=>[...e.reduce((e,t)=>{let n=t.category??`uncategorized`;return e.set(n,(e.get(n)??0)+1),e},new Map)].map(([e,t])=>({count:t,label:e})).sort((e,t)=>t.count-e.count||e.label.localeCompare(t.label)),W=async(e,t)=>{if(t.top<1)throw new y(`--top must be at least 1.`);let n=await b({config:await nt(e).toConfigs(),context:t,ruleIds:U}),r=n.flatMap(e=>e.messages.flatMap(t=>{let n=rt(e.filePath,t);return n?[n]:[]})),i=x(r,t.top),a=i[0],o=r.filter(e=>e.labels?.includes(`cycle`)).length,s=r.filter(e=>e.category===`package-boundary`).length,c=r.filter(e=>e.labels?.includes(`mechanical-fix`)).length;return{fileCount:n.length,findingsCount:r.length,nextStep:a?`Start with \`${a.file}\`; fix cycles and package-boundary findings before mechanical import cleanup.`:`No import-shape findings were reported by the current analyzer ruleset.`,suggestedOrder:i.map(e=>e.file),summaries:i,summaryLines:[`Import cycle findings: \`${o}\``,`Package-boundary findings: \`${s}\``,`Mechanical import fixes: \`${c}\``],title:`Imports Analysis`,topGroups:it(r)}},G={"@typescript-eslint/no-deprecated":`error`,"import/no-deprecated":`error`,"n/no-deprecated-api":`error`},K={"productive-rxjs/no-compat":`error`,"productive-rxjs/no-create":`error`,"productive-rxjs/no-topromise":`error`},q={"vue/no-deprecated-data-object-declaration":`error`,"vue/no-deprecated-delete-set":`error`,"vue/no-deprecated-destroyed-lifecycle":`error`,"vue/no-deprecated-dollar-listeners-api":`error`,"vue/no-deprecated-dollar-scopedslots-api":`error`,"vue/no-deprecated-events-api":`error`,"vue/no-deprecated-filter":`error`,"vue/no-deprecated-functional-template":`error`,"vue/no-deprecated-html-element-is":`error`,"vue/no-deprecated-inline-template":`error`,"vue/no-deprecated-model-definition":`error`,"vue/no-deprecated-props-default-this":`error`,"vue/no-deprecated-router-link-tag-prop":`error`,"vue/no-deprecated-scope-attribute":`error`,"vue/no-deprecated-slot-attribute":`error`,"vue/no-deprecated-slot-scope-attribute":`error`,"vue/no-deprecated-v-bind-sync":`error`,"vue/no-deprecated-v-is":`error`,"vue/no-deprecated-v-on-native-modifier":`error`,"vue/no-deprecated-v-on-number-modifiers":`error`,"vue/no-deprecated-vue-config-keycodes":`error`},J=[...Object.keys(G),...Object.keys(K),...Object.keys(q)],at=_,ot={"productive-rxjs/no-compat":`rxjs/no-compat`,"productive-rxjs/no-create":`rxjs/no-create`,"productive-rxjs/no-topromise":`rxjs/no-topromise`},st=e=>e.startsWith(`vue/no-deprecated-`)?{category:`vue-migration`,labels:[`vue-migration`,`deprecated-api`],reason:`Deprecated Vue API usage should be removed before framework upgrades.`,score:4,severity:`medium`}:e.startsWith(`@typescript-eslint/`)?{category:`typescript-migration`,labels:[`typescript-migration`,`deprecated-api`],reason:`Deprecated TypeScript symbols should be migrated before they become harder to trace.`,score:4,severity:`medium`}:e.startsWith(`import/`)?{category:`dependency-migration`,labels:[`dependency-migration`,`deprecated-api`],reason:`Deprecated imported APIs should be removed before dependency upgrades.`,score:4,severity:`medium`}:e.startsWith(`rxjs/`)?{category:`rxjs-migration`,labels:[`rxjs-migration`,`deprecated-api`],reason:`RxJS toPromise is deprecated and blocks clean reactive upgrades.`,score:4,severity:`medium`}:{category:`node-migration`,labels:[`node-migration`,`deprecated-api`],reason:`Deprecated Node APIs should be removed before runtime upgrades.`,score:4,severity:`medium`},ct=e=>e.clone().append({files:[o],name:`productive-eslint/analyze-migrations-source`,rules:G}).append({files:[o],name:`productive-eslint/analyze-migrations-rxjs`,plugins:{"productive-rxjs":at},rules:K}).append({files:[t],name:`productive-eslint/analyze-migrations-vue`,rules:q}),lt=(e,t)=>{if(!t.ruleId||!J.includes(t.ruleId))return null;let n=ot[t.ruleId]??t.ruleId,r=st(n);return{category:r.category,column:t.column,confidence:`high`,file:e,labels:r.labels,line:t.line,reasons:[r.reason,t.message],ruleId:n,score:r.score,severity:r.severity}},ut=async(e,t)=>{if(t.top<1)throw new y(`--top must be at least 1.`);let n=await b({config:await ct(e).toConfigs(),context:t,ruleIds:J}),r=n.flatMap(e=>e.messages.flatMap(t=>{let n=lt(e.filePath,t);return n?[n]:[]})),i=x(r,t.top),a=i[0],o=r.filter(e=>e.category===`vue-migration`).length,s=r.filter(e=>e.category===`rxjs-migration`).length,c=r.filter(e=>e.category===`node-migration`).length,l=r.filter(e=>e.category===`typescript-migration`).length,u=r.filter(e=>e.category===`dependency-migration`).length;return{fileCount:n.length,findingsCount:r.length,nextStep:a?`Start with \`${a.file}\` and remove deprecated APIs before planning larger framework upgrades.`:`No migration-tail findings were reported by the current analyzer ruleset.`,suggestedOrder:i.map(e=>e.file),summaries:i,summaryLines:[`Vue migration findings: \`${o}\``,`RxJS migration findings: \`${s}\``,`Node migration findings: \`${c}\``,`TypeScript migration findings: \`${l}\``,`Dependency migration findings: \`${u}\``],title:`Migrations Analysis`}},Y={"@typescript-eslint/ban-ts-comment":[`error`,{minimumDescriptionLength:10,"ts-check":!1,"ts-expect-error":`allow-with-description`,"ts-ignore":!0,"ts-nocheck":!0}],"eslint-comments/no-aggregating-enable":`error`,"eslint-comments/no-duplicate-disable":`error`,"eslint-comments/no-unlimited-disable":`error`,"eslint-comments/no-unused-enable":`error`},dt=f,X=Object.keys(Y),ft=/(^|\/)(test|tests|__tests__|__mocks__)\//u,pt=/\.(spec|test)\.(c|m)?[jt]sx?$/u,mt=/(^|\/)(shared|lib|libs|utils|helpers|api)\//u,ht=/eslint-disable|eslint-enable|@ts-(?:ignore|expect-error|nocheck|check)/gu,Z=/^Unused eslint-disable directive/u,gt={category:`unused-disable`,labels:[`unused-disable`,`stale-suppression`],reason:`The disable directive no longer suppresses anything and should be removed.`,score:4,severity:`medium`},_t={"@typescript-eslint/ban-ts-comment":{category:`ts-directive`,labels:[`ts-directive`,`compiler-suppression`],reason:`A TypeScript compiler directive is muting or weakening type feedback.`,score:4,severity:`medium`},"eslint-comments/no-aggregating-enable":{category:`aggregated-enable`,labels:[`aggregated-enable`,`scope-confusion`],reason:`One eslint-enable is reopening multiple disables, which makes suppression scope harder to reason about.`,score:3,severity:`medium`},"eslint-comments/no-duplicate-disable":{category:`duplicate-disable`,labels:[`duplicate-disable`,`redundant-suppression`],reason:`The same rule is being disabled more than once in the same area.`,score:3,severity:`medium`},"eslint-comments/no-unlimited-disable":{category:`broad-disable`,labels:[`broad-disable`,`wide-suppression`],reason:`An eslint-disable comment is muting every rule instead of naming the specific exception.`,score:6,severity:`high`},"eslint-comments/no-unused-enable":{category:`unused-enable`,labels:[`unused-enable`,`stale-suppression`],reason:`The eslint-enable directive is not paired with an active disable and should be cleaned up.`,score:2,severity:`low`}},vt=e=>ft.test(e)||pt.test(e),yt=e=>mt.test(e),bt=async(e,t)=>{let n=(await d(s.join(e,t),`utf8`)).match(ht)?.length??0;return{isSharedSurface:yt(t),isTest:vt(t),suppressionCount:n}},xt=e=>e.clone().append({linterOptions:{reportUnusedDisableDirectives:`error`},name:`productive-eslint/analyze-suppressions-options`}).append({files:[t],languageOptions:{parserOptions:{extraFileExtensions:[`.vue`],parser:p,projectService:!0,sourceType:`module`}},name:`productive-eslint/analyze-suppressions-vue-parser`}).append({files:[o,t],name:`productive-eslint/analyze-suppressions`,plugins:{"@typescript-eslint":dt},rules:Y}),St=e=>e.message.includes(`@ts-ignore`)?{category:`ts-ignore`,labels:[`ts-ignore`,`compiler-suppression`],reason:`ts-ignore suppresses the next compiler error even when the code stops failing later.`,score:6,severity:`high`}:e.message.includes(`@ts-nocheck`)?{category:`ts-nocheck`,labels:[`ts-nocheck`,`file-wide-suppression`],reason:`ts-nocheck suppresses type checking for the whole file and tends to hide deeper debt.`,score:7,severity:`high`}:e.message.includes(`@ts-expect-error`)?{category:`weak-ts-expect-error`,labels:[`ts-expect-error`,`weak-description`],reason:`ts-expect-error without a useful explanation makes the suppressed type debt harder to revisit safely.`,score:4,severity:`medium`}:_t[`@typescript-eslint/ban-ts-comment`]??{category:`ts-directive`,labels:[`ts-directive`,`compiler-suppression`],reason:`A TypeScript compiler directive is muting or weakening type feedback.`,score:4,severity:`medium`},Ct=(e,t,n)=>{let r=!t.ruleId&&Z.test(t.message)?gt:t.ruleId===`@typescript-eslint/ban-ts-comment`?St(t):t.ruleId?_t[t.ruleId]:null;if(!r)return null;let i=[...r.labels,...n.isSharedSurface?[`shared-surface`]:[],...n.isTest?[`test-only`]:[],...n.suppressionCount>=3?[`dense-suppressions`]:[]],a=[r.reason,t.message,...n.isSharedSurface?[`The suppression sits in a shared surface, so it can hide issues for multiple callers.`]:[],...n.isTest?[`The suppression is test-only, so cleanup urgency is usually lower than production code.`]:[],...n.suppressionCount>=3?[`This file already contains ${n.suppressionCount} suppression directives, which usually points to accumulated local debt.`]:[]],o=Math.max(1,r.score+(n.isSharedSurface?2:0)+(n.isTest?-2:0)+ +(n.suppressionCount>=3));return{category:r.category,column:t.column,confidence:t.ruleId?`high`:`medium`,file:e,labels:i,line:t.line,reasons:a,ruleId:t.ruleId??`eslint/unused-disable-directive`,score:o,severity:r.severity}},wt=async(e,t)=>{if(t.top<1)throw new y(`--top must be at least 1.`);let n=await b({config:await xt(e).toConfigs(),context:t,ruleIds:X}),r=n.filter(e=>e.messages.some(e=>!e.ruleId&&Z.test(e.message)||(e.ruleId?X.includes(e.ruleId):!1))),i=new Map;await Promise.all(r.map(async e=>{i.set(e.filePath,await bt(t.cwd,e.filePath))}));let a=r.flatMap(e=>e.messages.flatMap(t=>{let n=i.get(e.filePath);if(!n)return[];let r=Ct(e.filePath,t,n);return r?[r]:[]})),o=x(a,t.top),s=o[0],c=a.filter(e=>e.category===`unused-disable`||e.category===`unused-enable`).length,l=a.filter(e=>e.category===`broad-disable`).length,u=a.filter(e=>e.labels?.includes(`compiler-suppression`)).length,d=o.filter(e=>e.labels?.includes(`dense-suppressions`)).length;return{fileCount:n.length,findingsCount:a.length,nextStep:s?`Start with \`${s.file}\` and remove stale or broad suppressions there before touching lower-risk files.`:`No suppression findings were reported by the current analyzer ruleset.`,suggestedOrder:o.map(e=>e.file),summaries:o,summaryLines:[`Unused directive findings: \`${c}\``,`Broad disable findings: \`${l}\``,`TypeScript directive findings: \`${u}\``,`Dense suppression files: \`${d}\``],title:`Suppressions Analysis`}},Tt={"@typescript-eslint/ban-ts-comment":`error`,"@typescript-eslint/no-base-to-string":`error`,"@typescript-eslint/no-explicit-any":`error`,"@typescript-eslint/no-unnecessary-condition":`error`,"@typescript-eslint/no-unsafe-argument":`error`,"@typescript-eslint/no-unsafe-assignment":`error`,"@typescript-eslint/no-unsafe-call":`error`,"@typescript-eslint/no-unsafe-enum-comparison":`error`,"@typescript-eslint/no-unsafe-member-access":`error`,"@typescript-eslint/no-unsafe-return":`error`,"@typescript-eslint/no-unsafe-unary-minus":`error`},Et=f,Dt=Object.keys(Tt),Ot={"@typescript-eslint/ban-ts-comment":`Type suppression hides an actual type-system signal.`,"@typescript-eslint/no-base-to-string":`Implicit object stringification often hides an unexpected runtime representation.`,"@typescript-eslint/no-explicit-any":`Explicit any weakens the local type boundary.`,"@typescript-eslint/no-unnecessary-condition":`A condition is unnecessary according to TypeScript and can mislead control-flow analysis.`,"@typescript-eslint/no-unsafe-argument":`Unsafe argument passes an untyped value across a typed boundary.`,"@typescript-eslint/no-unsafe-assignment":`Unsafe assignment can spread an untyped value further.`,"@typescript-eslint/no-unsafe-call":`Unsafe call means runtime behavior depends on an untyped value.`,"@typescript-eslint/no-unsafe-enum-comparison":`Unsafe enum comparison can make a branch look valid while comparing incompatible domains.`,"@typescript-eslint/no-unsafe-member-access":`Unsafe member access depends on unchecked runtime shape.`,"@typescript-eslint/no-unsafe-return":`Unsafe return leaks a weakly typed value through an API boundary.`,"@typescript-eslint/no-unsafe-unary-minus":`Unsafe unary minus depends on unchecked numeric coercion.`},kt={"@typescript-eslint/ban-ts-comment":5,"@typescript-eslint/no-base-to-string":2,"@typescript-eslint/no-explicit-any":4,"@typescript-eslint/no-unnecessary-condition":2,"@typescript-eslint/no-unsafe-argument":4,"@typescript-eslint/no-unsafe-assignment":3,"@typescript-eslint/no-unsafe-call":3,"@typescript-eslint/no-unsafe-enum-comparison":3,"@typescript-eslint/no-unsafe-member-access":3,"@typescript-eslint/no-unsafe-return":4,"@typescript-eslint/no-unsafe-unary-minus":3},At={"@typescript-eslint/ban-ts-comment":`high`,"@typescript-eslint/no-base-to-string":`low`,"@typescript-eslint/no-explicit-any":`high`,"@typescript-eslint/no-unnecessary-condition":`low`,"@typescript-eslint/no-unsafe-argument":`high`,"@typescript-eslint/no-unsafe-assignment":`medium`,"@typescript-eslint/no-unsafe-call":`medium`,"@typescript-eslint/no-unsafe-enum-comparison":`medium`,"@typescript-eslint/no-unsafe-member-access":`medium`,"@typescript-eslint/no-unsafe-return":`high`,"@typescript-eslint/no-unsafe-unary-minus":`medium`},jt=/(^|\/)(test|tests|__tests__|__mocks__)\//u,Mt=/\.(spec|test)\.(mts|ts|tsx)$/u,Nt=/(^|\/)(shared|lib|libs|utils|helpers)\//u,Pt=/\.vue$/u,Ft=/(^|\/)(components|ui)\//u,It=[/\bdefineProps\s*</u,/\bdefineProps\s*\(/u,/\bdefineEmits\s*</u,/\bdefineEmits\s*\(/u,/\bdefineSlots\s*</u,/\bdefineSlots\s*\(/u,/\bdefineExpose\s*\(/u,/\bdefineModel\s*</u,/\bdefineModel\s*\(/u,/\bdefineComponent\s*\(/u],Lt=new Set([`@typescript-eslint/no-explicit-any`,`@typescript-eslint/no-unsafe-return`]),Rt=new Set([`@typescript-eslint/no-unnecessary-condition`]),zt=e=>e.endsWith(`.tsx`)?g.ScriptKind.TSX:g.ScriptKind.TS,Bt=e=>(g.getCombinedModifierFlags(e)&g.ModifierFlags.Export)!==0,Vt=e=>e.statements.flatMap(t=>{if(g.isExportAssignment(t)||g.isExportDeclaration(t)||Bt(t)){let n=e.getLineAndCharacterOfPosition(t.getStart(e)).line+1;return[{endLine:e.getLineAndCharacterOfPosition(t.getEnd()).line+1,startLine:n}]}return[]}),Ht=e=>e.split(`
6
+ `).flatMap((e,t)=>It.some(t=>t.test(e))?[t+1]:[]),Ut=async(e,t)=>{let n=s.join(e,t),r=await d(n,`utf8`);if(Pt.test(t)){let e=Ht(r);return{exportedRanges:[],hasExports:e.length>0,isSharedUtility:Nt.test(t)||Ft.test(t),isTest:jt.test(t)||Mt.test(t),isVueComponent:!0,vueContractLines:e}}let i=Vt(g.createSourceFile(n,r,g.ScriptTarget.Latest,!0,zt(n)));return{exportedRanges:i,hasExports:i.length>0,isSharedUtility:Nt.test(t),isTest:jt.test(t)||Mt.test(t),isVueComponent:!1,vueContractLines:[]}},Wt=(e,t)=>e!==void 0&&t.some(t=>e>=t.startLine&&e<=t.endLine),Gt=(e,t)=>e!==void 0&&t.some(t=>Math.abs(t-e)<=2),Kt=(e,t)=>Rt.has(t.ruleId??``)?{category:`control-flow-signal`,labels:[`control-flow-signal`,`unnecessary-condition`],reason:`The condition is impossible or redundant according to the current TypeScript model.`,scoreDelta:0}:e.isTest?{category:`test-only`,labels:[`test-only`],reason:`Test-only type holes usually have lower migration urgency.`,scoreDelta:-2}:Lt.has(t.ruleId??``)&&Wt(t.line,e.exportedRanges)?{category:`exported-api`,labels:[`exported-api`],reason:`The finding sits inside an exported declaration and can leak outward.`,scoreDelta:4}:e.isVueComponent&&Lt.has(t.ruleId??``)&&Gt(t.line,e.vueContractLines)?{category:`vue-component-contract`,labels:[`vue-component-contract`],reason:`The finding appears near a Vue component contract and can leak through props, emits, or exposed bindings.`,scoreDelta:4}:e.isVueComponent&&e.isSharedUtility?{category:`shared-vue-component`,labels:[`shared-vue-component`],reason:`Shared Vue components can spread weak typing through reusable props and emits contracts.`,scoreDelta:2}:e.hasExports&&s.basename(t.file)===`index.ts`?{category:`public-entry`,labels:[`public-entry`],reason:`The file looks like a public entrypoint, so weak typing spreads quickly.`,scoreDelta:3}:e.isSharedUtility?{category:`shared-utility`,labels:[`shared-utility`],reason:`Shared utilities tend to amplify weak typing across multiple callers.`,scoreDelta:2}:{category:`internal`,labels:[`internal`],reason:`The finding appears to be internal implementation detail.`,scoreDelta:0},qt=e=>e.clone().append({files:[`**/*.vue`],languageOptions:{parserOptions:{extraFileExtensions:[`.vue`],parser:p,projectService:!0,sourceType:`module`}},name:`productive-eslint/analyze-types-vue-parser`}).append({files:[`**/*.ts`,`**/*.tsx`,`**/*.vue`],name:`productive-eslint/analyze-types`,plugins:{"@typescript-eslint":Et},rules:Tt}),Jt=(e,t)=>!t.ruleId||!Dt.includes(t.ruleId)?null:{category:`internal`,column:t.column,confidence:`high`,file:e,labels:[],line:t.line,reasons:[Ot[t.ruleId]??t.message,t.message],ruleId:t.ruleId,score:kt[t.ruleId]??1,severity:At[t.ruleId]??`low`},Yt=async(e,t)=>{let n=await b({config:await qt(e).toConfigs(),context:t,ruleIds:Dt}),r=new Map;await Promise.all(n.map(async e=>{r.set(e.filePath,await Ut(t.cwd,e.filePath))}));let i=n.flatMap(e=>{let t=r.get(e.filePath);return e.messages.flatMap(n=>{let r=Jt(e.filePath,n);if(!r||!t)return r?[r]:[];let i=Kt(t,r);return[{...r,category:i.category,labels:[...new Set([...r.labels??[],...i.labels])],reasons:[...r.reasons,i.reason],score:Math.max(1,r.score+i.scoreDelta)}]})});if(t.top<1)throw new y(`--top must be at least 1.`);let a=x(i,t.top),o=a[0],s=o?.findings.every(e=>e.category===`control-flow-signal`)??!1,c=i.filter(e=>e.category===`exported-api`||e.category===`public-entry`).length,l=i.filter(e=>e.category===`vue-component-contract`||e.category===`shared-vue-component`).length,u=i.filter(e=>e.category===`shared-utility`).length,d=i.filter(e=>e.category===`control-flow-signal`).length,f=i.filter(e=>e.category===`test-only`).length;return{fileCount:n.length,findingsCount:i.length,nextStep:o?s?`Start with \`${o.file}\` and verify whether the redundant conditions are stale defensive code or outdated types.`:`Start with \`${o.file}\` and remove the highest-scoring type holes first.`:`No type analyzer findings were reported by the current analyzer ruleset.`,suggestedOrder:a.map(e=>e.file),summaries:a,summaryLines:[`Exported or public-surface findings: \`${c}\``,`Vue component contract findings: \`${l}\``,`Shared-utility findings: \`${u}\``,`Control-flow signal findings: \`${d}\``,`Test-only findings: \`${f}\``],title:`Types Analysis`}},Xt=[{analyze:Yt,label:`Types`,topic:`types`},{analyze:O,label:`Architecture`,topic:`architecture`},{analyze:z,label:`Complexity`,topic:`complexity`},{analyze:j,label:`Async`,topic:`async`},{analyze:wt,label:`Suppressions`,topic:`suppressions`},{analyze:V,label:`Dead Code`,topic:`dead-code`},{analyze:W,label:`Imports`,topic:`imports`},{analyze:w,label:`API`,topic:`api`}],Zt=e=>e>=10?`high`:e>=4?`medium`:`low`,Qt=(e,t)=>e.summaries.map(e=>({category:t.topic,confidence:`medium`,file:e.file,labels:[`risk`,`risk:${t.topic}`,...e.labels??[]],reasons:[`${t.label} analyzer reported score ${e.score} for this file.`,...(e.reasons??[]).slice(0,2)],ruleId:`productive/risk-${t.topic}`,score:e.score,severity:Zt(e.score)})),$t=async(e,t)=>{if(t.top<1)throw new y(`--top must be at least 1.`);let n=[];for(let r of Xt)n.push({analyzer:r,report:await r.analyze(e,t)});let r=x(n.flatMap(({analyzer:e,report:t})=>Qt(t,e)),t.top),i=r[0];return{fileCount:Math.max(0,...n.map(({report:e})=>e.fileCount)),findingsCount:n.reduce((e,{report:t})=>e+t.findingsCount,0),nextStep:i?`Start with \`${i.file}\`; it is the highest combined-risk file across the universal analyzer set.`:`No risk findings were reported by the universal analyzer set.`,suggestedOrder:r.map(e=>e.file),summaries:r,summaryLines:n.map(({analyzer:e,report:t})=>`${e.label} findings: \`${t.findingsCount}\``),title:`Risk Analysis`,topGroups:n.map(({analyzer:e,report:t})=>({count:t.findingsCount,label:e.label})).filter(e=>e.count>0)}},en={"rxjs/no-async-subscribe":`error`,"rxjs/no-exposed-subjects":`error`,"rxjs/no-ignored-error":`error`,"rxjs/no-ignored-observable":`error`,"rxjs/no-ignored-replay-buffer":`error`,"rxjs/no-ignored-subscribe":`error`,"rxjs/no-ignored-subscription":`error`,"rxjs/no-nested-subscribe":`error`,"rxjs/no-subject-unsubscribe":`error`,"rxjs/no-subject-value":`error`,"rxjs/no-topromise":`error`,"rxjs/no-unbound-methods":`error`,"rxjs/no-unsafe-switchmap":`error`,"rxjs/no-unsafe-takeuntil":`error`,"rxjs/throw-error":`error`},tn=_,Q=Object.keys(en),nn={"rxjs/no-async-subscribe":{category:`subscription-lifecycle`,labels:[`subscription-lifecycle`,`async-subscribe`],reason:`Async subscribe callbacks hide promise handling inside subscription lifecycle code.`,score:5,severity:`high`},"rxjs/no-exposed-subjects":{category:`state-exposure`,labels:[`state-exposure`,`subject-exposure`],reason:`Exposed subjects let callers push into internal reactive state.`,score:6,severity:`high`},"rxjs/no-ignored-error":{category:`error-handling`,labels:[`error-handling`,`missing-error-handler`],reason:`The subscription does not make the observable error path explicit.`,score:5,severity:`high`},"rxjs/no-ignored-observable":{category:`ignored-flow`,labels:[`ignored-flow`,`ignored-observable`],reason:`An observable is created but not returned, subscribed, or otherwise used.`,score:5,severity:`medium`},"rxjs/no-ignored-replay-buffer":{category:`state-exposure`,labels:[`state-exposure`,`unbounded-replay-buffer`],reason:`ReplaySubject without an explicit buffer size can retain more state than intended.`,score:4,severity:`medium`},"rxjs/no-ignored-subscribe":{category:`subscription-lifecycle`,labels:[`subscription-lifecycle`,`ignored-subscribe`],reason:`A subscription is started without making lifecycle ownership explicit.`,score:6,severity:`high`},"rxjs/no-ignored-subscription":{category:`subscription-lifecycle`,labels:[`subscription-lifecycle`,`ignored-subscription`],reason:`A subscription handle is ignored, making cleanup hard to verify.`,score:6,severity:`high`},"rxjs/no-nested-subscribe":{category:`flow-composition`,labels:[`flow-composition`,`nested-subscribe`],reason:`Nested subscriptions usually hide ordering and cleanup semantics.`,score:5,severity:`medium`},"rxjs/no-subject-unsubscribe":{category:`subscription-lifecycle`,labels:[`subscription-lifecycle`,`subject-unsubscribe`],reason:`Unsubscribing a Subject directly can break downstream reactive state unexpectedly.`,score:5,severity:`high`},"rxjs/no-subject-value":{category:`state-access`,labels:[`state-access`,`subject-value`],reason:`Reading Subject value directly bypasses normal observable flow.`,score:4,severity:`medium`},"rxjs/no-topromise":{category:`migration`,labels:[`migration`,`deprecated-api`],reason:`toPromise is deprecated and should be migrated to firstValueFrom or lastValueFrom.`,score:4,severity:`medium`},"rxjs/no-unbound-methods":{category:`flow-composition`,labels:[`flow-composition`,`unbound-method`],reason:`Passing unbound methods into reactive callbacks can lose the expected receiver.`,score:4,severity:`medium`},"rxjs/no-unsafe-switchmap":{category:`flow-cancellation`,labels:[`flow-cancellation`,`unsafe-switchmap`],reason:`Unsafe switchMap usage can cancel work that should be preserved.`,score:6,severity:`high`},"rxjs/no-unsafe-takeuntil":{category:`subscription-lifecycle`,labels:[`subscription-lifecycle`,`unsafe-takeuntil`],reason:`Operators after takeUntil can keep work alive past the intended teardown point.`,score:6,severity:`high`},"rxjs/throw-error":{category:`error-handling`,labels:[`error-handling`,`throw-error-factory`],reason:`RxJS throwError should preserve lazy error creation for reliable stacks and timing.`,score:4,severity:`medium`}},rn=e=>e.clone().append({files:[o],name:`productive-eslint/analyze-rxjs`,plugins:{rxjs:tn},rules:en}),an=(e,t)=>{if(!t.ruleId||!Q.includes(t.ruleId))return null;let n=nn[t.ruleId];return n?{category:n.category,column:t.column,confidence:`high`,file:e,labels:n.labels,line:t.line,reasons:[n.reason,t.message],ruleId:t.ruleId,score:n.score,severity:n.severity}:null},on=async(e,t)=>{if(t.top<1)throw new y(`--top must be at least 1.`);let n=await b({config:await rn(e).toConfigs(),context:t,ruleIds:Q}),r=n.flatMap(e=>e.messages.flatMap(t=>{let n=an(e.filePath,t);return n?[n]:[]})),i=x(r,t.top),a=i[0],o=r.filter(e=>e.category===`subscription-lifecycle`).length,s=r.filter(e=>[`flow-cancellation`,`flow-composition`,`ignored-flow`].includes(e.category??``)).length,c=r.filter(e=>[`state-access`,`state-exposure`].includes(e.category??``)).length,l=r.filter(e=>e.category===`error-handling`).length;return{fileCount:n.length,findingsCount:r.length,nextStep:a?`Start with \`${a.file}\` and clarify subscription ownership or reactive state boundaries first.`:`No RxJS flow findings were reported by the current analyzer ruleset.`,suggestedOrder:i.map(e=>e.file),summaries:i,summaryLines:[`Subscription lifecycle findings: \`${o}\``,`Reactive flow findings: \`${s}\``,`State boundary findings: \`${c}\``,`Error handling findings: \`${l}\``],title:`RxJS Analysis`}},sn={"vue/no-arrow-functions-in-watch":`error`,"vue/no-async-in-computed-properties":`error`,"vue/no-expose-after-await":`error`,"vue/no-lifecycle-after-await":`error`,"vue/no-mutating-props":`error`,"vue/no-ref-object-reactivity-loss":`error`,"vue/no-required-prop-with-default":`error`,"vue/no-side-effects-in-computed-properties":`error`,"vue/no-use-v-if-with-v-for":`error`,"vue/no-v-html":`error`,"vue/no-watch-after-await":`error`,"vue/require-explicit-emits":`error`,"vue/require-typed-object-prop":`error`,"vue/require-typed-ref":`error`,"vue/require-v-for-key":`error`,"vue/valid-v-html":`error`},cn=Object.keys(sn),ln={"vue/no-arrow-functions-in-watch":{category:`reactivity-hazard`,labels:[`reactivity-hazard`,`watch-this-binding`],reason:`Arrow functions in watchers can lose the component instance binding expected by Options API code.`,score:4,severity:`medium`},"vue/no-async-in-computed-properties":{category:`reactivity-hazard`,labels:[`reactivity-hazard`,`async-computed`],reason:`Async work inside computed properties makes reactivity timing unreliable.`,score:6,severity:`high`},"vue/no-expose-after-await":{category:`lifecycle-hazard`,labels:[`lifecycle-hazard`,`expose-after-await`],reason:`Bindings exposed after await can miss the synchronous setup contract.`,score:5,severity:`medium`},"vue/no-lifecycle-after-await":{category:`lifecycle-hazard`,labels:[`lifecycle-hazard`,`lifecycle-after-await`],reason:`Lifecycle hooks registered after await can miss component setup timing.`,score:5,severity:`medium`},"vue/no-mutating-props":{category:`component-contract`,labels:[`component-contract`,`prop-mutation`],reason:`Prop mutation breaks parent-owned component contracts.`,score:6,severity:`high`},"vue/no-ref-object-reactivity-loss":{category:`reactivity-hazard`,labels:[`reactivity-hazard`,`ref-reactivity-loss`],reason:`Destructuring or extracting ref object values can disconnect code from reactivity updates.`,score:5,severity:`medium`},"vue/no-required-prop-with-default":{category:`component-contract`,labels:[`component-contract`,`required-prop-default`],reason:`A prop cannot be both required from the parent and owned by a local default contract.`,score:4,severity:`medium`},"vue/no-side-effects-in-computed-properties":{category:`reactivity-hazard`,labels:[`reactivity-hazard`,`computed-side-effect`],reason:`Computed properties should stay pure so dependency tracking remains predictable.`,score:6,severity:`high`},"vue/no-use-v-if-with-v-for":{category:`template-correctness`,labels:[`template-correctness`,`v-if-with-v-for`],reason:`Combining v-if and v-for on one element can produce surprising render behavior.`,score:4,severity:`medium`},"vue/no-v-html":{category:`security`,labels:[`security`,`unsafe-html`],reason:`Raw HTML rendering can expose XSS risk when input is not fully trusted.`,score:7,severity:`high`},"vue/no-watch-after-await":{category:`lifecycle-hazard`,labels:[`lifecycle-hazard`,`watch-after-await`],reason:`Watchers registered after await can miss component lifecycle cleanup expectations.`,score:5,severity:`medium`},"vue/require-explicit-emits":{category:`component-contract`,labels:[`component-contract`,`emits-contract`],reason:`Component events should be declared as part of the public contract.`,score:4,severity:`medium`},"vue/require-typed-object-prop":{category:`component-contract`,labels:[`component-contract`,`object-prop-type`],reason:`Object props should declare their runtime shape precisely enough for safe component use.`,score:4,severity:`medium`},"vue/require-typed-ref":{category:`component-contract`,labels:[`component-contract`,`ref-type`],reason:`Untyped refs weaken component state contracts and make later reactive reads ambiguous.`,score:3,severity:`medium`},"vue/require-v-for-key":{category:`template-correctness`,labels:[`template-correctness`,`missing-key`],reason:`Missing keys make list updates harder for Vue to reconcile safely.`,score:4,severity:`medium`},"vue/valid-v-html":{category:`template-correctness`,labels:[`template-correctness`,`invalid-v-html`],reason:`Invalid v-html usage points to a broken template contract.`,score:4,severity:`medium`}},un=e=>e.clone().append({files:[t],name:`productive-eslint/analyze-vue`,rules:sn}),dn=(e,t)=>{if(!t.ruleId||!cn.includes(t.ruleId))return null;let n=ln[t.ruleId];return n?{category:n.category,column:t.column,confidence:`high`,file:e,labels:n.labels,line:t.line,reasons:[n.reason,t.message],ruleId:t.ruleId,score:n.score,severity:n.severity}:null},$={api:w,architecture:O,async:j,complexity:z,"dead-code":V,imports:W,migrations:ut,risk:$t,rxjs:on,suppressions:wt,types:Yt,vue:async(e,t)=>{if(t.top<1)throw new y(`--top must be at least 1.`);let n=await b({config:await un(e).toConfigs(),context:t,ruleIds:cn}),r=n.flatMap(e=>e.messages.flatMap(t=>{let n=dn(e.filePath,t);return n?[n]:[]})),i=x(r,t.top),a=i[0],o=r.filter(e=>e.category===`component-contract`).length,s=r.filter(e=>e.category===`reactivity-hazard`).length,c=r.filter(e=>e.category===`security`).length,l=r.filter(e=>e.category===`lifecycle-hazard`).length;return{fileCount:n.length,findingsCount:r.length,nextStep:a?`Start with \`${a.file}\` and fix component contract or reactivity hazards before template style cleanup.`:`No Vue semantic findings were reported by the current analyzer ruleset.`,suggestedOrder:i.map(e=>e.file),summaries:i,summaryLines:[`Component contract findings: \`${o}\``,`Reactivity hazard findings: \`${s}\``,`Lifecycle hazard findings: \`${l}\``,`Security findings: \`${c}\``],title:`Vue Analysis`}}},fn=e=>e in $,pn=async e=>{if(!fn(e.topic))throw new y(`Unknown analyzer topic "${e.topic}".`);let{composer:t}=await ie(e.cwd),n=$[e.topic];return v(await n(t,e))},mn=e=>{let[t,...n]=e;if(!t)throw new y(`Missing analyzer topic. Example: productive-eslint analyze types`);let r={cwd:c.cwd(),exclude:[],include:[],top:10,topic:t};for(let e=0;e<n.length;e+=1){let t=n[e],i=n[e+1];if(t===`--cwd`){if(!i)throw new y(`Missing value for --cwd.`);r.cwd=s.resolve(i),e+=1;continue}if(t===`--include`){if(!i)throw new y(`Missing value for --include.`);r.include.push(i),e+=1;continue}if(t===`--exclude`){if(!i)throw new y(`Missing value for --exclude.`);r.exclude.push(i),e+=1;continue}if(t===`--top`){if(!i)throw new y(`Missing value for --top.`);r.top=Number.parseInt(i,10),e+=1;continue}throw new y(`Unknown option "${t}".`)}if(!Number.isInteger(r.top)||r.top<1)throw new y(`--top must be a positive integer.`);return r};(async()=>{let[,,e,...t]=c.argv;if(e!==`analyze`)throw new y(`Unknown command. Supported command: productive-eslint analyze <topic>`);let n=await pn(mn(t));c.stdout.write(n)})().catch(e=>{let t=e instanceof Error?e.message:String(e);c.stderr.write(`${t}\n`),c.exitCode=1});export{};
@@ -1,21 +1,16 @@
1
- import { FlatConfigComposer } from "eslint-flat-config-utils";
2
1
  import { Linter } from "eslint";
2
+ import { FlatConfigComposer } from "eslint-flat-config-utils";
3
3
 
4
- //#region src/utils/strictness.d.ts
4
+ //#region src/utils/presets.d.ts
5
5
  /**
6
- * Strictness presets for ESLint config.
6
+ * Presets for ESLint config.
7
7
  *
8
- * - AutoFixable: only rules with ESLint autofix support
9
- * - Easy: autoFixable + agent-friendly rules (trivial manual fixes)
10
- * - Medium: easy + remaining rules from current config
11
- * - Hard: easy + medium + extra rules (user-defined)
8
+ * - AutoFixable: only rules with ESLint autofix support.
9
+ * - Recommended: autofixable rules plus mechanical non-autofixable rules.
12
10
  */
13
- /** Strictness presets for ESLint config. */
14
- declare enum StrictnessPreset {
11
+ declare enum Preset {
15
12
  AUTO_FIXABLE = "autoFixable",
16
- EASY = "easy",
17
- HARD = "hard",
18
- MEDIUM = "medium"
13
+ RECOMMENDED = "recommended"
19
14
  }
20
15
  //#endregion
21
16
  //#region src/index.config.d.ts
@@ -23,10 +18,10 @@ declare enum StrictnessPreset {
23
18
  interface IOptions {
24
19
  /** Files to ignore. Defaults to empty array. */
25
20
  ignores?: string[];
21
+ /** Preset: autoFixable or recommended. Defaults to recommended. */
22
+ preset?: Preset;
26
23
  /** Enable RxJS rules. Auto-detected from installed packages when not set. */
27
24
  rxjs?: boolean;
28
- /** Preset: autoFixable, easy, medium, or hard. Defaults to hard. */
29
- strictness?: StrictnessPreset;
30
25
  /** Enable Vue rules. Auto-detected from installed packages when not set. */
31
26
  vue?: boolean;
32
27
  }
@@ -36,17 +31,16 @@ interface IOptions {
36
31
  * @param options The options for generating the ESLint configuration.
37
32
  * @param options.ignores Additional glob patterns to ignore.
38
33
  * @param options.rxjs Enable RxJS rules. Auto-detected when not set.
39
- * @param options.strictness Preset: autoFixable (only auto-fixable rules), easy
40
- * (autoFixable + agent-friendly), medium (easy + rest), or hard (easy +
41
- * medium + user rules). Defaults to hard.
34
+ * @param options.preset Preset: autoFixable (only auto-fixable rules) or
35
+ * recommended (permanent mechanical baseline). Defaults to recommended.
42
36
  * @param options.vue Enable Vue rules. Auto-detected when not set.
43
37
  * @returns The generated ESLint configuration.
44
38
  */
45
39
  declare const createConfig: ({
46
40
  ignores,
41
+ preset,
47
42
  rxjs,
48
- strictness,
49
43
  vue
50
- }: IOptions) => FlatConfigComposer<Linter.Config>;
44
+ }?: IOptions) => FlatConfigComposer<Linter.Config>;
51
45
  //#endregion
52
- export { IOptions, StrictnessPreset, createConfig as default };
46
+ export { IOptions, Preset, createConfig };