ccmanager 0.1.9 → 0.1.10

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 CHANGED
@@ -54,18 +54,8 @@ $ npx ccmanager
54
54
 
55
55
  ### CCMANAGER_CLAUDE_ARGS
56
56
 
57
- You can pass additional arguments to Claude Code sessions by setting the `CCMANAGER_CLAUDE_ARGS` environment variable:
57
+ ⚠️ **Deprecated in v0.1.9**: `CCMANAGER_CLAUDE_ARGS` is no longer supported. Please use the [Command Configuration](#command-configuration) feature instead.
58
58
 
59
- ```bash
60
- # Start Claude Code with specific arguments for all sessions
61
- export CCMANAGER_CLAUDE_ARGS="--resume"
62
- npx ccmanager
63
-
64
- # Or set it inline
65
- CCMANAGER_CLAUDE_ARGS="--resume" npx ccmanager
66
- ```
67
-
68
- The arguments are applied to all Claude Code sessions started by CCManager.
69
59
 
70
60
  ## Keyboard Shortcuts
71
61
 
@@ -75,8 +75,9 @@ const DeleteWorktree = ({ onComplete, onCancel, }) => {
75
75
  React.createElement(Text, null, "You are about to delete the following worktrees:"),
76
76
  selectedWorktrees.map(wt => (React.createElement(Text, { key: wt.path, color: "red" },
77
77
  "\u2022 ",
78
- wt.branch.replace('refs/heads/', ''),
79
- " (",
78
+ wt.branch ? wt.branch.replace('refs/heads/', '') : 'detached',
79
+ ' ',
80
+ "(",
80
81
  wt.path,
81
82
  ")")))),
82
83
  React.createElement(Text, { bold: true }, "This will also delete their branches. Are you sure?")));
