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);
}