@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.

Packages

Package details

extension

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