ghagga 2.0.1 → 2.2.0
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 +90 -19
- package/dist/commands/hooks/index.d.ts +9 -0
- package/dist/commands/hooks/index.d.ts.map +1 -0
- package/dist/commands/hooks/index.js +16 -0
- package/dist/commands/hooks/index.js.map +1 -0
- package/dist/commands/hooks/install.d.ts +13 -0
- package/dist/commands/hooks/install.d.ts.map +1 -0
- package/dist/commands/hooks/install.js +64 -0
- package/dist/commands/hooks/install.js.map +1 -0
- package/dist/commands/hooks/install.test.d.ts +11 -0
- package/dist/commands/hooks/install.test.d.ts.map +1 -0
- package/dist/commands/hooks/install.test.js +157 -0
- package/dist/commands/hooks/install.test.js.map +1 -0
- package/dist/commands/hooks/status.d.ts +12 -0
- package/dist/commands/hooks/status.d.ts.map +1 -0
- package/dist/commands/hooks/status.js +38 -0
- package/dist/commands/hooks/status.js.map +1 -0
- package/dist/commands/hooks/status.test.d.ts +11 -0
- package/dist/commands/hooks/status.test.d.ts.map +1 -0
- package/dist/commands/hooks/status.test.js +123 -0
- package/dist/commands/hooks/status.test.js.map +1 -0
- package/dist/commands/hooks/uninstall.d.ts +12 -0
- package/dist/commands/hooks/uninstall.d.ts.map +1 -0
- package/dist/commands/hooks/uninstall.js +44 -0
- package/dist/commands/hooks/uninstall.js.map +1 -0
- package/dist/commands/hooks/uninstall.test.d.ts +10 -0
- package/dist/commands/hooks/uninstall.test.d.ts.map +1 -0
- package/dist/commands/hooks/uninstall.test.js +120 -0
- package/dist/commands/hooks/uninstall.test.js.map +1 -0
- package/dist/commands/login.d.ts.map +1 -1
- package/dist/commands/login.js +17 -14
- package/dist/commands/login.js.map +1 -1
- package/dist/commands/login.test.d.ts +9 -0
- package/dist/commands/login.test.d.ts.map +1 -0
- package/dist/commands/login.test.js +138 -0
- package/dist/commands/login.test.js.map +1 -0
- package/dist/commands/logout.d.ts.map +1 -1
- package/dist/commands/logout.js +4 -3
- package/dist/commands/logout.js.map +1 -1
- package/dist/commands/logout.test.d.ts +8 -0
- package/dist/commands/logout.test.d.ts.map +1 -0
- package/dist/commands/logout.test.js +61 -0
- package/dist/commands/logout.test.js.map +1 -0
- package/dist/commands/memory/clear.d.ts +11 -0
- package/dist/commands/memory/clear.d.ts.map +1 -0
- package/dist/commands/memory/clear.js +45 -0
- package/dist/commands/memory/clear.js.map +1 -0
- package/dist/commands/memory/clear.test.d.ts +11 -0
- package/dist/commands/memory/clear.test.d.ts.map +1 -0
- package/dist/commands/memory/clear.test.js +178 -0
- package/dist/commands/memory/clear.test.js.map +1 -0
- package/dist/commands/memory/delete.d.ts +11 -0
- package/dist/commands/memory/delete.d.ts.map +1 -0
- package/dist/commands/memory/delete.js +38 -0
- package/dist/commands/memory/delete.js.map +1 -0
- package/dist/commands/memory/delete.test.d.ts +11 -0
- package/dist/commands/memory/delete.test.d.ts.map +1 -0
- package/dist/commands/memory/delete.test.js +176 -0
- package/dist/commands/memory/delete.test.js.map +1 -0
- package/dist/commands/memory/index.d.ts +10 -0
- package/dist/commands/memory/index.d.ts.map +1 -0
- package/dist/commands/memory/index.js +23 -0
- package/dist/commands/memory/index.js.map +1 -0
- package/dist/commands/memory/list.d.ts +11 -0
- package/dist/commands/memory/list.d.ts.map +1 -0
- package/dist/commands/memory/list.js +47 -0
- package/dist/commands/memory/list.js.map +1 -0
- package/dist/commands/memory/list.test.d.ts +10 -0
- package/dist/commands/memory/list.test.d.ts.map +1 -0
- package/dist/commands/memory/list.test.js +135 -0
- package/dist/commands/memory/list.test.js.map +1 -0
- package/dist/commands/memory/search.d.ts +12 -0
- package/dist/commands/memory/search.d.ts.map +1 -0
- package/dist/commands/memory/search.js +44 -0
- package/dist/commands/memory/search.js.map +1 -0
- package/dist/commands/memory/search.test.d.ts +11 -0
- package/dist/commands/memory/search.test.d.ts.map +1 -0
- package/dist/commands/memory/search.test.js +122 -0
- package/dist/commands/memory/search.test.js.map +1 -0
- package/dist/commands/memory/show.d.ts +10 -0
- package/dist/commands/memory/show.d.ts.map +1 -0
- package/dist/commands/memory/show.js +51 -0
- package/dist/commands/memory/show.js.map +1 -0
- package/dist/commands/memory/show.test.d.ts +11 -0
- package/dist/commands/memory/show.test.d.ts.map +1 -0
- package/dist/commands/memory/show.test.js +156 -0
- package/dist/commands/memory/show.test.js.map +1 -0
- package/dist/commands/memory/stats.d.ts +11 -0
- package/dist/commands/memory/stats.d.ts.map +1 -0
- package/dist/commands/memory/stats.js +63 -0
- package/dist/commands/memory/stats.js.map +1 -0
- package/dist/commands/memory/stats.test.d.ts +10 -0
- package/dist/commands/memory/stats.test.d.ts.map +1 -0
- package/dist/commands/memory/stats.test.js +122 -0
- package/dist/commands/memory/stats.test.js.map +1 -0
- package/dist/commands/memory/utils.d.ts +44 -0
- package/dist/commands/memory/utils.d.ts.map +1 -0
- package/dist/commands/memory/utils.js +90 -0
- package/dist/commands/memory/utils.js.map +1 -0
- package/dist/commands/memory/utils.test.d.ts +10 -0
- package/dist/commands/memory/utils.test.d.ts.map +1 -0
- package/dist/commands/memory/utils.test.js +93 -0
- package/dist/commands/memory/utils.test.js.map +1 -0
- package/dist/commands/review-commit-msg.d.ts +28 -0
- package/dist/commands/review-commit-msg.d.ts.map +1 -0
- package/dist/commands/review-commit-msg.js +126 -0
- package/dist/commands/review-commit-msg.js.map +1 -0
- package/dist/commands/review-commit-msg.test.d.ts +11 -0
- package/dist/commands/review-commit-msg.test.d.ts.map +1 -0
- package/dist/commands/review-commit-msg.test.js +126 -0
- package/dist/commands/review-commit-msg.test.js.map +1 -0
- package/dist/commands/review.d.ts +7 -0
- package/dist/commands/review.d.ts.map +1 -1
- package/dist/commands/review.js +138 -103
- package/dist/commands/review.js.map +1 -1
- package/dist/commands/review.test.js +267 -1
- package/dist/commands/review.test.js.map +1 -1
- package/dist/commands/status.d.ts.map +1 -1
- package/dist/commands/status.js +12 -11
- package/dist/commands/status.js.map +1 -1
- package/dist/commands/status.test.d.ts +8 -0
- package/dist/commands/status.test.d.ts.map +1 -0
- package/dist/commands/status.test.js +106 -0
- package/dist/commands/status.test.js.map +1 -0
- package/dist/index.d.ts +5 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +48 -16
- package/dist/index.js.map +1 -1
- package/dist/lib/config.d.ts +1 -0
- package/dist/lib/config.d.ts.map +1 -1
- package/dist/lib/config.js +1 -1
- package/dist/lib/config.js.map +1 -1
- package/dist/lib/config.test.d.ts +9 -0
- package/dist/lib/config.test.d.ts.map +1 -0
- package/dist/lib/config.test.js +181 -0
- package/dist/lib/config.test.js.map +1 -0
- package/dist/lib/git-hooks.d.ts +19 -0
- package/dist/lib/git-hooks.d.ts.map +1 -0
- package/dist/lib/git-hooks.js +129 -0
- package/dist/lib/git-hooks.js.map +1 -0
- package/dist/lib/git-hooks.test.d.ts +11 -0
- package/dist/lib/git-hooks.test.d.ts.map +1 -0
- package/dist/lib/git-hooks.test.js +178 -0
- package/dist/lib/git-hooks.test.js.map +1 -0
- package/dist/lib/git.d.ts +33 -0
- package/dist/lib/git.d.ts.map +1 -0
- package/dist/lib/git.js +85 -0
- package/dist/lib/git.js.map +1 -0
- package/dist/lib/git.test.d.ts +2 -0
- package/dist/lib/git.test.d.ts.map +1 -0
- package/dist/lib/git.test.js +65 -0
- package/dist/lib/git.test.js.map +1 -0
- package/dist/lib/hook-templates.d.ts +12 -0
- package/dist/lib/hook-templates.d.ts.map +1 -0
- package/dist/lib/hook-templates.js +52 -0
- package/dist/lib/hook-templates.js.map +1 -0
- package/dist/lib/hook-templates.test.d.ts +11 -0
- package/dist/lib/hook-templates.test.d.ts.map +1 -0
- package/dist/lib/hook-templates.test.js +76 -0
- package/dist/lib/hook-templates.test.js.map +1 -0
- package/dist/lib/hooks-types.d.ts +30 -0
- package/dist/lib/hooks-types.d.ts.map +1 -0
- package/dist/lib/hooks-types.js +9 -0
- package/dist/lib/hooks-types.js.map +1 -0
- package/dist/lib/oauth.test.d.ts +8 -0
- package/dist/lib/oauth.test.d.ts.map +1 -0
- package/dist/lib/oauth.test.js +212 -0
- package/dist/lib/oauth.test.js.map +1 -0
- package/dist/ui/__tests__/format.test.d.ts +7 -0
- package/dist/ui/__tests__/format.test.d.ts.map +1 -0
- package/dist/ui/__tests__/format.test.js +220 -0
- package/dist/ui/__tests__/format.test.js.map +1 -0
- package/dist/ui/__tests__/theme.test.d.ts +7 -0
- package/dist/ui/__tests__/theme.test.d.ts.map +1 -0
- package/dist/ui/__tests__/theme.test.js +79 -0
- package/dist/ui/__tests__/theme.test.js.map +1 -0
- package/dist/ui/__tests__/tui.test.d.ts +9 -0
- package/dist/ui/__tests__/tui.test.d.ts.map +1 -0
- package/dist/ui/__tests__/tui.test.js +222 -0
- package/dist/ui/__tests__/tui.test.js.map +1 -0
- package/dist/ui/format.d.ts +38 -0
- package/dist/ui/format.d.ts.map +1 -0
- package/dist/ui/format.js +136 -0
- package/dist/ui/format.js.map +1 -0
- package/dist/ui/theme.d.ts +26 -0
- package/dist/ui/theme.d.ts.map +1 -0
- package/dist/ui/theme.js +63 -0
- package/dist/ui/theme.js.map +1 -0
- package/dist/ui/tui.d.ts +44 -0
- package/dist/ui/tui.d.ts.map +1 -0
- package/dist/ui/tui.js +121 -0
- package/dist/ui/tui.js.map +1 -0
- package/package.json +4 -2
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for memory command shared utilities.
|
|
3
|
+
*
|
|
4
|
+
* Tests pure formatting functions only (no mocking needed).
|
|
5
|
+
* formatTable, formatSize, formatId, truncate.
|
|
6
|
+
*
|
|
7
|
+
* @see T7.1
|
|
8
|
+
*/
|
|
9
|
+
import { describe, it, expect } from 'vitest';
|
|
10
|
+
import { formatTable, formatSize, formatId, truncate } from './utils.js';
|
|
11
|
+
// ─── formatTable ────────────────────────────────────────────────
|
|
12
|
+
describe('formatTable', () => {
|
|
13
|
+
it('renders headers, separator, and data rows', () => {
|
|
14
|
+
const headers = ['Name', 'Age'];
|
|
15
|
+
const rows = [
|
|
16
|
+
['Alice', '30'],
|
|
17
|
+
['Bob', '25'],
|
|
18
|
+
];
|
|
19
|
+
const widths = [10, 5];
|
|
20
|
+
const result = formatTable(headers, rows, widths);
|
|
21
|
+
const lines = result.split('\n');
|
|
22
|
+
expect(lines).toHaveLength(4); // header + separator + 2 rows
|
|
23
|
+
expect(lines[0]).toBe('Name Age ');
|
|
24
|
+
expect(lines[1]).toContain('──────────');
|
|
25
|
+
expect(lines[2]).toBe('Alice 30 ');
|
|
26
|
+
expect(lines[3]).toBe('Bob 25 ');
|
|
27
|
+
});
|
|
28
|
+
it('uses ─ characters for separator row', () => {
|
|
29
|
+
const result = formatTable(['H'], [['D']], [8]);
|
|
30
|
+
const lines = result.split('\n');
|
|
31
|
+
expect(lines[1]).toBe('────────');
|
|
32
|
+
});
|
|
33
|
+
it('pads columns to specified widths', () => {
|
|
34
|
+
const result = formatTable(['A', 'B'], [['x', 'y']], [5, 3]);
|
|
35
|
+
const lines = result.split('\n');
|
|
36
|
+
// 'A' padded to 5 + ' ' + 'B' padded to 3
|
|
37
|
+
expect(lines[0]).toBe('A B ');
|
|
38
|
+
});
|
|
39
|
+
it('handles empty rows array', () => {
|
|
40
|
+
const result = formatTable(['H1', 'H2'], [], [5, 5]);
|
|
41
|
+
const lines = result.split('\n');
|
|
42
|
+
expect(lines).toHaveLength(2); // header + separator only
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
// ─── formatSize ─────────────────────────────────────────────────
|
|
46
|
+
describe('formatSize', () => {
|
|
47
|
+
it('formats bytes when under 1024', () => {
|
|
48
|
+
expect(formatSize(0)).toBe('0 bytes');
|
|
49
|
+
expect(formatSize(512)).toBe('512 bytes');
|
|
50
|
+
expect(formatSize(1023)).toBe('1023 bytes');
|
|
51
|
+
});
|
|
52
|
+
it('formats KB when >= 1024 and < 1MB', () => {
|
|
53
|
+
expect(formatSize(1024)).toBe('1.0 KB');
|
|
54
|
+
expect(formatSize(2048)).toBe('2.0 KB');
|
|
55
|
+
expect(formatSize(512 * 1024)).toBe('512.0 KB');
|
|
56
|
+
expect(formatSize(1024 * 1024 - 1)).toBe('1024.0 KB');
|
|
57
|
+
});
|
|
58
|
+
it('formats MB when >= 1MB', () => {
|
|
59
|
+
expect(formatSize(1024 * 1024)).toBe('1.0 MB');
|
|
60
|
+
expect(formatSize(2457600)).toBe('2.3 MB');
|
|
61
|
+
expect(formatSize(10 * 1024 * 1024)).toBe('10.0 MB');
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
// ─── formatId ───────────────────────────────────────────────────
|
|
65
|
+
describe('formatId', () => {
|
|
66
|
+
it('zero-pads small IDs to 8 characters', () => {
|
|
67
|
+
expect(formatId(42)).toBe('00000042');
|
|
68
|
+
expect(formatId(1)).toBe('00000001');
|
|
69
|
+
expect(formatId(0)).toBe('00000000');
|
|
70
|
+
});
|
|
71
|
+
it('returns full ID when already 8 digits or more', () => {
|
|
72
|
+
expect(formatId(12345678)).toBe('12345678');
|
|
73
|
+
expect(formatId(123456789)).toBe('123456789');
|
|
74
|
+
});
|
|
75
|
+
it('pads 7-digit ID to 8 characters', () => {
|
|
76
|
+
expect(formatId(1234567)).toBe('01234567');
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
// ─── truncate ───────────────────────────────────────────────────
|
|
80
|
+
describe('truncate', () => {
|
|
81
|
+
it('returns short strings as-is', () => {
|
|
82
|
+
expect(truncate('hello', 10)).toBe('hello');
|
|
83
|
+
expect(truncate('exact', 5)).toBe('exact');
|
|
84
|
+
});
|
|
85
|
+
it('truncates long strings with ... suffix', () => {
|
|
86
|
+
expect(truncate('hello world', 8)).toBe('hello...');
|
|
87
|
+
expect(truncate('a very long string that exceeds the limit', 20)).toBe('a very long strin...');
|
|
88
|
+
});
|
|
89
|
+
it('returns empty string as-is', () => {
|
|
90
|
+
expect(truncate('', 10)).toBe('');
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
//# sourceMappingURL=utils.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.test.js","sourceRoot":"","sources":["../../../src/commands/memory/utils.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Commit message review — lightweight validation + optional LLM review.
|
|
3
|
+
*
|
|
4
|
+
* Used by `ghagga review --commit-msg <file>` for the commit-msg hook.
|
|
5
|
+
* Validates basic commit message format (heuristics) and optionally
|
|
6
|
+
* calls a single LLM prompt for quality assessment.
|
|
7
|
+
*
|
|
8
|
+
* Returns a ReviewResult-compatible structure for consistent exit-code handling.
|
|
9
|
+
*/
|
|
10
|
+
import type { LLMProvider, ReviewResult } from 'ghagga-core';
|
|
11
|
+
export interface CommitMsgReviewOptions {
|
|
12
|
+
/** Raw commit message string (file contents) */
|
|
13
|
+
message: string;
|
|
14
|
+
/** LLM provider */
|
|
15
|
+
provider: LLMProvider;
|
|
16
|
+
/** LLM model identifier */
|
|
17
|
+
model: string;
|
|
18
|
+
/** API key for the LLM provider */
|
|
19
|
+
apiKey: string;
|
|
20
|
+
/** When true, skip LLM and use heuristics only */
|
|
21
|
+
quick?: boolean;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Validate a commit message using heuristics and optional LLM review.
|
|
25
|
+
* Returns a ReviewResult for consistent exit-code and output handling.
|
|
26
|
+
*/
|
|
27
|
+
export declare function reviewCommitMessage(opts: CommitMsgReviewOptions): Promise<ReviewResult>;
|
|
28
|
+
//# sourceMappingURL=review-commit-msg.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"review-commit-msg.d.ts","sourceRoot":"","sources":["../../src/commands/review-commit-msg.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EACV,WAAW,EACX,YAAY,EAIb,MAAM,aAAa,CAAC;AAIrB,MAAM,WAAW,sBAAsB;IACrC,gDAAgD;IAChD,OAAO,EAAE,MAAM,CAAC;IAChB,mBAAmB;IACnB,QAAQ,EAAE,WAAW,CAAC;IACtB,2BAA2B;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,mCAAmC;IACnC,MAAM,EAAE,MAAM,CAAC;IACf,kDAAkD;IAClD,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAoFD;;;GAGG;AACH,wBAAsB,mBAAmB,CACvC,IAAI,EAAE,sBAAsB,GAC3B,OAAO,CAAC,YAAY,CAAC,CAyDvB"}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Commit message review — lightweight validation + optional LLM review.
|
|
3
|
+
*
|
|
4
|
+
* Used by `ghagga review --commit-msg <file>` for the commit-msg hook.
|
|
5
|
+
* Validates basic commit message format (heuristics) and optionally
|
|
6
|
+
* calls a single LLM prompt for quality assessment.
|
|
7
|
+
*
|
|
8
|
+
* Returns a ReviewResult-compatible structure for consistent exit-code handling.
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* Strip comment lines (starting with #) from a commit message.
|
|
12
|
+
* Git includes these as hints in the COMMIT_EDITMSG file.
|
|
13
|
+
*/
|
|
14
|
+
function stripComments(message) {
|
|
15
|
+
return message
|
|
16
|
+
.split('\n')
|
|
17
|
+
.filter((line) => !line.startsWith('#'))
|
|
18
|
+
.join('\n')
|
|
19
|
+
.trim();
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Run heuristic validations on a commit message.
|
|
23
|
+
* Returns an array of findings (empty = message is fine).
|
|
24
|
+
*/
|
|
25
|
+
function validateHeuristics(message) {
|
|
26
|
+
const findings = [];
|
|
27
|
+
const cleaned = stripComments(message);
|
|
28
|
+
// Empty message
|
|
29
|
+
if (cleaned.length === 0) {
|
|
30
|
+
findings.push({
|
|
31
|
+
severity: 'high',
|
|
32
|
+
message: 'Commit message is empty',
|
|
33
|
+
suggestion: 'Write a descriptive commit message explaining the change',
|
|
34
|
+
});
|
|
35
|
+
return findings; // No point checking further
|
|
36
|
+
}
|
|
37
|
+
// Too short (likely meaningless like "fix" or "wip")
|
|
38
|
+
if (cleaned.length <= 3) {
|
|
39
|
+
findings.push({
|
|
40
|
+
severity: 'high',
|
|
41
|
+
message: `Commit message is too short (${cleaned.length} chars)`,
|
|
42
|
+
suggestion: 'Write a descriptive message explaining what changed and why',
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
const lines = cleaned.split('\n');
|
|
46
|
+
const subject = lines[0] ?? '';
|
|
47
|
+
// Subject line > 72 chars
|
|
48
|
+
if (subject.length > 72) {
|
|
49
|
+
findings.push({
|
|
50
|
+
severity: 'medium',
|
|
51
|
+
message: `Subject line is ${subject.length} characters (recommended max: 72)`,
|
|
52
|
+
suggestion: 'Keep the subject line concise; move details to the body',
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
// Subject ends with period
|
|
56
|
+
if (subject.endsWith('.')) {
|
|
57
|
+
findings.push({
|
|
58
|
+
severity: 'low',
|
|
59
|
+
message: 'Subject line ends with a period',
|
|
60
|
+
suggestion: 'Remove the trailing period from the subject line',
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
// Body not separated by blank line
|
|
64
|
+
if (lines.length > 1 && lines[1] !== '') {
|
|
65
|
+
findings.push({
|
|
66
|
+
severity: 'medium',
|
|
67
|
+
message: 'Body is not separated from subject by a blank line',
|
|
68
|
+
suggestion: 'Add a blank line between the subject and body',
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
return findings;
|
|
72
|
+
}
|
|
73
|
+
// ─── Main Function ──────────────────────────────────────────────
|
|
74
|
+
/**
|
|
75
|
+
* Validate a commit message using heuristics and optional LLM review.
|
|
76
|
+
* Returns a ReviewResult for consistent exit-code and output handling.
|
|
77
|
+
*/
|
|
78
|
+
export async function reviewCommitMessage(opts) {
|
|
79
|
+
const startTime = Date.now();
|
|
80
|
+
const findings = [];
|
|
81
|
+
// ── Step 1: Heuristic validation ──────────────────────────
|
|
82
|
+
const heuristicFindings = validateHeuristics(opts.message);
|
|
83
|
+
for (const hf of heuristicFindings) {
|
|
84
|
+
findings.push({
|
|
85
|
+
severity: hf.severity,
|
|
86
|
+
category: hf.severity === 'low' ? 'style' : 'convention',
|
|
87
|
+
file: 'COMMIT_EDITMSG',
|
|
88
|
+
message: hf.message,
|
|
89
|
+
suggestion: hf.suggestion,
|
|
90
|
+
source: 'ai', // Use 'ai' as closest match in FindingSource union
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
// ── Step 2: LLM review (skip in quick mode) ───────────────
|
|
94
|
+
// Note: LLM commit message review is a future enhancement.
|
|
95
|
+
// For now, heuristic validation covers the essential checks.
|
|
96
|
+
// When --quick is NOT set and we have a non-empty message,
|
|
97
|
+
// a future version will call a single LLM prompt here.
|
|
98
|
+
// ── Step 3: Determine status ──────────────────────────────
|
|
99
|
+
const hasBlockingIssues = findings.some((f) => f.severity === 'critical' || f.severity === 'high');
|
|
100
|
+
const status = hasBlockingIssues ? 'FAILED' : 'PASSED';
|
|
101
|
+
const executionTimeMs = Date.now() - startTime;
|
|
102
|
+
const summary = findings.length === 0
|
|
103
|
+
? 'Commit message looks good.'
|
|
104
|
+
: `Found ${findings.length} issue(s) in commit message.`;
|
|
105
|
+
return {
|
|
106
|
+
status,
|
|
107
|
+
summary,
|
|
108
|
+
findings,
|
|
109
|
+
staticAnalysis: {
|
|
110
|
+
semgrep: { status: 'skipped', findings: [], executionTimeMs: 0 },
|
|
111
|
+
trivy: { status: 'skipped', findings: [], executionTimeMs: 0 },
|
|
112
|
+
cpd: { status: 'skipped', findings: [], executionTimeMs: 0 },
|
|
113
|
+
},
|
|
114
|
+
memoryContext: null,
|
|
115
|
+
metadata: {
|
|
116
|
+
mode: 'simple',
|
|
117
|
+
provider: opts.quick ? 'none' : opts.provider,
|
|
118
|
+
model: opts.quick ? 'static-only' : opts.model,
|
|
119
|
+
tokensUsed: 0,
|
|
120
|
+
executionTimeMs,
|
|
121
|
+
toolsRun: [],
|
|
122
|
+
toolsSkipped: ['semgrep', 'trivy', 'cpd'],
|
|
123
|
+
},
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
//# sourceMappingURL=review-commit-msg.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"review-commit-msg.js","sourceRoot":"","sources":["../../src/commands/review-commit-msg.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for commit message review (heuristic validation).
|
|
3
|
+
*
|
|
4
|
+
* Validates that reviewCommitMessage() correctly identifies
|
|
5
|
+
* empty, too-short, long subject, trailing period, missing blank line,
|
|
6
|
+
* git comments, and multiple issues. Also verifies quick mode skips AI.
|
|
7
|
+
*
|
|
8
|
+
* @see Phase 4, Test 3
|
|
9
|
+
*/
|
|
10
|
+
export {};
|
|
11
|
+
//# sourceMappingURL=review-commit-msg.test.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"review-commit-msg.test.d.ts","sourceRoot":"","sources":["../../src/commands/review-commit-msg.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG"}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for commit message review (heuristic validation).
|
|
3
|
+
*
|
|
4
|
+
* Validates that reviewCommitMessage() correctly identifies
|
|
5
|
+
* empty, too-short, long subject, trailing period, missing blank line,
|
|
6
|
+
* git comments, and multiple issues. Also verifies quick mode skips AI.
|
|
7
|
+
*
|
|
8
|
+
* @see Phase 4, Test 3
|
|
9
|
+
*/
|
|
10
|
+
import { describe, it, expect } from 'vitest';
|
|
11
|
+
import { reviewCommitMessage } from './review-commit-msg.js';
|
|
12
|
+
// ─── Helpers ────────────────────────────────────────────────────
|
|
13
|
+
/** Default options for tests (quick mode — heuristics only) */
|
|
14
|
+
function makeOpts(message, overrides = {}) {
|
|
15
|
+
return {
|
|
16
|
+
message,
|
|
17
|
+
provider: 'anthropic',
|
|
18
|
+
model: 'claude-sonnet-4-20250514',
|
|
19
|
+
apiKey: 'test-key',
|
|
20
|
+
quick: true,
|
|
21
|
+
...overrides,
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
// ─── Tests ──────────────────────────────────────────────────────
|
|
25
|
+
describe('reviewCommitMessage — heuristic validation', () => {
|
|
26
|
+
it('flags empty message with high severity', async () => {
|
|
27
|
+
const result = await reviewCommitMessage(makeOpts(''));
|
|
28
|
+
expect(result.status).toBe('FAILED');
|
|
29
|
+
expect(result.findings).toHaveLength(1);
|
|
30
|
+
expect(result.findings[0].severity).toBe('high');
|
|
31
|
+
expect(result.findings[0].message).toContain('empty');
|
|
32
|
+
});
|
|
33
|
+
it('flags too-short message (≤ 3 chars) with high severity', async () => {
|
|
34
|
+
const result = await reviewCommitMessage(makeOpts('fix'));
|
|
35
|
+
expect(result.status).toBe('FAILED');
|
|
36
|
+
expect(result.findings.length).toBeGreaterThanOrEqual(1);
|
|
37
|
+
const shortFinding = result.findings.find((f) => f.message.includes('too short'));
|
|
38
|
+
expect(shortFinding).toBeDefined();
|
|
39
|
+
expect(shortFinding.severity).toBe('high');
|
|
40
|
+
});
|
|
41
|
+
it('flags subject line > 72 chars with medium severity', async () => {
|
|
42
|
+
const longSubject = 'a'.repeat(80);
|
|
43
|
+
const result = await reviewCommitMessage(makeOpts(longSubject));
|
|
44
|
+
const finding = result.findings.find((f) => f.message.includes('72'));
|
|
45
|
+
expect(finding).toBeDefined();
|
|
46
|
+
expect(finding.severity).toBe('medium');
|
|
47
|
+
});
|
|
48
|
+
it('flags subject ending with period with low severity', async () => {
|
|
49
|
+
const result = await reviewCommitMessage(makeOpts('Add new feature for users.'));
|
|
50
|
+
const finding = result.findings.find((f) => f.message.includes('period'));
|
|
51
|
+
expect(finding).toBeDefined();
|
|
52
|
+
expect(finding.severity).toBe('low');
|
|
53
|
+
});
|
|
54
|
+
it('flags body not separated by blank line with medium severity', async () => {
|
|
55
|
+
const msg = 'Add feature\nThis is the body without blank line';
|
|
56
|
+
const result = await reviewCommitMessage(makeOpts(msg));
|
|
57
|
+
const finding = result.findings.find((f) => f.message.includes('blank line'));
|
|
58
|
+
expect(finding).toBeDefined();
|
|
59
|
+
expect(finding.severity).toBe('medium');
|
|
60
|
+
});
|
|
61
|
+
it('returns no findings for a valid conventional commit', async () => {
|
|
62
|
+
const msg = 'feat(auth): add OAuth token refresh support';
|
|
63
|
+
const result = await reviewCommitMessage(makeOpts(msg));
|
|
64
|
+
expect(result.status).toBe('PASSED');
|
|
65
|
+
expect(result.findings).toHaveLength(0);
|
|
66
|
+
expect(result.summary).toContain('looks good');
|
|
67
|
+
});
|
|
68
|
+
it('strips git comment lines (# lines) before validation', async () => {
|
|
69
|
+
const msg = [
|
|
70
|
+
'feat: add new feature',
|
|
71
|
+
'',
|
|
72
|
+
'# Please enter the commit message',
|
|
73
|
+
'# Lines starting with # will be ignored.',
|
|
74
|
+
].join('\n');
|
|
75
|
+
const result = await reviewCommitMessage(makeOpts(msg));
|
|
76
|
+
expect(result.status).toBe('PASSED');
|
|
77
|
+
expect(result.findings).toHaveLength(0);
|
|
78
|
+
});
|
|
79
|
+
it('detects multiple issues simultaneously', async () => {
|
|
80
|
+
// Long subject + ends with period + body without blank line
|
|
81
|
+
const longSubject = 'a'.repeat(80) + '.';
|
|
82
|
+
const msg = `${longSubject}\nBody without blank separator`;
|
|
83
|
+
const result = await reviewCommitMessage(makeOpts(msg));
|
|
84
|
+
// Should have at least 3 findings: long subject, period, no blank line
|
|
85
|
+
expect(result.findings.length).toBeGreaterThanOrEqual(3);
|
|
86
|
+
const severities = result.findings.map((f) => f.severity);
|
|
87
|
+
expect(severities).toContain('medium'); // long subject or blank line
|
|
88
|
+
expect(severities).toContain('low'); // period
|
|
89
|
+
});
|
|
90
|
+
it('uses "static-only" model in quick mode metadata', async () => {
|
|
91
|
+
const result = await reviewCommitMessage(makeOpts('feat: valid commit', { quick: true }));
|
|
92
|
+
expect(result.metadata.model).toBe('static-only');
|
|
93
|
+
expect(result.metadata.provider).toBe('none');
|
|
94
|
+
});
|
|
95
|
+
it('records actual provider and model when quick is false', async () => {
|
|
96
|
+
const result = await reviewCommitMessage(makeOpts('feat: valid commit', { quick: false }));
|
|
97
|
+
expect(result.metadata.provider).toBe('anthropic');
|
|
98
|
+
expect(result.metadata.model).toBe('claude-sonnet-4-20250514');
|
|
99
|
+
});
|
|
100
|
+
it('sets file to COMMIT_EDITMSG in all findings', async () => {
|
|
101
|
+
const result = await reviewCommitMessage(makeOpts(''));
|
|
102
|
+
for (const finding of result.findings) {
|
|
103
|
+
expect(finding.file).toBe('COMMIT_EDITMSG');
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
it('skips all static analysis tools', async () => {
|
|
107
|
+
const result = await reviewCommitMessage(makeOpts('feat: valid'));
|
|
108
|
+
expect(result.staticAnalysis.semgrep.status).toBe('skipped');
|
|
109
|
+
expect(result.staticAnalysis.trivy.status).toBe('skipped');
|
|
110
|
+
expect(result.staticAnalysis.cpd.status).toBe('skipped');
|
|
111
|
+
});
|
|
112
|
+
it('returns PASSED status when only low-severity findings exist', async () => {
|
|
113
|
+
const result = await reviewCommitMessage(makeOpts('Add new feature for users.'));
|
|
114
|
+
// Only low severity (period) — should still pass
|
|
115
|
+
const hasCriticalOrHigh = result.findings.some((f) => f.severity === 'critical' || f.severity === 'high');
|
|
116
|
+
expect(hasCriticalOrHigh).toBe(false);
|
|
117
|
+
expect(result.status).toBe('PASSED');
|
|
118
|
+
});
|
|
119
|
+
it('includes valid body separated by blank line without findings', async () => {
|
|
120
|
+
const msg = 'feat(core): add caching layer\n\nThis adds Redis-based caching.';
|
|
121
|
+
const result = await reviewCommitMessage(makeOpts(msg));
|
|
122
|
+
expect(result.status).toBe('PASSED');
|
|
123
|
+
expect(result.findings).toHaveLength(0);
|
|
124
|
+
});
|
|
125
|
+
});
|
|
126
|
+
//# sourceMappingURL=review-commit-msg.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"review-commit-msg.test.js","sourceRoot":"","sources":["../../src/commands/review-commit-msg.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -15,8 +15,15 @@ export interface ReviewOptions {
|
|
|
15
15
|
semgrep: boolean;
|
|
16
16
|
trivy: boolean;
|
|
17
17
|
cpd: boolean;
|
|
18
|
+
memory: boolean;
|
|
19
|
+
/** Memory backend: 'sqlite' (default) or 'engram' */
|
|
20
|
+
memoryBackend?: 'sqlite' | 'engram';
|
|
18
21
|
config?: string;
|
|
19
22
|
verbose: boolean;
|
|
23
|
+
staged?: boolean;
|
|
24
|
+
commitMsg?: string;
|
|
25
|
+
exitOnIssues?: boolean;
|
|
26
|
+
quick?: boolean;
|
|
20
27
|
}
|
|
21
28
|
export declare function reviewCommand(targetPath: string, options: ReviewOptions): Promise<void>;
|
|
22
29
|
//# sourceMappingURL=review.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"review.d.ts","sourceRoot":"","sources":["../../src/commands/review.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;
|
|
1
|
+
{"version":3,"file":"review.d.ts","sourceRoot":"","sources":["../../src/commands/review.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAWH,OAAO,KAAK,EACV,UAAU,EACV,WAAW,EAOZ,MAAM,aAAa,CAAC;AAUrB,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,UAAU,CAAC;IACjB,QAAQ,EAAE,WAAW,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,UAAU,GAAG,MAAM,CAAC;IAC5B,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,OAAO,CAAC;IACf,GAAG,EAAE,OAAO,CAAC;IACb,MAAM,EAAE,OAAO,CAAC;IAChB,qDAAqD;IACrD,aAAa,CAAC,EAAE,QAAQ,GAAG,QAAQ,CAAC;IACpC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,OAAO,CAAC;IAEjB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAgBD,wBAAsB,aAAa,CACjC,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,aAAa,GACrB,OAAO,CAAC,IAAI,CAAC,CAiLf"}
|