git-coco 0.6.2 → 0.6.3
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/dist/index.d.ts +14 -17
- package/dist/index.esm.mjs +143 -112
- package/dist/index.js +143 -112
- package/package.json +2 -2
package/dist/index.d.ts
CHANGED
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
/// <reference types="yargs" />
|
|
2
2
|
import * as yargs from 'yargs';
|
|
3
3
|
import { Argv } from 'yargs';
|
|
4
|
+
import { TiktokenModel } from 'langchain/dist/types/openai-types';
|
|
4
5
|
import { HuggingFaceInference } from 'langchain/llms/hf';
|
|
5
6
|
import { BaseLLMParams } from 'langchain/llms/base';
|
|
6
7
|
import { OpenAIInput, AzureOpenAIInput, OpenAI } from 'langchain/llms/openai';
|
|
7
8
|
import { SimpleGit } from 'simple-git';
|
|
8
9
|
import { Color } from 'chalk';
|
|
9
|
-
import GPT3NodeTokenizer from 'gpt3-tokenizer';
|
|
10
10
|
|
|
11
|
+
type ServiceProvider = "openai" | "huggingface";
|
|
12
|
+
type ServiceModel = TiktokenModel;
|
|
13
|
+
type Service = `${ServiceProvider}/${ServiceModel}`;
|
|
11
14
|
interface Config$1 {
|
|
12
15
|
/**
|
|
13
16
|
* The LLM model to use for generating results.
|
|
@@ -18,7 +21,7 @@ interface Config$1 {
|
|
|
18
21
|
* @example 'openai/gpt-3.5-turbo'
|
|
19
22
|
* @example 'huggingface/bigscience/bloom'
|
|
20
23
|
**/
|
|
21
|
-
|
|
24
|
+
service: Service;
|
|
22
25
|
/**
|
|
23
26
|
* The OpenAI API key.
|
|
24
27
|
*/
|
|
@@ -99,10 +102,10 @@ interface BaseArgvOptions {
|
|
|
99
102
|
}
|
|
100
103
|
interface BaseCommandOptions extends BaseArgvOptions {
|
|
101
104
|
[x: string]: unknown;
|
|
102
|
-
|
|
103
|
-
openAIApiKey:
|
|
104
|
-
huggingFaceHubApiKey:
|
|
105
|
-
tokenLimit:
|
|
105
|
+
service: Config$1['service'];
|
|
106
|
+
openAIApiKey: Config$1['openAIApiKey'];
|
|
107
|
+
huggingFaceHubApiKey: Config$1['huggingFaceHubApiKey'];
|
|
108
|
+
tokenLimit: Config$1['tokenLimit'];
|
|
106
109
|
}
|
|
107
110
|
|
|
108
111
|
interface CommitOptions$1 extends BaseCommandOptions {
|
|
@@ -169,7 +172,7 @@ declare const _default: {
|
|
|
169
172
|
* @param configuration
|
|
170
173
|
* @returns LLM Model
|
|
171
174
|
*/
|
|
172
|
-
declare function
|
|
175
|
+
declare function getLlm(service: Config$1['service'], key: string, fields?: (Partial<OpenAIInput> & Partial<AzureOpenAIInput> & BaseLLMParams) | undefined): OpenAI | HuggingFaceInference;
|
|
173
176
|
|
|
174
177
|
interface LoggerOptions {
|
|
175
178
|
color?: typeof Color;
|
|
@@ -194,14 +197,8 @@ declare class Logger {
|
|
|
194
197
|
stopSpinner(message?: string | undefined, options?: SpinnerOptions): Logger;
|
|
195
198
|
}
|
|
196
199
|
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
*
|
|
200
|
-
* @see https://github.com/botisan-ai/gpt3-tokenizer/issues/18
|
|
201
|
-
*
|
|
202
|
-
* @returns {GPT3NodeTokenizer} The GPT3NodeTokenizer instance.
|
|
203
|
-
*/
|
|
204
|
-
declare const getTokenizer: () => GPT3NodeTokenizer;
|
|
200
|
+
type TokenCounter = Awaited<ReturnType<typeof getTokenCounter>>;
|
|
201
|
+
declare const getTokenCounter: (modelName: ServiceModel) => Promise<(text: string) => number>;
|
|
205
202
|
|
|
206
203
|
type FileChangeStatus = 'modified' | 'renamed' | 'added' | 'deleted' | 'untracked' | 'unknown';
|
|
207
204
|
interface FileChange {
|
|
@@ -228,8 +225,8 @@ interface DirectoryDiff {
|
|
|
228
225
|
tokenCount: number;
|
|
229
226
|
}
|
|
230
227
|
interface BaseParserOptions {
|
|
231
|
-
tokenizer:
|
|
232
|
-
|
|
228
|
+
tokenizer: TokenCounter;
|
|
229
|
+
llm: ReturnType<typeof getLlm>;
|
|
233
230
|
git: SimpleGit;
|
|
234
231
|
logger: Logger;
|
|
235
232
|
}
|
package/dist/index.esm.mjs
CHANGED
|
@@ -8,7 +8,6 @@ import { loadSummarizationChain, LLMChain } from 'langchain/chains';
|
|
|
8
8
|
import { OpenAI } from 'langchain/llms/openai';
|
|
9
9
|
import { RecursiveCharacterTextSplitter } from 'langchain/text_splitter';
|
|
10
10
|
import { createTwoFilesPatch } from 'diff';
|
|
11
|
-
import GPT3NodeTokenizer from 'gpt3-tokenizer';
|
|
12
11
|
import chalk from 'chalk';
|
|
13
12
|
import ora from 'ora';
|
|
14
13
|
import now from 'performance-now';
|
|
@@ -23,6 +22,7 @@ import * as os from 'os';
|
|
|
23
22
|
import os__default from 'os';
|
|
24
23
|
import * as ini from 'ini';
|
|
25
24
|
import { simpleGit } from 'simple-git';
|
|
25
|
+
import { encoding_for_model } from 'tiktoken';
|
|
26
26
|
|
|
27
27
|
/**
|
|
28
28
|
* Extract the path from a file path string.
|
|
@@ -84,7 +84,7 @@ async function summarizeDirectoryDiff(directory, { chain, textSplitter, tokenize
|
|
|
84
84
|
returnIntermediateSteps: true,
|
|
85
85
|
},
|
|
86
86
|
});
|
|
87
|
-
const newTokenTotal = tokenizer
|
|
87
|
+
const newTokenTotal = tokenizer(directorySummary);
|
|
88
88
|
return {
|
|
89
89
|
diffs: directory.diffs,
|
|
90
90
|
path: directory.path,
|
|
@@ -215,9 +215,7 @@ async function collectDiffs(node, getFileDiff, tokenizer, logger) {
|
|
|
215
215
|
// Collect diffs for the files of the current node
|
|
216
216
|
const diffPromises = node.files.map(async (nodeFile) => {
|
|
217
217
|
const diff = await getFileDiff(nodeFile);
|
|
218
|
-
|
|
219
|
-
const tokenizedDiff = tokenizer.encode(diff).text;
|
|
220
|
-
const tokenCount = tokenizedDiff.length;
|
|
218
|
+
const tokenCount = tokenizer(diff);
|
|
221
219
|
logger.verbose(`Collected diff for ${nodeFile.filePath} (${tokenCount} tokens)`, {
|
|
222
220
|
color: 'magenta',
|
|
223
221
|
});
|
|
@@ -241,18 +239,29 @@ async function collectDiffs(node, getFileDiff, tokenizer, logger) {
|
|
|
241
239
|
};
|
|
242
240
|
}
|
|
243
241
|
|
|
242
|
+
function getModelAndProviderFromService(service) {
|
|
243
|
+
const [provider, model] = service.split(/\/(.*)/s);
|
|
244
|
+
if (!model || !provider) {
|
|
245
|
+
throw new Error(`Invalid service: ${service}`);
|
|
246
|
+
}
|
|
247
|
+
return { provider, model };
|
|
248
|
+
}
|
|
249
|
+
function getModelFromService(service) {
|
|
250
|
+
const { model } = getModelAndProviderFromService(service);
|
|
251
|
+
return model;
|
|
252
|
+
}
|
|
244
253
|
/**
|
|
245
254
|
* Get LLM Model Based on Configuration
|
|
246
255
|
* @param fields
|
|
247
256
|
* @param configuration
|
|
248
257
|
* @returns LLM Model
|
|
249
258
|
*/
|
|
250
|
-
function
|
|
251
|
-
const
|
|
259
|
+
function getLlm(service, key, fields) {
|
|
260
|
+
const { provider, model } = getModelAndProviderFromService(service);
|
|
252
261
|
if (!model) {
|
|
253
|
-
throw new Error(`Invalid
|
|
262
|
+
throw new Error(`Invalid LLM Service: ${service}`);
|
|
254
263
|
}
|
|
255
|
-
switch (
|
|
264
|
+
switch (provider) {
|
|
256
265
|
case 'huggingface':
|
|
257
266
|
return new HuggingFaceInference({
|
|
258
267
|
model: model,
|
|
@@ -271,16 +280,13 @@ function getModel(name, key, fields) {
|
|
|
271
280
|
}
|
|
272
281
|
/**
|
|
273
282
|
* Retrieve appropriate API key based on selected model
|
|
274
|
-
* @param
|
|
283
|
+
* @param service
|
|
275
284
|
* @param options
|
|
276
285
|
* @returns
|
|
277
286
|
*/
|
|
278
|
-
function getApiKeyForModel(
|
|
279
|
-
const
|
|
280
|
-
|
|
281
|
-
throw new Error(`Invalid model: ${name}`);
|
|
282
|
-
}
|
|
283
|
-
switch (llm) {
|
|
287
|
+
function getApiKeyForModel(service, options) {
|
|
288
|
+
const { provider } = getModelAndProviderFromService(service);
|
|
289
|
+
switch (provider) {
|
|
284
290
|
case 'huggingface':
|
|
285
291
|
return options.huggingFaceHubApiKey;
|
|
286
292
|
case 'openai':
|
|
@@ -302,7 +308,7 @@ function getTextSplitter(options = {}) {
|
|
|
302
308
|
* @param options
|
|
303
309
|
* @returns
|
|
304
310
|
*/
|
|
305
|
-
function
|
|
311
|
+
function getSummarizationChain(model, options = { type: 'map_reduce' }) {
|
|
306
312
|
return loadSummarizationChain(model, options);
|
|
307
313
|
}
|
|
308
314
|
function getPrompt({ template, variables, fallback }) {
|
|
@@ -402,9 +408,9 @@ async function getDiff(nodeFile, commit, { git, logger, }) {
|
|
|
402
408
|
}
|
|
403
409
|
|
|
404
410
|
const MAX_TOKENS_PER_SUMMARY = 2048;
|
|
405
|
-
async function fileChangeParser({ changes, commit, options: { tokenizer, git, model, logger }, }) {
|
|
411
|
+
async function fileChangeParser({ changes, commit, options: { tokenizer, git, llm: model, logger }, }) {
|
|
406
412
|
const textSplitter = getTextSplitter({ chunkSize: 2000, chunkOverlap: 125 });
|
|
407
|
-
const summarizationChain =
|
|
413
|
+
const summarizationChain = getSummarizationChain(model, {
|
|
408
414
|
type: 'map_reduce',
|
|
409
415
|
combineMapPrompt: SUMMARIZE_PROMPT,
|
|
410
416
|
combinePrompt: SUMMARIZE_PROMPT,
|
|
@@ -429,28 +435,6 @@ async function fileChangeParser({ changes, commit, options: { tokenizer, git, mo
|
|
|
429
435
|
return summary;
|
|
430
436
|
}
|
|
431
437
|
|
|
432
|
-
/**
|
|
433
|
-
* Wrapper around GPT3NodeTokenizer to handle default export.
|
|
434
|
-
*
|
|
435
|
-
* @see https://github.com/botisan-ai/gpt3-tokenizer/issues/18
|
|
436
|
-
*
|
|
437
|
-
* @returns {GPT3NodeTokenizer} The GPT3NodeTokenizer instance.
|
|
438
|
-
*/
|
|
439
|
-
const getTokenizer = () => {
|
|
440
|
-
let tokenizer;
|
|
441
|
-
// eslint-disable-next-line
|
|
442
|
-
// @ts-ignore
|
|
443
|
-
if (GPT3NodeTokenizer.default) {
|
|
444
|
-
// eslint-disable-next-line
|
|
445
|
-
// @ts-ignore
|
|
446
|
-
tokenizer = new GPT3NodeTokenizer.default({ type: 'gpt3' });
|
|
447
|
-
}
|
|
448
|
-
else {
|
|
449
|
-
tokenizer = new GPT3NodeTokenizer({ type: 'gpt3' });
|
|
450
|
-
}
|
|
451
|
-
return tokenizer;
|
|
452
|
-
};
|
|
453
|
-
|
|
454
438
|
class Logger {
|
|
455
439
|
constructor(config) {
|
|
456
440
|
this.config = config;
|
|
@@ -591,7 +575,7 @@ function removeUndefined(obj) {
|
|
|
591
575
|
* @type {Config}
|
|
592
576
|
*/
|
|
593
577
|
const DEFAULT_CONFIG = {
|
|
594
|
-
|
|
578
|
+
service: 'openai/gpt-4',
|
|
595
579
|
verbose: false,
|
|
596
580
|
tokenLimit: 1024,
|
|
597
581
|
summarizePrompt: SUMMARIZE_PROMPT.template,
|
|
@@ -602,7 +586,9 @@ const DEFAULT_CONFIG = {
|
|
|
602
586
|
defaultBranch: 'main',
|
|
603
587
|
};
|
|
604
588
|
/**
|
|
605
|
-
*
|
|
589
|
+
* Create a named export of all config keys for use in other modules.
|
|
590
|
+
*
|
|
591
|
+
* @see Currently used in `src/lib/config/services/env.ts` to validate all env vars.
|
|
606
592
|
*
|
|
607
593
|
* @type {string[]}
|
|
608
594
|
*/
|
|
@@ -741,7 +727,7 @@ function loadGitConfig(config) {
|
|
|
741
727
|
const gitConfigParsed = ini.parse(gitConfigRaw);
|
|
742
728
|
config = {
|
|
743
729
|
...config,
|
|
744
|
-
|
|
730
|
+
service: gitConfigParsed.coco?.model || config.service,
|
|
745
731
|
openAIApiKey: gitConfigParsed.coco?.openAIApiKey || config.openAIApiKey,
|
|
746
732
|
huggingFaceHubApiKey: gitConfigParsed.coco?.huggingFaceHubApiKey || config.huggingFaceHubApiKey,
|
|
747
733
|
tokenLimit: parseInt(gitConfigParsed.coco?.tokenLimit) || config.tokenLimit,
|
|
@@ -995,41 +981,51 @@ async function editResult(result, options) {
|
|
|
995
981
|
return result;
|
|
996
982
|
}
|
|
997
983
|
|
|
998
|
-
async function getUserReviewDecision() {
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
984
|
+
async function getUserReviewDecision({ label, descriptions, enableRetry = true, enableFullRetry = true, enableModifyPrompt = true, }) {
|
|
985
|
+
const choices = [
|
|
986
|
+
{
|
|
987
|
+
name: '✨ Looks good!',
|
|
988
|
+
value: 'approve',
|
|
989
|
+
description: descriptions?.approve || `Continue with the generated ${label}`,
|
|
990
|
+
},
|
|
991
|
+
{
|
|
992
|
+
name: '📝 Edit',
|
|
993
|
+
value: 'edit',
|
|
994
|
+
description: descriptions?.edit || `Edit the generated ${label} before proceeding`,
|
|
995
|
+
},
|
|
996
|
+
];
|
|
997
|
+
if (enableModifyPrompt) {
|
|
998
|
+
choices.push({
|
|
999
|
+
name: '🪶 Modify Prompt',
|
|
1000
|
+
value: 'modifyPrompt',
|
|
1001
|
+
description: descriptions?.modifyPrompt || `Modify the prompt template and regenerate the ${label}`,
|
|
1002
|
+
});
|
|
1003
|
+
}
|
|
1004
|
+
if (enableRetry) {
|
|
1005
|
+
choices.push({
|
|
1006
|
+
name: '🔄 Retry',
|
|
1007
|
+
value: 'retryMessageOnly',
|
|
1008
|
+
description: descriptions?.retryMessageOnly ||
|
|
1009
|
+
`Restart the function execution from generating the ${label}`,
|
|
1010
|
+
});
|
|
1011
|
+
}
|
|
1012
|
+
if (enableFullRetry) {
|
|
1013
|
+
choices.push({
|
|
1014
|
+
name: '🔄 Retry Full',
|
|
1015
|
+
value: 'retryFull',
|
|
1016
|
+
description: descriptions?.retryFull ||
|
|
1017
|
+
`Restart the function execution from the beginning, regenerating both the summary and ${label}`,
|
|
1018
|
+
});
|
|
1019
|
+
}
|
|
1020
|
+
choices.push({
|
|
1021
|
+
name: '💣 Cancel',
|
|
1022
|
+
value: 'cancel',
|
|
1023
|
+
description: descriptions?.cancel || `Cancel the ${label}`,
|
|
1032
1024
|
});
|
|
1025
|
+
return (await select({
|
|
1026
|
+
message: `Would you like to make any changes to the ${label}?`,
|
|
1027
|
+
choices,
|
|
1028
|
+
}));
|
|
1033
1029
|
}
|
|
1034
1030
|
|
|
1035
1031
|
async function editPrompt(options) {
|
|
@@ -1082,7 +1078,10 @@ async function generateAndReviewLoop({ label, factory, parser, noResult, agent,
|
|
|
1082
1078
|
.stopTimer();
|
|
1083
1079
|
if (options?.interactive) {
|
|
1084
1080
|
logResult(label, result);
|
|
1085
|
-
const reviewAnswer = await getUserReviewDecision(
|
|
1081
|
+
const reviewAnswer = await getUserReviewDecision({
|
|
1082
|
+
label,
|
|
1083
|
+
...options?.review || {},
|
|
1084
|
+
});
|
|
1086
1085
|
if (reviewAnswer === 'cancel') {
|
|
1087
1086
|
process.exit(0);
|
|
1088
1087
|
}
|
|
@@ -1136,20 +1135,20 @@ const executeChain = async ({ llm, prompt, variables }) => {
|
|
|
1136
1135
|
return res.text.trim();
|
|
1137
1136
|
};
|
|
1138
1137
|
|
|
1139
|
-
async function createCommit(commitMsg, git) {
|
|
1140
|
-
return await git.commit(commitMsg);
|
|
1141
|
-
}
|
|
1142
|
-
|
|
1143
1138
|
const logSuccess = () => {
|
|
1144
1139
|
console.log(chalk.green(chalk.bold('\nAll set! 🦾🤖')));
|
|
1145
1140
|
};
|
|
1146
1141
|
|
|
1147
|
-
|
|
1148
|
-
// Handle resulting commit message
|
|
1142
|
+
async function handleResult({ result, mode, interactiveHandler }) {
|
|
1149
1143
|
switch (mode) {
|
|
1150
1144
|
case 'interactive':
|
|
1151
|
-
|
|
1152
|
-
|
|
1145
|
+
if (interactiveHandler) {
|
|
1146
|
+
await interactiveHandler(result);
|
|
1147
|
+
}
|
|
1148
|
+
else {
|
|
1149
|
+
console.error('No result handler provided for interactive mode');
|
|
1150
|
+
logSuccess();
|
|
1151
|
+
}
|
|
1153
1152
|
break;
|
|
1154
1153
|
case 'stdout':
|
|
1155
1154
|
default:
|
|
@@ -1157,7 +1156,7 @@ const handleResult = async (result, { mode, git }) => {
|
|
|
1157
1156
|
break;
|
|
1158
1157
|
}
|
|
1159
1158
|
process.exit(0);
|
|
1160
|
-
}
|
|
1159
|
+
}
|
|
1161
1160
|
|
|
1162
1161
|
const getRepo = () => {
|
|
1163
1162
|
let git;
|
|
@@ -1171,17 +1170,32 @@ const getRepo = () => {
|
|
|
1171
1170
|
return git;
|
|
1172
1171
|
};
|
|
1173
1172
|
|
|
1173
|
+
const getTikToken = async (modelName) => {
|
|
1174
|
+
return await encoding_for_model(modelName);
|
|
1175
|
+
};
|
|
1176
|
+
const getTokenCounter = async (modelName) => getTikToken(modelName).then((tokenizer) => (text) => {
|
|
1177
|
+
// console.log('Running GetTokenCount', { tokenizer, length: text.length })
|
|
1178
|
+
const tokens = tokenizer.encode(text);
|
|
1179
|
+
// console.log('Tokens', { tokenCount: tokens.length })
|
|
1180
|
+
return tokens.length;
|
|
1181
|
+
});
|
|
1182
|
+
|
|
1183
|
+
async function createCommit(commitMsg, git) {
|
|
1184
|
+
return await git.commit(commitMsg);
|
|
1185
|
+
}
|
|
1186
|
+
|
|
1174
1187
|
async function handler$2(argv) {
|
|
1175
|
-
const tokenizer = getTokenizer();
|
|
1176
1188
|
const git = getRepo();
|
|
1177
1189
|
const options = loadConfig(argv);
|
|
1190
|
+
const { service } = options;
|
|
1178
1191
|
const logger = new Logger(options);
|
|
1179
|
-
const key = getApiKeyForModel(
|
|
1192
|
+
const key = getApiKeyForModel(service, options);
|
|
1193
|
+
const tokenizer = await getTokenCounter(getModelFromService(service));
|
|
1180
1194
|
if (!key) {
|
|
1181
1195
|
logger.log(`No API Key found. 🗝️🚪`, { color: 'red' });
|
|
1182
1196
|
process.exit(1);
|
|
1183
1197
|
}
|
|
1184
|
-
const
|
|
1198
|
+
const llm = getLlm(service, key, {
|
|
1185
1199
|
temperature: 0.4,
|
|
1186
1200
|
maxConcurrency: 10,
|
|
1187
1201
|
});
|
|
@@ -1194,16 +1208,16 @@ async function handler$2(argv) {
|
|
|
1194
1208
|
return await fileChangeParser({
|
|
1195
1209
|
changes,
|
|
1196
1210
|
commit: '--staged',
|
|
1197
|
-
options: { tokenizer, git,
|
|
1211
|
+
options: { tokenizer, git, llm, logger },
|
|
1198
1212
|
});
|
|
1199
1213
|
}
|
|
1200
1214
|
const commitMsg = await generateAndReviewLoop({
|
|
1201
|
-
label: '
|
|
1215
|
+
label: 'commit message',
|
|
1202
1216
|
factory,
|
|
1203
1217
|
parser,
|
|
1204
1218
|
agent: async (context, options) => {
|
|
1205
1219
|
return await executeChain({
|
|
1206
|
-
llm
|
|
1220
|
+
llm,
|
|
1207
1221
|
prompt: getPrompt({
|
|
1208
1222
|
template: options.prompt,
|
|
1209
1223
|
variables: COMMIT_PROMPT.inputVariables,
|
|
@@ -1221,12 +1235,25 @@ async function handler$2(argv) {
|
|
|
1221
1235
|
prompt: options.prompt || COMMIT_PROMPT.template,
|
|
1222
1236
|
logger,
|
|
1223
1237
|
interactive: INTERACTIVE,
|
|
1238
|
+
review: {
|
|
1239
|
+
descriptions: {
|
|
1240
|
+
approve: `Commit staged changes with generated commit message`,
|
|
1241
|
+
edit: 'Edit the commit message before proceeding',
|
|
1242
|
+
modifyPrompt: 'Modify the prompt template and regenerate the commit message',
|
|
1243
|
+
retryMessageOnly: 'Restart the function execution from generating the commit message',
|
|
1244
|
+
retryFull: 'Restart the function execution from the beginning, regenerating both the diff summary and commit message',
|
|
1245
|
+
},
|
|
1246
|
+
},
|
|
1224
1247
|
},
|
|
1225
1248
|
});
|
|
1226
1249
|
const MODE = (INTERACTIVE && 'interactive') || (options.commit && 'interactive') || options?.mode || 'stdout';
|
|
1227
|
-
handleResult(
|
|
1250
|
+
handleResult({
|
|
1251
|
+
result: commitMsg,
|
|
1252
|
+
interactiveHandler: async (result) => {
|
|
1253
|
+
await createCommit(result, git);
|
|
1254
|
+
logSuccess();
|
|
1255
|
+
},
|
|
1228
1256
|
mode: MODE,
|
|
1229
|
-
git,
|
|
1230
1257
|
});
|
|
1231
1258
|
}
|
|
1232
1259
|
|
|
@@ -1368,12 +1395,12 @@ async function handler$1(argv) {
|
|
|
1368
1395
|
const options = loadConfig(argv);
|
|
1369
1396
|
const logger = new Logger(options);
|
|
1370
1397
|
const git = getRepo();
|
|
1371
|
-
const key = getApiKeyForModel(options.
|
|
1398
|
+
const key = getApiKeyForModel(options.service, options);
|
|
1372
1399
|
if (!key) {
|
|
1373
1400
|
logger.log(`No API Key found. 🗝️🚪`, { color: 'red' });
|
|
1374
1401
|
process.exit(1);
|
|
1375
1402
|
}
|
|
1376
|
-
const model =
|
|
1403
|
+
const model = getLlm(options.service, key, {
|
|
1377
1404
|
temperature: 0.4,
|
|
1378
1405
|
maxConcurrency: 10,
|
|
1379
1406
|
});
|
|
@@ -1395,7 +1422,7 @@ async function handler$1(argv) {
|
|
|
1395
1422
|
return result;
|
|
1396
1423
|
}
|
|
1397
1424
|
const changelogMsg = await generateAndReviewLoop({
|
|
1398
|
-
label: '
|
|
1425
|
+
label: 'changelog',
|
|
1399
1426
|
factory,
|
|
1400
1427
|
parser,
|
|
1401
1428
|
agent: async (context, options) => {
|
|
@@ -1423,12 +1450,18 @@ async function handler$1(argv) {
|
|
|
1423
1450
|
prompt: options.prompt || CHANGELOG_PROMPT.template,
|
|
1424
1451
|
logger,
|
|
1425
1452
|
interactive: INTERACTIVE,
|
|
1453
|
+
review: {
|
|
1454
|
+
enableFullRetry: false,
|
|
1455
|
+
}
|
|
1426
1456
|
},
|
|
1427
1457
|
});
|
|
1428
1458
|
const MODE = (INTERACTIVE && 'interactive') || (options.commit && 'interactive') || options?.mode || 'stdout';
|
|
1429
|
-
handleResult(
|
|
1459
|
+
handleResult({
|
|
1460
|
+
result: changelogMsg,
|
|
1461
|
+
interactiveHandler: async () => {
|
|
1462
|
+
logSuccess();
|
|
1463
|
+
},
|
|
1430
1464
|
mode: MODE,
|
|
1431
|
-
git,
|
|
1432
1465
|
});
|
|
1433
1466
|
}
|
|
1434
1467
|
|
|
@@ -1724,25 +1757,23 @@ var types = /*#__PURE__*/Object.freeze({
|
|
|
1724
1757
|
__proto__: null
|
|
1725
1758
|
});
|
|
1726
1759
|
|
|
1727
|
-
yargs
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
.command([commit.command, '$0'], commit.desc,
|
|
1760
|
+
const y = yargs();
|
|
1761
|
+
y.scriptName('coco').usage('$0 <cmd> [args]');
|
|
1762
|
+
y.command([commit.command, '$0'], commit.desc,
|
|
1731
1763
|
// TODO: fix type on builder
|
|
1732
1764
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
1733
1765
|
// @ts-ignore
|
|
1734
|
-
commit.builder, commit.handler)
|
|
1735
|
-
|
|
1766
|
+
commit.builder, commit.handler);
|
|
1767
|
+
y.command(changelog.command, changelog.desc,
|
|
1736
1768
|
// TODO: fix type on builder
|
|
1737
1769
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
1738
1770
|
// @ts-ignore
|
|
1739
|
-
changelog.builder, changelog.handler)
|
|
1740
|
-
|
|
1771
|
+
changelog.builder, changelog.handler);
|
|
1772
|
+
y.command(init.command, init.desc,
|
|
1741
1773
|
// TODO: fix type on builder
|
|
1742
1774
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
1743
1775
|
// @ts-ignore
|
|
1744
|
-
init.builder, init.handler)
|
|
1745
|
-
|
|
1746
|
-
.help().argv;
|
|
1776
|
+
init.builder, init.handler);
|
|
1777
|
+
y.parse(process.argv.slice(2));
|
|
1747
1778
|
|
|
1748
1779
|
export { changelog, commit, init, types };
|
package/dist/index.js
CHANGED
|
@@ -10,7 +10,6 @@ var chains = require('langchain/chains');
|
|
|
10
10
|
var openai = require('langchain/llms/openai');
|
|
11
11
|
var text_splitter = require('langchain/text_splitter');
|
|
12
12
|
var diff = require('diff');
|
|
13
|
-
var GPT3NodeTokenizer = require('gpt3-tokenizer');
|
|
14
13
|
var chalk = require('chalk');
|
|
15
14
|
var ora = require('ora');
|
|
16
15
|
var now = require('performance-now');
|
|
@@ -22,6 +21,7 @@ var prompts$1 = require('@inquirer/prompts');
|
|
|
22
21
|
var os = require('os');
|
|
23
22
|
var ini = require('ini');
|
|
24
23
|
var simpleGit = require('simple-git');
|
|
24
|
+
var tiktoken = require('tiktoken');
|
|
25
25
|
|
|
26
26
|
function _interopNamespaceDefault(e) {
|
|
27
27
|
var n = Object.create(null);
|
|
@@ -105,7 +105,7 @@ async function summarizeDirectoryDiff(directory, { chain, textSplitter, tokenize
|
|
|
105
105
|
returnIntermediateSteps: true,
|
|
106
106
|
},
|
|
107
107
|
});
|
|
108
|
-
const newTokenTotal = tokenizer
|
|
108
|
+
const newTokenTotal = tokenizer(directorySummary);
|
|
109
109
|
return {
|
|
110
110
|
diffs: directory.diffs,
|
|
111
111
|
path: directory.path,
|
|
@@ -236,9 +236,7 @@ async function collectDiffs(node, getFileDiff, tokenizer, logger) {
|
|
|
236
236
|
// Collect diffs for the files of the current node
|
|
237
237
|
const diffPromises = node.files.map(async (nodeFile) => {
|
|
238
238
|
const diff = await getFileDiff(nodeFile);
|
|
239
|
-
|
|
240
|
-
const tokenizedDiff = tokenizer.encode(diff).text;
|
|
241
|
-
const tokenCount = tokenizedDiff.length;
|
|
239
|
+
const tokenCount = tokenizer(diff);
|
|
242
240
|
logger.verbose(`Collected diff for ${nodeFile.filePath} (${tokenCount} tokens)`, {
|
|
243
241
|
color: 'magenta',
|
|
244
242
|
});
|
|
@@ -262,18 +260,29 @@ async function collectDiffs(node, getFileDiff, tokenizer, logger) {
|
|
|
262
260
|
};
|
|
263
261
|
}
|
|
264
262
|
|
|
263
|
+
function getModelAndProviderFromService(service) {
|
|
264
|
+
const [provider, model] = service.split(/\/(.*)/s);
|
|
265
|
+
if (!model || !provider) {
|
|
266
|
+
throw new Error(`Invalid service: ${service}`);
|
|
267
|
+
}
|
|
268
|
+
return { provider, model };
|
|
269
|
+
}
|
|
270
|
+
function getModelFromService(service) {
|
|
271
|
+
const { model } = getModelAndProviderFromService(service);
|
|
272
|
+
return model;
|
|
273
|
+
}
|
|
265
274
|
/**
|
|
266
275
|
* Get LLM Model Based on Configuration
|
|
267
276
|
* @param fields
|
|
268
277
|
* @param configuration
|
|
269
278
|
* @returns LLM Model
|
|
270
279
|
*/
|
|
271
|
-
function
|
|
272
|
-
const
|
|
280
|
+
function getLlm(service, key, fields) {
|
|
281
|
+
const { provider, model } = getModelAndProviderFromService(service);
|
|
273
282
|
if (!model) {
|
|
274
|
-
throw new Error(`Invalid
|
|
283
|
+
throw new Error(`Invalid LLM Service: ${service}`);
|
|
275
284
|
}
|
|
276
|
-
switch (
|
|
285
|
+
switch (provider) {
|
|
277
286
|
case 'huggingface':
|
|
278
287
|
return new hf.HuggingFaceInference({
|
|
279
288
|
model: model,
|
|
@@ -292,16 +301,13 @@ function getModel(name, key, fields) {
|
|
|
292
301
|
}
|
|
293
302
|
/**
|
|
294
303
|
* Retrieve appropriate API key based on selected model
|
|
295
|
-
* @param
|
|
304
|
+
* @param service
|
|
296
305
|
* @param options
|
|
297
306
|
* @returns
|
|
298
307
|
*/
|
|
299
|
-
function getApiKeyForModel(
|
|
300
|
-
const
|
|
301
|
-
|
|
302
|
-
throw new Error(`Invalid model: ${name}`);
|
|
303
|
-
}
|
|
304
|
-
switch (llm) {
|
|
308
|
+
function getApiKeyForModel(service, options) {
|
|
309
|
+
const { provider } = getModelAndProviderFromService(service);
|
|
310
|
+
switch (provider) {
|
|
305
311
|
case 'huggingface':
|
|
306
312
|
return options.huggingFaceHubApiKey;
|
|
307
313
|
case 'openai':
|
|
@@ -323,7 +329,7 @@ function getTextSplitter(options = {}) {
|
|
|
323
329
|
* @param options
|
|
324
330
|
* @returns
|
|
325
331
|
*/
|
|
326
|
-
function
|
|
332
|
+
function getSummarizationChain(model, options = { type: 'map_reduce' }) {
|
|
327
333
|
return chains.loadSummarizationChain(model, options);
|
|
328
334
|
}
|
|
329
335
|
function getPrompt({ template, variables, fallback }) {
|
|
@@ -423,9 +429,9 @@ async function getDiff(nodeFile, commit, { git, logger, }) {
|
|
|
423
429
|
}
|
|
424
430
|
|
|
425
431
|
const MAX_TOKENS_PER_SUMMARY = 2048;
|
|
426
|
-
async function fileChangeParser({ changes, commit, options: { tokenizer, git, model, logger }, }) {
|
|
432
|
+
async function fileChangeParser({ changes, commit, options: { tokenizer, git, llm: model, logger }, }) {
|
|
427
433
|
const textSplitter = getTextSplitter({ chunkSize: 2000, chunkOverlap: 125 });
|
|
428
|
-
const summarizationChain =
|
|
434
|
+
const summarizationChain = getSummarizationChain(model, {
|
|
429
435
|
type: 'map_reduce',
|
|
430
436
|
combineMapPrompt: SUMMARIZE_PROMPT,
|
|
431
437
|
combinePrompt: SUMMARIZE_PROMPT,
|
|
@@ -450,28 +456,6 @@ async function fileChangeParser({ changes, commit, options: { tokenizer, git, mo
|
|
|
450
456
|
return summary;
|
|
451
457
|
}
|
|
452
458
|
|
|
453
|
-
/**
|
|
454
|
-
* Wrapper around GPT3NodeTokenizer to handle default export.
|
|
455
|
-
*
|
|
456
|
-
* @see https://github.com/botisan-ai/gpt3-tokenizer/issues/18
|
|
457
|
-
*
|
|
458
|
-
* @returns {GPT3NodeTokenizer} The GPT3NodeTokenizer instance.
|
|
459
|
-
*/
|
|
460
|
-
const getTokenizer = () => {
|
|
461
|
-
let tokenizer;
|
|
462
|
-
// eslint-disable-next-line
|
|
463
|
-
// @ts-ignore
|
|
464
|
-
if (GPT3NodeTokenizer.default) {
|
|
465
|
-
// eslint-disable-next-line
|
|
466
|
-
// @ts-ignore
|
|
467
|
-
tokenizer = new GPT3NodeTokenizer.default({ type: 'gpt3' });
|
|
468
|
-
}
|
|
469
|
-
else {
|
|
470
|
-
tokenizer = new GPT3NodeTokenizer({ type: 'gpt3' });
|
|
471
|
-
}
|
|
472
|
-
return tokenizer;
|
|
473
|
-
};
|
|
474
|
-
|
|
475
459
|
class Logger {
|
|
476
460
|
constructor(config) {
|
|
477
461
|
this.config = config;
|
|
@@ -612,7 +596,7 @@ function removeUndefined(obj) {
|
|
|
612
596
|
* @type {Config}
|
|
613
597
|
*/
|
|
614
598
|
const DEFAULT_CONFIG = {
|
|
615
|
-
|
|
599
|
+
service: 'openai/gpt-4',
|
|
616
600
|
verbose: false,
|
|
617
601
|
tokenLimit: 1024,
|
|
618
602
|
summarizePrompt: SUMMARIZE_PROMPT.template,
|
|
@@ -623,7 +607,9 @@ const DEFAULT_CONFIG = {
|
|
|
623
607
|
defaultBranch: 'main',
|
|
624
608
|
};
|
|
625
609
|
/**
|
|
626
|
-
*
|
|
610
|
+
* Create a named export of all config keys for use in other modules.
|
|
611
|
+
*
|
|
612
|
+
* @see Currently used in `src/lib/config/services/env.ts` to validate all env vars.
|
|
627
613
|
*
|
|
628
614
|
* @type {string[]}
|
|
629
615
|
*/
|
|
@@ -762,7 +748,7 @@ function loadGitConfig(config) {
|
|
|
762
748
|
const gitConfigParsed = ini__namespace.parse(gitConfigRaw);
|
|
763
749
|
config = {
|
|
764
750
|
...config,
|
|
765
|
-
|
|
751
|
+
service: gitConfigParsed.coco?.model || config.service,
|
|
766
752
|
openAIApiKey: gitConfigParsed.coco?.openAIApiKey || config.openAIApiKey,
|
|
767
753
|
huggingFaceHubApiKey: gitConfigParsed.coco?.huggingFaceHubApiKey || config.huggingFaceHubApiKey,
|
|
768
754
|
tokenLimit: parseInt(gitConfigParsed.coco?.tokenLimit) || config.tokenLimit,
|
|
@@ -1016,41 +1002,51 @@ async function editResult(result, options) {
|
|
|
1016
1002
|
return result;
|
|
1017
1003
|
}
|
|
1018
1004
|
|
|
1019
|
-
async function getUserReviewDecision() {
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1005
|
+
async function getUserReviewDecision({ label, descriptions, enableRetry = true, enableFullRetry = true, enableModifyPrompt = true, }) {
|
|
1006
|
+
const choices = [
|
|
1007
|
+
{
|
|
1008
|
+
name: '✨ Looks good!',
|
|
1009
|
+
value: 'approve',
|
|
1010
|
+
description: descriptions?.approve || `Continue with the generated ${label}`,
|
|
1011
|
+
},
|
|
1012
|
+
{
|
|
1013
|
+
name: '📝 Edit',
|
|
1014
|
+
value: 'edit',
|
|
1015
|
+
description: descriptions?.edit || `Edit the generated ${label} before proceeding`,
|
|
1016
|
+
},
|
|
1017
|
+
];
|
|
1018
|
+
if (enableModifyPrompt) {
|
|
1019
|
+
choices.push({
|
|
1020
|
+
name: '🪶 Modify Prompt',
|
|
1021
|
+
value: 'modifyPrompt',
|
|
1022
|
+
description: descriptions?.modifyPrompt || `Modify the prompt template and regenerate the ${label}`,
|
|
1023
|
+
});
|
|
1024
|
+
}
|
|
1025
|
+
if (enableRetry) {
|
|
1026
|
+
choices.push({
|
|
1027
|
+
name: '🔄 Retry',
|
|
1028
|
+
value: 'retryMessageOnly',
|
|
1029
|
+
description: descriptions?.retryMessageOnly ||
|
|
1030
|
+
`Restart the function execution from generating the ${label}`,
|
|
1031
|
+
});
|
|
1032
|
+
}
|
|
1033
|
+
if (enableFullRetry) {
|
|
1034
|
+
choices.push({
|
|
1035
|
+
name: '🔄 Retry Full',
|
|
1036
|
+
value: 'retryFull',
|
|
1037
|
+
description: descriptions?.retryFull ||
|
|
1038
|
+
`Restart the function execution from the beginning, regenerating both the summary and ${label}`,
|
|
1039
|
+
});
|
|
1040
|
+
}
|
|
1041
|
+
choices.push({
|
|
1042
|
+
name: '💣 Cancel',
|
|
1043
|
+
value: 'cancel',
|
|
1044
|
+
description: descriptions?.cancel || `Cancel the ${label}`,
|
|
1053
1045
|
});
|
|
1046
|
+
return (await prompts$1.select({
|
|
1047
|
+
message: `Would you like to make any changes to the ${label}?`,
|
|
1048
|
+
choices,
|
|
1049
|
+
}));
|
|
1054
1050
|
}
|
|
1055
1051
|
|
|
1056
1052
|
async function editPrompt(options) {
|
|
@@ -1103,7 +1099,10 @@ async function generateAndReviewLoop({ label, factory, parser, noResult, agent,
|
|
|
1103
1099
|
.stopTimer();
|
|
1104
1100
|
if (options?.interactive) {
|
|
1105
1101
|
logResult(label, result);
|
|
1106
|
-
const reviewAnswer = await getUserReviewDecision(
|
|
1102
|
+
const reviewAnswer = await getUserReviewDecision({
|
|
1103
|
+
label,
|
|
1104
|
+
...options?.review || {},
|
|
1105
|
+
});
|
|
1107
1106
|
if (reviewAnswer === 'cancel') {
|
|
1108
1107
|
process.exit(0);
|
|
1109
1108
|
}
|
|
@@ -1157,20 +1156,20 @@ const executeChain = async ({ llm, prompt, variables }) => {
|
|
|
1157
1156
|
return res.text.trim();
|
|
1158
1157
|
};
|
|
1159
1158
|
|
|
1160
|
-
async function createCommit(commitMsg, git) {
|
|
1161
|
-
return await git.commit(commitMsg);
|
|
1162
|
-
}
|
|
1163
|
-
|
|
1164
1159
|
const logSuccess = () => {
|
|
1165
1160
|
console.log(chalk.green(chalk.bold('\nAll set! 🦾🤖')));
|
|
1166
1161
|
};
|
|
1167
1162
|
|
|
1168
|
-
|
|
1169
|
-
// Handle resulting commit message
|
|
1163
|
+
async function handleResult({ result, mode, interactiveHandler }) {
|
|
1170
1164
|
switch (mode) {
|
|
1171
1165
|
case 'interactive':
|
|
1172
|
-
|
|
1173
|
-
|
|
1166
|
+
if (interactiveHandler) {
|
|
1167
|
+
await interactiveHandler(result);
|
|
1168
|
+
}
|
|
1169
|
+
else {
|
|
1170
|
+
console.error('No result handler provided for interactive mode');
|
|
1171
|
+
logSuccess();
|
|
1172
|
+
}
|
|
1174
1173
|
break;
|
|
1175
1174
|
case 'stdout':
|
|
1176
1175
|
default:
|
|
@@ -1178,7 +1177,7 @@ const handleResult = async (result, { mode, git }) => {
|
|
|
1178
1177
|
break;
|
|
1179
1178
|
}
|
|
1180
1179
|
process.exit(0);
|
|
1181
|
-
}
|
|
1180
|
+
}
|
|
1182
1181
|
|
|
1183
1182
|
const getRepo = () => {
|
|
1184
1183
|
let git;
|
|
@@ -1192,17 +1191,32 @@ const getRepo = () => {
|
|
|
1192
1191
|
return git;
|
|
1193
1192
|
};
|
|
1194
1193
|
|
|
1194
|
+
const getTikToken = async (modelName) => {
|
|
1195
|
+
return await tiktoken.encoding_for_model(modelName);
|
|
1196
|
+
};
|
|
1197
|
+
const getTokenCounter = async (modelName) => getTikToken(modelName).then((tokenizer) => (text) => {
|
|
1198
|
+
// console.log('Running GetTokenCount', { tokenizer, length: text.length })
|
|
1199
|
+
const tokens = tokenizer.encode(text);
|
|
1200
|
+
// console.log('Tokens', { tokenCount: tokens.length })
|
|
1201
|
+
return tokens.length;
|
|
1202
|
+
});
|
|
1203
|
+
|
|
1204
|
+
async function createCommit(commitMsg, git) {
|
|
1205
|
+
return await git.commit(commitMsg);
|
|
1206
|
+
}
|
|
1207
|
+
|
|
1195
1208
|
async function handler$2(argv) {
|
|
1196
|
-
const tokenizer = getTokenizer();
|
|
1197
1209
|
const git = getRepo();
|
|
1198
1210
|
const options = loadConfig(argv);
|
|
1211
|
+
const { service } = options;
|
|
1199
1212
|
const logger = new Logger(options);
|
|
1200
|
-
const key = getApiKeyForModel(
|
|
1213
|
+
const key = getApiKeyForModel(service, options);
|
|
1214
|
+
const tokenizer = await getTokenCounter(getModelFromService(service));
|
|
1201
1215
|
if (!key) {
|
|
1202
1216
|
logger.log(`No API Key found. 🗝️🚪`, { color: 'red' });
|
|
1203
1217
|
process.exit(1);
|
|
1204
1218
|
}
|
|
1205
|
-
const
|
|
1219
|
+
const llm = getLlm(service, key, {
|
|
1206
1220
|
temperature: 0.4,
|
|
1207
1221
|
maxConcurrency: 10,
|
|
1208
1222
|
});
|
|
@@ -1215,16 +1229,16 @@ async function handler$2(argv) {
|
|
|
1215
1229
|
return await fileChangeParser({
|
|
1216
1230
|
changes,
|
|
1217
1231
|
commit: '--staged',
|
|
1218
|
-
options: { tokenizer, git,
|
|
1232
|
+
options: { tokenizer, git, llm, logger },
|
|
1219
1233
|
});
|
|
1220
1234
|
}
|
|
1221
1235
|
const commitMsg = await generateAndReviewLoop({
|
|
1222
|
-
label: '
|
|
1236
|
+
label: 'commit message',
|
|
1223
1237
|
factory,
|
|
1224
1238
|
parser,
|
|
1225
1239
|
agent: async (context, options) => {
|
|
1226
1240
|
return await executeChain({
|
|
1227
|
-
llm
|
|
1241
|
+
llm,
|
|
1228
1242
|
prompt: getPrompt({
|
|
1229
1243
|
template: options.prompt,
|
|
1230
1244
|
variables: COMMIT_PROMPT.inputVariables,
|
|
@@ -1242,12 +1256,25 @@ async function handler$2(argv) {
|
|
|
1242
1256
|
prompt: options.prompt || COMMIT_PROMPT.template,
|
|
1243
1257
|
logger,
|
|
1244
1258
|
interactive: INTERACTIVE,
|
|
1259
|
+
review: {
|
|
1260
|
+
descriptions: {
|
|
1261
|
+
approve: `Commit staged changes with generated commit message`,
|
|
1262
|
+
edit: 'Edit the commit message before proceeding',
|
|
1263
|
+
modifyPrompt: 'Modify the prompt template and regenerate the commit message',
|
|
1264
|
+
retryMessageOnly: 'Restart the function execution from generating the commit message',
|
|
1265
|
+
retryFull: 'Restart the function execution from the beginning, regenerating both the diff summary and commit message',
|
|
1266
|
+
},
|
|
1267
|
+
},
|
|
1245
1268
|
},
|
|
1246
1269
|
});
|
|
1247
1270
|
const MODE = (INTERACTIVE && 'interactive') || (options.commit && 'interactive') || options?.mode || 'stdout';
|
|
1248
|
-
handleResult(
|
|
1271
|
+
handleResult({
|
|
1272
|
+
result: commitMsg,
|
|
1273
|
+
interactiveHandler: async (result) => {
|
|
1274
|
+
await createCommit(result, git);
|
|
1275
|
+
logSuccess();
|
|
1276
|
+
},
|
|
1249
1277
|
mode: MODE,
|
|
1250
|
-
git,
|
|
1251
1278
|
});
|
|
1252
1279
|
}
|
|
1253
1280
|
|
|
@@ -1389,12 +1416,12 @@ async function handler$1(argv) {
|
|
|
1389
1416
|
const options = loadConfig(argv);
|
|
1390
1417
|
const logger = new Logger(options);
|
|
1391
1418
|
const git = getRepo();
|
|
1392
|
-
const key = getApiKeyForModel(options.
|
|
1419
|
+
const key = getApiKeyForModel(options.service, options);
|
|
1393
1420
|
if (!key) {
|
|
1394
1421
|
logger.log(`No API Key found. 🗝️🚪`, { color: 'red' });
|
|
1395
1422
|
process.exit(1);
|
|
1396
1423
|
}
|
|
1397
|
-
const model =
|
|
1424
|
+
const model = getLlm(options.service, key, {
|
|
1398
1425
|
temperature: 0.4,
|
|
1399
1426
|
maxConcurrency: 10,
|
|
1400
1427
|
});
|
|
@@ -1416,7 +1443,7 @@ async function handler$1(argv) {
|
|
|
1416
1443
|
return result;
|
|
1417
1444
|
}
|
|
1418
1445
|
const changelogMsg = await generateAndReviewLoop({
|
|
1419
|
-
label: '
|
|
1446
|
+
label: 'changelog',
|
|
1420
1447
|
factory,
|
|
1421
1448
|
parser,
|
|
1422
1449
|
agent: async (context, options) => {
|
|
@@ -1444,12 +1471,18 @@ async function handler$1(argv) {
|
|
|
1444
1471
|
prompt: options.prompt || CHANGELOG_PROMPT.template,
|
|
1445
1472
|
logger,
|
|
1446
1473
|
interactive: INTERACTIVE,
|
|
1474
|
+
review: {
|
|
1475
|
+
enableFullRetry: false,
|
|
1476
|
+
}
|
|
1447
1477
|
},
|
|
1448
1478
|
});
|
|
1449
1479
|
const MODE = (INTERACTIVE && 'interactive') || (options.commit && 'interactive') || options?.mode || 'stdout';
|
|
1450
|
-
handleResult(
|
|
1480
|
+
handleResult({
|
|
1481
|
+
result: changelogMsg,
|
|
1482
|
+
interactiveHandler: async () => {
|
|
1483
|
+
logSuccess();
|
|
1484
|
+
},
|
|
1451
1485
|
mode: MODE,
|
|
1452
|
-
git,
|
|
1453
1486
|
});
|
|
1454
1487
|
}
|
|
1455
1488
|
|
|
@@ -1745,26 +1778,24 @@ var types = /*#__PURE__*/Object.freeze({
|
|
|
1745
1778
|
__proto__: null
|
|
1746
1779
|
});
|
|
1747
1780
|
|
|
1748
|
-
yargs
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
.command([commit.command, '$0'], commit.desc,
|
|
1781
|
+
const y = yargs();
|
|
1782
|
+
y.scriptName('coco').usage('$0 <cmd> [args]');
|
|
1783
|
+
y.command([commit.command, '$0'], commit.desc,
|
|
1752
1784
|
// TODO: fix type on builder
|
|
1753
1785
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
1754
1786
|
// @ts-ignore
|
|
1755
|
-
commit.builder, commit.handler)
|
|
1756
|
-
|
|
1787
|
+
commit.builder, commit.handler);
|
|
1788
|
+
y.command(changelog.command, changelog.desc,
|
|
1757
1789
|
// TODO: fix type on builder
|
|
1758
1790
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
1759
1791
|
// @ts-ignore
|
|
1760
|
-
changelog.builder, changelog.handler)
|
|
1761
|
-
|
|
1792
|
+
changelog.builder, changelog.handler);
|
|
1793
|
+
y.command(init.command, init.desc,
|
|
1762
1794
|
// TODO: fix type on builder
|
|
1763
1795
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
1764
1796
|
// @ts-ignore
|
|
1765
|
-
init.builder, init.handler)
|
|
1766
|
-
|
|
1767
|
-
.help().argv;
|
|
1797
|
+
init.builder, init.handler);
|
|
1798
|
+
y.parse(process.argv.slice(2));
|
|
1768
1799
|
|
|
1769
1800
|
exports.changelog = changelog;
|
|
1770
1801
|
exports.commit = commit;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "git-coco",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.3",
|
|
4
4
|
"description": "zero-effort git commits with coco.",
|
|
5
5
|
"author": "gfargo <ghfargo@gmail.com>",
|
|
6
6
|
"license": "MIT",
|
|
@@ -82,7 +82,6 @@
|
|
|
82
82
|
"@inquirer/prompts": "3.3.0",
|
|
83
83
|
"chalk": "4.1.2",
|
|
84
84
|
"diff": "5.1.0",
|
|
85
|
-
"gpt3-tokenizer": "1.1.5",
|
|
86
85
|
"ini": "4.1.1",
|
|
87
86
|
"langchain": "0.0.196",
|
|
88
87
|
"minimatch": "9.0.3",
|
|
@@ -91,6 +90,7 @@
|
|
|
91
90
|
"performance-now": "2.1.0",
|
|
92
91
|
"pretty-ms": "7.0.1",
|
|
93
92
|
"simple-git": "3.21.0",
|
|
93
|
+
"tiktoken": "1.0.11",
|
|
94
94
|
"yargs": "17.7.2"
|
|
95
95
|
}
|
|
96
96
|
}
|