@kyubiware/commit-mint 0.1.5 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.mjs CHANGED
@@ -25,7 +25,7 @@ var __exportAll = (all, no_symbols) => {
25
25
  //#region package.json
26
26
  var package_default = {
27
27
  name: "@kyubiware/commit-mint",
28
- version: "0.1.5",
28
+ version: "0.2.0",
29
29
  description: "A commit tool that actually handles hook failures",
30
30
  type: "module",
31
31
  bin: { "cmint": "./dist/cli.mjs" },
@@ -173,10 +173,6 @@ function buildUserPrompt(diff, hint, statSummary) {
173
173
  function isValidConventionalCommit(message) {
174
174
  return CONVENTIONAL_COMMIT_REGEX.test(message);
175
175
  }
176
- function enforceMaxLength(message, maxLength) {
177
- if (!maxLength || message.length <= maxLength) return message;
178
- return `${message.slice(0, maxLength - 3)}...`;
179
- }
180
176
  function extractContentText(content) {
181
177
  if (content == null) return "";
182
178
  if (typeof content === "string") return content.trim();
@@ -184,7 +180,7 @@ function extractContentText(content) {
184
180
  return "";
185
181
  }
186
182
  async function generateCommitMessage(diff, options) {
187
- debug("generateCommitMessage: model=%s, maxLength=%s, type=%s, hint=%s", options.model ?? "default", options.maxLength ?? "default", options.type ?? "none", options.hint ?? "none");
183
+ debug("generateCommitMessage: model=%s, type=%s, hint=%s", options.model ?? "default", options.type ?? "none", options.hint ?? "none");
188
184
  const timeoutMs = options.timeout ?? 6e4;
189
185
  debug("Timeout: %d ms", timeoutMs);
190
186
  const client = new Groq({
@@ -253,9 +249,8 @@ async function generateCommitMessage(diff, options) {
253
249
  message = retryMessage;
254
250
  } else debug("Retry also failed validation, using original message");
255
251
  }
256
- const result = enforceMaxLength(message, options.maxLength);
257
- debug("Final message (%d ms total): %s", Date.now() - totalStart, result);
258
- return result;
252
+ debug("Final message (%d ms total): %s", Date.now() - totalStart, message);
253
+ return message;
259
254
  } catch (error) {
260
255
  debug("AI error: %s", error instanceof Error ? error.message : String(error));
261
256
  if (error instanceof Groq.AuthenticationError) throw new Error("Invalid GROQ_API_KEY. Run: cmint config set GROQ_API_KEY=<key>");
@@ -323,12 +318,14 @@ var git_exports = /* @__PURE__ */ __exportAll({
323
318
  assertGitRepo: () => assertGitRepo,
324
319
  attemptCommit: () => attemptCommit,
325
320
  attemptCommitNoVerify: () => attemptCommitNoVerify,
321
+ getChangedFiles: () => getChangedFiles,
326
322
  getDefaultExcludes: () => getDefaultExcludes,
327
323
  getHead: () => getHead,
328
324
  getRepoRoot: () => getRepoRoot,
329
325
  getStagedDiff: () => getStagedDiff,
330
326
  getStatusShort: () => getStatusShort,
331
- stageAll: () => stageAll
327
+ stageAll: () => stageAll,
328
+ stageFiles: () => stageFiles
332
329
  });
333
330
  var KnownError = class extends Error {};
334
331
  async function assertGitRepo() {
@@ -406,6 +403,20 @@ async function getStatusShort() {
406
403
  const { stdout } = await execa("git", ["status", "--short"]);
407
404
  return stdout.trim();
408
405
  }
406
+ async function getChangedFiles() {
407
+ const { stdout } = await execa("git", ["status", "--short"]);
408
+ if (!stdout.trim()) return [];
409
+ const files = stdout.split("\n").filter(Boolean).map((line) => ({
410
+ status: line.slice(0, 2).trim(),
411
+ path: line.slice(3)
412
+ }));
413
+ debug("getChangedFiles:", files.length, "files");
414
+ return files;
415
+ }
416
+ async function stageFiles(paths) {
417
+ debug("stageFiles:", paths);
418
+ await execa("git", ["add", ...paths]);
419
+ }
409
420
  async function attemptCommit(message, extraArgs = []) {
410
421
  debug("attemptCommit:", message, extraArgs.length ? extraArgs : "(no extra args)");
411
422
  try {
@@ -592,6 +603,54 @@ async function copyToClipboard(content) {
592
603
  }
593
604
  //#endregion
594
605
  //#region src/ui/menu.ts
606
+ async function showStagingMenu(files) {
607
+ debug("showStagingMenu: %d files", files.length);
608
+ const statusLabel = (status) => {
609
+ switch (status) {
610
+ case "M": return yellow("M");
611
+ case "A": return green("A");
612
+ case "D": return red("D");
613
+ case "?": return dim("?");
614
+ default: return dim(status);
615
+ }
616
+ };
617
+ const choice = await p.select({
618
+ message: "Stage files for commit:",
619
+ options: [
620
+ {
621
+ label: "Stage all files",
622
+ value: "all",
623
+ hint: `${files.length} file${files.length !== 1 ? "s" : ""}`
624
+ },
625
+ {
626
+ label: "Select files...",
627
+ value: "select"
628
+ },
629
+ {
630
+ label: "Cancel",
631
+ value: "cancel"
632
+ }
633
+ ]
634
+ });
635
+ if (p.isCancel(choice) || choice === "cancel") return null;
636
+ if (choice === "all") return {
637
+ files: files.map((f) => f.path),
638
+ all: true
639
+ };
640
+ const selected = await p.multiselect({
641
+ message: "Select files to stage:",
642
+ options: files.map((f) => ({
643
+ label: `${statusLabel(f.status)} ${f.path}`,
644
+ value: f.path
645
+ })),
646
+ required: true
647
+ });
648
+ if (p.isCancel(selected)) return null;
649
+ return {
650
+ files: selected,
651
+ all: false
652
+ };
653
+ }
595
654
  async function showRecoveryMenu(errors, onRetry, onSkipHooks, onRestage, message, rawStderr) {
596
655
  debug("showRecoveryMenu: %d errors", errors.length);
597
656
  while (true) {
@@ -751,10 +810,36 @@ async function commitCommand(flags) {
751
810
  outro(dim("Nothing to commit."));
752
811
  return;
753
812
  }
813
+ const changedFiles = await getChangedFiles();
814
+ debug("Changed files:", changedFiles.length);
754
815
  const s = spinner();
755
- s.start("Staging all changes...");
756
- await stageAll();
757
- s.stop("Changes staged");
816
+ try {
817
+ if (flags.all) {
818
+ s.start("Staging all changes...");
819
+ await stageAll();
820
+ s.stop("Changes staged");
821
+ } else if (changedFiles.length === 1) {
822
+ s.start(`Staging ${changedFiles[0].path}...`);
823
+ await stageFiles([changedFiles[0].path]);
824
+ s.stop("File staged");
825
+ } else {
826
+ const stagingResult = await showStagingMenu(changedFiles);
827
+ if (!stagingResult) {
828
+ outro(dim("Cancelled."));
829
+ return;
830
+ }
831
+ s.start(`Staging ${stagingResult.files.length} file${stagingResult.files.length !== 1 ? "s" : ""}...`);
832
+ if (stagingResult.all) await stageAll();
833
+ else await stageFiles(stagingResult.files);
834
+ s.stop("Files staged");
835
+ }
836
+ } catch (err) {
837
+ s.stop(red("Staging failed."));
838
+ const msg = err instanceof Error ? err.message : String(err);
839
+ debug("Staging error:", msg);
840
+ outro(red(`Failed to stage files: ${msg}`));
841
+ process.exit(1);
842
+ }
758
843
  const diffResult = await getStagedDiff();
759
844
  if (!diffResult) {
760
845
  debug("No staged changes found after staging");
@@ -897,11 +982,10 @@ async function commitCommand(flags) {
897
982
  async function generateMessage(diff, hint) {
898
983
  const config = await readConfig();
899
984
  const apiKey = await getApiKey();
900
- debug("Generating message with model:", config.model, "max-length:", config["max-length"], "type:", config.type);
985
+ debug("Generating message with model:", config.model, "type:", config.type);
901
986
  return generateCommitMessage(diff, {
902
987
  apiKey,
903
988
  model: config.model,
904
- maxLength: config["max-length"] ? parseInt(config["max-length"], 10) : void 0,
905
989
  type: config.type,
906
990
  timeout: config.timeout ? parseInt(config.timeout, 10) : void 0,
907
991
  hint
package/dist/cli.mjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.mjs","names":["pkg"],"sources":["../package.json","../src/utils/debug.ts","../src/services/ai.ts","../src/services/config.ts","../src/services/git.ts","../src/services/hooks.ts","../src/services/clipboard.ts","../src/ui/menu.ts","../src/utils/cache.ts","../src/commands/commit.ts","../src/commands/config.ts","../src/cli.ts"],"sourcesContent":["","import { dim } from \"kolorist\";\n\nlet enabled = false;\n\nexport function setDebug(value: boolean): void {\n\tenabled = value;\n}\n\nexport function isDebug(): boolean {\n\treturn enabled;\n}\n\nexport function debug(...args: unknown[]): void {\n\tif (!enabled) return;\n\tconst timestamp = new Date().toISOString().slice(11, 23);\n\tconsole.error(dim(`[debug ${timestamp}]`), ...args);\n}\n","import Groq from \"groq-sdk\";\nimport { debug } from \"../utils/debug.js\";\n\nconst MAX_DIFF_CHARS = 20000;\n\nconst CONVENTIONAL_COMMIT_REGEX =\n\t/^(build|chore|ci|docs|feat|fix|perf|refactor|revert|style|test)(\\(.+\\))?!?: .+$/;\n\nfunction stripThinkTags(text: string): string {\n\treturn text.replace(/<think[\\s\\S]*?<\\/think>/gi, \"\").trim();\n}\n\nfunction deriveMessageFromReasoning(reasoning: string): string | null {\n\tconst match = reasoning.match(\n\t\t/(build|chore|ci|docs|feat|fix|perf|refactor|revert|style|test)(\\(.+\\))?!?: .+/i,\n\t);\n\tif (match) return match[0].trim();\n\n\tconst sentences = reasoning.split(/[.!?]/);\n\tconst first = sentences.find((s) => s.trim().length >= 10);\n\treturn first ? first.trim() : null;\n}\n\nfunction stripContextLines(diff: string): string {\n\treturn diff\n\t\t.split(\"\\n\")\n\t\t.filter((line) => !line.startsWith(\" \"))\n\t\t.join(\"\\n\");\n}\n\nfunction compressDiff(diff: string): string {\n\t// Tier 0 — Full diff\n\tif (diff.length <= MAX_DIFF_CHARS) {\n\t\treturn diff;\n\t}\n\n\t// Tier 1 — Strip context lines\n\tlet result = stripContextLines(diff);\n\tif (result.length <= MAX_DIFF_CHARS) {\n\t\treturn result;\n\t}\n\n\t// Tier 2 — Per-hunk line cap\n\tconst fileDiffs = result.split(/(?=diff --git)/).filter(Boolean);\n\tconst cappedFiles = fileDiffs.map((fd) => {\n\t\tconst parts = fd.split(/(?=\\n@@)/);\n\t\tconst cappedParts = parts.map((part, idx) => {\n\t\t\tif (idx === 0) return part; // Keep file header\n\t\t\tconst lines = part.split(\"\\n\");\n\t\t\tconst header = lines[0]; // @@ line\n\t\t\tconst changedLines = lines.slice(1).filter((l) => l.startsWith(\"+\") || l.startsWith(\"-\"));\n\t\t\tconst keptLines = changedLines.slice(0, 10);\n\t\t\treturn [header, ...keptLines].join(\"\\n\");\n\t\t});\n\t\treturn cappedParts.join(\"\");\n\t});\n\tresult = cappedFiles.join(\"\");\n\tif (result.length <= MAX_DIFF_CHARS) {\n\t\treturn result;\n\t}\n\n\t// Tier 3 — File summary\n\tconst fileMatches = diff.match(/^diff --git a\\/(.+) b\\/(.+)$/gm) || [];\n\tconst summary = fileMatches\n\t\t.map((f) => {\n\t\t\tconst match = f.match(/^diff --git a\\/(.+) b\\/(.+)$/);\n\t\t\treturn match && match[1] === match[2] ? `${match[1]} | changed` : \"\";\n\t\t})\n\t\t.filter(Boolean);\n\treturn `Summary of changes:\\n${summary.join(\"\\n\")}`;\n}\n\nfunction buildStatSummary(diff: string): string {\n\tconst files: { name: string; adds: number; dels: number }[] = [];\n\tlet currentFile = \"\";\n\tlet adds = 0;\n\tlet dels = 0;\n\n\tfor (const line of diff.split(\"\\n\")) {\n\t\tconst match = line.match(/^diff --git a\\/.+ b\\/(.+)$/);\n\t\tif (match) {\n\t\t\tif (currentFile) files.push({ name: currentFile, adds, dels });\n\t\t\tcurrentFile = match[1];\n\t\t\tadds = 0;\n\t\t\tdels = 0;\n\t\t} else if (line.startsWith(\"+\") && !line.startsWith(\"+++\")) {\n\t\t\tadds++;\n\t\t} else if (line.startsWith(\"-\") && !line.startsWith(\"---\")) {\n\t\t\tdels++;\n\t\t}\n\t}\n\tif (currentFile) files.push({ name: currentFile, adds, dels });\n\n\tconst totalAdds = files.reduce((s, f) => s + f.adds, 0);\n\tconst totalDels = files.reduce((s, f) => s + f.dels, 0);\n\n\tconst lines = files.map((f) => ` ${f.name} | +${f.adds} -${f.dels}`);\n\tlines.push(\n\t\t` ${files.length} files changed, ${totalAdds} insertions(+), ${totalDels} deletions(-)`,\n\t);\n\n\treturn lines.join(\"\\n\");\n}\n\nfunction buildSystemPrompt(type?: string): string {\n\tlet prompt =\n\t\t\"You are a commit message generator. Follow the Conventional Commits specification.\\n\" +\n\t\t\"Valid types: build, chore, ci, docs, feat, fix, perf, refactor, revert, style, test.\\n\" +\n\t\t\"Format: type(scope): description\\n\" +\n\t\t\"Use imperative mood, lowercase, no trailing period.\\n\" +\n\t\t\"Output ONLY the commit message, no markdown fences, no explanation.\";\n\n\tif (type && type.trim().length > 0) {\n\t\tprompt += `\\nYou MUST use type: ${type}`;\n\t}\n\n\treturn prompt;\n}\n\nfunction buildUserPrompt(diff: string, hint?: string, statSummary?: string): string {\n\tconst parts: string[] = [];\n\tif (hint) parts.push(`Context: ${hint}`);\n\tif (statSummary) parts.push(`Change summary:\\n${statSummary}`);\n\tparts.push(`Generate a conventional commit for:\\n\\n${diff}`);\n\treturn parts.join(\"\\n\\n\");\n}\n\nfunction isValidConventionalCommit(message: string): boolean {\n\treturn CONVENTIONAL_COMMIT_REGEX.test(message);\n}\n\nfunction enforceMaxLength(message: string, maxLength?: number): string {\n\tif (!maxLength || message.length <= maxLength) {\n\t\treturn message;\n\t}\n\treturn `${message.slice(0, maxLength - 3)}...`;\n}\n\nfunction extractContentText(\n\tcontent: string | Array<{ type: string; text?: string }> | null | undefined,\n): string {\n\tif (content == null) return \"\";\n\tif (typeof content === \"string\") return content.trim();\n\tif (Array.isArray(content)) {\n\t\treturn content\n\t\t\t.filter((part) => part.type === \"text\" && typeof part.text === \"string\")\n\t\t\t.map((part) => stripThinkTags(part.text as string))\n\t\t\t.join(\"\")\n\t\t\t.trim();\n\t}\n\treturn \"\";\n}\n\nexport async function generateCommitMessage(\n\tdiff: string,\n\toptions: {\n\t\tapiKey: string;\n\t\tmodel?: string;\n\t\tmaxLength?: number;\n\t\ttype?: string;\n\t\ttimeout?: number;\n\t\thint?: string;\n\t},\n): Promise<string> {\n\tdebug(\n\t\t\"generateCommitMessage: model=%s, maxLength=%s, type=%s, hint=%s\",\n\t\toptions.model ?? \"default\",\n\t\toptions.maxLength ?? \"default\",\n\t\toptions.type ?? \"none\",\n\t\toptions.hint ?? \"none\",\n\t);\n\n\tconst timeoutMs = options.timeout ?? 60000;\n\tdebug(\"Timeout: %d ms\", timeoutMs);\n\n\tconst client = new Groq({\n\t\tapiKey: options.apiKey,\n\t\ttimeout: timeoutMs,\n\t});\n\n\tconst compressedDiff = compressDiff(diff);\n\tconst statSummary = buildStatSummary(diff);\n\tconst systemPrompt = buildSystemPrompt(options.type);\n\tconst userPrompt = buildUserPrompt(compressedDiff, options.hint, statSummary);\n\n\tdebug(\"Diff: %d chars → compressed to %d chars\", diff.length, compressedDiff.length);\n\tdebug(\"Stat summary:\\n%s\", statSummary);\n\tdebug(\"User prompt length: %d chars\", userPrompt.length);\n\n\tasync function callAI(strictSystemPrompt?: string): Promise<string> {\n\t\tconst callStart = Date.now();\n\t\tconst isRetry = !!strictSystemPrompt;\n\t\tdebug(\n\t\t\t\"callAI: %s — model=%s, promptLen=%d, systemLen=%d\",\n\t\t\tisRetry ? \"RETRY (strict)\" : \"INITIAL\",\n\t\t\toptions.model ?? \"openai/gpt-oss-20b\",\n\t\t\tuserPrompt.length,\n\t\t\t(strictSystemPrompt ?? systemPrompt).length,\n\t\t);\n\t\ttry {\n\t\t\tconst isReasoningModel = /^(o[1-9]|.*gpt-oss.*|.*gpt-5.*)/i.test(options.model ?? \"\");\n\t\t\tconst completion = await client.chat.completions.create({\n\t\t\t\tmessages: [\n\t\t\t\t\t{ role: \"system\", content: strictSystemPrompt ?? systemPrompt },\n\t\t\t\t\t{ role: \"user\", content: userPrompt },\n\t\t\t\t],\n\t\t\t\tmodel: options.model ?? \"openai/gpt-oss-20b\",\n\t\t\t\ttemperature: 0.3,\n\t\t\t\t...(isReasoningModel ? { max_completion_tokens: 1024 } : { max_tokens: 1024 }),\n\t\t\t\treasoning_format: \"parsed\",\n\t\t\t});\n\n\t\t\tconst elapsed = Date.now() - callStart;\n\t\t\tconst rawContent = completion.choices[0]?.message?.content;\n\t\t\tconst processedContent =\n\t\t\t\ttypeof rawContent === \"string\" ? stripThinkTags(rawContent) : rawContent;\n\t\t\tconst content = extractContentText(processedContent);\n\t\t\tdebug(\n\t\t\t\t\"callAI response (%d ms): choices=%d, finishReason=%s, contentLen=%d, rawType=%s\",\n\t\t\t\telapsed,\n\t\t\t\tcompletion.choices.length,\n\t\t\t\tcompletion.choices[0]?.finish_reason ?? \"(none)\",\n\t\t\t\tcontent.length,\n\t\t\t\ttypeof rawContent,\n\t\t\t);\n\t\t\tdebug(\"callAI raw content: %s\", content.slice(0, 300) || \"(empty)\");\n\t\t\tif (!content) {\n\t\t\t\tconst reasoning = completion.choices[0]?.message?.reasoning;\n\t\t\t\tdebug(\n\t\t\t\t\t\"callAI: content empty, attempting reasoning fallback (reasoningLen=%d)\",\n\t\t\t\t\treasoning?.length ?? 0,\n\t\t\t\t);\n\t\t\t\tif (reasoning) {\n\t\t\t\t\tconst derived = deriveMessageFromReasoning(reasoning);\n\t\t\t\t\tif (derived) {\n\t\t\t\t\t\tdebug(\"callAI: derived message from reasoning: %s\", derived.slice(0, 100));\n\t\t\t\t\t\treturn stripThinkTags(derived);\n\t\t\t\t\t}\n\t\t\t\t\tdebug(\"callAI: could not derive message from reasoning\");\n\t\t\t\t}\n\t\t\t\tthrow new Error(\"AI returned an empty commit message\");\n\t\t\t}\n\t\t\treturn content;\n\t\t} catch (error) {\n\t\t\tconst elapsed = Date.now() - callStart;\n\t\t\tdebug(\n\t\t\t\t\"callAI FAILED after %d ms: %s\",\n\t\t\t\telapsed,\n\t\t\t\terror instanceof Error ? `${error.name}: ${error.message}` : String(error),\n\t\t\t);\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\ttry {\n\t\tconst totalStart = Date.now();\n\t\tlet message = await callAI();\n\t\tdebug(\n\t\t\t\"Validation: message=%s, isValid=%s\",\n\t\t\tmessage.slice(0, 100),\n\t\t\tisValidConventionalCommit(message),\n\t\t);\n\n\t\tif (!isValidConventionalCommit(message)) {\n\t\t\tdebug(\n\t\t\t\t\"Initial message failed conventional commit validation, retrying with strict prompt (elapsed: %d ms)\",\n\t\t\t\tDate.now() - totalStart,\n\t\t\t);\n\t\t\tconst retryMessage = await callAI(\n\t\t\t\t\"You MUST output ONLY a valid conventional commit message. \" +\n\t\t\t\t\t\"Format: type(scope): description. \" +\n\t\t\t\t\t\"If you output anything else your response will be rejected.\\n\" +\n\t\t\t\t\t\"Valid types: build, chore, ci, docs, feat, fix, perf, refactor, revert, style, test.\",\n\t\t\t);\n\t\t\tdebug(\n\t\t\t\t\"Retry validation: message=%s, isValid=%s\",\n\t\t\t\tretryMessage.slice(0, 100),\n\t\t\t\tisValidConventionalCommit(retryMessage),\n\t\t\t);\n\t\t\tif (isValidConventionalCommit(retryMessage)) {\n\t\t\t\tdebug(\"Retry produced valid conventional commit\");\n\t\t\t\tmessage = retryMessage;\n\t\t\t} else {\n\t\t\t\tdebug(\"Retry also failed validation, using original message\");\n\t\t\t}\n\t\t}\n\n\t\tconst result = enforceMaxLength(message, options.maxLength);\n\t\tdebug(\"Final message (%d ms total): %s\", Date.now() - totalStart, result);\n\t\treturn result;\n\t} catch (error) {\n\t\tdebug(\"AI error: %s\", error instanceof Error ? error.message : String(error));\n\t\tif (error instanceof Groq.AuthenticationError) {\n\t\t\tthrow new Error(\"Invalid GROQ_API_KEY. Run: cmint config set GROQ_API_KEY=<key>\");\n\t\t}\n\t\tif (error instanceof Groq.RateLimitError) {\n\t\t\tthrow new Error(\"Rate limited by Groq. Please wait and try again.\");\n\t\t}\n\t\tif (error instanceof Groq.APIConnectionTimeoutError) {\n\t\t\tthrow new Error(\"Request timed out. Check your network or try a smaller diff.\");\n\t\t}\n\t\tif (error instanceof Groq.APIError) {\n\t\t\tthrow new Error(`Groq API error: ${error.message}`);\n\t\t}\n\t\tthrow new Error(`Unexpected error: ${error instanceof Error ? error.message : String(error)}`);\n\t}\n}\n","import { readFile, writeFile } from \"node:fs/promises\";\nimport os from \"node:os\";\nimport { join } from \"node:path\";\nimport ini from \"ini\";\nimport { debug } from \"../utils/debug.js\";\n\nconst CONFIG_PATH = join(os.homedir(), \".commit-mint\");\n\ninterface Config {\n\tGROQ_API_KEY?: string;\n\tmodel?: string;\n\tlocale?: string;\n\t\"max-length\"?: string;\n\ttype?: string;\n\tproxy?: string;\n\ttimeout?: string;\n}\n\nconst defaults: Config = {\n\tmodel: \"openai/gpt-oss-20b\",\n\tlocale: \"en\",\n\t\"max-length\": \"100\",\n\ttype: \"\",\n\ttimeout: \"10000\",\n};\n\nexport async function readConfig(): Promise<Config> {\n\tdebug(\"readConfig: loading from %s\", CONFIG_PATH);\n\ttry {\n\t\tconst raw = await readFile(CONFIG_PATH, \"utf8\");\n\t\tconst parsed = ini.parse(raw);\n\t\tconst merged = { ...defaults, ...parsed };\n\t\tdebug(\"readConfig: loaded keys: %s\", Object.keys(merged).join(\", \"));\n\t\treturn merged;\n\t} catch {\n\t\tdebug(\"readConfig: no config file, using defaults\");\n\t\treturn { ...defaults };\n\t}\n}\n\nexport async function writeConfig(updates: Record<string, string>) {\n\tconst existing = await readConfig();\n\tObject.assign(existing, updates);\n\tawait writeFile(CONFIG_PATH, ini.stringify(existing), \"utf8\");\n}\n\nexport async function getConfigValue(key: string): Promise<string | undefined> {\n\tconst config = await readConfig();\n\treturn config[key as keyof Config];\n}\n\nexport async function setConfigValue(key: string, value: string) {\n\tawait writeConfig({ [key]: value });\n}\n\nexport async function getApiKey(): Promise<string> {\n\tconst envKey = process.env.GROQ_API_KEY;\n\tif (envKey) {\n\t\tdebug(\"getApiKey: found in env\");\n\t\treturn envKey;\n\t}\n\n\tconst config = await readConfig();\n\tif (config.GROQ_API_KEY) {\n\t\tdebug(\"getApiKey: found in config\");\n\t\treturn config.GROQ_API_KEY;\n\t}\n\n\tdebug(\"getApiKey: not found\");\n\tthrow new Error(\"Please set your Groq API key via `cmint config set GROQ_API_KEY=<your token>`\");\n}\n","import type { ExecaError } from \"execa\";\nimport { execa } from \"execa\";\nimport { debug } from \"../utils/debug.js\";\n\nexport class KnownError extends Error {}\n\nexport async function assertGitRepo() {\n\tdebug(\"assertGitRepo\");\n\tconst { failed } = await execa(\"git\", [\"rev-parse\", \"--show-toplevel\"], {\n\t\treject: false,\n\t});\n\tif (failed) {\n\t\tthrow new KnownError(\"The current directory must be a Git repository!\");\n\t}\n}\n\nexport async function getRepoRoot() {\n\tconst { stdout } = await execa(\"git\", [\"rev-parse\", \"--show-toplevel\"]);\n\tdebug(\"getRepoRoot:\", stdout.trim());\n\treturn stdout.trim();\n}\n\nexport interface StagedDiffResult {\n\tfiles: string[];\n\tdiff: string;\n}\n\nexport interface ExcludedFilesResult {\n\texcludedFiles: string[];\n}\n\nexport type DiffResult = StagedDiffResult | ExcludedFilesResult | null;\n\nconst DEFAULT_EXCLUDES = [\n\t\"package-lock.json\",\n\t\"node_modules/**\",\n\t\"dist/**\",\n\t\"build/**\",\n\t\".next/**\",\n\t\"coverage/**\",\n\t\"*.log\",\n\t\"*.min.js\",\n\t\"*.min.css\",\n\t\"*.lock\",\n\t\".DS_Store\",\n];\n\nexport function getDefaultExcludes(): string[] {\n\treturn [...DEFAULT_EXCLUDES];\n}\n\nexport async function getStagedDiff(exclude?: string[]): Promise<DiffResult> {\n\tconst excludeArgs = (exclude ?? []).map((e) => `:(exclude)${e}`);\n\tconst defaultExcludeArgs = DEFAULT_EXCLUDES.map((e) => `:(exclude)${e}`);\n\n\t// Check all staged files without excludes to detect \"all excluded\" case\n\tconst { stdout: allFiles } = await execa(\"git\", [\"diff\", \"--cached\", \"--name-only\"]);\n\tif (!allFiles) {\n\t\tdebug(\"getStagedDiff: no staged files\");\n\t\treturn null;\n\t}\n\n\t// Check staged files with excludes applied\n\tconst { stdout: files } = await execa(\"git\", [\n\t\t\"diff\",\n\t\t\"--cached\",\n\t\t\"--name-only\",\n\t\t...defaultExcludeArgs,\n\t\t...excludeArgs,\n\t]);\n\n\tif (!files) {\n\t\t// All staged files were excluded\n\t\tconst excludedFiles = allFiles.split(\"\\n\").filter(Boolean);\n\t\tdebug(\"getStagedDiff: all files excluded:\", excludedFiles);\n\t\treturn { excludedFiles };\n\t}\n\n\tconst { stdout: diff } = await execa(\"git\", [\n\t\t\"diff\",\n\t\t\"--cached\",\n\t\t\"--diff-algorithm=minimal\",\n\t\t...defaultExcludeArgs,\n\t\t...excludeArgs,\n\t]);\n\n\tdebug(\"getStagedDiff:\", files.split(\"\\n\").filter(Boolean).length, \"files,\", diff.length, \"chars\");\n\treturn { files: files.split(\"\\n\").filter(Boolean), diff };\n}\n\nexport async function stageAll() {\n\tdebug(\"stageAll: git add -A\");\n\tawait execa(\"git\", [\"add\", \"-A\"]);\n}\n\nexport async function getHead() {\n\tconst { stdout } = await execa(\"git\", [\"rev-parse\", \"HEAD\"]);\n\treturn stdout.trim();\n}\n\nexport async function getStatusShort() {\n\tconst { stdout } = await execa(\"git\", [\"status\", \"--short\"]);\n\treturn stdout.trim();\n}\n\nexport interface CommitResult {\n\tok: boolean;\n\terror?: string;\n\t/** Collected stderr from hooks/lint-staged — set on both success and failure */\n\tstderr?: string;\n}\n\nexport async function attemptCommit(\n\tmessage: string,\n\textraArgs: string[] = [],\n): Promise<CommitResult> {\n\tdebug(\"attemptCommit:\", message, extraArgs.length ? extraArgs : \"(no extra args)\");\n\ttry {\n\t\tconst subprocess = execa(\"git\", [\"commit\", \"-m\", message, ...extraArgs]);\n\n\t\t// Collect hook output (lint-staged, biome, etc.) for post-commit display\n\t\t// We don't stream to the terminal — the success/failure result is enough\n\t\tconst stderrChunks: string[] = [];\n\t\tsubprocess.stderr?.on(\"data\", (chunk: Buffer) => {\n\t\t\tstderrChunks.push(chunk.toString());\n\t\t});\n\n\t\tawait subprocess;\n\t\tdebug(\"attemptCommit: success\");\n\t\treturn { ok: true, stderr: stderrChunks.join(\"\") };\n\t} catch (error) {\n\t\tconst e = error as ExecaError;\n\t\tdebug(\"attemptCommit: failed —\", e.message?.slice(0, 200));\n\t\treturn {\n\t\t\tok: false,\n\t\t\terror: e.message,\n\t\t\tstderr: typeof e.stderr === \"string\" ? e.stderr : \"\",\n\t\t};\n\t}\n}\n\nexport async function attemptCommitNoVerify(message: string): Promise<CommitResult> {\n\tdebug(\"attemptCommitNoVerify:\", message);\n\treturn attemptCommit(message, [\"--no-verify\"]);\n}\n","import { debug } from \"../utils/debug.js\";\n\nexport interface HookError {\n\ttool: string;\n\tmessage: string;\n\traw: string;\n}\n\n/**\n * Parse git hook error output into structured, human-readable errors.\n * Handles output from lint-staged, biome, eslint, tsc, vitest, jest.\n */\nexport function parseHookErrors(stderr: string): HookError[] {\n\tif (!stderr) return [];\n\n\tdebug(\"parseHookErrors: stderr length=%d\", stderr.length);\n\tconst errors: HookError[] = [];\n\n\t// Detect lint-staged task failures\n\tif (stderr.includes(\"lint-staged\") || stderr.includes(\"[FAILED]\")) {\n\t\terrors.push(...parseLintStagedErrors(stderr));\n\t}\n\n\t// Detect biome errors\n\tif (stderr.includes(\"biome\") || stderr.includes(\"Biome\")) {\n\t\terrors.push(...parseBiomeErrors(stderr));\n\t}\n\n\t// Detect TypeScript errors\n\tif (stderr.includes(\"error TS\") || stderr.includes(\"tsc\")) {\n\t\terrors.push(...parseTscErrors(stderr));\n\t}\n\n\t// Detect vitest/jest test failures\n\tif (\n\t\tstderr.includes(\"vitest\") ||\n\t\tstderr.includes(\"jest\") ||\n\t\tstderr.includes(\"FAIL\") ||\n\t\tstderr.includes(\"test failed\")\n\t) {\n\t\terrors.push(...parseTestErrors(stderr));\n\t}\n\n\t// Detect ESLint errors\n\tif (stderr.includes(\"eslint\") || stderr.includes(\"ESLint\")) {\n\t\terrors.push(...parseEslintErrors(stderr));\n\t}\n\n\t// Fallback: if nothing parsed, return the raw output\n\tif (errors.length === 0) {\n\t\tdebug(\"parseHookErrors: no patterns matched, using raw fallback\");\n\t\terrors.push({\n\t\t\ttool: \"git hooks\",\n\t\t\tmessage: stderr.trim(),\n\t\t\traw: stderr,\n\t\t});\n\t}\n\n\tdebug(\"parseHookErrors: found %d errors\", errors.length);\n\treturn errors;\n}\n\nfunction parseLintStagedErrors(output: string): HookError[] {\n\tconst errors: HookError[] = [];\n\tfor (const match of output.matchAll(/\\[FAILED\\]\\s+(.+?)\\s+\\[FAILED\\]/g)) {\n\t\tconst task = match[1].trim();\n\t\terrors.push({\n\t\t\ttool: \"lint-staged\",\n\t\t\tmessage: `Task failed: ${task}`,\n\t\t\traw: match[0],\n\t\t});\n\t}\n\n\treturn errors;\n}\n\nfunction parseBiomeErrors(output: string): HookError[] {\n\tconst errors: HookError[] = [];\n\tfor (const match of output.matchAll(/^(.+?):(\\d+):(\\d+)\\s+(.+)$/gm)) {\n\t\terrors.push({\n\t\t\ttool: \"biome\",\n\t\t\tmessage: `${match[1]}:${match[2]}:${match[3]} — ${match[4]}`,\n\t\t\traw: match[0],\n\t\t});\n\t}\n\n\tif (errors.length === 0 && output.includes(\"biome\")) {\n\t\terrors.push({\n\t\t\ttool: \"biome\",\n\t\t\tmessage: \"Biome check failed. See raw output for details.\",\n\t\t\traw: output,\n\t\t});\n\t}\n\n\treturn errors;\n}\n\nfunction parseTscErrors(output: string): HookError[] {\n\tconst errors: HookError[] = [];\n\tfor (const match of output.matchAll(/^(.+?)\\((\\d+),(\\d+)\\):\\s+error\\s+(TS\\d+):\\s+(.+)$/gm)) {\n\t\terrors.push({\n\t\t\ttool: \"tsc\",\n\t\t\tmessage: `${match[1]}:${match[2]}:${match[3]} — ${match[4]}: ${match[5]}`,\n\t\t\traw: match[0],\n\t\t});\n\t}\n\n\treturn errors;\n}\n\nfunction parseTestErrors(output: string): HookError[] {\n\tconst errors: HookError[] = [];\n\tconst failPattern = /FAIL\\s+(.+\\.(test|spec)\\..+)/;\n\tconst match = failPattern.exec(output);\n\n\tif (match) {\n\t\terrors.push({\n\t\t\ttool: output.includes(\"vitest\") ? \"vitest\" : \"jest\",\n\t\t\tmessage: `Test file failed: ${match[1]}`,\n\t\t\traw: output,\n\t\t});\n\t}\n\n\tif (errors.length === 0 && (output.includes(\"vitest\") || output.includes(\"jest\"))) {\n\t\terrors.push({\n\t\t\ttool: output.includes(\"vitest\") ? \"vitest\" : \"jest\",\n\t\t\tmessage: \"Tests failed. See raw output for details.\",\n\t\t\traw: output,\n\t\t});\n\t}\n\n\treturn errors;\n}\n\nfunction parseEslintErrors(output: string): HookError[] {\n\tconst errors: HookError[] = [];\n\tfor (const match of output.matchAll(/^\\s*\\d+:(\\d+)\\s+(error|warning)\\s+(.+?)\\s+(.+?)$/gm)) {\n\t\terrors.push({\n\t\t\ttool: \"eslint\",\n\t\t\tmessage: `${match[2]}: ${match[3]} (${match[4]})`,\n\t\t\traw: match[0],\n\t\t});\n\t}\n\n\treturn errors;\n}\n\nexport function formatErrorReport(errors: HookError[]): string {\n\tif (errors.length === 0) return \"\";\n\n\tconst sections = errors.map((e) => `[${e.tool}]\\n${e.message}`);\n\treturn sections.join(\"\\n\\n\");\n}\n\n// ── Tool check parsing (success case) ──────────────────────────────\n\nexport interface ToolCheck {\n\ttool: string;\n\tok: boolean;\n}\n\n/**\n * Parse lint-staged/hook stderr output to discover which tools ran\n * and whether they succeeded. Used for clean post-commit summary.\n */\nexport function parseToolChecks(stderr: string): ToolCheck[] {\n\tif (!stderr) return [];\n\n\tconst checks: ToolCheck[] = [];\n\t// Match [COMPLETED] and [FAILED] status lines from lint-staged\n\tfor (const match of stderr.matchAll(/\\[(COMPLETED|FAILED)\\]\\s+(.+)/g)) {\n\t\tconst status = match[1];\n\t\tconst command = match[2].trim();\n\n\t\tif (isLintStagedMeta(command)) continue;\n\n\t\tconst tool = extractToolName(command);\n\t\tif (!tool) continue;\n\n\t\tchecks.push({ tool, ok: status === \"COMPLETED\" });\n\t}\n\n\t// Deduplicate by tool name (keep last occurrence — final status)\n\tconst seen = new Map<string, ToolCheck>();\n\tfor (const c of checks) {\n\t\tseen.set(c.tool, c);\n\t}\n\n\treturn [...seen.values()];\n}\n\n/** Heuristic: skip lint-staged internal metadata lines */\nfunction isLintStagedMeta(command: string): boolean {\n\t// Glob patterns in task labels\n\tif (/[*{}[\\]]/.test(command)) return true;\n\t// Task count labels: \"src/ — 3 files\", \"src/ — no files\"\n\t// The dash can be em-dash (—), en-dash (–), or plain hyphen (-)\n\tif (/\\s[-–—]\\s(\\d+\\s)?files?$/.test(command)) return true;\n\tif (/\\s[-–—]\\sno\\s files$/.test(command)) return true;\n\t// Internal lint-staged lifecycle messages\n\tif (\n\t\t/^(Running tasks|Applying modifications|Cleaning up|Backing up|Backed up|Updating Git)/.test(\n\t\t\tcommand,\n\t\t)\n\t)\n\t\treturn true;\n\t// Ends with ellipsis (e.g. \"Backing up original state...\")\n\tif (/\\.{3}$/.test(command)) return true;\n\treturn false;\n}\n\n/** Extract a display-friendly tool name from a lint-staged command */\nfunction extractToolName(command: string): string | null {\n\tconst tokens = command.split(/\\s+/);\n\tconst first = tokens[0];\n\n\t// npm/yarn/pnpm run <script>\n\tif ([\"npm\", \"yarn\", \"pnpm\", \"bun\"].includes(first)) {\n\t\t// Skip \"run\" if present\n\t\tconst scriptIdx = tokens[1] === \"run\" ? 2 : 1;\n\t\tconst script = tokens[scriptIdx];\n\t\tif (!script) return null;\n\t\t// Map common script names to their underlying tool\n\t\tconst scriptMap: Record<string, string> = {\n\t\t\ttypecheck: \"tsc\",\n\t\t\tlint: \"eslint\",\n\t\t\tformat: \"prettier\",\n\t\t};\n\t\treturn scriptMap[script] ?? script;\n\t}\n\n\t// npx <tool>\n\tif (first === \"npx\") return tokens[1] ?? null;\n\n\t// Direct tool invocation (biome, eslint, tsc, vitest, jest, prettier)\n\treturn first;\n}\n","import { execa } from \"execa\";\n\nexport async function copyToClipboard(content: string): Promise<boolean> {\n\tconst commands: [string, string[]][] = [\n\t\t[\"wl-copy\", []],\n\t\t[\"xclip\", [\"-selection\", \"clipboard\"]],\n\t\t[\"xsel\", [\"--clipboard\", \"--input\"]],\n\t\t[\"pbcopy\", []],\n\t];\n\n\tfor (const [cmd, args] of commands) {\n\t\ttry {\n\t\t\tawait execa(cmd, args, { input: content });\n\t\t\treturn true;\n\t\t} catch {}\n\t}\n\treturn false;\n}\n","import * as p from \"@clack/prompts\";\nimport { bold, cyan, dim, green, red, yellow } from \"kolorist\";\nimport { copyToClipboard } from \"../services/clipboard.js\";\nimport type { HookError } from \"../services/hooks.js\";\nimport { debug } from \"../utils/debug.js\";\n\nexport async function showRecoveryMenu(\n\terrors: HookError[],\n\tonRetry: () => Promise<boolean>,\n\tonSkipHooks: (message: string) => Promise<boolean>,\n\tonRestage: () => Promise<boolean>,\n\tmessage: string,\n\trawStderr: string,\n): Promise<void> {\n\tdebug(\"showRecoveryMenu: %d errors\", errors.length);\n\n\twhile (true) {\n\t\tp.note(\n\t\t\terrors.map((e) => ` ${red(\"•\")} [${e.tool}] ${e.message}`).join(\"\\n\"),\n\t\t\tred(bold(\"Pre-commit hook failed\")),\n\t\t);\n\n\t\tconst choice = await p.select({\n\t\t\tmessage: \"What do you want to do?\",\n\t\t\toptions: [\n\t\t\t\t{\n\t\t\t\t\tlabel: \"Copy error report to clipboard\",\n\t\t\t\t\tvalue: \"clipboard\",\n\t\t\t\t\thint: \"Paste into another terminal for an AI agent\",\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tlabel: \"Skip hooks and commit (--no-verify)\",\n\t\t\t\t\tvalue: \"skip\",\n\t\t\t\t\thint: \"Commit anyway, fix later\",\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tlabel: \"Re-stage files and retry\",\n\t\t\t\t\tvalue: \"restage\",\n\t\t\t\t\thint: \"Pick up fixes from another terminal\",\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tlabel: \"Edit commit message\",\n\t\t\t\t\tvalue: \"edit\",\n\t\t\t\t\thint: \"Modify the message before retrying\",\n\t\t\t\t},\n\t\t\t\t{ label: \"Cancel\", value: \"cancel\" },\n\t\t\t],\n\t\t});\n\n\t\tif (p.isCancel(choice)) {\n\t\t\tdebug(\"showRecoveryMenu: user cancelled\");\n\t\t\tp.outro(yellow(\"Cancelled. Message cached for --retry.\"));\n\t\t\tprocess.exit(1);\n\t\t\treturn;\n\t\t}\n\n\t\tdebug(\"showRecoveryMenu: user chose %s\", choice);\n\n\t\tswitch (choice) {\n\t\t\tcase \"clipboard\": {\n\t\t\t\tconst ok = await copyToClipboard(rawStderr);\n\t\t\t\tif (ok) {\n\t\t\t\t\tp.log.step(green(\"Errors copied\"));\n\t\t\t\t} else {\n\t\t\t\t\tp.log.warn(red(\"No clipboard tool found. Install xclip, wl-copy, or xsel.\"));\n\t\t\t\t}\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tcase \"skip\": {\n\t\t\t\tp.log.info(yellow(\"Committing with --no-verify...\"));\n\t\t\t\tconst ok = await onSkipHooks(message);\n\t\t\t\tif (ok) {\n\t\t\t\t\tp.outro(green(\"Committed (hooks skipped).\"));\n\t\t\t\t} else {\n\t\t\t\t\tp.outro(red(\"Commit failed even with --no-verify.\"));\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tcase \"restage\": {\n\t\t\t\tp.log.info(cyan(\"Re-staging and retrying...\"));\n\t\t\t\tconst ok = await onRestage();\n\t\t\t\tif (ok) {\n\t\t\t\t\tp.outro(green(\"Committed successfully.\"));\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\t// Loop back to menu on failure\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tcase \"edit\": {\n\t\t\t\tconst edited = await p.text({\n\t\t\t\t\tmessage: \"Edit commit message:\",\n\t\t\t\t\tinitialValue: message,\n\t\t\t\t\tvalidate: (v) => (v.trim() ? undefined : \"Message cannot be empty\"),\n\t\t\t\t});\n\t\t\t\tif (p.isCancel(edited)) {\n\t\t\t\t\tp.outro(yellow(\"Cancelled. Message cached for --retry.\"));\n\t\t\t\t\tprocess.exit(1);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tconst ok = await onRetry();\n\t\t\t\tif (ok) {\n\t\t\t\t\tp.outro(green(\"Committed successfully.\"));\n\t\t\t\t} else {\n\t\t\t\t\tp.outro(red(\"Commit failed again.\"));\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tcase \"cancel\": {\n\t\t\t\tp.outro(dim(\"Message cached for --retry.\"));\n\t\t\t\tprocess.exit(1);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t}\n}\n","import { createHash } from \"node:crypto\";\nimport { mkdir, readFile, writeFile } from \"node:fs/promises\";\nimport os from \"node:os\";\nimport { join } from \"node:path\";\nimport { debug } from \"./debug.js\";\n\nconst CACHE_DIR = join(os.homedir(), \".cache\", \"commit-mint\");\n\nfunction repoHash(repoPath: string): string {\n\treturn createHash(\"sha256\").update(repoPath).digest(\"hex\").slice(0, 12);\n}\n\nfunction cachePath(repoPath: string): string {\n\treturn join(CACHE_DIR, `${repoHash(repoPath)}.json`);\n}\n\nexport interface CachedCommit {\n\tmessage: string;\n\ttimestamp: number;\n\trepoPath: string;\n}\n\nexport async function saveCachedCommit(repoPath: string, message: string) {\n\tawait mkdir(CACHE_DIR, { recursive: true });\n\tconst data: CachedCommit = {\n\t\tmessage,\n\t\ttimestamp: Date.now(),\n\t\trepoPath,\n\t};\n\tconst path = cachePath(repoPath);\n\tdebug(\"saveCachedCommit: saving to %s\", path);\n\tawait writeFile(path, JSON.stringify(data, null, 2), \"utf8\");\n}\n\nexport async function loadCachedCommit(repoPath: string): Promise<CachedCommit | null> {\n\tconst path = cachePath(repoPath);\n\tdebug(\"loadCachedCommit: loading from %s\", path);\n\ttry {\n\t\tconst raw = await readFile(path, \"utf8\");\n\t\tconst data = JSON.parse(raw) as CachedCommit;\n\t\tdebug(\"loadCachedCommit: found message from %s\", new Date(data.timestamp).toISOString());\n\t\treturn data;\n\t} catch {\n\t\tdebug(\"loadCachedCommit: no cached commit found\");\n\t\treturn null;\n\t}\n}\n","import { intro, isCancel, log, outro, spinner } from \"@clack/prompts\";\nimport { bold, dim, green, red } from \"kolorist\";\nimport { generateCommitMessage } from \"../services/ai.js\";\nimport { getApiKey, readConfig, setConfigValue } from \"../services/config.js\";\nimport {\n\tassertGitRepo,\n\tattemptCommit,\n\tattemptCommitNoVerify,\n\tgetDefaultExcludes,\n\tgetHead,\n\tgetStagedDiff,\n\tgetStatusShort,\n\tstageAll,\n} from \"../services/git.js\";\nimport { parseHookErrors, parseToolChecks } from \"../services/hooks.js\";\nimport { showRecoveryMenu } from \"../ui/menu.js\";\nimport { loadCachedCommit, saveCachedCommit } from \"../utils/cache.js\";\nimport { debug } from \"../utils/debug.js\";\n\ninterface CommitFlags {\n\tretry: boolean;\n\tall: boolean;\n\tmessage?: string;\n\thint?: string;\n}\n\nexport async function commitCommand(flags: CommitFlags) {\n\tdebug(\"commitCommand called\", { flags });\n\tawait assertGitRepo();\n\n\t// ── Retry mode ──────────────────────────────────────────────────\n\tif (flags.retry) {\n\t\tdebug(\"Entering retry mode\");\n\t\tconst { getRepoRoot } = await import(\"../services/git.js\");\n\t\tconst repoRoot = await getRepoRoot();\n\t\tdebug(\"Repo root:\", repoRoot);\n\t\tconst cached = await loadCachedCommit(repoRoot);\n\t\tif (!cached) {\n\t\t\tdebug(\"No cached commit found\");\n\t\t\toutro(red(\"No cached commit message found. Run cmint without --retry first.\"));\n\t\t\tprocess.exit(1);\n\t\t}\n\t\tdebug(\"Loaded cached message:\", cached.message);\n\t\tintro(\"commit-mint — retry\");\n\t\tconst s = spinner();\n\t\ts.start(\"Retrying commit...\");\n\t\tconst result = await attemptCommit(cached.message);\n\t\ts.stop(\"Attempted commit\");\n\t\tdebug(\"Retry commit result:\", result);\n\t\tif (result.ok) {\n\t\t\t// Show clean tool check summary\n\t\t\tconst checks = parseToolChecks(result.stderr ?? \"\");\n\t\t\tif (checks.length > 0) {\n\t\t\t\tconst lines = checks.map((c) => ` ${c.ok ? green(\"✓\") : red(\"✗\")} ${c.tool}`);\n\t\t\t\tlog.info(lines.join(\"\\n\"));\n\t\t\t}\n\t\t\toutro(green(\"Committed successfully.\"));\n\t\t} else {\n\t\t\tconst errors = parseHookErrors(result.stderr ?? \"\");\n\t\t\tdebug(\"Hook errors on retry:\", errors.length);\n\t\t\tawait showRecoveryMenu(\n\t\t\t\terrors,\n\t\t\t\tasync () => (await attemptCommit(cached.message)).ok,\n\t\t\t\tasync (msg) => (await attemptCommitNoVerify(msg)).ok,\n\t\t\t\tasync () => {\n\t\t\t\t\tawait stageAll();\n\t\t\t\t\treturn (await attemptCommit(cached.message)).ok;\n\t\t\t\t},\n\t\t\t\tcached.message,\n\t\t\t\tresult.stderr ?? \"\",\n\t\t\t);\n\t\t}\n\t\treturn;\n\t}\n\n\t// ── Normal mode ─────────────────────────────────────────────────\n\tintro(\"commit-mint\");\n\n\tconst status = await getStatusShort();\n\tdebug(\"Git status:\", status || \"(empty)\");\n\tif (!status) {\n\t\toutro(dim(\"Nothing to commit.\"));\n\t\treturn;\n\t}\n\n\t// Stage all changes\n\tconst s = spinner();\n\ts.start(\"Staging all changes...\");\n\tawait stageAll();\n\ts.stop(\"Changes staged\");\n\n\t// Get diff for AI\n\tconst diffResult = await getStagedDiff();\n\tif (!diffResult) {\n\t\tdebug(\"No staged changes found after staging\");\n\t\toutro(red(\"No staged changes found.\"));\n\t\tprocess.exit(1);\n\t}\n\n\t// Handle all-staged-files-are-excluded case with hardcoded message\n\tif (\"excludedFiles\" in diffResult) {\n\t\tdebug(\"All staged files are excluded:\", diffResult.excludedFiles);\n\t\tconst message = buildExcludedFilesMessage(diffResult.excludedFiles);\n\n\t\tlog.info(diffResult.excludedFiles.map((f) => ` ${f}`).join(\"\\n\"));\n\n\t\t// Cache and commit with hardcoded message\n\t\tconst { getRepoRoot } = await import(\"../services/git.js\");\n\t\tconst repoRoot = await getRepoRoot();\n\t\tawait saveCachedCommit(repoRoot, message);\n\n\t\ts.start(\"Committing...\");\n\t\tconst headBefore = await getHead();\n\t\tconst result = await attemptCommit(message);\n\t\tconst headAfter = await getHead();\n\n\t\tif (result.ok || headBefore !== headAfter) {\n\t\t\ts.stop(\"Committed successfully.\");\n\t\t\toutro(green(\"Done.\"));\n\t\t\treturn;\n\t\t}\n\n\t\ts.stop(\"Commit failed.\");\n\t\tconst errors = parseHookErrors(result.stderr ?? \"\");\n\t\tawait showRecoveryMenu(\n\t\t\terrors,\n\t\t\tasync () => (await attemptCommit(message)).ok,\n\t\t\tasync (msg) => (await attemptCommitNoVerify(msg)).ok,\n\t\t\tasync () => {\n\t\t\t\tawait stageAll();\n\t\t\t\treturn (await attemptCommit(message)).ok;\n\t\t\t},\n\t\t\tmessage,\n\t\t\tresult.stderr ?? \"\",\n\t\t);\n\t\treturn;\n\t}\n\n\tdebug(\"Staged files:\", diffResult.files);\n\tdebug(\"Diff length:\", diffResult.diff.length, \"chars\");\n\n\tlog.info(diffResult.files.map((f) => ` ${f}`).join(\"\\n\"));\n\n\t// Generate or use provided message\n\tlet message: string;\n\n\tif (flags.message) {\n\t\tdebug(\"Using provided message:\", flags.message);\n\t\tmessage = flags.message;\n\t} else {\n\t\t// Ensure API key is available before generating\n\t\ttry {\n\t\t\tawait getApiKey();\n\t\t\tdebug(\"API key found\");\n\t\t} catch {\n\t\t\tdebug(\"No API key found, prompting user\");\n\t\t\tconst { text: promptText } = await import(\"@clack/prompts\");\n\t\t\tconst key = await promptText({\n\t\t\t\tmessage: \"Enter your Groq API key:\",\n\t\t\t\tplaceholder: \"gsk_...\",\n\t\t\t\tvalidate: (v: string) => (v.trim() ? undefined : \"API key is required\"),\n\t\t\t});\n\t\t\tif (isCancel(key)) {\n\t\t\t\toutro(dim(\"Cancelled.\"));\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tawait setConfigValue(\"GROQ_API_KEY\", String(key).trim());\n\t\t\tdebug(\"API key saved to config\");\n\t\t}\n\n\t\ts.start(\"Generating commit message...\");\n\t\ttry {\n\t\t\tconst genStart = Date.now();\n\t\t\tmessage = await generateMessage(diffResult.diff, flags.hint);\n\t\t\tdebug(\"generateMessage took %d ms\", Date.now() - genStart);\n\t\t\tdebug(\"Generated message:\", message);\n\t\t} catch (err) {\n\t\t\ts.stop(red(\"Failed to generate message.\"));\n\t\t\tdebug(\"Message generation failed:\", err instanceof Error ? err.message : String(err));\n\t\t\toutro(red(err instanceof Error ? err.message : String(err)));\n\t\t\treturn;\n\t\t}\n\t\ts.stop(\"Message generated\");\n\t}\n\n\t// Review message\n\tconst { select, text } = await import(\"@clack/prompts\");\n\tconst review = await select({\n\t\tmessage: `Review commit message:\\n\\n ${bold(message)}\\n`,\n\t\toptions: [\n\t\t\t{ label: \"Use as-is\", value: \"use\" },\n\t\t\t{ label: \"Edit\", value: \"edit\" },\n\t\t\t{ label: \"Cancel\", value: \"cancel\" },\n\t\t],\n\t});\n\n\tif (isCancel(review) || review === \"cancel\") {\n\t\tdebug(\"User cancelled at review step\");\n\t\toutro(dim(\"Cancelled.\"));\n\t\treturn;\n\t}\n\n\tif (review === \"edit\") {\n\t\tdebug(\"User chose to edit message\");\n\t\tconst edited = await text({\n\t\t\tmessage: \"Edit commit message:\",\n\t\t\tinitialValue: message,\n\t\t\tvalidate: (v: string) => (v.trim() ? undefined : \"Message cannot be empty\"),\n\t\t});\n\t\tif (isCancel(edited)) {\n\t\t\toutro(dim(\"Cancelled.\"));\n\t\t\treturn;\n\t\t}\n\t\tmessage = String(edited).trim();\n\t\tdebug(\"Edited message:\", message);\n\t}\n\n\t// Cache message before attempting commit\n\tconst { getRepoRoot } = await import(\"../services/git.js\");\n\tconst repoRoot = await getRepoRoot();\n\tawait saveCachedCommit(repoRoot, message);\n\tdebug(\"Message cached for repo:\", repoRoot);\n\n\t// Attempt commit\n\ts.start(\"Committing...\");\n\tconst headBefore = await getHead();\n\tdebug(\"HEAD before commit:\", headBefore);\n\tconst result = await attemptCommit(message);\n\tconst headAfter = await getHead();\n\tdebug(\"HEAD after commit:\", headAfter);\n\tdebug(\"Commit result:\", result);\n\n\tif (result.ok || headBefore !== headAfter) {\n\t\ts.stop(\"Committed successfully.\");\n\n\t\t// Show clean tool check summary\n\t\tconst checks = parseToolChecks(result.stderr ?? \"\");\n\t\tif (checks.length > 0) {\n\t\t\tconst lines = checks.map((c) => ` ${c.ok ? green(\"✓\") : red(\"✗\")} ${c.tool}`);\n\t\t\tlog.info(lines.join(\"\\n\"));\n\t\t}\n\n\t\toutro(green(\"Done.\"));\n\t\treturn;\n\t}\n\n\ts.stop(\"Commit failed.\");\n\tdebug(\"Commit failed, showing recovery menu\");\n\n\t// Hook failure — show recovery menu\n\tconst errors = parseHookErrors(result.stderr ?? \"\");\n\tdebug(\"Parsed hook errors:\", errors.length, \"errors\");\n\tawait showRecoveryMenu(\n\t\terrors,\n\t\tasync () => {\n\t\t\tconst r = await attemptCommit(message);\n\t\t\treturn r.ok;\n\t\t},\n\t\tasync (msg) => {\n\t\t\tconst r = await attemptCommitNoVerify(msg);\n\t\t\treturn r.ok;\n\t\t},\n\t\tasync () => {\n\t\t\tawait stageAll();\n\t\t\tconst r = await attemptCommit(message);\n\t\t\treturn r.ok;\n\t\t},\n\t\tmessage,\n\t\tresult.stderr ?? \"\",\n\t);\n}\n\nasync function generateMessage(diff: string, hint?: string): Promise<string> {\n\tconst config = await readConfig();\n\tconst apiKey = await getApiKey();\n\tdebug(\n\t\t\"Generating message with model:\",\n\t\tconfig.model,\n\t\t\"max-length:\",\n\t\tconfig[\"max-length\"],\n\t\t\"type:\",\n\t\tconfig.type,\n\t);\n\n\treturn generateCommitMessage(diff, {\n\t\tapiKey,\n\t\tmodel: config.model,\n\t\tmaxLength: config[\"max-length\"] ? parseInt(config[\"max-length\"], 10) : undefined,\n\t\ttype: config.type,\n\t\ttimeout: config.timeout ? parseInt(config.timeout, 10) : undefined,\n\t\thint,\n\t});\n}\n\nfunction buildExcludedFilesMessage(files: string[]): string {\n\tconst excludes = getDefaultExcludes();\n\tconst isLockfile = (f: string) =>\n\t\texcludes.some((pattern) => {\n\t\t\tif (pattern.endsWith(\".lock\") || pattern.endsWith(\".json\")) {\n\t\t\t\treturn f === pattern || f.endsWith(pattern.replace(\"*.\", \".\"));\n\t\t\t}\n\t\t\treturn false;\n\t\t});\n\n\tif (files.every(isLockfile)) {\n\t\treturn \"chore: update lockfile\";\n\t}\n\n\treturn \"chore: update generated files\";\n}\n","import { command } from \"cleye\";\nimport { getConfigValue, setConfigValue } from \"../services/config.js\";\n\nexport const configCommand = command(\n\t{\n\t\tname: \"config\",\n\t\tparameters: [\"<mode>\", \"<key=value...>\"],\n\t},\n\tasync (argv) => {\n\t\tconst { mode, keyValue } = argv._;\n\n\t\tif (mode === \"get\") {\n\t\t\tfor (const kv of keyValue) {\n\t\t\t\tconst key = kv.split(\"=\")[0];\n\t\t\t\tconst value = await getConfigValue(key);\n\t\t\t\tconsole.log(`${key}=${value ?? \"\"}`);\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\tif (mode === \"set\") {\n\t\t\tfor (const kv of keyValue) {\n\t\t\t\tconst [key, ...rest] = kv.split(\"=\");\n\t\t\t\tconst value = rest.join(\"=\");\n\t\t\t\tawait setConfigValue(key, value);\n\t\t\t}\n\t\t\tconsole.log(\"Config updated.\");\n\t\t\treturn;\n\t\t}\n\n\t\tconsole.error(`Unknown config mode: ${mode}. Use \"get\" or \"set\".`);\n\t\tprocess.exit(1);\n\t},\n);\n","#!/usr/bin/env node\nimport { cli } from \"cleye\";\nimport pkg from \"../package.json\" with { type: \"json\" };\n\nconst { version } = pkg;\n\nimport { commitCommand } from \"./commands/commit.js\";\nimport { configCommand } from \"./commands/config.js\";\nimport { setDebug } from \"./utils/debug.js\";\n\ncli(\n\t{\n\t\tname: \"cmint\",\n\t\tversion,\n\t\tdescription: \"A commit tool that actually handles hook failures\",\n\t\tflags: {\n\t\t\tretry: {\n\t\t\t\ttype: Boolean,\n\t\t\t\tdescription: \"Retry the last failed commit\",\n\t\t\t\talias: \"r\",\n\t\t\t\tdefault: false,\n\t\t\t},\n\t\t\tall: {\n\t\t\t\ttype: Boolean,\n\t\t\t\tdescription: \"Auto-stage all tracked files\",\n\t\t\t\talias: \"a\",\n\t\t\t\tdefault: false,\n\t\t\t},\n\t\t\tmessage: {\n\t\t\t\ttype: String,\n\t\t\t\tdescription: \"Provide a commit message directly (skip AI generation)\",\n\t\t\t\talias: \"m\",\n\t\t\t},\n\t\t\thint: {\n\t\t\t\ttype: String,\n\t\t\t\tdescription: \"Add context hint for AI commit message generation\",\n\t\t\t\talias: \"H\",\n\t\t\t},\n\t\t\tdebug: {\n\t\t\t\ttype: Boolean,\n\t\t\t\tdescription: \"Enable debug output\",\n\t\t\t\talias: \"d\",\n\t\t\t\tdefault: false,\n\t\t\t},\n\t\t},\n\t\tcommands: [configCommand],\n\t},\n\t(argv) => {\n\t\tsetDebug(argv.flags.debug);\n\t\tcommitCommand(argv.flags);\n\t},\n);\n"],"mappings":"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"}
1
+ {"version":3,"file":"cli.mjs","names":["pkg"],"sources":["../package.json","../src/utils/debug.ts","../src/services/ai.ts","../src/services/config.ts","../src/services/git.ts","../src/services/hooks.ts","../src/services/clipboard.ts","../src/ui/menu.ts","../src/utils/cache.ts","../src/commands/commit.ts","../src/commands/config.ts","../src/cli.ts"],"sourcesContent":["","import { dim } from \"kolorist\";\n\nlet enabled = false;\n\nexport function setDebug(value: boolean): void {\n\tenabled = value;\n}\n\nexport function isDebug(): boolean {\n\treturn enabled;\n}\n\nexport function debug(...args: unknown[]): void {\n\tif (!enabled) return;\n\tconst timestamp = new Date().toISOString().slice(11, 23);\n\tconsole.error(dim(`[debug ${timestamp}]`), ...args);\n}\n","import Groq from \"groq-sdk\";\nimport { debug } from \"../utils/debug.js\";\n\nconst MAX_DIFF_CHARS = 20000;\n\nconst CONVENTIONAL_COMMIT_REGEX =\n\t/^(build|chore|ci|docs|feat|fix|perf|refactor|revert|style|test)(\\(.+\\))?!?: .+$/;\n\nfunction stripThinkTags(text: string): string {\n\treturn text.replace(/<think[\\s\\S]*?<\\/think>/gi, \"\").trim();\n}\n\nfunction deriveMessageFromReasoning(reasoning: string): string | null {\n\tconst match = reasoning.match(\n\t\t/(build|chore|ci|docs|feat|fix|perf|refactor|revert|style|test)(\\(.+\\))?!?: .+/i,\n\t);\n\tif (match) return match[0].trim();\n\n\tconst sentences = reasoning.split(/[.!?]/);\n\tconst first = sentences.find((s) => s.trim().length >= 10);\n\treturn first ? first.trim() : null;\n}\n\nfunction stripContextLines(diff: string): string {\n\treturn diff\n\t\t.split(\"\\n\")\n\t\t.filter((line) => !line.startsWith(\" \"))\n\t\t.join(\"\\n\");\n}\n\nfunction compressDiff(diff: string): string {\n\t// Tier 0 — Full diff\n\tif (diff.length <= MAX_DIFF_CHARS) {\n\t\treturn diff;\n\t}\n\n\t// Tier 1 — Strip context lines\n\tlet result = stripContextLines(diff);\n\tif (result.length <= MAX_DIFF_CHARS) {\n\t\treturn result;\n\t}\n\n\t// Tier 2 — Per-hunk line cap\n\tconst fileDiffs = result.split(/(?=diff --git)/).filter(Boolean);\n\tconst cappedFiles = fileDiffs.map((fd) => {\n\t\tconst parts = fd.split(/(?=\\n@@)/);\n\t\tconst cappedParts = parts.map((part, idx) => {\n\t\t\tif (idx === 0) return part; // Keep file header\n\t\t\tconst lines = part.split(\"\\n\");\n\t\t\tconst header = lines[0]; // @@ line\n\t\t\tconst changedLines = lines.slice(1).filter((l) => l.startsWith(\"+\") || l.startsWith(\"-\"));\n\t\t\tconst keptLines = changedLines.slice(0, 10);\n\t\t\treturn [header, ...keptLines].join(\"\\n\");\n\t\t});\n\t\treturn cappedParts.join(\"\");\n\t});\n\tresult = cappedFiles.join(\"\");\n\tif (result.length <= MAX_DIFF_CHARS) {\n\t\treturn result;\n\t}\n\n\t// Tier 3 — File summary\n\tconst fileMatches = diff.match(/^diff --git a\\/(.+) b\\/(.+)$/gm) || [];\n\tconst summary = fileMatches\n\t\t.map((f) => {\n\t\t\tconst match = f.match(/^diff --git a\\/(.+) b\\/(.+)$/);\n\t\t\treturn match && match[1] === match[2] ? `${match[1]} | changed` : \"\";\n\t\t})\n\t\t.filter(Boolean);\n\treturn `Summary of changes:\\n${summary.join(\"\\n\")}`;\n}\n\nfunction buildStatSummary(diff: string): string {\n\tconst files: { name: string; adds: number; dels: number }[] = [];\n\tlet currentFile = \"\";\n\tlet adds = 0;\n\tlet dels = 0;\n\n\tfor (const line of diff.split(\"\\n\")) {\n\t\tconst match = line.match(/^diff --git a\\/.+ b\\/(.+)$/);\n\t\tif (match) {\n\t\t\tif (currentFile) files.push({ name: currentFile, adds, dels });\n\t\t\tcurrentFile = match[1];\n\t\t\tadds = 0;\n\t\t\tdels = 0;\n\t\t} else if (line.startsWith(\"+\") && !line.startsWith(\"+++\")) {\n\t\t\tadds++;\n\t\t} else if (line.startsWith(\"-\") && !line.startsWith(\"---\")) {\n\t\t\tdels++;\n\t\t}\n\t}\n\tif (currentFile) files.push({ name: currentFile, adds, dels });\n\n\tconst totalAdds = files.reduce((s, f) => s + f.adds, 0);\n\tconst totalDels = files.reduce((s, f) => s + f.dels, 0);\n\n\tconst lines = files.map((f) => ` ${f.name} | +${f.adds} -${f.dels}`);\n\tlines.push(\n\t\t` ${files.length} files changed, ${totalAdds} insertions(+), ${totalDels} deletions(-)`,\n\t);\n\n\treturn lines.join(\"\\n\");\n}\n\nfunction buildSystemPrompt(type?: string): string {\n\tlet prompt =\n\t\t\"You are a commit message generator. Follow the Conventional Commits specification.\\n\" +\n\t\t\"Valid types: build, chore, ci, docs, feat, fix, perf, refactor, revert, style, test.\\n\" +\n\t\t\"Format: type(scope): description\\n\" +\n\t\t\"Use imperative mood, lowercase, no trailing period.\\n\" +\n\t\t\"Output ONLY the commit message, no markdown fences, no explanation.\";\n\n\tif (type && type.trim().length > 0) {\n\t\tprompt += `\\nYou MUST use type: ${type}`;\n\t}\n\n\treturn prompt;\n}\n\nfunction buildUserPrompt(diff: string, hint?: string, statSummary?: string): string {\n\tconst parts: string[] = [];\n\tif (hint) parts.push(`Context: ${hint}`);\n\tif (statSummary) parts.push(`Change summary:\\n${statSummary}`);\n\tparts.push(`Generate a conventional commit for:\\n\\n${diff}`);\n\treturn parts.join(\"\\n\\n\");\n}\n\nfunction isValidConventionalCommit(message: string): boolean {\n\treturn CONVENTIONAL_COMMIT_REGEX.test(message);\n}\n\nfunction extractContentText(\n\tcontent: string | Array<{ type: string; text?: string }> | null | undefined,\n): string {\n\tif (content == null) return \"\";\n\tif (typeof content === \"string\") return content.trim();\n\tif (Array.isArray(content)) {\n\t\treturn content\n\t\t\t.filter((part) => part.type === \"text\" && typeof part.text === \"string\")\n\t\t\t.map((part) => stripThinkTags(part.text as string))\n\t\t\t.join(\"\")\n\t\t\t.trim();\n\t}\n\treturn \"\";\n}\n\nexport async function generateCommitMessage(\n\tdiff: string,\n\toptions: {\n\t\tapiKey: string;\n\t\tmodel?: string;\n\t\ttype?: string;\n\t\ttimeout?: number;\n\t\thint?: string;\n\t},\n): Promise<string> {\n\tdebug(\n\t\t\"generateCommitMessage: model=%s, type=%s, hint=%s\",\n\t\toptions.model ?? \"default\",\n\t\toptions.type ?? \"none\",\n\t\toptions.hint ?? \"none\",\n\t);\n\n\tconst timeoutMs = options.timeout ?? 60000;\n\tdebug(\"Timeout: %d ms\", timeoutMs);\n\n\tconst client = new Groq({\n\t\tapiKey: options.apiKey,\n\t\ttimeout: timeoutMs,\n\t});\n\n\tconst compressedDiff = compressDiff(diff);\n\tconst statSummary = buildStatSummary(diff);\n\tconst systemPrompt = buildSystemPrompt(options.type);\n\tconst userPrompt = buildUserPrompt(compressedDiff, options.hint, statSummary);\n\n\tdebug(\"Diff: %d chars → compressed to %d chars\", diff.length, compressedDiff.length);\n\tdebug(\"Stat summary:\\n%s\", statSummary);\n\tdebug(\"User prompt length: %d chars\", userPrompt.length);\n\n\tasync function callAI(strictSystemPrompt?: string): Promise<string> {\n\t\tconst callStart = Date.now();\n\t\tconst isRetry = !!strictSystemPrompt;\n\t\tdebug(\n\t\t\t\"callAI: %s — model=%s, promptLen=%d, systemLen=%d\",\n\t\t\tisRetry ? \"RETRY (strict)\" : \"INITIAL\",\n\t\t\toptions.model ?? \"openai/gpt-oss-20b\",\n\t\t\tuserPrompt.length,\n\t\t\t(strictSystemPrompt ?? systemPrompt).length,\n\t\t);\n\t\ttry {\n\t\t\tconst isReasoningModel = /^(o[1-9]|.*gpt-oss.*|.*gpt-5.*)/i.test(options.model ?? \"\");\n\t\t\tconst completion = await client.chat.completions.create({\n\t\t\t\tmessages: [\n\t\t\t\t\t{ role: \"system\", content: strictSystemPrompt ?? systemPrompt },\n\t\t\t\t\t{ role: \"user\", content: userPrompt },\n\t\t\t\t],\n\t\t\t\tmodel: options.model ?? \"openai/gpt-oss-20b\",\n\t\t\t\ttemperature: 0.3,\n\t\t\t\t...(isReasoningModel ? { max_completion_tokens: 1024 } : { max_tokens: 1024 }),\n\t\t\t\treasoning_format: \"parsed\",\n\t\t\t});\n\n\t\t\tconst elapsed = Date.now() - callStart;\n\t\t\tconst rawContent = completion.choices[0]?.message?.content;\n\t\t\tconst processedContent =\n\t\t\t\ttypeof rawContent === \"string\" ? stripThinkTags(rawContent) : rawContent;\n\t\t\tconst content = extractContentText(processedContent);\n\t\t\tdebug(\n\t\t\t\t\"callAI response (%d ms): choices=%d, finishReason=%s, contentLen=%d, rawType=%s\",\n\t\t\t\telapsed,\n\t\t\t\tcompletion.choices.length,\n\t\t\t\tcompletion.choices[0]?.finish_reason ?? \"(none)\",\n\t\t\t\tcontent.length,\n\t\t\t\ttypeof rawContent,\n\t\t\t);\n\t\t\tdebug(\"callAI raw content: %s\", content.slice(0, 300) || \"(empty)\");\n\t\t\tif (!content) {\n\t\t\t\tconst reasoning = completion.choices[0]?.message?.reasoning;\n\t\t\t\tdebug(\n\t\t\t\t\t\"callAI: content empty, attempting reasoning fallback (reasoningLen=%d)\",\n\t\t\t\t\treasoning?.length ?? 0,\n\t\t\t\t);\n\t\t\t\tif (reasoning) {\n\t\t\t\t\tconst derived = deriveMessageFromReasoning(reasoning);\n\t\t\t\t\tif (derived) {\n\t\t\t\t\t\tdebug(\"callAI: derived message from reasoning: %s\", derived.slice(0, 100));\n\t\t\t\t\t\treturn stripThinkTags(derived);\n\t\t\t\t\t}\n\t\t\t\t\tdebug(\"callAI: could not derive message from reasoning\");\n\t\t\t\t}\n\t\t\t\tthrow new Error(\"AI returned an empty commit message\");\n\t\t\t}\n\t\t\treturn content;\n\t\t} catch (error) {\n\t\t\tconst elapsed = Date.now() - callStart;\n\t\t\tdebug(\n\t\t\t\t\"callAI FAILED after %d ms: %s\",\n\t\t\t\telapsed,\n\t\t\t\terror instanceof Error ? `${error.name}: ${error.message}` : String(error),\n\t\t\t);\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\ttry {\n\t\tconst totalStart = Date.now();\n\t\tlet message = await callAI();\n\t\tdebug(\n\t\t\t\"Validation: message=%s, isValid=%s\",\n\t\t\tmessage.slice(0, 100),\n\t\t\tisValidConventionalCommit(message),\n\t\t);\n\n\t\tif (!isValidConventionalCommit(message)) {\n\t\t\tdebug(\n\t\t\t\t\"Initial message failed conventional commit validation, retrying with strict prompt (elapsed: %d ms)\",\n\t\t\t\tDate.now() - totalStart,\n\t\t\t);\n\t\t\tconst retryMessage = await callAI(\n\t\t\t\t\"You MUST output ONLY a valid conventional commit message. \" +\n\t\t\t\t\t\"Format: type(scope): description. \" +\n\t\t\t\t\t\"If you output anything else your response will be rejected.\\n\" +\n\t\t\t\t\t\"Valid types: build, chore, ci, docs, feat, fix, perf, refactor, revert, style, test.\",\n\t\t\t);\n\t\t\tdebug(\n\t\t\t\t\"Retry validation: message=%s, isValid=%s\",\n\t\t\t\tretryMessage.slice(0, 100),\n\t\t\t\tisValidConventionalCommit(retryMessage),\n\t\t\t);\n\t\t\tif (isValidConventionalCommit(retryMessage)) {\n\t\t\t\tdebug(\"Retry produced valid conventional commit\");\n\t\t\t\tmessage = retryMessage;\n\t\t\t} else {\n\t\t\t\tdebug(\"Retry also failed validation, using original message\");\n\t\t\t}\n\t\t}\n\n\t\tdebug(\"Final message (%d ms total): %s\", Date.now() - totalStart, message);\n\t\treturn message;\n\t} catch (error) {\n\t\tdebug(\"AI error: %s\", error instanceof Error ? error.message : String(error));\n\t\tif (error instanceof Groq.AuthenticationError) {\n\t\t\tthrow new Error(\"Invalid GROQ_API_KEY. Run: cmint config set GROQ_API_KEY=<key>\");\n\t\t}\n\t\tif (error instanceof Groq.RateLimitError) {\n\t\t\tthrow new Error(\"Rate limited by Groq. Please wait and try again.\");\n\t\t}\n\t\tif (error instanceof Groq.APIConnectionTimeoutError) {\n\t\t\tthrow new Error(\"Request timed out. Check your network or try a smaller diff.\");\n\t\t}\n\t\tif (error instanceof Groq.APIError) {\n\t\t\tthrow new Error(`Groq API error: ${error.message}`);\n\t\t}\n\t\tthrow new Error(`Unexpected error: ${error instanceof Error ? error.message : String(error)}`);\n\t}\n}\n","import { readFile, writeFile } from \"node:fs/promises\";\nimport os from \"node:os\";\nimport { join } from \"node:path\";\nimport ini from \"ini\";\nimport { debug } from \"../utils/debug.js\";\n\nconst CONFIG_PATH = join(os.homedir(), \".commit-mint\");\n\ninterface Config {\n\tGROQ_API_KEY?: string;\n\tmodel?: string;\n\tlocale?: string;\n\t\"max-length\"?: string;\n\ttype?: string;\n\tproxy?: string;\n\ttimeout?: string;\n}\n\nconst defaults: Config = {\n\tmodel: \"openai/gpt-oss-20b\",\n\tlocale: \"en\",\n\t\"max-length\": \"100\",\n\ttype: \"\",\n\ttimeout: \"10000\",\n};\n\nexport async function readConfig(): Promise<Config> {\n\tdebug(\"readConfig: loading from %s\", CONFIG_PATH);\n\ttry {\n\t\tconst raw = await readFile(CONFIG_PATH, \"utf8\");\n\t\tconst parsed = ini.parse(raw);\n\t\tconst merged = { ...defaults, ...parsed };\n\t\tdebug(\"readConfig: loaded keys: %s\", Object.keys(merged).join(\", \"));\n\t\treturn merged;\n\t} catch {\n\t\tdebug(\"readConfig: no config file, using defaults\");\n\t\treturn { ...defaults };\n\t}\n}\n\nexport async function writeConfig(updates: Record<string, string>) {\n\tconst existing = await readConfig();\n\tObject.assign(existing, updates);\n\tawait writeFile(CONFIG_PATH, ini.stringify(existing), \"utf8\");\n}\n\nexport async function getConfigValue(key: string): Promise<string | undefined> {\n\tconst config = await readConfig();\n\treturn config[key as keyof Config];\n}\n\nexport async function setConfigValue(key: string, value: string) {\n\tawait writeConfig({ [key]: value });\n}\n\nexport async function getApiKey(): Promise<string> {\n\tconst envKey = process.env.GROQ_API_KEY;\n\tif (envKey) {\n\t\tdebug(\"getApiKey: found in env\");\n\t\treturn envKey;\n\t}\n\n\tconst config = await readConfig();\n\tif (config.GROQ_API_KEY) {\n\t\tdebug(\"getApiKey: found in config\");\n\t\treturn config.GROQ_API_KEY;\n\t}\n\n\tdebug(\"getApiKey: not found\");\n\tthrow new Error(\"Please set your Groq API key via `cmint config set GROQ_API_KEY=<your token>`\");\n}\n","import type { ExecaError } from \"execa\";\nimport { execa } from \"execa\";\nimport { debug } from \"../utils/debug.js\";\n\nexport class KnownError extends Error {}\n\nexport async function assertGitRepo() {\n\tdebug(\"assertGitRepo\");\n\tconst { failed } = await execa(\"git\", [\"rev-parse\", \"--show-toplevel\"], {\n\t\treject: false,\n\t});\n\tif (failed) {\n\t\tthrow new KnownError(\"The current directory must be a Git repository!\");\n\t}\n}\n\nexport async function getRepoRoot() {\n\tconst { stdout } = await execa(\"git\", [\"rev-parse\", \"--show-toplevel\"]);\n\tdebug(\"getRepoRoot:\", stdout.trim());\n\treturn stdout.trim();\n}\n\nexport interface StagedDiffResult {\n\tfiles: string[];\n\tdiff: string;\n}\n\nexport interface ExcludedFilesResult {\n\texcludedFiles: string[];\n}\n\nexport type DiffResult = StagedDiffResult | ExcludedFilesResult | null;\n\nexport interface ChangedFile {\n\tpath: string;\n\tstatus: string;\n}\n\nconst DEFAULT_EXCLUDES = [\n\t\"package-lock.json\",\n\t\"node_modules/**\",\n\t\"dist/**\",\n\t\"build/**\",\n\t\".next/**\",\n\t\"coverage/**\",\n\t\"*.log\",\n\t\"*.min.js\",\n\t\"*.min.css\",\n\t\"*.lock\",\n\t\".DS_Store\",\n];\n\nexport function getDefaultExcludes(): string[] {\n\treturn [...DEFAULT_EXCLUDES];\n}\n\nexport async function getStagedDiff(exclude?: string[]): Promise<DiffResult> {\n\tconst excludeArgs = (exclude ?? []).map((e) => `:(exclude)${e}`);\n\tconst defaultExcludeArgs = DEFAULT_EXCLUDES.map((e) => `:(exclude)${e}`);\n\n\t// Check all staged files without excludes to detect \"all excluded\" case\n\tconst { stdout: allFiles } = await execa(\"git\", [\"diff\", \"--cached\", \"--name-only\"]);\n\tif (!allFiles) {\n\t\tdebug(\"getStagedDiff: no staged files\");\n\t\treturn null;\n\t}\n\n\t// Check staged files with excludes applied\n\tconst { stdout: files } = await execa(\"git\", [\n\t\t\"diff\",\n\t\t\"--cached\",\n\t\t\"--name-only\",\n\t\t...defaultExcludeArgs,\n\t\t...excludeArgs,\n\t]);\n\n\tif (!files) {\n\t\t// All staged files were excluded\n\t\tconst excludedFiles = allFiles.split(\"\\n\").filter(Boolean);\n\t\tdebug(\"getStagedDiff: all files excluded:\", excludedFiles);\n\t\treturn { excludedFiles };\n\t}\n\n\tconst { stdout: diff } = await execa(\"git\", [\n\t\t\"diff\",\n\t\t\"--cached\",\n\t\t\"--diff-algorithm=minimal\",\n\t\t...defaultExcludeArgs,\n\t\t...excludeArgs,\n\t]);\n\n\tdebug(\"getStagedDiff:\", files.split(\"\\n\").filter(Boolean).length, \"files,\", diff.length, \"chars\");\n\treturn { files: files.split(\"\\n\").filter(Boolean), diff };\n}\n\nexport async function stageAll() {\n\tdebug(\"stageAll: git add -A\");\n\tawait execa(\"git\", [\"add\", \"-A\"]);\n}\n\nexport async function getHead() {\n\tconst { stdout } = await execa(\"git\", [\"rev-parse\", \"HEAD\"]);\n\treturn stdout.trim();\n}\n\nexport async function getStatusShort() {\n\tconst { stdout } = await execa(\"git\", [\"status\", \"--short\"]);\n\treturn stdout.trim();\n}\n\nexport async function getChangedFiles(): Promise<ChangedFile[]> {\n\tconst { stdout } = await execa(\"git\", [\"status\", \"--short\"]);\n\tif (!stdout.trim()) return [];\n\tconst files = stdout\n\t\t.split(\"\\n\")\n\t\t.filter(Boolean)\n\t\t.map((line) => ({\n\t\t\tstatus: line.slice(0, 2).trim(),\n\t\t\tpath: line.slice(3),\n\t\t}));\n\tdebug(\"getChangedFiles:\", files.length, \"files\");\n\treturn files;\n}\n\nexport async function stageFiles(paths: string[]): Promise<void> {\n\tdebug(\"stageFiles:\", paths);\n\tawait execa(\"git\", [\"add\", ...paths]);\n}\n\nexport interface CommitResult {\n\tok: boolean;\n\terror?: string;\n\t/** Collected stderr from hooks/lint-staged — set on both success and failure */\n\tstderr?: string;\n}\n\nexport async function attemptCommit(\n\tmessage: string,\n\textraArgs: string[] = [],\n): Promise<CommitResult> {\n\tdebug(\"attemptCommit:\", message, extraArgs.length ? extraArgs : \"(no extra args)\");\n\ttry {\n\t\tconst subprocess = execa(\"git\", [\"commit\", \"-m\", message, ...extraArgs]);\n\n\t\t// Collect hook output (lint-staged, biome, etc.) for post-commit display\n\t\t// We don't stream to the terminal — the success/failure result is enough\n\t\tconst stderrChunks: string[] = [];\n\t\tsubprocess.stderr?.on(\"data\", (chunk: Buffer) => {\n\t\t\tstderrChunks.push(chunk.toString());\n\t\t});\n\n\t\tawait subprocess;\n\t\tdebug(\"attemptCommit: success\");\n\t\treturn { ok: true, stderr: stderrChunks.join(\"\") };\n\t} catch (error) {\n\t\tconst e = error as ExecaError;\n\t\tdebug(\"attemptCommit: failed —\", e.message?.slice(0, 200));\n\t\treturn {\n\t\t\tok: false,\n\t\t\terror: e.message,\n\t\t\tstderr: typeof e.stderr === \"string\" ? e.stderr : \"\",\n\t\t};\n\t}\n}\n\nexport async function attemptCommitNoVerify(message: string): Promise<CommitResult> {\n\tdebug(\"attemptCommitNoVerify:\", message);\n\treturn attemptCommit(message, [\"--no-verify\"]);\n}\n","import { debug } from \"../utils/debug.js\";\n\nexport interface HookError {\n\ttool: string;\n\tmessage: string;\n\traw: string;\n}\n\n/**\n * Parse git hook error output into structured, human-readable errors.\n * Handles output from lint-staged, biome, eslint, tsc, vitest, jest.\n */\nexport function parseHookErrors(stderr: string): HookError[] {\n\tif (!stderr) return [];\n\n\tdebug(\"parseHookErrors: stderr length=%d\", stderr.length);\n\tconst errors: HookError[] = [];\n\n\t// Detect lint-staged task failures\n\tif (stderr.includes(\"lint-staged\") || stderr.includes(\"[FAILED]\")) {\n\t\terrors.push(...parseLintStagedErrors(stderr));\n\t}\n\n\t// Detect biome errors\n\tif (stderr.includes(\"biome\") || stderr.includes(\"Biome\")) {\n\t\terrors.push(...parseBiomeErrors(stderr));\n\t}\n\n\t// Detect TypeScript errors\n\tif (stderr.includes(\"error TS\") || stderr.includes(\"tsc\")) {\n\t\terrors.push(...parseTscErrors(stderr));\n\t}\n\n\t// Detect vitest/jest test failures\n\tif (\n\t\tstderr.includes(\"vitest\") ||\n\t\tstderr.includes(\"jest\") ||\n\t\tstderr.includes(\"FAIL\") ||\n\t\tstderr.includes(\"test failed\")\n\t) {\n\t\terrors.push(...parseTestErrors(stderr));\n\t}\n\n\t// Detect ESLint errors\n\tif (stderr.includes(\"eslint\") || stderr.includes(\"ESLint\")) {\n\t\terrors.push(...parseEslintErrors(stderr));\n\t}\n\n\t// Fallback: if nothing parsed, return the raw output\n\tif (errors.length === 0) {\n\t\tdebug(\"parseHookErrors: no patterns matched, using raw fallback\");\n\t\terrors.push({\n\t\t\ttool: \"git hooks\",\n\t\t\tmessage: stderr.trim(),\n\t\t\traw: stderr,\n\t\t});\n\t}\n\n\tdebug(\"parseHookErrors: found %d errors\", errors.length);\n\treturn errors;\n}\n\nfunction parseLintStagedErrors(output: string): HookError[] {\n\tconst errors: HookError[] = [];\n\tfor (const match of output.matchAll(/\\[FAILED\\]\\s+(.+?)\\s+\\[FAILED\\]/g)) {\n\t\tconst task = match[1].trim();\n\t\terrors.push({\n\t\t\ttool: \"lint-staged\",\n\t\t\tmessage: `Task failed: ${task}`,\n\t\t\traw: match[0],\n\t\t});\n\t}\n\n\treturn errors;\n}\n\nfunction parseBiomeErrors(output: string): HookError[] {\n\tconst errors: HookError[] = [];\n\tfor (const match of output.matchAll(/^(.+?):(\\d+):(\\d+)\\s+(.+)$/gm)) {\n\t\terrors.push({\n\t\t\ttool: \"biome\",\n\t\t\tmessage: `${match[1]}:${match[2]}:${match[3]} — ${match[4]}`,\n\t\t\traw: match[0],\n\t\t});\n\t}\n\n\tif (errors.length === 0 && output.includes(\"biome\")) {\n\t\terrors.push({\n\t\t\ttool: \"biome\",\n\t\t\tmessage: \"Biome check failed. See raw output for details.\",\n\t\t\traw: output,\n\t\t});\n\t}\n\n\treturn errors;\n}\n\nfunction parseTscErrors(output: string): HookError[] {\n\tconst errors: HookError[] = [];\n\tfor (const match of output.matchAll(/^(.+?)\\((\\d+),(\\d+)\\):\\s+error\\s+(TS\\d+):\\s+(.+)$/gm)) {\n\t\terrors.push({\n\t\t\ttool: \"tsc\",\n\t\t\tmessage: `${match[1]}:${match[2]}:${match[3]} — ${match[4]}: ${match[5]}`,\n\t\t\traw: match[0],\n\t\t});\n\t}\n\n\treturn errors;\n}\n\nfunction parseTestErrors(output: string): HookError[] {\n\tconst errors: HookError[] = [];\n\tconst failPattern = /FAIL\\s+(.+\\.(test|spec)\\..+)/;\n\tconst match = failPattern.exec(output);\n\n\tif (match) {\n\t\terrors.push({\n\t\t\ttool: output.includes(\"vitest\") ? \"vitest\" : \"jest\",\n\t\t\tmessage: `Test file failed: ${match[1]}`,\n\t\t\traw: output,\n\t\t});\n\t}\n\n\tif (errors.length === 0 && (output.includes(\"vitest\") || output.includes(\"jest\"))) {\n\t\terrors.push({\n\t\t\ttool: output.includes(\"vitest\") ? \"vitest\" : \"jest\",\n\t\t\tmessage: \"Tests failed. See raw output for details.\",\n\t\t\traw: output,\n\t\t});\n\t}\n\n\treturn errors;\n}\n\nfunction parseEslintErrors(output: string): HookError[] {\n\tconst errors: HookError[] = [];\n\tfor (const match of output.matchAll(/^\\s*\\d+:(\\d+)\\s+(error|warning)\\s+(.+?)\\s+(.+?)$/gm)) {\n\t\terrors.push({\n\t\t\ttool: \"eslint\",\n\t\t\tmessage: `${match[2]}: ${match[3]} (${match[4]})`,\n\t\t\traw: match[0],\n\t\t});\n\t}\n\n\treturn errors;\n}\n\nexport function formatErrorReport(errors: HookError[]): string {\n\tif (errors.length === 0) return \"\";\n\n\tconst sections = errors.map((e) => `[${e.tool}]\\n${e.message}`);\n\treturn sections.join(\"\\n\\n\");\n}\n\n// ── Tool check parsing (success case) ──────────────────────────────\n\nexport interface ToolCheck {\n\ttool: string;\n\tok: boolean;\n}\n\n/**\n * Parse lint-staged/hook stderr output to discover which tools ran\n * and whether they succeeded. Used for clean post-commit summary.\n */\nexport function parseToolChecks(stderr: string): ToolCheck[] {\n\tif (!stderr) return [];\n\n\tconst checks: ToolCheck[] = [];\n\t// Match [COMPLETED] and [FAILED] status lines from lint-staged\n\tfor (const match of stderr.matchAll(/\\[(COMPLETED|FAILED)\\]\\s+(.+)/g)) {\n\t\tconst status = match[1];\n\t\tconst command = match[2].trim();\n\n\t\tif (isLintStagedMeta(command)) continue;\n\n\t\tconst tool = extractToolName(command);\n\t\tif (!tool) continue;\n\n\t\tchecks.push({ tool, ok: status === \"COMPLETED\" });\n\t}\n\n\t// Deduplicate by tool name (keep last occurrence — final status)\n\tconst seen = new Map<string, ToolCheck>();\n\tfor (const c of checks) {\n\t\tseen.set(c.tool, c);\n\t}\n\n\treturn [...seen.values()];\n}\n\n/** Heuristic: skip lint-staged internal metadata lines */\nfunction isLintStagedMeta(command: string): boolean {\n\t// Glob patterns in task labels\n\tif (/[*{}[\\]]/.test(command)) return true;\n\t// Task count labels: \"src/ — 3 files\", \"src/ — no files\"\n\t// The dash can be em-dash (—), en-dash (–), or plain hyphen (-)\n\tif (/\\s[-–—]\\s(\\d+\\s)?files?$/.test(command)) return true;\n\tif (/\\s[-–—]\\sno\\s files$/.test(command)) return true;\n\t// Internal lint-staged lifecycle messages\n\tif (\n\t\t/^(Running tasks|Applying modifications|Cleaning up|Backing up|Backed up|Updating Git)/.test(\n\t\t\tcommand,\n\t\t)\n\t)\n\t\treturn true;\n\t// Ends with ellipsis (e.g. \"Backing up original state...\")\n\tif (/\\.{3}$/.test(command)) return true;\n\treturn false;\n}\n\n/** Extract a display-friendly tool name from a lint-staged command */\nfunction extractToolName(command: string): string | null {\n\tconst tokens = command.split(/\\s+/);\n\tconst first = tokens[0];\n\n\t// npm/yarn/pnpm run <script>\n\tif ([\"npm\", \"yarn\", \"pnpm\", \"bun\"].includes(first)) {\n\t\t// Skip \"run\" if present\n\t\tconst scriptIdx = tokens[1] === \"run\" ? 2 : 1;\n\t\tconst script = tokens[scriptIdx];\n\t\tif (!script) return null;\n\t\t// Map common script names to their underlying tool\n\t\tconst scriptMap: Record<string, string> = {\n\t\t\ttypecheck: \"tsc\",\n\t\t\tlint: \"eslint\",\n\t\t\tformat: \"prettier\",\n\t\t};\n\t\treturn scriptMap[script] ?? script;\n\t}\n\n\t// npx <tool>\n\tif (first === \"npx\") return tokens[1] ?? null;\n\n\t// Direct tool invocation (biome, eslint, tsc, vitest, jest, prettier)\n\treturn first;\n}\n","import { execa } from \"execa\";\n\nexport async function copyToClipboard(content: string): Promise<boolean> {\n\tconst commands: [string, string[]][] = [\n\t\t[\"wl-copy\", []],\n\t\t[\"xclip\", [\"-selection\", \"clipboard\"]],\n\t\t[\"xsel\", [\"--clipboard\", \"--input\"]],\n\t\t[\"pbcopy\", []],\n\t];\n\n\tfor (const [cmd, args] of commands) {\n\t\ttry {\n\t\t\tawait execa(cmd, args, { input: content });\n\t\t\treturn true;\n\t\t} catch {}\n\t}\n\treturn false;\n}\n","import * as p from \"@clack/prompts\";\nimport { bold, cyan, dim, green, red, yellow } from \"kolorist\";\nimport { copyToClipboard } from \"../services/clipboard.js\";\nimport type { ChangedFile } from \"../services/git.js\";\nimport type { HookError } from \"../services/hooks.js\";\nimport { debug } from \"../utils/debug.js\";\n\nexport interface StagingChoice {\n\tfiles: string[]; // selected file paths to stage\n\tall: boolean; // whether user chose \"Stage all\"\n}\n\nexport async function showStagingMenu(files: ChangedFile[]): Promise<StagingChoice | null> {\n\tdebug(\"showStagingMenu: %d files\", files.length);\n\n\t// Build status labels with kolorist colors\n\tconst statusLabel = (status: string): string => {\n\t\tswitch (status) {\n\t\t\tcase \"M\":\n\t\t\t\treturn yellow(\"M\");\n\t\t\tcase \"A\":\n\t\t\t\treturn green(\"A\");\n\t\t\tcase \"D\":\n\t\t\t\treturn red(\"D\");\n\t\t\tcase \"?\":\n\t\t\t\treturn dim(\"?\");\n\t\t\tdefault:\n\t\t\t\treturn dim(status);\n\t\t}\n\t};\n\n\tconst choice = await p.select({\n\t\tmessage: \"Stage files for commit:\",\n\t\toptions: [\n\t\t\t{\n\t\t\t\tlabel: \"Stage all files\",\n\t\t\t\tvalue: \"all\",\n\t\t\t\thint: `${files.length} file${files.length !== 1 ? \"s\" : \"\"}`,\n\t\t\t},\n\t\t\t{ label: \"Select files...\", value: \"select\" },\n\t\t\t{ label: \"Cancel\", value: \"cancel\" },\n\t\t],\n\t});\n\n\tif (p.isCancel(choice) || choice === \"cancel\") {\n\t\treturn null;\n\t}\n\n\tif (choice === \"all\") {\n\t\treturn { files: files.map((f) => f.path), all: true };\n\t}\n\n\t// Multi-select\n\tconst selected = await p.multiselect({\n\t\tmessage: \"Select files to stage:\",\n\t\toptions: files.map((f) => ({\n\t\t\tlabel: `${statusLabel(f.status)} ${f.path}`,\n\t\t\tvalue: f.path,\n\t\t})),\n\t\trequired: true,\n\t});\n\n\tif (p.isCancel(selected)) {\n\t\treturn null;\n\t}\n\n\treturn { files: selected as string[], all: false };\n}\n\nexport async function showRecoveryMenu(\n\terrors: HookError[],\n\tonRetry: () => Promise<boolean>,\n\tonSkipHooks: (message: string) => Promise<boolean>,\n\tonRestage: () => Promise<boolean>,\n\tmessage: string,\n\trawStderr: string,\n): Promise<void> {\n\tdebug(\"showRecoveryMenu: %d errors\", errors.length);\n\n\twhile (true) {\n\t\tp.note(\n\t\t\terrors.map((e) => ` ${red(\"•\")} [${e.tool}] ${e.message}`).join(\"\\n\"),\n\t\t\tred(bold(\"Pre-commit hook failed\")),\n\t\t);\n\n\t\tconst choice = await p.select({\n\t\t\tmessage: \"What do you want to do?\",\n\t\t\toptions: [\n\t\t\t\t{\n\t\t\t\t\tlabel: \"Copy error report to clipboard\",\n\t\t\t\t\tvalue: \"clipboard\",\n\t\t\t\t\thint: \"Paste into another terminal for an AI agent\",\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tlabel: \"Skip hooks and commit (--no-verify)\",\n\t\t\t\t\tvalue: \"skip\",\n\t\t\t\t\thint: \"Commit anyway, fix later\",\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tlabel: \"Re-stage files and retry\",\n\t\t\t\t\tvalue: \"restage\",\n\t\t\t\t\thint: \"Pick up fixes from another terminal\",\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tlabel: \"Edit commit message\",\n\t\t\t\t\tvalue: \"edit\",\n\t\t\t\t\thint: \"Modify the message before retrying\",\n\t\t\t\t},\n\t\t\t\t{ label: \"Cancel\", value: \"cancel\" },\n\t\t\t],\n\t\t});\n\n\t\tif (p.isCancel(choice)) {\n\t\t\tdebug(\"showRecoveryMenu: user cancelled\");\n\t\t\tp.outro(yellow(\"Cancelled. Message cached for --retry.\"));\n\t\t\tprocess.exit(1);\n\t\t\treturn;\n\t\t}\n\n\t\tdebug(\"showRecoveryMenu: user chose %s\", choice);\n\n\t\tswitch (choice) {\n\t\t\tcase \"clipboard\": {\n\t\t\t\tconst ok = await copyToClipboard(rawStderr);\n\t\t\t\tif (ok) {\n\t\t\t\t\tp.log.step(green(\"Errors copied\"));\n\t\t\t\t} else {\n\t\t\t\t\tp.log.warn(red(\"No clipboard tool found. Install xclip, wl-copy, or xsel.\"));\n\t\t\t\t}\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tcase \"skip\": {\n\t\t\t\tp.log.info(yellow(\"Committing with --no-verify...\"));\n\t\t\t\tconst ok = await onSkipHooks(message);\n\t\t\t\tif (ok) {\n\t\t\t\t\tp.outro(green(\"Committed (hooks skipped).\"));\n\t\t\t\t} else {\n\t\t\t\t\tp.outro(red(\"Commit failed even with --no-verify.\"));\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tcase \"restage\": {\n\t\t\t\tp.log.info(cyan(\"Re-staging and retrying...\"));\n\t\t\t\tconst ok = await onRestage();\n\t\t\t\tif (ok) {\n\t\t\t\t\tp.outro(green(\"Committed successfully.\"));\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\t// Loop back to menu on failure\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tcase \"edit\": {\n\t\t\t\tconst edited = await p.text({\n\t\t\t\t\tmessage: \"Edit commit message:\",\n\t\t\t\t\tinitialValue: message,\n\t\t\t\t\tvalidate: (v) => (v.trim() ? undefined : \"Message cannot be empty\"),\n\t\t\t\t});\n\t\t\t\tif (p.isCancel(edited)) {\n\t\t\t\t\tp.outro(yellow(\"Cancelled. Message cached for --retry.\"));\n\t\t\t\t\tprocess.exit(1);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tconst ok = await onRetry();\n\t\t\t\tif (ok) {\n\t\t\t\t\tp.outro(green(\"Committed successfully.\"));\n\t\t\t\t} else {\n\t\t\t\t\tp.outro(red(\"Commit failed again.\"));\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tcase \"cancel\": {\n\t\t\t\tp.outro(dim(\"Message cached for --retry.\"));\n\t\t\t\tprocess.exit(1);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t}\n}\n","import { createHash } from \"node:crypto\";\nimport { mkdir, readFile, writeFile } from \"node:fs/promises\";\nimport os from \"node:os\";\nimport { join } from \"node:path\";\nimport { debug } from \"./debug.js\";\n\nconst CACHE_DIR = join(os.homedir(), \".cache\", \"commit-mint\");\n\nfunction repoHash(repoPath: string): string {\n\treturn createHash(\"sha256\").update(repoPath).digest(\"hex\").slice(0, 12);\n}\n\nfunction cachePath(repoPath: string): string {\n\treturn join(CACHE_DIR, `${repoHash(repoPath)}.json`);\n}\n\nexport interface CachedCommit {\n\tmessage: string;\n\ttimestamp: number;\n\trepoPath: string;\n}\n\nexport async function saveCachedCommit(repoPath: string, message: string) {\n\tawait mkdir(CACHE_DIR, { recursive: true });\n\tconst data: CachedCommit = {\n\t\tmessage,\n\t\ttimestamp: Date.now(),\n\t\trepoPath,\n\t};\n\tconst path = cachePath(repoPath);\n\tdebug(\"saveCachedCommit: saving to %s\", path);\n\tawait writeFile(path, JSON.stringify(data, null, 2), \"utf8\");\n}\n\nexport async function loadCachedCommit(repoPath: string): Promise<CachedCommit | null> {\n\tconst path = cachePath(repoPath);\n\tdebug(\"loadCachedCommit: loading from %s\", path);\n\ttry {\n\t\tconst raw = await readFile(path, \"utf8\");\n\t\tconst data = JSON.parse(raw) as CachedCommit;\n\t\tdebug(\"loadCachedCommit: found message from %s\", new Date(data.timestamp).toISOString());\n\t\treturn data;\n\t} catch {\n\t\tdebug(\"loadCachedCommit: no cached commit found\");\n\t\treturn null;\n\t}\n}\n","import { intro, isCancel, log, outro, spinner } from \"@clack/prompts\";\nimport { bold, dim, green, red } from \"kolorist\";\nimport { generateCommitMessage } from \"../services/ai.js\";\nimport { getApiKey, readConfig, setConfigValue } from \"../services/config.js\";\nimport {\n\tassertGitRepo,\n\tattemptCommit,\n\tattemptCommitNoVerify,\n\tgetChangedFiles,\n\tgetDefaultExcludes,\n\tgetHead,\n\tgetStagedDiff,\n\tgetStatusShort,\n\tstageAll,\n\tstageFiles,\n} from \"../services/git.js\";\nimport { parseHookErrors, parseToolChecks } from \"../services/hooks.js\";\nimport { showRecoveryMenu, showStagingMenu } from \"../ui/menu.js\";\nimport { loadCachedCommit, saveCachedCommit } from \"../utils/cache.js\";\nimport { debug } from \"../utils/debug.js\";\n\ninterface CommitFlags {\n\tretry: boolean;\n\tall: boolean;\n\tmessage?: string;\n\thint?: string;\n}\n\nexport async function commitCommand(flags: CommitFlags) {\n\tdebug(\"commitCommand called\", { flags });\n\tawait assertGitRepo();\n\n\t// ── Retry mode ──────────────────────────────────────────────────\n\tif (flags.retry) {\n\t\tdebug(\"Entering retry mode\");\n\t\tconst { getRepoRoot } = await import(\"../services/git.js\");\n\t\tconst repoRoot = await getRepoRoot();\n\t\tdebug(\"Repo root:\", repoRoot);\n\t\tconst cached = await loadCachedCommit(repoRoot);\n\t\tif (!cached) {\n\t\t\tdebug(\"No cached commit found\");\n\t\t\toutro(red(\"No cached commit message found. Run cmint without --retry first.\"));\n\t\t\tprocess.exit(1);\n\t\t}\n\t\tdebug(\"Loaded cached message:\", cached.message);\n\t\tintro(\"commit-mint — retry\");\n\t\tconst s = spinner();\n\t\ts.start(\"Retrying commit...\");\n\t\tconst result = await attemptCommit(cached.message);\n\t\ts.stop(\"Attempted commit\");\n\t\tdebug(\"Retry commit result:\", result);\n\t\tif (result.ok) {\n\t\t\t// Show clean tool check summary\n\t\t\tconst checks = parseToolChecks(result.stderr ?? \"\");\n\t\t\tif (checks.length > 0) {\n\t\t\t\tconst lines = checks.map((c) => ` ${c.ok ? green(\"✓\") : red(\"✗\")} ${c.tool}`);\n\t\t\t\tlog.info(lines.join(\"\\n\"));\n\t\t\t}\n\t\t\toutro(green(\"Committed successfully.\"));\n\t\t} else {\n\t\t\tconst errors = parseHookErrors(result.stderr ?? \"\");\n\t\t\tdebug(\"Hook errors on retry:\", errors.length);\n\t\t\tawait showRecoveryMenu(\n\t\t\t\terrors,\n\t\t\t\tasync () => (await attemptCommit(cached.message)).ok,\n\t\t\t\tasync (msg) => (await attemptCommitNoVerify(msg)).ok,\n\t\t\t\tasync () => {\n\t\t\t\t\tawait stageAll();\n\t\t\t\t\treturn (await attemptCommit(cached.message)).ok;\n\t\t\t\t},\n\t\t\t\tcached.message,\n\t\t\t\tresult.stderr ?? \"\",\n\t\t\t);\n\t\t}\n\t\treturn;\n\t}\n\n\t// ── Normal mode ─────────────────────────────────────────────────\n\tintro(\"commit-mint\");\n\n\tconst status = await getStatusShort();\n\tdebug(\"Git status:\", status || \"(empty)\");\n\tif (!status) {\n\t\toutro(dim(\"Nothing to commit.\"));\n\t\treturn;\n\t}\n\n\t// Stage changes\n\tconst changedFiles = await getChangedFiles();\n\tdebug(\"Changed files:\", changedFiles.length);\n\tconst s = spinner();\n\n\ttry {\n\t\tif (flags.all) {\n\t\t\t// --all flag: auto-stage everything (original behavior)\n\t\t\ts.start(\"Staging all changes...\");\n\t\t\tawait stageAll();\n\t\t\ts.stop(\"Changes staged\");\n\t\t} else if (changedFiles.length === 1) {\n\t\t\t// Single file: auto-stage it\n\t\t\ts.start(`Staging ${changedFiles[0].path}...`);\n\t\t\tawait stageFiles([changedFiles[0].path]);\n\t\t\ts.stop(\"File staged\");\n\t\t} else {\n\t\t\t// Multiple files: show interactive staging menu\n\t\t\tconst stagingResult = await showStagingMenu(changedFiles);\n\t\t\tif (!stagingResult) {\n\t\t\t\toutro(dim(\"Cancelled.\"));\n\t\t\t\treturn;\n\t\t\t}\n\t\t\ts.start(\n\t\t\t\t`Staging ${stagingResult.files.length} file${stagingResult.files.length !== 1 ? \"s\" : \"\"}...`,\n\t\t\t);\n\t\t\tif (stagingResult.all) {\n\t\t\t\tawait stageAll();\n\t\t\t} else {\n\t\t\t\tawait stageFiles(stagingResult.files);\n\t\t\t}\n\t\t\ts.stop(\"Files staged\");\n\t\t}\n\t} catch (err) {\n\t\ts.stop(red(\"Staging failed.\"));\n\t\tconst msg = err instanceof Error ? err.message : String(err);\n\t\tdebug(\"Staging error:\", msg);\n\t\toutro(red(`Failed to stage files: ${msg}`));\n\t\tprocess.exit(1);\n\t}\n\n\t// Get diff for AI\n\tconst diffResult = await getStagedDiff();\n\tif (!diffResult) {\n\t\tdebug(\"No staged changes found after staging\");\n\t\toutro(red(\"No staged changes found.\"));\n\t\tprocess.exit(1);\n\t}\n\n\t// Handle all-staged-files-are-excluded case with hardcoded message\n\tif (\"excludedFiles\" in diffResult) {\n\t\tdebug(\"All staged files are excluded:\", diffResult.excludedFiles);\n\t\tconst message = buildExcludedFilesMessage(diffResult.excludedFiles);\n\n\t\tlog.info(diffResult.excludedFiles.map((f) => ` ${f}`).join(\"\\n\"));\n\n\t\t// Cache and commit with hardcoded message\n\t\tconst { getRepoRoot } = await import(\"../services/git.js\");\n\t\tconst repoRoot = await getRepoRoot();\n\t\tawait saveCachedCommit(repoRoot, message);\n\n\t\ts.start(\"Committing...\");\n\t\tconst headBefore = await getHead();\n\t\tconst result = await attemptCommit(message);\n\t\tconst headAfter = await getHead();\n\n\t\tif (result.ok || headBefore !== headAfter) {\n\t\t\ts.stop(\"Committed successfully.\");\n\t\t\toutro(green(\"Done.\"));\n\t\t\treturn;\n\t\t}\n\n\t\ts.stop(\"Commit failed.\");\n\t\tconst errors = parseHookErrors(result.stderr ?? \"\");\n\t\tawait showRecoveryMenu(\n\t\t\terrors,\n\t\t\tasync () => (await attemptCommit(message)).ok,\n\t\t\tasync (msg) => (await attemptCommitNoVerify(msg)).ok,\n\t\t\tasync () => {\n\t\t\t\tawait stageAll();\n\t\t\t\treturn (await attemptCommit(message)).ok;\n\t\t\t},\n\t\t\tmessage,\n\t\t\tresult.stderr ?? \"\",\n\t\t);\n\t\treturn;\n\t}\n\n\tdebug(\"Staged files:\", diffResult.files);\n\tdebug(\"Diff length:\", diffResult.diff.length, \"chars\");\n\n\tlog.info(diffResult.files.map((f) => ` ${f}`).join(\"\\n\"));\n\n\t// Generate or use provided message\n\tlet message: string;\n\n\tif (flags.message) {\n\t\tdebug(\"Using provided message:\", flags.message);\n\t\tmessage = flags.message;\n\t} else {\n\t\t// Ensure API key is available before generating\n\t\ttry {\n\t\t\tawait getApiKey();\n\t\t\tdebug(\"API key found\");\n\t\t} catch {\n\t\t\tdebug(\"No API key found, prompting user\");\n\t\t\tconst { text: promptText } = await import(\"@clack/prompts\");\n\t\t\tconst key = await promptText({\n\t\t\t\tmessage: \"Enter your Groq API key:\",\n\t\t\t\tplaceholder: \"gsk_...\",\n\t\t\t\tvalidate: (v: string) => (v.trim() ? undefined : \"API key is required\"),\n\t\t\t});\n\t\t\tif (isCancel(key)) {\n\t\t\t\toutro(dim(\"Cancelled.\"));\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tawait setConfigValue(\"GROQ_API_KEY\", String(key).trim());\n\t\t\tdebug(\"API key saved to config\");\n\t\t}\n\n\t\ts.start(\"Generating commit message...\");\n\t\ttry {\n\t\t\tconst genStart = Date.now();\n\t\t\tmessage = await generateMessage(diffResult.diff, flags.hint);\n\t\t\tdebug(\"generateMessage took %d ms\", Date.now() - genStart);\n\t\t\tdebug(\"Generated message:\", message);\n\t\t} catch (err) {\n\t\t\ts.stop(red(\"Failed to generate message.\"));\n\t\t\tdebug(\"Message generation failed:\", err instanceof Error ? err.message : String(err));\n\t\t\toutro(red(err instanceof Error ? err.message : String(err)));\n\t\t\treturn;\n\t\t}\n\t\ts.stop(\"Message generated\");\n\t}\n\n\t// Review message\n\tconst { select, text } = await import(\"@clack/prompts\");\n\tconst review = await select({\n\t\tmessage: `Review commit message:\\n\\n ${bold(message)}\\n`,\n\t\toptions: [\n\t\t\t{ label: \"Use as-is\", value: \"use\" },\n\t\t\t{ label: \"Edit\", value: \"edit\" },\n\t\t\t{ label: \"Cancel\", value: \"cancel\" },\n\t\t],\n\t});\n\n\tif (isCancel(review) || review === \"cancel\") {\n\t\tdebug(\"User cancelled at review step\");\n\t\toutro(dim(\"Cancelled.\"));\n\t\treturn;\n\t}\n\n\tif (review === \"edit\") {\n\t\tdebug(\"User chose to edit message\");\n\t\tconst edited = await text({\n\t\t\tmessage: \"Edit commit message:\",\n\t\t\tinitialValue: message,\n\t\t\tvalidate: (v: string) => (v.trim() ? undefined : \"Message cannot be empty\"),\n\t\t});\n\t\tif (isCancel(edited)) {\n\t\t\toutro(dim(\"Cancelled.\"));\n\t\t\treturn;\n\t\t}\n\t\tmessage = String(edited).trim();\n\t\tdebug(\"Edited message:\", message);\n\t}\n\n\t// Cache message before attempting commit\n\tconst { getRepoRoot } = await import(\"../services/git.js\");\n\tconst repoRoot = await getRepoRoot();\n\tawait saveCachedCommit(repoRoot, message);\n\tdebug(\"Message cached for repo:\", repoRoot);\n\n\t// Attempt commit\n\ts.start(\"Committing...\");\n\tconst headBefore = await getHead();\n\tdebug(\"HEAD before commit:\", headBefore);\n\tconst result = await attemptCommit(message);\n\tconst headAfter = await getHead();\n\tdebug(\"HEAD after commit:\", headAfter);\n\tdebug(\"Commit result:\", result);\n\n\tif (result.ok || headBefore !== headAfter) {\n\t\ts.stop(\"Committed successfully.\");\n\n\t\t// Show clean tool check summary\n\t\tconst checks = parseToolChecks(result.stderr ?? \"\");\n\t\tif (checks.length > 0) {\n\t\t\tconst lines = checks.map((c) => ` ${c.ok ? green(\"✓\") : red(\"✗\")} ${c.tool}`);\n\t\t\tlog.info(lines.join(\"\\n\"));\n\t\t}\n\n\t\toutro(green(\"Done.\"));\n\t\treturn;\n\t}\n\n\ts.stop(\"Commit failed.\");\n\tdebug(\"Commit failed, showing recovery menu\");\n\n\t// Hook failure — show recovery menu\n\tconst errors = parseHookErrors(result.stderr ?? \"\");\n\tdebug(\"Parsed hook errors:\", errors.length, \"errors\");\n\tawait showRecoveryMenu(\n\t\terrors,\n\t\tasync () => {\n\t\t\tconst r = await attemptCommit(message);\n\t\t\treturn r.ok;\n\t\t},\n\t\tasync (msg) => {\n\t\t\tconst r = await attemptCommitNoVerify(msg);\n\t\t\treturn r.ok;\n\t\t},\n\t\tasync () => {\n\t\t\tawait stageAll();\n\t\t\tconst r = await attemptCommit(message);\n\t\t\treturn r.ok;\n\t\t},\n\t\tmessage,\n\t\tresult.stderr ?? \"\",\n\t);\n}\n\nasync function generateMessage(diff: string, hint?: string): Promise<string> {\n\tconst config = await readConfig();\n\tconst apiKey = await getApiKey();\n\tdebug(\"Generating message with model:\", config.model, \"type:\", config.type);\n\n\treturn generateCommitMessage(diff, {\n\t\tapiKey,\n\t\tmodel: config.model,\n\t\ttype: config.type,\n\t\ttimeout: config.timeout ? parseInt(config.timeout, 10) : undefined,\n\t\thint,\n\t});\n}\n\nfunction buildExcludedFilesMessage(files: string[]): string {\n\tconst excludes = getDefaultExcludes();\n\tconst isLockfile = (f: string) =>\n\t\texcludes.some((pattern) => {\n\t\t\tif (pattern.endsWith(\".lock\") || pattern.endsWith(\".json\")) {\n\t\t\t\treturn f === pattern || f.endsWith(pattern.replace(\"*.\", \".\"));\n\t\t\t}\n\t\t\treturn false;\n\t\t});\n\n\tif (files.every(isLockfile)) {\n\t\treturn \"chore: update lockfile\";\n\t}\n\n\treturn \"chore: update generated files\";\n}\n","import { command } from \"cleye\";\nimport { getConfigValue, setConfigValue } from \"../services/config.js\";\n\nexport const configCommand = command(\n\t{\n\t\tname: \"config\",\n\t\tparameters: [\"<mode>\", \"<key=value...>\"],\n\t},\n\tasync (argv) => {\n\t\tconst { mode, keyValue } = argv._;\n\n\t\tif (mode === \"get\") {\n\t\t\tfor (const kv of keyValue) {\n\t\t\t\tconst key = kv.split(\"=\")[0];\n\t\t\t\tconst value = await getConfigValue(key);\n\t\t\t\tconsole.log(`${key}=${value ?? \"\"}`);\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\tif (mode === \"set\") {\n\t\t\tfor (const kv of keyValue) {\n\t\t\t\tconst [key, ...rest] = kv.split(\"=\");\n\t\t\t\tconst value = rest.join(\"=\");\n\t\t\t\tawait setConfigValue(key, value);\n\t\t\t}\n\t\t\tconsole.log(\"Config updated.\");\n\t\t\treturn;\n\t\t}\n\n\t\tconsole.error(`Unknown config mode: ${mode}. Use \"get\" or \"set\".`);\n\t\tprocess.exit(1);\n\t},\n);\n","#!/usr/bin/env node\nimport { cli } from \"cleye\";\nimport pkg from \"../package.json\" with { type: \"json\" };\n\nconst { version } = pkg;\n\nimport { commitCommand } from \"./commands/commit.js\";\nimport { configCommand } from \"./commands/config.js\";\nimport { setDebug } from \"./utils/debug.js\";\n\ncli(\n\t{\n\t\tname: \"cmint\",\n\t\tversion,\n\t\tdescription: \"A commit tool that actually handles hook failures\",\n\t\tflags: {\n\t\t\tretry: {\n\t\t\t\ttype: Boolean,\n\t\t\t\tdescription: \"Retry the last failed commit\",\n\t\t\t\talias: \"r\",\n\t\t\t\tdefault: false,\n\t\t\t},\n\t\t\tall: {\n\t\t\t\ttype: Boolean,\n\t\t\t\tdescription: \"Auto-stage all tracked files\",\n\t\t\t\talias: \"a\",\n\t\t\t\tdefault: false,\n\t\t\t},\n\t\t\tmessage: {\n\t\t\t\ttype: String,\n\t\t\t\tdescription: \"Provide a commit message directly (skip AI generation)\",\n\t\t\t\talias: \"m\",\n\t\t\t},\n\t\t\thint: {\n\t\t\t\ttype: String,\n\t\t\t\tdescription: \"Add context hint for AI commit message generation\",\n\t\t\t\talias: \"H\",\n\t\t\t},\n\t\t\tdebug: {\n\t\t\t\ttype: Boolean,\n\t\t\t\tdescription: \"Enable debug output\",\n\t\t\t\talias: \"d\",\n\t\t\t\tdefault: false,\n\t\t\t},\n\t\t},\n\t\tcommands: [configCommand],\n\t},\n\t(argv) => {\n\t\tsetDebug(argv.flags.debug);\n\t\tcommitCommand(argv.flags);\n\t},\n);\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kyubiware/commit-mint",
3
- "version": "0.1.5",
3
+ "version": "0.2.0",
4
4
  "description": "A commit tool that actually handles hook failures",
5
5
  "type": "module",
6
6
  "bin": {