techunter 0.1.5 → 0.1.7
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/README.md +1 -1
- package/dist/index.js +165 -55
- package/dist/mcp.js +154 -58
- package/package.json +1 -1
package/README.md
CHANGED
package/dist/index.js
CHANGED
|
@@ -18,7 +18,9 @@ __export(github_exports, {
|
|
|
18
18
|
createPR: () => createPR,
|
|
19
19
|
createTask: () => createTask,
|
|
20
20
|
editTask: () => editTask,
|
|
21
|
+
embedBaseCommit: () => embedBaseCommit,
|
|
21
22
|
ensureLabels: () => ensureLabels,
|
|
23
|
+
extractBaseCommit: () => extractBaseCommit,
|
|
22
24
|
formatGuideAsMarkdown: () => formatGuideAsMarkdown,
|
|
23
25
|
getAuthenticatedUser: () => getAuthenticatedUser,
|
|
24
26
|
getDefaultBranch: () => getDefaultBranch,
|
|
@@ -29,6 +31,7 @@ __export(github_exports, {
|
|
|
29
31
|
listTasks: () => listTasks,
|
|
30
32
|
listTasksForReview: () => listTasksForReview,
|
|
31
33
|
markInReview: () => markInReview,
|
|
34
|
+
mergeWorkerIntoBase: () => mergeWorkerIntoBase,
|
|
32
35
|
postComment: () => postComment,
|
|
33
36
|
postGuideComment: () => postGuideComment,
|
|
34
37
|
rejectTask: () => rejectTask
|
|
@@ -77,19 +80,41 @@ async function getTask(config, number) {
|
|
|
77
80
|
const { data } = await octokit.issues.get({ owner, repo, issue_number: number });
|
|
78
81
|
return parseIssue(data);
|
|
79
82
|
}
|
|
80
|
-
|
|
83
|
+
function embedBaseCommit(body, sha) {
|
|
84
|
+
return `${body}
|
|
85
|
+
|
|
86
|
+
${BASE_COMMIT_MARKER}${sha} -->`;
|
|
87
|
+
}
|
|
88
|
+
function extractBaseCommit(body) {
|
|
89
|
+
if (!body) return null;
|
|
90
|
+
const match = body.match(/<!-- techunter-base:([a-f0-9]{7,40}) -->/);
|
|
91
|
+
return match?.[1] ?? null;
|
|
92
|
+
}
|
|
93
|
+
async function createTask(config, title, body, baseCommit) {
|
|
81
94
|
const octokit = createOctokit(config.githubToken);
|
|
82
95
|
const { owner, repo } = config.github;
|
|
83
96
|
await ensureLabels(config);
|
|
97
|
+
const finalBody = baseCommit ? embedBaseCommit(body ?? "", baseCommit) : body;
|
|
84
98
|
const { data } = await octokit.issues.create({
|
|
85
99
|
owner,
|
|
86
100
|
repo,
|
|
87
101
|
title,
|
|
88
|
-
body,
|
|
102
|
+
body: finalBody,
|
|
89
103
|
labels: [LABEL_AVAILABLE]
|
|
90
104
|
});
|
|
91
105
|
return parseIssue(data);
|
|
92
106
|
}
|
|
107
|
+
async function mergeWorkerIntoBase(config, workerBranch, baseBranch) {
|
|
108
|
+
const octokit = createOctokit(config.githubToken);
|
|
109
|
+
const { owner, repo } = config.github;
|
|
110
|
+
await octokit.repos.merge({
|
|
111
|
+
owner,
|
|
112
|
+
repo,
|
|
113
|
+
base: baseBranch,
|
|
114
|
+
head: workerBranch,
|
|
115
|
+
commit_message: `chore: merge ${workerBranch} into ${baseBranch}`
|
|
116
|
+
});
|
|
117
|
+
}
|
|
93
118
|
async function claimTask(config, number, username) {
|
|
94
119
|
const octokit = createOctokit(config.githubToken);
|
|
95
120
|
const { owner, repo } = config.github;
|
|
@@ -308,12 +333,12 @@ async function getDefaultBranch(config) {
|
|
|
308
333
|
const { data } = await octokit.repos.get({ owner, repo });
|
|
309
334
|
return data.default_branch;
|
|
310
335
|
}
|
|
311
|
-
async function acceptTask(config, issueNumber) {
|
|
336
|
+
async function acceptTask(config, issueNumber, headBranch) {
|
|
312
337
|
const octokit = createOctokit(config.githubToken);
|
|
313
338
|
const { owner, repo } = config.github;
|
|
314
339
|
const { data: prs } = await octokit.pulls.list({ owner, repo, state: "open", per_page: 100 });
|
|
315
|
-
const pr = prs.find((p) => p.head.ref.startsWith(`task-${issueNumber}-`));
|
|
316
|
-
if (!pr) throw new Error(`No open PR found for task #${issueNumber}
|
|
340
|
+
const pr = headBranch ? prs.find((p) => p.head.ref === headBranch) : prs.find((p) => p.head.ref.startsWith(`task-${issueNumber}-`) || p.head.ref.startsWith("worker-"));
|
|
341
|
+
if (!pr) throw new Error(`No open PR found for task #${issueNumber}`);
|
|
317
342
|
const { data: merge } = await octokit.pulls.merge({
|
|
318
343
|
owner,
|
|
319
344
|
repo,
|
|
@@ -323,7 +348,7 @@ async function acceptTask(config, issueNumber) {
|
|
|
323
348
|
await closeTask(config, issueNumber);
|
|
324
349
|
return { prNumber: pr.number, prUrl: pr.html_url, sha: merge.sha ?? "" };
|
|
325
350
|
}
|
|
326
|
-
var LABEL_AVAILABLE, LABEL_CLAIMED, LABEL_IN_REVIEW, LABEL_CHANGES_NEEDED, LABELS, TECHUNTER_LABELS;
|
|
351
|
+
var LABEL_AVAILABLE, LABEL_CLAIMED, LABEL_IN_REVIEW, LABEL_CHANGES_NEEDED, LABELS, TECHUNTER_LABELS, BASE_COMMIT_MARKER;
|
|
327
352
|
var init_github = __esm({
|
|
328
353
|
"src/lib/github.ts"() {
|
|
329
354
|
"use strict";
|
|
@@ -338,6 +363,7 @@ var init_github = __esm({
|
|
|
338
363
|
{ name: LABEL_CHANGES_NEEDED, color: "e11d48", description: "Task needs changes" }
|
|
339
364
|
];
|
|
340
365
|
TECHUNTER_LABELS = /* @__PURE__ */ new Set([LABEL_AVAILABLE, LABEL_CLAIMED, LABEL_IN_REVIEW, LABEL_CHANGES_NEEDED]);
|
|
366
|
+
BASE_COMMIT_MARKER = "<!-- techunter-base:";
|
|
341
367
|
}
|
|
342
368
|
});
|
|
343
369
|
|
|
@@ -362,6 +388,7 @@ var configSchema = z.object({
|
|
|
362
388
|
aiModel: z.string().optional(),
|
|
363
389
|
githubToken: z.string().min(1),
|
|
364
390
|
githubClientId: z.string().optional(),
|
|
391
|
+
baseBranch: z.string().optional(),
|
|
365
392
|
github: z.object({
|
|
366
393
|
owner: z.string().min(1),
|
|
367
394
|
repo: z.string().min(1)
|
|
@@ -406,6 +433,9 @@ function setConfig(partial) {
|
|
|
406
433
|
if (partial.githubClientId !== void 0) {
|
|
407
434
|
current["githubClientId"] = partial.githubClientId;
|
|
408
435
|
}
|
|
436
|
+
if (partial.baseBranch !== void 0) {
|
|
437
|
+
current["baseBranch"] = partial.baseBranch;
|
|
438
|
+
}
|
|
409
439
|
if (partial.taskState !== void 0) {
|
|
410
440
|
current["taskState"] = {
|
|
411
441
|
...current["taskState"] ?? {},
|
|
@@ -568,6 +598,28 @@ async function stageAllAndCommit(message) {
|
|
|
568
598
|
const branch = (await git.branch()).current;
|
|
569
599
|
await git.push("origin", branch, ["--set-upstream"]);
|
|
570
600
|
}
|
|
601
|
+
async function syncWithBase(baseBranch) {
|
|
602
|
+
await git.fetch("origin", baseBranch);
|
|
603
|
+
try {
|
|
604
|
+
await git.merge([`origin/${baseBranch}`, "--ff-only"]);
|
|
605
|
+
} catch {
|
|
606
|
+
await git.merge([`origin/${baseBranch}`, "-m", `chore: sync with ${baseBranch}`]);
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
async function getRemoteHeadSha(baseBranch) {
|
|
610
|
+
await git.fetch("origin", baseBranch);
|
|
611
|
+
return (await git.revparse([`origin/${baseBranch}`])).trim();
|
|
612
|
+
}
|
|
613
|
+
async function resetOrCreateBranch(branchName, sha) {
|
|
614
|
+
const branches = await git.branch();
|
|
615
|
+
const localExists = Object.keys(branches.branches).some((b) => b === branchName);
|
|
616
|
+
if (localExists) {
|
|
617
|
+
await git.checkout(branchName);
|
|
618
|
+
await git.reset(["--hard", sha]);
|
|
619
|
+
} else {
|
|
620
|
+
await git.checkoutBranch(branchName, sha);
|
|
621
|
+
}
|
|
622
|
+
}
|
|
571
623
|
|
|
572
624
|
// src/lib/client.ts
|
|
573
625
|
import OpenAI from "openai";
|
|
@@ -736,10 +788,12 @@ async function configCommand() {
|
|
|
736
788
|
`));
|
|
737
789
|
const currentBaseUrl = config.aiBaseUrl ?? DEFAULT_BASE_URL;
|
|
738
790
|
const currentModel = config.aiModel ?? DEFAULT_MODEL;
|
|
791
|
+
const currentBaseBranch = config.baseBranch ?? "main";
|
|
739
792
|
const field = await select2({
|
|
740
793
|
message: "Which setting to change?",
|
|
741
794
|
choices: [
|
|
742
795
|
{ name: `GitHub repo ${chalk3.dim(`${config.github.owner}/${config.github.repo}`)}`, value: "repo" },
|
|
796
|
+
{ name: `Base branch ${chalk3.dim(currentBaseBranch)}`, value: "baseBranch" },
|
|
743
797
|
{ name: `AI base URL ${chalk3.dim(currentBaseUrl)}`, value: "aiBaseUrl" },
|
|
744
798
|
{ name: `AI model ${chalk3.dim(currentModel)}`, value: "aiModel" },
|
|
745
799
|
{ name: `AI API Key ${chalk3.dim("(hidden)")}`, value: "aiApiKey" },
|
|
@@ -748,7 +802,15 @@ async function configCommand() {
|
|
|
748
802
|
]
|
|
749
803
|
});
|
|
750
804
|
if (field === "cancel") return;
|
|
751
|
-
if (field === "
|
|
805
|
+
if (field === "baseBranch") {
|
|
806
|
+
const val = await input2({ message: "Base branch name:", default: currentBaseBranch });
|
|
807
|
+
if (val.trim()) {
|
|
808
|
+
setConfig({ baseBranch: val.trim() });
|
|
809
|
+
console.log(chalk3.green(`
|
|
810
|
+
Base branch set to: ${val.trim()}
|
|
811
|
+
`));
|
|
812
|
+
}
|
|
813
|
+
} else if (field === "repo") {
|
|
752
814
|
const owner = await input2({ message: "GitHub repo owner:", default: config.github.owner });
|
|
753
815
|
const repo = await input2({ message: "GitHub repo name:", default: config.github.repo });
|
|
754
816
|
setConfig({ github: { ...config.github, owner: owner.trim(), repo: repo.trim() } });
|
|
@@ -805,6 +867,7 @@ init_github();
|
|
|
805
867
|
import chalk8 from "chalk";
|
|
806
868
|
import ora4 from "ora";
|
|
807
869
|
import { select as select5 } from "@inquirer/prompts";
|
|
870
|
+
init_github();
|
|
808
871
|
|
|
809
872
|
// src/lib/markdown.ts
|
|
810
873
|
import { marked } from "marked";
|
|
@@ -944,7 +1007,8 @@ async function launchClaudeCode(issue, branch) {
|
|
|
944
1007
|
const prompt = buildClaudePrompt(issue, branch);
|
|
945
1008
|
console.log(chalk5.dim("\n Launching Claude Code\u2026\n"));
|
|
946
1009
|
await new Promise((resolve) => {
|
|
947
|
-
const
|
|
1010
|
+
const safePrompt = prompt.replace(/\r?\n/g, " ").replace(/"/g, "'");
|
|
1011
|
+
const child = spawn(`claude "${safePrompt}"`, [], { stdio: "inherit", shell: true });
|
|
948
1012
|
child.on("close", () => resolve());
|
|
949
1013
|
child.on("error", () => {
|
|
950
1014
|
console.log(
|
|
@@ -1142,8 +1206,16 @@ async function run(_input, config) {
|
|
|
1142
1206
|
return `Commit failed: ${err.message}`;
|
|
1143
1207
|
}
|
|
1144
1208
|
if (isSelfSubmit) {
|
|
1209
|
+
spinner = ora2("Closing issue\u2026").start();
|
|
1210
|
+
try {
|
|
1211
|
+
await closeTask(config, issueNumber);
|
|
1212
|
+
spinner.stop();
|
|
1213
|
+
} catch (err) {
|
|
1214
|
+
spinner.stop();
|
|
1215
|
+
console.error(chalk7.yellow(`Warning: failed to close issue: ${err.message}`));
|
|
1216
|
+
}
|
|
1145
1217
|
setConfig({ taskState: { activeIssueNumber: void 0, baseCommit: void 0 } });
|
|
1146
|
-
return `Task #${issueNumber} committed.
|
|
1218
|
+
return `Task #${issueNumber} committed and closed.
|
|
1147
1219
|
Commit: "${commitMessage.trim()}"`;
|
|
1148
1220
|
}
|
|
1149
1221
|
spinner = ora2("Creating pull request\u2026").start();
|
|
@@ -1204,8 +1276,12 @@ async function execute(input3, config) {
|
|
|
1204
1276
|
return `Commit failed: ${err.message}`;
|
|
1205
1277
|
}
|
|
1206
1278
|
if (isSelfSubmit) {
|
|
1279
|
+
try {
|
|
1280
|
+
await closeTask(config, issueNumber);
|
|
1281
|
+
} catch {
|
|
1282
|
+
}
|
|
1207
1283
|
setConfig({ taskState: { activeIssueNumber: void 0, baseCommit: void 0 } });
|
|
1208
|
-
return `Task #${issueNumber} committed.
|
|
1284
|
+
return `Task #${issueNumber} committed and closed.
|
|
1209
1285
|
Commit: "${commitMessage}"`;
|
|
1210
1286
|
}
|
|
1211
1287
|
let prUrl;
|
|
@@ -1413,41 +1489,29 @@ Finish or submit it before claiming a new one.`;
|
|
|
1413
1489
|
await claimTask(config, issue.number, me);
|
|
1414
1490
|
spinner.stop();
|
|
1415
1491
|
const workerBranch = makeWorkerBranchName(me);
|
|
1416
|
-
|
|
1492
|
+
const taskBase = extractBaseCommit(issue.body);
|
|
1493
|
+
spinner = ora4(`Switching to ${workerBranch}${taskBase ? ` from ${taskBase.slice(0, 7)}` : ""}\u2026`).start();
|
|
1417
1494
|
try {
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
try {
|
|
1423
|
-
await pushBranch(workerBranch);
|
|
1424
|
-
spinner.stop();
|
|
1425
|
-
} catch {
|
|
1426
|
-
spinner.warn("Could not push worker branch");
|
|
1427
|
-
}
|
|
1495
|
+
if (taskBase) {
|
|
1496
|
+
await resetOrCreateBranch(workerBranch, taskBase);
|
|
1497
|
+
} else {
|
|
1498
|
+
await switchToBranchOrCreate(workerBranch);
|
|
1428
1499
|
}
|
|
1429
|
-
} catch {
|
|
1430
|
-
spinner.warn(`Could not switch to ${workerBranch}`);
|
|
1431
|
-
}
|
|
1432
|
-
const branch = makeBranchName(issue.number, me);
|
|
1433
|
-
spinner = ora4(`Creating task branch ${branch}\u2026`).start();
|
|
1434
|
-
try {
|
|
1435
|
-
await switchToBranchOrCreate(branch);
|
|
1436
1500
|
spinner.stop();
|
|
1437
|
-
spinner = ora4("Pushing
|
|
1501
|
+
spinner = ora4("Pushing worker branch\u2026").start();
|
|
1438
1502
|
try {
|
|
1439
|
-
await pushBranch(
|
|
1503
|
+
await pushBranch(workerBranch);
|
|
1440
1504
|
spinner.stop();
|
|
1441
1505
|
} catch {
|
|
1442
|
-
spinner.warn("Could not push
|
|
1506
|
+
spinner.warn("Could not push worker branch");
|
|
1443
1507
|
}
|
|
1444
1508
|
} catch {
|
|
1445
|
-
spinner.warn(`Could not
|
|
1509
|
+
spinner.warn(`Could not switch to ${workerBranch}`);
|
|
1446
1510
|
}
|
|
1447
1511
|
const baseCommit = await getCurrentCommit();
|
|
1448
1512
|
setConfig({ taskState: { activeIssueNumber: issue.number, baseCommit } });
|
|
1449
1513
|
console.log(chalk8.green(`
|
|
1450
|
-
Claimed! Branch: ${
|
|
1514
|
+
Claimed! Branch: ${workerBranch} (base: ${baseCommit.slice(0, 7)})
|
|
1451
1515
|
`));
|
|
1452
1516
|
let openClaude;
|
|
1453
1517
|
try {
|
|
@@ -1461,8 +1525,8 @@ Finish or submit it before claiming a new one.`;
|
|
|
1461
1525
|
} catch {
|
|
1462
1526
|
openClaude = false;
|
|
1463
1527
|
}
|
|
1464
|
-
if (openClaude) await launchClaudeCode(issue,
|
|
1465
|
-
return `Task #${issue.number} claimed. Branch: ${
|
|
1528
|
+
if (openClaude) await launchClaudeCode(issue, workerBranch);
|
|
1529
|
+
return `Task #${issue.number} claimed. Branch: ${workerBranch}`;
|
|
1466
1530
|
} catch (err) {
|
|
1467
1531
|
return `Error claiming task: ${err.message}`;
|
|
1468
1532
|
}
|
|
@@ -1501,28 +1565,22 @@ async function execute3(input3, config) {
|
|
|
1501
1565
|
return `Error claiming task: ${err.message}`;
|
|
1502
1566
|
}
|
|
1503
1567
|
const workerBranch = makeWorkerBranchName(me);
|
|
1568
|
+
const taskBase = extractBaseCommit(issue.body);
|
|
1504
1569
|
try {
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
} catch {
|
|
1510
|
-
}
|
|
1570
|
+
if (taskBase) {
|
|
1571
|
+
await resetOrCreateBranch(workerBranch, taskBase);
|
|
1572
|
+
} else {
|
|
1573
|
+
await switchToBranchOrCreate(workerBranch);
|
|
1511
1574
|
}
|
|
1512
1575
|
} catch {
|
|
1513
1576
|
}
|
|
1514
|
-
const branch = makeBranchName(issueNumber, me);
|
|
1515
1577
|
try {
|
|
1516
|
-
await
|
|
1517
|
-
} catch {
|
|
1518
|
-
}
|
|
1519
|
-
try {
|
|
1520
|
-
await pushBranch(branch);
|
|
1578
|
+
await pushBranch(workerBranch);
|
|
1521
1579
|
} catch {
|
|
1522
1580
|
}
|
|
1523
1581
|
const baseCommit = await getCurrentCommit();
|
|
1524
1582
|
setConfig({ taskState: { activeIssueNumber: issueNumber, baseCommit } });
|
|
1525
|
-
return `Task #${issueNumber} claimed. Branch: ${
|
|
1583
|
+
return `Task #${issueNumber} claimed. Branch: ${workerBranch} (base commit: ${baseCommit.slice(0, 7)})`;
|
|
1526
1584
|
}
|
|
1527
1585
|
return `Unknown action: ${action}`;
|
|
1528
1586
|
}
|
|
@@ -1692,12 +1750,26 @@ async function run4(input3, config) {
|
|
|
1692
1750
|
console.log(chalk9.yellow(` Revision error: ${err.message}`));
|
|
1693
1751
|
}
|
|
1694
1752
|
}
|
|
1753
|
+
const baseBranch = config.baseBranch ?? "main";
|
|
1754
|
+
let baseCommit;
|
|
1755
|
+
const syncSpinner = ora5(`Syncing with ${baseBranch}\u2026`).start();
|
|
1756
|
+
try {
|
|
1757
|
+
await syncWithBase(baseBranch);
|
|
1758
|
+
baseCommit = await getCurrentCommit();
|
|
1759
|
+
syncSpinner.succeed(`Synced with ${baseBranch} (base: ${baseCommit.slice(0, 7)})`);
|
|
1760
|
+
} catch {
|
|
1761
|
+
syncSpinner.warn(`Could not sync with ${baseBranch} \u2014 recording remote HEAD as base`);
|
|
1762
|
+
try {
|
|
1763
|
+
baseCommit = await getRemoteHeadSha(baseBranch);
|
|
1764
|
+
} catch {
|
|
1765
|
+
}
|
|
1766
|
+
}
|
|
1695
1767
|
const createSpinner = ora5(`Creating "${title}"\u2026`).start();
|
|
1696
1768
|
let htmlUrl;
|
|
1697
1769
|
let issueNumber;
|
|
1698
1770
|
let issueTitle;
|
|
1699
1771
|
try {
|
|
1700
|
-
const issue = await createTask(config, title, guide);
|
|
1772
|
+
const issue = await createTask(config, title, guide, baseCommit);
|
|
1701
1773
|
createSpinner.stop();
|
|
1702
1774
|
htmlUrl = issue.htmlUrl;
|
|
1703
1775
|
issueNumber = issue.number;
|
|
@@ -1734,8 +1806,19 @@ async function execute4(input3, config) {
|
|
|
1734
1806
|
if (feedback) {
|
|
1735
1807
|
guide = await generateGuide(config, title, { feedback, previousGuide: guide });
|
|
1736
1808
|
}
|
|
1809
|
+
const baseBranch = config.baseBranch ?? "main";
|
|
1810
|
+
let baseCommit;
|
|
1811
|
+
try {
|
|
1812
|
+
await syncWithBase(baseBranch);
|
|
1813
|
+
baseCommit = await getCurrentCommit();
|
|
1814
|
+
} catch {
|
|
1815
|
+
try {
|
|
1816
|
+
baseCommit = await getRemoteHeadSha(baseBranch);
|
|
1817
|
+
} catch {
|
|
1818
|
+
}
|
|
1819
|
+
}
|
|
1737
1820
|
try {
|
|
1738
|
-
const issue = await createTask(config, title, guide);
|
|
1821
|
+
const issue = await createTask(config, title, guide, baseCommit);
|
|
1739
1822
|
return `Created #${issue.number} "${issue.title}" \u2014 ${issue.htmlUrl}
|
|
1740
1823
|
|
|
1741
1824
|
Guide:
|
|
@@ -2136,16 +2219,40 @@ async function run10(input3, config) {
|
|
|
2136
2219
|
}
|
|
2137
2220
|
if (!confirmed) return "Cancelled.";
|
|
2138
2221
|
const spinner = ora9(`Merging PR for #${issueNumber}\u2026`).start();
|
|
2222
|
+
let result;
|
|
2139
2223
|
try {
|
|
2140
|
-
const
|
|
2224
|
+
const assigneeWorkerBranch = issue.assignee ? makeWorkerBranchName(issue.assignee) : void 0;
|
|
2225
|
+
result = await acceptTask(config, issueNumber, assigneeWorkerBranch);
|
|
2141
2226
|
spinner.succeed(`PR #${result.prNumber} merged into ${targetBranch}`);
|
|
2142
|
-
return `Task #${issueNumber} accepted.
|
|
2143
|
-
PR #${result.prNumber} merged \u2192 ${targetBranch}
|
|
2144
|
-
Issue closed.`;
|
|
2145
2227
|
} catch (err) {
|
|
2146
2228
|
spinner.fail("Failed");
|
|
2147
2229
|
return `Error: ${err.message}`;
|
|
2148
2230
|
}
|
|
2231
|
+
const baseBranch = config.baseBranch ?? "main";
|
|
2232
|
+
let pushToMain;
|
|
2233
|
+
try {
|
|
2234
|
+
pushToMain = await select8({
|
|
2235
|
+
message: `Push ${chalk11.cyan(targetBranch)} \u2192 ${chalk11.cyan(baseBranch)}?`,
|
|
2236
|
+
choices: [
|
|
2237
|
+
{ name: `Yes, push to ${baseBranch}`, value: true },
|
|
2238
|
+
{ name: "No, keep in worker branch", value: false }
|
|
2239
|
+
]
|
|
2240
|
+
});
|
|
2241
|
+
} catch {
|
|
2242
|
+
pushToMain = false;
|
|
2243
|
+
}
|
|
2244
|
+
if (pushToMain) {
|
|
2245
|
+
const mergeSpinner = ora9(`Merging ${targetBranch} \u2192 ${baseBranch}\u2026`).start();
|
|
2246
|
+
try {
|
|
2247
|
+
await mergeWorkerIntoBase(config, targetBranch, baseBranch);
|
|
2248
|
+
mergeSpinner.succeed(`Merged ${targetBranch} \u2192 ${baseBranch}`);
|
|
2249
|
+
} catch (err) {
|
|
2250
|
+
mergeSpinner.fail(`Could not merge to ${baseBranch}: ${err.message}`);
|
|
2251
|
+
}
|
|
2252
|
+
}
|
|
2253
|
+
return `Task #${issueNumber} accepted.
|
|
2254
|
+
PR #${result.prNumber} merged \u2192 ${targetBranch}${pushToMain ? ` \u2192 ${baseBranch}` : ""}
|
|
2255
|
+
Issue closed.`;
|
|
2149
2256
|
}
|
|
2150
2257
|
async function execute10(input3, config) {
|
|
2151
2258
|
const issueNumber = input3["issue_number"];
|
|
@@ -2159,7 +2266,8 @@ async function execute10(input3, config) {
|
|
|
2159
2266
|
const targetBranch = makeWorkerBranchName(me);
|
|
2160
2267
|
const spinner = ora9(`Merging PR for #${issueNumber}\u2026`).start();
|
|
2161
2268
|
try {
|
|
2162
|
-
const
|
|
2269
|
+
const assigneeWorkerBranch = issue.assignee ? makeWorkerBranchName(issue.assignee) : void 0;
|
|
2270
|
+
const result = await acceptTask(config, issueNumber, assigneeWorkerBranch);
|
|
2163
2271
|
spinner.stop();
|
|
2164
2272
|
return `Task #${issueNumber} accepted.
|
|
2165
2273
|
PR #${result.prNumber} merged \u2192 ${targetBranch}
|
|
@@ -2921,6 +3029,8 @@ function completer(line) {
|
|
|
2921
3029
|
var _rl = null;
|
|
2922
3030
|
function promptUser() {
|
|
2923
3031
|
return new Promise((resolve) => {
|
|
3032
|
+
if (process.stdin.isPaused()) process.stdin.resume();
|
|
3033
|
+
_rl.resume();
|
|
2924
3034
|
_rl.question(chalk14.cyan("You") + chalk14.dim(" \u203A "), resolve);
|
|
2925
3035
|
});
|
|
2926
3036
|
}
|
package/dist/mcp.js
CHANGED
|
@@ -18,7 +18,9 @@ __export(github_exports, {
|
|
|
18
18
|
createPR: () => createPR,
|
|
19
19
|
createTask: () => createTask,
|
|
20
20
|
editTask: () => editTask,
|
|
21
|
+
embedBaseCommit: () => embedBaseCommit,
|
|
21
22
|
ensureLabels: () => ensureLabels,
|
|
23
|
+
extractBaseCommit: () => extractBaseCommit,
|
|
22
24
|
formatGuideAsMarkdown: () => formatGuideAsMarkdown,
|
|
23
25
|
getAuthenticatedUser: () => getAuthenticatedUser,
|
|
24
26
|
getDefaultBranch: () => getDefaultBranch,
|
|
@@ -29,6 +31,7 @@ __export(github_exports, {
|
|
|
29
31
|
listTasks: () => listTasks,
|
|
30
32
|
listTasksForReview: () => listTasksForReview,
|
|
31
33
|
markInReview: () => markInReview,
|
|
34
|
+
mergeWorkerIntoBase: () => mergeWorkerIntoBase,
|
|
32
35
|
postComment: () => postComment,
|
|
33
36
|
postGuideComment: () => postGuideComment,
|
|
34
37
|
rejectTask: () => rejectTask
|
|
@@ -77,19 +80,41 @@ async function getTask(config, number) {
|
|
|
77
80
|
const { data } = await octokit.issues.get({ owner, repo, issue_number: number });
|
|
78
81
|
return parseIssue(data);
|
|
79
82
|
}
|
|
80
|
-
|
|
83
|
+
function embedBaseCommit(body, sha) {
|
|
84
|
+
return `${body}
|
|
85
|
+
|
|
86
|
+
${BASE_COMMIT_MARKER}${sha} -->`;
|
|
87
|
+
}
|
|
88
|
+
function extractBaseCommit(body) {
|
|
89
|
+
if (!body) return null;
|
|
90
|
+
const match = body.match(/<!-- techunter-base:([a-f0-9]{7,40}) -->/);
|
|
91
|
+
return match?.[1] ?? null;
|
|
92
|
+
}
|
|
93
|
+
async function createTask(config, title, body, baseCommit) {
|
|
81
94
|
const octokit = createOctokit(config.githubToken);
|
|
82
95
|
const { owner, repo } = config.github;
|
|
83
96
|
await ensureLabels(config);
|
|
97
|
+
const finalBody = baseCommit ? embedBaseCommit(body ?? "", baseCommit) : body;
|
|
84
98
|
const { data } = await octokit.issues.create({
|
|
85
99
|
owner,
|
|
86
100
|
repo,
|
|
87
101
|
title,
|
|
88
|
-
body,
|
|
102
|
+
body: finalBody,
|
|
89
103
|
labels: [LABEL_AVAILABLE]
|
|
90
104
|
});
|
|
91
105
|
return parseIssue(data);
|
|
92
106
|
}
|
|
107
|
+
async function mergeWorkerIntoBase(config, workerBranch, baseBranch) {
|
|
108
|
+
const octokit = createOctokit(config.githubToken);
|
|
109
|
+
const { owner, repo } = config.github;
|
|
110
|
+
await octokit.repos.merge({
|
|
111
|
+
owner,
|
|
112
|
+
repo,
|
|
113
|
+
base: baseBranch,
|
|
114
|
+
head: workerBranch,
|
|
115
|
+
commit_message: `chore: merge ${workerBranch} into ${baseBranch}`
|
|
116
|
+
});
|
|
117
|
+
}
|
|
93
118
|
async function claimTask(config, number, username) {
|
|
94
119
|
const octokit = createOctokit(config.githubToken);
|
|
95
120
|
const { owner, repo } = config.github;
|
|
@@ -308,12 +333,12 @@ async function getDefaultBranch(config) {
|
|
|
308
333
|
const { data } = await octokit.repos.get({ owner, repo });
|
|
309
334
|
return data.default_branch;
|
|
310
335
|
}
|
|
311
|
-
async function acceptTask(config, issueNumber) {
|
|
336
|
+
async function acceptTask(config, issueNumber, headBranch) {
|
|
312
337
|
const octokit = createOctokit(config.githubToken);
|
|
313
338
|
const { owner, repo } = config.github;
|
|
314
339
|
const { data: prs } = await octokit.pulls.list({ owner, repo, state: "open", per_page: 100 });
|
|
315
|
-
const pr = prs.find((p) => p.head.ref.startsWith(`task-${issueNumber}-`));
|
|
316
|
-
if (!pr) throw new Error(`No open PR found for task #${issueNumber}
|
|
340
|
+
const pr = headBranch ? prs.find((p) => p.head.ref === headBranch) : prs.find((p) => p.head.ref.startsWith(`task-${issueNumber}-`) || p.head.ref.startsWith("worker-"));
|
|
341
|
+
if (!pr) throw new Error(`No open PR found for task #${issueNumber}`);
|
|
317
342
|
const { data: merge } = await octokit.pulls.merge({
|
|
318
343
|
owner,
|
|
319
344
|
repo,
|
|
@@ -323,7 +348,7 @@ async function acceptTask(config, issueNumber) {
|
|
|
323
348
|
await closeTask(config, issueNumber);
|
|
324
349
|
return { prNumber: pr.number, prUrl: pr.html_url, sha: merge.sha ?? "" };
|
|
325
350
|
}
|
|
326
|
-
var LABEL_AVAILABLE, LABEL_CLAIMED, LABEL_IN_REVIEW, LABEL_CHANGES_NEEDED, LABELS, TECHUNTER_LABELS;
|
|
351
|
+
var LABEL_AVAILABLE, LABEL_CLAIMED, LABEL_IN_REVIEW, LABEL_CHANGES_NEEDED, LABELS, TECHUNTER_LABELS, BASE_COMMIT_MARKER;
|
|
327
352
|
var init_github = __esm({
|
|
328
353
|
"src/lib/github.ts"() {
|
|
329
354
|
"use strict";
|
|
@@ -338,6 +363,7 @@ var init_github = __esm({
|
|
|
338
363
|
{ name: LABEL_CHANGES_NEEDED, color: "e11d48", description: "Task needs changes" }
|
|
339
364
|
];
|
|
340
365
|
TECHUNTER_LABELS = /* @__PURE__ */ new Set([LABEL_AVAILABLE, LABEL_CLAIMED, LABEL_IN_REVIEW, LABEL_CHANGES_NEEDED]);
|
|
366
|
+
BASE_COMMIT_MARKER = "<!-- techunter-base:";
|
|
341
367
|
}
|
|
342
368
|
});
|
|
343
369
|
|
|
@@ -370,10 +396,6 @@ async function getCurrentBranch() {
|
|
|
370
396
|
async function pushBranch(name) {
|
|
371
397
|
await git.push("origin", name, ["--set-upstream"]);
|
|
372
398
|
}
|
|
373
|
-
function makeBranchName(issueNumber, username) {
|
|
374
|
-
const slug = username.toLowerCase().replace(/[^a-z0-9-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "") || "user";
|
|
375
|
-
return `task-${issueNumber}-${slug}`;
|
|
376
|
-
}
|
|
377
399
|
function makeWorkerBranchName(username) {
|
|
378
400
|
const slug = username.toLowerCase().replace(/[^a-z0-9-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "") || "user";
|
|
379
401
|
return `worker-${slug}`;
|
|
@@ -486,6 +508,31 @@ async function stageAllAndCommit(message) {
|
|
|
486
508
|
const branch = (await git.branch()).current;
|
|
487
509
|
await git.push("origin", branch, ["--set-upstream"]);
|
|
488
510
|
}
|
|
511
|
+
async function syncWithBase(baseBranch) {
|
|
512
|
+
await git.fetch("origin", baseBranch);
|
|
513
|
+
try {
|
|
514
|
+
await git.merge([`origin/${baseBranch}`, "--ff-only"]);
|
|
515
|
+
} catch {
|
|
516
|
+
await git.merge([`origin/${baseBranch}`, "-m", `chore: sync with ${baseBranch}`]);
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
async function getRemoteHeadSha(baseBranch) {
|
|
520
|
+
await git.fetch("origin", baseBranch);
|
|
521
|
+
return (await git.revparse([`origin/${baseBranch}`])).trim();
|
|
522
|
+
}
|
|
523
|
+
async function resetOrCreateBranch(branchName, sha) {
|
|
524
|
+
const branches = await git.branch();
|
|
525
|
+
const localExists = Object.keys(branches.branches).some((b) => b === branchName);
|
|
526
|
+
if (localExists) {
|
|
527
|
+
await git.checkout(branchName);
|
|
528
|
+
await git.reset(["--hard", sha]);
|
|
529
|
+
} else {
|
|
530
|
+
await git.checkoutBranch(branchName, sha);
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
// src/tools/pick/index.ts
|
|
535
|
+
init_github();
|
|
489
536
|
|
|
490
537
|
// src/lib/config.ts
|
|
491
538
|
import Conf from "conf";
|
|
@@ -496,6 +543,7 @@ var configSchema = z.object({
|
|
|
496
543
|
aiModel: z.string().optional(),
|
|
497
544
|
githubToken: z.string().min(1),
|
|
498
545
|
githubClientId: z.string().optional(),
|
|
546
|
+
baseBranch: z.string().optional(),
|
|
499
547
|
github: z.object({
|
|
500
548
|
owner: z.string().min(1),
|
|
501
549
|
repo: z.string().min(1)
|
|
@@ -540,6 +588,9 @@ function setConfig(partial) {
|
|
|
540
588
|
if (partial.githubClientId !== void 0) {
|
|
541
589
|
current["githubClientId"] = partial.githubClientId;
|
|
542
590
|
}
|
|
591
|
+
if (partial.baseBranch !== void 0) {
|
|
592
|
+
current["baseBranch"] = partial.baseBranch;
|
|
593
|
+
}
|
|
543
594
|
if (partial.taskState !== void 0) {
|
|
544
595
|
current["taskState"] = {
|
|
545
596
|
...current["taskState"] ?? {},
|
|
@@ -646,7 +697,8 @@ async function launchClaudeCode(issue, branch) {
|
|
|
646
697
|
const prompt = buildClaudePrompt(issue, branch);
|
|
647
698
|
console.log(chalk3.dim("\n Launching Claude Code\u2026\n"));
|
|
648
699
|
await new Promise((resolve) => {
|
|
649
|
-
const
|
|
700
|
+
const safePrompt = prompt.replace(/\r?\n/g, " ").replace(/"/g, "'");
|
|
701
|
+
const child = spawn(`claude "${safePrompt}"`, [], { stdio: "inherit", shell: true });
|
|
650
702
|
child.on("close", () => resolve());
|
|
651
703
|
child.on("error", () => {
|
|
652
704
|
console.log(
|
|
@@ -858,8 +910,16 @@ async function run(_input, config) {
|
|
|
858
910
|
return `Commit failed: ${err.message}`;
|
|
859
911
|
}
|
|
860
912
|
if (isSelfSubmit) {
|
|
913
|
+
spinner = ora("Closing issue\u2026").start();
|
|
914
|
+
try {
|
|
915
|
+
await closeTask(config, issueNumber);
|
|
916
|
+
spinner.stop();
|
|
917
|
+
} catch (err) {
|
|
918
|
+
spinner.stop();
|
|
919
|
+
console.error(chalk5.yellow(`Warning: failed to close issue: ${err.message}`));
|
|
920
|
+
}
|
|
861
921
|
setConfig({ taskState: { activeIssueNumber: void 0, baseCommit: void 0 } });
|
|
862
|
-
return `Task #${issueNumber} committed.
|
|
922
|
+
return `Task #${issueNumber} committed and closed.
|
|
863
923
|
Commit: "${commitMessage.trim()}"`;
|
|
864
924
|
}
|
|
865
925
|
spinner = ora("Creating pull request\u2026").start();
|
|
@@ -920,8 +980,12 @@ async function execute(input, config) {
|
|
|
920
980
|
return `Commit failed: ${err.message}`;
|
|
921
981
|
}
|
|
922
982
|
if (isSelfSubmit) {
|
|
983
|
+
try {
|
|
984
|
+
await closeTask(config, issueNumber);
|
|
985
|
+
} catch {
|
|
986
|
+
}
|
|
923
987
|
setConfig({ taskState: { activeIssueNumber: void 0, baseCommit: void 0 } });
|
|
924
|
-
return `Task #${issueNumber} committed.
|
|
988
|
+
return `Task #${issueNumber} committed and closed.
|
|
925
989
|
Commit: "${commitMessage}"`;
|
|
926
990
|
}
|
|
927
991
|
let prUrl;
|
|
@@ -1129,41 +1193,29 @@ Finish or submit it before claiming a new one.`;
|
|
|
1129
1193
|
await claimTask(config, issue.number, me);
|
|
1130
1194
|
spinner.stop();
|
|
1131
1195
|
const workerBranch = makeWorkerBranchName(me);
|
|
1132
|
-
|
|
1196
|
+
const taskBase = extractBaseCommit(issue.body);
|
|
1197
|
+
spinner = ora3(`Switching to ${workerBranch}${taskBase ? ` from ${taskBase.slice(0, 7)}` : ""}\u2026`).start();
|
|
1133
1198
|
try {
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
try {
|
|
1139
|
-
await pushBranch(workerBranch);
|
|
1140
|
-
spinner.stop();
|
|
1141
|
-
} catch {
|
|
1142
|
-
spinner.warn("Could not push worker branch");
|
|
1143
|
-
}
|
|
1199
|
+
if (taskBase) {
|
|
1200
|
+
await resetOrCreateBranch(workerBranch, taskBase);
|
|
1201
|
+
} else {
|
|
1202
|
+
await switchToBranchOrCreate(workerBranch);
|
|
1144
1203
|
}
|
|
1145
|
-
} catch {
|
|
1146
|
-
spinner.warn(`Could not switch to ${workerBranch}`);
|
|
1147
|
-
}
|
|
1148
|
-
const branch = makeBranchName(issue.number, me);
|
|
1149
|
-
spinner = ora3(`Creating task branch ${branch}\u2026`).start();
|
|
1150
|
-
try {
|
|
1151
|
-
await switchToBranchOrCreate(branch);
|
|
1152
1204
|
spinner.stop();
|
|
1153
|
-
spinner = ora3("Pushing
|
|
1205
|
+
spinner = ora3("Pushing worker branch\u2026").start();
|
|
1154
1206
|
try {
|
|
1155
|
-
await pushBranch(
|
|
1207
|
+
await pushBranch(workerBranch);
|
|
1156
1208
|
spinner.stop();
|
|
1157
1209
|
} catch {
|
|
1158
|
-
spinner.warn("Could not push
|
|
1210
|
+
spinner.warn("Could not push worker branch");
|
|
1159
1211
|
}
|
|
1160
1212
|
} catch {
|
|
1161
|
-
spinner.warn(`Could not
|
|
1213
|
+
spinner.warn(`Could not switch to ${workerBranch}`);
|
|
1162
1214
|
}
|
|
1163
1215
|
const baseCommit = await getCurrentCommit();
|
|
1164
1216
|
setConfig({ taskState: { activeIssueNumber: issue.number, baseCommit } });
|
|
1165
1217
|
console.log(chalk6.green(`
|
|
1166
|
-
Claimed! Branch: ${
|
|
1218
|
+
Claimed! Branch: ${workerBranch} (base: ${baseCommit.slice(0, 7)})
|
|
1167
1219
|
`));
|
|
1168
1220
|
let openClaude;
|
|
1169
1221
|
try {
|
|
@@ -1177,8 +1229,8 @@ Finish or submit it before claiming a new one.`;
|
|
|
1177
1229
|
} catch {
|
|
1178
1230
|
openClaude = false;
|
|
1179
1231
|
}
|
|
1180
|
-
if (openClaude) await launchClaudeCode(issue,
|
|
1181
|
-
return `Task #${issue.number} claimed. Branch: ${
|
|
1232
|
+
if (openClaude) await launchClaudeCode(issue, workerBranch);
|
|
1233
|
+
return `Task #${issue.number} claimed. Branch: ${workerBranch}`;
|
|
1182
1234
|
} catch (err) {
|
|
1183
1235
|
return `Error claiming task: ${err.message}`;
|
|
1184
1236
|
}
|
|
@@ -1217,28 +1269,22 @@ async function execute3(input, config) {
|
|
|
1217
1269
|
return `Error claiming task: ${err.message}`;
|
|
1218
1270
|
}
|
|
1219
1271
|
const workerBranch = makeWorkerBranchName(me);
|
|
1272
|
+
const taskBase = extractBaseCommit(issue.body);
|
|
1220
1273
|
try {
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
} catch {
|
|
1226
|
-
}
|
|
1274
|
+
if (taskBase) {
|
|
1275
|
+
await resetOrCreateBranch(workerBranch, taskBase);
|
|
1276
|
+
} else {
|
|
1277
|
+
await switchToBranchOrCreate(workerBranch);
|
|
1227
1278
|
}
|
|
1228
1279
|
} catch {
|
|
1229
1280
|
}
|
|
1230
|
-
const branch = makeBranchName(issueNumber, me);
|
|
1231
|
-
try {
|
|
1232
|
-
await switchToBranchOrCreate(branch);
|
|
1233
|
-
} catch {
|
|
1234
|
-
}
|
|
1235
1281
|
try {
|
|
1236
|
-
await pushBranch(
|
|
1282
|
+
await pushBranch(workerBranch);
|
|
1237
1283
|
} catch {
|
|
1238
1284
|
}
|
|
1239
1285
|
const baseCommit = await getCurrentCommit();
|
|
1240
1286
|
setConfig({ taskState: { activeIssueNumber: issueNumber, baseCommit } });
|
|
1241
|
-
return `Task #${issueNumber} claimed. Branch: ${
|
|
1287
|
+
return `Task #${issueNumber} claimed. Branch: ${workerBranch} (base commit: ${baseCommit.slice(0, 7)})`;
|
|
1242
1288
|
}
|
|
1243
1289
|
return `Unknown action: ${action}`;
|
|
1244
1290
|
}
|
|
@@ -1408,12 +1454,26 @@ async function run4(input, config) {
|
|
|
1408
1454
|
console.log(chalk7.yellow(` Revision error: ${err.message}`));
|
|
1409
1455
|
}
|
|
1410
1456
|
}
|
|
1457
|
+
const baseBranch = config.baseBranch ?? "main";
|
|
1458
|
+
let baseCommit;
|
|
1459
|
+
const syncSpinner = ora4(`Syncing with ${baseBranch}\u2026`).start();
|
|
1460
|
+
try {
|
|
1461
|
+
await syncWithBase(baseBranch);
|
|
1462
|
+
baseCommit = await getCurrentCommit();
|
|
1463
|
+
syncSpinner.succeed(`Synced with ${baseBranch} (base: ${baseCommit.slice(0, 7)})`);
|
|
1464
|
+
} catch {
|
|
1465
|
+
syncSpinner.warn(`Could not sync with ${baseBranch} \u2014 recording remote HEAD as base`);
|
|
1466
|
+
try {
|
|
1467
|
+
baseCommit = await getRemoteHeadSha(baseBranch);
|
|
1468
|
+
} catch {
|
|
1469
|
+
}
|
|
1470
|
+
}
|
|
1411
1471
|
const createSpinner = ora4(`Creating "${title}"\u2026`).start();
|
|
1412
1472
|
let htmlUrl;
|
|
1413
1473
|
let issueNumber;
|
|
1414
1474
|
let issueTitle;
|
|
1415
1475
|
try {
|
|
1416
|
-
const issue = await createTask(config, title, guide);
|
|
1476
|
+
const issue = await createTask(config, title, guide, baseCommit);
|
|
1417
1477
|
createSpinner.stop();
|
|
1418
1478
|
htmlUrl = issue.htmlUrl;
|
|
1419
1479
|
issueNumber = issue.number;
|
|
@@ -1450,8 +1510,19 @@ async function execute4(input, config) {
|
|
|
1450
1510
|
if (feedback) {
|
|
1451
1511
|
guide = await generateGuide(config, title, { feedback, previousGuide: guide });
|
|
1452
1512
|
}
|
|
1513
|
+
const baseBranch = config.baseBranch ?? "main";
|
|
1514
|
+
let baseCommit;
|
|
1515
|
+
try {
|
|
1516
|
+
await syncWithBase(baseBranch);
|
|
1517
|
+
baseCommit = await getCurrentCommit();
|
|
1518
|
+
} catch {
|
|
1519
|
+
try {
|
|
1520
|
+
baseCommit = await getRemoteHeadSha(baseBranch);
|
|
1521
|
+
} catch {
|
|
1522
|
+
}
|
|
1523
|
+
}
|
|
1453
1524
|
try {
|
|
1454
|
-
const issue = await createTask(config, title, guide);
|
|
1525
|
+
const issue = await createTask(config, title, guide, baseCommit);
|
|
1455
1526
|
return `Created #${issue.number} "${issue.title}" \u2014 ${issue.htmlUrl}
|
|
1456
1527
|
|
|
1457
1528
|
Guide:
|
|
@@ -1852,16 +1923,40 @@ async function run10(input, config) {
|
|
|
1852
1923
|
}
|
|
1853
1924
|
if (!confirmed) return "Cancelled.";
|
|
1854
1925
|
const spinner = ora8(`Merging PR for #${issueNumber}\u2026`).start();
|
|
1926
|
+
let result;
|
|
1855
1927
|
try {
|
|
1856
|
-
const
|
|
1928
|
+
const assigneeWorkerBranch = issue.assignee ? makeWorkerBranchName(issue.assignee) : void 0;
|
|
1929
|
+
result = await acceptTask(config, issueNumber, assigneeWorkerBranch);
|
|
1857
1930
|
spinner.succeed(`PR #${result.prNumber} merged into ${targetBranch}`);
|
|
1858
|
-
return `Task #${issueNumber} accepted.
|
|
1859
|
-
PR #${result.prNumber} merged \u2192 ${targetBranch}
|
|
1860
|
-
Issue closed.`;
|
|
1861
1931
|
} catch (err) {
|
|
1862
1932
|
spinner.fail("Failed");
|
|
1863
1933
|
return `Error: ${err.message}`;
|
|
1864
1934
|
}
|
|
1935
|
+
const baseBranch = config.baseBranch ?? "main";
|
|
1936
|
+
let pushToMain;
|
|
1937
|
+
try {
|
|
1938
|
+
pushToMain = await select6({
|
|
1939
|
+
message: `Push ${chalk9.cyan(targetBranch)} \u2192 ${chalk9.cyan(baseBranch)}?`,
|
|
1940
|
+
choices: [
|
|
1941
|
+
{ name: `Yes, push to ${baseBranch}`, value: true },
|
|
1942
|
+
{ name: "No, keep in worker branch", value: false }
|
|
1943
|
+
]
|
|
1944
|
+
});
|
|
1945
|
+
} catch {
|
|
1946
|
+
pushToMain = false;
|
|
1947
|
+
}
|
|
1948
|
+
if (pushToMain) {
|
|
1949
|
+
const mergeSpinner = ora8(`Merging ${targetBranch} \u2192 ${baseBranch}\u2026`).start();
|
|
1950
|
+
try {
|
|
1951
|
+
await mergeWorkerIntoBase(config, targetBranch, baseBranch);
|
|
1952
|
+
mergeSpinner.succeed(`Merged ${targetBranch} \u2192 ${baseBranch}`);
|
|
1953
|
+
} catch (err) {
|
|
1954
|
+
mergeSpinner.fail(`Could not merge to ${baseBranch}: ${err.message}`);
|
|
1955
|
+
}
|
|
1956
|
+
}
|
|
1957
|
+
return `Task #${issueNumber} accepted.
|
|
1958
|
+
PR #${result.prNumber} merged \u2192 ${targetBranch}${pushToMain ? ` \u2192 ${baseBranch}` : ""}
|
|
1959
|
+
Issue closed.`;
|
|
1865
1960
|
}
|
|
1866
1961
|
async function execute10(input, config) {
|
|
1867
1962
|
const issueNumber = input["issue_number"];
|
|
@@ -1875,7 +1970,8 @@ async function execute10(input, config) {
|
|
|
1875
1970
|
const targetBranch = makeWorkerBranchName(me);
|
|
1876
1971
|
const spinner = ora8(`Merging PR for #${issueNumber}\u2026`).start();
|
|
1877
1972
|
try {
|
|
1878
|
-
const
|
|
1973
|
+
const assigneeWorkerBranch = issue.assignee ? makeWorkerBranchName(issue.assignee) : void 0;
|
|
1974
|
+
const result = await acceptTask(config, issueNumber, assigneeWorkerBranch);
|
|
1879
1975
|
spinner.stop();
|
|
1880
1976
|
return `Task #${issueNumber} accepted.
|
|
1881
1977
|
PR #${result.prNumber} merged \u2192 ${targetBranch}
|