@zihanw/pi-forge 0.1.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Zihan Wang
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,286 @@
1
+ # pi-forge
2
+
3
+ Pi extension package for prompt stack and agent profile management. This first milestone implements file-backed prompt stacks.
4
+
5
+ ## Package setup
6
+
7
+ This repository is a Pi package. Its package manifest exposes `./src/index.ts` as the extension entrypoint.
8
+
9
+ For local development in this checkout, `.pi/settings.json` points at the package root:
10
+
11
+ ```json
12
+ {
13
+ "packages": [".."]
14
+ }
15
+ ```
16
+
17
+ After starting Pi in this directory, trust the project and use `/reload` if needed.
18
+
19
+ After publishing under your chosen npm package name, install it with:
20
+
21
+ ```bash
22
+ pi install npm:@zihanw/pi-forge
23
+ ```
24
+
25
+ ## Prompt stacks
26
+
27
+ Prompt stacks live in:
28
+
29
+ ```txt
30
+ .pi/prompt-stacks/*.json
31
+ ```
32
+
33
+ Activation rules:
34
+
35
+ 1. A restored `/preset use <id>` selection is used first when that stack still exists and has no validation errors.
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.
41
+
42
+ When a stack is active, pi-forge replaces Pi's default system prompt by default and rebuilds the first provider request for each user message around a movable `chat-history` slot. Tool-result follow-up turns use Pi's natural context so post-history instructions are not repeatedly re-appended after every tool call.
43
+
44
+ ## Commands
45
+
46
+ ```txt
47
+ /preset list
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
55
+ ```
56
+
57
+ ## SillyTavern preset import
58
+
59
+ Import a SillyTavern preset JSON into `.pi/prompt-stacks/<id>.json` and write a migration report to `.pi/forge/import-reports/<id>.md`:
60
+
61
+ ```txt
62
+ /preset import-silly <path> [character_id]
63
+ ```
64
+
65
+ If a preset contains multiple `prompt_order` entries, pass the desired `character_id`. Imported stacks are created with `autoActivate: false`; activate one with `/preset use <id>` after reviewing the report.
66
+
67
+ ## Agent tools
68
+
69
+ ### forge_set_var
70
+
71
+ Sets a persistent session variable. Only variables starting with `agent.` can be written by the agent; other variables are read-only. Useful for cross-turn state tracking in roleplay (character mood, story progress) or coding (task checkpoints, discovered facts).
72
+
73
+ When the `variables` slot is active in a prompt stack, the agent sees its variable state and can update it. Without the slot, variables still work for macro substitution but the agent won't have a structured view of the state.
74
+
75
+ ## Stack format
76
+
77
+ Minimal example:
78
+
79
+ ```json
80
+ {
81
+ "schemaVersion": 1,
82
+ "type": "pi-forge.prompt-stack",
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
+ ]
122
+ }
123
+ ```
124
+
125
+ ## Items
126
+
127
+ ### Blocks
128
+
129
+ Static text:
130
+
131
+ ```json
132
+ {
133
+ "kind": "block",
134
+ "id": "post-history-nudge",
135
+ "enabled": true,
136
+ "role": "user",
137
+ "content": "Reason carefully about the latest request before answering."
138
+ }
139
+ ```
140
+
141
+ ### Slots
142
+
143
+ Live Pi runtime content:
144
+
145
+ ```json
146
+ {
147
+ "kind": "slot",
148
+ "id": "skills",
149
+ "enabled": true,
150
+ "role": "system",
151
+ "slot": "skills"
152
+ }
153
+ ```
154
+
155
+ Supported slots:
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:
190
+
191
+ ```json
192
+ {
193
+ "includeStatic": true,
194
+ "includeSession": true,
195
+ "includeTurn": true
196
+ }
197
+ ```
198
+
199
+ Use this slot to give the agent visibility into mutable state that it can also update via `forge_set_var`.
200
+
201
+ ## Roles
202
+
203
+ - `system` items are compiled into the replacement system prompt.
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.
207
+
208
+ `chat-history` expands to the live conversation at its position. By default, only the first enabled `chat-history` slot is expanded.
209
+
210
+ To omit the latest user message from a chat history slot, use:
211
+
212
+ ```json
213
+ {
214
+ "kind": "slot",
215
+ "id": "chat-history",
216
+ "enabled": true,
217
+ "slot": "chat-history",
218
+ "options": {
219
+ "includeLastUserMessage": false
220
+ }
221
+ }
222
+ ```
223
+
224
+ This is useful for SillyTavern-style stacks that re-insert `{{lastUserMessage}}` later as a post-history instruction.
225
+
226
+ To intentionally duplicate history:
227
+
228
+ ```json
229
+ {
230
+ "context": {
231
+ "allowDuplicateChatHistory": true
232
+ }
233
+ }
234
+ ```
235
+
236
+ ## Macros
237
+
238
+ Supported macros in block content:
239
+
240
+ - `{{cwd}}`
241
+ - `{{date}}`
242
+ - `{{lastUserMessage}}`
243
+ - `{{selectedTools}}` / `{{tools}}`
244
+ - `{{activeModel}}`
245
+ - custom variables from the stack `variables` object, e.g. `{{char}}`
246
+
247
+ ### Variables
248
+
249
+ Static variables come from the stack file:
250
+
251
+ ```json
252
+ "variables": {
253
+ "char": "Assistant",
254
+ "user": "USER"
255
+ }
256
+ ```
257
+
258
+ Mutable turn variables are cleared for each user message:
259
+
260
+ ```txt
261
+ {{setvar::name::value}}
262
+ {{setturnvar::name::value}}
263
+ {{getvar::name}}
264
+ {{getturnvar::name}}
265
+ {{var::name}}
266
+ {{clearvar::name}}
267
+ ```
268
+
269
+ Mutable session variables persist in the Pi session as extension state:
270
+
271
+ ```txt
272
+ {{setsessionvar::name::value}}
273
+ {{setvar::session::name::value}}
274
+ {{getsessionvar::name}}
275
+ {{getvar::name}}
276
+ {{clearsessionvar::name}}
277
+ {{clearvar::session::name}}
278
+ ```
279
+
280
+ Lookup order for `{{getvar::name}}`, `{{var::name}}`, and bare `{{name}}` is:
281
+
282
+ 1. turn variables
283
+ 2. session variables
284
+ 3. static stack variables
285
+
286
+ `setvar` macros output empty text. Unknown macros warn by default and are kept literally.
@@ -0,0 +1,117 @@
1
+ {
2
+ "schemaVersion": 1,
3
+ "type": "pi-forge.prompt-stack",
4
+ "id": "default",
5
+ "name": "Default pi-forge Prompt Stack",
6
+ "description": "A starter stack that replaces Pi's default system prompt while keeping tools, skills, project context, and chat history as movable slots.",
7
+ "autoActivate": true,
8
+ "mode": "replace",
9
+ "defaults": {
10
+ "syntheticMessagesVisible": false,
11
+ "unresolvedMacroPolicy": "warn"
12
+ },
13
+ "context": {
14
+ "allowDuplicateChatHistory": false
15
+ },
16
+ "variables": {
17
+ "user": "User",
18
+ "char": "Assistant"
19
+ },
20
+ "items": [
21
+ {
22
+ "kind": "block",
23
+ "id": "main-role",
24
+ "name": "Main Role",
25
+ "enabled": true,
26
+ "role": "system",
27
+ "content": "You are an expert coding assistant operating inside pi-forge, a prompt-stack controlled Pi extension. Help the user by reading files, executing commands, editing code, and writing new files. Follow the active prompt stack layout exactly."
28
+ },
29
+ {
30
+ "kind": "slot",
31
+ "id": "tools",
32
+ "name": "Available Tools",
33
+ "enabled": true,
34
+ "role": "system",
35
+ "slot": "tools"
36
+ },
37
+ {
38
+ "kind": "slot",
39
+ "id": "tool-guidelines",
40
+ "name": "Tool Guidelines",
41
+ "enabled": true,
42
+ "role": "system",
43
+ "slot": "tool-guidelines"
44
+ },
45
+ {
46
+ "kind": "slot",
47
+ "id": "project-context",
48
+ "name": "Project Context",
49
+ "enabled": true,
50
+ "role": "system",
51
+ "slot": "project-context"
52
+ },
53
+ {
54
+ "kind": "slot",
55
+ "id": "skills",
56
+ "name": "Available Skills",
57
+ "enabled": true,
58
+ "role": "system",
59
+ "slot": "skills"
60
+ },
61
+ {
62
+ "kind": "slot",
63
+ "id": "date-cwd",
64
+ "name": "Date and Working Directory",
65
+ "enabled": true,
66
+ "role": "system",
67
+ "slot": "date-cwd"
68
+ },
69
+ {
70
+ "kind": "block",
71
+ "id": "history-open",
72
+ "name": "Open Conversation Context Wrapper",
73
+ "enabled": true,
74
+ "role": "user",
75
+ "content": "<conversation_context>\nThe following is the current Pi conversation context. Treat it as authoritative conversation state, including user requests, assistant replies, tool calls, and tool results."
76
+ },
77
+ {
78
+ "kind": "slot",
79
+ "id": "chat-history",
80
+ "name": "Chat History",
81
+ "enabled": true,
82
+ "slot": "chat-history"
83
+ },
84
+ {
85
+ "kind": "block",
86
+ "id": "history-close",
87
+ "name": "Close Conversation Context Wrapper",
88
+ "enabled": true,
89
+ "role": "user",
90
+ "content": "</conversation_context>"
91
+ },
92
+ {
93
+ "kind": "block",
94
+ "id": "post-history-focus",
95
+ "name": "Post-History Focus Instruction",
96
+ "enabled": true,
97
+ "role": "user",
98
+ "content": "<current_turn_instructions>\nFocus on the latest user request. Use the wrapped conversation context only as context; do not repeat it. If tools are needed, use the available Pi tools according to their schemas.\n</current_turn_instructions>"
99
+ },
100
+ {
101
+ "kind": "slot",
102
+ "id": "append-system-prompt",
103
+ "name": "User Append System Prompt",
104
+ "enabled": true,
105
+ "role": "user",
106
+ "slot": "append-system-prompt"
107
+ },
108
+ {
109
+ "kind": "slot",
110
+ "id": "pi-docs",
111
+ "name": "Pi Docs Guidance",
112
+ "enabled": false,
113
+ "role": "system",
114
+ "slot": "pi-docs"
115
+ }
116
+ ]
117
+ }
@@ -0,0 +1,38 @@
1
+ {
2
+ "schemaVersion": 1,
3
+ "type": "pi-forge.prompt-stack",
4
+ "id": "validation-issues",
5
+ "name": "Validation Issues Sample",
6
+ "description": "Deliberately invalid sample for testing /preset validate validation-issues.",
7
+ "autoActivate": false,
8
+ "mode": "replace",
9
+ "items": [
10
+ {
11
+ "kind": "block",
12
+ "id": "duplicate-id",
13
+ "enabled": true,
14
+ "role": "system",
15
+ "content": "This block is valid, but its id is duplicated below."
16
+ },
17
+ {
18
+ "kind": "block",
19
+ "id": "duplicate-id",
20
+ "enabled": true,
21
+ "role": "user",
22
+ "content": "This block intentionally duplicates the previous item id."
23
+ },
24
+ {
25
+ "kind": "slot",
26
+ "id": "unsupported-slot",
27
+ "enabled": true,
28
+ "role": "user",
29
+ "slot": "not-a-real-slot"
30
+ },
31
+ {
32
+ "kind": "block",
33
+ "id": "missing-role",
34
+ "enabled": true,
35
+ "content": "This enabled non-system block has no role and should be ignored."
36
+ }
37
+ ]
38
+ }
package/package.json ADDED
@@ -0,0 +1,58 @@
1
+ {
2
+ "name": "@zihanw/pi-forge",
3
+ "version": "0.1.0",
4
+ "description": "Pi extension for prompt stack and agent profile management.",
5
+ "type": "module",
6
+ "keywords": [
7
+ "pi-package",
8
+ "pi-extension",
9
+ "prompt-stack"
10
+ ],
11
+ "license": "MIT",
12
+ "pi": {
13
+ "extensions": [
14
+ "./src/index.ts"
15
+ ]
16
+ },
17
+ "peerDependencies": {
18
+ "@earendil-works/pi-agent-core": "*",
19
+ "@earendil-works/pi-ai": "*",
20
+ "@earendil-works/pi-coding-agent": "*",
21
+ "typebox": "*"
22
+ },
23
+ "peerDependenciesMeta": {
24
+ "@earendil-works/pi-agent-core": {
25
+ "optional": true
26
+ },
27
+ "@earendil-works/pi-ai": {
28
+ "optional": true
29
+ },
30
+ "@earendil-works/pi-coding-agent": {
31
+ "optional": true
32
+ },
33
+ "typebox": {
34
+ "optional": true
35
+ }
36
+ },
37
+ "devDependencies": {
38
+ "@earendil-works/pi-agent-core": "*",
39
+ "@earendil-works/pi-ai": "*",
40
+ "@earendil-works/pi-coding-agent": "*",
41
+ "@types/node": "latest",
42
+ "typescript": "latest",
43
+ "typebox": "latest"
44
+ },
45
+ "scripts": {
46
+ "typecheck": "tsc --noEmit",
47
+ "test": "node --test tests/*.test.ts"
48
+ },
49
+ "files": [
50
+ "src/",
51
+ "examples/",
52
+ "README.md",
53
+ "LICENSE*"
54
+ ],
55
+ "publishConfig": {
56
+ "access": "public"
57
+ }
58
+ }