ccmanager 3.5.3 → 3.6.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.
@@ -1,6 +1,6 @@
1
1
  import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
2
2
  import { useState, useEffect } from 'react';
3
- import { Box, Text, useInput } from 'ink';
3
+ import { Box, Text, useInput, useStdout } from 'ink';
4
4
  import SelectInput from 'ink-select-input';
5
5
  import { Effect } from 'effect';
6
6
  import { SessionManager } from '../services/sessionManager.js';
@@ -35,13 +35,29 @@ const Menu = ({ sessionManager, worktreeService, onSelectWorktree, onSelectRecen
35
35
  const [sessions, setSessions] = useState([]);
36
36
  const [items, setItems] = useState([]);
37
37
  const [recentProjects, setRecentProjects] = useState([]);
38
- const limit = 10;
39
- // Get worktree configuration for sorting
40
- const worktreeConfig = configReader.getWorktreeConfig();
38
+ const { stdout } = useStdout();
39
+ const fixedRows = 6;
40
+ const [terminalRows, setTerminalRows] = useState(stdout.rows);
41
+ // Update terminal rows on resize
42
+ useEffect(() => {
43
+ const handleResize = () => {
44
+ setTerminalRows(stdout.rows);
45
+ };
46
+ stdout.on('resize', handleResize);
47
+ return () => {
48
+ stdout.off('resize', handleResize);
49
+ };
50
+ }, [stdout]);
41
51
  // Use the search mode hook
42
52
  const { isSearchMode, searchQuery, selectedIndex, setSearchQuery } = useSearchMode(items.length, {
43
53
  isDisabled: !!error || !!loadError,
44
54
  });
55
+ const limit = Math.max(5, terminalRows -
56
+ fixedRows -
57
+ (isSearchMode ? 1 : 0) -
58
+ (error || loadError ? 3 : 0));
59
+ // Get worktree configuration for sorting
60
+ const worktreeConfig = configReader.getWorktreeConfig();
45
61
  useEffect(() => {
46
62
  let cancelled = false;
47
63
  // Load worktrees and default branch using Effect composition
@@ -17,7 +17,6 @@ export declare class SessionManager extends EventEmitter implements ISessionMana
17
17
  private spawn;
18
18
  detectTerminalState(session: Session): SessionState;
19
19
  detectBackgroundTask(session: Session): number;
20
- private getTerminalContent;
21
20
  private handleAutoApproval;
22
21
  private cancelAutoApprovalVerification;
23
22
  /**
@@ -46,11 +46,6 @@ export class SessionManager extends EventEmitter {
46
46
  detectBackgroundTask(session) {
47
47
  return session.stateDetector.detectBackgroundTask(session.terminal);
48
48
  }
49
- getTerminalContent(session) {
50
- // Use the new screen capture utility that correctly handles
51
- // both normal and alternate screen buffers
52
- return getTerminalScreenContent(session.terminal, TERMINAL_CONTENT_MAX_LINES);
53
- }
54
49
  handleAutoApproval(session) {
55
50
  // Cancel any existing verification before starting a new one
56
51
  this.cancelAutoApprovalVerification(session, 'Restarting verification for pending auto-approval state');
@@ -61,7 +56,7 @@ export class SessionManager extends EventEmitter {
61
56
  autoApprovalReason: undefined,
62
57
  }));
63
58
  // Get terminal content for verification
64
- const terminalContent = this.getTerminalContent(session);
59
+ const terminalContent = getTerminalScreenContent(session.terminal, TERMINAL_CONTENT_MAX_LINES);
65
60
  // Verify if permission is needed
66
61
  void Effect.runPromise(autoApprovalVerifier.verifyNeedsPermission(terminalContent, {
67
62
  signal: abortController.signal,
@@ -277,6 +272,11 @@ export class SessionManager extends EventEmitter {
277
272
  session.process.onData((data) => {
278
273
  // Write data to virtual terminal
279
274
  session.terminal.write(data);
275
+ // Check for screen clear escape sequence (e.g., from /clear command)
276
+ // When detected, clear the output history to prevent replaying old content on restore
277
+ if (data.includes('\x1B[2J')) {
278
+ session.outputHistory = [];
279
+ }
280
280
  // Store in output history as Buffer
281
281
  const buffer = Buffer.from(data, 'utf8');
282
282
  session.outputHistory.push(buffer);
@@ -1,21 +1,10 @@
1
+ import { getTerminalScreenContent } from '../../utils/screenCapture.js';
1
2
  export class BaseStateDetector {
2
3
  getTerminalLines(terminal, maxLines = 30) {
3
- const buffer = terminal.buffer.active;
4
- const lines = [];
5
- // Start from the bottom and work our way up
6
- for (let i = buffer.length - 1; i >= 0 && lines.length < maxLines; i--) {
7
- const line = buffer.getLine(i);
8
- if (line) {
9
- const text = line.translateToString(true);
10
- // Skip empty lines at the bottom
11
- if (lines.length > 0 || text.trim() !== '') {
12
- lines.unshift(text);
13
- }
14
- }
15
- }
16
- return lines;
4
+ const content = getTerminalScreenContent(terminal, maxLines);
5
+ return content.split('\n');
17
6
  }
18
7
  getTerminalContent(terminal, maxLines = 30) {
19
- return this.getTerminalLines(terminal, maxLines).join('\n');
8
+ return getTerminalScreenContent(terminal, maxLines);
20
9
  }
21
10
  }
@@ -2,6 +2,10 @@ import type { Terminal } from '../../types/index.js';
2
2
  /**
3
3
  * Creates a mock Terminal object for testing state detectors.
4
4
  * @param lines - Array of strings representing terminal output lines
5
+ * @param options - Optional configuration for rows and cols
5
6
  * @returns Mock Terminal object with buffer interface
6
7
  */
7
- export declare const createMockTerminal: (lines: string[]) => Terminal;
8
+ export declare const createMockTerminal: (lines: string[], options?: {
9
+ rows?: number;
10
+ cols?: number;
11
+ }) => Terminal;
@@ -1,9 +1,12 @@
1
1
  /**
2
2
  * Creates a mock Terminal object for testing state detectors.
3
3
  * @param lines - Array of strings representing terminal output lines
4
+ * @param options - Optional configuration for rows and cols
4
5
  * @returns Mock Terminal object with buffer interface
5
6
  */
6
- export const createMockTerminal = (lines) => {
7
+ export const createMockTerminal = (lines, options) => {
8
+ const rows = options?.rows ?? lines.length;
9
+ const cols = options?.cols ?? 80;
7
10
  const buffer = {
8
11
  length: lines.length,
9
12
  active: {
@@ -11,12 +14,12 @@ export const createMockTerminal = (lines) => {
11
14
  getLine: (index) => {
12
15
  if (index >= 0 && index < lines.length) {
13
16
  return {
14
- translateToString: () => lines[index],
17
+ translateToString: (_trimRight, _startCol, _endCol) => lines[index],
15
18
  };
16
19
  }
17
20
  return null;
18
21
  },
19
22
  },
20
23
  };
21
- return { buffer };
24
+ return { buffer, rows, cols };
22
25
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ccmanager",
3
- "version": "3.5.3",
3
+ "version": "3.6.0",
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.5.3",
45
- "@kodaikabasawa/ccmanager-darwin-x64": "3.5.3",
46
- "@kodaikabasawa/ccmanager-linux-arm64": "3.5.3",
47
- "@kodaikabasawa/ccmanager-linux-x64": "3.5.3",
48
- "@kodaikabasawa/ccmanager-win32-x64": "3.5.3"
44
+ "@kodaikabasawa/ccmanager-darwin-arm64": "3.6.0",
45
+ "@kodaikabasawa/ccmanager-darwin-x64": "3.6.0",
46
+ "@kodaikabasawa/ccmanager-linux-arm64": "3.6.0",
47
+ "@kodaikabasawa/ccmanager-linux-x64": "3.6.0",
48
+ "@kodaikabasawa/ccmanager-win32-x64": "3.6.0"
49
49
  },
50
50
  "devDependencies": {
51
51
  "@eslint/js": "^9.28.0",