ccmanager 2.8.0 → 2.9.1

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 (82) hide show
  1. package/dist/cli.test.js +13 -2
  2. package/dist/components/App.js +125 -50
  3. package/dist/components/App.test.js +270 -0
  4. package/dist/components/ConfigureShortcuts.js +82 -8
  5. package/dist/components/DeleteWorktree.js +39 -5
  6. package/dist/components/DeleteWorktree.test.d.ts +1 -0
  7. package/dist/components/DeleteWorktree.test.js +128 -0
  8. package/dist/components/LoadingSpinner.d.ts +8 -0
  9. package/dist/components/LoadingSpinner.js +37 -0
  10. package/dist/components/LoadingSpinner.test.d.ts +1 -0
  11. package/dist/components/LoadingSpinner.test.js +187 -0
  12. package/dist/components/Menu.js +64 -16
  13. package/dist/components/Menu.recent-projects.test.js +32 -11
  14. package/dist/components/Menu.test.js +136 -4
  15. package/dist/components/MergeWorktree.js +79 -18
  16. package/dist/components/MergeWorktree.test.d.ts +1 -0
  17. package/dist/components/MergeWorktree.test.js +227 -0
  18. package/dist/components/NewWorktree.js +88 -9
  19. package/dist/components/NewWorktree.test.d.ts +1 -0
  20. package/dist/components/NewWorktree.test.js +244 -0
  21. package/dist/components/ProjectList.js +44 -13
  22. package/dist/components/ProjectList.recent-projects.test.js +8 -3
  23. package/dist/components/ProjectList.test.js +105 -8
  24. package/dist/components/RemoteBranchSelector.test.js +3 -1
  25. package/dist/components/Session.js +11 -6
  26. package/dist/hooks/useGitStatus.d.ts +11 -0
  27. package/dist/hooks/useGitStatus.js +70 -12
  28. package/dist/hooks/useGitStatus.test.js +30 -23
  29. package/dist/services/configurationManager.d.ts +75 -0
  30. package/dist/services/configurationManager.effect.test.d.ts +1 -0
  31. package/dist/services/configurationManager.effect.test.js +407 -0
  32. package/dist/services/configurationManager.js +246 -0
  33. package/dist/services/globalSessionOrchestrator.test.js +0 -8
  34. package/dist/services/projectManager.d.ts +98 -2
  35. package/dist/services/projectManager.js +228 -59
  36. package/dist/services/projectManager.test.js +242 -2
  37. package/dist/services/sessionManager.d.ts +44 -2
  38. package/dist/services/sessionManager.effect.test.d.ts +1 -0
  39. package/dist/services/sessionManager.effect.test.js +321 -0
  40. package/dist/services/sessionManager.js +216 -65
  41. package/dist/services/sessionManager.statePersistence.test.js +18 -9
  42. package/dist/services/sessionManager.test.js +40 -36
  43. package/dist/services/shortcutManager.d.ts +2 -0
  44. package/dist/services/shortcutManager.js +53 -0
  45. package/dist/services/shortcutManager.test.d.ts +1 -0
  46. package/dist/services/shortcutManager.test.js +30 -0
  47. package/dist/services/worktreeService.d.ts +356 -26
  48. package/dist/services/worktreeService.js +793 -353
  49. package/dist/services/worktreeService.test.js +294 -313
  50. package/dist/types/errors.d.ts +74 -0
  51. package/dist/types/errors.js +31 -0
  52. package/dist/types/errors.test.d.ts +1 -0
  53. package/dist/types/errors.test.js +201 -0
  54. package/dist/types/index.d.ts +5 -17
  55. package/dist/utils/claudeDir.d.ts +58 -6
  56. package/dist/utils/claudeDir.js +103 -8
  57. package/dist/utils/claudeDir.test.d.ts +1 -0
  58. package/dist/utils/claudeDir.test.js +108 -0
  59. package/dist/utils/concurrencyLimit.d.ts +5 -0
  60. package/dist/utils/concurrencyLimit.js +11 -0
  61. package/dist/utils/concurrencyLimit.test.js +40 -1
  62. package/dist/utils/gitStatus.d.ts +36 -8
  63. package/dist/utils/gitStatus.js +170 -88
  64. package/dist/utils/gitStatus.test.js +12 -9
  65. package/dist/utils/hookExecutor.d.ts +41 -6
  66. package/dist/utils/hookExecutor.js +75 -32
  67. package/dist/utils/hookExecutor.test.js +73 -20
  68. package/dist/utils/terminalCapabilities.d.ts +18 -0
  69. package/dist/utils/terminalCapabilities.js +81 -0
  70. package/dist/utils/terminalCapabilities.test.d.ts +1 -0
  71. package/dist/utils/terminalCapabilities.test.js +104 -0
  72. package/dist/utils/testHelpers.d.ts +106 -0
  73. package/dist/utils/testHelpers.js +153 -0
  74. package/dist/utils/testHelpers.test.d.ts +1 -0
  75. package/dist/utils/testHelpers.test.js +114 -0
  76. package/dist/utils/worktreeConfig.d.ts +77 -2
  77. package/dist/utils/worktreeConfig.js +156 -16
  78. package/dist/utils/worktreeConfig.test.d.ts +1 -0
  79. package/dist/utils/worktreeConfig.test.js +39 -0
  80. package/package.json +4 -4
  81. package/dist/integration-tests/devcontainer.integration.test.js +0 -101
  82. /package/dist/{integration-tests/devcontainer.integration.test.d.ts → components/App.test.d.ts} +0 -0
