@zihanw/pi-forge 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,405 @@
1
+ # pi-forge
2
+
3
+ [English](README.md) | [简体中文](README.zh-CN.md)
4
+
5
+ **pi-forge** 让你自定义 Pi 的思考方式和行为。它提供 prompt stack(提示栈):这些 JSON 文件可以替换、追加到或插入到 Pi 的默认系统提示词之前,并控制 AI 的性格、可见工具、对话历史布局和跨轮次状态。
6
+
7
+ 可以把它理解为 AI agent 的角色卡。
8
+
9
+ ## 能做什么
10
+
11
+ - **赋予 Pi 个性** — 把它变成创意写手、角色扮演搭档、严格的代码审查员,或任何你想要的风格。
12
+ - **一键切换模式** — 在"写代码"、"写小说"、"做翻译"之间用一条命令切换。
13
+ - **控制 AI 看到什么** — 选择每个 prompt 中出现哪些工具、技能和项目上下文。
14
+ - **跨轮次记忆** — 让 agent 跟踪进度、存储笔记、记住用户偏好。
15
+ - **导入 SillyTavern 预设** — 一条命令把 ST 角色预设迁移到 Pi。
16
+ - **调试 prompt** — 拦截并查看实际发给模型的内容。
17
+
18
+ ## 快速上手
19
+
20
+ ### 安装
21
+
22
+ ```bash
23
+ pi install npm:@zihanw/pi-forge
24
+ ```
25
+
26
+ ### 第一个 prompt stack
27
+
28
+ 创建 `.pi/prompt-stacks/default.json`:
29
+
30
+ ```json
31
+ {
32
+ "schemaVersion": 1,
33
+ "type": "pi-forge.prompt-stack",
34
+ "id": "default",
35
+ "autoActivate": true,
36
+ "mode": "replace",
37
+ "items": [
38
+ {
39
+ "kind": "block",
40
+ "id": "role",
41
+ "name": "主要角色",
42
+ "enabled": true,
43
+ "role": "system",
44
+ "content": "你是一个友好简洁的编程助手,回答时优先给出简短说明和代码示例。"
45
+ },
46
+ {
47
+ "kind": "slot",
48
+ "id": "tools",
49
+ "name": "可用工具",
50
+ "enabled": true,
51
+ "role": "system",
52
+ "slot": "tools"
53
+ },
54
+ {
55
+ "kind": "slot",
56
+ "id": "chat-history",
57
+ "name": "对话历史",
58
+ "enabled": true,
59
+ "slot": "chat-history"
60
+ }
61
+ ]
62
+ }
63
+ ```
64
+
65
+ 搞定。重启 Pi 或执行 `/preset reload`。如果当前没有选中其他栈,`default.json` 会自动启用;如果你之前执行过 `/preset use none` 或选择了别的栈,请执行 `/preset use default`。
66
+
67
+ ### 可视化编辑器
68
+
69
+ 不想手写 JSON?pi-forge 内置了 Web 编辑器:
70
+
71
+ ```
72
+ /preset ui
73
+ ```
74
+
75
+ 拖拽、编辑、校验、预览、导入、导出、fork、删除栈 —— 全在浏览器里完成。
76
+
77
+ 导入支持原生 pi-forge stack JSON,也支持 SillyTavern 预设 JSON。SillyTavern 预设会自动转换成 prompt stack;如果一个预设里有多个 `character_id` 配置,编辑器会询问要使用哪一个。
78
+
79
+ 编辑器默认运行在 `127.0.0.1:41738`,并带有会话 token。写入需要项目被信任,且只会写入 `.pi/prompt-stacks`;保存、导入、fork、删除成功后会重新加载到当前 Pi 会话。需要时可以用 `/preset ui restart` 或 `/preset ui stop`。
80
+
81
+ 如果要使用别的固定端口,可以创建 `.pi/forge/config.json`:
82
+
83
+ ```json
84
+ {
85
+ "webEditor": {
86
+ "port": 41738
87
+ }
88
+ }
89
+ ```
90
+
91
+ ## 使用场景
92
+
93
+ ### 🎭 角色扮演 & 创意写作
94
+
95
+ 让 Pi 扮演一个角色。在系统提示词中定义性格,用 user message 注入写作风格规则,用 `{{lastUserMessage}}` 在对话历史之后重新插入用户输入。
96
+
97
+ 常用模式:
98
+ - 把长期角色规则放在 `system` block。
99
+ - 把 Pi 运行时上下文(工具、技能、项目)放在 `user` slot。
100
+ - 把 `chat-history` slot 设为跳过最新用户消息。
101
+ - 在最后加一个带 `{{lastUserMessage}}` 的 `user` block。
102
+
103
+ 这样最新请求会更清晰,也不会重复出现。
104
+
105
+ 可复制的起步示例见 [examples/default-prompt-stack.json](examples/default-prompt-stack.json)。
106
+
107
+ ### 🧑‍💻 专注代码审查
108
+
109
+ 创建一个 `reviewer.json` 栈,加入严格的审查规则,例如“优先检查正确性、回归风险、安全问题和缺失测试”。保留 `tools`、`project-context`、`variables` 和 `chat-history` slot,这样 Pi 仍然能检查仓库并记住审查状态。
110
+
111
+ 如果你想保留 Pi 原本的编程行为,只额外加上更严格的审查视角,可以使用 `mode: "append"`。
112
+
113
+ ### 🌐 翻译模式
114
+
115
+ 创建一个小型 `translator.json` 栈,用一个 system block 指定语气和目标语言,再保留 `chat-history` 和 `{{lastUserMessage}}` 的布局。这样可以在双语润色、直译、产品本地化审查之间快速切换,而不影响默认助手。
116
+
117
+ ### 🔀 多模式切换
118
+
119
+ 为不同任务创建独立的栈:
120
+
121
+ ```
122
+ .pi/prompt-stacks/
123
+ coder.json # 严格编程助手
124
+ writer.json # 创意写作搭档
125
+ translator.json # 双语翻译
126
+ ```
127
+
128
+ 用 `/preset use coder`、`/preset use writer` 等命令切换。
129
+
130
+ ### 🧠 跨轮次记忆
131
+
132
+ 定义 agent 可读写的状态:
133
+
134
+ ```json
135
+ "state": {
136
+ "definitions": {
137
+ "agent.progress": {
138
+ "type": "string",
139
+ "scope": "session",
140
+ "description": "当前任务进度",
141
+ "agentWritable": true
142
+ }
143
+ }
144
+ }
145
+ ```
146
+
147
+ Agent 用 `forge_state_set` 更新状态。你也可以手动设置:
148
+
149
+ ```
150
+ /state set user.preference "用 TypeScript,别用 JavaScript"
151
+ ```
152
+
153
+ ### 📦 SillyTavern 迁移
154
+
155
+ 把 ST 预设导入 Pi:
156
+
157
+ ```
158
+ /preset import-silly ~/SillyTavern/presets/my-preset.json
159
+ ```
160
+
161
+ pi-forge 会把预设转换为 prompt stack,并生成迁移报告,标明哪些已处理、哪些需要手动调整。
162
+
163
+ ### 🔍 Prompt 调试
164
+
165
+ 查看实际发给模型的内容:
166
+
167
+ ```
168
+ /payload next save=.pi/forge/payloads/last.json
169
+ ```
170
+
171
+ 或者不发送只预览编译结果:
172
+
173
+ ```
174
+ /preset preview
175
+ ```
176
+
177
+ ## 工作原理
178
+
179
+ 一个 prompt stack 是一个 JSON 文件,包含两种条目:
180
+
181
+ | 类型 | 作用 |
182
+ |------|------|
183
+ | **Block** | 在指定位置插入的静态文本(系统提示词、用户消息、助手消息) |
184
+ | **Slot** | 来自 Pi 运行时的动态内容 —— 工具、技能、对话历史、日期、项目上下文等 |
185
+
186
+ 条目按顺序排列。当栈激活时,pi-forge 会:
187
+
188
+ 1. 用你的 `system` 角色 block 和 slot 生成系统提示词,然后按照栈的 `mode` 应用。
189
+ 2. 在对话历史周围插入 `user`/`assistant` 角色的 block 和 slot。
190
+ 3. 展开 `{{宏}}`,如 `{{lastUserMessage}}`、`{{date}}` 和自定义变量。
191
+
192
+ ### Slot 一览
193
+
194
+ | Slot | 插入的内容 |
195
+ |------|-----------|
196
+ | `chat-history` | 当前对话 |
197
+ | `tools` | 可用工具及其描述 |
198
+ | `tool-guidelines` | 工具使用指导 |
199
+ | `skills` | 已加载的 Pi 技能 |
200
+ | `project-context` | 项目指令和上下文文件 |
201
+ | `variables` | Agent 和用户状态(进度、偏好、笔记) |
202
+ | `date` / `cwd` / `date-cwd` | 当前日期和工作目录 |
203
+ | `active-model` | 当前使用的模型 |
204
+ | `append-system-prompt` | 用户追加的系统提示词 |
205
+ | `pi-docs` | Pi 文档指导 |
206
+
207
+ ### 模式
208
+
209
+ - **replace**(默认)— 你的栈完全替换 Pi 的系统提示词。
210
+ - **append** — 你的栈追加在 Pi 默认系统提示词之后。
211
+ - **prepend** — 你的栈插入在 Pi 默认系统提示词之前。
212
+
213
+ ## 常用命令
214
+
215
+ ### 管理 prompt stack
216
+
217
+ | 命令 | 作用 |
218
+ |------|------|
219
+ | `/preset list` | 显示所有可用栈 |
220
+ | `/preset use <id>` | 激活一个栈 |
221
+ | `/preset use none` | 在当前会话中禁用 prompt stack |
222
+ | `/preset preview [id]` | 查看编译后的 prompt |
223
+ | `/preset validate [id]` | 检查栈是否有问题 |
224
+ | `/preset status` | 显示当前激活栈和诊断摘要 |
225
+ | `/preset diagnostics` | 显示运行时诊断 |
226
+ | `/preset reload` | 从磁盘重新加载栈 |
227
+ | `/preset ui [stop\|restart]` | 打开、停止或重启 Web 编辑器 |
228
+
229
+ ### 状态管理
230
+
231
+ | 命令 | 作用 |
232
+ |------|------|
233
+ | `/state list` | 显示所有会话状态 |
234
+ | `/state status` | 显示状态定义和当前值 |
235
+ | `/state set <name> <value>` | 设置状态变量 |
236
+ | `/state get <name>` | 读取状态变量 |
237
+ | `/state clear [name]` | 清除状态(全部或按名称) |
238
+ | `/preset vars ...` | 为旧栈保留的兼容变量命令 |
239
+
240
+ ### 导入 & 调试
241
+
242
+ | 命令 | 作用 |
243
+ |------|------|
244
+ | `/preset import-silly <path>` | 导入 SillyTavern 预设 |
245
+ | `/intercept` | 显示下一条 provider payload |
246
+ | `/payload next [save=<path>]` | 显示并可保存下一条 payload |
247
+
248
+ ## 常用宏
249
+
250
+ 在 block 内容中使用这些宏来插入动态值:
251
+
252
+ | 宏 | 展开为 |
253
+ |----|--------|
254
+ | `{{lastUserMessage}}` | 用户最新消息 |
255
+ | `{{date}}` | 当前日期 (YYYY-MM-DD) |
256
+ | `{{time}}` | 当前时间 (HH:MM:SS) |
257
+ | `{{cwd}}` | 当前工作目录 |
258
+ | `{{tools}}` | 逗号分隔的工具名 |
259
+ | `{{selectedTools}}` | 所选工具名的别名 |
260
+ | `{{activeModel}}` | 当前模型 (provider/id) |
261
+ | `{{char}}` / `{{user}}` | 栈中定义的自定义变量 |
262
+
263
+ ### 变量宏
264
+
265
+ ```
266
+ {{setvar::name::value}} 设置轮次变量(每条消息清空)
267
+ {{setsessionvar::name::value}} 设置会话变量(持久化)
268
+ {{setvar::session::name::value}} 也可设置会话变量
269
+ {{getvar::name}} 读取变量(轮次 → 会话 → 静态)
270
+ {{getturnvar::name}} 只读取轮次变量
271
+ {{getsessionvar::name}} 只读取会话变量
272
+ {{clearvar::name}} 清除变量
273
+ {{clearturnvar::name}} 清除轮次变量
274
+ {{clearsessionvar::name}} 清除会话变量
275
+ ```
276
+
277
+ ## Stack 参考
278
+
279
+ ### 完整条目类型
280
+
281
+ **Block:**
282
+
283
+ ```json
284
+ {
285
+ "kind": "block",
286
+ "id": "unique-id",
287
+ "name": "可读标签",
288
+ "enabled": true,
289
+ "role": "system",
290
+ "content": "你的文本。用 {{宏}} 插入动态内容。"
291
+ }
292
+ ```
293
+
294
+ 有效角色:`system`、`user`、`assistant`、`custom`。
295
+
296
+ **Slot:**
297
+
298
+ ```json
299
+ {
300
+ "kind": "slot",
301
+ "id": "unique-id",
302
+ "name": "对话历史",
303
+ "enabled": true,
304
+ "role": "user",
305
+ "slot": "chat-history",
306
+ "options": {
307
+ "includeLastUserMessage": false
308
+ }
309
+ }
310
+ ```
311
+
312
+ ### Chat history 选项
313
+
314
+ ```json
315
+ "options": {
316
+ "includeLastUserMessage": false
317
+ }
318
+ ```
319
+
320
+ 当你在 history 之后使用 `{{lastUserMessage}}` 时设为 `false`,避免用户消息出现两次。
321
+
322
+ ### Variables slot 选项
323
+
324
+ ```json
325
+ {
326
+ "kind": "slot",
327
+ "id": "state",
328
+ "enabled": true,
329
+ "role": "user",
330
+ "slot": "variables",
331
+ "options": {
332
+ "includeScopes": ["session"],
333
+ "includeNamespaces": ["user.*", "agent.*"],
334
+ "includeMetadata": true,
335
+ "format": "xml",
336
+ "maxValueChars": 1200
337
+ }
338
+ }
339
+ ```
340
+
341
+ ### 状态定义
342
+
343
+ ```json
344
+ "state": {
345
+ "schemaVersion": 1,
346
+ "definitions": {
347
+ "agent.progress": {
348
+ "type": "string",
349
+ "scope": "session",
350
+ "description": "当前任务进度",
351
+ "agentWritable": true
352
+ },
353
+ "user.preference": {
354
+ "type": "string",
355
+ "scope": "session",
356
+ "description": "用户在本会话中的偏好",
357
+ "userWritable": true
358
+ }
359
+ }
360
+ }
361
+ ```
362
+
363
+ 支持的类型:`string`、`number`、`boolean`、`null`、`object`、`array`、`string[]`、`number[]`、`boolean[]`、`unknown`,以及 `string | null` 这样的联合类型。
364
+
365
+ ## Agent 工具
366
+
367
+ pi-forge 注册了两个 AI agent 可以调用的工具:
368
+
369
+ ### `forge_state_set`
370
+
371
+ 批量更新持久状态。只有 `agent.*` 名称可写。用于跨轮次跟踪:
372
+
373
+ - 任务进度 (`agent.progress`)
374
+ - 待解决问题 (`agent.openQuestions`)
375
+ - 故事状态 (`agent.storyState`)
376
+ - 用户要求的笔记 (`agent.notes`)
377
+
378
+ ### `forge_set_var`
379
+
380
+ 设置单个字符串值的兼容性别名。推荐使用 `forge_state_set`。
381
+
382
+ ## 开发环境搭建
383
+
384
+ ```bash
385
+ git clone <repo>
386
+ cd pi-forge
387
+ # .pi/settings.json 已指向包根目录
388
+ pi # 启动 Pi,信任项目,必要时 /reload
389
+ ```
390
+
391
+ 运行测试:
392
+
393
+ ```bash
394
+ npm test
395
+ ```
396
+
397
+ 类型检查:
398
+
399
+ ```bash
400
+ npm run typecheck
401
+ ```
402
+
403
+ ## License
404
+
405
+ MIT
@@ -17,6 +17,36 @@
17
17
  "user": "User",
