git-coco 0.2.1 → 0.3.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.js CHANGED
@@ -1,27 +1,27 @@
1
1
  #!/usr/bin/env node
2
2
  'use strict';
3
3
 
4
+ var yargs = require('yargs');
4
5
  var prompts$1 = require('@inquirer/prompts');
6
+ var simpleGit = require('simple-git');
5
7
  var fs = require('fs');
6
8
  var os = require('os');
7
9
  var path = require('path');
8
10
  var ini = require('ini');
9
- var yargs = require('yargs');
10
- var helpers = require('yargs/helpers');
11
11
  var prompts = require('langchain/prompts');
12
12
  var pQueue = require('p-queue');
13
- var chalk = require('chalk');
14
- var ora = require('ora');
15
- var now = require('performance-now');
16
- var prettyMilliseconds = require('pretty-ms');
17
13
  var document = require('langchain/document');
14
+ var hf = require('langchain/llms/hf');
18
15
  var chains = require('langchain/chains');
19
16
  var openai = require('langchain/llms/openai');
20
17
  var text_splitter = require('langchain/text_splitter');
21
18
  var diff = require('diff');
19
+ var chalk = require('chalk');
22
20
  var GPT3NodeTokenizer = require('gpt3-tokenizer');
21
+ var ora = require('ora');
22
+ var now = require('performance-now');
23
+ var prettyMilliseconds = require('pretty-ms');
23
24
  var minimatch = require('minimatch');
24
- var simpleGit = require('simple-git');
25
25
 
26
26
  function _interopNamespaceDefault(e) {
27
27
  var n = Object.create(null);
@@ -63,7 +63,9 @@ function removeUndefined(obj) {
63
63
  **/
64
64
  function loadEnvConfig(config) {
65
65
  const envConfig = {
66
+ model: process.env.COCO_MODEL || undefined,
66
67
  openAIApiKey: process.env.OPENAI_API_KEY || undefined,
68
+ huggingFaceHubApiKey: process.env.HUGGINGFACE_HUB_API_KEY || undefined,
67
69
  tokenLimit: process.env.COCO_TOKEN_LIMIT
68
70
  ? parseInt(process.env.COCO_TOKEN_LIMIT)
69
71
  : undefined,
@@ -94,7 +96,9 @@ function loadGitConfig(config) {
94
96
  const gitConfigParsed = ini__namespace.parse(gitConfigRaw);
95
97
  config = {
96
98
  ...config,
99
+ model: gitConfigParsed.coco?.model || config.model,
97
100
  openAIApiKey: gitConfigParsed.coco?.openAIApiKey || config.openAIApiKey,
101
+ huggingFaceHubApiKey: gitConfigParsed.coco?.huggingFaceHubApiKey || config.huggingFaceHubApiKey,
98
102
  tokenLimit: parseInt(gitConfigParsed.coco?.tokenLimit) || config.tokenLimit,
99
103
  prompt: gitConfigParsed.coco?.prompt || config.prompt,
100
104
  mode: gitConfigParsed.coco?.mode || config.mode,
@@ -170,72 +174,6 @@ function loadXDGConfig(config) {
170
174
  return config;
171
175
  }
172
176
 
173
- /**
174
- * Command line options via yargs
175
- */
176
- const options = {
177
- openAIApiKey: { type: 'string', description: 'OpenAI API Key' },
178
- tokenLimit: { type: 'number', description: 'Token limit' },
179
- prompt: {
180
- type: 'string',
181
- alias: 'p',
182
- description: 'Commit message prompt',
183
- },
184
- interactive: {
185
- type: 'boolean',
186
- alias: 'i',
187
- description: 'Toggle interactive mode',
188
- },
189
- commit: {
190
- type: 'boolean',
191
- alias: 's',
192
- description: 'Commit staged changes with generated commit message',
193
- default: false,
194
- },
195
- openInEditor: {
196
- type: 'boolean',
197
- alias: 'e',
198
- description: 'Open commit message in editor before proceeding',
199
- },
200
- verbose: {
201
- type: 'boolean',
202
- description: 'Enable verbose logging',
203
- },
204
- summarizePrompt: {
205
- type: 'string',
206
- description: 'Large file summary prompt',
207
- },
208
- ignoredFiles: {
209
- type: 'array',
210
- description: 'Ignored files',
211
- },
212
- ignoredExtensions: {
213
- type: 'array',
214
- description: 'Ignored extensions',
215
- },
216
- };
217
- /**
218
- * Load command line flags via yargs
219
- *
220
- * @returns {Partial<Config>} Updated config
221
- */
222
- const loadArgv = () => {
223
- return yargs(helpers.hideBin(process.argv)).options(options).parseSync();
224
- };
225
- /**
226
- * Load command line flags
227
- *
228
- * Note: Arugments are parsed using yargs.
229
- *
230
- * @param {Config} config
231
- * @returns {Config} Updated config
232
- **/
233
- function loadCmdLineFlags(config) {
234
- const argv = loadArgv();
235
- config = { ...config, ...argv };
236
- return config;
237
- }
238
-
239
177
  const template$1 = `Write informative git commit message based on the diffs & file changes provided in the "Diff Summary" section.
240
178
  Commit Messages must have a short description that is less than 50 characters followed by a newline character and then a more verbose detailed description.
241
179
  - Write concisely using an informal tone
@@ -271,7 +209,7 @@ const SUMMARIZE_PROMPT = new prompts.PromptTemplate({
271
209
  * @type {Config}
272
210
  */
273
211
  const DEFAULT_CONFIG = {
274
- openAIApiKey: '',
212
+ model: 'openai/gpt-3.5-turbo',
275
213
  verbose: false,
276
214
  tokenLimit: 1024,
277
215
  prompt: COMMIT_PROMPT.template,
@@ -281,7 +219,6 @@ const DEFAULT_CONFIG = {
281
219
  ignoredFiles: ['package-lock.json'],
282
220
  ignoredExtensions: ['.map', '.lock'],
283
221
  };
284
-
285
222
  /**
286
223
  * Load application config
287
224
  *
@@ -299,7 +236,7 @@ const DEFAULT_CONFIG = {
299
236
  *
300
237
  * @returns {Config} application config
301
238
  **/
302
- function loadConfig() {
239
+ function loadConfig(argv = {}) {
303
240
  // Default config
304
241
  let config = DEFAULT_CONFIG;
305
242
  config = loadGitignore(config);
@@ -308,60 +245,7 @@ function loadConfig() {
308
245
  config = loadGitConfig(config);
309
246
  config = loadProjectConfig(config);
310
247
  config = loadEnvConfig(config);
311
- config = loadCmdLineFlags(config);
312
- return config;
313
- }
314
- const config = loadConfig();
315
-
316
- class Logger {
317
- constructor(config) {
318
- this.config = config;
319
- this.spinner = null;
320
- }
321
- log(message, options = { color: 'blue' }) {
322
- let outputMessage = message;
323
- if (options.color) {
324
- outputMessage = chalk[options.color](outputMessage);
325
- }
326
- console.log(outputMessage);
327
- return this;
328
- }
329
- verbose(message, options = {}) {
330
- if (!this.config?.verbose) {
331
- return this;
332
- }
333
- this.log(message, options);
334
- return this;
335
- }
336
- startTimer() {
337
- this.timerStart = now();
338
- return this;
339
- }
340
- stopTimer(message, options = { color: 'yellow' }) {
341
- if (!this.config?.verbose || !this.timerStart) {
342
- return this;
343
- }
344
- const elapsedTime = prettyMilliseconds(now() - this.timerStart);
345
- let outputMessage = message
346
- ? `${message} (⏲ ${elapsedTime})`
347
- : `⏲ ${elapsedTime}`;
348
- if (options.color) {
349
- outputMessage = chalk[options.color](outputMessage);
350
- }
351
- console.log(outputMessage);
352
- return this;
353
- }
354
- startSpinner(message, options = { color: 'green' }) {
355
- const spinnerMessage = options.color ? chalk[options.color](message) : message;
356
- this.spinner = ora(spinnerMessage).start();
357
- return this;
358
- }
359
- stopSpinner(message = '', options = { mode: 'succeed', color: 'green' }) {
360
- const spinnerMessage = options?.color ? chalk[options.color](message) : message;
361
- this.spinner?.[options.mode || 'succeed'](spinnerMessage);
362
- this.spinner = null;
363
- return this;
364
- }
248
+ return { ...config, ...argv };
365
249
  }
366
250
 
367
251
  /**
@@ -448,8 +332,7 @@ const defaultOutputCallback = (group) => {
448
332
  }
449
333
  return output;
450
334
  };
451
- async function summarizeDiffs(rootDiffNode, { tokenizer, maxTokens = 2048, textSplitter, chain, handleOutput = defaultOutputCallback, }) {
452
- const logger = new Logger(config);
335
+ async function summarizeDiffs(rootDiffNode, { tokenizer, logger, maxTokens = 2048, textSplitter, chain, handleOutput = defaultOutputCallback, }) {
453
336
  const queue = new pQueue({ concurrency: 8 });
454
337
  logger.startTimer().startSpinner(`Organizing Diffs...`, { color: 'blue' });
455
338
  const directoryDiffs = createDirectoryDiffs(rootDiffNode);
@@ -535,7 +418,7 @@ const createDiffTree = (changes) => {
535
418
  /**
536
419
  * Asynchronously collect diffs for a given node and its children.
537
420
  */
538
- async function collectDiffs(node, getFileDiff, tokenizer, logger = new Logger(config)) {
421
+ async function collectDiffs(node, getFileDiff, tokenizer, logger) {
539
422
  // Collect diffs for the files of the current node
540
423
  const diffPromises = node.files.map(async (nodeFile) => {
541
424
  const diff = await getFileDiff(nodeFile);
@@ -553,7 +436,7 @@ async function collectDiffs(node, getFileDiff, tokenizer, logger = new Logger(co
553
436
  };
554
437
  });
555
438
  // Collect diffs for the children of the current node
556
- const childrenPromises = Array.from(node.children.values()).map(async (child) => collectDiffs(child, getFileDiff, tokenizer));
439
+ const childrenPromises = Array.from(node.children.values()).map(async (child) => collectDiffs(child, getFileDiff, tokenizer, logger));
557
440
  const [diffs, children] = await Promise.all([
558
441
  Promise.all(diffPromises),
559
442
  Promise.all(childrenPromises),
@@ -565,22 +448,67 @@ async function collectDiffs(node, getFileDiff, tokenizer, logger = new Logger(co
565
448
  };
566
449
  }
567
450
 
568
- // TODO: Extend this to support other models! 🎉
569
- function getModel(fields, configuration) {
570
- return new openai.OpenAI(fields, configuration);
571
- // return new HuggingFaceInference({
572
- // // model: 'gpt2',
573
- // // model: 'bigcode/starcoder',
574
- // model: 'bigscience/bloom',
575
- // apiKey: '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',
576
- // maxConcurrency: 4,
577
- // cache: true,
578
- // // maxTokens: 2046,
579
- // })
451
+ /**
452
+ * Get LLM Model Based on Configuration
453
+ * @param fields
454
+ * @param configuration
455
+ * @returns LLM Model
456
+ */
457
+ function getModel(name, key, fields, configuration) {
458
+ const [llm, model] = name.split(/\/(.*)/s);
459
+ if (!model) {
460
+ throw new Error(`Invalid model: ${name}`);
461
+ }
462
+ switch (llm) {
463
+ case 'huggingface':
464
+ return new hf.HuggingFaceInference({
465
+ model: model,
466
+ apiKey: key,
467
+ maxConcurrency: 4,
468
+ ...fields,
469
+ });
470
+ case 'openai':
471
+ default:
472
+ return new openai.OpenAI({
473
+ openAIApiKey: key,
474
+ modelName: model,
475
+ ...fields,
476
+ }, configuration);
477
+ }
580
478
  }
479
+ /**
480
+ * Retrieve appropriate API key based on selected model
481
+ * @param name
482
+ * @param options
483
+ * @returns
484
+ */
485
+ function getModelAPIKey(name, options) {
486
+ const [llm, model] = name.split(/\/(.*)/s);
487
+ if (!model) {
488
+ throw new Error(`Invalid model: ${name}`);
489
+ }
490
+ switch (llm) {
491
+ case 'huggingface':
492
+ return options.huggingFaceHubApiKey;
493
+ case 'openai':
494
+ default:
495
+ return options.openAIApiKey;
496
+ }
497
+ }
498
+ /**
499
+ * Get Recursive Character Text Splitter
500
+ * @param options
501
+ * @returns
502
+ */
581
503
  function getTextSplitter(options = {}) {
582
504
  return new text_splitter.RecursiveCharacterTextSplitter(options);
583
505
  }
506
+ /**
507
+ * Get Summarization Chain
508
+ * @param model
509
+ * @param options
510
+ * @returns
511
+ */
584
512
  function getChain(model, options = { type: 'map_reduce' }) {
585
513
  return chains.loadSummarizationChain(model, options);
586
514
  }
@@ -594,6 +522,12 @@ function getPrompt({ template, variables, fallback }) {
594
522
  })
595
523
  : fallback);
596
524
  }
525
+ /**
526
+ * Verify template string contains all required input variables
527
+ * @param text template string
528
+ * @param inputVariables template variables
529
+ * @returns boolean or error message
530
+ */
597
531
  function validatePromptTemplate(text, inputVariables) {
598
532
  if (!text) {
599
533
  return 'Prompt template cannot be empty';
@@ -647,8 +581,7 @@ const getDiff = async (nodeFile, { git, logger, }) => {
647
581
  };
648
582
 
649
583
  const MAX_TOKENS_PER_SUMMARY = 2048;
650
- const fileChangeParser = async (changes, { tokenizer, git, model }) => {
651
- const logger = new Logger(config);
584
+ const fileChangeParser = async (changes, { tokenizer, git, model, logger }) => {
652
585
  const textSplitter = getTextSplitter({ chunkSize: 2000, chunkOverlap: 125, });
653
586
  const summarizationChain = getChain(model, {
654
587
  type: 'map_reduce',
@@ -669,6 +602,7 @@ const fileChangeParser = async (changes, { tokenizer, git, model }) => {
669
602
  maxTokens: MAX_TOKENS_PER_SUMMARY,
670
603
  textSplitter,
671
604
  chain: summarizationChain,
605
+ logger
672
606
  });
673
607
  logger.stopTimer(`\nSummary generated for ${changes.length} staged files`, { color: 'green' });
674
608
  return summary;
@@ -704,11 +638,77 @@ const getTokenizer = () => {
704
638
  return tokenizer;
705
639
  };
706
640
 
641
+ class Logger {
642
+ constructor(config) {
643
+ this.config = config;
644
+ this.spinner = null;
645
+ }
646
+ log(message, options = { color: 'blue' }) {
647
+ let outputMessage = message;
648
+ if (options.color) {
649
+ outputMessage = chalk[options.color](outputMessage);
650
+ }
651
+ console.log(outputMessage);
652
+ return this;
653
+ }
654
+ verbose(message, options = {}) {
655
+ if (!this.config?.verbose) {
656
+ return this;
657
+ }
658
+ this.log(message, options);
659
+ return this;
660
+ }
661
+ startTimer() {
662
+ this.timerStart = now();
663
+ return this;
664
+ }
665
+ stopTimer(message, options = { color: 'yellow' }) {
666
+ if (!this.config?.verbose || !this.timerStart) {
667
+ return this;
668
+ }
669
+ const elapsedTime = prettyMilliseconds(now() - this.timerStart);
670
+ let outputMessage = message
671
+ ? `${message} (⏲ ${elapsedTime})`
672
+ : `⏲ ${elapsedTime}`;
673
+ if (options.color) {
674
+ outputMessage = chalk[options.color](outputMessage);
675
+ }
676
+ console.log(outputMessage);
677
+ return this;
678
+ }
679
+ startSpinner(message, options = { color: 'green' }) {
680
+ const spinnerMessage = options.color ? chalk[options.color](message) : message;
681
+ this.spinner = ora(spinnerMessage).start();
682
+ return this;
683
+ }
684
+ stopSpinner(message = '', options = { mode: 'succeed', color: 'green' }) {
685
+ const spinnerMessage = options?.color ? chalk[options.color](message) : message;
686
+ this.spinner?.[options.mode || 'succeed'](spinnerMessage);
687
+ this.spinner = null;
688
+ return this;
689
+ }
690
+ }
691
+
707
692
  const llm = async ({ llm, prompt, variables }) => {
693
+ if (!llm || !prompt || !variables) {
694
+ throw new Error('The input parameters "llm", "prompt", and "variables" are all required.');
695
+ }
708
696
  const chain = new chains.LLMChain({ llm, prompt });
709
- const res = await chain.call(variables);
710
- if (res.error)
711
- throw new Error(res.error);
697
+ let res;
698
+ try {
699
+ res = await chain.call(variables);
700
+ }
701
+ catch (error) {
702
+ if (error instanceof Error) {
703
+ throw new Error(`LLMChain call error: ${error.message}`);
704
+ }
705
+ }
706
+ if (!res) {
707
+ throw new Error('Empty response from LLMChain call');
708
+ }
709
+ if (res.error) {
710
+ throw new Error(`LLMChain response error: ${res.error}`);
711
+ }
712
712
  return res.text.trim();
713
713
  };
714
714
 
@@ -746,6 +746,7 @@ const getSummaryText = (file, change) => {
746
746
  return `${status}: ${file.path}`;
747
747
  };
748
748
 
749
+ const config = loadConfig();
749
750
  const DEFAULT_IGNORED_FILES = config?.ignoredFiles?.length ? config.ignoredFiles : [];
750
751
  const DEFAULT_IGNORED_EXTENSIONS = config?.ignoredExtensions?.length ? config.ignoredExtensions : [];
751
752
  async function getChanges(git, options = {}) {
@@ -800,62 +801,117 @@ async function getChanges(git, options = {}) {
800
801
 
801
802
  const noResult = async ({ git, logger }) => {
802
803
  const { staged, unstaged, untracked } = await getChanges(git);
803
- if (staged.length > 0) {
804
+ const hasStaged = staged && staged.length > 0;
805
+ const hasUnstaged = unstaged && unstaged.length > 0;
806
+ const hasUntracked = untracked && untracked.length > 0;
807
+ if (hasStaged) {
804
808
  logger.log(`Staged files detected, but no summary generated...`, { color: 'red' });
805
809
  logger.log(`Files are likely either:\n • changed files are ignored\n • file diff is too large.`, { color: 'yellow' });
806
810
  }
807
- else if (unstaged && unstaged.length > 0) {
808
- logger.log('No staged files detected, but unstaged files detected.', { color: 'yellow' });
809
- logger.verbose(`\n Unstaged Changes: \n ${unstaged.map(({ summary }) => summary).join('\n ')}`, {
810
- color: 'yellow',
811
- });
812
- }
813
- else if (untracked && untracked.length > 0) {
814
- logger.log('No staged files detected, but untracked files detected.', { color: 'yellow' });
815
- logger.verbose(`\n Untracked Changes: \n ${untracked.map(({ summary }) => summary).join('\n ')}`, {
816
- color: 'yellow',
817
- });
811
+ else if (hasUnstaged || hasUntracked) {
812
+ logger.log('Forget something? No staged changes found... 👻', { color: 'red' });
813
+ if (hasUnstaged) {
814
+ logger.log('\nDetected unstaged changes', { color: 'yellow' });
815
+ logger.verbose(`\t${unstaged.map(({ summary }) => summary).join('\n\t')}`, {
816
+ color: 'red',
817
+ });
818
+ }
819
+ if (hasUntracked) {
820
+ logger.log('\nDetected untracked changes', { color: 'yellow' });
821
+ logger.verbose(`\t${untracked.map(({ summary }) => summary).join('\n\t')}`, {
822
+ color: 'red',
823
+ });
824
+ }
818
825
  }
819
826
  else {
820
- logger.log('No repo changes detected.', { color: 'yellow' });
827
+ logger.log('No repo changes detected. 👀', { color: 'blue' });
821
828
  }
822
- process.exit(0);
823
829
  };
824
830
 
825
831
  async function createCommit(commitMsg, git) {
826
832
  return await git.commit(commitMsg);
827
833
  }
828
834
 
829
- const argv = loadArgv();
835
+ // const argv = loadArgv()
830
836
  const tokenizer = getTokenizer();
831
837
  const git = simpleGit.simpleGit();
832
- async function main(options) {
833
- const logger = new Logger(config);
834
- if (!config.openAIApiKey) {
838
+ const command = ['commit', '$0'];
839
+ const description = 'Generate a commit message based on the diff summary';
840
+ const builder = {
841
+ model: { type: 'string', description: 'LLM/Model-Name' },
842
+ openAIApiKey: {
843
+ type: 'string',
844
+ description: 'OpenAI API Key',
845
+ conflicts: 'huggingFaceHubApiKey',
846
+ },
847
+ huggingFaceHubApiKey: {
848
+ type: 'string',
849
+ description: 'HuggingFace Hub API Key',
850
+ conflicts: 'openAIApiKey',
851
+ },
852
+ tokenLimit: { type: 'number', description: 'Token limit' },
853
+ prompt: {
854
+ type: 'string',
855
+ alias: 'p',
856
+ description: 'Commit message prompt',
857
+ },
858
+ i: {
859
+ type: 'boolean',
860
+ alias: 'interactive',
861
+ description: 'Toggle interactive mode',
862
+ },
863
+ s: {
864
+ type: 'boolean',
865
+ description: 'Automatically commit staged changes with generated commit message',
866
+ default: false,
867
+ },
868
+ e: {
869
+ type: 'boolean',
870
+ alias: 'edit',
871
+ description: 'Open commit message in editor before proceeding',
872
+ },
873
+ summarizePrompt: {
874
+ type: 'string',
875
+ description: 'Large file summary prompt',
876
+ },
877
+ ignoredFiles: {
878
+ type: 'array',
879
+ description: 'Ignored files',
880
+ },
881
+ ignoredExtensions: {
882
+ type: 'array',
883
+ description: 'Ignored extensions',
884
+ },
885
+ };
886
+ async function handler(argv) {
887
+ const options = loadConfig(argv);
888
+ const logger = new Logger(options);
889
+ const key = getModelAPIKey(options.model, options);
890
+ if (!key) {
835
891
  logger.log(`No API Key found. 🗝️🚪`, { color: 'red' });
836
892
  process.exit(1);
837
893
  }
838
- const model = getModel({
894
+ const model = getModel(options.model, key, {
839
895
  temperature: 0.4,
840
896
  maxConcurrency: 10,
841
- openAIApiKey: config.openAIApiKey,
842
897
  });
843
- const INTERACTIVE = config?.mode === 'interactive' || options.interactive;
898
+ const INTERACTIVE = options?.mode === 'interactive' || options.interactive;
844
899
  const { staged: changes } = await getChanges(git);
845
900
  let summary = '';
846
901
  let commitMsg = '';
847
- let promptTemplate = config?.prompt || '';
902
+ let promptTemplate = options?.prompt || '';
848
903
  let modifyPrompt = false;
849
904
  while (true) {
850
905
  if (changes.length !== 0 && !summary.length) {
851
906
  logger.verbose(`\nChanged Files: \n ${changes.map(({ summary }) => summary).join('\n ')}`, {
852
907
  color: 'blue',
853
908
  });
854
- summary = await fileChangeParser(changes, { tokenizer, git, model });
909
+ summary = await fileChangeParser(changes, { tokenizer, git, model, logger });
855
910
  }
856
911
  // Handle empty summary
857
912
  if (!summary.length) {
858
- noResult({ git, logger });
913
+ await noResult({ git, logger });
914
+ process.exit(0);
859
915
  }
860
916
  // Prompt user for commit template prompt, if necessary
861
917
  if (modifyPrompt) {
@@ -931,7 +987,7 @@ async function main(options) {
931
987
  process.exit(0);
932
988
  }
933
989
  if (reviewAnswer === 'edit') {
934
- config.openInEditor = true;
990
+ options.openInEditor = true;
935
991
  }
936
992
  if (reviewAnswer === 'retryFull') {
937
993
  summary = '';
@@ -950,7 +1006,7 @@ async function main(options) {
950
1006
  continue;
951
1007
  }
952
1008
  }
953
- if (config.openInEditor) {
1009
+ if (options.openInEditor) {
954
1010
  commitMsg = await prompts$1.editor({
955
1011
  message: 'Edit the commit message',
956
1012
  default: commitMsg,
@@ -965,7 +1021,7 @@ async function main(options) {
965
1021
  }
966
1022
  const MODE = (options.interactive && 'interactive') ||
967
1023
  (options.commit && 'interactive') ||
968
- config?.mode ||
1024
+ options?.mode ||
969
1025
  'stdout';
970
1026
  // Handle resulting commit message
971
1027
  switch (MODE) {
@@ -981,4 +1037,27 @@ async function main(options) {
981
1037
  process.exit(0);
982
1038
  }
983
1039
  }
984
- main(argv).catch(console.error);
1040
+
1041
+ var commit = /*#__PURE__*/Object.freeze({
1042
+ __proto__: null,
1043
+ builder: builder,
1044
+ command: command,
1045
+ description: description,
1046
+ handler: handler
1047
+ });
1048
+
1049
+ yargs
1050
+ .scriptName('coco')
1051
+ .commandDir('./commands', {
1052
+ extensions: ['ts'],
1053
+ })
1054
+ .demandCommand()
1055
+ .strict()
1056
+ .option('h', { alias: 'help' })
1057
+ .option('v', {
1058
+ alias: 'verbose',
1059
+ type: 'boolean',
1060
+ description: 'Run with verbose logging',
1061
+ }).argv;
1062
+
1063
+ exports.commit = commit;
@@ -1,4 +1,10 @@
1
1
  import { Config } from './types';
2
+ /**
3
+ * Default Config
4
+ *
5
+ * @type {Config}
6
+ */
7
+ export declare const DEFAULT_CONFIG: Config;
2
8
  /**
3
9
  * Load application config
4
10
  *
@@ -16,6 +22,4 @@ import { Config } from './types';
16
22
  *
17
23
  * @returns {Config} application config
18
24
  **/
19
- export declare function loadConfig(): Config;
20
- declare const config: Config;
21
- export default config;
25
+ export declare function loadConfig(argv?: {}): Config;
@@ -1,8 +1,22 @@
1
1
  export interface Config {
2
+ /**
3
+ * The LLM model to use for generating results.
4
+ *
5
+ * @default 'openai/gpt-3.5-turbo'
6
+ *
7
+ * @example 'openai/gpt-4'
8
+ * @example 'openai/gpt-3.5-turbo'
9
+ * @example 'huggingface/bigscience/bloom'
10
+ **/
11
+ model: string;
2
12
  /**
3
13
  * The OpenAI API key.
4
14
  */
5
- openAIApiKey: string;
15
+ openAIApiKey?: string;
16
+ /**
17
+ * The HuggingFace Hub API key.
18
+ */
19
+ huggingFaceHubApiKey?: string;
6
20
  /**
7
21
  * The maximum number of tokens per request.
8
22
  *