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>
- Guard browser.unref() call in studio command — mock spawn objects
in test environment don't have unref, causing TypeError
- Fix cli-integration test isolation: clean up harbor book and session
file after interact tests so book list/status tests find empty state
- Change interact --book test to use /books intent instead of
explain status which requires LLM connectivity
- Update v1 sections to v1.2 in README.md, README.en.md, README.ja.md
covering: shared interaction runtime, Ink TUI dashboard, Studio
assistant panel, conversational book creation, entity rename,
inkos interact endpoint, thinking model temperature clamp
- Add v1.2.0 changelog with full feature list, improvements, and fixes
- Bump all package versions from 1.1.1 to 1.2.0
The agent endpoint was returning `session.messages.at(-1)?.content` as
the response, but the last session message is the user's input — the
actual assistant reply is in `result.responseText`. This caused the
ChatBar to echo the user's own message back as the "assistant response".
The agent endpoint returns the response text in `data.response` and
the full session history in `data.session.messages`. The ChatBar was
replacing its entire message list with session history on every call,
but session messages don't include the latest assistant response —
so the reply was silently dropped every time.
Fix: always append `data.response` as an assistant message instead
of replacing with session history. Session restore only happens on
first panel open when ChatBar is empty.
The fadeIn animation's transform creates a stacking context on each
book card, trapping the dropdown's z-50 inside. When the BookMenu
opens, the parent card now gets z-50 to sit above sibling cards,
allowing the full dropdown (including delete) to render unclipped.
The paper-sheet hover effect used `transform: translateY(-4px)` which
creates a new stacking context, trapping the BookMenu dropdown's z-50
inside the card. Subsequent cards in DOM order rendered above it.
Remove transform and transform-style from paper-sheet, keep the
shadow-based hover effect. Dropdown menus now render above all cards.
The BookMenu dropdown (three-dot menu) was clipped by the parent
card's overflow-hidden. The progress bar already has its own
overflow-hidden, so the card-level clip was unnecessary.
54 commits from dev branch introducing:
- Shared interaction runtime (NL router, session management, edit controller)
- Ink-based TUI dashboard with themed animations and i18n
- Studio ChatBar shared session integration and book draft conversation flow
- CLI interact command for OpenClaw/agent interop
- Studio server endpoints for interaction session and agent routing
Core 585/585, Studio 70/70 tests pass.
- Fix SSE effect stale `loading` closure: use loadingRef to read
current loading state inside the SSE effect instead of capturing
a stale value from the dependency array
- Deduplicate handleSubmit/handleQuickCommand: extract shared
executeCommand() function, eliminating ~80 lines of copy-pasted
logic that would inevitably diverge
- Fix curly quote encoding issue causing esbuild parse failures
- Add semantic status colors and role colors to theme palette
- Improve conversation rows with distinct icons per role
- Upgrade ExecutionBadge with per-status icons
- Add visual separators between dashboard sections
- Improve composer with separated status/helper text
- Enhance startup animation with project info reveal
- Move hardcoded i18n strings into TuiCopy stageLabels
- Fix non-null assertions in effects.ts