pi-scenes
pi 扩展:场景切换器 + 资源自进化。通用层+分场景层打包 extension/skill,/scene 热切换;按实际用量(工具调用归因 + 会话结束 LLM 反思)生成吸收/淘汰提案,让场景定义随使用习惯进化。
Package details
Install pi-scenes from npm and Pi will load the resources declared by the package manifest.
$ pi install npm:pi-scenes- Package
pi-scenes- Version
0.4.1- Published
- Sep 22, 2026
- Downloads
- 123/mo · 123/wk
- Author
- feng-h
- License
- MIT
- Types
- extension
- Size
- 77.7 KB
- Dependencies
- 0 dependencies · 2 peers
Pi manifest JSON
{
"extensions": [
"./extensions"
]
}Security note
Pi packages can execute code and influence agent behavior. Review the source before installing third-party packages.
README
pi-scenes
Scenario switcher for pi: bundle extensions & skills into a common layer + per-scene layers, switch with one command, hot-reloaded.
What it is
pi loads every installed extension and every skill globally — the prompt grows fat, and office skills leak tokens while you code. pi-scenes adds a scenario dimension on top:
active resources = common layer ∪ current scene
- Common layer — extensions + skills that stay loaded in every scenario (quota display, session carryover…)
- Scene layers — per-scenario bundles (coding / office / writing…), loaded only while active
- Switching rewrites
packages/skillsinsettings.json, thenctx.reload()hot-reloads — no pi restart, session untouched - Every switch stamps a persistent status-bar badge (
◆ coding, or a per-sceneiconlike💻 coding) so the bar always answers "which scene am I in" — restored at session start, cleared by/scene off - Zero-memory discoverability (v0.4.1) — the command hint lists your scene names, and Tab completion shows every scene (icon + description + current marker) plus subcommands: type
/scene c+ Tab →/scene coding;/scenesworks as a plural alias - The data model reserves an
extendschain (with cycle detection) for future parent → child scene hierarchies
Install
pi install npm:pi-scenes
Or straight from git (no build step — pi loads the TypeScript source via jiti):
pi install git:github.com/Feng-H/pi-scenes
Then /reload and /scene is live.
Quick start
/scene # picker: all scenes, ● current, ○ switchable
/scene <Tab> # tab-complete: lists all scenes (icon + description) and subcommands
/scene c<Tab> # completes to /scene coding — no scene names to memorize
/scene coding # switch directly to the coding scene
/scene office # switch to the office scene
/scene off # common layer only (scene off)
/scene status # show active scene + effective packages/skills
/scene init # scaffold scenes.json template + scene skill dirs
/scene stats # usage dashboard: sessions, tool calls, reflections
/scene evolve # generate & apply evolution proposals (confirm-first)
/scene evolve auto # toggle auto-apply at session end (opt-in)
/scenes ... # plural alias — same command, same completions
First run of /scene offers to generate the template. It ships with seven preset scenes + common (all packages verified on npm, 2026-09):
| Layer | Preset packages | Why |
|---|---|---|
common |
npm:pi-scenes, npm:pi-carryover |
the switcher itself + cross-session carryover — always needed |
coding |
npm:pi-lens, npm:pi-subagents, npm:pi-git-worktree |
live LSP/lint feedback, delegated sub-agents, parallel worktrees |
office |
npm:pi-docparser |
PDF/Office document parsing |
pm |
npm:pi-web-access, npm:pi-goal-x, npm:@juicesharp/rpiv-todo |
market/competitor research, goal planning & audit, live todo overlay |
research |
npm:pi-web-access, npm:pi-subagents |
multi-source search/fetch/PDF/video, parallel multi-angle digging |
writing |
npm:pi-web-access |
source gathering & fact-checking with citations |
data |
npm:pi-docparser, npm:pi-mcp-adapter |
table extraction, connect any MCP server (DB/BI) |
Edit it to fit your setup (each scene also gets a skill dir scaffold at ~/.pi/agent/scenes/<name>/skills/):
// ~/.pi/agent/scenes.json
{
"common": { // ── common layer: always loaded
"description": "common",
"packages": ["npm:pi-zai-usage"],
"skills": ["~/.pi/agent/scenes/common/skills"]
},
"scenes": { // ── scene layers: stacked when active
"coding": {
"description": "coding",
"icon": "💻",
"packages": ["npm:pi-carryover"],
"skills": ["~/.pi/agent/scenes/coding/skills"]
},
"office": {
"description": "office work",
"packages": [
{ "source": "npm:pi-docparser", "skills": ["doc-parse"] } // object form = load only part of a package
],
"skills": ["~/.pi/agent/scenes/office/skills"]
}
}
}
Drop SKILL.md folders (or .md files) into a scene's skill directory; the whole directory toggles with the scene. /scene init scaffolds ~/.pi/agent/scenes/{common,coding,office,pm,research,writing,data}/skills/.
Status-bar scene badge
A successful switch sets a persistent footer badge (◆ coding), restored automatically at every session start and cleared by /scene off. The status bar should answer "which scene am I in" — not display tool internals.
The prefix is customizable per scene via the icon field — emoji works great (💻 coding). The scaffolded presets ship with 💻 📄 🎯 🔍 📝 📊, and the picker plus /scene status display the same icon; unset scenes fall back to ◆.
Scenes often bundle tool-heavy extensions whose own footer output crowds the bar. If you use the preset coding scene, pi-lens's diagnostics widget and LSP Inactive status can be silenced without losing any AI-side value (turn-end error injection, lens_diagnostics, symbol navigation) via ~/.pi-lens/config.json:
{ "ui": { "hideLspStatus": true }, "widget": { "visible": false } }
pi-scenes never edits a third-party package's global config — the badge stays the only thing pi-scenes itself puts on your status bar.
Self-evolution (usage-driven)
Scenes are not static. pi-scenes observes what you actually use and proposes updates:
- Collect (passive) — every
tool_callis attributed to its package (via a static scan of installed sources); packages present in settings but absent from every scene definition are tracked as absorb candidates; at each session end a tiny LLM call reflects on which loaded skills were actually useful (a few hundred tokens, 30s timeout, fails silently). - Propose —
/scene evolvegenerates proposals:- absorb: an unmanaged package seen in ≥2 sessions joins the scene it was observed in
- retire: a scene package with zero calls for 20 consecutive sessions (and a tool signal — command-only packages are protected) is proposed for removal; a skill the LLM never found useful (5+ "unused" reflections, 0 "useful") likewise
- protected: the
commonlayer and packages without tool signals (e.g./anywhere-style command-only extensions) are never auto-changed
- Apply (confirm-first) — each proposal shows a
scenes.jsondiff and asks; accepted edits are backed up (scenes.json.scenes-bak) and hot-reloaded./scene evolve autoopts into silent application at session end (changes take effect on the next natural reload).
Tunables live in scenes.json:
{
"evolve": {
"patience": 20, // sessions of zero usage before retire
"absorbThreshold": 2, // unmanaged sightings before absorb
"skillUnusedThreshold": 5 // "unused" reflections before skill retire
}
}
Usage data: ~/.pi/agent/scenes-usage.json (machine-local, inert). View anytime with /scene stats.
Install ≠ load (two-layer model)
- Loading is driven by
settings.jsononly. Switching = rewrite thepackages/skillsarrays +ctx.reload(); resources absent from the arrays are never loaded — no tools registered, no prompt tokens spent. Scene skill dirs (~/.pi/agent/scenes/<name>/skills) are not auto-discovered by pi, so they toggle with the scene. - Files stay on disk. Switching away never uninstalls (
pi removefor that); a switched-away package under~/.pi/agent/npm/is an inert file — instant switch-back, zero runtime cost.
In short: installed forever, loaded per scene. One caveat: extensions you installed manually (your own entries in settings) are yours — pi-scenes treats them as borrowed, never touches them, so they stay loaded in every scene.
Package updates are scene-independent. Every package — whichever scene lists it — is installed once, globally, under ~/.pi/agent/npm/; scenes only flip the settings.json switch. So pi update --extensions works no matter which scene is active (pi's update banner scans installed packages, not the active scene), and since scene definitions use version-less specs (npm:<pkg>), the new version loads on the next switch/reload. One caveat: 0.x caret ranges don't cross minor versions — if the banner persists after updating, pin it explicitly: pi install npm:<pkg>@<version>.
Duplicate & conflict handling
- Same spec in
commonand a scene → deduplicated at the union (loaded once). - Same package, different spellings (e.g.
npm:x@1.0.3vs barenpm:x) → identity-level dedupe keeps the first (common > scene); a manually pinned variant in settings is treated asborrowed(no duplicate injection)./sceneand/scene statuswarn about such spelling mismatches so you can unify them. - Local path vs npm spelling (v0.2.1): if settings already loads a package via a local path (e.g. your dev checkout
/Users/you/dev/pi-carryover), a scene preset listingnpm:pi-carryoverwill borrow your entry instead of installing —pi installis skipped so a second spelling is never appended to settings. This matters because two spellings of one package = the same extension loaded twice = pi exits at startup with a tool-name conflict. If settings ever ends up in that state (e.g. a manualpi installadded the duplicate),/scenerefuses to switch and tells you exactly which entries to delete. - Hotfix (v0.2.2):
/scene <name>crashed withExtension "command:scene" error: target.filter is not a function— the v0.2.1 refactor passedcomputeTarget()'s{packages, skills}object where the old call site usedtarget.packages. Any scene switch hit this. Fixed, plus a command-layer smoke test (isolated viaPI_SCENES_DIR+ dynamic import in a dedicated test file, because the extension's default export resolves its base dir at module-evaluation time — a static import in tests would read/write your real~/.pi/agent).
How it works
/scene coding
│
├─ read scenes.json → target set = common ∪ coding (extends chains resolved)
├─ missing packages → confirm → `pi install` each (global scope isn't auto-installed)
├─ rewrite settings.json (see "Injection & reclamation")
└─ await ctx.reload() → extensions/skills hot-reload, session uninterrupted
Injection & reclamation (your manual config stays untouched)
~/.pi/agent/scenes-state.jsonrecords the entries this extension injected (managed)- On switch: precisely remove old managed entries first, then append new target entries that are absent
- Packages/skills you configured by hand are never touched; if a scene target overlaps a manual entry, it's
borrowedand survives switching away settings.jsonis backed up tosettings.json.scenes-bakbefore every write; writes are atomic (tmp + rename)
Field reference
| Field | Description |
|---|---|
common.packages / common.skills |
common-layer resources, always loaded |
scenes.<name>.packages |
accepts "npm:<pkg>", "git:github.com/u/r", local paths, and object form (resource filtering, same grammar as pi settings) |
scenes.<name>.icon |
status-bar badge & picker prefix (emoji recommended; default ◆) |
scenes.<name>.skills |
paths/directories, ~ expanded |
scenes.<name>.extends |
🧪 inherit a parent scene (union merge + cycle detection) — forward-compatible entry for parent→child hierarchies |
evolve.patience / evolve.absorbThreshold / evolve.skillUnusedThreshold |
self-evolution tunables (see "Self-evolution") |
Rollback
pi remove npm:pi-scenes # uninstall the extension
cp ~/.pi/agent/settings.json.scenes-bak ~/.pi/agent/settings.json # restore if needed
Before uninstalling, /scene off and prune entries you don't want to keep from packages/skills. scenes.json / scenes-state.json / the scenes/ tree are inert leftovers — safe to keep or delete.
Development
git clone https://github.com/Feng-H/pi-scenes && cd pi-scenes
npm test # node:test, 20 cases: injection/reclaim + usage/evolution + conflict guards + command-layer smoke (no TUI needed)
Tests isolate via the PI_SCENES_DIR env var — your real ~/.pi/agent is never touched.
License
MIT
中文说明
pi 场景切换器:通用层 + 分场景层的 extension / skill 打包切换。 写代码时
/scene coding,办公时/scene office——一键换装,热重载生效。
这是什么
pi 的 packages / skills 是全局平铺的:所有已安装扩展、所有 skill 同时生效。
于是上下文越来越肥——写代码时被办公 skill 占 token,办公时被代码 skill 干扰。
pi-scenes 给资源加了「场景」维度:
生效资源 = 通用层(common) ∪ 当前场景(scene)
- 通用层:任何场景下恒加载的 extension + skill(如配额显示、会话延续)
- 场景层:每个场景自己的一组 extension + skill,激活才加载
- 切换 = 改写
settings.json的packages/skills→ctx.reload()热重载,无需重启 pi - 每次切换成功后状态栏常驻场景徽标(
◆ coding,或每场景自定义icon如💻 coding),状态栏随时回答「我现在在哪个场景」——会话启动自动恢复,/scene off清除 - 零记忆可发现性(v0.4.1)——命令提示行直接拼入场景名清单;Tab 补全列出全部场景(icon + 描述 + 当前标记)与子命令:
/scene c+ Tab →/scene coding;/scenes复数别名同义可用 - 数据模型预留
extends继承链(带环检测),为将来「主场景 → 子场景」层级铺路
安装
pi install npm:pi-scenes
或从 git 直装(无需构建,直接加载 TS 源码):
pi install git:github.com/Feng-H/pi-scenes
然后 /reload,/scene 生效。
快速开始
/scene # 弹出选择器:列出所有场景,● 当前,○ 可切
/scene <Tab> # Tab 补全:列出全部场景(icon + 描述)与子命令
/scene c<Tab> # 补齐为 /scene coding —— 无需记忆任何场景名
/scene coding # 直接切到 coding 场景
/scene office # 切到办公场景
/scene off # 仅保留通用层(关闭场景)
/scene status # 查看当前激活 + 生效的 packages/skills 清单
/scene init # 生成模板 scenes.json + 场景 skill 目录骨架
/scene stats # 用量仪表盘:会话数 / 工具调用 / 反思评分
/scene evolve # 生成并应用进化提案(逐条确认)
/scene evolve auto # 开关:会话结束自动应用(opt-in)
/scenes ... # 复数别名:与 /scene 完全同义,补全行为一致
首次运行 /scene 会询问是否生成模板,生成后编辑场景定义。模板内置 七个预设场景 + 通用层(包均在 npm 核验存在,2026-09):
| 层 | 预设 packages | 理由 |
|---|---|---|
common |
npm:pi-scenes、npm:pi-carryover |
切换器自身 + 跨会话承接,恒需 |
coding |
npm:pi-lens、npm:pi-subagents、npm:pi-git-worktree |
LSP/lint 实时反馈、子代理委派、worktree 并行开发 |
office |
npm:pi-docparser |
PDF/Office 文档解析 |
pm |
npm:pi-web-access、npm:pi-goal-x、npm:@juicesharp/rpiv-todo |
竞品/市场调研、目标规划与完成度审计、需求/任务清单 overlay |
research |
npm:pi-web-access、npm:pi-subagents |
多源搜索/抓取/PDF/视频,并行多角度深挖 |
writing |
npm:pi-web-access |
素材检索与事实核查(引用溯源) |
data |
npm:pi-docparser、npm:pi-mcp-adapter |
表格结构化抽取、接任意 MCP server(数据库/BI) |
每个场景同时生成 skill 目录骨架 ~/.pi/agent/scenes/<名>/skills/,按需编辑:
// ~/.pi/agent/scenes.json
{
"common": { // ── 通用层:所有场景恒加载
"description": "通用层",
"packages": ["npm:pi-zai-usage"],
"skills": ["~/.pi/agent/scenes/common/skills"]
},
"scenes": { // ── 场景层:激活才叠加
"coding": {
"description": "写代码",
"icon": "💻",
"packages": ["npm:pi-carryover"],
"skills": ["~/.pi/agent/scenes/coding/skills"]
},
"office": {
"description": "办公",
"packages": [
{ "source": "npm:pi-docparser", "skills": ["doc-parse"] } // object form = 只加载该包部分资源
],
"skills": ["~/.pi/agent/scenes/office/skills"]
}
}
}
场景 skill 目录里放 SKILL.md 文件夹(或 .md 文件)即可,切换场景时整目录启停。
/scene init 会创建 ~/.pi/agent/scenes/{common,coding,office,pm,research,writing,data}/skills/ 骨架。
状态栏场景徽标
切换成功后状态栏常驻徽标(◆ coding),每次会话启动自动恢复,/scene off 清除。状态栏应该回答「我现在在哪个场景」——而不是展示工具的内部状态。
徽标前缀可按场景用 icon 字段定制——emoji 完全可用(💻 coding)。脚手架预设自带 💻 📄 🎯 🔍 📝 📊,选择器与 /scene status 同步显示同一 icon;未配置的场景回退 ◆。
场景常打包工具型扩展,它们自己的 footer 输出会把状态栏拼得很吵。若使用预设 coding 场景,pi-lens 的诊断 widget 与 LSP Inactive 状态可在不损失任何 AI 侧价值(turn-end 错误注入、lens_diagnostics、符号导航)的前提下静音,写入 ~/.pi-lens/config.json:
{ "ui": { "hideLspStatus": true }, "widget": { "visible": false } }
pi-scenes 绝不改第三方包的全局配置——徽标是它放到状态栏上的唯一东西。
自进化(用量驱动)
场景不是静态的。pi-scenes 观察你的实际使用并提议更新:
- 采集(被动,零感知)——每次
tool_call通过静态扫描安装源码归因到包;settings 里存在但不在任何场景定义的包记为吸收候选;每个会话结束用一次小 LLM 调用反思哪些已加载 skill 真正有用(几百 token、30s 超时、失败静默)。 - 提案——
/scene evolve生成提案:- 吸收:未纳管的包在 ≥2 个会话中出现 → 提议加入观察到的场景
- 淘汰:场景包连续 20 个会话零调用(且必须有过工具信号——纯命令包受保护)→ 提议移出;skill 反思 5 次以上「未用到」且 0 次「有用」→ 提议移出
- 保护:
common通用层与无工具信号的包(如只有/anywhere命令的扩展)永不被自动变更,只能用户手动改
- 应用(确认优先)——每条提案展示
scenes.jsondiff 并逐条确认;接受后自动备份(scenes.json.scenes-bak)并热重载。/scene evolve auto开启会话结束静默应用(下次自然重载生效)。
阈值在 scenes.json 可调:
{
"evolve": {
"patience": 20, // 连续零调用会话数 → 淘汰
"absorbThreshold": 2, // 未纳管出现次数 → 吸收
"skillUnusedThreshold": 5 // skill 反思未用到次数 → 淘汰
}
}
用量数据:~/.pi/agent/scenes-usage.json(本机局部、惰性)。随时 /scene stats 查看。
安装 ≠ 加载(两层模型)
- 加载只看 settings.json。 切换 = 改写 packages/skills 数组 +
ctx.reload();不在数组里的资源不会被加载——不注册工具、不占 prompt token。场景 skill 目录(~/.pi/agent/scenes/<名>/skills)不是 pi 自动发现路径,天然随场景启停。 - 磁盘文件保留。 切走不卸载(彻底清理用
pi remove);不在 settings 里的包只是惰性文件——切回秒级,零运行时成本。
一句话:永远安装,只按场景加载。注意:你手工 pi install 的条目属于你自己——pi-scenes 视为 borrowed,永不触碰,因此在所有场景都保持加载。
包更新与场景无关。 无论包被哪个场景引用,都只在全局安装一份(~/.pi/agent/npm/),场景只是拨动 settings.json 里的开关。因此 pi update --extensions 与当前在哪个场景无关(pi 的更新提示扫的是已安装包,不是活跃场景),且场景定义写的是不带版本的 npm:<pkg>,更新后下次切换/重载自动加载新版。注意 0.x 的 caret 范围不跨 minor——若更新提示反复出现,显式装指定版本:pi install npm:<pkg>@<版本>。
重复与冲突处理
- 同一写法在 common 与场景重复 → 并集时去重,只加载一次。
- 同包异写法(如
npm:x@1.0.3与裸名npm:x)→ 身份级去重取首个(common 优先);settings 里手动 pin 的异写法条目视为borrowed,不重复注入。/scene切换与/scene status会对异写法发出提醒,建议统一。 - 本地路径与 npm 写法并存(v0.2.1): 若 settings 已通过本地路径加载某包(如你的开发目录
/Users/you/dev/pi-carryover),场景预设里的npm:pi-carryover会借用你的条目而不安装——跳过pi install,绝不向 settings 追加第二种写法。这一点很关键:同一包双写法 = 同一扩展被加载两份 = pi 启动时工具重名冲突退出。若 settings 已陷入该状态(如手动pi install造成的重复),/scene会拒绝切换并明确告知需要删除哪个条目。 - 热修(v0.2.2):
/scene <name>曾报Extension "command:scene" error: target.filter is not a function——v0.2.1 重构时把computeTarget()返回的{packages, skills}对象传给了旧调用点用target.packages的地方,任何场景切换必触发。已修复,并补 command 层冒烟测试(独立测试文件 +PI_SCENES_DIR环境变量 + 动态 import 隔离:扩展 default export 在模块求值时解析 base 目录,测试里静态 import 会直接读写真实~/.pi/agent)。
工作原理
/scene coding
│
├─ 读 scenes.json → 生效集合 = common ∪ coding(extends 链自动展开)
├─ 缺失的包 → confirm 后逐个 `pi install`(全局 scope pi 不自动装)
├─ 改写 settings.json(见「注入与回收」)
└─ await ctx.reload() → 扩展/skill 热重载,会话不中断
注入与回收(不动你的手工配置)
~/.pi/agent/scenes-state.json记录本扩展上次注入settings.json的条目(managed)- 切换时:先精确摘除 managed 旧条目,再追加新目标中缺失的条目
- 你手工配置的 packages/skills 永不触碰;若场景目标与你手配重合,该条目视为
borrowed,切走时保留 - 每次写
settings.json前自动备份为settings.json.scenes-bak,写入走 tmp+rename 原子替换
字段说明
| 字段 | 说明 |
|---|---|
common.packages / common.skills |
通用层资源,恒加载 |
scenes.<name>.packages |
支持 "npm:<pkg>"、"git:github.com/u/r"、本地路径字符串,及 object form(资源过滤,同 pi settings 规范) |
scenes.<name>.icon |
状态栏徽标与选择器前缀(建议 emoji;缺省 ◆) |
scenes.<name>.skills |
路径/目录数组,支持 ~ 展开 |
scenes.<name>.extends |
🧪 继承父场景(union 合并,带环检测)——「主场景→子场景」层级的前向兼容入口 |
evolve.patience / evolve.absorbThreshold / evolve.skillUnusedThreshold |
自进化阈值(见「自进化」节) |
回退方案
pi remove npm:pi-scenes # 卸载扩展
cp ~/.pi/agent/settings.json.scenes-bak ~/.pi/agent/settings.json # 如需恢复
managed 注入的条目在卸载前建议先 /scene off + 手工清理 packages/skills 里不想保留的条目;scenes.json / scenes-state.json / scenes/ 目录留着不影响 pi 运行。
开发
git clone https://github.com/Feng-H/pi-scenes && cd pi-scenes
npm test # node:test,20 用例:注入/回收 + 用量/进化 + 异写法冲突防护 + command 层冒烟(无需 TUI)
测试用 PI_SCENES_DIR 环境变量隔离基目录,不碰真实 ~/.pi/agent。
License
MIT