git-coco 0.10.1 → 0.11.1
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 +1 -5
- package/dist/index.esm.mjs +287 -207
- package/dist/index.js +288 -208
- package/package.json +7 -7
package/dist/index.js
CHANGED
|
@@ -7,30 +7,30 @@ 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');
|
|
16
16
|
var prettyMilliseconds = require('pretty-ms');
|
|
17
|
+
var output_parsers = require('@langchain/core/output_parsers');
|
|
18
|
+
var ollama = require('@langchain/ollama');
|
|
19
|
+
var openai = require('@langchain/openai');
|
|
17
20
|
var pQueue = require('p-queue');
|
|
18
21
|
var documents = require('@langchain/core/documents');
|
|
19
22
|
var outputs = require('@langchain/core/outputs');
|
|
20
23
|
var manager = require('@langchain/core/callbacks/manager');
|
|
21
24
|
var runnables = require('@langchain/core/runnables');
|
|
22
25
|
var base = require('@langchain/core/language_models/base');
|
|
23
|
-
var output_parsers = require('@langchain/core/output_parsers');
|
|
24
26
|
require('@langchain/core/messages');
|
|
25
27
|
require('@langchain/core/memory');
|
|
26
28
|
require('@langchain/core/chat_history');
|
|
27
29
|
require('@langchain/core/utils/tiktoken');
|
|
28
|
-
var openai = require('@langchain/openai');
|
|
29
30
|
require('@langchain/core/utils/async_caller');
|
|
30
31
|
require('@langchain/core/utils/env');
|
|
31
32
|
require('@langchain/core/utils/json_patch');
|
|
32
33
|
var diff = require('diff');
|
|
33
|
-
var ollama = require('@langchain/ollama');
|
|
34
34
|
var minimatch = require('minimatch');
|
|
35
35
|
var simpleGit = require('simple-git');
|
|
36
36
|
var tiktoken = require('tiktoken');
|
|
@@ -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
|
|
@@ -128,9 +128,9 @@ function getDefaultServiceApiKey(config) {
|
|
|
128
128
|
}
|
|
129
129
|
const DEFAULT_OPENAI_LLM_SERVICE = {
|
|
130
130
|
provider: 'openai',
|
|
131
|
-
model: 'gpt-4
|
|
131
|
+
model: 'gpt-4',
|
|
132
132
|
tokenLimit: 2024,
|
|
133
|
-
temperature: 0.
|
|
133
|
+
temperature: 0.32,
|
|
134
134
|
authentication: {
|
|
135
135
|
type: 'APIKey',
|
|
136
136
|
credentials: {
|
|
@@ -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
|
|
@@ -1854,6 +1936,66 @@ function commandExecutor(handler) {
|
|
|
1854
1936
|
};
|
|
1855
1937
|
}
|
|
1856
1938
|
|
|
1939
|
+
const executeChain = async ({ llm, prompt, variables, parser, }) => {
|
|
1940
|
+
if (!llm || !prompt || !variables) {
|
|
1941
|
+
throw new Error('The input parameters "llm", "prompt", and "variables" are all required.');
|
|
1942
|
+
}
|
|
1943
|
+
const chain = prompt.pipe(llm).pipe(parser);
|
|
1944
|
+
let res;
|
|
1945
|
+
try {
|
|
1946
|
+
res = await chain.invoke(variables);
|
|
1947
|
+
}
|
|
1948
|
+
catch (error) {
|
|
1949
|
+
if (error instanceof Error) {
|
|
1950
|
+
throw new Error(`LLMChain call error: ${error.message}`);
|
|
1951
|
+
}
|
|
1952
|
+
}
|
|
1953
|
+
if (!res) {
|
|
1954
|
+
throw new Error('Empty response from LLMChain call');
|
|
1955
|
+
}
|
|
1956
|
+
return res;
|
|
1957
|
+
};
|
|
1958
|
+
|
|
1959
|
+
/**
|
|
1960
|
+
* Get LLM Model Based on Configuration
|
|
1961
|
+
*
|
|
1962
|
+
* @param fields
|
|
1963
|
+
* @param configuration
|
|
1964
|
+
* @returns LLM Model
|
|
1965
|
+
*/
|
|
1966
|
+
function getLlm(provider, model, config) {
|
|
1967
|
+
if (!model) {
|
|
1968
|
+
throw new Error(`Invalid LLM Service: ${provider}/${model}`);
|
|
1969
|
+
}
|
|
1970
|
+
switch (provider) {
|
|
1971
|
+
case 'ollama':
|
|
1972
|
+
return new ollama.ChatOllama({
|
|
1973
|
+
baseUrl: DEFAULT_OLLAMA_LLM_SERVICE.endpoint,
|
|
1974
|
+
maxConcurrency: config.service.maxConcurrent,
|
|
1975
|
+
model,
|
|
1976
|
+
});
|
|
1977
|
+
case 'openai':
|
|
1978
|
+
default:
|
|
1979
|
+
const openAiModel = new openai.ChatOpenAI({
|
|
1980
|
+
openAIApiKey: getApiKeyForModel(config),
|
|
1981
|
+
model,
|
|
1982
|
+
temperature: config.service.temperature || 0.2,
|
|
1983
|
+
});
|
|
1984
|
+
return openAiModel;
|
|
1985
|
+
}
|
|
1986
|
+
}
|
|
1987
|
+
|
|
1988
|
+
function getPrompt({ template, variables, fallback }) {
|
|
1989
|
+
if (!template && !fallback)
|
|
1990
|
+
throw new Error('Must provide either a template or a fallback');
|
|
1991
|
+
return (template
|
|
1992
|
+
? new prompts.PromptTemplate({
|
|
1993
|
+
template,
|
|
1994
|
+
inputVariables: variables,
|
|
1995
|
+
})
|
|
1996
|
+
: fallback);
|
|
1997
|
+
}
|
|
1998
|
+
|
|
1857
1999
|
/**
|
|
1858
2000
|
* Extract the path from a file path string.
|
|
1859
2001
|
* @param {string} filePath - The full file path.
|
|
@@ -5437,64 +5579,15 @@ async function fileChangeParser({ changes, commit, options: { tokenizer, git, ll
|
|
|
5437
5579
|
return summary;
|
|
5438
5580
|
}
|
|
5439
5581
|
|
|
5440
|
-
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. The detailed summary should be separated from the short description by a blank line. Please follow the guidelines below when writing your commit message:
|
|
5442
|
-
|
|
5443
|
-
- Write concisely using an informal tone
|
|
5444
|
-
- DO NOT use phrases like "this commit", "this change", "this function", etc. Instead refer to the function, variable, or class by name
|
|
5445
|
-
- DO NOT use specific names or files from the code
|
|
5446
|
-
- DO NOT include any diffs or file changes in the commit message
|
|
5447
|
-
- Wrap variable, class, function, components, and dependency names in back ticks e.g. \`variable\`
|
|
5448
|
-
- ONLY respond with the resulting commit message.
|
|
5449
|
-
|
|
5450
|
-
--------
|
|
5451
|
-
{summary}
|
|
5452
|
-
--------
|
|
5453
|
-
|
|
5454
|
-
COMMIT MESSAGE:`;
|
|
5455
|
-
const inputVariables$1 = ['summary'];
|
|
5456
|
-
const COMMIT_PROMPT = new prompts.PromptTemplate({
|
|
5457
|
-
template: template$1,
|
|
5458
|
-
inputVariables: inputVariables$1,
|
|
5459
|
-
});
|
|
5460
|
-
|
|
5461
5582
|
/**
|
|
5462
|
-
*
|
|
5583
|
+
* Creates a commit with the specified commit message.
|
|
5463
5584
|
*
|
|
5464
|
-
* @param
|
|
5465
|
-
* @param
|
|
5466
|
-
* @returns
|
|
5585
|
+
* @param message The commit message.
|
|
5586
|
+
* @param git The SimpleGit instance.
|
|
5587
|
+
* @returns A Promise that resolves to the CommitResult.
|
|
5467
5588
|
*/
|
|
5468
|
-
function
|
|
5469
|
-
|
|
5470
|
-
throw new Error(`Invalid LLM Service: ${provider}/${model}`);
|
|
5471
|
-
}
|
|
5472
|
-
switch (provider) {
|
|
5473
|
-
case 'ollama':
|
|
5474
|
-
return new ollama.ChatOllama({
|
|
5475
|
-
baseUrl: DEFAULT_OLLAMA_LLM_SERVICE.endpoint,
|
|
5476
|
-
maxConcurrency: config.service.maxConcurrent,
|
|
5477
|
-
model,
|
|
5478
|
-
});
|
|
5479
|
-
case 'openai':
|
|
5480
|
-
default:
|
|
5481
|
-
const openAiModel = new openai.ChatOpenAI({
|
|
5482
|
-
openAIApiKey: getApiKeyForModel(config),
|
|
5483
|
-
model,
|
|
5484
|
-
});
|
|
5485
|
-
return openAiModel;
|
|
5486
|
-
}
|
|
5487
|
-
}
|
|
5488
|
-
|
|
5489
|
-
function getPrompt({ template, variables, fallback }) {
|
|
5490
|
-
if (!template && !fallback)
|
|
5491
|
-
throw new Error('Must provide either a template or a fallback');
|
|
5492
|
-
return (template
|
|
5493
|
-
? new prompts.PromptTemplate({
|
|
5494
|
-
template,
|
|
5495
|
-
inputVariables: variables,
|
|
5496
|
-
})
|
|
5497
|
-
: fallback);
|
|
5589
|
+
async function createCommit(message, git) {
|
|
5590
|
+
return await git.commit(message);
|
|
5498
5591
|
}
|
|
5499
5592
|
|
|
5500
5593
|
/**
|
|
@@ -5626,37 +5719,66 @@ async function getChanges({ git, options }) {
|
|
|
5626
5719
|
};
|
|
5627
5720
|
}
|
|
5628
5721
|
|
|
5629
|
-
|
|
5630
|
-
|
|
5631
|
-
|
|
5632
|
-
|
|
5633
|
-
|
|
5634
|
-
|
|
5635
|
-
|
|
5636
|
-
|
|
5722
|
+
/**
|
|
5723
|
+
* Retrieves the SimpleGit instance for the repository.
|
|
5724
|
+
* @returns {SimpleGit} The SimpleGit instance.
|
|
5725
|
+
*/
|
|
5726
|
+
const getRepo = () => {
|
|
5727
|
+
let git;
|
|
5728
|
+
try {
|
|
5729
|
+
git = simpleGit.simpleGit();
|
|
5637
5730
|
}
|
|
5638
|
-
|
|
5639
|
-
|
|
5640
|
-
|
|
5641
|
-
logger.log('\nChanges not staged for commit:', { color: 'yellow' });
|
|
5642
|
-
logger.verbose(`\t${unstaged.map(({ summary }) => summary).join('\n\t')}`, {
|
|
5643
|
-
color: 'red',
|
|
5644
|
-
});
|
|
5645
|
-
}
|
|
5646
|
-
if (hasUntracked) {
|
|
5647
|
-
logger.log('\nUntracked changes:', { color: 'yellow' });
|
|
5648
|
-
logger.verbose(`\t${untracked.map(({ summary }) => summary).join('\n\t')}`, {
|
|
5649
|
-
color: 'red',
|
|
5650
|
-
});
|
|
5651
|
-
}
|
|
5731
|
+
catch (e) {
|
|
5732
|
+
console.log('Error initializing git repo', e);
|
|
5733
|
+
process.exit(1);
|
|
5652
5734
|
}
|
|
5653
|
-
|
|
5654
|
-
|
|
5735
|
+
return git;
|
|
5736
|
+
};
|
|
5737
|
+
|
|
5738
|
+
const template$1 = `Write informative git commit message, in the imperative, based on the diffs & file changes provided in the "Diff Summary" section.
|
|
5739
|
+
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:
|
|
5740
|
+
|
|
5741
|
+
- Write concisely using an informal tone
|
|
5742
|
+
- DO NOT use phrases like "this commit", "this change", "this function", etc. Instead refer to the function, variable, or class by name
|
|
5743
|
+
- DO NOT use specific names or files from the code
|
|
5744
|
+
- DO NOT include any diffs or file changes in the commit message
|
|
5745
|
+
- Wrap variable, class, function, components, and dependency names in back ticks e.g. \`variable\`
|
|
5746
|
+
|
|
5747
|
+
{format_instructions}
|
|
5748
|
+
|
|
5749
|
+
"""{summary}"""`;
|
|
5750
|
+
const inputVariables$1 = ['summary', 'format_instructions'];
|
|
5751
|
+
const COMMIT_PROMPT = new prompts.PromptTemplate({
|
|
5752
|
+
template: template$1,
|
|
5753
|
+
inputVariables: inputVariables$1,
|
|
5754
|
+
});
|
|
5755
|
+
|
|
5756
|
+
/**
|
|
5757
|
+
* Verify template string contains all required input variables
|
|
5758
|
+
*
|
|
5759
|
+
* @param text template string
|
|
5760
|
+
* @param inputVariables template variables
|
|
5761
|
+
* @returns boolean or error message
|
|
5762
|
+
*/
|
|
5763
|
+
function validatePromptTemplate(text, inputVariables) {
|
|
5764
|
+
if (!text) {
|
|
5765
|
+
return 'Prompt template cannot be empty';
|
|
5655
5766
|
}
|
|
5767
|
+
if (!inputVariables.some((entry) => text.includes(entry))) {
|
|
5768
|
+
return ('Prompt template must include at least one of the following input variables: ' +
|
|
5769
|
+
inputVariables.map((value) => `{${value}}`).join(', '));
|
|
5770
|
+
}
|
|
5771
|
+
return true;
|
|
5656
5772
|
}
|
|
5657
5773
|
|
|
5658
|
-
function
|
|
5659
|
-
|
|
5774
|
+
async function editPrompt(options) {
|
|
5775
|
+
return await prompts$1.editor({
|
|
5776
|
+
message: 'Edit the prompt',
|
|
5777
|
+
default: options.prompt?.length ? options.prompt : COMMIT_PROMPT.template,
|
|
5778
|
+
waitForUseInput: false,
|
|
5779
|
+
postfix: 'Press ENTER to continue',
|
|
5780
|
+
validate: (text) => validatePromptTemplate(text, COMMIT_PROMPT.inputVariables),
|
|
5781
|
+
});
|
|
5660
5782
|
}
|
|
5661
5783
|
|
|
5662
5784
|
async function editResult(result, options) {
|
|
@@ -5718,32 +5840,8 @@ async function getUserReviewDecision({ label, descriptions, enableRetry = true,
|
|
|
5718
5840
|
}));
|
|
5719
5841
|
}
|
|
5720
5842
|
|
|
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
|
-
});
|
|
5843
|
+
function logResult(label, result) {
|
|
5844
|
+
console.log(`\n${chalk.bgBlue(chalk.bold(`Proposed ${label}:`))}\n${SEPERATOR}\n${result}\n${SEPERATOR}\n`);
|
|
5747
5845
|
}
|
|
5748
5846
|
|
|
5749
5847
|
async function generateAndReviewLoop({ label, factory, parser, noResult, agent, options, }) {
|
|
@@ -5754,7 +5852,7 @@ async function generateAndReviewLoop({ label, factory, parser, noResult, agent,
|
|
|
5754
5852
|
let result = '';
|
|
5755
5853
|
const changes = await factory();
|
|
5756
5854
|
// if we don't have any changes, bail.
|
|
5757
|
-
if (!changes || !changes.length) {
|
|
5855
|
+
if (!changes || !Object.keys(changes).length) {
|
|
5758
5856
|
await noResult(options);
|
|
5759
5857
|
}
|
|
5760
5858
|
while (continueLoop) {
|
|
@@ -5789,7 +5887,7 @@ async function generateAndReviewLoop({ label, factory, parser, noResult, agent,
|
|
|
5789
5887
|
logResult(label, result);
|
|
5790
5888
|
const reviewAnswer = await getUserReviewDecision({
|
|
5791
5889
|
label,
|
|
5792
|
-
...options?.review || {},
|
|
5890
|
+
...(options?.review || {}),
|
|
5793
5891
|
});
|
|
5794
5892
|
if (reviewAnswer === 'cancel') {
|
|
5795
5893
|
process.exit(0);
|
|
@@ -5821,26 +5919,6 @@ async function generateAndReviewLoop({ label, factory, parser, noResult, agent,
|
|
|
5821
5919
|
return result;
|
|
5822
5920
|
}
|
|
5823
5921
|
|
|
5824
|
-
const executeChain = async ({ llm, prompt, variables }) => {
|
|
5825
|
-
if (!llm || !prompt || !variables) {
|
|
5826
|
-
throw new Error('The input parameters "llm", "prompt", and "variables" are all required.');
|
|
5827
|
-
}
|
|
5828
|
-
const chain = prompt.pipe(llm);
|
|
5829
|
-
let res;
|
|
5830
|
-
try {
|
|
5831
|
-
res = await chain.invoke(variables);
|
|
5832
|
-
}
|
|
5833
|
-
catch (error) {
|
|
5834
|
-
if (error instanceof Error) {
|
|
5835
|
-
throw new Error(`LLMChain call error: ${error.message}`);
|
|
5836
|
-
}
|
|
5837
|
-
}
|
|
5838
|
-
if (!res) {
|
|
5839
|
-
throw new Error('Empty response from LLMChain call');
|
|
5840
|
-
}
|
|
5841
|
-
return res.content;
|
|
5842
|
-
};
|
|
5843
|
-
|
|
5844
5922
|
const logSuccess = () => {
|
|
5845
5923
|
console.log(chalk.green(chalk.bold('\nAll set! 🦾🤖')));
|
|
5846
5924
|
};
|
|
@@ -5864,22 +5942,6 @@ async function handleResult({ result, mode, interactiveHandler }) {
|
|
|
5864
5942
|
process.exit(0);
|
|
5865
5943
|
}
|
|
5866
5944
|
|
|
5867
|
-
/**
|
|
5868
|
-
* Retrieves the SimpleGit instance for the repository.
|
|
5869
|
-
* @returns {SimpleGit} The SimpleGit instance.
|
|
5870
|
-
*/
|
|
5871
|
-
const getRepo = () => {
|
|
5872
|
-
let git;
|
|
5873
|
-
try {
|
|
5874
|
-
git = simpleGit.simpleGit();
|
|
5875
|
-
}
|
|
5876
|
-
catch (e) {
|
|
5877
|
-
console.log('Error initializing git repo', e);
|
|
5878
|
-
process.exit(1);
|
|
5879
|
-
}
|
|
5880
|
-
return git;
|
|
5881
|
-
};
|
|
5882
|
-
|
|
5883
5945
|
/**
|
|
5884
5946
|
* Retrieves a TikToken for the specified model.
|
|
5885
5947
|
*
|
|
@@ -5902,15 +5964,33 @@ const getTokenCounter = async (modelName) => {
|
|
|
5902
5964
|
});
|
|
5903
5965
|
};
|
|
5904
5966
|
|
|
5905
|
-
|
|
5906
|
-
|
|
5907
|
-
|
|
5908
|
-
|
|
5909
|
-
|
|
5910
|
-
|
|
5911
|
-
|
|
5912
|
-
|
|
5913
|
-
|
|
5967
|
+
async function noResult({ git, logger }) {
|
|
5968
|
+
const { staged, unstaged, untracked } = await getChanges({ git });
|
|
5969
|
+
const hasStaged = staged && staged.length > 0;
|
|
5970
|
+
const hasUnstaged = unstaged && unstaged.length > 0;
|
|
5971
|
+
const hasUntracked = untracked && untracked.length > 0;
|
|
5972
|
+
if (hasStaged) {
|
|
5973
|
+
logger.log(`Staged files detected, but no summary generated...`, { color: 'red' });
|
|
5974
|
+
logger.log(`Files are likely either:\n • changed files are ignored\n • file diff is too large.`, { color: 'yellow' });
|
|
5975
|
+
}
|
|
5976
|
+
else if (hasUnstaged || hasUntracked) {
|
|
5977
|
+
logger.log('Forget something? No staged changes found... 👻', { color: 'red' });
|
|
5978
|
+
if (hasUnstaged) {
|
|
5979
|
+
logger.log('\nChanges not staged for commit:', { color: 'yellow' });
|
|
5980
|
+
logger.verbose(`\t${unstaged.map(({ summary }) => summary).join('\n\t')}`, {
|
|
5981
|
+
color: 'red',
|
|
5982
|
+
});
|
|
5983
|
+
}
|
|
5984
|
+
if (hasUntracked) {
|
|
5985
|
+
logger.log('\nUntracked changes:', { color: 'yellow' });
|
|
5986
|
+
logger.verbose(`\t${untracked.map(({ summary }) => summary).join('\n\t')}`, {
|
|
5987
|
+
color: 'red',
|
|
5988
|
+
});
|
|
5989
|
+
}
|
|
5990
|
+
}
|
|
5991
|
+
else {
|
|
5992
|
+
logger.log('No repo changes detected. 👀', { color: 'blue' });
|
|
5993
|
+
}
|
|
5914
5994
|
}
|
|
5915
5995
|
|
|
5916
5996
|
const handler$2 = async (argv, logger) => {
|
|
@@ -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(${hash}) - ${author_name}<${author_email}>`);
|
|
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
|
+
- Include the git commit hash as reference for each change, including just the first 7 characters
|
|
6215
|
+
- Logically group changes, and if necessary, 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
|
-
const result =
|
|
6259
|
+
async function parser({ branch, commits }) {
|
|
6260
|
+
const result = `## ${branch}\n\n${commits.map((commit) => `${commit}`).join('\n\n\n')}`;
|
|
6261
|
+
console.log({ result });
|
|
6170
6262
|
return result;
|
|
6171
6263
|
}
|
|
6172
6264
|
const changelogMsg = await generateAndReviewLoop({
|
|
@@ -6174,16 +6266,23 @@ 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', both strings.";
|
|
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
|
+
return `${changelog.header}\n\n${changelog.content}`;
|
|
6187
6286
|
},
|
|
6188
6287
|
noResult: async () => {
|
|
6189
6288
|
if (config.range) {
|
|
@@ -6222,17 +6321,6 @@ const options$1 = {
|
|
|
6222
6321
|
alias: 'r',
|
|
6223
6322
|
description: 'Commit range e.g `HEAD~2:HEAD`',
|
|
6224
6323
|
},
|
|
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
6324
|
tokenLimit: { type: 'number', description: 'Token limit' },
|
|
6237
6325
|
prompt: {
|
|
6238
6326
|
type: 'string',
|
|
@@ -6253,14 +6341,6 @@ const options$1 = {
|
|
|
6253
6341
|
type: 'string',
|
|
6254
6342
|
description: 'Prompt for summarizing large files',
|
|
6255
6343
|
},
|
|
6256
|
-
ignoredFiles: {
|
|
6257
|
-
type: 'array',
|
|
6258
|
-
description: 'Ignored files',
|
|
6259
|
-
},
|
|
6260
|
-
ignoredExtensions: {
|
|
6261
|
-
type: 'array',
|
|
6262
|
-
description: 'Ignored extensions',
|
|
6263
|
-
},
|
|
6264
6344
|
};
|
|
6265
6345
|
const builder$1 = (yargs) => {
|
|
6266
6346
|
return yargs.options(options$1);
|