diff --git a/out/cli.cjs b/out/cli.cjs index a4fe603b..13024407 100755 --- a/out/cli.cjs +++ b/out/cli.cjs @@ -431,8 +431,8 @@ var require_escape = __commonJS({ } function escapeArgument(arg, doubleEscapeMetaChars) { arg = `${arg}`; - arg = arg.replace(/(\\*)"/g, '$1$1\\"'); - arg = arg.replace(/(\\*)$/, "$1$1"); + arg = arg.replace(/(?=(\\+?)?)\1"/g, '$1$1\\"'); + arg = arg.replace(/(?=(\\+?)?)\1$/, "$1$1"); arg = `"${arg}"`; arg = arg.replace(metaCharsRegExp, "^$1"); if (doubleEscapeMetaChars) { @@ -578,7 +578,7 @@ var require_enoent = __commonJS({ const originalEmit = cp.emit; cp.emit = function(name, arg1) { if (name === "exit") { - const err = verifyENOENT(arg1, parsed, "spawn"); + const err = verifyENOENT(arg1, parsed); if (err) { return originalEmit.call(cp, "error", err); } @@ -1672,7 +1672,7 @@ var require_mappingTable = __commonJS({ var require_tr46 = __commonJS({ "node_modules/node-fetch/node_modules/tr46/index.js"(exports, module2) { "use strict"; - var punycode = require("punycode"); + const punycode = require('punycode/'); var mappingTable = require_mappingTable(); var PROCESSING_OPTIONS = { TRANSITIONAL: 0, @@ -1834,7 +1834,7 @@ var require_tr46 = __commonJS({ var require_url_state_machine = __commonJS({ "node_modules/node-fetch/node_modules/whatwg-url/lib/url-state-machine.js"(exports, module2) { "use strict"; - var punycode = require("punycode"); + const punycode = require('punycode/'); var tr46 = require_tr46(); var specialSchemes = { ftp: 21, @@ -27389,7 +27389,8 @@ var package_default = { "test:unit:docker": "npm run test:docker-build && DOCKER_CONTENT_TRUST=0 docker run --rm oco-test npm run test:unit", "test:e2e": "npm run test:e2e:setup && jest test/e2e", "test:e2e:setup": "sh test/e2e/setup.sh", - "test:e2e:docker": "npm run test:docker-build && DOCKER_CONTENT_TRUST=0 docker run --rm oco-test npm run test:e2e" + "test:e2e:docker": "npm run test:docker-build && DOCKER_CONTENT_TRUST=0 docker run --rm oco-test npm run test:e2e", + "mlx:start": "OCO_AI_PROVIDER='mlx' node ./out/cli.cjs" }, devDependencies: { "@commitlint/types": "^17.4.4", @@ -29933,6 +29934,8 @@ var getDefaultModel = (provider) => { switch (provider) { case "ollama": return ""; + case "mlx": + return ""; case "anthropic": return MODEL_LIST.anthropic[0]; case "gemini": @@ -29964,7 +29967,7 @@ var configValidators = { validateConfig( "OCO_API_KEY", value, - 'You need to provide the OCO_API_KEY when OCO_AI_PROVIDER set to "openai" (default) or "ollama" or "azure" or "gemini" or "flowise" or "anthropic". Run `oco config set OCO_API_KEY=your_key OCO_AI_PROVIDER=openai`' + 'You need to provide the OCO_API_KEY when OCO_AI_PROVIDER set to "openai" (default) or "ollama" or "mlx" or "azure" or "gemini" or "flowise" or "anthropic". Run `oco config set OCO_API_KEY=your_key OCO_AI_PROVIDER=openai`' ); return value; }, @@ -30070,8 +30073,8 @@ var configValidators = { "test", "flowise", "groq" - ].includes(value) || value.startsWith("ollama"), - `${value} is not supported yet, use 'ollama', 'anthropic', 'azure', 'gemini', 'flowise' or 'openai' (default)` + ].includes(value) || value.startsWith("ollama") || value.startsWith("mlx"), + `${value} is not supported yet, use 'ollama', 'mlx', anthropic', 'azure', 'gemini', 'flowise' or 'openai' (default)` ); return value; }, @@ -30111,6 +30114,7 @@ var OCO_AI_PROVIDER_ENUM = /* @__PURE__ */ ((OCO_AI_PROVIDER_ENUM2) => { OCO_AI_PROVIDER_ENUM2["TEST"] = "test"; OCO_AI_PROVIDER_ENUM2["FLOWISE"] = "flowise"; OCO_AI_PROVIDER_ENUM2["GROQ"] = "groq"; + OCO_AI_PROVIDER_ENUM2["MLX"] = "mlx"; return OCO_AI_PROVIDER_ENUM2; })(OCO_AI_PROVIDER_ENUM || {}); var defaultConfigPath = (0, import_path.join)((0, import_os.homedir)(), ".opencommit"); @@ -44524,6 +44528,38 @@ var GroqEngine = class extends OpenAiEngine { } }; +// src/engine/mlx.ts +var MLXEngine = class { + constructor(config7) { + this.config = config7; + this.client = axios_default.create({ + url: config7.baseURL ? `${config7.baseURL}/${config7.apiKey}` : "http://localhost:8080/v1/chat/completions", + headers: { "Content-Type": "application/json" } + }); + } + async generateCommitMessage(messages) { + const params = { + messages, + temperature: 0, + top_p: 0.1, + repetition_penalty: 1.5, + stream: false + }; + try { + const response = await this.client.post( + this.client.getUri(this.config), + params + ); + const choices = response.data.choices; + const message = choices[0].message; + return message?.content; + } catch (err) { + const message = err.response?.data?.error ?? err.message; + throw new Error(`MLX provider error: ${message}`); + } + } +}; + // src/utils/engine.ts function getEngine() { const config7 = getConfig(); @@ -44550,6 +44586,8 @@ function getEngine() { return new FlowiseEngine(DEFAULT_CONFIG2); case "groq" /* GROQ */: return new GroqEngine(DEFAULT_CONFIG2); + case "mlx" /* MLX */: + return new MLXEngine(DEFAULT_CONFIG2); default: return new OpenAiEngine(DEFAULT_CONFIG2); } @@ -44931,7 +44969,14 @@ var CONVENTIONAL_COMMIT_KEYWORDS = "Do not preface the commit with anything, exc var getCommitConvention = (fullGitMojiSpec) => config4.OCO_EMOJI ? fullGitMojiSpec ? FULL_GITMOJI_SPEC : GITMOJI_HELP : CONVENTIONAL_COMMIT_KEYWORDS; var getDescriptionInstruction = () => config4.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."; var getOneLineCommitInstruction = () => config4.OCO_ONE_LINE_COMMIT ? "Craft a concise commit message that encapsulates all changes made, with an emphasis on the primary updates. If the modifications share a common theme or scope, mention it succinctly; otherwise, leave the scope out to maintain focus. The goal is to provide a clear and unified overview of the changes in a one single message, without diverging into a list of commit per file change." : ""; -var INIT_MAIN_PROMPT2 = (language, fullGitMojiSpec) => ({ +var userInputCodeContext = (context) => { + if (context !== "" && context !== " ") { + return `Additional context provided by the user: ${context} +Consider this context when generating the commit message, incorporating relevant information when appropriate.`; + } + return ""; +}; +var INIT_MAIN_PROMPT2 = (language, fullGitMojiSpec, context) => ({ role: "system", content: (() => { const commitConvention = fullGitMojiSpec ? "GitMoji specification" : "Conventional Commit Convention"; @@ -44941,12 +44986,14 @@ var INIT_MAIN_PROMPT2 = (language, fullGitMojiSpec) => ({ const descriptionGuideline = getDescriptionInstruction(); const oneLineCommitGuideline = getOneLineCommitInstruction(); const generalGuidelines = `Use the present tense. Lines must not be longer than 74 characters. Use ${language} for the commit message.`; + const userInputContext = userInputCodeContext(context); return `${missionStatement} ${diffInstruction} ${conventionGuidelines} ${descriptionGuideline} ${oneLineCommitGuideline} -${generalGuidelines}`; +${generalGuidelines} +${userInputContext}`; })() }); var INIT_DIFF_PROMPT = { @@ -44988,7 +45035,7 @@ var INIT_CONSISTENCY_PROMPT = (translation4) => ({ role: "assistant", content: getContent(translation4) }); -var getMainCommitPrompt = async (fullGitMojiSpec) => { +var getMainCommitPrompt = async (fullGitMojiSpec, context) => { switch (config4.OCO_PROMPT_MODULE) { case "@commitlint": if (!await commitlintLLMConfigExists()) { @@ -45010,7 +45057,7 @@ var getMainCommitPrompt = async (fullGitMojiSpec) => { ]; default: return [ - INIT_MAIN_PROMPT2(translation3.localLanguage, fullGitMojiSpec), + INIT_MAIN_PROMPT2(translation3.localLanguage, fullGitMojiSpec, context), INIT_DIFF_PROMPT, INIT_CONSISTENCY_PROMPT(translation3) ]; @@ -45037,8 +45084,8 @@ function mergeDiffs(arr, maxStringLength) { var config5 = getConfig(); var MAX_TOKENS_INPUT = config5.OCO_TOKENS_MAX_INPUT; var MAX_TOKENS_OUTPUT = config5.OCO_TOKENS_MAX_OUTPUT; -var generateCommitMessageChatCompletionPrompt = async (diff, fullGitMojiSpec) => { - const INIT_MESSAGES_PROMPT = await getMainCommitPrompt(fullGitMojiSpec); +var generateCommitMessageChatCompletionPrompt = async (diff, fullGitMojiSpec, context) => { + const INIT_MESSAGES_PROMPT = await getMainCommitPrompt(fullGitMojiSpec, context); const chatContextAsCompletionRequest = [...INIT_MESSAGES_PROMPT]; chatContextAsCompletionRequest.push({ role: "user", @@ -45054,9 +45101,12 @@ var GenerateCommitMessageErrorEnum = ((GenerateCommitMessageErrorEnum2) => { return GenerateCommitMessageErrorEnum2; })(GenerateCommitMessageErrorEnum || {}); var ADJUSTMENT_FACTOR = 20; -var generateCommitMessageByDiff = async (diff, fullGitMojiSpec = false) => { +var generateCommitMessageByDiff = async (diff, fullGitMojiSpec = false, context = "") => { try { - const INIT_MESSAGES_PROMPT = await getMainCommitPrompt(fullGitMojiSpec); + const INIT_MESSAGES_PROMPT = await getMainCommitPrompt( + fullGitMojiSpec, + context + ); const INIT_MESSAGES_PROMPT_LENGTH = INIT_MESSAGES_PROMPT.map( (msg) => tokenCount(msg.content) + 4 ).reduce((a4, b7) => a4 + b7, 0); @@ -45076,7 +45126,8 @@ var generateCommitMessageByDiff = async (diff, fullGitMojiSpec = false) => { } const messages = await generateCommitMessageChatCompletionPrompt( diff, - fullGitMojiSpec + fullGitMojiSpec, + context ); const engine = getEngine(); const commitMessage = await engine.generateCommitMessage(messages); @@ -45283,6 +45334,7 @@ var checkMessageTemplate = (extraArgs2) => { var generateCommitMessageFromGitDiff = async ({ diff, extraArgs: extraArgs2, + context = "", fullGitMojiSpec = false, skipCommitConfirmation = false }) => { @@ -45292,7 +45344,8 @@ var generateCommitMessageFromGitDiff = async ({ try { let commitMessage = await generateCommitMessageByDiff( diff, - fullGitMojiSpec + fullGitMojiSpec, + context ); const messageTemplate = checkMessageTemplate(extraArgs2); if (config6.OCO_MESSAGE_TEMPLATE_PLACEHOLDER && typeof messageTemplate === "string") { @@ -45402,7 +45455,7 @@ ${source_default.grey("\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2 process.exit(1); } }; -async function commit(extraArgs2 = [], isStageAllFlag = false, fullGitMojiSpec = false, skipCommitConfirmation = false) { +async function commit(extraArgs2 = [], context = "", isStageAllFlag = false, fullGitMojiSpec = false, skipCommitConfirmation = false) { if (isStageAllFlag) { const changedFiles2 = await getChangedFiles(); if (changedFiles2) @@ -45433,7 +45486,7 @@ async function commit(extraArgs2 = [], isStageAllFlag = false, fullGitMojiSpec = if (hD2(isStageAllAndCommitConfirmedByUser)) process.exit(1); if (isStageAllAndCommitConfirmedByUser) { - await commit(extraArgs2, true, fullGitMojiSpec); + await commit(extraArgs2, context, true, fullGitMojiSpec); process.exit(1); } if (stagedFiles.length === 0 && changedFiles.length > 0) { @@ -45448,7 +45501,7 @@ async function commit(extraArgs2 = [], isStageAllFlag = false, fullGitMojiSpec = process.exit(1); await gitAdd({ files }); } - await commit(extraArgs2, false, fullGitMojiSpec); + await commit(extraArgs2, context, false, fullGitMojiSpec); process.exit(1); } stagedFilesSpinner.stop( @@ -45459,6 +45512,7 @@ ${stagedFiles.map((file) => ` ${file}`).join("\n")}` generateCommitMessageFromGitDiff({ diff: await getDiff({ files: stagedFiles }), extraArgs: extraArgs2, + context, fullGitMojiSpec, skipCommitConfirmation }) @@ -45817,6 +45871,12 @@ Z2( commands: [configCommand, hookCommand, commitlintConfigCommand], flags: { fgm: Boolean, + context: { + type: String, + alias: "c", + description: "Additional user input context for the commit message", + default: "" + }, yes: { type: Boolean, alias: "y", @@ -45833,7 +45893,7 @@ Z2( if (await isHookCalled()) { prepareCommitMessageHook(); } else { - commit(extraArgs, false, flags.fgm, flags.yes); + commit(extraArgs, flags.context, false, flags.fgm, flags.yes); } }, extraArgs diff --git a/out/github-action.cjs b/out/github-action.cjs index 5acd5e08..ea9abc46 100644 --- a/out/github-action.cjs +++ b/out/github-action.cjs @@ -20487,7 +20487,7 @@ var require_mappingTable = __commonJS({ var require_tr46 = __commonJS({ "node_modules/node-fetch/node_modules/tr46/index.js"(exports, module2) { "use strict"; - var punycode = require("punycode"); + const punycode = require('punycode/'); var mappingTable = require_mappingTable(); var PROCESSING_OPTIONS = { TRANSITIONAL: 0, @@ -20649,7 +20649,7 @@ var require_tr46 = __commonJS({ var require_url_state_machine = __commonJS({ "node_modules/node-fetch/node_modules/whatwg-url/lib/url-state-machine.js"(exports, module2) { "use strict"; - var punycode = require("punycode"); + const punycode = require('punycode/'); var tr46 = require_tr46(); var specialSchemes = { ftp: 21, @@ -48745,6 +48745,8 @@ var getDefaultModel = (provider) => { switch (provider) { case "ollama": return ""; + case "mlx": + return ""; case "anthropic": return MODEL_LIST.anthropic[0]; case "gemini": @@ -48776,7 +48778,7 @@ var configValidators = { validateConfig( "OCO_API_KEY", value, - 'You need to provide the OCO_API_KEY when OCO_AI_PROVIDER set to "openai" (default) or "ollama" or "azure" or "gemini" or "flowise" or "anthropic". Run `oco config set OCO_API_KEY=your_key OCO_AI_PROVIDER=openai`' + 'You need to provide the OCO_API_KEY when OCO_AI_PROVIDER set to "openai" (default) or "ollama" or "mlx" or "azure" or "gemini" or "flowise" or "anthropic". Run `oco config set OCO_API_KEY=your_key OCO_AI_PROVIDER=openai`' ); return value; }, @@ -48882,8 +48884,8 @@ var configValidators = { "test", "flowise", "groq" - ].includes(value) || value.startsWith("ollama"), - `${value} is not supported yet, use 'ollama', 'anthropic', 'azure', 'gemini', 'flowise' or 'openai' (default)` + ].includes(value) || value.startsWith("ollama") || value.startsWith("mlx"), + `${value} is not supported yet, use 'ollama', 'mlx', anthropic', 'azure', 'gemini', 'flowise' or 'openai' (default)` ); return value; }, @@ -63325,6 +63327,38 @@ var GroqEngine = class extends OpenAiEngine { } }; +// src/engine/mlx.ts +var MLXEngine = class { + constructor(config6) { + this.config = config6; + this.client = axios_default.create({ + url: config6.baseURL ? `${config6.baseURL}/${config6.apiKey}` : "http://localhost:8080/v1/chat/completions", + headers: { "Content-Type": "application/json" } + }); + } + async generateCommitMessage(messages) { + const params = { + messages, + temperature: 0, + top_p: 0.1, + repetition_penalty: 1.5, + stream: false + }; + try { + const response = await this.client.post( + this.client.getUri(this.config), + params + ); + const choices = response.data.choices; + const message = choices[0].message; + return message?.content; + } catch (err) { + const message = err.response?.data?.error ?? err.message; + throw new Error(`MLX provider error: ${message}`); + } + } +}; + // src/utils/engine.ts function getEngine() { const config6 = getConfig(); @@ -63351,6 +63385,8 @@ function getEngine() { return new FlowiseEngine(DEFAULT_CONFIG2); case "groq" /* GROQ */: return new GroqEngine(DEFAULT_CONFIG2); + case "mlx" /* MLX */: + return new MLXEngine(DEFAULT_CONFIG2); default: return new OpenAiEngine(DEFAULT_CONFIG2); } @@ -63732,7 +63768,14 @@ var CONVENTIONAL_COMMIT_KEYWORDS = "Do not preface the commit with anything, exc var getCommitConvention = (fullGitMojiSpec) => config4.OCO_EMOJI ? fullGitMojiSpec ? FULL_GITMOJI_SPEC : GITMOJI_HELP : CONVENTIONAL_COMMIT_KEYWORDS; var getDescriptionInstruction = () => config4.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."; var getOneLineCommitInstruction = () => config4.OCO_ONE_LINE_COMMIT ? "Craft a concise commit message that encapsulates all changes made, with an emphasis on the primary updates. If the modifications share a common theme or scope, mention it succinctly; otherwise, leave the scope out to maintain focus. The goal is to provide a clear and unified overview of the changes in a one single message, without diverging into a list of commit per file change." : ""; -var INIT_MAIN_PROMPT2 = (language, fullGitMojiSpec) => ({ +var userInputCodeContext = (context2) => { + if (context2 !== "" && context2 !== " ") { + return `Additional context provided by the user: ${context2} +Consider this context when generating the commit message, incorporating relevant information when appropriate.`; + } + return ""; +}; +var INIT_MAIN_PROMPT2 = (language, fullGitMojiSpec, context2) => ({ role: "system", content: (() => { const commitConvention = fullGitMojiSpec ? "GitMoji specification" : "Conventional Commit Convention"; @@ -63742,12 +63785,14 @@ var INIT_MAIN_PROMPT2 = (language, fullGitMojiSpec) => ({ const descriptionGuideline = getDescriptionInstruction(); const oneLineCommitGuideline = getOneLineCommitInstruction(); const generalGuidelines = `Use the present tense. Lines must not be longer than 74 characters. Use ${language} for the commit message.`; + const userInputContext = userInputCodeContext(context2); return `${missionStatement} ${diffInstruction} ${conventionGuidelines} ${descriptionGuideline} ${oneLineCommitGuideline} -${generalGuidelines}`; +${generalGuidelines} +${userInputContext}`; })() }); var INIT_DIFF_PROMPT = { @@ -63789,7 +63834,7 @@ var INIT_CONSISTENCY_PROMPT = (translation4) => ({ role: "assistant", content: getContent(translation4) }); -var getMainCommitPrompt = async (fullGitMojiSpec) => { +var getMainCommitPrompt = async (fullGitMojiSpec, context2) => { switch (config4.OCO_PROMPT_MODULE) { case "@commitlint": if (!await commitlintLLMConfigExists()) { @@ -63811,7 +63856,7 @@ var getMainCommitPrompt = async (fullGitMojiSpec) => { ]; default: return [ - INIT_MAIN_PROMPT2(translation3.localLanguage, fullGitMojiSpec), + INIT_MAIN_PROMPT2(translation3.localLanguage, fullGitMojiSpec, context2), INIT_DIFF_PROMPT, INIT_CONSISTENCY_PROMPT(translation3) ]; @@ -63838,8 +63883,8 @@ function mergeDiffs(arr, maxStringLength) { var config5 = getConfig(); var MAX_TOKENS_INPUT = config5.OCO_TOKENS_MAX_INPUT; var MAX_TOKENS_OUTPUT = config5.OCO_TOKENS_MAX_OUTPUT; -var generateCommitMessageChatCompletionPrompt = async (diff, fullGitMojiSpec) => { - const INIT_MESSAGES_PROMPT = await getMainCommitPrompt(fullGitMojiSpec); +var generateCommitMessageChatCompletionPrompt = async (diff, fullGitMojiSpec, context2) => { + const INIT_MESSAGES_PROMPT = await getMainCommitPrompt(fullGitMojiSpec, context2); const chatContextAsCompletionRequest = [...INIT_MESSAGES_PROMPT]; chatContextAsCompletionRequest.push({ role: "user", @@ -63855,9 +63900,12 @@ var GenerateCommitMessageErrorEnum = ((GenerateCommitMessageErrorEnum2) => { return GenerateCommitMessageErrorEnum2; })(GenerateCommitMessageErrorEnum || {}); var ADJUSTMENT_FACTOR = 20; -var generateCommitMessageByDiff = async (diff, fullGitMojiSpec = false) => { +var generateCommitMessageByDiff = async (diff, fullGitMojiSpec = false, context2 = "") => { try { - const INIT_MESSAGES_PROMPT = await getMainCommitPrompt(fullGitMojiSpec); + const INIT_MESSAGES_PROMPT = await getMainCommitPrompt( + fullGitMojiSpec, + context2 + ); const INIT_MESSAGES_PROMPT_LENGTH = INIT_MESSAGES_PROMPT.map( (msg) => tokenCount(msg.content) + 4 ).reduce((a3, b3) => a3 + b3, 0); @@ -63877,7 +63925,8 @@ var generateCommitMessageByDiff = async (diff, fullGitMojiSpec = false) => { } const messages = await generateCommitMessageChatCompletionPrompt( diff, - fullGitMojiSpec + fullGitMojiSpec, + context2 ); const engine = getEngine(); const commitMessage = await engine.generateCommitMessage(messages); diff --git a/package-lock.json b/package-lock.json index aede6fed..b4be8594 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,9 +17,9 @@ "@clack/prompts": "^0.6.1", "@dqbd/tiktoken": "^1.0.2", "@google/generative-ai": "^0.11.4", + "@mistralai/mistralai": "^1.3.5", "@octokit/webhooks-schemas": "^6.11.0", "@octokit/webhooks-types": "^6.11.0", - "ai": "^2.2.14", "axios": "^1.3.4", "chalk": "^5.2.0", "cleye": "^1.3.2", @@ -28,7 +28,9 @@ "ignore": "^5.2.4", "ini": "^3.0.1", "inquirer": "^9.1.4", - "openai": "^4.57.0" + "openai": "^4.57.0", + "punycode": "^2.3.1", + "zod": "^3.23.8" }, "bin": { "oco": "out/cli.cjs", @@ -108,6 +110,7 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", + "dev": true, "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" @@ -574,6 +577,7 @@ "version": "7.24.1", "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.1.tgz", "integrity": "sha512-Zo9c7N3xdOIQrNip7Lc9wvRPzlRtovHVE4lkz8WEDr7uYh/GMQhSiIgFxGIArRHYdJE5kxtZjAf8rT0xhdLCzg==", + "dev": true, "bin": { "parser": "bin/babel-parser.js" }, @@ -1728,6 +1732,7 @@ "version": "0.3.5", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", + "dev": true, "dependencies": { "@jridgewell/set-array": "^1.2.1", "@jridgewell/sourcemap-codec": "^1.4.10", @@ -1741,6 +1746,7 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, "engines": { "node": ">=6.0.0" } @@ -1749,6 +1755,7 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", + "dev": true, "engines": { "node": ">=6.0.0" } @@ -1756,12 +1763,14 @@ "node_modules/@jridgewell/sourcemap-codec": { "version": "1.4.15", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", - "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==" + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", + "dev": true }, "node_modules/@jridgewell/trace-mapping": { "version": "0.3.25", "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "dev": true, "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" @@ -1778,6 +1787,16 @@ "node": ">= 0.4" } }, + "node_modules/@mistralai/mistralai": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@mistralai/mistralai/-/mistralai-1.3.5.tgz", + "integrity": "sha512-yC91oJ5ScEPqbXmv3mJTwTFgu/ZtsYoOPOhaVXSsy6x4zXTqTI57yEC1flC9uiA8GpG/yhpn2BBUXF95+U9Blw==", + "peerDependencies": { + "react": "^18 || ^19", + "react-dom": "^18 || ^19", + "zod": ">= 3" + } + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -2013,12 +2032,6 @@ "@babel/types": "^7.20.7" } }, - "node_modules/@types/estree": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", - "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", - "peer": true - }, "node_modules/@types/graceful-fs": { "version": "4.1.9", "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz", @@ -2333,117 +2346,6 @@ "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", "dev": true }, - "node_modules/@vue/compiler-core": { - "version": "3.4.21", - "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.4.21.tgz", - "integrity": "sha512-MjXawxZf2SbZszLPYxaFCjxfibYrzr3eYbKxwpLR9EQN+oaziSu3qKVbwBERj1IFIB8OLUewxB5m/BFzi613og==", - "peer": true, - "dependencies": { - "@babel/parser": "^7.23.9", - "@vue/shared": "3.4.21", - "entities": "^4.5.0", - "estree-walker": "^2.0.2", - "source-map-js": "^1.0.2" - } - }, - "node_modules/@vue/compiler-core/node_modules/estree-walker": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", - "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", - "peer": true - }, - "node_modules/@vue/compiler-dom": { - "version": "3.4.21", - "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.4.21.tgz", - "integrity": "sha512-IZC6FKowtT1sl0CR5DpXSiEB5ayw75oT2bma1BEhV7RRR1+cfwLrxc2Z8Zq/RGFzJ8w5r9QtCOvTjQgdn0IKmA==", - "peer": true, - "dependencies": { - "@vue/compiler-core": "3.4.21", - "@vue/shared": "3.4.21" - } - }, - "node_modules/@vue/compiler-sfc": { - "version": "3.4.21", - "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.4.21.tgz", - "integrity": "sha512-me7epoTxYlY+2CUM7hy9PCDdpMPfIwrOvAXud2Upk10g4YLv9UBW7kL798TvMeDhPthkZ0CONNrK2GoeI1ODiQ==", - "peer": true, - "dependencies": { - "@babel/parser": "^7.23.9", - "@vue/compiler-core": "3.4.21", - "@vue/compiler-dom": "3.4.21", - "@vue/compiler-ssr": "3.4.21", - "@vue/shared": "3.4.21", - "estree-walker": "^2.0.2", - "magic-string": "^0.30.7", - "postcss": "^8.4.35", - "source-map-js": "^1.0.2" - } - }, - "node_modules/@vue/compiler-sfc/node_modules/estree-walker": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", - "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", - "peer": true - }, - "node_modules/@vue/compiler-ssr": { - "version": "3.4.21", - "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.4.21.tgz", - "integrity": "sha512-M5+9nI2lPpAsgXOGQobnIueVqc9sisBFexh5yMIMRAPYLa7+5wEJs8iqOZc1WAa9WQbx9GR2twgznU8LTIiZ4Q==", - "peer": true, - "dependencies": { - "@vue/compiler-dom": "3.4.21", - "@vue/shared": "3.4.21" - } - }, - "node_modules/@vue/reactivity": { - "version": "3.4.21", - "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.4.21.tgz", - "integrity": "sha512-UhenImdc0L0/4ahGCyEzc/pZNwVgcglGy9HVzJ1Bq2Mm9qXOpP8RyNTjookw/gOCUlXSEtuZ2fUg5nrHcoqJcw==", - "peer": true, - "dependencies": { - "@vue/shared": "3.4.21" - } - }, - "node_modules/@vue/runtime-core": { - "version": "3.4.21", - "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.4.21.tgz", - "integrity": "sha512-pQthsuYzE1XcGZznTKn73G0s14eCJcjaLvp3/DKeYWoFacD9glJoqlNBxt3W2c5S40t6CCcpPf+jG01N3ULyrA==", - "peer": true, - "dependencies": { - "@vue/reactivity": "3.4.21", - "@vue/shared": "3.4.21" - } - }, - "node_modules/@vue/runtime-dom": { - "version": "3.4.21", - "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.4.21.tgz", - "integrity": "sha512-gvf+C9cFpevsQxbkRBS1NpU8CqxKw0ebqMvLwcGQrNpx6gqRDodqKqA+A2VZZpQ9RpK2f9yfg8VbW/EpdFUOJw==", - "peer": true, - "dependencies": { - "@vue/runtime-core": "3.4.21", - "@vue/shared": "3.4.21", - "csstype": "^3.1.3" - } - }, - "node_modules/@vue/server-renderer": { - "version": "3.4.21", - "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.4.21.tgz", - "integrity": "sha512-aV1gXyKSN6Rz+6kZ6kr5+Ll14YzmIbeuWe7ryJl5muJ4uwSwY/aStXTixx76TwkZFJLm1aAlA/HSWEJ4EyiMkg==", - "peer": true, - "dependencies": { - "@vue/compiler-ssr": "3.4.21", - "@vue/shared": "3.4.21" - }, - "peerDependencies": { - "vue": "3.4.21" - } - }, - "node_modules/@vue/shared": { - "version": "3.4.21", - "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.21.tgz", - "integrity": "sha512-PuJe7vDIi6VYSinuEbUIQgMIRZGgM8e4R+G+/dQTk0X1NEdvgvvgv7m+rfmDH1gZzyA1OjjoWskvHlfRNfQf3g==", - "peer": true - }, "node_modules/abort-controller": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", @@ -2459,6 +2361,7 @@ "version": "8.11.3", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", + "dev": true, "bin": { "acorn": "bin/acorn" }, @@ -2506,43 +2409,6 @@ "node": ">= 8.0.0" } }, - "node_modules/ai": { - "version": "2.2.37", - "resolved": "https://registry.npmjs.org/ai/-/ai-2.2.37.tgz", - "integrity": "sha512-JIYm5N1muGVqBqWnvkt29FmXhESoO5TcDxw74OE41SsM+uIou6NPDDs0XWb/ABcd1gmp6k5zym64KWMPM2xm0A==", - "dependencies": { - "eventsource-parser": "1.0.0", - "nanoid": "3.3.6", - "solid-swr-store": "0.10.7", - "sswr": "2.0.0", - "swr": "2.2.0", - "swr-store": "0.10.6", - "swrv": "1.0.4" - }, - "engines": { - "node": ">=14.6" - }, - "peerDependencies": { - "react": "^18.2.0", - "solid-js": "^1.7.7", - "svelte": "^3.0.0 || ^4.0.0", - "vue": "^3.3.4" - }, - "peerDependenciesMeta": { - "react": { - "optional": true - }, - "solid-js": { - "optional": true - }, - "svelte": { - "optional": true - }, - "vue": { - "optional": true - } - } - }, "node_modules/ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", @@ -2629,15 +2495,6 @@ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "dev": true }, - "node_modules/aria-query": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz", - "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==", - "peer": true, - "dependencies": { - "dequal": "^2.0.3" - } - }, "node_modules/array-union": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", @@ -2671,15 +2528,6 @@ "proxy-from-env": "^1.1.0" } }, - "node_modules/axobject-query": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.0.0.tgz", - "integrity": "sha512-+60uv1hiVFhHZeO+Lz0RYzsVHy5Wr1ayX0mwda9KPDVLNJgZ1T9Ny7VmFbLDzxsH0D87I86vgj3gFrjTJUYznw==", - "peer": true, - "dependencies": { - "dequal": "^2.0.3" - } - }, "node_modules/babel-jest": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz", @@ -3256,19 +3104,6 @@ "node": ">= 0.12.0" } }, - "node_modules/code-red": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/code-red/-/code-red-1.0.4.tgz", - "integrity": "sha512-7qJWqItLA8/VPVlKJlFXU+NBlo/qyfs39aJcuMT/2ere32ZqvF5OSxgdM5xOfJJ7O429gg2HM47y8v9P+9wrNw==", - "peer": true, - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.4.15", - "@types/estree": "^1.0.1", - "acorn": "^8.10.0", - "estree-walker": "^3.0.3", - "periscopic": "^3.1.0" - } - }, "node_modules/collect-v8-coverage": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz", @@ -3390,9 +3225,10 @@ "dev": true }, "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "license": "MIT", "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", @@ -3408,25 +3244,6 @@ "integrity": "sha512-VxBKmeNcqQdiUQUW2Tzq0t377b54N2bMtXO/qiLa+6eRRmmC4qT3D4OnTGoT/U6O9aklQ/jTwbOtRMTTY8G0Ig==", "deprecated": "This package is no longer supported. It's now a built-in Node module. If you've depended on crypto, you should switch to the one that's built-in." }, - "node_modules/css-tree": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.3.1.tgz", - "integrity": "sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==", - "peer": true, - "dependencies": { - "mdn-data": "2.0.30", - "source-map-js": "^1.0.1" - }, - "engines": { - "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" - } - }, - "node_modules/csstype": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", - "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", - "peer": true - }, "node_modules/debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", @@ -3512,14 +3329,6 @@ "resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz", "integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==" }, - "node_modules/dequal": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", - "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", - "engines": { - "node": ">=6" - } - }, "node_modules/detect-newline": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", @@ -3606,18 +3415,6 @@ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" }, - "node_modules/entities": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", - "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", - "peer": true, - "engines": { - "node": ">=0.12" - }, - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, "node_modules/error-ex": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", @@ -4259,15 +4056,6 @@ "node": ">=4.0" } }, - "node_modules/estree-walker": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", - "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", - "peer": true, - "dependencies": { - "@types/estree": "^1.0.0" - } - }, "node_modules/esutils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", @@ -4285,14 +4073,6 @@ "node": ">=6" } }, - "node_modules/eventsource-parser": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-1.0.0.tgz", - "integrity": "sha512-9jgfSCa3dmEme2ES3mPByGXfgZ87VbP97tng1G2nWwWx6bV2nYxm2AWCrbQjXToSe+yYlqaZNtxffR9IeQr95g==", - "engines": { - "node": ">=14.18" - } - }, "node_modules/execa": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/execa/-/execa-7.2.0.tgz", @@ -5156,15 +4936,6 @@ "node": ">=0.10.0" } }, - "node_modules/is-reference": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-3.0.2.tgz", - "integrity": "sha512-v3rht/LgVcsdZa3O2Nqs+NMowLOxeOm7Ay9+/ARQ2F+qEoANRcqrjAZKGN0v8ymUetZGgkp26LTnGT7H0Qo9Pg==", - "peer": true, - "dependencies": { - "@types/estree": "*" - } - }, "node_modules/is-stream": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", @@ -6826,12 +6597,6 @@ "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", "dev": true }, - "node_modules/locate-character": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-character/-/locate-character-3.0.0.tgz", - "integrity": "sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA==", - "peer": true - }, "node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -6928,6 +6693,7 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "license": "MIT", "peer": true, "dependencies": { "js-tokens": "^3.0.0 || ^4.0.0" @@ -6954,18 +6720,6 @@ "lz-string": "bin/bin.js" } }, - "node_modules/magic-string": { - "version": "0.30.8", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.8.tgz", - "integrity": "sha512-ISQTe55T2ao7XtlAStud6qwYPZjE4GK1S/BeVPus4jrq6JuOnQ00YKQC581RWhR122W7msZV263KzVeLoqidyQ==", - "peer": true, - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.4.15" - }, - "engines": { - "node": ">=12" - } - }, "node_modules/make-dir": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", @@ -6996,12 +6750,6 @@ "tmpl": "1.0.5" } }, - "node_modules/mdn-data": { - "version": "2.0.30", - "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz", - "integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==", - "peer": true - }, "node_modules/merge-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", @@ -7017,12 +6765,13 @@ } }, "node_modules/micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", "dev": true, + "license": "MIT", "dependencies": { - "braces": "^3.0.2", + "braces": "^3.0.3", "picomatch": "^2.3.1" }, "engines": { @@ -7093,23 +6842,6 @@ "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/nanoid": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz", - "integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, "node_modules/natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", @@ -7494,17 +7226,6 @@ "node": ">=8" } }, - "node_modules/periscopic": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/periscopic/-/periscopic-3.1.0.tgz", - "integrity": "sha512-vKiQ8RRtkl9P+r/+oefh25C3fhybptkHKCZSPlcXiJux2tJF55GnEj3BVn4A5gKfq9NWWXXrxkHBwVPUfH0opw==", - "peer": true, - "dependencies": { - "@types/estree": "^1.0.0", - "estree-walker": "^3.0.0", - "is-reference": "^3.0.0" - } - }, "node_modules/picocolors": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", @@ -7595,52 +7316,6 @@ "node": ">=8" } }, - "node_modules/postcss": { - "version": "8.4.38", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.38.tgz", - "integrity": "sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "peer": true, - "dependencies": { - "nanoid": "^3.3.7", - "picocolors": "^1.0.0", - "source-map-js": "^1.2.0" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, - "node_modules/postcss/node_modules/nanoid": { - "version": "3.3.7", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", - "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "peer": true, - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -7701,7 +7376,7 @@ "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "dev": true, + "license": "MIT", "engines": { "node": ">=6" } @@ -7757,9 +7432,10 @@ ] }, "node_modules/react": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", - "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==", + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", + "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", + "license": "MIT", "peer": true, "dependencies": { "loose-envify": "^1.1.0" @@ -7768,6 +7444,20 @@ "node": ">=0.10.0" } }, + "node_modules/react-dom": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", + "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", + "license": "MIT", + "peer": true, + "dependencies": { + "loose-envify": "^1.1.0", + "scheduler": "^0.23.2" + }, + "peerDependencies": { + "react": "^18.3.1" + } + }, "node_modules/react-is": { "version": "18.2.0", "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", @@ -7993,6 +7683,16 @@ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, + "node_modules/scheduler": { + "version": "0.23.2", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", + "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "loose-envify": "^1.1.0" + } + }, "node_modules/semver": { "version": "7.6.0", "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", @@ -8026,27 +7726,6 @@ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "dev": true }, - "node_modules/seroval": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/seroval/-/seroval-1.0.5.tgz", - "integrity": "sha512-TM+Z11tHHvQVQKeNlOUonOWnsNM+2IBwZ4vwoi4j3zKzIpc5IDw8WPwCfcc8F17wy6cBcJGbZbFOR0UCuTZHQA==", - "peer": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/seroval-plugins": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/seroval-plugins/-/seroval-plugins-1.0.5.tgz", - "integrity": "sha512-8+pDC1vOedPXjKG7oz8o+iiHrtF2WswaMQJ7CKFpccvSYfrzmvKY9zOJWCg+881722wIHfwkdnRmiiDm9ym+zQ==", - "peer": true, - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "seroval": "^1.0" - } - }, "node_modules/set-function-length": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", @@ -8168,29 +7847,6 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, - "node_modules/solid-js": { - "version": "1.8.16", - "resolved": "https://registry.npmjs.org/solid-js/-/solid-js-1.8.16.tgz", - "integrity": "sha512-rja94MNU9flF3qQRLNsu60QHKBDKBkVE1DldJZPIfn2ypIn3NV2WpSbGTQIvsyGPBo+9E2IMjwqnqpbgfWuzeg==", - "peer": true, - "dependencies": { - "csstype": "^3.1.0", - "seroval": "^1.0.4", - "seroval-plugins": "^1.0.3" - } - }, - "node_modules/solid-swr-store": { - "version": "0.10.7", - "resolved": "https://registry.npmjs.org/solid-swr-store/-/solid-swr-store-0.10.7.tgz", - "integrity": "sha512-A6d68aJmRP471aWqKKPE2tpgOiR5fH4qXQNfKIec+Vap+MGQm3tvXlT8n0I8UgJSlNAsSAUuw2VTviH2h3Vv5g==", - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "solid-js": "^1.2", - "swr-store": "^0.10" - } - }, "node_modules/source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -8200,15 +7856,6 @@ "node": ">=0.10.0" } }, - "node_modules/source-map-js": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz", - "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==", - "peer": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/source-map-support": { "version": "0.5.13", "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", @@ -8225,17 +7872,6 @@ "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", "dev": true }, - "node_modules/sswr": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/sswr/-/sswr-2.0.0.tgz", - "integrity": "sha512-mV0kkeBHcjcb0M5NqKtKVg/uTIYNlIIniyDfSGrSfxpEdM9C365jK0z55pl9K0xAkNTJi2OAOVFQpgMPUk+V0w==", - "dependencies": { - "swrev": "^4.0.0" - }, - "peerDependencies": { - "svelte": "^4.0.0" - } - }, "node_modules/stack-utils": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", @@ -8367,66 +8003,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/svelte": { - "version": "4.2.12", - "resolved": "https://registry.npmjs.org/svelte/-/svelte-4.2.12.tgz", - "integrity": "sha512-d8+wsh5TfPwqVzbm4/HCXC783/KPHV60NvwitJnyTA5lWn1elhXMNWhXGCJ7PwPa8qFUnyJNIyuIRt2mT0WMug==", - "peer": true, - "dependencies": { - "@ampproject/remapping": "^2.2.1", - "@jridgewell/sourcemap-codec": "^1.4.15", - "@jridgewell/trace-mapping": "^0.3.18", - "@types/estree": "^1.0.1", - "acorn": "^8.9.0", - "aria-query": "^5.3.0", - "axobject-query": "^4.0.0", - "code-red": "^1.0.3", - "css-tree": "^2.3.1", - "estree-walker": "^3.0.3", - "is-reference": "^3.0.1", - "locate-character": "^3.0.0", - "magic-string": "^0.30.4", - "periscopic": "^3.1.0" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/swr": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/swr/-/swr-2.2.0.tgz", - "integrity": "sha512-AjqHOv2lAhkuUdIiBu9xbuettzAzWXmCEcLONNKJRba87WAefz8Ca9d6ds/SzrPc235n1IxWYdhJ2zF3MNUaoQ==", - "dependencies": { - "use-sync-external-store": "^1.2.0" - }, - "peerDependencies": { - "react": "^16.11.0 || ^17.0.0 || ^18.0.0" - } - }, - "node_modules/swr-store": { - "version": "0.10.6", - "resolved": "https://registry.npmjs.org/swr-store/-/swr-store-0.10.6.tgz", - "integrity": "sha512-xPjB1hARSiRaNNlUQvWSVrG5SirCjk2TmaUyzzvk69SZQan9hCJqw/5rG9iL7xElHU784GxRPISClq4488/XVw==", - "dependencies": { - "dequal": "^2.0.3" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/swrev": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/swrev/-/swrev-4.0.0.tgz", - "integrity": "sha512-LqVcOHSB4cPGgitD1riJ1Hh4vdmITOp+BkmfmXRh4hSF/t7EnS4iD+SOTmq7w5pPm/SiPeto4ADbKS6dHUDWFA==" - }, - "node_modules/swrv": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/swrv/-/swrv-1.0.4.tgz", - "integrity": "sha512-zjEkcP8Ywmj+xOJW3lIT65ciY/4AL4e/Or7Gj0MzU3zBJNMdJiT8geVZhINavnlHRMMCcJLHhraLTAiDOTmQ9g==", - "peerDependencies": { - "vue": ">=3.2.26 < 4" - } - }, "node_modules/terminal-columns": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/terminal-columns/-/terminal-columns-1.4.1.tgz", @@ -8667,7 +8243,7 @@ "version": "4.9.5", "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", - "devOptional": true, + "dev": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -8736,14 +8312,6 @@ "punycode": "^2.1.0" } }, - "node_modules/use-sync-external-store": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz", - "integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==", - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0" - } - }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -8777,27 +8345,6 @@ "node": ">=10.12.0" } }, - "node_modules/vue": { - "version": "3.4.21", - "resolved": "https://registry.npmjs.org/vue/-/vue-3.4.21.tgz", - "integrity": "sha512-5hjyV/jLEIKD/jYl4cavMcnzKwjMKohureP8ejn3hhEjwhWIhWeuzL2kJAjzl/WyVsgPY56Sy4Z40C3lVshxXA==", - "peer": true, - "dependencies": { - "@vue/compiler-dom": "3.4.21", - "@vue/compiler-sfc": "3.4.21", - "@vue/runtime-dom": "3.4.21", - "@vue/server-renderer": "3.4.21", - "@vue/shared": "3.4.21" - }, - "peerDependencies": { - "typescript": "*" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, "node_modules/walker": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", @@ -8960,6 +8507,15 @@ "funding": { "url": "https://github.com/sponsors/sindresorhus" } + }, + "node_modules/zod": { + "version": "3.23.8", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.23.8.tgz", + "integrity": "sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } } } } diff --git a/package.json b/package.json index 24d37d58..9942732f 100644 --- a/package.json +++ b/package.json @@ -58,7 +58,8 @@ "test:unit:docker": "npm run test:docker-build && DOCKER_CONTENT_TRUST=0 docker run --rm oco-test npm run test:unit", "test:e2e": "npm run test:e2e:setup && jest test/e2e", "test:e2e:setup": "sh test/e2e/setup.sh", - "test:e2e:docker": "npm run test:docker-build && DOCKER_CONTENT_TRUST=0 docker run --rm oco-test npm run test:e2e" + "test:e2e:docker": "npm run test:docker-build && DOCKER_CONTENT_TRUST=0 docker run --rm oco-test npm run test:e2e", + "mlx:start": "OCO_AI_PROVIDER='mlx' node ./out/cli.cjs" }, "devDependencies": { "@commitlint/types": "^17.4.4", @@ -87,6 +88,7 @@ "@clack/prompts": "^0.6.1", "@dqbd/tiktoken": "^1.0.2", "@google/generative-ai": "^0.11.4", + "@mistralai/mistralai": "^1.3.5", "@octokit/webhooks-schemas": "^6.11.0", "@octokit/webhooks-types": "^6.11.0", "axios": "^1.3.4", @@ -97,6 +99,8 @@ "ignore": "^5.2.4", "ini": "^3.0.1", "inquirer": "^9.1.4", - "openai": "^4.57.0" + "openai": "^4.57.0", + "punycode": "^2.3.1", + "zod": "^3.23.8" } } diff --git a/src/cli.ts b/src/cli.ts index 7d731d9f..fd07a907 100755 --- a/src/cli.ts +++ b/src/cli.ts @@ -20,6 +20,12 @@ cli( commands: [configCommand, hookCommand, commitlintConfigCommand], flags: { fgm: Boolean, + context: { + type: String, + alias: 'c', + description: 'Additional user input context for the commit message', + default: '' + }, yes: { type: Boolean, alias: 'y', @@ -37,7 +43,7 @@ cli( if (await isHookCalled()) { prepareCommitMessageHook(); } else { - commit(extraArgs, false, flags.fgm, flags.yes); + commit(extraArgs, flags.context, false, flags.fgm, flags.yes); } }, extraArgs diff --git a/src/commands/commit.ts b/src/commands/commit.ts index a26df269..1e7f2af6 100644 --- a/src/commands/commit.ts +++ b/src/commands/commit.ts @@ -39,6 +39,7 @@ const checkMessageTemplate = (extraArgs: string[]): string | false => { interface GenerateCommitMessageFromGitDiffParams { diff: string; extraArgs: string[]; + context?: string; fullGitMojiSpec?: boolean; skipCommitConfirmation?: boolean; } @@ -46,6 +47,7 @@ interface GenerateCommitMessageFromGitDiffParams { const generateCommitMessageFromGitDiff = async ({ diff, extraArgs, + context = '', fullGitMojiSpec = false, skipCommitConfirmation = false }: GenerateCommitMessageFromGitDiffParams): Promise => { @@ -56,7 +58,8 @@ const generateCommitMessageFromGitDiff = async ({ try { let commitMessage = await generateCommitMessageByDiff( diff, - fullGitMojiSpec + fullGitMojiSpec, + context ); const messageTemplate = checkMessageTemplate(extraArgs); @@ -135,8 +138,7 @@ ${chalk.grey('——————————————————')}` ]); pushSpinner.stop( - `${chalk.green('✔')} Successfully pushed all commits to ${ - remotes[0] + `${chalk.green('✔')} Successfully pushed all commits to ${remotes[0] }` ); @@ -146,26 +148,29 @@ ${chalk.grey('——————————————————')}` process.exit(0); } } else { + const skipOption = `don't push` const selectedRemote = (await select({ message: 'Choose a remote to push to', - options: remotes.map((remote) => ({ value: remote, label: remote })) + options: [...remotes, skipOption].map((remote) => ({ value: remote, label: remote })), })) as string; if (isCancel(selectedRemote)) process.exit(1); - const pushSpinner = spinner(); - - pushSpinner.start(`Running 'git push ${selectedRemote}'`); - - const { stdout } = await execa('git', ['push', selectedRemote]); - - if (stdout) outro(stdout); - - pushSpinner.stop( - `${chalk.green( - '✔' - )} successfully pushed all commits to ${selectedRemote}` - ); + if (selectedRemote !== skipOption) { + const pushSpinner = spinner(); + + pushSpinner.start(`Running 'git push ${selectedRemote}'`); + + const { stdout } = await execa('git', ['push', selectedRemote]); + + if (stdout) outro(stdout); + + pushSpinner.stop( + `${chalk.green( + '✔' + )} successfully pushed all commits to ${selectedRemote}` + ); + } } } else { const regenerateMessage = await confirm({ @@ -197,6 +202,7 @@ ${chalk.grey('——————————————————')}` export async function commit( extraArgs: string[] = [], + context: string = '', isStageAllFlag: Boolean = false, fullGitMojiSpec: boolean = false, skipCommitConfirmation: boolean = false @@ -238,7 +244,7 @@ export async function commit( if (isCancel(isStageAllAndCommitConfirmedByUser)) process.exit(1); if (isStageAllAndCommitConfirmedByUser) { - await commit(extraArgs, true, fullGitMojiSpec); + await commit(extraArgs, context, true, fullGitMojiSpec); process.exit(1); } @@ -256,7 +262,7 @@ export async function commit( await gitAdd({ files }); } - await commit(extraArgs, false, fullGitMojiSpec); + await commit(extraArgs, context, false, fullGitMojiSpec); process.exit(1); } @@ -270,6 +276,7 @@ export async function commit( generateCommitMessageFromGitDiff({ diff: await getDiff({ files: stagedFiles }), extraArgs, + context, fullGitMojiSpec, skipCommitConfirmation }) diff --git a/src/commands/config.ts b/src/commands/config.ts index e6736334..5381a8d1 100644 --- a/src/commands/config.ts +++ b/src/commands/config.ts @@ -86,6 +86,48 @@ export const MODEL_LIST = { 'llama-3.1-70b-versatile', // Llama 3.1 70B (Preview) 'gemma-7b-it', // Gemma 7B 'gemma2-9b-it' // Gemma 2 9B + ], + + mistral: [ + 'ministral-3b-2410', + 'ministral-3b-latest', + 'ministral-8b-2410', + 'ministral-8b-latest', + 'open-mistral-7b', + 'mistral-tiny', + 'mistral-tiny-2312', + 'open-mistral-nemo', + 'open-mistral-nemo-2407', + 'mistral-tiny-2407', + 'mistral-tiny-latest', + 'open-mixtral-8x7b', + 'mistral-small', + 'mistral-small-2312', + 'open-mixtral-8x22b', + 'open-mixtral-8x22b-2404', + 'mistral-small-2402', + 'mistral-small-2409', + 'mistral-small-latest', + 'mistral-medium-2312', + 'mistral-medium', + 'mistral-medium-latest', + 'mistral-large-2402', + 'mistral-large-2407', + 'mistral-large-2411', + 'mistral-large-latest', + 'pixtral-large-2411', + 'pixtral-large-latest', + 'codestral-2405', + 'codestral-latest', + 'codestral-mamba-2407', + 'open-codestral-mamba', + 'codestral-mamba-latest', + 'pixtral-12b-2409', + 'pixtral-12b', + 'pixtral-12b-latest', + 'mistral-embed', + 'mistral-moderation-2411', + 'mistral-moderation-latest', ] }; @@ -93,12 +135,16 @@ const getDefaultModel = (provider: string | undefined): string => { switch (provider) { case 'ollama': return ''; + case 'mlx': + return ''; case 'anthropic': return MODEL_LIST.anthropic[0]; case 'gemini': return MODEL_LIST.gemini[0]; case 'groq': return MODEL_LIST.groq[0]; + case 'mistral': + return MODEL_LIST.mistral[0]; default: return MODEL_LIST.openai[0]; } @@ -138,7 +184,7 @@ export const configValidators = { validateConfig( 'OCO_API_KEY', value, - 'You need to provide the OCO_API_KEY when OCO_AI_PROVIDER set to "openai" (default) or "ollama" or "azure" or "gemini" or "flowise" or "anthropic". Run `oco config set OCO_API_KEY=your_key OCO_AI_PROVIDER=openai`' + 'You need to provide the OCO_API_KEY when OCO_AI_PROVIDER set to "openai" (default) or "ollama" or "mlx" or "azure" or "gemini" or "flowise" or "anthropic". Run `oco config set OCO_API_KEY=your_key OCO_AI_PROVIDER=openai`' ); return value; @@ -255,6 +301,7 @@ export const configValidators = { CONFIG_KEYS.OCO_AI_PROVIDER, [ 'openai', + 'mistral', 'anthropic', 'gemini', 'azure', @@ -262,7 +309,7 @@ export const configValidators = { 'flowise', 'groq' ].includes(value) || value.startsWith('ollama'), - `${value} is not supported yet, use 'ollama', 'anthropic', 'azure', 'gemini', 'flowise' or 'openai' (default)` + `${value} is not supported yet, use 'ollama', 'mlx', 'anthropic', 'azure', 'gemini', 'flowise', 'mistral' or 'openai' (default)` ); return value; @@ -307,7 +354,9 @@ export enum OCO_AI_PROVIDER_ENUM { AZURE = 'azure', TEST = 'test', FLOWISE = 'flowise', - GROQ = 'groq' + GROQ = 'groq', + MISTRAL = 'mistral', + MLX = 'mlx' } export type ConfigType = { diff --git a/src/engine/Engine.ts b/src/engine/Engine.ts index 6a6f0238..19562271 100644 --- a/src/engine/Engine.ts +++ b/src/engine/Engine.ts @@ -3,6 +3,7 @@ import { OpenAIClient as AzureOpenAIClient } from '@azure/openai'; import { GoogleGenerativeAI as GeminiClient } from '@google/generative-ai'; import { AxiosInstance as RawAxiosClient } from 'axios'; import { OpenAI as OpenAIClient } from 'openai'; +import { Mistral as MistralClient } from '@mistralai/mistralai'; export interface AiEngineConfig { apiKey: string; @@ -17,7 +18,8 @@ type Client = | AzureOpenAIClient | AnthropicClient | RawAxiosClient - | GeminiClient; + | GeminiClient + | MistralClient; export interface AiEngine { config: AiEngineConfig; diff --git a/src/engine/mistral.ts b/src/engine/mistral.ts new file mode 100644 index 00000000..ce480f2e --- /dev/null +++ b/src/engine/mistral.ts @@ -0,0 +1,82 @@ +import axios from 'axios'; +import { Mistral } from '@mistralai/mistralai'; +import { OpenAI } from 'openai'; +import { GenerateCommitMessageErrorEnum } from '../generateCommitMessageFromGitDiff'; +import { tokenCount } from '../utils/tokenCount'; +import { AiEngine, AiEngineConfig } from './Engine'; +import { + AssistantMessage as MistralAssistantMessage, + SystemMessage as MistralSystemMessage, + ToolMessage as MistralToolMessage, + UserMessage as MistralUserMessage +} from '@mistralai/mistralai/models/components'; + +export interface MistralAiConfig extends AiEngineConfig {} +export type MistralCompletionMessageParam = Array< +| (MistralSystemMessage & { role: "system" }) +| (MistralUserMessage & { role: "user" }) +| (MistralAssistantMessage & { role: "assistant" }) +| (MistralToolMessage & { role: "tool" }) +> + +export class MistralAiEngine implements AiEngine { + config: MistralAiConfig; + client: Mistral; + + constructor(config: MistralAiConfig) { + this.config = config; + + if (!config.baseURL) { + this.client = new Mistral({ apiKey: config.apiKey }); + } else { + this.client = new Mistral({ apiKey: config.apiKey, serverURL: config.baseURL }); + } + } + + public generateCommitMessage = async ( + messages: Array + ): Promise => { + const params = { + model: this.config.model, + messages: messages as MistralCompletionMessageParam, + topP: 0.1, + maxTokens: this.config.maxTokensOutput + }; + + try { + const REQUEST_TOKENS = messages + .map((msg) => tokenCount(msg.content as string) + 4) + .reduce((a, b) => a + b, 0); + + if ( + REQUEST_TOKENS > + this.config.maxTokensInput - this.config.maxTokensOutput + ) + throw new Error(GenerateCommitMessageErrorEnum.tooMuchTokens); + + const completion = await this.client.chat.complete(params); + + if (!completion.choices) + throw Error('No completion choice available.') + + const message = completion.choices[0].message; + + if (!message || !message.content) + throw Error('No completion choice available.') + + return message.content as string; + } catch (error) { + const err = error as Error; + if ( + axios.isAxiosError<{ error?: { message: string } }>(error) && + error.response?.status === 401 + ) { + const mistralError = error.response.data.error; + + if (mistralError) throw new Error(mistralError.message); + } + + throw err; + } + }; +} diff --git a/src/engine/mlx.ts b/src/engine/mlx.ts new file mode 100644 index 00000000..4e324e5d --- /dev/null +++ b/src/engine/mlx.ts @@ -0,0 +1,47 @@ +import axios, { AxiosInstance } from 'axios'; +import { OpenAI } from 'openai'; +import { AiEngine, AiEngineConfig } from './Engine'; +import { chown } from 'fs'; + +interface MLXConfig extends AiEngineConfig {} + +export class MLXEngine implements AiEngine { + config: MLXConfig; + client: AxiosInstance; + + constructor(config) { + this.config = config; + this.client = axios.create({ + url: config.baseURL + ? `${config.baseURL}/${config.apiKey}` + : 'http://localhost:8080/v1/chat/completions', + headers: { 'Content-Type': 'application/json' } + }); + } + + async generateCommitMessage( + messages: Array): + Promise { + const params = { + messages, + temperature: 0, + top_p: 0.1, + repetition_penalty: 1.5, + stream: false + }; + try { + const response = await this.client.post( + this.client.getUri(this.config), + params + ); + + const choices = response.data.choices; + const message = choices[0].message; + + return message?.content; + } catch (err: any) { + const message = err.response?.data?.error ?? err.message; + throw new Error(`MLX provider error: ${message}`); + } + } +} \ No newline at end of file diff --git a/src/generateCommitMessageFromGitDiff.ts b/src/generateCommitMessageFromGitDiff.ts index 8ba87407..546de68a 100644 --- a/src/generateCommitMessageFromGitDiff.ts +++ b/src/generateCommitMessageFromGitDiff.ts @@ -11,9 +11,10 @@ const MAX_TOKENS_OUTPUT = config.OCO_TOKENS_MAX_OUTPUT; const generateCommitMessageChatCompletionPrompt = async ( diff: string, - fullGitMojiSpec: boolean + fullGitMojiSpec: boolean, + context: string ): Promise> => { - const INIT_MESSAGES_PROMPT = await getMainCommitPrompt(fullGitMojiSpec); + const INIT_MESSAGES_PROMPT = await getMainCommitPrompt(fullGitMojiSpec, context); const chatContextAsCompletionRequest = [...INIT_MESSAGES_PROMPT]; @@ -36,10 +37,14 @@ const ADJUSTMENT_FACTOR = 20; export const generateCommitMessageByDiff = async ( diff: string, - fullGitMojiSpec: boolean = false + fullGitMojiSpec: boolean = false, + context: string = "" ): Promise => { try { - const INIT_MESSAGES_PROMPT = await getMainCommitPrompt(fullGitMojiSpec); + const INIT_MESSAGES_PROMPT = await getMainCommitPrompt( + fullGitMojiSpec, + context + ); const INIT_MESSAGES_PROMPT_LENGTH = INIT_MESSAGES_PROMPT.map( (msg) => tokenCount(msg.content as string) + 4 @@ -69,7 +74,8 @@ export const generateCommitMessageByDiff = async ( const messages = await generateCommitMessageChatCompletionPrompt( diff, - fullGitMojiSpec + fullGitMojiSpec, + context, ); const engine = getEngine(); diff --git a/src/prompts.ts b/src/prompts.ts index 6cc64b9f..78a5ecd7 100644 --- a/src/prompts.ts +++ b/src/prompts.ts @@ -111,9 +111,24 @@ const getOneLineCommitInstruction = () => ? 'Craft a concise commit message that encapsulates all changes made, with an emphasis on the primary updates. If the modifications share a common theme or scope, mention it succinctly; otherwise, leave the scope out to maintain focus. The goal is to provide a clear and unified overview of the changes in a one single message, without diverging into a list of commit per file change.' : ''; +/** + * Get the context of the user input + * @param extraArgs - The arguments passed to the command line + * @example + * $ oco -- This is a context used to generate the commit message + * @returns - The context of the user input + */ +const userInputCodeContext = (context: string) => { + if (context !== '' && context !== ' ') { + return `Additional context provided by the user: ${context}\nConsider this context when generating the commit message, incorporating relevant information when appropriate.`; + } + return ''; +}; + const INIT_MAIN_PROMPT = ( language: string, - fullGitMojiSpec: boolean + fullGitMojiSpec: boolean, + context: string ): OpenAI.Chat.Completions.ChatCompletionMessageParam => ({ role: 'system', content: (() => { @@ -127,15 +142,16 @@ const INIT_MAIN_PROMPT = ( const descriptionGuideline = getDescriptionInstruction(); const oneLineCommitGuideline = getOneLineCommitInstruction(); const generalGuidelines = `Use the present tense. Lines must not be longer than 74 characters. Use ${language} for the commit message.`; + const userInputContext = userInputCodeContext(context); - return `${missionStatement}\n${diffInstruction}\n${conventionGuidelines}\n${descriptionGuideline}\n${oneLineCommitGuideline}\n${generalGuidelines}`; + return `${missionStatement}\n${diffInstruction}\n${conventionGuidelines}\n${descriptionGuideline}\n${oneLineCommitGuideline}\n${generalGuidelines}\n${userInputContext}`; })() }); export const INIT_DIFF_PROMPT: OpenAI.Chat.Completions.ChatCompletionMessageParam = - { - role: 'user', - content: `diff --git a/src/server.ts b/src/server.ts +{ + role: 'user', + content: `diff --git a/src/server.ts b/src/server.ts index ad4db42..f3b18a9 100644 --- a/src/server.ts +++ b/src/server.ts @@ -159,7 +175,7 @@ export const INIT_DIFF_PROMPT: OpenAI.Chat.Completions.ChatCompletionMessagePara +app.listen(process.env.PORT || PORT, () => { + console.log(\`Server listening on port \${PORT}\`); });` - }; +}; const getContent = (translation: ConsistencyPrompt) => { const fix = config.OCO_EMOJI @@ -185,7 +201,8 @@ const INIT_CONSISTENCY_PROMPT = ( }); export const getMainCommitPrompt = async ( - fullGitMojiSpec: boolean + fullGitMojiSpec: boolean, + context: string ): Promise> => { switch (config.OCO_PROMPT_MODULE) { case '@commitlint': @@ -207,14 +224,14 @@ export const getMainCommitPrompt = async ( INIT_DIFF_PROMPT, INIT_CONSISTENCY_PROMPT( commitLintConfig.consistency[ - translation.localLanguage + translation.localLanguage ] as ConsistencyPrompt ) ]; default: return [ - INIT_MAIN_PROMPT(translation.localLanguage, fullGitMojiSpec), + INIT_MAIN_PROMPT(translation.localLanguage, fullGitMojiSpec, context), INIT_DIFF_PROMPT, INIT_CONSISTENCY_PROMPT(translation) ]; diff --git a/src/utils/engine.ts b/src/utils/engine.ts index 5930c2ff..481a9f9b 100644 --- a/src/utils/engine.ts +++ b/src/utils/engine.ts @@ -6,8 +6,10 @@ import { FlowiseEngine } from '../engine/flowise'; import { GeminiEngine } from '../engine/gemini'; import { OllamaEngine } from '../engine/ollama'; import { OpenAiEngine } from '../engine/openAi'; +import { MistralAiEngine } from '../engine/mistral'; import { TestAi, TestMockType } from '../engine/testAi'; import { GroqEngine } from '../engine/groq'; +import { MLXEngine } from '../engine/mlx'; export function getEngine(): AiEngine { const config = getConfig(); @@ -43,6 +45,12 @@ export function getEngine(): AiEngine { case OCO_AI_PROVIDER_ENUM.GROQ: return new GroqEngine(DEFAULT_CONFIG); + case OCO_AI_PROVIDER_ENUM.MISTRAL: + return new MistralAiEngine(DEFAULT_CONFIG); + + case OCO_AI_PROVIDER_ENUM.MLX: + return new MLXEngine(DEFAULT_CONFIG); + default: return new OpenAiEngine(DEFAULT_CONFIG); }