git-coco 0.10.0 → 0.11.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/dist/index.d.ts +0 -4
- package/dist/index.esm.mjs +170 -89
- package/dist/index.js +170 -89
- package/package.json +2 -2
package/dist/index.d.ts
CHANGED
|
@@ -53,8 +53,6 @@ declare const _default$1: {
|
|
|
53
53
|
commit: boolean;
|
|
54
54
|
summarizePrompt: string;
|
|
55
55
|
openInEditor: boolean;
|
|
56
|
-
ignoredFiles: string[];
|
|
57
|
-
ignoredExtensions: string[];
|
|
58
56
|
interactive: boolean;
|
|
59
57
|
help: boolean;
|
|
60
58
|
verbose: boolean;
|
|
@@ -67,8 +65,6 @@ declare const _default$1: {
|
|
|
67
65
|
commit: boolean;
|
|
68
66
|
summarizePrompt: string;
|
|
69
67
|
openInEditor: boolean;
|
|
70
|
-
ignoredFiles: string[];
|
|
71
|
-
ignoredExtensions: string[];
|
|
72
68
|
interactive: boolean;
|
|
73
69
|
help: boolean;
|
|
74
70
|
verbose: boolean;
|
package/dist/index.esm.mjs
CHANGED
|
@@ -6,11 +6,11 @@ import * as fs from 'fs';
|
|
|
6
6
|
import fs__default from 'fs';
|
|
7
7
|
import { confirm, editor, select, password, input } from '@inquirer/prompts';
|
|
8
8
|
import chalk from 'chalk';
|
|
9
|
+
import * as ini from 'ini';
|
|
9
10
|
import * as os from 'os';
|
|
10
11
|
import os__default from 'os';
|
|
11
12
|
import * as path from 'path';
|
|
12
13
|
import path__default from 'path';
|
|
13
|
-
import * as ini from 'ini';
|
|
14
14
|
import Ajv from 'ajv';
|
|
15
15
|
import ora from 'ora';
|
|
16
16
|
import now from 'performance-now';
|
|
@@ -21,7 +21,7 @@ import { RUN_KEY } from '@langchain/core/outputs';
|
|
|
21
21
|
import { CallbackManager, parseCallbackConfigArg } from '@langchain/core/callbacks/manager';
|
|
22
22
|
import { ensureConfig, Runnable } from '@langchain/core/runnables';
|
|
23
23
|
import { BaseLangChain, BaseLanguageModel } from '@langchain/core/language_models/base';
|
|
24
|
-
import { BaseOutputParser } from '@langchain/core/output_parsers';
|
|
24
|
+
import { BaseOutputParser, JsonOutputParser } from '@langchain/core/output_parsers';
|
|
25
25
|
import '@langchain/core/messages';
|
|
26
26
|
import '@langchain/core/memory';
|
|
27
27
|
import '@langchain/core/chat_history';
|
|
@@ -259,12 +259,35 @@ function loadEnvConfig(config) {
|
|
|
259
259
|
if (envValue === undefined) {
|
|
260
260
|
return;
|
|
261
261
|
}
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
262
|
+
if (key === 'COCO_SERVICE_PROVIDER' || key === 'COCO_SERVICE_MODEL' || key === 'OPEN_AI_KEY') {
|
|
263
|
+
// NOTE: We want to ensure that the service object is always defined
|
|
264
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
265
|
+
// @ts-ignore
|
|
266
|
+
envConfig.service = envConfig.service || {};
|
|
267
|
+
handleServiceEnvVar(envConfig.service, key, envValue);
|
|
268
|
+
}
|
|
269
|
+
else {
|
|
270
|
+
envConfig[key] = envValue;
|
|
271
|
+
}
|
|
265
272
|
});
|
|
266
273
|
return { ...config, ...removeUndefined(envConfig) };
|
|
267
274
|
}
|
|
275
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
276
|
+
function handleServiceEnvVar(service, key, value) {
|
|
277
|
+
switch (key) {
|
|
278
|
+
case 'COCO_SERVICE_PROVIDER':
|
|
279
|
+
service.provider = value;
|
|
280
|
+
break;
|
|
281
|
+
case 'COCO_SERVICE_MODEL':
|
|
282
|
+
service.model = value;
|
|
283
|
+
break;
|
|
284
|
+
case 'OPEN_AI_KEY':
|
|
285
|
+
if (service.provider === 'openai') {
|
|
286
|
+
service.fields = { apiKey: value };
|
|
287
|
+
}
|
|
288
|
+
break;
|
|
289
|
+
}
|
|
290
|
+
}
|
|
268
291
|
function parseEnvValue(key, value) {
|
|
269
292
|
switch (true) {
|
|
270
293
|
// Handle undefined values
|
|
@@ -343,7 +366,12 @@ function loadGitConfig(config) {
|
|
|
343
366
|
if (fs.existsSync(gitConfigPath)) {
|
|
344
367
|
const gitConfigRaw = fs.readFileSync(gitConfigPath, 'utf-8');
|
|
345
368
|
const gitConfigParsed = ini.parse(gitConfigRaw);
|
|
346
|
-
const
|
|
369
|
+
const gitServiceAlias = gitConfigParsed.coco?.service;
|
|
370
|
+
let service = config.service;
|
|
371
|
+
if (gitServiceAlias) {
|
|
372
|
+
const gitServiceConfig = getDefaultServiceConfigFromAlias(gitServiceAlias);
|
|
373
|
+
service = parseServiceConfig$1(gitServiceConfig || config.service);
|
|
374
|
+
}
|
|
347
375
|
config = {
|
|
348
376
|
...config,
|
|
349
377
|
service: service,
|
|
@@ -358,6 +386,28 @@ function loadGitConfig(config) {
|
|
|
358
386
|
}
|
|
359
387
|
return removeUndefined(config);
|
|
360
388
|
}
|
|
389
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
390
|
+
function parseServiceConfig$1(service) {
|
|
391
|
+
if (!service)
|
|
392
|
+
return undefined;
|
|
393
|
+
switch (service.provider) {
|
|
394
|
+
case 'openai':
|
|
395
|
+
return {
|
|
396
|
+
provider: 'openai',
|
|
397
|
+
model: service.model,
|
|
398
|
+
fields: { apiKey: service.apiKey },
|
|
399
|
+
};
|
|
400
|
+
case 'ollama':
|
|
401
|
+
return {
|
|
402
|
+
provider: 'ollama',
|
|
403
|
+
model: service.model,
|
|
404
|
+
endpoint: service.endpoint,
|
|
405
|
+
fields: service.fields,
|
|
406
|
+
};
|
|
407
|
+
default:
|
|
408
|
+
return undefined;
|
|
409
|
+
}
|
|
410
|
+
}
|
|
361
411
|
/**
|
|
362
412
|
* Appends the provided configuration to a git config file.
|
|
363
413
|
*
|
|
@@ -1732,10 +1782,42 @@ function loadXDGConfig(config) {
|
|
|
1732
1782
|
const xdgConfigPath = path.join(xdgConfigHome, 'coco', 'config.json');
|
|
1733
1783
|
if (fs.existsSync(xdgConfigPath)) {
|
|
1734
1784
|
const xdgConfig = JSON.parse(fs.readFileSync(xdgConfigPath, 'utf-8'));
|
|
1735
|
-
|
|
1785
|
+
const service = parseServiceConfig(xdgConfig.service || config.service);
|
|
1786
|
+
config = {
|
|
1787
|
+
...config,
|
|
1788
|
+
...xdgConfig,
|
|
1789
|
+
service: service
|
|
1790
|
+
};
|
|
1736
1791
|
}
|
|
1737
1792
|
return config;
|
|
1738
1793
|
}
|
|
1794
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1795
|
+
function parseServiceConfig(service) {
|
|
1796
|
+
if (!service)
|
|
1797
|
+
return undefined;
|
|
1798
|
+
switch (service.provider) {
|
|
1799
|
+
case 'openai':
|
|
1800
|
+
return {
|
|
1801
|
+
provider: 'openai',
|
|
1802
|
+
model: service.model,
|
|
1803
|
+
authentication: {
|
|
1804
|
+
type: 'APIKey',
|
|
1805
|
+
credentials: {
|
|
1806
|
+
apiKey: service.apiKey
|
|
1807
|
+
}
|
|
1808
|
+
}
|
|
1809
|
+
};
|
|
1810
|
+
case 'ollama':
|
|
1811
|
+
return {
|
|
1812
|
+
provider: 'ollama',
|
|
1813
|
+
model: service.model,
|
|
1814
|
+
endpoint: service.endpoint,
|
|
1815
|
+
fields: service.fields
|
|
1816
|
+
};
|
|
1817
|
+
default:
|
|
1818
|
+
return undefined;
|
|
1819
|
+
}
|
|
1820
|
+
}
|
|
1739
1821
|
|
|
1740
1822
|
/**
|
|
1741
1823
|
* Load application config
|
|
@@ -5417,21 +5499,18 @@ async function fileChangeParser({ changes, commit, options: { tokenizer, git, ll
|
|
|
5417
5499
|
}
|
|
5418
5500
|
|
|
5419
5501
|
const template$1 = `Write informative git commit message, in the imperative, based on the diffs & file changes provided in the "Diff Summary" section.
|
|
5420
|
-
Commit Messages must have a short description that is less than 50 characters and a longer detailed summary no more than 300 characters, the shorter and more concise the better.
|
|
5502
|
+
Commit Messages must have a short description that is less than 50 characters and a longer detailed summary no more than 300 characters, the shorter and more concise the better. Please follow the guidelines below when writing your commit message:
|
|
5421
5503
|
|
|
5422
5504
|
- Write concisely using an informal tone
|
|
5423
5505
|
- DO NOT use phrases like "this commit", "this change", "this function", etc. Instead refer to the function, variable, or class by name
|
|
5424
5506
|
- DO NOT use specific names or files from the code
|
|
5425
5507
|
- DO NOT include any diffs or file changes in the commit message
|
|
5426
5508
|
- Wrap variable, class, function, components, and dependency names in back ticks e.g. \`variable\`
|
|
5427
|
-
- ONLY respond with the resulting commit message.
|
|
5428
5509
|
|
|
5429
|
-
|
|
5430
|
-
{summary}
|
|
5431
|
-
--------
|
|
5510
|
+
{format_instructions}
|
|
5432
5511
|
|
|
5433
|
-
|
|
5434
|
-
const inputVariables$1 = ['summary'];
|
|
5512
|
+
"""{summary}"""`;
|
|
5513
|
+
const inputVariables$1 = ['summary', 'format_instructions'];
|
|
5435
5514
|
const COMMIT_PROMPT = new PromptTemplate({
|
|
5436
5515
|
template: template$1,
|
|
5437
5516
|
inputVariables: inputVariables$1,
|
|
@@ -5460,6 +5539,7 @@ function getLlm(provider, model, config) {
|
|
|
5460
5539
|
const openAiModel = new ChatOpenAI({
|
|
5461
5540
|
openAIApiKey: getApiKeyForModel(config),
|
|
5462
5541
|
model,
|
|
5542
|
+
temperature: config.service.temperature || 0.2,
|
|
5463
5543
|
});
|
|
5464
5544
|
return openAiModel;
|
|
5465
5545
|
}
|
|
@@ -5634,8 +5714,32 @@ async function noResult({ git, logger }) {
|
|
|
5634
5714
|
}
|
|
5635
5715
|
}
|
|
5636
5716
|
|
|
5637
|
-
|
|
5638
|
-
|
|
5717
|
+
/**
|
|
5718
|
+
* Verify template string contains all required input variables
|
|
5719
|
+
*
|
|
5720
|
+
* @param text template string
|
|
5721
|
+
* @param inputVariables template variables
|
|
5722
|
+
* @returns boolean or error message
|
|
5723
|
+
*/
|
|
5724
|
+
function validatePromptTemplate(text, inputVariables) {
|
|
5725
|
+
if (!text) {
|
|
5726
|
+
return 'Prompt template cannot be empty';
|
|
5727
|
+
}
|
|
5728
|
+
if (!inputVariables.some((entry) => text.includes(entry))) {
|
|
5729
|
+
return ('Prompt template must include at least one of the following input variables: ' +
|
|
5730
|
+
inputVariables.map((value) => `{${value}}`).join(', '));
|
|
5731
|
+
}
|
|
5732
|
+
return true;
|
|
5733
|
+
}
|
|
5734
|
+
|
|
5735
|
+
async function editPrompt(options) {
|
|
5736
|
+
return await editor({
|
|
5737
|
+
message: 'Edit the prompt',
|
|
5738
|
+
default: options.prompt?.length ? options.prompt : COMMIT_PROMPT.template,
|
|
5739
|
+
waitForUseInput: false,
|
|
5740
|
+
postfix: 'Press ENTER to continue',
|
|
5741
|
+
validate: (text) => validatePromptTemplate(text, COMMIT_PROMPT.inputVariables),
|
|
5742
|
+
});
|
|
5639
5743
|
}
|
|
5640
5744
|
|
|
5641
5745
|
async function editResult(result, options) {
|
|
@@ -5697,32 +5801,8 @@ async function getUserReviewDecision({ label, descriptions, enableRetry = true,
|
|
|
5697
5801
|
}));
|
|
5698
5802
|
}
|
|
5699
5803
|
|
|
5700
|
-
|
|
5701
|
-
|
|
5702
|
-
*
|
|
5703
|
-
* @param text template string
|
|
5704
|
-
* @param inputVariables template variables
|
|
5705
|
-
* @returns boolean or error message
|
|
5706
|
-
*/
|
|
5707
|
-
function validatePromptTemplate(text, inputVariables) {
|
|
5708
|
-
if (!text) {
|
|
5709
|
-
return 'Prompt template cannot be empty';
|
|
5710
|
-
}
|
|
5711
|
-
if (!inputVariables.some((entry) => text.includes(entry))) {
|
|
5712
|
-
return ('Prompt template must include at least one of the following input variables: ' +
|
|
5713
|
-
inputVariables.map((value) => `{${value}}`).join(', '));
|
|
5714
|
-
}
|
|
5715
|
-
return true;
|
|
5716
|
-
}
|
|
5717
|
-
|
|
5718
|
-
async function editPrompt(options) {
|
|
5719
|
-
return await editor({
|
|
5720
|
-
message: 'Edit the prompt',
|
|
5721
|
-
default: options.prompt?.length ? options.prompt : COMMIT_PROMPT.template,
|
|
5722
|
-
waitForUseInput: false,
|
|
5723
|
-
postfix: 'Press ENTER to continue',
|
|
5724
|
-
validate: (text) => validatePromptTemplate(text, COMMIT_PROMPT.inputVariables),
|
|
5725
|
-
});
|
|
5804
|
+
function logResult(label, result) {
|
|
5805
|
+
console.log(`\n${chalk.bgBlue(chalk.bold(`Proposed ${label}:`))}\n${SEPERATOR}\n${result}\n${SEPERATOR}\n`);
|
|
5726
5806
|
}
|
|
5727
5807
|
|
|
5728
5808
|
async function generateAndReviewLoop({ label, factory, parser, noResult, agent, options, }) {
|
|
@@ -5733,7 +5813,7 @@ async function generateAndReviewLoop({ label, factory, parser, noResult, agent,
|
|
|
5733
5813
|
let result = '';
|
|
5734
5814
|
const changes = await factory();
|
|
5735
5815
|
// if we don't have any changes, bail.
|
|
5736
|
-
if (!changes || !changes.length) {
|
|
5816
|
+
if (!changes || !Object.keys(changes).length) {
|
|
5737
5817
|
await noResult(options);
|
|
5738
5818
|
}
|
|
5739
5819
|
while (continueLoop) {
|
|
@@ -5768,7 +5848,7 @@ async function generateAndReviewLoop({ label, factory, parser, noResult, agent,
|
|
|
5768
5848
|
logResult(label, result);
|
|
5769
5849
|
const reviewAnswer = await getUserReviewDecision({
|
|
5770
5850
|
label,
|
|
5771
|
-
...options?.review || {},
|
|
5851
|
+
...(options?.review || {}),
|
|
5772
5852
|
});
|
|
5773
5853
|
if (reviewAnswer === 'cancel') {
|
|
5774
5854
|
process.exit(0);
|
|
@@ -5800,11 +5880,11 @@ async function generateAndReviewLoop({ label, factory, parser, noResult, agent,
|
|
|
5800
5880
|
return result;
|
|
5801
5881
|
}
|
|
5802
5882
|
|
|
5803
|
-
const executeChain = async ({ llm, prompt, variables }) => {
|
|
5883
|
+
const executeChain = async ({ llm, prompt, variables, parser, }) => {
|
|
5804
5884
|
if (!llm || !prompt || !variables) {
|
|
5805
5885
|
throw new Error('The input parameters "llm", "prompt", and "variables" are all required.');
|
|
5806
5886
|
}
|
|
5807
|
-
const chain = prompt.pipe(llm);
|
|
5887
|
+
const chain = prompt.pipe(llm).pipe(parser);
|
|
5808
5888
|
let res;
|
|
5809
5889
|
try {
|
|
5810
5890
|
res = await chain.invoke(variables);
|
|
@@ -5817,7 +5897,7 @@ const executeChain = async ({ llm, prompt, variables }) => {
|
|
|
5817
5897
|
if (!res) {
|
|
5818
5898
|
throw new Error('Empty response from LLMChain call');
|
|
5819
5899
|
}
|
|
5820
|
-
return res
|
|
5900
|
+
return res;
|
|
5821
5901
|
};
|
|
5822
5902
|
|
|
5823
5903
|
const logSuccess = () => {
|
|
@@ -5938,16 +6018,20 @@ const handler$2 = async (argv, logger) => {
|
|
|
5938
6018
|
factory,
|
|
5939
6019
|
parser,
|
|
5940
6020
|
agent: async (context, options) => {
|
|
6021
|
+
const parser = new JsonOutputParser();
|
|
5941
6022
|
const prompt = getPrompt({
|
|
5942
6023
|
template: options.prompt,
|
|
5943
6024
|
variables: COMMIT_PROMPT.inputVariables,
|
|
5944
6025
|
fallback: COMMIT_PROMPT,
|
|
5945
6026
|
});
|
|
5946
|
-
|
|
6027
|
+
const formatInstructions = "Respond with a valid JSON object, containing two fields: 'title' and 'body'.";
|
|
6028
|
+
const commitMsg = await executeChain({
|
|
5947
6029
|
llm,
|
|
5948
6030
|
prompt,
|
|
5949
|
-
variables: { summary: context },
|
|
6031
|
+
variables: { summary: context, format_instructions: formatInstructions },
|
|
6032
|
+
parser,
|
|
5950
6033
|
});
|
|
6034
|
+
return `${commitMsg.title}\n\n${commitMsg.body}`;
|
|
5951
6035
|
},
|
|
5952
6036
|
noResult: async () => {
|
|
5953
6037
|
await noResult({ git, logger });
|
|
@@ -6020,20 +6104,6 @@ var commit = {
|
|
|
6020
6104
|
options: options$2,
|
|
6021
6105
|
};
|
|
6022
6106
|
|
|
6023
|
-
const template = `Write informative git changelog, in the imperative, based on a series of individual messages.
|
|
6024
|
-
|
|
6025
|
-
- Typically a hyphen or asterisk is used for the bullet
|
|
6026
|
-
- Summarize dependency updates
|
|
6027
|
-
|
|
6028
|
-
"""{summary}"""
|
|
6029
|
-
|
|
6030
|
-
Changelog:`;
|
|
6031
|
-
const inputVariables = ['summary'];
|
|
6032
|
-
const CHANGELOG_PROMPT = new PromptTemplate({
|
|
6033
|
-
template,
|
|
6034
|
-
inputVariables,
|
|
6035
|
-
});
|
|
6036
|
-
|
|
6037
6107
|
/**
|
|
6038
6108
|
* Retrieves the commit log range between two specified commits.
|
|
6039
6109
|
*
|
|
@@ -6047,7 +6117,7 @@ async function getCommitLogRange(from, to, { noMerges, git }) {
|
|
|
6047
6117
|
try {
|
|
6048
6118
|
const logOptions = { from: `${from}^1`, to, '--no-merges': noMerges };
|
|
6049
6119
|
const commitLog = await git.log(logOptions);
|
|
6050
|
-
return commitLog.all.map(({ message, date, body, author_name }) => `[${date}] ${message}\n${body}\n - ${author_name}`);
|
|
6120
|
+
return commitLog.all.map(({ message, date, body, author_name, hash, author_email }) => `[${date}] ${message}\n${body}\n - ${author_name}<${author_email}> (${hash})`);
|
|
6051
6121
|
}
|
|
6052
6122
|
catch (error) {
|
|
6053
6123
|
// If there's an error, handle it appropriately
|
|
@@ -6118,6 +6188,20 @@ async function getCommitLogCurrentBranch({ git, logger, comparisonBranch = 'main
|
|
|
6118
6188
|
return [];
|
|
6119
6189
|
}
|
|
6120
6190
|
|
|
6191
|
+
const template = `Write informative git changelog, in the imperative, based on a series of individual messages.
|
|
6192
|
+
|
|
6193
|
+
- Typically a hyphen or asterisk is used for the bullet
|
|
6194
|
+
- Summarize dependency updates
|
|
6195
|
+
|
|
6196
|
+
{format_instructions}
|
|
6197
|
+
|
|
6198
|
+
"""{summary}"""`;
|
|
6199
|
+
const inputVariables = ['format_instructions', 'summary'];
|
|
6200
|
+
const CHANGELOG_PROMPT = new PromptTemplate({
|
|
6201
|
+
template,
|
|
6202
|
+
inputVariables,
|
|
6203
|
+
});
|
|
6204
|
+
|
|
6121
6205
|
const handler$1 = async (argv, logger) => {
|
|
6122
6206
|
const config = loadConfig(argv);
|
|
6123
6207
|
const git = getRepo();
|
|
@@ -6133,19 +6217,27 @@ const handler$1 = async (argv, logger) => {
|
|
|
6133
6217
|
logger.log(LOGO);
|
|
6134
6218
|
}
|
|
6135
6219
|
async function factory() {
|
|
6220
|
+
const branchName = await getCurrentBranchName({ git });
|
|
6136
6221
|
if (config.range && config.range.includes(':')) {
|
|
6137
6222
|
const [from, to] = config.range.split(':');
|
|
6138
6223
|
if (!from || !to) {
|
|
6139
6224
|
logger.log(`Invalid range provided. Expected format is <from>:<to>`, { color: 'red' });
|
|
6140
6225
|
process.exit(1);
|
|
6141
6226
|
}
|
|
6142
|
-
return
|
|
6227
|
+
return {
|
|
6228
|
+
branch: branchName,
|
|
6229
|
+
commits: await getCommitLogRange(from, to, { git, noMerges: true }),
|
|
6230
|
+
};
|
|
6143
6231
|
}
|
|
6144
6232
|
logger.verbose(`No range provided. Defaulting to current branch`, { color: 'yellow' });
|
|
6145
|
-
return
|
|
6233
|
+
return {
|
|
6234
|
+
branch: branchName,
|
|
6235
|
+
commits: await getCommitLogCurrentBranch({ git, logger }),
|
|
6236
|
+
};
|
|
6146
6237
|
}
|
|
6147
|
-
async function parser(
|
|
6148
|
-
|
|
6238
|
+
async function parser({ branch, commits }) {
|
|
6239
|
+
console.log({ branch, commits });
|
|
6240
|
+
const result = `## ${branch}\n\n${commits.map((commit) => `- ${commit}`).join('\n')}`;
|
|
6149
6241
|
return result;
|
|
6150
6242
|
}
|
|
6151
6243
|
const changelogMsg = await generateAndReviewLoop({
|
|
@@ -6153,16 +6245,24 @@ const handler$1 = async (argv, logger) => {
|
|
|
6153
6245
|
factory,
|
|
6154
6246
|
parser,
|
|
6155
6247
|
agent: async (context, options) => {
|
|
6248
|
+
const parser = new JsonOutputParser();
|
|
6156
6249
|
const prompt = getPrompt({
|
|
6157
6250
|
template: options.prompt,
|
|
6158
6251
|
variables: CHANGELOG_PROMPT.inputVariables,
|
|
6159
6252
|
fallback: CHANGELOG_PROMPT,
|
|
6160
6253
|
});
|
|
6161
|
-
|
|
6254
|
+
const formatInstructions = "Respond with a valid JSON object, containing two fields: 'header' and 'content'.";
|
|
6255
|
+
const changelog = await executeChain({
|
|
6162
6256
|
llm,
|
|
6163
6257
|
prompt,
|
|
6164
|
-
variables: {
|
|
6258
|
+
variables: {
|
|
6259
|
+
summary: context,
|
|
6260
|
+
format_instructions: formatInstructions,
|
|
6261
|
+
},
|
|
6262
|
+
parser,
|
|
6165
6263
|
});
|
|
6264
|
+
console.log({ changelog });
|
|
6265
|
+
return `${changelog.header}\n\n${changelog.content}`;
|
|
6166
6266
|
},
|
|
6167
6267
|
noResult: async () => {
|
|
6168
6268
|
if (config.range) {
|
|
@@ -6201,17 +6301,6 @@ const options$1 = {
|
|
|
6201
6301
|
alias: 'r',
|
|
6202
6302
|
description: 'Commit range e.g `HEAD~2:HEAD`',
|
|
6203
6303
|
},
|
|
6204
|
-
model: { type: 'string', description: 'LLM/Model-Name' },
|
|
6205
|
-
openAIApiKey: {
|
|
6206
|
-
type: 'string',
|
|
6207
|
-
description: 'OpenAI API Key',
|
|
6208
|
-
conflicts: 'huggingFaceHubApiKey',
|
|
6209
|
-
},
|
|
6210
|
-
huggingFaceHubApiKey: {
|
|
6211
|
-
type: 'string',
|
|
6212
|
-
description: 'HuggingFace Hub API Key',
|
|
6213
|
-
conflicts: 'openAIApiKey',
|
|
6214
|
-
},
|
|
6215
6304
|
tokenLimit: { type: 'number', description: 'Token limit' },
|
|
6216
6305
|
prompt: {
|
|
6217
6306
|
type: 'string',
|
|
@@ -6232,14 +6321,6 @@ const options$1 = {
|
|
|
6232
6321
|
type: 'string',
|
|
6233
6322
|
description: 'Prompt for summarizing large files',
|
|
6234
6323
|
},
|
|
6235
|
-
ignoredFiles: {
|
|
6236
|
-
type: 'array',
|
|
6237
|
-
description: 'Ignored files',
|
|
6238
|
-
},
|
|
6239
|
-
ignoredExtensions: {
|
|
6240
|
-
type: 'array',
|
|
6241
|
-
description: 'Ignored extensions',
|
|
6242
|
-
},
|
|
6243
6324
|
};
|
|
6244
6325
|
const builder$1 = (yargs) => {
|
|
6245
6326
|
return yargs.options(options$1);
|
package/dist/index.js
CHANGED
|
@@ -7,9 +7,9 @@ var yargs = require('yargs');
|
|
|
7
7
|
var fs = require('fs');
|
|
8
8
|
var prompts$1 = require('@inquirer/prompts');
|
|
9
9
|
var chalk = require('chalk');
|
|
10
|
+
var ini = require('ini');
|
|
10
11
|
var os = require('os');
|
|
11
12
|
var path = require('path');
|
|
12
|
-
var ini = require('ini');
|
|
13
13
|
var Ajv = require('ajv');
|
|
14
14
|
var ora = require('ora');
|
|
15
15
|
var now = require('performance-now');
|
|
@@ -54,9 +54,9 @@ function _interopNamespaceDefault(e) {
|
|
|
54
54
|
}
|
|
55
55
|
|
|
56
56
|
var fs__namespace = /*#__PURE__*/_interopNamespaceDefault(fs);
|
|
57
|
+
var ini__namespace = /*#__PURE__*/_interopNamespaceDefault(ini);
|
|
57
58
|
var os__namespace = /*#__PURE__*/_interopNamespaceDefault(os);
|
|
58
59
|
var path__namespace = /*#__PURE__*/_interopNamespaceDefault(path);
|
|
59
|
-
var ini__namespace = /*#__PURE__*/_interopNamespaceDefault(ini);
|
|
60
60
|
|
|
61
61
|
/**
|
|
62
62
|
* Returns a new object with all undefined keys removed
|
|
@@ -280,12 +280,35 @@ function loadEnvConfig(config) {
|
|
|
280
280
|
if (envValue === undefined) {
|
|
281
281
|
return;
|
|
282
282
|
}
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
283
|
+
if (key === 'COCO_SERVICE_PROVIDER' || key === 'COCO_SERVICE_MODEL' || key === 'OPEN_AI_KEY') {
|
|
284
|
+
// NOTE: We want to ensure that the service object is always defined
|
|
285
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
286
|
+
// @ts-ignore
|
|
287
|
+
envConfig.service = envConfig.service || {};
|
|
288
|
+
handleServiceEnvVar(envConfig.service, key, envValue);
|
|
289
|
+
}
|
|
290
|
+
else {
|
|
291
|
+
envConfig[key] = envValue;
|
|
292
|
+
}
|
|
286
293
|
});
|
|
287
294
|
return { ...config, ...removeUndefined(envConfig) };
|
|
288
295
|
}
|
|
296
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
297
|
+
function handleServiceEnvVar(service, key, value) {
|
|
298
|
+
switch (key) {
|
|
299
|
+
case 'COCO_SERVICE_PROVIDER':
|
|
300
|
+
service.provider = value;
|
|
301
|
+
break;
|
|
302
|
+
case 'COCO_SERVICE_MODEL':
|
|
303
|
+
service.model = value;
|
|
304
|
+
break;
|
|
305
|
+
case 'OPEN_AI_KEY':
|
|
306
|
+
if (service.provider === 'openai') {
|
|
307
|
+
service.fields = { apiKey: value };
|
|
308
|
+
}
|
|
309
|
+
break;
|
|
310
|
+
}
|
|
311
|
+
}
|
|
289
312
|
function parseEnvValue(key, value) {
|
|
290
313
|
switch (true) {
|
|
291
314
|
// Handle undefined values
|
|
@@ -364,7 +387,12 @@ function loadGitConfig(config) {
|
|
|
364
387
|
if (fs__namespace.existsSync(gitConfigPath)) {
|
|
365
388
|
const gitConfigRaw = fs__namespace.readFileSync(gitConfigPath, 'utf-8');
|
|
366
389
|
const gitConfigParsed = ini__namespace.parse(gitConfigRaw);
|
|
367
|
-
const
|
|
390
|
+
const gitServiceAlias = gitConfigParsed.coco?.service;
|
|
391
|
+
let service = config.service;
|
|
392
|
+
if (gitServiceAlias) {
|
|
393
|
+
const gitServiceConfig = getDefaultServiceConfigFromAlias(gitServiceAlias);
|
|
394
|
+
service = parseServiceConfig$1(gitServiceConfig || config.service);
|
|
395
|
+
}
|
|
368
396
|
config = {
|
|
369
397
|
...config,
|
|
370
398
|
service: service,
|
|
@@ -379,6 +407,28 @@ function loadGitConfig(config) {
|
|
|
379
407
|
}
|
|
380
408
|
return removeUndefined(config);
|
|
381
409
|
}
|
|
410
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
411
|
+
function parseServiceConfig$1(service) {
|
|
412
|
+
if (!service)
|
|
413
|
+
return undefined;
|
|
414
|
+
switch (service.provider) {
|
|
415
|
+
case 'openai':
|
|
416
|
+
return {
|
|
417
|
+
provider: 'openai',
|
|
418
|
+
model: service.model,
|
|
419
|
+
fields: { apiKey: service.apiKey },
|
|
420
|
+
};
|
|
421
|
+
case 'ollama':
|
|
422
|
+
return {
|
|
423
|
+
provider: 'ollama',
|
|
424
|
+
model: service.model,
|
|
425
|
+
endpoint: service.endpoint,
|
|
426
|
+
fields: service.fields,
|
|
427
|
+
};
|
|
428
|
+
default:
|
|
429
|
+
return undefined;
|
|
430
|
+
}
|
|
431
|
+
}
|
|
382
432
|
/**
|
|
383
433
|
* Appends the provided configuration to a git config file.
|
|
384
434
|
*
|
|
@@ -1753,10 +1803,42 @@ function loadXDGConfig(config) {
|
|
|
1753
1803
|
const xdgConfigPath = path__namespace.join(xdgConfigHome, 'coco', 'config.json');
|
|
1754
1804
|
if (fs__namespace.existsSync(xdgConfigPath)) {
|
|
1755
1805
|
const xdgConfig = JSON.parse(fs__namespace.readFileSync(xdgConfigPath, 'utf-8'));
|
|
1756
|
-
|
|
1806
|
+
const service = parseServiceConfig(xdgConfig.service || config.service);
|
|
1807
|
+
config = {
|
|
1808
|
+
...config,
|
|
1809
|
+
...xdgConfig,
|
|
1810
|
+
service: service
|
|
1811
|
+
};
|
|
1757
1812
|
}
|
|
1758
1813
|
return config;
|
|
1759
1814
|
}
|
|
1815
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1816
|
+
function parseServiceConfig(service) {
|
|
1817
|
+
if (!service)
|
|
1818
|
+
return undefined;
|
|
1819
|
+
switch (service.provider) {
|
|
1820
|
+
case 'openai':
|
|
1821
|
+
return {
|
|
1822
|
+
provider: 'openai',
|
|
1823
|
+
model: service.model,
|
|
1824
|
+
authentication: {
|
|
1825
|
+
type: 'APIKey',
|
|
1826
|
+
credentials: {
|
|
1827
|
+
apiKey: service.apiKey
|
|
1828
|
+
}
|
|
1829
|
+
}
|
|
1830
|
+
};
|
|
1831
|
+
case 'ollama':
|
|
1832
|
+
return {
|
|
1833
|
+
provider: 'ollama',
|
|
1834
|
+
model: service.model,
|
|
1835
|
+
endpoint: service.endpoint,
|
|
1836
|
+
fields: service.fields
|
|
1837
|
+
};
|
|
1838
|
+
default:
|
|
1839
|
+
return undefined;
|
|
1840
|
+
}
|
|
1841
|
+
}
|
|
1760
1842
|
|
|
1761
1843
|
/**
|
|
1762
1844
|
* Load application config
|
|
@@ -5438,21 +5520,18 @@ async function fileChangeParser({ changes, commit, options: { tokenizer, git, ll
|
|
|
5438
5520
|
}
|
|
5439
5521
|
|
|
5440
5522
|
const template$1 = `Write informative git commit message, in the imperative, based on the diffs & file changes provided in the "Diff Summary" section.
|
|
5441
|
-
Commit Messages must have a short description that is less than 50 characters and a longer detailed summary no more than 300 characters, the shorter and more concise the better.
|
|
5523
|
+
Commit Messages must have a short description that is less than 50 characters and a longer detailed summary no more than 300 characters, the shorter and more concise the better. Please follow the guidelines below when writing your commit message:
|
|
5442
5524
|
|
|
5443
5525
|
- Write concisely using an informal tone
|
|
5444
5526
|
- DO NOT use phrases like "this commit", "this change", "this function", etc. Instead refer to the function, variable, or class by name
|
|
5445
5527
|
- DO NOT use specific names or files from the code
|
|
5446
5528
|
- DO NOT include any diffs or file changes in the commit message
|
|
5447
5529
|
- Wrap variable, class, function, components, and dependency names in back ticks e.g. \`variable\`
|
|
5448
|
-
- ONLY respond with the resulting commit message.
|
|
5449
5530
|
|
|
5450
|
-
|
|
5451
|
-
{summary}
|
|
5452
|
-
--------
|
|
5531
|
+
{format_instructions}
|
|
5453
5532
|
|
|
5454
|
-
|
|
5455
|
-
const inputVariables$1 = ['summary'];
|
|
5533
|
+
"""{summary}"""`;
|
|
5534
|
+
const inputVariables$1 = ['summary', 'format_instructions'];
|
|
5456
5535
|
const COMMIT_PROMPT = new prompts.PromptTemplate({
|
|
5457
5536
|
template: template$1,
|
|
5458
5537
|
inputVariables: inputVariables$1,
|
|
@@ -5481,6 +5560,7 @@ function getLlm(provider, model, config) {
|
|
|
5481
5560
|
const openAiModel = new openai.ChatOpenAI({
|
|
5482
5561
|
openAIApiKey: getApiKeyForModel(config),
|
|
5483
5562
|
model,
|
|
5563
|
+
temperature: config.service.temperature || 0.2,
|
|
5484
5564
|
});
|
|
5485
5565
|
return openAiModel;
|
|
5486
5566
|
}
|
|
@@ -5655,8 +5735,32 @@ async function noResult({ git, logger }) {
|
|
|
5655
5735
|
}
|
|
5656
5736
|
}
|
|
5657
5737
|
|
|
5658
|
-
|
|
5659
|
-
|
|
5738
|
+
/**
|
|
5739
|
+
* Verify template string contains all required input variables
|
|
5740
|
+
*
|
|
5741
|
+
* @param text template string
|
|
5742
|
+
* @param inputVariables template variables
|
|
5743
|
+
* @returns boolean or error message
|
|
5744
|
+
*/
|
|
5745
|
+
function validatePromptTemplate(text, inputVariables) {
|
|
5746
|
+
if (!text) {
|
|
5747
|
+
return 'Prompt template cannot be empty';
|
|
5748
|
+
}
|
|
5749
|
+
if (!inputVariables.some((entry) => text.includes(entry))) {
|
|
5750
|
+
return ('Prompt template must include at least one of the following input variables: ' +
|
|
5751
|
+
inputVariables.map((value) => `{${value}}`).join(', '));
|
|
5752
|
+
}
|
|
5753
|
+
return true;
|
|
5754
|
+
}
|
|
5755
|
+
|
|
5756
|
+
async function editPrompt(options) {
|
|
5757
|
+
return await prompts$1.editor({
|
|
5758
|
+
message: 'Edit the prompt',
|
|
5759
|
+
default: options.prompt?.length ? options.prompt : COMMIT_PROMPT.template,
|
|
5760
|
+
waitForUseInput: false,
|
|
5761
|
+
postfix: 'Press ENTER to continue',
|
|
5762
|
+
validate: (text) => validatePromptTemplate(text, COMMIT_PROMPT.inputVariables),
|
|
5763
|
+
});
|
|
5660
5764
|
}
|
|
5661
5765
|
|
|
5662
5766
|
async function editResult(result, options) {
|
|
@@ -5718,32 +5822,8 @@ async function getUserReviewDecision({ label, descriptions, enableRetry = true,
|
|
|
5718
5822
|
}));
|
|
5719
5823
|
}
|
|
5720
5824
|
|
|
5721
|
-
|
|
5722
|
-
|
|
5723
|
-
*
|
|
5724
|
-
* @param text template string
|
|
5725
|
-
* @param inputVariables template variables
|
|
5726
|
-
* @returns boolean or error message
|
|
5727
|
-
*/
|
|
5728
|
-
function validatePromptTemplate(text, inputVariables) {
|
|
5729
|
-
if (!text) {
|
|
5730
|
-
return 'Prompt template cannot be empty';
|
|
5731
|
-
}
|
|
5732
|
-
if (!inputVariables.some((entry) => text.includes(entry))) {
|
|
5733
|
-
return ('Prompt template must include at least one of the following input variables: ' +
|
|
5734
|
-
inputVariables.map((value) => `{${value}}`).join(', '));
|
|
5735
|
-
}
|
|
5736
|
-
return true;
|
|
5737
|
-
}
|
|
5738
|
-
|
|
5739
|
-
async function editPrompt(options) {
|
|
5740
|
-
return await prompts$1.editor({
|
|
5741
|
-
message: 'Edit the prompt',
|
|
5742
|
-
default: options.prompt?.length ? options.prompt : COMMIT_PROMPT.template,
|
|
5743
|
-
waitForUseInput: false,
|
|
5744
|
-
postfix: 'Press ENTER to continue',
|
|
5745
|
-
validate: (text) => validatePromptTemplate(text, COMMIT_PROMPT.inputVariables),
|
|
5746
|
-
});
|
|
5825
|
+
function logResult(label, result) {
|
|
5826
|
+
console.log(`\n${chalk.bgBlue(chalk.bold(`Proposed ${label}:`))}\n${SEPERATOR}\n${result}\n${SEPERATOR}\n`);
|
|
5747
5827
|
}
|
|
5748
5828
|
|
|
5749
5829
|
async function generateAndReviewLoop({ label, factory, parser, noResult, agent, options, }) {
|
|
@@ -5754,7 +5834,7 @@ async function generateAndReviewLoop({ label, factory, parser, noResult, agent,
|
|
|
5754
5834
|
let result = '';
|
|
5755
5835
|
const changes = await factory();
|
|
5756
5836
|
// if we don't have any changes, bail.
|
|
5757
|
-
if (!changes || !changes.length) {
|
|
5837
|
+
if (!changes || !Object.keys(changes).length) {
|
|
5758
5838
|
await noResult(options);
|
|
5759
5839
|
}
|
|
5760
5840
|
while (continueLoop) {
|
|
@@ -5789,7 +5869,7 @@ async function generateAndReviewLoop({ label, factory, parser, noResult, agent,
|
|
|
5789
5869
|
logResult(label, result);
|
|
5790
5870
|
const reviewAnswer = await getUserReviewDecision({
|
|
5791
5871
|
label,
|
|
5792
|
-
...options?.review || {},
|
|
5872
|
+
...(options?.review || {}),
|
|
5793
5873
|
});
|
|
5794
5874
|
if (reviewAnswer === 'cancel') {
|
|
5795
5875
|
process.exit(0);
|
|
@@ -5821,11 +5901,11 @@ async function generateAndReviewLoop({ label, factory, parser, noResult, agent,
|
|
|
5821
5901
|
return result;
|
|
5822
5902
|
}
|
|
5823
5903
|
|
|
5824
|
-
const executeChain = async ({ llm, prompt, variables }) => {
|
|
5904
|
+
const executeChain = async ({ llm, prompt, variables, parser, }) => {
|
|
5825
5905
|
if (!llm || !prompt || !variables) {
|
|
5826
5906
|
throw new Error('The input parameters "llm", "prompt", and "variables" are all required.');
|
|
5827
5907
|
}
|
|
5828
|
-
const chain = prompt.pipe(llm);
|
|
5908
|
+
const chain = prompt.pipe(llm).pipe(parser);
|
|
5829
5909
|
let res;
|
|
5830
5910
|
try {
|
|
5831
5911
|
res = await chain.invoke(variables);
|
|
@@ -5838,7 +5918,7 @@ const executeChain = async ({ llm, prompt, variables }) => {
|
|
|
5838
5918
|
if (!res) {
|
|
5839
5919
|
throw new Error('Empty response from LLMChain call');
|
|
5840
5920
|
}
|
|
5841
|
-
return res
|
|
5921
|
+
return res;
|
|
5842
5922
|
};
|
|
5843
5923
|
|
|
5844
5924
|
const logSuccess = () => {
|
|
@@ -5959,16 +6039,20 @@ const handler$2 = async (argv, logger) => {
|
|
|
5959
6039
|
factory,
|
|
5960
6040
|
parser,
|
|
5961
6041
|
agent: async (context, options) => {
|
|
6042
|
+
const parser = new output_parsers.JsonOutputParser();
|
|
5962
6043
|
const prompt = getPrompt({
|
|
5963
6044
|
template: options.prompt,
|
|
5964
6045
|
variables: COMMIT_PROMPT.inputVariables,
|
|
5965
6046
|
fallback: COMMIT_PROMPT,
|
|
5966
6047
|
});
|
|
5967
|
-
|
|
6048
|
+
const formatInstructions = "Respond with a valid JSON object, containing two fields: 'title' and 'body'.";
|
|
6049
|
+
const commitMsg = await executeChain({
|
|
5968
6050
|
llm,
|
|
5969
6051
|
prompt,
|
|
5970
|
-
variables: { summary: context },
|
|
6052
|
+
variables: { summary: context, format_instructions: formatInstructions },
|
|
6053
|
+
parser,
|
|
5971
6054
|
});
|
|
6055
|
+
return `${commitMsg.title}\n\n${commitMsg.body}`;
|
|
5972
6056
|
},
|
|
5973
6057
|
noResult: async () => {
|
|
5974
6058
|
await noResult({ git, logger });
|
|
@@ -6041,20 +6125,6 @@ var commit = {
|
|
|
6041
6125
|
options: options$2,
|
|
6042
6126
|
};
|
|
6043
6127
|
|
|
6044
|
-
const template = `Write informative git changelog, in the imperative, based on a series of individual messages.
|
|
6045
|
-
|
|
6046
|
-
- Typically a hyphen or asterisk is used for the bullet
|
|
6047
|
-
- Summarize dependency updates
|
|
6048
|
-
|
|
6049
|
-
"""{summary}"""
|
|
6050
|
-
|
|
6051
|
-
Changelog:`;
|
|
6052
|
-
const inputVariables = ['summary'];
|
|
6053
|
-
const CHANGELOG_PROMPT = new prompts.PromptTemplate({
|
|
6054
|
-
template,
|
|
6055
|
-
inputVariables,
|
|
6056
|
-
});
|
|
6057
|
-
|
|
6058
6128
|
/**
|
|
6059
6129
|
* Retrieves the commit log range between two specified commits.
|
|
6060
6130
|
*
|
|
@@ -6068,7 +6138,7 @@ async function getCommitLogRange(from, to, { noMerges, git }) {
|
|
|
6068
6138
|
try {
|
|
6069
6139
|
const logOptions = { from: `${from}^1`, to, '--no-merges': noMerges };
|
|
6070
6140
|
const commitLog = await git.log(logOptions);
|
|
6071
|
-
return commitLog.all.map(({ message, date, body, author_name }) => `[${date}] ${message}\n${body}\n - ${author_name}`);
|
|
6141
|
+
return commitLog.all.map(({ message, date, body, author_name, hash, author_email }) => `[${date}] ${message}\n${body}\n - ${author_name}<${author_email}> (${hash})`);
|
|
6072
6142
|
}
|
|
6073
6143
|
catch (error) {
|
|
6074
6144
|
// If there's an error, handle it appropriately
|
|
@@ -6139,6 +6209,20 @@ async function getCommitLogCurrentBranch({ git, logger, comparisonBranch = 'main
|
|
|
6139
6209
|
return [];
|
|
6140
6210
|
}
|
|
6141
6211
|
|
|
6212
|
+
const template = `Write informative git changelog, in the imperative, based on a series of individual messages.
|
|
6213
|
+
|
|
6214
|
+
- Typically a hyphen or asterisk is used for the bullet
|
|
6215
|
+
- Summarize dependency updates
|
|
6216
|
+
|
|
6217
|
+
{format_instructions}
|
|
6218
|
+
|
|
6219
|
+
"""{summary}"""`;
|
|
6220
|
+
const inputVariables = ['format_instructions', 'summary'];
|
|
6221
|
+
const CHANGELOG_PROMPT = new prompts.PromptTemplate({
|
|
6222
|
+
template,
|
|
6223
|
+
inputVariables,
|
|
6224
|
+
});
|
|
6225
|
+
|
|
6142
6226
|
const handler$1 = async (argv, logger) => {
|
|
6143
6227
|
const config = loadConfig(argv);
|
|
6144
6228
|
const git = getRepo();
|
|
@@ -6154,19 +6238,27 @@ const handler$1 = async (argv, logger) => {
|
|
|
6154
6238
|
logger.log(LOGO);
|
|
6155
6239
|
}
|
|
6156
6240
|
async function factory() {
|
|
6241
|
+
const branchName = await getCurrentBranchName({ git });
|
|
6157
6242
|
if (config.range && config.range.includes(':')) {
|
|
6158
6243
|
const [from, to] = config.range.split(':');
|
|
6159
6244
|
if (!from || !to) {
|
|
6160
6245
|
logger.log(`Invalid range provided. Expected format is <from>:<to>`, { color: 'red' });
|
|
6161
6246
|
process.exit(1);
|
|
6162
6247
|
}
|
|
6163
|
-
return
|
|
6248
|
+
return {
|
|
6249
|
+
branch: branchName,
|
|
6250
|
+
commits: await getCommitLogRange(from, to, { git, noMerges: true }),
|
|
6251
|
+
};
|
|
6164
6252
|
}
|
|
6165
6253
|
logger.verbose(`No range provided. Defaulting to current branch`, { color: 'yellow' });
|
|
6166
|
-
return
|
|
6254
|
+
return {
|
|
6255
|
+
branch: branchName,
|
|
6256
|
+
commits: await getCommitLogCurrentBranch({ git, logger }),
|
|
6257
|
+
};
|
|
6167
6258
|
}
|
|
6168
|
-
async function parser(
|
|
6169
|
-
|
|
6259
|
+
async function parser({ branch, commits }) {
|
|
6260
|
+
console.log({ branch, commits });
|
|
6261
|
+
const result = `## ${branch}\n\n${commits.map((commit) => `- ${commit}`).join('\n')}`;
|
|
6170
6262
|
return result;
|
|
6171
6263
|
}
|
|
6172
6264
|
const changelogMsg = await generateAndReviewLoop({
|
|
@@ -6174,16 +6266,24 @@ const handler$1 = async (argv, logger) => {
|
|
|
6174
6266
|
factory,
|
|
6175
6267
|
parser,
|
|
6176
6268
|
agent: async (context, options) => {
|
|
6269
|
+
const parser = new output_parsers.JsonOutputParser();
|
|
6177
6270
|
const prompt = getPrompt({
|
|
6178
6271
|
template: options.prompt,
|
|
6179
6272
|
variables: CHANGELOG_PROMPT.inputVariables,
|
|
6180
6273
|
fallback: CHANGELOG_PROMPT,
|
|
6181
6274
|
});
|
|
6182
|
-
|
|
6275
|
+
const formatInstructions = "Respond with a valid JSON object, containing two fields: 'header' and 'content'.";
|
|
6276
|
+
const changelog = await executeChain({
|
|
6183
6277
|
llm,
|
|
6184
6278
|
prompt,
|
|
6185
|
-
variables: {
|
|
6279
|
+
variables: {
|
|
6280
|
+
summary: context,
|
|
6281
|
+
format_instructions: formatInstructions,
|
|
6282
|
+
},
|
|
6283
|
+
parser,
|
|
6186
6284
|
});
|
|
6285
|
+
console.log({ changelog });
|
|
6286
|
+
return `${changelog.header}\n\n${changelog.content}`;
|
|
6187
6287
|
},
|
|
6188
6288
|
noResult: async () => {
|
|
6189
6289
|
if (config.range) {
|
|
@@ -6222,17 +6322,6 @@ const options$1 = {
|
|
|
6222
6322
|
alias: 'r',
|
|
6223
6323
|
description: 'Commit range e.g `HEAD~2:HEAD`',
|
|
6224
6324
|
},
|
|
6225
|
-
model: { type: 'string', description: 'LLM/Model-Name' },
|
|
6226
|
-
openAIApiKey: {
|
|
6227
|
-
type: 'string',
|
|
6228
|
-
description: 'OpenAI API Key',
|
|
6229
|
-
conflicts: 'huggingFaceHubApiKey',
|
|
6230
|
-
},
|
|
6231
|
-
huggingFaceHubApiKey: {
|
|
6232
|
-
type: 'string',
|
|
6233
|
-
description: 'HuggingFace Hub API Key',
|
|
6234
|
-
conflicts: 'openAIApiKey',
|
|
6235
|
-
},
|
|
6236
6325
|
tokenLimit: { type: 'number', description: 'Token limit' },
|
|
6237
6326
|
prompt: {
|
|
6238
6327
|
type: 'string',
|
|
@@ -6253,14 +6342,6 @@ const options$1 = {
|
|
|
6253
6342
|
type: 'string',
|
|
6254
6343
|
description: 'Prompt for summarizing large files',
|
|
6255
6344
|
},
|
|
6256
|
-
ignoredFiles: {
|
|
6257
|
-
type: 'array',
|
|
6258
|
-
description: 'Ignored files',
|
|
6259
|
-
},
|
|
6260
|
-
ignoredExtensions: {
|
|
6261
|
-
type: 'array',
|
|
6262
|
-
description: 'Ignored extensions',
|
|
6263
|
-
},
|
|
6264
6345
|
};
|
|
6265
6346
|
const builder$1 = (yargs) => {
|
|
6266
6347
|
return yargs.options(options$1);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "git-coco",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.11.0",
|
|
4
4
|
"description": "zero-effort git commits with coco.",
|
|
5
5
|
"author": "gfargo <ghfargo@gmail.com>",
|
|
6
6
|
"license": "MIT",
|
|
@@ -92,7 +92,7 @@
|
|
|
92
92
|
"chalk": "4.1.2",
|
|
93
93
|
"diff": "5.2.0",
|
|
94
94
|
"ini": "4.1.1",
|
|
95
|
-
"minimatch": "
|
|
95
|
+
"minimatch": "10.0.1",
|
|
96
96
|
"ora": "5.4.1",
|
|
97
97
|
"p-queue": "5.0.0",
|
|
98
98
|
"performance-now": "2.1.0",
|