git-coco 0.13.0 → 0.13.2

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/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2023 gfargo
3
+ Copyright (c) 2024 gfargo
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
package/README.md CHANGED
@@ -14,6 +14,8 @@
14
14
 
15
15
  - **`changelog`**: create changelogs for the current branch or a range of commits.
16
16
 
17
+ - **`recap`**: summarize changes from working-tree, or yesterday, or in the last month, or since the last tag!
18
+
17
19
  - **`init`**: step by step wizard to set up `coco` globally or for a project.
18
20
 
19
21
  ## Getting Started
@@ -42,6 +44,24 @@ coco
42
44
  coco commit
43
45
  ```
44
46
 
47
+ #### Useful options
48
+
49
+
50
+ ```bash
51
+ # --append
52
+ # Add content to the end of the generated commit
53
+ coco --append "Resolves #128"
54
+
55
+ # --append-ticket
56
+ # Automatically append Jira/Linear ticket ID from the branch name to the commit message
57
+ coco --append-ticket
58
+
59
+ # --additional
60
+ # Add extra context before generating the commit
61
+ coco --additional "Resolves UX bug with sign up button"
62
+ ```
63
+
64
+
45
65
  ### **`coco changelog`**
46
66
 
47
67
  Creates changelogs.
@@ -52,8 +72,22 @@ coco changelog
52
72
 
53
73
  # For a specific range
54
74
  coco changelog -r HEAD~5:HEAD
75
+
76
+ # For a target branch
77
+ coco changelog -b other-branch
55
78
  ```
56
79
 
80
+ ### **`coco recap`**
81
+
82
+ Summarize the working-tree, or other configured ranges
83
+
84
+ ```bash
85
+ # Summarize all working directory changes
86
+ coco recap
87
+
88
+ # Or these available ranges
89
+ coco recap --yesterday | --last-week | --last-month | --last-tag
90
+ ```
57
91
 
58
92
  ### Stdout vs. Interactive Mode
59
93
 
@@ -87,3 +121,11 @@ You can specify files to be ignored when generating commit messages by adding th
87
121
  ## Contribution
88
122
 
89
123
  We welcome contributions! Check out our [CONTRIBUTING.md](CONTRIBUTING.md) for more information.
