techunter 0.1.6 → 0.1.8
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/index.js +161 -53
- package/dist/mcp.js +140 -56
- package/package.json +2 -1
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,9 +363,19 @@ 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
|
+
// src/lib/proxy.ts
|
|
371
|
+
import { ProxyAgent, setGlobalDispatcher } from "undici";
|
|
372
|
+
function setupProxy() {
|
|
373
|
+
const proxy = process.env.HTTPS_PROXY ?? process.env.https_proxy ?? process.env.HTTP_PROXY ?? process.env.http_proxy ?? process.env.ALL_PROXY ?? process.env.all_proxy;
|
|
374
|
+
if (proxy) {
|
|
375
|
+
setGlobalDispatcher(new ProxyAgent(proxy));
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
|
|
344
379
|
// src/index.ts
|
|
345
380
|
import chalk14 from "chalk";
|
|
346
381
|
import readline from "readline";
|
|
@@ -362,6 +397,7 @@ var configSchema = z.object({
|
|
|
362
397
|
aiModel: z.string().optional(),
|
|
363
398
|
githubToken: z.string().min(1),
|
|
364
399
|
githubClientId: z.string().optional(),
|
|
400
|
+
baseBranch: z.string().optional(),
|
|
365
401
|
github: z.object({
|
|
366
402
|
owner: z.string().min(1),
|
|
367
403
|
repo: z.string().min(1)
|
|
@@ -406,6 +442,9 @@ function setConfig(partial) {
|
|
|
406
442
|
if (partial.githubClientId !== void 0) {
|
|
407
443
|
current["githubClientId"] = partial.githubClientId;
|
|
408
444
|
}
|
|
445
|
+
if (partial.baseBranch !== void 0) {
|
|
446
|
+
current["baseBranch"] = partial.baseBranch;
|
|
447
|
+
}
|
|
409
448
|
if (partial.taskState !== void 0) {
|
|
410
449
|
current["taskState"] = {
|
|
411
450
|
...current["taskState"] ?? {},
|
|
@@ -568,6 +607,28 @@ async function stageAllAndCommit(message) {
|
|
|
568
607
|
const branch = (await git.branch()).current;
|
|
569
608
|
await git.push("origin", branch, ["--set-upstream"]);
|
|
570
609
|
}
|
|
610
|
+
async function syncWithBase(baseBranch) {
|
|
611
|
+
await git.fetch("origin", baseBranch);
|
|
612
|
+
try {
|
|
613
|
+
await git.merge([`origin/${baseBranch}`, "--ff-only"]);
|
|
614
|
+
} catch {
|
|
615
|
+
await git.merge([`origin/${baseBranch}`, "-m", `chore: sync with ${baseBranch}`]);
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
async function getRemoteHeadSha(baseBranch) {
|
|
619
|
+
await git.fetch("origin", baseBranch);
|
|
620
|
+
return (await git.revparse([`origin/${baseBranch}`])).trim();
|
|
621
|
+
}
|
|
622
|
+
async function resetOrCreateBranch(branchName, sha) {
|
|
623
|
+
const branches = await git.branch();
|
|
624
|
+
const localExists = Object.keys(branches.branches).some((b) => b === branchName);
|
|
625
|
+
if (localExists) {
|
|
626
|
+
await git.checkout(branchName);
|
|
627
|
+
await git.reset(["--hard", sha]);
|
|
628
|
+
} else {
|
|
629
|
+
await git.checkoutBranch(branchName, sha);
|
|
630
|
+
}
|
|
631
|
+
}
|
|
571
632
|
|
|
572
633
|
// src/lib/client.ts
|
|
573
634
|
import OpenAI from "openai";
|
|
@@ -736,10 +797,12 @@ async function configCommand() {
|
|
|
736
797
|
`));
|
|
737
798
|
const currentBaseUrl = config.aiBaseUrl ?? DEFAULT_BASE_URL;
|
|
738
799
|
const currentModel = config.aiModel ?? DEFAULT_MODEL;
|
|
800
|
+
const currentBaseBranch = config.baseBranch ?? "main";
|
|
739
801
|
const field = await select2({
|
|
740
802
|
message: "Which setting to change?",
|
|
741
803
|
choices: [
|
|
742
804
|
{ name: `GitHub repo ${chalk3.dim(`${config.github.owner}/${config.github.repo}`)}`, value: "repo" },
|
|
805
|
+
{ name: `Base branch ${chalk3.dim(currentBaseBranch)}`, value: "baseBranch" },
|
|
743
806
|
{ name: `AI base URL ${chalk3.dim(currentBaseUrl)}`, value: "aiBaseUrl" },
|
|
744
807
|
{ name: `AI model ${chalk3.dim(currentModel)}`, value: "aiModel" },
|
|
745
808
|
{ name: `AI API Key ${chalk3.dim("(hidden)")}`, value: "aiApiKey" },
|
|
@@ -748,7 +811,15 @@ async function configCommand() {
|
|
|
748
811
|
]
|
|
749
812
|
});
|
|
750
813
|
if (field === "cancel") return;
|
|
751
|
-
if (field === "
|
|
814
|
+
if (field === "baseBranch") {
|
|
815
|
+
const val = await input2({ message: "Base branch name:", default: currentBaseBranch });
|
|
816
|
+
if (val.trim()) {
|
|
817
|
+
setConfig({ baseBranch: val.trim() });
|
|
818
|
+
console.log(chalk3.green(`
|
|
819
|
+
Base branch set to: ${val.trim()}
|
|
820
|
+
`));
|
|
821
|
+
}
|
|
822
|
+
} else if (field === "repo") {
|
|
752
823
|
const owner = await input2({ message: "GitHub repo owner:", default: config.github.owner });
|
|
753
824
|
const repo = await input2({ message: "GitHub repo name:", default: config.github.repo });
|
|
754
825
|
setConfig({ github: { ...config.github, owner: owner.trim(), repo: repo.trim() } });
|
|
@@ -805,6 +876,7 @@ init_github();
|
|
|
805
876
|
import chalk8 from "chalk";
|
|
806
877
|
import ora4 from "ora";
|
|
807
878
|
import { select as select5 } from "@inquirer/prompts";
|
|
879
|
+
init_github();
|
|
808
880
|
|
|
809
881
|
// src/lib/markdown.ts
|
|
810
882
|
import { marked } from "marked";
|
|
@@ -944,7 +1016,8 @@ async function launchClaudeCode(issue, branch) {
|
|
|
944
1016
|
const prompt = buildClaudePrompt(issue, branch);
|
|
945
1017
|
console.log(chalk5.dim("\n Launching Claude Code\u2026\n"));
|
|
946
1018
|
await new Promise((resolve) => {
|
|
947
|
-
const
|
|
1019
|
+
const safePrompt = prompt.replace(/\r?\n/g, " ").replace(/"/g, "'");
|
|
1020
|
+
const child = spawn(`claude "${safePrompt}"`, [], { stdio: "inherit", shell: true });
|
|
948
1021
|
child.on("close", () => resolve());
|
|
949
1022
|
child.on("error", () => {
|
|
950
1023
|
console.log(
|
|
@@ -1425,41 +1498,29 @@ Finish or submit it before claiming a new one.`;
|
|
|
1425
1498
|
await claimTask(config, issue.number, me);
|
|
1426
1499
|
spinner.stop();
|
|
1427
1500
|
const workerBranch = makeWorkerBranchName(me);
|
|
1428
|
-
|
|
1501
|
+
const taskBase = extractBaseCommit(issue.body);
|
|
1502
|
+
spinner = ora4(`Switching to ${workerBranch}${taskBase ? ` from ${taskBase.slice(0, 7)}` : ""}\u2026`).start();
|
|
1429
1503
|
try {
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
try {
|
|
1435
|
-
await pushBranch(workerBranch);
|
|
1436
|
-
spinner.stop();
|
|
1437
|
-
} catch {
|
|
1438
|
-
spinner.warn("Could not push worker branch");
|
|
1439
|
-
}
|
|
1504
|
+
if (taskBase) {
|
|
1505
|
+
await resetOrCreateBranch(workerBranch, taskBase);
|
|
1506
|
+
} else {
|
|
1507
|
+
await switchToBranchOrCreate(workerBranch);
|
|
1440
1508
|
}
|
|
1441
|
-
} catch {
|
|
1442
|
-
spinner.warn(`Could not switch to ${workerBranch}`);
|
|
1443
|
-
}
|
|
1444
|
-
const branch = makeBranchName(issue.number, me);
|
|
1445
|
-
spinner = ora4(`Creating task branch ${branch}\u2026`).start();
|
|
1446
|
-
try {
|
|
1447
|
-
await switchToBranchOrCreate(branch);
|
|
1448
1509
|
spinner.stop();
|
|
1449
|
-
spinner = ora4("Pushing
|
|
1510
|
+
spinner = ora4("Pushing worker branch\u2026").start();
|
|
1450
1511
|
try {
|
|
1451
|
-
await pushBranch(
|
|
1512
|
+
await pushBranch(workerBranch);
|
|
1452
1513
|
spinner.stop();
|
|
1453
1514
|
} catch {
|
|
1454
|
-
spinner.warn("Could not push
|
|
1515
|
+
spinner.warn("Could not push worker branch");
|
|
1455
1516
|
}
|
|
1456
1517
|
} catch {
|
|
1457
|
-
spinner.warn(`Could not
|
|
1518
|
+
spinner.warn(`Could not switch to ${workerBranch}`);
|
|
1458
1519
|
}
|
|
1459
1520
|
const baseCommit = await getCurrentCommit();
|
|
1460
1521
|
setConfig({ taskState: { activeIssueNumber: issue.number, baseCommit } });
|
|
1461
1522
|
console.log(chalk8.green(`
|
|
1462
|
-
Claimed! Branch: ${
|
|
1523
|
+
Claimed! Branch: ${workerBranch} (base: ${baseCommit.slice(0, 7)})
|
|
1463
1524
|
`));
|
|
1464
1525
|
let openClaude;
|
|
1465
1526
|
try {
|
|
@@ -1473,8 +1534,8 @@ Finish or submit it before claiming a new one.`;
|
|
|
1473
1534
|
} catch {
|
|
1474
1535
|
openClaude = false;
|
|
1475
1536
|
}
|
|
1476
|
-
if (openClaude) await launchClaudeCode(issue,
|
|
1477
|
-
return `Task #${issue.number} claimed. Branch: ${
|
|
1537
|
+
if (openClaude) await launchClaudeCode(issue, workerBranch);
|
|
1538
|
+
return `Task #${issue.number} claimed. Branch: ${workerBranch}`;
|
|
1478
1539
|
} catch (err) {
|
|
1479
1540
|
return `Error claiming task: ${err.message}`;
|
|
1480
1541
|
}
|
|
@@ -1513,28 +1574,22 @@ async function execute3(input3, config) {
|
|
|
1513
1574
|
return `Error claiming task: ${err.message}`;
|
|
1514
1575
|
}
|
|
1515
1576
|
const workerBranch = makeWorkerBranchName(me);
|
|
1577
|
+
const taskBase = extractBaseCommit(issue.body);
|
|
1516
1578
|
try {
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
} catch {
|
|
1522
|
-
}
|
|
1579
|
+
if (taskBase) {
|
|
1580
|
+
await resetOrCreateBranch(workerBranch, taskBase);
|
|
1581
|
+
} else {
|
|
1582
|
+
await switchToBranchOrCreate(workerBranch);
|
|
1523
1583
|
}
|
|
1524
1584
|
} catch {
|
|
1525
1585
|
}
|
|
1526
|
-
const branch = makeBranchName(issueNumber, me);
|
|
1527
|
-
try {
|
|
1528
|
-
await switchToBranchOrCreate(branch);
|
|
1529
|
-
} catch {
|
|
1530
|
-
}
|
|
1531
1586
|
try {
|
|
1532
|
-
await pushBranch(
|
|
1587
|
+
await pushBranch(workerBranch);
|
|
1533
1588
|
} catch {
|
|
1534
1589
|
}
|
|
1535
1590
|
const baseCommit = await getCurrentCommit();
|
|
1536
1591
|
setConfig({ taskState: { activeIssueNumber: issueNumber, baseCommit } });
|
|
1537
|
-
return `Task #${issueNumber} claimed. Branch: ${
|
|
1592
|
+
return `Task #${issueNumber} claimed. Branch: ${workerBranch} (base commit: ${baseCommit.slice(0, 7)})`;
|
|
1538
1593
|
}
|
|
1539
1594
|
return `Unknown action: ${action}`;
|
|
1540
1595
|
}
|
|
@@ -1704,12 +1759,26 @@ async function run4(input3, config) {
|
|
|
1704
1759
|
console.log(chalk9.yellow(` Revision error: ${err.message}`));
|
|
1705
1760
|
}
|
|
1706
1761
|
}
|
|
1762
|
+
const baseBranch = config.baseBranch ?? "main";
|
|
1763
|
+
let baseCommit;
|
|
1764
|
+
const syncSpinner = ora5(`Syncing with ${baseBranch}\u2026`).start();
|
|
1765
|
+
try {
|
|
1766
|
+
await syncWithBase(baseBranch);
|
|
1767
|
+
baseCommit = await getCurrentCommit();
|
|
1768
|
+
syncSpinner.succeed(`Synced with ${baseBranch} (base: ${baseCommit.slice(0, 7)})`);
|
|
1769
|
+
} catch {
|
|
1770
|
+
syncSpinner.warn(`Could not sync with ${baseBranch} \u2014 recording remote HEAD as base`);
|
|
1771
|
+
try {
|
|
1772
|
+
baseCommit = await getRemoteHeadSha(baseBranch);
|
|
1773
|
+
} catch {
|
|
1774
|
+
}
|
|
1775
|
+
}
|
|
1707
1776
|
const createSpinner = ora5(`Creating "${title}"\u2026`).start();
|
|
1708
1777
|
let htmlUrl;
|
|
1709
1778
|
let issueNumber;
|
|
1710
1779
|
let issueTitle;
|
|
1711
1780
|
try {
|
|
1712
|
-
const issue = await createTask(config, title, guide);
|
|
1781
|
+
const issue = await createTask(config, title, guide, baseCommit);
|
|
1713
1782
|
createSpinner.stop();
|
|
1714
1783
|
htmlUrl = issue.htmlUrl;
|
|
1715
1784
|
issueNumber = issue.number;
|
|
@@ -1746,8 +1815,19 @@ async function execute4(input3, config) {
|
|
|
1746
1815
|
if (feedback) {
|
|
1747
1816
|
guide = await generateGuide(config, title, { feedback, previousGuide: guide });
|
|
1748
1817
|
}
|
|
1818
|
+
const baseBranch = config.baseBranch ?? "main";
|
|
1819
|
+
let baseCommit;
|
|
1749
1820
|
try {
|
|
1750
|
-
|
|
1821
|
+
await syncWithBase(baseBranch);
|
|
1822
|
+
baseCommit = await getCurrentCommit();
|
|
1823
|
+
} catch {
|
|
1824
|
+
try {
|
|
1825
|
+
baseCommit = await getRemoteHeadSha(baseBranch);
|
|
1826
|
+
} catch {
|
|
1827
|
+
}
|
|
1828
|
+
}
|
|
1829
|
+
try {
|
|
1830
|
+
const issue = await createTask(config, title, guide, baseCommit);
|
|
1751
1831
|
return `Created #${issue.number} "${issue.title}" \u2014 ${issue.htmlUrl}
|
|
1752
1832
|
|
|
1753
1833
|
Guide:
|
|
@@ -2148,16 +2228,40 @@ async function run10(input3, config) {
|
|
|
2148
2228
|
}
|
|
2149
2229
|
if (!confirmed) return "Cancelled.";
|
|
2150
2230
|
const spinner = ora9(`Merging PR for #${issueNumber}\u2026`).start();
|
|
2231
|
+
let result;
|
|
2151
2232
|
try {
|
|
2152
|
-
const
|
|
2233
|
+
const assigneeWorkerBranch = issue.assignee ? makeWorkerBranchName(issue.assignee) : void 0;
|
|
2234
|
+
result = await acceptTask(config, issueNumber, assigneeWorkerBranch);
|
|
2153
2235
|
spinner.succeed(`PR #${result.prNumber} merged into ${targetBranch}`);
|
|
2154
|
-
return `Task #${issueNumber} accepted.
|
|
2155
|
-
PR #${result.prNumber} merged \u2192 ${targetBranch}
|
|
2156
|
-
Issue closed.`;
|
|
2157
2236
|
} catch (err) {
|
|
2158
2237
|
spinner.fail("Failed");
|
|
2159
2238
|
return `Error: ${err.message}`;
|
|
2160
2239
|
}
|
|
2240
|
+
const baseBranch = config.baseBranch ?? "main";
|
|
2241
|
+
let pushToMain;
|
|
2242
|
+
try {
|
|
2243
|
+
pushToMain = await select8({
|
|
2244
|
+
message: `Push ${chalk11.cyan(targetBranch)} \u2192 ${chalk11.cyan(baseBranch)}?`,
|
|
2245
|
+
choices: [
|
|
2246
|
+
{ name: `Yes, push to ${baseBranch}`, value: true },
|
|
2247
|
+
{ name: "No, keep in worker branch", value: false }
|
|
2248
|
+
]
|
|
2249
|
+
});
|
|
2250
|
+
} catch {
|
|
2251
|
+
pushToMain = false;
|
|
2252
|
+
}
|
|
2253
|
+
if (pushToMain) {
|
|
2254
|
+
const mergeSpinner = ora9(`Merging ${targetBranch} \u2192 ${baseBranch}\u2026`).start();
|
|
2255
|
+
try {
|
|
2256
|
+
await mergeWorkerIntoBase(config, targetBranch, baseBranch);
|
|
2257
|
+
mergeSpinner.succeed(`Merged ${targetBranch} \u2192 ${baseBranch}`);
|
|
2258
|
+
} catch (err) {
|
|
2259
|
+
mergeSpinner.fail(`Could not merge to ${baseBranch}: ${err.message}`);
|
|
2260
|
+
}
|
|
2261
|
+
}
|
|
2262
|
+
return `Task #${issueNumber} accepted.
|
|
2263
|
+
PR #${result.prNumber} merged \u2192 ${targetBranch}${pushToMain ? ` \u2192 ${baseBranch}` : ""}
|
|
2264
|
+
Issue closed.`;
|
|
2161
2265
|
}
|
|
2162
2266
|
async function execute10(input3, config) {
|
|
2163
2267
|
const issueNumber = input3["issue_number"];
|
|
@@ -2171,7 +2275,8 @@ async function execute10(input3, config) {
|
|
|
2171
2275
|
const targetBranch = makeWorkerBranchName(me);
|
|
2172
2276
|
const spinner = ora9(`Merging PR for #${issueNumber}\u2026`).start();
|
|
2173
2277
|
try {
|
|
2174
|
-
const
|
|
2278
|
+
const assigneeWorkerBranch = issue.assignee ? makeWorkerBranchName(issue.assignee) : void 0;
|
|
2279
|
+
const result = await acceptTask(config, issueNumber, assigneeWorkerBranch);
|
|
2175
2280
|
spinner.stop();
|
|
2176
2281
|
return `Task #${issueNumber} accepted.
|
|
2177
2282
|
PR #${result.prNumber} merged \u2192 ${targetBranch}
|
|
@@ -2894,6 +2999,7 @@ async function runAgentLoop(config, messages) {
|
|
|
2894
2999
|
}
|
|
2895
3000
|
|
|
2896
3001
|
// src/index.ts
|
|
3002
|
+
setupProxy();
|
|
2897
3003
|
var _require = createRequire(import.meta.url);
|
|
2898
3004
|
var { version } = _require("../package.json");
|
|
2899
3005
|
var SLASH_NAMES = [
|
|
@@ -2933,6 +3039,8 @@ function completer(line) {
|
|
|
2933
3039
|
var _rl = null;
|
|
2934
3040
|
function promptUser() {
|
|
2935
3041
|
return new Promise((resolve) => {
|
|
3042
|
+
if (process.stdin.isPaused()) process.stdin.resume();
|
|
3043
|
+
_rl.resume();
|
|
2936
3044
|
_rl.question(chalk14.cyan("You") + chalk14.dim(" \u203A "), resolve);
|
|
2937
3045
|
});
|
|
2938
3046
|
}
|
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(
|
|
@@ -1141,41 +1193,29 @@ Finish or submit it before claiming a new one.`;
|
|
|
1141
1193
|
await claimTask(config, issue.number, me);
|
|
1142
1194
|
spinner.stop();
|
|
1143
1195
|
const workerBranch = makeWorkerBranchName(me);
|
|
1144
|
-
|
|
1196
|
+
const taskBase = extractBaseCommit(issue.body);
|
|
1197
|
+
spinner = ora3(`Switching to ${workerBranch}${taskBase ? ` from ${taskBase.slice(0, 7)}` : ""}\u2026`).start();
|
|
1145
1198
|
try {
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
try {
|
|
1151
|
-
await pushBranch(workerBranch);
|
|
1152
|
-
spinner.stop();
|
|
1153
|
-
} catch {
|
|
1154
|
-
spinner.warn("Could not push worker branch");
|
|
1155
|
-
}
|
|
1199
|
+
if (taskBase) {
|
|
1200
|
+
await resetOrCreateBranch(workerBranch, taskBase);
|
|
1201
|
+
} else {
|
|
1202
|
+
await switchToBranchOrCreate(workerBranch);
|
|
1156
1203
|
}
|
|
1157
|
-
} catch {
|
|
1158
|
-
spinner.warn(`Could not switch to ${workerBranch}`);
|
|
1159
|
-
}
|
|
1160
|
-
const branch = makeBranchName(issue.number, me);
|
|
1161
|
-
spinner = ora3(`Creating task branch ${branch}\u2026`).start();
|
|
1162
|
-
try {
|
|
1163
|
-
await switchToBranchOrCreate(branch);
|
|
1164
1204
|
spinner.stop();
|
|
1165
|
-
spinner = ora3("Pushing
|
|
1205
|
+
spinner = ora3("Pushing worker branch\u2026").start();
|
|
1166
1206
|
try {
|
|
1167
|
-
await pushBranch(
|
|
1207
|
+
await pushBranch(workerBranch);
|
|
1168
1208
|
spinner.stop();
|
|
1169
1209
|
} catch {
|
|
1170
|
-
spinner.warn("Could not push
|
|
1210
|
+
spinner.warn("Could not push worker branch");
|
|
1171
1211
|
}
|
|
1172
1212
|
} catch {
|
|
1173
|
-
spinner.warn(`Could not
|
|
1213
|
+
spinner.warn(`Could not switch to ${workerBranch}`);
|
|
1174
1214
|
}
|
|
1175
1215
|
const baseCommit = await getCurrentCommit();
|
|
1176
1216
|
setConfig({ taskState: { activeIssueNumber: issue.number, baseCommit } });
|
|
1177
1217
|
console.log(chalk6.green(`
|
|
1178
|
-
Claimed! Branch: ${
|
|
1218
|
+
Claimed! Branch: ${workerBranch} (base: ${baseCommit.slice(0, 7)})
|
|
1179
1219
|
`));
|
|
1180
1220
|
let openClaude;
|
|
1181
1221
|
try {
|
|
@@ -1189,8 +1229,8 @@ Finish or submit it before claiming a new one.`;
|
|
|
1189
1229
|
} catch {
|
|
1190
1230
|
openClaude = false;
|
|
1191
1231
|
}
|
|
1192
|
-
if (openClaude) await launchClaudeCode(issue,
|
|
1193
|
-
return `Task #${issue.number} claimed. Branch: ${
|
|
1232
|
+
if (openClaude) await launchClaudeCode(issue, workerBranch);
|
|
1233
|
+
return `Task #${issue.number} claimed. Branch: ${workerBranch}`;
|
|
1194
1234
|
} catch (err) {
|
|
1195
1235
|
return `Error claiming task: ${err.message}`;
|
|
1196
1236
|
}
|
|
@@ -1229,28 +1269,22 @@ async function execute3(input, config) {
|
|
|
1229
1269
|
return `Error claiming task: ${err.message}`;
|
|
1230
1270
|
}
|
|
1231
1271
|
const workerBranch = makeWorkerBranchName(me);
|
|
1272
|
+
const taskBase = extractBaseCommit(issue.body);
|
|
1232
1273
|
try {
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
} catch {
|
|
1238
|
-
}
|
|
1274
|
+
if (taskBase) {
|
|
1275
|
+
await resetOrCreateBranch(workerBranch, taskBase);
|
|
1276
|
+
} else {
|
|
1277
|
+
await switchToBranchOrCreate(workerBranch);
|
|
1239
1278
|
}
|
|
1240
1279
|
} catch {
|
|
1241
1280
|
}
|
|
1242
|
-
const branch = makeBranchName(issueNumber, me);
|
|
1243
|
-
try {
|
|
1244
|
-
await switchToBranchOrCreate(branch);
|
|
1245
|
-
} catch {
|
|
1246
|
-
}
|
|
1247
1281
|
try {
|
|
1248
|
-
await pushBranch(
|
|
1282
|
+
await pushBranch(workerBranch);
|
|
1249
1283
|
} catch {
|
|
1250
1284
|
}
|
|
1251
1285
|
const baseCommit = await getCurrentCommit();
|
|
1252
1286
|
setConfig({ taskState: { activeIssueNumber: issueNumber, baseCommit } });
|
|
1253
|
-
return `Task #${issueNumber} claimed. Branch: ${
|
|
1287
|
+
return `Task #${issueNumber} claimed. Branch: ${workerBranch} (base commit: ${baseCommit.slice(0, 7)})`;
|
|
1254
1288
|
}
|
|
1255
1289
|
return `Unknown action: ${action}`;
|
|
1256
1290
|
}
|
|
@@ -1420,12 +1454,26 @@ async function run4(input, config) {
|
|
|
1420
1454
|
console.log(chalk7.yellow(` Revision error: ${err.message}`));
|
|
1421
1455
|
}
|
|
1422
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
|
+
}
|
|
1423
1471
|
const createSpinner = ora4(`Creating "${title}"\u2026`).start();
|
|
1424
1472
|
let htmlUrl;
|
|
1425
1473
|
let issueNumber;
|
|
1426
1474
|
let issueTitle;
|
|
1427
1475
|
try {
|
|
1428
|
-
const issue = await createTask(config, title, guide);
|
|
1476
|
+
const issue = await createTask(config, title, guide, baseCommit);
|
|
1429
1477
|
createSpinner.stop();
|
|
1430
1478
|
htmlUrl = issue.htmlUrl;
|
|
1431
1479
|
issueNumber = issue.number;
|
|
@@ -1462,8 +1510,19 @@ async function execute4(input, config) {
|
|
|
1462
1510
|
if (feedback) {
|
|
1463
1511
|
guide = await generateGuide(config, title, { feedback, previousGuide: guide });
|
|
1464
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
|
+
}
|
|
1465
1524
|
try {
|
|
1466
|
-
const issue = await createTask(config, title, guide);
|
|
1525
|
+
const issue = await createTask(config, title, guide, baseCommit);
|
|
1467
1526
|
return `Created #${issue.number} "${issue.title}" \u2014 ${issue.htmlUrl}
|
|
1468
1527
|
|
|
1469
1528
|
Guide:
|
|
@@ -1864,16 +1923,40 @@ async function run10(input, config) {
|
|
|
1864
1923
|
}
|
|
1865
1924
|
if (!confirmed) return "Cancelled.";
|
|
1866
1925
|
const spinner = ora8(`Merging PR for #${issueNumber}\u2026`).start();
|
|
1926
|
+
let result;
|
|
1867
1927
|
try {
|
|
1868
|
-
const
|
|
1928
|
+
const assigneeWorkerBranch = issue.assignee ? makeWorkerBranchName(issue.assignee) : void 0;
|
|
1929
|
+
result = await acceptTask(config, issueNumber, assigneeWorkerBranch);
|
|
1869
1930
|
spinner.succeed(`PR #${result.prNumber} merged into ${targetBranch}`);
|
|
1870
|
-
return `Task #${issueNumber} accepted.
|
|
1871
|
-
PR #${result.prNumber} merged \u2192 ${targetBranch}
|
|
1872
|
-
Issue closed.`;
|
|
1873
1931
|
} catch (err) {
|
|
1874
1932
|
spinner.fail("Failed");
|
|
1875
1933
|
return `Error: ${err.message}`;
|
|
1876
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.`;
|
|
1877
1960
|
}
|
|
1878
1961
|
async function execute10(input, config) {
|
|
1879
1962
|
const issueNumber = input["issue_number"];
|
|
@@ -1887,7 +1970,8 @@ async function execute10(input, config) {
|
|
|
1887
1970
|
const targetBranch = makeWorkerBranchName(me);
|
|
1888
1971
|
const spinner = ora8(`Merging PR for #${issueNumber}\u2026`).start();
|
|
1889
1972
|
try {
|
|
1890
|
-
const
|
|
1973
|
+
const assigneeWorkerBranch = issue.assignee ? makeWorkerBranchName(issue.assignee) : void 0;
|
|
1974
|
+
const result = await acceptTask(config, issueNumber, assigneeWorkerBranch);
|
|
1891
1975
|
spinner.stop();
|
|
1892
1976
|
return `Task #${issueNumber} accepted.
|
|
1893
1977
|
PR #${result.prNumber} merged \u2192 ${targetBranch}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "techunter",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.8",
|
|
4
4
|
"description": "AI-powered task distribution CLI for development teams",
|
|
5
5
|
"author": "Techunter Contributors",
|
|
6
6
|
"license": "MIT",
|
|
@@ -44,6 +44,7 @@
|
|
|
44
44
|
"openai": "^4.104.0",
|
|
45
45
|
"ora": "^8.1.1",
|
|
46
46
|
"simple-git": "^3.27.0",
|
|
47
|
+
"undici": "^7.23.0",
|
|
47
48
|
"zod": "^3.24.1"
|
|
48
49
|
},
|
|
49
50
|
"devDependencies": {
|