From bd4bc84283e6d9bd189e36732c4d483fd715eee4 Mon Sep 17 00:00:00 2001 From: Turtle Date: Mon, 27 Jul 2026 17:53:31 +0800 Subject: [PATCH] feat(install): consolidate checkouts under ~/.dsh/source and route PATH through a stable current symlink --- README.i18n.yaml | 6 +- README.md | 12 +- README.zh.md | 12 +- scripts/install.sh | 128 ++++++++++++++---- .../request-response.expected.json | 4 +- 5 files changed, 122 insertions(+), 40 deletions(-) diff --git a/README.i18n.yaml b/README.i18n.yaml index c6390e407a..7584d4f293 100644 --- a/README.i18n.yaml +++ b/README.i18n.yaml @@ -1,6 +1,6 @@ # Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: -# pnpm run verify-translation-pairing --write -README.md: 8b3a46081503ac9a29cc791cc302066e33e0f995 -README.zh.md: c73e2c70119d5b82d4629041a7a16848f7acbe92 +# pnpm run verify-translation-pairing --write README.md +README.md: f9f7294b42e29132d5cd46c0ab6a5f5265a1d8f3 +README.zh.md: 88cbf8522d8f1a183a48dc7e80858d1a0ced8f0f diff --git a/README.md b/README.md index 8b3a460815..f9f7294b42 100644 --- a/README.md +++ b/README.md @@ -16,16 +16,22 @@ curl -fsSL https://raw.githubusercontent.com/deepseek-harness/deepseek-harness/m The installer requires `git` and Node `^22.19 || >=24`, offers to install `pnpm` when it is missing, and prompts for a DeepSeek API key. -The installer clones DeepSeek Harness to `~/.dsh/source`, links `dsh` into `~/.local/bin`, and launches it. Re-running the command updates the checkout. See [`scripts/install.sh`](scripts/install.sh) for alternate install locations and other options. +The installer keeps every checkout under `~/.dsh/source`: the master clone at `~/.dsh/source/master` and each install's staging checkout as a git worktree `~/.dsh/source/staging-`. The stable symlink `~/.dsh/source/current` points at the active staging worktree, and `dsh` in `~/.local/bin` links to `current/bin/dsh`, so an upgrade repoints one symlink and the `dsh` on PATH never moves. Re-running the command adds a fresh staging worktree from an updated master and repoints `current` at it. See [`scripts/install.sh`](scripts/install.sh) for alternate install locations and other options. ## Use DeepSeek Harness ### Web UI -For the recommended local interface, build the frontend after installation and after each update, then start the Web UI: +For the recommended local interface, build the frontend after installation and after each update, then start the Web UI. Resolve the running checkout from the `dsh` launcher so the command holds regardless of which staging worktree is current (the launcher resolves through the stable `current` symlink): ```sh -pnpm --dir ~/.dsh/source run build && pnpm --dir ~/.dsh/source run build:web +dsh_bin=$(cd "$(dirname "$(command -v dsh)")" && pwd -P)/$(basename "$(command -v dsh)") +while [ -L "$dsh_bin" ]; do + link=$(readlink "$dsh_bin") + case $link in /*) dsh_bin=$link ;; *) dsh_bin=$(cd "$(dirname "$dsh_bin")" && cd "$(dirname "$link")" && pwd -P)/$(basename "$link") ;; esac +done +dsh_dir=$(cd "$(dirname "$dsh_bin")/.." && pwd -P) +pnpm --dir "$dsh_dir" run build && pnpm --dir "$dsh_dir" run build:web dsh web ``` diff --git a/README.zh.md b/README.zh.md index c73e2c7011..88cbf8522d 100644 --- a/README.zh.md +++ b/README.zh.md @@ -16,16 +16,22 @@ curl -fsSL https://raw.githubusercontent.com/deepseek-harness/deepseek-harness/m 安装器要求系统已安装 `git` 和 Node `^22.19 || >=24`,缺少 `pnpm` 时可代为安装,并会提示输入 DeepSeek API 密钥。 -安装器会将 DeepSeek Harness 克隆到 `~/.dsh/source`,把 `dsh` 链接到 `~/.local/bin`,然后启动它。再次运行该命令会更新源码目录。其他安装位置和选项见 [`scripts/install.sh`](scripts/install.sh)。 +安装器会把所有检出都放在 `~/.dsh/source` 下:master 克隆位于 `~/.dsh/source/master`,每次安装的 staging 检出是一个 git worktree `~/.dsh/source/staging-<时间戳>`。稳定符号链接 `~/.dsh/source/current` 指向当前生效的 staging worktree,`~/.local/bin` 中的 `dsh` 链接到 `current/bin/dsh`,因此升级只需重指一个符号链接,PATH 上的 `dsh` 从不移动。再次运行该命令会基于更新后的 master 新增一个 staging worktree,并把 `current` 重指到它。其他安装位置和选项见 [`scripts/install.sh`](scripts/install.sh)。 ## 使用 DeepSeek Harness ### Web UI -推荐在本地使用 Web UI。安装完成后以及每次更新后,请先构建前端,再启动 Web UI: +推荐在本地使用 Web UI。安装完成后以及每次更新后,请先构建前端,再启动 Web UI。通过 `dsh` 启动器解析当前运行的检出,这样无论当前是哪个 staging worktree,命令都成立(启动器会经由稳定的 `current` 符号链接解析): ```sh -pnpm --dir ~/.dsh/source run build && pnpm --dir ~/.dsh/source run build:web +dsh_bin=$(cd "$(dirname "$(command -v dsh)")" && pwd -P)/$(basename "$(command -v dsh)") +while [ -L "$dsh_bin" ]; do + link=$(readlink "$dsh_bin") + case $link in /*) dsh_bin=$link ;; *) dsh_bin=$(cd "$(dirname "$dsh_bin")" && cd "$(dirname "$link")" && pwd -P)/$(basename "$link") ;; esac +done +dsh_dir=$(cd "$(dirname "$dsh_bin")/.." && pwd -P) +pnpm --dir "$dsh_dir" run build && pnpm --dir "$dsh_dir" run build:web dsh web ``` diff --git a/scripts/install.sh b/scripts/install.sh index bc60dee983..41d5c749c1 100755 --- a/scripts/install.sh +++ b/scripts/install.sh @@ -3,16 +3,27 @@ # # curl -fsSL https://raw.githubusercontent.com/deepseek-harness/deepseek-harness/master/scripts/install.sh | sh # -# It clones the harness to ~/.dsh/source, checks host dependencies (git, Node, -# pnpm) and offers to install a missing pnpm, runs `pnpm install` (no build — -# the `bin/dsh` launcher runs the TypeScript source through the repo's own tsx), -# symlinks `dsh` onto PATH, records your API credentials in the Harness home -# (`~/.dsh`) dsh reads at boot, and drops you into `dsh`. +# It clones the harness under ~/.dsh/source (the master clone at +# ~/.dsh/source/master), adds a per-install staging worktree at +# ~/.dsh/source/staging- on branch dsh-staging/, checks +# host dependencies (git, Node, pnpm) and offers to install a missing pnpm, runs +# `pnpm install` (no build — the `bin/dsh` launcher runs the TypeScript source +# through the repo's own tsx), points the stable `~/.dsh/source/current` symlink +# at that staging worktree and symlinks `dsh` onto PATH at `current/bin/dsh`, +# records your API credentials in the Harness home (`~/.dsh`) dsh reads at boot, +# and drops you into `dsh`. Keeping every checkout under ~/.dsh/source keeps +# successive upgrades in one place instead of scattered sibling clones, and lets +# staging worktrees share the master clone's object store. The PATH symlink +# resolves through `current`, so an upgrade repoints one stable symlink instead +# of relinking PATH: the `dsh` on PATH never moves and can never dangle. # # When run from inside an existing checkout (e.g. `sh scripts/install.sh` rather -# than `curl ... | sh`) it reuses that checkout and skips the clone/update, leaving -# the working tree untouched; DSH_REF is ignored in that mode. Setting DSH_SOURCE -# to a different directory opts back into the normal clone/update path. +# than `curl ... | sh`) it reuses that checkout in place and skips the +# clone/worktree setup, leaving the working tree untouched and linking `dsh` +# straight at that checkout's `bin/dsh` (no `current` indirection — the checkout +# is not a managed staging worktree under the source container); DSH_REF is +# ignored in that mode. Setting DSH_SOURCE to a different directory opts back +# into the normal clone/worktree path. # # When run through `curl | sh` the script text arrives on stdin, so every # prompt and the final launch read the controlling terminal (/dev/tty) directly; @@ -21,7 +32,9 @@ # Overridable via environment: # DSH_REF branch or tag to clone/checkout (default: master) # DSH_REPO clone URL (default: the GitHub repo) -# DSH_SOURCE checkout location (default: ~/.dsh/source) +# DSH_SOURCE source container directory (default: ~/.dsh/source) +# DSH_MASTER master clone directory (default: $DSH_SOURCE/master) +# DSH_CURRENT stable symlink to the active worktree (default: $DSH_SOURCE/current) # DSH_BIN_DIR directory the `dsh` symlink lands in (default: ~/.local/bin) # DSH_HOME Harness home holding the personal config (default: ~/.dsh) # FIXME(install-ts): Move the post-checkout workflow into a tested TypeScript @@ -30,19 +43,31 @@ set -eu DSH_REF=${DSH_REF:-master} DSH_REPO=${DSH_REPO:-https://github.com/deepseek-harness/deepseek-harness.git} -# Remember whether the caller pinned a source location before defaulting it, so -# in-repo detection only repoints an unset DSH_SOURCE. +# DSH_SOURCE is the container directory that holds the master clone and every +# staging worktree; DSH_MASTER is the one real clone inside it. Remember whether +# the caller pinned the source container before defaulting it, so in-repo +# detection only repoints an unset DSH_SOURCE. if [ -n "${DSH_SOURCE:-}" ]; then DSH_SOURCE_EXPLICIT=1; else DSH_SOURCE_EXPLICIT=0; fi DSH_SOURCE=${DSH_SOURCE:-$HOME/.dsh/source} +DSH_MASTER=${DSH_MASTER:-$DSH_SOURCE/master} +# The stable symlink the PATH launcher resolves through: PATH -> current/bin/dsh +# -> /bin/dsh. Fresh installs and upgrades repoint this one symlink; the +# PATH launcher itself is written once and never moves. In-repo reuse ignores it. +DSH_CURRENT=${DSH_CURRENT:-$DSH_SOURCE/current} DSH_BIN_DIR=${DSH_BIN_DIR:-$HOME/.local/bin} +# One UTC basic timestamp names this install's staging branch and worktree. +DSH_STAMP=$(date -u +%Y%m%dT%H%M%SZ) +DSH_STAGING_BRANCH=dsh-staging/$DSH_STAMP +DSH_STAGING=$DSH_SOURCE/staging-$DSH_STAMP # --- in-repo detection --------------------------------------------------------- # Under `curl ... | sh` the script text arrives on stdin, so $0 is the shell # name and no file path resolves; running a checked-out copy (`sh # scripts/install.sh`) makes $0 the script file. When $0 is a readable file whose # parent is a scripts/ dir inside a real dsh checkout (bin/dsh launcher present), -# reuse that checkout and skip the clone. An explicit DSH_SOURCE pointing -# elsewhere opts back into the clone/update path. +# reuse that checkout in place — link `dsh` straight at it and skip the +# clone/worktree setup. An explicit DSH_SOURCE pointing elsewhere opts back into +# the clone/worktree path. IN_REPO=0 if [ -f "$0" ]; then _self_dir=$(CDPATH= cd -- "$(dirname -- "$0")" 2>/dev/null && pwd -P) || _self_dir='' @@ -52,7 +77,9 @@ if [ -f "$0" ]; then && [ -x "$_repo_root/bin/dsh" ] && [ -f "$_repo_root/scripts/install.sh" ]; then if [ "$DSH_SOURCE_EXPLICIT" = 0 ] || [ "$DSH_SOURCE" = "$_repo_root" ]; then IN_REPO=1 - DSH_SOURCE=$_repo_root + # In-repo reuse links `dsh` at this checkout as-is; the master/staging + # split applies only to fresh clone installs. + DSH_STAGING=$_repo_root fi fi fi @@ -120,7 +147,13 @@ confirm() { } printf '%s\n' "${B}DeepSeek Harness — dsh installer${RST}" -printf '%ssource %s @ %s%s\n' "$DIM" "$DSH_SOURCE" "$DSH_REF" "$RST" +if [ "$IN_REPO" = 1 ]; then + printf '%ssource %s (in-repo reuse) @ %s%s\n' "$DIM" "$DSH_STAGING" "$DSH_REF" "$RST" +else + printf '%smaster %s @ %s%s\n' "$DIM" "$DSH_MASTER" "$DSH_REF" "$RST" + printf '%sstaging %s%s\n' "$DIM" "$DSH_STAGING" "$RST" + printf '%scurrent %s%s\n' "$DIM" "$DSH_CURRENT" "$RST" +fi # --- 1. dependency check ------------------------------------------------------- step "Checking dependencies" @@ -170,36 +203,73 @@ else fi fi -# --- 2. clone (or update) the source ------------------------------------------ +# --- 2. clone the master and lay out the staging worktree --------------------- +# Fresh installs keep one real clone at $DSH_MASTER and check the running code +# out as a git worktree at $DSH_STAGING, so every checkout lives under +# $DSH_SOURCE and shares one object store. In-repo reuse links `dsh` at the +# existing checkout untouched. if [ "$IN_REPO" = 1 ]; then - step "Using existing checkout at $DSH_SOURCE" + step "Using existing checkout at $DSH_STAGING" info "running from inside the repo — skipping clone (DSH_REF ignored, working tree left untouched)" else -step "Fetching source into $DSH_SOURCE" -if [ -d "$DSH_SOURCE/.git" ]; then - info "existing checkout found — updating" - git -C "$DSH_SOURCE" fetch --depth 1 origin "$DSH_REF" - # Reset the checkout to the freshly fetched tip. FETCH_HEAD (not +step "Fetching source into $DSH_MASTER" +if [ -d "$DSH_MASTER/.git" ]; then + info "existing master clone found — updating" + git -C "$DSH_MASTER" fetch origin "$DSH_REF" + # Reset the master checkout to the freshly fetched tip. FETCH_HEAD (not # origin/) so this resolves for a tag as well as a branch, and -B makes # the re-run idempotent whether or not DSH_REF changed since the last install. - git -C "$DSH_SOURCE" checkout -q -B "$DSH_REF" FETCH_HEAD + git -C "$DSH_MASTER" checkout -q -B "$DSH_REF" FETCH_HEAD else - mkdir -p "$(dirname "$DSH_SOURCE")" - git clone --depth 1 --branch "$DSH_REF" "$DSH_REPO" "$DSH_SOURCE" + mkdir -p "$DSH_SOURCE" + git clone --branch "$DSH_REF" "$DSH_REPO" "$DSH_MASTER" fi + +step "Adding staging worktree at $DSH_STAGING" +[ -e "$DSH_STAGING" ] && die "staging path $DSH_STAGING already exists — remove it or set DSH_SOURCE elsewhere, then re-run." +# The staging worktree owns the branch dsh runs from; the master clone stays on +# $DSH_REF as the fetch/upgrade base. Exclude the per-worktree merge lock in the +# master clone's info/exclude, which every linked worktree inherits. +git -C "$DSH_MASTER" worktree add -b "$DSH_STAGING_BRANCH" "$DSH_STAGING" FETCH_HEAD 2>/dev/null \ + || git -C "$DSH_MASTER" worktree add -b "$DSH_STAGING_BRANCH" "$DSH_STAGING" HEAD +_exclude="$DSH_MASTER/.git/info/exclude" +if [ -f "$_exclude" ] && ! grep -qxF '.agents/merge.lock' "$_exclude" 2>/dev/null; then + printf '.agents/merge.lock\n' >>"$_exclude" +fi +mkdir -p "$DSH_STAGING/.agents" +: >"$DSH_STAGING/.agents/merge.lock" fi # --- 3. install dependencies (no build; the launcher runs from source) -------- step "Installing dependencies with pnpm (this can take a while)" -( cd "$DSH_SOURCE" && pnpm install ) +( cd "$DSH_STAGING" && pnpm install ) -[ -x "$DSH_SOURCE/bin/dsh" ] || die "launcher $DSH_SOURCE/bin/dsh missing after install — is DSH_REF a branch that ships apps/cli?" +[ -x "$DSH_STAGING/bin/dsh" ] || die "launcher $DSH_STAGING/bin/dsh missing after install — is DSH_REF a branch that ships apps/cli?" # --- 4. put `dsh` on PATH ------------------------------------------------------ +# Clone installs go through a stable `current` symlink so an upgrade repoints +# one symlink (current -> new worktree) and the PATH launcher never moves: +# PATH/dsh -> current/bin/dsh -> /bin/dsh. In-repo reuse links PATH +# straight at the checkout, since that checkout is not a managed worktree. step "Linking dsh into $DSH_BIN_DIR" mkdir -p "$DSH_BIN_DIR" -ln -sf "$DSH_SOURCE/bin/dsh" "$DSH_BIN_DIR/dsh" -info "linked $DSH_BIN_DIR/dsh -> $DSH_SOURCE/bin/dsh" +if [ "$IN_REPO" = 1 ]; then + DSH_LAUNCH_TARGET=$DSH_STAGING/bin/dsh + ln -sf "$DSH_LAUNCH_TARGET" "$DSH_BIN_DIR/dsh" + info "linked $DSH_BIN_DIR/dsh -> $DSH_LAUNCH_TARGET" +else + # Point `current` at this staging worktree with `ln -sfn`: -f replaces an + # existing `current` (re-run or upgrade) and -n stops `ln` from dereferencing + # an existing symlink-to-directory and dropping the new link *inside* the old + # worktree. `mv` is unusable here — BSD/macOS `mv` follows the existing dir + # symlink the same way. The swap is one unlink+symlink pair on a local fs; the + # installer holds no other process racing this path. + ln -sfn "$DSH_STAGING" "$DSH_CURRENT" + info "pointed $DSH_CURRENT -> $DSH_STAGING" + DSH_LAUNCH_TARGET=$DSH_CURRENT/bin/dsh + ln -sf "$DSH_LAUNCH_TARGET" "$DSH_BIN_DIR/dsh" + info "linked $DSH_BIN_DIR/dsh -> $DSH_LAUNCH_TARGET" +fi case ":$PATH:" in *":$DSH_BIN_DIR:"*) ON_PATH=1 ;; diff --git a/scripts/snapshots/translation-prompt-v4/request-response.expected.json b/scripts/snapshots/translation-prompt-v4/request-response.expected.json index cb25d0f061..e62327eb24 100644 --- a/scripts/snapshots/translation-prompt-v4/request-response.expected.json +++ b/scripts/snapshots/translation-prompt-v4/request-response.expected.json @@ -8,11 +8,11 @@ }, { "role": "user", - "content": "# DeepSeek Harness\n\nEnglish | [中文](README.zh.md)\n\nDeepSeek Harness (`dsh`) is an open-source coding agent built on the DeepSeek Harness SDK.\n\nIt uses an architecture where **everything is a plugin**.\n\n## Install\n\nInstall `dsh` with one command:\n\n```sh\ncurl -fsSL https://raw.githubusercontent.com/deepseek-harness/deepseek-harness/master/scripts/install.sh | sh\n```\n\nThe installer requires `git` and Node `^22.19 || >=24`, offers to install `pnpm` when it is missing, and prompts for a DeepSeek API key.\n\nThe installer clones DeepSeek Harness to `~/.dsh/source`, links `dsh` into `~/.local/bin`, and launches it. Re-running the command updates the checkout. See [`scripts/install.sh`](scripts/install.sh) for alternate install locations and other options.\n\n## Use DeepSeek Harness\n\n### Web UI\n\nFor the recommended local interface, build the frontend after installation and after each update, then start the Web UI:\n\n```sh\npnpm --dir ~/.dsh/source run build && pnpm --dir ~/.dsh/source run build:web\ndsh web\n```\n\nThe Web UI is served at `http://127.0.0.1:3080` by default.\n\n### TUI\n\nStart the full-screen terminal interface:\n\n```sh\ndsh\n```\n\n### Headless\n\nRun one task, print the final answer, and exit:\n\n```sh\ndsh -p \"summarize this workspace\"\n```\n\n## Why DeepSeek Harness\n\nBuilt-in capabilities cover file reading, editing, and search; shell execution; reusable skills; task tracking; subagents and workflows; persistent sessions; and context compaction. The TUI also includes Plan Mode.\n\n- **Everything is a plugin.** Models, tools, policies, storage, context management, and interfaces are composable [Cordis plugins](docs/user/develop/basic/index.md), so deployments can extend or replace behavior without forking the agent loop. See the [architecture](docs/architecture.md) for the underlying design.\n- **Code Mode (opt-in).** It exposes a `run_code` tool and a generated TypeScript SDK; only program output re-enters model context. See [Code Mode](packages/core/tools/README.md#code-mode).\n- **Self-referential Cordis tools are opt-in.** They let the agent inspect its live runtime and mount or unmount plugins while it runs. See the [Cordis tools](packages/cordis/tool-cordis/README.md).\n\n## Community\n\nFollow DeepSeek Harness on Twitter for project updates.\n\n## Development\n\n```sh\npnpm install\npnpm run test:coverage\n```\n\nStart with the [development guide](docs/development.md) and read the [architecture](docs/architecture.md) before changing packages.\n\nFor agents, follow [AGENTS.md](AGENTS.md).\n\nDeepSeek Harness is currently pre-release.\n\n## License\n\n[BSD 3-Clause](LICENSE)\n" + "content": "# DeepSeek Harness\n\nEnglish | [中文](README.zh.md)\n\nDeepSeek Harness (`dsh`) is an open-source coding agent built on the DeepSeek Harness SDK.\n\nIt uses an architecture where **everything is a plugin**.\n\n## Install\n\nInstall `dsh` with one command:\n\n```sh\ncurl -fsSL https://raw.githubusercontent.com/deepseek-harness/deepseek-harness/master/scripts/install.sh | sh\n```\n\nThe installer requires `git` and Node `^22.19 || >=24`, offers to install `pnpm` when it is missing, and prompts for a DeepSeek API key.\n\nThe installer keeps every checkout under `~/.dsh/source`: the master clone at `~/.dsh/source/master` and each install's staging checkout as a git worktree `~/.dsh/source/staging-`. The stable symlink `~/.dsh/source/current` points at the active staging worktree, and `dsh` in `~/.local/bin` links to `current/bin/dsh`, so an upgrade repoints one symlink and the `dsh` on PATH never moves. Re-running the command adds a fresh staging worktree from an updated master and repoints `current` at it. See [`scripts/install.sh`](scripts/install.sh) for alternate install locations and other options.\n\n## Use DeepSeek Harness\n\n### Web UI\n\nFor the recommended local interface, build the frontend after installation and after each update, then start the Web UI. Resolve the running checkout from the `dsh` launcher so the command holds regardless of which staging worktree is current (the launcher resolves through the stable `current` symlink):\n\n```sh\ndsh_bin=$(cd \"$(dirname \"$(command -v dsh)\")\" && pwd -P)/$(basename \"$(command -v dsh)\")\nwhile [ -L \"$dsh_bin\" ]; do\n link=$(readlink \"$dsh_bin\")\n case $link in /*) dsh_bin=$link ;; *) dsh_bin=$(cd \"$(dirname \"$dsh_bin\")\" && cd \"$(dirname \"$link\")\" && pwd -P)/$(basename \"$link\") ;; esac\ndone\ndsh_dir=$(cd \"$(dirname \"$dsh_bin\")/..\" && pwd -P)\npnpm --dir \"$dsh_dir\" run build && pnpm --dir \"$dsh_dir\" run build:web\ndsh web\n```\n\nThe Web UI is served at `http://127.0.0.1:3080` by default.\n\n### TUI\n\nStart the full-screen terminal interface:\n\n```sh\ndsh\n```\n\n### Headless\n\nRun one task, print the final answer, and exit:\n\n```sh\ndsh -p \"summarize this workspace\"\n```\n\n## Why DeepSeek Harness\n\nBuilt-in capabilities cover file reading, editing, and search; shell execution; reusable skills; task tracking; subagents and workflows; persistent sessions; and context compaction. The TUI also includes Plan Mode.\n\n- **Everything is a plugin.** Models, tools, policies, storage, context management, and interfaces are composable [Cordis plugins](docs/user/develop/basic/index.md), so deployments can extend or replace behavior without forking the agent loop. See the [architecture](docs/architecture.md) for the underlying design.\n- **Code Mode (opt-in).** It exposes a `run_code` tool and a generated TypeScript SDK; only program output re-enters model context. See [Code Mode](packages/core/tools/README.md#code-mode).\n- **Self-referential Cordis tools are opt-in.** They let the agent inspect its live runtime and mount or unmount plugins while it runs. See the [Cordis tools](packages/cordis/tool-cordis/README.md).\n\n## Community\n\nFollow DeepSeek Harness on Twitter for project updates.\n\n## Development\n\n```sh\npnpm install\npnpm run test:coverage\n```\n\nStart with the [development guide](docs/development.md) and read the [architecture](docs/architecture.md) before changing packages.\n\nFor agents, follow [AGENTS.md](AGENTS.md).\n\nDeepSeek Harness is currently pre-release.\n\n## License\n\n[BSD 3-Clause](LICENSE)\n" }, { "role": "assistant", - "content": "# DeepSeek Harness\n\n[English](README.md) | 中文\n\nDeepSeek Harness(`dsh`)是一款基于 DeepSeek Harness SDK 构建的开源 coding agent(编程智能体)。\n\n它采用了**一切皆插件**的架构。\n\n## 安装\n\n使用一条命令安装 `dsh`:\n\n```sh\ncurl -fsSL https://raw.githubusercontent.com/deepseek-harness/deepseek-harness/master/scripts/install.sh | sh\n```\n\n安装器要求系统已安装 `git` 和 Node `^22.19 || >=24`,缺少 `pnpm` 时可代为安装,并会提示输入 DeepSeek API 密钥。\n\n安装器会将 DeepSeek Harness 克隆到 `~/.dsh/source`,把 `dsh` 链接到 `~/.local/bin`,然后启动它。再次运行该命令会更新源码目录。其他安装位置和选项见 [`scripts/install.sh`](scripts/install.sh)。\n\n## 使用 DeepSeek Harness\n\n### Web UI\n\n推荐在本地使用 Web UI。安装完成后以及每次更新后,请先构建前端,再启动 Web UI:\n\n```sh\npnpm --dir ~/.dsh/source run build && pnpm --dir ~/.dsh/source run build:web\ndsh web\n```\n\nWeb UI 默认通过 `http://127.0.0.1:3080` 提供服务。\n\n### TUI\n\n启动全屏终端界面:\n\n```sh\ndsh\n```\n\n### Headless\n\n运行一项任务,打印最终答案后退出:\n\n```sh\ndsh -p \"summarize this workspace\"\n```\n\n## 为什么选择 DeepSeek Harness\n\n内置功能涵盖文件读取、编辑与搜索、shell 执行、可复用 skill(技能)、任务跟踪、subagent 与工作流、持久化会话,以及上下文压缩(context compaction)。TUI 还包含 Plan Mode。\n\n- **一切皆插件。** 模型、工具、策略、存储、上下文管理和界面均可组合为 [Cordis 插件](docs/user/develop/basic/index.md),部署方无需 fork agent loop(智能体循环)即可扩展或替换行为。底层设计见[架构文档](docs/architecture.md)。\n- **Code Mode(需显式启用)。** 它会提供 `run_code` 工具和生成的 TypeScript SDK,只有程序输出会重新进入模型上下文。参见 [Code Mode](packages/core/tools/README.md#code-mode)。\n- **自指 Cordis 工具需显式启用。** 这些工具可让 agent 检查自身的实时运行时,并在运行中挂载或卸载插件。参见 [Cordis 工具](packages/cordis/tool-cordis/README.md)。\n\n## 社区\n\n扫描二维码,或打开 DeepSeek Harness 微信社区申请页面 申请加入。\n\n

