dsh-git-worktree 0.1.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.
Files changed (43) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +37 -0
  3. package/README.zh.md +37 -0
  4. package/lib/adapters/files.d.ts +10 -0
  5. package/lib/adapters/files.js +101 -0
  6. package/lib/adapters/files.js.map +1 -0
  7. package/lib/adapters/git.d.ts +18 -0
  8. package/lib/adapters/git.js +172 -0
  9. package/lib/adapters/git.js.map +1 -0
  10. package/lib/adapters/lookup.d.ts +28 -0
  11. package/lib/adapters/lookup.js +41 -0
  12. package/lib/adapters/lookup.js.map +1 -0
  13. package/lib/adapters/registry.d.ts +15 -0
  14. package/lib/adapters/registry.js +242 -0
  15. package/lib/adapters/registry.js.map +1 -0
  16. package/lib/adapters/safe-file.d.ts +19 -0
  17. package/lib/adapters/safe-file.js +94 -0
  18. package/lib/adapters/safe-file.js.map +1 -0
  19. package/lib/commands.d.ts +12 -0
  20. package/lib/commands.js +121 -0
  21. package/lib/commands.js.map +1 -0
  22. package/lib/index.d.ts +76 -0
  23. package/lib/index.js +93 -0
  24. package/lib/index.js.map +1 -0
  25. package/lib/managed-worktree-path.d.ts +27 -0
  26. package/lib/managed-worktree-path.js +53 -0
  27. package/lib/managed-worktree-path.js.map +1 -0
  28. package/lib/ports.d.ts +204 -0
  29. package/lib/ports.js +10 -0
  30. package/lib/ports.js.map +1 -0
  31. package/lib/session-checkout-apply.d.ts +110 -0
  32. package/lib/session-checkout-apply.js +753 -0
  33. package/lib/session-checkout-apply.js.map +1 -0
  34. package/lib/session-checkout-module.d.ts +3 -0
  35. package/lib/session-checkout-module.js +2122 -0
  36. package/lib/session-checkout-module.js.map +1 -0
  37. package/lib/tools.d.ts +12 -0
  38. package/lib/tools.js +384 -0
  39. package/lib/tools.js.map +1 -0
  40. package/lib/types.d.ts +275 -0
  41. package/lib/types.js +40 -0
  42. package/lib/types.js.map +1 -0
  43. package/package.json +60 -0
