DeepSeek Harness Plugin Hub

Publish and manage complete Harness Profiles. Discover Plugins for your next setup.

Explore

PluginsPresetsDocsNews

Community

Publish a pluginContactReport an issue

Resources

Plugin Hub on GitHubDeepSeek HarnessSystem statusPrivacy notice
© 2026 DeepSeek Harness Plugin HubPowered byPaxTech

Independent and unofficial. Not affiliated with, authorized by, or endorsed by DeepSeek.

DeepSeek Harness Plugin Hub
ProfilesPluginsCategoriesNewsDocsSign inManage Profiles
ProfilesPluginsCategoriesNewsDocsSign in
← Plugins

source-code-mgmt

Source Code Mgmt

DSH plugin: source code management. Registers「代码管理」as a card in DSH's OWN right sidebar (the official guide page, beside 工作区文件 / 新建终端 / 浏览器) and as a tab page in that column — there is no separate button. Older hosts degrade gracefully: dsh-better-sidebar (if installed) gets a Tab; a host with neith

The plugin will be installed here. Keep web if you are unsure.

npx -y @deepseek-ai/dsh plugin --profile web add source-code-mgmt@1.25.0
READMECompatibilityVersions

Description

DSH plugin: source code management. Registers「代码管理」as a card in DSH's OWN right sidebar (the official guide page, beside 工作区文件 / 新建终端 / 浏览器) and as a tab page in that column — there is no separate button. Older hosts degrade gracefully: dsh-better-sidebar (if installed) gets a Tab; a host with neither gets a header entry (beside "Session log", plus a same-height pinned one in the empty state). Nothing is ever placed in DSH's own left sidebar. The SSH section lets you choose port 443 (through firewalls that block 22) or the standard 22, and switching rewrites ~/.ssh/config in place. Repo status batches its git calls (one numstat for all changed files, one `remote -v` for name+URL), cutting a load on a busy repo from ~1.3s to ~0.06s. Opens a panel with env checks (git/gh), SSH ed25519 key setup, GitHub/Gitee dual-platform (SSH config + Gitee OpenAPI), connectivity test, push / new repo, auto-ignore files over 100MB with per-file skipped reasons, a clone section (remote repos from your account with local-exists detection, plus clone any repo by URL into any location), and a five-step npm publish wizard. Cross-platform (Windows / Linux / macOS).

Compatibility and provenance

Source Code Mgmt is published as source-code-mgmt and currently resolves to version 1.25.0. The Hub verifies its manifest and preserves the exact installation source for reproducible installs.

DSH compatibility
*
Runtime surfaces
web
Release source
npm
Registry updated
10/2/2026

Versions

1.25.0stable
10/2/2026
1.24.4stable
9/30/2026
1.24.2stable
9/30/2026
Show 10 more versionsCollapse versions
1.24.0stable
9/22/2026
1.21.0stable
9/7/2026
1.20.0stable
9/7/2026
1.18.0stable
9/6/2026
1.15.1stable
9/6/2026
1.14.0stable
9/3/2026
1.13.0stable
9/2/2026
1.11.0stable
8/31/2026
1.10.1stable
8/29/2026
1.10.0stable
8/24/2026

Related plugins

Loading related plugins…

Latest
1.25.0
DSH
*
HMR
Process restart
Tree shaking
Safe tree shaking not declared
Unpacked size
559.3 kB
Files
6
Surface
web
License
MIT
Source
npm
GitHub
★ 0
Weekly downloads
0
Last push
10/2/2026
View source ↗
README badge

Click the badge to copy Markdown for your README.

Do you maintain this Plugin?Claim benefit · Priority security scan

Verify the GitHub repository declared in package.json to manage this listing. After you claim it, Hub will prioritize a security scan of the current version and publish the result when it passes.

Claim this Plugin →
Report an issue

Related plugins

More verified plugins in developer-tools.

MarketdshmarketVisual plugin market inside DeepSeek Harness — browse, search, and one-click install community plugins. · DSH 可视化插件市场:逛一逛,点一下,装好。Better Sidebardsh-better-sidebarBetter Sidebar for DSH — file tree, editor, changes, tasks and side chat in one column, separate for every conversation. Also exposes a service other plugins use to register sidebar tabs and file viewers.Client Ui Git Graph@linxin666/dsh-client-ui-git-graphExternal dsh web GUI plugin: a blank-session git branch selector + Git graph, with real host-side git operations and guards, as a dsh profile bundleSsh@linxin666/dsh-sshRemote SSH operations for the dsh web GUI: host config store (~/.dsh/dsh-ssh.json, import from ~/.ssh/config), a persistent ssh2 connection pool with jump-host support, exec / PTY web terminal / SFTP transfer / local port-forward tunnels / cluster executi

README

source-code-mgmt — DSH 源代码管理插件

English | 中文

版本:v1.25.0 | 更新日志见文末「版本历史」

界面语言跟随 DSH 设置实时切换:面板与 host 端消息自动使用 DSH 的语言(设置 → 通用 → 语言),中文 ↔ 英文即时生效,无需重启。

DSH Web GUI 源代码管理插件:一个「代码管理」面板把 ① 环境检查 → ② SSH 密钥与连接 → ③ 代码管理 → ④ 克隆仓库 → ⑤ 发布 npm 包 全流程串起来,GitHub / Gitee 双平台通用。环境检查自动检测 Git / GitHub CLI / SSH,缺工具可一键安装(按平台自适应、尽量免 sudo;gh 支持镜像下载源加速,Windows 亦可用),并带「检查更新」跟进新版本。

入口默认注册进 DSH 自带的右侧栏(不再保留单独的按钮):在侧边栏「开始」页上,「代码管理」是与 工作区文件 / 新建终端 / 浏览器 / 壁纸引擎 并列的一张卡片,点开即在该栏里以页面形式打开面板。老宿主自动降级:装了 dsh-better-sidebar 时注册成它的侧边栏 Tab;两者都没有时(旧版宿主)才回退到右上角的 header 入口(有会话时放在「Session 日志」旁;无会话/新对话时钉在右上角,与有会话时逐像素同高)。不会占用左边栏。

功能

集成入口(按可用性自动选择,无需手动切换;任何时刻只有一个入口):

  • DSH 自带右侧栏(默认,harness ≥ 0.1.5):「代码管理」作为侧边栏「开始」页(guide)上的一张卡片 + 该栏里的一个页面。点卡片即在右侧栏打开面板(官方行为:openTab(kind, { replaceTab: true }),替换掉「开始」页本身,像浏览器起始页那样)。卡片顺序排在内置三张(工作区文件 10 / 新建终端 20 / 浏览器 30)与其它插件(壁纸引擎 100)之后 = 110。

    • 没有独立按钮:座位 sidebar.right.pane.tab 存在即代表宿主自带右侧栏,此时插件完全不注册 header 入口 —— 这正是「去掉单独按钮」的落点。
    • 卡片标题/说明是函数(title: () => t("代码管理")),由宿主每次渲染时求值 ⇒ 切语言无需重新注册。
    • 面板用 variant: "tab":填满该栏、不画自己的 ✕(关闭由宿主的 tab 栏负责)。
  • 已安装 dsh-better-sidebar(旧宿主降级):「代码管理」注册为它侧边栏的一个新 Tab 页面,点击侧边栏 Tab 直接打开面板;

  • 两者都没有(旧宿主降级):入口只注册在会话 header 里(两处严格互斥,任何时刻只有一个「代码管理」可见)——

    • ① 有活跃(非空白)会话时:经 conversation.session.header.utilities 槽位放在「Session 日志」旁(同款胶囊、间距 8px 一致);
    • ② 空白(新对话)/无会话空态时:经常驻的 shell.overlay 槽注册一个常驻按钮,钉在右上角、且与「有会话时」那枚按钮完全同高。
      • 纵向与有会话时对齐:有会话时按钮在 header 的 utilities 行里,该行几何为 .header 的 padding-top: 10px + .titleRow 的 min-height: 30px(居中)⇒ 标题行占 y=10..40、中心 25px;按钮高 32px ⇒ top = 10 + (30 - 32)/2 = 9px。空态按钮取同一个 9px,所以两种形态切换时按钮不跳位。桌面壳里再叠加 --dsh-windows-titlebar-height(普通浏览器为 0),写法是 calc(var(--dsh-windows-titlebar-height, 0px) + 9px)。
      • 横向内缩 right: 48px,让开空态 header 右上角已有的「右侧栏展开按钮」:空态下 .headerBlank .headerCorner 带 margin-left: auto,把 ui-sidebar-right 的 ExpandButton(28×28 圆钮)顶到最右,其盒子位于视口右侧 12px~40px(.header 的 padding-right: 28px 减去 .headerCorner 的 margin-right: -16px 得到右缘 12px)。48 = 40 + 8(8px 与 header 内其它控件的间距一致),整个按钮落在圆钮左侧、互不相交。

    降级入口点击都打开同一个 dsh-better-sidebar 外观的右侧集成面板(内容放同一面板),并把主内容区往左推挤。

    座位「晚到」有宽限窗口(600ms):sidebar.right.pane.tab 座位属于另一个包(ui-sidebar-right),加载器不保证顺序。若座位比本插件晚声明,这段时间里先不挂兜底按钮 —— 否则官方宿主上会先闪一个「代码管理」按钮、随后才被侧边栏卡片取代。窗口内座位出现 → 永远不挂;窗口耗尽仍没有 → 才按老宿主降级(老宿主本来就吃这条路径,晚 0.6s 出现按钮不可见)。全程零 I/O,只是一个计时器。

    关闭方式:右侧栏形态由宿主的 tab 栏负责(点 ✕ / 切页);降级面板有三种 —— 面板右上角 ✕(标题行固定不滚动,内容滚到哪都在视口内)、按 Esc、或再次点「代码管理」按钮。

    降级面板顶部会让开窗口控件那条带(--dsh-frame-chrome-top):桌面壳里 ─ □ ✕ 是 OS 绘制的覆盖层,任何贴到右上角的网页按钮都会被它压住、点不到(v1.24.1 修的正是这个「打开后关不掉」),所以面板整体从该带下方开始;原生全屏时该变量归零,面板仍用满整屏。

    刻意不注册进 DSH 自带左边栏(sidebar.footer.action)——那个入口会挤在左边栏「设置」上方,与侧栏自身的项混在一起。

    也刻意不去抢 DSH 自己的单占位槽:conversation.session.header.corner 已被自带右侧栏开关(ui-sidebar-right 的 ExpandButton)占用,conversation.session.header.leading 被自带侧栏开关(ui-sidebar 的 HeaderLeadingControls)占用;sidebar.panellist 的每一项 id 必须是真实主面板 id(会交给 layout.selectPanel),本插件没有主面板,注册进去只会得到点不动的死按钮。

检测只是激活时两次内存读取(座位声明 + ctx.get('sidebarRightTabs')),零 I/O、零网络,不影响 DSH 启动速度。

面板分五步(①②③ 为核心三步,④⑤ 为新扩展板块,默认折叠按需展开):

① 环境检查

  • 显示操作系统(美化名:Windows / macOS / Linux,对应底层 Node 平台标识 win32 / darwin / linux)
  • 自动检测 Git、GitHub CLI 是否安装及版本(如 git version 2.55.0、gh version 2.97.0)
  • 检测 SSH 客户端是否可用(解析到可用 ssh 即显示「已找到」)
  • 缺工具时给安装指引 + 一键安装(按平台自适应、尽量免 sudo):某工具未找到时,该行显示「❌ 未安装」+「复制安装命令」+「安装」按钮:
    • GitHub CLI(Linux / macOS):免 sudo 用户级安装——从官方 GitHub Release 下载对应平台/架构的二进制包,解压后装到 ~/.local/bin/gh(目录不存在会自动创建,全程不需要管理员权限);失败时自动回退系统包管理器。
    • Git(Linux):apt / dnf / pacman(自动带 sudo -n;先探测 sudo 是否免密,需要密码时不再盲目执行,而是给出「请在终端手动执行」的指引 + 完整命令)。Git(macOS):brew,无 brew 时走 Xcode 命令行工具(xcode-select --install,系统自带安装,含 git/ssh)。
    • SSH(Windows):内置可选功能(需管理员);SSH(Linux):apt / dnf / pacman 的 openssh-client(同上 sudo 探测);macOS 系统自带。
    • 复制安装命令与「安装」按钮走同一套平台自适应逻辑(不再固定显示 Windows 的 winget 命令)。
  • 安装过程实时进度弹窗:点「安装」后弹出进度窗口,逐步显示「查询最新版本 → 下载 → 解压 → 安装 → 清理」及实时输出;完成后显示成功/失败原因。
  • 需要重启时提示并一键重启:检测到安装后当前 host 进程无法立即识别(如 gh 装到了不在 PATH 的 ~/.local/bin)时,弹窗提示并显示「重启 DSH」按钮——点击后 host 通过 detached 辅助进程按原启动命令自动拉起新进程(机制同 dsh-update),页面短暂断开后刷新即可;若 host 已能识别(如设置了 DSH_SCM_GH 或 ~/.local/bin 在 PATH),则不打扰直接完成。
  • 安装完成后自动重新检测。

