agent-booster-pack-whiteboard 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.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Alastair Dawson
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,49 @@
1
+ # Agent Booster Pack Whiteboard
2
+
3
+ Pi runtime companion for ABP whiteboarding and workflow completion. It enforces one user-facing question at a time while whiteboarding mode is active, and it asks for a stronger final summary after implementation if the agent does not explain why the change is better and what it enables next.
4
+
5
+ ## Install
6
+
7
+ ```sh
8
+ pi install npm:agent-booster-pack-whiteboard
9
+ ```
10
+
11
+ Then in Pi:
12
+
13
+ ```text
14
+ /reload
15
+ ```
16
+
17
+ ## Usage
18
+
19
+ Start whiteboarding mode manually:
20
+
21
+ ```text
22
+ /abp:whiteboard design the import flow
23
+ ```
24
+
25
+ Or invoke the ABP whiteboarding skill; the guard activates automatically:
26
+
27
+ ```text
28
+ /skill:whiteboarding design the import flow
29
+ ```
30
+
31
+ Stop enforcement:
32
+
33
+ ```text
34
+ /abp:whiteboard-off
35
+ ```
36
+
37
+ While active, assistant responses that ask more than one user-facing question are blocked and the agent is prompted to regenerate with exactly one decision question. Other uncertainties should be written as notes, not questions.
38
+
39
+ After implementation work that uses Pi's file-writing tools, the guard also checks the final assistant response. If the final response does not state what changed, why it is better than what came before, and what it enables going forward, the guard queues a follow-up prompt asking the agent to produce that final value reflection. If the agent cannot justify the change as an improvement, it must say why and name alternatives such as revising, reducing, reverting, or choosing a different approach.
40
+
41
+ ## Test
42
+
43
+ ```sh
44
+ npm test
45
+ ```
46
+
47
+ ## License
48
+
49
+ MIT — see `LICENSE`.
@@ -0,0 +1,199 @@
1
+ // Enforces ABP's whiteboarding conversation shape in Pi.
2
+
3
+ export const WHITEBOARD_STATE_ENTRY = "abp-whiteboard-state";
4
+
5
+ const ACTIVATION_PATTERN = /^\s*(\/abp:whiteboard(?:\s|$)|\/skill:whiteboarding(?:\s|$))/i;
6
+ const DEACTIVATION_PATTERN = /^\s*\/abp:whiteboard-off\s*$/i;
7
+ const QUESTION_PATTERN = /[^?]+\?/g;
8
+ const IMPLEMENTATION_TOOL_NAMES = new Set(["edit", "write"]);
9
+ const FINAL_VALUE_GUARD_MARKER = "ABP Final Value Guard";
10
+
11
+ const CHANGE_PATTERN = /\b(changed|updated|added|implemented|fixed|removed|created|bumped|committed|now)\b/i;
12
+ const BETTER_PATTERN = /\b(better|improv\w*|because|prevents?|reduces?|avoids?|simpler|safer|clearer|reliable|replaces|compared|previous|before)\b/i;
13
+ const ENABLES_PATTERN = /\b(enables?|going forward|future|next|allows?|unblocks?|sets up|makes it possible|can now)\b/i;
14
+ const ALTERNATIVE_PATTERN = /\b(not an improvement|cannot justify|can't justify|alternative strateg\w*|revise|revert|reduce|different strategy|instead)\b/i;
15
+
16
+ function messageText(value) {
17
+ if (typeof value === "string") return value;
18
+ if (Array.isArray(value)) {
19
+ return value
20
+ .map((item) => (typeof item?.text === "string" ? item.text : ""))
21
+ .join("\n");
22
+ }
23
+ return "";
24
+ }
25
+
26
+ function latestWhiteboardState(entries) {
27
+ const stateEntry = entries
28
+ .filter((entry) => entry?.type === "custom" && entry.customType === WHITEBOARD_STATE_ENTRY)
29
+ .at(-1);
30
+
31
+ return stateEntry?.data?.active === true;
32
+ }
33
+
34
+ function stripNonConversationText(text) {
35
+ return String(text ?? "")
36
+ .replace(/```[\s\S]*?```/g, "")
37
+ .replace(/^>.*$/gm, "");
38
+ }
39
+
40
+ export function isWhiteboardingActivation(text) {
41
+ return ACTIVATION_PATTERN.test(String(text ?? "")) && !isWhiteboardingDeactivation(text);
42
+ }
43
+
44
+ export function isWhiteboardingDeactivation(text) {
45
+ return DEACTIVATION_PATTERN.test(String(text ?? ""));
46
+ }
47
+
48
+ export function countUserFacingQuestions(text) {
49
+ const conversationText = stripNonConversationText(text);
50
+ return conversationText.match(QUESTION_PATTERN)?.length ?? 0;
51
+ }
52
+
53
+ export function shouldEnforceAssistantMessage(entries, assistantText) {
54
+ if (!latestWhiteboardState(entries)) return false;
55
+ return countUserFacingQuestions(assistantText) > 1;
56
+ }
57
+
58
+ export function makeCorrectionPrompt(blockedText) {
59
+ return `ABP Whiteboarding Guard blocked your previous response because it asked multiple user-facing questions during whiteboarding.
60
+
61
+ Regenerate the response with exactly one decision question. Keep any other uncertainties as notes, not questions. Do not use a question list.
62
+
63
+ Blocked response:
64
+ ${blockedText}`;
65
+ }
66
+
67
+ function hasImplementationToolCall(message) {
68
+ if (message?.role !== "assistant" || !Array.isArray(message.content)) return false;
69
+ return message.content.some((item) => item?.type === "toolCall" && IMPLEMENTATION_TOOL_NAMES.has(item.name));
70
+ }
71
+
72
+ function lastAssistantText(messages) {
73
+ for (let index = messages.length - 1; index >= 0; index -= 1) {
74
+ const message = messages[index];
75
+ if (message?.role !== "assistant") continue;
76
+
77
+ const text = messageText(message.content).trim();
78
+ if (text) return text;
79
+ }
80
+ return "";
81
+ }
82
+
83
+ function hasFinalValueReflection(text) {
84
+ if (!CHANGE_PATTERN.test(text)) return false;
85
+ return (BETTER_PATTERN.test(text) && ENABLES_PATTERN.test(text)) || ALTERNATIVE_PATTERN.test(text);
86
+ }
87
+
88
+ function isFinalValueCorrectionTurn(messages) {
89
+ return messages.some((message) => message?.role === "user" && messageText(message.content).includes(FINAL_VALUE_GUARD_MARKER));
90
+ }
91
+
92
+ export function shouldRequestFinalValueReflection(messages) {
93
+ if (!Array.isArray(messages) || messages.length === 0) return false;
94
+ if (isFinalValueCorrectionTurn(messages)) return false;
95
+ if (!messages.some(hasImplementationToolCall)) return false;
96
+
97
+ return !hasFinalValueReflection(lastAssistantText(messages));
98
+ }
99
+
100
+ export function makeFinalValuePrompt(finalText) {
101
+ return `${FINAL_VALUE_GUARD_MARKER} requested a stronger final step.
102
+
103
+ Your previous final response did not explain the value of the implementation clearly enough. Respond with a concise final summary that states:
104
+
105
+ 1. what changed,
106
+ 2. why the change or new feature is better than what came before, and
107
+ 3. what it enables going forward.
108
+
109
+ If you cannot justify the change as an improvement, say so directly and explain why it may not be an improvement. Then name alternative strategies such as revising, reducing, reverting, or choosing a different approach.
110
+
111
+ Previous final response:
112
+ ${finalText}`;
113
+ }
114
+
115
+ function activeReminder() {
116
+ return `\n\nABP Whiteboarding Guard is active. During whiteboarding, ask at most one user-facing question per assistant turn. You may list uncertainties as notes, but do not phrase more than one item as a question. If several decisions are open, ask only the next smallest decision question that changes the design.`;
117
+ }
118
+
119
+ function stateMessage(active, source) {
120
+ return {
121
+ customType: WHITEBOARD_STATE_ENTRY,
122
+ content: active ? "ABP whiteboarding one-question mode enabled." : "ABP whiteboarding one-question mode disabled.",
123
+ display: false,
124
+ details: { active, source },
125
+ };
126
+ }
127
+
128
+ function replacementMessage(message, replacementText) {
129
+ return {
130
+ ...message,
131
+ content: [{ type: "text", text: replacementText }],
132
+ };
133
+ }
134
+
135
+ /** Register the Whiteboarding One-Question Guard extension with Pi. */
136
+ export default function whiteboardOneQuestion(pi) {
137
+ pi.registerCommand("abp:whiteboard", {
138
+ description: "Start ABP whiteboarding mode with one-question-at-a-time enforcement",
139
+ handler: async (args, ctx) => {
140
+ pi.appendEntry(WHITEBOARD_STATE_ENTRY, { active: true, source: "command" });
141
+ pi.sendMessage(stateMessage(true, "command"), { deliverAs: "nextTurn" });
142
+ ctx.ui.notify("ABP whiteboarding guard enabled", "info");
143
+
144
+ if (args?.trim()) await ctx.sendUserMessage(args.trim());
145
+ },
146
+ });
147
+
148
+ pi.registerCommand("abp:whiteboard-off", {
149
+ description: "Stop ABP whiteboarding one-question enforcement",
150
+ handler: async (_args, ctx) => {
151
+ pi.appendEntry(WHITEBOARD_STATE_ENTRY, { active: false, source: "command" });
152
+ pi.sendMessage(stateMessage(false, "command"), { deliverAs: "nextTurn" });
153
+ ctx.ui.notify("ABP whiteboarding guard disabled", "info");
154
+ },
155
+ });
156
+
157
+ pi.on("input", async (event, ctx) => {
158
+ if (isWhiteboardingDeactivation(event.text)) {
159
+ pi.appendEntry(WHITEBOARD_STATE_ENTRY, { active: false, source: "input" });
160
+ ctx.ui.notify("ABP whiteboarding guard disabled", "info");
161
+ return { action: "handled" };
162
+ }
163
+
164
+ if (isWhiteboardingActivation(event.text)) {
165
+ pi.appendEntry(WHITEBOARD_STATE_ENTRY, { active: true, source: "input" });
166
+ ctx.ui.notify("ABP whiteboarding guard enabled", "info");
167
+ }
168
+
169
+ return { action: "continue" };
170
+ });
171
+
172
+ pi.on("before_agent_start", async (event, ctx) => {
173
+ if (!latestWhiteboardState(ctx.sessionManager.getEntries())) return;
174
+ return { systemPrompt: event.systemPrompt + activeReminder() };
175
+ });
176
+
177
+ pi.on("message_end", async (event, ctx) => {
178
+ if (event.message.role !== "assistant") return;
179
+
180
+ const text = messageText(event.message.content);
181
+ if (!shouldEnforceAssistantMessage(ctx.sessionManager.getEntries(), text)) return;
182
+
183
+ const correction = makeCorrectionPrompt(text);
184
+ pi.sendUserMessage(correction, { deliverAs: "followUp" });
185
+
186
+ return {
187
+ message: replacementMessage(
188
+ event.message,
189
+ "ABP Whiteboarding Guard blocked a response that asked multiple questions. Regenerating with one decision question."
190
+ ),
191
+ };
192
+ });
193
+
194
+ pi.on("agent_end", async (event) => {
195
+ if (!shouldRequestFinalValueReflection(event.messages)) return;
196
+
197
+ pi.sendUserMessage(makeFinalValuePrompt(lastAssistantText(event.messages)), { deliverAs: "followUp" });
198
+ });
199
+ }
package/package.json ADDED
@@ -0,0 +1,41 @@
1
+ {
2
+ "name": "agent-booster-pack-whiteboard",
3
+ "version": "1.0.0",
4
+ "description": "ABP Pi conversation guard: one whiteboarding question at a time plus final value reflection after implementation.",
5
+ "type": "module",
6
+ "engines": {
7
+ "node": ">=18"
8
+ },
9
+ "pi": {
10
+ "extensions": [
11
+ "./extensions"
12
+ ]
13
+ },
14
+ "files": [
15
+ "extensions",
16
+ "README.md",
17
+ "LICENSE"
18
+ ],
19
+ "scripts": {
20
+ "test": "node --test test/*.test.js",
21
+ "install-ext": "mkdir -p ~/.pi/agent/extensions && ln -sfn \"$(pwd)\" ~/.pi/agent/extensions/agent-booster-pack-whiteboard && echo 'Installed to ~/.pi/agent/extensions/agent-booster-pack-whiteboard -- run /reload in Pi to activate'",
22
+ "uninstall-ext": "rm -f ~/.pi/agent/extensions/agent-booster-pack-whiteboard && echo 'Uninstalled'"
23
+ },
24
+ "repository": {
25
+ "type": "git",
26
+ "url": "git+https://github.com/kreek/agent-booster-pack.git",
27
+ "directory": "agent-booster-pack-whiteboard"
28
+ },
29
+ "author": "Alastair Dawson",
30
+ "license": "MIT",
31
+ "keywords": [
32
+ "pi-package",
33
+ "pi-extension",
34
+ "pi",
35
+ "abp",
36
+ "agent-booster-pack",
37
+ "whiteboarding",
38
+ "conversation-guard",
39
+ "coding-agent"
40
+ ]
41
+ }