fix(tools): tool registry now contains ALL built-in tools

- createAllTools() populates registry with all 7 built-in tools
- --tools flag only sets initially active tools (default: read/bash/edit/write)
- Hooks can enable any tool from registry via setActiveTools()
- System prompt rebuilds with correct tool guidelines when tools change
- Document tsx module resolution workaround in README
This commit is contained in:
Mario Zechner 2026-01-04 18:44:41 +01:00
parent c447e62662
commit 2849623afc
2 changed files with 61 additions and 26 deletions

View file

@ -1132,6 +1132,21 @@ import { getPackageDir, getThemeDir } from "./paths.js";
Never use `__dirname` directly for package assets. Never use `__dirname` directly for package assets.
### Module Resolution with tsx
When running from source via `npx tsx src/cli.ts`, hooks loaded via jiti may get separate module instances from the main app. This can cause issues with global state (like the theme object).
**Workaround**: Functions like `getSettingsListTheme()` accept an optional theme parameter. In hooks, pass the theme from `ctx.ui.custom()`:
```typescript
await ctx.ui.custom((tui, theme, done) => {
const settingsTheme = getSettingsListTheme(theme);
// ...
});
```
When running the built version (`node dist/cli.js` or installed via npm), this is not an issue.
### Debug Command ### Debug Command
`/debug` (hidden) writes rendered lines with ANSI codes to `~/.pi/agent/pi-debug.log` for TUI debugging, as well as the last set of messages that were sent to the LLM. `/debug` (hidden) writes rendered lines with ANSI codes to `~/.pi/agent/pi-debug.log` for TUI debugging, as well as the last set of messages that were sent to the LLM.

View file

