@harms-haus/pi-workflows 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,383 @@
1
+ # Template Variables Reference
2
+
3
+ ## Overview
4
+
5
+ Pi-workflows uses a `{varName}` template syntax throughout its configurable strings — phase instructions, role instructions, block reasons, completion messages, and more. Templates are resolved by `resolveTemplate()` in `src/config/templates.ts`.
6
+
7
+ **Resolution rules:**
8
+
9
+ | Rule | Behavior |
10
+ | -------------------- | ------------------------------------------------------------------- |
11
+ | Matching `{varName}` | Replaced with the corresponding value from the variables map |
12
+ | Unknown `{varName}` | **Left as-is** — the literal text `{varName}` remains in the output |
13
+ | No curly braces | Plain text passes through unchanged |
14
+
15
+ ```typescript
16
+ // Simplified from src/config/templates.ts
17
+ function resolveTemplate(template: string, vars: Record<string, string>): string {
18
+ return template.replace(/\{(\w+)\}/g, (_match, key: string) => {
19
+ return Object.hasOwn(vars, key) ? vars[key] : `{${key}}`;
20
+ });
21
+ }
22
+ ```
23
+
24
+ > **Where to use templates:** Any configurable string in a workflow definition can contain template variables — `initialMessage`, `roleInstruction`, `advanceReminder`, `blockReasonTemplate`, `completionMessage`, `notDoneReminder`, and each phase's `instructions` frontmatter body. See [Configuration Reference](configuration-reference.md) for where each field lives.
25
+
26
+ ---
27
+
28
+ ## Variable Availability by Context
29
+
30
+ Variables are resolved in different contexts with different sets available. The table below summarizes which variables exist in each context; detailed tables follow.
31
+
32
+ | Variable | `initialMessage` | Phase Instructions / Context Prompt | `blockReasonTemplate` | `completionMessage` | Cancel case | `notDoneReminder` |
33
+ | ---------------------- | :--------------: | :---------------------------------: | :-------------------: | :-----------------: | :---------: | :---------------: |
34
+ | `{workflowName}` | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
35
+ | `{workflowKey}` | ✅ | ✅ | — | — | — | ✅ |
36
+ | `{description}` | ✅ | ✅ | — | — | — | — |
37
+ | `{taskDescription}` | — | — | — | ✅ | ✅ | ✅ |
38
+ | `{taskId}` | — | ✅ | — | ✅ | ✅ | ✅ |
39
+ | `{phaseId}` | — | ✅ | — | — | — | — |
40
+ | `{phaseName}` | — | ✅ | ✅ | — | — | ✅ |
41
+ | `{previousPhaseName}` | — | ✅ | — | — | — | — |
42
+ | `{nextPhaseName}` | — | ✅ | — | — | — | — |
43
+ | `{blockedToolsList}` | — | ✅ | — | — | — | — |
44
+ | `{toolName}` | — | ✅ | ✅ | — | — | — |
45
+ | `{breadcrumbPath}` | — | ✅ | — | — | — | — |
46
+ | `{globalStepCount}` | — | ✅ | — | — | — | — |
47
+ | `{phaseEmoji}` | — | — | — | — | — | ✅ |
48
+ | `{phaseInstructions}` | — | — | — | — | — | ✅ |
49
+ | `{phaseCount}` | — | — | — | ✅ | ✅ | — |
50
+ | `{allowedTools}` | — | — | ✅ | — | — | — |
51
+ | `{firstPhaseId}` | ✅ | — | — | — | — | — |
52
+ | `{firstPhaseName}` | ✅ | — | — | — | — | — |
53
+ | `{firstPhaseEmoji}` | ✅ | — | — | — | — | — |
54
+ | `{firstPhaseProfiles}` | ✅ | — | — | — | — | — |
55
+
56
+ > **Note:** There is no separate `cancelledMessage` field on `WorkflowDefinition`. The cancellation path reuses `completionMessage` with `DEFAULT_CANCELLED_MESSAGE` as fallback. See [Cancelled Message](#cancelled-message) below.
57
+
58
+ ---
59
+
60
+ ## `initialMessage` Variables
61
+
62
+ Resolved once when the workflow starts (in `src/command.ts`). The `initialMessage` field is defined in `workflow.yaml`.
63
+
64
+ | Variable | Source | Example Value |
65
+ | ---------------------- | ------------------------------------------------------------- | --------------------------------- |
66
+ | `{workflowName}` | `definition.name` | `Refine, Plan, Implement, Review` |
67
+ | `{workflowKey}` | Definition map key | `rpir` |
68
+ | `{description}` | User's description argument (trimmed) | `Add login page` |
69
+ | `{firstPhaseId}` | First concrete phase's `id` | `refine` |
70
+ | `{firstPhaseName}` | First concrete phase's `name` | `Refine Requirements` |
71
+ | `{firstPhaseEmoji}` | First concrete phase's `emoji` | `🔍` |
72
+ | `{firstPhaseProfiles}` | First phase's `availableProfiles` joined by `, `, or `(none)` | `planner, researcher` |
73
+
74
+ **Example template:**
75
+
76
+ ```yaml
77
+ # workflow.yaml
78
+ initialMessage: |
79
+ Starting {workflowName} ({workflowKey}).
80
+ Task: {description}
81
+ First phase: {firstPhaseEmoji} {firstPhaseName} ({firstPhaseId})
82
+ Available profiles: {firstPhaseProfiles}
83
+ ```
84
+
85
+ **Resolved output:**
86
+
87
+ ```
88
+ Starting Refine, Plan, Implement, Review (rpir).
89
+ Task: Add login page
90
+ First phase: 🔍 Refine Requirements (refine)
91
+ Available profiles: planner, researcher
92
+ ```
93
+
94
+ > **Note:** If the first phase entry is a subworkflow reference, resolution drills into it to find the first concrete phase.
95
+
96
+ ---
97
+
98
+ ## Phase Instructions Variables
99
+
100
+ Available inside every phase's `instructions` body (the markdown content in each `.md` phase file). Also available in `roleInstruction` and `advanceReminder` — see [Context Prompt Variables](#context-prompt-variables) below.
101
+
102
+ Resolved per-turn in `buildContextPrompt()` (`src/prompts.ts`).
103
+
104
+ | Variable | Source | Example Value |
105
+ | --------------------- | ---------------------------------------------- | --------------------------------- |
106
+ | `{workflowName}` | Top-level workflow `name` | `Refine, Plan, Implement, Review` |
107
+ | `{workflowKey}` | `state.workflowKey` | `rpir` |
108
+ | `{description}` | User's original description | `Add login page` |
109
+ | `{taskId}` | Generated task ID | `wf-1747234567890-a3f2k1` |
110
+ | `{phaseId}` | Current phase `id` | `implement` |
111
+ | `{phaseName}` | Current phase `name` | `Implementation` |
112
+ | `{previousPhaseName}` | Previous phase's `name`, or `(start)` if first | `Planning` |
113
+ | `{nextPhaseName}` | Next phase's `name`, or `DONE` if last | `Review` |
114
+ | `{blockedToolsList}` | Blocked tools joined by `, `, or `(none)` | `edit, write` |
115
+ | `{toolName}` | Always `"workflow_step"` | `workflow_step` |
116
+ | `{breadcrumbPath}` | Breadcrumb trail joined by `>` | `RPIR > Implementation` |
117
+ | `{globalStepCount}` | Monotonically increasing step counter | `3` |
118
+
119
+ **Example phase instructions:**
120
+
121
+ ```markdown
122
+ ---
123
+ id: implement
124
+ name: Implementation
125
+ emoji: ⚙️
126
+ ---
127
+
128
+ You are in phase **{phaseName}** of **{workflowName}** (step {globalStepCount}).
129
+
130
+ Task: {description} (ID: {taskId})
131
+
132
+ The previous phase ({previousPhaseName}) produced a plan. Now implement it.
133
+ When done, use `{toolName}` to advance to {nextPhaseName}.
134
+
135
+ Blocked tools: {blockedToolsList}
136
+ ```
137
+
138
+ **Resolved output (inside context prompt):**
139
+
140
+ ```
141
+ You are in phase **Implementation** of **Refine, Plan, Implement, Review** (step 3).
142
+
143
+ Task: Add login page (ID: wf-1747234567890-a3f2k1)
144
+
145
+ The previous phase (Planning) produced a plan. Now implement it.
146
+ When done, use `workflow_step` to advance to Review.
147
+
148
+ Blocked tools: edit, write
149
+ ```
150
+
151
+ ---
152
+
153
+ ## Context Prompt Variables
154
+
155
+ The **context prompt** is injected as a hidden message before every agent turn via `buildContextPrompt()` in `src/prompts.ts`. It composes several template strings together using the **same variable map** as phase instructions.
156
+
157
+ The following configurable templates all receive the full phase instructions variable set:
158
+
159
+ | Template Field | Purpose |
160
+ | -------------------- | --------------------------------------------------------------- |
161
+ | `roleInstruction` | Prepended to every context injection — defines the agent's role |
162
+ | `advanceReminder` | Appended to every context injection — reminds agent to advance |
163
+ | Phase `instructions` | The main body of what the agent should do this phase |
164
+
165
+ All three are resolved with the identical variable map documented in [Phase Instructions Variables](#phase-instructions-variables) above.
166
+
167
+ ---
168
+
169
+ ## Block Reason Variables
170
+
171
+ Resolved when a tool call is blocked during a phase (in `src/hooks.ts`, `handleToolCall()`). Used by the `blockReasonTemplate` field.
172
+
173
+ | Variable | Source | Example Value |
174
+ | ---------------- | -------------------------------- | ------------------------------------------- |
175
+ | `{workflowName}` | `definition.name` | `Refine, Plan, Implement, Review` |
176
+ | `{phaseName}` | Current phase `name` | `Refine Requirements` |
177
+ | `{toolName}` | The tool name that was blocked | `edit` |
178
+ | `{allowedTools}` | Description of what _is_ allowed | `all except: edit, write` or `read, search` |
179
+
180
+ The `{allowedTools}` value depends on the tool restriction mode:
181
+
182
+ - **Blacklist mode:** `"all except: " + blockedTools.join(", ")` → e.g. `all except: edit, write`
183
+ - **Whitelist mode:** `whitelist.join(", ")` → e.g. `read, search`
184
+
185
+ > **Note:** `workflow_step` is always allowed regardless of blacklist or whitelist configuration, but it is **not** included in the `{allowedTools}` value for whitelist mode. It will never appear in the resolved template.
186
+
187
+ **Example:**
188
+
189
+ Template:
190
+
191
+ ```
192
+ [workflow] "{toolName}" is blocked during {phaseName}.
193
+ Allowed: {allowedTools}. Use workflow_step when done.
194
+ ```
195
+
196
+ Resolved (blacklist):
197
+
198
+ ```
199
+ [workflow] "edit" is blocked during Refine Requirements.
200
+ Allowed: all except: edit, write. Use workflow_step when done.
201
+ ```
202
+
203
+ ---
204
+
205
+ ## Completion & Reminder Variables
206
+
207
+ ### Completion Message (`completionMessage`)
208
+
209
+ Resolved when the workflow reaches the DONE state.
210
+
211
+ | Variable | Source | Example Value |
212
+ | ------------------- | -------------------------------------- | --------------------------------- |
213
+ | `{workflowName}` | `definition.name` | `Refine, Plan, Implement, Review` |
214
+ | `{taskDescription}` | User's original description | `Add login page` |
215
+ | `{taskId}` | Generated task ID | `wf-1747234567890-a3f2k1` |
216
+ | `{phaseCount}` | Total phases in the top-level workflow | `4` |
217
+
218
+ ### Cancelled Message
219
+
220
+ There is **no separate** `cancelledMessage` field on `WorkflowDefinition`. The cancellation path reuses the same `completionMessage` field, but with a different fallback.
221
+
222
+ **Two cancellation paths exist:**
223
+
224
+ 1. **`/cancel-workflow` command** (`src/command.ts`) — Sends a **hardcoded message** with no template resolution at all:
225
+
226
+ ```
227
+ ❌ **Workflow Cancelled**
228
+
229
+ **Task:** {taskDescription}
230
+ **Task ID:** {taskId}
231
+ ```
232
+
233
+ This command also unloads the workflow state immediately, so the `agent_end` hook sees null state and does nothing further.
234
+
235
+ 2. **`agent_end` hook** (`src/hooks.ts`) — When `state.cancelled === true` and `state.completionNotified === false`, resolves `definition.completionMessage` if set, otherwise falls back to `DEFAULT_CANCELLED_MESSAGE` (not `DEFAULT_COMPLETION_MESSAGE`). Uses the same variable set as the completion message: `{workflowName}`, `{taskDescription}`, `{taskId}`, `{phaseCount}`.
236
+
237
+ **Default cancelled message** (`DEFAULT_CANCELLED_MESSAGE` from `src/prompts.ts`):
238
+
239
+ ```
240
+ ❌ **{workflowName} Cancelled**
241
+
242
+ **Task:** {taskDescription}
243
+ **Task ID:** {taskId}
244
+ ```
245
+
246
+ ### Not-Done Reminder (`notDoneReminder`)
247
+
248
+ Resolved when the agent tries to stop while the workflow is still active. Forces the agent to continue.
249
+
250
+ | Variable | Source | Example Value |
251
+ | --------------------- | -------------------------------------- | --------------------------------- |
252
+ | `{workflowName}` | `definition.name` | `Refine, Plan, Implement, Review` |
253
+ | `{taskDescription}` | User's original description | `Add login page` |
254
+ | `{taskId}` | Generated task ID | `wf-1747234567890-a3f2k1` |
255
+ | `{workflowKey}` | `state.workflowKey` | `rpir` |
256
+ | `{phaseName}` | Current phase `name` | `Implementation` |
257
+ | `{phaseEmoji}` | Current phase `emoji` | `⚙️` |
258
+ | `{phaseInstructions}` | Current phase's full instructions text | _(the raw instructions string)_ |
259
+
260
+ ---
261
+
262
+ ## Default Templates
263
+
264
+ All configurable template fields have sensible defaults defined in `src/prompts.ts` and `src/hooks.ts`. If a field is omitted from the workflow definition, the corresponding default is used.
265
+
266
+ ### `DEFAULT_ROLE_INSTRUCTION`
267
+
268
+ Used when `roleInstruction` is not set in the workflow definition.
269
+
270
+ ```
271
+ You are the ORCHESTRATOR for this workflow. You must NOT use the edit or write tools directly.
272
+ All implementation work must be delegated to subagents via the delegate_to_subagents tool.
273
+ Follow the phase instructions precisely.
274
+ ```
275
+
276
+ ### `DEFAULT_ADVANCE_REMINDER`
277
+
278
+ Used when `advanceReminder` is not set.
279
+
280
+ ```
281
+ When you finish this phase, call the workflow_step tool with action='next' to advance to the next phase. If you need to restart the current scope from the beginning, use action='loop'.
282
+ ```
283
+
284
+ ### `DEFAULT_BLOCK_REASON`
285
+
286
+ Used when `blockReasonTemplate` is not set. Defined in `src/hooks.ts`.
287
+
288
+ ```
289
+ [workflow] The tool "{toolName}" is blocked during the {phaseName} phase.
290
+ Refer to the current phase instructions for allowed tools and approaches.
291
+ When finished, call workflow_step to advance to the next phase.
292
+ ```
293
+
294
+ ### `DEFAULT_COMPLETION_MESSAGE`
295
+
296
+ Used when `completionMessage` is not set.
297
+
298
+ ```
299
+ ✅ **{workflowName} Complete**
300
+
301
+ **Task:** {taskDescription}
302
+ **Task ID:** {taskId}
303
+ **Phases completed:** {phaseCount}
304
+ ```
305
+
306
+ ### `DEFAULT_NOT_DONE_REMINDER`
307
+
308
+ Used when `notDoneReminder` is not set.
309
+
310
+ ```
311
+ ⚠️ The {workflowName} is still active. Current phase: {phaseEmoji} {phaseName}.
312
+
313
+ You must NOT stop yet. The workflow requires you to complete the current phase
314
+ and call workflow_step to advance.
315
+
316
+ Current phase instructions:
317
+ {phaseInstructions}
318
+
319
+ Continue working on the current phase and call workflow_step when done.
320
+ ```
321
+
322
+ ### `DEFAULT_CANCELLED_MESSAGE`
323
+
324
+ Fallback for the cancellation path in `handleAgentEnd` when `completionMessage` is not set on the workflow definition. Note: the `/cancel-workflow` command sends its own hardcoded message and does not use this constant.
325
+
326
+ ```
327
+ ❌ **{workflowName} Cancelled**
328
+
329
+ **Task:** {taskDescription}
330
+ **Task ID:** {taskId}
331
+ ```
332
+
333
+ ---
334
+
335
+ ## Resolution Examples
336
+
337
+ ### Full lifecycle trace
338
+
339
+ Given a workflow with `name: "Code Review"`, `commandName: "review"`, and two phases (`gather` → `report`):
340
+
341
+ **1. `/workflow review Check auth module`** — `initialMessage` resolved:
342
+
343
+ ```
344
+ {workflowName} → Code Review
345
+ {workflowKey} → code-review
346
+ {description} → Check auth module
347
+ {firstPhaseId} → gather
348
+ {firstPhaseName} → Gather Context
349
+ {firstPhaseEmoji} → 📋
350
+ {firstPhaseProfiles} → (none)
351
+ ```
352
+
353
+ **2. Agent turn during `gather` phase** — phase `instructions` resolved:
354
+
355
+ ```
356
+ {workflowName} → Code Review
357
+ {description} → Check auth module
358
+ {taskId} → wf-1747234567890-b2c4d6
359
+ {phaseId} → gather
360
+ {phaseName} → Gather Context
361
+ {previousPhaseName} → (start)
362
+ {nextPhaseName} → Report Findings
363
+ {blockedToolsList} → (none)
364
+ {globalStepCount} → 0
365
+ ```
366
+
367
+ **3. Agent calls `edit` during a phase with `blacklist: [edit]`** — `blockReasonTemplate` resolved:
368
+
369
+ ```
370
+ {workflowName} → Code Review
371
+ {phaseName} → Gather Context
372
+ {toolName} → edit
373
+ {allowedTools} → all except: edit
374
+ ```
375
+
376
+ **4. Workflow completes** — `completionMessage` resolved:
377
+
378
+ ```
379
+ {workflowName} → Code Review
380
+ {taskDescription} → Check auth module
381
+ {taskId} → wf-1747234567890-b2c4d6
382
+ {phaseCount} → 2
383
+ ```