mirror of
https://github.com/getcompanion-ai/co-mono.git
synced 2026-04-16 02:01:29 +00:00
WIP: Add hook API for dynamic tool control with plan-mode hook example
- Add pi.getTools() and pi.setTools(toolNames) to HookAPI - Hooks can now enable/disable tools dynamically - Changes take effect on next agent turn New example hook: plan-mode.ts - Claude Code-style read-only exploration mode - /plan command toggles plan mode on/off - Plan mode tools: read, bash, grep, find, ls - Edit/write tools disabled in plan mode - Injects context telling agent about restrictions - After each response, prompts to execute/stay/refine - State persists across sessions
This commit is contained in:
parent
5b95ccf830
commit
059292ead1
14 changed files with 304 additions and 8 deletions
|
|
@ -16,6 +16,7 @@ cp permission-gate.ts ~/.pi/agent/hooks/
|
|||
|
||||
| Hook | Description |
|
||||
|------|-------------|
|
||||
| `plan-mode.ts` | Claude Code-style plan mode for read-only exploration with `/plan` command |
|
||||
| `permission-gate.ts` | Prompts for confirmation before dangerous bash commands (rm -rf, sudo, etc.) |
|
||||
| `git-checkpoint.ts` | Creates git stash checkpoints at each turn for code restoration on branch |
|
||||
| `protected-paths.ts` | Blocks writes to protected paths (.env, .git/, node_modules/) |
|
||||
|
|
|
|||
119
packages/coding-agent/examples/hooks/plan-mode.ts
Normal file
119
packages/coding-agent/examples/hooks/plan-mode.ts
Normal file
|
|
@ -0,0 +1,119 @@
|
|||
/**
|
||||
* Plan Mode Hook
|
||||
*
|
||||
* Provides a Claude Code-style "plan mode" for safe code exploration.
|
||||
* When enabled, the agent can only use read-only tools and cannot modify files.
|
||||
*
|
||||
* Features:
|
||||
* - /plan command to toggle plan mode
|
||||
* - In plan mode: only read, bash (read-only), grep, find, ls are available
|
||||
* - Injects system context telling the agent about the restrictions
|
||||
* - After each agent response, prompts to execute the plan or continue planning
|
||||
*
|
||||
* Usage:
|
||||
* 1. Copy this file to ~/.pi/agent/hooks/ or your project's .pi/hooks/
|
||||
* 2. Use /plan to toggle plan mode on/off
|
||||
*/
|
||||
|
||||
import type { HookAPI } from "@mariozechner/pi-coding-agent/hooks";
|
||||
|
||||
// Read-only tools for plan mode
|
||||
const PLAN_MODE_TOOLS = ["read", "bash", "grep", "find", "ls"];
|
||||
|
||||
// Full set of tools for normal mode
|
||||
const NORMAL_MODE_TOOLS = ["read", "bash", "edit", "write"];
|
||||
|
||||
export default function planModeHook(pi: HookAPI) {
|
||||
// Track plan mode state
|
||||
let planModeEnabled = false;
|
||||
|
||||
// Register /plan command
|
||||
pi.registerCommand("plan", {
|
||||
description: "Toggle plan mode (read-only exploration)",
|
||||
handler: async (_args, ctx) => {
|
||||
planModeEnabled = !planModeEnabled;
|
||||
|
||||
if (planModeEnabled) {
|
||||
// Switch to read-only tools
|
||||
pi.setTools(PLAN_MODE_TOOLS);
|
||||
ctx.ui.notify(`Plan mode enabled. Tools: ${PLAN_MODE_TOOLS.join(", ")}`);
|
||||
} else {
|
||||
// Switch back to normal tools
|
||||
pi.setTools(NORMAL_MODE_TOOLS);
|
||||
ctx.ui.notify("Plan mode disabled. Full access restored.");
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
// Inject plan mode context at the start of each turn via before_agent_start
|
||||
pi.on("before_agent_start", async () => {
|
||||
if (!planModeEnabled) return;
|
||||
|
||||
// Return a message to inject into context
|
||||
return {
|
||||
message: {
|
||||
customType: "plan-mode-context",
|
||||
content: `[PLAN MODE ACTIVE]
|
||||
You are in plan mode - a read-only exploration mode for safe code analysis.
|
||||
|
||||
Restrictions:
|
||||
- You can only use: read, bash (read-only commands), grep, find, ls
|
||||
- You CANNOT use: edit, write (file modifications are disabled)
|
||||
- Focus on analysis, planning, and understanding the codebase
|
||||
|
||||
Your task is to explore, analyze, and create a detailed plan.
|
||||
Do NOT attempt to make changes - just describe what you would do.
|
||||
When you have a complete plan, I will switch to normal mode to execute it.`,
|
||||
display: false, // Don't show in TUI, just inject into context
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
// After agent finishes, offer to execute the plan
|
||||
pi.on("agent_end", async (_event, ctx) => {
|
||||
if (!planModeEnabled) return;
|
||||
if (!ctx.hasUI) return;
|
||||
|
||||
const choice = await ctx.ui.select("Plan mode - what next?", [
|
||||
"Execute the plan",
|
||||
"Stay in plan mode",
|
||||
"Refine the plan",
|
||||
]);
|
||||
|
||||
if (choice === "Execute the plan") {
|
||||
// Switch to normal mode
|
||||
planModeEnabled = false;
|
||||
pi.setTools(NORMAL_MODE_TOOLS);
|
||||
ctx.ui.notify("Switched to normal mode. Full access restored.");
|
||||
|
||||
// Set editor text to prompt execution
|
||||
ctx.ui.setEditorText("Execute the plan you just created. Proceed step by step.");
|
||||
} else if (choice === "Refine the plan") {
|
||||
const refinement = await ctx.ui.input("What should be refined?");
|
||||
if (refinement) {
|
||||
ctx.ui.setEditorText(refinement);
|
||||
}
|
||||
}
|
||||
// "Stay in plan mode" - do nothing, just continue
|
||||
});
|
||||
|
||||
// Persist plan mode state across sessions
|
||||
pi.on("session_start", async (_event, ctx) => {
|
||||
// Check if there's persisted plan mode state
|
||||
const entries = ctx.sessionManager.getEntries();
|
||||
const planModeEntry = entries
|
||||
.filter((e: { type: string; customType?: string }) => e.type === "custom" && e.customType === "plan-mode")
|
||||
.pop() as { data?: { enabled: boolean } } | undefined;
|
||||
|
||||
if (planModeEntry?.data?.enabled) {
|
||||
planModeEnabled = true;
|
||||
pi.setTools(PLAN_MODE_TOOLS);
|
||||
}
|
||||
});
|
||||
|
||||
// Save state when plan mode changes (via tool_call or other events)
|
||||
pi.on("turn_start", async () => {
|
||||
// Persist current state
|
||||
pi.appendEntry("plan-mode", { enabled: planModeEnabled });
|
||||
});
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue