git-coco 0.7.6 → 0.8.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 +119 -63
- package/dist/index.esm.mjs +218 -150
- package/dist/index.js +218 -150
- package/package.json +2 -1
package/dist/index.js
CHANGED
|
@@ -14,11 +14,11 @@ var now = require('performance-now');
|
|
|
14
14
|
var prettyMilliseconds = require('pretty-ms');
|
|
15
15
|
var pQueue = require('p-queue');
|
|
16
16
|
var document = require('langchain/document');
|
|
17
|
-
var hf = require('langchain/llms/hf');
|
|
18
17
|
var chains = require('langchain/chains');
|
|
19
|
-
var openai = require('langchain/llms/openai');
|
|
20
18
|
var text_splitter = require('langchain/text_splitter');
|
|
21
19
|
var diff = require('diff');
|
|
20
|
+
var ollama = require('langchain/llms/ollama');
|
|
21
|
+
var openai = require('langchain/llms/openai');
|
|
22
22
|
var minimatch = require('minimatch');
|
|
23
23
|
var simpleGit = require('simple-git');
|
|
24
24
|
var tiktoken = require('tiktoken');
|
|
@@ -68,33 +68,34 @@ const SUMMARIZE_PROMPT = new prompts.PromptTemplate({
|
|
|
68
68
|
inputVariables: inputVariables$2,
|
|
69
69
|
});
|
|
70
70
|
|
|
71
|
+
const DEFAULT_IGNORED_FILES = ['package-lock.json'];
|
|
72
|
+
const DEFAULT_IGNORED_EXTENSIONS = ['.map', '.lock'];
|
|
71
73
|
/**
|
|
72
74
|
* Default Config
|
|
73
75
|
*
|
|
74
76
|
* @type {Config}
|
|
75
77
|
*/
|
|
76
78
|
const DEFAULT_CONFIG = {
|
|
77
|
-
service: 'openai
|
|
79
|
+
service: 'openai',
|
|
78
80
|
verbose: false,
|
|
79
81
|
tokenLimit: 1024,
|
|
80
82
|
summarizePrompt: SUMMARIZE_PROMPT.template,
|
|
81
83
|
temperature: 0.4,
|
|
82
84
|
mode: 'stdout',
|
|
83
|
-
ignoredFiles:
|
|
84
|
-
ignoredExtensions:
|
|
85
|
+
ignoredFiles: DEFAULT_IGNORED_FILES,
|
|
86
|
+
ignoredExtensions: DEFAULT_IGNORED_EXTENSIONS,
|
|
85
87
|
defaultBranch: 'main',
|
|
86
88
|
};
|
|
87
89
|
/**
|
|
88
90
|
* Create a named export of all config keys for use in other modules.
|
|
89
91
|
*
|
|
90
|
-
* @see
|
|
92
|
+
* @see Used in `src/lib/config/services/env.ts` to validate all env vars.
|
|
91
93
|
*
|
|
92
94
|
* @type {string[]}
|
|
93
95
|
*/
|
|
94
96
|
const CONFIG_KEYS = Object.keys({
|
|
95
97
|
...DEFAULT_CONFIG,
|
|
96
|
-
|
|
97
|
-
openAIApiKey: '',
|
|
98
|
+
endpoint: '',
|
|
98
99
|
prompt: '',
|
|
99
100
|
});
|
|
100
101
|
const COCO_CONFIG_START_COMMENT = '# -- start coco config --';
|
|
@@ -173,8 +174,9 @@ function loadEnvConfig(config) {
|
|
|
173
174
|
CONFIG_KEYS.forEach((key) => {
|
|
174
175
|
const envVarName = toEnvVarName(key);
|
|
175
176
|
const envValue = parseEnvValue(key, process.env[envVarName]);
|
|
176
|
-
if (envValue === undefined)
|
|
177
|
+
if (envValue === undefined) {
|
|
177
178
|
return;
|
|
179
|
+
}
|
|
178
180
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
179
181
|
// @ts-ignore
|
|
180
182
|
envConfig[key] = envValue;
|
|
@@ -182,27 +184,28 @@ function loadEnvConfig(config) {
|
|
|
182
184
|
return { ...config, ...removeUndefined(envConfig) };
|
|
183
185
|
}
|
|
184
186
|
function parseEnvValue(key, value) {
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
187
|
+
switch (true) {
|
|
188
|
+
// Handle undefined values
|
|
189
|
+
case value === undefined:
|
|
190
|
+
return undefined;
|
|
191
|
+
// Handle comma separated strings for ignoredFiles and ignoredExtensions arrays
|
|
192
|
+
case (key === 'ignoredFiles' || key === 'ignoredExtensions') &&
|
|
193
|
+
typeof value === 'string' &&
|
|
194
|
+
value.includes(','):
|
|
195
|
+
return value.split(',');
|
|
196
|
+
// Handle boolean values
|
|
197
|
+
case typeof value === 'string' && (value === 'false' || value === 'true'):
|
|
198
|
+
return value === 'true';
|
|
199
|
+
default:
|
|
200
|
+
return value;
|
|
195
201
|
}
|
|
196
|
-
return value;
|
|
197
202
|
}
|
|
198
203
|
function toEnvVarName(key) {
|
|
199
204
|
switch (key) {
|
|
200
205
|
case 'openAIApiKey':
|
|
201
206
|
return 'OPENAI_API_KEY';
|
|
202
|
-
case 'huggingFaceHubApiKey':
|
|
203
|
-
return 'HUGGINGFACE_HUB_API_KEY';
|
|
204
207
|
default:
|
|
205
|
-
return
|
|
208
|
+
return `COCO_${key.replace(/([A-Z])/g, '_$1').toLocaleUpperCase()}`;
|
|
206
209
|
}
|
|
207
210
|
}
|
|
208
211
|
function formatEnvValue(value) {
|
|
@@ -246,13 +249,19 @@ function loadGitConfig(config) {
|
|
|
246
249
|
const gitConfigParsed = ini__namespace.parse(gitConfigRaw);
|
|
247
250
|
config = {
|
|
248
251
|
...config,
|
|
249
|
-
service: gitConfigParsed.coco?.
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
252
|
+
service: gitConfigParsed.coco?.service || config.service,
|
|
253
|
+
...(config.service === 'ollama'
|
|
254
|
+
? {
|
|
255
|
+
endpoint: gitConfigParsed.coco?.endpoint || config?.endpoint,
|
|
256
|
+
}
|
|
257
|
+
: {
|
|
258
|
+
openAIApiKey: gitConfigParsed.coco?.openAIApiKey || config?.openAIApiKey,
|
|
259
|
+
}),
|
|
260
|
+
model: gitConfigParsed.coco?.model || config?.model,
|
|
261
|
+
temperature: gitConfigParsed.coco?.temperature || config?.temperature,
|
|
262
|
+
tokenLimit: gitConfigParsed.coco?.tokenLimit || config?.tokenLimit,
|
|
253
263
|
prompt: gitConfigParsed.coco?.prompt || config.prompt,
|
|
254
264
|
mode: gitConfigParsed.coco?.mode || config.mode,
|
|
255
|
-
temperature: gitConfigParsed.coco?.temperature || config.temperature,
|
|
256
265
|
summarizePrompt: gitConfigParsed.coco?.summarizePrompt || config.summarizePrompt,
|
|
257
266
|
ignoredFiles: gitConfigParsed.coco?.ignoredFiles || config.ignoredFiles,
|
|
258
267
|
ignoredExtensions: gitConfigParsed.coco?.ignoredExtensions || config.ignoredExtensions,
|
|
@@ -682,64 +691,16 @@ async function collectDiffs(node, getFileDiff, tokenizer, logger) {
|
|
|
682
691
|
};
|
|
683
692
|
}
|
|
684
693
|
|
|
685
|
-
function getModelAndProviderFromService(service) {
|
|
686
|
-
if (!service) {
|
|
687
|
-
throw new Error(`Missing service`);
|
|
688
|
-
}
|
|
689
|
-
const [provider, model] = service.split(/\/(.*)/s);
|
|
690
|
-
if (!model || !provider) {
|
|
691
|
-
throw new Error(`Invalid service: ${service}`);
|
|
692
|
-
}
|
|
693
|
-
return { provider, model };
|
|
694
|
-
}
|
|
695
|
-
function getModelFromService(service) {
|
|
696
|
-
const { model } = getModelAndProviderFromService(service);
|
|
697
|
-
return model;
|
|
698
|
-
}
|
|
699
694
|
/**
|
|
700
|
-
* Get
|
|
701
|
-
* @param
|
|
702
|
-
* @param configuration
|
|
703
|
-
* @returns LLM Model
|
|
704
|
-
*/
|
|
705
|
-
function getLlm(service, key, fields) {
|
|
706
|
-
const { provider, model } = getModelAndProviderFromService(service);
|
|
707
|
-
if (!model) {
|
|
708
|
-
throw new Error(`Invalid LLM Service: ${service}`);
|
|
709
|
-
}
|
|
710
|
-
switch (provider) {
|
|
711
|
-
case 'huggingface':
|
|
712
|
-
return new hf.HuggingFaceInference({
|
|
713
|
-
model: model,
|
|
714
|
-
apiKey: key,
|
|
715
|
-
maxConcurrency: 4,
|
|
716
|
-
...fields,
|
|
717
|
-
});
|
|
718
|
-
case 'openai':
|
|
719
|
-
default:
|
|
720
|
-
return new openai.OpenAI({
|
|
721
|
-
openAIApiKey: key,
|
|
722
|
-
modelName: model,
|
|
723
|
-
...fields,
|
|
724
|
-
});
|
|
725
|
-
}
|
|
726
|
-
}
|
|
727
|
-
/**
|
|
728
|
-
* Retrieve appropriate API key based on selected model
|
|
729
|
-
* @param service
|
|
695
|
+
* Get Summarization Chain
|
|
696
|
+
* @param model
|
|
730
697
|
* @param options
|
|
731
698
|
* @returns
|
|
732
699
|
*/
|
|
733
|
-
function
|
|
734
|
-
|
|
735
|
-
switch (provider) {
|
|
736
|
-
case 'huggingface':
|
|
737
|
-
return options.huggingFaceHubApiKey;
|
|
738
|
-
case 'openai':
|
|
739
|
-
default:
|
|
740
|
-
return options.openAIApiKey;
|
|
741
|
-
}
|
|
700
|
+
function getSummarizationChain(model, options = { type: 'map_reduce' }) {
|
|
701
|
+
return chains.loadSummarizationChain(model, options);
|
|
742
702
|
}
|
|
703
|
+
|
|
743
704
|
/**
|
|
744
705
|
* Get Recursive Character Text Splitter
|
|
745
706
|
* @param options
|
|
@@ -748,41 +709,6 @@ function getApiKeyForModel(service, options) {
|
|
|
748
709
|
function getTextSplitter(options = {}) {
|
|
749
710
|
return new text_splitter.RecursiveCharacterTextSplitter(options);
|
|
750
711
|
}
|
|
751
|
-
/**
|
|
752
|
-
* Get Summarization Chain
|
|
753
|
-
* @param model
|
|
754
|
-
* @param options
|
|
755
|
-
* @returns
|
|
756
|
-
*/
|
|
757
|
-
function getSummarizationChain(model, options = { type: 'map_reduce' }) {
|
|
758
|
-
return chains.loadSummarizationChain(model, options);
|
|
759
|
-
}
|
|
760
|
-
function getPrompt({ template, variables, fallback }) {
|
|
761
|
-
if (!template && !fallback)
|
|
762
|
-
throw new Error('Must provide either a template or a fallback');
|
|
763
|
-
return (template
|
|
764
|
-
? new prompts.PromptTemplate({
|
|
765
|
-
template,
|
|
766
|
-
inputVariables: variables,
|
|
767
|
-
})
|
|
768
|
-
: fallback);
|
|
769
|
-
}
|
|
770
|
-
/**
|
|
771
|
-
* Verify template string contains all required input variables
|
|
772
|
-
* @param text template string
|
|
773
|
-
* @param inputVariables template variables
|
|
774
|
-
* @returns boolean or error message
|
|
775
|
-
*/
|
|
776
|
-
function validatePromptTemplate(text, inputVariables) {
|
|
777
|
-
if (!text) {
|
|
778
|
-
return 'Prompt template cannot be empty';
|
|
779
|
-
}
|
|
780
|
-
if (!inputVariables.some((entry) => text.includes(entry))) {
|
|
781
|
-
return ('Prompt template must include at least one of the following input variables: ' +
|
|
782
|
-
inputVariables.map((value) => `{${value}}`).join(', '));
|
|
783
|
-
}
|
|
784
|
-
return true;
|
|
785
|
-
}
|
|
786
712
|
|
|
787
713
|
async function parseDefaultFileDiff(nodeFile, commit = '--staged', git) {
|
|
788
714
|
if (commit !== '--staged') {
|
|
@@ -870,14 +796,14 @@ async function fileChangeParser({ changes, commit, options: { tokenizer, git, ll
|
|
|
870
796
|
}
|
|
871
797
|
|
|
872
798
|
const template$1 = `Write informative git commit message, in the imperative, based on the diffs & file changes provided in the "Diff Summary" section.
|
|
873
|
-
Commit Messages must have a short description that is less than 50 characters
|
|
799
|
+
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:
|
|
874
800
|
|
|
875
|
-
- Typically a hyphen or asterisk is used for the bullet
|
|
876
801
|
- Write concisely using an informal tone
|
|
877
802
|
- DO NOT use phrases like "this commit", "this change", "this function", etc. Instead refer to the function, variable, or class by name
|
|
878
|
-
- DO NOT use phrases like "this commit", "this change", "this function", etc. Instead refer to the function, variable, or class by name
|
|
879
803
|
- DO NOT use specific names or files from the code
|
|
804
|
+
- DO NOT include any diffs or file changes in the commit message
|
|
880
805
|
- Wrap variable, class, function, components, and dependency names in back ticks e.g. \`variable\`
|
|
806
|
+
- ONLY respond with the resulting commit message.
|
|
881
807
|
|
|
882
808
|
"""{summary}"""
|
|
883
809
|
|
|
@@ -888,6 +814,143 @@ const COMMIT_PROMPT = new prompts.PromptTemplate({
|
|
|
888
814
|
inputVariables: inputVariables$1,
|
|
889
815
|
});
|
|
890
816
|
|
|
817
|
+
const DEFAULT_OLLAMA_LLM_SERVICE = {
|
|
818
|
+
provider: 'ollama',
|
|
819
|
+
model: 'codellama',
|
|
820
|
+
endpoint: 'http://localhost:11434',
|
|
821
|
+
maxConcurrent: 1,
|
|
822
|
+
tokenLimit: 1024,
|
|
823
|
+
};
|
|
824
|
+
const DEFAULT_OPENAI_LLM_SERVICE = {
|
|
825
|
+
provider: 'openai',
|
|
826
|
+
model: 'gpt-4',
|
|
827
|
+
authentication: {
|
|
828
|
+
type: 'APIKey',
|
|
829
|
+
credentials: {
|
|
830
|
+
apiKey: '',
|
|
831
|
+
},
|
|
832
|
+
},
|
|
833
|
+
tokenLimit: 1024,
|
|
834
|
+
};
|
|
835
|
+
|
|
836
|
+
/**
|
|
837
|
+
* Retrieves the provider and model from the given configuration object.
|
|
838
|
+
* @param config The configuration object.
|
|
839
|
+
* @returns An object containing the provider and model.
|
|
840
|
+
* @throws Error if the configuration is invalid or missing required properties.
|
|
841
|
+
*/
|
|
842
|
+
function getModelAndProviderFromConfig(config) {
|
|
843
|
+
if (!config.service) {
|
|
844
|
+
throw new Error('Invalid service: undefined');
|
|
845
|
+
}
|
|
846
|
+
let result;
|
|
847
|
+
switch (typeof config.service) {
|
|
848
|
+
case 'string':
|
|
849
|
+
result = getDefaultServiceConfigFromAlias(config.service, config?.model);
|
|
850
|
+
break;
|
|
851
|
+
case 'object':
|
|
852
|
+
default:
|
|
853
|
+
result = config.service;
|
|
854
|
+
break;
|
|
855
|
+
}
|
|
856
|
+
const { provider, model } = result;
|
|
857
|
+
if (!model || !provider) {
|
|
858
|
+
throw new Error(`Invalid service: ${config.service}`);
|
|
859
|
+
}
|
|
860
|
+
return { provider, model };
|
|
861
|
+
}
|
|
862
|
+
/**
|
|
863
|
+
* Retrieve appropriate API key based on selected model
|
|
864
|
+
* @param service
|
|
865
|
+
* @param options
|
|
866
|
+
* @returns API Key
|
|
867
|
+
*/
|
|
868
|
+
function getApiKeyForModel(config) {
|
|
869
|
+
const { provider } = getModelAndProviderFromConfig(config);
|
|
870
|
+
switch (provider) {
|
|
871
|
+
case 'openai':
|
|
872
|
+
return config.openAIApiKey || getDefaultServiceApiKey(config);
|
|
873
|
+
default:
|
|
874
|
+
return getDefaultServiceApiKey(config);
|
|
875
|
+
}
|
|
876
|
+
}
|
|
877
|
+
/**
|
|
878
|
+
* Retrieves the default service API key from the given configuration.
|
|
879
|
+
* @param config The configuration object.
|
|
880
|
+
* @returns The default service API key.
|
|
881
|
+
*/
|
|
882
|
+
function getDefaultServiceApiKey(config) {
|
|
883
|
+
const service = config.service;
|
|
884
|
+
if (service.authentication.type === 'APIKey') {
|
|
885
|
+
return service.authentication.credentials?.apiKey;
|
|
886
|
+
}
|
|
887
|
+
else if (service.authentication.type === 'OAuth') {
|
|
888
|
+
return service.authentication.credentials?.token;
|
|
889
|
+
}
|
|
890
|
+
return '';
|
|
891
|
+
}
|
|
892
|
+
/**
|
|
893
|
+
* Retrieves the default service configuration based on the provided alias and optional model.
|
|
894
|
+
* @param alias - The alias of the service.
|
|
895
|
+
* @param model - The optional model to be used.
|
|
896
|
+
* @returns The default service configuration.
|
|
897
|
+
* @throws Error if the alias is invalid or undefined.
|
|
898
|
+
*/
|
|
899
|
+
function getDefaultServiceConfigFromAlias(alias, model) {
|
|
900
|
+
if (!alias) {
|
|
901
|
+
throw new Error('Invalid alias: undefined');
|
|
902
|
+
}
|
|
903
|
+
switch (alias) {
|
|
904
|
+
case 'openai':
|
|
905
|
+
return {
|
|
906
|
+
...DEFAULT_OPENAI_LLM_SERVICE,
|
|
907
|
+
model: model || DEFAULT_OPENAI_LLM_SERVICE.model,
|
|
908
|
+
};
|
|
909
|
+
case 'ollama':
|
|
910
|
+
return {
|
|
911
|
+
...DEFAULT_OLLAMA_LLM_SERVICE,
|
|
912
|
+
model: model || DEFAULT_OLLAMA_LLM_SERVICE.model,
|
|
913
|
+
};
|
|
914
|
+
}
|
|
915
|
+
}
|
|
916
|
+
|
|
917
|
+
/**
|
|
918
|
+
* Get LLM Model Based on Configuration
|
|
919
|
+
*
|
|
920
|
+
* @param fields
|
|
921
|
+
* @param configuration
|
|
922
|
+
* @returns LLM Model
|
|
923
|
+
*/
|
|
924
|
+
function getLlm(provider, model, config) {
|
|
925
|
+
if (!model) {
|
|
926
|
+
throw new Error(`Invalid LLM Service: ${provider}/${model}`);
|
|
927
|
+
}
|
|
928
|
+
switch (provider) {
|
|
929
|
+
case 'ollama':
|
|
930
|
+
return new ollama.Ollama({
|
|
931
|
+
baseUrl: DEFAULT_OLLAMA_LLM_SERVICE.endpoint,
|
|
932
|
+
model,
|
|
933
|
+
});
|
|
934
|
+
case 'openai':
|
|
935
|
+
default:
|
|
936
|
+
return new openai.OpenAI({
|
|
937
|
+
openAIApiKey: config.openAIApiKey,
|
|
938
|
+
modelName: model,
|
|
939
|
+
});
|
|
940
|
+
}
|
|
941
|
+
}
|
|
942
|
+
|
|
943
|
+
function getPrompt({ template, variables, fallback }) {
|
|
944
|
+
if (!template && !fallback)
|
|
945
|
+
throw new Error('Must provide either a template or a fallback');
|
|
946
|
+
return (template
|
|
947
|
+
? new prompts.PromptTemplate({
|
|
948
|
+
template,
|
|
949
|
+
inputVariables: variables,
|
|
950
|
+
})
|
|
951
|
+
: fallback);
|
|
952
|
+
}
|
|
953
|
+
|
|
891
954
|
function getStatus(file, location = 'index') {
|
|
892
955
|
if ('index' in file && 'working_dir' in file) {
|
|
893
956
|
const statusCode = file[location];
|
|
@@ -942,9 +1005,6 @@ function getSummaryText(file, change) {
|
|
|
942
1005
|
return `${status}: ${filePath}`;
|
|
943
1006
|
}
|
|
944
1007
|
|
|
945
|
-
const config = loadConfig();
|
|
946
|
-
const DEFAULT_IGNORED_FILES = config?.ignoredFiles?.length ? config.ignoredFiles : [];
|
|
947
|
-
const DEFAULT_IGNORED_EXTENSIONS = config?.ignoredExtensions?.length ? config.ignoredExtensions : [];
|
|
948
1008
|
async function getChanges({ git, options }) {
|
|
949
1009
|
const { ignoredFiles = DEFAULT_IGNORED_FILES, ignoredExtensions = DEFAULT_IGNORED_EXTENSIONS } = options || {};
|
|
950
1010
|
const staged = [];
|
|
@@ -1010,13 +1070,13 @@ async function noResult({ git, logger }) {
|
|
|
1010
1070
|
else if (hasUnstaged || hasUntracked) {
|
|
1011
1071
|
logger.log('Forget something? No staged changes found... 👻', { color: 'red' });
|
|
1012
1072
|
if (hasUnstaged) {
|
|
1013
|
-
logger.log('\
|
|
1073
|
+
logger.log('\nChanges not staged for commit:', { color: 'yellow' });
|
|
1014
1074
|
logger.verbose(`\t${unstaged.map(({ summary }) => summary).join('\n\t')}`, {
|
|
1015
1075
|
color: 'red',
|
|
1016
1076
|
});
|
|
1017
1077
|
}
|
|
1018
1078
|
if (hasUntracked) {
|
|
1019
|
-
logger.log('\
|
|
1079
|
+
logger.log('\nUntracked changes:', { color: 'yellow' });
|
|
1020
1080
|
logger.verbose(`\t${untracked.map(({ summary }) => summary).join('\n\t')}`, {
|
|
1021
1081
|
color: 'red',
|
|
1022
1082
|
});
|
|
@@ -1090,6 +1150,23 @@ async function getUserReviewDecision({ label, descriptions, enableRetry = true,
|
|
|
1090
1150
|
}));
|
|
1091
1151
|
}
|
|
1092
1152
|
|
|
1153
|
+
/**
|
|
1154
|
+
* Verify template string contains all required input variables
|
|
1155
|
+
* @param text template string
|
|
1156
|
+
* @param inputVariables template variables
|
|
1157
|
+
* @returns boolean or error message
|
|
1158
|
+
*/
|
|
1159
|
+
function validatePromptTemplate(text, inputVariables) {
|
|
1160
|
+
if (!text) {
|
|
1161
|
+
return 'Prompt template cannot be empty';
|
|
1162
|
+
}
|
|
1163
|
+
if (!inputVariables.some((entry) => text.includes(entry))) {
|
|
1164
|
+
return ('Prompt template must include at least one of the following input variables: ' +
|
|
1165
|
+
inputVariables.map((value) => `{${value}}`).join(', '));
|
|
1166
|
+
}
|
|
1167
|
+
return true;
|
|
1168
|
+
}
|
|
1169
|
+
|
|
1093
1170
|
async function editPrompt(options) {
|
|
1094
1171
|
return await prompts$1.editor({
|
|
1095
1172
|
message: 'Edit the prompt',
|
|
@@ -1235,12 +1312,14 @@ const getRepo = () => {
|
|
|
1235
1312
|
const getTikToken = async (modelName) => {
|
|
1236
1313
|
return await tiktoken.encoding_for_model(modelName);
|
|
1237
1314
|
};
|
|
1238
|
-
const getTokenCounter = async (modelName) =>
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1315
|
+
const getTokenCounter = async (modelName) => {
|
|
1316
|
+
return getTikToken(modelName).then((tokenizer) => (text) => {
|
|
1317
|
+
// console.log('Running GetTokenCount', { tokenizer, length: text.length })
|
|
1318
|
+
const tokens = tokenizer.encode(text);
|
|
1319
|
+
// console.log('Tokens', { tokenCount: tokens.length })
|
|
1320
|
+
return tokens.length;
|
|
1321
|
+
});
|
|
1322
|
+
};
|
|
1244
1323
|
|
|
1245
1324
|
async function createCommit(commitMsg, git) {
|
|
1246
1325
|
return await git.commit(commitMsg);
|
|
@@ -1249,17 +1328,14 @@ async function createCommit(commitMsg, git) {
|
|
|
1249
1328
|
const handler$2 = async (argv, logger) => {
|
|
1250
1329
|
const git = getRepo();
|
|
1251
1330
|
const options = loadConfig(argv);
|
|
1252
|
-
const
|
|
1253
|
-
const key = getApiKeyForModel(service, options);
|
|
1254
|
-
const tokenizer = await getTokenCounter(getModelFromService(service));
|
|
1331
|
+
const key = getApiKeyForModel(options);
|
|
1255
1332
|
if (!key) {
|
|
1256
1333
|
logger.log(`No API Key found. 🗝️🚪`, { color: 'red' });
|
|
1257
1334
|
process.exit(1);
|
|
1258
1335
|
}
|
|
1259
|
-
const
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
});
|
|
1336
|
+
const { provider, model } = getModelAndProviderFromConfig(options);
|
|
1337
|
+
const tokenizer = await getTokenCounter(provider === 'openai' ? model : 'gpt-4');
|
|
1338
|
+
const llm = getLlm(provider, model, options);
|
|
1263
1339
|
const INTERACTIVE = isInteractive(options);
|
|
1264
1340
|
if (INTERACTIVE) {
|
|
1265
1341
|
logger.log(LOGO);
|
|
@@ -1325,16 +1401,10 @@ const handler$2 = async (argv, logger) => {
|
|
|
1325
1401
|
* Command line options via yargs
|
|
1326
1402
|
*/
|
|
1327
1403
|
const options$2 = {
|
|
1328
|
-
|
|
1404
|
+
service: { type: 'string', description: 'LLM/Model-Name', choices: ['openai', 'ollama'] },
|
|
1329
1405
|
openAIApiKey: {
|
|
1330
1406
|
type: 'string',
|
|
1331
1407
|
description: 'OpenAI API Key',
|
|
1332
|
-
conflicts: 'huggingFaceHubApiKey',
|
|
1333
|
-
},
|
|
1334
|
-
huggingFaceHubApiKey: {
|
|
1335
|
-
type: 'string',
|
|
1336
|
-
description: 'HuggingFace Hub API Key',
|
|
1337
|
-
conflicts: 'openAIApiKey',
|
|
1338
1408
|
},
|
|
1339
1409
|
tokenLimit: { type: 'number', description: 'Token limit' },
|
|
1340
1410
|
prompt: {
|
|
@@ -1458,15 +1528,13 @@ async function getCommitLogCurrentBranch({ git, logger, comparisonBranch = 'main
|
|
|
1458
1528
|
const handler$1 = async (argv, logger) => {
|
|
1459
1529
|
const options = loadConfig(argv);
|
|
1460
1530
|
const git = getRepo();
|
|
1461
|
-
const key = getApiKeyForModel(options
|
|
1531
|
+
const key = getApiKeyForModel(options);
|
|
1462
1532
|
if (!key) {
|
|
1463
1533
|
logger.log(`No API Key found. 🗝️🚪`, { color: 'red' });
|
|
1464
1534
|
process.exit(1);
|
|
1465
1535
|
}
|
|
1466
|
-
const model =
|
|
1467
|
-
|
|
1468
|
-
maxConcurrency: 10,
|
|
1469
|
-
});
|
|
1536
|
+
const { provider, model } = getModelAndProviderFromConfig(options);
|
|
1537
|
+
const llm = getLlm(provider, model, options);
|
|
1470
1538
|
const INTERACTIVE = isInteractive(options);
|
|
1471
1539
|
if (INTERACTIVE) {
|
|
1472
1540
|
logger.log(LOGO);
|
|
@@ -1498,7 +1566,7 @@ const handler$1 = async (argv, logger) => {
|
|
|
1498
1566
|
fallback: CHANGELOG_PROMPT,
|
|
1499
1567
|
});
|
|
1500
1568
|
return await executeChain({
|
|
1501
|
-
llm
|
|
1569
|
+
llm,
|
|
1502
1570
|
prompt,
|
|
1503
1571
|
variables: { summary: context },
|
|
1504
1572
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "git-coco",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
4
4
|
"description": "zero-effort git commits with coco.",
|
|
5
5
|
"author": "gfargo <ghfargo@gmail.com>",
|
|
6
6
|
"license": "MIT",
|
|
@@ -33,6 +33,7 @@
|
|
|
33
33
|
"test": "npm run test:jest && npm run test:publish",
|
|
34
34
|
"test:publish": "npm run lint && npm publish --dry-run",
|
|
35
35
|
"test:jest": "jest",
|
|
36
|
+
"test:jest:watch": "jest --watch",
|
|
36
37
|
"release": "release-it",
|
|
37
38
|
"prepublishOnly": "npm run clean && npm run build",
|
|
38
39
|
"coco": "npm run coco:ts",
|