dmux 3.4.3 → 3.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/dist/actions/implementations/mergeAction.d.ts +3 -1
- package/dist/actions/implementations/mergeAction.d.ts.map +1 -1
- package/dist/actions/implementations/mergeAction.js +38 -4
- package/dist/actions/implementations/mergeAction.js.map +1 -1
- package/dist/actions/merge/multiMergeOrchestrator.d.ts +22 -0
- package/dist/actions/merge/multiMergeOrchestrator.d.ts.map +1 -0
- package/dist/actions/merge/multiMergeOrchestrator.js +452 -0
- package/dist/actions/merge/multiMergeOrchestrator.js.map +1 -0
- package/dist/actions/merge/types.d.ts +62 -0
- package/dist/actions/merge/types.d.ts.map +1 -0
- package/dist/actions/merge/types.js +7 -0
- package/dist/actions/merge/types.js.map +1 -0
- package/dist/utils/generated-agents-doc.d.ts +1 -1
- package/dist/utils/generated-agents-doc.js +1 -1
- package/dist/utils/hooksDocs.d.ts +1 -1
- package/dist/utils/worktreeDiscovery.d.ts +39 -0
- package/dist/utils/worktreeDiscovery.d.ts.map +1 -0
- package/dist/utils/worktreeDiscovery.js +241 -0
- package/dist/utils/worktreeDiscovery.js.map +1 -0
- package/package.json +1 -1
|
@@ -2,11 +2,13 @@
|
|
|
2
2
|
* MERGE Action - Merge a worktree into the main branch with comprehensive pre-checks
|
|
3
3
|
*
|
|
4
4
|
* This is the simplified orchestrator that delegates to specialized modules.
|
|
5
|
+
* Supports multi-merge: detects sub-worktrees and merges them all sequentially.
|
|
5
6
|
*/
|
|
6
7
|
import type { DmuxPane } from '../../types.js';
|
|
7
8
|
import type { ActionResult, ActionContext } from '../types.js';
|
|
8
9
|
/**
|
|
9
|
-
* Merge a worktree into the main branch with comprehensive pre-checks
|
|
10
|
+
* Merge a worktree into the main branch with comprehensive pre-checks.
|
|
11
|
+
* Supports multi-merge: if sub-worktrees exist, merges all of them sequentially.
|
|
10
12
|
*/
|
|
11
13
|
export declare function mergePane(pane: DmuxPane, context: ActionContext, params?: {
|
|
12
14
|
mainBranch?: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mergeAction.d.ts","sourceRoot":"","sources":["../../../src/actions/implementations/mergeAction.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"mergeAction.d.ts","sourceRoot":"","sources":["../../../src/actions/implementations/mergeAction.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC/C,OAAO,KAAK,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAU/D;;;GAGG;AACH,wBAAsB,SAAS,CAC7B,IAAI,EAAE,QAAQ,EACd,OAAO,EAAE,aAAa,EACtB,MAAM,CAAC,EAAE;IAAE,UAAU,CAAC,EAAE,MAAM,CAAA;CAAE,GAC/B,OAAO,CAAC,YAAY,CAAC,CA4CvB"}
|
|
@@ -2,12 +2,14 @@
|
|
|
2
2
|
* MERGE Action - Merge a worktree into the main branch with comprehensive pre-checks
|
|
3
3
|
*
|
|
4
4
|
* This is the simplified orchestrator that delegates to specialized modules.
|
|
5
|
+
* Supports multi-merge: detects sub-worktrees and merges them all sequentially.
|
|
5
6
|
*/
|
|
6
7
|
import { triggerHook } from '../../utils/hooks.js';
|
|
7
8
|
import { executeMerge } from '../merge/mergeExecution.js';
|
|
8
9
|
import { handleNothingToMerge, handleMainDirty, handleWorktreeUncommitted, handleMergeConflict, } from '../merge/issueHandlers/index.js';
|
|
9
10
|
/**
|
|
10
|
-
* Merge a worktree into the main branch with comprehensive pre-checks
|
|
11
|
+
* Merge a worktree into the main branch with comprehensive pre-checks.
|
|
12
|
+
* Supports multi-merge: if sub-worktrees exist, merges all of them sequentially.
|
|
11
13
|
*/
|
|
12
14
|
export async function mergePane(pane, context, params) {
|
|
13
15
|
// 1. Validation
|
|
@@ -18,15 +20,47 @@ export async function mergePane(pane, context, params) {
|
|
|
18
20
|
dismissable: true,
|
|
19
21
|
};
|
|
20
22
|
}
|
|
21
|
-
// 2.
|
|
23
|
+
// 2. Detect all worktrees (including sub-worktrees created by hooks)
|
|
24
|
+
const { detectAllWorktrees } = await import('../../utils/worktreeDiscovery.js');
|
|
25
|
+
const worktrees = detectAllWorktrees(pane.worktreePath);
|
|
26
|
+
console.error(`[mergeAction] Detected ${worktrees.length} worktree(s) in ${pane.worktreePath}`);
|
|
27
|
+
for (const wt of worktrees) {
|
|
28
|
+
console.error(`[mergeAction] - ${wt.repoName} (${wt.branch}) at ${wt.relativePath} [depth=${wt.depth}, isRoot=${wt.isRoot}]`);
|
|
29
|
+
}
|
|
30
|
+
// 3. Build merge queue (only worktrees with changes)
|
|
31
|
+
const { buildMergeQueue, executeMultiMerge } = await import('../merge/multiMergeOrchestrator.js');
|
|
32
|
+
const queue = await buildMergeQueue(worktrees);
|
|
33
|
+
console.error(`[mergeAction] Merge queue has ${queue.length} item(s)`);
|
|
34
|
+
// 4. Handle based on queue size
|
|
35
|
+
// No changes anywhere
|
|
36
|
+
if (queue.length === 0) {
|
|
37
|
+
return {
|
|
38
|
+
type: 'info',
|
|
39
|
+
message: 'No changes to merge in any repository',
|
|
40
|
+
dismissable: true,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
// Single root worktree = use existing flow (backwards compatible)
|
|
44
|
+
if (queue.length === 1 && queue[0].worktree.isRoot) {
|
|
45
|
+
console.error('[mergeAction] Single root worktree - using existing flow');
|
|
46
|
+
return executeSingleRootMerge(pane, context, params);
|
|
47
|
+
}
|
|
48
|
+
// Multiple worktrees or only sub-worktrees = use multi-merge flow
|
|
49
|
+
console.error('[mergeAction] Multiple worktrees or sub-worktrees - using multi-merge flow');
|
|
50
|
+
return executeMultiMerge(pane, context, queue);
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Execute single root worktree merge (original flow, backwards compatible)
|
|
54
|
+
*/
|
|
55
|
+
async function executeSingleRootMerge(pane, context, params) {
|
|
22
56
|
const { validateMerge } = await import('../../utils/mergeValidation.js');
|
|
23
57
|
const mainRepoPath = pane.worktreePath.replace(/\/\.dmux\/worktrees\/[^/]+$/, '');
|
|
24
58
|
const validation = validateMerge(mainRepoPath, pane.worktreePath, pane.slug);
|
|
25
|
-
//
|
|
59
|
+
// Handle detected issues
|
|
26
60
|
if (!validation.canMerge) {
|
|
27
61
|
return handleMergeIssues(pane, context, validation, mainRepoPath);
|
|
28
62
|
}
|
|
29
|
-
//
|
|
63
|
+
// No issues detected, proceed with merge confirmation
|
|
30
64
|
return {
|
|
31
65
|
type: 'confirm',
|
|
32
66
|
title: 'Merge Worktree',
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mergeAction.js","sourceRoot":"","sources":["../../../src/actions/implementations/mergeAction.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"mergeAction.js","sourceRoot":"","sources":["../../../src/actions/implementations/mergeAction.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Multi-Merge Orchestrator
|
|
3
|
+
*
|
|
4
|
+
* Coordinates merging multiple worktrees in sequence with proper
|
|
5
|
+
* dialog handling and error recovery.
|
|
6
|
+
*/
|
|
7
|
+
import type { ActionResult, ActionContext } from '../types.js';
|
|
8
|
+
import type { DmuxPane } from '../../types.js';
|
|
9
|
+
import type { WorktreeInfo, MergeQueueItem } from './types.js';
|
|
10
|
+
/**
|
|
11
|
+
* Build the merge queue from detected worktrees
|
|
12
|
+
* - Runs validation on each worktree
|
|
13
|
+
* - Filters to only those with changes to merge
|
|
14
|
+
* - Already sorted by depth (deepest first from detectAllWorktrees)
|
|
15
|
+
*/
|
|
16
|
+
export declare function buildMergeQueue(worktrees: WorktreeInfo[]): Promise<MergeQueueItem[]>;
|
|
17
|
+
/**
|
|
18
|
+
* Execute multi-merge with sequential dialogs
|
|
19
|
+
* Returns ActionResult that chains through each merge
|
|
20
|
+
*/
|
|
21
|
+
export declare function executeMultiMerge(pane: DmuxPane, context: ActionContext, queue: MergeQueueItem[]): Promise<ActionResult>;
|
|
22
|
+
//# sourceMappingURL=multiMergeOrchestrator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"multiMergeOrchestrator.d.ts","sourceRoot":"","sources":["../../../src/actions/merge/multiMergeOrchestrator.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC/D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC/C,OAAO,KAAK,EAAE,YAAY,EAAE,cAAc,EAAoB,MAAM,YAAY,CAAC;AAIjF;;;;;GAKG;AACH,wBAAsB,eAAe,CACnC,SAAS,EAAE,YAAY,EAAE,GACxB,OAAO,CAAC,cAAc,EAAE,CAAC,CA0B3B;AAED;;;GAGG;AACH,wBAAsB,iBAAiB,CACrC,IAAI,EAAE,QAAQ,EACd,OAAO,EAAE,aAAa,EACtB,KAAK,EAAE,cAAc,EAAE,GACtB,OAAO,CAAC,YAAY,CAAC,CAGvB"}
|
|
@@ -0,0 +1,452 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Multi-Merge Orchestrator
|
|
3
|
+
*
|
|
4
|
+
* Coordinates merging multiple worktrees in sequence with proper
|
|
5
|
+
* dialog handling and error recovery.
|
|
6
|
+
*/
|
|
7
|
+
import { getWorktreeDisplayLabel } from '../../utils/worktreeDiscovery.js';
|
|
8
|
+
/**
|
|
9
|
+
* Build the merge queue from detected worktrees
|
|
10
|
+
* - Runs validation on each worktree
|
|
11
|
+
* - Filters to only those with changes to merge
|
|
12
|
+
* - Already sorted by depth (deepest first from detectAllWorktrees)
|
|
13
|
+
*/
|
|
14
|
+
export async function buildMergeQueue(worktrees) {
|
|
15
|
+
const { validateMerge } = await import('../../utils/mergeValidation.js');
|
|
16
|
+
const queue = [];
|
|
17
|
+
for (const worktree of worktrees) {
|
|
18
|
+
const validation = validateMerge(worktree.parentRepoPath, worktree.worktreePath, worktree.branch);
|
|
19
|
+
// Include if there's something to merge (has issues that can be resolved, or can merge)
|
|
20
|
+
// Exclude only if the ONLY issue is 'nothing_to_merge'
|
|
21
|
+
const hasNothingToMerge = validation.issues.some(i => i.type === 'nothing_to_merge');
|
|
22
|
+
const hasOnlyNothingToMerge = validation.issues.length === 1 && hasNothingToMerge;
|
|
23
|
+
if (!hasOnlyNothingToMerge) {
|
|
24
|
+
queue.push({
|
|
25
|
+
worktree,
|
|
26
|
+
validation,
|
|
27
|
+
status: 'pending',
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return queue;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Execute multi-merge with sequential dialogs
|
|
35
|
+
* Returns ActionResult that chains through each merge
|
|
36
|
+
*/
|
|
37
|
+
export async function executeMultiMerge(pane, context, queue) {
|
|
38
|
+
// Start with confirmation dialog
|
|
39
|
+
return showMultiMergeConfirmation(pane, context, queue);
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Show initial confirmation dialog listing all worktrees to merge
|
|
43
|
+
*/
|
|
44
|
+
function showMultiMergeConfirmation(pane, context, queue) {
|
|
45
|
+
const worktreeList = queue
|
|
46
|
+
.map((item, idx) => {
|
|
47
|
+
const label = getWorktreeDisplayLabel(item.worktree);
|
|
48
|
+
const issues = item.validation.issues
|
|
49
|
+
.filter(i => i.type !== 'nothing_to_merge')
|
|
50
|
+
.map(i => i.type.replace(/_/g, ' '))
|
|
51
|
+
.join(', ');
|
|
52
|
+
return ` ${idx + 1}. ${label}${issues ? ` [${issues}]` : ''}`;
|
|
53
|
+
})
|
|
54
|
+
.join('\n');
|
|
55
|
+
return {
|
|
56
|
+
type: 'confirm',
|
|
57
|
+
title: 'Multi-Repository Merge',
|
|
58
|
+
message: `Merge ${queue.length} repositor${queue.length === 1 ? 'y' : 'ies'}?\n\n${worktreeList}`,
|
|
59
|
+
confirmLabel: 'Start Merge',
|
|
60
|
+
cancelLabel: 'Cancel',
|
|
61
|
+
onConfirm: async () => {
|
|
62
|
+
// Initialize result tracking
|
|
63
|
+
const result = {
|
|
64
|
+
totalWorktrees: queue.length,
|
|
65
|
+
successful: 0,
|
|
66
|
+
failed: 0,
|
|
67
|
+
skipped: 0,
|
|
68
|
+
results: [],
|
|
69
|
+
};
|
|
70
|
+
// Start processing the queue
|
|
71
|
+
return processNextInQueue(pane, context, queue, 0, result);
|
|
72
|
+
},
|
|
73
|
+
onCancel: async () => {
|
|
74
|
+
return {
|
|
75
|
+
type: 'info',
|
|
76
|
+
message: 'Multi-merge cancelled',
|
|
77
|
+
dismissable: true,
|
|
78
|
+
};
|
|
79
|
+
},
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Process the next item in the merge queue
|
|
84
|
+
*/
|
|
85
|
+
async function processNextInQueue(pane, context, queue, currentIndex, result) {
|
|
86
|
+
// Check if we're done
|
|
87
|
+
if (currentIndex >= queue.length) {
|
|
88
|
+
return showMultiMergeSummary(pane, context, result);
|
|
89
|
+
}
|
|
90
|
+
const item = queue[currentIndex];
|
|
91
|
+
const worktreeLabel = getWorktreeDisplayLabel(item.worktree);
|
|
92
|
+
// Show progress
|
|
93
|
+
console.error(`[multiMerge] Processing ${currentIndex + 1}/${queue.length}: ${worktreeLabel}`);
|
|
94
|
+
// Update status
|
|
95
|
+
item.status = 'in_progress';
|
|
96
|
+
// Execute the merge for this worktree
|
|
97
|
+
return executeSingleWorktreeMerge(pane, context, item, currentIndex, queue.length, async (success, error) => {
|
|
98
|
+
// Record result
|
|
99
|
+
if (success) {
|
|
100
|
+
item.status = 'completed';
|
|
101
|
+
result.successful++;
|
|
102
|
+
result.results.push({
|
|
103
|
+
worktree: item.worktree,
|
|
104
|
+
status: 'completed',
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
else if (error === 'skipped') {
|
|
108
|
+
item.status = 'skipped';
|
|
109
|
+
result.skipped++;
|
|
110
|
+
result.results.push({
|
|
111
|
+
worktree: item.worktree,
|
|
112
|
+
status: 'skipped',
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
else {
|
|
116
|
+
item.status = 'failed';
|
|
117
|
+
item.error = error;
|
|
118
|
+
result.failed++;
|
|
119
|
+
result.results.push({
|
|
120
|
+
worktree: item.worktree,
|
|
121
|
+
status: 'failed',
|
|
122
|
+
error,
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
// Process next item
|
|
126
|
+
return processNextInQueue(pane, context, queue, currentIndex + 1, result);
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Execute merge for a single worktree in the queue
|
|
131
|
+
*/
|
|
132
|
+
async function executeSingleWorktreeMerge(pane, context, item, currentIndex, totalCount, onComplete) {
|
|
133
|
+
const { validation, worktree } = item;
|
|
134
|
+
const worktreeLabel = getWorktreeDisplayLabel(worktree);
|
|
135
|
+
const progressPrefix = `[${currentIndex + 1}/${totalCount}] ${worktreeLabel}`;
|
|
136
|
+
// Handle issues first (same as single merge flow)
|
|
137
|
+
if (!validation.canMerge) {
|
|
138
|
+
return handleWorktreeMergeIssues(pane, context, item, progressPrefix, onComplete);
|
|
139
|
+
}
|
|
140
|
+
// No issues - show confirmation for this worktree
|
|
141
|
+
return {
|
|
142
|
+
type: 'confirm',
|
|
143
|
+
title: progressPrefix,
|
|
144
|
+
message: `Merge "${worktree.branch}" into ${validation.mainBranch}?`,
|
|
145
|
+
confirmLabel: 'Merge',
|
|
146
|
+
cancelLabel: 'Skip',
|
|
147
|
+
onConfirm: async () => {
|
|
148
|
+
return performWorktreeMerge(pane, context, item, progressPrefix, onComplete);
|
|
149
|
+
},
|
|
150
|
+
onCancel: async () => {
|
|
151
|
+
// Skip this worktree, continue with others
|
|
152
|
+
return onComplete(false, 'skipped');
|
|
153
|
+
},
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Handle merge issues for a single worktree in multi-merge context
|
|
158
|
+
*/
|
|
159
|
+
async function handleWorktreeMergeIssues(pane, context, item, progressPrefix, onComplete) {
|
|
160
|
+
const { validation, worktree } = item;
|
|
161
|
+
const { issues, mainBranch } = validation;
|
|
162
|
+
// Create retry function for this specific worktree
|
|
163
|
+
const retryMerge = async () => {
|
|
164
|
+
// Re-validate
|
|
165
|
+
const { validateMerge } = await import('../../utils/mergeValidation.js');
|
|
166
|
+
item.validation = validateMerge(worktree.parentRepoPath, worktree.worktreePath, worktree.branch);
|
|
167
|
+
if (item.validation.canMerge) {
|
|
168
|
+
return performWorktreeMerge(pane, context, item, progressPrefix, onComplete);
|
|
169
|
+
}
|
|
170
|
+
else {
|
|
171
|
+
return handleWorktreeMergeIssues(pane, context, item, progressPrefix, onComplete);
|
|
172
|
+
}
|
|
173
|
+
};
|
|
174
|
+
// Check for nothing to merge
|
|
175
|
+
const nothingToMerge = issues.find(i => i.type === 'nothing_to_merge');
|
|
176
|
+
if (nothingToMerge) {
|
|
177
|
+
return onComplete(false, 'skipped');
|
|
178
|
+
}
|
|
179
|
+
// Check for main dirty
|
|
180
|
+
const mainDirty = issues.find(i => i.type === 'main_dirty');
|
|
181
|
+
if (mainDirty) {
|
|
182
|
+
return handleMainDirtyForWorktree(item, progressPrefix, mainBranch, retryMerge, onComplete);
|
|
183
|
+
}
|
|
184
|
+
// Check for worktree uncommitted
|
|
185
|
+
const worktreeUncommitted = issues.find(i => i.type === 'worktree_uncommitted');
|
|
186
|
+
if (worktreeUncommitted) {
|
|
187
|
+
return handleUncommittedForWorktree(item, progressPrefix, retryMerge, onComplete);
|
|
188
|
+
}
|
|
189
|
+
// Check for merge conflict
|
|
190
|
+
const mergeConflict = issues.find(i => i.type === 'merge_conflict');
|
|
191
|
+
if (mergeConflict) {
|
|
192
|
+
return handleConflictForWorktree(pane, context, item, progressPrefix, mainBranch, onComplete);
|
|
193
|
+
}
|
|
194
|
+
// Unknown issue - skip with error
|
|
195
|
+
return onComplete(false, issues.map(i => i.message).join('; '));
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Handle main branch dirty for a worktree in multi-merge
|
|
199
|
+
*/
|
|
200
|
+
async function handleMainDirtyForWorktree(item, progressPrefix, mainBranch, retryMerge, onComplete) {
|
|
201
|
+
const { worktree, validation } = item;
|
|
202
|
+
const issue = validation.issues.find(i => i.type === 'main_dirty');
|
|
203
|
+
const files = issue.files || [];
|
|
204
|
+
return {
|
|
205
|
+
type: 'choice',
|
|
206
|
+
title: `${progressPrefix}: Main Branch Has Changes`,
|
|
207
|
+
message: `${mainBranch} in ${worktree.repoName} has uncommitted changes:\n${files.slice(0, 3).map(f => ` • ${f}`).join('\n')}${files.length > 3 ? '\n ...' : ''}`,
|
|
208
|
+
options: [
|
|
209
|
+
{
|
|
210
|
+
id: 'commit_automatic',
|
|
211
|
+
label: 'AI commit (automatic)',
|
|
212
|
+
description: 'Auto-generate and commit immediately',
|
|
213
|
+
default: true,
|
|
214
|
+
},
|
|
215
|
+
{
|
|
216
|
+
id: 'commit_ai_editable',
|
|
217
|
+
label: 'AI commit (editable)',
|
|
218
|
+
description: 'Generate message, edit before commit',
|
|
219
|
+
},
|
|
220
|
+
{
|
|
221
|
+
id: 'commit_manual',
|
|
222
|
+
label: 'Manual commit message',
|
|
223
|
+
description: 'Write your own commit message',
|
|
224
|
+
},
|
|
225
|
+
{
|
|
226
|
+
id: 'skip',
|
|
227
|
+
label: 'Skip this repo',
|
|
228
|
+
description: 'Continue with other repositories',
|
|
229
|
+
},
|
|
230
|
+
],
|
|
231
|
+
onSelect: async (optionId) => {
|
|
232
|
+
if (optionId === 'skip') {
|
|
233
|
+
return onComplete(false, 'skipped');
|
|
234
|
+
}
|
|
235
|
+
if (optionId === 'commit_automatic' ||
|
|
236
|
+
optionId === 'commit_ai_editable' ||
|
|
237
|
+
optionId === 'commit_manual') {
|
|
238
|
+
const { handleCommitWithOptions } = await import('./commitMessageHandler.js');
|
|
239
|
+
return handleCommitWithOptions(worktree.parentRepoPath, optionId, retryMerge);
|
|
240
|
+
}
|
|
241
|
+
return onComplete(false, 'Unknown option');
|
|
242
|
+
},
|
|
243
|
+
dismissable: false,
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
/**
|
|
247
|
+
* Handle uncommitted changes in worktree for multi-merge
|
|
248
|
+
*/
|
|
249
|
+
async function handleUncommittedForWorktree(item, progressPrefix, retryMerge, onComplete) {
|
|
250
|
+
const { worktree, validation } = item;
|
|
251
|
+
const issue = validation.issues.find(i => i.type === 'worktree_uncommitted');
|
|
252
|
+
const files = issue.files || [];
|
|
253
|
+
return {
|
|
254
|
+
type: 'choice',
|
|
255
|
+
title: `${progressPrefix}: Uncommitted Changes`,
|
|
256
|
+
message: `${worktree.repoName} worktree has uncommitted changes:\n${files.slice(0, 3).map(f => ` • ${f}`).join('\n')}${files.length > 3 ? '\n ...' : ''}`,
|
|
257
|
+
options: [
|
|
258
|
+
{
|
|
259
|
+
id: 'commit_automatic',
|
|
260
|
+
label: 'AI commit (automatic)',
|
|
261
|
+
description: 'Auto-generate and commit immediately',
|
|
262
|
+
default: true,
|
|
263
|
+
},
|
|
264
|
+
{
|
|
265
|
+
id: 'commit_ai_editable',
|
|
266
|
+
label: 'AI commit (editable)',
|
|
267
|
+
description: 'Generate message, edit before commit',
|
|
268
|
+
},
|
|
269
|
+
{
|
|
270
|
+
id: 'commit_manual',
|
|
271
|
+
label: 'Manual commit message',
|
|
272
|
+
description: 'Write your own commit message',
|
|
273
|
+
},
|
|
274
|
+
{
|
|
275
|
+
id: 'skip',
|
|
276
|
+
label: 'Skip this repo',
|
|
277
|
+
description: 'Continue with other repositories',
|
|
278
|
+
},
|
|
279
|
+
],
|
|
280
|
+
onSelect: async (optionId) => {
|
|
281
|
+
if (optionId === 'skip') {
|
|
282
|
+
return onComplete(false, 'skipped');
|
|
283
|
+
}
|
|
284
|
+
if (optionId === 'commit_automatic' ||
|
|
285
|
+
optionId === 'commit_ai_editable' ||
|
|
286
|
+
optionId === 'commit_manual') {
|
|
287
|
+
const { handleCommitWithOptions } = await import('./commitMessageHandler.js');
|
|
288
|
+
return handleCommitWithOptions(worktree.worktreePath, optionId, retryMerge);
|
|
289
|
+
}
|
|
290
|
+
return onComplete(false, 'Unknown option');
|
|
291
|
+
},
|
|
292
|
+
dismissable: false,
|
|
293
|
+
};
|
|
294
|
+
}
|
|
295
|
+
/**
|
|
296
|
+
* Handle merge conflict for a worktree in multi-merge
|
|
297
|
+
*/
|
|
298
|
+
async function handleConflictForWorktree(pane, context, item, progressPrefix, mainBranch, onComplete) {
|
|
299
|
+
const { worktree, validation } = item;
|
|
300
|
+
const issue = validation.issues.find(i => i.type === 'merge_conflict');
|
|
301
|
+
const files = issue.files || [];
|
|
302
|
+
return {
|
|
303
|
+
type: 'choice',
|
|
304
|
+
title: `${progressPrefix}: Merge Conflicts`,
|
|
305
|
+
message: `Conflicts detected in ${worktree.repoName}:\n${files.slice(0, 3).map(f => ` • ${f}`).join('\n')}${files.length > 3 ? '\n ...' : ''}`,
|
|
306
|
+
options: [
|
|
307
|
+
{
|
|
308
|
+
id: 'skip',
|
|
309
|
+
label: 'Skip this repo',
|
|
310
|
+
description: 'Resolve conflicts manually later',
|
|
311
|
+
default: true,
|
|
312
|
+
},
|
|
313
|
+
{
|
|
314
|
+
id: 'abort',
|
|
315
|
+
label: 'Stop multi-merge',
|
|
316
|
+
description: 'Abort remaining merges',
|
|
317
|
+
},
|
|
318
|
+
],
|
|
319
|
+
onSelect: async (optionId) => {
|
|
320
|
+
if (optionId === 'skip') {
|
|
321
|
+
return onComplete(false, 'skipped');
|
|
322
|
+
}
|
|
323
|
+
if (optionId === 'abort') {
|
|
324
|
+
return {
|
|
325
|
+
type: 'info',
|
|
326
|
+
title: 'Multi-Merge Aborted',
|
|
327
|
+
message: `Stopped at ${worktree.repoName} due to conflicts.`,
|
|
328
|
+
dismissable: true,
|
|
329
|
+
};
|
|
330
|
+
}
|
|
331
|
+
return onComplete(false, 'Unknown option');
|
|
332
|
+
},
|
|
333
|
+
dismissable: false,
|
|
334
|
+
};
|
|
335
|
+
}
|
|
336
|
+
/**
|
|
337
|
+
* Actually perform the merge for a worktree
|
|
338
|
+
*/
|
|
339
|
+
async function performWorktreeMerge(pane, context, item, progressPrefix, onComplete) {
|
|
340
|
+
const { worktree, validation } = item;
|
|
341
|
+
const { mainBranch } = validation;
|
|
342
|
+
const { mergeMainIntoWorktree, mergeWorktreeIntoMain } = await import('../../utils/mergeExecution.js');
|
|
343
|
+
const { triggerHook } = await import('../../utils/hooks.js');
|
|
344
|
+
// Trigger pre_merge hook
|
|
345
|
+
await triggerHook('pre_merge', worktree.parentRepoPath, pane, {
|
|
346
|
+
DMUX_TARGET_BRANCH: mainBranch,
|
|
347
|
+
DMUX_WORKTREE_PATH: worktree.worktreePath,
|
|
348
|
+
DMUX_REPO_NAME: worktree.repoName,
|
|
349
|
+
});
|
|
350
|
+
// Step 1: Merge main into worktree
|
|
351
|
+
const step1 = mergeMainIntoWorktree(worktree.worktreePath, mainBranch);
|
|
352
|
+
if (!step1.success) {
|
|
353
|
+
if (step1.needsManualResolution && step1.conflictFiles?.length) {
|
|
354
|
+
// Conflict occurred during merge
|
|
355
|
+
return {
|
|
356
|
+
type: 'choice',
|
|
357
|
+
title: `${progressPrefix}: Merge Conflict`,
|
|
358
|
+
message: `Conflict while merging ${mainBranch} into worktree:\n${step1.conflictFiles.slice(0, 3).map(f => ` • ${f}`).join('\n')}`,
|
|
359
|
+
options: [
|
|
360
|
+
{
|
|
361
|
+
id: 'skip',
|
|
362
|
+
label: 'Skip this repo',
|
|
363
|
+
description: 'Abort this merge, continue with others',
|
|
364
|
+
default: true,
|
|
365
|
+
},
|
|
366
|
+
{
|
|
367
|
+
id: 'abort_all',
|
|
368
|
+
label: 'Stop multi-merge',
|
|
369
|
+
description: 'Stop processing remaining repos',
|
|
370
|
+
},
|
|
371
|
+
],
|
|
372
|
+
onSelect: async (optionId) => {
|
|
373
|
+
const { abortMerge } = await import('../../utils/mergeExecution.js');
|
|
374
|
+
abortMerge(worktree.worktreePath);
|
|
375
|
+
if (optionId === 'abort_all') {
|
|
376
|
+
return {
|
|
377
|
+
type: 'info',
|
|
378
|
+
title: 'Multi-Merge Aborted',
|
|
379
|
+
message: `Stopped due to conflicts in ${worktree.repoName}`,
|
|
380
|
+
dismissable: true,
|
|
381
|
+
};
|
|
382
|
+
}
|
|
383
|
+
return onComplete(false, 'skipped');
|
|
384
|
+
},
|
|
385
|
+
dismissable: false,
|
|
386
|
+
};
|
|
387
|
+
}
|
|
388
|
+
return onComplete(false, `Merge failed: ${step1.error}`);
|
|
389
|
+
}
|
|
390
|
+
// Step 2: Merge worktree into main
|
|
391
|
+
const step2 = mergeWorktreeIntoMain(worktree.parentRepoPath, worktree.branch);
|
|
392
|
+
if (!step2.success) {
|
|
393
|
+
return onComplete(false, `Failed to merge into ${mainBranch}: ${step2.error}`);
|
|
394
|
+
}
|
|
395
|
+
// Trigger post_merge hook
|
|
396
|
+
await triggerHook('post_merge', worktree.parentRepoPath, pane, {
|
|
397
|
+
DMUX_TARGET_BRANCH: mainBranch,
|
|
398
|
+
DMUX_WORKTREE_PATH: worktree.worktreePath,
|
|
399
|
+
DMUX_REPO_NAME: worktree.repoName,
|
|
400
|
+
});
|
|
401
|
+
console.error(`[multiMerge] Successfully merged ${worktree.repoName}`);
|
|
402
|
+
return onComplete(true);
|
|
403
|
+
}
|
|
404
|
+
/**
|
|
405
|
+
* Show final summary of multi-merge operation
|
|
406
|
+
*/
|
|
407
|
+
function showMultiMergeSummary(pane, context, result) {
|
|
408
|
+
const summaryLines = result.results.map(r => {
|
|
409
|
+
const label = getWorktreeDisplayLabel(r.worktree);
|
|
410
|
+
const icon = r.status === 'completed' ? '✓' : r.status === 'skipped' ? '○' : '✗';
|
|
411
|
+
const suffix = r.error && r.status === 'failed' ? ` (${r.error})` : '';
|
|
412
|
+
return ` ${icon} ${label}${suffix}`;
|
|
413
|
+
});
|
|
414
|
+
const message = [
|
|
415
|
+
`Completed: ${result.successful}`,
|
|
416
|
+
result.skipped > 0 ? `Skipped: ${result.skipped}` : null,
|
|
417
|
+
result.failed > 0 ? `Failed: ${result.failed}` : null,
|
|
418
|
+
'',
|
|
419
|
+
...summaryLines,
|
|
420
|
+
]
|
|
421
|
+
.filter(Boolean)
|
|
422
|
+
.join('\n');
|
|
423
|
+
// If all successful, offer to close pane
|
|
424
|
+
if (result.failed === 0 && result.successful > 0) {
|
|
425
|
+
return {
|
|
426
|
+
type: 'confirm',
|
|
427
|
+
title: 'Multi-Merge Complete',
|
|
428
|
+
message,
|
|
429
|
+
confirmLabel: 'Close Pane',
|
|
430
|
+
cancelLabel: 'Keep Open',
|
|
431
|
+
onConfirm: async () => {
|
|
432
|
+
const { closePane } = await import('../implementations/closeAction.js');
|
|
433
|
+
return closePane(pane, context);
|
|
434
|
+
},
|
|
435
|
+
onCancel: async () => {
|
|
436
|
+
return {
|
|
437
|
+
type: 'success',
|
|
438
|
+
message: 'Merges complete. Pane kept open.',
|
|
439
|
+
dismissable: true,
|
|
440
|
+
};
|
|
441
|
+
},
|
|
442
|
+
};
|
|
443
|
+
}
|
|
444
|
+
// Some failures - just show info
|
|
445
|
+
return {
|
|
446
|
+
type: 'info',
|
|
447
|
+
title: result.failed > 0 ? 'Multi-Merge Partial' : 'Multi-Merge Complete',
|
|
448
|
+
message,
|
|
449
|
+
dismissable: true,
|
|
450
|
+
};
|
|
451
|
+
}
|
|
452
|
+
//# sourceMappingURL=multiMergeOrchestrator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"multiMergeOrchestrator.js","sourceRoot":"","sources":["../../../src/actions/merge/multiMergeOrchestrator.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAMH,OAAO,EAAE,uBAAuB,EAAE,MAAM,kCAAkC,CAAC;AAE3E;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,SAAyB;IAEzB,MAAM,EAAE,aAAa,EAAE,GAAG,MAAM,MAAM,CAAC,gCAAgC,CAAC,CAAC;IACzE,MAAM,KAAK,GAAqB,EAAE,CAAC;IAEnC,KAAK,MAAM,QAAQ,IAAI,SAAS,EAAE,CAAC;QACjC,MAAM,UAAU,GAAG,aAAa,CAC9B,QAAQ,CAAC,cAAc,EACvB,QAAQ,CAAC,YAAY,EACrB,QAAQ,CAAC,MAAM,CAChB,CAAC;QAEF,wFAAwF;QACxF,uDAAuD;QACvD,MAAM,iBAAiB,GAAG,UAAU,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,kBAAkB,CAAC,CAAC;QACrF,MAAM,qBAAqB,GAAG,UAAU,CAAC,MAAM,CAAC,MAAM,KAAK,CAAC,IAAI,iBAAiB,CAAC;QAElF,IAAI,CAAC,qBAAqB,EAAE,CAAC;YAC3B,KAAK,CAAC,IAAI,CAAC;gBACT,QAAQ;gBACR,UAAU;gBACV,MAAM,EAAE,SAAS;aAClB,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,IAAc,EACd,OAAsB,EACtB,KAAuB;IAEvB,iCAAiC;IACjC,OAAO,0BAA0B,CAAC,IAAI,EAAE,OAAO,EAAE,KAAK,CAAC,CAAC;AAC1D,CAAC;AAED;;GAEG;AACH,SAAS,0BAA0B,CACjC,IAAc,EACd,OAAsB,EACtB,KAAuB;IAEvB,MAAM,YAAY,GAAG,KAAK;SACvB,GAAG,CAAC,CAAC,IAAI,EAAE,GAAG,EAAE,EAAE;QACjB,MAAM,KAAK,GAAG,uBAAuB,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACrD,MAAM,MAAM,GAAG,IAAI,CAAC,UAAU,CAAC,MAAM;aAClC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,kBAAkB,CAAC;aAC1C,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;aACnC,IAAI,CAAC,IAAI,CAAC,CAAC;QACd,OAAO,IAAI,GAAG,GAAG,CAAC,KAAK,KAAK,GAAG,MAAM,CAAC,CAAC,CAAC,KAAK,MAAM,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC;IAChE,CAAC,CAAC;SACD,IAAI,CAAC,IAAI,CAAC,CAAC;IAEd,OAAO;QACL,IAAI,EAAE,SAAS;QACf,KAAK,EAAE,wBAAwB;QAC/B,OAAO,EAAE,SAAS,KAAK,CAAC,MAAM,aAAa,KAAK,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,QAAQ,YAAY,EAAE;QACjG,YAAY,EAAE,aAAa;QAC3B,WAAW,EAAE,QAAQ;QACrB,SAAS,EAAE,KAAK,IAAI,EAAE;YACpB,6BAA6B;YAC7B,MAAM,MAAM,GAAqB;gBAC/B,cAAc,EAAE,KAAK,CAAC,MAAM;gBAC5B,UAAU,EAAE,CAAC;gBACb,MAAM,EAAE,CAAC;gBACT,OAAO,EAAE,CAAC;gBACV,OAAO,EAAE,EAAE;aACZ,CAAC;YAEF,6BAA6B;YAC7B,OAAO,kBAAkB,CAAC,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,EAAE,MAAM,CAAC,CAAC;QAC7D,CAAC;QACD,QAAQ,EAAE,KAAK,IAAI,EAAE;YACnB,OAAO;gBACL,IAAI,EAAE,MAAM;gBACZ,OAAO,EAAE,uBAAuB;gBAChC,WAAW,EAAE,IAAI;aAClB,CAAC;QACJ,CAAC;KACF,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,KAAK,UAAU,kBAAkB,CAC/B,IAAc,EACd,OAAsB,EACtB,KAAuB,EACvB,YAAoB,EACpB,MAAwB;IAExB,sBAAsB;IACtB,IAAI,YAAY,IAAI,KAAK,CAAC,MAAM,EAAE,CAAC;QACjC,OAAO,qBAAqB,CAAC,IAAI,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;IACtD,CAAC;IAED,MAAM,IAAI,GAAG,KAAK,CAAC,YAAY,CAAC,CAAC;IACjC,MAAM,aAAa,GAAG,uBAAuB,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IAE7D,gBAAgB;IAChB,OAAO,CAAC,KAAK,CAAC,2BAA2B,YAAY,GAAG,CAAC,IAAI,KAAK,CAAC,MAAM,KAAK,aAAa,EAAE,CAAC,CAAC;IAE/F,gBAAgB;IAChB,IAAI,CAAC,MAAM,GAAG,aAAa,CAAC;IAE5B,sCAAsC;IACtC,OAAO,0BAA0B,CAC/B,IAAI,EACJ,OAAO,EACP,IAAI,EACJ,YAAY,EACZ,KAAK,CAAC,MAAM,EACZ,KAAK,EAAE,OAAgB,EAAE,KAAc,EAAE,EAAE;QACzC,gBAAgB;QAChB,IAAI,OAAO,EAAE,CAAC;YACZ,IAAI,CAAC,MAAM,GAAG,WAAW,CAAC;YAC1B,MAAM,CAAC,UAAU,EAAE,CAAC;YACpB,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC;gBAClB,QAAQ,EAAE,IAAI,CAAC,QAAQ;gBACvB,MAAM,EAAE,WAAW;aACpB,CAAC,CAAC;QACL,CAAC;aAAM,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;YAC/B,IAAI,CAAC,MAAM,GAAG,SAAS,CAAC;YACxB,MAAM,CAAC,OAAO,EAAE,CAAC;YACjB,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC;gBAClB,QAAQ,EAAE,IAAI,CAAC,QAAQ;gBACvB,MAAM,EAAE,SAAS;aAClB,CAAC,CAAC;QACL,CAAC;aAAM,CAAC;YACN,IAAI,CAAC,MAAM,GAAG,QAAQ,CAAC;YACvB,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;YACnB,MAAM,CAAC,MAAM,EAAE,CAAC;YAChB,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC;gBAClB,QAAQ,EAAE,IAAI,CAAC,QAAQ;gBACvB,MAAM,EAAE,QAAQ;gBAChB,KAAK;aACN,CAAC,CAAC;QACL,CAAC;QAED,oBAAoB;QACpB,OAAO,kBAAkB,CAAC,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,YAAY,GAAG,CAAC,EAAE,MAAM,CAAC,CAAC;IAC5E,CAAC,CACF,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,KAAK,UAAU,0BAA0B,CACvC,IAAc,EACd,OAAsB,EACtB,IAAoB,EACpB,YAAoB,EACpB,UAAkB,EAClB,UAAuE;IAEvE,MAAM,EAAE,UAAU,EAAE,QAAQ,EAAE,GAAG,IAAI,CAAC;IACtC,MAAM,aAAa,GAAG,uBAAuB,CAAC,QAAQ,CAAC,CAAC;IACxD,MAAM,cAAc,GAAG,IAAI,YAAY,GAAG,CAAC,IAAI,UAAU,KAAK,aAAa,EAAE,CAAC;IAE9E,kDAAkD;IAClD,IAAI,CAAC,UAAU,CAAC,QAAQ,EAAE,CAAC;QACzB,OAAO,yBAAyB,CAC9B,IAAI,EACJ,OAAO,EACP,IAAI,EACJ,cAAc,EACd,UAAU,CACX,CAAC;IACJ,CAAC;IAED,kDAAkD;IAClD,OAAO;QACL,IAAI,EAAE,SAAS;QACf,KAAK,EAAE,cAAc;QACrB,OAAO,EAAE,UAAU,QAAQ,CAAC,MAAM,UAAU,UAAU,CAAC,UAAU,GAAG;QACpE,YAAY,EAAE,OAAO;QACrB,WAAW,EAAE,MAAM;QACnB,SAAS,EAAE,KAAK,IAAI,EAAE;YACpB,OAAO,oBAAoB,CAAC,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,cAAc,EAAE,UAAU,CAAC,CAAC;QAC/E,CAAC;QACD,QAAQ,EAAE,KAAK,IAAI,EAAE;YACnB,2CAA2C;YAC3C,OAAO,UAAU,CAAC,KAAK,EAAE,SAAS,CAAC,CAAC;QACtC,CAAC;KACF,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,KAAK,UAAU,yBAAyB,CACtC,IAAc,EACd,OAAsB,EACtB,IAAoB,EACpB,cAAsB,EACtB,UAAuE;IAEvE,MAAM,EAAE,UAAU,EAAE,QAAQ,EAAE,GAAG,IAAI,CAAC;IACtC,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,GAAG,UAAU,CAAC;IAE1C,mDAAmD;IACnD,MAAM,UAAU,GAAG,KAAK,IAA2B,EAAE;QACnD,cAAc;QACd,MAAM,EAAE,aAAa,EAAE,GAAG,MAAM,MAAM,CAAC,gCAAgC,CAAC,CAAC;QACzE,IAAI,CAAC,UAAU,GAAG,aAAa,CAC7B,QAAQ,CAAC,cAAc,EACvB,QAAQ,CAAC,YAAY,EACrB,QAAQ,CAAC,MAAM,CAChB,CAAC;QAEF,IAAI,IAAI,CAAC,UAAU,CAAC,QAAQ,EAAE,CAAC;YAC7B,OAAO,oBAAoB,CAAC,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,cAAc,EAAE,UAAU,CAAC,CAAC;QAC/E,CAAC;aAAM,CAAC;YACN,OAAO,yBAAyB,CAAC,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,cAAc,EAAE,UAAU,CAAC,CAAC;QACpF,CAAC;IACH,CAAC,CAAC;IAEF,6BAA6B;IAC7B,MAAM,cAAc,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,kBAAkB,CAAC,CAAC;IACvE,IAAI,cAAc,EAAE,CAAC;QACnB,OAAO,UAAU,CAAC,KAAK,EAAE,SAAS,CAAC,CAAC;IACtC,CAAC;IAED,uBAAuB;IACvB,MAAM,SAAS,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,YAAY,CAAC,CAAC;IAC5D,IAAI,SAAS,EAAE,CAAC;QACd,OAAO,0BAA0B,CAC/B,IAAI,EACJ,cAAc,EACd,UAAU,EACV,UAAU,EACV,UAAU,CACX,CAAC;IACJ,CAAC;IAED,iCAAiC;IACjC,MAAM,mBAAmB,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,sBAAsB,CAAC,CAAC;IAChF,IAAI,mBAAmB,EAAE,CAAC;QACxB,OAAO,4BAA4B,CACjC,IAAI,EACJ,cAAc,EACd,UAAU,EACV,UAAU,CACX,CAAC;IACJ,CAAC;IAED,2BAA2B;IAC3B,MAAM,aAAa,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,gBAAgB,CAAC,CAAC;IACpE,IAAI,aAAa,EAAE,CAAC;QAClB,OAAO,yBAAyB,CAC9B,IAAI,EACJ,OAAO,EACP,IAAI,EACJ,cAAc,EACd,UAAU,EACV,UAAU,CACX,CAAC;IACJ,CAAC;IAED,kCAAkC;IAClC,OAAO,UAAU,CAAC,KAAK,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;AAClE,CAAC;AAED;;GAEG;AACH,KAAK,UAAU,0BAA0B,CACvC,IAAoB,EACpB,cAAsB,EACtB,UAAkB,EAClB,UAAuC,EACvC,UAAuE;IAEvE,MAAM,EAAE,QAAQ,EAAE,UAAU,EAAE,GAAG,IAAI,CAAC;IACtC,MAAM,KAAK,GAAG,UAAU,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,YAAY,CAAE,CAAC;IACpE,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,IAAI,EAAE,CAAC;IAEhC,OAAO;QACL,IAAI,EAAE,QAAQ;QACd,KAAK,EAAE,GAAG,cAAc,2BAA2B;QACnD,OAAO,EAAE,GAAG,UAAU,OAAO,QAAQ,CAAC,QAAQ,8BAA8B,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,EAAE;QAClK,OAAO,EAAE;YACP;gBACE,EAAE,EAAE,kBAAkB;gBACtB,KAAK,EAAE,uBAAuB;gBAC9B,WAAW,EAAE,sCAAsC;gBACnD,OAAO,EAAE,IAAI;aACd;YACD;gBACE,EAAE,EAAE,oBAAoB;gBACxB,KAAK,EAAE,sBAAsB;gBAC7B,WAAW,EAAE,sCAAsC;aACpD;YACD;gBACE,EAAE,EAAE,eAAe;gBACnB,KAAK,EAAE,uBAAuB;gBAC9B,WAAW,EAAE,+BAA+B;aAC7C;YACD;gBACE,EAAE,EAAE,MAAM;gBACV,KAAK,EAAE,gBAAgB;gBACvB,WAAW,EAAE,kCAAkC;aAChD;SACF;QACD,QAAQ,EAAE,KAAK,EAAE,QAAgB,EAAE,EAAE;YACnC,IAAI,QAAQ,KAAK,MAAM,EAAE,CAAC;gBACxB,OAAO,UAAU,CAAC,KAAK,EAAE,SAAS,CAAC,CAAC;YACtC,CAAC;YAED,IACE,QAAQ,KAAK,kBAAkB;gBAC/B,QAAQ,KAAK,oBAAoB;gBACjC,QAAQ,KAAK,eAAe,EAC5B,CAAC;gBACD,MAAM,EAAE,uBAAuB,EAAE,GAAG,MAAM,MAAM,CAAC,2BAA2B,CAAC,CAAC;gBAC9E,OAAO,uBAAuB,CAC5B,QAAQ,CAAC,cAAc,EACvB,QAAuE,EACvE,UAAU,CACX,CAAC;YACJ,CAAC;YAED,OAAO,UAAU,CAAC,KAAK,EAAE,gBAAgB,CAAC,CAAC;QAC7C,CAAC;QACD,WAAW,EAAE,KAAK;KACnB,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,KAAK,UAAU,4BAA4B,CACzC,IAAoB,EACpB,cAAsB,EACtB,UAAuC,EACvC,UAAuE;IAEvE,MAAM,EAAE,QAAQ,EAAE,UAAU,EAAE,GAAG,IAAI,CAAC;IACtC,MAAM,KAAK,GAAG,UAAU,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,sBAAsB,CAAE,CAAC;IAC9E,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,IAAI,EAAE,CAAC;IAEhC,OAAO;QACL,IAAI,EAAE,QAAQ;QACd,KAAK,EAAE,GAAG,cAAc,uBAAuB;QAC/C,OAAO,EAAE,GAAG,QAAQ,CAAC,QAAQ,uCAAuC,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,EAAE;QAC1J,OAAO,EAAE;YACP;gBACE,EAAE,EAAE,kBAAkB;gBACtB,KAAK,EAAE,uBAAuB;gBAC9B,WAAW,EAAE,sCAAsC;gBACnD,OAAO,EAAE,IAAI;aACd;YACD;gBACE,EAAE,EAAE,oBAAoB;gBACxB,KAAK,EAAE,sBAAsB;gBAC7B,WAAW,EAAE,sCAAsC;aACpD;YACD;gBACE,EAAE,EAAE,eAAe;gBACnB,KAAK,EAAE,uBAAuB;gBAC9B,WAAW,EAAE,+BAA+B;aAC7C;YACD;gBACE,EAAE,EAAE,MAAM;gBACV,KAAK,EAAE,gBAAgB;gBACvB,WAAW,EAAE,kCAAkC;aAChD;SACF;QACD,QAAQ,EAAE,KAAK,EAAE,QAAgB,EAAE,EAAE;YACnC,IAAI,QAAQ,KAAK,MAAM,EAAE,CAAC;gBACxB,OAAO,UAAU,CAAC,KAAK,EAAE,SAAS,CAAC,CAAC;YACtC,CAAC;YAED,IACE,QAAQ,KAAK,kBAAkB;gBAC/B,QAAQ,KAAK,oBAAoB;gBACjC,QAAQ,KAAK,eAAe,EAC5B,CAAC;gBACD,MAAM,EAAE,uBAAuB,EAAE,GAAG,MAAM,MAAM,CAAC,2BAA2B,CAAC,CAAC;gBAC9E,OAAO,uBAAuB,CAC5B,QAAQ,CAAC,YAAY,EACrB,QAAuE,EACvE,UAAU,CACX,CAAC;YACJ,CAAC;YAED,OAAO,UAAU,CAAC,KAAK,EAAE,gBAAgB,CAAC,CAAC;QAC7C,CAAC;QACD,WAAW,EAAE,KAAK;KACnB,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,KAAK,UAAU,yBAAyB,CACtC,IAAc,EACd,OAAsB,EACtB,IAAoB,EACpB,cAAsB,EACtB,UAAkB,EAClB,UAAuE;IAEvE,MAAM,EAAE,QAAQ,EAAE,UAAU,EAAE,GAAG,IAAI,CAAC;IACtC,MAAM,KAAK,GAAG,UAAU,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,gBAAgB,CAAE,CAAC;IACxE,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,IAAI,EAAE,CAAC;IAEhC,OAAO;QACL,IAAI,EAAE,QAAQ;QACd,KAAK,EAAE,GAAG,cAAc,mBAAmB;QAC3C,OAAO,EAAE,yBAAyB,QAAQ,CAAC,QAAQ,MAAM,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,EAAE;QAC/I,OAAO,EAAE;YACP;gBACE,EAAE,EAAE,MAAM;gBACV,KAAK,EAAE,gBAAgB;gBACvB,WAAW,EAAE,kCAAkC;gBAC/C,OAAO,EAAE,IAAI;aACd;YACD;gBACE,EAAE,EAAE,OAAO;gBACX,KAAK,EAAE,kBAAkB;gBACzB,WAAW,EAAE,wBAAwB;aACtC;SACF;QACD,QAAQ,EAAE,KAAK,EAAE,QAAgB,EAAE,EAAE;YACnC,IAAI,QAAQ,KAAK,MAAM,EAAE,CAAC;gBACxB,OAAO,UAAU,CAAC,KAAK,EAAE,SAAS,CAAC,CAAC;YACtC,CAAC;YACD,IAAI,QAAQ,KAAK,OAAO,EAAE,CAAC;gBACzB,OAAO;oBACL,IAAI,EAAE,MAAM;oBACZ,KAAK,EAAE,qBAAqB;oBAC5B,OAAO,EAAE,cAAc,QAAQ,CAAC,QAAQ,oBAAoB;oBAC5D,WAAW,EAAE,IAAI;iBAClB,CAAC;YACJ,CAAC;YACD,OAAO,UAAU,CAAC,KAAK,EAAE,gBAAgB,CAAC,CAAC;QAC7C,CAAC;QACD,WAAW,EAAE,KAAK;KACnB,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,KAAK,UAAU,oBAAoB,CACjC,IAAc,EACd,OAAsB,EACtB,IAAoB,EACpB,cAAsB,EACtB,UAAuE;IAEvE,MAAM,EAAE,QAAQ,EAAE,UAAU,EAAE,GAAG,IAAI,CAAC;IACtC,MAAM,EAAE,UAAU,EAAE,GAAG,UAAU,CAAC;IAElC,MAAM,EAAE,qBAAqB,EAAE,qBAAqB,EAAE,GAAG,MAAM,MAAM,CACnE,+BAA+B,CAChC,CAAC;IACF,MAAM,EAAE,WAAW,EAAE,GAAG,MAAM,MAAM,CAAC,sBAAsB,CAAC,CAAC;IAE7D,yBAAyB;IACzB,MAAM,WAAW,CAAC,WAAW,EAAE,QAAQ,CAAC,cAAc,EAAE,IAAI,EAAE;QAC5D,kBAAkB,EAAE,UAAU;QAC9B,kBAAkB,EAAE,QAAQ,CAAC,YAAY;QACzC,cAAc,EAAE,QAAQ,CAAC,QAAQ;KAClC,CAAC,CAAC;IAEH,mCAAmC;IACnC,MAAM,KAAK,GAAG,qBAAqB,CAAC,QAAQ,CAAC,YAAY,EAAE,UAAU,CAAC,CAAC;IAEvE,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC;QACnB,IAAI,KAAK,CAAC,qBAAqB,IAAI,KAAK,CAAC,aAAa,EAAE,MAAM,EAAE,CAAC;YAC/D,iCAAiC;YACjC,OAAO;gBACL,IAAI,EAAE,QAAQ;gBACd,KAAK,EAAE,GAAG,cAAc,kBAAkB;gBAC1C,OAAO,EAAE,0BAA0B,UAAU,oBAAoB,KAAK,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE;gBACjI,OAAO,EAAE;oBACP;wBACE,EAAE,EAAE,MAAM;wBACV,KAAK,EAAE,gBAAgB;wBACvB,WAAW,EAAE,wCAAwC;wBACrD,OAAO,EAAE,IAAI;qBACd;oBACD;wBACE,EAAE,EAAE,WAAW;wBACf,KAAK,EAAE,kBAAkB;wBACzB,WAAW,EAAE,iCAAiC;qBAC/C;iBACF;gBACD,QAAQ,EAAE,KAAK,EAAE,QAAgB,EAAE,EAAE;oBACnC,MAAM,EAAE,UAAU,EAAE,GAAG,MAAM,MAAM,CAAC,+BAA+B,CAAC,CAAC;oBACrE,UAAU,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAC;oBAElC,IAAI,QAAQ,KAAK,WAAW,EAAE,CAAC;wBAC7B,OAAO;4BACL,IAAI,EAAE,MAAM;4BACZ,KAAK,EAAE,qBAAqB;4BAC5B,OAAO,EAAE,+BAA+B,QAAQ,CAAC,QAAQ,EAAE;4BAC3D,WAAW,EAAE,IAAI;yBAClB,CAAC;oBACJ,CAAC;oBACD,OAAO,UAAU,CAAC,KAAK,EAAE,SAAS,CAAC,CAAC;gBACtC,CAAC;gBACD,WAAW,EAAE,KAAK;aACnB,CAAC;QACJ,CAAC;QAED,OAAO,UAAU,CAAC,KAAK,EAAE,iBAAiB,KAAK,CAAC,KAAK,EAAE,CAAC,CAAC;IAC3D,CAAC;IAED,mCAAmC;IACnC,MAAM,KAAK,GAAG,qBAAqB,CAAC,QAAQ,CAAC,cAAc,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;IAE9E,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC;QACnB,OAAO,UAAU,CAAC,KAAK,EAAE,wBAAwB,UAAU,KAAK,KAAK,CAAC,KAAK,EAAE,CAAC,CAAC;IACjF,CAAC;IAED,0BAA0B;IAC1B,MAAM,WAAW,CAAC,YAAY,EAAE,QAAQ,CAAC,cAAc,EAAE,IAAI,EAAE;QAC7D,kBAAkB,EAAE,UAAU;QAC9B,kBAAkB,EAAE,QAAQ,CAAC,YAAY;QACzC,cAAc,EAAE,QAAQ,CAAC,QAAQ;KAClC,CAAC,CAAC;IAEH,OAAO,CAAC,KAAK,CAAC,oCAAoC,QAAQ,CAAC,QAAQ,EAAE,CAAC,CAAC;IACvE,OAAO,UAAU,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC;AAED;;GAEG;AACH,SAAS,qBAAqB,CAC5B,IAAc,EACd,OAAsB,EACtB,MAAwB;IAExB,MAAM,YAAY,GAAG,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE;QAC1C,MAAM,KAAK,GAAG,uBAAuB,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;QAClD,MAAM,IAAI,GAAG,CAAC,CAAC,MAAM,KAAK,WAAW,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC;QACjF,MAAM,MAAM,GAAG,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;QACvE,OAAO,IAAI,IAAI,IAAI,KAAK,GAAG,MAAM,EAAE,CAAC;IACtC,CAAC,CAAC,CAAC;IAEH,MAAM,OAAO,GAAG;QACd,cAAc,MAAM,CAAC,UAAU,EAAE;QACjC,MAAM,CAAC,OAAO,GAAG,CAAC,CAAC,CAAC,CAAC,YAAY,MAAM,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,IAAI;QACxD,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,WAAW,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,IAAI;QACrD,EAAE;QACF,GAAG,YAAY;KAChB;SACE,MAAM,CAAC,OAAO,CAAC;SACf,IAAI,CAAC,IAAI,CAAC,CAAC;IAEd,yCAAyC;IACzC,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,IAAI,MAAM,CAAC,UAAU,GAAG,CAAC,EAAE,CAAC;QACjD,OAAO;YACL,IAAI,EAAE,SAAS;YACf,KAAK,EAAE,sBAAsB;YAC7B,OAAO;YACP,YAAY,EAAE,YAAY;YAC1B,WAAW,EAAE,WAAW;YACxB,SAAS,EAAE,KAAK,IAAI,EAAE;gBACpB,MAAM,EAAE,SAAS,EAAE,GAAG,MAAM,MAAM,CAAC,mCAAmC,CAAC,CAAC;gBACxE,OAAO,SAAS,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;YAClC,CAAC;YACD,QAAQ,EAAE,KAAK,IAAI,EAAE;gBACnB,OAAO;oBACL,IAAI,EAAE,SAAS;oBACf,OAAO,EAAE,kCAAkC;oBAC3C,WAAW,EAAE,IAAI;iBAClB,CAAC;YACJ,CAAC;SACF,CAAC;IACJ,CAAC;IAED,iCAAiC;IACjC,OAAO;QACL,IAAI,EAAE,MAAM;QACZ,KAAK,EAAE,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,qBAAqB,CAAC,CAAC,CAAC,sBAAsB;QACzE,OAAO;QACP,WAAW,EAAE,IAAI;KAClB,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Multi-Merge Type Definitions
|
|
3
|
+
*
|
|
4
|
+
* Types for managing multiple worktree merges in a single operation.
|
|
5
|
+
*/
|
|
6
|
+
import type { MergeValidationResult } from '../../utils/mergeValidation.js';
|
|
7
|
+
/**
|
|
8
|
+
* Information about a single worktree (either root or sub-worktree)
|
|
9
|
+
*/
|
|
10
|
+
export interface WorktreeInfo {
|
|
11
|
+
/** Absolute path to the worktree directory */
|
|
12
|
+
worktreePath: string;
|
|
13
|
+
/** Absolute path to the parent repo (where the worktree originated from) */
|
|
14
|
+
parentRepoPath: string;
|
|
15
|
+
/** Name of the repository (derived from parent repo's directory name) */
|
|
16
|
+
repoName: string;
|
|
17
|
+
/** Current branch in this worktree */
|
|
18
|
+
branch: string;
|
|
19
|
+
/** Main branch in parent repo (main, master, etc.) */
|
|
20
|
+
mainBranch: string;
|
|
21
|
+
/** Whether this is the root worktree (the dmux pane's worktree) */
|
|
22
|
+
isRoot: boolean;
|
|
23
|
+
/** Relative path from root worktree (for display), "." for root */
|
|
24
|
+
relativePath: string;
|
|
25
|
+
/** Depth level (0 = root, 1 = immediate child, etc.) */
|
|
26
|
+
depth: number;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* A single item in the merge queue
|
|
30
|
+
*/
|
|
31
|
+
export interface MergeQueueItem {
|
|
32
|
+
worktree: WorktreeInfo;
|
|
33
|
+
validation: MergeValidationResult;
|
|
34
|
+
status: 'pending' | 'in_progress' | 'completed' | 'failed' | 'skipped';
|
|
35
|
+
error?: string;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Result of multi-merge operation
|
|
39
|
+
*/
|
|
40
|
+
export interface MultiMergeResult {
|
|
41
|
+
totalWorktrees: number;
|
|
42
|
+
successful: number;
|
|
43
|
+
failed: number;
|
|
44
|
+
skipped: number;
|
|
45
|
+
results: Array<{
|
|
46
|
+
worktree: WorktreeInfo;
|
|
47
|
+
status: 'completed' | 'failed' | 'skipped';
|
|
48
|
+
error?: string;
|
|
49
|
+
}>;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Options for executing a single worktree merge within a multi-merge context
|
|
53
|
+
*/
|
|
54
|
+
export interface SingleMergeOptions {
|
|
55
|
+
/** Skip the close pane prompt (for multi-merge where we close after all) */
|
|
56
|
+
skipClosePrompt?: boolean;
|
|
57
|
+
/** Callback when merge completes (success or failure) */
|
|
58
|
+
onComplete?: (success: boolean, error?: string) => Promise<void>;
|
|
59
|
+
/** Label to display for this worktree in dialogs */
|
|
60
|
+
worktreeLabel?: string;
|
|
61
|
+
}
|
|
62
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/actions/merge/types.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,gCAAgC,CAAC;AAE5E;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,8CAA8C;IAC9C,YAAY,EAAE,MAAM,CAAC;IAErB,4EAA4E;IAC5E,cAAc,EAAE,MAAM,CAAC;IAEvB,yEAAyE;IACzE,QAAQ,EAAE,MAAM,CAAC;IAEjB,sCAAsC;IACtC,MAAM,EAAE,MAAM,CAAC;IAEf,sDAAsD;IACtD,UAAU,EAAE,MAAM,CAAC;IAEnB,mEAAmE;IACnE,MAAM,EAAE,OAAO,CAAC;IAEhB,mEAAmE;IACnE,YAAY,EAAE,MAAM,CAAC;IAErB,wDAAwD;IACxD,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,YAAY,CAAC;IACvB,UAAU,EAAE,qBAAqB,CAAC;IAClC,MAAM,EAAE,SAAS,GAAG,aAAa,GAAG,WAAW,GAAG,QAAQ,GAAG,SAAS,CAAC;IACvE,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,cAAc,EAAE,MAAM,CAAC;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,KAAK,CAAC;QACb,QAAQ,EAAE,YAAY,CAAC;QACvB,MAAM,EAAE,WAAW,GAAG,QAAQ,GAAG,SAAS,CAAC;QAC3C,KAAK,CAAC,EAAE,MAAM,CAAC;KAChB,CAAC,CAAC;CACJ;AAED;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,4EAA4E;IAC5E,eAAe,CAAC,EAAE,OAAO,CAAC;IAE1B,yDAAyD;IACzD,UAAU,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,KAAK,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAEjE,oDAAoD;IACpD,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/actions/merge/types.ts"],"names":[],"mappings":"AAAA;;;;GAIG"}
|
|
@@ -2,5 +2,5 @@
|
|
|
2
2
|
* Auto-generated AGENTS.md content
|
|
3
3
|
* DO NOT EDIT MANUALLY - run 'pnpm generate:hooks-docs' to regenerate
|
|
4
4
|
*/
|
|
5
|
-
export declare const AGENTS_MD = "# dmux Hooks System - Agent Reference\n\n**Auto-generated documentation for AI agents**\n\nThis document contains everything an AI agent needs to create, modify, and understand dmux hooks. It is automatically generated from the dmux source code and embedded in the binary.\n\n## What You're Working On\n\nYou are editing hooks for **dmux**, a tmux pane manager that creates AI-powered development workflows. Each pane runs in its own git worktree with an AI agent (Claude Code or opencode).\n\n## Your Goal\n\nCreate executable bash scripts in `.dmux-hooks/` that run automatically at key lifecycle events.\n\n## Quick Start\n\n1. **Create a hook file**: `touch .dmux-hooks/worktree_created`\n2. **Make it executable**: `chmod +x .dmux-hooks/worktree_created`\n3. **Add shebang**: Start with `#!/bin/bash`\n4. **Use environment variables**: Access `$DMUX_ROOT`, `$DMUX_WORKTREE_PATH`, etc.\n5. **Test it**: Set env vars manually and run the script\n\n## Hook Execution Model\n\n- **Non-blocking**: Hooks run in background (detached processes)\n- **Silent failures**: Hook errors are logged but don't stop dmux\n- **Environment-based**: All context passed via environment variables\n- **Version controlled**: Hooks in `.dmux-hooks/` are shared with team\n- **Priority resolution**: `.dmux-hooks/` \u2192 `.dmux/hooks/` \u2192 `~/.dmux/hooks/`\n\n## Available Hooks\n\n### Pane Lifecycle Hooks\n\n| Hook | When | Common Use Cases |\n|------|------|------------------|\n| `before_pane_create` | Before pane creation | Validation, notifications, pre-flight checks |\n| `pane_created` | After pane, before worktree | Configure tmux settings, prepare environment |\n| `worktree_created` | After full setup | Install deps, copy configs, setup git |\n| `before_pane_close` | Before closing | Save state, backup uncommitted work |\n| `pane_closed` | After closed | Cleanup resources, analytics, notifications |\n\n### Worktree Lifecycle Hooks\n\n| Hook | When | Common Use Cases |\n|------|------|------------------|\n| `before_worktree_remove` | Before worktree removal | Archive worktree, save artifacts |\n| `worktree_removed` | After worktree removed | Cleanup external references |\n\n### Merge Lifecycle Hooks\n\n| Hook | When | Common Use Cases |\n|------|------|------------------|\n| `pre_merge` | Before merge operation | Run final tests, create backups |\n| `post_merge` | After successful merge | Deploy, close issues, notify team |\n\n### Interactive Hooks (with HTTP callbacks)\n\n| Hook | When | Common Use Cases |\n|------|------|------------------|\n| `run_test` | When tests triggered | Run test suite, report status via HTTP |\n| `run_dev` | When dev server triggered | Start dev server, create tunnel, report URL |\n\n\n## Environment Variables\n\n### Always Available\n```bash\nDMUX_ROOT=\"/path/to/project\" # Project root directory\nDMUX_SERVER_PORT=\"3142\" # HTTP server port\n```\n\n### Pane Context (most hooks)\n```bash\nDMUX_PANE_ID=\"dmux-1234567890\" # dmux pane identifier\nDMUX_SLUG=\"fix-auth-bug\" # Branch/worktree name\nDMUX_PROMPT=\"Fix authentication bug\" # User's prompt\nDMUX_AGENT=\"claude\" # Agent type (claude|opencode)\nDMUX_TMUX_PANE_ID=\"%38\" # tmux pane ID\n```\n\n### Worktree Context\n```bash\nDMUX_WORKTREE_PATH=\"/path/.dmux/worktrees/fix-auth-bug\"\nDMUX_BRANCH=\"fix-auth-bug\" # Same as slug\n```\n\n### Merge Context\n```bash\nDMUX_TARGET_BRANCH=\"main\" # Branch being merged into\n```\n\n## HTTP Callback API\n\nInteractive hooks (`run_test` and `run_dev`) can update dmux UI via HTTP.\n\n### Update Test Status\n```bash\ncurl -X PUT \"http://localhost:$DMUX_SERVER_PORT/api/panes/$DMUX_PANE_ID/test\" -H \"Content-Type: application/json\" -d '{\"status\": \"running\", \"output\": \"optional test output\"}'\n\n# Status values: \"running\" | \"passed\" | \"failed\"\n```\n\n### Update Dev Server\n```bash\ncurl -X PUT \"http://localhost:$DMUX_SERVER_PORT/api/panes/$DMUX_PANE_ID/dev\" -H \"Content-Type: application/json\" -d '{\"status\": \"running\", \"url\": \"http://localhost:3000\"}'\n\n# Status values: \"running\" | \"stopped\"\n# url: Can be localhost or tunnel URL (ngrok, cloudflared, etc.)\n```\n\n## Common Patterns\n\n### Pattern 1: Install Dependencies\n```bash\n#!/bin/bash\n# .dmux-hooks/worktree_created\n\ncd \"$DMUX_WORKTREE_PATH\"\n\nif [ -f \"pnpm-lock.yaml\" ]; then\n pnpm install --prefer-offline &\nelif [ -f \"package-lock.json\" ]; then\n npm install &\nelif [ -f \"yarn.lock\" ]; then\n yarn install &\nelif [ -f \"Gemfile\" ]; then\n bundle install &\nelif [ -f \"requirements.txt\" ]; then\n pip install -r requirements.txt &\nelif [ -f \"Cargo.toml\" ]; then\n cargo build &\nfi\n```\n\n### Pattern 2: Copy Configuration\n```bash\n#!/bin/bash\n# .dmux-hooks/worktree_created\n\n# Copy environment file\nif [ -f \"$DMUX_ROOT/.env.local\" ]; then\n cp \"$DMUX_ROOT/.env.local\" \"$DMUX_WORKTREE_PATH/.env.local\"\nfi\n\n# Copy other config files\nfor file in .env.development .npmrc .yarnrc; do\n if [ -f \"$DMUX_ROOT/$file\" ]; then\n cp \"$DMUX_ROOT/$file\" \"$DMUX_WORKTREE_PATH/$file\"\n fi\ndone\n```\n\n### Pattern 3: Run Tests with Status Updates\n```bash\n#!/bin/bash\n# .dmux-hooks/run_test\n\nset -e\ncd \"$DMUX_WORKTREE_PATH\"\nAPI=\"http://localhost:$DMUX_SERVER_PORT/api/panes/$DMUX_PANE_ID/test\"\n\n# Update: starting\ncurl -s -X PUT \"$API\" -H \"Content-Type: application/json\" -d '{\"status\": \"running\"}' > /dev/null\n\n# Run tests and capture output\nOUTPUT_FILE=\"/tmp/dmux-test-$DMUX_PANE_ID.txt\"\nif pnpm test > \"$OUTPUT_FILE\" 2>&1; then\n STATUS=\"passed\"\nelse\n STATUS=\"failed\"\nfi\n\n# Get output (truncate if too long)\nOUTPUT=$(head -c 5000 \"$OUTPUT_FILE\")\n\n# Update: complete\ncurl -s -X PUT \"$API\" -H \"Content-Type: application/json\" -d \"$(jq -n --arg status \"$STATUS\" --arg output \"$OUTPUT\" '{status: $status, output: $output}')\" > /dev/null\n\nrm -f \"$OUTPUT_FILE\"\n```\n\n### Pattern 4: Dev Server with Tunnel\n```bash\n#!/bin/bash\n# .dmux-hooks/run_dev\n\nset -e\ncd \"$DMUX_WORKTREE_PATH\"\nAPI=\"http://localhost:$DMUX_SERVER_PORT/api/panes/$DMUX_PANE_ID/dev\"\n\n# Start dev server in background\nLOG_FILE=\"/tmp/dmux-dev-$DMUX_PANE_ID.log\"\npnpm dev > \"$LOG_FILE\" 2>&1 &\nDEV_PID=$!\n\n# Wait for server to start\nsleep 5\n\n# Detect port from logs\nPORT=$(grep -oP 'localhost:Kd+' \"$LOG_FILE\" | head -1)\n[ -z \"$PORT\" ] && PORT=3000\n\n# Optional: Create tunnel with cloudflared\nif command -v cloudflared &> /dev/null; then\n TUNNEL=$(cloudflared tunnel --url \"http://localhost:$PORT\" 2>&1 | grep -oP 'https://[a-z0-9-]+.trycloudflare.com' | head -1)\n URL=\"${TUNNEL:-http://localhost:$PORT}\"\nelse\n URL=\"http://localhost:$PORT\"\nfi\n\n# Report status\ncurl -s -X PUT \"$API\" -H \"Content-Type: application/json\" -d \"{\"status\": \"running\", \"url\": \"$URL\"}\" > /dev/null\n\necho \"[Hook] Dev server running at $URL (PID: $DEV_PID)\"\n```\n\n### Pattern 5: Post-Merge Deployment\n```bash\n#!/bin/bash\n# .dmux-hooks/post_merge\n\nset -e\ncd \"$DMUX_ROOT\"\n\n# Only deploy from main/master\nif [ \"$DMUX_TARGET_BRANCH\" != \"main\" ] && [ \"$DMUX_TARGET_BRANCH\" != \"master\" ]; then\n exit 0\nfi\n\n# Push to remote\ngit push origin \"$DMUX_TARGET_BRANCH\"\n\n# Trigger deployment (example: Vercel)\nif [ -n \"$VERCEL_TOKEN\" ]; then\n curl -s -X POST \"https://api.vercel.com/v1/deployments\" -H \"Authorization: Bearer $VERCEL_TOKEN\" -H \"Content-Type: application/json\" -d '{\"name\": \"my-project\"}' > /dev/null\nfi\n\n# Close GitHub issue if prompt contains #123\nISSUE=$(echo \"$DMUX_PROMPT\" | grep -oP '#Kd+' | head -1)\nif [ -n \"$ISSUE\" ] && command -v gh &> /dev/null; then\n gh issue close \"$ISSUE\" -c \"Resolved in $DMUX_SLUG, merged to $DMUX_TARGET_BRANCH\" 2>/dev/null || true\nfi\n```\n\n## Best Practices\n\n1. **Always start with shebang**: `#!/bin/bash`\n2. **Set error handling**: `set -e` (exit on error)\n3. **Make executable**: `chmod +x .dmux-hooks/hook_name`\n4. **Background long operations**: Append `&` to avoid blocking\n5. **Check for required tools**: `command -v tool &> /dev/null`\n6. **Log for debugging**: `echo \"[Hook] message\" >> \"$DMUX_ROOT/.dmux/hooks.log\"`\n7. **Handle missing vars gracefully**: `[ -z \"$VAR\" ] && exit 0`\n8. **Use silent curl**: `curl -s` to avoid noise in logs\n9. **Clean up temp files**: Remove files in `/tmp/`\n10. **Test before committing**: Run hooks manually with mock env vars\n\n## Testing Hooks\n\n### Manual Testing\n```bash\n# 1. Set environment variables\nexport DMUX_ROOT=\"$(pwd)\"\nexport DMUX_PANE_ID=\"test-pane\"\nexport DMUX_SLUG=\"test-branch\"\nexport DMUX_WORKTREE_PATH=\"$(pwd)\"\nexport DMUX_SERVER_PORT=\"3142\"\nexport DMUX_AGENT=\"claude\"\nexport DMUX_PROMPT=\"Test prompt\"\n\n# 2. Run hook directly\n./.dmux-hooks/worktree_created\n\n# 3. Check exit code\necho $? # Should be 0 for success\n```\n\n### Syntax Check\n```bash\n# Check for syntax errors without running\nbash -n ./.dmux-hooks/worktree_created\n```\n\n### Shellcheck (if available)\n```bash\nshellcheck ./.dmux-hooks/worktree_created\n```\n\n## Project Context Analysis\n\nBefore creating hooks, analyze these files in the project:\n\n### Package Manager Detection\n```bash\n# Check which package manager is used\nif [ -f \"pnpm-lock.yaml\" ]; then\n # Use: pnpm install, pnpm test, pnpm dev\nelif [ -f \"package-lock.json\" ]; then\n # Use: npm install, npm test, npm run dev\nelif [ -f \"yarn.lock\" ]; then\n # Use: yarn install, yarn test, yarn dev\nfi\n```\n\n### Test Command Discovery\n```bash\n# Read package.json to find test command\ncat package.json | grep '\"test\"'\n# Or with jq:\njq -r '.scripts.test' package.json\n```\n\n### Dev Command Discovery\n```bash\n# Read package.json to find dev command\ncat package.json | grep '\"dev\"'\n# Or with jq:\njq -r '.scripts.dev' package.json\n```\n\n### Environment Variables\n```bash\n# Check for .env files to copy\nls -la | grep '.env'\n```\n\n### Build System\n```bash\n# Detect build system\nif [ -f \"vite.config.ts\" ]; then\n # Vite project\nelif [ -f \"next.config.js\" ]; then\n # Next.js project\nelif [ -f \"nuxt.config.ts\" ]; then\n # Nuxt project\nfi\n```\n\n## Common Mistakes to Avoid\n\n\u274C **Blocking operations**: `sleep 60` (blocks dmux)\n\u2705 **Background long tasks**: `slow_operation &`\n\n\u274C **Hardcoded paths**: `/Users/me/project`\n\u2705 **Use variables**: `\"$DMUX_ROOT\"`\n\n\u274C **Assuming tools exist**: `pnpm install`\n\u2705 **Check first**: `command -v pnpm && pnpm install`\n\n\u274C **No error handling**: Script fails silently\n\u2705 **Set error mode**: `set -e` or check exit codes\n\n\u274C **Forgetting executable bit**: Hook won't run\n\u2705 **Make executable**: `chmod +x`\n\n\u274C **Noisy output**: Clutters dmux logs\n\u2705 **Silent operations**: `curl -s`, `> /dev/null 2>&1`\n\n\u274C **Not testing**: Deploy and hope\n\u2705 **Test manually**: Run with mock env vars first\n\n## Debugging\n\nIf a hook isn't working:\n\n1. **Check if file exists**: `ls -la .dmux-hooks/`\n2. **Check permissions**: Should show `x` in `rwxr-xr-x`\n3. **Check syntax**: `bash -n .dmux-hooks/hook_name`\n4. **Test manually**: Set env vars and run\n5. **Check logs**: dmux logs to stderr with `[Hooks]` prefix\n6. **Simplify**: Remove complex parts, test basic version\n7. **Check tool availability**: `command -v required_tool`\n\n### Debug Mode\n```bash\n#!/bin/bash\n# Add to top of hook for debugging\nset -x # Print each command before executing\nset -e # Exit on error\n\n# Your hook logic here\n```\n\n## Summary Checklist\n\nWhen creating a new hook:\n\n- [ ] Create file in `.dmux-hooks/`\n- [ ] Add shebang: `#!/bin/bash`\n- [ ] Make executable: `chmod +x`\n- [ ] Add `set -e` for error handling\n- [ ] Use environment variables (never hardcode paths)\n- [ ] Background long operations with `&`\n- [ ] Check for required tools before using\n- [ ] Test manually with mock env vars\n- [ ] Add comments explaining what it does\n- [ ] Commit to version control\n\n## Getting Help\n\n- **Full documentation**: See `HOOKS.md` in project root\n- **Claude-specific tips**: See `CLAUDE.md` in `.dmux-hooks/`\n- **Examples**: Check `.dmux-hooks/examples/` directory\n- **dmux API**: See `API.md` for REST endpoints\n\n---\n\n*This documentation was auto-generated from dmux source code.*\n*Version: 2025-12-11*\n";
|
|
5
|
+
export declare const AGENTS_MD = "# dmux Hooks System - Agent Reference\n\n**Auto-generated documentation for AI agents**\n\nThis document contains everything an AI agent needs to create, modify, and understand dmux hooks. It is automatically generated from the dmux source code and embedded in the binary.\n\n## What You're Working On\n\nYou are editing hooks for **dmux**, a tmux pane manager that creates AI-powered development workflows. Each pane runs in its own git worktree with an AI agent (Claude Code or opencode).\n\n## Your Goal\n\nCreate executable bash scripts in `.dmux-hooks/` that run automatically at key lifecycle events.\n\n## Quick Start\n\n1. **Create a hook file**: `touch .dmux-hooks/worktree_created`\n2. **Make it executable**: `chmod +x .dmux-hooks/worktree_created`\n3. **Add shebang**: Start with `#!/bin/bash`\n4. **Use environment variables**: Access `$DMUX_ROOT`, `$DMUX_WORKTREE_PATH`, etc.\n5. **Test it**: Set env vars manually and run the script\n\n## Hook Execution Model\n\n- **Non-blocking**: Hooks run in background (detached processes)\n- **Silent failures**: Hook errors are logged but don't stop dmux\n- **Environment-based**: All context passed via environment variables\n- **Version controlled**: Hooks in `.dmux-hooks/` are shared with team\n- **Priority resolution**: `.dmux-hooks/` \u2192 `.dmux/hooks/` \u2192 `~/.dmux/hooks/`\n\n## Available Hooks\n\n### Pane Lifecycle Hooks\n\n| Hook | When | Common Use Cases |\n|------|------|------------------|\n| `before_pane_create` | Before pane creation | Validation, notifications, pre-flight checks |\n| `pane_created` | After pane, before worktree | Configure tmux settings, prepare environment |\n| `worktree_created` | After full setup | Install deps, copy configs, setup git |\n| `before_pane_close` | Before closing | Save state, backup uncommitted work |\n| `pane_closed` | After closed | Cleanup resources, analytics, notifications |\n\n### Worktree Lifecycle Hooks\n\n| Hook | When | Common Use Cases |\n|------|------|------------------|\n| `before_worktree_remove` | Before worktree removal | Archive worktree, save artifacts |\n| `worktree_removed` | After worktree removed | Cleanup external references |\n\n### Merge Lifecycle Hooks\n\n| Hook | When | Common Use Cases |\n|------|------|------------------|\n| `pre_merge` | Before merge operation | Run final tests, create backups |\n| `post_merge` | After successful merge | Deploy, close issues, notify team |\n\n### Interactive Hooks (with HTTP callbacks)\n\n| Hook | When | Common Use Cases |\n|------|------|------------------|\n| `run_test` | When tests triggered | Run test suite, report status via HTTP |\n| `run_dev` | When dev server triggered | Start dev server, create tunnel, report URL |\n\n\n## Environment Variables\n\n### Always Available\n```bash\nDMUX_ROOT=\"/path/to/project\" # Project root directory\nDMUX_SERVER_PORT=\"3142\" # HTTP server port\n```\n\n### Pane Context (most hooks)\n```bash\nDMUX_PANE_ID=\"dmux-1234567890\" # dmux pane identifier\nDMUX_SLUG=\"fix-auth-bug\" # Branch/worktree name\nDMUX_PROMPT=\"Fix authentication bug\" # User's prompt\nDMUX_AGENT=\"claude\" # Agent type (claude|opencode)\nDMUX_TMUX_PANE_ID=\"%38\" # tmux pane ID\n```\n\n### Worktree Context\n```bash\nDMUX_WORKTREE_PATH=\"/path/.dmux/worktrees/fix-auth-bug\"\nDMUX_BRANCH=\"fix-auth-bug\" # Same as slug\n```\n\n### Merge Context\n```bash\nDMUX_TARGET_BRANCH=\"main\" # Branch being merged into\n```\n\n## HTTP Callback API\n\nInteractive hooks (`run_test` and `run_dev`) can update dmux UI via HTTP.\n\n### Update Test Status\n```bash\ncurl -X PUT \"http://localhost:$DMUX_SERVER_PORT/api/panes/$DMUX_PANE_ID/test\" -H \"Content-Type: application/json\" -d '{\"status\": \"running\", \"output\": \"optional test output\"}'\n\n# Status values: \"running\" | \"passed\" | \"failed\"\n```\n\n### Update Dev Server\n```bash\ncurl -X PUT \"http://localhost:$DMUX_SERVER_PORT/api/panes/$DMUX_PANE_ID/dev\" -H \"Content-Type: application/json\" -d '{\"status\": \"running\", \"url\": \"http://localhost:3000\"}'\n\n# Status values: \"running\" | \"stopped\"\n# url: Can be localhost or tunnel URL (ngrok, cloudflared, etc.)\n```\n\n## Common Patterns\n\n### Pattern 1: Install Dependencies\n```bash\n#!/bin/bash\n# .dmux-hooks/worktree_created\n\ncd \"$DMUX_WORKTREE_PATH\"\n\nif [ -f \"pnpm-lock.yaml\" ]; then\n pnpm install --prefer-offline &\nelif [ -f \"package-lock.json\" ]; then\n npm install &\nelif [ -f \"yarn.lock\" ]; then\n yarn install &\nelif [ -f \"Gemfile\" ]; then\n bundle install &\nelif [ -f \"requirements.txt\" ]; then\n pip install -r requirements.txt &\nelif [ -f \"Cargo.toml\" ]; then\n cargo build &\nfi\n```\n\n### Pattern 2: Copy Configuration\n```bash\n#!/bin/bash\n# .dmux-hooks/worktree_created\n\n# Copy environment file\nif [ -f \"$DMUX_ROOT/.env.local\" ]; then\n cp \"$DMUX_ROOT/.env.local\" \"$DMUX_WORKTREE_PATH/.env.local\"\nfi\n\n# Copy other config files\nfor file in .env.development .npmrc .yarnrc; do\n if [ -f \"$DMUX_ROOT/$file\" ]; then\n cp \"$DMUX_ROOT/$file\" \"$DMUX_WORKTREE_PATH/$file\"\n fi\ndone\n```\n\n### Pattern 3: Run Tests with Status Updates\n```bash\n#!/bin/bash\n# .dmux-hooks/run_test\n\nset -e\ncd \"$DMUX_WORKTREE_PATH\"\nAPI=\"http://localhost:$DMUX_SERVER_PORT/api/panes/$DMUX_PANE_ID/test\"\n\n# Update: starting\ncurl -s -X PUT \"$API\" -H \"Content-Type: application/json\" -d '{\"status\": \"running\"}' > /dev/null\n\n# Run tests and capture output\nOUTPUT_FILE=\"/tmp/dmux-test-$DMUX_PANE_ID.txt\"\nif pnpm test > \"$OUTPUT_FILE\" 2>&1; then\n STATUS=\"passed\"\nelse\n STATUS=\"failed\"\nfi\n\n# Get output (truncate if too long)\nOUTPUT=$(head -c 5000 \"$OUTPUT_FILE\")\n\n# Update: complete\ncurl -s -X PUT \"$API\" -H \"Content-Type: application/json\" -d \"$(jq -n --arg status \"$STATUS\" --arg output \"$OUTPUT\" '{status: $status, output: $output}')\" > /dev/null\n\nrm -f \"$OUTPUT_FILE\"\n```\n\n### Pattern 4: Dev Server with Tunnel\n```bash\n#!/bin/bash\n# .dmux-hooks/run_dev\n\nset -e\ncd \"$DMUX_WORKTREE_PATH\"\nAPI=\"http://localhost:$DMUX_SERVER_PORT/api/panes/$DMUX_PANE_ID/dev\"\n\n# Start dev server in background\nLOG_FILE=\"/tmp/dmux-dev-$DMUX_PANE_ID.log\"\npnpm dev > \"$LOG_FILE\" 2>&1 &\nDEV_PID=$!\n\n# Wait for server to start\nsleep 5\n\n# Detect port from logs\nPORT=$(grep -oP 'localhost:Kd+' \"$LOG_FILE\" | head -1)\n[ -z \"$PORT\" ] && PORT=3000\n\n# Optional: Create tunnel with cloudflared\nif command -v cloudflared &> /dev/null; then\n TUNNEL=$(cloudflared tunnel --url \"http://localhost:$PORT\" 2>&1 | grep -oP 'https://[a-z0-9-]+.trycloudflare.com' | head -1)\n URL=\"${TUNNEL:-http://localhost:$PORT}\"\nelse\n URL=\"http://localhost:$PORT\"\nfi\n\n# Report status\ncurl -s -X PUT \"$API\" -H \"Content-Type: application/json\" -d \"{\"status\": \"running\", \"url\": \"$URL\"}\" > /dev/null\n\necho \"[Hook] Dev server running at $URL (PID: $DEV_PID)\"\n```\n\n### Pattern 5: Post-Merge Deployment\n```bash\n#!/bin/bash\n# .dmux-hooks/post_merge\n\nset -e\ncd \"$DMUX_ROOT\"\n\n# Only deploy from main/master\nif [ \"$DMUX_TARGET_BRANCH\" != \"main\" ] && [ \"$DMUX_TARGET_BRANCH\" != \"master\" ]; then\n exit 0\nfi\n\n# Push to remote\ngit push origin \"$DMUX_TARGET_BRANCH\"\n\n# Trigger deployment (example: Vercel)\nif [ -n \"$VERCEL_TOKEN\" ]; then\n curl -s -X POST \"https://api.vercel.com/v1/deployments\" -H \"Authorization: Bearer $VERCEL_TOKEN\" -H \"Content-Type: application/json\" -d '{\"name\": \"my-project\"}' > /dev/null\nfi\n\n# Close GitHub issue if prompt contains #123\nISSUE=$(echo \"$DMUX_PROMPT\" | grep -oP '#Kd+' | head -1)\nif [ -n \"$ISSUE\" ] && command -v gh &> /dev/null; then\n gh issue close \"$ISSUE\" -c \"Resolved in $DMUX_SLUG, merged to $DMUX_TARGET_BRANCH\" 2>/dev/null || true\nfi\n```\n\n## Best Practices\n\n1. **Always start with shebang**: `#!/bin/bash`\n2. **Set error handling**: `set -e` (exit on error)\n3. **Make executable**: `chmod +x .dmux-hooks/hook_name`\n4. **Background long operations**: Append `&` to avoid blocking\n5. **Check for required tools**: `command -v tool &> /dev/null`\n6. **Log for debugging**: `echo \"[Hook] message\" >> \"$DMUX_ROOT/.dmux/hooks.log\"`\n7. **Handle missing vars gracefully**: `[ -z \"$VAR\" ] && exit 0`\n8. **Use silent curl**: `curl -s` to avoid noise in logs\n9. **Clean up temp files**: Remove files in `/tmp/`\n10. **Test before committing**: Run hooks manually with mock env vars\n\n## Testing Hooks\n\n### Manual Testing\n```bash\n# 1. Set environment variables\nexport DMUX_ROOT=\"$(pwd)\"\nexport DMUX_PANE_ID=\"test-pane\"\nexport DMUX_SLUG=\"test-branch\"\nexport DMUX_WORKTREE_PATH=\"$(pwd)\"\nexport DMUX_SERVER_PORT=\"3142\"\nexport DMUX_AGENT=\"claude\"\nexport DMUX_PROMPT=\"Test prompt\"\n\n# 2. Run hook directly\n./.dmux-hooks/worktree_created\n\n# 3. Check exit code\necho $? # Should be 0 for success\n```\n\n### Syntax Check\n```bash\n# Check for syntax errors without running\nbash -n ./.dmux-hooks/worktree_created\n```\n\n### Shellcheck (if available)\n```bash\nshellcheck ./.dmux-hooks/worktree_created\n```\n\n## Project Context Analysis\n\nBefore creating hooks, analyze these files in the project:\n\n### Package Manager Detection\n```bash\n# Check which package manager is used\nif [ -f \"pnpm-lock.yaml\" ]; then\n # Use: pnpm install, pnpm test, pnpm dev\nelif [ -f \"package-lock.json\" ]; then\n # Use: npm install, npm test, npm run dev\nelif [ -f \"yarn.lock\" ]; then\n # Use: yarn install, yarn test, yarn dev\nfi\n```\n\n### Test Command Discovery\n```bash\n# Read package.json to find test command\ncat package.json | grep '\"test\"'\n# Or with jq:\njq -r '.scripts.test' package.json\n```\n\n### Dev Command Discovery\n```bash\n# Read package.json to find dev command\ncat package.json | grep '\"dev\"'\n# Or with jq:\njq -r '.scripts.dev' package.json\n```\n\n### Environment Variables\n```bash\n# Check for .env files to copy\nls -la | grep '.env'\n```\n\n### Build System\n```bash\n# Detect build system\nif [ -f \"vite.config.ts\" ]; then\n # Vite project\nelif [ -f \"next.config.js\" ]; then\n # Next.js project\nelif [ -f \"nuxt.config.ts\" ]; then\n # Nuxt project\nfi\n```\n\n## Common Mistakes to Avoid\n\n\u274C **Blocking operations**: `sleep 60` (blocks dmux)\n\u2705 **Background long tasks**: `slow_operation &`\n\n\u274C **Hardcoded paths**: `/Users/me/project`\n\u2705 **Use variables**: `\"$DMUX_ROOT\"`\n\n\u274C **Assuming tools exist**: `pnpm install`\n\u2705 **Check first**: `command -v pnpm && pnpm install`\n\n\u274C **No error handling**: Script fails silently\n\u2705 **Set error mode**: `set -e` or check exit codes\n\n\u274C **Forgetting executable bit**: Hook won't run\n\u2705 **Make executable**: `chmod +x`\n\n\u274C **Noisy output**: Clutters dmux logs\n\u2705 **Silent operations**: `curl -s`, `> /dev/null 2>&1`\n\n\u274C **Not testing**: Deploy and hope\n\u2705 **Test manually**: Run with mock env vars first\n\n## Debugging\n\nIf a hook isn't working:\n\n1. **Check if file exists**: `ls -la .dmux-hooks/`\n2. **Check permissions**: Should show `x` in `rwxr-xr-x`\n3. **Check syntax**: `bash -n .dmux-hooks/hook_name`\n4. **Test manually**: Set env vars and run\n5. **Check logs**: dmux logs to stderr with `[Hooks]` prefix\n6. **Simplify**: Remove complex parts, test basic version\n7. **Check tool availability**: `command -v required_tool`\n\n### Debug Mode\n```bash\n#!/bin/bash\n# Add to top of hook for debugging\nset -x # Print each command before executing\nset -e # Exit on error\n\n# Your hook logic here\n```\n\n## Summary Checklist\n\nWhen creating a new hook:\n\n- [ ] Create file in `.dmux-hooks/`\n- [ ] Add shebang: `#!/bin/bash`\n- [ ] Make executable: `chmod +x`\n- [ ] Add `set -e` for error handling\n- [ ] Use environment variables (never hardcode paths)\n- [ ] Background long operations with `&`\n- [ ] Check for required tools before using\n- [ ] Test manually with mock env vars\n- [ ] Add comments explaining what it does\n- [ ] Commit to version control\n\n## Getting Help\n\n- **Full documentation**: See `HOOKS.md` in project root\n- **Claude-specific tips**: See `CLAUDE.md` in `.dmux-hooks/`\n- **Examples**: Check `.dmux-hooks/examples/` directory\n- **dmux API**: See `API.md` for REST endpoints\n\n---\n\n*This documentation was auto-generated from dmux source code.*\n*Version: 2025-12-12*\n";
|
|
6
6
|
//# sourceMappingURL=generated-agents-doc.d.ts.map
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* Main documentation - gets written as both AGENTS.md and CLAUDE.md
|
|
10
10
|
* Different agents look for different filenames, but content is identical
|
|
11
11
|
*/
|
|
12
|
-
export declare const HOOKS_DOCUMENTATION = "# dmux Hooks System - Agent Reference\n\n**Auto-generated documentation for AI agents**\n\nThis document contains everything an AI agent needs to create, modify, and understand dmux hooks. It is automatically generated from the dmux source code and embedded in the binary.\n\n## What You're Working On\n\nYou are editing hooks for **dmux**, a tmux pane manager that creates AI-powered development workflows. Each pane runs in its own git worktree with an AI agent (Claude Code or opencode).\n\n## Your Goal\n\nCreate executable bash scripts in `.dmux-hooks/` that run automatically at key lifecycle events.\n\n## Quick Start\n\n1. **Create a hook file**: `touch .dmux-hooks/worktree_created`\n2. **Make it executable**: `chmod +x .dmux-hooks/worktree_created`\n3. **Add shebang**: Start with `#!/bin/bash`\n4. **Use environment variables**: Access `$DMUX_ROOT`, `$DMUX_WORKTREE_PATH`, etc.\n5. **Test it**: Set env vars manually and run the script\n\n## Hook Execution Model\n\n- **Non-blocking**: Hooks run in background (detached processes)\n- **Silent failures**: Hook errors are logged but don't stop dmux\n- **Environment-based**: All context passed via environment variables\n- **Version controlled**: Hooks in `.dmux-hooks/` are shared with team\n- **Priority resolution**: `.dmux-hooks/` \u2192 `.dmux/hooks/` \u2192 `~/.dmux/hooks/`\n\n## Available Hooks\n\n### Pane Lifecycle Hooks\n\n| Hook | When | Common Use Cases |\n|------|------|------------------|\n| `before_pane_create` | Before pane creation | Validation, notifications, pre-flight checks |\n| `pane_created` | After pane, before worktree | Configure tmux settings, prepare environment |\n| `worktree_created` | After full setup | Install deps, copy configs, setup git |\n| `before_pane_close` | Before closing | Save state, backup uncommitted work |\n| `pane_closed` | After closed | Cleanup resources, analytics, notifications |\n\n### Worktree Lifecycle Hooks\n\n| Hook | When | Common Use Cases |\n|------|------|------------------|\n| `before_worktree_remove` | Before worktree removal | Archive worktree, save artifacts |\n| `worktree_removed` | After worktree removed | Cleanup external references |\n\n### Merge Lifecycle Hooks\n\n| Hook | When | Common Use Cases |\n|------|------|------------------|\n| `pre_merge` | Before merge operation | Run final tests, create backups |\n| `post_merge` | After successful merge | Deploy, close issues, notify team |\n\n### Interactive Hooks (with HTTP callbacks)\n\n| Hook | When | Common Use Cases |\n|------|------|------------------|\n| `run_test` | When tests triggered | Run test suite, report status via HTTP |\n| `run_dev` | When dev server triggered | Start dev server, create tunnel, report URL |\n\n\n## Environment Variables\n\n### Always Available\n```bash\nDMUX_ROOT=\"/path/to/project\" # Project root directory\nDMUX_SERVER_PORT=\"3142\" # HTTP server port\n```\n\n### Pane Context (most hooks)\n```bash\nDMUX_PANE_ID=\"dmux-1234567890\" # dmux pane identifier\nDMUX_SLUG=\"fix-auth-bug\" # Branch/worktree name\nDMUX_PROMPT=\"Fix authentication bug\" # User's prompt\nDMUX_AGENT=\"claude\" # Agent type (claude|opencode)\nDMUX_TMUX_PANE_ID=\"%38\" # tmux pane ID\n```\n\n### Worktree Context\n```bash\nDMUX_WORKTREE_PATH=\"/path/.dmux/worktrees/fix-auth-bug\"\nDMUX_BRANCH=\"fix-auth-bug\" # Same as slug\n```\n\n### Merge Context\n```bash\nDMUX_TARGET_BRANCH=\"main\" # Branch being merged into\n```\n\n## HTTP Callback API\n\nInteractive hooks (`run_test` and `run_dev`) can update dmux UI via HTTP.\n\n### Update Test Status\n```bash\ncurl -X PUT \"http://localhost:$DMUX_SERVER_PORT/api/panes/$DMUX_PANE_ID/test\" -H \"Content-Type: application/json\" -d '{\"status\": \"running\", \"output\": \"optional test output\"}'\n\n# Status values: \"running\" | \"passed\" | \"failed\"\n```\n\n### Update Dev Server\n```bash\ncurl -X PUT \"http://localhost:$DMUX_SERVER_PORT/api/panes/$DMUX_PANE_ID/dev\" -H \"Content-Type: application/json\" -d '{\"status\": \"running\", \"url\": \"http://localhost:3000\"}'\n\n# Status values: \"running\" | \"stopped\"\n# url: Can be localhost or tunnel URL (ngrok, cloudflared, etc.)\n```\n\n## Common Patterns\n\n### Pattern 1: Install Dependencies\n```bash\n#!/bin/bash\n# .dmux-hooks/worktree_created\n\ncd \"$DMUX_WORKTREE_PATH\"\n\nif [ -f \"pnpm-lock.yaml\" ]; then\n pnpm install --prefer-offline &\nelif [ -f \"package-lock.json\" ]; then\n npm install &\nelif [ -f \"yarn.lock\" ]; then\n yarn install &\nelif [ -f \"Gemfile\" ]; then\n bundle install &\nelif [ -f \"requirements.txt\" ]; then\n pip install -r requirements.txt &\nelif [ -f \"Cargo.toml\" ]; then\n cargo build &\nfi\n```\n\n### Pattern 2: Copy Configuration\n```bash\n#!/bin/bash\n# .dmux-hooks/worktree_created\n\n# Copy environment file\nif [ -f \"$DMUX_ROOT/.env.local\" ]; then\n cp \"$DMUX_ROOT/.env.local\" \"$DMUX_WORKTREE_PATH/.env.local\"\nfi\n\n# Copy other config files\nfor file in .env.development .npmrc .yarnrc; do\n if [ -f \"$DMUX_ROOT/$file\" ]; then\n cp \"$DMUX_ROOT/$file\" \"$DMUX_WORKTREE_PATH/$file\"\n fi\ndone\n```\n\n### Pattern 3: Run Tests with Status Updates\n```bash\n#!/bin/bash\n# .dmux-hooks/run_test\n\nset -e\ncd \"$DMUX_WORKTREE_PATH\"\nAPI=\"http://localhost:$DMUX_SERVER_PORT/api/panes/$DMUX_PANE_ID/test\"\n\n# Update: starting\ncurl -s -X PUT \"$API\" -H \"Content-Type: application/json\" -d '{\"status\": \"running\"}' > /dev/null\n\n# Run tests and capture output\nOUTPUT_FILE=\"/tmp/dmux-test-$DMUX_PANE_ID.txt\"\nif pnpm test > \"$OUTPUT_FILE\" 2>&1; then\n STATUS=\"passed\"\nelse\n STATUS=\"failed\"\nfi\n\n# Get output (truncate if too long)\nOUTPUT=$(head -c 5000 \"$OUTPUT_FILE\")\n\n# Update: complete\ncurl -s -X PUT \"$API\" -H \"Content-Type: application/json\" -d \"$(jq -n --arg status \"$STATUS\" --arg output \"$OUTPUT\" '{status: $status, output: $output}')\" > /dev/null\n\nrm -f \"$OUTPUT_FILE\"\n```\n\n### Pattern 4: Dev Server with Tunnel\n```bash\n#!/bin/bash\n# .dmux-hooks/run_dev\n\nset -e\ncd \"$DMUX_WORKTREE_PATH\"\nAPI=\"http://localhost:$DMUX_SERVER_PORT/api/panes/$DMUX_PANE_ID/dev\"\n\n# Start dev server in background\nLOG_FILE=\"/tmp/dmux-dev-$DMUX_PANE_ID.log\"\npnpm dev > \"$LOG_FILE\" 2>&1 &\nDEV_PID=$!\n\n# Wait for server to start\nsleep 5\n\n# Detect port from logs\nPORT=$(grep -oP 'localhost:Kd+' \"$LOG_FILE\" | head -1)\n[ -z \"$PORT\" ] && PORT=3000\n\n# Optional: Create tunnel with cloudflared\nif command -v cloudflared &> /dev/null; then\n TUNNEL=$(cloudflared tunnel --url \"http://localhost:$PORT\" 2>&1 | grep -oP 'https://[a-z0-9-]+.trycloudflare.com' | head -1)\n URL=\"${TUNNEL:-http://localhost:$PORT}\"\nelse\n URL=\"http://localhost:$PORT\"\nfi\n\n# Report status\ncurl -s -X PUT \"$API\" -H \"Content-Type: application/json\" -d \"{\"status\": \"running\", \"url\": \"$URL\"}\" > /dev/null\n\necho \"[Hook] Dev server running at $URL (PID: $DEV_PID)\"\n```\n\n### Pattern 5: Post-Merge Deployment\n```bash\n#!/bin/bash\n# .dmux-hooks/post_merge\n\nset -e\ncd \"$DMUX_ROOT\"\n\n# Only deploy from main/master\nif [ \"$DMUX_TARGET_BRANCH\" != \"main\" ] && [ \"$DMUX_TARGET_BRANCH\" != \"master\" ]; then\n exit 0\nfi\n\n# Push to remote\ngit push origin \"$DMUX_TARGET_BRANCH\"\n\n# Trigger deployment (example: Vercel)\nif [ -n \"$VERCEL_TOKEN\" ]; then\n curl -s -X POST \"https://api.vercel.com/v1/deployments\" -H \"Authorization: Bearer $VERCEL_TOKEN\" -H \"Content-Type: application/json\" -d '{\"name\": \"my-project\"}' > /dev/null\nfi\n\n# Close GitHub issue if prompt contains #123\nISSUE=$(echo \"$DMUX_PROMPT\" | grep -oP '#Kd+' | head -1)\nif [ -n \"$ISSUE\" ] && command -v gh &> /dev/null; then\n gh issue close \"$ISSUE\" -c \"Resolved in $DMUX_SLUG, merged to $DMUX_TARGET_BRANCH\" 2>/dev/null || true\nfi\n```\n\n## Best Practices\n\n1. **Always start with shebang**: `#!/bin/bash`\n2. **Set error handling**: `set -e` (exit on error)\n3. **Make executable**: `chmod +x .dmux-hooks/hook_name`\n4. **Background long operations**: Append `&` to avoid blocking\n5. **Check for required tools**: `command -v tool &> /dev/null`\n6. **Log for debugging**: `echo \"[Hook] message\" >> \"$DMUX_ROOT/.dmux/hooks.log\"`\n7. **Handle missing vars gracefully**: `[ -z \"$VAR\" ] && exit 0`\n8. **Use silent curl**: `curl -s` to avoid noise in logs\n9. **Clean up temp files**: Remove files in `/tmp/`\n10. **Test before committing**: Run hooks manually with mock env vars\n\n## Testing Hooks\n\n### Manual Testing\n```bash\n# 1. Set environment variables\nexport DMUX_ROOT=\"$(pwd)\"\nexport DMUX_PANE_ID=\"test-pane\"\nexport DMUX_SLUG=\"test-branch\"\nexport DMUX_WORKTREE_PATH=\"$(pwd)\"\nexport DMUX_SERVER_PORT=\"3142\"\nexport DMUX_AGENT=\"claude\"\nexport DMUX_PROMPT=\"Test prompt\"\n\n# 2. Run hook directly\n./.dmux-hooks/worktree_created\n\n# 3. Check exit code\necho $? # Should be 0 for success\n```\n\n### Syntax Check\n```bash\n# Check for syntax errors without running\nbash -n ./.dmux-hooks/worktree_created\n```\n\n### Shellcheck (if available)\n```bash\nshellcheck ./.dmux-hooks/worktree_created\n```\n\n## Project Context Analysis\n\nBefore creating hooks, analyze these files in the project:\n\n### Package Manager Detection\n```bash\n# Check which package manager is used\nif [ -f \"pnpm-lock.yaml\" ]; then\n # Use: pnpm install, pnpm test, pnpm dev\nelif [ -f \"package-lock.json\" ]; then\n # Use: npm install, npm test, npm run dev\nelif [ -f \"yarn.lock\" ]; then\n # Use: yarn install, yarn test, yarn dev\nfi\n```\n\n### Test Command Discovery\n```bash\n# Read package.json to find test command\ncat package.json | grep '\"test\"'\n# Or with jq:\njq -r '.scripts.test' package.json\n```\n\n### Dev Command Discovery\n```bash\n# Read package.json to find dev command\ncat package.json | grep '\"dev\"'\n# Or with jq:\njq -r '.scripts.dev' package.json\n```\n\n### Environment Variables\n```bash\n# Check for .env files to copy\nls -la | grep '.env'\n```\n\n### Build System\n```bash\n# Detect build system\nif [ -f \"vite.config.ts\" ]; then\n # Vite project\nelif [ -f \"next.config.js\" ]; then\n # Next.js project\nelif [ -f \"nuxt.config.ts\" ]; then\n # Nuxt project\nfi\n```\n\n## Common Mistakes to Avoid\n\n\u274C **Blocking operations**: `sleep 60` (blocks dmux)\n\u2705 **Background long tasks**: `slow_operation &`\n\n\u274C **Hardcoded paths**: `/Users/me/project`\n\u2705 **Use variables**: `\"$DMUX_ROOT\"`\n\n\u274C **Assuming tools exist**: `pnpm install`\n\u2705 **Check first**: `command -v pnpm && pnpm install`\n\n\u274C **No error handling**: Script fails silently\n\u2705 **Set error mode**: `set -e` or check exit codes\n\n\u274C **Forgetting executable bit**: Hook won't run\n\u2705 **Make executable**: `chmod +x`\n\n\u274C **Noisy output**: Clutters dmux logs\n\u2705 **Silent operations**: `curl -s`, `> /dev/null 2>&1`\n\n\u274C **Not testing**: Deploy and hope\n\u2705 **Test manually**: Run with mock env vars first\n\n## Debugging\n\nIf a hook isn't working:\n\n1. **Check if file exists**: `ls -la .dmux-hooks/`\n2. **Check permissions**: Should show `x` in `rwxr-xr-x`\n3. **Check syntax**: `bash -n .dmux-hooks/hook_name`\n4. **Test manually**: Set env vars and run\n5. **Check logs**: dmux logs to stderr with `[Hooks]` prefix\n6. **Simplify**: Remove complex parts, test basic version\n7. **Check tool availability**: `command -v required_tool`\n\n### Debug Mode\n```bash\n#!/bin/bash\n# Add to top of hook for debugging\nset -x # Print each command before executing\nset -e # Exit on error\n\n# Your hook logic here\n```\n\n## Summary Checklist\n\nWhen creating a new hook:\n\n- [ ] Create file in `.dmux-hooks/`\n- [ ] Add shebang: `#!/bin/bash`\n- [ ] Make executable: `chmod +x`\n- [ ] Add `set -e` for error handling\n- [ ] Use environment variables (never hardcode paths)\n- [ ] Background long operations with `&`\n- [ ] Check for required tools before using\n- [ ] Test manually with mock env vars\n- [ ] Add comments explaining what it does\n- [ ] Commit to version control\n\n## Getting Help\n\n- **Full documentation**: See `HOOKS.md` in project root\n- **Claude-specific tips**: See `CLAUDE.md` in `.dmux-hooks/`\n- **Examples**: Check `.dmux-hooks/examples/` directory\n- **dmux API**: See `API.md` for REST endpoints\n\n---\n\n*This documentation was auto-generated from dmux source code.*\n*Version: 2025-12-11*\n";
|
|
12
|
+
export declare const HOOKS_DOCUMENTATION = "# dmux Hooks System - Agent Reference\n\n**Auto-generated documentation for AI agents**\n\nThis document contains everything an AI agent needs to create, modify, and understand dmux hooks. It is automatically generated from the dmux source code and embedded in the binary.\n\n## What You're Working On\n\nYou are editing hooks for **dmux**, a tmux pane manager that creates AI-powered development workflows. Each pane runs in its own git worktree with an AI agent (Claude Code or opencode).\n\n## Your Goal\n\nCreate executable bash scripts in `.dmux-hooks/` that run automatically at key lifecycle events.\n\n## Quick Start\n\n1. **Create a hook file**: `touch .dmux-hooks/worktree_created`\n2. **Make it executable**: `chmod +x .dmux-hooks/worktree_created`\n3. **Add shebang**: Start with `#!/bin/bash`\n4. **Use environment variables**: Access `$DMUX_ROOT`, `$DMUX_WORKTREE_PATH`, etc.\n5. **Test it**: Set env vars manually and run the script\n\n## Hook Execution Model\n\n- **Non-blocking**: Hooks run in background (detached processes)\n- **Silent failures**: Hook errors are logged but don't stop dmux\n- **Environment-based**: All context passed via environment variables\n- **Version controlled**: Hooks in `.dmux-hooks/` are shared with team\n- **Priority resolution**: `.dmux-hooks/` \u2192 `.dmux/hooks/` \u2192 `~/.dmux/hooks/`\n\n## Available Hooks\n\n### Pane Lifecycle Hooks\n\n| Hook | When | Common Use Cases |\n|------|------|------------------|\n| `before_pane_create` | Before pane creation | Validation, notifications, pre-flight checks |\n| `pane_created` | After pane, before worktree | Configure tmux settings, prepare environment |\n| `worktree_created` | After full setup | Install deps, copy configs, setup git |\n| `before_pane_close` | Before closing | Save state, backup uncommitted work |\n| `pane_closed` | After closed | Cleanup resources, analytics, notifications |\n\n### Worktree Lifecycle Hooks\n\n| Hook | When | Common Use Cases |\n|------|------|------------------|\n| `before_worktree_remove` | Before worktree removal | Archive worktree, save artifacts |\n| `worktree_removed` | After worktree removed | Cleanup external references |\n\n### Merge Lifecycle Hooks\n\n| Hook | When | Common Use Cases |\n|------|------|------------------|\n| `pre_merge` | Before merge operation | Run final tests, create backups |\n| `post_merge` | After successful merge | Deploy, close issues, notify team |\n\n### Interactive Hooks (with HTTP callbacks)\n\n| Hook | When | Common Use Cases |\n|------|------|------------------|\n| `run_test` | When tests triggered | Run test suite, report status via HTTP |\n| `run_dev` | When dev server triggered | Start dev server, create tunnel, report URL |\n\n\n## Environment Variables\n\n### Always Available\n```bash\nDMUX_ROOT=\"/path/to/project\" # Project root directory\nDMUX_SERVER_PORT=\"3142\" # HTTP server port\n```\n\n### Pane Context (most hooks)\n```bash\nDMUX_PANE_ID=\"dmux-1234567890\" # dmux pane identifier\nDMUX_SLUG=\"fix-auth-bug\" # Branch/worktree name\nDMUX_PROMPT=\"Fix authentication bug\" # User's prompt\nDMUX_AGENT=\"claude\" # Agent type (claude|opencode)\nDMUX_TMUX_PANE_ID=\"%38\" # tmux pane ID\n```\n\n### Worktree Context\n```bash\nDMUX_WORKTREE_PATH=\"/path/.dmux/worktrees/fix-auth-bug\"\nDMUX_BRANCH=\"fix-auth-bug\" # Same as slug\n```\n\n### Merge Context\n```bash\nDMUX_TARGET_BRANCH=\"main\" # Branch being merged into\n```\n\n## HTTP Callback API\n\nInteractive hooks (`run_test` and `run_dev`) can update dmux UI via HTTP.\n\n### Update Test Status\n```bash\ncurl -X PUT \"http://localhost:$DMUX_SERVER_PORT/api/panes/$DMUX_PANE_ID/test\" -H \"Content-Type: application/json\" -d '{\"status\": \"running\", \"output\": \"optional test output\"}'\n\n# Status values: \"running\" | \"passed\" | \"failed\"\n```\n\n### Update Dev Server\n```bash\ncurl -X PUT \"http://localhost:$DMUX_SERVER_PORT/api/panes/$DMUX_PANE_ID/dev\" -H \"Content-Type: application/json\" -d '{\"status\": \"running\", \"url\": \"http://localhost:3000\"}'\n\n# Status values: \"running\" | \"stopped\"\n# url: Can be localhost or tunnel URL (ngrok, cloudflared, etc.)\n```\n\n## Common Patterns\n\n### Pattern 1: Install Dependencies\n```bash\n#!/bin/bash\n# .dmux-hooks/worktree_created\n\ncd \"$DMUX_WORKTREE_PATH\"\n\nif [ -f \"pnpm-lock.yaml\" ]; then\n pnpm install --prefer-offline &\nelif [ -f \"package-lock.json\" ]; then\n npm install &\nelif [ -f \"yarn.lock\" ]; then\n yarn install &\nelif [ -f \"Gemfile\" ]; then\n bundle install &\nelif [ -f \"requirements.txt\" ]; then\n pip install -r requirements.txt &\nelif [ -f \"Cargo.toml\" ]; then\n cargo build &\nfi\n```\n\n### Pattern 2: Copy Configuration\n```bash\n#!/bin/bash\n# .dmux-hooks/worktree_created\n\n# Copy environment file\nif [ -f \"$DMUX_ROOT/.env.local\" ]; then\n cp \"$DMUX_ROOT/.env.local\" \"$DMUX_WORKTREE_PATH/.env.local\"\nfi\n\n# Copy other config files\nfor file in .env.development .npmrc .yarnrc; do\n if [ -f \"$DMUX_ROOT/$file\" ]; then\n cp \"$DMUX_ROOT/$file\" \"$DMUX_WORKTREE_PATH/$file\"\n fi\ndone\n```\n\n### Pattern 3: Run Tests with Status Updates\n```bash\n#!/bin/bash\n# .dmux-hooks/run_test\n\nset -e\ncd \"$DMUX_WORKTREE_PATH\"\nAPI=\"http://localhost:$DMUX_SERVER_PORT/api/panes/$DMUX_PANE_ID/test\"\n\n# Update: starting\ncurl -s -X PUT \"$API\" -H \"Content-Type: application/json\" -d '{\"status\": \"running\"}' > /dev/null\n\n# Run tests and capture output\nOUTPUT_FILE=\"/tmp/dmux-test-$DMUX_PANE_ID.txt\"\nif pnpm test > \"$OUTPUT_FILE\" 2>&1; then\n STATUS=\"passed\"\nelse\n STATUS=\"failed\"\nfi\n\n# Get output (truncate if too long)\nOUTPUT=$(head -c 5000 \"$OUTPUT_FILE\")\n\n# Update: complete\ncurl -s -X PUT \"$API\" -H \"Content-Type: application/json\" -d \"$(jq -n --arg status \"$STATUS\" --arg output \"$OUTPUT\" '{status: $status, output: $output}')\" > /dev/null\n\nrm -f \"$OUTPUT_FILE\"\n```\n\n### Pattern 4: Dev Server with Tunnel\n```bash\n#!/bin/bash\n# .dmux-hooks/run_dev\n\nset -e\ncd \"$DMUX_WORKTREE_PATH\"\nAPI=\"http://localhost:$DMUX_SERVER_PORT/api/panes/$DMUX_PANE_ID/dev\"\n\n# Start dev server in background\nLOG_FILE=\"/tmp/dmux-dev-$DMUX_PANE_ID.log\"\npnpm dev > \"$LOG_FILE\" 2>&1 &\nDEV_PID=$!\n\n# Wait for server to start\nsleep 5\n\n# Detect port from logs\nPORT=$(grep -oP 'localhost:Kd+' \"$LOG_FILE\" | head -1)\n[ -z \"$PORT\" ] && PORT=3000\n\n# Optional: Create tunnel with cloudflared\nif command -v cloudflared &> /dev/null; then\n TUNNEL=$(cloudflared tunnel --url \"http://localhost:$PORT\" 2>&1 | grep -oP 'https://[a-z0-9-]+.trycloudflare.com' | head -1)\n URL=\"${TUNNEL:-http://localhost:$PORT}\"\nelse\n URL=\"http://localhost:$PORT\"\nfi\n\n# Report status\ncurl -s -X PUT \"$API\" -H \"Content-Type: application/json\" -d \"{\"status\": \"running\", \"url\": \"$URL\"}\" > /dev/null\n\necho \"[Hook] Dev server running at $URL (PID: $DEV_PID)\"\n```\n\n### Pattern 5: Post-Merge Deployment\n```bash\n#!/bin/bash\n# .dmux-hooks/post_merge\n\nset -e\ncd \"$DMUX_ROOT\"\n\n# Only deploy from main/master\nif [ \"$DMUX_TARGET_BRANCH\" != \"main\" ] && [ \"$DMUX_TARGET_BRANCH\" != \"master\" ]; then\n exit 0\nfi\n\n# Push to remote\ngit push origin \"$DMUX_TARGET_BRANCH\"\n\n# Trigger deployment (example: Vercel)\nif [ -n \"$VERCEL_TOKEN\" ]; then\n curl -s -X POST \"https://api.vercel.com/v1/deployments\" -H \"Authorization: Bearer $VERCEL_TOKEN\" -H \"Content-Type: application/json\" -d '{\"name\": \"my-project\"}' > /dev/null\nfi\n\n# Close GitHub issue if prompt contains #123\nISSUE=$(echo \"$DMUX_PROMPT\" | grep -oP '#Kd+' | head -1)\nif [ -n \"$ISSUE\" ] && command -v gh &> /dev/null; then\n gh issue close \"$ISSUE\" -c \"Resolved in $DMUX_SLUG, merged to $DMUX_TARGET_BRANCH\" 2>/dev/null || true\nfi\n```\n\n## Best Practices\n\n1. **Always start with shebang**: `#!/bin/bash`\n2. **Set error handling**: `set -e` (exit on error)\n3. **Make executable**: `chmod +x .dmux-hooks/hook_name`\n4. **Background long operations**: Append `&` to avoid blocking\n5. **Check for required tools**: `command -v tool &> /dev/null`\n6. **Log for debugging**: `echo \"[Hook] message\" >> \"$DMUX_ROOT/.dmux/hooks.log\"`\n7. **Handle missing vars gracefully**: `[ -z \"$VAR\" ] && exit 0`\n8. **Use silent curl**: `curl -s` to avoid noise in logs\n9. **Clean up temp files**: Remove files in `/tmp/`\n10. **Test before committing**: Run hooks manually with mock env vars\n\n## Testing Hooks\n\n### Manual Testing\n```bash\n# 1. Set environment variables\nexport DMUX_ROOT=\"$(pwd)\"\nexport DMUX_PANE_ID=\"test-pane\"\nexport DMUX_SLUG=\"test-branch\"\nexport DMUX_WORKTREE_PATH=\"$(pwd)\"\nexport DMUX_SERVER_PORT=\"3142\"\nexport DMUX_AGENT=\"claude\"\nexport DMUX_PROMPT=\"Test prompt\"\n\n# 2. Run hook directly\n./.dmux-hooks/worktree_created\n\n# 3. Check exit code\necho $? # Should be 0 for success\n```\n\n### Syntax Check\n```bash\n# Check for syntax errors without running\nbash -n ./.dmux-hooks/worktree_created\n```\n\n### Shellcheck (if available)\n```bash\nshellcheck ./.dmux-hooks/worktree_created\n```\n\n## Project Context Analysis\n\nBefore creating hooks, analyze these files in the project:\n\n### Package Manager Detection\n```bash\n# Check which package manager is used\nif [ -f \"pnpm-lock.yaml\" ]; then\n # Use: pnpm install, pnpm test, pnpm dev\nelif [ -f \"package-lock.json\" ]; then\n # Use: npm install, npm test, npm run dev\nelif [ -f \"yarn.lock\" ]; then\n # Use: yarn install, yarn test, yarn dev\nfi\n```\n\n### Test Command Discovery\n```bash\n# Read package.json to find test command\ncat package.json | grep '\"test\"'\n# Or with jq:\njq -r '.scripts.test' package.json\n```\n\n### Dev Command Discovery\n```bash\n# Read package.json to find dev command\ncat package.json | grep '\"dev\"'\n# Or with jq:\njq -r '.scripts.dev' package.json\n```\n\n### Environment Variables\n```bash\n# Check for .env files to copy\nls -la | grep '.env'\n```\n\n### Build System\n```bash\n# Detect build system\nif [ -f \"vite.config.ts\" ]; then\n # Vite project\nelif [ -f \"next.config.js\" ]; then\n # Next.js project\nelif [ -f \"nuxt.config.ts\" ]; then\n # Nuxt project\nfi\n```\n\n## Common Mistakes to Avoid\n\n\u274C **Blocking operations**: `sleep 60` (blocks dmux)\n\u2705 **Background long tasks**: `slow_operation &`\n\n\u274C **Hardcoded paths**: `/Users/me/project`\n\u2705 **Use variables**: `\"$DMUX_ROOT\"`\n\n\u274C **Assuming tools exist**: `pnpm install`\n\u2705 **Check first**: `command -v pnpm && pnpm install`\n\n\u274C **No error handling**: Script fails silently\n\u2705 **Set error mode**: `set -e` or check exit codes\n\n\u274C **Forgetting executable bit**: Hook won't run\n\u2705 **Make executable**: `chmod +x`\n\n\u274C **Noisy output**: Clutters dmux logs\n\u2705 **Silent operations**: `curl -s`, `> /dev/null 2>&1`\n\n\u274C **Not testing**: Deploy and hope\n\u2705 **Test manually**: Run with mock env vars first\n\n## Debugging\n\nIf a hook isn't working:\n\n1. **Check if file exists**: `ls -la .dmux-hooks/`\n2. **Check permissions**: Should show `x` in `rwxr-xr-x`\n3. **Check syntax**: `bash -n .dmux-hooks/hook_name`\n4. **Test manually**: Set env vars and run\n5. **Check logs**: dmux logs to stderr with `[Hooks]` prefix\n6. **Simplify**: Remove complex parts, test basic version\n7. **Check tool availability**: `command -v required_tool`\n\n### Debug Mode\n```bash\n#!/bin/bash\n# Add to top of hook for debugging\nset -x # Print each command before executing\nset -e # Exit on error\n\n# Your hook logic here\n```\n\n## Summary Checklist\n\nWhen creating a new hook:\n\n- [ ] Create file in `.dmux-hooks/`\n- [ ] Add shebang: `#!/bin/bash`\n- [ ] Make executable: `chmod +x`\n- [ ] Add `set -e` for error handling\n- [ ] Use environment variables (never hardcode paths)\n- [ ] Background long operations with `&`\n- [ ] Check for required tools before using\n- [ ] Test manually with mock env vars\n- [ ] Add comments explaining what it does\n- [ ] Commit to version control\n\n## Getting Help\n\n- **Full documentation**: See `HOOKS.md` in project root\n- **Claude-specific tips**: See `CLAUDE.md` in `.dmux-hooks/`\n- **Examples**: Check `.dmux-hooks/examples/` directory\n- **dmux API**: See `API.md` for REST endpoints\n\n---\n\n*This documentation was auto-generated from dmux source code.*\n*Version: 2025-12-12*\n";
|
|
13
13
|
/**
|
|
14
14
|
* README for the .dmux-hooks/ directory
|
|
15
15
|
*/
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Worktree Discovery Utilities
|
|
3
|
+
*
|
|
4
|
+
* Scans a directory for nested git worktrees (including the root).
|
|
5
|
+
* Used for multi-merge to find sub-worktrees created by hooks.
|
|
6
|
+
*/
|
|
7
|
+
import type { WorktreeInfo } from '../actions/merge/types.js';
|
|
8
|
+
/**
|
|
9
|
+
* Detect all git worktrees within a directory (recursively)
|
|
10
|
+
*
|
|
11
|
+
* @param rootWorktreePath - The root worktree path (dmux pane's worktree)
|
|
12
|
+
* @returns Array of WorktreeInfo objects, ordered by depth (deepest first, root last)
|
|
13
|
+
*/
|
|
14
|
+
export declare function detectAllWorktrees(rootWorktreePath: string): WorktreeInfo[];
|
|
15
|
+
/**
|
|
16
|
+
* Get the parent repository path for a worktree
|
|
17
|
+
*
|
|
18
|
+
* Uses: git rev-parse --path-format=absolute --git-common-dir
|
|
19
|
+
* Then removes ".git" suffix to get repo root
|
|
20
|
+
*/
|
|
21
|
+
export declare function getWorktreeParentPath(worktreePath: string): string | null;
|
|
22
|
+
/**
|
|
23
|
+
* Get repository name from path (directory name)
|
|
24
|
+
*/
|
|
25
|
+
export declare function getRepoName(repoPath: string): string;
|
|
26
|
+
/**
|
|
27
|
+
* Check if a directory is a git worktree (has .git file, not directory)
|
|
28
|
+
*/
|
|
29
|
+
export declare function isGitWorktree(dirPath: string): boolean;
|
|
30
|
+
/**
|
|
31
|
+
* Check if a directory is a git repository root (has .git directory)
|
|
32
|
+
*/
|
|
33
|
+
export declare function isGitRepository(dirPath: string): boolean;
|
|
34
|
+
/**
|
|
35
|
+
* Generate a display label for a worktree
|
|
36
|
+
* Format: "repo-name (branch)" or "repo-name (branch) - relative/path"
|
|
37
|
+
*/
|
|
38
|
+
export declare function getWorktreeDisplayLabel(worktree: WorktreeInfo): string;
|
|
39
|
+
//# sourceMappingURL=worktreeDiscovery.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"worktreeDiscovery.d.ts","sourceRoot":"","sources":["../../src/utils/worktreeDiscovery.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAKH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AAG9D;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,gBAAgB,EAAE,MAAM,GAAG,YAAY,EAAE,CAiB3E;AAwGD;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAqCzE;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAEpD;AAiDD;;GAEG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAOtD;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAOxD;AAED;;;GAGG;AACH,wBAAgB,uBAAuB,CAAC,QAAQ,EAAE,YAAY,GAAG,MAAM,CAMtE"}
|
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Worktree Discovery Utilities
|
|
3
|
+
*
|
|
4
|
+
* Scans a directory for nested git worktrees (including the root).
|
|
5
|
+
* Used for multi-merge to find sub-worktrees created by hooks.
|
|
6
|
+
*/
|
|
7
|
+
import { execSync } from 'child_process';
|
|
8
|
+
import { readdirSync, statSync, existsSync } from 'fs';
|
|
9
|
+
import { join, basename, relative } from 'path';
|
|
10
|
+
import { getCurrentBranch } from './git.js';
|
|
11
|
+
/**
|
|
12
|
+
* Detect all git worktrees within a directory (recursively)
|
|
13
|
+
*
|
|
14
|
+
* @param rootWorktreePath - The root worktree path (dmux pane's worktree)
|
|
15
|
+
* @returns Array of WorktreeInfo objects, ordered by depth (deepest first, root last)
|
|
16
|
+
*/
|
|
17
|
+
export function detectAllWorktrees(rootWorktreePath) {
|
|
18
|
+
const worktrees = [];
|
|
19
|
+
// Add the root worktree first
|
|
20
|
+
const rootInfo = getWorktreeInfo(rootWorktreePath, rootWorktreePath, true);
|
|
21
|
+
if (rootInfo) {
|
|
22
|
+
worktrees.push(rootInfo);
|
|
23
|
+
}
|
|
24
|
+
// Recursively scan for sub-worktrees
|
|
25
|
+
scanForWorktrees(rootWorktreePath, rootWorktreePath, worktrees, 1);
|
|
26
|
+
// Sort by depth descending (deepest first, root last)
|
|
27
|
+
// This ensures sub-worktrees are merged before their parents
|
|
28
|
+
worktrees.sort((a, b) => b.depth - a.depth);
|
|
29
|
+
return worktrees;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Recursively scan a directory for git worktrees
|
|
33
|
+
*/
|
|
34
|
+
function scanForWorktrees(dirPath, rootWorktreePath, worktrees, depth) {
|
|
35
|
+
try {
|
|
36
|
+
const entries = readdirSync(dirPath, { withFileTypes: true });
|
|
37
|
+
for (const entry of entries) {
|
|
38
|
+
// Skip hidden directories (except we need to check for .git)
|
|
39
|
+
if (entry.name.startsWith('.') && entry.name !== '.git') {
|
|
40
|
+
continue;
|
|
41
|
+
}
|
|
42
|
+
// Skip node_modules and other common large directories
|
|
43
|
+
if (entry.name === 'node_modules' || entry.name === 'vendor' || entry.name === '.pnpm') {
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
const fullPath = join(dirPath, entry.name);
|
|
47
|
+
if (entry.isDirectory()) {
|
|
48
|
+
// Check if this directory is a worktree (has .git file, not directory)
|
|
49
|
+
const gitPath = join(fullPath, '.git');
|
|
50
|
+
if (existsSync(gitPath)) {
|
|
51
|
+
const gitStat = statSync(gitPath);
|
|
52
|
+
if (gitStat.isFile()) {
|
|
53
|
+
// This is a worktree (has .git file pointing to parent)
|
|
54
|
+
const worktreeInfo = getWorktreeInfo(fullPath, rootWorktreePath, false, depth);
|
|
55
|
+
if (worktreeInfo) {
|
|
56
|
+
worktrees.push(worktreeInfo);
|
|
57
|
+
}
|
|
58
|
+
// Continue scanning inside this worktree for nested worktrees
|
|
59
|
+
scanForWorktrees(fullPath, rootWorktreePath, worktrees, depth + 1);
|
|
60
|
+
}
|
|
61
|
+
else if (gitStat.isDirectory()) {
|
|
62
|
+
// This is a full git repository, not a worktree
|
|
63
|
+
// It could still contain worktrees inside it, but we skip the repo itself
|
|
64
|
+
// (it's not a worktree of another repo)
|
|
65
|
+
scanForWorktrees(fullPath, rootWorktreePath, worktrees, depth + 1);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
// Regular directory, continue scanning
|
|
70
|
+
scanForWorktrees(fullPath, rootWorktreePath, worktrees, depth);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
catch (error) {
|
|
76
|
+
// Permission denied or other errors - skip this directory
|
|
77
|
+
console.error(`[worktreeDiscovery] Error scanning ${dirPath}: ${error}`);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Get detailed information about a worktree
|
|
82
|
+
*/
|
|
83
|
+
function getWorktreeInfo(worktreePath, rootWorktreePath, isRoot, depth = 0) {
|
|
84
|
+
try {
|
|
85
|
+
// Get the parent repo path using git rev-parse
|
|
86
|
+
const parentRepoPath = getWorktreeParentPath(worktreePath);
|
|
87
|
+
if (!parentRepoPath) {
|
|
88
|
+
console.error(`[worktreeDiscovery] Could not determine parent for ${worktreePath}`);
|
|
89
|
+
return null;
|
|
90
|
+
}
|
|
91
|
+
// Get repo name from parent path
|
|
92
|
+
const repoName = getRepoName(parentRepoPath);
|
|
93
|
+
// Get current branch in worktree
|
|
94
|
+
const branch = getCurrentBranch(worktreePath);
|
|
95
|
+
// Get main branch in parent repo
|
|
96
|
+
const mainBranch = getMainBranchForRepo(parentRepoPath);
|
|
97
|
+
// Calculate relative path from root
|
|
98
|
+
const relativePath = isRoot ? '.' : relative(rootWorktreePath, worktreePath);
|
|
99
|
+
return {
|
|
100
|
+
worktreePath,
|
|
101
|
+
parentRepoPath,
|
|
102
|
+
repoName,
|
|
103
|
+
branch,
|
|
104
|
+
mainBranch,
|
|
105
|
+
isRoot,
|
|
106
|
+
relativePath,
|
|
107
|
+
depth,
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
catch (error) {
|
|
111
|
+
console.error(`[worktreeDiscovery] Error getting info for ${worktreePath}: ${error}`);
|
|
112
|
+
return null;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Get the parent repository path for a worktree
|
|
117
|
+
*
|
|
118
|
+
* Uses: git rev-parse --path-format=absolute --git-common-dir
|
|
119
|
+
* Then removes ".git" suffix to get repo root
|
|
120
|
+
*/
|
|
121
|
+
export function getWorktreeParentPath(worktreePath) {
|
|
122
|
+
try {
|
|
123
|
+
// Get the common git directory (the parent repo's .git or .git/worktrees/...)
|
|
124
|
+
const gitCommonDir = execSync('git rev-parse --path-format=absolute --git-common-dir', {
|
|
125
|
+
cwd: worktreePath,
|
|
126
|
+
encoding: 'utf-8',
|
|
127
|
+
stdio: 'pipe',
|
|
128
|
+
}).trim();
|
|
129
|
+
// The git-common-dir returns the .git directory path
|
|
130
|
+
// Remove the ".git" suffix to get the repo root
|
|
131
|
+
// Handle both "/path/to/repo/.git" and "/path/to/repo/.git/worktrees/name"
|
|
132
|
+
let parentPath = gitCommonDir;
|
|
133
|
+
// If it ends with .git, remove it
|
|
134
|
+
if (parentPath.endsWith('.git')) {
|
|
135
|
+
parentPath = parentPath.slice(0, -4); // Remove ".git"
|
|
136
|
+
if (parentPath.endsWith('/')) {
|
|
137
|
+
parentPath = parentPath.slice(0, -1); // Remove trailing slash
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
else if (parentPath.includes('/.git/')) {
|
|
141
|
+
// It's a path like /repo/.git/worktrees/name, extract the repo path
|
|
142
|
+
parentPath = parentPath.split('/.git/')[0];
|
|
143
|
+
}
|
|
144
|
+
// Verify by getting the toplevel
|
|
145
|
+
const topLevel = execSync('git rev-parse --path-format=absolute --show-toplevel', {
|
|
146
|
+
cwd: parentPath,
|
|
147
|
+
encoding: 'utf-8',
|
|
148
|
+
stdio: 'pipe',
|
|
149
|
+
}).trim();
|
|
150
|
+
return topLevel;
|
|
151
|
+
}
|
|
152
|
+
catch (error) {
|
|
153
|
+
console.error(`[worktreeDiscovery] Error getting parent path for ${worktreePath}: ${error}`);
|
|
154
|
+
return null;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Get repository name from path (directory name)
|
|
159
|
+
*/
|
|
160
|
+
export function getRepoName(repoPath) {
|
|
161
|
+
return basename(repoPath);
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Get main branch for a specific repo (running git commands in that repo's context)
|
|
165
|
+
*/
|
|
166
|
+
function getMainBranchForRepo(repoPath) {
|
|
167
|
+
try {
|
|
168
|
+
// First try to get the default branch from origin
|
|
169
|
+
const originHead = execSync('git symbolic-ref refs/remotes/origin/HEAD 2>/dev/null', {
|
|
170
|
+
cwd: repoPath,
|
|
171
|
+
encoding: 'utf8',
|
|
172
|
+
stdio: 'pipe',
|
|
173
|
+
}).trim();
|
|
174
|
+
if (originHead) {
|
|
175
|
+
const match = originHead.match(/refs\/remotes\/origin\/(.+)/);
|
|
176
|
+
if (match) {
|
|
177
|
+
return match[1];
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
catch {
|
|
182
|
+
// Fallback if origin/HEAD is not set
|
|
183
|
+
}
|
|
184
|
+
try {
|
|
185
|
+
// Check if 'main' branch exists
|
|
186
|
+
execSync('git show-ref --verify --quiet refs/heads/main', {
|
|
187
|
+
cwd: repoPath,
|
|
188
|
+
stdio: 'pipe',
|
|
189
|
+
});
|
|
190
|
+
return 'main';
|
|
191
|
+
}
|
|
192
|
+
catch {
|
|
193
|
+
// main doesn't exist
|
|
194
|
+
}
|
|
195
|
+
try {
|
|
196
|
+
// Check if 'master' branch exists
|
|
197
|
+
execSync('git show-ref --verify --quiet refs/heads/master', {
|
|
198
|
+
cwd: repoPath,
|
|
199
|
+
stdio: 'pipe',
|
|
200
|
+
});
|
|
201
|
+
return 'master';
|
|
202
|
+
}
|
|
203
|
+
catch {
|
|
204
|
+
// master doesn't exist
|
|
205
|
+
}
|
|
206
|
+
return 'main'; // Default fallback
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Check if a directory is a git worktree (has .git file, not directory)
|
|
210
|
+
*/
|
|
211
|
+
export function isGitWorktree(dirPath) {
|
|
212
|
+
const gitPath = join(dirPath, '.git');
|
|
213
|
+
if (!existsSync(gitPath)) {
|
|
214
|
+
return false;
|
|
215
|
+
}
|
|
216
|
+
const stat = statSync(gitPath);
|
|
217
|
+
return stat.isFile();
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* Check if a directory is a git repository root (has .git directory)
|
|
221
|
+
*/
|
|
222
|
+
export function isGitRepository(dirPath) {
|
|
223
|
+
const gitPath = join(dirPath, '.git');
|
|
224
|
+
if (!existsSync(gitPath)) {
|
|
225
|
+
return false;
|
|
226
|
+
}
|
|
227
|
+
const stat = statSync(gitPath);
|
|
228
|
+
return stat.isDirectory();
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* Generate a display label for a worktree
|
|
232
|
+
* Format: "repo-name (branch)" or "repo-name (branch) - relative/path"
|
|
233
|
+
*/
|
|
234
|
+
export function getWorktreeDisplayLabel(worktree) {
|
|
235
|
+
const baseLabel = `${worktree.repoName} (${worktree.branch})`;
|
|
236
|
+
if (worktree.isRoot || worktree.relativePath === '.') {
|
|
237
|
+
return baseLabel;
|
|
238
|
+
}
|
|
239
|
+
return `${baseLabel} - ${worktree.relativePath}`;
|
|
240
|
+
}
|
|
241
|
+
//# sourceMappingURL=worktreeDiscovery.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"worktreeDiscovery.js","sourceRoot":"","sources":["../../src/utils/worktreeDiscovery.ts"],"names":[],"mappings":"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"}
|