diff --git a/packages/native/src/native.ts b/packages/native/src/native.ts index a8f81b2f9..b310cef28 100644 --- a/packages/native/src/native.ts +++ b/packages/native/src/native.ts @@ -151,6 +151,3 @@ export const native = loadNative() as { parseStreamingJson: (text: string) => unknown; xxHash32: (input: string, seed: number) => number; }; - -/** True when the native addon loaded successfully. False on unsupported platforms. */ -export const nativeAvailable = _loadedSuccessfully; diff --git a/packages/pi-agent-core/src/proxy.ts b/packages/pi-agent-core/src/proxy.ts index ebeb7d926..07540ca7e 100644 --- a/packages/pi-agent-core/src/proxy.ts +++ b/packages/pi-agent-core/src/proxy.ts @@ -82,7 +82,7 @@ export interface ProxyStreamOptions extends SimpleStreamOptions { * }); * ``` */ -export function streamProxy(model: Model, context: Context, options: ProxyStreamOptions): ProxyMessageEventStream { +function streamProxy(model: Model, context: Context, options: ProxyStreamOptions): ProxyMessageEventStream { const stream = new ProxyMessageEventStream(); (async () => { diff --git a/packages/pi-ai/src/api-registry.ts b/packages/pi-ai/src/api-registry.ts index ca0e06107..fb76a11af 100644 --- a/packages/pi-ai/src/api-registry.ts +++ b/packages/pi-ai/src/api-registry.ts @@ -81,18 +81,6 @@ export function getApiProvider(api: Api): ApiProviderInternal | undefined { return apiProviderRegistry.get(api)?.provider; } -export function getApiProviders(): ApiProviderInternal[] { - return Array.from(apiProviderRegistry.values(), (entry) => entry.provider); -} - -export function unregisterApiProviders(sourceId: string): void { - for (const [api, entry] of apiProviderRegistry.entries()) { - if (entry.sourceId === sourceId) { - apiProviderRegistry.delete(api); - } - } -} - export function clearApiProviders(): void { apiProviderRegistry.clear(); } diff --git a/packages/pi-ai/src/providers/anthropic.ts b/packages/pi-ai/src/providers/anthropic.ts index 16e02efe9..1041c96c3 100644 --- a/packages/pi-ai/src/providers/anthropic.ts +++ b/packages/pi-ai/src/providers/anthropic.ts @@ -230,7 +230,7 @@ function isTransientNetworkError(error: unknown): boolean { * Checks: retry-after (seconds or RFC date), x-ratelimit-reset-requests, x-ratelimit-reset-tokens. * Returns undefined if no valid delay is found or if the delay is in the past. */ -export function extractRetryAfterMs(headers: Headers | { get(name: string): string | null }, errorText = ""): number | undefined { +function extractRetryAfterMs(headers: Headers | { get(name: string): string | null }, errorText = ""): number | undefined { const normalizeDelay = (ms: number): number | undefined => (ms > 0 ? Math.ceil(ms + 1000) : undefined); const retryAfter = headers.get("retry-after"); diff --git a/packages/pi-ai/src/providers/github-copilot-headers.ts b/packages/pi-ai/src/providers/github-copilot-headers.ts index 4f01a9d2a..a009f88e7 100644 --- a/packages/pi-ai/src/providers/github-copilot-headers.ts +++ b/packages/pi-ai/src/providers/github-copilot-headers.ts @@ -2,7 +2,7 @@ import type { Message } from "../types.js"; // Copilot expects X-Initiator to indicate whether the request is user-initiated // or agent-initiated (e.g. follow-up after assistant/tool messages). -export function inferCopilotInitiator(messages: Message[]): "user" | "agent" { +function inferCopilotInitiator(messages: Message[]): "user" | "agent" { const last = messages[messages.length - 1]; return last && last.role !== "user" ? "agent" : "user"; } diff --git a/packages/pi-ai/src/providers/google-gemini-cli.ts b/packages/pi-ai/src/providers/google-gemini-cli.ts index c533fc7d1..318c46fe0 100644 --- a/packages/pi-ai/src/providers/google-gemini-cli.ts +++ b/packages/pi-ai/src/providers/google-gemini-cli.ts @@ -112,7 +112,7 @@ const CLAUDE_THINKING_BETA_HEADER = "interleaved-thinking-2025-05-14"; * - "Please retry in Xs" or "Please retry in Xms" * - "retryDelay": "34.074824224s" (JSON field) */ -export function extractRetryDelay(errorText: string, response?: Response | Headers): number | undefined { +function extractRetryDelay(errorText: string, response?: Response | Headers): number | undefined { const normalizeDelay = (ms: number): number | undefined => (ms > 0 ? Math.ceil(ms + 1000) : undefined); const headers = response instanceof Headers ? response : response?.headers; @@ -865,7 +865,7 @@ export const streamSimpleGoogleGeminiCli: StreamFunction<"google-gemini-cli", Si } satisfies GoogleGeminiCliOptions); }; -export function buildRequest( +function buildRequest( model: Model<"google-gemini-cli">, context: Context, projectId: string, diff --git a/packages/pi-ai/src/providers/google-shared.ts b/packages/pi-ai/src/providers/google-shared.ts index 255928c81..e6a31771f 100644 --- a/packages/pi-ai/src/providers/google-shared.ts +++ b/packages/pi-ai/src/providers/google-shared.ts @@ -66,7 +66,7 @@ function resolveThoughtSignature(isSameProviderAndModel: boolean, signature: str /** * Models via Google APIs that require explicit tool call IDs in function calls/responses. */ -export function requiresToolCallId(modelId: string): boolean { +function requiresToolCallId(modelId: string): boolean { return modelId.startsWith("claude-") || modelId.startsWith("gpt-oss-"); } diff --git a/packages/pi-ai/src/providers/register-builtins.ts b/packages/pi-ai/src/providers/register-builtins.ts index e75f84de3..f8fbdae35 100644 --- a/packages/pi-ai/src/providers/register-builtins.ts +++ b/packages/pi-ai/src/providers/register-builtins.ts @@ -116,7 +116,7 @@ function streamSimpleBedrockLazy( return outer; } -export function registerBuiltInApiProviders(): void { +function registerBuiltInApiProviders(): void { registerApiProvider({ api: "anthropic-messages", stream: streamAnthropic, diff --git a/packages/pi-ai/src/utils/event-stream.ts b/packages/pi-ai/src/utils/event-stream.ts index f4a7ceba8..74947477e 100644 --- a/packages/pi-ai/src/utils/event-stream.ts +++ b/packages/pi-ai/src/utils/event-stream.ts @@ -80,8 +80,3 @@ export class AssistantMessageEventStream extends EventStream t.name === toolCall.name); - if (!tool) { - throw new Error(`Tool "${toolCall.name}" not found`); - } - return validateToolArguments(tool, toolCall); -} - /** * Validates tool call arguments against the tool's TypeBox schema * @param tool The tool definition with TypeBox schema diff --git a/packages/pi-coding-agent/src/config.ts b/packages/pi-coding-agent/src/config.ts index 70297cc16..c50960035 100644 --- a/packages/pi-coding-agent/src/config.ts +++ b/packages/pi-coding-agent/src/config.ts @@ -18,15 +18,15 @@ export const isBunBinary = import.meta.url.includes("$bunfs") || import.meta.url.includes("~BUN") || import.meta.url.includes("%7EBUN"); /** Detect if Bun is the runtime (compiled binary or bun run) */ -export const isBunRuntime = !!process.versions.bun; +const isBunRuntime = !!process.versions.bun; // ============================================================================= // Install Method Detection // ============================================================================= -export type InstallMethod = "bun-binary" | "npm" | "pnpm" | "yarn" | "bun" | "unknown"; +type InstallMethod = "bun-binary" | "npm" | "pnpm" | "yarn" | "bun" | "unknown"; -export function detectInstallMethod(): InstallMethod { +function detectInstallMethod(): InstallMethod { if (isBunBinary) { return "bun-binary"; } @@ -79,7 +79,7 @@ export function getUpdateInstruction(packageName: string): string { */ let _cachedPackageDir: string | undefined; -export function getPackageDir(): string { +function getPackageDir(): string { if (_cachedPackageDir !== undefined) return _cachedPackageDir; // Allow override via environment variable (useful for Nix/Guix where store paths tokenize poorly) @@ -138,7 +138,7 @@ export function getExportTemplateDir(): string { } /** Get path to package.json */ -export function getPackageJsonPath(): string { +function getPackageJsonPath(): string { return join(getPackageDir(), "package.json"); } @@ -219,11 +219,6 @@ export function getSettingsPath(): string { return join(getAgentDir(), "settings.json"); } -/** Get path to tools directory */ -export function getToolsDir(): string { - return join(getAgentDir(), "tools"); -} - /** Get path to managed binaries directory (fd, rg) */ export function getBinDir(): string { return join(getAgentDir(), "bin"); diff --git a/packages/pi-coding-agent/src/core/export-html/ansi-to-html.ts b/packages/pi-coding-agent/src/core/export-html/ansi-to-html.ts index 92a3974e0..2e779f5ea 100644 --- a/packages/pi-coding-agent/src/core/export-html/ansi-to-html.ts +++ b/packages/pi-coding-agent/src/core/export-html/ansi-to-html.ts @@ -195,7 +195,7 @@ const ANSI_REGEX = /\x1b\[([\d;]*)m/g; /** * Convert ANSI-escaped text to HTML with inline styles. */ -export function ansiToHtml(text: string): string { +function ansiToHtml(text: string): string { const style = createEmptyStyle(); let result = ""; let lastIndex = 0; diff --git a/packages/pi-coding-agent/src/core/extensions/runner.ts b/packages/pi-coding-agent/src/core/extensions/runner.ts index 5ff1392bf..5519e2d87 100644 --- a/packages/pi-coding-agent/src/core/extensions/runner.ts +++ b/packages/pi-coding-agent/src/core/extensions/runner.ts @@ -153,19 +153,6 @@ export type ReloadHandler = () => Promise; export type ShutdownHandler = () => void; -/** - * Helper function to emit session_shutdown event to extensions. - * Returns true if the event was emitted, false if there were no handlers. - */ -export async function emitSessionShutdownEvent(extensionRunner: ExtensionRunner | undefined): Promise { - if (extensionRunner?.hasHandlers("session_shutdown")) { - await extensionRunner.emit({ - type: "session_shutdown", - }); - return true; - } - return false; -} const noOpUIContext: ExtensionUIContext = { select: async () => undefined, diff --git a/packages/pi-coding-agent/src/core/keybindings.ts b/packages/pi-coding-agent/src/core/keybindings.ts index 29bc8b967..ed69936f4 100644 --- a/packages/pi-coding-agent/src/core/keybindings.ts +++ b/packages/pi-coding-agent/src/core/keybindings.ts @@ -50,7 +50,7 @@ export type KeybindingsConfig = { /** * Default application keybindings. */ -export const DEFAULT_APP_KEYBINDINGS: Record = { +const DEFAULT_APP_KEYBINDINGS: Record = { interrupt: "escape", clear: "ctrl+c", exit: "ctrl+d", @@ -75,7 +75,7 @@ export const DEFAULT_APP_KEYBINDINGS: Record = { /** * All default keybindings (app + editor). */ -export const DEFAULT_KEYBINDINGS: Required = { +const DEFAULT_KEYBINDINGS: Required = { ...DEFAULT_EDITOR_KEYBINDINGS, ...DEFAULT_APP_KEYBINDINGS, }; diff --git a/packages/pi-coding-agent/src/core/lsp/client.ts b/packages/pi-coding-agent/src/core/lsp/client.ts index d9b53088e..930dc8374 100644 --- a/packages/pi-coding-agent/src/core/lsp/client.ts +++ b/packages/pi-coding-agent/src/core/lsp/client.ts @@ -618,76 +618,6 @@ export async function ensureFileOpen(client: LspClient, filePath: string, signal } } -/** - * Sync in-memory content to the LSP client without reading from disk. - */ -export async function syncContent( - client: LspClient, - filePath: string, - content: string, - signal?: AbortSignal, -): Promise { - const uri = fileToUri(filePath); - const lockKey = `${client.name}:${uri}`; - throwIfAborted(signal); - - const existingLock = fileOperationLocks.get(lockKey); - if (existingLock) { - await untilAborted(signal, () => existingLock); - } - - const syncPromise = (async () => { - client.diagnostics.delete(uri); - - const info = client.openFiles.get(uri); - - if (!info) { - const languageId = detectLanguageId(filePath); - throwIfAborted(signal); - await sendNotification(client, "textDocument/didOpen", { - textDocument: { - uri, - languageId, - version: 1, - text: content, - }, - }); - client.openFiles.set(uri, { version: 1, languageId }); - client.lastActivity = Date.now(); - return; - } - - const version = ++info.version; - throwIfAborted(signal); - await sendNotification(client, "textDocument/didChange", { - textDocument: { uri, version }, - contentChanges: [{ text: content }], - }); - client.lastActivity = Date.now(); - })(); - - fileOperationLocks.set(lockKey, syncPromise); - try { - await syncPromise; - } finally { - fileOperationLocks.delete(lockKey); - } -} - -/** - * Notify LSP that a file was saved. - */ -export async function notifySaved(client: LspClient, filePath: string, signal?: AbortSignal): Promise { - const uri = fileToUri(filePath); - const info = client.openFiles.get(uri); - if (!info) return; - - throwIfAborted(signal); - await sendNotification(client, "textDocument/didSave", { - textDocument: { uri }, - }); - client.lastActivity = Date.now(); -} /** * Refresh a file in the LSP client. @@ -761,7 +691,7 @@ export function notifyFileChanged(filePath: string): void { /** * Shutdown a specific client by key. */ -export function shutdownClient(key: string): void { +function shutdownClient(key: string): void { const client = clients.get(key); if (!client) return; @@ -865,7 +795,7 @@ export async function sendRequest( return promise; } -export async function sendNotification(client: LspClient, method: string, params: unknown): Promise { +async function sendNotification(client: LspClient, method: string, params: unknown): Promise { const notification: LspJsonRpcNotification = { jsonrpc: "2.0", method, @@ -889,7 +819,7 @@ export async function sendNotification(client: LspClient, method: string, params /** * Shutdown all LSP clients. */ -export function shutdownAll(): void { +function shutdownAll(): void { const clientsToShutdown = Array.from(clients.values()); clients.clear(); diff --git a/packages/pi-coding-agent/src/core/lsp/config.ts b/packages/pi-coding-agent/src/core/lsp/config.ts index c17494dc9..97c40f990 100644 --- a/packages/pi-coding-agent/src/core/lsp/config.ts +++ b/packages/pi-coding-agent/src/core/lsp/config.ts @@ -319,14 +319,3 @@ export function getServersForFile(config: LspConfig, filePath: string): Array<[s }); } -export function getServerForFile(config: LspConfig, filePath: string): [string, ServerConfig] | null { - const servers = getServersForFile(config, filePath); - return servers.length > 0 ? servers[0] : null; -} - -export function hasCapability( - config: ServerConfig, - capability: keyof NonNullable, -): boolean { - return config.capabilities?.[capability] === true; -} diff --git a/packages/pi-coding-agent/src/core/lsp/edits.ts b/packages/pi-coding-agent/src/core/lsp/edits.ts index 12c7e39a4..cb5f5fa6d 100644 --- a/packages/pi-coding-agent/src/core/lsp/edits.ts +++ b/packages/pi-coding-agent/src/core/lsp/edits.ts @@ -11,7 +11,7 @@ import { uriToFile } from "./utils.js"; * Apply text edits to a string in-memory. * Edits are applied in reverse order (bottom-to-top) to preserve line/character indices. */ -export function applyTextEditsToString(content: string, edits: TextEdit[]): string { +function applyTextEditsToString(content: string, edits: TextEdit[]): string { const lines = content.split("\n"); // Sort edits in reverse order (bottom-to-top, right-to-left) diff --git a/packages/pi-coding-agent/src/core/lsp/lspmux.ts b/packages/pi-coding-agent/src/core/lsp/lspmux.ts index 0e9f6e638..952c9a06c 100644 --- a/packages/pi-coding-agent/src/core/lsp/lspmux.ts +++ b/packages/pi-coding-agent/src/core/lsp/lspmux.ts @@ -172,7 +172,7 @@ export interface LspmuxWrappedCommand { env?: Record; } -export function wrapWithLspmux( +function wrapWithLspmux( originalCommand: string, originalArgs: string[] | undefined, state: LspmuxState, diff --git a/packages/pi-coding-agent/src/core/lsp/utils.ts b/packages/pi-coding-agent/src/core/lsp/utils.ts index 8047789fa..da037cfda 100644 --- a/packages/pi-coding-agent/src/core/lsp/utils.ts +++ b/packages/pi-coding-agent/src/core/lsp/utils.ts @@ -192,7 +192,7 @@ const SEVERITY_NAMES: Record = { 4: "hint", }; -export function severityToString(severity?: DiagnosticSeverity): string { +function severityToString(severity?: DiagnosticSeverity): string { return SEVERITY_NAMES[severity ?? 1] ?? "unknown"; } @@ -211,21 +211,6 @@ export function sortDiagnostics(diagnostics: Diagnostic[]): Diagnostic[] { }); } -export function severityToIcon(severity?: DiagnosticSeverity): string { - switch (severity ?? 1) { - case 1: - return "[E]"; - case 2: - return "[W]"; - case 3: - return "[I]"; - case 4: - return "[H]"; - default: - return "[E]"; - } -} - function stripDiagnosticNoise(message: string): string { return message .split("\n") @@ -353,10 +338,6 @@ export function formatLocation(location: Location, cwd: string): string { return `${file}:${line}:${col}`; } -export function formatPosition(line: number, col: number): string { - return `${line}:${col}`; -} - // ============================================================================= // WorkspaceEdit Formatting // ============================================================================= @@ -397,15 +378,6 @@ export function formatWorkspaceEdit(edit: WorkspaceEdit, cwd: string): string[] return results; } -export function formatTextEdit(edit: TextEdit, maxLength = 50): string { - const range = `${edit.range.start.line + 1}:${edit.range.start.character + 1}`; - const preview = - edit.newText.length > maxLength - ? `${edit.newText.slice(0, maxLength).replace(/\n/g, "\\n")}…` - : edit.newText.replace(/\n/g, "\\n"); - return `line ${range} -> "${preview}"`; -} - // ============================================================================= // Symbol Formatting // ============================================================================= @@ -443,10 +415,6 @@ export function symbolKindToIcon(kind: SymbolKind): string { return `[${SYMBOL_KIND_LABELS[kind] ?? "?"}]`; } -export function symbolKindToName(kind: SymbolKind): string { - return SYMBOL_KIND_LABELS[kind] ?? "Unknown"; -} - export function formatDocumentSymbol(symbol: DocumentSymbol, indent = 0): string[] { const prefix = " ".repeat(indent); const icon = symbolKindToIcon(symbol.kind); diff --git a/packages/pi-coding-agent/src/core/messages.ts b/packages/pi-coding-agent/src/core/messages.ts index 762aa5b85..e3909a41e 100644 --- a/packages/pi-coding-agent/src/core/messages.ts +++ b/packages/pi-coding-agent/src/core/messages.ts @@ -8,20 +8,20 @@ import type { AgentMessage } from "@gsd/pi-agent-core"; import type { ImageContent, Message, TextContent } from "@gsd/pi-ai"; -export const COMPACTION_SUMMARY_PREFIX = `The conversation history before this point was compacted into the following summary: +const COMPACTION_SUMMARY_PREFIX = `The conversation history before this point was compacted into the following summary: `; -export const COMPACTION_SUMMARY_SUFFIX = ` +const COMPACTION_SUMMARY_SUFFIX = ` `; -export const BRANCH_SUMMARY_PREFIX = `The following is a summary of a branch that this conversation came back from: +const BRANCH_SUMMARY_PREFIX = `The following is a summary of a branch that this conversation came back from: `; -export const BRANCH_SUMMARY_SUFFIX = ``; +const BRANCH_SUMMARY_SUFFIX = ``; /** * Message type for bash executions via the ! command. @@ -79,7 +79,7 @@ declare module "@gsd/pi-agent-core" { /** * Convert a BashExecutionMessage to user message text for LLM context. */ -export function bashExecutionToText(msg: BashExecutionMessage): string { +function bashExecutionToText(msg: BashExecutionMessage): string { let text = `Ran \`${msg.command}\`\n`; if (msg.output) { text += `\`\`\`\n${msg.output}\n\`\`\``; diff --git a/packages/pi-coding-agent/src/core/model-registry.ts b/packages/pi-coding-agent/src/core/model-registry.ts index a38068ccb..08766af24 100644 --- a/packages/pi-coding-agent/src/core/model-registry.ts +++ b/packages/pi-coding-agent/src/core/model-registry.ts @@ -216,8 +216,6 @@ function applyModelOverride(model: Model, override: ModelOverride): Model = { +const defaultModelPerProvider: Record = { "amazon-bedrock": "us.anthropic.claude-opus-4-6-v1", anthropic: "claude-opus-4-6[1m]", openai: "gpt-5.4", @@ -143,9 +143,9 @@ function buildFallbackModel(provider: string, modelId: string, availableModels: * - If suffix is valid thinking level, use it and recurse on prefix * - If suffix is invalid, warn and recurse on prefix with "off" * - * @internal Exported for testing + * @internal */ -export function parseModelPattern( +function parseModelPattern( pattern: string, availableModels: Model[], options?: { allowInvalidThinkingLevelFallback?: boolean }, @@ -546,77 +546,3 @@ export async function findInitialModel(options: { // 5. No model found return { model: undefined, thinkingLevel: DEFAULT_THINKING_LEVEL, fallbackMessage: undefined }; } - -/** - * Restore model from session, with fallback to available models - */ -export async function restoreModelFromSession( - savedProvider: string, - savedModelId: string, - currentModel: Model | undefined, - shouldPrintMessages: boolean, - modelRegistry: ModelRegistry, -): Promise<{ model: Model | undefined; fallbackMessage: string | undefined }> { - const restoredModel = modelRegistry.find(savedProvider, savedModelId); - - // Check if restored model exists and has a valid API key - const hasApiKey = restoredModel ? !!(await modelRegistry.getApiKey(restoredModel)) : false; - - if (restoredModel && hasApiKey) { - if (shouldPrintMessages) { - console.log(chalk.dim(`Restored model: ${savedProvider}/${savedModelId}`)); - } - return { model: restoredModel, fallbackMessage: undefined }; - } - - // Model not found or no API key - fall back - const reason = !restoredModel ? "model no longer exists" : "no API key available"; - - if (shouldPrintMessages) { - console.error(chalk.yellow(`Warning: Could not restore model ${savedProvider}/${savedModelId} (${reason}).`)); - } - - // If we already have a model, use it as fallback - if (currentModel) { - if (shouldPrintMessages) { - console.log(chalk.dim(`Falling back to: ${currentModel.provider}/${currentModel.id}`)); - } - return { - model: currentModel, - fallbackMessage: `Could not restore model ${savedProvider}/${savedModelId} (${reason}). Using ${currentModel.provider}/${currentModel.id}.`, - }; - } - - // Try to find any available model - const availableModels = await modelRegistry.getAvailable(); - - if (availableModels.length > 0) { - // Try to find a default model from known providers - let fallbackModel: Model | undefined; - for (const provider of Object.keys(defaultModelPerProvider) as KnownProvider[]) { - const defaultId = defaultModelPerProvider[provider]; - const match = availableModels.find((m) => m.provider === provider && m.id === defaultId); - if (match) { - fallbackModel = match; - break; - } - } - - // If no default found, use first available - if (!fallbackModel) { - fallbackModel = availableModels[0]; - } - - if (shouldPrintMessages) { - console.log(chalk.dim(`Falling back to: ${fallbackModel.provider}/${fallbackModel.id}`)); - } - - return { - model: fallbackModel, - fallbackMessage: `Could not restore model ${savedProvider}/${savedModelId} (${reason}). Using ${fallbackModel.provider}/${fallbackModel.id}.`, - }; - } - - // No models available - return { model: undefined, fallbackMessage: undefined }; -} diff --git a/packages/pi-coding-agent/src/core/prompt-templates.ts b/packages/pi-coding-agent/src/core/prompt-templates.ts index d7ed0953c..6f7ab64c6 100644 --- a/packages/pi-coding-agent/src/core/prompt-templates.ts +++ b/packages/pi-coding-agent/src/core/prompt-templates.ts @@ -19,7 +19,7 @@ export interface PromptTemplate { * Parse command arguments respecting quoted strings (bash-style) * Returns array of arguments */ -export function parseCommandArgs(argsString: string): string[] { +function parseCommandArgs(argsString: string): string[] { const args: string[] = []; let current = ""; let inQuote: string | null = null; @@ -63,7 +63,7 @@ export function parseCommandArgs(argsString: string): string[] { * Note: Replacement happens on the template string only. Argument values * containing patterns like $1, $@, or $ARGUMENTS are NOT recursively substituted. */ -export function substituteArgs(content: string, args: string[]): string { +function substituteArgs(content: string, args: string[]): string { let result = content; // Replace $1, $2, etc. with positional args FIRST (before wildcards) diff --git a/packages/pi-coding-agent/src/core/session-manager.ts b/packages/pi-coding-agent/src/core/session-manager.ts index ef60d4120..50b1bba42 100644 --- a/packages/pi-coding-agent/src/core/session-manager.ts +++ b/packages/pi-coding-agent/src/core/session-manager.ts @@ -577,8 +577,7 @@ function resolveBlobRefsInEntries(entries: FileEntry[], blobStore: BlobStore): v } } -/** Exported for testing */ -export function loadEntriesFromFile(filePath: string): FileEntry[] { +function loadEntriesFromFile(filePath: string): FileEntry[] { if (!existsSync(filePath)) return []; const content = readFileSync(filePath, "utf8"); @@ -620,8 +619,7 @@ function isValidSessionFile(filePath: string): boolean { } } -/** Exported for testing */ -export function findMostRecentSession(sessionDir: string): string | null { +function findMostRecentSession(sessionDir: string): string | null { try { const files = readdirSync(sessionDir) .filter((f) => f.endsWith(".jsonl")) diff --git a/packages/pi-coding-agent/src/core/settings-manager.ts b/packages/pi-coding-agent/src/core/settings-manager.ts index 603cd2e93..74ced2cfe 100644 --- a/packages/pi-coding-agent/src/core/settings-manager.ts +++ b/packages/pi-coding-agent/src/core/settings-manager.ts @@ -195,7 +195,7 @@ export interface SettingsError { error: Error; } -export class FileSettingsStorage implements SettingsStorage { +class FileSettingsStorage implements SettingsStorage { private globalSettingsPath: string; private projectSettingsPath: string; @@ -262,7 +262,7 @@ export class FileSettingsStorage implements SettingsStorage { } } -export class InMemorySettingsStorage implements SettingsStorage { +class InMemorySettingsStorage implements SettingsStorage { private global: string | undefined; private project: string | undefined; diff --git a/packages/pi-coding-agent/src/migrations.ts b/packages/pi-coding-agent/src/migrations.ts index 2721ed8d2..a59e1895b 100644 --- a/packages/pi-coding-agent/src/migrations.ts +++ b/packages/pi-coding-agent/src/migrations.ts @@ -16,7 +16,7 @@ const EXTENSIONS_DOC_URL = "https://github.com/badlogic/pi-mono/blob/main/packag * * @returns Array of provider names that were migrated */ -export function migrateAuthToAuthJson(): string[] { +function migrateAuthToAuthJson(): string[] { const agentDir = getAgentDir(); const authPath = join(agentDir, "auth.json"); const oauthPath = join(agentDir, "oauth.json"); @@ -79,7 +79,7 @@ export function migrateAuthToAuthJson(): string[] { * * See: https://github.com/badlogic/pi-mono/issues/320 */ -export function migrateSessionsFromAgentRoot(): void { +function migrateSessionsFromAgentRoot(): void { const agentDir = getAgentDir(); // Find all .jsonl files directly in agentDir (not in subdirectories) diff --git a/packages/pi-coding-agent/src/modes/interactive/components/session-selector-search.ts b/packages/pi-coding-agent/src/modes/interactive/components/session-selector-search.ts index b780d92e5..ad0b74cc4 100644 --- a/packages/pi-coding-agent/src/modes/interactive/components/session-selector-search.ts +++ b/packages/pi-coding-agent/src/modes/interactive/components/session-selector-search.ts @@ -36,7 +36,7 @@ function matchesNameFilter(session: SessionInfo, filter: NameFilter): boolean { return hasSessionName(session); } -export function parseSearchQuery(query: string): ParsedSearchQuery { +function parseSearchQuery(query: string): ParsedSearchQuery { const trimmed = query.trim(); if (!trimmed) { return { mode: "tokens", tokens: [], regex: null }; @@ -113,7 +113,7 @@ export function parseSearchQuery(query: string): ParsedSearchQuery { return { mode: "tokens", tokens, regex: null }; } -export function matchSession(session: SessionInfo, parsed: ParsedSearchQuery): MatchResult { +function matchSession(session: SessionInfo, parsed: ParsedSearchQuery): MatchResult { const text = getSessionSearchText(session); if (parsed.mode === "regex") { diff --git a/packages/pi-coding-agent/src/modes/interactive/theme/theme.ts b/packages/pi-coding-agent/src/modes/interactive/theme/theme.ts index 676d672d9..e0bf4d41b 100644 --- a/packages/pi-coding-agent/src/modes/interactive/theme/theme.ts +++ b/packages/pi-coding-agent/src/modes/interactive/theme/theme.ts @@ -880,14 +880,6 @@ export function getResolvedThemeColors(themeName?: string): Record v2 */ -export function compareVersions(v1: ChangelogEntry, v2: ChangelogEntry): number { +function compareVersions(v1: ChangelogEntry, v2: ChangelogEntry): number { if (v1.major !== v2.major) return v1.major - v2.major; if (v1.minor !== v2.minor) return v1.minor - v2.minor; return v1.patch - v2.patch; diff --git a/packages/pi-coding-agent/src/utils/clipboard-image.ts b/packages/pi-coding-agent/src/utils/clipboard-image.ts index 11616c1c9..ca78534e6 100644 --- a/packages/pi-coding-agent/src/utils/clipboard-image.ts +++ b/packages/pi-coding-agent/src/utils/clipboard-image.ts @@ -14,7 +14,7 @@ const DEFAULT_LIST_TIMEOUT_MS = 1000; const DEFAULT_READ_TIMEOUT_MS = 3000; const DEFAULT_MAX_BUFFER_BYTES = 50 * 1024 * 1024; -export function isWaylandSession(env: NodeJS.ProcessEnv = process.env): boolean { +function isWaylandSession(env: NodeJS.ProcessEnv = process.env): boolean { return Boolean(env.WAYLAND_DISPLAY) || env.XDG_SESSION_TYPE === "wayland"; } diff --git a/packages/pi-coding-agent/src/utils/photon.ts b/packages/pi-coding-agent/src/utils/photon.ts index 6c320705e..cdffed0a7 100644 --- a/packages/pi-coding-agent/src/utils/photon.ts +++ b/packages/pi-coding-agent/src/utils/photon.ts @@ -1,139 +1,2 @@ -/** - * Photon image processing wrapper. - * - * This module provides a unified interface to @silvia-odwyer/photon-node that works in: - * 1. Node.js (development, npm run build) - * 2. Bun compiled binaries (standalone distribution) - * - * The challenge: photon-node's CJS entry uses fs.readFileSync(__dirname + '/photon_rs_bg.wasm') - * which bakes the build machine's absolute path into Bun compiled binaries. - * - * Solution: - * 1. Patch fs.readFileSync to redirect missing photon_rs_bg.wasm reads - * 2. Copy photon_rs_bg.wasm next to the executable in build:binary - */ - -import type { PathOrFileDescriptor } from "fs"; -import { createRequire } from "module"; -import * as path from "path"; -import { fileURLToPath } from "url"; - -const require = createRequire(import.meta.url); -const fs = require("fs") as typeof import("fs"); - // Re-export types from the main package export type { PhotonImage as PhotonImageType } from "@silvia-odwyer/photon-node"; - -type ReadFileSync = typeof fs.readFileSync; - -const WASM_FILENAME = "photon_rs_bg.wasm"; - -// Lazy-loaded photon module -let photonModule: typeof import("@silvia-odwyer/photon-node") | null = null; -let loadPromise: Promise | null = null; - -function pathOrNull(file: PathOrFileDescriptor): string | null { - if (typeof file === "string") { - return file; - } - if (file instanceof URL) { - return fileURLToPath(file); - } - return null; -} - -function getFallbackWasmPaths(): string[] { - const execDir = path.dirname(process.execPath); - return [ - path.join(execDir, WASM_FILENAME), - path.join(execDir, "photon", WASM_FILENAME), - path.join(process.cwd(), WASM_FILENAME), - ]; -} - -function patchPhotonWasmRead(): () => void { - const originalReadFileSync: ReadFileSync = fs.readFileSync.bind(fs); - const fallbackPaths = getFallbackWasmPaths(); - const mutableFs = fs as { readFileSync: ReadFileSync }; - - const patchedReadFileSync: ReadFileSync = ((...args: Parameters) => { - const [file, options] = args; - const resolvedPath = pathOrNull(file); - - if (resolvedPath?.endsWith(WASM_FILENAME)) { - try { - return originalReadFileSync(...args); - } catch (error) { - const err = error as NodeJS.ErrnoException; - if (err?.code && err.code !== "ENOENT") { - throw error; - } - - for (const fallbackPath of fallbackPaths) { - if (!fs.existsSync(fallbackPath)) { - continue; - } - if (options === undefined) { - return originalReadFileSync(fallbackPath); - } - return originalReadFileSync(fallbackPath, options); - } - - throw error; - } - } - - return originalReadFileSync(...args); - }) as ReadFileSync; - - try { - mutableFs.readFileSync = patchedReadFileSync; - } catch { - Object.defineProperty(fs, "readFileSync", { - value: patchedReadFileSync, - writable: true, - configurable: true, - }); - } - - return () => { - try { - mutableFs.readFileSync = originalReadFileSync; - } catch { - Object.defineProperty(fs, "readFileSync", { - value: originalReadFileSync, - writable: true, - configurable: true, - }); - } - }; -} - -/** - * Load the photon module asynchronously. - * Returns cached module on subsequent calls. - */ -export async function loadPhoton(): Promise { - if (photonModule) { - return photonModule; - } - - if (loadPromise) { - return loadPromise; - } - - loadPromise = (async () => { - const restoreReadFileSync = patchPhotonWasmRead(); - try { - photonModule = await import("@silvia-odwyer/photon-node"); - return photonModule; - } catch { - photonModule = null; - return photonModule; - } finally { - restoreReadFileSync(); - } - })(); - - return loadPromise; -} diff --git a/packages/pi-coding-agent/src/utils/tools-manager.ts b/packages/pi-coding-agent/src/utils/tools-manager.ts index f51e042b4..468f8450f 100644 --- a/packages/pi-coding-agent/src/utils/tools-manager.ts +++ b/packages/pi-coding-agent/src/utils/tools-manager.ts @@ -80,7 +80,7 @@ function commandExists(cmd: string): boolean { } // Get the path to a tool (system-wide or in our tools dir) -export function getToolPath(tool: "fd" | "rg"): string | null { +function getToolPath(tool: "fd" | "rg"): string | null { const config = TOOLS[tool]; if (!config) return null; diff --git a/packages/pi-tui/src/components/editor.ts b/packages/pi-tui/src/components/editor.ts index df3c4bb64..0b4b2b525 100644 --- a/packages/pi-tui/src/components/editor.ts +++ b/packages/pi-tui/src/components/editor.ts @@ -28,7 +28,7 @@ export interface TextChunk { * @param maxWidth - Maximum visible width per chunk * @returns Array of chunks with text and position information */ -export function wordWrapLine(line: string, maxWidth: number): TextChunk[] { +function wordWrapLine(line: string, maxWidth: number): TextChunk[] { if (!line || maxWidth <= 0) { return [{ text: "", startIndex: 0, endIndex: 0 }]; } diff --git a/packages/pi-tui/src/utils.ts b/packages/pi-tui/src/utils.ts index 430710aed..de59d4dc3 100644 --- a/packages/pi-tui/src/utils.ts +++ b/packages/pi-tui/src/utils.ts @@ -33,49 +33,6 @@ export function isPunctuationChar(char: string): boolean { return PUNCTUATION_REGEX.test(char); } -/** - * Extract ANSI escape sequences from a string at the given position. - */ -export function extractAnsiCode(str: string, pos: number): { code: string; length: number } | null { - if (pos >= str.length || str[pos] !== "\x1b") return null; - - const next = str[pos + 1]; - - // CSI sequence: ESC [ ... m/G/K/H/J - if (next === "[") { - let j = pos + 2; - while (j < str.length && !/[mGKHJ]/.test(str[j]!)) j++; - if (j < str.length) return { code: str.substring(pos, j + 1), length: j + 1 - pos }; - return null; - } - - // OSC sequence: ESC ] ... BEL or ESC ] ... ST (ESC \) - // Used for hyperlinks (OSC 8), window titles, etc. - if (next === "]") { - let j = pos + 2; - while (j < str.length) { - if (str[j] === "\x07") return { code: str.substring(pos, j + 1), length: j + 1 - pos }; - if (str[j] === "\x1b" && str[j + 1] === "\\") return { code: str.substring(pos, j + 2), length: j + 2 - pos }; - j++; - } - return null; - } - - // APC sequence: ESC _ ... BEL or ESC _ ... ST (ESC \) - // Used for cursor marker and application-specific commands - if (next === "_") { - let j = pos + 2; - while (j < str.length) { - if (str[j] === "\x07") return { code: str.substring(pos, j + 1), length: j + 1 - pos }; - if (str[j] === "\x1b" && str[j + 1] === "\\") return { code: str.substring(pos, j + 2), length: j + 2 - pos }; - j++; - } - return null; - } - - return null; -} - // --------------------------------------------------------------------------- // Native text module wrappers // ---------------------------------------------------------------------------