@zihanw/pi-forge 0.2.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 CHANGED
@@ -2,7 +2,9 @@
2
2
 
3
3
  [English](README.md) | [简体中文](README.zh-CN.md)
4
4
 
5
- **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, and cross-turn state.
5
+ ![pi-forge header](https://raw.githubusercontent.com/MacroSony/pi-forge/main/assets/pi-forge-header-concept-1.png)
6
+
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.
6
8
 
7
9
  Think of it as a character sheet for your AI agent.
8
10
 
@@ -11,7 +13,9 @@ Think of it as a character sheet for your AI agent.
11
13
  - **Give Pi a personality** — turn it into a creative writer, a roleplay partner, a strict code reviewer, or anything in between.
12
14
  - **Switch contexts instantly** — one command to swap between "coding mode", "writing mode", and "translation mode".
13
15
  - **Control what the AI sees** — choose which tools, skills, and project context appear in each prompt.
14
- - **Remember things across turns** — let the agent track progress, store notes, and recall user preferences throughout a session.
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.
15
19
  - **Import SillyTavern presets** — bring your existing ST character presets into Pi with one command.
16
20
  - **Debug your prompts** — intercept and inspect exactly what gets sent to the model.
17
21
 
@@ -25,43 +29,17 @@ pi install npm:@zihanw/pi-forge
25
29
 
26
30
  ### Your first prompt stack
27
31
 
28
- Create `.pi/prompt-stacks/default.json`:
32
+ Create `.pi/forge/prompt-stacks/default.json` from [examples/default-prompt-stack.json](examples/default-prompt-stack.json).
29
33
 
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": "Main Role",
42
- "enabled": true,
43
- "role": "system",
44
- "content": "You are a friendly and concise coding assistant. Prefer short answers with code examples."
45
- },
46
- {
47
- "kind": "slot",
48
- "id": "tools",
49
- "name": "Available Tools",
50
- "enabled": true,
51
- "role": "system",
52
- "slot": "tools"
53
- },
54
- {
55
- "kind": "slot",
56
- "id": "chat-history",
57
- "name": "Chat History",
58
- "enabled": true,
59
- "slot": "chat-history"
60
- }
61
- ]
62
- }
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.
35
+
36
+ ```bash
37
+ mkdir -p .pi/forge/prompt-stacks
38
+ $EDITOR .pi/forge/prompt-stacks/default.json
63
39
  ```
64
40
 
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`.
42
+
65
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`.
66
44
 
67
45
  ### Visual editor
@@ -72,13 +50,15 @@ Prefer clicking over typing JSON? pi-forge has a built-in web editor:
72
50
  /preset ui
73
51
  ```
74
52
 
75
- Drag, drop, edit, validate, preview, import, export, fork, and delete stacks — all in your browser.
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.
76
54
 
77
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.
78
56
 
79
- The editor runs on `127.0.0.1:41738` by default with a session token. Writes require a trusted project and stay inside `.pi/prompt-stacks`; successful save, import, fork, and delete actions reload into the current Pi session. Use `/preset ui restart` or `/preset ui stop` when needed.
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.
80
60
 
81
- To use a different fixed port, create `.pi/forge/config.json`:
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:
82
62
 
83
63
  ```json
84
64
  {
@@ -102,11 +82,11 @@ Useful pattern:
102
82
 
103
83
  This keeps the latest request clear and avoids duplicating it.
104
84
 
105
- For a copyable starter stack, see [examples/default-prompt-stack.json](examples/default-prompt-stack.json).
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).
106
86
 
107
87
  ### 🧑‍💻 Focused code review
108
88
 
109
- 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 remember review state.
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.
110
90
 
111
91
  Use `mode: "append"` if you want to keep Pi's normal coding behavior and only add the sharper review lens.
112
92
 
@@ -119,7 +99,7 @@ Create a small `translator.json` stack with one system block for tone and target
119
99
  Create separate stacks for different tasks:
120
100
 
