Move the edit tool's hot-path diffing operations from JS to native Rust:
- `normalizeForFuzzyMatch`: single-pass Unicode normalization (smart quotes,
dashes, special spaces, trailing whitespace)
- `fuzzyFindText`: exact-then-fuzzy substring search with UTF-16 index
conversion for JS compatibility
- `generateDiff`: unified diff generation using the `similar` crate
(Myers' algorithm with optimizations)
The Rust module at `native/crates/engine/src/diff.rs` exposes three napi
functions. The TypeScript wrapper at `packages/native/src/diff/` follows
the existing module pattern. `edit-diff.ts` now delegates to native
implementations while keeping line-ending handling and file I/O in JS.
18 tests covering normalization, fuzzy matching (including UTF-16 index
correctness with emoji/surrogate pairs), and diff generation.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace manual binary header parsing (PNG/JPEG/GIF/WebP) in terminal-image.ts
with the native @gsd/native/image module, and replace photon-node (WASM) with
native N-API calls for image resize and format conversion.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The autocomplete file search no longer spawns the external `fd` binary via
spawnSync. It calls the in-process Rust fuzzyFind() function from @gsd/native,
which handles directory walking, gitignore, hidden files, and fuzzy scoring
in a single native call. The fdPath constructor parameter and ensureTool("fd")
download are removed since the binary is no longer needed.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Use the Rust-backed arboard clipboard (via @gsd/native/clipboard) for
text copy and image read, replacing the platform-tool shelling
(pbcopy/xclip/xsel) and @mariozechner/clipboard optional dependency.
OSC 52 is preserved as a fallback for SSH/mosh sessions. Linux Wayland
still falls back to wl-paste/xclip for image reads since arboard may
lack compositor access from a terminal.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Switch syntax highlighting from the cli-highlight npm package to the
@gsd/native Rust-based highlight module (syntect). The native module
accepts raw ANSI escape sequences via the HighlightColors interface,
eliminating the wrapper-function indirection of the old CliHighlightTheme.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The find tool's default path spawned `fd` and used the `glob` npm package
to discover nested .gitignore files. The native @gsd/native glob module
handles gitignore traversal natively via Rust's `ignore` crate, making
both dependencies unnecessary for this code path.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The copy-assets script was missing lsp.md from src/core/lsp/, causing
ENOENT at startup after the defaults.json fix landed.
Closes#233, closes#234
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Check if the destination file exists before performing a move in
hashline-edit. If it does, return an error instead of silently
overwriting the file.
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The delete operation in hashline-edit.ts wrapped both access() and
unlink() in a single try/catch. If access succeeded but unlink failed
(e.g., permissions), the error was silently swallowed and "Deleted" was
falsely reported. Now access and unlink have separate error handling:
access failures indicate the file doesn't exist, while unlink failures
propagate to the caller.
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Implement hashline edit mode inspired by Oh My Pi's approach. Each line
in a file is identified by a content hash (xxHash32, 2-char nibble
alphabet), enabling the model to reference lines by stable LINE#ID tags
instead of reproducing full line text. This eliminates the most common
edit failure mode (slightly misquoted original text) and reduces output
tokens.
New files:
- hashline.ts: core hash computation, formatting, parsing, validation,
and edit application engine (pure JS xxHash32, no native deps)
- hashline-edit.ts: AgentTool wrapper for hash-anchored file edits
- hashline-read.ts: read tool variant that outputs LINE#ID:CONTENT format
- hashline.test.ts: 54 tests covering all core operations
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- config.ts: Replace execSync(`which ${command}`) with spawnSync("which", [command])
to prevent shell injection from malicious lsp.json config files
- client.ts: Wrap JSON.parse in parseMessage with try/catch and handle null messages
in the stream reader to prevent process crashes from malformed LSP output
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Tests initialize, hover, go-to-definition, references, document symbols,
diagnostics (type error detection), and clean shutdown against a real
typescript-language-server instance with a temp TypeScript project.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
All 10 LSP files ported and adapted. Wired into tools/index.ts.
Remaining work: fix TypeScript compilation errors (see below).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
In RPC mode, `ctx.ui.custom()` returns `undefined as never`, causing
`showInterviewRound` to return undefined and `Object.keys(result.answers)`
to throw TypeError.
When `showInterviewRound` returns undefined (RPC mode), fall back to
sequential `ctx.ui.select()` calls for each question, forwarding the
abort signal (#171) and supporting `allowMultiple` (#165).
- Add `allowMultiple` to `ExtensionUIDialogOptions`
- Widen `select()` return type to `string | string[] | undefined`
- Add `allowMultiple` to RPC select request and `values` array to response
- Update RPC `select()` to forward `allowMultiple` and parse array responses
- Guard existing `ctx.ui.select()` callers against the widened return type
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
After /login, if the current model has no valid API key, auto-switch to
a model from the newly authenticated provider. After /logout, if the
current model belongs to the logged-out provider, auto-switch to a
fallback model from a different provider.
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Three locations used lastIndexOf("/") or includes("/") for path
manipulation, which fails on Windows where paths use backslashes.
- auto.ts: writeBlockerPlaceholder directory extraction → dirname()
- interactive-mode.ts: parent directory traversal → path.dirname() loop
- path-utils.ts: non-null assertion on MSYS drive letter access
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
On Windows, LLMs convert absolute paths like F:\Projects\.gsd\... to
Unix-style /f/Projects/.gsd/... which Node's path.resolve interprets
as drive-root-relative, creating F:\f\Projects\.gsd\... instead.
Replace all *AbsPath template variables in prompt templates with
relative .gsd/... paths that resolve correctly on all platforms.
Add MSYS path normalization in resolveToCwd as defense-in-depth.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
LLM-generated commands with `> NUL` create undeletable files on Windows
because Git Bash treats NUL as a literal filename. Rewrite NUL redirects
to /dev/null at all three bash spawn sites.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
External packages (pi-rtk, pi-context, pi-agent-browser, etc.) import from
the original @mariozechner/* scope which GSD forked to @gsd/*. Add aliases
in both jiti resolution paths (virtualModules for Bun, getAliases for Node)
so these packages resolve correctly without manual workarounds.
Closes#161
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add .gitignore negation for vendor path
- Restore marked.min.js from pi-mono upstream
- Restore highlight.min.js from pi-mono upstream
Fixes build failure in pi-coding-agent caused by
global vendor/ ignore rule excluding vendored libs.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Vendor all 4 Pi packages (tui, ai, agent-core, coding-agent) from
pi-mono v0.57.1 as @gsd/* workspace packages under packages/. This
replaces the compiled npm dependency (@mariozechner/pi-coding-agent)
and patch-package workflow, giving direct source access for
modifications.
- Copy Pi source from pi-mono v0.57.1 into packages/
- Create workspace package.json + tsconfig.json for each package
- Rename ~240 imports from @mariozechner/pi-* to @gsd/pi-*
- Apply existing patches as source edits (setModel persist, VT input)
- Remove @mariozechner/pi-coding-agent dep and patch-package
- Update build pipeline to build packages in dependency order
- Add pi-upstream git remote for future selective syncing
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>