把工作区复制成独立 Git 分支目录
29.9/100
综合分
0
GitHub 星标
—
npm 周下载
—/5
用户评分
数据截至 2026/10/15 · 周下载取自 npm 全网(含 CI / 镜像),非本站安装数;综合分由星标、活跃度与兼容性检查合成。
安装命令 · 需先装 dsh CLI 引擎
dsh plugin --profile web add Bainianzzz/dsh-fork-worktree该包不可直接 npm 安装,改从 GitHub 源安装(明细见下方安装检查)
状态速览
判定依据:实装验证 · 静态安装检查 · 自动扫描 · 仓库维护状态信任档位:待复验
是什么
dsh 原生插件 · dev
安全吗
本站尚未对该插件做风险分级
暂未覆盖,不等同于无风险
还在维护吗
活跃:最近一次提交在 17 天前
档位由哪些信号合成
本站实装验证(真实安装 L4 覆盖全站;L5 为运行时冒烟或实机截图,覆盖范围小于 L4) · 验证所用 dsh 版本 · 静态安装检查 · 风险分级 · 仓库维护状态。 下方各区块是它的证据明细。
社区实装还没有人打卡 · 装过的第一个就是你
安全扫描自动 · 静态分析源码v2.7.0🔵 扫描:含敏感能力尚无人工评估展开查看扫描说明与证据 ↓
安全扫描自动 · 静态分析源码
v2.7.0静态扫描发现插件具备触达本地系统的能力:执行外部命令、读写/删除本地文件、读取剪贴板或密钥类环境变量(至少其一)。这是能力清单,不是危险判定——具体能力项与证据见下方,安装前请自行判断是否需要。
可执行外部命令读写/删除本地文件
查看扫描证据(共 71 处,展示前 23 条)
- 读取本地文件impl.js:172
const info = lstatSync(path); - 读取本地文件impl.js:205
for (const entry of await readdir(root, { withFileTypes: true })) { - 读取本地文件impl.js:215
if (existsSync(to)) return true; - 读取本地文件impl.js:237
if (!existsSync(source)) return { kind: 'error', code: 'source-missing', text: `workspace directory does not exist: ${source}` }; - 读取本地文件impl.js:238
if (!statSync(source).isDirectory()) return { kind: 'error', code: 'source-not-directory', text: `workspace path is not a directory: ${source}` }; - 读取本地文件impl.js:251
if (existsSync(target)) return { kind: 'error', code: 'target-exists', text: `worktree target already exists: ${target}` }; - 删除本地文件递归impl.js:374
rmSync(source, { recursive: true, force: true }); - 写入本地文件verify-host.mjs:22
mkdirSync(root, { recursive: true }); - 执行外部命令固定命令verify-host.mjs:24
const git = (args, cwd) => execFileSync('git', args, { cwd, encoding: 'utf8' }).trim(); - 写入本地文件verify-host.mjs:57
mkdirSync(repo); - 写入本地文件verify-host.mjs:59
writeFileSync(join(repo, 'README.md'), '# scratch\n'); - 读取本地文件verify-host.mjs:66
check('fork path exists', first.kind === 'success' && existsSync(join(first.path, 'README.md'))); - 读取本地文件verify-host.mjs:67
check('fork is a linked worktree', first.kind === 'success' && readFileSync(join(first.path, '.git'), 'utf8').startsWith('gitdir:')); - 写入本地文件verify-host.mjs:75
mkdirSync(plain); - 写入本地文件verify-host.mjs:76
writeFileSync(join(plain, 'notes.txt'), 'x\n'); - 读取本地文件verify-host.mjs:84
check('source became a repository', existsSync(join(plain, '.git')) && git(['rev-parse', '--show-toplevel'], plain) === plain); - 读取本地文件verify-host.mjs:86
check('init fork carries the directory content', initialized.kind === 'success' && existsSync(join(initialized.path, 'notes.txt'))); - 写入本地文件verify-host.mjs:91
mkdirSync(empty); - 读取本地文件verify-host.mjs:182
check('copy mode keeps tracked files at HEAD', readFileSync(join(copyFork.path, 'src', 'tracked.ts'), 'utf8') === 'HEAD\n'); - 读取本地文件verify-host.mjs:183
check('copy mode brings untracked files', readFileSync(join(copyFork.path, 'src', 'untracked.ts'), 'utf8') === 'UNTRACKED\n'); - 删除本地文件递归verify-host.mjs:314
rmSync(gone, { recursive: true, force: true }); - 删除本地文件递归verify-host.mjs:319
rmSync(unrelated, { recursive: true, force: true }); - 删除本地文件递归verify-host.mjs:369
rmSync(root, { recursive: true, force: true });
扫描于 2026/10/31(1 天前) · 范围 入口闭包 + 发布文件清单 · 来源 GitHub 仓库
静态扫描只反映源码中出现的调用,不构成安全背书;人工评估与自动扫描相互独立、互不覆盖。
给这个插件打分还没有人投票↓
用户评分
还没有人投票,来当第一个
DeepSeek Harness 插件:把工作区行 fork 成 git worktree,或删除工作区文件夹
README
由 DeepSeek 最新模型翻译生成dsh-fork-worktree
给 dsh 侧边栏的工作区行加两个按钮(在 … 和 + 之间,fork 在左、垃圾桶在右):
- fork:确认后基于该工作区所在的 git 仓库建一个新 worktree,并把 worktree 目录注册成新的 dsh 工作区(并在新工作区里开会话)。
- 垃圾桶:确认后删掉该工作区的目录,并移除它的登记。
两个按钮都不会在点击时直接动手,都必须先在弹窗里确认。
位置与实现
- 两个按钮都只在行 hover 时显示(和 + 一样)。
- dsh 的工作区行没有 slot 扩展点(…、+ 都是 ui-workspace 写死的行内 UI),所以按钮是通过 shell.overlay 里挂一个 React 组件、再用 createPortal portal 进行动区的(见 client.js 的 WorkspaceRowActions)。用 data-row-key="workspace:<id>" 找行、data-slot="sidebar.workspaces" 作为只监听侧边栏的 MutationObserver 根(不监听整帧,流式输出不会触发重扫)。
展开完整文档(README 剩余 1 节)
dsh-fork-worktree
给 dsh 侧边栏的工作区行加两个按钮(在 … 和 + 之间,fork 在左、垃圾桶在右):
- fork:确认后基于该工作区所在的 git 仓库建一个新 worktree,并把 worktree 目录注册成新的 dsh 工作区(并在新工作区里开会话)。
- 垃圾桶:确认后删掉该工作区的目录,并移除它的登记。
两个按钮都不会在点击时直接动手,都必须先在弹窗里确认。
位置与实现
- 两个按钮都只在行 hover 时显示(和 + 一样)。
- dsh 的工作区行没有 slot 扩展点(…、+ 都是 ui-workspace 写死的行内 UI),所以按钮是通过 shell.overlay 里挂一个 React 组件、再用 createPortal portal 进行动区的(见 client.js 的 WorkspaceRowActions)。用 data-row-key="workspace:<id>" 找行、data-slot="sidebar.workspaces" 作为只监听侧边栏的 MutationObserver 根(不监听整帧,流式输出不会触发重扫)。
- 图标取自 Lucide(ISC):fork 用 git-fork(v1.57.0),垃圾桶用 trash 的无竖线画法(v0.400.0)、描边 1.8 —— v1.57.0 里 trash 与 trash-2 的 path 相同,那两道内线在 16px 下挤成一块、看着比 fork 粗,所以取无竖线版本并把描边调到 1.8,两个图标视觉粗细一致(约 1.2px)。都按原样的 24 单位 viewBox、currentColor、round caps/joins 内联在 client.js 里(本 bundle 不引入图标包),显示尺寸 16px。
- 风险:dsh 改版若改了这两个 DOM 钩子,按钮会消失(不会报错);那时需要跟着改选择器。
fork 流程
1. 点 fork:只弹确认框。
2. 确认 → POST dsh-fork-worktree/fork {source, init:false}(Host 的 forkRoute):
- 是 git 仓库 → git worktree add -b wt/ <仓库名>-wt- HEAD,注册工作区。
- 不是 git 仓库 → 返回 not-a-git-repo,同一个弹窗切成"初始化"模式。
3. 初始化模式确认 → init:true:git init + 把目录当前内容提交成第一个提交(空目录则空提交),再建 worktree。
4. 成功 → ctx.workspaces.create({path})(幂等,把新工作区并进 Client 模型)→ ctx.uiWorkspace.startSession(id) 在新工作区打开会话 → toast 显示路径。
/fork-worktree [name] [--init] 命令是同一套逻辑的第二个入口,可在该工作区的会话里手敲。
垃圾桶流程与删除语义
1. 点垃圾桶:只弹确认框,写明将删除的确切路径(还会写明会话记录保留)。
2. 确认 → POST dsh-fork-worktree/remove {source, force:false}(Host 的 removeRoute → removeWorkspaceFolder):
- linked worktree(.git 是文件)→ 走 git worktree remove,仓库的 worktrees/ 元数据跟着走(不留脏数据)。
- 有未提交修改或未跟踪文件 → 返回 worktree-dirty,弹窗切成"强制删除"模式,再确认才 --force。
- 其他目录 → 直接 rm -rf。
- 删完移除工作区登记;即使这一步失败,2 秒后的扫描也会把登记收掉。
3. 护栏:拒绝删除 $HOME 和文件系统根;目录不存在/不是目录都返回明确错误码。
4. 删除故意没有命令入口:命令会默认取当前会话的工作区,删掉自己正在用的目录不该只是一条命令的距离。
worktree 命名
- 目录:与仓库根同级,<仓库名>-wt-;label 是 <name>(若给)或 6 位随机 hex。
- 分支:wt/,基于当前 HEAD。
工作区自动消失
Host 每 2 秒扫一次注册表(watchRemovedWorktrees):
- 目录存在且 .git 是文件(linked worktree)→ 记为跟踪对象。
- 被跟踪对象连续两次扫描都目录缺失 → 静默删除该工作区注册(不动文件和会话,不报错)。
- 进程重启后不丢:目录已消失、但名字是 -wt-<6位hex>,或同级还有同名仓库目录时,同样会被清掉。
- 不动普通目录、不动 bg-switcher--worktrees/xxx--worktree 这类别的命名。
开发迭代
改 Host 侧(impl.js)后,让这一行重新加载即可,不用重启 dsh:插件页把 fork-worktree 关掉再打开,或调 plugin_manager 的 set_plugin(target: include:fork-worktree,先 enabled:false 再 enabled:true)。
原理:ES 模块按解析后的 URL 缓存,所以 Loader 那一行导入的 index.js 是个极小且不再改动的壳,它的 apply 里用 ?load=<时间戳> 动态 import 真正的实现 impl.js——每次重新加载都拿到新副本。
client.js 改了要刷新页面(F5)才生效。bundle 是 link: 安装时,pnpm 层面不用重装。
node verify-host.mjs:不依赖 dsh 的 Host 侧验证(61 项),覆盖 worktree 创建/分支/初始化、两种删除(worktree / 普通目录)、dirty 拒绝与强删、路径护栏、两个路由的信任校验与 JSON 契约、以及自动清理的各分支。临时仓库建在系统临时目录并在结束时删除,不会污染本目录。
安装
dsh plugin add github:Bainianzzz/dsh-fork-worktree
或者用 GitHub Release 上的预构建 tarball:
dsh plugin add https://github.com/Bainianzzz/dsh-fork-worktree/releases/latest/download/dsh-fork-worktree.tgz
装好后,鼠标悬停在侧边栏的工作区行上,就会出现两个按钮。本插件只用 Node 内置模块,不需要构建授权。
文件
- index.js:包入口,也是 Loader 那一行导入的壳(name/inject/apply)。
- impl.js:Host 实现——createWorktreeFork、removeWorkspaceFolder、两个路由、/fork-worktree 命令、工作区清理扫描。
- client.js:浏览器侧——工作区行两个按钮(React portal)、四种弹窗、toast。
- cordis.patch.yml:bundle 的 patch,插入 id 为 fork-worktree 的这一行。
- verify-host.mjs:Host 侧离线验证脚本。
- 依赖:只用 Node 内置模块,不 import 任何 @deepseek-ai/ 包,所以 dependencies 与 peerDependencies 都是空的。预览分享卡片长什么样 ↓
分享 · DeepSeek Harness Hub
Bainianzzz/dsh-fork-worktree
把工作区复制成独立 Git 分支目录
相关插件 · 开发 / 工程
隐私简历生成器reactive-resume/reactive-resume
Reactive Resume 已迁至 GitHub 上的…
★ 45,125已汉化MCP
71.7
AI 架构文档生成器sopaco/deepwiki-rs
🚀 Litho 已进化为 Terrain —— 给你的 AI 智能体一张会呼吸的代码库地图。
★ 3,157已汉化DSH
70.8
长时任务执行闭环AMAP-ML/LongHorizon-Harness
面向 Computer-Use Agent 的 Loop Engineering
★ 1,764已汉化DSH
70.5
AI 编程纪律方法包GanyuanRan/Aegis
让 AI 编程 agent 变得可信:少返工、更安全、说“完成”前先给证据。
★ 1,384已汉化DSH
69.6
多代理编排运行时Enderfga/claw-orchestrator
面向编程智能体的运行时。把 Claude Code、Codex、Antigravity、Grok…
★ 586已汉化MCP
69.4
记忆进化插件csyangwen/dsh-memory-evolve
dsh-memory-evolve 使用场景指南
★ 461已汉化DSH
67.9