dsh-plugin-worktrees 0.1.0 → 0.1.2
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/CHANGELOG.md +48 -0
- package/README.md +14 -3
- package/README.zh.md +4 -2
- package/cordis.patch.yml +21 -14
- package/docs/DESIGN.md +13 -9
- package/lib/config.js +9 -0
- package/lib/index.js +43 -32
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,54 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [0.1.2] - 2026-08-27
|
|
9
|
+
|
|
10
|
+
Fixes for the two load-failure issues reported against 0.1.1. **Upgrade is
|
|
11
|
+
required for npm-based installs** — 0.1.x cannot load at all from an
|
|
12
|
+
`dsh plugin add dsh-plugin-worktrees` install.
|
|
13
|
+
|
|
14
|
+
### Fixed
|
|
15
|
+
|
|
16
|
+
- **npm installs: plugin tree failed to load with `ERR_MODULE_NOT_FOUND`
|
|
17
|
+
(issue #1).** The patch entry in `cordis.patch.yml` said `name:
|
|
18
|
+
dsh-worktrees` (the repo name) while the npm package is
|
|
19
|
+
`dsh-plugin-worktrees`; the cordis loader imports the entry's `name` as a
|
|
20
|
+
module specifier against the profile's node_modules, so every npm-based
|
|
21
|
+
install died. The patch `name`, `package.json` name, and the module's
|
|
22
|
+
`export const name` are now identical (family convention), and
|
|
23
|
+
`test/cordis-patch.test.js` pins the invariant so the drift cannot
|
|
24
|
+
silently return.
|
|
25
|
+
- **`config:` parsing to null rejected by zod strict (issue #2).** A patch
|
|
26
|
+
`config:` key holding only comments parses as YAML `null`;
|
|
27
|
+
`validateConfig` now treats `null`/`undefined` as "no configuration" and
|
|
28
|
+
applies defaults instead of failing with `(root): Expected object,
|
|
29
|
+
received null`. The shipped patch uses an explicit `config: {}`. Other
|
|
30
|
+
non-object shapes (e.g. an array) still fail loudly with a precise root
|
|
31
|
+
message.
|
|
32
|
+
- **Source-tree (tsx) launcher: dual-instance self-check misfired fatally
|
|
33
|
+
(issue #2).** Under `pnpm dsh web` the harness resolves `dsh-tools` to its
|
|
34
|
+
src build while the plugin gets the lib build — two module instances, two
|
|
35
|
+
`TOOL_RUNTIME_SCHEDULER` Symbols — and `assertSingleDshToolsInstance`'s
|
|
36
|
+
fatal branch killed the boot even though the plugin works fine (its tools
|
|
37
|
+
only use the public `ctx.tools.register` API and never touch the Symbol).
|
|
38
|
+
The check is now an advisory warning naming the `setup:peer` escape hatch;
|
|
39
|
+
`npm run setup:peer` remains the fix for a genuine second copy whose tool
|
|
40
|
+
calls do fail with `Cannot read properties of undefined (reading
|
|
41
|
+
'prepare')`.
|
|
42
|
+
|
|
43
|
+
### Tests
|
|
44
|
+
|
|
45
|
+
- 295 → 310: new `test/cordis-patch.test.js` (patch/name/config shape
|
|
46
|
+
guard), null-config and missing-config boot coverage, array-config
|
|
47
|
+
failure coverage, and the softened self-check branches re-pinned (Symbol
|
|
48
|
+
split now expects warn + full six-tool registration instead of a throw).
|
|
49
|
+
|
|
50
|
+
## [0.1.1] - 2026-08-18
|
|
51
|
+
|
|
52
|
+
First release through the npm trusted-publishing pipeline (OIDC, tag
|
|
53
|
+
`v0.1.1`, no tokens, provenance attached). No code changes — 0.1.0 was
|
|
54
|
+
bootstrapped with a manual publish; this version proves the CI path.
|
|
55
|
+
|
|
8
56
|
## [0.1.0] - 2026-08-18
|
|
9
57
|
|
|
10
58
|
First release. `dsh-worktrees` gives the DeepSeek Harness **git worktree
|
package/README.md
CHANGED
|
@@ -44,8 +44,17 @@ dsh --profile web
|
|
|
44
44
|
**Expected result:** a new session exposes `worktree_create`, `worktree_list`,
|
|
45
45
|
`worktree_status`, `worktree_merge`, `worktree_queue`, `worktree_cleanup`.
|
|
46
46
|
|
|
47
|
+
> **Both launchers work (0.1.2).** The dual-instance check that 0.1.x turned
|
|
48
|
+
> into a hard failure is now an advisory warning: under the source-tree (tsx)
|
|
49
|
+
> launcher the harness loads the `src` build of `dsh-tools` while the plugin
|
|
50
|
+
> gets the `lib` build, which used to kill the boot with a false positive.
|
|
51
|
+
> This plugin only uses the public `ctx.tools.register` API, so the two
|
|
52
|
+
> copies coexist safely; the warning points at `setup:peer` for the rare case
|
|
53
|
+
> where tool calls DO fail with `Cannot read properties of undefined
|
|
54
|
+
> (reading 'prepare')`.
|
|
55
|
+
|
|
47
56
|
> **Local checkout instead of npm?** Run `npm install && npm run setup:peer`
|
|
48
|
-
> inside the repo first (avoids a second copy of `dsh-tools`, which
|
|
57
|
+
> inside the repo first (avoids a second copy of `dsh-tools`, which can crash
|
|
49
58
|
> every tool call), then `dsh plugin --profile web add "$(pwd)"`.
|
|
50
59
|
|
|
51
60
|
> **Want agents to work inside the worktrees?** The stock harness silently
|
|
@@ -163,8 +172,10 @@ row in your profile's `cordis.patch.yml`; unknown keys fail loudly at startup.
|
|
|
163
172
|
integration branch; `worktree_queue(action: "list")`, then `resolve` or
|
|
164
173
|
`retry` it.
|
|
165
174
|
- **Every tool call dies with `Cannot read properties of undefined
|
|
166
|
-
(reading 'prepare')`** —
|
|
167
|
-
|
|
175
|
+
(reading 'prepare')`** — a genuine second `dsh-tools` copy (typically a
|
|
176
|
+
local checkout without `npm run setup:peer`); re-run it after `npm
|
|
177
|
+
install` here or a dsh upgrade. A startup *warning* alone is expected and
|
|
178
|
+
harmless — see the note under Install.
|
|
168
179
|
|
|
169
180
|
## Development
|
|
170
181
|
|
package/README.zh.md
CHANGED
|
@@ -29,7 +29,9 @@ dsh --profile web
|
|
|
29
29
|
|
|
30
30
|
**预期结果:** 新会话里出现 `worktree_create`、`worktree_list`、`worktree_status`、`worktree_merge`、`worktree_queue`、`worktree_cleanup` 六个工具。
|
|
31
31
|
|
|
32
|
-
>
|
|
32
|
+
> **两种启动方式都支持(0.1.2 起)。** 0.1.x 曾把 dsh-tools 双实例检查做成硬失败,现在是建议性告警:源码树(tsx)启动器下宿主加载 dsh-tools 的 `src` 构建、插件拿到 `lib` 构建,旧版会被这个假阳性直接卡死启动。本插件只用公开的 `ctx.tools.register` API,两份拷贝可以安全共存;告警里指明的 `setup:peer` 只在工具调用真的报 `Cannot read properties of undefined (reading 'prepare')` 时才需要跑。
|
|
33
|
+
|
|
34
|
+
> **用本地检出而不是 npm?** 先在仓库里跑 `npm install && npm run setup:peer`(避免出现第二份 dsh-tools,否则每次工具调用都可能崩),再 `dsh plugin --profile web add "$(pwd)"`。
|
|
33
35
|
|
|
34
36
|
> **想让 agent 直接在 worktree 里干活?** 官方宿主会静默丢弃子代理的 `cwd`。安装
|
|
35
37
|
> [dsh-plugin-subagents](https://github.com/Luck9Star/dsh-plugin-subagents)
|
|
@@ -122,7 +124,7 @@ worktree_cleanup({ worktree_id: id1 }) // 移除 worktree 和分
|
|
|
122
124
|
- **一个仓库的合并只走一个 dsh 会话。** 队列是进程内的;不支持两个宿主并发合并同一仓库。
|
|
123
125
|
- **`cwd` 被忽略了?** 官方宿主会丢弃它 —— 装 dsh-plugin-subagents 并跑它的 `patches/install.sh`。
|
|
124
126
|
- **`active_job_exists` 报错** —— 更早的一个冲突作业占着集成分支;`worktree_queue(action: "list")`,然后 `resolve` 或 `retry` 它。
|
|
125
|
-
- **每次工具调用都报 `Cannot read properties of undefined (reading 'prepare')`** ——
|
|
127
|
+
- **每次工具调用都报 `Cannot read properties of undefined (reading 'prepare')`** —— 真正的第二份 dsh-tools 拷贝(典型:本地检出没跑 `npm run setup:peer`);在本仓库 `npm install` 后、或 dsh 升级后重跑。仅启动时出现一条*告警*是预期且无害的 —— 见「安装」下的说明。
|
|
126
128
|
|
|
127
129
|
## 开发
|
|
128
130
|
|
package/cordis.patch.yml
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# dsh-worktrees bundle patch (T11, DESIGN §8.3).
|
|
1
|
+
# dsh-plugin-worktrees bundle patch (T11, DESIGN §8.3).
|
|
2
2
|
#
|
|
3
3
|
# 用途:把 dsh-worktrees 插件实例挂进 profile 的层序。本插件是纯 tool
|
|
4
4
|
# 插件——六件工具(worktree_create / list / status / merge / queue /
|
|
@@ -7,24 +7,31 @@
|
|
|
7
7
|
# 零宿主补丁(本仓库没有 patches/ 目录)。形态对齐 dsh-ssh 的单
|
|
8
8
|
# insert 写法。
|
|
9
9
|
#
|
|
10
|
-
# 安装方式:`dsh plugin --profile <name> add dsh-worktrees`(或 add
|
|
10
|
+
# 安装方式:`dsh plugin --profile <name> add dsh-plugin-worktrees`(或 add
|
|
11
11
|
# <本地路径>)——reconcile 解析 package.json 的 dsh.bundle.patch 字段,
|
|
12
|
-
# 自动把本 patch 层追加到 bundles 尾部,insert
|
|
13
|
-
#
|
|
14
|
-
#
|
|
15
|
-
#
|
|
12
|
+
# 自动把本 patch 层追加到 bundles 尾部,insert 行随即生效。
|
|
13
|
+
#
|
|
14
|
+
# 两条曾经踩过的坑(0.1.2 修复,勿回退):
|
|
15
|
+
# 1. name 必须等于 npm 包名(package.json 的 name)——loader 拿这个
|
|
16
|
+
# 字段作为 import specifier 去 profile 的 node_modules 解析;写成
|
|
17
|
+
# 仓库名 dsh-worktrees 会让整个插件树 ERR_MODULE_NOT_FOUND(issue #1)。
|
|
18
|
+
# test/cordis-patch.test.js 钉死 patch name === package.json name。
|
|
19
|
+
# 2. config 必须是显式的 `{}` —— 只跟注释的裸 `config:` 键会被 YAML
|
|
20
|
+
# 解析成 null,zod strict 拒收(issue #2)。要覆盖配置,请在
|
|
21
|
+
# profile 层的同 id 行写 config(profile 层覆盖 bundle 层),键表见
|
|
22
|
+
# README「配置」:
|
|
23
|
+
# maxWorktrees: 16 # 非终态 worktree 全局上限(跨 repo 计数)
|
|
24
|
+
# allowedRoots: ["~/Documents/dev/Agents"] # repo 门禁额外放行的根(realpath 子树判定)
|
|
25
|
+
# 其余键:worktreeRoot / statePath / defaultBaseRef / gitTimeoutMs /
|
|
26
|
+
# mergeTimeoutMs / requireWorkspaceRegistration / autoCollect /
|
|
27
|
+
# retainJobHistory / register 六开关 —— 逐键说明见 README。
|
|
16
28
|
#
|
|
17
29
|
# 互斥:无。新名字工具不与官方行或同族插件冲突,可与 dsh-plugin-
|
|
18
30
|
# subagents 等任意 bundle 并存(正交组合正是本插件的卖点)。
|
|
19
31
|
|
|
20
32
|
- insert:
|
|
21
33
|
# 本行即 host-plane Cordis 插件实例;六件 worktree_* 工具由它注册。
|
|
34
|
+
# config 显式空表 = lib/config.js 的 zod strict 全默认。
|
|
22
35
|
- id: worktrees
|
|
23
|
-
name: dsh-worktrees
|
|
24
|
-
config:
|
|
25
|
-
# —— 全部键省略即走 lib/config.js 的 zod strict 默认;示例(注释):
|
|
26
|
-
# maxWorktrees: 16 # 非终态 worktree 全局上限(跨 repo 计数)
|
|
27
|
-
# allowedRoots: ["~/Documents/dev/Agents"] # repo 门禁额外放行的根(realpath 子树判定)
|
|
28
|
-
# 其余键:worktreeRoot / statePath / defaultBaseRef / gitTimeoutMs /
|
|
29
|
-
# mergeTimeoutMs / requireWorkspaceRegistration / autoCollect /
|
|
30
|
-
# retainJobHistory / register 六开关 —— 逐键说明见 README。
|
|
36
|
+
name: dsh-plugin-worktrees
|
|
37
|
+
config: {}
|
package/docs/DESIGN.md
CHANGED
|
@@ -628,22 +628,26 @@ dsh-worktrees/
|
|
|
628
628
|
### 8.3 cordis.patch.yml(形态)
|
|
629
629
|
|
|
630
630
|
```yaml
|
|
631
|
-
# dsh-worktrees bundle patch — 单行 insert(新名字工具在全局层天然可见,
|
|
631
|
+
# dsh-plugin-worktrees bundle patch — 单行 insert(新名字工具在全局层天然可见,
|
|
632
632
|
# 无需 disable 任何官方行;对齐 dsh-ssh 的单 insert 形态)。
|
|
633
633
|
- insert:
|
|
634
634
|
- id: worktrees
|
|
635
|
-
name: dsh-worktrees
|
|
636
|
-
config:
|
|
637
|
-
# 全部键省略走 lib/config.js 默认;示例:
|
|
638
|
-
# maxWorktrees: 16
|
|
639
|
-
# allowedRoots: ["~/Documents/dev/Agents"]
|
|
635
|
+
name: dsh-plugin-worktrees
|
|
636
|
+
config: {}
|
|
640
637
|
```
|
|
641
638
|
|
|
642
|
-
insert 行写法学自:dsh-ssh `cordis.patch.yml`(单 insert 双面插件)与 dsh-web-ui-all 聚合 patch(`- insert:` + `- id:` + `name:` 两键缩进形态)。`dsh plugin --profile <name> add dsh-worktrees` 后 reconcile 自动追加层序(分析 §4:解析 `dsh.bundle.patch` 依赖按序追加 bundles 尾部)。
|
|
639
|
+
insert 行写法学自:dsh-ssh `cordis.patch.yml`(单 insert 双面插件)与 dsh-web-ui-all 聚合 patch(`- insert:` + `- id:` + `name:` 两键缩进形态)。`dsh plugin --profile <name> add dsh-plugin-worktrees` 后 reconcile 自动追加层序(分析 §4:解析 `dsh.bundle.patch` 依赖按序追加 bundles 尾部)。
|
|
643
640
|
|
|
644
|
-
|
|
641
|
+
**0.1.2 修订(issue #1 + #2,两条都是这个文件的 drift,已由 `test/cordis-patch.test.js` 钉死):**
|
|
645
642
|
|
|
646
|
-
|
|
643
|
+
1. **`name` 必须等于 npm 包名**(package.json 的 `name`)。cordis loader 拿 entry 的 `name` 作为 import specifier 去 profile 的 node_modules 解析(`cordis-plugin-loader` `_init()` 的 `tree.import(this.options.name, …)`)——0.1.x 写成仓库名 `dsh-worktrees` 而 npm 发的是 `dsh-plugin-worktrees`,所有 npm 安装路径死于 `ERR_MODULE_NOT_FOUND`。家族惯例(dsh-plugin-subagents)三名一致:package name === patch name === 模块导出 `export const name`。
|
|
644
|
+
2. **`config` 必须显式写 `{}`**。只跟注释的裸 `config:` 键被 YAML 解析成 `null`,zod strict 报 `(root): Expected object, received null`(宿主把 entry config 原样传进 apply())。注释示例移到文件头注释,行内只留空表;`lib/config.js` 同时对 null 走默认值兜底(双保险)。
|
|
645
|
+
|
|
646
|
+
### 8.4 peer 双实例防御(C6)—— 0.1.2 起为建议性告警
|
|
647
|
+
|
|
648
|
+
`@deepseek-ai/dsh-tools` 声明 peer;README 安装步骤含 `npm run setup:peer`(symlink 到 live harness 根,脚本照搬 subagents `scripts/link-harness-dsh-tools.sh` 模式);`apply()` 开头保留 subagents 同款 `assertSingleDshToolsInstance` 探测(`TOOL_RUNTIME_SCHEDULER` Symbol)。
|
|
649
|
+
|
|
650
|
+
**0.1.2 修订(issue #2):分支 2(Symbol 缺失 + ToolRuntime 形状 ⇒ fatal + throw)软化为 warn + 继续注册。** 原设计照搬 dsh-plugin-subagents,但在源码树(tsx)启动器下必然误杀:宿主经 tsconfig paths 把 `@deepseek-ai/dsh-tools` 解析到 **src 构建**,插件经普通 ESM 从 profile node_modules 解析到 **lib 构建** —— 两个物理实例、两个 Symbol,`setup:peer` 的 symlink 只能指向 lib 构建、无法统一。软化是安全的:本插件六件工具全部经公开 `ctx.tools.register` API 注册、用 `defineTool` 纯定义对象,工具体从不触碰 scheduler Symbol(该 Symbol 由 dsh-agent-loop 消费、且永远作用于宿主自己的 dsh-tools 实例)——双实例对本插件无功能影响。告警文案保留 `setup:peer` 提示,覆盖真正双实例(工具调用报 `Cannot read properties of undefined (reading 'prepare')`)的场景。
|
|
647
651
|
|
|
648
652
|
---
|
|
649
653
|
|
package/lib/config.js
CHANGED
|
@@ -101,6 +101,15 @@ export const SCALAR_DEFAULTS = Object.freeze({
|
|
|
101
101
|
* clear message)
|
|
102
102
|
*/
|
|
103
103
|
export function validateConfig(config = {}) {
|
|
104
|
+
// A comment-only `config:` key in a host YAML layer parses as null (issue
|
|
105
|
+
// #2) — an absent configuration, not an invalid one, so it takes the same
|
|
106
|
+
// defaults path as an omitted key. Any other non-object still fails loud.
|
|
107
|
+
if (config === null || config === undefined) config = {}
|
|
108
|
+
if (typeof config !== 'object' || Array.isArray(config)) {
|
|
109
|
+
throw new Error(
|
|
110
|
+
`dsh-worktrees: invalid config — (root): expected an object, got ${Array.isArray(config) ? 'array' : typeof config}`,
|
|
111
|
+
)
|
|
112
|
+
}
|
|
104
113
|
const result = pluginConfigSchema.safeParse(config)
|
|
105
114
|
if (!result.success) {
|
|
106
115
|
const issues = result.error.issues.map(
|
package/lib/index.js
CHANGED
|
@@ -9,14 +9,15 @@
|
|
|
9
9
|
* apply() order (DESIGN §3 diagram + §7.3 "before tool registration"):
|
|
10
10
|
*
|
|
11
11
|
* a. validateConfig (lib/config.js zod strict; unknown keys fail loud);
|
|
12
|
-
* b. assertSingleDshToolsInstance — the @deepseek-ai/dsh-tools
|
|
13
|
-
* instance
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
12
|
+
* b. assertSingleDshToolsInstance — the @deepseek-ai/dsh-tools
|
|
13
|
+
* dual-instance ADVISORY probe (§8.4, softened at 0.1.2 per issue #2):
|
|
14
|
+
* the host ToolRuntime carries its scheduler under the dsh-tools
|
|
15
|
+
* module-level TOOL_RUNTIME_SCHEDULER Symbol; when that Symbol is not
|
|
16
|
+
* visible on ctx.tools (a second physical copy of the package, or the
|
|
17
|
+
* src/lib split under the source-tree tsx launcher) the probe warns
|
|
18
|
+
* with the setup:peer hint and lets the boot continue — this plugin
|
|
19
|
+
* only uses the public ctx.tools.register API, so a split instance
|
|
20
|
+
* cannot break its tool calls (see the function comment for history);
|
|
20
21
|
* c. createGitPort (timeoutMs = gitTimeoutMs) + createStateStore +
|
|
21
22
|
* store.load() (a corrupt state file aborts the boot loudly);
|
|
22
23
|
* d. reconcile — the crash reconciliation (§7.3), BEFORE any tool is
|
|
@@ -59,7 +60,13 @@ import { registerWorktreeMergeTool } from './tools/worktree-merge.js'
|
|
|
59
60
|
import { registerWorktreeQueueTool } from './tools/worktree-queue.js'
|
|
60
61
|
import { registerWorktreeCleanupTool } from './tools/worktree-cleanup.js'
|
|
61
62
|
|
|
62
|
-
|
|
63
|
+
// Plugin identity must equal the npm package name (package.json "name"):
|
|
64
|
+
// the cordis loader resolves the patch entry's `name` as an import specifier
|
|
65
|
+
// against the profile's node_modules, and the family convention (dsh-plugin-
|
|
66
|
+
// subagents) keeps package name === patch name === module export name. The
|
|
67
|
+
// 0.1.x drift (patch said `dsh-worktrees`, npm said `dsh-plugin-worktrees`)
|
|
68
|
+
// broke every npm-based install with ERR_MODULE_NOT_FOUND (issue #1).
|
|
69
|
+
export const name = 'dsh-plugin-worktrees'
|
|
63
70
|
|
|
64
71
|
// workspaceRegistry is used through OPTIONAL probing (§8.2 peer-face
|
|
65
72
|
// minimisation: headless hosts without a workspace registry still boot) —
|
|
@@ -81,38 +88,42 @@ function realPathBestEffort(target) {
|
|
|
81
88
|
}
|
|
82
89
|
|
|
83
90
|
/**
|
|
84
|
-
* dsh-tools dual-instance
|
|
85
|
-
*
|
|
91
|
+
* dsh-tools dual-instance advisory probe (§8.4; softened at 0.1.2, issue #2).
|
|
92
|
+
*
|
|
93
|
+
* History: this used to be a fatal check modelled on dsh-plugin-subagents —
|
|
94
|
+
* Symbol absent + ToolRuntime shape ⇒ throw. That misfires under the
|
|
95
|
+
* source-tree (tsx) harness launcher: the harness resolves dsh-tools to its
|
|
96
|
+
* src build (via tsconfig path mappings) while the plugin, loaded from the
|
|
97
|
+
* profile's node_modules by plain ESM, gets the lib build — two PHYSICAL
|
|
98
|
+
* module instances, two Symbols, and `npm run setup:peer` cannot unify them
|
|
99
|
+
* (its symlink points at the lib build only). The fatal then made the plugin
|
|
100
|
+
* outright unusable there.
|
|
101
|
+
*
|
|
102
|
+
* Why softening is safe: every tool this plugin registers goes through the
|
|
103
|
+
* PUBLIC `ctx.tools.register` API and plain `defineTool` definition objects.
|
|
104
|
+
* The tool bodies never touch the scheduler Symbol — that Symbol is consumed
|
|
105
|
+
* by dsh-agent-loop when dispatching the HOST's tools, always against the
|
|
106
|
+
* host's own dsh-tools instance. A second dsh-tools copy only matters if
|
|
107
|
+
* plugin code reached for cross-instance identity (ours never does), so a
|
|
108
|
+
* missing Symbol degrades to a warning that names the diagnostic rule of
|
|
109
|
+
* thumb, never a load failure.
|
|
86
110
|
*
|
|
87
111
|
* 1. `ctx.tools[TOOL_RUNTIME_SCHEDULER] !== undefined` → same physical
|
|
88
|
-
* module — healthy;
|
|
89
|
-
* 2. Symbol absent
|
|
90
|
-
*
|
|
91
|
-
* failing beats every tool call dying after load), pointing at the
|
|
92
|
-
* peer re-link (npm run setup:peer);
|
|
93
|
-
* 3. anything else (fake ctx / an unseen host shape) → cannot reliably
|
|
94
|
-
* judge; warn only. Tests and non-standard hosts must not be killed by
|
|
95
|
-
* a false positive.
|
|
112
|
+
* module — healthy, silent;
|
|
113
|
+
* 2. Symbol absent (fake ctx, src/lib split, or a true second copy) →
|
|
114
|
+
* warn only, keep booting.
|
|
96
115
|
*/
|
|
97
116
|
function assertSingleDshToolsInstance(ctx) {
|
|
98
117
|
const tools = ctx && ctx.tools
|
|
99
118
|
if (!tools || typeof tools.register !== 'function') return
|
|
100
119
|
if (tools[TOOL_RUNTIME_SCHEDULER] !== undefined) return // same physical module — healthy
|
|
101
|
-
const looksLikeToolRuntime =
|
|
102
|
-
typeof tools.view === 'function' && typeof tools.schemas === 'function'
|
|
103
|
-
if (looksLikeToolRuntime) {
|
|
104
|
-
const detail =
|
|
105
|
-
'dsh-worktrees: detected a second @deepseek-ai/dsh-tools module instance — '
|
|
106
|
-
+ "every tool call from this plugin would die with \"Cannot read properties of undefined (reading 'prepare')\". "
|
|
107
|
-
+ 'Run npm run setup:peer (scripts/link-harness-dsh-tools.sh) in the dsh-worktrees package '
|
|
108
|
-
+ 'so its dsh-tools copy resolves to the live harness root, then restart dsh.'
|
|
109
|
-
if (ctx.logger && typeof ctx.logger.fatal === 'function') ctx.logger.fatal(detail)
|
|
110
|
-
throw new Error(detail)
|
|
111
|
-
}
|
|
112
120
|
if (ctx.logger && typeof ctx.logger.warn === 'function') {
|
|
113
121
|
ctx.logger.warn(
|
|
114
|
-
'dsh-worktrees:
|
|
115
|
-
+ '
|
|
122
|
+
'dsh-worktrees: the @deepseek-ai/dsh-tools scheduler Symbol is not visible on ctx.tools '
|
|
123
|
+
+ '(a second dsh-tools copy, or the src/lib split under the source-tree tsx launcher). '
|
|
124
|
+
+ "This plugin only uses the public ctx.tools.register API, so tool calls are unaffected; "
|
|
125
|
+
+ 'if you DO see "Cannot read properties of undefined (reading \'prepare\')", run '
|
|
126
|
+
+ 'npm run setup:peer (scripts/link-harness-dsh-tools.sh) in this package and restart dsh.',
|
|
116
127
|
)
|
|
117
128
|
}
|
|
118
129
|
}
|
package/package.json
CHANGED