gityo 1.0.17 → 1.1.0
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 +0 -1
- package/dist/index.js +634 -103
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -104,6 +104,5 @@ Styles control how commit messages, PR titles, and PR bodies are written. Choose
|
|
|
104
104
|
|
|
105
105
|
- Run `gityo config styles` to view commit, PR title, and PR body styles.
|
|
106
106
|
- Add `.gityo.json` to a repository for project-specific settings.
|
|
107
|
-
- Add `.gityo.md` for project-specific commit instructions.
|
|
108
107
|
- Run `gityo config` to see config locations.
|
|
109
108
|
- Use the config schema for available settings and `--help` on each command for its options.
|
package/dist/index.js
CHANGED
|
@@ -1710,7 +1710,7 @@ function useColor() {
|
|
|
1710
1710
|
if (process$1.env.FORCE_COLOR || process$1.env.CLICOLOR_FORCE !== void 0) return true;
|
|
1711
1711
|
}
|
|
1712
1712
|
new Command();
|
|
1713
|
-
var version$4 = "1.0
|
|
1713
|
+
var version$4 = "1.1.0";
|
|
1714
1714
|
const keybindingLookup = new Set(["emacs", "vim"]);
|
|
1715
1715
|
function isKeybinding(value) {
|
|
1716
1716
|
return keybindingLookup.has(value);
|
|
@@ -24031,6 +24031,15 @@ async function getGit(cwd = process.cwd()) {
|
|
|
24031
24031
|
liveGit: createLiveGit(root)
|
|
24032
24032
|
};
|
|
24033
24033
|
}
|
|
24034
|
+
async function getStagedFiles(git) {
|
|
24035
|
+
return splitNull(await git.raw([
|
|
24036
|
+
"diff",
|
|
24037
|
+
"--cached",
|
|
24038
|
+
"--name-only",
|
|
24039
|
+
"--diff-filter=ACDMRTUXB",
|
|
24040
|
+
"-z"
|
|
24041
|
+
])).sort((left, right) => left.localeCompare(right));
|
|
24042
|
+
}
|
|
24034
24043
|
async function getChangedFiles(git) {
|
|
24035
24044
|
const [unstaged, staged, untracked] = await Promise.all([
|
|
24036
24045
|
git.raw([
|
|
@@ -24039,13 +24048,7 @@ async function getChangedFiles(git) {
|
|
|
24039
24048
|
"--diff-filter=ACDMRTUXB",
|
|
24040
24049
|
"-z"
|
|
24041
24050
|
]),
|
|
24042
|
-
git
|
|
24043
|
-
"diff",
|
|
24044
|
-
"--cached",
|
|
24045
|
-
"--name-only",
|
|
24046
|
-
"--diff-filter=ACDMRTUXB",
|
|
24047
|
-
"-z"
|
|
24048
|
-
]),
|
|
24051
|
+
getStagedFiles(git),
|
|
24049
24052
|
git.raw([
|
|
24050
24053
|
"ls-files",
|
|
24051
24054
|
"--others",
|
|
@@ -24054,12 +24057,12 @@ async function getChangedFiles(git) {
|
|
|
24054
24057
|
])
|
|
24055
24058
|
]);
|
|
24056
24059
|
return Array.from(new Set([
|
|
24057
|
-
...
|
|
24060
|
+
...staged,
|
|
24058
24061
|
...splitNull(unstaged),
|
|
24059
24062
|
...splitNull(untracked)
|
|
24060
24063
|
])).sort((left, right) => left.localeCompare(right));
|
|
24061
24064
|
}
|
|
24062
|
-
async function getCommitDiff(git, contextLines = 3) {
|
|
24065
|
+
async function getCommitDiff(git, scope, contextLines = 3) {
|
|
24063
24066
|
const unified = [`-U${contextLines}`];
|
|
24064
24067
|
const staged = await git.raw([
|
|
24065
24068
|
"diff",
|
|
@@ -24067,12 +24070,12 @@ async function getCommitDiff(git, contextLines = 3) {
|
|
|
24067
24070
|
"--no-ext-diff",
|
|
24068
24071
|
...unified
|
|
24069
24072
|
]);
|
|
24070
|
-
if (
|
|
24071
|
-
|
|
24072
|
-
|
|
24073
|
-
};
|
|
24073
|
+
if (scope === "staged") return staged;
|
|
24074
|
+
if (scope !== "all") throw new Error(`Unknown commit scope '${scope}'.`);
|
|
24075
|
+
const hasStaged = staged.trim().length > 0;
|
|
24074
24076
|
const [unstaged, untracked] = await Promise.all([git.raw([
|
|
24075
24077
|
"diff",
|
|
24078
|
+
...hasStaged ? ["HEAD"] : [],
|
|
24076
24079
|
"--no-ext-diff",
|
|
24077
24080
|
...unified
|
|
24078
24081
|
]), git.raw([
|
|
@@ -24081,17 +24084,14 @@ async function getCommitDiff(git, contextLines = 3) {
|
|
|
24081
24084
|
"--exclude-standard",
|
|
24082
24085
|
"-z"
|
|
24083
24086
|
])]);
|
|
24084
|
-
return
|
|
24085
|
-
diff
|
|
24086
|
-
|
|
24087
|
-
|
|
24088
|
-
|
|
24089
|
-
|
|
24090
|
-
|
|
24091
|
-
|
|
24092
|
-
])))].filter((part) => part.length > 0).join("\n"),
|
|
24093
|
-
hasStaged: false
|
|
24094
|
-
};
|
|
24087
|
+
return [unstaged, ...await Promise.all(splitNull(untracked).map((file$4) => git.raw([
|
|
24088
|
+
"diff",
|
|
24089
|
+
"--no-index",
|
|
24090
|
+
...unified,
|
|
24091
|
+
"--",
|
|
24092
|
+
"/dev/null",
|
|
24093
|
+
file$4
|
|
24094
|
+
])))].filter((part) => part.length > 0).join("\n");
|
|
24095
24095
|
}
|
|
24096
24096
|
function splitNull(output$3) {
|
|
24097
24097
|
return output$3.split("\0").filter(Boolean);
|
|
@@ -24106,17 +24106,16 @@ function createLiveGit(baseDir) {
|
|
|
24106
24106
|
}
|
|
24107
24107
|
const DEFAULT_MAX_DIFF_TOKENS = 24e3;
|
|
24108
24108
|
const DEFAULT_PER_FILE_CAP = 400;
|
|
24109
|
-
const CHARS_PER_TOKEN = 4;
|
|
24109
|
+
const CHARS_PER_TOKEN$2 = 4;
|
|
24110
24110
|
const MINIMIZED_CONTEXT_LINES = 1;
|
|
24111
24111
|
const FILE_SECTION_PREFIX = "diff --git ";
|
|
24112
24112
|
const HUNK_HEADER_PREFIX = "@@ ";
|
|
24113
24113
|
const NOISE_FILE_PATTERNS = [/\.(lock|min\.(?:js|mjs|cjs|css)|map)$/i, /-lock\.[^/]+$/i];
|
|
24114
24114
|
function estimateTokens(text$1) {
|
|
24115
|
-
return Math.ceil(text$1.length / CHARS_PER_TOKEN);
|
|
24115
|
+
return Math.ceil(text$1.length / CHARS_PER_TOKEN$2);
|
|
24116
24116
|
}
|
|
24117
24117
|
async function minimizeDiff(git, options) {
|
|
24118
|
-
const
|
|
24119
|
-
const sections = splitFileSections(diff).map(parseSection);
|
|
24118
|
+
const sections = splitFileSections(await getCommitDiff(git, options.scope, MINIMIZED_CONTEXT_LINES)).map(parseSection);
|
|
24120
24119
|
const kept = [];
|
|
24121
24120
|
const excludedPaths = /* @__PURE__ */ new Set();
|
|
24122
24121
|
for (const section of sections) if (isNoiseFile(section.path)) excludedPaths.add(section.path);
|
|
@@ -24215,7 +24214,7 @@ function splitOversizedSection(section, maxTokens) {
|
|
|
24215
24214
|
}), maxTokens);
|
|
24216
24215
|
}
|
|
24217
24216
|
function hardSplit(text$1, maxTokens) {
|
|
24218
|
-
const maxChars = maxTokens * CHARS_PER_TOKEN;
|
|
24217
|
+
const maxChars = maxTokens * CHARS_PER_TOKEN$2;
|
|
24219
24218
|
const chunks = [];
|
|
24220
24219
|
let current = "";
|
|
24221
24220
|
for (const line of text$1.split("\n")) {
|
|
@@ -36987,11 +36986,11 @@ var postToApi$7 = async ({ url: url$4, headers = {}, body, successfulResponseHan
|
|
|
36987
36986
|
});
|
|
36988
36987
|
}
|
|
36989
36988
|
};
|
|
36990
|
-
function tool
|
|
36989
|
+
function tool(tool2) {
|
|
36991
36990
|
return tool2;
|
|
36992
36991
|
}
|
|
36993
36992
|
function createProviderDefinedToolFactory$4({ id, inputSchema: inputSchema$1 }) {
|
|
36994
|
-
return ({ execute, outputSchema: outputSchema$2, needsApproval, toModelOutput, onInputStart, onInputDelta, onInputAvailable,...args }) => tool
|
|
36993
|
+
return ({ execute, outputSchema: outputSchema$2, needsApproval, toModelOutput, onInputStart, onInputDelta, onInputAvailable,...args }) => tool({
|
|
36995
36994
|
type: "provider",
|
|
36996
36995
|
isProviderExecuted: false,
|
|
36997
36996
|
id,
|
|
@@ -37007,7 +37006,7 @@ function createProviderDefinedToolFactory$4({ id, inputSchema: inputSchema$1 })
|
|
|
37007
37006
|
});
|
|
37008
37007
|
}
|
|
37009
37008
|
function createProviderDefinedToolFactoryWithOutputSchema$3({ id, inputSchema: inputSchema$1, outputSchema: outputSchema$2 }) {
|
|
37010
|
-
return ({ execute, needsApproval, toModelOutput, onInputStart, onInputDelta, onInputAvailable,...args }) => tool
|
|
37009
|
+
return ({ execute, needsApproval, toModelOutput, onInputStart, onInputDelta, onInputAvailable,...args }) => tool({
|
|
37011
37010
|
type: "provider",
|
|
37012
37011
|
isProviderExecuted: false,
|
|
37013
37012
|
id,
|
|
@@ -37023,7 +37022,7 @@ function createProviderDefinedToolFactoryWithOutputSchema$3({ id, inputSchema: i
|
|
|
37023
37022
|
});
|
|
37024
37023
|
}
|
|
37025
37024
|
function createProviderExecutedToolFactory$2({ id, inputSchema: inputSchema$1, outputSchema: outputSchema$2, supportsDeferredResults }) {
|
|
37026
|
-
return ({ onInputStart, onInputDelta, onInputAvailable,...args }) => tool
|
|
37025
|
+
return ({ onInputStart, onInputDelta, onInputAvailable,...args }) => tool({
|
|
37027
37026
|
type: "provider",
|
|
37028
37027
|
isProviderExecuted: true,
|
|
37029
37028
|
id,
|
|
@@ -48321,7 +48320,7 @@ var init_transport = __esmMin((() => {
|
|
|
48321
48320
|
init_parseUrl();
|
|
48322
48321
|
init_toEndpointV1$1();
|
|
48323
48322
|
}));
|
|
48324
|
-
var parseBoolean, expectBoolean, expectNumber, MAX_FLOAT, expectFloat32, expectLong, expectInt, expectInt32, expectShort, expectByte, expectSizedInt, castInt, expectNonNull, expectObject, expectString, expectUnion, strictParseDouble, strictParseFloat, strictParseFloat32, NUMBER_REGEX, parseNumber, limitedParseDouble, handleFloat, limitedParseFloat, limitedParseFloat32, parseFloatString, strictParseLong, strictParseInt, strictParseInt32, strictParseShort, strictParseByte, stackTraceWarning, logger$1;
|
|
48323
|
+
var parseBoolean, expectBoolean, expectNumber, MAX_FLOAT, expectFloat32, expectLong, expectInt, expectInt32, expectShort, expectByte, expectSizedInt, castInt, expectNonNull, expectObject, expectString, expectUnion, strictParseDouble, strictParseFloat, strictParseFloat32, NUMBER_REGEX, parseNumber$1, limitedParseDouble, handleFloat, limitedParseFloat, limitedParseFloat32, parseFloatString, strictParseLong, strictParseInt, strictParseInt32, strictParseShort, strictParseByte, stackTraceWarning, logger$1;
|
|
48325
48324
|
var init_parse_utils = __esmMin((() => {
|
|
48326
48325
|
init_transport();
|
|
48327
48326
|
parseBoolean = (value) => {
|
|
@@ -48427,16 +48426,16 @@ var init_parse_utils = __esmMin((() => {
|
|
|
48427
48426
|
return asObject;
|
|
48428
48427
|
};
|
|
48429
48428
|
strictParseDouble = (value) => {
|
|
48430
|
-
if (typeof value == "string") return expectNumber(parseNumber(value));
|
|
48429
|
+
if (typeof value == "string") return expectNumber(parseNumber$1(value));
|
|
48431
48430
|
return expectNumber(value);
|
|
48432
48431
|
};
|
|
48433
48432
|
strictParseFloat = strictParseDouble;
|
|
48434
48433
|
strictParseFloat32 = (value) => {
|
|
48435
|
-
if (typeof value == "string") return expectFloat32(parseNumber(value));
|
|
48434
|
+
if (typeof value == "string") return expectFloat32(parseNumber$1(value));
|
|
48436
48435
|
return expectFloat32(value);
|
|
48437
48436
|
};
|
|
48438
48437
|
NUMBER_REGEX = /(-?(?:0|[1-9]\d*)(?:\.\d+)?(?:[eE][+-]?\d+)?)|(-?Infinity)|(NaN)/g;
|
|
48439
|
-
parseNumber = (value) => {
|
|
48438
|
+
parseNumber$1 = (value) => {
|
|
48440
48439
|
const matches = value.match(NUMBER_REGEX);
|
|
48441
48440
|
if (matches === null || matches[0].length !== value.length) throw new TypeError(`Expected real number, got implicit NaN`);
|
|
48442
48441
|
return parseFloat(value);
|
|
@@ -48460,20 +48459,20 @@ var init_parse_utils = __esmMin((() => {
|
|
|
48460
48459
|
}
|
|
48461
48460
|
};
|
|
48462
48461
|
strictParseLong = (value) => {
|
|
48463
|
-
if (typeof value === "string") return expectLong(parseNumber(value));
|
|
48462
|
+
if (typeof value === "string") return expectLong(parseNumber$1(value));
|
|
48464
48463
|
return expectLong(value);
|
|
48465
48464
|
};
|
|
48466
48465
|
strictParseInt = strictParseLong;
|
|
48467
48466
|
strictParseInt32 = (value) => {
|
|
48468
|
-
if (typeof value === "string") return expectInt32(parseNumber(value));
|
|
48467
|
+
if (typeof value === "string") return expectInt32(parseNumber$1(value));
|
|
48469
48468
|
return expectInt32(value);
|
|
48470
48469
|
};
|
|
48471
48470
|
strictParseShort = (value) => {
|
|
48472
|
-
if (typeof value === "string") return expectShort(parseNumber(value));
|
|
48471
|
+
if (typeof value === "string") return expectShort(parseNumber$1(value));
|
|
48473
48472
|
return expectShort(value);
|
|
48474
48473
|
};
|
|
48475
48474
|
strictParseByte = (value) => {
|
|
48476
|
-
if (typeof value === "string") return expectByte(parseNumber(value));
|
|
48475
|
+
if (typeof value === "string") return expectByte(parseNumber$1(value));
|
|
48477
48476
|
return expectByte(value);
|
|
48478
48477
|
};
|
|
48479
48478
|
stackTraceWarning = (message) => {
|
|
@@ -48495,7 +48494,7 @@ function dateToUtcString(date$10) {
|
|
|
48495
48494
|
const secondsString = secondsInt < 10 ? `0${secondsInt}` : `${secondsInt}`;
|
|
48496
48495
|
return `${DAYS[dayOfWeek]}, ${dayOfMonthString} ${MONTHS[month]} ${year$1} ${hoursString}:${minutesString}:${secondsString} GMT`;
|
|
48497
48496
|
}
|
|
48498
|
-
var DAYS, MONTHS, RFC3339, parseRfc3339DateTime, RFC3339_WITH_OFFSET$1, parseRfc3339DateTimeWithOffset, IMF_FIXDATE$1, RFC_850_DATE$1, ASC_TIME$1, parseRfc7231DateTime, parseEpochTimestamp, buildDate, parseTwoDigitYear, FIFTY_YEARS_IN_MILLIS, adjustRfc850Year, parseMonthByShortName, DAYS_IN_MONTH, validateDayOfMonth, isLeapYear, parseDateValue, parseMilliseconds, parseOffsetToMilliseconds, stripLeadingZeroes;
|
|
48497
|
+
var DAYS, MONTHS, RFC3339, parseRfc3339DateTime, RFC3339_WITH_OFFSET$1, parseRfc3339DateTimeWithOffset, IMF_FIXDATE$1, RFC_850_DATE$1, ASC_TIME$1, parseRfc7231DateTime, parseEpochTimestamp, buildDate, parseTwoDigitYear, FIFTY_YEARS_IN_MILLIS, adjustRfc850Year, parseMonthByShortName, DAYS_IN_MONTH, validateDayOfMonth, isLeapYear, parseDateValue, parseMilliseconds$1, parseOffsetToMilliseconds, stripLeadingZeroes;
|
|
48499
48498
|
var init_date_utils = __esmMin((() => {
|
|
48500
48499
|
init_parse_utils();
|
|
48501
48500
|
DAYS = [
|
|
@@ -48608,7 +48607,7 @@ var init_date_utils = __esmMin((() => {
|
|
|
48608
48607
|
buildDate = (year$1, month, day, time$7) => {
|
|
48609
48608
|
const adjustedMonth = month - 1;
|
|
48610
48609
|
validateDayOfMonth(year$1, adjustedMonth, day);
|
|
48611
|
-
return new Date(Date.UTC(year$1, adjustedMonth, day, parseDateValue(time$7.hours, "hour", 0, 23), parseDateValue(time$7.minutes, "minute", 0, 59), parseDateValue(time$7.seconds, "seconds", 0, 60), parseMilliseconds(time$7.fractionalMilliseconds)));
|
|
48610
|
+
return new Date(Date.UTC(year$1, adjustedMonth, day, parseDateValue(time$7.hours, "hour", 0, 23), parseDateValue(time$7.minutes, "minute", 0, 59), parseDateValue(time$7.seconds, "seconds", 0, 60), parseMilliseconds$1(time$7.fractionalMilliseconds)));
|
|
48612
48611
|
};
|
|
48613
48612
|
parseTwoDigitYear = (value) => {
|
|
48614
48613
|
const thisYear = (/* @__PURE__ */ new Date()).getUTCFullYear();
|
|
@@ -48653,7 +48652,7 @@ var init_date_utils = __esmMin((() => {
|
|
|
48653
48652
|
if (dateVal < lower || dateVal > upper) throw new TypeError(`${type$1} must be between ${lower} and ${upper}, inclusive`);
|
|
48654
48653
|
return dateVal;
|
|
48655
48654
|
};
|
|
48656
|
-
parseMilliseconds = (value) => {
|
|
48655
|
+
parseMilliseconds$1 = (value) => {
|
|
48657
48656
|
if (value === null || value === void 0) return 0;
|
|
48658
48657
|
return strictParseFloat32("0." + value) * 1e3;
|
|
48659
48658
|
};
|
|
@@ -150845,7 +150844,7 @@ function jsonrepair(text$1) {
|
|
|
150845
150844
|
throwUnexpectedCharacter();
|
|
150846
150845
|
function parseValue() {
|
|
150847
150846
|
parseWhitespaceAndSkipComments();
|
|
150848
|
-
const processed$1 = parseObject() || parseArray() || parseString() || parseNumber$
|
|
150847
|
+
const processed$1 = parseObject() || parseArray() || parseString() || parseNumber$2() || parseKeywords() || parseUnquotedString(false) || parseRegex();
|
|
150849
150848
|
parseWhitespaceAndSkipComments();
|
|
150850
150849
|
return processed$1;
|
|
150851
150850
|
}
|
|
@@ -151139,7 +151138,7 @@ function jsonrepair(text$1) {
|
|
|
151139
151138
|
}
|
|
151140
151139
|
return processed$1;
|
|
151141
151140
|
}
|
|
151142
|
-
function parseNumber$
|
|
151141
|
+
function parseNumber$2() {
|
|
151143
151142
|
const start = i$5;
|
|
151144
151143
|
let num = "";
|
|
151145
151144
|
let invalid = false;
|
|
@@ -171147,7 +171146,7 @@ function hasWebSearch(client) {
|
|
|
171147
171146
|
return typeof client.webSearch === "function";
|
|
171148
171147
|
}
|
|
171149
171148
|
function webSearch(options = {}) {
|
|
171150
|
-
return tool
|
|
171149
|
+
return tool({
|
|
171151
171150
|
description: "Search the web for current information about any topic. Use this when you need up-to-date information, recent news, current statistics, or real-time data that may not be in your training data.",
|
|
171152
171151
|
inputSchema: webSearchInputSchema,
|
|
171153
171152
|
execute: async (input2, { abortSignal }) => {
|
|
@@ -171203,7 +171202,7 @@ function webSearch(options = {}) {
|
|
|
171203
171202
|
}
|
|
171204
171203
|
var webFetchInputSchema = external_exports$2.object({ url: external_exports$2.string().url("Must be a valid URL").describe("The URL to fetch content from") });
|
|
171205
171204
|
function webFetch(options = {}) {
|
|
171206
|
-
return tool
|
|
171205
|
+
return tool({
|
|
171207
171206
|
description: "Fetch and read content from a specific web URL. Use this to retrieve the full text content of web pages, articles, documentation, or any publicly accessible web content for analysis or summarization.",
|
|
171208
171207
|
inputSchema: webFetchInputSchema,
|
|
171209
171208
|
execute: async (input2, context) => {
|
|
@@ -203416,7 +203415,7 @@ var require_dist$2 = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
|
203416
203415
|
safeParseJSON: () => safeParseJSON$2,
|
|
203417
203416
|
safeValidateTypes: () => safeValidateTypes$2,
|
|
203418
203417
|
standardSchemaValidator: () => standardSchemaValidator,
|
|
203419
|
-
tool: () => tool$
|
|
203418
|
+
tool: () => tool$3,
|
|
203420
203419
|
validateTypes: () => validateTypes$2,
|
|
203421
203420
|
validator: () => validator,
|
|
203422
203421
|
validatorSymbol: () => validatorSymbol,
|
|
@@ -203918,7 +203917,7 @@ var require_dist$2 = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
|
203918
203917
|
});
|
|
203919
203918
|
}
|
|
203920
203919
|
};
|
|
203921
|
-
function tool$
|
|
203920
|
+
function tool$3(tool2) {
|
|
203922
203921
|
return tool2;
|
|
203923
203922
|
}
|
|
203924
203923
|
function dynamicTool$2(tool2) {
|
|
@@ -203928,7 +203927,7 @@ var require_dist$2 = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
|
203928
203927
|
};
|
|
203929
203928
|
}
|
|
203930
203929
|
function createProviderDefinedToolFactory({ id, name: name$18, inputSchema: inputSchema$1 }) {
|
|
203931
|
-
return ({ execute, outputSchema: outputSchema$2, toModelOutput, onInputStart, onInputDelta, onInputAvailable,...args }) => tool$
|
|
203930
|
+
return ({ execute, outputSchema: outputSchema$2, toModelOutput, onInputStart, onInputDelta, onInputAvailable,...args }) => tool$3({
|
|
203932
203931
|
type: "provider-defined",
|
|
203933
203932
|
id,
|
|
203934
203933
|
name: name$18,
|
|
@@ -203943,7 +203942,7 @@ var require_dist$2 = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
|
203943
203942
|
});
|
|
203944
203943
|
}
|
|
203945
203944
|
function createProviderDefinedToolFactoryWithOutputSchema({ id, name: name$18, inputSchema: inputSchema$1, outputSchema: outputSchema$2 }) {
|
|
203946
|
-
return ({ execute, toModelOutput, onInputStart, onInputDelta, onInputAvailable,...args }) => tool$
|
|
203945
|
+
return ({ execute, toModelOutput, onInputStart, onInputDelta, onInputAvailable,...args }) => tool$3({
|
|
203947
203946
|
type: "provider-defined",
|
|
203948
203947
|
id,
|
|
203949
203948
|
name: name$18,
|
|
@@ -206837,11 +206836,11 @@ var postToApi$1 = async ({ url: url$4, headers = {}, body, successfulResponseHan
|
|
|
206837
206836
|
});
|
|
206838
206837
|
}
|
|
206839
206838
|
};
|
|
206840
|
-
function tool$
|
|
206839
|
+
function tool$2(tool2) {
|
|
206841
206840
|
return tool2;
|
|
206842
206841
|
}
|
|
206843
206842
|
function createProviderToolFactory$1({ id, inputSchema: inputSchema$1 }) {
|
|
206844
|
-
return ({ execute, outputSchema: outputSchema$2, needsApproval, toModelOutput, onInputStart, onInputDelta, onInputAvailable,...args }) => tool$
|
|
206843
|
+
return ({ execute, outputSchema: outputSchema$2, needsApproval, toModelOutput, onInputStart, onInputDelta, onInputAvailable,...args }) => tool$2({
|
|
206845
206844
|
type: "provider",
|
|
206846
206845
|
id,
|
|
206847
206846
|
args,
|
|
@@ -206856,7 +206855,7 @@ function createProviderToolFactory$1({ id, inputSchema: inputSchema$1 }) {
|
|
|
206856
206855
|
});
|
|
206857
206856
|
}
|
|
206858
206857
|
function createProviderToolFactoryWithOutputSchema$1({ id, inputSchema: inputSchema$1, outputSchema: outputSchema$2, supportsDeferredResults }) {
|
|
206859
|
-
return ({ execute, needsApproval, toModelOutput, onInputStart, onInputDelta, onInputAvailable,...args }) => tool$
|
|
206858
|
+
return ({ execute, needsApproval, toModelOutput, onInputStart, onInputDelta, onInputAvailable,...args }) => tool$2({
|
|
206860
206859
|
type: "provider",
|
|
206861
206860
|
id,
|
|
206862
206861
|
args,
|
|
@@ -228815,6 +228814,11 @@ const configSchema = object$3({
|
|
|
228815
228814
|
prBodyStyle: string$5().min(1),
|
|
228816
228815
|
prTitleStyles: record$3(string$5().min(1), instructionSchema),
|
|
228817
228816
|
prBodyStyles: record$3(string$5().min(1), instructionSchema),
|
|
228817
|
+
prMergeMethod: _enum$4([
|
|
228818
|
+
"merge",
|
|
228819
|
+
"rebase",
|
|
228820
|
+
"squash"
|
|
228821
|
+
]),
|
|
228818
228822
|
maxDiffTokens: number$4().int().min(1e3),
|
|
228819
228823
|
perFileCap: number$4().int().min(50),
|
|
228820
228824
|
autoAcceptCommitMessage: boolean$4(),
|
|
@@ -228832,6 +228836,7 @@ function resolveConfig(input$3) {
|
|
|
228832
228836
|
prBodyStyle: parsed.prBodyStyle,
|
|
228833
228837
|
prTitleStyles: parsed.prTitleStyles,
|
|
228834
228838
|
prBodyStyles: parsed.prBodyStyles,
|
|
228839
|
+
prMergeMethod: parsed.prMergeMethod ?? "merge",
|
|
228835
228840
|
maxDiffTokens: parsed.maxDiffTokens,
|
|
228836
228841
|
perFileCap: parsed.perFileCap,
|
|
228837
228842
|
autoAcceptCommitMessage: parsed.autoAcceptCommitMessage ?? false,
|
|
@@ -228842,15 +228847,26 @@ function resolveConfig(input$3) {
|
|
|
228842
228847
|
const PROJECT_CONFIG_FILE_NAME = ".gityo.json";
|
|
228843
228848
|
const GLOBAL_CONFIG_FILE_PATH = path.join(os.homedir(), ".config", "gityo.json");
|
|
228844
228849
|
async function loadConfig(cwd = process.cwd()) {
|
|
228845
|
-
const [globalConfig$3, projectConfig
|
|
228846
|
-
readGlobalConfig(),
|
|
228847
|
-
readProjectConfig(cwd),
|
|
228848
|
-
readInstructionsFile(path.join(cwd, ".gityo.md"))
|
|
228849
|
-
]);
|
|
228850
|
+
const [globalConfig$3, projectConfig] = await Promise.all([readGlobalConfig(), readProjectConfig(cwd)]);
|
|
228850
228851
|
return resolveConfig({
|
|
228851
228852
|
...globalConfig$3 ?? {},
|
|
228852
228853
|
...projectConfig ?? {},
|
|
228853
|
-
|
|
228854
|
+
models: {
|
|
228855
|
+
...globalConfig$3?.models,
|
|
228856
|
+
...projectConfig?.models
|
|
228857
|
+
},
|
|
228858
|
+
commitStyles: {
|
|
228859
|
+
...globalConfig$3?.commitStyles,
|
|
228860
|
+
...projectConfig?.commitStyles
|
|
228861
|
+
},
|
|
228862
|
+
prTitleStyles: {
|
|
228863
|
+
...globalConfig$3?.prTitleStyles,
|
|
228864
|
+
...projectConfig?.prTitleStyles
|
|
228865
|
+
},
|
|
228866
|
+
prBodyStyles: {
|
|
228867
|
+
...globalConfig$3?.prBodyStyles,
|
|
228868
|
+
...projectConfig?.prBodyStyles
|
|
228869
|
+
}
|
|
228854
228870
|
});
|
|
228855
228871
|
}
|
|
228856
228872
|
async function readProjectConfig(cwd = process.cwd()) {
|
|
@@ -228877,17 +228893,6 @@ async function readConfigFile(filePath) {
|
|
|
228877
228893
|
if (!result.success) throw new Error(`Invalid config in ${filePath}: ${result.error.message}`);
|
|
228878
228894
|
return result.data;
|
|
228879
228895
|
}
|
|
228880
|
-
async function readInstructionsFile(filePath) {
|
|
228881
|
-
let contents;
|
|
228882
|
-
try {
|
|
228883
|
-
contents = await readFile(filePath, "utf8");
|
|
228884
|
-
} catch (error$125) {
|
|
228885
|
-
if (isMissingFileError(error$125)) return;
|
|
228886
|
-
throw error$125;
|
|
228887
|
-
}
|
|
228888
|
-
const value = contents.trim();
|
|
228889
|
-
return value.length > 0 ? value : void 0;
|
|
228890
|
-
}
|
|
228891
228896
|
function isMissingFileError(error$125) {
|
|
228892
228897
|
return error$125 instanceof Error && "code" in error$125 && typeof error$125.code === "string" && error$125.code === "ENOENT";
|
|
228893
228898
|
}
|
|
@@ -228950,10 +228955,10 @@ async function runWithLoading(label, task) {
|
|
|
228950
228955
|
readline.clearLine(process.stdout, 0);
|
|
228951
228956
|
}
|
|
228952
228957
|
}
|
|
228953
|
-
const MAP_CONCURRENCY = 3;
|
|
228954
|
-
const PROMPT_RESERVE_TOKENS = 800;
|
|
228955
|
-
const MIN_CHUNK_BUDGET_TOKENS = 1e3;
|
|
228956
|
-
const MAX_TOC_LINES = 500;
|
|
228958
|
+
const MAP_CONCURRENCY$1 = 3;
|
|
228959
|
+
const PROMPT_RESERVE_TOKENS$2 = 800;
|
|
228960
|
+
const MIN_CHUNK_BUDGET_TOKENS$1 = 1e3;
|
|
228961
|
+
const MAX_TOC_LINES$1 = 500;
|
|
228957
228962
|
async function mainController(options = {}) {
|
|
228958
228963
|
const { git, liveGit } = await getGit();
|
|
228959
228964
|
const config$5 = await loadConfig((await git.revparse(["--show-toplevel"])).trim());
|
|
@@ -228970,9 +228975,9 @@ async function mainController(options = {}) {
|
|
|
228970
228975
|
const forceExecPostCommand = options.post || options.yolo;
|
|
228971
228976
|
let finalCommitMessage = options.input?.trim() ?? "";
|
|
228972
228977
|
if (typeof options.input === "string" && finalCommitMessage.length === 0) throw new Error("Provided commit message input cannot be empty.");
|
|
228973
|
-
const files = await
|
|
228978
|
+
const { diffScope, files } = await getCommitFiles(git, options.scope ?? "staged-or-changes");
|
|
228974
228979
|
if (files.length === 0) return console.log("No changed files found.");
|
|
228975
|
-
const
|
|
228980
|
+
const diff = await getCommitDiff(git, diffScope);
|
|
228976
228981
|
if (finalCommitMessage.length > 0) {
|
|
228977
228982
|
console.log(source_default.yellow("✓ Using direct commit message"));
|
|
228978
228983
|
console.log(source_default.cyan.dim(finalCommitMessage));
|
|
@@ -228981,6 +228986,7 @@ async function mainController(options = {}) {
|
|
|
228981
228986
|
if (finalCommitMessage.length === 0) while (true) {
|
|
228982
228987
|
finalCommitMessage = (await runWithLoading("Generating commit message", () => generateMessage({
|
|
228983
228988
|
git,
|
|
228989
|
+
scope: diffScope,
|
|
228984
228990
|
languageModel,
|
|
228985
228991
|
style,
|
|
228986
228992
|
instructions,
|
|
@@ -228997,7 +229003,7 @@ async function mainController(options = {}) {
|
|
|
228997
229003
|
}
|
|
228998
229004
|
if (finalCommitMessage.length === 0) throw new Error("Commit message cannot be empty.");
|
|
228999
229005
|
if (forceLLMGenerate) console.log(source_default.green("✓ Committing changes"));
|
|
229000
|
-
if (
|
|
229006
|
+
if (diffScope === "all") await git.add(["-A"]);
|
|
229001
229007
|
console.log(files.join("\n"));
|
|
229002
229008
|
console.log("");
|
|
229003
229009
|
await liveGit.commit(finalCommitMessage);
|
|
@@ -229014,19 +229020,35 @@ async function mainController(options = {}) {
|
|
|
229014
229020
|
await liveGit.push();
|
|
229015
229021
|
if (config$5.postCommand === "push-and-pull") await liveGit.pull();
|
|
229016
229022
|
}
|
|
229023
|
+
async function getCommitFiles(git, commitScope) {
|
|
229024
|
+
const stagedFiles = await getStagedFiles(git);
|
|
229025
|
+
let diffScope;
|
|
229026
|
+
if (commitScope === "everything") diffScope = "all";
|
|
229027
|
+
else if (commitScope === "staged-only") {
|
|
229028
|
+
if (stagedFiles.length === 0) throw new Error("Nothing is staged.");
|
|
229029
|
+
diffScope = "staged";
|
|
229030
|
+
} else if (commitScope === "staged-or-changes") diffScope = stagedFiles.length > 0 ? "staged" : "all";
|
|
229031
|
+
else throw new Error(`Unknown commit scope '${commitScope}'.`);
|
|
229032
|
+
const files = diffScope === "staged" ? stagedFiles : await getChangedFiles(git);
|
|
229033
|
+
return {
|
|
229034
|
+
diffScope,
|
|
229035
|
+
files
|
|
229036
|
+
};
|
|
229037
|
+
}
|
|
229017
229038
|
async function generateMessage(options) {
|
|
229018
229039
|
const { git, languageModel, style, instructions, diff } = options;
|
|
229019
229040
|
if (estimateTokens(diff) <= options.maxDiffTokens) return generateCommitMessage(languageModel, style, instructions, diff);
|
|
229020
229041
|
const { toc, body } = await minimizeDiff(git, {
|
|
229042
|
+
scope: options.scope,
|
|
229021
229043
|
perFileCap: options.perFileCap,
|
|
229022
229044
|
allFiles: options.files
|
|
229023
229045
|
});
|
|
229024
229046
|
if (estimateTokens(toc) + estimateTokens(body) <= options.maxDiffTokens) return generateCommitMessage(languageModel, style, instructions, `${toc}\n\n${body}`);
|
|
229025
229047
|
let effectiveToc = toc;
|
|
229026
|
-
let chunkBudget = options.maxDiffTokens - estimateTokens(effectiveToc) - PROMPT_RESERVE_TOKENS;
|
|
229027
|
-
if (chunkBudget < MIN_CHUNK_BUDGET_TOKENS) {
|
|
229028
|
-
effectiveToc = toc.split("\n").slice(0, MAX_TOC_LINES).join("\n");
|
|
229029
|
-
chunkBudget = Math.max(options.maxDiffTokens - estimateTokens(effectiveToc) - PROMPT_RESERVE_TOKENS, MIN_CHUNK_BUDGET_TOKENS);
|
|
229048
|
+
let chunkBudget = options.maxDiffTokens - estimateTokens(effectiveToc) - PROMPT_RESERVE_TOKENS$2;
|
|
229049
|
+
if (chunkBudget < MIN_CHUNK_BUDGET_TOKENS$1) {
|
|
229050
|
+
effectiveToc = toc.split("\n").slice(0, MAX_TOC_LINES$1).join("\n");
|
|
229051
|
+
chunkBudget = Math.max(options.maxDiffTokens - estimateTokens(effectiveToc) - PROMPT_RESERVE_TOKENS$2, MIN_CHUNK_BUDGET_TOKENS$1);
|
|
229030
229052
|
}
|
|
229031
229053
|
const chunks = splitDiffIntoChunks(body, chunkBudget);
|
|
229032
229054
|
const summaries = new Array(chunks.length);
|
|
@@ -229037,7 +229059,7 @@ async function generateMessage(options) {
|
|
|
229037
229059
|
summaries[index$2] = await summarizeChanges(languageModel, effectiveToc, chunks[index$2]);
|
|
229038
229060
|
}
|
|
229039
229061
|
}
|
|
229040
|
-
await Promise.all(Array.from({ length: Math.min(MAP_CONCURRENCY, chunks.length) }, worker));
|
|
229062
|
+
await Promise.all(Array.from({ length: Math.min(MAP_CONCURRENCY$1, chunks.length) }, worker));
|
|
229041
229063
|
return generateCommitMessageFromSummaries(languageModel, style, instructions, effectiveToc, summaries);
|
|
229042
229064
|
}
|
|
229043
229065
|
const CONFIG_SCHEMA_URL = "https://github.com/NazmusSayad/gityo/raw/refs/heads/schema/schema.json";
|
|
@@ -229153,12 +229175,12 @@ async function createPullRequest$1(input$3) {
|
|
|
229153
229175
|
input$3.head
|
|
229154
229176
|
]);
|
|
229155
229177
|
}
|
|
229156
|
-
async function mergePullRequest(number$7) {
|
|
229178
|
+
async function mergePullRequest(number$7, method) {
|
|
229157
229179
|
await execInherit("gh", [
|
|
229158
229180
|
"pr",
|
|
229159
229181
|
"merge",
|
|
229160
229182
|
String(number$7),
|
|
229161
|
-
|
|
229183
|
+
`--${method}`
|
|
229162
229184
|
]);
|
|
229163
229185
|
}
|
|
229164
229186
|
async function openPullRequest(number$7) {
|
|
@@ -236143,7 +236165,7 @@ const renderPullRequest = createRenderer({
|
|
|
236143
236165
|
async function getCurrentBranch() {
|
|
236144
236166
|
return (await exec("git", ["branch", "--show-current"])).trim();
|
|
236145
236167
|
}
|
|
236146
|
-
async function getRepoRoot() {
|
|
236168
|
+
async function getRepoRoot$1() {
|
|
236147
236169
|
return (await exec("git", ["rev-parse", "--show-toplevel"])).trim();
|
|
236148
236170
|
}
|
|
236149
236171
|
async function resolvePrBranches(baseArg, headArg) {
|
|
@@ -236158,7 +236180,7 @@ async function resolvePrBranches(baseArg, headArg) {
|
|
|
236158
236180
|
};
|
|
236159
236181
|
}
|
|
236160
236182
|
async function loadPullRequestContext(options = {}) {
|
|
236161
|
-
const config$5 = await loadConfig(await getRepoRoot());
|
|
236183
|
+
const config$5 = await loadConfig(await getRepoRoot$1());
|
|
236162
236184
|
const languageModel = resolveLanguageModel(resolveModelConfig(config$5.models, options.modelKey ?? "default"));
|
|
236163
236185
|
const titleStyleKey = options.titleStyle ?? config$5.prTitleStyle ?? "default";
|
|
236164
236186
|
const titleStyle = config$5.prTitleStyles?.[titleStyleKey] ?? PR_TITLE_STYLES[titleStyleKey];
|
|
@@ -236247,9 +236269,11 @@ function buildCompareContext(compare, maxDiffTokens) {
|
|
|
236247
236269
|
async function handleUncommittedChanges(headBranch, options = {}) {
|
|
236248
236270
|
if (await getCurrentBranch() !== headBranch) return;
|
|
236249
236271
|
const { git } = await getGit();
|
|
236250
|
-
const
|
|
236272
|
+
const scope = options.scope ?? "staged-or-changes";
|
|
236273
|
+
const { diffScope, files } = await getCommitFiles(git, scope);
|
|
236251
236274
|
if (files.length === 0) return;
|
|
236252
|
-
|
|
236275
|
+
const label = diffScope === "staged" ? "staged" : "uncommitted";
|
|
236276
|
+
console.log(source_default.yellow(`• You have ${files.length} ${label} local change(s):\n${files.join("\n")}`));
|
|
236253
236277
|
console.log("");
|
|
236254
236278
|
if (!options.yolo) {
|
|
236255
236279
|
if (!await dist_default({
|
|
@@ -236257,14 +236281,15 @@ async function handleUncommittedChanges(headBranch, options = {}) {
|
|
|
236257
236281
|
default: true,
|
|
236258
236282
|
theme: selectionTheme
|
|
236259
236283
|
})) {
|
|
236260
|
-
console.log(source_default.dim(
|
|
236284
|
+
console.log(source_default.dim(`Ignoring ${label} local changes.`));
|
|
236261
236285
|
console.log("");
|
|
236262
236286
|
return;
|
|
236263
236287
|
}
|
|
236264
236288
|
}
|
|
236265
236289
|
await mainController({
|
|
236266
236290
|
yolo: options.yolo,
|
|
236267
|
-
push: true
|
|
236291
|
+
push: true,
|
|
236292
|
+
scope
|
|
236268
236293
|
});
|
|
236269
236294
|
console.log("");
|
|
236270
236295
|
}
|
|
@@ -236276,7 +236301,10 @@ async function createPullRequestController(baseArg, headArg, options = {}) {
|
|
|
236276
236301
|
});
|
|
236277
236302
|
const systemPrompt = buildPullRequestSystemPrompt(context);
|
|
236278
236303
|
const branches = await resolvePrBranches(baseArg, headArg);
|
|
236279
|
-
await handleUncommittedChanges(branches.head, {
|
|
236304
|
+
await handleUncommittedChanges(branches.head, {
|
|
236305
|
+
yolo: options.yolo ?? false,
|
|
236306
|
+
scope: options.all ? "everything" : "staged-or-changes"
|
|
236307
|
+
});
|
|
236280
236308
|
const pullRequest = await createPullRequest({
|
|
236281
236309
|
base: branches.base,
|
|
236282
236310
|
head: branches.head,
|
|
@@ -236300,7 +236328,10 @@ async function mergePullRequestController(baseArg, headArg, options = {}) {
|
|
|
236300
236328
|
});
|
|
236301
236329
|
const systemPrompt = buildPullRequestSystemPrompt(context);
|
|
236302
236330
|
const branches = await resolvePrBranches(baseArg, headArg);
|
|
236303
|
-
await handleUncommittedChanges(branches.head, {
|
|
236331
|
+
await handleUncommittedChanges(branches.head, {
|
|
236332
|
+
yolo: options.yolo ?? false,
|
|
236333
|
+
scope: options.all ? "everything" : "staged-or-changes"
|
|
236334
|
+
});
|
|
236304
236335
|
let pullRequest = await findPullRequest(branches.base, branches.head);
|
|
236305
236336
|
if (pullRequest) console.log(pullRequest.url);
|
|
236306
236337
|
else pullRequest = await createPullRequest({
|
|
@@ -236321,7 +236352,479 @@ async function mergePullRequestController(baseArg, headArg, options = {}) {
|
|
|
236321
236352
|
console.log("Pull request merge cancelled.");
|
|
236322
236353
|
return;
|
|
236323
236354
|
}
|
|
236324
|
-
await mergePullRequest(pullRequest.number);
|
|
236355
|
+
await mergePullRequest(pullRequest.number, options.mergeMethod ?? context.config.prMergeMethod);
|
|
236356
|
+
}
|
|
236357
|
+
const toZeroIfInfinity = (value) => Number.isFinite(value) ? value : 0;
|
|
236358
|
+
function parseNumber(milliseconds) {
|
|
236359
|
+
return {
|
|
236360
|
+
days: Math.trunc(milliseconds / 864e5),
|
|
236361
|
+
hours: Math.trunc(milliseconds / 36e5 % 24),
|
|
236362
|
+
minutes: Math.trunc(milliseconds / 6e4 % 60),
|
|
236363
|
+
seconds: Math.trunc(milliseconds / 1e3 % 60),
|
|
236364
|
+
milliseconds: Math.trunc(milliseconds % 1e3),
|
|
236365
|
+
microseconds: Math.trunc(toZeroIfInfinity(milliseconds * 1e3) % 1e3),
|
|
236366
|
+
nanoseconds: Math.trunc(toZeroIfInfinity(milliseconds * 1e6) % 1e3)
|
|
236367
|
+
};
|
|
236368
|
+
}
|
|
236369
|
+
function parseBigint(milliseconds) {
|
|
236370
|
+
return {
|
|
236371
|
+
days: milliseconds / 86400000n,
|
|
236372
|
+
hours: milliseconds / 3600000n % 24n,
|
|
236373
|
+
minutes: milliseconds / 60000n % 60n,
|
|
236374
|
+
seconds: milliseconds / 1000n % 60n,
|
|
236375
|
+
milliseconds: milliseconds % 1000n,
|
|
236376
|
+
microseconds: 0n,
|
|
236377
|
+
nanoseconds: 0n
|
|
236378
|
+
};
|
|
236379
|
+
}
|
|
236380
|
+
function parseMilliseconds(milliseconds) {
|
|
236381
|
+
switch (typeof milliseconds) {
|
|
236382
|
+
case "number":
|
|
236383
|
+
if (Number.isFinite(milliseconds)) return parseNumber(milliseconds);
|
|
236384
|
+
break;
|
|
236385
|
+
case "bigint": return parseBigint(milliseconds);
|
|
236386
|
+
}
|
|
236387
|
+
throw new TypeError("Expected a finite number or bigint");
|
|
236388
|
+
}
|
|
236389
|
+
const isZero = (value) => value === 0 || value === 0n;
|
|
236390
|
+
const pluralize = (word, count) => count === 1 || count === 1n ? word : `${word}s`;
|
|
236391
|
+
const SECOND_ROUNDING_EPSILON = 1e-7;
|
|
236392
|
+
const ONE_DAY_IN_MILLISECONDS = 24n * 60n * 60n * 1000n;
|
|
236393
|
+
function prettyMilliseconds(milliseconds, options) {
|
|
236394
|
+
const isBigInt = typeof milliseconds === "bigint";
|
|
236395
|
+
if (!isBigInt && !Number.isFinite(milliseconds)) throw new TypeError("Expected a finite number or bigint");
|
|
236396
|
+
options = { ...options };
|
|
236397
|
+
const sign$2 = milliseconds < 0 ? "-" : "";
|
|
236398
|
+
milliseconds = milliseconds < 0 ? -milliseconds : milliseconds;
|
|
236399
|
+
if (options.colonNotation) {
|
|
236400
|
+
options.compact = false;
|
|
236401
|
+
options.formatSubMilliseconds = false;
|
|
236402
|
+
options.separateMilliseconds = false;
|
|
236403
|
+
options.verbose = false;
|
|
236404
|
+
}
|
|
236405
|
+
if (options.compact) {
|
|
236406
|
+
options.unitCount = 1;
|
|
236407
|
+
options.secondsDecimalDigits = 0;
|
|
236408
|
+
options.millisecondsDecimalDigits = 0;
|
|
236409
|
+
}
|
|
236410
|
+
let result = [];
|
|
236411
|
+
const floorDecimals = (value, decimalDigits) => {
|
|
236412
|
+
const flooredInterimValue = Math.floor(value * 10 ** decimalDigits + SECOND_ROUNDING_EPSILON);
|
|
236413
|
+
return (Math.round(flooredInterimValue) / 10 ** decimalDigits).toFixed(decimalDigits);
|
|
236414
|
+
};
|
|
236415
|
+
const add = (value, long, short, valueString) => {
|
|
236416
|
+
if ((result.length === 0 || !options.colonNotation) && isZero(value) && !(options.colonNotation && short === "m")) return;
|
|
236417
|
+
valueString ?? (valueString = String(value));
|
|
236418
|
+
if (options.colonNotation) {
|
|
236419
|
+
const wholeDigits = valueString.includes(".") ? valueString.split(".")[0].length : valueString.length;
|
|
236420
|
+
const minLength = result.length > 0 ? 2 : 1;
|
|
236421
|
+
valueString = "0".repeat(Math.max(0, minLength - wholeDigits)) + valueString;
|
|
236422
|
+
} else valueString += options.verbose ? " " + pluralize(long, value) : short;
|
|
236423
|
+
result.push(valueString);
|
|
236424
|
+
};
|
|
236425
|
+
const parsed = parseMilliseconds(milliseconds);
|
|
236426
|
+
const days = BigInt(parsed.days);
|
|
236427
|
+
if (options.hideYearAndDays) add(BigInt(days) * 24n + BigInt(parsed.hours), "hour", "h");
|
|
236428
|
+
else {
|
|
236429
|
+
if (options.hideYear) add(days, "day", "d");
|
|
236430
|
+
else {
|
|
236431
|
+
add(days / 365n, "year", "y");
|
|
236432
|
+
add(days % 365n, "day", "d");
|
|
236433
|
+
}
|
|
236434
|
+
add(Number(parsed.hours), "hour", "h");
|
|
236435
|
+
}
|
|
236436
|
+
add(Number(parsed.minutes), "minute", "m");
|
|
236437
|
+
if (!options.hideSeconds) if (options.separateMilliseconds || options.formatSubMilliseconds || !options.colonNotation && milliseconds < 1e3 && !options.subSecondsAsDecimals) {
|
|
236438
|
+
const seconds = Number(parsed.seconds);
|
|
236439
|
+
const milliseconds$1 = Number(parsed.milliseconds);
|
|
236440
|
+
const microseconds = Number(parsed.microseconds);
|
|
236441
|
+
const nanoseconds = Number(parsed.nanoseconds);
|
|
236442
|
+
add(seconds, "second", "s");
|
|
236443
|
+
if (options.formatSubMilliseconds) {
|
|
236444
|
+
add(milliseconds$1, "millisecond", "ms");
|
|
236445
|
+
add(microseconds, "microsecond", "µs");
|
|
236446
|
+
add(nanoseconds, "nanosecond", "ns");
|
|
236447
|
+
} else {
|
|
236448
|
+
const millisecondsAndBelow = milliseconds$1 + microseconds / 1e3 + nanoseconds / 1e6;
|
|
236449
|
+
const millisecondsDecimalDigits = typeof options.millisecondsDecimalDigits === "number" ? options.millisecondsDecimalDigits : 0;
|
|
236450
|
+
const roundedMilliseconds = millisecondsAndBelow >= 1 ? Math.round(millisecondsAndBelow) : Math.ceil(millisecondsAndBelow);
|
|
236451
|
+
const maximumMilliseconds = 1e3 - 10 ** -millisecondsDecimalDigits;
|
|
236452
|
+
const millisecondsString = millisecondsDecimalDigits ? Math.min(millisecondsAndBelow, maximumMilliseconds).toFixed(millisecondsDecimalDigits) : Math.min(roundedMilliseconds, maximumMilliseconds);
|
|
236453
|
+
add(Number.parseFloat(millisecondsString), "millisecond", "ms", millisecondsString);
|
|
236454
|
+
}
|
|
236455
|
+
} else {
|
|
236456
|
+
const secondsFixed = floorDecimals((isBigInt ? Number(milliseconds % ONE_DAY_IN_MILLISECONDS) : milliseconds) / 1e3 % 60, typeof options.secondsDecimalDigits === "number" ? options.secondsDecimalDigits : 1);
|
|
236457
|
+
const secondsString = options.keepDecimalsOnWholeSeconds ? secondsFixed : secondsFixed.replace(/\.0+$/, "");
|
|
236458
|
+
add(Number.parseFloat(secondsString), "second", "s", secondsString);
|
|
236459
|
+
}
|
|
236460
|
+
if (result.length === 0) return sign$2 + "0" + (options.verbose ? " milliseconds" : "ms");
|
|
236461
|
+
const separator = options.colonNotation ? ":" : " ";
|
|
236462
|
+
if (typeof options.unitCount === "number") result = result.slice(0, Math.max(options.unitCount, 1));
|
|
236463
|
+
return sign$2 + result.join(separator);
|
|
236464
|
+
}
|
|
236465
|
+
async function listReleases(limit) {
|
|
236466
|
+
const output$3 = await exec("gh", [
|
|
236467
|
+
"release",
|
|
236468
|
+
"list",
|
|
236469
|
+
"--exclude-drafts",
|
|
236470
|
+
"--limit",
|
|
236471
|
+
String(limit),
|
|
236472
|
+
"--json",
|
|
236473
|
+
"tagName,publishedAt"
|
|
236474
|
+
]);
|
|
236475
|
+
return JSON.parse(output$3).sort((a, b$2) => Date.parse(b$2.publishedAt) - Date.parse(a.publishedAt));
|
|
236476
|
+
}
|
|
236477
|
+
const COMMIT_JQ = "{hash: .sha, date: .commit.author.date, authorName: .commit.author.name, authorEmail: .commit.author.email, message: .commit.message}";
|
|
236478
|
+
async function getBranchHeadSha(branch) {
|
|
236479
|
+
return (await exec("gh", [
|
|
236480
|
+
"api",
|
|
236481
|
+
`repos/{owner}/{repo}/commits/${encodeURIComponent(branch)}`,
|
|
236482
|
+
"--jq",
|
|
236483
|
+
".sha"
|
|
236484
|
+
])).trim();
|
|
236485
|
+
}
|
|
236486
|
+
async function listReleaseCommits(fromTag, toSha) {
|
|
236487
|
+
const commits = (await exec("gh", [
|
|
236488
|
+
"api",
|
|
236489
|
+
"--paginate",
|
|
236490
|
+
...fromTag === null ? [
|
|
236491
|
+
`repos/{owner}/{repo}/commits?sha=${toSha}&per_page=100`,
|
|
236492
|
+
"--jq",
|
|
236493
|
+
`.[] | ${COMMIT_JQ}`
|
|
236494
|
+
] : [
|
|
236495
|
+
`repos/{owner}/{repo}/compare/${encodeURIComponent(fromTag)}...${toSha}?per_page=100`,
|
|
236496
|
+
"--jq",
|
|
236497
|
+
`.commits[] | ${COMMIT_JQ}`
|
|
236498
|
+
]
|
|
236499
|
+
])).split("\n").filter((line) => line.length > 0).map((line) => JSON.parse(line));
|
|
236500
|
+
return fromTag === null ? commits : commits.reverse();
|
|
236501
|
+
}
|
|
236502
|
+
async function releaseExists(tag) {
|
|
236503
|
+
try {
|
|
236504
|
+
await exec("gh", [
|
|
236505
|
+
"release",
|
|
236506
|
+
"view",
|
|
236507
|
+
tag,
|
|
236508
|
+
"--json",
|
|
236509
|
+
"tagName"
|
|
236510
|
+
]);
|
|
236511
|
+
return true;
|
|
236512
|
+
} catch {
|
|
236513
|
+
return false;
|
|
236514
|
+
}
|
|
236515
|
+
}
|
|
236516
|
+
async function deleteRelease(tag) {
|
|
236517
|
+
await execInherit("gh", [
|
|
236518
|
+
"release",
|
|
236519
|
+
"delete",
|
|
236520
|
+
tag,
|
|
236521
|
+
"--yes"
|
|
236522
|
+
]);
|
|
236523
|
+
await execInherit("gh", [
|
|
236524
|
+
"api",
|
|
236525
|
+
"--method",
|
|
236526
|
+
"DELETE",
|
|
236527
|
+
`repos/{owner}/{repo}/git/refs/tags/${encodeURIComponent(tag)}`
|
|
236528
|
+
]);
|
|
236529
|
+
}
|
|
236530
|
+
async function createRelease(input$3) {
|
|
236531
|
+
await execInherit("gh", [
|
|
236532
|
+
"release",
|
|
236533
|
+
"create",
|
|
236534
|
+
input$3.tag,
|
|
236535
|
+
"--target",
|
|
236536
|
+
input$3.target,
|
|
236537
|
+
"--notes",
|
|
236538
|
+
input$3.notes
|
|
236539
|
+
]);
|
|
236540
|
+
}
|
|
236541
|
+
async function getRepoRoot() {
|
|
236542
|
+
return (await exec("git", ["rev-parse", "--show-toplevel"])).trim();
|
|
236543
|
+
}
|
|
236544
|
+
async function fetchBranchAndTags(branch) {
|
|
236545
|
+
await exec("git", [
|
|
236546
|
+
"fetch",
|
|
236547
|
+
"origin",
|
|
236548
|
+
branch,
|
|
236549
|
+
"--tags",
|
|
236550
|
+
"--force"
|
|
236551
|
+
]);
|
|
236552
|
+
}
|
|
236553
|
+
async function assertLocalCommitsMatch(fromTag, toSha, githubHashes) {
|
|
236554
|
+
for (const ref of fromTag === null ? [toSha] : [fromTag, toSha]) try {
|
|
236555
|
+
await exec("git", [
|
|
236556
|
+
"cat-file",
|
|
236557
|
+
"-e",
|
|
236558
|
+
`${ref}^{commit}`
|
|
236559
|
+
]);
|
|
236560
|
+
} catch {
|
|
236561
|
+
throw new Error(`'${ref}' from GitHub is not in your local repository. Fetch it and try again.`);
|
|
236562
|
+
}
|
|
236563
|
+
const range$2 = fromTag === null ? toSha : `${fromTag}..${toSha}`;
|
|
236564
|
+
const localHashes = (await exec("git", ["rev-list", range$2])).split("\n").filter((line) => line.length > 0);
|
|
236565
|
+
const githubSet = new Set(githubHashes);
|
|
236566
|
+
if (!(localHashes.length === githubHashes.length && localHashes.every((hash$5) => githubSet.has(hash$5)))) throw new Error(`Local commits do not match GitHub for ${range$2}: ${localHashes.length} local, ${githubHashes.length} on GitHub.`);
|
|
236567
|
+
}
|
|
236568
|
+
var prompt_default = "You generate concise, well-structured, easy-to-read release notes from git commit history.\n\n**Audience**: end users, product managers, and non-technical stakeholders — not engineers.\n\n## What to include:\n\n- Include every change a user could notice, however small — new features, fixes, improvements, changed behavior. When in doubt, include it: your job is to translate a change into user terms, not to omit it.\n- Drop a change only when it is purely internal with zero user impact (refactors, renames, formatting, internal tooling, dependency/version bumps). Drop it silently — never mention it, not even as a generic \"internal improvements\" note.\n\n## How to write:\n\n- Describe WHAT changed and WHY it matters, not HOW it is implemented.\n- Write for someone who doesn't know the product is built with code. Use everyday language, framing each change as what the user can now do, see, or no longer worry about.\n- Avoid all technical jargon, library/framework names, and implementation-specific terminology.\n\n## Never expose:\n\n- Internal details: source code, file names or paths, class/function/variable names, API routes, database tables, config keys, dependency or package names, or any other internal identifier. Restate the change in user terms instead — omit it only if it has no user impact at all.\n- Sensitive information: credentials, secrets, tokens, internal URLs, private endpoints, or environment-specific values.\n\n## Output format:\n\n- Group related changes under `##` headings (e.g. \"New Features\", \"Bug Fixes\", \"Improvements\", \"Breaking Changes\"), with each change as a bullet point.\n- Put breaking changes in their own section and explain how users should adapt.\n- Output ONLY the release note content — no preamble, commentary, \"Here is...\", or top-level \"Release Note\" title.\n- Separate sections with `##` headings, never `---`.\n- If and ONLY if every change is purely internal with zero user impact, output exactly `_No notable changes in this release._` and nothing else — never explaining or listing what was dropped. If even one change is user-noticeable, write the note normally.\n";
|
|
236569
|
+
const MAP_CONCURRENCY = 3;
|
|
236570
|
+
async function summarizeChunks(languageModel, instructions, chunks) {
|
|
236571
|
+
const summaries = new Array(chunks.length);
|
|
236572
|
+
let nextChunk = 0;
|
|
236573
|
+
async function worker() {
|
|
236574
|
+
while (nextChunk < chunks.length) {
|
|
236575
|
+
const index$2 = nextChunk++;
|
|
236576
|
+
summaries[index$2] = (await generateText({
|
|
236577
|
+
model: languageModel,
|
|
236578
|
+
instructions,
|
|
236579
|
+
messages: [{
|
|
236580
|
+
role: "user",
|
|
236581
|
+
content: chunks[index$2]
|
|
236582
|
+
}]
|
|
236583
|
+
})).text.trim();
|
|
236584
|
+
}
|
|
236585
|
+
}
|
|
236586
|
+
await Promise.all(Array.from({ length: Math.min(MAP_CONCURRENCY, chunks.length) }, worker));
|
|
236587
|
+
return summaries.map((summary, index$2) => `Part ${index$2 + 1}:\n${summary}`).join("\n\n");
|
|
236588
|
+
}
|
|
236589
|
+
const CHARS_PER_TOKEN$1 = 4;
|
|
236590
|
+
const MIN_TOOL_OUTPUT_TOKENS = 200;
|
|
236591
|
+
const PROMPT_RESERVE_TOKENS$1 = 800;
|
|
236592
|
+
const MIN_CHUNK_BUDGET_TOKENS = 1e3;
|
|
236593
|
+
const MAX_TOC_LINES = 500;
|
|
236594
|
+
const BUDGET_EXHAUSTED = "Size limit reached. Write the release notes with the information you already have.";
|
|
236595
|
+
const NOISE_FILE_EXCLUDES = [
|
|
236596
|
+
":(top,exclude)*.lock",
|
|
236597
|
+
":(top,exclude)*-lock.*",
|
|
236598
|
+
":(top,exclude)*.min.js",
|
|
236599
|
+
":(top,exclude)*.min.mjs",
|
|
236600
|
+
":(top,exclude)*.min.cjs",
|
|
236601
|
+
":(top,exclude)*.min.css",
|
|
236602
|
+
":(top,exclude)*.map"
|
|
236603
|
+
];
|
|
236604
|
+
const diffSummaryPrompt = `You summarize parts of a large git diff for a release notes generator.
|
|
236605
|
+
Describe WHAT changed and any effect a user could notice.
|
|
236606
|
+
Be factual and terse. Do NOT write release notes. Maximum 120 words.`;
|
|
236607
|
+
function parseLsTree(output$3) {
|
|
236608
|
+
return output$3.split("\n").filter((line) => line.length > 0).map((line) => {
|
|
236609
|
+
const tabIdx = line.indexOf(" ");
|
|
236610
|
+
return tabIdx >= 0 ? line.slice(tabIdx + 1) : "";
|
|
236611
|
+
});
|
|
236612
|
+
}
|
|
236613
|
+
function createReleaseTools(languageModel, limits) {
|
|
236614
|
+
let remainingTokens = limits.maxTokens;
|
|
236615
|
+
function spend(text$1) {
|
|
236616
|
+
const maxChars = remainingTokens * CHARS_PER_TOKEN$1;
|
|
236617
|
+
const fitted = text$1.length <= maxChars ? text$1 : `${text$1.slice(0, maxChars)}\n[... truncated to fit the size limit]`;
|
|
236618
|
+
remainingTokens -= estimateTokens(fitted);
|
|
236619
|
+
return fitted;
|
|
236620
|
+
}
|
|
236621
|
+
async function readCommitDiff(ref) {
|
|
236622
|
+
const fullDiff = await exec("git", [
|
|
236623
|
+
"show",
|
|
236624
|
+
"--no-color",
|
|
236625
|
+
"--pretty=format:",
|
|
236626
|
+
ref
|
|
236627
|
+
]);
|
|
236628
|
+
if (estimateTokens(fullDiff) <= remainingTokens) return fullDiff;
|
|
236629
|
+
const tocLines = (await exec("git", [
|
|
236630
|
+
"show",
|
|
236631
|
+
"--no-color",
|
|
236632
|
+
"--pretty=format:",
|
|
236633
|
+
"--numstat",
|
|
236634
|
+
ref
|
|
236635
|
+
])).trim().split("\n");
|
|
236636
|
+
const toc = tocLines.length <= MAX_TOC_LINES ? tocLines.join("\n") : [...tocLines.slice(0, MAX_TOC_LINES), `[... ${tocLines.length - MAX_TOC_LINES} more files]`].join("\n");
|
|
236637
|
+
const header = `This diff is too large to show in full.\n\nChanged files (additions, deletions, path):\n${toc}`;
|
|
236638
|
+
const minimizedDiff = capFileSections(await exec("git", [
|
|
236639
|
+
"show",
|
|
236640
|
+
"--no-color",
|
|
236641
|
+
"--pretty=format:",
|
|
236642
|
+
"--unified=1",
|
|
236643
|
+
ref,
|
|
236644
|
+
"--",
|
|
236645
|
+
":/",
|
|
236646
|
+
...NOISE_FILE_EXCLUDES
|
|
236647
|
+
]), limits.perFileCap);
|
|
236648
|
+
const minimized = `${header}\n\nDiff with less context, generated files excluded, and long files truncated:\n${minimizedDiff}`;
|
|
236649
|
+
if (estimateTokens(minimized) <= remainingTokens) return minimized;
|
|
236650
|
+
return `${header}\n\nSummaries of the diff:\n${await summarizeChunks(languageModel, diffSummaryPrompt, splitDiffIntoChunks(minimizedDiff, Math.max(limits.maxTokens - estimateTokens(toc) - PROMPT_RESERVE_TOKENS$1, MIN_CHUNK_BUDGET_TOKENS)).map((chunk$1) => `All changed files:\n${toc}\n\nChanges (part of a larger diff):\n${chunk$1}`))}`;
|
|
236651
|
+
}
|
|
236652
|
+
return {
|
|
236653
|
+
check_diff: tool({
|
|
236654
|
+
description: "Get the full diff of a specific commit. Returns the patch showing all changes (additions, deletions, modifications) introduced by the commit.",
|
|
236655
|
+
inputSchema: object$3({ commithash: string$5().describe("The commit hash, tag, branch, or any other git ref resolvable by git rev-parse.") }),
|
|
236656
|
+
execute: async ({ commithash }) => {
|
|
236657
|
+
if (remainingTokens < MIN_TOOL_OUTPUT_TOKENS) return { error: BUDGET_EXHAUSTED };
|
|
236658
|
+
return spend(await readCommitDiff(commithash));
|
|
236659
|
+
}
|
|
236660
|
+
}),
|
|
236661
|
+
browse_code: tool({
|
|
236662
|
+
description: "Browse the code at a specific commit. If the path points to a file, returns its content. If the path points to a folder, returns the list of files and sub-folders it contains. Pass an empty string for the path to browse the repository root.",
|
|
236663
|
+
inputSchema: object$3({
|
|
236664
|
+
commithash: string$5().describe("The commit hash, tag, branch, or any other git ref resolvable by git rev-parse."),
|
|
236665
|
+
path: string$5().describe("Path to a file or folder within the repository, relative to the repo root. Use forward slashes. Pass an empty string for the root.")
|
|
236666
|
+
}),
|
|
236667
|
+
execute: async ({ commithash, path: path$4 }) => {
|
|
236668
|
+
if (remainingTokens < MIN_TOOL_OUTPUT_TOKENS) return { error: BUDGET_EXHAUSTED };
|
|
236669
|
+
const normalized = path$4.replace(/^\/+|\/+$/g, "");
|
|
236670
|
+
if (normalized === "") return {
|
|
236671
|
+
type: "folder",
|
|
236672
|
+
items: parseLsTree(spend(await exec("git", ["ls-tree", commithash])))
|
|
236673
|
+
};
|
|
236674
|
+
const ref = `${commithash}:${normalized}`;
|
|
236675
|
+
let objectType$2;
|
|
236676
|
+
try {
|
|
236677
|
+
objectType$2 = (await exec("git", [
|
|
236678
|
+
"cat-file",
|
|
236679
|
+
"-t",
|
|
236680
|
+
ref
|
|
236681
|
+
])).trim();
|
|
236682
|
+
} catch {
|
|
236683
|
+
return { error: "doesn't exists" };
|
|
236684
|
+
}
|
|
236685
|
+
if (objectType$2 === "tree") return {
|
|
236686
|
+
type: "folder",
|
|
236687
|
+
items: parseLsTree(spend(await exec("git", ["ls-tree", ref])))
|
|
236688
|
+
};
|
|
236689
|
+
if (objectType$2 === "blob") return {
|
|
236690
|
+
type: "file",
|
|
236691
|
+
content: spend(await exec("git", ["show", ref]))
|
|
236692
|
+
};
|
|
236693
|
+
return { error: "doesn't exists" };
|
|
236694
|
+
}
|
|
236695
|
+
})
|
|
236696
|
+
};
|
|
236697
|
+
}
|
|
236698
|
+
function capFileSections(diff, perFileCap) {
|
|
236699
|
+
return ("\n" + diff.trimStart()).split("\ndiff --git ").slice(1).map((section) => {
|
|
236700
|
+
const lines = `diff --git ${section}`.split("\n");
|
|
236701
|
+
const bodyLines = lines.length - 1;
|
|
236702
|
+
if (bodyLines <= perFileCap) return lines.join("\n");
|
|
236703
|
+
return [...lines.slice(0, perFileCap + 1), `[... ${bodyLines - perFileCap} more lines truncated]`].join("\n");
|
|
236704
|
+
}).join("\n");
|
|
236705
|
+
}
|
|
236706
|
+
const EMPTY_RELEASE_NOTES = "_No notable changes in this release._";
|
|
236707
|
+
const PROMPT_RESERVE_TOKENS = 800;
|
|
236708
|
+
const CHARS_PER_TOKEN = 4;
|
|
236709
|
+
const commitsSummaryPrompt = `You summarize part of a git commit history for a release notes generator.
|
|
236710
|
+
List every change a user could notice, each with the commit hash(es) it comes from.
|
|
236711
|
+
Skip purely internal changes. Be factual and terse. Do NOT write release notes. Maximum 200 words.`;
|
|
236712
|
+
async function generateReleaseNotes(languageModel, commits, limits) {
|
|
236713
|
+
const commitsContext = await buildCommitsContext(languageModel, commits, limits.maxTokens);
|
|
236714
|
+
return (await generateText({
|
|
236715
|
+
model: languageModel,
|
|
236716
|
+
instructions: prompt_default,
|
|
236717
|
+
tools: createReleaseTools(languageModel, limits),
|
|
236718
|
+
stopWhen: isStepCount(1e3),
|
|
236719
|
+
messages: [{
|
|
236720
|
+
role: "user",
|
|
236721
|
+
content: [{
|
|
236722
|
+
type: "text",
|
|
236723
|
+
text: "Here are the commits related to the release:"
|
|
236724
|
+
}, {
|
|
236725
|
+
type: "text",
|
|
236726
|
+
text: commitsContext
|
|
236727
|
+
}]
|
|
236728
|
+
}]
|
|
236729
|
+
})).text.trim();
|
|
236730
|
+
}
|
|
236731
|
+
async function buildCommitsContext(languageModel, commits, maxTokens) {
|
|
236732
|
+
const fullList = commits.map((commit) => formatCommit(commit, commit.message)).join("\n\n");
|
|
236733
|
+
if (estimateTokens(fullList) <= maxTokens) return fullList;
|
|
236734
|
+
const subjectList = commits.map((commit) => formatCommit(commit, commit.message.split("\n")[0])).join("\n\n");
|
|
236735
|
+
if (estimateTokens(subjectList) <= maxTokens) return `Only commit subjects are shown because the full messages are too large. Use check_diff for details.\n\n${subjectList}`;
|
|
236736
|
+
const chunkBudget = Math.max(maxTokens - PROMPT_RESERVE_TOKENS, 1e3);
|
|
236737
|
+
const chunks = [];
|
|
236738
|
+
let current = "";
|
|
236739
|
+
for (const commit of commits) {
|
|
236740
|
+
const entry = formatCommit(commit, commit.message).slice(0, chunkBudget * CHARS_PER_TOKEN);
|
|
236741
|
+
if (current.length > 0 && estimateTokens(`${current}\n\n${entry}`) > chunkBudget) {
|
|
236742
|
+
chunks.push(current);
|
|
236743
|
+
current = "";
|
|
236744
|
+
}
|
|
236745
|
+
current = current.length === 0 ? entry : `${current}\n\n${entry}`;
|
|
236746
|
+
}
|
|
236747
|
+
if (current.length > 0) chunks.push(current);
|
|
236748
|
+
const summaries = await summarizeChunks(languageModel, commitsSummaryPrompt, chunks);
|
|
236749
|
+
return `There are ${commits.length} commits, too many to show in full, so here are summaries of them. Use check_diff for details.\n\n${summaries}`;
|
|
236750
|
+
}
|
|
236751
|
+
function formatCommit(commit, message) {
|
|
236752
|
+
return [
|
|
236753
|
+
`- Commithash: ${commit.hash}`,
|
|
236754
|
+
` - > ${message.replaceAll("\n", "\n > ")}`,
|
|
236755
|
+
` - Timestamp: ${commit.date}`,
|
|
236756
|
+
` - Author: ${commit.authorName} <${commit.authorEmail}>`
|
|
236757
|
+
].join("\n");
|
|
236758
|
+
}
|
|
236759
|
+
const renderReleaseNotes = createRenderer({
|
|
236760
|
+
width: 80,
|
|
236761
|
+
listIndent: 2
|
|
236762
|
+
});
|
|
236763
|
+
async function releaseController(tagArg, options = {}) {
|
|
236764
|
+
if (options.yolo && tagArg === void 0) throw new Error("A release tag is required with --yolo.");
|
|
236765
|
+
const config$5 = await loadConfig(await getRepoRoot());
|
|
236766
|
+
const languageModel = resolveLanguageModel(resolveModelConfig(config$5.models, options.model ?? "default"));
|
|
236767
|
+
const releases = await listReleases(100);
|
|
236768
|
+
let tag = tagArg?.trim() ?? "";
|
|
236769
|
+
if (tagArg === void 0) {
|
|
236770
|
+
printRecentReleases(releases.slice(0, 5).reverse());
|
|
236771
|
+
tag = (await dist_default$1({
|
|
236772
|
+
message: "Release tag",
|
|
236773
|
+
theme: selectionTheme,
|
|
236774
|
+
validate: (value) => value.trim().length > 0 || "Release tag cannot be empty."
|
|
236775
|
+
})).trim();
|
|
236776
|
+
}
|
|
236777
|
+
if (tag.length === 0) throw new Error("Release tag cannot be empty.");
|
|
236778
|
+
const exists$2 = await releaseExists(tag);
|
|
236779
|
+
if (exists$2 && !options.force) {
|
|
236780
|
+
if (!await dist_default({
|
|
236781
|
+
message: source_default.yellow(`Release ${tag} already exists. Recreate it?`),
|
|
236782
|
+
default: false,
|
|
236783
|
+
theme: selectionTheme
|
|
236784
|
+
})) {
|
|
236785
|
+
console.log("Release creation cancelled.");
|
|
236786
|
+
return;
|
|
236787
|
+
}
|
|
236788
|
+
}
|
|
236789
|
+
const branch = await getDefaultBranch();
|
|
236790
|
+
if (branch.length === 0) throw new Error("Could not determine the default branch.");
|
|
236791
|
+
const tagIndex = releases.findIndex((release) => release.tagName === tag);
|
|
236792
|
+
const previousTag = (tagIndex === -1 ? releases[0] : releases[tagIndex + 1])?.tagName ?? null;
|
|
236793
|
+
const headSha = await getBranchHeadSha(branch);
|
|
236794
|
+
const commits = await runWithLoading("Loading commits from GitHub", () => listReleaseCommits(previousTag, headSha));
|
|
236795
|
+
await runWithLoading("Fetching commits", () => fetchBranchAndTags(branch));
|
|
236796
|
+
await assertLocalCommitsMatch(previousTag, headSha, commits.map((commit) => commit.hash));
|
|
236797
|
+
console.log(source_default.dim(`${commits.length} commit(s) since ${previousTag ?? "the first commit"}`));
|
|
236798
|
+
let notes = "";
|
|
236799
|
+
while (true) {
|
|
236800
|
+
notes = commits.length === 0 ? EMPTY_RELEASE_NOTES : await runWithLoading("Generating release notes", () => generateReleaseNotes(languageModel, commits, {
|
|
236801
|
+
maxTokens: config$5.maxDiffTokens ?? DEFAULT_MAX_DIFF_TOKENS,
|
|
236802
|
+
perFileCap: config$5.perFileCap ?? DEFAULT_PER_FILE_CAP
|
|
236803
|
+
}));
|
|
236804
|
+
console.log(renderReleaseNotes(notes).trim());
|
|
236805
|
+
console.log("");
|
|
236806
|
+
if (options.yolo || await acceptGenerated(`Create release ${source_default.cyan.bold(tag)} on ${source_default.yellow.bold(branch)}`, "generate new release notes")) break;
|
|
236807
|
+
}
|
|
236808
|
+
if (notes.length === 0) throw new Error("The selected model returned empty release notes.");
|
|
236809
|
+
if (exists$2) await deleteRelease(tag);
|
|
236810
|
+
await createRelease({
|
|
236811
|
+
tag,
|
|
236812
|
+
target: headSha,
|
|
236813
|
+
notes
|
|
236814
|
+
});
|
|
236815
|
+
}
|
|
236816
|
+
function printRecentReleases(releases) {
|
|
236817
|
+
if (releases.length === 0) return;
|
|
236818
|
+
const longestTag = Math.max(...releases.map((release) => release.tagName.length));
|
|
236819
|
+
console.log(source_default.blue.bold("Recent:"));
|
|
236820
|
+
for (const release of releases) {
|
|
236821
|
+
const age = prettyMilliseconds(Date.now() - Date.parse(release.publishedAt), {
|
|
236822
|
+
verbose: true,
|
|
236823
|
+
unitCount: 1
|
|
236824
|
+
});
|
|
236825
|
+
console.log(source_default.cyan(release.tagName.padEnd(longestTag)), source_default.dim(`${age} ago`));
|
|
236826
|
+
}
|
|
236827
|
+
console.log("");
|
|
236325
236828
|
}
|
|
236326
236829
|
async function showStylesController() {
|
|
236327
236830
|
const config$5 = await loadConfig();
|
|
@@ -236358,26 +236861,54 @@ function handleError(fn$1) {
|
|
|
236358
236861
|
}
|
|
236359
236862
|
});
|
|
236360
236863
|
}
|
|
236361
|
-
const app = new Command().enablePositionalOptions().name("gityo").description("Stage changes, generate a commit message, create a commit, and run a post-commit git command.").option("-i, --input <input>", "Use the provided input as the commit message.").option("-s, --style <style>", "Commit message style key to use (defaults to \"default\").").option("
|
|
236864
|
+
const app = new Command().enablePositionalOptions().name("gityo").description("Stage changes, generate a commit message, create a commit, and run a post-commit git command.").option("-i, --input <input>", "Use the provided input as the commit message.").option("-s, --style <style>", "Commit message style key to use (defaults to \"default\").").option("--model <model>", "Model key from config to use (defaults to \"default\").").option("-g, --generate", "Generate a commit message and commit without asking.").option("-p, --post", "Run the post-commit git command without asking.").option("-S, --staged", "Commit only staged files. Fails if nothing is staged.").option("-A, --all", "Commit all changes, not just staged files.").option("-y, --yolo", "Skip all questions, and generate message, commit, run post command. [Will fail if no model available]").version(`v${version$4}`, "-v, --version", "Show the current version.").action((options) => {
|
|
236362
236865
|
if (options.generate && options.input) {
|
|
236363
236866
|
console.error("Cannot use --generate and --input together.");
|
|
236364
236867
|
process.exit(1);
|
|
236365
236868
|
}
|
|
236366
|
-
|
|
236869
|
+
if (options.staged && options.all) {
|
|
236870
|
+
console.error("Cannot use --staged and --all together.");
|
|
236871
|
+
process.exit(1);
|
|
236872
|
+
}
|
|
236873
|
+
handleError(() => mainController({
|
|
236874
|
+
...options,
|
|
236875
|
+
scope: options.all ? "everything" : options.staged ? "staged-only" : "staged-or-changes"
|
|
236876
|
+
}));
|
|
236367
236877
|
});
|
|
236368
|
-
app.command("commit").description("Stage changes, generate a commit message, create a commit, and run a post-commit git command.").option("-i, --input <input>", "Use the provided input as the commit message.").option("-s, --style <style>", "Commit message style key to use (defaults to \"default\").").option("
|
|
236878
|
+
app.command("commit").description("Stage changes, generate a commit message, create a commit, and run a post-commit git command.").option("-i, --input <input>", "Use the provided input as the commit message.").option("-s, --style <style>", "Commit message style key to use (defaults to \"default\").").option("--model <model>", "Model key from config to use (defaults to \"default\").").option("-g, --generate", "Generate a commit message and commit without asking.").option("-p, --post", "Run the post-commit git command without asking.").option("-S, --staged", "Commit only staged files. Fails if nothing is staged.").option("-A, --all", "Commit all changes, not just staged files.").option("-y, --yolo", "Skip all questions, and generate message, commit, run post command. [Will fail if no model available]").action((options) => {
|
|
236369
236879
|
if (options.generate && options.input) {
|
|
236370
236880
|
console.error("Cannot use --generate and --input together.");
|
|
236371
236881
|
process.exit(1);
|
|
236372
236882
|
}
|
|
236373
|
-
|
|
236883
|
+
if (options.staged && options.all) {
|
|
236884
|
+
console.error("Cannot use --staged and --all together.");
|
|
236885
|
+
process.exit(1);
|
|
236886
|
+
}
|
|
236887
|
+
handleError(() => mainController({
|
|
236888
|
+
...options,
|
|
236889
|
+
scope: options.all ? "everything" : options.staged ? "staged-only" : "staged-or-changes"
|
|
236890
|
+
}));
|
|
236374
236891
|
});
|
|
236375
236892
|
const pr = app.command("pr").description("Create and merge GitHub pull requests.");
|
|
236376
|
-
pr.command("create").description("Create a pull request with an AI-generated title and body.").argument("[base-branch]", "Base branch to merge into (defaults to the repository default branch).").argument("[head-branch]", "Head branch with the changes (defaults to the current branch).").option("
|
|
236893
|
+
pr.command("create").description("Create a pull request with an AI-generated title and body.").argument("[base-branch]", "Base branch to merge into (defaults to the repository default branch).").argument("[head-branch]", "Head branch with the changes (defaults to the current branch).").option("--model <model>", "Model key from config to use (defaults to \"default\").").option("-t, --title-style <style>", "Title style key to use (defaults to \"default\").").option("-b, --body-style <style>", "Body style key to use (defaults to \"default\").").option("-y, --yolo", "Create the pull request without asking for confirmation.").option("-A, --all", "Commit all local changes, not just staged files, before continuing.").option("-w, --web", "Open the pull request in a browser.").action((baseBranch, headBranch, options) => {
|
|
236377
236894
|
handleError(() => createPullRequestController(baseBranch, headBranch, options));
|
|
236378
236895
|
});
|
|
236379
|
-
pr.command("merge").description("Create the pull request if needed, then merge it.").argument("[base-branch]", "Base branch to merge into (defaults to the repository default branch).").argument("[head-branch]", "Head branch with the changes (defaults to the current branch).").option("
|
|
236380
|
-
|
|
236896
|
+
pr.command("merge").description("Create the pull request if needed, then merge it.").argument("[base-branch]", "Base branch to merge into (defaults to the repository default branch).").argument("[head-branch]", "Head branch with the changes (defaults to the current branch).").option("--model <model>", "Model key from config to use (defaults to \"default\").").option("-t, --title-style <style>", "Title style key to use (defaults to \"default\").").option("-b, --body-style <style>", "Body style key to use (defaults to \"default\").").option("-y, --yolo", "Merge the pull request without asking for confirmation.").option("-A, --all", "Commit all local changes, not just staged files, before continuing.").option("-m, --merge", "Merge the commits with the base branch.").option("-r, --rebase", "Rebase the commits onto the base branch.").option("-s, --squash", "Squash the commits into one commit.").action((baseBranch, headBranch, options) => {
|
|
236897
|
+
if ([
|
|
236898
|
+
options.merge,
|
|
236899
|
+
options.rebase,
|
|
236900
|
+
options.squash
|
|
236901
|
+
].filter(Boolean).length > 1) {
|
|
236902
|
+
console.error("Use only one of --merge, --rebase, or --squash.");
|
|
236903
|
+
process.exit(1);
|
|
236904
|
+
}
|
|
236905
|
+
handleError(() => mergePullRequestController(baseBranch, headBranch, {
|
|
236906
|
+
...options,
|
|
236907
|
+
mergeMethod: options.merge ? "merge" : options.rebase ? "rebase" : options.squash ? "squash" : void 0
|
|
236908
|
+
}));
|
|
236909
|
+
});
|
|
236910
|
+
app.command("release").description("Create a GitHub release with AI-generated release notes.").argument("[tag]", "Release tag to create (asks when omitted).").option("--model <model>", "Model key from config to use (defaults to \"default\").").option("-y, --yolo", "Create the release without asking for confirmation (requires a tag).").option("-f, --force", "Recreate the release without asking if it already exists.").action((tag, options) => {
|
|
236911
|
+
handleError(() => releaseController(tag, options));
|
|
236381
236912
|
});
|
|
236382
236913
|
app.command("config").description("Show where to manage your gityo configuration.").action(() => {
|
|
236383
236914
|
handleError(showConfigController);
|