diff --git a/README.md b/README.md
index ff70c47f..20df9579 100644
--- a/README.md
+++ b/README.md
@@ -26,7 +26,7 @@ It connects to **14 AI coding tools** via Unix socket IPC, displaying session st
## Features
- **Notch-native UI** — Expands from the MacBook notch, collapses when idle
-- **14 AI tools supported** — Claude Code, Codex, Grok CLI, Gemini CLI, Cursor, Copilot, Trae/Traecli, Qoder, Factory, CodeBuddy, OpenCode, Kimi Code CLI, Cline, Pi / Oh My Pi
+- **15 AI tools supported** — Claude Code, Codex, Grok CLI, Gemini CLI, Cursor, Copilot, Trae/Traecli, Qoder, Factory, CodeBuddy, OpenCode, Kimi Code CLI, Cline, Pi / Oh My Pi, DeepSeek Harness
- **Live status tracking** — See active sessions, tool calls, and AI responses in real time
- **Permission management** — Approve/deny tool permissions directly from the panel
- **Question answering** — Respond to agent questions without leaving your current app
@@ -57,6 +57,7 @@ It connects to **14 AI coding tools** via Unix socket IPC, displaying session st
|
|
OpenCode | All | APP/Terminal | Full |
|
|
Cline | 5 | VSCode | Full |
| |
Pi / Oh My Pi | 8 | Terminal | Full |
+| |
DeepSeek Harness | 9 | Terminal | Full |
## Installation
@@ -118,6 +119,14 @@ CodeIsland installs lightweight hooks into each AI tool's config. When the tool
For **OpenCode**, a JS plugin connects directly to the socket — no bridge binary needed.
+For **DeepSeek Harness (DSH)**, the [dsh-island](https://github.com/cdxiaodong/dsh-island) cordis plugin listens to DSH's built-in events (`session/created`, `tools/pre-execute`, `approval/request`, …) and writes the same JSON over the Unix socket. Install it inside DSH:
+
+```bash
+dsh plugin --profile add github:cdxiaodong/dsh-island
+```
+
+DSH is plugin-native, so no hook configuration is installed — CodeIsland only needs to know the `dsh` source name to render its session card.
+
## Settings
CodeIsland provides a 7-tab settings panel:
diff --git a/README.zh-CN.md b/README.zh-CN.md
index 95b051bf..42f8f6f5 100644
--- a/README.zh-CN.md
+++ b/README.zh-CN.md
@@ -26,7 +26,7 @@ CodeIsland 住在你 MacBook 的刘海区域,实时展示 AI 编码 Agent 的
## 功能特性
- **刘海原生 UI** — 从 MacBook 刘海处展开,空闲时自动收起
-- **支持 14 种 AI 工具** — Claude Code、Codex、Grok CLI、Gemini CLI、Cursor、Copilot、Trae/Traecli、Qoder、Factory、CodeBuddy、OpenCode、Kimi Code CLI、Cline、Pi / Oh My Pi
+- **支持 15 种 AI 工具** — Claude Code、Codex、Grok CLI、Gemini CLI、Cursor、Copilot、Trae/Traecli、Qoder、Factory、CodeBuddy、OpenCode、Kimi Code CLI、Cline、Pi / Oh My Pi、DeepSeek Harness
- **实时状态追踪** — 查看活跃会话、工具调用和 AI 回复
- **权限管理** — 直接在面板上审批/拒绝工具权限请求
- **问题回答** — 无需离开当前应用即可回答 Agent 的问题
@@ -57,6 +57,7 @@ CodeIsland 住在你 MacBook 的刘海区域,实时展示 AI 编码 Agent 的
|
|
OpenCode | All | APP/终端 | 完整 |
|
|
Cline | 5 | VSCode | 完整 |
| |
Pi / Oh My Pi | 8 | 终端 | 完整 |
+| |
DeepSeek Harness | 9 | 终端 | 完整 |
## 安装
@@ -118,6 +119,14 @@ CodeIsland 在每个 AI 工具的配置中安装轻量级 hooks。当工具触
**OpenCode** 使用 JS 插件直接连接 socket,无需 bridge 二进制。
+**DeepSeek Harness(DSH)** 通过 [dsh-island](https://github.com/cdxiaodong/dsh-island) cordis 插件接入:插件监听 DSH 内置事件(`session/created`、`tools/pre-execute`、`approval/request` 等),把同样的 JSON 写入 Unix socket。在 DSH 内安装:
+
+```bash
+dsh plugin --profile add github:cdxiaodong/dsh-island
+```
+
+DSH 是插件原生运行时,因此无需安装任何 hook 配置 —— CodeIsland 只需识别 `dsh` source 即可渲染它的会话卡片。
+
## 设置
CodeIsland 提供 7 个标签页的设置面板:
diff --git a/Sources/CodeIsland/ConfigInstaller.swift b/Sources/CodeIsland/ConfigInstaller.swift
index f815ad52..56d92de2 100644
--- a/Sources/CodeIsland/ConfigInstaller.swift
+++ b/Sources/CodeIsland/ConfigInstaller.swift
@@ -606,6 +606,19 @@ struct ConfigInstaller {
format: .none,
events: []
),
+ // DSH (DeepSeek Harness) — cordis plugin runtime ("everything is a
+ // plugin"). No shell hooks: the dsh-island plugin
+ // (github:cdxiaodong/dsh-island) listens to DSH's built-in events and
+ // forwards them over the CodeIsland socket directly. CodeIsland only
+ // needs the source name so DSH events render as their own session card.
+ CLIConfig(
+ name: "DeepSeek Harness",
+ source: "dsh",
+ configPath: ".dsh",
+ configKey: "",
+ format: .none,
+ events: []
+ ),
// ZCode (Z.ai) — Electron desktop app, NOT a Claude Code fork. Reads
// hooks from ~/.zcode/cli/config.json (strict event whitelist, no
// hot-reload — see `.zcode` HookFormat doc for details) (#245).
@@ -935,7 +948,7 @@ struct ConfigInstaller {
if !installHermesHooks(fm: fm) { ok = false }
} else if cli.format == .zcode {
if !installZcodeHooks(fm: fm) { ok = false }
- } else if cli.source == "pi" || cli.source == "omp" || cli.source == "openclaw" {
+ } else if cli.source == "pi" || cli.source == "omp" || cli.source == "openclaw" || cli.source == "dsh" {
continue
} else {
if !installExternalHooks(cli: cli, fm: fm) { ok = false }
@@ -992,6 +1005,8 @@ struct ConfigInstaller {
uninstallOmpExtension(fm: fm)
} else if cli.source == "openclaw" {
uninstallOpenclawPlugin(fm: fm)
+ } else if cli.source == "dsh" {
+ // DSH is bridged via the dsh-island plugin; no shell hooks to remove.
} else {
uninstallHooks(cli: cli, fm: fm)
}
@@ -1013,6 +1028,7 @@ struct ConfigInstaller {
if source == "pi" { return isPiExtensionInstalled(fm: FileManager.default) }
if source == "omp" { return isOmpExtensionInstalled(fm: FileManager.default) }
if source == "openclaw" { return isOpenclawPluginInstalled(fm: FileManager.default) }
+ if source == "dsh" { return isDshInstalled() }
if source == "traecli" { return isTraecliHooksInstalled(fm: FileManager.default) }
if source == "hermes" { return isHermesHooksInstalled(fm: FileManager.default) }
if source == "zcode" { return isZcodeHooksInstalled(fm: FileManager.default) }
@@ -1030,6 +1046,13 @@ struct ConfigInstaller {
if source == "pi" { return FileManager.default.fileExists(atPath: piAgentDir) }
if source == "omp" { return FileManager.default.fileExists(atPath: ompAgentDir) }
if source == "openclaw" { return FileManager.default.fileExists(atPath: openclawDir) }
+ if source == "dsh" {
+ // DSH harness home defaults to ~/.dsh (override with $DSH_HOME).
+ if let home = ProcessInfo.processInfo.environment["DSH_HOME"], !home.isEmpty {
+ return FileManager.default.fileExists(atPath: home)
+ }
+ return FileManager.default.fileExists(atPath: NSHomeDirectory() + "/.dsh")
+ }
if source == "grok" { return FileManager.default.fileExists(atPath: grokHome()) }
if source == "copilot" { return FileManager.default.fileExists(atPath: NSHomeDirectory() + "/.copilot") }
if source == "cline" {
@@ -1053,6 +1076,17 @@ struct ConfigInstaller {
return FileManager.default.fileExists(atPath: cli.dirPath)
}
+ /// DSH (DeepSeek Harness) is bridged via the dsh-island plugin
+ /// (`dsh plugin add github:cdxiaodong/dsh-island`). CodeIsland cannot
+ /// introspect the cordis plugin store, so "installed" means the harness is
+ /// present on this machine — its plugin wiring is the user's responsibility.
+ static func isDshInstalled() -> Bool {
+ if let home = ProcessInfo.processInfo.environment["DSH_HOME"], !home.isEmpty {
+ return FileManager.default.fileExists(atPath: home)
+ }
+ return FileManager.default.fileExists(atPath: NSHomeDirectory() + "/.dsh")
+ }
+
// Keep backward compat
static func isCodexInstalled() -> Bool { isInstalled(source: "codex") }
diff --git a/Sources/CodeIsland/NotchPanelView.swift b/Sources/CodeIsland/NotchPanelView.swift
index 50c9bc7b..210dd5e4 100644
--- a/Sources/CodeIsland/NotchPanelView.swift
+++ b/Sources/CodeIsland/NotchPanelView.swift
@@ -2918,6 +2918,7 @@ private let cliIconFiles: [String: String] = [
"omp": "pi",
"opencode": "opencode",
"cline": "cline",
+ "dsh": "dsh",
// Rendered from the in-house pixel mascots via
// MascotRenderHarness/testRenderCliIcons (MASCOT_ICON_DIR=…).
"kiro": "kiro",
diff --git a/Sources/CodeIsland/Resources/cli-icons/dsh.png b/Sources/CodeIsland/Resources/cli-icons/dsh.png
new file mode 100644
index 00000000..d1a4d983
Binary files /dev/null and b/Sources/CodeIsland/Resources/cli-icons/dsh.png differ
diff --git a/Sources/CodeIslandCore/Models.swift b/Sources/CodeIslandCore/Models.swift
index 4fdd04a9..92a6862f 100644
--- a/Sources/CodeIslandCore/Models.swift
+++ b/Sources/CodeIslandCore/Models.swift
@@ -15,6 +15,10 @@ public enum CLIProcessResolver {
return lowercasedPath.hasSuffix("/codex") || lowercasedPath.contains("/codex ")
case "claude":
return lowercasedPath.hasSuffix("/claude") || lowercasedPath.contains("/claude ")
+ case "dsh":
+ // DeepSeek Harness CLI ships a `dsh` binary. It has no shell hooks —
+ // the dsh-island plugin forwards events over the socket directly.
+ return lowercasedPath.hasSuffix("/dsh") || lowercasedPath.contains("/dsh ")
case "qwen":
return lowercasedPath.hasSuffix("/qwen")
|| lowercasedPath.hasSuffix("/qwen-code")
diff --git a/Sources/CodeIslandCore/SessionSnapshot.swift b/Sources/CodeIslandCore/SessionSnapshot.swift
index 6bfe801c..b8bb3c86 100644
--- a/Sources/CodeIslandCore/SessionSnapshot.swift
+++ b/Sources/CodeIslandCore/SessionSnapshot.swift
@@ -38,6 +38,7 @@ public struct SessionSnapshot: Sendable {
"pi",
"kiro",
"cline",
+ "dsh",
"zcode",
]