diff --git a/src/app/(dashboard)/dashboard/cli-tools/[toolId]/ToolDetailClient.js b/src/app/(dashboard)/dashboard/cli-tools/[toolId]/ToolDetailClient.js
index 209a6d33..82211e48 100644
--- a/src/app/(dashboard)/dashboard/cli-tools/[toolId]/ToolDetailClient.js
+++ b/src/app/(dashboard)/dashboard/cli-tools/[toolId]/ToolDetailClient.js
@@ -8,7 +8,7 @@ import { getModelsByProviderId, PROVIDER_ID_TO_ALIAS } from "@/shared/constants/
import {
ClaudeToolCard, CodexToolCard, DroidToolCard, OpenClawToolCard,
HermesToolCard, DefaultToolCard, OpenCodeToolCard, CoworkToolCard,
- CopilotToolCard, ClineToolCard, KiloToolCard, DeepSeekTuiToolCard,
+ ClineToolCard, KiloToolCard, DeepSeekTuiToolCard,
JcodeToolCard, GrokBuildToolCard,
} from "../components";
@@ -156,8 +156,6 @@ export default function ToolDetailClient({ toolId, machineId }) {
return ;
case "hermes":
return ;
- case "copilot":
- return ;
case "cline":
return ;
case "kilo":
diff --git a/src/app/(dashboard)/dashboard/cli-tools/components/ToolSummaryCard.js b/src/app/(dashboard)/dashboard/cli-tools/components/ToolSummaryCard.js
index 6e944d79..83d6c15b 100644
--- a/src/app/(dashboard)/dashboard/cli-tools/components/ToolSummaryCard.js
+++ b/src/app/(dashboard)/dashboard/cli-tools/components/ToolSummaryCard.js
@@ -5,7 +5,8 @@ import Image from "next/image";
import { Card } from "@/shared/components";
// Derive simple connected/configured/not-installed status from API payload
-function getStatus(status) {
+function getStatus(status, tool) {
+ if (tool?.configType === "guide") return { label: "Guide", cls: "bg-blue-500/10 text-blue-600 dark:text-blue-400" };
if (!status) return { label: "Unknown", cls: "bg-gray-500/10 text-gray-500" };
if (!status.installed) return { label: "Not installed", cls: "bg-gray-500/10 text-gray-500" };
if (status.has9Router) return { label: "Connected", cls: "bg-green-500/10 text-green-600 dark:text-green-400" };
@@ -13,7 +14,7 @@ function getStatus(status) {
}
export default function ToolSummaryCard({ toolId, tool, status }) {
- const s = getStatus(status);
+ const s = getStatus(status, tool);
return (
diff --git a/src/app/api/cli-tools/all-statuses/route.js b/src/app/api/cli-tools/all-statuses/route.js
index 5925e526..b4270163 100644
--- a/src/app/api/cli-tools/all-statuses/route.js
+++ b/src/app/api/cli-tools/all-statuses/route.js
@@ -8,7 +8,6 @@ import { GET as droidGet } from "../droid-settings/route";
import { GET as openclawGet } from "../openclaw-settings/route";
import { GET as hermesGet } from "../hermes-settings/route";
import { GET as coworkGet } from "../cowork-settings/route";
-import { GET as copilotGet } from "../copilot-settings/route";
import { GET as clineGet } from "../cline-settings/route";
import { GET as kiloGet } from "../kilo-settings/route";
import { GET as deepseekTuiGet } from "../deepseek-tui-settings/route";
@@ -24,7 +23,6 @@ const STATUS_GETTERS = {
openclaw: openclawGet,
hermes: hermesGet,
cowork: coworkGet,
- copilot: copilotGet,
cline: clineGet,
kilo: kiloGet,
"deepseek-tui": deepseekTuiGet,
diff --git a/src/shared/constants/cliTools.js b/src/shared/constants/cliTools.js
index 9a388866..0f7c0236 100644
--- a/src/shared/constants/cliTools.js
+++ b/src/shared/constants/cliTools.js
@@ -30,32 +30,6 @@ export const MITM_TOOLS = {
{ id: "gemini-3-flash", name: "Gemini 3 Flash (Command)", alias: "gemini-3-flash" },
],
},
- copilot: {
- id: "copilot",
- name: "GitHub Copilot",
- image: "/providers/copilot.png",
- color: "#1F6FEB",
- description: "GitHub Copilot IDE with MITM",
- configType: "mitm",
- mitmDomain: "api.individual.githubcopilot.com",
- modelAliases: ["gpt-5-mini", "gpt-5.4-nano", "claude-haiku-4.5", "gpt-4o", "gpt-4.1"],
- defaultModels: [
- // Verified via live MITM passthrough capture of the GitHub Copilot CLI: its model
- // picker offers "GPT-5 mini" (default → wire id "gpt-5-mini"), "Claude Haiku 4.5"
- // ("claude-haiku-4.5") and "Auto". "Auto" is NOT a wire id — Copilot dispatches
- // concrete models dynamically (observed "gpt-5.4-nano" for light tasks and
- // "claude-haiku-4.5"), so it needs no slot of its own. Without a slot for
- // gpt-5-mini / gpt-5.4-nano, getMappedModel returns null and the /chat/completions
- // call is passed through to GitHub Copilot instead of the configured provider —
- // and gpt-5-mini is the CLI default, so the primary turn leaks (same class as the
- // Kiro "auto" misrouting). gpt-4o / gpt-4.1 are kept for the VS Code Copilot Chat picker.
- { id: "gpt-5-mini", name: "GPT-5 mini", alias: "gpt-5-mini" },
- { id: "gpt-5.4-nano", name: "GPT-5.4 nano", alias: "gpt-5.4-nano" },
- { id: "claude-haiku-4.5", name: "Claude Haiku 4.5", alias: "claude-haiku-4.5" },
- { id: "gpt-4o", name: "GPT-4o", alias: "gpt-4o" },
- { id: "gpt-4.1", name: "GPT-4.1", alias: "gpt-4.1" },
- ],
- },
kiro: {
id: "kiro",
name: "Kiro",
@@ -140,6 +114,34 @@ export const CLI_TOOLS = {
description: "OpenAI Codex CLI",
configType: "custom",
},
+ copilot: {
+ id: "copilot",
+ name: "GitHub Copilot",
+ image: "/providers/copilot.png",
+ color: "#1F6FEB",
+ description: "GitHub Copilot in VS Code via 9Router extension",
+ configType: "guide",
+ docsUrl: "https://marketplace.visualstudio.com/items?itemName=hotrungnhan.9router-for-github-copilot",
+ guideSteps: [
+ {
+ step: 1,
+ title: "Install Extension",
+ desc: "In VS Code, open Extensions (Ctrl+Shift+X or Cmd+Shift+X), search for '9Router for Github Copilot' and click Install.",
+ },
+ {
+ step: 2,
+ title: "Configure Server",
+ desc: "Press Cmd+Shift+P (or Ctrl+Shift+P), run '9Router: Configure Server', then enter your Server URL and API Key:",
+ value: "{{baseUrl}}",
+ copyable: true,
+ },
+ {
+ step: 3,
+ title: "Select Model in Copilot Chat",
+ desc: "Open Copilot Chat, click the model picker at the bottom → 'Manage Models...' → check the 9Router models to use.",
+ },
+ ],
+ },
opencode: {
id: "opencode",
name: "OpenCode",