import { defineConfig, globalIgnores } from "eslint/config"; import nextPlugin from "@next/eslint-plugin-next"; import tsParser from "@typescript-eslint/parser"; import importPlugin from "eslint-plugin-import"; /** * ESLint with one job here: ENFORCE THE IMPORT LAW (the shell/core * architecture, docs/architecture.md). Everything else stays out of the * way — this is not a style linter. * * components/** → "@/src/core" (the pure core barrel) — ONLY * app/** → "@/src/core" + "@/src/server" (the server barrel) — ONLY * pure core → never imports src/agent, src/server, src/store * * tests/ and scripts/ are internal — unrestricted. */ export default defineConfig([ globalIgnores([".next/**", "node_modules/**", "data/**", "next-env.d.ts", "scripts/**"]), { files: ["**/*.{js,mjs,ts,tsx}"], languageOptions: { parser: tsParser, parserOptions: { ecmaVersion: "latest", sourceType: "module" }, }, plugins: { "@next/next": nextPlugin, import: importPlugin }, settings: { "import/resolver": { typescript: { alwaysTryTypes: true } }, }, rules: { ...nextPlugin.configs.recommended.rules, "import/no-restricted-paths": [ "error", { zones: [ { target: "./components", from: "./src", // NOTE: the plugin resolves `except` relative to the zone's `from` path. except: ["./core/index.ts"], message: "Browser shell imports ONLY the pure core barrel: `from \"@/src/core\"` (architecture: docs/architecture.md).", }, { target: "./app", from: "./src", except: ["./core/index.ts", "./server/index.ts"], message: "app/ (transport + server components) imports ONLY the barrels: `@/src/core` and `@/src/server` (architecture: docs/architecture.md).", }, { target: "./src/core/{index,contracts,question-core,plan-tree,phases,pending,math-delims,ui-message}.ts", from: ["./src/agent", "./src/server", "./src/store"], message: "Pure core modules never import IO layers (agent/server/store). IO-bound core (graph.ts, graph-context.ts) is exempt until the storage-port stage inverts it.", }, ], }, ], }, }, ]);