I'm ready to analyze the Github project changes and create an exciting update summary! However, I don't see any input provided after "INPUT:" in your message.

Please share the changelog, commit history, release notes, or any other information about what changed in the Github project since the last release, and I'll create a clean CHANGES section with exciting 10-word bullets and emojis for you.
This commit is contained in:
Pedram Amini
2025-12-17 14:52:17 -06:00
parent a8d3e06ad0
commit 86220f7d28
18 changed files with 152 additions and 109 deletions

View File

@@ -0,0 +1,5 @@
---
applyTo: '**'
---
Remembered number: 42

View File

@@ -242,6 +242,7 @@ jobs:
fi
- name: Create Release
id: create_release
if: steps.check_artifacts.outputs.has_artifacts == 'true'
uses: softprops/action-gh-release@v2
with:
@@ -255,3 +256,14 @@ jobs:
generate_release_notes: true
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Notify Discord
if: steps.check_artifacts.outputs.has_artifacts == 'true'
uses: sarisia/actions-status-discord@v1
with:
webhook: ${{ secrets.DISCORD_WEBHOOK_URL }}
nodetail: true
title: "🎉 Maestro ${{ github.ref_name }} Released!"
description: "A new version of Maestro is now available. Check out the release for download links and changelog."
url: ${{ github.server_url }}/${{ github.repository }}/releases/tag/${{ github.ref_name }}
color: 0x58B09C

View File

