最近在考虑从 opencode 转 pi,也阅读了很多佬友分享的配置和经验。
但是在实际上手配置之后,我发现其实很多…经常被提起的ext,实际上都有一些不太容易察觉的问题。有些ext使用了之后会大幅提高 system-prompt 的 token 用量。有些会影响原有的自带的工具(或者是对应的system prompt)。
首先是 pi-tool-display,理论上他只是一个 UI 美化的包,但是实际上因为是通过重写 read write edit 那些 tools 实现的,导致了一个很奇怪的现象。在使用这个 package 之前,我的 pi 的system prompt 是:
Available tools:
read: Read file contents
bash: Execute bash commands (ls, grep, find, etc.)
edit: Make precise file edits with exact text replacement, including multiple disjoint edits in one call
write: Create or overwrite files
todo: Manage a task list to track multi-step progress
ask_user_question: Ask the user up to 4 structured questions (2-4 options each) when requirements are ambiguous
web_search: Search the web for up-to-date information
web_fetch: Fetch and read content from a specific URL
ffgrep: Grep contents
fffind: Find files by path or glob
In addition to the tools above, you may have access to other custom tools depending on the project.
...
使用了之后就变成
Available tools:
bash: Execute bash commands (ls, grep, find, etc.)
todo: Manage a task list to track multi-step progress
web_search: Search the web for up-to-date information
web_fetch: Fetch and read content from a specific URL
ffgrep: Grep contents
fffind: Find files by path or glob
In addition to the tools above, you may have access to other custom tools depending on the project.
也就是 read write edit 那些的 snippet 不见了。理论上不是什么问题,不过还是提一下,有些 ext 可能会有意想不到的side effect。
然后是 @juicesharp/rpiv-todo,这个 todo 工具已经算是比较简洁的了(不是 claude code 那种带 subagent task assign 的重量级工具),本身的工具的定义也只需要几百token,但是注入到 system prompt 里的 guideline 却是不少:
-Use todo for complex work with 3+ steps, when the user gives you a list of tasks, or immediately after receiving new instructions to capture requirements. Skip it for single trivial tasks and purely conversational requests.
-When starting any task, mark it in_progress BEFORE beginning work. Mark it completed IMMEDIATELY when done — never batch completions. Exactly one task should be in_progress at a time.
-Never mark a task completed if tests are failing, the implementation is partial, or you hit unresolved errors — keep it in_progress and create a new task for the blocker instead.
-Task status is a 4-state machine: pending → in_progress → completed, plus deleted as a tombstone. Pass activeForm (present-continuous label, e.g. 'researching existing tool') when marking in_progress.
-To change a task's status, call update with the task id and the target status, e.g. {"action":"update","id":3,"status":"completed"} or {"action":"update","id":3,"status":"in_progress","activeForm":"writing tests"}. status is the field that changes the task; an update without a mutable field (status or another) is rejected.
-Use blockedBy to express dependencies (A is blocked by B). On create, pass blockedBy as the initial set. On update, use addBlockedBy / removeBlockedBy (additive merge — do not resend the full array). Cycles are rejected.
-list hides tombstoned (deleted) tasks by default; pass includeDeleted:true to see them. Pass status to filter by a single status.
-Subject must be short and imperative (e.g. 'Research existing tool'); description is for long-form detail. activeForm is a present-continuous label shown while in_progress.
更大的问题是。。。这个注入的内容,没有表明是使用什么工具的时候要注意。我其实有点怀疑到底有多大的用处,会不会影响agent做其他事情的效果。同一个作者的 @juicesharp/rpiv-ask-user-question 和 @juicesharp/rpiv-web-tools 也有一样的问题。
相比起来,@ff-labs/pi-fff 的 guideline 就明显规范一点。有工具名,也很简洁。
- ffgrep: prefer bare identifiers as patterns. Literal queries are most efficient.
- ffgrep: use path for include ('src/', '.ts') and exclude for noise ('test/,.min.js').
- ffgrep: caseSensitive: true when you need exact case (smart-case otherwise).
- ffgrep: after 1-2 greps, read the top match instead of more greps.
- fffind: matches the WHOLE path, not just the filename — profile hits chrome/browser/profiles/x.cc too.
- fffind: keep queries to 1-2 terms; extra words narrow.
- fffind: use for paths, not content. Use ffgrep for content.
- fffind: for exact path matches use a glob in path — e.g. path: '/profile.h' for exact filename, or path: 'src//profile.h' scoped to a subtree. Bare patterns are fuzzy.
- fffind: to list everything inside a directory, pass path: 'dir/**' with an empty or wildcard pattern instead of using pattern alone.
- fffind: use exclude: 'test/,*.min.js' to cut noise in large repos.
说这些的目的就是。。提醒各位佬,在挑选pi的插件的时候,可能不止要看看用的人多不多,实际上可能也要看看实现是否符合各位佬自己的需求和想法。然后。。。可能有些插件的实现水平还是比较参差,具体好不好用还是要多多测试,体验。