refactor(gsd): unify duplicate padRight/truncate into shared format-utils (#1045)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
518ccaf8a8
commit
9488865b9e
3 changed files with 16 additions and 17 deletions
|
|
@ -2,7 +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 { formatDuration, padRight, truncateWithEllipsis } from "../shared/format-utils.js";
|
||||
import {
|
||||
getLedger, getProjectTotals, formatCost, formatTokenCount,
|
||||
aggregateBySlice, aggregateByPhase, aggregateByModel, loadLedgerFromDisk,
|
||||
|
|
@ -58,7 +58,7 @@ export async function handleHistory(args: string, ctx: ExtensionCommandContext,
|
|||
lines.push(
|
||||
padRight(formatRelativeTime(u.finishedAt), 14) +
|
||||
padRight(u.type, 20) +
|
||||
padRight(truncate(u.id, 15), 16) +
|
||||
padRight(truncateWithEllipsis(u.id, 15), 16) +
|
||||
padRight(shortModel(u.model), 14) +
|
||||
padRight(formatCost(u.cost), 10) +
|
||||
padRight(formatTokenCount(u.tokens.total), 10) +
|
||||
|
|
@ -141,10 +141,3 @@ function shortModel(model: string): string {
|
|||
return model.replace(/^claude-/, "").replace(/^anthropic\//, "");
|
||||
}
|
||||
|
||||
function truncate(s: string, maxLen: number): string {
|
||||
return s.length > maxLen ? s.slice(0, maxLen - 1) + "…" : s;
|
||||
}
|
||||
|
||||
function padRight(s: string, len: number): string {
|
||||
return s.length >= len ? s.slice(0, len) : s + " ".repeat(len - s.length);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@
|
|||
|
||||
import { readFileSync, readdirSync, existsSync, statSync } from "node:fs";
|
||||
import { basename, join } from "node:path";
|
||||
import { truncateWithEllipsis } from "../shared/format-utils.js";
|
||||
import { nativeParseJsonlTail } from "./native-parser-bridge.js";
|
||||
import { MAX_JSONL_BYTES, parseJSONL } from "./jsonl-utils.js";
|
||||
import { nativeWorkingTreeStatus, nativeDiffStat } from "./native-git-bridge.js";
|
||||
|
|
@ -366,7 +367,7 @@ function formatRecoveryPrompt(
|
|||
sections.push("", "### Commands Already Run");
|
||||
for (const c of significantCommands.slice(-10)) {
|
||||
const status = c.failed ? " ❌" : " ✓";
|
||||
sections.push(`- \`${truncate(c.command, 120)}\`${status}`);
|
||||
sections.push(`- \`${truncateWithEllipsis(c.command, 121)}\`${status}`);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -374,7 +375,7 @@ function formatRecoveryPrompt(
|
|||
if (trace.errors.length > 0) {
|
||||
sections.push(
|
||||
"", "### Errors Before Interruption",
|
||||
...trace.errors.slice(-3).map(e => `- ${truncate(e, 200)}`),
|
||||
...trace.errors.slice(-3).map(e => `- ${truncateWithEllipsis(e, 201)}`),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -440,7 +441,7 @@ function compressToolCallTrace(calls: ToolCall[]): string {
|
|||
if (call.name === "write" || call.name === "edit") {
|
||||
lines.push(`${num}. ${call.name} \`${call.input.path || "?"}\`${err}`);
|
||||
} else if (call.name === "bash" || call.name === "bg_shell") {
|
||||
const cmd = truncate(String(call.input.command || ""), 80);
|
||||
const cmd = truncateWithEllipsis(String(call.input.command || ""), 81);
|
||||
lines.push(`${num}. ${call.name}: \`${cmd}\`${err}`);
|
||||
} else {
|
||||
lines.push(`${num}. ${call.name}${err}`);
|
||||
|
|
@ -459,7 +460,7 @@ function formatTraceSummary(trace: ExecutionTrace): string {
|
|||
parts.push(`Files written: ${trace.filesWritten.map(f => `\`${f}\``).join(", ")}`);
|
||||
}
|
||||
if (trace.commandsRun.length > 0) {
|
||||
const cmds = trace.commandsRun.slice(-5).map(c => `\`${truncate(c.command, 80)}\`${c.failed ? " ❌" : ""}`);
|
||||
const cmds = trace.commandsRun.slice(-5).map(c => `\`${truncateWithEllipsis(c.command, 81)}\`${c.failed ? " ❌" : ""}`);
|
||||
parts.push(`Commands run: ${cmds.join(", ")}`);
|
||||
}
|
||||
if (trace.errors.length > 0) {
|
||||
|
|
@ -517,7 +518,7 @@ function redactInput(name: string, input: Record<string, unknown>): Record<strin
|
|||
const safe: Record<string, unknown> = {};
|
||||
for (const [key, value] of Object.entries(input)) {
|
||||
if (key === "content" || key === "oldText" || key === "newText") {
|
||||
safe[key] = typeof value === "string" ? truncate(value, 100) : "[redacted]";
|
||||
safe[key] = typeof value === "string" ? truncateWithEllipsis(value, 101) : "[redacted]";
|
||||
} else {
|
||||
safe[key] = value;
|
||||
}
|
||||
|
|
@ -533,6 +534,3 @@ function findLast<T>(arr: T[], predicate: (item: T) => boolean): T | undefined {
|
|||
return undefined;
|
||||
}
|
||||
|
||||
function truncate(s: string, max: number): string {
|
||||
return s.length > max ? s.slice(0, max) + "…" : s;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -70,6 +70,14 @@ export function fitColumns(parts: string[], width: number, separator = " "): st
|
|||
return truncateToWidth(result, width);
|
||||
}
|
||||
|
||||
// ─── Text Truncation ─────────────────────────────────────────────────────────
|
||||
|
||||
/** Truncate a string to `maxLength` characters, replacing the last character with an ellipsis if needed. */
|
||||
export function truncateWithEllipsis(text: string, maxLength: number): string {
|
||||
if (text.length <= maxLength) return text
|
||||
return text.slice(0, maxLength - 1) + "…"
|
||||
}
|
||||
|
||||
// ─── Data Visualization ───────────────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue