@zihanw/pi-forge 0.1.0 → 0.3.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.
- package/README.md +354 -185
- package/README.zh-CN.md +453 -0
- package/examples/default-prompt-stack.json +54 -51
- package/examples/reviewer-prompt-stack.json +115 -0
- package/examples/sillytavern-dm-writer-prompt-stack.json +190 -0
- package/package.json +4 -8
- package/src/compiler.ts +588 -87
- package/src/index.ts +509 -140
- package/src/loader.ts +179 -12
- package/src/payload-capture.ts +85 -0
- package/src/policy.ts +42 -0
- package/src/regex.ts +500 -0
- package/src/sillytavern-importer.ts +484 -50
- package/src/stack-migration.ts +159 -0
- package/src/storage.ts +28 -0
- package/src/types.ts +83 -2
- package/src/web-editor/index.ts +2 -0
- package/src/web-editor/page.ts +2844 -0
- package/src/web-editor/server.ts +289 -0
- package/src/web-editor/types.ts +84 -0
- package/src/web-host.ts +229 -0
package/README.md
CHANGED
|
@@ -1,286 +1,455 @@
|
|
|
1
1
|
# pi-forge
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[English](README.md) | [简体中文](README.zh-CN.md)
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+

|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
**pi-forge** lets you customize how Pi thinks and behaves. It gives you prompt stacks — JSON files that can replace, append to, or prepend Pi's default system prompt while controlling the AI's personality, visible tools, conversation history layout, template variables, and prompt transforms.
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
Think of it as a character sheet for your AI agent.
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
{
|
|
13
|
-
"packages": [".."]
|
|
14
|
-
}
|
|
15
|
-
```
|
|
11
|
+
## What you can do with it
|
|
16
12
|
|
|
17
|
-
|
|
13
|
+
- **Give Pi a personality** — turn it into a creative writer, a roleplay partner, a strict code reviewer, or anything in between.
|
|
14
|
+
- **Switch contexts instantly** — one command to swap between "coding mode", "writing mode", and "translation mode".
|
|
15
|
+
- **Control what the AI sees** — choose which tools, skills, and project context appear in each prompt.
|
|
16
|
+
- **Limit tools and skills per stack** — enforce active tool policy and filter skill visibility for focused modes.
|
|
17
|
+
- **Use template variables** — define static values such as `{{char}}` / `{{user}}`, and use ST-style turn/session variable macros inside prompt text.
|
|
18
|
+
- **Transform outgoing and finalized text** — run deterministic regex replacements on selected history, compiled prompt text, or finalized assistant messages.
|
|
19
|
+
- **Import SillyTavern presets** — bring your existing ST character presets into Pi with one command.
|
|
20
|
+
- **Debug your prompts** — intercept and inspect exactly what gets sent to the model.
|
|
18
21
|
|
|
19
|
-
|
|
22
|
+
## Quick start
|
|
23
|
+
|
|
24
|
+
### Install
|
|
20
25
|
|
|
21
26
|
```bash
|
|
22
27
|
pi install npm:@zihanw/pi-forge
|
|
23
28
|
```
|
|
24
29
|
|
|
25
|
-
|
|
30
|
+
### Your first prompt stack
|
|
31
|
+
|
|
32
|
+
Create `.pi/forge/prompt-stacks/default.json` from [examples/default-prompt-stack.json](examples/default-prompt-stack.json).
|
|
26
33
|
|
|
27
|
-
|
|
34
|
+
The default example mirrors Pi's own prompt builder from `@earendil-works/pi-coding-agent/dist/core/system-prompt.js`, but splits it into movable pi-forge slots: role, tools, guidelines, Pi docs guidance, appended system prompt text, project context, skills, date/cwd, and chat history.
|
|
28
35
|
|
|
29
|
-
```
|
|
30
|
-
.pi/prompt-stacks
|
|
36
|
+
```bash
|
|
37
|
+
mkdir -p .pi/forge/prompt-stacks
|
|
38
|
+
$EDITOR .pi/forge/prompt-stacks/default.json
|
|
31
39
|
```
|
|
32
40
|
|
|
33
|
-
|
|
41
|
+
Paste the example JSON into that file. If you are working inside this repository, you can copy it directly with `cp examples/default-prompt-stack.json .pi/forge/prompt-stacks/default.json`.
|
|
34
42
|
|
|
35
|
-
|
|
36
|
-
2. A restored `/preset use none` selection disables prompt stack replacement.
|
|
37
|
-
3. `.pi/prompt-stacks/default.json` auto-activates when present unless the stack sets `"autoActivate": false`.
|
|
38
|
-
4. Otherwise, the first stack with `"autoActivate": true` is used.
|
|
39
|
-
5. `/preset use <id>` switches stacks for the session and persists that choice.
|
|
40
|
-
6. `/preset use none` disables prompt stack replacement and persists that choice.
|
|
43
|
+
That's it. Restart Pi or run `/preset reload`. If no stack is already selected, `default.json` auto-activates. If you previously chose another stack or `/preset use none`, run `/preset use default`.
|
|
41
44
|
|
|
42
|
-
|
|
45
|
+
### Visual editor
|
|
43
46
|
|
|
44
|
-
|
|
47
|
+
Prefer clicking over typing JSON? pi-forge has a built-in web editor:
|
|
45
48
|
|
|
46
|
-
```
|
|
47
|
-
/preset
|
|
48
|
-
/preset use <id|none>
|
|
49
|
-
/preset preview [id]
|
|
50
|
-
/preset validate [id]
|
|
51
|
-
/preset reload
|
|
52
|
-
/preset vars [set <name> <value>|get <name>|clear [name]]
|
|
53
|
-
/preset import-silly <path> [character_id]
|
|
54
|
-
/intercept
|
|
49
|
+
```
|
|
50
|
+
/preset ui
|
|
55
51
|
```
|
|
56
52
|
|
|
57
|
-
|
|
53
|
+
Drag, drop, edit, validate, inspect full previews and captured payloads, manage variables/context/regex rules in tabs, switch dark mode, recover through raw stack JSON, import, export, fork, and delete stacks — all in your browser. Stack metadata is collapsible so the active editor stays in view.
|
|
58
54
|
|
|
59
|
-
Import
|
|
55
|
+
Import accepts native pi-forge stack JSON and SillyTavern preset JSON. SillyTavern presets are converted to prompt stacks automatically; if a preset contains multiple `character_id` configs, the editor asks which one to use.
|
|
60
56
|
|
|
61
|
-
|
|
62
|
-
|
|
57
|
+
The editor runs on an available `127.0.0.1` port with a session token, so multiple Pi instances can run editors at the same time. If Pi reinitializes the extension after session navigation or a new session, `/preset ui` reuses the existing editor URL for the same project instead of orphaning the old server. Writes require a trusted project and stay inside prompt-stack storage. New stacks are written to `.pi/forge/prompt-stacks`; existing legacy stacks under `.pi/prompt-stacks` remain readable and editable. Successful save, import, fork, and delete actions reload into the current Pi session. Use `/preset ui restart` or `/preset ui stop` when needed.
|
|
58
|
+
|
|
59
|
+
To copy old stacks into the new location, run `/preset migrate-stacks`. Add `--dry-run` to preview, `--overwrite` to replace existing target files, and `--delete-legacy` to remove old files after successful copy.
|
|
60
|
+
|
|
61
|
+
To prefer a specific port, create `.pi/forge/config.json`. If that port is busy, pi-forge falls back to another available port and shows the actual URL:
|
|
62
|
+
|
|
63
|
+
```json
|
|
64
|
+
{
|
|
65
|
+
"webEditor": {
|
|
66
|
+
"port": 41738
|
|
67
|
+
}
|
|
68
|
+
}
|
|
63
69
|
```
|
|
64
70
|
|
|
65
|
-
|
|
71
|
+
## Use cases
|
|
72
|
+
|
|
73
|
+
### 🎭 Roleplay & creative writing
|
|
66
74
|
|
|
67
|
-
|
|
75
|
+
Turn Pi into a character. Define their personality in the system prompt, inject writing style rules as user messages, and use `{{lastUserMessage}}` to re-insert the user's input after the conversation history.
|
|
68
76
|
|
|
69
|
-
|
|
77
|
+
Useful pattern:
|
|
78
|
+
- Put long-term character rules in a `system` block.
|
|
79
|
+
- Keep Pi runtime context (tools, skills, project) in `user` slots.
|
|
80
|
+
- Set the `chat-history` slot to skip the latest user message.
|
|
81
|
+
- Add a final `user` block with `{{lastUserMessage}}`.
|
|
70
82
|
|
|
71
|
-
|
|
83
|
+
This keeps the latest request clear and avoids duplicating it.
|
|
72
84
|
|
|
73
|
-
|
|
85
|
+
For a baseline stack to fork before turning Pi into a character, start from [examples/default-prompt-stack.json](examples/default-prompt-stack.json).
|
|
74
86
|
|
|
75
|
-
|
|
87
|
+
### 🧑💻 Focused code review
|
|
76
88
|
|
|
77
|
-
|
|
89
|
+
Create a `reviewer.json` stack with a strict review block: "prioritize correctness, regressions, security, and missing tests." Keep the `tools`, `project-context`, `variables`, and `chat-history` slots enabled so Pi can still inspect the repo and see any template variables you expose.
|
|
90
|
+
|
|
91
|
+
Use `mode: "append"` if you want to keep Pi's normal coding behavior and only add the sharper review lens.
|
|
92
|
+
|
|
93
|
+
### 🌐 Translation mode
|
|
94
|
+
|
|
95
|
+
Create a small `translator.json` stack with one system block for tone and target language, then keep `chat-history` and `{{lastUserMessage}}` in the layout. This works well for switching between bilingual editing, literal translation, and localization review without changing your default assistant.
|
|
96
|
+
|
|
97
|
+
### 🔀 Multi-mode switching
|
|
98
|
+
|
|
99
|
+
Create separate stacks for different tasks:
|
|
100
|
+
|
|
101
|
+
```
|
|
102
|
+
.pi/forge/prompt-stacks/
|
|
103
|
+
coder.json # strict coding assistant
|
|
104
|
+
writer.json # creative writing partner
|
|
105
|
+
translator.json # bilingual translator
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
Switch with `/preset use coder`, `/preset use writer`, etc.
|
|
109
|
+
|
|
110
|
+
### 🧪 Presets that show off pi-forge
|
|
111
|
+
|
|
112
|
+
- **Pi mirror** — start from [examples/default-prompt-stack.json](examples/default-prompt-stack.json). It preserves normal Pi behavior while making every runtime section movable and inspectable.
|
|
113
|
+
- **Focused reviewer** — see [examples/reviewer-prompt-stack.json](examples/reviewer-prompt-stack.json). It denies file-writing tools, wraps prior chat history as background, removes the latest user message from history, then reinserts `{{lastUserMessage}}` as the explicit review target.
|
|
114
|
+
- **Read-only scout** — use `tools.allow` for `read`, `grep`, `find`, and `ls`; omit editing tools; cap `chat-history` with `maxChars`. Good for exploration turns where the model should report findings without changing files.
|
|
115
|
+
- **Surgical patcher** — keep the Pi mirror, require `read`, `edit`, and `bash`, strip assistant thinking from inserted history, and move `project-context` near the final user turn. Good for focused implementation passes.
|
|
116
|
+
- **SillyTavern DM writer** — see [examples/sillytavern-dm-writer-prompt-stack.json](examples/sillytavern-dm-writer-prompt-stack.json). It defines a Dungeon Master character with `{{char}}` / `{{user}}`, wraps prior adventure history, reinserts `{{lastUserMessage}}` as the current player action, and uses regex cleanup for OOC notes, secret-roll markers, dice notation, and `Player:` prefixes.
|
|
117
|
+
- **Payload lab** — include `active-model`, `date-cwd`, and variables slots, then add `compiled` regex rules for deterministic redaction or formatting. Pair it with `/payload next` or the web editor's capture view to audit exactly what changed.
|
|
118
|
+
- **Docs-only Pi expert** — allow only read/search tools, enable the `pi-docs` slot, and keep project context. Useful when you want answers grounded in the installed Pi docs instead of general memory.
|
|
119
|
+
|
|
120
|
+
### 🔧 Template variables
|
|
78
121
|
|
|
79
122
|
```json
|
|
80
|
-
{
|
|
81
|
-
"
|
|
82
|
-
"
|
|
83
|
-
"id": "default",
|
|
84
|
-
"autoActivate": true,
|
|
85
|
-
"mode": "replace",
|
|
86
|
-
"items": [
|
|
87
|
-
{
|
|
88
|
-
"kind": "block",
|
|
89
|
-
"id": "main-role",
|
|
90
|
-
"enabled": true,
|
|
91
|
-
"role": "system",
|
|
92
|
-
"content": "You are a precise coding assistant."
|
|
93
|
-
},
|
|
94
|
-
{
|
|
95
|
-
"kind": "slot",
|
|
96
|
-
"id": "tools",
|
|
97
|
-
"enabled": true,
|
|
98
|
-
"role": "system",
|
|
99
|
-
"slot": "tools"
|
|
100
|
-
},
|
|
101
|
-
{
|
|
102
|
-
"kind": "block",
|
|
103
|
-
"id": "history-open",
|
|
104
|
-
"enabled": true,
|
|
105
|
-
"role": "user",
|
|
106
|
-
"content": "<conversation_context>"
|
|
107
|
-
},
|
|
108
|
-
{
|
|
109
|
-
"kind": "slot",
|
|
110
|
-
"id": "chat-history",
|
|
111
|
-
"enabled": true,
|
|
112
|
-
"slot": "chat-history"
|
|
113
|
-
},
|
|
114
|
-
{
|
|
115
|
-
"kind": "block",
|
|
116
|
-
"id": "history-close",
|
|
117
|
-
"enabled": true,
|
|
118
|
-
"role": "user",
|
|
119
|
-
"content": "</conversation_context>"
|
|
120
|
-
}
|
|
121
|
-
]
|
|
123
|
+
"variables": {
|
|
124
|
+
"char": "Konata",
|
|
125
|
+
"user": "User"
|
|
122
126
|
}
|
|
123
127
|
```
|
|
124
128
|
|
|
125
|
-
|
|
129
|
+
Use static variables for stable prompt constants, and ST-style macros for local prompt-time mutation:
|
|
130
|
+
|
|
131
|
+
```
|
|
132
|
+
{{setvar::mood::focused}}
|
|
133
|
+
{{getvar::mood}}
|
|
134
|
+
{{setsessionvar::topic::compiler cleanup}}
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
For durable project memory, use normal files in the repo rather than pi-forge prompt variables.
|
|
138
|
+
|
|
139
|
+
### 📦 SillyTavern migration
|
|
126
140
|
|
|
127
|
-
|
|
141
|
+
Bring your ST presets into Pi:
|
|
142
|
+
|
|
143
|
+
```
|
|
144
|
+
/preset import-silly ~/SillyTavern/presets/my-preset.json
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
pi-forge converts the preset to a prompt stack and generates a migration report showing what was handled and what needs manual tweaking.
|
|
148
|
+
|
|
149
|
+
Deterministic SillyTavern `promptOnly` regex scripts are converted to pi-forge `regex.rules` as history-stage rules when they can be represented safely, including full-match token conversion, trim strings, depth fields, and clear user/assistant placements. Display-only, mixed prompt/display, DOM/browser, CSS/HTML decoration, JavaScript, unsupported placements, and invalid regex scripts stay report-only for manual review.
|
|
150
|
+
|
|
151
|
+
### 🔍 Prompt debugging
|
|
152
|
+
|
|
153
|
+
See exactly what gets sent to the model:
|
|
154
|
+
|
|
155
|
+
```
|
|
156
|
+
/payload next save=.pi/forge/payloads/last.json
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
Or open `/preset ui`, click **Arm payload**, send the next Pi prompt, and inspect the redacted provider payload in the browser.
|
|
160
|
+
|
|
161
|
+
Or preview your compiled prompt without sending anything:
|
|
162
|
+
|
|
163
|
+
```
|
|
164
|
+
/preset preview
|
|
165
|
+
```
|
|
128
166
|
|
|
129
|
-
|
|
167
|
+
## How it works
|
|
168
|
+
|
|
169
|
+
A prompt stack is a JSON file with two kinds of items:
|
|
170
|
+
|
|
171
|
+
| Kind | What it does |
|
|
172
|
+
|------|-------------|
|
|
173
|
+
| **Block** | Static text inserted at a specific position (system prompt, user message, assistant message) |
|
|
174
|
+
| **Slot** | Dynamic content from Pi's runtime — tools, skills, chat history, date, project context, etc. |
|
|
175
|
+
|
|
176
|
+
Items are arranged in order. When the stack is active, pi-forge:
|
|
177
|
+
|
|
178
|
+
1. Builds a system prompt from your `system`-role blocks and slots, then applies it with the stack's `mode`.
|
|
179
|
+
2. Inserts `user`/`assistant` blocks and slots around the conversation history.
|
|
180
|
+
3. Expands `{{macros}}` like `{{lastUserMessage}}`, `{{date}}`, and custom variables.
|
|
181
|
+
4. Applies stack tool policy to Pi's active tool set and filters pi-forge-rendered tool/skill slots.
|
|
182
|
+
5. Applies enabled outgoing regex rules for the `history` and `compiled` stages.
|
|
183
|
+
6. Optionally applies destructive `finalize` regex rules when an assistant message finishes.
|
|
184
|
+
|
|
185
|
+
### Slots at a glance
|
|
186
|
+
|
|
187
|
+
| Slot | What it inserts |
|
|
188
|
+
|------|----------------|
|
|
189
|
+
| `chat-history` | The current conversation |
|
|
190
|
+
| `tools` | Available tools and their descriptions |
|
|
191
|
+
| `tool-guidelines` | Tool usage instructions |
|
|
192
|
+
| `skills` | Loaded Pi skills |
|
|
193
|
+
| `project-context` | Project instructions and context files |
|
|
194
|
+
| `variables` | Static/session/turn template variables |
|
|
195
|
+
| `date` / `cwd` / `date-cwd` | Current date and working directory |
|
|
196
|
+
| `active-model` | Which model is being used |
|
|
197
|
+
| `append-system-prompt` | User's appended system prompt text |
|
|
198
|
+
| `pi-docs` | Pi documentation guidance |
|
|
199
|
+
|
|
200
|
+
### Modes
|
|
201
|
+
|
|
202
|
+
- **replace** (default) — your stack replaces Pi's system prompt entirely.
|
|
203
|
+
- **append** — your stack is added after Pi's default system prompt.
|
|
204
|
+
- **prepend** — your stack is added before Pi's default system prompt.
|
|
205
|
+
|
|
206
|
+
## Common commands
|
|
207
|
+
|
|
208
|
+
### Managing stacks
|
|
209
|
+
|
|
210
|
+
| Command | What it does |
|
|
211
|
+
|---------|-------------|
|
|
212
|
+
| `/preset list` | Show all available stacks |
|
|
213
|
+
| `/preset use <id>` | Activate a stack |
|
|
214
|
+
| `/preset use none` | Disable prompt stacks for the session |
|
|
215
|
+
| `/preset preview [id]` | See the compiled prompt |
|
|
216
|
+
| `/preset validate [id]` | Check a stack for issues |
|
|
217
|
+
| `/preset status` | Show the active stack and diagnostics summary |
|
|
218
|
+
| `/preset diagnostics` | Show runtime diagnostics |
|
|
219
|
+
| `/preset reload` | Reload stacks from disk |
|
|
220
|
+
| `/preset migrate-stacks [--dry-run] [--overwrite] [--delete-legacy]` | Copy legacy `.pi/prompt-stacks` files into `.pi/forge/prompt-stacks` |
|
|
221
|
+
| `/preset ui [stop\|restart]` | Open, stop, or restart the web editor |
|
|
222
|
+
|
|
223
|
+
### Import & debug
|
|
224
|
+
|
|
225
|
+
| Command | What it does |
|
|
226
|
+
|---------|-------------|
|
|
227
|
+
| `/preset import-silly <path>` | Import a SillyTavern preset |
|
|
228
|
+
| `/intercept` | Show the next provider payload |
|
|
229
|
+
| `/payload next [save=<path>]` | Show, save, and expose the next payload to the web editor |
|
|
230
|
+
|
|
231
|
+
## Common macros
|
|
232
|
+
|
|
233
|
+
Use these in block content to insert dynamic values:
|
|
234
|
+
|
|
235
|
+
| Macro | Expands to |
|
|
236
|
+
|-------|-----------|
|
|
237
|
+
| `{{lastUserMessage}}` | The user's latest message |
|
|
238
|
+
| `{{date}}` | Current date (YYYY-MM-DD) |
|
|
239
|
+
| `{{time}}` | Current time (HH:MM:SS) |
|
|
240
|
+
| `{{cwd}}` | Current working directory |
|
|
241
|
+
| `{{tools}}` | Comma-separated tool names |
|
|
242
|
+
| `{{selectedTools}}` | Alias for selected tool names |
|
|
243
|
+
| `{{activeModel}}` | Current model (provider/id) |
|
|
244
|
+
| `{{char}}` / `{{user}}` | Custom variables from your stack |
|
|
245
|
+
|
|
246
|
+
### Variable macros
|
|
247
|
+
|
|
248
|
+
```
|
|
249
|
+
{{setvar::name::value}} set a turn variable (cleared each message)
|
|
250
|
+
{{setsessionvar::name::value}} set a session variable (persists)
|
|
251
|
+
{{setvar::session::name::value}} also set a session variable
|
|
252
|
+
{{getvar::name}} read a variable (turn → session → static)
|
|
253
|
+
{{getturnvar::name}} read only a turn variable
|
|
254
|
+
{{getsessionvar::name}} read only a session variable
|
|
255
|
+
{{clearvar::name}} clear a variable
|
|
256
|
+
{{clearturnvar::name}} clear a turn variable
|
|
257
|
+
{{clearsessionvar::name}} clear a session variable
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
## Stack reference
|
|
261
|
+
|
|
262
|
+
### Full item types
|
|
263
|
+
|
|
264
|
+
**Block:**
|
|
130
265
|
|
|
131
266
|
```json
|
|
132
267
|
{
|
|
133
268
|
"kind": "block",
|
|
134
|
-
"id": "
|
|
269
|
+
"id": "unique-id",
|
|
270
|
+
"name": "Readable label",
|
|
135
271
|
"enabled": true,
|
|
136
|
-
"role": "
|
|
137
|
-
"content": "
|
|
272
|
+
"role": "system",
|
|
273
|
+
"content": "Your text here. Use {{macros}} for dynamic content."
|
|
138
274
|
}
|
|
139
275
|
```
|
|
140
276
|
|
|
141
|
-
|
|
277
|
+
Valid roles: `system`, `user`, `assistant`, `custom`.
|
|
142
278
|
|
|
143
|
-
|
|
279
|
+
**Slot:**
|
|
144
280
|
|
|
145
281
|
```json
|
|
146
282
|
{
|
|
147
283
|
"kind": "slot",
|
|
148
|
-
"id": "
|
|
284
|
+
"id": "unique-id",
|
|
285
|
+
"name": "Chat History",
|
|
149
286
|
"enabled": true,
|
|
150
|
-
"role": "
|
|
151
|
-
"slot": "
|
|
287
|
+
"role": "user",
|
|
288
|
+
"slot": "chat-history",
|
|
289
|
+
"options": {
|
|
290
|
+
"includeLastUserMessage": false
|
|
291
|
+
}
|
|
152
292
|
}
|
|
153
293
|
```
|
|
154
294
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
- `chat-history` — current Pi conversation context from the session
|
|
158
|
-
- `tools` — active tool names and prompt snippets
|
|
159
|
-
- `tool-guidelines` — active tool guidance
|
|
160
|
-
- `skills` — loaded Pi skills
|
|
161
|
-
- `project-context` — project instructions/context files
|
|
162
|
-
- `append-system-prompt` — user-provided append system prompt text
|
|
163
|
-
- `variables` — static/session/turn variables rendered as structured XML
|
|
164
|
-
- `date`
|
|
165
|
-
- `cwd`
|
|
166
|
-
- `date-cwd`
|
|
167
|
-
- `active-model`
|
|
168
|
-
- `pi-docs`
|
|
169
|
-
|
|
170
|
-
### Variables slot
|
|
171
|
-
|
|
172
|
-
Renders variable state as structured XML:
|
|
173
|
-
|
|
174
|
-
```xml
|
|
175
|
-
<prompt_variables>
|
|
176
|
-
<static>
|
|
177
|
-
<char>Agent</char>
|
|
178
|
-
</static>
|
|
179
|
-
<session>
|
|
180
|
-
<agent.mood>happy</agent.mood>
|
|
181
|
-
<agent.progress>step 3</agent.progress>
|
|
182
|
-
</session>
|
|
183
|
-
<turn>
|
|
184
|
-
<recent>just happened</recent>
|
|
185
|
-
</turn>
|
|
186
|
-
</prompt_variables>
|
|
187
|
-
```
|
|
188
|
-
|
|
189
|
-
Options:
|
|
295
|
+
### Chat history options
|
|
190
296
|
|
|
191
297
|
```json
|
|
192
|
-
{
|
|
193
|
-
"
|
|
194
|
-
"
|
|
195
|
-
"
|
|
298
|
+
"options": {
|
|
299
|
+
"includeLastUserMessage": false,
|
|
300
|
+
"stripAssistantThinking": true,
|
|
301
|
+
"includeSummaries": true,
|
|
302
|
+
"toolMode": "keep",
|
|
303
|
+
"roles": ["user", "assistant"],
|
|
304
|
+
"maxMessages": 40,
|
|
305
|
+
"maxChars": 20000
|
|
196
306
|
}
|
|
197
307
|
```
|
|
198
308
|
|
|
199
|
-
|
|
309
|
+
Set to `false` when you use `{{lastUserMessage}}` after the history — prevents the user's message from appearing twice.
|
|
200
310
|
|
|
201
|
-
|
|
311
|
+
Set `stripAssistantThinking` to `true` to remove prior assistant thinking blocks from inserted chat history. Visible assistant text, tool calls, and tool result messages are preserved. This only affects history inserted by that slot and does not alter the current agent loop or stored transcript.
|
|
202
312
|
|
|
203
|
-
- `
|
|
204
|
-
- `user` items are inserted as ephemeral user messages.
|
|
205
|
-
- `assistant` items are inserted as ephemeral assistant messages.
|
|
206
|
-
- `custom` items are inserted as hidden Pi custom messages and converted to user context by Pi.
|
|
313
|
+
Use `includeSummaries: false` to omit Pi branch/compaction summary messages, `roles` to keep only specific message roles, `toolMode: "drop"` to remove prior tool-call/tool-result history, and `maxMessages` / `maxChars` to keep only recent history. When filters or limits can break tool-call pairs, pi-forge removes dangling tool calls/results instead of sending inconsistent tool history.
|
|
207
314
|
|
|
208
|
-
|
|
315
|
+
### Structured slot format options
|
|
209
316
|
|
|
210
|
-
|
|
317
|
+
Structured runtime slots default to XML-style wrappers. Add `"format": "plain"` to `tools`, `tool-guidelines`, `skills`, `project-context`, or `variables` slots for compact newline-separated output.
|
|
211
318
|
|
|
212
319
|
```json
|
|
213
320
|
{
|
|
214
321
|
"kind": "slot",
|
|
215
|
-
"id": "
|
|
322
|
+
"id": "tools",
|
|
216
323
|
"enabled": true,
|
|
217
|
-
"
|
|
324
|
+
"role": "system",
|
|
325
|
+
"slot": "tools",
|
|
218
326
|
"options": {
|
|
219
|
-
"
|
|
327
|
+
"format": "plain"
|
|
220
328
|
}
|
|
221
329
|
}
|
|
222
330
|
```
|
|
223
331
|
|
|
224
|
-
|
|
332
|
+
The default Pi mirror uses a few extra slot options:
|
|
225
333
|
|
|
226
|
-
|
|
334
|
+
```json
|
|
335
|
+
{
|
|
336
|
+
"slot": "tools",
|
|
337
|
+
"options": {
|
|
338
|
+
"format": "plain",
|
|
339
|
+
"onlyWithSnippets": true
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
`tools.onlyWithSnippets` matches Pi's default "Available tools" section by hiding tools that do not provide prompt snippets. `tool-guidelines.heading`, `tool-guidelines.includePiDefaultGuidelines`, and `tool-guidelines.piStyle` make the guidelines slot match Pi's default heading and bullets. `skills.requireReadTool` hides skills unless the read tool is active, matching Pi's default behavior.
|
|
345
|
+
|
|
346
|
+
### Tool and skill policy
|
|
347
|
+
|
|
348
|
+
Prompt stacks can constrain tools and skills with stack-level `allow` or `deny` lists. Patterns are exact by default and support `*` wildcards.
|
|
227
349
|
|
|
228
350
|
```json
|
|
229
351
|
{
|
|
230
|
-
"
|
|
231
|
-
"
|
|
352
|
+
"tools": {
|
|
353
|
+
"allow": ["read", "bash"]
|
|
354
|
+
},
|
|
355
|
+
"skills": {
|
|
356
|
+
"deny": ["browser-danger"]
|
|
232
357
|
}
|
|
233
358
|
}
|
|
234
359
|
```
|
|
235
360
|
|
|
236
|
-
|
|
361
|
+
Use `allow` when only matching tools or skills should remain active. Use `deny` when everything except matching tools or skills should remain active. A single resource policy cannot contain both non-empty lists; mixed `allow` and `deny` entries are validation errors.
|
|
237
362
|
|
|
238
|
-
|
|
363
|
+
Tool policy is enforced through Pi's active tool list while the stack is active. pi-forge remembers the previous active tools and restores them when prompt stacks are disabled or switched to an unrestricted stack.
|
|
239
364
|
|
|
240
|
-
- `
|
|
241
|
-
- `{{date}}`
|
|
242
|
-
- `{{lastUserMessage}}`
|
|
243
|
-
- `{{selectedTools}}` / `{{tools}}`
|
|
244
|
-
- `{{activeModel}}`
|
|
245
|
-
- custom variables from the stack `variables` object, e.g. `{{char}}`
|
|
365
|
+
Skill policy filters skills rendered by pi-forge's `skills` slot. If a stack uses `mode: "append"` or `"prepend"`, Pi's base prompt may already contain unfiltered skills; use `mode: "replace"` when skill visibility must be controlled.
|
|
246
366
|
|
|
247
|
-
###
|
|
367
|
+
### Regex transforms
|
|
248
368
|
|
|
249
|
-
|
|
369
|
+
Prompt stacks can run deterministic regex replacements on model-bound prompt text and, optionally, finalized assistant messages. Outgoing rules support `history` and `compiled` stages. Destructive final-message cleanup uses `effect: "finalize"` at `stage: "compiled"` with the `messages` target. True display-only streaming transforms and provider-payload rewrites are not active yet.
|
|
250
370
|
|
|
251
371
|
```json
|
|
252
|
-
"
|
|
253
|
-
"
|
|
254
|
-
"
|
|
372
|
+
"regex": {
|
|
373
|
+
"schemaVersion": 1,
|
|
374
|
+
"rules": [
|
|
375
|
+
{
|
|
376
|
+
"id": "trim-ooc",
|
|
377
|
+
"enabled": true,
|
|
378
|
+
"stage": "history",
|
|
379
|
+
"effect": "outgoing",
|
|
380
|
+
"pattern": "\\(OOC:[^)]+\\)",
|
|
381
|
+
"flags": "gi",
|
|
382
|
+
"replace": "",
|
|
383
|
+
"roles": ["assistant"],
|
|
384
|
+
"maxMessages": 20
|
|
385
|
+
}
|
|
386
|
+
]
|
|
255
387
|
}
|
|
256
388
|
```
|
|
257
389
|
|
|
258
|
-
|
|
390
|
+
Use `stage: "history"` to transform messages inserted by the `chat-history` slot. Use `stage: "compiled"` with optional `targets: ["system"]`, `["messages"]`, or both to transform the final compiled prompt. Message rules can filter by `roles`, `maxMessages`, `maxChars`, `minDepth`, and `maxDepth`, where depth `0` is the latest message. Replacements use JavaScript syntax (`$&` for the full match, `$1` for captures; `$0` is also accepted as a full-match alias, and `$$` escapes a literal `$`). `trimStrings` removes literal strings from expanded replacement matches/captures, matching SillyTavern's Trim Out behavior. Supported regex flags are `g`, `i`, `m`, `s`, and `u`.
|
|
259
391
|
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
{
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
392
|
+
To clean a completed assistant message after streaming, use `effect: "finalize"`:
|
|
393
|
+
|
|
394
|
+
```json
|
|
395
|
+
{
|
|
396
|
+
"id": "finalize-ooc",
|
|
397
|
+
"enabled": true,
|
|
398
|
+
"stage": "compiled",
|
|
399
|
+
"effect": "finalize",
|
|
400
|
+
"targets": ["messages"],
|
|
401
|
+
"roles": ["assistant"],
|
|
402
|
+
"pattern": "\\s*\\(OOC:[^)]+\\)",
|
|
403
|
+
"flags": "gi",
|
|
404
|
+
"replace": ""
|
|
405
|
+
}
|
|
267
406
|
```
|
|
268
407
|
|
|
269
|
-
|
|
408
|
+
Warning: `finalize` runs at `message_end`, after raw output may already have streamed in the TUI. It returns a cleaned replacement message to Pi, so the original model output is not preserved in the stored transcript.
|
|
409
|
+
|
|
410
|
+
`effect: "outgoing"` changes model input. `effect: "finalize"` changes finalized assistant transcript content. `effect: "display"` and `"both"` validate with warnings but are ignored at runtime until true display transforms are implemented.
|
|
411
|
+
|
|
412
|
+
SillyTavern imports convert deterministic prompt-only `{{match}}` / `$0` full-match replacements to JavaScript `$&` (both `$0` and `$&` work in pi-forge), preserve original regex metadata in `source.sillytavern`, and run as history-stage rules so depth stays chat-relative. Display-only/browser/unsupported-placement scripts stay report-only. The web editor has a structured Regex dialog for these rule fields and preserves advanced unknown fields for raw JSON editing.
|
|
413
|
+
|
|
414
|
+
### Variables slot options
|
|
270
415
|
|
|
271
|
-
```
|
|
272
|
-
{
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
416
|
+
```json
|
|
417
|
+
{
|
|
418
|
+
"kind": "slot",
|
|
419
|
+
"id": "variables",
|
|
420
|
+
"enabled": true,
|
|
421
|
+
"role": "user",
|
|
422
|
+
"slot": "variables",
|
|
423
|
+
"options": {
|
|
424
|
+
"includeStatic": true,
|
|
425
|
+
"includeSession": true,
|
|
426
|
+
"includeTurn": false,
|
|
427
|
+
"format": "xml"
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
```
|
|
431
|
+
|
|
432
|
+
## Package setup for development
|
|
433
|
+
|
|
434
|
+
```bash
|
|
435
|
+
git clone <repo>
|
|
436
|
+
cd pi-forge
|
|
437
|
+
# .pi/settings.json already points at the package root
|
|
438
|
+
pi # start Pi, trust the project, /reload if needed
|
|
278
439
|
```
|
|
279
440
|
|
|
280
|
-
|
|
441
|
+
Run tests:
|
|
442
|
+
|
|
443
|
+
```bash
|
|
444
|
+
npm test
|
|
445
|
+
```
|
|
446
|
+
|
|
447
|
+
Typecheck:
|
|
448
|
+
|
|
449
|
+
```bash
|
|
450
|
+
npm run typecheck
|
|
451
|
+
```
|
|
281
452
|
|
|
282
|
-
|
|
283
|
-
2. session variables
|
|
284
|
-
3. static stack variables
|
|
453
|
+
## License
|
|
285
454
|
|
|
286
|
-
|
|
455
|
+
MIT
|