@ -60,6 +60,7 @@ import {
allTools, allTools,
bashTool, bashTool,
codingTools, codingTools,
createAllTools,
createBashTool, createBashTool,
createCodingTools, createCodingTools,
createEditTool, createEditTool,
@ -76,6 +77,7 @@ import {
readOnlyTools, readOnlyTools,
readTool, readTool,
type Tool, type Tool,
type ToolName,
writeTool, writeTool,
} from "./tools/index.js"; } from "./tools/index.js";
@ -567,8 +569,15 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
time("discoverContextFiles"); time("discoverContextFiles");
const autoResizeImages = settingsManager.getImageAutoResize(); const autoResizeImages = settingsManager.getImageAutoResize();
const builtInTools = options.tools ?? createCodingTools(cwd, { read: { autoResizeImages } }); // Create ALL built-in tools for the registry (hooks can enable any of them)
time("createCodingTools"); const allBuiltInToolsMap = createAllTools(cwd, { read: { autoResizeImages } });
// Determine initially active built-in tools (default: read, bash, edit, write)
const defaultActiveToolNames: ToolName[] = ["read", "bash", "edit", "write"];
const initialActiveToolNames: ToolName[] = options.tools
? options.tools.map((t) => t.name).filter((n): n is ToolName => n in allBuiltInToolsMap)
: defaultActiveToolNames;
const initialActiveBuiltInTools = initialActiveToolNames.map((name) => allBuiltInToolsMap[name]);
time("createAllTools");
let customToolsResult: CustomToolsLoadResult; let customToolsResult: CustomToolsLoadResult;
if (options.customTools !== undefined) { if (options.customTools !== undefined) {
@ -630,51 +639,61 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
})); }));
// Create tool registry mapping name -> tool (for hook getTools/setTools) // Create tool registry mapping name -> tool (for hook getTools/setTools)
// Cast to AgentTool since createCodingTools actually returns AgentTool[] (type is just Tool[]) // Registry contains ALL built-in tools so hooks can enable any of them
const toolRegistry = new Map<string, AgentTool>(); const toolRegistry = new Map<string, AgentTool>();
for (const tool of builtInTools as AgentTool[]) { for (const [name, tool] of Object.entries(allBuiltInToolsMap)) {
toolRegistry.set(tool.name, tool); toolRegistry.set(name, tool as AgentTool);
} }
for (const tool of wrappedCustomTools as AgentTool[]) { for (const tool of wrappedCustomTools as AgentTool[]) {
toolRegistry.set(tool.name, tool); toolRegistry.set(tool.name, tool);
} }
let allToolsArray: Tool[] = [...builtInTools, ...wrappedCustomTools]; // Initially active tools = active built-in + custom
let activeToolsArray: Tool[] = [...initialActiveBuiltInTools, ...wrappedCustomTools];
time("combineTools"); time("combineTools");
// Wrap tools with hooks if available // Wrap tools with hooks if available
let wrappedToolRegistry: Map<string, AgentTool> | undefined; let wrappedToolRegistry: Map<string, AgentTool> | undefined;
if (hookRunner) { if (hookRunner) {
allToolsArray = wrapToolsWithHooks(allToolsArray as AgentTool[], hookRunner); activeToolsArray = wrapToolsWithHooks(activeToolsArray as AgentTool[], hookRunner);
// Also create a wrapped version of the registry for setTools // Also create a wrapped version of the registry for setTools
wrappedToolRegistry = new Map<string, AgentTool>(); wrappedToolRegistry = new Map<string, AgentTool>();
for (const tool of allToolsArray as AgentTool[]) { for (const tool of activeToolsArray as AgentTool[]) {
wrappedToolRegistry.set(tool.name, tool); wrappedToolRegistry.set(tool.name, tool);
} }
} }
let systemPrompt: string; // Function to rebuild system prompt when tools change
// Captures static options (cwd, agentDir, skills, contextFiles, customPrompt)
const rebuildSystemPrompt = (toolNames: string[]): string => {
// Filter to valid tool names
const validToolNames = toolNames.filter((n): n is ToolName => n in allBuiltInToolsMap);
const defaultPrompt = buildSystemPromptInternal({ const defaultPrompt = buildSystemPromptInternal({
cwd, cwd,
agentDir, agentDir,
skills, skills,
contextFiles, contextFiles,
selectedTools: validToolNames,
}); });
time("buildSystemPrompt");
if (options.systemPrompt === undefined) { if (options.systemPrompt === undefined) {
systemPrompt = defaultPrompt; return defaultPrompt;
} else if (typeof options.systemPrompt === "string") { } else if (typeof options.systemPrompt === "string") {
systemPrompt = buildSystemPromptInternal({ return buildSystemPromptInternal({
cwd, cwd,
agentDir, agentDir,
skills, skills,
contextFiles, contextFiles,
selectedTools: validToolNames,
customPrompt: options.systemPrompt, customPrompt: options.systemPrompt,
}); });
} else { } else {
systemPrompt = options.systemPrompt(defaultPrompt); return options.systemPrompt(defaultPrompt);
} }
};
const systemPrompt = rebuildSystemPrompt(initialActiveToolNames);
time("buildSystemPrompt");
const slashCommands = options.slashCommands ?? discoverSlashCommands(cwd, agentDir); const slashCommands = options.slashCommands ?? discoverSlashCommands(cwd, agentDir);
time("discoverSlashCommands"); time("discoverSlashCommands");
@ -684,7 +703,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
systemPrompt, systemPrompt,
model, model,
thinkingLevel, thinkingLevel,
tools: allToolsArray, tools: activeToolsArray,
}, },
convertToLlm, convertToLlm,
transformContext: hookRunner transformContext: hookRunner
@ -730,6 +749,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
skillsSettings: settingsManager.getSkillsSettings(), skillsSettings: settingsManager.getSkillsSettings(),
modelRegistry, modelRegistry,
toolRegistry: wrappedToolRegistry ?? toolRegistry, toolRegistry: wrappedToolRegistry ?? toolRegistry,
rebuildSystemPrompt,
}); });
time("createAgentSession"); time("createAgentSession");