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.
Files changed (204) hide show
  1. package/README.md +1248 -0
  2. package/bin/dev.cmd +3 -0
  3. package/bin/dev.js +16 -0
  4. package/bin/run.cmd +3 -0
  5. package/bin/run.js +19 -0
  6. package/dist/commands/branch.d.ts +45 -0
  7. package/dist/commands/branch.js +488 -0
  8. package/dist/commands/clean.d.ts +34 -0
  9. package/dist/commands/clean.js +186 -0
  10. package/dist/commands/clear.d.ts +51 -0
  11. package/dist/commands/clear.js +835 -0
  12. package/dist/commands/init/index.d.ts +107 -0
  13. package/dist/commands/init/index.js +565 -0
  14. package/dist/commands/launch.d.ts +21 -0
  15. package/dist/commands/launch.js +108 -0
  16. package/dist/index.d.ts +1 -0
  17. package/dist/index.js +1 -0
  18. package/dist/lib/base-command.d.ts +114 -0
  19. package/dist/lib/base-command.js +153 -0
  20. package/dist/lib/bmad-installer.d.ts +38 -0
  21. package/dist/lib/bmad-installer.js +145 -0
  22. package/dist/lib/claude-settings-types.d.ts +102 -0
  23. package/dist/lib/claude-settings-types.js +5 -0
  24. package/dist/lib/config.d.ts +25 -0
  25. package/dist/lib/config.js +46 -0
  26. package/dist/lib/debug.d.ts +39 -0
  27. package/dist/lib/debug.js +74 -0
  28. package/dist/lib/env-compat.d.ts +26 -0
  29. package/dist/lib/env-compat.js +35 -0
  30. package/dist/lib/errors.d.ts +126 -0
  31. package/dist/lib/errors.js +145 -0
  32. package/dist/lib/generic-merge.d.ts +74 -0
  33. package/dist/lib/generic-merge.js +105 -0
  34. package/dist/lib/git/branch.d.ts +67 -0
  35. package/dist/lib/git/branch.js +155 -0
  36. package/dist/lib/git/index.d.ts +11 -0
  37. package/dist/lib/git/index.js +13 -0
  38. package/dist/lib/git/safety-checks.d.ts +44 -0
  39. package/dist/lib/git/safety-checks.js +102 -0
  40. package/dist/lib/git/types.d.ts +31 -0
  41. package/dist/lib/git/types.js +6 -0
  42. package/dist/lib/git/worktree.d.ts +67 -0
  43. package/dist/lib/git/worktree.js +220 -0
  44. package/dist/lib/gitignore-manager.d.ts +10 -0
  45. package/dist/lib/gitignore-manager.js +60 -0
  46. package/dist/lib/hooks-merger.d.ts +28 -0
  47. package/dist/lib/hooks-merger.js +94 -0
  48. package/dist/lib/ide-path-resolver.d.ts +102 -0
  49. package/dist/lib/ide-path-resolver.js +129 -0
  50. package/dist/lib/index.d.ts +13 -0
  51. package/dist/lib/index.js +22 -0
  52. package/dist/lib/output.d.ts +51 -0
  53. package/dist/lib/output.js +76 -0
  54. package/dist/lib/paths.d.ts +66 -0
  55. package/dist/lib/paths.js +136 -0
  56. package/dist/lib/quiet.d.ts +12 -0
  57. package/dist/lib/quiet.js +17 -0
  58. package/dist/lib/settings-hierarchy.d.ts +42 -0
  59. package/dist/lib/settings-hierarchy.js +105 -0
  60. package/dist/lib/spawn.d.ts +105 -0
  61. package/dist/lib/spawn.js +157 -0
  62. package/dist/lib/spinner.d.ts +19 -0
  63. package/dist/lib/spinner.js +34 -0
  64. package/dist/lib/stdin.d.ts +48 -0
  65. package/dist/lib/stdin.js +60 -0
  66. package/dist/lib/template-installer.d.ts +92 -0
  67. package/dist/lib/template-installer.js +375 -0
  68. package/dist/lib/template-linter.d.ts +49 -0
  69. package/dist/lib/template-linter.js +173 -0
  70. package/dist/lib/template-merger.d.ts +47 -0
  71. package/dist/lib/template-merger.js +173 -0
  72. package/dist/lib/template-resolver.d.ts +20 -0
  73. package/dist/lib/template-resolver.js +60 -0
  74. package/dist/lib/terminal.d.ts +102 -0
  75. package/dist/lib/terminal.js +245 -0
  76. package/dist/lib/tty-detection.d.ts +62 -0
  77. package/dist/lib/tty-detection.js +83 -0
  78. package/dist/lib/user-utils.d.ts +5 -0
  79. package/dist/lib/user-utils.js +23 -0
  80. package/dist/lib/version.d.ts +99 -0
  81. package/dist/lib/version.js +144 -0
  82. package/dist/lib/watch-templates.d.ts +6 -0
  83. package/dist/lib/watch-templates.js +73 -0
  84. package/dist/lib/windsurf-hooks-hierarchy.d.ts +30 -0
  85. package/dist/lib/windsurf-hooks-hierarchy.js +66 -0
  86. package/dist/lib/windsurf-hooks-merger.d.ts +26 -0
  87. package/dist/lib/windsurf-hooks-merger.js +53 -0
  88. package/dist/lib/windsurf-hooks-types.d.ts +33 -0
  89. package/dist/lib/windsurf-hooks-types.js +5 -0
  90. package/dist/templates/CLAUDE.md +174 -0
  91. package/dist/templates/_shared/.claude/commands/handoff.md +14 -0
  92. package/dist/templates/_shared/.claude/settings.json +61 -0
  93. package/dist/templates/_shared/.codex/workflows/handoff.md +14 -0
  94. package/dist/templates/_shared/.windsurf/workflows/handoff.md +14 -0
  95. package/dist/templates/_shared/hooks/__init__.py +16 -0
  96. package/dist/templates/_shared/hooks/archive_plan.py +270 -0
  97. package/dist/templates/_shared/hooks/context_enforcer.py +621 -0
  98. package/dist/templates/_shared/hooks/context_monitor.py +322 -0
  99. package/dist/templates/_shared/hooks/file-suggestion.py +188 -0
  100. package/dist/templates/_shared/hooks/task_create_capture.py +194 -0
  101. package/dist/templates/_shared/hooks/task_update_capture.py +254 -0
  102. package/dist/templates/_shared/hooks/user_prompt_submit.py +157 -0
  103. package/dist/templates/_shared/lib/__init__.py +1 -0
  104. package/dist/templates/_shared/lib/base/__init__.py +49 -0
  105. package/dist/templates/_shared/lib/base/__pycache__/constants.cpython-313.pyc +0 -0
  106. package/dist/templates/_shared/lib/base/atomic_write.py +180 -0
  107. package/dist/templates/_shared/lib/base/constants.py +299 -0
  108. package/dist/templates/_shared/lib/base/inference.py +189 -0
  109. package/dist/templates/_shared/lib/base/utils.py +216 -0
  110. package/dist/templates/_shared/lib/context/__init__.py +119 -0
  111. package/dist/templates/_shared/lib/context/__pycache__/__init__.cpython-313.pyc +0 -0
  112. package/dist/templates/_shared/lib/context/__pycache__/cache.cpython-313.pyc +0 -0
  113. package/dist/templates/_shared/lib/context/__pycache__/context_manager.cpython-313.pyc +0 -0
  114. package/dist/templates/_shared/lib/context/__pycache__/event_log.cpython-313.pyc +0 -0
  115. package/dist/templates/_shared/lib/context/cache.py +446 -0
  116. package/dist/templates/_shared/lib/context/context_manager.py +1171 -0
  117. package/dist/templates/_shared/lib/context/discovery.py +486 -0
  118. package/dist/templates/_shared/lib/context/event_log.py +308 -0
  119. package/dist/templates/_shared/lib/context/plan_archive.py +247 -0
  120. package/dist/templates/_shared/lib/context/task_sync.py +367 -0
  121. package/dist/templates/_shared/lib/handoff/__init__.py +22 -0
  122. package/dist/templates/_shared/lib/handoff/document_generator.py +307 -0
  123. package/dist/templates/_shared/lib/templates/README.md +215 -0
  124. package/dist/templates/_shared/lib/templates/__init__.py +40 -0
  125. package/dist/templates/_shared/lib/templates/formatters.py +147 -0
  126. package/dist/templates/_shared/lib/templates/plan_context.py +119 -0
  127. package/dist/templates/_shared/scripts/save_handoff.py +99 -0
  128. package/dist/templates/_shared/workflows/handoff.md +212 -0
  129. package/dist/templates/cc-native/.claude/agents/cc-native/ACCESSIBILITY-TESTER.md +80 -0
  130. package/dist/templates/cc-native/.claude/agents/cc-native/ARCHITECT-REVIEWER.md +75 -0
  131. package/dist/templates/cc-native/.claude/agents/cc-native/ASSUMPTION-CHAIN-TRACER.md +239 -0
  132. package/dist/templates/cc-native/.claude/agents/cc-native/CLARITY-AUDITOR.md +109 -0
  133. package/dist/templates/cc-native/.claude/agents/cc-native/CODE-REVIEWER.md +71 -0
  134. package/dist/templates/cc-native/.claude/agents/cc-native/COMPLETENESS-CHECKER.md +104 -0
  135. package/dist/templates/cc-native/.claude/agents/cc-native/CONTEXT-EXTRACTOR.md +93 -0
  136. package/dist/templates/cc-native/.claude/agents/cc-native/DEVILS-ADVOCATE.md +223 -0
  137. package/dist/templates/cc-native/.claude/agents/cc-native/DOCUMENTATION-REVIEWER.md +73 -0
  138. package/dist/templates/cc-native/.claude/agents/cc-native/FEASIBILITY-ANALYST.md +93 -0
  139. package/dist/templates/cc-native/.claude/agents/cc-native/FRESH-PERSPECTIVE.md +103 -0
  140. package/dist/templates/cc-native/.claude/agents/cc-native/HANDOFF-READINESS.md +145 -0
  141. package/dist/templates/cc-native/.claude/agents/cc-native/HIDDEN-COMPLEXITY-DETECTOR.md +248 -0
  142. package/dist/templates/cc-native/.claude/agents/cc-native/INCENTIVE-MAPPER.md +235 -0
  143. package/dist/templates/cc-native/.claude/agents/cc-native/PENETRATION-TESTER.md +80 -0
  144. package/dist/templates/cc-native/.claude/agents/cc-native/PERFORMANCE-ENGINEER.md +76 -0
  145. package/dist/templates/cc-native/.claude/agents/cc-native/PLAN-ORCHESTRATOR.md +141 -0
  146. package/dist/templates/cc-native/.claude/agents/cc-native/PRECEDENT-FINDER.md +240 -0
  147. package/dist/templates/cc-native/.claude/agents/cc-native/REVERSIBILITY-ANALYST.md +211 -0
  148. package/dist/templates/cc-native/.claude/agents/cc-native/RISK-ASSESSOR.md +101 -0
  149. package/dist/templates/cc-native/.claude/agents/cc-native/SECOND-ORDER-ANALYST.md +197 -0
  150. package/dist/templates/cc-native/.claude/agents/cc-native/SIMPLICITY-GUARDIAN.md +97 -0
  151. package/dist/templates/cc-native/.claude/agents/cc-native/SKEPTIC.md +349 -0
  152. package/dist/templates/cc-native/.claude/agents/cc-native/STAKEHOLDER-ADVOCATE.md +106 -0
  153. package/dist/templates/cc-native/.claude/agents/cc-native/TRADE-OFF-ILLUMINATOR.md +205 -0
  154. package/dist/templates/cc-native/.claude/commands/cc-native/fresh-perspective.md +8 -0
  155. package/dist/templates/cc-native/.claude/commands/cc-native/specdev.md +10 -0
  156. package/dist/templates/cc-native/.claude/settings.json +119 -0
  157. package/dist/templates/cc-native/.windsurf/workflows/cc-native/fix.md +8 -0
  158. package/dist/templates/cc-native/.windsurf/workflows/cc-native/fresh-perspective.md +8 -0
  159. package/dist/templates/cc-native/.windsurf/workflows/cc-native/implement.md +8 -0
  160. package/dist/templates/cc-native/.windsurf/workflows/cc-native/research.md +8 -0
  161. package/dist/templates/cc-native/CC-NATIVE-README.md +192 -0
  162. package/dist/templates/cc-native/MIGRATION.md +86 -0
  163. package/dist/templates/cc-native/TEMPLATE-SCHEMA.md +331 -0
  164. package/dist/templates/cc-native/_cc-native/docs/PERMISSION_REQUEST_VERIFICATION.md +147 -0
  165. package/dist/templates/cc-native/_cc-native/hooks/__pycache__/add_plan_context.cpython-313.pyc +0 -0
  166. package/dist/templates/cc-native/_cc-native/hooks/__pycache__/archive_plan.cpython-313.pyc +0 -0
  167. package/dist/templates/cc-native/_cc-native/hooks/__pycache__/cc-native-agent-review.cpython-313.pyc +0 -0
  168. package/dist/templates/cc-native/_cc-native/hooks/__pycache__/cc-native-plan-review.cpython-313.pyc +0 -0
  169. package/dist/templates/cc-native/_cc-native/hooks/__pycache__/test_permission_request.cpython-313.pyc +0 -0
  170. package/dist/templates/cc-native/_cc-native/hooks/add_plan_context.py +150 -0
  171. package/dist/templates/cc-native/_cc-native/hooks/cc-native-plan-review.py +746 -0
  172. package/dist/templates/cc-native/_cc-native/hooks/suggest-fresh-perspective.py +339 -0
  173. package/dist/templates/cc-native/_cc-native/lib/__init__.py +57 -0
  174. package/dist/templates/cc-native/_cc-native/lib/__pycache__/__init__.cpython-313.pyc +0 -0
  175. package/dist/templates/cc-native/_cc-native/lib/__pycache__/orchestrator.cpython-313.pyc +0 -0
  176. package/dist/templates/cc-native/_cc-native/lib/__pycache__/state.cpython-313.pyc +0 -0
  177. package/dist/templates/cc-native/_cc-native/lib/__pycache__/utils.cpython-313.pyc +0 -0
  178. package/dist/templates/cc-native/_cc-native/lib/async_archive.py +68 -0
  179. package/dist/templates/cc-native/_cc-native/lib/atomic_write.py +98 -0
  180. package/dist/templates/cc-native/_cc-native/lib/constants.py +45 -0
  181. package/dist/templates/cc-native/_cc-native/lib/orchestrator.py +273 -0
  182. package/dist/templates/cc-native/_cc-native/lib/reviewers/__init__.py +28 -0
  183. package/dist/templates/cc-native/_cc-native/lib/reviewers/__pycache__/__init__.cpython-313.pyc +0 -0
  184. package/dist/templates/cc-native/_cc-native/lib/reviewers/__pycache__/agent.cpython-313.pyc +0 -0
  185. package/dist/templates/cc-native/_cc-native/lib/reviewers/__pycache__/base.cpython-313.pyc +0 -0
  186. package/dist/templates/cc-native/_cc-native/lib/reviewers/__pycache__/codex.cpython-313.pyc +0 -0
  187. package/dist/templates/cc-native/_cc-native/lib/reviewers/__pycache__/gemini.cpython-313.pyc +0 -0
  188. package/dist/templates/cc-native/_cc-native/lib/reviewers/agent.py +164 -0
  189. package/dist/templates/cc-native/_cc-native/lib/reviewers/base.py +89 -0
  190. package/dist/templates/cc-native/_cc-native/lib/reviewers/codex.py +119 -0
  191. package/dist/templates/cc-native/_cc-native/lib/reviewers/gemini.py +103 -0
  192. package/dist/templates/cc-native/_cc-native/lib/state.py +251 -0
  193. package/dist/templates/cc-native/_cc-native/lib/utils.py +830 -0
  194. package/dist/templates/cc-native/_cc-native/plan-review.config.json +76 -0
  195. package/dist/templates/cc-native/_cc-native/scripts/__pycache__/aggregate_agents.cpython-313.pyc +0 -0
  196. package/dist/templates/cc-native/_cc-native/scripts/aggregate_agents.py +151 -0
  197. package/dist/templates/cc-native/_cc-native/workflows/fresh-perspective.md +134 -0
  198. package/dist/templates/cc-native/_cc-native/workflows/specdev.md +9 -0
  199. package/dist/types/exit-codes.d.ts +11 -0
  200. package/dist/types/exit-codes.js +10 -0
  201. package/dist/types/index.d.ts +5 -0
  202. package/dist/types/index.js +7 -0
  203. package/oclif.manifest.json +405 -0
  204. 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';