@lenard9191/pi-project-profile

Pi (π) coding-agent extension: project-aware system prompt for any repo (stack, commands, conventions, AGENTS.md/CLAUDE.md rules) plus a gate that runs the project's own typecheck/lint/tests after every agent turn, with self-repair and diff review.

Packages

Package details

extensionskill

Install @lenard9191/pi-project-profile from npm and Pi will load the resources declared by the package manifest.

$ pi install npm:@lenard9191/pi-project-profile
Package
@lenard9191/pi-project-profile
Version
1.5.0
Published
Nov 9, 2026
Downloads
746/mo · 80/wk
Author
lenard9191
License
MIT
Types
extension, skill
Size
469.1 KB
Dependencies
0 dependencies · 3 peers
Pi manifest JSON
{
  "image": "https://raw.githubusercontent.com/jlrrequina/pi-project-profile/main/docs/cover.png",
  "skills": [
    "./skills"
  ],
  "extensions": [
    "./index.ts"
  ]
}

Security note

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

README

pi-project-profile

Project-aware π (pi) coding agent, with a verification gate. A π extension that detects any repository's stack, commands, conventions and instruction files (AGENTS.md, CLAUDE.md, .cursorrules, …), injects them as a stable <project_profile> system-prompt section, and then runs the project's own typecheck, lint, tests and build after every agent turn — with a bounded self-repair loop and a review of the agent's diff.

npm version npm downloads π package CI OpenSSF Scorecard license

pi install npm:@lenard9191/pi-project-profile

pi-project-profile: the injected project profile and the verification of an agent turn

  • Profile: detects the stack, commands (including how to run one test), conventions, CI checks, test layout, generated code and instruction files (AGENTS.md, CLAUDE.md, .cursorrules, Copilot, Windsurf, Cline, Gemini, Codex, …), and injects them as a stable <project_profile> system-prompt section.
  • Verify: after every turn that changed files, runs the project's own checks and sends new failures back to the agent for a bounded number of repair rounds.
  • Review: flags what a reviewer would want to know about the agent's diff: added suppressions, skipped or focused tests, stubs, loosened configs, deleted tests, secrets, unignored .env files, stale lockfiles.
  • Rules on demand: delivers nested AGENTS.md files and glob-scoped rules (Cursor, Copilot, Windsurf) when the agent first touches a file they apply to, and warns before hand-editing generated files.
  • Memory: per-repository notes with author, date and evidence — what the agent or you worked out and the files do not show. The agent records them with a remember tool, a sleep pass validates them against the repository at every session start, /profile dream has the agent consolidate them against the verification history, and the pre-existing-failure baseline survives between sessions on an unchanged tree.

It never writes into the repository. Cache, notes, history and settings live under ~/.pi/agent/project-profile/ (/profile storage shows and prunes them).

Why

A coding agent dropped into an unfamiliar repository guesses: which package manager, which test runner, whether tsc or biome is the source of truth, where tests live, which files are generated. Then it reports "done" without having run anything, or runs the wrong thing. pi-project-profile answers those questions once per repository, keeps the answer stable so prompt caching works, and turns "done" into "the project's own checks pass" — without ever running installs, migrations or deploys on its own.

