pi-code-quality
v0.1.4
Published
Declarative Pi extension that automatically formats and lints files after write/edit.
Downloads
653
Maintainers
Readme
pi-code-quality
Declarative Pi extension that automatically formats and lints files after successful write / edit tool calls.
Why
pi-code-quality lets users configure formatters and linters with JSON instead of installing a separate Pi plugin for every language.
It is intentionally separate from pi-lsp: formatters/linters are short-lived command-line tools, while LSP servers are long-lived JSON-RPC processes.
Install
pi install npm:pi-code-qualityFor local development:
pi install /absolute/path/to/pi-code-qualityConfiguration
Global config, trusted automatically:
~/.pi/agent/code-quality.jsonProject-local config, requires trust:
.pi/code-quality.jsonProject entries override global entries with the same id. A project entry with "enabled": false disables the global entry with that id.
Trust and security
Project-local config can auto-run binaries on your machine. For that reason:
- project-local config content is hashed;
- unknown hashes prompt for
Trust once,Trust always, orReject; - the prompt shows every configured auto-run binary;
Trust alwaysstores the hash in~/.pi/agent/trust/code-quality.json;- changing the config changes the hash and asks again;
- non-interactive mode rejects project-local config by default;
- commands run as
bin+args[], never as shell strings.
Global config is considered trusted because it is user-owned agent configuration.
Example config
{
"version": 1,
"tools": [
{
"id": "go",
"enabled": true,
"include": ["**/*.go"],
"exclude": ["vendor/**"],
"rootMarkers": ["go.mod"],
"formatter": {
"bin": "gofmt",
"args": ["-w", "{file}"],
"cwd": "{root}",
"timeoutMs": 25000
},
"linter": {
"bin": "golangci-lint",
"args": ["run", "--new-from-rev=HEAD", "--timeout=25s", "./{relDir}"],
"cwd": "{root}",
"timeoutMs": 30000,
"diagnosticExitCodes": [1]
}
},
{
"id": "python",
"enabled": true,
"include": ["**/*.py"],
"rootMarkers": ["pyproject.toml", "ruff.toml"],
"formatter": {
"bin": "uv",
"args": ["run", "ruff", "format", "{file}"],
"cwd": "{root}",
"timeoutMs": 25000
},
"linter": {
"bin": "uv",
"args": ["run", "ruff", "check", "{file}"],
"cwd": "{root}",
"timeoutMs": 30000,
"diagnosticExitCodes": [1]
}
},
{
"id": "typescript",
"enabled": true,
"include": ["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx"],
"exclude": ["node_modules/**", "dist/**"],
"rootMarkers": ["package.json"],
"formatter": {
"bin": "node_modules/.bin/prettier",
"args": ["--write", "{file}"],
"cwd": "{root}",
"timeoutMs": 25000
},
"linter": {
"bin": "node_modules/.bin/eslint",
"args": ["{file}"],
"cwd": "{root}",
"timeoutMs": 30000,
"diagnosticExitCodes": [1]
}
}
]
}Behavior
After a successful write or edit:
- finds tools matching
include/exclude; - finds
{root}usingrootMarkers; - skips files above
maxFileSizeBytes; - runs formatter first;
- runs linter second;
- appends a compact summary to the original tool result.
Lint diagnostic exit codes do not make write / edit fail. Diagnostics are extra context for the model. Summaries that contain issues are also sent as a user-visible, tool-styled diagnostic notice.
No slash commands are registered.
Relative paths and placeholders
Path resolution:
- absolute paths are used as-is;
- relative
bin,config, andcwdvalues with/are resolved relative to{root}; - bare binary names are resolved through
PATH.
Placeholders:
{workspace}— Pi working directory or directory containing project.piconfig{root}— nearest directory containing one ofrootMarkers{file}— absolute file path{relFile}— file path relative to{root}{dir}— absolute file directory{relDir}— file directory relative to{root}{config}— resolved command config path{configDir}— directory containing{config}
Output example
Code quality:
✅ gofmt: formatted internal/service/foo.go
✅ golangci-lint: no issues in internal/serviceCode quality:
✅ gofmt: formatted internal/service/foo.go
⚠️ golangci-lint found issues:
internal/service/foo.go:42:13: unchecked errorDisable a tool
{
"version": 1,
"tools": [{ "id": "go", "enabled": false }]
}Development
npm install
npm run verify
npm pack --dry-run