\n \"DeepSeek\n

\n\n## 开发\n\n```sh\npnpm install\npnpm run test:coverage\n```\n\n请先阅读[开发指南](docs/development.md);修改包之前,请阅读[架构文档](docs/architecture.md)。\n\n面向 agent:遵循 [AGENTS.md](AGENTS.md)。\n\nDeepSeek Harness 目前处于预发布阶段。\n\n## 许可证\n\n[BSD 3-Clause](LICENSE)\n" + "content": "# DeepSeek Harness\n\n[English](README.md) | 中文\n\nDeepSeek Harness(`dsh`)是一款基于 DeepSeek Harness SDK 构建的开源 coding agent(编程智能体)。\n\n它采用了**一切皆插件**的架构。\n\n## 安装\n\n使用一条命令安装 `dsh`:\n\n```sh\ncurl -fsSL https://raw.githubusercontent.com/deepseek-harness/deepseek-harness/master/scripts/install.sh | sh\n```\n\n安装器要求系统已安装 `git` 和 Node `^22.19 || >=24`,缺少 `pnpm` 时可代为安装,并会提示输入 DeepSeek API 密钥。\n\n安装器会把所有检出都放在 `~/.dsh/source` 下:master 克隆位于 `~/.dsh/source/master`,每次安装的 staging 检出是一个 git worktree `~/.dsh/source/staging-<时间戳>`。稳定符号链接 `~/.dsh/source/current` 指向当前生效的 staging worktree,`~/.local/bin` 中的 `dsh` 链接到 `current/bin/dsh`,因此升级只需重指一个符号链接,PATH 上的 `dsh` 从不移动。再次运行该命令会基于更新后的 master 新增一个 staging worktree,并把 `current` 重指到它。其他安装位置和选项见 [`scripts/install.sh`](scripts/install.sh)。\n\n## 使用 DeepSeek Harness\n\n### Web UI\n\n推荐在本地使用 Web UI。安装完成后以及每次更新后,请先构建前端,再启动 Web UI。通过 `dsh` 启动器解析当前运行的检出,这样无论当前是哪个 staging worktree,命令都成立(启动器会经由稳定的 `current` 符号链接解析):\n\n```sh\ndsh_bin=$(cd \"$(dirname \"$(command -v dsh)\")\" && pwd -P)/$(basename \"$(command -v dsh)\")\nwhile [ -L \"$dsh_bin\" ]; do\n link=$(readlink \"$dsh_bin\")\n case $link in /*) dsh_bin=$link ;; *) dsh_bin=$(cd \"$(dirname \"$dsh_bin\")\" && cd \"$(dirname \"$link\")\" && pwd -P)/$(basename \"$link\") ;; esac\ndone\ndsh_dir=$(cd \"$(dirname \"$dsh_bin\")/..\" && pwd -P)\npnpm --dir \"$dsh_dir\" run build && pnpm --dir \"$dsh_dir\" run build:web\ndsh web\n```\n\nWeb UI 默认通过 `http://127.0.0.1:3080` 提供服务。\n\n### TUI\n\n启动全屏终端界面:\n\n```sh\ndsh\n```\n\n### Headless\n\n运行一项任务,打印最终答案后退出:\n\n```sh\ndsh -p \"summarize this workspace\"\n```\n\n## 为什么选择 DeepSeek Harness\n\n内置功能涵盖文件读取、编辑与搜索、shell 执行、可复用 skill(技能)、任务跟踪、subagent 与工作流、持久化会话,以及上下文压缩(context compaction)。TUI 还包含 Plan Mode。\n\n- **一切皆插件。** 模型、工具、策略、存储、上下文管理和界面均可组合为 [Cordis 插件](docs/user/develop/basic/index.md),部署方无需 fork agent loop(智能体循环)即可扩展或替换行为。底层设计见[架构文档](docs/architecture.md)。\n- **Code Mode(需显式启用)。** 它会提供 `run_code` 工具和生成的 TypeScript SDK,只有程序输出会重新进入模型上下文。参见 [Code Mode](packages/core/tools/README.md#code-mode)。\n- **自指 Cordis 工具需显式启用。** 这些工具可让 agent 检查自身的实时运行时,并在运行中挂载或卸载插件。参见 [Cordis 工具](packages/cordis/tool-cordis/README.md)。\n\n## 社区\n\n扫描二维码,或打开 DeepSeek Harness 微信社区申请页面 申请加入。\n\n

\n \"DeepSeek\n

\n\n## 开发\n\n```sh\npnpm install\npnpm run test:coverage\n```\n\n请先阅读[开发指南](docs/development.md);修改包之前,请阅读[架构文档](docs/architecture.md)。\n\n面向 agent:遵循 [AGENTS.md](AGENTS.md)。\n\nDeepSeek Harness 目前处于预发布阶段。\n\n## 许可证\n\n[BSD 3-Clause](LICENSE)\n" }, { "role": "user",