Skip to content

feat(cli): install the Solana dev skills on first scaffold - #761

Open
michael-moffett wants to merge 4 commits into
solana-foundation:mainfrom
caliperforge:feat/scaffold-dev-skill-install
Open

feat(cli): install the Solana dev skills on first scaffold#761
michael-moffett wants to merge 4 commits into
solana-foundation:mainfrom
caliperforge:feat/scaffold-dev-skill-install

Conversation

@michael-moffett

@michael-moffett michael-moffett commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

Implements #567.

On a first surfpool start, the run that scaffolds a txtx.yml, this runs the command from the issue:

npx -y skills@1.5.22 add https://github.com/solana-foundation/solana-dev-skill --skill "*" -y

The command in the issue is unversioned; this pins it to skills@1.5.22 so a first scaffold runs a known version rather than whatever is latest that day.

Fire and forget as the issue asks: the scaffold spawns the child and never waits on it. The install runs behind the confirmation, so declining the prompt starts nothing, and a later start is no longer a scaffold so it cannot start a second one.

Four tests cover it: the invocation, the working directory, a declined confirmation building nothing to spawn, and the spawn driven through a missing binary, a non-zero exit and a hang. The last shells out to sh, so it is gated #[cfg(unix)].

+152 / -0, one file. cargo test -p surfpool-cli: 44 passed, 0 failed.

@greptile-apps

greptile-apps Bot commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

The PR adds a fire-and-forget Solana development-skill installation after successful first-time scaffolding.

  • Pins the npm installer to skills@1.5.22.
  • Runs the installer in the scaffolded project directory.
  • Suppresses installation after declined confirmation and adds command, directory, cancellation, and non-blocking tests.

Confidence Score: 3/5

The PR is not yet safe to merge because first-time scaffolding still automatically installs skill content from a mutable repository revision.

The npm installer is now pinned and the prior control-flow issues are fixed, but the command still passes an unversioned GitHub repository URL, so the installed skill content can change or be compromised independently of this release.

Files Needing Attention: crates/cli/src/scaffold/mod.rs

Important Files Changed

Filename Overview
crates/cli/src/scaffold/mod.rs Adds the detached first-scaffold skill installer and fixes the previously reported package pinning, working-directory, cancellation, and ordering issues.

Reviews (4): Last reviewed commit: "fix(cli): drop the install from the exis..." | Re-trigger Greptile

Comment thread crates/cli/src/scaffold/mod.rs Outdated
Comment on lines +142 to +143
let mut command = Command::new("npx");
command.args(["-y", "skills", "add", DEV_SKILL_REPO, "--skill", "*", "-y"]);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 security Unpinned package executes automatically

If the latest npm release of skills is compromised, the first-scaffold path resolves and executes that mutable package through npx -y without user consent, causing arbitrary code to run with the Surfpool user's permissions. How this was verified: The changed command supplies an unversioned package name to npx -y and spawns it automatically before any user prompt.

Comment thread crates/cli/src/scaffold/mod.rs Outdated
Comment on lines +141 to +145
fn dev_skill_install_command() -> Command {
let mut command = Command::new("npx");
command.args(["-y", "skills", "add", DEV_SKILL_REPO, "--skill", "*", "-y"]);
command
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Installer targets inherited directory

When the configured manifest's parent differs from the process launch directory, this command inherits the process cwd instead of using base_location, causing project-local skill files to be installed outside the newly scaffolded project.

Comment thread crates/cli/src/scaffold/mod.rs Outdated
Comment on lines +240 to +241
// start; #567 asks for the skills here. Ahead of the prompts, to overlap.
spawn_dev_skill_install(dev_skill_install_command());

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Installation precedes scaffold success

When an early prompt is canceled or a later scaffold operation fails, the detached installer has already started and continues modifying the environment, causing an unsuccessful scaffold to leave partial side effects and a retry to start another installation.

}
}

const DEV_SKILL_REPO: &str = "https://github.com/solana-foundation/solana-dev-skill";

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 security Installed repository remains mutable

If the repository's default revision is compromised or unexpectedly changed before a first scaffold, the automatic command installs every skill from that mutable revision without visible output, persisting repository-controlled instructions that can direct subsequent agent activity with the user's permissions.

How this was verified: The command pins the npm installer but passes a bare GitHub repository URL without an immutable revision.

Runs the command from solana-foundation#567 on the run that scaffolds a txtx.yml. The child is
spawned with all three stdio handles nulled and is never waited on, so a slow,
failing or absent install cannot affect startup. It is reaped on a detached
thread so it does not sit defunct.

Two tests cover the invocation and all three failure modes: a missing binary,
a non-zero exit, and a hang.
Three follow-ups on the first-scaffold install, from review of the
previous commit.

The invocation carried no version, so a first scaffold ran whatever the
registry called latest at that moment. It is pinned to skills@1.5.22.
An exact version is the only form that resolves the same way twice; a
range still floats to the newest release inside it.

The child inherited the process working directory rather than the
project being scaffolded. Those differ whenever -m points at a manifest
outside the current directory, so the skills could land somewhere other
than the project. It now runs in the manifest's directory.

The spawn sat at the top of scaffold_iac_layout, so a cancelled prompt
or a failure part way through left an install running behind a scaffold
that never finished, and the next start began a second one. It now runs
only once the scaffold has finished, from either exit that reaches that
point.

One added test pins the working directory. The existing test that pins
the invocation now pins the version with it, and fails on a range or a
bare package name.
Declining the deployment prompt printed "Deployment canceled" and fell
through to the install, which spawned against the project anyway. Route
the install through the confirmation so a decline builds no command, and
replace the comment above it, which claimed every exit on that path was
an Err.
@michael-moffett

michael-moffett commented Aug 24, 2026

Copy link
Copy Markdown
Contributor Author

Hello! I corrected the last one. The cancel path: Confirm::interact() returns Ok(false) on a decline rather than an error, so it fell through and the install ran anyway. Guarded now, with a test.

DEV_SKILL_REPO I've left alone: it has no revision, so the installer is pinned and the content isn't. I'll add it if you want it. Thanks!

That call site sat in the arm taken when runbooks/deployment/main.tx is
already present, and returned before the confirmation is bound further
down, so on that path the install started with nothing to decline.

It is removed rather than routed through a prompt because the arm exists
to return early. A project with a runbooks tree and no txtx.yml now
scaffolds the manifest and installs nothing; the only install left is
the one behind the confirmation.

@MicaiahReid MicaiahReid left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wouldn't want a CLI I use to automatically install a skill without asking me. At a minimum this should:

  1. Check if the skill is already installed and not prompt anything if so
  2. If not installed, ask the user if they want it instealled
  3. Install if yes, never ask again if no

If you want to make these changes, lmk. If this is beyond the scope you originally signed up for, lmk and I'll add these notes to the issue

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.

2 participants