Files
backnotprop__plannotator/apps/pi-extension/tool-scope.ts
Peete d26289060d fix(pi): preserve extension tool selections (#1124)
* fix(pi): preserve extension tool selections

* fix(pi): release phase tools when the plan completes

The agent_end plan-completion path dropped back to idle via
restoreSavedState but never called releaseAddedPhaseTools(), so tools
Plannotator activated for the executing phase stayed active in idle and
a stale phaseAddedTools list was persisted. Mirror exitToIdle.

Claude-Session: https://claude.ai/code/session_01H5KQWqXqjrPxyxUNso1QHS

* fix(pi): keep grep/find/ls in the default planning tool set

Dropping them from the planning phase profile silently removed native
search from default planning sessions. Restore them as default planning
phase tools so they now flow through the new tracking mechanism: they
are recorded in phaseAddedTools and released on phase exit, and a
discovery tool another extension already activated is neither claimed
nor removed. A user's own planning.activeTools still overrides them.

Claude-Session: https://claude.ai/code/session_01H5KQWqXqjrPxyxUNso1QHS

* fix(pi): always keep the submit tool active during planning

Planning tools now come purely from the resolved profile's activeTools,
which uses replace semantics: a phases.planning.activeTools in
~/.pi/agent/plannotator.json or cwd/.pi/plannotator.json supersedes the
built-in list wholesale. Pi's setActiveToolsByName then replaces the
session's tool list outright, so such a config silently dropped
plannotator_submit_plan while the planning system prompt kept telling the
model to call it - a dead end with no way to submit a plan.

Union PLAN_SUBMIT_TOOL into the configured list for the planning phase
only (deduped if the config already lists it). It flows through
phaseAddedTools like any other addition, so it is released normally on
phase exit. Other tools are left to the user: overriding
planning.activeTools is a deliberate choice of tool set, and only the
submit tool is contractually required for the phase to function.

Also corrects the README's "extra tools ... additive" wording, which
described the old merge behavior.

Claude-Session: https://claude.ai/code/session_01H5KQWqXqjrPxyxUNso1QHS

---------

Co-authored-by: Michael Ramos <support@plannotator.ai>
Co-authored-by: Michael Ramos <mdramos8@gmail.com>
2026-07-26 22:28:11 -07:00

50 lines
1.7 KiB
TypeScript

import { extname, isAbsolute, relative, resolve } from "node:path";
export type Phase = "idle" | "planning" | "executing";
export const PLAN_SUBMIT_TOOL = "plannotator_submit_plan";
const ALLOWED_PLAN_EXTENSIONS = new Set<string>([".md", ".mdx"]);
export function stripPlanningOnlyTools(tools: readonly string[]): string[] {
return tools.filter((tool) => tool !== PLAN_SUBMIT_TOOL);
}
export function applyPhaseTools(
activeTools: readonly string[],
previouslyAddedTools: readonly string[],
configuredTools: readonly string[],
): { activeTools: string[]; addedTools: string[] } {
const previousAdditions = new Set(previouslyAddedTools);
const baseTools = activeTools.filter((tool) => !previousAdditions.has(tool));
const baseToolSet = new Set(baseTools);
const addedTools = [...new Set(configuredTools)].filter(
(tool) => !baseToolSet.has(tool),
);
return {
activeTools: [...baseTools, ...addedTools],
addedTools,
};
}
export function releasePhaseTools(
activeTools: readonly string[],
addedTools: readonly string[],
): string[] {
const additions = new Set(addedTools);
return activeTools.filter((tool) => !additions.has(tool));
}
// Used by both the planning-phase write gate and plannotator_submit_plan.
// Path must resolve inside cwd (no traversal, no absolute escape) and end
// in a permitted markdown extension.
export function isPlanWritePathAllowed(inputPath: string, cwd: string): boolean {
if (!inputPath) return false;
const targetAbs = resolve(cwd, inputPath);
const rel = relative(resolve(cwd), targetAbs);
if (rel === "" || rel.startsWith("..") || isAbsolute(rel)) return false;
const ext = extname(targetAbs).toLowerCase();
return ALLOWED_PLAN_EXTENSIONS.has(ext);
}