@dsh-cc/tool-git-worktree 0.5.0
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/LICENSE +201 -0
- package/README.i18n.yaml +6 -0
- package/README.md +49 -0
- package/README.zh.md +49 -0
- package/lib/index.d.ts +25 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +330 -0
- package/lib/index.js.map +1 -0
- package/lib/invariant.d.ts +16 -0
- package/lib/invariant.d.ts.map +1 -0
- package/lib/invariant.js +23 -0
- package/lib/invariant.js.map +1 -0
- package/lib/render.d.ts +41 -0
- package/lib/render.d.ts.map +1 -0
- package/lib/render.js +71 -0
- package/lib/render.js.map +1 -0
- package/lib/worktree.d.ts +125 -0
- package/lib/worktree.d.ts.map +1 -0
- package/lib/worktree.js +184 -0
- package/lib/worktree.js.map +1 -0
- package/package.json +59 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
Apache License
|
|
2
|
+
Version 2.0, January 2004
|
|
3
|
+
http://www.apache.org/licenses/
|
|
4
|
+
|
|
5
|
+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
6
|
+
|
|
7
|
+
1. Definitions.
|
|
8
|
+
|
|
9
|
+
"License" shall mean the terms and conditions for use, reproduction,
|
|
10
|
+
and distribution as defined by Sections 1 through 9 of this document.
|
|
11
|
+
|
|
12
|
+
"Licensor" shall mean the copyright owner or entity authorized by
|
|
13
|
+
the copyright owner that is granting the License.
|
|
14
|
+
|
|
15
|
+
"Legal Entity" shall mean the union of the acting entity and all
|
|
16
|
+
other entities that control, are controlled by, or are under common
|
|
17
|
+
control with that entity. For the purposes of this definition,
|
|
18
|
+
"control" means (i) the power, direct or indirect, to cause the
|
|
19
|
+
direction or management of such entity, whether by contract or
|
|
20
|
+
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
|
21
|
+
outstanding shares, or (iii) beneficial ownership of such entity.
|
|
22
|
+
|
|
23
|
+
"You" (or "Your") shall mean an individual or Legal Entity
|
|
24
|
+
exercising permissions granted by this License.
|
|
25
|
+
|
|
26
|
+
"Source" form shall mean the preferred form for making modifications,
|
|
27
|
+
including but not limited to software source code, documentation
|
|
28
|
+
source, and configuration files.
|
|
29
|
+
|
|
30
|
+
"Object" form shall mean any form resulting from mechanical
|
|
31
|
+
transformation or translation of a Source form, including but
|
|
32
|
+
not limited to compiled object code, generated documentation,
|
|
33
|
+
and conversions to other media types.
|
|
34
|
+
|
|
35
|
+
"Work" shall mean the work of authorship, whether in Source or
|
|
36
|
+
Object form, made available under the License, as indicated by a
|
|
37
|
+
copyright notice that is included in or attached to the work
|
|
38
|
+
(an example is provided in the Appendix below).
|
|
39
|
+
|
|
40
|
+
"Derivative Works" shall mean any work, whether in Source or Object
|
|
41
|
+
form, that is based on (or derived from) the Work and for which the
|
|
42
|
+
editorial revisions, annotations, elaborations, or other modifications
|
|
43
|
+
represent, as a whole, an original work of authorship. For the purposes
|
|
44
|
+
of this License, Derivative Works shall not include works that remain
|
|
45
|
+
separable from, or merely link (or bind by name) to the interfaces of,
|
|
46
|
+
the Work and Derivative Works thereof.
|
|
47
|
+
|
|
48
|
+
"Contribution" shall mean any work of authorship, including
|
|
49
|
+
the original version of the Work and any modifications or additions
|
|
50
|
+
to that Work or Derivative Works thereof, that is intentionally
|
|
51
|
+
submitted to Licensor for inclusion in the Work by the copyright owner
|
|
52
|
+
or by an individual or Legal Entity authorized to submit on behalf of
|
|
53
|
+
the copyright owner. For the purposes of this definition, "submitted"
|
|
54
|
+
means any form of electronic, verbal, or written communication sent
|
|
55
|
+
to the Licensor or its representatives, including but not limited to
|
|
56
|
+
communication on electronic mailing lists, source code control systems,
|
|
57
|
+
and issue tracking systems that are managed by, or on behalf of, the
|
|
58
|
+
Licensor for the purpose of discussing and improving the Work, but
|
|
59
|
+
excluding communication that is conspicuously marked or otherwise
|
|
60
|
+
designated in writing by the copyright owner as "Not a Contribution."
|
|
61
|
+
|
|
62
|
+
"Contributor" shall mean Licensor and any individual or Legal Entity
|
|
63
|
+
on behalf of whom a Contribution has been received by Licensor and
|
|
64
|
+
subsequently incorporated within the Work.
|
|
65
|
+
|
|
66
|
+
2. Grant of Copyright License. Subject to the terms and conditions of
|
|
67
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
68
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
69
|
+
copyright license to reproduce, prepare Derivative Works of,
|
|
70
|
+
publicly display, publicly perform, sublicense, and distribute the
|
|
71
|
+
Work and such Derivative Works in Source or Object form.
|
|
72
|
+
|
|
73
|
+
3. Grant of Patent License. Subject to the terms and conditions of
|
|
74
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
75
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
76
|
+
(except as stated in this section) patent license to make, have made,
|
|
77
|
+
use, offer to sell, sell, import, and otherwise transfer the Work,
|
|
78
|
+
where such license applies only to those patent claims licensable
|
|
79
|
+
by such Contributor that are necessarily infringed by their
|
|
80
|
+
Contribution(s) alone or by combination of their Contribution(s)
|
|
81
|
+
with the Work to which such Contribution(s) was submitted. If You
|
|
82
|
+
institute patent litigation against any entity (including a
|
|
83
|
+
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
|
84
|
+
or a Contribution incorporated within the Work constitutes direct
|
|
85
|
+
or contributory patent infringement, then any patent licenses
|
|
86
|
+
granted to You under this License for that Work shall terminate
|
|
87
|
+
as of the date such litigation is filed.
|
|
88
|
+
|
|
89
|
+
4. Redistribution. You may reproduce and distribute copies of the
|
|
90
|
+
Work or Derivative Works thereof in any medium, with or without
|
|
91
|
+
modifications, and in Source or Object form, provided that You
|
|
92
|
+
meet the following conditions:
|
|
93
|
+
|
|
94
|
+
(a) You must give any other recipients of the Work or
|
|
95
|
+
Derivative Works a copy of this License; and
|
|
96
|
+
|
|
97
|
+
(b) You must cause any modified files to carry prominent notices
|
|
98
|
+
stating that You changed the files; and
|
|
99
|
+
|
|
100
|
+
(c) You must retain, in the Source form of any Derivative Works
|
|
101
|
+
that You distribute, all copyright, patent, trademark, and
|
|
102
|
+
attribution notices from the Source form of the Work,
|
|
103
|
+
excluding those notices that do not pertain to any part of
|
|
104
|
+
the Derivative Works; and
|
|
105
|
+
|
|
106
|
+
(d) If the Work includes a "NOTICE" text file as part of its
|
|
107
|
+
distribution, then any Derivative Works that You distribute must
|
|
108
|
+
include a readable copy of the attribution notices contained
|
|
109
|
+
within such NOTICE file, excluding those notices that do not
|
|
110
|
+
pertain to any part of the Derivative Works, in at least one
|
|
111
|
+
of the following places: within a NOTICE text file distributed
|
|
112
|
+
as part of the Derivative Works; within the Source form or
|
|
113
|
+
documentation, if provided along with the Derivative Works; or,
|
|
114
|
+
within a display generated by the Derivative Works, if and
|
|
115
|
+
wherever such third-party notices normally appear. The contents
|
|
116
|
+
of the NOTICE file are for informational purposes only and
|
|
117
|
+
do not modify the License. You may add Your own attribution
|
|
118
|
+
notices within Derivative Works that You distribute, alongside
|
|
119
|
+
or as an addendum to the NOTICE text from the Work, provided
|
|
120
|
+
that such additional attribution notices cannot be construed
|
|
121
|
+
as modifying the License.
|
|
122
|
+
|
|
123
|
+
You may add Your own copyright statement to Your modifications and
|
|
124
|
+
may provide additional or different license terms and conditions
|
|
125
|
+
for use, reproduction, or distribution of Your modifications, or
|
|
126
|
+
for any such Derivative Works as a whole, provided Your use,
|
|
127
|
+
reproduction, and distribution of the Work otherwise complies with
|
|
128
|
+
the conditions stated in this License.
|
|
129
|
+
|
|
130
|
+
5. Submission of Contributions. Unless You explicitly state otherwise,
|
|
131
|
+
any Contribution intentionally submitted for inclusion in the Work
|
|
132
|
+
by You to the Licensor shall be under the terms and conditions of
|
|
133
|
+
this License, without any additional terms or conditions.
|
|
134
|
+
Notwithstanding the above, nothing herein shall supersede or modify
|
|
135
|
+
the terms of any separate license agreement you may have executed
|
|
136
|
+
with Licensor regarding such Contributions.
|
|
137
|
+
|
|
138
|
+
6. Trademarks. This License does not grant permission to use the trade
|
|
139
|
+
names, trademarks, service marks, or product names of the Licensor,
|
|
140
|
+
except as required for reasonable and customary use in describing the
|
|
141
|
+
origin of the Work and reproducing the content of the NOTICE file.
|
|
142
|
+
|
|
143
|
+
7. Disclaimer of Warranty. Unless required by applicable law or
|
|
144
|
+
agreed to in writing, Licensor provides the Work (and each
|
|
145
|
+
Contributor provides its Contributions) on an "AS IS" BASIS,
|
|
146
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
147
|
+
implied, including, without limitation, any warranties or conditions
|
|
148
|
+
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
|
149
|
+
PARTICULAR PURPOSE. You are solely responsible for determining the
|
|
150
|
+
appropriateness of using or redistributing the Work and assume any
|
|
151
|
+
risks associated with Your exercise of permissions under this License.
|
|
152
|
+
|
|
153
|
+
8. Limitation of Liability. In no event and under no legal theory,
|
|
154
|
+
whether in tort (including negligence), contract, or otherwise,
|
|
155
|
+
unless required by applicable law (such as deliberate and grossly
|
|
156
|
+
negligent acts) or agreed to in writing, shall any Contributor be
|
|
157
|
+
liable to You for damages, including any direct, indirect, special,
|
|
158
|
+
incidental, or consequential damages of any character arising as a
|
|
159
|
+
result of this License or out of the use or inability to use the
|
|
160
|
+
Work (including but not limited to damages for loss of goodwill,
|
|
161
|
+
work stoppage, computer failure or malfunction, or any and all
|
|
162
|
+
other commercial damages or losses), even if such Contributor
|
|
163
|
+
has been advised of the possibility of such damages.
|
|
164
|
+
|
|
165
|
+
9. Accepting Warranty or Additional Liability. While redistributing
|
|
166
|
+
the Work or Derivative Works thereof, You may choose to offer,
|
|
167
|
+
and charge a fee for, acceptance of support, warranty, indemnity,
|
|
168
|
+
or other liability obligations and/or rights consistent with this
|
|
169
|
+
License. However, in accepting such obligations, You may act only
|
|
170
|
+
on Your own behalf and on Your sole responsibility, not on behalf
|
|
171
|
+
of any other Contributor, and only if You agree to indemnify,
|
|
172
|
+
defend, and hold each Contributor harmless for any liability
|
|
173
|
+
incurred by, or claims asserted against, such Contributor by reason
|
|
174
|
+
of your accepting any such warranty or additional liability.
|
|
175
|
+
|
|
176
|
+
END OF TERMS AND CONDITIONS
|
|
177
|
+
|
|
178
|
+
APPENDIX: How to apply the Apache License to your work.
|
|
179
|
+
|
|
180
|
+
To apply the Apache License to your work, attach the following
|
|
181
|
+
boilerplate notice, with the fields enclosed by brackets "[]"
|
|
182
|
+
replaced with your own identifying information. (Don't include
|
|
183
|
+
the brackets!) The text should be enclosed in the appropriate
|
|
184
|
+
comment syntax for the file format. We also recommend that a
|
|
185
|
+
file or class name and description of purpose be included on the
|
|
186
|
+
same "printed page" as the copyright notice for easier
|
|
187
|
+
identification within third-party archives.
|
|
188
|
+
|
|
189
|
+
Copyright [yyyy] [name of copyright owner]
|
|
190
|
+
|
|
191
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
192
|
+
you may not use this file except in compliance with the License.
|
|
193
|
+
You may obtain a copy of the License at
|
|
194
|
+
|
|
195
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
196
|
+
|
|
197
|
+
Unless required by applicable law or agreed to in writing, software
|
|
198
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
199
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
200
|
+
See the License for the specific language governing permissions and
|
|
201
|
+
limitations under the License.
|
package/README.i18n.yaml
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
|
|
2
|
+
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
|
3
|
+
# after editing either side, bring the other along and re-record with:
|
|
4
|
+
# pnpm run verify-translation-pairing --write packages/workspace/tool-git-worktree/README.md
|
|
5
|
+
README.md: b9e39859c338cb70b6339a6d15361aded783d288
|
|
6
|
+
README.zh.md: 66232faaee6cc48bb10aaef0977af9e88b92a766
|
package/README.md
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# @dsh-cc/tool-git-worktree
|
|
2
|
+
|
|
3
|
+
English | [中文](README.zh.md)
|
|
4
|
+
|
|
5
|
+
Model-facing `EnterWorktree` / `ExitWorktree` tools that create, keep, and remove isolated git worktrees under `<repo>/.claude/worktrees/`. They run every git command through the `ctx.shell` executor seam (resolve then run — never a direct spawn) and validate every path they act on stays inside the repository through `ctx.fs`.
|
|
6
|
+
|
|
7
|
+
Requires a loaded shell executor Service Provider (e.g. `@deepseek-ai/dsh-bash-local`), a filesystem provider (`@deepseek-ai/dsh-fs-local`), `ctx.tools`, and `ctx.systemPrompt`. The plugin stays pending until `inject: ['tools', 'shell', 'systemPrompt', 'fs']` is satisfied.
|
|
8
|
+
|
|
9
|
+
## Tools
|
|
10
|
+
|
|
11
|
+
### `EnterWorktree`
|
|
12
|
+
|
|
13
|
+
Creates a worktree on a fresh `worktree-<name>` branch from HEAD and switches the session into it.
|
|
14
|
+
|
|
15
|
+
| Arg | Type | Notes |
|
|
16
|
+
|---|---|---|
|
|
17
|
+
| `name` | string | Worktree slug. Each `/`-separated segment allows letters, digits, `.`, `_`, `-`; max 64 chars. A random `adjective-noun-suffix` slug is generated when omitted. |
|
|
18
|
+
|
|
19
|
+
The tool locates the repository root from the calling agent's session cwd (`git rev-parse --show-toplevel`); outside a git working tree it returns a structured error rather than changing anything. Because the session working directory is fixed at session creation in this harness, the cwd switch is declared two ways that are safe given the immutable session cwd: the tool result and a `tool:worktree:cwd` systemPrompt runtime context both state the new working directory, and the model is told to pass `workdir` equal to the reported `worktreePath` for subsequent shell and fs calls. The pre-release-state choice is recorded in the [git-worktree-tools Agent Note](../../../.agents/notes/implemented/feature/2026-08-14-git-worktree-tools.md).
|
|
20
|
+
|
|
21
|
+
### `ExitWorktree`
|
|
22
|
+
|
|
23
|
+
Leaves the active EnterWorktree session and returns to the original directory.
|
|
24
|
+
|
|
25
|
+
| Arg | Type | Notes |
|
|
26
|
+
|---|---|---|
|
|
27
|
+
| `action` | `"keep"` \| `"remove"` | `keep` leaves the worktree and branch on disk; `remove` deletes both (destructive). |
|
|
28
|
+
| `discard_changes` | boolean | Required `true` with `action: "remove"` when the worktree has uncommitted files or commits not on the base branch; the tool refuses and lists the evidence otherwise. |
|
|
29
|
+
|
|
30
|
+
`ExitWorktree` only operates on worktrees created by `EnterWorktree` in the current session: it is a no-op otherwise and never touches manually-created or previous-session worktrees. Before a `remove` it probes `git status --porcelain` and `git rev-list --count <base>..HEAD` and **fails closed** — if the state cannot be verified it refuses without `discard_changes: true`, so a silent 0/0 can never destroy real work.
|
|
31
|
+
|
|
32
|
+
## Safety
|
|
33
|
+
|
|
34
|
+
- Both tools are `isConcurrencySafe = () => false`; they must not overlap other tools.
|
|
35
|
+
- `ExitWorktree` with `action: "remove"` is destructive (deletes the worktree directory and its branch); with uncommitted or unmerged work it requires the explicit `discard_changes: true` grant. dsh-tools has no dedicated `isDestructive` field, so destructiveness is expressed in the human-readable tool description and the `remove`-vs-`keep` call presentation.
|
|
36
|
+
- Every on-disk path is validated to stay inside the repository's `.claude/worktrees/` directory before any git command runs.
|
|
37
|
+
|
|
38
|
+
## Git command construction
|
|
39
|
+
|
|
40
|
+
All git commands are built in one module (`src/worktree.ts`) as `{ command, workdir, label }` opaque values that the tools hand to `ctx.shell`. This is the seam a future pure-JS git implementation would replace; tool code never interpolates git argument lists itself.
|
|
41
|
+
|
|
42
|
+
## UI presentation
|
|
43
|
+
|
|
44
|
+
`EnterWorktree` and `ExitWorktree` own their `presentCall`/`presentResult` render intent as generic cards. `presentCall` is a pure function of the arguments (an `EnterWorktree` call names the worktree; an `ExitWorktree` call distinguishes `remove` (destructive) from `keep` in its title and content). `presentResult` shows a plain message on success and fenced text on error; a `diff` card is not used because a worktree exit has no textual diff to render.
|
|
45
|
+
|
|
46
|
+
## Known limitations
|
|
47
|
+
|
|
48
|
+
- The session cwd is immutable, so the worktree switch is expressed through the declared cwd and the systemPrompt runtime context rather than a true per-session cwd mutation; subsequent shell/fs calls must pass `workdir`.
|
|
49
|
+
- The active worktree session is a process-wide singleton (mirroring the claude-code reference), so one process has at most one active worktree at a time.
|
package/README.zh.md
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# @dsh-cc/tool-git-worktree
|
|
2
|
+
|
|
3
|
+
[English](README.md) | 中文
|
|
4
|
+
|
|
5
|
+
模型侧 `EnterWorktree` / `ExitWorktree` 工具:在 `<仓库>/.claude/worktrees/` 下创建、保留、删除隔离的 git worktree。所有 git 命令都经 `ctx.shell` 执行器 seam 运行(先 resolve 再 run——绝不直接 spawn),并用 `ctx.fs` 校验每个要操作的路径都在仓库内。
|
|
6
|
+
|
|
7
|
+
需要加载 shell 执行器 Service Provider(例如 `@deepseek-ai/dsh-bash-local`)、文件系统 Provider(`@deepseek-ai/dsh-fs-local`)、`ctx.tools` 与 `ctx.systemPrompt`。在 `inject: ['tools', 'shell', 'systemPrompt', 'fs']` 满足之前,插件保持等待状态。
|
|
8
|
+
|
|
9
|
+
## 工具
|
|
10
|
+
|
|
11
|
+
### `EnterWorktree`
|
|
12
|
+
|
|
13
|
+
基于 HEAD 在新建的 `worktree-<name>` 分支上创建 worktree,并将会话切换到其中。
|
|
14
|
+
|
|
15
|
+
| 参数 | 类型 | 说明 |
|
|
16
|
+
|---|---|---|
|
|
17
|
+
| `name` | string | worktree slug。每个以 `/` 分隔的段只允许字母、数字、`.`、`_`、`-`;最长 64 字符。省略时自动生成 `形容词-名词-后缀` 随机 slug。 |
|
|
18
|
+
|
|
19
|
+
工具从调用方 agent 的会话 cwd 定位仓库根(`git rev-parse --show-toplevel`);不在 git 工作树内时返回结构化错误而不做任何更改。由于本 harness 中会话工作目录在创建时即固定,cwd 切换通过两种对不可变会话 cwd 安全的方式进行声明:工具结果与 `tool:worktree:cwd` systemPrompt 运行时上下文都会声明新的工作目录,并告知模型后续 shell/fs 调用需传入与返回的 `worktreePath` 相同的 `workdir`。pre-release 状态下的取舍记录在 [git-worktree-tools Agent Note](../../../.agents/notes/implemented/feature/2026-08-14-git-worktree-tools.md)。
|
|
20
|
+
|
|
21
|
+
### `ExitWorktree`
|
|
22
|
+
|
|
23
|
+
退出当前 EnterWorktree 会话并返回原目录。
|
|
24
|
+
|
|
25
|
+
| 参数 | 类型 | 说明 |
|
|
26
|
+
|---|---|---|
|
|
27
|
+
| `action` | `"keep"` \| `"remove"` | `keep` 在磁盘上保留 worktree 与分支;`remove` 删除两者(破坏性)。 |
|
|
28
|
+
| `discard_changes` | boolean | 当 `action: "remove"` 且 worktree 有未提交文件或不在基准分支上的提交时必须为 `true`;否则工具拒绝并列出证据。 |
|
|
29
|
+
|
|
30
|
+
`ExitWorktree` 只操作当前会话中由 `EnterWorktree` 创建的 worktree:否则为 no-op,绝不触碰手动创建或先前会话的 worktree。执行 `remove` 前会用 `git status --porcelain` 与 `git rev-list --count <base>..HEAD` 探测状态并**失败即关闭(fail closed)**——若状态无法核实,则没有 `discard_changes: true` 就拒绝,从而绝不让静默的 0/0 摧毁真实工作。
|
|
31
|
+
|
|
32
|
+
## 安全
|
|
33
|
+
|
|
34
|
+
- 两个工具都为 `isConcurrencySafe = () => false`;不得与其他工具重叠。
|
|
35
|
+
- `ExitWorktree` 且 `action: "remove"` 具有破坏性(删除 worktree 目录及其分支);存在未提交或未合并工作时需要显式 `discard_changes: true` 许可。dsh-tools 没有专门的 `isDestructive` 字段,因此破坏性体现在人读的工具描述以及 `remove` 与 `keep` 的调用呈现区别中。
|
|
36
|
+
- 每个磁盘路径在运行任何 git 命令前都会校验仍位于仓库的 `.claude/worktrees/` 目录内。
|
|
37
|
+
|
|
38
|
+
## Git 命令构造
|
|
39
|
+
|
|
40
|
+
所有 git 命令都在一个模块(`src/worktree.ts`)中构建为 `{ command, workdir, label }` 不透明值,交给 `ctx.shell`。这是未来替换为纯 JS git 实现的 seam;工具代码绝不自拼 git 参数列表。
|
|
41
|
+
|
|
42
|
+
## UI 呈现
|
|
43
|
+
|
|
44
|
+
`EnterWorktree` 与 `ExitWorktree` 拥有各自的 `presentCall`/`presentResult` 呈现意图,均为 generic 卡片。`presentCall` 是参数的纯函数(`EnterWorktree` 调用指明 worktree 名;`ExitWorktree` 调用在标题与内容中区分 `remove`(破坏性)与 `keep`)。`presentResult` 成功时显示普通消息、出错时显示围栏文本;不使用 `diff` 卡片,因为退出 worktree 没有可渲染的文本差异。
|
|
45
|
+
|
|
46
|
+
## 已知限制
|
|
47
|
+
|
|
48
|
+
- 会话 cwd 不可变,因此 worktree 切换通过声明的 cwd 与 systemPrompt 运行时上下文表达,而非真正的按会话 cwd 修改;后续 shell/fs 调用必须传入 `workdir`。
|
|
49
|
+
- 活跃 worktree 会话是进程级单例(对应 claude-code 参考实现),因此一个进程任一时刻最多只有一个活跃 worktree。
|
package/lib/index.d.ts
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Model-facing `EnterWorktree` / `ExitWorktree` tools over the `ctx.shell`
|
|
3
|
+
* executor seam. EnterWorktree creates an isolated git worktree under
|
|
4
|
+
* `<repo>/.claude/worktrees/` and declares the cwd switch; ExitWorktree keeps
|
|
5
|
+
* or removes it after a fail-closed safety gate. Software is `git` today, but
|
|
6
|
+
* every command is constructed in one module so a pure-JS git backend can
|
|
7
|
+
* replace it later.
|
|
8
|
+
* @module @dsh-cc/tool-git-worktree
|
|
9
|
+
*/
|
|
10
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
11
|
+
import z from '@deepseek-ai/schemastery';
|
|
12
|
+
export declare const name = "tool-git-worktree";
|
|
13
|
+
export declare const inject: string[];
|
|
14
|
+
/** Runtime configuration for the git-worktree tools. */
|
|
15
|
+
export interface Config {
|
|
16
|
+
/** Whether `EnterWorktree` may create worktrees (default true). */
|
|
17
|
+
enableEnterWorktree?: boolean;
|
|
18
|
+
/** Whether `ExitWorktree` may remove worktrees (default true). */
|
|
19
|
+
enableExitWorktree?: boolean;
|
|
20
|
+
}
|
|
21
|
+
/** Runtime configuration schema. */
|
|
22
|
+
export declare const Config: z<Config>;
|
|
23
|
+
export declare function apply(ctx: Context, config?: Config): void;
|
|
24
|
+
export { validateSlug, worktreeBranch, worktreePathFor, randomSlug, flattenSlug } from './worktree.ts';
|
|
25
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAClD,OAAO,CAAC,MAAM,0BAA0B,CAAA;AA4BxC,eAAO,MAAM,IAAI,sBAAsB,CAAA;AACvC,eAAO,MAAM,MAAM,UAA2C,CAAA;AAE9D,wDAAwD;AACxD,MAAM,WAAW,MAAM;IACrB,mEAAmE;IACnE,mBAAmB,CAAC,EAAE,OAAO,CAAA;IAC7B,kEAAkE;IAClE,kBAAkB,CAAC,EAAE,OAAO,CAAA;CAC7B;AAED,oCAAoC;AACpC,eAAO,MAAM,MAAM,EAAE,CAAC,CAAC,MAAM,CAG3B,CAAA;AA6JF,wBAAgB,KAAK,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,GAAE,MAAW,GAAG,IAAI,CA+L7D;AAED,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,eAAe,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,eAAe,CAAA"}
|
package/lib/index.js
ADDED
|
@@ -0,0 +1,330 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Model-facing `EnterWorktree` / `ExitWorktree` tools over the `ctx.shell`
|
|
3
|
+
* executor seam. EnterWorktree creates an isolated git worktree under
|
|
4
|
+
* `<repo>/.claude/worktrees/` and declares the cwd switch; ExitWorktree keeps
|
|
5
|
+
* or removes it after a fail-closed safety gate. Software is `git` today, but
|
|
6
|
+
* every command is constructed in one module so a pure-JS git backend can
|
|
7
|
+
* replace it later.
|
|
8
|
+
* @module @dsh-cc/tool-git-worktree
|
|
9
|
+
*/
|
|
10
|
+
import z from '@deepseek-ai/schemastery';
|
|
11
|
+
import { relative, sep } from 'node:path';
|
|
12
|
+
import { defineTool, TOOL_ABORTED } from '@dsh-cc/tools';
|
|
13
|
+
import { HarnessError } from '@deepseek-ai/dsh-llm';
|
|
14
|
+
import { addWorktree, clearActiveWorktreeSession, commitsAhead, deleteBranch, forceRemoveWorktree, getActiveWorktreeSession, randomSlug, repoLabel, setActiveWorktreeSession, status, validateSlug, worktreeBranch, worktreePathFor, } from "./worktree.js";
|
|
15
|
+
import { presentEnterCall, presentWorktreeResult, presentExitCall } from "./render.js";
|
|
16
|
+
import { setSessionCwd } from '@dsh-cc/session-cwd';
|
|
17
|
+
export const name = 'tool-git-worktree';
|
|
18
|
+
export const inject = ['tools', 'shell', 'systemPrompt', 'fs'];
|
|
19
|
+
/** Runtime configuration schema. */
|
|
20
|
+
export const Config = z.object({
|
|
21
|
+
enableEnterWorktree: z.boolean().default(true),
|
|
22
|
+
enableExitWorktree: z.boolean().default(true),
|
|
23
|
+
});
|
|
24
|
+
/** A structured, model-visible failure (maps to an isError tool result). */
|
|
25
|
+
class WorktreeError extends Error {
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Resolve the canonical session working directory this tool operates in.
|
|
29
|
+
* Follows the bash/fs convention of reading the agent's durable session cwd,
|
|
30
|
+
* falling back to the process working directory.
|
|
31
|
+
* @param exec - the running tool call.
|
|
32
|
+
* @returns the absolute cwd, or `undefined` when none is known.
|
|
33
|
+
*/
|
|
34
|
+
function sessionCwd(exec) {
|
|
35
|
+
return exec.agent?.session.header.cwd;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Propagate a cwd change into the session-cwd plugin (WS1): a durable
|
|
39
|
+
* `worktree/entered` event plus the live overlay. Tolerant of test fakes and
|
|
40
|
+
* headless contexts whose session face lacks the append seam — the tool must
|
|
41
|
+
* not fail because a non-persistent session cannot record the move.
|
|
42
|
+
* @param exec - the running tool call.
|
|
43
|
+
* @param path - the new absolute session working directory.
|
|
44
|
+
*/
|
|
45
|
+
function updateSessionCwd(exec, path) {
|
|
46
|
+
const agent = exec.agent;
|
|
47
|
+
if (agent === undefined)
|
|
48
|
+
return;
|
|
49
|
+
const session = agent.session;
|
|
50
|
+
if (session === undefined || typeof session.append !== 'function')
|
|
51
|
+
return;
|
|
52
|
+
// Fail-soft: a session that cannot persist the event still completes the
|
|
53
|
+
// worktree operation; the worktree session singleton remains authoritative.
|
|
54
|
+
try {
|
|
55
|
+
setSessionCwd(agent, path);
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
// cwd bookkeeping is best-effort here.
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Run one git command to completion through the `ctx.shell` seam. Resolves a
|
|
63
|
+
* fresh request (never passing an unresolved one to `run`) and maps abort /
|
|
64
|
+
* spawn failures to a structured {@link HarnessError}; a nonzero git exit
|
|
65
|
+
* resolves normally for the caller to interpret.
|
|
66
|
+
* @param ctx - the Cordis context.
|
|
67
|
+
* @param cmd - the constructed git command.
|
|
68
|
+
* @param signal - the tool-call cancellation signal.
|
|
69
|
+
* @returns the shell result, with `exitCode` nonzero representing a git-level failure.
|
|
70
|
+
*/
|
|
71
|
+
async function runGit(ctx, cmd, signal) {
|
|
72
|
+
const result = await ctx.shell.run(ctx.shell.resolve({
|
|
73
|
+
command: cmd.command,
|
|
74
|
+
workdir: cmd.workdir,
|
|
75
|
+
signal,
|
|
76
|
+
}));
|
|
77
|
+
if (result.aborted) {
|
|
78
|
+
const error = new HarnessError('tool call aborted', TOOL_ABORTED);
|
|
79
|
+
error.name = 'AbortError';
|
|
80
|
+
throw error;
|
|
81
|
+
}
|
|
82
|
+
return result;
|
|
83
|
+
}
|
|
84
|
+
/** Truncated stderr tail used to surface git failure causes in messages. */
|
|
85
|
+
function gitFailure(result) {
|
|
86
|
+
return result.stderr.text.trim() || result.stdout.text.trim() || `exit code ${result.exitCode}`;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Assert a computed worktree path is contained by this repo's `worktrees`
|
|
90
|
+
* directory. All paths a tool will act on are validated here before any git
|
|
91
|
+
* command runs.
|
|
92
|
+
* @param ctx - the Cordis context.
|
|
93
|
+
* @param repoRoot - the canonical repository root.
|
|
94
|
+
* @param worktreePath - candidate absolute worktree path.
|
|
95
|
+
*/
|
|
96
|
+
async function assertPathInRepo(ctx, repoRoot, worktreePath) {
|
|
97
|
+
if (relative(repoRoot, worktreePath).startsWith(`..${sep}`)) {
|
|
98
|
+
throw new WorktreeError(`refusing worktree path outside the repository: "${worktreePath}"`);
|
|
99
|
+
}
|
|
100
|
+
const rootTarget = await ctx.fs.resolve(repoRoot);
|
|
101
|
+
const pathTarget = await ctx.fs.resolve(worktreePath);
|
|
102
|
+
if (!ctx.fs.contains(rootTarget, pathTarget)) {
|
|
103
|
+
throw new WorktreeError(`refusing worktree path outside the repository: "${worktreePath}"`);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Find the canonical repository root for a working directory using git. A
|
|
108
|
+
* nonzero exit means the cwd is not inside a git working tree.
|
|
109
|
+
* @param ctx - the Cordis context.
|
|
110
|
+
* @param cwd - the working directory to search from.
|
|
111
|
+
* @param signal - the tool-call cancellation signal.
|
|
112
|
+
* @returns the canonical root, or `undefined` when not a git repository.
|
|
113
|
+
*/
|
|
114
|
+
async function findRepoRoot(ctx, cwd, signal) {
|
|
115
|
+
const result = await runGit(ctx, { command: 'git rev-parse --show-toplevel', workdir: cwd, label: 'locate repository root' }, signal);
|
|
116
|
+
const root = result.stdout.text.trim();
|
|
117
|
+
return result.exitCode === 0 && root.length > 0 ? root : undefined;
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Probe whether the (remove-gate) worktree currently differs from the commit it
|
|
121
|
+
* was created from, counting both uncommitted files and new commits. Returns
|
|
122
|
+
* `null` when the state cannot be determined reliably — callers treat that as
|
|
123
|
+
* "unknown, assume unsafe" (fail-closed) so a silent 0/0 can never let a remove
|
|
124
|
+
* destroy real work.
|
|
125
|
+
* @param ctx - the Cordis context.
|
|
126
|
+
* @param session - the active worktree session.
|
|
127
|
+
* @returns the change counts, or `null` when unknown.
|
|
128
|
+
*/
|
|
129
|
+
async function countWorktreeChanges(ctx, session, signal) {
|
|
130
|
+
const statusResult = await runGit(ctx, status(session.worktreePath), signal);
|
|
131
|
+
if (statusResult.exitCode !== 0)
|
|
132
|
+
return null;
|
|
133
|
+
const changedFiles = statusResult.stdout.text.split('\n').filter(line => line.trim() !== '').length;
|
|
134
|
+
const revResult = await runGit(ctx, commitsAhead(session.worktreePath, session.originalHead), signal);
|
|
135
|
+
if (revResult.exitCode !== 0)
|
|
136
|
+
return null;
|
|
137
|
+
const commits = parseInt(revResult.stdout.text.trim(), 10) || 0;
|
|
138
|
+
return { changedFiles, commits };
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Register the runtime-context entry that surfaces the active worktree cwd to
|
|
142
|
+
* the model. When no worktree is active the provider contributes nothing, so
|
|
143
|
+
* the entry is inert outside an EnterWorktree session.
|
|
144
|
+
* @param ctx - the Cordis context.
|
|
145
|
+
*/
|
|
146
|
+
function registerWorktreeCwdContext(ctx) {
|
|
147
|
+
ctx.systemPrompt.context({
|
|
148
|
+
name: 'tool:worktree:cwd',
|
|
149
|
+
order: 120,
|
|
150
|
+
text: () => {
|
|
151
|
+
const session = getActiveWorktreeSession();
|
|
152
|
+
if (session === null)
|
|
153
|
+
return '';
|
|
154
|
+
return `Current working directory: ${session.worktreePath}`;
|
|
155
|
+
},
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
export function apply(ctx, config = {}) {
|
|
159
|
+
registerWorktreeCwdContext(ctx);
|
|
160
|
+
if (config.enableEnterWorktree ?? true) {
|
|
161
|
+
ctx.tools.register(defineTool({
|
|
162
|
+
name: 'EnterWorktree',
|
|
163
|
+
description: 'Creates an isolated git worktree under <repo>/.claude/worktrees/ and switches the session into it. '
|
|
164
|
+
+ 'Run an uncommitted or speculative change in the worktree without touching the main working tree. '
|
|
165
|
+
+ 'Because the session working directory is fixed at creation, subsequent shell and fs calls should pass '
|
|
166
|
+
+ '`workdir` equal to the reported worktreePath to operate inside it; the runtime context and this result '
|
|
167
|
+
+ 'both declare the current working directory. This tool is NOT concurrency-safe and must not overlap '
|
|
168
|
+
+ 'other tools. Only call it when the user explicitly asks to work in a worktree.',
|
|
169
|
+
parameters: {
|
|
170
|
+
name: {
|
|
171
|
+
type: 'string',
|
|
172
|
+
description: 'Optional name for the worktree. Each "/"-separated segment may contain only letters, digits, dots, underscores, and dashes; max 64 chars total. A random name is generated if not provided.',
|
|
173
|
+
},
|
|
174
|
+
},
|
|
175
|
+
output: {
|
|
176
|
+
schema: {
|
|
177
|
+
type: 'object',
|
|
178
|
+
additionalProperties: false,
|
|
179
|
+
properties: {
|
|
180
|
+
worktreePath: { type: 'string', required: true },
|
|
181
|
+
worktreeBranch: { type: 'string', required: true },
|
|
182
|
+
message: { type: 'string', required: true },
|
|
183
|
+
},
|
|
184
|
+
},
|
|
185
|
+
render: (_args, value) => [{ type: 'text', text: value.message }],
|
|
186
|
+
},
|
|
187
|
+
isConcurrencySafe: () => false,
|
|
188
|
+
presentCall: presentEnterCall,
|
|
189
|
+
presentResult: presentWorktreeResult,
|
|
190
|
+
async execute(args, exec) {
|
|
191
|
+
const cwd = sessionCwd(exec) ?? process.cwd();
|
|
192
|
+
const repoRoot = await findRepoRoot(ctx, cwd, exec.signal);
|
|
193
|
+
if (repoRoot === undefined) {
|
|
194
|
+
throw new WorktreeError(`cannot create a worktree: not in a git repository (cwd: ${cwd}). EnterWorktree requires a git working tree.`);
|
|
195
|
+
}
|
|
196
|
+
const slug = args.name ?? randomSlug();
|
|
197
|
+
validateSlug(slug);
|
|
198
|
+
const worktreePath = worktreePathFor(repoRoot, slug);
|
|
199
|
+
await assertPathInRepo(ctx, repoRoot, worktreePath);
|
|
200
|
+
const branch = worktreeBranch(slug);
|
|
201
|
+
const headResult = await runGit(ctx, { command: 'git rev-parse HEAD', workdir: repoRoot, label: 'resolve HEAD' }, exec.signal);
|
|
202
|
+
if (headResult.exitCode !== 0) {
|
|
203
|
+
throw new WorktreeError(`cannot resolve repository HEAD at ${repoRoot}: ${gitFailure(headResult)}`);
|
|
204
|
+
}
|
|
205
|
+
const originalHead = headResult.stdout.text.trim();
|
|
206
|
+
const create = await runGit(ctx, addWorktree(repoRoot, slug), exec.signal);
|
|
207
|
+
if (create.exitCode !== 0) {
|
|
208
|
+
throw new WorktreeError(`failed to create worktree: ${gitFailure(create)}`);
|
|
209
|
+
}
|
|
210
|
+
setActiveWorktreeSession({ originalCwd: cwd, repoRoot, worktreePath, worktreeBranch: branch, originalHead });
|
|
211
|
+
updateSessionCwd(exec, worktreePath);
|
|
212
|
+
return {
|
|
213
|
+
worktreePath,
|
|
214
|
+
worktreeBranch: branch,
|
|
215
|
+
message: `Created worktree at ${worktreePath} on branch ${branch} in ${repoLabel(repoRoot)}. `
|
|
216
|
+
+ `The session's working directory is now the worktree; pass \`workdir: ${worktreePath}\` to shell and fs calls. `
|
|
217
|
+
+ 'Use ExitWorktree to leave it (keep or remove).',
|
|
218
|
+
};
|
|
219
|
+
},
|
|
220
|
+
}));
|
|
221
|
+
}
|
|
222
|
+
if (config.enableExitWorktree ?? true) {
|
|
223
|
+
ctx.tools.register(defineTool({
|
|
224
|
+
name: 'ExitWorktree',
|
|
225
|
+
description: 'Leaves a worktree session created by EnterWorktree and returns the session to its original directory. '
|
|
226
|
+
+ 'This tool ONLY operates on worktrees created by EnterWorktree in this session; it never touches '
|
|
227
|
+
+ 'manually-created worktrees or worktrees from a previous session, and is a no-op when EnterWorktree '
|
|
228
|
+
+ 'was never called. action "remove" deletes the worktree directory AND its branch (DESTRUCTIVE, permanent): '
|
|
229
|
+
+ 'it refuses unless discard_changes is true when the worktree has uncommitted files or new commits, and '
|
|
230
|
+
+ 'lists the evidence otherwise. action "keep" leaves the worktree and branch on disk untouched. '
|
|
231
|
+
+ 'This tool is NOT concurrency-safe and must not overlap other tools.',
|
|
232
|
+
parameters: {
|
|
233
|
+
action: {
|
|
234
|
+
type: 'string',
|
|
235
|
+
enum: ['keep', 'remove'],
|
|
236
|
+
description: '"keep" leaves the worktree and branch intact on disk; "remove" deletes both (destructive).',
|
|
237
|
+
},
|
|
238
|
+
discard_changes: {
|
|
239
|
+
type: 'boolean',
|
|
240
|
+
description: 'Required true when action is "remove" and the worktree has uncommitted files or unmerged commits. The tool refuses and lists them otherwise.',
|
|
241
|
+
},
|
|
242
|
+
},
|
|
243
|
+
output: {
|
|
244
|
+
schema: {
|
|
245
|
+
type: 'object',
|
|
246
|
+
additionalProperties: false,
|
|
247
|
+
properties: {
|
|
248
|
+
action: { type: 'string', required: true },
|
|
249
|
+
originalCwd: { type: 'string', required: true },
|
|
250
|
+
worktreePath: { type: 'string', required: true },
|
|
251
|
+
worktreeBranch: { type: 'string', required: true },
|
|
252
|
+
discardedFiles: { type: 'integer' },
|
|
253
|
+
discardedCommits: { type: 'integer' },
|
|
254
|
+
message: { type: 'string', required: true },
|
|
255
|
+
},
|
|
256
|
+
},
|
|
257
|
+
render: (_args, value) => [{ type: 'text', text: value.message }],
|
|
258
|
+
},
|
|
259
|
+
isConcurrencySafe: () => false,
|
|
260
|
+
presentCall: presentExitCall,
|
|
261
|
+
presentResult: presentWorktreeResult,
|
|
262
|
+
async execute(args, exec) {
|
|
263
|
+
const session = getActiveWorktreeSession();
|
|
264
|
+
if (session === null) {
|
|
265
|
+
throw new WorktreeError('No-op: there is no active EnterWorktree session to exit. This tool only operates on worktrees '
|
|
266
|
+
+ 'created by EnterWorktree in the current session — it will not touch manually-created worktrees '
|
|
267
|
+
+ 'or worktrees from a previous session. No filesystem changes were made.');
|
|
268
|
+
}
|
|
269
|
+
if (args.action === 'keep') {
|
|
270
|
+
clearActiveWorktreeSession();
|
|
271
|
+
updateSessionCwd(exec, session.originalCwd);
|
|
272
|
+
return {
|
|
273
|
+
action: 'keep',
|
|
274
|
+
originalCwd: session.originalCwd,
|
|
275
|
+
worktreePath: session.worktreePath,
|
|
276
|
+
worktreeBranch: session.worktreeBranch,
|
|
277
|
+
message: `Exited worktree. Your work is preserved at ${session.worktreePath} on branch `
|
|
278
|
+
+ `${session.worktreeBranch}. Session is now back in ${session.originalCwd}.`,
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
// action === 'remove': gate on the safety probe.
|
|
282
|
+
const summary = await countWorktreeChanges(ctx, session, exec.signal);
|
|
283
|
+
if (summary === null) {
|
|
284
|
+
throw new WorktreeError(`Could not verify worktree state at ${session.worktreePath}. Refusing to remove without explicit `
|
|
285
|
+
+ 'confirmation. Re-invoke with discard_changes: true to proceed, or use action: "keep" to preserve it.');
|
|
286
|
+
}
|
|
287
|
+
const { changedFiles, commits } = summary;
|
|
288
|
+
if (!args.discard_changes && (changedFiles > 0 || commits > 0)) {
|
|
289
|
+
const parts = [];
|
|
290
|
+
if (changedFiles > 0)
|
|
291
|
+
parts.push(`${changedFiles} uncommitted ${changedFiles === 1 ? 'file' : 'files'}`);
|
|
292
|
+
if (commits > 0)
|
|
293
|
+
parts.push(`${commits} ${commits === 1 ? 'commit' : 'commits'} on ${session.worktreeBranch}`);
|
|
294
|
+
throw new WorktreeError(`Worktree has ${parts.join(' and ')}. Removing will discard this work permanently. Confirm with '
|
|
295
|
+
+ 'the user, then re-invoke with discard_changes: true — or use action: "keep" to preserve the worktree.`);
|
|
296
|
+
}
|
|
297
|
+
await assertPathInRepo(ctx, session.repoRoot, session.worktreePath);
|
|
298
|
+
const removed = await runGit(ctx, forceRemoveWorktree(session.repoRoot, session.worktreePath), exec.signal);
|
|
299
|
+
if (removed.exitCode !== 0) {
|
|
300
|
+
throw new WorktreeError(`failed to remove worktree: ${gitFailure(removed)}`);
|
|
301
|
+
}
|
|
302
|
+
const branchDeleted = await runGit(ctx, deleteBranch(session.repoRoot, session.worktreeBranch), exec.signal);
|
|
303
|
+
if (branchDeleted.exitCode !== 0) {
|
|
304
|
+
// The worktree directory is gone; a surviving branch is a lint residue, not a locked failure.
|
|
305
|
+
ctx.logger.warn(`could not delete worktree branch ${session.worktreeBranch}: ${gitFailure(branchDeleted)}`);
|
|
306
|
+
}
|
|
307
|
+
clearActiveWorktreeSession();
|
|
308
|
+
updateSessionCwd(exec, session.originalCwd);
|
|
309
|
+
const discardParts = [];
|
|
310
|
+
if (commits > 0)
|
|
311
|
+
discardParts.push(`${commits} ${commits === 1 ? 'commit' : 'commits'}`);
|
|
312
|
+
if (changedFiles > 0)
|
|
313
|
+
discardParts.push(`${changedFiles} uncommitted ${changedFiles === 1 ? 'file' : 'files'}`);
|
|
314
|
+
const discardNote = discardParts.length > 0 ? ` Discarded ${discardParts.join(' and ')}.` : '';
|
|
315
|
+
return {
|
|
316
|
+
action: 'remove',
|
|
317
|
+
originalCwd: session.originalCwd,
|
|
318
|
+
worktreePath: session.worktreePath,
|
|
319
|
+
worktreeBranch: session.worktreeBranch,
|
|
320
|
+
discardedFiles: changedFiles,
|
|
321
|
+
discardedCommits: commits,
|
|
322
|
+
message: `Exited and removed worktree at ${session.worktreePath}.${discardNote} `
|
|
323
|
+
+ `Session is now back in ${session.originalCwd}.`,
|
|
324
|
+
};
|
|
325
|
+
},
|
|
326
|
+
}));
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
export { validateSlug, worktreeBranch, worktreePathFor, randomSlug, flattenSlug } from "./worktree.js";
|
|
330
|
+
//# sourceMappingURL=index.js.map
|
package/lib/index.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,OAAO,CAAC,MAAM,0BAA0B,CAAA;AACxC,OAAO,EAAE,QAAQ,EAAE,GAAG,EAAE,MAAM,WAAW,CAAA;AACzC,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AAExD,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAA;AAKnD,OAAO,EACL,WAAW,EACX,0BAA0B,EAC1B,YAAY,EACZ,YAAY,EACZ,mBAAmB,EACnB,wBAAwB,EACxB,UAAU,EACV,SAAS,EACT,wBAAwB,EACxB,MAAM,EACN,YAAY,EACZ,cAAc,EACd,eAAe,GAChB,MAAM,eAAe,CAAA;AAEtB,OAAO,EAAE,gBAAgB,EAAE,qBAAqB,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA;AACtF,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAA;AAEnD,MAAM,CAAC,MAAM,IAAI,GAAG,mBAAmB,CAAA;AACvC,MAAM,CAAC,MAAM,MAAM,GAAG,CAAC,OAAO,EAAE,OAAO,EAAE,cAAc,EAAE,IAAI,CAAC,CAAA;AAU9D,oCAAoC;AACpC,MAAM,CAAC,MAAM,MAAM,GAAc,CAAC,CAAC,MAAM,CAAC;IACxC,mBAAmB,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC;IAC9C,kBAAkB,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC;CAC9C,CAAC,CAAA;AAaF,4EAA4E;AAC5E,MAAM,aAAc,SAAQ,KAAK;CAAG;AAEpC;;;;;;GAMG;AACH,SAAS,UAAU,CAAC,IAAoB;IACtC,OAAO,IAAI,CAAC,KAAK,EAAE,OAAO,CAAC,MAAM,CAAC,GAAG,CAAA;AACvC,CAAC;AAED;;;;;;;GAOG;AACH,SAAS,gBAAgB,CAAC,IAAoB,EAAE,IAAY;IAC1D,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAA;IACxB,IAAI,KAAK,KAAK,SAAS;QAAE,OAAM;IAC/B,MAAM,OAAO,GAAG,KAAK,CAAC,OAAsD,CAAA;IAC5E,IAAI,OAAO,KAAK,SAAS,IAAI,OAAO,OAAO,CAAC,MAAM,KAAK,UAAU;QAAE,OAAM;IACzE,yEAAyE;IACzE,4EAA4E;IAC5E,IAAI,CAAC;QACH,aAAa,CAAC,KAAK,EAAE,IAAI,CAAC,CAAA;IAC5B,CAAC;IAAC,MAAM,CAAC;QACP,uCAAuC;IACzC,CAAC;AACH,CAAC;AAED;;;;;;;;;GASG;AACH,KAAK,UAAU,MAAM,CACnB,GAAY,EACZ,GAAW,EACX,MAAmB;IAEnB,MAAM,MAAM,GAAG,MAAM,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC;QACnD,OAAO,EAAE,GAAG,CAAC,OAAO;QACpB,OAAO,EAAE,GAAG,CAAC,OAAO;QACpB,MAAM;KACP,CAAC,CAAC,CAAA;IACH,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;QACnB,MAAM,KAAK,GAAG,IAAI,YAAY,CAAC,mBAAmB,EAAE,YAAY,CAAC,CAAA;QACjE,KAAK,CAAC,IAAI,GAAG,YAAY,CAAA;QACzB,MAAM,KAAK,CAAA;IACb,CAAC;IACD,OAAO,MAAM,CAAA;AACf,CAAC;AAED,4EAA4E;AAC5E,SAAS,UAAU,CAAC,MAAsB;IACxC,OAAO,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,aAAa,MAAM,CAAC,QAAQ,EAAE,CAAA;AACjG,CAAC;AAED;;;;;;;GAOG;AACH,KAAK,UAAU,gBAAgB,CAAC,GAAY,EAAE,QAAgB,EAAE,YAAoB;IAClF,IAAI,QAAQ,CAAC,QAAQ,EAAE,YAAY,CAAC,CAAC,UAAU,CAAC,KAAK,GAAG,EAAE,CAAC,EAAE,CAAC;QAC5D,MAAM,IAAI,aAAa,CAAC,mDAAmD,YAAY,GAAG,CAAC,CAAA;IAC7F,CAAC;IACD,MAAM,UAAU,GAAG,MAAM,GAAG,CAAC,EAAE,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAA;IACjD,MAAM,UAAU,GAAG,MAAM,GAAG,CAAC,EAAE,CAAC,OAAO,CAAC,YAAY,CAAC,CAAA;IACrD,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,QAAQ,CAAC,UAAU,EAAE,UAAU,CAAC,EAAE,CAAC;QAC7C,MAAM,IAAI,aAAa,CAAC,mDAAmD,YAAY,GAAG,CAAC,CAAA;IAC7F,CAAC;AACH,CAAC;AAED;;;;;;;GAOG;AACH,KAAK,UAAU,YAAY,CAAC,GAAY,EAAE,GAAW,EAAE,MAAmB;IACxE,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,GAAG,EAAE,EAAE,OAAO,EAAE,+BAA+B,EAAE,OAAO,EAAE,GAAG,EAAE,KAAK,EAAE,wBAAwB,EAAE,EAAE,MAAM,CAAC,CAAA;IACrI,MAAM,IAAI,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,CAAA;IACtC,OAAO,MAAM,CAAC,QAAQ,KAAK,CAAC,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAA;AACpE,CAAC;AAED;;;;;;;;;GASG;AACH,KAAK,UAAU,oBAAoB,CACjC,GAAY,EACZ,OAAwB,EACxB,MAAmB;IAEnB,MAAM,YAAY,GAAG,MAAM,MAAM,CAAC,GAAG,EAAE,MAAM,CAAC,OAAO,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC,CAAA;IAC5E,IAAI,YAAY,CAAC,QAAQ,KAAK,CAAC;QAAE,OAAO,IAAI,CAAA;IAC5C,MAAM,YAAY,GAAG,YAAY,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC,MAAM,CAAA;IACnG,MAAM,SAAS,GAAG,MAAM,MAAM,CAAC,GAAG,EAAE,YAAY,CAAC,OAAO,CAAC,YAAY,EAAE,OAAO,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC,CAAA;IACrG,IAAI,SAAS,CAAC,QAAQ,KAAK,CAAC;QAAE,OAAO,IAAI,CAAA;IACzC,MAAM,OAAO,GAAG,QAAQ,CAAC,SAAS,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,EAAE,CAAC,IAAI,CAAC,CAAA;IAC/D,OAAO,EAAE,YAAY,EAAE,OAAO,EAAE,CAAA;AAClC,CAAC;AAED;;;;;GAKG;AACH,SAAS,0BAA0B,CAAC,GAAY;IAC9C,GAAG,CAAC,YAAY,CAAC,OAAO,CAAC;QACvB,IAAI,EAAE,mBAAmB;QACzB,KAAK,EAAE,GAAG;QACV,IAAI,EAAE,GAAG,EAAE;YACT,MAAM,OAAO,GAAG,wBAAwB,EAAE,CAAA;YAC1C,IAAI,OAAO,KAAK,IAAI;gBAAE,OAAO,EAAE,CAAA;YAC/B,OAAO,8BAA8B,OAAO,CAAC,YAAY,EAAE,CAAA;QAC7D,CAAC;KACF,CAAC,CAAA;AACJ,CAAC;AAED,MAAM,UAAU,KAAK,CAAC,GAAY,EAAE,SAAiB,EAAE;IACrD,0BAA0B,CAAC,GAAG,CAAC,CAAA;IAE/B,IAAI,MAAM,CAAC,mBAAmB,IAAI,IAAI,EAAE,CAAC;QACvC,GAAG,CAAC,KAAK,CAAC,QAAQ,CAAC,UAAU,CAAC;YAC5B,IAAI,EAAE,eAAe;YACrB,WAAW,EACT,qGAAqG;kBACnG,mGAAmG;kBACnG,wGAAwG;kBACxG,yGAAyG;kBACzG,qGAAqG;kBACrG,gFAAgF;YACpF,UAAU,EAAE;gBACV,IAAI,EAAE;oBACJ,IAAI,EAAE,QAAQ;oBACd,WAAW,EAAE,6LAA6L;iBAC3M;aACF;YACD,MAAM,EAAE;gBACN,MAAM,EAAE;oBACN,IAAI,EAAE,QAAQ;oBACd,oBAAoB,EAAE,KAAK;oBAC3B,UAAU,EAAE;wBACV,YAAY,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE;wBAChD,cAAc,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE;wBAClD,OAAO,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE;qBAC5C;iBACF;gBACD,MAAM,EAAE,CAAC,KAAwB,EAAE,KAAwE,EAAE,EAAE,CAC7G,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,CAAC,OAAO,EAAE,CAAC;aAC1C;YACD,iBAAiB,EAAE,GAAG,EAAE,CAAC,KAAK;YAC9B,WAAW,EAAE,gBAAgB;YAC7B,aAAa,EAAE,qBAAqB;YACpC,KAAK,CAAC,OAAO,CAAC,IAAuB,EAAE,IAAI;gBACzC,MAAM,GAAG,GAAG,UAAU,CAAC,IAAI,CAAC,IAAI,OAAO,CAAC,GAAG,EAAE,CAAA;gBAC7C,MAAM,QAAQ,GAAG,MAAM,YAAY,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,CAAC,MAAM,CAAC,CAAA;gBAC1D,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;oBAC3B,MAAM,IAAI,aAAa,CACrB,2DAA2D,GAAG,+CAA+C,CAC9G,CAAA;gBACH,CAAC;gBACD,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,IAAI,UAAU,EAAE,CAAA;gBACtC,YAAY,CAAC,IAAI,CAAC,CAAA;gBAElB,MAAM,YAAY,GAAG,eAAe,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAA;gBACpD,MAAM,gBAAgB,CAAC,GAAG,EAAE,QAAQ,EAAE,YAAY,CAAC,CAAA;gBAEnD,MAAM,MAAM,GAAG,cAAc,CAAC,IAAI,CAAC,CAAA;gBACnC,MAAM,UAAU,GAAG,MAAM,MAAM,CAAC,GAAG,EAAE,EAAE,OAAO,EAAE,oBAAoB,EAAE,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,cAAc,EAAE,EAAE,IAAI,CAAC,MAAM,CAAC,CAAA;gBAC9H,IAAI,UAAU,CAAC,QAAQ,KAAK,CAAC,EAAE,CAAC;oBAC9B,MAAM,IAAI,aAAa,CAAC,qCAAqC,QAAQ,KAAK,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC,CAAA;gBACrG,CAAC;gBACD,MAAM,YAAY,GAAG,UAAU,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,CAAA;gBAElD,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,GAAG,EAAE,WAAW,CAAC,QAAQ,EAAE,IAAI,CAAC,EAAE,IAAI,CAAC,MAAM,CAAC,CAAA;gBAC1E,IAAI,MAAM,CAAC,QAAQ,KAAK,CAAC,EAAE,CAAC;oBAC1B,MAAM,IAAI,aAAa,CAAC,8BAA8B,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC,CAAA;gBAC7E,CAAC;gBAED,wBAAwB,CAAC,EAAE,WAAW,EAAE,GAAG,EAAE,QAAQ,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,EAAE,YAAY,EAAE,CAAC,CAAA;gBAC5G,gBAAgB,CAAC,IAAI,EAAE,YAAY,CAAC,CAAA;gBAEpC,OAAO;oBACL,YAAY;oBACZ,cAAc,EAAE,MAAM;oBACtB,OAAO,EACL,uBAAuB,YAAY,cAAc,MAAM,OAAO,SAAS,CAAC,QAAQ,CAAC,IAAI;0BACnF,wEAAwE,YAAY,4BAA4B;0BAChH,gDAAgD;iBACrD,CAAA;YACH,CAAC;SACF,CAAC,CAAC,CAAA;IACL,CAAC;IAED,IAAI,MAAM,CAAC,kBAAkB,IAAI,IAAI,EAAE,CAAC;QACtC,GAAG,CAAC,KAAK,CAAC,QAAQ,CAAC,UAAU,CAAC;YAC5B,IAAI,EAAE,cAAc;YACpB,WAAW,EACT,wGAAwG;kBACtG,kGAAkG;kBAClG,qGAAqG;kBACrG,4GAA4G;kBAC5G,wGAAwG;kBACxG,gGAAgG;kBAChG,qEAAqE;YACzE,UAAU,EAAE;gBACV,MAAM,EAAE;oBACN,IAAI,EAAE,QAAQ;oBACd,IAAI,EAAE,CAAC,MAAM,EAAE,QAAQ,CAAC;oBACxB,WAAW,EAAE,4FAA4F;iBAC1G;gBACD,eAAe,EAAE;oBACf,IAAI,EAAE,SAAS;oBACf,WAAW,EAAE,8IAA8I;iBAC5J;aACF;YACD,MAAM,EAAE;gBACN,MAAM,EAAE;oBACN,IAAI,EAAE,QAAQ;oBACd,oBAAoB,EAAE,KAAK;oBAC3B,UAAU,EAAE;wBACV,MAAM,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE;wBAC1C,WAAW,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE;wBAC/C,YAAY,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE;wBAChD,cAAc,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE;wBAClD,cAAc,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE;wBACnC,gBAAgB,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE;wBACrC,OAAO,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE;qBAC5C;iBACF;gBACD,MAAM,EAAE,CAAC,KAAuB,EAAE,KAA0B,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,CAAC,OAAO,EAAE,CAAC;aACzG;YACD,iBAAiB,EAAE,GAAG,EAAE,CAAC,KAAK;YAC9B,WAAW,EAAE,eAAe;YAC5B,aAAa,EAAE,qBAAqB;YACpC,KAAK,CAAC,OAAO,CAAC,IAAsB,EAAE,IAAI;gBACxC,MAAM,OAAO,GAAG,wBAAwB,EAAE,CAAA;gBAC1C,IAAI,OAAO,KAAK,IAAI,EAAE,CAAC;oBACrB,MAAM,IAAI,aAAa,CACrB,gGAAgG;0BAC9F,iGAAiG;0BACjG,wEAAwE,CAC3E,CAAA;gBACH,CAAC;gBAED,IAAI,IAAI,CAAC,MAAM,KAAK,MAAM,EAAE,CAAC;oBAC3B,0BAA0B,EAAE,CAAA;oBAC5B,gBAAgB,CAAC,IAAI,EAAE,OAAO,CAAC,WAAW,CAAC,CAAA;oBAC3C,OAAO;wBACL,MAAM,EAAE,MAAe;wBACvB,WAAW,EAAE,OAAO,CAAC,WAAW;wBAChC,YAAY,EAAE,OAAO,CAAC,YAAY;wBAClC,cAAc,EAAE,OAAO,CAAC,cAAc;wBACtC,OAAO,EACL,8CAA8C,OAAO,CAAC,YAAY,aAAa;8BAC7E,GAAG,OAAO,CAAC,cAAc,4BAA4B,OAAO,CAAC,WAAW,GAAG;qBAChF,CAAA;gBACH,CAAC;gBAED,iDAAiD;gBACjD,MAAM,OAAO,GAAG,MAAM,oBAAoB,CAAC,GAAG,EAAE,OAAO,EAAE,IAAI,CAAC,MAAM,CAAC,CAAA;gBACrE,IAAI,OAAO,KAAK,IAAI,EAAE,CAAC;oBACrB,MAAM,IAAI,aAAa,CACrB,sCAAsC,OAAO,CAAC,YAAY,wCAAwC;0BAChG,sGAAsG,CACzG,CAAA;gBACH,CAAC;gBACD,MAAM,EAAE,YAAY,EAAE,OAAO,EAAE,GAAG,OAAO,CAAA;gBACzC,IAAI,CAAC,IAAI,CAAC,eAAe,IAAI,CAAC,YAAY,GAAG,CAAC,IAAI,OAAO,GAAG,CAAC,CAAC,EAAE,CAAC;oBAC/D,MAAM,KAAK,GAAa,EAAE,CAAA;oBAC1B,IAAI,YAAY,GAAG,CAAC;wBAAE,KAAK,CAAC,IAAI,CAAC,GAAG,YAAY,gBAAgB,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,CAAA;oBACxG,IAAI,OAAO,GAAG,CAAC;wBAAE,KAAK,CAAC,IAAI,CAAC,GAAG,OAAO,IAAI,OAAO,KAAK,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS,OAAO,OAAO,CAAC,cAAc,EAAE,CAAC,CAAA;oBAC9G,MAAM,IAAI,aAAa,CACrB,gBAAgB,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC;qHACsE,CAC1G,CAAA;gBACH,CAAC;gBAED,MAAM,gBAAgB,CAAC,GAAG,EAAE,OAAO,CAAC,QAAQ,EAAE,OAAO,CAAC,YAAY,CAAC,CAAA;gBACnE,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,GAAG,EAAE,mBAAmB,CAAC,OAAO,CAAC,QAAQ,EAAE,OAAO,CAAC,YAAY,CAAC,EAAE,IAAI,CAAC,MAAM,CAAC,CAAA;gBAC3G,IAAI,OAAO,CAAC,QAAQ,KAAK,CAAC,EAAE,CAAC;oBAC3B,MAAM,IAAI,aAAa,CAAC,8BAA8B,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC,CAAA;gBAC9E,CAAC;gBACD,MAAM,aAAa,GAAG,MAAM,MAAM,CAAC,GAAG,EAAE,YAAY,CAAC,OAAO,CAAC,QAAQ,EAAE,OAAO,CAAC,cAAc,CAAC,EAAE,IAAI,CAAC,MAAM,CAAC,CAAA;gBAC5G,IAAI,aAAa,CAAC,QAAQ,KAAK,CAAC,EAAE,CAAC;oBACjC,8FAA8F;oBAC9F,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,oCAAoC,OAAO,CAAC,cAAc,KAAK,UAAU,CAAC,aAAa,CAAC,EAAE,CAAC,CAAA;gBAC7G,CAAC;gBAED,0BAA0B,EAAE,CAAA;gBAC5B,gBAAgB,CAAC,IAAI,EAAE,OAAO,CAAC,WAAW,CAAC,CAAA;gBAC3C,MAAM,YAAY,GAAa,EAAE,CAAA;gBACjC,IAAI,OAAO,GAAG,CAAC;oBAAE,YAAY,CAAC,IAAI,CAAC,GAAG,OAAO,IAAI,OAAO,KAAK,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS,EAAE,CAAC,CAAA;gBACxF,IAAI,YAAY,GAAG,CAAC;oBAAE,YAAY,CAAC,IAAI,CAAC,GAAG,YAAY,gBAAgB,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,CAAA;gBAC/G,MAAM,WAAW,GAAG,YAAY,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,cAAc,YAAY,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAA;gBAC9F,OAAO;oBACL,MAAM,EAAE,QAAiB;oBACzB,WAAW,EAAE,OAAO,CAAC,WAAW;oBAChC,YAAY,EAAE,OAAO,CAAC,YAAY;oBAClC,cAAc,EAAE,OAAO,CAAC,cAAc;oBACtC,cAAc,EAAE,YAAY;oBAC5B,gBAAgB,EAAE,OAAO;oBACzB,OAAO,EACL,kCAAkC,OAAO,CAAC,YAAY,IAAI,WAAW,GAAG;0BACtE,0BAA0B,OAAO,CAAC,WAAW,GAAG;iBACrD,CAAA;YACH,CAAC;SACF,CAAC,CAAC,CAAA;IACL,CAAC;AACH,CAAC;AAED,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,eAAe,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,eAAe,CAAA"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Package-owned invariant companion for `@dsh-cc/tool-git-worktree`.
|
|
3
|
+
* @module @dsh-cc/tool-git-worktree/invariant
|
|
4
|
+
*/
|
|
5
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
6
|
+
/** Cordis companion plugin name. */
|
|
7
|
+
export declare const name = "tool-git-worktree-invariant";
|
|
8
|
+
/** Service required before the companion can reserve package ownership. */
|
|
9
|
+
export declare const inject: string[];
|
|
10
|
+
/**
|
|
11
|
+
* Register this package's invariant companion.
|
|
12
|
+
* @param ctx - Cordis context carrying the invariant service.
|
|
13
|
+
* @returns the installed registration's disposer after setup succeeds.
|
|
14
|
+
*/
|
|
15
|
+
export declare const apply: (ctx: Context) => Promise<() => void>;
|
|
16
|
+
//# sourceMappingURL=invariant.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"invariant.d.ts","sourceRoot":"","sources":["../src/invariant.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAKlD,oCAAoC;AACpC,eAAO,MAAM,IAAI,gCAAgC,CAAA;AACjD,2EAA2E;AAC3E,eAAO,MAAM,MAAM,UAAiB,CAAA;AASpC;;;;GAIG;AACH,eAAO,MAAM,KAAK,GAAI,KAAK,OAAO,KAAG,OAAO,CAAC,MAAM,IAAI,CACU,CAAA"}
|
package/lib/invariant.js
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Package-owned invariant companion for `@dsh-cc/tool-git-worktree`.
|
|
3
|
+
* @module @dsh-cc/tool-git-worktree/invariant
|
|
4
|
+
*/
|
|
5
|
+
const PACKAGE_NAME = '@dsh-cc/tool-git-worktree';
|
|
6
|
+
/** Cordis companion plugin name. */
|
|
7
|
+
export const name = 'tool-git-worktree-invariant';
|
|
8
|
+
/** Service required before the companion can reserve package ownership. */
|
|
9
|
+
export const inject = ['invariants'];
|
|
10
|
+
/**
|
|
11
|
+
* No runtime invariant: git is an external process the package never vends a
|
|
12
|
+
* snapshot of, and the active worktree session is process-local navigation
|
|
13
|
+
* state rather than a durable published projection a companion could cross-check.
|
|
14
|
+
*/
|
|
15
|
+
const install = () => { };
|
|
16
|
+
/**
|
|
17
|
+
* Register this package's invariant companion.
|
|
18
|
+
* @param ctx - Cordis context carrying the invariant service.
|
|
19
|
+
* @returns the installed registration's disposer after setup succeeds.
|
|
20
|
+
*/
|
|
21
|
+
export const apply = (ctx) => Promise.resolve(ctx.invariants.register(PACKAGE_NAME, install));
|
|
22
|
+
/* jscpd:ignore-end */
|
|
23
|
+
//# sourceMappingURL=invariant.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"invariant.js","sourceRoot":"","sources":["../src/invariant.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAMH,MAAM,YAAY,GAAG,2BAA2B,CAAA;AAEhD,oCAAoC;AACpC,MAAM,CAAC,MAAM,IAAI,GAAG,6BAA6B,CAAA;AACjD,2EAA2E;AAC3E,MAAM,CAAC,MAAM,MAAM,GAAG,CAAC,YAAY,CAAC,CAAA;AAEpC;;;;GAIG;AACH,MAAM,OAAO,GAAuB,GAAG,EAAE,GAAE,CAAC,CAAA;AAE5C;;;;GAIG;AACH,MAAM,CAAC,MAAM,KAAK,GAAG,CAAC,GAAY,EAAuB,EAAE,CACzD,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,QAAQ,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC,CAAA;AACjE,sBAAsB"}
|
package/lib/render.d.ts
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure presentation (presentCall / presentResult) and message rendering for
|
|
3
|
+
* the git-worktree tools. All functions are pure in their arguments so the
|
|
4
|
+
* registry can replay them against logged call metadata.
|
|
5
|
+
* @module @dsh-cc/tool-git-worktree/render
|
|
6
|
+
*/
|
|
7
|
+
import type { GenericCallView, ToolResult, ToolResultView } from '@dsh-cc/tools';
|
|
8
|
+
/** Arguments accepted by the EnterWorktree tool. */
|
|
9
|
+
export interface EnterWorktreeArgs {
|
|
10
|
+
name?: string;
|
|
11
|
+
}
|
|
12
|
+
/** Arguments accepted by the ExitWorktree tool. */
|
|
13
|
+
export interface ExitWorktreeArgs {
|
|
14
|
+
action: 'keep' | 'remove';
|
|
15
|
+
discard_changes?: boolean;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Present an EnterWorktree pending call as a generic execute card.
|
|
19
|
+
* @param args - validated tool arguments.
|
|
20
|
+
* @returns a generic card view.
|
|
21
|
+
*/
|
|
22
|
+
export declare function presentEnterCall(args: EnterWorktreeArgs): GenericCallView;
|
|
23
|
+
/**
|
|
24
|
+
* Present a worktree tool result as a generic card with a human-readable
|
|
25
|
+
* summary. Both tools share this presenter because their result shapes are the
|
|
26
|
+
* same — a plain message on success, fenced text on error so a failure is
|
|
27
|
+
* visually distinct from a success summary.
|
|
28
|
+
* @param _args - validated tool arguments (unused by the presenter).
|
|
29
|
+
* @param result - the finalized tool result.
|
|
30
|
+
* @returns a generic card view, or `undefined` for an unexpected shape.
|
|
31
|
+
*/
|
|
32
|
+
export declare function presentWorktreeResult(_args: unknown, result: ToolResult): ToolResultView | undefined;
|
|
33
|
+
/**
|
|
34
|
+
* Present an ExitWorktree pending call. A `remove` is the destructive branch
|
|
35
|
+
* and is surfaced with a generic execute card whose content states the deletion
|
|
36
|
+
* consequence; `keep` is a generic card stating that nothing is deleted.
|
|
37
|
+
* @param args - validated tool arguments.
|
|
38
|
+
* @returns a generic card view.
|
|
39
|
+
*/
|
|
40
|
+
export declare function presentExitCall(args: ExitWorktreeArgs): GenericCallView;
|
|
41
|
+
//# sourceMappingURL=render.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"render.d.ts","sourceRoot":"","sources":["../src/render.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,eAAe,CAAA;AAEhF,oDAAoD;AACpD,MAAM,WAAW,iBAAiB;IAChC,IAAI,CAAC,EAAE,MAAM,CAAA;CACd;AAED,mDAAmD;AACnD,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,MAAM,GAAG,QAAQ,CAAA;IACzB,eAAe,CAAC,EAAE,OAAO,CAAA;CAC1B;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,iBAAiB,GAAG,eAAe,CAezE;AAED;;;;;;;;GAQG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,GAAG,cAAc,GAAG,SAAS,CAMpG;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,gBAAgB,GAAG,eAAe,CAkBvE"}
|
package/lib/render.js
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure presentation (presentCall / presentResult) and message rendering for
|
|
3
|
+
* the git-worktree tools. All functions are pure in their arguments so the
|
|
4
|
+
* registry can replay them against logged call metadata.
|
|
5
|
+
* @module @dsh-cc/tool-git-worktree/render
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Present an EnterWorktree pending call as a generic execute card.
|
|
9
|
+
* @param args - validated tool arguments.
|
|
10
|
+
* @returns a generic card view.
|
|
11
|
+
*/
|
|
12
|
+
export function presentEnterCall(args) {
|
|
13
|
+
return {
|
|
14
|
+
card: 'generic',
|
|
15
|
+
title: 'Creating worktree',
|
|
16
|
+
kind: 'execute',
|
|
17
|
+
rawInput: args.name ?? '(generated name)',
|
|
18
|
+
content: [
|
|
19
|
+
{
|
|
20
|
+
type: 'text',
|
|
21
|
+
text: args.name === undefined
|
|
22
|
+
? 'Create a new git worktree with a generated name and switch the session into it.'
|
|
23
|
+
: `Create a new git worktree "${args.name}" and switch the session into it.`,
|
|
24
|
+
},
|
|
25
|
+
],
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Present a worktree tool result as a generic card with a human-readable
|
|
30
|
+
* summary. Both tools share this presenter because their result shapes are the
|
|
31
|
+
* same — a plain message on success, fenced text on error so a failure is
|
|
32
|
+
* visually distinct from a success summary.
|
|
33
|
+
* @param _args - validated tool arguments (unused by the presenter).
|
|
34
|
+
* @param result - the finalized tool result.
|
|
35
|
+
* @returns a generic card view, or `undefined` for an unexpected shape.
|
|
36
|
+
*/
|
|
37
|
+
export function presentWorktreeResult(_args, result) {
|
|
38
|
+
const block = result.content.length === 1 ? result.content[0] : undefined;
|
|
39
|
+
if (block === undefined || block.type !== 'text')
|
|
40
|
+
return undefined;
|
|
41
|
+
return result.isError
|
|
42
|
+
? { card: 'generic', content: [{ type: 'text', text: `\`\`\`\n${block.text.replace(/\n+$/, '')}\n\`\`\`` }] }
|
|
43
|
+
: { card: 'generic', content: [{ type: 'text', text: block.text }] };
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Present an ExitWorktree pending call. A `remove` is the destructive branch
|
|
47
|
+
* and is surfaced with a generic execute card whose content states the deletion
|
|
48
|
+
* consequence; `keep` is a generic card stating that nothing is deleted.
|
|
49
|
+
* @param args - validated tool arguments.
|
|
50
|
+
* @returns a generic card view.
|
|
51
|
+
*/
|
|
52
|
+
export function presentExitCall(args) {
|
|
53
|
+
const removing = args.action === 'remove';
|
|
54
|
+
return {
|
|
55
|
+
card: 'generic',
|
|
56
|
+
title: removing ? 'Removing worktree' : 'Keeping worktree',
|
|
57
|
+
kind: 'execute',
|
|
58
|
+
rawInput: removing ? 'remove' : 'keep',
|
|
59
|
+
content: [
|
|
60
|
+
{
|
|
61
|
+
type: 'text',
|
|
62
|
+
text: removing
|
|
63
|
+
? 'Remove the active worktree and its branch. This deletes the worktree directory'
|
|
64
|
+
+ (args.discard_changes === true ? ' and discards any uncommitted changes and commits' : '')
|
|
65
|
+
+ '.'
|
|
66
|
+
: 'Leave the active worktree and its branch on disk and return to the original directory.',
|
|
67
|
+
},
|
|
68
|
+
],
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
//# sourceMappingURL=render.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"render.js","sourceRoot":"","sources":["../src/render.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAeH;;;;GAIG;AACH,MAAM,UAAU,gBAAgB,CAAC,IAAuB;IACtD,OAAO;QACL,IAAI,EAAE,SAAS;QACf,KAAK,EAAE,mBAAmB;QAC1B,IAAI,EAAE,SAAS;QACf,QAAQ,EAAE,IAAI,CAAC,IAAI,IAAI,kBAAkB;QACzC,OAAO,EAAE;YACP;gBACE,IAAI,EAAE,MAAM;gBACZ,IAAI,EAAE,IAAI,CAAC,IAAI,KAAK,SAAS;oBAC3B,CAAC,CAAC,iFAAiF;oBACnF,CAAC,CAAC,8BAA8B,IAAI,CAAC,IAAI,mCAAmC;aAC/E;SACF;KACF,CAAA;AACH,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,qBAAqB,CAAC,KAAc,EAAE,MAAkB;IACtE,MAAM,KAAK,GAAG,MAAM,CAAC,OAAO,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAA;IACzE,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,CAAC,IAAI,KAAK,MAAM;QAAE,OAAO,SAAS,CAAA;IAClE,OAAO,MAAM,CAAC,OAAO;QACnB,CAAC,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,WAAW,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,UAAU,EAAE,CAAC,EAAE;QAC7G,CAAC,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,CAAC,EAAE,CAAA;AACxE,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,eAAe,CAAC,IAAsB;IACpD,MAAM,QAAQ,GAAG,IAAI,CAAC,MAAM,KAAK,QAAQ,CAAA;IACzC,OAAO;QACL,IAAI,EAAE,SAAS;QACf,KAAK,EAAE,QAAQ,CAAC,CAAC,CAAC,mBAAmB,CAAC,CAAC,CAAC,kBAAkB;QAC1D,IAAI,EAAE,SAAS;QACf,QAAQ,EAAE,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM;QACtC,OAAO,EAAE;YACP;gBACE,IAAI,EAAE,MAAM;gBACZ,IAAI,EAAE,QAAQ;oBACZ,CAAC,CAAC,gFAAgF;0BAC9E,CAAC,IAAI,CAAC,eAAe,KAAK,IAAI,CAAC,CAAC,CAAC,mDAAmD,CAAC,CAAC,CAAC,EAAE,CAAC;0BAC1F,GAAG;oBACP,CAAC,CAAC,wFAAwF;aAC7F;SACF;KACF,CAAA;AACH,CAAC"}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Centralized git-worktree command construction and active-session state for
|
|
3
|
+
* `@dsh-cc/tool-git-worktree`. Keeping every git command in one module
|
|
4
|
+
* is the seam a future pure-JS git implementation would replace; callers never
|
|
5
|
+
* construct `git ...` argument lists themselves.
|
|
6
|
+
* @module @dsh-cc/tool-git-worktree/worktree
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Maximum length of a worktree slug. Mirrors the claude-code reference constraint.
|
|
10
|
+
*/
|
|
11
|
+
export declare const MAX_SLUG_LENGTH = 64;
|
|
12
|
+
/**
|
|
13
|
+
* Validate a worktree slug to keep it inside `.claude/worktrees/` and safe as a
|
|
14
|
+
* git branch suffix. Rejects empty segments, `.`/`..`, drive characters, and
|
|
15
|
+
* leading/trailing slashes; allows `user/feature` nesting.
|
|
16
|
+
* @param slug - The requested worktree name.
|
|
17
|
+
* @returns nothing on success; throws on an invalid slug.
|
|
18
|
+
*/
|
|
19
|
+
export declare function validateSlug(slug: string): void;
|
|
20
|
+
/**
|
|
21
|
+
* Flatten a possibly nested slug (`user/feature` → `user+feature`) into a value
|
|
22
|
+
* safe as a single directory name and a single git ref. `+` is valid in branch
|
|
23
|
+
* names and paths but absent from the slug allowlist, so the map is injective.
|
|
24
|
+
* @param slug - A validated worktree slug.
|
|
25
|
+
* @returns the flattened name under `.claude/worktrees/`.
|
|
26
|
+
*/
|
|
27
|
+
export declare function flattenSlug(slug: string): string;
|
|
28
|
+
/**
|
|
29
|
+
* The git branch backing a worktree. Prefixes `worktree-` so it never collides
|
|
30
|
+
* with a user branch.
|
|
31
|
+
* @param slug - A validated worktree slug.
|
|
32
|
+
* @returns the branch name.
|
|
33
|
+
*/
|
|
34
|
+
export declare function worktreeBranch(slug: string): string;
|
|
35
|
+
/**
|
|
36
|
+
* The on-disk worktree path for a repo root and slug.
|
|
37
|
+
* @param repoRoot - The canonical repository root.
|
|
38
|
+
* @param slug - A validated worktree slug.
|
|
39
|
+
* @returns the absolute worktree path.
|
|
40
|
+
*/
|
|
41
|
+
export declare function worktreePathFor(repoRoot: string, slug: string): string;
|
|
42
|
+
/** The directory holding all worktrees for a repo root. */
|
|
43
|
+
export declare function worktreesDir(repoRoot: string): string;
|
|
44
|
+
export interface GitCmd {
|
|
45
|
+
/** The full command string to run in the shell. */
|
|
46
|
+
readonly command: string;
|
|
47
|
+
/** The directory git runs in (the repo root, never the worktree being removed). */
|
|
48
|
+
readonly workdir: string;
|
|
49
|
+
/** Short human label for logs and errors. */
|
|
50
|
+
readonly label: string;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* `git worktree add` on a fresh branch based on the current HEAD. Uses `-B`
|
|
54
|
+
* (not `-b`) so a stale orphan branch left by a removed worktree is reset
|
|
55
|
+
* rather than failing.
|
|
56
|
+
* @param repoRoot - The canonical repository root.
|
|
57
|
+
* @param slug - A validated worktree slug.
|
|
58
|
+
* @returns the command to run.
|
|
59
|
+
*/
|
|
60
|
+
export declare function addWorktree(repoRoot: string, slug: string): GitCmd;
|
|
61
|
+
/**
|
|
62
|
+
* `git worktree remove --force <path>`: git's force flag removes a dirty or
|
|
63
|
+
* committed-on worktree. Callers gate `--force` on an explicit
|
|
64
|
+
* `discard_changes` grant; this command is constructed only for that path.
|
|
65
|
+
* @param repoRoot - The canonical repository root.
|
|
66
|
+
* @param path - Absolute worktree path (must be a registered worktree).
|
|
67
|
+
* @returns the command to run.
|
|
68
|
+
*/
|
|
69
|
+
export declare function forceRemoveWorktree(repoRoot: string, path: string): GitCmd;
|
|
70
|
+
/**
|
|
71
|
+
* `git branch -D <branch>` to delete the now-unregistered worktree branch.
|
|
72
|
+
* @param repoRoot - The canonical repository root.
|
|
73
|
+
* @param branch - Worktree branch to delete.
|
|
74
|
+
* @returns the command to run.
|
|
75
|
+
*/
|
|
76
|
+
export declare function deleteBranch(repoRoot: string, branch: string): GitCmd;
|
|
77
|
+
/**
|
|
78
|
+
* `git status --porcelain` inside the worktree — the working-tree dirtiness
|
|
79
|
+
* probe for the remove gate.
|
|
80
|
+
* @param worktreePath - Absolute worktree path.
|
|
81
|
+
* @returns the command to run.
|
|
82
|
+
*/
|
|
83
|
+
export declare function status(worktreePath: string): GitCmd;
|
|
84
|
+
/**
|
|
85
|
+
* `git rev-list --count <base>..HEAD` inside the worktree — the commit probe
|
|
86
|
+
* for the remove gate.
|
|
87
|
+
* @param worktreePath - Absolute worktree path.
|
|
88
|
+
* @param originalHead - The commit the worktree was created from.
|
|
89
|
+
* @returns the command to run.
|
|
90
|
+
*/
|
|
91
|
+
export declare function commitsAhead(worktreePath: string, originalHead: string): GitCmd;
|
|
92
|
+
/**
|
|
93
|
+
* Quote an argument for a `bash -c` command line using single-quote escaping.
|
|
94
|
+
* @param arg - A path or ref to embed literally.
|
|
95
|
+
* @returns the shell-quoted token.
|
|
96
|
+
*/
|
|
97
|
+
export declare function quote(arg: string): string;
|
|
98
|
+
/** Human-readable for a repo — the basename of its canonical root. */
|
|
99
|
+
export declare function repoLabel(repoRoot: string): string;
|
|
100
|
+
/** A live EnterWorktree session for the current process. */
|
|
101
|
+
export interface WorktreeSession {
|
|
102
|
+
/** The cwd of the session before EnterWorktree ran. */
|
|
103
|
+
readonly originalCwd: string;
|
|
104
|
+
/** The session's canonical repository root (where `.claude/worktrees` lives). */
|
|
105
|
+
readonly repoRoot: string;
|
|
106
|
+
/** Absolute on-disk worktree path. */
|
|
107
|
+
readonly worktreePath: string;
|
|
108
|
+
/** The worktree branch name. */
|
|
109
|
+
readonly worktreeBranch: string;
|
|
110
|
+
/** The commit the worktree was based on (the remove gate's baseline). */
|
|
111
|
+
readonly originalHead: string;
|
|
112
|
+
}
|
|
113
|
+
/** The active worktree session, or `null` when not inside one. */
|
|
114
|
+
export declare function getActiveWorktreeSession(): WorktreeSession | null;
|
|
115
|
+
/** Set the active worktree session. @internal */
|
|
116
|
+
export declare function setActiveWorktreeSession(session: WorktreeSession): void;
|
|
117
|
+
/** Clear the active worktree session. @internal */
|
|
118
|
+
export declare function clearActiveWorktreeSession(): void;
|
|
119
|
+
/**
|
|
120
|
+
* Generate a random, valid worktree slug in the style of the claude-code
|
|
121
|
+
* reference (`swift-fox-8f3a`).
|
|
122
|
+
* @returns a valid random slug.
|
|
123
|
+
*/
|
|
124
|
+
export declare function randomSlug(): string;
|
|
125
|
+
//# sourceMappingURL=worktree.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"worktree.d.ts","sourceRoot":"","sources":["../src/worktree.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH;;GAEG;AACH,eAAO,MAAM,eAAe,KAAK,CAAA;AAKjC;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAkB/C;AAED;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEhD;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEnD;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAEtE;AAED,2DAA2D;AAC3D,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAErD;AAED,MAAM,WAAW,MAAM;IACrB,mDAAmD;IACnD,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,mFAAmF;IACnF,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,6CAA6C;IAC7C,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;CACvB;AAED;;;;;;;GAOG;AACH,wBAAgB,WAAW,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAOlE;AAED;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAM1E;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAMrE;AAED;;;;;GAKG;AACH,wBAAgB,MAAM,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,CAMnD;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,YAAY,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,MAAM,CAM/E;AAED;;;;GAIG;AACH,wBAAgB,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAEzC;AAED,sEAAsE;AACtE,wBAAgB,SAAS,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAElD;AAED,4DAA4D;AAC5D,MAAM,WAAW,eAAe;IAC9B,uDAAuD;IACvD,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;IAC5B,iFAAiF;IACjF,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,sCAAsC;IACtC,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAA;IAC7B,gCAAgC;IAChC,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAA;IAC/B,yEAAyE;IACzE,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAA;CAC9B;AAUD,kEAAkE;AAClE,wBAAgB,wBAAwB,IAAI,eAAe,GAAG,IAAI,CAEjE;AAED,iDAAiD;AACjD,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,eAAe,GAAG,IAAI,CAEvE;AAED,mDAAmD;AACnD,wBAAgB,0BAA0B,IAAI,IAAI,CAEjD;AAOD;;;;GAIG;AACH,wBAAgB,UAAU,IAAI,MAAM,CAKnC"}
|
package/lib/worktree.js
ADDED
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Centralized git-worktree command construction and active-session state for
|
|
3
|
+
* `@dsh-cc/tool-git-worktree`. Keeping every git command in one module
|
|
4
|
+
* is the seam a future pure-JS git implementation would replace; callers never
|
|
5
|
+
* construct `git ...` argument lists themselves.
|
|
6
|
+
* @module @dsh-cc/tool-git-worktree/worktree
|
|
7
|
+
*/
|
|
8
|
+
import { basename, join } from 'node:path';
|
|
9
|
+
/**
|
|
10
|
+
* Maximum length of a worktree slug. Mirrors the claude-code reference constraint.
|
|
11
|
+
*/
|
|
12
|
+
export const MAX_SLUG_LENGTH = 64;
|
|
13
|
+
/** Allowlist for one `/`-separated slug segment (letters, digits, `.`, `_`, `-`). */
|
|
14
|
+
const SEGMENT = /^[a-zA-Z0-9._-]+$/;
|
|
15
|
+
/**
|
|
16
|
+
* Validate a worktree slug to keep it inside `.claude/worktrees/` and safe as a
|
|
17
|
+
* git branch suffix. Rejects empty segments, `.`/`..`, drive characters, and
|
|
18
|
+
* leading/trailing slashes; allows `user/feature` nesting.
|
|
19
|
+
* @param slug - The requested worktree name.
|
|
20
|
+
* @returns nothing on success; throws on an invalid slug.
|
|
21
|
+
*/
|
|
22
|
+
export function validateSlug(slug) {
|
|
23
|
+
if (slug.length > MAX_SLUG_LENGTH) {
|
|
24
|
+
throw new Error(`invalid worktree name: must be ${MAX_SLUG_LENGTH} characters or fewer (got ${slug.length})`);
|
|
25
|
+
}
|
|
26
|
+
for (const segment of slug.split('/')) {
|
|
27
|
+
if (segment === '.' || segment === '..') {
|
|
28
|
+
throw new Error(`invalid worktree name "${slug}": must not contain "." or ".." path segments`);
|
|
29
|
+
}
|
|
30
|
+
if (!SEGMENT.test(segment)) {
|
|
31
|
+
throw new Error(`invalid worktree name "${slug}": each "/"-separated segment must be non-empty and contain only letters, digits, dots, underscores, and dashes`);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Flatten a possibly nested slug (`user/feature` → `user+feature`) into a value
|
|
37
|
+
* safe as a single directory name and a single git ref. `+` is valid in branch
|
|
38
|
+
* names and paths but absent from the slug allowlist, so the map is injective.
|
|
39
|
+
* @param slug - A validated worktree slug.
|
|
40
|
+
* @returns the flattened name under `.claude/worktrees/`.
|
|
41
|
+
*/
|
|
42
|
+
export function flattenSlug(slug) {
|
|
43
|
+
return slug.replaceAll('/', '+');
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* The git branch backing a worktree. Prefixes `worktree-` so it never collides
|
|
47
|
+
* with a user branch.
|
|
48
|
+
* @param slug - A validated worktree slug.
|
|
49
|
+
* @returns the branch name.
|
|
50
|
+
*/
|
|
51
|
+
export function worktreeBranch(slug) {
|
|
52
|
+
return `worktree-${flattenSlug(slug)}`;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* The on-disk worktree path for a repo root and slug.
|
|
56
|
+
* @param repoRoot - The canonical repository root.
|
|
57
|
+
* @param slug - A validated worktree slug.
|
|
58
|
+
* @returns the absolute worktree path.
|
|
59
|
+
*/
|
|
60
|
+
export function worktreePathFor(repoRoot, slug) {
|
|
61
|
+
return join(repoRoot, '.claude', 'worktrees', flattenSlug(slug));
|
|
62
|
+
}
|
|
63
|
+
/** The directory holding all worktrees for a repo root. */
|
|
64
|
+
export function worktreesDir(repoRoot) {
|
|
65
|
+
return join(repoRoot, '.claude', 'worktrees');
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* `git worktree add` on a fresh branch based on the current HEAD. Uses `-B`
|
|
69
|
+
* (not `-b`) so a stale orphan branch left by a removed worktree is reset
|
|
70
|
+
* rather than failing.
|
|
71
|
+
* @param repoRoot - The canonical repository root.
|
|
72
|
+
* @param slug - A validated worktree slug.
|
|
73
|
+
* @returns the command to run.
|
|
74
|
+
*/
|
|
75
|
+
export function addWorktree(repoRoot, slug) {
|
|
76
|
+
const path = worktreePathFor(repoRoot, slug);
|
|
77
|
+
return {
|
|
78
|
+
command: `git worktree add -B ${quote(worktreeBranch(slug))} ${quote(path)} HEAD`,
|
|
79
|
+
workdir: repoRoot,
|
|
80
|
+
label: `create worktree "${path}"`,
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* `git worktree remove --force <path>`: git's force flag removes a dirty or
|
|
85
|
+
* committed-on worktree. Callers gate `--force` on an explicit
|
|
86
|
+
* `discard_changes` grant; this command is constructed only for that path.
|
|
87
|
+
* @param repoRoot - The canonical repository root.
|
|
88
|
+
* @param path - Absolute worktree path (must be a registered worktree).
|
|
89
|
+
* @returns the command to run.
|
|
90
|
+
*/
|
|
91
|
+
export function forceRemoveWorktree(repoRoot, path) {
|
|
92
|
+
return {
|
|
93
|
+
command: `git worktree remove --force ${quote(path)}`,
|
|
94
|
+
workdir: repoRoot,
|
|
95
|
+
label: `remove worktree "${path}"`,
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* `git branch -D <branch>` to delete the now-unregistered worktree branch.
|
|
100
|
+
* @param repoRoot - The canonical repository root.
|
|
101
|
+
* @param branch - Worktree branch to delete.
|
|
102
|
+
* @returns the command to run.
|
|
103
|
+
*/
|
|
104
|
+
export function deleteBranch(repoRoot, branch) {
|
|
105
|
+
return {
|
|
106
|
+
command: `git branch -D ${quote(branch)}`,
|
|
107
|
+
workdir: repoRoot,
|
|
108
|
+
label: `delete worktree branch "${branch}"`,
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* `git status --porcelain` inside the worktree — the working-tree dirtiness
|
|
113
|
+
* probe for the remove gate.
|
|
114
|
+
* @param worktreePath - Absolute worktree path.
|
|
115
|
+
* @returns the command to run.
|
|
116
|
+
*/
|
|
117
|
+
export function status(worktreePath) {
|
|
118
|
+
return {
|
|
119
|
+
command: 'git status --porcelain',
|
|
120
|
+
workdir: worktreePath,
|
|
121
|
+
label: `status "${worktreePath}"`,
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* `git rev-list --count <base>..HEAD` inside the worktree — the commit probe
|
|
126
|
+
* for the remove gate.
|
|
127
|
+
* @param worktreePath - Absolute worktree path.
|
|
128
|
+
* @param originalHead - The commit the worktree was created from.
|
|
129
|
+
* @returns the command to run.
|
|
130
|
+
*/
|
|
131
|
+
export function commitsAhead(worktreePath, originalHead) {
|
|
132
|
+
return {
|
|
133
|
+
command: `git rev-list --count ${quote(`${originalHead}..HEAD`)}`,
|
|
134
|
+
workdir: worktreePath,
|
|
135
|
+
label: `count commits ahead of worktree "${worktreePath}"`,
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Quote an argument for a `bash -c` command line using single-quote escaping.
|
|
140
|
+
* @param arg - A path or ref to embed literally.
|
|
141
|
+
* @returns the shell-quoted token.
|
|
142
|
+
*/
|
|
143
|
+
export function quote(arg) {
|
|
144
|
+
return `'${arg.replaceAll('\'', '\'\\\'\'')}'`;
|
|
145
|
+
}
|
|
146
|
+
/** Human-readable for a repo — the basename of its canonical root. */
|
|
147
|
+
export function repoLabel(repoRoot) {
|
|
148
|
+
return basename(repoRoot);
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* The single active worktree session for this process, mirroring the
|
|
152
|
+
* claude-code reference's module-level singleton. EnterWorktree sets it;
|
|
153
|
+
* ExitWorktree clears it. This is intentionally process-wide (not per-session)
|
|
154
|
+
* to match the reference and keep systemPrompt context injection unambiguous.
|
|
155
|
+
*/
|
|
156
|
+
let currentSession = null;
|
|
157
|
+
/** The active worktree session, or `null` when not inside one. */
|
|
158
|
+
export function getActiveWorktreeSession() {
|
|
159
|
+
return currentSession;
|
|
160
|
+
}
|
|
161
|
+
/** Set the active worktree session. @internal */
|
|
162
|
+
export function setActiveWorktreeSession(session) {
|
|
163
|
+
currentSession = session;
|
|
164
|
+
}
|
|
165
|
+
/** Clear the active worktree session. @internal */
|
|
166
|
+
export function clearActiveWorktreeSession() {
|
|
167
|
+
currentSession = null;
|
|
168
|
+
}
|
|
169
|
+
/** A base repo name for default (random) worktree slugs. @internal */
|
|
170
|
+
const ADJECTIVES = ['swift', 'bright', 'calm', 'keen', 'bold'];
|
|
171
|
+
/** A base repo noun for default (random) worktree slugs. @internal */
|
|
172
|
+
const NOUNS = ['fox', 'owl', 'elm', 'oak', 'ray'];
|
|
173
|
+
/**
|
|
174
|
+
* Generate a random, valid worktree slug in the style of the claude-code
|
|
175
|
+
* reference (`swift-fox-8f3a`).
|
|
176
|
+
* @returns a valid random slug.
|
|
177
|
+
*/
|
|
178
|
+
export function randomSlug() {
|
|
179
|
+
const adjective = ADJECTIVES[Math.floor(Math.random() * ADJECTIVES.length)];
|
|
180
|
+
const noun = NOUNS[Math.floor(Math.random() * NOUNS.length)];
|
|
181
|
+
const suffix = Math.random().toString(36).slice(2, 6);
|
|
182
|
+
return `${adjective}-${noun}-${suffix}`;
|
|
183
|
+
}
|
|
184
|
+
//# sourceMappingURL=worktree.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"worktree.js","sourceRoot":"","sources":["../src/worktree.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@dsh-cc/tool-git-worktree",
|
|
3
|
+
"description": "EnterWorktree / ExitWorktree model tools over the ctx.shell seam: create, keep, and remove isolated git worktrees inside .claude/worktrees",
|
|
4
|
+
"version": "0.5.0",
|
|
5
|
+
"repository": {
|
|
6
|
+
"type": "git",
|
|
7
|
+
"url": "git+https://github.com/dsh-cc/dsh-cc.git",
|
|
8
|
+
"directory": "packages/workspace/tool-git-worktree"
|
|
9
|
+
},
|
|
10
|
+
"type": "module",
|
|
11
|
+
"exports": {
|
|
12
|
+
".": {
|
|
13
|
+
"types": "./lib/index.d.ts",
|
|
14
|
+
"default": "./lib/index.js"
|
|
15
|
+
},
|
|
16
|
+
"./invariant": {
|
|
17
|
+
"types": "./lib/invariant.d.ts",
|
|
18
|
+
"default": "./lib/invariant.js"
|
|
19
|
+
},
|
|
20
|
+
"./src/*": "./src/*",
|
|
21
|
+
"./package.json": "./package.json"
|
|
22
|
+
},
|
|
23
|
+
"files": [
|
|
24
|
+
"lib"
|
|
25
|
+
],
|
|
26
|
+
"license": "Apache-2.0",
|
|
27
|
+
"peerDependencies": {
|
|
28
|
+
"@deepseek-ai/dsh-agent": ">=0.1.1-rc.2",
|
|
29
|
+
"@deepseek-ai/dsh-fs": ">=0.1.1-rc.2",
|
|
30
|
+
"@deepseek-ai/dsh-invariants": ">=0.1.1-rc.2",
|
|
31
|
+
"@deepseek-ai/dsh-llm": ">=0.1.1-rc.2",
|
|
32
|
+
"@deepseek-ai/dsh-shell": ">=0.1.1-rc.2",
|
|
33
|
+
"@deepseek-ai/dsh-system-prompt": ">=0.1.1-rc.2",
|
|
34
|
+
"@deepseek-ai/cordis": ">=0.1.1-rc.2",
|
|
35
|
+
"@deepseek-ai/schemastery": "^3.18.1",
|
|
36
|
+
"@dsh-cc/session-cwd": "^0.5.0",
|
|
37
|
+
"@dsh-cc/tools": "^0.5.0"
|
|
38
|
+
},
|
|
39
|
+
"devDependencies": {
|
|
40
|
+
"@deepseek-ai/dsh-agent": "link:../../../../deepseek-harness/packages/core/agent",
|
|
41
|
+
"@deepseek-ai/dsh-agent-loop": "link:../../../../deepseek-harness/packages/core/agent-loop",
|
|
42
|
+
"@deepseek-ai/dsh-agent-loop-testkit": "link:../../../../deepseek-harness/packages/test-support/agent-loop-testkit",
|
|
43
|
+
"@deepseek-ai/dsh-fs": "link:../../../../deepseek-harness/packages/fs/fs",
|
|
44
|
+
"@deepseek-ai/dsh-fs-local": "link:../../../../deepseek-harness/packages/fs/fs-local",
|
|
45
|
+
"@deepseek-ai/dsh-invariants": "link:../../../../deepseek-harness/packages/runtime-diagnostics/invariants",
|
|
46
|
+
"@deepseek-ai/dsh-llm": "link:../../../../deepseek-harness/packages/llm/llm",
|
|
47
|
+
"@deepseek-ai/dsh-shell": "link:../../../../deepseek-harness/packages/shell/shell",
|
|
48
|
+
"@deepseek-ai/dsh-subprocess-local": "link:../../../../deepseek-harness/packages/subprocess/subprocess-local",
|
|
49
|
+
"@deepseek-ai/dsh-system-prompt": "link:../../../../deepseek-harness/packages/core/system-prompt",
|
|
50
|
+
"@dsh-cc/session-cwd": "link:../session-cwd",
|
|
51
|
+
"@deepseek-ai/cordis": "link:../../../../deepseek-harness/vendor/cordis",
|
|
52
|
+
"@deepseek-ai/schemastery": "link:../../../../deepseek-harness/vendor/schemastery",
|
|
53
|
+
"@deepseek-ai/dsh-bash-local": "link:../../../../deepseek-harness/packages/shell/bash-local",
|
|
54
|
+
"@dsh-cc/tools": "^0.5.0"
|
|
55
|
+
},
|
|
56
|
+
"publishConfig": {
|
|
57
|
+
"access": "public"
|
|
58
|
+
}
|
|
59
|
+
}
|