Status
PASS
882 executed tests
Passed
882
100.0% pass rate
Failed
0
Duration
7.4s
1.5s vitest + 5.9s android
TS Line Coverage
28.6%
3,700/12,939 TypeScript lines
Total Tests
882
Pass Rate
100.0%
Line Coverage
28.6%
◈
Terminal Output Parsing
PASS
Claude Code와 Codex CLI의 터미널 출력을 정확히 해석하는가?
236 passed
0 failed
3 files
319ms
output-parser.test.ts
197
/
197
259ms
OutputParser
197 / 197
basic option parsing › parses clean numbered options with newlines
basic option parsing › detects (recommended) marker
basic option parsing › detects ✔ selected marker
basic option parsing › handles both recommended and selected on different options
basic option parsing › parses full Claude model selector with labels and middle dot
ANSI-stripped options › parses concatenated text with · delimiter
ANSI-stripped options › extracts version numbers from · delimited labels
chunked input debounce › does NOT emit immediately on first chunk
chunked input debounce › batches multiple chunks into single emission
chunked input debounce › resets debounce timer when new chunk with options arrives
chunked input debounce › simulates real /model flow: split across chunks
model ID date rejection › does NOT parse 20251001 from model ID as option number
model ID date rejection › rejects large numbers that look like dates
version number rejection › does NOT parse version "4.6" followed by digit as option
version number rejection › does NOT parse "6)" from "4.6)" as option 6
stale buffer overwrite › newer options overwrite older ones with same index
permission prompts › detects Yes/No/Always pattern
permission prompts › detects (Y)es/(N)o pattern
permission prompts › emits immediately with no debounce
permission prompts › sets yes_no_always prompt type
permission prompts › sets yes_no prompt type
permission prompts › suppresses false idle from user prompt echo after permission (interactive cooldown)
permission prompts › allows real idle after interactive cooldown expires
diff prompts › detects (V)iew/(A)pply/(D)eny pattern
diff prompts › detects lowercase (a)pply/(d)eny/(v)iew pattern
diff prompts › emits immediately with no debounce
diff prompts › suppresses false idle from user prompt echo after diff (interactive cooldown)
various option counts › handles 1 option
various option counts › handles 2 options
various option counts › handles 5+ options
various option counts › handles bullet-style options
option updates › emits new options when data changes after first emission
spinner cancels option timer › spinner cancels pending option debounce
idle vs option debounce › idle prompt is ignored when option debounce is pending
idle vs option debounce › idle prompt fires normally when no option debounce is pending
interactive prompt during spinner › stops spinner when permission prompt arrives
interactive prompt during spinner › stops spinner when diff prompt arrives
interactive prompt during spinner › stops spinner when option prompt arrives
interactive prompt during spinner › stops spinner when idle prompt arrives in small chunk
interactive prompt during spinner › ignores idle prompt in large chunk during spinner (screen redraw)
cleanOptionLabel (via parseOptions) › deduplicates exact CamelCase matches
cleanOptionLabel (via parseOptions) › preserves labels without · delimiter
cleanOptionLabel (via parseOptions) › removes (recommended) from label text
cleanOptionLabel (via parseOptions) › removes ✔ from label text
cleanOptionLabel (via parseOptions) › extracts version from identity before middle dot
metadata events › emits project_name from startup banner
metadata events › emits project_name only once (caches)
metadata events › parses absolute path for project_name
metadata events › emits model_info with model and plan
metadata events › emits model_info without plan
metadata events › emits model_info only once for same model (caches)
metadata events › re-emits model_info when model changes
metadata events › parses ANSI-stripped model_info
metadata events › detects API billing plan
metadata events › emits status_line with duration and tokens
metadata events › parses zero-minute status line
metadata events › emits tool_action from ⏺ pattern
metadata events › extracts various tool names
user prompt › emits user_prompt after first idle has been seen
user prompt › does NOT emit user_prompt before first idle
user prompt › filters out mode banner text
user prompt › filters out numbered option lines
user prompt › filters out keyboard hint text
user prompt › filters out "esc to interrupt" text
user prompt › filters out autocomplete suggestions
user prompt › filters out box-drawing decorative lines
usage info › parses usage percentage
usage info › parses usage cost
usage info › parses session percentage with hour limit
usage info › parses reset time with timezone
usage info › parses time remaining
spinner lifecycle › emits spinner_start on spinner char (after idle seen)
spinner lifecycle › does NOT emit spinner_start before first idle
spinner lifecycle › emits spinner_stop after debounce (2000ms)
spinner lifecycle › does NOT emit duplicate spinner_start on repeated chars
spinner lifecycle › resets spinner debounce timer on repeated chars
spinner lifecycle › ignores spinner chars in large text blocks (>80 non-ws)
spinner lifecycle › recognizes all spinner characters
idle detection › emits idle after IDLE_DEBOUNCE_MS (300ms)
idle detection › sets seenFirstIdle on first idle prompt
idle detection › cancels idle timer when spinner starts
idle detection › recognizes > as idle prompt char
mode detection › detects plan mode
mode detection › detects accept edits mode
mode detection › detects default mode after Shift+Tab
mode detection › detects default mode via idle prompt after Shift+Tab
mode detection › emits default mode on Shift+Tab timeout (2s)
mode detection › detects ANSI-stripped plan mode text
mode detection › detects ANSI-stripped accept edits text
mode detection › clears pending mode switch when mode banner is detected
reset › clears cached project name and model name
reset › disables spinner detection (clears seenFirstIdle)
reset › allows re-detection after new idle prompt post-reset
reset › allows re-detection of project name after reset
reset › allows re-detection of model info after reset
buffer management › truncates buffer when exceeding 8192 chars
buffer management › keeps the last 4096 chars after truncation
option navigation › cursor movement during option selection does NOT trigger idle
option navigation › cursor movement does NOT trigger user_prompt
option navigation › normal cursor movement between options emits no idle or user_prompt
option navigation › real idle prompt still works after option navigation
option navigation › effort/hint text during navigation does not trigger events
option navigation › IDLE_PROMPT requires space/tab/NBSP after ❯ (not newline)
option navigation › USER_PROMPT requires space/tab after ❯ (not newline)
permission reclassification › reclassifies numbered Yes/No options as permission_prompt
permission reclassification › does NOT reclassify regular options as permission
permission reclassification › does NOT reclassify cursor-selection UI with "Enter to confirm" as permission
permission reclassification › detects cursor-selection UI even when ANSI stripping removes spaces
permission reclassification › includes navigable and cursorIndex in reclassified permission_prompt
permission reclassification › infers shortcuts for reclassified permission options
permission reclassification › infers shortcut "a" for "don't ask again" labels
permission reclassification › infers shortcut "a" for "allow all sessions" labels
ghost text suggestion › detects SGR 90 (bright black) ghost text on prompt line
ghost text suggestion › detects ghost text via Strategy 1 (Try "..." in clean text)
ghost text suggestion › unwraps Try "..." wrapper with smart quotes
ghost text suggestion › unwraps Try "..." wrapper with straight quotes
ghost text suggestion › detects ghost text on first idle (❯ and suggestion in same chunk)
ghost text suggestion › clears suggestion on spinner start
ghost text suggestion › debounces rapid updates
ghost text suggestion › ignores dim (SGR 2) text without ❯ prompt context
ghost text suggestion › detects dim (SGR 2) ghost text on ❯ prompt line
ghost text suggestion › detects ghost text with cursor-forward spacing (Strategy 1)
ghost text suggestion › filters UI chrome fragments
ghost text suggestion › filters box-drawing lines as UI chrome
ghost text suggestion › filters file paths (false positive from screen redraws)
ghost text suggestion › detects 256-color gray ghost text
ghost text suggestion › handles multi-segment ghost text on prompt line
ghost text suggestion › ignores ghost text on non-prompt lines
ghost text suggestion › detects 24-bit RGB gray ghost text
ghost text suggestion › ignores 24-bit RGB non-gray colors (e.g. blue prompt char)
ghost text suggestion › filters out short gray text like prompt char itself
ghost text suggestion › suppresses ghost text detection during spinner (processing)
ghost text suggestion › rejects digit+operator fragments like diff markers "96 +"
ghost text UI chrome filtering › filters out "Tip:" segments on prompt line
ghost text UI chrome filtering › filters out "(ctrl+...)" shortcut hints on prompt line
ghost text UI chrome filtering › filters concatenated UI chrome even in scheduleSuggestion
ghost text UI chrome filtering › keeps ghost text when UI chrome is also present on the line
filters out extended thinking indicators › rejects "(thought for 1s)" as ghost text via isUiChrome
filters out extended thinking indicators › rejects "(thought for 1s)>" via scheduleSuggestion
filters out extended thinking indicators › rejects longer thinking durations like "(thought for 15s)"
filters out extended thinking indicators › rejects "(thought for 1m 30s)" multipart duration
filters out extended thinking indicators › rejects "✻ Cooked for 1m 26s" sparkle indicator
filters out extended thinking indicators › rejects "✻ Cooked for 5s" short duration variant
filters out extended thinking indicators › rejects "Cooked for 10s" without sparkle (cross-chunk)
parenthesized placeholder filtering › rejects "(no content)" via SGR 2 (dim)
parenthesized placeholder filtering › rejects "(no content)" via SGR 90 (bright black)
parenthesized placeholder filtering › rejects "(loading...)"
parenthesized placeholder filtering › rejects "(empty)"
parenthesized placeholder filtering › rejects "(waiting for response)"
parenthesized placeholder filtering › allows "fix the broken (auth) module" — parens in middle
parenthesized placeholder filtering › allows "(optional) refactor the code" — paren prefix with text after
stacked ANSI + cross-chunk ghost text › detects ghost text with stacked ANSI escapes (gray + italic)
stacked ANSI + cross-chunk ghost text › detects ghost text with combined SGR params (2;90 = dim+bright-black)
stacked ANSI + cross-chunk ghost text › detects cross-chunk ghost text (❯ and gray text in separate feeds)
stacked ANSI + cross-chunk ghost text › does NOT cross-chunk detect when chunk contains ⎿ output fence
stacked ANSI + cross-chunk ghost text › does NOT cross-chunk detect when new chunk has \n (different line)
ghost option from stale buffer › excludes stale numbered list items before actual option prompt
ghost option from stale buffer › bypasses chunk size guard when ❯ cursor is present in large chunk
ghost option from stale buffer › still works with scrambled TUI order after backward scan
ghost option from stale buffer › excludes file path fragments from Read() tool in permission prompt
option index ordering › returns options sorted by index even when TUI lines arrive out of order
split ANSI sequence buffering › handles ANSI SGR codes split across chunks
split ANSI sequence buffering › handles bare ESC at end of chunk
split ANSI sequence buffering › does not buffer complete ANSI sequences
large chunk guard for option detection › does NOT detect numbered items in large response chunks as options
large chunk guard for option detection › still detects real options in small TUI chunks
CUP-positioned options › parses options using CUP (\x1b[row;colH) instead of newlines
CUP-positioned options › parses options using CUD (\x1b[B) for vertical movement
trailing TUI chrome stripping › strips "Esc to cancel" from last option label
trailing TUI chrome stripping › strips "Enter to confirm" from last option label
trailing TUI chrome stripping › does NOT strip "Esc" when it appears within legitimate label text
CJK suggestion detection › accepts ghost text containing CJK characters
CJK suggestion detection › accepts ghost text that is purely CJK (no ASCII words)
AskUserQuestion with separators and descriptions › handles AskUserQuestion with separator and descriptions
AskUserQuestion with separators and descriptions › handles options starting from non-zero index (buffer truncation)
permission scroll does not trigger idle (Bug 1) › scroll chunk with ❯ option text does NOT cause idle when navigable
permission scroll does not trigger idle (Bug 1) › /model combined chunk: confirmation + idle prompt clears options
permission scroll does not trigger idle (Bug 1) › /model separate chunks: ANSI reposition timer does not block idle
permission scroll does not trigger idle (Bug 1) › cursor move chunk with option text does NOT falsely trigger idle
permission scroll does not trigger idle (Bug 1) › genuine idle prompt exits navigable state and emits idle
TUI cursor-overwrite label correction (Bug 2) › fixes contaminated permission option label using correction line
TUI cursor-overwrite label correction (Bug 2) › leaves labels unchanged when no correction line is present
genuine idle detection (semantic) › "❯ \n" is genuine idle — only prompt character, no label text
genuine idle detection (semantic) › "❯ Beta" is NOT idle — has label text after prompt char
genuine idle detection (semantic) › "❯ A" is NOT idle — single-char label still counts
genuine idle detection (semantic) › ">" also treated as idle prompt character
effort level parsing › detects "High effort" selection pattern
effort level parsing › detects "Medium effort" selection pattern
effort level parsing › detects "Low effort" selection pattern
effort level parsing › detects "with high effort" confirmation line
effort level parsing › detects effort in model info line
effort level parsing › caches effort level — no re-emit on same value
effort level parsing › emits on effort level change
effort level parsing › resets effort level on reset()
effort level parsing › does not match "effort" in unrelated context
effort level parsing › does not match effort inside numbered option lines
effort level parsing › getter returns current effort level
codex-output-parser.test.ts
24
/
24
32ms
CodexOutputParser
24 / 24
idle detection › emits idle on ❯ prompt
idle detection › emits idle on > prompt
idle detection › debounces rapid idle signals
spinner detection › emits spinner_start on braille spinner chars after first idle
spinner detection › does not emit spinner_start before first idle
spinner detection › emits spinner_stop + idle on timeout
spinner detection › emits spinner_stop when idle prompt appears
spinner detection › detects "Thinking" text as processing
permission prompt detection › emits permission_prompt on Allow/Deny pattern
permission prompt detection › emits permission_prompt on y/n pattern
permission prompt detection › stops spinner when approval detected
permission prompt detection › extracts Allow once / Always allow options
tool action detection › detects Running: command pattern
tool action detection › detects file operation patterns
tool action detection › detects Editing file pattern
model info detection › detects gpt model name
model info detection › detects o-series model
model info detection › does not re-emit same model
project name detection › detects working directory
project name detection › detects project from path
project name detection › only detects project name once
buffer management › truncates buffer at 8192 chars
buffer management › handles incomplete ANSI sequences
getProjectName › returns null when no project detected
cursor-sync.test.ts
15
/
15
28ms
Cursor Synchronization
15 / 15
terminal keyboard cursor tracking › emits cursor_update when full option redraw arrives with ❯ at new position
terminal keyboard cursor tracking › triggers buffer re-parse on small non-❯ chunk during navigable state
terminal keyboard cursor tracking › does not emit cursor_update when cursor position unchanged
genuine idle distinction › treats "❯ \n" as genuine idle (clears navigable state)
genuine idle distinction › does NOT treat "❯ Beta" as idle (cursor on option label)
genuine idle distinction › does NOT treat "❯ Allow once" as idle (permission cursor move)
cursor authority in StateMachine › accepts optimistic update immediately
cursor authority in StateMachine › suppresses stale PTY value within 200ms of optimistic update
cursor authority in StateMachine › accepts PTY value after 200ms grace period
cursor authority in StateMachine › always accepts PTY when no recent optimistic update
cursor authority in StateMachine › resets authority on state transition out of AWAITING
cursor authority in StateMachine › default source parameter is pty
select_option proportional delay › delay scales with step count
option re-emission with cursorIndex › AWAITING_OPTION update triggers state_changed with options
option re-emission with cursorIndex › updateCursorIndex emits snapshot with new cursor value
◇
State Machine & Adapters
PASS
에이전트의 상태 전이와 타입별 명령 라우팅이 정확한가?
167 passed
0 failed
3 files
166ms
state-machine.test.ts
50
/
50
53ms
StateMachine
50 / 50
basic transitions › starts in DISCONNECTED
basic transitions › SessionStart → IDLE
basic transitions › UserPromptSubmit → PROCESSING
basic transitions › Stop → IDLE from PROCESSING
basic transitions › SessionEnd → DISCONNECTED from any state
permission flow › permission_prompt → AWAITING_PERMISSION
permission flow › user respond → PROCESSING from AWAITING_PERMISSION
option flow › option_prompt → AWAITING_OPTION
option flow › select_option → PROCESSING from AWAITING_OPTION
diff flow › diff_prompt → AWAITING_DIFF
diff flow › respond → PROCESSING from AWAITING_DIFF
interrupt › interrupt → IDLE from PROCESSING
interrupt › interrupt → IDLE from AWAITING_PERMISSION
strict transitions › allows IDLE → AWAITING_PERMISSION (prompt without spinner)
strict transitions › blocks invalid transition: DISCONNECTED → PROCESSING
strict transitions › allows wildcard session_end from any state
stuck timeout › PROCESSING for >5min → auto-recovery to IDLE
stuck timeout › AWAITING_PERMISSION does NOT timeout (waits for user)
stuck timeout › AWAITING_OPTION does NOT timeout (waits for user)
stuck timeout › AWAITING_DIFF does NOT timeout (waits for user)
stuck timeout › timer resets on state change before timeout
stuck timeout › no timeout in IDLE state
parser events › spinner_start → PROCESSING from IDLE
parser events › spinner_stop → IDLE from PROCESSING
parser events › idle → IDLE from PROCESSING
parser events › mode_change updates permission mode
snapshot › emits state_changed on transitions
snapshot › includes tool info in snapshot
snapshot › clears tool info on PostToolUse
snapshot › includes project name and model
billingType detection › defaults to unknown
billingType detection › detects subscription from "Claude Max" plan
billingType detection › detects subscription from "Max" (case-insensitive)
billingType detection › detects api from "api.anthropic.com"
billingType detection › detects api (case-insensitive)
billingType detection › stays unknown for unrecognized plan
billingType detection › stays unknown when plan is absent
billingType detection › persists billingType across subsequent model_info without plan
billingType detection › emits state_changed when billingType is set
spinner_start recovery from awaiting states › spinner_start transitions from AWAITING_OPTION to PROCESSING
spinner_start recovery from awaiting states › spinner_start transitions from AWAITING_PERMISSION to PROCESSING
spinner_start recovery from awaiting states › spinner_start transitions from AWAITING_DIFF to PROCESSING
spinner_start recovery from awaiting states › clears options and navigable state on spinner_start from AWAITING_OPTION
spinner_start recovery from awaiting states › stores navigable and cursorIndex from permission_prompt
spinner_start recovery from awaiting states › cursor index tracking via updateCursorIndex
cursor authority (optimistic vs pty) › optimistic source updates cursor immediately
cursor authority (optimistic vs pty) › suppresses stale PTY within 200ms of optimistic
cursor authority (optimistic vs pty) › accepts PTY after 200ms grace period
cursor authority (optimistic vs pty) › emits state_changed on optimistic update
cursor authority (optimistic vs pty) › does NOT emit state_changed when stale PTY is suppressed
adapter.test.ts
93
/
93
83ms
createAdapter factory
5 / 5
creates ClaudeCodeAdapter for "claude-code"
creates OpenClawAdapter for "openclaw"
passes gatewayUrl to OpenClawAdapter
creates CodexCliAdapter for "codex-cli"
throws for unknown agent type
ClaudeCodeAdapter
17 / 17
capabilities › reports all Claude Code capabilities as true
handleCommand routing › handles respond → returns true
handleCommand routing › handles switch_mode → returns true
handleCommand routing › handles interrupt → returns true
handleCommand routing › handles escape → returns true
handleCommand routing › defers select_option to bridge → returns false
handleCommand routing › defers navigate_option to bridge → returns false
handleCommand routing › defers send_prompt to bridge → returns false
handleCommand routing › defers voice to bridge → returns false
handleCommand routing › defers query_usage to bridge → returns false
switch_mode debounce › debounces rapid switch_mode calls (< 100ms apart)
lifecycle › isAlive returns false before start
lifecycle › getTtyPath returns undefined before start
lifecycle › getProjectName returns null before start
lifecycle › getHttpServer returns an object
onRawData callback › can register callback without error
onDiag handler › can register handler without error
OpenClawAdapter
17 / 17
capabilities › reports OpenClaw capabilities correctly
handleCommand routing › handles respond → returns true (RPC)
handleCommand routing › handles select_option → returns true (RPC)
handleCommand routing › handles navigate_option → returns true (no-op)
handleCommand routing › handles send_prompt → returns false without session key
handleCommand routing › handles interrupt → returns true (RPC)
handleCommand routing › handles escape → returns true (RPC)
handleCommand routing › defers switch_mode → returns false (not supported)
handleCommand routing › defers voice to bridge → returns false
handleCommand routing › defers query_usage to bridge → returns false
lifecycle › isAlive returns false before start
lifecycle › getTtyPath returns undefined (no PTY)
lifecycle › getProjectName returns null before start
lifecycle › getHttpServer returns an object
lifecycle › attachTerminal is a no-op
onRawData callback › can register callback without error
onDiag handler › can register handler without error
OpenClawAdapter gateway protocol
13 / 13
sends connect request with correct format on connect.challenge
becomes alive after hello-ok response
emits spinner_start on chat delta event
emits idle on chat final event
emits idle on chat aborted event
emits idle on chat error event
emits permission_prompt on exec.approval.requested
sends exec.approval.resolve on respond after approval request
sends exec.approval.resolve with deny on respond "n"
sends chat.send on send_prompt with active session
sends chat.abort on interrupt with active session and runId
emits SessionEnd on shutdown event
clears pendingApprovalId on exec.approval.resolved
CodexCliAdapter
16 / 16
capabilities › reports Codex CLI capabilities correctly
handleCommand routing › handles respond → returns true
handleCommand routing › handles interrupt → returns true
handleCommand routing › handles escape → returns true
handleCommand routing › does not handle switch_mode → returns false
handleCommand routing › defers select_option to bridge → returns false
handleCommand routing › defers navigate_option to bridge → returns false
handleCommand routing › defers send_prompt to bridge → returns false
handleCommand routing › defers voice to bridge → returns false
handleCommand routing › defers query_usage to bridge → returns false
lifecycle › isAlive returns false before start
lifecycle › getTtyPath returns undefined before start
lifecycle › getProjectName returns null before start
lifecycle › getHttpServer returns an object
onRawData callback › can register callback without error
onDiag handler › can register handler without error
CodexCliAdapter start lifecycle
2 / 2
emits SessionStart and connected on start
feeds PTY data to output parser and emits activity
MonitorAdapter
18 / 18
capabilities › reports monitor capabilities correctly
handleCommand routing › rejects respond (no PTY)
handleCommand routing › rejects interrupt (no PTY)
handleCommand routing › rejects escape (no PTY)
handleCommand routing › rejects switch_mode (no PTY)
handleCommand routing › rejects select_option (no PTY)
handleCommand routing › rejects send_prompt (no PTY)
handleCommand routing › defers voice to bridge
handleCommand routing › defers query_usage to bridge
lifecycle › isAlive always returns true
lifecycle › getTtyPath returns undefined (no PTY)
lifecycle › getProjectName returns null
lifecycle › getHttpServer returns an object
lifecycle › writeInput is a no-op (no PTY)
lifecycle › attachTerminal is a no-op
lifecycle › onRawData is a no-op
start and event emission › emits connected event on start
start and event emission › exposes hook server for external wiring
ClaudeCodeAdapter start lifecycle
5 / 5
emits SessionStart and connected on start
uses claude as default command
feeds PTY data to output parser and emits activity
emits SessionEnd and disconnected on PTY exit
registers rawData callback without error
protocol-contract.test.ts
24
/
24
29ms
Protocol Contract — StateUpdateEvent
5 / 5
minimal state_update has required fields
full state_update has all optional fields as correct types
state_update serializes to valid JSON
state enum values are lowercase strings
promptType values match known set
Protocol Contract — UsageEvent
3 / 3
has all required numeric fields
rate limit fields are present when available
tokenStatus matches known values
Protocol Contract — SessionsListEvent
2 / 2
sessions have required fields
agentType is optional string
Protocol Contract — ButtonStateEvent
1 / 1
buttons have required fields
Protocol Contract — EncoderStateEvent
1 / 1
encoders have required fields
Protocol Contract — TimelineEventMsg
1 / 1
entry has required fields
Protocol Contract — ConnectionEvent
1 / 1
has required fields
Protocol Contract — DisplayStateEvent
1 / 1
has boolean displayOn
Protocol Contract — PluginCommand shapes
5 / 5
respond command has value
select_option has numeric index
navigate_option has direction
send_prompt has text
utility command has action
Protocol Contract — BridgeEvent discriminated union
2 / 2
all event types in union have type field
SERIAL_FORWARDED_EVENTS covers expected types
Protocol Contract — Backward Compatibility
2 / 2
state_update can be parsed with only required fields (old client)
usage_update required fields are sufficient for display
◆
Timeline Pipeline
PASS
이벤트 타임라인의 저장, 중복 제거, 세션 간 릴레이가 올바른가?
80 passed
0 failed
3 files
1.3s
timeline.test.ts
54
/
54
16ms
cleanDetailText
12 / 12
returns empty/falsy input unchanged
strips markdown bold
strips markdown headings
strips code fences
strips inline backticks
strips markdown links
strips blockquotes
strips list markers
collapses multiple blank lines
filters system JSON blobs (connectionId)
extracts error from JSON blob
compacts other JSON
cleanRawText
2 / 2
strips bold, headings, links, backticks
returns empty/falsy unchanged
cleanNopMarkers
3 / 3
removes NOP markers
collapses resulting blank lines
returns empty/falsy unchanged
extractSemanticCore
4 / 4
strips duration suffix for chat_end
keeps full text for chat_end without separator
keeps full text for non-chat_end types
trims whitespace
isRepetitiveEntry
11 / 11
detects exact duplicate chat_end entries
detects keyword-similar entries
returns -1 for non-matching entries
ignores entries outside window
only applies to chat_end, chat_start, and error types
dedupes repeated error entries within 1h window
matches chat_start entries
dedupes automated entries regardless of content
does not dedup automated vs non-automated
uses 8h window for automated entries
expires automated dedup after 8h
parseLogLine
22 / 22
returns null for null/undefined/non-object
returns null for empty message
returns null for model start/complete (suppressed)
parses memory/recall (legacy structured)
parses tool execution (legacy structured)
filters gateway/ws subsystem
filters infrastructure noise
filters channel infra reconnect noise
filters connection status JSON blobs
filters transient fetch timeouts
filters edit mismatch errors (agent retries)
filters failover cascade noise
parses genuine errors
parses error from message pattern (not level)
filters model/inference patterns (suppressed)
parses memory patterns from message text
parses tool patterns from message text
parses ISO timestamp correctly
falls back to Date.now() for invalid timestamp
truncates long tool raw to 500 chars
does NOT filter user-facing whatsapp tool messages (non-infra subsystem)
filters whatsapp noise only from channel infra subsystem
timeline-integration.test.ts
21
/
21
16ms
BridgeTimelineStore
10 / 10
adds and retrieves entries
filters history by timestamp
calls listeners on new entries
upsert updates existing entry with same ts+type
upsert adds new entry when no match exists
updateEntryStatus updates approval status
getLastEntry returns most recent of given type
getLastEntry returns null when type not found
removeListener stops notifications
enforces MAX_ENTRIES (200) with FIFO
deduplicateEntry pipeline
3 / 3
exact duplicate within 5s → skip
same type, different content → add
different type, same raw → add
Timeline → WS broadcast pipeline
4 / 4
store entries trigger broadcast via listener
upsert entries broadcast with upsert flag
exact duplicate within 5s is skipped by store
different content within 5s is NOT deduped
TimelineEntry types
4 / 4
common entry types have expected shape
entries with status field
entries with detail field
entries with automated flag
session-timeline-relay.test.ts
5
/
5
1.2s
SessionTimelineRelay
5 / 5
connects to sibling and relays timeline_event
relays timeline_history entries
removes subscription when session disappears
ignores daemon sessions
handles upsert timeline events
◉
Daemon & Infrastructure
PASS
데몬 싱글톤, 세션 레지스트리, 사용량 릴레이가 안정적인가?
60 passed
0 failed
4 files
1.3s
daemon-lifecycle.test.ts
19
/
19
51ms
daemon.json lifecycle
6 / 6
writeDaemonInfo creates daemon.json with correct content
readDaemonInfo returns info when PID is alive
readDaemonInfo returns null and removes file when PID is dead
readDaemonInfo returns null when file does not exist
removeDaemonInfo deletes daemon.json
removeDaemonInfo is safe when file already gone
session registry with real files
7 / 7
register creates sessions.json
register replaces existing entry with same id
deregister removes session by id
listActive prunes dead PIDs
multiple sessions coexist
findExistingDaemon returns daemon session
findExistingDaemon returns null when no daemon
findDaemonPort
4 / 4
returns port from daemon.json (priority 1)
falls back to sessions.json daemon entry
returns null when no daemon anywhere
daemon.json takes precedence over sessions.json
probeDaemonHealth
2 / 2
returns health JSON from running server
returns null for closed port
session-registry.test.ts
10
/
10
10ms
Session Registry Logic
10 / 10
pruneDeadSessions › keeps alive sessions
pruneDeadSessions › removes sessions with dead PIDs
pruneDeadSessions › keeps old sessions if PID is alive
pruneDeadSessions › handles mix of alive and dead sessions
port allocation logic › returns base port when no ports are used
port allocation logic › returns next port when base is taken
port allocation logic › skips used ports
port allocation logic › finds gaps in used ports
port allocation logic › throws when all ports are taken
atomic write › write-then-rename produces valid JSON
usage-relay.test.ts
12
/
12
625ms
Usage relay — HTTP (Tier 1)
3 / 3
fetches usage from sibling GET /usage
returns null usage when sibling has no data
rejects stale data (>5 min old)
Usage relay — WebSocket (Tier 2)
2 / 2
receives usage_update from sibling WS on connect
does not send usage_update when sibling has no data
HookServer GET /usage (relay source)
2 / 2
returns cached usage via onApiUsage getter
returns fresh usage after update
429 prevention — relay-first strategy
2 / 2
sibling with data prevents direct API call
multiple siblings — first with data wins
ApiUsageData shape
3 / 3
has all required fields
serializes to valid JSON and back
handles null optional fields
bridge-core.test.ts
19
/
19
584ms
BridgeCore Orchestration
19 / 19
buildStateEvent › builds state_update with IDLE state
buildStateEvent › includes cached ollamaStatus and gatewayAvailable
buildStateEvent › computes promptType for permission options
buildStateEvent › computes promptType yes_no_always for 3+ permission options
usage management › updateApiUsage caches and broadcasts
usage management › buildUsage includes API usage data
usage management › buildUsage works without API data
usage management › inferredBillingType propagates to StateMachine
sendInitialState › sends state_update, usage_update, connection, display_state on connect
sendInitialState › includes timeline_history when entries exist
state change broadcast › state_changed emits to WS clients when wired by caller
wireTimeline › timeline entries broadcast as timeline_event
hasClients guard › wsServer.getClientCount reflects connected clients
hasClients guard › external client count provider extends hasClients
voice assistant state › updateVoiceAssistantState caches and triggers state broadcast
voice assistant state › disabled voice assistant state is not included in event
session registry › registerSession writes to sessions.json
session registry › deregisterSession removes from sessions.json
broadcast coordination › broadcast sends to WS and SSE callback
◎
Integration Tests
PASS
전체 파이프라인이 실제 서버 환경에서 동작하는가?
29 passed
0 failed
2 files
942ms
server-integration.test.ts
15
/
15
640ms
Server Integration
15 / 15
SessionStart hook → IDLE state broadcast
full session lifecycle: SessionStart → UserPromptSubmit → Stop
PreToolUse → PostToolUse cycle broadcasts tool info
SessionEnd → DISCONNECTED
GET /health returns valid JSON
GET /usage returns data when getter is set
GET /usage returns null when no getter
GET /devices returns empty when no getter
POST to unknown route returns 404
hook endpoint responds immediately
SSE client receives state_update events
broadcasts to multiple WS clients
WS command from client triggers callback
rapid hook events do not corrupt state
token counts accumulate through hook events
tier3-integration.test.ts
14
/
14
302ms
mDNS crash recovery
3 / 3
invalidateMdnsInstance does not throw when no instance exists
EADDRNOTAVAIL pattern matches expected error messages
non-mDNS errors are NOT matched by recovery pattern
Display sleep/wake broadcast
5 / 5
DisplayMonitor initial state is ON
display_state event has correct shape
display_state broadcasts to WS clients
display_state broadcasts to multiple clients
SSE also receives display_state
Voice transcription endpoint
4 / 4
returns 503 when no voice manager is set
returns 400 for empty audio data
returns transcription from voice manager
returns 500 when voice manager throws
WsServer broadcast hooks (serial relay)
2 / 2
onBroadcast hook receives all broadcast events
broadcast hook errors do not crash server
▣
Stream Deck Plugin UI
PASS
플러그인의 연결 관리, 옵션 레이아웃, 렌더링이 정확한가?
107 passed
0 failed
5 files
1.8s
connection-manager.test.ts
15
/
15
11ms
ConnectionManager
15 / 15
starts disconnected
start() begins bridge connection
emits connected when bridge connects
emits disconnected when bridge disconnects
forwards state_update from bridge
send() delegates to bridge
send() drops command when not connected
reconnectBridgeTo delegates to bridge
getBridgePort returns bridge port
scanLatestPort setter delegates to bridge
switchToOpenClaw() › sends switch_agent command to bridge
switchToClaude() › sends switch_agent command to bridge
isGatewayAvailable() › returns false by default
isGatewayAvailable() › returns true when bridge reports gateway available
isGatewayAvailable() › returns false when bridge reports gateway unavailable
connection-integration.test.ts
6
/
6
1.7s
ConnectionManager Integration — Real WebSocket
6 / 6
client connects and receives broadcast events
multiple clients receive same broadcast
client sends command to server
handles rapid connect/disconnect without crashes
server detects client disconnect
bridge → gateway priority: second server takes over on disconnect
option-scenario.test.ts
15
/
15
8ms
6-option SELECT scenario
9 / 9
Quick Action slots: 3 options + MORE (6 options)
STOP slot is always preserved (getStopSlotOverride returns null)
3 options: all shown in Quick Action slots, 4th slot DIM
4 options: all 4 shown, no MORE
5 options: 3 options + MORE
E2 Focus panel renders with adaptive font
E3 List panel renders 4 visible rows with 14px font
E4 Detail panel shows word-wrapped label (12px, left-aligned)
PERMISSION without shortcut: diffButtons uses label first char
colorForOption — "don't ask again" / "allow all sessions"
6 / 6
returns blue for "Yes, and don't ask again for: tail:*"
returns blue for "Yes, and don’t ask again" (smart quote)
returns blue for "Yes, allow all sessions in project"
returns green for plain "Yes" (shortcut y)
returns green for "Apply" (shortcut a, but no "don't ask" pattern)
returns blue for "Always allow"
renderer-snapshots.test.ts
52
/
52
59ms
voice-renderer snapshots
12 / 12
renderVoiceReady
renderVoiceRecording frame 0
renderVoiceRecording frame 30 (>1min)
renderVoiceTranscribing frame 0
renderVoiceTranscribing frame 10 (different dot phase)
renderVoiceError with message
renderVoiceError default
renderVoiceDisabled
renderVoiceAssistantListening frame 0
renderVoiceAssistantProcessing frame 5
renderVoiceAssistantSpeaking frame 0
renderWideVoiceText returns correct panel count
utility-renderer snapshots
4 / 4
renderSetupUtility
renderUtilityGeneric with icon
renderUtilityGeneric text-only (no icon)
renderUtilityMedia
iterm-renderer snapshots
4 / 4
renderItermReady
renderItermPanel short name
renderItermPanel long name wrapping
renderItermDisabled
response-renderer snapshots
7 / 7
renderResponseIdle
renderResponseProcessing
renderResponseDisconnected
renderResponseDisabled
renderResponseSuggestion
renderResponseInteractive
renderSetupPrompt
option-renderer snapshots
7 / 7
renderContextPanel permission
renderContextPanel diff
renderFocusPanel with recommended option
renderListPanel 3 options
renderListPanel 6 options (scroll indicator)
renderDetailPanel
renderWideOptionList returns correct panel count
button-renderer snapshots
7 / 7
basic text button
button with subtitle
disabled button
loading button
long text that needs abbreviation
svgToDataUrl returns data URI
labelNeedsHaiku detects long labels
timeline-renderer snapshots
5 / 5
empty timeline
single entry
multiple entries fisheye
detail mode
with session status
qr-renderer snapshots
3 / 3
extractUrlLabel extracts host:port
qrPathData deterministic
renderQrButtonSvg
agent-logos snapshots
3 / 3
claude-code watermark
openclaw watermark
CLAUDE_LOGO_PATH is defined
text-utils-and-labels.test.ts
19
/
19
11ms
text-utils: CJK width measurement
4 / 4
Latin text is ~0.55em per char
Korean text is 1em per char (double-width)
mixed text measures correctly
isWide detects Hangul, CJK, fullwidth
text-utils: wrapTextByWidth
3 / 3
short text returns single line
long Latin text wraps to multiple lines
Korean text wraps at correct pixel width
button-renderer: abbreviation
10 / 10
short label renders without abbreviation indicator
"Yes, I trust this folder" fits with wrapping (no abbreviation needed)
"Yes, allow and don't ask again" wraps into 3 lines
very long label triggers abbreviation with ~ indicator
short permission labels like "No" render unchanged
labelNeedsHaiku returns false for short labels
labelNeedsHaiku returns false when heuristic abbreviation fits
labelNeedsHaiku returns true for very long unknown labels
labelNeedsHaiku returns false when Haiku cache has result
BUTTON_MAX_CHARS is reasonable
button-renderer: CJK labels
2 / 2
Korean label does not overflow (produces valid SVG)
mixed CJK/Latin label renders
▤
TUI Dashboard
PASS
터미널 대시보드의 상태 렌더링과 테라리움 애니메이션이 올바른가?
49 passed
0 failed
3 files
42ms
tui-dashboard.test.ts
8
/
8
8ms
TUI dashboard models
8 / 8
stores modelCatalog from state_update
renders OAuth catalog and Ollama models in wide layout
renders disconnected OAuth state
shows current session summary and control hints
renders agent list secondary line as model dash compact state
renders sibling models in session bridge mode and omits uptime label
renders help overlay when helpVisible is on
shows numbered session badges
tui-renderer-snapshots.test.ts
29
/
29
14ms
blockGauge snapshots
7 / 7
0% — all empty, green
50% — half filled, green
75% — yellow threshold
95% — red threshold
100% — all filled, red
clamps negative to 0
clamps above 100
resetTimeStr
5 / 5
returns empty for undefined
returns ↻now for past time
formats minutes
formats hours and minutes
formats days and hours
formatUptime
3 / 3
seconds
minutes
hours and minutes
formatTokens
3 / 3
below 1000 unchanged
1k-10k with decimal
10k+ rounded
activityDensityBar
3 / 3
empty timestamps — all dim
recent burst — bright right side
spread timestamps — distributed density
getLayout
3 / 3
wide for 120+ cols
standard for 80-119 cols
narrow for <80 cols
shouldShowTerrarium
3 / 3
true for adequate size
false for too narrow
false for too short
spinner
2 / 2
returns braille characters at different frames
spinner cycles through 10 frames
tui-terrarium-snapshots.test.ts
12
/
12
20ms
TUI terrarium snapshots
12 / 12
initTerrarium creates context with expected structure
setOctopi configures octopus instances
setCrayfish configures crayfish state
setJellyfish configures jellyfish instances
renderTerrariumFrame empty terrarium (small)
renderTerrariumFrame empty terrarium (large)
renderTerrariumFrame with idle octopus
renderTerrariumFrame with processing octopus
renderTerrariumFrame with routing crayfish
renderTerrariumFrame with sick crayfish
renderTerrariumFrame too small returns empty
updateTerrarium advances bubble positions
▥
Serial Protocol
PASS
ESP32와의 시리얼 바이트스트림이 정확히 프레이밍되는가?
22 passed
0 failed
1 files
16ms
esp32-serial-node.test.ts
22
/
22
16ms
SERIAL_FORWARDED_EVENTS
4 / 4
includes all display events
includes timeline events (unique to serial)
does NOT include plugin-only events
required serial events are present
ESP32 port detection patterns
5 / 5
matches CH340 ports (86 Box)
matches native USB JTAG ports (IPS 3.5", Round AMOLED)
matches Linux serial ports
excludes Bluetooth and WLAN
excludes non-ESP32 ports
handleSerialLine (source)
4 / 4
parses device_info message and updates deviceInfo
skips debug lines (non-JSON)
recovers from malformed JSON
ignores JSON without type field
prepareForSerial (source)
5 / 5
strips agentCapabilities from state_update
strips billingType and remoteUrl from state_update
preserves essential state_update fields
strips legacy usage fields from usage_update
passes through other events unchanged
WiFi provision protocol
1 / 1
provision message has all required fields
serial buffer management
3 / 3
line splitting handles multiple lines in one chunk
handles partial message across chunks
truncates buffer when exceeding 8KB limit
▦
Display Rendering
PASS
외부 디스플레이에 전송할 이미지 데이터가 올바른가?
3 passed
0 failed
1 files
3ms
pixoo-sprites.test.ts
3
/
3
3ms
getOctopusPaletteForSession
3 / 3
keeps the first additional session near the original terracotta tone
darkens later sessions while preserving channel ordering
clamps very large session indices to the darkest supported tone
▧
Hook Installation
PASS
Claude Code hook의 설치, 제거, 마이그레이션이 안전한가?
18 passed
0 failed
1 files
13ms
install.test.ts
18
/
18
13ms
Hook Installer
18 / 18
buildHookEntry › creates matcher-group format with AGENTDECK_PORT env var
buildHookEntry › includes fallback port 9120 in command
applyHooks › installs hooks to empty settings in matcher-group format
applyHooks › preserves non-AgentDeck hooks
applyHooks › replaces old flat-format hooks
applyHooks › replaces old matcher-format hooks
applyHooks › is idempotent — running twice produces same result
applyHooks › preserves existing non-hook settings
removeHooks › removes all AgentDeck hooks (new format)
removeHooks › removes old flat-format AgentDeck hooks
removeHooks › preserves non-AgentDeck hooks
removeHooks › handles empty settings gracefully
migrateHooks › migrates old hardcoded port to env var
migrateHooks › migrates flat format to matcher-group format
migrateHooks › skips already-migrated hooks (new format)
migrateHooks › skips non-AgentDeck hooks
migrateHooks › migrates multiple events at once
migrateHooks › migrates hardcoded port inside matcher-group
○
Other Tests
Uncategorized test files
22 passed
0 failed
3 files
bridge-core-sessions.test.ts
2
/
2
77ms
BridgeCore sessions_list
2 / 2
broadcastSessionsList enriches sessions before broadcast
sendInitialState sends enriched sessions_list to the connecting client
session-aggregator.test.ts
4
/
4
13ms
session-aggregator
4 / 4
uses ownState for the current session without fetching /health
fetches sibling /health and merges state and modelName
falls back to base session info when sibling /health fails
buildEnrichedSessionsList excludes daemon and own session before enrichment
format-utils.test.ts
16
/
16
6ms
adjustUsagePercent
9 / 9
returns undefined when percent is null
returns undefined when percent is undefined
returns percent unchanged when resetsAt is null
returns percent unchanged when resetsAt is undefined
returns percent unchanged when resetsAt is in the future
returns 0 when resetsAt is in the past (window expired)
returns 0 when resetsAt equals now (edge case)
handles invalid date string gracefully (returns percent)
handles empty string resetsAt (returns percent)
formatResetTime
7 / 7
returns "now" when resetsAt is in the past
returns undefined for null input
returns minutes-only for < 1h remaining
returns hours and minutes for < 24h remaining
returns days and hours for >= 24h remaining
omits minutes when exactly on the hour
passes through pre-formatted strings (no T)
▣
Android
PASS
JUnit + Robolectric · 4 files · 82 tests
net.ProtocolTest
32
/
32
5.8s
parse state_update with all permission modes
parse connection connected with sessionId
parse sessions_list
PluginCommands selectOption generates valid JSON
parse button_state
PluginCommands utility with and without value
parse invalid json returns null
parse timeline_history
parse timeline_event upsert
parse state_update with processing state and tool info
PluginCommands respond escapes special characters
PluginCommands respond generates valid JSON
parse state_update with permission options
parse connection disconnected
parse missing type returns null
parse deck_slot_map
parse usage_update with extra usage
parse state_update with ollama status
PluginCommands navigateOption
parse state_update with agent capabilities
parse unknown type returns null
parse user_prompt
BridgeTimelineEntry converts to TimelineEntry
parse state_update with model catalog
parse state_update ignores unknown fields
parse state_update with idle state
parse display_state sleep and wake
parse voice_state
parse encoder_state
PluginCommands interrupt and escape
parse usage_update with rate limits
parse timeline_event
state.SessionMetricsTest
8
/
8
8ms
reset clears all metrics
onMessageReceived updates lastMessageAt
onConnected sets connectedSince
onDisconnected clears connectedSince
clean reconnect after disconnect does not increment
initial state has no connection
reconnect increments reconnectCount when still connected
onMessageReceived increments count
state.TimelineStoreTest
22
/
22
19ms
addEntry allows same type+summary after 5s
clear empties the store
upsertEntry updates existing entry within 1s tolerance
groupConsecutive empty list returns empty
upsertEntry adds new entry if no match
addEntry stores entry
groupConsecutive groups same summary within 60s
updateLastOfType modifies the last matching entry
addEntries merges and deduplicates
upsertEntry preserves existing detail if new detail is null
groupConsecutive splits tool_request after 10s gap
addEntries sorts by timestamp
groupConsecutive splits different types
addEntry allows different type within 5s
groupConsecutive single entry
addEntry deduplicates within 5s window
addEntry allows different summary within 5s
groupConsecutive groups chat_end by type only within 60s
addEntry caps at MAX_ENTRIES
groupConsecutive requires same summary for other types
updateLastOfType no-op if type not found
groupConsecutive groups tool_request within 10s
util.TimeFormatUtilsTest
20
/
20
19ms
gaugeBar clamps below 0
formatCount thousands show K
formatBytes kilobytes
formatDurationCompact seconds
formatDurationCompact exact minutes no seconds
formatDurationCompact sub-second
formatCount int overload works
gaugeBar clamps above 100
formatBytes small values
formatCount small numbers unchanged
gaugeBar 50 percent is half filled
gaugeBar 0 percent is all empty
formatBytes megabytes
formatResetTime returns original on parse failure
gaugeBar custom width
formatBytes gigabytes
formatUptime zero returns 0 colon 00
formatCount millions show M
formatDurationCompact minutes
gaugeBar 100 percent is all filled
▩
Robot Framework
PASS
ESP32 Hardware Tests · 1 suites · 4 scenarios · 7 tests · 4 boards
| Board | Build | Flash+Boot | Boot Time | FW Size | Boot Heap | Latency |
|---|---|---|---|---|---|---|
| 86Box | 20.2s | — | — | — | — | — |
| IPS 3.5" | 16.2s | — | — | — | — | — |
| Round | 16.2s | — | — | — | — | — |
| TC001 | 16.1s | — | — | — | — | — |
01_build.robot
7
/
7
Build And Verify
4 boards
4 / 4
Given the "${board}" firmware is built
Then the firmware binary should exist for "${board}"
And the firmware size should be sane for "${board}"
And the partitions binary should exist for "${board}"
✓ 86Box✓ IPS 3.5"✓ Round✓ TC001
Box 86 Build And Verify
IPS 3.5 Build And Verify
Round AMOLED Build And Verify
Ulanzi TC001 Build And Verify
Boot Test Environment Builds Successfully
Source Files Are Present
PlatformIO Configuration Parses Cleanly
▦
Scenario Coverage
User scenario mapping against actual test results
| Score | Scenario | Priority | Unit | Integ. | Platform | E2E |
|---|---|---|---|---|---|---|
| 6/8 |
Session Lifecycle
Start session -> processing -> idle -> stop. Core state machine transitions that drive all UI.
|
critical | 4/4 | 1/2 | 1/2 | — |
| 3/5 |
Permission Flow
Agent requests permission -> user sees options -> approves/denies -> state transitions back.
|
critical | 2/2 | 0/2 | 1/1 | — |
| 7/7 |
Multi-agent Monitoring
Daemon hub orchestrates multiple session bridges, unified dashboard for all agents.
|
high | 3/3 | 4/4 | — | — |
| 7/7 |
Device Connection
mDNS discovery -> auth token validation -> WebSocket/serial state sync with dashboard clients.
|
high | 3/3 | 3/3 | 1/1 | — |
| 4/5 |
Timeline Aggregation
Events from multiple sessions -> daemon relay -> dedup -> unified timeline for all clients.
|
high | 2/2 | 1/1 | 1/2 | — |
| 1/1 |
Voice Recording & Transcription
Audio capture -> whisper transcription -> text delivery to Claude PTY or clipboard.
|
medium | — | 1/1 | — | — |
| 3/3 |
Encoder & Button Interaction
Dial rotate -> option scroll -> push select. Button press -> action trigger. Label rendering.
|
medium | 3/3 | — | — | — |
| 4/5 |
Usage Tracking & Rate Limits
API usage fetch -> rate limit gauge -> sibling relay (429 prevention) -> dashboard display.
|
medium | 0/1 | 2/2 | 2/2 | — |
| 2/2 |
Daemon Singleton Guard
PID check -> port probe -> prevent double daemon. Auto-fallback on port conflict.
|
medium | 1/1 | 1/1 | — | — |
| 1/1 |
Hook Installation & Migration
Install Claude Code hooks -> migrate v2.0 flat format to v2.1 matcher groups -> port injection.
|
medium | 1/1 | — | — | — |
▤
Coverage
v8 provider · 12,939 lines tracked
Lines ≥17%: 28.6%
Functions ≥15%: 24.8%
Branches ≥14%: 23.8%
Statements ≥16%: 27.4%
bridge
Lines
Stmts
Funcs
Branch
2708/8137 lines covered
plugin
Lines
Stmts
Funcs
Branch
747/4390 lines covered
shared
Lines
Stmts
Funcs
Branch
191/328 lines covered
hooks
Lines
Stmts
Funcs
Branch
54/84 lines covered
| File | Stmts | Branch | Funcs | Lines | |
|---|---|---|---|---|---|
| bridge/src/adb-reverse.ts | 0% | 0% | 0% | 0% | |
| bridge/src/check-deps.ts | 0% | 0% | 0% | 0% | |
| bridge/src/cli.ts | 0% | 0% | 0% | 0% | |
| bridge/src/daemon-server.ts | 0% | 0% | 0% | 0% | |
| bridge/src/daemon.ts | 0% | 0% | 0% | 0% | |
| bridge/src/diag-analyzer.ts | 0% | 0% | 0% | 0% | |
| bridge/src/event-journal.ts | 0% | 0% | 0% | 0% | |
| bridge/src/index.ts | 0% | 0% | 0% | 0% | |
| bridge/src/log-stream.ts | 0% | 0% | 0% | 0% | |
| bridge/src/pty-ringbuffer.ts | 0% | 0% | 0% | 0% | |
| bridge/src/terminal-status.ts | 0% | 0% | 0% | 0% | |
| bridge/src/tts.ts | 0% | 0% | 0% | 0% | |
| bridge/src/types.ts | 0% | 0% | 0% | 0% | |
| bridge/src/utility-proxy.ts | 0% | 0% | 0% | 0% | |
| bridge/src/version-check.ts | 0% | 0% | 0% | 0% | |
| bridge/src/voice-assistant.ts | 0% | 0% | 0% | 0% | |
| bridge/src/voice.ts | 0% | 0% | 0% | 0% | |
| bridge/src/wake-word.ts | 0% | 0% | 0% | 0% | |
| bridge/src/whisper-server-manager.ts | 0% | 0% | 0% | 0% | |
| bridge/src/wifi-config.ts | 0% | 0% | 0% | 0% | |
| bridge/src/modules/adb-module.ts | 0% | 0% | 0% | 0% | |
| bridge/src/modules/index.ts | 0% | 0% | 0% | 0% | |
| bridge/src/modules/mdns-module.ts | 0% | 100% | 0% | 0% | |
| bridge/src/modules/pixoo-module.ts | 0% | 0% | 0% | 0% | |
| bridge/src/modules/serial-module.ts | 0% | 0% | 0% | 0% | |
| bridge/src/modules/types.ts | 0% | 0% | 0% | 0% | |
| bridge/src/pixoo/pixoo-font.ts | 0% | 0% | 0% | 0% | |
| bridge/src/pixoo/pixoo-settings.ts | 0% | 0% | 0% | 0% | |
| bridge/src/tui/screen.ts | 0% | 0% | 0% | 0% | |
| bridge/src/types/picovoice.d.ts | 0% | 0% | 0% | 0% | |
| bridge/src/mdns.ts | 4% | 4% | 11% | 5% | |
| bridge/src/ollama-probe.ts | 5% | 0% | 0% | 5% | |
| bridge/src/tui/dashboard.ts | 6% | 12% | 3% | 6% | |
| bridge/src/pixoo/pixoo-camera.ts | 6% | 0% | 0% | 7% | |
| bridge/src/pixoo/pixoo-renderer.ts | 6% | 0% | 0% | 8% | |
| bridge/src/timeline-summarizer.ts | 7% | 0% | 0% | 8% | |
| bridge/src/pixoo/pixoo-sprites.ts | 9% | 0% | 9% | 10% | |
| bridge/src/pixoo/pixoo-client.ts | 11% | 0% | 0% | 11% | |
| bridge/src/pixoo/pixoo-bridge.ts | 12% | 0% | 0% | 14% | |
| bridge/src/esp32-serial.ts | 13% | 12% | 5% | 14% | |
| bridge/src/logger.ts | 18% | 10% | 17% | 15% | |
| bridge/src/gateway-probe.ts | 12% | 0% | 0% | 16% | |
| bridge/src/display-monitor.ts | 20% | 9% | 18% | 22% | |
| bridge/src/usage-api.ts | 26% | 11% | 55% | 29% | |
| bridge/src/bridge-core.ts | 40% | 38% | 29% | 43% | |
| bridge/src/usage-tracker.ts | 44% | 55% | 23% | 44% | |
| bridge/src/model-catalog.ts | 42% | 20% | 40% | 48% | |
| bridge/src/pty-manager.ts | 54% | 31% | 53% | 55% | |
| bridge/src/hook-server.ts | 53% | 29% | 53% | 55% | |
| bridge/src/ws-server.ts | 61% | 31% | 61% | 60% | |
| bridge/src/auth.ts | 59% | 37% | 75% | 63% | |
| bridge/src/adapters/openclaw.ts | 63% | 44% | 56% | 65% | |
| bridge/src/tui/renderer.ts | 67% | 51% | 92% | 67% | |
| bridge/src/tui/terrarium.ts | 70% | 56% | 76% | 72% | |
| bridge/src/adapters/claude-code.ts | 73% | 40% | 50% | 73% | |
| bridge/src/tui/ansi.ts | 75% | 49% | 83% | 75% | |
| bridge/src/state-machine.ts | 76% | 58% | 81% | 76% | |
| bridge/src/session-registry.ts | 75% | 58% | 74% | 79% | |
| bridge/src/adapters/monitor.ts | 81% | 60% | 87% | 81% | |
| bridge/src/timeline-store.ts | 83% | 71% | 100% | 82% | |
| bridge/src/adapters/pty-adapter.ts | 82% | 81% | 65% | 82% | |
| bridge/src/session-timeline-relay.ts | 78% | 58% | 67% | 83% | |
| bridge/src/adapters/codex-cli.ts | 83% | 100% | 75% | 83% | |
| bridge/src/adapters/index.ts | 83% | 80% | 100% | 83% | |
| bridge/src/output-parser.ts | 89% | 83% | 100% | 93% | |
| bridge/src/codex-output-parser.ts | 90% | 83% | 92% | 94% | |
| bridge/src/session-aggregator.ts | 100% | 100% | 100% | 100% | |
| bridge/src/usage-event.ts | 100% | 100% | 100% | 100% | |
| bridge/src/tui/gauge.ts | 98% | 96% | 100% | 100% | |
| hooks/src/install.ts | 62% | 71% | 73% | 64% | |
| plugin/src/agent-link.ts | 0% | 0% | 0% | 0% | |
| plugin/src/bridge-client.ts | 0% | 0% | 0% | 0% | |
| plugin/src/encoder-registry.ts | 0% | 0% | 0% | 0% | |
| plugin/src/encoder-takeover.ts | 0% | 0% | 0% | 0% | |
| plugin/src/expanded-actions.ts | 0% | 0% | 0% | 0% | |
| plugin/src/label-summarizer.ts | 0% | 0% | 0% | 0% | |
| plugin/src/log.ts | 0% | 0% | 0% | 0% | |
| plugin/src/plugin.ts | 0% | 0% | 0% | 0% | |
| plugin/src/project-picker.ts | 0% | 0% | 0% | 0% | |
| plugin/src/project-scanner.ts | 0% | 0% | 0% | 0% | |
| plugin/src/timeline-store.ts | 0% | 0% | 0% | 0% | |
| plugin/src/voice-local.ts | 0% | 0% | 0% | 0% | |
| plugin/src/actions/iterm-dial.ts | 0% | 0% | 0% | 0% | |
| plugin/src/actions/mode-button.ts | 0% | 0% | 0% | 0% | |
| plugin/src/actions/option-dial.ts | 0% | 0% | 0% | 0% | |
| plugin/src/actions/response-button.ts | 0% | 0% | 0% | 0% | |
| plugin/src/actions/session-button.ts | 0% | 0% | 0% | 0% | |
| plugin/src/actions/stop-button.ts | 0% | 0% | 0% | 0% | |
| plugin/src/actions/usage-button.ts | 0% | 0% | 0% | 0% | |
| plugin/src/actions/utility-dial.ts | 0% | 0% | 0% | 0% | |
| plugin/src/actions/voice-dial.ts | 0% | 0% | 0% | 0% | |
| plugin/src/utility-modes/brightness.ts | 0% | 0% | 0% | 0% | |
| plugin/src/utility-modes/darkmode.ts | 0% | 0% | 0% | 0% | |
| plugin/src/utility-modes/diag.ts | 0% | 0% | 0% | 0% | |
| plugin/src/utility-modes/index.ts | 0% | 0% | 0% | 0% | |
| plugin/src/utility-modes/macos.ts | 0% | 0% | 0% | 0% | |
| plugin/src/utility-modes/media.ts | 0% | 0% | 0% | 0% | |
| plugin/src/utility-modes/mic.ts | 0% | 0% | 0% | 0% | |
| plugin/src/utility-modes/timer.ts | 0% | 0% | 0% | 0% | |
| plugin/src/utility-modes/types.ts | 0% | 0% | 0% | 0% | |
| plugin/src/utility-modes/volume.ts | 0% | 0% | 0% | 0% | |
| plugin/src/layout-manager.ts | 65% | 64% | 62% | 66% | |
| plugin/src/renderers/timeline-renderer.ts | 62% | 37% | 79% | 67% | |
| plugin/src/renderers/button-renderer.ts | 82% | 72% | 100% | 82% | |
| plugin/src/renderers/response-renderer.ts | 85% | 40% | 92% | 87% | |
| plugin/src/renderers/voice-renderer.ts | 88% | 57% | 87% | 90% | |
| plugin/src/renderers/option-renderer.ts | 92% | 69% | 100% | 93% | |
| plugin/src/connection-manager.ts | 90% | 70% | 94% | 94% | |
| plugin/src/renderers/iterm-renderer.ts | 95% | 72% | 100% | 97% | |
| plugin/src/renderers/utility-renderer.ts | 98% | 57% | 100% | 98% | |
| plugin/src/renderers/qr-renderer.ts | 98% | 90% | 80% | 98% | |
| plugin/src/renderers/agent-logos.ts | 100% | 100% | 100% | 100% | |
| plugin/src/renderers/text-utils.ts | 92% | 81% | 100% | 100% | |
| shared/src/adapter.ts | 0% | 100% | 100% | 0% | |
| shared/src/index.ts | 0% | 0% | 0% | 0% | |
| shared/src/net-utils.ts | 0% | 0% | 0% | 0% | |
| shared/src/timeline-summarizer.ts | 0% | 0% | 0% | 0% | |
| shared/src/voice-paths.ts | 0% | 0% | 0% | 0% | |
| shared/src/format-utils.ts | 39% | 39% | 29% | 39% | |
| shared/src/timeline.ts | 70% | 68% | 93% | 76% | |
| shared/src/protocol.ts | 100% | 100% | 100% | 100% | |
| shared/src/states.ts | 100% | 100% | 100% | 100% |