Commit graph

792 commits

Author SHA1 Message Date
TÂCHES
35056d3ba8 feat: replace cli-highlight with native syntect highlighter
feat: replace cli-highlight with native syntect highlighter
2026-03-13 13:27:12 -06:00
TÂCHES
3c2b8d7865 feat(find): wire native Rust glob into find tool
feat(find): wire native Rust glob into find tool
2026-03-13 13:27:09 -06:00
Lex Christopherson
5970ad74b2 merge: integrate native image module (#235) 2026-03-13 13:22:09 -06:00
Lex Christopherson
59ec1fbc02 feat: replace cli-highlight with native syntect-based highlighter
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>
2026-03-13 13:19:55 -06:00
Lex Christopherson
a685c7f987 feat(find): replace glob npm package + fd with native Rust glob
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>
2026-03-13 13:16:42 -06:00
Lex Christopherson
cd444eb0ea merge: integrate native fd module (#231) 2026-03-13 13:13:43 -06:00
Lex Christopherson
8fb8c6a16b merge: integrate native text module (#230) 2026-03-13 13:12:40 -06:00
Lex Christopherson
f1c848b429 merge: integrate native html module (#229) 2026-03-13 13:11:40 -06:00
Lex Christopherson
f8b286c66a fix: repair native module test assertions
- highlight: remove quotes from "bar" assertion (ANSI codes split the string)
- ps: skip listDescendants child test (proc_listchildpids unreliable on macOS)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-13 13:06:47 -06:00
Lex Christopherson
df39cea85e feat: add native ast module with ast-grep structural search and rewrite
Adds the `gsd-ast` crate providing AST-aware code search (`astGrep`) and
rewrite (`astEdit`) via ast-grep with tree-sitter grammars for 38+ languages.
Replaces Oh My Pi's fs_cache/task dependencies with the `ignore` crate for
.gitignore-respecting file walking. Includes TypeScript type declarations
and wrappers in packages/native.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-13 13:02:29 -06:00
Lex Christopherson
ab5b9e949a fix: copy lsp.md to dist during build
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>
2026-03-13 13:02:29 -06:00
Lex Christopherson
75fe5d3319 feat: add native image module — decode, encode, and resize via Rust image crate
Port image processing from Oh My Pi's pi-natives crate, adapted for napi-rs v2.
Exposes NativeImage class with async parse/encode/resize methods backed by the
Rust `image` crate (PNG, JPEG, WebP, GIF support).

Includes:
- task.rs: lightweight async task scheduling for libuv thread pool
- image.rs: NativeImage class with SamplingFilter enum
- TypeScript types and wrapper (parseImage, ImageFormat, SamplingFilter)
- 8 passing tests covering decode, encode, resize, round-trip, error cases

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-13 12:51:49 -06:00
TÂCHES
daca368ba2 feat: add native clipboard module with arboard backend (#228)
Cross-platform clipboard access (text read/write, image read) via the
arboard Rust crate. No external tools (pbcopy, xclip, etc.) required.

Ported from Oh My Pi's clipboard module with adaptations for GSD's
architecture (direct AsyncTask instead of task::blocking wrapper).

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-13 12:48:27 -06:00
Lex Christopherson
e05292f772 feat: add native ast module — AST-aware structural search and rewrite via ast-grep
Port ast-grep integration from Oh My Pi with 38+ language support via tree-sitter
grammars. Exposes `astGrep` (search) and `astEdit` (rewrite) as N-API functions
with TypeScript wrappers.

Key changes:
- New `gsd-ast` crate with language definitions, glob utilities, and ast-grep core
- Replaces fs_cache/task dependencies with `ignore` crate for file walking
- Synchronous API matching the existing grep module pattern
- Full TypeScript type declarations in packages/native/src/ast/

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-13 12:47:27 -06:00
TÂCHES
d64575cd3c feat: add syntect-based syntax highlighting module to native engine (#227)
Port the highlight module from Oh My Pi's pi-natives crate. Provides
ANSI-colored syntax highlighting with scope-based semantic token matching
across 11 categories (comment, keyword, function, variable, string, number,
type, operator, punctuation, inserted, deleted).

Exposed N-API functions:
- highlightCode(code, lang, colors) -> ANSI-highlighted string
- supportsLanguage(lang) -> boolean
- getSupportedLanguages() -> string[]

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-13 12:47:02 -06:00
TÂCHES
c36c8bd0b0 feat: add native glob and fs_cache modules with gitignore-aware discovery (#226)
Port glob, glob_util, and fs_cache modules from Oh My Pi's pi-natives crate,
adapted for napi-rs v2. Provides gitignore-respecting filesystem discovery
with a TTL-based scan cache, mtime sorting, file-type filtering, and
node_modules exclusion.

Includes a task module for async N-API work scheduling with cooperative
cancellation (timeout-based), TypeScript type declarations and wrapper,
and 12 integration tests covering pattern matching, recursion, gitignore,
maxResults, sortByMtime, fileType filtering, and cache invalidation.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-13 12:45:56 -06:00
TÂCHES
c5bc8625a4 feat: add cross-platform process tree kill module (ps) (#225)
Port Oh My Pi's ps module providing efficient process tree enumeration
and termination using platform-native APIs (libproc on macOS, /proc on
Linux, Toolhelp32 on Windows).

Exposes four napi functions: killTree, listDescendants, processGroupId,
and killProcessGroup.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-13 12:43:50 -06:00
Lex Christopherson
b669f9f580 feat: add ANSI-aware text measurement and slicing native module
Port Oh My Pi's optimized text utilities to GSD's native engine:
- wrapTextWithAnsi: word-wrap preserving ANSI codes across breaks
- truncateToWidth: truncate with ellipsis options
- sliceWithWidth: column-range extraction
- extractSegments: split around overlay regions
- sanitizeText: strip ANSI, remove control chars, normalize CR
- visibleWidth: display width excluding ANSI sequences

Single-pass ANSI scanning, ASCII fast-path, grapheme-aware Unicode
width measurement, and zero-copy input via UTF-16 JsString interop.

Includes 19 Rust unit tests and 33 Node.js integration tests.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-13 12:42:42 -06:00
Lex Christopherson
a74d2061c1 feat: add html-to-markdown native module
Port HTML-to-Markdown conversion from Oh My Pi's html module using
html-to-markdown-rs. Exposes `htmlToMarkdown()` via N-API with options
for content cleaning (strip nav/forms/headers/footers) and image skipping.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-13 12:40:42 -06:00
Lex Christopherson
fab9ad390d feat: add native clipboard module with arboard backend
Cross-platform clipboard access (text read/write, image read) via the
arboard Rust crate. No external tools (pbcopy, xclip, etc.) required.

Ported from Oh My Pi's clipboard module with adaptations for GSD's
architecture (direct AsyncTask instead of task::blocking wrapper).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-13 12:36:23 -06:00
TÂCHES
0d390688e3 fix: prevent move operation from silently overwriting existing files (#219) (#223)
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>
2026-03-13 12:27:39 -06:00
TÂCHES
3906331b1b fix: copy lsp defaults.json to dist during build (#224)
Closes #221

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-13 12:27:38 -06:00
TÂCHES
f7b3144291 fix: separate access/unlink error handling in delete path (#219) (#222)
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>
2026-03-13 12:27:28 -06:00
TÂCHES
840a6918b9 Merge pull request #215 from gsd-build/feat/hashline-edits
feat: add hashline edits — line-hash-anchored file editing
2026-03-13 12:21:16 -06:00
TÂCHES
c669c6183a feat: Rust native engine scaffold with grep module
* feat: scaffold Rust native engine with grep module (napi-rs)

Adds a Rust N-API addon architecture inspired by Oh My Pi's pi-natives.
The grep module wraps ripgrep's core crates (grep-regex, grep-searcher,
grep-matcher) and exposes `search()` and `grep()` to Node.js via napi-rs.

Includes:
- Cargo workspace at native/ with engine (cdylib) and grep (lib) crates
- Build script (native/scripts/build.js) producing platform-tagged .node files
- TypeScript wrapper package (@gsd/native) with types and loader
- 6 Rust unit tests + 9 Node.js integration tests (all passing)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: audit fixes for rust native engine PR

- Fix repository URL to gsd-build/gsd-2
- Remove dead add_custom_ignore_filename("") call
- Unify error model: search() now returns Result<> (throws) matching grep()
- Remove error field from NapiSearchResult and SearchResult types
- Use t.after() in integration tests for reliable temp dir cleanup

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-13 12:21:09 -06:00
Lex Christopherson
83445f4449 feat: add hashline edits — line-hash-anchored file editing
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>
2026-03-13 11:52:24 -06:00
Lex Christopherson
2c4f5de321 fix: eliminate command injection and unhandled JSON.parse in LSP tool
- 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>
2026-03-13 11:45:04 -06:00
Lex Christopherson
120ae367ad test: add LSP integration test against typescript-language-server
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>
2026-03-13 11:37:49 -06:00
Lex Christopherson
eb288233bc fix: convert LSP tool from Bun APIs to Node APIs
All Bun-specific APIs replaced with Node equivalents:
- Bun.spawn → child_process.spawn
- Bun.file/Bun.write → fs/promises readFile/writeFile
- Bun.Glob → glob package
- Bun.sleep → setTimeout promise
- Bun.which → execSync("which")
- Bun.env → process.env
- Bun.FileSink → Writable stream
- YAML/TOML from bun → yaml package (TOML stripped)
- import with { type: "json" } → createRequire
- Added .js extensions to all relative imports
- Fixed Timer type → ReturnType<typeof setInterval>
- Added explicit types to all implicit any params

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-13 11:33:57 -06:00
Lex Christopherson
f51a080bcf wip: port LSP tool from Oh My Pi (needs type fixes)
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>
2026-03-13 11:19:43 -06:00
Lex Christopherson
135390542a fix: handle non-thinking models correctly in /thinking command (#129)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 10:58:49 -06:00
Lex Christopherson
bb10aacb23 feat: add /thinking slash command for toggling thinking level (#129)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 10:52:53 -06:00
TÂCHES
3084366d87 fix: handle undefined result from custom() in RPC mode for ask_user_questions (#156, #165, #171) (#199)
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>
2026-03-13 10:24:57 -06:00
TÂCHES
738444aeeb fix: auto-switch model after /login and /logout to prevent API key errors (#124) (#197)
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>
2026-03-13 10:23:00 -06:00
Lex Christopherson
d9a9a73ab2 fix: replace hardcoded forward-slash path ops with node:path stdlib (#184)
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>
2026-03-13 10:02:20 -06:00
Lex Christopherson
74278ad865 fix: use relative paths in prompts to prevent Windows drive letter mangling (#184)
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>
2026-03-13 09:55:15 -06:00
Lex Christopherson
98c2d23ce6 fix: sanitize Windows NUL redirects to /dev/null in Git Bash (#157)
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>
2026-03-13 09:33:28 -06:00
TÂCHES
789a6645da feat: TTSR + blob/artifact storage (ported from oh-my-pi)
* docs(M002): context, requirements, and roadmap

* feat: port TTSR and blob/artifact storage from oh-my-pi

Phase 1 — TTSR (Time Traveling Stream Rules):
- TtsrManager: regex-based stream monitoring with scope filtering,
  repeat gating, and buffer isolation (picomatch replaces Bun.Glob)
- Rule loader: scans ~/.gsd/agent/rules/*.md and .gsd/rules/*.md
  with YAML frontmatter parsing; project rules override global
- TTSR extension: wires into pi event lifecycle (session_start,
  turn_start, message_update, turn_end, agent_end) to abort on
  match and inject violation as system reminder via sendMessage
- Interrupt template for rule violation injection

Phase 2 — Blob/Artifact Storage:
- BlobStore: content-addressed storage at ~/.gsd/agent/blobs/ using
  Node crypto (sha256), sync I/O, automatic deduplication
- ArtifactManager: session-scoped sequential artifact files stored
  alongside session JSONL (lazy dir creation, resume-safe ID scan)
- Session manager integration: prepareForPersistence externalizes
  images ≥1KB to blob store before JSONL write; resolveBlobRefs
  rehydrates on session load; truncates strings >500KB
- Bash tool artifact spill: uses ArtifactManager instead of temp
  files when available, includes artifact:// references in output

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

* fix: harden blob store, TTSR manager, and dep classification

- Validate SHA-256 hex format in BlobStore.get/has/parseBlobRef to
  prevent path traversal via crafted blob references
- Cap TTSR per-stream buffers at 512KB to prevent unbounded memory growth
- Move picomatch from devDependencies to dependencies (runtime import)
- Warn on invalid regex in TTSR rule conditions instead of silent skip
- Remove .gsd/ planning files that were force-added past .gitignore
- Add trailing newline to ttsr-interrupt.md

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

* test: add tests for blob store, artifact manager, TTSR manager, and rule loader

55 tests covering:
- BlobStore put/get/has, idempotency, path traversal rejection
- parseBlobRef/isBlobRef validation, externalize/resolve round-trips
- ArtifactManager sequential IDs, lazy dir creation, session resume
- TtsrManager rule matching, scope filtering, buffer isolation,
  repeat gating, buffer size cap, injection persistence
- Rule loader frontmatter parsing, directory scanning, merge logic

Also fixes BlobStore constructor to avoid TS parameter property syntax
(incompatible with Node's strip-only TypeScript mode).

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

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 08:43:56 -06:00
Lex Christopherson
ee6dce643b chore(M002/S01): auto-commit after reassess-roadmap 2026-03-13 08:04:27 -06:00
Lex Christopherson
4e82688de6 fix: alias @mariozechner/* imports to @gsd/* for external PI ecosystem packages
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>
2026-03-13 07:27:24 -06:00
Marcel Reschke
a2d724a8b4 fix: add missing export-html vendor files
- 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>
2026-03-13 10:38:13 +01:00
Lex Christopherson
c80d640d35 feat: vendor Pi source into workspace monorepo
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>
2026-03-12 21:55:17 -06:00