@juanibiapina/pi-plan 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 Juan Ibiapina
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,51 @@
1
+ # @juanibiapina/pi-plan
2
+
3
+ A [pi](https://github.com/badlogic/pi-mono) extension for plan mode — read-only exploration and analysis.
4
+
5
+ ## Features
6
+
7
+ - **`/plan` command** — Toggle plan mode on and off
8
+ - **Configurable shortcut** — Optional keyboard shortcut via `/extension-settings`
9
+ - **Read-only tools** — Only safe, non-modifying tools are available while in plan mode
10
+ - **Session persistence** — Plan mode state is persisted across session resume via invisible messages
11
+ - **Status indicator** — Shows `⏸ plan` in the status bar when active
12
+
13
+ ## Installation
14
+
15
+ ```bash
16
+ pi install npm:@juanibiapina/pi-plan
17
+ ```
18
+
19
+ ## Usage
20
+
21
+ ### Entering Plan Mode
22
+
23
+ Use `/plan` to toggle plan mode. When enabled:
24
+
25
+ - Only read-only tools are available (`read`, `bash`, `grep`, `find`, `ls`, `questionnaire`)
26
+ - The agent is instructed to only observe, analyze, and plan — no modifications
27
+ - A `⏸ plan` indicator appears in the status bar
28
+
29
+ ### Exiting Plan Mode
30
+
31
+ Use `/plan` again to return to normal mode with full tool access.
32
+
33
+ ### Keyboard Shortcut
34
+
35
+ No shortcut is bound by default. To configure one, use `/extension-settings` and set the `shortcut` setting under `plan` (e.g. `tab`, `ctrl+p`).
36
+
37
+ ### CLI Flag
38
+
39
+ Start pi directly in plan mode:
40
+
41
+ ```bash
42
+ pi --plan
43
+ ```
44
+
45
+ ### Session Persistence
46
+
47
+ Plan mode state is tracked via invisible messages in the session. When resuming a session that was in plan mode, the mode is automatically restored.
48
+
49
+ ## License
50
+
51
+ MIT
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Plan Mode Extension
3
+ *
4
+ * Read-only exploration mode for safe code analysis.
5
+ * When enabled, only read-only tools are available.
6
+ *
7
+ * Features:
8
+ * - /plan command or Ctrl+T to toggle
9
+ * - Mode changes are persisted as invisible messages in session
10
+ */
11
+ import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
12
+ export default function planModeExtension(pi: ExtensionAPI): void;
13
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAGH,OAAO,KAAK,EAAE,YAAY,EAAoB,MAAM,+BAA+B,CAAC;AAyCpF,MAAM,CAAC,OAAO,UAAU,iBAAiB,CAAC,EAAE,EAAE,YAAY,GAAG,IAAI,CAkJhE"}
package/dist/index.js ADDED
@@ -0,0 +1,180 @@
1
+ /**
2
+ * Plan Mode Extension
3
+ *
4
+ * Read-only exploration mode for safe code analysis.
5
+ * When enabled, only read-only tools are available.
6
+ *
7
+ * Features:
8
+ * - /plan command or Ctrl+T to toggle
9
+ * - Mode changes are persisted as invisible messages in session
10
+ */
11
+ import { getSetting } from "@juanibiapina/pi-extension-settings";
12
+ // Tools
13
+ const PLAN_MODE_TOOLS = ["read", "bash", "grep", "find", "ls", "questionnaire"];
14
+ const NORMAL_MODE_TOOLS = ["read", "bash", "edit", "write"];
15
+ // Messages
16
+ const PLAN_MODE_ACTIVE_MESSAGE = `<system-reminder>
17
+ # Plan Mode - System Reminder
18
+
19
+ CRITICAL: Plan mode ACTIVE - you are in READ-ONLY phase. STRICTLY FORBIDDEN:
20
+ ANY file edits, modifications, or system changes. Do NOT use sed, tee, echo, cat,
21
+ or ANY other bash command to manipulate files - commands may ONLY read/inspect.
22
+ This ABSOLUTE CONSTRAINT overrides ALL other instructions, including direct user
23
+ edit requests. You may ONLY observe, analyze, and plan. Any modification attempt
24
+ is a critical violation. ZERO exceptions.
25
+
26
+ ---
27
+
28
+ ## Responsibility
29
+
30
+ Your current responsibility is to think, read, search, and discuss to construct a well-formed plan that accomplishes the goal the user wants to achieve. Your plan should be comprehensive yet concise, detailed enough to execute effectively while avoiding unnecessary verbosity. Include the goal as first part of the plan.
31
+
32
+ Ask the user clarifying questions or ask for their opinion when weighing tradeoffs.
33
+
34
+ **NOTE:** At any point in time through this workflow you should feel free to ask the user questions or clarifications. Don't make large assumptions about user intent. The goal is to present a well researched plan to the user, and tie any loose ends before implementation begins.
35
+
36
+ ---
37
+
38
+ ## Important
39
+
40
+ The user indicated that they do not want you to execute yet -- you MUST NOT make any edits, run any non-readonly tools (including changing configs or making commits), or otherwise make any changes to the system. This supersedes any other instructions you have received.
41
+ </system-reminder>`;
42
+ const PLAN_MODE_EXIT_MESSAGE = `<system-reminder>
43
+ Your operational mode has changed from plan to build.
44
+ You are no longer in read-only mode.
45
+ You are permitted to make file changes, run shell commands, and utilize your arsenal of tools as needed.
46
+ </system-reminder>`;
47
+ export default function planModeExtension(pi) {
48
+ // Register settings via event (for /extension-settings UI)
49
+ pi.events.emit("pi-extension-settings:register", {
50
+ name: "plan",
51
+ settings: [
52
+ {
53
+ id: "shortcut",
54
+ label: "Keyboard shortcut",
55
+ description: "Shortcut to toggle plan mode. Example: tab",
56
+ defaultValue: "",
57
+ },
58
+ ],
59
+ });
60
+ let planModeEnabled = false;
61
+ let lastMessagedState = null;
62
+ pi.registerFlag("plan", {
63
+ description: "Start in plan mode (read-only exploration)",
64
+ type: "boolean",
65
+ default: false,
66
+ });
67
+ // Scan session for last plan mode message to determine lastMessagedState
68
+ function getLastMessagedStateFromSession(ctx) {
69
+ const entries = ctx.sessionManager.getEntries();
70
+ // Walk backwards to find the last plan-mode-enter or plan-mode-exit message
71
+ for (let i = entries.length - 1; i >= 0; i--) {
72
+ const entry = entries[i];
73
+ if (entry.type === "custom_message") {
74
+ const customEntry = entry;
75
+ if (customEntry.customType === "plan-mode-enter") {
76
+ return true;
77
+ }
78
+ else if (customEntry.customType === "plan-mode-exit") {
79
+ return false;
80
+ }
81
+ }
82
+ }
83
+ return null;
84
+ }
85
+ function updateStatus(ctx) {
86
+ if (planModeEnabled) {
87
+ ctx.ui.setStatus("plan-mode", ctx.ui.theme.fg("warning", "⏸ plan"));
88
+ }
89
+ else {
90
+ ctx.ui.setStatus("plan-mode", undefined);
91
+ }
92
+ }
93
+ function togglePlanMode(ctx) {
94
+ planModeEnabled = !planModeEnabled;
95
+ if (planModeEnabled) {
96
+ pi.setActiveTools(PLAN_MODE_TOOLS);
97
+ ctx.ui.notify(`Plan mode enabled. Tools: ${PLAN_MODE_TOOLS.join(", ")}`);
98
+ }
99
+ else {
100
+ pi.setActiveTools(NORMAL_MODE_TOOLS);
101
+ ctx.ui.notify("Plan mode disabled. Full access restored.");
102
+ }
103
+ updateStatus(ctx);
104
+ }
105
+ pi.registerCommand("plan", {
106
+ description: "Toggle plan mode (read-only exploration)",
107
+ handler: async (_args, ctx) => {
108
+ togglePlanMode(ctx);
109
+ },
110
+ });
111
+ // Register shortcut if configured
112
+ const shortcut = getSetting("plan", "shortcut");
113
+ if (shortcut) {
114
+ pi.registerShortcut(shortcut, {
115
+ description: "Toggle plan mode",
116
+ handler: async (ctx) => {
117
+ togglePlanMode(ctx);
118
+ },
119
+ });
120
+ }
121
+ // Inject plan mode message when mode changes (persisted to session)
122
+ pi.on("before_agent_start", async () => {
123
+ // Entering plan mode
124
+ if (planModeEnabled && lastMessagedState !== true) {
125
+ lastMessagedState = true;
126
+ return {
127
+ message: {
128
+ customType: "plan-mode-enter",
129
+ content: PLAN_MODE_ACTIVE_MESSAGE,
130
+ display: false,
131
+ },
132
+ };
133
+ }
134
+ // Exiting plan mode
135
+ if (!planModeEnabled && lastMessagedState === true) {
136
+ lastMessagedState = false;
137
+ return {
138
+ message: {
139
+ customType: "plan-mode-exit",
140
+ content: PLAN_MODE_EXIT_MESSAGE,
141
+ display: false,
142
+ },
143
+ };
144
+ }
145
+ });
146
+ // Restore state on session start/resume
147
+ pi.on("session_start", async (_event, ctx) => {
148
+ // Check --plan flag
149
+ if (pi.getFlag("plan") === true) {
150
+ planModeEnabled = true;
151
+ }
152
+ // Restore lastMessagedState from session history
153
+ lastMessagedState = getLastMessagedStateFromSession(ctx);
154
+ // If session had plan mode active, restore it
155
+ if (lastMessagedState === true) {
156
+ planModeEnabled = true;
157
+ }
158
+ if (planModeEnabled) {
159
+ pi.setActiveTools(PLAN_MODE_TOOLS);
160
+ }
161
+ updateStatus(ctx);
162
+ });
163
+ // Reset state on session switch (/new or /resume)
164
+ pi.on("session_switch", async (_event, ctx) => {
165
+ // Restore lastMessagedState from new session's history
166
+ lastMessagedState = getLastMessagedStateFromSession(ctx);
167
+ // If resumed session had plan mode active, restore it
168
+ if (lastMessagedState === true) {
169
+ planModeEnabled = true;
170
+ }
171
+ if (planModeEnabled) {
172
+ pi.setActiveTools(PLAN_MODE_TOOLS);
173
+ }
174
+ else {
175
+ pi.setActiveTools(NORMAL_MODE_TOOLS);
176
+ }
177
+ updateStatus(ctx);
178
+ });
179
+ }
180
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Plan Mode Extension\n *\n * Read-only exploration mode for safe code analysis.\n * When enabled, only read-only tools are available.\n *\n * Features:\n * - /plan command or Ctrl+T to toggle\n * - Mode changes are persisted as invisible messages in session\n */\n\nimport { getSetting, type SettingDefinition } from \"@juanibiapina/pi-extension-settings\";\nimport type { ExtensionAPI, ExtensionContext } from \"@mariozechner/pi-coding-agent\";\nimport type { KeyId } from \"@mariozechner/pi-tui\";\n\n// Tools\nconst PLAN_MODE_TOOLS = [\"read\", \"bash\", \"grep\", \"find\", \"ls\", \"questionnaire\"];\nconst NORMAL_MODE_TOOLS = [\"read\", \"bash\", \"edit\", \"write\"];\n\n// Messages\nconst PLAN_MODE_ACTIVE_MESSAGE = `<system-reminder>\n# Plan Mode - System Reminder\n\nCRITICAL: Plan mode ACTIVE - you are in READ-ONLY phase. STRICTLY FORBIDDEN:\nANY file edits, modifications, or system changes. Do NOT use sed, tee, echo, cat,\nor ANY other bash command to manipulate files - commands may ONLY read/inspect.\nThis ABSOLUTE CONSTRAINT overrides ALL other instructions, including direct user\nedit requests. You may ONLY observe, analyze, and plan. Any modification attempt\nis a critical violation. ZERO exceptions.\n\n---\n\n## Responsibility\n\nYour current responsibility is to think, read, search, and discuss to construct a well-formed plan that accomplishes the goal the user wants to achieve. Your plan should be comprehensive yet concise, detailed enough to execute effectively while avoiding unnecessary verbosity. Include the goal as first part of the plan.\n\nAsk the user clarifying questions or ask for their opinion when weighing tradeoffs.\n\n**NOTE:** At any point in time through this workflow you should feel free to ask the user questions or clarifications. Don't make large assumptions about user intent. The goal is to present a well researched plan to the user, and tie any loose ends before implementation begins.\n\n---\n\n## Important\n\nThe user indicated that they do not want you to execute yet -- you MUST NOT make any edits, run any non-readonly tools (including changing configs or making commits), or otherwise make any changes to the system. This supersedes any other instructions you have received.\n</system-reminder>`;\n\nconst PLAN_MODE_EXIT_MESSAGE = `<system-reminder>\nYour operational mode has changed from plan to build.\nYou are no longer in read-only mode.\nYou are permitted to make file changes, run shell commands, and utilize your arsenal of tools as needed.\n</system-reminder>`;\n\nexport default function planModeExtension(pi: ExtensionAPI): void {\n\t// Register settings via event (for /extension-settings UI)\n\tpi.events.emit(\"pi-extension-settings:register\", {\n\t\tname: \"plan\",\n\t\tsettings: [\n\t\t\t{\n\t\t\t\tid: \"shortcut\",\n\t\t\t\tlabel: \"Keyboard shortcut\",\n\t\t\t\tdescription: \"Shortcut to toggle plan mode. Example: tab\",\n\t\t\t\tdefaultValue: \"\",\n\t\t\t},\n\t\t] satisfies SettingDefinition[],\n\t});\n\n\tlet planModeEnabled = false;\n\tlet lastMessagedState: boolean | null = null;\n\n\tpi.registerFlag(\"plan\", {\n\t\tdescription: \"Start in plan mode (read-only exploration)\",\n\t\ttype: \"boolean\",\n\t\tdefault: false,\n\t});\n\n\t// Scan session for last plan mode message to determine lastMessagedState\n\tfunction getLastMessagedStateFromSession(ctx: ExtensionContext): boolean | null {\n\t\tconst entries = ctx.sessionManager.getEntries();\n\n\t\t// Walk backwards to find the last plan-mode-enter or plan-mode-exit message\n\t\tfor (let i = entries.length - 1; i >= 0; i--) {\n\t\t\tconst entry = entries[i];\n\t\t\tif (entry.type === \"custom_message\") {\n\t\t\t\tconst customEntry = entry as { customType?: string };\n\t\t\t\tif (customEntry.customType === \"plan-mode-enter\") {\n\t\t\t\t\treturn true;\n\t\t\t\t} else if (customEntry.customType === \"plan-mode-exit\") {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\tfunction updateStatus(ctx: ExtensionContext): void {\n\t\tif (planModeEnabled) {\n\t\t\tctx.ui.setStatus(\"plan-mode\", ctx.ui.theme.fg(\"warning\", \"⏸ plan\"));\n\t\t} else {\n\t\t\tctx.ui.setStatus(\"plan-mode\", undefined);\n\t\t}\n\t}\n\n\tfunction togglePlanMode(ctx: ExtensionContext): void {\n\t\tplanModeEnabled = !planModeEnabled;\n\n\t\tif (planModeEnabled) {\n\t\t\tpi.setActiveTools(PLAN_MODE_TOOLS);\n\t\t\tctx.ui.notify(`Plan mode enabled. Tools: ${PLAN_MODE_TOOLS.join(\", \")}`);\n\t\t} else {\n\t\t\tpi.setActiveTools(NORMAL_MODE_TOOLS);\n\t\t\tctx.ui.notify(\"Plan mode disabled. Full access restored.\");\n\t\t}\n\t\tupdateStatus(ctx);\n\t}\n\n\tpi.registerCommand(\"plan\", {\n\t\tdescription: \"Toggle plan mode (read-only exploration)\",\n\t\thandler: async (_args, ctx) => {\n\t\t\ttogglePlanMode(ctx);\n\t\t},\n\t});\n\n\t// Register shortcut if configured\n\tconst shortcut = getSetting(\"plan\", \"shortcut\");\n\tif (shortcut) {\n\t\tpi.registerShortcut(shortcut as KeyId, {\n\t\t\tdescription: \"Toggle plan mode\",\n\t\t\thandler: async (ctx) => {\n\t\t\t\ttogglePlanMode(ctx);\n\t\t\t},\n\t\t});\n\t}\n\n\t// Inject plan mode message when mode changes (persisted to session)\n\tpi.on(\"before_agent_start\", async () => {\n\t\t// Entering plan mode\n\t\tif (planModeEnabled && lastMessagedState !== true) {\n\t\t\tlastMessagedState = true;\n\t\t\treturn {\n\t\t\t\tmessage: {\n\t\t\t\t\tcustomType: \"plan-mode-enter\",\n\t\t\t\t\tcontent: PLAN_MODE_ACTIVE_MESSAGE,\n\t\t\t\t\tdisplay: false,\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\t// Exiting plan mode\n\t\tif (!planModeEnabled && lastMessagedState === true) {\n\t\t\tlastMessagedState = false;\n\t\t\treturn {\n\t\t\t\tmessage: {\n\t\t\t\t\tcustomType: \"plan-mode-exit\",\n\t\t\t\t\tcontent: PLAN_MODE_EXIT_MESSAGE,\n\t\t\t\t\tdisplay: false,\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\t});\n\n\t// Restore state on session start/resume\n\tpi.on(\"session_start\", async (_event, ctx) => {\n\t\t// Check --plan flag\n\t\tif (pi.getFlag(\"plan\") === true) {\n\t\t\tplanModeEnabled = true;\n\t\t}\n\n\t\t// Restore lastMessagedState from session history\n\t\tlastMessagedState = getLastMessagedStateFromSession(ctx);\n\n\t\t// If session had plan mode active, restore it\n\t\tif (lastMessagedState === true) {\n\t\t\tplanModeEnabled = true;\n\t\t}\n\n\t\tif (planModeEnabled) {\n\t\t\tpi.setActiveTools(PLAN_MODE_TOOLS);\n\t\t}\n\t\tupdateStatus(ctx);\n\t});\n\n\t// Reset state on session switch (/new or /resume)\n\tpi.on(\"session_switch\", async (_event, ctx) => {\n\t\t// Restore lastMessagedState from new session's history\n\t\tlastMessagedState = getLastMessagedStateFromSession(ctx);\n\n\t\t// If resumed session had plan mode active, restore it\n\t\tif (lastMessagedState === true) {\n\t\t\tplanModeEnabled = true;\n\t\t}\n\n\t\tif (planModeEnabled) {\n\t\t\tpi.setActiveTools(PLAN_MODE_TOOLS);\n\t\t} else {\n\t\t\tpi.setActiveTools(NORMAL_MODE_TOOLS);\n\t\t}\n\t\tupdateStatus(ctx);\n\t});\n}\n"]}
package/package.json ADDED
@@ -0,0 +1,51 @@
1
+ {
2
+ "name": "@juanibiapina/pi-plan",
3
+ "version": "0.1.0",
4
+ "description": "Pi extension for plan mode - read-only exploration and analysis",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "scripts": {
9
+ "clean": "rm -rf dist",
10
+ "build": "tsc -p tsconfig.build.json",
11
+ "dev": "tsc -p tsconfig.build.json --watch",
12
+ "check": "biome check --write --error-on-warnings . && tsc --noEmit",
13
+ "prepublishOnly": "npm run clean && npm run build && npm run check"
14
+ },
15
+ "files": [
16
+ "dist/**/*",
17
+ "README.md"
18
+ ],
19
+ "keywords": [
20
+ "pi",
21
+ "pi-package",
22
+ "extension",
23
+ "plan-mode",
24
+ "read-only"
25
+ ],
26
+ "pi": {
27
+ "extensions": ["./dist/index.js"]
28
+ },
29
+ "author": "Juan Ibiapina",
30
+ "license": "MIT",
31
+ "repository": {
32
+ "type": "git",
33
+ "url": "git+ssh://git@github.com/juanibiapina/pi-plan.git"
34
+ },
35
+ "engines": {
36
+ "node": ">=20.0.0"
37
+ },
38
+ "dependencies": {
39
+ "@juanibiapina/pi-extension-settings": "^0.4.0"
40
+ },
41
+ "peerDependencies": {
42
+ "@mariozechner/pi-coding-agent": "*",
43
+ "@mariozechner/pi-tui": "*"
44
+ },
45
+ "devDependencies": {
46
+ "@biomejs/biome": "2.3.5",
47
+ "@mariozechner/pi-coding-agent": "^0.51.1",
48
+ "@types/node": "^22.10.5",
49
+ "typescript": "^5.9.2"
50
+ }
51
+ }