Reference

paniolo.config.json

One file at a repository's root tells @paniolo/cli which agent harnesses it targets, how to weigh the rules, which sibling repos it governs, and what checks run after an edit. The CLI reads it — the skills write it.

60-Second Quick Start

One file, three forms

A single repo that only needs scan policy uses the leaf form — exactly what paniolo-config-init writes:

// paniolo.config.json — a leaf repo { "$schema": "https://paniolo.ai/schemas/paniolo.config.v2.json", "extends": ["@paniolo/cli/presets/meta-harness.json"], "harnesses": ["claude", "cursor"], "rules": {} }

A harness repo at the workspace root governs every child under repos/ — the layout we encourage. It uses the full form: a named host plus a repos map, with shared policy factored into repoDefaults and repoTypes instead of repeated per repo:

// paniolo.config.json — workspace root, children under repos/ { "name": "my-harness", "repoDefaults": { "intelligenceLayer": { "enabled": true, "role": "source" }, "scan": { "harnesses": ["claude", "cursor"] } }, "repoTypes": { "wiki": { "verify": { "onStop": [{ "name": "check", "command": ["paniolo", "wiki"] }] }, "wiki": { "root": "wiki" } } }, "repos": { "my-app": { "path": "repos/my-app" }, "my-wiki": { "path": "repos/my-wiki", "type": "wiki", "wiki": {} } } }

A harness can also live inside the workspace it governs — the same file, its paths pointing at the repos beside it:

// my-harness/paniolo.config.json — harness as a sibling repo { "name": "my-harness", // ... same repoDefaults / repoTypes ... "repos": { "my-app": { "path": "../my-app" }, "my-wiki": { "path": "../my-wiki", "type": "wiki", "wiki": {} } } }

my-wiki is a wiki because its wiki key is present — the empty object is the membership marker; its root comes from the wiki type. Every repo gets repoDefaults; only my-wiki gets the wiki type's policy.

Layering

Where a value comes from

RuleMeaning
One type per repoA node opts into a class layer with "type": "<name>". An unknown type name is a config error, like an unknown repos key.
Identity stays on the nodepath, qmd, and workspace are never layered — membership is per-repo by definition.
wiki marks membershipPresence, not content, makes a repo a wiki. "wiki": {} is legal and inherits root, pagePrefixes, domains, allowUnknownWikis field-by-field from its layers.
Empty means unsetAn absent field or empty list inherits. A node cannot clear an inherited list back to empty.
rules merge per-keyA node's own rule entry wins over the same rule in any layer, so "rule-id": "off" still disables what a type enabled.

To see which layer a live value came from: npx @paniolo/cli evolve config effective --target <repo> reports every setting plus its provenance — node, type, defaults, preset, builtin, or cli.

Field Reference

Top level

FieldMeaning
$schemaJSON Schema URL. Informational — the CLI doesn't fetch it.
versionMinimum CLI version the file requires. A binary older than the declaration refuses to load it, instead of silently ignoring fields it doesn't know.
nameThe host repo's stable key.
extendsPreset chain, e.g. @paniolo/cli/presets/meta-harness.json.
harnessesAgent harnesses this repo targets: claude, cursor, copilot, codex, gemini, antigravity, devin. Absent means every harness-specific rule still runs.
intelligenceLayer{ enabled, role } — whether source-layer rules apply, and whether this repo is a source or consumer.
rulesRule id → severity (off|info|warn|error) plus optional rule options. The primary control surface.
customRulesRepo-declared rules that join the registry — tuned by rules like a built-in.
boundary{ include, exclude } globs for the boundary analyzer.
optionsEngine knobs: failOn, ci, excludes, inventory.skillsDirs/skillsExclude.
verify.onStopPost-edit check recipe for the host: { name, include, exclude, command, appendMatchedPaths } — argv never goes through a shell.
wikiHost wiki registry and tuning; per-repo wikis live on the nodes.
reposSibling repos this file governs, keyed by stable name.
repoDefaultsDefaults layered under every repos entry. Never applies to the host.
repoTypesNamed class layers; a node selects one with "type".
workspace, qmd, rumdlWorkspace-manifest discovery, retrieval settings passthrough, and markdown-style (rumdl) passthrough.

repos.<name> — a repo node

FieldMeaning
pathRepo path relative to the config file (or absolute). Required.
typeSelects one repoTypes entry.
intelligenceLayerPer-repo override of the layer classification.
scan{ paths, harnesses, rules, boundary, options } — this repo's scan policy.
verify.onStopThis repo's post-edit check recipe.
wikiPresent = this repo is a wiki. Fields: root, pagePrefixes, domains, allowUnknownWikis — each inheritable from the layers.
qmd / workspacePer-node membership ({ enabled }). Never inherited.

repoDefaults / repoTypes.<name>

Both carry the same shape — every repos node's policy fields except path, qmd, workspace, and type: intelligenceLayer, scan, verify, and a wiki defaults section whose fields fill declared node wikis.

CLI Reference

Commands that read or write it

CommandWhat it does
paniolo initScaffolds a harness and stamps a config — repoTypes per role, "type" on each node, deduplicated by construction.
paniolo evolve config migrateUpgrades an existing config to the current shape — previews first; --apply writes a verified rewrite and keeps a .pre-migration.bak. Fields shared by every repo lift into repoDefaults, and an upgrade that emits one raises version so older binaries fail loudly.
paniolo evolve config graphPrints the normalized repo graph — every node's effective values and their provenance (--json for the machine-readable document).
paniolo evolve config effective --target <repo>One repo's resolved settings after all layers — the answer to "where did this value come from".
paniolo evolve harness connectRegisters a sibling repo — writes a repos entry after confirmation.
paniolo wiki initStamps a wiki directory and registers it in the config.
paniolo scan --target <name>Scans one governed repo using its layered effective settings.

Prefer the CLI's writers over hand edits — they preserve comments and JSONC formatting and verify semantic equivalence before writing.

Compatibility

Versions and safety

CaseBehavior
No version declaredAlways accepted — older files keep loading.
version ≤ installed CLIAccepted.
version > installed CLIRefused — upgrade @paniolo/cli.
Unknown keysIgnored — forward-compatible.
Comments (JSONC)Preserved by every CLI writer.

Version note: repoDefaults and repoTypes are the newest fields — if your installed @paniolo/cli doesn't know them, upgrade first; the version floor keeps an old binary from misreading a config that uses them.