techunter 0.1.2 → 0.1.4
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 +117 -122
- package/dist/mcp.js +61 -41
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
> An AI-powered task distribution CLI for development teams. Manage GitHub Issues through a conversational terminal interface.
|
|
4
4
|
|
|
5
5
|
```
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
╔═══════════════╗
|
|
7
|
+
◆═══╬ TECHUNTER ╬═══▶ Techunter v0.1.4
|
|
8
|
+
╚═══════════════╝ GLM-5 · z-ai · owner/repo
|
|
9
9
|
```
|
|
10
10
|
|
|
11
11
|
---
|
|
@@ -29,10 +29,11 @@
|
|
|
29
29
|
## Features
|
|
30
30
|
|
|
31
31
|
- **Conversational REPL** — Describe what you need in plain English; the Agent calls the right tools automatically
|
|
32
|
-
- **GitHub Issues integration** — Create, claim, submit, and close tasks; labels and assignees stay in sync
|
|
32
|
+
- **GitHub Issues integration** — Create, claim, submit, review, and close tasks; labels and assignees stay in sync
|
|
33
33
|
- **Automatic branch management** — Claiming a task creates and pushes the corresponding Git branch
|
|
34
34
|
- **Smart task guides** — Before you start, the Agent scans your codebase and posts a detailed implementation guide as an Issue comment
|
|
35
|
-
- **One-command delivery** — `/submit` lets the Agent review your changes against acceptance criteria, then commits and pushes
|
|
35
|
+
- **One-command delivery** — `/submit` lets the Agent review your changes against acceptance criteria, then commits and pushes
|
|
36
|
+
- **Review & accept flow** — `/review` lists in-review PRs; `/accept` merges and closes
|
|
36
37
|
- **Slash commands** — Common actions don't need a description: just `/pick`, `/new`, `/submit`
|
|
37
38
|
- **Persistent conversation history** — Full context is retained across turns in the same REPL session
|
|
38
39
|
|
|
@@ -43,7 +44,7 @@
|
|
|
43
44
|
- Node.js ≥ 18
|
|
44
45
|
- A GitHub repository with Issues enabled
|
|
45
46
|
- GitHub Personal Access Token or OAuth Device Flow authorization
|
|
46
|
-
-
|
|
47
|
+
- An OpenAI-compatible API key (OpenRouter by default, or any custom provider)
|
|
47
48
|
|
|
48
49
|
---
|
|
49
50
|
|
|
@@ -75,9 +76,10 @@ tch init
|
|
|
75
76
|
|
|
76
77
|
The wizard will ask for:
|
|
77
78
|
|
|
78
|
-
1. **GitHub authentication** —
|
|
79
|
-
-
|
|
80
|
-
2. **
|
|
79
|
+
1. **GitHub authentication** — Browser OAuth (recommended) or a Personal Access Token
|
|
80
|
+
- PAT: create one at https://github.com/settings/tokens/new with `repo` and `read:user` scopes
|
|
81
|
+
2. **AI provider** — OpenRouter (default) or a custom OpenAI-compatible endpoint
|
|
82
|
+
- OpenRouter key: https://openrouter.ai/settings/keys
|
|
81
83
|
3. **GitHub repository** — Auto-detected from your git remote, or enter manually
|
|
82
84
|
|
|
83
85
|
Config is stored at `~/.config/techunter/`.
|
|
@@ -99,10 +101,14 @@ Starts the conversational REPL. Type natural language or slash commands:
|
|
|
99
101
|
| `/pick` | `/p` | Browse and act on tasks interactively |
|
|
100
102
|
| `/new` | `/n` | Create a new task |
|
|
101
103
|
| `/close` | `/d` | Close (delete) a task |
|
|
102
|
-
| `/
|
|
103
|
-
| `/
|
|
104
|
+
| `/edit` | `/e` | Edit the title or description of a task |
|
|
105
|
+
| `/submit` | `/s` | Review changes, commit, and push |
|
|
106
|
+
| `/review` | `/rv` | List tasks waiting for your approval |
|
|
107
|
+
| `/accept` | `/ac` | Accept a reviewed task: merge PR and close issue |
|
|
104
108
|
| `/status` | `/me` | Show tasks assigned to you |
|
|
105
109
|
| `/code` | `/c` | Launch Claude Code for the current task branch |
|
|
110
|
+
| `/config` | `/cfg` | Change settings (repo, API keys, etc.) |
|
|
111
|
+
| `/init` | | Re-run the setup wizard for this repo |
|
|
106
112
|
|
|
107
113
|
Any other input is sent to the AI Agent, for example:
|
|
108
114
|
|
|
@@ -141,6 +147,12 @@ task-{issue_number}-{your-github-username}
|
|
|
141
147
|
|
|
142
148
|
Example: Issue #7 claimed by `johndoe` → `task-7-johndoe`
|
|
143
149
|
|
|
150
|
+
Worker branches (for persistent personal workspaces) follow:
|
|
151
|
+
|
|
152
|
+
```
|
|
153
|
+
worker-{your-github-username}
|
|
154
|
+
```
|
|
155
|
+
|
|
144
156
|
---
|
|
145
157
|
|
|
146
158
|
## AI Agent Tools
|
package/dist/index.js
CHANGED
|
@@ -21,7 +21,6 @@ __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,
|
|
27
26
|
isCollaborator: () => isCollaborator,
|
|
@@ -309,10 +308,6 @@ async function getDefaultBranch(config) {
|
|
|
309
308
|
const { data } = await octokit.repos.get({ owner, repo });
|
|
310
309
|
return data.default_branch;
|
|
311
310
|
}
|
|
312
|
-
function getBaseBranch(config) {
|
|
313
|
-
if (config.github.baseBranch) return Promise.resolve(config.github.baseBranch);
|
|
314
|
-
return getDefaultBranch(config);
|
|
315
|
-
}
|
|
316
311
|
async function acceptTask(config, issueNumber) {
|
|
317
312
|
const octokit = createOctokit(config.githubToken);
|
|
318
313
|
const { owner, repo } = config.github;
|
|
@@ -350,7 +345,6 @@ var init_github = __esm({
|
|
|
350
345
|
import chalk14 from "chalk";
|
|
351
346
|
import readline from "readline";
|
|
352
347
|
import { createRequire } from "module";
|
|
353
|
-
import { input as input3 } from "@inquirer/prompts";
|
|
354
348
|
|
|
355
349
|
// src/commands/init.ts
|
|
356
350
|
import { input, password, select } from "@inquirer/prompts";
|
|
@@ -370,8 +364,7 @@ var configSchema = z.object({
|
|
|
370
364
|
githubClientId: z.string().optional(),
|
|
371
365
|
github: z.object({
|
|
372
366
|
owner: z.string().min(1),
|
|
373
|
-
repo: z.string().min(1)
|
|
374
|
-
baseBranch: z.string().optional()
|
|
367
|
+
repo: z.string().min(1)
|
|
375
368
|
}),
|
|
376
369
|
taskState: z.object({
|
|
377
370
|
activeIssueNumber: z.number().optional(),
|
|
@@ -702,11 +695,6 @@ async function initCommand() {
|
|
|
702
695
|
required: true
|
|
703
696
|
});
|
|
704
697
|
}
|
|
705
|
-
const detectedDefault = "main";
|
|
706
|
-
const baseBranch = await input({
|
|
707
|
-
message: "Main branch to merge PRs into:",
|
|
708
|
-
default: detectedDefault
|
|
709
|
-
});
|
|
710
698
|
const config = {
|
|
711
699
|
githubToken,
|
|
712
700
|
githubClientId,
|
|
@@ -715,8 +703,7 @@ async function initCommand() {
|
|
|
715
703
|
...aiModel ? { aiModel } : {},
|
|
716
704
|
github: {
|
|
717
705
|
owner: owner.trim(),
|
|
718
|
-
repo: repo.trim()
|
|
719
|
-
baseBranch: baseBranch.trim() || detectedDefault
|
|
706
|
+
repo: repo.trim()
|
|
720
707
|
}
|
|
721
708
|
};
|
|
722
709
|
setConfig(config);
|
|
@@ -752,7 +739,6 @@ async function configCommand() {
|
|
|
752
739
|
const field = await select2({
|
|
753
740
|
message: "Which setting to change?",
|
|
754
741
|
choices: [
|
|
755
|
-
{ name: `Base branch ${chalk3.dim(config.github.baseBranch ?? "(not set, uses repo default)")}`, value: "baseBranch" },
|
|
756
742
|
{ name: `GitHub repo ${chalk3.dim(`${config.github.owner}/${config.github.repo}`)}`, value: "repo" },
|
|
757
743
|
{ name: `AI base URL ${chalk3.dim(currentBaseUrl)}`, value: "aiBaseUrl" },
|
|
758
744
|
{ name: `AI model ${chalk3.dim(currentModel)}`, value: "aiModel" },
|
|
@@ -762,16 +748,7 @@ async function configCommand() {
|
|
|
762
748
|
]
|
|
763
749
|
});
|
|
764
750
|
if (field === "cancel") return;
|
|
765
|
-
if (field === "
|
|
766
|
-
const val = await input2({
|
|
767
|
-
message: "Main branch to merge PRs into:",
|
|
768
|
-
default: config.github.baseBranch ?? "main"
|
|
769
|
-
});
|
|
770
|
-
setConfig({ github: { ...config.github, baseBranch: val.trim() || "main" } });
|
|
771
|
-
console.log(chalk3.green(`
|
|
772
|
-
Base branch set to: ${val.trim() || "main"}
|
|
773
|
-
`));
|
|
774
|
-
} else if (field === "repo") {
|
|
751
|
+
if (field === "repo") {
|
|
775
752
|
const owner = await input2({ message: "GitHub repo owner:", default: config.github.owner });
|
|
776
753
|
const repo = await input2({ message: "GitHub repo name:", default: config.github.repo });
|
|
777
754
|
setConfig({ github: { ...config.github, owner: owner.trim(), repo: repo.trim() } });
|
|
@@ -995,8 +972,8 @@ import { select as select3, input as promptInput } from "@inquirer/prompts";
|
|
|
995
972
|
|
|
996
973
|
// src/lib/agent-ui.ts
|
|
997
974
|
import chalk6 from "chalk";
|
|
998
|
-
function formatInput(
|
|
999
|
-
return Object.entries(
|
|
975
|
+
function formatInput(input3) {
|
|
976
|
+
return Object.entries(input3).map(([k, v]) => {
|
|
1000
977
|
if (typeof v === "number") return `${k}=${v}`;
|
|
1001
978
|
if (typeof v === "string") {
|
|
1002
979
|
if (k === "body" || v.length > 50) return `${k}=[${v.length} chars]`;
|
|
@@ -1009,8 +986,8 @@ function summarize(result) {
|
|
|
1009
986
|
const first = result.split("\n").find((l) => l.trim()) ?? result;
|
|
1010
987
|
return first.length > 100 ? first.slice(0, 97) + "..." : first;
|
|
1011
988
|
}
|
|
1012
|
-
function printToolCall(name,
|
|
1013
|
-
const params = formatInput(
|
|
989
|
+
function printToolCall(name, input3) {
|
|
990
|
+
const params = formatInput(input3);
|
|
1014
991
|
console.log(` ${chalk6.cyan("\u2192")} ${chalk6.bold(name)}${params ? " " + chalk6.dim(params) : ""}`);
|
|
1015
992
|
}
|
|
1016
993
|
function printToolResult(result) {
|
|
@@ -1028,7 +1005,7 @@ async function runSubAgentLoop(config, systemPrompt, userMessage, toolNames) {
|
|
|
1028
1005
|
{ role: "system", content: systemPrompt },
|
|
1029
1006
|
{ role: "user", content: userMessage }
|
|
1030
1007
|
];
|
|
1031
|
-
const MAX_ITERATIONS =
|
|
1008
|
+
const MAX_ITERATIONS = 100;
|
|
1032
1009
|
let iterations = 0;
|
|
1033
1010
|
for (; ; ) {
|
|
1034
1011
|
if (++iterations > MAX_ITERATIONS) {
|
|
@@ -1046,15 +1023,15 @@ async function runSubAgentLoop(config, systemPrompt, userMessage, toolNames) {
|
|
|
1046
1023
|
}
|
|
1047
1024
|
if (choice.finish_reason === "tool_calls") {
|
|
1048
1025
|
for (const tc of choice.message.tool_calls ?? []) {
|
|
1049
|
-
let
|
|
1026
|
+
let input3;
|
|
1050
1027
|
try {
|
|
1051
|
-
|
|
1028
|
+
input3 = JSON.parse(tc.function.arguments);
|
|
1052
1029
|
} catch {
|
|
1053
|
-
|
|
1030
|
+
input3 = {};
|
|
1054
1031
|
}
|
|
1055
|
-
printToolCall(tc.function.name,
|
|
1032
|
+
printToolCall(tc.function.name, input3);
|
|
1056
1033
|
const mod = selected.find((m) => m.definition.function.name === tc.function.name);
|
|
1057
|
-
const result = mod ? await mod.execute(
|
|
1034
|
+
const result = mod ? await mod.execute(input3, config) : `Unknown tool: ${tc.function.name}`;
|
|
1058
1035
|
printToolResult(result);
|
|
1059
1036
|
messages.push({ role: "tool", tool_call_id: tc.id, content: result });
|
|
1060
1037
|
}
|
|
@@ -1103,14 +1080,14 @@ async function run(_input, config) {
|
|
|
1103
1080
|
return "No active task found. Claim a task first with /pick.";
|
|
1104
1081
|
}
|
|
1105
1082
|
let spinner = ora2("Loading task and diff\u2026").start();
|
|
1106
|
-
const diffPromise = taskState?.baseCommit ? getDiffFromCommit(taskState.baseCommit) : getDiff(
|
|
1107
|
-
const [issue,
|
|
1083
|
+
const diffPromise = taskState?.baseCommit ? getDiffFromCommit(taskState.baseCommit) : getDiff();
|
|
1084
|
+
const [issue, diff, me] = await Promise.all([
|
|
1108
1085
|
getTask(config, issueNumber),
|
|
1109
|
-
getBaseBranch(config),
|
|
1110
1086
|
diffPromise,
|
|
1111
1087
|
getAuthenticatedUser(config)
|
|
1112
1088
|
]);
|
|
1113
1089
|
spinner.stop();
|
|
1090
|
+
const workerBranch = makeWorkerBranchName(issue.author ?? me);
|
|
1114
1091
|
const branch = await getCurrentBranch();
|
|
1115
1092
|
const isSelfSubmit = issue.author !== null && issue.author === me;
|
|
1116
1093
|
let review = "";
|
|
@@ -1175,7 +1152,7 @@ ${issue.body}` : "",
|
|
|
1175
1152
|
## AI Review
|
|
1176
1153
|
${review}` : ""
|
|
1177
1154
|
].join("\n").trim();
|
|
1178
|
-
prUrl = await createPR(config, issue.title, prBody, branch,
|
|
1155
|
+
prUrl = await createPR(config, issue.title, prBody, branch, workerBranch);
|
|
1179
1156
|
spinner.stop();
|
|
1180
1157
|
} catch (err) {
|
|
1181
1158
|
spinner.stop();
|
|
@@ -1194,18 +1171,18 @@ ${review}` : ""
|
|
|
1194
1171
|
Commit: "${commitMessage.trim()}"
|
|
1195
1172
|
PR: ${prUrl}`;
|
|
1196
1173
|
}
|
|
1197
|
-
async function execute(
|
|
1174
|
+
async function execute(input3, config) {
|
|
1198
1175
|
const taskState = getConfig().taskState;
|
|
1199
1176
|
const issueNumber = taskState?.activeIssueNumber;
|
|
1200
1177
|
if (!issueNumber) return "No active task found. Claim a task first.";
|
|
1201
|
-
const diffPromise = taskState?.baseCommit ? getDiffFromCommit(taskState.baseCommit) : getDiff(
|
|
1202
|
-
const [issue,
|
|
1178
|
+
const diffPromise = taskState?.baseCommit ? getDiffFromCommit(taskState.baseCommit) : getDiff();
|
|
1179
|
+
const [issue, diff, branch, me] = await Promise.all([
|
|
1203
1180
|
getTask(config, issueNumber),
|
|
1204
|
-
getBaseBranch(config),
|
|
1205
1181
|
diffPromise,
|
|
1206
1182
|
getCurrentBranch(),
|
|
1207
1183
|
getAuthenticatedUser(config)
|
|
1208
1184
|
]);
|
|
1185
|
+
const workerBranch = makeWorkerBranchName(issue.author ?? me);
|
|
1209
1186
|
const isSelfSubmit = issue.author !== null && issue.author === me;
|
|
1210
1187
|
let review = "";
|
|
1211
1188
|
if (!isSelfSubmit) {
|
|
@@ -1215,7 +1192,7 @@ async function execute(input4, config) {
|
|
|
1215
1192
|
review = `(Review failed: ${err.message})`;
|
|
1216
1193
|
}
|
|
1217
1194
|
}
|
|
1218
|
-
const commitMessage =
|
|
1195
|
+
const commitMessage = input3["commit_message"]?.trim() || `complete: ${issue.title}`;
|
|
1219
1196
|
try {
|
|
1220
1197
|
await stageAllAndCommit(commitMessage);
|
|
1221
1198
|
} catch (err) {
|
|
@@ -1231,7 +1208,7 @@ ${issue.body}` : "",
|
|
|
1231
1208
|
## AI Review
|
|
1232
1209
|
${review}` : ""
|
|
1233
1210
|
].join("\n").trim();
|
|
1234
|
-
prUrl = await createPR(config, issue.title, prBody, branch,
|
|
1211
|
+
prUrl = await createPR(config, issue.title, prBody, branch, workerBranch);
|
|
1235
1212
|
} catch (err) {
|
|
1236
1213
|
return `Committed but PR creation failed: ${err.message}`;
|
|
1237
1214
|
}
|
|
@@ -1272,8 +1249,8 @@ var definition2 = {
|
|
|
1272
1249
|
}
|
|
1273
1250
|
}
|
|
1274
1251
|
};
|
|
1275
|
-
async function run2(
|
|
1276
|
-
let issueNumber =
|
|
1252
|
+
async function run2(input3, config) {
|
|
1253
|
+
let issueNumber = input3["issue_number"];
|
|
1277
1254
|
if (!issueNumber) {
|
|
1278
1255
|
let tasks;
|
|
1279
1256
|
try {
|
|
@@ -1314,8 +1291,8 @@ async function run2(input4, config) {
|
|
|
1314
1291
|
return `Error: ${err.message}`;
|
|
1315
1292
|
}
|
|
1316
1293
|
}
|
|
1317
|
-
async function execute2(
|
|
1318
|
-
const issueNumber =
|
|
1294
|
+
async function execute2(input3, config) {
|
|
1295
|
+
const issueNumber = input3["issue_number"];
|
|
1319
1296
|
const spinner = ora3(`Closing #${issueNumber}\u2026`).start();
|
|
1320
1297
|
try {
|
|
1321
1298
|
await closeTask(config, issueNumber);
|
|
@@ -1348,8 +1325,8 @@ var definition3 = {
|
|
|
1348
1325
|
}
|
|
1349
1326
|
}
|
|
1350
1327
|
};
|
|
1351
|
-
async function run3(
|
|
1352
|
-
const preselected =
|
|
1328
|
+
async function run3(input3, config) {
|
|
1329
|
+
const preselected = input3["issue_number"];
|
|
1353
1330
|
let chosenNumber;
|
|
1354
1331
|
if (preselected !== void 0) {
|
|
1355
1332
|
chosenNumber = preselected;
|
|
@@ -1425,23 +1402,37 @@ Finish or submit it before claiming a new one.`;
|
|
|
1425
1402
|
let spinner = ora4(`Claiming #${issue.number}\u2026`).start();
|
|
1426
1403
|
await claimTask(config, issue.number, me);
|
|
1427
1404
|
spinner.stop();
|
|
1428
|
-
const
|
|
1429
|
-
spinner = ora4(`Switching to
|
|
1430
|
-
let isNew = false;
|
|
1405
|
+
const workerBranch = makeWorkerBranchName(me);
|
|
1406
|
+
spinner = ora4(`Switching to ${workerBranch}\u2026`).start();
|
|
1431
1407
|
try {
|
|
1432
|
-
|
|
1408
|
+
const isNewWorker = await switchToBranchOrCreate(workerBranch);
|
|
1433
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
|
+
}
|
|
1434
1419
|
} catch {
|
|
1435
|
-
spinner.warn(`Could not switch to
|
|
1420
|
+
spinner.warn(`Could not switch to ${workerBranch}`);
|
|
1436
1421
|
}
|
|
1437
|
-
|
|
1438
|
-
|
|
1422
|
+
const branch = makeBranchName(issue.number, me);
|
|
1423
|
+
spinner = ora4(`Creating task branch ${branch}\u2026`).start();
|
|
1424
|
+
try {
|
|
1425
|
+
await switchToBranchOrCreate(branch);
|
|
1426
|
+
spinner.stop();
|
|
1427
|
+
spinner = ora4("Pushing task branch\u2026").start();
|
|
1439
1428
|
try {
|
|
1440
1429
|
await pushBranch(branch);
|
|
1441
1430
|
spinner.stop();
|
|
1442
1431
|
} catch {
|
|
1443
|
-
spinner.warn("Could not push branch");
|
|
1432
|
+
spinner.warn("Could not push task branch");
|
|
1444
1433
|
}
|
|
1434
|
+
} catch {
|
|
1435
|
+
spinner.warn(`Could not create branch ${branch}`);
|
|
1445
1436
|
}
|
|
1446
1437
|
const baseCommit = await getCurrentCommit();
|
|
1447
1438
|
setConfig({ taskState: { activeIssueNumber: issue.number, baseCommit } });
|
|
@@ -1470,9 +1461,9 @@ Finish or submit it before claiming a new one.`;
|
|
|
1470
1461
|
if (action === "close") return run2({ issue_number: issue.number }, config);
|
|
1471
1462
|
return "Cancelled.";
|
|
1472
1463
|
}
|
|
1473
|
-
async function execute3(
|
|
1474
|
-
const issueNumber =
|
|
1475
|
-
const action =
|
|
1464
|
+
async function execute3(input3, config) {
|
|
1465
|
+
const issueNumber = input3["issue_number"];
|
|
1466
|
+
const action = input3["action"];
|
|
1476
1467
|
let issue;
|
|
1477
1468
|
try {
|
|
1478
1469
|
issue = await getTask(config, issueNumber);
|
|
@@ -1499,17 +1490,25 @@ async function execute3(input4, config) {
|
|
|
1499
1490
|
} catch (err) {
|
|
1500
1491
|
return `Error claiming task: ${err.message}`;
|
|
1501
1492
|
}
|
|
1502
|
-
const
|
|
1503
|
-
let isNew = false;
|
|
1493
|
+
const workerBranch = makeWorkerBranchName(me);
|
|
1504
1494
|
try {
|
|
1505
|
-
|
|
1495
|
+
const isNewWorker = await switchToBranchOrCreate(workerBranch);
|
|
1496
|
+
if (isNewWorker) {
|
|
1497
|
+
try {
|
|
1498
|
+
await pushBranch(workerBranch);
|
|
1499
|
+
} catch {
|
|
1500
|
+
}
|
|
1501
|
+
}
|
|
1506
1502
|
} catch {
|
|
1507
1503
|
}
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1504
|
+
const branch = makeBranchName(issueNumber, me);
|
|
1505
|
+
try {
|
|
1506
|
+
await switchToBranchOrCreate(branch);
|
|
1507
|
+
} catch {
|
|
1508
|
+
}
|
|
1509
|
+
try {
|
|
1510
|
+
await pushBranch(branch);
|
|
1511
|
+
} catch {
|
|
1513
1512
|
}
|
|
1514
1513
|
const baseCommit = await getCurrentCommit();
|
|
1515
1514
|
setConfig({ taskState: { activeIssueNumber: issueNumber, baseCommit } });
|
|
@@ -1552,7 +1551,7 @@ List each file path with CREATE/MODIFY, and one sentence describing what changes
|
|
|
1552
1551
|
What the feature/fix receives as input and what it produces or affects.
|
|
1553
1552
|
|
|
1554
1553
|
### \u9A8C\u6536\u6807\u51C6
|
|
1555
|
-
Checkbox list of testable conditions.
|
|
1554
|
+
Checkbox list of only the most essential testable conditions. Maximum 3 items \u2014 include only what is strictly necessary to verify the task is done.
|
|
1556
1555
|
`.trim();
|
|
1557
1556
|
|
|
1558
1557
|
// src/tools/new-task/guide-generator.ts
|
|
@@ -1603,7 +1602,7 @@ var definition4 = {
|
|
|
1603
1602
|
}
|
|
1604
1603
|
}
|
|
1605
1604
|
};
|
|
1606
|
-
async function run4(
|
|
1605
|
+
async function run4(input3, config) {
|
|
1607
1606
|
const authSpinner = ora5("Checking permissions\u2026").start();
|
|
1608
1607
|
let me;
|
|
1609
1608
|
let allowed;
|
|
@@ -1618,7 +1617,7 @@ async function run4(input4, config) {
|
|
|
1618
1617
|
if (!allowed) {
|
|
1619
1618
|
return `Permission denied: only repository collaborators can create tasks.`;
|
|
1620
1619
|
}
|
|
1621
|
-
let title =
|
|
1620
|
+
let title = input3["title"]?.trim();
|
|
1622
1621
|
if (!title) {
|
|
1623
1622
|
try {
|
|
1624
1623
|
title = (await promptInput2({ message: "Task title:" })).trim();
|
|
@@ -1714,13 +1713,13 @@ async function run4(input4, config) {
|
|
|
1714
1713
|
}
|
|
1715
1714
|
return `Created #${issueNumber} "${issueTitle}" \u2014 ${htmlUrl}`;
|
|
1716
1715
|
}
|
|
1717
|
-
async function execute4(
|
|
1716
|
+
async function execute4(input3, config) {
|
|
1718
1717
|
const me = await getAuthenticatedUser(config);
|
|
1719
1718
|
if (!await isCollaborator(config, me)) {
|
|
1720
1719
|
return `Permission denied: only repository collaborators can create tasks.`;
|
|
1721
1720
|
}
|
|
1722
|
-
const title =
|
|
1723
|
-
const feedback =
|
|
1721
|
+
const title = input3["title"].trim();
|
|
1722
|
+
const feedback = input3["feedback"];
|
|
1724
1723
|
let guide = await generateGuide(config, title);
|
|
1725
1724
|
if (feedback) {
|
|
1726
1725
|
guide = await generateGuide(config, title, { feedback, previousGuide: guide });
|
|
@@ -1935,8 +1934,8 @@ var definition9 = {
|
|
|
1935
1934
|
}
|
|
1936
1935
|
}
|
|
1937
1936
|
};
|
|
1938
|
-
async function run9(
|
|
1939
|
-
const issueNumber =
|
|
1937
|
+
async function run9(input3, config) {
|
|
1938
|
+
const issueNumber = input3["issue_number"];
|
|
1940
1939
|
const [me, issue] = await Promise.all([
|
|
1941
1940
|
getAuthenticatedUser(config),
|
|
1942
1941
|
getTask(config, issueNumber)
|
|
@@ -2010,9 +2009,9 @@ async function run9(input4, config) {
|
|
|
2010
2009
|
return `Task #${issueNumber} rejected. Label changed to changes-needed.`;
|
|
2011
2010
|
}
|
|
2012
2011
|
}
|
|
2013
|
-
async function execute9(
|
|
2014
|
-
const issueNumber =
|
|
2015
|
-
const feedback =
|
|
2012
|
+
async function execute9(input3, config) {
|
|
2013
|
+
const issueNumber = input3["issue_number"];
|
|
2014
|
+
const feedback = input3["feedback"];
|
|
2016
2015
|
const [me, issue] = await Promise.all([
|
|
2017
2016
|
getAuthenticatedUser(config),
|
|
2018
2017
|
getTask(config, issueNumber)
|
|
@@ -2059,7 +2058,7 @@ var definition10 = {
|
|
|
2059
2058
|
type: "function",
|
|
2060
2059
|
function: {
|
|
2061
2060
|
name: "accept",
|
|
2062
|
-
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.",
|
|
2063
2062
|
parameters: {
|
|
2064
2063
|
type: "object",
|
|
2065
2064
|
properties: {
|
|
@@ -2069,8 +2068,8 @@ var definition10 = {
|
|
|
2069
2068
|
}
|
|
2070
2069
|
}
|
|
2071
2070
|
};
|
|
2072
|
-
async function run10(
|
|
2073
|
-
let issueNumber =
|
|
2071
|
+
async function run10(input3, config) {
|
|
2072
|
+
let issueNumber = input3["issue_number"];
|
|
2074
2073
|
if (!issueNumber) {
|
|
2075
2074
|
const spinner3 = ora9("Loading tasks for review\u2026").start();
|
|
2076
2075
|
let tasks;
|
|
@@ -2112,11 +2111,11 @@ async function run10(input4, config) {
|
|
|
2112
2111
|
if (issue.author && issue.author !== me2) {
|
|
2113
2112
|
return `Permission denied: only the task author (@${issue.author}) can accept task #${issueNumber}.`;
|
|
2114
2113
|
}
|
|
2115
|
-
const
|
|
2114
|
+
const targetBranch = makeWorkerBranchName(me2);
|
|
2116
2115
|
let confirmed;
|
|
2117
2116
|
try {
|
|
2118
2117
|
confirmed = await select8({
|
|
2119
|
-
message: `Merge PR for #${issueNumber} into ${chalk11.cyan(
|
|
2118
|
+
message: `Merge PR for #${issueNumber} into ${chalk11.cyan(targetBranch)} and close issue?`,
|
|
2120
2119
|
choices: [
|
|
2121
2120
|
{ name: "Yes, accept", value: true },
|
|
2122
2121
|
{ name: "Cancel", value: false }
|
|
@@ -2129,17 +2128,17 @@ async function run10(input4, config) {
|
|
|
2129
2128
|
const spinner = ora9(`Merging PR for #${issueNumber}\u2026`).start();
|
|
2130
2129
|
try {
|
|
2131
2130
|
const result = await acceptTask(config, issueNumber);
|
|
2132
|
-
spinner.succeed(`PR #${result.prNumber} merged into ${
|
|
2131
|
+
spinner.succeed(`PR #${result.prNumber} merged into ${targetBranch}`);
|
|
2133
2132
|
return `Task #${issueNumber} accepted.
|
|
2134
|
-
PR #${result.prNumber} merged \u2192 ${
|
|
2133
|
+
PR #${result.prNumber} merged \u2192 ${targetBranch}
|
|
2135
2134
|
Issue closed.`;
|
|
2136
2135
|
} catch (err) {
|
|
2137
2136
|
spinner.fail("Failed");
|
|
2138
2137
|
return `Error: ${err.message}`;
|
|
2139
2138
|
}
|
|
2140
2139
|
}
|
|
2141
|
-
async function execute10(
|
|
2142
|
-
const issueNumber =
|
|
2140
|
+
async function execute10(input3, config) {
|
|
2141
|
+
const issueNumber = input3["issue_number"];
|
|
2143
2142
|
const [me, issue] = await Promise.all([
|
|
2144
2143
|
getAuthenticatedUser(config),
|
|
2145
2144
|
getTask(config, issueNumber)
|
|
@@ -2147,13 +2146,13 @@ async function execute10(input4, config) {
|
|
|
2147
2146
|
if (issue.author && issue.author !== me) {
|
|
2148
2147
|
return `Permission denied: only the task author (@${issue.author}) can accept task #${issueNumber}.`;
|
|
2149
2148
|
}
|
|
2149
|
+
const targetBranch = makeWorkerBranchName(me);
|
|
2150
2150
|
const spinner = ora9(`Merging PR for #${issueNumber}\u2026`).start();
|
|
2151
2151
|
try {
|
|
2152
2152
|
const result = await acceptTask(config, issueNumber);
|
|
2153
2153
|
spinner.stop();
|
|
2154
|
-
const baseBranch = config.github.baseBranch ?? "main";
|
|
2155
2154
|
return `Task #${issueNumber} accepted.
|
|
2156
|
-
PR #${result.prNumber} merged \u2192 ${
|
|
2155
|
+
PR #${result.prNumber} merged \u2192 ${targetBranch}
|
|
2157
2156
|
Issue closed.`;
|
|
2158
2157
|
} catch (err) {
|
|
2159
2158
|
spinner.stop();
|
|
@@ -2189,8 +2188,8 @@ var definition11 = {
|
|
|
2189
2188
|
}
|
|
2190
2189
|
}
|
|
2191
2190
|
};
|
|
2192
|
-
async function run11(
|
|
2193
|
-
let issueNumber =
|
|
2191
|
+
async function run11(input3, config) {
|
|
2192
|
+
let issueNumber = input3["issue_number"];
|
|
2194
2193
|
if (!issueNumber) {
|
|
2195
2194
|
let tasks;
|
|
2196
2195
|
try {
|
|
@@ -2241,10 +2240,10 @@ async function run11(input4, config) {
|
|
|
2241
2240
|
return `Error: ${err.message}`;
|
|
2242
2241
|
}
|
|
2243
2242
|
}
|
|
2244
|
-
async function execute11(
|
|
2245
|
-
const issueNumber =
|
|
2246
|
-
const title =
|
|
2247
|
-
const body =
|
|
2243
|
+
async function execute11(input3, config) {
|
|
2244
|
+
const issueNumber = input3["issue_number"];
|
|
2245
|
+
const title = input3["title"];
|
|
2246
|
+
const body = input3["body"];
|
|
2248
2247
|
const spinner = ora10(`Updating #${issueNumber}\u2026`).start();
|
|
2249
2248
|
try {
|
|
2250
2249
|
await editTask(config, issueNumber, title, body);
|
|
@@ -2307,8 +2306,8 @@ var definition13 = {
|
|
|
2307
2306
|
}
|
|
2308
2307
|
}
|
|
2309
2308
|
};
|
|
2310
|
-
async function execute13(
|
|
2311
|
-
const issue = await getTask(config,
|
|
2309
|
+
async function execute13(input3, config) {
|
|
2310
|
+
const issue = await getTask(config, input3["issue_number"]);
|
|
2312
2311
|
const status = issue.labels.find((l) => l.startsWith("techunter:"))?.replace("techunter:", "") ?? "unknown";
|
|
2313
2312
|
const assignee = issue.assignee ? `@${issue.assignee}` : "\u2014";
|
|
2314
2313
|
const lines = [
|
|
@@ -2344,9 +2343,9 @@ var definition14 = {
|
|
|
2344
2343
|
}
|
|
2345
2344
|
}
|
|
2346
2345
|
};
|
|
2347
|
-
async function execute14(
|
|
2348
|
-
const issueNumber =
|
|
2349
|
-
const limit =
|
|
2346
|
+
async function execute14(input3, config) {
|
|
2347
|
+
const issueNumber = input3["issue_number"];
|
|
2348
|
+
const limit = input3["limit"] ?? 5;
|
|
2350
2349
|
const spinner = ora11(`Loading comments for #${issueNumber}...`).start();
|
|
2351
2350
|
try {
|
|
2352
2351
|
const comments = await listComments(config, issueNumber, limit);
|
|
@@ -2381,7 +2380,7 @@ var definition15 = {
|
|
|
2381
2380
|
async function execute15(_input, _config) {
|
|
2382
2381
|
const spinner = ora12("Reading git diff...").start();
|
|
2383
2382
|
try {
|
|
2384
|
-
const diff = await getDiff(
|
|
2383
|
+
const diff = await getDiff();
|
|
2385
2384
|
spinner.stop();
|
|
2386
2385
|
return diff;
|
|
2387
2386
|
} catch (err) {
|
|
@@ -2414,8 +2413,8 @@ var definition16 = {
|
|
|
2414
2413
|
}
|
|
2415
2414
|
}
|
|
2416
2415
|
};
|
|
2417
|
-
async function execute16(
|
|
2418
|
-
const command =
|
|
2416
|
+
async function execute16(input3, _config) {
|
|
2417
|
+
const command = input3["command"];
|
|
2419
2418
|
const cwd = process.cwd();
|
|
2420
2419
|
const spinner = ora13(`$ ${command}`).start();
|
|
2421
2420
|
try {
|
|
@@ -2607,8 +2606,8 @@ var definition17 = {
|
|
|
2607
2606
|
}
|
|
2608
2607
|
}
|
|
2609
2608
|
};
|
|
2610
|
-
async function execute17(
|
|
2611
|
-
const focus =
|
|
2609
|
+
async function execute17(input3, _config) {
|
|
2610
|
+
const focus = input3["focus"] ?? "";
|
|
2612
2611
|
const spinner = ora14("Scanning project...").start();
|
|
2613
2612
|
try {
|
|
2614
2613
|
const cwd = process.cwd();
|
|
@@ -2657,8 +2656,8 @@ var definition18 = {
|
|
|
2657
2656
|
}
|
|
2658
2657
|
}
|
|
2659
2658
|
};
|
|
2660
|
-
async function execute18(
|
|
2661
|
-
const filePath =
|
|
2659
|
+
async function execute18(input3, _config) {
|
|
2660
|
+
const filePath = input3["path"];
|
|
2662
2661
|
try {
|
|
2663
2662
|
const fullPath = path3.join(process.cwd(), filePath);
|
|
2664
2663
|
const content = await readFile3(fullPath, "utf-8");
|
|
@@ -2695,9 +2694,9 @@ var definition19 = {
|
|
|
2695
2694
|
}
|
|
2696
2695
|
}
|
|
2697
2696
|
};
|
|
2698
|
-
async function execute19(
|
|
2699
|
-
const question =
|
|
2700
|
-
const options =
|
|
2697
|
+
async function execute19(input3, _config) {
|
|
2698
|
+
const question = input3["question"];
|
|
2699
|
+
const options = input3["options"];
|
|
2701
2700
|
const OTHER = "__other__";
|
|
2702
2701
|
console.log("");
|
|
2703
2702
|
console.log(chalk12.dim(" \u250C\u2500 Agent question " + "\u2500".repeat(51)));
|
|
@@ -2750,12 +2749,12 @@ var toolModules = [
|
|
|
2750
2749
|
|
|
2751
2750
|
// src/lib/agent.ts
|
|
2752
2751
|
var tools = toolModules.map((m) => m.definition);
|
|
2753
|
-
async function executeTool(name,
|
|
2752
|
+
async function executeTool(name, input3, config) {
|
|
2754
2753
|
const mod = toolModules.find((m) => m.definition.function.name === name);
|
|
2755
2754
|
if (!mod) return `Unknown tool: ${name}`;
|
|
2756
2755
|
try {
|
|
2757
2756
|
const fn = mod.run ?? mod.execute;
|
|
2758
|
-
return await fn(
|
|
2757
|
+
return await fn(input3, config);
|
|
2759
2758
|
} catch (err) {
|
|
2760
2759
|
return `Error: ${err.message}`;
|
|
2761
2760
|
}
|
|
@@ -2802,7 +2801,7 @@ async function runAgentLoop(config, messages) {
|
|
|
2802
2801
|
"5. To reject: write a structured rejection comment and call reject."
|
|
2803
2802
|
].join("\n")
|
|
2804
2803
|
};
|
|
2805
|
-
const MAX_ITERATIONS =
|
|
2804
|
+
const MAX_ITERATIONS = 100;
|
|
2806
2805
|
let iterations = 0;
|
|
2807
2806
|
for (; ; ) {
|
|
2808
2807
|
if (++iterations > MAX_ITERATIONS) {
|
|
@@ -2959,13 +2958,9 @@ async function initNewRepo(config, owner, repo) {
|
|
|
2959
2958
|
console.log("");
|
|
2960
2959
|
console.log(chalk14.bold.cyan(` New repo detected: ${owner}/${repo}`));
|
|
2961
2960
|
console.log(chalk14.dim(" Setting up Techunter for this repository...\n"));
|
|
2962
|
-
const baseBranch = await input3({
|
|
2963
|
-
message: "Main branch to merge PRs into:",
|
|
2964
|
-
default: "main"
|
|
2965
|
-
});
|
|
2966
2961
|
const newConfig = {
|
|
2967
2962
|
...config,
|
|
2968
|
-
github: { owner, repo
|
|
2963
|
+
github: { owner, repo }
|
|
2969
2964
|
};
|
|
2970
2965
|
setConfig({ github: newConfig.github });
|
|
2971
2966
|
const ora16 = (await import("ora")).default;
|
package/dist/mcp.js
CHANGED
|
@@ -21,7 +21,6 @@ __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,
|
|
27
26
|
isCollaborator: () => isCollaborator,
|
|
@@ -309,10 +308,6 @@ async function getDefaultBranch(config) {
|
|
|
309
308
|
const { data } = await octokit.repos.get({ owner, repo });
|
|
310
309
|
return data.default_branch;
|
|
311
310
|
}
|
|
312
|
-
function getBaseBranch(config) {
|
|
313
|
-
if (config.github.baseBranch) return Promise.resolve(config.github.baseBranch);
|
|
314
|
-
return getDefaultBranch(config);
|
|
315
|
-
}
|
|
316
311
|
async function acceptTask(config, issueNumber) {
|
|
317
312
|
const octokit = createOctokit(config.githubToken);
|
|
318
313
|
const { owner, repo } = config.github;
|
|
@@ -375,6 +370,10 @@ async function getCurrentBranch() {
|
|
|
375
370
|
async function pushBranch(name) {
|
|
376
371
|
await git.push("origin", name, ["--set-upstream"]);
|
|
377
372
|
}
|
|
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
|
+
}
|
|
378
377
|
function makeWorkerBranchName(username) {
|
|
379
378
|
const slug = username.toLowerCase().replace(/[^a-z0-9-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "") || "user";
|
|
380
379
|
return `worker-${slug}`;
|
|
@@ -499,8 +498,7 @@ var configSchema = z.object({
|
|
|
499
498
|
githubClientId: z.string().optional(),
|
|
500
499
|
github: z.object({
|
|
501
500
|
owner: z.string().min(1),
|
|
502
|
-
repo: z.string().min(1)
|
|
503
|
-
baseBranch: z.string().optional()
|
|
501
|
+
repo: z.string().min(1)
|
|
504
502
|
}),
|
|
505
503
|
taskState: z.object({
|
|
506
504
|
activeIssueNumber: z.number().optional(),
|
|
@@ -723,7 +721,7 @@ async function runSubAgentLoop(config, systemPrompt, userMessage, toolNames) {
|
|
|
723
721
|
{ role: "system", content: systemPrompt },
|
|
724
722
|
{ role: "user", content: userMessage }
|
|
725
723
|
];
|
|
726
|
-
const MAX_ITERATIONS =
|
|
724
|
+
const MAX_ITERATIONS = 100;
|
|
727
725
|
let iterations = 0;
|
|
728
726
|
for (; ; ) {
|
|
729
727
|
if (++iterations > MAX_ITERATIONS) {
|
|
@@ -798,14 +796,14 @@ async function run(_input, config) {
|
|
|
798
796
|
return "No active task found. Claim a task first with /pick.";
|
|
799
797
|
}
|
|
800
798
|
let spinner = ora("Loading task and diff\u2026").start();
|
|
801
|
-
const diffPromise = taskState?.baseCommit ? getDiffFromCommit(taskState.baseCommit) : getDiff(
|
|
802
|
-
const [issue,
|
|
799
|
+
const diffPromise = taskState?.baseCommit ? getDiffFromCommit(taskState.baseCommit) : getDiff();
|
|
800
|
+
const [issue, diff, me] = await Promise.all([
|
|
803
801
|
getTask(config, issueNumber),
|
|
804
|
-
getBaseBranch(config),
|
|
805
802
|
diffPromise,
|
|
806
803
|
getAuthenticatedUser(config)
|
|
807
804
|
]);
|
|
808
805
|
spinner.stop();
|
|
806
|
+
const workerBranch = makeWorkerBranchName(issue.author ?? me);
|
|
809
807
|
const branch = await getCurrentBranch();
|
|
810
808
|
const isSelfSubmit = issue.author !== null && issue.author === me;
|
|
811
809
|
let review = "";
|
|
@@ -870,7 +868,7 @@ ${issue.body}` : "",
|
|
|
870
868
|
## AI Review
|
|
871
869
|
${review}` : ""
|
|
872
870
|
].join("\n").trim();
|
|
873
|
-
prUrl = await createPR(config, issue.title, prBody, branch,
|
|
871
|
+
prUrl = await createPR(config, issue.title, prBody, branch, workerBranch);
|
|
874
872
|
spinner.stop();
|
|
875
873
|
} catch (err) {
|
|
876
874
|
spinner.stop();
|
|
@@ -893,14 +891,14 @@ async function execute(input, config) {
|
|
|
893
891
|
const taskState = getConfig().taskState;
|
|
894
892
|
const issueNumber = taskState?.activeIssueNumber;
|
|
895
893
|
if (!issueNumber) return "No active task found. Claim a task first.";
|
|
896
|
-
const diffPromise = taskState?.baseCommit ? getDiffFromCommit(taskState.baseCommit) : getDiff(
|
|
897
|
-
const [issue,
|
|
894
|
+
const diffPromise = taskState?.baseCommit ? getDiffFromCommit(taskState.baseCommit) : getDiff();
|
|
895
|
+
const [issue, diff, branch, me] = await Promise.all([
|
|
898
896
|
getTask(config, issueNumber),
|
|
899
|
-
getBaseBranch(config),
|
|
900
897
|
diffPromise,
|
|
901
898
|
getCurrentBranch(),
|
|
902
899
|
getAuthenticatedUser(config)
|
|
903
900
|
]);
|
|
901
|
+
const workerBranch = makeWorkerBranchName(issue.author ?? me);
|
|
904
902
|
const isSelfSubmit = issue.author !== null && issue.author === me;
|
|
905
903
|
let review = "";
|
|
906
904
|
if (!isSelfSubmit) {
|
|
@@ -926,7 +924,7 @@ ${issue.body}` : "",
|
|
|
926
924
|
## AI Review
|
|
927
925
|
${review}` : ""
|
|
928
926
|
].join("\n").trim();
|
|
929
|
-
prUrl = await createPR(config, issue.title, prBody, branch,
|
|
927
|
+
prUrl = await createPR(config, issue.title, prBody, branch, workerBranch);
|
|
930
928
|
} catch (err) {
|
|
931
929
|
return `Committed but PR creation failed: ${err.message}`;
|
|
932
930
|
}
|
|
@@ -1120,23 +1118,37 @@ Finish or submit it before claiming a new one.`;
|
|
|
1120
1118
|
let spinner = ora3(`Claiming #${issue.number}\u2026`).start();
|
|
1121
1119
|
await claimTask(config, issue.number, me);
|
|
1122
1120
|
spinner.stop();
|
|
1123
|
-
const
|
|
1124
|
-
spinner = ora3(`Switching to
|
|
1125
|
-
let isNew = false;
|
|
1121
|
+
const workerBranch = makeWorkerBranchName(me);
|
|
1122
|
+
spinner = ora3(`Switching to ${workerBranch}\u2026`).start();
|
|
1126
1123
|
try {
|
|
1127
|
-
|
|
1124
|
+
const isNewWorker = await switchToBranchOrCreate(workerBranch);
|
|
1128
1125
|
spinner.stop();
|
|
1126
|
+
if (isNewWorker) {
|
|
1127
|
+
spinner = ora3("Pushing worker branch\u2026").start();
|
|
1128
|
+
try {
|
|
1129
|
+
await pushBranch(workerBranch);
|
|
1130
|
+
spinner.stop();
|
|
1131
|
+
} catch {
|
|
1132
|
+
spinner.warn("Could not push worker branch");
|
|
1133
|
+
}
|
|
1134
|
+
}
|
|
1129
1135
|
} catch {
|
|
1130
|
-
spinner.warn(`Could not switch to
|
|
1136
|
+
spinner.warn(`Could not switch to ${workerBranch}`);
|
|
1131
1137
|
}
|
|
1132
|
-
|
|
1133
|
-
|
|
1138
|
+
const branch = makeBranchName(issue.number, me);
|
|
1139
|
+
spinner = ora3(`Creating task branch ${branch}\u2026`).start();
|
|
1140
|
+
try {
|
|
1141
|
+
await switchToBranchOrCreate(branch);
|
|
1142
|
+
spinner.stop();
|
|
1143
|
+
spinner = ora3("Pushing task branch\u2026").start();
|
|
1134
1144
|
try {
|
|
1135
1145
|
await pushBranch(branch);
|
|
1136
1146
|
spinner.stop();
|
|
1137
1147
|
} catch {
|
|
1138
|
-
spinner.warn("Could not push branch");
|
|
1148
|
+
spinner.warn("Could not push task branch");
|
|
1139
1149
|
}
|
|
1150
|
+
} catch {
|
|
1151
|
+
spinner.warn(`Could not create branch ${branch}`);
|
|
1140
1152
|
}
|
|
1141
1153
|
const baseCommit = await getCurrentCommit();
|
|
1142
1154
|
setConfig({ taskState: { activeIssueNumber: issue.number, baseCommit } });
|
|
@@ -1194,17 +1206,25 @@ async function execute3(input, config) {
|
|
|
1194
1206
|
} catch (err) {
|
|
1195
1207
|
return `Error claiming task: ${err.message}`;
|
|
1196
1208
|
}
|
|
1197
|
-
const
|
|
1198
|
-
let isNew = false;
|
|
1209
|
+
const workerBranch = makeWorkerBranchName(me);
|
|
1199
1210
|
try {
|
|
1200
|
-
|
|
1211
|
+
const isNewWorker = await switchToBranchOrCreate(workerBranch);
|
|
1212
|
+
if (isNewWorker) {
|
|
1213
|
+
try {
|
|
1214
|
+
await pushBranch(workerBranch);
|
|
1215
|
+
} catch {
|
|
1216
|
+
}
|
|
1217
|
+
}
|
|
1201
1218
|
} catch {
|
|
1202
1219
|
}
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1220
|
+
const branch = makeBranchName(issueNumber, me);
|
|
1221
|
+
try {
|
|
1222
|
+
await switchToBranchOrCreate(branch);
|
|
1223
|
+
} catch {
|
|
1224
|
+
}
|
|
1225
|
+
try {
|
|
1226
|
+
await pushBranch(branch);
|
|
1227
|
+
} catch {
|
|
1208
1228
|
}
|
|
1209
1229
|
const baseCommit = await getCurrentCommit();
|
|
1210
1230
|
setConfig({ taskState: { activeIssueNumber: issueNumber, baseCommit } });
|
|
@@ -1247,7 +1267,7 @@ List each file path with CREATE/MODIFY, and one sentence describing what changes
|
|
|
1247
1267
|
What the feature/fix receives as input and what it produces or affects.
|
|
1248
1268
|
|
|
1249
1269
|
### \u9A8C\u6536\u6807\u51C6
|
|
1250
|
-
Checkbox list of testable conditions.
|
|
1270
|
+
Checkbox list of only the most essential testable conditions. Maximum 3 items \u2014 include only what is strictly necessary to verify the task is done.
|
|
1251
1271
|
`.trim();
|
|
1252
1272
|
|
|
1253
1273
|
// src/tools/new-task/guide-generator.ts
|
|
@@ -1754,7 +1774,7 @@ var definition10 = {
|
|
|
1754
1774
|
type: "function",
|
|
1755
1775
|
function: {
|
|
1756
1776
|
name: "accept",
|
|
1757
|
-
description: "Accept an in-review task: merges the PR into
|
|
1777
|
+
description: "Accept an in-review task: merges the PR into your worker branch and closes the issue.",
|
|
1758
1778
|
parameters: {
|
|
1759
1779
|
type: "object",
|
|
1760
1780
|
properties: {
|
|
@@ -1807,11 +1827,11 @@ async function run10(input, config) {
|
|
|
1807
1827
|
if (issue.author && issue.author !== me2) {
|
|
1808
1828
|
return `Permission denied: only the task author (@${issue.author}) can accept task #${issueNumber}.`;
|
|
1809
1829
|
}
|
|
1810
|
-
const
|
|
1830
|
+
const targetBranch = makeWorkerBranchName(me2);
|
|
1811
1831
|
let confirmed;
|
|
1812
1832
|
try {
|
|
1813
1833
|
confirmed = await select6({
|
|
1814
|
-
message: `Merge PR for #${issueNumber} into ${chalk9.cyan(
|
|
1834
|
+
message: `Merge PR for #${issueNumber} into ${chalk9.cyan(targetBranch)} and close issue?`,
|
|
1815
1835
|
choices: [
|
|
1816
1836
|
{ name: "Yes, accept", value: true },
|
|
1817
1837
|
{ name: "Cancel", value: false }
|
|
@@ -1824,9 +1844,9 @@ async function run10(input, config) {
|
|
|
1824
1844
|
const spinner = ora8(`Merging PR for #${issueNumber}\u2026`).start();
|
|
1825
1845
|
try {
|
|
1826
1846
|
const result = await acceptTask(config, issueNumber);
|
|
1827
|
-
spinner.succeed(`PR #${result.prNumber} merged into ${
|
|
1847
|
+
spinner.succeed(`PR #${result.prNumber} merged into ${targetBranch}`);
|
|
1828
1848
|
return `Task #${issueNumber} accepted.
|
|
1829
|
-
PR #${result.prNumber} merged \u2192 ${
|
|
1849
|
+
PR #${result.prNumber} merged \u2192 ${targetBranch}
|
|
1830
1850
|
Issue closed.`;
|
|
1831
1851
|
} catch (err) {
|
|
1832
1852
|
spinner.fail("Failed");
|
|
@@ -1842,13 +1862,13 @@ async function execute10(input, config) {
|
|
|
1842
1862
|
if (issue.author && issue.author !== me) {
|
|
1843
1863
|
return `Permission denied: only the task author (@${issue.author}) can accept task #${issueNumber}.`;
|
|
1844
1864
|
}
|
|
1865
|
+
const targetBranch = makeWorkerBranchName(me);
|
|
1845
1866
|
const spinner = ora8(`Merging PR for #${issueNumber}\u2026`).start();
|
|
1846
1867
|
try {
|
|
1847
1868
|
const result = await acceptTask(config, issueNumber);
|
|
1848
1869
|
spinner.stop();
|
|
1849
|
-
const baseBranch = config.github.baseBranch ?? "main";
|
|
1850
1870
|
return `Task #${issueNumber} accepted.
|
|
1851
|
-
PR #${result.prNumber} merged \u2192 ${
|
|
1871
|
+
PR #${result.prNumber} merged \u2192 ${targetBranch}
|
|
1852
1872
|
Issue closed.`;
|
|
1853
1873
|
} catch (err) {
|
|
1854
1874
|
spinner.stop();
|
|
@@ -2076,7 +2096,7 @@ var definition15 = {
|
|
|
2076
2096
|
async function execute15(_input, _config) {
|
|
2077
2097
|
const spinner = ora11("Reading git diff...").start();
|
|
2078
2098
|
try {
|
|
2079
|
-
const diff = await getDiff(
|
|
2099
|
+
const diff = await getDiff();
|
|
2080
2100
|
spinner.stop();
|
|
2081
2101
|
return diff;
|
|
2082
2102
|
} catch (err) {
|