techunter 0.1.11 → 1.0.1
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 +135 -107
- package/dist/index.js +1960 -1003
- package/dist/mcp.js +1212 -382
- package/package.json +1 -1
package/dist/mcp.js
CHANGED
|
@@ -39,12 +39,22 @@ __export(github_exports, {
|
|
|
39
39
|
createTask: () => createTask,
|
|
40
40
|
editTask: () => editTask,
|
|
41
41
|
embedBaseCommit: () => embedBaseCommit,
|
|
42
|
+
embedTargetBranch: () => embedTargetBranch,
|
|
42
43
|
ensureLabels: () => ensureLabels,
|
|
44
|
+
ensureRemoteBranch: () => ensureRemoteBranch,
|
|
43
45
|
extractBaseCommit: () => extractBaseCommit,
|
|
46
|
+
extractTargetBranch: () => extractTargetBranch,
|
|
44
47
|
formatGuideAsMarkdown: () => formatGuideAsMarkdown,
|
|
45
48
|
getAuthenticatedUser: () => getAuthenticatedUser,
|
|
49
|
+
getBranchHeadSha: () => getBranchHeadSha,
|
|
46
50
|
getDefaultBranch: () => getDefaultBranch,
|
|
51
|
+
getIssueNumberFromBranch: () => getIssueNumberFromBranch,
|
|
52
|
+
getOpenSubtasks: () => getOpenSubtasks,
|
|
53
|
+
getRepoFile: () => getRepoFile,
|
|
47
54
|
getTask: () => getTask,
|
|
55
|
+
getTaskBranch: () => getTaskBranch,
|
|
56
|
+
getTaskPR: () => getTaskPR,
|
|
57
|
+
getTaskPRDiff: () => getTaskPRDiff,
|
|
48
58
|
isCollaborator: () => isCollaborator,
|
|
49
59
|
listComments: () => listComments,
|
|
50
60
|
listMyTasks: () => listMyTasks,
|
|
@@ -52,9 +62,11 @@ __export(github_exports, {
|
|
|
52
62
|
listTasksForReview: () => listTasksForReview,
|
|
53
63
|
markInReview: () => markInReview,
|
|
54
64
|
mergeWorkerIntoBase: () => mergeWorkerIntoBase,
|
|
65
|
+
moveTask: () => moveTask,
|
|
55
66
|
postComment: () => postComment,
|
|
56
67
|
postGuideComment: () => postGuideComment,
|
|
57
|
-
rejectTask: () => rejectTask
|
|
68
|
+
rejectTask: () => rejectTask,
|
|
69
|
+
upsertRepoFile: () => upsertRepoFile
|
|
58
70
|
});
|
|
59
71
|
import { Octokit } from "@octokit/rest";
|
|
60
72
|
import { fetch as undiciFetch } from "undici";
|
|
@@ -117,11 +129,22 @@ function extractBaseCommit(body) {
|
|
|
117
129
|
const match = body.match(/<!-- techunter-base:([a-f0-9]{7,40}) -->/);
|
|
118
130
|
return match?.[1] ?? null;
|
|
119
131
|
}
|
|
120
|
-
|
|
132
|
+
function embedTargetBranch(body, branch) {
|
|
133
|
+
return `${body}
|
|
134
|
+
${TARGET_BRANCH_MARKER}${branch} -->`;
|
|
135
|
+
}
|
|
136
|
+
function extractTargetBranch(body) {
|
|
137
|
+
if (!body) return null;
|
|
138
|
+
const match = body.match(/<!-- techunter-target:([^\s>]+) -->/);
|
|
139
|
+
return match?.[1] ?? null;
|
|
140
|
+
}
|
|
141
|
+
async function createTask(config, title, body, baseCommit, targetBranch) {
|
|
121
142
|
const octokit = createOctokit(config.githubToken);
|
|
122
143
|
const { owner, repo } = config.github;
|
|
123
144
|
await ensureLabels(config);
|
|
124
|
-
|
|
145
|
+
let finalBody = body ?? "";
|
|
146
|
+
if (baseCommit) finalBody = embedBaseCommit(finalBody, baseCommit);
|
|
147
|
+
if (targetBranch) finalBody = embedTargetBranch(finalBody, targetBranch);
|
|
125
148
|
const { data } = await octokit.issues.create({
|
|
126
149
|
owner,
|
|
127
150
|
repo,
|
|
@@ -134,13 +157,22 @@ async function createTask(config, title, body, baseCommit) {
|
|
|
134
157
|
async function mergeWorkerIntoBase(config, workerBranch, baseBranch) {
|
|
135
158
|
const octokit = createOctokit(config.githubToken);
|
|
136
159
|
const { owner, repo } = config.github;
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
160
|
+
try {
|
|
161
|
+
await octokit.repos.merge({
|
|
162
|
+
owner,
|
|
163
|
+
repo,
|
|
164
|
+
base: baseBranch,
|
|
165
|
+
head: workerBranch,
|
|
166
|
+
commit_message: `chore: merge ${workerBranch} into ${baseBranch}`
|
|
167
|
+
});
|
|
168
|
+
} catch (err) {
|
|
169
|
+
if (err.status === 409) {
|
|
170
|
+
throw new Error(
|
|
171
|
+
`Merge conflict: ${workerBranch} cannot be merged into ${baseBranch} cleanly. Resolve conflicts manually.`
|
|
172
|
+
);
|
|
173
|
+
}
|
|
174
|
+
throw err;
|
|
175
|
+
}
|
|
144
176
|
}
|
|
145
177
|
async function claimTask(config, number, username) {
|
|
146
178
|
const octokit = createOctokit(config.githubToken);
|
|
@@ -216,6 +248,23 @@ async function postGuideComment(config, number, guide) {
|
|
|
216
248
|
body
|
|
217
249
|
});
|
|
218
250
|
}
|
|
251
|
+
async function ensureRemoteBranch(config, branchName, fallbackBase) {
|
|
252
|
+
const octokit = createOctokit(config.githubToken);
|
|
253
|
+
const { owner, repo } = config.github;
|
|
254
|
+
try {
|
|
255
|
+
await octokit.repos.getBranch({ owner, repo, branch: branchName });
|
|
256
|
+
return;
|
|
257
|
+
} catch (err) {
|
|
258
|
+
if (err.status !== 404) throw err;
|
|
259
|
+
}
|
|
260
|
+
const { data: baseRef } = await octokit.repos.getBranch({ owner, repo, branch: fallbackBase });
|
|
261
|
+
await octokit.git.createRef({
|
|
262
|
+
owner,
|
|
263
|
+
repo,
|
|
264
|
+
ref: `refs/heads/${branchName}`,
|
|
265
|
+
sha: baseRef.commit.sha
|
|
266
|
+
});
|
|
267
|
+
}
|
|
219
268
|
async function createPR(config, title, body, branch, base) {
|
|
220
269
|
const octokit = createOctokit(config.githubToken);
|
|
221
270
|
const { owner, repo } = config.github;
|
|
@@ -232,14 +281,11 @@ async function createPR(config, title, body, branch, base) {
|
|
|
232
281
|
async function markInReview(config, number) {
|
|
233
282
|
const octokit = createOctokit(config.githubToken);
|
|
234
283
|
const { owner, repo } = config.github;
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
owner,
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
name: LABEL_CLAIMED
|
|
241
|
-
});
|
|
242
|
-
} catch {
|
|
284
|
+
for (const label of [LABEL_CLAIMED, LABEL_CHANGES_NEEDED]) {
|
|
285
|
+
try {
|
|
286
|
+
await octokit.issues.removeLabel({ owner, repo, issue_number: number, name: label });
|
|
287
|
+
} catch {
|
|
288
|
+
}
|
|
243
289
|
}
|
|
244
290
|
await octokit.issues.addLabels({
|
|
245
291
|
owner,
|
|
@@ -354,28 +400,146 @@ async function editTask(config, number, title, body) {
|
|
|
354
400
|
const { owner, repo } = config.github;
|
|
355
401
|
await octokit.issues.update({ owner, repo, issue_number: number, title, body });
|
|
356
402
|
}
|
|
403
|
+
async function upsertRepoFile(config, filePath, content, message) {
|
|
404
|
+
const octokit = createOctokit(config.githubToken);
|
|
405
|
+
const { owner, repo } = config.github;
|
|
406
|
+
let sha;
|
|
407
|
+
try {
|
|
408
|
+
const { data: data2 } = await octokit.repos.getContent({ owner, repo, path: filePath });
|
|
409
|
+
if (!Array.isArray(data2) && data2.type === "file") {
|
|
410
|
+
sha = data2.sha;
|
|
411
|
+
}
|
|
412
|
+
} catch {
|
|
413
|
+
}
|
|
414
|
+
const { data } = await octokit.repos.createOrUpdateFileContents({
|
|
415
|
+
owner,
|
|
416
|
+
repo,
|
|
417
|
+
path: filePath,
|
|
418
|
+
message,
|
|
419
|
+
content: Buffer.from(content, "utf-8").toString("base64"),
|
|
420
|
+
...sha ? { sha } : {}
|
|
421
|
+
});
|
|
422
|
+
return data.content?.html_url ?? `https://github.com/${owner}/${repo}/blob/main/${filePath}`;
|
|
423
|
+
}
|
|
424
|
+
async function getRepoFile(config, filePath) {
|
|
425
|
+
const octokit = createOctokit(config.githubToken);
|
|
426
|
+
const { owner, repo } = config.github;
|
|
427
|
+
try {
|
|
428
|
+
const { data } = await octokit.repos.getContent({ owner, repo, path: filePath });
|
|
429
|
+
if (!Array.isArray(data) && data.type === "file" && "content" in data) {
|
|
430
|
+
return Buffer.from(data.content, "base64").toString("utf-8");
|
|
431
|
+
}
|
|
432
|
+
return null;
|
|
433
|
+
} catch {
|
|
434
|
+
return null;
|
|
435
|
+
}
|
|
436
|
+
}
|
|
357
437
|
async function getDefaultBranch(config) {
|
|
358
438
|
const octokit = createOctokit(config.githubToken);
|
|
359
439
|
const { owner, repo } = config.github;
|
|
360
440
|
const { data } = await octokit.repos.get({ owner, repo });
|
|
361
441
|
return data.default_branch;
|
|
362
442
|
}
|
|
363
|
-
async function
|
|
443
|
+
async function getTaskBranch(config, issueNumber) {
|
|
364
444
|
const octokit = createOctokit(config.githubToken);
|
|
365
445
|
const { owner, repo } = config.github;
|
|
366
446
|
const { data: prs } = await octokit.pulls.list({ owner, repo, state: "open", per_page: 100 });
|
|
367
|
-
const pr =
|
|
368
|
-
if (
|
|
369
|
-
const { data:
|
|
447
|
+
const pr = prs.find((p) => new RegExp(`Closes #${issueNumber}\\b`, "i").test(p.body ?? ""));
|
|
448
|
+
if (pr) return pr.head.ref;
|
|
449
|
+
const { data: branches } = await octokit.repos.listBranches({ owner, repo, per_page: 100 });
|
|
450
|
+
const taskBranch = branches.find((b) => new RegExp(`^task-${issueNumber}-`).test(b.name));
|
|
451
|
+
return taskBranch?.name ?? null;
|
|
452
|
+
}
|
|
453
|
+
async function getBranchHeadSha(config, branchName) {
|
|
454
|
+
const octokit = createOctokit(config.githubToken);
|
|
455
|
+
const { owner, repo } = config.github;
|
|
456
|
+
try {
|
|
457
|
+
const { data } = await octokit.repos.getBranch({ owner, repo, branch: branchName });
|
|
458
|
+
return data.commit.sha;
|
|
459
|
+
} catch {
|
|
460
|
+
return null;
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
async function moveTask(config, issueNumber, newTargetBranch, newBaseCommit) {
|
|
464
|
+
const octokit = createOctokit(config.githubToken);
|
|
465
|
+
const { owner, repo } = config.github;
|
|
466
|
+
const { data } = await octokit.issues.get({ owner, repo, issue_number: issueNumber });
|
|
467
|
+
let body = data.body ?? "";
|
|
468
|
+
body = body.replace(/\n*<!-- techunter-base:[a-f0-9]{7,40} -->/g, "");
|
|
469
|
+
body = body.replace(/\n*<!-- techunter-target:[^\s>]+ -->/g, "");
|
|
470
|
+
body = embedBaseCommit(body, newBaseCommit);
|
|
471
|
+
body = embedTargetBranch(body, newTargetBranch);
|
|
472
|
+
await octokit.issues.update({ owner, repo, issue_number: issueNumber, body });
|
|
473
|
+
}
|
|
474
|
+
async function getTaskPR(config, issueNumber) {
|
|
475
|
+
const octokit = createOctokit(config.githubToken);
|
|
476
|
+
const { owner, repo } = config.github;
|
|
477
|
+
const { data: prs } = await octokit.pulls.list({ owner, repo, state: "open", per_page: 100 });
|
|
478
|
+
const pr = prs.find(
|
|
479
|
+
(p) => new RegExp(`Closes #${issueNumber}\\b`, "i").test(p.body ?? "")
|
|
480
|
+
);
|
|
481
|
+
if (!pr) return null;
|
|
482
|
+
return { number: pr.number, url: pr.html_url, body: pr.body ?? "", baseBranch: pr.base.ref };
|
|
483
|
+
}
|
|
484
|
+
async function getOpenSubtasks(config, targetBranch) {
|
|
485
|
+
const octokit = createOctokit(config.githubToken);
|
|
486
|
+
const { owner, repo } = config.github;
|
|
487
|
+
const { data } = await octokit.issues.listForRepo({
|
|
488
|
+
owner,
|
|
489
|
+
repo,
|
|
490
|
+
state: "open",
|
|
491
|
+
per_page: 100
|
|
492
|
+
});
|
|
493
|
+
return data.filter((issue) => !issue.pull_request).filter((issue) => extractTargetBranch(issue.body ?? null) === targetBranch).map((issue) => issue.number);
|
|
494
|
+
}
|
|
495
|
+
async function getIssueNumberFromBranch(config, branch) {
|
|
496
|
+
const octokit = createOctokit(config.githubToken);
|
|
497
|
+
const { owner, repo } = config.github;
|
|
498
|
+
const { data: prs } = await octokit.pulls.list({ owner, repo, state: "open", per_page: 100 });
|
|
499
|
+
const pr = prs.find((p) => p.head.ref === branch);
|
|
500
|
+
if (!pr) return null;
|
|
501
|
+
const match = (pr.body ?? "").match(/Closes #(\d+)/i);
|
|
502
|
+
if (!match) return null;
|
|
503
|
+
return { issueNumber: parseInt(match[1], 10), prUrl: pr.html_url };
|
|
504
|
+
}
|
|
505
|
+
async function getTaskPRDiff(config, prNumber) {
|
|
506
|
+
const octokit = createOctokit(config.githubToken);
|
|
507
|
+
const { owner, repo } = config.github;
|
|
508
|
+
const response = await octokit.pulls.get({
|
|
370
509
|
owner,
|
|
371
510
|
repo,
|
|
372
|
-
pull_number:
|
|
373
|
-
|
|
511
|
+
pull_number: prNumber,
|
|
512
|
+
mediaType: { format: "diff" }
|
|
374
513
|
});
|
|
375
|
-
|
|
376
|
-
|
|
514
|
+
return response.data;
|
|
515
|
+
}
|
|
516
|
+
async function acceptTask(config, issueNumber) {
|
|
517
|
+
const octokit = createOctokit(config.githubToken);
|
|
518
|
+
const { owner, repo } = config.github;
|
|
519
|
+
const { data: prs } = await octokit.pulls.list({ owner, repo, state: "open", per_page: 100 });
|
|
520
|
+
const pr = prs.find(
|
|
521
|
+
(p) => new RegExp(`Closes #${issueNumber}\\b`, "i").test(p.body ?? "")
|
|
522
|
+
);
|
|
523
|
+
if (!pr) throw new Error(`No open PR found for task #${issueNumber}`);
|
|
524
|
+
try {
|
|
525
|
+
const { data: merge } = await octokit.pulls.merge({
|
|
526
|
+
owner,
|
|
527
|
+
repo,
|
|
528
|
+
pull_number: pr.number,
|
|
529
|
+
merge_method: "merge"
|
|
530
|
+
});
|
|
531
|
+
await closeTask(config, issueNumber);
|
|
532
|
+
return { prNumber: pr.number, prUrl: pr.html_url, sha: merge.sha ?? "", baseBranch: pr.base.ref };
|
|
533
|
+
} catch (err) {
|
|
534
|
+
if (err.status === 405) {
|
|
535
|
+
throw new Error(
|
|
536
|
+
`PR #${pr.number} cannot be merged \u2014 may have conflicts or is not in a mergeable state.`
|
|
537
|
+
);
|
|
538
|
+
}
|
|
539
|
+
throw err;
|
|
540
|
+
}
|
|
377
541
|
}
|
|
378
|
-
var LABEL_AVAILABLE, LABEL_CLAIMED, LABEL_IN_REVIEW, LABEL_CHANGES_NEEDED, LABELS, TECHUNTER_LABELS, BASE_COMMIT_MARKER;
|
|
542
|
+
var LABEL_AVAILABLE, LABEL_CLAIMED, LABEL_IN_REVIEW, LABEL_CHANGES_NEEDED, LABELS, TECHUNTER_LABELS, BASE_COMMIT_MARKER, TARGET_BRANCH_MARKER;
|
|
379
543
|
var init_github = __esm({
|
|
380
544
|
"src/lib/github.ts"() {
|
|
381
545
|
"use strict";
|
|
@@ -392,6 +556,7 @@ var init_github = __esm({
|
|
|
392
556
|
];
|
|
393
557
|
TECHUNTER_LABELS = /* @__PURE__ */ new Set([LABEL_AVAILABLE, LABEL_CLAIMED, LABEL_IN_REVIEW, LABEL_CHANGES_NEEDED]);
|
|
394
558
|
BASE_COMMIT_MARKER = "<!-- techunter-base:";
|
|
559
|
+
TARGET_BRANCH_MARKER = "<!-- techunter-target:";
|
|
395
560
|
}
|
|
396
561
|
});
|
|
397
562
|
|
|
@@ -428,6 +593,17 @@ function makeWorkerBranchName(username) {
|
|
|
428
593
|
const slug = username.toLowerCase().replace(/[^a-z0-9-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "") || "user";
|
|
429
594
|
return `worker-${slug}`;
|
|
430
595
|
}
|
|
596
|
+
function makeTaskBranchName(issueNumber, username) {
|
|
597
|
+
const slug = username.toLowerCase().replace(/[^a-z0-9-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "") || "user";
|
|
598
|
+
return `task-${issueNumber}-${slug}`;
|
|
599
|
+
}
|
|
600
|
+
function isTaskBranch(branch) {
|
|
601
|
+
return /^task-\d+-/.test(branch);
|
|
602
|
+
}
|
|
603
|
+
function parseIssueNumberFromBranch(branch) {
|
|
604
|
+
const match = branch.match(/^task-(\d+)-/);
|
|
605
|
+
return match ? parseInt(match[1], 10) : null;
|
|
606
|
+
}
|
|
431
607
|
async function getCurrentCommit() {
|
|
432
608
|
return (await git.revparse(["HEAD"])).trim();
|
|
433
609
|
}
|
|
@@ -548,16 +724,27 @@ async function getRemoteHeadSha(baseBranch) {
|
|
|
548
724
|
await git.fetch("origin", baseBranch);
|
|
549
725
|
return (await git.revparse([`origin/${baseBranch}`])).trim();
|
|
550
726
|
}
|
|
551
|
-
async function
|
|
552
|
-
const branches = await git.branch();
|
|
553
|
-
const
|
|
554
|
-
|
|
727
|
+
async function checkoutFromCommit(branchName, sha) {
|
|
728
|
+
const branches = await git.branch(["-a"]);
|
|
729
|
+
const exists = Object.keys(branches.branches).some(
|
|
730
|
+
(b) => b === branchName || b === `remotes/origin/${branchName}`
|
|
731
|
+
);
|
|
732
|
+
if (exists) {
|
|
555
733
|
await git.checkout(branchName);
|
|
556
|
-
await git.reset(["--hard", sha]);
|
|
557
734
|
} else {
|
|
558
735
|
await git.checkoutBranch(branchName, sha);
|
|
559
736
|
}
|
|
560
737
|
}
|
|
738
|
+
async function hasUncommittedChanges() {
|
|
739
|
+
const status = await git.status();
|
|
740
|
+
return !status.isClean();
|
|
741
|
+
}
|
|
742
|
+
async function stash(message) {
|
|
743
|
+
await git.stash(["push", "-u", "-m", message]);
|
|
744
|
+
}
|
|
745
|
+
async function stashPop() {
|
|
746
|
+
await git.stash(["pop"]);
|
|
747
|
+
}
|
|
561
748
|
|
|
562
749
|
// src/tools/pick/index.ts
|
|
563
750
|
init_github();
|
|
@@ -578,7 +765,8 @@ var configSchema = z.object({
|
|
|
578
765
|
}),
|
|
579
766
|
taskState: z.object({
|
|
580
767
|
activeIssueNumber: z.number().optional(),
|
|
581
|
-
baseCommit: z.string().optional()
|
|
768
|
+
baseCommit: z.string().optional(),
|
|
769
|
+
activeBranch: z.string().optional()
|
|
582
770
|
}).optional()
|
|
583
771
|
});
|
|
584
772
|
var store = new Conf({
|
|
@@ -665,11 +853,22 @@ function colorStatus(status) {
|
|
|
665
853
|
return padded;
|
|
666
854
|
}
|
|
667
855
|
}
|
|
856
|
+
function parentIssueFromBranch(branch) {
|
|
857
|
+
if (!isTaskBranch(branch)) return null;
|
|
858
|
+
const match = branch.match(/^task-(\d+)-/);
|
|
859
|
+
return match ? parseInt(match[1], 10) : null;
|
|
860
|
+
}
|
|
861
|
+
function getParentIssueNumber(issue) {
|
|
862
|
+
const target = extractTargetBranch(issue.body);
|
|
863
|
+
if (!target) return null;
|
|
864
|
+
return parentIssueFromBranch(target);
|
|
865
|
+
}
|
|
668
866
|
function printTaskDetail(issue) {
|
|
669
867
|
const divider = chalk2.dim("\u2500".repeat(70));
|
|
868
|
+
const parentNum = getParentIssueNumber(issue);
|
|
670
869
|
console.log("\n" + divider);
|
|
671
870
|
console.log(
|
|
672
|
-
chalk2.bold(` #${issue.number}`) + " " + colorStatus(getStatus(issue)) + " " + chalk2.dim(issue.assignee ? `@${issue.assignee}` : "\u2014")
|
|
871
|
+
chalk2.bold(` #${issue.number}`) + " " + colorStatus(getStatus(issue)) + " " + chalk2.dim(issue.assignee ? `@${issue.assignee}` : "\u2014") + (parentNum ? chalk2.dim(` sub-task of #${parentNum}`) : "")
|
|
673
872
|
);
|
|
674
873
|
console.log(chalk2.bold("\n " + issue.title));
|
|
675
874
|
if (issue.body) {
|
|
@@ -689,12 +888,34 @@ async function printTaskList(config) {
|
|
|
689
888
|
if (tasks.length === 0) {
|
|
690
889
|
console.log(chalk2.dim(" (no tasks)"));
|
|
691
890
|
} else {
|
|
692
|
-
|
|
891
|
+
let printTask2 = function(t, indent, connector, isLast) {
|
|
693
892
|
const num = `#${t.number}`.padEnd(5);
|
|
694
893
|
const status = colorStatus(getStatus(t));
|
|
695
894
|
const assignee = (t.assignee ? `@${t.assignee}` : "\u2014").padEnd(16);
|
|
696
|
-
const
|
|
697
|
-
|
|
895
|
+
const fullPrefix = indent + connector;
|
|
896
|
+
const maxTitle = 36 - fullPrefix.length;
|
|
897
|
+
const title = t.title.length > maxTitle ? t.title.slice(0, maxTitle - 3) + "..." : t.title;
|
|
898
|
+
console.log(` ${num}${status}${assignee}${chalk2.dim(fullPrefix)}${title}`);
|
|
899
|
+
const children = childrenOf.get(t.number) ?? [];
|
|
900
|
+
const childIndent = indent + (isLast ? " " : "\u2502 ");
|
|
901
|
+
for (let i = 0; i < children.length; i++) {
|
|
902
|
+
const childIsLast = i === children.length - 1;
|
|
903
|
+
printTask2(children[i], childIndent, childIsLast ? "\u2514\u2500 " : "\u251C\u2500 ", childIsLast);
|
|
904
|
+
}
|
|
905
|
+
};
|
|
906
|
+
var printTask = printTask2;
|
|
907
|
+
const taskMap = new Map(tasks.map((t) => [t.number, t]));
|
|
908
|
+
const childrenOf = /* @__PURE__ */ new Map();
|
|
909
|
+
for (const t of tasks) {
|
|
910
|
+
const parentNum = getParentIssueNumber(t);
|
|
911
|
+
const key = parentNum !== null && taskMap.has(parentNum) ? parentNum : null;
|
|
912
|
+
if (!childrenOf.has(key)) childrenOf.set(key, []);
|
|
913
|
+
childrenOf.get(key).push(t);
|
|
914
|
+
}
|
|
915
|
+
const roots = childrenOf.get(null) ?? [];
|
|
916
|
+
for (let i = 0; i < roots.length; i++) {
|
|
917
|
+
const isLast = i === roots.length - 1;
|
|
918
|
+
printTask2(roots[i], "", isLast ? "\u2514\u2500 " : "\u251C\u2500 ", isLast);
|
|
698
919
|
}
|
|
699
920
|
}
|
|
700
921
|
console.log(divider);
|
|
@@ -873,9 +1094,19 @@ var definition = {
|
|
|
873
1094
|
};
|
|
874
1095
|
async function run(_input, config) {
|
|
875
1096
|
const taskState = getConfig().taskState;
|
|
876
|
-
const
|
|
1097
|
+
const currentBranch = await getCurrentBranch();
|
|
1098
|
+
let issueNumber = taskState?.activeIssueNumber && taskState?.activeBranch && currentBranch === taskState.activeBranch ? taskState.activeIssueNumber : void 0;
|
|
877
1099
|
if (!issueNumber) {
|
|
878
|
-
|
|
1100
|
+
const fromBranch = parseIssueNumberFromBranch(currentBranch);
|
|
1101
|
+
if (fromBranch) {
|
|
1102
|
+
issueNumber = fromBranch;
|
|
1103
|
+
} else {
|
|
1104
|
+
const found = await getIssueNumberFromBranch(config, currentBranch);
|
|
1105
|
+
if (!found) {
|
|
1106
|
+
return "No active task found. Claim a task first with /pick.";
|
|
1107
|
+
}
|
|
1108
|
+
issueNumber = found.issueNumber;
|
|
1109
|
+
}
|
|
879
1110
|
}
|
|
880
1111
|
let spinner = ora("Loading task and diff\u2026").start();
|
|
881
1112
|
const diffPromise = taskState?.baseCommit ? getDiffFromCommit(taskState.baseCommit) : getDiff();
|
|
@@ -885,9 +1116,16 @@ async function run(_input, config) {
|
|
|
885
1116
|
getAuthenticatedUser(config)
|
|
886
1117
|
]);
|
|
887
1118
|
spinner.stop();
|
|
888
|
-
const
|
|
1119
|
+
const targetBranch = extractTargetBranch(issue.body) ?? makeWorkerBranchName(issue.author ?? me);
|
|
889
1120
|
const branch = await getCurrentBranch();
|
|
890
1121
|
const isSelfSubmit = issue.author !== null && issue.author === me;
|
|
1122
|
+
spinner = ora("Checking for open sub-tasks\u2026").start();
|
|
1123
|
+
const openSubtaskNumbers = await getOpenSubtasks(config, branch);
|
|
1124
|
+
spinner.stop();
|
|
1125
|
+
if (openSubtaskNumbers.length > 0) {
|
|
1126
|
+
return `Cannot submit: ${openSubtaskNumbers.length} sub-task(s) still open:
|
|
1127
|
+
` + openSubtaskNumbers.map((n) => ` - #${n}`).join("\n") + "\nComplete all sub-tasks before submitting.";
|
|
1128
|
+
}
|
|
891
1129
|
let review = "";
|
|
892
1130
|
if (!isSelfSubmit) {
|
|
893
1131
|
const reviewSpinner = ora("Reviewing changes\u2026").start();
|
|
@@ -948,26 +1186,35 @@ async function run(_input, config) {
|
|
|
948
1186
|
spinner.stop();
|
|
949
1187
|
console.error(chalk5.yellow(`Warning: failed to close issue: ${err.message}`));
|
|
950
1188
|
}
|
|
951
|
-
setConfig({ taskState: { activeIssueNumber: void 0, baseCommit: void 0 } });
|
|
1189
|
+
setConfig({ taskState: { activeIssueNumber: void 0, baseCommit: void 0, activeBranch: void 0 } });
|
|
952
1190
|
return `Task #${issueNumber} committed and closed.
|
|
953
1191
|
Commit: "${commitMessage.trim()}"`;
|
|
954
1192
|
}
|
|
955
|
-
spinner = ora("
|
|
1193
|
+
spinner = ora("Checking for existing PR\u2026").start();
|
|
1194
|
+
const existingPR = await getTaskPR(config, issueNumber);
|
|
1195
|
+
spinner.stop();
|
|
956
1196
|
let prUrl;
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
1197
|
+
if (existingPR) {
|
|
1198
|
+
prUrl = existingPR.url;
|
|
1199
|
+
console.log(chalk5.dim(` Existing PR found: ${prUrl} \u2014 updating.`));
|
|
1200
|
+
} else {
|
|
1201
|
+
spinner = ora("Creating pull request\u2026").start();
|
|
1202
|
+
try {
|
|
1203
|
+
await ensureRemoteBranch(config, targetBranch, config.baseBranch ?? "main");
|
|
1204
|
+
const prBody = [
|
|
1205
|
+
`Closes #${issueNumber}`,
|
|
1206
|
+
issue.body ? `
|
|
961
1207
|
${issue.body}` : "",
|
|
962
|
-
|
|
1208
|
+
review ? `
|
|
963
1209
|
## AI Review
|
|
964
1210
|
${review}` : ""
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
1211
|
+
].join("\n").trim();
|
|
1212
|
+
prUrl = await createPR(config, issue.title, prBody, branch, targetBranch);
|
|
1213
|
+
spinner.stop();
|
|
1214
|
+
} catch (err) {
|
|
1215
|
+
spinner.stop();
|
|
1216
|
+
return `Committed but PR creation failed: ${err.message}`;
|
|
1217
|
+
}
|
|
971
1218
|
}
|
|
972
1219
|
spinner = ora("Marking as in-review\u2026").start();
|
|
973
1220
|
try {
|
|
@@ -975,17 +1222,27 @@ ${review}` : ""
|
|
|
975
1222
|
spinner.stop();
|
|
976
1223
|
} catch (err) {
|
|
977
1224
|
spinner.stop();
|
|
978
|
-
return `PR created (${prUrl}) but failed to update label: ${err.message}`;
|
|
1225
|
+
return `PR ${existingPR ? "updated" : "created"} (${prUrl}) but failed to update label: ${err.message}`;
|
|
979
1226
|
}
|
|
980
|
-
setConfig({ taskState: { activeIssueNumber: void 0, baseCommit: void 0 } });
|
|
981
|
-
return `Task #${issueNumber} submitted.
|
|
1227
|
+
setConfig({ taskState: { activeIssueNumber: void 0, baseCommit: void 0, activeBranch: void 0 } });
|
|
1228
|
+
return `Task #${issueNumber} ${existingPR ? "re-submitted" : "submitted"}.
|
|
982
1229
|
Commit: "${commitMessage.trim()}"
|
|
983
1230
|
PR: ${prUrl}`;
|
|
984
1231
|
}
|
|
985
1232
|
async function execute(input, config) {
|
|
986
1233
|
const taskState = getConfig().taskState;
|
|
987
|
-
|
|
988
|
-
if (!issueNumber)
|
|
1234
|
+
let issueNumber = taskState?.activeIssueNumber;
|
|
1235
|
+
if (!issueNumber) {
|
|
1236
|
+
const currentBranch = await getCurrentBranch();
|
|
1237
|
+
const fromBranch = parseIssueNumberFromBranch(currentBranch);
|
|
1238
|
+
if (fromBranch) {
|
|
1239
|
+
issueNumber = fromBranch;
|
|
1240
|
+
} else {
|
|
1241
|
+
const found = await getIssueNumberFromBranch(config, currentBranch);
|
|
1242
|
+
if (!found) return "No active task found. Claim a task first.";
|
|
1243
|
+
issueNumber = found.issueNumber;
|
|
1244
|
+
}
|
|
1245
|
+
}
|
|
989
1246
|
const diffPromise = taskState?.baseCommit ? getDiffFromCommit(taskState.baseCommit) : getDiff();
|
|
990
1247
|
const [issue, diff, branch, me] = await Promise.all([
|
|
991
1248
|
getTask(config, issueNumber),
|
|
@@ -993,7 +1250,11 @@ async function execute(input, config) {
|
|
|
993
1250
|
getCurrentBranch(),
|
|
994
1251
|
getAuthenticatedUser(config)
|
|
995
1252
|
]);
|
|
996
|
-
const
|
|
1253
|
+
const targetBranch = extractTargetBranch(issue.body) ?? makeWorkerBranchName(issue.author ?? me);
|
|
1254
|
+
const openSubtaskNumbers = await getOpenSubtasks(config, branch);
|
|
1255
|
+
if (openSubtaskNumbers.length > 0) {
|
|
1256
|
+
return `Cannot submit: ${openSubtaskNumbers.length} sub-task(s) still open: ` + openSubtaskNumbers.map((n) => `#${n}`).join(", ");
|
|
1257
|
+
}
|
|
997
1258
|
const isSelfSubmit = issue.author !== null && issue.author === me;
|
|
998
1259
|
let review = "";
|
|
999
1260
|
if (!isSelfSubmit) {
|
|
@@ -1014,29 +1275,36 @@ async function execute(input, config) {
|
|
|
1014
1275
|
await closeTask(config, issueNumber);
|
|
1015
1276
|
} catch {
|
|
1016
1277
|
}
|
|
1017
|
-
setConfig({ taskState: { activeIssueNumber: void 0, baseCommit: void 0 } });
|
|
1278
|
+
setConfig({ taskState: { activeIssueNumber: void 0, baseCommit: void 0, activeBranch: void 0 } });
|
|
1018
1279
|
return `Task #${issueNumber} committed and closed.
|
|
1019
1280
|
Commit: "${commitMessage}"`;
|
|
1020
1281
|
}
|
|
1282
|
+
const existingPR = await getTaskPR(config, issueNumber);
|
|
1021
1283
|
let prUrl;
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1284
|
+
if (existingPR) {
|
|
1285
|
+
prUrl = existingPR.url;
|
|
1286
|
+
} else {
|
|
1287
|
+
try {
|
|
1288
|
+
await ensureRemoteBranch(config, targetBranch, config.baseBranch ?? "main");
|
|
1289
|
+
const prBody = [
|
|
1290
|
+
`Closes #${issueNumber}`,
|
|
1291
|
+
issue.body ? `
|
|
1026
1292
|
${issue.body}` : "",
|
|
1027
|
-
|
|
1293
|
+
review ? `
|
|
1028
1294
|
## AI Review
|
|
1029
1295
|
${review}` : ""
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1296
|
+
].join("\n").trim();
|
|
1297
|
+
prUrl = await createPR(config, issue.title, prBody, branch, targetBranch);
|
|
1298
|
+
} catch (err) {
|
|
1299
|
+
return `Committed but PR creation failed: ${err.message}`;
|
|
1300
|
+
}
|
|
1034
1301
|
}
|
|
1035
1302
|
try {
|
|
1036
1303
|
await markInReview(config, issueNumber);
|
|
1037
1304
|
} catch {
|
|
1038
1305
|
}
|
|
1039
|
-
|
|
1306
|
+
setConfig({ taskState: { activeIssueNumber: void 0, baseCommit: void 0, activeBranch: void 0 } });
|
|
1307
|
+
return `Task #${issueNumber} ${existingPR ? "re-submitted" : "submitted"}.
|
|
1040
1308
|
Review:
|
|
1041
1309
|
${review}
|
|
1042
1310
|
Commit: "${commitMessage}"
|
|
@@ -1195,8 +1463,23 @@ async function run3(input, config) {
|
|
|
1195
1463
|
}
|
|
1196
1464
|
}
|
|
1197
1465
|
const actions = [];
|
|
1198
|
-
if (status === "available")
|
|
1199
|
-
|
|
1466
|
+
if (status === "available") {
|
|
1467
|
+
actions.push({ name: "Claim this task", value: "claim" });
|
|
1468
|
+
}
|
|
1469
|
+
if (status === "claimed") {
|
|
1470
|
+
actions.push({ name: "Submit this task", value: "submit" });
|
|
1471
|
+
}
|
|
1472
|
+
if (status === "changes-needed") {
|
|
1473
|
+
const { getAuthenticatedUser: getAuthenticatedUser2 } = await Promise.resolve().then(() => (init_github(), github_exports));
|
|
1474
|
+
const me = await getAuthenticatedUser2(config);
|
|
1475
|
+
const taskBranch = issue.assignee ? makeTaskBranchName(issue.number, issue.assignee) : makeTaskBranchName(issue.number, me);
|
|
1476
|
+
const currentBranch = await getCurrentBranch();
|
|
1477
|
+
if (currentBranch === taskBranch) {
|
|
1478
|
+
actions.push({ name: "Submit this task (fixes done)", value: "submit" });
|
|
1479
|
+
} else {
|
|
1480
|
+
actions.push({ name: `Switch to ${taskBranch} to fix`, value: "switch-fix" });
|
|
1481
|
+
}
|
|
1482
|
+
}
|
|
1200
1483
|
actions.push({ name: "Close this task", value: "close" });
|
|
1201
1484
|
actions.push({ name: "Nothing, just viewing", value: "none" });
|
|
1202
1485
|
let action;
|
|
@@ -1208,9 +1491,8 @@ async function run3(input, config) {
|
|
|
1208
1491
|
if (action === "none") return `Viewed task #${issue.number}.`;
|
|
1209
1492
|
if (action === "claim") {
|
|
1210
1493
|
try {
|
|
1211
|
-
const
|
|
1212
|
-
const
|
|
1213
|
-
const myTasks = await listMyTasks2(config, me);
|
|
1494
|
+
const me = await getAuthenticatedUser(config);
|
|
1495
|
+
const myTasks = await listMyTasks(config, me);
|
|
1214
1496
|
const activeTask = myTasks.find((t) => {
|
|
1215
1497
|
const labels = t.labels;
|
|
1216
1498
|
return labels.includes("techunter:claimed") || labels.includes("techunter:changes-needed");
|
|
@@ -1219,33 +1501,61 @@ async function run3(input, config) {
|
|
|
1219
1501
|
return `You already have an active task: #${activeTask.number} "${activeTask.title}"
|
|
1220
1502
|
Finish or submit it before claiming a new one.`;
|
|
1221
1503
|
}
|
|
1504
|
+
let stashed = false;
|
|
1505
|
+
if (await hasUncommittedChanges()) {
|
|
1506
|
+
let choice;
|
|
1507
|
+
try {
|
|
1508
|
+
choice = await select3({
|
|
1509
|
+
message: "You have uncommitted changes. What would you like to do?",
|
|
1510
|
+
choices: [
|
|
1511
|
+
{ name: "Stash changes and switch branch (restore with: git stash pop)", value: "stash" },
|
|
1512
|
+
{ name: "Cancel", value: "cancel" }
|
|
1513
|
+
]
|
|
1514
|
+
});
|
|
1515
|
+
} catch {
|
|
1516
|
+
choice = "cancel";
|
|
1517
|
+
}
|
|
1518
|
+
if (choice === "cancel") return "Cancelled.";
|
|
1519
|
+
await stash(`tch: before claiming #${issue.number}`);
|
|
1520
|
+
stashed = true;
|
|
1521
|
+
console.log(chalk6.dim(" Changes stashed. Run `git stash pop` after you finish this task to restore them."));
|
|
1522
|
+
}
|
|
1222
1523
|
let spinner = ora3(`Claiming #${issue.number}\u2026`).start();
|
|
1223
1524
|
await claimTask(config, issue.number, me);
|
|
1224
1525
|
spinner.stop();
|
|
1225
|
-
const
|
|
1526
|
+
const taskBranch = makeTaskBranchName(issue.number, me);
|
|
1226
1527
|
const taskBase = extractBaseCommit(issue.body);
|
|
1227
|
-
spinner = ora3(`
|
|
1528
|
+
spinner = ora3(`Creating branch ${taskBranch}${taskBase ? ` from ${taskBase.slice(0, 7)}` : ""}\u2026`).start();
|
|
1228
1529
|
try {
|
|
1229
1530
|
if (taskBase) {
|
|
1230
|
-
await
|
|
1531
|
+
await checkoutFromCommit(taskBranch, taskBase);
|
|
1231
1532
|
} else {
|
|
1232
|
-
await switchToBranchOrCreate(
|
|
1533
|
+
await switchToBranchOrCreate(taskBranch);
|
|
1233
1534
|
}
|
|
1234
1535
|
spinner.stop();
|
|
1235
|
-
spinner = ora3("Pushing
|
|
1536
|
+
spinner = ora3("Pushing task branch\u2026").start();
|
|
1236
1537
|
try {
|
|
1237
|
-
await pushBranch(
|
|
1538
|
+
await pushBranch(taskBranch);
|
|
1238
1539
|
spinner.stop();
|
|
1239
1540
|
} catch {
|
|
1240
|
-
spinner.warn("Could not push
|
|
1541
|
+
spinner.warn("Could not push task branch \u2014 will push on submit");
|
|
1241
1542
|
}
|
|
1242
|
-
} catch {
|
|
1243
|
-
spinner.warn(`Could not switch to ${
|
|
1543
|
+
} catch (err) {
|
|
1544
|
+
spinner.warn(`Could not switch to ${taskBranch}`);
|
|
1545
|
+
if (stashed) {
|
|
1546
|
+
try {
|
|
1547
|
+
await stashPop();
|
|
1548
|
+
console.log(chalk6.dim(" Restored stashed changes."));
|
|
1549
|
+
} catch {
|
|
1550
|
+
console.log(chalk6.yellow(" Warning: could not restore stash automatically. Run `git stash pop` manually."));
|
|
1551
|
+
}
|
|
1552
|
+
}
|
|
1553
|
+
throw err;
|
|
1244
1554
|
}
|
|
1245
1555
|
const baseCommit = await getCurrentCommit();
|
|
1246
|
-
setConfig({ taskState: { activeIssueNumber: issue.number, baseCommit } });
|
|
1556
|
+
setConfig({ taskState: { activeIssueNumber: issue.number, baseCommit, activeBranch: taskBranch } });
|
|
1247
1557
|
console.log(chalk6.green(`
|
|
1248
|
-
Claimed! Branch: ${
|
|
1558
|
+
Claimed! Branch: ${taskBranch} (base: ${baseCommit.slice(0, 7)})
|
|
1249
1559
|
`));
|
|
1250
1560
|
let openClaude;
|
|
1251
1561
|
try {
|
|
@@ -1259,12 +1569,58 @@ Finish or submit it before claiming a new one.`;
|
|
|
1259
1569
|
} catch {
|
|
1260
1570
|
openClaude = false;
|
|
1261
1571
|
}
|
|
1262
|
-
if (openClaude) await launchClaudeCode(issue,
|
|
1263
|
-
return `Task #${issue.number} claimed. Branch: ${
|
|
1572
|
+
if (openClaude) await launchClaudeCode(issue, taskBranch);
|
|
1573
|
+
return `Task #${issue.number} claimed. Branch: ${taskBranch}`;
|
|
1264
1574
|
} catch (err) {
|
|
1265
1575
|
return `Error claiming task: ${err.message}`;
|
|
1266
1576
|
}
|
|
1267
1577
|
}
|
|
1578
|
+
if (action === "switch-fix") {
|
|
1579
|
+
const { getAuthenticatedUser: getAuthenticatedUser2 } = await Promise.resolve().then(() => (init_github(), github_exports));
|
|
1580
|
+
const me = await getAuthenticatedUser2(config);
|
|
1581
|
+
const taskBranch = issue.assignee ? makeTaskBranchName(issue.number, issue.assignee) : makeTaskBranchName(issue.number, me);
|
|
1582
|
+
let stashed = false;
|
|
1583
|
+
if (await hasUncommittedChanges()) {
|
|
1584
|
+
let choice;
|
|
1585
|
+
try {
|
|
1586
|
+
choice = await select3({
|
|
1587
|
+
message: "You have uncommitted changes. What would you like to do?",
|
|
1588
|
+
choices: [
|
|
1589
|
+
{ name: "Stash changes and switch branch (restore with: git stash pop)", value: "stash" },
|
|
1590
|
+
{ name: "Cancel", value: "cancel" }
|
|
1591
|
+
]
|
|
1592
|
+
});
|
|
1593
|
+
} catch {
|
|
1594
|
+
choice = "cancel";
|
|
1595
|
+
}
|
|
1596
|
+
if (choice === "cancel") return "Cancelled.";
|
|
1597
|
+
await stash(`tch: before switching to ${taskBranch}`);
|
|
1598
|
+
stashed = true;
|
|
1599
|
+
console.log(chalk6.dim(" Changes stashed. Run `git stash pop` to restore them later."));
|
|
1600
|
+
}
|
|
1601
|
+
const spinner = ora3(`Switching to ${taskBranch}\u2026`).start();
|
|
1602
|
+
try {
|
|
1603
|
+
await switchToBranchOrCreate(taskBranch);
|
|
1604
|
+
spinner.stop();
|
|
1605
|
+
} catch (err) {
|
|
1606
|
+
spinner.warn(`Could not switch to ${taskBranch}: ${err.message}`);
|
|
1607
|
+
if (stashed) {
|
|
1608
|
+
try {
|
|
1609
|
+
await stashPop();
|
|
1610
|
+
console.log(chalk6.dim(" Restored stashed changes."));
|
|
1611
|
+
} catch {
|
|
1612
|
+
console.log(chalk6.yellow(" Run `git stash pop` manually to restore your changes."));
|
|
1613
|
+
}
|
|
1614
|
+
}
|
|
1615
|
+
return `Error: ${err.message}`;
|
|
1616
|
+
}
|
|
1617
|
+
const baseCommit = extractBaseCommit(issue.body) ?? await getCurrentCommit();
|
|
1618
|
+
setConfig({ taskState: { activeIssueNumber: issue.number, baseCommit, activeBranch: taskBranch } });
|
|
1619
|
+
console.log(chalk6.green(`
|
|
1620
|
+
Switched to ${taskBranch}. Fix the issues then run /submit.
|
|
1621
|
+
`));
|
|
1622
|
+
return `Switched to ${taskBranch} for task #${issue.number}.`;
|
|
1623
|
+
}
|
|
1268
1624
|
if (action === "submit") return run({}, config);
|
|
1269
1625
|
if (action === "close") return run2({ issue_number: issue.number }, config);
|
|
1270
1626
|
return "Cancelled.";
|
|
@@ -1293,28 +1649,31 @@ async function execute3(input, config) {
|
|
|
1293
1649
|
if (activeTask) {
|
|
1294
1650
|
return `You already have an active task: #${activeTask.number} "${activeTask.title}". Finish it before claiming a new one.`;
|
|
1295
1651
|
}
|
|
1652
|
+
if (await hasUncommittedChanges()) {
|
|
1653
|
+
return "Cannot claim: you have uncommitted changes. Commit or stash them first (git stash).";
|
|
1654
|
+
}
|
|
1296
1655
|
try {
|
|
1297
1656
|
await claimTask(config, issueNumber, me);
|
|
1298
1657
|
} catch (err) {
|
|
1299
1658
|
return `Error claiming task: ${err.message}`;
|
|
1300
1659
|
}
|
|
1301
|
-
const
|
|
1660
|
+
const taskBranch = makeTaskBranchName(issue.number, me);
|
|
1302
1661
|
const taskBase = extractBaseCommit(issue.body);
|
|
1303
1662
|
try {
|
|
1304
1663
|
if (taskBase) {
|
|
1305
|
-
await
|
|
1664
|
+
await checkoutFromCommit(taskBranch, taskBase);
|
|
1306
1665
|
} else {
|
|
1307
|
-
await switchToBranchOrCreate(
|
|
1666
|
+
await switchToBranchOrCreate(taskBranch);
|
|
1308
1667
|
}
|
|
1309
1668
|
} catch {
|
|
1310
1669
|
}
|
|
1311
1670
|
try {
|
|
1312
|
-
await pushBranch(
|
|
1671
|
+
await pushBranch(taskBranch);
|
|
1313
1672
|
} catch {
|
|
1314
1673
|
}
|
|
1315
1674
|
const baseCommit = await getCurrentCommit();
|
|
1316
|
-
setConfig({ taskState: { activeIssueNumber: issueNumber, baseCommit } });
|
|
1317
|
-
return `Task #${issueNumber} claimed. Branch: ${
|
|
1675
|
+
setConfig({ taskState: { activeIssueNumber: issueNumber, baseCommit, activeBranch: taskBranch } });
|
|
1676
|
+
return `Task #${issueNumber} claimed. Branch: ${taskBranch} (base commit: ${baseCommit.slice(0, 7)})`;
|
|
1318
1677
|
}
|
|
1319
1678
|
return `Unknown action: ${action}`;
|
|
1320
1679
|
}
|
|
@@ -1389,6 +1748,77 @@ async function openInEditor(content) {
|
|
|
1389
1748
|
await rm(dir, { recursive: true, force: true });
|
|
1390
1749
|
}
|
|
1391
1750
|
}
|
|
1751
|
+
async function resolveBaseAndTarget(config, me, interactive) {
|
|
1752
|
+
const currentBranch = await getCurrentBranch();
|
|
1753
|
+
if (isTaskBranch(currentBranch)) {
|
|
1754
|
+
if (await hasUncommittedChanges()) {
|
|
1755
|
+
if (!interactive) {
|
|
1756
|
+
throw new Error("Cannot create sub-task: you have uncommitted changes. Commit them first so the executor starts from the correct base.");
|
|
1757
|
+
}
|
|
1758
|
+
const { select: inquirerSelect } = await import("@inquirer/prompts");
|
|
1759
|
+
let choice;
|
|
1760
|
+
try {
|
|
1761
|
+
choice = await inquirerSelect({
|
|
1762
|
+
message: "You have uncommitted changes. The sub-task executor will start from the last commit \u2014 they won't see your current unsaved work.",
|
|
1763
|
+
choices: [
|
|
1764
|
+
{ name: "Commit first (cancel and commit manually)", value: "cancel" },
|
|
1765
|
+
{ name: "Continue anyway (executor starts without my unsaved changes)", value: "continue" }
|
|
1766
|
+
]
|
|
1767
|
+
});
|
|
1768
|
+
} catch {
|
|
1769
|
+
choice = "cancel";
|
|
1770
|
+
}
|
|
1771
|
+
if (choice === "cancel") throw new Error("Cancelled. Commit your changes first, then create the sub-task.");
|
|
1772
|
+
}
|
|
1773
|
+
const baseCommit2 = await getCurrentCommit();
|
|
1774
|
+
return { baseCommit: baseCommit2, targetBranch: currentBranch, isSubtask: true };
|
|
1775
|
+
}
|
|
1776
|
+
let stashedForSync = false;
|
|
1777
|
+
if (await hasUncommittedChanges()) {
|
|
1778
|
+
if (!interactive) {
|
|
1779
|
+
throw new Error("Cannot create task: you have uncommitted changes. Commit or stash them first (git stash).");
|
|
1780
|
+
}
|
|
1781
|
+
const { select: inquirerSelect } = await import("@inquirer/prompts");
|
|
1782
|
+
let choice;
|
|
1783
|
+
try {
|
|
1784
|
+
choice = await inquirerSelect({
|
|
1785
|
+
message: "You have uncommitted changes. Syncing with main requires a clean working tree.",
|
|
1786
|
+
choices: [
|
|
1787
|
+
{ name: "Stash changes and continue (restore with: git stash pop)", value: "stash" },
|
|
1788
|
+
{ name: "Cancel", value: "cancel" }
|
|
1789
|
+
]
|
|
1790
|
+
});
|
|
1791
|
+
} catch {
|
|
1792
|
+
choice = "cancel";
|
|
1793
|
+
}
|
|
1794
|
+
if (choice === "cancel") throw new Error("Cancelled.");
|
|
1795
|
+
await stash("tch: before creating new task");
|
|
1796
|
+
stashedForSync = true;
|
|
1797
|
+
console.log(chalk7.dim(" Changes stashed. Run `git stash pop` after creating the task."));
|
|
1798
|
+
}
|
|
1799
|
+
const baseBranch = config.baseBranch ?? "main";
|
|
1800
|
+
let baseCommit;
|
|
1801
|
+
const syncSpinner = ora4(`Syncing with ${baseBranch}\u2026`).start();
|
|
1802
|
+
try {
|
|
1803
|
+
await syncWithBase(baseBranch);
|
|
1804
|
+
baseCommit = await getCurrentCommit();
|
|
1805
|
+
syncSpinner.succeed(`Synced with ${baseBranch} (base: ${baseCommit.slice(0, 7)})`);
|
|
1806
|
+
} catch {
|
|
1807
|
+
syncSpinner.warn(`Could not sync with ${baseBranch} \u2014 recording remote HEAD as base`);
|
|
1808
|
+
try {
|
|
1809
|
+
baseCommit = await getRemoteHeadSha(baseBranch);
|
|
1810
|
+
} catch {
|
|
1811
|
+
}
|
|
1812
|
+
if (stashedForSync) {
|
|
1813
|
+
try {
|
|
1814
|
+
await stashPop();
|
|
1815
|
+
} catch {
|
|
1816
|
+
}
|
|
1817
|
+
throw new Error(`Could not sync with ${baseBranch}. Your changes have been restored from stash.`);
|
|
1818
|
+
}
|
|
1819
|
+
}
|
|
1820
|
+
return { baseCommit, targetBranch: makeWorkerBranchName(me), isSubtask: false };
|
|
1821
|
+
}
|
|
1392
1822
|
var definition4 = {
|
|
1393
1823
|
type: "function",
|
|
1394
1824
|
function: {
|
|
@@ -1484,26 +1914,23 @@ async function run4(input, config) {
|
|
|
1484
1914
|
console.log(chalk7.yellow(` Revision error: ${err.message}`));
|
|
1485
1915
|
}
|
|
1486
1916
|
}
|
|
1487
|
-
const baseBranch = config.baseBranch ?? "main";
|
|
1488
1917
|
let baseCommit;
|
|
1489
|
-
|
|
1918
|
+
let targetBranch;
|
|
1919
|
+
let isSubtask;
|
|
1490
1920
|
try {
|
|
1491
|
-
await
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
}
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
baseCommit = await getRemoteHeadSha(baseBranch);
|
|
1498
|
-
} catch {
|
|
1499
|
-
}
|
|
1921
|
+
({ baseCommit, targetBranch, isSubtask } = await resolveBaseAndTarget(config, me, true));
|
|
1922
|
+
} catch (err) {
|
|
1923
|
+
return err.message;
|
|
1924
|
+
}
|
|
1925
|
+
if (isSubtask) {
|
|
1926
|
+
console.log(chalk7.dim(` Sub-task: will target branch ${chalk7.cyan(targetBranch)} (base: ${baseCommit?.slice(0, 7) ?? "HEAD"})`));
|
|
1500
1927
|
}
|
|
1501
1928
|
const createSpinner = ora4(`Creating "${title}"\u2026`).start();
|
|
1502
1929
|
let htmlUrl;
|
|
1503
1930
|
let issueNumber;
|
|
1504
1931
|
let issueTitle;
|
|
1505
1932
|
try {
|
|
1506
|
-
const issue = await createTask(config, title, guide, baseCommit);
|
|
1933
|
+
const issue = await createTask(config, title, guide, baseCommit, targetBranch);
|
|
1507
1934
|
createSpinner.stop();
|
|
1508
1935
|
htmlUrl = issue.htmlUrl;
|
|
1509
1936
|
issueNumber = issue.number;
|
|
@@ -1540,19 +1967,9 @@ async function execute4(input, config) {
|
|
|
1540
1967
|
if (feedback) {
|
|
1541
1968
|
guide = await generateGuide(config, title, { feedback, previousGuide: guide });
|
|
1542
1969
|
}
|
|
1543
|
-
const
|
|
1544
|
-
let baseCommit;
|
|
1545
|
-
try {
|
|
1546
|
-
await syncWithBase(baseBranch);
|
|
1547
|
-
baseCommit = await getCurrentCommit();
|
|
1548
|
-
} catch {
|
|
1549
|
-
try {
|
|
1550
|
-
baseCommit = await getRemoteHeadSha(baseBranch);
|
|
1551
|
-
} catch {
|
|
1552
|
-
}
|
|
1553
|
-
}
|
|
1970
|
+
const { baseCommit, targetBranch } = await resolveBaseAndTarget(config, me, false);
|
|
1554
1971
|
try {
|
|
1555
|
-
const issue = await createTask(config, title, guide, baseCommit);
|
|
1972
|
+
const issue = await createTask(config, title, guide, baseCommit, targetBranch);
|
|
1556
1973
|
return `Created #${issue.number} "${issue.title}" \u2014 ${issue.htmlUrl}
|
|
1557
1974
|
|
|
1558
1975
|
Guide:
|
|
@@ -1602,119 +2019,249 @@ var terminal5 = true;
|
|
|
1602
2019
|
// src/tools/review/index.ts
|
|
1603
2020
|
var review_exports = {};
|
|
1604
2021
|
__export(review_exports, {
|
|
2022
|
+
definition: () => definition8,
|
|
2023
|
+
execute: () => execute8,
|
|
2024
|
+
run: () => run8,
|
|
2025
|
+
terminal: () => terminal8
|
|
2026
|
+
});
|
|
2027
|
+
init_github();
|
|
2028
|
+
import chalk10 from "chalk";
|
|
2029
|
+
import ora8 from "ora";
|
|
2030
|
+
import { select as select7 } from "@inquirer/prompts";
|
|
2031
|
+
|
|
2032
|
+
// src/tools/accept/index.ts
|
|
2033
|
+
var accept_exports = {};
|
|
2034
|
+
__export(accept_exports, {
|
|
1605
2035
|
definition: () => definition6,
|
|
1606
2036
|
execute: () => execute6,
|
|
1607
2037
|
run: () => run6,
|
|
1608
2038
|
terminal: () => terminal6
|
|
1609
2039
|
});
|
|
1610
2040
|
init_github();
|
|
2041
|
+
import chalk8 from "chalk";
|
|
2042
|
+
import { select as select5 } from "@inquirer/prompts";
|
|
1611
2043
|
import ora6 from "ora";
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
2044
|
+
|
|
2045
|
+
// src/tools/wiki/prompts.ts
|
|
2046
|
+
var WIKI_FORMAT = `
|
|
2047
|
+
The document you produce must be valid Markdown with these exact sections:
|
|
2048
|
+
|
|
2049
|
+
# [Project Name]
|
|
2050
|
+
|
|
2051
|
+
> One-sentence description of what this project does.
|
|
2052
|
+
|
|
2053
|
+
## What Is This?
|
|
2054
|
+
|
|
2055
|
+
2-4 paragraphs covering:
|
|
2056
|
+
- The problem this project solves
|
|
2057
|
+
- Who uses it and in what context
|
|
2058
|
+
- Core capabilities / key features
|
|
2059
|
+
|
|
2060
|
+
## Quick Start
|
|
2061
|
+
|
|
2062
|
+
Numbered steps for a brand-new developer to install, configure, and run the project for the first time.
|
|
2063
|
+
|
|
2064
|
+
## Architecture
|
|
2065
|
+
|
|
2066
|
+
High-level explanation of how the system is structured:
|
|
2067
|
+
- Key components / layers and their responsibilities
|
|
2068
|
+
- Request or data flow (prose or ASCII diagram)
|
|
2069
|
+
- Noteworthy design decisions
|
|
2070
|
+
|
|
2071
|
+
## Key Files
|
|
2072
|
+
|
|
2073
|
+
| File / Directory | Purpose |
|
|
2074
|
+
|---|---|
|
|
2075
|
+
| ... | ... |
|
|
2076
|
+
|
|
2077
|
+
(List the 8-15 most important files.)
|
|
2078
|
+
|
|
2079
|
+
## Development Workflow
|
|
2080
|
+
|
|
2081
|
+
Common day-to-day tasks a contributor will need:
|
|
2082
|
+
- How to build / run locally
|
|
2083
|
+
- How to add a new feature (brief steps)
|
|
2084
|
+
- Any testing or linting commands
|
|
2085
|
+
|
|
2086
|
+
---
|
|
2087
|
+
*Maintained by Techunter \u2014 run \`tch wiki\` to regenerate*
|
|
2088
|
+
`;
|
|
2089
|
+
|
|
2090
|
+
// src/tools/wiki/wiki-generator.ts
|
|
2091
|
+
async function generateWiki(config) {
|
|
2092
|
+
return runSubAgentLoop(
|
|
2093
|
+
config,
|
|
2094
|
+
"You are a senior engineer writing a project overview document for new team members. Use list_files to understand the project structure, then grep_code and run_command to read key files (e.g. package.json, README, entry points, config files). Be concrete and specific \u2014 reference real file names, commands, and concepts from this codebase. Avoid vague filler. When you have enough context, write the document.\n\n" + WIKI_FORMAT,
|
|
2095
|
+
"Analyze this project thoroughly and produce a comprehensive TECHUNTER.md overview document for new team members.",
|
|
2096
|
+
["list_files", "grep_code", "run_command"]
|
|
2097
|
+
);
|
|
2098
|
+
}
|
|
2099
|
+
|
|
2100
|
+
// src/tools/accept/index.ts
|
|
2101
|
+
var definition6 = {
|
|
2102
|
+
type: "function",
|
|
2103
|
+
function: {
|
|
2104
|
+
name: "accept",
|
|
2105
|
+
description: "Accept an in-review task: merges the PR into the target branch and closes the issue.",
|
|
2106
|
+
parameters: {
|
|
2107
|
+
type: "object",
|
|
2108
|
+
properties: {
|
|
2109
|
+
issue_number: { type: "number", description: "GitHub issue number to accept" }
|
|
2110
|
+
},
|
|
2111
|
+
required: ["issue_number"]
|
|
2112
|
+
}
|
|
1618
2113
|
}
|
|
1619
2114
|
};
|
|
1620
|
-
async function run6(
|
|
1621
|
-
|
|
2115
|
+
async function run6(input, config) {
|
|
2116
|
+
let issueNumber = input["issue_number"];
|
|
2117
|
+
if (!issueNumber) {
|
|
2118
|
+
const spinner3 = ora6("Loading tasks for review\u2026").start();
|
|
2119
|
+
let tasks;
|
|
2120
|
+
let me;
|
|
2121
|
+
try {
|
|
2122
|
+
me = await getAuthenticatedUser(config);
|
|
2123
|
+
tasks = await listTasksForReview(config, me);
|
|
2124
|
+
spinner3.stop();
|
|
2125
|
+
} catch (err) {
|
|
2126
|
+
spinner3.stop();
|
|
2127
|
+
return `Error: ${err.message}`;
|
|
2128
|
+
}
|
|
2129
|
+
if (tasks.length === 0) return "No tasks pending review.";
|
|
2130
|
+
try {
|
|
2131
|
+
issueNumber = await select5({
|
|
2132
|
+
message: "Which task to accept?",
|
|
2133
|
+
choices: tasks.map((t) => ({
|
|
2134
|
+
name: `#${t.number} @${t.assignee ?? "\u2014"} ${t.title}`,
|
|
2135
|
+
value: t.number
|
|
2136
|
+
}))
|
|
2137
|
+
});
|
|
2138
|
+
} catch {
|
|
2139
|
+
return "Cancelled.";
|
|
2140
|
+
}
|
|
2141
|
+
}
|
|
2142
|
+
const spinner2 = ora6("Verifying permissions\u2026").start();
|
|
2143
|
+
let me2;
|
|
2144
|
+
let issue;
|
|
1622
2145
|
try {
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
return `Tasks pending review (created by @${me}):
|
|
1629
|
-
${lines.join("\n")}`;
|
|
2146
|
+
[me2, issue] = await Promise.all([
|
|
2147
|
+
getAuthenticatedUser(config),
|
|
2148
|
+
getTask(config, issueNumber)
|
|
2149
|
+
]);
|
|
2150
|
+
spinner2.stop();
|
|
1630
2151
|
} catch (err) {
|
|
1631
|
-
|
|
2152
|
+
spinner2.stop();
|
|
1632
2153
|
return `Error: ${err.message}`;
|
|
1633
2154
|
}
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
var terminal6 = true;
|
|
1637
|
-
|
|
1638
|
-
// src/tools/refresh/index.ts
|
|
1639
|
-
var refresh_exports = {};
|
|
1640
|
-
__export(refresh_exports, {
|
|
1641
|
-
definition: () => definition7,
|
|
1642
|
-
execute: () => execute7,
|
|
1643
|
-
run: () => run7,
|
|
1644
|
-
terminal: () => terminal7
|
|
1645
|
-
});
|
|
1646
|
-
var definition7 = {
|
|
1647
|
-
type: "function",
|
|
1648
|
-
function: {
|
|
1649
|
-
name: "refresh",
|
|
1650
|
-
description: "Reload and display the full task list. Equivalent to /refresh.",
|
|
1651
|
-
parameters: { type: "object", properties: {}, required: [] }
|
|
2155
|
+
if (issue.author && issue.author !== me2) {
|
|
2156
|
+
return `Permission denied: only the task author (@${issue.author}) can accept task #${issueNumber}.`;
|
|
1652
2157
|
}
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
}
|
|
1665
|
-
var execute7 = run7;
|
|
1666
|
-
var terminal7 = true;
|
|
1667
|
-
|
|
1668
|
-
// src/tools/open-code/index.ts
|
|
1669
|
-
var open_code_exports = {};
|
|
1670
|
-
__export(open_code_exports, {
|
|
1671
|
-
definition: () => definition8,
|
|
1672
|
-
execute: () => execute8,
|
|
1673
|
-
run: () => run8,
|
|
1674
|
-
terminal: () => terminal8
|
|
1675
|
-
});
|
|
1676
|
-
init_github();
|
|
1677
|
-
var definition8 = {
|
|
1678
|
-
type: "function",
|
|
1679
|
-
function: {
|
|
1680
|
-
name: "open_code",
|
|
1681
|
-
description: "Launch Claude Code for the current task branch. Equivalent to /code.",
|
|
1682
|
-
parameters: { type: "object", properties: {}, required: [] }
|
|
2158
|
+
let confirmed;
|
|
2159
|
+
try {
|
|
2160
|
+
confirmed = await select5({
|
|
2161
|
+
message: `Merge PR for #${issueNumber} and close issue?`,
|
|
2162
|
+
choices: [
|
|
2163
|
+
{ name: "Yes, accept", value: true },
|
|
2164
|
+
{ name: "Cancel", value: false }
|
|
2165
|
+
]
|
|
2166
|
+
});
|
|
2167
|
+
} catch {
|
|
2168
|
+
return "Cancelled.";
|
|
1683
2169
|
}
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
let
|
|
2170
|
+
if (!confirmed) return "Cancelled.";
|
|
2171
|
+
const spinner = ora6(`Merging PR for #${issueNumber}\u2026`).start();
|
|
2172
|
+
let result;
|
|
1687
2173
|
try {
|
|
1688
|
-
|
|
2174
|
+
result = await acceptTask(config, issueNumber);
|
|
2175
|
+
spinner.succeed(`PR #${result.prNumber} merged \u2192 ${chalk8.cyan(result.baseBranch)}`);
|
|
1689
2176
|
} catch (err) {
|
|
2177
|
+
spinner.fail("Failed");
|
|
1690
2178
|
return `Error: ${err.message}`;
|
|
1691
2179
|
}
|
|
1692
|
-
const
|
|
1693
|
-
if (!
|
|
1694
|
-
|
|
1695
|
-
|
|
2180
|
+
const mergedIntoTaskBranch = isTaskBranch(result.baseBranch);
|
|
2181
|
+
if (!mergedIntoTaskBranch) {
|
|
2182
|
+
const baseBranch = config.baseBranch ?? "main";
|
|
2183
|
+
let pushToMain;
|
|
2184
|
+
try {
|
|
2185
|
+
pushToMain = await select5({
|
|
2186
|
+
message: `Push ${chalk8.cyan(result.baseBranch)} \u2192 ${chalk8.cyan(baseBranch)}?`,
|
|
2187
|
+
choices: [
|
|
2188
|
+
{ name: `Yes, push to ${baseBranch}`, value: true },
|
|
2189
|
+
{ name: "No, keep in worker branch", value: false }
|
|
2190
|
+
]
|
|
2191
|
+
});
|
|
2192
|
+
} catch {
|
|
2193
|
+
pushToMain = false;
|
|
2194
|
+
}
|
|
2195
|
+
if (pushToMain) {
|
|
2196
|
+
const mergeSpinner = ora6(`Merging ${result.baseBranch} \u2192 ${baseBranch}\u2026`).start();
|
|
2197
|
+
try {
|
|
2198
|
+
await mergeWorkerIntoBase(config, result.baseBranch, baseBranch);
|
|
2199
|
+
mergeSpinner.succeed(`Merged ${result.baseBranch} \u2192 ${baseBranch}`);
|
|
2200
|
+
} catch (err) {
|
|
2201
|
+
mergeSpinner.fail(`Could not merge to ${baseBranch}: ${err.message}`);
|
|
2202
|
+
}
|
|
2203
|
+
}
|
|
2204
|
+
}
|
|
2205
|
+
let updateWiki = false;
|
|
1696
2206
|
try {
|
|
1697
|
-
|
|
2207
|
+
updateWiki = await select5({
|
|
2208
|
+
message: "Update TECHUNTER.md project overview?",
|
|
2209
|
+
choices: [
|
|
2210
|
+
{ name: "Yes, regenerate", value: true },
|
|
2211
|
+
{ name: "No, skip", value: false }
|
|
2212
|
+
]
|
|
2213
|
+
});
|
|
2214
|
+
} catch {
|
|
2215
|
+
}
|
|
2216
|
+
if (updateWiki) {
|
|
2217
|
+
const wikiSpinner = ora6("Regenerating TECHUNTER.md\u2026").start();
|
|
2218
|
+
try {
|
|
2219
|
+
const content = await generateWiki(config);
|
|
2220
|
+
await upsertRepoFile(config, "TECHUNTER.md", content, "docs: update TECHUNTER.md project overview");
|
|
2221
|
+
wikiSpinner.succeed("TECHUNTER.md updated");
|
|
2222
|
+
} catch (err) {
|
|
2223
|
+
wikiSpinner.fail(`Wiki update failed: ${err.message}`);
|
|
2224
|
+
}
|
|
2225
|
+
}
|
|
2226
|
+
const mergeTarget = mergedIntoTaskBranch ? `${result.baseBranch} (sub-task merged, no push to main)` : result.baseBranch;
|
|
2227
|
+
return `Task #${issueNumber} accepted.
|
|
2228
|
+
PR #${result.prNumber} merged \u2192 ${mergeTarget}
|
|
2229
|
+
Issue closed.`;
|
|
2230
|
+
}
|
|
2231
|
+
async function execute6(input, config) {
|
|
2232
|
+
const issueNumber = input["issue_number"];
|
|
2233
|
+
const [me, issue] = await Promise.all([
|
|
2234
|
+
getAuthenticatedUser(config),
|
|
2235
|
+
getTask(config, issueNumber)
|
|
2236
|
+
]);
|
|
2237
|
+
if (issue.author && issue.author !== me) {
|
|
2238
|
+
return `Permission denied: only the task author (@${issue.author}) can accept task #${issueNumber}.`;
|
|
2239
|
+
}
|
|
2240
|
+
const spinner = ora6(`Merging PR for #${issueNumber}\u2026`).start();
|
|
2241
|
+
try {
|
|
2242
|
+
const result = await acceptTask(config, issueNumber);
|
|
2243
|
+
spinner.stop();
|
|
2244
|
+
return `Task #${issueNumber} accepted.
|
|
2245
|
+
PR #${result.prNumber} merged \u2192 ${result.baseBranch}
|
|
2246
|
+
Issue closed.`;
|
|
1698
2247
|
} catch (err) {
|
|
2248
|
+
spinner.stop();
|
|
1699
2249
|
return `Error: ${err.message}`;
|
|
1700
2250
|
}
|
|
1701
|
-
await launchClaudeCode(issue, branch);
|
|
1702
|
-
return "Claude Code session ended.";
|
|
1703
2251
|
}
|
|
1704
|
-
var
|
|
1705
|
-
var terminal8 = true;
|
|
2252
|
+
var terminal6 = true;
|
|
1706
2253
|
|
|
1707
2254
|
// src/tools/reject/index.ts
|
|
1708
2255
|
var reject_exports = {};
|
|
1709
2256
|
__export(reject_exports, {
|
|
1710
|
-
definition: () =>
|
|
1711
|
-
execute: () =>
|
|
1712
|
-
run: () =>
|
|
1713
|
-
terminal: () =>
|
|
2257
|
+
definition: () => definition7,
|
|
2258
|
+
execute: () => execute7,
|
|
2259
|
+
run: () => run7,
|
|
2260
|
+
terminal: () => terminal7
|
|
1714
2261
|
});
|
|
1715
2262
|
init_github();
|
|
1716
|
-
import
|
|
1717
|
-
import { select as
|
|
2263
|
+
import chalk9 from "chalk";
|
|
2264
|
+
import { select as select6, input as promptInput3 } from "@inquirer/prompts";
|
|
1718
2265
|
import ora7 from "ora";
|
|
1719
2266
|
|
|
1720
2267
|
// src/tools/reject/prompts.ts
|
|
@@ -1746,7 +2293,7 @@ Reviewer feedback: ${userFeedback}`,
|
|
|
1746
2293
|
}
|
|
1747
2294
|
|
|
1748
2295
|
// src/tools/reject/index.ts
|
|
1749
|
-
var
|
|
2296
|
+
var definition7 = {
|
|
1750
2297
|
type: "function",
|
|
1751
2298
|
function: {
|
|
1752
2299
|
name: "reject",
|
|
@@ -1761,7 +2308,7 @@ var definition9 = {
|
|
|
1761
2308
|
}
|
|
1762
2309
|
}
|
|
1763
2310
|
};
|
|
1764
|
-
async function
|
|
2311
|
+
async function run7(input, config) {
|
|
1765
2312
|
const issueNumber = input["issue_number"];
|
|
1766
2313
|
const [me, issue] = await Promise.all([
|
|
1767
2314
|
getAuthenticatedUser(config),
|
|
@@ -1779,7 +2326,7 @@ async function run9(input, config) {
|
|
|
1779
2326
|
return "Cancelled.";
|
|
1780
2327
|
}
|
|
1781
2328
|
if (!feedback.trim()) return "Cancelled.";
|
|
1782
|
-
const divider =
|
|
2329
|
+
const divider = chalk9.dim("\u2500".repeat(70));
|
|
1783
2330
|
for (; ; ) {
|
|
1784
2331
|
const spinner = ora7("Generating rejection comment\u2026").start();
|
|
1785
2332
|
let comment;
|
|
@@ -1791,13 +2338,13 @@ async function run9(input, config) {
|
|
|
1791
2338
|
return `Error generating comment: ${err.message}`;
|
|
1792
2339
|
}
|
|
1793
2340
|
console.log("\n" + divider);
|
|
1794
|
-
console.log(
|
|
2341
|
+
console.log(chalk9.bold(` Rejection preview \u2014 issue #${issueNumber}`));
|
|
1795
2342
|
console.log(divider);
|
|
1796
2343
|
console.log(renderMarkdown(comment));
|
|
1797
2344
|
console.log(divider + "\n");
|
|
1798
2345
|
let decision;
|
|
1799
2346
|
try {
|
|
1800
|
-
decision = await
|
|
2347
|
+
decision = await select6({
|
|
1801
2348
|
message: `Post rejection and mark #${issueNumber} as changes-needed?`,
|
|
1802
2349
|
choices: [
|
|
1803
2350
|
{ name: "Post & Reject", value: "yes" },
|
|
@@ -1836,7 +2383,7 @@ async function run9(input, config) {
|
|
|
1836
2383
|
return `Task #${issueNumber} rejected. Label changed to changes-needed.`;
|
|
1837
2384
|
}
|
|
1838
2385
|
}
|
|
1839
|
-
async function
|
|
2386
|
+
async function execute7(input, config) {
|
|
1840
2387
|
const issueNumber = input["issue_number"];
|
|
1841
2388
|
const feedback = input["feedback"];
|
|
1842
2389
|
const [me, issue] = await Promise.all([
|
|
@@ -1867,150 +2414,181 @@ async function execute9(input, config) {
|
|
|
1867
2414
|
Comment posted:
|
|
1868
2415
|
${comment}`;
|
|
1869
2416
|
}
|
|
1870
|
-
var
|
|
2417
|
+
var terminal7 = true;
|
|
1871
2418
|
|
|
1872
|
-
// src/tools/
|
|
1873
|
-
var
|
|
1874
|
-
__export(accept_exports, {
|
|
1875
|
-
definition: () => definition10,
|
|
1876
|
-
execute: () => execute10,
|
|
1877
|
-
run: () => run10,
|
|
1878
|
-
terminal: () => terminal10
|
|
1879
|
-
});
|
|
1880
|
-
init_github();
|
|
1881
|
-
import chalk9 from "chalk";
|
|
1882
|
-
import { select as select6 } from "@inquirer/prompts";
|
|
1883
|
-
import ora8 from "ora";
|
|
1884
|
-
var definition10 = {
|
|
2419
|
+
// src/tools/review/index.ts
|
|
2420
|
+
var definition8 = {
|
|
1885
2421
|
type: "function",
|
|
1886
2422
|
function: {
|
|
1887
|
-
name: "
|
|
1888
|
-
description: "
|
|
1889
|
-
parameters: {
|
|
1890
|
-
type: "object",
|
|
1891
|
-
properties: {
|
|
1892
|
-
issue_number: { type: "number", description: "GitHub issue number to accept" }
|
|
1893
|
-
},
|
|
1894
|
-
required: ["issue_number"]
|
|
1895
|
-
}
|
|
2423
|
+
name: "review",
|
|
2424
|
+
description: "List tasks waiting for your review (submitted by others, created by you), then let you accept or reject one. Equivalent to /review.",
|
|
2425
|
+
parameters: { type: "object", properties: {}, required: [] }
|
|
1896
2426
|
}
|
|
1897
2427
|
};
|
|
1898
|
-
async function
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
spinner3.stop();
|
|
1910
|
-
return `Error: ${err.message}`;
|
|
1911
|
-
}
|
|
1912
|
-
if (tasks.length === 0) return "No tasks pending review.";
|
|
1913
|
-
try {
|
|
1914
|
-
issueNumber = await select6({
|
|
1915
|
-
message: "Which task to accept?",
|
|
1916
|
-
choices: tasks.map((t) => ({
|
|
1917
|
-
name: `#${t.number} @${t.assignee ?? "\u2014"} ${t.title}`,
|
|
1918
|
-
value: t.number
|
|
1919
|
-
}))
|
|
1920
|
-
});
|
|
1921
|
-
} catch {
|
|
1922
|
-
return "Cancelled.";
|
|
1923
|
-
}
|
|
2428
|
+
async function run8(_input, config) {
|
|
2429
|
+
const spinner = ora8("Loading tasks for review\u2026").start();
|
|
2430
|
+
let me;
|
|
2431
|
+
let tasks;
|
|
2432
|
+
try {
|
|
2433
|
+
me = await getAuthenticatedUser(config);
|
|
2434
|
+
tasks = await listTasksForReview(config, me);
|
|
2435
|
+
spinner.stop();
|
|
2436
|
+
} catch (err) {
|
|
2437
|
+
spinner.stop();
|
|
2438
|
+
return `Error: ${err.message}`;
|
|
1924
2439
|
}
|
|
1925
|
-
|
|
1926
|
-
let
|
|
1927
|
-
let issue;
|
|
2440
|
+
if (tasks.length === 0) return `No tasks pending review for @${me}.`;
|
|
2441
|
+
let issueNumber;
|
|
1928
2442
|
try {
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
2443
|
+
issueNumber = await select7({
|
|
2444
|
+
message: "Select a task to review:",
|
|
2445
|
+
choices: tasks.map((t) => ({
|
|
2446
|
+
name: `#${String(t.number).padEnd(4)} @${t.assignee ?? "\u2014"} ${t.title}`,
|
|
2447
|
+
value: t.number
|
|
2448
|
+
}))
|
|
2449
|
+
});
|
|
2450
|
+
} catch {
|
|
2451
|
+
return "Cancelled.";
|
|
2452
|
+
}
|
|
2453
|
+
const spinner2 = ora8(`Loading #${issueNumber}\u2026`).start();
|
|
2454
|
+
let pr;
|
|
2455
|
+
try {
|
|
2456
|
+
pr = await getTaskPR(config, issueNumber);
|
|
1933
2457
|
spinner2.stop();
|
|
1934
2458
|
} catch (err) {
|
|
1935
2459
|
spinner2.stop();
|
|
1936
|
-
return `Error: ${err.message}`;
|
|
2460
|
+
return `Error loading PR: ${err.message}`;
|
|
1937
2461
|
}
|
|
1938
|
-
|
|
1939
|
-
|
|
2462
|
+
const divider = chalk10.dim("\u2500".repeat(70));
|
|
2463
|
+
console.log("\n" + divider);
|
|
2464
|
+
if (pr) {
|
|
2465
|
+
console.log(chalk10.bold(` PR #${pr.number}`) + " " + chalk10.dim(pr.url));
|
|
2466
|
+
console.log(divider);
|
|
2467
|
+
console.log(renderMarkdown(pr.body));
|
|
2468
|
+
} else {
|
|
2469
|
+
console.log(chalk10.yellow(` No open PR found for task #${issueNumber}`));
|
|
2470
|
+
}
|
|
2471
|
+
console.log(divider + "\n");
|
|
2472
|
+
for (; ; ) {
|
|
2473
|
+
let action;
|
|
2474
|
+
try {
|
|
2475
|
+
action = await select7({
|
|
2476
|
+
message: "Review action:",
|
|
2477
|
+
choices: [
|
|
2478
|
+
...pr ? [{ name: "View diff", value: "diff" }] : [],
|
|
2479
|
+
{ name: chalk10.green("Accept") + " \u2014 merge PR and close issue", value: "accept" },
|
|
2480
|
+
{ name: chalk10.red("Reject") + " \u2014 request changes", value: "reject" },
|
|
2481
|
+
{ name: "Nothing, just viewing", value: "none" }
|
|
2482
|
+
]
|
|
2483
|
+
});
|
|
2484
|
+
} catch {
|
|
2485
|
+
return "Cancelled.";
|
|
2486
|
+
}
|
|
2487
|
+
if (action === "none") return `Viewed task #${issueNumber}.`;
|
|
2488
|
+
if (action === "accept") return run6({ issue_number: issueNumber }, config);
|
|
2489
|
+
if (action === "reject") return run7({ issue_number: issueNumber }, config);
|
|
2490
|
+
if (action === "diff") {
|
|
2491
|
+
const diffSpinner = ora8("Fetching diff\u2026").start();
|
|
2492
|
+
let diff;
|
|
2493
|
+
try {
|
|
2494
|
+
diff = await getTaskPRDiff(config, pr.number);
|
|
2495
|
+
diffSpinner.stop();
|
|
2496
|
+
} catch (err) {
|
|
2497
|
+
diffSpinner.stop();
|
|
2498
|
+
console.log(chalk10.red(`Error fetching diff: ${err.message}`));
|
|
2499
|
+
continue;
|
|
2500
|
+
}
|
|
2501
|
+
console.log("\n" + divider);
|
|
2502
|
+
for (const line of diff.split("\n")) {
|
|
2503
|
+
if (line.startsWith("+") && !line.startsWith("+++")) {
|
|
2504
|
+
process.stdout.write(chalk10.green(line) + "\n");
|
|
2505
|
+
} else if (line.startsWith("-") && !line.startsWith("---")) {
|
|
2506
|
+
process.stdout.write(chalk10.red(line) + "\n");
|
|
2507
|
+
} else if (line.startsWith("@@")) {
|
|
2508
|
+
process.stdout.write(chalk10.cyan(line) + "\n");
|
|
2509
|
+
} else if (line.startsWith("diff ") || line.startsWith("index ") || line.startsWith("+++") || line.startsWith("---")) {
|
|
2510
|
+
process.stdout.write(chalk10.bold(line) + "\n");
|
|
2511
|
+
} else {
|
|
2512
|
+
process.stdout.write(line + "\n");
|
|
2513
|
+
}
|
|
2514
|
+
}
|
|
2515
|
+
console.log(divider + "\n");
|
|
2516
|
+
}
|
|
2517
|
+
}
|
|
2518
|
+
}
|
|
2519
|
+
var execute8 = run8;
|
|
2520
|
+
var terminal8 = true;
|
|
2521
|
+
|
|
2522
|
+
// src/tools/refresh/index.ts
|
|
2523
|
+
var refresh_exports = {};
|
|
2524
|
+
__export(refresh_exports, {
|
|
2525
|
+
definition: () => definition9,
|
|
2526
|
+
execute: () => execute9,
|
|
2527
|
+
run: () => run9,
|
|
2528
|
+
terminal: () => terminal9
|
|
2529
|
+
});
|
|
2530
|
+
var definition9 = {
|
|
2531
|
+
type: "function",
|
|
2532
|
+
function: {
|
|
2533
|
+
name: "refresh",
|
|
2534
|
+
description: "Reload and display the full task list. Equivalent to /refresh.",
|
|
2535
|
+
parameters: { type: "object", properties: {}, required: [] }
|
|
1940
2536
|
}
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
2537
|
+
};
|
|
2538
|
+
async function run9(_input, config) {
|
|
2539
|
+
const tasks = await printTaskList(config);
|
|
2540
|
+
if (tasks.length === 0) return "No tasks found.";
|
|
2541
|
+
const lines = tasks.map((t) => {
|
|
2542
|
+
const status = getStatus(t);
|
|
2543
|
+
const assignee = t.assignee ? `@${t.assignee}` : "\u2014";
|
|
2544
|
+
return `#${t.number} [${status}] ${assignee} ${t.title}`;
|
|
2545
|
+
});
|
|
2546
|
+
return `Tasks (${tasks.length}):
|
|
2547
|
+
${lines.join("\n")}`;
|
|
2548
|
+
}
|
|
2549
|
+
var execute9 = run9;
|
|
2550
|
+
var terminal9 = true;
|
|
2551
|
+
|
|
2552
|
+
// src/tools/open-code/index.ts
|
|
2553
|
+
var open_code_exports = {};
|
|
2554
|
+
__export(open_code_exports, {
|
|
2555
|
+
definition: () => definition10,
|
|
2556
|
+
execute: () => execute10,
|
|
2557
|
+
run: () => run10,
|
|
2558
|
+
terminal: () => terminal10
|
|
2559
|
+
});
|
|
2560
|
+
init_github();
|
|
2561
|
+
var definition10 = {
|
|
2562
|
+
type: "function",
|
|
2563
|
+
function: {
|
|
2564
|
+
name: "open_code",
|
|
2565
|
+
description: "Launch Claude Code for the current task branch. Equivalent to /code.",
|
|
2566
|
+
parameters: { type: "object", properties: {}, required: [] }
|
|
1953
2567
|
}
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
let
|
|
2568
|
+
};
|
|
2569
|
+
async function run10(_input, config) {
|
|
2570
|
+
let branch;
|
|
1957
2571
|
try {
|
|
1958
|
-
|
|
1959
|
-
result = await acceptTask(config, issueNumber, assigneeWorkerBranch);
|
|
1960
|
-
spinner.succeed(`PR #${result.prNumber} merged into ${targetBranch}`);
|
|
2572
|
+
branch = await getCurrentBranch();
|
|
1961
2573
|
} catch (err) {
|
|
1962
|
-
spinner.fail("Failed");
|
|
1963
2574
|
return `Error: ${err.message}`;
|
|
1964
2575
|
}
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
choices: [
|
|
1971
|
-
{ name: `Yes, push to ${baseBranch}`, value: true },
|
|
1972
|
-
{ name: "No, keep in worker branch", value: false }
|
|
1973
|
-
]
|
|
1974
|
-
});
|
|
1975
|
-
} catch {
|
|
1976
|
-
pushToMain = false;
|
|
1977
|
-
}
|
|
1978
|
-
if (pushToMain) {
|
|
1979
|
-
const mergeSpinner = ora8(`Merging ${targetBranch} \u2192 ${baseBranch}\u2026`).start();
|
|
1980
|
-
try {
|
|
1981
|
-
await mergeWorkerIntoBase(config, targetBranch, baseBranch);
|
|
1982
|
-
mergeSpinner.succeed(`Merged ${targetBranch} \u2192 ${baseBranch}`);
|
|
1983
|
-
} catch (err) {
|
|
1984
|
-
mergeSpinner.fail(`Could not merge to ${baseBranch}: ${err.message}`);
|
|
1985
|
-
}
|
|
1986
|
-
}
|
|
1987
|
-
return `Task #${issueNumber} accepted.
|
|
1988
|
-
PR #${result.prNumber} merged \u2192 ${targetBranch}${pushToMain ? ` \u2192 ${baseBranch}` : ""}
|
|
1989
|
-
Issue closed.`;
|
|
1990
|
-
}
|
|
1991
|
-
async function execute10(input, config) {
|
|
1992
|
-
const issueNumber = input["issue_number"];
|
|
1993
|
-
const [me, issue] = await Promise.all([
|
|
1994
|
-
getAuthenticatedUser(config),
|
|
1995
|
-
getTask(config, issueNumber)
|
|
1996
|
-
]);
|
|
1997
|
-
if (issue.author && issue.author !== me) {
|
|
1998
|
-
return `Permission denied: only the task author (@${issue.author}) can accept task #${issueNumber}.`;
|
|
2576
|
+
let issueNum = getConfig().taskState?.activeIssueNumber;
|
|
2577
|
+
if (!issueNum) {
|
|
2578
|
+
const found = await getIssueNumberFromBranch(config, branch);
|
|
2579
|
+
if (!found) return `No active task found (current branch: ${branch}).`;
|
|
2580
|
+
issueNum = found.issueNumber;
|
|
1999
2581
|
}
|
|
2000
|
-
|
|
2001
|
-
const spinner = ora8(`Merging PR for #${issueNumber}\u2026`).start();
|
|
2582
|
+
let issue;
|
|
2002
2583
|
try {
|
|
2003
|
-
|
|
2004
|
-
const result = await acceptTask(config, issueNumber, assigneeWorkerBranch);
|
|
2005
|
-
spinner.stop();
|
|
2006
|
-
return `Task #${issueNumber} accepted.
|
|
2007
|
-
PR #${result.prNumber} merged \u2192 ${targetBranch}
|
|
2008
|
-
Issue closed.`;
|
|
2584
|
+
issue = await getTask(config, issueNum);
|
|
2009
2585
|
} catch (err) {
|
|
2010
|
-
spinner.stop();
|
|
2011
2586
|
return `Error: ${err.message}`;
|
|
2012
2587
|
}
|
|
2588
|
+
await launchClaudeCode(issue, branch);
|
|
2589
|
+
return "Claude Code session ended.";
|
|
2013
2590
|
}
|
|
2591
|
+
var execute10 = run10;
|
|
2014
2592
|
var terminal10 = true;
|
|
2015
2593
|
|
|
2016
2594
|
// src/tools/edit-task/index.ts
|
|
@@ -2022,7 +2600,7 @@ __export(edit_task_exports, {
|
|
|
2022
2600
|
terminal: () => terminal11
|
|
2023
2601
|
});
|
|
2024
2602
|
init_github();
|
|
2025
|
-
import { select as
|
|
2603
|
+
import { select as select8, input as promptInput4 } from "@inquirer/prompts";
|
|
2026
2604
|
import ora9 from "ora";
|
|
2027
2605
|
var definition11 = {
|
|
2028
2606
|
type: "function",
|
|
@@ -2051,7 +2629,7 @@ async function run11(input, config) {
|
|
|
2051
2629
|
}
|
|
2052
2630
|
if (tasks.length === 0) return "No tasks found.";
|
|
2053
2631
|
try {
|
|
2054
|
-
issueNumber = await
|
|
2632
|
+
issueNumber = await select8({
|
|
2055
2633
|
message: "Select task to edit:",
|
|
2056
2634
|
choices: tasks.map((t) => ({ name: `#${t.number} [${getStatus(t)}] ${t.title}`, value: t.number }))
|
|
2057
2635
|
});
|
|
@@ -2108,14 +2686,264 @@ async function execute11(input, config) {
|
|
|
2108
2686
|
}
|
|
2109
2687
|
var terminal11 = true;
|
|
2110
2688
|
|
|
2689
|
+
// src/tools/move-task/index.ts
|
|
2690
|
+
var move_task_exports = {};
|
|
2691
|
+
__export(move_task_exports, {
|
|
2692
|
+
definition: () => definition12,
|
|
2693
|
+
execute: () => execute12,
|
|
2694
|
+
run: () => run12,
|
|
2695
|
+
terminal: () => terminal12
|
|
2696
|
+
});
|
|
2697
|
+
init_github();
|
|
2698
|
+
import { select as select9 } from "@inquirer/prompts";
|
|
2699
|
+
import ora10 from "ora";
|
|
2700
|
+
import chalk11 from "chalk";
|
|
2701
|
+
var definition12 = {
|
|
2702
|
+
type: "function",
|
|
2703
|
+
function: {
|
|
2704
|
+
name: "move_task",
|
|
2705
|
+
description: "Move one of your own published tasks to be a sub-task of another task. Updates the target branch and base commit so executors sync from the new parent HEAD. Equivalent to /move.",
|
|
2706
|
+
parameters: {
|
|
2707
|
+
type: "object",
|
|
2708
|
+
properties: {
|
|
2709
|
+
issue_number: { type: "number", description: "Issue number of the task to move." },
|
|
2710
|
+
parent_issue_number: { type: "number", description: "Issue number of the new parent task." }
|
|
2711
|
+
},
|
|
2712
|
+
required: ["issue_number", "parent_issue_number"]
|
|
2713
|
+
}
|
|
2714
|
+
}
|
|
2715
|
+
};
|
|
2716
|
+
async function run12(input, config) {
|
|
2717
|
+
const me = await getAuthenticatedUser(config);
|
|
2718
|
+
const allTasks = await listTasks(config);
|
|
2719
|
+
let issueNumber = input["issue_number"];
|
|
2720
|
+
let taskToMove;
|
|
2721
|
+
if (issueNumber) {
|
|
2722
|
+
try {
|
|
2723
|
+
taskToMove = await getTask(config, issueNumber);
|
|
2724
|
+
} catch (err) {
|
|
2725
|
+
return `Error loading task #${issueNumber}: ${err.message}`;
|
|
2726
|
+
}
|
|
2727
|
+
if (taskToMove.author !== me) {
|
|
2728
|
+
return `Task #${issueNumber} was not authored by you \u2014 you can only move your own tasks.`;
|
|
2729
|
+
}
|
|
2730
|
+
} else {
|
|
2731
|
+
const myTasks = allTasks.filter((t) => t.author === me);
|
|
2732
|
+
if (myTasks.length === 0) return "No tasks you authored are available to move.";
|
|
2733
|
+
try {
|
|
2734
|
+
issueNumber = await select9({
|
|
2735
|
+
message: "Select task to move:",
|
|
2736
|
+
choices: myTasks.map((t) => ({
|
|
2737
|
+
name: `#${t.number} [${getStatus(t)}] ${t.title}`,
|
|
2738
|
+
value: t.number
|
|
2739
|
+
}))
|
|
2740
|
+
});
|
|
2741
|
+
} catch {
|
|
2742
|
+
return "Cancelled.";
|
|
2743
|
+
}
|
|
2744
|
+
taskToMove = myTasks.find((t) => t.number === issueNumber);
|
|
2745
|
+
}
|
|
2746
|
+
const candidates = allTasks.filter((t) => t.number !== taskToMove.number);
|
|
2747
|
+
const resolveSpinner = ora10("Finding parent task branches\u2026").start();
|
|
2748
|
+
const parents = [];
|
|
2749
|
+
for (const t of candidates) {
|
|
2750
|
+
const branch = await getTaskBranch(config, t.number);
|
|
2751
|
+
if (branch) parents.push({ task: t, branch });
|
|
2752
|
+
}
|
|
2753
|
+
resolveSpinner.stop();
|
|
2754
|
+
if (parents.length === 0) {
|
|
2755
|
+
return "No other tasks with known branches are available as a parent.";
|
|
2756
|
+
}
|
|
2757
|
+
let parentIssueNumber = input["parent_issue_number"];
|
|
2758
|
+
let chosen;
|
|
2759
|
+
if (parentIssueNumber) {
|
|
2760
|
+
const found = parents.find((p) => p.task.number === parentIssueNumber);
|
|
2761
|
+
if (!found) {
|
|
2762
|
+
return `Task #${parentIssueNumber} is not available as a parent (no branch found or not open).`;
|
|
2763
|
+
}
|
|
2764
|
+
chosen = found;
|
|
2765
|
+
} else {
|
|
2766
|
+
try {
|
|
2767
|
+
const selectedBranch = await select9({
|
|
2768
|
+
message: `Move #${taskToMove.number} under which task?`,
|
|
2769
|
+
choices: parents.map((p) => ({
|
|
2770
|
+
name: `#${p.task.number} [${getStatus(p.task)}] ${p.task.title} ${chalk11.dim("\u2192 " + p.branch)}`,
|
|
2771
|
+
value: p.branch
|
|
2772
|
+
}))
|
|
2773
|
+
});
|
|
2774
|
+
chosen = parents.find((p) => p.branch === selectedBranch);
|
|
2775
|
+
} catch {
|
|
2776
|
+
return "Cancelled.";
|
|
2777
|
+
}
|
|
2778
|
+
}
|
|
2779
|
+
const sha = await getBranchHeadSha(config, chosen.branch);
|
|
2780
|
+
if (!sha) {
|
|
2781
|
+
return `Could not resolve HEAD of branch ${chosen.branch} \u2014 does it exist on the remote?`;
|
|
2782
|
+
}
|
|
2783
|
+
const spinner = ora10(`Moving #${taskToMove.number} under #${chosen.task.number}\u2026`).start();
|
|
2784
|
+
try {
|
|
2785
|
+
await moveTask(config, taskToMove.number, chosen.branch, sha);
|
|
2786
|
+
spinner.succeed(
|
|
2787
|
+
`Task #${taskToMove.number} moved under #${chosen.task.number} "${chosen.task.title}"
|
|
2788
|
+
target: ${chalk11.cyan(chosen.branch)} base: ${chalk11.dim(sha.slice(0, 7))}`
|
|
2789
|
+
);
|
|
2790
|
+
return `Task #${taskToMove.number} moved under #${chosen.task.number} (branch: ${chosen.branch}, base: ${sha.slice(0, 7)})`;
|
|
2791
|
+
} catch (err) {
|
|
2792
|
+
spinner.fail(`Failed: ${err.message}`);
|
|
2793
|
+
return `Error: ${err.message}`;
|
|
2794
|
+
}
|
|
2795
|
+
}
|
|
2796
|
+
async function execute12(input, config) {
|
|
2797
|
+
const me = await getAuthenticatedUser(config);
|
|
2798
|
+
const issueNumber = input["issue_number"];
|
|
2799
|
+
const parentIssueNumber = input["parent_issue_number"];
|
|
2800
|
+
const task = await getTask(config, issueNumber);
|
|
2801
|
+
if (task.author !== me) {
|
|
2802
|
+
return `Task #${issueNumber} was not authored by you \u2014 you can only move your own tasks.`;
|
|
2803
|
+
}
|
|
2804
|
+
const branch = await getTaskBranch(config, parentIssueNumber);
|
|
2805
|
+
if (!branch) return `No branch found for parent task #${parentIssueNumber}.`;
|
|
2806
|
+
const sha = await getBranchHeadSha(config, branch);
|
|
2807
|
+
if (!sha) return `Could not resolve HEAD of branch ${branch}.`;
|
|
2808
|
+
await moveTask(config, issueNumber, branch, sha);
|
|
2809
|
+
return `Task #${issueNumber} moved under #${parentIssueNumber} (branch: ${branch}, base: ${sha.slice(0, 7)})`;
|
|
2810
|
+
}
|
|
2811
|
+
var terminal12 = true;
|
|
2812
|
+
|
|
2813
|
+
// src/tools/wiki/index.ts
|
|
2814
|
+
var wiki_exports = {};
|
|
2815
|
+
__export(wiki_exports, {
|
|
2816
|
+
definition: () => definition13,
|
|
2817
|
+
execute: () => execute13,
|
|
2818
|
+
run: () => run13,
|
|
2819
|
+
terminal: () => terminal13
|
|
2820
|
+
});
|
|
2821
|
+
import ora11 from "ora";
|
|
2822
|
+
import chalk12 from "chalk";
|
|
2823
|
+
import { readFile as readFile2 } from "fs/promises";
|
|
2824
|
+
import { select as select10 } from "@inquirer/prompts";
|
|
2825
|
+
init_github();
|
|
2826
|
+
var WIKI_PATH = "TECHUNTER.md";
|
|
2827
|
+
var definition13 = {
|
|
2828
|
+
type: "function",
|
|
2829
|
+
function: {
|
|
2830
|
+
name: "update_wiki",
|
|
2831
|
+
description: "Generate or refresh the project overview document (TECHUNTER.md) by scanning the codebase. The document helps new team members understand what the project does, how it is architected, and how to start contributing. Equivalent to /wiki.",
|
|
2832
|
+
parameters: {
|
|
2833
|
+
type: "object",
|
|
2834
|
+
properties: {},
|
|
2835
|
+
required: []
|
|
2836
|
+
}
|
|
2837
|
+
}
|
|
2838
|
+
};
|
|
2839
|
+
async function readWikiContent(config) {
|
|
2840
|
+
try {
|
|
2841
|
+
return await readFile2(WIKI_PATH, "utf-8");
|
|
2842
|
+
} catch {
|
|
2843
|
+
}
|
|
2844
|
+
return getRepoFile(config, WIKI_PATH);
|
|
2845
|
+
}
|
|
2846
|
+
function printWiki(content) {
|
|
2847
|
+
const divider = chalk12.dim("\u2500".repeat(70));
|
|
2848
|
+
console.log("\n" + divider);
|
|
2849
|
+
console.log(chalk12.bold(" TECHUNTER.md"));
|
|
2850
|
+
console.log(divider);
|
|
2851
|
+
console.log(renderMarkdown(content));
|
|
2852
|
+
console.log(divider + "\n");
|
|
2853
|
+
}
|
|
2854
|
+
async function run13(_input, config) {
|
|
2855
|
+
const fetchSpinner = ora11("Checking for existing wiki\u2026").start();
|
|
2856
|
+
const existing = await readWikiContent(config).catch(() => null);
|
|
2857
|
+
fetchSpinner.stop();
|
|
2858
|
+
let action;
|
|
2859
|
+
try {
|
|
2860
|
+
action = await select10({
|
|
2861
|
+
message: "TECHUNTER.md \u2014 what would you like to do?",
|
|
2862
|
+
choices: [
|
|
2863
|
+
...existing ? [{ name: "View current wiki", value: "view" }] : [],
|
|
2864
|
+
{ name: existing ? "Regenerate & commit to repo" : "Generate & commit to repo", value: "generate" },
|
|
2865
|
+
{ name: "Cancel", value: "cancel" }
|
|
2866
|
+
]
|
|
2867
|
+
});
|
|
2868
|
+
} catch {
|
|
2869
|
+
return "Cancelled.";
|
|
2870
|
+
}
|
|
2871
|
+
if (action === "cancel") return "Cancelled.";
|
|
2872
|
+
if (action === "view") {
|
|
2873
|
+
printWiki(existing);
|
|
2874
|
+
return "Displayed TECHUNTER.md.";
|
|
2875
|
+
}
|
|
2876
|
+
const authSpinner = ora11("Checking permissions\u2026").start();
|
|
2877
|
+
let me;
|
|
2878
|
+
let allowed;
|
|
2879
|
+
try {
|
|
2880
|
+
me = await getAuthenticatedUser(config);
|
|
2881
|
+
allowed = await isCollaborator(config, me);
|
|
2882
|
+
authSpinner.stop();
|
|
2883
|
+
} catch (err) {
|
|
2884
|
+
authSpinner.stop();
|
|
2885
|
+
return `Error checking permissions: ${err.message}`;
|
|
2886
|
+
}
|
|
2887
|
+
if (!allowed) {
|
|
2888
|
+
return `Permission denied: only repository collaborators can update the project wiki.`;
|
|
2889
|
+
}
|
|
2890
|
+
const genSpinner = ora11("Analyzing project and generating overview\u2026").start();
|
|
2891
|
+
let content;
|
|
2892
|
+
try {
|
|
2893
|
+
content = await generateWiki(config);
|
|
2894
|
+
genSpinner.stop();
|
|
2895
|
+
} catch (err) {
|
|
2896
|
+
genSpinner.stop();
|
|
2897
|
+
return `Error generating wiki: ${err.message}`;
|
|
2898
|
+
}
|
|
2899
|
+
printWiki(content);
|
|
2900
|
+
let confirm;
|
|
2901
|
+
try {
|
|
2902
|
+
confirm = await select10({
|
|
2903
|
+
message: `Publish to repository as ${WIKI_PATH}?`,
|
|
2904
|
+
choices: [
|
|
2905
|
+
{ name: "Yes, commit to repo", value: "publish" },
|
|
2906
|
+
{ name: "Cancel", value: "cancel" }
|
|
2907
|
+
]
|
|
2908
|
+
});
|
|
2909
|
+
} catch {
|
|
2910
|
+
return "Cancelled.";
|
|
2911
|
+
}
|
|
2912
|
+
if (confirm === "cancel") return "Cancelled.";
|
|
2913
|
+
const writeSpinner = ora11(`Writing ${WIKI_PATH}\u2026`).start();
|
|
2914
|
+
try {
|
|
2915
|
+
const url = await upsertRepoFile(config, WIKI_PATH, content, "docs: update TECHUNTER.md project overview");
|
|
2916
|
+
writeSpinner.succeed(`Written: ${url}`);
|
|
2917
|
+
console.log("");
|
|
2918
|
+
return `TECHUNTER.md updated \u2014 ${url}`;
|
|
2919
|
+
} catch (err) {
|
|
2920
|
+
writeSpinner.fail(`Failed: ${err.message}`);
|
|
2921
|
+
return `Error: ${err.message}`;
|
|
2922
|
+
}
|
|
2923
|
+
}
|
|
2924
|
+
async function execute13(_input, config) {
|
|
2925
|
+
const me = await getAuthenticatedUser(config);
|
|
2926
|
+
if (!await isCollaborator(config, me)) {
|
|
2927
|
+
return `Permission denied: only repository collaborators can update the project wiki.`;
|
|
2928
|
+
}
|
|
2929
|
+
const content = await generateWiki(config);
|
|
2930
|
+
try {
|
|
2931
|
+
const url = await upsertRepoFile(config, WIKI_PATH, content, "docs: update TECHUNTER.md project overview");
|
|
2932
|
+
return `TECHUNTER.md updated \u2014 ${url}`;
|
|
2933
|
+
} catch (err) {
|
|
2934
|
+
return `Error: ${err.message}`;
|
|
2935
|
+
}
|
|
2936
|
+
}
|
|
2937
|
+
var terminal13 = true;
|
|
2938
|
+
|
|
2111
2939
|
// src/tools/list-tasks/index.ts
|
|
2112
2940
|
var list_tasks_exports = {};
|
|
2113
2941
|
__export(list_tasks_exports, {
|
|
2114
|
-
definition: () =>
|
|
2115
|
-
execute: () =>
|
|
2942
|
+
definition: () => definition14,
|
|
2943
|
+
execute: () => execute14
|
|
2116
2944
|
});
|
|
2117
2945
|
init_github();
|
|
2118
|
-
var
|
|
2946
|
+
var definition14 = {
|
|
2119
2947
|
type: "function",
|
|
2120
2948
|
function: {
|
|
2121
2949
|
name: "list_tasks",
|
|
@@ -2127,7 +2955,7 @@ var definition12 = {
|
|
|
2127
2955
|
}
|
|
2128
2956
|
}
|
|
2129
2957
|
};
|
|
2130
|
-
async function
|
|
2958
|
+
async function execute14(_input, config) {
|
|
2131
2959
|
const tasks = await listTasks(config);
|
|
2132
2960
|
if (tasks.length === 0) return "No open tasks.";
|
|
2133
2961
|
return tasks.map((t) => {
|
|
@@ -2140,11 +2968,11 @@ async function execute12(_input, config) {
|
|
|
2140
2968
|
// src/tools/get-task/index.ts
|
|
2141
2969
|
var get_task_exports = {};
|
|
2142
2970
|
__export(get_task_exports, {
|
|
2143
|
-
definition: () =>
|
|
2144
|
-
execute: () =>
|
|
2971
|
+
definition: () => definition15,
|
|
2972
|
+
execute: () => execute15
|
|
2145
2973
|
});
|
|
2146
2974
|
init_github();
|
|
2147
|
-
var
|
|
2975
|
+
var definition15 = {
|
|
2148
2976
|
type: "function",
|
|
2149
2977
|
function: {
|
|
2150
2978
|
name: "get_task",
|
|
@@ -2158,7 +2986,7 @@ var definition13 = {
|
|
|
2158
2986
|
}
|
|
2159
2987
|
}
|
|
2160
2988
|
};
|
|
2161
|
-
async function
|
|
2989
|
+
async function execute15(input, config) {
|
|
2162
2990
|
const issue = await getTask(config, input["issue_number"]);
|
|
2163
2991
|
const status = issue.labels.find((l) => l.startsWith("techunter:"))?.replace("techunter:", "") ?? "unknown";
|
|
2164
2992
|
const assignee = issue.assignee ? `@${issue.assignee}` : "\u2014";
|
|
@@ -2175,12 +3003,12 @@ ${issue.body}`);
|
|
|
2175
3003
|
// src/tools/get-comments/index.ts
|
|
2176
3004
|
var get_comments_exports = {};
|
|
2177
3005
|
__export(get_comments_exports, {
|
|
2178
|
-
definition: () =>
|
|
2179
|
-
execute: () =>
|
|
3006
|
+
definition: () => definition16,
|
|
3007
|
+
execute: () => execute16
|
|
2180
3008
|
});
|
|
2181
3009
|
init_github();
|
|
2182
|
-
import
|
|
2183
|
-
var
|
|
3010
|
+
import ora12 from "ora";
|
|
3011
|
+
var definition16 = {
|
|
2184
3012
|
type: "function",
|
|
2185
3013
|
function: {
|
|
2186
3014
|
name: "get_comments",
|
|
@@ -2195,10 +3023,10 @@ var definition14 = {
|
|
|
2195
3023
|
}
|
|
2196
3024
|
}
|
|
2197
3025
|
};
|
|
2198
|
-
async function
|
|
3026
|
+
async function execute16(input, config) {
|
|
2199
3027
|
const issueNumber = input["issue_number"];
|
|
2200
3028
|
const limit = input["limit"] ?? 5;
|
|
2201
|
-
const spinner =
|
|
3029
|
+
const spinner = ora12(`Loading comments for #${issueNumber}...`).start();
|
|
2202
3030
|
try {
|
|
2203
3031
|
const comments = await listComments(config, issueNumber, limit);
|
|
2204
3032
|
spinner.stop();
|
|
@@ -2217,11 +3045,11 @@ ${lines.join("\n\n")}`;
|
|
|
2217
3045
|
// src/tools/get-diff/index.ts
|
|
2218
3046
|
var get_diff_exports = {};
|
|
2219
3047
|
__export(get_diff_exports, {
|
|
2220
|
-
definition: () =>
|
|
2221
|
-
execute: () =>
|
|
3048
|
+
definition: () => definition17,
|
|
3049
|
+
execute: () => execute17
|
|
2222
3050
|
});
|
|
2223
|
-
import
|
|
2224
|
-
var
|
|
3051
|
+
import ora13 from "ora";
|
|
3052
|
+
var definition17 = {
|
|
2225
3053
|
type: "function",
|
|
2226
3054
|
function: {
|
|
2227
3055
|
name: "get_diff",
|
|
@@ -2229,8 +3057,8 @@ var definition15 = {
|
|
|
2229
3057
|
parameters: { type: "object", properties: {}, required: [] }
|
|
2230
3058
|
}
|
|
2231
3059
|
};
|
|
2232
|
-
async function
|
|
2233
|
-
const spinner =
|
|
3060
|
+
async function execute17(_input, _config) {
|
|
3061
|
+
const spinner = ora13("Reading git diff...").start();
|
|
2234
3062
|
try {
|
|
2235
3063
|
const diff = await getDiff();
|
|
2236
3064
|
spinner.stop();
|
|
@@ -2244,14 +3072,14 @@ async function execute15(_input, _config) {
|
|
|
2244
3072
|
// src/tools/run-command/index.ts
|
|
2245
3073
|
var run_command_exports = {};
|
|
2246
3074
|
__export(run_command_exports, {
|
|
2247
|
-
definition: () =>
|
|
2248
|
-
execute: () =>
|
|
3075
|
+
definition: () => definition18,
|
|
3076
|
+
execute: () => execute18
|
|
2249
3077
|
});
|
|
2250
3078
|
import { exec } from "child_process";
|
|
2251
3079
|
import { promisify } from "util";
|
|
2252
|
-
import
|
|
3080
|
+
import ora14 from "ora";
|
|
2253
3081
|
var execAsync = promisify(exec);
|
|
2254
|
-
var
|
|
3082
|
+
var definition18 = {
|
|
2255
3083
|
type: "function",
|
|
2256
3084
|
function: {
|
|
2257
3085
|
name: "run_command",
|
|
@@ -2265,10 +3093,10 @@ var definition16 = {
|
|
|
2265
3093
|
}
|
|
2266
3094
|
}
|
|
2267
3095
|
};
|
|
2268
|
-
async function
|
|
3096
|
+
async function execute18(input, _config) {
|
|
2269
3097
|
const command = input["command"];
|
|
2270
3098
|
const cwd = process.cwd();
|
|
2271
|
-
const spinner =
|
|
3099
|
+
const spinner = ora14(`$ ${command}`).start();
|
|
2272
3100
|
try {
|
|
2273
3101
|
const { stdout, stderr } = await execAsync(command, { cwd, timeout: 6e4, maxBuffer: 1024 * 1024 });
|
|
2274
3102
|
spinner.stop();
|
|
@@ -2287,15 +3115,15 @@ ${out || e.message}`;
|
|
|
2287
3115
|
// src/tools/list-files/index.ts
|
|
2288
3116
|
var list_files_exports = {};
|
|
2289
3117
|
__export(list_files_exports, {
|
|
2290
|
-
definition: () =>
|
|
2291
|
-
execute: () =>
|
|
3118
|
+
definition: () => definition19,
|
|
3119
|
+
execute: () => execute19
|
|
2292
3120
|
});
|
|
2293
|
-
import { readFile as
|
|
3121
|
+
import { readFile as readFile3 } from "fs/promises";
|
|
2294
3122
|
import { existsSync } from "fs";
|
|
2295
3123
|
import path2 from "path";
|
|
2296
3124
|
import { globby } from "globby";
|
|
2297
3125
|
import ignore from "ignore";
|
|
2298
|
-
var
|
|
3126
|
+
var definition19 = {
|
|
2299
3127
|
type: "function",
|
|
2300
3128
|
function: {
|
|
2301
3129
|
name: "list_files",
|
|
@@ -2335,13 +3163,13 @@ var BINARY_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
|
2335
3163
|
".sqlite",
|
|
2336
3164
|
".lock"
|
|
2337
3165
|
]);
|
|
2338
|
-
async function
|
|
3166
|
+
async function execute19(input, _config) {
|
|
2339
3167
|
const glob = input["glob"] ?? "**/*";
|
|
2340
3168
|
const cwd = process.cwd();
|
|
2341
3169
|
const ig = ignore();
|
|
2342
3170
|
const gitignorePath = path2.join(cwd, ".gitignore");
|
|
2343
3171
|
if (existsSync(gitignorePath)) {
|
|
2344
|
-
ig.add(await
|
|
3172
|
+
ig.add(await readFile3(gitignorePath, "utf-8"));
|
|
2345
3173
|
}
|
|
2346
3174
|
ig.add(["node_modules", "dist", ".git", ".next", "__pycache__", "build", "coverage"]);
|
|
2347
3175
|
const files = await globby(glob, { cwd, dot: false, onlyFiles: true, gitignore: false });
|
|
@@ -2354,15 +3182,15 @@ ${filtered.join("\n")}`;
|
|
|
2354
3182
|
// src/tools/grep-code/index.ts
|
|
2355
3183
|
var grep_code_exports = {};
|
|
2356
3184
|
__export(grep_code_exports, {
|
|
2357
|
-
definition: () =>
|
|
2358
|
-
execute: () =>
|
|
3185
|
+
definition: () => definition20,
|
|
3186
|
+
execute: () => execute20
|
|
2359
3187
|
});
|
|
2360
|
-
import { readFile as
|
|
3188
|
+
import { readFile as readFile4 } from "fs/promises";
|
|
2361
3189
|
import { existsSync as existsSync2 } from "fs";
|
|
2362
3190
|
import path3 from "path";
|
|
2363
3191
|
import { globby as globby2 } from "globby";
|
|
2364
3192
|
import ignore2 from "ignore";
|
|
2365
|
-
var
|
|
3193
|
+
var definition20 = {
|
|
2366
3194
|
type: "function",
|
|
2367
3195
|
function: {
|
|
2368
3196
|
name: "grep_code",
|
|
@@ -2403,7 +3231,7 @@ async function buildIgnore(cwd) {
|
|
|
2403
3231
|
const ig = ignore2();
|
|
2404
3232
|
const gitignorePath = path3.join(cwd, ".gitignore");
|
|
2405
3233
|
if (existsSync2(gitignorePath)) {
|
|
2406
|
-
ig.add(await
|
|
3234
|
+
ig.add(await readFile4(gitignorePath, "utf-8"));
|
|
2407
3235
|
}
|
|
2408
3236
|
ig.add(["node_modules", "dist", ".git", ".next", "__pycache__", "build", "coverage"]);
|
|
2409
3237
|
return ig;
|
|
@@ -2435,7 +3263,7 @@ function isText(f) {
|
|
|
2435
3263
|
return !BINARY_EXTENSIONS2.has(path3.extname(f).toLowerCase());
|
|
2436
3264
|
}
|
|
2437
3265
|
var MAX_RANGE_LINES = 300;
|
|
2438
|
-
async function
|
|
3266
|
+
async function execute20(input, _config) {
|
|
2439
3267
|
const pattern = input["pattern"] ?? "";
|
|
2440
3268
|
const fileGlob = input["file_glob"] ?? "**/*";
|
|
2441
3269
|
const contextLines = Math.min(input["context_lines"] ?? 2, 5);
|
|
@@ -2447,7 +3275,7 @@ async function execute18(input, _config) {
|
|
|
2447
3275
|
const files = await globby2(fileGlob, { cwd, dot: false, onlyFiles: true, gitignore: false });
|
|
2448
3276
|
if (files.length === 0) return `No file matched: ${fileGlob}`;
|
|
2449
3277
|
if (files.length > 1) return `file_glob matched ${files.length} files \u2014 narrow it to a single file for read-range mode.`;
|
|
2450
|
-
const raw = await
|
|
3278
|
+
const raw = await readFile4(path3.join(cwd, files[0]), "utf-8");
|
|
2451
3279
|
const lines = raw.split("\n");
|
|
2452
3280
|
const total = lines.length;
|
|
2453
3281
|
const from = Math.max(1, startLine);
|
|
@@ -2476,7 +3304,7 @@ ${numbered}
|
|
|
2476
3304
|
if (totalMatches >= maxResults) break;
|
|
2477
3305
|
let content;
|
|
2478
3306
|
try {
|
|
2479
|
-
content = await
|
|
3307
|
+
content = await readFile4(path3.join(cwd, file), "utf-8");
|
|
2480
3308
|
} catch {
|
|
2481
3309
|
continue;
|
|
2482
3310
|
}
|
|
@@ -2522,12 +3350,12 @@ ${snippets.join("\n---\n")}
|
|
|
2522
3350
|
// src/tools/ask-user/index.ts
|
|
2523
3351
|
var ask_user_exports = {};
|
|
2524
3352
|
__export(ask_user_exports, {
|
|
2525
|
-
definition: () =>
|
|
2526
|
-
execute: () =>
|
|
3353
|
+
definition: () => definition21,
|
|
3354
|
+
execute: () => execute21
|
|
2527
3355
|
});
|
|
2528
|
-
import
|
|
2529
|
-
import { select as
|
|
2530
|
-
var
|
|
3356
|
+
import chalk13 from "chalk";
|
|
3357
|
+
import { select as select11, input as promptInput5 } from "@inquirer/prompts";
|
|
3358
|
+
var definition21 = {
|
|
2531
3359
|
type: "function",
|
|
2532
3360
|
function: {
|
|
2533
3361
|
name: "ask_user",
|
|
@@ -2546,24 +3374,24 @@ var definition19 = {
|
|
|
2546
3374
|
}
|
|
2547
3375
|
}
|
|
2548
3376
|
};
|
|
2549
|
-
async function
|
|
3377
|
+
async function execute21(input, _config) {
|
|
2550
3378
|
const question = input["question"];
|
|
2551
3379
|
const options = input["options"];
|
|
2552
3380
|
const OTHER = "__other__";
|
|
2553
3381
|
console.log("");
|
|
2554
|
-
console.log(
|
|
2555
|
-
console.log(
|
|
3382
|
+
console.log(chalk13.dim(" \u250C\u2500 Agent question " + "\u2500".repeat(51)));
|
|
3383
|
+
console.log(chalk13.dim(" \u2502"));
|
|
2556
3384
|
for (const line of question.split("\n")) {
|
|
2557
|
-
console.log(
|
|
3385
|
+
console.log(chalk13.dim(" \u2502 ") + line);
|
|
2558
3386
|
}
|
|
2559
|
-
console.log(
|
|
3387
|
+
console.log(chalk13.dim(" \u2514" + "\u2500".repeat(67)));
|
|
2560
3388
|
let answer;
|
|
2561
3389
|
try {
|
|
2562
|
-
const chosen = await
|
|
3390
|
+
const chosen = await select11({
|
|
2563
3391
|
message: " ",
|
|
2564
3392
|
choices: [
|
|
2565
3393
|
...options.map((o) => ({ name: o, value: o })),
|
|
2566
|
-
{ name:
|
|
3394
|
+
{ name: chalk13.dim("Other (describe below)"), value: OTHER }
|
|
2567
3395
|
]
|
|
2568
3396
|
});
|
|
2569
3397
|
answer = chosen === OTHER ? await promptInput5({ message: "Your answer:" }) : chosen;
|
|
@@ -2588,6 +3416,8 @@ var toolModules = [
|
|
|
2588
3416
|
reject_exports,
|
|
2589
3417
|
accept_exports,
|
|
2590
3418
|
edit_task_exports,
|
|
3419
|
+
move_task_exports,
|
|
3420
|
+
wiki_exports,
|
|
2591
3421
|
// Low-level tools
|
|
2592
3422
|
list_tasks_exports,
|
|
2593
3423
|
get_task_exports,
|