18
18
  "char": "Assistant"
19
19
  },
20
+ "state": {
21
+ "schemaVersion": 1,
22
+ "definitions": {
23
+ "agent.progress": {
24
+ "type": "string",
25
+ "scope": "session",
26
+ "description": "Concise summary of current task, story, or conversation progress.",
27
+ "agentWritable": true
28
+ },
29
+ "agent.openQuestions": {
30
+ "type": "string[]",
31
+ "scope": "session",
32
+ "description": "Short list of questions that may need user input.",
33
+ "agentWritable": true
34
+ },
35
+ "agent.notes": {
36
+ "type": "string[]",
37
+ "scope": "session",
38
+ "description": "Durable notes the user explicitly asked the agent to remember.",
39
+ "agentWritable": true
40
+ },
41
+ "user.preference": {
42
+ "type": "string",
43
+ "scope": "session",
44
+ "description": "User-provided preference or instruction for this session.",
45
+ "agentWritable": false,
46
+ "userWritable": true
47
+ }
48
+ }
49
+ },
20
50
  "items": [
21
51
  {
22
52
  "kind": "block",
@@ -66,6 +96,20 @@
66
96
  "role": "system",
67
97
  "slot": "date-cwd"
68
98
  },
99
+ {
100
+ "kind": "slot",
101
+ "id": "prompt-state",
102
+ "name": "Prompt State",
103
+ "enabled": true,
104
+ "role": "user",
105
+ "slot": "variables",
106
+ "options": {
107
+ "includeScopes": ["session"],
108
+ "includeNamespaces": ["user.*", "agent.*"],
109
+ "includeMetadata": true,
110
+ "maxValueChars": 1200
111
+ }
112
+ },
69
113
  {
70
114
  "kind": "block",
71
115
  "id": "history-open",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zihanw/pi-forge",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Pi extension for prompt stack and agent profile management.",
5
5
  "type": "module",
6
6
  "keywords": [
@@ -50,6 +50,7 @@
50
50
  "src/",
51
51
  "examples/",
52
52
  "README.md",
53
+ "README.zh-CN.md",
53
54
  "LICENSE*"
54
55
  ],
55
56
  "publishConfig": {