@@ -0,0 +1,242 @@
1
+ /**
2
+ * Durable registry adapter: the session-checkout registry (version 2) as one
3
+ * atomic JSON file under the plugin's state directory. Validation gates every
4
+ * read — a corrupt file fails loud instead of being silently overwritten;
5
+ * v1 records migrate conservatively to `recovery_required` like Domi.
6
+ * @module dsh-git-worktree/adapters/registry
7
+ */
8
+ import { existsSync, mkdirSync } from 'node:fs';
9
+ import { dirname } from 'node:path';
10
+ import { SessionCheckoutError } from '../index.js';
11
+ import { readJsonFileSafe, writeJsonFileAtomic } from './safe-file.js';
12
+ function emptyRegistry() {
13
+ return {
14
+ version: 2,
15
+ revision: 0,
16
+ sessionBindings: {},
17
+ managedCheckouts: {},
18
+ };
19
+ }
20
+ function isRecord(value) {
21
+ return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
22
+ }
23
+ function isStringArray(value) {
24
+ return Array.isArray(value) && value.every((item) => typeof item === 'string');
25
+ }
26
+ function isTargetRef(value) {
27
+ if (!isRecord(value) || typeof value.kind !== 'string')
28
+ return false;
29
+ return value.kind === 'unselected'
30
+ || value.kind === 'local'
31
+ || (value.kind === 'isolated' && typeof value.checkoutId === 'string');
32
+ }
33
+ function isSessionBinding(value) {
34
+ return isRecord(value)
35
+ && typeof value.sessionId === 'string'
36
+ && typeof value.projectId === 'string'
37
+ && typeof value.projectName === 'string'
38
+ && isTargetRef(value.target)
39
+ && typeof value.ownerSessionId === 'string'
40
+ && typeof value.sourceRef === 'string'
41
+ && typeof value.sourceOid === 'string'
42
+ && typeof value.revision === 'number';
43
+ }
44
+ function isReview(value) {
45
+ return isRecord(value)
46
+ && typeof value.reviewId === 'string'
47
+ && typeof value.iteration === 'number'
48
+ && typeof value.preparedAt === 'number'
49
+ && (value.detailsMarkdown === undefined || typeof value.detailsMarkdown === 'string')
50
+ && typeof value.summary === 'string'
51
+ && (value.validationStatus === 'passed' || value.validationStatus === 'failed' || value.validationStatus === 'partial' || value.validationStatus === 'not_run')
52
+ && (value.validationSummary === undefined || typeof value.validationSummary === 'string')
53
+ && Array.isArray(value.tests)
54
+ && value.tests.every((test) => isRecord(test)
55
+ && typeof test.command === 'string'
56
+ && (test.status === 'passed' || test.status === 'failed' || test.status === 'not_run')
57
+ && (test.summary === undefined || typeof test.summary === 'string'))
58
+ && isStringArray(value.changedFiles)
59
+ && typeof value.suggestedCommitMessage === 'string'
60
+ && typeof value.isolatedFingerprint === 'string'
61
+ && typeof value.isolatedHeadOid === 'string';
62
+ }
63
+ function isDeliveryProof(value) {
64
+ return isRecord(value)
65
+ && (value.localBranch === null || typeof value.localBranch === 'string')
66
+ && typeof value.localHeadBefore === 'string'
67
+ && typeof value.localHeadAfter === 'string'
68
+ && isStringArray(value.changedFiles);
69
+ }
70
+ function isDelivery(value) {
71
+ if (!isRecord(value) || typeof value.state !== 'string')
72
+ return false;
73
+ if (value.state === 'working')
74
+ return typeof value.iteration === 'number';
75
+ if (value.state === 'ready_for_review')
76
+ return isReview(value.review);
77
+ if (value.state === 'finalized') {
78
+ return isReview(value.review)
79
+ && (value.commitOid === null || typeof value.commitOid === 'string')
80
+ && (value.proof === undefined || isDeliveryProof(value.proof))
81
+ && typeof value.isolatedFingerprint === 'string'
82
+ && typeof value.finalizedAt === 'number'
83
+ && (value.cleanup === 'pending' || value.cleanup === 'blocked')
84
+ && (value.cleanupMessage === undefined || typeof value.cleanupMessage === 'string');
85
+ }
86
+ if (value.state === 'retained') {
87
+ return isReview(value.review)
88
+ && (value.commitOid === null || typeof value.commitOid === 'string')
89
+ && (value.proof === undefined || isDeliveryProof(value.proof))
90
+ && typeof value.isolatedFingerprint === 'string'
91
+ && (value.retention === 'retain_24h' || value.retention === 'retain_3d' || value.retention === 'retain_manual')
92
+ && typeof value.retainedAt === 'number'
93
+ && (value.expiresAt === null || typeof value.expiresAt === 'number')
94
+ && (value.cleanup === 'scheduled' || value.cleanup === 'blocked')
95
+ && (value.cleanupMessage === undefined || typeof value.cleanupMessage === 'string');
96
+ }
97
+ if (value.state === 'delivered') {
98
+ return typeof value.iteration === 'number'
99
+ && (value.commitOid === null || typeof value.commitOid === 'string')
100
+ && (value.proof === undefined || isDeliveryProof(value.proof))
101
+ && typeof value.deliveredAt === 'number';
102
+ }
103
+ return false;
104
+ }
105
+ function isJournal(value) {
106
+ if (value === null)
107
+ return true;
108
+ if (!isRecord(value)
109
+ || typeof value.operationId !== 'string'
110
+ || typeof value.step !== 'string'
111
+ || typeof value.startedAt !== 'number')
112
+ return false;
113
+ if (value.operation === 'create')
114
+ return value.step === 'creating_worktree';
115
+ const validOperation = value.operation === 'apply'
116
+ || value.operation === 'finish'
117
+ || value.operation === 'cleanup';
118
+ const validStep = value.step === 'planning'
119
+ || value.step === 'writing_local'
120
+ || value.step === 'removing_worktree';
121
+ return validOperation
122
+ && validStep
123
+ && (value.baseOid === undefined || typeof value.baseOid === 'string')
124
+ && (value.planRevision === undefined || typeof value.planRevision === 'string')
125
+ && (value.localFingerprint === undefined || typeof value.localFingerprint === 'string')
126
+ && (value.isolatedFingerprint === undefined || typeof value.isolatedFingerprint === 'string')
127
+ && (value.effectiveBaseOid === undefined || typeof value.effectiveBaseOid === 'string')
128
+ && (value.baseStrategy === undefined || value.baseStrategy === 'recorded_base' || value.baseStrategy === 'isolated_contains_local_head' || value.baseStrategy === 'local_contains_isolated_head')
129
+ && (value.localHeadOid === undefined || typeof value.localHeadOid === 'string')
130
+ && (value.isolatedHeadOid === undefined || typeof value.isolatedHeadOid === 'string')
131
+ && (value.commitOid === undefined || typeof value.commitOid === 'string')
132
+ && (value.retention === undefined || value.retention === 'cleanup' || value.retention === 'retain_24h' || value.retention === 'retain_3d' || value.retention === 'retain_manual')
133
+ && (value.changedFiles === undefined || isStringArray(value.changedFiles));
134
+ }
135
+ function isManagedCheckout(value) {
136
+ return isRecord(value)
137
+ && typeof value.checkoutId === 'string'
138
+ && typeof value.projectId === 'string'
139
+ && typeof value.projectName === 'string'
140
+ && typeof value.ownerSessionId === 'string'
141
+ && typeof value.localRoot === 'string'
142
+ && typeof value.managedRoot === 'string'
143
+ && typeof value.managedGitRoot === 'string'
144
+ && typeof value.gitCommonDir === 'string'
145
+ && typeof value.gitDir === 'string'
146
+ && typeof value.baseOid === 'string'
147
+ && (value.applyBaseOid === undefined || typeof value.applyBaseOid === 'string')
148
+ && typeof value.sourceRef === 'string'
149
+ && (value.phase === 'preparing' || value.phase === 'ready' || value.phase === 'mutating' || value.phase === 'recovery_required' || value.phase === 'finalized' || value.phase === 'retained' || value.phase === 'discarded')
150
+ && isDelivery(value.delivery)
151
+ && isJournal(value.journal)
152
+ && typeof value.revision === 'number';
153
+ }
154
+ function isRegistry(value) {
155
+ if (!isRecord(value)
156
+ || value.version !== 2
157
+ || typeof value.revision !== 'number'
158
+ || !isRecord(value.sessionBindings)
159
+ || !isRecord(value.managedCheckouts))
160
+ return false;
161
+ return Object.values(value.sessionBindings).every(isSessionBinding)
162
+ && Object.values(value.managedCheckouts).every(isManagedCheckout);
163
+ }
164
+ function migrateLegacyRegistry(value) {
165
+ if (!isRecord(value)
166
+ || value.version !== 1
167
+ || typeof value.revision !== 'number'
168
+ || !isRecord(value.sessionBindings)
169
+ || !isRecord(value.managedCheckouts)
170
+ || !Object.values(value.sessionBindings).every(isSessionBinding))
171
+ return null;
172
+ const managedCheckouts = {};
173
+ for (const [checkoutId, raw] of Object.entries(value.managedCheckouts)) {
174
+ if (!isRecord(raw)
175
+ || typeof raw.checkoutId !== 'string'
176
+ || typeof raw.projectId !== 'string'
177
+ || typeof raw.projectName !== 'string'
178
+ || typeof raw.ownerSessionId !== 'string'
179
+ || typeof raw.localRoot !== 'string'
180
+ || typeof raw.managedRoot !== 'string'
181
+ || typeof raw.managedGitRoot !== 'string'
182
+ || typeof raw.gitCommonDir !== 'string'
183
+ || typeof raw.gitDir !== 'string'
184
+ || typeof raw.baseOid !== 'string'
185
+ || typeof raw.sourceRef !== 'string'
186
+ || typeof raw.revision !== 'number')
187
+ return null;
188
+ const legacyPhase = raw.phase;
189
+ const phase = legacyPhase === 'ready' || legacyPhase === 'discarded'
190
+ ? legacyPhase
191
+ : 'recovery_required';
192
+ managedCheckouts[checkoutId] = {
193
+ checkoutId: raw.checkoutId,
194
+ projectId: raw.projectId,
195
+ projectName: raw.projectName,
196
+ ownerSessionId: raw.ownerSessionId,
197
+ localRoot: raw.localRoot,
198
+ managedRoot: raw.managedRoot,
199
+ managedGitRoot: raw.managedGitRoot,
200
+ gitCommonDir: raw.gitCommonDir,
201
+ gitDir: raw.gitDir,
202
+ baseOid: raw.baseOid,
203
+ ...(typeof raw.applyBaseOid === 'string' ? { applyBaseOid: raw.applyBaseOid } : {}),
204
+ sourceRef: raw.sourceRef,
205
+ phase,
206
+ delivery: { state: 'working', iteration: 1 },
207
+ journal: null,
208
+ revision: raw.revision + (phase === legacyPhase ? 0 : 1),
209
+ };
210
+ }
211
+ return {
212
+ version: 2,
213
+ revision: value.revision + 1,
214
+ sessionBindings: value.sessionBindings,
215
+ managedCheckouts,
216
+ };
217
+ }
218
+ /** Atomic JSON registry file with `.tmp`/`.bak` crash recovery (see safe-file). */
219
+ export class AtomicJsonCheckoutRegistry {
220
+ path;
221
+ constructor(path) {
222
+ this.path = path;
223
+ }
224
+ read() {
225
+ const value = readJsonFileSafe(this.path);
226
+ if (value && isRegistry(value))
227
+ return value;
228
+ const migrated = migrateLegacyRegistry(value);
229
+ if (migrated) {
230
+ this.write(migrated);
231
+ return migrated;
232
+ }
233
+ if (!existsSync(this.path))
234
+ return emptyRegistry();
235
+ throw new SessionCheckoutError('registry_corrupt', 'managed-checkouts.json 损坏,已停止访问 checkout');
236
+ }
237
+ write(registry) {
238
+ mkdirSync(dirname(this.path), { recursive: true });
239
+ writeJsonFileAtomic(this.path, registry);
240
+ }
241
+ }
242
+ //# sourceMappingURL=registry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/adapters/registry.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ /**
2
+ * 崩溃安全的 JSON 文件读写工具
3
+ *
4
+ * 解决系统强制关机/崩溃时 JSON 索引文件被截断导致数据丢失的问题。
5
+ * - 写入:write-to-temp → rename(POSIX 原子操作)+ .bak 备份
6
+ * - 读取:主文件 → .tmp 残留 → .bak 回退,多层容错
7
+ */
8
+ /**
9
+ * 原子写入 JSON 文件:write-to-temp → rename
10
+ * 写入前自动保留 .bak 备份
11
+ */
12
+ export declare function writeJsonFileAtomic(filePath: string, data: object, skipBackup?: boolean): void;
13
+ /** 原子重写文本文件(用于 JSONL 会话等非单个 JSON 文档)。 */
14
+ export declare function writeTextFileAtomic(filePath: string, content: string): void;
15
+ /**
16
+ * 安全读取 JSON 索引文件
17
+ * 优先读主文件,损坏则尝试 .tmp / .bak,都失败返回 null
18
+ */
19
+ export declare function readJsonFileSafe<T>(filePath: string): T | null;
@@ -0,0 +1,94 @@
1
+ /**
2
+ * 崩溃安全的 JSON 文件读写工具
3
+ *
4
+ * 解决系统强制关机/崩溃时 JSON 索引文件被截断导致数据丢失的问题。
5
+ * - 写入:write-to-temp → rename(POSIX 原子操作)+ .bak 备份
6
+ * - 读取:主文件 → .tmp 残留 → .bak 回退,多层容错
7
+ */
8
+ import { writeFileSync, renameSync, existsSync, copyFileSync, readFileSync, unlinkSync } from 'node:fs';
9
+ /**
10
+ * 原子写入 JSON 文件:write-to-temp → rename
11
+ * 写入前自动保留 .bak 备份
12
+ */
13
+ export function writeJsonFileAtomic(filePath, data, skipBackup = false) {
14
+ const tmpPath = filePath + '.tmp';
15
+ const bakPath = filePath + '.bak';
16
+ // 备份当前文件(如果存在且可读)
17
+ if (!skipBackup && existsSync(filePath)) {
18
+ try {
19
+ copyFileSync(filePath, bakPath);
20
+ }
21
+ catch {
22
+ // 备份失败不阻塞写入
23
+ }
24
+ }
25
+ // 写入临时文件
26
+ writeFileSync(tmpPath, JSON.stringify(data, null, 2), 'utf-8');
27
+ // 原子重命名(POSIX rename 是原子操作)
28
+ renameSync(tmpPath, filePath);
29
+ }
30
+ /** 原子重写文本文件(用于 JSONL 会话等非单个 JSON 文档)。 */
31
+ export function writeTextFileAtomic(filePath, content) {
32
+ const tmpPath = filePath + '.tmp';
33
+ writeFileSync(tmpPath, content, 'utf-8');
34
+ renameSync(tmpPath, filePath);
35
+ }
36
+ /**
37
+ * 安全读取 JSON 索引文件
38
+ * 优先读主文件,损坏则尝试 .tmp / .bak,都失败返回 null
39
+ */
40
+ export function readJsonFileSafe(filePath) {
41
+ const tmpPath = filePath + '.tmp';
42
+ const bakPath = filePath + '.bak';
43
+ // 1. 尝试读取主文件
44
+ if (existsSync(filePath)) {
45
+ try {
46
+ const raw = readFileSync(filePath, 'utf-8');
47
+ if (raw.trim().length > 0) {
48
+ return JSON.parse(raw);
49
+ }
50
+ }
51
+ catch {
52
+ console.warn(`[数据恢复] 主索引文件损坏: ${filePath}`);
53
+ }
54
+ }
55
+ // 2. 检查是否有未完成的 .tmp 文件(上次 rename 前崩溃)
56
+ if (existsSync(tmpPath)) {
57
+ try {
58
+ const raw = readFileSync(tmpPath, 'utf-8');
59
+ if (raw.trim().length > 0) {
60
+ const parsed = JSON.parse(raw);
61
+ // .tmp 有效 → 提升为主文件
62
+ renameSync(tmpPath, filePath);
63
+ console.log(`[数据恢复] 从 .tmp 文件恢复: ${filePath}`);
64
+ return parsed;
65
+ }
66
+ }
67
+ catch {
68
+ // .tmp 也损坏,继续 fallback
69
+ }
70
+ // 清理无效的 .tmp
71
+ try {
72
+ unlinkSync(tmpPath);
73
+ }
74
+ catch { /* ignore */ }
75
+ }
76
+ // 3. Fallback 到 .bak
77
+ if (existsSync(bakPath)) {
78
+ try {
79
+ const raw = readFileSync(bakPath, 'utf-8');
80
+ if (raw.trim().length > 0) {
81
+ const parsed = JSON.parse(raw);
82
+ // 用 .bak 恢复主文件(跳过备份,避免用损坏的主文件覆盖好的 .bak)
83
+ writeJsonFileAtomic(filePath, parsed, true);
84
+ console.log(`[数据恢复] 从 .bak 文件恢复: ${filePath}`);
85
+ return parsed;
86
+ }
87
+ }
88
+ catch {
89
+ console.error(`[数据恢复] .bak 文件也损坏: ${bakPath}`);
90
+ }
91
+ }
92
+ return null; // 全部失败,需要上层从 JSONL 重建
93
+ }
94
+ //# sourceMappingURL=safe-file.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"safe-file.js","sourceRoot":"","sources":["../../src/adapters/safe-file.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * `/worktree` human command surface: create, list, apply, finish, discard,
3
+ * remove. Commands run against the receiving agent's session like the tools,
4
+ * but are user-initiated, so destructive verbs carry their own confirmation
5
+ * semantics (dirty discard is the user's own command).
6
+ * @module dsh-git-worktree/commands
7
+ */
8
+ import type { Context } from '@deepseek-ai/cordis';
9
+ import '@deepseek-ai/dsh-commands';
10
+ import type { SessionCheckoutModule } from './index.js';
11
+ /** Register the `/worktree` command. */
12
+ export declare function registerWorktreeCommand(ctx: Context, module: SessionCheckoutModule): void;
@@ -0,0 +1,121 @@
1
+ /**
2
+ * `/worktree` human command surface: create, list, apply, finish, discard,
3
+ * remove. Commands run against the receiving agent's session like the tools,
4
+ * but are user-initiated, so destructive verbs carry their own confirmation
5
+ * semantics (dirty discard is the user's own command).
6
+ * @module dsh-git-worktree/commands
7
+ */
8
+ import '@deepseek-ai/dsh-commands';
9
+ function targetText(target) {
10
+ const lines = [
11
+ `checkout: ${target.checkout.label} (${target.checkout.kind}, phase ${target.checkout.phase})`,
12
+ `dirty: ${target.dirty}`,
13
+ `current: ${target.current.branch ?? 'detached'} @ ${target.current.oid.slice(0, 7)}`,
14
+ ];
15
+ if (target.delivery)
16
+ lines.push(`delivery: ${target.delivery.state}`);
17
+ return lines.join('\n');
18
+ }
19
+ function sessionIdOf(agent) {
20
+ const session = agent?.session;
21
+ if (!session || typeof session.id !== 'string')
22
+ throw new Error('`/worktree` 只能在 DSH Agent 会话中使用');
23
+ return session.id;
24
+ }
25
+ const USAGE = 'Usage: /worktree create | list | apply | finish <message> | discard | remove <checkoutId>';
26
+ /** Register the `/worktree` command. */
27
+ export function registerWorktreeCommand(ctx, module) {
28
+ ctx.commands.register({
29
+ name: 'worktree',
30
+ description: 'manage Domi-grade git worktrees (create / list / apply / finish / discard / remove)',
31
+ input: { hint: 'create | list | apply | finish <message> | discard | remove <checkoutId>' },
32
+ handler: async (invocation) => {
33
+ const tokens = invocation.rawInput.trim().split(/\s+/u).filter(Boolean);
34
+ const verb = tokens[0] ?? 'list';
35
+ const sessionId = sessionIdOf(invocation.agent);
36
+ try {
37
+ switch (verb) {
38
+ case 'create': {
39
+ const target = await module.bind(sessionId, { kind: 'isolated' });
40
+ return { kind: 'success', text: `Worktree created:\n${targetText(target)}\n打开新会话并选择该工作区以在其中工作。` };
41
+ }
42
+ case 'list': {
43
+ const summaries = await module.listManagedWorktrees();
44
+ if (summaries.length === 0)
45
+ return { kind: 'success', text: 'No managed worktrees.' };
46
+ return {
47
+ kind: 'success',
48
+ text: summaries.map((summary) => (`${summary.checkoutId} ${summary.project.name} i${summary.iteration} ${summary.state}/${summary.phase} dirty=${summary.dirty}`)).join('\n'),
49
+ };
50
+ }
51
+ case 'apply': {
52
+ const target = await module.inspect(sessionId);
53
+ const result = await module.operate({ action: 'apply', sessionId, expectedRevision: target.revision });
54
+ if (result.status === 'applied')
55
+ return { kind: 'success', text: `Applied ${result.changedFiles.length} file(s) to Local.` };
56
+ if (result.status === 'conflict') {
57
+ return { kind: 'error', text: `Conflict: ${result.conflictingFiles.join('、')}\nLocal HEAD: ${result.localHeadOid.slice(0, 7)} — 在 worktree 内同步并解决后重试。` };
58
+ }
59
+ if (result.status === 'error')
60
+ return { kind: 'error', text: `${result.code}: ${result.message}` };
61
+ return { kind: 'error', text: 'Apply 未返回预期结果' };
62
+ }
63
+ case 'finish': {
64
+ const commitMessage = tokens.slice(1).join(' ').trim();
65
+ if (!commitMessage)
66
+ return { kind: 'error', text: USAGE };
67
+ const target = await module.inspect(sessionId);
68
+ const result = await module.operate({
69
+ action: 'finish',
70
+ sessionId,
71
+ expectedRevision: target.revision,
72
+ commitMessage,
73
+ });
74
+ if (result.status === 'finished') {
75
+ return { kind: 'success', text: `Finished: committed ${result.changedFiles.length} file(s) as ${result.commitOid?.slice(0, 7) ?? 'no-op'} (cleanup: ${result.cleanup}).` };
76
+ }
77
+ if (result.status === 'error')
78
+ return { kind: 'error', text: `${result.code}: ${result.message}` };
79
+ return { kind: 'error', text: 'Finish 未返回预期结果' };
80
+ }
81
+ case 'discard': {
82
+ const target = await module.inspect(sessionId);
83
+ const result = await module.operate({
84
+ action: 'discard',
85
+ sessionId,
86
+ expectedRevision: target.revision,
87
+ confirmDirty: true,
88
+ });
89
+ return result.status === 'discarded'
90
+ ? { kind: 'success', text: 'Worktree discarded.' }
91
+ : result.status === 'error'
92
+ ? { kind: 'error', text: `${result.code}: ${result.message}` }
93
+ : { kind: 'error', text: 'Discard 未返回预期结果' };
94
+ }
95
+ case 'remove': {
96
+ const checkoutId = tokens[1];
97
+ if (!checkoutId)
98
+ return { kind: 'error', text: USAGE };
99
+ const summaries = await module.listManagedWorktrees({ checkoutId });
100
+ const summary = summaries[0];
101
+ if (!summary)
102
+ return { kind: 'error', text: `No managed worktree ${checkoutId}.` };
103
+ await module.manageManagedWorktree({
104
+ checkoutId,
105
+ expectedRevision: summary.revision,
106
+ action: 'discard',
107
+ confirmDirty: true,
108
+ });
109
+ return { kind: 'success', text: `Removed worktree ${checkoutId}.` };
110
+ }
111
+ default:
112
+ return { kind: 'error', text: `Unknown verb ${JSON.stringify(verb)}.\n${USAGE}` };
113
+ }
114
+ }
115
+ catch (error) {
116
+ return { kind: 'error', text: error instanceof Error ? error.message : String(error) };
117
+ }
118
+ },
119
+ });
120
+ }
121
+ //# sourceMappingURL=commands.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"commands.js","sourceRoot":"","sources":["../src/commands.ts"],"names":[],"mappings":"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"}
package/lib/index.d.ts ADDED
@@ -0,0 +1,76 @@
1
+ /**
2
+ * dsh-git-worktree plugin entry. The session-checkout domain (state machine,
3
+ * apply engine, ports) is Domi-ported and host-agnostic; this file is the DSH
4
+ * plugin face: exports the domain error type and the module contract, builds
5
+ * the DSH adapters, mounts the state machine, and registers the model tools,
6
+ * `/worktree` command, and session context note.
7
+ * @module dsh-git-worktree
8
+ */
9
+ import type { Context } from '@deepseek-ai/cordis';
10
+ import type { ManagedWorktreeSummaryView, BulkCleanupManagedWorktreeCandidate, BulkCleanupManagedWorktreesResult, SessionCheckoutErrorCode, SessionCheckoutOperation, SessionCheckoutOperationResult, SessionTargetBindChoice, SessionTargetView, WorktreeRetentionMode, WorktreeValidationItem, WorktreeValidationStatus, WorktreeApplyPreflightView } from './types.js';
11
+ /** Stable domain failure raised by the session-checkout state machine and its adapters. */
12
+ export declare class SessionCheckoutError extends Error {
13
+ readonly code: SessionCheckoutErrorCode;
14
+ /**
15
+ * @param code - stable error code from `SESSION_CHECKOUT_ERROR_CODES`.
16
+ * @param message - user-facing message, surfaced verbatim by tools.
17
+ */
18
+ constructor(code: SessionCheckoutErrorCode, message: string);
19
+ }
20
+ export interface SessionCheckoutReconcileSummary {
21
+ recoveryRequiredCheckoutIds: string[];
22
+ orphanedCheckoutIds: string[];
23
+ dirtyOrphanedCheckoutIds: string[];
24
+ retainedCheckoutCount: number;
25
+ }
26
+ export interface ManageManagedWorktreeInput {
27
+ checkoutId: string;
28
+ expectedRevision: number;
29
+ action: 'cleanup_retained' | 'retry_cleanup' | 'set_retention' | 'discard';
30
+ retention?: Exclude<WorktreeRetentionMode, 'cleanup'>;
31
+ confirmDirty?: boolean;
32
+ }
33
+ export interface ListManagedWorktreesInput {
34
+ projectId?: string;
35
+ needsAttention?: boolean;
36
+ checkoutId?: string;
37
+ includeDiagnostics?: boolean;
38
+ }
39
+ export interface MarkReadyForReviewInput {
40
+ detailsMarkdown?: string;
41
+ summary: string;
42
+ validationStatus: WorktreeValidationStatus;
43
+ validationSummary?: string;
44
+ tests: WorktreeValidationItem[];
45
+ suggestedCommitMessage: string;
46
+ }
47
+ /** Complete core interface the session-checkout module exposes to business callers. */
48
+ export interface SessionCheckoutModule {
49
+ inspect(sessionId: string): Promise<SessionTargetView>;
50
+ /** Read-only sync preflight; modifies no Local, worktree, registry, review state, or Git refs. */
51
+ preflight?(sessionId: string, expectedRevision: number): Promise<WorktreeApplyPreflightView>;
52
+ /** Cross-module mutation under the same exclusive lock as bind/apply/discard/cleanup. */
53
+ runExclusiveSessionMutation<T>(sessionId: string, operation: (target: SessionTargetView) => Promise<T>): Promise<T>;
54
+ bind(sessionId: string, choice: SessionTargetBindChoice): Promise<SessionTargetView>;
55
+ /** Lazily create the next isolated checkout when a delivered owner session needs code changes. */
56
+ beginNextIteration(sessionId: string): Promise<SessionTargetView>;
57
+ markReadyForReview(sessionId: string, input: MarkReadyForReviewInput): Promise<SessionTargetView>;
58
+ operate(input: SessionCheckoutOperation): Promise<SessionCheckoutOperationResult>;
59
+ listManagedWorktrees(input?: ListManagedWorktreesInput): Promise<ManagedWorktreeSummaryView[]>;
60
+ /** Main-owned read-only cleanup inspection; writes no registry, refs, or directories. */
61
+ inspectManagedWorktreeCleanup(input?: ListManagedWorktreesInput): Promise<ManagedWorktreeSummaryView[]>;
62
+ bulkCleanupManagedWorktrees(candidates: BulkCleanupManagedWorktreeCandidate[]): Promise<BulkCleanupManagedWorktreesResult>;
63
+ manageManagedWorktree(input: ManageManagedWorktreeInput): Promise<ManagedWorktreeSummaryView>;
64
+ /** Resolve a managed checkout's canonical root after identity validation (workspace registration). */
65
+ resolveManagedRoot(checkoutId: string): Promise<string>;
66
+ cleanupExpiredRetained(now?: number): Promise<string[]>;
67
+ reconcile(): Promise<SessionCheckoutReconcileSummary>;
68
+ }
69
+ /**
70
+ * Mount the plugin: DSH adapters, the session-checkout module, the seven
71
+ * worktree tools, the `/worktree` command, the once-per-session worktree
72
+ * context note, startup reconciliation, and the retention-expiry timer.
73
+ */
74
+ export declare function apply(ctx: Context, config?: {
75
+ stateDir?: string;
76
+ }): void;