ccmanager 3.11.1 → 3.11.2
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/dist/components/App.js +7 -5
- package/dist/components/Dashboard.js +7 -1
- package/dist/components/DeleteWorktree.js +13 -2
- package/dist/components/Menu.js +2 -1
- package/dist/services/worktreeNameGenerator.d.ts +1 -0
- package/dist/services/worktreeNameGenerator.js +10 -0
- package/dist/services/worktreeNameGenerator.test.js +13 -1
- package/dist/services/worktreeService.js +26 -0
- package/dist/services/worktreeService.test.js +30 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/utils/worktreeUtils.d.ts +7 -0
- package/dist/utils/worktreeUtils.js +42 -0
- package/dist/utils/worktreeUtils.test.js +4 -0
- package/package.json +6 -6
package/dist/components/App.js
CHANGED
|
@@ -14,7 +14,8 @@ import RemoteBranchSelector from './RemoteBranchSelector.js';
|
|
|
14
14
|
import LoadingSpinner from './LoadingSpinner.js';
|
|
15
15
|
import { globalSessionOrchestrator } from '../services/globalSessionOrchestrator.js';
|
|
16
16
|
import { WorktreeService } from '../services/worktreeService.js';
|
|
17
|
-
import { worktreeNameGenerator } from '../services/worktreeNameGenerator.js';
|
|
17
|
+
import { worktreeNameGenerator, generateFallbackBranchName, } from '../services/worktreeNameGenerator.js';
|
|
18
|
+
import { logger } from '../utils/logger.js';
|
|
18
19
|
import { AmbiguousBranchError, } from '../types/index.js';
|
|
19
20
|
import { configReader } from '../services/config/configReader.js';
|
|
20
21
|
import { ENV_VARS } from '../constants/env.js';
|
|
@@ -327,11 +328,12 @@ const App = ({ devcontainerConfig, multiProject, version, }) => {
|
|
|
327
328
|
const existingBranches = allBranches._tag === 'Right' ? allBranches.right : [];
|
|
328
329
|
const generatedBranch = await Effect.runPromise(Effect.either(worktreeNameGenerator.generateBranchNameEffect(request.initialPrompt, request.baseBranch, existingBranches)));
|
|
329
330
|
if (generatedBranch._tag === 'Left') {
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
331
|
+
logger.warn(`Branch name generation failed, using fallback: ${formatErrorMessage(generatedBranch.left)}`);
|
|
332
|
+
branch = generateFallbackBranchName(existingBranches);
|
|
333
|
+
}
|
|
334
|
+
else {
|
|
335
|
+
branch = generatedBranch.right;
|
|
333
336
|
}
|
|
334
|
-
branch = generatedBranch.right;
|
|
335
337
|
if (request.autoDirectoryPattern) {
|
|
336
338
|
targetPath = generateWorktreeDirectory(request.projectPath, branch, request.autoDirectoryPattern);
|
|
337
339
|
}
|
|
@@ -11,7 +11,7 @@ import { WorktreeService } from '../services/worktreeService.js';
|
|
|
11
11
|
import { STATUS_ICONS, STATUS_LABELS, MENU_ICONS, getStatusDisplay, } from '../constants/statusIcons.js';
|
|
12
12
|
import { useSearchMode } from '../hooks/useSearchMode.js';
|
|
13
13
|
import { useGitStatus } from '../hooks/useGitStatus.js';
|
|
14
|
-
import { truncateString, calculateColumnPositions, assembleWorktreeLabel, } from '../utils/worktreeUtils.js';
|
|
14
|
+
import { truncateString, calculateColumnPositions, assembleWorktreeLabel, formatRelativeDate, } from '../utils/worktreeUtils.js';
|
|
15
15
|
import { formatGitFileChanges, formatGitAheadBehind, formatParentBranch, } from '../utils/gitStatus.js';
|
|
16
16
|
import TextInputWrapper from './TextInputWrapper.js';
|
|
17
17
|
const MAX_BRANCH_NAME_LENGTH = 70;
|
|
@@ -241,12 +241,18 @@ const Dashboard = ({ projectsDir, onSelectSession, onSelectProject, error, onDis
|
|
|
241
241
|
fileChanges,
|
|
242
242
|
aheadBehind,
|
|
243
243
|
parentBranch,
|
|
244
|
+
lastCommitDate: wt.lastCommitDate
|
|
245
|
+
? `\x1b[90m${formatRelativeDate(wt.lastCommitDate)}\x1b[0m`
|
|
246
|
+
: '',
|
|
244
247
|
error: itemError,
|
|
245
248
|
lengths: {
|
|
246
249
|
base: stripAnsi(baseLabel).length,
|
|
247
250
|
fileChanges: stripAnsi(fileChanges).length,
|
|
248
251
|
aheadBehind: stripAnsi(aheadBehind).length,
|
|
249
252
|
parentBranch: stripAnsi(parentBranch).length,
|
|
253
|
+
lastCommitDate: wt.lastCommitDate
|
|
254
|
+
? formatRelativeDate(wt.lastCommitDate).length
|
|
255
|
+
: 0,
|
|
250
256
|
},
|
|
251
257
|
};
|
|
252
258
|
});
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useState, useEffect } from 'react';
|
|
3
|
+
import path from 'path';
|
|
3
4
|
import { Box, Text, useInput } from 'ink';
|
|
4
5
|
import SelectInput from 'ink-select-input';
|
|
5
6
|
import { Effect } from 'effect';
|
|
@@ -20,8 +21,18 @@ const DeleteWorktree = ({ projectPath, onComplete, onCancel, }) => {
|
|
|
20
21
|
try {
|
|
21
22
|
const allWorktrees = await Effect.runPromise(worktreeService.getWorktreesEffect());
|
|
22
23
|
if (!cancelled) {
|
|
23
|
-
// Filter out main worktree
|
|
24
|
-
const
|
|
24
|
+
// Filter out main worktree and current working directory worktree
|
|
25
|
+
const resolvedCwd = path.resolve(process.cwd());
|
|
26
|
+
const deletableWorktrees = allWorktrees.filter(wt => {
|
|
27
|
+
if (wt.isMainWorktree)
|
|
28
|
+
return false;
|
|
29
|
+
const resolvedPath = path.resolve(wt.path);
|
|
30
|
+
if (resolvedCwd === resolvedPath ||
|
|
31
|
+
resolvedCwd.startsWith(resolvedPath + path.sep)) {
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
return true;
|
|
35
|
+
});
|
|
25
36
|
setWorktrees(deletableWorktrees);
|
|
26
37
|
setIsLoading(false);
|
|
27
38
|
}
|
package/dist/components/Menu.js
CHANGED
|
@@ -144,7 +144,8 @@ const Menu = ({ sessionManager, worktreeService, onSelectWorktree, onSelectRecen
|
|
|
144
144
|
sessionManager.isAutoApprovalDisabledForWorktree(item.worktree.path);
|
|
145
145
|
const label = baseLabel + (aaDisabled ? ' [Auto Approval Off]' : '');
|
|
146
146
|
// Only show numbers for worktrees (0-9) when not in search mode
|
|
147
|
-
|
|
147
|
+
// Use fixed-width prefix to prevent flicker at scroll boundary
|
|
148
|
+
const numberPrefix = !isSearchMode && index < 10 ? `${index} ❯ ` : ' ❯ ';
|
|
148
149
|
return {
|
|
149
150
|
type: 'worktree',
|
|
150
151
|
label: numberPrefix + label,
|
|
@@ -2,6 +2,7 @@ import { Effect } from 'effect';
|
|
|
2
2
|
import { ProcessError } from '../types/errors.js';
|
|
3
3
|
export declare const extractBranchNameFromOutput: (stdout: string) => string;
|
|
4
4
|
export declare const deduplicateBranchName: (name: string, existingBranches: string[]) => string;
|
|
5
|
+
export declare const generateFallbackBranchName: (existingBranches?: string[]) => string;
|
|
5
6
|
export declare class WorktreeNameGenerator {
|
|
6
7
|
generateBranchNameEffect(userPrompt: string, baseBranch: string, existingBranches?: string[]): Effect.Effect<string, ProcessError, never>;
|
|
7
8
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { randomBytes } from 'crypto';
|
|
1
2
|
import { Effect } from 'effect';
|
|
2
3
|
import { execFile } from 'child_process';
|
|
3
4
|
import { ProcessError } from '../types/errors.js';
|
|
@@ -121,6 +122,15 @@ export const deduplicateBranchName = (name, existingBranches) => {
|
|
|
121
122
|
}
|
|
122
123
|
}
|
|
123
124
|
};
|
|
125
|
+
export const generateFallbackBranchName = (existingBranches) => {
|
|
126
|
+
const date = new Date();
|
|
127
|
+
const dateStr = `${date.getFullYear()}${String(date.getMonth() + 1).padStart(2, '0')}${String(date.getDate()).padStart(2, '0')}`;
|
|
128
|
+
const randomSuffix = randomBytes(3).toString('hex');
|
|
129
|
+
const name = `${dateStr}-${randomSuffix}`;
|
|
130
|
+
return existingBranches
|
|
131
|
+
? deduplicateBranchName(name, existingBranches)
|
|
132
|
+
: name;
|
|
133
|
+
};
|
|
124
134
|
export class WorktreeNameGenerator {
|
|
125
135
|
generateBranchNameEffect(userPrompt, baseBranch, existingBranches) {
|
|
126
136
|
return Effect.tryPromise({
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { describe, expect, it } from 'vitest';
|
|
2
|
-
import { deduplicateBranchName, extractBranchNameFromOutput, worktreeNameGenerator as generator, } from './worktreeNameGenerator.js';
|
|
2
|
+
import { deduplicateBranchName, extractBranchNameFromOutput, generateFallbackBranchName, worktreeNameGenerator as generator, } from './worktreeNameGenerator.js';
|
|
3
3
|
describe('WorktreeNameGenerator output parsing', () => {
|
|
4
4
|
it('normalizes direct json branchName responses', async () => {
|
|
5
5
|
const value = extractBranchNameFromOutput('{"branchName":"feature/add prompt"}');
|
|
@@ -33,3 +33,15 @@ describe('deduplicateBranchName', () => {
|
|
|
33
33
|
expect(deduplicateBranchName('Feature/New', ['feature/new'])).toBe('Feature/New-2');
|
|
34
34
|
});
|
|
35
35
|
});
|
|
36
|
+
describe('generateFallbackBranchName', () => {
|
|
37
|
+
it('returns a name matching YYYYMMDD-hex pattern', () => {
|
|
38
|
+
const name = generateFallbackBranchName();
|
|
39
|
+
expect(name).toMatch(/^\d{8}-[0-9a-f]{6}$/);
|
|
40
|
+
});
|
|
41
|
+
it('deduplicates against existing branches', () => {
|
|
42
|
+
const first = generateFallbackBranchName();
|
|
43
|
+
const name = generateFallbackBranchName([first]);
|
|
44
|
+
// Either it's different (random collision unlikely) or it has a -2 suffix
|
|
45
|
+
expect(name).not.toBe(first);
|
|
46
|
+
});
|
|
47
|
+
});
|
|
@@ -662,6 +662,21 @@ export class WorktreeService {
|
|
|
662
662
|
if (mainWorktree && mainWorktree.path.includes('.git/modules')) {
|
|
663
663
|
mainWorktree.path = self.gitRootPath;
|
|
664
664
|
}
|
|
665
|
+
// Fetch last commit date for each worktree
|
|
666
|
+
for (const wt of worktrees) {
|
|
667
|
+
try {
|
|
668
|
+
const dateStr = execSync('git log -1 --format=%aI', {
|
|
669
|
+
cwd: wt.path,
|
|
670
|
+
encoding: 'utf8',
|
|
671
|
+
}).trim();
|
|
672
|
+
if (dateStr) {
|
|
673
|
+
wt.lastCommitDate = new Date(dateStr);
|
|
674
|
+
}
|
|
675
|
+
}
|
|
676
|
+
catch {
|
|
677
|
+
// Ignore errors (e.g., empty repo)
|
|
678
|
+
}
|
|
679
|
+
}
|
|
665
680
|
// Sort worktrees by last session if requested
|
|
666
681
|
if (sortByLastSession) {
|
|
667
682
|
worktrees.sort((a, b) => {
|
|
@@ -900,6 +915,17 @@ export class WorktreeService {
|
|
|
900
915
|
stderr: 'Cannot delete the main worktree',
|
|
901
916
|
}));
|
|
902
917
|
}
|
|
918
|
+
// Prevent deleting the worktree that contains the current working directory
|
|
919
|
+
const resolvedWorktreePath = path.resolve(worktreePath);
|
|
920
|
+
const resolvedCwd = path.resolve(process.cwd());
|
|
921
|
+
if (resolvedCwd === resolvedWorktreePath ||
|
|
922
|
+
resolvedCwd.startsWith(resolvedWorktreePath + path.sep)) {
|
|
923
|
+
return yield* Effect.fail(new GitError({
|
|
924
|
+
command: 'git worktree remove',
|
|
925
|
+
exitCode: 1,
|
|
926
|
+
stderr: `Cannot delete the worktree at "${worktreePath}" because it is the current working directory`,
|
|
927
|
+
}));
|
|
928
|
+
}
|
|
903
929
|
// Remove the worktree
|
|
904
930
|
yield* Effect.try({
|
|
905
931
|
try: () => {
|
|
@@ -757,6 +757,36 @@ branch refs/heads/main
|
|
|
757
757
|
expect.fail('Should have returned Left with GitError');
|
|
758
758
|
}
|
|
759
759
|
});
|
|
760
|
+
it('should return Effect that fails with GitError when trying to delete worktree matching cwd', async () => {
|
|
761
|
+
const cwdPath = process.cwd();
|
|
762
|
+
mockedExecSync.mockImplementation((cmd, _options) => {
|
|
763
|
+
if (typeof cmd === 'string') {
|
|
764
|
+
if (cmd === 'git rev-parse --git-common-dir') {
|
|
765
|
+
return '/fake/path/.git\n';
|
|
766
|
+
}
|
|
767
|
+
if (cmd === 'git worktree list --porcelain') {
|
|
768
|
+
return `worktree /fake/path
|
|
769
|
+
HEAD abcd1234
|
|
770
|
+
branch refs/heads/main
|
|
771
|
+
|
|
772
|
+
worktree ${cwdPath}
|
|
773
|
+
HEAD efgh5678
|
|
774
|
+
branch refs/heads/feature
|
|
775
|
+
`;
|
|
776
|
+
}
|
|
777
|
+
}
|
|
778
|
+
throw new Error('Command not mocked: ' + cmd);
|
|
779
|
+
});
|
|
780
|
+
const effect = service.deleteWorktreeEffect(cwdPath);
|
|
781
|
+
const result = await Effect.runPromise(Effect.either(effect));
|
|
782
|
+
if (result._tag === 'Left') {
|
|
783
|
+
expect(result.left).toBeInstanceOf(GitError);
|
|
784
|
+
expect(result.left.stderr).toContain('because it is the current working directory');
|
|
785
|
+
}
|
|
786
|
+
else {
|
|
787
|
+
expect.fail('Should have returned Left with GitError');
|
|
788
|
+
}
|
|
789
|
+
});
|
|
760
790
|
});
|
|
761
791
|
describe('Effect-based mergeWorktree', () => {
|
|
762
792
|
it('should return Effect with void on successful merge', async () => {
|
package/dist/types/index.d.ts
CHANGED
|
@@ -9,14 +9,20 @@ export interface WorktreeItem {
|
|
|
9
9
|
fileChanges: string;
|
|
10
10
|
aheadBehind: string;
|
|
11
11
|
parentBranch: string;
|
|
12
|
+
lastCommitDate: string;
|
|
12
13
|
error?: string;
|
|
13
14
|
lengths: {
|
|
14
15
|
base: number;
|
|
15
16
|
fileChanges: number;
|
|
16
17
|
aheadBehind: number;
|
|
17
18
|
parentBranch: number;
|
|
19
|
+
lastCommitDate: number;
|
|
18
20
|
};
|
|
19
21
|
}
|
|
22
|
+
/**
|
|
23
|
+
* Format a date as a relative time string (e.g., "2h ago", "3d ago").
|
|
24
|
+
*/
|
|
25
|
+
export declare function formatRelativeDate(date: Date): string;
|
|
20
26
|
export declare function truncateString(str: string, maxLength: number): string;
|
|
21
27
|
export declare function generateWorktreeDirectory(projectPath: string, branchName: string, pattern?: string): string;
|
|
22
28
|
export declare function extractBranchParts(branchName: string): {
|
|
@@ -34,6 +40,7 @@ export declare function calculateColumnPositions(items: WorktreeItem[]): {
|
|
|
34
40
|
fileChanges: number;
|
|
35
41
|
aheadBehind: number;
|
|
36
42
|
parentBranch: number;
|
|
43
|
+
lastCommitDate: number;
|
|
37
44
|
};
|
|
38
45
|
/**
|
|
39
46
|
* Assembles the final worktree label with proper column alignment
|
|
@@ -6,6 +6,33 @@ import { formatGitFileChanges, formatGitAheadBehind, formatParentBranch, } from
|
|
|
6
6
|
// Constants
|
|
7
7
|
const MAX_BRANCH_NAME_LENGTH = 70; // Maximum characters for branch name display
|
|
8
8
|
const MIN_COLUMN_PADDING = 2; // Minimum spaces between columns
|
|
9
|
+
/**
|
|
10
|
+
* Format a date as a relative time string (e.g., "2h ago", "3d ago").
|
|
11
|
+
*/
|
|
12
|
+
export function formatRelativeDate(date) {
|
|
13
|
+
const now = Date.now();
|
|
14
|
+
const diffMs = now - date.getTime();
|
|
15
|
+
const diffSec = Math.floor(diffMs / 1000);
|
|
16
|
+
const diffMin = Math.floor(diffSec / 60);
|
|
17
|
+
const diffHour = Math.floor(diffMin / 60);
|
|
18
|
+
const diffDay = Math.floor(diffHour / 24);
|
|
19
|
+
const diffWeek = Math.floor(diffDay / 7);
|
|
20
|
+
const diffMonth = Math.floor(diffDay / 30);
|
|
21
|
+
const diffYear = Math.floor(diffDay / 365);
|
|
22
|
+
if (diffYear > 0)
|
|
23
|
+
return `${diffYear}y ago`;
|
|
24
|
+
if (diffMonth > 0)
|
|
25
|
+
return `${diffMonth}mo ago`;
|
|
26
|
+
if (diffWeek > 0)
|
|
27
|
+
return `${diffWeek}w ago`;
|
|
28
|
+
if (diffDay > 0)
|
|
29
|
+
return `${diffDay}d ago`;
|
|
30
|
+
if (diffHour > 0)
|
|
31
|
+
return `${diffHour}h ago`;
|
|
32
|
+
if (diffMin > 0)
|
|
33
|
+
return `${diffMin}m ago`;
|
|
34
|
+
return 'just now';
|
|
35
|
+
}
|
|
9
36
|
// Utility function to truncate strings with ellipsis
|
|
10
37
|
export function truncateString(str, maxLength) {
|
|
11
38
|
if (str.length <= maxLength)
|
|
@@ -107,6 +134,10 @@ export function prepareWorktreeItems(worktrees, sessions) {
|
|
|
107
134
|
// Show fetching status in dim gray
|
|
108
135
|
fileChanges = '\x1b[90m[fetching...]\x1b[0m';
|
|
109
136
|
}
|
|
137
|
+
// Format last commit date as dim relative time
|
|
138
|
+
const lastCommitDate = wt.lastCommitDate
|
|
139
|
+
? `\x1b[90m${formatRelativeDate(wt.lastCommitDate)}\x1b[0m`
|
|
140
|
+
: '';
|
|
110
141
|
return {
|
|
111
142
|
worktree: wt,
|
|
112
143
|
session,
|
|
@@ -114,12 +145,14 @@ export function prepareWorktreeItems(worktrees, sessions) {
|
|
|
114
145
|
fileChanges,
|
|
115
146
|
aheadBehind,
|
|
116
147
|
parentBranch,
|
|
148
|
+
lastCommitDate,
|
|
117
149
|
error,
|
|
118
150
|
lengths: {
|
|
119
151
|
base: stripAnsi(baseLabel).length,
|
|
120
152
|
fileChanges: stripAnsi(fileChanges).length,
|
|
121
153
|
aheadBehind: stripAnsi(aheadBehind).length,
|
|
122
154
|
parentBranch: stripAnsi(parentBranch).length,
|
|
155
|
+
lastCommitDate: stripAnsi(lastCommitDate).length,
|
|
123
156
|
},
|
|
124
157
|
};
|
|
125
158
|
});
|
|
@@ -132,6 +165,7 @@ export function calculateColumnPositions(items) {
|
|
|
132
165
|
let maxBranchLength = 0;
|
|
133
166
|
let maxFileChangesLength = 0;
|
|
134
167
|
let maxAheadBehindLength = 0;
|
|
168
|
+
let maxParentBranchLength = 0;
|
|
135
169
|
items.forEach(item => {
|
|
136
170
|
// Skip items with errors for alignment calculation
|
|
137
171
|
if (item.error)
|
|
@@ -139,15 +173,18 @@ export function calculateColumnPositions(items) {
|
|
|
139
173
|
maxBranchLength = Math.max(maxBranchLength, item.lengths.base);
|
|
140
174
|
maxFileChangesLength = Math.max(maxFileChangesLength, item.lengths.fileChanges);
|
|
141
175
|
maxAheadBehindLength = Math.max(maxAheadBehindLength, item.lengths.aheadBehind);
|
|
176
|
+
maxParentBranchLength = Math.max(maxParentBranchLength, item.lengths.parentBranch);
|
|
142
177
|
});
|
|
143
178
|
// Simple column positioning
|
|
144
179
|
const fileChangesColumn = maxBranchLength + MIN_COLUMN_PADDING;
|
|
145
180
|
const aheadBehindColumn = fileChangesColumn + maxFileChangesLength + MIN_COLUMN_PADDING + 2;
|
|
146
181
|
const parentBranchColumn = aheadBehindColumn + maxAheadBehindLength + MIN_COLUMN_PADDING + 2;
|
|
182
|
+
const lastCommitDateColumn = parentBranchColumn + maxParentBranchLength + MIN_COLUMN_PADDING + 2;
|
|
147
183
|
return {
|
|
148
184
|
fileChanges: fileChangesColumn,
|
|
149
185
|
aheadBehind: aheadBehindColumn,
|
|
150
186
|
parentBranch: parentBranchColumn,
|
|
187
|
+
lastCommitDate: lastCommitDateColumn,
|
|
151
188
|
};
|
|
152
189
|
}
|
|
153
190
|
// Pad string to column position
|
|
@@ -175,6 +212,11 @@ export function assembleWorktreeLabel(item, columns) {
|
|
|
175
212
|
if (item.parentBranch) {
|
|
176
213
|
label =
|
|
177
214
|
padTo(label, currentLength, columns.parentBranch) + item.parentBranch;
|
|
215
|
+
currentLength = columns.parentBranch + item.lengths.parentBranch;
|
|
216
|
+
}
|
|
217
|
+
if (item.lastCommitDate) {
|
|
218
|
+
label =
|
|
219
|
+
padTo(label, currentLength, columns.lastCommitDate) + item.lastCommitDate;
|
|
178
220
|
}
|
|
179
221
|
return label;
|
|
180
222
|
}
|
|
@@ -170,11 +170,13 @@ describe('column alignment', () => {
|
|
|
170
170
|
fileChanges: '\x1b[32m+10\x1b[0m \x1b[31m-5\x1b[0m',
|
|
171
171
|
aheadBehind: '\x1b[33m↑2 ↓3\x1b[0m',
|
|
172
172
|
parentBranch: '',
|
|
173
|
+
lastCommitDate: '',
|
|
173
174
|
lengths: {
|
|
174
175
|
base: 19, // 'feature/test-branch'.length
|
|
175
176
|
fileChanges: 6, // '+10 -5'.length
|
|
176
177
|
aheadBehind: 5, // '↑2 ↓3'.length
|
|
177
178
|
parentBranch: 0,
|
|
179
|
+
lastCommitDate: 0,
|
|
178
180
|
},
|
|
179
181
|
},
|
|
180
182
|
{
|
|
@@ -183,11 +185,13 @@ describe('column alignment', () => {
|
|
|
183
185
|
fileChanges: '\x1b[32m+2\x1b[0m \x1b[31m-1\x1b[0m',
|
|
184
186
|
aheadBehind: '\x1b[33m↑1\x1b[0m',
|
|
185
187
|
parentBranch: '',
|
|
188
|
+
lastCommitDate: '',
|
|
186
189
|
lengths: {
|
|
187
190
|
base: 4, // 'main'.length
|
|
188
191
|
fileChanges: 5, // '+2 -1'.length
|
|
189
192
|
aheadBehind: 2, // '↑1'.length
|
|
190
193
|
parentBranch: 0,
|
|
194
|
+
lastCommitDate: 0,
|
|
191
195
|
},
|
|
192
196
|
},
|
|
193
197
|
];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ccmanager",
|
|
3
|
-
"version": "3.11.
|
|
3
|
+
"version": "3.11.2",
|
|
4
4
|
"description": "TUI application for managing multiple Claude Code sessions across Git worktrees",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Kodai Kabasawa",
|
|
@@ -41,11 +41,11 @@
|
|
|
41
41
|
"bin"
|
|
42
42
|
],
|
|
43
43
|
"optionalDependencies": {
|
|
44
|
-
"@kodaikabasawa/ccmanager-darwin-arm64": "3.11.
|
|
45
|
-
"@kodaikabasawa/ccmanager-darwin-x64": "3.11.
|
|
46
|
-
"@kodaikabasawa/ccmanager-linux-arm64": "3.11.
|
|
47
|
-
"@kodaikabasawa/ccmanager-linux-x64": "3.11.
|
|
48
|
-
"@kodaikabasawa/ccmanager-win32-x64": "3.11.
|
|
44
|
+
"@kodaikabasawa/ccmanager-darwin-arm64": "3.11.2",
|
|
45
|
+
"@kodaikabasawa/ccmanager-darwin-x64": "3.11.2",
|
|
46
|
+
"@kodaikabasawa/ccmanager-linux-arm64": "3.11.2",
|
|
47
|
+
"@kodaikabasawa/ccmanager-linux-x64": "3.11.2",
|
|
48
|
+
"@kodaikabasawa/ccmanager-win32-x64": "3.11.2"
|
|
49
49
|
},
|
|
50
50
|
"devDependencies": {
|
|
51
51
|
"@eslint/js": "^9.28.0",
|