Skip to content

Native modules: Ruby, PHP, .NET, Swift, Zig and Deno (#364) - #370

Draft
raiseCatError wants to merge 5 commits into
release/v0.18.0from
feature/native-runtimes
Draft

raiseCatError wants to merge 5 commits into
release/v0.18.0from
feature/native-runtimes

Conversation

@raiseCatError

@raiseCatError raiseCatError commented Oct 10, 2026 •

Copy link
Copy Markdown
Owner

Native runtime modules for #364 (under #305), based on #345. Six new modules in the bundled nmsh.project pack: Ruby, PHP, .NET, Swift, Zig, Deno. This PR is the canonical owner of the native-runtime code; #368 carries an older copy of Ruby/PHP (see below).

  • Requested version from the project's own files: .ruby-version/Gemfile, .php-version/composer.json, global.json, .swift-version/Package.swift, build.zig.zon/.zigversion, .dvmrc, and .tool-versions/mise for all (asdf's dotnet-core included).
  • Active version from the executable's install location (Homebrew, mise, asdf, rbenv, RVM, chruby…), never by running the tool; a binary inside the workspace is refused; unknown stays unknown.
  • Minimum versions (global.json without rollForward: disable, swift-tools-version, minimum_zig_version) show as ≥x and are not judged as mismatches.
  • Collected only on demand, with the Context Engine's cache, timeout and provenance.
  • Icons for all six (code points cross-checked against Starship's Nerd Font preset and Nerd Fonts' glyph names) and recommendations from each runtime's project files.

Security review (this round)

  • Gemfile ReDoS (fixed in 8664941, verified): the old ^\s*ruby\s+… with m rescanned every line start: 730–817 ms on a 60 KB hostile Gemfile vs. 0.3 ms now. The regression test now asserts the parsed value and a 250 ms bound, and fails against the old pattern (checked).
  • Package.swift: read from the first line only, as SwiftPM does; manifests over 4 KiB were silently skipped before (real packages often are), now read up to the standard bound.
  • go.mod/go.work: directives can no longer match across line breaks (CRLF and trailing comments allowed).
  • build.zig.zon: standard 64 KiB bound instead of 16 KiB.
  • Other parsers reviewed: all reads are bounded, symlink/FIFO-refusing (readMetadataText), values are sanitized at the fact boundary (controls, bidi). The hostile-workspace test now plants php/dotnet/swift/zig bait and hostile Ruby/.NET/Swift/Zig/Deno/PHP metadata, and asserts every runtime's active version stays unknown.
  • The planted-binary tests previously passed home as the workspace, which by design disables the workspace boundary, and the planted binaries had no install layout; they now use a real home and a layout that would report 6.6.6 if the boundary failed.

Overlap with #368 (for the integration branch)

src/context/capabilities/runtimes.ts (Ruby/PHP), src/context/packs/builtin/nmsh.project.json (ruby/php modules), tests/contextCapabilities.test.ts, tests/moduleManager.test.ts (module count). #368's copy still has the quadratic Gemfile regex; take this PR's version. Details: docs/development/context-workspace-handoff.md (handoff PR).

Verification

  • Targeted: typecheck; contextCapabilities, contextPacks, moduleManager, contextHostileWorkspace, contextKernel, agentContext all pass.
  • Full local npm run verify at 417d105: 2111/2115; the four failures (live completion capture, a screensaver frame budget, a motion test, fuzzy capture) pass in isolation (63/63) and came from machine load (load average 24–48). An earlier run also failed live fish: keys typed the moment a read finishes (Flaky: Fish can lose keys typed right after a handed-over read returns #356, workflow track), which passes in isolation.
  • CI for 417d105: see checks. The earlier run's Tests (ubuntu-24.04, 2/2) was cancelled (a hung live test after ~9 minutes), so it was not a green result.

Stacked follow-ups: #372 (Elixir, Dart, Flutter) → #373 (build module).

…e project's own files and the install location

Same pattern as the existing runtimes: the requested version from the project's files (.ruby-version/Gemfile, .php-version/composer.json, global.json, .swift-version/Package.swift, build.zig.zon/.zigversion, .dvmrc, plus .tool-versions and mise), the active version from the install path, never by running the tool. Minimum versions (global.json, swift-tools-version, minimum_zig_version) are floors and are not judged as mismatches. A binary inside the workspace is not inspected.
- Package.swift: swift-tools-version comes from the first line only, as
  SwiftPM reads it, and manifests larger than 4 KiB are no longer skipped.
- build.zig.zon: read up to the standard 64 KiB metadata bound.
- go.mod/go.work: directives stay on one line (CRLF and a trailing comment
  allowed); a match can no longer span line breaks.
- .tool-versions: asdf's .NET plugin name (dotnet-core) is recognized.
- Install layouts: RVM and chruby/ruby-install rubies report their version.

The linear-time regression now asserts the parsed values and a 250 ms bound
that the previous Gemfile pattern (about 0.8 s on the same input) fails. The
hostile-workspace test plants php, dotnet, swift and zig bait, hostile Ruby,
.NET, Swift, Zig, Deno and PHP metadata, and checks every runtime's active
version stays unknown for workspace binaries.
…re recommended from their project files

The six modules named icon ids the core icon set did not have, so they
showed their text label even with Nerd icons on. The code points match
Starship's Nerd Font preset and Nerd Fonts' glyph names (provenance in
icons.ts). Each module is now recommended when its own project files are
present (Gemfile, composer.json, global.json, Package.swift, build.zig,
deno.json and their version files); recommendations still enable nothing.

A test keeps every first-party pack module on a core icon.
…ent the six modules

The planted-binary checks passed home as the workspace, and a workspace
that contains home is deliberately not a workspace (it holds the user's own
tools), so the refusal path was never reached; the planted binaries also had
no install layout to report. They now use a home outside the workspace and
a Homebrew/rbenv-shaped layout that would report 6.6.6 if the boundary did
not hold, and assert the refusal note.

The Context Modules guide lists the new capabilities, what each reads, and
which declarations are floors.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant