diff --git a/docs/components/command-palette.md b/docs/components/command-palette.md index f09ef5c9..ab4e6dc2 100644 --- a/docs/components/command-palette.md +++ b/docs/components/command-palette.md @@ -47,8 +47,8 @@ selected worktree has a pull request). source. Same-titled commands can coexist; disabled commands do not appear. - **Handoff** (every runnable workspace, repository/worktree, or plain folder): a single **Hand Off…** row opens the staged Hand Off HUD, where you choose - the receiving agent (or update the brief only) and watch the run with Skip - and Cancel available during the source agent's briefing turn. Same flow as + the receiving agent (or save progress only) and watch the run with Skip and + Cancel available while the source agent summarizes its progress. Same flow as the toolbar Agents capsule. See [handoff](handoff.md). - **Debug** (Debug builds only): toast/update/dock simulations. diff --git a/docs/components/handoff.md b/docs/components/handoff.md index 9bc0b58d..63ecbcf0 100644 --- a/docs/components/handoff.md +++ b/docs/components/handoff.md @@ -130,26 +130,28 @@ for targets that have never run `prowl handoff save` or `prowl handoff to`. A capsule button left of the branch title identifies the selected pane's detected agent (badge and name — live status stays with the terminal and -the Active Agents panel). Clicking it opens a popover that previews what a -hand-off will do — "codex will brief the incoming agent first" when the -native session is resumable — and carries **Hand Off…**, which opens a -centered HUD. Future agent actions land in the same popover. The capsule is -disabled when no agent is detected in the selected pane. +the Active Agents panel). Clicking it opens a popover whose hand-off row +explains the action in place — "Pass this task to another agent in a new +tab", plus "codex will summarize its progress first" when the native session +is resumable — and opens a centered HUD. Future agent actions land in the +same popover. The capsule is disabled when no agent is detected in the +selected pane. The HUD runs in two steps: 1. **Choose** — pick the receiving agent (the current agent stays listed as a - fresh-session restart) or **Only brief, don't hand off**, which updates the - artifact without launching anything. Rows state read-only launch facts, - e.g. when an explicitly observed unrestricted mode carries over. There are - no other options; launch configuration follows the same adapter rules as - `prowl handoff to`. -2. **Run** — staged progress: collect the brief from the source agent, save - context, archive, launch. While the source agent writes its brief you can - **Skip** (continue immediately with the existing notes and fresh repo - state, `preparation=skipped`) or **Cancel** (abort entirely — the artifact - is untouched and nothing is logged, like Ctrl-C on the CLI). After the - brief the remaining steps are sub-second and cannot be interrupted. + fresh-session restart) or **Only save progress, don't hand off**, which + updates the artifact without launching anything. Rows state read-only + launch facts, e.g. when an explicitly observed unrestricted mode carries + over. There are no other options; launch configuration follows the same + adapter rules as `prowl handoff to`. +2. **Run** — staged progress: collect a progress summary from the source + agent, save context, archive, launch. While the source agent writes its + summary you can **Skip** (continue immediately with the existing notes and + fresh repo state, `preparation=skipped`) or **Cancel** (abort entirely — + the artifact is untouched and nothing is logged, like Ctrl-C on the CLI). + After the summary the remaining steps are sub-second and cannot be + interrupted. The Command Palette (`⌘P`) offers the same flow as a single **Hand Off…** row for any selected workspace, git repository, worktree, or plain folder; diff --git a/supacode/Features/HandoffHud/Reducer/HandoffHudFeature.swift b/supacode/Features/HandoffHud/Reducer/HandoffHudFeature.swift index 6c9914c6..2f512d15 100644 --- a/supacode/Features/HandoffHud/Reducer/HandoffHudFeature.swift +++ b/supacode/Features/HandoffHud/Reducer/HandoffHudFeature.swift @@ -115,7 +115,7 @@ struct HandoffHudFeature { targets.append( HandoffTargetOption( kind: .briefOnly, - title: "Only brief, don't hand off", + title: "Only save progress, don't hand off", subtitle: "Saves the current state for a later hand-off", isCurrentAgent: false ) diff --git a/supacode/Features/HandoffHud/Views/HandoffHudOverlayView.swift b/supacode/Features/HandoffHud/Views/HandoffHudOverlayView.swift index 4ba08413..a183d91c 100644 --- a/supacode/Features/HandoffHud/Views/HandoffHudOverlayView.swift +++ b/supacode/Features/HandoffHud/Views/HandoffHudOverlayView.swift @@ -95,12 +95,12 @@ private struct HandoffHudCard: View { case .agent: return "Handing off to \(run.target.title)" case .briefOnly: - return "Updating the brief" + return "Saving progress" } case .finished(.handedOff(let name)): return "Handed off to \(name)" case .finished(.briefSaved): - return "Brief updated" + return "Progress saved" case .finished(.failed): return "Hand off failed" } @@ -110,16 +110,17 @@ private struct HandoffHudCard: View { switch store.phase { case .choosing: if store.source.preparationRequest != nil { - return "\(store.source.displayName) will brief the incoming agent first" + return "Pass this task to another agent in a new tab. " + + "\(store.source.displayName) will summarize its progress first." } - return "Hands this task to another agent in a new tab" + return "Pass this task to another agent in a new tab." case .running(let run): if run.stage == .briefing { - return "\(store.source.displayName) is writing a brief for the next agent" + return "\(store.source.displayName) is summarizing its progress" } return "Preparing the hand-off…" case .finished(.handedOff): - return "The receiving agent starts from the hand-off notes in a new tab" + return "The receiving agent picks up the task in a new tab" case .finished(.briefSaved): return "The current state is saved for a later hand-off" case .finished(.failed(let message)): @@ -192,7 +193,7 @@ private struct HandoffHudChooseView: View { case .agent: return "Hand Off to \(target.title)" case .briefOnly: - return "Update Brief" + return "Save Progress" } } } @@ -264,7 +265,7 @@ private struct HandoffTargetRow: View { case .agent: return "Hand this task to \(target.title) in a new tab" case .briefOnly: - return "Update the hand-off notes without launching another agent" + return "Save the agent's progress for a later hand-off without launching anything" } } } @@ -290,7 +291,7 @@ private struct HandoffHudRunView: View { HStack { if run.stage == .briefing { - Text("This can take a moment while \(sourceDisplayName) writes its brief.") + Text("This can take a moment while \(sourceDisplayName) summarizes its progress.") .font(.caption) .foregroundStyle(.secondary) } @@ -345,7 +346,7 @@ private struct HandoffHudRunView: View { private func stageTitle(_ stage: HandoffStage) -> String { switch stage { case .briefing: - return "Collect brief from \(sourceDisplayName)" + return "Collect progress summary from \(sourceDisplayName)" case .saving: return "Save context" case .archiving: diff --git a/supacode/Features/Repositories/Views/AgentsToolbarButton.swift b/supacode/Features/Repositories/Views/AgentsToolbarButton.swift index 1a452386..530f28bd 100644 --- a/supacode/Features/Repositories/Views/AgentsToolbarButton.swift +++ b/supacode/Features/Repositories/Views/AgentsToolbarButton.swift @@ -9,7 +9,8 @@ struct AgentsCapsuleState: Equatable { /// the assembler with the same two-step token fallback the Active Agents /// panel uses, so a wrapper process name never loses the brand icon. let iconSource: TabIconSource? - /// Behavior preview shown as the popover's read-only first line. + /// Plain-language explanation of the hand-off action, shown under its + /// title in the popover row; varies with the source session's state. let infoLine: String } @@ -113,53 +114,52 @@ struct AgentsToolbarButton: View { } } -/// The agent-actions popover: a read-only behavior preview line and the -/// action list. Rows follow menu conventions (full-width highlight on -/// hover) so future actions slot in as additional rows. +/// The agent-actions popover. Each action is one full row — title with a +/// plain-language explanation underneath, highlighted together on hover — +/// so future actions slot in as additional rows. private struct AgentsPopoverContent: View { let capsule: AgentsCapsuleState let onHandOff: () -> Void var body: some View { - VStack(alignment: .leading, spacing: 6) { - Text(capsule.infoLine) - .font(.caption) - .foregroundStyle(.secondary) - .padding(.horizontal, 10) - .padding(.top, 10) - - Divider() - .padding(.horizontal, 6) - + VStack(alignment: .leading, spacing: 0) { AgentsPopoverRow( title: "Hand Off…", + subtitle: capsule.infoLine, systemImage: "arrow.left.arrow.right", action: onHandOff ) - .padding(.horizontal, 6) - .padding(.bottom, 6) } - .frame(width: 260, alignment: .leading) + .padding(6) + .frame(width: 280, alignment: .leading) } } private struct AgentsPopoverRow: View { let title: String + let subtitle: String let systemImage: String let action: () -> Void @State private var isHovered = false var body: some View { Button(action: action) { - HStack(spacing: 8) { + HStack(alignment: .top, spacing: 8) { Image(systemName: systemImage) .frame(width: 16) + .padding(.top, 2) .accessibilityHidden(true) - Text(title) + VStack(alignment: .leading, spacing: 2) { + Text(title) + Text(subtitle) + .font(.caption) + .foregroundStyle(.secondary) + .fixedSize(horizontal: false, vertical: true) + } Spacer(minLength: 0) } .padding(.horizontal, 8) - .padding(.vertical, 5) + .padding(.vertical, 6) .contentShape(.rect) } .buttonStyle(.plain) diff --git a/supacode/Features/Repositories/Views/WorktreeDetailToolbarViews.swift b/supacode/Features/Repositories/Views/WorktreeDetailToolbarViews.swift index c20eae35..445b7560 100644 --- a/supacode/Features/Repositories/Views/WorktreeDetailToolbarViews.swift +++ b/supacode/Features/Repositories/Views/WorktreeDetailToolbarViews.swift @@ -251,7 +251,7 @@ private struct WorktreeToolbarPreview: View { agentsCapsule: AgentsCapsuleState( displayName: "codex", iconSource: CommandIconMap.iconForFirstToken("codex"), - infoLine: "codex will brief the incoming agent first" + infoLine: "Pass this task to another agent in a new tab. codex will summarize its progress first." ), statusToast: nil, pullRequest: nil, diff --git a/supacode/Features/Repositories/Views/WorktreeDetailView.swift b/supacode/Features/Repositories/Views/WorktreeDetailView.swift index 1c188258..119e1f46 100644 --- a/supacode/Features/Repositories/Views/WorktreeDetailView.swift +++ b/supacode/Features/Repositories/Views/WorktreeDetailView.swift @@ -403,8 +403,9 @@ struct WorktreeDetailView: View { displayName: agent.displayName, iconSource: iconSource, infoLine: resumable - ? "\(agent.displayName) will brief the incoming agent first" - : "Hands this task to another agent in a new tab" + ? "Pass this task to another agent in a new tab. " + + "\(agent.displayName) will summarize its progress first." + : "Pass this task to another agent in a new tab." ) }