@@ -1,17 +1,44 @@
1
+ import { Effect } from 'effect';
1
2
  import { Worktree } from '../types/index.js';
3
+ import { GitError, FileSystemError } from '../types/errors.js';
4
+ /**
5
+ * WorktreeService - Git worktree management with Effect-based error handling
6
+ *
7
+ * All public methods return Effect types for type-safe, composable error handling.
8
+ * See CLAUDE.md for complete examples and patterns.
9
+ *
10
+ * ## Effect-ts Resources
11
+ * - Effect Type: https://effect.website/docs/effect/effect-type
12
+ * - Error Management: https://effect.website/docs/error-management/error-handling
13
+ * - Effect Execution: https://effect.website/docs/guides/running-effects
14
+ * - Tagged Errors: https://effect.website/docs/error-management/expected-errors#tagged-errors
15
+ *
16
+ * ## Key Concepts
17
+ * - All operations return `Effect.Effect<T, E, never>` where T is success type, E is error type
18
+ * - Execute Effects with `Effect.runPromise()` or `Effect.match()` for type-safe handling
19
+ * - Use error discrimination via `error._tag` property for TypeScript type narrowing
20
+ * - Compose Effects with `Effect.flatMap()`, `Effect.all()`, `Effect.catchTag()`, etc.
21
+ *
22
+ * @example
23
+ * ```typescript
24
+ * // See individual method JSDoc for specific usage examples
25
+ * const service = new WorktreeService();
26
+ * ```
27
+ */
2
28
  export declare class WorktreeService {
3
29
  private rootPath;
4
30
  private gitRootPath;
5
31
  constructor(rootPath?: string);
6
32
  private getGitRepositoryRoot;
7
- getWorktrees(): Worktree[];
8
- private getCurrentBranch;
9
33
  isGitRepository(): boolean;
10
34
  getGitRootPath(): string;
11
- getDefaultBranch(): string;
12
- getAllBranches(): string[];
13
35
  /**
14
- * Resolves a branch name to its proper git reference.
36
+ * SYNCHRONOUS HELPER: Resolves a branch name to its proper git reference.
37
+ *
38
+ * This method remains synchronous as it's called within Effect.gen contexts
39
+ * but doesn't need to be wrapped in Effect itself. It's used by createWorktreeEffect()
40
+ * to resolve branch references before creating worktrees.
41
+ *
15
42
  * Handles multiple remotes and throws AmbiguousBranchError when disambiguation is needed.
16
43
  *
17
44
  * Priority order:
@@ -19,31 +46,334 @@ export declare class WorktreeService {
19
46
  * 2. Single remote branch -> return remote/branch
20
47
  * 3. Multiple remote branches -> throw AmbiguousBranchError
21
48
  * 4. No branches found -> return original (let git handle error)
49
+ *
50
+ * @private
51
+ * @param {string} branchName - Branch name to resolve
52
+ * @returns {string} Resolved branch reference
53
+ * @throws {AmbiguousBranchError} When branch exists in multiple remotes
22
54
  */
23
55
  private resolveBranchReference;
24
56
  /**
25
- * Gets all git remotes for this repository.
57
+ * SYNCHRONOUS HELPER: Gets all git remotes for this repository.
58
+ *
59
+ * This method remains synchronous as it's a simple utility used by resolveBranchReference().
60
+ * No need for Effect version since it's a pure read operation with no complex error handling.
61
+ *
62
+ * @private
63
+ * @returns {string[]} Array of remote names, empty array on error
26
64
  */
27
65
  private getAllRemotes;
28
- createWorktree(worktreePath: string, branch: string, baseBranch: string, copySessionData?: boolean, copyClaudeDirectory?: boolean): Promise<{
29
- success: boolean;
30
- error?: string;
31
- }>;
32
- deleteWorktree(worktreePath: string, options?: {
33
- deleteBranch?: boolean;
34
- }): {
35
- success: boolean;
36
- error?: string;
37
- };
38
- mergeWorktree(sourceBranch: string, targetBranch: string, useRebase?: boolean): {
39
- success: boolean;
40
- error?: string;
41
- };
42
- deleteWorktreeByBranch(branch: string): {
43
- success: boolean;
44
- error?: string;
45
- };
66
+ /**
67
+ * SYNCHRONOUS HELPER: Copies Claude Code session data between worktrees.
68
+ *
69
+ * This method remains synchronous and is wrapped in Effect.try when called from
70
+ * createWorktreeEffect() (line ~676). This provides proper error handling while
71
+ * keeping the implementation simple.
72
+ *
73
+ * @private
74
+ * @param {string} sourceWorktreePath - Source worktree path
75
+ * @param {string} targetWorktreePath - Target worktree path
76
+ * @throws {Error} When copy operation fails
77
+ */
46
78
  private copyClaudeSessionData;
47
- hasClaudeDirectoryInBranch(branchName: string): boolean;
48
- private copyClaudeDirectoryFromBaseBranch;
79
+ /**
80
+ * Effect-based hasClaudeDirectoryInBranch operation
81
+ * Checks if a .claude directory exists in the worktree for the specified branch
82
+ *
83
+ * @param {string} branchName - Name of the branch to check
84
+ * @returns {Effect.Effect<boolean, GitError, never>} Effect containing true if .claude directory exists, false otherwise
85
+ *
86
+ * @example
87
+ * ```typescript
88
+ * // Check if branch has .claude directory
89
+ * const hasClaudeDir = await Effect.runPromise(
90
+ * effect
91
+ * );
92
+ *
93
+ * // Or use Effect.match for error handling
94
+ * const result = await Effect.runPromise(
95
+ * Effect.match(effect, {
96
+ * onFailure: (error: GitError) => ({
97
+ * type: 'error' as const,
98
+ * message: error.stderr
99
+ * }),
100
+ * onSuccess: (hasDir: boolean) => ({
101
+ * type: 'success' as const,
102
+ * data: hasDir
103
+ * })
104
+ * })
105
+ * );
106
+ * ```
107
+ *
108
+ * @throws {GitError} When git operations fail
109
+ */
110
+ hasClaudeDirectoryInBranchEffect(branchName: string): Effect.Effect<boolean, GitError, never>;
111
+ /**
112
+ * Effect-based copyClaudeDirectoryFromBaseBranch operation
113
+ * Copies .claude directory from base branch worktree to target worktree
114
+ *
115
+ * @param {string} worktreePath - Path of the target worktree
116
+ * @param {string} baseBranch - Name of the base branch to copy from
117
+ * @returns {Effect.Effect<void, GitError | FileSystemError, never>} Effect that completes successfully or fails with error
118
+ *
119
+ * @example
120
+ * ```typescript
121
+ * // Copy .claude directory from main branch
122
+ * await Effect.runPromise(
123
+ * effect
124
+ * );
125
+ *
126
+ * // With error handling
127
+ * const result = await Effect.runPromise(
128
+ * Effect.catchAll(
129
+ * effect,
130
+ * (error) => {
131
+ * console.warn('Could not copy .claude directory:', error);
132
+ * return Effect.succeed(undefined); // Continue despite error
133
+ * }
134
+ * )
135
+ * );
136
+ * ```
137
+ *
138
+ * @throws {GitError} When base worktree cannot be found
139
+ * @throws {FileSystemError} When copying the directory fails
140
+ */
141
+ private copyClaudeDirectoryFromBaseBranchEffect;
142
+ /**
143
+ * Effect-based getDefaultBranch operation
144
+ * Returns Effect that may fail with GitError
145
+ *
146
+ * @returns {Effect.Effect<string, GitError, never>} Effect containing default branch name or GitError
147
+ *
148
+ * @example
149
+ * ```typescript
150
+ * // Use Effect.match for type-safe error handling
151
+ * const result = await Effect.runPromise(
152
+ * Effect.match(effect, {
153
+ * onFailure: (error: GitError) => ({
154
+ * type: 'error' as const,
155
+ * message: `Failed to get default branch: ${error.stderr}`
156
+ * }),
157
+ * onSuccess: (branch: string) => ({
158
+ * type: 'success' as const,
159
+ * data: branch
160
+ * })
161
+ * })
162
+ * );
163
+ *
164
+ * if (result.type === 'success') {
165
+ * console.log(`Default branch is: ${result.data}`);
166
+ * }
167
+ * ```
168
+ *
169
+ * @throws {GitError} When git symbolic-ref command fails and fallback detection also fails
170
+ */
171
+ getDefaultBranchEffect(): Effect.Effect<string, GitError, never>;
172
+ /**
173
+ * Effect-based getAllBranches operation
174
+ * Returns Effect that succeeds with array of branches (empty on failure for non-critical operation)
175
+ *
176
+ * @returns {Effect.Effect<string[], GitError, never>} Effect containing array of branch names
177
+ *
178
+ * @example
179
+ * ```typescript
180
+ * // Execute in async context - this operation returns empty array on failure
181
+ * const branches = await Effect.runPromise(
182
+ * effect
183
+ * );
184
+ * console.log(`Found ${branches.length} branches`);
185
+ *
186
+ * // Or use Effect.match for explicit error handling
187
+ * const result = await Effect.runPromise(
188
+ * Effect.match(effect, {
189
+ * onFailure: (error: GitError) => ({
190
+ * type: 'error' as const,
191
+ * message: error.stderr
192
+ * }),
193
+ * onSuccess: (branches: string[]) => ({
194
+ * type: 'success' as const,
195
+ * data: branches
196
+ * })
197
+ * })
198
+ * );
199
+ * ```
200
+ *
201
+ * @throws {GitError} When git branch command fails (but falls back to empty array)
202
+ */
203
+ getAllBranchesEffect(): Effect.Effect<string[], GitError, never>;
204
+ /**
205
+ * Effect-based getCurrentBranch operation
206
+ * Returns Effect that may fail with GitError
207
+ *
208
+ * @returns {Effect.Effect<string, GitError, never>} Effect containing current branch name or GitError
209
+ *
210
+ * @example
211
+ * ```typescript
212
+ * // Use Effect.match for type-safe error handling
213
+ * const result = await Effect.runPromise(
214
+ * Effect.match(effect, {
215
+ * onFailure: (error: GitError) => ({
216
+ * type: 'error' as const,
217
+ * message: `Failed to get current branch: ${error.stderr}`
218
+ * }),
219
+ * onSuccess: (branch: string) => ({
220
+ * type: 'success' as const,
221
+ * data: branch
222
+ * })
223
+ * })
224
+ * );
225
+ *
226
+ * if (result.type === 'success') {
227
+ * console.log(`Current branch: ${result.data}`);
228
+ * }
229
+ * ```
230
+ *
231
+ * @throws {GitError} When git rev-parse command fails
232
+ */
233
+ getCurrentBranchEffect(): Effect.Effect<string, GitError, never>;
234
+ /**
235
+ * Effect-based getWorktrees operation
236
+ * Returns Effect that may fail with GitError
237
+ *
238
+ * @returns {Effect.Effect<Worktree[], GitError, never>} Effect containing array of worktrees or GitError
239
+ *
240
+ * @example
241
+ * ```typescript
242
+ * // Execute in async context
243
+ * const worktrees = await Effect.runPromise(
244
+ * effect
245
+ * );
246
+ *
247
+ * // Or use Effect.match for type-safe error handling
248
+ * const result = await Effect.runPromise(
249
+ * Effect.match(effect, {
250
+ * onFailure: (error: GitError) => ({
251
+ * type: 'error' as const,
252
+ * message: `Git error: ${error.stderr}`
253
+ * }),
254
+ * onSuccess: (worktrees: Worktree[]) => ({
255
+ * type: 'success' as const,
256
+ * data: worktrees
257
+ * })
258
+ * })
259
+ * );
260
+ *
261
+ * if (result.type === 'error') {
262
+ * console.error(result.message);
263
+ * } else {
264
+ * console.log(`Found ${result.data.length} worktrees`);
265
+ * }
266
+ * ```
267
+ *
268
+ * @throws {GitError} When git worktree list command fails
269
+ */
270
+ getWorktreesEffect(): Effect.Effect<Worktree[], GitError, never>;
271
+ /**
272
+ * Effect-based createWorktree operation
273
+ * May fail with GitError or FileSystemError
274
+ *
275
+ * @param {string} worktreePath - Path where the new worktree will be created
276
+ * @param {string} branch - Name of the branch for the new worktree
277
+ * @param {string} baseBranch - Base branch to create the new branch from
278
+ * @param {boolean} copySessionData - Whether to copy Claude session data (default: false)
279
+ * @param {boolean} copyClaudeDirectory - Whether to copy .claude directory (default: false)
280
+ * @returns {Effect.Effect<Worktree, GitError | FileSystemError, never>} Effect containing created worktree or error
281
+ *
282
+ * @example
283
+ * ```typescript
284
+ * // Create new worktree with Effect.match for error handling
285
+ * const result = await Effect.runPromise(
286
+ * Effect.match(
287
+ * effect,
288
+ * {
289
+ * onFailure: (error: GitError | FileSystemError) => {
290
+ * switch (error._tag) {
291
+ * case 'GitError':
292
+ * return {type: 'error' as const, msg: `Git failed: ${error.stderr}`};
293
+ * case 'FileSystemError':
294
+ * return {type: 'error' as const, msg: `FS failed: ${error.cause}`};
295
+ * }
296
+ * },
297
+ * onSuccess: (worktree: Worktree) => ({
298
+ * type: 'success' as const,
299
+ * data: worktree
300
+ * })
301
+ * }
302
+ * )
303
+ * );
304
+ *
305
+ * if (result.type === 'error') {
306
+ * console.error(result.msg);
307
+ * } else {
308
+ * console.log(`Created worktree at ${result.data.path}`);
309
+ * }
310
+ * ```
311
+ *
312
+ * @throws {GitError} When git worktree add command fails
313
+ * @throws {FileSystemError} When session data copy fails
314
+ */
315
+ createWorktreeEffect(worktreePath: string, branch: string, baseBranch: string, copySessionData?: boolean, copyClaudeDirectory?: boolean): Effect.Effect<Worktree, GitError | FileSystemError, never>;
316
+ /**
317
+ * Effect-based deleteWorktree operation
318
+ * May fail with GitError
319
+ *
320
+ * @param {string} worktreePath - Path of the worktree to delete
321
+ * @param {{deleteBranch?: boolean}} options - Options for deletion (default: deleteBranch = true)
322
+ * @returns {Effect.Effect<void, GitError, never>} Effect that completes successfully or fails with GitError
323
+ *
324
+ * @example
325
+ * ```typescript
326
+ * // Delete worktree with Effect.catchTag for specific error handling
327
+ * await Effect.runPromise(
328
+ * Effect.catchTag(
329
+ * effect,
330
+ * 'GitError',
331
+ * (error) => {
332
+ * console.error(`Failed to delete worktree: ${error.stderr}`);
333
+ * return Effect.succeed(undefined); // Continue despite error
334
+ * }
335
+ * )
336
+ * );
337
+ * ```
338
+ *
339
+ * @throws {GitError} When git worktree remove command fails or worktree not found
340
+ */
341
+ deleteWorktreeEffect(worktreePath: string, options?: {
342
+ deleteBranch?: boolean;
343
+ }): Effect.Effect<void, GitError, never>;
344
+ /**
345
+ * Effect-based mergeWorktree operation
346
+ * May fail with GitError
347
+ *
348
+ * @param {string} sourceBranch - Branch to merge from
349
+ * @param {string} targetBranch - Branch to merge into
350
+ * @param {boolean} useRebase - Whether to use rebase instead of merge (default: false)
351
+ * @returns {Effect.Effect<void, GitError, never>} Effect that completes successfully or fails with GitError
352
+ *
353
+ * @example
354
+ * ```typescript
355
+ * // Merge with Effect.all for parallel operations
356
+ * await Effect.runPromise(
357
+ * Effect.all([
358
+ * effect1,
359
+ * effect2
360
+ * ], {concurrency: 1}) // Sequential to avoid conflicts
361
+ * );
362
+ *
363
+ * // Or use Effect.catchAll for fallback behavior
364
+ * const result = await Effect.runPromise(
365
+ * Effect.catchAll(
366
+ * effect,
367
+ * (error: GitError) => {
368
+ * console.error(`Merge failed: ${error.stderr}`);
369
+ * // Return alternative Effect or rethrow
370
+ * return Effect.fail(error);
371
+ * }
372
+ * )
373
+ * );
374
+ * ```
375
+ *
376
+ * @throws {GitError} When git merge/rebase command fails or worktrees not found
377
+ */
378
+ mergeWorktreeEffect(sourceBranch: string, targetBranch: string, useRebase?: boolean): Effect.Effect<void, GitError, never>;
49
379
  }