@danypops/pi-profiles
Named profiles for pi: bundle model, thinking level, tools, and system-prompt instructions, with a soft model-allowlist guard for separating contexts like a personal OpenRouter budget from a work Vertex AI project.
Package details
Install @danypops/pi-profiles from npm and Pi will load the resources declared by the package manifest.
$ pi install npm:@danypops/pi-profiles- Package
@danypops/pi-profiles- Version
0.1.1- Published
- Jul 21, 2026
- Downloads
- 220/mo · 220/wk
- Author
- danypops
- License
- MIT
- Types
- extension
- Size
- 20.8 KB
- Dependencies
- 0 dependencies · 3 peers
Pi manifest JSON
{
"extensions": [
"./index.ts"
]
}Security note
Pi packages can execute code and influence agent behavior. Review the source before installing third-party packages.
README
@danypops/pi-profiles
A pi extension for named profiles: bundle a model, thinking level, active tools, and system-prompt instructions behind one name, and switch between them with /profile, --profile, or Ctrl+Shift+U.
Built directly on pi-coding-agent's own bundled preset.ts example (same config-merge, CLI flag, selector UI, and cycling pattern), extended with one thing that example doesn't do: an allowedModels soft guard that warns if you manually switch to a model outside the active profile's expected set. That's the part that matters when profiles map to separate billing — e.g. a personal OpenRouter/OpenAI budget vs. a work Google Cloud project paying for Claude via Vertex AI.
Not to be confused with pi-profiles (npm, unscoped)
There's an unrelated, unscoped npm package also called pi-profiles (chaychoong/pi-profiles, CLI: ppi) that solves a different, complementary problem: it wraps PI_CODING_AGENT_DIR to give you fully isolated ~/.pi/profiles/<name>/ config directories — separate settings, extensions, skills, auth, and sessions per profile — launched via ppi use <name>. Its own docs are explicit that it's a different layer: "No mid-session model/tool switching. Pi's built-in /model command handles that."
Use ppi for the hard wall (see below). Use this extension for fast, in-session switching once you're already running pi. They compose fine together.
What this is not
This does not hide providers, disable extensions, or enforce a hard boundary. Pi has no supported extension API to unload another extension or restrict which providers/models are selectable — see the allowedModels section below. If you need a hard separation (distinct credential stores, distinct session history, no way to accidentally cross-select), use ppi (or hand-roll the same idea with pi's own PI_CODING_AGENT_DIR env var pointing at two entirely separate config directories), or project-local .pi/settings.json if the split maps cleanly to different working directories.
Install
Local, not published:
pi install ~/Projects/pi-profiles
# or just for one run:
pi -e ~/Projects/pi-profiles/index.ts
Config
Merged from (project overrides global):
~/.pi/agent/profiles.json(global)<project>/.pi/profiles.json(project-local)
{
"personal": {
"provider": "openai",
"model": "gpt-4.1",
"thinkingLevel": "medium",
"allowedModels": ["openai/*", "openrouter/*", "google/*"]
},
"work": {
"provider": "anthropic-vertex",
"model": "claude-sonnet-5",
"thinkingLevel": "high",
"allowedModels": ["anthropic-vertex/*"],
"instructions": "Billed to the work GCP project. Keep usage scoped to work tasks."
}
}
A starter file matching this repo's own personal/work split (openai vs. anthropic-vertex) was seeded at ~/.pi/agent/profiles.json — edit models/providers to match what you actually have configured (pi --list-models).
Fields
| Field | Type | Effect |
|---|---|---|
provider + model |
string | Resolved via ctx.modelRegistry.find() and applied with pi.setModel() |
thinkingLevel |
off|minimal|low|medium|high|xhigh|max |
pi.setThinkingLevel() |
tools |
string[] | Replaces the active tool set via pi.setActiveTools(); unknown tool names are warned and dropped |
instructions |
string | Appended to the system prompt for the rest of the session while the profile is active |
allowedModels |
string[] (glob, matched against provider/model) |
Advisory only — see below |
allowedModels, the soft guard
While a profile is active, switching models by hand (/model, Ctrl+P cycling) to something outside allowedModels triggers a warning notification. It does not block the switch — model_select is a notification-only event in pi's extension API, there's no supported way to veto it. Its only job is to catch the "forgot which profile was active" slip before you burn tokens against the wrong account.
Switches the profile itself makes (via /profile <name> or --profile) never trigger the warning, and restoring a model from a saved session (event.source === "restore") is ignored too.
Usage
pi --profile work # start with the "work" profile applied
/profile # interactive selector (also shows "(none)" to clear)
/profile work # switch directly
Ctrl+Shift+U # cycle through all defined profiles, then "(none)"
The active profile name is shown in the footer status bar and persisted across turns in the session (so it survives /reload and session resume), but model/tools/thinking are only re-applied on --profile//profile/cycling — not silently re-applied on session restore, since the session already has whatever was active when it was saved.
Development
npm install
npm run check # tsc --noEmit
Iterate without reinstalling:
pi -e ~/Projects/pi-profiles/index.ts
License
MIT