How it works

  1. Session start — the repository is scanned (manifests, lockfiles, CI files, Makefiles, instruction files; nothing is executed), cached under ~/.pi/agent/project-profile/, and rendered as <project_profile> in the system prompt. Re-detection happens automatically when a manifest changes. The notes of the repository are validated against it (a note that names a path that no longer exists is flagged), and the last session's pre-existing-failure baseline is restored when the tree has not changed since.
  2. Each turn — files the agent touches are tracked; nested AGENTS.md/CLAUDE.md and glob-scoped rules that apply to them are delivered with the first matching tool result; generated files get a warning. Without a restored baseline, the agent's first read in a project directory starts that project's typecheck and lint once, in the background, on the untouched tree: whatever already fails there is the baseline, so it is never blamed on the agent (a run that finishes after the agent's first write is discarded).
  3. Before the turn settles — if files changed, the syntax, typecheck and lint tiers run (tests and builds once you have allowed them for the repo). New failures go back to the agent as a [verification] message for at most three repair rounds; pre-existing failures are reported to you once and never blamed on the agent. The diff review runs alongside. Each verdict is appended to the repository's gate history (dates, checks, rounds — never code or output).

Install

pi install npm:@lenard9191/pi-project-profile

Requires π ≥ 0.87 and Node ≥ 22.18. No runtime dependencies. Update with pi update npm:@lenard9191/pi-project-profile. Try it once without installing: pi -e npm:@lenard9191/pi-project-profile.

The package ships one extension and one Agent Skill (skills/project-profile): an operating and troubleshooting guide the model loads on demand when a verification message needs interpreting or you ask how to configure the extension. /skill:project-profile loads it explicitly.

What the agent sees

<project_profile>
- Project: zod — ~/projects/zod · remote github.com/colinhacks/zod · ~741 tracked files
- Stack: monorepo: pnpm workspaces (8 packages) · pnpm · Vite · Vitest
- Commands: install `pnpm install` · typecheck `pnpm exec tsc --noEmit -p tsconfig.json` · lint `pnpm run lint` · test `pnpm run test` · test one `pnpm exec vitest run <file> -t "<name>"`
- Conventions: ESM · Prettier · Biome · Husky git hooks · License: MIT
- Tests: `*.test.ts` in tests/ (202)
- Instruction files: AGENTS.md (loaded) · .cursorrules (22 KB — read it when relevant)
- Notes: (user 3f9a1c) `pnpm test` needs Docker running for the Postgres container · (agent 8b02e4) Run `pnpm codegen` before the typecheck after a schema change — evidence: tsc failed on missing generated types until I ran it
- Verification: after each turn that changed files, the harness runs `pnpm exec tsc --noEmit -p tsconfig.json`, `pnpm run lint:check` automatically; asks once before running tests
</project_profile>

The section is identical for every turn of a session, so prompt caching keeps working.

Verification

Tier Examples Policy
syntax node --check, python -c ast.parse, ruby -c, bash -n, JSON parse automatic, changed files only
fast tsc --noEmit, cargo check, go build/go vet, mypy/pyright, mix compile, dart analyze automatic
lint eslint, biome, prettier --check, ruff, cargo fmt --check/clippy, gofmt, rubocop, swiftlint automatic
test npm test, cargo test, pytest, go test, rspec, mix test, phpunit, swift test, ./gradlew test confirmed once per repo
build pnpm build, ./gradlew compile*, swift build, zig build, cmake --build confirmed once per repo

Installs, migrations, deploys and anything network-bound are never run.

  • Only new failures cost repair rounds. Diagnostics recorded before the agent's first write (a repo that was already red) are pre-existing: not sent back to the agent, noted once for you, and they don't block the tests. The baseline comes from a background run of the project-wide typecheck and lint, started by the agent's first read in each project directory (at most four per session; workspace-wide scripts such as turbo run lint are not started this way).
  • Narrowed runs. Tests run as vitest related, jest --findRelatedTests, go test ./<pkg>/..., cargo test -p <package> or pytest <changed test files> when that is safe; cargo check drops --all-targets when no test/bench/example code changed. Monorepo packages are checked in their own directory with their own scripts, and a workspace-wide root run replaces the per-package duplicates.
  • Fast. Read-only checks in a tier run in parallel; tests and builds run one at a time.
  • Actionable failures. Diagnostic lines first (~40 lines, full log path), plus the exact auto-fix command for format/lint failures (prettier --write <files>, ruff check --fix <files>, …).
  • Bounded. At most maxRepairRounds (3); stops early when a round changes nothing or repeats the same failure, then asks the agent for a summary. Environment failures (an executable not on PATH, deps not installed, timeout, database down) go to you, one message per cause with the directories it affects, and disable those checks for the session; /profile refresh turns them back on.
  • Only real changes. Committing or staging changes no file content, so it starts no check. Deleted files never reach per-file checks, and a related-tests run that involves one becomes the full run. Docs and lockfiles alone start nothing.
  • The summary stays last. Each follow-up that sends the agent back asks it to end with a complete summary of the task, updated with the fixes and the verification result. The last message (the end of the transcript, pi -p output, /copy) is the task's result, not a reply about a check.

Review of the agent's changes

Compared with the content before the agent's first write (or git HEAD; files that were already dirty are never blamed on the agent):

  • @ts-ignore, eslint-disable, # type: ignore, # noqa, #[allow], //nolint, @Suppress, … added
  • tests skipped (.skip, xit, @pytest.mark.skip, t.Skip, #[ignore]) or focused (.only), test files deleted (not when a whole worktree or clone is removed), test cases removed
  • stubs left in (raise NotImplementedError, todo!(), throw new Error("not implemented"))
  • checks loosened in config ("strict": false, ESLint rules "off", continue-on-error: true, || true in scripts)
  • secrets (AWS, GitHub, OpenAI/Anthropic, Slack, Stripe, Google, npm, Hugging Face keys, private keys; values are never repeated), .env files that are not gitignored
  • dependency changes without a lockfile update (pnpm install, uv lock, go mod tidy, …)

In JS and TS files, a suppression counts only as a directive that opens a comment and a focus or skip marker only in code, so test titles and fixture strings that mention them are not reported. Files in ignoreDirs (node_modules, build output) are not reviewed. Secrets, stale lockfiles and .only get one follow-up turn. Weakening that can hide a check that failed in this task also gets one follow-up turn: a @ts-ignore while the typecheck fails, a .skip while tests fail, but not an eslint-disable while tests fail. Everything else is shown to you.

Memory

The detected profile needs no memory: it is re-read from the repository every session. Memory is for what the files do not show and that cost effort to learn — a command that must run before another, an environment variable the tests need, a convention no check enforces. The design follows Anthropic's components of agent memory: a small injected layer, notes that carry how they were verified, and a sleep phase that checks memory against ground truth.

  • Notes live per repository under ~/.pi/agent/project-profile/, never in the repository, and appear under Notes: in <project_profile> with their author and id: (user 3f9a1c) …, (agent 8b02e4) … — evidence: …. You write them with /profile note <text>; the agent writes them with the remember tool (text plus evidence: how it knows), at most 25 agent notes per repository, and you are told each time. forget_note lets the agent drop a note of its own that proved wrong; your notes it can remove only during a dream. /profile notes lists them with ids and dates, /profile notes rm <id|n> removes one.
  • The injected layer stays small. Notes are shown within profile.maxNotesChars (1200); the rest is counted and referenced by path instead of silently dropped.
  • The learning loop. When a repair round turns green, the repair message has already asked the agent to save the cause as a note if it was a project fact the profile did not show — with the evidence — and to skip it when the cause was its own change. Purpose, not a rule: what to record is the agent's judgment.
  • Sleep pass (deterministic, every session start and /profile refresh): a note that names a path that no longer exists is flagged [may be stale: …] in the prompt and to you; a command override that detection now reproduces is reported with /profile set <key> default to drop it. Nothing is removed without you.
  • /profile dream: sends the agent the notes, the overrides and the verification history with one instruction — check each note against the repository, remove what is wrong or duplicated, merge, add evidence, keep the set small, change no file — and lets it remove your notes too for that run. It ends with a summary of what it kept, changed and removed.
  • History (/profile history): every verdict, repair round, environment failure, review finding and note change is one JSON line per repository (dates, labels and counts only). The report shows first-attempt failures per check with the auto-fix that was offered, environment failures by session, pre-existing failures, and patterns such as "lint failed on the first attempt in 6 of 14 tasks" — the review that used to need a read of every session transcript.
  • Persisted baseline (verify.baselineCache): the pre-existing-failure baseline is kept with a fingerprint of the tree (HEAD, every dirty file's content stamp, manifests, installed-dependency markers) for three days. A session that starts on the same tree knows what already failed before the agent's first write, without the background run and its race. Any change to the fingerprint, or no git, means no cache.

All of it is bounded: history files rewrite themselves past 256 KB, a baseline past 2 MB or 5000 diagnostics per check is not written, and pruning at session start drops files of repositories whose root is gone (profiles with notes or overrides are kept: an unmounted disk is not a deleted project), baselines past their life, and profiles not visited for 120 days. /profile storage shows every directory with its size; /profile storage prune runs the pruning now; /profile forget drops one repository's profile, notes, history and baseline.

Commands and tool

/profile show the profile, checks, availability and cache path
/profile doctor required vs installed runtimes, tool and dependency availability, fix commands (never run)
/profile refresh re-detect (also automatic when a manifest changes)
/profile set <key> <cmd> override typecheck, lint, test, build, … (- disables, default removes the override)
/profile note <text> persist a per-repo note for the agent (/profile notes lists them with ids, /profile notes rm <id|n> removes one, /profile notes clear drops them all)
/profile tests allow|deny|ask test-tier permission (same for build)
/profile verify on|off per-repo switch for the gate
/profile history what the gate observed in this repo: tasks, first-attempt failures per check, environment failures, review findings, patterns
/profile dream the agent consolidates the notes against the repository and the history (needs profile.agentNotes)
/profile storage [prune] sizes of everything the extension stores, and pruning on demand
/profile forget drop the profile, notes, history and baseline for this repo
/verify [fast|lint|test|build|all] [file…] run checks now, showing every failure (/verify cancel aborts)
run_checks the same, as a tool the model can call mid-task; at an umbrella directory without a manifest it runs the single nested project, or names the nested projects when there are several
remember / forget_note tools the model calls to save a project note (text + evidence) or remove one of its own; off with profile.agentNotes: false

Configuration

~/.pi/agent/project-profile/config.json — create it with /profile config. Main keys:

{
  "verify": { "enabled": true, "maxRepairRounds": 3, "runTests": "ask", "runBuild": "ask", "guard": true, "perTurn": false, "concurrency": 0, "baselineCache": true },
  "profile": { "inject": true, "scopedInstructions": true, "maxInstructionFileChars": 3000, "agentNotes": true, "maxNotesChars": 1200 }
}

profile.agentNotes: false removes the remember/forget_note tools and /profile dream; notes are then yours alone (/profile note). verify.baselineCache: false runs the background baseline every session instead of restoring it.

verify.perTurn also runs the syntax + fast tiers after every turn that changed files and adds a short informational note when red (no repair loop: multi-file edits are legitimately red half-way). PI_PROJECT_PROFILE_DEBUG=1 writes a JSONL trace to $TMPDIR/pi-project-profile/.

Design rules

  • Nothing is hardcoded per project; every capability degrades detected → generic → no-op, never to a wrong action.
  • Only read-only checks run unprompted. A workspace script (turbo run lint, pnpm -r lint) is one only when every package script it runs is read-only too: a package lint with --fix would rewrite files, so its packages are checked with their own read-only checks instead. The gate runs only in projects π reports as trusted.
  • Tools are bound late (node_modules/.bin, uv run/poetry run/.venv), so the profile stays valid before and after installs.
  • Repository instructions are framed as repository content, inlined only when small, and skipped when they read like a bot directive.

Coverage

Node/TypeScript (npm, pnpm, yarn, bun; workspaces, Turborepo, Nx), Deno, Rust, Go, Python (uv, poetry, pdm, pipenv, hatch), Ruby, JVM (Gradle, Maven, Kotlin, Android), .NET, Swift, PHP, Elixir, Dart/Flutter, C/C++ (CMake, Meson), Zig, Haskell, Scala, OCaml, Gleam, Erlang, Terraform, shell, Lua, Perl, plus Makefile/justfile/Taskfile targets, Bazel, CI (GitHub Actions, GitLab, CircleCI, Azure, Travis, Jenkins, Buildkite, Bitbucket), docker-compose services and umbrella repos. Tested on macOS, Linux and Windows.

Instruction files: AGENTS.md, AGENTS.override.md, CLAUDE.md (root and nested), .cursorrules, .cursor/rules/*.mdc, .github/copilot-instructions.md, .github/instructions/*.instructions.md, .windsurfrules, .windsurf/rules, .clinerules, .roo/rules, .agents/rules, GEMINI.md, CONVENTIONS.md, .junie/guidelines.md, .codex/instructions.md, codex.md, .continuerules, .aider.conf.yml.

FAQ

Does it work with Claude Code, Cursor, Codex or GitHub Copilot?

It is an extension for the π coding agent only. It does read the instruction files those tools use (CLAUDE.md, .cursorrules, .cursor/rules, copilot-instructions.md, .github/instructions, Windsurf and Cline rules, GEMINI.md, codex.md), so a repository already set up for them is understood by π with no extra configuration.

Does it change anything in my repository?

No. Detection is read-only, the cache, notes, history, baselines and per-repo settings live under ~/.pi/agent/project-profile/, logs under $TMPDIR/pi-project-profile/. The automatic checks are the project's own read-only commands; auto-fix commands are suggested, never run. /profile storage shows what is stored and prunes it.

Can the agent write to its own memory? Can I stop it?

Yes, with the remember tool: a note with the evidence for it, at most 25 per repository, each announced to you and removable with /profile notes rm <id>. The agent cannot remove your notes except during a /profile dream you start. profile.agentNotes: false in config.json removes the tools; notes are then written only with /profile note.

Will it run my tests or builds automatically?

Only after you allow it once per repository (/profile tests allow, or answer the prompt the first time). Typecheck and lint run automatically because they are read-only. Installs, migrations, deploys and anything that needs the network are never run.

What happens when a check fails?

The failing diagnostics (pruned to ~40 lines, with the full log path and an auto-fix hint) go back to the agent, which must fix the cause — not disable the check — and end its turn; the checks re-run. After three rounds, or when a round changes nothing or repeats the same failure, the loop stops and the agent summarizes what is still failing instead of claiming success. Each follow-up asks the agent to end with a complete summary of the task, updated with the verification result, so its last message is the task's result and not a reply about a check.

My repo was already failing before the agent touched it. Will the agent try to fix that?

No. The project-wide typecheck and lint run once in the background when the agent starts reading, before it writes; what fails then is pre-existing: hidden from the agent, reported to you once ("already fails on the untouched tree"), and it doesn't block the test tier. /verify still shows everything.

Does it break prompt caching?

No. The <project_profile> section contains no timestamps, branch names or iteration-order-dependent content, so it is byte-identical for every turn of a session.

The detected command is wrong. How do I fix it?

/profile set test "pnpm vitest run" overrides one key for this repository (/profile set lint - disables one); /profile doctor explains missing tools; /profile refresh re-detects. Persist guidance for the agent with /profile note "…". For a wrong detection worth reporting, include the output of node scripts/scan.ts <repo> --checks in the issue.

How is this different from an LSP or linter extension?

An LSP-based extension gives the model diagnostics as it edits. pi-project-profile runs the project's own commands — the ones CI runs — as a gate after the turn, with a repair loop, a pre-existing-failure baseline and a diff review; it also supplies the profile in the first place. The two are complementary.

Does it work in headless or scripted π runs?

Yes: verify.headless (default true) runs the gate in print/JSON/RPC modes too, and test/build tiers that would need a confirmation are skipped instead of blocking.

Links

Development

npm install
npm run check                       # tsc
npm test                            # node --test
node scripts/scan.ts <dir> --checks # print the profile + checks for any directory

CI runs the tests on Linux, macOS and Windows (Node 22.18, 24, 26), smoke-tests the packed tarball, and runs detection weekly on ~20 real repositories (npm run corpus). Releases are published from GitHub Actions with npm provenance and a signed build attestation. See AGENTS.md for the constraints every change must keep and CONTRIBUTING.md for releasing. MIT © John Lenard Requina