dsh-jj
Verified@aqian0/dsh-jj · v0.3.0 · MIT
First-class Jujutsu (jj) support for DeepSeek Harness
Install
dsh plugin add @aqian0/dsh-jj Confirm the layer applied with dsh --profile default --dump-config — see the install guide.
Source
Tags
Readme
dsh-jj
为 DeepSeek Harness 提供 Jujutsu (jj) 一等支持。
功能
- 仓库识别:区分
git/jj-colocated/jj-native/ 非仓库;jj 与 git 嵌套时给出明确归属,不会把 jj 工作区当成普通 git 仓库。 - 防混淆 guard:在 jj 仓库里执行
git commit、git reset等会破坏 change graph 的命令时,默认放行并在结果里提示等价 jj 命令,也可配置为直接拒绝。 - jj 原生工具:15 个
jj_*工具(status / log / diff / new / commit / describe / commit style / squash / undo / op log / workspace / bookmark / rebase)。 - 提交描述风格:默认继承仓库里最近 jj/Git 描述的写法;历史不足或混杂时先询问你选 Conventional Commits / Angular / Gitmoji / 纯文本或自定义风格,也支持在配置里固定风格。
- 小步提交:默认要求模型每完成一个内聚步骤就用
jj commit收尾并开新 change,避免整份任务堆在@里难以审阅。 - 模型上下文:在 jj 仓库里自动向模型注入「优先用 jj」的上下文;非 jj 仓库不注入。
- 多会话并行:检测同一进程、同一 jj 仓库里的其他顶层会话;共用工作副本时引导先隔离到仓库外的独立 jj workspace,同仓库不同 workspace 时提醒不要改写别人的提交,并在任务收尾按标准 rebase 流程交付。
- 项目根发现:让
agent-instructions也能把.jj当作项目根标记(headless/CLI profile 生效)。 - 不回退 git:jj 不可用或目录不是 jj 仓库时明确报错,绝不静默改用 git。
安装
需要 DSH 与 jj CLI(jj 在 PATH 中,或用 jjCommand 指定);无其他运行时依赖。
兼容 DSH 0.2.1-rc.2 及之后的 0.2.x:peer 依赖写作 ^0.2.1-rc.2,同一 0.2 系列内的补丁/预发布升级(如 0.2.1-rc.3)无需更新插件;0.3 起需要插件适配。
从 npm 安装:在 Plugin Manager 里把 spec 填成精确版本(如 @aqian0/[email protected],避免发版后已装内容变化),或执行:
dsh plugin --profile <name> add @aqian0/[email protected]
本地目录安装:lib/ 是构建产物、不入库,先在仓库根执行 npm run build,再在 Plugin Manager 里安装本地 bundle,spec 填本仓库绝对路径(如 /path/to/dsh-jj);Web GUI 在设置 → 插件管理,Agent 侧用 plugin_manager 的 install_bundle。
安装后自动启用 jj-runtime / jj-tools / jj-guard 三行,并给 agent-instructions 加上 .jj 根标记;用 Plugin 页面或 list_plugins 确认。
使用
安装后自动生效,无需手工写 profile。典型的 agent 用法:
| 你说 | 模型调用 |
|---|---|
| "看下当前 jj 状态" | jj_status |
| "列出最近 10 个提交" | jj_log |
| "把当前改动并进上一个提交" | jj_squash |
| "改一下当前 change 的描述" | jj_describe |
| "把这步提交了,继续下一项" | jj_commit |
| "这个仓库的提交信息是什么风格?" | jj_commit_style |
| "撤销刚才那次 jj 操作" | jj_undo |
| "新建一个 workspace 放到 /tmp/wt" | jj_workspace_add |
在 jj 仓库里直接让 agent 跑 git commit 之类的命令时,默认会在工具结果里追加「优先用 jj」的提示;
把 guardMutatingGit 设为 deny 后会直接拒绝并给出等价命令:
Blocked: `git commit` in a Jujutsu (jj) workspace (jj-colocated: /path/to/repo).
Use the jj equivalent instead:
jj commit -m "…" (or: jj describe -m "…" then jj new)
提交描述与拆分提交
默认 commitStyle: 'auto':模型在第一次写描述/提交前会用 jj_commit_style 采样本仓库最近的描述
(jj 历史里包含 colocated 仓库的 Git 提交),继承其 type(scope) 语法、语言、大小写、标题长度与
body 习惯。历史为空、少于 3 条,或多种风格混杂无法确定时,工具返回 needsUserChoice,模型会先
询问你想用哪种风格,而不是擅自决定:
- Conventional Commits(
feat(scope): …) - Angular 约定
- Gitmoji(
:sparkles: …) - 纯文本短句
- 自定义(你把规则或示例贴给它)
也可以直接在配置里固定风格(配置优先于历史):
- id: jj-runtime
name: '@aqian0/dsh-jj'
config:
commitStyle: angular # auto | conventional | angular | gitmoji | plain | custom
# commitStyle: custom
# commitStyleCustom: '中文小写祈使句,不加前缀,必要时空行写原因'
用户在对话里明确指定的风格优先于配置与历史。对话里选定的风格只对当前会话生效(子代理/新会话会重新开始);
要长期固定请写进配置。这些提交策略配置由 jj-runtime 行读取,preferJjContext: false 会连同这段上下文
一起关闭;preferSplitCommits: false 只关闭注入的拆分提示,jj_commit 工具仍可用来拆分。
默认 preferSplitCommits: true:模型被要求按「小步提交」推进——每完成一个内聚步骤(一个功能、
一个修复、一次重构、一次测试/文档调整)就用 jj_commit(jj commit -m "…")描述当前 change
并在其上加一个新的 change(未传 paths 时为空),而不是把整个任务堆在 @ 里;从混杂改动中只提交
部分文件时,给 jj_commit 传 paths。这样人可以从容地逐个提交审阅。commitGuidance: false 可关闭以上注入的提示。
多会话并行与 jj workspace
默认部署里同一个 DSH 进程可能有多个顶层会话同时工作(Web GUI 的会话共用一个进程)。jj-runtime 会检测同一个
jj 仓库里是否还有其他存活的顶层会话(只算顶层会话,子代理不算);检测到多会话时,注入的上下文会要求模型在
动文件之前先隔离工作副本,并在任务收尾时做一次标准 rebase。
multiSessionGuidance 控制这段指引:
auto(默认):仅当同一进程里还有其它顶层会话在同一个 jj 仓库工作时注入;一旦检测到并行会话,收尾 rebase 清单会保留到本会话结束(即使另一个会话先退出),避免最后交付时缺少 rebase 提示。always:始终注入——多个dsh进程并行操作同一仓库时用它。off:不注入多会话指引。
自动检测只能看到同一 DSH 进程内的会话:Web GUI 的会话都在一个进程里,而多个 dsh CLI 进程互相不可见;
跨进程并行请显式配置 multiSessionGuidance: always。检测是 fail-open 的:宿主没有提供 agent 注册表 /
工作目录服务时按「没有其它会话」处理,不注入,也不会影响会话启动。
共享工作副本 vs 同仓库其它 workspace
- 另一个存活会话与本会话共用同一个 jj 工作副本时:动文件前先隔离(只读检查不受影响)。用
jj_workspace_add在仓库外建一个 sibling workspace(绝对路径,例如<repo 的兄弟目录>/repo-session-a,不要放进仓库根内), 再用working_directory工具把本会话切过去;先jj_workspace_list看一下,不要复用别人的 workspace。共享副本 不安全:每条 jj 命令(包括jj workspace add)都会先快照整个目录,不带paths的jj commit可能把另一个会话 未提交的文件一起提交。如果本会话已经在共享副本里改了东西,先只提交本会话自己的路径(jj_commit传paths), 再用jj_workspace_add的parents: ["@"](jj workspace add --revision @ <path>)把这部分带入新 workspace。 只有用户明确要求时才直接改共享副本,此时提交要传paths。 - 另一个会话在同一个 jj 仓库的另一个 workspace:工作副本彼此隔离,但 bookmark 与 operation log 共享。 只可以 rewrite 本会话自己的提交;不要去 rebase / undo / restore 另一个 workspace 的提交,也不要移动它的 bookmark。
任务收尾:标准 rebase
多会话指引生效时,按下面的顺序结束任务:
- 用
jj_commit提交最后一个内聚步骤,让本会话的改动是一段连续的 change 栈。 - 选目标。先跑
jj git fetch(仓库没有远端时 jj 会以 1 退出并提示 "No git remotes to fetch from",忽略即可)。 再确认目标:jj log -r '<target>'必须是一个真实提交(不是空的 root commit),且本会话提交栈的 base 已经在 它的祖先里 ——jj log -r '(<first-change>-) & ~::<target>'必须无输出。任一检查失败(仓库没有远端 trunk, 或本地main领先于远端)时,改用本地main/masterbookmark 或用户在对话里指定的 revision。 - 一次 rebase 移动整段栈:
jj_rebase(revision = 本会话创建的第一个 change,destination = 目标, mode =source)或jj rebase -s <first-change> -d <target>。栈 rebase 绝不能用默认的revisions/-r: 它只移动指定提交,后代会被 re-parent 回旧父提交、留在原地。执行前确认这次移动的后代都属于本会话。 不要 cherry-pick,也不要用 git rebase/merge。 - 复核:
jj_status无冲突、jj_log显示栈直接位于目标之上、jj_diff只有预期文件。冲突时把文件编辑成最终内容 (删掉<<<<<<</%%%%%%%/>>>>>>>标记块)再跑一次jj_status让 jj 完成快照与解决,jj resolve --list可列出仍未解决的冲突;中间提交的冲突用jj edit <change>修完再回到顶层。若 jj 提示 working copy is stale (另一个会话 rewrite 了共享祖先),运行jj workspace update-stale后重新复核 —— 不要用jj undo/jj op restore。 解决不了就带着 change id 停下来向用户报告。 - 只有 bookmark 属于本会话时才用
jj_bookmark_set移动它,然后向用户报告 rebase 后的 change id 与目标。 - 不要自己执行
jj workspace forget—— forget 当前 workspace 会成功,但该目录不再是 jj 的 working copy(文件保留, 之后的 jj 命令会报没有 working copy)。临时 workspace 交给用户:报告jj_workspace_list里的名字/路径, 由用户在默认 workspace 里 forget,再删除目录。
工具一览
| 工具 | 作用 |
|---|---|
jj_status |
工作副本 @ 的状态 |
jj_log |
按 revset 查看提交(@、@-、bookmarks() 等) |
jj_diff |
指定 revset 的 diff |
jj_new |
新建 change 并设为 @ |
jj_commit |
描述当前 change 并新建 change(jj commit = describe + new;可只提交部分路径,剩余改动留在新 change) |
jj_describe |
就地修改提交描述(代替 git commit --amend) |
jj_commit_style |
采样最近描述,报告仓库/配置的提交描述风格;无法确定时返回待用户选择的选项 |
jj_squash |
把改动并进另一个提交 |
jj_undo |
撤销/恢复到某个 operation |
jj_op_log |
operation 历史,用于找 jj_undo 的目标 |
jj_workspace_add / jj_workspace_list |
新建/列出 jj workspace |
jj_bookmark_list / jj_bookmark_set |
列出/设置 bookmark |
jj_rebase |
rebase revision 到目标;mode 可选 source(连同后代,整栈 rebase 用它)/ branch / revisions(默认,只移动指定提交) |
非 jj 仓库会直接拒绝工具调用并说明如何初始化 jj;不会回退到 git。
配置
所有配置键可选,写在 profile 中对应 bundle 行的 config 里:
| 键 | 默认 | 说明 |
|---|---|---|
jjCommand / gitCommand |
jj / git |
可执行文件名;gitCommand 只用于探测和提示 |
timeoutMs / graceMs |
30000 / 3000 |
子进程超时,以及超时后强制结束前的宽限时间 |
maxOutputBytes |
262144 |
单个输出流(stdout / stderr)的上限 |
detectCacheTtlMs |
10000 |
仓库检测结果的缓存时长 |
guardMutatingGit |
warn |
jj 仓库内 mutating git 的处置:warn(放行+提示)/ deny(拒绝)/ off |
guardGitInJjNative |
warn |
仅 jj-native 仓库内 git 的处置 |
guardJjRepositoryWide |
warn |
jj undo / jj op restore 等仓库级 jj 操作的处置 |
guardTools |
bash / pwsh / terminal_send |
参与上述守卫判定的 shell 工具名 |
guardCreateWorktree |
true |
在 jj 仓库里拒绝 create_worktree,引导用 jj workspace add |
readOnlyGitNotice |
true |
colocated 仓库内只读 git 命令是否附「优先用 jj」提示 |
preferJjContext |
true |
会话开始时注入「优先用 jj」的模型可见上下文;关闭会连提交描述与多会话指引一起关闭 |
multiSessionGuidance |
auto |
多会话并行指引:auto(同进程的同一 jj 仓库检测到其他顶层会话时注入)/ always / off |
rebaseTarget |
trunk() |
任务收尾标准 rebase 的目标 revset,不能为空;trunk() 解析不到时回退到 main / master 或用户指定的 revision |
commitGuidance |
true |
是否注入提交描述风格与拆分提交指引(需 preferJjContext 开启) |
commitStyle |
auto |
描述风格:auto(继承历史)/ conventional / angular / gitmoji / plain / custom |
commitStyleCustom |
'' |
commitStyle: custom 时的风格规则(该模式下必填) |
preferSplitCommits |
true |
是否注入「按内聚步骤拆成小提交」的指引(仅影响注入上下文) |
commitStyleSampleLimit |
30 |
jj_commit_style 最多沿用的非空描述条数(读取上限另受 jj-tools 行 maxLogLimit 约束) |
defaultLogLimit / maxLogLimit |
30 / 200 |
jj_log 的默认与最大返回条数 |
defaultCwd |
'' |
工具没有 workdir 时的回退目录(空 = 进程 cwd) |
commitGuidance/commitStyle/commitStyleCustom/preferSplitCommits/commitStyleSampleLimit由jj-runtime行读取:prompt 与jj_commit_style共用同一份策略,这些键写在jj-tools行不会生效。multiSessionGuidance/rebaseTarget同样只由jj-runtime行读取,只影响注入的多会话与收尾 rebase 指引。
已知限制
- jj 未安装或目录不是 jj 仓库时,
jj_*工具会报错并给出处理建议,不会退回 git。 - 描述风格检测是启发式的:分类只看描述首行,body/页脚会通过
jj_commit_style的 examples 返回给模型参照 (jj_log只返回首行),但都不参与自动判定;少于 3 条非空描述或混杂时转为询问用户;设置commitStyle: custom时完全按commitStyleCustom的规则执行。 - Angular 与 Conventional Commits 语法相同,自动检测统一报
conventional(需要严格 Angular 规则时请配置commitStyle: angular);合并提交、大写Fix:之类的写法会归入 plain 或造成 mixed,可能触发询问。 agent-instructions的.jj根标记覆盖在 Web profile 不生效(Web 在 bundle 层禁用了顶层该行),headless/CLI profile 生效。- jj-native 工作区里项目级 skill 根仍按
.git查找(上游硬编码),暂不可用。 - 多会话自动检测只看同一 DSH 进程内的顶层会话(Web GUI 的会话都在一个进程;多个
dshCLI 进程互相不可见), 跨进程并行请配置multiSessionGuidance: always。检测是 fail-open 的:宿主没有 agents / workingDirectory 服务时auto不注入任何内容,也不会让会话启动失败。 - 子代理不注入多会话指引(包括
always):子代理与父会话共享工作副本,父会话继续运行时子代理同时写文件不受本功能 覆盖;需要严格隔离时由父会话显式给子任务指定独立 workspace。