Welcome to the final chapter of our tutorial series!
In External Editor Integration, we gave our CLI the power to open files and interact with your operating system. That is a very powerful feature. But with great power comes great responsibility.
If you are in "Creative Mode" just brainstorming ideas, you probably don't want your tool to accidentally delete files or execute dangerous terminal commands.
In this chapter, we will explore the Permission System.
Imagine a high-security research facility.
In the plan project, when we switch modes (e.g., entering plan mode), we automatically update the user's "Keycard" (the Permission Context) to ensure they only have access to the tools relevant to that task.
In Session State & Mode Management, we introduced the Global State. Inside that state lives a very specific object called toolPermissionContext.
This object is your ID badge. It tracks:
standard, plan, ask).
When the plan command runs, it first checks your ID badge.
// --- File: plan.tsx ---
const { getAppState } = context;
const appState = getAppState();
// Check the ID Badge
const currentMode = appState.toolPermissionContext.mode;
If the system detects you are not in plan mode, it needs to issue you a new badge.
We don't just manually hack the database. We use a helper function called prepareContextForPlanMode. Think of this as a "Template" or a "Preset" for permissions.
import { prepareContextForPlanMode } from '../../utils/permissions/permissionSetup.js';
// Get the current permissions
const currentContext = prev.toolPermissionContext;
// Calculate the new rules based on the 'plan' preset
const newContextRules = prepareContextForPlanMode(currentContext);
Now that we have prepared the new rules, we need to stamp them into the session state.
We use a secure function called applyPermissionUpdate. This ensures the state is updated correctly and logs the change.
import { applyPermissionUpdate } from '../../utils/permissions/PermissionUpdate.js';
// Create the instruction to change the mode
const updateInstruction = {
type: 'setMode',
mode: 'plan',
destination: 'session'
};
// Apply the update to get the final permission object
const finalPermissions = applyPermissionUpdate(
newContextRules,
updateInstruction
);
newContextRules: The rules we calculated in the previous step.destination: 'session': This means "Keep these rules active for the entire session" (until we switch modes again).
Let's look at the actual code block in plan.tsx where this transition happens. This runs when you type plan but aren't in plan mode yet.
// --- File: plan.tsx ---
setAppState(prev => ({
...prev,
// Update the permission context
toolPermissionContext: applyPermissionUpdate(
prepareContextForPlanMode(prev.toolPermissionContext),
{ type: 'setMode', mode: 'plan', destination: 'session' }
),
}));
It looks complex, but it is just a chain of events:
prev).prepare...).apply...).Let's visualize the security check when a user tries to change modes.
block: [exec], and rejects the action.You might wonder why we need all this code just to change a variable.
In simple scripts, you might just do mode = 'plan'. But in a complex AI tool:
prepareContextForPlanMode, every part of the app enforces the rules in exactly the same way.applyPermissionUpdate can verify valid transitions (e.g., preventing a user from jumping to 'admin' mode without proper checks).
Congratulations! You have completed the 5-chapter tutorial on the architecture of plan.
Let's recap what you have built:
You now understand the core pillars of building a robust, interactive, and safe CLI tool using modern TypeScript and React!
Happy Coding! ๐
Generated by Code IQ