@triflux/remote 10.0.0-alpha.2 → 10.0.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.
@@ -1,172 +0,0 @@
1
- // hub/team/worktree-lifecycle.mjs — Git worktree lifecycle management
2
- // Replaces shell commands in tfx-codex-swarm Step 5 + merge-worktree Phase 6.
3
- // Convention: .codex-swarm/wt-{slug} paths, swarm/{runId}/{slug} branches.
4
-
5
- import { execFile } from 'node:child_process';
6
- import { resolve, normalize } from 'node:path';
7
- import { mkdir, rm, access } from 'node:fs/promises';
8
-
9
- const SWARM_ROOT = '.codex-swarm';
10
- const SLEEP_MS = 2000; // WT race-guard (MEMORY.md: wt-attach-spacing)
11
-
12
- function git(args, cwd) {
13
- return new Promise((res, rej) => {
14
- execFile('git', args, { cwd, windowsHide: true, timeout: 30_000 }, (err, stdout, stderr) => {
15
- if (err) {
16
- const msg = `git ${args[0]} failed: ${stderr?.trim() || err.message}`;
17
- rej(new Error(msg));
18
- } else {
19
- res(stdout.trim());
20
- }
21
- });
22
- });
23
- }
24
-
25
- function sleep(ms) {
26
- return new Promise((r) => setTimeout(r, ms));
27
- }
28
-
29
- /** Normalize path for Windows compatibility. */
30
- function normPath(p) {
31
- return normalize(p).replace(/\\/g, '/');
32
- }
33
-
34
- /**
35
- * Create a worktree for a shard.
36
- *
37
- * @param {object} opts
38
- * @param {string} opts.slug — shard identifier (e.g. "issue-42" or "auth-refactor")
39
- * @param {string} opts.runId — swarm run ID
40
- * @param {string} [opts.rootDir=process.cwd()] — repo root
41
- * @param {string} [opts.baseBranch='main'] — base branch to branch from
42
- * @returns {Promise<{ worktreePath: string, branchName: string }>}
43
- */
44
- export async function ensureWorktree({ slug, runId, rootDir = process.cwd(), baseBranch = 'main' }) {
45
- const wtDir = resolve(rootDir, SWARM_ROOT, `wt-${slug}`);
46
- const branchName = `swarm/${runId}/${slug}`;
47
-
48
- await mkdir(resolve(rootDir, SWARM_ROOT), { recursive: true });
49
-
50
- // Check if worktree already exists
51
- try {
52
- await access(wtDir);
53
- // Already exists — verify it's a valid worktree
54
- await git(['rev-parse', '--is-inside-work-tree'], wtDir);
55
- return { worktreePath: normPath(wtDir), branchName };
56
- } catch {
57
- // Doesn't exist or invalid — create fresh
58
- }
59
-
60
- // Clean up stale references
61
- try {
62
- await git(['worktree', 'prune'], rootDir);
63
- } catch { /* best-effort */ }
64
-
65
- await sleep(SLEEP_MS);
66
-
67
- // Try create with new branch, fallback to existing branch
68
- try {
69
- await git(['worktree', 'add', wtDir, '-b', branchName, baseBranch], rootDir);
70
- } catch {
71
- await git(['worktree', 'add', wtDir, branchName], rootDir);
72
- }
73
-
74
- return { worktreePath: normPath(wtDir), branchName };
75
- }
76
-
77
- /**
78
- * Create a temporary integration branch for merge operations.
79
- *
80
- * @param {object} opts
81
- * @param {string} opts.runId
82
- * @param {string} opts.baseBranch
83
- * @param {string} [opts.rootDir=process.cwd()]
84
- * @returns {Promise<{ integrationBranch: string, baseCommit: string }>}
85
- */
86
- export async function prepareIntegrationBranch({ runId, baseBranch, rootDir = process.cwd() }) {
87
- const integrationBranch = `swarm/${runId}/merge`;
88
-
89
- // Record base commit for rollback
90
- const baseCommit = await git(['rev-parse', baseBranch], rootDir);
91
-
92
- // Create integration branch from base
93
- try {
94
- await git(['branch', integrationBranch, baseBranch], rootDir);
95
- } catch {
96
- // Branch may already exist — reset to base
97
- await git(['branch', '-f', integrationBranch, baseBranch], rootDir);
98
- }
99
-
100
- return { integrationBranch, baseCommit };
101
- }
102
-
103
- /**
104
- * Rebase a shard branch onto the integration branch.
105
- * Uses rebase + fast-forward only (no merge commits).
106
- *
107
- * @param {object} opts
108
- * @param {string} opts.shardBranch — the shard's branch name
109
- * @param {string} opts.integrationBranch — target integration branch
110
- * @param {string} [opts.rootDir=process.cwd()]
111
- * @returns {Promise<{ ok: boolean, headCommit?: string, error?: string }>}
112
- */
113
- export async function rebaseShardOntoIntegration({ shardBranch, integrationBranch, rootDir = process.cwd() }) {
114
- // Backup integration HEAD for rollback
115
- const backupCommit = await git(['rev-parse', integrationBranch], rootDir);
116
-
117
- try {
118
- // Rebase shard onto integration
119
- await git(['rebase', integrationBranch, shardBranch], rootDir);
120
-
121
- // Fast-forward integration to include shard changes
122
- await git(['checkout', integrationBranch], rootDir);
123
- await git(['merge', '--ff-only', shardBranch], rootDir);
124
-
125
- const headCommit = await git(['rev-parse', 'HEAD'], rootDir);
126
- return { ok: true, headCommit };
127
- } catch (err) {
128
- // Abort rebase and restore integration branch
129
- try { await git(['rebase', '--abort'], rootDir); } catch { /* already clean */ }
130
- try { await git(['checkout', integrationBranch], rootDir); } catch { /* best-effort */ }
131
- try { await git(['reset', '--hard', backupCommit], rootDir); } catch { /* best-effort */ }
132
-
133
- return { ok: false, error: err.message };
134
- }
135
- }
136
-
137
- /**
138
- * Remove a worktree and its branch.
139
- * Follows WT race-guard: sleep between operations.
140
- *
141
- * @param {object} opts
142
- * @param {string} opts.worktreePath
143
- * @param {string} [opts.branchName] — optional branch to delete
144
- * @param {string} [opts.rootDir=process.cwd()]
145
- * @param {boolean} [opts.force=false]
146
- */
147
- export async function pruneWorktree({ worktreePath, branchName, rootDir = process.cwd(), force = false }) {
148
- const forceFlag = force ? '--force' : '';
149
-
150
- // Remove worktree (with retry for Windows file handle issues — E5)
151
- for (let attempt = 0; attempt < 3; attempt++) {
152
- try {
153
- await git(['worktree', 'remove', worktreePath, ...(forceFlag ? [forceFlag] : [])], rootDir);
154
- break;
155
- } catch (err) {
156
- if (attempt === 2) {
157
- // Last resort: rm the directory and prune
158
- try { await rm(worktreePath, { recursive: true, force: true }); } catch { /* ignore */ }
159
- }
160
- await sleep(SLEEP_MS);
161
- }
162
- }
163
-
164
- // Prune stale worktree references
165
- await sleep(SLEEP_MS);
166
- try { await git(['worktree', 'prune'], rootDir); } catch { /* best-effort */ }
167
-
168
- // Delete branch if specified
169
- if (branchName) {
170
- try { await git(['branch', '-D', branchName], rootDir); } catch { /* may not exist */ }
171
- }
172
- }