Keep our try-catch for empty-content errors while incorporating
upstream's chat function structure.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The bare catch block silently swallowed all exceptions including
network errors, auth failures, and timeouts. Now only catches
errors related to empty content (thinking model edge case) and
re-throws everything else so users see real error messages.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Tables were overflowing the visible area because the width calculation
didn't account for the 2-char prefix (◆) in addition to paddingX.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Update assertions for removed statusSecondaryLine, composer
status/helper lines, and layout changes (minWidth prefix).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Other terminals (Ghostty, kitty, etc.) have their own theme
independent of macOS appearance. Only fall back to AppleInterfaceStyle
for Terminal.app. All other terminals default to dark when COLORFGBG
is not set.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Terminal.app doesn't set $COLORFGBG. Fall back to macOS
AppleInterfaceStyle via execSync. All colors remain hex (no
16-color ANSI names that caused Terminal.app crashes).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Detect terminal background via $COLORFGBG (env var only, no child
process). Use darker hex colors for light backgrounds so text is
readable. No 16-color ANSI names — all hex like the main branch.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Remove all Terminal.app color/theme workarounds (16-color palette,
execSync theme detection, light/dark palettes). Use the same static
hex colors as the main branch. Only keep isAppleTerminal for the
markdown rendering fallback.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Shift+Arrow is not reliably detected by Ink's useInput. Use raw
escape sequences for Page Up (\x1b[5~) and Page Down (\x1b[6~)
which work across Terminal.app, iTerm2, and other terminals.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The stdout write buffering, alternate screen, and maxFps throttle we
added were not fixing the Terminal.app crashes — the main branch
doesn't crash because it uses a simpler rendering path.
Root cause: the complex Box nesting (<Box minWidth={2}> + flexShrink)
and renderMarkdown processing generate more ANSI cursor-positioning
codes per frame, which triggers Terminal.app's CoreGraphics bug.
Fix: on Terminal.app, ConversationRow uses the same simple layout as
the main branch (<Box><Text>prefix</Text><Text>{content}</Text></Box>)
without renderMarkdown. Enhanced rendering (minWidth layout, markdown)
is only used on terminals that can handle it (iTerm2, etc.).
Also adds Shift+Up/Down scrolling for conversation history, and
reverts the Terminal.app light palette change.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add scrollOffset to the conversation area so users can scroll back
through message history. Shift+Up scrolls up 3 messages, Shift+Down
scrolls back down. Scroll resets to bottom on new message submit.
Also reverts the Terminal.app light palette color change (keep
original yellow/blue).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The alternate screen disables terminal scrollback. Only enable it
on Terminal.app where it's needed to prevent IME XPC crashes.
Other terminals (iTerm2, etc.) keep native scrolling.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
ANSI "blue" renders as bright purple on light backgrounds.
Use "black" for AI reply text and "gray" for accents on
Terminal.app light mode.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Use marked-terminal for layout (tables, lists, indentation) on all
terminals, then strip ANSI escape codes for Terminal.app instead of
bypassing marked-terminal entirely. Box-drawing characters (┌─┐│└┘)
are plain Unicode and survive the strip.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
resetImportReplayTruthFiles intentionally skipped pending_hooks.md,
but the architect's foundation output includes hooks from the full
imported text (e.g. chapter 60 hooks). When replay starts from
chapter 1, these future hooks leak into the context.
Reset pending_hooks.md to an empty table using the existing
buildImportReplayHooksSeed helper. The replay will incrementally
rebuild hooks from each chapter's analysis.
Fixes the pre-existing CI failure: "does not leak imported future
state into early replay chapters".
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Terminal.app crashes in IMKInputSession when the IME's NSRemoteView
XPC connection conflicts with Ink's screen redraws. Enter the
alternate screen buffer (DEC 1049) before rendering — this isolates
Ink to a separate terminal view so the IME interacts with the
primary screen without corruption. Same approach as Claude Code's
<AlternateScreen> component.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The normal depth profile hardcoded maxTokens: 240, which was always
passed to the chat LLM call via getChatRequestOptions. This defeated
the "only pass maxTokens when /depth is explicitly set" fix in
project-tools.ts — thinking models like kimi-k2.5 were still
truncated at 240 tokens on the default TUI path.
Make maxTokens optional in ChatDepthProfile. Only light/deep depths
set it; normal leaves it undefined so the model decides its own
output length.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Terminal.app crashes (SIGSEGV in CGFontStrikeGetValue) when it sees
partial CJK UTF-8 bytes across multiple write() calls — the font
glyph cache gets corrupted with text bytes in pointer fields.
Buffer all stdout.write() calls within a single event-loop tick and
flush them as one atomic write via process.nextTick. This mirrors
Claude Code's single-write buffering approach (writeDiffToTerminal)
and ensures Terminal.app always receives complete frames.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Terminal.app's CoreGraphics/ColorSync pipeline has a memory corruption
bug where UTF-8 text bytes (CJK, em dash 0xe28094) end up in color
space pointers, causing SIGSEGV. Even basic 16-color ANSI codes
trigger it. Set FORCE_COLOR=0 to make chalk emit zero escape codes.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Terminal.app's CoreGraphics crashes with 24-bit RGB escape sequences
(\x1b[38;2;r;g;b;m) combined with CJK text. Use basic ANSI color
names (cyan, yellow, white, etc.) which map to safe 16-color codes.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Terminal.app's CoreGraphics pipeline crashes (SIGSEGV in
color_space_icc_md5_equal) when ANSI escape codes + CJK text
are rendered together — UTF-8 bytes get misinterpreted as
color space pointers.
On Terminal.app, renderMarkdown now uses a plain-text fallback
that strips markdown syntax without emitting any ANSI codes.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Detect Terminal.app via $TERM_PROGRAM and lower Ink render FPS from
30 to 15 to reduce ANSI redraw pressure during CJK IME composition
- Use Intl.Segmenter for grapheme-aware backspace handling (correctly
deletes CJK characters, emoji with ZWJ, surrogate pairs)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Terminal.app does not set $COLORFGBG. Fall back to macOS
AppleInterfaceStyle via `defaults read` to correctly detect
light/dark mode on Terminal.app.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Detect light/dark terminal via $COLORFGBG env var (same method as
Claude Code's initial seed). Provide distinct dark and light palettes
so all UI colors remain readable on both backgrounds.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Bold is invisible with RGB colors in Ink; use empty style so headers
inherit the parent WARM_REPLY color like body cells.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace columnGap/trailing-space approach with <Box minWidth={2}> for
prefix icons (◆ │ ·), matching Claude Code's layout pattern.
Also fix user message body color to ROLE_USER (light blue).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Keep **markers as plain text through reflowText to prevent ANSI code splitting
- Strip \x1b[0m full-reset codes that override Ink's <Text color> context
- Replace * bullets with · in post-processing
- Convert **text** to ANSI bold after reflow is complete
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- WARM_ACCENT: #c88a56 → #d4a070 (warmer highlight for ◆ and accents)
- WARM_REPLY: #f0e6d8 → #c0a480 (warm sand tone for AI response body)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Use marked + marked-terminal to render markdown in assistant replies,
supporting bold, italic, inline code, tables, and lists in the terminal.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Revert the isSimpleGreeting LLM bypass in runtime.ts chat handler to
preserve existing behavior where tools.chat is always called. Adapt the
nl-router test to pass hasFailed: true when testing explain_failure
routing, matching the new guard condition.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Remove hardcoded maxTokens cap (240/700) from chat and developBookDraft
tools so thinking models like kimi-k2.5 are not truncated mid-response
- Add resolveProjectRoot() to launchTui so running `inkos` from inside a
book directory auto-resolves to the correct project root
- Pass projectRoot through loadConfig in createInteractionTools instead of
falling back to process.cwd()
- Skip LLM call for simple greetings (你好/hi/hello) that already have
built-in fallback responses, preventing empty-response errors
- Add try-catch around chat tool LLM call to gracefully degrade when
thinking models return empty content
- Guard "为什么/why" routing to explain_failure behind hasFailed context
flag so normal questions containing "为什么" route to chat instead
- Use columnGap={1} for conversation row spacing in Ink v7
- Remove statusSecondaryLine and mode label from status bar
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>