ghagga 2.1.0 → 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 +41 -8
- 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/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 +6 -0
- package/dist/commands/review.d.ts.map +1 -1
- package/dist/commands/review.js +105 -10
- package/dist/commands/review.js.map +1 -1
- package/dist/index.d.ts +5 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +21 -7
- package/dist/index.js.map +1 -1
- 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 +10 -0
- package/dist/lib/git.d.ts.map +1 -1
- package/dist/lib/git.js +32 -0
- package/dist/lib/git.js.map +1 -1
- 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/package.json +2 -2
package/README.md
CHANGED
|
@@ -52,7 +52,15 @@ npx ghagga status # Show auth & config
|
|
|
52
52
|
npx ghagga logout # Clear credentials
|
|
53
53
|
```
|
|
54
54
|
|
|
55
|
-
### 4.
|
|
55
|
+
### 4. Install git hooks (optional)
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
npx ghagga hooks install # Auto-review on every commit
|
|
59
|
+
npx ghagga hooks status # Check hook status
|
|
60
|
+
npx ghagga hooks uninstall # Remove hooks
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### 5. Manage review memory
|
|
56
64
|
|
|
57
65
|
```bash
|
|
58
66
|
npx ghagga memory list # List stored observations
|
|
@@ -98,9 +106,29 @@ Options:
|
|
|
98
106
|
--no-trivy Disable Trivy vulnerability scanning
|
|
99
107
|
--no-cpd Disable CPD duplicate detection
|
|
100
108
|
--no-memory Disable review memory (skip search and persist)
|
|
109
|
+
--memory-backend <type> Memory backend: sqlite (default) or engram
|
|
110
|
+
--staged Review only staged files (for pre-commit hook)
|
|
111
|
+
--quick Static analysis only, skip AI review (~5-10s)
|
|
112
|
+
--commit-msg <file> Validate commit message from file
|
|
113
|
+
--exit-on-issues Exit with code 1 if critical/high issues found
|
|
101
114
|
-c, --config <path> Path to .ghagga.json config file
|
|
102
115
|
```
|
|
103
116
|
|
|
117
|
+
## Git Hooks
|
|
118
|
+
|
|
119
|
+
Install git hooks for automatic code review on every commit:
|
|
120
|
+
|
|
121
|
+
```bash
|
|
122
|
+
ghagga hooks install # Install pre-commit + commit-msg hooks
|
|
123
|
+
ghagga hooks install --force # Overwrite existing hooks (backs up originals)
|
|
124
|
+
ghagga hooks install --pre-commit # Only pre-commit hook
|
|
125
|
+
ghagga hooks install --commit-msg # Only commit-msg hook
|
|
126
|
+
ghagga hooks uninstall # Remove GHAGGA-managed hooks
|
|
127
|
+
ghagga hooks status # Show hook status
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
Hooks auto-detect `ghagga` in PATH and fail gracefully if not found. Installed hooks use `--plain --exit-on-issues` automatically.
|
|
131
|
+
|
|
104
132
|
## Memory Subcommands
|
|
105
133
|
|
|
106
134
|
```bash
|
|
@@ -160,10 +188,13 @@ ghagga review --provider ollama
|
|
|
160
188
|
## Environment Variables
|
|
161
189
|
|
|
162
190
|
```bash
|
|
163
|
-
GHAGGA_API_KEY=<key>
|
|
164
|
-
GHAGGA_PROVIDER=<provider>
|
|
165
|
-
GHAGGA_MODEL=<model>
|
|
166
|
-
|
|
191
|
+
GHAGGA_API_KEY=<key> # API key for the LLM provider
|
|
192
|
+
GHAGGA_PROVIDER=<provider> # LLM provider override
|
|
193
|
+
GHAGGA_MODEL=<model> # Model identifier override
|
|
194
|
+
GHAGGA_MEMORY_BACKEND=<type> # Memory backend: sqlite (default) or engram
|
|
195
|
+
GHAGGA_ENGRAM_HOST=<url> # Engram server URL (default: http://localhost:7437)
|
|
196
|
+
GHAGGA_ENGRAM_TIMEOUT=<seconds> # Engram connection timeout (default: 5)
|
|
197
|
+
GITHUB_TOKEN=<token> # GitHub token (fallback for github provider)
|
|
167
198
|
```
|
|
168
199
|
|
|
169
200
|
## Config File
|
|
@@ -183,14 +214,16 @@ Create a `.ghagga.json` in your project root:
|
|
|
183
214
|
|
|
184
215
|
## How It Works
|
|
185
216
|
|
|
186
|
-
1. Gets your `git diff` (staged or uncommitted changes)
|
|
217
|
+
1. Gets your `git diff` (staged or uncommitted changes; `--staged` uses `git diff --cached`)
|
|
187
218
|
2. Parses the diff and detects tech stacks
|
|
188
219
|
3. Runs static analysis (Semgrep, Trivy, CPD) if available
|
|
189
|
-
4. Searches
|
|
190
|
-
5. Sends the diff + static findings + memory context to the AI review agent
|
|
220
|
+
4. Searches memory for relevant past observations (SQLite + FTS5 at `~/.config/ghagga/memory.db`, or Engram if `--memory-backend engram`)
|
|
221
|
+
5. Sends the diff + static findings + memory context to the AI review agent (skipped with `--quick`)
|
|
191
222
|
6. Returns findings with severity, file, line, and suggestions
|
|
192
223
|
7. Persists new observations (decisions, patterns, bug fixes) to local memory for future reviews
|
|
193
224
|
|
|
225
|
+
With `ghagga hooks install`, steps 1-7 run automatically on every commit via pre-commit and commit-msg hooks.
|
|
226
|
+
|
|
194
227
|
## Requirements
|
|
195
228
|
|
|
196
229
|
- Node.js >= 20
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hooks command group barrel.
|
|
3
|
+
*
|
|
4
|
+
* Registers the `ghagga hooks` subcommands (install, uninstall, status)
|
|
5
|
+
* and exports the parent Command for registration in the CLI entry point.
|
|
6
|
+
*/
|
|
7
|
+
import { Command } from 'commander';
|
|
8
|
+
export declare const hooksCommand: Command;
|
|
9
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/commands/hooks/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAKpC,eAAO,MAAM,YAAY,SACmC,CAAC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hooks command group barrel.
|
|
3
|
+
*
|
|
4
|
+
* Registers the `ghagga hooks` subcommands (install, uninstall, status)
|
|
5
|
+
* and exports the parent Command for registration in the CLI entry point.
|
|
6
|
+
*/
|
|
7
|
+
import { Command } from 'commander';
|
|
8
|
+
import { registerInstallCommand } from './install.js';
|
|
9
|
+
import { registerUninstallCommand } from './uninstall.js';
|
|
10
|
+
import { registerStatusCommand } from './status.js';
|
|
11
|
+
export const hooksCommand = new Command('hooks')
|
|
12
|
+
.description('Manage git hooks for automated code review');
|
|
13
|
+
registerInstallCommand(hooksCommand);
|
|
14
|
+
registerUninstallCommand(hooksCommand);
|
|
15
|
+
registerStatusCommand(hooksCommand);
|
|
16
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/commands/hooks/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,sBAAsB,EAAE,MAAM,cAAc,CAAC;AACtD,OAAO,EAAE,wBAAwB,EAAE,MAAM,gBAAgB,CAAC;AAC1D,OAAO,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AAEpD,MAAM,CAAC,MAAM,YAAY,GAAG,IAAI,OAAO,CAAC,OAAO,CAAC;KAC7C,WAAW,CAAC,4CAA4C,CAAC,CAAC;AAE7D,sBAAsB,CAAC,YAAY,CAAC,CAAC;AACrC,wBAAwB,CAAC,YAAY,CAAC,CAAC;AACvC,qBAAqB,CAAC,YAAY,CAAC,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `ghagga hooks install` subcommand.
|
|
3
|
+
*
|
|
4
|
+
* Installs GHAGGA-managed pre-commit and/or commit-msg hooks into the
|
|
5
|
+
* current git repository. Handles backup of existing non-GHAGGA hooks,
|
|
6
|
+
* idempotent reinstall of GHAGGA hooks, and the --force flag for
|
|
7
|
+
* overwriting external hooks.
|
|
8
|
+
*
|
|
9
|
+
* @see Phase 3, Task 3.2
|
|
10
|
+
*/
|
|
11
|
+
import { Command } from 'commander';
|
|
12
|
+
export declare function registerInstallCommand(parent: Command): void;
|
|
13
|
+
//# sourceMappingURL=install.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"install.d.ts","sourceRoot":"","sources":["../../../src/commands/hooks/install.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAYpC,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,OAAO,GAAG,IAAI,CAwD5D"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `ghagga hooks install` subcommand.
|
|
3
|
+
*
|
|
4
|
+
* Installs GHAGGA-managed pre-commit and/or commit-msg hooks into the
|
|
5
|
+
* current git repository. Handles backup of existing non-GHAGGA hooks,
|
|
6
|
+
* idempotent reinstall of GHAGGA hooks, and the --force flag for
|
|
7
|
+
* overwriting external hooks.
|
|
8
|
+
*
|
|
9
|
+
* @see Phase 3, Task 3.2
|
|
10
|
+
*/
|
|
11
|
+
import { isGitRepo, getHooksDir, installHook } from '../../lib/git-hooks.js';
|
|
12
|
+
import { generatePreCommitHook, generateCommitMsgHook } from '../../lib/hook-templates.js';
|
|
13
|
+
import * as tui from '../../ui/tui.js';
|
|
14
|
+
export function registerInstallCommand(parent) {
|
|
15
|
+
parent
|
|
16
|
+
.command('install')
|
|
17
|
+
.description('Install git hooks for automated code review')
|
|
18
|
+
.option('--force', 'Overwrite existing non-GHAGGA hooks (with backup)')
|
|
19
|
+
.option('--pre-commit', 'Install only the pre-commit hook')
|
|
20
|
+
.option('--commit-msg', 'Install only the commit-msg hook')
|
|
21
|
+
.action((opts) => {
|
|
22
|
+
if (!isGitRepo()) {
|
|
23
|
+
tui.log.error('Not a git repository. Run this command from inside a git repo.');
|
|
24
|
+
process.exit(1);
|
|
25
|
+
}
|
|
26
|
+
const hooksDir = getHooksDir();
|
|
27
|
+
const force = opts.force ?? false;
|
|
28
|
+
// Determine which hooks to install.
|
|
29
|
+
// If neither flag is set, install both. If one or both are set, install only those.
|
|
30
|
+
const installPreCommit = opts.preCommit || (!opts.preCommit && !opts.commitMsg);
|
|
31
|
+
const installCommitMsg = opts.commitMsg || (!opts.preCommit && !opts.commitMsg);
|
|
32
|
+
const hooks = [];
|
|
33
|
+
if (installPreCommit) {
|
|
34
|
+
hooks.push({
|
|
35
|
+
type: 'pre-commit',
|
|
36
|
+
content: generatePreCommitHook(),
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
if (installCommitMsg) {
|
|
40
|
+
hooks.push({
|
|
41
|
+
type: 'commit-msg',
|
|
42
|
+
content: generateCommitMsgHook(),
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
let installed = 0;
|
|
46
|
+
for (const hook of hooks) {
|
|
47
|
+
const result = installHook(hooksDir, hook.type, hook.content, force);
|
|
48
|
+
if (result.success) {
|
|
49
|
+
tui.log.success(result.message);
|
|
50
|
+
installed++;
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
tui.log.error(result.message);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
if (installed > 0) {
|
|
57
|
+
tui.log.info(`\nInstalled ${installed} hook(s) to ${hooksDir}`);
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
tui.log.warn('No hooks were installed.');
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
//# sourceMappingURL=install.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"install.js","sourceRoot":"","sources":["../../../src/commands/hooks/install.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAGH,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAC7E,OAAO,EAAE,qBAAqB,EAAE,qBAAqB,EAAE,MAAM,6BAA6B,CAAC;AAE3F,OAAO,KAAK,GAAG,MAAM,iBAAiB,CAAC;AAQvC,MAAM,UAAU,sBAAsB,CAAC,MAAe;IACpD,MAAM;SACH,OAAO,CAAC,SAAS,CAAC;SAClB,WAAW,CAAC,6CAA6C,CAAC;SAC1D,MAAM,CAAC,SAAS,EAAE,mDAAmD,CAAC;SACtE,MAAM,CAAC,cAAc,EAAE,kCAAkC,CAAC;SAC1D,MAAM,CAAC,cAAc,EAAE,kCAAkC,CAAC;SAC1D,MAAM,CAAC,CAAC,IAAoB,EAAE,EAAE;QAC/B,IAAI,CAAC,SAAS,EAAE,EAAE,CAAC;YACjB,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,gEAAgE,CAAC,CAAC;YAChF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;QAED,MAAM,QAAQ,GAAG,WAAW,EAAE,CAAC;QAC/B,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,IAAI,KAAK,CAAC;QAElC,oCAAoC;QACpC,oFAAoF;QACpF,MAAM,gBAAgB,GAAG,IAAI,CAAC,SAAS,IAAI,CAAC,CAAC,IAAI,CAAC,SAAS,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QAChF,MAAM,gBAAgB,GAAG,IAAI,CAAC,SAAS,IAAI,CAAC,CAAC,IAAI,CAAC,SAAS,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QAEhF,MAAM,KAAK,GAA+C,EAAE,CAAC;QAE7D,IAAI,gBAAgB,EAAE,CAAC;YACrB,KAAK,CAAC,IAAI,CAAC;gBACT,IAAI,EAAE,YAAY;gBAClB,OAAO,EAAE,qBAAqB,EAAE;aACjC,CAAC,CAAC;QACL,CAAC;QAED,IAAI,gBAAgB,EAAE,CAAC;YACrB,KAAK,CAAC,IAAI,CAAC;gBACT,IAAI,EAAE,YAAY;gBAClB,OAAO,EAAE,qBAAqB,EAAE;aACjC,CAAC,CAAC;QACL,CAAC;QAED,IAAI,SAAS,GAAG,CAAC,CAAC;QAElB,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,MAAM,MAAM,GAAG,WAAW,CAAC,QAAQ,EAAE,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;YAErE,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;gBACnB,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;gBAChC,SAAS,EAAE,CAAC;YACd,CAAC;iBAAM,CAAC;gBACN,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;YAChC,CAAC;QACH,CAAC;QAED,IAAI,SAAS,GAAG,CAAC,EAAE,CAAC;YAClB,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,eAAe,SAAS,eAAe,QAAQ,EAAE,CAAC,CAAC;QAClE,CAAC;aAAM,CAAC;YACN,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,0BAA0B,CAAC,CAAC;QAC3C,CAAC;IACH,CAAC,CAAC,CAAC;AACP,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for `ghagga hooks install` subcommand.
|
|
3
|
+
*
|
|
4
|
+
* Mocks git-hooks utilities, hook-templates, and the TUI layer.
|
|
5
|
+
* Tests hook selection (both, --pre-commit, --commit-msg),
|
|
6
|
+
* --force flag, non-git-repo error, and success/failure messages.
|
|
7
|
+
*
|
|
8
|
+
* @see Phase 4, Test 4
|
|
9
|
+
*/
|
|
10
|
+
export {};
|
|
11
|
+
//# sourceMappingURL=install.test.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"install.test.d.ts","sourceRoot":"","sources":["../../../src/commands/hooks/install.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG"}
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for `ghagga hooks install` subcommand.
|
|
3
|
+
*
|
|
4
|
+
* Mocks git-hooks utilities, hook-templates, and the TUI layer.
|
|
5
|
+
* Tests hook selection (both, --pre-commit, --commit-msg),
|
|
6
|
+
* --force flag, non-git-repo error, and success/failure messages.
|
|
7
|
+
*
|
|
8
|
+
* @see Phase 4, Test 4
|
|
9
|
+
*/
|
|
10
|
+
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
|
11
|
+
import { Command } from 'commander';
|
|
12
|
+
// ─── Mocks ──────────────────────────────────────────────────────
|
|
13
|
+
const { mockIsGitRepo, mockGetHooksDir, mockInstallHook, mockGenPreCommit, mockGenCommitMsg } = vi.hoisted(() => ({
|
|
14
|
+
mockIsGitRepo: vi.fn(),
|
|
15
|
+
mockGetHooksDir: vi.fn(),
|
|
16
|
+
mockInstallHook: vi.fn(),
|
|
17
|
+
mockGenPreCommit: vi.fn(),
|
|
18
|
+
mockGenCommitMsg: vi.fn(),
|
|
19
|
+
}));
|
|
20
|
+
vi.mock('../../lib/git-hooks.js', () => ({
|
|
21
|
+
isGitRepo: (...args) => mockIsGitRepo(...args),
|
|
22
|
+
getHooksDir: (...args) => mockGetHooksDir(...args),
|
|
23
|
+
installHook: (...args) => mockInstallHook(...args),
|
|
24
|
+
}));
|
|
25
|
+
vi.mock('../../lib/hook-templates.js', () => ({
|
|
26
|
+
generatePreCommitHook: (...args) => mockGenPreCommit(...args),
|
|
27
|
+
generateCommitMsgHook: (...args) => mockGenCommitMsg(...args),
|
|
28
|
+
}));
|
|
29
|
+
vi.mock('../../ui/tui.js', () => ({
|
|
30
|
+
log: {
|
|
31
|
+
success: vi.fn(),
|
|
32
|
+
error: vi.fn(),
|
|
33
|
+
info: vi.fn(),
|
|
34
|
+
warn: vi.fn(),
|
|
35
|
+
},
|
|
36
|
+
}));
|
|
37
|
+
import { registerInstallCommand } from './install.js';
|
|
38
|
+
import * as tui from '../../ui/tui.js';
|
|
39
|
+
// ─── Helpers ────────────────────────────────────────────────────
|
|
40
|
+
class ProcessExitError extends Error {
|
|
41
|
+
code;
|
|
42
|
+
constructor(code) {
|
|
43
|
+
super(`process.exit(${code})`);
|
|
44
|
+
this.code = code;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
async function runInstallCommand(args = []) {
|
|
48
|
+
const parent = new Command('hooks');
|
|
49
|
+
registerInstallCommand(parent);
|
|
50
|
+
try {
|
|
51
|
+
await parent.parseAsync(['install', ...args], { from: 'user' });
|
|
52
|
+
}
|
|
53
|
+
catch (err) {
|
|
54
|
+
if (!(err instanceof ProcessExitError))
|
|
55
|
+
throw err;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
// ─── Setup ──────────────────────────────────────────────────────
|
|
59
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
60
|
+
let exitSpy;
|
|
61
|
+
beforeEach(() => {
|
|
62
|
+
vi.clearAllMocks();
|
|
63
|
+
exitSpy = vi
|
|
64
|
+
.spyOn(process, 'exit')
|
|
65
|
+
.mockImplementation(((code) => {
|
|
66
|
+
throw new ProcessExitError(code);
|
|
67
|
+
}));
|
|
68
|
+
// Defaults: in a git repo, hooks dir exists
|
|
69
|
+
mockIsGitRepo.mockReturnValue(true);
|
|
70
|
+
mockGetHooksDir.mockReturnValue('/repo/.git/hooks');
|
|
71
|
+
mockGenPreCommit.mockReturnValue('pre-commit-content');
|
|
72
|
+
mockGenCommitMsg.mockReturnValue('commit-msg-content');
|
|
73
|
+
mockInstallHook.mockReturnValue({
|
|
74
|
+
type: 'pre-commit',
|
|
75
|
+
success: true,
|
|
76
|
+
message: 'Installed pre-commit hook',
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
afterEach(() => {
|
|
80
|
+
exitSpy.mockRestore();
|
|
81
|
+
});
|
|
82
|
+
// ─── Tests ──────────────────────────────────────────────────────
|
|
83
|
+
describe('ghagga hooks install', () => {
|
|
84
|
+
it('installs both hooks by default', async () => {
|
|
85
|
+
mockInstallHook
|
|
86
|
+
.mockReturnValueOnce({ type: 'pre-commit', success: true, message: 'Installed pre-commit hook' })
|
|
87
|
+
.mockReturnValueOnce({ type: 'commit-msg', success: true, message: 'Installed commit-msg hook' });
|
|
88
|
+
await runInstallCommand();
|
|
89
|
+
expect(mockInstallHook).toHaveBeenCalledTimes(2);
|
|
90
|
+
expect(mockInstallHook).toHaveBeenCalledWith('/repo/.git/hooks', 'pre-commit', 'pre-commit-content', false);
|
|
91
|
+
expect(mockInstallHook).toHaveBeenCalledWith('/repo/.git/hooks', 'commit-msg', 'commit-msg-content', false);
|
|
92
|
+
expect(tui.log.success).toHaveBeenCalledTimes(2);
|
|
93
|
+
});
|
|
94
|
+
it('installs only pre-commit when --pre-commit is passed', async () => {
|
|
95
|
+
mockInstallHook.mockReturnValue({
|
|
96
|
+
type: 'pre-commit', success: true, message: 'Installed pre-commit hook',
|
|
97
|
+
});
|
|
98
|
+
await runInstallCommand(['--pre-commit']);
|
|
99
|
+
expect(mockInstallHook).toHaveBeenCalledTimes(1);
|
|
100
|
+
expect(mockInstallHook).toHaveBeenCalledWith('/repo/.git/hooks', 'pre-commit', 'pre-commit-content', false);
|
|
101
|
+
});
|
|
102
|
+
it('installs only commit-msg when --commit-msg is passed', async () => {
|
|
103
|
+
mockInstallHook.mockReturnValue({
|
|
104
|
+
type: 'commit-msg', success: true, message: 'Installed commit-msg hook',
|
|
105
|
+
});
|
|
106
|
+
await runInstallCommand(['--commit-msg']);
|
|
107
|
+
expect(mockInstallHook).toHaveBeenCalledTimes(1);
|
|
108
|
+
expect(mockInstallHook).toHaveBeenCalledWith('/repo/.git/hooks', 'commit-msg', 'commit-msg-content', false);
|
|
109
|
+
});
|
|
110
|
+
it('passes force flag when --force is used', async () => {
|
|
111
|
+
mockInstallHook.mockReturnValue({
|
|
112
|
+
type: 'pre-commit', success: true, message: 'Installed',
|
|
113
|
+
});
|
|
114
|
+
await runInstallCommand(['--force', '--pre-commit']);
|
|
115
|
+
expect(mockInstallHook).toHaveBeenCalledWith('/repo/.git/hooks', 'pre-commit', 'pre-commit-content', true);
|
|
116
|
+
});
|
|
117
|
+
it('exits with error when not in a git repo', async () => {
|
|
118
|
+
mockIsGitRepo.mockReturnValue(false);
|
|
119
|
+
await runInstallCommand();
|
|
120
|
+
expect(exitSpy).toHaveBeenCalledWith(1);
|
|
121
|
+
expect(tui.log.error).toHaveBeenCalledWith(expect.stringContaining('Not a git repository'));
|
|
122
|
+
expect(mockInstallHook).not.toHaveBeenCalled();
|
|
123
|
+
});
|
|
124
|
+
it('shows error message when install fails', async () => {
|
|
125
|
+
mockInstallHook
|
|
126
|
+
.mockReturnValueOnce({
|
|
127
|
+
type: 'pre-commit',
|
|
128
|
+
success: false,
|
|
129
|
+
message: 'Hook pre-commit already exists (not managed by GHAGGA). Use --force to overwrite.',
|
|
130
|
+
})
|
|
131
|
+
.mockReturnValueOnce({
|
|
132
|
+
type: 'commit-msg',
|
|
133
|
+
success: true,
|
|
134
|
+
message: 'Installed commit-msg hook',
|
|
135
|
+
});
|
|
136
|
+
await runInstallCommand();
|
|
137
|
+
expect(tui.log.error).toHaveBeenCalledWith(expect.stringContaining('--force'));
|
|
138
|
+
expect(tui.log.success).toHaveBeenCalledWith(expect.stringContaining('Installed commit-msg'));
|
|
139
|
+
});
|
|
140
|
+
it('shows "no hooks installed" warning when all installs fail', async () => {
|
|
141
|
+
mockInstallHook.mockReturnValue({
|
|
142
|
+
type: 'pre-commit',
|
|
143
|
+
success: false,
|
|
144
|
+
message: 'Failed',
|
|
145
|
+
});
|
|
146
|
+
await runInstallCommand();
|
|
147
|
+
expect(tui.log.warn).toHaveBeenCalledWith(expect.stringContaining('No hooks were installed'));
|
|
148
|
+
});
|
|
149
|
+
it('shows install count summary on success', async () => {
|
|
150
|
+
mockInstallHook
|
|
151
|
+
.mockReturnValueOnce({ type: 'pre-commit', success: true, message: 'ok' })
|
|
152
|
+
.mockReturnValueOnce({ type: 'commit-msg', success: true, message: 'ok' });
|
|
153
|
+
await runInstallCommand();
|
|
154
|
+
expect(tui.log.info).toHaveBeenCalledWith(expect.stringContaining('Installed 2 hook(s)'));
|
|
155
|
+
});
|
|
156
|
+
});
|
|
157
|
+
//# sourceMappingURL=install.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"install.test.js","sourceRoot":"","sources":["../../../src/commands/hooks/install.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `ghagga hooks status` subcommand.
|
|
3
|
+
*
|
|
4
|
+
* Shows the status of pre-commit and commit-msg hooks in the
|
|
5
|
+
* current git repository: not installed, installed (GHAGGA-managed),
|
|
6
|
+
* or installed (external).
|
|
7
|
+
*
|
|
8
|
+
* @see Phase 3, Task 3.4
|
|
9
|
+
*/
|
|
10
|
+
import { Command } from 'commander';
|
|
11
|
+
export declare function registerStatusCommand(parent: Command): void;
|
|
12
|
+
//# sourceMappingURL=status.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../../../src/commands/hooks/status.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAOpC,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,OAAO,GAAG,IAAI,CA0B3D"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `ghagga hooks status` subcommand.
|
|
3
|
+
*
|
|
4
|
+
* Shows the status of pre-commit and commit-msg hooks in the
|
|
5
|
+
* current git repository: not installed, installed (GHAGGA-managed),
|
|
6
|
+
* or installed (external).
|
|
7
|
+
*
|
|
8
|
+
* @see Phase 3, Task 3.4
|
|
9
|
+
*/
|
|
10
|
+
import { isGitRepo, getHooksDir, getHookStatus } from '../../lib/git-hooks.js';
|
|
11
|
+
import * as tui from '../../ui/tui.js';
|
|
12
|
+
const HOOK_TYPES = ['pre-commit', 'commit-msg'];
|
|
13
|
+
export function registerStatusCommand(parent) {
|
|
14
|
+
parent
|
|
15
|
+
.command('status')
|
|
16
|
+
.description('Show status of git hooks')
|
|
17
|
+
.action(() => {
|
|
18
|
+
if (!isGitRepo()) {
|
|
19
|
+
tui.log.error('Not a git repository. Run this command from inside a git repo.');
|
|
20
|
+
process.exit(1);
|
|
21
|
+
}
|
|
22
|
+
const hooksDir = getHooksDir();
|
|
23
|
+
tui.log.info(`Hooks directory: ${hooksDir}\n`);
|
|
24
|
+
for (const hookType of HOOK_TYPES) {
|
|
25
|
+
const status = getHookStatus(hooksDir, hookType);
|
|
26
|
+
if (!status.installed) {
|
|
27
|
+
tui.log.info(` ${hookType}: not installed`);
|
|
28
|
+
}
|
|
29
|
+
else if (status.managedByGhagga) {
|
|
30
|
+
tui.log.success(` ${hookType}: installed (GHAGGA-managed)`);
|
|
31
|
+
}
|
|
32
|
+
else {
|
|
33
|
+
tui.log.warn(` ${hookType}: installed (external — not managed by GHAGGA)`);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=status.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"status.js","sourceRoot":"","sources":["../../../src/commands/hooks/status.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAE/E,OAAO,KAAK,GAAG,MAAM,iBAAiB,CAAC;AAEvC,MAAM,UAAU,GAAe,CAAC,YAAY,EAAE,YAAY,CAAC,CAAC;AAE5D,MAAM,UAAU,qBAAqB,CAAC,MAAe;IACnD,MAAM;SACH,OAAO,CAAC,QAAQ,CAAC;SACjB,WAAW,CAAC,0BAA0B,CAAC;SACvC,MAAM,CAAC,GAAG,EAAE;QACX,IAAI,CAAC,SAAS,EAAE,EAAE,CAAC;YACjB,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,gEAAgE,CAAC,CAAC;YAChF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;QAED,MAAM,QAAQ,GAAG,WAAW,EAAE,CAAC;QAE/B,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,oBAAoB,QAAQ,IAAI,CAAC,CAAC;QAE/C,KAAK,MAAM,QAAQ,IAAI,UAAU,EAAE,CAAC;YAClC,MAAM,MAAM,GAAG,aAAa,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;YAEjD,IAAI,CAAC,MAAM,CAAC,SAAS,EAAE,CAAC;gBACtB,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,QAAQ,iBAAiB,CAAC,CAAC;YAC/C,CAAC;iBAAM,IAAI,MAAM,CAAC,eAAe,EAAE,CAAC;gBAClC,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,KAAK,QAAQ,8BAA8B,CAAC,CAAC;YAC/D,CAAC;iBAAM,CAAC;gBACN,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,QAAQ,gDAAgD,CAAC,CAAC;YAC9E,CAAC;QACH,CAAC;IACH,CAAC,CAAC,CAAC;AACP,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for `ghagga hooks status` subcommand.
|
|
3
|
+
*
|
|
4
|
+
* Mocks git-hooks utilities and the TUI layer.
|
|
5
|
+
* Tests display of hook status for both hooks, non-git-repo error,
|
|
6
|
+
* and correct labels for not-installed / GHAGGA / external hooks.
|
|
7
|
+
*
|
|
8
|
+
* @see Phase 4, Test 6
|
|
9
|
+
*/
|
|
10
|
+
export {};
|
|
11
|
+
//# sourceMappingURL=status.test.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"status.test.d.ts","sourceRoot":"","sources":["../../../src/commands/hooks/status.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG"}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for `ghagga hooks status` subcommand.
|
|
3
|
+
*
|
|
4
|
+
* Mocks git-hooks utilities and the TUI layer.
|
|
5
|
+
* Tests display of hook status for both hooks, non-git-repo error,
|
|
6
|
+
* and correct labels for not-installed / GHAGGA / external hooks.
|
|
7
|
+
*
|
|
8
|
+
* @see Phase 4, Test 6
|
|
9
|
+
*/
|
|
10
|
+
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
|
11
|
+
import { Command } from 'commander';
|
|
12
|
+
// ─── Mocks ──────────────────────────────────────────────────────
|
|
13
|
+
const { mockIsGitRepo, mockGetHooksDir, mockGetHookStatus } = vi.hoisted(() => ({
|
|
14
|
+
mockIsGitRepo: vi.fn(),
|
|
15
|
+
mockGetHooksDir: vi.fn(),
|
|
16
|
+
mockGetHookStatus: vi.fn(),
|
|
17
|
+
}));
|
|
18
|
+
vi.mock('../../lib/git-hooks.js', () => ({
|
|
19
|
+
isGitRepo: (...args) => mockIsGitRepo(...args),
|
|
20
|
+
getHooksDir: (...args) => mockGetHooksDir(...args),
|
|
21
|
+
getHookStatus: (...args) => mockGetHookStatus(...args),
|
|
22
|
+
}));
|
|
23
|
+
vi.mock('../../ui/tui.js', () => ({
|
|
24
|
+
log: {
|
|
25
|
+
success: vi.fn(),
|
|
26
|
+
error: vi.fn(),
|
|
27
|
+
info: vi.fn(),
|
|
28
|
+
warn: vi.fn(),
|
|
29
|
+
},
|
|
30
|
+
}));
|
|
31
|
+
import { registerStatusCommand } from './status.js';
|
|
32
|
+
import * as tui from '../../ui/tui.js';
|
|
33
|
+
// ─── Helpers ────────────────────────────────────────────────────
|
|
34
|
+
class ProcessExitError extends Error {
|
|
35
|
+
code;
|
|
36
|
+
constructor(code) {
|
|
37
|
+
super(`process.exit(${code})`);
|
|
38
|
+
this.code = code;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
async function runStatusCommand(args = []) {
|
|
42
|
+
const parent = new Command('hooks');
|
|
43
|
+
registerStatusCommand(parent);
|
|
44
|
+
try {
|
|
45
|
+
await parent.parseAsync(['status', ...args], { from: 'user' });
|
|
46
|
+
}
|
|
47
|
+
catch (err) {
|
|
48
|
+
if (!(err instanceof ProcessExitError))
|
|
49
|
+
throw err;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
// ─── Setup ──────────────────────────────────────────────────────
|
|
53
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
54
|
+
let exitSpy;
|
|
55
|
+
beforeEach(() => {
|
|
56
|
+
vi.clearAllMocks();
|
|
57
|
+
exitSpy = vi
|
|
58
|
+
.spyOn(process, 'exit')
|
|
59
|
+
.mockImplementation(((code) => {
|
|
60
|
+
throw new ProcessExitError(code);
|
|
61
|
+
}));
|
|
62
|
+
mockIsGitRepo.mockReturnValue(true);
|
|
63
|
+
mockGetHooksDir.mockReturnValue('/repo/.git/hooks');
|
|
64
|
+
});
|
|
65
|
+
afterEach(() => {
|
|
66
|
+
exitSpy.mockRestore();
|
|
67
|
+
});
|
|
68
|
+
// ─── Tests ──────────────────────────────────────────────────────
|
|
69
|
+
describe('ghagga hooks status', () => {
|
|
70
|
+
it('shows status for both pre-commit and commit-msg hooks', async () => {
|
|
71
|
+
mockGetHookStatus
|
|
72
|
+
.mockReturnValueOnce({ type: 'pre-commit', installed: true, managedByGhagga: true, path: '/repo/.git/hooks/pre-commit' })
|
|
73
|
+
.mockReturnValueOnce({ type: 'commit-msg', installed: false, managedByGhagga: false, path: '/repo/.git/hooks/commit-msg' });
|
|
74
|
+
await runStatusCommand();
|
|
75
|
+
expect(mockGetHookStatus).toHaveBeenCalledTimes(2);
|
|
76
|
+
expect(mockGetHookStatus).toHaveBeenCalledWith('/repo/.git/hooks', 'pre-commit');
|
|
77
|
+
expect(mockGetHookStatus).toHaveBeenCalledWith('/repo/.git/hooks', 'commit-msg');
|
|
78
|
+
});
|
|
79
|
+
it('exits with error when not in a git repo', async () => {
|
|
80
|
+
mockIsGitRepo.mockReturnValue(false);
|
|
81
|
+
await runStatusCommand();
|
|
82
|
+
expect(exitSpy).toHaveBeenCalledWith(1);
|
|
83
|
+
expect(tui.log.error).toHaveBeenCalledWith(expect.stringContaining('Not a git repository'));
|
|
84
|
+
expect(mockGetHookStatus).not.toHaveBeenCalled();
|
|
85
|
+
});
|
|
86
|
+
it('shows "not installed" for hooks that do not exist', async () => {
|
|
87
|
+
mockGetHookStatus.mockReturnValue({
|
|
88
|
+
type: 'pre-commit', installed: false, managedByGhagga: false, path: '/repo/.git/hooks/pre-commit',
|
|
89
|
+
});
|
|
90
|
+
await runStatusCommand();
|
|
91
|
+
expect(tui.log.info).toHaveBeenCalledWith(expect.stringContaining('not installed'));
|
|
92
|
+
});
|
|
93
|
+
it('shows "GHAGGA-managed" for installed GHAGGA hooks', async () => {
|
|
94
|
+
mockGetHookStatus.mockReturnValue({
|
|
95
|
+
type: 'pre-commit', installed: true, managedByGhagga: true, path: '/repo/.git/hooks/pre-commit',
|
|
96
|
+
});
|
|
97
|
+
await runStatusCommand();
|
|
98
|
+
expect(tui.log.success).toHaveBeenCalledWith(expect.stringContaining('GHAGGA-managed'));
|
|
99
|
+
});
|
|
100
|
+
it('shows "external" for installed non-GHAGGA hooks', async () => {
|
|
101
|
+
mockGetHookStatus.mockReturnValue({
|
|
102
|
+
type: 'pre-commit', installed: true, managedByGhagga: false, path: '/repo/.git/hooks/pre-commit',
|
|
103
|
+
});
|
|
104
|
+
await runStatusCommand();
|
|
105
|
+
expect(tui.log.warn).toHaveBeenCalledWith(expect.stringContaining('external'));
|
|
106
|
+
});
|
|
107
|
+
it('shows hooks directory path', async () => {
|
|
108
|
+
mockGetHookStatus.mockReturnValue({
|
|
109
|
+
type: 'pre-commit', installed: false, managedByGhagga: false, path: '/repo/.git/hooks/pre-commit',
|
|
110
|
+
});
|
|
111
|
+
await runStatusCommand();
|
|
112
|
+
expect(tui.log.info).toHaveBeenCalledWith(expect.stringContaining('/repo/.git/hooks'));
|
|
113
|
+
});
|
|
114
|
+
it('shows mixed status for different hooks', async () => {
|
|
115
|
+
mockGetHookStatus
|
|
116
|
+
.mockReturnValueOnce({ type: 'pre-commit', installed: true, managedByGhagga: true, path: '/repo/.git/hooks/pre-commit' })
|
|
117
|
+
.mockReturnValueOnce({ type: 'commit-msg', installed: true, managedByGhagga: false, path: '/repo/.git/hooks/commit-msg' });
|
|
118
|
+
await runStatusCommand();
|
|
119
|
+
expect(tui.log.success).toHaveBeenCalledWith(expect.stringContaining('pre-commit'));
|
|
120
|
+
expect(tui.log.warn).toHaveBeenCalledWith(expect.stringContaining('commit-msg'));
|
|
121
|
+
});
|
|
122
|
+
});
|
|
123
|
+
//# sourceMappingURL=status.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"status.test.js","sourceRoot":"","sources":["../../../src/commands/hooks/status.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `ghagga hooks uninstall` subcommand.
|
|
3
|
+
*
|
|
4
|
+
* Removes GHAGGA-managed hooks from the current git repository.
|
|
5
|
+
* Only removes hooks that contain the GHAGGA marker comment.
|
|
6
|
+
* Restores backed-up hooks if they exist.
|
|
7
|
+
*
|
|
8
|
+
* @see Phase 3, Task 3.3
|
|
9
|
+
*/
|
|
10
|
+
import { Command } from 'commander';
|
|
11
|
+
export declare function registerUninstallCommand(parent: Command): void;
|
|
12
|
+
//# sourceMappingURL=uninstall.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"uninstall.d.ts","sourceRoot":"","sources":["../../../src/commands/hooks/uninstall.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAOpC,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,OAAO,GAAG,IAAI,CAgC9D"}
|