From b43bf6991e00564265ddfa9e78ebe37b36a98b84 Mon Sep 17 00:00:00 2001 From: Mikael Hugo Date: Thu, 30 Apr 2026 20:21:12 +0200 Subject: [PATCH] sf snapshot: pre-dispatch, uncommitted changes after 47m inactivity --- CONTRIBUTING.md | 2 +- biome.json | 2 +- docs/README.md | 2 +- docs/dev/ADR-016-charm-ai-stack-adoption.md | 2 +- docs/dev/FILE-SYSTEM-MAP.md | 102 +++---- .../specs/2026-03-17-cicd-pipeline-design.md | 2 +- package.json | 10 +- packages/native/package.json | 4 +- packages/native/src/native.ts | 10 +- packages/pi-coding-agent/src/cli/args.ts | 6 +- .../pi-coding-agent/src/core/agent-session.ts | 4 +- packages/pi-coding-agent/src/core/sdk.ts | 6 +- .../pi-coding-agent/src/core/system-prompt.ts | 4 +- .../pi-coding-agent/src/core/tools/index.ts | 21 +- .../src/modes/interactive/theme/themes.ts | 261 +++++++++--------- {native => rust-engine}/.cargo/config.toml | 0 {native => rust-engine}/.gitignore | 0 {native => rust-engine}/.npmignore | 0 {native => rust-engine}/Cargo.lock | 0 {native => rust-engine}/Cargo.toml | 0 {native => rust-engine}/README.md | 10 +- {native => rust-engine}/crates/ast/Cargo.toml | 0 {native => rust-engine}/crates/ast/src/ast.rs | 0 .../crates/ast/src/glob_util.rs | 0 .../crates/ast/src/language/mod.rs | 0 .../crates/ast/src/language/parsers.rs | 0 {native => rust-engine}/crates/ast/src/lib.rs | 0 .../crates/engine/Cargo.toml | 0 .../crates/engine/build.rs | 0 .../crates/engine/src/ast.rs | 0 .../crates/engine/src/clipboard.rs | 0 .../crates/engine/src/diff.rs | 0 .../crates/engine/src/fd.rs | 0 .../crates/engine/src/forge_parser.rs | 0 .../crates/engine/src/fs_cache.rs | 0 .../crates/engine/src/git.rs | 0 .../crates/engine/src/glob.rs | 0 .../crates/engine/src/glob_util.rs | 0 .../crates/engine/src/grep.rs | 0 .../crates/engine/src/highlight.rs | 0 .../crates/engine/src/html.rs | 0 .../crates/engine/src/image.rs | 0 .../crates/engine/src/json_parse.rs | 0 .../crates/engine/src/lib.rs | 0 .../crates/engine/src/ps.rs | 0 .../crates/engine/src/stream_process.rs | 0 .../crates/engine/src/task.rs | 0 .../crates/engine/src/text.rs | 0 .../crates/engine/src/truncate.rs | 0 .../crates/engine/src/ttsr.rs | 0 .../crates/engine/src/xxhash.rs | 0 .../crates/grep/Cargo.toml | 0 .../crates/grep/src/lib.rs | 0 .../npm/darwin-arm64/package.json | 0 .../npm/darwin-x64/package.json | 0 .../npm/linux-arm64-gnu/package.json | 0 .../npm/linux-x64-gnu/package.json | 0 .../npm/win32-x64-msvc/package.json | 0 {native => rust-engine}/scripts/build.js | 0 .../scripts/sync-platform-versions.cjs | 0 scripts/bump-version.mjs | 2 +- scripts/link-workspace-packages.cjs | 2 +- src/resources/extensions/sf-tui/footer.ts | 171 ++++++------ src/resources/extensions/sf-tui/git.ts | 21 +- .../sf/skills/finish-and-verify/SKILL.md | 2 +- .../sf/skills/systematic-debugging/SKILL.md | 2 +- tsconfig.extensions.json | 2 +- web/middleware.ts | 80 ------ 68 files changed, 346 insertions(+), 384 deletions(-) rename {native => rust-engine}/.cargo/config.toml (100%) rename {native => rust-engine}/.gitignore (100%) rename {native => rust-engine}/.npmignore (100%) rename {native => rust-engine}/Cargo.lock (100%) rename {native => rust-engine}/Cargo.toml (100%) rename {native => rust-engine}/README.md (94%) rename {native => rust-engine}/crates/ast/Cargo.toml (100%) rename {native => rust-engine}/crates/ast/src/ast.rs (100%) rename {native => rust-engine}/crates/ast/src/glob_util.rs (100%) rename {native => rust-engine}/crates/ast/src/language/mod.rs (100%) rename {native => rust-engine}/crates/ast/src/language/parsers.rs (100%) rename {native => rust-engine}/crates/ast/src/lib.rs (100%) rename {native => rust-engine}/crates/engine/Cargo.toml (100%) rename {native => rust-engine}/crates/engine/build.rs (100%) rename {native => rust-engine}/crates/engine/src/ast.rs (100%) rename {native => rust-engine}/crates/engine/src/clipboard.rs (100%) rename {native => rust-engine}/crates/engine/src/diff.rs (100%) rename {native => rust-engine}/crates/engine/src/fd.rs (100%) rename {native => rust-engine}/crates/engine/src/forge_parser.rs (100%) rename {native => rust-engine}/crates/engine/src/fs_cache.rs (100%) rename {native => rust-engine}/crates/engine/src/git.rs (100%) rename {native => rust-engine}/crates/engine/src/glob.rs (100%) rename {native => rust-engine}/crates/engine/src/glob_util.rs (100%) rename {native => rust-engine}/crates/engine/src/grep.rs (100%) rename {native => rust-engine}/crates/engine/src/highlight.rs (100%) rename {native => rust-engine}/crates/engine/src/html.rs (100%) rename {native => rust-engine}/crates/engine/src/image.rs (100%) rename {native => rust-engine}/crates/engine/src/json_parse.rs (100%) rename {native => rust-engine}/crates/engine/src/lib.rs (100%) rename {native => rust-engine}/crates/engine/src/ps.rs (100%) rename {native => rust-engine}/crates/engine/src/stream_process.rs (100%) rename {native => rust-engine}/crates/engine/src/task.rs (100%) rename {native => rust-engine}/crates/engine/src/text.rs (100%) rename {native => rust-engine}/crates/engine/src/truncate.rs (100%) rename {native => rust-engine}/crates/engine/src/ttsr.rs (100%) rename {native => rust-engine}/crates/engine/src/xxhash.rs (100%) rename {native => rust-engine}/crates/grep/Cargo.toml (100%) rename {native => rust-engine}/crates/grep/src/lib.rs (100%) rename {native => rust-engine}/npm/darwin-arm64/package.json (100%) rename {native => rust-engine}/npm/darwin-x64/package.json (100%) rename {native => rust-engine}/npm/linux-arm64-gnu/package.json (100%) rename {native => rust-engine}/npm/linux-x64-gnu/package.json (100%) rename {native => rust-engine}/npm/win32-x64-msvc/package.json (100%) rename {native => rust-engine}/scripts/build.js (100%) rename {native => rust-engine}/scripts/sync-platform-versions.cjs (100%) delete mode 100644 web/middleware.ts diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f11e3a04d..7564c7d7e 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -149,7 +149,7 @@ The codebase is organized into these areas. All are open to contributions: | MCP server | `packages/mcp-server` | Project state tools and MCP protocol | | SF extension | `src/resources/extensions/sf/` | SF workflow — RFC required for auto-mode | | Other extensions | `src/resources/extensions/` | Browser, search, voice, MCP client, etc. | -| Native engine | `native/` | Rust N-API modules (grep, git, AST, etc.) | +| Native engine | `rust-engine/` | Rust N-API modules (grep, git, AST, etc.) | | VS Code extension | `vscode-extension/` | Chat participant, sidebar, RPC integration | | Web interface | `web/` | Browser-based dashboard | | CI/Build | `.github/`, `scripts/` | Workflows, build scripts | diff --git a/biome.json b/biome.json index f66ce99b8..6ff8b3329 100644 --- a/biome.json +++ b/biome.json @@ -6,7 +6,7 @@ "useIgnoreFile": true }, "files": { - "includes": ["**", "!!**/dist", "!!**/dist-test", "!!**/native/npm"] + "includes": ["**", "!!**/dist", "!!**/dist-test", "!!**/rust-engine/npm"] }, "formatter": { "enabled": true, diff --git a/docs/README.md b/docs/README.md index f0bc187b8..61f54849e 100644 --- a/docs/README.md +++ b/docs/README.md @@ -38,7 +38,7 @@ Design documents, ADRs, and internal references. Located in [`dev/`](./dev/). | Guide | Description | |-------|-------------| | [Architecture Overview](./dev/architecture.md) | System design, extension model, state-on-disk, and dispatch pipeline | -| [Native Engine](../native/README.md) | Rust N-API modules for performance-critical operations | +| [Native Engine](../rust-engine/README.md) | Rust N-API modules for performance-critical operations | | [ADR-001: Branchless Worktree Architecture](./dev/ADR-001-branchless-worktree-architecture.md) | Decision record for the v2.14 git architecture | | [ADR-003: Pipeline Simplification](./dev/ADR-003-pipeline-simplification.md) | Research merged into planning, mechanical completion (v2.30) | | [ADR-004: Capability-Aware Model Routing](./dev/ADR-004-capability-aware-model-routing.md) | Extend routing from tier/cost selection to task-capability matching | diff --git a/docs/dev/ADR-016-charm-ai-stack-adoption.md b/docs/dev/ADR-016-charm-ai-stack-adoption.md index a2474eac5..a9290e8f8 100644 --- a/docs/dev/ADR-016-charm-ai-stack-adoption.md +++ b/docs/dev/ADR-016-charm-ai-stack-adoption.md @@ -25,7 +25,7 @@ The question the SPEC retarget didn't have to answer: **now that this much is he - **sf core (TypeScript on pi-mono): unchanged.** SPEC §1 retarget rationale stands. Pi-mono SDK alignment, MCP-server story, ~200+ TS files, real production users — none of it justifies a 3–6 month rewrite. - **New services: Go on Charm, comprehensively.** sf-worker (ADR-013), Singularity Knowledge + Agent Platform (ADR-014), flight recorder (ADR-015), Charm TUI client (ADR-017) — all in Go using the Charm ecosystem. -- **Native engine (Rust): permanent.** ~11k LOC in `native/` (git, text, forge_parser, grep, highlight, ast, diff, etc.) is best-of-breed and not re-implementable in Go without losing performance. Bindings (napi-rs from TS today; cgo from Go for new services if needed) flex per consumer. +- **Native engine (Rust): permanent.** ~11k LOC in `rust-engine/` (git, text, forge_parser, grep, highlight, ast, diff, etc.) is best-of-breed and not re-implementable in Go without losing performance. Bindings (napi-rs from TS today; cgo from Go for new services if needed) flex per consumer. - **Pony adoption: now, not deferred.** Reversed from initial conservative stance. Adopting pony from day one in Phase-3 admin surfaces (Singularity Memory admin UI, future audit dashboards) — admin tolerates churn better than user-facing surfaces, and the foundation bet pays back if pony stabilises. - **Other `charmbracelet/x/*` packages: adopted comprehensively.** When a new Go service needs a primitive (image rendering, session recording, pty, editor, input handling), use the `x/*` package. Don't reinvent. - **Re-evaluation trigger: 12 months from first Go service in production.** If >50% of *new* sf code lands in Go services, the question of consolidating sf core becomes worth re-asking. Until then, polyglot is the right cost shape. diff --git a/docs/dev/FILE-SYSTEM-MAP.md b/docs/dev/FILE-SYSTEM-MAP.md index 921c9e88f..30056ad69 100644 --- a/docs/dev/FILE-SYSTEM-MAP.md +++ b/docs/dev/FILE-SYSTEM-MAP.md @@ -847,55 +847,55 @@ --- -## native/ — Rust Engine +## rust-engine/ — Rust Engine | File | System Label(s) | Description | |------|-----------------|-------------| -| native/crates/engine/src/lib.rs | Native/Rust Tools | N-API entry point exposing all Rust modules | -| native/crates/engine/src/grep.rs | File Search, Native/Rust Tools | Ripgrep-backed regex search with context/globbing | -| native/crates/engine/src/glob.rs | File Search, Native/Rust Tools | Glob-pattern FS discovery with gitignore + scan cache | -| native/crates/engine/src/fd.rs | File Search, Native/Rust Tools | Fuzzy file discovery for autocomplete/@-mentions | -| native/crates/engine/src/highlight.rs | Syntax Highlighting, Native/Rust Tools | Syntect-backed ANSI syntax highlighting | -| native/crates/engine/src/ast.rs | AST, Native/Rust Tools | Linker shim for AST N-API registrations | -| native/crates/engine/src/diff.rs | Text Processing, Native/Rust Tools | Fuzzy matching, Unicode normalization, unified diffs | -| native/crates/engine/src/image.rs | Image Processing, Native/Rust Tools | Image decode/encode and resize | -| native/crates/engine/src/html.rs | Text Processing, Native/Rust Tools | HTML to Markdown conversion | -| native/crates/engine/src/text.rs | Text Processing, Native/Rust Tools | ANSI-aware text measurement and slicing | -| native/crates/engine/src/truncate.rs | Text Processing, Native/Rust Tools | Line-boundary-aware output truncation | -| native/crates/engine/src/ps.rs | Native/Rust Tools | Cross-platform process tree management | -| native/crates/engine/src/clipboard.rs | Native/Rust Tools | Clipboard read/write for text and images | -| native/crates/engine/src/json_parse.rs | Text Processing, Native/Rust Tools | Streaming JSON parser with partial recovery | -| native/crates/engine/src/sf_parser.rs | SF Workflow, Native/Rust Tools | .sf/ directory file parser (markdown, frontmatter) | -| native/crates/engine/src/ttsr.rs | TTSR, Native/Rust Tools | TTSR regex engine with compiled RegexSet | -| native/crates/engine/src/stream_process.rs | Text Processing, Native/Rust Tools | Bash stream processor (UTF-8, ANSI strip, binary) | -| native/crates/engine/src/xxhash.rs | Native/Rust Tools | xxHash32 for hashline edit tool | -| native/crates/engine/src/git.rs | Native/Rust Tools | Native git operations via libgit2 | -| native/crates/engine/src/fs_cache.rs | File Search, Native/Rust Tools | TTL-based FS scan cache with explicit invalidation | -| native/crates/engine/src/glob_util.rs | File Search, Native/Rust Tools | Shared glob-pattern helpers | -| native/crates/engine/src/task.rs | Native/Rust Tools | Blocking work on libuv thread pool with cancellation | -| native/crates/engine/build.rs | Build System | Cargo build script for napi-build compilation | -| native/crates/grep/src/lib.rs | File Search, Native/Rust Tools | Ripgrep search library (in-memory and on-disk) | -| native/crates/ast/src/lib.rs | AST, Native/Rust Tools | AST-aware structural search and rewrite engine | -| native/crates/ast/src/ast.rs | AST, Native/Rust Tools | ast-grep integration for structural code search | -| native/crates/ast/src/language/mod.rs | AST, Native/Rust Tools | Vendored language defs and tree-sitter bindings | -| native/crates/ast/src/language/parsers.rs | AST, Native/Rust Tools | Pre-compiled tree-sitter parsers (50+ languages) | +| rust-engine/crates/engine/src/lib.rs | Native/Rust Tools | N-API entry point exposing all Rust modules | +| rust-engine/crates/engine/src/grep.rs | File Search, Native/Rust Tools | Ripgrep-backed regex search with context/globbing | +| rust-engine/crates/engine/src/glob.rs | File Search, Native/Rust Tools | Glob-pattern FS discovery with gitignore + scan cache | +| rust-engine/crates/engine/src/fd.rs | File Search, Native/Rust Tools | Fuzzy file discovery for autocomplete/@-mentions | +| rust-engine/crates/engine/src/highlight.rs | Syntax Highlighting, Native/Rust Tools | Syntect-backed ANSI syntax highlighting | +| rust-engine/crates/engine/src/ast.rs | AST, Native/Rust Tools | Linker shim for AST N-API registrations | +| rust-engine/crates/engine/src/diff.rs | Text Processing, Native/Rust Tools | Fuzzy matching, Unicode normalization, unified diffs | +| rust-engine/crates/engine/src/image.rs | Image Processing, Native/Rust Tools | Image decode/encode and resize | +| rust-engine/crates/engine/src/html.rs | Text Processing, Native/Rust Tools | HTML to Markdown conversion | +| rust-engine/crates/engine/src/text.rs | Text Processing, Native/Rust Tools | ANSI-aware text measurement and slicing | +| rust-engine/crates/engine/src/truncate.rs | Text Processing, Native/Rust Tools | Line-boundary-aware output truncation | +| rust-engine/crates/engine/src/ps.rs | Native/Rust Tools | Cross-platform process tree management | +| rust-engine/crates/engine/src/clipboard.rs | Native/Rust Tools | Clipboard read/write for text and images | +| rust-engine/crates/engine/src/json_parse.rs | Text Processing, Native/Rust Tools | Streaming JSON parser with partial recovery | +| rust-engine/crates/engine/src/sf_parser.rs | SF Workflow, Native/Rust Tools | .sf/ directory file parser (markdown, frontmatter) | +| rust-engine/crates/engine/src/ttsr.rs | TTSR, Native/Rust Tools | TTSR regex engine with compiled RegexSet | +| rust-engine/crates/engine/src/stream_process.rs | Text Processing, Native/Rust Tools | Bash stream processor (UTF-8, ANSI strip, binary) | +| rust-engine/crates/engine/src/xxhash.rs | Native/Rust Tools | xxHash32 for hashline edit tool | +| rust-engine/crates/engine/src/git.rs | Native/Rust Tools | Native git operations via libgit2 | +| rust-engine/crates/engine/src/fs_cache.rs | File Search, Native/Rust Tools | TTL-based FS scan cache with explicit invalidation | +| rust-engine/crates/engine/src/glob_util.rs | File Search, Native/Rust Tools | Shared glob-pattern helpers | +| rust-engine/crates/engine/src/task.rs | Native/Rust Tools | Blocking work on libuv thread pool with cancellation | +| rust-engine/crates/engine/build.rs | Build System | Cargo build script for napi-build compilation | +| rust-engine/crates/grep/src/lib.rs | File Search, Native/Rust Tools | Ripgrep search library (in-memory and on-disk) | +| rust-engine/crates/ast/src/lib.rs | AST, Native/Rust Tools | AST-aware structural search and rewrite engine | +| rust-engine/crates/ast/src/ast.rs | AST, Native/Rust Tools | ast-grep integration for structural code search | +| rust-engine/crates/ast/src/language/mod.rs | AST, Native/Rust Tools | Vendored language defs and tree-sitter bindings | +| rust-engine/crates/ast/src/language/parsers.rs | AST, Native/Rust Tools | Pre-compiled tree-sitter parsers (50+ languages) | -## packages/native/src/ — Node.js Rust Bindings +## packages/rust-engine/src/ — Node.js Rust Bindings | File | System Label(s) | Description | |------|-----------------|-------------| -| packages/native/src/native.ts | Native/Rust Tools, Node.js Bindings | Native addon loader with platform fallback | -| packages/native/src/grep/index.ts | File Search, Node.js Bindings | Ripgrep wrapper for regex search | -| packages/native/src/fd/index.ts | File Search, Node.js Bindings | Fuzzy file discovery wrapper | -| packages/native/src/highlight/index.ts | Syntax Highlighting, Node.js Bindings | Syntax highlighting wrapper | -| packages/native/src/image/index.ts | Image Processing, Node.js Bindings | Image processing wrapper | -| packages/native/src/html/index.ts | Text Processing, Node.js Bindings | HTML to Markdown wrapper | -| packages/native/src/diff/index.ts | Text Processing, Node.js Bindings | Text diffing wrapper | -| packages/native/src/ps/index.ts | Native/Rust Tools, Node.js Bindings | Process tree management wrapper | -| packages/native/src/truncate/index.ts | Text Processing, Node.js Bindings | Output truncation wrapper | -| packages/native/src/json-parse/index.ts | Text Processing, Node.js Bindings | JSON parsing wrapper | -| packages/native/src/stream-process/index.ts | Text Processing, Node.js Bindings | Stream processing wrapper | -| packages/native/src/ttsr/index.ts | TTSR, Node.js Bindings | TTSR regex engine wrapper | +| packages/rust-engine/src/native.ts | Native/Rust Tools, Node.js Bindings | Native addon loader with platform fallback | +| packages/rust-engine/src/grep/index.ts | File Search, Node.js Bindings | Ripgrep wrapper for regex search | +| packages/rust-engine/src/fd/index.ts | File Search, Node.js Bindings | Fuzzy file discovery wrapper | +| packages/rust-engine/src/highlight/index.ts | Syntax Highlighting, Node.js Bindings | Syntax highlighting wrapper | +| packages/rust-engine/src/image/index.ts | Image Processing, Node.js Bindings | Image processing wrapper | +| packages/rust-engine/src/html/index.ts | Text Processing, Node.js Bindings | HTML to Markdown wrapper | +| packages/rust-engine/src/diff/index.ts | Text Processing, Node.js Bindings | Text diffing wrapper | +| packages/rust-engine/src/ps/index.ts | Native/Rust Tools, Node.js Bindings | Process tree management wrapper | +| packages/rust-engine/src/truncate/index.ts | Text Processing, Node.js Bindings | Output truncation wrapper | +| packages/rust-engine/src/json-parse/index.ts | Text Processing, Node.js Bindings | JSON parsing wrapper | +| packages/rust-engine/src/stream-process/index.ts | Text Processing, Node.js Bindings | Stream processing wrapper | +| packages/rust-engine/src/ttsr/index.ts | TTSR, Node.js Bindings | TTSR regex engine wrapper | --- @@ -964,13 +964,13 @@ Quick lookup: which files are part of each system? | **Agent Core** | pi-agent-core/src/*, pi-coding-agent/src/core/agent-session.ts, agent-loop.ts, agent.ts, event-bus.ts, sdk.ts | | **AI Providers** | pi-ai/src/providers/*, pi-ai/src/stream.ts, pi-ai/src/models*.ts | | **API Routes** | web/app/api/**/*.ts | -| **AST** | native/crates/ast/*, packages/native/src/ast/ | +| **AST** | rust-engine/crates/ast/*, packages/rust-engine/src/ast/ | | **Async Jobs** | src/resources/extensions/async-jobs/* | | **Auth / OAuth** | pi-ai/src/utils/oauth/*, src/web/web-auth-storage.ts, core/auth-storage.ts, src/pi-migration.ts, aws-auth/index.ts, web/lib/auth.ts | | **Auto Engine** | src/resources/extensions/sf/auto*.ts, sf/auto-loop.ts, sf/auto-supervisor.ts, sf/unit-runtime.ts | | **Bg Shell** | src/resources/extensions/bg-shell/* | | **Browser Tools** | src/resources/extensions/browser-tools/* | -| **Build System** | scripts/*, native/crates/engine/build.rs | +| **Build System** | scripts/*, rust-engine/crates/engine/build.rs | | **CLI** | src/cli.ts, src/cli-web-branch.ts, src/help-text.ts, src/update*.ts, pi-coding-agent/src/cli.ts, src/worktree-cli.ts | | **CMux** | src/resources/extensions/cmux/index.ts | | **Commands** | sf/commands*.ts, sf/exit-command.ts, sf/undo.ts, sf/kill.ts, pi-coding-agent/src/core/slash-commands.ts | @@ -981,11 +981,11 @@ Quick lookup: which files are part of each system? | **Event System** | pi-coding-agent/src/core/event-bus.ts, sf/auto-observability.ts | | **Extension Registry** | src/extension-discovery.ts, src/extension-registry.ts, src/bundled-extension-paths.ts | | **Extensions** | pi-coding-agent/src/core/extensions/*, src/resource-loader.ts | -| **File Search** | native/crates/engine/src/grep.rs, glob.rs, fd.rs, fs_cache.rs, packages/native/src/grep/*, fd/*, core/tools/grep.ts, find.ts | +| **File Search** | rust-engine/crates/engine/src/grep.rs, glob.rs, fd.rs, fs_cache.rs, packages/rust-engine/src/grep/*, fd/*, core/tools/grep.ts, find.ts | | **SF Workflow** | src/resources/extensions/sf/* (non-auto), sf/reports.ts, sf/notifications.ts, sf/prompts/*, sf/workflow-templates/* | | **Google Search** | src/resources/extensions/google-search/index.ts | | **Headless Mode** | src/headless*.ts | -| **Image Processing** | native/crates/engine/src/image.rs, packages/native/src/image/*, utils/image-*.ts, web/lib/image-utils.ts | +| **Image Processing** | rust-engine/crates/engine/src/image.rs, packages/rust-engine/src/image/*, utils/image-*.ts, web/lib/image-utils.ts | | **Integration Tests** | tests/**/* | | **Loader / Bootstrap** | src/loader.ts, src/resource-loader.ts, src/tool-bootstrap.ts, src/bundled-resource-path.ts, sf/bootstrap/* | | **LSP** | pi-coding-agent/src/core/lsp/* | @@ -995,8 +995,8 @@ Quick lookup: which files are part of each system? | **Migration** | sf/migrate/*, src/pi-migration.ts, pi-coding-agent/src/migrations.ts, scripts/recover-*.sh | | **Modes** | pi-coding-agent/src/modes/* | | **Model System** | pi-coding-agent/src/core/model-*.ts, pi-ai/src/models*.ts, pi-ai/src/api-registry.ts, sf/model-router.ts | -| **Native / Rust Tools** | native/crates/engine/src/* | -| **Node.js Bindings** | packages/native/src/* | +| **Native / Rust Tools** | rust-engine/crates/engine/src/* | +| **Node.js Bindings** | packages/rust-engine/src/* | | **Onboarding** | src/onboarding.ts, src/wizard.ts, web/components/sf/onboarding/*, web/app/api/onboarding/* | | **Permissions** | core/extensions/project-trust.ts, core/auth-storage.ts | | **Remote Questions** | src/resources/extensions/remote-questions/* | @@ -1007,10 +1007,10 @@ Quick lookup: which files are part of each system? | **State Machine** | sf/state.ts, sf/history.ts, sf/json-persistence.ts, sf/memory-store.ts, sf/reactive-graph.ts, core/agent-session.ts, web/lib/sf-workspace-store.tsx | | **Studio App** | studio/* | | **Subagent** | src/resources/extensions/subagent/*, src/resources/agents/* | -| **Syntax Highlighting** | native/crates/engine/src/highlight.rs, packages/native/src/highlight/* | -| **Text Processing** | native/crates/engine/src/diff.rs, html.rs, text.rs, truncate.rs, json_parse.rs, stream_process.rs | +| **Syntax Highlighting** | rust-engine/crates/engine/src/highlight.rs, packages/rust-engine/src/highlight/* | +| **Text Processing** | rust-engine/crates/engine/src/diff.rs, html.rs, text.rs, truncate.rs, json_parse.rs, stream_process.rs | | **Tool System** | pi-coding-agent/src/core/tools/*, core/bash-executor.ts, core/exec.ts | -| **TTSR** | src/resources/extensions/ttsr/*, native/crates/engine/src/ttsr.rs, packages/native/src/ttsr/* | +| **TTSR** | src/resources/extensions/ttsr/*, rust-engine/crates/engine/src/ttsr.rs, packages/rust-engine/src/ttsr/* | | **TUI Components** | packages/pi-tui/src/*, pi-coding-agent/src/modes/interactive/components/*, pi-coding-agent/src/modes/interactive/controllers/* | | **Universal Config** | src/resources/extensions/universal-config/* | | **Voice** | src/resources/extensions/voice/* | diff --git a/docs/dev/superpowers/specs/2026-03-17-cicd-pipeline-design.md b/docs/dev/superpowers/specs/2026-03-17-cicd-pipeline-design.md index 04331a610..5ae552982 100644 --- a/docs/dev/superpowers/specs/2026-03-17-cicd-pipeline-design.md +++ b/docs/dev/superpowers/specs/2026-03-17-cicd-pipeline-design.md @@ -82,7 +82,7 @@ The `-dev.` prerelease identifier is distinct from the existing `-next.` convent Dev versions (`@dev` tag) use the native binaries from the most recent stable `build-native.yml` release. The `optionalDependencies` in `package.json` use `>=` ranges, so a `-dev.` version of `sf-run` resolves the latest stable `@sf-build/engine-*` packages from the registry. -If a PR modifies Rust native crate code (`native/` directory), the dev publish will bundle stale native binaries. This is acceptable because: +If a PR modifies Rust native crate code (`rust-engine/` directory), the dev publish will bundle stale native binaries. This is acceptable because: - Native crate changes are infrequent and always accompanied by a `v*` tag release - The Test stage validates the installed package works end-to-end - Full native binary validation happens via `build-native.yml` on the version tag diff --git a/package.json b/package.json index c4b4dea1b..aea3d2490 100644 --- a/package.json +++ b/package.json @@ -62,7 +62,7 @@ "test:packages": "node --test packages/pi-coding-agent/dist/core/*.test.js packages/pi-coding-agent/dist/core/tools/spawn-shell-windows.test.js", "test:marketplace": "node scripts/with-env.mjs SF_TEST_CLONE_MARKETPLACES=1 -- node --import ./src/resources/extensions/sf/tests/resolve-ts.mjs --experimental-strip-types --test src/resources/extensions/sf/tests/claude-import-tui.test.ts src/resources/extensions/sf/tests/plugin-importer-live.test.ts src/tests/marketplace-discovery.test.ts", "test:sf-light": "node --max-old-space-size=2048 --import ./src/resources/extensions/sf/tests/resolve-ts.mjs --experimental-strip-types --test-timeout=30000 --test \"src/resources/extensions/sf/tests/*.test.ts\"", - "test:coverage": "c8 --reporter=text --reporter=lcov --exclude=\"src/resources/extensions/sf/tests/**\" --exclude=\"src/tests/**\" --exclude=\"scripts/**\" --exclude=\"native/**\" --exclude=\"node_modules/**\" --check-coverage --statements=40 --lines=40 --branches=20 --functions=20 node --import ./src/resources/extensions/sf/tests/resolve-ts.mjs --experimental-strip-types --experimental-test-isolation=process --test src/resources/extensions/sf/tests/*.test.ts src/resources/extensions/sf/tests/*.test.mjs src/tests/*.test.ts src/resources/extensions/shared/tests/*.test.ts", + "test:coverage": "c8 --reporter=text --reporter=lcov --exclude=\"src/resources/extensions/sf/tests/**\" --exclude=\"src/tests/**\" --exclude=\"scripts/**\" --exclude=\"rust-engine/**\" --exclude=\"node_modules/**\" --check-coverage --statements=40 --lines=40 --branches=20 --functions=20 node --import ./src/resources/extensions/sf/tests/resolve-ts.mjs --experimental-strip-types --experimental-test-isolation=process --test src/resources/extensions/sf/tests/*.test.ts src/resources/extensions/sf/tests/*.test.mjs src/tests/*.test.ts src/resources/extensions/shared/tests/*.test.ts", "test:integration": "node --import ./src/resources/extensions/sf/tests/resolve-ts.mjs --experimental-strip-types --test \"src/tests/integration/*.test.ts\" \"src/resources/extensions/sf/tests/integration/*.test.ts\" \"src/resources/extensions/async-jobs/*.test.ts\" \"src/resources/extensions/browser-tools/tests/*.test.mjs\"", "pretest": "npm run typecheck:extensions", "test": "npm run test:unit && npm run test:integration", @@ -71,12 +71,12 @@ "test:fixtures:record": "node scripts/with-env.mjs SF_FIXTURE_MODE=record -- node --experimental-strip-types tests/fixtures/record.ts", "test:live": "node scripts/with-env.mjs SF_LIVE_TESTS=1 -- node --experimental-strip-types tests/live/run.ts", "test:browser-tools": "node --test src/resources/extensions/browser-tools/tests/browser-tools-unit.test.cjs src/resources/extensions/browser-tools/tests/browser-tools-integration.test.mjs", - "test:native": "node --test packages/native/src/__tests__/grep.test.mjs", + "test:native": "node --test packages/rust-engine/src/__tests__/grep.test.mjs", "test:secret-scan": "node --import ./src/resources/extensions/sf/tests/resolve-ts.mjs --experimental-strip-types --test src/tests/secret-scan.test.ts", "secret-scan": "node scripts/secret-scan.mjs", "secret-scan:install-hook": "node scripts/install-hooks.mjs", - "build:native": "node native/scripts/build.js", - "build:native:dev": "node native/scripts/build.js --dev", + "build:native": "node rust-engine/scripts/build.js", + "build:native:dev": "node rust-engine/scripts/build.js --dev", "dev": "node scripts/dev.js", "sf": "node scripts/dev-cli.js", "sf:web": "npm run build:pi && npm run copy-resources && node scripts/build-web-if-stale.cjs && node scripts/dev-cli.js --web", @@ -86,7 +86,7 @@ "pi:install-global": "node scripts/install-pi-global.js", "pi:uninstall-global": "node scripts/uninstall-pi-global.js", "sync-pkg-version": "node scripts/sync-pkg-version.cjs", - "sync-platform-versions": "node native/scripts/sync-platform-versions.cjs", + "sync-platform-versions": "node rust-engine/scripts/sync-platform-versions.cjs", "validate-pack": "node scripts/validate-pack.js", "typecheck:extensions": "npm run check:versioned-json && tsc --noEmit --project tsconfig.extensions.json", "check:versioned-json": "node scripts/check-versioned-json.mjs", diff --git a/packages/native/package.json b/packages/native/package.json index 6805f8409..9018d6646 100644 --- a/packages/native/package.json +++ b/packages/native/package.json @@ -7,8 +7,8 @@ "types": "./dist/index.d.ts", "scripts": { "build": "tsc -p tsconfig.json", - "build:native": "node ../../native/scripts/build.js", - "build:native:dev": "node ../../native/scripts/build.js --dev", + "build:native": "node ../../rust-engine/scripts/build.js", + "build:native:dev": "node ../../rust-engine/scripts/build.js --dev", "test": "npm run build:native:dev && node --test src/__tests__/grep.test.mjs src/__tests__/ps.test.mjs src/__tests__/glob.test.mjs src/__tests__/clipboard.test.mjs src/__tests__/highlight.test.mjs src/__tests__/html.test.mjs src/__tests__/text.test.mjs src/__tests__/fd.test.mjs src/__tests__/image.test.mjs" }, "exports": { diff --git a/packages/native/src/native.ts b/packages/native/src/native.ts index 589a8bc2f..378ca6407 100644 --- a/packages/native/src/native.ts +++ b/packages/native/src/native.ts @@ -4,8 +4,8 @@ * Locates and loads the compiled Rust N-API addon (`.node` file). * Resolution order: * 1. @singularity-forge/engine-{platform} npm optional dependency (production install) - * 2. native/addon/forge_engine.{platform}.node (local release build) - * 3. native/addon/forge_engine.dev.node (local debug build) + * 2. rust-engine/addon/forge_engine.{platform}.node (local release build) + * 3. rust-engine/addon/forge_engine.dev.node (local debug build) */ import * as path from "node:path"; @@ -16,7 +16,7 @@ import * as path from "node:path"; const _dirname = __dirname; const _require = require; -const addonDir = path.resolve(_dirname, "..", "..", "..", "native", "addon"); +const addonDir = path.resolve(_dirname, "..", "..", "..", "rust-engine", "addon"); const platformTag = `${process.platform}-${process.arch}`; /** Map Node.js platform/arch to the npm package suffix */ @@ -44,7 +44,7 @@ function loadNative(): Record { } } - // 2. Try local release build (native/addon/forge_engine.{platform}.node) + // 2. Try local release build (rust-engine/addon/forge_engine.{platform}.node) const releasePath = path.join(addonDir, `forge_engine.${platformTag}.node`); try { _loadedSuccessfully = true; return _require(releasePath) as Record; @@ -53,7 +53,7 @@ function loadNative(): Record { errors.push(`${releasePath}: ${message}`); } - // 3. Try local dev build (native/addon/forge_engine.dev.node) + // 3. Try local dev build (rust-engine/addon/forge_engine.dev.node) const devPath = path.join(addonDir, "forge_engine.dev.node"); try { _loadedSuccessfully = true; return _require(devPath) as Record; diff --git a/packages/pi-coding-agent/src/cli/args.ts b/packages/pi-coding-agent/src/cli/args.ts index bd8ad3913..663b185d7 100644 --- a/packages/pi-coding-agent/src/cli/args.ts +++ b/packages/pi-coding-agent/src/cli/args.ts @@ -208,7 +208,7 @@ export function parseArgs(args: string[], extensionFlags?: Map Comma-separated list of tools to enable (default: read,bash,edit,write) - Available: read, bash, edit, write, lsp, grep, find, ls + Available: read, grep, find, ls, bash, edit, write, lsp --thinking Set thinking level: off, minimal, low, medium, high, xhigh --extension, -e Load an extension file (can be used multiple times) --no-extensions, -ne Disable extension discovery (explicit -e paths still work) @@ -339,7 +339,7 @@ ${chalk.bold("Environment Variables:")} PI_OFFLINE - Disable startup network operations when set to 1/true/yes PI_SHARE_VIEWER_URL - Base URL for /share command (default: https://pi.dev/session/) -${chalk.bold("Available Tools (default: read, bash, edit, write):")} +${chalk.bold("Available Tools (default: read, grep, find, ls, bash, edit, write, lsp):")} read - Read file contents bash - Execute bash commands edit - Edit files with find/replace diff --git a/packages/pi-coding-agent/src/core/agent-session.ts b/packages/pi-coding-agent/src/core/agent-session.ts index 022f2ff36..116261f29 100644 --- a/packages/pi-coding-agent/src/core/agent-session.ts +++ b/packages/pi-coding-agent/src/core/agent-session.ts @@ -160,7 +160,7 @@ export interface AgentSessionConfig { customTools?: ToolDefinition[]; /** Model registry for API key resolution and model discovery */ modelRegistry: ModelRegistry; - /** Initial active built-in tool names. Default: [read, bash, edit, write] */ + /** Initial active built-in tool names. Default: [read, grep, find, ls, bash, edit, write, lsp] */ initialActiveToolNames?: string[]; /** Override base tools (useful for custom runtimes). */ baseToolsOverride?: Record; @@ -2233,7 +2233,7 @@ export class AgentSession { const defaultActiveToolNames = this._baseToolsOverride ? Object.keys(this._baseToolsOverride) - : ["read", "bash", "edit", "write", "lsp"]; + : ["read", "grep", "find", "ls", "bash", "edit", "write", "lsp"]; const baseActiveToolNames = options.activeToolNames ?? defaultActiveToolNames; this._refreshToolRegistry({ activeToolNames: baseActiveToolNames, diff --git a/packages/pi-coding-agent/src/core/sdk.ts b/packages/pi-coding-agent/src/core/sdk.ts index c39b7f7a7..787887e49 100644 --- a/packages/pi-coding-agent/src/core/sdk.ts +++ b/packages/pi-coding-agent/src/core/sdk.ts @@ -94,7 +94,7 @@ export interface CreateAgentSessionOptions { /** Models available for cycling (Ctrl+P in interactive mode) */ scopedModels?: Array<{ model: Model; thinkingLevel?: ThinkingLevel }>; - /** Built-in tools to use. Default: codingTools [read, bash, edit, write] */ + /** Built-in tools to use. Default: codingTools [read, grep, find, ls, bash, edit, write, lsp] */ tools?: Tool[]; /** Custom tools to register (in addition to built-in tools). */ customTools?: ToolDefinition[]; @@ -313,8 +313,8 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {} const editMode = settingsManager.getEditMode(); const defaultActiveToolNames: ToolName[] = editMode === "hashline" - ? ["hashline_read", "bash", "hashline_edit", "write", "lsp"] - : ["read", "bash", "edit", "write", "lsp"]; + ? ["hashline_read", "grep", "find", "ls", "bash", "hashline_edit", "write", "lsp"] + : ["read", "grep", "find", "ls", "bash", "edit", "write", "lsp"]; const initialActiveToolNames: ToolName[] = options.tools ? options.tools.map((t) => t.name).filter((n): n is ToolName => n in allTools) : defaultActiveToolNames; diff --git a/packages/pi-coding-agent/src/core/system-prompt.ts b/packages/pi-coding-agent/src/core/system-prompt.ts index be602844a..073e684d7 100644 --- a/packages/pi-coding-agent/src/core/system-prompt.ts +++ b/packages/pi-coding-agent/src/core/system-prompt.ts @@ -21,7 +21,7 @@ const toolDescriptions: Record = { export interface BuildSystemPromptOptions { /** Custom system prompt (replaces default). */ customPrompt?: string; - /** Tools to include in prompt. Default: [read, bash, edit, write] */ + /** Tools to include in prompt. Default: [read, grep, find, ls, bash, edit, write, lsp] */ selectedTools?: string[]; /** Optional one-line tool snippets keyed by tool name. */ toolSnippets?: Record; @@ -149,7 +149,7 @@ export function buildSystemPrompt(options: BuildSystemPromptOptions = {}): strin // Build tools list based on selected tools. // Built-ins use toolDescriptions. Custom tools can provide one-line snippets. - const tools = selectedTools || ["read", "bash", "edit", "write"]; + const tools = selectedTools || ["read", "grep", "find", "ls", "bash", "edit", "write", "lsp"]; const toolsList = tools.length > 0 ? tools diff --git a/packages/pi-coding-agent/src/core/tools/index.ts b/packages/pi-coding-agent/src/core/tools/index.ts index 14b1cf388..8165d193f 100644 --- a/packages/pi-coding-agent/src/core/tools/index.ts +++ b/packages/pi-coding-agent/src/core/tools/index.ts @@ -136,7 +136,7 @@ import { createLspTool, lspTool } from "../lsp/index.js"; export type Tool = AgentTool; // Default tools for full access mode (using process.cwd()) -export const codingTools: Tool[] = [readTool, bashTool, editTool, writeTool]; +export const codingTools: Tool[] = [readTool, grepTool, findTool, lsTool, bashTool, editTool, writeTool, lspTool]; // Read-only tools for exploration without modification (using process.cwd()) export const readOnlyTools: Tool[] = [readTool, grepTool, findTool, lsTool]; @@ -156,7 +156,16 @@ export const allTools = { }; // Hashline-mode coding tools — read with hash anchors, edit with hash references -export const hashlineCodingTools: Tool[] = [hashlineReadTool, bashTool, hashlineEditTool, writeTool]; +export const hashlineCodingTools: Tool[] = [ + hashlineReadTool, + grepTool, + findTool, + lsTool, + bashTool, + hashlineEditTool, + writeTool, + lspTool, +]; export type ToolName = keyof typeof allTools; @@ -173,9 +182,13 @@ export interface ToolsOptions { export function createCodingTools(cwd: string, options?: ToolsOptions): Tool[] { return [ createReadTool(cwd, options?.read), + createGrepTool(cwd), + createFindTool(cwd), + createLsTool(cwd), createBashTool(cwd, options?.bash), createEditTool(cwd), createWriteTool(cwd), + createLspTool(cwd), ]; } @@ -211,8 +224,12 @@ export function createAllTools(cwd: string, options?: ToolsOptions): Record = { dark, light }; diff --git a/native/.cargo/config.toml b/rust-engine/.cargo/config.toml similarity index 100% rename from native/.cargo/config.toml rename to rust-engine/.cargo/config.toml diff --git a/native/.gitignore b/rust-engine/.gitignore similarity index 100% rename from native/.gitignore rename to rust-engine/.gitignore diff --git a/native/.npmignore b/rust-engine/.npmignore similarity index 100% rename from native/.npmignore rename to rust-engine/.npmignore diff --git a/native/Cargo.lock b/rust-engine/Cargo.lock similarity index 100% rename from native/Cargo.lock rename to rust-engine/Cargo.lock diff --git a/native/Cargo.toml b/rust-engine/Cargo.toml similarity index 100% rename from native/Cargo.toml rename to rust-engine/Cargo.toml diff --git a/native/README.md b/rust-engine/README.md similarity index 94% rename from native/README.md rename to rust-engine/README.md index 81a727b23..214927e90 100644 --- a/native/README.md +++ b/rust-engine/README.md @@ -7,7 +7,7 @@ Rust N-API addon providing high-performance native modules for SF. ``` JS (packages/native) -> N-API -> Rust crates -native/crates/ +rust-engine/crates/ ├── engine/ (N-API bindings, cdylib — 20+ modules) ├── grep/ (ripgrep internals, pure Rust lib) └── ast/ (ast-grep structural search) @@ -30,7 +30,7 @@ npm run build:native npm run build:native:dev ``` -The build script compiles the Rust code and copies the `.node` shared library to `native/addon/`. +The build script compiles the Rust code and copies the `.node` shared library to `rust-engine/addon/`. ## Test @@ -153,7 +153,7 @@ xxHash hashing. Provides fast, non-cryptographic hashing via the xxHash algorith ## Adding New Modules -1. Create a new crate in `native/crates/` (pure Rust library) -2. Add N-API bindings in `native/crates/engine/src/` -3. Add TypeScript wrapper in `packages/native/src/` +1. Create a new crate in `rust-engine/crates/` (pure Rust library) +2. Add N-API bindings in `rust-engine/crates/engine/src/` +3. Add TypeScript wrapper in `package./rust-engine/src/` 4. Add the crate to `engine/Cargo.toml` dependencies diff --git a/native/crates/ast/Cargo.toml b/rust-engine/crates/ast/Cargo.toml similarity index 100% rename from native/crates/ast/Cargo.toml rename to rust-engine/crates/ast/Cargo.toml diff --git a/native/crates/ast/src/ast.rs b/rust-engine/crates/ast/src/ast.rs similarity index 100% rename from native/crates/ast/src/ast.rs rename to rust-engine/crates/ast/src/ast.rs diff --git a/native/crates/ast/src/glob_util.rs b/rust-engine/crates/ast/src/glob_util.rs similarity index 100% rename from native/crates/ast/src/glob_util.rs rename to rust-engine/crates/ast/src/glob_util.rs diff --git a/native/crates/ast/src/language/mod.rs b/rust-engine/crates/ast/src/language/mod.rs similarity index 100% rename from native/crates/ast/src/language/mod.rs rename to rust-engine/crates/ast/src/language/mod.rs diff --git a/native/crates/ast/src/language/parsers.rs b/rust-engine/crates/ast/src/language/parsers.rs similarity index 100% rename from native/crates/ast/src/language/parsers.rs rename to rust-engine/crates/ast/src/language/parsers.rs diff --git a/native/crates/ast/src/lib.rs b/rust-engine/crates/ast/src/lib.rs similarity index 100% rename from native/crates/ast/src/lib.rs rename to rust-engine/crates/ast/src/lib.rs diff --git a/native/crates/engine/Cargo.toml b/rust-engine/crates/engine/Cargo.toml similarity index 100% rename from native/crates/engine/Cargo.toml rename to rust-engine/crates/engine/Cargo.toml diff --git a/native/crates/engine/build.rs b/rust-engine/crates/engine/build.rs similarity index 100% rename from native/crates/engine/build.rs rename to rust-engine/crates/engine/build.rs diff --git a/native/crates/engine/src/ast.rs b/rust-engine/crates/engine/src/ast.rs similarity index 100% rename from native/crates/engine/src/ast.rs rename to rust-engine/crates/engine/src/ast.rs diff --git a/native/crates/engine/src/clipboard.rs b/rust-engine/crates/engine/src/clipboard.rs similarity index 100% rename from native/crates/engine/src/clipboard.rs rename to rust-engine/crates/engine/src/clipboard.rs diff --git a/native/crates/engine/src/diff.rs b/rust-engine/crates/engine/src/diff.rs similarity index 100% rename from native/crates/engine/src/diff.rs rename to rust-engine/crates/engine/src/diff.rs diff --git a/native/crates/engine/src/fd.rs b/rust-engine/crates/engine/src/fd.rs similarity index 100% rename from native/crates/engine/src/fd.rs rename to rust-engine/crates/engine/src/fd.rs diff --git a/native/crates/engine/src/forge_parser.rs b/rust-engine/crates/engine/src/forge_parser.rs similarity index 100% rename from native/crates/engine/src/forge_parser.rs rename to rust-engine/crates/engine/src/forge_parser.rs diff --git a/native/crates/engine/src/fs_cache.rs b/rust-engine/crates/engine/src/fs_cache.rs similarity index 100% rename from native/crates/engine/src/fs_cache.rs rename to rust-engine/crates/engine/src/fs_cache.rs diff --git a/native/crates/engine/src/git.rs b/rust-engine/crates/engine/src/git.rs similarity index 100% rename from native/crates/engine/src/git.rs rename to rust-engine/crates/engine/src/git.rs diff --git a/native/crates/engine/src/glob.rs b/rust-engine/crates/engine/src/glob.rs similarity index 100% rename from native/crates/engine/src/glob.rs rename to rust-engine/crates/engine/src/glob.rs diff --git a/native/crates/engine/src/glob_util.rs b/rust-engine/crates/engine/src/glob_util.rs similarity index 100% rename from native/crates/engine/src/glob_util.rs rename to rust-engine/crates/engine/src/glob_util.rs diff --git a/native/crates/engine/src/grep.rs b/rust-engine/crates/engine/src/grep.rs similarity index 100% rename from native/crates/engine/src/grep.rs rename to rust-engine/crates/engine/src/grep.rs diff --git a/native/crates/engine/src/highlight.rs b/rust-engine/crates/engine/src/highlight.rs similarity index 100% rename from native/crates/engine/src/highlight.rs rename to rust-engine/crates/engine/src/highlight.rs diff --git a/native/crates/engine/src/html.rs b/rust-engine/crates/engine/src/html.rs similarity index 100% rename from native/crates/engine/src/html.rs rename to rust-engine/crates/engine/src/html.rs diff --git a/native/crates/engine/src/image.rs b/rust-engine/crates/engine/src/image.rs similarity index 100% rename from native/crates/engine/src/image.rs rename to rust-engine/crates/engine/src/image.rs diff --git a/native/crates/engine/src/json_parse.rs b/rust-engine/crates/engine/src/json_parse.rs similarity index 100% rename from native/crates/engine/src/json_parse.rs rename to rust-engine/crates/engine/src/json_parse.rs diff --git a/native/crates/engine/src/lib.rs b/rust-engine/crates/engine/src/lib.rs similarity index 100% rename from native/crates/engine/src/lib.rs rename to rust-engine/crates/engine/src/lib.rs diff --git a/native/crates/engine/src/ps.rs b/rust-engine/crates/engine/src/ps.rs similarity index 100% rename from native/crates/engine/src/ps.rs rename to rust-engine/crates/engine/src/ps.rs diff --git a/native/crates/engine/src/stream_process.rs b/rust-engine/crates/engine/src/stream_process.rs similarity index 100% rename from native/crates/engine/src/stream_process.rs rename to rust-engine/crates/engine/src/stream_process.rs diff --git a/native/crates/engine/src/task.rs b/rust-engine/crates/engine/src/task.rs similarity index 100% rename from native/crates/engine/src/task.rs rename to rust-engine/crates/engine/src/task.rs diff --git a/native/crates/engine/src/text.rs b/rust-engine/crates/engine/src/text.rs similarity index 100% rename from native/crates/engine/src/text.rs rename to rust-engine/crates/engine/src/text.rs diff --git a/native/crates/engine/src/truncate.rs b/rust-engine/crates/engine/src/truncate.rs similarity index 100% rename from native/crates/engine/src/truncate.rs rename to rust-engine/crates/engine/src/truncate.rs diff --git a/native/crates/engine/src/ttsr.rs b/rust-engine/crates/engine/src/ttsr.rs similarity index 100% rename from native/crates/engine/src/ttsr.rs rename to rust-engine/crates/engine/src/ttsr.rs diff --git a/native/crates/engine/src/xxhash.rs b/rust-engine/crates/engine/src/xxhash.rs similarity index 100% rename from native/crates/engine/src/xxhash.rs rename to rust-engine/crates/engine/src/xxhash.rs diff --git a/native/crates/grep/Cargo.toml b/rust-engine/crates/grep/Cargo.toml similarity index 100% rename from native/crates/grep/Cargo.toml rename to rust-engine/crates/grep/Cargo.toml diff --git a/native/crates/grep/src/lib.rs b/rust-engine/crates/grep/src/lib.rs similarity index 100% rename from native/crates/grep/src/lib.rs rename to rust-engine/crates/grep/src/lib.rs diff --git a/native/npm/darwin-arm64/package.json b/rust-engine/npm/darwin-arm64/package.json similarity index 100% rename from native/npm/darwin-arm64/package.json rename to rust-engine/npm/darwin-arm64/package.json diff --git a/native/npm/darwin-x64/package.json b/rust-engine/npm/darwin-x64/package.json similarity index 100% rename from native/npm/darwin-x64/package.json rename to rust-engine/npm/darwin-x64/package.json diff --git a/native/npm/linux-arm64-gnu/package.json b/rust-engine/npm/linux-arm64-gnu/package.json similarity index 100% rename from native/npm/linux-arm64-gnu/package.json rename to rust-engine/npm/linux-arm64-gnu/package.json diff --git a/native/npm/linux-x64-gnu/package.json b/rust-engine/npm/linux-x64-gnu/package.json similarity index 100% rename from native/npm/linux-x64-gnu/package.json rename to rust-engine/npm/linux-x64-gnu/package.json diff --git a/native/npm/win32-x64-msvc/package.json b/rust-engine/npm/win32-x64-msvc/package.json similarity index 100% rename from native/npm/win32-x64-msvc/package.json rename to rust-engine/npm/win32-x64-msvc/package.json diff --git a/native/scripts/build.js b/rust-engine/scripts/build.js similarity index 100% rename from native/scripts/build.js rename to rust-engine/scripts/build.js diff --git a/native/scripts/sync-platform-versions.cjs b/rust-engine/scripts/sync-platform-versions.cjs similarity index 100% rename from native/scripts/sync-platform-versions.cjs rename to rust-engine/scripts/sync-platform-versions.cjs diff --git a/scripts/bump-version.mjs b/scripts/bump-version.mjs index f4c88337d..7105033b9 100644 --- a/scripts/bump-version.mjs +++ b/scripts/bump-version.mjs @@ -59,7 +59,7 @@ for (const name of workspacePackages) { } // 3. Sync platform package versions (reads from root package.json) -execSync("node native/scripts/sync-platform-versions.cjs", { cwd: root, stdio: "inherit" }); +execSync("node rust-engine/scripts/sync-platform-versions.cjs", { cwd: root, stdio: "inherit" }); // 4. Sync pkg/package.json (reads from pi-coding-agent) execSync("node scripts/sync-pkg-version.cjs", { cwd: root, stdio: "inherit" }); diff --git a/scripts/link-workspace-packages.cjs b/scripts/link-workspace-packages.cjs index fe735146e..714d070c6 100644 --- a/scripts/link-workspace-packages.cjs +++ b/scripts/link-workspace-packages.cjs @@ -88,7 +88,7 @@ for (const dir of packageDirs) { if (linked > 0) process.stderr.write(` Linked ${linked} workspace package${linked !== 1 ? 's' : ''}\n`) if (copied > 0) process.stderr.write(` Copied ${copied} workspace package${copied !== 1 ? 's' : ''} (symlinks unavailable)\n`) -// Platform-specific native engine packages live under native/npm//, not packages/. +// Platform-specific native engine packages live under rust-engine/npm//, not packages/. // Wire them into node_modules/@singularity-forge/ so native.ts can require() them without // a registry install. Only link platforms where the binary (forge_engine.node) is present. const nativeNpmDir = join(root, 'native', 'npm') diff --git a/src/resources/extensions/sf-tui/footer.ts b/src/resources/extensions/sf-tui/footer.ts index 713d50c4d..0371114bf 100644 --- a/src/resources/extensions/sf-tui/footer.ts +++ b/src/resources/extensions/sf-tui/footer.ts @@ -5,11 +5,62 @@ import type { } from "@singularity-forge/pi-coding-agent"; import { truncateToWidth, visibleWidth } from "@singularity-forge/pi-tui"; import { refreshGitStatus } from "./git.js"; -import { - renderPowerline, - renderPowerlineRight, - type Segment, -} from "./powerline.js"; + +const RESET = "\x1b[0m"; +const BOLD = "\x1b[1m"; + +const SE = { + ember40: "#ff8838", + gray60: "#8d877a", + stone60: "#6b6659", + paper: "#f7f5f1", + success: "#24a148", + error: "#da1e28", +} as const; + +type Tone = "muted" | "accent" | "text" | "success" | "warning" | "error"; + +function hexToRgb(hex: string): { r: number; g: number; b: number } { + const cleaned = hex.replace("#", ""); + return { + r: parseInt(cleaned.slice(0, 2), 16), + g: parseInt(cleaned.slice(2, 4), 16), + b: parseInt(cleaned.slice(4, 6), 16), + }; +} + +function ansiFg(hex: string, text: string, bold = false): string { + const { r, g, b } = hexToRgb(hex); + return `\x1b[${bold ? "1;" : ""}38;2;${r};${g};${b}m${text}${RESET}`; +} + +function toneHex(tone: Tone): string { + switch (tone) { + case "accent": + case "warning": + return SE.ember40; + case "success": + return SE.success; + case "error": + return SE.error; + case "text": + return SE.paper; + default: + return SE.gray60; + } +} + +function chip(label: string, value: string, tone: Tone = "text"): string { + return `${ansiFg(SE.gray60, `${label} `)}${ansiFg(toneHex(tone), value)}`; +} + +function join(parts: string[]): string { + return parts.filter(Boolean).join(ansiFg(SE.stone60, " | ")); +} + +function shorten(text: string, max: number): string { + return text.length > max ? `${text.slice(0, Math.max(0, max - 3))}...` : text; +} function getSessionStats(ctx: ExtensionContext) { let cost = 0; @@ -36,7 +87,7 @@ function getSessionStats(ctx: ExtensionContext) { } export function renderFooter( - theme: Theme, + _theme: Theme, footerData: ReadonlyFooterDataProvider, ctx: ExtensionContext, width: number, @@ -44,95 +95,57 @@ export function renderFooter( const git = refreshGitStatus(process.cwd()); const { cost, cxPct } = getSessionStats(ctx); - const leftSegments: Segment[] = []; - - if (git.branch) { - const dirtyIcon = git.dirty ? "dirty" : git.untracked ? "new" : "clean"; - leftSegments.push({ - text: `repo ${git.branch}`, - fg: "white", - bg: git.dirty || git.untracked ? "brightBlack" : "blue", - bold: true, - }); - leftSegments.push({ - text: dirtyIcon, - fg: git.dirty || git.untracked ? "black" : "white", - bg: git.dirty || git.untracked ? "yellow" : "green", - bold: true, - }); - - if (git.added || git.deleted) { - const diffText = `Δ +${git.added}/-${git.deleted}`; - leftSegments.push({ text: diffText, fg: "white", bg: "brightBlack" }); - } - - if (git.lastCommit) { - const msg = - git.lastCommit.message.length > 20 - ? git.lastCommit.message.slice(0, 19) + "…" - : git.lastCommit.message; - leftSegments.push({ - text: `${git.lastCommit.timeAgo} · ${msg}`, - fg: "white", - bg: "brightBlack", - }); - } + const leftParts: string[] = []; + if (git.repo) { + leftParts.push(ansiFg(SE.ember40, git.repo, true)); } else { - leftSegments.push({ text: "SF", fg: "white", bg: "blue", bold: true }); + leftParts.push(`${BOLD}${ansiFg(SE.ember40, "SF")}`); + } + + if (git.branch) { + leftParts.push(chip("branch", git.branch, "muted")); + const state = git.dirty ? "dirty" : git.untracked ? "new" : "clean"; + leftParts.push(chip("state", state, state === "clean" ? "success" : "warning")); + if (git.added || git.deleted) { + leftParts.push(chip("diff", `+${git.added}/-${git.deleted}`, "warning")); + } + if (git.ahead || git.behind) { + leftParts.push(chip("sync", `${git.ahead} ahead ${git.behind} behind`, "warning")); + } + if (git.lastCommit) { + leftParts.push(chip("last", `${git.lastCommit.timeAgo} ${shorten(git.lastCommit.message, 26)}`, "muted")); + } } - // Extension statuses const statuses = Array.from(footerData.getExtensionStatuses().entries()) .sort(([a], [b]) => a.localeCompare(b)) .map(([, text]) => text.trim()) .filter(Boolean); if (statuses.length) { - leftSegments.push({ - text: `status ${statuses.join(" ")}`, - fg: "white", - bg: "brightBlack", - }); + leftParts.push(chip("status", statuses.join(" "), "accent")); } - const rightSegments: Segment[] = []; - + const rightParts: string[] = []; if (ctx.model) { - rightSegments.push({ - text: `model ${ctx.model.provider}/${ctx.model.id}`, - fg: "white", - bg: "blue", - }); + rightParts.push(chip("model", `${ctx.model.provider}/${ctx.model.id}`, "text")); } - if (cost > 0) { - rightSegments.push({ - text: `spent $${cost.toFixed(2)}`, - fg: "black", - bg: "yellow", - }); + rightParts.push(chip("spent", `$${cost.toFixed(2)}`, "warning")); + } + const cxTone: Tone = cxPct >= 85 ? "error" : cxPct >= 60 ? "warning" : "success"; + rightParts.push(chip("ctx", `${Math.round(cxPct)}%`, cxTone)); + + let rightLine = join(rightParts); + const maxRightWidth = Math.max(16, Math.floor(width * 0.55)); + if (visibleWidth(rightLine) > maxRightWidth) { + rightLine = truncateToWidth(rightLine, maxRightWidth, ansiFg(SE.gray60, "...")); } - const cxColor = cxPct >= 85 ? "red" : cxPct >= 60 ? "yellow" : "green"; - rightSegments.push({ - text: `ctx ${Math.round(cxPct)}%`, - fg: cxPct >= 85 ? "white" : "black", - bg: cxColor, - }); - - // Reserve space for right side - const rightLine = renderPowerlineRight(rightSegments, width, theme); const rightWidth = visibleWidth(rightLine); - - const leftLine = renderPowerline( - leftSegments, - Math.max(1, width - rightWidth), - theme, - ); - const leftWidth = visibleWidth(leftLine); - - // Compose: left powerline + spaces to align + right powerline - const gap = Math.max(0, width - leftWidth - rightWidth); + const leftBudget = Math.max(1, width - rightWidth - 2); + const leftLine = truncateToWidth(join(leftParts), leftBudget, ansiFg(SE.gray60, "...")); + const gap = Math.max(1, width - visibleWidth(leftLine) - rightWidth); const line = leftLine + " ".repeat(gap) + rightLine; - return [truncateToWidth(line, width, theme.fg("dim", "…"))]; + return [truncateToWidth(line, width, ansiFg(SE.gray60, "..."))]; } diff --git a/src/resources/extensions/sf-tui/git.ts b/src/resources/extensions/sf-tui/git.ts index f08f228a6..1e0f9148f 100644 --- a/src/resources/extensions/sf-tui/git.ts +++ b/src/resources/extensions/sf-tui/git.ts @@ -1,6 +1,8 @@ import { execFileSync } from "node:child_process"; +import { basename } from "node:path"; export interface GitStatus { + repo: string | null; branch: string | null; dirty: boolean; untracked: boolean; @@ -15,6 +17,20 @@ export interface GitStatus { let cache: GitStatus | null = null; let lastFetch = 0; +function getRepoName(cwd: string): string | null { + try { + const root = execFileSync("git", ["rev-parse", "--show-toplevel"], { + cwd, + encoding: "utf-8", + stdio: ["pipe", "pipe", "ignore"], + timeout: 1500, + }).trim(); + return root ? basename(root) : basename(cwd) || null; + } catch { + return basename(cwd) || null; + } +} + function getLastCommit( cwd: string, ): { timeAgo: string; message: string } | null { @@ -74,6 +90,7 @@ export function refreshGitStatus(cwd: string): GitStatus { if (now - lastFetch < 400 && cache) return cache; lastFetch = now; + const repo = getRepoName(cwd); let branch: string | null = null; try { branch = @@ -85,6 +102,7 @@ export function refreshGitStatus(cwd: string): GitStatus { }).trim() || null; } catch { cache = { + repo, branch: null, dirty: false, untracked: false, @@ -136,9 +154,10 @@ export function refreshGitStatus(cwd: string): GitStatus { const diff = getDiffStats(cwd); const lastCommit = getLastCommit(cwd); - cache = { branch, dirty, untracked, ahead, behind, ...diff, lastCommit }; + cache = { repo, branch, dirty, untracked, ahead, behind, ...diff, lastCommit }; } catch { cache = { + repo, branch, dirty: false, untracked: false, diff --git a/src/resources/extensions/sf/skills/finish-and-verify/SKILL.md b/src/resources/extensions/sf/skills/finish-and-verify/SKILL.md index 6758c6a24..86ba82ab4 100644 --- a/src/resources/extensions/sf/skills/finish-and-verify/SKILL.md +++ b/src/resources/extensions/sf/skills/finish-and-verify/SKILL.md @@ -75,7 +75,7 @@ For native (Rust) changes: ```bash npm run build:native -ldd native/npm/linux-x64-gnu/forge_engine.node | grep -E "not found" || echo "OK" +ldd rust-engine/npm/linux-x64-gnu/forge_engine.node | grep -E "not found" || echo "OK" ``` ## Git Workflow diff --git a/src/resources/extensions/sf/skills/systematic-debugging/SKILL.md b/src/resources/extensions/sf/skills/systematic-debugging/SKILL.md index 8012dd850..de3a6dc14 100644 --- a/src/resources/extensions/sf/skills/systematic-debugging/SKILL.md +++ b/src/resources/extensions/sf/skills/systematic-debugging/SKILL.md @@ -59,7 +59,7 @@ For LLM/provider/transport bugs, capture all of: For native-engine bugs (`forge_engine.node`): ```bash -ldd native/npm/linux-x64-gnu/forge_engine.node 2>&1 | grep -E "not found|missing" +ldd rust-engine/npm/linux-x64-gnu/forge_engine.node 2>&1 | grep -E "not found|missing" ``` When evidence collection splits into independent streams, fan out with parallel subagents (`Explore` for repo search, `Plan` for design analysis). Keep the root-cause + repro doctrine in this skill. diff --git a/tsconfig.extensions.json b/tsconfig.extensions.json index 5d8aad216..dbc2ef636 100644 --- a/tsconfig.extensions.json +++ b/tsconfig.extensions.json @@ -16,7 +16,7 @@ "@singularity-forge/pi-agent-core": ["packages/pi-agent-core/src/index.ts"], "@singularity-forge/pi-tui": ["packages/pi-tui/src/index.ts"], "@singularity-forge/native": ["packages/native/src/index.ts"], - "@singularity-forge/native/*": ["packages/native/src/*/index.ts"], + "@singularity-forge/native/*": ["packages/rust-engine/src/*/index.ts"], "@singularity-forge/mcp-server": ["packages/mcp-server/src/index.ts"], "@singularity-forge/rpc-client": ["packages/rpc-client/src/index.ts"] } diff --git a/web/middleware.ts b/web/middleware.ts deleted file mode 100644 index 299deff3f..000000000 --- a/web/middleware.ts +++ /dev/null @@ -1,80 +0,0 @@ -import { NextResponse, type NextRequest } from "next/server" - -/** - * Next.js middleware — validates bearer token and origin on all API routes. - * - * The SF_WEB_AUTH_TOKEN env var is set at server launch. Every /api/* request - * must carry a matching `Authorization: Bearer ` header. EventSource - * (SSE) connections may use the `_token` query parameter instead since the - * EventSource API cannot set custom headers. - * - * Additionally, if an `Origin` header is present, it must match the expected - * localhost origin to prevent cross-site request forgery. - */ -export function middleware(request: NextRequest): NextResponse | undefined { - const { pathname } = request.nextUrl - - // Only gate API routes - if (!pathname.startsWith("/api/")) return NextResponse.next() - - const expectedToken = process.env.SF_WEB_AUTH_TOKEN - if (!expectedToken) { - // If no token was configured (e.g. dev mode without launch harness), - // allow everything — the server didn't opt into auth. - return NextResponse.next() - } - - // ── Origin / CORS check ──────────────────────────────────────────── - const origin = request.headers.get("origin") - if (origin) { - const host = process.env.SF_WEB_HOST || "127.0.0.1" - const port = process.env.SF_WEB_PORT || "3000" - - // Default: localhost origin for the launched host:port - const allowed = new Set([`http://${host}:${port}`]) - - // SF_WEB_ALLOWED_ORIGINS lets users whitelist additional origins for - // secure tunnel setups (Tailscale Serve, Cloudflare Tunnel, ngrok, etc.) - const extra = process.env.SF_WEB_ALLOWED_ORIGINS - if (extra) { - for (const entry of extra.split(",")) { - const trimmed = entry.trim() - if (trimmed) allowed.add(trimmed) - } - } - - if (!allowed.has(origin)) { - return NextResponse.json( - { error: "Forbidden: origin mismatch" }, - { status: 403 }, - ) - } - } - - // ── Bearer token check ───────────────────────────────────────────── - let token: string | null = null - - // 1. Authorization header (preferred) - const authHeader = request.headers.get("authorization") - if (authHeader?.startsWith("Bearer ")) { - token = authHeader.slice(7) - } - - // 2. Query parameter fallback for EventSource / SSE - if (!token) { - token = request.nextUrl.searchParams.get("_token") - } - - if (!token || token !== expectedToken) { - return NextResponse.json( - { error: "Unauthorized" }, - { status: 401 }, - ) - } - - return NextResponse.next() -} - -export const config = { - matcher: "/api/:path*", -}