Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 26 additions & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
@@ -1 +1,26 @@
@AGENTS.md
@AGENTS.md

## Workflow Profile

```yaml
workflow:
base_branch: main
direct_to_main: false # Contributor — work on feature branches
investigation: light # Quick search, no Explore agent
plan_approval: auto # Auto-approve plans
user_testing: skip # No manual testing needed
quality_gates:
- npm test
review:
triage: true # Use NONE/LIGHT triage
max_level: LIGHT # No custom review agents in this repo
agents:
- code-reviewer
ship:
method: pr # Contributor PR to upstream
target: main
linear_status: "In Progress" # Maintainer merge completes work
deploy_hint: "Maintainer reviews and merges PR"
labels:
auto_detect: false
```
28 changes: 16 additions & 12 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@ import { updateSettingsLocal, markSaved } from '@/store/settingsSlice'
import { clearIdleWarning, recordIdleWarning } from '@/store/idleWarningsSlice'
import { setTerminalMetaSnapshot, upsertTerminalMeta, removeTerminalMeta } from '@/store/terminalMetaSlice'
import { handleSdkMessage } from '@/lib/sdk-message-handler'
import { createLogger } from '@/lib/client-logger'


const log = createLogger('App')

const SIDEBAR_MIN_WIDTH = 200
const SIDEBAR_MAX_WIDTH = 500
Expand Down Expand Up @@ -104,7 +108,7 @@ export default function App() {
await api.patch('/api/settings', { sidebar: settings.sidebar })
dispatch(markSaved())
} catch (err) {
console.warn('Failed to save sidebar settings', err)
log.warn('Failed to save sidebar settings', err)
}
}, [settings.sidebar, dispatch])

Expand All @@ -118,7 +122,7 @@ export default function App() {
await api.patch('/api/settings', { sidebar: { ...settings.sidebar, collapsed: newCollapsed } })
dispatch(markSaved())
} catch (err) {
console.warn('Failed to save sidebar settings', err)
log.warn('Failed to save sidebar settings', err)
}
}, [isMobile, sidebarCollapsed, settings.sidebar, dispatch])

Expand All @@ -129,7 +133,7 @@ export default function App() {
await api.patch('/api/settings', { theme: newTheme })
dispatch(markSaved())
} catch (err) {
console.warn('Failed to save theme setting', err)
log.warn('Failed to save theme setting', err)
}
}

Expand Down Expand Up @@ -171,15 +175,15 @@ export default function App() {
})
} catch (err) {
// User cancelled or share failed - that's okay
console.warn('Share cancelled or failed:', err)
log.warn('Share cancelled or failed:', err)
}
} else {
// Fallback: copy to clipboard
try {
await navigator.clipboard.writeText(shareText)
// TODO: Show toast notification
} catch (err) {
console.warn('Clipboard write failed:', err)
log.warn('Clipboard write failed:', err)
}
}
}
Expand All @@ -191,7 +195,7 @@ export default function App() {
setCopied(true)
setTimeout(() => setCopied(false), 2000)
} catch (err) {
console.warn('Clipboard write failed:', err)
log.warn('Clipboard write failed:', err)
}
}

Expand All @@ -205,7 +209,7 @@ export default function App() {
const settings = await api.get('/api/settings')
if (!cancelled) dispatch(setSettings(applyLocalTerminalFontFamily(settings)))
} catch (err: any) {
console.warn('Failed to load settings', err)
log.warn('Failed to load settings', err)
}

try {
Expand All @@ -217,14 +221,14 @@ export default function App() {
}
}
} catch (err: any) {
console.warn('Failed to load platform info', err)
log.warn('Failed to load platform info', err)
}

try {
const projects = await api.get('/api/sessions')
if (!cancelled) dispatch(setProjects(projects))
} catch (err: any) {
console.warn('Failed to load sessions', err)
log.warn('Failed to load sessions', err)
}

