@kyubiware/commit-mint 0.1.5 → 0.2.1
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 +140 -22
- package/dist/cli.mjs.map +1 -1
- package/package.json +1 -1
package/dist/cli.mjs
CHANGED
|
@@ -9,6 +9,7 @@ import os from "node:os";
|
|
|
9
9
|
import { join } from "node:path";
|
|
10
10
|
import ini from "ini";
|
|
11
11
|
import { execa } from "execa";
|
|
12
|
+
import { spawn } from "node:child_process";
|
|
12
13
|
import { createHash } from "node:crypto";
|
|
13
14
|
//#region \0rolldown/runtime.js
|
|
14
15
|
var __defProp = Object.defineProperty;
|
|
@@ -25,7 +26,7 @@ var __exportAll = (all, no_symbols) => {
|
|
|
25
26
|
//#region package.json
|
|
26
27
|
var package_default = {
|
|
27
28
|
name: "@kyubiware/commit-mint",
|
|
28
|
-
version: "0.1
|
|
29
|
+
version: "0.2.1",
|
|
29
30
|
description: "A commit tool that actually handles hook failures",
|
|
30
31
|
type: "module",
|
|
31
32
|
bin: { "cmint": "./dist/cli.mjs" },
|
|
@@ -173,10 +174,6 @@ function buildUserPrompt(diff, hint, statSummary) {
|
|
|
173
174
|
function isValidConventionalCommit(message) {
|
|
174
175
|
return CONVENTIONAL_COMMIT_REGEX.test(message);
|
|
175
176
|
}
|
|
176
|
-
function enforceMaxLength(message, maxLength) {
|
|
177
|
-
if (!maxLength || message.length <= maxLength) return message;
|
|
178
|
-
return `${message.slice(0, maxLength - 3)}...`;
|
|
179
|
-
}
|
|
180
177
|
function extractContentText(content) {
|
|
181
178
|
if (content == null) return "";
|
|
182
179
|
if (typeof content === "string") return content.trim();
|
|
@@ -184,7 +181,7 @@ function extractContentText(content) {
|
|
|
184
181
|
return "";
|
|
185
182
|
}
|
|
186
183
|
async function generateCommitMessage(diff, options) {
|
|
187
|
-
debug("generateCommitMessage: model=%s,
|
|
184
|
+
debug("generateCommitMessage: model=%s, type=%s, hint=%s", options.model ?? "default", options.type ?? "none", options.hint ?? "none");
|
|
188
185
|
const timeoutMs = options.timeout ?? 6e4;
|
|
189
186
|
debug("Timeout: %d ms", timeoutMs);
|
|
190
187
|
const client = new Groq({
|
|
@@ -253,9 +250,8 @@ async function generateCommitMessage(diff, options) {
|
|
|
253
250
|
message = retryMessage;
|
|
254
251
|
} else debug("Retry also failed validation, using original message");
|
|
255
252
|
}
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
return result;
|
|
253
|
+
debug("Final message (%d ms total): %s", Date.now() - totalStart, message);
|
|
254
|
+
return message;
|
|
259
255
|
} catch (error) {
|
|
260
256
|
debug("AI error: %s", error instanceof Error ? error.message : String(error));
|
|
261
257
|
if (error instanceof Groq.AuthenticationError) throw new Error("Invalid GROQ_API_KEY. Run: cmint config set GROQ_API_KEY=<key>");
|
|
@@ -323,12 +319,14 @@ var git_exports = /* @__PURE__ */ __exportAll({
|
|
|
323
319
|
assertGitRepo: () => assertGitRepo,
|
|
324
320
|
attemptCommit: () => attemptCommit,
|
|
325
321
|
attemptCommitNoVerify: () => attemptCommitNoVerify,
|
|
322
|
+
getChangedFiles: () => getChangedFiles,
|
|
326
323
|
getDefaultExcludes: () => getDefaultExcludes,
|
|
327
324
|
getHead: () => getHead,
|
|
328
325
|
getRepoRoot: () => getRepoRoot,
|
|
329
326
|
getStagedDiff: () => getStagedDiff,
|
|
330
327
|
getStatusShort: () => getStatusShort,
|
|
331
|
-
stageAll: () => stageAll
|
|
328
|
+
stageAll: () => stageAll,
|
|
329
|
+
stageFiles: () => stageFiles
|
|
332
330
|
});
|
|
333
331
|
var KnownError = class extends Error {};
|
|
334
332
|
async function assertGitRepo() {
|
|
@@ -406,6 +404,20 @@ async function getStatusShort() {
|
|
|
406
404
|
const { stdout } = await execa("git", ["status", "--short"]);
|
|
407
405
|
return stdout.trim();
|
|
408
406
|
}
|
|
407
|
+
async function getChangedFiles() {
|
|
408
|
+
const { stdout } = await execa("git", ["status", "--short"]);
|
|
409
|
+
if (!stdout.trim()) return [];
|
|
410
|
+
const files = stdout.split("\n").filter(Boolean).map((line) => ({
|
|
411
|
+
status: line.slice(0, 2).trim(),
|
|
412
|
+
path: line.slice(3)
|
|
413
|
+
}));
|
|
414
|
+
debug("getChangedFiles:", files.length, "files");
|
|
415
|
+
return files;
|
|
416
|
+
}
|
|
417
|
+
async function stageFiles(paths) {
|
|
418
|
+
debug("stageFiles:", paths);
|
|
419
|
+
await execa("git", ["add", ...paths]);
|
|
420
|
+
}
|
|
409
421
|
async function attemptCommit(message, extraArgs = []) {
|
|
410
422
|
debug("attemptCommit:", message, extraArgs.length ? extraArgs : "(no extra args)");
|
|
411
423
|
try {
|
|
@@ -585,24 +597,102 @@ async function copyToClipboard(content) {
|
|
|
585
597
|
["xsel", ["--clipboard", "--input"]],
|
|
586
598
|
["pbcopy", []]
|
|
587
599
|
]) try {
|
|
588
|
-
await
|
|
589
|
-
|
|
600
|
+
if (await new Promise((resolve) => {
|
|
601
|
+
const child = spawn(cmd, args, { stdio: [
|
|
602
|
+
"pipe",
|
|
603
|
+
"ignore",
|
|
604
|
+
"ignore"
|
|
605
|
+
] });
|
|
606
|
+
let settled = false;
|
|
607
|
+
const done = (result) => {
|
|
608
|
+
if (settled) return;
|
|
609
|
+
settled = true;
|
|
610
|
+
resolve(result);
|
|
611
|
+
};
|
|
612
|
+
child.on("error", () => done(false));
|
|
613
|
+
child.on("exit", (code) => {
|
|
614
|
+
if (code !== 0) done(false);
|
|
615
|
+
});
|
|
616
|
+
child.stdin.write(content, (err) => {
|
|
617
|
+
if (err) {
|
|
618
|
+
done(false);
|
|
619
|
+
return;
|
|
620
|
+
}
|
|
621
|
+
child.stdin.end(() => {
|
|
622
|
+
child.unref();
|
|
623
|
+
done(true);
|
|
624
|
+
});
|
|
625
|
+
});
|
|
626
|
+
})) return true;
|
|
590
627
|
} catch {}
|
|
591
628
|
return false;
|
|
592
629
|
}
|
|
593
630
|
//#endregion
|
|
594
631
|
//#region src/ui/menu.ts
|
|
632
|
+
async function showStagingMenu(files) {
|
|
633
|
+
debug("showStagingMenu: %d files", files.length);
|
|
634
|
+
const statusLabel = (status) => {
|
|
635
|
+
switch (status) {
|
|
636
|
+
case "M": return yellow("M");
|
|
637
|
+
case "A": return green("A");
|
|
638
|
+
case "D": return red("D");
|
|
639
|
+
case "?": return dim("?");
|
|
640
|
+
default: return dim(status);
|
|
641
|
+
}
|
|
642
|
+
};
|
|
643
|
+
const choice = await p.select({
|
|
644
|
+
message: "Stage files for commit:",
|
|
645
|
+
options: [
|
|
646
|
+
{
|
|
647
|
+
label: "Stage all files",
|
|
648
|
+
value: "all",
|
|
649
|
+
hint: `${files.length} file${files.length !== 1 ? "s" : ""}`
|
|
650
|
+
},
|
|
651
|
+
{
|
|
652
|
+
label: "Select files...",
|
|
653
|
+
value: "select"
|
|
654
|
+
},
|
|
655
|
+
{
|
|
656
|
+
label: "Cancel",
|
|
657
|
+
value: "cancel"
|
|
658
|
+
}
|
|
659
|
+
]
|
|
660
|
+
});
|
|
661
|
+
if (p.isCancel(choice) || choice === "cancel") return null;
|
|
662
|
+
if (choice === "all") return {
|
|
663
|
+
files: files.map((f) => f.path),
|
|
664
|
+
all: true
|
|
665
|
+
};
|
|
666
|
+
const selected = await p.multiselect({
|
|
667
|
+
message: "Select files to stage:",
|
|
668
|
+
options: files.map((f) => ({
|
|
669
|
+
label: `${statusLabel(f.status)} ${f.path}`,
|
|
670
|
+
value: f.path
|
|
671
|
+
})),
|
|
672
|
+
required: true
|
|
673
|
+
});
|
|
674
|
+
if (p.isCancel(selected)) return null;
|
|
675
|
+
return {
|
|
676
|
+
files: selected,
|
|
677
|
+
all: false
|
|
678
|
+
};
|
|
679
|
+
}
|
|
595
680
|
async function showRecoveryMenu(errors, onRetry, onSkipHooks, onRestage, message, rawStderr) {
|
|
596
681
|
debug("showRecoveryMenu: %d errors", errors.length);
|
|
682
|
+
let clipboardCopied = false;
|
|
683
|
+
let showNote = true;
|
|
597
684
|
while (true) {
|
|
598
|
-
|
|
685
|
+
if (showNote) {
|
|
686
|
+
p.note(errors.map((e) => ` ${red("•")} [${e.tool}] ${e.message}`).join("\n"), red(bold("Pre-commit hook failed")));
|
|
687
|
+
showNote = false;
|
|
688
|
+
}
|
|
599
689
|
const choice = await p.select({
|
|
600
690
|
message: "What do you want to do?",
|
|
601
691
|
options: [
|
|
602
692
|
{
|
|
603
|
-
label: "Copy error report to clipboard",
|
|
693
|
+
label: clipboardCopied ? `${green("✓")} Copy error report to clipboard` : "Copy error report to clipboard",
|
|
604
694
|
value: "clipboard",
|
|
605
|
-
hint: "Paste into another terminal for an AI agent"
|
|
695
|
+
hint: clipboardCopied ? "Copied!" : "Paste into another terminal for an AI agent"
|
|
606
696
|
},
|
|
607
697
|
{
|
|
608
698
|
label: "Skip hooks and commit (--no-verify)",
|
|
@@ -634,8 +724,10 @@ async function showRecoveryMenu(errors, onRetry, onSkipHooks, onRestage, message
|
|
|
634
724
|
debug("showRecoveryMenu: user chose %s", choice);
|
|
635
725
|
switch (choice) {
|
|
636
726
|
case "clipboard":
|
|
637
|
-
if (await copyToClipboard(rawStderr))
|
|
638
|
-
|
|
727
|
+
if (await copyToClipboard(rawStderr)) {
|
|
728
|
+
clipboardCopied = true;
|
|
729
|
+
p.log.step(green("Copied to clipboard."));
|
|
730
|
+
} else p.log.warn(red("No clipboard tool found. Install xclip, wl-copy, or xsel."));
|
|
639
731
|
continue;
|
|
640
732
|
case "skip":
|
|
641
733
|
p.log.info(yellow("Committing with --no-verify..."));
|
|
@@ -648,6 +740,7 @@ async function showRecoveryMenu(errors, onRetry, onSkipHooks, onRestage, message
|
|
|
648
740
|
p.outro(green("Committed successfully."));
|
|
649
741
|
return;
|
|
650
742
|
}
|
|
743
|
+
showNote = true;
|
|
651
744
|
continue;
|
|
652
745
|
case "edit": {
|
|
653
746
|
const edited = await p.text({
|
|
@@ -751,10 +844,36 @@ async function commitCommand(flags) {
|
|
|
751
844
|
outro(dim("Nothing to commit."));
|
|
752
845
|
return;
|
|
753
846
|
}
|
|
847
|
+
const changedFiles = await getChangedFiles();
|
|
848
|
+
debug("Changed files:", changedFiles.length);
|
|
754
849
|
const s = spinner();
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
850
|
+
try {
|
|
851
|
+
if (flags.all) {
|
|
852
|
+
s.start("Staging all changes...");
|
|
853
|
+
await stageAll();
|
|
854
|
+
s.stop("Changes staged");
|
|
855
|
+
} else if (changedFiles.length === 1) {
|
|
856
|
+
s.start(`Staging ${changedFiles[0].path}...`);
|
|
857
|
+
await stageFiles([changedFiles[0].path]);
|
|
858
|
+
s.stop("File staged");
|
|
859
|
+
} else {
|
|
860
|
+
const stagingResult = await showStagingMenu(changedFiles);
|
|
861
|
+
if (!stagingResult) {
|
|
862
|
+
outro(dim("Cancelled."));
|
|
863
|
+
return;
|
|
864
|
+
}
|
|
865
|
+
s.start(`Staging ${stagingResult.files.length} file${stagingResult.files.length !== 1 ? "s" : ""}...`);
|
|
866
|
+
if (stagingResult.all) await stageAll();
|
|
867
|
+
else await stageFiles(stagingResult.files);
|
|
868
|
+
s.stop("Files staged");
|
|
869
|
+
}
|
|
870
|
+
} catch (err) {
|
|
871
|
+
s.stop(red("Staging failed."));
|
|
872
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
873
|
+
debug("Staging error:", msg);
|
|
874
|
+
outro(red(`Failed to stage files: ${msg}`));
|
|
875
|
+
process.exit(1);
|
|
876
|
+
}
|
|
758
877
|
const diffResult = await getStagedDiff();
|
|
759
878
|
if (!diffResult) {
|
|
760
879
|
debug("No staged changes found after staging");
|
|
@@ -897,11 +1016,10 @@ async function commitCommand(flags) {
|
|
|
897
1016
|
async function generateMessage(diff, hint) {
|
|
898
1017
|
const config = await readConfig();
|
|
899
1018
|
const apiKey = await getApiKey();
|
|
900
|
-
debug("Generating message with model:", config.model, "
|
|
1019
|
+
debug("Generating message with model:", config.model, "type:", config.type);
|
|
901
1020
|
return generateCommitMessage(diff, {
|
|
902
1021
|
apiKey,
|
|
903
1022
|
model: config.model,
|
|
904
|
-
maxLength: config["max-length"] ? parseInt(config["max-length"], 10) : void 0,
|
|
905
1023
|
type: config.type,
|
|
906
1024
|
timeout: config.timeout ? parseInt(config.timeout, 10) : void 0,
|
|
907
1025
|
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 { spawn } from \"node:child_process\";\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\tconst success = await new Promise<boolean>((resolve) => {\n\t\t\t\tconst child = spawn(cmd, args, {\n\t\t\t\t\tstdio: [\"pipe\", \"ignore\", \"ignore\"],\n\t\t\t\t});\n\n\t\t\t\tlet settled = false;\n\t\t\t\tconst done = (result: boolean) => {\n\t\t\t\t\tif (settled) return;\n\t\t\t\t\tsettled = true;\n\t\t\t\t\tresolve(result);\n\t\t\t\t};\n\n\t\t\t\t// Command not found (ENOENT)\n\t\t\t\tchild.on(\"error\", () => done(false));\n\t\t\t\t// Tool failed immediately (e.g. wl-copy on non-Wayland)\n\t\t\t\tchild.on(\"exit\", (code) => {\n\t\t\t\t\tif (code !== 0) done(false);\n\t\t\t\t});\n\n\t\t\t\tchild.stdin.write(content, (err) => {\n\t\t\t\t\tif (err) {\n\t\t\t\t\t\tdone(false);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tchild.stdin.end(() => {\n\t\t\t\t\t\t// Content sent to tool — don't wait for exit since\n\t\t\t\t\t\t// clipboard tools (xclip, wl-copy) hold selection open\n\t\t\t\t\t\tchild.unref();\n\t\t\t\t\t\tdone(true);\n\t\t\t\t\t});\n\t\t\t\t});\n\t\t\t});\n\n\t\t\tif (success) return 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\tlet clipboardCopied = false;\n\tlet showNote = true;\n\n\twhile (true) {\n\t\tif (showNote) {\n\t\t\tp.note(\n\t\t\t\terrors.map((e) => ` ${red(\"•\")} [${e.tool}] ${e.message}`).join(\"\\n\"),\n\t\t\t\tred(bold(\"Pre-commit hook failed\")),\n\t\t\t);\n\t\t\tshowNote = false;\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: clipboardCopied\n\t\t\t\t\t\t? `${green(\"✓\")} Copy error report to clipboard`\n\t\t\t\t\t\t: \"Copy error report to clipboard\",\n\t\t\t\t\tvalue: \"clipboard\",\n\t\t\t\t\thint: clipboardCopied ? \"Copied!\" : \"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\tclipboardCopied = true;\n\t\t\t\t\tp.log.step(green(\"Copied to clipboard.\"));\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// Re-show errors after failed restage for context\n\t\t\t\tshowNote = true;\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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"}
|