@@ -249,6 +249,7 @@ describe('agent-capabilities', () => {
'supportsStreaming',
'supportsResultMessages',
'supportsModelSelection',
'requiresPromptToStart',
];
const defaultKeys = Object.keys(DEFAULT_CAPABILITIES);

View File

@@ -152,7 +152,7 @@ let projectStatsCallback: ((stats: unknown) => void) | null = null;
const createDefaultProps = (overrides: Partial<Parameters<typeof AgentSessionsBrowser>[0]> = {}) => ({
theme: defaultTheme,
activeSession: createMockActiveSession(),
activeClaudeSessionId: null as string | null,
activeAgentSessionId: null as string | null,
onClose: vi.fn(),
onResumeSession: vi.fn(),
onNewSession: vi.fn(),
@@ -491,7 +491,7 @@ describe('AgentSessionsBrowser', () => {
it('shows active Claude session ID badge when provided', async () => {
const props = createDefaultProps({
activeClaudeSessionId: 'abc12345-def6-7890',
activeAgentSessionId: 'abc12345-def6-7890',
});
await act(async () => {
@@ -2351,7 +2351,7 @@ describe('AgentSessionsBrowser', () => {
});
const props = createDefaultProps({
activeClaudeSessionId: 'active-session-123',
activeAgentSessionId: 'active-session-123',
});
await act(async () => {
@@ -2359,7 +2359,7 @@ describe('AgentSessionsBrowser', () => {
await vi.runAllTimersAsync();
});
// When activeClaudeSessionId is provided, the component auto-jumps to detail view
// When activeAgentSessionId is provided, the component auto-jumps to detail view
// Go back to list view first
const backButton = screen.getByTestId('icon-chevron-left').closest('button');
await act(async () => {
@@ -2378,7 +2378,7 @@ describe('AgentSessionsBrowser', () => {
// ============================================================================
describe('auto-jump to session', () => {
it('auto-opens session detail when activeClaudeSessionId provided', async () => {
it('auto-opens session detail when activeAgentSessionId provided', async () => {
const session = createMockClaudeSession({ sessionId: 'target-session' });
vi.mocked(window.maestro.agentSessions.listPaginated).mockResolvedValue({
sessions: [session],
@@ -2393,7 +2393,7 @@ describe('AgentSessionsBrowser', () => {
});
const props = createDefaultProps({
activeClaudeSessionId: 'target-session',
activeAgentSessionId: 'target-session',
});
await act(async () => {

View File

@@ -23,6 +23,8 @@ vi.mock('../../../renderer/hooks/useAgentCapabilities', () => ({
supportsBatchMode: true,
supportsStreaming: true,
supportsResultMessages: true,
supportsModelSelection: false,
requiresPromptToStart: false,
},
loading: false,
error: null,
@@ -42,6 +44,8 @@ vi.mock('../../../renderer/hooks/useAgentCapabilities', () => ({
supportsBatchMode: true,
supportsStreaming: true,
supportsResultMessages: true,
supportsModelSelection: false,
requiresPromptToStart: false,
};
return capabilities[cap] ?? false;
}),
@@ -683,6 +687,8 @@ describe('InputArea', () => {
supportsBatchMode: false,
supportsStreaming: true,
supportsResultMessages: true,
supportsModelSelection: false,
requiresPromptToStart: false,
},
loading: false,
error: null,
@@ -719,6 +725,8 @@ describe('InputArea', () => {
supportsBatchMode: false,
supportsStreaming: true,
supportsResultMessages: true,
supportsModelSelection: false,
requiresPromptToStart: false,
},
loading: false,
error: null,

View File

@@ -267,9 +267,9 @@ describe('MainPanel', () => {
setGitDiffPreview: vi.fn(),
setLogViewerOpen: vi.fn(),
setAgentSessionsOpen: vi.fn(),
setActiveClaudeSessionId: vi.fn(),
onResumeClaudeSession: vi.fn(),
onNewClaudeSession: vi.fn(),
setActiveAgentSessionId: vi.fn(),
onResumeAgentSession: vi.fn(),
onNewAgentSession: vi.fn(),
setActiveFocus: vi.fn(),
setOutputSearchOpen: vi.fn(),
setOutputSearchQuery: vi.fn(),
@@ -439,16 +439,16 @@ describe('MainPanel', () => {
it('should open Agent Sessions when button is clicked', () => {
const setAgentSessionsOpen = vi.fn();
const setActiveClaudeSessionId = vi.fn();
const setActiveAgentSessionId = vi.fn();
render(<MainPanel
{...defaultProps}
setAgentSessionsOpen={setAgentSessionsOpen}
setActiveClaudeSessionId={setActiveClaudeSessionId}
setActiveAgentSessionId={setActiveAgentSessionId}
/>);
fireEvent.click(screen.getByTitle(/Agent Sessions/));
expect(setActiveClaudeSessionId).toHaveBeenCalledWith(null);
expect(setActiveAgentSessionId).toHaveBeenCalledWith(null);
expect(setAgentSessionsOpen).toHaveBeenCalledWith(true);
});

View File

@@ -142,7 +142,7 @@ const createDefaultProps = (overrides: Partial<React.ComponentProps<typeof Quick
setLogViewerOpen: vi.fn(),
setProcessMonitorOpen: vi.fn(),
setAgentSessionsOpen: vi.fn(),
setActiveClaudeSessionId: vi.fn(),
setActiveAgentSessionId: vi.fn(),
setGitDiffPreview: vi.fn(),
setGitLogOpen: vi.fn(),
...overrides,
@@ -531,7 +531,7 @@ describe('QuickActionsModal', () => {
fireEvent.click(screen.getByText('View Agent Sessions for Test Session'));
expect(props.setActiveClaudeSessionId).toHaveBeenCalledWith(null);
expect(props.setActiveAgentSessionId).toHaveBeenCalledWith(null);
expect(props.setAgentSessionsOpen).toHaveBeenCalledWith(true);
expect(props.setQuickActionOpen).toHaveBeenCalledWith(false);
});

View File

@@ -96,6 +96,22 @@ describe('useSessionManager', () => {
args: ['--print'],
available: true,
}),
getCapabilities: vi.fn().mockResolvedValue({
supportsResume: true,
supportsReadOnlyMode: true,
supportsJsonOutput: true,
supportsSessionId: true,
supportsImageInput: true,
supportsSlashCommands: true,
supportsSessionStorage: true,
supportsCostTracking: true,
supportsUsageStats: true,
supportsBatchMode: true,
supportsStreaming: true,
supportsResultMessages: true,
supportsModelSelection: false,
requiresPromptToStart: false,
}),
},
process: {
spawn: vi.fn().mockResolvedValue({ pid: 12345, success: true }),

View File

@@ -46,7 +46,7 @@ import { useKeyboardShortcutHelpers } from './hooks/useKeyboardShortcutHelpers';
import { useKeyboardNavigation } from './hooks/useKeyboardNavigation';
import { useMainKeyboardHandler } from './hooks/useMainKeyboardHandler';
import { useRemoteIntegration } from './hooks/useRemoteIntegration';
import { useClaudeSessionManagement } from './hooks/useClaudeSessionManagement';
import { useAgentSessionManagement } from './hooks/useAgentSessionManagement';
import { useAgentExecution } from './hooks/useAgentExecution';
import { useFileTreeManagement } from './hooks/useFileTreeManagement';
import { useGroupManagement } from './hooks/useGroupManagement';
@@ -315,7 +315,7 @@ export default function MaestroConsole() {
// Agent Sessions Browser State (main panel view)
const [agentSessionsOpen, setAgentSessionsOpen] = useState(false);
const [activeClaudeSessionId, setActiveClaudeSessionId] = useState<string | null>(null);
const [activeAgentSessionId, setActiveAgentSessionId] = useState<string | null>(null);
// NOTE: showSessionJumpNumbers state is now provided by useMainKeyboardHandler hook
@@ -1513,7 +1513,7 @@ export default function MaestroConsole() {
activeSessionIdRef.current = activeSessionId;
// Note: spawnBackgroundSynopsisRef and spawnAgentWithPromptRef are now provided by useAgentExecution hook
// Note: addHistoryEntryRef and startNewClaudeSessionRef are now provided by useClaudeSessionManagement hook
// Note: addHistoryEntryRef and startNewAgentSessionRef are now provided by useAgentSessionManagement hook
// Ref for processQueuedMessage - allows batch exit handler to process queued messages
const processQueuedItemRef = useRef<((sessionId: string, item: QueuedItem) => Promise<void>) | null>(null);
@@ -1994,14 +1994,14 @@ export default function MaestroConsole() {
const {
addHistoryEntry,
addHistoryEntryRef,
startNewClaudeSession,
startNewClaudeSessionRef,
startNewAgentSession,
startNewAgentSessionRef,
handleJumpToAgentSession,
handleResumeSession,
} = useClaudeSessionManagement({
} = useAgentSessionManagement({
activeSession,
setSessions,
setActiveClaudeSessionId,
setActiveAgentSessionId,
setAgentSessionsOpen,
addLogToActiveTab,
rightPanelRef,
@@ -3412,10 +3412,10 @@ export default function MaestroConsole() {
return;
}
// Build spawn args with resume if we have a Claude session ID
// Build spawn args with resume if we have an agent session ID
// Use the ACTIVE TAB's agentSessionId (not the deprecated session-level one)
const activeTab = getActiveTab(session);
const tabClaudeSessionId = activeTab?.agentSessionId;
const tabAgentSessionId = activeTab?.agentSessionId;
const isReadOnly = activeTab?.readOnlyMode;
// Filter out YOLO/skip-permissions flags when read-only mode is active
@@ -3429,8 +3429,8 @@ export default function MaestroConsole() {
)
: [...agent.args];
if (tabClaudeSessionId) {
spawnArgs.push('--resume', tabClaudeSessionId);
if (tabAgentSessionId) {
spawnArgs.push('--resume', tabAgentSessionId);
}
// Add read-only/plan mode if the active tab has readOnlyMode enabled
@@ -3442,12 +3442,12 @@ export default function MaestroConsole() {
const targetSessionId = `${sessionId}-ai-${activeTab?.id || 'default'}`;
const commandToUse = agent.path || agent.command;
console.log('[Remote] Spawning Claude directly:', {
console.log('[Remote] Spawning agent:', {
maestroSessionId: sessionId,
targetSessionId,
activeTabId: activeTab?.id,
tabClaudeSessionId: tabClaudeSessionId || 'NEW SESSION',
isResume: !!tabClaudeSessionId,
tabAgentSessionId: tabAgentSessionId || 'NEW SESSION',
isResume: !!tabAgentSessionId,
command: commandToUse,
args: spawnArgs,
prompt: promptToSend.substring(0, 100)
@@ -3598,7 +3598,7 @@ export default function MaestroConsole() {
// Build spawn args with resume if we have a session ID
// Use the TARGET TAB's agentSessionId (not the active tab or deprecated session-level one)
const tabClaudeSessionId = targetTab?.agentSessionId;
const tabAgentSessionId = targetTab?.agentSessionId;
const isReadOnly = item.readOnlyMode || targetTab?.readOnlyMode;
// Filter out YOLO/skip-permissions flags when read-only mode is active
@@ -3612,8 +3612,8 @@ export default function MaestroConsole() {
)
: [...(agent.args || [])];
if (tabClaudeSessionId) {
spawnArgs.push('--resume', tabClaudeSessionId);
if (tabAgentSessionId) {
spawnArgs.push('--resume', tabAgentSessionId);
}
// Add read-only/plan mode if the queued item was from a read-only tab
@@ -4420,7 +4420,7 @@ export default function MaestroConsole() {
setLeftSidebarOpen, setRightPanelOpen, addNewSession, deleteSession, setQuickActionInitialMode,
setQuickActionOpen, cycleSession, toggleInputMode, setShortcutsHelpOpen, setSettingsModalOpen,
setSettingsTab, setActiveRightTab, handleSetActiveRightTab, setActiveFocus, setBookmarksCollapsed, setGroups,
setSelectedSidebarIndex, setActiveSessionId, handleViewGitDiff, setGitLogOpen, setActiveClaudeSessionId,
setSelectedSidebarIndex, setActiveSessionId, handleViewGitDiff, setGitLogOpen, setActiveAgentSessionId,
setAgentSessionsOpen, setLogViewerOpen, setProcessMonitorOpen, logsEndRef, inputRef, terminalOutputRef, sidebarContainerRef,
setSessions, createTab, closeTab, reopenClosedTab, getActiveTab, setRenameTabId, setRenameTabInitialName,
setRenameTabModalOpen, navigateToNextTab, navigateToPrevTab, navigateToTabByIndex, navigateToLastTab,
@@ -4685,7 +4685,7 @@ export default function MaestroConsole() {
setProcessMonitorOpen={setProcessMonitorOpen}
setActiveRightTab={setActiveRightTab}
setAgentSessionsOpen={setAgentSessionsOpen}
setActiveClaudeSessionId={setActiveClaudeSessionId}
setActiveAgentSessionId={setActiveAgentSessionId}
setGitDiffPreview={setGitDiffPreview}
setGitLogOpen={setGitLogOpen}
isAiMode={activeSession?.inputMode === 'ai'}
@@ -5114,7 +5114,7 @@ export default function MaestroConsole() {
ref={mainPanelRef}
logViewerOpen={logViewerOpen}
agentSessionsOpen={agentSessionsOpen}
activeClaudeSessionId={activeClaudeSessionId}
activeAgentSessionId={activeAgentSessionId}
activeSession={activeSession}
sessions={sessions}
theme={theme}
@@ -5146,12 +5146,12 @@ export default function MaestroConsole() {
setGitDiffPreview={setGitDiffPreview}
setLogViewerOpen={setLogViewerOpen}
setAgentSessionsOpen={setAgentSessionsOpen}
setActiveClaudeSessionId={setActiveClaudeSessionId}
onResumeClaudeSession={(agentSessionId: string, messages: LogEntry[], sessionName?: string, starred?: boolean) => {
setActiveAgentSessionId={setActiveAgentSessionId}
onResumeAgentSession={(agentSessionId: string, messages: LogEntry[], sessionName?: string, starred?: boolean) => {
// Opens the Claude session as a new tab (or switches to existing tab if duplicate)
handleResumeSession(agentSessionId, messages, sessionName, starred);
}}
onNewClaudeSession={() => {
onNewAgentSession={() => {
// Create a fresh AI tab
if (activeSession) {
setSessions(prev => prev.map(s => {
@@ -5159,7 +5159,7 @@ export default function MaestroConsole() {
const result = createTab(s, { saveToHistory: defaultSaveToHistory });
return result.session;
}));
setActiveClaudeSessionId(null);
setActiveAgentSessionId(null);
}
setAgentSessionsOpen(false);
}}

View File

@@ -23,7 +23,7 @@ interface SearchResult {
interface AgentSessionsBrowserProps {
theme: Theme;
activeSession: Session | undefined;
activeClaudeSessionId: string | null;
activeAgentSessionId: string | null;
onClose: () => void;
onResumeSession: (agentSessionId: string, messages: LogEntry[], sessionName?: string, starred?: boolean) => void;
onNewSession: () => void;
@@ -33,7 +33,7 @@ interface AgentSessionsBrowserProps {
export function AgentSessionsBrowser({
theme,
activeSession,
activeClaudeSessionId,
activeAgentSessionId,
onClose,
onResumeSession,
onNewSession,
@@ -273,17 +273,17 @@ export function AgentSessionsBrowser({
cancelRename();
}, [activeSession?.cwd, renameValue, viewingSession?.sessionId, cancelRename, onUpdateTab, updateSession]);
// Auto-view session when activeClaudeSessionId is provided (e.g., from history panel click)
// Auto-view session when activeAgentSessionId is provided (e.g., from history panel click)
useEffect(() => {
// Only auto-jump once per activeClaudeSessionId
if (!loading && sessions.length > 0 && activeClaudeSessionId && !viewingSession && autoJumpedRef.current !== activeClaudeSessionId) {
const targetSession = sessions.find(s => s.sessionId === activeClaudeSessionId);
// Only auto-jump once per activeAgentSessionId
if (!loading && sessions.length > 0 && activeAgentSessionId && !viewingSession && autoJumpedRef.current !== activeAgentSessionId) {
const targetSession = sessions.find(s => s.sessionId === activeAgentSessionId);
if (targetSession) {
autoJumpedRef.current = activeClaudeSessionId;
autoJumpedRef.current = activeAgentSessionId;
handleViewSession(targetSession);
}
}
}, [loading, sessions, activeClaudeSessionId, viewingSession, handleViewSession]);
}, [loading, sessions, activeAgentSessionId, viewingSession, handleViewSession]);
// Focus input on mount
useEffect(() => {
@@ -639,12 +639,12 @@ export function AgentSessionsBrowser({
<span className="text-sm font-medium" style={{ color: theme.colors.textMain }}>
{agentId === 'claude-code' ? 'Claude' : 'Agent'} Sessions for {activeSession?.name || 'Agent'}
</span>
{activeClaudeSessionId && (
{activeAgentSessionId && (
<span
className="text-xs px-2 py-0.5 rounded-full"
style={{ backgroundColor: theme.colors.accent + '20', color: theme.colors.accent }}
>
Active: {activeClaudeSessionId.slice(0, 8)}...
Active: {activeAgentSessionId.slice(0, 8)}...
</span>
)}
</>
@@ -1116,7 +1116,7 @@ export function AgentSessionsBrowser({
index={i}
selectedIndex={selectedIndex}
isStarred={starredSessions.has(session.sessionId)}
activeClaudeSessionId={activeClaudeSessionId}
activeAgentSessionId={activeAgentSessionId}
renamingSessionId={renamingSessionId}
renameValue={renameValue}
searchMode={searchMode}

View File

@@ -32,7 +32,7 @@ interface MainPanelProps {
// State
logViewerOpen: boolean;
agentSessionsOpen: boolean;
activeClaudeSessionId: string | null;
activeAgentSessionId: string | null;
activeSession: Session | null;
sessions: Session[]; // All sessions for InputArea's ThinkingStatusPill
theme: Theme;
@@ -77,9 +77,9 @@ interface MainPanelProps {
setGitDiffPreview: (preview: string | null) => void;
setLogViewerOpen: (open: boolean) => void;
setAgentSessionsOpen: (open: boolean) => void;
setActiveClaudeSessionId: (id: string | null) => void;
onResumeClaudeSession: (agentSessionId: string, messages: import('../types').LogEntry[], sessionName?: string, starred?: boolean) => void;
onNewClaudeSession: () => void;
setActiveAgentSessionId: (id: string | null) => void;
onResumeAgentSession: (agentSessionId: string, messages: import('../types').LogEntry[], sessionName?: string, starred?: boolean) => void;
onNewAgentSession: () => void;
setActiveFocus: (focus: FocusArea) => void;
setOutputSearchOpen: (open: boolean) => void;
setOutputSearchQuery: (query: string) => void;
@@ -184,7 +184,7 @@ interface MainPanelProps {
export const MainPanel = forwardRef<MainPanelHandle, MainPanelProps>(function MainPanel(props, ref) {
const {
logViewerOpen, agentSessionsOpen, activeClaudeSessionId, activeSession, sessions, theme, activeFocus, outputSearchOpen, outputSearchQuery,
logViewerOpen, agentSessionsOpen, activeAgentSessionId, activeSession, sessions, theme, activeFocus, outputSearchOpen, outputSearchQuery,
inputValue, enterToSendAI, enterToSendTerminal, stagedImages, commandHistoryOpen, commandHistoryFilter,
commandHistorySelectedIndex, slashCommandOpen, slashCommands, selectedSlashCommandIndex,
tabCompletionOpen, tabCompletionSuggestions, selectedTabCompletionIndex, tabCompletionFilter,
@@ -192,8 +192,8 @@ export const MainPanel = forwardRef<MainPanelHandle, MainPanelProps>(function Ma
atMentionOpen, atMentionFilter, atMentionStartIndex, atMentionSuggestions, selectedAtMentionIndex,
setAtMentionOpen, setAtMentionFilter, setAtMentionStartIndex, setSelectedAtMentionIndex,
previewFile, markdownEditMode, shortcuts, rightPanelOpen, maxOutputLines, gitDiffPreview,
fileTreeFilterOpen, logLevel, setGitDiffPreview, setLogViewerOpen, setAgentSessionsOpen, setActiveClaudeSessionId,
onResumeClaudeSession, onNewClaudeSession, setActiveFocus, setOutputSearchOpen, setOutputSearchQuery,
fileTreeFilterOpen, logLevel, setGitDiffPreview, setLogViewerOpen, setAgentSessionsOpen, setActiveAgentSessionId,
onResumeAgentSession, onNewAgentSession, setActiveFocus, setOutputSearchOpen, setOutputSearchQuery,
setInputValue, setEnterToSendAI, setEnterToSendTerminal, setStagedImages, setLightboxImage, setCommandHistoryOpen,
setCommandHistoryFilter, setCommandHistorySelectedIndex, setSlashCommandOpen,
setSelectedSlashCommandIndex, setPreviewFile, setMarkdownEditMode,
@@ -377,10 +377,10 @@ export const MainPanel = forwardRef<MainPanelHandle, MainPanelProps>(function Ma
<AgentSessionsBrowser
theme={theme}
activeSession={activeSession || undefined}
activeClaudeSessionId={activeClaudeSessionId}
activeAgentSessionId={activeAgentSessionId}
onClose={() => setAgentSessionsOpen(false)}
onResumeSession={onResumeClaudeSession}
onNewSession={onNewClaudeSession}
onResumeSession={onResumeAgentSession}
onNewSession={onNewAgentSession}
onUpdateTab={props.onUpdateTabByClaudeSessionId}
/>
</div>
@@ -782,7 +782,7 @@ export const MainPanel = forwardRef<MainPanelHandle, MainPanelProps>(function Ma
{hasCapability('supportsSessionStorage') && (
<button
onClick={() => {
setActiveClaudeSessionId(null);
setActiveAgentSessionId(null);
setAgentSessionsOpen(true);
}}
className="p-2 rounded hover:bg-white/5"

View File

@@ -344,7 +344,7 @@ export function NewInstanceModal({ isOpen, onClose, onCreate, theme, defaultAgen
className="text-xs font-mono px-3 py-2 rounded"
style={{ backgroundColor: theme.colors.bgActivity, color: theme.colors.textDim }}
>
{agent.path}
<span className="opacity-60">Detected:</span> {agent.path}
</div>
)}
{/* Custom path input */}

View File

@@ -49,7 +49,7 @@ interface QuickActionsModalProps {
setLogViewerOpen: (open: boolean) => void;
setProcessMonitorOpen: (open: boolean) => void;
setAgentSessionsOpen: (open: boolean) => void;
setActiveClaudeSessionId: (id: string | null) => void;
setActiveAgentSessionId: (id: string | null) => void;
setGitDiffPreview: (diff: string | null) => void;
setGitLogOpen: (open: boolean) => void;
onRenameTab?: () => void;
@@ -80,7 +80,7 @@ export function QuickActionsModal(props: QuickActionsModalProps) {
setLeftSidebarOpen, setRightPanelOpen, setActiveRightTab, toggleInputMode,
deleteSession, addNewSession, setSettingsModalOpen, setSettingsTab,
setShortcutsHelpOpen, setAboutModalOpen, setLogViewerOpen, setProcessMonitorOpen,
setAgentSessionsOpen, setActiveClaudeSessionId, setGitDiffPreview, setGitLogOpen,
setAgentSessionsOpen, setActiveAgentSessionId, setGitDiffPreview, setGitLogOpen,
onRenameTab, onToggleReadOnlyMode, onOpenTabSwitcher, tabShortcuts, isAiMode, setPlaygroundOpen, onRefreshGitFileState,
onDebugReleaseQueuedItem, markdownEditMode, onToggleMarkdownEditMode, setUpdateCheckModalOpen, openWizard, wizardGoToStep, setDebugWizardModalOpen, startTour, setFuzzyFileSearchOpen
} = props;
@@ -240,7 +240,7 @@ export function QuickActionsModal(props: QuickActionsModalProps) {
...(startTour ? [{ id: 'tour', label: 'Start Introductory Tour', subtext: 'Take a guided tour of the interface', action: () => { startTour(); setQuickActionOpen(false); } }] : []),
{ id: 'logs', label: 'View System Logs', shortcut: shortcuts.systemLogs, action: () => { setLogViewerOpen(true); setQuickActionOpen(false); } },
{ id: 'processes', label: 'View System Processes', shortcut: shortcuts.processMonitor, action: () => { setProcessMonitorOpen(true); setQuickActionOpen(false); } },
...(activeSession ? [{ id: 'agentSessions', label: `View Agent Sessions for ${activeSession.name}`, shortcut: shortcuts.agentSessions, action: () => { setActiveClaudeSessionId(null); setAgentSessionsOpen(true); setQuickActionOpen(false); } }] : []),
...(activeSession ? [{ id: 'agentSessions', label: `View Agent Sessions for ${activeSession.name}`, shortcut: shortcuts.agentSessions, action: () => { setActiveAgentSessionId(null); setAgentSessionsOpen(true); setQuickActionOpen(false); } }] : []),
...(activeSession?.isGitRepo ? [{ id: 'gitDiff', label: 'View Git Diff', shortcut: shortcuts.viewGitDiff, action: async () => {
const cwd = activeSession.inputMode === 'terminal' ? (activeSession.shellCwd || activeSession.cwd) : activeSession.cwd;
const diff = await gitService.getDiff(cwd);

View File

@@ -51,7 +51,7 @@ export interface SessionListItemProps {
/** Whether this session is starred */
isStarred: boolean;
/** Currently active Claude session ID (if any) */
activeClaudeSessionId: string | null;
activeAgentSessionId: string | null;
/** ID of session currently being renamed (if any) */
renamingSessionId: string | null;
/** Current rename input value */
@@ -90,7 +90,7 @@ export function SessionListItem({
index,
selectedIndex,
isStarred,
activeClaudeSessionId,
activeAgentSessionId,
renamingSessionId,
renameValue,
searchMode,
@@ -108,7 +108,7 @@ export function SessionListItem({
}: SessionListItemProps) {
const isSelected = index === selectedIndex;
const isRenaming = renamingSessionId === session.sessionId;
const isActive = activeClaudeSessionId === session.sessionId;
const isActive = activeAgentSessionId === session.sessionId;
return (
<div

View File

@@ -25,7 +25,7 @@ export { useKeyboardShortcutHelpers } from './useKeyboardShortcutHelpers';
export { useKeyboardNavigation } from './useKeyboardNavigation';
export { useMainKeyboardHandler } from './useMainKeyboardHandler';
export { useRemoteIntegration } from './useRemoteIntegration';
export { useClaudeSessionManagement } from './useClaudeSessionManagement';
export { useAgentSessionManagement } from './useAgentSessionManagement';
export { useAgentExecution } from './useAgentExecution';
export { useFileTreeManagement } from './useFileTreeManagement';
export { useGroupManagement } from './useGroupManagement';
@@ -80,10 +80,10 @@ export type {
UseRemoteIntegrationReturn,
} from './useRemoteIntegration';
export type {
UseClaudeSessionManagementDeps,
UseClaudeSessionManagementReturn,
UseAgentSessionManagementDeps,
UseAgentSessionManagementReturn,
HistoryEntryInput,
} from './useClaudeSessionManagement';
} from './useAgentSessionManagement';
export type {
UseAgentExecutionDeps,
UseAgentExecutionReturn,

View File

@@ -22,15 +22,15 @@ export interface HistoryEntryInput {
}
/**
* Dependencies for the useClaudeSessionManagement hook.
* Dependencies for the useAgentSessionManagement hook.
*/
export interface UseClaudeSessionManagementDeps {
export interface UseAgentSessionManagementDeps {
/** Current active session (null if none selected) */
activeSession: Session | null;
/** Session state setter */
setSessions: React.Dispatch<React.SetStateAction<Session[]>>;
/** Claude session ID setter */
setActiveClaudeSessionId: (id: string | null) => void;
/** Agent session ID setter */
setActiveAgentSessionId: (id: string | null) => void;
/** Agent sessions browser open state setter */
setAgentSessionsOpen: (open: boolean) => void;
/** Helper to add a log entry to the active tab */
@@ -45,20 +45,20 @@ export interface UseClaudeSessionManagementDeps {
}
/**
* Return type for useClaudeSessionManagement hook.
* Return type for useAgentSessionManagement hook.
*/
export interface UseClaudeSessionManagementReturn {
export interface UseAgentSessionManagementReturn {
/** Add a history entry for the current session */
addHistoryEntry: (entry: HistoryEntryInput) => Promise<void>;
/** Ref to addHistoryEntry for use in callbacks that need latest version */
addHistoryEntryRef: React.MutableRefObject<((entry: HistoryEntryInput) => Promise<void>) | null>;
/** Clear Claude session and start fresh */
startNewClaudeSession: () => void;
/** Ref to startNewClaudeSession for use in callbacks that need latest version */
startNewClaudeSessionRef: React.MutableRefObject<(() => void) | null>;
/** Clear Agent session and start fresh */
startNewAgentSession: () => void;
/** Ref to startNewAgentSession for use in callbacks that need latest version */
startNewAgentSessionRef: React.MutableRefObject<(() => void) | null>;
/** Jump to a specific agent session in the browser */
handleJumpToAgentSession: (agentSessionId: string) => void;
/** Resume a Claude session, opening as a new tab or switching to existing */
/** Resume a Agent session, opening as a new tab or switching to existing */
handleResumeSession: (
agentSessionId: string,
providedMessages?: LogEntry[],
@@ -68,24 +68,24 @@ export interface UseClaudeSessionManagementReturn {
}
/**
* Hook for Claude-specific session operations.
* Hook for Agent-specific session operations.
*
* Handles:
* - Adding history entries with session metadata
* - Starting new Claude sessions (clearing context)
* - Jumping to Claude sessions in the browser
* - Resuming saved Claude sessions as tabs
* - Starting new Agent sessions (clearing context)
* - Jumping to Agent sessions in the browser
* - Resuming saved Agent sessions as tabs
*
* @param deps - Hook dependencies
* @returns Session management functions and refs
*/
export function useClaudeSessionManagement(
deps: UseClaudeSessionManagementDeps
): UseClaudeSessionManagementReturn {
export function useAgentSessionManagement(
deps: UseAgentSessionManagementDeps
): UseAgentSessionManagementReturn {
const {
activeSession,
setSessions,
setActiveClaudeSessionId,
setActiveAgentSessionId,
setAgentSessionsOpen,
addLogToActiveTab,
rightPanelRef,
@@ -94,7 +94,7 @@ export function useClaudeSessionManagement(
// Refs for functions that need to be accessed from other callbacks
const addHistoryEntryRef = useRef<((entry: HistoryEntryInput) => Promise<void>) | null>(null);
const startNewClaudeSessionRef = useRef<(() => void) | null>(null);
const startNewAgentSessionRef = useRef<(() => void) | null>(null);
/**
* Add a history entry for a session.
@@ -135,10 +135,10 @@ export function useClaudeSessionManagement(
}, [activeSession, rightPanelRef]);
/**
* Start a new Claude session by clearing the current context.
* Start a new Agent session by clearing the current context.
* Blocks if there are queued items.
*/
const startNewClaudeSession = useCallback(() => {
const startNewAgentSession = useCallback(() => {
if (!activeSession) return;
// Block clearing when there are queued items
@@ -168,8 +168,8 @@ export function useClaudeSessionManagement(
aiTabs: updatedAiTabs
};
}));
setActiveClaudeSessionId(null);
}, [activeSession, addLogToActiveTab, setSessions, setActiveClaudeSessionId]);
setActiveAgentSessionId(null);
}, [activeSession, addLogToActiveTab, setSessions, setActiveAgentSessionId]);
/**
* Jump to a specific agent session in the agent sessions browser.
@@ -177,11 +177,11 @@ export function useClaudeSessionManagement(
const handleJumpToAgentSession = useCallback((agentSessionId: string) => {
// Set the agent session ID and load its messages
if (activeSession) {
setActiveClaudeSessionId(agentSessionId);
setActiveAgentSessionId(agentSessionId);
// Open the agent sessions browser to show the selected session
setAgentSessionsOpen(true);
}
}, [activeSession, setActiveClaudeSessionId, setAgentSessionsOpen]);
}, [activeSession, setActiveAgentSessionId, setAgentSessionsOpen]);
/**
* Resume an agent session - opens as a new tab or switches to existing tab.
@@ -204,7 +204,7 @@ export function useClaudeSessionManagement(
? { ...s, activeTabId: existingTab.id, inputMode: 'ai' }
: s
));
setActiveClaudeSessionId(agentSessionId);
setActiveAgentSessionId(agentSessionId);
return;
}
@@ -239,7 +239,7 @@ export function useClaudeSessionManagement(
if (!starred && !sessionName) {
try {
// Look up session metadata from session origins (name and starred)
// Note: getSessionOrigins is still Claude-specific until we add generic origin tracking
// Note: getSessionOrigins is still Agent-specific until we add generic origin tracking
const origins = await window.maestro.claude.getSessionOrigins(activeSession.cwd);
const originData = origins[agentSessionId];
if (originData && typeof originData === 'object') {
@@ -271,21 +271,21 @@ export function useClaudeSessionManagement(
return { ...updatedSession, inputMode: 'ai' };
}));
setActiveClaudeSessionId(agentSessionId);
setActiveAgentSessionId(agentSessionId);
} catch (error) {
console.error('Failed to resume session:', error);
}
}, [activeSession?.cwd, activeSession?.id, activeSession?.aiTabs, activeSession?.toolType, setSessions, setActiveClaudeSessionId, defaultSaveToHistory]);
}, [activeSession?.cwd, activeSession?.id, activeSession?.aiTabs, activeSession?.toolType, setSessions, setActiveAgentSessionId, defaultSaveToHistory]);
// Update refs for slash command functions (so other handlers can access latest versions)
addHistoryEntryRef.current = addHistoryEntry;
startNewClaudeSessionRef.current = startNewClaudeSession;
startNewAgentSessionRef.current = startNewAgentSession;
return {
addHistoryEntry,
addHistoryEntryRef,
startNewClaudeSession,
startNewClaudeSessionRef,
startNewAgentSession,
startNewAgentSessionRef,
handleJumpToAgentSession,
handleResumeSession,
};

View File

@@ -244,7 +244,7 @@ export function useMainKeyboardHandler(): UseMainKeyboardHandlerReturn {
else if (ctx.isShortcut(e, 'agentSessions')) {
e.preventDefault();
if (ctx.activeSession?.toolType === 'claude-code') {
ctx.setActiveClaudeSessionId(null);
ctx.setActiveAgentSessionId(null);
ctx.setAgentSessionsOpen(true);
}
}

View File

@@ -191,11 +191,12 @@ export function useSessionManager(): UseSessionManagerReturn {
// Spawn AI process (terminal uses runCommand which spawns fresh shells per command)
try {
// Spawn AI agent process (skip for Claude batch mode)
const isClaudeBatchMode = agentId === 'claude-code';
let aiSpawnResult = { pid: 0, success: true }; // Default for batch mode
// Check if agent requires a prompt to start (Codex, OpenCode) - skip eager spawn for those
const capabilities = await window.maestro.agents.getCapabilities(agentId);
let aiSpawnResult = { pid: 0, success: true }; // Default for agents that don't need eager spawn
if (!isClaudeBatchMode) {
if (!capabilities.requiresPromptToStart) {
// Spawn for agents that support interactive mode (Claude Code)
aiSpawnResult = await window.maestro.process.spawn({
sessionId: `${newId}-ai`,
toolType: agentId,