@webywz/vibe-commit 0.1.4 → 0.1.5
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 +7 -6
- package/dist/git.js +2 -1
- package/dist/heuristic.d.ts +7 -2
- package/dist/heuristic.js +37 -6
- package/dist/index.d.ts +2 -1
- package/dist/index.js +1 -1
- package/dist/propose.js +10 -3
- package/dist/render.js +4 -0
- package/dist/types.d.ts +2 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -56,16 +56,15 @@ vibe-commit install # optional prepare-commit-msg hook
|
|
|
56
56
|
| `--vibe <style>` | `playful` \| `pro` \| `terse` (default: `pro`) |
|
|
57
57
|
| `--no-ai` | Skip the API even if `OPENAI_API_KEY` is set · 即使有 Key 也不走 AI |
|
|
58
58
|
| `--breaking` | Force breaking change (`!` + `BREAKING CHANGE:` footer) · 强制破坏性变更(`!` + footer) |
|
|
59
|
-
|
|
60
|
-
Offline heuristic also marks breaking when the staged diff removes public `export`s or bumps a `package.json` major version, and always attaches a `BREAKING CHANGE:` footer when `breaking` is true (so `--json` body matches the formatted message).
|
|
61
|
-
|
|
62
|
-
离线启发式在删掉公开 `export`、或 `package.json` 主版本升级时也会标 breaking;只要 `breaking` 为真就会补上 `BREAKING CHANGE:` footer(`--json` 的 body 与格式化输出一致)。
|
|
63
|
-
|
|
64
59
|
| `--json` | Machine-readable proposal · 机器可读提案 |
|
|
65
60
|
| `install [--path]` | Install the prepare-commit-msg hook · 安装钩子 |
|
|
66
61
|
| `uninstall [--path]` | Remove the managed hook · 卸载钩子 |
|
|
67
62
|
| `-h` / `-v` | Help / version · 帮助 / 版本 |
|
|
68
63
|
|
|
64
|
+
Offline heuristic also marks breaking when the staged diff removes public `export`s or bumps a `package.json` major version, and always attaches a `BREAKING CHANGE:` footer when `breaking` is true (so `--json` body matches the formatted message). Dry-run prints a one-line cause; `--json` may include `breakingReason`.
|
|
65
|
+
|
|
66
|
+
离线启发式在删掉公开 `export`、或 `package.json` 主版本升级时也会标 breaking;只要 `breaking` 为真就会补上 `BREAKING CHANGE:` footer(`--json` 的 body 与格式化输出一致)。干跑会打一行原因;`--json` 可带 `breakingReason`。
|
|
67
|
+
|
|
69
68
|
**Vibes** · 语气
|
|
70
69
|
|
|
71
70
|
- **pro** — default. Imperative, review-ready. · 默认:祈使句,可进 review。
|
|
@@ -147,7 +146,9 @@ console.log(formatCommit(message));
|
|
|
147
146
|
- new files / `export function` → `feat`
|
|
148
147
|
3. Optional AI draft with automatic heuristic fallback.
|
|
149
148
|
可选 AI 草稿,失败自动回退启发式。
|
|
150
|
-
4.
|
|
149
|
+
4. **Breaking** when the diff removes a public `export`, bumps a `package.json` major, adds a `BREAKING CHANGE:` footer, or you pass `--breaking`. Dry-run shows why; `--json` may include `breakingReason`.
|
|
150
|
+
**破坏性变更**:删公开 `export`、主版本升级、diff 里已有 footer,或传 `--breaking`。干跑说明原因;`--json` 可带 `breakingReason`。
|
|
151
|
+
5. Managed `prepare-commit-msg` hook via `install` / `uninstall`.
|
|
151
152
|
通过 `install` / `uninstall` 管理 `prepare-commit-msg` 钩子。
|
|
152
153
|
|
|
153
154
|
---
|
package/dist/git.js
CHANGED
|
@@ -13,7 +13,8 @@ export function inspectStaged(cwd = process.cwd()) {
|
|
|
13
13
|
const raw = git(["diff", "--cached", "-M", "--unified=3"], cwd);
|
|
14
14
|
const diff = assembleDiff(nameStatus, numstat, raw);
|
|
15
15
|
if (diff.files.length === 0) {
|
|
16
|
-
throw new GitError("No staged changes. Stage files first: git add -p
|
|
16
|
+
throw new GitError("No staged changes. Stage files first: git add -p (or git add <path>)\n" +
|
|
17
|
+
"暂存区为空。请先暂存:git add -p(或 git add <path>)");
|
|
17
18
|
}
|
|
18
19
|
return diff;
|
|
19
20
|
}
|
package/dist/heuristic.d.ts
CHANGED
|
@@ -13,9 +13,14 @@ export declare function primaryRole(path: string): CommitType;
|
|
|
13
13
|
export declare function classifyPath(path: string): CommitType[];
|
|
14
14
|
export declare function inferScope(files: StagedFile[]): string | undefined;
|
|
15
15
|
export declare function inferSubject(diff: StagedDiff, type: CommitType, scope?: string): string;
|
|
16
|
+
export type BreakingKind = "export-removed" | "major-bump" | "footer" | "forced";
|
|
16
17
|
export declare function inferBreaking(diff: StagedDiff): boolean;
|
|
17
|
-
/**
|
|
18
|
-
export declare function
|
|
18
|
+
/** Why breaking was set — heuristic signal first, then --breaking. */
|
|
19
|
+
export declare function detectBreakingKind(diff: StagedDiff, forced?: boolean): BreakingKind | undefined;
|
|
20
|
+
/** One-line bilingual cause for dry-run / --json.breakingReason. */
|
|
21
|
+
export declare function formatBreakingReason(kind: BreakingKind): string;
|
|
22
|
+
/** Detail text for the BREAKING CHANGE footer, or undefined when not inferred. */
|
|
23
|
+
export declare function breakingFooterDetail(diff: StagedDiff): string | undefined;
|
|
19
24
|
/**
|
|
20
25
|
* Ensure body carries a conventional BREAKING CHANGE footer when breaking.
|
|
21
26
|
* Skips if the body already mentions one. Uses subject as a last-resort reason.
|
package/dist/heuristic.js
CHANGED
|
@@ -28,10 +28,16 @@ export function proposeHeuristic(input) {
|
|
|
28
28
|
const scope = input.scope ?? inferScope(diff.files);
|
|
29
29
|
const subject = inferSubject(diff, type, scope);
|
|
30
30
|
const body = inferBody(diff, type);
|
|
31
|
-
const
|
|
31
|
+
const forced = Boolean(input.breaking);
|
|
32
|
+
const kind = detectBreakingKind(diff, forced);
|
|
33
|
+
const breaking = Boolean(kind);
|
|
32
34
|
const styled = applyVibe({ type, scope, subject, body, breaking }, vibe);
|
|
33
|
-
const message = {
|
|
34
|
-
|
|
35
|
+
const message = {
|
|
36
|
+
...styled,
|
|
37
|
+
engine: "heuristic",
|
|
38
|
+
breakingReason: kind ? formatBreakingReason(kind) : undefined,
|
|
39
|
+
};
|
|
40
|
+
return ensureBreakingFooter(message, breakingFooterDetail(diff));
|
|
35
41
|
}
|
|
36
42
|
export function inferType(diff) {
|
|
37
43
|
const files = diff.files;
|
|
@@ -218,10 +224,35 @@ function inferBody(diff, type) {
|
|
|
218
224
|
return `${lead}\n${listed}${extra}`;
|
|
219
225
|
}
|
|
220
226
|
export function inferBreaking(diff) {
|
|
221
|
-
return Boolean(
|
|
227
|
+
return Boolean(detectBreakingKind(diff, false));
|
|
228
|
+
}
|
|
229
|
+
/** Why breaking was set — heuristic signal first, then --breaking. */
|
|
230
|
+
export function detectBreakingKind(diff, forced = false) {
|
|
231
|
+
if (detectBreakingFooter(diff))
|
|
232
|
+
return "footer";
|
|
233
|
+
if (detectRemovedExports(diff).length > 0)
|
|
234
|
+
return "export-removed";
|
|
235
|
+
if (detectMajorVersionBump(diff))
|
|
236
|
+
return "major-bump";
|
|
237
|
+
if (forced)
|
|
238
|
+
return "forced";
|
|
239
|
+
return undefined;
|
|
240
|
+
}
|
|
241
|
+
/** One-line bilingual cause for dry-run / --json.breakingReason. */
|
|
242
|
+
export function formatBreakingReason(kind) {
|
|
243
|
+
switch (kind) {
|
|
244
|
+
case "export-removed":
|
|
245
|
+
return "export removed · 公开 export 删除";
|
|
246
|
+
case "major-bump":
|
|
247
|
+
return "major bump · package.json 主版本升级";
|
|
248
|
+
case "footer":
|
|
249
|
+
return "BREAKING CHANGE footer in diff";
|
|
250
|
+
case "forced":
|
|
251
|
+
return "forced via --breaking · --breaking 强制";
|
|
252
|
+
}
|
|
222
253
|
}
|
|
223
|
-
/**
|
|
224
|
-
export function
|
|
254
|
+
/** Detail text for the BREAKING CHANGE footer, or undefined when not inferred. */
|
|
255
|
+
export function breakingFooterDetail(diff) {
|
|
225
256
|
const footer = detectBreakingFooter(diff);
|
|
226
257
|
if (footer)
|
|
227
258
|
return footer;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
export { proposeWithAi } from "./ai.ts";
|
|
2
2
|
export { formatCommit, parseCommit, normalizeSubject } from "./format.ts";
|
|
3
3
|
export { inspectStaged, commitWithMessage, GitError } from "./git.ts";
|
|
4
|
-
export { proposeHeuristic, inferType, inferScope, inferSubject, inferBreaking, ensureBreakingFooter, classifyPath, } from "./heuristic.ts";
|
|
4
|
+
export { proposeHeuristic, inferType, inferScope, inferSubject, inferBreaking, detectBreakingKind, formatBreakingReason, breakingFooterDetail, ensureBreakingFooter, classifyPath, } from "./heuristic.ts";
|
|
5
|
+
export type { BreakingKind } from "./heuristic.ts";
|
|
5
6
|
export { parseArgs, helpText, UsageError } from "./parse-args.ts";
|
|
6
7
|
export { assembleDiff, parseUnifiedDiff } from "./parse-diff.ts";
|
|
7
8
|
export { installHook, uninstallHook, runPrepareCommitMsg, shouldSkipPrepareCommitMsg, hasNonCommentContent, HOOK_BEGIN, HOOK_END, } from "./hook.ts";
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export { proposeWithAi } from "./ai.js";
|
|
2
2
|
export { formatCommit, parseCommit, normalizeSubject } from "./format.js";
|
|
3
3
|
export { inspectStaged, commitWithMessage, GitError } from "./git.js";
|
|
4
|
-
export { proposeHeuristic, inferType, inferScope, inferSubject, inferBreaking, ensureBreakingFooter, classifyPath, } from "./heuristic.js";
|
|
4
|
+
export { proposeHeuristic, inferType, inferScope, inferSubject, inferBreaking, detectBreakingKind, formatBreakingReason, breakingFooterDetail, ensureBreakingFooter, classifyPath, } from "./heuristic.js";
|
|
5
5
|
export { parseArgs, helpText, UsageError } from "./parse-args.js";
|
|
6
6
|
export { assembleDiff, parseUnifiedDiff } from "./parse-diff.js";
|
|
7
7
|
export { installHook, uninstallHook, runPrepareCommitMsg, shouldSkipPrepareCommitMsg, hasNonCommentContent, HOOK_BEGIN, HOOK_END, } from "./hook.js";
|
package/dist/propose.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { proposeWithAi } from "./ai.js";
|
|
2
|
-
import {
|
|
2
|
+
import { breakingFooterDetail, detectBreakingKind, ensureBreakingFooter, formatBreakingReason, proposeHeuristic, } from "./heuristic.js";
|
|
3
3
|
export async function proposeMessage(diff, options, env = process.env) {
|
|
4
4
|
const heuristic = proposeHeuristic({
|
|
5
5
|
diff,
|
|
@@ -22,7 +22,14 @@ export async function proposeMessage(diff, options, env = process.env) {
|
|
|
22
22
|
message = ai;
|
|
23
23
|
}
|
|
24
24
|
}
|
|
25
|
-
const
|
|
26
|
-
|
|
25
|
+
const forced = Boolean(options.breaking);
|
|
26
|
+
const breaking = forced || heuristic.breaking || message.breaking;
|
|
27
|
+
const kind = detectBreakingKind(diff, forced);
|
|
28
|
+
const breakingReason = kind
|
|
29
|
+
? formatBreakingReason(kind)
|
|
30
|
+
: breaking
|
|
31
|
+
? heuristic.breakingReason ?? message.breakingReason
|
|
32
|
+
: undefined;
|
|
33
|
+
return ensureBreakingFooter({ ...message, breaking, breakingReason }, breakingFooterDetail(diff));
|
|
27
34
|
}
|
|
28
35
|
//# sourceMappingURL=propose.js.map
|
package/dist/render.js
CHANGED
|
@@ -31,6 +31,10 @@ export function renderProposal(diff, message, hints = {}) {
|
|
|
31
31
|
...indent(formatted, pc),
|
|
32
32
|
"",
|
|
33
33
|
];
|
|
34
|
+
if (message.breaking && message.breakingReason) {
|
|
35
|
+
lines.push(` ${pc.yellow("breaking")} ${pc.dim(message.breakingReason)}`);
|
|
36
|
+
lines.push("");
|
|
37
|
+
}
|
|
34
38
|
if (hints.committed) {
|
|
35
39
|
lines.push(` ${pc.green("committed")}`);
|
|
36
40
|
lines.push("");
|
package/dist/types.d.ts
CHANGED
|
@@ -27,6 +27,8 @@ export interface ProposedMessage {
|
|
|
27
27
|
subject: string;
|
|
28
28
|
body?: string;
|
|
29
29
|
breaking: boolean;
|
|
30
|
+
/** Short cause when breaking (export removed / major bump / --breaking / footer). */
|
|
31
|
+
breakingReason?: string;
|
|
30
32
|
engine: Engine;
|
|
31
33
|
}
|
|
32
34
|
export interface ProposeOptions {
|