@synmux/claude-commit 0.1.0 → 0.1.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/README.md +8 -8
- package/package.json +1 -1
- package/src/agent.ts +1 -1
- package/src/config.ts +1 -1
- package/src/diff.ts +1 -1
- package/src/git.ts +2 -2
- package/src/prompts.ts +3 -3
- package/src/ui/interactive.ts +4 -4
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# `claude-commit`
|
|
2
2
|
|
|
3
|
-
Generate high-quality git commit messages with Claude
|
|
3
|
+
Generate high-quality git commit messages with Claude - using your **Claude Code
|
|
4
4
|
subscription**, not an API key.
|
|
5
5
|
|
|
6
6
|
`claude-commit` reads your staged diff, has a strong model summarize it, and a fast
|
|
@@ -23,10 +23,10 @@ Want more details? See [WALKTHROUGH.md](WALKTHROUGH.md).
|
|
|
23
23
|
staged diff ──split──▶ [chunk, …] ──sonnet[1m]──▶ summaries ──haiku──▶ commit message
|
|
24
24
|
```
|
|
25
25
|
|
|
26
|
-
1. **Summarize**
|
|
26
|
+
1. **Summarize** - the diff is split into chunks that fit the context window and
|
|
27
27
|
each chunk is summarized by a strong model (`sonnet[1m]`, Sonnet with a 1M-token
|
|
28
28
|
context). Diffs larger than 1M tokens simply produce more chunks.
|
|
29
|
-
2. **Write**
|
|
29
|
+
2. **Write** - the summaries are handed to a fast model (`haiku`) that writes the
|
|
30
30
|
final commit message according to your formatting rules.
|
|
31
31
|
|
|
32
32
|
Both stages run through the [Claude Agent SDK](https://code.claude.com/docs/en/agent-sdk/overview).
|
|
@@ -50,7 +50,7 @@ bun run bin/cco.ts --help
|
|
|
50
50
|
|
|
51
51
|
`claude-commit` uses the Claude Agent SDK and, by default, always authenticates
|
|
52
52
|
with your Claude Code subscription session (run `claude login` once). Usage is
|
|
53
|
-
bundled with your Claude Code usage
|
|
53
|
+
bundled with your Claude Code usage - no separate API bill.
|
|
54
54
|
|
|
55
55
|
To protect you from surprise pay-as-you-go charges, API credentials in your
|
|
56
56
|
environment (`ANTHROPIC_API_KEY` / `ANTHROPIC_AUTH_TOKEN`) are **ignored by
|
|
@@ -103,7 +103,7 @@ cco --dry-run | cat # print a message without committing (TUI-free, pipe-sa
|
|
|
103
103
|
git commit -F <(cco -d) # use the message with your own git invocation
|
|
104
104
|
```
|
|
105
105
|
|
|
106
|
-
In a pipe (no TTY) there is no spinner and no confirmation prompt
|
|
106
|
+
In a pipe (no TTY) there is no spinner and no confirmation prompt - `cco` just
|
|
107
107
|
generates and commits (or prints, with `--dry-run`).
|
|
108
108
|
|
|
109
109
|
## Interactive mode
|
|
@@ -116,8 +116,8 @@ cancel.
|
|
|
116
116
|
|
|
117
117
|
To make interactive mode the default without typing `-i` every time, set
|
|
118
118
|
`"interactive": true` in your config (see below); opt out of a single run with
|
|
119
|
-
`--no-interactive`. When there is no interactive terminal
|
|
120
|
-
or with `--dry-run`
|
|
119
|
+
`--no-interactive`. When there is no interactive terminal - in a pipe, a CI job,
|
|
120
|
+
or with `--dry-run` - `cco` ignores the setting and falls back to the
|
|
121
121
|
non-interactive flow rather than failing.
|
|
122
122
|
|
|
123
123
|
## Configuration
|
|
@@ -126,7 +126,7 @@ Configuration is layered, from lowest to highest precedence:
|
|
|
126
126
|
|
|
127
127
|
1. Built-in defaults.
|
|
128
128
|
2. **A global user config** at `~/.config/claude-commit/config.json` (or
|
|
129
|
-
`$XDG_CONFIG_HOME/claude-commit/config.json`)
|
|
129
|
+
`$XDG_CONFIG_HOME/claude-commit/config.json`) - your personal defaults across
|
|
130
130
|
every project. The `.claude-commit.json` / `.claude-commitrc.json` /
|
|
131
131
|
`.claude-commitrc` names are also accepted in that directory.
|
|
132
132
|
3. A `claude-commit` key in the repo's `package.json`.
|
package/package.json
CHANGED
package/src/agent.ts
CHANGED
|
@@ -92,7 +92,7 @@ export interface SubprocessEnvOptions {
|
|
|
92
92
|
*
|
|
93
93
|
* Unless `allowApiKey` is set, API credential variables are removed so the
|
|
94
94
|
* spawned `claude` binary always authenticates with the user's subscription
|
|
95
|
-
* session
|
|
95
|
+
* session - an exported `ANTHROPIC_API_KEY` must never silently switch
|
|
96
96
|
* billing to pay-as-you-go.
|
|
97
97
|
*/
|
|
98
98
|
export function buildSubprocessEnv(
|
package/src/config.ts
CHANGED
|
@@ -197,7 +197,7 @@ export async function loadFileConfig(
|
|
|
197
197
|
|
|
198
198
|
// package.json#claude-commit at the repo root (above the global config, below
|
|
199
199
|
// project config files). A malformed package.json is not cco's concern to
|
|
200
|
-
// enforce
|
|
200
|
+
// enforce - skip it rather than blocking the commit (the user may even be
|
|
201
201
|
// committing its fix).
|
|
202
202
|
let pkg: unknown;
|
|
203
203
|
try {
|
package/src/diff.ts
CHANGED
|
@@ -16,7 +16,7 @@ const HUNK_HEADER = "@@";
|
|
|
16
16
|
* which we stop trying to subdivide a section. This guards against a
|
|
17
17
|
* misconfigured tiny `maxChars`, or a pathologically large file header,
|
|
18
18
|
* driving the line-splitter's budget to zero and shattering a hunk into
|
|
19
|
-
* one-character pieces
|
|
19
|
+
* one-character pieces - which would otherwise spawn a model request per
|
|
20
20
|
* character.
|
|
21
21
|
*/
|
|
22
22
|
const MIN_SPLIT_BUDGET = 64;
|
package/src/git.ts
CHANGED
|
@@ -37,7 +37,7 @@ export async function isGitRepo(): Promise<boolean> {
|
|
|
37
37
|
const res = await $`git rev-parse --is-inside-work-tree`.quiet().nothrow();
|
|
38
38
|
return res.exitCode === 0 && res.stdout.toString().trim() === "true";
|
|
39
39
|
} catch {
|
|
40
|
-
// git missing or unrunnable
|
|
40
|
+
// git missing or unrunnable - treat as "not a usable repo".
|
|
41
41
|
return false;
|
|
42
42
|
}
|
|
43
43
|
}
|
|
@@ -81,7 +81,7 @@ export async function getStagedStat(): Promise<string> {
|
|
|
81
81
|
/**
|
|
82
82
|
* Create a commit with the given message. The message is piped to
|
|
83
83
|
* `git commit -F -` over stdin, so arbitrary content (leading dashes, multiple
|
|
84
|
-
* lines, special characters) is handled safely
|
|
84
|
+
* lines, special characters) is handled safely - and nothing touches disk, so
|
|
85
85
|
* there is no temp file to be raced or read by another user.
|
|
86
86
|
*/
|
|
87
87
|
export async function commit(message: string): Promise<void> {
|
package/src/prompts.ts
CHANGED
|
@@ -147,7 +147,7 @@ export function buildFinalSystem(config: Config, structured = false): string {
|
|
|
147
147
|
|
|
148
148
|
rules.push(
|
|
149
149
|
structured
|
|
150
|
-
? "Each commit message must be the raw message text only
|
|
150
|
+
? "Each commit message must be the raw message text only - no surrounding quotes, no markdown, and no code fences."
|
|
151
151
|
: "Output ONLY the commit message itself: no surrounding quotes, no markdown, no code fences, no preamble, and no explanation.",
|
|
152
152
|
);
|
|
153
153
|
|
|
@@ -157,7 +157,7 @@ export function buildFinalSystem(config: Config, structured = false): string {
|
|
|
157
157
|
/**
|
|
158
158
|
* The shared instruction for requesting several distinct options.
|
|
159
159
|
*
|
|
160
|
-
* It insists each option be a COMPLETE message obeying the formatting rules
|
|
160
|
+
* It insists each option be a COMPLETE message obeying the formatting rules -
|
|
161
161
|
* crucially the body when `multiline` is on. The previous wording asked the
|
|
162
162
|
* model to "vary the structure" of the options, which let it drop bodies to
|
|
163
163
|
* manufacture variety, so `multiline` appeared to be ignored in interactive
|
|
@@ -166,7 +166,7 @@ export function buildFinalSystem(config: Config, structured = false): string {
|
|
|
166
166
|
function multiOptionInstruction(count: number): string {
|
|
167
167
|
return (
|
|
168
168
|
`Produce exactly ${count} distinct commit-message options for this change. ` +
|
|
169
|
-
`Each option must be a complete commit message that independently obeys all the formatting rules above
|
|
169
|
+
`Each option must be a complete commit message that independently obeys all the formatting rules above - ` +
|
|
170
170
|
`including the blank line and body when those rules ask for one. ` +
|
|
171
171
|
`Make the options genuinely different in wording and emphasis, but never drop the subject or a required body just to create variety.`
|
|
172
172
|
);
|
package/src/ui/interactive.ts
CHANGED
|
@@ -51,7 +51,7 @@ export async function runInteractive(
|
|
|
51
51
|
try {
|
|
52
52
|
selection = await selectWithTui(messages);
|
|
53
53
|
} catch {
|
|
54
|
-
// TUI failed to initialize (unusual terminal, etc.)
|
|
54
|
+
// TUI failed to initialize (unusual terminal, etc.) - degrade gracefully.
|
|
55
55
|
selection = await selectWithReadline(messages);
|
|
56
56
|
}
|
|
57
57
|
|
|
@@ -98,7 +98,7 @@ const PICKER_CHROME_ROWS = 4;
|
|
|
98
98
|
* The picker's height, sized to its content (two rows per candidate) but capped
|
|
99
99
|
* to the terminal so a large `interactiveCount` still fits; past the cap the list
|
|
100
100
|
* scrolls internally (see `showScrollIndicator` in {@link buildPickerScene}). An
|
|
101
|
-
* explicit height keeps the picker compact
|
|
101
|
+
* explicit height keeps the picker compact - only as tall as the options need -
|
|
102
102
|
* rather than stretching to fill the screen.
|
|
103
103
|
*/
|
|
104
104
|
export function pickerHeight(count: number, terminalRows: number): number {
|
|
@@ -163,7 +163,7 @@ export function buildPickerScene(
|
|
|
163
163
|
showDescription: true,
|
|
164
164
|
wrapSelection: true,
|
|
165
165
|
// A calm slate highlight (OpenTUI's own default background) with soft
|
|
166
|
-
// near-white text
|
|
166
|
+
// near-white text - the previous bright cyan/black fill was too harsh.
|
|
167
167
|
// The default description greys (#888888 / #CCCCCC) read fine on the slate,
|
|
168
168
|
// so they are left untouched.
|
|
169
169
|
selectedBackgroundColor: "#334455",
|
|
@@ -246,7 +246,7 @@ async function selectWithTui(messages: string[]): Promise<Selection> {
|
|
|
246
246
|
} catch {
|
|
247
247
|
// A renderable method threw unexpectedly. Rather than let the error
|
|
248
248
|
// escape the key handler and leave the terminal stuck in raw mode,
|
|
249
|
-
// cancel cleanly
|
|
249
|
+
// cancel cleanly - finish() restores the terminal via cleanup().
|
|
250
250
|
finish({ action: "cancel" });
|
|
251
251
|
}
|
|
252
252
|
};
|