dsh-git-tools 0.0.0-stage → 1.2.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,3 +1,520 @@
1
- # Temporary Holding Version
1
+ > 本仓库创建于 2026年10月2日 10:33,配套的快捷命令让你能直接联通 GitHub 仓库。
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ # dsh-git-tools
4
+
5
+ 给 DeepSeek Harness 的 agent 用的 Git 工具,git 在**宿主进程**中执行。
6
+
7
+ ## 版本与测试环境
8
+
9
+ | 项目 | 版本 |
10
+ |---|---|
11
+ | **已验证的 DSH 版本** | **`0.1.1-rc.2`** 与 **`0.1.7-rc.2`** |
12
+ | Node.js | `v24.9.0`(DSH 随包自带) |
13
+ | Git | `2.55.0.windows.3` |
14
+ | 操作系统 | Windows 11(build 10.0.26200) |
15
+
16
+ ### 兼容性范围
17
+
18
+ 本插件已在**两个不同年代的 DSH 版本**上实测通过,两者都能正常加载并注册全部
19
+ 8 个 agent 工具与 13 个斜杠命令:
20
+
21
+ | DSH 版本 | 场景 | 结果 |
22
+ |---|---|---|
23
+ | `0.1.1-rc.2` | 全局 CLI(`dsh web --port 8080`) | ✅ 通过 |
24
+ | `0.1.7-rc.2` | DSH Desktop | ✅ 通过 |
25
+
26
+ 之所以能跨这两个版本,是因为插件**只使用两者共有的 API**:
27
+
28
+ - 宿主服务 `ctx.subprocess`(执行 git)与 `ctx.commands`(注册斜杠命令)
29
+ - 命令注册字段仅用 `name` / `description` / `handler` / `input.hint`
30
+
31
+ ### 刻意避开的字段
32
+
33
+ `CommandDefinitionId`(来自 `@deepseek-ai/dsh-commands/brand`)**只存在于较新版本**,
34
+ `0.1.1-rc.2` 的 `brand` 模块仅导出 `CommandId`。它的类型声明是:
35
+
36
+ ```ts
37
+ export interface CommandDefinition {
38
+ readonly name: string;
39
+ readonly description: string;
40
+ readonly input?: CommandInputDescriptor;
41
+ readonly recordInput?: boolean;
42
+ readonly handler: (invocation) => CommandResult | Promise<CommandResult>;
43
+ }
44
+ ```
45
+
46
+ **没有 `definitionId`。** 早期版本曾依赖该字段,导致在 `0.1.1-rc.2` 上加载时报:
47
+
48
+ ```
49
+ SyntaxError: The requested module '@deepseek-ai/dsh-commands/brand'
50
+ does not provide an export named 'CommandDefinitionId'
51
+ ```
52
+
53
+ 现已完全移除。因为 `definitionId` 在新版中本就是**可选**字段,移除不影响新版行为,
54
+ 却让旧版也能加载。
55
+
56
+ ### 其他说明
57
+
58
+ - `package.json` 的 `peerDependencies` 声明了 `@deepseek-ai/cordis@^4.0.1` 与
59
+ `@deepseek-ai/dsh-tools@^0.1.1-rc.2`(下限设为已验证的最低版本,以便旧版也能安装)。
60
+ 这两个包由 DSH 运行时注入,**不需要 pnpm 安装**,安装时若出现 `missing peer` 警告可以忽略。
61
+ - **升级 DSH 后请重新验证。** 尤其是斜杠命令的注册契约与命令名规则
62
+ (`/^[a-z][a-z0-9_-]*$/`)属于宿主内部约定,跨版本可能变化;本插件之所以兼容两个版本,
63
+ 正是因为它只依赖这些约定中最稳定的部分。
64
+ - 使用新版本专有 API 会立刻破坏旧版兼容。若确需使用,请在本文档的兼容表中
65
+ 注明最低版本要求。
66
+
67
+ ## 为什么需要它
68
+
69
+ agent 的 shell 运行在 `workspace-write` 文件沙箱里,实测该环境**无法访问网络**:
70
+ 受限进程拿不到 TLS 凭据句柄,任何 `https://` 请求都会失败(包括 GitHub、gitee、百度)。
71
+ 同时 **git 不读 Windows 系统代理(WinINET)**,所以即使 FLClash 开着系统代理,
72
+ 沙箱内的 `git fetch` / `pull` / `push` 依然失败。
73
+
74
+ 本插件通过 `ctx.subprocess` 在宿主进程里 spawn git。宿主进程不受 agent 沙箱约束,
75
+ 因此这些联网操作可以正常工作。这与官方 `dsh-workspace-changes` 插件执行
76
+ `git diff-tree` 的方式一致。
77
+
78
+ ## 提供的工具
79
+
80
+ | 工具 | 作用 | 是否联网 |
81
+ |---|---|---|
82
+ | `git_status` | 分支、upstream、ahead/behind、逐文件状态 | 否 |
83
+ | `git_diff` | 工作区 / 暂存区 / 指定 rev 的 diff | 否 |
84
+ | `git_log` | 最近提交列表 | 否 |
85
+ | `git_stage` | 暂存或取消暂存(`all` 或指定路径) | 否 |
86
+ | `git_commit` | 创建提交(支持 `all`、`amend`) | 否 |
87
+ | `git_fetch` | 抓取远端并报告 ahead/behind | **是** |
88
+ | `git_pull` | 抓取并集成(支持 `rebase`) | **是** |
89
+ | `git_push` | 推送到远端(支持 `set-upstream`) | **是** |
90
+
91
+ 所有工具都接受可选 `cwd`,默认使用当前会话的工作目录。
92
+
93
+ ## 提供的斜杠命令(人类使用)
94
+
95
+ 在输入框直接输入即可,**不经过模型**,在宿主进程中执行 git:
96
+
97
+ | 命令 | 选项 | 是否联网 |
98
+ |---|---|---|
99
+ | `/git` | — | 否 |
100
+ | `/git-status` | `cwd=<dir>` | 否 |
101
+ | `/git-diff` | `--staged` `rev=<rev>` `cwd=<dir>` | 否 |
102
+ | `/git-log` | `n` `cwd=<dir>` | 否 |
103
+ | `/git-commit` | `--all` `--amend` `<message>` `cwd=<dir>` | 否 |
104
+ | `/git-fetch` | `--prune` `remote=<name>` `cwd=<dir>` | **是** |
105
+ | `/git-pull` | `--rebase` `remote=<name>` `branch=<name>` `cwd=<dir>` | **是** |
106
+ | `/git-push` | `--set-upstream` `remote=<name>` `branch=<name>` `cwd=<dir>` | **是** |
107
+ | `/git-name-set` | `name=<name>` `email=<email>` `remote=<url>` `cwd=<dir>` | 否 |
108
+ | `/git-tag-show` | `[version]` `cwd=<dir>` | 否 |
109
+ | `/git-tag` | `<version>` `[<remote>]` `message=<text>` `rev=<rev>` `cwd=<dir>`;或 `--push [version] [<remote>]` | **是** |
110
+
111
+ **每一个命令都支持 `cwd=<dir>`**,用于指定仓库所在目录(见下方「关于仓库定位」)。
112
+
113
+ ### 参数类型约定
114
+
115
+ | 记号 | 含义 | 例子 |
116
+ |---|---|---|
117
+ | `<x>` | 必填值 | `<message>` |
118
+ | `[x]` | 可选 | `[cwd=<dir>]` |
119
+ | `--flag` | 开关,写了就生效,**不带值** | `--staged` |
120
+ | `key=<v>` | 键值对,**必须带 `=`** | `cwd=D:\repo` |
121
+
122
+ ⚠️ **值参数漏掉 `=` 会失效**:`cwd=D:\repo` ✅;`cwd D:\repo` ❌(被当成两个无关的词)。
123
+
124
+ ## 两个关键参数:`cwd` 与 `remote`
125
+
126
+ 这两个是**寻址参数**——它们不提供新能力,只负责把命令指向正确的目标。
127
+
128
+ ### `cwd=<dir>`:指定操作哪个仓库
129
+
130
+ **所有 9 个命令都支持**,这是最常用的参数。
131
+
132
+ | 项目 | 说明 |
133
+ |---|---|
134
+ | 指定什么 | git 在**哪个目录**执行,也就是操作哪个仓库 |
135
+ | 默认值 | 省略时使用**当前会话的工作目录** |
136
+ | 写法 | `cwd=D:\path\to\repo` 或 `cwd=D:/path/to/repo`(两种斜杠都行) |
137
+ | 必须是仓库 | 目录不是 git 仓库时会明确报错,**不会静默失败** |
138
+ | 路径含空格 | 目前**不支持**(解析按空格分词),请避免 |
139
+
140
+ **什么情况下必须写 `cwd=`:**
141
+
142
+ 会话工作目录本身不是 git 仓库时。典型情形是工作区指向一个"容器目录",
143
+ 真正的仓库在它的子目录里:
144
+
145
+ ```
146
+ 工作区根 D:\Githubrep ← 不是仓库
147
+ 真正的仓库 D:\Githubrep\skills-introduction-to-github ← 仓库在这里
148
+ ```
149
+
150
+ 此时每个命令都要带 `cwd=`:
151
+
152
+ ```
153
+ /git-status cwd=D:\Githubrep\skills-introduction-to-github
154
+ /git-commit cwd=D:\Githubrep\skills-introduction-to-github "改了什么"
155
+ /git-push cwd=D:\Githubrep\skills-introduction-to-github
156
+ ```
157
+
158
+ **一劳永逸的替代方案**:把 DSH 工作区直接指向仓库根目录。
159
+ 之后所有命令零参数,也不再需要 `cwd=`。
160
+
161
+ ### `remote=<...>`:指定远端——**注意语义有两种**
162
+
163
+ `remote=` 在两类命令里含义**完全不同**,这是最容易搞混的地方。
164
+ (下表只列出**涉及远端**的命令,不是命令全集;全集见上面的命令表。)
165
+
166
+ | 命令 | `remote=` 填什么 | 例子 | 效果 |
167
+ |---|---|---|---|
168
+ | `/git-push`<br>`/git-pull`<br>`/git-fetch` | **远端名** | `remote=origin` | 对哪个已配置的远端操作 |
169
+ | `/git-name-set` | **URL** | `remote=https://github.com/you/repo.git` | 把这个仓库的 origin **改指向**新地址 |
170
+
171
+ **为什么容易错**:`remote=upstream` 在 push/pull/fetch 里是合法的("名叫 upstream 的远端"),
172
+ 但在 `/git-name-set` 里会被**拒绝**,因为它期待一个 URL。
173
+
174
+ ```
175
+ /git-push remote=upstream ✅ 远端名,推到名为 upstream 的远端
176
+ /git-name-set name=X email=x@y.com remote=upstream ❌ 被拒绝:这需要 URL
177
+ /git-name-set name=X email=x@y.com remote=https://...git ✅ URL,改写 origin
178
+ ```
179
+
180
+ **基本信息:**
181
+
182
+ | 项目 | 说明 |
183
+ |---|---|
184
+ | 适用命令 | 只有 `/git-push`、`/git-pull`、`/git-fetch`(其余命令不涉及远端) |
185
+ | 默认值 | `origin`——git 克隆仓库时自动创建的默认远端 |
186
+ | 什么时候需要改 | 见下方「多个远端的场景」 |
187
+
188
+ **多个远端的场景**(默认 `origin` 不够用时):
189
+
190
+ ```powershell
191
+ # 先在终端里添加一个远端(插件没有添加远端的命令)
192
+ git remote add gitee https://gitee.com/you/repo.git
193
+ git remote add upstream https://github.com/original/repo.git
194
+ ```
195
+
196
+ 之后就能用 `remote=` 区分:
197
+
198
+ ```
199
+ /git-push remote=gitee 推到 Gitee 而不是 GitHub
200
+ /git-fetch remote=upstream 从原始仓库(你 fork 的来源)取更新
201
+ /git-pull remote=upstream branch=main 把原始仓库的 main 合并进来
202
+ ```
203
+
204
+ **查看当前有哪些远端**:插件没有专门命令,用终端 `git remote -v`,
205
+ 或让 agent 执行。
206
+
207
+ ### `/git-name-set`:配置提交身份与仓库指向
208
+
209
+ 一次性设置**提交身份**,并可选地把当前仓库指向另一个远端地址:
210
+
211
+ ```
212
+ /git-name-set name=ZhangSan email=zhangsan@example.com
213
+ /git-name-set name=ZhangSan email=zhangsan@example.com remote=https://github.com/zhangsan/repo.git
214
+ ```
215
+
216
+ | 参数 | 必填 | 写入位置 | 作用范围 |
217
+ |---|---|---|---|
218
+ | `name=<name>` | ✅ | `git config --global user.name` | **全机器所有仓库** |
219
+ | `email=<email>` | ✅ | `git config --global user.email` | **全机器所有仓库** |
220
+ | `remote=<url>` | — | 目标仓库的 `origin`(`remote set-url`) | **仅该仓库** |
221
+ | `cwd=<dir>` | — | — | 指定要改远端的仓库 |
222
+
223
+ **两点必须理解清楚:**
224
+
225
+ 1. **身份是全局的,不是临时的。** `git config --global` 写入 `~/.gitconfig`,
226
+ 之后本机所有仓库的提交都用这个名字和邮箱。想只影响单个仓库,请手动用
227
+ `git config --local`。
228
+ 2. **`remote=` 必须填 URL,不能填远端名。** 填 `remote=upstream` 会被拒绝,
229
+ 因为这是"远端名"而非地址。它的作用是**改变这个仓库推送的目标地址**,
230
+ 而不是新建一个远端。
231
+
232
+ 所有校验都在写入之前完成:**参数有误时不会有任何副作用**(不会写配置、不会改 remote)。
233
+
234
+ 示例:
235
+
236
+ ```
237
+ /git 列出全部命令与用法
238
+ /git-status
239
+ /git-commit 修复登录跳转
240
+ /git-commit --all 批量更新文档
241
+ /git-push --set-upstream
242
+ /git-pull --rebase
243
+ /git-diff --staged
244
+ ```
245
+
246
+ 也支持 `key=value` 形式的选项,用于覆盖默认值:
247
+
248
+ ```
249
+ /git-status cwd=D:\some\other\repo
250
+ /git-push remote=upstream branch=release
251
+ /git-diff rev=origin/main...HEAD
252
+ ```
253
+
254
+ **关于仓库定位**:命令默认以「会话工作目录」为仓库根。如果该目录本身不是仓库
255
+ (例如工作区指向 `D:\Githubrep` 而仓库在其子目录),命令会返回一条明确提示,
256
+ 此时用 `cwd=<路径>` 指向真正的仓库,或直接把工作区改到仓库根目录。
257
+
258
+ ## 版本管理(标签)
259
+
260
+ ### 先理解:版本是什么
261
+
262
+ git 里**没有自动的版本号**。每 `git commit` 一次就产生一个提交,但提交只由哈希
263
+ (如 `9f6af9d`)标识,无法用 `v1.0.0` 这样称呼它。
264
+
265
+ **标签(tag)就是给某个提交起的名字**,这才是"版本":
266
+
267
+ ```
268
+ 提交 b237987 ──▶ C1
269
+ 提交 8985e21 ──▶ C2
270
+ 提交 816bced ──▶ C3 ◀── 标签 v1.0.0 指向这里
271
+ ```
272
+
273
+ 关键性质:
274
+
275
+ - **标签指向一个提交**,所以任何版本都能被永久取回(只要该提交存在)
276
+ - **标签是本地对象**,`git tag` 只写进你的本地仓库
277
+ - **必须推送到远端**(`/git-tag` 创建时就会推送),GitHub 才会在 **Tags** 和 **Releases** 页显示它
278
+ - **删掉标签不影响提交**;提交本身不会被标签"绑定"
279
+
280
+ ### 两个命令
281
+
282
+ 打标签与推送被合并进同一条命令,读写分离:**`/git-tag-show` 只读**,**`/git-tag` 负责写和推**。
283
+
284
+ | 命令 | 作用 | 联网 |
285
+ |---|---|---|
286
+ | `/git-tag-show` | 列出所有版本(最新在前,含哈希、日期、主题) | 否 |
287
+ | `/git-tag-show <version>` | 查看某个版本:标签信息 + 改动的文件 | 否 |
288
+ | `/git-tag <version> [<remote>]` | 打标签并推送到远端(默认 `origin`) | **是** |
289
+ | `/git-tag --push [version]` | 推送已存在的标签;省略版本则推送全部 | **是** |
290
+
291
+ ### 完整工作流
292
+
293
+ ```
294
+ # 1. 确认当前状态,想清楚给哪个提交打版本
295
+ /git-status cwd=D:\Githubrep\skills-introduction-to-github
296
+ /git-log 5 cwd=D:\Githubrep\skills-introduction-to-github
297
+
298
+ # 2. 打版本标签并推送(一步完成;省略远端则用 origin)
299
+ /git-tag v1.0.0 message=首个可用版本 cwd=D:\Githubrep\skills-introduction-to-github
300
+
301
+ # 3. 本地确认(列出全部版本)
302
+ /git-tag-show cwd=D:\Githubrep\skills-introduction-to-github
303
+
304
+ # 4. 若推送失败(标签已留在本地),只重推不重复创建
305
+ /git-tag --push v1.0.0 cwd=D:\Githubrep\skills-introduction-to-github
306
+
307
+ # 5. 随时回顾某个版本改了什么
308
+ /git-tag-show v1.0.0 cwd=D:\Githubrep\skills-introduction-to-github
309
+ ```
310
+
311
+ ### 参数细节
312
+
313
+ **`/git-tag <version>`**(创建 + 推送)
314
+
315
+ | 参数 | 必填 | 说明 |
316
+ |---|---|---|
317
+ | `<version>` | ✅ | 版本名,如 `v1.0.0`。**第一个**位置参数 |
318
+ | `[<remote>]` | — | **推送目标**,**第二个**位置参数,如 `/git-tag v1.0.0 origin`;省略则用默认远端 `origin`。也可写成 `remote=<name>`,两者等价 |
319
+ | `message=<text>` | — | 给出则创建**附注标签**(带说明与打标签者信息);省略则创建**轻量标签** |
320
+ | `rev=<rev>` | — | 给指定提交打标签;省略则给当前 `HEAD` 打 |
321
+ | `cwd=<dir>` | — | 仓库目录 |
322
+
323
+ ⚠️ **两个位置参数就是上限,且选项值不能含空格。** 解析按空格分词,所以
324
+ `message=first release` 会变成 `message=first` 加一个游离词 `release`。命令**不会**
325
+ 把它当成推送目标——它会拒绝并说明原因(游离词若恰好是已配置的远端名,仍会被当作
326
+ 推送目标,所以给 message 赋值时请勿留空格):
327
+
328
+ ```
329
+ /git-tag v1.0.0 message=first release ❌ 报 Unknown remote: release
330
+ /git-tag v1.0.0 origin message=first release ❌ 报 Too many arguments
331
+ /git-tag v1.0.0 message=first-release ✅
332
+ ```
333
+
334
+ 第二个位置参数会**对照 `git remote` 校验**:写成未配置的名字会直接报错并列出可用远端,
335
+ 不会静默推到一个不存在的目标。`remote=<name>` 等价于第二个位置参数。
336
+
337
+ **版本名规则**(同时用内置正则与 `git check-ref-format` 双重校验):
338
+
339
+ | 规则 | 例 |
340
+ |---|---|
341
+ | 不能含空格 | ❌ `v1 0` |
342
+ | 不能含 `~ ^ : ? * [ \ |` | ❌ `v1:0` |
343
+ | 不能含连续两个点 | ❌ `v1..0` |
344
+ | 不能以 `.` 或 `-` 开头 | ❌ `-v1` |
345
+ | 不能以 `.` 或 `.lock` 结尾 | ❌ `v1.0.0.lock` |
346
+ | 允许斜杠(可做分层命名) | ✅ `release/v1.0.0` |
347
+
348
+ **`/git-tag --push [version]`**(只推送,不创建)
349
+
350
+ | 参数 | 必填 | 说明 |
351
+ |---|---|---|
352
+ | `[version]` | — | **省略则推送全部本地标签**(`push --tags`) |
353
+ | `[<remote>]` | — | 推送目标,第二个位置参数,默认 `origin`(`remote=<name>` 等价) |
354
+ | `cwd=<dir>` | — | 仓库目录 |
355
+
356
+ ⚠️ `--push` 之后的**第一个词永远是版本名**,所以 `/git-tag --push origin` 是在找名为
357
+ `origin` 的标签。想把全部标签推到某个远端,请写 `remote=`:
358
+
359
+ ```
360
+ /git-tag --push origin ❌ 报 "origin is a configured remote, not a tag"
361
+ /git-tag --push remote=origin ✅ 全部标签推到 origin
362
+ /git-tag --push v1.0.0 origin ✅ 只推 v1.0.0 到 origin
363
+ ```
364
+
365
+ **执行顺序**:先创建(本地、快),再推送。两者各自报告结果;推送失败时标签**已经存在**
366
+ 于本地,命令会明确告诉你这一点并给出重推命令,不会让你误以为版本没打成。
367
+
368
+ ### 与 GitHub Releases 的关系
369
+
370
+ 推送标签后,GitHub 会**自动生成** Tags 页:
371
+
372
+ ```
373
+ https://github.com/<用户>/<仓库>/tags
374
+ ```
375
+
376
+ 而 **Releases 页**需要额外一步(在标签基础上附加发布说明、二进制包):
377
+
378
+ ```
379
+ https://github.com/<用户>/<仓库>/releases
380
+ ```
381
+
382
+ 两种做法:
383
+
384
+ 1. **在 GitHub 网页上创建 Release**(推荐)——打开 Tags 页,点标签右侧的
385
+ "Create release",填标题和说明即可
386
+ 2. **等本插件后续支持** —— 创建 Releases 需要调用 GitHub API,目前**未实现**;
387
+ 本插件的标签命令只负责 git 侧的标签,不涉及 GitHub Releases API
388
+
389
+ ### 已知限制
390
+
391
+ - **没有删除标签的命令**。要删请用终端 `git tag -d <版本>`(本地)或
392
+ `git push origin --delete <版本>`(远端)
393
+ - **不能检出/切换到某个版本**。查看用 `/git-tag-show <版本>`,真要切过去需要
394
+ `git switch --detach <版本>`
395
+ - **不支持签名标签**(`git tag -s`)
396
+ - **没有"只创建不推送"的用法**:`/git-tag` 一律创建后推送。推送失败时标签留在本地,
397
+ 用 `/git-tag --push <版本>` 单独重推;不需要推送的标签请在终端用 `git tag` 手动创建
398
+ - **一次 `/git-tag --push` 不带版本会推送全部标签**,注意目标仓库是否需要这么多版本
399
+
400
+ ## 提供的 agent 工具
401
+
402
+ 与斜杠命令并列,模型也可以自主调用同名能力(`git_status`、`git_commit` 等)。
403
+ 两者的区别只是触发者:命令由**人**输入 `/` 触发,工具由**模型**按需调用。
404
+
405
+ ## 设计说明
406
+
407
+ - **不做 force push**:`git_push` 只使用 git 默认的非强制推送,没有提供 force 参数。
408
+ - **输出与语言环境无关**:使用 `--porcelain`、`-z`、显式 `--pretty=format`,
409
+ 不依赖 `LC_ALL`。
410
+ - **凭据**:`GIT_TERMINAL_PROMPT=0` 保证缺少凭据时快速失败而不是挂起。
411
+ Windows 上 `credential.helper=manager` 会从凭据管理器取票,无需交互。
412
+ - **超时**:本地操作 120 秒,联网操作 300 秒。
413
+ - **`--no-color` 只用于接受它的子命令**:实测(git 2.55)`git diff` 与 `git log`
414
+ **接受** `--no-color`,而 `git commit`、`git fetch`、`git pull`、`git push`
415
+ **拒绝**它并直接以 `error: unknown option 'no-color'` 失败。切勿给后者添加该参数。
416
+ - **沙箱边界不变**:agent 自己的 shell 仍受 `workspace-write` 限制,
417
+ 本插件没有放宽它,只是把 git 放到了宿主侧执行。
418
+
419
+ ## 安装
420
+
421
+ **本仓库根目录就是一个可安装的 DSH bundle**(根 `package.json` 声明了
422
+ `dsh.bundle.patch`,指向根 `cordis.patch.yml`),所以三种方式都可以:
423
+
424
+ ### 方式 1:从 GitHub 地址安装
425
+
426
+ DSH GUI 侧边栏 →「插件」页 →「添加插件」,填入仓库地址:
427
+
428
+ ```
429
+ https://github.com/Yangi-252410/dsh-import-repositories
430
+ ```
431
+
432
+ ### 方式 2:从本地目录安装
433
+
434
+ 先把仓库克隆到本机,然后填入**该目录的绝对路径**:
435
+
436
+ ```
437
+ D:\Githubrep\dsh-git-tools
438
+ ```
439
+
440
+ ⚠️ 用**正斜杠**更稳妥(GUI 输入框里反斜杠可能被转义吃掉):
441
+
442
+ ```
443
+ D:/Githubrep/dsh-git-tools
444
+ ```
445
+
446
+ ### 方式 3:由 agent 安装
447
+
448
+ 由具备 `plugin_manager` 工具的会话执行 `install_bundle`,target 填本地绝对路径。
449
+
450
+ ### 安装后
451
+
452
+ | 项目 | 说明 |
453
+ |---|---|
454
+ | 生效范围 | **该 `DSH_HOME` 的这个 profile**(同一根下的所有工作区;其他 `DSH_HOME` 不受影响) |
455
+ | 生效时机 | 重启宿主进程 + 新会话(命令集与工具集在会话创建时固定) |
456
+ | 出现内容 | 8 个 agent 工具(`git_status` 等) |
457
+ | 斜杠命令 | 输入 `/git` 列出全部 |
458
+ | `missing peer` 警告 | 可忽略,`@deepseek-ai/*` 由运行时注入 |
459
+
460
+ **版本对应**:`package.json` 的 `version` 字段(当前 `1.2.3`)是插件自身的版本,
461
+ 与仓库的 git 标签(如 `v1.2.3`)是两套编号——习惯上让它们对齐,但升插件版本不会自动打标签,
462
+ 反之打标签也不会自动改 `package.json`。查看已发布的版本请到仓库的 Releases 页。
463
+
464
+ ## 多宿主与更新(重要)
465
+
466
+ **装在哪里由 `$DSH_HOME` 决定,不由本仓库的位置决定**:
467
+
468
+ ```
469
+ $DSH_HOME/profiles/<profile>/ ← 插件的实际安装目录
470
+ ```
471
+
472
+ `DSH_HOME` 的取值顺序(见 `@deepseek-ai/dsh-home-paths`):显式配置 → 环境变量 `DSH_HOME`
473
+ → 默认 `~/.dsh`。桌面应用会把自己的 harness 目录设为 `DSH_HOME`;自定义启动脚本
474
+ (例如 `start-dsh-web.cmd` 里的 `set DSH_HOME=...`)可以指向任意目录。
475
+
476
+ **因此:不同的 `DSH_HOME` 就是不同的安装,彼此完全独立。** 同一台机器上很容易同时存在多个:
477
+
478
+ | 宿主 | `DSH_HOME`(示意) | profile |
479
+ |---|---|---|
480
+ | 全局 CLI `dsh web`(环境里没设 `DSH_HOME`) | `~/.dsh` | 默认或 `--profile` 指定 |
481
+ | DSH 桌面应用 | `%APPDATA%\dsh-desktop\harness` | `web` |
482
+ | 自定义脚本启动的实例 | 脚本里 `set DSH_HOME=` 的目录 | `--profile` 指定 |
483
+
484
+ 在某一端安装或更新,**其他端不会跟着变**;同一个根下的多个进程(例如两个 `dsh web`)
485
+ 才共享同一份安装。想确认自己在哪一端,就在该端终端执行 `"$env:DSH_HOME"`,
486
+ 或直接看界面里 `/git` 有没有命令。
487
+
488
+ ### 两种安装形态:更新方式不同
489
+
490
+ | 形态 | 出现位置 | 由谁创建 | 如何更新 |
491
+ |---|---|---|---|
492
+ | **普通 pnpm 安装** | `profiles/<p>/node_modules/dsh-git-tools` | CLI `dsh plugin add` 或网页插件页 | `dsh plugin --profile <p> rm dsh-git-tools`,再 `add <spec>`,然后重启进程 |
493
+ | **generation 快照** | `profiles/.generations/live/<包名>+<版本>+<哈希>/`,并由 `profiles/<p>/package.json` 的 `pnpm.overrides` 指向它 | **只有桌面应用**(日志形如 `generation-install: … promoted to …`) | 在桌面插件页重新安装(源填本地路径最稳)。此时 `dsh plugin add` 只改依赖记录,**不会**重投影快照,代码不会变 |
494
+
495
+ ### 更新步骤(通用)
496
+
497
+ 1. 确认目标端的根:`"$env:DSH_HOME"`;
498
+ 2. 更新:CLI/网页端用 `dsh plugin --profile <p> rm|add <spec>`;桌面端用插件页重装;
499
+ 3. **重启该宿主进程**(`Ctrl+C` 后重新 `dsh web`,或重启桌面应用);
500
+ 4. **新开会话**——旧会话永远是旧命令表;
501
+ 5. 自检(都在 `$DSH_HOME/profiles/<p>/` 内):
502
+ - `package.json` → `dependencies["dsh-git-tools"]` 的版本;
503
+ - `pnpm-lock.yaml` → 该包后面的 commit(`git+…#<sha>`);
504
+ - `node_modules/dsh-git-tools/index.js` → 是否含新命令(例如 `git-tag-show`);
505
+ - 界面上 `/git` 列出的命令。
506
+
507
+ ### 两个必须知道的坑
508
+
509
+ - **git 依赖被 lock 钉死**:`add github:<owner>/<repo>` 之后,`pnpm-lock.yaml` 记录的是
510
+ 当时解析到的 commit。**再 `add` 同一个 spec 不会换 pin**,必须 `rm` 之后再 `add`
511
+ (或显式写 `#main` / `#<sha>`),否则会以为"更新了"其实还是旧代码。
512
+ - **填写过的 spec 不会被原样记录**:`package.json` 里存的是解析后的版本号(如 `1.2.3`),
513
+ 所以只看依赖版本**分不出**当初是从本地路径还是 GitHub 装的。要看去
514
+ `profiles/<p>/.plugin-manager/logs/*/generation.log`(桌面端)或 `pnpm-lock.yaml`(CLI 端)。
515
+
516
+ ## 已知限制
517
+
518
+ - 仅宿主插件,暂无 Web UI 面板(面板是下一步)。
519
+ - `git_push` 依赖已保存在 Windows 凭据管理器中的凭据;没有缓存凭据时会失败并给出 git 的诊断信息。
520
+ - 冲突的 `git_pull` 会报错并把冲突留给用户解决,不会自动处理。
@@ -0,0 +1,7 @@
1
+ # Bundle patch for dsh-git-tools.
2
+ # Inserts the Host plugin row that registers the git tools for the agent.
3
+
4
+ - insert:
5
+ - id: git-tools
6
+ name: 'dsh-git-tools'
7
+ config: {}
package/icon.svg ADDED
@@ -0,0 +1,8 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24" fill="none" stroke="#f05033" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
2
+ <circle cx="6" cy="5" r="2.6" />
3
+ <circle cx="6" cy="19" r="2.6" />
4
+ <circle cx="18" cy="12" r="2.6" />
5
+ <path d="M6 7.6v8.8" />
6
+ <path d="M8.6 5h3.4a3 3 0 0 1 3 3v1.5" />
7
+ <path d="M8.6 19h3.4a3 3 0 0 0 3-3v-1.5" />
8
+ </svg>