techunter 1.0.0 → 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 +237 -237
- package/dist/index.js +328 -149
- package/dist/mcp.js +221 -62
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -46,11 +46,13 @@ __export(github_exports, {
|
|
|
46
46
|
extractTargetBranch: () => extractTargetBranch,
|
|
47
47
|
formatGuideAsMarkdown: () => formatGuideAsMarkdown,
|
|
48
48
|
getAuthenticatedUser: () => getAuthenticatedUser,
|
|
49
|
+
getBranchHeadSha: () => getBranchHeadSha,
|
|
49
50
|
getDefaultBranch: () => getDefaultBranch,
|
|
50
51
|
getIssueNumberFromBranch: () => getIssueNumberFromBranch,
|
|
51
52
|
getOpenSubtasks: () => getOpenSubtasks,
|
|
52
53
|
getRepoFile: () => getRepoFile,
|
|
53
54
|
getTask: () => getTask,
|
|
55
|
+
getTaskBranch: () => getTaskBranch,
|
|
54
56
|
getTaskPR: () => getTaskPR,
|
|
55
57
|
getTaskPRDiff: () => getTaskPRDiff,
|
|
56
58
|
isCollaborator: () => isCollaborator,
|
|
@@ -60,6 +62,7 @@ __export(github_exports, {
|
|
|
60
62
|
listTasksForReview: () => listTasksForReview,
|
|
61
63
|
markInReview: () => markInReview,
|
|
62
64
|
mergeWorkerIntoBase: () => mergeWorkerIntoBase,
|
|
65
|
+
moveTask: () => moveTask,
|
|
63
66
|
postComment: () => postComment,
|
|
64
67
|
postGuideComment: () => postGuideComment,
|
|
65
68
|
rejectTask: () => rejectTask,
|
|
@@ -437,6 +440,37 @@ async function getDefaultBranch(config) {
|
|
|
437
440
|
const { data } = await octokit.repos.get({ owner, repo });
|
|
438
441
|
return data.default_branch;
|
|
439
442
|
}
|
|
443
|
+
async function getTaskBranch(config, issueNumber) {
|
|
444
|
+
const octokit = createOctokit(config.githubToken);
|
|
445
|
+
const { owner, repo } = config.github;
|
|
446
|
+
const { data: prs } = await octokit.pulls.list({ owner, repo, state: "open", per_page: 100 });
|
|
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
|
+
}
|
|
440
474
|
async function getTaskPR(config, issueNumber) {
|
|
441
475
|
const octokit = createOctokit(config.githubToken);
|
|
442
476
|
const { owner, repo } = config.github;
|
|
@@ -527,14 +561,14 @@ var init_github = __esm({
|
|
|
527
561
|
});
|
|
528
562
|
|
|
529
563
|
// src/index.ts
|
|
530
|
-
import
|
|
564
|
+
import chalk18 from "chalk";
|
|
531
565
|
import readline from "readline";
|
|
532
566
|
import { createRequire } from "module";
|
|
533
567
|
|
|
534
568
|
// src/commands/init.ts
|
|
535
|
-
import { input, password, select as
|
|
536
|
-
import
|
|
537
|
-
import
|
|
569
|
+
import { input, password, select as select12 } from "@inquirer/prompts";
|
|
570
|
+
import chalk14 from "chalk";
|
|
571
|
+
import ora15 from "ora";
|
|
538
572
|
import open2 from "open";
|
|
539
573
|
import { createOAuthDeviceAuth } from "@octokit/auth-oauth-device";
|
|
540
574
|
|
|
@@ -2601,21 +2635,145 @@ async function execute11(input3, config) {
|
|
|
2601
2635
|
}
|
|
2602
2636
|
var terminal11 = true;
|
|
2603
2637
|
|
|
2604
|
-
// src/tools/
|
|
2605
|
-
var
|
|
2606
|
-
__export(
|
|
2638
|
+
// src/tools/move-task/index.ts
|
|
2639
|
+
var move_task_exports = {};
|
|
2640
|
+
__export(move_task_exports, {
|
|
2607
2641
|
definition: () => definition12,
|
|
2608
2642
|
execute: () => execute12,
|
|
2609
2643
|
run: () => run12,
|
|
2610
2644
|
terminal: () => terminal12
|
|
2611
2645
|
});
|
|
2646
|
+
init_github();
|
|
2647
|
+
import { select as select9 } from "@inquirer/prompts";
|
|
2612
2648
|
import ora10 from "ora";
|
|
2613
2649
|
import chalk10 from "chalk";
|
|
2650
|
+
var definition12 = {
|
|
2651
|
+
type: "function",
|
|
2652
|
+
function: {
|
|
2653
|
+
name: "move_task",
|
|
2654
|
+
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.",
|
|
2655
|
+
parameters: {
|
|
2656
|
+
type: "object",
|
|
2657
|
+
properties: {
|
|
2658
|
+
issue_number: { type: "number", description: "Issue number of the task to move." },
|
|
2659
|
+
parent_issue_number: { type: "number", description: "Issue number of the new parent task." }
|
|
2660
|
+
},
|
|
2661
|
+
required: ["issue_number", "parent_issue_number"]
|
|
2662
|
+
}
|
|
2663
|
+
}
|
|
2664
|
+
};
|
|
2665
|
+
async function run12(input3, config) {
|
|
2666
|
+
const me = await getAuthenticatedUser(config);
|
|
2667
|
+
const allTasks = await listTasks(config);
|
|
2668
|
+
let issueNumber = input3["issue_number"];
|
|
2669
|
+
let taskToMove;
|
|
2670
|
+
if (issueNumber) {
|
|
2671
|
+
try {
|
|
2672
|
+
taskToMove = await getTask(config, issueNumber);
|
|
2673
|
+
} catch (err) {
|
|
2674
|
+
return `Error loading task #${issueNumber}: ${err.message}`;
|
|
2675
|
+
}
|
|
2676
|
+
if (taskToMove.author !== me) {
|
|
2677
|
+
return `Task #${issueNumber} was not authored by you \u2014 you can only move your own tasks.`;
|
|
2678
|
+
}
|
|
2679
|
+
} else {
|
|
2680
|
+
const myTasks = allTasks.filter((t) => t.author === me);
|
|
2681
|
+
if (myTasks.length === 0) return "No tasks you authored are available to move.";
|
|
2682
|
+
try {
|
|
2683
|
+
issueNumber = await select9({
|
|
2684
|
+
message: "Select task to move:",
|
|
2685
|
+
choices: myTasks.map((t) => ({
|
|
2686
|
+
name: `#${t.number} [${getStatus(t)}] ${t.title}`,
|
|
2687
|
+
value: t.number
|
|
2688
|
+
}))
|
|
2689
|
+
});
|
|
2690
|
+
} catch {
|
|
2691
|
+
return "Cancelled.";
|
|
2692
|
+
}
|
|
2693
|
+
taskToMove = myTasks.find((t) => t.number === issueNumber);
|
|
2694
|
+
}
|
|
2695
|
+
const candidates = allTasks.filter((t) => t.number !== taskToMove.number);
|
|
2696
|
+
const resolveSpinner = ora10("Finding parent task branches\u2026").start();
|
|
2697
|
+
const parents = [];
|
|
2698
|
+
for (const t of candidates) {
|
|
2699
|
+
const branch = await getTaskBranch(config, t.number);
|
|
2700
|
+
if (branch) parents.push({ task: t, branch });
|
|
2701
|
+
}
|
|
2702
|
+
resolveSpinner.stop();
|
|
2703
|
+
if (parents.length === 0) {
|
|
2704
|
+
return "No other tasks with known branches are available as a parent.";
|
|
2705
|
+
}
|
|
2706
|
+
let parentIssueNumber = input3["parent_issue_number"];
|
|
2707
|
+
let chosen;
|
|
2708
|
+
if (parentIssueNumber) {
|
|
2709
|
+
const found = parents.find((p) => p.task.number === parentIssueNumber);
|
|
2710
|
+
if (!found) {
|
|
2711
|
+
return `Task #${parentIssueNumber} is not available as a parent (no branch found or not open).`;
|
|
2712
|
+
}
|
|
2713
|
+
chosen = found;
|
|
2714
|
+
} else {
|
|
2715
|
+
try {
|
|
2716
|
+
const selectedBranch = await select9({
|
|
2717
|
+
message: `Move #${taskToMove.number} under which task?`,
|
|
2718
|
+
choices: parents.map((p) => ({
|
|
2719
|
+
name: `#${p.task.number} [${getStatus(p.task)}] ${p.task.title} ${chalk10.dim("\u2192 " + p.branch)}`,
|
|
2720
|
+
value: p.branch
|
|
2721
|
+
}))
|
|
2722
|
+
});
|
|
2723
|
+
chosen = parents.find((p) => p.branch === selectedBranch);
|
|
2724
|
+
} catch {
|
|
2725
|
+
return "Cancelled.";
|
|
2726
|
+
}
|
|
2727
|
+
}
|
|
2728
|
+
const sha = await getBranchHeadSha(config, chosen.branch);
|
|
2729
|
+
if (!sha) {
|
|
2730
|
+
return `Could not resolve HEAD of branch ${chosen.branch} \u2014 does it exist on the remote?`;
|
|
2731
|
+
}
|
|
2732
|
+
const spinner = ora10(`Moving #${taskToMove.number} under #${chosen.task.number}\u2026`).start();
|
|
2733
|
+
try {
|
|
2734
|
+
await moveTask(config, taskToMove.number, chosen.branch, sha);
|
|
2735
|
+
spinner.succeed(
|
|
2736
|
+
`Task #${taskToMove.number} moved under #${chosen.task.number} "${chosen.task.title}"
|
|
2737
|
+
target: ${chalk10.cyan(chosen.branch)} base: ${chalk10.dim(sha.slice(0, 7))}`
|
|
2738
|
+
);
|
|
2739
|
+
return `Task #${taskToMove.number} moved under #${chosen.task.number} (branch: ${chosen.branch}, base: ${sha.slice(0, 7)})`;
|
|
2740
|
+
} catch (err) {
|
|
2741
|
+
spinner.fail(`Failed: ${err.message}`);
|
|
2742
|
+
return `Error: ${err.message}`;
|
|
2743
|
+
}
|
|
2744
|
+
}
|
|
2745
|
+
async function execute12(input3, config) {
|
|
2746
|
+
const me = await getAuthenticatedUser(config);
|
|
2747
|
+
const issueNumber = input3["issue_number"];
|
|
2748
|
+
const parentIssueNumber = input3["parent_issue_number"];
|
|
2749
|
+
const task = await getTask(config, issueNumber);
|
|
2750
|
+
if (task.author !== me) {
|
|
2751
|
+
return `Task #${issueNumber} was not authored by you \u2014 you can only move your own tasks.`;
|
|
2752
|
+
}
|
|
2753
|
+
const branch = await getTaskBranch(config, parentIssueNumber);
|
|
2754
|
+
if (!branch) return `No branch found for parent task #${parentIssueNumber}.`;
|
|
2755
|
+
const sha = await getBranchHeadSha(config, branch);
|
|
2756
|
+
if (!sha) return `Could not resolve HEAD of branch ${branch}.`;
|
|
2757
|
+
await moveTask(config, issueNumber, branch, sha);
|
|
2758
|
+
return `Task #${issueNumber} moved under #${parentIssueNumber} (branch: ${branch}, base: ${sha.slice(0, 7)})`;
|
|
2759
|
+
}
|
|
2760
|
+
var terminal12 = true;
|
|
2761
|
+
|
|
2762
|
+
// src/tools/wiki/index.ts
|
|
2763
|
+
var wiki_exports = {};
|
|
2764
|
+
__export(wiki_exports, {
|
|
2765
|
+
definition: () => definition13,
|
|
2766
|
+
execute: () => execute13,
|
|
2767
|
+
run: () => run13,
|
|
2768
|
+
terminal: () => terminal13
|
|
2769
|
+
});
|
|
2770
|
+
import ora11 from "ora";
|
|
2771
|
+
import chalk11 from "chalk";
|
|
2614
2772
|
import { readFile as readFile2 } from "fs/promises";
|
|
2615
|
-
import { select as
|
|
2773
|
+
import { select as select10 } from "@inquirer/prompts";
|
|
2616
2774
|
init_github();
|
|
2617
2775
|
var WIKI_PATH = "TECHUNTER.md";
|
|
2618
|
-
var
|
|
2776
|
+
var definition13 = {
|
|
2619
2777
|
type: "function",
|
|
2620
2778
|
function: {
|
|
2621
2779
|
name: "update_wiki",
|
|
@@ -2635,20 +2793,20 @@ async function readWikiContent(config) {
|
|
|
2635
2793
|
return getRepoFile(config, WIKI_PATH);
|
|
2636
2794
|
}
|
|
2637
2795
|
function printWiki(content) {
|
|
2638
|
-
const divider =
|
|
2796
|
+
const divider = chalk11.dim("\u2500".repeat(70));
|
|
2639
2797
|
console.log("\n" + divider);
|
|
2640
|
-
console.log(
|
|
2798
|
+
console.log(chalk11.bold(" TECHUNTER.md"));
|
|
2641
2799
|
console.log(divider);
|
|
2642
2800
|
console.log(renderMarkdown(content));
|
|
2643
2801
|
console.log(divider + "\n");
|
|
2644
2802
|
}
|
|
2645
|
-
async function
|
|
2646
|
-
const fetchSpinner =
|
|
2803
|
+
async function run13(_input, config) {
|
|
2804
|
+
const fetchSpinner = ora11("Checking for existing wiki\u2026").start();
|
|
2647
2805
|
const existing = await readWikiContent(config).catch(() => null);
|
|
2648
2806
|
fetchSpinner.stop();
|
|
2649
2807
|
let action;
|
|
2650
2808
|
try {
|
|
2651
|
-
action = await
|
|
2809
|
+
action = await select10({
|
|
2652
2810
|
message: "TECHUNTER.md \u2014 what would you like to do?",
|
|
2653
2811
|
choices: [
|
|
2654
2812
|
...existing ? [{ name: "View current wiki", value: "view" }] : [],
|
|
@@ -2664,7 +2822,7 @@ async function run12(_input, config) {
|
|
|
2664
2822
|
printWiki(existing);
|
|
2665
2823
|
return "Displayed TECHUNTER.md.";
|
|
2666
2824
|
}
|
|
2667
|
-
const authSpinner =
|
|
2825
|
+
const authSpinner = ora11("Checking permissions\u2026").start();
|
|
2668
2826
|
let me;
|
|
2669
2827
|
let allowed;
|
|
2670
2828
|
try {
|
|
@@ -2678,7 +2836,7 @@ async function run12(_input, config) {
|
|
|
2678
2836
|
if (!allowed) {
|
|
2679
2837
|
return `Permission denied: only repository collaborators can update the project wiki.`;
|
|
2680
2838
|
}
|
|
2681
|
-
const genSpinner =
|
|
2839
|
+
const genSpinner = ora11("Analyzing project and generating overview\u2026").start();
|
|
2682
2840
|
let content;
|
|
2683
2841
|
try {
|
|
2684
2842
|
content = await generateWiki(config);
|
|
@@ -2690,7 +2848,7 @@ async function run12(_input, config) {
|
|
|
2690
2848
|
printWiki(content);
|
|
2691
2849
|
let confirm;
|
|
2692
2850
|
try {
|
|
2693
|
-
confirm = await
|
|
2851
|
+
confirm = await select10({
|
|
2694
2852
|
message: `Publish to repository as ${WIKI_PATH}?`,
|
|
2695
2853
|
choices: [
|
|
2696
2854
|
{ name: "Yes, commit to repo", value: "publish" },
|
|
@@ -2701,7 +2859,7 @@ async function run12(_input, config) {
|
|
|
2701
2859
|
return "Cancelled.";
|
|
2702
2860
|
}
|
|
2703
2861
|
if (confirm === "cancel") return "Cancelled.";
|
|
2704
|
-
const writeSpinner =
|
|
2862
|
+
const writeSpinner = ora11(`Writing ${WIKI_PATH}\u2026`).start();
|
|
2705
2863
|
try {
|
|
2706
2864
|
const url = await upsertRepoFile(config, WIKI_PATH, content, "docs: update TECHUNTER.md project overview");
|
|
2707
2865
|
writeSpinner.succeed(`Written: ${url}`);
|
|
@@ -2712,7 +2870,7 @@ async function run12(_input, config) {
|
|
|
2712
2870
|
return `Error: ${err.message}`;
|
|
2713
2871
|
}
|
|
2714
2872
|
}
|
|
2715
|
-
async function
|
|
2873
|
+
async function execute13(_input, config) {
|
|
2716
2874
|
const me = await getAuthenticatedUser(config);
|
|
2717
2875
|
if (!await isCollaborator(config, me)) {
|
|
2718
2876
|
return `Permission denied: only repository collaborators can update the project wiki.`;
|
|
@@ -2725,16 +2883,16 @@ async function execute12(_input, config) {
|
|
|
2725
2883
|
return `Error: ${err.message}`;
|
|
2726
2884
|
}
|
|
2727
2885
|
}
|
|
2728
|
-
var
|
|
2886
|
+
var terminal13 = true;
|
|
2729
2887
|
|
|
2730
2888
|
// src/tools/list-tasks/index.ts
|
|
2731
2889
|
var list_tasks_exports = {};
|
|
2732
2890
|
__export(list_tasks_exports, {
|
|
2733
|
-
definition: () =>
|
|
2734
|
-
execute: () =>
|
|
2891
|
+
definition: () => definition14,
|
|
2892
|
+
execute: () => execute14
|
|
2735
2893
|
});
|
|
2736
2894
|
init_github();
|
|
2737
|
-
var
|
|
2895
|
+
var definition14 = {
|
|
2738
2896
|
type: "function",
|
|
2739
2897
|
function: {
|
|
2740
2898
|
name: "list_tasks",
|
|
@@ -2746,7 +2904,7 @@ var definition13 = {
|
|
|
2746
2904
|
}
|
|
2747
2905
|
}
|
|
2748
2906
|
};
|
|
2749
|
-
async function
|
|
2907
|
+
async function execute14(_input, config) {
|
|
2750
2908
|
const tasks = await listTasks(config);
|
|
2751
2909
|
if (tasks.length === 0) return "No open tasks.";
|
|
2752
2910
|
return tasks.map((t) => {
|
|
@@ -2759,11 +2917,11 @@ async function execute13(_input, config) {
|
|
|
2759
2917
|
// src/tools/get-task/index.ts
|
|
2760
2918
|
var get_task_exports = {};
|
|
2761
2919
|
__export(get_task_exports, {
|
|
2762
|
-
definition: () =>
|
|
2763
|
-
execute: () =>
|
|
2920
|
+
definition: () => definition15,
|
|
2921
|
+
execute: () => execute15
|
|
2764
2922
|
});
|
|
2765
2923
|
init_github();
|
|
2766
|
-
var
|
|
2924
|
+
var definition15 = {
|
|
2767
2925
|
type: "function",
|
|
2768
2926
|
function: {
|
|
2769
2927
|
name: "get_task",
|
|
@@ -2777,7 +2935,7 @@ var definition14 = {
|
|
|
2777
2935
|
}
|
|
2778
2936
|
}
|
|
2779
2937
|
};
|
|
2780
|
-
async function
|
|
2938
|
+
async function execute15(input3, config) {
|
|
2781
2939
|
const issue = await getTask(config, input3["issue_number"]);
|
|
2782
2940
|
const status = issue.labels.find((l) => l.startsWith("techunter:"))?.replace("techunter:", "") ?? "unknown";
|
|
2783
2941
|
const assignee = issue.assignee ? `@${issue.assignee}` : "\u2014";
|
|
@@ -2794,12 +2952,12 @@ ${issue.body}`);
|
|
|
2794
2952
|
// src/tools/get-comments/index.ts
|
|
2795
2953
|
var get_comments_exports = {};
|
|
2796
2954
|
__export(get_comments_exports, {
|
|
2797
|
-
definition: () =>
|
|
2798
|
-
execute: () =>
|
|
2955
|
+
definition: () => definition16,
|
|
2956
|
+
execute: () => execute16
|
|
2799
2957
|
});
|
|
2800
2958
|
init_github();
|
|
2801
|
-
import
|
|
2802
|
-
var
|
|
2959
|
+
import ora12 from "ora";
|
|
2960
|
+
var definition16 = {
|
|
2803
2961
|
type: "function",
|
|
2804
2962
|
function: {
|
|
2805
2963
|
name: "get_comments",
|
|
@@ -2814,10 +2972,10 @@ var definition15 = {
|
|
|
2814
2972
|
}
|
|
2815
2973
|
}
|
|
2816
2974
|
};
|
|
2817
|
-
async function
|
|
2975
|
+
async function execute16(input3, config) {
|
|
2818
2976
|
const issueNumber = input3["issue_number"];
|
|
2819
2977
|
const limit = input3["limit"] ?? 5;
|
|
2820
|
-
const spinner =
|
|
2978
|
+
const spinner = ora12(`Loading comments for #${issueNumber}...`).start();
|
|
2821
2979
|
try {
|
|
2822
2980
|
const comments = await listComments(config, issueNumber, limit);
|
|
2823
2981
|
spinner.stop();
|
|
@@ -2836,11 +2994,11 @@ ${lines.join("\n\n")}`;
|
|
|
2836
2994
|
// src/tools/get-diff/index.ts
|
|
2837
2995
|
var get_diff_exports = {};
|
|
2838
2996
|
__export(get_diff_exports, {
|
|
2839
|
-
definition: () =>
|
|
2840
|
-
execute: () =>
|
|
2997
|
+
definition: () => definition17,
|
|
2998
|
+
execute: () => execute17
|
|
2841
2999
|
});
|
|
2842
|
-
import
|
|
2843
|
-
var
|
|
3000
|
+
import ora13 from "ora";
|
|
3001
|
+
var definition17 = {
|
|
2844
3002
|
type: "function",
|
|
2845
3003
|
function: {
|
|
2846
3004
|
name: "get_diff",
|
|
@@ -2848,8 +3006,8 @@ var definition16 = {
|
|
|
2848
3006
|
parameters: { type: "object", properties: {}, required: [] }
|
|
2849
3007
|
}
|
|
2850
3008
|
};
|
|
2851
|
-
async function
|
|
2852
|
-
const spinner =
|
|
3009
|
+
async function execute17(_input, _config) {
|
|
3010
|
+
const spinner = ora13("Reading git diff...").start();
|
|
2853
3011
|
try {
|
|
2854
3012
|
const diff = await getDiff();
|
|
2855
3013
|
spinner.stop();
|
|
@@ -2863,14 +3021,14 @@ async function execute16(_input, _config) {
|
|
|
2863
3021
|
// src/tools/run-command/index.ts
|
|
2864
3022
|
var run_command_exports = {};
|
|
2865
3023
|
__export(run_command_exports, {
|
|
2866
|
-
definition: () =>
|
|
2867
|
-
execute: () =>
|
|
3024
|
+
definition: () => definition18,
|
|
3025
|
+
execute: () => execute18
|
|
2868
3026
|
});
|
|
2869
3027
|
import { exec } from "child_process";
|
|
2870
3028
|
import { promisify } from "util";
|
|
2871
|
-
import
|
|
3029
|
+
import ora14 from "ora";
|
|
2872
3030
|
var execAsync = promisify(exec);
|
|
2873
|
-
var
|
|
3031
|
+
var definition18 = {
|
|
2874
3032
|
type: "function",
|
|
2875
3033
|
function: {
|
|
2876
3034
|
name: "run_command",
|
|
@@ -2884,10 +3042,10 @@ var definition17 = {
|
|
|
2884
3042
|
}
|
|
2885
3043
|
}
|
|
2886
3044
|
};
|
|
2887
|
-
async function
|
|
3045
|
+
async function execute18(input3, _config) {
|
|
2888
3046
|
const command = input3["command"];
|
|
2889
3047
|
const cwd = process.cwd();
|
|
2890
|
-
const spinner =
|
|
3048
|
+
const spinner = ora14(`$ ${command}`).start();
|
|
2891
3049
|
try {
|
|
2892
3050
|
const { stdout, stderr } = await execAsync(command, { cwd, timeout: 6e4, maxBuffer: 1024 * 1024 });
|
|
2893
3051
|
spinner.stop();
|
|
@@ -2906,15 +3064,15 @@ ${out || e.message}`;
|
|
|
2906
3064
|
// src/tools/list-files/index.ts
|
|
2907
3065
|
var list_files_exports = {};
|
|
2908
3066
|
__export(list_files_exports, {
|
|
2909
|
-
definition: () =>
|
|
2910
|
-
execute: () =>
|
|
3067
|
+
definition: () => definition19,
|
|
3068
|
+
execute: () => execute19
|
|
2911
3069
|
});
|
|
2912
3070
|
import { readFile as readFile3 } from "fs/promises";
|
|
2913
3071
|
import { existsSync } from "fs";
|
|
2914
3072
|
import path2 from "path";
|
|
2915
3073
|
import { globby } from "globby";
|
|
2916
3074
|
import ignore from "ignore";
|
|
2917
|
-
var
|
|
3075
|
+
var definition19 = {
|
|
2918
3076
|
type: "function",
|
|
2919
3077
|
function: {
|
|
2920
3078
|
name: "list_files",
|
|
@@ -2954,7 +3112,7 @@ var BINARY_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
|
2954
3112
|
".sqlite",
|
|
2955
3113
|
".lock"
|
|
2956
3114
|
]);
|
|
2957
|
-
async function
|
|
3115
|
+
async function execute19(input3, _config) {
|
|
2958
3116
|
const glob = input3["glob"] ?? "**/*";
|
|
2959
3117
|
const cwd = process.cwd();
|
|
2960
3118
|
const ig = ignore();
|
|
@@ -2973,15 +3131,15 @@ ${filtered.join("\n")}`;
|
|
|
2973
3131
|
// src/tools/grep-code/index.ts
|
|
2974
3132
|
var grep_code_exports = {};
|
|
2975
3133
|
__export(grep_code_exports, {
|
|
2976
|
-
definition: () =>
|
|
2977
|
-
execute: () =>
|
|
3134
|
+
definition: () => definition20,
|
|
3135
|
+
execute: () => execute20
|
|
2978
3136
|
});
|
|
2979
3137
|
import { readFile as readFile4 } from "fs/promises";
|
|
2980
3138
|
import { existsSync as existsSync2 } from "fs";
|
|
2981
3139
|
import path3 from "path";
|
|
2982
3140
|
import { globby as globby2 } from "globby";
|
|
2983
3141
|
import ignore2 from "ignore";
|
|
2984
|
-
var
|
|
3142
|
+
var definition20 = {
|
|
2985
3143
|
type: "function",
|
|
2986
3144
|
function: {
|
|
2987
3145
|
name: "grep_code",
|
|
@@ -3054,7 +3212,7 @@ function isText(f) {
|
|
|
3054
3212
|
return !BINARY_EXTENSIONS2.has(path3.extname(f).toLowerCase());
|
|
3055
3213
|
}
|
|
3056
3214
|
var MAX_RANGE_LINES = 300;
|
|
3057
|
-
async function
|
|
3215
|
+
async function execute20(input3, _config) {
|
|
3058
3216
|
const pattern = input3["pattern"] ?? "";
|
|
3059
3217
|
const fileGlob = input3["file_glob"] ?? "**/*";
|
|
3060
3218
|
const contextLines = Math.min(input3["context_lines"] ?? 2, 5);
|
|
@@ -3141,12 +3299,12 @@ ${snippets.join("\n---\n")}
|
|
|
3141
3299
|
// src/tools/ask-user/index.ts
|
|
3142
3300
|
var ask_user_exports = {};
|
|
3143
3301
|
__export(ask_user_exports, {
|
|
3144
|
-
definition: () =>
|
|
3145
|
-
execute: () =>
|
|
3302
|
+
definition: () => definition21,
|
|
3303
|
+
execute: () => execute21
|
|
3146
3304
|
});
|
|
3147
|
-
import
|
|
3148
|
-
import { select as
|
|
3149
|
-
var
|
|
3305
|
+
import chalk12 from "chalk";
|
|
3306
|
+
import { select as select11, input as promptInput5 } from "@inquirer/prompts";
|
|
3307
|
+
var definition21 = {
|
|
3150
3308
|
type: "function",
|
|
3151
3309
|
function: {
|
|
3152
3310
|
name: "ask_user",
|
|
@@ -3165,24 +3323,24 @@ var definition20 = {
|
|
|
3165
3323
|
}
|
|
3166
3324
|
}
|
|
3167
3325
|
};
|
|
3168
|
-
async function
|
|
3326
|
+
async function execute21(input3, _config) {
|
|
3169
3327
|
const question = input3["question"];
|
|
3170
3328
|
const options = input3["options"];
|
|
3171
3329
|
const OTHER = "__other__";
|
|
3172
3330
|
console.log("");
|
|
3173
|
-
console.log(
|
|
3174
|
-
console.log(
|
|
3331
|
+
console.log(chalk12.dim(" \u250C\u2500 Agent question " + "\u2500".repeat(51)));
|
|
3332
|
+
console.log(chalk12.dim(" \u2502"));
|
|
3175
3333
|
for (const line of question.split("\n")) {
|
|
3176
|
-
console.log(
|
|
3334
|
+
console.log(chalk12.dim(" \u2502 ") + line);
|
|
3177
3335
|
}
|
|
3178
|
-
console.log(
|
|
3336
|
+
console.log(chalk12.dim(" \u2514" + "\u2500".repeat(67)));
|
|
3179
3337
|
let answer;
|
|
3180
3338
|
try {
|
|
3181
|
-
const chosen = await
|
|
3339
|
+
const chosen = await select11({
|
|
3182
3340
|
message: " ",
|
|
3183
3341
|
choices: [
|
|
3184
3342
|
...options.map((o) => ({ name: o, value: o })),
|
|
3185
|
-
{ name:
|
|
3343
|
+
{ name: chalk12.dim("Other (describe below)"), value: OTHER }
|
|
3186
3344
|
]
|
|
3187
3345
|
});
|
|
3188
3346
|
answer = chosen === OTHER ? await promptInput5({ message: "Your answer:" }) : chosen;
|
|
@@ -3207,6 +3365,7 @@ var toolModules = [
|
|
|
3207
3365
|
reject_exports,
|
|
3208
3366
|
accept_exports,
|
|
3209
3367
|
edit_task_exports,
|
|
3368
|
+
move_task_exports,
|
|
3210
3369
|
wiki_exports,
|
|
3211
3370
|
// Low-level tools
|
|
3212
3371
|
list_tasks_exports,
|
|
@@ -3220,7 +3379,7 @@ var toolModules = [
|
|
|
3220
3379
|
];
|
|
3221
3380
|
|
|
3222
3381
|
// src/lib/agent-ui.ts
|
|
3223
|
-
import
|
|
3382
|
+
import chalk13 from "chalk";
|
|
3224
3383
|
function formatInput(input3) {
|
|
3225
3384
|
return Object.entries(input3).map(([k, v]) => {
|
|
3226
3385
|
if (typeof v === "number") return `${k}=${v}`;
|
|
@@ -3237,12 +3396,12 @@ function summarize(result) {
|
|
|
3237
3396
|
}
|
|
3238
3397
|
function printToolCall(name, input3) {
|
|
3239
3398
|
const params = formatInput(input3);
|
|
3240
|
-
console.log(` ${
|
|
3399
|
+
console.log(` ${chalk13.cyan("\u2192")} ${chalk13.bold(name)}${params ? " " + chalk13.dim(params) : ""}`);
|
|
3241
3400
|
}
|
|
3242
3401
|
function printToolResult(result) {
|
|
3243
3402
|
const ok = !result.startsWith("Error:");
|
|
3244
|
-
const icon = ok ?
|
|
3245
|
-
console.log(` ${icon} ${
|
|
3403
|
+
const icon = ok ? chalk13.green("\u2713") : chalk13.red("\u2717");
|
|
3404
|
+
console.log(` ${icon} ${chalk13.dim(summarize(result))}`);
|
|
3246
3405
|
}
|
|
3247
3406
|
|
|
3248
3407
|
// src/lib/sub-agent.ts
|
|
@@ -3345,8 +3504,8 @@ async function generateWiki(config) {
|
|
|
3345
3504
|
|
|
3346
3505
|
// src/commands/init.ts
|
|
3347
3506
|
async function getGitHubTokenViaPAT() {
|
|
3348
|
-
console.log(
|
|
3349
|
-
console.log(
|
|
3507
|
+
console.log(chalk14.dim("\n Create a token at: https://github.com/settings/tokens/new"));
|
|
3508
|
+
console.log(chalk14.dim(" Required scopes: repo, read:user\n"));
|
|
3350
3509
|
const token = await password({
|
|
3351
3510
|
message: "GitHub Personal Access Token:",
|
|
3352
3511
|
mask: "*"
|
|
@@ -3365,17 +3524,17 @@ async function getGitHubTokenViaDeviceFlow() {
|
|
|
3365
3524
|
verificationUri = verification.verification_uri;
|
|
3366
3525
|
userCode = verification.user_code;
|
|
3367
3526
|
console.log("");
|
|
3368
|
-
console.log(
|
|
3369
|
-
console.log(" " +
|
|
3527
|
+
console.log(chalk14.bold(" 1. Open this URL in your browser:"));
|
|
3528
|
+
console.log(" " + chalk14.cyan(verificationUri));
|
|
3370
3529
|
console.log("");
|
|
3371
|
-
console.log(
|
|
3372
|
-
console.log(" " +
|
|
3530
|
+
console.log(chalk14.bold(" 2. Enter this code:"));
|
|
3531
|
+
console.log(" " + chalk14.yellow.bold(userCode));
|
|
3373
3532
|
console.log("");
|
|
3374
3533
|
open2(verificationUri).catch(() => {
|
|
3375
3534
|
});
|
|
3376
3535
|
}
|
|
3377
3536
|
});
|
|
3378
|
-
const spinner =
|
|
3537
|
+
const spinner = ora15("Waiting for authorization in browser...").start();
|
|
3379
3538
|
let token;
|
|
3380
3539
|
try {
|
|
3381
3540
|
const result = await auth({ type: "oauth" });
|
|
@@ -3388,7 +3547,7 @@ async function getGitHubTokenViaDeviceFlow() {
|
|
|
3388
3547
|
return { token, clientId: OAUTH_CLIENT_ID };
|
|
3389
3548
|
}
|
|
3390
3549
|
async function initCommand() {
|
|
3391
|
-
console.log(
|
|
3550
|
+
console.log(chalk14.bold.cyan("\nTechunter \u2014 Initial Setup\n"));
|
|
3392
3551
|
let detectedOwner = "";
|
|
3393
3552
|
let detectedRepo = "";
|
|
3394
3553
|
const remoteUrl = await getRemoteUrl();
|
|
@@ -3397,11 +3556,11 @@ async function initCommand() {
|
|
|
3397
3556
|
if (parsed) {
|
|
3398
3557
|
detectedOwner = parsed.owner;
|
|
3399
3558
|
detectedRepo = parsed.repo;
|
|
3400
|
-
console.log(
|
|
3559
|
+
console.log(chalk14.dim(`Detected GitHub repo: ${detectedOwner}/${detectedRepo}
|
|
3401
3560
|
`));
|
|
3402
3561
|
}
|
|
3403
3562
|
}
|
|
3404
|
-
const authMethod = await
|
|
3563
|
+
const authMethod = await select12({
|
|
3405
3564
|
message: "How would you like to authenticate with GitHub?",
|
|
3406
3565
|
choices: [
|
|
3407
3566
|
{
|
|
@@ -3424,10 +3583,10 @@ async function initCommand() {
|
|
|
3424
3583
|
const result = await getGitHubTokenViaPAT();
|
|
3425
3584
|
githubToken = result.token;
|
|
3426
3585
|
}
|
|
3427
|
-
const providerChoice = await
|
|
3586
|
+
const providerChoice = await select12({
|
|
3428
3587
|
message: "AI provider:",
|
|
3429
3588
|
choices: [
|
|
3430
|
-
{ name: `OpenRouter (recommended) ${
|
|
3589
|
+
{ name: `OpenRouter (recommended) ${chalk14.dim(`${DEFAULT_BASE_URL} \xB7 ${DEFAULT_MODEL}`)}`, value: "openrouter" },
|
|
3431
3590
|
{ name: "Custom (specify base URL and model)", value: "custom" }
|
|
3432
3591
|
]
|
|
3433
3592
|
});
|
|
@@ -3437,7 +3596,7 @@ async function initCommand() {
|
|
|
3437
3596
|
aiBaseUrl = (await input({ message: "API base URL:", default: DEFAULT_BASE_URL })).trim();
|
|
3438
3597
|
aiModel = (await input({ message: "Model name:", default: DEFAULT_MODEL })).trim();
|
|
3439
3598
|
}
|
|
3440
|
-
const apiKeyHint = providerChoice === "openrouter" ?
|
|
3599
|
+
const apiKeyHint = providerChoice === "openrouter" ? chalk14.dim(" Get a key at: https://openrouter.ai/settings/keys\n") : chalk14.dim(" API key for your provider\n");
|
|
3441
3600
|
console.log(apiKeyHint);
|
|
3442
3601
|
const aiApiKey = await password({
|
|
3443
3602
|
message: "API Key:",
|
|
@@ -3467,20 +3626,20 @@ async function initCommand() {
|
|
|
3467
3626
|
}
|
|
3468
3627
|
};
|
|
3469
3628
|
setConfig(config);
|
|
3470
|
-
const spinner =
|
|
3629
|
+
const spinner = ora15("Setting up GitHub labels...").start();
|
|
3471
3630
|
try {
|
|
3472
3631
|
await ensureLabels(config);
|
|
3473
3632
|
spinner.succeed("GitHub labels created");
|
|
3474
3633
|
} catch (err) {
|
|
3475
3634
|
spinner.fail("Failed to create labels (check token permissions)");
|
|
3476
|
-
console.error(
|
|
3635
|
+
console.error(chalk14.red(String(err)));
|
|
3477
3636
|
}
|
|
3478
|
-
console.log(
|
|
3479
|
-
console.log(
|
|
3637
|
+
console.log(chalk14.green("\nSetup complete!"));
|
|
3638
|
+
console.log(chalk14.dim(`Config saved to: ${getConfigPath()}
|
|
3480
3639
|
`));
|
|
3481
3640
|
let genWiki = false;
|
|
3482
3641
|
try {
|
|
3483
|
-
genWiki = await
|
|
3642
|
+
genWiki = await select12({
|
|
3484
3643
|
message: "Generate TECHUNTER.md project overview for new team members?",
|
|
3485
3644
|
choices: [
|
|
3486
3645
|
{ name: "Yes, generate now", value: true },
|
|
@@ -3490,7 +3649,7 @@ async function initCommand() {
|
|
|
3490
3649
|
} catch {
|
|
3491
3650
|
}
|
|
3492
3651
|
if (genWiki) {
|
|
3493
|
-
const wikiSpinner =
|
|
3652
|
+
const wikiSpinner = ora15("Analyzing project and generating TECHUNTER.md\u2026").start();
|
|
3494
3653
|
try {
|
|
3495
3654
|
const content = await generateWiki(config);
|
|
3496
3655
|
await upsertRepoFile(config, "TECHUNTER.md", content, "docs: add TECHUNTER.md project overview");
|
|
@@ -3503,31 +3662,31 @@ async function initCommand() {
|
|
|
3503
3662
|
}
|
|
3504
3663
|
|
|
3505
3664
|
// src/commands/config.ts
|
|
3506
|
-
import { input as input2, password as password2, select as
|
|
3507
|
-
import
|
|
3665
|
+
import { input as input2, password as password2, select as select13 } from "@inquirer/prompts";
|
|
3666
|
+
import chalk15 from "chalk";
|
|
3508
3667
|
async function configCommand() {
|
|
3509
3668
|
let config;
|
|
3510
3669
|
try {
|
|
3511
3670
|
config = getConfig();
|
|
3512
3671
|
} catch {
|
|
3513
|
-
console.error(
|
|
3672
|
+
console.error(chalk15.red("No config found. Run `tch init` first."));
|
|
3514
3673
|
process.exit(1);
|
|
3515
3674
|
}
|
|
3516
|
-
console.log(
|
|
3517
|
-
console.log(
|
|
3675
|
+
console.log(chalk15.bold.cyan("\nTechunter \u2014 Settings\n"));
|
|
3676
|
+
console.log(chalk15.dim(`Config file: ${getConfigPath()}
|
|
3518
3677
|
`));
|
|
3519
3678
|
const currentBaseUrl = config.aiBaseUrl ?? DEFAULT_BASE_URL;
|
|
3520
3679
|
const currentModel = config.aiModel ?? DEFAULT_MODEL;
|
|
3521
3680
|
const currentBaseBranch = config.baseBranch ?? "main";
|
|
3522
|
-
const field = await
|
|
3681
|
+
const field = await select13({
|
|
3523
3682
|
message: "Which setting to change?",
|
|
3524
3683
|
choices: [
|
|
3525
|
-
{ name: `GitHub repo ${
|
|
3526
|
-
{ name: `Base branch ${
|
|
3527
|
-
{ name: `AI base URL ${
|
|
3528
|
-
{ name: `AI model ${
|
|
3529
|
-
{ name: `AI API Key ${
|
|
3530
|
-
{ name: `GitHub Token ${
|
|
3684
|
+
{ name: `GitHub repo ${chalk15.dim(`${config.github.owner}/${config.github.repo}`)}`, value: "repo" },
|
|
3685
|
+
{ name: `Base branch ${chalk15.dim(currentBaseBranch)}`, value: "baseBranch" },
|
|
3686
|
+
{ name: `AI base URL ${chalk15.dim(currentBaseUrl)}`, value: "aiBaseUrl" },
|
|
3687
|
+
{ name: `AI model ${chalk15.dim(currentModel)}`, value: "aiModel" },
|
|
3688
|
+
{ name: `AI API Key ${chalk15.dim("(hidden)")}`, value: "aiApiKey" },
|
|
3689
|
+
{ name: `GitHub Token ${chalk15.dim("(hidden)")}`, value: "githubToken" },
|
|
3531
3690
|
{ name: "Cancel", value: "cancel" }
|
|
3532
3691
|
]
|
|
3533
3692
|
});
|
|
@@ -3536,7 +3695,7 @@ async function configCommand() {
|
|
|
3536
3695
|
const val = await input2({ message: "Base branch name:", default: currentBaseBranch });
|
|
3537
3696
|
if (val.trim()) {
|
|
3538
3697
|
setConfig({ baseBranch: val.trim() });
|
|
3539
|
-
console.log(
|
|
3698
|
+
console.log(chalk15.green(`
|
|
3540
3699
|
Base branch set to: ${val.trim()}
|
|
3541
3700
|
`));
|
|
3542
3701
|
}
|
|
@@ -3544,14 +3703,14 @@ Base branch set to: ${val.trim()}
|
|
|
3544
3703
|
const owner = await input2({ message: "GitHub repo owner:", default: config.github.owner });
|
|
3545
3704
|
const repo = await input2({ message: "GitHub repo name:", default: config.github.repo });
|
|
3546
3705
|
setConfig({ github: { ...config.github, owner: owner.trim(), repo: repo.trim() } });
|
|
3547
|
-
console.log(
|
|
3706
|
+
console.log(chalk15.green(`
|
|
3548
3707
|
Repo set to: ${owner.trim()}/${repo.trim()}
|
|
3549
3708
|
`));
|
|
3550
3709
|
} else if (field === "aiBaseUrl") {
|
|
3551
3710
|
const val = await input2({ message: "AI base URL:", default: currentBaseUrl });
|
|
3552
3711
|
if (val.trim()) {
|
|
3553
3712
|
setConfig({ aiBaseUrl: val.trim() });
|
|
3554
|
-
console.log(
|
|
3713
|
+
console.log(chalk15.green(`
|
|
3555
3714
|
AI base URL set to: ${val.trim()}
|
|
3556
3715
|
`));
|
|
3557
3716
|
}
|
|
@@ -3559,7 +3718,7 @@ AI base URL set to: ${val.trim()}
|
|
|
3559
3718
|
const val = await input2({ message: "AI model name:", default: currentModel });
|
|
3560
3719
|
if (val.trim()) {
|
|
3561
3720
|
setConfig({ aiModel: val.trim() });
|
|
3562
|
-
console.log(
|
|
3721
|
+
console.log(chalk15.green(`
|
|
3563
3722
|
AI model set to: ${val.trim()}
|
|
3564
3723
|
`));
|
|
3565
3724
|
}
|
|
@@ -3567,13 +3726,13 @@ AI model set to: ${val.trim()}
|
|
|
3567
3726
|
const val = await password2({ message: "New AI API Key:", mask: "*" });
|
|
3568
3727
|
if (val.trim()) {
|
|
3569
3728
|
setConfig({ aiApiKey: val.trim() });
|
|
3570
|
-
console.log(
|
|
3729
|
+
console.log(chalk15.green("\nAI API Key updated.\n"));
|
|
3571
3730
|
}
|
|
3572
3731
|
} else if (field === "githubToken") {
|
|
3573
3732
|
const val = await password2({ message: "New GitHub Token:", mask: "*" });
|
|
3574
3733
|
if (val.trim()) {
|
|
3575
3734
|
setConfig({ githubToken: val.trim() });
|
|
3576
|
-
console.log(
|
|
3735
|
+
console.log(chalk15.green("\nGitHub Token updated.\n"));
|
|
3577
3736
|
}
|
|
3578
3737
|
}
|
|
3579
3738
|
}
|
|
@@ -3582,8 +3741,8 @@ AI model set to: ${val.trim()}
|
|
|
3582
3741
|
init_github();
|
|
3583
3742
|
|
|
3584
3743
|
// src/lib/agent.ts
|
|
3585
|
-
import
|
|
3586
|
-
import
|
|
3744
|
+
import ora16 from "ora";
|
|
3745
|
+
import chalk16 from "chalk";
|
|
3587
3746
|
var tools = toolModules.map((m) => m.definition);
|
|
3588
3747
|
var HISTORY_KEEP_TURNS = 8;
|
|
3589
3748
|
function trimHistory(messages) {
|
|
@@ -3669,7 +3828,7 @@ async function runAgentLoop(config, messages) {
|
|
|
3669
3828
|
throw new Error(`Agent exceeded ${MAX_ITERATIONS} iterations without finishing.`);
|
|
3670
3829
|
}
|
|
3671
3830
|
trimHistory(messages);
|
|
3672
|
-
const spinner =
|
|
3831
|
+
const spinner = ora16({ text: chalk16.dim("Thinking\u2026"), color: "cyan" }).start();
|
|
3673
3832
|
let response;
|
|
3674
3833
|
try {
|
|
3675
3834
|
response = await client.chat.completions.create({
|
|
@@ -3714,7 +3873,7 @@ async function runAgentLoop(config, messages) {
|
|
|
3714
3873
|
return executeTool(tc.function.name, parsed, config);
|
|
3715
3874
|
})
|
|
3716
3875
|
);
|
|
3717
|
-
let
|
|
3876
|
+
let terminal14 = false;
|
|
3718
3877
|
for (let i = 0; i < toolCalls.length; i++) {
|
|
3719
3878
|
printToolResult(results[i]);
|
|
3720
3879
|
messages.push({
|
|
@@ -3723,10 +3882,10 @@ async function runAgentLoop(config, messages) {
|
|
|
3723
3882
|
content: results[i]
|
|
3724
3883
|
});
|
|
3725
3884
|
if (toolModules.find((m) => m.definition.function.name === toolCalls[i].function.name)?.terminal) {
|
|
3726
|
-
|
|
3885
|
+
terminal14 = true;
|
|
3727
3886
|
}
|
|
3728
3887
|
}
|
|
3729
|
-
if (
|
|
3888
|
+
if (terminal14) return results[results.length - 1];
|
|
3730
3889
|
} else {
|
|
3731
3890
|
return choice.message.content ?? "";
|
|
3732
3891
|
}
|
|
@@ -3735,7 +3894,7 @@ async function runAgentLoop(config, messages) {
|
|
|
3735
3894
|
|
|
3736
3895
|
// src/lib/update-check.ts
|
|
3737
3896
|
import Conf2 from "conf";
|
|
3738
|
-
import
|
|
3897
|
+
import chalk17 from "chalk";
|
|
3739
3898
|
import { execFile } from "child_process";
|
|
3740
3899
|
var PACKAGE_NAME = "techunter";
|
|
3741
3900
|
var CHECK_INTERVAL_MS = 24 * 60 * 60 * 1e3;
|
|
@@ -3796,16 +3955,16 @@ async function startAutoUpdate(currentVersion) {
|
|
|
3796
3955
|
]);
|
|
3797
3956
|
if (!latest) return;
|
|
3798
3957
|
console.log(
|
|
3799
|
-
|
|
3958
|
+
chalk17.cyan("\n \u2191 Auto-updating to v" + latest + "\u2026") + chalk17.dim(" (running in background)\n")
|
|
3800
3959
|
);
|
|
3801
3960
|
installUpdate().then((installedVersion) => {
|
|
3802
3961
|
console.log(
|
|
3803
|
-
"\n" +
|
|
3962
|
+
"\n" + chalk17.green(" \u2714 Updated to v" + (installedVersion || latest)) + chalk17.dim(" \u2014 restart tch to use the new version\n") + chalk17.cyan(" You \u203A ")
|
|
3804
3963
|
// redraw the prompt hint
|
|
3805
3964
|
);
|
|
3806
3965
|
}).catch((err) => {
|
|
3807
3966
|
console.log(
|
|
3808
|
-
"\n" +
|
|
3967
|
+
"\n" + chalk17.red(" \u2718 Auto-update failed: ") + chalk17.dim(err.message) + "\n" + chalk17.dim(" Run manually: npm install -g techunter\n") + chalk17.cyan(" You \u203A ")
|
|
3809
3968
|
);
|
|
3810
3969
|
});
|
|
3811
3970
|
}
|
|
@@ -3828,6 +3987,8 @@ var SLASH_NAMES = [
|
|
|
3828
3987
|
"/d",
|
|
3829
3988
|
"/edit",
|
|
3830
3989
|
"/e",
|
|
3990
|
+
"/move",
|
|
3991
|
+
"/mv",
|
|
3831
3992
|
"/review",
|
|
3832
3993
|
"/rv",
|
|
3833
3994
|
"/accept",
|
|
@@ -3853,8 +4014,14 @@ var _rl = null;
|
|
|
3853
4014
|
function promptUser() {
|
|
3854
4015
|
return new Promise((resolve) => {
|
|
3855
4016
|
if (process.stdin.isPaused()) process.stdin.resume();
|
|
4017
|
+
if (process.stdin.isTTY) {
|
|
4018
|
+
try {
|
|
4019
|
+
process.stdin.setRawMode(true);
|
|
4020
|
+
} catch {
|
|
4021
|
+
}
|
|
4022
|
+
}
|
|
3856
4023
|
_rl.resume();
|
|
3857
|
-
_rl.question(
|
|
4024
|
+
_rl.question(chalk18.cyan("You") + chalk18.dim(" \u203A "), resolve);
|
|
3858
4025
|
});
|
|
3859
4026
|
}
|
|
3860
4027
|
var COMMANDS = [
|
|
@@ -3864,6 +4031,7 @@ var COMMANDS = [
|
|
|
3864
4031
|
{ cmd: "/new", alias: "/n", desc: "Create a new task interactively" },
|
|
3865
4032
|
{ cmd: "/close", alias: "/d", desc: "Close (delete) a task" },
|
|
3866
4033
|
{ cmd: "/edit", alias: "/e", desc: "Edit the title or description of a task" },
|
|
4034
|
+
{ cmd: "/move", alias: "/mv", desc: "Move your task under another task as a sub-task" },
|
|
3867
4035
|
{ cmd: "/submit", alias: "/s", desc: "Commit, create PR, and mark in-review" },
|
|
3868
4036
|
{ cmd: "/review", alias: "/rv", desc: "List tasks waiting for your approval" },
|
|
3869
4037
|
{ cmd: "/accept", alias: "/ac", desc: "Accept a reviewed task: merge PR and close issue" },
|
|
@@ -3875,40 +4043,40 @@ var COMMANDS = [
|
|
|
3875
4043
|
];
|
|
3876
4044
|
function cmdHelp() {
|
|
3877
4045
|
console.log("");
|
|
3878
|
-
console.log(
|
|
3879
|
-
console.log(
|
|
4046
|
+
console.log(chalk18.bold(" Commands"));
|
|
4047
|
+
console.log(chalk18.dim(" \u2500".repeat(35)));
|
|
3880
4048
|
for (const { cmd, alias, desc } of COMMANDS) {
|
|
3881
|
-
const left = (cmd + (alias ? ` ${
|
|
3882
|
-
console.log(` ${
|
|
4049
|
+
const left = (cmd + (alias ? ` ${chalk18.dim(alias)}` : "")).padEnd(22);
|
|
4050
|
+
console.log(` ${chalk18.cyan(cmd)}${alias ? " " + chalk18.dim(alias) : ""}`.padEnd(30) + chalk18.dim(desc));
|
|
3883
4051
|
}
|
|
3884
|
-
console.log(
|
|
4052
|
+
console.log(chalk18.dim("\n Anything else is sent to the AI agent.\n"));
|
|
3885
4053
|
}
|
|
3886
4054
|
function printBanner(config) {
|
|
3887
4055
|
const { owner, repo } = config.github;
|
|
3888
|
-
const g =
|
|
3889
|
-
const b =
|
|
3890
|
-
const p =
|
|
4056
|
+
const g = chalk18.cyan;
|
|
4057
|
+
const b = chalk18.bold.white;
|
|
4058
|
+
const p = chalk18.yellow.bold;
|
|
3891
4059
|
console.log("");
|
|
3892
4060
|
console.log(" " + g("\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557"));
|
|
3893
4061
|
console.log(p("\u25C6") + b("\u2550\u2550\u2550") + g("\u256C") + b(" TECHUNTER ") + g("\u256C") + b("\u2550\u2550\u2550\u25B6"));
|
|
3894
4062
|
console.log(" " + g("\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D"));
|
|
3895
4063
|
console.log("");
|
|
3896
4064
|
console.log(
|
|
3897
|
-
" " +
|
|
4065
|
+
" " + chalk18.bold("Techunter") + chalk18.dim(` v${version}`) + chalk18.dim(" \xB7 ") + chalk18.cyan(getModel(config)) + chalk18.dim(" \xB7 ") + chalk18.dim(`${owner}/${repo}`)
|
|
3898
4066
|
);
|
|
3899
4067
|
console.log("");
|
|
3900
4068
|
}
|
|
3901
4069
|
async function initNewRepo(config, owner, repo) {
|
|
3902
4070
|
console.log("");
|
|
3903
|
-
console.log(
|
|
3904
|
-
console.log(
|
|
4071
|
+
console.log(chalk18.bold.cyan(` New repo detected: ${owner}/${repo}`));
|
|
4072
|
+
console.log(chalk18.dim(" Setting up Techunter for this repository...\n"));
|
|
3905
4073
|
const newConfig = {
|
|
3906
4074
|
...config,
|
|
3907
4075
|
github: { owner, repo }
|
|
3908
4076
|
};
|
|
3909
4077
|
setConfig({ github: newConfig.github });
|
|
3910
|
-
const
|
|
3911
|
-
const spinner =
|
|
4078
|
+
const ora17 = (await import("ora")).default;
|
|
4079
|
+
const spinner = ora17("Creating Techunter labels...").start();
|
|
3912
4080
|
try {
|
|
3913
4081
|
await ensureLabels(newConfig);
|
|
3914
4082
|
spinner.succeed("Labels ready");
|
|
@@ -3924,7 +4092,7 @@ async function main() {
|
|
|
3924
4092
|
try {
|
|
3925
4093
|
await configCommand();
|
|
3926
4094
|
} catch (err) {
|
|
3927
|
-
console.error(
|
|
4095
|
+
console.error(chalk18.red(`
|
|
3928
4096
|
Error: ${err.message}`));
|
|
3929
4097
|
process.exit(1);
|
|
3930
4098
|
}
|
|
@@ -3938,7 +4106,7 @@ Error: ${err.message}`));
|
|
|
3938
4106
|
await initCommand();
|
|
3939
4107
|
config = getConfig();
|
|
3940
4108
|
} catch (err) {
|
|
3941
|
-
console.error(
|
|
4109
|
+
console.error(chalk18.red(`
|
|
3942
4110
|
Setup failed: ${err.message}`));
|
|
3943
4111
|
process.exit(1);
|
|
3944
4112
|
return;
|
|
@@ -3956,11 +4124,11 @@ Setup failed: ${err.message}`));
|
|
|
3956
4124
|
}
|
|
3957
4125
|
}
|
|
3958
4126
|
} else if (!config.github.owner) {
|
|
3959
|
-
console.error(
|
|
4127
|
+
console.error(chalk18.red("\nNo git remote found and no repo configured. Run tch init."));
|
|
3960
4128
|
process.exit(1);
|
|
3961
4129
|
}
|
|
3962
4130
|
printBanner(config);
|
|
3963
|
-
console.log(
|
|
4131
|
+
console.log(chalk18.dim(" Type /help for commands, or describe what you want.\n"));
|
|
3964
4132
|
startAutoUpdate(version).catch(() => {
|
|
3965
4133
|
});
|
|
3966
4134
|
await printTaskList(config);
|
|
@@ -3972,11 +4140,11 @@ Setup failed: ${err.message}`));
|
|
|
3972
4140
|
terminal: true
|
|
3973
4141
|
});
|
|
3974
4142
|
_rl.on("close", () => {
|
|
3975
|
-
console.log(
|
|
4143
|
+
console.log(chalk18.gray("\nGoodbye!"));
|
|
3976
4144
|
process.exit(0);
|
|
3977
4145
|
});
|
|
3978
4146
|
_rl.on("SIGINT", () => {
|
|
3979
|
-
console.log(
|
|
4147
|
+
console.log(chalk18.gray("\nGoodbye!"));
|
|
3980
4148
|
process.exit(0);
|
|
3981
4149
|
});
|
|
3982
4150
|
const messages = [];
|
|
@@ -4001,7 +4169,7 @@ Setup failed: ${err.message}`));
|
|
|
4001
4169
|
const preselected = arg ? parseInt(arg, 10) : void 0;
|
|
4002
4170
|
const result = await run3({ issue_number: Number.isNaN(preselected) ? void 0 : preselected }, config);
|
|
4003
4171
|
if (result && result !== "Cancelled.") {
|
|
4004
|
-
console.log(
|
|
4172
|
+
console.log(chalk18.green(`
|
|
4005
4173
|
${result}
|
|
4006
4174
|
`));
|
|
4007
4175
|
}
|
|
@@ -4011,7 +4179,7 @@ Setup failed: ${err.message}`));
|
|
|
4011
4179
|
case "/new":
|
|
4012
4180
|
case "/n": {
|
|
4013
4181
|
const result = await run4({}, config);
|
|
4014
|
-
console.log(
|
|
4182
|
+
console.log(chalk18.green(`
|
|
4015
4183
|
${result}
|
|
4016
4184
|
`));
|
|
4017
4185
|
await printTaskList(config);
|
|
@@ -4029,7 +4197,18 @@ Setup failed: ${err.message}`));
|
|
|
4029
4197
|
const arg = userInput.slice(cmd.length).trim().replace(/^#/, "");
|
|
4030
4198
|
const preselected = arg ? parseInt(arg, 10) : void 0;
|
|
4031
4199
|
const result = await run11({ issue_number: Number.isNaN(preselected) ? void 0 : preselected }, config);
|
|
4032
|
-
console.log(
|
|
4200
|
+
console.log(chalk18.green(`
|
|
4201
|
+
${result}
|
|
4202
|
+
`));
|
|
4203
|
+
await printTaskList(config);
|
|
4204
|
+
break;
|
|
4205
|
+
}
|
|
4206
|
+
case "/move":
|
|
4207
|
+
case "/mv": {
|
|
4208
|
+
const arg = userInput.slice(cmd.length).trim().replace(/^#/, "");
|
|
4209
|
+
const preselected = arg ? parseInt(arg, 10) : void 0;
|
|
4210
|
+
const result = await run12({ issue_number: Number.isNaN(preselected) ? void 0 : preselected }, config);
|
|
4211
|
+
console.log(chalk18.green(`
|
|
4033
4212
|
${result}
|
|
4034
4213
|
`));
|
|
4035
4214
|
await printTaskList(config);
|
|
@@ -4038,7 +4217,7 @@ Setup failed: ${err.message}`));
|
|
|
4038
4217
|
case "/close":
|
|
4039
4218
|
case "/d": {
|
|
4040
4219
|
const result = await run2({}, config);
|
|
4041
|
-
console.log(
|
|
4220
|
+
console.log(chalk18.green(`
|
|
4042
4221
|
${result}
|
|
4043
4222
|
`));
|
|
4044
4223
|
await printTaskList(config);
|
|
@@ -4061,7 +4240,7 @@ Setup failed: ${err.message}`));
|
|
|
4061
4240
|
const arg = userInput.slice(cmd.length).trim().replace(/^#/, "");
|
|
4062
4241
|
const preselected = arg ? parseInt(arg, 10) : void 0;
|
|
4063
4242
|
const result = await run6({ issue_number: Number.isNaN(preselected) ? void 0 : preselected }, config);
|
|
4064
|
-
console.log(
|
|
4243
|
+
console.log(chalk18.green(`
|
|
4065
4244
|
${result}
|
|
4066
4245
|
`));
|
|
4067
4246
|
await printTaskList(config);
|
|
@@ -4077,7 +4256,7 @@ Setup failed: ${err.message}`));
|
|
|
4077
4256
|
config = getConfig();
|
|
4078
4257
|
await printTaskList(config);
|
|
4079
4258
|
} catch (err) {
|
|
4080
|
-
console.error(
|
|
4259
|
+
console.error(chalk18.red(`
|
|
4081
4260
|
Init failed: ${err.message}
|
|
4082
4261
|
`));
|
|
4083
4262
|
}
|
|
@@ -4088,14 +4267,14 @@ Init failed: ${err.message}
|
|
|
4088
4267
|
break;
|
|
4089
4268
|
case "/wiki":
|
|
4090
4269
|
case "/w": {
|
|
4091
|
-
const result = await
|
|
4092
|
-
console.log(
|
|
4270
|
+
const result = await run13({}, config);
|
|
4271
|
+
console.log(chalk18.green(`
|
|
4093
4272
|
${result}
|
|
4094
4273
|
`));
|
|
4095
4274
|
break;
|
|
4096
4275
|
}
|
|
4097
4276
|
default:
|
|
4098
|
-
console.log(
|
|
4277
|
+
console.log(chalk18.yellow(` Unknown command: ${cmd} (try /help)`));
|
|
4099
4278
|
}
|
|
4100
4279
|
continue;
|
|
4101
4280
|
}
|
|
@@ -4103,10 +4282,10 @@ Init failed: ${err.message}
|
|
|
4103
4282
|
messages.push({ role: "user", content: userInput });
|
|
4104
4283
|
try {
|
|
4105
4284
|
const response = await runAgentLoop(config, messages);
|
|
4106
|
-
console.log("\n" +
|
|
4285
|
+
console.log("\n" + chalk18.green("Techunter:") + "\n" + renderMarkdown(response));
|
|
4107
4286
|
} catch (err) {
|
|
4108
4287
|
messages.splice(prevLength);
|
|
4109
|
-
console.error(
|
|
4288
|
+
console.error(chalk18.red(`
|
|
4110
4289
|
Error: ${err.message}
|
|
4111
4290
|
`));
|
|
4112
4291
|
}
|
|
@@ -4114,6 +4293,6 @@ Error: ${err.message}
|
|
|
4114
4293
|
}
|
|
4115
4294
|
}
|
|
4116
4295
|
main().catch((err) => {
|
|
4117
|
-
console.error(
|
|
4296
|
+
console.error(chalk18.red(`Fatal: ${err.message}`));
|
|
4118
4297
|
process.exit(1);
|
|
4119
4298
|
});
|