@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 +82 -139
- package/dist/cli.mjs +84 -28
- package/dist/cli.mjs.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
|
-
#
|
|
1
|
+
# commit-mint
|
|
2
2
|
|
|
3
3
|
[](https://www.npmjs.com/package/@kyubiware/commit-mint)
|
|
4
|
+
[](https://github.com/kyubiware/commit-mint/actions)
|
|
5
|
+

|
|
6
|
+
[](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
|
-
┌
|
|
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
|
|
28
|
+
● Commit group 2 of 3: "Panel refactor"
|
|
36
29
|
◇ Message generated
|
|
37
30
|
│
|
|
38
|
-
● feat(
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
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 -
|
|
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
|
-
| `-
|
|
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
|
-
##
|
|
98
|
+
## Modes
|
|
137
99
|
|
|
138
|
-
|
|
100
|
+
### Auto mode (`-a`)
|
|
139
101
|
|
|
140
|
-
-
|
|
141
|
-
|
|
142
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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 `-
|
|
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
|
-
| `
|
|
249
|
-
| `
|
|
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
|
|
249
|
+
| `timeout` | `10000` | AI request timeout in ms |
|
|
253
250
|
| `proxy` | — | Proxy URL for API requests |
|
|
254
251
|
|
|
255
|
-
|
|
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
|
|
323
|
-
- Not a commitizen replacement —
|
|
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.
|
|
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)
|
|
1578
|
-
|
|
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("
|
|
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
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
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
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
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
|
|
1848
|
-
if (await showCheckFailureMenu(
|
|
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
|