pi-code-quality

Declarative Pi extension that automatically formats and lints files after write/edit.

Packages

Package details

extension

Install pi-code-quality from npm and Pi will load the resources declared by the package manifest.

$ pi install npm:pi-code-quality
Package
pi-code-quality
Version
0.1.4
Published
May 11, 2026
Downloads
682/mo · 27/wk
Author
conte777
License
MIT
Types
extension
Size
37.2 KB
Dependencies
0 dependencies · 2 peers
Pi manifest JSON
{
  "extensions": [
    "extensions/pi-code-quality/index.ts"
  ]
}

Security note

Pi packages can execute code and influence agent behavior. Review the source before installing third-party packages.

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-quality

For local development:

pi install /absolute/path/to/pi-code-quality

Configuration

Global config, trusted automatically:

~/.pi/agent/code-quality.json

Project-local config, requires trust:

.pi/code-quality.json

Project 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, or Reject;
  • the prompt shows every configured auto-run binary;
  • Trust always stores 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:

  1. finds tools matching include / exclude;
  2. finds {root} using rootMarkers;
  3. skips files above maxFileSizeBytes;
  4. runs formatter first;
  5. runs linter second;
  6. 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:

  1. absolute paths are used as-is;
  2. relative bin, config, and cwd values with / are resolved relative to {root};
  3. bare binary names are resolved through PATH.

Placeholders:

  • {workspace} — Pi working directory or directory containing project .pi config
  • {root} — nearest directory containing one of rootMarkers
  • {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/service
Code quality:

✅ gofmt: formatted internal/service/foo.go
⚠️ golangci-lint found issues:
internal/service/foo.go:42:13: unchecked error

Disable a tool

{
  "version": 1,
  "tools": [{ "id": "go", "enabled": false }]
}

Development

npm install
npm run verify
npm pack --dry-run