From dd3eab58aa4abf7680c27693ca2ed6da0234cb9a Mon Sep 17 00:00:00 2001 From: Ammar Date: Thu, 9 Oct 2025 11:27:28 -0500 Subject: [PATCH 1/5] =?UTF-8?q?=F0=9F=A4=96=20Add=20'Compact=20Here'=20but?= =?UTF-8?q?ton=20to=20Plan=20Results?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds a compact button to ProposePlanToolCall that allows users to start fresh from a proposed plan. When clicked, it replaces the conversation history with a compacted message containing the plan content formatted with the plan title as an H1 heading. Changes: - Thread workspaceId through MessageRenderer -> ToolMessage -> ProposePlanToolCall - Add handleCompactHere handler that creates a compacted CmuxMessage - Add 'Compact Here' button to plan header (only shown when workspaceId available) - Preserve plan formatting in compacted message (title + plan content) --- src/components/AIView.tsx | 6 ++- src/components/Messages/MessageRenderer.tsx | 5 ++- src/components/Messages/ToolMessage.tsx | 4 +- src/components/tools/ProposePlanToolCall.tsx | 40 ++++++++++++++++++++ 4 files changed, 51 insertions(+), 4 deletions(-) diff --git a/src/components/AIView.tsx b/src/components/AIView.tsx index 01e60f3e8c..12597516ef 100644 --- a/src/components/AIView.tsx +++ b/src/components/AIView.tsx @@ -376,7 +376,11 @@ const AIViewInner: React.FC = ({ return ( - + {isAtCutoff && ( ⚠️ Messages below this line will be removed when you submit the edit diff --git a/src/components/Messages/MessageRenderer.tsx b/src/components/Messages/MessageRenderer.tsx index a2918980e7..40695d3099 100644 --- a/src/components/Messages/MessageRenderer.tsx +++ b/src/components/Messages/MessageRenderer.tsx @@ -11,11 +11,12 @@ interface MessageRendererProps { message: DisplayedMessage; className?: string; onEditUserMessage?: (messageId: string, content: string) => void; + workspaceId?: string; } // Memoized to prevent unnecessary re-renders when parent (AIView) updates export const MessageRenderer = React.memo( - ({ message, className, onEditUserMessage }) => { + ({ message, className, onEditUserMessage, workspaceId }) => { // Route based on message type switch (message.type) { case "user": @@ -23,7 +24,7 @@ export const MessageRenderer = React.memo( case "assistant": return ; case "tool": - return ; + return ; case "reasoning": return ; case "stream-error": diff --git a/src/components/Messages/ToolMessage.tsx b/src/components/Messages/ToolMessage.tsx index 607a1e3977..1a90d4460e 100644 --- a/src/components/Messages/ToolMessage.tsx +++ b/src/components/Messages/ToolMessage.tsx @@ -21,6 +21,7 @@ import type { interface ToolMessageProps { message: DisplayedMessage & { type: "tool" }; className?: string; + workspaceId?: string; } // Type guard for bash tool @@ -71,7 +72,7 @@ function isProposePlanTool(toolName: string, args: unknown): args is ProposePlan return toolName === "propose_plan" && typeof args === "object" && args !== null && "plan" in args; } -export const ToolMessage: React.FC = ({ message, className }) => { +export const ToolMessage: React.FC = ({ message, className, workspaceId }) => { // Route to specialized components based on tool name if (isBashTool(message.toolName, message.args)) { return ( @@ -130,6 +131,7 @@ export const ToolMessage: React.FC = ({ message, className }) args={message.args} result={message.result as ProposePlanToolResult | undefined} status={message.status} + workspaceId={workspaceId} /> ); diff --git a/src/components/tools/ProposePlanToolCall.tsx b/src/components/tools/ProposePlanToolCall.tsx index 3560c2c8a0..9f0b5f6de8 100644 --- a/src/components/tools/ProposePlanToolCall.tsx +++ b/src/components/tools/ProposePlanToolCall.tsx @@ -12,6 +12,7 @@ import { import { useToolExpansion, getStatusDisplay, type ToolStatus } from "./shared/toolUtils"; import { MarkdownRenderer } from "../Messages/MarkdownRenderer"; import { formatKeybind, KEYBINDS } from "@/utils/ui/keybinds"; +import { createCmuxMessage } from "@/types/message"; const PlanContainer = styled.div` padding: 12px; @@ -238,16 +239,19 @@ interface ProposePlanToolCallProps { args: ProposePlanToolArgs; result?: ProposePlanToolResult; status?: ToolStatus; + workspaceId?: string; } export const ProposePlanToolCall: React.FC = ({ args, result: _result, status = "pending", + workspaceId, }) => { const { expanded, toggleExpanded } = useToolExpansion(true); // Expand by default const [showRaw, setShowRaw] = useState(false); const [copied, setCopied] = useState(false); + const [isCompacting, setIsCompacting] = useState(false); const statusDisplay = getStatusDisplay(status); @@ -261,6 +265,37 @@ export const ProposePlanToolCall: React.FC = ({ } }; + const handleCompactHere = async () => { + if (!workspaceId || isCompacting) return; + + setIsCompacting(true); + try { + // Create a compacted message with the plan content + // Format: Title as H1 + plan content + const compactedContent = `# ${args.title}\n\n${args.plan}`; + + const summaryMessage = createCmuxMessage( + `compact-${Date.now()}-${Math.random().toString(36).substring(2, 11)}`, + "assistant", + compactedContent, + { + timestamp: Date.now(), + compacted: true, + } + ); + + const result = await window.api.workspace.replaceChatHistory(workspaceId, summaryMessage); + + if (!result.success) { + console.error("Failed to compact:", result.error); + } + } catch (err) { + console.error("Compact error:", err); + } finally { + setIsCompacting(false); + } + }; + return ( @@ -278,6 +313,11 @@ export const ProposePlanToolCall: React.FC = ({ {args.title} + {workspaceId && ( + void handleCompactHere()} disabled={isCompacting}> + {isCompacting ? "Compacting..." : "📦 Compact Here"} + + )} void handleCopy()}> {copied ? "✓ Copied" : "Copy"} From e3f932f62a164aeaa93c94817c5a9e511aef6cf9 Mon Sep 17 00:00:00 2001 From: Ammar Date: Thu, 9 Oct 2025 11:27:44 -0500 Subject: [PATCH 2/5] gitignore: Add TESTING.md --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 41d72c3ea9..42cc53d078 100644 --- a/.gitignore +++ b/.gitignore @@ -79,3 +79,4 @@ profile.txt src/version.ts OPENAI_FIX_SUMMARY.md docs/vercel/ +TESTING.md From 6da45581d72e3e6675a7c43af484848aeadc2192 Mon Sep 17 00:00:00 2001 From: Ammar Date: Thu, 9 Oct 2025 11:28:06 -0500 Subject: [PATCH 3/5] gitignore: Add FEATURE_SUMMARY.md --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 42cc53d078..471c8c6f4c 100644 --- a/.gitignore +++ b/.gitignore @@ -80,3 +80,4 @@ src/version.ts OPENAI_FIX_SUMMARY.md docs/vercel/ TESTING.md +FEATURE_SUMMARY.md From d5f12a9feee896465fd3a099932de6dba9b4b603 Mon Sep 17 00:00:00 2001 From: Ammar Date: Thu, 9 Oct 2025 11:28:30 -0500 Subject: [PATCH 4/5] gitignore: Add CODE_CHANGES.md --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 471c8c6f4c..8331b6761b 100644 --- a/.gitignore +++ b/.gitignore @@ -81,3 +81,4 @@ OPENAI_FIX_SUMMARY.md docs/vercel/ TESTING.md FEATURE_SUMMARY.md +CODE_CHANGES.md From c15906df60e9628537376a6642bbecbdf248c844 Mon Sep 17 00:00:00 2001 From: Ammar Date: Thu, 9 Oct 2025 11:29:13 -0500 Subject: [PATCH 5/5] gitignore: Add README_COMPACT_HERE.md --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 8331b6761b..6f46f0d97f 100644 --- a/.gitignore +++ b/.gitignore @@ -82,3 +82,4 @@ docs/vercel/ TESTING.md FEATURE_SUMMARY.md CODE_CHANGES.md +README_COMPACT_HERE.md