import js from '@eslint/js'; import tseslint from 'typescript-eslint'; import globals from 'globals'; import pluginN from 'eslint-plugin-n'; import noOnlyTests from 'eslint-plugin-no-only-tests'; import { existsSync } from 'fs'; import { join, dirname } from 'path'; import { fileURLToPath } from 'url'; const __dirname = dirname(fileURLToPath(import.meta.url)); // Local plugin with three custom AST rules import noSourceGrep from './eslint-rules/no-source-grep.cjs'; import noMagicSleepInTests from './eslint-rules/no-magic-sleep-in-tests.cjs'; import noElapsedAssertion from './eslint-rules/no-elapsed-assertion.cjs'; const localPlugin = { rules: { 'no-source-grep': noSourceGrep, 'no-magic-sleep-in-tests': noMagicSleepInTests, 'no-elapsed-assertion': noElapsedAssertion, }, }; // Generated bin/lib files — never lint const GENERATED_CJS_IGNORES = [ 'get-shit-done/bin/lib/command-aliases.cjs', 'get-shit-done/bin/lib/configuration.cjs', 'get-shit-done/bin/lib/decisions.cjs', 'get-shit-done/bin/lib/phase-lifecycle.cjs', 'get-shit-done/bin/lib/plan-scan.cjs', 'get-shit-done/bin/lib/project-root.cjs', 'get-shit-done/bin/lib/schema-detect.cjs', 'get-shit-done/bin/lib/secrets.cjs', 'get-shit-done/bin/lib/state-document.cjs', 'get-shit-done/bin/lib/validate.cjs', 'get-shit-done/bin/lib/workstream-inventory-builder.cjs', 'get-shit-done/bin/lib/workstream-name-policy.cjs', ]; const sdkSrcExists = existsSync(join(__dirname, 'sdk', 'src')); export default tseslint.config( // ── Global ignores ───────────────────────────────────────────────────────── { ignores: [ 'node_modules/**', '**/dist/**', 'sdk/dist/**', '.worktrees/**', '.claude/**', 'coverage/**', '**/*.generated.cjs', ...GENERATED_CJS_IGNORES, ], }, // ── get-shit-done/bin/**/*.cjs + scripts/**/*.cjs ─────────────────────────── // CommonJS Node files: js.recommended + eslint-plugin-n + local plugin rules // Type-aware via parserOptions.project=tsconfig.lint.json where applicable { files: ['get-shit-done/bin/**/*.cjs', 'scripts/**/*.cjs'], plugins: { n: pluginN, local: localPlugin, }, languageOptions: { sourceType: 'commonjs', globals: { ...globals.node, }, }, rules: { ...js.configs.recommended.rules, // Generic quality rules 'no-var': 'error', 'prefer-const': 'warn', 'no-unused-vars': ['warn', { argsIgnorePattern: '^_', varsIgnorePattern: '^_', caughtErrors: 'none', }], 'no-empty': ['warn', { allowEmptyCatch: true }], // Downgraded from recommended error → warn (pre-existing violations; follow-up to fix) 'no-useless-escape': 'warn', 'no-unsafe-finally': 'warn', // eslint-plugin-n rules 'n/no-process-exit': 'warn', // Local rules — warn for now; flip to error after cleanup phases 'local/no-source-grep': 'warn', }, }, // ── tests/**/*.test.cjs ───────────────────────────────────────────────────── { files: ['tests/**/*.test.cjs'], plugins: { 'no-only-tests': noOnlyTests, local: localPlugin, }, languageOptions: { sourceType: 'commonjs', globals: { ...globals.node, }, }, rules: { ...js.configs.recommended.rules, 'no-only-tests/no-only-tests': 'error', // Timing anti-patterns — warn for now; flip to error after cleanup 'local/no-magic-sleep-in-tests': 'warn', 'local/no-elapsed-assertion': 'warn', // Ban raw setTimeout sync + elapsed/duration-style assertions via no-restricted-syntax 'no-restricted-syntax': [ 'warn', { selector: 'AwaitExpression > NewExpression[callee.name="Promise"] ArrowFunctionExpression CallExpression[callee.name="setTimeout"]', message: 'Raw setTimeout used for synchronization in tests. Use proper async patterns instead.', }, { selector: 'CallExpression[callee.object.name="Atomics"][callee.property.name="wait"]', message: 'Atomics.wait() used as a sleep in tests. Use a proper async wait pattern instead.', }, ], 'no-unused-vars': ['warn', { argsIgnorePattern: '^_', varsIgnorePattern: '^_', caughtErrors: 'none', }], 'no-empty': ['warn', { allowEmptyCatch: true }], // Downgraded from recommended error → warn (pre-existing violations; follow-up to fix) 'no-useless-escape': 'warn', 'no-regex-spaces': 'warn', 'no-control-regex': 'warn', 'no-irregular-whitespace': 'warn', }, }, );