From 942cdf5fc7d866a59b91ad2db773e32bcac517f1 Mon Sep 17 00:00:00 2001 From: Joseph Stanton Date: Sat, 27 Jan 2024 19:47:37 -0500 Subject: [PATCH 1/2] Updated OLLama module to allow model and url config The changes include adding a constructor with `model` and `basePath` options to the `OllamaAi` class, updating the `generateCommitMessage` method to use these new options, adding static methods `validateBaseUrl` and `validateModel` to validate the Ollama API base URL and model availability respectively, and updating the import of `OllamaAi` in `engine.ts` --- src/commands/config.ts | 30 +++++++++++++++++++++-- src/engine/ollama.ts | 55 ++++++++++++++++++++++++++++++++++++------ src/utils/engine.ts | 7 ++++-- 3 files changed, 81 insertions(+), 11 deletions(-) diff --git a/src/commands/config.ts b/src/commands/config.ts index 50dfc5da..81eab8b5 100644 --- a/src/commands/config.ts +++ b/src/commands/config.ts @@ -10,6 +10,7 @@ import { intro, outro } from '@clack/prompts'; import { COMMANDS } from '../CommandsEnum'; import { getI18nLocal } from '../i18n'; +import { OllamaAi } from '../engine/ollama'; dotenv.config(); @@ -24,6 +25,8 @@ export enum CONFIG_KEYS { OCO_MESSAGE_TEMPLATE_PLACEHOLDER = 'OCO_MESSAGE_TEMPLATE_PLACEHOLDER', OCO_PROMPT_MODULE = 'OCO_PROMPT_MODULE', OCO_AI_PROVIDER = 'OCO_AI_PROVIDER', + OCO_OLLAMA_MODEL = 'OCO_OLLAMA_MODEL', + OCO_OLLAMA_BASE_PATH = 'OCO_OLLAMA_BASE_PATH' } export const DEFAULT_MODEL_TOKEN_LIMIT = 4096; @@ -166,6 +169,27 @@ export const configValidators = { ); return value; }, + async [CONFIG_KEYS.OCO_OLLAMA_BASE_PATH](value: any) { + validateConfig( + CONFIG_KEYS.OCO_OLLAMA_BASE_PATH, + typeof value === 'string', + 'Must be string' + ); + validateConfig( + CONFIG_KEYS.OCO_OLLAMA_BASE_PATH, + await OllamaAi.validateBaseUrl(value), + 'Must be valid ollama url' + ); + return value; + }, + async [CONFIG_KEYS.OCO_OLLAMA_MODEL](value: any, config: any = {}) { + validateConfig( + CONFIG_KEYS.OCO_OLLAMA_MODEL, + await OllamaAi.validateModel(value, config?.OCO_OLLAMA_BASE_PATH), + `${value} is not supported yet, use 'mistral' (default)` + ); + return value; + } }; export type ConfigType = { @@ -188,7 +212,9 @@ export const getConfig = (): ConfigType | null => { OCO_MESSAGE_TEMPLATE_PLACEHOLDER: process.env.OCO_MESSAGE_TEMPLATE_PLACEHOLDER || '$msg', OCO_PROMPT_MODULE: process.env.OCO_PROMPT_MODULE || 'conventional-commit', - OCO_AI_PROVIDER: process.env.OCO_AI_PROVIDER || 'openai' + OCO_AI_PROVIDER: process.env.OCO_AI_PROVIDER || 'openai', + OCO_OLLAMA_BASE_PATH: process.env.OCO_OLLAMA_BASE_PATH || 'http://localhost:11434', + OCO_OLLAMA_MODEL: process.env.OCO_OLLAMA_MODEL || 'mistral' }; const configExists = existsSync(configPath); @@ -244,7 +270,7 @@ export const setConfig = (keyValues: [key: string, value: string][]) => { } const validValue = - configValidators[configKey as CONFIG_KEYS](parsedConfigValue); + configValidators[configKey as CONFIG_KEYS](parsedConfigValue, config); config[configKey as CONFIG_KEYS] = validValue; } diff --git a/src/engine/ollama.ts b/src/engine/ollama.ts index 2ca324b7..0362f4fc 100644 --- a/src/engine/ollama.ts +++ b/src/engine/ollama.ts @@ -1,20 +1,24 @@ -import axios, { AxiosError } from 'axios'; +import axios from 'axios'; import { ChatCompletionRequestMessage } from 'openai'; import { AiEngine } from './Engine'; export class OllamaAi implements AiEngine { + + constructor(private model: string = 'mistral', private basePath: string = 'http://localhost:11434') { + } + + async generateCommitMessage( messages: Array ): Promise { - const model = 'mistral'; // todo: allow other models + let prompt = messages.map((x) => x.content).join('\n'); //hoftix: local models are not so clever so im changing the prompt a bit... prompt += 'Summarize above git diff in 10 words or less'; - - const url = 'http://localhost:11434/api/generate'; + const url = `${this.basePath}/api/generate`; const p = { - model, + model: this.model, prompt, stream: false }; @@ -24,13 +28,50 @@ export class OllamaAi implements AiEngine { 'Content-Type': 'application/json' } }); - const answer = response.data?.response; - return answer; + return response.data?.response; } catch (err: any) { const message = err.response?.data?.error ?? err.message; throw new Error('local model issues. details: ' + message); } } + + static async validateBaseUrl(baseUrl: string): Promise { + baseUrl = baseUrl.trim().replace(/\/$/, '') + const url = `${baseUrl}/api/tags`; + try { + const response = await axios.get(url, { + headers: { + 'Content-Type': 'application/json' + } + }); + return response.status===200; + + } catch (err: any) { + const message = err.response?.data?.error ?? err.message; + console.error('Failed to connect to api: ' + err) + throw new Error('Failed to connect to api: ' + message); + } + } + + static async validateModel(model: string, baseUrl: string = 'http://localhost:11434'): Promise { + const url = `${baseUrl}/api/show`; + const p = { + name: model + }; + try { + const response = await axios.post(url, p, { + headers: { + 'Content-Type': 'application/json' + } + }); + return response.status===200; + } catch (err: any) { + const message = err.response?.data?.error ?? err.message; + throw new Error('local model issues. details: ' + message); + } + } + + } export const ollamaAi = new OllamaAi(); diff --git a/src/utils/engine.ts b/src/utils/engine.ts index 74d780b1..3c92f1b5 100644 --- a/src/utils/engine.ts +++ b/src/utils/engine.ts @@ -1,12 +1,15 @@ import { AiEngine } from '../engine/Engine'; import { api } from '../engine/openAi'; import { getConfig } from '../commands/config'; -import { ollamaAi } from '../engine/ollama'; +import { OllamaAi } from '../engine/ollama'; export function getEngine(): AiEngine { const config = getConfig(); if (config?.OCO_AI_PROVIDER == 'ollama') { - return ollamaAi; + return new OllamaAi( + config?.OCO_OLLAMA_MODEL || 'mistral', + config?.OCO_OLLAMA_BASE_PATH || 'http://localhost:11434' + ); } //open ai gpt by default return api; From 885e11889e60d2008c60d176cc2b23c665f776bb Mon Sep 17 00:00:00 2001 From: Joseph Stanton Date: Sat, 27 Jan 2024 19:58:41 -0500 Subject: [PATCH 2/2] build --- out/cli.cjs | 2024 +++++++++++++++++++++-------------------- out/github-action.cjs | 2022 ++++++++++++++++++++-------------------- 2 files changed, 2083 insertions(+), 1963 deletions(-) diff --git a/out/cli.cjs b/out/cli.cjs index bc7c85ca..e425bbc6 100755 --- a/out/cli.cjs +++ b/out/cli.cjs @@ -14823,6 +14823,76 @@ var require_ini = __commonJS({ } }); +// node_modules/proxy-from-env/index.js +var require_proxy_from_env = __commonJS({ + "node_modules/proxy-from-env/index.js"(exports) { + "use strict"; + var parseUrl = require("url").parse; + var DEFAULT_PORTS = { + ftp: 21, + gopher: 70, + http: 80, + https: 443, + ws: 80, + wss: 443 + }; + var stringEndsWith = String.prototype.endsWith || function(s) { + return s.length <= this.length && this.indexOf(s, this.length - s.length) !== -1; + }; + function getProxyForUrl2(url3) { + var parsedUrl = typeof url3 === "string" ? parseUrl(url3) : url3 || {}; + var proto2 = parsedUrl.protocol; + var hostname = parsedUrl.host; + var port = parsedUrl.port; + if (typeof hostname !== "string" || !hostname || typeof proto2 !== "string") { + return ""; + } + proto2 = proto2.split(":", 1)[0]; + hostname = hostname.replace(/:\d*$/, ""); + port = parseInt(port) || DEFAULT_PORTS[proto2] || 0; + if (!shouldProxy(hostname, port)) { + return ""; + } + var proxy = getEnv2("npm_config_" + proto2 + "_proxy") || getEnv2(proto2 + "_proxy") || getEnv2("npm_config_proxy") || getEnv2("all_proxy"); + if (proxy && proxy.indexOf("://") === -1) { + proxy = proto2 + "://" + proxy; + } + return proxy; + } + function shouldProxy(hostname, port) { + var NO_PROXY = (getEnv2("npm_config_no_proxy") || getEnv2("no_proxy")).toLowerCase(); + if (!NO_PROXY) { + return true; + } + if (NO_PROXY === "*") { + return false; + } + return NO_PROXY.split(/[,\s]/).every(function(proxy) { + if (!proxy) { + return true; + } + var parsedProxy = proxy.match(/^(.+):(\d+)$/); + var parsedProxyHostname = parsedProxy ? parsedProxy[1] : proxy; + var parsedProxyPort = parsedProxy ? parseInt(parsedProxy[2]) : 0; + if (parsedProxyPort && parsedProxyPort !== port) { + return true; + } + if (!/^[.*]/.test(parsedProxyHostname)) { + return hostname !== parsedProxyHostname; + } + if (parsedProxyHostname.charAt(0) === "*") { + parsedProxyHostname = parsedProxyHostname.slice(1); + } + return !stringEndsWith.call(hostname, parsedProxyHostname); + }); + } + function getEnv2(key) { + return process.env[key.toLowerCase()] || process.env[key.toUpperCase()] || ""; + } + exports.getProxyForUrl = getProxyForUrl2; + } +}); + // node_modules/@commitlint/types/lib/ensure.js var require_ensure = __commonJS({ "node_modules/@commitlint/types/lib/ensure.js"(exports) { @@ -14935,76 +15005,6 @@ var require_lib = __commonJS({ } }); -// node_modules/proxy-from-env/index.js -var require_proxy_from_env = __commonJS({ - "node_modules/proxy-from-env/index.js"(exports) { - "use strict"; - var parseUrl = require("url").parse; - var DEFAULT_PORTS = { - ftp: 21, - gopher: 70, - http: 80, - https: 443, - ws: 80, - wss: 443 - }; - var stringEndsWith = String.prototype.endsWith || function(s) { - return s.length <= this.length && this.indexOf(s, this.length - s.length) !== -1; - }; - function getProxyForUrl2(url3) { - var parsedUrl = typeof url3 === "string" ? parseUrl(url3) : url3 || {}; - var proto2 = parsedUrl.protocol; - var hostname = parsedUrl.host; - var port = parsedUrl.port; - if (typeof hostname !== "string" || !hostname || typeof proto2 !== "string") { - return ""; - } - proto2 = proto2.split(":", 1)[0]; - hostname = hostname.replace(/:\d*$/, ""); - port = parseInt(port) || DEFAULT_PORTS[proto2] || 0; - if (!shouldProxy(hostname, port)) { - return ""; - } - var proxy = getEnv2("npm_config_" + proto2 + "_proxy") || getEnv2(proto2 + "_proxy") || getEnv2("npm_config_proxy") || getEnv2("all_proxy"); - if (proxy && proxy.indexOf("://") === -1) { - proxy = proto2 + "://" + proxy; - } - return proxy; - } - function shouldProxy(hostname, port) { - var NO_PROXY = (getEnv2("npm_config_no_proxy") || getEnv2("no_proxy")).toLowerCase(); - if (!NO_PROXY) { - return true; - } - if (NO_PROXY === "*") { - return false; - } - return NO_PROXY.split(/[,\s]/).every(function(proxy) { - if (!proxy) { - return true; - } - var parsedProxy = proxy.match(/^(.+):(\d+)$/); - var parsedProxyHostname = parsedProxy ? parsedProxy[1] : proxy; - var parsedProxyPort = parsedProxy ? parseInt(parsedProxy[2]) : 0; - if (parsedProxyPort && parsedProxyPort !== port) { - return true; - } - if (!/^[.*]/.test(parsedProxyHostname)) { - return hostname !== parsedProxyHostname; - } - if (parsedProxyHostname.charAt(0) === "*") { - parsedProxyHostname = parsedProxyHostname.slice(1); - } - return !stringEndsWith.call(hostname, parsedProxyHostname); - }); - } - function getEnv2(key) { - return process.env[key.toLowerCase()] || process.env[key.toUpperCase()] || ""; - } - exports.getProxyForUrl = getProxyForUrl2; - } -}); - // node_modules/@dqbd/tiktoken/lite/tiktoken_bg.cjs var require_tiktoken_bg = __commonJS({ "node_modules/@dqbd/tiktoken/lite/tiktoken_bg.cjs"(exports, module2) { @@ -16425,7 +16425,7 @@ var package_default = { scripts: { watch: "npm run -S build -- --sourcemap --watch", start: "node ./out/cli.cjs", - "ollama:start": "AI_PROVIDER='ollama' node./out/cli.cjs", + "ollama:start": "OCO_AI_PROVIDER='ollama' node ./out/cli.cjs", dev: "ts-node ./src/cli.ts", build: "rimraf out && node esbuild.config.js", "build:push": "npm run build && git add . && git commit -m 'build' && git push", @@ -18648,677 +18648,263 @@ function getI18nLocal(value) { return false; } -// src/commands/config.ts -dotenv.config(); -var DEFAULT_MODEL_TOKEN_LIMIT = 4096; -var validateConfig = (key, condition, validationMessage) => { - if (!condition) { - ce( - `${source_default.red("\u2716")} Unsupported config key ${key}: ${validationMessage}` - ); - process.exit(1); +// node_modules/axios/lib/helpers/bind.js +function bind(fn, thisArg) { + return function wrap() { + return fn.apply(thisArg, arguments); + }; +} + +// node_modules/axios/lib/utils.js +var { toString } = Object.prototype; +var { getPrototypeOf } = Object; +var kindOf = ((cache) => (thing) => { + const str = toString.call(thing); + return cache[str] || (cache[str] = str.slice(8, -1).toLowerCase()); +})(/* @__PURE__ */ Object.create(null)); +var kindOfTest = (type) => { + type = type.toLowerCase(); + return (thing) => kindOf(thing) === type; +}; +var typeOfTest = (type) => (thing) => typeof thing === type; +var { isArray } = Array; +var isUndefined = typeOfTest("undefined"); +function isBuffer(val) { + return val !== null && !isUndefined(val) && val.constructor !== null && !isUndefined(val.constructor) && isFunction(val.constructor.isBuffer) && val.constructor.isBuffer(val); +} +var isArrayBuffer = kindOfTest("ArrayBuffer"); +function isArrayBufferView(val) { + let result; + if (typeof ArrayBuffer !== "undefined" && ArrayBuffer.isView) { + result = ArrayBuffer.isView(val); + } else { + result = val && val.buffer && isArrayBuffer(val.buffer); + } + return result; +} +var isString = typeOfTest("string"); +var isFunction = typeOfTest("function"); +var isNumber = typeOfTest("number"); +var isObject = (thing) => thing !== null && typeof thing === "object"; +var isBoolean = (thing) => thing === true || thing === false; +var isPlainObject = (val) => { + if (kindOf(val) !== "object") { + return false; } + const prototype3 = getPrototypeOf(val); + return (prototype3 === null || prototype3 === Object.prototype || Object.getPrototypeOf(prototype3) === null) && !(Symbol.toStringTag in val) && !(Symbol.iterator in val); }; -var configValidators = { - ["OCO_OPENAI_API_KEY" /* OCO_OPENAI_API_KEY */](value, config8 = {}) { - validateConfig("API_KEY", value || config8.OCO_AI_PROVIDER == "ollama", "You need to provide an API key"); - validateConfig( - "OCO_OPENAI_API_KEY" /* OCO_OPENAI_API_KEY */, - value.startsWith("sk-"), - 'Must start with "sk-"' - ); - validateConfig( - "OCO_OPENAI_API_KEY" /* OCO_OPENAI_API_KEY */, - config8["OCO_OPENAI_BASE_PATH" /* OCO_OPENAI_BASE_PATH */] || value.length === 51, - "Must be 51 characters long" - ); - return value; - }, - ["OCO_DESCRIPTION" /* OCO_DESCRIPTION */](value) { - validateConfig( - "OCO_DESCRIPTION" /* OCO_DESCRIPTION */, - typeof value === "boolean", - "Must be true or false" - ); - return value; - }, - ["OCO_OPENAI_MAX_TOKENS" /* OCO_OPENAI_MAX_TOKENS */](value) { - if (typeof value === "string") { - value = parseInt(value); - validateConfig( - "OCO_OPENAI_MAX_TOKENS" /* OCO_OPENAI_MAX_TOKENS */, - !isNaN(value), - "Must be a number" - ); +var isDate = kindOfTest("Date"); +var isFile = kindOfTest("File"); +var isBlob = kindOfTest("Blob"); +var isFileList = kindOfTest("FileList"); +var isStream2 = (val) => isObject(val) && isFunction(val.pipe); +var isFormData = (thing) => { + const pattern = "[object FormData]"; + return thing && (typeof FormData === "function" && thing instanceof FormData || toString.call(thing) === pattern || isFunction(thing.toString) && thing.toString() === pattern); +}; +var isURLSearchParams = kindOfTest("URLSearchParams"); +var trim = (str) => str.trim ? str.trim() : str.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, ""); +function forEach(obj, fn, { allOwnKeys = false } = {}) { + if (obj === null || typeof obj === "undefined") { + return; + } + let i2; + let l; + if (typeof obj !== "object") { + obj = [obj]; + } + if (isArray(obj)) { + for (i2 = 0, l = obj.length; i2 < l; i2++) { + fn.call(null, obj[i2], i2, obj); } - validateConfig( - "OCO_OPENAI_MAX_TOKENS" /* OCO_OPENAI_MAX_TOKENS */, - value ? typeof value === "number" : void 0, - "Must be a number" - ); - return value; - }, - ["OCO_EMOJI" /* OCO_EMOJI */](value) { - validateConfig( - "OCO_EMOJI" /* OCO_EMOJI */, - typeof value === "boolean", - "Must be true or false" - ); - return value; - }, - ["OCO_LANGUAGE" /* OCO_LANGUAGE */](value) { - validateConfig( - "OCO_LANGUAGE" /* OCO_LANGUAGE */, - getI18nLocal(value), - `${value} is not supported yet` - ); - return getI18nLocal(value); - }, - ["OCO_OPENAI_BASE_PATH" /* OCO_OPENAI_BASE_PATH */](value) { - validateConfig( - "OCO_OPENAI_BASE_PATH" /* OCO_OPENAI_BASE_PATH */, - typeof value === "string", - "Must be string" - ); - return value; - }, - ["OCO_MODEL" /* OCO_MODEL */](value) { - validateConfig( - "OCO_MODEL" /* OCO_MODEL */, - [ - "gpt-3.5-turbo", - "gpt-4", - "gpt-3.5-turbo-16k", - "gpt-3.5-turbo-0613" - ].includes(value), - `${value} is not supported yet, use 'gpt-4', 'gpt-3.5-turbo-16k' (default), 'gpt-3.5-turbo-0613' or 'gpt-3.5-turbo'` - ); - return value; - }, - ["OCO_MESSAGE_TEMPLATE_PLACEHOLDER" /* OCO_MESSAGE_TEMPLATE_PLACEHOLDER */](value) { - validateConfig( - "OCO_MESSAGE_TEMPLATE_PLACEHOLDER" /* OCO_MESSAGE_TEMPLATE_PLACEHOLDER */, - value.startsWith("$"), - `${value} must start with $, for example: '$msg'` - ); - return value; - }, - ["OCO_PROMPT_MODULE" /* OCO_PROMPT_MODULE */](value) { - validateConfig( - "OCO_PROMPT_MODULE" /* OCO_PROMPT_MODULE */, - ["conventional-commit", "@commitlint"].includes(value), - `${value} is not supported yet, use '@commitlint' or 'conventional-commit' (default)` - ); - return value; - }, - ["OCO_AI_PROVIDER" /* OCO_AI_PROVIDER */](value) { - validateConfig( - "OCO_AI_PROVIDER" /* OCO_AI_PROVIDER */, - [ - "", - "openai", - "ollama" - ].includes(value), - `${value} is not supported yet, use 'ollama' or 'openai' (default)` - ); - return value; - } -}; -var configPath = (0, import_path.join)((0, import_os.homedir)(), ".opencommit"); -var getConfig = () => { - const configFromEnv = { - OCO_OPENAI_API_KEY: process.env.OCO_OPENAI_API_KEY, - OCO_OPENAI_MAX_TOKENS: process.env.OCO_OPENAI_MAX_TOKENS ? Number(process.env.OCO_OPENAI_MAX_TOKENS) : void 0, - OCO_OPENAI_BASE_PATH: process.env.OCO_OPENAI_BASE_PATH, - OCO_DESCRIPTION: process.env.OCO_DESCRIPTION === "true" ? true : false, - OCO_EMOJI: process.env.OCO_EMOJI === "true" ? true : false, - OCO_MODEL: process.env.OCO_MODEL || "gpt-3.5-turbo-16k", - OCO_LANGUAGE: process.env.OCO_LANGUAGE || "en", - OCO_MESSAGE_TEMPLATE_PLACEHOLDER: process.env.OCO_MESSAGE_TEMPLATE_PLACEHOLDER || "$msg", - OCO_PROMPT_MODULE: process.env.OCO_PROMPT_MODULE || "conventional-commit", - OCO_AI_PROVIDER: process.env.OCO_AI_PROVIDER || "openai" - }; - const configExists = (0, import_fs.existsSync)(configPath); - if (!configExists) - return configFromEnv; - const configFile = (0, import_fs.readFileSync)(configPath, "utf8"); - const config8 = (0, import_ini.parse)(configFile); - for (const configKey of Object.keys(config8)) { - if (!config8[configKey] || ["null", "undefined"].includes(config8[configKey])) { - config8[configKey] = void 0; - continue; + } else { + const keys = allOwnKeys ? Object.getOwnPropertyNames(obj) : Object.keys(obj); + const len = keys.length; + let key; + for (i2 = 0; i2 < len; i2++) { + key = keys[i2]; + fn.call(null, obj[key], key, obj); } - try { - const validator = configValidators[configKey]; - const validValue = validator( - config8[configKey] ?? configFromEnv[configKey], - config8 - ); - config8[configKey] = validValue; - } catch (error) { - ce( - `'${configKey}' name is invalid, it should be either 'OCO_${configKey.toUpperCase()}' or it doesn't exist.` - ); - ce( - `Manually fix the '.env' file or global '~/.opencommit' config file.` - ); - process.exit(1); + } +} +function findKey(obj, key) { + key = key.toLowerCase(); + const keys = Object.keys(obj); + let i2 = keys.length; + let _key; + while (i2-- > 0) { + _key = keys[i2]; + if (key === _key.toLowerCase()) { + return _key; } } - return config8; -}; -var setConfig = (keyValues) => { - const config8 = getConfig() || {}; - for (const [configKey, configValue] of keyValues) { - if (!configValidators.hasOwnProperty(configKey)) { - throw new Error(`Unsupported config key: ${configKey}`); + return null; +} +var _global = (() => { + if (typeof globalThis !== "undefined") + return globalThis; + return typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : global; +})(); +var isContextDefined = (context) => !isUndefined(context) && context !== _global; +function merge() { + const { caseless } = isContextDefined(this) && this || {}; + const result = {}; + const assignValue = (val, key) => { + const targetKey = caseless && findKey(result, key) || key; + if (isPlainObject(result[targetKey]) && isPlainObject(val)) { + result[targetKey] = merge(result[targetKey], val); + } else if (isPlainObject(val)) { + result[targetKey] = merge({}, val); + } else if (isArray(val)) { + result[targetKey] = val.slice(); + } else { + result[targetKey] = val; } - let parsedConfigValue; - try { - parsedConfigValue = JSON.parse(configValue); - } catch (error) { - parsedConfigValue = configValue; + }; + for (let i2 = 0, l = arguments.length; i2 < l; i2++) { + arguments[i2] && forEach(arguments[i2], assignValue); + } + return result; +} +var extend = (a2, b6, thisArg, { allOwnKeys } = {}) => { + forEach(b6, (val, key) => { + if (thisArg && isFunction(val)) { + a2[key] = bind(val, thisArg); + } else { + a2[key] = val; } - const validValue = configValidators[configKey](parsedConfigValue); - config8[configKey] = validValue; + }, { allOwnKeys }); + return a2; +}; +var stripBOM = (content) => { + if (content.charCodeAt(0) === 65279) { + content = content.slice(1); } - (0, import_fs.writeFileSync)(configPath, (0, import_ini.stringify)(config8), "utf8"); - ce(`${source_default.green("\u2714")} Config successfully set`); + return content; }; -var configCommand = G3( - { - name: "config" /* config */, - parameters: ["", ""] - }, - async (argv) => { - ae("opencommit \u2014 config"); - try { - const { mode: mode2, keyValues } = argv._; - if (mode2 === "get" /* get */) { - const config8 = getConfig() || {}; - for (const key of keyValues) { - ce(`${key}=${config8[key]}`); - } - } else if (mode2 === "set" /* set */) { - await setConfig( - keyValues.map((keyValue) => keyValue.split("=")) - ); - } else { - throw new Error( - `Unsupported mode: ${mode2}. Valid modes are: "set" and "get"` - ); +var inherits = (constructor, superConstructor, props, descriptors3) => { + constructor.prototype = Object.create(superConstructor.prototype, descriptors3); + constructor.prototype.constructor = constructor; + Object.defineProperty(constructor, "super", { + value: superConstructor.prototype + }); + props && Object.assign(constructor.prototype, props); +}; +var toFlatObject = (sourceObj, destObj, filter2, propFilter) => { + let props; + let i2; + let prop; + const merged = {}; + destObj = destObj || {}; + if (sourceObj == null) + return destObj; + do { + props = Object.getOwnPropertyNames(sourceObj); + i2 = props.length; + while (i2-- > 0) { + prop = props[i2]; + if ((!propFilter || propFilter(prop, sourceObj, destObj)) && !merged[prop]) { + destObj[prop] = sourceObj[prop]; + merged[prop] = true; } - } catch (error) { - ce(`${source_default.red("\u2716")} ${error}`); - process.exit(1); } + sourceObj = filter2 !== false && getPrototypeOf(sourceObj); + } while (sourceObj && (!filter2 || filter2(sourceObj, destObj)) && sourceObj !== Object.prototype); + return destObj; +}; +var endsWith = (str, searchString, position) => { + str = String(str); + if (position === void 0 || position > str.length) { + position = str.length; } -); - -// src/prompts.ts -var import_openai3 = __toESM(require_dist(), 1); - -// src/modules/commitlint/constants.ts -var COMMITLINT_LLM_CONFIG_PATH = `${process.env.PWD}/.opencommit-commitlint`; - -// src/modules/commitlint/crypto.ts -var import_crypto = __toESM(require("crypto"), 1); -var computeHash = async (content, algorithm = "sha256") => { - try { - const hash = import_crypto.default.createHash(algorithm); - hash.update(content); - return hash.digest("hex"); - } catch (error) { - console.error("Error while computing hash:", error); - throw error; - } + position -= searchString.length; + const lastIndex = str.indexOf(searchString, position); + return lastIndex !== -1 && lastIndex === position; }; - -// src/modules/commitlint/prompts.ts -var import_openai = __toESM(require_dist(), 1); -var import_types = __toESM(require_lib(), 1); -var config2 = getConfig(); -var translation = i18n[config2?.OCO_LANGUAGE || "en"]; -var getTypeRuleExtraDescription = (type, prompt) => prompt?.questions?.type?.enum?.[type]?.description; -var llmReadableRules = { - blankline: (key, applicable) => `There should ${applicable} be a blank line at the beginning of the ${key}.`, - caseRule: (key, applicable, value) => `The ${key} should ${applicable} be in ${Array.isArray(value) ? `one of the following case: - - ${value.join("\n - ")}.` : `${value} case.`}`, - emptyRule: (key, applicable) => `The ${key} should ${applicable} be empty.`, - enumRule: (key, applicable, value) => `The ${key} should ${applicable} be one of the following values: - - ${Array.isArray(value) ? value.join("\n - ") : value}.`, - enumTypeRule: (key, applicable, value, prompt) => `The ${key} should ${applicable} be one of the following values: - - ${Array.isArray(value) ? value.map((v4) => { - const description = getTypeRuleExtraDescription(v4, prompt); - if (description) { - return `${v4} (${description})`; - } else - return v4; - }).join("\n - ") : value}.`, - fullStopRule: (key, applicable, value) => `The ${key} should ${applicable} end with '${value}'.`, - maxLengthRule: (key, applicable, value) => `The ${key} should ${applicable} have ${value} characters or less.`, - minLengthRule: (key, applicable, value) => `The ${key} should ${applicable} have ${value} characters or more.` -}; -var rulesPrompts = { - "body-case": (applicable, value) => llmReadableRules.caseRule("body", applicable, value), - "body-empty": (applicable) => llmReadableRules.emptyRule("body", applicable, void 0), - "body-full-stop": (applicable, value) => llmReadableRules.fullStopRule("body", applicable, value), - "body-leading-blank": (applicable) => llmReadableRules.blankline("body", applicable, void 0), - "body-max-length": (applicable, value) => llmReadableRules.maxLengthRule("body", applicable, value), - "body-max-line-length": (applicable, value) => `Each line of the body should ${applicable} have ${value} characters or less.`, - "body-min-length": (applicable, value) => llmReadableRules.minLengthRule("body", applicable, value), - "footer-case": (applicable, value) => llmReadableRules.caseRule("footer", applicable, value), - "footer-empty": (applicable) => llmReadableRules.emptyRule("footer", applicable, void 0), - "footer-leading-blank": (applicable) => llmReadableRules.blankline("footer", applicable, void 0), - "footer-max-length": (applicable, value) => llmReadableRules.maxLengthRule("footer", applicable, value), - "footer-max-line-length": (applicable, value) => `Each line of the footer should ${applicable} have ${value} characters or less.`, - "footer-min-length": (applicable, value) => llmReadableRules.minLengthRule("footer", applicable, value), - "header-case": (applicable, value) => llmReadableRules.caseRule("header", applicable, value), - "header-full-stop": (applicable, value) => llmReadableRules.fullStopRule("header", applicable, value), - "header-max-length": (applicable, value) => llmReadableRules.maxLengthRule("header", applicable, value), - "header-min-length": (applicable, value) => llmReadableRules.minLengthRule("header", applicable, value), - "references-empty": (applicable) => llmReadableRules.emptyRule("references section", applicable, void 0), - "scope-case": (applicable, value) => llmReadableRules.caseRule("scope", applicable, value), - "scope-empty": (applicable) => llmReadableRules.emptyRule("scope", applicable, void 0), - "scope-enum": (applicable, value) => llmReadableRules.enumRule("type", applicable, value), - "scope-max-length": (applicable, value) => llmReadableRules.maxLengthRule("scope", applicable, value), - "scope-min-length": (applicable, value) => llmReadableRules.minLengthRule("scope", applicable, value), - "signed-off-by": (applicable, value) => `The commit message should ${applicable} have a "Signed-off-by" line with the value "${value}".`, - "subject-case": (applicable, value) => llmReadableRules.caseRule("subject", applicable, value), - "subject-empty": (applicable) => llmReadableRules.emptyRule("subject", applicable, void 0), - "subject-full-stop": (applicable, value) => llmReadableRules.fullStopRule("subject", applicable, value), - "subject-max-length": (applicable, value) => llmReadableRules.maxLengthRule("subject", applicable, value), - "subject-min-length": (applicable, value) => llmReadableRules.minLengthRule("subject", applicable, value), - "type-case": (applicable, value) => llmReadableRules.caseRule("type", applicable, value), - "type-empty": (applicable) => llmReadableRules.emptyRule("type", applicable, void 0), - "type-enum": (applicable, value, prompt) => llmReadableRules.enumTypeRule("type", applicable, value, prompt), - "type-max-length": (applicable, value) => llmReadableRules.maxLengthRule("type", applicable, value), - "type-min-length": (applicable, value) => llmReadableRules.minLengthRule("type", applicable, value) -}; -var getPrompt = (ruleName, ruleConfig, prompt) => { - const [severity, applicable, value] = ruleConfig; - if (severity === import_types.RuleConfigSeverity.Disabled) +var toArray = (thing) => { + if (!thing) return null; - const promptFn = rulesPrompts[ruleName]; - if (promptFn) { - return promptFn(applicable, value, prompt); - } - ce(`${source_default.red("\u2716")} No prompt handler for rule "${ruleName}".`); - return `Please manualy set the prompt for rule "${ruleName}".`; -}; -var inferPromptsFromCommitlintConfig = (config8) => { - const { rules, prompt } = config8; - if (!rules) - return []; - return Object.keys(rules).map( - (ruleName) => getPrompt(ruleName, rules[ruleName], prompt) - ).filter((prompt2) => prompt2 !== null); -}; -var STRUCTURE_OF_COMMIT = ` -- Header of commit is composed of type, scope, subject: (): -- Description of commit is composed of body and footer (optional): -`; -var GEN_COMMITLINT_CONSISTENCY_PROMPT = (prompts) => [ - { - role: import_openai.ChatCompletionRequestMessageRoleEnum.Assistant, - content: `${IDENTITY} Your mission is to create clean and comprehensive commit messages for two different changes in a single codebase and output them in the provided JSON format: one for a bug fix and another for a new feature. - -Here are the specific requirements and conventions that should be strictly followed: - -Commit Message Conventions: -- The commit message consists of three parts: Header, Body, and Footer. -- Header: - - Format: \`(): \` -- ${prompts.join("\n- ")} - -JSON Output Format: -- The JSON output should contain the commit messages for a bug fix and a new feature in the following format: -\`\`\`json -{ - "localLanguage": "${translation.localLanguage}", - "commitFix": "
", - "commitFeat": "
", - "commitDescription": "" -} -\`\`\` -- The "commitDescription" should not include the commit message\u2019s header, only the description. -- Description should not be more than 74 characters. - -Additional Details: -- Changing the variable 'port' to uppercase 'PORT' is considered a bug fix. -- Allowing the server to listen on a port specified through the environment variable is considered a new feature. - -Example Git Diff is to follow:` - }, - INIT_DIFF_PROMPT -]; -var INIT_MAIN_PROMPT = (language, prompts) => ({ - role: import_openai.ChatCompletionRequestMessageRoleEnum.System, - content: `${IDENTITY} Your mission is to create clean and comprehensive commit messages in the given @commitlint convention and explain WHAT were the changes and WHY the changes were done. I'll send you an output of 'git diff --staged' command, and you convert it into a commit message. -${config2?.OCO_EMOJI ? "Use GitMoji convention to preface the commit." : "Do not preface the commit with anything."} -${config2?.OCO_DESCRIPTION ? `Add a short description of WHY the changes are done after the commit message. Don't start it with "This commit", just describe the changes.` : "Don't add any descriptions to the commit, only commit message."} -Use the present tense. Use ${language} to answer. - -You will strictly follow the following conventions to generate the content of the commit message: -- ${prompts.join("\n- ")} - -The conventions refers to the following structure of commit message: -${STRUCTURE_OF_COMMIT} - - ` -}); -var commitlintPrompts = { - INIT_MAIN_PROMPT, - GEN_COMMITLINT_CONSISTENCY_PROMPT -}; - -// src/modules/commitlint/pwd-commitlint.ts -var import_path2 = __toESM(require("path"), 1); -var nodeModulesPath = import_path2.default.join( - process.env.PWD || process.cwd(), - "node_modules", - "@commitlint", - "load" -); -var getCommitLintPWDConfig = async () => { - const load = require(nodeModulesPath).default; - if (load && typeof load === "function") { - return await load(); + if (isArray(thing)) + return thing; + let i2 = thing.length; + if (!isNumber(i2)) + return null; + const arr = new Array(i2); + while (i2-- > 0) { + arr[i2] = thing[i2]; } - return null; + return arr; }; - -// src/modules/commitlint/utils.ts -var import_promises = __toESM(require("fs/promises"), 1); -var removeDoubleNewlines = (input) => { - const pattern = /\\n\\n/g; - if (pattern.test(input)) { - const newInput = input.replace(pattern, ""); - return removeDoubleNewlines(newInput); +var isTypedArray = ((TypedArray) => { + return (thing) => { + return TypedArray && thing instanceof TypedArray; + }; +})(typeof Uint8Array !== "undefined" && getPrototypeOf(Uint8Array)); +var forEachEntry = (obj, fn) => { + const generator = obj && obj[Symbol.iterator]; + const iterator = generator.call(obj); + let result; + while ((result = iterator.next()) && !result.done) { + const pair = result.value; + fn.call(obj, pair[0], pair[1]); } - return input; }; -var commitlintLLMConfigExists = async () => { - let exists; - try { - await import_promises.default.access(COMMITLINT_LLM_CONFIG_PATH); - exists = true; - } catch (e2) { - exists = false; +var matchAll = (regExp, str) => { + let matches; + const arr = []; + while ((matches = regExp.exec(str)) !== null) { + arr.push(matches); } - return exists; + return arr; }; -var writeCommitlintLLMConfig = async (commitlintLLMConfig) => { - await import_promises.default.writeFile( - COMMITLINT_LLM_CONFIG_PATH, - JSON.stringify(commitlintLLMConfig, null, 2) +var isHTMLForm = kindOfTest("HTMLFormElement"); +var toCamelCase = (str) => { + return str.toLowerCase().replace( + /[-_\s]([a-z\d])(\w*)/g, + function replacer(m4, p1, p22) { + return p1.toUpperCase() + p22; + } ); }; -var getCommitlintLLMConfig = async () => { - const content = await import_promises.default.readFile(COMMITLINT_LLM_CONFIG_PATH); - const commitLintLLMConfig = JSON.parse( - content.toString() - ); - return commitLintLLMConfig; +var hasOwnProperty = (({ hasOwnProperty: hasOwnProperty2 }) => (obj, prop) => hasOwnProperty2.call(obj, prop))(Object.prototype); +var isRegExp = kindOfTest("RegExp"); +var reduceDescriptors = (obj, reducer) => { + const descriptors3 = Object.getOwnPropertyDescriptors(obj); + const reducedDescriptors = {}; + forEach(descriptors3, (descriptor, name) => { + if (reducer(descriptor, name, obj) !== false) { + reducedDescriptors[name] = descriptor; + } + }); + Object.defineProperties(obj, reducedDescriptors); }; - -// node_modules/axios/lib/helpers/bind.js -function bind(fn, thisArg) { - return function wrap() { - return fn.apply(thisArg, arguments); - }; -} - -// node_modules/axios/lib/utils.js -var { toString } = Object.prototype; -var { getPrototypeOf } = Object; -var kindOf = ((cache) => (thing) => { - const str = toString.call(thing); - return cache[str] || (cache[str] = str.slice(8, -1).toLowerCase()); -})(/* @__PURE__ */ Object.create(null)); -var kindOfTest = (type) => { - type = type.toLowerCase(); - return (thing) => kindOf(thing) === type; -}; -var typeOfTest = (type) => (thing) => typeof thing === type; -var { isArray } = Array; -var isUndefined = typeOfTest("undefined"); -function isBuffer(val) { - return val !== null && !isUndefined(val) && val.constructor !== null && !isUndefined(val.constructor) && isFunction(val.constructor.isBuffer) && val.constructor.isBuffer(val); -} -var isArrayBuffer = kindOfTest("ArrayBuffer"); -function isArrayBufferView(val) { - let result; - if (typeof ArrayBuffer !== "undefined" && ArrayBuffer.isView) { - result = ArrayBuffer.isView(val); - } else { - result = val && val.buffer && isArrayBuffer(val.buffer); - } - return result; -} -var isString = typeOfTest("string"); -var isFunction = typeOfTest("function"); -var isNumber = typeOfTest("number"); -var isObject = (thing) => thing !== null && typeof thing === "object"; -var isBoolean = (thing) => thing === true || thing === false; -var isPlainObject = (val) => { - if (kindOf(val) !== "object") { - return false; - } - const prototype3 = getPrototypeOf(val); - return (prototype3 === null || prototype3 === Object.prototype || Object.getPrototypeOf(prototype3) === null) && !(Symbol.toStringTag in val) && !(Symbol.iterator in val); -}; -var isDate = kindOfTest("Date"); -var isFile = kindOfTest("File"); -var isBlob = kindOfTest("Blob"); -var isFileList = kindOfTest("FileList"); -var isStream2 = (val) => isObject(val) && isFunction(val.pipe); -var isFormData = (thing) => { - const pattern = "[object FormData]"; - return thing && (typeof FormData === "function" && thing instanceof FormData || toString.call(thing) === pattern || isFunction(thing.toString) && thing.toString() === pattern); -}; -var isURLSearchParams = kindOfTest("URLSearchParams"); -var trim = (str) => str.trim ? str.trim() : str.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, ""); -function forEach(obj, fn, { allOwnKeys = false } = {}) { - if (obj === null || typeof obj === "undefined") { - return; - } - let i2; - let l; - if (typeof obj !== "object") { - obj = [obj]; - } - if (isArray(obj)) { - for (i2 = 0, l = obj.length; i2 < l; i2++) { - fn.call(null, obj[i2], i2, obj); - } - } else { - const keys = allOwnKeys ? Object.getOwnPropertyNames(obj) : Object.keys(obj); - const len = keys.length; - let key; - for (i2 = 0; i2 < len; i2++) { - key = keys[i2]; - fn.call(null, obj[key], key, obj); - } - } -} -function findKey(obj, key) { - key = key.toLowerCase(); - const keys = Object.keys(obj); - let i2 = keys.length; - let _key; - while (i2-- > 0) { - _key = keys[i2]; - if (key === _key.toLowerCase()) { - return _key; - } - } - return null; -} -var _global = (() => { - if (typeof globalThis !== "undefined") - return globalThis; - return typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : global; -})(); -var isContextDefined = (context) => !isUndefined(context) && context !== _global; -function merge() { - const { caseless } = isContextDefined(this) && this || {}; - const result = {}; - const assignValue = (val, key) => { - const targetKey = caseless && findKey(result, key) || key; - if (isPlainObject(result[targetKey]) && isPlainObject(val)) { - result[targetKey] = merge(result[targetKey], val); - } else if (isPlainObject(val)) { - result[targetKey] = merge({}, val); - } else if (isArray(val)) { - result[targetKey] = val.slice(); - } else { - result[targetKey] = val; - } - }; - for (let i2 = 0, l = arguments.length; i2 < l; i2++) { - arguments[i2] && forEach(arguments[i2], assignValue); - } - return result; -} -var extend = (a2, b6, thisArg, { allOwnKeys } = {}) => { - forEach(b6, (val, key) => { - if (thisArg && isFunction(val)) { - a2[key] = bind(val, thisArg); - } else { - a2[key] = val; - } - }, { allOwnKeys }); - return a2; -}; -var stripBOM = (content) => { - if (content.charCodeAt(0) === 65279) { - content = content.slice(1); - } - return content; -}; -var inherits = (constructor, superConstructor, props, descriptors3) => { - constructor.prototype = Object.create(superConstructor.prototype, descriptors3); - constructor.prototype.constructor = constructor; - Object.defineProperty(constructor, "super", { - value: superConstructor.prototype - }); - props && Object.assign(constructor.prototype, props); -}; -var toFlatObject = (sourceObj, destObj, filter2, propFilter) => { - let props; - let i2; - let prop; - const merged = {}; - destObj = destObj || {}; - if (sourceObj == null) - return destObj; - do { - props = Object.getOwnPropertyNames(sourceObj); - i2 = props.length; - while (i2-- > 0) { - prop = props[i2]; - if ((!propFilter || propFilter(prop, sourceObj, destObj)) && !merged[prop]) { - destObj[prop] = sourceObj[prop]; - merged[prop] = true; - } - } - sourceObj = filter2 !== false && getPrototypeOf(sourceObj); - } while (sourceObj && (!filter2 || filter2(sourceObj, destObj)) && sourceObj !== Object.prototype); - return destObj; -}; -var endsWith = (str, searchString, position) => { - str = String(str); - if (position === void 0 || position > str.length) { - position = str.length; - } - position -= searchString.length; - const lastIndex = str.indexOf(searchString, position); - return lastIndex !== -1 && lastIndex === position; -}; -var toArray = (thing) => { - if (!thing) - return null; - if (isArray(thing)) - return thing; - let i2 = thing.length; - if (!isNumber(i2)) - return null; - const arr = new Array(i2); - while (i2-- > 0) { - arr[i2] = thing[i2]; - } - return arr; -}; -var isTypedArray = ((TypedArray) => { - return (thing) => { - return TypedArray && thing instanceof TypedArray; - }; -})(typeof Uint8Array !== "undefined" && getPrototypeOf(Uint8Array)); -var forEachEntry = (obj, fn) => { - const generator = obj && obj[Symbol.iterator]; - const iterator = generator.call(obj); - let result; - while ((result = iterator.next()) && !result.done) { - const pair = result.value; - fn.call(obj, pair[0], pair[1]); - } -}; -var matchAll = (regExp, str) => { - let matches; - const arr = []; - while ((matches = regExp.exec(str)) !== null) { - arr.push(matches); - } - return arr; -}; -var isHTMLForm = kindOfTest("HTMLFormElement"); -var toCamelCase = (str) => { - return str.toLowerCase().replace( - /[-_\s]([a-z\d])(\w*)/g, - function replacer(m4, p1, p22) { - return p1.toUpperCase() + p22; - } - ); -}; -var hasOwnProperty = (({ hasOwnProperty: hasOwnProperty2 }) => (obj, prop) => hasOwnProperty2.call(obj, prop))(Object.prototype); -var isRegExp = kindOfTest("RegExp"); -var reduceDescriptors = (obj, reducer) => { - const descriptors3 = Object.getOwnPropertyDescriptors(obj); - const reducedDescriptors = {}; - forEach(descriptors3, (descriptor, name) => { - if (reducer(descriptor, name, obj) !== false) { - reducedDescriptors[name] = descriptor; - } - }); - Object.defineProperties(obj, reducedDescriptors); -}; -var freezeMethods = (obj) => { - reduceDescriptors(obj, (descriptor, name) => { - if (isFunction(obj) && ["arguments", "caller", "callee"].indexOf(name) !== -1) { - return false; - } - const value = obj[name]; - if (!isFunction(value)) - return; - descriptor.enumerable = false; - if ("writable" in descriptor) { - descriptor.writable = false; - return; - } - if (!descriptor.set) { - descriptor.set = () => { - throw Error("Can not rewrite read-only method '" + name + "'"); - }; - } - }); -}; -var toObjectSet = (arrayOrString, delimiter) => { - const obj = {}; - const define = (arr) => { - arr.forEach((value) => { - obj[value] = true; - }); +var freezeMethods = (obj) => { + reduceDescriptors(obj, (descriptor, name) => { + if (isFunction(obj) && ["arguments", "caller", "callee"].indexOf(name) !== -1) { + return false; + } + const value = obj[name]; + if (!isFunction(value)) + return; + descriptor.enumerable = false; + if ("writable" in descriptor) { + descriptor.writable = false; + return; + } + if (!descriptor.set) { + descriptor.set = () => { + throw Error("Can not rewrite read-only method '" + name + "'"); + }; + } + }); +}; +var toObjectSet = (arrayOrString, delimiter) => { + const obj = {}; + const define = (arr) => { + arr.forEach((value) => { + obj[value] = true; + }); }; isArray(arrayOrString) ? define(arrayOrString) : define(String(arrayOrString).split(delimiter)); return obj; @@ -21625,260 +21211,760 @@ var Axios = class { } } try { - promise = dispatchRequest.call(this, newConfig); + promise = dispatchRequest.call(this, newConfig); + } catch (error) { + return Promise.reject(error); + } + i2 = 0; + len = responseInterceptorChain.length; + while (i2 < len) { + promise = promise.then(responseInterceptorChain[i2++], responseInterceptorChain[i2++]); + } + return promise; + } + getUri(config8) { + config8 = mergeConfig(this.defaults, config8); + const fullPath = buildFullPath(config8.baseURL, config8.url); + return buildURL(fullPath, config8.params, config8.paramsSerializer); + } +}; +utils_default.forEach(["delete", "get", "head", "options"], function forEachMethodNoData2(method) { + Axios.prototype[method] = function(url3, config8) { + return this.request(mergeConfig(config8 || {}, { + method, + url: url3, + data: (config8 || {}).data + })); + }; +}); +utils_default.forEach(["post", "put", "patch"], function forEachMethodWithData2(method) { + function generateHTTPMethod(isForm) { + return function httpMethod(url3, data, config8) { + return this.request(mergeConfig(config8 || {}, { + method, + headers: isForm ? { + "Content-Type": "multipart/form-data" + } : {}, + url: url3, + data + })); + }; + } + Axios.prototype[method] = generateHTTPMethod(); + Axios.prototype[method + "Form"] = generateHTTPMethod(true); +}); +var Axios_default = Axios; + +// node_modules/axios/lib/cancel/CancelToken.js +var CancelToken = class { + constructor(executor) { + if (typeof executor !== "function") { + throw new TypeError("executor must be a function."); + } + let resolvePromise; + this.promise = new Promise(function promiseExecutor(resolve) { + resolvePromise = resolve; + }); + const token = this; + this.promise.then((cancel) => { + if (!token._listeners) + return; + let i2 = token._listeners.length; + while (i2-- > 0) { + token._listeners[i2](cancel); + } + token._listeners = null; + }); + this.promise.then = (onfulfilled) => { + let _resolve; + const promise = new Promise((resolve) => { + token.subscribe(resolve); + _resolve = resolve; + }).then(onfulfilled); + promise.cancel = function reject() { + token.unsubscribe(_resolve); + }; + return promise; + }; + executor(function cancel(message, config8, request) { + if (token.reason) { + return; + } + token.reason = new CanceledError_default(message, config8, request); + resolvePromise(token.reason); + }); + } + throwIfRequested() { + if (this.reason) { + throw this.reason; + } + } + subscribe(listener) { + if (this.reason) { + listener(this.reason); + return; + } + if (this._listeners) { + this._listeners.push(listener); + } else { + this._listeners = [listener]; + } + } + unsubscribe(listener) { + if (!this._listeners) { + return; + } + const index = this._listeners.indexOf(listener); + if (index !== -1) { + this._listeners.splice(index, 1); + } + } + static source() { + let cancel; + const token = new CancelToken(function executor(c3) { + cancel = c3; + }); + return { + token, + cancel + }; + } +}; +var CancelToken_default = CancelToken; + +// node_modules/axios/lib/helpers/spread.js +function spread(callback) { + return function wrap(arr) { + return callback.apply(null, arr); + }; +} + +// node_modules/axios/lib/helpers/isAxiosError.js +function isAxiosError(payload) { + return utils_default.isObject(payload) && payload.isAxiosError === true; +} + +// node_modules/axios/lib/helpers/HttpStatusCode.js +var HttpStatusCode = { + Continue: 100, + SwitchingProtocols: 101, + Processing: 102, + EarlyHints: 103, + Ok: 200, + Created: 201, + Accepted: 202, + NonAuthoritativeInformation: 203, + NoContent: 204, + ResetContent: 205, + PartialContent: 206, + MultiStatus: 207, + AlreadyReported: 208, + ImUsed: 226, + MultipleChoices: 300, + MovedPermanently: 301, + Found: 302, + SeeOther: 303, + NotModified: 304, + UseProxy: 305, + Unused: 306, + TemporaryRedirect: 307, + PermanentRedirect: 308, + BadRequest: 400, + Unauthorized: 401, + PaymentRequired: 402, + Forbidden: 403, + NotFound: 404, + MethodNotAllowed: 405, + NotAcceptable: 406, + ProxyAuthenticationRequired: 407, + RequestTimeout: 408, + Conflict: 409, + Gone: 410, + LengthRequired: 411, + PreconditionFailed: 412, + PayloadTooLarge: 413, + UriTooLong: 414, + UnsupportedMediaType: 415, + RangeNotSatisfiable: 416, + ExpectationFailed: 417, + ImATeapot: 418, + MisdirectedRequest: 421, + UnprocessableEntity: 422, + Locked: 423, + FailedDependency: 424, + TooEarly: 425, + UpgradeRequired: 426, + PreconditionRequired: 428, + TooManyRequests: 429, + RequestHeaderFieldsTooLarge: 431, + UnavailableForLegalReasons: 451, + InternalServerError: 500, + NotImplemented: 501, + BadGateway: 502, + ServiceUnavailable: 503, + GatewayTimeout: 504, + HttpVersionNotSupported: 505, + VariantAlsoNegotiates: 506, + InsufficientStorage: 507, + LoopDetected: 508, + NotExtended: 510, + NetworkAuthenticationRequired: 511 +}; +Object.entries(HttpStatusCode).forEach(([key, value]) => { + HttpStatusCode[value] = key; +}); +var HttpStatusCode_default = HttpStatusCode; + +// node_modules/axios/lib/axios.js +function createInstance(defaultConfig) { + const context = new Axios_default(defaultConfig); + const instance = bind(Axios_default.prototype.request, context); + utils_default.extend(instance, Axios_default.prototype, context, { allOwnKeys: true }); + utils_default.extend(instance, context, null, { allOwnKeys: true }); + instance.create = function create(instanceConfig) { + return createInstance(mergeConfig(defaultConfig, instanceConfig)); + }; + return instance; +} +var axios = createInstance(defaults_default); +axios.Axios = Axios_default; +axios.CanceledError = CanceledError_default; +axios.CancelToken = CancelToken_default; +axios.isCancel = isCancel; +axios.VERSION = VERSION; +axios.toFormData = toFormData_default; +axios.AxiosError = AxiosError_default; +axios.Cancel = axios.CanceledError; +axios.all = function all(promises) { + return Promise.all(promises); +}; +axios.spread = spread; +axios.isAxiosError = isAxiosError; +axios.mergeConfig = mergeConfig; +axios.AxiosHeaders = AxiosHeaders_default; +axios.formToJSON = (thing) => formDataToJSON_default(utils_default.isHTMLForm(thing) ? new FormData(thing) : thing); +axios.HttpStatusCode = HttpStatusCode_default; +axios.default = axios; +var axios_default = axios; + +// node_modules/axios/index.js +var { + Axios: Axios2, + AxiosError: AxiosError2, + CanceledError: CanceledError2, + isCancel: isCancel2, + CancelToken: CancelToken2, + VERSION: VERSION2, + all: all2, + Cancel, + isAxiosError: isAxiosError2, + spread: spread2, + toFormData: toFormData2, + AxiosHeaders: AxiosHeaders2, + HttpStatusCode: HttpStatusCode2, + formToJSON, + mergeConfig: mergeConfig2 +} = axios_default; + +// src/engine/ollama.ts +var OllamaAi = class { + constructor(model = "mistral", basePath2 = "http://localhost:11434") { + this.model = model; + this.basePath = basePath2; + } + async generateCommitMessage(messages) { + let prompt = messages.map((x4) => x4.content).join("\n"); + prompt += "Summarize above git diff in 10 words or less"; + const url3 = `${this.basePath}/api/generate`; + const p4 = { + model: this.model, + prompt, + stream: false + }; + try { + const response = await axios_default.post(url3, p4, { + headers: { + "Content-Type": "application/json" + } + }); + return response.data?.response; + } catch (err) { + const message = err.response?.data?.error ?? err.message; + throw new Error("local model issues. details: " + message); + } + } + static async validateBaseUrl(baseUrl) { + baseUrl = baseUrl.trim().replace(/\/$/, ""); + const url3 = `${baseUrl}/api/tags`; + try { + const response = await axios_default.get(url3, { + headers: { + "Content-Type": "application/json" + } + }); + return response.status === 200; + } catch (err) { + const message = err.response?.data?.error ?? err.message; + console.error("Failed to connect to api: " + err); + throw new Error("Failed to connect to api: " + message); + } + } + static async validateModel(model, baseUrl = "http://localhost:11434") { + const url3 = `${baseUrl}/api/show`; + const p4 = { + name: model + }; + try { + const response = await axios_default.post(url3, p4, { + headers: { + "Content-Type": "application/json" + } + }); + return response.status === 200; + } catch (err) { + const message = err.response?.data?.error ?? err.message; + throw new Error("local model issues. details: " + message); + } + } +}; +var ollamaAi = new OllamaAi(); + +// src/commands/config.ts +dotenv.config(); +var DEFAULT_MODEL_TOKEN_LIMIT = 4096; +var validateConfig = (key, condition, validationMessage) => { + if (!condition) { + ce( + `${source_default.red("\u2716")} Unsupported config key ${key}: ${validationMessage}` + ); + process.exit(1); + } +}; +var configValidators = { + ["OCO_OPENAI_API_KEY" /* OCO_OPENAI_API_KEY */](value, config8 = {}) { + validateConfig("API_KEY", value || config8.OCO_AI_PROVIDER == "ollama", "You need to provide an API key"); + validateConfig( + "OCO_OPENAI_API_KEY" /* OCO_OPENAI_API_KEY */, + value.startsWith("sk-"), + 'Must start with "sk-"' + ); + validateConfig( + "OCO_OPENAI_API_KEY" /* OCO_OPENAI_API_KEY */, + config8["OCO_OPENAI_BASE_PATH" /* OCO_OPENAI_BASE_PATH */] || value.length === 51, + "Must be 51 characters long" + ); + return value; + }, + ["OCO_DESCRIPTION" /* OCO_DESCRIPTION */](value) { + validateConfig( + "OCO_DESCRIPTION" /* OCO_DESCRIPTION */, + typeof value === "boolean", + "Must be true or false" + ); + return value; + }, + ["OCO_OPENAI_MAX_TOKENS" /* OCO_OPENAI_MAX_TOKENS */](value) { + if (typeof value === "string") { + value = parseInt(value); + validateConfig( + "OCO_OPENAI_MAX_TOKENS" /* OCO_OPENAI_MAX_TOKENS */, + !isNaN(value), + "Must be a number" + ); + } + validateConfig( + "OCO_OPENAI_MAX_TOKENS" /* OCO_OPENAI_MAX_TOKENS */, + value ? typeof value === "number" : void 0, + "Must be a number" + ); + return value; + }, + ["OCO_EMOJI" /* OCO_EMOJI */](value) { + validateConfig( + "OCO_EMOJI" /* OCO_EMOJI */, + typeof value === "boolean", + "Must be true or false" + ); + return value; + }, + ["OCO_LANGUAGE" /* OCO_LANGUAGE */](value) { + validateConfig( + "OCO_LANGUAGE" /* OCO_LANGUAGE */, + getI18nLocal(value), + `${value} is not supported yet` + ); + return getI18nLocal(value); + }, + ["OCO_OPENAI_BASE_PATH" /* OCO_OPENAI_BASE_PATH */](value) { + validateConfig( + "OCO_OPENAI_BASE_PATH" /* OCO_OPENAI_BASE_PATH */, + typeof value === "string", + "Must be string" + ); + return value; + }, + ["OCO_MODEL" /* OCO_MODEL */](value) { + validateConfig( + "OCO_MODEL" /* OCO_MODEL */, + [ + "gpt-3.5-turbo", + "gpt-4", + "gpt-3.5-turbo-16k", + "gpt-3.5-turbo-0613" + ].includes(value), + `${value} is not supported yet, use 'gpt-4', 'gpt-3.5-turbo-16k' (default), 'gpt-3.5-turbo-0613' or 'gpt-3.5-turbo'` + ); + return value; + }, + ["OCO_MESSAGE_TEMPLATE_PLACEHOLDER" /* OCO_MESSAGE_TEMPLATE_PLACEHOLDER */](value) { + validateConfig( + "OCO_MESSAGE_TEMPLATE_PLACEHOLDER" /* OCO_MESSAGE_TEMPLATE_PLACEHOLDER */, + value.startsWith("$"), + `${value} must start with $, for example: '$msg'` + ); + return value; + }, + ["OCO_PROMPT_MODULE" /* OCO_PROMPT_MODULE */](value) { + validateConfig( + "OCO_PROMPT_MODULE" /* OCO_PROMPT_MODULE */, + ["conventional-commit", "@commitlint"].includes(value), + `${value} is not supported yet, use '@commitlint' or 'conventional-commit' (default)` + ); + return value; + }, + ["OCO_AI_PROVIDER" /* OCO_AI_PROVIDER */](value) { + validateConfig( + "OCO_AI_PROVIDER" /* OCO_AI_PROVIDER */, + [ + "", + "openai", + "ollama" + ].includes(value), + `${value} is not supported yet, use 'ollama' or 'openai' (default)` + ); + return value; + }, + async ["OCO_OLLAMA_BASE_PATH" /* OCO_OLLAMA_BASE_PATH */](value) { + validateConfig( + "OCO_OLLAMA_BASE_PATH" /* OCO_OLLAMA_BASE_PATH */, + typeof value === "string", + "Must be string" + ); + validateConfig( + "OCO_OLLAMA_BASE_PATH" /* OCO_OLLAMA_BASE_PATH */, + await OllamaAi.validateBaseUrl(value), + "Must be valid ollama url" + ); + return value; + }, + async ["OCO_OLLAMA_MODEL" /* OCO_OLLAMA_MODEL */](value, config8 = {}) { + validateConfig( + "OCO_OLLAMA_MODEL" /* OCO_OLLAMA_MODEL */, + await OllamaAi.validateModel(value, config8?.OCO_OLLAMA_BASE_PATH), + `${value} is not supported yet, use 'mistral' (default)` + ); + return value; + } +}; +var configPath = (0, import_path.join)((0, import_os.homedir)(), ".opencommit"); +var getConfig = () => { + const configFromEnv = { + OCO_OPENAI_API_KEY: process.env.OCO_OPENAI_API_KEY, + OCO_OPENAI_MAX_TOKENS: process.env.OCO_OPENAI_MAX_TOKENS ? Number(process.env.OCO_OPENAI_MAX_TOKENS) : void 0, + OCO_OPENAI_BASE_PATH: process.env.OCO_OPENAI_BASE_PATH, + OCO_DESCRIPTION: process.env.OCO_DESCRIPTION === "true" ? true : false, + OCO_EMOJI: process.env.OCO_EMOJI === "true" ? true : false, + OCO_MODEL: process.env.OCO_MODEL || "gpt-3.5-turbo-16k", + OCO_LANGUAGE: process.env.OCO_LANGUAGE || "en", + OCO_MESSAGE_TEMPLATE_PLACEHOLDER: process.env.OCO_MESSAGE_TEMPLATE_PLACEHOLDER || "$msg", + OCO_PROMPT_MODULE: process.env.OCO_PROMPT_MODULE || "conventional-commit", + OCO_AI_PROVIDER: process.env.OCO_AI_PROVIDER || "openai", + OCO_OLLAMA_BASE_PATH: process.env.OCO_OLLAMA_BASE_PATH || "http://localhost:11434", + OCO_OLLAMA_MODEL: process.env.OCO_OLLAMA_MODEL || "mistral" + }; + const configExists = (0, import_fs.existsSync)(configPath); + if (!configExists) + return configFromEnv; + const configFile = (0, import_fs.readFileSync)(configPath, "utf8"); + const config8 = (0, import_ini.parse)(configFile); + for (const configKey of Object.keys(config8)) { + if (!config8[configKey] || ["null", "undefined"].includes(config8[configKey])) { + config8[configKey] = void 0; + continue; + } + try { + const validator = configValidators[configKey]; + const validValue = validator( + config8[configKey] ?? configFromEnv[configKey], + config8 + ); + config8[configKey] = validValue; } catch (error) { - return Promise.reject(error); - } - i2 = 0; - len = responseInterceptorChain.length; - while (i2 < len) { - promise = promise.then(responseInterceptorChain[i2++], responseInterceptorChain[i2++]); + ce( + `'${configKey}' name is invalid, it should be either 'OCO_${configKey.toUpperCase()}' or it doesn't exist.` + ); + ce( + `Manually fix the '.env' file or global '~/.opencommit' config file.` + ); + process.exit(1); } - return promise; - } - getUri(config8) { - config8 = mergeConfig(this.defaults, config8); - const fullPath = buildFullPath(config8.baseURL, config8.url); - return buildURL(fullPath, config8.params, config8.paramsSerializer); } + return config8; }; -utils_default.forEach(["delete", "get", "head", "options"], function forEachMethodNoData2(method) { - Axios.prototype[method] = function(url3, config8) { - return this.request(mergeConfig(config8 || {}, { - method, - url: url3, - data: (config8 || {}).data - })); - }; -}); -utils_default.forEach(["post", "put", "patch"], function forEachMethodWithData2(method) { - function generateHTTPMethod(isForm) { - return function httpMethod(url3, data, config8) { - return this.request(mergeConfig(config8 || {}, { - method, - headers: isForm ? { - "Content-Type": "multipart/form-data" - } : {}, - url: url3, - data - })); - }; - } - Axios.prototype[method] = generateHTTPMethod(); - Axios.prototype[method + "Form"] = generateHTTPMethod(true); -}); -var Axios_default = Axios; - -// node_modules/axios/lib/cancel/CancelToken.js -var CancelToken = class { - constructor(executor) { - if (typeof executor !== "function") { - throw new TypeError("executor must be a function."); +var setConfig = (keyValues) => { + const config8 = getConfig() || {}; + for (const [configKey, configValue] of keyValues) { + if (!configValidators.hasOwnProperty(configKey)) { + throw new Error(`Unsupported config key: ${configKey}`); } - let resolvePromise; - this.promise = new Promise(function promiseExecutor(resolve) { - resolvePromise = resolve; - }); - const token = this; - this.promise.then((cancel) => { - if (!token._listeners) - return; - let i2 = token._listeners.length; - while (i2-- > 0) { - token._listeners[i2](cancel); - } - token._listeners = null; - }); - this.promise.then = (onfulfilled) => { - let _resolve; - const promise = new Promise((resolve) => { - token.subscribe(resolve); - _resolve = resolve; - }).then(onfulfilled); - promise.cancel = function reject() { - token.unsubscribe(_resolve); - }; - return promise; - }; - executor(function cancel(message, config8, request) { - if (token.reason) { - return; - } - token.reason = new CanceledError_default(message, config8, request); - resolvePromise(token.reason); - }); - } - throwIfRequested() { - if (this.reason) { - throw this.reason; + let parsedConfigValue; + try { + parsedConfigValue = JSON.parse(configValue); + } catch (error) { + parsedConfigValue = configValue; } + const validValue = configValidators[configKey](parsedConfigValue, config8); + config8[configKey] = validValue; } - subscribe(listener) { - if (this.reason) { - listener(this.reason); - return; - } - if (this._listeners) { - this._listeners.push(listener); - } else { - this._listeners = [listener]; + (0, import_fs.writeFileSync)(configPath, (0, import_ini.stringify)(config8), "utf8"); + ce(`${source_default.green("\u2714")} Config successfully set`); +}; +var configCommand = G3( + { + name: "config" /* config */, + parameters: ["", ""] + }, + async (argv) => { + ae("opencommit \u2014 config"); + try { + const { mode: mode2, keyValues } = argv._; + if (mode2 === "get" /* get */) { + const config8 = getConfig() || {}; + for (const key of keyValues) { + ce(`${key}=${config8[key]}`); + } + } else if (mode2 === "set" /* set */) { + await setConfig( + keyValues.map((keyValue) => keyValue.split("=")) + ); + } else { + throw new Error( + `Unsupported mode: ${mode2}. Valid modes are: "set" and "get"` + ); + } + } catch (error) { + ce(`${source_default.red("\u2716")} ${error}`); + process.exit(1); } } - unsubscribe(listener) { - if (!this._listeners) { - return; - } - const index = this._listeners.indexOf(listener); - if (index !== -1) { - this._listeners.splice(index, 1); - } +); + +// src/prompts.ts +var import_openai3 = __toESM(require_dist(), 1); + +// src/modules/commitlint/constants.ts +var COMMITLINT_LLM_CONFIG_PATH = `${process.env.PWD}/.opencommit-commitlint`; + +// src/modules/commitlint/crypto.ts +var import_crypto = __toESM(require("crypto"), 1); +var computeHash = async (content, algorithm = "sha256") => { + try { + const hash = import_crypto.default.createHash(algorithm); + hash.update(content); + return hash.digest("hex"); + } catch (error) { + console.error("Error while computing hash:", error); + throw error; } - static source() { - let cancel; - const token = new CancelToken(function executor(c3) { - cancel = c3; - }); - return { - token, - cancel - }; +}; + +// src/modules/commitlint/prompts.ts +var import_openai = __toESM(require_dist(), 1); +var import_types = __toESM(require_lib(), 1); +var config2 = getConfig(); +var translation = i18n[config2?.OCO_LANGUAGE || "en"]; +var getTypeRuleExtraDescription = (type, prompt) => prompt?.questions?.type?.enum?.[type]?.description; +var llmReadableRules = { + blankline: (key, applicable) => `There should ${applicable} be a blank line at the beginning of the ${key}.`, + caseRule: (key, applicable, value) => `The ${key} should ${applicable} be in ${Array.isArray(value) ? `one of the following case: + - ${value.join("\n - ")}.` : `${value} case.`}`, + emptyRule: (key, applicable) => `The ${key} should ${applicable} be empty.`, + enumRule: (key, applicable, value) => `The ${key} should ${applicable} be one of the following values: + - ${Array.isArray(value) ? value.join("\n - ") : value}.`, + enumTypeRule: (key, applicable, value, prompt) => `The ${key} should ${applicable} be one of the following values: + - ${Array.isArray(value) ? value.map((v4) => { + const description = getTypeRuleExtraDescription(v4, prompt); + if (description) { + return `${v4} (${description})`; + } else + return v4; + }).join("\n - ") : value}.`, + fullStopRule: (key, applicable, value) => `The ${key} should ${applicable} end with '${value}'.`, + maxLengthRule: (key, applicable, value) => `The ${key} should ${applicable} have ${value} characters or less.`, + minLengthRule: (key, applicable, value) => `The ${key} should ${applicable} have ${value} characters or more.` +}; +var rulesPrompts = { + "body-case": (applicable, value) => llmReadableRules.caseRule("body", applicable, value), + "body-empty": (applicable) => llmReadableRules.emptyRule("body", applicable, void 0), + "body-full-stop": (applicable, value) => llmReadableRules.fullStopRule("body", applicable, value), + "body-leading-blank": (applicable) => llmReadableRules.blankline("body", applicable, void 0), + "body-max-length": (applicable, value) => llmReadableRules.maxLengthRule("body", applicable, value), + "body-max-line-length": (applicable, value) => `Each line of the body should ${applicable} have ${value} characters or less.`, + "body-min-length": (applicable, value) => llmReadableRules.minLengthRule("body", applicable, value), + "footer-case": (applicable, value) => llmReadableRules.caseRule("footer", applicable, value), + "footer-empty": (applicable) => llmReadableRules.emptyRule("footer", applicable, void 0), + "footer-leading-blank": (applicable) => llmReadableRules.blankline("footer", applicable, void 0), + "footer-max-length": (applicable, value) => llmReadableRules.maxLengthRule("footer", applicable, value), + "footer-max-line-length": (applicable, value) => `Each line of the footer should ${applicable} have ${value} characters or less.`, + "footer-min-length": (applicable, value) => llmReadableRules.minLengthRule("footer", applicable, value), + "header-case": (applicable, value) => llmReadableRules.caseRule("header", applicable, value), + "header-full-stop": (applicable, value) => llmReadableRules.fullStopRule("header", applicable, value), + "header-max-length": (applicable, value) => llmReadableRules.maxLengthRule("header", applicable, value), + "header-min-length": (applicable, value) => llmReadableRules.minLengthRule("header", applicable, value), + "references-empty": (applicable) => llmReadableRules.emptyRule("references section", applicable, void 0), + "scope-case": (applicable, value) => llmReadableRules.caseRule("scope", applicable, value), + "scope-empty": (applicable) => llmReadableRules.emptyRule("scope", applicable, void 0), + "scope-enum": (applicable, value) => llmReadableRules.enumRule("type", applicable, value), + "scope-max-length": (applicable, value) => llmReadableRules.maxLengthRule("scope", applicable, value), + "scope-min-length": (applicable, value) => llmReadableRules.minLengthRule("scope", applicable, value), + "signed-off-by": (applicable, value) => `The commit message should ${applicable} have a "Signed-off-by" line with the value "${value}".`, + "subject-case": (applicable, value) => llmReadableRules.caseRule("subject", applicable, value), + "subject-empty": (applicable) => llmReadableRules.emptyRule("subject", applicable, void 0), + "subject-full-stop": (applicable, value) => llmReadableRules.fullStopRule("subject", applicable, value), + "subject-max-length": (applicable, value) => llmReadableRules.maxLengthRule("subject", applicable, value), + "subject-min-length": (applicable, value) => llmReadableRules.minLengthRule("subject", applicable, value), + "type-case": (applicable, value) => llmReadableRules.caseRule("type", applicable, value), + "type-empty": (applicable) => llmReadableRules.emptyRule("type", applicable, void 0), + "type-enum": (applicable, value, prompt) => llmReadableRules.enumTypeRule("type", applicable, value, prompt), + "type-max-length": (applicable, value) => llmReadableRules.maxLengthRule("type", applicable, value), + "type-min-length": (applicable, value) => llmReadableRules.minLengthRule("type", applicable, value) +}; +var getPrompt = (ruleName, ruleConfig, prompt) => { + const [severity, applicable, value] = ruleConfig; + if (severity === import_types.RuleConfigSeverity.Disabled) + return null; + const promptFn = rulesPrompts[ruleName]; + if (promptFn) { + return promptFn(applicable, value, prompt); } + ce(`${source_default.red("\u2716")} No prompt handler for rule "${ruleName}".`); + return `Please manualy set the prompt for rule "${ruleName}".`; +}; +var inferPromptsFromCommitlintConfig = (config8) => { + const { rules, prompt } = config8; + if (!rules) + return []; + return Object.keys(rules).map( + (ruleName) => getPrompt(ruleName, rules[ruleName], prompt) + ).filter((prompt2) => prompt2 !== null); }; -var CancelToken_default = CancelToken; +var STRUCTURE_OF_COMMIT = ` +- Header of commit is composed of type, scope, subject: (): +- Description of commit is composed of body and footer (optional): +`; +var GEN_COMMITLINT_CONSISTENCY_PROMPT = (prompts) => [ + { + role: import_openai.ChatCompletionRequestMessageRoleEnum.Assistant, + content: `${IDENTITY} Your mission is to create clean and comprehensive commit messages for two different changes in a single codebase and output them in the provided JSON format: one for a bug fix and another for a new feature. -// node_modules/axios/lib/helpers/spread.js -function spread(callback) { - return function wrap(arr) { - return callback.apply(null, arr); - }; -} +Here are the specific requirements and conventions that should be strictly followed: -// node_modules/axios/lib/helpers/isAxiosError.js -function isAxiosError(payload) { - return utils_default.isObject(payload) && payload.isAxiosError === true; +Commit Message Conventions: +- The commit message consists of three parts: Header, Body, and Footer. +- Header: + - Format: \`(): \` +- ${prompts.join("\n- ")} + +JSON Output Format: +- The JSON output should contain the commit messages for a bug fix and a new feature in the following format: +\`\`\`json +{ + "localLanguage": "${translation.localLanguage}", + "commitFix": "
", + "commitFeat": "
", + "commitDescription": "" } +\`\`\` +- The "commitDescription" should not include the commit message\u2019s header, only the description. +- Description should not be more than 74 characters. -// node_modules/axios/lib/helpers/HttpStatusCode.js -var HttpStatusCode = { - Continue: 100, - SwitchingProtocols: 101, - Processing: 102, - EarlyHints: 103, - Ok: 200, - Created: 201, - Accepted: 202, - NonAuthoritativeInformation: 203, - NoContent: 204, - ResetContent: 205, - PartialContent: 206, - MultiStatus: 207, - AlreadyReported: 208, - ImUsed: 226, - MultipleChoices: 300, - MovedPermanently: 301, - Found: 302, - SeeOther: 303, - NotModified: 304, - UseProxy: 305, - Unused: 306, - TemporaryRedirect: 307, - PermanentRedirect: 308, - BadRequest: 400, - Unauthorized: 401, - PaymentRequired: 402, - Forbidden: 403, - NotFound: 404, - MethodNotAllowed: 405, - NotAcceptable: 406, - ProxyAuthenticationRequired: 407, - RequestTimeout: 408, - Conflict: 409, - Gone: 410, - LengthRequired: 411, - PreconditionFailed: 412, - PayloadTooLarge: 413, - UriTooLong: 414, - UnsupportedMediaType: 415, - RangeNotSatisfiable: 416, - ExpectationFailed: 417, - ImATeapot: 418, - MisdirectedRequest: 421, - UnprocessableEntity: 422, - Locked: 423, - FailedDependency: 424, - TooEarly: 425, - UpgradeRequired: 426, - PreconditionRequired: 428, - TooManyRequests: 429, - RequestHeaderFieldsTooLarge: 431, - UnavailableForLegalReasons: 451, - InternalServerError: 500, - NotImplemented: 501, - BadGateway: 502, - ServiceUnavailable: 503, - GatewayTimeout: 504, - HttpVersionNotSupported: 505, - VariantAlsoNegotiates: 506, - InsufficientStorage: 507, - LoopDetected: 508, - NotExtended: 510, - NetworkAuthenticationRequired: 511 -}; -Object.entries(HttpStatusCode).forEach(([key, value]) => { - HttpStatusCode[value] = key; +Additional Details: +- Changing the variable 'port' to uppercase 'PORT' is considered a bug fix. +- Allowing the server to listen on a port specified through the environment variable is considered a new feature. + +Example Git Diff is to follow:` + }, + INIT_DIFF_PROMPT +]; +var INIT_MAIN_PROMPT = (language, prompts) => ({ + role: import_openai.ChatCompletionRequestMessageRoleEnum.System, + content: `${IDENTITY} Your mission is to create clean and comprehensive commit messages in the given @commitlint convention and explain WHAT were the changes and WHY the changes were done. I'll send you an output of 'git diff --staged' command, and you convert it into a commit message. +${config2?.OCO_EMOJI ? "Use GitMoji convention to preface the commit." : "Do not preface the commit with anything."} +${config2?.OCO_DESCRIPTION ? `Add a short description of WHY the changes are done after the commit message. Don't start it with "This commit", just describe the changes.` : "Don't add any descriptions to the commit, only commit message."} +Use the present tense. Use ${language} to answer. + +You will strictly follow the following conventions to generate the content of the commit message: +- ${prompts.join("\n- ")} + +The conventions refers to the following structure of commit message: +${STRUCTURE_OF_COMMIT} + + ` }); -var HttpStatusCode_default = HttpStatusCode; +var commitlintPrompts = { + INIT_MAIN_PROMPT, + GEN_COMMITLINT_CONSISTENCY_PROMPT +}; -// node_modules/axios/lib/axios.js -function createInstance(defaultConfig) { - const context = new Axios_default(defaultConfig); - const instance = bind(Axios_default.prototype.request, context); - utils_default.extend(instance, Axios_default.prototype, context, { allOwnKeys: true }); - utils_default.extend(instance, context, null, { allOwnKeys: true }); - instance.create = function create(instanceConfig) { - return createInstance(mergeConfig(defaultConfig, instanceConfig)); - }; - return instance; -} -var axios = createInstance(defaults_default); -axios.Axios = Axios_default; -axios.CanceledError = CanceledError_default; -axios.CancelToken = CancelToken_default; -axios.isCancel = isCancel; -axios.VERSION = VERSION; -axios.toFormData = toFormData_default; -axios.AxiosError = AxiosError_default; -axios.Cancel = axios.CanceledError; -axios.all = function all(promises) { - return Promise.all(promises); +// src/modules/commitlint/pwd-commitlint.ts +var import_path2 = __toESM(require("path"), 1); +var nodeModulesPath = import_path2.default.join( + process.env.PWD || process.cwd(), + "node_modules", + "@commitlint", + "load" +); +var getCommitLintPWDConfig = async () => { + const load = require(nodeModulesPath).default; + if (load && typeof load === "function") { + return await load(); + } + return null; }; -axios.spread = spread; -axios.isAxiosError = isAxiosError; -axios.mergeConfig = mergeConfig; -axios.AxiosHeaders = AxiosHeaders_default; -axios.formToJSON = (thing) => formDataToJSON_default(utils_default.isHTMLForm(thing) ? new FormData(thing) : thing); -axios.HttpStatusCode = HttpStatusCode_default; -axios.default = axios; -var axios_default = axios; -// node_modules/axios/index.js -var { - Axios: Axios2, - AxiosError: AxiosError2, - CanceledError: CanceledError2, - isCancel: isCancel2, - CancelToken: CancelToken2, - VERSION: VERSION2, - all: all2, - Cancel, - isAxiosError: isAxiosError2, - spread: spread2, - toFormData: toFormData2, - AxiosHeaders: AxiosHeaders2, - HttpStatusCode: HttpStatusCode2, - formToJSON, - mergeConfig: mergeConfig2 -} = axios_default; +// src/modules/commitlint/utils.ts +var import_promises = __toESM(require("fs/promises"), 1); +var removeDoubleNewlines = (input) => { + const pattern = /\\n\\n/g; + if (pattern.test(input)) { + const newInput = input.replace(pattern, ""); + return removeDoubleNewlines(newInput); + } + return input; +}; +var commitlintLLMConfigExists = async () => { + let exists; + try { + await import_promises.default.access(COMMITLINT_LLM_CONFIG_PATH); + exists = true; + } catch (e2) { + exists = false; + } + return exists; +}; +var writeCommitlintLLMConfig = async (commitlintLLMConfig) => { + await import_promises.default.writeFile( + COMMITLINT_LLM_CONFIG_PATH, + JSON.stringify(commitlintLLMConfig, null, 2) + ); +}; +var getCommitlintLLMConfig = async () => { + const content = await import_promises.default.readFile(COMMITLINT_LLM_CONFIG_PATH); + const commitLintLLMConfig = JSON.parse( + content.toString() + ); + return commitLintLLMConfig; +}; // src/engine/openAi.ts var import_openai2 = __toESM(require_dist(), 1); @@ -21962,40 +22048,14 @@ var OpenAi = class { }; var api = new OpenAi(); -// src/engine/ollama.ts -var OllamaAi = class { - async generateCommitMessage(messages) { - const model = "mistral"; - let prompt = messages.map((x4) => x4.content).join("\n"); - prompt += "Summarize above git diff in 10 words or less"; - const url3 = "http://localhost:11434/api/generate"; - const p4 = { - model, - prompt, - stream: false - }; - try { - const response = await axios_default.post(url3, p4, { - headers: { - "Content-Type": "application/json" - } - }); - const answer = response.data?.response; - console.log("answer", answer); - return answer; - } catch (err) { - const message = err.response?.data?.error ?? err.message; - throw new Error("local model issues. details: " + message); - } - } -}; -var ollamaAi = new OllamaAi(); - // src/utils/engine.ts function getEngine() { const config8 = getConfig(); if (config8?.OCO_AI_PROVIDER == "ollama") { - return ollamaAi; + return new OllamaAi( + config8?.OCO_OLLAMA_MODEL || "mistral", + config8?.OCO_OLLAMA_BASE_PATH || "http://localhost:11434" + ); } return api; } diff --git a/out/github-action.cjs b/out/github-action.cjs index 5672873f..7fd0cce7 100644 --- a/out/github-action.cjs +++ b/out/github-action.cjs @@ -22609,6 +22609,76 @@ var require_ini = __commonJS({ } }); +// node_modules/proxy-from-env/index.js +var require_proxy_from_env = __commonJS({ + "node_modules/proxy-from-env/index.js"(exports) { + "use strict"; + var parseUrl = require("url").parse; + var DEFAULT_PORTS = { + ftp: 21, + gopher: 70, + http: 80, + https: 443, + ws: 80, + wss: 443 + }; + var stringEndsWith = String.prototype.endsWith || function(s) { + return s.length <= this.length && this.indexOf(s, this.length - s.length) !== -1; + }; + function getProxyForUrl2(url2) { + var parsedUrl = typeof url2 === "string" ? parseUrl(url2) : url2 || {}; + var proto2 = parsedUrl.protocol; + var hostname = parsedUrl.host; + var port = parsedUrl.port; + if (typeof hostname !== "string" || !hostname || typeof proto2 !== "string") { + return ""; + } + proto2 = proto2.split(":", 1)[0]; + hostname = hostname.replace(/:\d*$/, ""); + port = parseInt(port) || DEFAULT_PORTS[proto2] || 0; + if (!shouldProxy(hostname, port)) { + return ""; + } + var proxy = getEnv("npm_config_" + proto2 + "_proxy") || getEnv(proto2 + "_proxy") || getEnv("npm_config_proxy") || getEnv("all_proxy"); + if (proxy && proxy.indexOf("://") === -1) { + proxy = proto2 + "://" + proxy; + } + return proxy; + } + function shouldProxy(hostname, port) { + var NO_PROXY = (getEnv("npm_config_no_proxy") || getEnv("no_proxy")).toLowerCase(); + if (!NO_PROXY) { + return true; + } + if (NO_PROXY === "*") { + return false; + } + return NO_PROXY.split(/[,\s]/).every(function(proxy) { + if (!proxy) { + return true; + } + var parsedProxy = proxy.match(/^(.+):(\d+)$/); + var parsedProxyHostname = parsedProxy ? parsedProxy[1] : proxy; + var parsedProxyPort = parsedProxy ? parseInt(parsedProxy[2]) : 0; + if (parsedProxyPort && parsedProxyPort !== port) { + return true; + } + if (!/^[.*]/.test(parsedProxyHostname)) { + return hostname !== parsedProxyHostname; + } + if (parsedProxyHostname.charAt(0) === "*") { + parsedProxyHostname = parsedProxyHostname.slice(1); + } + return !stringEndsWith.call(hostname, parsedProxyHostname); + }); + } + function getEnv(key) { + return process.env[key.toLowerCase()] || process.env[key.toUpperCase()] || ""; + } + exports.getProxyForUrl = getProxyForUrl2; + } +}); + // node_modules/@commitlint/types/lib/ensure.js var require_ensure = __commonJS({ "node_modules/@commitlint/types/lib/ensure.js"(exports) { @@ -22721,76 +22791,6 @@ var require_lib4 = __commonJS({ } }); -// node_modules/proxy-from-env/index.js -var require_proxy_from_env = __commonJS({ - "node_modules/proxy-from-env/index.js"(exports) { - "use strict"; - var parseUrl = require("url").parse; - var DEFAULT_PORTS = { - ftp: 21, - gopher: 70, - http: 80, - https: 443, - ws: 80, - wss: 443 - }; - var stringEndsWith = String.prototype.endsWith || function(s) { - return s.length <= this.length && this.indexOf(s, this.length - s.length) !== -1; - }; - function getProxyForUrl2(url2) { - var parsedUrl = typeof url2 === "string" ? parseUrl(url2) : url2 || {}; - var proto2 = parsedUrl.protocol; - var hostname = parsedUrl.host; - var port = parsedUrl.port; - if (typeof hostname !== "string" || !hostname || typeof proto2 !== "string") { - return ""; - } - proto2 = proto2.split(":", 1)[0]; - hostname = hostname.replace(/:\d*$/, ""); - port = parseInt(port) || DEFAULT_PORTS[proto2] || 0; - if (!shouldProxy(hostname, port)) { - return ""; - } - var proxy = getEnv("npm_config_" + proto2 + "_proxy") || getEnv(proto2 + "_proxy") || getEnv("npm_config_proxy") || getEnv("all_proxy"); - if (proxy && proxy.indexOf("://") === -1) { - proxy = proto2 + "://" + proxy; - } - return proxy; - } - function shouldProxy(hostname, port) { - var NO_PROXY = (getEnv("npm_config_no_proxy") || getEnv("no_proxy")).toLowerCase(); - if (!NO_PROXY) { - return true; - } - if (NO_PROXY === "*") { - return false; - } - return NO_PROXY.split(/[,\s]/).every(function(proxy) { - if (!proxy) { - return true; - } - var parsedProxy = proxy.match(/^(.+):(\d+)$/); - var parsedProxyHostname = parsedProxy ? parsedProxy[1] : proxy; - var parsedProxyPort = parsedProxy ? parseInt(parsedProxy[2]) : 0; - if (parsedProxyPort && parsedProxyPort !== port) { - return true; - } - if (!/^[.*]/.test(parsedProxyHostname)) { - return hostname !== parsedProxyHostname; - } - if (parsedProxyHostname.charAt(0) === "*") { - parsedProxyHostname = parsedProxyHostname.slice(1); - } - return !stringEndsWith.call(hostname, parsedProxyHostname); - }); - } - function getEnv(key) { - return process.env[key.toLowerCase()] || process.env[key.toUpperCase()] || ""; - } - exports.getProxyForUrl = getProxyForUrl2; - } -}); - // node_modules/@dqbd/tiktoken/lite/tiktoken_bg.cjs var require_tiktoken_bg = __commonJS({ "node_modules/@dqbd/tiktoken/lite/tiktoken_bg.cjs"(exports, module2) { @@ -24143,677 +24143,263 @@ function getI18nLocal(value) { return false; } -// src/commands/config.ts -dotenv.config(); -var DEFAULT_MODEL_TOKEN_LIMIT = 4096; -var validateConfig = (key, condition, validationMessage) => { - if (!condition) { - ce( - `${source_default.red("\u2716")} Unsupported config key ${key}: ${validationMessage}` - ); - process.exit(1); +// node_modules/axios/lib/helpers/bind.js +function bind(fn, thisArg) { + return function wrap() { + return fn.apply(thisArg, arguments); + }; +} + +// node_modules/axios/lib/utils.js +var { toString } = Object.prototype; +var { getPrototypeOf } = Object; +var kindOf = ((cache) => (thing) => { + const str = toString.call(thing); + return cache[str] || (cache[str] = str.slice(8, -1).toLowerCase()); +})(/* @__PURE__ */ Object.create(null)); +var kindOfTest = (type) => { + type = type.toLowerCase(); + return (thing) => kindOf(thing) === type; +}; +var typeOfTest = (type) => (thing) => typeof thing === type; +var { isArray } = Array; +var isUndefined = typeOfTest("undefined"); +function isBuffer(val) { + return val !== null && !isUndefined(val) && val.constructor !== null && !isUndefined(val.constructor) && isFunction(val.constructor.isBuffer) && val.constructor.isBuffer(val); +} +var isArrayBuffer = kindOfTest("ArrayBuffer"); +function isArrayBufferView(val) { + let result; + if (typeof ArrayBuffer !== "undefined" && ArrayBuffer.isView) { + result = ArrayBuffer.isView(val); + } else { + result = val && val.buffer && isArrayBuffer(val.buffer); + } + return result; +} +var isString = typeOfTest("string"); +var isFunction = typeOfTest("function"); +var isNumber = typeOfTest("number"); +var isObject = (thing) => thing !== null && typeof thing === "object"; +var isBoolean = (thing) => thing === true || thing === false; +var isPlainObject = (val) => { + if (kindOf(val) !== "object") { + return false; } + const prototype3 = getPrototypeOf(val); + return (prototype3 === null || prototype3 === Object.prototype || Object.getPrototypeOf(prototype3) === null) && !(Symbol.toStringTag in val) && !(Symbol.iterator in val); }; -var configValidators = { - ["OCO_OPENAI_API_KEY" /* OCO_OPENAI_API_KEY */](value, config7 = {}) { - validateConfig("API_KEY", value || config7.OCO_AI_PROVIDER == "ollama", "You need to provide an API key"); - validateConfig( - "OCO_OPENAI_API_KEY" /* OCO_OPENAI_API_KEY */, - value.startsWith("sk-"), - 'Must start with "sk-"' - ); - validateConfig( - "OCO_OPENAI_API_KEY" /* OCO_OPENAI_API_KEY */, - config7["OCO_OPENAI_BASE_PATH" /* OCO_OPENAI_BASE_PATH */] || value.length === 51, - "Must be 51 characters long" - ); - return value; - }, - ["OCO_DESCRIPTION" /* OCO_DESCRIPTION */](value) { - validateConfig( - "OCO_DESCRIPTION" /* OCO_DESCRIPTION */, - typeof value === "boolean", - "Must be true or false" - ); - return value; - }, - ["OCO_OPENAI_MAX_TOKENS" /* OCO_OPENAI_MAX_TOKENS */](value) { - if (typeof value === "string") { - value = parseInt(value); - validateConfig( - "OCO_OPENAI_MAX_TOKENS" /* OCO_OPENAI_MAX_TOKENS */, - !isNaN(value), - "Must be a number" - ); +var isDate = kindOfTest("Date"); +var isFile = kindOfTest("File"); +var isBlob = kindOfTest("Blob"); +var isFileList = kindOfTest("FileList"); +var isStream = (val) => isObject(val) && isFunction(val.pipe); +var isFormData = (thing) => { + const pattern = "[object FormData]"; + return thing && (typeof FormData === "function" && thing instanceof FormData || toString.call(thing) === pattern || isFunction(thing.toString) && thing.toString() === pattern); +}; +var isURLSearchParams = kindOfTest("URLSearchParams"); +var trim = (str) => str.trim ? str.trim() : str.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, ""); +function forEach(obj, fn, { allOwnKeys = false } = {}) { + if (obj === null || typeof obj === "undefined") { + return; + } + let i2; + let l; + if (typeof obj !== "object") { + obj = [obj]; + } + if (isArray(obj)) { + for (i2 = 0, l = obj.length; i2 < l; i2++) { + fn.call(null, obj[i2], i2, obj); } - validateConfig( - "OCO_OPENAI_MAX_TOKENS" /* OCO_OPENAI_MAX_TOKENS */, - value ? typeof value === "number" : void 0, - "Must be a number" - ); - return value; - }, - ["OCO_EMOJI" /* OCO_EMOJI */](value) { - validateConfig( - "OCO_EMOJI" /* OCO_EMOJI */, - typeof value === "boolean", - "Must be true or false" - ); - return value; - }, - ["OCO_LANGUAGE" /* OCO_LANGUAGE */](value) { - validateConfig( - "OCO_LANGUAGE" /* OCO_LANGUAGE */, - getI18nLocal(value), - `${value} is not supported yet` - ); - return getI18nLocal(value); - }, - ["OCO_OPENAI_BASE_PATH" /* OCO_OPENAI_BASE_PATH */](value) { - validateConfig( - "OCO_OPENAI_BASE_PATH" /* OCO_OPENAI_BASE_PATH */, - typeof value === "string", - "Must be string" - ); - return value; - }, - ["OCO_MODEL" /* OCO_MODEL */](value) { - validateConfig( - "OCO_MODEL" /* OCO_MODEL */, - [ - "gpt-3.5-turbo", - "gpt-4", - "gpt-3.5-turbo-16k", - "gpt-3.5-turbo-0613" - ].includes(value), - `${value} is not supported yet, use 'gpt-4', 'gpt-3.5-turbo-16k' (default), 'gpt-3.5-turbo-0613' or 'gpt-3.5-turbo'` - ); - return value; - }, - ["OCO_MESSAGE_TEMPLATE_PLACEHOLDER" /* OCO_MESSAGE_TEMPLATE_PLACEHOLDER */](value) { - validateConfig( - "OCO_MESSAGE_TEMPLATE_PLACEHOLDER" /* OCO_MESSAGE_TEMPLATE_PLACEHOLDER */, - value.startsWith("$"), - `${value} must start with $, for example: '$msg'` - ); - return value; - }, - ["OCO_PROMPT_MODULE" /* OCO_PROMPT_MODULE */](value) { - validateConfig( - "OCO_PROMPT_MODULE" /* OCO_PROMPT_MODULE */, - ["conventional-commit", "@commitlint"].includes(value), - `${value} is not supported yet, use '@commitlint' or 'conventional-commit' (default)` - ); - return value; - }, - ["OCO_AI_PROVIDER" /* OCO_AI_PROVIDER */](value) { - validateConfig( - "OCO_AI_PROVIDER" /* OCO_AI_PROVIDER */, - [ - "", - "openai", - "ollama" - ].includes(value), - `${value} is not supported yet, use 'ollama' or 'openai' (default)` - ); - return value; - } -}; -var configPath = (0, import_path.join)((0, import_os.homedir)(), ".opencommit"); -var getConfig = () => { - const configFromEnv = { - OCO_OPENAI_API_KEY: process.env.OCO_OPENAI_API_KEY, - OCO_OPENAI_MAX_TOKENS: process.env.OCO_OPENAI_MAX_TOKENS ? Number(process.env.OCO_OPENAI_MAX_TOKENS) : void 0, - OCO_OPENAI_BASE_PATH: process.env.OCO_OPENAI_BASE_PATH, - OCO_DESCRIPTION: process.env.OCO_DESCRIPTION === "true" ? true : false, - OCO_EMOJI: process.env.OCO_EMOJI === "true" ? true : false, - OCO_MODEL: process.env.OCO_MODEL || "gpt-3.5-turbo-16k", - OCO_LANGUAGE: process.env.OCO_LANGUAGE || "en", - OCO_MESSAGE_TEMPLATE_PLACEHOLDER: process.env.OCO_MESSAGE_TEMPLATE_PLACEHOLDER || "$msg", - OCO_PROMPT_MODULE: process.env.OCO_PROMPT_MODULE || "conventional-commit", - OCO_AI_PROVIDER: process.env.OCO_AI_PROVIDER || "openai" - }; - const configExists = (0, import_fs.existsSync)(configPath); - if (!configExists) - return configFromEnv; - const configFile = (0, import_fs.readFileSync)(configPath, "utf8"); - const config7 = (0, import_ini.parse)(configFile); - for (const configKey of Object.keys(config7)) { - if (!config7[configKey] || ["null", "undefined"].includes(config7[configKey])) { - config7[configKey] = void 0; - continue; + } else { + const keys = allOwnKeys ? Object.getOwnPropertyNames(obj) : Object.keys(obj); + const len = keys.length; + let key; + for (i2 = 0; i2 < len; i2++) { + key = keys[i2]; + fn.call(null, obj[key], key, obj); } - try { - const validator = configValidators[configKey]; - const validValue = validator( - config7[configKey] ?? configFromEnv[configKey], - config7 - ); - config7[configKey] = validValue; - } catch (error) { - ce( - `'${configKey}' name is invalid, it should be either 'OCO_${configKey.toUpperCase()}' or it doesn't exist.` - ); - ce( - `Manually fix the '.env' file or global '~/.opencommit' config file.` - ); - process.exit(1); + } +} +function findKey(obj, key) { + key = key.toLowerCase(); + const keys = Object.keys(obj); + let i2 = keys.length; + let _key; + while (i2-- > 0) { + _key = keys[i2]; + if (key === _key.toLowerCase()) { + return _key; } } - return config7; -}; -var setConfig = (keyValues) => { - const config7 = getConfig() || {}; - for (const [configKey, configValue] of keyValues) { - if (!configValidators.hasOwnProperty(configKey)) { - throw new Error(`Unsupported config key: ${configKey}`); + return null; +} +var _global = (() => { + if (typeof globalThis !== "undefined") + return globalThis; + return typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : global; +})(); +var isContextDefined = (context2) => !isUndefined(context2) && context2 !== _global; +function merge() { + const { caseless } = isContextDefined(this) && this || {}; + const result = {}; + const assignValue = (val, key) => { + const targetKey = caseless && findKey(result, key) || key; + if (isPlainObject(result[targetKey]) && isPlainObject(val)) { + result[targetKey] = merge(result[targetKey], val); + } else if (isPlainObject(val)) { + result[targetKey] = merge({}, val); + } else if (isArray(val)) { + result[targetKey] = val.slice(); + } else { + result[targetKey] = val; } - let parsedConfigValue; - try { - parsedConfigValue = JSON.parse(configValue); - } catch (error) { - parsedConfigValue = configValue; + }; + for (let i2 = 0, l = arguments.length; i2 < l; i2++) { + arguments[i2] && forEach(arguments[i2], assignValue); + } + return result; +} +var extend = (a2, b2, thisArg, { allOwnKeys } = {}) => { + forEach(b2, (val, key) => { + if (thisArg && isFunction(val)) { + a2[key] = bind(val, thisArg); + } else { + a2[key] = val; } - const validValue = configValidators[configKey](parsedConfigValue); - config7[configKey] = validValue; + }, { allOwnKeys }); + return a2; +}; +var stripBOM = (content) => { + if (content.charCodeAt(0) === 65279) { + content = content.slice(1); } - (0, import_fs.writeFileSync)(configPath, (0, import_ini.stringify)(config7), "utf8"); - ce(`${source_default.green("\u2714")} Config successfully set`); + return content; }; -var configCommand = G3( - { - name: "config" /* config */, - parameters: ["", ""] - }, - async (argv) => { - ae("opencommit \u2014 config"); - try { - const { mode: mode2, keyValues } = argv._; - if (mode2 === "get" /* get */) { - const config7 = getConfig() || {}; - for (const key of keyValues) { - ce(`${key}=${config7[key]}`); - } - } else if (mode2 === "set" /* set */) { - await setConfig( - keyValues.map((keyValue) => keyValue.split("=")) - ); - } else { - throw new Error( - `Unsupported mode: ${mode2}. Valid modes are: "set" and "get"` - ); +var inherits = (constructor, superConstructor, props, descriptors2) => { + constructor.prototype = Object.create(superConstructor.prototype, descriptors2); + constructor.prototype.constructor = constructor; + Object.defineProperty(constructor, "super", { + value: superConstructor.prototype + }); + props && Object.assign(constructor.prototype, props); +}; +var toFlatObject = (sourceObj, destObj, filter2, propFilter) => { + let props; + let i2; + let prop; + const merged = {}; + destObj = destObj || {}; + if (sourceObj == null) + return destObj; + do { + props = Object.getOwnPropertyNames(sourceObj); + i2 = props.length; + while (i2-- > 0) { + prop = props[i2]; + if ((!propFilter || propFilter(prop, sourceObj, destObj)) && !merged[prop]) { + destObj[prop] = sourceObj[prop]; + merged[prop] = true; } - } catch (error) { - ce(`${source_default.red("\u2716")} ${error}`); - process.exit(1); } + sourceObj = filter2 !== false && getPrototypeOf(sourceObj); + } while (sourceObj && (!filter2 || filter2(sourceObj, destObj)) && sourceObj !== Object.prototype); + return destObj; +}; +var endsWith = (str, searchString, position) => { + str = String(str); + if (position === void 0 || position > str.length) { + position = str.length; } -); - -// src/prompts.ts -var import_openai3 = __toESM(require_dist2(), 1); - -// src/modules/commitlint/constants.ts -var COMMITLINT_LLM_CONFIG_PATH = `${process.env.PWD}/.opencommit-commitlint`; - -// src/modules/commitlint/crypto.ts -var import_crypto = __toESM(require("crypto"), 1); -var computeHash = async (content, algorithm = "sha256") => { - try { - const hash = import_crypto.default.createHash(algorithm); - hash.update(content); - return hash.digest("hex"); - } catch (error) { - console.error("Error while computing hash:", error); - throw error; - } + position -= searchString.length; + const lastIndex = str.indexOf(searchString, position); + return lastIndex !== -1 && lastIndex === position; }; - -// src/modules/commitlint/prompts.ts -var import_openai = __toESM(require_dist2(), 1); -var import_types = __toESM(require_lib4(), 1); -var config2 = getConfig(); -var translation = i18n[config2?.OCO_LANGUAGE || "en"]; -var getTypeRuleExtraDescription = (type, prompt) => prompt?.questions?.type?.enum?.[type]?.description; -var llmReadableRules = { - blankline: (key, applicable) => `There should ${applicable} be a blank line at the beginning of the ${key}.`, - caseRule: (key, applicable, value) => `The ${key} should ${applicable} be in ${Array.isArray(value) ? `one of the following case: - - ${value.join("\n - ")}.` : `${value} case.`}`, - emptyRule: (key, applicable) => `The ${key} should ${applicable} be empty.`, - enumRule: (key, applicable, value) => `The ${key} should ${applicable} be one of the following values: - - ${Array.isArray(value) ? value.join("\n - ") : value}.`, - enumTypeRule: (key, applicable, value, prompt) => `The ${key} should ${applicable} be one of the following values: - - ${Array.isArray(value) ? value.map((v2) => { - const description = getTypeRuleExtraDescription(v2, prompt); - if (description) { - return `${v2} (${description})`; - } else - return v2; - }).join("\n - ") : value}.`, - fullStopRule: (key, applicable, value) => `The ${key} should ${applicable} end with '${value}'.`, - maxLengthRule: (key, applicable, value) => `The ${key} should ${applicable} have ${value} characters or less.`, - minLengthRule: (key, applicable, value) => `The ${key} should ${applicable} have ${value} characters or more.` -}; -var rulesPrompts = { - "body-case": (applicable, value) => llmReadableRules.caseRule("body", applicable, value), - "body-empty": (applicable) => llmReadableRules.emptyRule("body", applicable, void 0), - "body-full-stop": (applicable, value) => llmReadableRules.fullStopRule("body", applicable, value), - "body-leading-blank": (applicable) => llmReadableRules.blankline("body", applicable, void 0), - "body-max-length": (applicable, value) => llmReadableRules.maxLengthRule("body", applicable, value), - "body-max-line-length": (applicable, value) => `Each line of the body should ${applicable} have ${value} characters or less.`, - "body-min-length": (applicable, value) => llmReadableRules.minLengthRule("body", applicable, value), - "footer-case": (applicable, value) => llmReadableRules.caseRule("footer", applicable, value), - "footer-empty": (applicable) => llmReadableRules.emptyRule("footer", applicable, void 0), - "footer-leading-blank": (applicable) => llmReadableRules.blankline("footer", applicable, void 0), - "footer-max-length": (applicable, value) => llmReadableRules.maxLengthRule("footer", applicable, value), - "footer-max-line-length": (applicable, value) => `Each line of the footer should ${applicable} have ${value} characters or less.`, - "footer-min-length": (applicable, value) => llmReadableRules.minLengthRule("footer", applicable, value), - "header-case": (applicable, value) => llmReadableRules.caseRule("header", applicable, value), - "header-full-stop": (applicable, value) => llmReadableRules.fullStopRule("header", applicable, value), - "header-max-length": (applicable, value) => llmReadableRules.maxLengthRule("header", applicable, value), - "header-min-length": (applicable, value) => llmReadableRules.minLengthRule("header", applicable, value), - "references-empty": (applicable) => llmReadableRules.emptyRule("references section", applicable, void 0), - "scope-case": (applicable, value) => llmReadableRules.caseRule("scope", applicable, value), - "scope-empty": (applicable) => llmReadableRules.emptyRule("scope", applicable, void 0), - "scope-enum": (applicable, value) => llmReadableRules.enumRule("type", applicable, value), - "scope-max-length": (applicable, value) => llmReadableRules.maxLengthRule("scope", applicable, value), - "scope-min-length": (applicable, value) => llmReadableRules.minLengthRule("scope", applicable, value), - "signed-off-by": (applicable, value) => `The commit message should ${applicable} have a "Signed-off-by" line with the value "${value}".`, - "subject-case": (applicable, value) => llmReadableRules.caseRule("subject", applicable, value), - "subject-empty": (applicable) => llmReadableRules.emptyRule("subject", applicable, void 0), - "subject-full-stop": (applicable, value) => llmReadableRules.fullStopRule("subject", applicable, value), - "subject-max-length": (applicable, value) => llmReadableRules.maxLengthRule("subject", applicable, value), - "subject-min-length": (applicable, value) => llmReadableRules.minLengthRule("subject", applicable, value), - "type-case": (applicable, value) => llmReadableRules.caseRule("type", applicable, value), - "type-empty": (applicable) => llmReadableRules.emptyRule("type", applicable, void 0), - "type-enum": (applicable, value, prompt) => llmReadableRules.enumTypeRule("type", applicable, value, prompt), - "type-max-length": (applicable, value) => llmReadableRules.maxLengthRule("type", applicable, value), - "type-min-length": (applicable, value) => llmReadableRules.minLengthRule("type", applicable, value) -}; -var getPrompt = (ruleName, ruleConfig, prompt) => { - const [severity, applicable, value] = ruleConfig; - if (severity === import_types.RuleConfigSeverity.Disabled) +var toArray = (thing) => { + if (!thing) return null; - const promptFn = rulesPrompts[ruleName]; - if (promptFn) { - return promptFn(applicable, value, prompt); - } - ce(`${source_default.red("\u2716")} No prompt handler for rule "${ruleName}".`); - return `Please manualy set the prompt for rule "${ruleName}".`; -}; -var inferPromptsFromCommitlintConfig = (config7) => { - const { rules, prompt } = config7; - if (!rules) - return []; - return Object.keys(rules).map( - (ruleName) => getPrompt(ruleName, rules[ruleName], prompt) - ).filter((prompt2) => prompt2 !== null); -}; -var STRUCTURE_OF_COMMIT = ` -- Header of commit is composed of type, scope, subject: (): -- Description of commit is composed of body and footer (optional): -`; -var GEN_COMMITLINT_CONSISTENCY_PROMPT = (prompts) => [ - { - role: import_openai.ChatCompletionRequestMessageRoleEnum.Assistant, - content: `${IDENTITY} Your mission is to create clean and comprehensive commit messages for two different changes in a single codebase and output them in the provided JSON format: one for a bug fix and another for a new feature. - -Here are the specific requirements and conventions that should be strictly followed: - -Commit Message Conventions: -- The commit message consists of three parts: Header, Body, and Footer. -- Header: - - Format: \`(): \` -- ${prompts.join("\n- ")} - -JSON Output Format: -- The JSON output should contain the commit messages for a bug fix and a new feature in the following format: -\`\`\`json -{ - "localLanguage": "${translation.localLanguage}", - "commitFix": "
", - "commitFeat": "
", - "commitDescription": "" -} -\`\`\` -- The "commitDescription" should not include the commit message\u2019s header, only the description. -- Description should not be more than 74 characters. - -Additional Details: -- Changing the variable 'port' to uppercase 'PORT' is considered a bug fix. -- Allowing the server to listen on a port specified through the environment variable is considered a new feature. - -Example Git Diff is to follow:` - }, - INIT_DIFF_PROMPT -]; -var INIT_MAIN_PROMPT = (language, prompts) => ({ - role: import_openai.ChatCompletionRequestMessageRoleEnum.System, - content: `${IDENTITY} Your mission is to create clean and comprehensive commit messages in the given @commitlint convention and explain WHAT were the changes and WHY the changes were done. I'll send you an output of 'git diff --staged' command, and you convert it into a commit message. -${config2?.OCO_EMOJI ? "Use GitMoji convention to preface the commit." : "Do not preface the commit with anything."} -${config2?.OCO_DESCRIPTION ? `Add a short description of WHY the changes are done after the commit message. Don't start it with "This commit", just describe the changes.` : "Don't add any descriptions to the commit, only commit message."} -Use the present tense. Use ${language} to answer. - -You will strictly follow the following conventions to generate the content of the commit message: -- ${prompts.join("\n- ")} - -The conventions refers to the following structure of commit message: -${STRUCTURE_OF_COMMIT} - - ` -}); -var commitlintPrompts = { - INIT_MAIN_PROMPT, - GEN_COMMITLINT_CONSISTENCY_PROMPT -}; - -// src/modules/commitlint/pwd-commitlint.ts -var import_path2 = __toESM(require("path"), 1); -var nodeModulesPath = import_path2.default.join( - process.env.PWD || process.cwd(), - "node_modules", - "@commitlint", - "load" -); -var getCommitLintPWDConfig = async () => { - const load = require(nodeModulesPath).default; - if (load && typeof load === "function") { - return await load(); + if (isArray(thing)) + return thing; + let i2 = thing.length; + if (!isNumber(i2)) + return null; + const arr = new Array(i2); + while (i2-- > 0) { + arr[i2] = thing[i2]; } - return null; + return arr; }; - -// src/modules/commitlint/utils.ts -var import_promises = __toESM(require("fs/promises"), 1); -var removeDoubleNewlines = (input) => { - const pattern = /\\n\\n/g; - if (pattern.test(input)) { - const newInput = input.replace(pattern, ""); - return removeDoubleNewlines(newInput); +var isTypedArray = ((TypedArray) => { + return (thing) => { + return TypedArray && thing instanceof TypedArray; + }; +})(typeof Uint8Array !== "undefined" && getPrototypeOf(Uint8Array)); +var forEachEntry = (obj, fn) => { + const generator = obj && obj[Symbol.iterator]; + const iterator = generator.call(obj); + let result; + while ((result = iterator.next()) && !result.done) { + const pair = result.value; + fn.call(obj, pair[0], pair[1]); } - return input; }; -var commitlintLLMConfigExists = async () => { - let exists; - try { - await import_promises.default.access(COMMITLINT_LLM_CONFIG_PATH); - exists = true; - } catch (e2) { - exists = false; +var matchAll = (regExp, str) => { + let matches; + const arr = []; + while ((matches = regExp.exec(str)) !== null) { + arr.push(matches); } - return exists; + return arr; }; -var writeCommitlintLLMConfig = async (commitlintLLMConfig) => { - await import_promises.default.writeFile( - COMMITLINT_LLM_CONFIG_PATH, - JSON.stringify(commitlintLLMConfig, null, 2) +var isHTMLForm = kindOfTest("HTMLFormElement"); +var toCamelCase = (str) => { + return str.toLowerCase().replace( + /[-_\s]([a-z\d])(\w*)/g, + function replacer(m3, p1, p2) { + return p1.toUpperCase() + p2; + } ); }; -var getCommitlintLLMConfig = async () => { - const content = await import_promises.default.readFile(COMMITLINT_LLM_CONFIG_PATH); - const commitLintLLMConfig = JSON.parse( - content.toString() - ); - return commitLintLLMConfig; +var hasOwnProperty = (({ hasOwnProperty: hasOwnProperty2 }) => (obj, prop) => hasOwnProperty2.call(obj, prop))(Object.prototype); +var isRegExp = kindOfTest("RegExp"); +var reduceDescriptors = (obj, reducer) => { + const descriptors2 = Object.getOwnPropertyDescriptors(obj); + const reducedDescriptors = {}; + forEach(descriptors2, (descriptor, name) => { + if (reducer(descriptor, name, obj) !== false) { + reducedDescriptors[name] = descriptor; + } + }); + Object.defineProperties(obj, reducedDescriptors); }; - -// node_modules/axios/lib/helpers/bind.js -function bind(fn, thisArg) { - return function wrap() { - return fn.apply(thisArg, arguments); - }; -} - -// node_modules/axios/lib/utils.js -var { toString } = Object.prototype; -var { getPrototypeOf } = Object; -var kindOf = ((cache) => (thing) => { - const str = toString.call(thing); - return cache[str] || (cache[str] = str.slice(8, -1).toLowerCase()); -})(/* @__PURE__ */ Object.create(null)); -var kindOfTest = (type) => { - type = type.toLowerCase(); - return (thing) => kindOf(thing) === type; -}; -var typeOfTest = (type) => (thing) => typeof thing === type; -var { isArray } = Array; -var isUndefined = typeOfTest("undefined"); -function isBuffer(val) { - return val !== null && !isUndefined(val) && val.constructor !== null && !isUndefined(val.constructor) && isFunction(val.constructor.isBuffer) && val.constructor.isBuffer(val); -} -var isArrayBuffer = kindOfTest("ArrayBuffer"); -function isArrayBufferView(val) { - let result; - if (typeof ArrayBuffer !== "undefined" && ArrayBuffer.isView) { - result = ArrayBuffer.isView(val); - } else { - result = val && val.buffer && isArrayBuffer(val.buffer); - } - return result; -} -var isString = typeOfTest("string"); -var isFunction = typeOfTest("function"); -var isNumber = typeOfTest("number"); -var isObject = (thing) => thing !== null && typeof thing === "object"; -var isBoolean = (thing) => thing === true || thing === false; -var isPlainObject = (val) => { - if (kindOf(val) !== "object") { - return false; - } - const prototype3 = getPrototypeOf(val); - return (prototype3 === null || prototype3 === Object.prototype || Object.getPrototypeOf(prototype3) === null) && !(Symbol.toStringTag in val) && !(Symbol.iterator in val); -}; -var isDate = kindOfTest("Date"); -var isFile = kindOfTest("File"); -var isBlob = kindOfTest("Blob"); -var isFileList = kindOfTest("FileList"); -var isStream = (val) => isObject(val) && isFunction(val.pipe); -var isFormData = (thing) => { - const pattern = "[object FormData]"; - return thing && (typeof FormData === "function" && thing instanceof FormData || toString.call(thing) === pattern || isFunction(thing.toString) && thing.toString() === pattern); -}; -var isURLSearchParams = kindOfTest("URLSearchParams"); -var trim = (str) => str.trim ? str.trim() : str.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, ""); -function forEach(obj, fn, { allOwnKeys = false } = {}) { - if (obj === null || typeof obj === "undefined") { - return; - } - let i2; - let l; - if (typeof obj !== "object") { - obj = [obj]; - } - if (isArray(obj)) { - for (i2 = 0, l = obj.length; i2 < l; i2++) { - fn.call(null, obj[i2], i2, obj); - } - } else { - const keys = allOwnKeys ? Object.getOwnPropertyNames(obj) : Object.keys(obj); - const len = keys.length; - let key; - for (i2 = 0; i2 < len; i2++) { - key = keys[i2]; - fn.call(null, obj[key], key, obj); - } - } -} -function findKey(obj, key) { - key = key.toLowerCase(); - const keys = Object.keys(obj); - let i2 = keys.length; - let _key; - while (i2-- > 0) { - _key = keys[i2]; - if (key === _key.toLowerCase()) { - return _key; - } - } - return null; -} -var _global = (() => { - if (typeof globalThis !== "undefined") - return globalThis; - return typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : global; -})(); -var isContextDefined = (context2) => !isUndefined(context2) && context2 !== _global; -function merge() { - const { caseless } = isContextDefined(this) && this || {}; - const result = {}; - const assignValue = (val, key) => { - const targetKey = caseless && findKey(result, key) || key; - if (isPlainObject(result[targetKey]) && isPlainObject(val)) { - result[targetKey] = merge(result[targetKey], val); - } else if (isPlainObject(val)) { - result[targetKey] = merge({}, val); - } else if (isArray(val)) { - result[targetKey] = val.slice(); - } else { - result[targetKey] = val; - } - }; - for (let i2 = 0, l = arguments.length; i2 < l; i2++) { - arguments[i2] && forEach(arguments[i2], assignValue); - } - return result; -} -var extend = (a2, b2, thisArg, { allOwnKeys } = {}) => { - forEach(b2, (val, key) => { - if (thisArg && isFunction(val)) { - a2[key] = bind(val, thisArg); - } else { - a2[key] = val; - } - }, { allOwnKeys }); - return a2; -}; -var stripBOM = (content) => { - if (content.charCodeAt(0) === 65279) { - content = content.slice(1); - } - return content; -}; -var inherits = (constructor, superConstructor, props, descriptors2) => { - constructor.prototype = Object.create(superConstructor.prototype, descriptors2); - constructor.prototype.constructor = constructor; - Object.defineProperty(constructor, "super", { - value: superConstructor.prototype - }); - props && Object.assign(constructor.prototype, props); -}; -var toFlatObject = (sourceObj, destObj, filter2, propFilter) => { - let props; - let i2; - let prop; - const merged = {}; - destObj = destObj || {}; - if (sourceObj == null) - return destObj; - do { - props = Object.getOwnPropertyNames(sourceObj); - i2 = props.length; - while (i2-- > 0) { - prop = props[i2]; - if ((!propFilter || propFilter(prop, sourceObj, destObj)) && !merged[prop]) { - destObj[prop] = sourceObj[prop]; - merged[prop] = true; - } - } - sourceObj = filter2 !== false && getPrototypeOf(sourceObj); - } while (sourceObj && (!filter2 || filter2(sourceObj, destObj)) && sourceObj !== Object.prototype); - return destObj; -}; -var endsWith = (str, searchString, position) => { - str = String(str); - if (position === void 0 || position > str.length) { - position = str.length; - } - position -= searchString.length; - const lastIndex = str.indexOf(searchString, position); - return lastIndex !== -1 && lastIndex === position; -}; -var toArray = (thing) => { - if (!thing) - return null; - if (isArray(thing)) - return thing; - let i2 = thing.length; - if (!isNumber(i2)) - return null; - const arr = new Array(i2); - while (i2-- > 0) { - arr[i2] = thing[i2]; - } - return arr; -}; -var isTypedArray = ((TypedArray) => { - return (thing) => { - return TypedArray && thing instanceof TypedArray; - }; -})(typeof Uint8Array !== "undefined" && getPrototypeOf(Uint8Array)); -var forEachEntry = (obj, fn) => { - const generator = obj && obj[Symbol.iterator]; - const iterator = generator.call(obj); - let result; - while ((result = iterator.next()) && !result.done) { - const pair = result.value; - fn.call(obj, pair[0], pair[1]); - } -}; -var matchAll = (regExp, str) => { - let matches; - const arr = []; - while ((matches = regExp.exec(str)) !== null) { - arr.push(matches); - } - return arr; -}; -var isHTMLForm = kindOfTest("HTMLFormElement"); -var toCamelCase = (str) => { - return str.toLowerCase().replace( - /[-_\s]([a-z\d])(\w*)/g, - function replacer(m3, p1, p2) { - return p1.toUpperCase() + p2; - } - ); -}; -var hasOwnProperty = (({ hasOwnProperty: hasOwnProperty2 }) => (obj, prop) => hasOwnProperty2.call(obj, prop))(Object.prototype); -var isRegExp = kindOfTest("RegExp"); -var reduceDescriptors = (obj, reducer) => { - const descriptors2 = Object.getOwnPropertyDescriptors(obj); - const reducedDescriptors = {}; - forEach(descriptors2, (descriptor, name) => { - if (reducer(descriptor, name, obj) !== false) { - reducedDescriptors[name] = descriptor; - } - }); - Object.defineProperties(obj, reducedDescriptors); -}; -var freezeMethods = (obj) => { - reduceDescriptors(obj, (descriptor, name) => { - if (isFunction(obj) && ["arguments", "caller", "callee"].indexOf(name) !== -1) { - return false; - } - const value = obj[name]; - if (!isFunction(value)) - return; - descriptor.enumerable = false; - if ("writable" in descriptor) { - descriptor.writable = false; - return; - } - if (!descriptor.set) { - descriptor.set = () => { - throw Error("Can not rewrite read-only method '" + name + "'"); - }; - } - }); -}; -var toObjectSet = (arrayOrString, delimiter) => { - const obj = {}; - const define = (arr) => { - arr.forEach((value) => { - obj[value] = true; - }); +var freezeMethods = (obj) => { + reduceDescriptors(obj, (descriptor, name) => { + if (isFunction(obj) && ["arguments", "caller", "callee"].indexOf(name) !== -1) { + return false; + } + const value = obj[name]; + if (!isFunction(value)) + return; + descriptor.enumerable = false; + if ("writable" in descriptor) { + descriptor.writable = false; + return; + } + if (!descriptor.set) { + descriptor.set = () => { + throw Error("Can not rewrite read-only method '" + name + "'"); + }; + } + }); +}; +var toObjectSet = (arrayOrString, delimiter) => { + const obj = {}; + const define = (arr) => { + arr.forEach((value) => { + obj[value] = true; + }); }; isArray(arrayOrString) ? define(arrayOrString) : define(String(arrayOrString).split(delimiter)); return obj; @@ -27120,260 +26706,760 @@ var Axios = class { } } try { - promise = dispatchRequest.call(this, newConfig); + promise = dispatchRequest.call(this, newConfig); + } catch (error) { + return Promise.reject(error); + } + i2 = 0; + len = responseInterceptorChain.length; + while (i2 < len) { + promise = promise.then(responseInterceptorChain[i2++], responseInterceptorChain[i2++]); + } + return promise; + } + getUri(config7) { + config7 = mergeConfig(this.defaults, config7); + const fullPath = buildFullPath(config7.baseURL, config7.url); + return buildURL(fullPath, config7.params, config7.paramsSerializer); + } +}; +utils_default.forEach(["delete", "get", "head", "options"], function forEachMethodNoData2(method) { + Axios.prototype[method] = function(url2, config7) { + return this.request(mergeConfig(config7 || {}, { + method, + url: url2, + data: (config7 || {}).data + })); + }; +}); +utils_default.forEach(["post", "put", "patch"], function forEachMethodWithData2(method) { + function generateHTTPMethod(isForm) { + return function httpMethod(url2, data, config7) { + return this.request(mergeConfig(config7 || {}, { + method, + headers: isForm ? { + "Content-Type": "multipart/form-data" + } : {}, + url: url2, + data + })); + }; + } + Axios.prototype[method] = generateHTTPMethod(); + Axios.prototype[method + "Form"] = generateHTTPMethod(true); +}); +var Axios_default = Axios; + +// node_modules/axios/lib/cancel/CancelToken.js +var CancelToken = class { + constructor(executor) { + if (typeof executor !== "function") { + throw new TypeError("executor must be a function."); + } + let resolvePromise; + this.promise = new Promise(function promiseExecutor(resolve) { + resolvePromise = resolve; + }); + const token = this; + this.promise.then((cancel) => { + if (!token._listeners) + return; + let i2 = token._listeners.length; + while (i2-- > 0) { + token._listeners[i2](cancel); + } + token._listeners = null; + }); + this.promise.then = (onfulfilled) => { + let _resolve; + const promise = new Promise((resolve) => { + token.subscribe(resolve); + _resolve = resolve; + }).then(onfulfilled); + promise.cancel = function reject() { + token.unsubscribe(_resolve); + }; + return promise; + }; + executor(function cancel(message, config7, request) { + if (token.reason) { + return; + } + token.reason = new CanceledError_default(message, config7, request); + resolvePromise(token.reason); + }); + } + throwIfRequested() { + if (this.reason) { + throw this.reason; + } + } + subscribe(listener) { + if (this.reason) { + listener(this.reason); + return; + } + if (this._listeners) { + this._listeners.push(listener); + } else { + this._listeners = [listener]; + } + } + unsubscribe(listener) { + if (!this._listeners) { + return; + } + const index = this._listeners.indexOf(listener); + if (index !== -1) { + this._listeners.splice(index, 1); + } + } + static source() { + let cancel; + const token = new CancelToken(function executor(c) { + cancel = c; + }); + return { + token, + cancel + }; + } +}; +var CancelToken_default = CancelToken; + +// node_modules/axios/lib/helpers/spread.js +function spread(callback) { + return function wrap(arr) { + return callback.apply(null, arr); + }; +} + +// node_modules/axios/lib/helpers/isAxiosError.js +function isAxiosError(payload) { + return utils_default.isObject(payload) && payload.isAxiosError === true; +} + +// node_modules/axios/lib/helpers/HttpStatusCode.js +var HttpStatusCode = { + Continue: 100, + SwitchingProtocols: 101, + Processing: 102, + EarlyHints: 103, + Ok: 200, + Created: 201, + Accepted: 202, + NonAuthoritativeInformation: 203, + NoContent: 204, + ResetContent: 205, + PartialContent: 206, + MultiStatus: 207, + AlreadyReported: 208, + ImUsed: 226, + MultipleChoices: 300, + MovedPermanently: 301, + Found: 302, + SeeOther: 303, + NotModified: 304, + UseProxy: 305, + Unused: 306, + TemporaryRedirect: 307, + PermanentRedirect: 308, + BadRequest: 400, + Unauthorized: 401, + PaymentRequired: 402, + Forbidden: 403, + NotFound: 404, + MethodNotAllowed: 405, + NotAcceptable: 406, + ProxyAuthenticationRequired: 407, + RequestTimeout: 408, + Conflict: 409, + Gone: 410, + LengthRequired: 411, + PreconditionFailed: 412, + PayloadTooLarge: 413, + UriTooLong: 414, + UnsupportedMediaType: 415, + RangeNotSatisfiable: 416, + ExpectationFailed: 417, + ImATeapot: 418, + MisdirectedRequest: 421, + UnprocessableEntity: 422, + Locked: 423, + FailedDependency: 424, + TooEarly: 425, + UpgradeRequired: 426, + PreconditionRequired: 428, + TooManyRequests: 429, + RequestHeaderFieldsTooLarge: 431, + UnavailableForLegalReasons: 451, + InternalServerError: 500, + NotImplemented: 501, + BadGateway: 502, + ServiceUnavailable: 503, + GatewayTimeout: 504, + HttpVersionNotSupported: 505, + VariantAlsoNegotiates: 506, + InsufficientStorage: 507, + LoopDetected: 508, + NotExtended: 510, + NetworkAuthenticationRequired: 511 +}; +Object.entries(HttpStatusCode).forEach(([key, value]) => { + HttpStatusCode[value] = key; +}); +var HttpStatusCode_default = HttpStatusCode; + +// node_modules/axios/lib/axios.js +function createInstance(defaultConfig) { + const context2 = new Axios_default(defaultConfig); + const instance = bind(Axios_default.prototype.request, context2); + utils_default.extend(instance, Axios_default.prototype, context2, { allOwnKeys: true }); + utils_default.extend(instance, context2, null, { allOwnKeys: true }); + instance.create = function create(instanceConfig) { + return createInstance(mergeConfig(defaultConfig, instanceConfig)); + }; + return instance; +} +var axios = createInstance(defaults_default); +axios.Axios = Axios_default; +axios.CanceledError = CanceledError_default; +axios.CancelToken = CancelToken_default; +axios.isCancel = isCancel; +axios.VERSION = VERSION; +axios.toFormData = toFormData_default; +axios.AxiosError = AxiosError_default; +axios.Cancel = axios.CanceledError; +axios.all = function all(promises) { + return Promise.all(promises); +}; +axios.spread = spread; +axios.isAxiosError = isAxiosError; +axios.mergeConfig = mergeConfig; +axios.AxiosHeaders = AxiosHeaders_default; +axios.formToJSON = (thing) => formDataToJSON_default(utils_default.isHTMLForm(thing) ? new FormData(thing) : thing); +axios.HttpStatusCode = HttpStatusCode_default; +axios.default = axios; +var axios_default = axios; + +// node_modules/axios/index.js +var { + Axios: Axios2, + AxiosError: AxiosError2, + CanceledError: CanceledError2, + isCancel: isCancel2, + CancelToken: CancelToken2, + VERSION: VERSION2, + all: all2, + Cancel, + isAxiosError: isAxiosError2, + spread: spread2, + toFormData: toFormData2, + AxiosHeaders: AxiosHeaders2, + HttpStatusCode: HttpStatusCode2, + formToJSON, + mergeConfig: mergeConfig2 +} = axios_default; + +// src/engine/ollama.ts +var OllamaAi = class { + constructor(model = "mistral", basePath2 = "http://localhost:11434") { + this.model = model; + this.basePath = basePath2; + } + async generateCommitMessage(messages) { + let prompt = messages.map((x2) => x2.content).join("\n"); + prompt += "Summarize above git diff in 10 words or less"; + const url2 = `${this.basePath}/api/generate`; + const p2 = { + model: this.model, + prompt, + stream: false + }; + try { + const response = await axios_default.post(url2, p2, { + headers: { + "Content-Type": "application/json" + } + }); + return response.data?.response; + } catch (err) { + const message = err.response?.data?.error ?? err.message; + throw new Error("local model issues. details: " + message); + } + } + static async validateBaseUrl(baseUrl) { + baseUrl = baseUrl.trim().replace(/\/$/, ""); + const url2 = `${baseUrl}/api/tags`; + try { + const response = await axios_default.get(url2, { + headers: { + "Content-Type": "application/json" + } + }); + return response.status === 200; + } catch (err) { + const message = err.response?.data?.error ?? err.message; + console.error("Failed to connect to api: " + err); + throw new Error("Failed to connect to api: " + message); + } + } + static async validateModel(model, baseUrl = "http://localhost:11434") { + const url2 = `${baseUrl}/api/show`; + const p2 = { + name: model + }; + try { + const response = await axios_default.post(url2, p2, { + headers: { + "Content-Type": "application/json" + } + }); + return response.status === 200; + } catch (err) { + const message = err.response?.data?.error ?? err.message; + throw new Error("local model issues. details: " + message); + } + } +}; +var ollamaAi = new OllamaAi(); + +// src/commands/config.ts +dotenv.config(); +var DEFAULT_MODEL_TOKEN_LIMIT = 4096; +var validateConfig = (key, condition, validationMessage) => { + if (!condition) { + ce( + `${source_default.red("\u2716")} Unsupported config key ${key}: ${validationMessage}` + ); + process.exit(1); + } +}; +var configValidators = { + ["OCO_OPENAI_API_KEY" /* OCO_OPENAI_API_KEY */](value, config7 = {}) { + validateConfig("API_KEY", value || config7.OCO_AI_PROVIDER == "ollama", "You need to provide an API key"); + validateConfig( + "OCO_OPENAI_API_KEY" /* OCO_OPENAI_API_KEY */, + value.startsWith("sk-"), + 'Must start with "sk-"' + ); + validateConfig( + "OCO_OPENAI_API_KEY" /* OCO_OPENAI_API_KEY */, + config7["OCO_OPENAI_BASE_PATH" /* OCO_OPENAI_BASE_PATH */] || value.length === 51, + "Must be 51 characters long" + ); + return value; + }, + ["OCO_DESCRIPTION" /* OCO_DESCRIPTION */](value) { + validateConfig( + "OCO_DESCRIPTION" /* OCO_DESCRIPTION */, + typeof value === "boolean", + "Must be true or false" + ); + return value; + }, + ["OCO_OPENAI_MAX_TOKENS" /* OCO_OPENAI_MAX_TOKENS */](value) { + if (typeof value === "string") { + value = parseInt(value); + validateConfig( + "OCO_OPENAI_MAX_TOKENS" /* OCO_OPENAI_MAX_TOKENS */, + !isNaN(value), + "Must be a number" + ); + } + validateConfig( + "OCO_OPENAI_MAX_TOKENS" /* OCO_OPENAI_MAX_TOKENS */, + value ? typeof value === "number" : void 0, + "Must be a number" + ); + return value; + }, + ["OCO_EMOJI" /* OCO_EMOJI */](value) { + validateConfig( + "OCO_EMOJI" /* OCO_EMOJI */, + typeof value === "boolean", + "Must be true or false" + ); + return value; + }, + ["OCO_LANGUAGE" /* OCO_LANGUAGE */](value) { + validateConfig( + "OCO_LANGUAGE" /* OCO_LANGUAGE */, + getI18nLocal(value), + `${value} is not supported yet` + ); + return getI18nLocal(value); + }, + ["OCO_OPENAI_BASE_PATH" /* OCO_OPENAI_BASE_PATH */](value) { + validateConfig( + "OCO_OPENAI_BASE_PATH" /* OCO_OPENAI_BASE_PATH */, + typeof value === "string", + "Must be string" + ); + return value; + }, + ["OCO_MODEL" /* OCO_MODEL */](value) { + validateConfig( + "OCO_MODEL" /* OCO_MODEL */, + [ + "gpt-3.5-turbo", + "gpt-4", + "gpt-3.5-turbo-16k", + "gpt-3.5-turbo-0613" + ].includes(value), + `${value} is not supported yet, use 'gpt-4', 'gpt-3.5-turbo-16k' (default), 'gpt-3.5-turbo-0613' or 'gpt-3.5-turbo'` + ); + return value; + }, + ["OCO_MESSAGE_TEMPLATE_PLACEHOLDER" /* OCO_MESSAGE_TEMPLATE_PLACEHOLDER */](value) { + validateConfig( + "OCO_MESSAGE_TEMPLATE_PLACEHOLDER" /* OCO_MESSAGE_TEMPLATE_PLACEHOLDER */, + value.startsWith("$"), + `${value} must start with $, for example: '$msg'` + ); + return value; + }, + ["OCO_PROMPT_MODULE" /* OCO_PROMPT_MODULE */](value) { + validateConfig( + "OCO_PROMPT_MODULE" /* OCO_PROMPT_MODULE */, + ["conventional-commit", "@commitlint"].includes(value), + `${value} is not supported yet, use '@commitlint' or 'conventional-commit' (default)` + ); + return value; + }, + ["OCO_AI_PROVIDER" /* OCO_AI_PROVIDER */](value) { + validateConfig( + "OCO_AI_PROVIDER" /* OCO_AI_PROVIDER */, + [ + "", + "openai", + "ollama" + ].includes(value), + `${value} is not supported yet, use 'ollama' or 'openai' (default)` + ); + return value; + }, + async ["OCO_OLLAMA_BASE_PATH" /* OCO_OLLAMA_BASE_PATH */](value) { + validateConfig( + "OCO_OLLAMA_BASE_PATH" /* OCO_OLLAMA_BASE_PATH */, + typeof value === "string", + "Must be string" + ); + validateConfig( + "OCO_OLLAMA_BASE_PATH" /* OCO_OLLAMA_BASE_PATH */, + await OllamaAi.validateBaseUrl(value), + "Must be valid ollama url" + ); + return value; + }, + async ["OCO_OLLAMA_MODEL" /* OCO_OLLAMA_MODEL */](value, config7 = {}) { + validateConfig( + "OCO_OLLAMA_MODEL" /* OCO_OLLAMA_MODEL */, + await OllamaAi.validateModel(value, config7?.OCO_OLLAMA_BASE_PATH), + `${value} is not supported yet, use 'mistral' (default)` + ); + return value; + } +}; +var configPath = (0, import_path.join)((0, import_os.homedir)(), ".opencommit"); +var getConfig = () => { + const configFromEnv = { + OCO_OPENAI_API_KEY: process.env.OCO_OPENAI_API_KEY, + OCO_OPENAI_MAX_TOKENS: process.env.OCO_OPENAI_MAX_TOKENS ? Number(process.env.OCO_OPENAI_MAX_TOKENS) : void 0, + OCO_OPENAI_BASE_PATH: process.env.OCO_OPENAI_BASE_PATH, + OCO_DESCRIPTION: process.env.OCO_DESCRIPTION === "true" ? true : false, + OCO_EMOJI: process.env.OCO_EMOJI === "true" ? true : false, + OCO_MODEL: process.env.OCO_MODEL || "gpt-3.5-turbo-16k", + OCO_LANGUAGE: process.env.OCO_LANGUAGE || "en", + OCO_MESSAGE_TEMPLATE_PLACEHOLDER: process.env.OCO_MESSAGE_TEMPLATE_PLACEHOLDER || "$msg", + OCO_PROMPT_MODULE: process.env.OCO_PROMPT_MODULE || "conventional-commit", + OCO_AI_PROVIDER: process.env.OCO_AI_PROVIDER || "openai", + OCO_OLLAMA_BASE_PATH: process.env.OCO_OLLAMA_BASE_PATH || "http://localhost:11434", + OCO_OLLAMA_MODEL: process.env.OCO_OLLAMA_MODEL || "mistral" + }; + const configExists = (0, import_fs.existsSync)(configPath); + if (!configExists) + return configFromEnv; + const configFile = (0, import_fs.readFileSync)(configPath, "utf8"); + const config7 = (0, import_ini.parse)(configFile); + for (const configKey of Object.keys(config7)) { + if (!config7[configKey] || ["null", "undefined"].includes(config7[configKey])) { + config7[configKey] = void 0; + continue; + } + try { + const validator = configValidators[configKey]; + const validValue = validator( + config7[configKey] ?? configFromEnv[configKey], + config7 + ); + config7[configKey] = validValue; } catch (error) { - return Promise.reject(error); - } - i2 = 0; - len = responseInterceptorChain.length; - while (i2 < len) { - promise = promise.then(responseInterceptorChain[i2++], responseInterceptorChain[i2++]); + ce( + `'${configKey}' name is invalid, it should be either 'OCO_${configKey.toUpperCase()}' or it doesn't exist.` + ); + ce( + `Manually fix the '.env' file or global '~/.opencommit' config file.` + ); + process.exit(1); } - return promise; - } - getUri(config7) { - config7 = mergeConfig(this.defaults, config7); - const fullPath = buildFullPath(config7.baseURL, config7.url); - return buildURL(fullPath, config7.params, config7.paramsSerializer); } + return config7; }; -utils_default.forEach(["delete", "get", "head", "options"], function forEachMethodNoData2(method) { - Axios.prototype[method] = function(url2, config7) { - return this.request(mergeConfig(config7 || {}, { - method, - url: url2, - data: (config7 || {}).data - })); - }; -}); -utils_default.forEach(["post", "put", "patch"], function forEachMethodWithData2(method) { - function generateHTTPMethod(isForm) { - return function httpMethod(url2, data, config7) { - return this.request(mergeConfig(config7 || {}, { - method, - headers: isForm ? { - "Content-Type": "multipart/form-data" - } : {}, - url: url2, - data - })); - }; - } - Axios.prototype[method] = generateHTTPMethod(); - Axios.prototype[method + "Form"] = generateHTTPMethod(true); -}); -var Axios_default = Axios; - -// node_modules/axios/lib/cancel/CancelToken.js -var CancelToken = class { - constructor(executor) { - if (typeof executor !== "function") { - throw new TypeError("executor must be a function."); +var setConfig = (keyValues) => { + const config7 = getConfig() || {}; + for (const [configKey, configValue] of keyValues) { + if (!configValidators.hasOwnProperty(configKey)) { + throw new Error(`Unsupported config key: ${configKey}`); } - let resolvePromise; - this.promise = new Promise(function promiseExecutor(resolve) { - resolvePromise = resolve; - }); - const token = this; - this.promise.then((cancel) => { - if (!token._listeners) - return; - let i2 = token._listeners.length; - while (i2-- > 0) { - token._listeners[i2](cancel); - } - token._listeners = null; - }); - this.promise.then = (onfulfilled) => { - let _resolve; - const promise = new Promise((resolve) => { - token.subscribe(resolve); - _resolve = resolve; - }).then(onfulfilled); - promise.cancel = function reject() { - token.unsubscribe(_resolve); - }; - return promise; - }; - executor(function cancel(message, config7, request) { - if (token.reason) { - return; - } - token.reason = new CanceledError_default(message, config7, request); - resolvePromise(token.reason); - }); - } - throwIfRequested() { - if (this.reason) { - throw this.reason; + let parsedConfigValue; + try { + parsedConfigValue = JSON.parse(configValue); + } catch (error) { + parsedConfigValue = configValue; } + const validValue = configValidators[configKey](parsedConfigValue, config7); + config7[configKey] = validValue; } - subscribe(listener) { - if (this.reason) { - listener(this.reason); - return; - } - if (this._listeners) { - this._listeners.push(listener); - } else { - this._listeners = [listener]; + (0, import_fs.writeFileSync)(configPath, (0, import_ini.stringify)(config7), "utf8"); + ce(`${source_default.green("\u2714")} Config successfully set`); +}; +var configCommand = G3( + { + name: "config" /* config */, + parameters: ["", ""] + }, + async (argv) => { + ae("opencommit \u2014 config"); + try { + const { mode: mode2, keyValues } = argv._; + if (mode2 === "get" /* get */) { + const config7 = getConfig() || {}; + for (const key of keyValues) { + ce(`${key}=${config7[key]}`); + } + } else if (mode2 === "set" /* set */) { + await setConfig( + keyValues.map((keyValue) => keyValue.split("=")) + ); + } else { + throw new Error( + `Unsupported mode: ${mode2}. Valid modes are: "set" and "get"` + ); + } + } catch (error) { + ce(`${source_default.red("\u2716")} ${error}`); + process.exit(1); } } - unsubscribe(listener) { - if (!this._listeners) { - return; - } - const index = this._listeners.indexOf(listener); - if (index !== -1) { - this._listeners.splice(index, 1); - } +); + +// src/prompts.ts +var import_openai3 = __toESM(require_dist2(), 1); + +// src/modules/commitlint/constants.ts +var COMMITLINT_LLM_CONFIG_PATH = `${process.env.PWD}/.opencommit-commitlint`; + +// src/modules/commitlint/crypto.ts +var import_crypto = __toESM(require("crypto"), 1); +var computeHash = async (content, algorithm = "sha256") => { + try { + const hash = import_crypto.default.createHash(algorithm); + hash.update(content); + return hash.digest("hex"); + } catch (error) { + console.error("Error while computing hash:", error); + throw error; } - static source() { - let cancel; - const token = new CancelToken(function executor(c) { - cancel = c; - }); - return { - token, - cancel - }; +}; + +// src/modules/commitlint/prompts.ts +var import_openai = __toESM(require_dist2(), 1); +var import_types = __toESM(require_lib4(), 1); +var config2 = getConfig(); +var translation = i18n[config2?.OCO_LANGUAGE || "en"]; +var getTypeRuleExtraDescription = (type, prompt) => prompt?.questions?.type?.enum?.[type]?.description; +var llmReadableRules = { + blankline: (key, applicable) => `There should ${applicable} be a blank line at the beginning of the ${key}.`, + caseRule: (key, applicable, value) => `The ${key} should ${applicable} be in ${Array.isArray(value) ? `one of the following case: + - ${value.join("\n - ")}.` : `${value} case.`}`, + emptyRule: (key, applicable) => `The ${key} should ${applicable} be empty.`, + enumRule: (key, applicable, value) => `The ${key} should ${applicable} be one of the following values: + - ${Array.isArray(value) ? value.join("\n - ") : value}.`, + enumTypeRule: (key, applicable, value, prompt) => `The ${key} should ${applicable} be one of the following values: + - ${Array.isArray(value) ? value.map((v2) => { + const description = getTypeRuleExtraDescription(v2, prompt); + if (description) { + return `${v2} (${description})`; + } else + return v2; + }).join("\n - ") : value}.`, + fullStopRule: (key, applicable, value) => `The ${key} should ${applicable} end with '${value}'.`, + maxLengthRule: (key, applicable, value) => `The ${key} should ${applicable} have ${value} characters or less.`, + minLengthRule: (key, applicable, value) => `The ${key} should ${applicable} have ${value} characters or more.` +}; +var rulesPrompts = { + "body-case": (applicable, value) => llmReadableRules.caseRule("body", applicable, value), + "body-empty": (applicable) => llmReadableRules.emptyRule("body", applicable, void 0), + "body-full-stop": (applicable, value) => llmReadableRules.fullStopRule("body", applicable, value), + "body-leading-blank": (applicable) => llmReadableRules.blankline("body", applicable, void 0), + "body-max-length": (applicable, value) => llmReadableRules.maxLengthRule("body", applicable, value), + "body-max-line-length": (applicable, value) => `Each line of the body should ${applicable} have ${value} characters or less.`, + "body-min-length": (applicable, value) => llmReadableRules.minLengthRule("body", applicable, value), + "footer-case": (applicable, value) => llmReadableRules.caseRule("footer", applicable, value), + "footer-empty": (applicable) => llmReadableRules.emptyRule("footer", applicable, void 0), + "footer-leading-blank": (applicable) => llmReadableRules.blankline("footer", applicable, void 0), + "footer-max-length": (applicable, value) => llmReadableRules.maxLengthRule("footer", applicable, value), + "footer-max-line-length": (applicable, value) => `Each line of the footer should ${applicable} have ${value} characters or less.`, + "footer-min-length": (applicable, value) => llmReadableRules.minLengthRule("footer", applicable, value), + "header-case": (applicable, value) => llmReadableRules.caseRule("header", applicable, value), + "header-full-stop": (applicable, value) => llmReadableRules.fullStopRule("header", applicable, value), + "header-max-length": (applicable, value) => llmReadableRules.maxLengthRule("header", applicable, value), + "header-min-length": (applicable, value) => llmReadableRules.minLengthRule("header", applicable, value), + "references-empty": (applicable) => llmReadableRules.emptyRule("references section", applicable, void 0), + "scope-case": (applicable, value) => llmReadableRules.caseRule("scope", applicable, value), + "scope-empty": (applicable) => llmReadableRules.emptyRule("scope", applicable, void 0), + "scope-enum": (applicable, value) => llmReadableRules.enumRule("type", applicable, value), + "scope-max-length": (applicable, value) => llmReadableRules.maxLengthRule("scope", applicable, value), + "scope-min-length": (applicable, value) => llmReadableRules.minLengthRule("scope", applicable, value), + "signed-off-by": (applicable, value) => `The commit message should ${applicable} have a "Signed-off-by" line with the value "${value}".`, + "subject-case": (applicable, value) => llmReadableRules.caseRule("subject", applicable, value), + "subject-empty": (applicable) => llmReadableRules.emptyRule("subject", applicable, void 0), + "subject-full-stop": (applicable, value) => llmReadableRules.fullStopRule("subject", applicable, value), + "subject-max-length": (applicable, value) => llmReadableRules.maxLengthRule("subject", applicable, value), + "subject-min-length": (applicable, value) => llmReadableRules.minLengthRule("subject", applicable, value), + "type-case": (applicable, value) => llmReadableRules.caseRule("type", applicable, value), + "type-empty": (applicable) => llmReadableRules.emptyRule("type", applicable, void 0), + "type-enum": (applicable, value, prompt) => llmReadableRules.enumTypeRule("type", applicable, value, prompt), + "type-max-length": (applicable, value) => llmReadableRules.maxLengthRule("type", applicable, value), + "type-min-length": (applicable, value) => llmReadableRules.minLengthRule("type", applicable, value) +}; +var getPrompt = (ruleName, ruleConfig, prompt) => { + const [severity, applicable, value] = ruleConfig; + if (severity === import_types.RuleConfigSeverity.Disabled) + return null; + const promptFn = rulesPrompts[ruleName]; + if (promptFn) { + return promptFn(applicable, value, prompt); } + ce(`${source_default.red("\u2716")} No prompt handler for rule "${ruleName}".`); + return `Please manualy set the prompt for rule "${ruleName}".`; +}; +var inferPromptsFromCommitlintConfig = (config7) => { + const { rules, prompt } = config7; + if (!rules) + return []; + return Object.keys(rules).map( + (ruleName) => getPrompt(ruleName, rules[ruleName], prompt) + ).filter((prompt2) => prompt2 !== null); }; -var CancelToken_default = CancelToken; +var STRUCTURE_OF_COMMIT = ` +- Header of commit is composed of type, scope, subject: (): +- Description of commit is composed of body and footer (optional): +`; +var GEN_COMMITLINT_CONSISTENCY_PROMPT = (prompts) => [ + { + role: import_openai.ChatCompletionRequestMessageRoleEnum.Assistant, + content: `${IDENTITY} Your mission is to create clean and comprehensive commit messages for two different changes in a single codebase and output them in the provided JSON format: one for a bug fix and another for a new feature. -// node_modules/axios/lib/helpers/spread.js -function spread(callback) { - return function wrap(arr) { - return callback.apply(null, arr); - }; -} +Here are the specific requirements and conventions that should be strictly followed: -// node_modules/axios/lib/helpers/isAxiosError.js -function isAxiosError(payload) { - return utils_default.isObject(payload) && payload.isAxiosError === true; +Commit Message Conventions: +- The commit message consists of three parts: Header, Body, and Footer. +- Header: + - Format: \`(): \` +- ${prompts.join("\n- ")} + +JSON Output Format: +- The JSON output should contain the commit messages for a bug fix and a new feature in the following format: +\`\`\`json +{ + "localLanguage": "${translation.localLanguage}", + "commitFix": "
", + "commitFeat": "
", + "commitDescription": "" } +\`\`\` +- The "commitDescription" should not include the commit message\u2019s header, only the description. +- Description should not be more than 74 characters. -// node_modules/axios/lib/helpers/HttpStatusCode.js -var HttpStatusCode = { - Continue: 100, - SwitchingProtocols: 101, - Processing: 102, - EarlyHints: 103, - Ok: 200, - Created: 201, - Accepted: 202, - NonAuthoritativeInformation: 203, - NoContent: 204, - ResetContent: 205, - PartialContent: 206, - MultiStatus: 207, - AlreadyReported: 208, - ImUsed: 226, - MultipleChoices: 300, - MovedPermanently: 301, - Found: 302, - SeeOther: 303, - NotModified: 304, - UseProxy: 305, - Unused: 306, - TemporaryRedirect: 307, - PermanentRedirect: 308, - BadRequest: 400, - Unauthorized: 401, - PaymentRequired: 402, - Forbidden: 403, - NotFound: 404, - MethodNotAllowed: 405, - NotAcceptable: 406, - ProxyAuthenticationRequired: 407, - RequestTimeout: 408, - Conflict: 409, - Gone: 410, - LengthRequired: 411, - PreconditionFailed: 412, - PayloadTooLarge: 413, - UriTooLong: 414, - UnsupportedMediaType: 415, - RangeNotSatisfiable: 416, - ExpectationFailed: 417, - ImATeapot: 418, - MisdirectedRequest: 421, - UnprocessableEntity: 422, - Locked: 423, - FailedDependency: 424, - TooEarly: 425, - UpgradeRequired: 426, - PreconditionRequired: 428, - TooManyRequests: 429, - RequestHeaderFieldsTooLarge: 431, - UnavailableForLegalReasons: 451, - InternalServerError: 500, - NotImplemented: 501, - BadGateway: 502, - ServiceUnavailable: 503, - GatewayTimeout: 504, - HttpVersionNotSupported: 505, - VariantAlsoNegotiates: 506, - InsufficientStorage: 507, - LoopDetected: 508, - NotExtended: 510, - NetworkAuthenticationRequired: 511 -}; -Object.entries(HttpStatusCode).forEach(([key, value]) => { - HttpStatusCode[value] = key; +Additional Details: +- Changing the variable 'port' to uppercase 'PORT' is considered a bug fix. +- Allowing the server to listen on a port specified through the environment variable is considered a new feature. + +Example Git Diff is to follow:` + }, + INIT_DIFF_PROMPT +]; +var INIT_MAIN_PROMPT = (language, prompts) => ({ + role: import_openai.ChatCompletionRequestMessageRoleEnum.System, + content: `${IDENTITY} Your mission is to create clean and comprehensive commit messages in the given @commitlint convention and explain WHAT were the changes and WHY the changes were done. I'll send you an output of 'git diff --staged' command, and you convert it into a commit message. +${config2?.OCO_EMOJI ? "Use GitMoji convention to preface the commit." : "Do not preface the commit with anything."} +${config2?.OCO_DESCRIPTION ? `Add a short description of WHY the changes are done after the commit message. Don't start it with "This commit", just describe the changes.` : "Don't add any descriptions to the commit, only commit message."} +Use the present tense. Use ${language} to answer. + +You will strictly follow the following conventions to generate the content of the commit message: +- ${prompts.join("\n- ")} + +The conventions refers to the following structure of commit message: +${STRUCTURE_OF_COMMIT} + + ` }); -var HttpStatusCode_default = HttpStatusCode; +var commitlintPrompts = { + INIT_MAIN_PROMPT, + GEN_COMMITLINT_CONSISTENCY_PROMPT +}; -// node_modules/axios/lib/axios.js -function createInstance(defaultConfig) { - const context2 = new Axios_default(defaultConfig); - const instance = bind(Axios_default.prototype.request, context2); - utils_default.extend(instance, Axios_default.prototype, context2, { allOwnKeys: true }); - utils_default.extend(instance, context2, null, { allOwnKeys: true }); - instance.create = function create(instanceConfig) { - return createInstance(mergeConfig(defaultConfig, instanceConfig)); - }; - return instance; -} -var axios = createInstance(defaults_default); -axios.Axios = Axios_default; -axios.CanceledError = CanceledError_default; -axios.CancelToken = CancelToken_default; -axios.isCancel = isCancel; -axios.VERSION = VERSION; -axios.toFormData = toFormData_default; -axios.AxiosError = AxiosError_default; -axios.Cancel = axios.CanceledError; -axios.all = function all(promises) { - return Promise.all(promises); +// src/modules/commitlint/pwd-commitlint.ts +var import_path2 = __toESM(require("path"), 1); +var nodeModulesPath = import_path2.default.join( + process.env.PWD || process.cwd(), + "node_modules", + "@commitlint", + "load" +); +var getCommitLintPWDConfig = async () => { + const load = require(nodeModulesPath).default; + if (load && typeof load === "function") { + return await load(); + } + return null; }; -axios.spread = spread; -axios.isAxiosError = isAxiosError; -axios.mergeConfig = mergeConfig; -axios.AxiosHeaders = AxiosHeaders_default; -axios.formToJSON = (thing) => formDataToJSON_default(utils_default.isHTMLForm(thing) ? new FormData(thing) : thing); -axios.HttpStatusCode = HttpStatusCode_default; -axios.default = axios; -var axios_default = axios; -// node_modules/axios/index.js -var { - Axios: Axios2, - AxiosError: AxiosError2, - CanceledError: CanceledError2, - isCancel: isCancel2, - CancelToken: CancelToken2, - VERSION: VERSION2, - all: all2, - Cancel, - isAxiosError: isAxiosError2, - spread: spread2, - toFormData: toFormData2, - AxiosHeaders: AxiosHeaders2, - HttpStatusCode: HttpStatusCode2, - formToJSON, - mergeConfig: mergeConfig2 -} = axios_default; +// src/modules/commitlint/utils.ts +var import_promises = __toESM(require("fs/promises"), 1); +var removeDoubleNewlines = (input) => { + const pattern = /\\n\\n/g; + if (pattern.test(input)) { + const newInput = input.replace(pattern, ""); + return removeDoubleNewlines(newInput); + } + return input; +}; +var commitlintLLMConfigExists = async () => { + let exists; + try { + await import_promises.default.access(COMMITLINT_LLM_CONFIG_PATH); + exists = true; + } catch (e2) { + exists = false; + } + return exists; +}; +var writeCommitlintLLMConfig = async (commitlintLLMConfig) => { + await import_promises.default.writeFile( + COMMITLINT_LLM_CONFIG_PATH, + JSON.stringify(commitlintLLMConfig, null, 2) + ); +}; +var getCommitlintLLMConfig = async () => { + const content = await import_promises.default.readFile(COMMITLINT_LLM_CONFIG_PATH); + const commitLintLLMConfig = JSON.parse( + content.toString() + ); + return commitLintLLMConfig; +}; // src/engine/openAi.ts var import_openai2 = __toESM(require_dist2(), 1); @@ -27457,40 +27543,14 @@ var OpenAi = class { }; var api = new OpenAi(); -// src/engine/ollama.ts -var OllamaAi = class { - async generateCommitMessage(messages) { - const model = "mistral"; - let prompt = messages.map((x2) => x2.content).join("\n"); - prompt += "Summarize above git diff in 10 words or less"; - const url2 = "http://localhost:11434/api/generate"; - const p2 = { - model, - prompt, - stream: false - }; - try { - const response = await axios_default.post(url2, p2, { - headers: { - "Content-Type": "application/json" - } - }); - const answer = response.data?.response; - console.log("answer", answer); - return answer; - } catch (err) { - const message = err.response?.data?.error ?? err.message; - throw new Error("local model issues. details: " + message); - } - } -}; -var ollamaAi = new OllamaAi(); - // src/utils/engine.ts function getEngine() { const config7 = getConfig(); if (config7?.OCO_AI_PROVIDER == "ollama") { - return ollamaAi; + return new OllamaAi( + config7?.OCO_OLLAMA_MODEL || "mistral", + config7?.OCO_OLLAMA_BASE_PATH || "http://localhost:11434" + ); } return api; }