@iloom/cli 0.1.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +33 -0
- package/README.md +711 -0
- package/dist/ClaudeContextManager-XOSXQ67R.js +13 -0
- package/dist/ClaudeContextManager-XOSXQ67R.js.map +1 -0
- package/dist/ClaudeService-YSZ6EXWP.js +12 -0
- package/dist/ClaudeService-YSZ6EXWP.js.map +1 -0
- package/dist/GitHubService-F7Z3XJOS.js +11 -0
- package/dist/GitHubService-F7Z3XJOS.js.map +1 -0
- package/dist/LoomLauncher-MODG2SEM.js +263 -0
- package/dist/LoomLauncher-MODG2SEM.js.map +1 -0
- package/dist/NeonProvider-PAGPUH7F.js +12 -0
- package/dist/NeonProvider-PAGPUH7F.js.map +1 -0
- package/dist/PromptTemplateManager-7FINLRDE.js +9 -0
- package/dist/PromptTemplateManager-7FINLRDE.js.map +1 -0
- package/dist/SettingsManager-VAZF26S2.js +19 -0
- package/dist/SettingsManager-VAZF26S2.js.map +1 -0
- package/dist/SettingsMigrationManager-MTQIMI54.js +146 -0
- package/dist/SettingsMigrationManager-MTQIMI54.js.map +1 -0
- package/dist/add-issue-22JBNOML.js +54 -0
- package/dist/add-issue-22JBNOML.js.map +1 -0
- package/dist/agents/iloom-issue-analyze-and-plan.md +580 -0
- package/dist/agents/iloom-issue-analyzer.md +290 -0
- package/dist/agents/iloom-issue-complexity-evaluator.md +224 -0
- package/dist/agents/iloom-issue-enhancer.md +266 -0
- package/dist/agents/iloom-issue-implementer.md +262 -0
- package/dist/agents/iloom-issue-planner.md +358 -0
- package/dist/agents/iloom-issue-reviewer.md +63 -0
- package/dist/chunk-2ZPFJQ3B.js +63 -0
- package/dist/chunk-2ZPFJQ3B.js.map +1 -0
- package/dist/chunk-37DYYFVK.js +29 -0
- package/dist/chunk-37DYYFVK.js.map +1 -0
- package/dist/chunk-BLCTGFZN.js +121 -0
- package/dist/chunk-BLCTGFZN.js.map +1 -0
- package/dist/chunk-CP2NU2JC.js +545 -0
- package/dist/chunk-CP2NU2JC.js.map +1 -0
- package/dist/chunk-CWR2SANQ.js +39 -0
- package/dist/chunk-CWR2SANQ.js.map +1 -0
- package/dist/chunk-F3XBU2R7.js +110 -0
- package/dist/chunk-F3XBU2R7.js.map +1 -0
- package/dist/chunk-GEHQXLEI.js +130 -0
- package/dist/chunk-GEHQXLEI.js.map +1 -0
- package/dist/chunk-GYCR2LOU.js +143 -0
- package/dist/chunk-GYCR2LOU.js.map +1 -0
- package/dist/chunk-GZP4UGGM.js +48 -0
- package/dist/chunk-GZP4UGGM.js.map +1 -0
- package/dist/chunk-H4E4THUZ.js +55 -0
- package/dist/chunk-H4E4THUZ.js.map +1 -0
- package/dist/chunk-HPJJSYNS.js +644 -0
- package/dist/chunk-HPJJSYNS.js.map +1 -0
- package/dist/chunk-JBH2ZYYZ.js +220 -0
- package/dist/chunk-JBH2ZYYZ.js.map +1 -0
- package/dist/chunk-JNKJ7NJV.js +78 -0
- package/dist/chunk-JNKJ7NJV.js.map +1 -0
- package/dist/chunk-JQ7VOSTC.js +437 -0
- package/dist/chunk-JQ7VOSTC.js.map +1 -0
- package/dist/chunk-KQDEK2ZW.js +199 -0
- package/dist/chunk-KQDEK2ZW.js.map +1 -0
- package/dist/chunk-O2QWO64Z.js +179 -0
- package/dist/chunk-O2QWO64Z.js.map +1 -0
- package/dist/chunk-OC4H6HJD.js +248 -0
- package/dist/chunk-OC4H6HJD.js.map +1 -0
- package/dist/chunk-PR7FKQBG.js +120 -0
- package/dist/chunk-PR7FKQBG.js.map +1 -0
- package/dist/chunk-PXZBAC2M.js +250 -0
- package/dist/chunk-PXZBAC2M.js.map +1 -0
- package/dist/chunk-QEPVTTHD.js +383 -0
- package/dist/chunk-QEPVTTHD.js.map +1 -0
- package/dist/chunk-RSRO7564.js +203 -0
- package/dist/chunk-RSRO7564.js.map +1 -0
- package/dist/chunk-SJUQ2NDR.js +146 -0
- package/dist/chunk-SJUQ2NDR.js.map +1 -0
- package/dist/chunk-SPYPLHMK.js +177 -0
- package/dist/chunk-SPYPLHMK.js.map +1 -0
- package/dist/chunk-SSCQCCJ7.js +75 -0
- package/dist/chunk-SSCQCCJ7.js.map +1 -0
- package/dist/chunk-SSR5AVRJ.js +41 -0
- package/dist/chunk-SSR5AVRJ.js.map +1 -0
- package/dist/chunk-T7QPXANZ.js +315 -0
- package/dist/chunk-T7QPXANZ.js.map +1 -0
- package/dist/chunk-U3WU5OWO.js +203 -0
- package/dist/chunk-U3WU5OWO.js.map +1 -0
- package/dist/chunk-W3DQTW63.js +124 -0
- package/dist/chunk-W3DQTW63.js.map +1 -0
- package/dist/chunk-WKEWRSDB.js +151 -0
- package/dist/chunk-WKEWRSDB.js.map +1 -0
- package/dist/chunk-Y7SAGNUT.js +66 -0
- package/dist/chunk-Y7SAGNUT.js.map +1 -0
- package/dist/chunk-YETJNRQM.js +39 -0
- package/dist/chunk-YETJNRQM.js.map +1 -0
- package/dist/chunk-YYSKGAZT.js +384 -0
- package/dist/chunk-YYSKGAZT.js.map +1 -0
- package/dist/chunk-ZZZWQGTS.js +169 -0
- package/dist/chunk-ZZZWQGTS.js.map +1 -0
- package/dist/claude-7LUVDZZ4.js +17 -0
- package/dist/claude-7LUVDZZ4.js.map +1 -0
- package/dist/cleanup-3LUWPSM7.js +412 -0
- package/dist/cleanup-3LUWPSM7.js.map +1 -0
- package/dist/cli-overrides-XFZWY7CM.js +16 -0
- package/dist/cli-overrides-XFZWY7CM.js.map +1 -0
- package/dist/cli.js +603 -0
- package/dist/cli.js.map +1 -0
- package/dist/color-ZVALX37U.js +21 -0
- package/dist/color-ZVALX37U.js.map +1 -0
- package/dist/enhance-XJIQHVPD.js +166 -0
- package/dist/enhance-XJIQHVPD.js.map +1 -0
- package/dist/env-MDFL4ZXL.js +23 -0
- package/dist/env-MDFL4ZXL.js.map +1 -0
- package/dist/feedback-23CLXKFT.js +158 -0
- package/dist/feedback-23CLXKFT.js.map +1 -0
- package/dist/finish-CY4CIH6O.js +1608 -0
- package/dist/finish-CY4CIH6O.js.map +1 -0
- package/dist/git-LVRZ57GJ.js +43 -0
- package/dist/git-LVRZ57GJ.js.map +1 -0
- package/dist/ignite-WXEF2ID5.js +359 -0
- package/dist/ignite-WXEF2ID5.js.map +1 -0
- package/dist/index.d.ts +1341 -0
- package/dist/index.js +3058 -0
- package/dist/index.js.map +1 -0
- package/dist/init-RHACUR4E.js +123 -0
- package/dist/init-RHACUR4E.js.map +1 -0
- package/dist/installation-detector-VARGFFRZ.js +11 -0
- package/dist/installation-detector-VARGFFRZ.js.map +1 -0
- package/dist/logger-MKYH4UDV.js +12 -0
- package/dist/logger-MKYH4UDV.js.map +1 -0
- package/dist/mcp/chunk-6SDFJ42P.js +62 -0
- package/dist/mcp/chunk-6SDFJ42P.js.map +1 -0
- package/dist/mcp/claude-YHHHLSXH.js +249 -0
- package/dist/mcp/claude-YHHHLSXH.js.map +1 -0
- package/dist/mcp/color-QS5BFCNN.js +168 -0
- package/dist/mcp/color-QS5BFCNN.js.map +1 -0
- package/dist/mcp/github-comment-server.js +165 -0
- package/dist/mcp/github-comment-server.js.map +1 -0
- package/dist/mcp/terminal-SDCMDVD7.js +202 -0
- package/dist/mcp/terminal-SDCMDVD7.js.map +1 -0
- package/dist/open-X6BTENPV.js +278 -0
- package/dist/open-X6BTENPV.js.map +1 -0
- package/dist/prompt-ANTQWHUF.js +13 -0
- package/dist/prompt-ANTQWHUF.js.map +1 -0
- package/dist/prompts/issue-prompt.txt +230 -0
- package/dist/prompts/pr-prompt.txt +35 -0
- package/dist/prompts/regular-prompt.txt +14 -0
- package/dist/run-2JCPQAX3.js +278 -0
- package/dist/run-2JCPQAX3.js.map +1 -0
- package/dist/schema/settings.schema.json +221 -0
- package/dist/start-LWVRBJ6S.js +982 -0
- package/dist/start-LWVRBJ6S.js.map +1 -0
- package/dist/terminal-3D6TUAKJ.js +16 -0
- package/dist/terminal-3D6TUAKJ.js.map +1 -0
- package/dist/test-git-XPF4SZXJ.js +52 -0
- package/dist/test-git-XPF4SZXJ.js.map +1 -0
- package/dist/test-prefix-XGFXFAYN.js +68 -0
- package/dist/test-prefix-XGFXFAYN.js.map +1 -0
- package/dist/test-tabs-JRKY3QMM.js +69 -0
- package/dist/test-tabs-JRKY3QMM.js.map +1 -0
- package/dist/test-webserver-M2I3EV4J.js +62 -0
- package/dist/test-webserver-M2I3EV4J.js.map +1 -0
- package/dist/update-3ZT2XX2G.js +79 -0
- package/dist/update-3ZT2XX2G.js.map +1 -0
- package/dist/update-notifier-QSSEB5KC.js +11 -0
- package/dist/update-notifier-QSSEB5KC.js.map +1 -0
- package/package.json +113 -0
|
@@ -0,0 +1,437 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
logger
|
|
4
|
+
} from "./chunk-GEHQXLEI.js";
|
|
5
|
+
|
|
6
|
+
// src/utils/git.ts
|
|
7
|
+
import path from "path";
|
|
8
|
+
import { execa } from "execa";
|
|
9
|
+
async function executeGitCommand(args, options) {
|
|
10
|
+
try {
|
|
11
|
+
const result = await execa("git", args, {
|
|
12
|
+
cwd: (options == null ? void 0 : options.cwd) ?? process.cwd(),
|
|
13
|
+
timeout: (options == null ? void 0 : options.timeout) ?? 3e4,
|
|
14
|
+
encoding: "utf8",
|
|
15
|
+
stdio: (options == null ? void 0 : options.stdio) ?? "pipe",
|
|
16
|
+
verbose: logger.isDebugEnabled()
|
|
17
|
+
});
|
|
18
|
+
return result.stdout;
|
|
19
|
+
} catch (error) {
|
|
20
|
+
const execaError = error;
|
|
21
|
+
const stderr = execaError.stderr ?? execaError.message ?? "Unknown Git error";
|
|
22
|
+
throw new Error(`Git command failed: ${stderr}`);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
function parseWorktreeList(output, defaultBranch) {
|
|
26
|
+
var _a, _b;
|
|
27
|
+
const worktrees = [];
|
|
28
|
+
const lines = output.trim().split("\n");
|
|
29
|
+
let i = 0;
|
|
30
|
+
while (i < lines.length) {
|
|
31
|
+
const pathLine = lines[i];
|
|
32
|
+
if (!(pathLine == null ? void 0 : pathLine.startsWith("worktree "))) {
|
|
33
|
+
i++;
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
const pathMatch = pathLine.match(/^worktree (.+)$/);
|
|
37
|
+
if (!pathMatch) {
|
|
38
|
+
i++;
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
let branch = "";
|
|
42
|
+
let commit = "";
|
|
43
|
+
let detached = false;
|
|
44
|
+
let bare = false;
|
|
45
|
+
let locked = false;
|
|
46
|
+
let lockReason;
|
|
47
|
+
i++;
|
|
48
|
+
while (i < lines.length && !((_a = lines[i]) == null ? void 0 : _a.startsWith("worktree "))) {
|
|
49
|
+
const line = (_b = lines[i]) == null ? void 0 : _b.trim();
|
|
50
|
+
if (!line) {
|
|
51
|
+
i++;
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
if (line === "bare") {
|
|
55
|
+
bare = true;
|
|
56
|
+
branch = defaultBranch ?? "main";
|
|
57
|
+
} else if (line === "detached") {
|
|
58
|
+
detached = true;
|
|
59
|
+
branch = "HEAD";
|
|
60
|
+
} else if (line.startsWith("locked")) {
|
|
61
|
+
locked = true;
|
|
62
|
+
const lockMatch = line.match(/^locked (.+)$/);
|
|
63
|
+
lockReason = lockMatch == null ? void 0 : lockMatch[1];
|
|
64
|
+
branch = branch || "unknown";
|
|
65
|
+
} else if (line.startsWith("HEAD ")) {
|
|
66
|
+
const commitMatch = line.match(/^HEAD ([a-f0-9]+)/);
|
|
67
|
+
if (commitMatch) {
|
|
68
|
+
commit = commitMatch[1] ?? "";
|
|
69
|
+
}
|
|
70
|
+
} else if (line.startsWith("branch ")) {
|
|
71
|
+
const branchMatch = line.match(/^branch refs\/heads\/(.+)$/);
|
|
72
|
+
branch = (branchMatch == null ? void 0 : branchMatch[1]) ?? line.replace("branch ", "");
|
|
73
|
+
}
|
|
74
|
+
i++;
|
|
75
|
+
}
|
|
76
|
+
const worktree = {
|
|
77
|
+
path: pathMatch[1] ?? "",
|
|
78
|
+
branch,
|
|
79
|
+
commit,
|
|
80
|
+
bare,
|
|
81
|
+
detached,
|
|
82
|
+
locked
|
|
83
|
+
};
|
|
84
|
+
if (lockReason !== void 0) {
|
|
85
|
+
worktree.lockReason = lockReason;
|
|
86
|
+
}
|
|
87
|
+
worktrees.push(worktree);
|
|
88
|
+
}
|
|
89
|
+
return worktrees;
|
|
90
|
+
}
|
|
91
|
+
function isPRBranch(branchName) {
|
|
92
|
+
const prPatterns = [
|
|
93
|
+
/^pr\/\d+/i,
|
|
94
|
+
// pr/123, pr/123-feature-name
|
|
95
|
+
/^pull\/\d+/i,
|
|
96
|
+
// pull/123
|
|
97
|
+
/^\d+[-_]/,
|
|
98
|
+
// 123-feature-name, 123_feature_name
|
|
99
|
+
/^feature\/pr[-_]?\d+/i,
|
|
100
|
+
// feature/pr123, feature/pr-123
|
|
101
|
+
/^hotfix\/pr[-_]?\d+/i
|
|
102
|
+
// hotfix/pr123
|
|
103
|
+
];
|
|
104
|
+
return prPatterns.some((pattern) => pattern.test(branchName));
|
|
105
|
+
}
|
|
106
|
+
function extractPRNumber(branchName) {
|
|
107
|
+
const patterns = [
|
|
108
|
+
/^pr\/(\d+)/i,
|
|
109
|
+
// pr/123
|
|
110
|
+
/^pull\/(\d+)/i,
|
|
111
|
+
// pull/123
|
|
112
|
+
/^(\d+)[-_]/,
|
|
113
|
+
// 123-feature-name
|
|
114
|
+
/^feature\/pr[-_]?(\d+)/i,
|
|
115
|
+
// feature/pr123
|
|
116
|
+
/^hotfix\/pr[-_]?(\d+)/i,
|
|
117
|
+
// hotfix/pr123
|
|
118
|
+
/pr[-_]?(\d+)/i
|
|
119
|
+
// anywhere with pr123 or pr-123
|
|
120
|
+
];
|
|
121
|
+
for (const pattern of patterns) {
|
|
122
|
+
const match = branchName.match(pattern);
|
|
123
|
+
if (match == null ? void 0 : match[1]) {
|
|
124
|
+
const num = parseInt(match[1], 10);
|
|
125
|
+
if (!isNaN(num)) return num;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
return null;
|
|
129
|
+
}
|
|
130
|
+
function isWorktreePath(path2) {
|
|
131
|
+
const worktreePatterns = [
|
|
132
|
+
/\/worktrees?\//i,
|
|
133
|
+
// Contains /worktree/ or /worktrees/
|
|
134
|
+
/\/workspace[-_]?\d+/i,
|
|
135
|
+
// workspace123, workspace-123
|
|
136
|
+
/\/issue[-_]?\d+/i,
|
|
137
|
+
// issue123, issue-123
|
|
138
|
+
/\/pr[-_]?\d+/i,
|
|
139
|
+
// pr123, pr-123
|
|
140
|
+
/-worktree$/i,
|
|
141
|
+
// ends with -worktree
|
|
142
|
+
/\.worktree$/i
|
|
143
|
+
// ends with .worktree
|
|
144
|
+
];
|
|
145
|
+
return worktreePatterns.some((pattern) => pattern.test(path2));
|
|
146
|
+
}
|
|
147
|
+
function generateWorktreePath(branchName, rootDir = process.cwd(), options) {
|
|
148
|
+
let sanitized = branchName.replace(/\//g, "-");
|
|
149
|
+
if ((options == null ? void 0 : options.isPR) && (options == null ? void 0 : options.prNumber)) {
|
|
150
|
+
sanitized = `${sanitized}_pr_${options.prNumber}`;
|
|
151
|
+
}
|
|
152
|
+
const parentDir = path.dirname(rootDir);
|
|
153
|
+
let prefix;
|
|
154
|
+
if ((options == null ? void 0 : options.prefix) === void 0) {
|
|
155
|
+
const mainFolderName = path.basename(rootDir);
|
|
156
|
+
prefix = mainFolderName ? `${mainFolderName}-looms/` : "looms/";
|
|
157
|
+
} else if (options.prefix === "") {
|
|
158
|
+
prefix = "";
|
|
159
|
+
} else {
|
|
160
|
+
prefix = options.prefix;
|
|
161
|
+
const hasNestedPath = prefix.includes("/");
|
|
162
|
+
if (hasNestedPath) {
|
|
163
|
+
const endsWithSeparator = /[-_/]$/.test(prefix);
|
|
164
|
+
if (!endsWithSeparator) {
|
|
165
|
+
prefix = `${prefix}-`;
|
|
166
|
+
}
|
|
167
|
+
} else {
|
|
168
|
+
const endsWithSeparator = /[-_]$/.test(prefix);
|
|
169
|
+
if (!endsWithSeparator) {
|
|
170
|
+
prefix = `${prefix}-`;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
if (prefix === "") {
|
|
175
|
+
return path.join(parentDir, sanitized);
|
|
176
|
+
} else if (prefix.endsWith("/")) {
|
|
177
|
+
return path.join(parentDir, prefix, sanitized);
|
|
178
|
+
} else if (prefix.includes("/")) {
|
|
179
|
+
const lastSlashIndex = prefix.lastIndexOf("/");
|
|
180
|
+
const dirPath = prefix.substring(0, lastSlashIndex);
|
|
181
|
+
const prefixWithSeparator = prefix.substring(lastSlashIndex + 1);
|
|
182
|
+
return path.join(parentDir, dirPath, `${prefixWithSeparator}${sanitized}`);
|
|
183
|
+
} else {
|
|
184
|
+
return path.join(parentDir, `${prefix}${sanitized}`);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
async function isValidGitRepo(path2) {
|
|
188
|
+
try {
|
|
189
|
+
await executeGitCommand(["rev-parse", "--git-dir"], { cwd: path2 });
|
|
190
|
+
return true;
|
|
191
|
+
} catch {
|
|
192
|
+
return false;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
async function getCurrentBranch(path2 = process.cwd()) {
|
|
196
|
+
try {
|
|
197
|
+
const result = await executeGitCommand(["branch", "--show-current"], { cwd: path2 });
|
|
198
|
+
return result.trim();
|
|
199
|
+
} catch {
|
|
200
|
+
return null;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
async function branchExists(branchName, path2 = process.cwd(), includeRemote = true) {
|
|
204
|
+
try {
|
|
205
|
+
const localResult = await executeGitCommand(["branch", "--list", branchName], { cwd: path2 });
|
|
206
|
+
if (localResult.trim()) {
|
|
207
|
+
return true;
|
|
208
|
+
}
|
|
209
|
+
if (includeRemote) {
|
|
210
|
+
const remoteResult = await executeGitCommand(["branch", "-r", "--list", `*/${branchName}`], {
|
|
211
|
+
cwd: path2
|
|
212
|
+
});
|
|
213
|
+
if (remoteResult.trim()) {
|
|
214
|
+
return true;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
return false;
|
|
218
|
+
} catch {
|
|
219
|
+
return false;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
async function getRepoRoot(path2 = process.cwd()) {
|
|
223
|
+
try {
|
|
224
|
+
const result = await executeGitCommand(["rev-parse", "--show-toplevel"], { cwd: path2 });
|
|
225
|
+
return result.trim();
|
|
226
|
+
} catch {
|
|
227
|
+
return null;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
async function findMainWorktreePath(path2 = process.cwd(), options) {
|
|
231
|
+
try {
|
|
232
|
+
const output = await executeGitCommand(["worktree", "list", "--porcelain"], { cwd: path2 });
|
|
233
|
+
const worktrees = parseWorktreeList(output, options == null ? void 0 : options.mainBranch);
|
|
234
|
+
if (worktrees.length === 0) {
|
|
235
|
+
throw new Error("No worktrees found in repository");
|
|
236
|
+
}
|
|
237
|
+
if (options == null ? void 0 : options.mainBranch) {
|
|
238
|
+
const specified = worktrees.find((wt) => wt.branch === options.mainBranch);
|
|
239
|
+
if (!(specified == null ? void 0 : specified.path)) {
|
|
240
|
+
throw new Error(
|
|
241
|
+
`No worktree found with branch '${options.mainBranch}' (specified in settings). Available worktrees: ${worktrees.map((wt) => `${wt.path} (${wt.branch})`).join(", ")}`
|
|
242
|
+
);
|
|
243
|
+
}
|
|
244
|
+
return specified.path;
|
|
245
|
+
}
|
|
246
|
+
const mainBranch = worktrees.find((wt) => wt.branch === "main");
|
|
247
|
+
if (mainBranch == null ? void 0 : mainBranch.path) {
|
|
248
|
+
return mainBranch.path;
|
|
249
|
+
}
|
|
250
|
+
const firstWorktree = worktrees[0];
|
|
251
|
+
if (!(firstWorktree == null ? void 0 : firstWorktree.path)) {
|
|
252
|
+
throw new Error("Failed to determine primary worktree path");
|
|
253
|
+
}
|
|
254
|
+
return firstWorktree.path;
|
|
255
|
+
} catch (error) {
|
|
256
|
+
if (error instanceof Error && (error.message.includes("No worktree found with branch") || error.message.includes("No worktrees found") || error.message.includes("Failed to determine primary worktree"))) {
|
|
257
|
+
throw error;
|
|
258
|
+
}
|
|
259
|
+
throw new Error(`Failed to find main worktree: ${error instanceof Error ? error.message : String(error)}`);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
async function findMainWorktreePathWithSettings(path2, settingsManager) {
|
|
263
|
+
if (!settingsManager) {
|
|
264
|
+
const { SettingsManager: SM } = await import("./SettingsManager-VAZF26S2.js");
|
|
265
|
+
settingsManager = new SM();
|
|
266
|
+
}
|
|
267
|
+
const settings = await settingsManager.loadSettings(path2);
|
|
268
|
+
const findOptions = settings.mainBranch ? { mainBranch: settings.mainBranch } : void 0;
|
|
269
|
+
return findMainWorktreePath(path2, findOptions);
|
|
270
|
+
}
|
|
271
|
+
async function hasUncommittedChanges(path2 = process.cwd()) {
|
|
272
|
+
try {
|
|
273
|
+
const result = await executeGitCommand(["status", "--porcelain"], { cwd: path2 });
|
|
274
|
+
return result.trim().length > 0;
|
|
275
|
+
} catch {
|
|
276
|
+
return false;
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
async function getDefaultBranch(path2 = process.cwd()) {
|
|
280
|
+
try {
|
|
281
|
+
const remoteResult = await executeGitCommand(["symbolic-ref", "refs/remotes/origin/HEAD"], {
|
|
282
|
+
cwd: path2
|
|
283
|
+
});
|
|
284
|
+
const match = remoteResult.match(/refs\/remotes\/origin\/(.+)/);
|
|
285
|
+
if (match) return match[1] ?? "main";
|
|
286
|
+
const commonDefaults = ["main", "master", "develop"];
|
|
287
|
+
for (const branch of commonDefaults) {
|
|
288
|
+
if (await branchExists(branch, path2)) {
|
|
289
|
+
return branch;
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
return "main";
|
|
293
|
+
} catch {
|
|
294
|
+
return "main";
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
async function findAllBranchesForIssue(issueNumber, path2 = process.cwd(), settingsManager) {
|
|
298
|
+
if (!settingsManager) {
|
|
299
|
+
const { SettingsManager: SM } = await import("./SettingsManager-VAZF26S2.js");
|
|
300
|
+
settingsManager = new SM();
|
|
301
|
+
}
|
|
302
|
+
const protectedBranches = await settingsManager.getProtectedBranches(path2);
|
|
303
|
+
const output = await executeGitCommand(["branch", "-a"], { cwd: path2 });
|
|
304
|
+
const branches = [];
|
|
305
|
+
const lines = output.split("\n").filter(Boolean);
|
|
306
|
+
for (const line of lines) {
|
|
307
|
+
if (line.includes("remotes/origin/HEAD")) {
|
|
308
|
+
continue;
|
|
309
|
+
}
|
|
310
|
+
let cleanBranch = line.replace(/^[*+ ]+/, "");
|
|
311
|
+
cleanBranch = cleanBranch.replace(/^origin\//, "");
|
|
312
|
+
cleanBranch = cleanBranch.replace(/^remotes\/origin\//, "");
|
|
313
|
+
cleanBranch = cleanBranch.trim();
|
|
314
|
+
if (protectedBranches.includes(cleanBranch)) {
|
|
315
|
+
continue;
|
|
316
|
+
}
|
|
317
|
+
const notPartOfNumber = new RegExp(`(?<!\\d)${issueNumber}(?!\\d)`);
|
|
318
|
+
if (!notPartOfNumber.test(cleanBranch)) {
|
|
319
|
+
continue;
|
|
320
|
+
}
|
|
321
|
+
const beforeNumber = cleanBranch.substring(0, cleanBranch.indexOf(String(issueNumber)));
|
|
322
|
+
if (beforeNumber) {
|
|
323
|
+
const lastWord = beforeNumber.match(/([a-zA-Z]+)[-_/\s]*$/);
|
|
324
|
+
if (lastWord == null ? void 0 : lastWord[1]) {
|
|
325
|
+
const word = lastWord[1].toLowerCase();
|
|
326
|
+
const knownPrefixes = [
|
|
327
|
+
"issue",
|
|
328
|
+
"issues",
|
|
329
|
+
"feat",
|
|
330
|
+
"feature",
|
|
331
|
+
"features",
|
|
332
|
+
"fix",
|
|
333
|
+
"fixes",
|
|
334
|
+
"bugfix",
|
|
335
|
+
"hotfix",
|
|
336
|
+
"pr",
|
|
337
|
+
"pull",
|
|
338
|
+
"test",
|
|
339
|
+
"tests",
|
|
340
|
+
"chore",
|
|
341
|
+
"docs",
|
|
342
|
+
"refactor",
|
|
343
|
+
"perf",
|
|
344
|
+
"style",
|
|
345
|
+
"ci",
|
|
346
|
+
"build",
|
|
347
|
+
"revert"
|
|
348
|
+
];
|
|
349
|
+
if (!knownPrefixes.includes(word)) {
|
|
350
|
+
continue;
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
if (!branches.includes(cleanBranch)) {
|
|
355
|
+
branches.push(cleanBranch);
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
return branches;
|
|
359
|
+
}
|
|
360
|
+
async function isEmptyRepository(path2 = process.cwd()) {
|
|
361
|
+
try {
|
|
362
|
+
await executeGitCommand(["rev-parse", "--verify", "HEAD"], { cwd: path2 });
|
|
363
|
+
return false;
|
|
364
|
+
} catch {
|
|
365
|
+
return true;
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
async function ensureRepositoryHasCommits(path2 = process.cwd()) {
|
|
369
|
+
const isEmpty = await isEmptyRepository(path2);
|
|
370
|
+
if (isEmpty) {
|
|
371
|
+
await executeGitCommand(["commit", "--no-verify", "--allow-empty", "-m", "Initial commit"], { cwd: path2 });
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
async function pushBranchToRemote(branchName, worktreePath, options) {
|
|
375
|
+
if (options == null ? void 0 : options.dryRun) {
|
|
376
|
+
return;
|
|
377
|
+
}
|
|
378
|
+
try {
|
|
379
|
+
await executeGitCommand(["push", "origin", branchName], {
|
|
380
|
+
cwd: worktreePath,
|
|
381
|
+
timeout: 12e4
|
|
382
|
+
// 120 second timeout for push operations
|
|
383
|
+
});
|
|
384
|
+
} catch (error) {
|
|
385
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
386
|
+
if (errorMessage.includes("failed to push") || errorMessage.includes("rejected")) {
|
|
387
|
+
throw new Error(
|
|
388
|
+
`Failed to push changes to origin/${branchName}
|
|
389
|
+
|
|
390
|
+
Possible causes:
|
|
391
|
+
\u2022 Remote branch was deleted
|
|
392
|
+
\u2022 Push was rejected (non-fast-forward)
|
|
393
|
+
\u2022 Network connectivity issues
|
|
394
|
+
|
|
395
|
+
To retry: il finish --pr <number>
|
|
396
|
+
To force push: git push origin ${branchName} --force`
|
|
397
|
+
);
|
|
398
|
+
}
|
|
399
|
+
if (errorMessage.includes("Could not resolve host") || errorMessage.includes("network")) {
|
|
400
|
+
throw new Error(
|
|
401
|
+
`Failed to push changes to origin/${branchName}: Network connectivity issues
|
|
402
|
+
|
|
403
|
+
Check your internet connection and try again.`
|
|
404
|
+
);
|
|
405
|
+
}
|
|
406
|
+
if (errorMessage.includes("No such remote")) {
|
|
407
|
+
throw new Error(
|
|
408
|
+
`Failed to push changes: Remote 'origin' not found
|
|
409
|
+
|
|
410
|
+
Configure remote: git remote add origin <url>`
|
|
411
|
+
);
|
|
412
|
+
}
|
|
413
|
+
throw new Error(`Failed to push to remote: ${errorMessage}`);
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
export {
|
|
418
|
+
executeGitCommand,
|
|
419
|
+
parseWorktreeList,
|
|
420
|
+
isPRBranch,
|
|
421
|
+
extractPRNumber,
|
|
422
|
+
isWorktreePath,
|
|
423
|
+
generateWorktreePath,
|
|
424
|
+
isValidGitRepo,
|
|
425
|
+
getCurrentBranch,
|
|
426
|
+
branchExists,
|
|
427
|
+
getRepoRoot,
|
|
428
|
+
findMainWorktreePath,
|
|
429
|
+
findMainWorktreePathWithSettings,
|
|
430
|
+
hasUncommittedChanges,
|
|
431
|
+
getDefaultBranch,
|
|
432
|
+
findAllBranchesForIssue,
|
|
433
|
+
isEmptyRepository,
|
|
434
|
+
ensureRepositoryHasCommits,
|
|
435
|
+
pushBranchToRemote
|
|
436
|
+
};
|
|
437
|
+
//# sourceMappingURL=chunk-JQ7VOSTC.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/utils/git.ts"],"sourcesContent":["import path from 'path'\nimport { execa, type ExecaError } from 'execa'\nimport { type GitWorktree } from '../types/worktree.js'\nimport type { SettingsManager } from '../lib/SettingsManager.js'\nimport { logger } from './logger.js'\n\n/**\n * Execute a Git command and return the stdout result\n * Throws an error if the command fails\n */\nexport async function executeGitCommand(\n args: string[],\n options?: { cwd?: string; timeout?: number; stdio?: 'inherit' | 'pipe' }\n): Promise<string> {\n try {\n const result = await execa('git', args, {\n cwd: options?.cwd ?? process.cwd(),\n timeout: options?.timeout ?? 30000,\n encoding: 'utf8',\n stdio: options?.stdio ?? 'pipe',\n verbose: logger.isDebugEnabled(),\n })\n\n return result.stdout\n } catch (error) {\n const execaError = error as ExecaError\n const stderr = execaError.stderr ?? execaError.message ?? 'Unknown Git error'\n throw new Error(`Git command failed: ${stderr}`)\n }\n}\n\n/**\n * Parse git worktree list output into structured data\n * @param output - The output from git worktree list --porcelain\n * @param defaultBranch - Default branch name to use for bare repositories (defaults to 'main')\n */\nexport function parseWorktreeList(output: string, defaultBranch?: string): GitWorktree[] {\n const worktrees: GitWorktree[] = []\n const lines = output.trim().split('\\n')\n\n let i = 0\n while (i < lines.length) {\n const pathLine = lines[i]\n if (!pathLine?.startsWith('worktree ')) {\n i++\n continue\n }\n\n // Parse path line: \"worktree /path/to/worktree\"\n const pathMatch = pathLine.match(/^worktree (.+)$/)\n if (!pathMatch) {\n i++\n continue\n }\n\n let branch = ''\n let commit = ''\n let detached = false\n let bare = false\n let locked = false\n let lockReason: string | undefined\n\n // Process subsequent lines for this worktree\n i++\n while (i < lines.length && !lines[i]?.startsWith('worktree ')) {\n const line = lines[i]?.trim()\n if (!line) {\n i++\n continue\n }\n\n if (line === 'bare') {\n bare = true\n branch = defaultBranch ?? 'main' // Default assumption for bare repo\n } else if (line === 'detached') {\n detached = true\n branch = 'HEAD'\n } else if (line.startsWith('locked')) {\n locked = true\n const lockMatch = line.match(/^locked (.+)$/)\n lockReason = lockMatch?.[1]\n branch = branch || 'unknown'\n } else if (line.startsWith('HEAD ')) {\n // Parse commit line: \"HEAD abc123def456...\"\n const commitMatch = line.match(/^HEAD ([a-f0-9]+)/)\n if (commitMatch) {\n commit = commitMatch[1] ?? ''\n }\n } else if (line.startsWith('branch ')) {\n // Parse branch line: \"branch refs/heads/feature-branch\"\n const branchMatch = line.match(/^branch refs\\/heads\\/(.+)$/)\n branch = branchMatch?.[1] ?? line.replace('branch ', '')\n }\n\n i++\n }\n\n const worktree: GitWorktree = {\n path: pathMatch[1] ?? '',\n branch,\n commit,\n bare,\n detached,\n locked,\n }\n\n if (lockReason !== undefined) {\n worktree.lockReason = lockReason\n }\n\n worktrees.push(worktree)\n }\n\n return worktrees\n}\n\n/**\n * Check if a branch name follows PR naming patterns\n */\nexport function isPRBranch(branchName: string): boolean {\n const prPatterns = [\n /^pr\\/\\d+/i, // pr/123, pr/123-feature-name\n /^pull\\/\\d+/i, // pull/123\n /^\\d+[-_]/, // 123-feature-name, 123_feature_name\n /^feature\\/pr[-_]?\\d+/i, // feature/pr123, feature/pr-123\n /^hotfix\\/pr[-_]?\\d+/i, // hotfix/pr123\n ]\n\n return prPatterns.some(pattern => pattern.test(branchName))\n}\n\n/**\n * Extract PR number from branch name\n */\nexport function extractPRNumber(branchName: string): number | null {\n const patterns = [\n /^pr\\/(\\d+)/i, // pr/123\n /^pull\\/(\\d+)/i, // pull/123\n /^(\\d+)[-_]/, // 123-feature-name\n /^feature\\/pr[-_]?(\\d+)/i, // feature/pr123\n /^hotfix\\/pr[-_]?(\\d+)/i, // hotfix/pr123\n /pr[-_]?(\\d+)/i, // anywhere with pr123 or pr-123\n ]\n\n for (const pattern of patterns) {\n const match = branchName.match(pattern)\n if (match?.[1]) {\n const num = parseInt(match[1], 10)\n if (!isNaN(num)) return num\n }\n }\n\n return null\n}\n\n/**\n * Check if a path follows worktree naming patterns\n */\nexport function isWorktreePath(path: string): boolean {\n const worktreePatterns = [\n /\\/worktrees?\\//i, // Contains /worktree/ or /worktrees/\n /\\/workspace[-_]?\\d+/i, // workspace123, workspace-123\n /\\/issue[-_]?\\d+/i, // issue123, issue-123\n /\\/pr[-_]?\\d+/i, // pr123, pr-123\n /-worktree$/i, // ends with -worktree\n /\\.worktree$/i, // ends with .worktree\n ]\n\n return worktreePatterns.some(pattern => pattern.test(path))\n}\n\n/**\n * Generate a worktree path based on branch name and root directory\n * For PRs, adds _pr_<PR_NUM> suffix to distinguish from issue branches\n */\nexport function generateWorktreePath(\n branchName: string,\n rootDir: string = process.cwd(),\n options?: { isPR?: boolean; prNumber?: number; prefix?: string }\n): string {\n // Replace slashes with dashes (matches bash line 593)\n let sanitized = branchName.replace(/\\//g, '-')\n\n // Add PR suffix if this is a PR (matches bash lines 595-597)\n if (options?.isPR && options?.prNumber) {\n sanitized = `${sanitized}_pr_${options.prNumber}`\n }\n\n const parentDir = path.dirname(rootDir)\n\n // Handle prefix logic\n let prefix: string\n\n if (options?.prefix === undefined) {\n // No prefix in options - calculate default: <basename>-looms\n const mainFolderName = path.basename(rootDir)\n prefix = mainFolderName ? `${mainFolderName}-looms/` : 'looms/'\n } else if (options.prefix === '') {\n // Empty string = no prefix mode\n prefix = ''\n } else {\n // Custom prefix provided\n prefix = options.prefix\n\n // Check if prefix contains forward slashes (nested directory structure)\n const hasNestedPath = prefix.includes('/')\n\n if (hasNestedPath) {\n // Check if it ends with a separator character (dash, underscore, or slash)\n const endsWithSeparator = /[-_/]$/.test(prefix)\n\n if (!endsWithSeparator) {\n // Has nested path but no trailing separator: auto-append hyphen\n // Example: \"temp/looms\" becomes \"temp/looms-\"\n prefix = `${prefix}-`\n }\n // If it already ends with -, _, or /, keep as-is\n } else {\n // Single-level prefix: auto-append separator if it doesn't end with one\n const endsWithSeparator = /[-_]$/.test(prefix)\n if (!endsWithSeparator) {\n prefix = `${prefix}-`\n }\n }\n }\n\n // Apply prefix (or not, if empty)\n if (prefix === '') {\n return path.join(parentDir, sanitized)\n } else if (prefix.endsWith('/')) {\n // Forward slash = nested directory, use path.join for proper handling\n return path.join(parentDir, prefix, sanitized)\n } else if (prefix.includes('/')) {\n // Contains slash but doesn't end with slash = nested with separator (e.g., \"looms/myprefix-\")\n // Split and handle: last part is prefix with separator, rest is directory path\n const lastSlashIndex = prefix.lastIndexOf('/')\n const dirPath = prefix.substring(0, lastSlashIndex)\n const prefixWithSeparator = prefix.substring(lastSlashIndex + 1)\n return path.join(parentDir, dirPath, `${prefixWithSeparator}${sanitized}`)\n } else {\n // Dash/underscore separator = single directory name\n return path.join(parentDir, `${prefix}${sanitized}`)\n }\n}\n\n/**\n * Validate that a directory is a valid Git repository\n */\nexport async function isValidGitRepo(path: string): Promise<boolean> {\n try {\n await executeGitCommand(['rev-parse', '--git-dir'], { cwd: path })\n return true\n } catch {\n return false\n }\n}\n\n/**\n * Get the current branch name for a repository\n */\nexport async function getCurrentBranch(path: string = process.cwd()): Promise<string | null> {\n try {\n const result = await executeGitCommand(['branch', '--show-current'], { cwd: path })\n return result.trim()\n } catch {\n return null\n }\n}\n\n/**\n * Check if a branch exists (local or remote)\n */\nexport async function branchExists(\n branchName: string,\n path: string = process.cwd(),\n includeRemote = true\n): Promise<boolean> {\n try {\n // Check local branches\n const localResult = await executeGitCommand(['branch', '--list', branchName], { cwd: path })\n if (localResult.trim()) {\n return true\n }\n\n // Check remote branches if requested\n if (includeRemote) {\n const remoteResult = await executeGitCommand(['branch', '-r', '--list', `*/${branchName}`], {\n cwd: path,\n })\n if (remoteResult.trim()) {\n return true\n }\n }\n\n return false\n } catch {\n return false\n }\n}\n\n/**\n * Get repository root directory\n */\nexport async function getRepoRoot(path: string = process.cwd()): Promise<string | null> {\n try {\n const result = await executeGitCommand(['rev-parse', '--show-toplevel'], { cwd: path })\n return result.trim()\n } catch {\n return null\n }\n}\n\n/**\n * Find the worktree path where main branch is checked out\n * Copies bash script approach: parse git worktree list to find main\n */\nexport async function findMainWorktreePath(\n path: string = process.cwd(),\n options?: { mainBranch?: string }\n): Promise<string> {\n try {\n const output = await executeGitCommand(['worktree', 'list', '--porcelain'], { cwd: path })\n const worktrees = parseWorktreeList(output, options?.mainBranch)\n\n // Guard: empty worktree list\n if (worktrees.length === 0) {\n throw new Error('No worktrees found in repository')\n }\n\n // Tier 1: Check for specified mainBranch in options\n if (options?.mainBranch) {\n const specified = worktrees.find(wt => wt.branch === options.mainBranch)\n if (!specified?.path) {\n throw new Error(\n `No worktree found with branch '${options.mainBranch}' (specified in settings). Available worktrees: ${worktrees.map(wt => `${wt.path} (${wt.branch})`).join(', ')}`\n )\n }\n return specified.path\n }\n\n // Tier 2: Look for \"main\" branch\n const mainBranch = worktrees.find(wt => wt.branch === 'main')\n if (mainBranch?.path) {\n return mainBranch.path\n }\n\n // Tier 3: Use first worktree (primary worktree)\n const firstWorktree = worktrees[0]\n if (!firstWorktree?.path) {\n throw new Error('Failed to determine primary worktree path')\n }\n return firstWorktree.path\n } catch (error) {\n if (\n error instanceof Error &&\n (error.message.includes('No worktree found with branch') ||\n error.message.includes('No worktrees found') ||\n error.message.includes('Failed to determine primary worktree'))\n ) {\n // Re-throw our specific errors\n throw error\n }\n throw new Error(`Failed to find main worktree: ${error instanceof Error ? error.message : String(error)}`)\n }\n}\n\n/**\n * Find main worktree path with automatic settings loading\n *\n * This is a convenience wrapper that:\n * 1. Loads project settings from .iloom/settings.json\n * 2. Extracts mainBranch configuration if present\n * 3. Calls findMainWorktreePath with appropriate options\n *\n * @param path - Path to search from (defaults to process.cwd())\n * @param settingsManager - Optional SettingsManager instance (for DI/testing)\n * @returns Path to main worktree\n * @throws Error if main worktree cannot be found\n */\nexport async function findMainWorktreePathWithSettings(\n path?: string,\n settingsManager?: SettingsManager\n): Promise<string> {\n // Lazy load SettingsManager to avoid circular dependencies\n if (!settingsManager) {\n const { SettingsManager: SM } = await import('../lib/SettingsManager.js')\n settingsManager = new SM()\n }\n\n const settings = await settingsManager.loadSettings(path)\n const findOptions = settings.mainBranch ? { mainBranch: settings.mainBranch } : undefined\n return findMainWorktreePath(path, findOptions)\n}\n\n/**\n * Check if there are uncommitted changes in a repository\n */\nexport async function hasUncommittedChanges(path: string = process.cwd()): Promise<boolean> {\n try {\n const result = await executeGitCommand(['status', '--porcelain'], { cwd: path })\n return result.trim().length > 0\n } catch {\n return false\n }\n}\n\n/**\n * Get the default branch name for a repository\n */\nexport async function getDefaultBranch(path: string = process.cwd()): Promise<string> {\n try {\n // Try to get from remote\n const remoteResult = await executeGitCommand(['symbolic-ref', 'refs/remotes/origin/HEAD'], {\n cwd: path,\n })\n const match = remoteResult.match(/refs\\/remotes\\/origin\\/(.+)/)\n if (match) return match[1] ?? 'main'\n\n // Fallback to common default branch names\n const commonDefaults = ['main', 'master', 'develop']\n for (const branch of commonDefaults) {\n if (await branchExists(branch, path)) {\n return branch\n }\n }\n\n return 'main' // Final fallback\n } catch {\n return 'main'\n }\n}\n\n/**\n * Find all branches related to a GitHub issue or PR number\n * Matches patterns like:\n * - Issue patterns: issue-25, issue/25, 25-feature, feat-25, feat/issue-25\n * - PR patterns: pr/25, pull/25, pr-25, feature/pr-25\n *\n * Based on bash cleanup-worktree.sh find_issue_branches() (lines 133-154)\n *\n * @param issueNumber - The issue or PR number to search for\n * @param path - Working directory to search from (defaults to process.cwd())\n * @param settingsManager - Optional SettingsManager instance (for DI/testing)\n */\nexport async function findAllBranchesForIssue(\n issueNumber: number,\n path: string = process.cwd(),\n settingsManager?: SettingsManager\n): Promise<string[]> {\n // Lazy load SettingsManager to avoid circular dependencies\n if (!settingsManager) {\n const { SettingsManager: SM } = await import('../lib/SettingsManager.js')\n settingsManager = new SM()\n }\n\n // Get protected branches list from centralized method\n const protectedBranches = await settingsManager.getProtectedBranches(path)\n\n // Get all branches (local and remote)\n const output = await executeGitCommand(['branch', '-a'], { cwd: path })\n\n const branches: string[] = []\n const lines = output.split('\\n').filter(Boolean)\n\n for (const line of lines) {\n // Skip remotes/origin/HEAD pointer\n if (line.includes('remotes/origin/HEAD')) {\n continue\n }\n\n // Clean the branch name:\n // 1. Remove git status markers (* + spaces at start)\n let cleanBranch = line.replace(/^[*+ ]+/, '')\n\n // 2. Remove 'origin/' prefix if present\n cleanBranch = cleanBranch.replace(/^origin\\//, '')\n\n // 3. Remove 'remotes/origin/' prefix if present\n cleanBranch = cleanBranch.replace(/^remotes\\/origin\\//, '')\n\n // 4. Trim any remaining whitespace\n cleanBranch = cleanBranch.trim()\n\n // Skip protected branches\n if (protectedBranches.includes(cleanBranch)) {\n continue\n }\n\n // Check if branch contains issue number with strict word boundary pattern\n // The issue number must NOT be:\n // - Part of a larger number (preceded or followed by a digit)\n // - After an unknown word (like \"tissue-25\")\n // The issue number CAN be:\n // - At start: \"25-feature\"\n // - After known prefix + separator: \"issue-25\", \"feat-25\", \"fix-25\", \"pr-25\"\n // - After just a separator with no prefix: test_25 (separator at start)\n\n // First check: not part of a larger number\n const notPartOfNumber = new RegExp(`(?<!\\\\d)${issueNumber}(?!\\\\d)`)\n if (!notPartOfNumber.test(cleanBranch)) {\n continue\n }\n\n // Second check: if preceded by letters, validate they're known issue-related prefixes\n // This prevents \"tissue-25\" but allows \"issue-25\", \"feat-25\", etc.\n const beforeNumber = cleanBranch.substring(0, cleanBranch.indexOf(String(issueNumber)))\n\n if (beforeNumber) {\n // Extract the last word (letters) before the number\n const lastWord = beforeNumber.match(/([a-zA-Z]+)[-_/\\s]*$/)\n if (lastWord?.[1]) {\n const word = lastWord[1].toLowerCase()\n // Known prefixes for issue-related branches\n const knownPrefixes = [\n 'issue', 'issues',\n 'feat', 'feature', 'features',\n 'fix', 'fixes', 'bugfix', 'hotfix',\n 'pr', 'pull',\n 'test', 'tests',\n 'chore',\n 'docs',\n 'refactor',\n 'perf',\n 'style',\n 'ci',\n 'build',\n 'revert'\n ]\n\n // If we found a word and it's NOT in the known list, skip this branch\n if (!knownPrefixes.includes(word)) {\n continue\n }\n }\n }\n\n // Passed all checks - add to results\n if (!branches.includes(cleanBranch)) {\n branches.push(cleanBranch)\n }\n }\n\n return branches\n}\n\n/**\n * Check if a repository is empty (has no commits yet)\n * @param path - Repository path to check (defaults to process.cwd())\n * @returns true if repository has no commits, false otherwise\n */\nexport async function isEmptyRepository(path: string = process.cwd()): Promise<boolean> {\n try {\n await executeGitCommand(['rev-parse', '--verify', 'HEAD'], { cwd: path })\n return false // HEAD exists, repo has commits\n } catch {\n return true // HEAD doesn't exist, repo is empty\n }\n}\n\n/**\n * Ensure repository has at least one commit\n * Creates an initial empty commit if repository is empty\n * @param path - Repository path (defaults to process.cwd())\n */\nexport async function ensureRepositoryHasCommits(path: string = process.cwd()): Promise<void> {\n const isEmpty = await isEmptyRepository(path)\n if (isEmpty) {\n await executeGitCommand(['commit', '--no-verify', '--allow-empty', '-m', 'Initial commit'], { cwd: path })\n }\n}\n\n/**\n * Push a branch to remote repository\n * Used for PR workflow to push changes to remote without merging locally\n *\n * @param branchName - The branch name to push\n * @param worktreePath - The worktree path where the branch is checked out\n * @param options - Push options\n * @throws Error if push fails\n */\nexport async function pushBranchToRemote(\n branchName: string,\n worktreePath: string,\n options?: { dryRun?: boolean }\n): Promise<void> {\n if (options?.dryRun) {\n // In dry-run mode, just log what would be done\n return\n }\n\n try {\n // Execute: git push origin <branch-name>\n // This matches the bash script behavior (merge-and-clean.sh line 359)\n await executeGitCommand(['push', 'origin', branchName], {\n cwd: worktreePath,\n timeout: 120000, // 120 second timeout for push operations\n })\n } catch (error) {\n // Provide helpful error message based on common push failures\n const errorMessage = error instanceof Error ? error.message : String(error)\n\n // Check for common error patterns\n if (errorMessage.includes('failed to push') || errorMessage.includes('rejected')) {\n throw new Error(\n `Failed to push changes to origin/${branchName}\\n\\n` +\n ` Possible causes:\\n` +\n ` • Remote branch was deleted\\n` +\n ` • Push was rejected (non-fast-forward)\\n` +\n ` • Network connectivity issues\\n\\n` +\n ` To retry: il finish --pr <number>\\n` +\n ` To force push: git push origin ${branchName} --force`\n )\n }\n\n if (errorMessage.includes('Could not resolve host') || errorMessage.includes('network')) {\n throw new Error(\n `Failed to push changes to origin/${branchName}: Network connectivity issues\\n\\n` +\n ` Check your internet connection and try again.`\n )\n }\n\n if (errorMessage.includes('No such remote')) {\n throw new Error(\n `Failed to push changes: Remote 'origin' not found\\n\\n` +\n ` Configure remote: git remote add origin <url>`\n )\n }\n\n // For other errors, re-throw with original message\n throw new Error(`Failed to push to remote: ${errorMessage}`)\n }\n}\n"],"mappings":"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","names":["path"]}
|