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.
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.
Where a value comes from
| Rule | Meaning |
|---|---|
| One type per repo | A 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 node | path, qmd, and workspace are never layered — membership is per-repo by definition. |
wiki marks membership | Presence, not content, makes a repo a wiki. "wiki": {} is legal and inherits root, pagePrefixes, domains, allowUnknownWikis field-by-field from its layers. |
| Empty means unset | An absent field or empty list inherits. A node cannot clear an inherited list back to empty. |
rules merge per-key | A 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.
Top level
| Field | Meaning |
|---|---|
$schema | JSON Schema URL. Informational — the CLI doesn't fetch it. |
version | Minimum CLI version the file requires. A binary older than the declaration refuses to load it, instead of silently ignoring fields it doesn't know. |
name | The host repo's stable key. |
extends | Preset chain, e.g. @paniolo/cli/presets/meta-harness.json. |
harnesses | Agent 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. |
rules | Rule id → severity (off|info|warn|error) plus optional rule options. The primary control surface. |
customRules | Repo-declared rules that join the registry — tuned by rules like a built-in. |
boundary | { include, exclude } globs for the boundary analyzer. |
options | Engine knobs: failOn, ci, excludes, inventory.skillsDirs/skillsExclude. |
verify.onStop | Post-edit check recipe for the host: { name, include, exclude, command, appendMatchedPaths } — argv never goes through a shell. |
wiki | Host wiki registry and tuning; per-repo wikis live on the nodes. |
repos | Sibling repos this file governs, keyed by stable name. |
repoDefaults | Defaults layered under every repos entry. Never applies to the host. |
repoTypes | Named class layers; a node selects one with "type". |
workspace, qmd, rumdl | Workspace-manifest discovery, retrieval settings passthrough, and markdown-style (rumdl) passthrough. |
repos.<name> — a repo node
| Field | Meaning |
|---|---|
path | Repo path relative to the config file (or absolute). Required. |
type | Selects one repoTypes entry. |
intelligenceLayer | Per-repo override of the layer classification. |
scan | { paths, harnesses, rules, boundary, options } — this repo's scan policy. |
verify.onStop | This repo's post-edit check recipe. |
wiki | Present = this repo is a wiki. Fields: root, pagePrefixes, domains, allowUnknownWikis — each inheritable from the layers. |
qmd / workspace | Per-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.
Commands that read or write it
| Command | What it does |
|---|---|
paniolo init | Scaffolds a harness and stamps a config — repoTypes per role, "type" on each node, deduplicated by construction. |
paniolo evolve config migrate | Upgrades 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 graph | Prints 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 connect | Registers a sibling repo — writes a repos entry after confirmation. |
paniolo wiki init | Stamps 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.
Versions and safety
| Case | Behavior |
|---|---|
No version declared | Always accepted — older files keep loading. |
version ≤ installed CLI | Accepted. |
version > installed CLI | Refused — upgrade @paniolo/cli. |
| Unknown keys | Ignored — 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.