From 149af312dcc889e23dda943befafefbf8040517b Mon Sep 17 00:00:00 2001 From: Andrew Blakeslee Moore Date: Sat, 4 Jul 2026 00:27:20 -0700 Subject: [PATCH] iPad: show the commit card for host-run and pending-approval commits too Extend the git commit card beyond the Bash transcript path to the other two surfaces a commit appears on, so all three read like the Mac: - ApprovalCardView: a pending `git commit` approval (Bash or host_exec) renders the structured commit card (subject + Markdown body) with the literal command under "Show command", instead of a raw blob or the generic host breakdown -- so you see exactly the message you're granting. - HostExecToolCard: a host-run `git commit` surfaces the commit card in its expanded body (the `$ command` already sits in the header), rather than only the generic "Commit changes" purpose. Both reuse GitCommitSummary.parse + GitCommitCard from the previous commit. Co-Authored-By: Claude Opus 4.8 --- .../NucleicRemote/Views/ApprovalCardView.swift | 15 +++++++++------ .../Views/Transcript/HostExecCard.swift | 11 +++++++++-- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/NucleicRemote/NucleicRemote/Views/ApprovalCardView.swift b/NucleicRemote/NucleicRemote/Views/ApprovalCardView.swift index 76edfbf..b8a09bb 100644 --- a/NucleicRemote/NucleicRemote/Views/ApprovalCardView.swift +++ b/NucleicRemote/NucleicRemote/Views/ApprovalCardView.swift @@ -33,13 +33,16 @@ struct ApprovalCardView: View { .background(approval.risk.color.opacity(0.2), in: Capsule()) .foregroundStyle(approval.risk.color) } - // A host_exec gate escapes the sandbox onto the macOS host, so lay out what it's - // about to run — the inferred purpose and the program/actions/flags, parsed from the - // command itself (never from the agent) — then the exact command beneath it. Every + // A git commit reads as a structured commit card (subject + Markdown body) so you can + // see exactly what you're granting; the literal command stays under "Show command". + // Otherwise a host_exec gate lays out its parsed breakdown + exact command, and every // other tool shows its untruncated detail in a single scrollable box. - if approval.toolName == HostCommandSummary.hostExecToolName, - let command = approval.input["command"]?.stringValue, - let parsed = HostCommandSummary.summary(for: command) { + if let command = approval.input["command"]?.stringValue, + let commit = GitCommitSummary.parse(command) { + GitCommitCard(commit: commit, rawCommand: command) + } else if approval.toolName == HostCommandSummary.hostExecToolName, + let command = approval.input["command"]?.stringValue, + let parsed = HostCommandSummary.summary(for: command) { HostCommandBreakdown(summary: parsed) Text("Exact command").font(.caption2.weight(.semibold)).foregroundStyle(.secondary) ApprovalDetailBox(text: command) diff --git a/NucleicRemote/NucleicRemote/Views/Transcript/HostExecCard.swift b/NucleicRemote/NucleicRemote/Views/Transcript/HostExecCard.swift index f502a28..b59726f 100644 --- a/NucleicRemote/NucleicRemote/Views/Transcript/HostExecCard.swift +++ b/NucleicRemote/NucleicRemote/Views/Transcript/HostExecCard.swift @@ -29,7 +29,10 @@ struct HostExecToolCard: View { !$0.flags.isEmpty || !$0.arguments.isEmpty || !$0.env.isEmpty } } - private var canExpand: Bool { hasOutput || hasBreakdown } + /// A host-run `git commit` — surfaced as a structured commit card (subject + Markdown body), + /// like the Mac, instead of only the generic "Commit changes" purpose. + private var commit: GitCommitSummary.Commit? { GitCommitSummary.parse(command) } + private var canExpand: Bool { hasOutput || hasBreakdown || commit != nil } var body: some View { VStack(alignment: .leading, spacing: 8) { @@ -37,7 +40,11 @@ struct HostExecToolCard: View { label: { header } .buttonStyle(.plain) if expanded { - if hasBreakdown, let parsed { + if let commit { + // The `$ command` is already shown in the header, so omit the disclosure here. + Divider().overlay(Color.primary.opacity(0.06)) + GitCommitCard(commit: commit) + } else if hasBreakdown, let parsed { Divider().overlay(Color.primary.opacity(0.06)) HostCommandBreakdown(summary: parsed, showPurpose: false) }