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