124
+
125
+ ## Project Stats
126
+
127
+ ![Alt](https://repobeats.axiom.co/api/embed/ea76b881139f16595a343046d4f2bc9125a47e26.svg "Repobeats analytics image")
128
+
129
+ ## License
130
+
131
+ MIT © [gfargo](https://github.com/gfargo/)
package/dist/index.d.ts CHANGED
@@ -195,9 +195,9 @@ declare const _default$1: {
195
195
 
196
196
  interface RecapOptions extends BaseCommandOptions {
197
197
  yesterday?: boolean;
198
- "last-week"?: boolean;
199
- "last-month"?: boolean;
200
- "last-tag"?: boolean;
198
+ 'last-week'?: boolean;
199
+ 'last-month'?: boolean;
200
+ 'last-tag'?: boolean;
201
201
  }
202
202
  type RecapArgv = Arguments<RecapOptions>;
203
203
 
@@ -4,7 +4,7 @@ import { ConditionalPromptSelector, isChatModel } from '@langchain/core/example_
4
4
  import yargs from 'yargs';
5
5
  import chalk from 'chalk';
6
6
  import * as fs from 'fs';
7
- import fs__default from 'fs';
7
+ import fs__default, { promises } from 'fs';
8
8
  import { confirm, editor, select, password, input } from '@inquirer/prompts';
9
9
  import * as ini from 'ini';
10
10
  import * as os from 'os';
@@ -55,7 +55,7 @@ const getCommandUsageHeader = (command) => {
55
55
  return chalk.green(`${USAGE_BANNER}\n${chalk.white('Command:')}\n\xa0\xa0\xa0\xa0\xa0 $0 ${chalk.greenBright(command)} [options]`);
56
56
  };
57
57
  const CONFIG_ALREADY_EXISTS = (path) => {
58
- return `coco config found in '${path}', do you want to override it?`;
58
+ return `existing config found in '${path}', do you want to override it?`;
59
59
  };
60
60
 
61
61
  /**
@@ -376,11 +376,11 @@ function loadGitConfig(config) {
376
376
  if (fs.existsSync(gitConfigPath)) {
377
377
  const gitConfigRaw = fs.readFileSync(gitConfigPath, 'utf-8');
378
378
  const gitConfigParsed = ini.parse(gitConfigRaw);
379
- const gitServiceAlias = gitConfigParsed.coco?.service;
379
+ const gitConfigServiceObject = gitConfigParsed.coco?.service;
380
380
  let service = config.service;
381
- if (gitServiceAlias) {
382
- const gitServiceConfig = getDefaultServiceConfigFromAlias(gitServiceAlias);
383
- service = parseServiceConfig$1(gitServiceConfig || config.service);
381
+ if (gitConfigServiceObject) {
382
+ const gitServiceConfig = JSON.parse(gitConfigServiceObject);
383
+ service = gitServiceConfig || config?.service;
384
384
  }
385
385
  config = {
386
386
  ...config,
@@ -391,32 +391,11 @@ function loadGitConfig(config) {
391
391
  ignoredFiles: gitConfigParsed.coco?.ignoredFiles || config.ignoredFiles,
392
392
  ignoredExtensions: gitConfigParsed.coco?.ignoredExtensions || config.ignoredExtensions,
393
393
  defaultBranch: gitConfigParsed.coco?.defaultBranch || config.defaultBranch,
394
+ verbose: gitConfigParsed.coco?.verbose || config.verbose,
394
395
  };
395
396
  }
396
397
  return removeUndefined(config);
397
398
  }
398
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
399
- function parseServiceConfig$1(service) {
400
- if (!service)
401
- return undefined;
402
- switch (service.provider) {
403
- case 'openai':
404
- return {
405
- provider: 'openai',
406
- model: service.model,
407
- fields: { apiKey: service.apiKey },
408
- };
409
- case 'ollama':
410
- return {
411
- provider: 'ollama',
412
- model: service.model,
413
- endpoint: service.endpoint,
414
- fields: service.fields,
415
- };
416
- default:
417
- return undefined;
418
- }
419
- }
420
399
  /**
421
400
  * Appends the provided configuration to a git config file.
422
401
  *
@@ -431,15 +410,18 @@ const appendToGitConfig = async (filePath, config) => {
431
410
  const getNewContent = async () => {
432
411
  const contentLines = [header];
433
412
  for (const key in config) {
434
- // check if string has new lines, if so, wrap in quotes
435
- if (typeof config[key] === 'string') {
436
- const value = config[key];
437
- if (value.includes('\n')) {
438
- contentLines.push(`\t${key} = ${JSON.stringify(value)}`);
439
- continue;
440
- }
413
+ const value = config[key];
414
+ if (typeof value === 'object') {
415
+ // Serialize object to JSON string
416
+ contentLines.push(`\t${key} = ${JSON.stringify(value)}`);
417
+ }
418
+ else if (typeof value === 'string' && value.includes('\n')) {
419
+ // Wrap strings with new lines in quotes
420
+ contentLines.push(`\t${key} = ${JSON.stringify(value)}`);
421
+ }
422
+ else {
423
+ contentLines.push(`\t${key} = ${value}`);
441
424
  }
442
- contentLines.push(`\t${key} = ${config[key]}`);
443
425
  }
444
426
  return contentLines.join('\n');
445
427
  };
@@ -2475,8 +2457,8 @@ const options$3 = {
2475
2457
  description: 'Toggle interactive mode',
2476
2458
  },
2477
2459
  };
2478
- const builder$3 = (yargsInstance) => {
2479
- return yargsInstance.options(options$3).usage(getCommandUsageHeader(changelog.command));
2460
+ const builder$3 = (yargs) => {
2461
+ return yargs.options(options$3).usage(getCommandUsageHeader(changelog.command));
2480
2462
  };
2481
2463
 
2482
2464
  var changelog = {
@@ -5966,8 +5948,14 @@ async function parseDefaultFileDiff(nodeFile, commit = '--staged', git) {
5966
5948
  return await git.diff([nodeFile.filePath]);
5967
5949
  }
5968
5950
  else if (commit === '--untracked') {
5969
- // For untracked files, return the entire file content
5970
- return await git.show([`:${nodeFile.filePath}`]);
5951
+ // For untracked files, read the file content directly from the filesystem
5952
+ try {
5953
+ const fileContent = await promises.readFile(nodeFile.filePath, 'utf-8');
5954
+ return fileContent;
5955
+ }
5956
+ catch (error) {
5957
+ throw new Error(`Error reading untracked file: ${error?.message || 'Unknown error'}`);
5958
+ }
5971
5959
  }
5972
5960
  return await git.diff([commit, nodeFile.filePath]);
5973
5961
  }
@@ -6380,8 +6368,8 @@ const options$2 = {
6380
6368
  type: 'string',
6381
6369
  },
6382
6370
  };
6383
- const builder$2 = (yargsInstance) => {
6384
- return yargsInstance.options(options$2).usage(getCommandUsageHeader(commit.command));
6371
+ const builder$2 = (yargs) => {
6372
+ return yargs.options(options$2).usage(getCommandUsageHeader(commit.command));
6385
6373
  };
6386
6374
 
6387
6375
  var commit = {
@@ -6480,9 +6468,16 @@ async function checkAndHandlePackageInstallation({ global = false, logger, }) {
6480
6468
  try {
6481
6469
  // Global installation
6482
6470
  if (global) {
6483
- logger.startSpinner(`Installing '${packageName}' globally...`, { color: 'blue' });
6484
- await installNpmPackage({ name: packageName, flags: ['-g'] });
6485
- logger.stopSpinner(`Installed '${packageName}' globally`);
6471
+ const shouldInstall = await confirm({
6472
+ message: `Would you like to install/update '${packageName}' globally at this time?`,
6473
+ default: true,
6474
+ });
6475
+ if (!shouldInstall) {
6476
+ return;
6477
+ }
6478
+ logger.startSpinner(`Updating '${packageName}'...`, { color: 'blue' });
6479
+ // await installNpmPackage({ name: packageName, flags: ['-g'] })
6480
+ logger.stopSpinner(`Updated '${packageName}'`);
6486
6481
  return;
6487
6482
  }
6488
6483
  // Project level installation
@@ -6995,17 +6990,17 @@ const options = {
6995
6990
  type: 'boolean',
6996
6991
  description: 'Recap for yesterday',
6997
6992
  },
6998
- "last-week": {
6993
+ 'last-week': {
6999
6994
  alias: 'week',
7000
6995
  type: 'boolean',
7001
6996
  description: 'Recap for last week',
7002
6997
  },
7003
- "last-month": {
6998
+ 'last-month': {
7004
6999
  alias: 'month',
7005
7000
  type: 'boolean',
7006
7001
  description: 'Recap for last month',
7007
7002
  },
7008
- "last-tag": {
7003
+ 'last-tag': {
7009
7004
  alias: 'tag',
7010
7005
  type: 'boolean',
7011
7006
  description: 'Recap for last tag',
@@ -7016,8 +7011,8 @@ const options = {
7016
7011
  description: 'Toggle interactive mode',
7017
7012
  },
7018
7013
  };
7019
- const builder = (yargsInstance) => {
7020
- return yargsInstance.options(options).usage(getCommandUsageHeader(recap.command));
7014
+ const builder = (yargs) => {
7015
+ return yargs.options(options).usage(getCommandUsageHeader(recap.command));
7021
7016
  };
7022
7017
 
7023
7018
  var recap = {
package/dist/index.js CHANGED
@@ -76,7 +76,7 @@ const getCommandUsageHeader = (command) => {
76
76
  return chalk.green(`${USAGE_BANNER}\n${chalk.white('Command:')}\n\xa0\xa0\xa0\xa0\xa0 $0 ${chalk.greenBright(command)} [options]`);
77
77
  };
78
78
  const CONFIG_ALREADY_EXISTS = (path) => {
79
- return `coco config found in '${path}', do you want to override it?`;
79
+ return `existing config found in '${path}', do you want to override it?`;
80
80
  };
81
81
 
82
82
  /**
@@ -397,11 +397,11 @@ function loadGitConfig(config) {
397
397
  if (fs__namespace.existsSync(gitConfigPath)) {
398
398
  const gitConfigRaw = fs__namespace.readFileSync(gitConfigPath, 'utf-8');
399
399
  const gitConfigParsed = ini__namespace.parse(gitConfigRaw);
400
- const gitServiceAlias = gitConfigParsed.coco?.service;
400
+ const gitConfigServiceObject = gitConfigParsed.coco?.service;
401
401
  let service = config.service;
402
- if (gitServiceAlias) {
403
- const gitServiceConfig = getDefaultServiceConfigFromAlias(gitServiceAlias);
404
- service = parseServiceConfig$1(gitServiceConfig || config.service);
402
+ if (gitConfigServiceObject) {
403
+ const gitServiceConfig = JSON.parse(gitConfigServiceObject);
404
+ service = gitServiceConfig || config?.service;
405
405
  }
406
406
  config = {
407
407
  ...config,
@@ -412,32 +412,11 @@ function loadGitConfig(config) {
412
412
  ignoredFiles: gitConfigParsed.coco?.ignoredFiles || config.ignoredFiles,
413
413
  ignoredExtensions: gitConfigParsed.coco?.ignoredExtensions || config.ignoredExtensions,
414
414
  defaultBranch: gitConfigParsed.coco?.defaultBranch || config.defaultBranch,
415
+ verbose: gitConfigParsed.coco?.verbose || config.verbose,
415
416
  };
416
417
  }
417
418
  return removeUndefined(config);
418
419
  }
419
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
420
- function parseServiceConfig$1(service) {
421
- if (!service)
422
- return undefined;
423
- switch (service.provider) {
424
- case 'openai':
425
- return {
426
- provider: 'openai',
427
- model: service.model,
428
- fields: { apiKey: service.apiKey },
429
- };
430
- case 'ollama':
431
- return {
432
- provider: 'ollama',
433
- model: service.model,
434
- endpoint: service.endpoint,
435
- fields: service.fields,
436
- };
437
- default:
438
- return undefined;
439
- }
440
- }
441
420
  /**
442
421
  * Appends the provided configuration to a git config file.
443
422
  *
@@ -452,15 +431,18 @@ const appendToGitConfig = async (filePath, config) => {
452
431
  const getNewContent = async () => {
453
432
  const contentLines = [header];
454
433
  for (const key in config) {
455
- // check if string has new lines, if so, wrap in quotes
456
- if (typeof config[key] === 'string') {
457
- const value = config[key];
458
- if (value.includes('\n')) {
459
- contentLines.push(`\t${key} = ${JSON.stringify(value)}`);
460
- continue;
461
- }
434
+ const value = config[key];
435
+ if (typeof value === 'object') {
436
+ // Serialize object to JSON string
437
+ contentLines.push(`\t${key} = ${JSON.stringify(value)}`);
438
+ }
439
+ else if (typeof value === 'string' && value.includes('\n')) {
440
+ // Wrap strings with new lines in quotes
441
+ contentLines.push(`\t${key} = ${JSON.stringify(value)}`);
442
+ }
443
+ else {
444
+ contentLines.push(`\t${key} = ${value}`);
462
445
  }
463
- contentLines.push(`\t${key} = ${config[key]}`);
464
446
  }
465
447
  return contentLines.join('\n');
466
448
  };
@@ -2496,8 +2478,8 @@ const options$3 = {
2496
2478
  description: 'Toggle interactive mode',
2497
2479
  },
2498
2480
  };
2499
- const builder$3 = (yargsInstance) => {
2500
- return yargsInstance.options(options$3).usage(getCommandUsageHeader(changelog.command));
2481
+ const builder$3 = (yargs) => {
2482
+ return yargs.options(options$3).usage(getCommandUsageHeader(changelog.command));
2501
2483
  };
2502
2484
 
2503
2485
  var changelog = {
@@ -5987,8 +5969,14 @@ async function parseDefaultFileDiff(nodeFile, commit = '--staged', git) {
5987
5969
  return await git.diff([nodeFile.filePath]);
5988
5970
  }
5989
5971
  else if (commit === '--untracked') {
5990
- // For untracked files, return the entire file content
5991
- return await git.show([`:${nodeFile.filePath}`]);
5972
+ // For untracked files, read the file content directly from the filesystem
5973
+ try {
5974
+ const fileContent = await fs.promises.readFile(nodeFile.filePath, 'utf-8');
5975
+ return fileContent;
5976
+ }
5977
+ catch (error) {
5978
+ throw new Error(`Error reading untracked file: ${error?.message || 'Unknown error'}`);
5979
+ }
5992
5980
  }
5993
5981
  return await git.diff([commit, nodeFile.filePath]);
5994
5982
  }
@@ -6401,8 +6389,8 @@ const options$2 = {
6401
6389
  type: 'string',
6402
6390
  },
6403
6391
  };
6404
- const builder$2 = (yargsInstance) => {
6405
- return yargsInstance.options(options$2).usage(getCommandUsageHeader(commit.command));
6392
+ const builder$2 = (yargs) => {
6393
+ return yargs.options(options$2).usage(getCommandUsageHeader(commit.command));
6406
6394
  };
6407
6395
 
6408
6396
  var commit = {
@@ -6501,9 +6489,16 @@ async function checkAndHandlePackageInstallation({ global = false, logger, }) {
6501
6489
  try {
6502
6490
  // Global installation
6503
6491
  if (global) {
6504
- logger.startSpinner(`Installing '${packageName}' globally...`, { color: 'blue' });
6505
- await installNpmPackage({ name: packageName, flags: ['-g'] });
6506
- logger.stopSpinner(`Installed '${packageName}' globally`);
6492
+ const shouldInstall = await prompts.confirm({
6493
+ message: `Would you like to install/update '${packageName}' globally at this time?`,
6494
+ default: true,
6495
+ });
6496
+ if (!shouldInstall) {
6497
+ return;
6498
+ }
6499
+ logger.startSpinner(`Updating '${packageName}'...`, { color: 'blue' });
6500
+ // await installNpmPackage({ name: packageName, flags: ['-g'] })
6501
+ logger.stopSpinner(`Updated '${packageName}'`);
6507
6502
  return;
6508
6503
  }
6509
6504
  // Project level installation
@@ -7016,17 +7011,17 @@ const options = {
7016
7011
  type: 'boolean',
7017
7012
  description: 'Recap for yesterday',
7018
7013
  },
7019
- "last-week": {
7014
+ 'last-week': {
7020
7015
  alias: 'week',
7021
7016
  type: 'boolean',
7022
7017
  description: 'Recap for last week',
7023
7018
  },
7024
- "last-month": {
7019
+ 'last-month': {
7025
7020
  alias: 'month',
7026
7021
  type: 'boolean',
7027
7022
  description: 'Recap for last month',
7028
7023
  },
7029
- "last-tag": {
7024
+ 'last-tag': {
7030
7025
  alias: 'tag',
7031
7026
  type: 'boolean',
7032
7027
  description: 'Recap for last tag',
@@ -7037,8 +7032,8 @@ const options = {
7037
7032
  description: 'Toggle interactive mode',
7038
7033
  },
7039
7034
  };
7040
- const builder = (yargsInstance) => {
7041
- return yargsInstance.options(options).usage(getCommandUsageHeader(recap.command));
7035
+ const builder = (yargs) => {
7036
+ return yargs.options(options).usage(getCommandUsageHeader(recap.command));
7042
7037
  };
7043
7038
 
7044
7039
  var recap = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "git-coco",
3
- "version": "0.13.0",
3
+ "version": "0.13.2",
4
4
  "description": "zero-effort git commits with coco.",
5
5
  "author": "gfargo <ghfargo@gmail.com>",
6
6
  "license": "MIT",