gut-cli 0.1.10 → 0.1.12
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 +21 -0
- package/dist/index.js +415 -224
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/index.ts
|
|
4
|
-
import { Command as
|
|
4
|
+
import { Command as Command18 } from "commander";
|
|
5
5
|
|
|
6
6
|
// src/commands/cleanup.ts
|
|
7
7
|
import { Command } from "commander";
|
|
@@ -395,7 +395,6 @@ function findTemplate(repoRoot, templateName) {
|
|
|
395
395
|
}
|
|
396
396
|
function applyTemplate(userTemplate, templateName, variables) {
|
|
397
397
|
const langInstruction = getLanguageInstruction(getLanguage());
|
|
398
|
-
const isUserTemplate = !!userTemplate;
|
|
399
398
|
let result = userTemplate || loadTemplate(templateName);
|
|
400
399
|
result = result.replace(/\{\{#(\w+)\}\}([\s\S]*?)\{\{\/\1\}\}/g, (_, key, content) => {
|
|
401
400
|
return variables[key] ? content : "";
|
|
@@ -403,7 +402,7 @@ function applyTemplate(userTemplate, templateName, variables) {
|
|
|
403
402
|
for (const [key, value] of Object.entries(variables)) {
|
|
404
403
|
result = result.replace(new RegExp(`\\{\\{${key}\\}\\}`, "g"), value || "");
|
|
405
404
|
}
|
|
406
|
-
if (
|
|
405
|
+
if (langInstruction) {
|
|
407
406
|
result += langInstruction;
|
|
408
407
|
}
|
|
409
408
|
return result;
|
|
@@ -514,7 +513,8 @@ async function generateChangelog(context, options, template) {
|
|
|
514
513
|
fromRef: context.fromRef,
|
|
515
514
|
toRef: context.toRef,
|
|
516
515
|
commits: commitList,
|
|
517
|
-
diff: context.diff.slice(0, 8e3)
|
|
516
|
+
diff: context.diff.slice(0, 8e3),
|
|
517
|
+
todayDate: (/* @__PURE__ */ new Date()).toISOString().split("T")[0]
|
|
518
518
|
});
|
|
519
519
|
const result = await generateObject({
|
|
520
520
|
model,
|
|
@@ -799,14 +799,14 @@ var commitCommand = new Command3("commit").description("Generate a commit messag
|
|
|
799
799
|
console.log(chalk3.green("\u2713 Committed successfully"));
|
|
800
800
|
} else if (answer.toLowerCase() === "e") {
|
|
801
801
|
console.log(chalk3.gray("Opening editor..."));
|
|
802
|
-
const { execSync:
|
|
802
|
+
const { execSync: execSync7 } = await import("child_process");
|
|
803
803
|
const editor = process.env.EDITOR || process.env.VISUAL || "vi";
|
|
804
804
|
const fs2 = await import("fs");
|
|
805
805
|
const os = await import("os");
|
|
806
806
|
const path2 = await import("path");
|
|
807
807
|
const tmpFile = path2.join(os.tmpdir(), "gut-commit-msg.txt");
|
|
808
808
|
fs2.writeFileSync(tmpFile, message);
|
|
809
|
-
|
|
809
|
+
execSync7(`${editor} "${tmpFile}"`, { stdio: "inherit" });
|
|
810
810
|
const editedMessage = fs2.readFileSync(tmpFile, "utf-8").trim();
|
|
811
811
|
fs2.unlinkSync(tmpFile);
|
|
812
812
|
if (editedMessage) {
|
|
@@ -830,11 +830,45 @@ var commitCommand = new Command3("commit").description("Generate a commit messag
|
|
|
830
830
|
|
|
831
831
|
// src/commands/pr.ts
|
|
832
832
|
import { Command as Command4 } from "commander";
|
|
833
|
-
import
|
|
833
|
+
import chalk5 from "chalk";
|
|
834
834
|
import ora3 from "ora";
|
|
835
835
|
import { simpleGit as simpleGit3 } from "simple-git";
|
|
836
836
|
import { existsSync as existsSync3, readFileSync as readFileSync3 } from "fs";
|
|
837
837
|
import { join as join3 } from "path";
|
|
838
|
+
import { execSync as execSync3 } from "child_process";
|
|
839
|
+
|
|
840
|
+
// src/lib/gh.ts
|
|
841
|
+
import { execSync as execSync2 } from "child_process";
|
|
842
|
+
import chalk4 from "chalk";
|
|
843
|
+
var ghInstalledCache = null;
|
|
844
|
+
function isGhCliInstalled() {
|
|
845
|
+
if (ghInstalledCache !== null) {
|
|
846
|
+
return ghInstalledCache;
|
|
847
|
+
}
|
|
848
|
+
try {
|
|
849
|
+
execSync2("gh --version", { stdio: "pipe" });
|
|
850
|
+
ghInstalledCache = true;
|
|
851
|
+
return true;
|
|
852
|
+
} catch {
|
|
853
|
+
ghInstalledCache = false;
|
|
854
|
+
return false;
|
|
855
|
+
}
|
|
856
|
+
}
|
|
857
|
+
function printGhNotInstalledMessage() {
|
|
858
|
+
console.log(chalk4.yellow("\n\u26A0 GitHub CLI (gh) is not installed"));
|
|
859
|
+
console.log(chalk4.gray(" This command requires gh CLI. Install it:"));
|
|
860
|
+
console.log(chalk4.gray(" brew install gh (macOS)"));
|
|
861
|
+
console.log(chalk4.gray(" https://cli.github.com/"));
|
|
862
|
+
}
|
|
863
|
+
function requireGhCli() {
|
|
864
|
+
if (!isGhCliInstalled()) {
|
|
865
|
+
printGhNotInstalledMessage();
|
|
866
|
+
return false;
|
|
867
|
+
}
|
|
868
|
+
return true;
|
|
869
|
+
}
|
|
870
|
+
|
|
871
|
+
// src/commands/pr.ts
|
|
838
872
|
var GITHUB_PR_TEMPLATE_PATHS = [
|
|
839
873
|
".github/pull_request_template.md",
|
|
840
874
|
".github/PULL_REQUEST_TEMPLATE.md",
|
|
@@ -855,7 +889,7 @@ var prCommand = new Command4("pr").description("Generate a pull request title an
|
|
|
855
889
|
const git = simpleGit3();
|
|
856
890
|
const isRepo = await git.checkIsRepo();
|
|
857
891
|
if (!isRepo) {
|
|
858
|
-
console.error(
|
|
892
|
+
console.error(chalk5.red("Error: Not a git repository"));
|
|
859
893
|
process.exit(1);
|
|
860
894
|
}
|
|
861
895
|
const provider = options.provider.toLowerCase();
|
|
@@ -903,73 +937,78 @@ var prCommand = new Command4("pr").description("Generate a pull request title an
|
|
|
903
937
|
template || void 0
|
|
904
938
|
);
|
|
905
939
|
spinner.stop();
|
|
906
|
-
console.log(
|
|
907
|
-
console.log(
|
|
908
|
-
console.log(
|
|
909
|
-
console.log(
|
|
940
|
+
console.log(chalk5.bold("\n\u{1F4DD} Generated PR:\n"));
|
|
941
|
+
console.log(chalk5.cyan("Title:"), chalk5.white(title));
|
|
942
|
+
console.log(chalk5.cyan("\nDescription:"));
|
|
943
|
+
console.log(chalk5.gray("\u2500".repeat(50)));
|
|
910
944
|
console.log(body);
|
|
911
|
-
console.log(
|
|
945
|
+
console.log(chalk5.gray("\u2500".repeat(50)));
|
|
912
946
|
if (options.copy) {
|
|
913
947
|
try {
|
|
914
|
-
const { execSync: execSync5 } = await import("child_process");
|
|
915
948
|
const fullText = `${title}
|
|
916
949
|
|
|
917
950
|
${body}`;
|
|
918
|
-
|
|
919
|
-
console.log(
|
|
951
|
+
execSync3("pbcopy", { input: fullText });
|
|
952
|
+
console.log(chalk5.green("\n\u2713 Copied to clipboard"));
|
|
920
953
|
} catch {
|
|
921
|
-
console.log(
|
|
954
|
+
console.log(chalk5.yellow("\n\u26A0 Could not copy to clipboard"));
|
|
922
955
|
}
|
|
923
956
|
}
|
|
924
|
-
|
|
957
|
+
const ghInstalled = isGhCliInstalled();
|
|
958
|
+
if (!ghInstalled) {
|
|
959
|
+
console.log(chalk5.yellow("\n\u26A0 GitHub CLI (gh) is not installed"));
|
|
960
|
+
console.log(chalk5.gray(" To create PRs directly from gut, install gh CLI:"));
|
|
961
|
+
console.log(chalk5.gray(" brew install gh (macOS)"));
|
|
962
|
+
console.log(chalk5.gray(" https://cli.github.com/"));
|
|
963
|
+
if (!options.copy) {
|
|
964
|
+
console.log(chalk5.gray("\nTip: Use --copy to copy to clipboard"));
|
|
965
|
+
}
|
|
966
|
+
} else {
|
|
925
967
|
const readline = await import("readline");
|
|
926
968
|
const rl = readline.createInterface({
|
|
927
969
|
input: process.stdin,
|
|
928
970
|
output: process.stdout
|
|
929
971
|
});
|
|
972
|
+
const promptMessage = options.create ? chalk5.cyan("\nCreate PR with this description? (y/N) ") : chalk5.cyan("\nCreate PR with gh CLI? (y/N) ");
|
|
930
973
|
const answer = await new Promise((resolve) => {
|
|
931
|
-
rl.question(
|
|
974
|
+
rl.question(promptMessage, resolve);
|
|
932
975
|
});
|
|
933
976
|
rl.close();
|
|
934
977
|
if (answer.toLowerCase() === "y") {
|
|
935
978
|
const createSpinner = ora3("Creating PR...").start();
|
|
936
979
|
try {
|
|
937
|
-
const { execSync: execSync5 } = await import("child_process");
|
|
938
980
|
const escapedTitle = title.replace(/"/g, '\\"');
|
|
939
981
|
const escapedBody = body.replace(/"/g, '\\"');
|
|
940
|
-
|
|
982
|
+
execSync3(
|
|
941
983
|
`gh pr create --title "${escapedTitle}" --body "${escapedBody}" --base ${baseBranch}`,
|
|
942
984
|
{ stdio: "pipe" }
|
|
943
985
|
);
|
|
944
986
|
createSpinner.succeed("PR created successfully!");
|
|
945
987
|
} catch (error) {
|
|
946
988
|
createSpinner.fail("Failed to create PR");
|
|
947
|
-
console.error(
|
|
989
|
+
console.error(chalk5.gray("Make sure gh CLI is authenticated: gh auth login"));
|
|
948
990
|
}
|
|
991
|
+
} else if (!options.copy) {
|
|
992
|
+
console.log(chalk5.gray("\nTip: Use --copy to copy to clipboard"));
|
|
949
993
|
}
|
|
950
994
|
}
|
|
951
|
-
if (!options.copy && !options.create) {
|
|
952
|
-
console.log(chalk4.gray("\nOptions:"));
|
|
953
|
-
console.log(chalk4.gray(" --copy Copy to clipboard"));
|
|
954
|
-
console.log(chalk4.gray(" --create Create PR with gh CLI"));
|
|
955
|
-
}
|
|
956
995
|
} catch (error) {
|
|
957
996
|
spinner.fail("Failed to generate PR description");
|
|
958
|
-
console.error(
|
|
997
|
+
console.error(chalk5.red(error instanceof Error ? error.message : "Unknown error"));
|
|
959
998
|
process.exit(1);
|
|
960
999
|
}
|
|
961
1000
|
});
|
|
962
1001
|
|
|
963
1002
|
// src/commands/review.ts
|
|
964
1003
|
import { Command as Command5 } from "commander";
|
|
965
|
-
import
|
|
1004
|
+
import chalk6 from "chalk";
|
|
966
1005
|
import ora4 from "ora";
|
|
967
1006
|
import { simpleGit as simpleGit4 } from "simple-git";
|
|
968
|
-
import { execSync as
|
|
1007
|
+
import { execSync as execSync4 } from "child_process";
|
|
969
1008
|
async function getPRDiff(prNumber) {
|
|
970
1009
|
try {
|
|
971
|
-
const diff =
|
|
972
|
-
const prJsonStr =
|
|
1010
|
+
const diff = execSync4(`gh pr diff ${prNumber}`, { encoding: "utf-8", maxBuffer: 10 * 1024 * 1024 });
|
|
1011
|
+
const prJsonStr = execSync4(`gh pr view ${prNumber} --json number,title,author,url`, { encoding: "utf-8" });
|
|
973
1012
|
const prJson = JSON.parse(prJsonStr);
|
|
974
1013
|
return {
|
|
975
1014
|
diff,
|
|
@@ -991,7 +1030,7 @@ var reviewCommand = new Command5("review").description("Get an AI code review of
|
|
|
991
1030
|
const git = simpleGit4();
|
|
992
1031
|
const isRepo = await git.checkIsRepo();
|
|
993
1032
|
if (!isRepo) {
|
|
994
|
-
console.error(
|
|
1033
|
+
console.error(chalk6.red("Error: Not a git repository"));
|
|
995
1034
|
process.exit(1);
|
|
996
1035
|
}
|
|
997
1036
|
const provider = options.provider.toLowerCase();
|
|
@@ -1000,7 +1039,11 @@ var reviewCommand = new Command5("review").description("Get an AI code review of
|
|
|
1000
1039
|
let diff;
|
|
1001
1040
|
let prInfo = null;
|
|
1002
1041
|
if (prNumber) {
|
|
1003
|
-
spinner.
|
|
1042
|
+
spinner.stop();
|
|
1043
|
+
if (!requireGhCli()) {
|
|
1044
|
+
process.exit(1);
|
|
1045
|
+
}
|
|
1046
|
+
spinner.start(`Fetching PR #${prNumber}...`);
|
|
1004
1047
|
const result = await getPRDiff(prNumber);
|
|
1005
1048
|
diff = result.diff;
|
|
1006
1049
|
prInfo = result.prInfo;
|
|
@@ -1035,29 +1078,29 @@ var reviewCommand = new Command5("review").description("Get an AI code review of
|
|
|
1035
1078
|
return;
|
|
1036
1079
|
}
|
|
1037
1080
|
if (prInfo) {
|
|
1038
|
-
console.log(
|
|
1081
|
+
console.log(chalk6.bold(`
|
|
1039
1082
|
\u{1F517} PR #${prInfo.number}: ${prInfo.title}`));
|
|
1040
|
-
console.log(
|
|
1083
|
+
console.log(chalk6.gray(` by ${prInfo.author} - ${prInfo.url}`));
|
|
1041
1084
|
}
|
|
1042
1085
|
printReview(review);
|
|
1043
1086
|
} catch (error) {
|
|
1044
1087
|
spinner.fail("Failed to generate review");
|
|
1045
|
-
console.error(
|
|
1088
|
+
console.error(chalk6.red(error instanceof Error ? error.message : "Unknown error"));
|
|
1046
1089
|
process.exit(1);
|
|
1047
1090
|
}
|
|
1048
1091
|
});
|
|
1049
1092
|
function printReview(review) {
|
|
1050
|
-
console.log(
|
|
1051
|
-
console.log(
|
|
1093
|
+
console.log(chalk6.bold("\n\u{1F50D} AI Code Review\n"));
|
|
1094
|
+
console.log(chalk6.cyan("Summary:"));
|
|
1052
1095
|
console.log(` ${review.summary}
|
|
1053
1096
|
`);
|
|
1054
1097
|
if (review.issues.length > 0) {
|
|
1055
|
-
console.log(
|
|
1098
|
+
console.log(chalk6.cyan("Issues Found:"));
|
|
1056
1099
|
for (const issue of review.issues) {
|
|
1057
1100
|
const severityColors = {
|
|
1058
|
-
critical:
|
|
1059
|
-
warning:
|
|
1060
|
-
suggestion:
|
|
1101
|
+
critical: chalk6.red,
|
|
1102
|
+
warning: chalk6.yellow,
|
|
1103
|
+
suggestion: chalk6.blue
|
|
1061
1104
|
};
|
|
1062
1105
|
const severityIcons = {
|
|
1063
1106
|
critical: "\u{1F534}",
|
|
@@ -1068,34 +1111,34 @@ function printReview(review) {
|
|
|
1068
1111
|
const icon = severityIcons[issue.severity];
|
|
1069
1112
|
console.log(`
|
|
1070
1113
|
${icon} ${color(issue.severity.toUpperCase())}`);
|
|
1071
|
-
console.log(` ${
|
|
1114
|
+
console.log(` ${chalk6.gray("File:")} ${issue.file}${issue.line ? `:${issue.line}` : ""}`);
|
|
1072
1115
|
console.log(` ${issue.message}`);
|
|
1073
1116
|
if (issue.suggestion) {
|
|
1074
|
-
console.log(` ${
|
|
1117
|
+
console.log(` ${chalk6.green("\u2192")} ${issue.suggestion}`);
|
|
1075
1118
|
}
|
|
1076
1119
|
}
|
|
1077
1120
|
} else {
|
|
1078
|
-
console.log(
|
|
1121
|
+
console.log(chalk6.green(" \u2713 No issues found!\n"));
|
|
1079
1122
|
}
|
|
1080
1123
|
if (review.positives.length > 0) {
|
|
1081
|
-
console.log(
|
|
1124
|
+
console.log(chalk6.cyan("\nGood Practices:"));
|
|
1082
1125
|
for (const positive of review.positives) {
|
|
1083
|
-
console.log(` ${
|
|
1126
|
+
console.log(` ${chalk6.green("\u2713")} ${positive}`);
|
|
1084
1127
|
}
|
|
1085
1128
|
}
|
|
1086
1129
|
const criticalCount = review.issues.filter((i) => i.severity === "critical").length;
|
|
1087
1130
|
const warningCount = review.issues.filter((i) => i.severity === "warning").length;
|
|
1088
1131
|
const suggestionCount = review.issues.filter((i) => i.severity === "suggestion").length;
|
|
1089
|
-
console.log(
|
|
1132
|
+
console.log(chalk6.gray("\n\u2500".repeat(40)));
|
|
1090
1133
|
console.log(
|
|
1091
|
-
` ${
|
|
1134
|
+
` ${chalk6.red(criticalCount)} critical ${chalk6.yellow(warningCount)} warnings ${chalk6.blue(suggestionCount)} suggestions`
|
|
1092
1135
|
);
|
|
1093
1136
|
console.log();
|
|
1094
1137
|
}
|
|
1095
1138
|
|
|
1096
1139
|
// src/commands/merge.ts
|
|
1097
1140
|
import { Command as Command6 } from "commander";
|
|
1098
|
-
import
|
|
1141
|
+
import chalk7 from "chalk";
|
|
1099
1142
|
import ora5 from "ora";
|
|
1100
1143
|
import { simpleGit as simpleGit5 } from "simple-git";
|
|
1101
1144
|
import * as fs from "fs";
|
|
@@ -1104,53 +1147,53 @@ var mergeCommand = new Command6("merge").description("Merge a branch with AI-pow
|
|
|
1104
1147
|
const git = simpleGit5();
|
|
1105
1148
|
const isRepo = await git.checkIsRepo();
|
|
1106
1149
|
if (!isRepo) {
|
|
1107
|
-
console.error(
|
|
1150
|
+
console.error(chalk7.red("Error: Not a git repository"));
|
|
1108
1151
|
process.exit(1);
|
|
1109
1152
|
}
|
|
1110
1153
|
const provider = options.provider.toLowerCase();
|
|
1111
1154
|
const status = await git.status();
|
|
1112
1155
|
if (status.modified.length > 0 || status.staged.length > 0) {
|
|
1113
|
-
console.error(
|
|
1114
|
-
console.log(
|
|
1156
|
+
console.error(chalk7.red("Error: Working directory has uncommitted changes"));
|
|
1157
|
+
console.log(chalk7.gray("Please commit or stash your changes first"));
|
|
1115
1158
|
process.exit(1);
|
|
1116
1159
|
}
|
|
1117
1160
|
const branchInfo = await git.branch();
|
|
1118
1161
|
const currentBranch = branchInfo.current;
|
|
1119
|
-
console.log(
|
|
1120
|
-
Merging ${
|
|
1162
|
+
console.log(chalk7.bold(`
|
|
1163
|
+
Merging ${chalk7.cyan(branch)} into ${chalk7.cyan(currentBranch)}...
|
|
1121
1164
|
`));
|
|
1122
1165
|
try {
|
|
1123
1166
|
await git.merge([branch]);
|
|
1124
|
-
console.log(
|
|
1167
|
+
console.log(chalk7.green("\u2713 Merged successfully (no conflicts)"));
|
|
1125
1168
|
return;
|
|
1126
1169
|
} catch (error) {
|
|
1127
1170
|
}
|
|
1128
1171
|
const conflictStatus = await git.status();
|
|
1129
1172
|
const conflictedFiles = conflictStatus.conflicted;
|
|
1130
1173
|
if (conflictedFiles.length === 0) {
|
|
1131
|
-
console.error(
|
|
1174
|
+
console.error(chalk7.red("Merge failed for unknown reason"));
|
|
1132
1175
|
await git.merge(["--abort"]);
|
|
1133
1176
|
process.exit(1);
|
|
1134
1177
|
}
|
|
1135
|
-
console.log(
|
|
1178
|
+
console.log(chalk7.yellow(`\u26A0 ${conflictedFiles.length} conflict(s) detected
|
|
1136
1179
|
`));
|
|
1137
1180
|
const spinner = ora5();
|
|
1138
1181
|
const rootDir = await git.revparse(["--show-toplevel"]);
|
|
1139
1182
|
const template = findTemplate(rootDir.trim(), "merge");
|
|
1140
1183
|
if (template) {
|
|
1141
|
-
console.log(
|
|
1184
|
+
console.log(chalk7.gray("Using merge template from project...\n"));
|
|
1142
1185
|
}
|
|
1143
1186
|
for (const file of conflictedFiles) {
|
|
1144
1187
|
const filePath = path.join(rootDir.trim(), file);
|
|
1145
1188
|
const content = fs.readFileSync(filePath, "utf-8");
|
|
1146
|
-
console.log(
|
|
1189
|
+
console.log(chalk7.bold(`
|
|
1147
1190
|
\u{1F4C4} ${file}`));
|
|
1148
1191
|
const conflictMatch = content.match(/<<<<<<< HEAD[\s\S]*?>>>>>>>.+/g);
|
|
1149
1192
|
if (conflictMatch) {
|
|
1150
|
-
console.log(
|
|
1151
|
-
console.log(
|
|
1152
|
-
if (conflictMatch[0].length > 500) console.log(
|
|
1153
|
-
console.log(
|
|
1193
|
+
console.log(chalk7.gray("\u2500".repeat(50)));
|
|
1194
|
+
console.log(chalk7.gray(conflictMatch[0].slice(0, 500)));
|
|
1195
|
+
if (conflictMatch[0].length > 500) console.log(chalk7.gray("..."));
|
|
1196
|
+
console.log(chalk7.gray("\u2500".repeat(50)));
|
|
1154
1197
|
}
|
|
1155
1198
|
spinner.start("AI is analyzing conflict...");
|
|
1156
1199
|
try {
|
|
@@ -1160,55 +1203,55 @@ Merging ${chalk6.cyan(branch)} into ${chalk6.cyan(currentBranch)}...
|
|
|
1160
1203
|
theirsRef: branch
|
|
1161
1204
|
}, { provider, model: options.model }, template || void 0);
|
|
1162
1205
|
spinner.stop();
|
|
1163
|
-
console.log(
|
|
1164
|
-
console.log(
|
|
1206
|
+
console.log(chalk7.cyan("\n\u{1F916} AI suggests:"));
|
|
1207
|
+
console.log(chalk7.gray("\u2500".repeat(50)));
|
|
1165
1208
|
const preview = resolution.resolvedContent.slice(0, 800);
|
|
1166
1209
|
console.log(preview);
|
|
1167
|
-
if (resolution.resolvedContent.length > 800) console.log(
|
|
1168
|
-
console.log(
|
|
1169
|
-
console.log(
|
|
1170
|
-
console.log(
|
|
1210
|
+
if (resolution.resolvedContent.length > 800) console.log(chalk7.gray("..."));
|
|
1211
|
+
console.log(chalk7.gray("\u2500".repeat(50)));
|
|
1212
|
+
console.log(chalk7.gray(`Strategy: ${resolution.strategy}`));
|
|
1213
|
+
console.log(chalk7.gray(`Reason: ${resolution.explanation}`));
|
|
1171
1214
|
const readline = await import("readline");
|
|
1172
1215
|
const rl = readline.createInterface({
|
|
1173
1216
|
input: process.stdin,
|
|
1174
1217
|
output: process.stdout
|
|
1175
1218
|
});
|
|
1176
1219
|
const answer = await new Promise((resolve) => {
|
|
1177
|
-
rl.question(
|
|
1220
|
+
rl.question(chalk7.cyan("\nAccept this resolution? (y/n/s to skip) "), resolve);
|
|
1178
1221
|
});
|
|
1179
1222
|
rl.close();
|
|
1180
1223
|
if (answer.toLowerCase() === "y") {
|
|
1181
1224
|
fs.writeFileSync(filePath, resolution.resolvedContent);
|
|
1182
1225
|
await git.add(file);
|
|
1183
|
-
console.log(
|
|
1226
|
+
console.log(chalk7.green(`\u2713 Resolved ${file}`));
|
|
1184
1227
|
} else if (answer.toLowerCase() === "s") {
|
|
1185
|
-
console.log(
|
|
1228
|
+
console.log(chalk7.yellow(`\u23ED Skipped ${file}`));
|
|
1186
1229
|
} else {
|
|
1187
|
-
console.log(
|
|
1230
|
+
console.log(chalk7.yellow(`\u2717 Rejected - resolve manually: ${file}`));
|
|
1188
1231
|
}
|
|
1189
1232
|
} catch (error) {
|
|
1190
1233
|
spinner.fail("AI resolution failed");
|
|
1191
|
-
console.error(
|
|
1192
|
-
console.log(
|
|
1234
|
+
console.error(chalk7.red(error instanceof Error ? error.message : "Unknown error"));
|
|
1235
|
+
console.log(chalk7.yellow(`Please resolve manually: ${file}`));
|
|
1193
1236
|
}
|
|
1194
1237
|
}
|
|
1195
1238
|
const finalStatus = await git.status();
|
|
1196
1239
|
if (finalStatus.conflicted.length > 0) {
|
|
1197
|
-
console.log(
|
|
1240
|
+
console.log(chalk7.yellow(`
|
|
1198
1241
|
\u26A0 ${finalStatus.conflicted.length} conflict(s) remaining`));
|
|
1199
|
-
console.log(
|
|
1242
|
+
console.log(chalk7.gray("Resolve manually and run: git add <files> && git commit"));
|
|
1200
1243
|
} else if (options.commit !== false) {
|
|
1201
1244
|
await git.commit(`Merge branch '${branch}' into ${currentBranch}`);
|
|
1202
|
-
console.log(
|
|
1245
|
+
console.log(chalk7.green("\n\u2713 All conflicts resolved and committed"));
|
|
1203
1246
|
} else {
|
|
1204
|
-
console.log(
|
|
1205
|
-
console.log(
|
|
1247
|
+
console.log(chalk7.green("\n\u2713 All conflicts resolved"));
|
|
1248
|
+
console.log(chalk7.gray("Run: git commit"));
|
|
1206
1249
|
}
|
|
1207
1250
|
});
|
|
1208
1251
|
|
|
1209
1252
|
// src/commands/changelog.ts
|
|
1210
1253
|
import { Command as Command7 } from "commander";
|
|
1211
|
-
import
|
|
1254
|
+
import chalk8 from "chalk";
|
|
1212
1255
|
import ora6 from "ora";
|
|
1213
1256
|
import { simpleGit as simpleGit6 } from "simple-git";
|
|
1214
1257
|
function formatChangelog(changelog) {
|
|
@@ -1235,7 +1278,7 @@ var changelogCommand = new Command7("changelog").description("Generate a changel
|
|
|
1235
1278
|
const git = simpleGit6();
|
|
1236
1279
|
const isRepo = await git.checkIsRepo();
|
|
1237
1280
|
if (!isRepo) {
|
|
1238
|
-
console.error(
|
|
1281
|
+
console.error(chalk8.red("Error: Not a git repository"));
|
|
1239
1282
|
process.exit(1);
|
|
1240
1283
|
}
|
|
1241
1284
|
const provider = options.provider.toLowerCase();
|
|
@@ -1275,34 +1318,34 @@ var changelogCommand = new Command7("changelog").description("Generate a changel
|
|
|
1275
1318
|
console.log(JSON.stringify(changelog, null, 2));
|
|
1276
1319
|
return;
|
|
1277
1320
|
}
|
|
1278
|
-
console.log(
|
|
1279
|
-
console.log(
|
|
1321
|
+
console.log(chalk8.bold("\n\u{1F4CB} Generated Changelog\n"));
|
|
1322
|
+
console.log(chalk8.gray("\u2500".repeat(50)));
|
|
1280
1323
|
console.log(formatChangelog(changelog));
|
|
1281
|
-
console.log(
|
|
1282
|
-
console.log(
|
|
1324
|
+
console.log(chalk8.gray("\u2500".repeat(50)));
|
|
1325
|
+
console.log(chalk8.gray(`
|
|
1283
1326
|
Range: ${fromRef}..${toRef} (${commits.length} commits)`));
|
|
1284
1327
|
if (template) {
|
|
1285
|
-
console.log(
|
|
1328
|
+
console.log(chalk8.gray("Style matched from existing CHANGELOG.md"));
|
|
1286
1329
|
}
|
|
1287
1330
|
} catch (error) {
|
|
1288
1331
|
spinner.fail("Failed to generate changelog");
|
|
1289
|
-
console.error(
|
|
1332
|
+
console.error(chalk8.red(error instanceof Error ? error.message : "Unknown error"));
|
|
1290
1333
|
process.exit(1);
|
|
1291
1334
|
}
|
|
1292
1335
|
});
|
|
1293
1336
|
|
|
1294
1337
|
// src/commands/explain.ts
|
|
1295
1338
|
import { Command as Command8 } from "commander";
|
|
1296
|
-
import
|
|
1339
|
+
import chalk9 from "chalk";
|
|
1297
1340
|
import ora7 from "ora";
|
|
1298
1341
|
import { simpleGit as simpleGit7 } from "simple-git";
|
|
1299
|
-
import { execSync as
|
|
1342
|
+
import { execSync as execSync5 } from "child_process";
|
|
1300
1343
|
import { existsSync as existsSync4, readFileSync as readFileSync5 } from "fs";
|
|
1301
1344
|
var explainCommand = new Command8("explain").description("Get an AI-powered explanation of changes, commits, PRs, or files").argument("[target]", "Commit hash, PR number, PR URL, or file path (default: uncommitted changes)").option("-p, --provider <provider>", "AI provider (gemini, openai, anthropic)", "gemini").option("-m, --model <model>", "Model to use (provider-specific)").option("-s, --staged", "Explain only staged changes").option("-n, --commits <n>", "Number of commits to analyze for file history (default: 1)", "1").option("--history", "Explain file change history instead of content").option("--json", "Output as JSON").action(async (target, options) => {
|
|
1302
1345
|
const git = simpleGit7();
|
|
1303
1346
|
const isRepo = await git.checkIsRepo();
|
|
1304
1347
|
if (!isRepo) {
|
|
1305
|
-
console.error(
|
|
1348
|
+
console.error(chalk9.red("Error: Not a git repository"));
|
|
1306
1349
|
process.exit(1);
|
|
1307
1350
|
}
|
|
1308
1351
|
const provider = options.provider.toLowerCase();
|
|
@@ -1320,6 +1363,11 @@ var explainCommand = new Command8("explain").description("Get an AI-powered expl
|
|
|
1320
1363
|
const isPR = target.match(/^#?\d+$/) || target.includes("/pull/");
|
|
1321
1364
|
const isFile = existsSync4(target);
|
|
1322
1365
|
if (isPR) {
|
|
1366
|
+
spinner.stop();
|
|
1367
|
+
if (!requireGhCli()) {
|
|
1368
|
+
process.exit(1);
|
|
1369
|
+
}
|
|
1370
|
+
spinner.start();
|
|
1323
1371
|
context = await getPRContext(target, spinner);
|
|
1324
1372
|
} else if (isFile) {
|
|
1325
1373
|
if (options.history) {
|
|
@@ -1335,7 +1383,7 @@ var explainCommand = new Command8("explain").description("Get an AI-powered expl
|
|
|
1335
1383
|
const templateName = isFileContent ? "explain-file" : "explain";
|
|
1336
1384
|
const template = findTemplate(repoRoot.trim(), templateName);
|
|
1337
1385
|
if (template) {
|
|
1338
|
-
console.log(
|
|
1386
|
+
console.log(chalk9.gray("Using template from project..."));
|
|
1339
1387
|
}
|
|
1340
1388
|
spinner.text = "AI is generating explanation...";
|
|
1341
1389
|
const explanation = await generateExplanation(
|
|
@@ -1351,7 +1399,7 @@ var explainCommand = new Command8("explain").description("Get an AI-powered expl
|
|
|
1351
1399
|
printExplanation(explanation, context.type);
|
|
1352
1400
|
} catch (error) {
|
|
1353
1401
|
spinner.fail("Failed to generate explanation");
|
|
1354
|
-
console.error(
|
|
1402
|
+
console.error(chalk9.red(error instanceof Error ? error.message : "Unknown error"));
|
|
1355
1403
|
process.exit(1);
|
|
1356
1404
|
}
|
|
1357
1405
|
});
|
|
@@ -1455,7 +1503,7 @@ async function getPRContext(target, spinner) {
|
|
|
1455
1503
|
spinner.text = `Fetching PR #${prNumber}...`;
|
|
1456
1504
|
let prInfo;
|
|
1457
1505
|
try {
|
|
1458
|
-
const prJson =
|
|
1506
|
+
const prJson = execSync5(
|
|
1459
1507
|
`gh pr view ${prNumber} --json title,url,baseRefName,headRefName,commits`,
|
|
1460
1508
|
{ encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
|
|
1461
1509
|
);
|
|
@@ -1466,7 +1514,7 @@ async function getPRContext(target, spinner) {
|
|
|
1466
1514
|
spinner.text = `Getting diff for PR #${prNumber}...`;
|
|
1467
1515
|
let diff;
|
|
1468
1516
|
try {
|
|
1469
|
-
diff =
|
|
1517
|
+
diff = execSync5(`gh pr diff ${prNumber}`, {
|
|
1470
1518
|
encoding: "utf-8",
|
|
1471
1519
|
stdio: ["pipe", "pipe", "pipe"],
|
|
1472
1520
|
maxBuffer: 10 * 1024 * 1024
|
|
@@ -1498,31 +1546,31 @@ function printExplanation(explanation, type) {
|
|
|
1498
1546
|
staged: "\u{1F4CB}"
|
|
1499
1547
|
};
|
|
1500
1548
|
const icon = icons[type] || "\u{1F4DD}";
|
|
1501
|
-
console.log(
|
|
1549
|
+
console.log(chalk9.bold(`
|
|
1502
1550
|
${icon} Explanation
|
|
1503
1551
|
`));
|
|
1504
|
-
console.log(
|
|
1552
|
+
console.log(chalk9.cyan("Summary:"));
|
|
1505
1553
|
console.log(` ${explanation.summary}
|
|
1506
1554
|
`);
|
|
1507
|
-
console.log(
|
|
1555
|
+
console.log(chalk9.cyan("Purpose:"));
|
|
1508
1556
|
console.log(` ${explanation.purpose}
|
|
1509
1557
|
`);
|
|
1510
1558
|
if (explanation.changes.length > 0) {
|
|
1511
1559
|
const header = type === "file-content" ? "Components:" : "Key Changes:";
|
|
1512
|
-
console.log(
|
|
1560
|
+
console.log(chalk9.cyan(header));
|
|
1513
1561
|
for (const change of explanation.changes) {
|
|
1514
|
-
console.log(` ${
|
|
1515
|
-
console.log(` ${
|
|
1562
|
+
console.log(` ${chalk9.yellow(change.file)}`);
|
|
1563
|
+
console.log(` ${chalk9.gray(change.description)}`);
|
|
1516
1564
|
}
|
|
1517
1565
|
console.log();
|
|
1518
1566
|
}
|
|
1519
|
-
console.log(
|
|
1567
|
+
console.log(chalk9.cyan("Impact:"));
|
|
1520
1568
|
console.log(` ${explanation.impact}
|
|
1521
1569
|
`);
|
|
1522
1570
|
if (explanation.notes && explanation.notes.length > 0) {
|
|
1523
|
-
console.log(
|
|
1571
|
+
console.log(chalk9.cyan("Notes:"));
|
|
1524
1572
|
for (const note of explanation.notes) {
|
|
1525
|
-
console.log(` ${
|
|
1573
|
+
console.log(` ${chalk9.gray("\u2022")} ${note}`);
|
|
1526
1574
|
}
|
|
1527
1575
|
console.log();
|
|
1528
1576
|
}
|
|
@@ -1530,14 +1578,14 @@ ${icon} Explanation
|
|
|
1530
1578
|
|
|
1531
1579
|
// src/commands/find.ts
|
|
1532
1580
|
import { Command as Command9 } from "commander";
|
|
1533
|
-
import
|
|
1581
|
+
import chalk10 from "chalk";
|
|
1534
1582
|
import ora8 from "ora";
|
|
1535
1583
|
import { simpleGit as simpleGit8 } from "simple-git";
|
|
1536
1584
|
var findCommand = new Command9("find").description("Find commits matching a vague description using AI").argument("<query>", 'Description of the change you are looking for (e.g., "login feature added")').option("-p, --provider <provider>", "AI provider (gemini, openai, anthropic)", "gemini").option("-m, --model <model>", "Model to use (provider-specific)").option("-n, --num <n>", "Number of commits to search through", "100").option("--path <path>", "Limit search to commits affecting this path").option("--author <author>", "Limit search to commits by this author").option("--since <date>", "Limit search to commits after this date").option("--until <date>", "Limit search to commits before this date").option("--max-results <n>", "Maximum number of matching commits to return", "5").option("--json", "Output as JSON").action(async (query, options) => {
|
|
1537
1585
|
const git = simpleGit8();
|
|
1538
1586
|
const isRepo = await git.checkIsRepo();
|
|
1539
1587
|
if (!isRepo) {
|
|
1540
|
-
console.error(
|
|
1588
|
+
console.error(chalk10.red("Error: Not a git repository"));
|
|
1541
1589
|
process.exit(1);
|
|
1542
1590
|
}
|
|
1543
1591
|
const provider = options.provider.toLowerCase();
|
|
@@ -1581,8 +1629,8 @@ var findCommand = new Command9("find").description("Find commits matching a vagu
|
|
|
1581
1629
|
);
|
|
1582
1630
|
spinner.stop();
|
|
1583
1631
|
if (results.matches.length === 0) {
|
|
1584
|
-
console.log(
|
|
1585
|
-
console.log(
|
|
1632
|
+
console.log(chalk10.yellow("\nNo matching commits found for your query."));
|
|
1633
|
+
console.log(chalk10.gray(`Searched ${commits.length} commits.`));
|
|
1586
1634
|
process.exit(0);
|
|
1587
1635
|
}
|
|
1588
1636
|
if (options.json) {
|
|
@@ -1592,46 +1640,46 @@ var findCommand = new Command9("find").description("Find commits matching a vagu
|
|
|
1592
1640
|
printResults(results, query);
|
|
1593
1641
|
} catch (error) {
|
|
1594
1642
|
spinner.fail("Failed to search commits");
|
|
1595
|
-
console.error(
|
|
1643
|
+
console.error(chalk10.red(error instanceof Error ? error.message : "Unknown error"));
|
|
1596
1644
|
process.exit(1);
|
|
1597
1645
|
}
|
|
1598
1646
|
});
|
|
1599
1647
|
function printResults(results, query) {
|
|
1600
|
-
console.log(
|
|
1648
|
+
console.log(chalk10.bold(`
|
|
1601
1649
|
\u{1F50D} Found ${results.matches.length} matching commit(s)
|
|
1602
1650
|
`));
|
|
1603
|
-
console.log(
|
|
1651
|
+
console.log(chalk10.gray(`Query: "${query}"
|
|
1604
1652
|
`));
|
|
1605
1653
|
for (let i = 0; i < results.matches.length; i++) {
|
|
1606
1654
|
const match = results.matches[i];
|
|
1607
1655
|
const num = i + 1;
|
|
1608
|
-
console.log(
|
|
1609
|
-
console.log(` ${
|
|
1610
|
-
console.log(` ${
|
|
1611
|
-
console.log(` ${
|
|
1612
|
-
console.log(` ${
|
|
1613
|
-
console.log(` ${
|
|
1656
|
+
console.log(chalk10.cyan(`\u{1F4DD} Commit ${num}`));
|
|
1657
|
+
console.log(` ${chalk10.gray("Hash:")} ${chalk10.yellow(match.hash.slice(0, 7))}`);
|
|
1658
|
+
console.log(` ${chalk10.gray("Message:")} ${match.message.split("\n")[0]}`);
|
|
1659
|
+
console.log(` ${chalk10.gray("Author:")} ${match.author} <${match.email}>`);
|
|
1660
|
+
console.log(` ${chalk10.gray("Date:")} ${match.date}`);
|
|
1661
|
+
console.log(` ${chalk10.gray("Reason:")} ${chalk10.green(match.reason)}`);
|
|
1614
1662
|
if (match.relevance) {
|
|
1615
|
-
const relevanceColor = match.relevance === "high" ?
|
|
1616
|
-
console.log(` ${
|
|
1663
|
+
const relevanceColor = match.relevance === "high" ? chalk10.green : match.relevance === "medium" ? chalk10.yellow : chalk10.gray;
|
|
1664
|
+
console.log(` ${chalk10.gray("Match:")} ${relevanceColor(match.relevance)}`);
|
|
1617
1665
|
}
|
|
1618
1666
|
console.log();
|
|
1619
1667
|
}
|
|
1620
1668
|
if (results.summary) {
|
|
1621
|
-
console.log(
|
|
1622
|
-
console.log(
|
|
1669
|
+
console.log(chalk10.gray("---"));
|
|
1670
|
+
console.log(chalk10.gray(`Summary: ${results.summary}`));
|
|
1623
1671
|
}
|
|
1624
1672
|
}
|
|
1625
1673
|
|
|
1626
1674
|
// src/commands/branch.ts
|
|
1627
1675
|
import { Command as Command10 } from "commander";
|
|
1628
|
-
import
|
|
1676
|
+
import chalk11 from "chalk";
|
|
1629
1677
|
import ora9 from "ora";
|
|
1630
1678
|
import { simpleGit as simpleGit9 } from "simple-git";
|
|
1631
|
-
import { execSync as
|
|
1679
|
+
import { execSync as execSync6 } from "child_process";
|
|
1632
1680
|
function getIssueInfo(issueNumber) {
|
|
1633
1681
|
try {
|
|
1634
|
-
const result =
|
|
1682
|
+
const result = execSync6(`gh issue view ${issueNumber} --json title,body`, {
|
|
1635
1683
|
encoding: "utf-8",
|
|
1636
1684
|
stdio: ["pipe", "pipe", "pipe"]
|
|
1637
1685
|
});
|
|
@@ -1645,7 +1693,7 @@ var branchCommand = new Command10("branch").description("Generate a branch name
|
|
|
1645
1693
|
const repoRoot = await git.revparse(["--show-toplevel"]).catch(() => process.cwd());
|
|
1646
1694
|
const isRepo = await git.checkIsRepo();
|
|
1647
1695
|
if (!isRepo) {
|
|
1648
|
-
console.error(
|
|
1696
|
+
console.error(chalk11.red("Error: Not a git repository"));
|
|
1649
1697
|
process.exit(1);
|
|
1650
1698
|
}
|
|
1651
1699
|
let description;
|
|
@@ -1653,30 +1701,33 @@ var branchCommand = new Command10("branch").description("Generate a branch name
|
|
|
1653
1701
|
if (options.description) {
|
|
1654
1702
|
description = options.description;
|
|
1655
1703
|
} else if (issue) {
|
|
1704
|
+
if (!requireGhCli()) {
|
|
1705
|
+
process.exit(1);
|
|
1706
|
+
}
|
|
1656
1707
|
issueNumber = issue.replace(/^#/, "");
|
|
1657
1708
|
const spinner2 = ora9(`Fetching issue #${issueNumber}...`).start();
|
|
1658
1709
|
const issueInfo = getIssueInfo(issueNumber);
|
|
1659
1710
|
if (!issueInfo) {
|
|
1660
1711
|
spinner2.fail(`Could not fetch issue #${issueNumber}`);
|
|
1661
|
-
console.log(
|
|
1712
|
+
console.log(chalk11.gray("Make sure you are authenticated: gh auth login"));
|
|
1662
1713
|
process.exit(1);
|
|
1663
1714
|
}
|
|
1664
1715
|
spinner2.stop();
|
|
1665
|
-
console.log(
|
|
1716
|
+
console.log(chalk11.gray(`Issue: ${issueInfo.title}`));
|
|
1666
1717
|
description = `${issueInfo.title}
|
|
1667
1718
|
|
|
1668
1719
|
${issueInfo.body || ""}`;
|
|
1669
1720
|
} else {
|
|
1670
|
-
console.error(
|
|
1671
|
-
console.log(
|
|
1672
|
-
console.log(
|
|
1673
|
-
console.log(
|
|
1721
|
+
console.error(chalk11.red("Error: Please provide an issue number or use -d for description"));
|
|
1722
|
+
console.log(chalk11.gray("Usage:"));
|
|
1723
|
+
console.log(chalk11.gray(" gut branch 123"));
|
|
1724
|
+
console.log(chalk11.gray(' gut branch -d "add user authentication"'));
|
|
1674
1725
|
process.exit(1);
|
|
1675
1726
|
}
|
|
1676
1727
|
const provider = options.provider.toLowerCase();
|
|
1677
1728
|
const template = findTemplate(repoRoot.trim(), "branch");
|
|
1678
1729
|
if (template) {
|
|
1679
|
-
console.log(
|
|
1730
|
+
console.log(chalk11.gray("Using template from project..."));
|
|
1680
1731
|
}
|
|
1681
1732
|
const spinner = ora9("Generating branch name...").start();
|
|
1682
1733
|
try {
|
|
@@ -1687,12 +1738,12 @@ ${issueInfo.body || ""}`;
|
|
|
1687
1738
|
template || void 0
|
|
1688
1739
|
);
|
|
1689
1740
|
spinner.stop();
|
|
1690
|
-
console.log(
|
|
1691
|
-
console.log(
|
|
1741
|
+
console.log(chalk11.bold("\nGenerated branch name:\n"));
|
|
1742
|
+
console.log(chalk11.green(` ${branchName}`));
|
|
1692
1743
|
console.log();
|
|
1693
1744
|
if (options.checkout) {
|
|
1694
1745
|
await git.checkoutLocalBranch(branchName);
|
|
1695
|
-
console.log(
|
|
1746
|
+
console.log(chalk11.green(`\u2713 Created and checked out branch: ${branchName}`));
|
|
1696
1747
|
} else {
|
|
1697
1748
|
const readline = await import("readline");
|
|
1698
1749
|
const rl = readline.createInterface({
|
|
@@ -1700,34 +1751,34 @@ ${issueInfo.body || ""}`;
|
|
|
1700
1751
|
output: process.stdout
|
|
1701
1752
|
});
|
|
1702
1753
|
const answer = await new Promise((resolve) => {
|
|
1703
|
-
rl.question(
|
|
1754
|
+
rl.question(chalk11.cyan("Create and checkout this branch? (y/N) "), resolve);
|
|
1704
1755
|
});
|
|
1705
1756
|
rl.close();
|
|
1706
1757
|
if (answer.toLowerCase() === "y") {
|
|
1707
1758
|
await git.checkoutLocalBranch(branchName);
|
|
1708
|
-
console.log(
|
|
1759
|
+
console.log(chalk11.green(`\u2713 Created and checked out branch: ${branchName}`));
|
|
1709
1760
|
} else {
|
|
1710
|
-
console.log(
|
|
1711
|
-
console.log(
|
|
1761
|
+
console.log(chalk11.gray("\nTo create manually:"));
|
|
1762
|
+
console.log(chalk11.gray(` git checkout -b ${branchName}`));
|
|
1712
1763
|
}
|
|
1713
1764
|
}
|
|
1714
1765
|
} catch (error) {
|
|
1715
1766
|
spinner.fail("Failed to generate branch name");
|
|
1716
|
-
console.error(
|
|
1767
|
+
console.error(chalk11.red(error instanceof Error ? error.message : "Unknown error"));
|
|
1717
1768
|
process.exit(1);
|
|
1718
1769
|
}
|
|
1719
1770
|
});
|
|
1720
1771
|
|
|
1721
1772
|
// src/commands/checkout.ts
|
|
1722
1773
|
import { Command as Command11 } from "commander";
|
|
1723
|
-
import
|
|
1774
|
+
import chalk12 from "chalk";
|
|
1724
1775
|
import ora10 from "ora";
|
|
1725
1776
|
import { simpleGit as simpleGit10 } from "simple-git";
|
|
1726
1777
|
var checkoutCommand = new Command11("checkout").description("Generate a branch name from current diff and checkout").option("-p, --provider <provider>", "AI provider (gemini, openai, anthropic)", "gemini").option("-m, --model <model>", "Model to use (provider-specific)").option("-y, --yes", "Skip confirmation and checkout directly").option("-s, --staged", "Use staged changes only instead of all changes").action(async (options) => {
|
|
1727
1778
|
const git = simpleGit10();
|
|
1728
1779
|
const isRepo = await git.checkIsRepo();
|
|
1729
1780
|
if (!isRepo) {
|
|
1730
|
-
console.error(
|
|
1781
|
+
console.error(chalk12.red("Error: Not a git repository"));
|
|
1731
1782
|
process.exit(1);
|
|
1732
1783
|
}
|
|
1733
1784
|
const repoRoot = await git.revparse(["--show-toplevel"]).catch(() => process.cwd());
|
|
@@ -1744,7 +1795,7 @@ var checkoutCommand = new Command11("checkout").description("Generate a branch n
|
|
|
1744
1795
|
const hasChanges = diff.trim() || status.not_added.length > 0 || status.created.length > 0;
|
|
1745
1796
|
if (!hasChanges) {
|
|
1746
1797
|
spinner.fail("No changes found");
|
|
1747
|
-
console.log(
|
|
1798
|
+
console.log(chalk12.gray("Make some changes first, then run gut checkout"));
|
|
1748
1799
|
process.exit(1);
|
|
1749
1800
|
}
|
|
1750
1801
|
if (!diff.trim() && (status.not_added.length > 0 || status.created.length > 0)) {
|
|
@@ -1756,7 +1807,7 @@ ${untrackedFiles.map((f) => `+ ${f}`).join("\n")}`;
|
|
|
1756
1807
|
const provider = options.provider.toLowerCase();
|
|
1757
1808
|
const template = findTemplate(repoRoot.trim(), "checkout");
|
|
1758
1809
|
if (template) {
|
|
1759
|
-
console.log(
|
|
1810
|
+
console.log(chalk12.gray("\nUsing template from project..."));
|
|
1760
1811
|
}
|
|
1761
1812
|
try {
|
|
1762
1813
|
const branchName = await generateBranchNameFromDiff(
|
|
@@ -1765,12 +1816,12 @@ ${untrackedFiles.map((f) => `+ ${f}`).join("\n")}`;
|
|
|
1765
1816
|
template
|
|
1766
1817
|
);
|
|
1767
1818
|
spinner.stop();
|
|
1768
|
-
console.log(
|
|
1769
|
-
console.log(
|
|
1819
|
+
console.log(chalk12.bold("\nGenerated branch name:\n"));
|
|
1820
|
+
console.log(chalk12.green(` ${branchName}`));
|
|
1770
1821
|
console.log();
|
|
1771
1822
|
if (options.yes) {
|
|
1772
1823
|
await git.checkoutLocalBranch(branchName);
|
|
1773
|
-
console.log(
|
|
1824
|
+
console.log(chalk12.green(`\u2713 Created and checked out branch: ${branchName}`));
|
|
1774
1825
|
} else {
|
|
1775
1826
|
const readline = await import("readline");
|
|
1776
1827
|
const rl = readline.createInterface({
|
|
@@ -1778,34 +1829,34 @@ ${untrackedFiles.map((f) => `+ ${f}`).join("\n")}`;
|
|
|
1778
1829
|
output: process.stdout
|
|
1779
1830
|
});
|
|
1780
1831
|
const answer = await new Promise((resolve) => {
|
|
1781
|
-
rl.question(
|
|
1832
|
+
rl.question(chalk12.cyan("Create and checkout this branch? (y/N) "), resolve);
|
|
1782
1833
|
});
|
|
1783
1834
|
rl.close();
|
|
1784
1835
|
if (answer.toLowerCase() === "y") {
|
|
1785
1836
|
await git.checkoutLocalBranch(branchName);
|
|
1786
|
-
console.log(
|
|
1837
|
+
console.log(chalk12.green(`\u2713 Created and checked out branch: ${branchName}`));
|
|
1787
1838
|
} else {
|
|
1788
|
-
console.log(
|
|
1789
|
-
console.log(
|
|
1839
|
+
console.log(chalk12.gray("\nTo create manually:"));
|
|
1840
|
+
console.log(chalk12.gray(` git checkout -b ${branchName}`));
|
|
1790
1841
|
}
|
|
1791
1842
|
}
|
|
1792
1843
|
} catch (error) {
|
|
1793
1844
|
spinner.fail("Failed to generate branch name");
|
|
1794
|
-
console.error(
|
|
1845
|
+
console.error(chalk12.red(error instanceof Error ? error.message : "Unknown error"));
|
|
1795
1846
|
process.exit(1);
|
|
1796
1847
|
}
|
|
1797
1848
|
});
|
|
1798
1849
|
|
|
1799
1850
|
// src/commands/sync.ts
|
|
1800
1851
|
import { Command as Command12 } from "commander";
|
|
1801
|
-
import
|
|
1852
|
+
import chalk13 from "chalk";
|
|
1802
1853
|
import ora11 from "ora";
|
|
1803
1854
|
import { simpleGit as simpleGit11 } from "simple-git";
|
|
1804
1855
|
var syncCommand = new Command12("sync").description("Sync current branch with remote (fetch + rebase/merge)").option("-m, --merge", "Use merge instead of rebase").option("--no-push", "Skip push after syncing").option("--stash", "Auto-stash changes before sync").option("-f, --force", "Force sync even with uncommitted changes").action(async (options) => {
|
|
1805
1856
|
const git = simpleGit11();
|
|
1806
1857
|
const isRepo = await git.checkIsRepo();
|
|
1807
1858
|
if (!isRepo) {
|
|
1808
|
-
console.error(
|
|
1859
|
+
console.error(chalk13.red("Error: Not a git repository"));
|
|
1809
1860
|
process.exit(1);
|
|
1810
1861
|
}
|
|
1811
1862
|
const spinner = ora11("Checking repository status...").start();
|
|
@@ -1814,15 +1865,15 @@ var syncCommand = new Command12("sync").description("Sync current branch with re
|
|
|
1814
1865
|
const hasChanges = !status.isClean();
|
|
1815
1866
|
if (hasChanges && !options.stash && !options.force) {
|
|
1816
1867
|
spinner.stop();
|
|
1817
|
-
console.log(
|
|
1868
|
+
console.log(chalk13.yellow("You have uncommitted changes:"));
|
|
1818
1869
|
if (status.modified.length > 0) {
|
|
1819
|
-
console.log(
|
|
1870
|
+
console.log(chalk13.gray(` Modified: ${status.modified.length} file(s)`));
|
|
1820
1871
|
}
|
|
1821
1872
|
if (status.not_added.length > 0) {
|
|
1822
|
-
console.log(
|
|
1873
|
+
console.log(chalk13.gray(` Untracked: ${status.not_added.length} file(s)`));
|
|
1823
1874
|
}
|
|
1824
1875
|
console.log();
|
|
1825
|
-
console.log(
|
|
1876
|
+
console.log(chalk13.gray("Use --stash to auto-stash, or --force to sync anyway"));
|
|
1826
1877
|
process.exit(1);
|
|
1827
1878
|
}
|
|
1828
1879
|
let stashed = false;
|
|
@@ -1841,11 +1892,11 @@ var syncCommand = new Command12("sync").description("Sync current branch with re
|
|
|
1841
1892
|
const trackingBranch = status.tracking;
|
|
1842
1893
|
if (!trackingBranch) {
|
|
1843
1894
|
spinner.warn(`Branch ${currentBranch} has no upstream tracking branch`);
|
|
1844
|
-
console.log(
|
|
1895
|
+
console.log(chalk13.gray(`
|
|
1845
1896
|
To set upstream: git push -u origin ${currentBranch}`));
|
|
1846
1897
|
if (stashed) {
|
|
1847
1898
|
await git.stash(["pop"]);
|
|
1848
|
-
console.log(
|
|
1899
|
+
console.log(chalk13.gray("Restored stashed changes"));
|
|
1849
1900
|
}
|
|
1850
1901
|
return;
|
|
1851
1902
|
}
|
|
@@ -1859,36 +1910,36 @@ To set upstream: git push -u origin ${currentBranch}`));
|
|
|
1859
1910
|
}
|
|
1860
1911
|
} catch (error) {
|
|
1861
1912
|
spinner.fail(`${strategy} failed - you may have conflicts`);
|
|
1862
|
-
console.log(
|
|
1913
|
+
console.log(chalk13.yellow("\nResolve conflicts and then:"));
|
|
1863
1914
|
if (options.merge) {
|
|
1864
|
-
console.log(
|
|
1915
|
+
console.log(chalk13.gray(" git add . && git commit"));
|
|
1865
1916
|
} else {
|
|
1866
|
-
console.log(
|
|
1917
|
+
console.log(chalk13.gray(" git add . && git rebase --continue"));
|
|
1867
1918
|
}
|
|
1868
1919
|
if (stashed) {
|
|
1869
|
-
console.log(
|
|
1920
|
+
console.log(chalk13.yellow("\nNote: You have stashed changes. Run `git stash pop` after resolving."));
|
|
1870
1921
|
}
|
|
1871
1922
|
process.exit(1);
|
|
1872
1923
|
}
|
|
1873
1924
|
const newStatus = await git.status();
|
|
1874
1925
|
const ahead = newStatus.ahead || 0;
|
|
1875
1926
|
const behind = newStatus.behind || 0;
|
|
1876
|
-
spinner.succeed(
|
|
1927
|
+
spinner.succeed(chalk13.green("Synced successfully"));
|
|
1877
1928
|
if (behind > 0) {
|
|
1878
|
-
console.log(
|
|
1929
|
+
console.log(chalk13.yellow(` \u2193 ${behind} commit(s) behind`));
|
|
1879
1930
|
}
|
|
1880
1931
|
if (ahead > 0) {
|
|
1881
1932
|
if (options.push !== false) {
|
|
1882
1933
|
const pushSpinner = ora11("Pushing to remote...").start();
|
|
1883
1934
|
try {
|
|
1884
1935
|
await git.push();
|
|
1885
|
-
pushSpinner.succeed(
|
|
1936
|
+
pushSpinner.succeed(chalk13.green(`Pushed ${ahead} commit(s)`));
|
|
1886
1937
|
} catch (error) {
|
|
1887
1938
|
pushSpinner.fail("Push failed");
|
|
1888
|
-
console.error(
|
|
1939
|
+
console.error(chalk13.red(error instanceof Error ? error.message : "Unknown error"));
|
|
1889
1940
|
}
|
|
1890
1941
|
} else {
|
|
1891
|
-
console.log(
|
|
1942
|
+
console.log(chalk13.cyan(` \u2191 ${ahead} commit(s) ahead`));
|
|
1892
1943
|
}
|
|
1893
1944
|
}
|
|
1894
1945
|
if (stashed) {
|
|
@@ -1898,37 +1949,37 @@ To set upstream: git push -u origin ${currentBranch}`));
|
|
|
1898
1949
|
spinner.succeed("Restored stashed changes");
|
|
1899
1950
|
} catch {
|
|
1900
1951
|
spinner.warn("Could not auto-restore stash (may have conflicts)");
|
|
1901
|
-
console.log(
|
|
1952
|
+
console.log(chalk13.gray(" Run `git stash pop` manually"));
|
|
1902
1953
|
}
|
|
1903
1954
|
}
|
|
1904
1955
|
} catch (error) {
|
|
1905
1956
|
spinner.fail("Sync failed");
|
|
1906
|
-
console.error(
|
|
1957
|
+
console.error(chalk13.red(error instanceof Error ? error.message : "Unknown error"));
|
|
1907
1958
|
process.exit(1);
|
|
1908
1959
|
}
|
|
1909
1960
|
});
|
|
1910
1961
|
|
|
1911
1962
|
// src/commands/stash.ts
|
|
1912
1963
|
import { Command as Command13 } from "commander";
|
|
1913
|
-
import
|
|
1964
|
+
import chalk14 from "chalk";
|
|
1914
1965
|
import ora12 from "ora";
|
|
1915
1966
|
import { simpleGit as simpleGit12 } from "simple-git";
|
|
1916
1967
|
var stashCommand = new Command13("stash").description("Stash changes with AI-generated name").argument("[name]", "Custom stash name (skips AI generation)").option("-p, --provider <provider>", "AI provider (gemini, openai, anthropic)", "gemini").option("-m, --model <model>", "Model to use (provider-specific)").option("-l, --list", "List all stashes").option("-a, --apply [index]", "Apply stash (default: latest)").option("--pop [index]", "Pop stash (default: latest)").option("-d, --drop [index]", "Drop stash").option("--clear", "Clear all stashes").action(async (name, options) => {
|
|
1917
1968
|
const git = simpleGit12();
|
|
1918
1969
|
const isRepo = await git.checkIsRepo();
|
|
1919
1970
|
if (!isRepo) {
|
|
1920
|
-
console.error(
|
|
1971
|
+
console.error(chalk14.red("Error: Not a git repository"));
|
|
1921
1972
|
process.exit(1);
|
|
1922
1973
|
}
|
|
1923
1974
|
if (options.list) {
|
|
1924
1975
|
const stashList = await git.stashList();
|
|
1925
1976
|
if (stashList.all.length === 0) {
|
|
1926
|
-
console.log(
|
|
1977
|
+
console.log(chalk14.gray("No stashes found"));
|
|
1927
1978
|
return;
|
|
1928
1979
|
}
|
|
1929
|
-
console.log(
|
|
1980
|
+
console.log(chalk14.bold("\nStashes:\n"));
|
|
1930
1981
|
stashList.all.forEach((stash, index) => {
|
|
1931
|
-
console.log(` ${
|
|
1982
|
+
console.log(` ${chalk14.cyan(index.toString())} ${stash.message}`);
|
|
1932
1983
|
});
|
|
1933
1984
|
console.log();
|
|
1934
1985
|
return;
|
|
@@ -1937,9 +1988,9 @@ var stashCommand = new Command13("stash").description("Stash changes with AI-gen
|
|
|
1937
1988
|
const index = typeof options.apply === "string" ? options.apply : "0";
|
|
1938
1989
|
try {
|
|
1939
1990
|
await git.stash(["apply", `stash@{${index}}`]);
|
|
1940
|
-
console.log(
|
|
1991
|
+
console.log(chalk14.green(`\u2713 Applied stash@{${index}}`));
|
|
1941
1992
|
} catch (error) {
|
|
1942
|
-
console.error(
|
|
1993
|
+
console.error(chalk14.red(`Failed to apply stash: ${error instanceof Error ? error.message : "Unknown error"}`));
|
|
1943
1994
|
process.exit(1);
|
|
1944
1995
|
}
|
|
1945
1996
|
return;
|
|
@@ -1948,9 +1999,9 @@ var stashCommand = new Command13("stash").description("Stash changes with AI-gen
|
|
|
1948
1999
|
const index = typeof options.pop === "string" ? options.pop : "0";
|
|
1949
2000
|
try {
|
|
1950
2001
|
await git.stash(["pop", `stash@{${index}}`]);
|
|
1951
|
-
console.log(
|
|
2002
|
+
console.log(chalk14.green(`\u2713 Popped stash@{${index}}`));
|
|
1952
2003
|
} catch (error) {
|
|
1953
|
-
console.error(
|
|
2004
|
+
console.error(chalk14.red(`Failed to pop stash: ${error instanceof Error ? error.message : "Unknown error"}`));
|
|
1954
2005
|
process.exit(1);
|
|
1955
2006
|
}
|
|
1956
2007
|
return;
|
|
@@ -1959,9 +2010,9 @@ var stashCommand = new Command13("stash").description("Stash changes with AI-gen
|
|
|
1959
2010
|
const index = typeof options.drop === "string" ? options.drop : "0";
|
|
1960
2011
|
try {
|
|
1961
2012
|
await git.stash(["drop", `stash@{${index}}`]);
|
|
1962
|
-
console.log(
|
|
2013
|
+
console.log(chalk14.green(`\u2713 Dropped stash@{${index}}`));
|
|
1963
2014
|
} catch (error) {
|
|
1964
|
-
console.error(
|
|
2015
|
+
console.error(chalk14.red(`Failed to drop stash: ${error instanceof Error ? error.message : "Unknown error"}`));
|
|
1965
2016
|
process.exit(1);
|
|
1966
2017
|
}
|
|
1967
2018
|
return;
|
|
@@ -1973,20 +2024,20 @@ var stashCommand = new Command13("stash").description("Stash changes with AI-gen
|
|
|
1973
2024
|
output: process.stdout
|
|
1974
2025
|
});
|
|
1975
2026
|
const answer = await new Promise((resolve) => {
|
|
1976
|
-
rl.question(
|
|
2027
|
+
rl.question(chalk14.yellow("Clear all stashes? This cannot be undone. (y/N) "), resolve);
|
|
1977
2028
|
});
|
|
1978
2029
|
rl.close();
|
|
1979
2030
|
if (answer.toLowerCase() === "y") {
|
|
1980
2031
|
await git.stash(["clear"]);
|
|
1981
|
-
console.log(
|
|
2032
|
+
console.log(chalk14.green("\u2713 Cleared all stashes"));
|
|
1982
2033
|
} else {
|
|
1983
|
-
console.log(
|
|
2034
|
+
console.log(chalk14.gray("Cancelled"));
|
|
1984
2035
|
}
|
|
1985
2036
|
return;
|
|
1986
2037
|
}
|
|
1987
2038
|
const status = await git.status();
|
|
1988
2039
|
if (status.isClean()) {
|
|
1989
|
-
console.log(
|
|
2040
|
+
console.log(chalk14.yellow("No changes to stash"));
|
|
1990
2041
|
return;
|
|
1991
2042
|
}
|
|
1992
2043
|
let stashName;
|
|
@@ -2013,19 +2064,19 @@ var stashCommand = new Command13("stash").description("Stash changes with AI-gen
|
|
|
2013
2064
|
}
|
|
2014
2065
|
}
|
|
2015
2066
|
await git.stash(["push", "-u", "-m", stashName]);
|
|
2016
|
-
console.log(
|
|
2067
|
+
console.log(chalk14.green(`\u2713 Stashed: ${stashName}`));
|
|
2017
2068
|
});
|
|
2018
2069
|
|
|
2019
2070
|
// src/commands/summary.ts
|
|
2020
2071
|
import { Command as Command14 } from "commander";
|
|
2021
|
-
import
|
|
2072
|
+
import chalk15 from "chalk";
|
|
2022
2073
|
import ora13 from "ora";
|
|
2023
2074
|
import { simpleGit as simpleGit13 } from "simple-git";
|
|
2024
2075
|
var summaryCommand = new Command14("summary").description("Generate a work summary from your commits (for daily/weekly reports)").option("-p, --provider <provider>", "AI provider (gemini, openai, anthropic)", "gemini").option("-m, --model <model>", "Model to use (provider-specific)").option("--since <date>", "Start date (default: today)", "today").option("--until <date>", "End date").option("--author <author>", "Filter by author (default: current user)").option("--daily", "Generate daily report (alias for --since today)").option("--weekly", 'Generate weekly report (alias for --since "1 week ago")').option("--with-diff", "Include diff analysis for more detail").option("--markdown", "Output as markdown").option("--json", "Output as JSON").option("--copy", "Copy to clipboard").action(async (options) => {
|
|
2025
2076
|
const git = simpleGit13();
|
|
2026
2077
|
const isRepo = await git.checkIsRepo();
|
|
2027
2078
|
if (!isRepo) {
|
|
2028
|
-
console.error(
|
|
2079
|
+
console.error(chalk15.red("Error: Not a git repository"));
|
|
2029
2080
|
process.exit(1);
|
|
2030
2081
|
}
|
|
2031
2082
|
const provider = options.provider.toLowerCase();
|
|
@@ -2093,13 +2144,13 @@ var summaryCommand = new Command14("summary").description("Generate a work summa
|
|
|
2093
2144
|
const output = options.markdown ? formatMarkdown(summary, author, since, options.until) : null;
|
|
2094
2145
|
if (options.copy) {
|
|
2095
2146
|
const textToCopy = output || formatMarkdown(summary, author, since, options.until);
|
|
2096
|
-
const { execSync:
|
|
2147
|
+
const { execSync: execSync7 } = await import("child_process");
|
|
2097
2148
|
try {
|
|
2098
|
-
|
|
2099
|
-
console.log(
|
|
2149
|
+
execSync7("pbcopy", { input: textToCopy });
|
|
2150
|
+
console.log(chalk15.green("Summary copied to clipboard!"));
|
|
2100
2151
|
console.log();
|
|
2101
2152
|
} catch {
|
|
2102
|
-
console.log(
|
|
2153
|
+
console.log(chalk15.yellow("Could not copy to clipboard"));
|
|
2103
2154
|
}
|
|
2104
2155
|
}
|
|
2105
2156
|
if (options.markdown) {
|
|
@@ -2109,7 +2160,7 @@ var summaryCommand = new Command14("summary").description("Generate a work summa
|
|
|
2109
2160
|
}
|
|
2110
2161
|
} catch (error) {
|
|
2111
2162
|
spinner.fail("Failed to generate summary");
|
|
2112
|
-
console.error(
|
|
2163
|
+
console.error(chalk15.red(error instanceof Error ? error.message : "Unknown error"));
|
|
2113
2164
|
process.exit(1);
|
|
2114
2165
|
}
|
|
2115
2166
|
});
|
|
@@ -2177,29 +2228,29 @@ function formatDate(d) {
|
|
|
2177
2228
|
}
|
|
2178
2229
|
function printSummary(summary, author, since, until) {
|
|
2179
2230
|
const period = until ? `${since} - ${until}` : `${since} - now`;
|
|
2180
|
-
console.log(
|
|
2231
|
+
console.log(chalk15.bold(`
|
|
2181
2232
|
\u{1F4CA} ${summary.title}
|
|
2182
2233
|
`));
|
|
2183
|
-
console.log(
|
|
2184
|
-
console.log(
|
|
2234
|
+
console.log(chalk15.gray(`Author: ${author}`));
|
|
2235
|
+
console.log(chalk15.gray(`Period: ${period}`));
|
|
2185
2236
|
if (summary.stats) {
|
|
2186
|
-
console.log(
|
|
2237
|
+
console.log(chalk15.gray(`Commits: ${summary.stats.commits}`));
|
|
2187
2238
|
}
|
|
2188
2239
|
console.log();
|
|
2189
|
-
console.log(
|
|
2240
|
+
console.log(chalk15.cyan("Overview:"));
|
|
2190
2241
|
console.log(` ${summary.overview}`);
|
|
2191
2242
|
console.log();
|
|
2192
2243
|
if (summary.highlights.length > 0) {
|
|
2193
|
-
console.log(
|
|
2244
|
+
console.log(chalk15.cyan("Highlights:"));
|
|
2194
2245
|
for (const highlight of summary.highlights) {
|
|
2195
|
-
console.log(` ${
|
|
2246
|
+
console.log(` ${chalk15.green("\u2605")} ${highlight}`);
|
|
2196
2247
|
}
|
|
2197
2248
|
console.log();
|
|
2198
2249
|
}
|
|
2199
2250
|
if (summary.details.length > 0) {
|
|
2200
|
-
console.log(
|
|
2251
|
+
console.log(chalk15.cyan("Details:"));
|
|
2201
2252
|
for (const section of summary.details) {
|
|
2202
|
-
console.log(` ${
|
|
2253
|
+
console.log(` ${chalk15.yellow(section.category)}`);
|
|
2203
2254
|
for (const item of section.items) {
|
|
2204
2255
|
console.log(` \u2022 ${item}`);
|
|
2205
2256
|
}
|
|
@@ -2210,26 +2261,26 @@ function printSummary(summary, author, since, until) {
|
|
|
2210
2261
|
|
|
2211
2262
|
// src/commands/config.ts
|
|
2212
2263
|
import { Command as Command15 } from "commander";
|
|
2213
|
-
import
|
|
2264
|
+
import chalk16 from "chalk";
|
|
2214
2265
|
var configCommand = new Command15("config").description("Manage gut configuration");
|
|
2215
2266
|
configCommand.command("set <key> <value>").description("Set a configuration value").option("--local", "Set for current repository only").action((key, value, options) => {
|
|
2216
2267
|
if (key === "lang") {
|
|
2217
2268
|
if (!isValidLanguage(value)) {
|
|
2218
|
-
console.error(
|
|
2219
|
-
console.error(
|
|
2269
|
+
console.error(chalk16.red(`Invalid language: ${value}`));
|
|
2270
|
+
console.error(chalk16.gray(`Valid languages: ${VALID_LANGUAGES.join(", ")}`));
|
|
2220
2271
|
process.exit(1);
|
|
2221
2272
|
}
|
|
2222
2273
|
try {
|
|
2223
2274
|
setLanguage(value, options.local ?? false);
|
|
2224
2275
|
const scope = options.local ? "(local)" : "(global)";
|
|
2225
|
-
console.log(
|
|
2276
|
+
console.log(chalk16.green(`\u2713 Language set to: ${value} ${scope}`));
|
|
2226
2277
|
} catch (err) {
|
|
2227
|
-
console.error(
|
|
2278
|
+
console.error(chalk16.red(err.message));
|
|
2228
2279
|
process.exit(1);
|
|
2229
2280
|
}
|
|
2230
2281
|
} else {
|
|
2231
|
-
console.error(
|
|
2232
|
-
console.error(
|
|
2282
|
+
console.error(chalk16.red(`Unknown config key: ${key}`));
|
|
2283
|
+
console.error(chalk16.gray("Available keys: lang"));
|
|
2233
2284
|
process.exit(1);
|
|
2234
2285
|
}
|
|
2235
2286
|
});
|
|
@@ -2238,7 +2289,7 @@ configCommand.command("get <key>").description("Get a configuration value").acti
|
|
|
2238
2289
|
if (key in config) {
|
|
2239
2290
|
console.log(config[key]);
|
|
2240
2291
|
} else {
|
|
2241
|
-
console.error(
|
|
2292
|
+
console.error(chalk16.red(`Unknown config key: ${key}`));
|
|
2242
2293
|
process.exit(1);
|
|
2243
2294
|
}
|
|
2244
2295
|
});
|
|
@@ -2246,49 +2297,188 @@ configCommand.command("list").description("List all configuration values").actio
|
|
|
2246
2297
|
const globalConfig = getGlobalConfig();
|
|
2247
2298
|
const localConfig = getLocalConfig();
|
|
2248
2299
|
const effectiveConfig = getConfig();
|
|
2249
|
-
console.log(
|
|
2300
|
+
console.log(chalk16.bold("Configuration:"));
|
|
2250
2301
|
console.log();
|
|
2251
2302
|
for (const key of Object.keys(effectiveConfig)) {
|
|
2252
2303
|
const value = effectiveConfig[key];
|
|
2253
2304
|
const isLocal = key in localConfig;
|
|
2254
|
-
const scope = isLocal ?
|
|
2255
|
-
console.log(` ${
|
|
2305
|
+
const scope = isLocal ? chalk16.cyan(" (local)") : chalk16.gray(" (global)");
|
|
2306
|
+
console.log(` ${chalk16.cyan(key)}: ${value}${scope}`);
|
|
2256
2307
|
}
|
|
2257
2308
|
if (Object.keys(localConfig).length > 0) {
|
|
2258
2309
|
console.log();
|
|
2259
|
-
console.log(
|
|
2310
|
+
console.log(chalk16.gray("Local config: .gut/config.json"));
|
|
2260
2311
|
}
|
|
2261
2312
|
});
|
|
2262
2313
|
|
|
2263
2314
|
// src/commands/lang.ts
|
|
2264
2315
|
import { Command as Command16 } from "commander";
|
|
2265
|
-
import
|
|
2316
|
+
import chalk17 from "chalk";
|
|
2266
2317
|
var langCommand = new Command16("lang").description("Set or show output language").argument("[language]", `Language to set (${VALID_LANGUAGES.join(", ")})`).option("--local", "Set for current repository only").action((language, options) => {
|
|
2267
2318
|
if (!language) {
|
|
2268
2319
|
const lang = getLanguage();
|
|
2269
2320
|
const localConfig = getLocalConfig();
|
|
2270
2321
|
const isLocal = "lang" in localConfig;
|
|
2271
|
-
const scope = isLocal ?
|
|
2322
|
+
const scope = isLocal ? chalk17.cyan("(local)") : chalk17.gray("(global)");
|
|
2272
2323
|
console.log(`${lang} ${scope}`);
|
|
2273
2324
|
return;
|
|
2274
2325
|
}
|
|
2275
2326
|
if (!isValidLanguage(language)) {
|
|
2276
|
-
console.error(
|
|
2277
|
-
console.error(
|
|
2327
|
+
console.error(chalk17.red(`Invalid language: ${language}`));
|
|
2328
|
+
console.error(chalk17.gray(`Valid languages: ${VALID_LANGUAGES.join(", ")}`));
|
|
2278
2329
|
process.exit(1);
|
|
2279
2330
|
}
|
|
2280
2331
|
try {
|
|
2281
2332
|
setLanguage(language, options.local ?? false);
|
|
2282
2333
|
const scope = options.local ? "(local)" : "(global)";
|
|
2283
|
-
console.log(
|
|
2334
|
+
console.log(chalk17.green(`\u2713 Language set to: ${language} ${scope}`));
|
|
2284
2335
|
} catch (err) {
|
|
2285
|
-
console.error(
|
|
2336
|
+
console.error(chalk17.red(err.message));
|
|
2286
2337
|
process.exit(1);
|
|
2287
2338
|
}
|
|
2288
2339
|
});
|
|
2289
2340
|
|
|
2341
|
+
// src/commands/init.ts
|
|
2342
|
+
import { Command as Command17 } from "commander";
|
|
2343
|
+
import chalk18 from "chalk";
|
|
2344
|
+
import ora14 from "ora";
|
|
2345
|
+
import { simpleGit as simpleGit14 } from "simple-git";
|
|
2346
|
+
import { existsSync as existsSync5, mkdirSync as mkdirSync2, readFileSync as readFileSync6, writeFileSync as writeFileSync3 } from "fs";
|
|
2347
|
+
import { join as join5, dirname as dirname2 } from "path";
|
|
2348
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
2349
|
+
import { generateText as generateText2 } from "ai";
|
|
2350
|
+
import { createGoogleGenerativeAI as createGoogleGenerativeAI2 } from "@ai-sdk/google";
|
|
2351
|
+
import { createOpenAI as createOpenAI2 } from "@ai-sdk/openai";
|
|
2352
|
+
import { createAnthropic as createAnthropic2 } from "@ai-sdk/anthropic";
|
|
2353
|
+
var __filename2 = fileURLToPath2(import.meta.url);
|
|
2354
|
+
var __dirname2 = dirname2(__filename2);
|
|
2355
|
+
var GUT_ROOT2 = join5(__dirname2, "..");
|
|
2356
|
+
var TEMPLATE_FILES = [
|
|
2357
|
+
"branch.md",
|
|
2358
|
+
"changelog.md",
|
|
2359
|
+
"checkout.md",
|
|
2360
|
+
"commit.md",
|
|
2361
|
+
"explain.md",
|
|
2362
|
+
"explain-file.md",
|
|
2363
|
+
"find.md",
|
|
2364
|
+
"merge.md",
|
|
2365
|
+
"pr.md",
|
|
2366
|
+
"review.md",
|
|
2367
|
+
"stash.md",
|
|
2368
|
+
"summary.md"
|
|
2369
|
+
];
|
|
2370
|
+
async function translateTemplate(content, targetLang, provider) {
|
|
2371
|
+
const apiKey = await getApiKey(provider);
|
|
2372
|
+
if (!apiKey) {
|
|
2373
|
+
throw new Error(`No API key found for ${provider}`);
|
|
2374
|
+
}
|
|
2375
|
+
const modelName = provider === "gemini" ? "gemini-2.0-flash" : provider === "openai" ? "gpt-4o-mini" : "claude-sonnet-4-20250514";
|
|
2376
|
+
let model;
|
|
2377
|
+
switch (provider) {
|
|
2378
|
+
case "gemini": {
|
|
2379
|
+
const google = createGoogleGenerativeAI2({ apiKey });
|
|
2380
|
+
model = google(modelName);
|
|
2381
|
+
break;
|
|
2382
|
+
}
|
|
2383
|
+
case "openai": {
|
|
2384
|
+
const openai = createOpenAI2({ apiKey });
|
|
2385
|
+
model = openai(modelName);
|
|
2386
|
+
break;
|
|
2387
|
+
}
|
|
2388
|
+
case "anthropic": {
|
|
2389
|
+
const anthropic = createAnthropic2({ apiKey });
|
|
2390
|
+
model = anthropic(modelName);
|
|
2391
|
+
break;
|
|
2392
|
+
}
|
|
2393
|
+
}
|
|
2394
|
+
const langNames = {
|
|
2395
|
+
ja: "Japanese",
|
|
2396
|
+
en: "English",
|
|
2397
|
+
zh: "Chinese",
|
|
2398
|
+
ko: "Korean",
|
|
2399
|
+
es: "Spanish",
|
|
2400
|
+
fr: "French",
|
|
2401
|
+
de: "German"
|
|
2402
|
+
};
|
|
2403
|
+
const targetLangName = langNames[targetLang] || targetLang;
|
|
2404
|
+
const { text } = await generateText2({
|
|
2405
|
+
model,
|
|
2406
|
+
prompt: `Translate the following prompt template to ${targetLangName}.
|
|
2407
|
+
Keep all {{variable}} placeholders exactly as they are - do not translate them.
|
|
2408
|
+
Keep the markdown formatting intact.
|
|
2409
|
+
Only translate the instructional text.
|
|
2410
|
+
|
|
2411
|
+
Template to translate:
|
|
2412
|
+
${content}
|
|
2413
|
+
|
|
2414
|
+
Translated template:`
|
|
2415
|
+
});
|
|
2416
|
+
return text.trim();
|
|
2417
|
+
}
|
|
2418
|
+
var initCommand = new Command17("init").description("Initialize .gut/ templates in your project").option("-p, --provider <provider>", "AI provider for translation (gemini, openai, anthropic)", "gemini").option("-f, --force", "Overwrite existing templates").option("--no-translate", "Skip translation even if language is not English").action(async (options) => {
|
|
2419
|
+
const git = simpleGit14();
|
|
2420
|
+
const isRepo = await git.checkIsRepo();
|
|
2421
|
+
if (!isRepo) {
|
|
2422
|
+
console.error(chalk18.red("Error: Not a git repository"));
|
|
2423
|
+
process.exit(1);
|
|
2424
|
+
}
|
|
2425
|
+
const repoRoot = await git.revparse(["--show-toplevel"]).catch(() => process.cwd());
|
|
2426
|
+
const targetDir = join5(repoRoot.trim(), ".gut");
|
|
2427
|
+
const sourceDir = join5(GUT_ROOT2, ".gut");
|
|
2428
|
+
if (!existsSync5(targetDir)) {
|
|
2429
|
+
mkdirSync2(targetDir, { recursive: true });
|
|
2430
|
+
console.log(chalk18.green(`Created ${targetDir}`));
|
|
2431
|
+
}
|
|
2432
|
+
const lang = getLanguage();
|
|
2433
|
+
const needsTranslation = options.translate !== false && lang !== "en";
|
|
2434
|
+
const provider = options.provider.toLowerCase();
|
|
2435
|
+
if (needsTranslation) {
|
|
2436
|
+
console.log(chalk18.gray(`Language: ${lang} - templates will be translated
|
|
2437
|
+
`));
|
|
2438
|
+
}
|
|
2439
|
+
const spinner = ora14();
|
|
2440
|
+
let copied = 0;
|
|
2441
|
+
let skipped = 0;
|
|
2442
|
+
for (const filename of TEMPLATE_FILES) {
|
|
2443
|
+
const sourcePath = join5(sourceDir, filename);
|
|
2444
|
+
const targetPath = join5(targetDir, filename);
|
|
2445
|
+
if (!existsSync5(sourcePath)) {
|
|
2446
|
+
continue;
|
|
2447
|
+
}
|
|
2448
|
+
if (existsSync5(targetPath) && !options.force) {
|
|
2449
|
+
console.log(chalk18.gray(` Skipped: ${filename} (already exists)`));
|
|
2450
|
+
skipped++;
|
|
2451
|
+
continue;
|
|
2452
|
+
}
|
|
2453
|
+
let content = readFileSync6(sourcePath, "utf-8");
|
|
2454
|
+
if (needsTranslation) {
|
|
2455
|
+
spinner.start(`Translating ${filename}...`);
|
|
2456
|
+
try {
|
|
2457
|
+
content = await translateTemplate(content, lang, provider);
|
|
2458
|
+
spinner.succeed(`Translated: ${filename}`);
|
|
2459
|
+
} catch (error) {
|
|
2460
|
+
spinner.fail(`Failed to translate ${filename}`);
|
|
2461
|
+
console.error(chalk18.red(` ${error instanceof Error ? error.message : "Unknown error"}`));
|
|
2462
|
+
console.log(chalk18.gray(` Using original English template`));
|
|
2463
|
+
}
|
|
2464
|
+
} else {
|
|
2465
|
+
console.log(chalk18.green(` Copied: ${filename}`));
|
|
2466
|
+
}
|
|
2467
|
+
writeFileSync3(targetPath, content);
|
|
2468
|
+
copied++;
|
|
2469
|
+
}
|
|
2470
|
+
console.log();
|
|
2471
|
+
if (copied > 0) {
|
|
2472
|
+
console.log(chalk18.green(`\u2713 ${copied} template(s) initialized in .gut/`));
|
|
2473
|
+
}
|
|
2474
|
+
if (skipped > 0) {
|
|
2475
|
+
console.log(chalk18.gray(` ${skipped} template(s) skipped (use --force to overwrite)`));
|
|
2476
|
+
}
|
|
2477
|
+
console.log(chalk18.gray("\nYou can now customize these templates for your project."));
|
|
2478
|
+
});
|
|
2479
|
+
|
|
2290
2480
|
// src/index.ts
|
|
2291
|
-
var program = new
|
|
2481
|
+
var program = new Command18();
|
|
2292
2482
|
program.name("gut").description("Git Utility Tool - AI-powered git commands").version("0.1.0");
|
|
2293
2483
|
program.addCommand(cleanupCommand);
|
|
2294
2484
|
program.addCommand(authCommand);
|
|
@@ -2306,5 +2496,6 @@ program.addCommand(stashCommand);
|
|
|
2306
2496
|
program.addCommand(summaryCommand);
|
|
2307
2497
|
program.addCommand(configCommand);
|
|
2308
2498
|
program.addCommand(langCommand);
|
|
2499
|
+
program.addCommand(initCommand);
|
|
2309
2500
|
program.parse();
|
|
2310
2501
|
//# sourceMappingURL=index.js.map
|