@archships/dim-plugin-plan-mode
Advanced tools
+77
-61
@@ -19,11 +19,2 @@ import { mkdir, readFile, rm, writeFile } from "node:fs/promises"; | ||
| ]); | ||
| const HARD_FORBIDDEN_SHELL_PATTERNS = [ | ||
| [/\r|\n/, "Plan mode only allows single-line shell commands."], | ||
| [/&&|\|\||;/, "Plan mode blocks chained shell commands."], | ||
| [/\$\(|`/, "Plan mode blocks command substitution."] | ||
| ]; | ||
| const STDERR_REDIRECT_RE = /\s+2>\s*(?:\/dev\/null|&1)/g; | ||
| const STDOUT_REDIRECT_RE = />/; | ||
| const HEREDOC_RE = /<</; | ||
| const PIPE_RE = /\|/; | ||
| const DEFAULT_ALLOWED_EXEC_PATTERNS = [ | ||
@@ -440,2 +431,23 @@ { | ||
| return { | ||
| inspector: { | ||
| getConfig: async () => ({ | ||
| id: normalized.id, | ||
| modeName: normalized.modeName, | ||
| description: normalized.description, | ||
| activationMetadataKey: normalized.activationMetadataKey, | ||
| allowedExecPatterns: normalized.allowedExecPatterns.map((rule) => ({ | ||
| pattern: rule.pattern, | ||
| action: rule.action | ||
| })), | ||
| extraVisibleTools: [...normalized.extraVisibleTools] | ||
| }), | ||
| getSessionState: async (sessionId) => { | ||
| const state = readStateEntry(await context.services.pluginState.get(sessionId)); | ||
| return { | ||
| active: state.active ?? false, | ||
| pendingActive: state.pendingActive ?? false, | ||
| draftPath: planDraftPath(hostDataDir, sessionId) | ||
| }; | ||
| } | ||
| }, | ||
| modes: [{ | ||
@@ -493,7 +505,7 @@ name: normalized.modeName, | ||
| if (toolName === "exec") { | ||
| const command = readStringArg(payload.toolCall.function.arguments.command); | ||
| const decision = classifyExecCommand(command, normalized.allowedExecPatterns); | ||
| const execInput = readExecToolInput(payload.toolCall.function.arguments); | ||
| const decision = classifyExecCommand(execInput, normalized.allowedExecPatterns); | ||
| if (decision.action === "allow") return payload; | ||
| if (decision.action === "ask") return askToolCall({ message: decision.reason ?? `Plan mode requires approval for exec command: ${command}` }); | ||
| return denyToolCall(decision.reason ?? `Plan mode blocked exec command: ${command}`); | ||
| if (decision.action === "ask") return askToolCall({ message: decision.reason ?? `Plan mode requires approval for exec command: ${formatExecInput(execInput)}` }); | ||
| return denyToolCall(decision.reason ?? `Plan mode blocked exec command: ${formatExecInput(execInput)}`); | ||
| } | ||
@@ -680,3 +692,3 @@ if (!createAllowedToolNameSet(normalized).has(toolName)) return denyToolCall(`Plan mode blocks tool ${toolName}. Disable plan mode before implementation.`); | ||
| "- `write`, `edit`, and `patch` style tools are blocked.", | ||
| "- `exec` is restricted to an allowlisted set of read-only inspection commands.", | ||
| "- `exec` only accepts argv arrays and is restricted to read-only inspection commands.", | ||
| "- Build, test, lint, install, and typecheck commands are blocked in plan mode.", | ||
@@ -730,46 +742,22 @@ `- The only writable artifact is the plan draft at: ${planDraftPath(hostDataDir, sessionId)}`, | ||
| } | ||
| function classifyExecCommand(command, rules) { | ||
| const normalized = normalizeCommand(command); | ||
| if (!normalized) return { | ||
| function classifyExecCommand(input, rules) { | ||
| if (input.action === "poll" || input.action === "terminate") return { action: "allow" }; | ||
| const command = normalizeExecPatternInput(input.command); | ||
| if (!command) return { | ||
| action: "deny", | ||
| reason: "Plan mode requires a non-empty exec command." | ||
| reason: "Plan mode requires exec command to be a non-empty string array." | ||
| }; | ||
| for (const [pattern, reason] of HARD_FORBIDDEN_SHELL_PATTERNS) if (pattern.test(normalized)) return { | ||
| for (const deniedPrefix of HARD_DENIED_COMMAND_PREFIXES) if (matchesPrefix(command, deniedPrefix)) return { | ||
| action: "deny", | ||
| reason | ||
| reason: `Plan mode blocks exec command: ${command}` | ||
| }; | ||
| const stripped = normalizeCommand(normalized.replace(STDERR_REDIRECT_RE, "")); | ||
| if (!stripped) return { | ||
| action: "deny", | ||
| reason: "Plan mode requires a non-empty exec command." | ||
| }; | ||
| if (HEREDOC_RE.test(stripped)) return { | ||
| action: "deny", | ||
| reason: "Plan mode blocks shell redirection." | ||
| }; | ||
| if (STDOUT_REDIRECT_RE.test(stripped)) return { | ||
| action: "deny", | ||
| reason: "Plan mode blocks shell redirection." | ||
| }; | ||
| if (PIPE_RE.test(stripped)) { | ||
| const segments = stripped.split("|").map((s) => s.trim()).filter(Boolean); | ||
| for (const segment of segments) if (!isSegmentAllowed(segment, rules)) return { | ||
| action: "deny", | ||
| reason: `Plan mode blocked exec command: ${segment}` | ||
| }; | ||
| return { action: "allow" }; | ||
| } | ||
| for (const deniedPrefix of HARD_DENIED_COMMAND_PREFIXES) if (matchesPrefix(stripped, deniedPrefix)) return { | ||
| action: "deny", | ||
| reason: `Plan mode blocks exec command: ${stripped}` | ||
| }; | ||
| let decision = { | ||
| action: "deny", | ||
| reason: `Plan mode blocked exec command: ${stripped}` | ||
| reason: `Plan mode blocked exec command: ${command}` | ||
| }; | ||
| for (const rule of rules) { | ||
| if (!matchesCommandPattern(stripped, rule.pattern)) continue; | ||
| if (!matchesCommandPattern(command, rule.pattern)) continue; | ||
| decision = { | ||
| action: rule.action, | ||
| reason: rule.action === "allow" ? void 0 : rule.action === "ask" ? `Plan mode requires approval for exec command: ${stripped}` : `Plan mode blocked exec command: ${stripped}` | ||
| reason: rule.action === "allow" ? void 0 : rule.action === "ask" ? `Plan mode requires approval for exec command: ${command}` : `Plan mode blocked exec command: ${command}` | ||
| }; | ||
@@ -779,12 +767,2 @@ } | ||
| } | ||
| function isSegmentAllowed(segment, rules) { | ||
| const trimmed = segment.trim(); | ||
| if (!trimmed) return false; | ||
| for (const deniedPrefix of HARD_DENIED_COMMAND_PREFIXES) if (matchesPrefix(trimmed, deniedPrefix)) return false; | ||
| for (const rule of rules) if (matchesCommandPattern(trimmed, rule.pattern) && rule.action === "allow") return true; | ||
| return false; | ||
| } | ||
| function normalizeCommand(command) { | ||
| return command.trim().replace(/\s+/g, " "); | ||
| } | ||
| function matchesPrefix(command, prefix) { | ||
@@ -797,2 +775,43 @@ return command === prefix.trim() || command.startsWith(`${prefix.trim()} `); | ||
| } | ||
| function readExecToolInput(args) { | ||
| const action = readExecAction(args.action, args); | ||
| const command = readExecCommand(args.command); | ||
| if (action === "start") { | ||
| if (!command) throw new Error("Plan mode requires exec command to be a non-empty string array."); | ||
| return { | ||
| action, | ||
| command | ||
| }; | ||
| } | ||
| const processId = args.processId; | ||
| if (typeof processId !== "number" || !Number.isInteger(processId) || processId <= 0) throw new Error(`Plan mode requires exec ${action} processId to be a positive integer.`); | ||
| return { | ||
| action, | ||
| processId | ||
| }; | ||
| } | ||
| function readExecAction(value, args) { | ||
| if (value === void 0) { | ||
| if ("command" in args && !("processId" in args)) return "start"; | ||
| if ("processId" in args && !("command" in args)) return "poll"; | ||
| throw new Error("Plan mode requires exec to include either command or processId."); | ||
| } | ||
| if (value === "start" || value === "poll" || value === "terminate") return value; | ||
| throw new Error("Plan mode only allows exec action to be start, poll, or terminate."); | ||
| } | ||
| function readExecCommand(value) { | ||
| if (value === void 0) return void 0; | ||
| if (!Array.isArray(value) || value.length === 0) throw new Error("Plan mode requires exec command to be a non-empty string array."); | ||
| if (value.some((entry) => typeof entry !== "string")) throw new Error("Plan mode requires every exec command entry to be a string."); | ||
| if (typeof value[0] === "string" && value[0].trim().length === 0) throw new Error("Plan mode requires exec command[0] to be a non-empty string."); | ||
| return [...value]; | ||
| } | ||
| function normalizeExecPatternInput(command) { | ||
| if (!command || command.length === 0) return ""; | ||
| return command.map((part) => part.trim()).join(" ").trim().replace(/\s+/g, " "); | ||
| } | ||
| function formatExecInput(input) { | ||
| if (input.action === "start") return normalizeExecPatternInput(input.command); | ||
| return `${input.action} process ${input.processId ?? "unknown"}`; | ||
| } | ||
| function draftDirectory(hostDataDir, sessionId) { | ||
@@ -856,5 +875,2 @@ return path.join(path.resolve(hostDataDir), "plans", encodeURIComponent(sessionId)); | ||
| } | ||
| function readStringArg(value) { | ||
| return typeof value === "string" ? value : ""; | ||
| } | ||
| function isRecord(value) { | ||
@@ -861,0 +877,0 @@ return typeof value === "object" && value !== null && !Array.isArray(value); |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"index.js","names":[],"sources":["../src/index.ts"],"sourcesContent":["import { mkdir, readFile, rm, writeFile } from 'node:fs/promises'\nimport path from 'node:path'\nimport { BaseTool, normalizeToolResult } from '@archships/dim-agent-sdk'\nimport { askToolCall, denyToolCall } from '@archships/dim-plugin-api'\nimport type {\n CallToolResult,\n DimPlugin,\n HookHandlerInput,\n PluginRuntimeContext,\n PluginSessionControllerContext,\n PluginSessionStateEntry,\n PromptResolvePayload,\n RunStartPayload,\n ToolBeforeExecutePayload,\n ToolsetResolvePayload,\n ToolExecutionContext,\n} from '@archships/dim-plugin-api'\n\nexport interface PlanModeCommandRule {\n pattern: string\n action: 'allow' | 'deny' | 'ask'\n}\n\nexport interface PlanModePluginOptions {\n id?: string\n modeName?: string\n description?: string\n activationMetadataKey?: string\n allowedExecPatterns?: PlanModeCommandRule[]\n extraVisibleTools?: string[]\n}\n\nexport interface PlanModeStatus {\n active: boolean\n pendingActive?: boolean\n draftPath: string\n}\n\nexport interface PlanModeSessionController {\n getState(): Promise<PlanModeStatus>\n enable(): Promise<PlanModeStatus>\n disable(): Promise<PlanModeStatus>\n clearDraft(): Promise<PlanModeStatus>\n}\n\ninterface NormalizedPlanModePluginOptions<TId extends string = string> {\n id: TId\n modeName: string\n description: string\n activationMetadataKey: string\n allowedExecPatterns: PlanModeCommandRule[]\n extraVisibleTools: string[]\n}\n\ninterface PlanModeStateData {\n active?: boolean\n pendingActive?: boolean\n}\n\ninterface DraftMetadata {\n sessionId: string\n mode: 'plan'\n createdAt: number\n updatedAt: number\n}\n\ninterface CommandDecision {\n action: 'allow' | 'deny' | 'ask'\n reason?: string\n}\n\nconst PLAN_READ_TOOL_NAME = 'plan_read'\nconst PLAN_WRITE_TOOL_NAME = 'plan_write'\nconst DEFAULT_VISIBLE_TOOL_NAMES = ['read', 'grep', 'glob', 'exec']\nconst IMMUTABLY_BLOCKED_TOOL_NAMES = new Set(['write', 'edit', 'patch'])\nconst HARD_FORBIDDEN_SHELL_PATTERNS: Array<[RegExp, string]> = [\n [/\\r|\\n/, 'Plan mode only allows single-line shell commands.'],\n [/&&|\\|\\||;/, 'Plan mode blocks chained shell commands.'],\n [/\\$\\(|`/, 'Plan mode blocks command substitution.'],\n]\nconst STDERR_REDIRECT_RE = /\\s+2>\\s*(?:\\/dev\\/null|&1)/g\nconst STDOUT_REDIRECT_RE = />/\nconst HEREDOC_RE = /<</\nconst PIPE_RE = /\\|/\n\nconst DEFAULT_ALLOWED_EXEC_PATTERNS: PlanModeCommandRule[] = [\n { pattern: 'pwd', action: 'allow' },\n { pattern: 'ls', action: 'allow' },\n { pattern: 'ls *', action: 'allow' },\n { pattern: 'find *', action: 'allow' },\n { pattern: 'cat *', action: 'allow' },\n { pattern: 'head *', action: 'allow' },\n { pattern: 'tail *', action: 'allow' },\n { pattern: 'wc *', action: 'allow' },\n { pattern: 'stat *', action: 'allow' },\n { pattern: 'file *', action: 'allow' },\n { pattern: 'sed -n *', action: 'allow' },\n { pattern: 'rg *', action: 'allow' },\n { pattern: 'grep *', action: 'allow' },\n { pattern: 'fd *', action: 'allow' },\n { pattern: 'git status', action: 'allow' },\n { pattern: 'git status *', action: 'allow' },\n { pattern: 'git diff', action: 'allow' },\n { pattern: 'git diff *', action: 'allow' },\n { pattern: 'git show *', action: 'allow' },\n { pattern: 'git log', action: 'allow' },\n { pattern: 'git log *', action: 'allow' },\n { pattern: 'git branch', action: 'allow' },\n { pattern: 'git branch *', action: 'allow' },\n { pattern: 'git rev-parse *', action: 'allow' },\n { pattern: 'git ls-files', action: 'allow' },\n { pattern: 'git ls-files *', action: 'allow' },\n { pattern: 'git grep *', action: 'allow' },\n // Purely read-only git subcommands\n { pattern: 'git blame *', action: 'allow' },\n { pattern: 'git shortlog', action: 'allow' },\n { pattern: 'git shortlog *', action: 'allow' },\n { pattern: 'git describe', action: 'allow' },\n { pattern: 'git describe *', action: 'allow' },\n { pattern: 'git cat-file *', action: 'allow' },\n { pattern: 'git ls-tree *', action: 'allow' },\n { pattern: 'git show-ref', action: 'allow' },\n { pattern: 'git show-ref *', action: 'allow' },\n { pattern: 'git ls-remote', action: 'allow' },\n { pattern: 'git ls-remote *', action: 'allow' },\n { pattern: 'git for-each-ref', action: 'allow' },\n { pattern: 'git for-each-ref *', action: 'allow' },\n { pattern: 'git rev-list *', action: 'allow' },\n { pattern: 'git merge-base *', action: 'allow' },\n { pattern: 'git diff-tree *', action: 'allow' },\n { pattern: 'git cherry', action: 'allow' },\n { pattern: 'git cherry *', action: 'allow' },\n { pattern: 'git name-rev *', action: 'allow' },\n { pattern: 'git check-ignore *', action: 'allow' },\n // Git subcommands with write modes — only safe forms allowed\n { pattern: 'git tag', action: 'allow' },\n { pattern: 'git tag -l', action: 'allow' },\n { pattern: 'git tag -l *', action: 'allow' },\n { pattern: 'git tag --list', action: 'allow' },\n { pattern: 'git tag --list *', action: 'allow' },\n { pattern: 'git remote', action: 'allow' },\n { pattern: 'git remote -v', action: 'allow' },\n { pattern: 'git remote show *', action: 'allow' },\n { pattern: 'git remote get-url *', action: 'allow' },\n { pattern: 'git reflog', action: 'allow' },\n { pattern: 'git reflog show *', action: 'allow' },\n { pattern: 'git stash list', action: 'allow' },\n { pattern: 'git stash list *', action: 'allow' },\n { pattern: 'git stash show *', action: 'allow' },\n { pattern: 'git config --get *', action: 'allow' },\n { pattern: 'git config --get-regexp *', action: 'allow' },\n { pattern: 'git config --list', action: 'allow' },\n { pattern: 'git config -l', action: 'allow' },\n { pattern: 'head', action: 'allow' },\n { pattern: 'tail', action: 'allow' },\n { pattern: 'wc', action: 'allow' },\n { pattern: 'cat', action: 'allow' },\n { pattern: 'sort', action: 'allow' },\n { pattern: 'sort *', action: 'allow' },\n { pattern: 'uniq', action: 'allow' },\n { pattern: 'uniq *', action: 'allow' },\n { pattern: 'cut *', action: 'allow' },\n { pattern: 'tr *', action: 'allow' },\n { pattern: 'awk *', action: 'allow' },\n { pattern: 'jq', action: 'allow' },\n { pattern: 'jq *', action: 'allow' },\n { pattern: 'yq', action: 'allow' },\n { pattern: 'yq *', action: 'allow' },\n { pattern: 'xargs grep *', action: 'allow' },\n { pattern: 'xargs rg *', action: 'allow' },\n { pattern: 'du *', action: 'allow' },\n { pattern: 'df', action: 'allow' },\n { pattern: 'df *', action: 'allow' },\n { pattern: 'tree', action: 'allow' },\n { pattern: 'tree *', action: 'allow' },\n { pattern: 'which *', action: 'allow' },\n { pattern: 'echo', action: 'allow' },\n { pattern: 'echo *', action: 'allow' },\n { pattern: 'realpath *', action: 'allow' },\n { pattern: 'basename *', action: 'allow' },\n { pattern: 'dirname *', action: 'allow' },\n]\n\nconst HARD_DENIED_COMMAND_PREFIXES = [\n 'touch',\n 'mkdir',\n 'rm',\n 'mv',\n 'cp',\n 'ln',\n 'chmod',\n 'chown',\n 'git add',\n 'git commit',\n 'git push',\n 'git checkout',\n 'git switch',\n 'git restore',\n 'git apply',\n 'npm ',\n 'pnpm ',\n 'yarn ',\n 'bun ',\n]\n\nexport function createPlanModePlugin(): DimPlugin<'plan-mode', PlanModeSessionController>\nexport function createPlanModePlugin(\n options: PlanModePluginOptions,\n): DimPlugin<'plan-mode', PlanModeSessionController>\nexport function createPlanModePlugin<TId extends string>(\n options: PlanModePluginOptions & { id: TId },\n): DimPlugin<TId, PlanModeSessionController>\nexport function createPlanModePlugin<TId extends string>(\n options: PlanModePluginOptions & { id?: TId } = {},\n): DimPlugin<TId, PlanModeSessionController> {\n const normalized = normalizeOptions(options)\n\n return {\n manifest: {\n id: normalized.id,\n version: '1.0.0',\n apiVersion: 1,\n permissions: { fs: true },\n capabilities: ['plan-mode'],\n },\n setup(context) {\n const hostDataDir = context.hostDataDir\n if (!hostDataDir)\n throw new Error(\n 'createPlanModePlugin requires createAgent({ hostDataDir }) so plan drafts can be stored outside the workspace',\n )\n\n const planReadTool = new PlanReadTool(hostDataDir, normalized.activationMetadataKey)\n const planWriteTool = new PlanWriteTool(hostDataDir, normalized.activationMetadataKey)\n\n return {\n modes: [\n {\n name: normalized.modeName,\n description: normalized.description,\n },\n ],\n tools: [planReadTool, planWriteTool],\n createSessionController: (controllerContext) =>\n new DefaultPlanModeSessionController(normalized, controllerContext, hostDataDir),\n hooks: [\n {\n descriptor: { name: 'run.start' },\n middleware: [\n async ({ payload, context: runtimeContext }: HookHandlerInput<RunStartPayload>) => {\n await reconcilePendingActivation(runtimeContext)\n if (\n await isPlanModeActiveForCurrentRun(\n runtimeContext,\n normalized.activationMetadataKey,\n )\n )\n await ensureDraftMetadata(hostDataDir, payload.sessionId)\n return payload\n },\n ],\n },\n {\n descriptor: { name: 'prompt.resolve' },\n middleware: [\n async ({ payload, context: runtimeContext }: HookHandlerInput<PromptResolvePayload>) => {\n if (\n !(await isPlanModeActiveForCurrentRun(\n runtimeContext,\n normalized.activationMetadataKey,\n ))\n )\n return payload\n\n const sessionId = runtimeContext.sessionId\n if (!sessionId) return payload\n\n return {\n ...payload,\n promptSegments: [\n buildReminderPrompt(hostDataDir, sessionId),\n ...payload.promptSegments,\n ],\n }\n },\n ],\n },\n {\n descriptor: { name: 'toolset.resolve' },\n middleware: [\n async ({ payload, context: runtimeContext }: HookHandlerInput<ToolsetResolvePayload>) => {\n if (\n !(await isPlanModeActiveForCurrentRun(\n runtimeContext,\n normalized.activationMetadataKey,\n ))\n ) {\n return {\n ...payload,\n tools: payload.tools.filter(\n (tool) =>\n tool.name !== PLAN_READ_TOOL_NAME && tool.name !== PLAN_WRITE_TOOL_NAME,\n ),\n }\n }\n\n const allowed = createAllowedToolNameSet(normalized)\n return {\n ...payload,\n tools: payload.tools.filter((tool) => allowed.has(tool.name)),\n }\n },\n ],\n },\n {\n descriptor: { name: 'tool.beforeExecute' },\n middleware: [\n async ({\n payload,\n context: runtimeContext,\n }: HookHandlerInput<ToolBeforeExecutePayload>) => {\n const toolName = payload.toolCall.function.name\n const active = await isPlanModeActiveForCurrentRun(\n runtimeContext,\n normalized.activationMetadataKey,\n )\n\n if (!active) {\n if (toolName === PLAN_READ_TOOL_NAME || toolName === PLAN_WRITE_TOOL_NAME) {\n return denyToolCall('Plan mode is not active for this session.')\n }\n return payload\n }\n\n if (IMMUTABLY_BLOCKED_TOOL_NAMES.has(toolName))\n return denyToolCall(\n `Plan mode blocks ${toolName}. Use ${PLAN_WRITE_TOOL_NAME} to update the plan draft instead.`,\n )\n\n if (toolName === PLAN_READ_TOOL_NAME || toolName === PLAN_WRITE_TOOL_NAME)\n return payload\n\n if (toolName === 'exec') {\n const command = readStringArg(payload.toolCall.function.arguments.command)\n const decision = classifyExecCommand(command, normalized.allowedExecPatterns)\n if (decision.action === 'allow') return payload\n if (decision.action === 'ask')\n return askToolCall({\n message:\n decision.reason ??\n `Plan mode requires approval for exec command: ${command}`,\n })\n return denyToolCall(\n decision.reason ?? `Plan mode blocked exec command: ${command}`,\n )\n }\n\n const allowed = createAllowedToolNameSet(normalized)\n if (!allowed.has(toolName))\n return denyToolCall(\n `Plan mode blocks tool ${toolName}. Disable plan mode before implementation.`,\n )\n\n return payload\n },\n ],\n },\n ],\n }\n },\n }\n}\n\nclass DefaultPlanModeSessionController implements PlanModeSessionController {\n private readonly options: NormalizedPlanModePluginOptions\n private readonly context: PluginSessionControllerContext\n private readonly hostDataDir: string\n\n constructor(\n options: NormalizedPlanModePluginOptions,\n context: PluginSessionControllerContext,\n hostDataDir: string,\n ) {\n this.options = options\n this.context = context\n this.hostDataDir = hostDataDir\n }\n\n async getState(): Promise<PlanModeStatus> {\n const state = readStateEntry(await this.context.pluginState.get(this.context.sessionId))\n return resolvePlanModeStatus(\n state,\n this.context.metadata,\n this.context.isRunning(),\n this.options.activationMetadataKey,\n this.hostDataDir,\n this.context.sessionId,\n )\n }\n\n async enable(): Promise<PlanModeStatus> {\n return this.updateState((state) => {\n if (this.context.isRunning()) {\n return {\n ...state,\n pendingActive: true,\n }\n }\n\n return {\n ...state,\n active: true,\n pendingActive: undefined,\n }\n })\n }\n\n async disable(): Promise<PlanModeStatus> {\n return this.updateState((state) => {\n if (this.context.isRunning()) {\n return {\n ...state,\n pendingActive: false,\n }\n }\n\n return {\n ...state,\n active: false,\n pendingActive: undefined,\n }\n })\n }\n\n async clearDraft(): Promise<PlanModeStatus> {\n await rm(draftDirectory(this.hostDataDir, this.context.sessionId), {\n recursive: true,\n force: true,\n })\n return this.getState()\n }\n\n private async updateState(\n mutate: (state: PlanModeStateData) => PlanModeStateData,\n ): Promise<PlanModeStatus> {\n const current = readStateEntry(await this.context.pluginState.get(this.context.sessionId))\n const next = mutate(current)\n await this.context.pluginState.replace(this.context.sessionId, createStateEntry(next))\n if (!this.context.isRunning()) await this.context.save()\n\n return resolvePlanModeStatus(\n next,\n this.context.metadata,\n this.context.isRunning(),\n this.options.activationMetadataKey,\n this.hostDataDir,\n this.context.sessionId,\n )\n }\n}\n\nclass PlanReadTool extends BaseTool {\n private readonly hostDataDir: string\n private readonly activationMetadataKey: string\n\n constructor(hostDataDir: string, activationMetadataKey: string) {\n super({\n name: PLAN_READ_TOOL_NAME,\n description: 'Read the current plan draft for this session.',\n inputSchema: {\n type: 'object',\n properties: {},\n },\n })\n this.hostDataDir = hostDataDir\n this.activationMetadataKey = activationMetadataKey\n }\n\n async execute(\n _args: Record<string, unknown>,\n context: ToolExecutionContext,\n ): Promise<CallToolResult> {\n await assertActiveToolContext(context, this.activationMetadataKey)\n const sessionId = context.sessionId\n const draftPath = planDraftPath(this.hostDataDir, sessionId)\n const content = await readTextIfExists(draftPath)\n\n return normalizeToolResult({\n content: [\n {\n type: 'text',\n text: content ?? `No plan draft has been written yet.\\nPath: ${draftPath}`,\n },\n ],\n structuredContent: {\n path: draftPath,\n hasDraft: content !== null,\n },\n })\n }\n}\n\nclass PlanWriteTool extends BaseTool {\n private readonly hostDataDir: string\n private readonly activationMetadataKey: string\n\n constructor(hostDataDir: string, activationMetadataKey: string) {\n super({\n name: PLAN_WRITE_TOOL_NAME,\n description: 'Write the current plan draft for this session.',\n inputSchema: {\n type: 'object',\n properties: {\n content: {\n type: 'string',\n description: 'Markdown content to write into the plan draft.',\n },\n mode: {\n type: 'string',\n enum: ['append', 'replace'],\n description: 'Whether to append to or replace the current draft.',\n },\n },\n required: ['content'],\n },\n })\n this.hostDataDir = hostDataDir\n this.activationMetadataKey = activationMetadataKey\n }\n\n async execute(\n args: Record<string, unknown>,\n context: ToolExecutionContext,\n ): Promise<CallToolResult> {\n await assertActiveToolContext(context, this.activationMetadataKey)\n const sessionId = context.sessionId\n const content = readRequiredStringArg(args, 'content')\n const mode = args.mode === 'replace' ? 'replace' : 'append'\n const draftPath = planDraftPath(this.hostDataDir, sessionId)\n const existing = mode === 'append' ? (await readTextIfExists(draftPath)) ?? '' : ''\n const next = mode === 'append' ? `${existing}${content}` : content\n\n await writeDraft(this.hostDataDir, sessionId, next)\n\n return normalizeToolResult({\n content: [\n {\n type: 'text',\n text: `Updated plan draft at ${draftPath}`,\n },\n ],\n structuredContent: {\n path: draftPath,\n bytes: Buffer.byteLength(next, 'utf8'),\n mode,\n },\n })\n }\n}\n\nfunction normalizeOptions<TId extends string>(\n options: PlanModePluginOptions & { id?: TId },\n): NormalizedPlanModePluginOptions<TId> {\n return {\n id: (options.id ?? 'plan-mode') as TId,\n modeName: options.modeName ?? 'plan',\n description:\n options.description ??\n 'Analyze and plan only. The runtime blocks implementation tools until the mode is disabled.',\n activationMetadataKey: options.activationMetadataKey ?? 'planMode',\n allowedExecPatterns: [...DEFAULT_ALLOWED_EXEC_PATTERNS, ...(options.allowedExecPatterns ?? [])],\n extraVisibleTools: dedupeToolNames(options.extraVisibleTools ?? []),\n }\n}\n\nfunction dedupeToolNames(names: string[]): string[] {\n const seen = new Set<string>()\n const deduped: string[] = []\n\n for (const name of names) {\n if (!name.trim()) continue\n if (IMMUTABLY_BLOCKED_TOOL_NAMES.has(name)) continue\n if (seen.has(name)) continue\n seen.add(name)\n deduped.push(name)\n }\n\n return deduped\n}\n\nfunction createAllowedToolNameSet(options: NormalizedPlanModePluginOptions): Set<string> {\n return new Set([\n ...DEFAULT_VISIBLE_TOOL_NAMES,\n PLAN_READ_TOOL_NAME,\n PLAN_WRITE_TOOL_NAME,\n ...options.extraVisibleTools,\n ])\n}\n\nfunction buildReminderPrompt(hostDataDir: string, sessionId: string): string {\n return [\n 'PLAN MODE ACTIVE',\n '',\n 'You are in planning mode.',\n '',\n 'Rules:',\n '- Analyze and plan only. Do not implement repository changes.',\n '- `write`, `edit`, and `patch` style tools are blocked.',\n '- `exec` is restricted to an allowlisted set of read-only inspection commands.',\n '- Build, test, lint, install, and typecheck commands are blocked in plan mode.',\n `- The only writable artifact is the plan draft at: ${planDraftPath(hostDataDir, sessionId)}`,\n `- Use \\`${PLAN_WRITE_TOOL_NAME}\\` to update the plan draft and \\`${PLAN_READ_TOOL_NAME}\\` to inspect it.`,\n '- If implementation is required, disable plan mode first.',\n ].join('\\n')\n}\n\nfunction resolvePlanModeStatus(\n state: PlanModeStateData,\n metadata: Record<string, unknown> | undefined,\n isRunning: boolean,\n activationMetadataKey: string,\n hostDataDir: string,\n sessionId: string,\n): PlanModeStatus {\n const metadataActive = metadata?.[activationMetadataKey] === true\n const persistedActive = state.active ?? metadataActive\n const pendingActive = typeof state.pendingActive === 'boolean' ? state.pendingActive : undefined\n\n return {\n active: !isRunning && pendingActive !== undefined ? pendingActive : persistedActive,\n pendingActive: isRunning ? pendingActive : undefined,\n draftPath: planDraftPath(hostDataDir, sessionId),\n }\n}\n\nasync function isPlanModeActiveForCurrentRun(\n context: Pick<PluginRuntimeContext, 'sessionId' | 'metadata' | 'services'>,\n activationMetadataKey: string,\n): Promise<boolean> {\n if (!context.sessionId) return false\n const state = readStateEntry(await context.services.pluginState.get(context.sessionId))\n return state.active ?? context.metadata?.[activationMetadataKey] === true\n}\n\nasync function reconcilePendingActivation(\n context: Pick<PluginRuntimeContext, 'sessionId' | 'services'>,\n): Promise<void> {\n if (!context.sessionId) return\n\n const current = readStateEntry(await context.services.pluginState.get(context.sessionId))\n\n if (typeof current.pendingActive !== 'boolean') return\n\n const next: PlanModeStateData = {\n ...current,\n active: current.pendingActive,\n pendingActive: undefined,\n }\n await context.services.pluginState.replace(context.sessionId, createStateEntry(next))\n}\n\nfunction createStateEntry(state: PlanModeStateData): PluginSessionStateEntry {\n return {\n version: 1,\n data: {\n active: state.active ?? false,\n ...(typeof state.pendingActive === 'boolean' ? { pendingActive: state.pendingActive } : {}),\n },\n updatedAt: Date.now(),\n }\n}\n\nfunction readStateEntry(entry: PluginSessionStateEntry | null): PlanModeStateData {\n if (!entry || !isRecord(entry.data)) return {}\n\n return {\n active: typeof entry.data.active === 'boolean' ? entry.data.active : undefined,\n pendingActive:\n typeof entry.data.pendingActive === 'boolean' ? entry.data.pendingActive : undefined,\n }\n}\n\nfunction classifyExecCommand(\n command: string,\n rules: PlanModeCommandRule[],\n): CommandDecision {\n const normalized = normalizeCommand(command)\n if (!normalized) return { action: 'deny', reason: 'Plan mode requires a non-empty exec command.' }\n\n // Hard-forbidden patterns: always blocked regardless of content\n for (const [pattern, reason] of HARD_FORBIDDEN_SHELL_PATTERNS) {\n if (pattern.test(normalized)) return { action: 'deny', reason }\n }\n\n // Strip safe stderr redirections (2>/dev/null, 2>&1) for further analysis\n const stripped = normalizeCommand(normalized.replace(STDERR_REDIRECT_RE, ''))\n if (!stripped) return { action: 'deny', reason: 'Plan mode requires a non-empty exec command.' }\n\n // Block stdout redirections and heredocs even after stripping stderr\n if (HEREDOC_RE.test(stripped))\n return { action: 'deny', reason: 'Plan mode blocks shell redirection.' }\n if (STDOUT_REDIRECT_RE.test(stripped))\n return { action: 'deny', reason: 'Plan mode blocks shell redirection.' }\n\n // Handle pipeline commands: split by | and validate each segment\n if (PIPE_RE.test(stripped)) {\n const segments = stripped.split('|').map(s => s.trim()).filter(Boolean)\n for (const segment of segments) {\n if (!isSegmentAllowed(segment, rules))\n return {\n action: 'deny',\n reason: `Plan mode blocked exec command: ${segment}`,\n }\n }\n return { action: 'allow' }\n }\n\n // Single command (no pipe): check against hard-denied prefixes and rules\n for (const deniedPrefix of HARD_DENIED_COMMAND_PREFIXES) {\n if (matchesPrefix(stripped, deniedPrefix)) {\n return {\n action: 'deny',\n reason: `Plan mode blocks exec command: ${stripped}`,\n }\n }\n }\n\n let decision: CommandDecision = {\n action: 'deny',\n reason: `Plan mode blocked exec command: ${stripped}`,\n }\n for (const rule of rules) {\n if (!matchesCommandPattern(stripped, rule.pattern)) continue\n decision = {\n action: rule.action,\n reason:\n rule.action === 'allow'\n ? undefined\n : rule.action === 'ask'\n ? `Plan mode requires approval for exec command: ${stripped}`\n : `Plan mode blocked exec command: ${stripped}`,\n }\n }\n\n return decision\n}\n\nfunction isSegmentAllowed(\n segment: string,\n rules: PlanModeCommandRule[],\n): boolean {\n const trimmed = segment.trim()\n if (!trimmed) return false\n for (const deniedPrefix of HARD_DENIED_COMMAND_PREFIXES) {\n if (matchesPrefix(trimmed, deniedPrefix)) return false\n }\n for (const rule of rules) {\n if (matchesCommandPattern(trimmed, rule.pattern) && rule.action === 'allow')\n return true\n }\n return false\n}\n\nfunction normalizeCommand(command: string): string {\n return command.trim().replace(/\\s+/g, ' ')\n}\n\nfunction matchesPrefix(command: string, prefix: string): boolean {\n return command === prefix.trim() || command.startsWith(`${prefix.trim()} `)\n}\n\nfunction matchesCommandPattern(command: string, pattern: string): boolean {\n const escaped = pattern\n .replace(/[.+^${}()|[\\]\\\\]/g, '\\\\$&')\n .replace(/\\*/g, '.*')\n .replace(/\\?/g, '.')\n return new RegExp(`^${escaped}$`).test(command)\n}\n\nfunction draftDirectory(hostDataDir: string, sessionId: string): string {\n return path.join(path.resolve(hostDataDir), 'plans', encodeURIComponent(sessionId))\n}\n\nfunction planDraftPath(hostDataDir: string, sessionId: string): string {\n return path.join(draftDirectory(hostDataDir, sessionId), 'plan.md')\n}\n\nfunction draftMetaPath(hostDataDir: string, sessionId: string): string {\n return path.join(draftDirectory(hostDataDir, sessionId), 'meta.json')\n}\n\nasync function ensureDraftMetadata(hostDataDir: string, sessionId: string): Promise<void> {\n const directory = draftDirectory(hostDataDir, sessionId)\n const metaPath = draftMetaPath(hostDataDir, sessionId)\n const current = await readDraftMetadata(hostDataDir, sessionId)\n const now = Date.now()\n const next: DraftMetadata = {\n sessionId,\n mode: 'plan',\n createdAt: current?.createdAt ?? now,\n updatedAt: now,\n }\n\n await mkdir(directory, { recursive: true })\n await writeFile(metaPath, JSON.stringify(next, null, 2), 'utf8')\n}\n\nasync function writeDraft(hostDataDir: string, sessionId: string, content: string): Promise<void> {\n await ensureDraftMetadata(hostDataDir, sessionId)\n await writeFile(planDraftPath(hostDataDir, sessionId), content, 'utf8')\n await ensureDraftMetadata(hostDataDir, sessionId)\n}\n\nasync function readDraftMetadata(\n hostDataDir: string,\n sessionId: string,\n): Promise<DraftMetadata | null> {\n try {\n const raw = await readFile(draftMetaPath(hostDataDir, sessionId), 'utf8')\n const parsed = JSON.parse(raw)\n if (!isRecord(parsed)) return null\n if (typeof parsed.sessionId !== 'string') return null\n if (parsed.mode !== 'plan') return null\n if (typeof parsed.createdAt !== 'number' || typeof parsed.updatedAt !== 'number') return null\n return parsed as unknown as DraftMetadata\n } catch {\n return null\n }\n}\n\nasync function readTextIfExists(filePath: string): Promise<string | null> {\n try {\n return await readFile(filePath, 'utf8')\n } catch {\n return null\n }\n}\n\nasync function assertActiveToolContext(\n context: ToolExecutionContext,\n activationMetadataKey: string,\n): Promise<void> {\n const state = context.services\n ? readStateEntry(await context.services.pluginState.get(context.sessionId))\n : {}\n const metadataActive = context.metadata?.[activationMetadataKey] === true\n if (!(state.active ?? metadataActive))\n throw new Error('Plan mode is not active for this session.')\n}\n\nfunction readRequiredStringArg(args: Record<string, unknown>, key: string): string {\n const value = args[key]\n if (typeof value !== 'string' || !value.length) throw new Error(`${key} must be a string`)\n return value\n}\n\nfunction readStringArg(value: unknown): string {\n return typeof value === 'string' ? value : ''\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n}\n"],"mappings":"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"} | ||
| {"version":3,"file":"index.js","names":[],"sources":["../src/index.ts"],"sourcesContent":["import { mkdir, readFile, rm, writeFile } from 'node:fs/promises'\nimport path from 'node:path'\nimport { BaseTool, normalizeToolResult } from '@archships/dim-agent-sdk'\nimport { askToolCall, denyToolCall } from '@archships/dim-plugin-api'\nimport type {\n CallToolResult,\n DimPlugin,\n HookHandlerInput,\n PluginRuntimeContext,\n PluginSessionControllerContext,\n PluginSessionStateEntry,\n PromptResolvePayload,\n RunStartPayload,\n ToolBeforeExecutePayload,\n ToolsetResolvePayload,\n ToolExecutionContext,\n} from '@archships/dim-plugin-api'\n\nexport interface PlanModeCommandRule {\n pattern: string\n action: 'allow' | 'deny' | 'ask'\n}\n\nexport interface PlanModePluginOptions {\n id?: string\n modeName?: string\n description?: string\n activationMetadataKey?: string\n allowedExecPatterns?: PlanModeCommandRule[]\n extraVisibleTools?: string[]\n}\n\nexport interface PlanModeStatus {\n active: boolean\n pendingActive?: boolean\n draftPath: string\n}\n\nexport interface PlanModeSessionController {\n getState(): Promise<PlanModeStatus>\n enable(): Promise<PlanModeStatus>\n disable(): Promise<PlanModeStatus>\n clearDraft(): Promise<PlanModeStatus>\n}\n\ninterface NormalizedPlanModePluginOptions<TId extends string = string> {\n id: TId\n modeName: string\n description: string\n activationMetadataKey: string\n allowedExecPatterns: PlanModeCommandRule[]\n extraVisibleTools: string[]\n}\n\ninterface PlanModeStateData {\n active?: boolean\n pendingActive?: boolean\n}\n\ninterface DraftMetadata {\n sessionId: string\n mode: 'plan'\n createdAt: number\n updatedAt: number\n}\n\ninterface CommandDecision {\n action: 'allow' | 'deny' | 'ask'\n reason?: string\n}\n\ninterface ExecToolInput {\n action: 'start' | 'poll' | 'terminate'\n command?: string[]\n processId?: number\n}\n\nconst PLAN_READ_TOOL_NAME = 'plan_read'\nconst PLAN_WRITE_TOOL_NAME = 'plan_write'\nconst DEFAULT_VISIBLE_TOOL_NAMES = ['read', 'grep', 'glob', 'exec']\nconst IMMUTABLY_BLOCKED_TOOL_NAMES = new Set(['write', 'edit', 'patch'])\n\nconst DEFAULT_ALLOWED_EXEC_PATTERNS: PlanModeCommandRule[] = [\n { pattern: 'pwd', action: 'allow' },\n { pattern: 'ls', action: 'allow' },\n { pattern: 'ls *', action: 'allow' },\n { pattern: 'find *', action: 'allow' },\n { pattern: 'cat *', action: 'allow' },\n { pattern: 'head *', action: 'allow' },\n { pattern: 'tail *', action: 'allow' },\n { pattern: 'wc *', action: 'allow' },\n { pattern: 'stat *', action: 'allow' },\n { pattern: 'file *', action: 'allow' },\n { pattern: 'sed -n *', action: 'allow' },\n { pattern: 'rg *', action: 'allow' },\n { pattern: 'grep *', action: 'allow' },\n { pattern: 'fd *', action: 'allow' },\n { pattern: 'git status', action: 'allow' },\n { pattern: 'git status *', action: 'allow' },\n { pattern: 'git diff', action: 'allow' },\n { pattern: 'git diff *', action: 'allow' },\n { pattern: 'git show *', action: 'allow' },\n { pattern: 'git log', action: 'allow' },\n { pattern: 'git log *', action: 'allow' },\n { pattern: 'git branch', action: 'allow' },\n { pattern: 'git branch *', action: 'allow' },\n { pattern: 'git rev-parse *', action: 'allow' },\n { pattern: 'git ls-files', action: 'allow' },\n { pattern: 'git ls-files *', action: 'allow' },\n { pattern: 'git grep *', action: 'allow' },\n // Purely read-only git subcommands\n { pattern: 'git blame *', action: 'allow' },\n { pattern: 'git shortlog', action: 'allow' },\n { pattern: 'git shortlog *', action: 'allow' },\n { pattern: 'git describe', action: 'allow' },\n { pattern: 'git describe *', action: 'allow' },\n { pattern: 'git cat-file *', action: 'allow' },\n { pattern: 'git ls-tree *', action: 'allow' },\n { pattern: 'git show-ref', action: 'allow' },\n { pattern: 'git show-ref *', action: 'allow' },\n { pattern: 'git ls-remote', action: 'allow' },\n { pattern: 'git ls-remote *', action: 'allow' },\n { pattern: 'git for-each-ref', action: 'allow' },\n { pattern: 'git for-each-ref *', action: 'allow' },\n { pattern: 'git rev-list *', action: 'allow' },\n { pattern: 'git merge-base *', action: 'allow' },\n { pattern: 'git diff-tree *', action: 'allow' },\n { pattern: 'git cherry', action: 'allow' },\n { pattern: 'git cherry *', action: 'allow' },\n { pattern: 'git name-rev *', action: 'allow' },\n { pattern: 'git check-ignore *', action: 'allow' },\n // Git subcommands with write modes — only safe forms allowed\n { pattern: 'git tag', action: 'allow' },\n { pattern: 'git tag -l', action: 'allow' },\n { pattern: 'git tag -l *', action: 'allow' },\n { pattern: 'git tag --list', action: 'allow' },\n { pattern: 'git tag --list *', action: 'allow' },\n { pattern: 'git remote', action: 'allow' },\n { pattern: 'git remote -v', action: 'allow' },\n { pattern: 'git remote show *', action: 'allow' },\n { pattern: 'git remote get-url *', action: 'allow' },\n { pattern: 'git reflog', action: 'allow' },\n { pattern: 'git reflog show *', action: 'allow' },\n { pattern: 'git stash list', action: 'allow' },\n { pattern: 'git stash list *', action: 'allow' },\n { pattern: 'git stash show *', action: 'allow' },\n { pattern: 'git config --get *', action: 'allow' },\n { pattern: 'git config --get-regexp *', action: 'allow' },\n { pattern: 'git config --list', action: 'allow' },\n { pattern: 'git config -l', action: 'allow' },\n { pattern: 'head', action: 'allow' },\n { pattern: 'tail', action: 'allow' },\n { pattern: 'wc', action: 'allow' },\n { pattern: 'cat', action: 'allow' },\n { pattern: 'sort', action: 'allow' },\n { pattern: 'sort *', action: 'allow' },\n { pattern: 'uniq', action: 'allow' },\n { pattern: 'uniq *', action: 'allow' },\n { pattern: 'cut *', action: 'allow' },\n { pattern: 'tr *', action: 'allow' },\n { pattern: 'awk *', action: 'allow' },\n { pattern: 'jq', action: 'allow' },\n { pattern: 'jq *', action: 'allow' },\n { pattern: 'yq', action: 'allow' },\n { pattern: 'yq *', action: 'allow' },\n { pattern: 'xargs grep *', action: 'allow' },\n { pattern: 'xargs rg *', action: 'allow' },\n { pattern: 'du *', action: 'allow' },\n { pattern: 'df', action: 'allow' },\n { pattern: 'df *', action: 'allow' },\n { pattern: 'tree', action: 'allow' },\n { pattern: 'tree *', action: 'allow' },\n { pattern: 'which *', action: 'allow' },\n { pattern: 'echo', action: 'allow' },\n { pattern: 'echo *', action: 'allow' },\n { pattern: 'realpath *', action: 'allow' },\n { pattern: 'basename *', action: 'allow' },\n { pattern: 'dirname *', action: 'allow' },\n]\n\nconst HARD_DENIED_COMMAND_PREFIXES = [\n 'touch',\n 'mkdir',\n 'rm',\n 'mv',\n 'cp',\n 'ln',\n 'chmod',\n 'chown',\n 'git add',\n 'git commit',\n 'git push',\n 'git checkout',\n 'git switch',\n 'git restore',\n 'git apply',\n 'npm ',\n 'pnpm ',\n 'yarn ',\n 'bun ',\n]\n\nexport function createPlanModePlugin(): DimPlugin<'plan-mode', PlanModeSessionController>\nexport function createPlanModePlugin(\n options: PlanModePluginOptions,\n): DimPlugin<'plan-mode', PlanModeSessionController>\nexport function createPlanModePlugin<TId extends string>(\n options: PlanModePluginOptions & { id: TId },\n): DimPlugin<TId, PlanModeSessionController>\nexport function createPlanModePlugin<TId extends string>(\n options: PlanModePluginOptions & { id?: TId } = {},\n): DimPlugin<TId, PlanModeSessionController> {\n const normalized = normalizeOptions(options)\n\n return {\n manifest: {\n id: normalized.id,\n version: '1.0.0',\n apiVersion: 1,\n permissions: { fs: true },\n capabilities: ['plan-mode'],\n },\n setup(context) {\n const hostDataDir = context.hostDataDir\n if (!hostDataDir)\n throw new Error(\n 'createPlanModePlugin requires createAgent({ hostDataDir }) so plan drafts can be stored outside the workspace',\n )\n\n const planReadTool = new PlanReadTool(hostDataDir, normalized.activationMetadataKey)\n const planWriteTool = new PlanWriteTool(hostDataDir, normalized.activationMetadataKey)\n\n return {\n inspector: {\n getConfig: async () => ({\n id: normalized.id,\n modeName: normalized.modeName,\n description: normalized.description,\n activationMetadataKey: normalized.activationMetadataKey,\n allowedExecPatterns: normalized.allowedExecPatterns.map((rule) => ({\n pattern: rule.pattern,\n action: rule.action,\n })),\n extraVisibleTools: [...normalized.extraVisibleTools],\n }),\n getSessionState: async (sessionId) => {\n const state = readStateEntry(await context.services.pluginState.get(sessionId))\n return {\n active: state.active ?? false,\n pendingActive: state.pendingActive ?? false,\n draftPath: planDraftPath(hostDataDir, sessionId),\n }\n },\n },\n modes: [\n {\n name: normalized.modeName,\n description: normalized.description,\n },\n ],\n tools: [planReadTool, planWriteTool],\n createSessionController: (controllerContext) =>\n new DefaultPlanModeSessionController(normalized, controllerContext, hostDataDir),\n hooks: [\n {\n descriptor: { name: 'run.start' },\n middleware: [\n async ({ payload, context: runtimeContext }: HookHandlerInput<RunStartPayload>) => {\n await reconcilePendingActivation(runtimeContext)\n if (\n await isPlanModeActiveForCurrentRun(\n runtimeContext,\n normalized.activationMetadataKey,\n )\n )\n await ensureDraftMetadata(hostDataDir, payload.sessionId)\n return payload\n },\n ],\n },\n {\n descriptor: { name: 'prompt.resolve' },\n middleware: [\n async ({ payload, context: runtimeContext }: HookHandlerInput<PromptResolvePayload>) => {\n if (\n !(await isPlanModeActiveForCurrentRun(\n runtimeContext,\n normalized.activationMetadataKey,\n ))\n )\n return payload\n\n const sessionId = runtimeContext.sessionId\n if (!sessionId) return payload\n\n return {\n ...payload,\n promptSegments: [\n buildReminderPrompt(hostDataDir, sessionId),\n ...payload.promptSegments,\n ],\n }\n },\n ],\n },\n {\n descriptor: { name: 'toolset.resolve' },\n middleware: [\n async ({ payload, context: runtimeContext }: HookHandlerInput<ToolsetResolvePayload>) => {\n if (\n !(await isPlanModeActiveForCurrentRun(\n runtimeContext,\n normalized.activationMetadataKey,\n ))\n ) {\n return {\n ...payload,\n tools: payload.tools.filter(\n (tool) =>\n tool.name !== PLAN_READ_TOOL_NAME && tool.name !== PLAN_WRITE_TOOL_NAME,\n ),\n }\n }\n\n const allowed = createAllowedToolNameSet(normalized)\n return {\n ...payload,\n tools: payload.tools.filter((tool) => allowed.has(tool.name)),\n }\n },\n ],\n },\n {\n descriptor: { name: 'tool.beforeExecute' },\n middleware: [\n async ({\n payload,\n context: runtimeContext,\n }: HookHandlerInput<ToolBeforeExecutePayload>) => {\n const toolName = payload.toolCall.function.name\n const active = await isPlanModeActiveForCurrentRun(\n runtimeContext,\n normalized.activationMetadataKey,\n )\n\n if (!active) {\n if (toolName === PLAN_READ_TOOL_NAME || toolName === PLAN_WRITE_TOOL_NAME) {\n return denyToolCall('Plan mode is not active for this session.')\n }\n return payload\n }\n\n if (IMMUTABLY_BLOCKED_TOOL_NAMES.has(toolName))\n return denyToolCall(\n `Plan mode blocks ${toolName}. Use ${PLAN_WRITE_TOOL_NAME} to update the plan draft instead.`,\n )\n\n if (toolName === PLAN_READ_TOOL_NAME || toolName === PLAN_WRITE_TOOL_NAME)\n return payload\n\n if (toolName === 'exec') {\n const execInput = readExecToolInput(payload.toolCall.function.arguments)\n const decision = classifyExecCommand(execInput, normalized.allowedExecPatterns)\n if (decision.action === 'allow') return payload\n if (decision.action === 'ask')\n return askToolCall({\n message:\n decision.reason ??\n `Plan mode requires approval for exec command: ${formatExecInput(execInput)}`,\n })\n return denyToolCall(\n decision.reason\n ?? `Plan mode blocked exec command: ${formatExecInput(execInput)}`,\n )\n }\n\n const allowed = createAllowedToolNameSet(normalized)\n if (!allowed.has(toolName))\n return denyToolCall(\n `Plan mode blocks tool ${toolName}. Disable plan mode before implementation.`,\n )\n\n return payload\n },\n ],\n },\n ],\n }\n },\n }\n}\n\nclass DefaultPlanModeSessionController implements PlanModeSessionController {\n private readonly options: NormalizedPlanModePluginOptions\n private readonly context: PluginSessionControllerContext\n private readonly hostDataDir: string\n\n constructor(\n options: NormalizedPlanModePluginOptions,\n context: PluginSessionControllerContext,\n hostDataDir: string,\n ) {\n this.options = options\n this.context = context\n this.hostDataDir = hostDataDir\n }\n\n async getState(): Promise<PlanModeStatus> {\n const state = readStateEntry(await this.context.pluginState.get(this.context.sessionId))\n return resolvePlanModeStatus(\n state,\n this.context.metadata,\n this.context.isRunning(),\n this.options.activationMetadataKey,\n this.hostDataDir,\n this.context.sessionId,\n )\n }\n\n async enable(): Promise<PlanModeStatus> {\n return this.updateState((state) => {\n if (this.context.isRunning()) {\n return {\n ...state,\n pendingActive: true,\n }\n }\n\n return {\n ...state,\n active: true,\n pendingActive: undefined,\n }\n })\n }\n\n async disable(): Promise<PlanModeStatus> {\n return this.updateState((state) => {\n if (this.context.isRunning()) {\n return {\n ...state,\n pendingActive: false,\n }\n }\n\n return {\n ...state,\n active: false,\n pendingActive: undefined,\n }\n })\n }\n\n async clearDraft(): Promise<PlanModeStatus> {\n await rm(draftDirectory(this.hostDataDir, this.context.sessionId), {\n recursive: true,\n force: true,\n })\n return this.getState()\n }\n\n private async updateState(\n mutate: (state: PlanModeStateData) => PlanModeStateData,\n ): Promise<PlanModeStatus> {\n const current = readStateEntry(await this.context.pluginState.get(this.context.sessionId))\n const next = mutate(current)\n await this.context.pluginState.replace(this.context.sessionId, createStateEntry(next))\n if (!this.context.isRunning()) await this.context.save()\n\n return resolvePlanModeStatus(\n next,\n this.context.metadata,\n this.context.isRunning(),\n this.options.activationMetadataKey,\n this.hostDataDir,\n this.context.sessionId,\n )\n }\n}\n\nclass PlanReadTool extends BaseTool {\n private readonly hostDataDir: string\n private readonly activationMetadataKey: string\n\n constructor(hostDataDir: string, activationMetadataKey: string) {\n super({\n name: PLAN_READ_TOOL_NAME,\n description: 'Read the current plan draft for this session.',\n inputSchema: {\n type: 'object',\n properties: {},\n },\n })\n this.hostDataDir = hostDataDir\n this.activationMetadataKey = activationMetadataKey\n }\n\n async execute(\n _args: Record<string, unknown>,\n context: ToolExecutionContext,\n ): Promise<CallToolResult> {\n await assertActiveToolContext(context, this.activationMetadataKey)\n const sessionId = context.sessionId\n const draftPath = planDraftPath(this.hostDataDir, sessionId)\n const content = await readTextIfExists(draftPath)\n\n return normalizeToolResult({\n content: [\n {\n type: 'text',\n text: content ?? `No plan draft has been written yet.\\nPath: ${draftPath}`,\n },\n ],\n structuredContent: {\n path: draftPath,\n hasDraft: content !== null,\n },\n })\n }\n}\n\nclass PlanWriteTool extends BaseTool {\n private readonly hostDataDir: string\n private readonly activationMetadataKey: string\n\n constructor(hostDataDir: string, activationMetadataKey: string) {\n super({\n name: PLAN_WRITE_TOOL_NAME,\n description: 'Write the current plan draft for this session.',\n inputSchema: {\n type: 'object',\n properties: {\n content: {\n type: 'string',\n description: 'Markdown content to write into the plan draft.',\n },\n mode: {\n type: 'string',\n enum: ['append', 'replace'],\n description: 'Whether to append to or replace the current draft.',\n },\n },\n required: ['content'],\n },\n })\n this.hostDataDir = hostDataDir\n this.activationMetadataKey = activationMetadataKey\n }\n\n async execute(\n args: Record<string, unknown>,\n context: ToolExecutionContext,\n ): Promise<CallToolResult> {\n await assertActiveToolContext(context, this.activationMetadataKey)\n const sessionId = context.sessionId\n const content = readRequiredStringArg(args, 'content')\n const mode = args.mode === 'replace' ? 'replace' : 'append'\n const draftPath = planDraftPath(this.hostDataDir, sessionId)\n const existing = mode === 'append' ? (await readTextIfExists(draftPath)) ?? '' : ''\n const next = mode === 'append' ? `${existing}${content}` : content\n\n await writeDraft(this.hostDataDir, sessionId, next)\n\n return normalizeToolResult({\n content: [\n {\n type: 'text',\n text: `Updated plan draft at ${draftPath}`,\n },\n ],\n structuredContent: {\n path: draftPath,\n bytes: Buffer.byteLength(next, 'utf8'),\n mode,\n },\n })\n }\n}\n\nfunction normalizeOptions<TId extends string>(\n options: PlanModePluginOptions & { id?: TId },\n): NormalizedPlanModePluginOptions<TId> {\n return {\n id: (options.id ?? 'plan-mode') as TId,\n modeName: options.modeName ?? 'plan',\n description:\n options.description ??\n 'Analyze and plan only. The runtime blocks implementation tools until the mode is disabled.',\n activationMetadataKey: options.activationMetadataKey ?? 'planMode',\n allowedExecPatterns: [...DEFAULT_ALLOWED_EXEC_PATTERNS, ...(options.allowedExecPatterns ?? [])],\n extraVisibleTools: dedupeToolNames(options.extraVisibleTools ?? []),\n }\n}\n\nfunction dedupeToolNames(names: string[]): string[] {\n const seen = new Set<string>()\n const deduped: string[] = []\n\n for (const name of names) {\n if (!name.trim()) continue\n if (IMMUTABLY_BLOCKED_TOOL_NAMES.has(name)) continue\n if (seen.has(name)) continue\n seen.add(name)\n deduped.push(name)\n }\n\n return deduped\n}\n\nfunction createAllowedToolNameSet(options: NormalizedPlanModePluginOptions): Set<string> {\n return new Set([\n ...DEFAULT_VISIBLE_TOOL_NAMES,\n PLAN_READ_TOOL_NAME,\n PLAN_WRITE_TOOL_NAME,\n ...options.extraVisibleTools,\n ])\n}\n\nfunction buildReminderPrompt(hostDataDir: string, sessionId: string): string {\n return [\n 'PLAN MODE ACTIVE',\n '',\n 'You are in planning mode.',\n '',\n 'Rules:',\n '- Analyze and plan only. Do not implement repository changes.',\n '- `write`, `edit`, and `patch` style tools are blocked.',\n '- `exec` only accepts argv arrays and is restricted to read-only inspection commands.',\n '- Build, test, lint, install, and typecheck commands are blocked in plan mode.',\n `- The only writable artifact is the plan draft at: ${planDraftPath(hostDataDir, sessionId)}`,\n `- Use \\`${PLAN_WRITE_TOOL_NAME}\\` to update the plan draft and \\`${PLAN_READ_TOOL_NAME}\\` to inspect it.`,\n '- If implementation is required, disable plan mode first.',\n ].join('\\n')\n}\n\nfunction resolvePlanModeStatus(\n state: PlanModeStateData,\n metadata: Record<string, unknown> | undefined,\n isRunning: boolean,\n activationMetadataKey: string,\n hostDataDir: string,\n sessionId: string,\n): PlanModeStatus {\n const metadataActive = metadata?.[activationMetadataKey] === true\n const persistedActive = state.active ?? metadataActive\n const pendingActive = typeof state.pendingActive === 'boolean' ? state.pendingActive : undefined\n\n return {\n active: !isRunning && pendingActive !== undefined ? pendingActive : persistedActive,\n pendingActive: isRunning ? pendingActive : undefined,\n draftPath: planDraftPath(hostDataDir, sessionId),\n }\n}\n\nasync function isPlanModeActiveForCurrentRun(\n context: Pick<PluginRuntimeContext, 'sessionId' | 'metadata' | 'services'>,\n activationMetadataKey: string,\n): Promise<boolean> {\n if (!context.sessionId) return false\n const state = readStateEntry(await context.services.pluginState.get(context.sessionId))\n return state.active ?? context.metadata?.[activationMetadataKey] === true\n}\n\nasync function reconcilePendingActivation(\n context: Pick<PluginRuntimeContext, 'sessionId' | 'services'>,\n): Promise<void> {\n if (!context.sessionId) return\n\n const current = readStateEntry(await context.services.pluginState.get(context.sessionId))\n\n if (typeof current.pendingActive !== 'boolean') return\n\n const next: PlanModeStateData = {\n ...current,\n active: current.pendingActive,\n pendingActive: undefined,\n }\n await context.services.pluginState.replace(context.sessionId, createStateEntry(next))\n}\n\nfunction createStateEntry(state: PlanModeStateData): PluginSessionStateEntry {\n return {\n version: 1,\n data: {\n active: state.active ?? false,\n ...(typeof state.pendingActive === 'boolean' ? { pendingActive: state.pendingActive } : {}),\n },\n updatedAt: Date.now(),\n }\n}\n\nfunction readStateEntry(entry: PluginSessionStateEntry | null): PlanModeStateData {\n if (!entry || !isRecord(entry.data)) return {}\n\n return {\n active: typeof entry.data.active === 'boolean' ? entry.data.active : undefined,\n pendingActive:\n typeof entry.data.pendingActive === 'boolean' ? entry.data.pendingActive : undefined,\n }\n}\n\nfunction classifyExecCommand(\n input: ExecToolInput,\n rules: PlanModeCommandRule[],\n): CommandDecision {\n if (input.action === 'poll' || input.action === 'terminate') return { action: 'allow' }\n\n const command = normalizeExecPatternInput(input.command)\n if (!command)\n return { action: 'deny', reason: 'Plan mode requires exec command to be a non-empty string array.' }\n\n for (const deniedPrefix of HARD_DENIED_COMMAND_PREFIXES) {\n if (matchesPrefix(command, deniedPrefix)) {\n return {\n action: 'deny',\n reason: `Plan mode blocks exec command: ${command}`,\n }\n }\n }\n\n let decision: CommandDecision = {\n action: 'deny',\n reason: `Plan mode blocked exec command: ${command}`,\n }\n for (const rule of rules) {\n if (!matchesCommandPattern(command, rule.pattern)) continue\n decision = {\n action: rule.action,\n reason:\n rule.action === 'allow'\n ? undefined\n : rule.action === 'ask'\n ? `Plan mode requires approval for exec command: ${command}`\n : `Plan mode blocked exec command: ${command}`,\n }\n }\n\n return decision\n}\n\nfunction matchesPrefix(command: string, prefix: string): boolean {\n return command === prefix.trim() || command.startsWith(`${prefix.trim()} `)\n}\n\nfunction matchesCommandPattern(command: string, pattern: string): boolean {\n const escaped = pattern\n .replace(/[.+^${}()|[\\]\\\\]/g, '\\\\$&')\n .replace(/\\*/g, '.*')\n .replace(/\\?/g, '.')\n return new RegExp(`^${escaped}$`).test(command)\n}\n\nfunction readExecToolInput(args: Record<string, unknown>): ExecToolInput {\n const action = readExecAction(args.action, args)\n const command = readExecCommand(args.command)\n\n if (action === 'start') {\n if (!command) {\n throw new Error('Plan mode requires exec command to be a non-empty string array.')\n }\n return { action, command }\n }\n\n const processId = args.processId\n if (typeof processId !== 'number' || !Number.isInteger(processId) || processId <= 0) {\n throw new Error(`Plan mode requires exec ${action} processId to be a positive integer.`)\n }\n\n return { action, processId }\n}\n\nfunction readExecAction(\n value: unknown,\n args: Record<string, unknown>,\n): ExecToolInput['action'] {\n if (value === undefined) {\n if ('command' in args && !('processId' in args)) return 'start'\n if ('processId' in args && !('command' in args)) return 'poll'\n throw new Error('Plan mode requires exec to include either command or processId.')\n }\n if (value === 'start' || value === 'poll' || value === 'terminate') return value\n throw new Error('Plan mode only allows exec action to be start, poll, or terminate.')\n}\n\nfunction readExecCommand(value: unknown): string[] | undefined {\n if (value === undefined) return undefined\n if (!Array.isArray(value) || value.length === 0)\n throw new Error('Plan mode requires exec command to be a non-empty string array.')\n if (value.some((entry) => typeof entry !== 'string'))\n throw new Error('Plan mode requires every exec command entry to be a string.')\n if (typeof value[0] === 'string' && value[0].trim().length === 0)\n throw new Error('Plan mode requires exec command[0] to be a non-empty string.')\n return [...value]\n}\n\nfunction normalizeExecPatternInput(command: string[] | undefined): string {\n if (!command || command.length === 0) return ''\n return command.map((part) => part.trim()).join(' ').trim().replace(/\\s+/g, ' ')\n}\n\nfunction formatExecInput(input: ExecToolInput): string {\n if (input.action === 'start') return normalizeExecPatternInput(input.command)\n return `${input.action} process ${input.processId ?? 'unknown'}`\n}\n\nfunction draftDirectory(hostDataDir: string, sessionId: string): string {\n return path.join(path.resolve(hostDataDir), 'plans', encodeURIComponent(sessionId))\n}\n\nfunction planDraftPath(hostDataDir: string, sessionId: string): string {\n return path.join(draftDirectory(hostDataDir, sessionId), 'plan.md')\n}\n\nfunction draftMetaPath(hostDataDir: string, sessionId: string): string {\n return path.join(draftDirectory(hostDataDir, sessionId), 'meta.json')\n}\n\nasync function ensureDraftMetadata(hostDataDir: string, sessionId: string): Promise<void> {\n const directory = draftDirectory(hostDataDir, sessionId)\n const metaPath = draftMetaPath(hostDataDir, sessionId)\n const current = await readDraftMetadata(hostDataDir, sessionId)\n const now = Date.now()\n const next: DraftMetadata = {\n sessionId,\n mode: 'plan',\n createdAt: current?.createdAt ?? now,\n updatedAt: now,\n }\n\n await mkdir(directory, { recursive: true })\n await writeFile(metaPath, JSON.stringify(next, null, 2), 'utf8')\n}\n\nasync function writeDraft(hostDataDir: string, sessionId: string, content: string): Promise<void> {\n await ensureDraftMetadata(hostDataDir, sessionId)\n await writeFile(planDraftPath(hostDataDir, sessionId), content, 'utf8')\n await ensureDraftMetadata(hostDataDir, sessionId)\n}\n\nasync function readDraftMetadata(\n hostDataDir: string,\n sessionId: string,\n): Promise<DraftMetadata | null> {\n try {\n const raw = await readFile(draftMetaPath(hostDataDir, sessionId), 'utf8')\n const parsed = JSON.parse(raw)\n if (!isRecord(parsed)) return null\n if (typeof parsed.sessionId !== 'string') return null\n if (parsed.mode !== 'plan') return null\n if (typeof parsed.createdAt !== 'number' || typeof parsed.updatedAt !== 'number') return null\n return parsed as unknown as DraftMetadata\n } catch {\n return null\n }\n}\n\nasync function readTextIfExists(filePath: string): Promise<string | null> {\n try {\n return await readFile(filePath, 'utf8')\n } catch {\n return null\n }\n}\n\nasync function assertActiveToolContext(\n context: ToolExecutionContext,\n activationMetadataKey: string,\n): Promise<void> {\n const state = context.services\n ? readStateEntry(await context.services.pluginState.get(context.sessionId))\n : {}\n const metadataActive = context.metadata?.[activationMetadataKey] === true\n if (!(state.active ?? metadataActive))\n throw new Error('Plan mode is not active for this session.')\n}\n\nfunction readRequiredStringArg(args: Record<string, unknown>, key: string): string {\n const value = args[key]\n if (typeof value !== 'string' || !value.length) throw new Error(`${key} must be a string`)\n return value\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n}\n"],"mappings":"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"} |
+3
-3
| { | ||
| "name": "@archships/dim-plugin-plan-mode", | ||
| "version": "0.0.8", | ||
| "version": "0.0.9", | ||
| "description": "Official plan-mode plugin for dim-agent-sdk.", | ||
@@ -27,4 +27,4 @@ "homepage": "https://dimcode.dev/", | ||
| "devDependencies": { | ||
| "@archships/dim-agent-sdk": "0.0.42", | ||
| "@archships/dim-plugin-api": "0.0.13" | ||
| "@archships/dim-agent-sdk": "0.0.43", | ||
| "@archships/dim-plugin-api": "0.0.14" | ||
| }, | ||
@@ -31,0 +31,0 @@ "scripts": { |
+2
-1
@@ -14,3 +14,3 @@ # @archships/dim-plugin-plan-mode | ||
| - denies hidden or fabricated implementation tool calls through `tool.beforeExecute` | ||
| - keeps `exec` visible, but only allows an allowlisted set of read-only inspection commands | ||
| - keeps `exec` visible, but only allows an allowlisted set of read-only argv inspection commands | ||
| - exposes a session-scoped controller through `session.getPlugin('plan-mode')` | ||
@@ -59,2 +59,3 @@ - stores session drafts under `<hostDataDir>/plans/<sessionId>/plan.md` | ||
| - `write`, `edit`, and `patch` style tools are blocked while plan mode is active | ||
| - `exec` must use argv-style `command: string[]` input while plan mode is active | ||
| - `plan_write` is the only writable artifact exposed to the model in plan mode | ||
@@ -61,0 +62,0 @@ - state changes are `next run only` for in-flight runs |
Explicitly Unlicensed Item
LicenseSomething was found which is explicitly marked as unlicensed.
Found 1 instance in 1 package
Explicitly Unlicensed Item
LicenseSomething was found which is explicitly marked as unlicensed.
Found 1 instance in 1 package
71401
3.96%904
1.8%63
1.61%