@@ -90,7 +91,9 @@ const DeleteWorktree = ({ onComplete, onCancel, }) => {
90
91
  worktrees.map((worktree, index) => {
91
92
  const isSelected = selectedIndices.has(index);
92
93
  const isFocused = index === focusedIndex;
93
- const branchName = worktree.branch.replace('refs/heads/', '');
94
+ const branchName = worktree.branch
95
+ ? worktree.branch.replace('refs/heads/', '')
96
+ : 'detached';
94
97
  return (React.createElement(Box, { key: worktree.path },
95
98
  React.createElement(Text, { color: isFocused ? 'green' : undefined, inverse: isFocused, dimColor: !isFocused && !isSelected },
96
99
  isSelected ? '[✓]' : '[ ]',
@@ -41,7 +41,9 @@ const Menu = ({ sessionManager, onSelectWorktree }) => {
41
41
  if (session) {
42
42
  status = ` [${getStatusDisplay(session.state)}]`;
43
43
  }
44
- const branchName = wt.branch.replace('refs/heads/', '');
44
+ const branchName = wt.branch
45
+ ? wt.branch.replace('refs/heads/', '')
46
+ : 'detached';
45
47
  const isMain = wt.isMainWorktree ? ' (main)' : '';
46
48
  return {
47
49
  label: `${branchName}${isMain}${status}`,
@@ -16,9 +16,9 @@ const MergeWorktree = ({ onComplete, onCancel, }) => {
16
16
  const loadedWorktrees = worktreeService.getWorktrees();
17
17
  // Create branch items for selection
18
18
  const items = loadedWorktrees.map(wt => ({
19
- label: wt.branch.replace('refs/heads/', '') +
19
+ label: (wt.branch ? wt.branch.replace('refs/heads/', '') : 'detached') +
20
20
  (wt.isMainWorktree ? ' (main)' : ''),
21
- value: wt.branch.replace('refs/heads/', ''),
21
+ value: wt.branch ? wt.branch.replace('refs/heads/', '') : 'detached',
22
22
  }));
23
23
  setBranchItems(items);
24
24
  }, []);
@@ -42,32 +42,41 @@ export class WorktreeService {
42
42
  });
43
43
  const worktrees = [];
44
44
  const lines = output.trim().split('\n');
45
- let currentWorktree = {};
46
- for (const line of lines) {
47
- if (line.startsWith('worktree ')) {
48
- if (currentWorktree.path) {
49
- worktrees.push(currentWorktree);
50
- }
51
- currentWorktree = {
52
- path: line.substring(9),
53
- isMainWorktree: false,
54
- hasSession: false,
55
- };
45
+ const parseWorktree = (lines, startIndex) => {
46
+ const worktreeLine = lines[startIndex];
47
+ if (!worktreeLine?.startsWith('worktree ')) {
48
+ return [null, startIndex];
56
49
  }
57
- else if (line.startsWith('branch ')) {
58
- let branch = line.substring(7);
59
- // Remove refs/heads/ prefix if present
60
- if (branch.startsWith('refs/heads/')) {
61
- branch = branch.substring(11);
50
+ const worktree = {
51
+ path: worktreeLine.substring(9),
52
+ isMainWorktree: false,
53
+ hasSession: false,
54
+ };
55
+ let i = startIndex + 1;
56
+ while (i < lines.length &&
57
+ lines[i] &&
58
+ !lines[i].startsWith('worktree ')) {
59
+ const line = lines[i];
60
+ if (line && line.startsWith('branch ')) {
61
+ const branch = line.substring(7);
62
+ worktree.branch = branch.startsWith('refs/heads/')
63
+ ? branch.substring(11)
64
+ : branch;
62
65
  }
63
- currentWorktree.branch = branch;
66
+ else if (line === 'bare') {
67
+ worktree.isMainWorktree = true;
68
+ }
69
+ i++;
64
70
  }
65
- else if (line === 'bare') {
66
- currentWorktree.isMainWorktree = true;
71
+ return [worktree, i];
72
+ };
73
+ let index = 0;
74
+ while (index < lines.length) {
75
+ const [worktree, nextIndex] = parseWorktree(lines, index);
76
+ if (worktree) {
77
+ worktrees.push(worktree);
67
78
  }
68
- }
69
- if (currentWorktree.path) {
70
- worktrees.push(currentWorktree);
79
+ index = nextIndex > index ? nextIndex : index + 1;
71
80
  }
72
81
  // Mark the first worktree as main if none are marked
73
82
  if (worktrees.length > 0 && !worktrees.some(w => w.isMainWorktree)) {
@@ -224,7 +233,9 @@ export class WorktreeService {
224
233
  encoding: 'utf8',
225
234
  });
226
235
  // Delete the branch if it exists
227
- const branchName = worktree.branch.replace('refs/heads/', '');
236
+ const branchName = worktree.branch
237
+ ? worktree.branch.replace('refs/heads/', '')
238
+ : 'detached';
228
239
  try {
229
240
  execSync(`git branch -D "${branchName}"`, {
230
241
  cwd: this.rootPath,
@@ -248,7 +259,7 @@ export class WorktreeService {
248
259
  try {
249
260
  // Get worktrees to find the target worktree path
250
261
  const worktrees = this.getWorktrees();
251
- const targetWorktree = worktrees.find(wt => wt.branch.replace('refs/heads/', '') === targetBranch);
262
+ const targetWorktree = worktrees.find(wt => wt.branch && wt.branch.replace('refs/heads/', '') === targetBranch);
252
263
  if (!targetWorktree) {
253
264
  return {
254
265
  success: false,
@@ -258,7 +269,7 @@ export class WorktreeService {
258
269
  // Perform the merge or rebase in the target worktree
259
270
  if (useRebase) {
260
271
  // For rebase, we need to checkout source branch and rebase it onto target
261
- const sourceWorktree = worktrees.find(wt => wt.branch.replace('refs/heads/', '') === sourceBranch);
272
+ const sourceWorktree = worktrees.find(wt => wt.branch && wt.branch.replace('refs/heads/', '') === sourceBranch);
262
273
  if (!sourceWorktree) {
263
274
  return {
264
275
  success: false,
@@ -300,7 +311,7 @@ export class WorktreeService {
300
311
  try {
301
312
  // Get worktrees to find the worktree by branch
302
313
  const worktrees = this.getWorktrees();
303
- const worktree = worktrees.find(wt => wt.branch.replace('refs/heads/', '') === branch);
314
+ const worktree = worktrees.find(wt => wt.branch && wt.branch.replace('refs/heads/', '') === branch);
304
315
  if (!worktree) {
305
316
  return {
306
317
  success: false,
@@ -4,7 +4,7 @@ export type Terminal = InstanceType<typeof pkg.Terminal>;
4
4
  export type SessionState = 'idle' | 'busy' | 'waiting_input';
5
5
  export interface Worktree {
6
6
  path: string;
7
- branch: string;
7
+ branch?: string;
8
8
  isMainWorktree: boolean;
9
9
  hasSession: boolean;
10
10
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ccmanager",
3
- "version": "0.1.9",
3
+ "version": "0.1.10",
4
4
  "description": "TUI application for managing multiple Claude Code sessions across Git worktrees",
5
5
  "license": "MIT",
6
6
  "author": "Kodai Kabasawa",