claude-git-hooks 2.18.0 → 2.19.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/CHANGELOG.md +38 -0
- package/CLAUDE.md +12 -8
- package/README.md +2 -1
- package/bin/claude-hooks +75 -89
- package/lib/cli-metadata.js +301 -0
- package/lib/commands/analyze-diff.js +12 -10
- package/lib/commands/analyze.js +9 -5
- package/lib/commands/bump-version.js +66 -43
- package/lib/commands/create-pr.js +71 -34
- package/lib/commands/debug.js +4 -7
- package/lib/commands/generate-changelog.js +11 -4
- package/lib/commands/help.js +47 -27
- package/lib/commands/helpers.js +66 -43
- package/lib/commands/hooks.js +15 -13
- package/lib/commands/install.js +546 -39
- package/lib/commands/migrate-config.js +8 -11
- package/lib/commands/presets.js +6 -13
- package/lib/commands/setup-github.js +12 -3
- package/lib/commands/telemetry-cmd.js +8 -6
- package/lib/commands/update.js +1 -2
- package/lib/config.js +36 -31
- package/lib/hooks/pre-commit.js +34 -54
- package/lib/hooks/prepare-commit-msg.js +39 -58
- package/lib/utils/analysis-engine.js +28 -21
- package/lib/utils/changelog-generator.js +162 -34
- package/lib/utils/claude-client.js +438 -377
- package/lib/utils/claude-diagnostics.js +20 -10
- package/lib/utils/file-operations.js +51 -79
- package/lib/utils/file-utils.js +46 -9
- package/lib/utils/git-operations.js +140 -123
- package/lib/utils/git-tag-manager.js +24 -23
- package/lib/utils/github-api.js +85 -61
- package/lib/utils/github-client.js +12 -14
- package/lib/utils/installation-diagnostics.js +4 -4
- package/lib/utils/interactive-ui.js +29 -17
- package/lib/utils/logger.js +4 -1
- package/lib/utils/pr-metadata-engine.js +67 -33
- package/lib/utils/preset-loader.js +20 -62
- package/lib/utils/prompt-builder.js +50 -55
- package/lib/utils/resolution-prompt.js +33 -44
- package/lib/utils/sanitize.js +20 -19
- package/lib/utils/task-id.js +27 -40
- package/lib/utils/telemetry.js +29 -17
- package/lib/utils/version-manager.js +173 -126
- package/lib/utils/which-command.js +23 -12
- package/package.json +69 -69
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,44 @@ Todos los cambios notables en este proyecto se documentarán en este archivo.
|
|
|
5
5
|
El formato está basado en [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
y este proyecto adhiere a [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [2.19.0] - 2026-03-06
|
|
9
|
+
|
|
10
|
+
### ✨ Added
|
|
11
|
+
- Shell autocompletion for all CLI commands, flags, and arguments — Bash, Zsh, Fish, and PowerShell (#78)
|
|
12
|
+
- Command registry (`lib/cli-metadata.js`) — single source of truth for commands, flags, descriptions, and completion metadata
|
|
13
|
+
- Four shell completion generators with per-command flag/arg awareness and dynamic branch completion for `analyze-diff` and `create-pr`
|
|
14
|
+
- Completions auto-install during `claude-hooks install` and auto-remove during `claude-hooks uninstall`
|
|
15
|
+
- Self-healing RC file updates — reinstall detects and replaces stale source lines (e.g., Windows backslash paths) automatically
|
|
16
|
+
|
|
17
|
+
### 🔧 Changed
|
|
18
|
+
- Refactored `bin/claude-hooks` — replaced 130-line switch/case with declarative command registry lookup via `buildCommandMap()`
|
|
19
|
+
- Shell source lines now use `$HOME`-relative paths for cross-platform compatibility (MINGW64, WSL, native Linux/macOS)
|
|
20
|
+
- Completion source lines written to both `~/.bashrc` and `~/.bash_profile` (MINGW64 reads `.bash_profile` first)
|
|
21
|
+
- PowerShell profile detection now queries `$PROFILE` directly via `powershell.exe`/`pwsh`, handling OneDrive-redirected and locale-specific Documents folders
|
|
22
|
+
- PowerShell completion script uses fully qualified .NET type names (`System.Management.Automation.CompletionResult`) for compatibility with dot-sourced scripts
|
|
23
|
+
- Updated CLAUDE.md with cli-metadata.js documentation, exports table, and Command Registry design pattern
|
|
24
|
+
|
|
25
|
+
### 🐛 Fixed
|
|
26
|
+
- Bash completions failing on MINGW64/Git Bash due to Windows backslash paths in source lines
|
|
27
|
+
- PowerShell completions failing due to hardcoded Unix profile paths on Windows
|
|
28
|
+
- PowerShell 5.1 `-Native` completers not firing for npm `.ps1` shims — added proxy function wrapper that re-exposes the command as `Function` type
|
|
29
|
+
- PowerShell `[CompletionResult]` type accelerator not resolving in dot-sourced profile scripts
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
## [2.18.1] - 2026-03-04
|
|
33
|
+
|
|
34
|
+
### ✨ Added
|
|
35
|
+
- Monorepo support for CHANGELOG discovery - discovers all CHANGELOG.md files and prompts to select when multiple are found (#77)
|
|
36
|
+
- AI-powered help and issue creation system (#79)
|
|
37
|
+
|
|
38
|
+
### 🔧 Changed
|
|
39
|
+
- Enhanced `walkDirectoryTree` utility function in file-utils.js for reusable directory traversal
|
|
40
|
+
- Updated documentation to reflect monorepo CHANGELOG discovery capabilities
|
|
41
|
+
|
|
42
|
+
### 🐛 Fixed
|
|
43
|
+
- Cross-platform path handling in git hooks directory resolution (normalized path separators)
|
|
44
|
+
|
|
45
|
+
|
|
8
46
|
## [2.18.0] - 2026-03-03
|
|
9
47
|
|
|
10
48
|
### ✨ Added
|
package/CLAUDE.md
CHANGED
|
@@ -45,8 +45,9 @@ This separation enables:
|
|
|
45
45
|
```
|
|
46
46
|
claude-git-hooks/
|
|
47
47
|
├── bin/
|
|
48
|
-
│ └── claude-hooks # Thin CLI router -
|
|
48
|
+
│ └── claude-hooks # Thin CLI router - uses cli-metadata.js registry for dispatch
|
|
49
49
|
├── lib/
|
|
50
|
+
│ ├── cli-metadata.js # Command registry - single source of truth for CLI commands, flags, descriptions
|
|
50
51
|
│ ├── config.js # Config system - load/merge with priority
|
|
51
52
|
│ ├── commands/ # Command modules - one file per CLI command
|
|
52
53
|
│ │ ├── helpers.js # Shared CLI utilities - colors, output, platform
|
|
@@ -235,15 +236,17 @@ Wait for both → consolidate results
|
|
|
235
236
|
|
|
236
237
|
| Module | Purpose | Key Exports |
|
|
237
238
|
| ------------------------ | --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
239
|
+
| `lib/cli-metadata.js` | Command registry | `commands`, `buildCommandMap()`, `generateCompletionData()`, `PRESET_NAMES`, `HOOK_NAMES`, `BUMP_TYPES` |
|
|
238
240
|
| `lib/config.js` | Config system | `getConfig()` |
|
|
239
241
|
| `analysis-engine.js` | Shared analysis logic | `buildFileData()`, `buildFilesData()`, `runAnalysis()`, `consolidateResults()`, `hasBlockingIssues()`, `hasAnyIssues()`, `displayResults()`, `displayIssueSummary()` (v2.13.0) |
|
|
240
242
|
| `claude-client.js` | Claude CLI wrapper | `analyzeCode()`, `analyzeCodeParallel()`, `executeClaudeWithRetry()` |
|
|
241
243
|
| `prompt-builder.js` | Prompt construction | `buildAnalysisPrompt()`, `loadPrompt()` |
|
|
242
244
|
| `git-operations.js` | Git abstractions | `getStagedFiles()`, `getUnstagedFiles()`, `getAllTrackedFiles()`, `getDiff()`, `getRepoRoot()`, `getBranchPushStatus()`, `pushBranch()`, `createCommit()`, `fetchRemote()`, `branchExists()`, `getRemoteBranches()`, `resolveBaseBranch()`, `getChangedFilesBetweenRefs()`, `getDiffBetweenRefs()`, `getCommitsBetweenRefs()` |
|
|
245
|
+
| `file-utils.js` | File operations | `ensureDir()`, `ensureOutputDir()`, `writeOutputFile()`, `walkDirectoryTree()` |
|
|
243
246
|
| `pr-metadata-engine.js` | PR metadata generation | `getBranchContext()`, `buildDiffPayload()`, `generatePRMetadata()`, `analyzeBranchForPR()` (v2.14.0) |
|
|
244
247
|
| `git-tag-manager.js` | Git tag operations | `createTag()`, `pushTags()`, `getLocalTags()`, `getRemoteTags()`, `compareLocalAndRemoteTags()`, `tagExists()` (v2.12.0) |
|
|
245
248
|
| `version-manager.js` | Version management | `discoverVersionFiles()`, `getDiscoveryResult()`, `readVersionFromFile()`, `writeVersionToFile()`, `updateVersionFiles()`, `modifySuffix()`, `incrementVersion()`, `parseVersion()`, `validateVersionFormat()`, `compareVersions()`, `validateVersionAlignment()` (v2.15.5) |
|
|
246
|
-
| `changelog-generator.js` | CHANGELOG generation | `generateChangelogEntry()`, `updateChangelogFile()`, `getLastFinalVersionTag()`, `getCommitsSinceTag()` (v2.12.0)
|
|
249
|
+
| `changelog-generator.js` | CHANGELOG generation | `generateChangelogEntry()`, `updateChangelogFile()`, `getLastFinalVersionTag()`, `getCommitsSinceTag()`, `discoverChangelogFiles()`, `selectChangelogFile()` (v2.12.0) |
|
|
247
250
|
| `github-api.js` | Octokit integration | `createPullRequest()`, `validateToken()`, `saveGitHubToken()`, `fetchFileContent()`, `fetchDirectoryListing()`, `createIssue()` |
|
|
248
251
|
| `github-client.js` | GitHub helpers | `getReviewersForFiles()`, `parseGitHubRepo()` |
|
|
249
252
|
| `preset-loader.js` | Preset system | `loadPreset()`, `listPresets()` |
|
|
@@ -255,12 +258,13 @@ Wait for both → consolidate results
|
|
|
255
258
|
|
|
256
259
|
### Design Patterns
|
|
257
260
|
|
|
258
|
-
1. **Command
|
|
259
|
-
2. **
|
|
260
|
-
3. **
|
|
261
|
-
4. **
|
|
262
|
-
5. **
|
|
263
|
-
6. **
|
|
261
|
+
1. **Command Registry**: `lib/cli-metadata.js` - single source of truth for CLI commands, flags, and descriptions. When adding CLI commands, add an entry to `lib/cli-metadata.js` — routing, completions, and help derive from it.
|
|
262
|
+
2. **Command Pattern**: `lib/commands/*.js` - each CLI command is a self-contained module
|
|
263
|
+
3. **Factory Pattern**: `preset-loader.js` loads configurations dynamically per tech-stack
|
|
264
|
+
4. **Template Method**: `prompt-builder.js` builds prompts from markdown templates
|
|
265
|
+
5. **Strategy Pattern**: `claude-client.js` selects between sequential or parallel analysis
|
|
266
|
+
6. **Adapter Pattern**: `git-operations.js` abstracts git commands into JS functions
|
|
267
|
+
7. **Singleton Pattern**: `config.js` loads configuration once per execution
|
|
264
268
|
|
|
265
269
|
### Key Data Flows
|
|
266
270
|
|
package/README.md
CHANGED
|
@@ -157,7 +157,8 @@ claude-hooks generate-changelog --base-branch develop
|
|
|
157
157
|
- Analyzes commits since last tag using Claude
|
|
158
158
|
- Categorizes by Conventional Commits types (feat, fix, refactor, etc.)
|
|
159
159
|
- Generates Keep a Changelog format entries
|
|
160
|
-
-
|
|
160
|
+
- Discovers all CHANGELOG.md files (monorepo aware); prompts to select when multiple found
|
|
161
|
+
- Updates the selected CHANGELOG.md automatically
|
|
161
162
|
- Useful when `bump-version --update-changelog` fails
|
|
162
163
|
|
|
163
164
|
### Disable/Enable Hooks
|
package/bin/claude-hooks
CHANGED
|
@@ -4,27 +4,12 @@
|
|
|
4
4
|
* File: claude-hooks
|
|
5
5
|
* Purpose: Main CLI entry point - thin router to command modules
|
|
6
6
|
*
|
|
7
|
+
* Routes commands via cli-metadata.js registry.
|
|
7
8
|
* All command implementations are in lib/commands/
|
|
8
|
-
* This file only handles argument parsing and routing.
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
11
|
import { error } from '../lib/commands/helpers.js';
|
|
12
|
-
|
|
13
|
-
// Import commands
|
|
14
|
-
import { runInstall } from '../lib/commands/install.js';
|
|
15
|
-
import { runEnable, runDisable, runStatus, runUninstall } from '../lib/commands/hooks.js';
|
|
16
|
-
import { runAnalyze } from '../lib/commands/analyze.js';
|
|
17
|
-
import { runAnalyzeDiff } from '../lib/commands/analyze-diff.js';
|
|
18
|
-
import { runCreatePr } from '../lib/commands/create-pr.js';
|
|
19
|
-
import { runSetupGitHub } from '../lib/commands/setup-github.js';
|
|
20
|
-
import { runShowPresets, runSetPreset, runCurrentPreset } from '../lib/commands/presets.js';
|
|
21
|
-
import { runUpdate } from '../lib/commands/update.js';
|
|
22
|
-
import { runMigrateConfig } from '../lib/commands/migrate-config.js';
|
|
23
|
-
import { runSetDebug } from '../lib/commands/debug.js';
|
|
24
|
-
import { runShowTelemetry, runClearTelemetry } from '../lib/commands/telemetry-cmd.js';
|
|
25
|
-
import { runShowHelp, runShowVersion } from '../lib/commands/help.js';
|
|
26
|
-
import { runBumpVersion } from '../lib/commands/bump-version.js';
|
|
27
|
-
import { runGenerateChangelog } from '../lib/commands/generate-changelog.js';
|
|
12
|
+
import { buildCommandMap } from '../lib/cli-metadata.js';
|
|
28
13
|
|
|
29
14
|
/**
|
|
30
15
|
* Main CLI router
|
|
@@ -33,97 +18,98 @@ async function main() {
|
|
|
33
18
|
const args = process.argv.slice(2);
|
|
34
19
|
const command = args[0];
|
|
35
20
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
await
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
21
|
+
const commandMap = buildCommandMap();
|
|
22
|
+
const entry = commandMap.get(command);
|
|
23
|
+
|
|
24
|
+
// help / --help / -h / no command
|
|
25
|
+
if (command === undefined || command === '--help' || command === '-h') {
|
|
26
|
+
const { runShowHelp } = await import('../lib/commands/help.js');
|
|
27
|
+
await runShowHelp();
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if (!entry) {
|
|
32
|
+
error(`Unknown command: ${command}`);
|
|
33
|
+
const { runShowHelp } = await import('../lib/commands/help.js');
|
|
34
|
+
runShowHelp();
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// --- Commands with special argument handling ---
|
|
39
|
+
|
|
40
|
+
// analyze: translate flags to options object
|
|
41
|
+
if (entry.name === 'analyze') {
|
|
42
|
+
const handler = await entry.handler();
|
|
43
|
+
await handler({
|
|
57
44
|
staged: !args.includes('--unstaged') && !args.includes('--all'),
|
|
58
45
|
unstaged: args.includes('--unstaged'),
|
|
59
46
|
all: args.includes('--all')
|
|
60
47
|
});
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
await runCreatePr(args.slice(1));
|
|
67
|
-
break;
|
|
68
|
-
case 'setup-github':
|
|
69
|
-
await runSetupGitHub();
|
|
70
|
-
break;
|
|
71
|
-
case 'presets':
|
|
72
|
-
await runShowPresets();
|
|
73
|
-
break;
|
|
74
|
-
case '--set-preset':
|
|
75
|
-
await runSetPreset(args[1]);
|
|
76
|
-
break;
|
|
77
|
-
case 'preset':
|
|
78
|
-
// Handle subcommands: preset current
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// preset: subcommand routing
|
|
52
|
+
if (entry.name === 'preset') {
|
|
79
53
|
if (args[1] === 'current') {
|
|
54
|
+
const { runCurrentPreset } = await import('../lib/commands/presets.js');
|
|
80
55
|
await runCurrentPreset();
|
|
81
56
|
} else {
|
|
82
57
|
error(`Unknown preset subcommand: ${args[1]}`);
|
|
83
58
|
}
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
await runBumpVersion(args.slice(1));
|
|
90
|
-
break;
|
|
91
|
-
case 'generate-changelog':
|
|
92
|
-
await runGenerateChangelog(args.slice(1));
|
|
93
|
-
break;
|
|
94
|
-
case 'telemetry':
|
|
95
|
-
// Handle subcommands: telemetry show, telemetry clear
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// telemetry: subcommand routing
|
|
63
|
+
if (entry.name === 'telemetry') {
|
|
96
64
|
if (args[1] === 'show' || args[1] === undefined) {
|
|
65
|
+
const { runShowTelemetry } = await import('../lib/commands/telemetry-cmd.js');
|
|
97
66
|
await runShowTelemetry();
|
|
98
67
|
} else if (args[1] === 'clear') {
|
|
68
|
+
const { runClearTelemetry } = await import('../lib/commands/telemetry-cmd.js');
|
|
99
69
|
await runClearTelemetry();
|
|
100
70
|
} else {
|
|
101
71
|
error(`Unknown telemetry subcommand: ${args[1]}`);
|
|
102
72
|
}
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
case 'help':
|
|
113
|
-
await runShowHelp(args.slice(1));
|
|
114
|
-
break;
|
|
115
|
-
case '--help':
|
|
116
|
-
case '-h':
|
|
117
|
-
case undefined:
|
|
118
|
-
runShowHelp();
|
|
119
|
-
break;
|
|
120
|
-
default:
|
|
121
|
-
error(`Unknown command: ${command}`);
|
|
122
|
-
runShowHelp();
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// --- Commands that take args[1] as single argument ---
|
|
77
|
+
// enable/disable take optional hook name
|
|
78
|
+
if (entry.name === 'enable' || entry.name === 'disable') {
|
|
79
|
+
const handler = await entry.handler();
|
|
80
|
+
handler(args[1]);
|
|
81
|
+
return;
|
|
123
82
|
}
|
|
83
|
+
|
|
84
|
+
// --set-preset takes preset name, --debug takes value
|
|
85
|
+
if (entry.name === '--set-preset' || entry.name === '--debug') {
|
|
86
|
+
const handler = await entry.handler();
|
|
87
|
+
await handler(args[1]);
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// --- Commands that take args.slice(1) ---
|
|
92
|
+
if (
|
|
93
|
+
[
|
|
94
|
+
'install',
|
|
95
|
+
'analyze-diff',
|
|
96
|
+
'create-pr',
|
|
97
|
+
'bump-version',
|
|
98
|
+
'generate-changelog',
|
|
99
|
+
'help'
|
|
100
|
+
].includes(entry.name)
|
|
101
|
+
) {
|
|
102
|
+
const handler = await entry.handler();
|
|
103
|
+
await handler(args.slice(1));
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// --- Commands with no arguments ---
|
|
108
|
+
const handler = await entry.handler();
|
|
109
|
+
await handler();
|
|
124
110
|
}
|
|
125
111
|
|
|
126
112
|
// Execute main
|
|
127
|
-
main().catch(err => {
|
|
113
|
+
main().catch((err) => {
|
|
128
114
|
error(`Unexpected error: ${err.message}`);
|
|
129
115
|
});
|
|
@@ -0,0 +1,301 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* File: cli-metadata.js
|
|
3
|
+
* Purpose: Single source of truth for CLI commands, flags, and descriptions
|
|
4
|
+
*
|
|
5
|
+
* Three consumers read from this registry:
|
|
6
|
+
* 1. bin/claude-hooks — routing (replaces switch/case)
|
|
7
|
+
* 2. installCompletions() — generates shell completion scripts at install time
|
|
8
|
+
* 3. showStaticHelp() — can derive help text from metadata
|
|
9
|
+
*
|
|
10
|
+
* Adding a new command = adding one entry here.
|
|
11
|
+
* Completions auto-update for every user at next install.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Available preset names for --set-preset completion
|
|
16
|
+
* @type {string[]}
|
|
17
|
+
*/
|
|
18
|
+
export const PRESET_NAMES = ['ai', 'backend', 'database', 'default', 'frontend', 'fullstack'];
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Hook names for enable/disable completion
|
|
22
|
+
* @type {string[]}
|
|
23
|
+
*/
|
|
24
|
+
export const HOOK_NAMES = ['pre-commit', 'prepare-commit-msg'];
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Bump types for bump-version completion
|
|
28
|
+
* @type {string[]}
|
|
29
|
+
*/
|
|
30
|
+
export const BUMP_TYPES = ['major', 'minor', 'patch'];
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* @typedef {Object} CommandFlag
|
|
34
|
+
* @property {string} description - Flag description for completions
|
|
35
|
+
* @property {string[]} [values] - Completable values for the flag
|
|
36
|
+
* @property {boolean} [takesValue] - Whether the flag expects a value argument
|
|
37
|
+
*/
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* @typedef {Object} CommandEntry
|
|
41
|
+
* @property {string} name - Primary command name
|
|
42
|
+
* @property {string[]} [aliases] - Alternative names (e.g., --version, -v)
|
|
43
|
+
* @property {string} description - Short description for completions and help
|
|
44
|
+
* @property {function(): Promise<function>} handler - Dynamic import returning handler function
|
|
45
|
+
* @property {Object<string, CommandFlag>} [flags] - Supported flags
|
|
46
|
+
* @property {string[]} [subcommands] - Available subcommands
|
|
47
|
+
* @property {Object} [args] - Positional argument info
|
|
48
|
+
* @property {string} [args.name] - Argument name
|
|
49
|
+
* @property {string[]} [args.values] - Completable values
|
|
50
|
+
* @property {string} [args.completion] - Shell command for dynamic completion
|
|
51
|
+
*/
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Command registry — all CLI commands defined declaratively
|
|
55
|
+
* @type {CommandEntry[]}
|
|
56
|
+
*/
|
|
57
|
+
export const commands = [
|
|
58
|
+
{
|
|
59
|
+
name: 'install',
|
|
60
|
+
description: 'Install hooks in current repository',
|
|
61
|
+
handler: async () => (await import('./commands/install.js')).runInstall,
|
|
62
|
+
flags: {
|
|
63
|
+
'--force': { description: 'Reinstall even if already exist' },
|
|
64
|
+
'--skip-auth': { description: 'Skip Claude authentication check' }
|
|
65
|
+
}
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
name: 'update',
|
|
69
|
+
description: 'Update to latest version',
|
|
70
|
+
handler: async () => (await import('./commands/update.js')).runUpdate
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
name: 'uninstall',
|
|
74
|
+
description: 'Remove hooks from repository',
|
|
75
|
+
handler: async () => (await import('./commands/hooks.js')).runUninstall
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
name: 'enable',
|
|
79
|
+
description: 'Enable hooks (all or specific)',
|
|
80
|
+
handler: async () => (await import('./commands/hooks.js')).runEnable,
|
|
81
|
+
args: {
|
|
82
|
+
name: 'hook',
|
|
83
|
+
values: HOOK_NAMES
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
name: 'disable',
|
|
88
|
+
description: 'Disable hooks (all or specific)',
|
|
89
|
+
handler: async () => (await import('./commands/hooks.js')).runDisable,
|
|
90
|
+
args: {
|
|
91
|
+
name: 'hook',
|
|
92
|
+
values: HOOK_NAMES
|
|
93
|
+
}
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
name: 'status',
|
|
97
|
+
description: 'Show hook status',
|
|
98
|
+
handler: async () => (await import('./commands/hooks.js')).runStatus
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
name: 'analyze',
|
|
102
|
+
description: 'Analyze code interactively before committing',
|
|
103
|
+
handler: async () => (await import('./commands/analyze.js')).runAnalyze,
|
|
104
|
+
flags: {
|
|
105
|
+
'--unstaged': { description: 'Analyze unstaged changes' },
|
|
106
|
+
'--all': { description: 'Analyze all tracked files' }
|
|
107
|
+
}
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
name: 'analyze-diff',
|
|
111
|
+
description: 'Analyze diff and generate PR metadata',
|
|
112
|
+
handler: async () => (await import('./commands/analyze-diff.js')).runAnalyzeDiff,
|
|
113
|
+
args: {
|
|
114
|
+
name: 'base-branch',
|
|
115
|
+
completion: "git branch --format='%(refname:short)'"
|
|
116
|
+
}
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
name: 'create-pr',
|
|
120
|
+
description: 'Create PR with auto-generated metadata',
|
|
121
|
+
handler: async () => (await import('./commands/create-pr.js')).runCreatePr,
|
|
122
|
+
args: {
|
|
123
|
+
name: 'base-branch',
|
|
124
|
+
completion: "git branch --format='%(refname:short)'"
|
|
125
|
+
}
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
name: 'setup-github',
|
|
129
|
+
description: 'Configure GitHub token for PR creation',
|
|
130
|
+
handler: async () => (await import('./commands/setup-github.js')).runSetupGitHub
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
name: 'presets',
|
|
134
|
+
description: 'List all available presets',
|
|
135
|
+
handler: async () => (await import('./commands/presets.js')).runShowPresets
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
name: '--set-preset',
|
|
139
|
+
description: 'Set the active preset',
|
|
140
|
+
handler: async () => (await import('./commands/presets.js')).runSetPreset,
|
|
141
|
+
args: {
|
|
142
|
+
name: 'preset',
|
|
143
|
+
values: PRESET_NAMES
|
|
144
|
+
}
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
name: 'preset',
|
|
148
|
+
description: 'Preset management',
|
|
149
|
+
handler: async () => null,
|
|
150
|
+
subcommands: ['current']
|
|
151
|
+
},
|
|
152
|
+
{
|
|
153
|
+
name: 'migrate-config',
|
|
154
|
+
description: 'Migrate legacy config to v2.8.0 format',
|
|
155
|
+
handler: async () => (await import('./commands/migrate-config.js')).runMigrateConfig
|
|
156
|
+
},
|
|
157
|
+
{
|
|
158
|
+
name: 'bump-version',
|
|
159
|
+
description: 'Bump version with CHANGELOG and Git tag',
|
|
160
|
+
handler: async () => (await import('./commands/bump-version.js')).runBumpVersion,
|
|
161
|
+
args: {
|
|
162
|
+
name: 'type',
|
|
163
|
+
values: BUMP_TYPES
|
|
164
|
+
},
|
|
165
|
+
flags: {
|
|
166
|
+
'--dry-run': { description: 'Preview changes without applying' },
|
|
167
|
+
'--interactive': { description: 'Force file selection menu' },
|
|
168
|
+
'--no-commit': { description: 'Skip automatic commit' },
|
|
169
|
+
'--no-tag': { description: 'Skip Git tag creation' },
|
|
170
|
+
'--push': { description: 'Push tag to remote' },
|
|
171
|
+
'--remove-suffix': { description: 'Remove version suffix' },
|
|
172
|
+
'--set-suffix': { description: 'Set/replace version suffix', takesValue: true },
|
|
173
|
+
'--suffix': { description: 'Add version suffix (e.g., SNAPSHOT)', takesValue: true },
|
|
174
|
+
'--update-changelog': { description: 'Generate CHANGELOG entry' }
|
|
175
|
+
}
|
|
176
|
+
},
|
|
177
|
+
{
|
|
178
|
+
name: 'generate-changelog',
|
|
179
|
+
description: 'Generate CHANGELOG entry independently',
|
|
180
|
+
handler: async () =>
|
|
181
|
+
(await import('./commands/generate-changelog.js')).runGenerateChangelog,
|
|
182
|
+
flags: {
|
|
183
|
+
'--base-branch': { description: 'Compare against branch', takesValue: true },
|
|
184
|
+
'--release': { description: 'Mark as released' }
|
|
185
|
+
}
|
|
186
|
+
},
|
|
187
|
+
{
|
|
188
|
+
name: 'telemetry',
|
|
189
|
+
description: 'View or clear telemetry data',
|
|
190
|
+
handler: async () => null,
|
|
191
|
+
subcommands: ['show', 'clear']
|
|
192
|
+
},
|
|
193
|
+
{
|
|
194
|
+
name: '--debug',
|
|
195
|
+
description: 'Toggle debug mode',
|
|
196
|
+
handler: async () => (await import('./commands/debug.js')).runSetDebug,
|
|
197
|
+
args: {
|
|
198
|
+
name: 'value',
|
|
199
|
+
values: ['true', 'false', 'status']
|
|
200
|
+
}
|
|
201
|
+
},
|
|
202
|
+
{
|
|
203
|
+
name: 'version',
|
|
204
|
+
aliases: ['--version', '-v'],
|
|
205
|
+
description: 'Show current version',
|
|
206
|
+
handler: async () => (await import('./commands/help.js')).runShowVersion
|
|
207
|
+
},
|
|
208
|
+
{
|
|
209
|
+
name: 'help',
|
|
210
|
+
aliases: ['--help', '-h'],
|
|
211
|
+
description: 'Show help or ask AI a question',
|
|
212
|
+
handler: async () => (await import('./commands/help.js')).runShowHelp,
|
|
213
|
+
flags: {
|
|
214
|
+
'--report-issue': { description: 'Create GitHub issue interactively' }
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
];
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Build a lookup map from command name/alias to command entry
|
|
221
|
+
* @returns {Map<string, CommandEntry>}
|
|
222
|
+
*/
|
|
223
|
+
export function buildCommandMap() {
|
|
224
|
+
const map = new Map();
|
|
225
|
+
for (const cmd of commands) {
|
|
226
|
+
map.set(cmd.name, cmd);
|
|
227
|
+
if (cmd.aliases) {
|
|
228
|
+
for (const alias of cmd.aliases) {
|
|
229
|
+
map.set(alias, cmd);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
return map;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Generate flat completion data for shell script generators
|
|
238
|
+
*
|
|
239
|
+
* @returns {{ commands: string[], descriptions: Object<string, string>, flags: Object<string, string[]>, subcommands: Object<string, string[]>, argValues: Object<string, string[]>, argCompletions: Object<string, string>, flagValues: Object<string, Object<string, string[]>> }}
|
|
240
|
+
*/
|
|
241
|
+
export function generateCompletionData() {
|
|
242
|
+
const data = {
|
|
243
|
+
commands: [],
|
|
244
|
+
descriptions: {},
|
|
245
|
+
flags: {},
|
|
246
|
+
subcommands: {},
|
|
247
|
+
argValues: {},
|
|
248
|
+
argCompletions: {},
|
|
249
|
+
flagValues: {}
|
|
250
|
+
};
|
|
251
|
+
|
|
252
|
+
for (const cmd of commands) {
|
|
253
|
+
// Skip alias-only entries (--version, --help mapped via version/help)
|
|
254
|
+
if (cmd.name.startsWith('-')) {
|
|
255
|
+
// Still include --set-preset and --debug as they are primary names
|
|
256
|
+
if (cmd.name !== '--set-preset' && cmd.name !== '--debug') {
|
|
257
|
+
continue;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
data.commands.push(cmd.name);
|
|
262
|
+
data.descriptions[cmd.name] = cmd.description;
|
|
263
|
+
|
|
264
|
+
if (cmd.flags) {
|
|
265
|
+
data.flags[cmd.name] = Object.keys(cmd.flags);
|
|
266
|
+
|
|
267
|
+
// Collect flag-level values (e.g., --suffix takes a value)
|
|
268
|
+
const flagVals = {};
|
|
269
|
+
for (const [flag, meta] of Object.entries(cmd.flags)) {
|
|
270
|
+
if (meta.values) {
|
|
271
|
+
flagVals[flag] = meta.values;
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
if (Object.keys(flagVals).length > 0) {
|
|
275
|
+
data.flagValues[cmd.name] = flagVals;
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
if (cmd.subcommands) {
|
|
280
|
+
data.subcommands[cmd.name] = cmd.subcommands;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
if (cmd.args) {
|
|
284
|
+
if (cmd.args.values) {
|
|
285
|
+
data.argValues[cmd.name] = cmd.args.values;
|
|
286
|
+
}
|
|
287
|
+
if (cmd.args.completion) {
|
|
288
|
+
data.argCompletions[cmd.name] = cmd.args.completion;
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
// Add aliases that users type directly
|
|
294
|
+
data.commands.push('--version', '-v', '--help', '-h');
|
|
295
|
+
data.descriptions['--version'] = 'Show current version';
|
|
296
|
+
data.descriptions['-v'] = 'Show current version';
|
|
297
|
+
data.descriptions['--help'] = 'Show help';
|
|
298
|
+
data.descriptions['-h'] = 'Show help';
|
|
299
|
+
|
|
300
|
+
return data;
|
|
301
|
+
}
|
|
@@ -7,12 +7,7 @@ import fs from 'fs';
|
|
|
7
7
|
import { analyzeBranchForPR } from '../utils/pr-metadata-engine.js';
|
|
8
8
|
import { getConfig } from '../config.js';
|
|
9
9
|
import logger from '../utils/logger.js';
|
|
10
|
-
import {
|
|
11
|
-
colors,
|
|
12
|
-
error,
|
|
13
|
-
info,
|
|
14
|
-
checkGitRepo
|
|
15
|
-
} from './helpers.js';
|
|
10
|
+
import { colors, error, info, checkGitRepo } from './helpers.js';
|
|
16
11
|
|
|
17
12
|
/**
|
|
18
13
|
* Analyze-diff command
|
|
@@ -33,12 +28,18 @@ export async function runAnalyzeDiff(args) {
|
|
|
33
28
|
// Parse target branch from arguments
|
|
34
29
|
const targetBranch = args[0];
|
|
35
30
|
|
|
36
|
-
info(
|
|
31
|
+
info(
|
|
32
|
+
targetBranch ? `Analyzing differences with ${targetBranch}...` : 'Analyzing differences...'
|
|
33
|
+
);
|
|
37
34
|
const startTime = Date.now();
|
|
38
35
|
|
|
39
36
|
try {
|
|
40
37
|
// Call PR metadata engine
|
|
41
|
-
const {
|
|
38
|
+
const {
|
|
39
|
+
success: engineSuccess,
|
|
40
|
+
result,
|
|
41
|
+
error: engineError
|
|
42
|
+
} = await analyzeBranchForPR(targetBranch, {
|
|
42
43
|
hook: 'analyze-diff'
|
|
43
44
|
});
|
|
44
45
|
|
|
@@ -132,8 +133,9 @@ export async function runAnalyzeDiff(args) {
|
|
|
132
133
|
console.log(` git branch -m ${result.suggestedBranchName}`);
|
|
133
134
|
}
|
|
134
135
|
|
|
135
|
-
console.log(
|
|
136
|
-
|
|
136
|
+
console.log(
|
|
137
|
+
`💡 ${colors.yellow}Tip:${colors.reset} Use this information to create your PR on GitHub.`
|
|
138
|
+
);
|
|
137
139
|
} catch (e) {
|
|
138
140
|
error(`Error analyzing diff: ${e.message}`);
|
|
139
141
|
}
|