From 2337b022fea3bbb69fc7648424338d47f72fc767 Mon Sep 17 00:00:00 2001 From: Nucleic Date: Tue, 4 Aug 2026 23:13:58 -0700 Subject: [PATCH] Merge nucleic/humble-dewy-weasel-eren into dev --- .../NucleicRemote/Views/ApprovalCardView.swift | 4 +++- .../Views/Transcript/SandboxToolDisplay.swift | 12 ++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/NucleicRemote/NucleicRemote/Views/ApprovalCardView.swift b/NucleicRemote/NucleicRemote/Views/ApprovalCardView.swift index e2582d9..049805c 100644 --- a/NucleicRemote/NucleicRemote/Views/ApprovalCardView.swift +++ b/NucleicRemote/NucleicRemote/Views/ApprovalCardView.swift @@ -67,7 +67,9 @@ struct ApprovalCardView: View { // A destructive action (rm, force-push, reset --hard, …) offers no remembered // allow: every one must be a deliberate, one-off approval, never granted in a // way that lets the next one through unseen. Mirrors the Mac. - if approval.risk != .destructive, !store.capabilities.allowAlwaysScopes.isEmpty { + if approval.risk != .destructive, + approval.toolName != "mcp__nucleic__copy_artifact", + !store.capabilities.allowAlwaysScopes.isEmpty { Menu("Allow always…") { ForEach(store.capabilities.allowAlwaysScopes, id: \.self) { scope in Button(alwaysLabel(scope)) { diff --git a/NucleicRemote/NucleicRemote/Views/Transcript/SandboxToolDisplay.swift b/NucleicRemote/NucleicRemote/Views/Transcript/SandboxToolDisplay.swift index 90520ae..2354eba 100644 --- a/NucleicRemote/NucleicRemote/Views/Transcript/SandboxToolDisplay.swift +++ b/NucleicRemote/NucleicRemote/Views/Transcript/SandboxToolDisplay.swift @@ -31,6 +31,7 @@ enum SandboxToolDisplay { // The wire name is Claude Code's, but the shell behind it is nash. case "Bash": "Nash" case "BashOutput": "Nash output" + case "copy_artifact": "Copy artifact" case "mac_vm_exec", "mac_vm_control": "macOS VM" case "mac_vm_computer", "mac_vm_computer_batch": "macOS VM screen" case "mac_vm_clear_notifications": "macOS VM notifications" @@ -56,6 +57,7 @@ enum SandboxToolDisplay { /// the way the Mac's status line does. static func gerund(for toolName: String) -> String? { switch bareName(toolName) { + case "copy_artifact": "Copying an artifact…" case "mac_vm_exec": "Running on a macOS VM…" case "linux_vm_exec": "Running on a Linux VM…" case "linux_container": "Running in a Linux container…" @@ -73,6 +75,7 @@ enum SandboxToolDisplay { /// for a lifecycle op — so the glyph carries the same distinction the label does. static func icon(for toolName: String) -> String? { switch bareName(toolName) { + case "copy_artifact": "doc.on.doc" case "mac_vm_exec": "macwindow" case "linux_vm_exec": "server.rack" case "linux_container": "shippingbox" @@ -90,6 +93,15 @@ enum SandboxToolDisplay { /// case the caller falls back to the generic input gist. static func detail(for toolName: String, input: JSONValue) -> String? { switch bareName(toolName) { + case "copy_artifact": + let source = string(input, "source").map(singleLine) + let destination = string(input, "destination").map(singleLine) + switch (source, destination) { + case let (.some(source), .some(destination)): return "\(source) → \(destination)" + case let (.some(source), .none): return source + case let (.none, .some(destination)): return destination + case (.none, .none): return nil + } case "mac_vm_exec", "linux_vm_exec": return string(input, "command").map { "$ \(singleLine($0))" } case "mac_vm_computer", "linux_vm_computer":