@zihanw/pi-forge 0.4.0-beta.1 → 0.4.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.
Files changed (186) hide show
  1. package/CHANGELOG.md +37 -1
  2. package/PUBLIC_API.md +3 -26
  3. package/README.md +90 -601
  4. package/README.zh-CN.md +86 -585
  5. package/SUBAGENT_ADAPTER_CONTRACT.md +3 -197
  6. package/dist/forge-config.d.ts +80 -0
  7. package/dist/forge-config.d.ts.map +1 -1
  8. package/dist/forge-config.js +268 -18
  9. package/dist/forge-config.js.map +1 -1
  10. package/dist/index.d.ts +1 -4
  11. package/dist/index.d.ts.map +1 -1
  12. package/dist/index.js +29 -4
  13. package/dist/index.js.map +1 -1
  14. package/dist/lifecycle.js +1 -1
  15. package/dist/profile-service.d.ts +1 -1
  16. package/dist/profile-service.d.ts.map +1 -1
  17. package/dist/profile-service.js +10 -5
  18. package/dist/profile-service.js.map +1 -1
  19. package/dist/runtime/subagent-runtime.d.ts +23 -8
  20. package/dist/runtime/subagent-runtime.d.ts.map +1 -1
  21. package/dist/runtime/subagent-runtime.js +283 -62
  22. package/dist/runtime/subagent-runtime.js.map +1 -1
  23. package/dist/storage.d.ts +1 -0
  24. package/dist/storage.d.ts.map +1 -1
  25. package/dist/storage.js +15 -1
  26. package/dist/storage.js.map +1 -1
  27. package/dist/subagent/canonical.d.ts +19 -7
  28. package/dist/subagent/canonical.d.ts.map +1 -1
  29. package/dist/subagent/canonical.js +19 -47
  30. package/dist/subagent/canonical.js.map +1 -1
  31. package/dist/subagent/contract.d.ts +1 -2
  32. package/dist/subagent/contract.d.ts.map +1 -1
  33. package/dist/subagent/contract.js +1 -2
  34. package/dist/subagent/contract.js.map +1 -1
  35. package/dist/subagent/index.d.ts +4 -3
  36. package/dist/subagent/index.d.ts.map +1 -1
  37. package/dist/subagent/index.js +4 -3
  38. package/dist/subagent/index.js.map +1 -1
  39. package/dist/subagent/plan.d.ts +5 -1
  40. package/dist/subagent/plan.d.ts.map +1 -1
  41. package/dist/subagent/plan.js +28 -31
  42. package/dist/subagent/plan.js.map +1 -1
  43. package/dist/subagent/types.d.ts +62 -178
  44. package/dist/subagent/types.d.ts.map +1 -1
  45. package/dist/subagent/types.js +1 -1
  46. package/dist/subagent/types.js.map +1 -1
  47. package/dist/subagent/validation.d.ts +14 -14
  48. package/dist/subagent/validation.d.ts.map +1 -1
  49. package/dist/subagent/validation.js +52 -238
  50. package/dist/subagent/validation.js.map +1 -1
  51. package/dist/subagent-command.d.ts.map +1 -1
  52. package/dist/subagent-command.js +109 -16
  53. package/dist/subagent-command.js.map +1 -1
  54. package/dist/subagent-host.d.ts.map +1 -1
  55. package/dist/subagent-host.js +1 -0
  56. package/dist/subagent-host.js.map +1 -1
  57. package/dist/subagent-profile-tool.d.ts +25 -2
  58. package/dist/subagent-profile-tool.d.ts.map +1 -1
  59. package/dist/subagent-profile-tool.js +39 -8
  60. package/dist/subagent-profile-tool.js.map +1 -1
  61. package/dist/subagent-tool.d.ts +6 -3
  62. package/dist/subagent-tool.d.ts.map +1 -1
  63. package/dist/subagent-tool.js +85 -14
  64. package/dist/subagent-tool.js.map +1 -1
  65. package/dist/web-editor/client-script.generated.d.ts +1 -1
  66. package/dist/web-editor/client-script.generated.d.ts.map +1 -1
  67. package/dist/web-editor/client-script.generated.js +1 -1
  68. package/dist/web-editor/client-script.generated.js.map +1 -1
  69. package/dist/web-editor/client-styles.d.ts +2 -0
  70. package/dist/web-editor/client-styles.d.ts.map +1 -0
  71. package/dist/web-editor/client-styles.generated.d.ts +2 -0
  72. package/dist/web-editor/client-styles.generated.d.ts.map +1 -0
  73. package/dist/web-editor/client-styles.generated.js +3 -0
  74. package/dist/web-editor/client-styles.generated.js.map +1 -0
  75. package/dist/web-editor/client-styles.js +2 -0
  76. package/dist/web-editor/client-styles.js.map +1 -0
  77. package/dist/web-editor/page.d.ts +2 -0
  78. package/dist/web-editor/page.d.ts.map +1 -1
  79. package/dist/web-editor/page.js +11 -73
  80. package/dist/web-editor/page.js.map +1 -1
  81. package/dist/web-editor/server.d.ts.map +1 -1
  82. package/dist/web-editor/server.js +148 -0
  83. package/dist/web-editor/server.js.map +1 -1
  84. package/dist/web-editor/styles.d.ts.map +1 -1
  85. package/dist/web-editor/styles.js +60 -3
  86. package/dist/web-editor/styles.js.map +1 -1
  87. package/dist/web-editor/types.d.ts +79 -0
  88. package/dist/web-editor/types.d.ts.map +1 -1
  89. package/dist/web-host.d.ts +13 -2
  90. package/dist/web-host.d.ts.map +1 -1
  91. package/dist/web-host.js +301 -0
  92. package/dist/web-host.js.map +1 -1
  93. package/docs/README.md +41 -0
  94. package/docs/concepts/agent-profiles.md +60 -0
  95. package/docs/concepts/prompt-stacks.md +90 -0
  96. package/docs/design/README.md +17 -0
  97. package/docs/design/roadmap-0.4-archive.md +216 -0
  98. package/docs/design/subagents/design-review.md +220 -0
  99. package/docs/design/subagents/interface-design.md +274 -0
  100. package/docs/design/subagents/sdk-spike-findings.md +117 -0
  101. package/docs/development/complexity-review.md +86 -0
  102. package/docs/development/release.md +31 -0
  103. package/docs/development/roadmap.md +42 -0
  104. package/docs/development/setup.md +75 -0
  105. package/docs/getting-started.md +93 -0
  106. package/docs/guides/custom-macros-and-slots.md +68 -0
  107. package/docs/guides/debugging.md +39 -0
  108. package/docs/guides/delegation.md +99 -0
  109. package/docs/guides/sillytavern-import.md +47 -0
  110. package/docs/guides/use-cases.md +65 -0
  111. package/docs/guides/web-editor.md +75 -0
  112. package/docs/reference/commands.md +60 -0
  113. package/docs/reference/configuration.md +64 -0
  114. package/docs/reference/features.md +279 -0
  115. package/docs/reference/macros-and-slots.md +82 -0
  116. package/docs/reference/public-api.md +28 -0
  117. package/docs/reference/stack-schema.md +167 -0
  118. package/docs/reference/subagent-adapter.md +204 -0
  119. package/docs/zh-CN/README.md +37 -0
  120. package/docs/zh-CN/concepts/agent-profiles.md +44 -0
  121. package/docs/zh-CN/concepts/prompt-stacks.md +40 -0
  122. package/docs/zh-CN/getting-started.md +79 -0
  123. package/docs/zh-CN/guides/delegation.md +66 -0
  124. package/docs/zh-CN/guides/web-editor.md +45 -0
  125. package/docs/zh-CN/reference/commands.md +58 -0
  126. package/package.json +28 -14
  127. package/dist/subagent/backend-registry.d.ts +0 -75
  128. package/dist/subagent/backend-registry.d.ts.map +0 -1
  129. package/dist/subagent/backend-registry.js +0 -463
  130. package/dist/subagent/backend-registry.js.map +0 -1
  131. package/dist/subagent/diagnostics.d.ts +0 -3
  132. package/dist/subagent/diagnostics.d.ts.map +0 -1
  133. package/dist/subagent/diagnostics.js +0 -5
  134. package/dist/subagent/diagnostics.js.map +0 -1
  135. package/dist/subagent/pi-model-runtime.d.ts +0 -8
  136. package/dist/subagent/pi-model-runtime.d.ts.map +0 -1
  137. package/dist/subagent/pi-model-runtime.js +0 -22
  138. package/dist/subagent/pi-model-runtime.js.map +0 -1
  139. package/dist/subagent/pi-sdk-backend.d.ts +0 -23
  140. package/dist/subagent/pi-sdk-backend.d.ts.map +0 -1
  141. package/dist/subagent/pi-sdk-backend.js +0 -383
  142. package/dist/subagent/pi-sdk-backend.js.map +0 -1
  143. package/dist/subagent/pi-subprocess-backend.d.ts +0 -72
  144. package/dist/subagent/pi-subprocess-backend.d.ts.map +0 -1
  145. package/dist/subagent/pi-subprocess-backend.js +0 -756
  146. package/dist/subagent/pi-subprocess-backend.js.map +0 -1
  147. package/dist/subagent/subprocess-bridge.d.ts +0 -21
  148. package/dist/subagent/subprocess-bridge.d.ts.map +0 -1
  149. package/dist/subagent/subprocess-bridge.js +0 -87
  150. package/dist/subagent/subprocess-bridge.js.map +0 -1
  151. package/dist/subagent/subprocess-report.d.ts +0 -4
  152. package/dist/subagent/subprocess-report.d.ts.map +0 -1
  153. package/dist/subagent/subprocess-report.js +0 -55
  154. package/dist/subagent/subprocess-report.js.map +0 -1
  155. package/dist/subagent-contract.d.ts +0 -8
  156. package/dist/subagent-contract.d.ts.map +0 -1
  157. package/dist/subagent-contract.js +0 -8
  158. package/dist/subagent-contract.js.map +0 -1
  159. package/dist/web-editor/client/api.d.ts +0 -9
  160. package/dist/web-editor/client/api.d.ts.map +0 -1
  161. package/dist/web-editor/client/api.js +0 -26
  162. package/dist/web-editor/client/api.js.map +0 -1
  163. package/dist/web-editor/client/dom.d.ts +0 -13
  164. package/dist/web-editor/client/dom.d.ts.map +0 -1
  165. package/dist/web-editor/client/dom.js +0 -30
  166. package/dist/web-editor/client/dom.js.map +0 -1
  167. package/dist/web-editor/client/inspector.d.ts +0 -22
  168. package/dist/web-editor/client/inspector.d.ts.map +0 -1
  169. package/dist/web-editor/client/inspector.js +0 -226
  170. package/dist/web-editor/client/inspector.js.map +0 -1
  171. package/dist/web-editor/client/main.d.ts +0 -2
  172. package/dist/web-editor/client/main.d.ts.map +0 -1
  173. package/dist/web-editor/client/main.js +0 -1468
  174. package/dist/web-editor/client/main.js.map +0 -1
  175. package/dist/web-editor/client/policy-editor.d.ts +0 -16
  176. package/dist/web-editor/client/policy-editor.d.ts.map +0 -1
  177. package/dist/web-editor/client/policy-editor.js +0 -330
  178. package/dist/web-editor/client/policy-editor.js.map +0 -1
  179. package/dist/web-editor/client/regex-editor.d.ts +0 -19
  180. package/dist/web-editor/client/regex-editor.d.ts.map +0 -1
  181. package/dist/web-editor/client/regex-editor.js +0 -281
  182. package/dist/web-editor/client/regex-editor.js.map +0 -1
  183. package/dist/web-editor/client/types.d.ts +0 -60
  184. package/dist/web-editor/client/types.d.ts.map +0 -1
  185. package/dist/web-editor/client/types.js +0 -2
  186. package/dist/web-editor/client/types.js.map +0 -1
