tool registry, claude tool awareness, and model selector fix

- Add agent-agnostic tool registry (tool-registry.ts) that discovers tools from disk
- Embed tool-loader extension as platform infrastructure (ensure-tool-loader.ts)
- Inject tool context into Claude prompts on first message
- Add marketplace tool sync (sync-marketplace.ts)
- Fix model selector defaulting to claude-code when no model explicitly selected
- Exclude tool-loader-source.ts from tsconfig (Pi-specific deps)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-07 11:55:38 +00:00
co-authored by Claude Opus 4.6
parent e97b4b2459
commit 49cd559c8a
8 changed files with 565 additions and 11 deletions
+33 -4
View File
@@ -4,6 +4,8 @@ import type { PiEvent, MessageCost } from '../../api/pi/types';
import type { ClaudeSpawnParams, ClaudeSpawnStreamingParams, ClaudeCodeResult } from '../protocol';
import { getState, setClaudeSession, clearClaudeSession, getClaudeSession } from './state';
import { getProxySecret } from './proxy';
import { discoverTools } from '../../tool-registry';
import type { ToolDefinition } from '../../tool-registry';
const SEND_TIMEOUT_MS = 5 * 60 * 1000;
const PROXY_PORT = process.env.ANTHROPIC_PROXY_PORT ?? '5051';
@@ -11,6 +13,27 @@ const DATA_PATH = process.env.DATA_PATH ?? join(process.cwd(), 'data');
const getHomeDir = (email: string) => join(DATA_PATH, email, 'home');
function buildToolContext(email: string): string {
const tools = discoverTools(email);
if (tools.length === 0) return '';
const sections = tools.map((tool) => {
const lines = [`## ${tool.name}`, tool.description];
const inputEntries = Object.entries(tool.inputs);
if (inputEntries.length > 0) {
const inputParts = inputEntries.map(([name, input]) => {
const opt = input.optional ? ', optional' : '';
const vals = input.values ? `, values: ${input.values}` : '';
return `${name} (${input.type}${vals}${opt}): ${input.description}`;
});
lines.push(`Inputs: ${inputParts.join('; ')}`);
}
return lines.join('\n');
});
return `The following tools are available on this platform:\n\n${sections.join('\n\n---\n\n')}\n\n---\n\n`;
}
const toShellUsername = (username: string, email: string): string => {
const raw = username || email.split('@')[0]!;
return (
@@ -66,12 +89,15 @@ export async function spawnClaude(params: ClaudeSpawnParams): Promise<ClaudeCode
const homeDir = getHomeDir(email);
const shellUsername = toShellUsername(username, email);
const claudeArgs = [CLAUDE_BIN, '-p', prompt, '--dangerously-skip-permissions', '--output-format', 'json'];
const existingSession = getClaudeSession(sessionKey);
const isResume = !!existingSession;
const effectivePrompt = isResume ? prompt : buildToolContext(email) + prompt;
const claudeArgs = [CLAUDE_BIN, '-p', effectivePrompt, '--dangerously-skip-permissions', '--output-format', 'json'];
const subModel = params.model?.split('/')[1];
if (subModel) claudeArgs.push('--model', subModel);
const existingSession = getClaudeSession(sessionKey);
if (existingSession) {
claudeArgs.push('--resume', existingSession);
}
@@ -157,10 +183,14 @@ export async function spawnClaudeStreaming(
const homeDir = getHomeDir(email);
const workDir = cwd ?? homeDir;
const existingSession = getClaudeSession(sessionKey);
const isResume = !!existingSession;
const effectivePrompt = isResume ? prompt : buildToolContext(email) + prompt;
const claudeArgs = [
CLAUDE_BIN,
'-p',
prompt,
effectivePrompt,
'--dangerously-skip-permissions',
'--output-format',
'stream-json',
@@ -171,7 +201,6 @@ export async function spawnClaudeStreaming(
const subModel = params.model?.split('/')[1];
if (subModel) claudeArgs.push('--model', subModel);
const existingSession = getClaudeSession(sessionKey);
if (existingSession) {
claudeArgs.push('--resume', existingSession);
}