Skip to content

Commit 72d404a

Browse files
author
jetnet
committed
feat: add /purpose toggle command to purpose-gate extension
Adds /purpose [on|off|set|<text>] command: - /purpose off — disable purpose injection, remove widget - /purpose on — re-enable with existing or prompt for new - /purpose set — prompt for a new purpose - /purpose <text> — set purpose directly from command When disabled, input is no longer blocked and system prompt injection is skipped. Addresses Issue disler#23.
1 parent 87617b0 commit 72d404a

1 file changed

Lines changed: 61 additions & 1 deletion

File tree

extensions/purpose-gate.ts

Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ function bold(s: string): string {
3333

3434
export default function (pi: ExtensionAPI) {
3535
let purpose: string | undefined;
36+
let purposeEnabled = true; // toggle state
3637

3738
async function askForPurpose(ctx: any) {
3839
while (!purpose) {
@@ -67,14 +68,73 @@ export default function (pi: ExtensionAPI) {
6768
void askForPurpose(ctx);
6869
});
6970

71+
pi.registerCommand("purpose", {
72+
description: "Toggle purpose gate on/off, or set a new purpose: /purpose [on|off|set]",
73+
handler: async (args, ctx) => {
74+
const cmd = (args || "").trim().toLowerCase();
75+
76+
if (cmd === "off") {
77+
purposeEnabled = false;
78+
ctx.ui.setWidget("purpose", undefined);
79+
ctx.ui.notify("Purpose gate disabled — prompts are no longer filtered.", "info");
80+
return;
81+
}
82+
83+
if (cmd === "on") {
84+
purposeEnabled = true;
85+
if (purpose) {
86+
// Re-show widget
87+
ctx.ui.setWidget("purpose", () => ({
88+
render(width: number): string[] {
89+
const pad = bg(" ".repeat(width));
90+
const label = pink(bold(" PURPOSE: "));
91+
const msg = cyan(bold(purpose!));
92+
const content = bg(truncateToWidth(label + msg + " ".repeat(width), width, ""));
93+
return [pad, content, pad];
94+
},
95+
invalidate() {},
96+
}));
97+
ctx.ui.notify(`Purpose gate enabled: ${purpose}`, "info");
98+
} else {
99+
void askForPurpose(ctx);
100+
}
101+
return;
102+
}
103+
104+
if (cmd === "set" || cmd === "") {
105+
const prev = purpose;
106+
purpose = undefined;
107+
purposeEnabled = true;
108+
void askForPurpose(ctx);
109+
return;
110+
}
111+
112+
// Treat anything else as the new purpose directly
113+
purpose = cmd;
114+
purposeEnabled = true;
115+
ctx.ui.setWidget("purpose", () => ({
116+
render(width: number): string[] {
117+
const pad = bg(" ".repeat(width));
118+
const label = pink(bold(" PURPOSE: "));
119+
const msg = cyan(bold(purpose!));
120+
const content = bg(truncateToWidth(label + msg + " ".repeat(width), width, ""));
121+
return [pad, content, pad];
122+
},
123+
invalidate() {},
124+
}));
125+
ctx.ui.notify(`Purpose set: ${purpose}`, "info");
126+
},
127+
});
128+
70129
pi.on("before_agent_start", async (event) => {
71-
if (!purpose) return;
130+
if (!purpose || !purposeEnabled) return;
72131
return {
73132
systemPrompt: event.systemPrompt + `\n\n<purpose>\nYour singular purpose this session: ${purpose}\nStay focused on this goal. If a request drifts from this purpose, gently remind the user.\n</purpose>`,
74133
};
75134
});
76135

77136
pi.on("input", async (_event, ctx) => {
137+
if (!purposeEnabled) return { action: "continue" as const };
78138
if (!purpose) {
79139
ctx.ui.notify("Set a purpose first.", "warning");
80140
return { action: "handled" as const };

0 commit comments

Comments
 (0)