Skip to content

Close the skill/binary bootstrap loop: let a CLI-only install set up agent skills #2368

Closed

Description

@malibio

Relationship to #2146 — this is the third direction

#2146 ("One-line install: curl | sh bootstrap with optional GUI prompt, plus skill-triggered install") covered the install story broadly and is closed. It explicitly names two directions between the skill and the binary:

This issue is the third direction: CLI → skill, which neither covers. A user who installs CLI-only has no GUI first launch to trigger skill_setup.rs, and #2146's work runs the other way — it gets them the CLI from the skill, not the skill from the CLI.

#2146 line 102 already anticipates the reconciliation constraint this inherits: "the app's first-launch installer must not clobber a skill the user installed manually, and vice versa." A CLI-driven installer becomes a third writer to the same state and must not clobber either.

The new fact not present in #2146: nodespace-skill-installer is bundled only as a Tauri sidecar, so a CLI-only machine has no copy of it and no release asset to fetch one from. That is what makes this a distribution problem rather than a wiring one.

The loop does not close

The intended path, per nodespace-skill's README, is:

  1. User adds the skill to their agent harness
  2. The skill tells them the CLI and daemon are prerequisites: curl -fsSL https://nodespace.ai/install.sh | sh
  3. …and the thing they installed should then set the skill up properly

Step 3 does not happen. install.sh never touches skills — confirmed, it has no skill handling at all — and the CLI has no subcommand for it. A user who arrives from the skill, follows its own instruction, and installs CLI-only ends up with the skill still in whatever partial state they hand-copied it into.

GUI users do get this: skill_setup.rs::install_skill() runs the real installer during onboarding and reports which agents it installed into. CLI users get nothing — despite being, almost by definition, the people already living in a terminal with these harnesses installed.

The installer already exists — it just is not reachable

packages/skill/ is not a bare file copy. It already does everything a wizard would:

  • agents.ts defines all four harnesses — claude-code, codex, antigravity, opencode — each with a detectionDir, an installDir, and its own shims
  • Detection is directory-presence based (see the test at skill_setup.rs:848, which creates a fake ~/.claude "so the installer detects claude-code")
  • Per-harness integration, not one generic folder: Claude Code gets nodespace-hook.ts, Codex gets nodespace-plugin.ts, OpenCode gets nodespace-plugin.ts, and Antigravity gets its own shim (format TBD by Replace Gemini CLI with Antigravity CLI as a supported PTY agent #2473)
  • parse_installer_output already reads back success lines like ✓ claude-code: installed 2 file(s)

This is not a "build a wizard" issue. Start from packages/skill/, not a blank page.

Why a CLI user cannot reach it today

nodespace-skill-installer is bundled as a Tauri sidecar (tauri.conf.json:49, alongside resources/skill/**/*). It is built during the Tauri jobs and never uploaded as a standalone release asset. So a CLI-only machine has no copy of the installer, and nothing in the release publishes one it could fetch.

That is the actual blocker, and it is a distribution question rather than a coding one.

Design decision needed

Options, roughly in increasing order of effort:

  1. Upload nodespace-skill-installer as a release asset, and have install.sh download and run it after the CLI install. Smallest change; matches how install.sh already fetches other artifacts.
  2. A nodespace skill install subcommand on the CLI itself. Most discoverable — it shows up in --help alongside the existing uninstall — and it is the only option that handles the re-run case below. Needs the installer available to the CLI, so it likely subsumes option 1.
  3. Reimplement detection in install.sh. Rejected: it would be a second copy of agents.ts's harness table in another language, and this repo has been bitten by exactly that kind of duplicate-that-drifts more than once (see Install/release channel drift: GUI discovery, unsynced install.sh, pkg KeepAlive bug, incomplete cask zap #2366).

There is a real argument for option 2 beyond the initial install: today nothing handles a harness installed later. Add Codex a month after installing NodeSpace and nothing notices or offers to extend the skill to it — install_skill only runs during GUI onboarding. A subcommand makes that a one-liner.

Note the installer has a compiled-binary path (resolve_compiled_installer_path) with a script fallback, and a test asserting the compiled variant works with "zero runtime on PATH" — so a shell caller can likely invoke it without requiring Bun, but that should be verified rather than assumed.

Which repo each change lands in

Three repos are involved, and only two are editable:

Repo Role Editable
nodespace-core Source of truth: the CLI, the app, packages/skill/ (agents.ts, shims, the nodespace-skill-installer sidecar) Yes
nodespace-skill Generated publish target. Rendered from packages/skill and pushed by this repo's release pipeline. A hand edit there is overwritten, not merged, on the next release No
nodespace-website Hosts install.sh as a committed static file Yes

This issue belongs in nodespace-core because everything it touches — the harness table, the shims, the sidecar bundling, any new subcommand — lives in packages/skill/. Nothing here changes SKILL.md, references/cli.md, or marketplace.json, which is all nodespace-skill holds.

Worth weighing when choosing between the options above: option 1 spans two repos (upload the asset from nodespace-core, add the fetch logic to nodespace-website's install.sh), while option 2 stays entirely within nodespace-core. Given that cross-repo sync is precisely what is already broken in #2366 item 2 — a missing token has left install.sh unsynced for five releases — keeping this change inside one repo is worth something.

Proposed UX

Detected agent harnesses:
  - Claude Code
  - Antigravity CLI

Install the NodeSpace skill into them? [Y/n]

Follow install.sh's existing conventions: ns_prompt_gui() already has the TTY-detection pattern, and the same no-TTY rule should apply — never prompt without a terminal, pick a documented default, and say which was chosen.

Acceptance criteria

  • Decide between options 1 and 2 above (or both) and record why
  • A CLI-only install can install the skill without the GUI app present
  • Detection reuses packages/skill's agents.ts — no second copy of the harness table
  • All four harnesses supported, with their per-harness shims, matching what the GUI path installs
  • Non-interactive behaviour defined: no prompt without a TTY, documented default, chosen default stated in output
  • A flag for non-interactive use, mirroring the existing --gui / --no-gui pair
  • Re-running is safe and idempotent, and picks up harnesses installed since last time
  • Output reports which harnesses were installed into, and which were detected but skipped
  • Skipping is graceful when no harness is detected — no error, no empty prompt
  • Verified end to end on a machine with more than one harness present

Context

Found while tracing how the skill and the binary are meant to bootstrap each other. Related to #2366 (install/release channel drift), but distinct: the four items there are artifacts that drifted out of sync, while this is a path that was never wired up, and it needs a design decision rather than a mechanical fix.

Harness table update: the fourth harness was Gemini CLI at the time this issue was filed; Google discontinued Gemini CLI for individual/consumer accounts, so #2473 replaces it with Antigravity CLI (agy) in packages/skill/src/agents.ts. This issue's harness table and example output above have been updated to match — implementation here should target whatever agents.ts contains once #2473 lands (or build the shim format alongside it if worked concurrently).

Activity

  1. malibio commented on Sep 8, 2026

    @malibio
    CollaboratorAuthor

    Baseline (frontend, Happy-DOM): Test Files 238 passed (238), Tests 4925 passed (4925). Duration 144.51s.

  2. self-assigned this
    on Sep 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions