git-coco 0.14.2 → 0.14.3

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.
Files changed (3) hide show
  1. package/dist/index.esm.mjs +1447 -1441
  2. package/dist/index.js +1447 -1441
  3. package/package.json +3 -2
@@ -52,987 +52,589 @@ import * as readline from 'readline';
52
52
 
53
53
  // This file is auto-generated - DO NOT EDIT
54
54
  /* eslint-disable */
55
- /**
56
- * Schema ID for JSON validation
57
- */
58
- const SCHEMA_PUBLIC_URL = "http://git-co.co/schema.json";
59
55
  /**
60
56
  * Current build version from package.json
61
57
  */
62
- const BUILD_VERSION = "0.14.2";
58
+ const BUILD_VERSION = "0.14.3";
59
+
60
+ const isInteractive = (config) => {
61
+ return config?.mode === 'interactive' || !!config?.interactive;
62
+ };
63
+ const SEPERATOR = chalk.blue('─────────────');
64
+ const DIVIDER = chalk.dim(`\\`);
65
+ const LOGO = chalk.green(`┌──────┐
66
+ │┏┏┓┏┏┓│
67
+ │┗┗┛┗┗┛│
68
+ └──────┘`);
69
+ chalk.green(`┌────┐
70
+ │coco│
71
+ └────┘`);
72
+ const bannerWithHeader = (banner) => {
73
+ return chalk.green(`┌────┐
74
+ │coco│ ${banner}
75
+ └────┘`);
76
+ };
77
+ const USAGE_BANNER = chalk.green(`${LOGO}
78
+ ${chalk.bgGreen(`\xa0v${BUILD_VERSION}\xa0`) }
79
+ `);
80
+ const getCommandUsageHeader = (command) => {
81
+ return chalk.green(`${USAGE_BANNER}\n${chalk.white('Command:')}\n\xa0\xa0\xa0\xa0\xa0 $0 ${chalk.greenBright(command)} [options]`);
82
+ };
83
+ const CONFIG_ALREADY_EXISTS = (path) => {
84
+ return `existing config found in '${path}', do you want to override it?`;
85
+ };
86
+ const severityColors = [
87
+ chalk.greenBright, // 1
88
+ chalk.green, // 2
89
+ chalk.cyan, // 3
90
+ chalk.yellowBright, // 4
91
+ chalk.yellow, // 5
92
+ chalk.bgYellow, // 6
93
+ chalk.red, // 7
94
+ chalk.redBright, // 8
95
+ chalk.bgRed, // 9
96
+ chalk.bgRedBright, // 10
97
+ ];
98
+ const severityColor = (severity) => {
99
+ return severityColors[Math.min(severity - 1, severityColors.length - 1)];
100
+ };
101
+ const statusColor = (status) => {
102
+ switch (status) {
103
+ case 'completed':
104
+ return chalk.green;
105
+ case 'skipped':
106
+ return chalk.yellow;
107
+ case 'omitted':
108
+ return chalk.red;
109
+ default:
110
+ return chalk.blue;
111
+ }
112
+ };
113
+ const hotKey = (key) => chalk.dim(`(${key})`);
114
+
63
115
  /**
64
- * Generated JSON schema
116
+ * Returns a new object with all undefined keys removed
117
+ *
118
+ * @param obj Object to remove undefined keys from
119
+ * @returns
65
120
  */
66
- const schema$1 = {
67
- "$id": "http://git-co.co/schema.json",
68
- "$schema": "http://json-schema.org/draft-07/schema#",
69
- "$ref": "#/definitions/ConfigWithServiceObject",
70
- "definitions": {
71
- "ConfigWithServiceObject": {
72
- "type": "object",
73
- "additionalProperties": false,
74
- "properties": {
75
- "service": {
76
- "$ref": "#/definitions/LLMService"
77
- },
78
- "interactive": {
79
- "type": "boolean"
80
- },
81
- "verbose": {
82
- "type": "boolean",
83
- "description": "Enable verbose logging.",
84
- "default": false
85
- },
86
- "version": {
87
- "type": "boolean"
88
- },
89
- "help": {
90
- "type": "boolean"
91
- },
92
- "mode": {
93
- "type": "string",
94
- "enum": [
95
- "stdout",
96
- "interactive"
97
- ],
98
- "description": "The output destination for the generated result.\n- 'stdout': Prints the result to the standard output. This is the default behavior.\n- 'interactive': Provides an interactive prompt for editing the result & committing the changes.",
99
- "default": "stdout"
100
- },
101
- "openInEditor": {
102
- "type": "boolean",
103
- "description": "Open the commit message in an editor for editing before proceeding.",
104
- "default": false
105
- },
106
- "prompt": {
107
- "type": "string",
108
- "description": "The prompt text used for generating results."
109
- },
110
- "summarizePrompt": {
111
- "type": "string",
112
- "description": "The prompt text used specifically for generating summaries of large files."
113
- },
114
- "ignoredFiles": {
115
- "type": "array",
116
- "items": {
117
- "type": "string"
118
- },
119
- "description": "An array of file paths or patterns to be ignored during processing.\n\nNote: This is a list of patterns interpreted by the `minimatch` library.",
120
- "examples": [
121
- [
122
- "package-lock.json",
123
- "node_modules"
124
- ]
125
- ],
126
- "default": "['package-lock.json', contents of .gitignore, contents of .ignore]"
127
- },
128
- "ignoredExtensions": {
129
- "type": "array",
130
- "items": {
131
- "type": "string"
132
- },
133
- "description": "An array of file extensions to be ignored during processing.",
134
- "default": [
135
- ".map",
136
- ".lock"
137
- ]
138
- },
139
- "defaultBranch": {
140
- "type": "string",
141
- "description": "Default git branch for the repository.",
142
- "default": "main"
143
- }
144
- },
145
- "required": [
146
- "defaultBranch",
147
- "mode",
148
- "service"
149
- ]
150
- },
151
- "LLMService": {
152
- "anyOf": [
153
- {
154
- "$ref": "#/definitions/OpenAILLMService"
155
- },
156
- {
157
- "$ref": "#/definitions/OllamaLLMService"
158
- },
159
- {
160
- "$ref": "#/definitions/AnthropicLLMService"
121
+ function removeUndefined(obj) {
122
+ return Object.fromEntries(Object.entries(obj).filter(([, value]) => value !== undefined));
123
+ }
124
+
125
+ async function updateFileSection({ filePath, startComment, endComment, getNewContent, confirmUpdate = true, confirmMessage = (path) => `A section already exists in ${path}, do you want to override it?`, }) {
126
+ const lines = fs__default.existsSync(filePath) ? fs__default.readFileSync(filePath, 'utf-8').split(/\r?\n/) : [];
127
+ const newLines = [];
128
+ let foundSection = false;
129
+ for (let i = 0; i < lines.length; i++) {
130
+ if (lines[i].trim() === startComment) {
131
+ foundSection = true;
132
+ if (confirmUpdate) {
133
+ const confirmOverwrite = await confirm({
134
+ message: typeof confirmMessage === 'function' ? confirmMessage(filePath) : confirmMessage,
135
+ default: false,
136
+ });
137
+ if (!confirmOverwrite) {
138
+ // keep all lines until the end comment
139
+ while (i < lines.length && lines[i].trim() !== endComment) {
140
+ newLines.push(lines[i]);
141
+ i++;
142
+ }
143
+ newLines.push(endComment);
144
+ continue;
161
145
  }
162
- ]
163
- },
164
- "OpenAILLMService": {
165
- "type": "object",
166
- "additionalProperties": false,
167
- "properties": {
168
- "provider": {
169
- "$ref": "#/definitions/LLMProvider"
170
- },
171
- "model": {
172
- "$ref": "#/definitions/LLMModel"
173
- },
174
- "fields": {
175
- "type": "object",
176
- "additionalProperties": false,
177
- "properties": {
178
- "verbose": {
179
- "type": "boolean"
180
- },
181
- "callbacks": {
182
- "$ref": "#/definitions/Callbacks"
183
- },
184
- "tags": {
185
- "type": "array",
186
- "items": {
187
- "type": "string"
188
- }
189
- },
190
- "metadata": {
191
- "type": "object",
192
- "additionalProperties": {}
193
- },
194
- "maxConcurrency": {
195
- "type": "number",
196
- "description": "The maximum number of concurrent calls that can be made. Defaults to `Infinity`, which means no limit."
197
- },
198
- "maxRetries": {
199
- "type": "number",
200
- "description": "The maximum number of retries that can be made for a single call, with an exponential backoff between each attempt. Defaults to 6."
201
- },
202
- "onFailedAttempt": {
203
- "$ref": "#/definitions/FailedAttemptHandler",
204
- "description": "Custom handler to handle failed attempts. Takes the originally thrown error object as input, and should itself throw an error if the input error is not retryable."
205
- },
206
- "callbackManager": {
207
- "$ref": "#/definitions/CallbackManager",
208
- "deprecated": "Use `callbacks` instead"
209
- },
210
- "cache": {
211
- "anyOf": [
212
- {
213
- "$ref": "#/definitions/BaseCache"
214
- },
215
- {
216
- "type": "boolean"
217
- }
218
- ]
219
- },
220
- "concurrency": {
221
- "type": "number",
222
- "deprecated": "Use `maxConcurrency` instead"
223
- },
224
- "bestOf": {
225
- "type": "number",
226
- "description": "Generates `bestOf` completions server side and returns the \"best\""
227
- },
228
- "batchSize": {
229
- "type": "number",
230
- "description": "Batch size to use when passing multiple documents to generate"
231
- },
232
- "temperature": {
233
- "type": "number",
234
- "description": "Sampling temperature to use"
235
- },
236
- "maxTokens": {
237
- "type": "number",
238
- "description": "Maximum number of tokens to generate in the completion. -1 returns as many tokens as possible given the prompt and the model's maximum context size."
239
- },
240
- "topP": {
241
- "type": "number",
242
- "description": "Total probability mass of tokens to consider at each step"
243
- },
244
- "frequencyPenalty": {
245
- "type": "number",
246
- "description": "Penalizes repeated tokens according to frequency"
247
- },
248
- "presencePenalty": {
249
- "type": "number",
250
- "description": "Penalizes repeated tokens"
251
- },
252
- "n": {
253
- "type": "number",
254
- "description": "Number of completions to generate for each prompt"
255
- },
256
- "logitBias": {
257
- "type": "object",
258
- "additionalProperties": {
259
- "type": "number"
260
- },
261
- "description": "Dictionary used to adjust the probability of specific tokens being generated"
262
- },
263
- "user": {
264
- "type": "string",
265
- "description": "Unique string identifier representing your end-user, which can help OpenAI to monitor and detect abuse."
266
- },
267
- "streaming": {
268
- "type": "boolean",
269
- "description": "Whether to stream the results or not. Enabling disables tokenUsage reporting"
270
- },
271
- "streamUsage": {
272
- "type": "boolean",
273
- "description": "Whether or not to include token usage data in streamed chunks.",
274
- "default": true
275
- },
276
- "modelName": {
277
- "type": "string",
278
- "description": "Model name to use Alias for `model`"
279
- },
280
- "model": {
281
- "type": "string",
282
- "description": "Model name to use"
283
- },
284
- "modelKwargs": {
285
- "type": "object",
286
- "description": "Holds any additional parameters that are valid to pass to {@link * https://platform.openai.com/docs/api-reference/completions/create | } * `openai.createCompletion`} that are not explicitly specified on this class."
287
- },
288
- "stop": {
289
- "type": "array",
290
- "items": {
291
- "type": "string"
292
- },
293
- "description": "List of stop words to use when generating Alias for `stopSequences`"
294
- },
295
- "stopSequences": {
296
- "type": "array",
297
- "items": {
298
- "type": "string"
299
- },
300
- "description": "List of stop words to use when generating"
301
- },
302
- "timeout": {
303
- "type": "number",
304
- "description": "Timeout to use when making requests to OpenAI."
305
- },
306
- "openAIApiKey": {
307
- "type": "string",
308
- "description": "API key to use when making requests to OpenAI. Defaults to the value of `OPENAI_API_KEY` environment variable. Alias for `apiKey`"
309
- },
310
- "apiKey": {
311
- "type": "string",
312
- "description": "API key to use when making requests to OpenAI. Defaults to the value of `OPENAI_API_KEY` environment variable."
313
- }
314
- }
315
- },
316
- "tokenLimit": {
317
- "type": "number",
318
- "description": "The maximum number of tokens per request.",
319
- "default": 2048
320
- },
321
- "temperature": {
322
- "type": "number",
323
- "description": "The temperature value controls the randomness of the generated output. Higher values (e.g., 0.8) make the output more random, while lower values (e.g., 0.2) make it more deterministic.",
324
- "default": 0.4
325
- },
326
- "maxConcurrent": {
327
- "type": "number",
328
- "description": "The maximum number of requests to make concurrently.",
329
- "default": 6
330
- },
331
- "authentication": {
332
- "anyOf": [
333
- {
334
- "type": "object",
335
- "properties": {
336
- "type": {
337
- "type": "string",
338
- "const": "None"
339
- },
340
- "credentials": {
341
- "not": {}
342
- }
343
- },
344
- "required": [
345
- "type"
346
- ],
347
- "additionalProperties": false
348
- },
349
- {
350
- "type": "object",
351
- "properties": {
352
- "type": {
353
- "type": "string",
354
- "const": "OAuth"
355
- },
356
- "credentials": {
357
- "type": "object",
358
- "properties": {
359
- "clientId": {
360
- "type": "string"
361
- },
362
- "clientSecret": {
363
- "type": "string"
364
- },
365
- "token": {
366
- "type": "string"
367
- }
368
- },
369
- "additionalProperties": false
370
- }
371
- },
372
- "required": [
373
- "type",
374
- "credentials"
375
- ],
376
- "additionalProperties": false
377
- },
378
- {
379
- "type": "object",
380
- "properties": {
381
- "type": {
382
- "type": "string",
383
- "const": "APIKey"
384
- },
385
- "credentials": {
386
- "type": "object",
387
- "properties": {
388
- "apiKey": {
389
- "type": "string"
390
- }
391
- },
392
- "required": [
393
- "apiKey"
394
- ],
395
- "additionalProperties": false
396
- }
397
- },
398
- "required": [
399
- "type",
400
- "credentials"
401
- ],
402
- "additionalProperties": false
403
- }
404
- ]
405
- },
406
- "requestOptions": {
407
- "type": "object",
408
- "properties": {
409
- "timeout": {
410
- "type": "number"
411
- },
412
- "maxRetries": {
413
- "type": "number"
414
- }
415
- },
416
- "additionalProperties": false
417
- }
418
- },
419
- "required": [
420
- "authentication",
421
- "model",
422
- "provider"
423
- ]
424
- },
425
- "LLMProvider": {
426
- "type": "string",
427
- "enum": [
428
- "openai",
429
- "ollama",
430
- "anthropic"
431
- ]
432
- },
433
- "LLMModel": {
434
- "anyOf": [
435
- {
436
- "type": "string",
437
- "enum": [
438
- "davinci-002",
439
- "babbage-002",
440
- "text-davinci-003",
441
- "text-davinci-002",
442
- "text-davinci-001",
443
- "text-curie-001",
444
- "text-babbage-001",
445
- "text-ada-001",
446
- "davinci",
447
- "curie",
448
- "babbage",
449
- "ada",
450
- "code-davinci-002",
451
- "code-davinci-001",
452
- "code-cushman-002",
453
- "code-cushman-001",
454
- "davinci-codex",
455
- "cushman-codex",
456
- "text-davinci-edit-001",
457
- "code-davinci-edit-001",
458
- "text-embedding-ada-002",
459
- "text-similarity-davinci-001",
460
- "text-similarity-curie-001",
461
- "text-similarity-babbage-001",
462
- "text-similarity-ada-001",
463
- "text-search-davinci-doc-001",
464
- "text-search-curie-doc-001",
465
- "text-search-babbage-doc-001",
466
- "text-search-ada-doc-001",
467
- "code-search-babbage-code-001",
468
- "code-search-ada-code-001",
469
- "gpt2",
470
- "gpt-3.5-turbo",
471
- "gpt-35-turbo",
472
- "gpt-3.5-turbo-0301",
473
- "gpt-3.5-turbo-0613",
474
- "gpt-3.5-turbo-1106",
475
- "gpt-3.5-turbo-0125",
476
- "gpt-3.5-turbo-16k",
477
- "gpt-3.5-turbo-16k-0613",
478
- "gpt-3.5-turbo-instruct",
479
- "gpt-3.5-turbo-instruct-0914",
480
- "gpt-4",
481
- "gpt-4-0314",
482
- "gpt-4-0613",
483
- "gpt-4-32k",
484
- "gpt-4-32k-0314",
485
- "gpt-4-32k-0613",
486
- "gpt-4-turbo",
487
- "gpt-4-turbo-2024-04-09",
488
- "gpt-4-turbo-preview",
489
- "gpt-4-1106-preview",
490
- "gpt-4-0125-preview",
491
- "gpt-4-vision-preview",
492
- "gpt-4o",
493
- "gpt-4o-2024-05-13"
494
- ]
495
- },
496
- {
497
- "$ref": "#/definitions/OllamaModel"
498
- },
499
- {
500
- "$ref": "#/definitions/AnthropicModel"
501
- }
502
- ]
503
- },
504
- "OllamaModel": {
505
- "type": "string",
506
- "enum": [
507
- "codegemma:2b",
508
- "codegemma:7b-code",
509
- "codegemma",
510
- "codellama:13b",
511
- "codellama:34b",
512
- "codellama:70b",
513
- "codellama:7b",
514
- "codellama:instruct",
515
- "codellama:latest",
516
- "codellama",
517
- "gemma:2b",
518
- "gemma:7b",
519
- "gemma:latest",
520
- "gemma",
521
- "llama2:13b",
522
- "llama2:70b",
523
- "llama2:chat",
524
- "llama2:latest",
525
- "llama2:text",
526
- "llama2",
527
- "llama3:70b-text",
528
- "llama3:70b",
529
- "llama3:latest",
530
- "llama3:text",
531
- "llama3.1:70b",
532
- "llama3.1:8b",
533
- "llama3.1:latest",
534
- "llama3.2",
535
- "llama3.2:latest",
536
- "llama3.2:1b",
537
- "llama3.2:3b",
538
- "llama3.2:1b-instruct-fp16",
539
- "llama3.2:1b-instruct-q3_K_M",
540
- "llama3",
541
- "mistral:7b",
542
- "mistral:latest",
543
- "mistral:text",
544
- "mistral",
545
- "phi3:14b",
546
- "phi3:3.8b",
547
- "phi3:instruct",
548
- "phi3:medium-128k",
549
- "phi3:medium-4k",
550
- "phi3:medium",
551
- "phi3",
552
- "qwen2:0.5b",
553
- "qwen2:1.5b",
554
- "qwen2:72b-text",
555
- "qwen2:72b",
556
- "qwen2"
557
- ]
558
- },
559
- "AnthropicModel": {
560
- "type": "string",
561
- "enum": [
562
- "claude-3-5-sonnet-20240620",
563
- "claude-3-opus-20240229",
564
- "claude-3-sonnet-20240229",
565
- "claude-3-haiku-20240307",
566
- "claude-2.1",
567
- "claude-2.0"
568
- ]
569
- },
570
- "Callbacks": {
571
- "anyOf": [
572
- {
573
- "$ref": "#/definitions/CallbackManager"
574
- },
575
- {
576
- "type": "array",
577
- "items": {
578
- "anyOf": [
579
- {
580
- "$ref": "#/definitions/BaseCallbackHandler"
581
- },
582
- {
583
- "$ref": "#/definitions/CallbackHandlerMethods"
584
- }
585
- ]
586
- }
587
- }
588
- ]
589
- },
590
- "CallbackManager": {
591
- "type": "object",
592
- "properties": {
593
- "handlers": {
594
- "type": "array",
595
- "items": {
596
- "$ref": "#/definitions/BaseCallbackHandler"
597
- }
598
- },
599
- "inheritableHandlers": {
600
- "type": "array",
601
- "items": {
602
- "$ref": "#/definitions/BaseCallbackHandler"
603
- }
604
- },
605
- "tags": {
606
- "type": "array",
607
- "items": {
608
- "type": "string"
609
- }
610
- },
611
- "inheritableTags": {
612
- "type": "array",
613
- "items": {
614
- "type": "string"
615
- }
616
- },
617
- "metadata": {
618
- "type": "object",
619
- "additionalProperties": {}
620
- },
621
- "inheritableMetadata": {
622
- "type": "object",
623
- "additionalProperties": {}
624
- },
625
- "name": {
626
- "type": "string"
627
- },
628
- "_parentRunId": {
629
- "type": "string"
630
- }
631
- },
632
- "required": [
633
- "handlers",
634
- "inheritableHandlers",
635
- "tags",
636
- "inheritableTags",
637
- "metadata",
638
- "inheritableMetadata",
639
- "name"
640
- ],
641
- "additionalProperties": false
146
+ }
147
+ newLines.push(startComment);
148
+ // Insert the new content
149
+ const newContent = await getNewContent();
150
+ newLines.push(newContent);
151
+ // Skip the existing content of the section
152
+ while (i < lines.length && lines[i].trim() !== endComment) {
153
+ i++;
154
+ }
155
+ newLines.push(endComment);
156
+ continue;
157
+ }
158
+ if (!foundSection || lines[i].trim() !== endComment) {
159
+ newLines.push(lines[i]);
160
+ }
161
+ }
162
+ // If section wasn't found, append it at the end
163
+ if (!foundSection) {
164
+ newLines.push('\n' + startComment);
165
+ const newContent = await getNewContent();
166
+ newLines.push(newContent);
167
+ newLines.push(endComment);
168
+ }
169
+ // Write the updated contents back to the file
170
+ fs__default.writeFileSync(filePath, newLines.join('\n'));
171
+ }
172
+
173
+ const template$5 = `GOAL: Use functional abstractions to summarize the following text
174
+
175
+ RULES: Avoid phrases like "this change", "this code", or "this function" etc. Instead refer to the function, variable, or class by name.
176
+
177
+ TEXT:"""{text}"""
178
+ `;
179
+ const inputVariables$4 = ['text'];
180
+ const SUMMARIZE_PROMPT = new PromptTemplate({
181
+ inputVariables: inputVariables$4,
182
+ template: template$5,
183
+ });
184
+
185
+ /**
186
+ * Retrieves the provider and model from the given configuration object.
187
+ * @param config The configuration object.
188
+ * @returns An object containing the provider and model.
189
+ * @throws Error if the configuration is invalid or missing required properties.
190
+ */
191
+ function getModelAndProviderFromConfig(config) {
192
+ if (!config.service) {
193
+ throw new Error('Invalid service: undefined');
194
+ }
195
+ const { provider, model } = config.service;
196
+ if (!model || !provider) {
197
+ throw new Error(`Invalid service: ${config.service}`);
198
+ }
199
+ return { provider, model };
200
+ }
201
+ /**
202
+ * Retrieve appropriate API key based on selected model
203
+ * @param service
204
+ * @param options
205
+ * @returns API Key
206
+ */
207
+ function getApiKeyForModel(config) {
208
+ const { provider } = getModelAndProviderFromConfig(config);
209
+ switch (provider) {
210
+ case 'openai':
211
+ return getDefaultServiceApiKey(config);
212
+ default:
213
+ return getDefaultServiceApiKey(config);
214
+ }
215
+ }
216
+ /**
217
+ * Retrieves the default service API key from the given configuration.
218
+ * @param config The configuration object.
219
+ * @returns The default service API key.
220
+ */
221
+ function getDefaultServiceApiKey(config) {
222
+ const service = config.service;
223
+ if (service.authentication.type === 'APIKey') {
224
+ return service.authentication.credentials?.apiKey;
225
+ }
226
+ else if (service.authentication.type === 'OAuth') {
227
+ return service.authentication.credentials?.token;
228
+ }
229
+ return '';
230
+ }
231
+ const DEFAULT_OPENAI_LLM_SERVICE = {
232
+ provider: 'openai',
233
+ model: 'gpt-4',
234
+ tokenLimit: 2024,
235
+ temperature: 0.32,
236
+ authentication: {
237
+ type: 'APIKey',
238
+ credentials: {
239
+ apiKey: '',
642
240
  },
643
- "BaseCallbackHandler": {
644
- "type": "object",
645
- "properties": {
646
- "lc_serializable": {
647
- "type": "boolean"
648
- },
649
- "lc_kwargs": {
650
- "$ref": "#/definitions/SerializedFields"
651
- },
652
- "lc_namespace": {
653
- "type": "array",
654
- "items": {
655
- "type": "string"
656
- },
657
- "description": "A path to the module that contains the class, eg. [\"langchain\", \"llms\"] Usually should be the same as the entrypoint the class is exported from."
658
- },
659
- "ignoreLLM": {
660
- "type": "boolean"
661
- },
662
- "ignoreChain": {
663
- "type": "boolean"
664
- },
665
- "ignoreAgent": {
666
- "type": "boolean"
667
- },
668
- "ignoreRetriever": {
669
- "type": "boolean"
241
+ },
242
+ };
243
+ const DEFAULT_OLLAMA_LLM_SERVICE = {
244
+ provider: 'ollama',
245
+ model: 'llama3',
246
+ endpoint: 'http://localhost:11434',
247
+ maxConcurrent: 1,
248
+ tokenLimit: 2024,
249
+ temperature: 0.4,
250
+ authentication: {
251
+ type: 'None',
252
+ credentials: undefined,
253
+ },
254
+ };
255
+ /**
256
+ * Retrieves the default service configuration based on the provided alias and optional model.
257
+ * @param provider - The alias of the service.
258
+ * @param model - The optional model to be used.
259
+ * @returns The default service configuration.
260
+ * @throws Error if the alias is invalid or undefined.
261
+ */
262
+ function getDefaultServiceConfigFromAlias(provider, model) {
263
+ switch (provider) {
264
+ case 'anthropic':
265
+ return {
266
+ ...DEFAULT_OPENAI_LLM_SERVICE,
267
+ model: model || DEFAULT_OPENAI_LLM_SERVICE.model,
268
+ };
269
+ case 'ollama':
270
+ return {
271
+ ...DEFAULT_OLLAMA_LLM_SERVICE,
272
+ model: model || DEFAULT_OLLAMA_LLM_SERVICE.model,
273
+ };
274
+ case 'openai':
275
+ default:
276
+ return {
277
+ ...DEFAULT_OPENAI_LLM_SERVICE,
278
+ model: model || DEFAULT_OPENAI_LLM_SERVICE.model,
279
+ };
280
+ }
281
+ }
282
+
283
+ const DEFAULT_IGNORED_FILES = ['package-lock.json', 'yarn.lock', 'node_modules'];
284
+ const DEFAULT_IGNORED_EXTENSIONS = ['.map', '.lock'];
285
+ const COCO_CONFIG_START_COMMENT = '# -- start coco config --';
286
+ const COCO_CONFIG_END_COMMENT = '# -- end coco config --';
287
+ /**
288
+ * Default Config
289
+ *
290
+ * @type {Config}
291
+ */
292
+ const DEFAULT_CONFIG$1 = {
293
+ mode: 'stdout',
294
+ verbose: false,
295
+ defaultBranch: 'main',
296
+ service: getDefaultServiceConfigFromAlias('openai'),
297
+ summarizePrompt: SUMMARIZE_PROMPT.template,
298
+ ignoredFiles: DEFAULT_IGNORED_FILES,
299
+ ignoredExtensions: DEFAULT_IGNORED_EXTENSIONS,
300
+ };
301
+ /**
302
+ * Create a named export of all config keys for use in other modules.
303
+ *
304
+ * @see Used in `src/lib/config/services/env.ts` to validate all env vars.
305
+ *
306
+ * @type {string[]}
307
+ */
308
+ const CONFIG_KEYS = Object.keys({
309
+ ...DEFAULT_CONFIG$1,
310
+ prompt: '',
311
+ });
312
+
313
+ /**
314
+ * Load environment variables
315
+ *
316
+ * @param {Config} config
317
+ * @returns {Config} Updated config
318
+ **/
319
+ function loadEnvConfig(config) {
320
+ const envConfig = {};
321
+ const envKeys = [...CONFIG_KEYS, 'COCO_SERVICE_PROVIDER', 'COCO_SERVICE_MODEL', 'OPEN_AI_KEY'];
322
+ envKeys.forEach((key) => {
323
+ const envVarName = toEnvVarName(key);
324
+ const envValue = parseEnvValue(key, process.env[envVarName]);
325
+ if (envValue === undefined) {
326
+ return;
327
+ }
328
+ if (key === 'COCO_SERVICE_PROVIDER' || key === 'COCO_SERVICE_MODEL' || key === 'OPEN_AI_KEY') {
329
+ // NOTE: We want to ensure that the service object is always defined
330
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
331
+ // @ts-ignore
332
+ envConfig.service = envConfig.service || {};
333
+ handleServiceEnvVar(envConfig.service, key, envValue);
334
+ }
335
+ else {
336
+ if (key === 'service' || !envValue) {
337
+ return;
338
+ }
339
+ envConfig[key] = envValue;
340
+ }
341
+ });
342
+ return { ...config, ...removeUndefined(envConfig) };
343
+ }
344
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
345
+ function handleServiceEnvVar(service, key, value) {
346
+ switch (key) {
347
+ case 'COCO_SERVICE_PROVIDER':
348
+ service.provider = value;
349
+ break;
350
+ case 'COCO_SERVICE_MODEL':
351
+ service.model = value;
352
+ break;
353
+ case 'OPEN_AI_KEY':
354
+ if (service.provider === 'openai') {
355
+ service.fields = { apiKey: value };
356
+ }
357
+ break;
358
+ }
359
+ }
360
+ function parseEnvValue(key, value) {
361
+ switch (true) {
362
+ // Handle undefined values
363
+ case value === undefined:
364
+ return undefined;
365
+ // Handle comma separated strings for ignoredFiles and ignoredExtensions arrays
366
+ case (key === 'ignoredFiles' || key === 'ignoredExtensions') &&
367
+ typeof value === 'string' &&
368
+ value.includes(','):
369
+ return value.split(',');
370
+ // Handle boolean values
371
+ case typeof value === 'string' && (value === 'false' || value === 'true'):
372
+ return value === 'true';
373
+ // Handle number values
374
+ case typeof value === 'string' && !isNaN(Number(value)):
375
+ return Number(value);
376
+ default:
377
+ return value;
378
+ }
379
+ }
380
+ function toEnvVarName(key) {
381
+ if (key === 'service') {
382
+ return key;
383
+ }
384
+ if (key.includes('COCO_')) {
385
+ return key;
386
+ }
387
+ return `COCO_${key.replace(/([A-Z])/g, '_$1').toLocaleUpperCase()}`;
388
+ }
389
+ function formatEnvValue(value) {
390
+ if (typeof value === 'number') {
391
+ return `${value}`;
392
+ }
393
+ else if (Array.isArray(value)) {
394
+ return `${value.join(',')}`;
395
+ }
396
+ else if (typeof value === 'string') {
397
+ // Escape newlines and tabs in strings
398
+ return `${value.replace(/\n/g, '\\n').replace(/\t/g, '\\t')}`;
399
+ }
400
+ return `${value}`;
401
+ }
402
+ const appendToEnvFile = async (filePath, config) => {
403
+ const getNewContent = async () => {
404
+ return Object.entries(config)
405
+ .map(([key, value]) => {
406
+ if (key === 'service') {
407
+ const service = value;
408
+ return `${service.provider ? `COCO_SERVICE_PROVIDER=${service.provider}` : ''}\n${service.model ? `COCO_SERVICE_MODEL=${service.model}` : ''}\n${service.authentication.type === 'APIKey'
409
+ ? `OPEN_AI_KEY=${service.authentication.credentials.apiKey}`
410
+ : ''}`;
411
+ }
412
+ const envVarName = toEnvVarName(key);
413
+ const envValue = formatEnvValue(value);
414
+ return `${envVarName}=${envValue}`;
415
+ })
416
+ .join('\n');
417
+ };
418
+ await updateFileSection({
419
+ filePath,
420
+ startComment: COCO_CONFIG_START_COMMENT,
421
+ endComment: COCO_CONFIG_END_COMMENT,
422
+ getNewContent,
423
+ confirmMessage: CONFIG_ALREADY_EXISTS,
424
+ });
425
+ };
426
+
427
+ /**
428
+ * Load git profile config (from ~/.gitconfig)
429
+ *
430
+ * @param {Config} config
431
+ * @returns {Config} Updated config
432
+ **/
433
+ function loadGitConfig(config) {
434
+ const gitConfigPath = path.join(os.homedir(), '.gitconfig');
435
+ if (fs.existsSync(gitConfigPath)) {
436
+ const gitConfigRaw = fs.readFileSync(gitConfigPath, 'utf-8');
437
+ const gitConfigParsed = ini.parse(gitConfigRaw);
438
+ const gitConfigServiceObject = gitConfigParsed.coco?.service;
439
+ let service = config.service;
440
+ if (gitConfigServiceObject) {
441
+ const gitServiceConfig = JSON.parse(gitConfigServiceObject);
442
+ service = gitServiceConfig || config?.service;
443
+ }
444
+ config = {
445
+ ...config,
446
+ service: service,
447
+ prompt: gitConfigParsed.coco?.prompt || config.prompt,
448
+ mode: gitConfigParsed.coco?.mode || config.mode,
449
+ summarizePrompt: gitConfigParsed.coco?.summarizePrompt || config.summarizePrompt,
450
+ ignoredFiles: gitConfigParsed.coco?.ignoredFiles || config.ignoredFiles,
451
+ ignoredExtensions: gitConfigParsed.coco?.ignoredExtensions || config.ignoredExtensions,
452
+ defaultBranch: gitConfigParsed.coco?.defaultBranch || config.defaultBranch,
453
+ verbose: gitConfigParsed.coco?.verbose || config.verbose,
454
+ };
455
+ }
456
+ return removeUndefined(config);
457
+ }
458
+ /**
459
+ * Appends the provided configuration to a git config file.
460
+ *
461
+ * @param filePath - The path to the .gitconfig
462
+ * @param config - The configuration object to append.
463
+ */
464
+ const appendToGitConfig = async (filePath, config) => {
465
+ if (!fs.existsSync(filePath)) {
466
+ throw new Error(`File ${filePath} does not exist.`);
467
+ }
468
+ const header = '[coco]';
469
+ const getNewContent = async () => {
470
+ const contentLines = [header];
471
+ for (const key in config) {
472
+ const value = config[key];
473
+ if (typeof value === 'object') {
474
+ // Serialize object to JSON string
475
+ contentLines.push(`\t${key} = ${JSON.stringify(value)}`);
476
+ }
477
+ else if (typeof value === 'string' && value.includes('\n')) {
478
+ // Wrap strings with new lines in quotes
479
+ contentLines.push(`\t${key} = ${JSON.stringify(value)}`);
480
+ }
481
+ else {
482
+ contentLines.push(`\t${key} = ${value}`);
483
+ }
484
+ }
485
+ return contentLines.join('\n');
486
+ };
487
+ await updateFileSection({
488
+ filePath,
489
+ startComment: COCO_CONFIG_START_COMMENT,
490
+ endComment: COCO_CONFIG_END_COMMENT,
491
+ getNewContent,
492
+ confirmUpdate: true,
493
+ confirmMessage: CONFIG_ALREADY_EXISTS,
494
+ });
495
+ };
496
+
497
+ /**
498
+ * Load .gitignore in project root
499
+ *
500
+ * @param {Config} config
501
+ * @returns
502
+ */
503
+ function loadGitignore(config) {
504
+ if (fs.existsSync('.gitignore')) {
505
+ const gitignoreContent = fs.readFileSync('.gitignore', 'utf-8');
506
+ config.ignoredFiles = [
507
+ ...(config?.ignoredFiles || []),
508
+ ...gitignoreContent.split('\n').filter((line) => line.trim() !== '' && !line.startsWith('#')),
509
+ ];
510
+ }
511
+ return config;
512
+ }
513
+ /**
514
+ * Load .ignore in project root
515
+ *
516
+ * @param {Config} config
517
+ * @returns
518
+ */
519
+ function loadIgnore(config) {
520
+ if (fs.existsSync('.ignore')) {
521
+ const ignoreContent = fs.readFileSync('.ignore', 'utf-8');
522
+ config.ignoredFiles = [
523
+ ...(config?.ignoredFiles || []),
524
+ ...ignoreContent.split('\n').filter((line) => line.trim() !== '' && !line.startsWith('#')),
525
+ ];
526
+ }
527
+ return config;
528
+ }
529
+
530
+ // This file is auto-generated - DO NOT EDIT
531
+ /* eslint-disable */
532
+ /**
533
+ * Schema ID for JSON validation
534
+ */
535
+ const SCHEMA_PUBLIC_URL = "http://git-co.co/schema.json";
536
+ /**
537
+ * Generated JSON schema
538
+ */
539
+ const schema$1 = {
540
+ "$id": "http://git-co.co/schema.json",
541
+ "$schema": "http://json-schema.org/draft-07/schema#",
542
+ "$ref": "#/definitions/ConfigWithServiceObject",
543
+ "definitions": {
544
+ "ConfigWithServiceObject": {
545
+ "type": "object",
546
+ "additionalProperties": false,
547
+ "properties": {
548
+ "service": {
549
+ "$ref": "#/definitions/LLMService"
670
550
  },
671
- "ignoreCustomEvent": {
551
+ "interactive": {
672
552
  "type": "boolean"
673
553
  },
674
- "_awaitHandler": {
675
- "type": "boolean"
554
+ "verbose": {
555
+ "type": "boolean",
556
+ "description": "Enable verbose logging.",
557
+ "default": false
676
558
  },
677
- "raiseError": {
559
+ "version": {
678
560
  "type": "boolean"
679
561
  },
680
- "name": {
681
- "type": "string"
682
- },
683
- "awaitHandlers": {
562
+ "help": {
684
563
  "type": "boolean"
685
- }
686
- },
687
- "required": [
688
- "awaitHandlers",
689
- "ignoreAgent",
690
- "ignoreChain",
691
- "ignoreCustomEvent",
692
- "ignoreLLM",
693
- "ignoreRetriever",
694
- "lc_kwargs",
695
- "lc_namespace",
696
- "lc_serializable",
697
- "name",
698
- "raiseError"
699
- ],
700
- "additionalProperties": false,
701
- "description": "Abstract base class for creating callback handlers in the LangChain framework. It provides a set of optional methods that can be overridden in derived classes to handle various events during the execution of a LangChain application."
702
- },
703
- "SerializedFields": {
704
- "type": "object"
705
- },
706
- "CallbackHandlerMethods": {
707
- "type": "object",
708
- "additionalProperties": false,
709
- "description": "Base interface for callbacks. All methods are optional. If a method is not implemented, it will be ignored. If a method is implemented, it will be called at the appropriate time. All methods are called with the run ID of the LLM/ChatModel/Chain that is running, which is generated by the CallbackManager."
710
- },
711
- "FailedAttemptHandler": {
712
- "$comment": "(error: any) => any",
713
- "type": "object",
714
- "properties": {
715
- "namedArgs": {
716
- "type": "object",
717
- "properties": {
718
- "error": {}
719
- },
720
- "required": [
721
- "error"
722
- ],
723
- "additionalProperties": false
724
- }
725
- }
726
- },
727
- "BaseCache": {
728
- "type": "object",
729
- "additionalProperties": false,
730
- "description": "Base class for all caches. All caches should extend this class."
731
- },
732
- "OllamaLLMService": {
733
- "type": "object",
734
- "additionalProperties": false,
735
- "properties": {
736
- "provider": {
737
- "$ref": "#/definitions/LLMProvider"
738
- },
739
- "model": {
740
- "$ref": "#/definitions/LLMModel"
741
- },
742
- "endpoint": {
743
- "type": "string"
744
564
  },
745
- "fields": {
746
- "type": "object",
747
- "additionalProperties": false,
748
- "properties": {
749
- "verbose": {
750
- "type": "boolean"
751
- },
752
- "callbacks": {
753
- "$ref": "#/definitions/Callbacks"
754
- },
755
- "tags": {
756
- "type": "array",
757
- "items": {
758
- "type": "string"
759
- }
760
- },
761
- "metadata": {
762
- "type": "object",
763
- "additionalProperties": {}
764
- },
765
- "maxConcurrency": {
766
- "type": "number",
767
- "description": "The maximum number of concurrent calls that can be made. Defaults to `Infinity`, which means no limit."
768
- },
769
- "maxRetries": {
770
- "type": "number",
771
- "description": "The maximum number of retries that can be made for a single call, with an exponential backoff between each attempt. Defaults to 6."
772
- },
773
- "onFailedAttempt": {
774
- "$ref": "#/definitions/FailedAttemptHandler",
775
- "description": "Custom handler to handle failed attempts. Takes the originally thrown error object as input, and should itself throw an error if the input error is not retryable."
776
- },
777
- "callbackManager": {
778
- "$ref": "#/definitions/CallbackManager",
779
- "deprecated": "Use `callbacks` instead"
780
- },
781
- "cache": {
782
- "anyOf": [
783
- {
784
- "$ref": "#/definitions/BaseCache"
785
- },
786
- {
787
- "type": "boolean"
788
- }
789
- ]
790
- },
791
- "concurrency": {
792
- "type": "number",
793
- "deprecated": "Use `maxConcurrency` instead"
794
- },
795
- "embeddingOnly": {
796
- "type": "boolean"
797
- },
798
- "f16KV": {
799
- "type": "boolean"
800
- },
801
- "frequencyPenalty": {
802
- "type": "number"
803
- },
804
- "headers": {
805
- "type": "object",
806
- "additionalProperties": {
807
- "type": "string"
808
- }
809
- },
810
- "keepAlive": {
811
- "type": "string"
812
- },
813
- "logitsAll": {
814
- "type": "boolean"
815
- },
816
- "lowVram": {
817
- "type": "boolean"
818
- },
819
- "mainGpu": {
820
- "type": "number"
821
- },
822
- "model": {
823
- "type": "string"
824
- },
825
- "baseUrl": {
826
- "type": "string"
827
- },
828
- "mirostat": {
829
- "type": "number"
830
- },
831
- "mirostatEta": {
832
- "type": "number"
833
- },
834
- "mirostatTau": {
835
- "type": "number"
836
- },
837
- "numBatch": {
838
- "type": "number"
839
- },
840
- "numCtx": {
841
- "type": "number"
842
- },
843
- "numGpu": {
844
- "type": "number"
845
- },
846
- "numGqa": {
847
- "type": "number"
848
- },
849
- "numKeep": {
850
- "type": "number"
851
- },
852
- "numPredict": {
853
- "type": "number"
854
- },
855
- "numThread": {
856
- "type": "number"
857
- },
858
- "penalizeNewline": {
859
- "type": "boolean"
860
- },
861
- "presencePenalty": {
862
- "type": "number"
863
- },
864
- "repeatLastN": {
865
- "type": "number"
866
- },
867
- "repeatPenalty": {
868
- "type": "number"
869
- },
870
- "ropeFrequencyBase": {
871
- "type": "number"
872
- },
873
- "ropeFrequencyScale": {
874
- "type": "number"
875
- },
876
- "temperature": {
877
- "type": "number"
878
- },
879
- "stop": {
880
- "type": "array",
881
- "items": {
882
- "type": "string"
883
- }
884
- },
885
- "tfsZ": {
886
- "type": "number"
887
- },
888
- "topK": {
889
- "type": "number"
890
- },
891
- "topP": {
892
- "type": "number"
893
- },
894
- "typicalP": {
895
- "type": "number"
896
- },
897
- "useMLock": {
898
- "type": "boolean"
899
- },
900
- "useMMap": {
901
- "type": "boolean"
902
- },
903
- "vocabOnly": {
904
- "type": "boolean"
905
- },
906
- "format": {
907
- "$ref": "#/definitions/StringWithAutocomplete%3C%22json%22%3E"
908
- }
909
- }
565
+ "mode": {
566
+ "type": "string",
567
+ "enum": [
568
+ "stdout",
569
+ "interactive"
570
+ ],
571
+ "description": "The output destination for the generated result.\n- 'stdout': Prints the result to the standard output. This is the default behavior.\n- 'interactive': Provides an interactive prompt for editing the result & committing the changes.",
572
+ "default": "stdout"
910
573
  },
911
- "tokenLimit": {
912
- "type": "number",
913
- "description": "The maximum number of tokens per request.",
914
- "default": 2048
574
+ "openInEditor": {
575
+ "type": "boolean",
576
+ "description": "Open the commit message in an editor for editing before proceeding.",
577
+ "default": false
915
578
  },
916
- "temperature": {
917
- "type": "number",
918
- "description": "The temperature value controls the randomness of the generated output. Higher values (e.g., 0.8) make the output more random, while lower values (e.g., 0.2) make it more deterministic.",
919
- "default": 0.4
579
+ "prompt": {
580
+ "type": "string",
581
+ "description": "The prompt text used for generating results."
920
582
  },
921
- "maxConcurrent": {
922
- "type": "number",
923
- "description": "The maximum number of requests to make concurrently.",
924
- "default": 6
583
+ "summarizePrompt": {
584
+ "type": "string",
585
+ "description": "The prompt text used specifically for generating summaries of large files."
925
586
  },
926
- "authentication": {
927
- "anyOf": [
928
- {
929
- "type": "object",
930
- "properties": {
931
- "type": {
932
- "type": "string",
933
- "const": "None"
934
- },
935
- "credentials": {
936
- "not": {}
937
- }
938
- },
939
- "required": [
940
- "type"
941
- ],
942
- "additionalProperties": false
943
- },
944
- {
945
- "type": "object",
946
- "properties": {
947
- "type": {
948
- "type": "string",
949
- "const": "OAuth"
950
- },
951
- "credentials": {
952
- "type": "object",
953
- "properties": {
954
- "clientId": {
955
- "type": "string"
956
- },
957
- "clientSecret": {
958
- "type": "string"
959
- },
960
- "token": {
961
- "type": "string"
962
- }
963
- },
964
- "additionalProperties": false
965
- }
966
- },
967
- "required": [
968
- "type",
969
- "credentials"
970
- ],
971
- "additionalProperties": false
972
- },
973
- {
974
- "type": "object",
975
- "properties": {
976
- "type": {
977
- "type": "string",
978
- "const": "APIKey"
979
- },
980
- "credentials": {
981
- "type": "object",
982
- "properties": {
983
- "apiKey": {
984
- "type": "string"
985
- }
986
- },
987
- "required": [
988
- "apiKey"
989
- ],
990
- "additionalProperties": false
991
- }
992
- },
993
- "required": [
994
- "type",
995
- "credentials"
996
- ],
997
- "additionalProperties": false
998
- }
999
- ]
587
+ "ignoredFiles": {
588
+ "type": "array",
589
+ "items": {
590
+ "type": "string"
591
+ },
592
+ "description": "An array of file paths or patterns to be ignored during processing.\n\nNote: This is a list of patterns interpreted by the `minimatch` library.",
593
+ "examples": [
594
+ [
595
+ "package-lock.json",
596
+ "node_modules"
597
+ ]
598
+ ],
599
+ "default": "['package-lock.json', contents of .gitignore, contents of .ignore]"
1000
600
  },
1001
- "requestOptions": {
1002
- "type": "object",
1003
- "properties": {
1004
- "timeout": {
1005
- "type": "number"
1006
- },
1007
- "maxRetries": {
1008
- "type": "number"
1009
- }
601
+ "ignoredExtensions": {
602
+ "type": "array",
603
+ "items": {
604
+ "type": "string"
1010
605
  },
1011
- "additionalProperties": false
606
+ "description": "An array of file extensions to be ignored during processing.",
607
+ "default": [
608
+ ".map",
609
+ ".lock"
610
+ ]
611
+ },
612
+ "defaultBranch": {
613
+ "type": "string",
614
+ "description": "Default git branch for the repository.",
615
+ "default": "main"
1012
616
  }
1013
617
  },
1014
618
  "required": [
1015
- "authentication",
1016
- "endpoint",
1017
- "model",
1018
- "provider"
619
+ "defaultBranch",
620
+ "mode",
621
+ "service"
1019
622
  ]
1020
623
  },
1021
- "StringWithAutocomplete<\"json\">": {
624
+ "LLMService": {
1022
625
  "anyOf": [
1023
626
  {
1024
- "type": "string"
627
+ "$ref": "#/definitions/OpenAILLMService"
1025
628
  },
1026
629
  {
1027
- "type": "string",
1028
- "enum": [
1029
- "json"
1030
- ]
630
+ "$ref": "#/definitions/OllamaLLMService"
631
+ },
632
+ {
633
+ "$ref": "#/definitions/AnthropicLLMService"
1031
634
  }
1032
- ],
1033
- "description": "Represents a string value with autocompleted, but not required, suggestions."
635
+ ]
1034
636
  },
1035
- "AnthropicLLMService": {
637
+ "OpenAILLMService": {
1036
638
  "type": "object",
1037
639
  "additionalProperties": false,
1038
640
  "properties": {
@@ -1044,15 +646,145 @@ const schema$1 = {
1044
646
  },
1045
647
  "fields": {
1046
648
  "type": "object",
649
+ "additionalProperties": false,
1047
650
  "properties": {
651
+ "verbose": {
652
+ "type": "boolean"
653
+ },
654
+ "callbacks": {
655
+ "$ref": "#/definitions/Callbacks"
656
+ },
657
+ "tags": {
658
+ "type": "array",
659
+ "items": {
660
+ "type": "string"
661
+ }
662
+ },
663
+ "metadata": {
664
+ "type": "object",
665
+ "additionalProperties": {}
666
+ },
667
+ "maxConcurrency": {
668
+ "type": "number",
669
+ "description": "The maximum number of concurrent calls that can be made. Defaults to `Infinity`, which means no limit."
670
+ },
671
+ "maxRetries": {
672
+ "type": "number",
673
+ "description": "The maximum number of retries that can be made for a single call, with an exponential backoff between each attempt. Defaults to 6."
674
+ },
675
+ "onFailedAttempt": {
676
+ "$ref": "#/definitions/FailedAttemptHandler",
677
+ "description": "Custom handler to handle failed attempts. Takes the originally thrown error object as input, and should itself throw an error if the input error is not retryable."
678
+ },
679
+ "callbackManager": {
680
+ "$ref": "#/definitions/CallbackManager",
681
+ "deprecated": "Use `callbacks` instead"
682
+ },
683
+ "cache": {
684
+ "anyOf": [
685
+ {
686
+ "$ref": "#/definitions/BaseCache"
687
+ },
688
+ {
689
+ "type": "boolean"
690
+ }
691
+ ]
692
+ },
693
+ "concurrency": {
694
+ "type": "number",
695
+ "deprecated": "Use `maxConcurrency` instead"
696
+ },
697
+ "bestOf": {
698
+ "type": "number",
699
+ "description": "Generates `bestOf` completions server side and returns the \"best\""
700
+ },
701
+ "batchSize": {
702
+ "type": "number",
703
+ "description": "Batch size to use when passing multiple documents to generate"
704
+ },
1048
705
  "temperature": {
1049
- "type": "number"
706
+ "type": "number",
707
+ "description": "Sampling temperature to use"
708
+ },
709
+ "maxTokens": {
710
+ "type": "number",
711
+ "description": "Maximum number of tokens to generate in the completion. -1 returns as many tokens as possible given the prompt and the model's maximum context size."
712
+ },
713
+ "topP": {
714
+ "type": "number",
715
+ "description": "Total probability mass of tokens to consider at each step"
716
+ },
717
+ "frequencyPenalty": {
718
+ "type": "number",
719
+ "description": "Penalizes repeated tokens according to frequency"
720
+ },
721
+ "presencePenalty": {
722
+ "type": "number",
723
+ "description": "Penalizes repeated tokens"
724
+ },
725
+ "n": {
726
+ "type": "number",
727
+ "description": "Number of completions to generate for each prompt"
728
+ },
729
+ "logitBias": {
730
+ "type": "object",
731
+ "additionalProperties": {
732
+ "type": "number"
733
+ },
734
+ "description": "Dictionary used to adjust the probability of specific tokens being generated"
735
+ },
736
+ "user": {
737
+ "type": "string",
738
+ "description": "Unique string identifier representing your end-user, which can help OpenAI to monitor and detect abuse."
739
+ },
740
+ "streaming": {
741
+ "type": "boolean",
742
+ "description": "Whether to stream the results or not. Enabling disables tokenUsage reporting"
743
+ },
744
+ "streamUsage": {
745
+ "type": "boolean",
746
+ "description": "Whether or not to include token usage data in streamed chunks.",
747
+ "default": true
1050
748
  },
1051
- "maxTokens": {
1052
- "type": "number"
749
+ "modelName": {
750
+ "type": "string",
751
+ "description": "Model name to use Alias for `model`"
752
+ },
753
+ "model": {
754
+ "type": "string",
755
+ "description": "Model name to use"
756
+ },
757
+ "modelKwargs": {
758
+ "type": "object",
759
+ "description": "Holds any additional parameters that are valid to pass to {@link * https://platform.openai.com/docs/api-reference/completions/create | } * `openai.createCompletion`} that are not explicitly specified on this class."
760
+ },
761
+ "stop": {
762
+ "type": "array",
763
+ "items": {
764
+ "type": "string"
765
+ },
766
+ "description": "List of stop words to use when generating Alias for `stopSequences`"
767
+ },
768
+ "stopSequences": {
769
+ "type": "array",
770
+ "items": {
771
+ "type": "string"
772
+ },
773
+ "description": "List of stop words to use when generating"
774
+ },
775
+ "timeout": {
776
+ "type": "number",
777
+ "description": "Timeout to use when making requests to OpenAI."
778
+ },
779
+ "openAIApiKey": {
780
+ "type": "string",
781
+ "description": "API key to use when making requests to OpenAI. Defaults to the value of `OPENAI_API_KEY` environment variable. Alias for `apiKey`"
782
+ },
783
+ "apiKey": {
784
+ "type": "string",
785
+ "description": "API key to use when making requests to OpenAI. Defaults to the value of `OPENAI_API_KEY` environment variable."
1053
786
  }
1054
- },
1055
- "additionalProperties": false
787
+ }
1056
788
  },
1057
789
  "tokenLimit": {
1058
790
  "type": "number",
@@ -1144,498 +876,769 @@ const schema$1 = {
1144
876
  }
1145
877
  ]
1146
878
  },
1147
- "requestOptions": {
879
+ "requestOptions": {
880
+ "type": "object",
881
+ "properties": {
882
+ "timeout": {
883
+ "type": "number"
884
+ },
885
+ "maxRetries": {
886
+ "type": "number"
887
+ }
888
+ },
889
+ "additionalProperties": false
890
+ }
891
+ },
892
+ "required": [
893
+ "authentication",
894
+ "model",
895
+ "provider"
896
+ ]
897
+ },
898
+ "LLMProvider": {
899
+ "type": "string",
900
+ "enum": [
901
+ "openai",
902
+ "ollama",
903
+ "anthropic"
904
+ ]
905
+ },
906
+ "LLMModel": {
907
+ "anyOf": [
908
+ {
909
+ "type": "string",
910
+ "enum": [
911
+ "davinci-002",
912
+ "babbage-002",
913
+ "text-davinci-003",
914
+ "text-davinci-002",
915
+ "text-davinci-001",
916
+ "text-curie-001",
917
+ "text-babbage-001",
918
+ "text-ada-001",
919
+ "davinci",
920
+ "curie",
921
+ "babbage",
922
+ "ada",
923
+ "code-davinci-002",
924
+ "code-davinci-001",
925
+ "code-cushman-002",
926
+ "code-cushman-001",
927
+ "davinci-codex",
928
+ "cushman-codex",
929
+ "text-davinci-edit-001",
930
+ "code-davinci-edit-001",
931
+ "text-embedding-ada-002",
932
+ "text-similarity-davinci-001",
933
+ "text-similarity-curie-001",
934
+ "text-similarity-babbage-001",
935
+ "text-similarity-ada-001",
936
+ "text-search-davinci-doc-001",
937
+ "text-search-curie-doc-001",
938
+ "text-search-babbage-doc-001",
939
+ "text-search-ada-doc-001",
940
+ "code-search-babbage-code-001",
941
+ "code-search-ada-code-001",
942
+ "gpt2",
943
+ "gpt-3.5-turbo",
944
+ "gpt-35-turbo",
945
+ "gpt-3.5-turbo-0301",
946
+ "gpt-3.5-turbo-0613",
947
+ "gpt-3.5-turbo-1106",
948
+ "gpt-3.5-turbo-0125",
949
+ "gpt-3.5-turbo-16k",
950
+ "gpt-3.5-turbo-16k-0613",
951
+ "gpt-3.5-turbo-instruct",
952
+ "gpt-3.5-turbo-instruct-0914",
953
+ "gpt-4",
954
+ "gpt-4-0314",
955
+ "gpt-4-0613",
956
+ "gpt-4-32k",
957
+ "gpt-4-32k-0314",
958
+ "gpt-4-32k-0613",
959
+ "gpt-4-turbo",
960
+ "gpt-4-turbo-2024-04-09",
961
+ "gpt-4-turbo-preview",
962
+ "gpt-4-1106-preview",
963
+ "gpt-4-0125-preview",
964
+ "gpt-4-vision-preview",
965
+ "gpt-4o",
966
+ "gpt-4o-2024-05-13"
967
+ ]
968
+ },
969
+ {
970
+ "$ref": "#/definitions/OllamaModel"
971
+ },
972
+ {
973
+ "$ref": "#/definitions/AnthropicModel"
974
+ }
975
+ ]
976
+ },
977
+ "OllamaModel": {
978
+ "type": "string",
979
+ "enum": [
980
+ "codegemma:2b",
981
+ "codegemma:7b-code",
982
+ "codegemma",
983
+ "codellama:13b",
984
+ "codellama:34b",
985
+ "codellama:70b",
986
+ "codellama:7b",
987
+ "codellama:instruct",
988
+ "codellama:latest",
989
+ "codellama",
990
+ "gemma:2b",
991
+ "gemma:7b",
992
+ "gemma:latest",
993
+ "gemma",
994
+ "llama2:13b",
995
+ "llama2:70b",
996
+ "llama2:chat",
997
+ "llama2:latest",
998
+ "llama2:text",
999
+ "llama2",
1000
+ "llama3:70b-text",
1001
+ "llama3:70b",
1002
+ "llama3:latest",
1003
+ "llama3:text",
1004
+ "llama3.1:70b",
1005
+ "llama3.1:8b",
1006
+ "llama3.1:latest",
1007
+ "llama3.2",
1008
+ "llama3.2:latest",
1009
+ "llama3.2:1b",
1010
+ "llama3.2:3b",
1011
+ "llama3.2:1b-instruct-fp16",
1012
+ "llama3.2:1b-instruct-q3_K_M",
1013
+ "llama3",
1014
+ "mistral:7b",
1015
+ "mistral:latest",
1016
+ "mistral:text",
1017
+ "mistral",
1018
+ "phi3:14b",
1019
+ "phi3:3.8b",
1020
+ "phi3:instruct",
1021
+ "phi3:medium-128k",
1022
+ "phi3:medium-4k",
1023
+ "phi3:medium",
1024
+ "phi3",
1025
+ "qwen2:0.5b",
1026
+ "qwen2:1.5b",
1027
+ "qwen2:72b-text",
1028
+ "qwen2:72b",
1029
+ "qwen2"
1030
+ ]
1031
+ },
1032
+ "AnthropicModel": {
1033
+ "type": "string",
1034
+ "enum": [
1035
+ "claude-3-5-sonnet-20240620",
1036
+ "claude-3-opus-20240229",
1037
+ "claude-3-sonnet-20240229",
1038
+ "claude-3-haiku-20240307",
1039
+ "claude-2.1",
1040
+ "claude-2.0"
1041
+ ]
1042
+ },
1043
+ "Callbacks": {
1044
+ "anyOf": [
1045
+ {
1046
+ "$ref": "#/definitions/CallbackManager"
1047
+ },
1048
+ {
1049
+ "type": "array",
1050
+ "items": {
1051
+ "anyOf": [
1052
+ {
1053
+ "$ref": "#/definitions/BaseCallbackHandler"
1054
+ },
1055
+ {
1056
+ "$ref": "#/definitions/CallbackHandlerMethods"
1057
+ }
1058
+ ]
1059
+ }
1060
+ }
1061
+ ]
1062
+ },
1063
+ "CallbackManager": {
1064
+ "type": "object",
1065
+ "properties": {
1066
+ "handlers": {
1067
+ "type": "array",
1068
+ "items": {
1069
+ "$ref": "#/definitions/BaseCallbackHandler"
1070
+ }
1071
+ },
1072
+ "inheritableHandlers": {
1073
+ "type": "array",
1074
+ "items": {
1075
+ "$ref": "#/definitions/BaseCallbackHandler"
1076
+ }
1077
+ },
1078
+ "tags": {
1079
+ "type": "array",
1080
+ "items": {
1081
+ "type": "string"
1082
+ }
1083
+ },
1084
+ "inheritableTags": {
1085
+ "type": "array",
1086
+ "items": {
1087
+ "type": "string"
1088
+ }
1089
+ },
1090
+ "metadata": {
1148
1091
  "type": "object",
1149
- "properties": {
1150
- "timeout": {
1151
- "type": "number"
1152
- },
1153
- "maxRetries": {
1154
- "type": "number"
1155
- }
1156
- },
1157
- "additionalProperties": false
1092
+ "additionalProperties": {}
1093
+ },
1094
+ "inheritableMetadata": {
1095
+ "type": "object",
1096
+ "additionalProperties": {}
1097
+ },
1098
+ "name": {
1099
+ "type": "string"
1100
+ },
1101
+ "_parentRunId": {
1102
+ "type": "string"
1158
1103
  }
1159
1104
  },
1160
1105
  "required": [
1161
- "authentication",
1162
- "model",
1163
- "provider"
1164
- ]
1165
- }
1166
- }
1167
- };
1168
-
1169
- const isInteractive = (config) => {
1170
- return config?.mode === 'interactive' || !!config?.interactive;
1171
- };
1172
- const SEPERATOR = chalk.blue('─────────────');
1173
- const DIVIDER = chalk.dim(`\\`);
1174
- const LOGO = chalk.green(`┌──────┐
1175
- │┏┏┓┏┏┓│
1176
- │┗┗┛┗┗┛│
1177
- └──────┘`);
1178
- chalk.green(`┌────┐
1179
- │coco│
1180
- └────┘`);
1181
- const bannerWithHeader = (banner) => {
1182
- return chalk.green(`┌────┐
1183
- │coco│ ${banner}
1184
- └────┘`);
1185
- };
1186
- const USAGE_BANNER = chalk.green(`${LOGO}
1187
- ${chalk.bgGreen(`\xa0v${BUILD_VERSION}\xa0`)}
1188
- `);
1189
- const getCommandUsageHeader = (command) => {
1190
- return chalk.green(`${USAGE_BANNER}\n${chalk.white('Command:')}\n\xa0\xa0\xa0\xa0\xa0 $0 ${chalk.greenBright(command)} [options]`);
1191
- };
1192
- const CONFIG_ALREADY_EXISTS = (path) => {
1193
- return `existing config found in '${path}', do you want to override it?`;
1194
- };
1195
- const severityColors = [
1196
- chalk.greenBright, // 1
1197
- chalk.green, // 2
1198
- chalk.cyan, // 3
1199
- chalk.yellowBright, // 4
1200
- chalk.yellow, // 5
1201
- chalk.bgYellow, // 6
1202
- chalk.red, // 7
1203
- chalk.redBright, // 8
1204
- chalk.bgRed, // 9
1205
- chalk.bgRedBright, // 10
1206
- ];
1207
- const severityColor = (severity) => {
1208
- return severityColors[Math.min(severity - 1, severityColors.length - 1)];
1209
- };
1210
- const statusColor = (status) => {
1211
- switch (status) {
1212
- case 'completed':
1213
- return chalk.green;
1214
- case 'skipped':
1215
- return chalk.yellow;
1216
- case 'omitted':
1217
- return chalk.red;
1218
- default:
1219
- return chalk.blue;
1220
- }
1221
- };
1222
- const hotKey = (key) => chalk.dim(`(${key})`);
1223
-
1224
- /**
1225
- * Returns a new object with all undefined keys removed
1226
- *
1227
- * @param obj Object to remove undefined keys from
1228
- * @returns
1229
- */
1230
- function removeUndefined(obj) {
1231
- return Object.fromEntries(Object.entries(obj).filter(([, value]) => value !== undefined));
1232
- }
1233
-
1234
- async function updateFileSection({ filePath, startComment, endComment, getNewContent, confirmUpdate = true, confirmMessage = (path) => `A section already exists in ${path}, do you want to override it?`, }) {
1235
- const lines = fs__default.existsSync(filePath) ? fs__default.readFileSync(filePath, 'utf-8').split(/\r?\n/) : [];
1236
- const newLines = [];
1237
- let foundSection = false;
1238
- for (let i = 0; i < lines.length; i++) {
1239
- if (lines[i].trim() === startComment) {
1240
- foundSection = true;
1241
- if (confirmUpdate) {
1242
- const confirmOverwrite = await confirm({
1243
- message: typeof confirmMessage === 'function' ? confirmMessage(filePath) : confirmMessage,
1244
- default: false,
1245
- });
1246
- if (!confirmOverwrite) {
1247
- // keep all lines until the end comment
1248
- while (i < lines.length && lines[i].trim() !== endComment) {
1249
- newLines.push(lines[i]);
1250
- i++;
1251
- }
1252
- newLines.push(endComment);
1253
- continue;
1254
- }
1255
- }
1256
- newLines.push(startComment);
1257
- // Insert the new content
1258
- const newContent = await getNewContent();
1259
- newLines.push(newContent);
1260
- // Skip the existing content of the section
1261
- while (i < lines.length && lines[i].trim() !== endComment) {
1262
- i++;
1263
- }
1264
- newLines.push(endComment);
1265
- continue;
1266
- }
1267
- if (!foundSection || lines[i].trim() !== endComment) {
1268
- newLines.push(lines[i]);
1269
- }
1270
- }
1271
- // If section wasn't found, append it at the end
1272
- if (!foundSection) {
1273
- newLines.push('\n' + startComment);
1274
- const newContent = await getNewContent();
1275
- newLines.push(newContent);
1276
- newLines.push(endComment);
1277
- }
1278
- // Write the updated contents back to the file
1279
- fs__default.writeFileSync(filePath, newLines.join('\n'));
1280
- }
1281
-
1282
- const template$5 = `GOAL: Use functional abstractions to summarize the following text
1283
-
1284
- RULES: Avoid phrases like "this change", "this code", or "this function" etc. Instead refer to the function, variable, or class by name.
1285
-
1286
- TEXT:"""{text}"""
1287
- `;
1288
- const inputVariables$4 = ['text'];
1289
- const SUMMARIZE_PROMPT = new PromptTemplate({
1290
- inputVariables: inputVariables$4,
1291
- template: template$5,
1292
- });
1293
-
1294
- /**
1295
- * Retrieves the provider and model from the given configuration object.
1296
- * @param config The configuration object.
1297
- * @returns An object containing the provider and model.
1298
- * @throws Error if the configuration is invalid or missing required properties.
1299
- */
1300
- function getModelAndProviderFromConfig(config) {
1301
- if (!config.service) {
1302
- throw new Error('Invalid service: undefined');
1303
- }
1304
- const { provider, model } = config.service;
1305
- if (!model || !provider) {
1306
- throw new Error(`Invalid service: ${config.service}`);
1307
- }
1308
- return { provider, model };
1309
- }
1310
- /**
1311
- * Retrieve appropriate API key based on selected model
1312
- * @param service
1313
- * @param options
1314
- * @returns API Key
1315
- */
1316
- function getApiKeyForModel(config) {
1317
- const { provider } = getModelAndProviderFromConfig(config);
1318
- switch (provider) {
1319
- case 'openai':
1320
- return getDefaultServiceApiKey(config);
1321
- default:
1322
- return getDefaultServiceApiKey(config);
1323
- }
1324
- }
1325
- /**
1326
- * Retrieves the default service API key from the given configuration.
1327
- * @param config The configuration object.
1328
- * @returns The default service API key.
1329
- */
1330
- function getDefaultServiceApiKey(config) {
1331
- const service = config.service;
1332
- if (service.authentication.type === 'APIKey') {
1333
- return service.authentication.credentials?.apiKey;
1334
- }
1335
- else if (service.authentication.type === 'OAuth') {
1336
- return service.authentication.credentials?.token;
1337
- }
1338
- return '';
1339
- }
1340
- const DEFAULT_OPENAI_LLM_SERVICE = {
1341
- provider: 'openai',
1342
- model: 'gpt-4',
1343
- tokenLimit: 2024,
1344
- temperature: 0.32,
1345
- authentication: {
1346
- type: 'APIKey',
1347
- credentials: {
1348
- apiKey: '',
1106
+ "handlers",
1107
+ "inheritableHandlers",
1108
+ "tags",
1109
+ "inheritableTags",
1110
+ "metadata",
1111
+ "inheritableMetadata",
1112
+ "name"
1113
+ ],
1114
+ "additionalProperties": false
1115
+ },
1116
+ "BaseCallbackHandler": {
1117
+ "type": "object",
1118
+ "properties": {
1119
+ "lc_serializable": {
1120
+ "type": "boolean"
1121
+ },
1122
+ "lc_kwargs": {
1123
+ "$ref": "#/definitions/SerializedFields"
1124
+ },
1125
+ "lc_namespace": {
1126
+ "type": "array",
1127
+ "items": {
1128
+ "type": "string"
1129
+ },
1130
+ "description": "A path to the module that contains the class, eg. [\"langchain\", \"llms\"] Usually should be the same as the entrypoint the class is exported from."
1131
+ },
1132
+ "ignoreLLM": {
1133
+ "type": "boolean"
1134
+ },
1135
+ "ignoreChain": {
1136
+ "type": "boolean"
1137
+ },
1138
+ "ignoreAgent": {
1139
+ "type": "boolean"
1140
+ },
1141
+ "ignoreRetriever": {
1142
+ "type": "boolean"
1143
+ },
1144
+ "ignoreCustomEvent": {
1145
+ "type": "boolean"
1146
+ },
1147
+ "_awaitHandler": {
1148
+ "type": "boolean"
1149
+ },
1150
+ "raiseError": {
1151
+ "type": "boolean"
1152
+ },
1153
+ "name": {
1154
+ "type": "string"
1155
+ },
1156
+ "awaitHandlers": {
1157
+ "type": "boolean"
1158
+ }
1159
+ },
1160
+ "required": [
1161
+ "awaitHandlers",
1162
+ "ignoreAgent",
1163
+ "ignoreChain",
1164
+ "ignoreCustomEvent",
1165
+ "ignoreLLM",
1166
+ "ignoreRetriever",
1167
+ "lc_kwargs",
1168
+ "lc_namespace",
1169
+ "lc_serializable",
1170
+ "name",
1171
+ "raiseError"
1172
+ ],
1173
+ "additionalProperties": false,
1174
+ "description": "Abstract base class for creating callback handlers in the LangChain framework. It provides a set of optional methods that can be overridden in derived classes to handle various events during the execution of a LangChain application."
1349
1175
  },
1350
- },
1351
- };
1352
- const DEFAULT_OLLAMA_LLM_SERVICE = {
1353
- provider: 'ollama',
1354
- model: 'llama3',
1355
- endpoint: 'http://localhost:11434',
1356
- maxConcurrent: 1,
1357
- tokenLimit: 2024,
1358
- temperature: 0.4,
1359
- authentication: {
1360
- type: 'None',
1361
- credentials: undefined,
1362
- },
1363
- };
1364
- /**
1365
- * Retrieves the default service configuration based on the provided alias and optional model.
1366
- * @param provider - The alias of the service.
1367
- * @param model - The optional model to be used.
1368
- * @returns The default service configuration.
1369
- * @throws Error if the alias is invalid or undefined.
1370
- */
1371
- function getDefaultServiceConfigFromAlias(provider, model) {
1372
- switch (provider) {
1373
- case 'anthropic':
1374
- return {
1375
- ...DEFAULT_OPENAI_LLM_SERVICE,
1376
- model: model || DEFAULT_OPENAI_LLM_SERVICE.model,
1377
- };
1378
- case 'ollama':
1379
- return {
1380
- ...DEFAULT_OLLAMA_LLM_SERVICE,
1381
- model: model || DEFAULT_OLLAMA_LLM_SERVICE.model,
1382
- };
1383
- case 'openai':
1384
- default:
1385
- return {
1386
- ...DEFAULT_OPENAI_LLM_SERVICE,
1387
- model: model || DEFAULT_OPENAI_LLM_SERVICE.model,
1388
- };
1389
- }
1390
- }
1391
-
1392
- const DEFAULT_IGNORED_FILES = ['package-lock.json', 'yarn.lock', 'node_modules'];
1393
- const DEFAULT_IGNORED_EXTENSIONS = ['.map', '.lock'];
1394
- const COCO_CONFIG_START_COMMENT = '# -- start coco config --';
1395
- const COCO_CONFIG_END_COMMENT = '# -- end coco config --';
1396
- /**
1397
- * Default Config
1398
- *
1399
- * @type {Config}
1400
- */
1401
- const DEFAULT_CONFIG$1 = {
1402
- mode: 'stdout',
1403
- verbose: false,
1404
- defaultBranch: 'main',
1405
- service: getDefaultServiceConfigFromAlias('openai'),
1406
- summarizePrompt: SUMMARIZE_PROMPT.template,
1407
- ignoredFiles: DEFAULT_IGNORED_FILES,
1408
- ignoredExtensions: DEFAULT_IGNORED_EXTENSIONS,
1409
- };
1410
- /**
1411
- * Create a named export of all config keys for use in other modules.
1412
- *
1413
- * @see Used in `src/lib/config/services/env.ts` to validate all env vars.
1414
- *
1415
- * @type {string[]}
1416
- */
1417
- const CONFIG_KEYS = Object.keys({
1418
- ...DEFAULT_CONFIG$1,
1419
- prompt: '',
1420
- });
1421
-
1422
- /**
1423
- * Load environment variables
1424
- *
1425
- * @param {Config} config
1426
- * @returns {Config} Updated config
1427
- **/
1428
- function loadEnvConfig(config) {
1429
- const envConfig = {};
1430
- const envKeys = [...CONFIG_KEYS, 'COCO_SERVICE_PROVIDER', 'COCO_SERVICE_MODEL', 'OPEN_AI_KEY'];
1431
- envKeys.forEach((key) => {
1432
- const envVarName = toEnvVarName(key);
1433
- const envValue = parseEnvValue(key, process.env[envVarName]);
1434
- if (envValue === undefined) {
1435
- return;
1436
- }
1437
- if (key === 'COCO_SERVICE_PROVIDER' || key === 'COCO_SERVICE_MODEL' || key === 'OPEN_AI_KEY') {
1438
- // NOTE: We want to ensure that the service object is always defined
1439
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
1440
- // @ts-ignore
1441
- envConfig.service = envConfig.service || {};
1442
- handleServiceEnvVar(envConfig.service, key, envValue);
1443
- }
1444
- else {
1445
- if (key === 'service' || !envValue) {
1446
- return;
1447
- }
1448
- envConfig[key] = envValue;
1449
- }
1450
- });
1451
- return { ...config, ...removeUndefined(envConfig) };
1452
- }
1453
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
1454
- function handleServiceEnvVar(service, key, value) {
1455
- switch (key) {
1456
- case 'COCO_SERVICE_PROVIDER':
1457
- service.provider = value;
1458
- break;
1459
- case 'COCO_SERVICE_MODEL':
1460
- service.model = value;
1461
- break;
1462
- case 'OPEN_AI_KEY':
1463
- if (service.provider === 'openai') {
1464
- service.fields = { apiKey: value };
1465
- }
1466
- break;
1467
- }
1468
- }
1469
- function parseEnvValue(key, value) {
1470
- switch (true) {
1471
- // Handle undefined values
1472
- case value === undefined:
1473
- return undefined;
1474
- // Handle comma separated strings for ignoredFiles and ignoredExtensions arrays
1475
- case (key === 'ignoredFiles' || key === 'ignoredExtensions') &&
1476
- typeof value === 'string' &&
1477
- value.includes(','):
1478
- return value.split(',');
1479
- // Handle boolean values
1480
- case typeof value === 'string' && (value === 'false' || value === 'true'):
1481
- return value === 'true';
1482
- // Handle number values
1483
- case typeof value === 'string' && !isNaN(Number(value)):
1484
- return Number(value);
1485
- default:
1486
- return value;
1487
- }
1488
- }
1489
- function toEnvVarName(key) {
1490
- if (key === 'service') {
1491
- return key;
1492
- }
1493
- if (key.includes('COCO_')) {
1494
- return key;
1495
- }
1496
- return `COCO_${key.replace(/([A-Z])/g, '_$1').toLocaleUpperCase()}`;
1497
- }
1498
- function formatEnvValue(value) {
1499
- if (typeof value === 'number') {
1500
- return `${value}`;
1501
- }
1502
- else if (Array.isArray(value)) {
1503
- return `${value.join(',')}`;
1504
- }
1505
- else if (typeof value === 'string') {
1506
- // Escape newlines and tabs in strings
1507
- return `${value.replace(/\n/g, '\\n').replace(/\t/g, '\\t')}`;
1508
- }
1509
- return `${value}`;
1510
- }
1511
- const appendToEnvFile = async (filePath, config) => {
1512
- const getNewContent = async () => {
1513
- return Object.entries(config)
1514
- .map(([key, value]) => {
1515
- if (key === 'service') {
1516
- const service = value;
1517
- return `${service.provider ? `COCO_SERVICE_PROVIDER=${service.provider}` : ''}\n${service.model ? `COCO_SERVICE_MODEL=${service.model}` : ''}\n${service.authentication.type === 'APIKey'
1518
- ? `OPEN_AI_KEY=${service.authentication.credentials.apiKey}`
1519
- : ''}`;
1176
+ "SerializedFields": {
1177
+ "type": "object"
1178
+ },
1179
+ "CallbackHandlerMethods": {
1180
+ "type": "object",
1181
+ "additionalProperties": false,
1182
+ "description": "Base interface for callbacks. All methods are optional. If a method is not implemented, it will be ignored. If a method is implemented, it will be called at the appropriate time. All methods are called with the run ID of the LLM/ChatModel/Chain that is running, which is generated by the CallbackManager."
1183
+ },
1184
+ "FailedAttemptHandler": {
1185
+ "$comment": "(error: any) => any",
1186
+ "type": "object",
1187
+ "properties": {
1188
+ "namedArgs": {
1189
+ "type": "object",
1190
+ "properties": {
1191
+ "error": {}
1192
+ },
1193
+ "required": [
1194
+ "error"
1195
+ ],
1196
+ "additionalProperties": false
1197
+ }
1520
1198
  }
1521
- const envVarName = toEnvVarName(key);
1522
- const envValue = formatEnvValue(value);
1523
- return `${envVarName}=${envValue}`;
1524
- })
1525
- .join('\n');
1526
- };
1527
- await updateFileSection({
1528
- filePath,
1529
- startComment: COCO_CONFIG_START_COMMENT,
1530
- endComment: COCO_CONFIG_END_COMMENT,
1531
- getNewContent,
1532
- confirmMessage: CONFIG_ALREADY_EXISTS,
1533
- });
1534
- };
1535
-
1536
- /**
1537
- * Load git profile config (from ~/.gitconfig)
1538
- *
1539
- * @param {Config} config
1540
- * @returns {Config} Updated config
1541
- **/
1542
- function loadGitConfig(config) {
1543
- const gitConfigPath = path.join(os.homedir(), '.gitconfig');
1544
- if (fs.existsSync(gitConfigPath)) {
1545
- const gitConfigRaw = fs.readFileSync(gitConfigPath, 'utf-8');
1546
- const gitConfigParsed = ini.parse(gitConfigRaw);
1547
- const gitConfigServiceObject = gitConfigParsed.coco?.service;
1548
- let service = config.service;
1549
- if (gitConfigServiceObject) {
1550
- const gitServiceConfig = JSON.parse(gitConfigServiceObject);
1551
- service = gitServiceConfig || config?.service;
1199
+ },
1200
+ "BaseCache": {
1201
+ "type": "object",
1202
+ "additionalProperties": false,
1203
+ "description": "Base class for all caches. All caches should extend this class."
1204
+ },
1205
+ "OllamaLLMService": {
1206
+ "type": "object",
1207
+ "additionalProperties": false,
1208
+ "properties": {
1209
+ "provider": {
1210
+ "$ref": "#/definitions/LLMProvider"
1211
+ },
1212
+ "model": {
1213
+ "$ref": "#/definitions/LLMModel"
1214
+ },
1215
+ "endpoint": {
1216
+ "type": "string"
1217
+ },
1218
+ "fields": {
1219
+ "type": "object",
1220
+ "additionalProperties": false,
1221
+ "properties": {
1222
+ "verbose": {
1223
+ "type": "boolean"
1224
+ },
1225
+ "callbacks": {
1226
+ "$ref": "#/definitions/Callbacks"
1227
+ },
1228
+ "tags": {
1229
+ "type": "array",
1230
+ "items": {
1231
+ "type": "string"
1232
+ }
1233
+ },
1234
+ "metadata": {
1235
+ "type": "object",
1236
+ "additionalProperties": {}
1237
+ },
1238
+ "maxConcurrency": {
1239
+ "type": "number",
1240
+ "description": "The maximum number of concurrent calls that can be made. Defaults to `Infinity`, which means no limit."
1241
+ },
1242
+ "maxRetries": {
1243
+ "type": "number",
1244
+ "description": "The maximum number of retries that can be made for a single call, with an exponential backoff between each attempt. Defaults to 6."
1245
+ },
1246
+ "onFailedAttempt": {
1247
+ "$ref": "#/definitions/FailedAttemptHandler",
1248
+ "description": "Custom handler to handle failed attempts. Takes the originally thrown error object as input, and should itself throw an error if the input error is not retryable."
1249
+ },
1250
+ "callbackManager": {
1251
+ "$ref": "#/definitions/CallbackManager",
1252
+ "deprecated": "Use `callbacks` instead"
1253
+ },
1254
+ "cache": {
1255
+ "anyOf": [
1256
+ {
1257
+ "$ref": "#/definitions/BaseCache"
1258
+ },
1259
+ {
1260
+ "type": "boolean"
1261
+ }
1262
+ ]
1263
+ },
1264
+ "concurrency": {
1265
+ "type": "number",
1266
+ "deprecated": "Use `maxConcurrency` instead"
1267
+ },
1268
+ "embeddingOnly": {
1269
+ "type": "boolean"
1270
+ },
1271
+ "f16KV": {
1272
+ "type": "boolean"
1273
+ },
1274
+ "frequencyPenalty": {
1275
+ "type": "number"
1276
+ },
1277
+ "headers": {
1278
+ "type": "object",
1279
+ "additionalProperties": {
1280
+ "type": "string"
1281
+ }
1282
+ },
1283
+ "keepAlive": {
1284
+ "type": "string"
1285
+ },
1286
+ "logitsAll": {
1287
+ "type": "boolean"
1288
+ },
1289
+ "lowVram": {
1290
+ "type": "boolean"
1291
+ },
1292
+ "mainGpu": {
1293
+ "type": "number"
1294
+ },
1295
+ "model": {
1296
+ "type": "string"
1297
+ },
1298
+ "baseUrl": {
1299
+ "type": "string"
1300
+ },
1301
+ "mirostat": {
1302
+ "type": "number"
1303
+ },
1304
+ "mirostatEta": {
1305
+ "type": "number"
1306
+ },
1307
+ "mirostatTau": {
1308
+ "type": "number"
1309
+ },
1310
+ "numBatch": {
1311
+ "type": "number"
1312
+ },
1313
+ "numCtx": {
1314
+ "type": "number"
1315
+ },
1316
+ "numGpu": {
1317
+ "type": "number"
1318
+ },
1319
+ "numGqa": {
1320
+ "type": "number"
1321
+ },
1322
+ "numKeep": {
1323
+ "type": "number"
1324
+ },
1325
+ "numPredict": {
1326
+ "type": "number"
1327
+ },
1328
+ "numThread": {
1329
+ "type": "number"
1330
+ },
1331
+ "penalizeNewline": {
1332
+ "type": "boolean"
1333
+ },
1334
+ "presencePenalty": {
1335
+ "type": "number"
1336
+ },
1337
+ "repeatLastN": {
1338
+ "type": "number"
1339
+ },
1340
+ "repeatPenalty": {
1341
+ "type": "number"
1342
+ },
1343
+ "ropeFrequencyBase": {
1344
+ "type": "number"
1345
+ },
1346
+ "ropeFrequencyScale": {
1347
+ "type": "number"
1348
+ },
1349
+ "temperature": {
1350
+ "type": "number"
1351
+ },
1352
+ "stop": {
1353
+ "type": "array",
1354
+ "items": {
1355
+ "type": "string"
1356
+ }
1357
+ },
1358
+ "tfsZ": {
1359
+ "type": "number"
1360
+ },
1361
+ "topK": {
1362
+ "type": "number"
1363
+ },
1364
+ "topP": {
1365
+ "type": "number"
1366
+ },
1367
+ "typicalP": {
1368
+ "type": "number"
1369
+ },
1370
+ "useMLock": {
1371
+ "type": "boolean"
1372
+ },
1373
+ "useMMap": {
1374
+ "type": "boolean"
1375
+ },
1376
+ "vocabOnly": {
1377
+ "type": "boolean"
1378
+ },
1379
+ "format": {
1380
+ "$ref": "#/definitions/StringWithAutocomplete%3C%22json%22%3E"
1381
+ }
1382
+ }
1383
+ },
1384
+ "tokenLimit": {
1385
+ "type": "number",
1386
+ "description": "The maximum number of tokens per request.",
1387
+ "default": 2048
1388
+ },
1389
+ "temperature": {
1390
+ "type": "number",
1391
+ "description": "The temperature value controls the randomness of the generated output. Higher values (e.g., 0.8) make the output more random, while lower values (e.g., 0.2) make it more deterministic.",
1392
+ "default": 0.4
1393
+ },
1394
+ "maxConcurrent": {
1395
+ "type": "number",
1396
+ "description": "The maximum number of requests to make concurrently.",
1397
+ "default": 6
1398
+ },
1399
+ "authentication": {
1400
+ "anyOf": [
1401
+ {
1402
+ "type": "object",
1403
+ "properties": {
1404
+ "type": {
1405
+ "type": "string",
1406
+ "const": "None"
1407
+ },
1408
+ "credentials": {
1409
+ "not": {}
1410
+ }
1411
+ },
1412
+ "required": [
1413
+ "type"
1414
+ ],
1415
+ "additionalProperties": false
1416
+ },
1417
+ {
1418
+ "type": "object",
1419
+ "properties": {
1420
+ "type": {
1421
+ "type": "string",
1422
+ "const": "OAuth"
1423
+ },
1424
+ "credentials": {
1425
+ "type": "object",
1426
+ "properties": {
1427
+ "clientId": {
1428
+ "type": "string"
1429
+ },
1430
+ "clientSecret": {
1431
+ "type": "string"
1432
+ },
1433
+ "token": {
1434
+ "type": "string"
1435
+ }
1436
+ },
1437
+ "additionalProperties": false
1438
+ }
1439
+ },
1440
+ "required": [
1441
+ "type",
1442
+ "credentials"
1443
+ ],
1444
+ "additionalProperties": false
1445
+ },
1446
+ {
1447
+ "type": "object",
1448
+ "properties": {
1449
+ "type": {
1450
+ "type": "string",
1451
+ "const": "APIKey"
1452
+ },
1453
+ "credentials": {
1454
+ "type": "object",
1455
+ "properties": {
1456
+ "apiKey": {
1457
+ "type": "string"
1458
+ }
1459
+ },
1460
+ "required": [
1461
+ "apiKey"
1462
+ ],
1463
+ "additionalProperties": false
1464
+ }
1465
+ },
1466
+ "required": [
1467
+ "type",
1468
+ "credentials"
1469
+ ],
1470
+ "additionalProperties": false
1471
+ }
1472
+ ]
1473
+ },
1474
+ "requestOptions": {
1475
+ "type": "object",
1476
+ "properties": {
1477
+ "timeout": {
1478
+ "type": "number"
1479
+ },
1480
+ "maxRetries": {
1481
+ "type": "number"
1482
+ }
1483
+ },
1484
+ "additionalProperties": false
1485
+ }
1486
+ },
1487
+ "required": [
1488
+ "authentication",
1489
+ "endpoint",
1490
+ "model",
1491
+ "provider"
1492
+ ]
1493
+ },
1494
+ "StringWithAutocomplete<\"json\">": {
1495
+ "anyOf": [
1496
+ {
1497
+ "type": "string"
1498
+ },
1499
+ {
1500
+ "type": "string",
1501
+ "enum": [
1502
+ "json"
1503
+ ]
1504
+ }
1505
+ ],
1506
+ "description": "Represents a string value with autocompleted, but not required, suggestions."
1507
+ },
1508
+ "AnthropicLLMService": {
1509
+ "type": "object",
1510
+ "additionalProperties": false,
1511
+ "properties": {
1512
+ "provider": {
1513
+ "$ref": "#/definitions/LLMProvider"
1514
+ },
1515
+ "model": {
1516
+ "$ref": "#/definitions/LLMModel"
1517
+ },
1518
+ "fields": {
1519
+ "type": "object",
1520
+ "properties": {
1521
+ "temperature": {
1522
+ "type": "number"
1523
+ },
1524
+ "maxTokens": {
1525
+ "type": "number"
1526
+ }
1527
+ },
1528
+ "additionalProperties": false
1529
+ },
1530
+ "tokenLimit": {
1531
+ "type": "number",
1532
+ "description": "The maximum number of tokens per request.",
1533
+ "default": 2048
1534
+ },
1535
+ "temperature": {
1536
+ "type": "number",
1537
+ "description": "The temperature value controls the randomness of the generated output. Higher values (e.g., 0.8) make the output more random, while lower values (e.g., 0.2) make it more deterministic.",
1538
+ "default": 0.4
1539
+ },
1540
+ "maxConcurrent": {
1541
+ "type": "number",
1542
+ "description": "The maximum number of requests to make concurrently.",
1543
+ "default": 6
1544
+ },
1545
+ "authentication": {
1546
+ "anyOf": [
1547
+ {
1548
+ "type": "object",
1549
+ "properties": {
1550
+ "type": {
1551
+ "type": "string",
1552
+ "const": "None"
1553
+ },
1554
+ "credentials": {
1555
+ "not": {}
1556
+ }
1557
+ },
1558
+ "required": [
1559
+ "type"
1560
+ ],
1561
+ "additionalProperties": false
1562
+ },
1563
+ {
1564
+ "type": "object",
1565
+ "properties": {
1566
+ "type": {
1567
+ "type": "string",
1568
+ "const": "OAuth"
1569
+ },
1570
+ "credentials": {
1571
+ "type": "object",
1572
+ "properties": {
1573
+ "clientId": {
1574
+ "type": "string"
1575
+ },
1576
+ "clientSecret": {
1577
+ "type": "string"
1578
+ },
1579
+ "token": {
1580
+ "type": "string"
1581
+ }
1582
+ },
1583
+ "additionalProperties": false
1584
+ }
1585
+ },
1586
+ "required": [
1587
+ "type",
1588
+ "credentials"
1589
+ ],
1590
+ "additionalProperties": false
1591
+ },
1592
+ {
1593
+ "type": "object",
1594
+ "properties": {
1595
+ "type": {
1596
+ "type": "string",
1597
+ "const": "APIKey"
1598
+ },
1599
+ "credentials": {
1600
+ "type": "object",
1601
+ "properties": {
1602
+ "apiKey": {
1603
+ "type": "string"
1604
+ }
1605
+ },
1606
+ "required": [
1607
+ "apiKey"
1608
+ ],
1609
+ "additionalProperties": false
1610
+ }
1611
+ },
1612
+ "required": [
1613
+ "type",
1614
+ "credentials"
1615
+ ],
1616
+ "additionalProperties": false
1617
+ }
1618
+ ]
1619
+ },
1620
+ "requestOptions": {
1621
+ "type": "object",
1622
+ "properties": {
1623
+ "timeout": {
1624
+ "type": "number"
1625
+ },
1626
+ "maxRetries": {
1627
+ "type": "number"
1628
+ }
1629
+ },
1630
+ "additionalProperties": false
1631
+ }
1632
+ },
1633
+ "required": [
1634
+ "authentication",
1635
+ "model",
1636
+ "provider"
1637
+ ]
1552
1638
  }
1553
- config = {
1554
- ...config,
1555
- service: service,
1556
- prompt: gitConfigParsed.coco?.prompt || config.prompt,
1557
- mode: gitConfigParsed.coco?.mode || config.mode,
1558
- summarizePrompt: gitConfigParsed.coco?.summarizePrompt || config.summarizePrompt,
1559
- ignoredFiles: gitConfigParsed.coco?.ignoredFiles || config.ignoredFiles,
1560
- ignoredExtensions: gitConfigParsed.coco?.ignoredExtensions || config.ignoredExtensions,
1561
- defaultBranch: gitConfigParsed.coco?.defaultBranch || config.defaultBranch,
1562
- verbose: gitConfigParsed.coco?.verbose || config.verbose,
1563
- };
1564
- }
1565
- return removeUndefined(config);
1566
- }
1567
- /**
1568
- * Appends the provided configuration to a git config file.
1569
- *
1570
- * @param filePath - The path to the .gitconfig
1571
- * @param config - The configuration object to append.
1572
- */
1573
- const appendToGitConfig = async (filePath, config) => {
1574
- if (!fs.existsSync(filePath)) {
1575
- throw new Error(`File ${filePath} does not exist.`);
1576
1639
  }
1577
- const header = '[coco]';
1578
- const getNewContent = async () => {
1579
- const contentLines = [header];
1580
- for (const key in config) {
1581
- const value = config[key];
1582
- if (typeof value === 'object') {
1583
- // Serialize object to JSON string
1584
- contentLines.push(`\t${key} = ${JSON.stringify(value)}`);
1585
- }
1586
- else if (typeof value === 'string' && value.includes('\n')) {
1587
- // Wrap strings with new lines in quotes
1588
- contentLines.push(`\t${key} = ${JSON.stringify(value)}`);
1589
- }
1590
- else {
1591
- contentLines.push(`\t${key} = ${value}`);
1592
- }
1593
- }
1594
- return contentLines.join('\n');
1595
- };
1596
- await updateFileSection({
1597
- filePath,
1598
- startComment: COCO_CONFIG_START_COMMENT,
1599
- endComment: COCO_CONFIG_END_COMMENT,
1600
- getNewContent,
1601
- confirmUpdate: true,
1602
- confirmMessage: CONFIG_ALREADY_EXISTS,
1603
- });
1604
1640
  };
1605
1641
 
1606
- /**
1607
- * Load .gitignore in project root
1608
- *
1609
- * @param {Config} config
1610
- * @returns
1611
- */
1612
- function loadGitignore(config) {
1613
- if (fs.existsSync('.gitignore')) {
1614
- const gitignoreContent = fs.readFileSync('.gitignore', 'utf-8');
1615
- config.ignoredFiles = [
1616
- ...(config?.ignoredFiles || []),
1617
- ...gitignoreContent.split('\n').filter((line) => line.trim() !== '' && !line.startsWith('#')),
1618
- ];
1619
- }
1620
- return config;
1621
- }
1622
- /**
1623
- * Load .ignore in project root
1624
- *
1625
- * @param {Config} config
1626
- * @returns
1627
- */
1628
- function loadIgnore(config) {
1629
- if (fs.existsSync('.ignore')) {
1630
- const ignoreContent = fs.readFileSync('.ignore', 'utf-8');
1631
- config.ignoredFiles = [
1632
- ...(config?.ignoredFiles || []),
1633
- ...ignoreContent.split('\n').filter((line) => line.trim() !== '' && !line.startsWith('#')),
1634
- ];
1635
- }
1636
- return config;
1637
- }
1638
-
1639
1642
  const ajv = new Ajv({
1640
1643
  allErrors: true,
1641
1644
  verbose: true,
@@ -2275,8 +2278,11 @@ async function handleResult({ result, mode, interactiveModeCallback }) {
2275
2278
 
2276
2279
  const template$3 = `Write informative git changelog, in the imperative, based on a series of individual messages.
2277
2280
 
2278
- - Include the git commit hash as reference for each change, including just the first 7 characters
2281
+ - Annotate each change with the git commit hash as reference, including just the first 7 characters
2279
2282
  - Logically group changes, and if necessary, summarize dependency updates
2283
+ - Include a descriptive title for the changelog, to give a high-level overview of the changes
2284
+ - Depending on the size of the changes, consider breaking the changelog into sections
2285
+ - Avoid generlizations like "various bug fixes" or "improvements" or "enhancements"
2280
2286
 
2281
2287
  {format_instructions}
2282
2288
 
@@ -2357,7 +2363,7 @@ const handler$4 = async (argv, logger) => {
2357
2363
  variables: CHANGELOG_PROMPT.inputVariables,
2358
2364
  fallback: CHANGELOG_PROMPT,
2359
2365
  });
2360
- const formatInstructions = "Respond with a valid JSON object, containing two fields: 'header' and 'content', both strings.";
2366
+ const formatInstructions = "Respond with a valid JSON object, containing two fields: 'title' a string, no more than 65 characters, and 'content' a string.";
2361
2367
  const changelog = await executeChain({
2362
2368
  llm,
2363
2369
  prompt,
@@ -2370,7 +2376,7 @@ const handler$4 = async (argv, logger) => {
2370
2376
  const branchName = await getCurrentBranchName({ git });
2371
2377
  const ticketId = extractTicketIdFromBranchName(branchName);
2372
2378
  const footer = ticketId ? `\n\nPart of **${ticketId}**` : '';
2373
- return `${changelog.header}\n\n${changelog.content}${footer}`;
2379
+ return `${changelog.title}\n\n${changelog.content}${footer}`;
2374
2380
  },
2375
2381
  noResult: async () => {
2376
2382
  if (config.range) {
@@ -7003,7 +7009,7 @@ const options = {
7003
7009
  alias: 'interactive',
7004
7010
  description: 'Toggle interactive mode',
7005
7011
  },
7006
- 'b': {
7012
+ b: {
7007
7013
  type: 'string',
7008
7014
  alias: 'branch',
7009
7015
  description: 'Branch to review',
@@ -28329,7 +28335,7 @@ const handler = async (argv, logger) => {
28329
28335
 
28330
28336
  var review = {
28331
28337
  command,
28332
- desc: 'Review the staged changes',
28338
+ desc: 'Perform a code review on your changes',
28333
28339
  builder,
28334
28340
  handler: commandExecutor(handler),
28335
28341
  options,