diff --git a/src/resources/extensions/gsd/commands.ts b/src/resources/extensions/gsd/commands.ts index 8c93cd0f8..3131659b7 100644 --- a/src/resources/extensions/gsd/commands.ts +++ b/src/resources/extensions/gsd/commands.ts @@ -1803,7 +1803,7 @@ async function handleDryRun(ctx: ExtensionCommandContext, basePath: string): Pro const { getLedger, getProjectTotals, formatCost, formatTokenCount, loadLedgerFromDisk } = await import("./metrics.js"); const { loadEffectiveGSDPreferences: loadPrefs } = await import("./preferences.js"); - const { formatDuration } = await import("./history.js"); + const { formatDuration } = await import("../shared/format-utils.js"); const ledger = getLedger(); const units = ledger?.units ?? loadLedgerFromDisk(basePath)?.units ?? []; diff --git a/src/resources/extensions/gsd/export-html.ts b/src/resources/extensions/gsd/export-html.ts index d86e87278..df7eb316d 100644 --- a/src/resources/extensions/gsd/export-html.ts +++ b/src/resources/extensions/gsd/export-html.ts @@ -25,7 +25,7 @@ import type { VisualizerMilestone, VisualizerSlice, } from './visualizer-data.js'; -import { formatDuration } from './history.js'; +import { formatDuration } from '../shared/format-utils.js'; import { formatCost, formatTokenCount } from './metrics.js'; // ─── Public API ──────────────────────────────────────────────────────────────── diff --git a/src/resources/extensions/gsd/export.ts b/src/resources/extensions/gsd/export.ts index c7f9bc290..5f28998cb 100644 --- a/src/resources/extensions/gsd/export.ts +++ b/src/resources/extensions/gsd/export.ts @@ -10,7 +10,7 @@ import { } from "./metrics.js"; import type { UnitMetrics } from "./metrics.js"; import { gsdRoot } from "./paths.js"; -import { formatDuration } from "./history.js"; +import { formatDuration } from "../shared/format-utils.js"; /** * Write an export file directly, without requiring an ExtensionCommandContext. diff --git a/src/resources/extensions/gsd/forensics.ts b/src/resources/extensions/gsd/forensics.ts index 0efe2e326..c3bd714d2 100644 --- a/src/resources/extensions/gsd/forensics.ts +++ b/src/resources/extensions/gsd/forensics.ts @@ -27,7 +27,7 @@ import { deriveState } from "./state.js"; import { isAutoActive } from "./auto.js"; import { loadPrompt } from "./prompt-loader.js"; import { gsdRoot } from "./paths.js"; -import { formatDuration } from "./history.js"; +import { formatDuration } from "../shared/format-utils.js"; import { getAutoWorktreePath } from "./auto-worktree.js"; // ─── Types ──────────────────────────────────────────────────────────────────── diff --git a/src/resources/extensions/gsd/history.ts b/src/resources/extensions/gsd/history.ts index 6d9b08af6..c50e7eaa5 100644 --- a/src/resources/extensions/gsd/history.ts +++ b/src/resources/extensions/gsd/history.ts @@ -2,6 +2,7 @@ // Human-readable display of past auto-mode unit executions. import type { ExtensionCommandContext } from "@gsd/pi-coding-agent"; +import { formatDuration } from "../shared/format-utils.js"; import { getLedger, getProjectTotals, formatCost, formatTokenCount, aggregateBySlice, aggregateByPhase, aggregateByModel, loadLedgerFromDisk, @@ -128,18 +129,6 @@ function showModelBreakdown(units: UnitMetrics[], ctx: ExtensionCommandContext): // ─── Formatting helpers ────────────────────────────────────────────────────── -export function formatDuration(ms: number): string { - if (ms < 1000) return `${ms}ms`; - const secs = Math.floor(ms / 1000); - if (secs < 60) return `${secs}s`; - const mins = Math.floor(secs / 60); - const remSecs = secs % 60; - if (mins < 60) return `${mins}m ${remSecs}s`; - const hours = Math.floor(mins / 60); - const remMins = mins % 60; - return `${hours}h ${remMins}m`; -} - function formatRelativeTime(timestamp: number): string { const diff = Date.now() - timestamp; if (diff < 60_000) return "just now"; diff --git a/src/resources/extensions/gsd/reports.ts b/src/resources/extensions/gsd/reports.ts index c31d73bff..4901456db 100644 --- a/src/resources/extensions/gsd/reports.ts +++ b/src/resources/extensions/gsd/reports.ts @@ -18,7 +18,7 @@ import { writeFileSync, readFileSync, mkdirSync, existsSync } from 'node:fs'; import { join, basename } from 'node:path'; import { gsdRoot } from './paths.js'; import { formatCost, formatTokenCount } from './metrics.js'; -import { formatDuration } from './history.js'; +import { formatDuration } from '../shared/format-utils.js'; // ─── Types ──────────────────────────────────────────────────────────────────── diff --git a/src/resources/extensions/shared/format-utils.ts b/src/resources/extensions/shared/format-utils.ts index 6445552ca..f184c1ad3 100644 --- a/src/resources/extensions/shared/format-utils.ts +++ b/src/resources/extensions/shared/format-utils.ts @@ -11,6 +11,7 @@ import { truncateToWidth, visibleWidth } from "@gsd/pi-tui"; /** Format a millisecond duration as a compact human-readable string. */ export function formatDuration(ms: number): string { + if (ms < 1000) return `${ms}ms`; const s = Math.floor(ms / 1000); if (s < 60) return `${s}s`; const m = Math.floor(s / 60);