② SSH 密钥与连接

  • 平台选择:下拉选择代码托管平台 GitHub(默认) / Gitee,决定下面的 SSH 配置写入与连接测试目标
  • 自动探测 ed25519 密钥:扫描 ~/.ssh/*.pub 中已存在的 ed25519 公钥——优先用 id_ed25519;否则用找到的第一个(支持任意命名的密钥,如 github_ed25519);都没有则默认名 id_ed25519。状态行显示实际检测到的密钥文件名,SSH config 的 IdentityFile 也用它
  • 一键生成 ed25519 密钥(无密码;本地已有 ed25519 密钥时复用,不会重复生成)
  • 一键写入 SSH config,端口可选(见下):
    • 443(穿墙,默认):GitHub 写 github.com → ssh.github.com:443;Gitee 写 gitee.com:443。用于绕过封锁 22 端口的网络。
    • 22(标准):GitHub 写 github.com → github.com:22(标准端点)。家宽 / 办公室 / VPS 通常直连即可。
    • 切换端口后再次点「配置 SSH(config)」会就地改写 ~/.ssh/config 里对应的 Host 块(不会留下重复块,也不动其它 Host);状态行显示的是实际配置的端口(host 端解析 ~/.ssh/config 得出),不再是写死的 (443)。
    • 「测试连接」也按所选端口测试(ssh -T -p <port>),失败时提示「若是网络封锁 22 端口,请改用 443」。
  • 测试连接 ssh -T -p <port> git@<host>(GitHub 走 ssh.github.com / github.com,Gitee 走 gitee.com)
  • 显示公钥内容,方便复制上传到对应平台
  • 检测 gh 是否已登录及账号

443 与「是否有代理」无关(常见误解)。 普通 HTTP 代理只对 git-over-HTTPS / curl 生效;OpenSSH 完全无视 HTTP_PROXY / HTTPS_PROXY(要走代理得配 ProxyCommand)。所以「我开了代理」并不等于「22 端口通了」——该不该用 443 取决于你的网络是否封锁 22 端口,因此这里交给用户自己选,而不是由插件猜测。

海外/正常网络需要 443 吗?——不需要。 「写 SSH config」本身是可选的(只有你点按钮才写 ~/.ssh/config)。GitHub 标准端点就是 git@github.com 走 22 端口,正常网络开箱即用:生成密钥 → 把公钥贴到 GitHub → 测试连接 → 推送,全程 22 端口。443(Host github.com → HostName ssh.github.com, Port 443)是 GitHub 官方支持的兜底方案,典型场景是国内网络、部分公司/校园网;只有当 443 也被封锁时才会反而不通(极少数网络)。Gitee 是国内平台,海外用户基本只会用到 GitHub。

③ 代码管理

  • 跟随 ② 平台:本区所有「检测/新建/可见性」逻辑随 ② 的平台选择切换(GitHub 走 gh CLI,Gitee 走 Gitee OpenAPI)
  • Gitee 令牌(仅 Gitee 模式显示):输入 Gitee 私人访问令牌(需 projects 权限)→ 保存在本机 ~/.dsh/storages/source-code-mgmt-gitee.json(0600,不写入插件目录、不回传到浏览器/日志);可一键清除;令牌无效会自动清掉
  • 选择工作区:下拉选择 DSH 已登记的工作区文件夹,选中即加载
  • 选择目录 →:下拉右侧按钮,可手动输入/粘贴目录绝对路径或点击「浏览…」弹出原生文件夹选择器;确认后持久化加入自定义目录列表(插件独立存储于 ~/.dsh/storages/source-code-mgmt-dirs.json,不写入插件目录,开源不泄漏个人路径),下次打开无需重新选择;手动添加的目录会以自定义目录徽标显示,末端带 ✕ 可一键删除该下拉记录(只删记录,不删实际文件夹)
  • 显示仓库状态:平台来源、分支、远程地址、待提交改动数、领先/落后远程、>100MB 文件
  • 查看详情:有改动时「改动」行旁出现「查看」按钮 → 点击弹出窗口列出改动/新增/删除/重命名的文件或文件夹名称;所有可文本预览的文件(含新增/untracked 新文件)都可点击文件行展开查看内容——并排视图(左旧右新,删除行红底、新增行绿底),纯新增文件没有旧版本,只显示「新版本」一列;二进制文件不显示「查看」(内容无法按文本预览);本地与远程存在差异时「同步」行旁出现「查看」按钮 → 点击弹出窗口显示本地领先/落后的具体提交列表;无改动或已一致时不显示按钮
  • 本地 Git 工作流(不改动远程同步逻辑):
    • 「改动」弹窗里每个文件行有暂存 / 取消暂存按钮(按 git status 的 XY 状态区分 staged/unstaged),并显示「已暂存 / 未暂存」标记
    • ③ 面板仓库名上方有提交信息输入框 + 「提交」按钮(仅 git 仓库且有改动时显示)——可写自定义提交信息,不再用固定 message;留空则自动生成
    • 「分支」行旁有**「切换」**按钮 → 弹窗列出分支,点选即 checkout
    • 「分支」行旁有**「历史」按钮 → 弹窗列出提交(hash+subject+author+date),每条可查看**(并排 diff)、revert、cherry-pick(后两者带确认框,因为会改写历史)
  • 远程按平台 + 当前账号匹配:③ 的「远程 / 同步」只认「属于当前平台的远程」且 owner 等于当前登录账号(GitHub 平台 = gh 账号如 Zhucy123 名下,Gitee 平台 = Gitee 令牌账号如 Zhucy2100 名下)。这样:
    • 切到 Gitee 时不读 GitHub 的 origin,只读 gitee.com 的远程;
    • github.com 上属于别人/其他组织的仓库(如 deepseek-ai/deepseek-harness)不会被当作「用户自己的远程」显示,ahead/behind 也不对它计算;
    • 本地没有属于当前账号的远程时,「远程」显示「(无)」、不计算同步,只走「同名仓库检测 + 新建仓库并推送」形态。
  • 创建 Git:当所选目录不是 git 仓库但远程已存在同名仓库时显示该按钮,仅执行 git init(+设默认身份),不拉取不推送,由用户自行决定下一步是拉取还是推送
  • 新建仓库并推送:默认以文件夹名为仓库名(只读不可改),可选私有/公开;同名仓库已存在时按钮禁用并在下方提示「同名仓库已经创建」。若目录还是全新的(尚无任何提交),会先自动 + 生成一个初始提交再创建,避免推送时报 "no commits found"

④ 克隆仓库(Clone)

  • 跟随 ② 平台:把已登录账号(GitHub 走 gh,Gitee 走 OpenAPI)名下所有远程仓库列出来,并自动标记本地是否已有同名 git 仓库(默认按「默认工作区 + 已登记工作区 + 自定义目录」判定)
  • 克隆到目录:可选目标父目录(默认 = DSH 默认工作区),不选即克隆到默认位置;克隆走 SSH URL(git@github.com:… / git@gitee.com:…),成功后自动加入自定义目录列表,③ 里可直接选中
  • 列表里「本地已有」的仓库不可重复克隆,其余每个仓库一个「克隆」按钮

⑤ 发布 npm 包(npm publish)

  • 五步向导(在目标目录执行):① 确认源 npm config get registry → ② 查看认证配置 npm config list(自动脱敏 token/auth/password)→ ③ 验证身份 npm whoami → ④ 预览打包 npm pack --dry-run(先看会发布哪些文件,不真正打包发布)→ ⑤ 发布 npm publish
  • 登录与发布固定走官方源 https://registry.npmjs.org:即使你的全局 npm 配置是镜像源(如 registry.npmmirror.com——镜像只同步、不接受发布),whoami / npm login / npm publish 也统一带 --registry=https://registry.npmjs.org;状态区在检测到镜像配置时会黄色提示「发布将使用官方源」。
  • 「打开终端执行 npm login」安全弹终端:先探测系统真实存在的终端程序(konsole / gnome-terminal / xterm 系等),再 spawn(并挂 error 监听兜底)——修复了旧版 spawn 不存在二进制时异步 ENOENT 未捕获、直接把 dsh host 进程打崩的问题;KDE 下用 konsole --separate 开独立窗口,不与宿主所在终端实例纠缠。找不到终端时明确返回手动命令指引,不再假装成功。
  • 发布前必须勾选「我已核对以上内容,确认发布到 npm registry」才可点「发布」,防止误操作

数据加载时机(打开时联网、显示刷新中)

DSH 打开时不联网同步仓库,只预取静态的环境/SSH/工作区列表。打开插件、切换工作区、刷新状态、以及推送/拉取/暂存/提交等操作后,都会联网获取对应工作区的最新仓库状态,并显示「⟳ 刷新中…」提示——避免打开/重开/切换时显示可能过期的旧数据(如旧的「无改动」)。关闭面板再打开也会重新同步,不会停留在旧状态。

安装

本插件以 Profile Bundle 形态分发:package.json 声明了 dsh.bundle(携带 cordis.patch.yml 配置层),所以 dsh plugin --profile web add 一条命令装完即自动激活——无需手动编辑任何配置文件。

方式一:从 npm 官方包安装(推荐)

最省事的方式——只需一条命令,且不用先进 profile 目录,推荐给普通使用者。

在任意目录执行:

# 用 DSH 自带的插件命令(自动定位/初始化 web profile)
dsh plugin --profile web add source-code-mgmt

该命令在 web profile 目录里执行 pnpm add,成功后对账插件层:检测到本插件声明 dsh.bundle,会自动把它追加进 dsh.profile.bundles(见 ~/.dsh/profiles/web/package.json)并注册进 Cordis loader 树,一步装完即用。

装完完全重启 dsh web(不是刷新页面,而是要停掉旧进程后重新启动),然后浏览器 F5 刷新,「代码管理」入口即出现(已装 dsh-better-sidebar 时为侧边栏 Tab,未装时为右上角 Session log 旁的「代码管理」按钮 + 右侧集成面板)。

方式二:从本地目录安装(开发/测试)

Windows (PowerShell):

# 用 DSH 插件命令安装本地源码(link: 协议,符号链接,改源码即生效)
dsh plugin --profile web add link:C:/path/to/source-code-mgmt
dsh web

Linux / macOS:

dsh plugin --profile web add link:/home/yourname/path/to/source-code-mgmt
dsh web

方式三:从 GitHub 安装(分发场景)

Windows / Linux / macOS 通用:

dsh plugin --profile web add git+https://github.com/Zhucy123/source-code-mgmt.git
dsh web

git 安装会把源码实际拷贝到 node_modules,改动源码需重新 dsh plugin --profile web add ... 拉取(不像 link: 是符号链接、改源码即生效)。

验证安装是否成功

安装并重启后,可以核对以下几点:

  1. 依赖已写入:~/.dsh/profiles/web/package.json 的 dependencies 里应有 source-code-mgmt。
  2. 已加入配置层:~/.dsh/profiles/web/package.json 的 dsh.profile.bundles 列表里应有 source-code-mgmt(dsh plugin add 自动写入,无需手动编辑)。
  3. 符号链接已建立(link: 方式):~/.dsh/profiles/web/node_modules/source-code-mgmt 指向源码目录(Windows 显示为 Junction)。
  4. 重启后入口可见:已装 dsh-better-sidebar 时侧边栏出现「代码管理」Tab;未装时右上角 Session log 旁出现「代码管理」按钮,点击展开右侧集成面板。

常见排障

现象原因 / 处理
已 dsh plugin add 并重启,但按钮不出现最常见:装完没有完全重启(不是刷新)。停掉旧 dsh web 进程再启动(旧进程还占着 3080 端口时,新实例起不来)。
安装时提示「declares no dsh.bundle」装到的版本缺少 bundle 声明(旧版或打包遗漏 cordis.patch.yml)。确认版本 ≥ 1.9.0 后重新安装/更新。
出现「Failed to load plugins」插件 host 端 index.js 启动报错(多为依赖解析问题)。查看启动日志,确认 node_modules 依赖已装齐。

使用步骤

  1. 重启 dsh web 并刷新浏览器
  2. 点击「代码管理」入口(已装 dsh-better-sidebar 时点侧边栏 Tab,未装时点右上角 Session log 旁的「代码管理」按钮展开右侧集成面板)
  3. 面板打开(联网获取当前工作区最新状态,显示「⟳ 刷新中…」,拉取完成即显示内容)
  4. ①确认 Git / GitHub CLI 已安装 → ②生成密钥并测试连接 → ③选择工作区后推送或新建仓库

后端 API 路由

路由方法说明
/api/source-code-mgmt/envGET环境检查(git/gh 版本)
/api/source-code-mgmt/install-toolPOST一键安装缺失工具(body tool: git/gh/ssh;gh 可另传 source: official 或 gh-proxy.com/ghfast.top/ghproxy.net 等镜像 id,按平台自适应:gh 在 Linux/macOS 免 sudo 装到 ~/.local/bin;git/ssh 走系统包管理器 / brew / Xcode CLT / winget;响应为 NDJSON 事件流:step/out/result,供进度弹窗实时展示)
/api/source-code-mgmt/install-commandPOST按工具与下载源返回可复制的安装命令(body tool + 可选 source;与「安装」同源逻辑)
/api/source-code-mgmt/check-updatePOST检查 gh / git 是否有可用更新(body tool;返回 {current, latest, hasUpdate})
/api/source-code-mgmt/sshGETSSH 密钥 / config / gh 登录状态
/api/source-code-mgmt/gh/loginPOST打开终端运行 gh auth login(交互式登录需用户在弹出终端完成;找不到终端时返回手动命令指引)
/api/source-code-mgmt/gen-keyPOST生成 ed25519 密钥
/api/source-code-mgmt/write-configPOST写入 SSH config(body provider: github 默认 / gitee)
/api/source-code-mgmt/ssh-testPOST测试 SSH 连接(body provider: github 默认 / gitee)
/api/source-code-mgmt/default-dirGET当前工作区目录
/api/source-code-mgmt/workspacesGET列出所有工作区目录 + 自定义目录集合(DSH 工作区 + 插件自定义目录)
/api/source-code-mgmt/pick-dirPOST宿主端弹出原生文件夹选择对话框,返回选中的路径
/api/source-code-mgmt/add-workspacePOST校验目录存在并持久化加入插件自定义目录列表,返回合并后的工作区列表

安全

所有路由均为 loopback-only(sec-fetch-site + Origin 校验),仅本机浏览器可访问,LAN/手机来源一律 403——与控制面板同一策略。

跨平台

  • Windows / Linux / macOS 通用
  • 用 process.platform 检测平台
  • ~/.ssh 通过 homedir() 定位(Windows: C:\Users\用户名\ .ssh,Linux/macOS: /home/用户名/.ssh 或 /Users/用户名/.ssh)
  • Linux/macOS 下 SSH config 自动设 0600 权限
  • git / gh / ssh / ssh-keygen 二进制自动探测:启动时依次按 ①环境变量覆盖 → ②PATH 查找(Windows 加 .exe)→(仅 Windows)③Git 自带目录(usr\bin / bin)回退,最后兜底用裸命令名。因此只要装了 Git,即使 ssh 不在 PATH 里也能正常工作,换设备无需额外配置。
  • 如需手动指定二进制路径,可用环境变量覆盖:DSH_SCM_GIT / DSH_SCM_GH / DSH_SCM_SSH / DSH_SCM_SSH_KEYGEN
  • SSH 传输修复:Git for Windows 自带的 MSYS ssh.exe(usr\bin\ssh.exe)在被 detached/agent 进程调用时可能报 couldn't create signal pipe, Win32 error 5,导致 git push/git pull 失败。插件执行 git 远程命令时会自动注入 GIT_SSH 指向解析到的可用 ssh(通常为系统 OpenSSH C:\Windows\System32\OpenSSH\ssh.exe),避免该问题。
  • 中文文件名兼容:git 默认 core.quotepath 会把含非 ASCII 字节的路径输出成八进制转义的引号串(显示为乱码)。插件对所有 git 调用统一注入 -c core.quotepath=false(直接输出原始 UTF-8 路径),并对仍带引号转义的路径做 parseGitPath() 反转义兜底——改动列表 / diff 里的中文文件名显示正常
  • 缺工具一键安装跨平台(v1.15.0 起按平台自适应、尽量免 sudo):GitHub CLI 在 Linux/macOS 直接免 sudo 用户级安装(官方二进制 → ~/.local/bin/gh,失败回退系统包管理器);git/ssh 在 Linux 走 apt-get / dnf / pacman(先探测 sudo 免密,需要密码时给出手动命令指引而非静默失败),macOS 走 brew / Xcode 命令行工具,Windows 走 winget / 内置功能(回退 choco/scoop)。安装过程有实时进度弹窗,需要重启时提供一键重启 dsh。SSH 密钥探测、本地 Git 工作流、并排 diff 等在三个平台行为一致;「浏览目录」的原生选择器仅 Windows 可用,macOS/Linux 上请在输入框直接填路径(可手动输入粘贴)。

开发

git clone https://github.com/Zhucy123/source-code-mgmt.git
cd source-code-mgmt
# 在本地 DSH 测试(安装到 web profile,自动激活)
dsh plugin --profile web add link:$(pwd)
  • 改动 lib/client.js(浏览器端)→ 刷新页面即生效
  • 改动 lib/index.js(host/Node 端)→ 刷新页面即可让面板重新检测(toolInstalled 按当前 PATH / ~/.local/bin 实测);如 host 端 API 仍为旧版,重启 dsh web 进程一次

版本历史

v1.25.0(当前)

变更:入口默认注册进 DSH 自带的右侧栏,「代码管理」不再是一个单独的按钮:

  • 需求:把插件默认注册进 DSH 的侧边栏,而不是保留右上角那个独立按钮。
  • 背景(先说清「侧边栏」指哪个):截图里被圈掉的是右上角那枚「代码管理」按钮;红圈标出的位置是右侧栏「开始」页上 工作区文件 / 新建终端 / 浏览器 / 壁纸引擎 那张卡片列表下面的空位。那四张卡片来自 DSH 自带右侧栏(ui-sidebar-right)的 guide(sidebar.right.pane.tab 座位 + sidebarRightTabs.register({ guide })),并不是 sidebar.footer.action(左边栏底部)——左边栏早在 v1.1.2 起就明确不碰。
  • 实现:激活时 slots.inject('sidebar.right.pane.tab', …) —— 座位声明了就是这个宿主自带右侧栏,回调跑没跑即天然的能力门(不嗅探版本号,少一个漂移源)。回调里:
    • ctx.get('sidebarRightTabs').register({ id: 'source-code-mgmt/sidebar', kind: 'source-code-mgmt', title: () => t('代码管理'), guide: [{ id: 'panel', order: 110, title, description, icon: RepoIcon }] }) —— 这张 guide 条目就是「开始」页上的卡片;
    • slots.register({ name: 'sidebar.right.pane.tab', key: 'source-code-mgmt/sidebar' }, () => h(ScmPanel, { variant: 'tab' })) —— 卡片点开后在该栏里渲染的页面。key 必须等于 tab 类型的 id:宿主 TabSlot 用 definition.id 作 entryKey 派发 tab 本体,写错就会「卡片点开了但面板空白」。
    • 卡片顺序 order: 110:内置三张是 10 / 20 / 30,壁纸引擎(dsh-plugin-wallpaper-engine)是 100,所以本卡片排在其后、落在列表末尾。
    • title / description 是函数,宿主每次渲染都求值 ⇒ 切语言不需要重新注册(旧的 better-sidebar 路径因为收的是字符串,才需要 subscribe 后重注册)。
  • 「去掉单独按钮」的落点:座位存在时根本不注册 header 两处入口 —— 不是「注册了再隐藏」。分支顺序 ①官方右侧栏 → ②dsh-better-sidebar → ③header 兜底,先成立者独占入口。
  • 座位晚到的宽限窗口:sidebar.right.pane.tab 属于另一个包,可能比本插件晚声明。新增 SCM_FALLBACK_GRACE_MS = 600:这段窗口内先不挂兜底按钮(否则官方宿主上会先闪一个按钮再被卡片取代),窗口内座位出现 → 永远不挂,窗口耗尽才降级。全程零 I/O。
  • 服务晚到:座位声明了 ≠ sidebarRightTabs 服务已就绪,故 ctx.get 短轮询(250ms × 40 ≈ 10s)后才放弃并回落 ③;tab 本体注册包在 ctx.effect 里,让 DSH 在插件卸载时自动注销(避免 fiber 已 dispose 时抛 INACTIVE_EFFECT)。
  • 老宿主不受影响:装 dsh-better-sidebar 的走 ②(原逻辑不动);两者都没有的走 ③(header 两处入口的原几何、互斥判定全部保留)。
  • 测试:开发期用 4 个针对性回归文件覆盖本条改动 —— verify-entry-runtime.test.mjs(32 项,三段分支 + 卡片形状 + 图标尺寸 + 流体布局 + 卸载清理)、verify-sidebar-entry.test.mjs(16 项,「忠实宿主模型」:座位按宣告纪元重跑注入、keyed 座位拒绝重复 key、ctx.effect 按注入作用域回收、卸载按 LIFO / FIFO 两种顺序都验、注册抛异常路径,以及「兜底只能从路由里挂」的结构断言)、verify-sidebar-adversarial.test.mjs(16 项对抗式:能不能让官方宿主冒出独立按钮 / 能不能把用户搞成「零入口」)、verify-sidebar-postfix.test.mjs(3 项,独立复核者所写)。这些测试文件已在本版发布前删除(见下方「变更范围」),此处保留它们的结论作为改动依据的记录。
  • :由一位独立验证者对照真实 逐条核对宿主契约(座位名/guide 排序//图标 size 22|26//无 也能按 kind 打开/ 确实有语义),并,随后全部修掉:

v1.24.4(历史)

修复:面板打开后,窗口顶栏仍然有一条白缝(v1.24.3 的补带只补了面板那一段):

  • 现象:重启后白缝依旧 —— 顶栏在面板左缘仍是断开的。
  • 根因:v1.24.3 补的那条带宽度写的是 panelW(跟面板同宽),但白缝的位置并不都在面板宽度内 —— 面板宽度随拖拽变化,补带宽度跟着变就总会留下没补到的段。
  • 修复:补带改为 left: 0; right: 0 横贯整个窗口宽度。左半部分本来就是同色的原生顶栏,重复铺同色无副作用;右半部分把缺的那段补齐。不再依赖面板宽度 —— 拖拽改宽、开合面板都不会在顶栏留下任何缝。顺带删掉了为同步宽度而加的 chromeRef/getChromeEl(不再需要)。
  • 回归测试:断言补带必须 left: 0; right: 0 横贯整宽(变异测试验证过:改回 panelW 宽会失败)。
  • 变更范围:lib/client.js(桌面版无需操作,自动热重载)、tools/verify-close-button.test.mjs、README.md、README.en.md、package.json。版本号 1.24.3 → 1.24.4。

v1.24.3(历史)

v1.24.3(历史)

修复:面板打开后,窗口顶栏那一段出现明显色差(白条):

  • 现象:打开「代码管理」后,窗口最顶部那条原生标题栏在面板上方变成白色,左半(正常内容上方)仍是原来的浅灰 #f9fafb —— 中间在面板左缘形成一条硬缝。
  • 根因(推挤的连带副作用):桌面壳的 40px 顶栏是由 .BynINW_frame(及其 :before)用 --dsw-specific-sidebar-fill(= --dsw-static-neutral-bluish-50,#f9fafb)画出来的,而 frame 在 #root 里。本插件为了让位,用 .LAYOUT_CSS 把 #root 收窄了 panelW:#root { margin-right: var(--scm-push); width: calc(100% - var(--scm-push)) }。顶栏跟着 #root 一起被收窄,于是在面板左缘戛然而止;面板上方那一段没人画,露出页面底色(--dsw-alias-bg-layer-1 = #ffffff)。不是面板盖住了顶栏,而是顶栏被推挤截断了。
  • 修复:在面板 portal 里补一条与顶栏同高、同宽(面板宽)的色带,用同一个 token var(--dsw-specific-sidebar-fill, transparent) 填充,顶栏于是横贯整个窗口宽度、看不出接缝。
    • pointerEvents: "none" —— 这条带只负责上色,鼠标事件照旧穿透给底下的原生拖拽区域,不改变顶栏原有的拖动行为;
    • 高度用 SCM_CHROME_TOP(普通浏览器下为 0px → 高度 0、整块不可见,与改造前一致);原生全屏下同样归零,不会多出一条带;
    • 拖拽改宽时它必须跟面板同步改宽(否则顶栏会随拖拽裂开一条缝):makeResizeHandler 相应多接一个 getChromeEl,逐帧同时写面板与色带的 width。
  • 回归测试(tools/verify-close-button.test.mjs,9 项,本次新增 1 项):断言 portal 里必须存在这条带、用的是 --dsw-specific-sidebar-fill、高度等于 SCM_CHROME_TOP、pointerEvents: "none"、带 ref 且 makeResizeHandler 拿到了它(并用变异测试验证:去掉色带的背景后该断言确实失败)。
  • 变更范围:lib/client.js(刷新页面即生效)、tools/verify-close-button.test.mjs、README.md、README.en.md、package.json。版本号 1.24.2 → 1.24.3。

v1.24.2(历史)

修复:面板顶部那条灰带盖住了说明文字(v1.24.1 引入的回归):

  • 现象:打开「代码管理」后,标题「源代码管理」下方压着一条灰色横带,把「按顺序完成:①环境检查 → …」那行说明文字盖住/挤掉。
  • 根因:v1.24.1 为让 ✕ 常驻,把标题行做成了 position: sticky 并给了它一层底色(--dsw-alias-bg-layer-1)。sticky 行是浮在内容之上的——它不会把后续内容推开,只是悬停在那里,于是底色正好压住从下面滚上来的文字;同时 padding 又让标题行多占高度,把说明文字整体下推。用「浮层 + 底色」去解决「常驻可见」,方向本身就错了。
  • 修复(结构性分离,而不是再调颜色/层级):
    • drawer 下面板根节点改为 overflow: hidden(面板本身不滚动),标题行是普通静态行(flex: 0 0 auto,不带任何底色),只有它下面的 body 容器 overflow: auto 负责滚动。标题行在正常流里、不重叠内容,因此 ✕ 照样常驻可见,却不可能盖住任何文字——与原生侧边栏同一做法。
    • tab 变体保持整块滚动(滚动语义交给宿主 dsh-better-sidebar),不受影响。
  • 回归测试(tools/verify-close-button.test.mjs,8 项)相应重写:断言 ScmPanel 内不得再出现 position: "sticky"(断言范围限定在 ScmPanel——插件自己的 diff 查看器用 sticky 是正确的,不能误伤)、headStyle 不得带 background、drawer 根节点必须是 overflow: hidden、滚动 body 必须排在标题行之后、✕ 仍在且 tab 变体不渲染。
  • 变更范围:lib/client.js(刷新页面即生效)、tools/verify-close-button.test.mjs、README.md、README.en.md、package.json。版本号 1.24.1 → 1.24.2。

v1.24.1(历史)

修复:换成 DSH 官方桌面版后,右侧面板打开却关不掉:

  • 现象:桌面版里点「代码管理」能打开右侧面板,但右上角没有可点的关闭按钮,面板也退不出去。
  • 根因(不是按钮没渲染,是点不到):面板自带 ✕ 一直都在(variant === "drawer" 时渲染),但桌面壳会把 --dsh-windows-titlebar-height: 40px 写到 documentElement 上,而窗口控件(─ □ ✕)是 OS 绘制的覆盖层——它永远画在网页之上,z-index 加到多大都没用。面板原本是 position: fixed; inset: 0,它自己的 ✕ 正好落在 y≈20..42、x≈右边 20..50 那一带,被原生 ✕ 完整压住:按钮在 DOM 里、也响应 hover,就是收不到点击。网页版没有这条 OS 带,所以旧版一直没暴露。
  • 修复(四处,都写进回归测试):
    1. 面板整体下移:外层遮罩由 inset: 0 改为 top: var(--dsh-frame-chrome-top, var(--dsh-windows-titlebar-height, 0px)) + right/bottom/left: 0。优先用 DSH 自己发布的 --dsh-frame-chrome-top——它正是 DSH 用来「让模态遮罩不要画到 Windows 顶栏上」的量,原生全屏时自动归零,面板在全屏下仍能用满整屏;只发布旧变量的壳回退到 --dsh-windows-titlebar-height;普通浏览器两者皆无 → 0px,与改造前逐像素一致。
    2. ✕ 常驻可见:面板内容很长(①②③④⑤),普通滚动会把标题行连同 ✕ 一起带出视口,得一路滚回顶部才能关。注:本版最初用 position: sticky + 底色实现,方向是错的(sticky 是浮层,底色会盖住下面滚上来的文字)——已在 v1.24.2 改为「标题行固定 + 只有 body 滚动」的结构性分离。tab 变体保持原样——它由宿主 dsh-better-sidebar 负责关闭,本就不渲染 ✕。
    3. Esc 兜底:面板打开时挂 keydown 监听,按 Esc 关闭(关闭/卸载时摘除)。桌面壳里任何贴边的网页控件都可能被 OS 覆盖层压住,键盘是永远可用的退路。
    4. 入口按钮兼作开关:「代码管理」按钮由单向 setOpen(true) 改为 toggleOpen(再点一次收起,并加 aria-expanded)。它挂在 header 里、不在被窗口控件压住的那条带上,是最不容易失灵的关闭入口。
  • 新增回归测试 tools/verify-close-button.test.mjs(7 项,v1.24.2 重写为 8 项):断言 SCM_CHROME_TOP 的变量链与回退顺序、遮罩必须用 top: SCM_CHROME_TOP、drawer 里不得再出现 inset: 0(断言范围限定在 HeaderScmAction 内——插件自己的居中弹窗用整屏 mask 是正确的,不能误伤)、标题行 sticky 且 ✕ 在滚动容器内、tab 变体不渲染 ✕、拖拽条 zIndex 必须高于当时的 sticky 标题行(标题行吸顶后会盖住拖拽条内侧 4px,造成顶部一段拖不动的盲区;已把拖拽条从 zIndex: 1 抬到 3,并用变异测试验证过该断言会失败)、Esc 监听的挂载与清理、入口按钮必须是 toggle 而非单向 open。
  • 变更范围:lib/client.js(刷新页面即生效,无需重启)、tools/verify-close-button.test.mjs(新增)、README.md、README.en.md、package.json。版本号 1.24.0 → 1.24.1。

v1.24.0(历史)

③ 加载提速:把「每个文件一次 git」改成批量调用(实测每次加载省 ~1.2s):

  • 先回答「为什么开代理没变快」:push/pull/fetch 走的是 SSH(git@github.com:...),而 OpenSSH 不使用 HTTP_PROXY/HTTPS_PROXY/git http.proxy——已实测(ssh -G 在设了这些变量后不产生任何代理指令)。代理只对 git-over-HTTPS 生效。所以对 SSH 远端,开系统代理不会改变速度;这是协议决定的,不是配置问题。要让 SSH 走代理必须显式配 ProxyCommand(需要 ncat/connect 之类的辅助程序,本机没有)。
  • 实测瓶颈分布(本机真实仓库,单个工作区):
    阶段耗时
    本地 git 调用(branch/status/ls-files/remote/rev-list)~330 ms
    其中「每个改动文件一次 git diff --numstat」~1250 ms(30 个改动 → 30 次进程)
    git fetch(联网,仅 full 模式)~4000 ms
    SSH 握手本身~3200 ms
    根因:Windows 上每次 git 进程约 35ms,旧代码对「工作区副本不存在」的改动文件逐个跑 git diff --numstat -- <path>,是典型 N+1。
  • 修复:
    • 新增 viewableMap():二进制判定批量做——先直接读文件头 8KB 嗅探(零进程),只有确实读不到的文件才汇总成至多一次 git diff --numstat(按暂存/未暂存分两批)。
    • providerRemoteInfo():用一次 git remote -v 同时拿到远程名与 URL,替换原先「git remote + 每个远程一次 get-url」以及 repoStatus 里又一次 get-url。
    • 实测:1258 ms → 58 ms(30 个改动文件),加上远程查询共每次加载省约 1.24 s。
  • 语义保持一致(有测试兜底):新增 tools/verify-viewable-batch.test.mjs(9 项),在真实临时仓库里同时跑「旧实现」与「新实现」,断言两者对每个文件给出完全相同的结论。这个对照测试当场抓出一个我引入的回归:已删除文件上 git diff --numstat 不输出任何行,旧逻辑判为「不可预览」(正确——没有工作区副本可看),我的批量版一开始误判为「可预览」。已修回与旧实现一致。
  • 定位瓶颈与验证优化时用的是一次性计时脚本(对真实仓库逐项计时,上表数字即由此得出);脚本本身无断言、不参与回归,用完已删除,未随仓库保留。
  • 结论/建议:本地加载约 0.1s 量级、已经很快;fetch 那 4s 是网络与 SSH 握手成本,代理帮不上,只能靠「少 fetch」——插件已默认在快速模式不联网 fetch(用本地 tracking ref 算 ahead/behind),只有点「刷新状态」才联网。
  • 顺带清理:删掉 4 个无断言的纯诊断脚本(scan-keys / / 两个计时脚本)和 1 个在本机跑不起来的旧测试( 硬编码了 )。带断言的 与 i18n 工作流全部保留。

v1.23.0(历史)

SSH 端口改为用户可选(443 穿墙 / 22 标准):

  • 需求:用户提出「有代理的话是不是不用配 443,可以让用户自己选」。核实后按「做成可选」实现。
  • 先纠正一个前提(已实测):SSH 不使用 HTTP_PROXY / HTTPS_PROXY——用 ssh -G 验证过,设了这些环境变量后 OpenSSH 不会产生任何代理指令(要走代理必须配 ProxyCommand)。所以「有代理」≠「22 端口通」;443 的真正意义是绕过封锁 22 端口的网络。因此做成用户按网络情况自己选,而不是由插件按「有没有代理」去猜。
  • host 端(lib/index.js):
    • providerCfg(provider, port) 接受端口,并据此决定 Hostname——443 用 GitHub 的 ssh.github.com,22 用标准 github.com(Gitee 两种都用自身域名)。
    • 新增 normalizePort():只接受 22 / 443,其余一律回退 443(避免脏输入写坏配置)。
    • writeSshConfig(provider, port):端口变了会就地改写 ~/.ssh/config 里对应的 Host 块(用新增的 hostBlockSpan() 定位行区间),不产生重复块、不影响其它 Host;端口未变则仍是幂等 no-op。
    • sshTest(provider, port):显式带 -p <port>(443 时直接指向 git@ssh.github.com),不再依赖配置文件先写好。
    • 路由 /write-config、/ssh-test 透传端口;checkSsh() 新增 sshGitHubPort / sshGiteePort(从配置解析实际端口)。
  • client 端(lib/client.js):② 的「SSH 配置」行加端口下拉(443(穿墙) / 22(标准));状态文案显示实际配置的端口(不再写死 (443));首次读到已有配置时选择器跟随一次,之后以用户选择为准(不覆盖用户手动切换);「测试连接」按所选端口测,失败提示「若是网络封锁 22 端口,请改用 443」。
  • 测试:新增 tools/verify-ssh-port.test.mjs(16 项:端口归一化、Host 块解析/端口读取、相邻块不误吞、github.com.evil 之类相似域名不误匹配、路由透传);新增 tools/verify-ssh-port-e2e.test.mjs(7 项:在临时目录里真实跑「写 443 → 幂等 → 切 22 就地改写 → 切回 443 → 保留其它 Host → 追加 Gitee 不干扰」,不触碰真实 ~/.ssh/config);新增 tools/verify-ssh-crlf.test.mjs(4 项)与 tools/verify-ssh-crlf-write.test.mjs(5 项);新增 tools/verify-en-dict.mjs(校验新 UI 文案的英文翻译已进内联词典,280 → 287)。
  • 顺带修掉一个会被本机配置直接踩中的 bug(换行符):Windows 上 ~/.ssh/config 常见是 CRLF 换行。原先的「就地改写」把文件按 \n 切开再按 \n 拼回,会让被改写的那个 Host 块退化成 LF,整个文件变成 CRLF/LF 混合(在 dotfiles 仓库里表现为整段 diff)。现已改为按文件自身的换行符切分与拼接(),新写入的块、追加的块、以及拼接缝都保持原样。这组 CRLF 用例是拿本机真实配置(确认就是 CRLF)验证出来的。

v1.22.4(历史)

空态按钮下移到与「有会话时」完全相同的高度:

  • 症状:没有会话时,「代码管理」按钮比有会话时高了几个像素,切换会话时位置会跳。
  • 根因:上一版把空态按钮的 top 对齐到了侧栏开关那一行(calc((var(--dsh-windows-titlebar-height, 40px) - 28px) / 2) = 6px),但「有会话时」的按钮挂在 header 的 utilities 行里,其高度由另一套几何决定:.header 的 padding-top: 10px + .titleRow 的 min-height: 30px(居中)⇒ 标题行 y=10..40、中心 25px;按钮高 32px ⇒ top = 10 + (30 - 32)/2 = 9px。两者差 3px。
  • 修复:空态按钮改用 header 自身的几何表达 top = SCM_HEADER_PAD_TOP + (SCM_TITLE_ROW_H - SCM_BUTTON_H) / 2 = 9px(常量 10 / 30 / 32 分别对应 .header 的 padding-top、.titleRow 的 min-height、headerBtnStyle 的 height),桌面壳再叠加 --dsh-windows-titlebar-height(普通浏览器回退 0px,即精确 9px)。两种形态切换时按钮逐像素同位、不跳位。
  • 回归测试把「纵向」断言整组换掉:新增「空态与有会话时同高(由 header 度量推导出同一个 9px)」、「按钮高度必须等于 headerBtnStyle 的 32px」、「叠加标题栏偏移」三项,并断言旧的 6px 公式不得残留(共 13 项);用变异测试验证过改回 6px 会被抓出。另抽出 stripComments() 助手,让「禁止残留」类断言只匹配代码、不被解释性注释误伤。
  • 变更范围:lib/client.js(刷新页面即生效)、README.md、package.json(版本号)。版本号 1.22.3 → 1.22.4。

本轮(v1.22.0 → v1.22.4)新增的回归测试文件(随包发布,不在 files 白名单内,仅供本地校验):

  • tools/verify-entry-placement.test.mjs —— 入口几何位置与槽位选择回归(13 项)。
  • tools/verify-entry-runtime.test.mjs —— 在模拟 cordis 运行时里真实执行 apply(ctx)、并用模拟 session 快照真实渲染每个注册项,验证两个分支与入口互斥(13 项)。

运行:node tools/verify-entry-placement.test.mjs / node tools/verify-entry-runtime.test.mjs

⚠️ 以上测试文件已在 v1.25.0 删除(用户要求清理插件目录),下面各历史版本里提到的测试文件同样已不存在;当时的结论保留作改动依据。

v1.22.3(历史)

空态入口让开右上角的「右侧栏展开按钮」(修掉横向重叠):

  • 症状:上一版把空态常驻按钮设成 right: 12px 后,它与 DSH 空态 header 右上角已有的按钮叠在一起。
  • 根因:那个按钮不是左侧栏开关,而是 ui-sidebar-right 的 ExpandButton(右侧栏展开按钮)。空态下 .headerBlank .headerCorner 带 margin-left: auto,把它顶到最右;按 DSH 的 CSS 算它的盒子:.header 是 padding-right: 28px、.headerCorner 是 margin-right: -16px、圆钮宽 28px ⇒ 它占据视口右侧 12px~40px。right: 12 正好把「代码管理」的右缘压在同一条线上。
  • 修复:改为 right: 48px(常量 SCM_TOPRIGHT_OFFSET = 48)= 圆钮左缘 40px + 8px 间距(与 header 内其它控件的 8px 间距一致),整个按钮落在圆钮左侧、互不相交。纵向当时仍复用 DSH 自己的公式(该纵向写法已在 v1.22.4 被替换为「与有会话时同高」,见上)。
  • 回归测试新增 3 项几何断言:右缘必须 ≥ 展开按钮左缘(SCM_TOPRIGHT_OFFSET >= 40,并断言 right: 12 不得再出现)、偏移量恰为 40 + 8;并用变异测试验证过把偏移改回 12 会被抓出。(当时共 14 项;v1.22.4 重排纵向断言后为 13 项。)
  • 变更范围:lib/client.js(刷新页面即生效)、README.md。版本号 1.22.2 → 1.22.3。

v1.22.2(历史)

空态入口改回右上角、与 DSH 自带控件同一行:

  • 按用户反馈调整位置:空态(无会话 / 新对话)时常驻按钮此前被钉在会话 header 下方(top: 84px; right: 28px),用户要求改回右上角——即与 DSH 自带侧栏开关同一条水平线上。
  • 用 DSH 自己的公式对齐,而不是写死像素:DSH 在 Windows 桌面壳下把侧栏开关钉成 position: fixed; top: calc((var(--dsh-windows-titlebar-height) - 28px) / 2); left: 12px(ui-sidebar 的 SidebarRoot.module.css,「新会话」按钮同一条公式、left: 48px)。本插件复用同一条公式(带 40px 回退值,取自 apps/desktop 的 WINDOWS_TITLEBAR_HEIGHT),所以标题栏高度变化时按钮自动跟着走、不会错位;普通浏览器下变量缺失时退化为 6px,与桌面壳完全一致。
  • 横向不再遮挡:自带开关在左侧 left: 12px,插件按钮贴右侧 right: 12px,两者分居两端,不会叠在一起。
  • 回归测试 verify-entry-placement.test.mjs 同步更新:新增「复用 DSH 的纵向公式」「公式与 DSH 逐字一致(只差回退值)」「浏览器回退与桌面壳同为 6px」「不再钉在 header 下方」四项断言(共 12 项)。
  • 变更范围:lib/client.js(刷新页面即生效)、README.md。版本号 1.22.1 → 1.22.2。

v1.22.1(历史)

修复「同时出现两个『代码管理』入口」+ 移除左边栏入口:

  • 移除左边栏入口(按用户要求):不再注册进 DSH 自带侧边栏底部(sidebar.footer.action)——那个入口会挤在左边栏「设置」上方。同时删掉只为此存在的跨 React 根订阅桥(openScmPanel()/scmOpenListeners)和 SidebarFooterScmAction 组件。入口现在只保留会话 header 里的两处。
  • 修复「两个『代码管理』」根因:sessions.list.getSnapshot() 返回的是 SessionListState,形状为 { ids, byId, phase, subagentsByParent, jobsBySession }——没有 current 字段。而空态常驻入口的显隐判断读的是 snap.current,永远拿到 undefined,于是 hasActiveSession 恒为 false:即使主视图正展示一个活跃会话,常驻按钮也照样渲染,与 header utilities 里的按钮同时出现两个。
  • 修复方式:改用 DSH 自己的判定惯例 retainedBy.mainView > 0(ui-layout/DocumentTitle、ui-workspace/WorkspaceBrowser、ui-settings-general 等 5 处都这么写),新增 activeMainSession(snap) helper;并给「DSH 版本未提供 retainedBy」加了兜底(退回「非空白会话里 updatedAt 最新的一条」),拿不准时宁可认为有活跃会话,保证任何时候都不出现两个按钮。
  • 两个入口改为严格互斥:header utilities 那个不再直接注册裸按钮,而是注册自门控的 HeaderScmEntry——只有主视图展示「非空白」会话时才渲染;常驻入口只在否则情况下渲染。两者由同一个信号(activeMainSession)决定,不再依赖 DSH 的 hideChrome 时机,从构造上不可能同时出现。
  • 新增回归测试:verify-entry-runtime.test.mjs 会用模拟的 session 快照真实渲染每个注册项,断言「非空白会话 / 空白会话 / 无会话 / 非主视图空白会话 / 缺 retainedBy」五种状态都恰好只显示一个 header 入口、且没有任何入口渲染在 header 之外(共 13 项);并用变异测试验证过这些断言确实能抓出该 bug。verify-entry-placement.test.mjs 增至 12 项(含「不得注册进左边栏」「恰好两个入口」)。
  • 变更范围:lib/client.js(刷新页面即生效)、README.md。版本号 1.22.0 → 1.22.1。

v1.22.0(历史)

修复「未安装 dsh-better-sidebar 时,『代码管理』按钮压住 DSH 自带侧边栏按钮」:

  • 根因:空态常驻入口此前用 position: fixed; top: 8px; right: 12px(按钮高 32px → 纵向 y=8..40)。而 DSH 自带侧栏开关(ui-sidebar 的 HeaderLeadingControls,注册在 conversation.session.header.leading)位于会话 header 的 30px 标题行内——.header 是 padding: 10px 28px 0 20px,即自带按钮落在 y≈10..40;空态下 .headerCorner 还带 margin-left: auto,右侧同行同样被占。两条带直接相交,于是按钮叠在一起。
  • 修复:空态常驻按钮从那条带里挪出来——改钉在会话 header 之下(top: 84px; right: 28px,即 .header 的 76px 高度 + 8px、右缩进对齐 header 的 28px 内边距),与自带侧栏按钮在纵向和横向都不相交;有会话时的入口(Session 日志旁)本来就与自带按钮不在同一侧,保持不变。
  • 刻意避开的槽位(避免与 DSH 互相顶掉或产生死按钮):conversation.session.header.corner 是 single 槽且已被自带右侧栏开关(ui-sidebar-right 的 ExpandButton)占用;conversation.session.header.leading 是 single 槽、被自带侧栏开关占用;sidebar.panellist 的每一项 id 必须是真实主面板 id(会交给 layout.selectPanel),本插件没有主面板。
  • 新增 tools/verify-entry-placement.test.mjs(几何 + 注册位回归)与 tools/verify-entry-runtime.test.mjs(在模拟 cordis 运行时里真实跑 apply(ctx),断言两个分支的注册结果)。
  • 变更范围:lib/client.js(刷新页面即生效)、README.md。host 端 /api 路由未改动。版本号 1.21.0 → 1.22.0。
  • 注:本版曾同时新增「注册进 DSH 自带侧边栏底部」的入口,已在 v1.22.1 按用户要求移除。

v1.21.0(历史)

彻底移除 Gitee CLI + Gitee 私人令牌配置移到 ②:

  • 背景:Gitee CLI 不是刚需——插件核心的 Gitee 功能(建仓 / push / clone / ②③)靠私人令牌(OpenAPI) 与 SSH 公钥驱动,与 CLI 二进制无关;且 Windows 上安装经常失败(无 npm.exe 的 ENOENT / npm.cmd 的 EINVAL 坑)报「未找到可用的包管理器」误导。
  • host 端:删除 GITEE 常量、env.gitee 检测、installHints.gitee、checkSsh 的 giteeLoggedIn/giteeAccount、giteeUserLevelScript、checkGiteeUpdate、giteeLoginTerminal、/gitee/login 与 /gitee/import-token 路由、installCommandSystem/installCommand/installToolFlow/toolInstalled//check-update 里的 gitee 分支;installCommandSystem 对未知工具(含 gitee)直接返回 null,避免误落到 gh 的包名。
  • client 端:① 环境检查固定检测 gh(不再按平台切 gitee CLI 行);② Gitee 私人令牌改在 ② 直接输入/保存/清除(/gitee-token 存储,保存/清除后即时通知 ③④ 刷新);③ 只读显示令牌配置状态(未配置时提示去 ②),移除「从 Gitee CLI 导入」按钮。
  • 保留不变:Gitee 平台本身、私人令牌存储(~/.dsh/storages,0600)、建仓/push/clone、SSH 443 配置全部保留。
  • 变更范围:lib/index.js(重启 dsh web 生效)、lib/client.js(刷新即生效)。版本号 1.20.0 → 1.21.0。

v1.20.0(历史)

① 环境检查新增 Git 检查更新(仅 Windows)(复用 gh/gitee 的「检查更新」交互):

  • 已安装的 Git 行显示「检查更新」按钮(仅 Windows 显示):host 对比本地 git --version 与 Git 官方最新稳定版本,有更新则 window.confirm 确认后一键升级(进度弹窗实时展示)。
  • Git 官方版本来源不能用 GitHub Releases(git/git 没有 release,/releases/latest 是 404),改用其 tags API 并过滤预发布/RC(v2.56.0-rc0 之类会被剔除,避免诱导更新到 RC)。
  • 更新 = 系统包管理器「升级」命令:Windows winget upgrade Git.Git;需要 root 时沿用 canSudo() 探测(Windows 走 winget 自处理提权)。
  • Linux/macOS 不显示 Git 检查更新:这两个平台的 git 是发行版 / brew / Xcode CLT 管理的,版本与官方不同步(Linux 常停留在发行版固定版本),拿官方最新版对比会永远误报,且随系统更新走无需单独检查——host 端也加了防御,非 Windows 的 git 更新直接提示「Git 随系统更新」。
  • SSH 不加更新(系统组件、无独立可升级版本,且没必要)。
  • 新增 POST /check-update 的 tool=git 分支(返回 {ok, current, latest, hasUpdate})。
  • 变更范围:lib/index.js(刷新/重启一次生效)、lib/client.js(刷新即生效)。版本号 1.19.0 → 1.20.0。

v1.19.0(历史)

GitHub CLI 下载源选择扩展到 Windows(此前仅 Linux/macOS):

  • Windows 也支持镜像下载源下拉:之前下载源选择只在 Linux/macOS 显示,Windows 走 winget 直连 GitHub Releases 导致「下载很慢 / 不走进度」。现在 ① 环境检查在 Windows 的 gh 缺失时同样显示「下载源」下拉(默认第一个镜像 gh-proxy.com)。
  • Windows 镜像安装 = 下载官方 msi + msiexec 静默安装:选定镜像后,host 按「镜像前缀 + 官方 msi URL」下载 gh_<版本>_windows_<arch>.msi(实测约 5-6MB/s),再用 msiexec /qn 全静默安装——与 winget 效果一致(gh 注册进 Program Files 与 PATH),但避开了直连 GitHub 的慢速段。「一键安装」分步进度 /「复制安装命令」/「检查更新」均跟随所选镜像。msiexec 用 PowerShell -EncodedCommand(UTF-16LE base64)经 Start-Process -Verb RunAs -Wait 触发 UAC 提权安装(DSH 进程通常非提权)。
  • 修复 Windows 下载报 curl: (3) URL rejected: Port number...:最初用 cmd /c 拼字符串执行 curl,Node spawn 在 Windows 会重排引号,把镜像 URL 的第二个 https:// 误判成「host:端口」导致 curl 拒绝。已改为参数数组直接调用 curl.exe(runLive(bin, args)),逐个传值,彻底绕开 shell 引号重排。
  • 列举镜像:gh-proxy.com(快,推荐)→ ghfast.top / ghproxy.net(可用,较慢)→ 官网(慢);与 Linux/macOS 共用同一份 GH_MIRRORS,仅下载资产从 .tar.gz 换成 .msi。
  • 「下载源」下拉在 gh 已安装时也显示(三平台统一):此前只在 gh 缺失时渲染,导致已安装点「检查更新」时无法选镜像(更新走隐藏的 ghSource,若默认落官网则很慢)——Linux/macOS/Windows 都存在。现改为 gh 行常驻「下载源」下拉:未安装时影响一键安装,已安装时影响「检查更新」触发的覆盖安装;并修复默认镜像的竞态(改为独立 useEffect 监听 env,保证「已安装点更新」也不会竞态落到官网)。
  • 回退策略:选「官网(慢)」或镜像版本解析失败时,Windows 回落系统包管理器(winget / choco / scoop),原有行为不变。
  • 变更范围:lib/index.js(刷新/重启一次生效)、lib/client.js(刷新即生效)。版本号 1.18.0 → 1.19.0。

v1.18.0(历史)

① 环境检查新增 CLI 检查更新(GitHub CLI / Gitee CLI):

  • 已安装时显示「检查更新」按钮:host 对比本地版本与官方最新 release(gh 查 GitHub Releases API;gitee 查 Gitee OpenAPI 公开端点,无需令牌)。
  • 有更新 → 询问 → 按安装逻辑更新:window.confirm 展示「发现新版本:x → y,是否立即更新?」;确认后复用一键安装流程(/install-tool 带 force,跳过「已安装」短路)——gh 走用户级安装脚本 + 所选下载源(镜像),gitee 走官方安装脚本,进度弹窗实时展示,装完自动重新检测。
  • 无更新提示「已是最新版本(x)」;错误(读不到版本/连不上官方)有明确提示。
  • 点击「检查更新」后按钮变为「正在检查更新…」并禁用,检查完成自动恢复。
  • 修复更新不生效:/install-tool 路由最初未透传 force(更新确认后命中「已安装」短路,显示"已安装 执行成功"但版本不变),已修复——force 完整透传到安装流程,更新真正覆盖安装到最新版。
  • 新增 POST /check-update 路由(body tool: gh/gitee → {ok, current, latest, hasUpdate})。
  • 变更范围:lib/index.js、lib/client.js。版本号 1.17.0 → 1.18.0。

v1.17.0(历史)

Gitee 平台改用官方 Gitee CLI 检测与登录(对齐 GitHub 的 gh 体验):

  • ① 环境检查按平台自适应:切到 Gitee 后,工具行检测 Gitee CLI(gitee,官方 oschina/gitee-cli) 而不是 GitHub CLI(gh);切回 GitHub 恢复检测 gh。Gitee CLI 未安装时可一键安装(Linux/macOS 走官方安装脚本免 sudo 装到 ~/.local/bin/gitee,gitee.com 国内直连无需镜像;Windows 走 npm install -g @gitee/gitee-cli),安装后点「重新检查」即可。
  • ② Gitee 登录行用 CLI 状态:Gitee CLI 已装时显示 gitee auth status 结果——未登录提供「登录 Gitee」按钮(host 打开终端运行 gitee auth login,粘贴私人令牌即可)+「复制登录命令」+「重新检查」;CLI 未装时提示「请在 ① 环境检查安装 Gitee CLI」,并显示令牌兜底状态(Gitee API 仍需要私人令牌,可在 ③ 配置)。
  • ③「从 Gitee CLI 导入」令牌:Gitee CLI 已登录时,③ 令牌配置块出现「从 Gitee CLI 导入」按钮——把 CLI 保存的令牌(gitee auth token)一键导入插件存储(~/.dsh/storages),免去手动复制粘贴;「新建仓库并推送」等 OpenAPI 功能随即可用。
  • 私人令牌仍是 Gitee API 认证方式(创建仓库等必需);SSH 公钥只管 push/pull 已有仓库。
  • 变更范围:lib/index.js、lib/client.js。版本号 1.16.0 → 1.17.0。

v1.16.0(历史)

GitHub CLI 下载提速 + gh 登录按钮 + 移除一键重启:

  • GitHub CLI 下载源可选(官网 / 镜像):官网直连在国内网络经常「下载很慢 / 基本不走进度」。现 gh 未安装时,①环境检查会显示「下载源」下拉(默认第一个镜像):gh-proxy.com(实测约 2-3.5MB/s、稳定)→ ghfast.top / ghproxy.net(可用但偏慢)→ 官网(慢)。下载 URL 由 host 按所选源构造(镜像 = 前缀 + 官方 Releases URL),「一键安装」与「复制安装命令」都跟随所选下载源。镜像列表由 /env 的 ghMirrors 下发,后续增删镜像无需改浏览器端。
  • 安装完成提示改为「刷新网页即可生效」:删除「需要重启 DSH 生效」提示与「重启 DSH」按钮;成功提示统一为「安装完成…,刷新网页即可生效」。host 端删除整套一键重启机制(/restart 路由、restartLaunch / scheduleRestart / restartNeededFor 等)。
  • 缺少环境时也显示「重新检查」按钮:之前只有全部就绪才显示;现在有缺失时提示行旁同样提供「重新检查」,手动安装 / 刷新后无需重启即可重新检测。
  • gh 未登录时可一键登录:② SSH 密钥与连接的「GH 登录」行在未登录时显示「登录 GitHub」按钮(host 打开终端运行 gh auth login,KDE konsole 独立窗口、找不到终端给出手动命令指引)+「复制登录命令」+「重新检查」;登录完成后点「重新检查」即可看到账号。
  • 变更范围:lib/client.js(刷新即生效)、lib/index.js(刷新/重启一次生效)。版本号 1.15.1 → 1.16.0。

v1.15.1(历史)

⑤ 发布 npm 包功能修复(v1.15.0 的补丁):

  • 修复「打开终端执行 npm login」打崩 host:旧实现 spawn 不存在的终端二进制(如 SteamOS 上没有 x-terminal-emulator)时,异步 ENOENT 错误无监听器被抛成未捕获异常,直接把 dsh host 进程打崩(表现:运行 pnpm dsh web 的终端跟着结束)。现改为先 findTerminal() 探测 PATH 中真实存在的终端(konsole / gnome-terminal / xterm 系等),并对每个 child 挂 error 监听器兜底——绝不再崩宿主。
  • KDE konsole 用 --separate 开独立窗口:不与宿主所在的 konsole 实例纠缠;找不到任何终端时明确返回「请手动运行:<命令>」指引,不再假装打开成功。
  • npm 登录/发布固定走官方源:npm login / npm whoami / npm publish 统一带 --registry=https://registry.npmjs.org(镜像源只同步、不接受发布);npmStatus 新增 publishRegistry / mirrorConfigured,面板在检测到镜像配置(如 npmmirror)时黄色提示「发布将使用官方源」;「复制登录命令」也同步为官方源版本。
  • 变更范围:lib/index.js(需重启 dsh web)、lib/client.js(刷新即生效)。版本号 1.15.0 → 1.15.1。

v1.15.0(历史)

一键安装全面升级:平台自适应 + 尽量免 sudo + 实时进度 + 一键重启。

  • 修复「复制安装命令」在 Linux/macOS 上错误显示 winget:客户端写死的 Windows 提示改为取 host /env 返回的 installHints(与「安装」按钮同一套平台自适应逻辑);非 Windows 且无可用方式时留空,不再误导。
  • GitHub CLI(Linux/macOS)改免 sudo 用户级安装:installCommand() 对 gh 优先返回官方二进制下载脚本(查最新版本 → 按平台/架构下载 gh_<ver>_linux_amd64 / macOS_arm64 等 → 解压 → 装到 ~/.local/bin/gh),一键安装失败自动回退系统包管理器。resolveBin 新增 ~/.local/bin 探测——插件加载时即使 PATH 里没有该目录也能找到用户级 gh(不再依赖改 PATH / 设环境变量)。
  • sudo 免密探测(Linux):canSudo() 用 sudo -n true 探测;需要密码时不再盲目执行(-n 必然失败),返回「请在终端手动执行:<命令>」指引 + 原因,配合复制按钮即可完成。
  • macOS 兜底:git/ssh 无 brew 时走 Xcode 命令行工具(xcode-select --install,系统自带安装、含 git/ssh),弹窗以「⏳ 已触发,按系统提示完成后点重新检查」呈现。
  • 安装后实测复查:toolInstalled() 改为按当前 PATH/落点直接探测(git/ssh 走 --version、gh 走 ~/.local/bin 优先),不再依赖模块加载时缓存的历史解析结果——装完立即识别,修掉「装好了还报失败」的隐患。
  • 安装进度弹窗:/install-tool 改为 NDJSON 事件流(step/out/result,runLive() spawn 流式转发),浏览器端 jpostStream() 逐行消费;弹窗逐步显示「▶ 查询最新版本 → 下载 → 解压 → 安装 → 清理」与实时输出尾部。
  • 需要重启时提示 + 一键重启:restartNeededFor() 智能判定(DSH_SCM_GH 显式指定或 ~/.local/bin 在 PATH 时无需重启);需要时弹窗显示「重启 DSH」按钮——新增 POST /restart 路由(机制同 dsh-update:detached helper 等端口释放后按原启动命令拉起新进程,POSIX detached spawn / Windows PowerShell 隐藏窗口;严格 loopback + origin 同源校验;systemd supervisor 托管或 DSH_SCM_RESTART=0 时禁用),点击后页面短暂断开、新进程自动起来。
  • 变更范围:lib/index.js(需重启 dsh web)、lib/client.js(刷新即生效)。版本号 1.14.0 → 1.15.0。

v1.14.0(历史)

按你的要求移除了 ⑤「提交 PR」功能:

  • 前端:lib/client.js 删除整个 PrSection(含 ⑤ 区块的渲染与面板导语中的「⑤提交 PR」文案),一并清理了对应的 i18n 键(EN_DICT)。
  • host:lib/index.js 删除整套 PR 实现——/pr/targets、/pr/rule、/pr/analyze、/pr/generate、/pr/execute 五个路由,prAnalyzeFlow / prGenerateFlow / prExecuteFlow,PR 目标仓库本地存储(source-code-mgmt-pr-targets.json)、PR 规则缓存读写(rules/ 下的 readPrRule/writePrRule 等)、parseRepoUrl、fillPrTemplate、安全校验(safeEntryPath / safeRegenerateCommand)、PRESET_PR_RULES 与启动时的规则预置写入。
  • 保留 llmComplete() 这一通用 LLM 调用助手(与 PR 无关,属可复用能力,本轮暂未用到可直接忽略)。
  • 版本号 1.13.0 → 1.14.0;package.json 描述同步去掉「AI-assisted Pull Requests」。克隆板块的「按地址克隆」「本地已有判定」与 ⑥ npm(默认空目录)等仍保持 v1.13.0 行为不变。
  • 变更范围:lib/client.js(刷新即生效)、lib/index.js(需重启 dsh web)。

说明:rules/ 目录下旧预置文件 rules/awesome-dsh-plugin__awesome-dsh-plugin.json 已不再被任何代码引用,可自行删除或保留(不影响功能)。

v1.13.0(历史)

按第二轮反馈继续精化(④⑤⑥ 三处交互收敛):

  • ④「本地已有」判定扩大到已登记的目录:此前克隆列表只在「当前选中的克隆目录」下判定本地是否已有,用户若把克隆目录切到别处(如工作区 workspace),明明已存在的仓库仍标「可克隆」。现在新增 localRepoExistsAnywhere():除了当前选中目录,还会遍历 ③ 代码管理登记过的所有工作区 / 自定义目录(~/.dsh/storages/workspace.json + source-code-mgmt-dirs.json)逐一判定——任一被登记的位置已有同名仓库即标「本地已有」;克隆成功后目标目录本就会记入自定义目录,因此「某位置已有该仓库」的知识会自动沉淀下来,下次列表直接命中。
  • ④ 新增「按仓库地址克隆到任意位置」:克隆板块底部新增一行——粘贴任意 git 仓库地址(HTTPS 或 SSH,GitHub / Gitee / 自建均可)+ 选择目标目录,自动从 URL 末段推导仓库名(去掉 .git)并 POST /clone/run 克隆到所选位置;复用「选择目录…」按钮(可输入绝对路径或弹系统文件夹选择器,确认后记住到自定义目录)。目标目录默认沿用顶部下拉,未选择时给出提示。
  • ⑥ 发布 npm:目标目录默认留空:此前默认回填默认工作区,用户不选就可能发布到非预期目录。现在目标目录默认留空,界面提示「当前留空,不会自动使用默认目录」;未选目录时「重新检查 / 登录 / 执行」按钮全部禁用并拦截,用户必须手动选择(同 ③/④ 的「选择目录…」按钮)。
  • ⑤ 收敛为「提交 awesome-dsh-plugin 插件收录 PR」:按你要求先只保留「awesome-dsh-plugin 插件收录 PR」这最常用的一条路——隐藏「目标仓库地址」输入框、隐藏「记录到本地 / 已记录」行与标签、隐藏「分析 PR 规则 / 重新分析(忽略缓存)」按钮与手填规则面板、隐藏通用 PR 流程兜底框;目标仓库锁定为 https://github.com/awesome-dsh-plugin/awesome-dsh-plugin,挂载时只读加载一次其预置收录规则(不消耗 AI)并展示加载状态。保留下部:插件信息(名称/分类/描述 en/zh)→「生成 PR 内容(AI)」(命中预置模板可免 AI 填充、仍可编辑)→ 可编辑的标题/描述/条目文件内容 + 工作目录 →「执行 PR」(逐步日志 + PR 链接)。host 端 /pr/analyze、/pr/targets 等路由与预置规则文件保持不变,仅前端做了收敛。

v1.12.0(历史)

本次更新(新增④克隆 / ⑤提交 PR / ⑥发布 npm 三大板块):

  • ④ 克隆仓库:列出已登录账号(GitHub gh repo list / Gitee OpenAPI user/repos)名下所有远程仓库并标记本地是否已有;默认目标目录 = 用户主目录(home),不选即克隆到 home;每一行目录都有与 ③ 代码管理一致的「选择目录…」按钮(输入绝对路径或系统文件夹选择器,确认后记住到自定义目录);克隆走 SSH URL,成功后自动加入自定义目录列表。host 新增 GET /clone/default-dir、GET /clone/home、GET /clone/repos、POST /clone/run;本地已存在判定同时覆盖「目录名 == 仓库名且含 .git」的场景
  • ⑤ 提交 PR:目标仓库网址记录到 ~/.dsh/storages/source-code-mgmt-pr-targets.json(不写入插件目录);输入网址即自动读取该仓库的本地规则(GET /pr/rule,只读缓存、不消耗 AI):命中预置/缓存规则就显示对应专属 PR 功能,未命中则显示通用流程并提示「分析 PR 规则」;预置 rules/awesome-dsh-plugin__awesome-dsh-plugin.json——输入 https://github.com/awesome-dsh-plugin/awesome-dsh-plugin 直接给出它的收录规则(fork → clone → 在 data/plugins/<owner>__<repo>.yml 写条目 → npm ci && node scripts/generate-readme.mjs 重生成 README → commit → push → PR,规则写在插件目录);「分析 PR 规则」抓取目标仓库 README/CONTRIBUTING/PR 模板/package.json 交给 DSH 默认模型思考该仓库应如何 PR,输出结构化步骤规则并缓存到插件目录 rules/<owner>__<repo>.json(下次直接执行、不再消耗 AI;「重新分析(忽略缓存)」可强制重想);「生成 PR 内容」按规则模板产出标题/描述/条目文件内容(带预置模板时可免 AI 直接填充、仍可编辑),生成后可改、绝不直接提交;「执行 PR」按 fork→clone→加 upstream→fetch→建分支→写条目→重生成 README→commit→push→gh pr create(Gitee 走 OpenAPI) 逐步执行并显示日志与 PR 链接。host 新增 GET /pr/targets、POST /pr/targets、POST /pr/rule、POST /pr/analyze、POST /pr/generate、POST /pr/execute
  • ⑥ 发布 npm 包:五步向导(npm config get registry → npm config list(脱敏 token/auth/password)→ npm whoami → npm pack --dry-run → npm publish),发布前需勾选确认;可切换包目录(同样带「选择目录…」按钮);未登录/未配置时明确提示:显示当前 registry / whoami,未登录给出「打开终端执行 npm login」(host 弹系统终端、进入目标目录执行,POST /npm/login)与「复制登录命令」,登完回面板点「重新检查」;GET /npm/status 会报告目录是否存在、是否含 package.json 并给出对应警告。host 新增 GET /npm/status、POST /npm/step、POST /npm/login
  • host 端 AI 能力:新增 llmComplete()——通过 ctx.get('llm') 调 DSH 的 LLM 运行时,默认模型取 ctx.get('settings').get('agent-default-model')(兜底解析 ~/.dsh/settings.yaml),消息格式适配 GenerateOptions(text-delta 组装)
  • 维护性:新增 i18n 键通过 并入 / ,不改动原超长字典行;新增 共享输入框样式; 双端通过

v1.11.0(历史)

本次更新(改动文件查看体验 + 中文文件名兼容性):

  • 新增/untracked 文件现在可以查看内容:改动列表里「新增」文件行也显示「▸ 查看」,点击展开显示完整文件内容(按「全部新增」的 diff 渲染,超大文件只显示前 2000 行并附截断提示,上限 1MB 不会整读进内存);空的新文件显示「(空文件)」。
  • 新增文件不再显示「旧版本」列:并排 diff 在没有任何删除行(纯新增,如新增/untracked 文件)时只显示「新版本」一列,不再出现空白的「旧版本」表头与左列;有增有删的修改仍保留双列对比。
  • 二进制文件不显示「查看」:每个改动文件按内容嗅探(NUL 字节启发式,与 git 一致)判断是否可文本预览——二进制文件(图片、exe 等)不显示「查看」按钮(悬停提示「二进制文件,无法查看文本内容」);已删除/暂存后无副本的文件用 git diff --numstat(二进制条目 -\t-)判断。
  • 中文文件名兼容性修复:git 默认 core.quotepath 会把中文路径输出成八进制转义的引号串(如 "\346\270\270…md"),导致改动列表与 diff 头部的中文文件名显示成乱码。现在所有 git 调用统一注入 -c core.quotepath=false(直接输出 UTF-8 路径),并新增 parseGitPath() 反转义兜底——改动列表、ls-files -z、diff --name-only、git diff/git show 头部路径等解析点全覆盖。
  • 新增 tools/verify-cn-paths.test.mjs 回归测试(中文路径解析、文本/二进制可查看性、新增文件 diff 生成、已删除文件判断)。(该文件硬编码了 C:\Users\Administrator\workspace,在本机跑不起来,已于 v1.24.0 删除;相关覆盖由 tools/verify-viewable-batch.test.mjs 承担。)

v1.10.1(历史)

本次更新(修复 better-sidebar 集成与「未安装 better-sidebar」时入口的若干问题):

  • 修复:装了 dsh-better-sidebar 后,右上角「代码管理」按钮仍残留。根因:未装 better-sidebar 时的降级入口此前只给 ReactDOM 降级路径赋了 entryUnmount,slots 路径漏了——导致切到「侧边栏 Tab」形态时 header 入口没被拆掉。现已把 slots.inject 返回的 disposer 记进 entryUnmount,切到 Tab 时正确拆除(顺带修正同类清理逻辑)。
  • 修复:未安装 better-sidebar 时,入口只在对话内显示,新对话/无会话空态不显示。根因:旧入口挂 conversation.session.header.utilities(scope: 'session'),而空态时整个会话 header 被 hideChrome 隐藏,按钮出不来。现在改为右上角常驻——有活跃会话时放在「Session 日志」旁(会话 header 的右对齐列表),新对话/无会话空态时改为 shell.overlay 常驻槽里 position: fixed 钉在右上角的按钮(仅空白/无会话时显示,避免与 header 按钮重复)。
  • 修复:刷新瞬间「代码管理」按钮与「Session 日志」重叠。根因:captureSessions() 延迟 1.2s 才捕获到 session 列表,捕获前误判为「无会话」而提前点亮常驻按钮。现在未捕获到 session 列表前不渲染常驻按钮,杜绝刷新闪叠。
  • 判定信号修正:常驻按钮的显隐改用「当前会话是否空白」判断(sessions.list.getSnapshot().byId[current].blank)——活跃(非空白)会话才隐藏常驻按钮(交给 header 内的按钮),空白/无会话则显示,替代原来不准确的「current === undefined」判断。
  • 健壮性:better-sidebar 服务的探测由「一次性 1.5s 重试」改为有界多档重试(快节奏起步再放慢,覆盖约 44s,拿到即停、卸载即清),兜底 better-sidebar 客户端冷启动较慢导致漏切 Tab 的竞态。

以上均为客户端(lib/client.js)改动,刷新页面即生效;host 端 /api 路由与推送/忽略逻辑未改动。

v1.10.0(历史)

本次更新:

  • 界面中英文实时切换(跟随 DSH 语言设置):面板全部文案(①②③ 三步、按钮、弹窗、状态/结果消息、确认框、Tab 标题)与 host 端错误/结果消息改为双语词典驱动——语言 = DSH 设置 → 通用 → 语言,切换即时生效(面板经 ctx.locale 订阅实时重渲染,Tab 标题随动),无需刷新/重启;host 端按请求的 ?lang= 经 AsyncLocalStorage 按请求返回对应语言,并发不串扰。中文界面与 v1.9.0 完全一致,英文界面为完整翻译(含 >100MB 忽略原因、Gitee 令牌提示、git 命令失败回退等全部消息)。新增 tools/ 下的 i18n 提取/应用/测试脚本便于后续维护

v1.9.0(历史)

本次更新:

  • 改为 Profile Bundle 分发,安装即激活:package.json 的 dsh.bundle 从裸字符串 "./lib/index.js" 改为对象形态 { "patch": "./cordis.patch.yml" },并新增 cordis.patch.yml(insert source-code-mgmt 行)。现在 dsh plugin --profile web add source-code-mgmt 一步装完即被自动加入 dsh.profile.bundles 并注册进 Cordis loader 树,不再需要手动编辑 cordis.patch.yml 激活;README 中「安装 ≠ 激活」说明与 PowerShell 激活脚本已删除。功能行为零变化(仍是同一份 lib/index.js host 端 + lib/client.js 浏览器端)

v1.8.0(历史)

本次更新:

  • 新增「推送暂存」按钮(③面板「同步」行):当本地领先有提交或有已暂存的改动时,「同步」行「本地领先 N 提交」后面出现「推送暂存」按钮——点击后只把已暂存的内容用你填写的提交信息(没填则自动生成 chore: update <文件夹名>)提交,然后推送到远程;不会像「推送更改」那样自动暂存所有未暂存的改动。推送成功后自动刷新状态并显示「⟳ 刷新中…」,界面显示本次提交的信息与 hash。host 端新增 POST /push-staged 路由(pushStagedFlow,沿用本插件 run()/GIT_SSH)

v1.7.0(历史)

本次更新:

  • 数据加载时机重做:打开时联网、显示「刷新中」:此前打开 DSH 会预取所有工作区的仓库状态并缓存,导致打开/重开/切换面板时命中旧缓存,显示可能过期的数据(如旧的「无改动」)。现在改为——打开 DSH 时不联网同步仓库(只预取静态的 env/SSH/工作区列表);打开插件、切换工作区、自定义目录点击、刷新状态、以及推送/拉取/暂存/取消暂存/提交/对齐/初始化等操作后,一律联网获取对应工作区的最新状态,并显示「⟳ 刷新中…」提示(刷新期间按钮变「刷新中…」、状态区显示同步中)。关闭面板再打开也会重新同步,不再停留在旧状态。删除了预取所有工作区的 syncAllRepos 与「命中缓存秒显」分支

v1.6.0(历史)

本次更新:

  • 未安装 dsh-better-sidebar 时的入口改为「右上角 Session log 旁 + 右侧集成面板」:原「右上角浮动按钮 + 右侧抽屉」改为——「代码管理」按钮经 DSH 的 conversation.session.header.utilities 槽位注册,出现在右上角 Session log 旁边的右对齐列表里(与 Session log 同款胶囊样式、间距 8px 不挤在一起);点击后在右侧展开一个 dsh-better-sidebar 外观的集成面板(复用原话术:环境检查 / SSH / 代码管理三步),并把主内容区 #root 往左推挤(margin-right + width calc)。slots 服务不可用时降级为右上角浮动按钮 + 右侧面板。改动仅限 lib/client.js(浏览器端),刷新页面即生效,host 端 /api 路由未动
  • 已安装 dsh-better-sidebar 的形态不变:仍注册为它侧边栏的「代码管理」Tab;安装/未安装两形态依旧自动检测切换

v1.5.0(历史)

本次更新:

  • ① 环境检查缺工具一键安装:某个工具(git / gh / ssh)未检测到时,该行显示「❌ 未安装」+「复制安装命令」+「安装」按钮——「安装」由 host 自动选包管理器执行(Windows winget / 内置功能、macOS brew、Linux apt/dnf/pacman),安装后自动重新检测;host 新增 POST /install-tool 路由(best-effort,回传执行命令与输出)
  • 修复切换平台后 ③ 未刷新:仓库状态缓存按目录 + 平台区分,② 切换 GitHub / Gitee 后 ③ 会重新拉取对应平台的检测内容,不再残留上一个平台的「远程/同步/同名校验」结果;拉取期间显示「⟳ 切换平台,正在重新检测…」提示,避免旧内容停留几秒让用户误以为没变化
  • macOS / Linux 适配修复:Linux 缺工具一键安装自动带 sudo -n(已是 root 则省略,避免权限失败挂起);decodeSessionDir 按平台区分 Windows 盘符路径与 POSIX 绝对路径,macOS / Linux 下也能正确从 session 目录还原工作区

v1.4.0(历史)

本次更新:

  • SSH 密钥自动探测:扫描 ~/.ssh/*.pub 中的 ed25519 公钥——优先用 id_ed25519,否则用找到的第一个(支持任意命名密钥);都没有才用默认名 id_ed25519 新建。② 状态行显示实际检测到的密钥名,SSH config 的 IdentityFile 也用它,有自定义命名密钥也能正确识别、配置并推送

v1.3.0(历史)

本次更新(补齐本地 Git 工作流,未改动远程同步逻辑):

  • ② SSH 默认折叠 + 标题行切平台:② SSH 部分默认折叠,标题行内嵌「平台」下拉(折叠时也能切),切换后自动展开该部分以继续配置,③ 跟随平台

  • 选择性暂存 + 自定义提交信息:「改动」弹窗每个文件行新增暂存 / 取消暂存按钮(按 git status --porcelain 的 XY 状态区分 staged/unstaged),并显示「已暂存 / 未暂存」;③ 面板仓库名上方新增提交信息输入框 + 「提交」按钮,提交不再用固定 chore: update workspace via DSH...(留空则自动生成基于文件夹名的信息);沿用 pushFlow 的无身份兜底逻辑

  • 分支切换 + 提交历史 + revert / cherry-pick:「分支」行旁新增「切换」(弹窗列出分支,点选 checkout)与「历史」(弹窗列出提交,每条可「查看」「revert」「cherry-pick」——后两者带确认框)

  • 真·并排 diff:把 unified diff 解析成(旧行/新行)配对,左右两栏渲染(删除行红底、新增行绿底);改动文件展开与历史点某提交(/commit-diff)都用并排视图;保留纯文本 renderDiff 作降级(二进制/无法解析时)

  • 「查看改动」秒出:加载仓库状态时后台并发预取所有改动文件的 diff(并发≤3、纯本地 git 读取)并缓存,点开文件行立即显示,不再等「加载中…」

  • 提交历史右键菜单:「历史」弹窗每条提交可点**「⋯」或右键**打开菜单——查看提交差异、复制短哈希、复制完整哈希、复制提交信息、还原此提交(revert)、拾取此提交(cherry-pick,后两者带确认框)

  • 本地 Git 功能按 better-sidebar 自适应隐藏:已安装并激活 dsh-better-sidebar 时,③ 面板隐藏本插件自带的本地 Git 工作流(暂存/取消暂存、自定义提交、分支「切换」、提交「历史」+ revert/cherry-pick、并排 diff),因为这些能力 better-sidebar 的 Git 面板已覆盖,避免重复;此情况下「查看改动」弹窗只列改动文件,并提示具体改动内容请到 dsh-better-sidebar 的「源代码管理 / Git 面板」查看;未安装时则展示完整本地 Git 功能

  • host 端新增 /stage /unstage /commit /branches /checkout /log /revert /cherrypick /commit-diff 路由(全部走 isLoopbackRequest 校验 + run()/GIT_SSH)

v1.2.0(历史)

本次更新:

  • 入口自适应(不再占用左栏底部按钮):「代码管理」的入口改为自动检测——已安装 dsh-better-sidebar 时,通过其 ctx.betterSidebar.registerTab 把「代码管理」注册为该侧边栏的一个新 Tab 页面;未安装时,在 DSH 页面右上角显示一个浮动按钮,点击展开右侧栏抽屉(形态类似 dsh-better-sidebar 的右栏),内容为同一面板
  • 检测零开销:激活时仅一次内存读取(ctx.get('betterSidebar')),零 I/O、零网络,毫秒级,不影响 DSH 启动;并对激活顺序做了兜底延迟重试,保证最终落在正确的形态
  • 移除原左栏底部按钮:不再通过 sidebar.footer.action 插槽注册触发按钮;ScmPanel 支持 variant(tab / drawer)以分别适配侧边栏 Tab 与右侧抽屉布局,面板 UI 与 host 端 /api 路由完全复用、未改动
  • 可见性默认值更聪明:「私有 / 公开」下拉默认选当前仓库的实际可见性(已存在且可读时);当所选工作区没有远程(即将新建的仓库)时,默认选公开,而不是固定「私有」
  • 每个部分可折叠:①环境检查 / ②SSH / ③代码管理 标题行右上角各加一个折叠按钮,点击收起只显示标题、再点展开;①环境检查在所有工具都就绪(git/gh/ssh 均已安装)时默认折叠并显示「✅ 均存在」提示

v1.1.2(历史)

本次更新:

  • 改动详情可展开查看内容 diff:「改动」查看弹窗里,对已跟踪的改动(修改/删除/新增)点击文件行即可展开查看该文件的 diff——删除行红色带 -、新增行绿色带 +、上下文灰色;untracked 新文件无 diff 只列名称(后端对每个改动文件追加 git diff/git diff --cached 内容)

v1.1.1(历史)

本次更新:

  • 远程按平台切换:切到 Gitee 时「远程 / 同步」不再读取 GitHub 的 origin,而是只匹配 gitee.com 的远程(GitHub 平台仍读 github.com 远程)——解决「切到 Gitee 却仍显示 git@github.com:...」的问题;本地没有对应平台远程时显示「(无)」且不计算同步
  • 远程必须属于当前账号:此前只按 github.com/gitee.com 域名匹配远程,导致 deepseek-ai/deepseek-harness 这类别人的远程被当成用户自己的远程显示、还能点「推送更改」。现在校验远程 owner 必须等于当前登录账号(GitHub=gh 账号、Gitee=令牌账号),不属于自己的远程一律不算,界面回到「同名仓库检测 + 新建仓库」形态;同时无远程时「同步」行显示「(无)」,不再误显示「与远程一致」
  • 「拉取更新」改用强制对齐的实现并直接执行:「本地干净 + 远程有更新」状态的「拉取更新」按钮不再走 git pull --ff-only,而是复用「强制对齐」的 git fetch + git reset --hard origin/<branch> 逻辑——本地干净没有可丢失的改动,reset 等同快速前进到远程最新,且不弹确认框直接执行,结果更可靠(可绕过 rebase/merge 常见失败场景)
  • 操作按钮只保留当前状态对应的唯一动作:只有本地有改动(远程一致)→ 只显示「推送更改」;只有远程有更新(本地干净)→ 只显示「拉取更新」;「强制对齐」仅在「本地和远程都有更新」状态出现(该状态的唯一操作按钮),不再在推送/拉取/已同步状态里当作兜底显示
  • 「本地有改动 + 远程有更新」状态只保留「强制对齐」:此前该状态显示「拉取更新并推送更改 / 强制推送 / 强制拉取」三按钮,但这些操作容易因未提交改动、分支保护等失败且行为难预测(表现为点了没反应、不显示结果),现改为只显示提示 + 「强制对齐」按钮(git fetch + git reset --hard,本地完全重置为远程),与用户实际想要的「一键对齐」一致
  • 修复「点了按钮没有任何反馈」:操作回调先 setErr 再刷新仓库状态,随后 loadRepo 把错误提示无条件清空,导致失败时结果被立即抹掉、界面像什么都没发生;现在错误/结果会保留显示(切换目录 / 切换平台 / 手动刷新状态仍会清空旧提示),对齐失败等也能看到真实原因

v1.1.0(历史)

本次更新:

  • ② SSH 新增平台选择:在「② SSH 密钥与连接」顶部增加下拉选择代码托管平台 GitHub(默认) / Gitee
    • 「配置 SSH(config)」按所选平台写入相应 443 端口配置(GitHub:github.com → ssh.github.com:443;Gitee:gitee.com 443)
    • 「测试连接」按所选平台执行 ssh -T git@github.com / ssh -T git@gitee.com
    • 「SSH 配置」状态行按所选平台分别显示 github / gitee 的配置情况,公钥上传提示文案也随平台切换
  • ③ 代码管理完整适配 Gitee:③ 全部跟随 ② 的平台选择
    • Gitee 模式走 Gitee OpenAPI(REST API + 私人令牌,curl 调用),令牌在 ③ 输入并保存到本机 ~/.dsh/storages/source-code-mgmt-gitee.json(0600),可清除
    • 同名仓库检测:Gitee GET /repos/{owner}/{name};可见性读取/修改:Gitee GET/PATCH /repos/{owner}/{name}
    • 新建仓库并推送:Gitee POST /user/repos 建仓 + SSH 远程 git@gitee.com:<owner>/<name>.git 推送
    • 仓库状态行显示当前平台来源;推送/拉取/强制对齐等 git 操作平台无关照常可用

v1.0.1(历史)

本次更新:

  • 「改动」详情查看:「改动 N 个文件」旁新增「查看」按钮,点击弹出窗口列出改动的文件/新增的文件/文件夹名称(含状态标签:新增/修改/删除/重命名),只显示名称不显示具体内容;无改动时不显示该按钮
  • 「同步」详情查看:「同步」行在本地与远程存在差异时新增「查看」按钮,点击弹出窗口显示本地领先/落后的具体提交列表;与远程一致时无差异可看,不显示按钮
  • 修复:点「查看」不再导致插件崩溃(补全弹窗挂载容器)
  • 修复:推送/拉取/新建仓库等操作后,操作结果(如「已推送 已提交 …」)能正常显示并保留,不再被刷新逻辑立即清空
  • 修复:切换工作区 / 选择新目录 / 手动刷新状态时,会清空上一个工作区的操作结果,避免残留
  • 优化:操作后的自动刷新不再闪断信息栏(目录/分支/改动/同步等字段保持显示,仅后台更新数据)
  • 操作系统显示统一为美化名 Windows / macOS / Linux(对应底层 win32 / darwin / linux)

v1.0.0(历史)

首发版本,包含以下功能与修复:

  • 跨平台二进制自动探测:git / gh / ssh / ssh-keygen 按 ①环境变量覆盖 → ②PATH 查找 → ③(仅 Windows)Git 自带目录回退 自动定位,换设备无需额外配置,即使 ssh 不在 PATH 也能工作
  • 环境检查:显示操作系统、Git、GitHub CLI、SSH 是否可用
  • SSH:密钥生成 / 写入 github.com 443 配置 / 测试连接 / 公钥展示 / gh 登录检测
  • 代码管理:工作区选择、动态操作按钮(推送 / 拉取更新 / 合并推送 / 强制推送 / 强制拉取 / 新建仓库并推送)
  • 修改已有仓库可见性(私有 ↔ 公开)
  • 新建仓库并推送:对无提交的全新目录自动生成初始提交,避免 gh 报 "no commits found"
  • 100MB 文件自动忽略(整目录或单文件)并显示未上传原因

  • 所有 API 均为 loopback-only,仅本机浏览器可访问

License

MIT

git add
  • GitHub:gh repo create --private|--public --source=. --push
  • Gitee:用令牌调 Gitee OpenAPI POST /user/repos 建仓,再设置 SSH 远程 git@gitee.com:<owner>/<name>.git 并 git push(走 ② 已配的 SSH 密钥)
  • >100MB 文件处理:自动识别超过 100MB 单文件限制的文件——文件在一级子目录内则忽略整个一级目录(该文件夹为一整体),根目录独立文件则忽略单个文件;已存在于 .gitignore 的不重复添加,并显示「未上传原因」
  • /api/source-code-mgmt/remove-workspace
    POST
    仅删除自定义目录的下拉记录(不删实际文件夹),返回更新后的列表
    /api/source-code-mgmt/alignPOST强制对齐:git fetch + git reset --hard origin/<branch>,本地完全重置为远程状态
    /api/source-code-mgmt/init-gitPOST仅 git init + 设置默认身份,不拉取不推送(由用户决定下一步)
    /api/source-code-mgmt/repo-existsPOST检测同名仓库是否存在(body provider: github/gitee)
    /api/source-code-mgmt/repo?dir=GET获取仓库状态(query provider: github/gitee)
    /api/source-code-mgmt/repo-diff?dir=&path=GET按需返回单个改动文件的 unified diff 文本
    /api/source-code-mgmt/stagePOST暂存改动(body dir、path;path 空=全部)
    /api/source-code-mgmt/unstagePOST取消暂存(body dir、path;path 空=全部)
    /api/source-code-mgmt/commitPOST用自定义信息提交(body dir、message、paths?;沿用 pushFlow 的无身份兜底逻辑)
    /api/source-code-mgmt/branchesPOST列出分支(当前分支在前)
    /api/source-code-mgmt/checkoutPOST切换分支(body dir、branch)
    /api/source-code-mgmt/logPOST最近提交历史(body dir、count?,返回 hash/subject/author/date)
    /api/source-code-mgmt/revertPOSTrevert 某提交(body dir、hash)
    /api/source-code-mgmt/cherrypickPOSTcherry-pick 某提交(body dir、hash)
    /api/source-code-mgmt/commit-diffPOST返回某提交的完整 patch(body dir、hash)
    /api/source-code-mgmt/pushPOST提交并推送(git 操作,平台无关)
    /api/source-code-mgmt/pullPOST从远程拉取更新(git pull --ff-only,已最新/成功/冲突反馈)
    /api/source-code-mgmt/merge-pushPOST拉取并推送(git pull --rebase + git push,本地有更改且远程有更新时合并推送)
    /api/source-code-mgmt/force-pushPOST强制推送(git push --force,覆盖远程为本地状态)
    /api/source-code-mgmt/force-pullPOST强制拉取(git pull --force,拉入远程更新)
    /api/source-code-mgmt/createPOST新建仓库并推送(body provider;GitHub 走 gh repo create,Gitee 走 OpenAPI + SSH push)
    /api/source-code-mgmt/set-visibilityPOST修改仓库可见性(body provider;GitHub 走 gh repo edit,Gitee 走 PATCH /repos/{owner}/{repo})
    /api/source-code-mgmt/gitee-tokenGET/POSTGET:令牌是否已配置 + 账号;POST:保存({token})或清除({clear:true})Gitee 令牌
    独立复核(本轮最有价值的部分)
    app.asar
    entryKey = definition.id
    openTab(kind,{replaceTab:true})
    patterns
    keepMounted
    用对抗用例查出 4 个真实缺陷
    1. 两个入口并存:座位晚于本插件声明、且装了 dsh-better-sidebar 时,官方卡片会把 ② 的 Tab 也留着 —— 交接时只拆了 header 兜底,没拆 ②。修法:拆出 betterEntryActive(入口归属)与 hasBetterSidebar(是否安装,面板据此隐藏自带 Git 工作流)两个概念,官方成功路径同时 teardownBetterTab()。
    2. 插件彻底不可达:座位被宿主收回(自身重载)时,清理路径带着 stoppedByHost=true,恢复逻辑被跳过 ⇒ 一个入口都不剩。修法:座位回收不再设该标记,改为走统一的 routeToFallback() 恢复;stoppedByHost 只留给「插件卸载」与「② 已接管」。
    3. 重试阶梯其实只覆盖 5 秒:刻度被当成「距 apply 的绝对延时」而非「间隔」,[500,500,750,1000,1500,2000,3000,5000×7] 因此挤在头 5s 内烧完(注释却写「约 44s」)。后果:官方座位在、服务 10s 后才发布的宿主永远停在兜底按钮上。修法:改为累加调度(retryAt += interval,末级 ≈44s),并让重试在服务真正就绪时重新尝试官方注册。
    4. 服务超时后无法恢复:officialGaveUp 曾被当作永久放弃。修法:它只表示「这一轮 40×250ms 探测结束」,重试阶梯看到服务已就绪会把官方卡片换上、拆掉兜底。 另修一处口径错误:installHeaderFallback 原本用 hasBetterSidebar 当门,而该标记在官方入口赢下后仍为 true ⇒ 兜底永远挂不上(与缺陷 2 同类的不可达)。
  • 第二轮复核又抓出 2 个问题(都已修): 5. 注册抛异常时会多出一个入口:官方注册被拒(宿主 id 撞车时 tabs.register 会抛)的 catch 分支直接调 installHeaderFallback(),绕过了「② 已接管入口」的判断 —— 若此刻 ② 的 Tab 正挂着,屏幕上就是「Tab + header 按钮」两个入口。修法:该分支改走 routeToFallback(true)。(第一轮我曾以为 installHeaderFallback 里的 betterEntryActive 门是冗余防御,这个判断是错的:它在「抛异常」这条路径上是唯一的拦截点。) 6. 死状态:officialGaveUp 在改成「非永久」之后所有读取都被移除,只剩写入 ⇒ 是误导下一个读者的死变量(相关注释还在描述已不存在的门)。修法:整个删除,重入只由 officialCleanup/officialInstalled 两个条件表达。
  • 有界窗口的精确边界:独立复核实测,服务最晚约 54s 出现的宿主仍能升到官方卡片(50s 可升、54s 不可)—— 因为 44.25s 的最后一级会再起一轮 40×250ms 探测。这是刻意保留的有界窗口,不是缺陷。
  • 第二轮变异测试:独立复核跑了 10 个变异体,9 个被杀;唯一存活的正是上面的死状态(据此删除)。我这边补了「注册抛异常」路径的专项用例:单独改任一门都会被另一门掩盖,双变异(catch 直调兜底 + 拆掉 betterEntryActive 门)下用例确实失败 —— 说明该不变量被测到。
  • 第三轮复核:独立复核确认死状态已清除、注释无残留、行为不变(18/18 × 两种卸载顺序;1000 条随机事件序列下「同时存在的入口数」恒为 1、注册表 0 违规),并逐条攻击了抛异常路径(不会零入口 / 不会重复注册 / 不会半个注册 / 无泄漏定时器)。另把「服务最晚多久出现仍能升级」实测到 53s 可、54s 不可(与代码注释、README 一致)。它还诚实披露了自己两个测试夹 bug(ctx.effect 归属与 carriers() 短路),并复跑了受影响结论。
  • 补上最后一处覆盖缺口:前两轮里 installHeaderFallback 的 betterEntryActive 门与「catch 走路由」这两处,单独改任一处都无法从行为上观察到(路由先拦住)。除了保留双变异用例,另加一条结构断言:全文件里 installHeaderFallback( 只能有 1 处调用、且必须在 routeToFallback 内部 —— 这样将来有人在别处直调(正是第二轮那个重复入口 bug 的形态)会立刻失败。实测:单独把 catch 改回直调,这条断言即失败。
  • 变异测试:上述每条不变量都做了变异验证(去掉宽限窗口 / 卡片 order 提前 / body key 与 id 不一致 / better-sidebar 抢在官方前 / 去掉 ② 交接拆除 / 座位回收恢复被阻断 / 阶梯改回绝对延时 / 停止重试升级 / 注册抛异常路径绕过路由 → 对应用例均失败)。
  • 本轮之后清理:按用户要求,本插件的测试文件已全部删除(14 个 *.test.mjs + smoke-test.mjs + verify-en-dict.mjs)。保留 tools/ 下的 i18n 工具链(i18n-apply.mjs、i18n-extract.mjs、client-en.json、host-en.json)—— 它们是生成 lib/client.js 内联英文词典的脚本与数据源,删掉后词典就只能手改。上面的测试结论与变异测试结果作为本轮改动的历史依据保留。
  • 变更范围:lib/client.js(桌面版无需操作,自动热重载;host 端未改)、tools/client-en.json、README.md、README.en.md、package.json,以及删除 tools/ 下 15 个测试文件。版本号 1.24.4 → 1.25.0(新功能用次版本号)。
  • scan-host-keys
    verify-cn-paths.test.mjs
    C:\Users\Administrator\workspace
    verify-*.test.mjs
  • 变更范围:lib/index.js(需重启 dsh web 生效)、README.md、README.en.md、package.json。版本号 1.23.0 → 1.24.0。
  • const eol = current.includes('\r\n') ? '\r\n' : '\n'
  • 变更范围:lib/index.js(需重启 dsh web 生效)、lib/client.js(刷新页面生效)、README.md、package.json。版本号 1.22.4 → 1.23.0(新功能,次版本号 +1)。
  • Object.assign
    EN_DICT
    HOST_EN
    inputStyle()
    node --check
  • 对抗式审查修复(同版本内):
    • 修复 Windows npm 路径含空格导致 ⑥ 不可用:npm 解析路径(如 C:\Program Files\nodejs\npm.cmd)含空格时,shell 模式会把命令截断成 'C:\Program' 不是内部命令。run() 现对含空格的可执行文件路径加引号包裹(只包裹路径本身,参数仍按数组安全传递),npm registry / whoami / version 实测通过;
    • 修复「重新分析(忽略缓存)」失效:/pr/analyze 路由此前未把 force 传给分析流程,导致该按钮永远走缓存。现已透传 body.force;
    • 修复点号仓库名解析:parseRepoUrl 此前把 owner/my.repo 截断成 my(正则排除点号),现允许点号、仅剥离尾部 .git;
    • 安全加固 PR 规则执行:规则来自 AI/缓存、默认受信任,新增 safeEntryPath() 拦截条目文件路径穿越(.. / 绝对路径),safeRegenerateCommand() 只放行白名单构建命令并拒绝 shell 元字符——防止恶意仓库文档诱导 AI 产出危险规则后自动执行;
    • 修复「确保 fork」日志假成功:GitHub gh repo fork 失败时此前仍显示 ✅,现仅在真实成功或「already exists」时显示成功;
    • Gitee 仓库列表分页:user/repos 默认每页 20,此前只列前 20 个;现按每页 100 分页拉全(上限 10 页 ≈ 1000 个,与 GitHub 对齐);
    • 扩展 tools/smoke-test.mjs 至 31 项(覆盖点号仓库名、路径穿越、命令白名单等边界),全绿;node --check 双端通过
  • 按反馈完善(同版本内):
    • ④ 默认克隆目录改为用户主目录:此前默认是 DSH 工作区,且「工作区目录本身就是仓库」时被误标为可克隆;现默认改为 os.homedir()(Windows 为 C:\Users\<用户名>,Linux/macOS 为用户主目录),本地已存在判定补上「基目录名 == 仓库名 且含 .git」;
    • ④⑥ 目录选择按钮与 ③ 一致:克隆目标与 npm 包目录都提供「选择目录…」按钮,行为与 ③ 代码管理相同(可输入绝对路径或弹出系统文件夹选择器,确认后记住到自定义目录),不再局限于工作区内;
    • ⑤ 按仓库显示对应 PR 功能:输入目标仓库网址后自动 GET /pr/rule 读缓存规则(不消耗 AI)——有规则显示专属流程、无规则显示通用流程并提示分析;预置 awesome-dsh-plugin 收录规则(写入插件目录 rules/),输入官方仓库网址即命中;
    • ⑥ 未登录/未配置时引导登录:npm whoami 未登录时面板给出明确警告,可一键弹出系统终端执行 npm login(POST /npm/login,Windows 用 cmd /k、其他平台回退常见终端模拟器)或复制登录命令,完成后「重新检查」即可继续发布流程;
    • ⑤ 分析 PR 规则可手动输入规则来源:点击「分析 PR 规则」展开输入面板——可手动填写该仓库的 PR 规则文本,或提供规则文件(本地路径 / URL,如某仓库 README),留空则由 AI 自动抓取仓库文档(README/CONTRIBUTING/PR 模板)分析;提供内容时走精简提示词(只忠实整理用户输入、不抓文档、不编造,节省 token),结果同样结构化缓存到插件目录 rules/;仓库已有本地规则时点击「分析 PR 规则」会先询问「是否重新分析」(选否则不做任何事)。