From f89b4a3686b7c0ac0ffd9498b81f1fed3489bb83 Mon Sep 17 00:00:00 2001 From: benleibowitz Date: Sun, 4 May 2025 10:55:33 -0400 Subject: [PATCH 1/2] feat(commands/config.ts): add support for 'describe' mode to print detailed help for config parameters fix(commands/config.ts): improve error handling for 'get' and 'set' modes when no config keys are provided The changes in this commit add a new 'describe' mode to the config command, which allows users to get detailed information about the available configuration parameters. This includes a description of each parameter and the accepted values. The 'describe' mode can be used in two ways: 1. To print help for all available config parameters: ``` oco config describe ``` 2. To print help for specific config parameters: ``` oco config describe OCO_MODEL OCO_API_KEY ``` Additionally, the commit fixes the error handling for the 'get' and 'set' modes, ensuring that an error is thrown if no config keys are provided. These changes improve the usability and discoverability of the configuration system, making it easier for users to understand and manage their opencommit settings. --- README.md | 12 ++++ src/commands/config.ts | 135 +++++++++++++++++++++++++++++++++++++++-- 2 files changed, 143 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 74c42296..2605bc06 100644 --- a/README.md +++ b/README.md @@ -132,6 +132,18 @@ Simply set any of the variables above like this: oco config set OCO_MODEL=gpt-4o-mini ``` +To see all available configuration parameters and their accepted values: + +```sh +oco config describe +``` + +To see details for a specific parameter: + +```sh +oco config describe OCO_MODEL +``` + Configure [GitMoji](https://gitmoji.dev/) to preface a message. ```sh diff --git a/src/commands/config.ts b/src/commands/config.ts index 169ee3cd..26e465c3 100644 --- a/src/commands/config.ts +++ b/src/commands/config.ts @@ -31,7 +31,8 @@ export enum CONFIG_KEYS { export enum CONFIG_MODES { get = 'get', - set = 'set' + set = 'set', + describe = 'describe' } export const MODEL_LIST = { @@ -603,28 +604,154 @@ export const setConfig = ( outro(`${chalk.green('✔')} config successfully set`); }; +// --- HELP MESSAGE GENERATION --- +function getConfigKeyDetails(key) { + switch (key) { + case CONFIG_KEYS.OCO_MODEL: + return { + description: 'The AI model to use for generating commit messages', + values: MODEL_LIST + }; + case CONFIG_KEYS.OCO_AI_PROVIDER: + return { + description: 'The AI provider to use', + values: Object.values(OCO_AI_PROVIDER_ENUM) + }; + case CONFIG_KEYS.OCO_PROMPT_MODULE: + return { + description: 'The prompt module to use for commit message generation', + values: Object.values(OCO_PROMPT_MODULE_ENUM) + }; + case CONFIG_KEYS.OCO_LANGUAGE: + return { + description: 'The language to use for commit messages', + values: Object.keys(i18n) + }; + case CONFIG_KEYS.OCO_TEST_MOCK_TYPE: + return { + description: 'The type of test mock to use', + values: ['commit-message', 'prompt-module-commitlint-config'] + }; + case CONFIG_KEYS.OCO_DESCRIPTION: + case CONFIG_KEYS.OCO_EMOJI: + case CONFIG_KEYS.OCO_WHY: + case CONFIG_KEYS.OCO_ONE_LINE_COMMIT: + case CONFIG_KEYS.OCO_OMIT_SCOPE: + case CONFIG_KEYS.OCO_GITPUSH: + return { + description: 'Boolean flag', + values: ['true', 'false'] + }; + case CONFIG_KEYS.OCO_TOKENS_MAX_INPUT: + case CONFIG_KEYS.OCO_TOKENS_MAX_OUTPUT: + return { + description: 'Integer number', + values: ['Any positive integer'] + }; + case CONFIG_KEYS.OCO_API_KEY: + return { + description: 'API key for the selected provider', + values: ['String (required for most providers)'] + }; + case CONFIG_KEYS.OCO_API_URL: + return { + description: 'Custom API URL', + values: ["URL string (must start with 'http://' or 'https://')"] + }; + case CONFIG_KEYS.OCO_MESSAGE_TEMPLATE_PLACEHOLDER: + return { + description: 'Template placeholder for commit messages', + values: ["String (must start with $)"] + }; + default: + return { + description: 'String value', + values: ['Any string'] + }; + } +} + +function printConfigKeyHelp(param) { + if (!Object.values(CONFIG_KEYS).includes(param)) { + console.log(chalk.red(`Unknown config parameter: ${param}`)); + return; + } + + const details = getConfigKeyDetails(param as CONFIG_KEYS); + + console.log(chalk.bold(`\n${param}:`)); + console.log(chalk.gray(` Description: ${details.description}`)); + + if (Array.isArray(details.values)) { + console.log(chalk.gray(' Accepted values:')); + details.values.forEach(value => { + console.log(chalk.gray(` - ${value}`)); + }); + } else { + console.log(chalk.gray(' Accepted values by provider:')); + Object.entries(details.values).forEach(([provider, values]) => { + console.log(chalk.gray(` ${provider}:`)); + (values as string[]).forEach(value => { + console.log(chalk.gray(` - ${value}`)); + }); + }); + } +} + +function printAllConfigHelp() { + console.log(chalk.bold('Available config parameters:')); + for (const key of Object.values(CONFIG_KEYS)) { + printConfigKeyHelp(key); + } + console.log(chalk.gray('\nFor more help, see https://github.com/di-sukharev/opencommit')); +} + export const configCommand = command( { name: COMMANDS.config, - parameters: ['', ''] + parameters: ['', '[key=values...]'], + help: { + description: 'Configure opencommit settings', + examples: [ + 'Describe all config parameters: oco config describe', + 'Describe a specific parameter: oco config describe OCO_MODEL', + 'Get a config value: oco config get OCO_MODEL', + 'Set a config value: oco config set OCO_MODEL=gpt-4' + ] + } }, async (argv) => { try { const { mode, keyValues } = argv._; intro(`COMMAND: config ${mode} ${keyValues}`); - if (mode === CONFIG_MODES.get) { + if (mode === CONFIG_MODES.describe) { + if (!keyValues || keyValues.length === 0) { + printAllConfigHelp(); + } else { + for (const key of keyValues) { + printConfigKeyHelp(key); + } + } + process.exit(0); + } else if (mode === CONFIG_MODES.get) { + if (!keyValues || keyValues.length === 0) { + throw new Error('No config keys specified for get mode'); + } const config = getConfig() || {}; for (const key of keyValues) { outro(`${key}=${config[key as keyof typeof config]}`); } } else if (mode === CONFIG_MODES.set) { + if (!keyValues || keyValues.length === 0) { + throw new Error('No config keys specified for set mode'); + } await setConfig( keyValues.map((keyValue) => keyValue.split('=') as [string, string]) ); } else { throw new Error( - `Unsupported mode: ${mode}. Valid modes are: "set" and "get"` + `Unsupported mode: ${mode}. Valid modes are: "set", "get", and "describe"` ); } } catch (error) { From 96de6e43d005517d447113aea908e3d47fce1771 Mon Sep 17 00:00:00 2001 From: Ben Leibowitz Date: Fri, 9 May 2025 10:36:16 -0400 Subject: [PATCH 2/2] docs(README.md): update default model to gpt-4o-mini in configuration section refactor(config.ts): improve config key descriptions and add default values --- README.md | 2 +- src/commands/config.ts | 78 +++++++++++++++++++++++++++++++++++------- 2 files changed, 67 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 2605bc06..65a4954e 100644 --- a/README.md +++ b/README.md @@ -113,7 +113,7 @@ OCO_TOKENS_MAX_INPUT= OCO_TOKENS_MAX_OUTPUT= OCO_DESCRIPTION= OCO_EMOJI= -OCO_MODEL= +OCO_MODEL= OCO_LANGUAGE= OCO_MESSAGE_TEMPLATE_PLACEHOLDER= OCO_PROMPT_MODULE= diff --git a/src/commands/config.ts b/src/commands/config.ts index 26e465c3..d34cfc13 100644 --- a/src/commands/config.ts +++ b/src/commands/config.ts @@ -624,7 +624,7 @@ function getConfigKeyDetails(key) { }; case CONFIG_KEYS.OCO_LANGUAGE: return { - description: 'The language to use for commit messages', + description: 'The locale to use for commit messages', values: Object.keys(i18n) }; case CONFIG_KEYS.OCO_TEST_MOCK_TYPE: @@ -632,20 +632,44 @@ function getConfigKeyDetails(key) { description: 'The type of test mock to use', values: ['commit-message', 'prompt-module-commitlint-config'] }; + case CONFIG_KEYS.OCO_ONE_LINE_COMMIT: + return { + description: 'One line commit message', + values: ['true', 'false'] + }; case CONFIG_KEYS.OCO_DESCRIPTION: + return { + description: 'Postface a message with ~3 sentences description of the changes', + values: ['true', 'false'] + }; case CONFIG_KEYS.OCO_EMOJI: + return { + description: 'Preface a message with GitMoji', + values: ['true', 'false'] + }; case CONFIG_KEYS.OCO_WHY: - case CONFIG_KEYS.OCO_ONE_LINE_COMMIT: + return { + description: 'Output a short description of why the changes were done after the commit message (default: false)', + values: ['true', 'false'] + } case CONFIG_KEYS.OCO_OMIT_SCOPE: + return { + description: 'Do not include a scope in the commit message', + values: ['true', 'false'] + }; case CONFIG_KEYS.OCO_GITPUSH: return { - description: 'Boolean flag', + description: 'Push to git after commit (deprecated). If false, oco will exit after committing', values: ['true', 'false'] }; case CONFIG_KEYS.OCO_TOKENS_MAX_INPUT: + return { + description: 'Max model token limit', + values: ['Any positive integer'] + }; case CONFIG_KEYS.OCO_TOKENS_MAX_OUTPUT: return { - description: 'Integer number', + description: 'Max response tokens', values: ['Any positive integer'] }; case CONFIG_KEYS.OCO_API_KEY: @@ -655,12 +679,12 @@ function getConfigKeyDetails(key) { }; case CONFIG_KEYS.OCO_API_URL: return { - description: 'Custom API URL', + description: 'Custom API URL - may be used to set proxy path to OpenAI API', values: ["URL string (must start with 'http://' or 'https://')"] }; case CONFIG_KEYS.OCO_MESSAGE_TEMPLATE_PLACEHOLDER: return { - description: 'Template placeholder for commit messages', + description: 'Message template placeholder', values: ["String (must start with $)"] }; default: @@ -678,10 +702,25 @@ function printConfigKeyHelp(param) { } const details = getConfigKeyDetails(param as CONFIG_KEYS); - + + let desc = details.description; + let defaultValue = undefined; + if (param in DEFAULT_CONFIG) { + defaultValue = DEFAULT_CONFIG[param]; + } + + console.log(chalk.bold(`\n${param}:`)); - console.log(chalk.gray(` Description: ${details.description}`)); - + console.log(chalk.gray(` Description: ${desc}`)); + if (defaultValue !== undefined) { + // Print booleans and numbers as-is, strings without quotes + if (typeof defaultValue === 'string') { + console.log(chalk.gray(` Default: ${defaultValue}`)); + } else { + console.log(chalk.gray(` Default: ${defaultValue}`)); + } + } + if (Array.isArray(details.values)) { console.log(chalk.gray(' Accepted values:')); details.values.forEach(value => { @@ -700,10 +739,25 @@ function printConfigKeyHelp(param) { function printAllConfigHelp() { console.log(chalk.bold('Available config parameters:')); - for (const key of Object.values(CONFIG_KEYS)) { - printConfigKeyHelp(key); + for (const key of Object.values(CONFIG_KEYS).sort()) { + const details = getConfigKeyDetails(key); + // Try to get the default value from DEFAULT_CONFIG + let defaultValue = undefined; + if (key in DEFAULT_CONFIG) { + defaultValue = DEFAULT_CONFIG[key]; + } + + console.log(chalk.bold(`\n${key}:`)); + console.log(chalk.gray(` Description: ${details.description}`)); + if (defaultValue !== undefined) { + if (typeof defaultValue === 'string') { + console.log(chalk.gray(` Default: ${defaultValue}`)); + } else { + console.log(chalk.gray(` Default: ${defaultValue}`)); + } + } } - console.log(chalk.gray('\nFor more help, see https://github.com/di-sukharev/opencommit')); + console.log(chalk.yellow('\nUse "oco config describe [PARAMETER]" to see accepted values and more details for a specific config parameter.')); } export const configCommand = command(