From 7793d4fa1842e07351e92c0d9982abeb96b2555a Mon Sep 17 00:00:00 2001 From: Kiet Ho Date: Sun, 22 Feb 2026 10:53:11 -0800 Subject: [PATCH 01/14] refactor(mcp): rename start_claude_session to start_agent_session with multi-agent support Generalize the MCP tool to support multiple AI agents (claude, codex, gemini, opencode, copilot, cursor-agent) instead of being hardcoded to Claude Code. --- .claude/commands/task-run.md | 12 +- .../OpenInWorkspace/OpenInWorkspace.tsx | 4 +- .../tasks/$taskId/utils/buildAgentCommand.ts | 6 + .../tasks/$taskId/utils/buildClaudeCommand.ts | 1 - .../hooks/useCommandWatcher/tools/index.ts | 4 +- ...aude-session.ts => start-agent-session.ts} | 6 +- apps/docs/content/docs/mcp.mdx | 47 ++++---- .../get-workspace-details.ts | 2 +- .../devices/start-agent-session/index.ts | 1 + .../start-agent-session.ts} | 33 +++++- .../devices/start-claude-session/index.ts | 1 - packages/mcp/src/tools/index.ts | 4 +- packages/shared/package.json | 4 + packages/shared/src/agent-command.ts | 108 ++++++++++++++++++ packages/shared/src/claude-command.ts | 63 ++-------- 15 files changed, 193 insertions(+), 103 deletions(-) create mode 100644 apps/desktop/src/renderer/routes/_authenticated/_dashboard/tasks/$taskId/utils/buildAgentCommand.ts delete mode 100644 apps/desktop/src/renderer/routes/_authenticated/_dashboard/tasks/$taskId/utils/buildClaudeCommand.ts rename apps/desktop/src/renderer/routes/_authenticated/components/AgentHooks/hooks/useCommandWatcher/tools/{start-claude-session.ts => start-agent-session.ts} (93%) create mode 100644 packages/mcp/src/tools/devices/start-agent-session/index.ts rename packages/mcp/src/tools/devices/{start-claude-session/start-claude-session.ts => start-agent-session/start-agent-session.ts} (71%) delete mode 100644 packages/mcp/src/tools/devices/start-claude-session/index.ts create mode 100644 packages/shared/src/agent-command.ts diff --git a/.claude/commands/task-run.md b/.claude/commands/task-run.md index a324030d651..d4816d61c2a 100644 --- a/.claude/commands/task-run.md +++ b/.claude/commands/task-run.md @@ -1,9 +1,9 @@ --- -description: Create a task, workspace, and start a Claude Code session to work on it -allowed-tools: mcp__superset__create_task, mcp__superset__list_members, mcp__superset__list_devices, mcp__superset__list_projects, mcp__superset__create_workspace, mcp__superset__start_claude_session, Bash(git config user.email) +description: Create a task, workspace, and start an AI agent session to work on it +allowed-tools: mcp__superset__create_task, mcp__superset__list_members, mcp__superset__list_devices, mcp__superset__list_projects, mcp__superset__create_workspace, mcp__superset__start_agent_session, Bash(git config user.email) --- -Create a new task in Superset, spin up a workspace, and start a Claude Code session to work on it. +Create a new task in Superset, spin up a workspace, and start an AI agent session to work on it. ## Input @@ -44,15 +44,15 @@ Run these in parallel: - Default to `feat/...` if the type is ambiguous - Create the workspace using `mcp__superset__create_workspace` -### 3. Start Claude Code session +### 3. Start AI agent session -- Start a Claude Code session using `mcp__superset__start_claude_session` with the created task ID and workspace ID +- Start an AI agent session using `mcp__superset__start_agent_session` with the created task ID and workspace ID ## Output Confirm with a summary: - Task: title, priority, slug - Workspace: name, branch -- Claude Code: running +- Agent session: running $ARGUMENTS diff --git a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/tasks/$taskId/components/PropertiesSidebar/components/OpenInWorkspace/OpenInWorkspace.tsx b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/tasks/$taskId/components/PropertiesSidebar/components/OpenInWorkspace/OpenInWorkspace.tsx index 4dd4c521c88..41455a63311 100644 --- a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/tasks/$taskId/components/PropertiesSidebar/components/OpenInWorkspace/OpenInWorkspace.tsx +++ b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/tasks/$taskId/components/PropertiesSidebar/components/OpenInWorkspace/OpenInWorkspace.tsx @@ -13,7 +13,7 @@ import { useCreateWorkspace } from "renderer/react-query/workspaces"; import { ProjectThumbnail } from "renderer/screens/main/components/WorkspaceSidebar/ProjectSection/ProjectThumbnail"; import { useWorkspaceInitStore } from "renderer/stores/workspace-init"; import type { TaskWithStatus } from "../../../../../components/TasksView/hooks/useTasksTable"; -import { buildClaudeCommand } from "../../../../utils/buildClaudeCommand"; +import { buildAgentCommand } from "../../../../utils/buildAgentCommand"; import { deriveBranchName } from "../../../../utils/deriveBranchName"; interface OpenInWorkspaceProps { @@ -61,7 +61,7 @@ export function OpenInWorkspace({ task }: OpenInWorkspaceProps) { }); if (!result.wasExisting) { - const command = buildClaudeCommand({ + const command = buildAgentCommand({ task: { id: task.id, slug: task.slug, diff --git a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/tasks/$taskId/utils/buildAgentCommand.ts b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/tasks/$taskId/utils/buildAgentCommand.ts new file mode 100644 index 00000000000..f1fb8129898 --- /dev/null +++ b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/tasks/$taskId/utils/buildAgentCommand.ts @@ -0,0 +1,6 @@ +export { + type AgentType, + buildAgentCommand, + buildClaudeCommand, + type TaskInput, +} from "@superset/shared/agent-command"; diff --git a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/tasks/$taskId/utils/buildClaudeCommand.ts b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/tasks/$taskId/utils/buildClaudeCommand.ts deleted file mode 100644 index 4eea275f03b..00000000000 --- a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/tasks/$taskId/utils/buildClaudeCommand.ts +++ /dev/null @@ -1 +0,0 @@ -export { buildClaudeCommand } from "@superset/shared/claude-command"; diff --git a/apps/desktop/src/renderer/routes/_authenticated/components/AgentHooks/hooks/useCommandWatcher/tools/index.ts b/apps/desktop/src/renderer/routes/_authenticated/components/AgentHooks/hooks/useCommandWatcher/tools/index.ts index 85f08e8d7f1..61d097834c4 100644 --- a/apps/desktop/src/renderer/routes/_authenticated/components/AgentHooks/hooks/useCommandWatcher/tools/index.ts +++ b/apps/desktop/src/renderer/routes/_authenticated/components/AgentHooks/hooks/useCommandWatcher/tools/index.ts @@ -5,7 +5,7 @@ import { getWorkspaceDetails } from "./get-workspace-details"; import { listProjects } from "./list-projects"; import { listWorkspaces } from "./list-workspaces"; import { navigateToWorkspace } from "./navigate-to-workspace"; -import { startClaudeSession } from "./start-claude-session"; +import { startAgentSession } from "./start-agent-session"; import { switchWorkspace } from "./switch-workspace"; import type { CommandResult, ToolContext, ToolDefinition } from "./types"; import { updateWorkspace } from "./update-workspace"; @@ -20,7 +20,7 @@ const tools: ToolDefinition[] = [ listProjects, listWorkspaces, navigateToWorkspace, - startClaudeSession, + startAgentSession, switchWorkspace, updateWorkspace, ]; diff --git a/apps/desktop/src/renderer/routes/_authenticated/components/AgentHooks/hooks/useCommandWatcher/tools/start-claude-session.ts b/apps/desktop/src/renderer/routes/_authenticated/components/AgentHooks/hooks/useCommandWatcher/tools/start-agent-session.ts similarity index 93% rename from apps/desktop/src/renderer/routes/_authenticated/components/AgentHooks/hooks/useCommandWatcher/tools/start-claude-session.ts rename to apps/desktop/src/renderer/routes/_authenticated/components/AgentHooks/hooks/useCommandWatcher/tools/start-agent-session.ts index 8a63cad3a76..c5dfd5135cd 100644 --- a/apps/desktop/src/renderer/routes/_authenticated/components/AgentHooks/hooks/useCommandWatcher/tools/start-claude-session.ts +++ b/apps/desktop/src/renderer/routes/_authenticated/components/AgentHooks/hooks/useCommandWatcher/tools/start-agent-session.ts @@ -76,13 +76,13 @@ async function execute( error: error instanceof Error ? error.message - : "Failed to start Claude session", + : "Failed to start agent session", }; } } -export const startClaudeSession: ToolDefinition = { - name: "start_claude_session", +export const startAgentSession: ToolDefinition = { + name: "start_agent_session", schema, execute, }; diff --git a/apps/docs/content/docs/mcp.mdx b/apps/docs/content/docs/mcp.mdx index d83d38468df..066243f417b 100644 --- a/apps/docs/content/docs/mcp.mdx +++ b/apps/docs/content/docs/mcp.mdx @@ -15,7 +15,7 @@ Superset provides an [MCP (Model Context Protocol)](https://modelcontextprotocol | **Workspaces** | Create, update, switch, delete, list, navigate workspaces | | **Devices** | List devices, projects, and app context | | **Organization** | List members and task statuses | -| **AI Sessions** | Start autonomous Claude sessions and subagents | +| **AI Sessions** | Start autonomous AI agent sessions (Claude, Codex, Gemini, OpenCode, Copilot, Cursor Agent) and subagents | ## Setup @@ -43,27 +43,27 @@ Run `opencode mcp add` and follow the interactive prompts: ```ansi title="terminal" opencode mcp add -┌ Add MCP server -│ -◇ Location -│ Current project -│ -◇ Enter MCP server name -│ superset -│ -◇ Select MCP server type -│ Remote -│ -◇ Enter MCP server URL -│ https://api.superset.sh/api/agent/mcp -│ -◇ Does this server require OAuth authentication? -│ Yes -│ -◇ Do you have a pre-registered client ID? -│ No -│ -└ MCP server added successfully +[2m┌[0m Add MCP server +[2m│[0m +[2m◇[0m Location +[2m│[0m [36mCurrent project[0m +[2m│[0m +[2m◇[0m Enter MCP server name +[2m│[0m [36msuperset[0m +[2m│[0m +[2m◇[0m Select MCP server type +[2m│[0m [36mRemote[0m +[2m│[0m +[2m◇[0m Enter MCP server URL +[2m│[0m [36mhttps://api.superset.sh/api/agent/mcp[0m +[2m│[0m +[2m◇[0m Does this server require OAuth authentication? +[2m│[0m [36mYes[0m +[2m│[0m +[2m◇[0m Do you have a pre-registered client ID? +[2m│[0m [36mNo[0m +[2m│[0m +[2m└[0m [32mMCP server added successfully[0m ``` @@ -240,7 +240,7 @@ API keys grant full access to your organization. Keep them secret and never comm | Tool | Description | |------|-------------| -| `start_claude_session` | Start an autonomous Claude Code session for a task. Launches Claude with the task context in the specified workspace. When `paneId` is provided, adds a new terminal pane to the tab containing that pane (subagent behavior) instead of initializing the workspace. | +| `start_agent_session` | Start an autonomous AI agent session for a task. Supports multiple agents: Claude, Codex, Gemini, OpenCode, Copilot, and Cursor Agent (defaults to Claude). Launches the agent with task context in the specified workspace. When `paneId` is provided, adds a new terminal pane to the tab containing that pane (subagent behavior) instead of initializing the workspace. | ## Example Usage @@ -251,4 +251,5 @@ Once connected, you can ask your AI agent to: - "Create a new workspace for the auth feature" - "Show me who's online in my team" - "Start a Claude session on my MacBook to work on the auth task" +- "Start a Codex session to fix the failing tests" - "Spin up a subagent to fix the failing tests" diff --git a/packages/mcp/src/tools/devices/get-workspace-details/get-workspace-details.ts b/packages/mcp/src/tools/devices/get-workspace-details/get-workspace-details.ts index d9e521ebcc1..866abdf26ff 100644 --- a/packages/mcp/src/tools/devices/get-workspace-details/get-workspace-details.ts +++ b/packages/mcp/src/tools/devices/get-workspace-details/get-workspace-details.ts @@ -7,7 +7,7 @@ export function register(server: McpServer) { "get_workspace_details", { description: - "Get detailed information about a workspace on a device, including its tabs and panes. Use this to discover pane IDs needed for start_claude_session's paneId parameter. The target device must belong to the current user.", + "Get detailed information about a workspace on a device, including its tabs and panes. Use this to discover pane IDs needed for start_agent_session's paneId parameter. The target device must belong to the current user.", inputSchema: { deviceId: z.string().describe("Target device ID"), workspaceId: z.string().describe("Workspace ID to get details for"), diff --git a/packages/mcp/src/tools/devices/start-agent-session/index.ts b/packages/mcp/src/tools/devices/start-agent-session/index.ts new file mode 100644 index 00000000000..a0be3cad15d --- /dev/null +++ b/packages/mcp/src/tools/devices/start-agent-session/index.ts @@ -0,0 +1 @@ +export { register } from "./start-agent-session"; diff --git a/packages/mcp/src/tools/devices/start-claude-session/start-claude-session.ts b/packages/mcp/src/tools/devices/start-agent-session/start-agent-session.ts similarity index 71% rename from packages/mcp/src/tools/devices/start-claude-session/start-claude-session.ts rename to packages/mcp/src/tools/devices/start-agent-session/start-agent-session.ts index c42db1c70cf..4459c6c5bea 100644 --- a/packages/mcp/src/tools/devices/start-claude-session/start-claude-session.ts +++ b/packages/mcp/src/tools/devices/start-agent-session/start-agent-session.ts @@ -1,7 +1,7 @@ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { db } from "@superset/db/client"; import { taskStatuses, tasks } from "@superset/db/schema"; -import { buildClaudeCommand } from "@superset/shared/claude-command"; +import { AGENT_TYPES, buildAgentCommand } from "@superset/shared/agent-command"; import { and, eq, isNull } from "drizzle-orm"; import { alias } from "drizzle-orm/pg-core"; import { z } from "zod"; @@ -44,13 +44,21 @@ function validateArgs(args: Record): { taskId: string; workspaceId: string; paneId?: string; + agent?: string; } | null { const deviceId = args.deviceId as string; const taskId = args.taskId as string; const workspaceId = args.workspaceId as string; const paneId = args.paneId as string | undefined; + const agent = args.agent as string | undefined; if (!deviceId || !taskId || !workspaceId) return null; - return { deviceId, taskId, workspaceId, ...(paneId ? { paneId } : {}) }; + return { + deviceId, + taskId, + workspaceId, + ...(paneId ? { paneId } : {}), + ...(agent ? { agent } : {}), + }; } const ERROR_ARGS_REQUIRED = { @@ -70,10 +78,10 @@ const ERROR_TASK_NOT_FOUND = { export function register(server: McpServer) { server.registerTool( - "start_claude_session", + "start_agent_session", { description: - "Start an autonomous Claude Code session for a task in an existing workspace. Launches Claude with the task context in the specified workspace. When paneId is provided, adds a new terminal pane to the tab containing that pane (subagent behavior) instead of initializing the workspace. The target device must belong to the current user.", + "Start an autonomous AI agent session for a task in an existing workspace. Launches the specified agent (defaults to Claude) with the task context in the specified workspace. When paneId is provided, adds a new terminal pane to the tab containing that pane (subagent behavior) instead of initializing the workspace. The target device must belong to the current user.", inputSchema: { deviceId: z.string().describe("Target device ID"), taskId: z.string().describe("Task ID to work on"), @@ -88,6 +96,12 @@ export function register(server: McpServer) { .describe( "Optional pane ID. When provided, adds a new pane to the tab containing this pane instead of initializing the workspace.", ), + agent: z + .enum(AGENT_TYPES) + .optional() + .describe( + 'AI agent to use: "claude", "codex", "gemini", "opencode", "copilot", or "cursor-agent". Defaults to "claude".', + ), }, }, async (args, extra) => { @@ -95,6 +109,9 @@ export function register(server: McpServer) { const validated = validateArgs(args); if (!validated) return ERROR_ARGS_REQUIRED; + const agent = + (validated.agent as (typeof AGENT_TYPES)[number]) ?? "claude"; + const task = await fetchTask({ taskId: validated.taskId, organizationId: ctx.organizationId, @@ -104,9 +121,13 @@ export function register(server: McpServer) { return executeOnDevice({ ctx, deviceId: validated.deviceId, - tool: "start_claude_session", + tool: "start_agent_session", params: { - command: buildClaudeCommand({ task, randomId: crypto.randomUUID() }), + command: buildAgentCommand({ + task, + randomId: crypto.randomUUID(), + agent, + }), name: task.slug, workspaceId: validated.workspaceId, ...(validated.paneId ? { paneId: validated.paneId } : {}), diff --git a/packages/mcp/src/tools/devices/start-claude-session/index.ts b/packages/mcp/src/tools/devices/start-claude-session/index.ts deleted file mode 100644 index 4a758bd261c..00000000000 --- a/packages/mcp/src/tools/devices/start-claude-session/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { register } from "./start-claude-session"; diff --git a/packages/mcp/src/tools/index.ts b/packages/mcp/src/tools/index.ts index 2f5e4bde332..443c8625a8f 100644 --- a/packages/mcp/src/tools/index.ts +++ b/packages/mcp/src/tools/index.ts @@ -7,7 +7,7 @@ import { register as listDevices } from "./devices/list-devices"; import { register as listProjects } from "./devices/list-projects"; import { register as listWorkspaces } from "./devices/list-workspaces"; import { register as navigateToWorkspace } from "./devices/navigate-to-workspace"; -import { register as startClaudeSession } from "./devices/start-claude-session"; +import { register as startAgentSession } from "./devices/start-agent-session"; import { register as switchWorkspace } from "./devices/switch-workspace"; import { register as updateWorkspace } from "./devices/update-workspace"; import { register as listMembers } from "./organizations/list-members"; @@ -36,7 +36,7 @@ const allTools = [ switchWorkspace, deleteWorkspace, updateWorkspace, - startClaudeSession, + startAgentSession, ]; export function registerTools(server: McpServer) { diff --git a/packages/shared/package.json b/packages/shared/package.json index 909835f3934..878937fb036 100644 --- a/packages/shared/package.json +++ b/packages/shared/package.json @@ -20,6 +20,10 @@ "types": "./src/auth/index.ts", "default": "./src/auth/index.ts" }, + "./agent-command": { + "types": "./src/agent-command.ts", + "default": "./src/agent-command.ts" + }, "./claude-command": { "types": "./src/claude-command.ts", "default": "./src/claude-command.ts" diff --git a/packages/shared/src/agent-command.ts b/packages/shared/src/agent-command.ts new file mode 100644 index 00000000000..a4145c3d379 --- /dev/null +++ b/packages/shared/src/agent-command.ts @@ -0,0 +1,108 @@ +export const AGENT_TYPES = [ + "claude", + "codex", + "gemini", + "opencode", + "copilot", + "cursor-agent", +] as const; + +export type AgentType = (typeof AGENT_TYPES)[number]; + +export interface TaskInput { + id: string; + slug: string; + title: string; + description: string | null; + priority: string; + statusName: string | null; + labels: string[] | null; +} + +function buildPrompt(task: TaskInput): string { + const metadata = [ + `Priority: ${task.priority}`, + task.statusName && `Status: ${task.statusName}`, + task.labels?.length && `Labels: ${task.labels.join(", ")}`, + ] + .filter(Boolean) + .join("\n"); + + return `You are working on task "${task.title}" (${task.slug}). + +${metadata} + +## Task Description + +${task.description || "No description provided."} + +## Instructions + +You are running fully autonomously. Do not ask questions or wait for user feedback — make all decisions independently based on the codebase and task description. + +1. Explore the codebase to understand the relevant code and architecture +2. Create a detailed execution plan for this task including: + - Purpose and scope of the changes + - Key assumptions + - Concrete implementation steps with specific files to modify + - How to validate the changes work correctly +3. Implement the plan +4. Verify your changes work correctly (run relevant tests, typecheck, lint) +5. When done, use the Superset MCP \`update_task\` tool to update task "${task.id}" with a summary of what was done`; +} + +function buildHeredoc( + prompt: string, + delimiter: string, + command: string, + suffix?: string, +): string { + const closing = suffix ? `)" ${suffix}` : ')"'; + return [`${command} "$(cat <<'${delimiter}'`, prompt, delimiter, closing].join( + "\n", + ); +} + +const AGENT_COMMANDS: Record< + AgentType, + (prompt: string, delimiter: string) => string +> = { + claude: (prompt, delimiter) => + buildHeredoc(prompt, delimiter, "claude --dangerously-skip-permissions"), + codex: (prompt, delimiter) => + buildHeredoc(prompt, delimiter, "codex --full-auto"), + gemini: (prompt, delimiter) => + buildHeredoc(prompt, delimiter, "gemini --yolo"), + opencode: (prompt, delimiter) => + buildHeredoc(prompt, delimiter, "opencode run"), + copilot: (prompt, delimiter) => + buildHeredoc(prompt, delimiter, "copilot -p", "--yolo"), + "cursor-agent": (prompt, delimiter) => + buildHeredoc(prompt, delimiter, "cursor-agent -p", "--yolo"), +}; + +export function buildAgentCommand({ + task, + randomId, + agent = "claude", +}: { + task: TaskInput; + randomId: string; + agent?: AgentType; +}): string { + const prompt = buildPrompt(task); + const delimiter = `SUPERSET_PROMPT_${randomId.replaceAll("-", "")}`; + const builder = AGENT_COMMANDS[agent]; + return builder(prompt, delimiter); +} + +/** @deprecated Use `buildAgentCommand` instead */ +export function buildClaudeCommand({ + task, + randomId, +}: { + task: TaskInput; + randomId: string; +}): string { + return buildAgentCommand({ task, randomId, agent: "claude" }); +} diff --git a/packages/shared/src/claude-command.ts b/packages/shared/src/claude-command.ts index f409be6becb..7cdb3dbf6cc 100644 --- a/packages/shared/src/claude-command.ts +++ b/packages/shared/src/claude-command.ts @@ -1,56 +1,7 @@ -interface TaskInput { - id: string; - slug: string; - title: string; - description: string | null; - priority: string; - statusName: string | null; - labels: string[] | null; -} - -export function buildClaudeCommand({ - task, - randomId, -}: { - task: TaskInput; - randomId: string; -}): string { - const metadata = [ - `Priority: ${task.priority}`, - task.statusName && `Status: ${task.statusName}`, - task.labels?.length && `Labels: ${task.labels.join(", ")}`, - ] - .filter(Boolean) - .join("\n"); - - const prompt = `You are working on task "${task.title}" (${task.slug}). - -${metadata} - -## Task Description - -${task.description || "No description provided."} - -## Instructions - -You are running fully autonomously. Do not ask questions or wait for user feedback — make all decisions independently based on the codebase and task description. - -1. Explore the codebase to understand the relevant code and architecture -2. Create a detailed execution plan for this task including: - - Purpose and scope of the changes - - Key assumptions - - Concrete implementation steps with specific files to modify - - How to validate the changes work correctly -3. Implement the plan -4. Verify your changes work correctly (run relevant tests, typecheck, lint) -5. When done, use the Superset MCP \`update_task\` tool to update task "${task.id}" with a summary of what was done`; - - const delimiter = `SUPERSET_PROMPT_${randomId.replaceAll("-", "")}`; - - return [ - `claude --dangerously-skip-permissions "$(cat <<'${delimiter}'`, - prompt, - delimiter, - ')"', - ].join("\n"); -} +/** @deprecated Use `@superset/shared/agent-command` instead */ +export { + type AgentType, + buildAgentCommand, + buildClaudeCommand, + type TaskInput, +} from "./agent-command"; From a74a7178f67ff763879b58076ef0c04773cfc81e Mon Sep 17 00:00:00 2001 From: Kiet Ho Date: Sun, 22 Feb 2026 10:54:42 -0800 Subject: [PATCH 02/14] Lint --- apps/desktop/src/renderer/stores/tabs/utils.test.ts | 1 - apps/desktop/src/renderer/stores/tabs/utils.ts | 6 +++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/apps/desktop/src/renderer/stores/tabs/utils.test.ts b/apps/desktop/src/renderer/stores/tabs/utils.test.ts index 29daadda60d..790582fcf02 100644 --- a/apps/desktop/src/renderer/stores/tabs/utils.test.ts +++ b/apps/desktop/src/renderer/stores/tabs/utils.test.ts @@ -1,6 +1,5 @@ import { describe, expect, it } from "bun:test"; import type { MosaicNode } from "react-mosaic-component"; -import type { FileStatus } from "shared/changes-types"; import type { Tab } from "./types"; import { buildMultiPaneLayout, diff --git a/apps/desktop/src/renderer/stores/tabs/utils.ts b/apps/desktop/src/renderer/stores/tabs/utils.ts index b3d851c5f75..17d3c20ce59 100644 --- a/apps/desktop/src/renderer/stores/tabs/utils.ts +++ b/apps/desktop/src/renderer/stores/tabs/utils.ts @@ -1,5 +1,9 @@ import type { MosaicBranch, MosaicNode } from "react-mosaic-component"; -import { type ChangeCategory, type FileStatus, isNewFile } from "shared/changes-types"; +import { + type ChangeCategory, + type FileStatus, + isNewFile, +} from "shared/changes-types"; import { hasRenderedPreview, isImageFile } from "shared/file-types"; import type { BrowserPaneState, From 894a1cd423391a89d40cccc70471808a50a05bf4 Mon Sep 17 00:00:00 2001 From: Kiet Ho Date: Sun, 22 Feb 2026 11:18:53 -0800 Subject: [PATCH 03/14] feat(setup): add --mcp flag to configure local MCP server Adds -m/--mcp flag to .superset/setup.sh that adds or updates a superset-local entry in .mcp.json pointing to the local API port. --- .superset/lib/setup/args.sh | 6 ++++++ .superset/lib/setup/main.sh | 7 +++++++ .superset/lib/setup/steps.sh | 31 +++++++++++++++++++++++++++++++ 3 files changed, 44 insertions(+) diff --git a/.superset/lib/setup/args.sh b/.superset/lib/setup/args.sh index f1c780dd76f..c37d106f08b 100644 --- a/.superset/lib/setup/args.sh +++ b/.superset/lib/setup/args.sh @@ -1,6 +1,7 @@ # Setup argument parsing. FORCE_OVERWRITE_DATA=0 +SETUP_LOCAL_MCP=0 setup_print_usage() { cat < /dev/null; then + error "jq not available" + return 1 + fi + + local api_port="${API_PORT:-3001}" + local local_url="http://localhost:${api_port}/api/agent/mcp" + + # Add or update superset-local entry + local tmp_file="${mcp_file}.tmp.$$" + if ! jq --arg url "$local_url" '.mcpServers["superset-local"] = {"type": "http", "url": $url}' "$mcp_file" > "$tmp_file"; then + error "Failed to set local MCP entry" + rm -f "$tmp_file" + return 1 + fi + mv "$tmp_file" "$mcp_file" + success "Local MCP set to $local_url" + + return 0 +} + step_seed_auth_token() { echo "🔑 Seeding auth token into superset-dev-data/..." From f6c0dc7bb4d11df80aa6414c7f99ea23c978dbd4 Mon Sep 17 00:00:00 2001 From: Kiet Ho Date: Sun, 22 Feb 2026 11:23:14 -0800 Subject: [PATCH 04/14] feat(desktop): add agent selector to task view and refactor preset templates - Add agent dropdown to OpenInWorkspace with icons and localStorage persistence - Extract AGENT_LABELS, AGENT_PRESET_COMMANDS, AGENT_PRESET_DESCRIPTIONS into packages/shared for reuse - Refactor PresetsSection to derive PRESET_TEMPLATES from shared agent metadata --- .../OpenInWorkspace/OpenInWorkspace.tsx | 58 +++++++++++++++ .../components/PresetsSection.tsx | 70 ++++--------------- packages/shared/src/agent-command.ts | 38 +++++++++- packages/shared/src/claude-command.ts | 4 ++ 4 files changed, 110 insertions(+), 60 deletions(-) diff --git a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/tasks/$taskId/components/PropertiesSidebar/components/OpenInWorkspace/OpenInWorkspace.tsx b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/tasks/$taskId/components/PropertiesSidebar/components/OpenInWorkspace/OpenInWorkspace.tsx index 41455a63311..b9e4fca8784 100644 --- a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/tasks/$taskId/components/PropertiesSidebar/components/OpenInWorkspace/OpenInWorkspace.tsx +++ b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/tasks/$taskId/components/PropertiesSidebar/components/OpenInWorkspace/OpenInWorkspace.tsx @@ -1,3 +1,8 @@ +import { + AGENT_LABELS, + AGENT_TYPES, + type AgentType, +} from "@superset/shared/agent-command"; import { Button } from "@superset/ui/button"; import { DropdownMenu, @@ -5,9 +10,20 @@ import { DropdownMenuItem, DropdownMenuTrigger, } from "@superset/ui/dropdown-menu"; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "@superset/ui/select"; import { toast } from "@superset/ui/sonner"; import { useEffect, useState } from "react"; import { HiArrowRight, HiChevronDown } from "react-icons/hi2"; +import { + getPresetIcon, + useIsDarkTheme, +} from "renderer/assets/app-icons/preset-icons"; import { electronTrpc } from "renderer/lib/electron-trpc"; import { useCreateWorkspace } from "renderer/react-query/workspaces"; import { ProjectThumbnail } from "renderer/screens/main/components/WorkspaceSidebar/ProjectSection/ProjectThumbnail"; @@ -24,9 +40,13 @@ export function OpenInWorkspace({ task }: OpenInWorkspaceProps) { const { data: recentProjects = [] } = electronTrpc.projects.getRecents.useQuery(); const createWorkspace = useCreateWorkspace(); + const isDark = useIsDarkTheme(); const [selectedProjectId, setSelectedProjectId] = useState( () => localStorage.getItem("lastOpenedInProjectId"), ); + const [selectedAgent, setSelectedAgent] = useState( + () => (localStorage.getItem("lastSelectedAgent") as AgentType) || "claude", + ); // Default to the first recent project const effectiveProjectId = selectedProjectId ?? recentProjects[0]?.id ?? null; @@ -72,6 +92,7 @@ export function OpenInWorkspace({ task }: OpenInWorkspaceProps) { labels: task.labels, }, randomId: window.crypto.randomUUID(), + agent: selectedAgent, }); const store = useWorkspaceInitStore.getState(); @@ -94,6 +115,8 @@ export function OpenInWorkspace({ task }: OpenInWorkspaceProps) { } }; + const agentIcon = getPresetIcon(selectedAgent, isDark); + return (
Open in workspace @@ -168,6 +191,41 @@ export function OpenInWorkspace({ task }: OpenInWorkspaceProps) {
+ ); } diff --git a/apps/desktop/src/renderer/routes/_authenticated/settings/terminal/components/TerminalSettings/components/PresetsSection.tsx b/apps/desktop/src/renderer/routes/_authenticated/settings/terminal/components/TerminalSettings/components/PresetsSection.tsx index 65cbe43fe9e..bc8825965e6 100644 --- a/apps/desktop/src/renderer/routes/_authenticated/settings/terminal/components/TerminalSettings/components/PresetsSection.tsx +++ b/apps/desktop/src/renderer/routes/_authenticated/settings/terminal/components/TerminalSettings/components/PresetsSection.tsx @@ -1,4 +1,9 @@ import type { ExecutionMode, TerminalPreset } from "@superset/local-db"; +import { + AGENT_PRESET_COMMANDS, + AGENT_PRESET_DESCRIPTIONS, + AGENT_TYPES, +} from "@superset/shared/agent-command"; import { Button } from "@superset/ui/button"; import { Label } from "@superset/ui/label"; import { Tooltip, TooltipContent, TooltipTrigger } from "@superset/ui/tooltip"; @@ -29,64 +34,15 @@ interface PresetTemplate { }; } -const PRESET_TEMPLATES: PresetTemplate[] = [ - { - name: "codex", - preset: { - name: "codex", - description: "Danger mode: All permissions auto-approved", - cwd: "", - commands: [ - 'codex -c model_reasoning_effort="high" --ask-for-approval never --sandbox danger-full-access -c model_reasoning_summary="detailed" -c model_supports_reasoning_summaries=true', - ], - }, - }, - { - name: "claude", - preset: { - name: "claude", - description: "Danger mode: All permissions auto-approved", - cwd: "", - commands: ["claude --dangerously-skip-permissions"], - }, - }, - { - name: "gemini", - preset: { - name: "gemini", - description: "Danger mode: All permissions auto-approved", - cwd: "", - commands: ["gemini --yolo"], - }, - }, - { - name: "cursor-agent", - preset: { - name: "cursor-agent", - description: "Cursor AI agent for terminal-based coding assistance", - cwd: "", - commands: ["cursor-agent"], - }, - }, - { - name: "opencode", - preset: { - name: "opencode", - description: "OpenCode: Open-source AI coding agent", - cwd: "", - commands: ["opencode"], - }, - }, - { - name: "copilot", - preset: { - name: "copilot", - description: "GitHub Copilot: AI-powered coding in your terminal", - cwd: "", - commands: ["copilot"], - }, +const PRESET_TEMPLATES: PresetTemplate[] = AGENT_TYPES.map((agent) => ({ + name: agent, + preset: { + name: agent, + description: AGENT_PRESET_DESCRIPTIONS[agent], + cwd: "", + commands: AGENT_PRESET_COMMANDS[agent], }, -]; +})); interface PresetsSectionProps { showPresets: boolean; diff --git a/packages/shared/src/agent-command.ts b/packages/shared/src/agent-command.ts index a4145c3d379..43b073dd81b 100644 --- a/packages/shared/src/agent-command.ts +++ b/packages/shared/src/agent-command.ts @@ -9,6 +9,35 @@ export const AGENT_TYPES = [ export type AgentType = (typeof AGENT_TYPES)[number]; +export const AGENT_LABELS: Record = { + claude: "Claude", + codex: "Codex", + gemini: "Gemini", + opencode: "OpenCode", + copilot: "Copilot", + "cursor-agent": "Cursor Agent", +}; + +export const AGENT_PRESET_COMMANDS: Record = { + claude: ["claude --dangerously-skip-permissions"], + codex: [ + 'codex -c model_reasoning_effort="high" --ask-for-approval never --sandbox danger-full-access -c model_reasoning_summary="detailed" -c model_supports_reasoning_summaries=true', + ], + gemini: ["gemini --yolo"], + opencode: ["opencode"], + copilot: ["copilot"], + "cursor-agent": ["cursor-agent"], +}; + +export const AGENT_PRESET_DESCRIPTIONS: Record = { + claude: "Danger mode: All permissions auto-approved", + codex: "Danger mode: All permissions auto-approved", + gemini: "Danger mode: All permissions auto-approved", + opencode: "OpenCode: Open-source AI coding agent", + copilot: "GitHub Copilot: AI-powered coding in your terminal", + "cursor-agent": "Cursor AI agent for terminal-based coding assistance", +}; + export interface TaskInput { id: string; slug: string; @@ -58,9 +87,12 @@ function buildHeredoc( suffix?: string, ): string { const closing = suffix ? `)" ${suffix}` : ')"'; - return [`${command} "$(cat <<'${delimiter}'`, prompt, delimiter, closing].join( - "\n", - ); + return [ + `${command} "$(cat <<'${delimiter}'`, + prompt, + delimiter, + closing, + ].join("\n"); } const AGENT_COMMANDS: Record< diff --git a/packages/shared/src/claude-command.ts b/packages/shared/src/claude-command.ts index 7cdb3dbf6cc..1eb61781c32 100644 --- a/packages/shared/src/claude-command.ts +++ b/packages/shared/src/claude-command.ts @@ -1,5 +1,9 @@ /** @deprecated Use `@superset/shared/agent-command` instead */ export { + AGENT_LABELS, + AGENT_PRESET_COMMANDS, + AGENT_PRESET_DESCRIPTIONS, + AGENT_TYPES, type AgentType, buildAgentCommand, buildClaudeCommand, From ad88dacd9417d0c63a3c702ebebdd5525d7aff30 Mon Sep 17 00:00:00 2001 From: Kiet Ho Date: Sun, 22 Feb 2026 11:25:47 -0800 Subject: [PATCH 05/14] fix(desktop): remove duplicated agent icon in select trigger --- .../OpenInWorkspace/OpenInWorkspace.tsx | 24 +++++-------------- 1 file changed, 6 insertions(+), 18 deletions(-) diff --git a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/tasks/$taskId/components/PropertiesSidebar/components/OpenInWorkspace/OpenInWorkspace.tsx b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/tasks/$taskId/components/PropertiesSidebar/components/OpenInWorkspace/OpenInWorkspace.tsx index b9e4fca8784..19e91057b01 100644 --- a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/tasks/$taskId/components/PropertiesSidebar/components/OpenInWorkspace/OpenInWorkspace.tsx +++ b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/tasks/$taskId/components/PropertiesSidebar/components/OpenInWorkspace/OpenInWorkspace.tsx @@ -203,27 +203,15 @@ export function OpenInWorkspace({ task }: OpenInWorkspaceProps) { {agentIcon && ( )} - + - {AGENT_TYPES.map((agent) => { - const icon = getPresetIcon(agent, isDark); - return ( - - - {icon && ( - - )} - {AGENT_LABELS[agent]} - - - ); - })} + {AGENT_TYPES.map((agent) => ( + + {AGENT_LABELS[agent]} + + ))} From ef5e10b89f5471cf022ee3f346c24f57b3b0ed96 Mon Sep 17 00:00:00 2001 From: Kiet Ho Date: Sun, 22 Feb 2026 11:26:33 -0800 Subject: [PATCH 06/14] fix(desktop): show agent icon in select items, not trigger --- .../OpenInWorkspace/OpenInWorkspace.tsx | 31 +++++++++++-------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/tasks/$taskId/components/PropertiesSidebar/components/OpenInWorkspace/OpenInWorkspace.tsx b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/tasks/$taskId/components/PropertiesSidebar/components/OpenInWorkspace/OpenInWorkspace.tsx index 19e91057b01..851e15d63ed 100644 --- a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/tasks/$taskId/components/PropertiesSidebar/components/OpenInWorkspace/OpenInWorkspace.tsx +++ b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/tasks/$taskId/components/PropertiesSidebar/components/OpenInWorkspace/OpenInWorkspace.tsx @@ -115,8 +115,6 @@ export function OpenInWorkspace({ task }: OpenInWorkspaceProps) { } }; - const agentIcon = getPresetIcon(selectedAgent, isDark); - return (
Open in workspace @@ -199,19 +197,26 @@ export function OpenInWorkspace({ task }: OpenInWorkspaceProps) { }} > - - {agentIcon && ( - - )} - - + - {AGENT_TYPES.map((agent) => ( - - {AGENT_LABELS[agent]} - - ))} + {AGENT_TYPES.map((agent) => { + const icon = getPresetIcon(agent, isDark); + return ( + + + {icon && ( + + )} + {AGENT_LABELS[agent]} + + + ); + })}
From 9a4326ba8ff9717225ce5da2a59243387ef12443 Mon Sep 17 00:00:00 2001 From: Kiet Ho Date: Sun, 22 Feb 2026 11:30:04 -0800 Subject: [PATCH 07/14] fix(desktop): use agentCommand field for separate pane instead of initialCommands --- .../components/OpenInWorkspace/OpenInWorkspace.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/tasks/$taskId/components/PropertiesSidebar/components/OpenInWorkspace/OpenInWorkspace.tsx b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/tasks/$taskId/components/PropertiesSidebar/components/OpenInWorkspace/OpenInWorkspace.tsx index 851e15d63ed..a117efb0437 100644 --- a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/tasks/$taskId/components/PropertiesSidebar/components/OpenInWorkspace/OpenInWorkspace.tsx +++ b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/tasks/$taskId/components/PropertiesSidebar/components/OpenInWorkspace/OpenInWorkspace.tsx @@ -100,8 +100,9 @@ export function OpenInWorkspace({ task }: OpenInWorkspaceProps) { store.addPendingTerminalSetup({ workspaceId: result.workspace.id, projectId: result.projectId, - initialCommands: [...(pending?.initialCommands ?? []), command], + initialCommands: pending?.initialCommands ?? null, defaultPresets: pending?.defaultPresets, + agentCommand: command, }); } From 323ff2c9547d7da03a0241c75de46760584d3da3 Mon Sep 17 00:00:00 2001 From: Kiet Ho Date: Sun, 22 Feb 2026 11:32:59 -0800 Subject: [PATCH 08/14] fix(shared): launch opencode in interactive TUI mode instead of headless --- packages/shared/src/agent-command.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/shared/src/agent-command.ts b/packages/shared/src/agent-command.ts index 43b073dd81b..f00f5d31215 100644 --- a/packages/shared/src/agent-command.ts +++ b/packages/shared/src/agent-command.ts @@ -105,8 +105,7 @@ const AGENT_COMMANDS: Record< buildHeredoc(prompt, delimiter, "codex --full-auto"), gemini: (prompt, delimiter) => buildHeredoc(prompt, delimiter, "gemini --yolo"), - opencode: (prompt, delimiter) => - buildHeredoc(prompt, delimiter, "opencode run"), + opencode: () => "opencode", copilot: (prompt, delimiter) => buildHeredoc(prompt, delimiter, "copilot -p", "--yolo"), "cursor-agent": (prompt, delimiter) => From 93f5be29ba080710ac916c81b026708b88708edf Mon Sep 17 00:00:00 2001 From: Kiet Ho Date: Sun, 22 Feb 2026 11:34:08 -0800 Subject: [PATCH 09/14] fix(shared): launch opencode in interactive mode with prompt --- packages/shared/src/agent-command.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/shared/src/agent-command.ts b/packages/shared/src/agent-command.ts index f00f5d31215..2e96c654433 100644 --- a/packages/shared/src/agent-command.ts +++ b/packages/shared/src/agent-command.ts @@ -105,7 +105,8 @@ const AGENT_COMMANDS: Record< buildHeredoc(prompt, delimiter, "codex --full-auto"), gemini: (prompt, delimiter) => buildHeredoc(prompt, delimiter, "gemini --yolo"), - opencode: () => "opencode", + opencode: (prompt, delimiter) => + buildHeredoc(prompt, delimiter, "opencode"), copilot: (prompt, delimiter) => buildHeredoc(prompt, delimiter, "copilot -p", "--yolo"), "cursor-agent": (prompt, delimiter) => From 71d8cf53d6013b4655faf6b1169064f20e0d4b99 Mon Sep 17 00:00:00 2001 From: Kiet Ho Date: Sun, 22 Feb 2026 11:37:49 -0800 Subject: [PATCH 10/14] fix(shared): use --prompt flag for opencode interactive mode --- packages/shared/src/agent-command.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/shared/src/agent-command.ts b/packages/shared/src/agent-command.ts index 2e96c654433..74dddc62827 100644 --- a/packages/shared/src/agent-command.ts +++ b/packages/shared/src/agent-command.ts @@ -106,7 +106,7 @@ const AGENT_COMMANDS: Record< gemini: (prompt, delimiter) => buildHeredoc(prompt, delimiter, "gemini --yolo"), opencode: (prompt, delimiter) => - buildHeredoc(prompt, delimiter, "opencode"), + buildHeredoc(prompt, delimiter, "opencode --prompt"), copilot: (prompt, delimiter) => buildHeredoc(prompt, delimiter, "copilot -p", "--yolo"), "cursor-agent": (prompt, delimiter) => From 47eb26b914efa066dbf646329fd24678b01df12a Mon Sep 17 00:00:00 2001 From: Kiet Ho Date: Sun, 22 Feb 2026 11:40:25 -0800 Subject: [PATCH 11/14] fix(desktop): send agent command to existing workspaces via new tab --- .../OpenInWorkspace/OpenInWorkspace.tsx | 36 +++++++++++-------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/tasks/$taskId/components/PropertiesSidebar/components/OpenInWorkspace/OpenInWorkspace.tsx b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/tasks/$taskId/components/PropertiesSidebar/components/OpenInWorkspace/OpenInWorkspace.tsx index a117efb0437..2f1104dd82b 100644 --- a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/tasks/$taskId/components/PropertiesSidebar/components/OpenInWorkspace/OpenInWorkspace.tsx +++ b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/tasks/$taskId/components/PropertiesSidebar/components/OpenInWorkspace/OpenInWorkspace.tsx @@ -27,6 +27,7 @@ import { import { electronTrpc } from "renderer/lib/electron-trpc"; import { useCreateWorkspace } from "renderer/react-query/workspaces"; import { ProjectThumbnail } from "renderer/screens/main/components/WorkspaceSidebar/ProjectSection/ProjectThumbnail"; +import { useTabsStore } from "renderer/stores/tabs/store"; import { useWorkspaceInitStore } from "renderer/stores/workspace-init"; import type { TaskWithStatus } from "../../../../../components/TasksView/hooks/useTasksTable"; import { buildAgentCommand } from "../../../../utils/buildAgentCommand"; @@ -40,6 +41,8 @@ export function OpenInWorkspace({ task }: OpenInWorkspaceProps) { const { data: recentProjects = [] } = electronTrpc.projects.getRecents.useQuery(); const createWorkspace = useCreateWorkspace(); + const addTab = useTabsStore((s) => s.addTab); + const setTabAutoTitle = useTabsStore((s) => s.setTabAutoTitle); const isDark = useIsDarkTheme(); const [selectedProjectId, setSelectedProjectId] = useState( () => localStorage.getItem("lastOpenedInProjectId"), @@ -80,21 +83,26 @@ export function OpenInWorkspace({ task }: OpenInWorkspaceProps) { branchName, }); - if (!result.wasExisting) { - const command = buildAgentCommand({ - task: { - id: task.id, - slug: task.slug, - title: task.title, - description: task.description, - priority: task.priority, - statusName: task.status.name, - labels: task.labels, - }, - randomId: window.crypto.randomUUID(), - agent: selectedAgent, - }); + const command = buildAgentCommand({ + task: { + id: task.id, + slug: task.slug, + title: task.title, + description: task.description, + priority: task.priority, + statusName: task.status.name, + labels: task.labels, + }, + randomId: window.crypto.randomUUID(), + agent: selectedAgent, + }); + if (result.wasExisting) { + const { tabId } = addTab(result.workspace.id, { + initialCommands: [command], + }); + setTabAutoTitle(tabId, "Agent"); + } else { const store = useWorkspaceInitStore.getState(); const pending = store.pendingTerminalSetups[result.workspace.id]; store.addPendingTerminalSetup({ From e0d0177bb58b54f8f450821143246365eda4c32d Mon Sep 17 00:00:00 2001 From: Kiet Ho Date: Sun, 22 Feb 2026 11:46:38 -0800 Subject: [PATCH 12/14] fix(shared): correct copilot and cursor-agent CLI flags for interactive mode --- packages/shared/src/agent-command.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/shared/src/agent-command.ts b/packages/shared/src/agent-command.ts index 74dddc62827..7f3c1014c97 100644 --- a/packages/shared/src/agent-command.ts +++ b/packages/shared/src/agent-command.ts @@ -108,9 +108,9 @@ const AGENT_COMMANDS: Record< opencode: (prompt, delimiter) => buildHeredoc(prompt, delimiter, "opencode --prompt"), copilot: (prompt, delimiter) => - buildHeredoc(prompt, delimiter, "copilot -p", "--yolo"), + buildHeredoc(prompt, delimiter, "copilot -i", "--yolo"), "cursor-agent": (prompt, delimiter) => - buildHeredoc(prompt, delimiter, "cursor-agent -p", "--yolo"), + buildHeredoc(prompt, delimiter, "cursor-agent --yolo"), }; export function buildAgentCommand({ From 02ae0ce517453a7558ae464480ffad5f2c025e05 Mon Sep 17 00:00:00 2001 From: Kiet Ho Date: Sun, 22 Feb 2026 11:50:04 -0800 Subject: [PATCH 13/14] Deslop --- .../components/OpenInWorkspace/OpenInWorkspace.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/tasks/$taskId/components/PropertiesSidebar/components/OpenInWorkspace/OpenInWorkspace.tsx b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/tasks/$taskId/components/PropertiesSidebar/components/OpenInWorkspace/OpenInWorkspace.tsx index 2f1104dd82b..56aaaed0191 100644 --- a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/tasks/$taskId/components/PropertiesSidebar/components/OpenInWorkspace/OpenInWorkspace.tsx +++ b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/tasks/$taskId/components/PropertiesSidebar/components/OpenInWorkspace/OpenInWorkspace.tsx @@ -51,13 +51,11 @@ export function OpenInWorkspace({ task }: OpenInWorkspaceProps) { () => (localStorage.getItem("lastSelectedAgent") as AgentType) || "claude", ); - // Default to the first recent project const effectiveProjectId = selectedProjectId ?? recentProjects[0]?.id ?? null; const selectedProject = recentProjects.find( (p) => p.id === effectiveProjectId, ); - // Sync default once projects load useEffect(() => { if (!selectedProjectId && recentProjects.length > 0) { setSelectedProjectId(recentProjects[0].id); From 78842533fc1088ea30d6c472e12aaa21125615d1 Mon Sep 17 00:00:00 2001 From: Kiet Ho Date: Sun, 22 Feb 2026 11:58:59 -0800 Subject: [PATCH 14/14] fix: address PR review feedback for agent command safety - Guard against heredoc delimiter collision with prompt content - Derive API_PORT from SUPERSET_PORT_BASE for non-first workspaces - Add error handling for mv in local MCP setup - Validate localStorage agent type against AGENT_TYPES before use --- .superset/lib/setup/steps.sh | 8 ++++++-- .../components/OpenInWorkspace/OpenInWorkspace.tsx | 9 ++++++--- packages/shared/src/agent-command.ts | 5 ++++- 3 files changed, 16 insertions(+), 6 deletions(-) diff --git a/.superset/lib/setup/steps.sh b/.superset/lib/setup/steps.sh index 018481798be..acd8b262191 100644 --- a/.superset/lib/setup/steps.sh +++ b/.superset/lib/setup/steps.sh @@ -463,7 +463,7 @@ step_setup_local_mcp() { return 1 fi - local api_port="${API_PORT:-3001}" + local api_port="${API_PORT:-$((${SUPERSET_PORT_BASE:-3000} + 1))}" local local_url="http://localhost:${api_port}/api/agent/mcp" # Add or update superset-local entry @@ -473,7 +473,11 @@ step_setup_local_mcp() { rm -f "$tmp_file" return 1 fi - mv "$tmp_file" "$mcp_file" + if ! mv "$tmp_file" "$mcp_file"; then + error "Failed to write $mcp_file" + rm -f "$tmp_file" + return 1 + fi success "Local MCP set to $local_url" return 0 diff --git a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/tasks/$taskId/components/PropertiesSidebar/components/OpenInWorkspace/OpenInWorkspace.tsx b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/tasks/$taskId/components/PropertiesSidebar/components/OpenInWorkspace/OpenInWorkspace.tsx index 56aaaed0191..074173d084d 100644 --- a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/tasks/$taskId/components/PropertiesSidebar/components/OpenInWorkspace/OpenInWorkspace.tsx +++ b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/tasks/$taskId/components/PropertiesSidebar/components/OpenInWorkspace/OpenInWorkspace.tsx @@ -47,9 +47,12 @@ export function OpenInWorkspace({ task }: OpenInWorkspaceProps) { const [selectedProjectId, setSelectedProjectId] = useState( () => localStorage.getItem("lastOpenedInProjectId"), ); - const [selectedAgent, setSelectedAgent] = useState( - () => (localStorage.getItem("lastSelectedAgent") as AgentType) || "claude", - ); + const [selectedAgent, setSelectedAgent] = useState(() => { + const stored = localStorage.getItem("lastSelectedAgent"); + return stored && (AGENT_TYPES as readonly string[]).includes(stored) + ? (stored as AgentType) + : "claude"; + }); const effectiveProjectId = selectedProjectId ?? recentProjects[0]?.id ?? null; const selectedProject = recentProjects.find( diff --git a/packages/shared/src/agent-command.ts b/packages/shared/src/agent-command.ts index 7f3c1014c97..6e90133451e 100644 --- a/packages/shared/src/agent-command.ts +++ b/packages/shared/src/agent-command.ts @@ -123,7 +123,10 @@ export function buildAgentCommand({ agent?: AgentType; }): string { const prompt = buildPrompt(task); - const delimiter = `SUPERSET_PROMPT_${randomId.replaceAll("-", "")}`; + let delimiter = `SUPERSET_PROMPT_${randomId.replaceAll("-", "")}`; + while (prompt.includes(delimiter)) { + delimiter = `${delimiter}_X`; + } const builder = AGENT_COMMANDS[agent]; return builder(prompt, delimiter); }