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,220 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Git worktree operations.
|
|
3
|
+
*
|
|
4
|
+
* Provides utilities for managing git worktrees.
|
|
5
|
+
*
|
|
6
|
+
* @module lib/git/worktree
|
|
7
|
+
*/
|
|
8
|
+
import { execSync, spawn } from 'node:child_process';
|
|
9
|
+
import { promises as fs } from 'node:fs';
|
|
10
|
+
/**
|
|
11
|
+
* Get all worktrees in the repository.
|
|
12
|
+
*
|
|
13
|
+
* @returns Array of worktree info objects
|
|
14
|
+
* @throws Error if unable to list worktrees
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```typescript
|
|
18
|
+
* const worktrees = getAllWorktrees()
|
|
19
|
+
* for (const wt of worktrees) {
|
|
20
|
+
* console.log(`${wt.branch}: ${wt.path}`)
|
|
21
|
+
* }
|
|
22
|
+
* ```
|
|
23
|
+
*/
|
|
24
|
+
export function getAllWorktrees() {
|
|
25
|
+
try {
|
|
26
|
+
const output = execSync('git worktree list --porcelain', {
|
|
27
|
+
encoding: 'utf8',
|
|
28
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
29
|
+
});
|
|
30
|
+
// Parse worktree list output
|
|
31
|
+
// Format:
|
|
32
|
+
// worktree /path/to/worktree
|
|
33
|
+
// HEAD <commit-hash>
|
|
34
|
+
// branch refs/heads/branchname
|
|
35
|
+
// (blank line between entries)
|
|
36
|
+
const worktrees = [];
|
|
37
|
+
const lines = output.split('\n');
|
|
38
|
+
let currentWorktree = null;
|
|
39
|
+
for (const line of lines) {
|
|
40
|
+
if (line.startsWith('worktree ')) {
|
|
41
|
+
if (currentWorktree) {
|
|
42
|
+
worktrees.push(currentWorktree);
|
|
43
|
+
}
|
|
44
|
+
currentWorktree = {
|
|
45
|
+
branch: null,
|
|
46
|
+
head: '',
|
|
47
|
+
path: line.slice('worktree '.length).trim(),
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
else if (line.startsWith('HEAD ')) {
|
|
51
|
+
if (currentWorktree) {
|
|
52
|
+
currentWorktree.head = line.slice('HEAD '.length).trim();
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
else if (line.startsWith('branch ')) {
|
|
56
|
+
if (currentWorktree) {
|
|
57
|
+
const branchRef = line.slice('branch '.length).trim();
|
|
58
|
+
// Extract branch name from refs/heads/branchname
|
|
59
|
+
currentWorktree.branch = branchRef.replace('refs/heads/', '');
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
else if (line === '' && currentWorktree) {
|
|
63
|
+
// Empty line marks end of worktree entry
|
|
64
|
+
worktrees.push(currentWorktree);
|
|
65
|
+
currentWorktree = null;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
// Push the last worktree if exists
|
|
69
|
+
if (currentWorktree) {
|
|
70
|
+
worktrees.push(currentWorktree);
|
|
71
|
+
}
|
|
72
|
+
return worktrees;
|
|
73
|
+
}
|
|
74
|
+
catch (error) {
|
|
75
|
+
const err = error;
|
|
76
|
+
throw new Error(`Failed to get worktrees: ${err.message}`);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Get the worktree path for a branch.
|
|
81
|
+
*
|
|
82
|
+
* @param branchName - Name of the branch
|
|
83
|
+
* @returns Worktree path if found, null otherwise
|
|
84
|
+
*
|
|
85
|
+
* @example
|
|
86
|
+
* ```typescript
|
|
87
|
+
* const path = getWorktreePath('main')
|
|
88
|
+
* if (path) {
|
|
89
|
+
* console.log(`Main branch worktree: ${path}`)
|
|
90
|
+
* }
|
|
91
|
+
* ```
|
|
92
|
+
*/
|
|
93
|
+
export function getWorktreePath(branchName) {
|
|
94
|
+
try {
|
|
95
|
+
const output = execSync('git worktree list --porcelain', {
|
|
96
|
+
encoding: 'utf8',
|
|
97
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
98
|
+
});
|
|
99
|
+
// Parse worktree list output
|
|
100
|
+
const lines = output.split('\n');
|
|
101
|
+
let currentWorktreePath = null;
|
|
102
|
+
for (const line of lines) {
|
|
103
|
+
if (line.startsWith('worktree ')) {
|
|
104
|
+
currentWorktreePath = line.slice('worktree '.length).trim();
|
|
105
|
+
}
|
|
106
|
+
else if (line.startsWith('branch ')) {
|
|
107
|
+
const branchRef = line.slice('branch '.length).trim();
|
|
108
|
+
if (branchRef === `refs/heads/${branchName}` && currentWorktreePath) {
|
|
109
|
+
return currentWorktreePath;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
else if (line === '') {
|
|
113
|
+
// Reset for next worktree entry
|
|
114
|
+
currentWorktreePath = null;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
return null;
|
|
118
|
+
}
|
|
119
|
+
catch {
|
|
120
|
+
return null;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Create a git worktree with the specified branch name.
|
|
125
|
+
*
|
|
126
|
+
* @param branchName - Name of the branch to create
|
|
127
|
+
* @param worktreePath - Path where the worktree should be created
|
|
128
|
+
* @returns Promise that resolves when worktree is created
|
|
129
|
+
* @throws Error if unable to create worktree
|
|
130
|
+
*
|
|
131
|
+
* @example
|
|
132
|
+
* ```typescript
|
|
133
|
+
* await createWorktree('feature-branch', '/path/to/worktree')
|
|
134
|
+
* ```
|
|
135
|
+
*/
|
|
136
|
+
export async function createWorktree(branchName, worktreePath) {
|
|
137
|
+
return new Promise((resolve, reject) => {
|
|
138
|
+
const gitProcess = spawn('git', ['worktree', 'add', '-b', branchName, worktreePath], {
|
|
139
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
140
|
+
});
|
|
141
|
+
let stderr = '';
|
|
142
|
+
gitProcess.stderr.on('data', (data) => {
|
|
143
|
+
stderr += data.toString();
|
|
144
|
+
});
|
|
145
|
+
gitProcess.on('close', (code) => {
|
|
146
|
+
if (code === 0) {
|
|
147
|
+
resolve();
|
|
148
|
+
}
|
|
149
|
+
else {
|
|
150
|
+
const error = new Error(`Git worktree creation failed: ${stderr}`);
|
|
151
|
+
error.code = 'GIT_ERROR';
|
|
152
|
+
error.stderr = stderr;
|
|
153
|
+
reject(error);
|
|
154
|
+
}
|
|
155
|
+
});
|
|
156
|
+
gitProcess.on('error', (err) => {
|
|
157
|
+
reject(new Error(`Failed to execute git command: ${err.message}`));
|
|
158
|
+
});
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Delete a worktree folder and remove worktree from git.
|
|
163
|
+
*
|
|
164
|
+
* @param worktreePath - Path to the worktree to delete
|
|
165
|
+
* @param options - Command options including debug logging
|
|
166
|
+
* @throws Error if unable to delete worktree folder
|
|
167
|
+
*
|
|
168
|
+
* @example
|
|
169
|
+
* ```typescript
|
|
170
|
+
* await deleteWorktreeFolder('/path/to/worktree', {
|
|
171
|
+
* debugLog: (msg) => console.debug(msg)
|
|
172
|
+
* })
|
|
173
|
+
* ```
|
|
174
|
+
*/
|
|
175
|
+
export async function deleteWorktreeFolder(worktreePath, options) {
|
|
176
|
+
const { debugLog } = options || {};
|
|
177
|
+
// First, try to remove the worktree from git
|
|
178
|
+
debugLog?.(`Removing worktree from git: ${worktreePath}`);
|
|
179
|
+
// Platform-specific path escaping for git commands
|
|
180
|
+
const escapedPath = process.platform === 'win32'
|
|
181
|
+
? `"${worktreePath.replaceAll('"', String.raw `\"`)}"` // Windows: double quotes
|
|
182
|
+
: `'${worktreePath.replaceAll('\'', String.raw `'\''`)}'`; // Unix/macOS: single quotes
|
|
183
|
+
try {
|
|
184
|
+
execSync(`git worktree remove ${escapedPath} --force`, {
|
|
185
|
+
encoding: 'utf8',
|
|
186
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
187
|
+
});
|
|
188
|
+
debugLog?.('Git worktree removed successfully');
|
|
189
|
+
}
|
|
190
|
+
catch (error) {
|
|
191
|
+
const err = error;
|
|
192
|
+
// If git reports the worktree doesn't exist, that's fine - the folder might be orphaned
|
|
193
|
+
// We'll still try to delete the folder below
|
|
194
|
+
if (err.message?.includes('not a working tree')) {
|
|
195
|
+
debugLog?.(`Git worktree not found (orphaned folder): ${err.message}`);
|
|
196
|
+
}
|
|
197
|
+
else {
|
|
198
|
+
// For other git errors, log but continue to folder deletion
|
|
199
|
+
debugLog?.(`Git worktree remove failed: ${err.message}`);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
// Always try to delete the folder if it exists
|
|
203
|
+
try {
|
|
204
|
+
await fs.access(worktreePath);
|
|
205
|
+
debugLog?.(`Deleting folder: ${worktreePath}`);
|
|
206
|
+
await fs.rm(worktreePath, { recursive: true, force: true });
|
|
207
|
+
debugLog?.('Folder deleted successfully');
|
|
208
|
+
}
|
|
209
|
+
catch (error) {
|
|
210
|
+
const err = error;
|
|
211
|
+
// If folder doesn't exist, that's fine
|
|
212
|
+
if (err.code === 'ENOENT') {
|
|
213
|
+
debugLog?.('Folder already deleted');
|
|
214
|
+
}
|
|
215
|
+
else {
|
|
216
|
+
// For real errors (permissions, locks, etc), throw
|
|
217
|
+
throw new Error(`Failed to delete worktree folder: ${err.message}`);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Update .gitignore with patterns for installed folders.
|
|
3
|
+
*
|
|
4
|
+
* Creates .gitignore if it doesn't exist, or appends to existing file.
|
|
5
|
+
* Prevents duplicate patterns by checking each pattern individually.
|
|
6
|
+
*
|
|
7
|
+
* @param targetDir - Directory containing .gitignore file
|
|
8
|
+
* @param folders - List of folder names to add as gitignore patterns (e.g., ['_bmad', '.claude'])
|
|
9
|
+
*/
|
|
10
|
+
export declare function updateGitignore(targetDir: string, folders: string[]): Promise<void>;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { promises as fs } from 'node:fs';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
/**
|
|
4
|
+
* Update .gitignore with patterns for installed folders.
|
|
5
|
+
*
|
|
6
|
+
* Creates .gitignore if it doesn't exist, or appends to existing file.
|
|
7
|
+
* Prevents duplicate patterns by checking each pattern individually.
|
|
8
|
+
*
|
|
9
|
+
* @param targetDir - Directory containing .gitignore file
|
|
10
|
+
* @param folders - List of folder names to add as gitignore patterns (e.g., ['_bmad', '.claude'])
|
|
11
|
+
*/
|
|
12
|
+
export async function updateGitignore(targetDir, folders) {
|
|
13
|
+
const gitignorePath = join(targetDir, '.gitignore');
|
|
14
|
+
try {
|
|
15
|
+
// Try to read existing .gitignore
|
|
16
|
+
const existing = await fs.readFile(gitignorePath, 'utf8');
|
|
17
|
+
// Filter out patterns that already exist in .gitignore
|
|
18
|
+
const newPatterns = folders.filter((folder) => {
|
|
19
|
+
const pattern = `${folder}/`;
|
|
20
|
+
// Check if this exact pattern exists in the file
|
|
21
|
+
return !existing.includes(pattern);
|
|
22
|
+
});
|
|
23
|
+
// If no new patterns to add, we're done
|
|
24
|
+
if (newPatterns.length === 0) {
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
// Build patterns string
|
|
28
|
+
const patterns = newPatterns.map((folder) => `${folder}/`).join('\n');
|
|
29
|
+
// Check if AIW Installation header already exists
|
|
30
|
+
const hasAiwHeader = existing.includes('# AIW Installation');
|
|
31
|
+
let updatedContent;
|
|
32
|
+
if (hasAiwHeader) {
|
|
33
|
+
// Find the AIW Installation section and append to it
|
|
34
|
+
const lines = existing.split('\n');
|
|
35
|
+
const headerIndex = lines.findIndex((line) => line.includes('# AIW Installation'));
|
|
36
|
+
if (headerIndex === -1) {
|
|
37
|
+
// Fallback: append at the end with header (shouldn't happen, but defensive)
|
|
38
|
+
const separator = existing.endsWith('\n') ? '\n' : '\n\n';
|
|
39
|
+
updatedContent = existing + separator + `# AIW Installation\n${patterns}\n`;
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
// Insert new patterns right after the header
|
|
43
|
+
lines.splice(headerIndex + 1, 0, patterns);
|
|
44
|
+
updatedContent = lines.join('\n');
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
// Add new section with header
|
|
49
|
+
const separator = existing.length > 0 && existing.endsWith('\n') ? '\n' : existing.length > 0 ? '\n\n' : '';
|
|
50
|
+
updatedContent = existing + separator + `# AIW Installation\n${patterns}\n`;
|
|
51
|
+
}
|
|
52
|
+
await fs.writeFile(gitignorePath, updatedContent, 'utf8');
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
// .gitignore doesn't exist, create it
|
|
56
|
+
const patterns = folders.map((folder) => `${folder}/`).join('\n');
|
|
57
|
+
const patternsBlock = `# AIW Installation\n${patterns}`;
|
|
58
|
+
await fs.writeFile(gitignorePath, patternsBlock + '\n', 'utf8');
|
|
59
|
+
}
|
|
60
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { ClaudeSettings, HooksConfig } from './claude-settings-types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Merge hooks configurations from template into existing
|
|
4
|
+
*
|
|
5
|
+
* Strategy:
|
|
6
|
+
* - For each event type in template hooks
|
|
7
|
+
* - Concatenate with existing hooks for that event
|
|
8
|
+
* - Deduplicate based on matcher configuration
|
|
9
|
+
* - Maintain order: existing hooks first, then template hooks
|
|
10
|
+
*
|
|
11
|
+
* @param existing - Existing hooks configuration (will not be modified)
|
|
12
|
+
* @param template - Template hooks configuration to merge
|
|
13
|
+
* @returns New merged hooks configuration
|
|
14
|
+
*/
|
|
15
|
+
export declare function mergeHooks(existing: HooksConfig | undefined, template: HooksConfig | undefined): HooksConfig;
|
|
16
|
+
/**
|
|
17
|
+
* Merge complete Claude settings configurations
|
|
18
|
+
*
|
|
19
|
+
* Strategy:
|
|
20
|
+
* - Shallow merge most properties (template overrides existing)
|
|
21
|
+
* - Deep merge hooks using mergeHooks function
|
|
22
|
+
* - Deep merge permissions by concatenating arrays
|
|
23
|
+
*
|
|
24
|
+
* @param existing - Existing settings (will not be modified)
|
|
25
|
+
* @param template - Template settings to merge
|
|
26
|
+
* @returns New merged settings configuration
|
|
27
|
+
*/
|
|
28
|
+
export declare function mergeClaudeSettings(existing: ClaudeSettings | undefined, template: ClaudeSettings | undefined): ClaudeSettings;
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { mergeArraysWithDedup, mergeConfigByEventType } from './generic-merge.js';
|
|
2
|
+
/**
|
|
3
|
+
* Check if two hook commands are equivalent
|
|
4
|
+
*/
|
|
5
|
+
function areHookCommandsEqual(a, b) {
|
|
6
|
+
return a.type === b.type && a.command === b.command && a.timeout === b.timeout;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Check if two hook matchers are equivalent
|
|
10
|
+
*/
|
|
11
|
+
function areHookMatchersEqual(a, b) {
|
|
12
|
+
if (a.matcher !== b.matcher || a.once !== b.once) {
|
|
13
|
+
return false;
|
|
14
|
+
}
|
|
15
|
+
if (a.hooks.length !== b.hooks.length) {
|
|
16
|
+
return false;
|
|
17
|
+
}
|
|
18
|
+
// Check if all hooks are equivalent (order-independent)
|
|
19
|
+
return a.hooks.every((hookA) => b.hooks.some((hookB) => areHookCommandsEqual(hookA, hookB)));
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Merge hooks configurations from template into existing
|
|
23
|
+
*
|
|
24
|
+
* Strategy:
|
|
25
|
+
* - For each event type in template hooks
|
|
26
|
+
* - Concatenate with existing hooks for that event
|
|
27
|
+
* - Deduplicate based on matcher configuration
|
|
28
|
+
* - Maintain order: existing hooks first, then template hooks
|
|
29
|
+
*
|
|
30
|
+
* @param existing - Existing hooks configuration (will not be modified)
|
|
31
|
+
* @param template - Template hooks configuration to merge
|
|
32
|
+
* @returns New merged hooks configuration
|
|
33
|
+
*/
|
|
34
|
+
export function mergeHooks(existing, template) {
|
|
35
|
+
return mergeConfigByEventType(existing, template, (existingMatchers, templateMatchers) => mergeArraysWithDedup(existingMatchers, templateMatchers, areHookMatchersEqual));
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Merge complete Claude settings configurations
|
|
39
|
+
*
|
|
40
|
+
* Strategy:
|
|
41
|
+
* - Shallow merge most properties (template overrides existing)
|
|
42
|
+
* - Deep merge hooks using mergeHooks function
|
|
43
|
+
* - Deep merge permissions by concatenating arrays
|
|
44
|
+
*
|
|
45
|
+
* @param existing - Existing settings (will not be modified)
|
|
46
|
+
* @param template - Template settings to merge
|
|
47
|
+
* @returns New merged settings configuration
|
|
48
|
+
*/
|
|
49
|
+
export function mergeClaudeSettings(existing, template) {
|
|
50
|
+
// If no template settings, return existing (or empty object)
|
|
51
|
+
if (!template || Object.keys(template).length === 0) {
|
|
52
|
+
return existing || {};
|
|
53
|
+
}
|
|
54
|
+
// If no existing settings, return template
|
|
55
|
+
if (!existing || Object.keys(existing).length === 0) {
|
|
56
|
+
return template;
|
|
57
|
+
}
|
|
58
|
+
// Merge permissions by concatenating arrays (no duplicates)
|
|
59
|
+
const mergedPermissions = {
|
|
60
|
+
allow: [...new Set([...(existing.permissions?.allow || []), ...(template.permissions?.allow || [])])],
|
|
61
|
+
deny: [...new Set([...(existing.permissions?.deny || []), ...(template.permissions?.deny || [])])],
|
|
62
|
+
};
|
|
63
|
+
// Merge environment variables (template values override)
|
|
64
|
+
const mergedEnv = {
|
|
65
|
+
...existing.env,
|
|
66
|
+
...template.env,
|
|
67
|
+
};
|
|
68
|
+
// Merge enabled plugins (template values override)
|
|
69
|
+
const mergedEnabledPlugins = {
|
|
70
|
+
...existing.enabledPlugins,
|
|
71
|
+
...template.enabledPlugins,
|
|
72
|
+
};
|
|
73
|
+
// Merge hooks using dedicated function
|
|
74
|
+
const mergedHooks = mergeHooks(existing.hooks, template.hooks);
|
|
75
|
+
// Merge methods tracking (existing takes precedence, template adds new)
|
|
76
|
+
const mergedMethods = {
|
|
77
|
+
...template.methods,
|
|
78
|
+
...existing.methods,
|
|
79
|
+
};
|
|
80
|
+
// Create merged settings
|
|
81
|
+
const merged = {
|
|
82
|
+
...existing,
|
|
83
|
+
...template,
|
|
84
|
+
permissions: mergedPermissions,
|
|
85
|
+
env: mergedEnv,
|
|
86
|
+
enabledPlugins: mergedEnabledPlugins,
|
|
87
|
+
hooks: mergedHooks,
|
|
88
|
+
};
|
|
89
|
+
// Only add methods if there are any (avoid setting to undefined)
|
|
90
|
+
if (Object.keys(mergedMethods).length > 0) {
|
|
91
|
+
merged.methods = mergedMethods;
|
|
92
|
+
}
|
|
93
|
+
return merged;
|
|
94
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Centralized IDE and AIW path resolver.
|
|
3
|
+
* Provides consistent path construction for all IDE and AIW folders/files.
|
|
4
|
+
*
|
|
5
|
+
* This utility ensures that path construction is consistent across the codebase
|
|
6
|
+
* and reduces coupling by providing a single source of truth for path patterns.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```typescript
|
|
10
|
+
* const resolver = new IdePathResolver('/path/to/project')
|
|
11
|
+
*
|
|
12
|
+
* // Get .claude folder path
|
|
13
|
+
* const claudePath = resolver.getClaudeDir()
|
|
14
|
+
* // Returns: /path/to/project/.claude
|
|
15
|
+
*
|
|
16
|
+
* // Get .claude/settings.json path
|
|
17
|
+
* const settingsPath = resolver.getClaudeSettings()
|
|
18
|
+
* // Returns: /path/to/project/.claude/settings.json
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
21
|
+
export declare class IdePathResolver {
|
|
22
|
+
private readonly projectRoot;
|
|
23
|
+
constructor(projectRoot: string);
|
|
24
|
+
/**
|
|
25
|
+
* Get the .aiwcli container directory path
|
|
26
|
+
*
|
|
27
|
+
* @returns Absolute path to .aiwcli directory
|
|
28
|
+
*/
|
|
29
|
+
getAiwcliContainer(): string;
|
|
30
|
+
/**
|
|
31
|
+
* Get path to a folder within the .aiwcli container
|
|
32
|
+
*
|
|
33
|
+
* @param folderName - Folder name within .aiwcli (e.g., '_shared', '_bmad')
|
|
34
|
+
* @returns Absolute path to the folder
|
|
35
|
+
*/
|
|
36
|
+
getAiwcliFolder(folderName: string): string;
|
|
37
|
+
/**
|
|
38
|
+
* Get path to a file/folder within .claude directory
|
|
39
|
+
*
|
|
40
|
+
* @param relativePath - Relative path within .claude (e.g., 'settings.json', 'commands/bmad')
|
|
41
|
+
* @returns Absolute path to the file/folder
|
|
42
|
+
*/
|
|
43
|
+
getClaude(relativePath: string): string;
|
|
44
|
+
/**
|
|
45
|
+
* Get the .claude directory path
|
|
46
|
+
*
|
|
47
|
+
* @returns Absolute path to .claude directory
|
|
48
|
+
*/
|
|
49
|
+
getClaudeDir(): string;
|
|
50
|
+
/**
|
|
51
|
+
* Get the .claude/settings.json path
|
|
52
|
+
*
|
|
53
|
+
* @returns Absolute path to Claude settings file
|
|
54
|
+
*/
|
|
55
|
+
getClaudeSettings(): string;
|
|
56
|
+
/**
|
|
57
|
+
* Get IDE directory path by IDE name
|
|
58
|
+
*
|
|
59
|
+
* @param ideName - IDE name ('claude', 'windsurf', etc.)
|
|
60
|
+
* @returns Absolute path to IDE directory
|
|
61
|
+
*/
|
|
62
|
+
getIdeDir(ideName: string): string;
|
|
63
|
+
/**
|
|
64
|
+
* Get method-specific folder path within .aiwcli
|
|
65
|
+
* Convention: _{methodName} (e.g., _bmad, _gsd, _cc-native)
|
|
66
|
+
*
|
|
67
|
+
* @param methodName - Method name (e.g., 'bmad', 'gsd')
|
|
68
|
+
* @returns Absolute path to method folder
|
|
69
|
+
*/
|
|
70
|
+
getMethodFolder(methodName: string): string;
|
|
71
|
+
/**
|
|
72
|
+
* Get the project root directory
|
|
73
|
+
*
|
|
74
|
+
* @returns Absolute path to project root
|
|
75
|
+
*/
|
|
76
|
+
getProjectRoot(): string;
|
|
77
|
+
/**
|
|
78
|
+
* Get the shared folder path within .aiwcli
|
|
79
|
+
*
|
|
80
|
+
* @returns Absolute path to _shared directory
|
|
81
|
+
*/
|
|
82
|
+
getSharedFolder(): string;
|
|
83
|
+
/**
|
|
84
|
+
* Get path to a file/folder within .windsurf directory
|
|
85
|
+
*
|
|
86
|
+
* @param relativePath - Relative path within .windsurf (e.g., 'hooks.json', 'workflows/bmad')
|
|
87
|
+
* @returns Absolute path to the file/folder
|
|
88
|
+
*/
|
|
89
|
+
getWindsurf(relativePath: string): string;
|
|
90
|
+
/**
|
|
91
|
+
* Get the .windsurf directory path
|
|
92
|
+
*
|
|
93
|
+
* @returns Absolute path to .windsurf directory
|
|
94
|
+
*/
|
|
95
|
+
getWindsurfDir(): string;
|
|
96
|
+
/**
|
|
97
|
+
* Get the .windsurf/hooks.json path
|
|
98
|
+
*
|
|
99
|
+
* @returns Absolute path to Windsurf hooks file
|
|
100
|
+
*/
|
|
101
|
+
getWindsurfHooks(): string;
|
|
102
|
+
}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { join } from 'node:path';
|
|
2
|
+
/**
|
|
3
|
+
* Centralized IDE and AIW path resolver.
|
|
4
|
+
* Provides consistent path construction for all IDE and AIW folders/files.
|
|
5
|
+
*
|
|
6
|
+
* This utility ensures that path construction is consistent across the codebase
|
|
7
|
+
* and reduces coupling by providing a single source of truth for path patterns.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```typescript
|
|
11
|
+
* const resolver = new IdePathResolver('/path/to/project')
|
|
12
|
+
*
|
|
13
|
+
* // Get .claude folder path
|
|
14
|
+
* const claudePath = resolver.getClaudeDir()
|
|
15
|
+
* // Returns: /path/to/project/.claude
|
|
16
|
+
*
|
|
17
|
+
* // Get .claude/settings.json path
|
|
18
|
+
* const settingsPath = resolver.getClaudeSettings()
|
|
19
|
+
* // Returns: /path/to/project/.claude/settings.json
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
export class IdePathResolver {
|
|
23
|
+
projectRoot;
|
|
24
|
+
constructor(projectRoot) {
|
|
25
|
+
this.projectRoot = projectRoot;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Get the .aiwcli container directory path
|
|
29
|
+
*
|
|
30
|
+
* @returns Absolute path to .aiwcli directory
|
|
31
|
+
*/
|
|
32
|
+
getAiwcliContainer() {
|
|
33
|
+
return join(this.projectRoot, '.aiwcli');
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Get path to a folder within the .aiwcli container
|
|
37
|
+
*
|
|
38
|
+
* @param folderName - Folder name within .aiwcli (e.g., '_shared', '_bmad')
|
|
39
|
+
* @returns Absolute path to the folder
|
|
40
|
+
*/
|
|
41
|
+
getAiwcliFolder(folderName) {
|
|
42
|
+
return join(this.getAiwcliContainer(), folderName);
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Get path to a file/folder within .claude directory
|
|
46
|
+
*
|
|
47
|
+
* @param relativePath - Relative path within .claude (e.g., 'settings.json', 'commands/bmad')
|
|
48
|
+
* @returns Absolute path to the file/folder
|
|
49
|
+
*/
|
|
50
|
+
getClaude(relativePath) {
|
|
51
|
+
return join(this.getClaudeDir(), relativePath);
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Get the .claude directory path
|
|
55
|
+
*
|
|
56
|
+
* @returns Absolute path to .claude directory
|
|
57
|
+
*/
|
|
58
|
+
getClaudeDir() {
|
|
59
|
+
return join(this.projectRoot, '.claude');
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Get the .claude/settings.json path
|
|
63
|
+
*
|
|
64
|
+
* @returns Absolute path to Claude settings file
|
|
65
|
+
*/
|
|
66
|
+
getClaudeSettings() {
|
|
67
|
+
return this.getClaude('settings.json');
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Get IDE directory path by IDE name
|
|
71
|
+
*
|
|
72
|
+
* @param ideName - IDE name ('claude', 'windsurf', etc.)
|
|
73
|
+
* @returns Absolute path to IDE directory
|
|
74
|
+
*/
|
|
75
|
+
getIdeDir(ideName) {
|
|
76
|
+
return join(this.projectRoot, `.${ideName}`);
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Get method-specific folder path within .aiwcli
|
|
80
|
+
* Convention: _{methodName} (e.g., _bmad, _gsd, _cc-native)
|
|
81
|
+
*
|
|
82
|
+
* @param methodName - Method name (e.g., 'bmad', 'gsd')
|
|
83
|
+
* @returns Absolute path to method folder
|
|
84
|
+
*/
|
|
85
|
+
getMethodFolder(methodName) {
|
|
86
|
+
return this.getAiwcliFolder(`_${methodName}`);
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Get the project root directory
|
|
90
|
+
*
|
|
91
|
+
* @returns Absolute path to project root
|
|
92
|
+
*/
|
|
93
|
+
getProjectRoot() {
|
|
94
|
+
return this.projectRoot;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Get the shared folder path within .aiwcli
|
|
98
|
+
*
|
|
99
|
+
* @returns Absolute path to _shared directory
|
|
100
|
+
*/
|
|
101
|
+
getSharedFolder() {
|
|
102
|
+
return this.getAiwcliFolder('_shared');
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Get path to a file/folder within .windsurf directory
|
|
106
|
+
*
|
|
107
|
+
* @param relativePath - Relative path within .windsurf (e.g., 'hooks.json', 'workflows/bmad')
|
|
108
|
+
* @returns Absolute path to the file/folder
|
|
109
|
+
*/
|
|
110
|
+
getWindsurf(relativePath) {
|
|
111
|
+
return join(this.getWindsurfDir(), relativePath);
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Get the .windsurf directory path
|
|
115
|
+
*
|
|
116
|
+
* @returns Absolute path to .windsurf directory
|
|
117
|
+
*/
|
|
118
|
+
getWindsurfDir() {
|
|
119
|
+
return join(this.projectRoot, '.windsurf');
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Get the .windsurf/hooks.json path
|
|
123
|
+
*
|
|
124
|
+
* @returns Absolute path to Windsurf hooks file
|
|
125
|
+
*/
|
|
126
|
+
getWindsurfHooks() {
|
|
127
|
+
return this.getWindsurf('hooks.json');
|
|
128
|
+
}
|
|
129
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared library code for AI Workflow CLI.
|
|
3
|
+
* Re-exports all library modules from this barrel file.
|
|
4
|
+
*/
|
|
5
|
+
export { type AiwcliConfig, getAiwDir, loadConfig, validateAiwDir, } from './config.js';
|
|
6
|
+
export { debug, debugConfig, debugSpawn, debugVersion, isDebugEnabled, setDebugEnabled } from './debug.js';
|
|
7
|
+
export { getAiwConfig, getAiwDir as getAiwDirFromEnv, isUsingLegacyEnvVars, loadEnvWithCompatibility, } from './env-compat.js';
|
|
8
|
+
export { AiwError, ConfigNotFoundError, EnvironmentError, formatErrorMessage, InvalidUsageError, ProcessSpawnError, } from './errors.js';
|
|
9
|
+
export { mergeArraysWithDedup, mergeConfigByEventType } from './generic-merge.js';
|
|
10
|
+
export { branchExists, createWorktree, deleteBranch, deleteWorktreeFolder, getAllWorktrees, getCurrentBranch, getMainBranch, getWorktreePath, type GitCommandOptions, hasMergeRequest, hasUnpushedCommits, type WorktreeInfo, } from './git/index.js';
|
|
11
|
+
export { expandPath, findWorkspaceRoot, getHomePath, getWorkspacePath, isWorkspace, normalizePath, pathExists, resolvePath, toUnixPath, toWindowsPath, } from './paths.js';
|
|
12
|
+
export { spawnProcess, type SpawnProcessOptions } from './spawn.js';
|
|
13
|
+
export { escapeShellArg, launchTerminal, type TerminalLaunchOptions, type TerminalLaunchResult, } from './terminal.js';
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared library code for AI Workflow CLI.
|
|
3
|
+
* Re-exports all library modules from this barrel file.
|
|
4
|
+
*/
|
|
5
|
+
// Configuration resolution
|
|
6
|
+
export { getAiwDir, loadConfig, validateAiwDir, } from './config.js';
|
|
7
|
+
// Debug logging
|
|
8
|
+
export { debug, debugConfig, debugSpawn, debugVersion, isDebugEnabled, setDebugEnabled } from './debug.js';
|
|
9
|
+
// Environment variable compatibility
|
|
10
|
+
export { getAiwConfig, getAiwDir as getAiwDirFromEnv, isUsingLegacyEnvVars, loadEnvWithCompatibility, } from './env-compat.js';
|
|
11
|
+
// Custom error classes and utilities
|
|
12
|
+
export { AiwError, ConfigNotFoundError, EnvironmentError, formatErrorMessage, InvalidUsageError, ProcessSpawnError, } from './errors.js';
|
|
13
|
+
// Generic merge utilities
|
|
14
|
+
export { mergeArraysWithDedup, mergeConfigByEventType } from './generic-merge.js';
|
|
15
|
+
// Git utilities
|
|
16
|
+
export { branchExists, createWorktree, deleteBranch, deleteWorktreeFolder, getAllWorktrees, getCurrentBranch, getMainBranch, getWorktreePath, hasMergeRequest, hasUnpushedCommits, } from './git/index.js';
|
|
17
|
+
// Cross-platform path utilities
|
|
18
|
+
export { expandPath, findWorkspaceRoot, getHomePath, getWorkspacePath, isWorkspace, normalizePath, pathExists, resolvePath, toUnixPath, toWindowsPath, } from './paths.js';
|
|
19
|
+
// Process spawning utilities
|
|
20
|
+
export { spawnProcess } from './spawn.js';
|
|
21
|
+
// Cross-platform terminal launching
|
|
22
|
+
export { escapeShellArg, launchTerminal, } from './terminal.js';
|