git-coco 0.11.0 → 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 -1
- package/dist/index.esm.mjs +135 -136
- package/dist/index.js +135 -136
- package/package.json +7 -7
package/dist/index.d.ts
CHANGED
|
@@ -75,7 +75,7 @@ declare const _default$1: {
|
|
|
75
75
|
};
|
|
76
76
|
|
|
77
77
|
type LLMProvider = 'openai' | 'ollama';
|
|
78
|
-
type OllamaModel = '
|
|
78
|
+
type OllamaModel = 'codegemma:2b' | 'codegemma:7b-code' | 'codegemma' | 'codellama:13b' | 'codellama:34b' | 'codellama:70b' | 'codellama:7b' | 'codellama:instruct' | 'codellama:latest' | 'codellama' | 'gemma:2b' | 'gemma:7b' | 'gemma:latest' | 'gemma' | 'llama2:13b' | 'llama2:70b' | 'llama2:chat' | 'llama2:latest' | 'llama2:text' | 'llama2' | 'llama3:70b-text' | 'llama3:70b' | 'llama3:latest' | 'llama3:text' | 'llama3.1:70b' | 'llama3.1:8b' | 'llama3.1:latest' | 'llama3.2' | 'llama3.2:latest' | 'llama3.2:1b' | 'llama3.2:3b' | 'llama3.2:1b-instruct-fp16' | 'llama3.2:1b-instruct-q3_K_M' | 'llama3' | 'mistral:7b' | 'mistral:latest' | 'mistral:text' | 'mistral' | 'phi3:14b' | 'phi3:3.8b' | 'phi3:instruct' | 'phi3:medium-128k' | 'phi3:medium-4k' | 'phi3:medium' | 'phi3' | 'qwen2:0.5b' | 'qwen2:1.5b' | 'qwen2:72b-text' | 'qwen2:72b' | 'qwen2';
|
|
79
79
|
type LLMModel = TiktokenModel | OllamaModel;
|
|
80
80
|
type BaseLLMService = {
|
|
81
81
|
provider: LLMProvider;
|
package/dist/index.esm.mjs
CHANGED
|
@@ -15,23 +15,23 @@ import Ajv from 'ajv';
|
|
|
15
15
|
import ora from 'ora';
|
|
16
16
|
import now from 'performance-now';
|
|
17
17
|
import prettyMilliseconds from 'pretty-ms';
|
|
18
|
+
import { BaseOutputParser, JsonOutputParser } from '@langchain/core/output_parsers';
|
|
19
|
+
import { ChatOllama } from '@langchain/ollama';
|
|
20
|
+
import { ChatOpenAI } from '@langchain/openai';
|
|
18
21
|
import pQueue from 'p-queue';
|
|
19
22
|
import { Document, BaseDocumentTransformer } from '@langchain/core/documents';
|
|
20
23
|
import { RUN_KEY } from '@langchain/core/outputs';
|
|
21
24
|
import { CallbackManager, parseCallbackConfigArg } from '@langchain/core/callbacks/manager';
|
|
22
25
|
import { ensureConfig, Runnable } from '@langchain/core/runnables';
|
|
23
26
|
import { BaseLangChain, BaseLanguageModel } from '@langchain/core/language_models/base';
|
|
24
|
-
import { BaseOutputParser, JsonOutputParser } from '@langchain/core/output_parsers';
|
|
25
27
|
import '@langchain/core/messages';
|
|
26
28
|
import '@langchain/core/memory';
|
|
27
29
|
import '@langchain/core/chat_history';
|
|
28
30
|
import '@langchain/core/utils/tiktoken';
|
|
29
|
-
import { ChatOpenAI } from '@langchain/openai';
|
|
30
31
|
import '@langchain/core/utils/async_caller';
|
|
31
32
|
import '@langchain/core/utils/env';
|
|
32
33
|
import '@langchain/core/utils/json_patch';
|
|
33
34
|
import { createTwoFilesPatch } from 'diff';
|
|
34
|
-
import { ChatOllama } from '@langchain/ollama';
|
|
35
35
|
import { minimatch } from 'minimatch';
|
|
36
36
|
import { simpleGit } from 'simple-git';
|
|
37
37
|
import { encoding_for_model } from 'tiktoken';
|
|
@@ -107,9 +107,9 @@ function getDefaultServiceApiKey(config) {
|
|
|
107
107
|
}
|
|
108
108
|
const DEFAULT_OPENAI_LLM_SERVICE = {
|
|
109
109
|
provider: 'openai',
|
|
110
|
-
model: 'gpt-4
|
|
110
|
+
model: 'gpt-4',
|
|
111
111
|
tokenLimit: 2024,
|
|
112
|
-
temperature: 0.
|
|
112
|
+
temperature: 0.32,
|
|
113
113
|
authentication: {
|
|
114
114
|
type: 'APIKey',
|
|
115
115
|
credentials: {
|
|
@@ -1915,6 +1915,66 @@ function commandExecutor(handler) {
|
|
|
1915
1915
|
};
|
|
1916
1916
|
}
|
|
1917
1917
|
|
|
1918
|
+
const executeChain = async ({ llm, prompt, variables, parser, }) => {
|
|
1919
|
+
if (!llm || !prompt || !variables) {
|
|
1920
|
+
throw new Error('The input parameters "llm", "prompt", and "variables" are all required.');
|
|
1921
|
+
}
|
|
1922
|
+
const chain = prompt.pipe(llm).pipe(parser);
|
|
1923
|
+
let res;
|
|
1924
|
+
try {
|
|
1925
|
+
res = await chain.invoke(variables);
|
|
1926
|
+
}
|
|
1927
|
+
catch (error) {
|
|
1928
|
+
if (error instanceof Error) {
|
|
1929
|
+
throw new Error(`LLMChain call error: ${error.message}`);
|
|
1930
|
+
}
|
|
1931
|
+
}
|
|
1932
|
+
if (!res) {
|
|
1933
|
+
throw new Error('Empty response from LLMChain call');
|
|
1934
|
+
}
|
|
1935
|
+
return res;
|
|
1936
|
+
};
|
|
1937
|
+
|
|
1938
|
+
/**
|
|
1939
|
+
* Get LLM Model Based on Configuration
|
|
1940
|
+
*
|
|
1941
|
+
* @param fields
|
|
1942
|
+
* @param configuration
|
|
1943
|
+
* @returns LLM Model
|
|
1944
|
+
*/
|
|
1945
|
+
function getLlm(provider, model, config) {
|
|
1946
|
+
if (!model) {
|
|
1947
|
+
throw new Error(`Invalid LLM Service: ${provider}/${model}`);
|
|
1948
|
+
}
|
|
1949
|
+
switch (provider) {
|
|
1950
|
+
case 'ollama':
|
|
1951
|
+
return new ChatOllama({
|
|
1952
|
+
baseUrl: DEFAULT_OLLAMA_LLM_SERVICE.endpoint,
|
|
1953
|
+
maxConcurrency: config.service.maxConcurrent,
|
|
1954
|
+
model,
|
|
1955
|
+
});
|
|
1956
|
+
case 'openai':
|
|
1957
|
+
default:
|
|
1958
|
+
const openAiModel = new ChatOpenAI({
|
|
1959
|
+
openAIApiKey: getApiKeyForModel(config),
|
|
1960
|
+
model,
|
|
1961
|
+
temperature: config.service.temperature || 0.2,
|
|
1962
|
+
});
|
|
1963
|
+
return openAiModel;
|
|
1964
|
+
}
|
|
1965
|
+
}
|
|
1966
|
+
|
|
1967
|
+
function getPrompt({ template, variables, fallback }) {
|
|
1968
|
+
if (!template && !fallback)
|
|
1969
|
+
throw new Error('Must provide either a template or a fallback');
|
|
1970
|
+
return (template
|
|
1971
|
+
? new PromptTemplate({
|
|
1972
|
+
template,
|
|
1973
|
+
inputVariables: variables,
|
|
1974
|
+
})
|
|
1975
|
+
: fallback);
|
|
1976
|
+
}
|
|
1977
|
+
|
|
1918
1978
|
/**
|
|
1919
1979
|
* Extract the path from a file path string.
|
|
1920
1980
|
* @param {string} filePath - The full file path.
|
|
@@ -5498,62 +5558,15 @@ async function fileChangeParser({ changes, commit, options: { tokenizer, git, ll
|
|
|
5498
5558
|
return summary;
|
|
5499
5559
|
}
|
|
5500
5560
|
|
|
5501
|
-
const template$1 = `Write informative git commit message, in the imperative, based on the diffs & file changes provided in the "Diff Summary" section.
|
|
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:
|
|
5503
|
-
|
|
5504
|
-
- Write concisely using an informal tone
|
|
5505
|
-
- DO NOT use phrases like "this commit", "this change", "this function", etc. Instead refer to the function, variable, or class by name
|
|
5506
|
-
- DO NOT use specific names or files from the code
|
|
5507
|
-
- DO NOT include any diffs or file changes in the commit message
|
|
5508
|
-
- Wrap variable, class, function, components, and dependency names in back ticks e.g. \`variable\`
|
|
5509
|
-
|
|
5510
|
-
{format_instructions}
|
|
5511
|
-
|
|
5512
|
-
"""{summary}"""`;
|
|
5513
|
-
const inputVariables$1 = ['summary', 'format_instructions'];
|
|
5514
|
-
const COMMIT_PROMPT = new PromptTemplate({
|
|
5515
|
-
template: template$1,
|
|
5516
|
-
inputVariables: inputVariables$1,
|
|
5517
|
-
});
|
|
5518
|
-
|
|
5519
5561
|
/**
|
|
5520
|
-
*
|
|
5562
|
+
* Creates a commit with the specified commit message.
|
|
5521
5563
|
*
|
|
5522
|
-
* @param
|
|
5523
|
-
* @param
|
|
5524
|
-
* @returns
|
|
5564
|
+
* @param message The commit message.
|
|
5565
|
+
* @param git The SimpleGit instance.
|
|
5566
|
+
* @returns A Promise that resolves to the CommitResult.
|
|
5525
5567
|
*/
|
|
5526
|
-
function
|
|
5527
|
-
|
|
5528
|
-
throw new Error(`Invalid LLM Service: ${provider}/${model}`);
|
|
5529
|
-
}
|
|
5530
|
-
switch (provider) {
|
|
5531
|
-
case 'ollama':
|
|
5532
|
-
return new ChatOllama({
|
|
5533
|
-
baseUrl: DEFAULT_OLLAMA_LLM_SERVICE.endpoint,
|
|
5534
|
-
maxConcurrency: config.service.maxConcurrent,
|
|
5535
|
-
model,
|
|
5536
|
-
});
|
|
5537
|
-
case 'openai':
|
|
5538
|
-
default:
|
|
5539
|
-
const openAiModel = new ChatOpenAI({
|
|
5540
|
-
openAIApiKey: getApiKeyForModel(config),
|
|
5541
|
-
model,
|
|
5542
|
-
temperature: config.service.temperature || 0.2,
|
|
5543
|
-
});
|
|
5544
|
-
return openAiModel;
|
|
5545
|
-
}
|
|
5546
|
-
}
|
|
5547
|
-
|
|
5548
|
-
function getPrompt({ template, variables, fallback }) {
|
|
5549
|
-
if (!template && !fallback)
|
|
5550
|
-
throw new Error('Must provide either a template or a fallback');
|
|
5551
|
-
return (template
|
|
5552
|
-
? new PromptTemplate({
|
|
5553
|
-
template,
|
|
5554
|
-
inputVariables: variables,
|
|
5555
|
-
})
|
|
5556
|
-
: fallback);
|
|
5568
|
+
async function createCommit(message, git) {
|
|
5569
|
+
return await git.commit(message);
|
|
5557
5570
|
}
|
|
5558
5571
|
|
|
5559
5572
|
/**
|
|
@@ -5685,34 +5698,39 @@ async function getChanges({ git, options }) {
|
|
|
5685
5698
|
};
|
|
5686
5699
|
}
|
|
5687
5700
|
|
|
5688
|
-
|
|
5689
|
-
|
|
5690
|
-
|
|
5691
|
-
|
|
5692
|
-
|
|
5693
|
-
|
|
5694
|
-
|
|
5695
|
-
|
|
5696
|
-
}
|
|
5697
|
-
else if (hasUnstaged || hasUntracked) {
|
|
5698
|
-
logger.log('Forget something? No staged changes found... 👻', { color: 'red' });
|
|
5699
|
-
if (hasUnstaged) {
|
|
5700
|
-
logger.log('\nChanges not staged for commit:', { color: 'yellow' });
|
|
5701
|
-
logger.verbose(`\t${unstaged.map(({ summary }) => summary).join('\n\t')}`, {
|
|
5702
|
-
color: 'red',
|
|
5703
|
-
});
|
|
5704
|
-
}
|
|
5705
|
-
if (hasUntracked) {
|
|
5706
|
-
logger.log('\nUntracked changes:', { color: 'yellow' });
|
|
5707
|
-
logger.verbose(`\t${untracked.map(({ summary }) => summary).join('\n\t')}`, {
|
|
5708
|
-
color: 'red',
|
|
5709
|
-
});
|
|
5710
|
-
}
|
|
5701
|
+
/**
|
|
5702
|
+
* Retrieves the SimpleGit instance for the repository.
|
|
5703
|
+
* @returns {SimpleGit} The SimpleGit instance.
|
|
5704
|
+
*/
|
|
5705
|
+
const getRepo = () => {
|
|
5706
|
+
let git;
|
|
5707
|
+
try {
|
|
5708
|
+
git = simpleGit();
|
|
5711
5709
|
}
|
|
5712
|
-
|
|
5713
|
-
|
|
5710
|
+
catch (e) {
|
|
5711
|
+
console.log('Error initializing git repo', e);
|
|
5712
|
+
process.exit(1);
|
|
5714
5713
|
}
|
|
5715
|
-
|
|
5714
|
+
return git;
|
|
5715
|
+
};
|
|
5716
|
+
|
|
5717
|
+
const template$1 = `Write informative git commit message, in the imperative, based on the diffs & file changes provided in the "Diff Summary" section.
|
|
5718
|
+
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:
|
|
5719
|
+
|
|
5720
|
+
- Write concisely using an informal tone
|
|
5721
|
+
- DO NOT use phrases like "this commit", "this change", "this function", etc. Instead refer to the function, variable, or class by name
|
|
5722
|
+
- DO NOT use specific names or files from the code
|
|
5723
|
+
- DO NOT include any diffs or file changes in the commit message
|
|
5724
|
+
- Wrap variable, class, function, components, and dependency names in back ticks e.g. \`variable\`
|
|
5725
|
+
|
|
5726
|
+
{format_instructions}
|
|
5727
|
+
|
|
5728
|
+
"""{summary}"""`;
|
|
5729
|
+
const inputVariables$1 = ['summary', 'format_instructions'];
|
|
5730
|
+
const COMMIT_PROMPT = new PromptTemplate({
|
|
5731
|
+
template: template$1,
|
|
5732
|
+
inputVariables: inputVariables$1,
|
|
5733
|
+
});
|
|
5716
5734
|
|
|
5717
5735
|
/**
|
|
5718
5736
|
* Verify template string contains all required input variables
|
|
@@ -5880,26 +5898,6 @@ async function generateAndReviewLoop({ label, factory, parser, noResult, agent,
|
|
|
5880
5898
|
return result;
|
|
5881
5899
|
}
|
|
5882
5900
|
|
|
5883
|
-
const executeChain = async ({ llm, prompt, variables, parser, }) => {
|
|
5884
|
-
if (!llm || !prompt || !variables) {
|
|
5885
|
-
throw new Error('The input parameters "llm", "prompt", and "variables" are all required.');
|
|
5886
|
-
}
|
|
5887
|
-
const chain = prompt.pipe(llm).pipe(parser);
|
|
5888
|
-
let res;
|
|
5889
|
-
try {
|
|
5890
|
-
res = await chain.invoke(variables);
|
|
5891
|
-
}
|
|
5892
|
-
catch (error) {
|
|
5893
|
-
if (error instanceof Error) {
|
|
5894
|
-
throw new Error(`LLMChain call error: ${error.message}`);
|
|
5895
|
-
}
|
|
5896
|
-
}
|
|
5897
|
-
if (!res) {
|
|
5898
|
-
throw new Error('Empty response from LLMChain call');
|
|
5899
|
-
}
|
|
5900
|
-
return res;
|
|
5901
|
-
};
|
|
5902
|
-
|
|
5903
5901
|
const logSuccess = () => {
|
|
5904
5902
|
console.log(chalk.green(chalk.bold('\nAll set! 🦾🤖')));
|
|
5905
5903
|
};
|
|
@@ -5923,22 +5921,6 @@ async function handleResult({ result, mode, interactiveHandler }) {
|
|
|
5923
5921
|
process.exit(0);
|
|
5924
5922
|
}
|
|
5925
5923
|
|
|
5926
|
-
/**
|
|
5927
|
-
* Retrieves the SimpleGit instance for the repository.
|
|
5928
|
-
* @returns {SimpleGit} The SimpleGit instance.
|
|
5929
|
-
*/
|
|
5930
|
-
const getRepo = () => {
|
|
5931
|
-
let git;
|
|
5932
|
-
try {
|
|
5933
|
-
git = simpleGit();
|
|
5934
|
-
}
|
|
5935
|
-
catch (e) {
|
|
5936
|
-
console.log('Error initializing git repo', e);
|
|
5937
|
-
process.exit(1);
|
|
5938
|
-
}
|
|
5939
|
-
return git;
|
|
5940
|
-
};
|
|
5941
|
-
|
|
5942
5924
|
/**
|
|
5943
5925
|
* Retrieves a TikToken for the specified model.
|
|
5944
5926
|
*
|
|
@@ -5961,15 +5943,33 @@ const getTokenCounter = async (modelName) => {
|
|
|
5961
5943
|
});
|
|
5962
5944
|
};
|
|
5963
5945
|
|
|
5964
|
-
|
|
5965
|
-
|
|
5966
|
-
|
|
5967
|
-
|
|
5968
|
-
|
|
5969
|
-
|
|
5970
|
-
|
|
5971
|
-
|
|
5972
|
-
|
|
5946
|
+
async function noResult({ git, logger }) {
|
|
5947
|
+
const { staged, unstaged, untracked } = await getChanges({ git });
|
|
5948
|
+
const hasStaged = staged && staged.length > 0;
|
|
5949
|
+
const hasUnstaged = unstaged && unstaged.length > 0;
|
|
5950
|
+
const hasUntracked = untracked && untracked.length > 0;
|
|
5951
|
+
if (hasStaged) {
|
|
5952
|
+
logger.log(`Staged files detected, but no summary generated...`, { color: 'red' });
|
|
5953
|
+
logger.log(`Files are likely either:\n • changed files are ignored\n • file diff is too large.`, { color: 'yellow' });
|
|
5954
|
+
}
|
|
5955
|
+
else if (hasUnstaged || hasUntracked) {
|
|
5956
|
+
logger.log('Forget something? No staged changes found... 👻', { color: 'red' });
|
|
5957
|
+
if (hasUnstaged) {
|
|
5958
|
+
logger.log('\nChanges not staged for commit:', { color: 'yellow' });
|
|
5959
|
+
logger.verbose(`\t${unstaged.map(({ summary }) => summary).join('\n\t')}`, {
|
|
5960
|
+
color: 'red',
|
|
5961
|
+
});
|
|
5962
|
+
}
|
|
5963
|
+
if (hasUntracked) {
|
|
5964
|
+
logger.log('\nUntracked changes:', { color: 'yellow' });
|
|
5965
|
+
logger.verbose(`\t${untracked.map(({ summary }) => summary).join('\n\t')}`, {
|
|
5966
|
+
color: 'red',
|
|
5967
|
+
});
|
|
5968
|
+
}
|
|
5969
|
+
}
|
|
5970
|
+
else {
|
|
5971
|
+
logger.log('No repo changes detected. 👀', { color: 'blue' });
|
|
5972
|
+
}
|
|
5973
5973
|
}
|
|
5974
5974
|
|
|
5975
5975
|
const handler$2 = async (argv, logger) => {
|
|
@@ -6117,7 +6117,7 @@ async function getCommitLogRange(from, to, { noMerges, git }) {
|
|
|
6117
6117
|
try {
|
|
6118
6118
|
const logOptions = { from: `${from}^1`, to, '--no-merges': noMerges };
|
|
6119
6119
|
const commitLog = await git.log(logOptions);
|
|
6120
|
-
return commitLog.all.map(({ message, date, body, author_name, hash, author_email }) => `[${date}] ${message}\n${body}\n - ${author_name}<${author_email}
|
|
6120
|
+
return commitLog.all.map(({ message, date, body, author_name, hash, author_email }) => `[${date}] ${message}\n${body}\n(${hash}) - ${author_name}<${author_email}>`);
|
|
6121
6121
|
}
|
|
6122
6122
|
catch (error) {
|
|
6123
6123
|
// If there's an error, handle it appropriately
|
|
@@ -6190,8 +6190,8 @@ async function getCommitLogCurrentBranch({ git, logger, comparisonBranch = 'main
|
|
|
6190
6190
|
|
|
6191
6191
|
const template = `Write informative git changelog, in the imperative, based on a series of individual messages.
|
|
6192
6192
|
|
|
6193
|
-
-
|
|
6194
|
-
-
|
|
6193
|
+
- Include the git commit hash as reference for each change, including just the first 7 characters
|
|
6194
|
+
- Logically group changes, and if necessary, summarize dependency updates
|
|
6195
6195
|
|
|
6196
6196
|
{format_instructions}
|
|
6197
6197
|
|
|
@@ -6236,8 +6236,8 @@ const handler$1 = async (argv, logger) => {
|
|
|
6236
6236
|
};
|
|
6237
6237
|
}
|
|
6238
6238
|
async function parser({ branch, commits }) {
|
|
6239
|
-
|
|
6240
|
-
|
|
6239
|
+
const result = `## ${branch}\n\n${commits.map((commit) => `${commit}`).join('\n\n\n')}`;
|
|
6240
|
+
console.log({ result });
|
|
6241
6241
|
return result;
|
|
6242
6242
|
}
|
|
6243
6243
|
const changelogMsg = await generateAndReviewLoop({
|
|
@@ -6251,7 +6251,7 @@ const handler$1 = async (argv, logger) => {
|
|
|
6251
6251
|
variables: CHANGELOG_PROMPT.inputVariables,
|
|
6252
6252
|
fallback: CHANGELOG_PROMPT,
|
|
6253
6253
|
});
|
|
6254
|
-
const formatInstructions = "Respond with a valid JSON object, containing two fields: 'header' and 'content'.";
|
|
6254
|
+
const formatInstructions = "Respond with a valid JSON object, containing two fields: 'header' and 'content', both strings.";
|
|
6255
6255
|
const changelog = await executeChain({
|
|
6256
6256
|
llm,
|
|
6257
6257
|
prompt,
|
|
@@ -6261,7 +6261,6 @@ const handler$1 = async (argv, logger) => {
|
|
|
6261
6261
|
},
|
|
6262
6262
|
parser,
|
|
6263
6263
|
});
|
|
6264
|
-
console.log({ changelog });
|
|
6265
6264
|
return `${changelog.header}\n\n${changelog.content}`;
|
|
6266
6265
|
},
|
|
6267
6266
|
noResult: async () => {
|
package/dist/index.js
CHANGED
|
@@ -14,23 +14,23 @@ 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');
|
|
@@ -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: {
|
|
@@ -1936,6 +1936,66 @@ function commandExecutor(handler) {
|
|
|
1936
1936
|
};
|
|
1937
1937
|
}
|
|
1938
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
|
+
|
|
1939
1999
|
/**
|
|
1940
2000
|
* Extract the path from a file path string.
|
|
1941
2001
|
* @param {string} filePath - The full file path.
|
|
@@ -5519,62 +5579,15 @@ async function fileChangeParser({ changes, commit, options: { tokenizer, git, ll
|
|
|
5519
5579
|
return summary;
|
|
5520
5580
|
}
|
|
5521
5581
|
|
|
5522
|
-
const template$1 = `Write informative git commit message, in the imperative, based on the diffs & file changes provided in the "Diff Summary" section.
|
|
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:
|
|
5524
|
-
|
|
5525
|
-
- Write concisely using an informal tone
|
|
5526
|
-
- DO NOT use phrases like "this commit", "this change", "this function", etc. Instead refer to the function, variable, or class by name
|
|
5527
|
-
- DO NOT use specific names or files from the code
|
|
5528
|
-
- DO NOT include any diffs or file changes in the commit message
|
|
5529
|
-
- Wrap variable, class, function, components, and dependency names in back ticks e.g. \`variable\`
|
|
5530
|
-
|
|
5531
|
-
{format_instructions}
|
|
5532
|
-
|
|
5533
|
-
"""{summary}"""`;
|
|
5534
|
-
const inputVariables$1 = ['summary', 'format_instructions'];
|
|
5535
|
-
const COMMIT_PROMPT = new prompts.PromptTemplate({
|
|
5536
|
-
template: template$1,
|
|
5537
|
-
inputVariables: inputVariables$1,
|
|
5538
|
-
});
|
|
5539
|
-
|
|
5540
5582
|
/**
|
|
5541
|
-
*
|
|
5583
|
+
* Creates a commit with the specified commit message.
|
|
5542
5584
|
*
|
|
5543
|
-
* @param
|
|
5544
|
-
* @param
|
|
5545
|
-
* @returns
|
|
5585
|
+
* @param message The commit message.
|
|
5586
|
+
* @param git The SimpleGit instance.
|
|
5587
|
+
* @returns A Promise that resolves to the CommitResult.
|
|
5546
5588
|
*/
|
|
5547
|
-
function
|
|
5548
|
-
|
|
5549
|
-
throw new Error(`Invalid LLM Service: ${provider}/${model}`);
|
|
5550
|
-
}
|
|
5551
|
-
switch (provider) {
|
|
5552
|
-
case 'ollama':
|
|
5553
|
-
return new ollama.ChatOllama({
|
|
5554
|
-
baseUrl: DEFAULT_OLLAMA_LLM_SERVICE.endpoint,
|
|
5555
|
-
maxConcurrency: config.service.maxConcurrent,
|
|
5556
|
-
model,
|
|
5557
|
-
});
|
|
5558
|
-
case 'openai':
|
|
5559
|
-
default:
|
|
5560
|
-
const openAiModel = new openai.ChatOpenAI({
|
|
5561
|
-
openAIApiKey: getApiKeyForModel(config),
|
|
5562
|
-
model,
|
|
5563
|
-
temperature: config.service.temperature || 0.2,
|
|
5564
|
-
});
|
|
5565
|
-
return openAiModel;
|
|
5566
|
-
}
|
|
5567
|
-
}
|
|
5568
|
-
|
|
5569
|
-
function getPrompt({ template, variables, fallback }) {
|
|
5570
|
-
if (!template && !fallback)
|
|
5571
|
-
throw new Error('Must provide either a template or a fallback');
|
|
5572
|
-
return (template
|
|
5573
|
-
? new prompts.PromptTemplate({
|
|
5574
|
-
template,
|
|
5575
|
-
inputVariables: variables,
|
|
5576
|
-
})
|
|
5577
|
-
: fallback);
|
|
5589
|
+
async function createCommit(message, git) {
|
|
5590
|
+
return await git.commit(message);
|
|
5578
5591
|
}
|
|
5579
5592
|
|
|
5580
5593
|
/**
|
|
@@ -5706,34 +5719,39 @@ async function getChanges({ git, options }) {
|
|
|
5706
5719
|
};
|
|
5707
5720
|
}
|
|
5708
5721
|
|
|
5709
|
-
|
|
5710
|
-
|
|
5711
|
-
|
|
5712
|
-
|
|
5713
|
-
|
|
5714
|
-
|
|
5715
|
-
|
|
5716
|
-
|
|
5717
|
-
}
|
|
5718
|
-
else if (hasUnstaged || hasUntracked) {
|
|
5719
|
-
logger.log('Forget something? No staged changes found... 👻', { color: 'red' });
|
|
5720
|
-
if (hasUnstaged) {
|
|
5721
|
-
logger.log('\nChanges not staged for commit:', { color: 'yellow' });
|
|
5722
|
-
logger.verbose(`\t${unstaged.map(({ summary }) => summary).join('\n\t')}`, {
|
|
5723
|
-
color: 'red',
|
|
5724
|
-
});
|
|
5725
|
-
}
|
|
5726
|
-
if (hasUntracked) {
|
|
5727
|
-
logger.log('\nUntracked changes:', { color: 'yellow' });
|
|
5728
|
-
logger.verbose(`\t${untracked.map(({ summary }) => summary).join('\n\t')}`, {
|
|
5729
|
-
color: 'red',
|
|
5730
|
-
});
|
|
5731
|
-
}
|
|
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();
|
|
5732
5730
|
}
|
|
5733
|
-
|
|
5734
|
-
|
|
5731
|
+
catch (e) {
|
|
5732
|
+
console.log('Error initializing git repo', e);
|
|
5733
|
+
process.exit(1);
|
|
5735
5734
|
}
|
|
5736
|
-
|
|
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
|
+
});
|
|
5737
5755
|
|
|
5738
5756
|
/**
|
|
5739
5757
|
* Verify template string contains all required input variables
|
|
@@ -5901,26 +5919,6 @@ async function generateAndReviewLoop({ label, factory, parser, noResult, agent,
|
|
|
5901
5919
|
return result;
|
|
5902
5920
|
}
|
|
5903
5921
|
|
|
5904
|
-
const executeChain = async ({ llm, prompt, variables, parser, }) => {
|
|
5905
|
-
if (!llm || !prompt || !variables) {
|
|
5906
|
-
throw new Error('The input parameters "llm", "prompt", and "variables" are all required.');
|
|
5907
|
-
}
|
|
5908
|
-
const chain = prompt.pipe(llm).pipe(parser);
|
|
5909
|
-
let res;
|
|
5910
|
-
try {
|
|
5911
|
-
res = await chain.invoke(variables);
|
|
5912
|
-
}
|
|
5913
|
-
catch (error) {
|
|
5914
|
-
if (error instanceof Error) {
|
|
5915
|
-
throw new Error(`LLMChain call error: ${error.message}`);
|
|
5916
|
-
}
|
|
5917
|
-
}
|
|
5918
|
-
if (!res) {
|
|
5919
|
-
throw new Error('Empty response from LLMChain call');
|
|
5920
|
-
}
|
|
5921
|
-
return res;
|
|
5922
|
-
};
|
|
5923
|
-
|
|
5924
5922
|
const logSuccess = () => {
|
|
5925
5923
|
console.log(chalk.green(chalk.bold('\nAll set! 🦾🤖')));
|
|
5926
5924
|
};
|
|
@@ -5944,22 +5942,6 @@ async function handleResult({ result, mode, interactiveHandler }) {
|
|
|
5944
5942
|
process.exit(0);
|
|
5945
5943
|
}
|
|
5946
5944
|
|
|
5947
|
-
/**
|
|
5948
|
-
* Retrieves the SimpleGit instance for the repository.
|
|
5949
|
-
* @returns {SimpleGit} The SimpleGit instance.
|
|
5950
|
-
*/
|
|
5951
|
-
const getRepo = () => {
|
|
5952
|
-
let git;
|
|
5953
|
-
try {
|
|
5954
|
-
git = simpleGit.simpleGit();
|
|
5955
|
-
}
|
|
5956
|
-
catch (e) {
|
|
5957
|
-
console.log('Error initializing git repo', e);
|
|
5958
|
-
process.exit(1);
|
|
5959
|
-
}
|
|
5960
|
-
return git;
|
|
5961
|
-
};
|
|
5962
|
-
|
|
5963
5945
|
/**
|
|
5964
5946
|
* Retrieves a TikToken for the specified model.
|
|
5965
5947
|
*
|
|
@@ -5982,15 +5964,33 @@ const getTokenCounter = async (modelName) => {
|
|
|
5982
5964
|
});
|
|
5983
5965
|
};
|
|
5984
5966
|
|
|
5985
|
-
|
|
5986
|
-
|
|
5987
|
-
|
|
5988
|
-
|
|
5989
|
-
|
|
5990
|
-
|
|
5991
|
-
|
|
5992
|
-
|
|
5993
|
-
|
|
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
|
+
}
|
|
5994
5994
|
}
|
|
5995
5995
|
|
|
5996
5996
|
const handler$2 = async (argv, logger) => {
|
|
@@ -6138,7 +6138,7 @@ async function getCommitLogRange(from, to, { noMerges, git }) {
|
|
|
6138
6138
|
try {
|
|
6139
6139
|
const logOptions = { from: `${from}^1`, to, '--no-merges': noMerges };
|
|
6140
6140
|
const commitLog = await git.log(logOptions);
|
|
6141
|
-
return commitLog.all.map(({ message, date, body, author_name, hash, author_email }) => `[${date}] ${message}\n${body}\n - ${author_name}<${author_email}
|
|
6141
|
+
return commitLog.all.map(({ message, date, body, author_name, hash, author_email }) => `[${date}] ${message}\n${body}\n(${hash}) - ${author_name}<${author_email}>`);
|
|
6142
6142
|
}
|
|
6143
6143
|
catch (error) {
|
|
6144
6144
|
// If there's an error, handle it appropriately
|
|
@@ -6211,8 +6211,8 @@ async function getCommitLogCurrentBranch({ git, logger, comparisonBranch = 'main
|
|
|
6211
6211
|
|
|
6212
6212
|
const template = `Write informative git changelog, in the imperative, based on a series of individual messages.
|
|
6213
6213
|
|
|
6214
|
-
-
|
|
6215
|
-
-
|
|
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
6216
|
|
|
6217
6217
|
{format_instructions}
|
|
6218
6218
|
|
|
@@ -6257,8 +6257,8 @@ const handler$1 = async (argv, logger) => {
|
|
|
6257
6257
|
};
|
|
6258
6258
|
}
|
|
6259
6259
|
async function parser({ branch, commits }) {
|
|
6260
|
-
|
|
6261
|
-
|
|
6260
|
+
const result = `## ${branch}\n\n${commits.map((commit) => `${commit}`).join('\n\n\n')}`;
|
|
6261
|
+
console.log({ result });
|
|
6262
6262
|
return result;
|
|
6263
6263
|
}
|
|
6264
6264
|
const changelogMsg = await generateAndReviewLoop({
|
|
@@ -6272,7 +6272,7 @@ const handler$1 = async (argv, logger) => {
|
|
|
6272
6272
|
variables: CHANGELOG_PROMPT.inputVariables,
|
|
6273
6273
|
fallback: CHANGELOG_PROMPT,
|
|
6274
6274
|
});
|
|
6275
|
-
const formatInstructions = "Respond with a valid JSON object, containing two fields: 'header' and 'content'.";
|
|
6275
|
+
const formatInstructions = "Respond with a valid JSON object, containing two fields: 'header' and 'content', both strings.";
|
|
6276
6276
|
const changelog = await executeChain({
|
|
6277
6277
|
llm,
|
|
6278
6278
|
prompt,
|
|
@@ -6282,7 +6282,6 @@ const handler$1 = async (argv, logger) => {
|
|
|
6282
6282
|
},
|
|
6283
6283
|
parser,
|
|
6284
6284
|
});
|
|
6285
|
-
console.log({ changelog });
|
|
6286
6285
|
return `${changelog.header}\n\n${changelog.content}`;
|
|
6287
6286
|
},
|
|
6288
6287
|
noResult: async () => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "git-coco",
|
|
3
|
-
"version": "0.11.
|
|
3
|
+
"version": "0.11.1",
|
|
4
4
|
"description": "zero-effort git commits with coco.",
|
|
5
5
|
"author": "gfargo <ghfargo@gmail.com>",
|
|
6
6
|
"license": "MIT",
|
|
@@ -54,7 +54,7 @@
|
|
|
54
54
|
"@types/ini": "^1.3.31",
|
|
55
55
|
"@types/inquirer": "^9.0.3",
|
|
56
56
|
"@types/jest": "^29.5.10",
|
|
57
|
-
"@types/node": "^
|
|
57
|
+
"@types/node": "^22.7.5",
|
|
58
58
|
"@types/yargs": "^17.0.32",
|
|
59
59
|
"@typescript-eslint/eslint-plugin": "^7.13.1",
|
|
60
60
|
"@typescript-eslint/parser": "^7.13.1",
|
|
@@ -63,7 +63,7 @@
|
|
|
63
63
|
"jest": "^29.7.0",
|
|
64
64
|
"jest-mock": "^29.5.0",
|
|
65
65
|
"release-it": "^17.0.0",
|
|
66
|
-
"rimraf": "^
|
|
66
|
+
"rimraf": "^6.0.1",
|
|
67
67
|
"rollup": "^3.26.1",
|
|
68
68
|
"rollup-plugin-banner2": "^1.2.2",
|
|
69
69
|
"rollup-plugin-bin": "^1.0.0",
|
|
@@ -83,10 +83,10 @@
|
|
|
83
83
|
"dependencies": {
|
|
84
84
|
"@inquirer/prompts": "3.3.0",
|
|
85
85
|
"@jridgewell/sourcemap-codec": "^1.4.15",
|
|
86
|
-
"@langchain/community": "^0.3.
|
|
87
|
-
"@langchain/core": "^0.3.
|
|
86
|
+
"@langchain/community": "^0.3.4",
|
|
87
|
+
"@langchain/core": "^0.3.7",
|
|
88
88
|
"@langchain/ollama": "^0.1.0",
|
|
89
|
-
"@langchain/openai": "^0.3.
|
|
89
|
+
"@langchain/openai": "^0.3.5",
|
|
90
90
|
"ajv": "^8.16.0",
|
|
91
91
|
"ajv-formats": "^3.0.1",
|
|
92
92
|
"chalk": "4.1.2",
|
|
@@ -98,7 +98,7 @@
|
|
|
98
98
|
"performance-now": "2.1.0",
|
|
99
99
|
"pretty-ms": "7.0.1",
|
|
100
100
|
"simple-git": "3.25.0",
|
|
101
|
-
"tiktoken": "^1.0.
|
|
101
|
+
"tiktoken": "^1.0.17",
|
|
102
102
|
"yargs": "17.7.2"
|
|
103
103
|
}
|
|
104
104
|
}
|