@@ -51,7 +51,40 @@ const logger = getDefaultLogger()
5151// safely infer. Each one IS fixable — the AI step does the work.
5252// The deterministic linter already handled the unambiguous shapes;
5353// what remains is the structural-rewrite set.
54- const AI_HANDLED_RULES = new Set ( [ 'socket/inclusive-language' , 'socket/max-file-lines' , 'socket/no-fetch-prefer-http-request' , 'socket/no-placeholders' , 'socket/personal-path-placeholders' , 'socket/prefer-async-spawn' , 'socket/prefer-exists-sync' , 'socket/prefer-node-builtin-imports' , 'socket/prefer-undefined-over-null' ] )
54+ const AI_HANDLED_RULES = new Set ( [
55+ // master/slave — context decides main/primary/controller vs
56+ // replica/worker. Other forms (whitelist/blacklist/etc.) auto-fix.
57+ 'socket/inclusive-language' ,
58+ // Literal username in a user-home path. In source: substitute a
59+ // placeholder / env-var / delete. In WASM or generated bundles:
60+ // the bundler is leaking the path — fix the build config.
61+ 'socket/personal-path-placeholders' ,
62+ // fs.access / fs.stat existence checks. AI rewrites the try/catch
63+ // → if/else and preserves metadata calls when the result is
64+ // destructured. Wrapper-name shapes (fileExists / pathExists /
65+ // isFile / isDir) auto-fix deterministically.
66+ 'socket/prefer-exists-sync' ,
67+ // node:fs default/namespace where references are "weird" (computed
68+ // access, passed as a value, reassigned). Plain `fs.X` shapes
69+ // auto-fix via scope rename.
70+ 'socket/prefer-node-builtin-imports' ,
71+ // spawnSync where the call site isn't already in async context or
72+ // its return value is consumed (assignment, property access).
73+ // await/expression-statement shapes auto-fix.
74+ 'socket/prefer-async-spawn' ,
75+ // null whose surrounding type annotation also mentions null. AI
76+ // flips BOTH the annotation and the value in lockstep through the
77+ // function signatures / interfaces / return types involved.
78+ // Cross-file ripple is handled by per-file passes on the next run.
79+ 'socket/prefer-undefined-over-null' ,
80+ // File splitting needs to choose natural seams.
81+ 'socket/max-file-lines' ,
82+ // Placeholder finishes need actual implementation.
83+ 'socket/no-placeholders' ,
84+ // No-fetch needs httpJson/httpText/httpRequest decision based on
85+ // how the response is consumed.
86+ 'socket/no-fetch-prefer-http-request' ,
87+ ] )
5588
5689interface OxlintMessage {
5790 ruleId ?: string
@@ -75,7 +108,7 @@ interface CliArgs {
75108 passthrough : string [ ]
76109}
77110
78- export function parseArgs ( argv : readonly string [ ] ) : CliArgs {
111+ function parseArgs ( argv : readonly string [ ] ) : CliArgs {
79112 const passthrough : string [ ] = [ ]
80113 let noAi = false
81114 let staged = false
@@ -100,7 +133,7 @@ export function parseArgs(argv: readonly string[]): CliArgs {
100133 return { all, noAi, passthrough, staged }
101134}
102135
103- export async function runLintJson (
136+ async function runLintJson (
104137 passthrough : readonly string [ ] ,
105138) : Promise < OxlintFile [ ] > {
106139 // Run oxlint directly with --format=json. Bypass `pnpm run lint`
@@ -147,7 +180,7 @@ export async function runLintJson(
147180 }
148181}
149182
150- export function bucketFindings ( files : OxlintFile [ ] ) : Map < string , OxlintMessage [ ] > {
183+ function bucketFindings ( files : OxlintFile [ ] ) : Map < string , OxlintMessage [ ] > {
151184 const byFile = new Map < string , OxlintMessage [ ] > ( )
152185 for ( const f of files ) {
153186 const handled = f . messages . filter (
@@ -173,6 +206,7 @@ export function bucketFindings(files: OxlintFile[]): Map<string, OxlintMessage[]
173206const RULE_GUIDANCE = {
174207 // oxlint-disable-next-line socket/prefer-undefined-over-null -- null-prototype object literal.
175208 __proto__ : null ,
209+ // oxlint-disable-next-line socket/inclusive-language -- rule guidance string documents the legacy terms it scans for.
176210 'socket/inclusive-language' :
177211 'Replace `master`/`slave` with the contextually correct term: `main` (branch), `primary`/`controller` (process), `replica`/`worker`/`secondary`/`follower` (subordinate). Read the surrounding code to pick the right one. Do not autofix when an external API field name forces the legacy term — leave a `// inclusive-language: external-api` comment instead.' ,
178212 'socket/personal-path-placeholders' :
@@ -193,7 +227,7 @@ const RULE_GUIDANCE = {
193227 'Replace `fetch(url, opts)` with the right helper from `@socketsecurity/lib/http-request`: `httpJson` when the caller calls `.json()` on the response, `httpText` when it calls `.text()`, `httpRequest` for raw access. Add the named import.' ,
194228} as unknown as Readonly < Record < string , string > >
195229
196- export function renderFindings ( findings : OxlintMessage [ ] , _rel : string ) : string {
230+ function renderFindings ( findings : OxlintMessage [ ] , _rel : string ) : string {
197231 return findings
198232 . map (
199233 f =>
@@ -207,7 +241,7 @@ export function renderFindings(findings: OxlintMessage[], _rel: string): string
207241 . join ( '\n' )
208242}
209243
210- export function renderRuleGuidance ( findings : OxlintMessage [ ] ) : string {
244+ function renderRuleGuidance ( findings : OxlintMessage [ ] ) : string {
211245 const seen = new Set < string > ( )
212246 for ( const f of findings ) {
213247 if ( f . ruleId ) {
@@ -245,7 +279,7 @@ export function renderRuleGuidance(findings: OxlintMessage[]): string {
245279 * the guidance block carries enough context), and how to use Edit /
246280 * Read. Adding boilerplate dilutes the instructions.
247281 */
248- export function buildPrompt ( filePath : string , findings : OxlintMessage [ ] ) : string {
282+ function buildPrompt ( filePath : string , findings : OxlintMessage [ ] ) : string {
249283 const rel = path . relative ( process . cwd ( ) , filePath )
250284 const findingsBlock = renderFindings ( findings , rel )
251285 const rulesBlock = renderRuleGuidance ( findings )
@@ -270,7 +304,7 @@ ${rulesBlock}
270304<output>One short sentence summarizing what you changed. No markdown, no code blocks, no preamble.</output>`
271305}
272306
273- export async function runClaudeFix (
307+ async function runClaudeFix (
274308 _filePath : string ,
275309 prompt : string ,
276310 cwd : string ,
@@ -324,7 +358,7 @@ export async function runClaudeFix(
324358 return { exitCode, stderr, stdout }
325359}
326360
327- export async function hasClaudeCli ( ) : Promise < boolean > {
361+ async function hasClaudeCli ( ) : Promise < boolean > {
328362 try {
329363 const result = await spawn ( 'claude' , [ '--version' ] , {
330364 shell : process . platform === 'win32' ,
0 commit comments