Add neon plugins to install the Neon agent plugin into coding agents - #479
Merged
Conversation
Use project/user in the table and summary, list only agents that can install at the current scope, and name a silent timeout instead of leaking the child argv.
Argv assertions use the GitHub shorthand rather than the production constant, so a source change fails the suite. The README notes Windows Cursor uses extensions instead of ~/.claude/plugins.
Kill the npx process group so a hung plugins grandchild cannot outlive the 120 second bound, and say so even when the child already printed a banner. Deduped Claude targets list every requested agent in the table.
detached disables execa's parent-exit cleanup, so SIGINT and SIGTERM kill the process group before the CLI exits. The timeout test's grandchild now inherits stdio, which is the hang that used to outlive execa's timeout.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
The Neon agent plugin ships from the
neondatabase/agent-skillsmarketplace. Installing it today means knowing that Vercel'spluginsCLI exists, knowing the marketplace source string and knowing which-ttarget name each coding agent goes by:All three of those are facts about Neon's packaging, so the CLI is the place that should hold them. It already holds the equivalents for its other agent integrations:
neon mcpwrites the MCP server config andneon skillsinstalls the Neon skills. The plugin was the piece still left to the user.The command
neon pluginsinstalls the Neon plugin (neon-postgres) into coding agents by runningnpx plugins addonce per mapped target. It does not call the Neon API.On a TTY,
neon pluginsasks which agents should get the plugin, then prints this summary:followed by the confirm question
Install the Neon plugin into these agents?, which defaults to yes. Answering no printsAborted. Nothing was written.and exits 0.Detected agents start selected. At project scope detection reads project-folder markers such as
.cursor; with--globalit detects agents installed on the machine.Flags
-y,--yesfalse--globalfalse-a,--agent <name>There is one plugin, so there is no plugin picker and no
--plugin. There is noupdate,discoverortargetssubcommand;neon plugins updateis rejected withUnknown command: update.--agenton its own is enough. It names the agents and the plugin and scope are already fixed, so nothing is left for-yto answer:Without a TTY and without either flag, the command refuses rather than guessing:
Scope
Default scope is
project.--globalisuser. Those two words are the scope field the plugins CLI records against the install; the files still land in the agent's own cache either way. On macOS and Linux, Cursor and Claude Code cache the plugin under~/.claude/plugins. On Windows, Cursor installs into Cursor extensions.Scope shows up as
projectoruserin the confirm summary, in the table and in the success line.-s userand-s projectare the plugins CLI's vocabulary and stay inside the child invocation.Agent mapping
--agent-ttargetcursorcursorclaude-codeclaude-codeclaude-desktopclaude-codecodexcodexvscodevscode--globalonlygithub-copilot-cligithub-copilot--globalonlygrok-buildgrok--globalonlyClaude Desktop and Claude Code share a target, so naming both is one spawn.
neon skillscollapses the same pair. Here the row keeps both requested names in itsAgentcell (claude-code, claude-desktop), so the table accounts for everything that was asked for.The three user-level-only agents are dropped from project scope. When something else is selected they are a warning and the rest installs:
When nothing else is selected, it is an error that names the way out:
The supported-agent list is filtered by the scope in play, so the suggestions are always installable as invoked:
--agent *gets its own message:neon plugins does not accept --agent *. Pass --agent <name> for each coding agent, or omit --agent to use detected agents.mcporteris a known MCP name with no plugins mapping, so it is skipped with a warning.The child process
One spawn per mapped target:
The marketplace source is fixed in the code. Help output and the retry line are written in terms of
neon plugins, so the source reaches the user only when the child prints it in its own failure dump.The child environment drops
DISABLE_TELEMETRYandDO_NOT_TRACK, so the plugins CLI records its own install while Neon records the parent command. It also dropsNEON_API_KEYin any casing, since the child talks to a marketplace and has no use for a Neon credential. Both rules matchneon skills, which builds its child environment the same way.neon pluginsskips Neon authentication and.neoncontext enrichment, the same wayneon skillsdoes. It runs in an unlinked directory and while signed out.Failures
The table row carries a fixed
plugins CLI failed. The child's output is printed once on stderr, followed by a retry line that is aneoncommand:On a partial failure the retry names only the agents that failed.
--globalis carried through:A missing
npxgets its own message and names the fix:Timeout
Each target gets 120 seconds.
npxleaves the plugins CLI running as a grandchild. execa's owntimeoutsignalsnpxand then keeps waiting on the stdio pipe, which the grandchild still holds, so a hang there never settles and the bound does nothing. The child is therefore spawneddetachedand the timer kills the whole process group (taskkill /pid <pid> /T /Fon Windows). Detached also turns off execa's parent-exit cleanup, so Ctrl-C and SIGTERM kill that same tree before the CLI exits.A child that printed a banner before it hung leaves output behind. That output on its own would read as an ordinary failure, so the timeout wins the message and the child's text follows it:
With nothing printed, the message is
plugins CLI timed out after 120 seconds.Also in here
isPluginsCommandincontext.ts, used by the auth middleware and byenrichFromContextto skip both forplugins.pluginsadded toNO_SUBCOMMANDS_VERBSinindex.ts, so the help-fallback middleware lets a bareneon pluginsrun its handler instead of printing help.neonandneonctl.Verification
Built with
pnpm exec tsdowninpackages/cli, over based0101bf.Live install with the built CLI and real
npx, into an isolatedHOMEand a scratch project containing.cursor.installed_plugins.jsonunder thatHOMErecordsneon-postgres@neonversion1.1.2with"scope": "project".known_marketplaces.jsonrecords theneonmarketplace pointing atneondatabase/agent-skills. Every terminal excerpt above is copied from runs of that build.pnpm exec vitest run src/plugins src/commands/plugins.test.ts src/context.test.ts: 84 tests across 6 files, all passing. What they cover:-yinstalls into agents detected from project-folder markers, with argv exactly-y plugins add neondatabase/agent-skills -t cursor -s project -yDISABLE_TELEMETRY, noDO_NOT_TRACKand noNEON_API_KEY, in any casing--agentinstalls without-y; a non-TTY run with neither flag spawns nothing at allclaude-codeandclaude-desktopcollapse to one spawn with both names in the row--agent *are rejected, with a supported list that follows the scope--pluginandplugins updateare rejected before anything spawns.neonenrichment are skipped even with a.neonfile present--globalsends-s user;--agent vscodeneeds it and fails without spawning when it is absent--globalfailure keeps--globalCommand failed with exit codenpxis named--helpshows--agentand--global, without--plugin,plugins update, the marketplace source or-s user/-s projectnpxthat spawned a grandchild holding the pipe: with a 400 ms bound the run rejects withtimed out after 1 secondin under 4 secondsNot verified:
taskkill /T /Fbranch of the process-group kill has no test. The Cursor extensions install path is the plugins CLI's behavior rather than something this branch controls.For your attention
neon pluginsat a different marketplace is a code change, with no flag or env override.npx, so the first run downloads thepluginspackage. On a machine without Node.js it fails with the named error and installs nothing.npxcache spends part of that budget before the plugins CLI starts.neon plugins -yin a project with VS Code and Cursor gets a warning about VS Code and a success line about Cursor.update. Reinstalling means running the command again; the marketplace entry is written withautoUpdate: true, which the plugins CLI owns.neon skillsandneon pluginsinstall overlapping content through different mechanisms. Nothing in this branch reconciles them or warns when both are installed.