@bacnh85/pi-plan 0.1.3 → 0.1.5

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.
Files changed (3) hide show
  1. package/README.md +1 -1
  2. package/index.ts +13 -6
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -4,7 +4,7 @@ Pi extension that adds a lightweight plan mode inspired by Codex and Claude Code
4
4
 
5
5
  - Toggle plan mode with `/plan` or `Ctrl+Alt+P`.
6
6
  - Remembers separate thinking/reasoning levels for planning and normal execution across sessions based on the active mode when you change Pi's reasoning level.
7
- - Keeps planning read-only by disabling built-in `edit`/`write` and blocking destructive shell commands.
7
+ - Keeps planning read-only by limiting active tools to planning-safe tools and blocking destructive shell commands.
8
8
  - Provides a `write_plan` tool so the agent writes reviewable Markdown plans into `.agents/plans/` in the current workspace.
9
9
  - Provides an `ask_plan_question` tool so the agent can ask selection-style clarifying questions during planning, with an option for free-form user input.
10
10
  - Prompts after a plan is written so you can approve execution, approve only, keep planning, or refine with feedback.
package/index.ts CHANGED
@@ -11,8 +11,8 @@ const PLAN_TOOL = "write_plan";
11
11
  const PLAN_QUESTION_TOOL = "ask_plan_question";
12
12
  const PLAN_EXECUTE_COMMAND = "plan-execute";
13
13
  const PREFERENCES_FILE = path.join(os.homedir(), ".pi", "agent", "pi-plan", "preferences.json");
14
- const PLAN_MODE_DISABLED_TOOLS = new Set(["edit", "write"]);
15
14
  const DEFAULT_PLAN_TOOLS = ["read", "bash", "grep", "find", "ls", PLAN_TOOL, PLAN_QUESTION_TOOL];
15
+ const PLAN_ALLOWED_TOOLS = new Set(DEFAULT_PLAN_TOOLS);
16
16
  const THINKING_LEVELS = ["off", "minimal", "low", "medium", "high", "xhigh"] as const;
17
17
 
18
18
  type ThinkingLevel = (typeof THINKING_LEVELS)[number];
@@ -152,6 +152,13 @@ function isDestructiveBash(command: string): boolean {
152
152
  return DESTRUCTIVE_BASH_PATTERNS.some((pattern) => pattern.test(command));
153
153
  }
154
154
 
155
+ function isReadOnlyBash(command: string): boolean {
156
+ const trimmed = command.trim();
157
+ if (!trimmed) return true;
158
+ if (/[;&|`$(){}]/.test(trimmed) || /\b(python|python3|node|ruby|perl|php|sh|bash|zsh|fish)\b/i.test(trimmed)) return false;
159
+ return /^(git\s+(status|branch|rev-parse|diff|show|log|ls-files)\b|(?:rg|grep|find|fd|ls|pwd|cat|head|tail|sed|awk|wc|sort|uniq|cut)\b)/i.test(trimmed);
160
+ }
161
+
155
162
  export default function piPlanExtension(pi: ExtensionAPI): void {
156
163
  let planModeEnabled = false;
157
164
  let executionMode = false;
@@ -199,7 +206,7 @@ export default function piPlanExtension(pi: ExtensionAPI): void {
199
206
  function enablePlanTools(): void {
200
207
  const baseline = toolsBeforePlan ?? pi.getActiveTools();
201
208
  toolsBeforePlan = baseline;
202
- pi.setActiveTools(unique([...baseline.filter((tool) => !PLAN_MODE_DISABLED_TOOLS.has(tool)), ...DEFAULT_PLAN_TOOLS]));
209
+ pi.setActiveTools(unique([...baseline.filter((tool) => PLAN_ALLOWED_TOOLS.has(tool)), ...DEFAULT_PLAN_TOOLS]));
203
210
  }
204
211
 
205
212
  function restoreTools(): void {
@@ -484,12 +491,12 @@ export default function piPlanExtension(pi: ExtensionAPI): void {
484
491
 
485
492
  pi.on("tool_call", async (event) => {
486
493
  if (!planModeEnabled) return;
487
- if (event.toolName === "edit" || event.toolName === "write") {
488
- return { block: true, reason: `pi-plan: ${event.toolName} is disabled until the plan is approved. Use ${PLAN_TOOL} to write the plan file.` };
494
+ if (!PLAN_ALLOWED_TOOLS.has(event.toolName)) {
495
+ return { block: true, reason: `pi-plan: ${event.toolName} is disabled in read-only plan mode. Use ${PLAN_TOOL} to write the plan file.` };
489
496
  }
490
497
  if (!isToolCallEventType("bash", event)) return;
491
- if (isDestructiveBash(event.input.command)) {
492
- return { block: true, reason: `pi-plan: bash command blocked in plan mode because it may modify state.\nCommand: ${event.input.command}` };
498
+ if (isDestructiveBash(event.input.command) || !isReadOnlyBash(event.input.command)) {
499
+ return { block: true, reason: `pi-plan: bash command blocked in plan mode because only simple read-only inspection commands are allowed.\nCommand: ${event.input.command}` };
493
500
  }
494
501
  });
495
502
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bacnh85/pi-plan",
3
- "version": "0.1.3",
3
+ "version": "0.1.5",
4
4
  "description": "Pi extension that adds a plan mode with workspace markdown plans and thinking-level presets.",
5
5
  "license": "MIT",
6
6
  "publishConfig": {