@kyubiware/commit-mint 0.5.2 → 0.5.3

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/README.md CHANGED
@@ -1,11 +1,14 @@
1
- # 🌿 commit-mint
1
+ # commit-mint
2
2
 
3
3
  [![npm version](https://img.shields.io/npm/v/@kyubiware/commit-mint.svg)](https://www.npmjs.com/package/@kyubiware/commit-mint)
4
+ [![Build Status](https://img.shields.io/github/actions/workflow/status/kyubiware/commit-mint/ci.yml?style=flat-square)](https://github.com/kyubiware/commit-mint/actions)
5
+ ![Node.js](https://img.shields.io/badge/Node.js->=18-3c873a?style=flat-square)
6
+ [![License](https://img.shields.io/badge/License-MIT-blue?style=flat-square)](LICENSE)
4
7
 
5
8
  > Auto-group your changes into clean, conventional commits. AI handles the grouping, messages, and hook failures so you don't have to.
6
9
 
7
10
  ```
8
- 🌿 commit-mint
11
+ ┌ commit-mint
9
12
 
10
13
  ◇ Files analyzed
11
14
 
@@ -21,76 +24,34 @@
21
24
  │ ✓ eslint
22
25
  │ ✓ tsc
23
26
  │ ✓ vitest
24
- ◇ Message generated
25
-
26
- ● feat(text-study): add compound component support to ReadingInspectorPanel
27
-
28
- ◇ Committed successfully.
29
-
30
- ● ✓ prettier
31
- │ ✓ eslint
32
- │ ✓ tsc
33
- │ ✓ vitest
34
27
 
35
- ● Commit group 3 of 3: "Reading study list update"
28
+ ● Commit group 2 of 3: "Panel refactor"
36
29
  ◇ Message generated
37
30
 
38
- ● feat(reading-study-list-body): add compound component support to reading study list
31
+ ● feat(text-study): add compound component support to ReadingInspectorPanel
39
32
 
40
33
  ◇ Committed successfully.
41
34
 
42
- ● ✓ prettier
43
- │ ✓ eslint
44
- │ ✓ tsc
45
- │ ✓ vitest
46
-
47
35
  └ All groups committed.
48
36
  ```
49
37
 
50
- Requires **Node.js 18+**.
51
-
52
- ## Quick Start
38
+ ## Quick start
53
39
 
54
40
  ```bash
55
41
  npm install -g @kyubiware/commit-mint
56
- ```
57
-
58
- ```bash
59
42
  cmint -a
60
43
  ```
61
44
 
62
- On first run, you'll be prompted for a `GROQ_API_KEY` if it's not set in `~/.commit-mint` or as an environment variable. It's saved for future runs.
63
-
64
- ## Why commit-mint?
65
-
66
- - **Auto-group by intent.** AI reads your diff and groups files into logical commits. No more `feat: update stuff` that bundles unrelated changes.
67
- - **Zero-prompt auto mode.** `cmint -a` stages, groups, generates messages, and commits without a single prompt. Walk away and come back to clean history.
68
- - **Hook failures handled in-flow.** When pre-commit hooks fail, you get a parsed error summary and a menu to copy, skip, retry, or edit. No raw stderr dumps.
69
- - **Built-in pre-commit checks.** Define checks in a cmint config file and run them before AI generation. A failing check never wastes an API call.
70
- - **Message caching on failure.** A failed commit caches its message. Fix the error, run `cmint --retry`, and pick up exactly where you left off.
71
-
72
- ## Auto mode (`-a`)
73
-
74
- `cmint -a` is the primary way to use commit-mint. It runs the full pipeline with no prompts:
45
+ On first run, you'll be prompted for an API key if one isn't set. It's saved for future runs.
75
46
 
76
- 1. AI analyzes all changed files
77
- 2. Files are grouped into logical commits by intent
78
- 3. Each group gets its own AI-generated conventional commit message
79
- 4. Groups are committed sequentially
80
- 5. Hook failures per group trigger the recovery menu
47
+ ## Features
81
48
 
82
- Use this when you want clean history without the ceremony.
83
-
84
- ## Manual mode
85
-
86
- Run `cmint` without flags for the interactive flow:
87
-
88
- 1. **Staging menu** — stage all, select files, auto-group, or cancel
89
- 2. **File selection** — multi-select specific files if you don't want everything
90
- 3. **Message review** — review the AI-generated message before committing
91
- 4. **Commit attempt** — hooks run, recovery menu appears on failure
92
-
93
- If only one file changed, it's staged automatically.
49
+ - **Auto-group by intent** AI reads your diff and groups files into logical commits. No more `feat: update stuff` that bundles unrelated changes.
50
+ - **Zero-prompt auto mode** — `cmint -a` stages, groups, generates messages, and commits without a single prompt.
51
+ - **Hook failures handled in-flow** — Parsed error summary with a recovery menu to copy, skip, retry, or edit. No raw stderr dumps.
52
+ - **Multi-provider AI** — Works with Groq, Cerebras, and Mistral out of the box. Per-provider model configuration supported.
53
+ - **Built-in pre-commit checks** — Define checks in a cmint config file and run them before AI generation. A failing check never wastes an API call.
54
+ - **Message caching on failure** — A failed commit caches its message. Fix the error, run `cmint --retry`, and pick up where you left off.
94
55
 
95
56
  ## Usage
96
57
 
@@ -111,7 +72,7 @@ cmint -H "refactoring auth module"
111
72
  cmint -r
112
73
 
113
74
  # Skip pre-commit checks
114
- cmint -n
75
+ cmint -N
115
76
 
116
77
  # Debug mode
117
78
  cmint -d
@@ -119,6 +80,7 @@ cmint -d
119
80
  # Configuration
120
81
  cmint config get GROQ_API_KEY
121
82
  cmint config set GROQ_API_KEY=gsk_...
83
+ cmint config set provider cerebras
122
84
  cmint config set model openai/gpt-oss-20b
123
85
  ```
124
86
 
@@ -128,18 +90,48 @@ cmint config set model openai/gpt-oss-20b
128
90
  | `-m, --message` | Provide a commit message directly (skip AI) |
129
91
  | `-H, --hint` | Add context hint for AI message generation |
130
92
  | `-r, --retry` | Retry the last failed commit |
131
- | `-n, --noCheck` | Skip pre-commit checks |
93
+ | `-N, --noCheck` | Skip pre-commit checks |
132
94
  | `-d, --debug` | Enable debug output |
133
95
  | `-h, --help` | Show help |
134
96
  | `-v, --version` | Show version |
135
97
 
136
- ## Message review
98
+ ## Modes
137
99
 
138
- Before every commit, choose what to do with the generated message:
100
+ ### Auto mode (`-a`)
139
101
 
140
- - **Use as-is** accept the AI-generated message
141
- - **Edit** — modify the message in a prompt
142
- - **Cancel** exit (message is cached for `--retry`)
102
+ `cmint -a` runs the full pipeline with no prompts:
103
+
104
+ 1. AI analyzes all changed files
105
+ 2. Files are grouped into logical commits by intent
106
+ 3. Each group gets its own AI-generated conventional commit message
107
+ 4. Groups are committed sequentially
108
+ 5. Hook failures per group trigger the recovery menu
109
+
110
+ ### Manual mode
111
+
112
+ Run `cmint` without flags for the interactive flow:
113
+
114
+ 1. **Staging menu** — stage all, select files, auto-group, or run checks
115
+ 2. **File selection** — multi-select specific files if you don't want everything
116
+ 3. **Message review** — review the AI-generated message before committing
117
+ 4. **Commit attempt** — hooks run, recovery menu appears on failure
118
+
119
+ If only one file changed, it's staged automatically.
120
+
121
+ ## Providers
122
+
123
+ commit-mint supports multiple AI providers with per-provider configuration:
124
+
125
+ | Provider | Env key | Default model |
126
+ |----------|---------|---------------|
127
+ | **Groq** | `GROQ_API_KEY` | `openai/gpt-oss-20b` |
128
+ | **Cerebras** | `CEREBRAS_API_KEY` | `gpt-oss-120b` |
129
+ | **Mistral** | `MISTRAL_API_KEY` | `mistral-small` |
130
+
131
+ Switch providers with `cmint config set provider <name>`. Override the model per-provider with `cmint config set model_cerebras <model>`.
132
+
133
+ > [!TIP]
134
+ > All providers use OpenAI-compatible APIs. Groq uses the official SDK; Cerebras and Mistral use a built-in fetch client.
143
135
 
144
136
  ## Recovery menu
145
137
 
@@ -155,6 +147,7 @@ When a pre-commit hook blocks your commit, commit-mint parses the error output a
155
147
  │ What do you want to do? │
156
148
  │ │
157
149
  │ Copy error report to clipboard │
150
+ │ View full output │
158
151
  │ Skip hooks and commit (--no-verify) │
159
152
  │ Re-stage files and retry │
160
153
  │ Edit commit message │
@@ -165,21 +158,21 @@ When a pre-commit hook blocks your commit, commit-mint parses the error output a
165
158
  | Option | What it does |
166
159
  |--------|-------------|
167
160
  | **Copy error report** | Copies parsed, clean error output to clipboard |
161
+ | **View full output** | Shows raw stderr from the failed hook |
168
162
  | **Skip hooks** | Re-runs `git commit --no-verify` with the same message |
169
163
  | **Re-stage & retry** | Runs `git add -A` again, then retries the commit |
170
164
  | **Edit message** | Opens a prompt to modify the commit message, then retries |
171
165
  | **Cancel** | Exits. Commit message is cached for `cmint --retry` |
172
166
 
173
- The recovery menu also appears when cmint config pre-commit checks fail.
174
-
175
- commit-mint parses errors from **lint-staged**, **biome**, **TypeScript** (`tsc`), **vitest** / **jest**, and **ESLint**. Unrecognized output falls back to raw stderr.
167
+ Errors are parsed from **lint-staged**, **biome**, **TypeScript** (`tsc`), **vitest**/**jest**, and **ESLint**. Unrecognized output falls back to raw stderr.
176
168
 
177
169
  ## Pre-commit checks
178
170
 
179
171
  Define custom checks in a cmint config file at your project root. Supported file names (checked in priority order):
172
+
180
173
  `.cmintrc`, `.cmintrc.json`, `.cmintrc.{mjs,mts,js,ts,cjs,cts}`, `cmint.config.{mjs,mts,js,ts,cjs,cts}`
181
174
 
182
- They run after staging, before AI message generation, so a failing check never wastes an API call.
175
+ Checks run after staging, before AI message generation, so a failing check never wastes an API call.
183
176
 
184
177
  ```js
185
178
  export default {
@@ -195,7 +188,7 @@ Glob patterns use [picomatch](https://github.com/micromatch/picomatch). String c
195
188
 
196
189
  ### TypeScript config
197
190
 
198
- Use `.cmintrc.ts` or `cmint.config.ts` for type safety without adding commit-mint as a project dependency. Add an inline interface and use `satisfies`:
191
+ Use `.cmintrc.ts` or `cmint.config.ts` for type safety without adding commit-mint as a project dependency:
199
192
 
200
193
  ```ts
201
194
  interface Cmintrc {
@@ -208,36 +201,33 @@ export default {
208
201
  } satisfies Cmintrc;
209
202
  ```
210
203
 
211
- This gives you editor autocomplete and catches invalid values (e.g. numbers, objects) at edit time — no package install needed.
204
+ This gives you editor autocomplete and catches invalid values at edit time — no package install needed.
212
205
 
213
206
  Checks execute sequentially and fail fast. Each command has a 60-second timeout.
214
207
 
215
- ### When checks run
216
-
217
- - **Manual mode**: The staging menu shows a "Run checks" option when a cmint config file exists
218
- - **Auto-group mode**: Checks run automatically on all staged files before grouping
219
- - **Normal commit**: Checks run on staged files after staging, before diff/AI
220
-
221
208
  ### Check failure menu
222
209
 
223
- When a check fails, you get a menu with three options:
210
+ When a check fails, you get a menu with four options:
224
211
 
225
212
  | Option | What it does |
226
213
  |--------|-------------|
227
214
  | **Copy error report** | Copies the error output to clipboard |
215
+ | **View full output** | Shows the raw check output |
228
216
  | **Skip checks** | Proceeds without running checks |
229
217
  | **Cancel** | Exits. Message is cached for `cmint --retry` |
230
218
 
231
- Pass `--noCheck` or `-n` to skip checks entirely.
219
+ Pass `--noCheck` or `-N` to skip checks entirely.
232
220
 
233
221
  ## Configuration
234
222
 
235
- Stored in `~/.commit-mint` (INI format):
223
+ Stored in `~/.commit-mint` (INI format). Set values via `cmint config set <key> <value>`.
236
224
 
237
225
  ```ini
226
+ provider=groq
238
227
  GROQ_API_KEY=gsk_...
239
228
  model=openai/gpt-oss-20b
240
229
  locale=en
230
+ max-length=100
241
231
  type=conventional
242
232
  timeout=10000
243
233
  proxy=
@@ -245,69 +235,26 @@ proxy=
245
235
 
246
236
  | Key | Default | Description |
247
237
  |-----|---------|-------------|
248
- | `GROQ_API_KEY` | | Groq API key for AI message generation |
249
- | `model` | `openai/gpt-oss-20b` | AI model for commit message generation |
238
+ | `provider` | `groq` | AI provider (`groq`, `cerebras`, or `mistral`) |
239
+ | `GROQ_API_KEY` | | Groq API key (or set `GROQ_API_KEY` env var) |
240
+ | `CEREBRAS_API_KEY` | — | Cerebras API key (or set `CEREBRAS_API_KEY` env var) |
241
+ | `MISTRAL_API_KEY` | — | Mistral API key (or set `MISTRAL_API_KEY` env var) |
242
+ | `model` | `openai/gpt-oss-20b` | Default AI model for message generation |
243
+ | `model_groq` | — | Model override for Groq provider |
244
+ | `model_cerebras` | — | Model override for Cerebras provider |
245
+ | `model_mistral` | — | Model override for Mistral provider |
250
246
  | `locale` | `en` | Locale for generated messages |
247
+ | `max-length` | `100` | Maximum commit message length |
251
248
  | `type` | — | Commit type prefix (e.g. `conventional`) |
252
- | `timeout` | `10000` | AI request timeout (ms) |
249
+ | `timeout` | `10000` | AI request timeout in ms |
253
250
  | `proxy` | — | Proxy URL for API requests |
254
251
 
255
- You can also set `GROQ_API_KEY` via environment variable.
256
-
257
- ## CLI reference
258
-
259
- ```
260
- cmint --help
261
-
262
- cmint
263
-
264
- A commit tool that actually handles hook failures
265
-
266
- Options:
267
- --retry, -r Retry the last failed commit (default: false)
268
- --auto, -a Auto-group files into commits, accept all messages (default: false)
269
- --message, -m Provide a commit message directly (skip AI generation)
270
- --hint, -H Add context hint for AI commit message generation
271
- --noCheck, -n Skip pre-commit checks (default: false)
272
- --debug, -d Enable debug output (default: false)
273
- --help, -h Show help
274
- --version, -v Show version
275
-
276
- Commands:
277
- config Get/set configuration values
278
- ```
252
+ Model resolution follows a chain: `model_<provider>` → global `model` provider default. Per-provider keys prevent cross-provider model leaks when switching providers.
279
253
 
280
254
  ## Retry persistence
281
255
 
282
256
  Failed commit messages are cached to `~/.cache/commit-mint/<repo-hash>.json`. Running `cmint --retry` reuses the last message without regenerating. Fix errors in another terminal, come back, and retry.
283
257
 
284
- ## How it works
285
-
286
- ```
287
- commit-mint/
288
- ├── src/
289
- │ ├── cli.ts # Entry point, argument parsing (cleye)
290
- │ ├── commands/
291
- │ │ ├── commit.ts # Main commit flow orchestrator
292
- │ │ ├── auto-group.ts # Auto-group multi-commit flow
293
- │ │ └── config.ts # Config get/set subcommand
294
- │ ├── services/
295
- │ │ ├── git.ts # Git operations (stage, commit, diff, HEAD)
296
- │ │ ├── ai.ts # Groq AI commit message generation (3-tier diff compression)
297
- │ │ ├── grouping.ts # AI-powered file grouping into logical commits
298
- │ │ ├── hooks.ts # Hook error parser (lint-staged, biome, tsc, etc.)
299
- │ │ ├── checks.ts # Pre-commit checks via cmint config files (glob matching, command execution)
300
- │ │ ├── config.ts # INI config read/write at ~/.commit-mint
301
- │ │ └── clipboard.ts # Cross-platform clipboard (xclip/wl-copy/pbcopy)
302
- │ ├── ui/
303
- │ │ ├── menu.ts # Interactive recovery TUI + staging menu
304
- │ │ ├── grouping.ts # Grouping confirmation UI
305
- │ │ └── review-message.ts # Message review step (use/edit/cancel)
306
- │ └── utils/
307
- │ ├── cache.ts # Commit message persistence at ~/.cache/commit-mint/
308
- │ └── debug.ts # Timestamped debug logging to stderr
309
- ```
310
-
311
258
  ## Requirements
312
259
 
313
260
  - **Node.js 18+**
@@ -317,11 +264,7 @@ commit-mint/
317
264
 
318
265
  ## Non-goals
319
266
 
320
- - Not a git hook manager — cmint config checks run in-flow, not via git hooks. For git hooks, use husky or lefthook
321
- - Not a linter/formatter — use biome, eslint, prettier
322
- - Not a git TUI — use lazygit, gitui
323
- - Not a commitizen replacement — just generates conventional commit messages via AI
324
-
325
- ## License
326
-
327
- MIT © kyubiware
267
+ - Not a git hook manager — cmint config checks run in-flow, not via git hooks. For git hooks, use husky or lefthook.
268
+ - Not a linter/formatter — use biome, eslint, or prettier.
269
+ - Not a git TUI — use lazygit or gitui.
270
+ - Not a commitizen replacement — generates conventional commit messages via AI.
package/dist/cli.mjs CHANGED
@@ -28,7 +28,7 @@ var __exportAll = (all, no_symbols) => {
28
28
  //#region package.json
29
29
  var package_default = {
30
30
  name: "@kyubiware/commit-mint",
31
- version: "0.5.2",
31
+ version: "0.5.3",
32
32
  description: "🌿 A commit tool that actually handles hook failures",
33
33
  type: "module",
34
34
  bin: { "cmint": "./dist/cli.mjs" },
@@ -337,6 +337,50 @@ function parseEslintErrors(output) {
337
337
  return errors;
338
338
  }
339
339
  /**
340
+ * Parse cmint check output into structured errors.
341
+ * Check output uses [tool] prefix format (built by callers in auto-group.ts / staging.ts).
342
+ * This is distinct from parseHookErrors which handles raw git hook stderr.
343
+ */
344
+ function parseCheckErrors(output) {
345
+ if (!output.trim()) return [];
346
+ const errors = [];
347
+ const lines = output.split("\n");
348
+ let currentTool = "";
349
+ let currentLines = [];
350
+ const flush = () => {
351
+ if (currentTool && currentLines.length > 0) {
352
+ const message = currentLines.join("\n").trim();
353
+ if (message) errors.push({
354
+ tool: currentTool,
355
+ message,
356
+ raw: currentLines.join("\n")
357
+ });
358
+ }
359
+ currentTool = "";
360
+ currentLines = [];
361
+ };
362
+ for (const line of lines) {
363
+ const match = line.match(/^\[(\S+)\]\s*(.*)/);
364
+ if (match) {
365
+ flush();
366
+ currentTool = match[1];
367
+ const rest = match[2].trim();
368
+ if (rest) currentLines.push(rest);
369
+ } else if (currentTool) currentLines.push(line);
370
+ else currentLines.push(line);
371
+ }
372
+ flush();
373
+ if (errors.length === 0) {
374
+ const trimmed = output.trim();
375
+ if (trimmed) errors.push({
376
+ tool: "checks",
377
+ message: trimmed,
378
+ raw: output
379
+ });
380
+ }
381
+ return errors;
382
+ }
383
+ /**
340
384
  * Parse lint-staged/hook stderr output to discover which tools ran
341
385
  * and whether they succeeded. Used for clean post-commit summary.
342
386
  */
@@ -1569,35 +1613,44 @@ async function runAutoGroupFlow(changedFiles, flags) {
1569
1613
  if (excluded.length > 0) {
1570
1614
  debug("Committing %d excluded files upfront:", excluded.length, excluded);
1571
1615
  const message = buildExcludedFilesMessage(excluded);
1616
+ log.info(excluded.map((f) => ` ${f}`).join("\n"));
1572
1617
  await resetStaging();
1573
1618
  await stageFiles(excluded);
1574
1619
  const headBefore = await getHead();
1575
1620
  const commitResult = await attemptCommit(message);
1576
1621
  const headAfter = await getHead();
1577
- if (commitResult.ok || headBefore !== headAfter) debug("Excluded files committed:", message);
1578
- else debug("Excluded files commit failed, continuing without them");
1622
+ if (commitResult.ok || headBefore !== headAfter) {
1623
+ debug("Excluded files committed:", message);
1624
+ log.success(dim(message));
1625
+ } else {
1626
+ debug("Excluded files commit failed, continuing without them");
1627
+ log.warn(red("Failed to commit excluded files."));
1628
+ }
1579
1629
  }
1580
1630
  if (included.length === 0) {
1581
1631
  debug("No included files to group, done");
1582
- outro(green("Done."));
1632
+ if (excluded.length > 0) outro(green("Committed excluded files. No other changes to group."));
1633
+ else outro(dim("Nothing to commit."));
1583
1634
  return "committed";
1584
1635
  }
1585
1636
  if (!flags.noCheck) {
1586
1637
  const { getRepoRoot } = await Promise.resolve().then(() => git_exports);
1587
1638
  const repoRoot = await getRepoRoot();
1588
1639
  const allFiles = included.filter((f) => f.status !== "D").map((f) => f.path);
1589
- debug("Running user checks on %d files...", allFiles.length);
1590
- const ck = spinner();
1591
- ck.start("Running checks...");
1592
- const checkResults = await runAllChecks(repoRoot, allFiles, 6e4);
1593
- debug("Check results: ok=%s, count=%d", checkResults.ok, checkResults.results.length);
1594
- if (!checkResults.ok) {
1595
- ck.stop(`${checkResults.results.filter((r) => !r.ok).length} check(s) failed`);
1596
- const rawStderr = checkResults.results.filter((r) => !r.ok).map((r) => `[${r.tool}] ${r.stderr}`).join("\n");
1597
- if (await showCheckFailureMenu(parseHookErrors(rawStderr), rawStderr) === "cancelled") return "cancelled";
1598
- } else {
1599
- ck.stop("All checks passed");
1600
- if (checkResults.results.length > 0) log.info(checkResults.results.map((r) => ` ${green("✓")} ${r.tool}`).join("\n"));
1640
+ if (await detectConfig(repoRoot)) {
1641
+ debug("Running user checks on %d files...", allFiles.length);
1642
+ const ck = spinner();
1643
+ ck.start("Running checks...");
1644
+ const checkResults = await runAllChecks(repoRoot, allFiles, 6e4);
1645
+ debug("Check results: ok=%s, count=%d", checkResults.ok, checkResults.results.length);
1646
+ if (!checkResults.ok) {
1647
+ ck.stop(`${checkResults.results.filter((r) => !r.ok).length} check(s) failed`);
1648
+ const rawOutput = checkResults.results.filter((r) => !r.ok).map((r) => `[${r.tool}]\n${r.stdout}\n${r.stderr}`.trim()).join("\n\n");
1649
+ if (await showCheckFailureMenu(parseCheckErrors(rawOutput), rawOutput) === "cancelled") return "cancelled";
1650
+ } else {
1651
+ ck.stop("All checks passed");
1652
+ if (checkResults.results.length > 0) log.info(checkResults.results.map((r) => ` ${green("✓")} ${r.tool}`).join("\n"));
1653
+ }
1601
1654
  }
1602
1655
  }
1603
1656
  const config = await readConfig();
@@ -1796,16 +1849,19 @@ async function handleStaging(changedFiles, flags) {
1796
1849
  }
1797
1850
  if (stagingResult === "checks") {
1798
1851
  await stageAll();
1799
- const ckSpinner = spinner();
1800
- ckSpinner.start("Running checks...");
1801
- const ckResult = await runAllChecks(repoRoot, currentFiles.filter((f) => f.status !== "D").map((f) => f.path), 6e4);
1802
- if (ckResult.ok) {
1803
- ckSpinner.stop("All checks passed");
1804
- for (const r of ckResult.results) if (r.stdout.trim()) log.info(dim(r.stdout.trim()));
1805
- } else {
1806
- const failed = ckResult.results.filter((r) => !r.ok);
1807
- ckSpinner.stop(`${failed.length} check${failed.length !== 1 ? "s" : ""} failed`);
1808
- for (const r of failed) log.info(r.stderr?.trim() || r.stdout?.trim() || `Check failed: ${r.command}`);
1852
+ const allFiles = currentFiles.filter((f) => f.status !== "D").map((f) => f.path);
1853
+ if (await detectConfig(repoRoot)) {
1854
+ const ckSpinner = spinner();
1855
+ ckSpinner.start("Running checks...");
1856
+ const ckResult = await runAllChecks(repoRoot, allFiles, 6e4);
1857
+ if (ckResult.ok) {
1858
+ ckSpinner.stop("All checks passed");
1859
+ for (const r of ckResult.results) if (r.stdout.trim()) log.info(dim(r.stdout.trim()));
1860
+ } else {
1861
+ const failed = ckResult.results.filter((r) => !r.ok);
1862
+ ckSpinner.stop(`${failed.length} check${failed.length !== 1 ? "s" : ""} failed`);
1863
+ for (const r of failed) log.info(r.stderr?.trim() || r.stdout?.trim() || `Check failed: ${r.command}`);
1864
+ }
1809
1865
  }
1810
1866
  currentFiles = await getChangedFiles();
1811
1867
  continue;
@@ -1844,8 +1900,8 @@ async function runPreCommitChecks(changedFiles, noCheck) {
1844
1900
  const checkResults = await runAllChecks(checkRoot, stagedFileList, 6e4);
1845
1901
  debug("Check results: ok=%s, count=%d", checkResults.ok, checkResults.results.length);
1846
1902
  if (!checkResults.ok) {
1847
- const rawStderr = checkResults.results.filter((r) => !r.ok).map((r) => `[${r.tool}] ${r.stderr}`).join("\n");
1848
- if (await showCheckFailureMenu(parseHookErrors(rawStderr), rawStderr) === "cancelled") process.exit(1);
1903
+ const rawOutput = checkResults.results.filter((r) => !r.ok).map((r) => `[${r.tool}]\n${r.stdout}\n${r.stderr}`.trim()).join("\n\n");
1904
+ if (await showCheckFailureMenu(parseCheckErrors(rawOutput), rawOutput) === "cancelled") process.exit(1);
1849
1905
  }
1850
1906
  }
1851
1907
  //#endregion