pi-rename1.2.0
Pi coding agent extension — auto-name a new session after the first turn, /rename from the last 5 turns, and mirror the session name into the terminal tab title (VS Code pwsh / Git Bash; patches terminal.integrated.tabs.title on Windows)
Untested on omp
This is a pi package. No one has reported whether it works on omp yet. If you try it, add your result below.
Reports 0
No reports yet.
Readme
pi-rename
pi coding agent 扩展:根据对话内容给会话重命名,并把名字同步成终端标题。
- 新会话第一轮对话结束后,自动起一个名字(已有名字的会话不会被覆盖)。
/rename—— 按最近 5 轮对话重新命名,有名字就覆盖。/rename model—— 打开选择器直接选,从可用模型里挑;session model那一项 = 回到会话模型。/rename model <provider/id>—— 指定用来起名字的模型(支持 id 子串模糊匹配)。/rename model show—— 打印当前实际生效的模型,不改配置。/rename model list—— 打印可用模型列表,不弹选择器(print/json 模式下不带参数也是这个)。/rename reasoning <minimal|low|medium|high>—— 起名调用的思考等级,默认low;不带参数打印当前值。不提供off:不传等级等于显式关闭思考,只支持思考的中转会直接拒绝(旧配置里存的off会自动矫正为low)。/rename on//rename off—— 开启 / 关闭自动重命名。/rename vscode—— 手动重跑一次 VS Code 标签标题设置检查(见下文)。
安装
pi install npm:pi-rename
或写进 ~/.pi/agent/settings.json:
{ "packages": ["npm:pi-rename"] }
安装后在 pi 里执行 /reload 生效(首次安装会自动发现)。单次试用:pi -e npm:pi-rename。
本地开发版:pi install ./pi-rename,或 pi -e ./extensions/index.ts。
命令
| 命令 | 作用 |
|---|---|
/rename |
取最近 5 轮对话让模型起名字,覆盖当前名字 |
/rename <文本> |
直接用这段文本当名字(覆盖) |
/rename on / /rename off |
开/关「第一轮结束后自动重命名」,写进配置文件 |
/rename model |
不带参数:打开选择器,从可用模型里选 |
/rename model <provider/id> |
设置起名字用的模型(支持 id 子串模糊匹配) |
/rename model default |
取消设置,回到「用当前会话模型」 |
/rename model show |
打印当前实际生效的模型 |
/rename model list |
打印可用模型列表,不弹选择器 |
/rename reasoning <level> |
起名的思考等级(minimal / low / medium / high) |
/rename reasoning |
不带参数:打印当前等级 |
/rename vscode |
手动重跑 VS Code 标签标题设置检查(详见下文「终端标题」) |
配置在 ~/.pi/agent/pi-rename/config.json(用 PI_RENAME_CONFIG_DIR 可换目录),只有三个字段:
{ "auto": true, "model": "", "reasoning": "low" }
命名规则
两条触发路径各自看到多少轮(窗口都是 RECENT_TURNS = 5):
| 触发 | 时机 | 实际送入的轮数 |
|---|---|---|
| 自动重命名 | 新会话第一轮 agent_settled(非 Esc 打断) |
1 轮 —— 此时转录里只有第一轮,拿不到更多 |
/rename |
你主动执行 | 最近 5 轮;不足 5 轮就全拿 |
所以上面那些「只拿到很少上下文」的情况不是窗口配小了,而是超预算时被整轮丢了——现在只有用户消息本身巨大到装不下才会发生,而且提示词头部的轮数会写真实值。
送给模型的上下文(extensions/naming.ts):
- 只读当前分支
ctx.sessionManager.getBranch(),取最近 5 轮; - 用户消息全文发送,不截断;
- 每轮 只带最后一个 assistant 正文块(工具调用之间的「我先读一下文件」这类过程话不要),
toolResult/thinking/ tool call / 图片全部丢弃; - 一轮 = 一条有文字的 user 消息 + 其后的 assistant 文字;
- 超预算(12000 字符)时的取舍:先按阶段
3000 → 1200 → 400字符裁 assistant 答案,尽量把 5 轮都保住;真装不下(用户消息本身就是几万字)才整轮丢掉最旧的,且提示词头部的轮数会写实际发送的轮数(不会谎称 5 turns)。
名字长度:上限 15 个单位,1 个中日韩汉字/假名/谚文 = 1 单位,1 个英文(或其他拼音文字)单词 = 1 单位,空格标点不计。pi插件 = 3 单位,rename the session = 3 单位。截断按单位边界切,不加省略号。
语言:从用户消息的字符构成判定(汉字/假名/谚文/西里尔/拉丁占比),判出的语言同时写进 system prompt 和 user 消息里,例如中文会话 → Write the name in: Chinese (Simplified unless the user writes Traditional)。只看用户侧,不看 assistant 侧,避免被模型输出语言带偏。
不拿「回答要求」当主题:实测发生过会话被命名成「一句话回答」,因为用户消息尾部写了「只用一句话回答」而模型把它当成主题。system prompt 因此明写:忽略关于回复格式/长度的指令,命名的是对话内容本身。
实际发出的请求:一次隔离的单轮调用,不带 pi 的 system prompt、不带工具、不进转录。
system: "You name AI coding-assistant sessions … - Length limit: at most 15 units … - Write the name in: <判定出的语言> …"
user: "Name this conversation (5 turns) in <语言>, within 15 units:\n\nUSER: …\nASSISTANT: …\n\nName:"
命名请求只带一个模型参数:reasoning: <等级>(默认 low),外加把所有等级的思考 token 预算钉在 1024(thinkingBudgets)。temperature、maxTokens、cacheRetention 一律不传,走模型默认,输出完全由提示词控制。
reasoning 必须是真实等级、不能省:不传等于显式关闭思考(Anthropic 适配层发 thinking:{type:"disabled"},OpenAI 适配层发 enable_thinking:false),“始终思考”的中转会直接 400,报“该模型始终思考,不支持关闭思考”。思考预算钉在 1024 是因为部分 Anthropic 格式中转把 budget_tokens 当思考档位开关:实测某中转(glm-5.3-flash)对 ≥1025 的预算一律 400(误导性地报同一句“不支持关闭思考”),1024 则正常;而 pi-ai 对 low 的默认预算恰好是 2048,不钉住就炸。effort 型 provider(OpenAI 兼容)收到的是 reasoning_effort,thinkingBudgets 对它们无效也无害。模型默认用当前会话模型,/rename model 可换便宜的。
返回后清洗:剥 ``` 围栏 → 只取第一个非空行 → 去 -/*/#/>/引号装饰与 Title:/标题: 前缀 → 去结尾标点 → 折叠空白 → 按 15 单位截断 → 剥控制字符。清洗后一个实词都不剩(或调用失败)就退化成窗口里第一条用户消息的第一行(同样限 15 单位),并 notify 一条 warning。
/rename <文本> 不走清洗截断——你打的什么就是什么,只折叠空白和控制字符。
终端标题
命名之后调 ctx.ui.setTitle(name):TUI 模式发出 OSC 标题序列,RPC 模式转成 extension_ui_request/setTitle 交给宿主。pi 原生在 session_info_changed 时把标题刷成 π - <name> - <cwd>,我们随后覆盖成纯名字;你自己用 pi 的 /name 或 --name 改名时,标题也会跟着同步(挂了同一个事件)。
VS Code(pwsh / Git Bash)
pi 发的标题序列本身不分平台:ctx.ui.setTitle() 在 Windows 上同样向 stdout 写 OSC 0(pi-tui 的 terminal.setTitle 没有 win32 分支),ConPTY 会把它转交给 VS Code。所以窗口标题变了但标签栏没跟着变时,是 VS Code 标签标题模板没读 shell 上报的标题,改成:
{ "terminal.integrated.tabs.title": "${sequence}" }
这个设置在 Windows 上尤其必要:Windows 终端的默认标签模板不会跟随 OSC 序列刷新标签(macOS/Linux 上多数默认模板会)。改完重开一个终端标签生效。
插件会替你改:从 v1.2.0 起,pi 在 Windows 上启动会话时自动检查用户级 settings.json(存在即认为装了 VS Code;按 %APPDATA%\Code\User\settings.json → Insiders → VSCodium 的顺序找),只要 terminal.integrated.tabs.title 还不是 ${sequence},就直接写进去,文件里其它设置原样保留。也就是说这一节的手工步骤通常不用做了:
- 没装 VS Code(找不到任何 settings.json)→ 什么都不写,只在 pi 里提示一句;
- settings.json 带 JSONC 注释/尾逗号 → 照常解析,改完回写成 VS Code 自己的制表符缩进风格;
- 文件解析失败(手写坏了)→ 不碰它,报一条 warning 让你自己修;
- 已经是
${sequence}→ 跳过,不重写文件; - 非 Windows 平台默认不动你的配置(那边的默认模板本来就跟随 OSC),想改随时
/rename vscode手动触发一次; - 想跳过这个行为 → 在 settings.json 里预先写好
${sequence},或在 VS Code 设置里关掉后插件只会提示「已是目标值」而不再写入。
再核对三层排查顺序:
- 窗口标题变了,标签没变 → 上面的
${sequence}模板,改完一定生效。 - 窗口标题也没变 → pi 这边确实发出了序列,但被别的东西盖掉了。两个已知来源:
- VS Code 的 Terminal: Rename 手动改过标签名会永久压住 shell 上报的标题,需要 Terminal: Clear Rename 或开新终端;
- pi 启动时的扩展检查(npm 进程)在 Windows 上会重写控制台标题,pi 自己会在检查结束后恢复标题 —— 但如果你在恢复之前重命名过,标题会被恢复动作盖回
π - <name> - <cwd>。重跑一次/rename即可。
- pi 退出后标题被打回原形 → pwsh / Git Bash 在下一次画提示符时把标题写回去,这是 shell 行为,不是扩展失效。
行为细节
- 自动重命名每个会话只尝试一次:尝试前
pi.appendEntry("pi-rename", …)记一条不进模型上下文的 custom entry,/reload、--continue、--resume扫分支恢复状态,不会重复烧 token。 - Esc 打断(
agent_settled.aborted === true)不重命名。 - 只在
tui/rpc模式自动重命名;pi -p/--mode json不做额外模型调用。 - 手动
/rename之间有并发锁,不会同时发两个命名请求。
开发
pi-rename/
├── extensions/index.ts 事件与命令接线
├── extensions/naming.ts 纯函数:轮次提取、单位计数、语言判定、prompt 组装、名字清洗
├── test/naming.test.ts 纯函数用例
├── test/extension.test.ts mock ExtensionAPI,跑自动重命名 / /rename / model / 标题
├── tsconfig.json 只校验 extensions/
└── tsconfig.test.json 加 bun types,校验 extensions/ + test/
npm run check # tsc + tsc(test) + bun test(79 个用例)
pi -e ./extensions/index.ts
English
pi coding agent extension: names your sessions from the conversation, and mirrors that name into the terminal tab title.
- A new session gets named automatically after its first turn (sessions that already have a name are never overwritten).
/rename— re-name from the last 5 turns, overwriting the current name./rename model— opens a picker over the available models; thesession modelentry falls back to the session's own model./rename model <provider/id>— pick the model used for naming (substring / fuzzy id match)./rename model show— print the model that is actually in effect, without changing anything./rename model list— print the available models instead of opening a picker (also the no-argument behaviour in print/json mode)./rename reasoning <minimal|low|medium|high>— thinking level for the naming call, defaultlow; no argument prints the current level.offis deliberately not offered: omitting the level is what turns thinking off, which always-thinking relays reject (a storedofffrom older versions is coerced tolow)./rename on//rename off— enable / disable auto-renaming./rename vscode— manually re-run the VS Code tab-title settings check (see below).
Install
pi install npm:pi-rename
Or add it to ~/.pi/agent/settings.json:
{ "packages": ["npm:pi-rename"] }
Run /reload in pi afterwards (auto-discovered on first install). To try it once without installing: pi -e npm:pi-rename.
Commands
| Command | Effect |
|---|---|
/rename |
Name the session from the last 5 turns, overwriting the current name |
/rename <text> |
Use this text as the name, verbatim (overwrite) |
/rename on / /rename off |
Turn auto-renaming after the first turn on / off (persisted) |
/rename model |
No argument: open a picker over the available models |
/rename model <provider/id> |
Set the naming model (substring / fuzzy id match) |
/rename model default |
Clear the override, fall back to the current session model |
/rename model show |
Print the model that is actually in effect |
/rename model list |
Print the available models, no picker |
/rename reasoning <level> |
起名的思考等级(minimal / low / medium / high) |
/rename reasoning |
不带参数:打印当前等级 |
/rename vscode |
手动重跑 VS Code 标签标题设置检查(详见下文「终端标题」) |
Config lives in ~/.pi/agent/pi-rename/config.json (override the directory with PI_RENAME_CONFIG_DIR), three fields:
{ "auto": true, "model": "", "reasoning": "low" }
How the name is produced
Both triggers use a 5-turn window (RECENT_TURNS = 5):
| Trigger | When | Turns actually available |
|---|---|---|
| Auto-rename | First turn of a new session settles (agent_settled, not aborted) |
1 turn — the transcript holds nothing else yet |
/rename |
You run it | Last 5 turns, or all of them if there are fewer |
Context sent to the model (extensions/naming.ts):
- Only the current branch is read, via
ctx.sessionManager.getBranch(), last 5 turns; - User messages are sent in full, never truncated;
- Per turn only the final assistant text block is kept (filler like "let me read that file" between tool calls is dropped);
toolResult/thinking/ tool calls / images are all discarded; - A turn = one user message with text + the assistant text after it;
- Over the 12000-character budget: assistant answers are clipped first (
3000 → 1200 → 400chars) so all 5 turns survive; only when a user message alone is unclippable is the oldest whole turn dropped — and the prompt header then states the real turn count instead of claiming 5.
Length limit: 15 units, where 1 CJK ideograph / kana / hangul = 1 unit and 1 English (or other alphabetic) word = 1 unit; spaces and punctuation don't count. pi plugin = 2 units, rename the session = 3 units. Truncation cuts on unit boundaries, no ellipsis.
Language is detected from the user messages only (share of CJK / Cyrillic / Latin characters) and written into both the system prompt and the user message, e.g. a Chinese session → Write the name in: Chinese (Simplified unless the user writes Traditional). Looking only at the user side keeps the name from being dragged into the model's own output language.
Formatting requests are not a topic. A session once got named "answer in one sentence" because the user's last line said so; the system prompt now explicitly says to ignore instructions about reply format or length and name the content instead.
The request itself is one isolated single-turn call carrying exactly one model parameter: reasoning: <level> (default low), plus thinkingBudgets pinning every level's token budget to 1024. No temperature, no maxTokens, no cacheRetention — provider defaults apply and the prompt alone shapes the answer.
reasoning must be a real level and can never be omitted: omitting it is the explicit thinking-disable signal (the Anthropic adapter sends thinking:{type:"disabled"}, OpenAI adapters send enable_thinking:false), which always-thinking relays reject with a 400 (e.g. “该模型始终思考,不支持关闭思考”). The 1024 budget cap exists because some Anthropic-format relays treat budget_tokens as a thinking-tier switch: one such relay (glm-5.3-flash) was verified to 400 on any budget ≥ 1025 — misleadingly reporting the same “doesn't support disabling thinking” error — while 1024 works; pi-ai's default budget for low is 2048, so without the cap the call fails on those gateways. Effort-style providers receive reasoning_effort and ignore thinkingBudgets entirely.
Post-processing: strip ``` fences → take the first non-empty line → drop -/*/#/> decoration and Title: / 标题: prefixes → strip trailing punctuation → collapse whitespace → truncate to 15 units → strip control characters. If nothing word-like survives (or the call fails), the name falls back to the first line of the first user message in the window (also capped at 15 units) and a warning is shown.
/rename <text> skips sanitization and truncation — you get exactly what you typed, apart from whitespace and control-character collapsing.
Terminal title
After naming, ctx.ui.setTitle(name) is called: TUI mode emits the OSC title sequence, RPC mode forwards extension_ui_request/setTitle to the host. pi natively refreshes the title to π - <name> - <cwd> on session_info_changed; we overwrite it with the bare name right after. Renaming through pi's own /name or --name syncs the title too (same event).
VS Code (pwsh / Git Bash)
The title sequence itself is platform-independent: ctx.ui.setTitle() writes OSC 0 to stdout on Windows too (pi-tui's terminal.setTitle has no win32 branch), and ConPTY forwards it to VS Code. So when the window title changes but the tab label doesn't, the tab title template is not reading the shell-reported title. Point it at the sequence:
{ "terminal.integrated.tabs.title": "${sequence}" }
This setting matters most on Windows: the default tab template on Windows terminals does not follow OSC sequences (on macOS/Linux most default templates do). Reopen a terminal tab after changing it.
The extension does this for you. Since v1.2.0, when a session starts on Windows, pi-rename checks the user-level settings.json (its existence means VS Code is installed; probed in the order %APPDATA%\Code\User\settings.json → Insiders → VSCodium) and, if terminal.integrated.tabs.title is not ${sequence} yet, writes it directly — every other setting is preserved. The manual step above is normally no longer needed:
- No VS Code (no settings.json anywhere) → nothing is written; pi shows one info notice;
- JSONC comments / trailing commas in settings.json → parsed fine; the file is rewritten in VS Code's own tab-indented style;
- Unparseable file (hand-broken) → left untouched, with a warning asking you to fix it;
- Already
${sequence}→ skipped, no rewrite; - On non-Windows platforms the settings are not touched by default (their default templates already follow OSC); run
/rename vscodeto trigger the check manually on any platform; - To opt out, pre-set
${sequence}yourself — the extension then only reports "already set" and never writes.
Then triage in this order:
- Window title changed, tab label didn't → the
${sequence}template above; it will work after the change. - Window title didn't change either → pi did emit the sequence but something overwrote it. Two known sources:
- A manual Terminal: Rename in VS Code permanently overrides the shell-reported title; use Terminal: Clear Rename or a fresh terminal.
- pi's startup extension check (an npm subprocess) rewrites the console title on Windows; pi restores the title afterwards — but if you renamed before the restore ran, your title gets replaced by
π - <name> - <cwd>. Run/renameonce more.
- The title reverts after pi exits → pwsh / Git Bash write their own title back on the next prompt. That's shell behaviour, not a broken extension.
Behaviour details
- Auto-rename is attempted once per session: before the attempt,
pi.appendEntry("pi-rename", …)records a custom entry that stays out of the model context;/reload,--continueand--resumerestore the state from the branch, so tokens are never spent twice. - An Esc abort (
agent_settled.aborted === true) does not rename. - Auto-rename runs only in
tui/rpcmodes;pi -pand--mode jsonmake no extra model call. - Manual
/renamecalls are serialized by a lock, so two naming requests never overlap.
Development
npm run check # tsc + tsc(test) + bun test (79 cases)
pi -e ./extensions/index.ts
License
MIT