aiwcli 0.9.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 +1248 -0
- package/bin/dev.cmd +3 -0
- package/bin/dev.js +16 -0
- package/bin/run.cmd +3 -0
- package/bin/run.js +19 -0
- package/dist/commands/branch.d.ts +45 -0
- package/dist/commands/branch.js +488 -0
- package/dist/commands/clean.d.ts +34 -0
- package/dist/commands/clean.js +186 -0
- package/dist/commands/clear.d.ts +51 -0
- package/dist/commands/clear.js +835 -0
- package/dist/commands/init/index.d.ts +107 -0
- package/dist/commands/init/index.js +565 -0
- package/dist/commands/launch.d.ts +21 -0
- package/dist/commands/launch.js +108 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/lib/base-command.d.ts +114 -0
- package/dist/lib/base-command.js +153 -0
- package/dist/lib/bmad-installer.d.ts +38 -0
- package/dist/lib/bmad-installer.js +145 -0
- package/dist/lib/claude-settings-types.d.ts +102 -0
- package/dist/lib/claude-settings-types.js +5 -0
- package/dist/lib/config.d.ts +25 -0
- package/dist/lib/config.js +46 -0
- package/dist/lib/debug.d.ts +39 -0
- package/dist/lib/debug.js +74 -0
- package/dist/lib/env-compat.d.ts +26 -0
- package/dist/lib/env-compat.js +35 -0
- package/dist/lib/errors.d.ts +126 -0
- package/dist/lib/errors.js +145 -0
- package/dist/lib/generic-merge.d.ts +74 -0
- package/dist/lib/generic-merge.js +105 -0
- package/dist/lib/git/branch.d.ts +67 -0
- package/dist/lib/git/branch.js +155 -0
- package/dist/lib/git/index.d.ts +11 -0
- package/dist/lib/git/index.js +13 -0
- package/dist/lib/git/safety-checks.d.ts +44 -0
- package/dist/lib/git/safety-checks.js +102 -0
- package/dist/lib/git/types.d.ts +31 -0
- package/dist/lib/git/types.js +6 -0
- package/dist/lib/git/worktree.d.ts +67 -0
- package/dist/lib/git/worktree.js +220 -0
- package/dist/lib/gitignore-manager.d.ts +10 -0
- package/dist/lib/gitignore-manager.js +60 -0
- package/dist/lib/hooks-merger.d.ts +28 -0
- package/dist/lib/hooks-merger.js +94 -0
- package/dist/lib/ide-path-resolver.d.ts +102 -0
- package/dist/lib/ide-path-resolver.js +129 -0
- package/dist/lib/index.d.ts +13 -0
- package/dist/lib/index.js +22 -0
- package/dist/lib/output.d.ts +51 -0
- package/dist/lib/output.js +76 -0
- package/dist/lib/paths.d.ts +66 -0
- package/dist/lib/paths.js +136 -0
- package/dist/lib/quiet.d.ts +12 -0
- package/dist/lib/quiet.js +17 -0
- package/dist/lib/settings-hierarchy.d.ts +42 -0
- package/dist/lib/settings-hierarchy.js +105 -0
- package/dist/lib/spawn.d.ts +105 -0
- package/dist/lib/spawn.js +157 -0
- package/dist/lib/spinner.d.ts +19 -0
- package/dist/lib/spinner.js +34 -0
- package/dist/lib/stdin.d.ts +48 -0
- package/dist/lib/stdin.js +60 -0
- package/dist/lib/template-installer.d.ts +92 -0
- package/dist/lib/template-installer.js +375 -0
- package/dist/lib/template-linter.d.ts +49 -0
- package/dist/lib/template-linter.js +173 -0
- package/dist/lib/template-merger.d.ts +47 -0
- package/dist/lib/template-merger.js +173 -0
- package/dist/lib/template-resolver.d.ts +20 -0
- package/dist/lib/template-resolver.js +60 -0
- package/dist/lib/terminal.d.ts +102 -0
- package/dist/lib/terminal.js +245 -0
- package/dist/lib/tty-detection.d.ts +62 -0
- package/dist/lib/tty-detection.js +83 -0
- package/dist/lib/user-utils.d.ts +5 -0
- package/dist/lib/user-utils.js +23 -0
- package/dist/lib/version.d.ts +99 -0
- package/dist/lib/version.js +144 -0
- package/dist/lib/watch-templates.d.ts +6 -0
- package/dist/lib/watch-templates.js +73 -0
- package/dist/lib/windsurf-hooks-hierarchy.d.ts +30 -0
- package/dist/lib/windsurf-hooks-hierarchy.js +66 -0
- package/dist/lib/windsurf-hooks-merger.d.ts +26 -0
- package/dist/lib/windsurf-hooks-merger.js +53 -0
- package/dist/lib/windsurf-hooks-types.d.ts +33 -0
- package/dist/lib/windsurf-hooks-types.js +5 -0
- package/dist/templates/CLAUDE.md +174 -0
- package/dist/templates/_shared/.claude/commands/handoff.md +14 -0
- package/dist/templates/_shared/.claude/settings.json +61 -0
- package/dist/templates/_shared/.codex/workflows/handoff.md +14 -0
- package/dist/templates/_shared/.windsurf/workflows/handoff.md +14 -0
- package/dist/templates/_shared/hooks/__init__.py +16 -0
- package/dist/templates/_shared/hooks/archive_plan.py +270 -0
- package/dist/templates/_shared/hooks/context_enforcer.py +621 -0
- package/dist/templates/_shared/hooks/context_monitor.py +322 -0
- package/dist/templates/_shared/hooks/file-suggestion.py +188 -0
- package/dist/templates/_shared/hooks/task_create_capture.py +194 -0
- package/dist/templates/_shared/hooks/task_update_capture.py +254 -0
- package/dist/templates/_shared/hooks/user_prompt_submit.py +157 -0
- package/dist/templates/_shared/lib/__init__.py +1 -0
- package/dist/templates/_shared/lib/base/__init__.py +49 -0
- package/dist/templates/_shared/lib/base/__pycache__/constants.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/base/atomic_write.py +180 -0
- package/dist/templates/_shared/lib/base/constants.py +299 -0
- package/dist/templates/_shared/lib/base/inference.py +189 -0
- package/dist/templates/_shared/lib/base/utils.py +216 -0
- package/dist/templates/_shared/lib/context/__init__.py +119 -0
- package/dist/templates/_shared/lib/context/__pycache__/__init__.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/context/__pycache__/cache.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/context/__pycache__/context_manager.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/context/__pycache__/event_log.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/context/cache.py +446 -0
- package/dist/templates/_shared/lib/context/context_manager.py +1171 -0
- package/dist/templates/_shared/lib/context/discovery.py +486 -0
- package/dist/templates/_shared/lib/context/event_log.py +308 -0
- package/dist/templates/_shared/lib/context/plan_archive.py +247 -0
- package/dist/templates/_shared/lib/context/task_sync.py +367 -0
- package/dist/templates/_shared/lib/handoff/__init__.py +22 -0
- package/dist/templates/_shared/lib/handoff/document_generator.py +307 -0
- package/dist/templates/_shared/lib/templates/README.md +215 -0
- package/dist/templates/_shared/lib/templates/__init__.py +40 -0
- package/dist/templates/_shared/lib/templates/formatters.py +147 -0
- package/dist/templates/_shared/lib/templates/plan_context.py +119 -0
- package/dist/templates/_shared/scripts/save_handoff.py +99 -0
- package/dist/templates/_shared/workflows/handoff.md +212 -0
- package/dist/templates/cc-native/.claude/agents/cc-native/ACCESSIBILITY-TESTER.md +80 -0
- package/dist/templates/cc-native/.claude/agents/cc-native/ARCHITECT-REVIEWER.md +75 -0
- package/dist/templates/cc-native/.claude/agents/cc-native/ASSUMPTION-CHAIN-TRACER.md +239 -0
- package/dist/templates/cc-native/.claude/agents/cc-native/CLARITY-AUDITOR.md +109 -0
- package/dist/templates/cc-native/.claude/agents/cc-native/CODE-REVIEWER.md +71 -0
- package/dist/templates/cc-native/.claude/agents/cc-native/COMPLETENESS-CHECKER.md +104 -0
- package/dist/templates/cc-native/.claude/agents/cc-native/CONTEXT-EXTRACTOR.md +93 -0
- package/dist/templates/cc-native/.claude/agents/cc-native/DEVILS-ADVOCATE.md +223 -0
- package/dist/templates/cc-native/.claude/agents/cc-native/DOCUMENTATION-REVIEWER.md +73 -0
- package/dist/templates/cc-native/.claude/agents/cc-native/FEASIBILITY-ANALYST.md +93 -0
- package/dist/templates/cc-native/.claude/agents/cc-native/FRESH-PERSPECTIVE.md +103 -0
- package/dist/templates/cc-native/.claude/agents/cc-native/HANDOFF-READINESS.md +145 -0
- package/dist/templates/cc-native/.claude/agents/cc-native/HIDDEN-COMPLEXITY-DETECTOR.md +248 -0
- package/dist/templates/cc-native/.claude/agents/cc-native/INCENTIVE-MAPPER.md +235 -0
- package/dist/templates/cc-native/.claude/agents/cc-native/PENETRATION-TESTER.md +80 -0
- package/dist/templates/cc-native/.claude/agents/cc-native/PERFORMANCE-ENGINEER.md +76 -0
- package/dist/templates/cc-native/.claude/agents/cc-native/PLAN-ORCHESTRATOR.md +141 -0
- package/dist/templates/cc-native/.claude/agents/cc-native/PRECEDENT-FINDER.md +240 -0
- package/dist/templates/cc-native/.claude/agents/cc-native/REVERSIBILITY-ANALYST.md +211 -0
- package/dist/templates/cc-native/.claude/agents/cc-native/RISK-ASSESSOR.md +101 -0
- package/dist/templates/cc-native/.claude/agents/cc-native/SECOND-ORDER-ANALYST.md +197 -0
- package/dist/templates/cc-native/.claude/agents/cc-native/SIMPLICITY-GUARDIAN.md +97 -0
- package/dist/templates/cc-native/.claude/agents/cc-native/SKEPTIC.md +349 -0
- package/dist/templates/cc-native/.claude/agents/cc-native/STAKEHOLDER-ADVOCATE.md +106 -0
- package/dist/templates/cc-native/.claude/agents/cc-native/TRADE-OFF-ILLUMINATOR.md +205 -0
- package/dist/templates/cc-native/.claude/commands/cc-native/fresh-perspective.md +8 -0
- package/dist/templates/cc-native/.claude/commands/cc-native/specdev.md +10 -0
- package/dist/templates/cc-native/.claude/settings.json +119 -0
- package/dist/templates/cc-native/.windsurf/workflows/cc-native/fix.md +8 -0
- package/dist/templates/cc-native/.windsurf/workflows/cc-native/fresh-perspective.md +8 -0
- package/dist/templates/cc-native/.windsurf/workflows/cc-native/implement.md +8 -0
- package/dist/templates/cc-native/.windsurf/workflows/cc-native/research.md +8 -0
- package/dist/templates/cc-native/CC-NATIVE-README.md +192 -0
- package/dist/templates/cc-native/MIGRATION.md +86 -0
- package/dist/templates/cc-native/TEMPLATE-SCHEMA.md +331 -0
- package/dist/templates/cc-native/_cc-native/docs/PERMISSION_REQUEST_VERIFICATION.md +147 -0
- package/dist/templates/cc-native/_cc-native/hooks/__pycache__/add_plan_context.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/hooks/__pycache__/archive_plan.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/hooks/__pycache__/cc-native-agent-review.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/hooks/__pycache__/cc-native-plan-review.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/hooks/__pycache__/test_permission_request.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/hooks/add_plan_context.py +150 -0
- package/dist/templates/cc-native/_cc-native/hooks/cc-native-plan-review.py +746 -0
- package/dist/templates/cc-native/_cc-native/hooks/suggest-fresh-perspective.py +339 -0
- package/dist/templates/cc-native/_cc-native/lib/__init__.py +57 -0
- package/dist/templates/cc-native/_cc-native/lib/__pycache__/__init__.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/lib/__pycache__/orchestrator.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/lib/__pycache__/state.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/lib/__pycache__/utils.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/lib/async_archive.py +68 -0
- package/dist/templates/cc-native/_cc-native/lib/atomic_write.py +98 -0
- package/dist/templates/cc-native/_cc-native/lib/constants.py +45 -0
- package/dist/templates/cc-native/_cc-native/lib/orchestrator.py +273 -0
- package/dist/templates/cc-native/_cc-native/lib/reviewers/__init__.py +28 -0
- package/dist/templates/cc-native/_cc-native/lib/reviewers/__pycache__/__init__.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/lib/reviewers/__pycache__/agent.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/lib/reviewers/__pycache__/base.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/lib/reviewers/__pycache__/codex.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/lib/reviewers/__pycache__/gemini.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/lib/reviewers/agent.py +164 -0
- package/dist/templates/cc-native/_cc-native/lib/reviewers/base.py +89 -0
- package/dist/templates/cc-native/_cc-native/lib/reviewers/codex.py +119 -0
- package/dist/templates/cc-native/_cc-native/lib/reviewers/gemini.py +103 -0
- package/dist/templates/cc-native/_cc-native/lib/state.py +251 -0
- package/dist/templates/cc-native/_cc-native/lib/utils.py +830 -0
- package/dist/templates/cc-native/_cc-native/plan-review.config.json +76 -0
- package/dist/templates/cc-native/_cc-native/scripts/__pycache__/aggregate_agents.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/scripts/aggregate_agents.py +151 -0
- package/dist/templates/cc-native/_cc-native/workflows/fresh-perspective.md +134 -0
- package/dist/templates/cc-native/_cc-native/workflows/specdev.md +9 -0
- package/dist/types/exit-codes.d.ts +11 -0
- package/dist/types/exit-codes.js +10 -0
- package/dist/types/index.d.ts +5 -0
- package/dist/types/index.js +7 -0
- package/oclif.manifest.json +405 -0
- package/package.json +109 -0
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Generic merge utilities for configuration merging.
|
|
3
|
+
*
|
|
4
|
+
* This module provides reusable utilities for merging arrays with deduplication
|
|
5
|
+
* and merging configuration objects by event type. Used by hook merger modules
|
|
6
|
+
* to eliminate code duplication.
|
|
7
|
+
*
|
|
8
|
+
* ## Usage
|
|
9
|
+
* ```typescript
|
|
10
|
+
* import { mergeArraysWithDedup, mergeConfigByEventType } from '../lib/generic-merge.js'
|
|
11
|
+
*
|
|
12
|
+
* // Merge arrays with custom equality
|
|
13
|
+
* const merged = mergeArraysWithDedup(existing, template, (a, b) => a.id === b.id)
|
|
14
|
+
*
|
|
15
|
+
* // Merge config objects by event type
|
|
16
|
+
* const mergedConfig = mergeConfigByEventType(
|
|
17
|
+
* existingConfig,
|
|
18
|
+
* templateConfig,
|
|
19
|
+
* (existing, template) => mergeArraysWithDedup(existing, template, areEqual)
|
|
20
|
+
* )
|
|
21
|
+
* ```
|
|
22
|
+
*
|
|
23
|
+
* @module lib/generic-merge
|
|
24
|
+
*/
|
|
25
|
+
/**
|
|
26
|
+
* Merge two arrays with deduplication based on custom equality function.
|
|
27
|
+
*
|
|
28
|
+
* Items from the template array are appended to the existing array only if
|
|
29
|
+
* no equivalent item exists. Order is preserved: existing items first.
|
|
30
|
+
*
|
|
31
|
+
* @template T - Type of array elements
|
|
32
|
+
* @param existing - Existing array (preserved in order)
|
|
33
|
+
* @param template - Template array to merge (items appended if not duplicate)
|
|
34
|
+
* @param areEqual - Function to check if two items are equivalent
|
|
35
|
+
* @returns New merged array with duplicates removed
|
|
36
|
+
*
|
|
37
|
+
* @example
|
|
38
|
+
* ```typescript
|
|
39
|
+
* const merged = mergeArraysWithDedup(
|
|
40
|
+
* [{id: 1, name: 'a'}],
|
|
41
|
+
* [{id: 1, name: 'b'}, {id: 2, name: 'c'}],
|
|
42
|
+
* (a, b) => a.id === b.id
|
|
43
|
+
* )
|
|
44
|
+
* // Result: [{id: 1, name: 'a'}, {id: 2, name: 'c'}]
|
|
45
|
+
* ```
|
|
46
|
+
*/
|
|
47
|
+
export function mergeArraysWithDedup(existing, template, areEqual) {
|
|
48
|
+
const merged = [...existing];
|
|
49
|
+
for (const templateItem of template) {
|
|
50
|
+
const isDuplicate = merged.some((existingItem) => areEqual(existingItem, templateItem));
|
|
51
|
+
if (!isDuplicate) {
|
|
52
|
+
merged.push(templateItem);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return merged;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Merge configuration objects organized by event type.
|
|
59
|
+
*
|
|
60
|
+
* Each configuration maps event types to arrays of items. This function
|
|
61
|
+
* merges configurations by:
|
|
62
|
+
* 1. Collecting all event types from both configurations
|
|
63
|
+
* 2. For each event type, merging the arrays using the provided merge function
|
|
64
|
+
*
|
|
65
|
+
* @template TEventType - Type of event type keys (typically string union)
|
|
66
|
+
* @template TItem - Type of items in the arrays
|
|
67
|
+
* @template TConfig - Type of the configuration object
|
|
68
|
+
* @param existing - Existing configuration (may be undefined)
|
|
69
|
+
* @param template - Template configuration to merge (may be undefined)
|
|
70
|
+
* @param mergeArrays - Function to merge arrays for a single event type
|
|
71
|
+
* @returns New merged configuration
|
|
72
|
+
*
|
|
73
|
+
* @example
|
|
74
|
+
* ```typescript
|
|
75
|
+
* const mergedConfig = mergeConfigByEventType(
|
|
76
|
+
* {click: [handler1], hover: [handler2]},
|
|
77
|
+
* {click: [handler3], focus: [handler4]},
|
|
78
|
+
* (existing, template) => mergeArraysWithDedup(existing, template, areEqual)
|
|
79
|
+
* )
|
|
80
|
+
* // Result: {click: [handler1, handler3], hover: [handler2], focus: [handler4]}
|
|
81
|
+
* ```
|
|
82
|
+
*/
|
|
83
|
+
export function mergeConfigByEventType(existing, template, mergeArrays) {
|
|
84
|
+
// If no template, return existing (or empty object)
|
|
85
|
+
if (!template || Object.keys(template).length === 0) {
|
|
86
|
+
return (existing || {});
|
|
87
|
+
}
|
|
88
|
+
// If no existing, return template
|
|
89
|
+
if (!existing || Object.keys(existing).length === 0) {
|
|
90
|
+
return template;
|
|
91
|
+
}
|
|
92
|
+
const merged = {};
|
|
93
|
+
// Get all unique event types from both configurations
|
|
94
|
+
const allEventTypes = new Set([
|
|
95
|
+
...Object.keys(existing),
|
|
96
|
+
...Object.keys(template),
|
|
97
|
+
]);
|
|
98
|
+
// Merge each event type
|
|
99
|
+
for (const eventType of allEventTypes) {
|
|
100
|
+
const existingItems = existing[eventType] || [];
|
|
101
|
+
const templateItems = template[eventType] || [];
|
|
102
|
+
merged[eventType] = mergeArrays(existingItems, templateItems);
|
|
103
|
+
}
|
|
104
|
+
return merged;
|
|
105
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Git branch operations.
|
|
3
|
+
*
|
|
4
|
+
* Provides utilities for checking, getting, and deleting git branches.
|
|
5
|
+
*
|
|
6
|
+
* @module lib/git/branch
|
|
7
|
+
*/
|
|
8
|
+
import type { GitCommandOptions } from './types.js';
|
|
9
|
+
/**
|
|
10
|
+
* Check if a git branch exists (local).
|
|
11
|
+
*
|
|
12
|
+
* @param branchName - Name of the branch to check
|
|
13
|
+
* @returns True if branch exists locally
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```typescript
|
|
17
|
+
* if (await branchExists('feature-branch')) {
|
|
18
|
+
* console.log('Branch exists')
|
|
19
|
+
* }
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
export declare function branchExists(branchName: string): boolean;
|
|
23
|
+
/**
|
|
24
|
+
* Get the current git branch name.
|
|
25
|
+
*
|
|
26
|
+
* @returns Current branch name
|
|
27
|
+
* @throws Error if not in a git repository or unable to determine branch
|
|
28
|
+
*
|
|
29
|
+
* @example
|
|
30
|
+
* ```typescript
|
|
31
|
+
* const branch = getCurrentBranch()
|
|
32
|
+
* console.log(`Currently on: ${branch}`)
|
|
33
|
+
* ```
|
|
34
|
+
*/
|
|
35
|
+
export declare function getCurrentBranch(): string;
|
|
36
|
+
/**
|
|
37
|
+
* Determine which main branch exists (main or master).
|
|
38
|
+
*
|
|
39
|
+
* Returns 'main' if it exists (preferred), 'master' if it exists,
|
|
40
|
+
* or null if neither exists.
|
|
41
|
+
*
|
|
42
|
+
* @returns 'main', 'master', or null
|
|
43
|
+
*
|
|
44
|
+
* @example
|
|
45
|
+
* ```typescript
|
|
46
|
+
* const mainBranch = getMainBranch()
|
|
47
|
+
* if (mainBranch) {
|
|
48
|
+
* console.log(`Main branch: ${mainBranch}`)
|
|
49
|
+
* }
|
|
50
|
+
* ```
|
|
51
|
+
*/
|
|
52
|
+
export declare function getMainBranch(): null | string;
|
|
53
|
+
/**
|
|
54
|
+
* Delete a git branch (local and remote if exists).
|
|
55
|
+
*
|
|
56
|
+
* @param branchName - Name of the branch to delete
|
|
57
|
+
* @param options - Command options including debug logging
|
|
58
|
+
* @throws Error if unable to delete branch
|
|
59
|
+
*
|
|
60
|
+
* @example
|
|
61
|
+
* ```typescript
|
|
62
|
+
* await deleteBranch('feature-branch', {
|
|
63
|
+
* debugLog: (msg) => console.debug(msg)
|
|
64
|
+
* })
|
|
65
|
+
* ```
|
|
66
|
+
*/
|
|
67
|
+
export declare function deleteBranch(branchName: string, options?: GitCommandOptions): void;
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Git branch operations.
|
|
3
|
+
*
|
|
4
|
+
* Provides utilities for checking, getting, and deleting git branches.
|
|
5
|
+
*
|
|
6
|
+
* @module lib/git/branch
|
|
7
|
+
*/
|
|
8
|
+
import { execSync } from 'node:child_process';
|
|
9
|
+
/**
|
|
10
|
+
* Check if a git branch exists (local).
|
|
11
|
+
*
|
|
12
|
+
* @param branchName - Name of the branch to check
|
|
13
|
+
* @returns True if branch exists locally
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```typescript
|
|
17
|
+
* if (await branchExists('feature-branch')) {
|
|
18
|
+
* console.log('Branch exists')
|
|
19
|
+
* }
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
export function branchExists(branchName) {
|
|
23
|
+
try {
|
|
24
|
+
execSync(`git show-ref --verify refs/heads/${branchName}`, {
|
|
25
|
+
encoding: 'utf8',
|
|
26
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
27
|
+
});
|
|
28
|
+
return true;
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Get the current git branch name.
|
|
36
|
+
*
|
|
37
|
+
* @returns Current branch name
|
|
38
|
+
* @throws Error if not in a git repository or unable to determine branch
|
|
39
|
+
*
|
|
40
|
+
* @example
|
|
41
|
+
* ```typescript
|
|
42
|
+
* const branch = getCurrentBranch()
|
|
43
|
+
* console.log(`Currently on: ${branch}`)
|
|
44
|
+
* ```
|
|
45
|
+
*/
|
|
46
|
+
export function getCurrentBranch() {
|
|
47
|
+
try {
|
|
48
|
+
const branch = execSync('git rev-parse --abbrev-ref HEAD', {
|
|
49
|
+
encoding: 'utf8',
|
|
50
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
51
|
+
}).trim();
|
|
52
|
+
return branch;
|
|
53
|
+
}
|
|
54
|
+
catch (error) {
|
|
55
|
+
const err = error;
|
|
56
|
+
throw new Error(`Failed to get current branch: ${err.message}`);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Determine which main branch exists (main or master).
|
|
61
|
+
*
|
|
62
|
+
* Returns 'main' if it exists (preferred), 'master' if it exists,
|
|
63
|
+
* or null if neither exists.
|
|
64
|
+
*
|
|
65
|
+
* @returns 'main', 'master', or null
|
|
66
|
+
*
|
|
67
|
+
* @example
|
|
68
|
+
* ```typescript
|
|
69
|
+
* const mainBranch = getMainBranch()
|
|
70
|
+
* if (mainBranch) {
|
|
71
|
+
* console.log(`Main branch: ${mainBranch}`)
|
|
72
|
+
* }
|
|
73
|
+
* ```
|
|
74
|
+
*/
|
|
75
|
+
export function getMainBranch() {
|
|
76
|
+
// Check for 'main' first (more modern convention)
|
|
77
|
+
try {
|
|
78
|
+
execSync('git show-ref --verify refs/heads/main', {
|
|
79
|
+
encoding: 'utf8',
|
|
80
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
81
|
+
});
|
|
82
|
+
return 'main';
|
|
83
|
+
}
|
|
84
|
+
catch {
|
|
85
|
+
// main doesn't exist, try master
|
|
86
|
+
}
|
|
87
|
+
// Check for 'master'
|
|
88
|
+
try {
|
|
89
|
+
execSync('git show-ref --verify refs/heads/master', {
|
|
90
|
+
encoding: 'utf8',
|
|
91
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
92
|
+
});
|
|
93
|
+
return 'master';
|
|
94
|
+
}
|
|
95
|
+
catch {
|
|
96
|
+
// neither exists
|
|
97
|
+
return null;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Delete a git branch (local and remote if exists).
|
|
102
|
+
*
|
|
103
|
+
* @param branchName - Name of the branch to delete
|
|
104
|
+
* @param options - Command options including debug logging
|
|
105
|
+
* @throws Error if unable to delete branch
|
|
106
|
+
*
|
|
107
|
+
* @example
|
|
108
|
+
* ```typescript
|
|
109
|
+
* await deleteBranch('feature-branch', {
|
|
110
|
+
* debugLog: (msg) => console.debug(msg)
|
|
111
|
+
* })
|
|
112
|
+
* ```
|
|
113
|
+
*/
|
|
114
|
+
export function deleteBranch(branchName, options) {
|
|
115
|
+
const { debugLog } = options || {};
|
|
116
|
+
// Platform-specific branch name escaping
|
|
117
|
+
const escapedBranch = process.platform === 'win32'
|
|
118
|
+
? `"${branchName.replaceAll('"', String.raw `\"`)}"` // Windows: double quotes
|
|
119
|
+
: `'${branchName.replaceAll('\'', String.raw `'\''`)}'`; // Unix/macOS: single quotes
|
|
120
|
+
// Delete local branch
|
|
121
|
+
debugLog?.(`Deleting local branch '${branchName}'...`);
|
|
122
|
+
try {
|
|
123
|
+
execSync(`git branch -D ${escapedBranch}`, {
|
|
124
|
+
encoding: 'utf8',
|
|
125
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
catch (error) {
|
|
129
|
+
const err = error;
|
|
130
|
+
// If branch doesn't exist (orphaned worktree), that's fine - just log it
|
|
131
|
+
if (err.message?.includes('not found')) {
|
|
132
|
+
debugLog?.(`Branch '${branchName}' not found (orphaned worktree)`);
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
// For other errors, throw
|
|
136
|
+
throw new Error(`Failed to delete branch: ${err.message}`);
|
|
137
|
+
}
|
|
138
|
+
// Check if remote branch exists
|
|
139
|
+
try {
|
|
140
|
+
execSync(`git show-ref --verify refs/remotes/origin/${branchName}`, {
|
|
141
|
+
encoding: 'utf8',
|
|
142
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
143
|
+
});
|
|
144
|
+
// Remote branch exists, delete it
|
|
145
|
+
debugLog?.(`Deleting remote branch '${branchName}'...`);
|
|
146
|
+
execSync(`git push origin --delete ${escapedBranch}`, {
|
|
147
|
+
encoding: 'utf8',
|
|
148
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
catch {
|
|
152
|
+
// Remote branch doesn't exist, skip deletion
|
|
153
|
+
debugLog?.('No remote branch to delete');
|
|
154
|
+
}
|
|
155
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Git utilities module.
|
|
3
|
+
*
|
|
4
|
+
* Provides reusable git operations for branch, worktree, and safety checks.
|
|
5
|
+
*
|
|
6
|
+
* @module lib/git
|
|
7
|
+
*/
|
|
8
|
+
export { branchExists, deleteBranch, getCurrentBranch, getMainBranch } from './branch.js';
|
|
9
|
+
export { hasMergeRequest, hasUnpushedCommits } from './safety-checks.js';
|
|
10
|
+
export type { GitCommandOptions, WorktreeInfo } from './types.js';
|
|
11
|
+
export { createWorktree, deleteWorktreeFolder, getAllWorktrees, getWorktreePath } from './worktree.js';
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Git utilities module.
|
|
3
|
+
*
|
|
4
|
+
* Provides reusable git operations for branch, worktree, and safety checks.
|
|
5
|
+
*
|
|
6
|
+
* @module lib/git
|
|
7
|
+
*/
|
|
8
|
+
// Branch operations
|
|
9
|
+
export { branchExists, deleteBranch, getCurrentBranch, getMainBranch } from './branch.js';
|
|
10
|
+
// Safety checks
|
|
11
|
+
export { hasMergeRequest, hasUnpushedCommits } from './safety-checks.js';
|
|
12
|
+
// Worktree operations
|
|
13
|
+
export { createWorktree, deleteWorktreeFolder, getAllWorktrees, getWorktreePath } from './worktree.js';
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Git safety check operations.
|
|
3
|
+
*
|
|
4
|
+
* Provides utilities for checking if branches are safe to delete.
|
|
5
|
+
*
|
|
6
|
+
* @module lib/git/safety-checks
|
|
7
|
+
*/
|
|
8
|
+
import type { GitCommandOptions } from './types.js';
|
|
9
|
+
/**
|
|
10
|
+
* Check if a branch has unpushed commits.
|
|
11
|
+
*
|
|
12
|
+
* Returns true if there are commits not pushed to remote, or if
|
|
13
|
+
* the branch has no remote tracking branch (safer assumption).
|
|
14
|
+
*
|
|
15
|
+
* @param branchName - Name of the branch to check
|
|
16
|
+
* @param options - Command options including debug logging
|
|
17
|
+
* @returns True if there are unpushed commits
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* ```typescript
|
|
21
|
+
* if (hasUnpushedCommits('feature-branch')) {
|
|
22
|
+
* console.log('Branch has unpushed commits, cannot delete')
|
|
23
|
+
* }
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
export declare function hasUnpushedCommits(branchName: string, options?: GitCommandOptions): boolean;
|
|
27
|
+
/**
|
|
28
|
+
* Check if a branch has an open merge request/pull request.
|
|
29
|
+
*
|
|
30
|
+
* Uses GitHub CLI (gh) to check for open PRs. Returns false if gh is
|
|
31
|
+
* not available or if unable to check.
|
|
32
|
+
*
|
|
33
|
+
* @param branchName - Name of the branch to check
|
|
34
|
+
* @param options - Command options including debug logging
|
|
35
|
+
* @returns True if an open PR exists for this branch
|
|
36
|
+
*
|
|
37
|
+
* @example
|
|
38
|
+
* ```typescript
|
|
39
|
+
* if (hasMergeRequest('feature-branch')) {
|
|
40
|
+
* console.log('Branch has an open PR, cannot delete')
|
|
41
|
+
* }
|
|
42
|
+
* ```
|
|
43
|
+
*/
|
|
44
|
+
export declare function hasMergeRequest(branchName: string, options?: GitCommandOptions): boolean;
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Git safety check operations.
|
|
3
|
+
*
|
|
4
|
+
* Provides utilities for checking if branches are safe to delete.
|
|
5
|
+
*
|
|
6
|
+
* @module lib/git/safety-checks
|
|
7
|
+
*/
|
|
8
|
+
import { execSync } from 'node:child_process';
|
|
9
|
+
/**
|
|
10
|
+
* Check if a branch has unpushed commits.
|
|
11
|
+
*
|
|
12
|
+
* Returns true if there are commits not pushed to remote, or if
|
|
13
|
+
* the branch has no remote tracking branch (safer assumption).
|
|
14
|
+
*
|
|
15
|
+
* @param branchName - Name of the branch to check
|
|
16
|
+
* @param options - Command options including debug logging
|
|
17
|
+
* @returns True if there are unpushed commits
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* ```typescript
|
|
21
|
+
* if (hasUnpushedCommits('feature-branch')) {
|
|
22
|
+
* console.log('Branch has unpushed commits, cannot delete')
|
|
23
|
+
* }
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
export function hasUnpushedCommits(branchName, options) {
|
|
27
|
+
const { debugLog } = options || {};
|
|
28
|
+
try {
|
|
29
|
+
// Check if remote branch exists first
|
|
30
|
+
const escapedBranch = branchName.replaceAll('\'', String.raw `'\''`);
|
|
31
|
+
try {
|
|
32
|
+
execSync(`git show-ref --verify refs/remotes/origin/${escapedBranch}`, {
|
|
33
|
+
encoding: 'utf8',
|
|
34
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
// No remote branch - treat as having unpushed commits (safer)
|
|
39
|
+
debugLog?.(`Branch '${branchName}' has no remote tracking branch`);
|
|
40
|
+
return true;
|
|
41
|
+
}
|
|
42
|
+
// Check if there are commits ahead of remote
|
|
43
|
+
const output = execSync(`git rev-list origin/${escapedBranch}..${escapedBranch} --count`, {
|
|
44
|
+
encoding: 'utf8',
|
|
45
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
46
|
+
}).trim();
|
|
47
|
+
const commitCount = Number.parseInt(output, 10);
|
|
48
|
+
return commitCount > 0;
|
|
49
|
+
}
|
|
50
|
+
catch (error) {
|
|
51
|
+
// If we can't determine, err on the side of caution
|
|
52
|
+
debugLog?.(`Error checking unpushed commits for '${branchName}': ${error}`);
|
|
53
|
+
return true;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Check if a branch has an open merge request/pull request.
|
|
58
|
+
*
|
|
59
|
+
* Uses GitHub CLI (gh) to check for open PRs. Returns false if gh is
|
|
60
|
+
* not available or if unable to check.
|
|
61
|
+
*
|
|
62
|
+
* @param branchName - Name of the branch to check
|
|
63
|
+
* @param options - Command options including debug logging
|
|
64
|
+
* @returns True if an open PR exists for this branch
|
|
65
|
+
*
|
|
66
|
+
* @example
|
|
67
|
+
* ```typescript
|
|
68
|
+
* if (hasMergeRequest('feature-branch')) {
|
|
69
|
+
* console.log('Branch has an open PR, cannot delete')
|
|
70
|
+
* }
|
|
71
|
+
* ```
|
|
72
|
+
*/
|
|
73
|
+
export function hasMergeRequest(branchName, options) {
|
|
74
|
+
const { debugLog } = options || {};
|
|
75
|
+
try {
|
|
76
|
+
// Check if gh CLI is available
|
|
77
|
+
try {
|
|
78
|
+
execSync('gh --version', {
|
|
79
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
catch {
|
|
83
|
+
// gh CLI not available, can't check for PRs
|
|
84
|
+
debugLog?.('gh CLI not available, skipping PR check');
|
|
85
|
+
return false;
|
|
86
|
+
}
|
|
87
|
+
// Check for open PRs for this branch
|
|
88
|
+
const escapedBranch = branchName.replaceAll('\'', String.raw `'\''`);
|
|
89
|
+
const output = execSync(`gh pr list --head '${escapedBranch}' --state open --json number`, {
|
|
90
|
+
encoding: 'utf8',
|
|
91
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
92
|
+
}).trim();
|
|
93
|
+
// Parse JSON output
|
|
94
|
+
const prs = JSON.parse(output);
|
|
95
|
+
return prs.length > 0;
|
|
96
|
+
}
|
|
97
|
+
catch (error) {
|
|
98
|
+
// If we can't determine, assume no PR (less conservative than unpushed commits)
|
|
99
|
+
debugLog?.(`Error checking for PR on branch '${branchName}': ${error}`);
|
|
100
|
+
return false;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Git-related type definitions.
|
|
3
|
+
*
|
|
4
|
+
* @module lib/git/types
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Information about a git worktree.
|
|
8
|
+
*/
|
|
9
|
+
export interface WorktreeInfo {
|
|
10
|
+
/**
|
|
11
|
+
* Branch name, or null if detached HEAD.
|
|
12
|
+
*/
|
|
13
|
+
branch: null | string;
|
|
14
|
+
/**
|
|
15
|
+
* Commit hash of the worktree HEAD.
|
|
16
|
+
*/
|
|
17
|
+
head: string;
|
|
18
|
+
/**
|
|
19
|
+
* Absolute path to the worktree directory.
|
|
20
|
+
*/
|
|
21
|
+
path: string;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Options for git command execution.
|
|
25
|
+
*/
|
|
26
|
+
export interface GitCommandOptions {
|
|
27
|
+
/**
|
|
28
|
+
* Optional debug logging function.
|
|
29
|
+
*/
|
|
30
|
+
debugLog?: (message: string) => void;
|
|
31
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Git worktree operations.
|
|
3
|
+
*
|
|
4
|
+
* Provides utilities for managing git worktrees.
|
|
5
|
+
*
|
|
6
|
+
* @module lib/git/worktree
|
|
7
|
+
*/
|
|
8
|
+
import type { GitCommandOptions, WorktreeInfo } from './types.js';
|
|
9
|
+
/**
|
|
10
|
+
* Get all worktrees in the repository.
|
|
11
|
+
*
|
|
12
|
+
* @returns Array of worktree info objects
|
|
13
|
+
* @throws Error if unable to list worktrees
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```typescript
|
|
17
|
+
* const worktrees = getAllWorktrees()
|
|
18
|
+
* for (const wt of worktrees) {
|
|
19
|
+
* console.log(`${wt.branch}: ${wt.path}`)
|
|
20
|
+
* }
|
|
21
|
+
* ```
|
|
22
|
+
*/
|
|
23
|
+
export declare function getAllWorktrees(): WorktreeInfo[];
|
|
24
|
+
/**
|
|
25
|
+
* Get the worktree path for a branch.
|
|
26
|
+
*
|
|
27
|
+
* @param branchName - Name of the branch
|
|
28
|
+
* @returns Worktree path if found, null otherwise
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
* ```typescript
|
|
32
|
+
* const path = getWorktreePath('main')
|
|
33
|
+
* if (path) {
|
|
34
|
+
* console.log(`Main branch worktree: ${path}`)
|
|
35
|
+
* }
|
|
36
|
+
* ```
|
|
37
|
+
*/
|
|
38
|
+
export declare function getWorktreePath(branchName: string): null | string;
|
|
39
|
+
/**
|
|
40
|
+
* Create a git worktree with the specified branch name.
|
|
41
|
+
*
|
|
42
|
+
* @param branchName - Name of the branch to create
|
|
43
|
+
* @param worktreePath - Path where the worktree should be created
|
|
44
|
+
* @returns Promise that resolves when worktree is created
|
|
45
|
+
* @throws Error if unable to create worktree
|
|
46
|
+
*
|
|
47
|
+
* @example
|
|
48
|
+
* ```typescript
|
|
49
|
+
* await createWorktree('feature-branch', '/path/to/worktree')
|
|
50
|
+
* ```
|
|
51
|
+
*/
|
|
52
|
+
export declare function createWorktree(branchName: string, worktreePath: string): Promise<void>;
|
|
53
|
+
/**
|
|
54
|
+
* Delete a worktree folder and remove worktree from git.
|
|
55
|
+
*
|
|
56
|
+
* @param worktreePath - Path to the worktree to delete
|
|
57
|
+
* @param options - Command options including debug logging
|
|
58
|
+
* @throws Error if unable to delete worktree folder
|
|
59
|
+
*
|
|
60
|
+
* @example
|
|
61
|
+
* ```typescript
|
|
62
|
+
* await deleteWorktreeFolder('/path/to/worktree', {
|
|
63
|
+
* debugLog: (msg) => console.debug(msg)
|
|
64
|
+
* })
|
|
65
|
+
* ```
|
|
66
|
+
*/
|
|
67
|
+
export declare function deleteWorktreeFolder(worktreePath: string, options?: GitCommandOptions): Promise<void>;
|