Configuration
qoq.config.js exports a single configuration object. Every section is optional and falls back to opinionated defaults.
TypeScript config
The config can be authored as qoq.config.{js,ts,mjs,cjs}. For a TypeScript config, import the exported QoqConfig type and annotate the object with satisfies — this gives you autocomplete and type-checking while keeping the literal's own inferred type intact:
import type { QoqConfig } from '@ladamczyk/qoq-cli';
export default {
srcPath: './src',
} satisfies QoqConfig;
Prefer satisfies QoqConfig over a : QoqConfig annotation: it still validates every field against the schema, but it doesn't widen the literal to the type, so the precise types of what you wrote stay visible to the rest of your tooling.
General
srcPath(default./src) — fallback path used when a tool isn't given explicit sources.configType('CJS' | 'ESM') — overrides the module format auto-detected from your ownpackage.json'stypefield, for the config files QoQ generates for the underlying tools (ESLint, Stylelint, …). Explicit config authoring only — the Config Wizard doesn't offer it.configPaths— override where the CLI looks for each tool's config. Each override must begin with a leading/, which resolves relative toprocess.cwd().
{
srcPath: './src',
configPaths: {
eslint: '/eslint.config.js',
prettier: '/.prettierrc',
stylelint: '/stylelint.config.js',
},
}
For example, an ESLint config stored at configs/eslint.config.local.js is referenced as /configs/eslint.config.local.js.
NPM
{
npm: {
checkOutdatedEvery: 1, // days between outdated-dependency checks (default 1)
},
}
Prettier
{
prettier: {
sources: ['.'], // paths to format; defaults to [srcPath]
},
}
Execution respects your .prettierignore.
JSCPD
{
jscpd: {
format: [...], // see jscpd docs
ignore: [...], // see jscpd docs
threshold: 2, // max allowed duplication %, default 2
},
}
Knip
{
knip: {
entry: [...], // default: [${srcPath}/{index,cli,main,root}.{ts,tsx,js,jsx}]
project: [...], // default: [${srcPath}/**/*.{ts,tsx,js,jsx}]
ignore: [...],
ignoreDependencies: [...], // default: ['@ladamczyk/qoq-*']
ignoreBinaries: [...],
},
}
File extensions in the entry/project defaults are derived from the QoQ modules you have installed.
ESLint
Use standard ESLint flat config objects, and extend a QoQ preset through the template property. The value is the unscoped package name (the CLI prepends @ladamczyk/ for you), so write qoq-eslint-v9-ts, not @ladamczyk/qoq-eslint-v9-ts. Each entry may also carry its own files, ignores, and rules:
{
eslint: [
{
template: 'qoq-eslint-v9-ts', // merges baseConfig from this template
files: ['packages/**/src/**/*.ts'],
ignores: ['**/*.spec.ts'],
rules: {}, // additional ESLint rules
},
{
template: 'qoq-eslint-v9-ts-vitest',
files: ['packages/**/src/**/*.spec.ts'],
},
],
}
Available template values (any @ladamczyk/qoq-eslint-v9-* package, minus the scope): qoq-eslint-v9-{js,ts,js-react,ts-react,js-jest,ts-jest,js-jest-rtl,ts-jest-rtl,js-vitest,ts-vitest,js-vitest-rtl,ts-vitest-rtl}.
Stylelint
Use a standard Stylelint config object and extend a QoQ preset via template. As with ESLint, the value is the unscoped package name — qoq-stylelint-css or qoq-stylelint-scss (the SCSS preset extends the CSS one). Omit the whole stylelint section to disable it:
{
stylelint: {
template: 'qoq-stylelint-css', // or 'qoq-stylelint-scss'
strict: false, // true = fail on warnings
},
}
Structurelint
Validates your project's file/folder structure via Structurelint. It runs only when a structurelint section is present; omit the section to disable it. Unlike the other sections, this one isn't a separate config-file pointer — the block mirrors Structurelint's own config shape (IStructureConfig) directly, so you author the rules inline in qoq.config.* instead of a standalone structure.config.{ts,js,mjs}:
{
structurelint: {
structureRoot: '.', // root folder to validate, default '.'
ignorePatterns: ['*.d.ts'], // optional glob-ish names/paths to ignore
rules: {}, // optional reusable named rules, referenced from `structure` via `{ ruleId }`
structure: [
// required — allowed children of the root folder
],
},
}
structure is required — the CLI errors out if the structurelint section is present without it. See the Structurelint page for the full structure/rules syntax and worked examples.
Skillslint
Lints your agent skills (SKILL.md files) as part of the pipeline — markdown prose quality plus structured quality scoring. It runs only when a skillslint section is present and @ladamczyk/skillslint is installed; omit the section to disable it. The Config Wizard adds the section and installs the package when you opt into agent-skills linting.
{
skillslint: {
path: './skills', // directory of skill subdirectories (default ./skills)
threshold: 70, // overall quality floor 0–100, used when no per-category floors are set
ignored: [], // skill directory names to skip
overall: 80, // optional per-category score floors (0–100)
structure: 80,
clarity: 80,
specificity: 80,
advanced: 80,
},
}
Only path is required; everything else falls back to Skillslint's own defaults. qoq --fix forwards --fix so Skillslint auto-corrects what textlint can resolve. See the Skillslint page for the scoring categories and bundled rules.
Config health check
Every run (qoq --check, qoq staged, qoq --fix) also performs an in-process self-check of your qoq.config.js: it warns about entries that are redundant because they merely restate a tool default (e.g. jscpd.threshold: 2) or a rule already declared in the qoq-eslint-v9-* template an eslint entry extends. It never fails the run — it only prints warnings — and there is no config section to enable or disable it.
Config health check found 2 redundant entries:
• jscpd.threshold = 2 — matches the tool default
• eslint[0].rules.no-console = "warn" — already set in qoq-eslint-v9-ts base config
Use --silent to suppress it along with the rest of QoQ's console output.