diff --git a/packages/coding-agent/src/core/extensions/loader.ts b/packages/coding-agent/src/core/extensions/loader.ts index a93f7c852..5de785279 100644 --- a/packages/coding-agent/src/core/extensions/loader.ts +++ b/packages/coding-agent/src/core/extensions/loader.ts @@ -28,6 +28,7 @@ import { createEventBus, type EventBus } from "../event-bus.ts"; import type { ExecOptions } from "../exec.ts"; import { execCommand } from "../exec.ts"; import { createSyntheticSourceInfo } from "../source-info.ts"; +import { time } from "../timings.ts"; import type { Extension, ExtensionAPI, @@ -431,6 +432,7 @@ async function loadExtension( try { const factory = await loadExtensionModule(resolvedPath, cacheToken); + time(`${extensionPath} module import`, "extensions"); if (!factory) { return { extension: null, error: `Extension does not export a valid factory function: ${extensionPath}` }; } @@ -438,6 +440,7 @@ async function loadExtension( const extension = createExtension(extensionPath, resolvedPath); const api = createExtensionAPI(extension, runtime, cwd, eventBus); await factory(api); + time(`${extensionPath} factory`, "extensions"); return { extension, error: null }; } catch (err) { @@ -460,6 +463,7 @@ export async function loadExtensionFromFactory( const resolvedCwd = resolvePath(cwd); const api = createExtensionAPI(extension, runtime, resolvedCwd, eventBus); await factory(api); + time(`${extensionPath} factory`, "extensions"); return extension; } diff --git a/packages/coding-agent/src/core/resource-loader.ts b/packages/coding-agent/src/core/resource-loader.ts index 18486ead9..a187af8d9 100644 --- a/packages/coding-agent/src/core/resource-loader.ts +++ b/packages/coding-agent/src/core/resource-loader.ts @@ -23,6 +23,7 @@ import { SettingsManager } from "./settings-manager.ts"; import type { Skill } from "./skills.ts"; import { loadSkills } from "./skills.ts"; import { createSourceInfo, type SourceInfo } from "./source-info.ts"; +import { resetTimings } from "./timings.ts"; export interface ResourceExtensionPaths { skillPaths?: Array<{ path: string; metadata: PathMetadata }>; @@ -338,6 +339,8 @@ export class DefaultResourceLoader implements ResourceLoader { } async reload(options?: ResourceLoaderReloadOptions): Promise { + resetTimings("extensions"); + if (this.loaded) { clearExtensionCache(); } diff --git a/packages/coding-agent/src/core/timings.ts b/packages/coding-agent/src/core/timings.ts index 0b3e5cf48..2e031e828 100644 --- a/packages/coding-agent/src/core/timings.ts +++ b/packages/coding-agent/src/core/timings.ts @@ -4,28 +4,47 @@ */ const ENABLED = process.env.PI_TIMING === "1"; -const timings: Array<{ label: string; ms: number }> = []; -let lastTime = Date.now(); - -export function resetTimings(): void { - if (!ENABLED) return; - timings.length = 0; - lastTime = Date.now(); +interface TimingNamespace { + timings: Array<{ label: string; ms: number }>; + lastTime: number; } -export function time(label: string): void { +type TimingLabel = "main" | "extensions"; + +const timingNamespaces = new Map(); + +export function resetTimings(namespace: TimingLabel = "main"): void { + if (!ENABLED) return; + timingNamespaces.set(namespace, { timings: [], lastTime: Date.now() }); +} + +export function time(label: string, namespace: TimingLabel = "main"): void { if (!ENABLED) return; const now = Date.now(); - timings.push({ label, ms: now - lastTime }); - lastTime = now; + + if (!timingNamespaces.has(namespace)) { + resetTimings(namespace); + } + + const timingNamespace = timingNamespaces.get(namespace)!; + timingNamespace.timings.push({ label, ms: now - timingNamespace.lastTime }); + timingNamespace.lastTime = now; +} + +function printTimingGroup(title: string, timings: TimingNamespace["timings"]): void { + const printableTimings = timings.filter((timing) => timing.ms >= 0); + if (printableTimings.length === 0) return; + console.error(`\n--- ${title} ---`); + for (const t of printableTimings) { + console.error(` ${t.label}: ${t.ms}ms`); + } + console.error(` TOTAL: ${printableTimings.reduce((a, b) => a + b.ms, 0)}ms`); + console.error(`${"-".repeat(title.length + 8)}\n`); } export function printTimings(): void { - if (!ENABLED || timings.length === 0) return; - console.error("\n--- Startup Timings ---"); - for (const t of timings) { - console.error(` ${t.label}: ${t.ms}ms`); + if (!ENABLED) return; + for (const [namespace, timingNamespace] of timingNamespaces) { + printTimingGroup(`Startup Timings: ${namespace}`, timingNamespace.timings); } - console.error(` TOTAL: ${timings.reduce((a, b) => a + b.ms, 0)}ms`); - console.error("------------------------\n"); }