const ws = getWsClient()
Expand Down Expand Up @@ -301,7 +305,7 @@ export default function App() {
if (msg.type === 'terminal.exit') {
const terminalId = msg.terminalId
const code = msg.exitCode
if (import.meta.env.MODE === 'development') console.log('terminal exit', terminalId, code)
log.debug('terminal exit', terminalId, code)
if (terminalId) {
dispatch(clearIdleWarning(terminalId))
dispatch(removeTerminalMeta(terminalId))
Expand All @@ -320,9 +324,9 @@ export default function App() {
// Log session repair status (silent for healthy/repaired, visible for problems)
const { sessionId, status, orphansFixed } = msg
if (status === 'missing') {
if (import.meta.env.MODE === 'development') console.warn(`Session ${sessionId.slice(0, 8)}... file is missing`)
log.warn(`Session ${sessionId.slice(0, 8)}... file is missing`)
} else if (status === 'repaired') {
if (import.meta.env.MODE === 'development') console.log(`Session ${sessionId.slice(0, 8)}... repaired (${orphansFixed} orphans fixed)`)
log.debug(`Session ${sessionId.slice(0, 8)}... repaired (${orphansFixed} orphans fixed)`)
}
// For 'healthy' status, no logging needed
}
Expand Down
8 changes: 6 additions & 2 deletions src/components/SettingsView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ import { terminalThemes, darkThemes, lightThemes, getTerminalTheme } from '@/lib
import { resolveTerminalFontFamily, saveLocalTerminalFontFamily } from '@/lib/terminal-fonts'
import type { SidebarSortMode, TerminalTheme, CodexSandboxMode, ClaudePermissionMode, CodingCliProviderName } from '@/store/types'
import { CODING_CLI_PROVIDER_CONFIGS } from '@/lib/coding-cli-utils'
import { createLogger } from '@/lib/client-logger'


const log = createLogger('SettingsView')

/** Monospace fonts with good Unicode block element support for terminal use */
const terminalFonts = [
Expand Down Expand Up @@ -198,7 +202,7 @@ export default function SettingsView() {
const scheduleSave = useCallback((updates: any) => {
if (pendingRef.current) clearTimeout(pendingRef.current)
pendingRef.current = setTimeout(() => {
patch(updates).catch((err) => console.warn('Failed to save settings', err))
patch(updates).catch((err) => log.warn('Failed to save settings', err))
pendingRef.current = null
}, 500)
}, [patch])
Expand All @@ -214,7 +218,7 @@ export default function SettingsView() {
const commitDefaultCwd = useCallback((nextValue: string | undefined) => {
if (nextValue === settings.defaultCwd) return
dispatch(updateSettingsLocal({ defaultCwd: nextValue } as any))
patch({ defaultCwd: nextValue ?? null } as any).catch((err) => console.warn('Failed to save settings', err))
patch({ defaultCwd: nextValue ?? null } as any).catch((err) => log.warn('Failed to save settings', err))
}, [dispatch, patch, settings.defaultCwd])

const scheduleDefaultCwdValidation = useCallback((value: string) => {
Expand Down
6 changes: 5 additions & 1 deletion src/components/Sidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ import { makeSelectSortedSessionItems, type SidebarSessionItem } from '@/store/s
import { ContextIds } from '@/components/context-menu/context-menu-constants'
import { ProviderIcon } from '@/components/icons/provider-icons'
import { getActiveSessionRefForTab } from '@/lib/session-utils'
import { createLogger } from '@/lib/client-logger'


const log = createLogger('Sidebar')

export type AppView = 'terminal' | 'sessions' | 'overview' | 'settings'

Expand Down Expand Up @@ -123,7 +127,7 @@ export default function Sidebar({
setSearchResults(response.results)
}
} catch (err) {
console.error('Search failed:', err)
log.error('Search failed:', err)
if (!controller.signal.aborted) {
setSearchResults([])
}
Expand Down
22 changes: 13 additions & 9 deletions src/components/TerminalView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ import { Loader2 } from 'lucide-react'
import { ConfirmModal } from '@/components/ui/confirm-modal'
import type { PaneContent, TerminalPaneContent } from '@/store/paneTypes'
import 'xterm/css/xterm.css'
import { createLogger } from '@/lib/client-logger'


const log = createLogger('TerminalView')

const SESSION_ACTIVITY_THROTTLE_MS = 5000
const RATE_LIMIT_RETRY_MAX_ATTEMPTS = 3
Expand Down Expand Up @@ -85,7 +89,7 @@ export default function TerminalView({ tabId, paneId, paneContent, hidden }: Ter
if (terminalContent) {
const prev = contentRef.current
if (prev && terminalContent.resumeSessionId !== prev.resumeSessionId) {
if (debugRef.current) console.log('[TRACE resumeSessionId] ref sync from props CHANGED resumeSessionId', {
if (debugRef.current) log.debug('[TRACE resumeSessionId] ref sync from props CHANGED resumeSessionId', {
paneId,
from: prev.resumeSessionId,
to: terminalContent.resumeSessionId,
Expand Down Expand Up @@ -140,7 +144,7 @@ export default function TerminalView({ tabId, paneId, paneContent, hidden }: Ter
const next = { ...current, ...updates }
// Trace resumeSessionId changes
if ('resumeSessionId' in updates && updates.resumeSessionId !== current.resumeSessionId) {
if (debugRef.current) console.log('[TRACE resumeSessionId] updateContent CHANGING resumeSessionId', {
if (debugRef.current) log.debug('[TRACE resumeSessionId] updateContent CHANGING resumeSessionId', {
paneId,
from: current.resumeSessionId,
to: updates.resumeSessionId,
Expand Down Expand Up @@ -458,7 +462,7 @@ export default function TerminalView({ tabId, paneId, paneContent, hidden }: Ter
const sendCreate = (requestId: string) => {
const restore = getRestoreFlag(requestId)
const resumeId = getResumeSessionIdFromRef(contentRef)
if (debugRef.current) console.log('[TRACE resumeSessionId] sendCreate', {
if (debugRef.current) log.debug('[TRACE resumeSessionId] sendCreate', {
paneId: paneIdRef.current,
requestId,
resumeSessionId: resumeId,
Expand Down Expand Up @@ -551,7 +555,7 @@ export default function TerminalView({ tabId, paneId, paneContent, hidden }: Ter
if (msg.type === 'terminal.created' && msg.requestId === reqId) {
clearRateLimitRetry()
const newId = msg.terminalId as string
if (debugRef.current) console.log('[TRACE resumeSessionId] terminal.created received', {
if (debugRef.current) log.debug('[TRACE resumeSessionId] terminal.created received', {
paneId: paneIdRef.current,
requestId: reqId,
terminalId: newId,
Expand Down Expand Up @@ -631,7 +635,7 @@ export default function TerminalView({ tabId, paneId, paneContent, hidden }: Ter
// Message type: { type: 'terminal.session.associated', terminalId: string, sessionId: string }
if (msg.type === 'terminal.session.associated' && msg.terminalId === tid) {
const sessionId = msg.sessionId as string
if (debugRef.current) console.log('[TRACE resumeSessionId] terminal.session.associated', {
if (debugRef.current) log.debug('[TRACE resumeSessionId] terminal.session.associated', {
paneId: paneIdRef.current,
terminalId: tid,
oldResumeSessionId: contentRef.current?.resumeSessionId,
Expand Down Expand Up @@ -662,7 +666,7 @@ export default function TerminalView({ tabId, paneId, paneContent, hidden }: Ter
if (msg.type === 'error' && msg.code === 'INVALID_TERMINAL_ID' && !msg.requestId) {
const currentTerminalId = terminalIdRef.current
const current = contentRef.current
if (debugRef.current) console.log('[TRACE resumeSessionId] INVALID_TERMINAL_ID received', {
if (debugRef.current) log.debug('[TRACE resumeSessionId] INVALID_TERMINAL_ID received', {
paneId: paneIdRef.current,
msgTerminalId: msg.terminalId,
currentTerminalId,
Expand All @@ -683,7 +687,7 @@ export default function TerminalView({ tabId, paneId, paneContent, hidden }: Ter
if (currentTerminalId && current?.status !== 'exited') {
term.writeln('\r\n[Reconnecting...]\r\n')
const newRequestId = nanoid()
if (debugRef.current) console.log('[TRACE resumeSessionId] INVALID_TERMINAL_ID reconnecting', {
if (debugRef.current) log.debug('[TRACE resumeSessionId] INVALID_TERMINAL_ID reconnecting', {
paneId: paneIdRef.current,
oldRequestId: requestIdRef.current,
newRequestId,
Expand Down Expand Up @@ -714,7 +718,7 @@ export default function TerminalView({ tabId, paneId, paneContent, hidden }: Ter
unsubReconnect = ws.onReconnect(() => {
bumpConnectionGeneration()
const tid = terminalIdRef.current
if (debugRef.current) console.log('[TRACE resumeSessionId] onReconnect', {
if (debugRef.current) log.debug('[TRACE resumeSessionId] onReconnect', {
paneId: paneIdRef.current,
terminalId: tid,
resumeSessionId: contentRef.current?.resumeSessionId,
Expand All @@ -728,7 +732,7 @@ export default function TerminalView({ tabId, paneId, paneContent, hidden }: Ter
// not re-run when terminalId changes from undefined to defined
const currentTerminalId = terminalIdRef.current

if (debugRef.current) console.log('[TRACE resumeSessionId] effect initial decision', {
if (debugRef.current) log.debug('[TRACE resumeSessionId] effect initial decision', {
paneId: paneIdRef.current,
currentTerminalId,
createRequestId,
Expand Down
22 changes: 13 additions & 9 deletions src/components/panes/EditorPane.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ import { isAbsolutePath, joinPath } from '@/lib/path-utils'
import { copyText } from '@/lib/clipboard'
import { registerEditorActions } from '@/lib/pane-action-registry'
import { ContextIds } from '@/components/context-menu/context-menu-constants'
import { createLogger } from '@/lib/client-logger'


const log = createLogger('EditorPane')

const AUTO_SAVE_DELAY = 5000

Expand Down Expand Up @@ -220,7 +224,7 @@ export default function EditorPane({
} catch (err) {
if (cancelled) return
const message = err instanceof Error ? err.message : String(err)
console.error(
log.error(
JSON.stringify({
severity: 'error',
event: 'editor_terminal_list_fetch_failed',
Expand Down Expand Up @@ -270,7 +274,7 @@ export default function EditorPane({
} catch (err) {
if (!mountedRef.current) return
const message = err instanceof Error ? err.message : String(err)
console.error(
log.error(
JSON.stringify({
severity: 'error',
event: 'editor_autocomplete_failed',
Expand Down Expand Up @@ -372,7 +376,7 @@ export default function EditorPane({
} catch (err) {
if (!mountedRef.current) return
const message = err instanceof Error ? err.message : String(err)
console.error(
log.error(
JSON.stringify({
severity: 'error',
event: 'editor_file_load_failed',
Expand Down Expand Up @@ -444,7 +448,7 @@ export default function EditorPane({
}
} catch (err) {
const message = err instanceof Error ? err.message : String(err)
console.error(
log.error(
JSON.stringify({
severity: 'error',
event: 'editor_file_picker_failed',
Expand All @@ -465,7 +469,7 @@ export default function EditorPane({

if (!picker) {
setFilePickerMessage('Native file picker is unavailable. Use the path field instead.')
console.warn(
log.warn(
JSON.stringify({
severity: 'warn',
event: 'editor_file_picker_unavailable',
Expand Down Expand Up @@ -533,7 +537,7 @@ export default function EditorPane({
return
}
const message = err instanceof Error ? err.message : String(err)
console.error(
log.error(
JSON.stringify({
severity: 'error',
event: 'editor_file_picker_failed',
Expand Down Expand Up @@ -575,7 +579,7 @@ export default function EditorPane({
}
} catch (err) {
const message = err instanceof Error ? err.message : String(err)
console.error(
log.error(
JSON.stringify({
severity: 'error',
event: 'editor_autosave_failed',
Expand Down Expand Up @@ -610,7 +614,7 @@ export default function EditorPane({
}
} catch (err) {
const message = err instanceof Error ? err.message : String(err)
console.error(
log.error(
JSON.stringify({
severity: 'error',
event: 'editor_manual_save_failed',
Expand All @@ -627,7 +631,7 @@ export default function EditorPane({
await api.post('/api/files/open', { path: resolved, reveal })
} catch (err) {
const message = err instanceof Error ? err.message : String(err)
console.error(
log.error(
JSON.stringify({
severity: 'error',
event: 'editor_open_external_failed',
Expand Down
Loading