121
101
  ```
122
- .pi/prompt-stacks/
102
+ .pi/forge/prompt-stacks/
123
103
  coder.json # strict coding assistant
124
104
  writer.json # creative writing partner
125
105
  translator.json # bilingual translator
@@ -127,29 +107,35 @@ Create separate stacks for different tasks:
127
107
 
128
108
  Switch with `/preset use coder`, `/preset use writer`, etc.
129
109
 
130
- ### 🧠 Cross-turn memory
110
+ ### 🧪 Presets that show off pi-forge
131
111
 
132
- Define state the agent can read and write:
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
133
121
 
134
122
  ```json
135
- "state": {
136
- "definitions": {
137
- "agent.progress": {
138
- "type": "string",
139
- "scope": "session",
140
- "description": "What we're working on",
141
- "agentWritable": true
142
- }
143
- }
123
+ "variables": {
124
+ "char": "Konata",
125
+ "user": "User"
144
126
  }
145
127
  ```
146
128
 
147
- The agent updates it with `forge_state_set`. You can also set state manually:
129
+ Use static variables for stable prompt constants, and ST-style macros for local prompt-time mutation:
148
130
 
149
131
  ```
150
- /state set user.preference "use TypeScript, not JavaScript"
132
+ {{setvar::mood::focused}}
133
+ {{getvar::mood}}
134
+ {{setsessionvar::topic::compiler cleanup}}
151
135
  ```
152
136
 
137
+ For durable project memory, use normal files in the repo rather than pi-forge prompt variables.
138
+
153
139
  ### 📦 SillyTavern migration
154
140
 
155
141
  Bring your ST presets into Pi:
@@ -160,6 +146,8 @@ Bring your ST presets into Pi:
160
146
 
161
147
  pi-forge converts the preset to a prompt stack and generates a migration report showing what was handled and what needs manual tweaking.
162
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
+
163
151
  ### 🔍 Prompt debugging
164
152
 
165
153
  See exactly what gets sent to the model:
@@ -168,6 +156,8 @@ See exactly what gets sent to the model:
168
156
  /payload next save=.pi/forge/payloads/last.json
169
157
  ```
170
158
 
159
+ Or open `/preset ui`, click **Arm payload**, send the next Pi prompt, and inspect the redacted provider payload in the browser.
160
+
171
161
  Or preview your compiled prompt without sending anything:
172
162
 
173
163
  ```
@@ -188,6 +178,9 @@ Items are arranged in order. When the stack is active, pi-forge:
188
178
  1. Builds a system prompt from your `system`-role blocks and slots, then applies it with the stack's `mode`.
189
179
  2. Inserts `user`/`assistant` blocks and slots around the conversation history.
190
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.
191
184
 
192
185
  ### Slots at a glance
193
186
 
@@ -198,7 +191,7 @@ Items are arranged in order. When the stack is active, pi-forge:
198
191
  | `tool-guidelines` | Tool usage instructions |
199
192
  | `skills` | Loaded Pi skills |
200
193
  | `project-context` | Project instructions and context files |
201
- | `variables` | Agent and user state (progress, preferences, notes) |
194
+ | `variables` | Static/session/turn template variables |
202
195
  | `date` / `cwd` / `date-cwd` | Current date and working directory |
203
196
  | `active-model` | Which model is being used |
204
197
  | `append-system-prompt` | User's appended system prompt text |
@@ -224,26 +217,16 @@ Items are arranged in order. When the stack is active, pi-forge:
224
217
  | `/preset status` | Show the active stack and diagnostics summary |
225
218
  | `/preset diagnostics` | Show runtime diagnostics |
226
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` |
227
221
  | `/preset ui [stop\|restart]` | Open, stop, or restart the web editor |
228
222
 
229
- ### State management
230
-
231
- | Command | What it does |
232
- |---------|-------------|
233
- | `/state list` | Show all session state |
234
- | `/state status` | Show state definitions and current values |
235
- | `/state set <name> <value>` | Set a state variable |
236
- | `/state get <name>` | Read a state variable |
237
- | `/state clear [name]` | Clear state (all or by name) |
238
- | `/preset vars ...` | Legacy variable commands kept for older stacks |
239
-
240
223
  ### Import & debug
241
224
 
242
225
  | Command | What it does |
243
226
  |---------|-------------|
244
227
  | `/preset import-silly <path>` | Import a SillyTavern preset |
245
228
  | `/intercept` | Show the next provider payload |
246
- | `/payload next [save=<path>]` | Show and optionally save the next payload |
229
+ | `/payload next [save=<path>]` | Show, save, and expose the next payload to the web editor |
247
230
 
248
231
  ## Common macros
249
232
 
@@ -313,71 +296,138 @@ Valid roles: `system`, `user`, `assistant`, `custom`.
313
296
 
314
297
  ```json
315
298
  "options": {
316
- "includeLastUserMessage": false
299
+ "includeLastUserMessage": false,
300
+ "stripAssistantThinking": true,
301
+ "includeSummaries": true,
302
+ "toolMode": "keep",
303
+ "roles": ["user", "assistant"],
304
+ "maxMessages": 40,
305
+ "maxChars": 20000
317
306
  }
318
307
  ```
319
308
 
320
309
  Set to `false` when you use `{{lastUserMessage}}` after the history — prevents the user's message from appearing twice.
321
310
 
322
- ### Variables slot options
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.
312
+
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.
314
+
315
+ ### Structured slot format options
316
+
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.
323
318
 
324
319
  ```json
325
320
  {
326
321
  "kind": "slot",
327
- "id": "state",
322
+ "id": "tools",
328
323
  "enabled": true,
329
- "role": "user",
330
- "slot": "variables",
324
+ "role": "system",
325
+ "slot": "tools",
331
326
  "options": {
332
- "includeScopes": ["session"],
333
- "includeNamespaces": ["user.*", "agent.*"],
334
- "includeMetadata": true,
335
- "format": "xml",
336
- "maxValueChars": 1200
327
+ "format": "plain"
337
328
  }
338
329
  }
339
330
  ```
340
331
 
341
- ### State definitions
332
+ The default Pi mirror uses a few extra slot options:
342
333
 
343
334
  ```json
344
- "state": {
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.
349
+
350
+ ```json
351
+ {
352
+ "tools": {
353
+ "allow": ["read", "bash"]
354
+ },
355
+ "skills": {
356
+ "deny": ["browser-danger"]
357
+ }
358
+ }
359
+ ```
360
+
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.
362
+
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.
364
+
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.
366
+
367
+ ### Regex transforms
368
+
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.
370
+
371
+ ```json
372
+ "regex": {
345
373
  "schemaVersion": 1,
346
- "definitions": {
347
- "agent.progress": {
348
- "type": "string",
349
- "scope": "session",
350
- "description": "Current task progress",
351
- "agentWritable": true
352
- },
353
- "user.preference": {
354
- "type": "string",
355
- "scope": "session",
356
- "description": "User's preference for this session",
357
- "userWritable": true
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
358
385
  }
359
- }
386
+ ]
360
387
  }
361
388
  ```
362
389
 
363
- Supported types: `string`, `number`, `boolean`, `null`, `object`, `array`, `string[]`, `number[]`, `boolean[]`, `unknown`, and unions like `string | null`.
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`.
364
391
 
365
- ## Agent tools
392
+ To clean a completed assistant message after streaming, use `effect: "finalize"`:
366
393
 
367
- pi-forge registers two tools that the AI agent can call:
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
+ }
406
+ ```
368
407
 
369
- ### `forge_state_set`
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.
370
409
 
371
- Batch-update persistent state. Only `agent.*` names are writable. Use for cross-turn tracking:
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.
372
411
 
373
- - Task progress (`agent.progress`)
374
- - Open questions (`agent.openQuestions`)
375
- - Story state (`agent.storyState`)
376
- - User-requested notes (`agent.notes`)
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.
377
413
 
378
- ### `forge_set_var`
414
+ ### Variables slot options
379
415
 
380
- Legacy alias for setting a single string value. Prefer `forge_state_set`.
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
+ ```
381
431
 
382
432
  ## Package setup for development
383
433