/** * Lint / structural-test / eval config emitters. * * v1 ships canonical templates per detected stack. The Design stage picks one tool per * category; this module produces the config string for that tool. Implementation tasks * write the file; Validate verifies the tool runs cleanly. * * We deliberately avoid bundling the actual tools — Plan tasks install them via the * package manager. */ export interface LintConfigInput { tool: string; /** Detected language(s) — used to scope rule sets where the tool requires it. */ languages: readonly string[]; } export function renderLintConfig(input: LintConfigInput): { filename: string; content: string } | null { switch (input.tool) { case "eslint": return { filename: "eslint.config.js", content: [ "// Generated by /supi:harness. Edit with care; harness re-emits on rebuild.", "import js from \"@eslint/js\";", "", "export default [", " js.configs.recommended,", " {", " rules: {", " // Harness-enforced: prevents the most common slop traps.", " \"no-unused-vars\": [\"error\", { argsIgnorePattern: \"^_\" }],", " \"no-duplicate-imports\": \"error\",", " \"no-unreachable\": \"error\",", " },", " },", "];", "", ].join("\n"), }; case "biome": return { filename: "biome.json", content: JSON.stringify( { $schema: "https://biomejs.dev/schemas/1.9.0/schema.json", organizeImports: { enabled: true }, linter: { enabled: true, rules: { recommended: true, correctness: { noUnusedVariables: "error", noUnreachable: "error" }, style: { noDuplicateImports: "error" }, }, }, }, null, 2, ) + "\n", }; case "ruff": return { filename: "ruff.toml", content: [ "# Generated by /supi:harness.", "select = [\"E\", \"F\", \"W\"]", "ignore = []", "line-length = 100", "", ].join("\n"), }; default: return null; } } export function renderStructuralTestConfig(input: { tool: string }): { filename: string; content: string } | null { switch (input.tool) { case "dependency-cruiser": return { filename: ".dependency-cruiser.cjs", content: [ "// Generated by /supi:harness — structural test config.", "module.exports = {", " forbidden: [", " {", " name: \"no-circular\",", " severity: \"error\",", " from: {},", " to: { circular: true },", " },", " ],", " options: { tsConfig: { fileName: \"tsconfig.json\" } },", "};", "", ].join("\n"), }; case "import-linter": return { filename: ".importlinter", content: [ "[importlinter]", "root_packages =", " src", "", "[importlinter:contract:layers]", "name = Layered architecture", "type = layers", "layers =", " domain", " infrastructure", " ui", "", ].join("\n"), }; default: return null; } } export function renderEvalConfig(input: { tool: string }): { filename: string; content: string } | null { switch (input.tool) { case "vitest": return { filename: "tests/evals/.gitkeep", content: "# Eval suite skeleton — populate with /supi:harness implement.\n", }; case "playwright": return { filename: "tests/evals/.gitkeep", content: "# Eval suite skeleton (Playwright e2e). Populate with /supi:harness implement.\n", }; default: return null; } }