techunter 0.1.1 → 0.1.3
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 +23 -11
- package/dist/index.js +308 -159
- package/dist/mcp.js +299 -104
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -21,9 +21,9 @@ __export(github_exports, {
|
|
|
21
21
|
ensureLabels: () => ensureLabels,
|
|
22
22
|
formatGuideAsMarkdown: () => formatGuideAsMarkdown,
|
|
23
23
|
getAuthenticatedUser: () => getAuthenticatedUser,
|
|
24
|
-
getBaseBranch: () => getBaseBranch,
|
|
25
24
|
getDefaultBranch: () => getDefaultBranch,
|
|
26
25
|
getTask: () => getTask,
|
|
26
|
+
isCollaborator: () => isCollaborator,
|
|
27
27
|
listComments: () => listComments,
|
|
28
28
|
listMyTasks: () => listMyTasks,
|
|
29
29
|
listTasks: () => listTasks,
|
|
@@ -50,6 +50,7 @@ function parseIssue(issue) {
|
|
|
50
50
|
title: issue.title,
|
|
51
51
|
body: issue.body ?? null,
|
|
52
52
|
state: issue.state,
|
|
53
|
+
author: issue.user?.login ?? null,
|
|
53
54
|
assignee: issue.assignee?.login ?? null,
|
|
54
55
|
labels: (issue.labels ?? []).map(
|
|
55
56
|
(l) => typeof l === "string" ? l : l.name ?? ""
|
|
@@ -226,6 +227,16 @@ async function getAuthenticatedUser(config) {
|
|
|
226
227
|
const { data } = await octokit.users.getAuthenticated();
|
|
227
228
|
return data.login;
|
|
228
229
|
}
|
|
230
|
+
async function isCollaborator(config, username) {
|
|
231
|
+
const octokit = createOctokit(config.githubToken);
|
|
232
|
+
const { owner, repo } = config.github;
|
|
233
|
+
try {
|
|
234
|
+
const { data } = await octokit.repos.getCollaboratorPermissionLevel({ owner, repo, username });
|
|
235
|
+
return data.permission === "admin" || data.permission === "write" || data.permission === "maintain";
|
|
236
|
+
} catch {
|
|
237
|
+
return false;
|
|
238
|
+
}
|
|
239
|
+
}
|
|
229
240
|
async function listMyTasks(config, username) {
|
|
230
241
|
const octokit = createOctokit(config.githubToken);
|
|
231
242
|
const { owner, repo } = config.github;
|
|
@@ -297,10 +308,6 @@ async function getDefaultBranch(config) {
|
|
|
297
308
|
const { data } = await octokit.repos.get({ owner, repo });
|
|
298
309
|
return data.default_branch;
|
|
299
310
|
}
|
|
300
|
-
function getBaseBranch(config) {
|
|
301
|
-
if (config.github.baseBranch) return Promise.resolve(config.github.baseBranch);
|
|
302
|
-
return getDefaultBranch(config);
|
|
303
|
-
}
|
|
304
311
|
async function acceptTask(config, issueNumber) {
|
|
305
312
|
const octokit = createOctokit(config.githubToken);
|
|
306
313
|
const { owner, repo } = config.github;
|
|
@@ -338,7 +345,6 @@ var init_github = __esm({
|
|
|
338
345
|
import chalk14 from "chalk";
|
|
339
346
|
import readline from "readline";
|
|
340
347
|
import { createRequire } from "module";
|
|
341
|
-
import { input as input3 } from "@inquirer/prompts";
|
|
342
348
|
|
|
343
349
|
// src/commands/init.ts
|
|
344
350
|
import { input, password, select } from "@inquirer/prompts";
|
|
@@ -358,9 +364,12 @@ var configSchema = z.object({
|
|
|
358
364
|
githubClientId: z.string().optional(),
|
|
359
365
|
github: z.object({
|
|
360
366
|
owner: z.string().min(1),
|
|
361
|
-
repo: z.string().min(1)
|
|
362
|
-
|
|
363
|
-
|
|
367
|
+
repo: z.string().min(1)
|
|
368
|
+
}),
|
|
369
|
+
taskState: z.object({
|
|
370
|
+
activeIssueNumber: z.number().optional(),
|
|
371
|
+
baseCommit: z.string().optional()
|
|
372
|
+
}).optional()
|
|
364
373
|
});
|
|
365
374
|
var store = new Conf({
|
|
366
375
|
projectName: "techunter",
|
|
@@ -397,6 +406,12 @@ function setConfig(partial) {
|
|
|
397
406
|
if (partial.githubClientId !== void 0) {
|
|
398
407
|
current["githubClientId"] = partial.githubClientId;
|
|
399
408
|
}
|
|
409
|
+
if (partial.taskState !== void 0) {
|
|
410
|
+
current["taskState"] = {
|
|
411
|
+
...current["taskState"] ?? {},
|
|
412
|
+
...partial.taskState
|
|
413
|
+
};
|
|
414
|
+
}
|
|
400
415
|
store.store = current;
|
|
401
416
|
}
|
|
402
417
|
function getConfigPath() {
|
|
@@ -414,9 +429,6 @@ async function getCurrentBranch() {
|
|
|
414
429
|
const summary = await git.branch();
|
|
415
430
|
return summary.current;
|
|
416
431
|
}
|
|
417
|
-
async function createAndSwitchBranch(name) {
|
|
418
|
-
await git.checkoutLocalBranch(name);
|
|
419
|
-
}
|
|
420
432
|
async function pushBranch(name) {
|
|
421
433
|
await git.push("origin", name, ["--set-upstream"]);
|
|
422
434
|
}
|
|
@@ -444,6 +456,61 @@ function makeBranchName(issueNumber, username) {
|
|
|
444
456
|
const slug = username.toLowerCase().replace(/[^a-z0-9-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "") || "user";
|
|
445
457
|
return `task-${issueNumber}-${slug}`;
|
|
446
458
|
}
|
|
459
|
+
function makeWorkerBranchName(username) {
|
|
460
|
+
const slug = username.toLowerCase().replace(/[^a-z0-9-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "") || "user";
|
|
461
|
+
return `worker-${slug}`;
|
|
462
|
+
}
|
|
463
|
+
async function getCurrentCommit() {
|
|
464
|
+
return (await git.revparse(["HEAD"])).trim();
|
|
465
|
+
}
|
|
466
|
+
async function switchToBranchOrCreate(name) {
|
|
467
|
+
try {
|
|
468
|
+
const branches = await git.branch(["-a"]);
|
|
469
|
+
const exists = Object.keys(branches.branches).some(
|
|
470
|
+
(b) => b === name || b === `remotes/origin/${name}`
|
|
471
|
+
);
|
|
472
|
+
if (exists) {
|
|
473
|
+
await git.checkout(name);
|
|
474
|
+
return false;
|
|
475
|
+
}
|
|
476
|
+
await git.checkoutLocalBranch(name);
|
|
477
|
+
return true;
|
|
478
|
+
} catch {
|
|
479
|
+
await git.checkoutLocalBranch(name);
|
|
480
|
+
return true;
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
async function getDiffFromCommit(baseCommit) {
|
|
484
|
+
const status = await git.status();
|
|
485
|
+
const parts = [];
|
|
486
|
+
const fileLines = [
|
|
487
|
+
...status.modified.map((f) => ` M ${f}`),
|
|
488
|
+
...status.created.map((f) => ` A ${f}`),
|
|
489
|
+
...status.deleted.map((f) => ` D ${f}`),
|
|
490
|
+
...status.renamed.map((f) => ` R ${f.from} \u2192 ${f.to}`),
|
|
491
|
+
...status.not_added.map((f) => ` ? ${f}`)
|
|
492
|
+
];
|
|
493
|
+
if (fileLines.length > 0) {
|
|
494
|
+
parts.push("## Uncommitted changes\n" + fileLines.join("\n"));
|
|
495
|
+
const uncommitted = await git.diff(["HEAD"]);
|
|
496
|
+
if (uncommitted) {
|
|
497
|
+
const capped = uncommitted.length > 8e3 ? uncommitted.slice(0, 8e3) + "\n... (truncated)" : uncommitted;
|
|
498
|
+
parts.push("## Uncommitted diff\n```diff\n" + capped + "\n```");
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
const log = await git.log({ from: baseCommit, to: "HEAD" });
|
|
502
|
+
if (log.total > 0) {
|
|
503
|
+
const logLines = log.all.map((c) => ` ${c.hash.slice(0, 7)} ${c.message}`);
|
|
504
|
+
parts.push(`## Commits since task claimed (${log.total} total)
|
|
505
|
+
` + logLines.join("\n"));
|
|
506
|
+
const branchDiff = await git.diff([baseCommit, "HEAD"]);
|
|
507
|
+
if (branchDiff) {
|
|
508
|
+
const capped = branchDiff.length > 12e3 ? branchDiff.slice(0, 12e3) + "\n... (truncated)" : branchDiff;
|
|
509
|
+
parts.push("## Full diff since task claimed\n```diff\n" + capped + "\n```");
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
return parts.length > 0 ? parts.join("\n\n") : "No changes since task was claimed.";
|
|
513
|
+
}
|
|
447
514
|
async function findMergeBase(configuredBase) {
|
|
448
515
|
const candidates = configuredBase ? [`origin/${configuredBase}`, "origin/main", "origin/master"] : ["origin/main", "origin/master"];
|
|
449
516
|
const unique = [...new Set(candidates)];
|
|
@@ -628,11 +695,6 @@ async function initCommand() {
|
|
|
628
695
|
required: true
|
|
629
696
|
});
|
|
630
697
|
}
|
|
631
|
-
const detectedDefault = "main";
|
|
632
|
-
const baseBranch = await input({
|
|
633
|
-
message: "Main branch to merge PRs into:",
|
|
634
|
-
default: detectedDefault
|
|
635
|
-
});
|
|
636
698
|
const config = {
|
|
637
699
|
githubToken,
|
|
638
700
|
githubClientId,
|
|
@@ -641,8 +703,7 @@ async function initCommand() {
|
|
|
641
703
|
...aiModel ? { aiModel } : {},
|
|
642
704
|
github: {
|
|
643
705
|
owner: owner.trim(),
|
|
644
|
-
repo: repo.trim()
|
|
645
|
-
baseBranch: baseBranch.trim() || detectedDefault
|
|
706
|
+
repo: repo.trim()
|
|
646
707
|
}
|
|
647
708
|
};
|
|
648
709
|
setConfig(config);
|
|
@@ -678,7 +739,6 @@ async function configCommand() {
|
|
|
678
739
|
const field = await select2({
|
|
679
740
|
message: "Which setting to change?",
|
|
680
741
|
choices: [
|
|
681
|
-
{ name: `Base branch ${chalk3.dim(config.github.baseBranch ?? "(not set, uses repo default)")}`, value: "baseBranch" },
|
|
682
742
|
{ name: `GitHub repo ${chalk3.dim(`${config.github.owner}/${config.github.repo}`)}`, value: "repo" },
|
|
683
743
|
{ name: `AI base URL ${chalk3.dim(currentBaseUrl)}`, value: "aiBaseUrl" },
|
|
684
744
|
{ name: `AI model ${chalk3.dim(currentModel)}`, value: "aiModel" },
|
|
@@ -688,16 +748,7 @@ async function configCommand() {
|
|
|
688
748
|
]
|
|
689
749
|
});
|
|
690
750
|
if (field === "cancel") return;
|
|
691
|
-
if (field === "
|
|
692
|
-
const val = await input2({
|
|
693
|
-
message: "Main branch to merge PRs into:",
|
|
694
|
-
default: config.github.baseBranch ?? "main"
|
|
695
|
-
});
|
|
696
|
-
setConfig({ github: { ...config.github, baseBranch: val.trim() || "main" } });
|
|
697
|
-
console.log(chalk3.green(`
|
|
698
|
-
Base branch set to: ${val.trim() || "main"}
|
|
699
|
-
`));
|
|
700
|
-
} else if (field === "repo") {
|
|
751
|
+
if (field === "repo") {
|
|
701
752
|
const owner = await input2({ message: "GitHub repo owner:", default: config.github.owner });
|
|
702
753
|
const repo = await input2({ message: "GitHub repo name:", default: config.github.repo });
|
|
703
754
|
setConfig({ github: { ...config.github, owner: owner.trim(), repo: repo.trim() } });
|
|
@@ -921,8 +972,8 @@ import { select as select3, input as promptInput } from "@inquirer/prompts";
|
|
|
921
972
|
|
|
922
973
|
// src/lib/agent-ui.ts
|
|
923
974
|
import chalk6 from "chalk";
|
|
924
|
-
function formatInput(
|
|
925
|
-
return Object.entries(
|
|
975
|
+
function formatInput(input3) {
|
|
976
|
+
return Object.entries(input3).map(([k, v]) => {
|
|
926
977
|
if (typeof v === "number") return `${k}=${v}`;
|
|
927
978
|
if (typeof v === "string") {
|
|
928
979
|
if (k === "body" || v.length > 50) return `${k}=[${v.length} chars]`;
|
|
@@ -935,8 +986,8 @@ function summarize(result) {
|
|
|
935
986
|
const first = result.split("\n").find((l) => l.trim()) ?? result;
|
|
936
987
|
return first.length > 100 ? first.slice(0, 97) + "..." : first;
|
|
937
988
|
}
|
|
938
|
-
function printToolCall(name,
|
|
939
|
-
const params = formatInput(
|
|
989
|
+
function printToolCall(name, input3) {
|
|
990
|
+
const params = formatInput(input3);
|
|
940
991
|
console.log(` ${chalk6.cyan("\u2192")} ${chalk6.bold(name)}${params ? " " + chalk6.dim(params) : ""}`);
|
|
941
992
|
}
|
|
942
993
|
function printToolResult(result) {
|
|
@@ -972,15 +1023,15 @@ async function runSubAgentLoop(config, systemPrompt, userMessage, toolNames) {
|
|
|
972
1023
|
}
|
|
973
1024
|
if (choice.finish_reason === "tool_calls") {
|
|
974
1025
|
for (const tc of choice.message.tool_calls ?? []) {
|
|
975
|
-
let
|
|
1026
|
+
let input3;
|
|
976
1027
|
try {
|
|
977
|
-
|
|
1028
|
+
input3 = JSON.parse(tc.function.arguments);
|
|
978
1029
|
} catch {
|
|
979
|
-
|
|
1030
|
+
input3 = {};
|
|
980
1031
|
}
|
|
981
|
-
printToolCall(tc.function.name,
|
|
1032
|
+
printToolCall(tc.function.name, input3);
|
|
982
1033
|
const mod = selected.find((m) => m.definition.function.name === tc.function.name);
|
|
983
|
-
const result = mod ? await mod.execute(
|
|
1034
|
+
const result = mod ? await mod.execute(input3, config) : `Unknown tool: ${tc.function.name}`;
|
|
984
1035
|
printToolResult(result);
|
|
985
1036
|
messages.push({ role: "tool", tool_call_id: tc.id, content: result });
|
|
986
1037
|
}
|
|
@@ -989,7 +1040,7 @@ async function runSubAgentLoop(config, systemPrompt, userMessage, toolNames) {
|
|
|
989
1040
|
}
|
|
990
1041
|
|
|
991
1042
|
// src/tools/submit/prompts.ts
|
|
992
|
-
var REVIEWER_SYSTEM_PROMPT = "You are a concise code reviewer. Use run_command to run tests/lint if needed, and read_file to inspect specific files. Then output your review: for each acceptance criterion mark \u2705 met or \u274C not met with a one-line reason. End with an overall verdict line: Ready to submit / Not ready. Reply in the same language as the task.";
|
|
1043
|
+
var REVIEWER_SYSTEM_PROMPT = "You are a concise code reviewer. The diff provided shows all changes on this worker branch since the task was claimed. The branch is shared across tasks, so the diff may contain changes unrelated to this specific task \u2014 use the task title and acceptance criteria to identify which changes are relevant, and ignore the rest. Use run_command to run tests/lint if needed, and read_file to inspect specific files. Then output your review: for each acceptance criterion mark \u2705 met or \u274C not met with a one-line reason. End with an overall verdict line: Ready to submit / Not ready. Reply in the same language as the task.";
|
|
993
1044
|
|
|
994
1045
|
// src/tools/submit/reviewer.ts
|
|
995
1046
|
async function reviewChanges(config, issueNumber, issue, diff) {
|
|
@@ -1023,32 +1074,41 @@ var definition = {
|
|
|
1023
1074
|
}
|
|
1024
1075
|
};
|
|
1025
1076
|
async function run(_input, config) {
|
|
1026
|
-
const
|
|
1027
|
-
const
|
|
1028
|
-
if (!
|
|
1029
|
-
return
|
|
1077
|
+
const taskState = getConfig().taskState;
|
|
1078
|
+
const issueNumber = taskState?.activeIssueNumber;
|
|
1079
|
+
if (!issueNumber) {
|
|
1080
|
+
return "No active task found. Claim a task first with /pick.";
|
|
1030
1081
|
}
|
|
1031
|
-
const issueNumber = parseInt(match[1], 10);
|
|
1032
1082
|
let spinner = ora2("Loading task and diff\u2026").start();
|
|
1033
|
-
const
|
|
1083
|
+
const diffPromise = taskState?.baseCommit ? getDiffFromCommit(taskState.baseCommit) : getDiff();
|
|
1084
|
+
const [issue, diff, me] = await Promise.all([
|
|
1034
1085
|
getTask(config, issueNumber),
|
|
1035
|
-
|
|
1036
|
-
|
|
1086
|
+
diffPromise,
|
|
1087
|
+
getAuthenticatedUser(config)
|
|
1037
1088
|
]);
|
|
1038
1089
|
spinner.stop();
|
|
1039
|
-
const
|
|
1090
|
+
const workerBranch = makeWorkerBranchName(issue.author ?? me);
|
|
1091
|
+
const branch = await getCurrentBranch();
|
|
1092
|
+
const isSelfSubmit = issue.author !== null && issue.author === me;
|
|
1040
1093
|
let review = "";
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1094
|
+
if (!isSelfSubmit) {
|
|
1095
|
+
const reviewSpinner = ora2("Reviewing changes\u2026").start();
|
|
1096
|
+
try {
|
|
1097
|
+
review = await reviewChanges(config, issueNumber, issue, diff);
|
|
1098
|
+
} catch (err) {
|
|
1099
|
+
review = `(Review failed: ${err.message})`;
|
|
1100
|
+
}
|
|
1101
|
+
reviewSpinner.stop();
|
|
1045
1102
|
}
|
|
1046
|
-
reviewSpinner.stop();
|
|
1047
1103
|
const divider = chalk7.dim("\u2500".repeat(70));
|
|
1048
1104
|
console.log("\n" + divider);
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1105
|
+
if (isSelfSubmit) {
|
|
1106
|
+
console.log(chalk7.yellow(` Self-submit detected \u2014 AI review skipped.`));
|
|
1107
|
+
} else {
|
|
1108
|
+
console.log(chalk7.bold(` Review \u2014 task #${issueNumber} "${issue.title}"`));
|
|
1109
|
+
console.log(divider);
|
|
1110
|
+
console.log(renderMarkdown(review));
|
|
1111
|
+
}
|
|
1052
1112
|
console.log(divider + "\n");
|
|
1053
1113
|
let shouldProceed;
|
|
1054
1114
|
try {
|
|
@@ -1084,15 +1144,15 @@ async function run(_input, config) {
|
|
|
1084
1144
|
spinner = ora2("Creating pull request\u2026").start();
|
|
1085
1145
|
let prUrl;
|
|
1086
1146
|
try {
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
issue.
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
);
|
|
1147
|
+
const prBody = [
|
|
1148
|
+
`Closes #${issueNumber}`,
|
|
1149
|
+
issue.body ? `
|
|
1150
|
+
${issue.body}` : "",
|
|
1151
|
+
review ? `
|
|
1152
|
+
## AI Review
|
|
1153
|
+
${review}` : ""
|
|
1154
|
+
].join("\n").trim();
|
|
1155
|
+
prUrl = await createPR(config, issue.title, prBody, branch, workerBranch);
|
|
1096
1156
|
spinner.stop();
|
|
1097
1157
|
} catch (err) {
|
|
1098
1158
|
spinner.stop();
|
|
@@ -1106,27 +1166,33 @@ ${issue.body ?? ""}`.trim(),
|
|
|
1106
1166
|
spinner.stop();
|
|
1107
1167
|
return `PR created (${prUrl}) but failed to update label: ${err.message}`;
|
|
1108
1168
|
}
|
|
1169
|
+
setConfig({ taskState: { activeIssueNumber: void 0, baseCommit: void 0 } });
|
|
1109
1170
|
return `Task #${issueNumber} submitted.
|
|
1110
1171
|
Commit: "${commitMessage.trim()}"
|
|
1111
1172
|
PR: ${prUrl}`;
|
|
1112
1173
|
}
|
|
1113
|
-
async function execute(
|
|
1114
|
-
const
|
|
1115
|
-
const
|
|
1116
|
-
if (!
|
|
1117
|
-
const
|
|
1118
|
-
const [issue,
|
|
1174
|
+
async function execute(input3, config) {
|
|
1175
|
+
const taskState = getConfig().taskState;
|
|
1176
|
+
const issueNumber = taskState?.activeIssueNumber;
|
|
1177
|
+
if (!issueNumber) return "No active task found. Claim a task first.";
|
|
1178
|
+
const diffPromise = taskState?.baseCommit ? getDiffFromCommit(taskState.baseCommit) : getDiff();
|
|
1179
|
+
const [issue, diff, branch, me] = await Promise.all([
|
|
1119
1180
|
getTask(config, issueNumber),
|
|
1120
|
-
|
|
1121
|
-
|
|
1181
|
+
diffPromise,
|
|
1182
|
+
getCurrentBranch(),
|
|
1183
|
+
getAuthenticatedUser(config)
|
|
1122
1184
|
]);
|
|
1185
|
+
const workerBranch = makeWorkerBranchName(issue.author ?? me);
|
|
1186
|
+
const isSelfSubmit = issue.author !== null && issue.author === me;
|
|
1123
1187
|
let review = "";
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1188
|
+
if (!isSelfSubmit) {
|
|
1189
|
+
try {
|
|
1190
|
+
review = await reviewChanges(config, issueNumber, issue, diff);
|
|
1191
|
+
} catch (err) {
|
|
1192
|
+
review = `(Review failed: ${err.message})`;
|
|
1193
|
+
}
|
|
1128
1194
|
}
|
|
1129
|
-
const commitMessage =
|
|
1195
|
+
const commitMessage = input3["commit_message"]?.trim() || `complete: ${issue.title}`;
|
|
1130
1196
|
try {
|
|
1131
1197
|
await stageAllAndCommit(commitMessage);
|
|
1132
1198
|
} catch (err) {
|
|
@@ -1134,15 +1200,15 @@ async function execute(input4, config) {
|
|
|
1134
1200
|
}
|
|
1135
1201
|
let prUrl;
|
|
1136
1202
|
try {
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
issue.
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
);
|
|
1203
|
+
const prBody = [
|
|
1204
|
+
`Closes #${issueNumber}`,
|
|
1205
|
+
issue.body ? `
|
|
1206
|
+
${issue.body}` : "",
|
|
1207
|
+
review ? `
|
|
1208
|
+
## AI Review
|
|
1209
|
+
${review}` : ""
|
|
1210
|
+
].join("\n").trim();
|
|
1211
|
+
prUrl = await createPR(config, issue.title, prBody, branch, workerBranch);
|
|
1146
1212
|
} catch (err) {
|
|
1147
1213
|
return `Committed but PR creation failed: ${err.message}`;
|
|
1148
1214
|
}
|
|
@@ -1183,8 +1249,8 @@ var definition2 = {
|
|
|
1183
1249
|
}
|
|
1184
1250
|
}
|
|
1185
1251
|
};
|
|
1186
|
-
async function run2(
|
|
1187
|
-
let issueNumber =
|
|
1252
|
+
async function run2(input3, config) {
|
|
1253
|
+
let issueNumber = input3["issue_number"];
|
|
1188
1254
|
if (!issueNumber) {
|
|
1189
1255
|
let tasks;
|
|
1190
1256
|
try {
|
|
@@ -1225,8 +1291,8 @@ async function run2(input4, config) {
|
|
|
1225
1291
|
return `Error: ${err.message}`;
|
|
1226
1292
|
}
|
|
1227
1293
|
}
|
|
1228
|
-
async function execute2(
|
|
1229
|
-
const issueNumber =
|
|
1294
|
+
async function execute2(input3, config) {
|
|
1295
|
+
const issueNumber = input3["issue_number"];
|
|
1230
1296
|
const spinner = ora3(`Closing #${issueNumber}\u2026`).start();
|
|
1231
1297
|
try {
|
|
1232
1298
|
await closeTask(config, issueNumber);
|
|
@@ -1259,8 +1325,8 @@ var definition3 = {
|
|
|
1259
1325
|
}
|
|
1260
1326
|
}
|
|
1261
1327
|
};
|
|
1262
|
-
async function run3(
|
|
1263
|
-
const preselected =
|
|
1328
|
+
async function run3(input3, config) {
|
|
1329
|
+
const preselected = input3["issue_number"];
|
|
1264
1330
|
let chosenNumber;
|
|
1265
1331
|
if (preselected !== void 0) {
|
|
1266
1332
|
chosenNumber = preselected;
|
|
@@ -1336,23 +1402,42 @@ Finish or submit it before claiming a new one.`;
|
|
|
1336
1402
|
let spinner = ora4(`Claiming #${issue.number}\u2026`).start();
|
|
1337
1403
|
await claimTask(config, issue.number, me);
|
|
1338
1404
|
spinner.stop();
|
|
1339
|
-
const
|
|
1340
|
-
spinner = ora4(`
|
|
1405
|
+
const workerBranch = makeWorkerBranchName(me);
|
|
1406
|
+
spinner = ora4(`Switching to ${workerBranch}\u2026`).start();
|
|
1341
1407
|
try {
|
|
1342
|
-
await
|
|
1408
|
+
const isNewWorker = await switchToBranchOrCreate(workerBranch);
|
|
1343
1409
|
spinner.stop();
|
|
1410
|
+
if (isNewWorker) {
|
|
1411
|
+
spinner = ora4("Pushing worker branch\u2026").start();
|
|
1412
|
+
try {
|
|
1413
|
+
await pushBranch(workerBranch);
|
|
1414
|
+
spinner.stop();
|
|
1415
|
+
} catch {
|
|
1416
|
+
spinner.warn("Could not push worker branch");
|
|
1417
|
+
}
|
|
1418
|
+
}
|
|
1344
1419
|
} catch {
|
|
1345
|
-
spinner.warn(`Could not
|
|
1420
|
+
spinner.warn(`Could not switch to ${workerBranch}`);
|
|
1346
1421
|
}
|
|
1347
|
-
|
|
1422
|
+
const branch = makeBranchName(issue.number, me);
|
|
1423
|
+
spinner = ora4(`Creating task branch ${branch}\u2026`).start();
|
|
1348
1424
|
try {
|
|
1349
|
-
await
|
|
1425
|
+
await switchToBranchOrCreate(branch);
|
|
1350
1426
|
spinner.stop();
|
|
1427
|
+
spinner = ora4("Pushing task branch\u2026").start();
|
|
1428
|
+
try {
|
|
1429
|
+
await pushBranch(branch);
|
|
1430
|
+
spinner.stop();
|
|
1431
|
+
} catch {
|
|
1432
|
+
spinner.warn("Could not push task branch");
|
|
1433
|
+
}
|
|
1351
1434
|
} catch {
|
|
1352
|
-
spinner.warn(
|
|
1435
|
+
spinner.warn(`Could not create branch ${branch}`);
|
|
1353
1436
|
}
|
|
1437
|
+
const baseCommit = await getCurrentCommit();
|
|
1438
|
+
setConfig({ taskState: { activeIssueNumber: issue.number, baseCommit } });
|
|
1354
1439
|
console.log(chalk8.green(`
|
|
1355
|
-
Claimed! Branch: ${branch}
|
|
1440
|
+
Claimed! Branch: ${branch} (base: ${baseCommit.slice(0, 7)})
|
|
1356
1441
|
`));
|
|
1357
1442
|
let openClaude;
|
|
1358
1443
|
try {
|
|
@@ -1376,9 +1461,9 @@ Finish or submit it before claiming a new one.`;
|
|
|
1376
1461
|
if (action === "close") return run2({ issue_number: issue.number }, config);
|
|
1377
1462
|
return "Cancelled.";
|
|
1378
1463
|
}
|
|
1379
|
-
async function execute3(
|
|
1380
|
-
const issueNumber =
|
|
1381
|
-
const action =
|
|
1464
|
+
async function execute3(input3, config) {
|
|
1465
|
+
const issueNumber = input3["issue_number"];
|
|
1466
|
+
const action = input3["action"];
|
|
1382
1467
|
let issue;
|
|
1383
1468
|
try {
|
|
1384
1469
|
issue = await getTask(config, issueNumber);
|
|
@@ -1405,16 +1490,29 @@ async function execute3(input4, config) {
|
|
|
1405
1490
|
} catch (err) {
|
|
1406
1491
|
return `Error claiming task: ${err.message}`;
|
|
1407
1492
|
}
|
|
1493
|
+
const workerBranch = makeWorkerBranchName(me);
|
|
1494
|
+
try {
|
|
1495
|
+
const isNewWorker = await switchToBranchOrCreate(workerBranch);
|
|
1496
|
+
if (isNewWorker) {
|
|
1497
|
+
try {
|
|
1498
|
+
await pushBranch(workerBranch);
|
|
1499
|
+
} catch {
|
|
1500
|
+
}
|
|
1501
|
+
}
|
|
1502
|
+
} catch {
|
|
1503
|
+
}
|
|
1408
1504
|
const branch = makeBranchName(issueNumber, me);
|
|
1409
1505
|
try {
|
|
1410
|
-
await
|
|
1506
|
+
await switchToBranchOrCreate(branch);
|
|
1411
1507
|
} catch {
|
|
1412
1508
|
}
|
|
1413
1509
|
try {
|
|
1414
1510
|
await pushBranch(branch);
|
|
1415
1511
|
} catch {
|
|
1416
1512
|
}
|
|
1417
|
-
|
|
1513
|
+
const baseCommit = await getCurrentCommit();
|
|
1514
|
+
setConfig({ taskState: { activeIssueNumber: issueNumber, baseCommit } });
|
|
1515
|
+
return `Task #${issueNumber} claimed. Branch: ${branch} (base commit: ${baseCommit.slice(0, 7)})`;
|
|
1418
1516
|
}
|
|
1419
1517
|
return `Unknown action: ${action}`;
|
|
1420
1518
|
}
|
|
@@ -1504,8 +1602,22 @@ var definition4 = {
|
|
|
1504
1602
|
}
|
|
1505
1603
|
}
|
|
1506
1604
|
};
|
|
1507
|
-
async function run4(
|
|
1508
|
-
|
|
1605
|
+
async function run4(input3, config) {
|
|
1606
|
+
const authSpinner = ora5("Checking permissions\u2026").start();
|
|
1607
|
+
let me;
|
|
1608
|
+
let allowed;
|
|
1609
|
+
try {
|
|
1610
|
+
me = await getAuthenticatedUser(config);
|
|
1611
|
+
allowed = await isCollaborator(config, me);
|
|
1612
|
+
authSpinner.stop();
|
|
1613
|
+
} catch (err) {
|
|
1614
|
+
authSpinner.stop();
|
|
1615
|
+
return `Error checking permissions: ${err.message}`;
|
|
1616
|
+
}
|
|
1617
|
+
if (!allowed) {
|
|
1618
|
+
return `Permission denied: only repository collaborators can create tasks.`;
|
|
1619
|
+
}
|
|
1620
|
+
let title = input3["title"]?.trim();
|
|
1509
1621
|
if (!title) {
|
|
1510
1622
|
try {
|
|
1511
1623
|
title = (await promptInput2({ message: "Task title:" })).trim();
|
|
@@ -1601,9 +1713,13 @@ async function run4(input4, config) {
|
|
|
1601
1713
|
}
|
|
1602
1714
|
return `Created #${issueNumber} "${issueTitle}" \u2014 ${htmlUrl}`;
|
|
1603
1715
|
}
|
|
1604
|
-
async function execute4(
|
|
1605
|
-
const
|
|
1606
|
-
|
|
1716
|
+
async function execute4(input3, config) {
|
|
1717
|
+
const me = await getAuthenticatedUser(config);
|
|
1718
|
+
if (!await isCollaborator(config, me)) {
|
|
1719
|
+
return `Permission denied: only repository collaborators can create tasks.`;
|
|
1720
|
+
}
|
|
1721
|
+
const title = input3["title"].trim();
|
|
1722
|
+
const feedback = input3["feedback"];
|
|
1607
1723
|
let guide = await generateGuide(config, title);
|
|
1608
1724
|
if (feedback) {
|
|
1609
1725
|
guide = await generateGuide(config, title, { feedback, previousGuide: guide });
|
|
@@ -1818,8 +1934,15 @@ var definition9 = {
|
|
|
1818
1934
|
}
|
|
1819
1935
|
}
|
|
1820
1936
|
};
|
|
1821
|
-
async function run9(
|
|
1822
|
-
const issueNumber =
|
|
1937
|
+
async function run9(input3, config) {
|
|
1938
|
+
const issueNumber = input3["issue_number"];
|
|
1939
|
+
const [me, issue] = await Promise.all([
|
|
1940
|
+
getAuthenticatedUser(config),
|
|
1941
|
+
getTask(config, issueNumber)
|
|
1942
|
+
]);
|
|
1943
|
+
if (issue.author && issue.author !== me) {
|
|
1944
|
+
return `Permission denied: only the task author (@${issue.author}) can reject task #${issueNumber}.`;
|
|
1945
|
+
}
|
|
1823
1946
|
let feedback;
|
|
1824
1947
|
try {
|
|
1825
1948
|
feedback = await promptInput3({
|
|
@@ -1886,9 +2009,16 @@ async function run9(input4, config) {
|
|
|
1886
2009
|
return `Task #${issueNumber} rejected. Label changed to changes-needed.`;
|
|
1887
2010
|
}
|
|
1888
2011
|
}
|
|
1889
|
-
async function execute9(
|
|
1890
|
-
const issueNumber =
|
|
1891
|
-
const feedback =
|
|
2012
|
+
async function execute9(input3, config) {
|
|
2013
|
+
const issueNumber = input3["issue_number"];
|
|
2014
|
+
const feedback = input3["feedback"];
|
|
2015
|
+
const [me, issue] = await Promise.all([
|
|
2016
|
+
getAuthenticatedUser(config),
|
|
2017
|
+
getTask(config, issueNumber)
|
|
2018
|
+
]);
|
|
2019
|
+
if (issue.author && issue.author !== me) {
|
|
2020
|
+
return `Permission denied: only the task author (@${issue.author}) can reject task #${issueNumber}.`;
|
|
2021
|
+
}
|
|
1892
2022
|
let comment;
|
|
1893
2023
|
try {
|
|
1894
2024
|
comment = await generateRejectionComment(config, issueNumber, feedback);
|
|
@@ -1928,7 +2058,7 @@ var definition10 = {
|
|
|
1928
2058
|
type: "function",
|
|
1929
2059
|
function: {
|
|
1930
2060
|
name: "accept",
|
|
1931
|
-
description: "Accept an in-review task: merges the PR into
|
|
2061
|
+
description: "Accept an in-review task: merges the PR into your worker branch and closes the issue.",
|
|
1932
2062
|
parameters: {
|
|
1933
2063
|
type: "object",
|
|
1934
2064
|
properties: {
|
|
@@ -1938,18 +2068,18 @@ var definition10 = {
|
|
|
1938
2068
|
}
|
|
1939
2069
|
}
|
|
1940
2070
|
};
|
|
1941
|
-
async function run10(
|
|
1942
|
-
let issueNumber =
|
|
2071
|
+
async function run10(input3, config) {
|
|
2072
|
+
let issueNumber = input3["issue_number"];
|
|
1943
2073
|
if (!issueNumber) {
|
|
1944
|
-
const
|
|
2074
|
+
const spinner3 = ora9("Loading tasks for review\u2026").start();
|
|
1945
2075
|
let tasks;
|
|
1946
2076
|
let me;
|
|
1947
2077
|
try {
|
|
1948
2078
|
me = await getAuthenticatedUser(config);
|
|
1949
2079
|
tasks = await listTasksForReview(config, me);
|
|
1950
|
-
|
|
2080
|
+
spinner3.stop();
|
|
1951
2081
|
} catch (err) {
|
|
1952
|
-
|
|
2082
|
+
spinner3.stop();
|
|
1953
2083
|
return `Error: ${err.message}`;
|
|
1954
2084
|
}
|
|
1955
2085
|
if (tasks.length === 0) return "No tasks pending review.";
|
|
@@ -1965,11 +2095,27 @@ async function run10(input4, config) {
|
|
|
1965
2095
|
return "Cancelled.";
|
|
1966
2096
|
}
|
|
1967
2097
|
}
|
|
1968
|
-
const
|
|
2098
|
+
const spinner2 = ora9("Verifying permissions\u2026").start();
|
|
2099
|
+
let me2;
|
|
2100
|
+
let issue;
|
|
2101
|
+
try {
|
|
2102
|
+
[me2, issue] = await Promise.all([
|
|
2103
|
+
getAuthenticatedUser(config),
|
|
2104
|
+
getTask(config, issueNumber)
|
|
2105
|
+
]);
|
|
2106
|
+
spinner2.stop();
|
|
2107
|
+
} catch (err) {
|
|
2108
|
+
spinner2.stop();
|
|
2109
|
+
return `Error: ${err.message}`;
|
|
2110
|
+
}
|
|
2111
|
+
if (issue.author && issue.author !== me2) {
|
|
2112
|
+
return `Permission denied: only the task author (@${issue.author}) can accept task #${issueNumber}.`;
|
|
2113
|
+
}
|
|
2114
|
+
const targetBranch = makeWorkerBranchName(me2);
|
|
1969
2115
|
let confirmed;
|
|
1970
2116
|
try {
|
|
1971
2117
|
confirmed = await select8({
|
|
1972
|
-
message: `Merge PR for #${issueNumber} into ${chalk11.cyan(
|
|
2118
|
+
message: `Merge PR for #${issueNumber} into ${chalk11.cyan(targetBranch)} and close issue?`,
|
|
1973
2119
|
choices: [
|
|
1974
2120
|
{ name: "Yes, accept", value: true },
|
|
1975
2121
|
{ name: "Cancel", value: false }
|
|
@@ -1982,24 +2128,31 @@ async function run10(input4, config) {
|
|
|
1982
2128
|
const spinner = ora9(`Merging PR for #${issueNumber}\u2026`).start();
|
|
1983
2129
|
try {
|
|
1984
2130
|
const result = await acceptTask(config, issueNumber);
|
|
1985
|
-
spinner.succeed(`PR #${result.prNumber} merged into ${
|
|
2131
|
+
spinner.succeed(`PR #${result.prNumber} merged into ${targetBranch}`);
|
|
1986
2132
|
return `Task #${issueNumber} accepted.
|
|
1987
|
-
PR #${result.prNumber} merged \u2192 ${
|
|
2133
|
+
PR #${result.prNumber} merged \u2192 ${targetBranch}
|
|
1988
2134
|
Issue closed.`;
|
|
1989
2135
|
} catch (err) {
|
|
1990
2136
|
spinner.fail("Failed");
|
|
1991
2137
|
return `Error: ${err.message}`;
|
|
1992
2138
|
}
|
|
1993
2139
|
}
|
|
1994
|
-
async function execute10(
|
|
1995
|
-
const issueNumber =
|
|
2140
|
+
async function execute10(input3, config) {
|
|
2141
|
+
const issueNumber = input3["issue_number"];
|
|
2142
|
+
const [me, issue] = await Promise.all([
|
|
2143
|
+
getAuthenticatedUser(config),
|
|
2144
|
+
getTask(config, issueNumber)
|
|
2145
|
+
]);
|
|
2146
|
+
if (issue.author && issue.author !== me) {
|
|
2147
|
+
return `Permission denied: only the task author (@${issue.author}) can accept task #${issueNumber}.`;
|
|
2148
|
+
}
|
|
2149
|
+
const targetBranch = makeWorkerBranchName(me);
|
|
1996
2150
|
const spinner = ora9(`Merging PR for #${issueNumber}\u2026`).start();
|
|
1997
2151
|
try {
|
|
1998
2152
|
const result = await acceptTask(config, issueNumber);
|
|
1999
2153
|
spinner.stop();
|
|
2000
|
-
const baseBranch = config.github.baseBranch ?? "main";
|
|
2001
2154
|
return `Task #${issueNumber} accepted.
|
|
2002
|
-
PR #${result.prNumber} merged \u2192 ${
|
|
2155
|
+
PR #${result.prNumber} merged \u2192 ${targetBranch}
|
|
2003
2156
|
Issue closed.`;
|
|
2004
2157
|
} catch (err) {
|
|
2005
2158
|
spinner.stop();
|
|
@@ -2035,8 +2188,8 @@ var definition11 = {
|
|
|
2035
2188
|
}
|
|
2036
2189
|
}
|
|
2037
2190
|
};
|
|
2038
|
-
async function run11(
|
|
2039
|
-
let issueNumber =
|
|
2191
|
+
async function run11(input3, config) {
|
|
2192
|
+
let issueNumber = input3["issue_number"];
|
|
2040
2193
|
if (!issueNumber) {
|
|
2041
2194
|
let tasks;
|
|
2042
2195
|
try {
|
|
@@ -2087,10 +2240,10 @@ async function run11(input4, config) {
|
|
|
2087
2240
|
return `Error: ${err.message}`;
|
|
2088
2241
|
}
|
|
2089
2242
|
}
|
|
2090
|
-
async function execute11(
|
|
2091
|
-
const issueNumber =
|
|
2092
|
-
const title =
|
|
2093
|
-
const body =
|
|
2243
|
+
async function execute11(input3, config) {
|
|
2244
|
+
const issueNumber = input3["issue_number"];
|
|
2245
|
+
const title = input3["title"];
|
|
2246
|
+
const body = input3["body"];
|
|
2094
2247
|
const spinner = ora10(`Updating #${issueNumber}\u2026`).start();
|
|
2095
2248
|
try {
|
|
2096
2249
|
await editTask(config, issueNumber, title, body);
|
|
@@ -2153,8 +2306,8 @@ var definition13 = {
|
|
|
2153
2306
|
}
|
|
2154
2307
|
}
|
|
2155
2308
|
};
|
|
2156
|
-
async function execute13(
|
|
2157
|
-
const issue = await getTask(config,
|
|
2309
|
+
async function execute13(input3, config) {
|
|
2310
|
+
const issue = await getTask(config, input3["issue_number"]);
|
|
2158
2311
|
const status = issue.labels.find((l) => l.startsWith("techunter:"))?.replace("techunter:", "") ?? "unknown";
|
|
2159
2312
|
const assignee = issue.assignee ? `@${issue.assignee}` : "\u2014";
|
|
2160
2313
|
const lines = [
|
|
@@ -2190,9 +2343,9 @@ var definition14 = {
|
|
|
2190
2343
|
}
|
|
2191
2344
|
}
|
|
2192
2345
|
};
|
|
2193
|
-
async function execute14(
|
|
2194
|
-
const issueNumber =
|
|
2195
|
-
const limit =
|
|
2346
|
+
async function execute14(input3, config) {
|
|
2347
|
+
const issueNumber = input3["issue_number"];
|
|
2348
|
+
const limit = input3["limit"] ?? 5;
|
|
2196
2349
|
const spinner = ora11(`Loading comments for #${issueNumber}...`).start();
|
|
2197
2350
|
try {
|
|
2198
2351
|
const comments = await listComments(config, issueNumber, limit);
|
|
@@ -2227,7 +2380,7 @@ var definition15 = {
|
|
|
2227
2380
|
async function execute15(_input, _config) {
|
|
2228
2381
|
const spinner = ora12("Reading git diff...").start();
|
|
2229
2382
|
try {
|
|
2230
|
-
const diff = await getDiff(
|
|
2383
|
+
const diff = await getDiff();
|
|
2231
2384
|
spinner.stop();
|
|
2232
2385
|
return diff;
|
|
2233
2386
|
} catch (err) {
|
|
@@ -2260,8 +2413,8 @@ var definition16 = {
|
|
|
2260
2413
|
}
|
|
2261
2414
|
}
|
|
2262
2415
|
};
|
|
2263
|
-
async function execute16(
|
|
2264
|
-
const command =
|
|
2416
|
+
async function execute16(input3, _config) {
|
|
2417
|
+
const command = input3["command"];
|
|
2265
2418
|
const cwd = process.cwd();
|
|
2266
2419
|
const spinner = ora13(`$ ${command}`).start();
|
|
2267
2420
|
try {
|
|
@@ -2453,8 +2606,8 @@ var definition17 = {
|
|
|
2453
2606
|
}
|
|
2454
2607
|
}
|
|
2455
2608
|
};
|
|
2456
|
-
async function execute17(
|
|
2457
|
-
const focus =
|
|
2609
|
+
async function execute17(input3, _config) {
|
|
2610
|
+
const focus = input3["focus"] ?? "";
|
|
2458
2611
|
const spinner = ora14("Scanning project...").start();
|
|
2459
2612
|
try {
|
|
2460
2613
|
const cwd = process.cwd();
|
|
@@ -2503,8 +2656,8 @@ var definition18 = {
|
|
|
2503
2656
|
}
|
|
2504
2657
|
}
|
|
2505
2658
|
};
|
|
2506
|
-
async function execute18(
|
|
2507
|
-
const filePath =
|
|
2659
|
+
async function execute18(input3, _config) {
|
|
2660
|
+
const filePath = input3["path"];
|
|
2508
2661
|
try {
|
|
2509
2662
|
const fullPath = path3.join(process.cwd(), filePath);
|
|
2510
2663
|
const content = await readFile3(fullPath, "utf-8");
|
|
@@ -2541,9 +2694,9 @@ var definition19 = {
|
|
|
2541
2694
|
}
|
|
2542
2695
|
}
|
|
2543
2696
|
};
|
|
2544
|
-
async function execute19(
|
|
2545
|
-
const question =
|
|
2546
|
-
const options =
|
|
2697
|
+
async function execute19(input3, _config) {
|
|
2698
|
+
const question = input3["question"];
|
|
2699
|
+
const options = input3["options"];
|
|
2547
2700
|
const OTHER = "__other__";
|
|
2548
2701
|
console.log("");
|
|
2549
2702
|
console.log(chalk12.dim(" \u250C\u2500 Agent question " + "\u2500".repeat(51)));
|
|
@@ -2596,12 +2749,12 @@ var toolModules = [
|
|
|
2596
2749
|
|
|
2597
2750
|
// src/lib/agent.ts
|
|
2598
2751
|
var tools = toolModules.map((m) => m.definition);
|
|
2599
|
-
async function executeTool(name,
|
|
2752
|
+
async function executeTool(name, input3, config) {
|
|
2600
2753
|
const mod = toolModules.find((m) => m.definition.function.name === name);
|
|
2601
2754
|
if (!mod) return `Unknown tool: ${name}`;
|
|
2602
2755
|
try {
|
|
2603
2756
|
const fn = mod.run ?? mod.execute;
|
|
2604
|
-
return await fn(
|
|
2757
|
+
return await fn(input3, config);
|
|
2605
2758
|
} catch (err) {
|
|
2606
2759
|
return `Error: ${err.message}`;
|
|
2607
2760
|
}
|
|
@@ -2805,13 +2958,9 @@ async function initNewRepo(config, owner, repo) {
|
|
|
2805
2958
|
console.log("");
|
|
2806
2959
|
console.log(chalk14.bold.cyan(` New repo detected: ${owner}/${repo}`));
|
|
2807
2960
|
console.log(chalk14.dim(" Setting up Techunter for this repository...\n"));
|
|
2808
|
-
const baseBranch = await input3({
|
|
2809
|
-
message: "Main branch to merge PRs into:",
|
|
2810
|
-
default: "main"
|
|
2811
|
-
});
|
|
2812
2961
|
const newConfig = {
|
|
2813
2962
|
...config,
|
|
2814
|
-
github: { owner, repo
|
|
2963
|
+
github: { owner, repo }
|
|
2815
2964
|
};
|
|
2816
2965
|
setConfig({ github: newConfig.github });
|
|
2817
2966
|
const ora16 = (await import("ora")).default;
|