- Migrated native bindings from TypeScript wrappers to NAPI-RS generated modules with auto-generated type definitions and runtime enums. - Replaced chunk tree API with stateful ChunkState class supporting render, edit, and resolve operations with improved error handling. - Converted callback signatures to error-first pattern (error, result) for shell, PTY, glob, and grep operations. - Introduced type-safe enums for MacOSAppearance, GrepOutputMode, KeyEventType, ImageFormat, and AstMatchStrictness replacing string literals. - Refactored chunk tree implementation with dedicated modules for edit, indent, resolve, and state management with comprehensive validation. - Moved clipboard utilities from native bindings to coding-agent package with improved OSC 52 and Termux compatibility.
6.9 KiB
ChunkState Migration — Parallelization Stages
Stage 0: Types (sequential, ~5 min)
Owner: main agent
Write the shared type definitions that all subsequent stages depend on.
File: crates/pi-natives/src/chunk/types.rs
- Remove
#[napi(object)]fromChunkNodeandChunkTree(become Rust-internal) - Remove
RenderChunkTreeParamsnapi object (replaced by method params) - Add
ChunkInfo— lightweight#[napi(object)]returned to JS from queries - Add
EditOperation,EditParams—#[napi(object)]input types - Add
EditResult—#[napi(object)]output withChunkStateinstance - Add
ReadRenderParams,ReadResult—#[napi(object)]for render_read - Keep
ChunkAnchorStyleandVisibleLineRangeas-is
Output: committed types.rs that cargo check won't pass yet (consumers not updated), but all type definitions are final.
Stage 1: Parallel Rust modules (4 agents, worktree isolation)
All agents receive the finalized types from Stage 0 in their prompt. Each writes a single new file.
Agent 1A: resolve.rs — Selector resolution
File: crates/pi-natives/src/chunk/resolve.rs (new)
Port from chunk-tree.ts lines 875–990:
sanitize_selector(sel) -> Option<String>— strip filename prefix, leading colon, trailing #XXXXresolve_exact(chunks, path) -> Option<usize>— index lookupresolve_suffix(chunks, sel) -> Result<usize>— suffix match ("fn_foo" → "class_Bar.fn_foo")resolve_prefix(chunks, sel) -> Result<usize>— CHUNK_NAME_PREFIXES expansionresolve_kind_path(chunks, sel) -> Result<usize>— "class.fn" → "class_Server.fn_start"resolve_by_crc(chunks, crc) -> Option<usize>— checksum lookupresolve_selector(chunks, sel) -> Result<usize, ResolveError>— orchestrates all of the abovesuggest_paths(chunks, query, limit) -> Vec<String>— similarity-based suggestions for errorssanitize_crc(crc) -> Option<String>
Reads: chunk-tree.ts (resolveAnchorChunk, sanitizeChunkSelector, sanitizeCrc, chunkPathSimilarity, suggestChunkPaths, CHUNK_NAME_PREFIXES, CONTAINER_NAME_PREFIXES)
Agent 1B: indent.rs — Indentation & content normalization
File: crates/pi-natives/src/chunk/indent.rs (new)
Port from chunk-tree.ts lines 217–410:
detect_common_indent(text) -> (String, usize)dedent_python_style(text) -> Stringindent_non_empty_lines(text, prefix) -> Stringdetect_space_indent_step(text) -> usizenormalize_leading_whitespace_char(text, target_char, file_indent_step) -> Stringreindent_inserted_block(content, target_indent, file_indent_step) -> Stringnormalize_inserted_content(content, target_indent, file_indent_step) -> Stringstrip_content_prefixes(content) -> String(line-number gutter stripping)detect_file_indent_step(chunks) -> u32
Reads: chunk-tree.ts (all indentation helpers, stripContentPrefixes, CHUNK_GUTTER_CODE_ROW_RE)
Agent 1C: edit.rs — Edit engine
File: crates/pi-natives/src/chunk/edit.rs (new)
Port from chunk-tree.ts lines 1040–1850. Assumes resolve.rs and indent.rs exist with the signatures above.
validate_crc(chunk, crc) -> Result<()>validate_line_range(chunk, line, end_line) -> Result<()>compute_insert_indent(state, anchor, inside) -> Stringget_insertion_point(state, anchor, placement) -> InsertionPointcompute_insert_spacing(state, anchor, pos) -> Spacingnormalize_insertion_boundary(source, offset, content, spacing) -> Stringcleanup_blank_line_artifacts(text, offset) -> Stringgo_type_append_child_insertion_point(state, anchor, content) -> Option<InsertionPoint>apply_edits(state: &ChunkStateInner, params: EditParams) -> Result<EditResult>- The main entry point. Schedules ops, sorts line-scoped, executes in order, rebuilds tree after each, validates parse errors, generates response.
Reads: chunk-tree.ts (applyChunkEdits and everything it calls: validateCrc, validateLineRange, computeInsertIndent, getInsertionPoint, getInsertionPointForPosition, computeInsertSpacing, normalizeInsertionBoundaryContent, cleanupBlankLineArtifactsAtOffset, goTypeAppendChildInsertionPoint, isContainerLikeChunk, renderChangedHunks)
Agent 1D: state.rs — ChunkState napi class
File: crates/pi-natives/src/chunk/state.rs (new)
The napi class that ties everything together. Assumes resolve.rs, indent.rs, edit.rs exist.
ChunkStatestruct withArc<ChunkStateInner>ChunkStateInnerstruct (source, language, checksum, line_count, parse_errors, chunks, chunk_index)#[napi(factory)] fn parse(source, language) -> Result<ChunkState>#[napi(getter)]for source, language, checksum, line_count, parse_errors#[napi] fn resolve(&self, path) -> Option<ChunkInfo>#[napi] fn resolve_selector(&self, selector) -> Result<ChunkInfo>#[napi] fn resolve_by_crc(&self, crc) -> Option<ChunkInfo>#[napi] fn line_to_chunk_path(&self, line) -> Option<String>#[napi] fn line_to_containing_chunk_path(&self, line) -> Option<String>#[napi] fn render(&self, params: RenderParams) -> String— delegates to render.rs#[napi] fn render_read(&self, params: ReadRenderParams) -> ReadResult— replaces formatChunkedRead#[napi] fn format_grep_line(&self, line, text, display_path) -> String#[napi] fn format_anchor(&self, name, checksum, depth, style, omit_checksum) -> String#[napi] fn apply_edits(&self, params: EditParams) -> Result<EditResult>— delegates to edit.rs
Reads: chunk-tree.ts (loadChunkTreeForFile, formatChunkedRead, formatChunkedGrepLine — for the render_read and format_grep_line signatures/logic), existing mod.rs build_chunk_tree, render.rs
Stage 2: Parallel wiring (2 agents)
Agent 2A: Rust integration
Files: crates/pi-natives/src/chunk/mod.rs, crates/pi-natives/src/chunk/render.rs
- Add
mod state; mod resolve; mod indent; mod edit;to mod.rs - Remove old napi function exports (parseChunkTree, resolveChunkPath, etc)
- Export
ChunkStatefrom mod.rs - Adapt render.rs to accept
&[ChunkNodeInternal]/&ChunkStateInnerinstead of&RenderChunkTreeParams cargo check -p pi-natives
Agent 2B: TS rewrite
Files: packages/natives/src/chunk/{types.ts, index.ts}, packages/coding-agent/src/tools/chunk-tree.ts, packages/coding-agent/src/tools/read.ts, packages/coding-agent/src/patch/index.ts
- New
types.ts:ChunkStateclass interface,ChunkInfo,EditParams,EditResult, etc - New
index.ts: exportChunkStateclass from native - Gut
chunk-tree.tsto ~200 lines: LRU cache ofChunkStateinstances, thin async wrappers - Update
read.tsandpatch/index.tsto use new API bun run tsc --noEmit
Stage 3: Test & fix (sequential)
Owner: main agent
cargo check -p pi-natives+cargo testbun run tsc --noEmitbun test packages/coding-agent/test/core/chunk-tree.test.tsbun test packages/coding-agent/test/tools/chunk-mode.test.ts- Fix any integration issues
- Render prompt templates with all anchor styles to verify