diff --git a/packages/coding-agent/CHANGELOG.md b/packages/coding-agent/CHANGELOG.md index 46dbe1233..fc38bfb87 100644 --- a/packages/coding-agent/CHANGELOG.md +++ b/packages/coding-agent/CHANGELOG.md @@ -40,6 +40,7 @@ - Changed Read tool title to display line range when offset or limit arguments are provided - Changed worker instantiation to use direct URL import instead of pre-bundled worker files - Changed `omp commit` to use agentic mode by default with tool-based git inspection +- Changed agentic commit progress output to show real-time thinking previews and structured tool argument details ## [8.0.0] - 2026-01-23 ### Added diff --git a/packages/coding-agent/src/commit/agentic/agent.ts b/packages/coding-agent/src/commit/agentic/agent.ts index 553c9318a..dc151675d 100644 --- a/packages/coding-agent/src/commit/agentic/agent.ts +++ b/packages/coding-agent/src/commit/agentic/agent.ts @@ -57,15 +57,33 @@ export async function runCommitAgentSession(input: CommitAgentInput): Promise }>(); + const writeThinkingLine = (text: string) => { + const line = chalk.dim(`… ${text}`); + process.stdout.write(`\r\x1b[2K${line}`); + thinkingLineActive = true; + }; + const clearThinkingLine = () => { + if (!thinkingLineActive) return; + process.stdout.write("\r\x1b[2K"); + thinkingLineActive = false; + }; const unsubscribe = session.subscribe((event: AgentSessionEvent) => { switch (event.type) { case "message_start": if (event.message.role === "assistant") { isThinking = true; - writeStdout(chalk.dim("… thinking")); + thinkingLineActive = false; } break; + case "message_update": { + if (event.message?.role !== "assistant") break; + const preview = extractMessagePreview(event.message?.content ?? []); + if (!preview) break; + writeThinkingLine(preview); + break; + } case "tool_execution_start": toolCalls += 1; toolArgsById.set(event.toolCallId, { name: event.toolName, args: event.args }); @@ -75,16 +93,24 @@ export async function runCommitAgentSession(input: CommitAgentInput): Promise): string { +function extractMessagePreview(content: Array<{ type: string; text?: string }>): string | null { + const textBlocks = content + .filter((block) => block.type === "text" && typeof block.text === "string") + .map((block) => block.text?.trim()) + .filter((value): value is string => Boolean(value)); + if (textBlocks.length === 0) return null; + const combined = textBlocks.join(" ").replace(/\s+/g, " ").trim(); + return truncateToolArg(combined); +} + +function formatToolLabel(toolName: string): string { const displayName = toolName .split(/[_-]/) .map((segment) => segment.charAt(0).toUpperCase() + segment.slice(1)) .join(""); - if (!args) return displayName; - const argValue = extractToolArgument(args); - if (!argValue) return displayName; - return `${displayName}(${argValue})`; + return displayName; } -function extractToolArgument(args: Record): string | null { - const candidates = ["path", "file", "pattern", "query", "url", "command"]; - for (const key of candidates) { +function formatToolArgs(args?: Record): string | null { + if (!args || Object.keys(args).length === 0) return null; + const textParts: string[] = []; + const pushIf = (key: string, label = key) => { const value = args[key]; if (typeof value === "string" && value.trim()) { - return truncateToolArg(value); + textParts.push(`${label}: ${value.trim()}`); } - } + }; + pushIf("summary"); + pushIf("type"); + pushIf("scope"); + pushIf("file"); + pushIf("path"); + pushIf("pattern"); + pushIf("query"); + pushIf("url"); + pushIf("command"); const files = args.files; if (Array.isArray(files) && files.length > 0) { - const first = typeof files[0] === "string" ? files[0] : String(files[0]); - const suffix = files.length > 1 ? ` +${files.length - 1}` : ""; - return truncateToolArg(`${first}${suffix}`); + const list = files.map((file) => String(file)); + textParts.push(`files: ${list.join(", ")}`); } - return null; + const hunks = args.hunks; + if (Array.isArray(hunks) && hunks.length > 0) { + textParts.push(`hunks: ${hunks.join(", ")}`); + } + if (textParts.length > 0) { + return textParts.join("\n"); + } + try { + return JSON.stringify(args, null, 2); + } catch { + return String(args); + } +} + +function formatToolArgsBlock(text: string): string { + const lines = text.split("\n"); + return lines + .map((line, index) => (index === 0 ? ` ⎿ ${line}` : ` ${line}`)) + .join("\n"); } function truncateToolArg(value: string): string {