package/README.md CHANGED
@@ -1,666 +1,155 @@
1
1
  # pi-forge
2
2
 
3
- [English](README.md) | [简体中文](README.zh-CN.md)
3
+ [English](README.md) | [简体中文](README.zh-CN.md) · [Documentation](docs/README.md)
4
4
 
5
5
  ![pi-forge header](https://raw.githubusercontent.com/MacroSony/pi-forge/main/assets/pi-forge-header-concept-1.png)
6
6
 
7
- **pi-forge** lets you customize how Pi thinks and behaves. It gives you prompt stacks for prompt/tool policy and agent profiles that apply a model, thinking level, and prompt stack as a reusable one-shot preset.
7
+ **pi-forge** lets you customize how [Pi](https://github.com/badlogic/pi-mono) thinks and behaves. Prompt stacks control prompt composition and tool policy; agent profiles apply a model, thinking level, and stack as a reusable one-shot preset.
8
8
 
9
- Think of it as a character sheet for your AI agent.
9
+ Think of it as a character sheet and workbench for your AI agent.
10
10
 
11
- ## What you can do with it
11
+ ## Highlights
12
12
 
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
- - **Save complete agent presets** — capture the current model, thinking level, and prompt stack, then apply them together later.
16
- - **Control what the AI sees** — choose which tools, skills, and project context appear in each prompt.
17
- - **Limit tools and skills per stack** — enforce active tool policy and filter skill visibility for focused modes.
18
- - **Use template variables** — define static values such as `{{char}}` / `{{user}}`, and use ST-style turn/session variable macros inside prompt text.
19
- - **Transform outgoing and finalized text** — run deterministic regex replacements on selected history, compiled prompt text, or finalized assistant messages.
20
- - **Import SillyTavern presets** — bring your existing ST character presets into Pi with one command.
21
- - **Debug your prompts** — intercept and inspect exactly what gets sent to the model.
13
+ - Compose Pi's system prompt, conversation history, tools, skills, project context, and runtime data as ordered blocks and slots.
14
+ - Switch between coding, reviewing, writing, roleplay, and translation modes with one command.
15
+ - Save and apply complete model/thinking/stack profiles.
16
+ - Enforce per-stack tool policy and filter model-visible skills.
17
+ - Use static, turn, and session variables with nested template macros.
18
+ - Apply deterministic regex transforms to outgoing prompts or finalized assistant messages.
19
+ - Import SillyTavern presets and inspect the migration report.
20
+ - Edit stacks and profiles in a local browser UI and inspect the exact provider payload.
21
+ - Run an explicitly enabled profile as an experimental, approval-gated foreground subagent.
22
22
 
23
- ## Quick start
23
+ ## Install
24
24
 
25
- ### Install
25
+ pi-forge requires Node.js 22.19 or newer.
26
26
 
27
27
  ```bash
28
- pi install npm:@zihanw/pi-forge@0.4.0-beta.1
28
+ pi install npm:@zihanw/pi-forge
29
29
  ```
30
30
 
31
- The beta is published under npm's `next` channel rather than replacing the stable `latest` release. It requires Node.js 22.19 or newer and the exact `@earendil-works/pi-*` 0.80.10 packages used by Pi 0.80.10.
31
+ Restart Pi after installing or updating the extension. Pi supplies its SDK packages to extensions at runtime; pi-forge keeps exact Pi versions only for reproducible development and tests. See [compatibility and setup](docs/development/setup.md#pi-compatibility) for the supported/tested policy.
32
32
 
33
- > **Pi version compatibility:** Pi changed session authentication/runtime wiring within the 0.80.x line. This build targets 0.80.10 exactly; 0.80.6–0.80.9 and later unverified 0.80.x releases are not claimed as compatible. After installing or rebuilding pi-forge, restart Pi so the extension and host SDK agree. If the parent agent can use a provider but subagent preparation reports `No API key found`, check for an older pi-forge build before using `/login`: a build using the pre-0.80.10 session API can lose the parent authentication during preparation, and logging in again does not fix that version mismatch.
33
+ ## Five-minute start
34
34
 
35
- ### Your first prompt stack
35
+ ### 1. Create a prompt stack
36
36
 
37
- Create `.pi/forge/prompt-stacks/default.json` from [examples/default-prompt-stack.json](examples/default-prompt-stack.json).
38
-
39
- 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.
37
+ Create `.pi/forge/prompt-stacks/default.json` from [the default Pi mirror](examples/default-prompt-stack.json):
40
38
 
41
39
  ```bash
42
40
  mkdir -p .pi/forge/prompt-stacks
43
- $EDITOR .pi/forge/prompt-stacks/default.json
44
- ```
45
-
46
- 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`.
47
-
48
- 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`.
49
-
50
- ### Visual editor
51
-
52
- Prefer clicking over typing JSON? pi-forge has a built-in web editor:
53
-
41
+ cp examples/default-prompt-stack.json .pi/forge/prompt-stacks/default.json
54
42
  ```
55
- /preset ui
56
- ```
57
-
58
- Drag, drop, create, 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. New stacks start from the default Pi prompt mirror layout. Existing stack IDs are immutable; use Fork to create a new ID without risking profile references or active selection. Stack metadata is collapsible so the active editor stays in view. The policy tab shows registered tools and loaded skills with selected-pattern chips and filtering, so allow/deny rules can be built from exact names while still supporting wildcards.
59
43
 
60
- 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.
44
+ If you installed from npm rather than cloning this repository, open `/preset ui` and create a new stack; the editor starts with the same Pi-mirror layout.
61
45
 
62
- 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; resources and preview remain available across lifecycle refreshes. 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.
63
-
64
- 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.
65
-
66
- 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:
67
-
68
- ```json
69
- {
70
- "webEditor": {
71
- "port": 41738
72
- }
73
- }
74
- ```
75
-
76
- ### Agent profiles
77
-
78
- Agent profiles are project-local JSON files under `.pi/forge/agent-profiles`. The quickest way to create one is to configure Pi normally and capture the current model, thinking level, and prompt-stack selection:
46
+ Restart Pi or run:
79
47
 
80
48
  ```text
81
- /profile save reviewer
82
- /profile use reviewer
83
- ```
84
-
85
- Profiles are applied once. They do not continuously own Pi's model or thinking level, so later manual changes are preserved until `/profile use reviewer` is run again. Prompt-stack tool policy remains strict for as long as that stack is active.
86
-
87
- A profile can also be written directly:
88
-
89
- ```json
90
- {
91
- "schemaVersion": 1,
92
- "type": "pi-forge.agent-profile",
93
- "id": "reviewer",
94
- "name": "Reviewer",
95
- "description": "Reviews code without making changes.",
96
- "autoActivate": true,
97
- "model": {
98
- "provider": "provider-id",
99
- "id": "model-id"
100
- },
101
- "thinkingLevel": "high",
102
- "promptStack": "reviewer"
103
- }
49
+ /preset reload
50
+ /preset use default
104
51
  ```
105
52
 
106
- `autoActivate: true` applies the complete profile once when Pi starts a fresh session. At most one profile may request auto-activation. An auto-activated profile takes precedence over standalone prompt-stack autoload, including when its `promptStack` is `null`; if no profile requests auto-activation, the existing `default.json`/`autoActivate` stack behavior remains the fallback. Restored branch selections take precedence over both mechanisms.
107
-
108
- `promptStack` may be `null`. Tool names and skill lists do not belong in profile v1: the referenced prompt stack is the single source of truth for tool policy and model-visible skill filtering. Profile validation rejects unsupported fields rather than silently retaining inert generation or runner settings.
109
-
110
- `/profile preview <id>` resolves the model, authentication, thinking-level support, prompt stack, and effective tools without changing runtime state. `/profile status` reports the last-applied profile and current drift; it deliberately does not describe a profile as active. Provenance follows the session branch for status purposes but never causes automatic reapplication during reload, resume, tree navigation, or compaction. Fresh-session auto-activation is still one-shot, so later manual changes are preserved.
53
+ `default.json` auto-activates when no stack or restored session selection takes precedence.
111
54
 
112
- ### Experimental foreground subagent
113
-
114
- The 0.4 beta can run a stored profile as a separate, clean, one-shot Pi subprocess. The no-egress `forge_subagent_profiles` tool lets the main agent discover the currently loaded profile IDs, names, descriptions, declared model/thinking/stack, current resolution status, and approval mode. It should call that first when the user has not specified a profile, then invoke `forge_subagent`. A restrictive main-agent prompt stack must permit both tool names. The same execution path remains available to a human through commands:
55
+ ### 2. Open the visual editor
115
56
 
116
57
  ```text
117
- /forge-agent backends
118
- /forge-agent plan reviewer Review this API design for correctness.
119
- /forge-agent run reviewer Review this API design for correctness.
120
- ```
121
-
122
- `plan` resolves the profile and stack, compiles the exact provider-bound prompt, validates an immutable execution plan, and then discards it without contacting the provider. `/forge-agent run` and, by default, `forge_subagent` prepare that same exact plan before showing an approval screen. The default screen shows the agent task, profile/stack, provider, model, thinking level, effective tools, working directory, security boundary, payload size, and execution fingerprint. Choose **View full prompt** to inspect the complete system prompt and ordered provider-bound messages before approving; any editor changes are ignored.
123
-
124
- To deliberately let the parent agent invoke `forge_subagent` without per-run approval, set the following trusted-project option in `.pi/forge/config.json`:
125
-
126
- ```json
127
- {
128
- "subagents": {
129
- "allowAgentInvocationWithoutApproval": true
130
- }
131
- }
132
- ```
133
-
134
- This option affects only the model-callable `forge_subagent` tool; `/forge-agent run` remains interactively approved. The exact preflight and immutable-plan checks still run, and the tool result records `trusted-project-config` as its authorization source, but provider transport begins without showing the prompt to a human. Profile discovery reports the active approval mode. The setting is ignored for untrusted projects and malformed values fail closed. Treat the project config as authorization: do not enable or commit this option in a repository unless every parent agent allowed to use `forge_subagent` should be able to send the compiled prompt and readable file contents to the selected provider without asking again.
135
-
136
- The child starts with a clean conversation and receives no parent history automatically. It runs in the foreground with the profile's exact model/thinking level and prompt stack. Its only candidate tools are `read`, `grep`, `find`, and `ls`, further restricted by the stack's tool policy; it receives no write/edit/shell tools, skills, prompt templates, context files, or third-party extensions. The final tool result contains a bounded model-visible report plus expandable human-visible execution details. Retained transcript strings are individually bounded, base64-like text is redacted, and the transcript keeps a 512 KiB rolling tail so the final report remains available without making the TUI retain an unbounded tool history. Inline image data stays inside the child long enough for the selected vision model to use it, but the dedicated report channel replaces binary payloads with MIME type and encoded-size metadata before anything is retained in the parent session.
137
-
138
- Important: this first backend is **shared-user**, not an operating-system sandbox. Read-only is a model-tool policy: the subprocess retains the invoking user's OS permissions, and absolute paths readable by that user may be read, sent to the selected provider, and retained as text inside the parent tool-result details. Host timeout and cancellation are best effort. `/tree` removes the invocation and result from the active conversation branch, but abandoned entries can remain in Pi's on-disk session JSONL; deleting sensitive retained text requires deleting the relevant session data. `/tree` also cannot undo provider requests, billing, or external side effects. The default tool set intentionally provides no filesystem mutation path while a bubblewrap-style sandbox and staged write mode remain future work.
139
-
140
- ## Use cases
141
-
142
- ### 🎭 Roleplay & creative writing
143
-
144
- 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.
145
-
146
- Useful pattern:
147
- - Put long-term character rules in a `system` block.
148
- - Keep Pi runtime context (tools, skills, project) in `user` slots.
149
- - Set the `chat-history` slot to skip the latest user message.
150
- - Add a final `user` block with `{{lastUserMessage}}`.
151
-
152
- This keeps the latest request clear and avoids duplicating it.
153
-
154
- For a baseline stack to fork before turning Pi into a character, start from [examples/default-prompt-stack.json](examples/default-prompt-stack.json).
155
-
156
- ### 🧑‍💻 Focused code review
157
-
158
- 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.
159
-
160
- Use `mode: "append"` if you want to keep Pi's normal coding behavior and only add the sharper review lens.
161
-
162
- ### 🌐 Translation mode
163
-
164
- 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.
165
-
166
- ### 🔀 Multi-mode switching
167
-
168
- Create separate stacks for different tasks:
169
-
170
- ```
171
- .pi/forge/prompt-stacks/
172
- coder.json # strict coding assistant
173
- writer.json # creative writing partner
174
- translator.json # bilingual translator
175
- ```
176
-
177
- Switch with `/preset use coder`, `/preset use writer`, etc.
178
-
179
- ### 🧪 Presets that show off pi-forge
180
-
181
- - **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.
182
- - **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.
183
- - **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.
184
- - **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.
185
- - **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.
186
- - **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.
187
- - **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.
188
-
189
- ### 🔧 Template variables
190
-
191
- ```json
192
- "variables": {
193
- "char": "Konata",
194
- "user": "User"
195
- }
196
- ```
197
-
198
- Use static variables for stable prompt constants, and ST-style macros for local prompt-time mutation:
199
-
200
- ```
201
- {{setvar::mood::focused}}
202
- {{getvar::mood}}
203
- {{setsessionvar::topic::compiler cleanup}}
204
- ```
205
-
206
- For durable project memory, use normal files in the repo rather than pi-forge prompt variables.
207
-
208
- ### 📦 SillyTavern migration
209
-
210
- Bring your ST presets into Pi:
211
-
212
- ```
213
- /preset import-silly ~/SillyTavern/presets/my-preset.json
58
+ /preset ui
214
59
  ```
215
60
 
216
- pi-forge converts the preset to a prompt stack and generates a migration report showing what was handled and what needs manual tweaking.
217
-
218
- 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.
219
-
220
- ### 🔍 Prompt debugging
221
-
222
- See exactly what gets sent to the model:
61
+ The local editor can create, fork, validate, preview, import, export, and delete prompt stacks. Its **Agent profiles** view manages one-shot model/thinking/stack presets and experimental delegation settings. Writes require a trusted project.
223
62
 
224
- ```
225
- /payload next save=.pi/forge/payloads/last.json
226
- ```
227
-
228
- Or open `/preset ui`, click **Arm payload**, send the next Pi prompt, and inspect the redacted provider payload in the browser. Credential-shaped token fields remain hidden, while normal limits and accounting fields such as `max_tokens`, `input_tokens`, and `output_tokens` remain visible.
63
+ ### 3. Save a profile
229
64
 
230
- Or preview your compiled prompt without sending anything:
65
+ Configure Pi normally, then capture and reuse the current settings:
231
66
 
232
- ```
233
- /preset preview
67
+ ```text
68
+ /profile save reviewer
69
+ /profile use reviewer
234
70
  ```
235
71
 
236
- ## How it works
72
+ A profile applies once. Later manual changes to the model or thinking level remain in effect until the profile is applied again; an active prompt stack continues enforcing its tool policy.
237
73
 
238
- A prompt stack is a JSON file with two kinds of items:
74
+ ## The basic model
239
75
 
240
- | Kind | What it does |
241
- |------|-------------|
242
- | **Block** | Static text inserted at a specific position (system prompt, user message, assistant message) |
243
- | **Slot** | Dynamic content from Pi's runtime — tools, skills, chat history, date, project context, etc. |
76
+ A prompt stack is an ordered JSON document containing:
244
77
 
245
- Items are arranged in order. When the stack is active, pi-forge:
78
+ | Item | Purpose |
79
+ |---|---|
80
+ | **Block** | Static `system`, `user`, `assistant`, or hidden `custom` text |
81
+ | **Slot** | Runtime content such as tools, skills, project context, variables, date/cwd, or chat history |
246
82
 
247
- 1. Builds a system prompt from your `system`-role blocks and slots, then applies it with the stack's `mode`.
248
- 2. Inserts `user`/`assistant` blocks and slots around the conversation history.
249
- 3. Expands `{{macros}}` like `{{lastUserMessage}}`, `{{date}}`, and custom variables.
250
- 4. Applies stack tool policy to Pi's active tool set and filters pi-forge-rendered tool/skill slots.
251
- 5. Applies enabled outgoing regex rules for the `history` and `compiled` stages.
252
- 6. Optionally applies destructive `finalize` regex rules when an assistant message finishes.
83
+ Stacks can `replace`, `append`, or `prepend` Pi's base system prompt. During compilation, pi-forge expands macros, inserts conversation content, enforces tool policy, filters its skill listing, and applies enabled regex rules.
253
84
 
254
- ### Slots at a glance
85
+ Agent profiles are project-local references to an exact provider/model, thinking level, and prompt stack. They intentionally do not duplicate tool or skill policy—the referenced stack remains the source of truth.
255
86
 
256
- | Slot | What it inserts |
257
- |------|----------------|
258
- | `chat-history` | The current conversation |
259
- | `tools` | Available tools and their descriptions |
260
- | `tool-guidelines` | Tool usage instructions |
261
- | `skills` | Loaded Pi skills |
262
- | `project-context` | Project instructions and context files |
263
- | `variables` | Static/session/turn template variables |
264
- | `date` / `cwd` / `date-cwd` | Current date, optional current time, and working directory |
265
- | `active-model` | Which model is being used |
266
- | `append-system-prompt` | User's appended system prompt text |
267
- | `pi-docs` | Pi documentation guidance |
87
+ Start with these examples:
268
88
 
269
- ### Modes
270
-
271
- - **replace** (default) — your stack replaces Pi's system prompt entirely.
272
- - **append** — your stack is added after Pi's default system prompt.
273
- - **prepend** — your stack is added before Pi's default system prompt.
89
+ - [Default Pi mirror](examples/default-prompt-stack.json) keeps normal Pi behavior while making its sections movable.
90
+ - [Focused reviewer](examples/reviewer-prompt-stack.json) creates a read-only review layout with an explicit latest-user target.
91
+ - [SillyTavern DM writer](examples/sillytavern-dm-writer-prompt-stack.json) demonstrates characters, variables, history placement, and regex cleanup.
92
+ - [Custom system-status extension](examples/custom-system-status-extension/README.md) registers a trusted macro and slot.
274
93
 
275
94
  ## Common commands
276
95
 
277
- ### Managing stacks
278
-
279
- | Command | What it does |
280
- |---------|-------------|
281
- | `/preset list` | Show all available stacks |
282
- | `/preset use <id>` | Activate a stack |
283
- | `/preset use none` | Disable prompt stacks for the session |
284
- | `/preset preview [id]` | See the compiled prompt |
285
- | `/preset validate [id]` | Check a stack for issues |
286
- | `/preset status` | Show the active stack and diagnostics summary |
287
- | `/preset diagnostics` | Show runtime diagnostics |
288
- | `/preset reload` | Reload stacks from disk |
289
- | `/preset migrate-stacks [--dry-run] [--overwrite] [--delete-legacy]` | Copy legacy `.pi/prompt-stacks` files into `.pi/forge/prompt-stacks` |
290
- | `/preset ui [stop\|restart]` | Open, stop, or restart the web editor |
291
-
292
- ### Managing agent profiles
293
-
294
- | Command | What it does |
295
- |---------|-------------|
296
- | `/profile list` | Show project profiles and resolution diagnostics |
96
+ | Command | Purpose |
97
+ |---|---|
98
+ | `/preset ui [stop\|restart]` | Open or manage the web editor |
99
+ | `/preset list` | List prompt stacks |
100
+ | `/preset use <id\|none>` | Select or disable a stack |
101
+ | `/preset preview [id]` | Compile a stack without sending a request |
102
+ | `/preset validate [id]` | Validate one stack or all stacks |
103
+ | `/preset diagnostics` | Show runtime and extension diagnostics |
104
+ | `/profile list` | List and preflight profiles |
105
+ | `/profile save <id> [--overwrite]` | Capture the current runtime as a profile |
297
106
  | `/profile use <id>` | Preflight and apply a profile once |
298
- | `/profile save <id> [--overwrite]` | Capture the current model, thinking level, and prompt stack |
299
- | `/profile status` | Show current runtime, last-applied provenance, and drift |
300
- | `/profile preview <id>` | Preview resolved effects and effective tools without applying |
301
- | `/profile validate [id]` | Validate one profile, or all profiles when omitted |
302
- | `/profile reload` | Reload profile files without applying them |
303
- | `/profile forget` | Forget last-applied provenance without changing runtime state |
304
-
305
- ### Experimental foreground subagent
107
+ | `/profile status` | Show last-applied provenance and runtime drift |
108
+ | `/payload next [save=<path>]` | Inspect the next redacted provider payload |
306
109
 
307
- | Command | What it does |
308
- |---------|-------------|
309
- | `/forge-agent backends` | Show the available experimental backend and its capabilities |
310
- | `/forge-agent plan <profile> <task>` | Prepare, validate, display, and discard an exact plan without provider transport |
311
- | `/forge-agent run <profile> <task>` | Review the exact plan and run one foreground read-only text task after approval |
110
+ See the [complete command reference](docs/reference/commands.md).
312
111
 
313
- The model-callable tools are `forge_subagent_profiles` for local metadata discovery and `forge_subagent` for execution. Discovery needs no approval and performs no provider request or subagent prompt preparation. Invocation requires the current main-agent tool policy to permit it; execution then requires either an interactive approval UI or the explicit trusted-project unattended option described above.
112
+ ## Experimental foreground delegation
314
113
 
315
- ### Import & debug
114
+ pi-forge can run an explicitly enabled profile as a clean, foreground Pi subprocess. The model can discover eligible profiles with `forge_subagent_profiles` and invoke one with `forge_subagent`; humans use `/forge-agent plan` and `/forge-agent run`.
316
115
 
317
- | Command | What it does |
318
- |---------|-------------|
319
- | `/preset import-silly <path>` | Import a SillyTavern preset |
320
- | `/intercept` | Show the next provider payload |
321
- | `/payload next [save=<path>]` | Show, save, and expose the next payload to the web editor |
116
+ This feature is **experimental** and profiles are not delegatable by default. Enable each profile in the trusted project's `.pi/forge/config.json` or its web-editor delegation card. Interactive execution presents an immutable plan for approval unless the project explicitly authorizes unattended model invocation.
322
117
 
323
- ## Common macros
118
+ > **Security boundary:** The current backends are shared-user processes, not operating-system sandboxes. “Read-only” describes the model-visible tool policy. The child retains the invoking user's OS read permissions, and readable content may be sent to the selected provider and retained in Pi's session data. Timeout and cancellation are best effort, and `/tree` cannot undo provider requests, billing, or external effects.
324
119
 
325
- Use these in block content to insert dynamic values:
120
+ Read [foreground delegation and its safety model](docs/guides/delegation.md) before enabling it.
326
121
 
327
- | Macro | Expands to |
328
- |-------|-----------|
329
- | `{{lastUserMessage}}` | The user's latest message |
330
- | `{{date}}` | Current date (YYYY-MM-DD) |
331
- | `{{time}}` | Current time (HH:MM:SS) |
332
- | `{{cwd}}` | Current working directory |
333
- | `{{tools}}` | Comma-separated tool names |
334
- | `{{selectedTools}}` | Alias for selected tool names |
335
- | `{{activeModel}}` | Current model (provider/id) |
336
- | `{{char}}` / `{{user}}` | Custom variables from your stack |
122
+ ## Documentation
337
123
 
338
- ### Variable macros
124
+ ### Learn
339
125
 
340
- ```
341
- {{setvar::name::value}} set a turn variable (cleared each message)
342
- {{setsessionvar::name::value}} set a session variable (persists)
343
- {{setvar::session::name::value}} also set a session variable
344
- {{getvar::name}} read a variable (turn → session → static)
345
- {{getturnvar::name}} read only a turn variable
346
- {{getsessionvar::name}} read only a session variable
347
- {{clearvar::name}} clear a variable
348
- {{clearturnvar::name}} clear a turn variable
349
- {{clearsessionvar::name}} clear a session variable
350
- ```
351
-
352
- ### Filter and conditional macros
353
-
354
- Nested macros are supported, and `::` separators are parsed only at the current macro depth.
355
-
356
- | Macro | Expands to |
357
- |-------|-----------|
358
- | `{{trim::value}}` | `value` with leading/trailing whitespace removed |
359
- | `{{upper::value}}` | Uppercase `value` |
360
- | `{{lower::value}}` | Lowercase `value` |
361
- | `{{json::value}}` | JSON string literal for `value` |
362
- | `{{xml::value}}` | XML-escaped `value` |
363
- | `{{ifvar::name::then::else}}` | `then` when a variable exists, otherwise `else` |
364
- | `{{ifeq::name::expected::then::else}}` | `then` when a variable equals `expected`, otherwise `else` |
365
- | `{{iftools::tool::then::else}}` | `then` when the selected tool list includes `tool`, otherwise `else` |
366
- | `{{ifslot::slot::then::else}}` | `then` when the enabled stack items include `slot`, otherwise `else` |
367
-
368
- Conditional branches are lazy: only the selected branch is expanded, so skipped branches cannot set or clear variables. The final `else` argument is optional and defaults to empty text.
369
-
370
- ### Trusted custom macros and slots
371
-
372
- Custom macros and slots are registered by trusted extension code, not embedded in prompt-stack JSON. For project-local customization, put registration modules in `.pi/forge/extensions/`. For machine-wide personal customization, put them in `~/.pi/forge/extensions/`. pi-forge loads global modules first, then project-local modules, after project trust and before stack validation. Both locations reload on `/preset reload`.
373
-
374
- These modules receive the registration API from pi-forge, so they do not need to import `@zihanw/pi-forge` or know where pi-forge is installed.
375
-
376
- ```ts
377
- // .pi/forge/extensions/ticket-context.ts
378
- export default function register(api) {
379
- api.registerMacro({
380
- name: "ticketId",
381
- description: "Current ticket id from session variables.",
382
- render: (ctx) => ctx.variables.toMacroText(ctx.variables.get("ticket.id")),
383
- });
384
-
385
- api.registerSlot({
386
- name: "ticket-context",
387
- description: "Render ticket context for the current task.",
388
- options: {
389
- heading: { type: "string", default: "Ticket context" },
390
- },
391
- render: (ctx) => [
392
- String(ctx.options.heading ?? "Ticket context") + ":",
393
- "- Ticket: " + ctx.variables.toMacroText(ctx.variables.get("ticket.id")),
394
- "- Project: " + ctx.helpers.normalizePath(ctx.runtime.options.cwd),
395
- ].join("\n"),
396
- });
397
- }
398
- ```
399
-
400
- The stack remains declarative:
401
-
402
- ```json
403
- {
404
- "kind": "slot",
405
- "id": "ticket-context",
406
- "enabled": true,
407
- "role": "system",
408
- "slot": "ticket-context",
409
- "options": {
410
- "heading": "Current ticket"
411
- }
412
- }
413
- ```
414
-
415
- Supported module files are `.ts`, `.js`, `.mjs`, `.cjs`, and `index.*` inside a subdirectory. TypeScript modules should stick to syntax Node can strip at runtime, or you can use `.js` / `.mjs` instead. A module can export either `default function register(api)` or `export function register(api)`. Registered macro and slot names must be unique across built-ins, global extensions, and project extensions; duplicate names show as extension load warnings.
416
-
417
- The API includes `cwd`, `forgeDir`, `extensionPath`, `helpers`, `registerMacro`, `registerSlot`, `getRegisteredMacros`, and `getRegisteredSlots`. For global modules, `forgeDir` is `~/.pi/forge`; for project modules, it is `<project>/.pi/forge`.
418
-
419
- Missing custom slots are validation warnings until the registering module is loaded. Built-in macros and slots use the same registry internally, so `getRegisteredMacros()` and `getRegisteredSlots()` can be used as implementation references. `/preset diagnostics` shows loaded pi-forge extension files and load failures.
420
-
421
- For a complete copyable extension and stack, see [examples/custom-system-status-extension](examples/custom-system-status-extension). It registers a `{{cpuLoad}}` macro and a `machine-status` slot from `.pi/forge/extensions/system-status.ts`.
422
-
423
- Reusable Pi packages can still import `registerMacro` and `registerSlot` from `@zihanw/pi-forge`. The `.pi/forge/extensions` and `~/.pi/forge/extensions` loaders are intended for small trusted customizations without package boilerplate.
424
-
425
- ## Stack reference
426
-
427
- ### Full item types
428
-
429
- **Block:**
430
-
431
- ```json
432
- {
433
- "kind": "block",
434
- "id": "unique-id",
435
- "name": "Readable label",
436
- "enabled": true,
437
- "role": "system",
438
- "content": "Your text here. Use {{macros}} for dynamic content."
439
- }
440
- ```
441
-
442
- Valid roles: `system`, `user`, `assistant`, `custom`.
443
-
444
- **Slot:**
445
-
446
- ```json
447
- {
448
- "kind": "slot",
449
- "id": "unique-id",
450
- "name": "Chat History",
451
- "enabled": true,
452
- "role": "user",
453
- "slot": "chat-history",
454
- "options": {
455
- "includeLastUserMessage": false
456
- }
457
- }
458
- ```
459
-
460
- ### Chat history options
461
-
462
- ```json
463
- "options": {
464
- "includeLastUserMessage": false,
465
- "stripAssistantThinking": true,
466
- "includeSummaries": true,
467
- "toolMode": "keep",
468
- "roles": ["user", "assistant"],
469
- "maxMessages": 40,
470
- "maxChars": 20000
471
- }
472
- ```
473
-
474
- Set to `false` when you use `{{lastUserMessage}}` after the history — prevents the user's message from appearing twice.
475
-
476
- 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.
477
-
478
- 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.
479
-
480
- ### Date slot options
126
+ - [Getting started](docs/getting-started.md)
127
+ - [Prompt-stack concepts](docs/concepts/prompt-stacks.md)
128
+ - [Agent-profile concepts](docs/concepts/agent-profiles.md)
129
+ - [Web editor](docs/guides/web-editor.md)
130
+ - [Prompt-stack patterns and examples](docs/guides/use-cases.md)
131
+ - [SillyTavern import](docs/guides/sillytavern-import.md)
132
+ - [Custom macros and slots](docs/guides/custom-macros-and-slots.md)
133
+ - [Prompt and payload debugging](docs/guides/debugging.md)
481
134
 
482
- Set `"includeTime": true` on a `date` or `date-cwd` slot to include the current time in `HH:MM:SS` after the current date.
135
+ ### Reference
483
136
 
484
- ### Structured slot format options
137
+ - [Commands](docs/reference/commands.md)
138
+ - [Stack schema and policy](docs/reference/stack-schema.md)
139
+ - [Macros and slots](docs/reference/macros-and-slots.md)
140
+ - [Configuration](docs/reference/configuration.md)
141
+ - [Public API policy](docs/reference/public-api.md)
142
+ - [Experimental subagent adapter](docs/reference/subagent-adapter.md)
485
143
 
486
- 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.
487
-
488
- ```json
489
- {
490
- "kind": "slot",
491
- "id": "tools",
492
- "enabled": true,
493
- "role": "system",
494
- "slot": "tools",
495
- "options": {
496
- "format": "plain"
497
- }
498
- }
499
- ```
500
-
501
- The default Pi mirror uses a few extra slot options:
502
-
503
- ```json
504
- {
505
- "slot": "tools",
506
- "options": {
507
- "format": "plain",
508
- "onlyWithSnippets": true
509
- }
510
- }
511
- ```
512
-
513
- `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.
514
-
515
- ### Tool and skill policy
516
-
517
- Prompt stacks can constrain active tools and filter model-visible skills with stack-level `allow` or `deny` lists. Patterns are exact by default and support `*` wildcards.
518
-
519
- ```json
520
- {
521
- "tools": {
522
- "allow": ["read", "bash"]
523
- },
524
- "skills": {
525
- "deny": ["browser-danger"]
526
- }
527
- }
528
- ```
529
-
530
- For tools, use `allow` when only matching active tools should remain and `deny` when matching active tools should be removed. For skills, the same patterns control which skills remain visible in pi-forge's rendered `skills` slots. A single resource policy cannot contain both non-empty lists; mixed `allow` and `deny` entries are validation errors.
531
-
532
- Tool policy is enforced through Pi's active tool list while the stack is active. On startup and reload, pi-forge waits for other extensions to finish their `session_start` tool configuration before capturing the baseline and applying the stack policy. It reasserts the policy before user input and turns, and a tool-call guard blocks disallowed model tool execution even if another extension later calls `setActiveTools()`. External tool additions are preserved in the restorable baseline, which is restored when prompt stacks are disabled or switched to an unrestricted stack.
533
-
534
- Skill policy filters skills rendered by pi-forge's `skills` slot. It does not disable explicit skill invocation and is not a capability or security boundary. If a stack uses `mode: "append"` or `"prepend"`, Pi's base prompt may already contain unfiltered skills; use `mode: "replace"` when model-visible skill listings must be controlled.
535
-
536
- ### Regex transforms
537
-
538
- 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.
539
-
540
- ```json
541
- "regex": {
542
- "schemaVersion": 1,
543
- "rules": [
544
- {
545
- "id": "trim-ooc",
546
- "enabled": true,
547
- "stage": "history",
548
- "effect": "outgoing",
549
- "pattern": "\\(OOC:[^)]+\\)",
550
- "flags": "gi",
551
- "replace": "",
552
- "roles": ["assistant"],
553
- "maxMessages": 20
554
- }
555
- ]
556
- }
557
- ```
558
-
559
- 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`.
560
-
561
- To clean a completed assistant message after streaming, use `effect: "finalize"`:
562
-
563
- ```json
564
- {
565
- "id": "finalize-ooc",
566
- "enabled": true,
567
- "stage": "compiled",
568
- "effect": "finalize",
569
- "targets": ["messages"],
570
- "roles": ["assistant"],
571
- "pattern": "\\s*\\(OOC:[^)]+\\)",
572
- "flags": "gi",
573
- "replace": ""
574
- }
575
- ```
144
+ ### Develop and design
576
145
 
577
- 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.
578
-
579
- `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.
580
-
581
- 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.
582
-
583
- ### Variables slot options
584
-
585
- ```json
586
- {
587
- "kind": "slot",
588
- "id": "variables",
589
- "enabled": true,
590
- "role": "user",
591
- "slot": "variables",
592
- "options": {
593
- "includeStatic": true,
594
- "includeSession": true,
595
- "includeTurn": false,
596
- "format": "xml"
597
- }
598
- }
599
- ```
600
-
601
- ## Package setup for development
602
-
603
- ```bash
604
- git clone https://github.com/MacroSony/pi-forge.git
605
- cd pi-forge
606
- npm install
607
- npm run build
608
- # .pi/settings.json loads the package's built dist/index.js
609
- pi # start Pi, trust the project, /reload if needed
610
- ```
611
-
612
- The npm package intentionally omits physical `src/` files and loads compiled `dist/` output at runtime. To inspect or modify pi-forge itself, clone or fork the repository instead of editing `node_modules` or generated `dist/` files. A clone preserves your changes in Git and includes the development dependencies, tests, and source-to-dist consistency checks.
613
-
614
- For release-like local testing, register the cloned package directory. Its package manifest loads the tracked `dist/index.js`:
615
-
616
- ```json
617
- {
618
- "packages": ["../pi-forge"]
619
- }
620
- ```
621
-
622
- For live source development, remove that pi-forge package entry and load the TypeScript extension directly from `.pi/settings.json`:
623
-
624
- ```json
625
- {
626
- "extensions": ["../pi-forge/src/index.ts"]
627
- }
628
- ```
629
-
630
- You can also run `pi -e ../pi-forge/src/index.ts` for a one-off source-level smoke test. Do not load the package and source entry simultaneously or pi-forge will initialize twice. Browser-client source changes additionally require `npm run build:client` because the local editor serves its generated browser bundle.
631
-
632
- Run tests:
633
-
634
- ```bash
635
- npm test
636
- ```
637
-
638
- Run the real-browser editor smoke test (set `CHROME_PATH` if Chrome is not in a standard location):
639
-
640
- ```bash
641
- npm run test:browser
642
- ```
643
-
644
- Typecheck:
645
-
646
- ```bash
647
- npm run typecheck
648
- ```
649
-
650
- Build package output:
651
-
652
- ```bash
653
- npm run build
654
- ```
655
-
656
- Run the full repository verification, including a clean temporary build that checks tracked `dist/` byte-for-byte against `src/`:
657
-
658
- ```bash
659
- npm run verify
660
- ```
146
+ - [Development setup](docs/development/setup.md)
147
+ - [Release process](docs/development/release.md)
148
+ - [Roadmap](docs/development/roadmap.md)
149
+ - [Historical design archive](docs/design/README.md)
661
150
 
662
- The same verification runs in CI. When source changes affect generated output, run `npm run build` and commit the matching `dist/` changes with the source changes.
151
+ Chinese user documentation starts at [docs/zh-CN/README.md](docs/zh-CN/README.md).
663
152
 
664
153
  ## License
665
154
 
666
- MIT
155
+ [MIT](LICENSE)