feat: 工具层及 mcp 大重构 (#252)

* feat: 第一版大重构

* fix: 修复类型问题

* chore: 更新版本到 1.3.2

* Add brave as alternative WebSearchTool

* fix: 修正顺序

* fix: 修复对穷鬼模式的 auto dream 和 session memory 越过

* feat: 穷鬼模式去除 session-summary

* feat: 创建 builtin-tools 包,搬运所有工具实现

将 src/tools/ 下的全部 60 个工具目录迁移至 packages/builtin-tools/src/tools/,
内部导入路径已更新为 src/ alias 模式。

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* refactor: 更新 src/ 中所有工具引用至 builtin-tools 包,删除 src/tools/

- src/tools.ts 及 178 个 src/ 文件的 import 路径从 ./tools/ 改为 builtin-tools/tools/
- 删除 src/tools/ 整个目录(已迁移至 packages/builtin-tools/)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* chore: 添加 builtin-tools 路径别名至 tsconfig,更新 bun.lock

- tsconfig.json 新增 builtin-tools/* 和 builtin-tools 路径映射
- 新增 packages/builtin-tools/src 至 include

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* refactor: 为 builtin-tools、mcp-client、agent-tools 添加 @claude-code-best 作用域前缀

所有包名及 import 路径统一添加 @claude-code-best/ 前缀:
- builtin-tools → @claude-code-best/builtin-tools
- mcp-client → @claude-code-best/mcp-client
- agent-tools → @claude-code-best/agent-tools

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: 修复 node 环境没有 bun 的问题

---------

Co-authored-by: Eric-Guo <eric.guocz@gmail.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
claude-code-best
2026-04-13 09:52:05 +08:00
committed by GitHub
parent bbb8b613a9
commit 2fb1c9dcd8
559 changed files with 9346 additions and 1837 deletions

View File

@@ -0,0 +1,301 @@
# Chapter 11: Core Architecture
This chapter covers the internal rendering pipeline, DOM model, and screen buffer system. This is advanced material -- most users only need the component and hooks APIs.
## Rendering Pipeline
```
React Component Tree
↓ (React reconciler)
Ink DOM Tree (virtual terminal DOM)
↓ (Yoga layout)
Positioned DOM Tree (computed x, y, width, height)
↓ (renderNodeToOutput)
Output Buffer (styled characters)
↓ (renderer → Screen)
Screen Buffer (Int32Array of cells)
↓ (diffEach)
ANSI Diff Patches (minimal escape sequences)
↓ (writeDiffToTerminal)
Terminal stdout
```
### Frame Lifecycle
Each render cycle (`onRender`) follows these phases:
1. **React Commit** -- React reconciles the virtual tree; host config updates Ink DOM
2. **Yoga Layout** -- All dirty nodes have their styles applied and layout computed
3. **Renderer** -- Creates Output buffer, calls `renderNodeToOutput` for the full tree
4. **Screen Diff** -- New frame is compared against previous frame cell-by-cell
5. **Optimize** -- Patches are merged and ordered for minimal cursor movement
6. **Write** -- ANSI escape sequences are written to stdout
### Frame Timing
```ts
const FRAME_INTERVAL_MS = 16 // ~60fps cap
```
Renders are throttled. Multiple state updates in one frame are batched.
### Double Buffering
Two frames are maintained:
- **`frontFrame`** -- The currently displayed frame
- **`backFrame`** -- The frame being rendered
After rendering, they are swapped. This prevents partial updates from being visible.
## Ink DOM
### Node Types
```ts
type ElementNames =
| 'ink-root' // Root container
| 'ink-box' // Box component
| 'ink-text' // Text component
| 'ink-virtual-text' // Intermediate text wrapper
| 'ink-link' // Link component
| 'ink-raw-ansi' // Raw ANSI content
```
### DOMElement
```ts
type DOMElement = {
nodeName: ElementNames
attributes: Record<string, unknown>
childNodes: DOMNode[] // DOMElement | TextNode
yogaNode?: LayoutNode // Yoga layout node
textStyles?: TextStyles // Inherited text styles
// Scroll state
scrollTop?: number
scrollHeight?: number
scrollViewportHeight?: number
scrollViewportTop?: number
stickyScroll?: boolean
pendingScrollDelta?: number
scrollAnchor?: { el: DOMElement; offset: number }
// Dirty tracking
dirty: boolean
// Event handlers (stored separately)
onClick?: (event: ClickEvent) => void
onFocus?: (event: FocusEvent) => void
onBlur?: (event: FocusEvent) => void
onKeyDown?: (event: KeyboardEvent) => void
onMouseEnter?: () => void
onMouseLeave?: () => void
}
```
### TextNode
```ts
type TextNode = {
nodeName: '#text'
nodeValue: string
yogaNode?: LayoutNode
}
```
### DOM Operations
```ts
// Node creation
createNode(nodeName: string): DOMElement
createTextNode(text: string): TextNode
// Tree manipulation
appendChildNode(parent: DOMElement, child: DOMNode): void
insertBeforeNode(parent: DOMElement, child: DOMNode, before: DOMNode): void
removeChildNode(parent: DOMElement, child: DOMNode): void
// Attribute manipulation
setAttribute(node: DOMElement, key: string, value: unknown): void
setStyle(node: DOMElement, style: Styles): void
setTextStyles(node: DOMElement, styles: TextStyles): void
// Dirty tracking
markDirty(node: DOMElement): void
scheduleRenderFrom(node: DOMElement): void
```
## Screen Buffer
### Cell Storage
The screen buffer uses packed `Int32Array` storage for memory efficiency:
```ts
type Screen = {
width: number
height: number
cells: Int32Array // 2 Int32s per cell: [charId, packed_style_hyperlink_width]
cells64: BigInt64Array // For bulk fill operations
charPool: CharPool // String interning
stylePool: StylePool // ANSI code interning
hyperlinkPool: HyperlinkPool
emptyStyleId: number
damage: Rectangle | undefined // Bounding box of changed cells
noSelect: Uint8Array // Per-cell no-select bitmap
softWrap: Int32Array // Per-row soft-wrap markers
}
```
### Cell Width
```ts
enum CellWidth {
Narrow = 0, // Regular character (1 column)
Wide = 1, // CJK/emoji (2 columns)
SpacerTail = 2, // Right half of wide character
SpacerHead = 3, // Soft-wrapped wide character
}
```
### Style Pool
ANSI style codes are interned for efficiency:
```ts
class StylePool {
intern(codes: AnsiCode[]): number // Returns compact ID
get(id: number): AnsiCode[]
transition(from: number, to: number): string // Cached ANSI transition
withInverse(id: number): number // Selection overlay
setSelectionBg(bg: AnsiCode): void // Theme-aware selection bg
}
```
### Diff Algorithm
```ts
diffEach(prev: Screen, next: Screen, callback: (x, y, oldCell, newCell) => void): void
```
Only iterates cells within the damage bounding box. Unchanged regions are skipped entirely.
### Screen Operations
```ts
createScreen(width, height, stylePool, charPool, hyperlinkPool): Screen
setCellAt(screen, x, y, cell): void
cellAt(screen, x, y): Cell
clearRegion(screen, x, y, width, height): void
blitRegion(dst, src, x, y, maxX, maxY): void
shiftRows(screen, top, bottom, n): void
```
## Layout Engine
### Yoga Integration
Ink wraps Facebook's Yoga layout engine for Flexbox computation:
```ts
// Layout node types
enum LayoutDisplay { Flex, None }
enum LayoutPositionType { Absolute, Relative }
enum LayoutOverflow { Visible, Hidden, Scroll }
enum LayoutFlexDirection { Row, Column, RowReverse, ColumnReverse }
enum LayoutWrap { NoWrap, Wrap, WrapReverse }
enum LayoutAlign { FlexStart, Center, FlexEnd, Stretch }
enum LayoutJustify { FlexStart, Center, FlexEnd, SpaceBetween, SpaceAround, SpaceEvenly }
enum LayoutEdge { Top, Bottom, Left, Right, Start, End, Horizontal, Vertical, All }
enum LayoutGutter { Column, Row, All }
```
### Style Application
Styles from React props are applied to Yoga nodes during the commit phase:
```ts
function styles(node: LayoutNode, style: Styles, resolvedStyle?: Styles): void
```
This function maps each CSS-like prop to the corresponding Yoga setter.
## Output Buffer
Intermediate rendering target before screen diff:
```ts
class Output {
write(text: string, x: number, y: number, styles: TextStyles): void
wrap(width: number, textWrap: TextWrap): void
}
```
`renderNodeToOutput` walks the DOM tree and writes styled characters into this buffer.
## Reconciler
Custom React reconciler that bridges React and the Ink DOM:
- **Host config** -- Defines how React operations map to Ink DOM mutations
- **Concurrent mode** -- Supports `ConcurrentRoot` for React 19 features
- **Yoga integration** -- Applies styles during commit phase
- **DevTools** -- Connected in development mode
### Host Config Methods
| Method | Purpose |
|--------|---------|
| `createInstance` | Create `ink-box`, `ink-text`, etc. |
| `createTextInstance` | Create `#text` node |
| `appendChildNode` | Add child to parent |
| `removeChildNode` | Remove child from parent |
| `insertBefore` | Insert child before sibling |
| `commitUpdate` | Update element attributes/styles |
| `commitTextUpdate` | Update text content |
| `getPublicInstance` | Return DOMElement for refs |
## Performance Optimizations
1. **String Interning** -- CharPool deduplicates character strings across frames
2. **Style Caching** -- StylePool caches ANSI transition strings
3. **Damage Tracking** -- Only diff cells within the changed bounding box
4. **Bulk Operations** -- `Int32Array.set()` for fast region blit
5. **Throttled Rendering** -- Frame rate capped at ~60fps
6. **Viewport Culling** -- ScrollBox only renders visible children
7. **Microtask Coalescing** -- Multiple scroll deltas merged into one render
## Frame Events
Debug instrumentation for render performance:
```ts
type FrameEvent = {
durationMs: number
phases: {
renderer: number // Yoga + renderNodeToOutput
diff: number // Screen diff
optimize: number // Patch optimization
write: number // Terminal write
patches: number // Number of ANSI patches
yoga: number // Yoga layout time
commit: number // React commit time
yogaVisited: number // Yoga nodes visited
yogaMeasured: number // Yoga nodes measured
yogaCacheHits: number // Cached measurements
yogaLive: number // Active Yoga nodes
}
flickers: FlickerReason[]
}
```
Enable with `onFrame` in RenderOptions:
```tsx
render(<App />, {
onFrame: (event) => {
console.log(`Frame: ${event.durationMs}ms`)
}
})
```