From 3f0d3700ade4467b7402c57d9c87c71dcf7b70c2 Mon Sep 17 00:00:00 2001 From: Cesar-M-Diaz Date: Mon, 6 Jul 2026 10:04:27 +0200 Subject: [PATCH 1/4] remove staging flag, unnecesary docs and improve readme --- README.md | 78 ++-- docs/antigravity-research.md | 10 +- docs/qa-guide.md | 370 ------------------ packages/core/README.md | 8 +- packages/core/src/auth/mcp-url.ts | 4 +- packages/core/src/auth/token-validator.ts | 2 - packages/core/src/cli.ts | 9 +- packages/core/src/index.ts | 5 +- packages/core/src/types.ts | 2 +- .../auth/accounts-url-override.test.ts | 22 +- .../integration/auth/auth-manager.test.ts | 55 --- .../core/test/integration/cli-help.test.ts | 3 +- .../core/test/integration/installer.test.ts | 8 +- .../test/unit/auth/token-validator.test.ts | 6 +- packages/pi-plugin/README.md | 4 +- scripts/materialize-github-marketplace.mjs | 4 +- scripts/mcp-wrapper.js | 6 +- scripts/plugin-generators.mjs | 6 +- 18 files changed, 77 insertions(+), 525 deletions(-) delete mode 100644 docs/qa-guide.md diff --git a/README.md b/README.md index f05f132..d338d77 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # nsolid-plugin -Cross-harness plugin distribution for NodeSource AI skills and MCP servers. The repo keeps one canonical skill source, a shared core CLI/setup package, a real Pi package, and generated native plugin artifacts for Claude, Codex, and Antigravity. OpenCode remains CLI-only until its plugin distribution model is clearer. +N|Solid Plugin installs NodeSource AI skills and MCP servers into Claude Code, Codex CLI, OpenCode, Antigravity CLI, and Pi Agent. The repo keeps one canonical skill source, a shared core CLI/setup package, a real Pi package, and generated native plugin artifacts for Claude, Codex, and Antigravity. OpenCode remains CLI-only until its plugin distribution model is clearer. ## Supported harnesses @@ -8,12 +8,18 @@ Cross-harness plugin distribution for NodeSource AI skills and MCP servers. The |---|---|---| | **Claude Code** | Root GitHub marketplace/plugin + `.claude-plugin/plugin.json` | Native plugin install, then explicit setup | | **Codex CLI** | Root GitHub marketplace/plugin + `.codex-plugin/plugin.json` | Native plugin install, then explicit setup | -| **OpenCode** | CLI-only (user-level skills + MCP config) | `nsolid-plugin setup --harness opencode` (auth + direct install) | +| **OpenCode** | CLI direct install (user-level skills + MCP config) | `nsolid-plugin setup --harness opencode`, then `nsolid-plugin install --harness opencode` | | **Antigravity CLI** | Root GitHub plugin + `plugin.json` | `agy plugin install `, then explicit setup | | **Pi Agent** | npm package + `pi.skills` | `pi install npm:nsolid-pi-plugin`, `nsolid-plugin setup --harness pi`, then `pi install npm:pi-mcp-adapter` | No harness relies on npm `postinstall` hooks. See `openspec/changes/cross-harness-plugin-installer/design.md` and `openspec/changes/cross-harness-plugin-installer/specs/installation-and-auth.md` for the full design rationale. +## What it includes + +- 16 Node.js operations skills for memory leaks/spikes, CPU spikes, event loop delays, traces, saved N|Solid assets, vulnerability analysis, dependency audits, package and Node.js upgrades, benchmarks, SBOM generation, and optimization validation. +- Three MCP servers: `nsolid-console`, `ns-benchmark`, and `ncm`. +- Explicit OAuth setup against NodeSource accounts. You need a NodeSource account with access to the target N|Solid organization. + ## Structure ```text @@ -41,25 +47,8 @@ Skills are canonical in the repository-root `skills/` directory. The repo root i | **Codex** | Root plugin | Native marketplace/plugin install; `setup` for auth | | **Antigravity** | Root plugin | `agy plugin install `; `setup` for auth | | **Pi** | Pi npm package (`pi.skills`) | Pi package owns skills; `setup` writes auth/MCP config | -| **OpenCode** | CLI-only harness copy | `setup` authenticates and copies skills/MCP config locally; `install` is no-browser fallback | - - -## Quick start - -```bash -pnpm install -pnpm build -pnpm test -pnpm lint -``` - -If `nsolid-plugin` is not installed on your `PATH` from a release package, use the source CLI after building: +| **OpenCode** | CLI direct install | `setup` authenticates; `install` copies skills and writes MCP config | -```bash -node packages/core/dist/src/cli.js --help -``` - -Replace `nsolid-plugin` with `node packages/core/dist/src/cli.js` in the examples below when running directly from this checkout. ## Authentication @@ -77,7 +66,7 @@ On setup: 4. An `mcpUrl` is derived from the callback's `consoleId` (or via string transform from `consoleUrl`). 5. Credentials are stored at `~/.agents/.nodesource-auth.json` with mode `0600`. -**What is stored:** `serviceToken`, `organizationId`, `saasToken`, `consoleUrl`, `mcpUrl`, `expiresAt`, `permissions`, and the `accountsUrl` auth origin used to mint/validate the token (useful for staging QA). +**What is stored:** `serviceToken`, `organizationId`, `saasToken`, `consoleUrl`, `mcpUrl`, `expiresAt`, `permissions`, and the `accountsUrl` auth origin used to mint/validate the token. **Token lifecycle:** Expired credentials trigger re-authentication during explicit setup/login. Runtime MCP wrappers fail with an actionable `Run: nsolid-plugin setup --harness ` message if credentials are missing or expired. Credentials are shared across harnesses. @@ -87,17 +76,35 @@ On setup: ## Per-harness install -The GitHub marketplace flow installs from the repository root. +Requirements: Node.js `>=22.3.0`, the target harness CLI, and a NodeSource account with access to your N|Solid organization. + +Install the stable CLI once: -The setup step requires the `nsolid-plugin` CLI on your PATH: +```bash +npm i -g nsolid-plugin +``` + +Or invoke commands without a global install: ```bash -npm i -g nsolid-plugin # stable release -# during pre-release: -npm i -g nsolid-plugin@next +npx -y nsolid-plugin setup --harness +npx -y nsolid-plugin install --harness ``` -Or invoke it once-off with `npx -y nsolid-plugin setup --harness ` (`npx -y nsolid-plugin@next ...` during pre-release). +The setup step requires a NodeSource account and writes shared credentials to `~/.agents/.nodesource-auth.json`. The install step is needed only for direct CLI installs such as OpenCode or fallback/repair installs. + +### Direct CLI install + +`nsolid-plugin install --harness ` is not a native harness plugin install. It directly adds N|Solid skills and MCP server config to the selected harness. Run `setup` first so MCP server credentials are available: + +```bash +nsolid-plugin setup --harness +nsolid-plugin install --harness +``` + +Without a global install, use `npx -y nsolid-plugin setup --harness ` and `npx -y nsolid-plugin install --harness `. + +Use direct CLI install as the primary install path for OpenCode. For Claude Code, Codex CLI, and Antigravity CLI, prefer the native plugin commands below and keep `nsolid-plugin install` for fallback or repair. For Pi Agent, skills come from `nsolid-pi-plugin`; the CLI writes Pi MCP config only. ### Claude Code @@ -109,8 +116,6 @@ nsolid-plugin setup --harness claude Claude installs plugins through marketplaces. The repository root includes `.claude-plugin/marketplace.json`, so GitHub install works directly. If marketplace/local plugin install is unavailable, `nsolid-plugin install --harness claude` is the fallback direct installer and does not open a browser. -> `nsolid-plugin` must be available on your PATH. Once published, install it globally (`npm i -g nsolid-plugin`) or invoke it via npx (`npx -y nsolid-plugin setup --harness claude`). During the pre-release window, use the prerelease tag (`npx -y nsolid-plugin@next ...` or `npm i -g nsolid-plugin@next`). - ### Codex CLI ```bash @@ -125,11 +130,10 @@ Codex is marketplace-owned. A NodeSource-owned Git/local marketplace can be used ```bash nsolid-plugin setup --harness opencode +nsolid-plugin install --harness opencode ``` -OpenCode is CLI-only. `setup` authenticates with NodeSource, copies skills directly to `~/.config/opencode/skills/`, and writes MCP servers to `~/.config/opencode/opencode.jsonc` under the top-level `mcp` key. It does not use shared `~/.agents/skills/`, avoiding cross-harness skill leakage and Pi package-owned skill collisions. - -Use `nsolid-plugin install --harness opencode` only as a no-browser fallback/repair command; MCP config is written only when valid credentials already exist. +OpenCode does not use this repository as a native plugin. `setup` authenticates with NodeSource. `install` copies skills directly to `~/.config/opencode/skills/` and writes MCP servers to `~/.config/opencode/opencode.jsonc` under the top-level `mcp` key. It does not use shared `~/.agents/skills/`, avoiding cross-harness skill leakage and Pi package-owned skill collisions. ### Antigravity CLI @@ -169,6 +173,14 @@ The package declares its skills via `pi.skills`, so Pi owns/lists them from the > **Using `@0xkobold/pi-mcp` instead?** It is an alternative adapter, but it reads `~/.0xkobold/mcp.json` in a different (`servers[]`) format and does **not** pick up the config this plugin writes (`~/.pi/agent/mcp.json`). You would need to create and maintain a separate `~/.0xkobold/mcp.json` manually. Prefer `pi-mcp-adapter` for automatic setup. +## Verify install + +```bash +nsolid-plugin doctor --harness +``` + +In Claude Code, Codex CLI, OpenCode, and Antigravity CLI, check the harness UI for N|Solid entries with `/skills` and `/mcp`. In Pi Agent, run `pi list` and confirm `nsolid-pi-plugin` and `pi-mcp-adapter` are installed. + ## Development ### Build @@ -262,7 +274,7 @@ nsolid-plugin restore --harness --backup ~/.agents/.config-backup/` is a no-browser fallback/direct installer. Claude, Codex, and Antigravity should normally use native GitHub plugin install from the repository root. OpenCode uses `nsolid-plugin setup --harness opencode` as its primary onboarding command because it authenticates and then performs the direct install; `install --harness opencode` is for repair/offline staging. Pi is package-owned: `pi install npm:nsolid-pi-plugin` installs skills, while `nsolid-plugin install/setup --harness pi` only writes Pi MCP config. +`nsolid-plugin setup --harness ` authenticates with NodeSource and may open a browser. `nsolid-plugin install --harness ` never opens a browser; it directly writes N|Solid skills and MCP config for a harness. Claude, Codex, and Antigravity should normally use native GitHub plugin install from the repository root. OpenCode uses the explicit two-step CLI path: `setup`, then `install`. Pi is package-owned: `pi install npm:nsolid-pi-plugin` installs skills, while `nsolid-plugin install/setup --harness pi` only writes Pi MCP config. ### Verbose logging diff --git a/docs/antigravity-research.md b/docs/antigravity-research.md index 2266db1..86a4cb0 100644 --- a/docs/antigravity-research.md +++ b/docs/antigravity-research.md @@ -1,9 +1,9 @@ -# Antigravity CLI — Plugin Research (Task 38) +# Antigravity CLI — Plugin Research (Task 38, archival) -> **Amendment (2026-06-24):** The shared core was renamed `@nodesource/plugin-core` → `nsolid-plugin`. The `scripts/install.js` approach described here was superseded — Antigravity now installs directly from the GitHub repository root. See `docs/qa-guide.md` §9. +> **Amendment (2026-06-24):** The shared core was renamed `@nodesource/plugin-core` → `nsolid-plugin`. The `scripts/install.js` approach described below was superseded — Antigravity now installs directly from the GitHub repository root with `agy plugin install https://github.com/NodeSource/nsolid-plugin.git`, followed by explicit `nsolid-plugin setup --harness antigravity`. **Status:** Complete (June 2026). -**Verdict:** Antigravity has no install-time/session-start hook, so the plugin must ship a one-time `scripts/install.js` that the user runs manually. That script copies the plugin directory for discovery and invokes the shared core installer so auth, MCP config, and skills work consistently with the other harnesses. +**Verdict at the time:** Antigravity had no install-time/session-start hook, so the plugin design originally used a one-time `scripts/install.js` that the user ran manually. This design is now archival; current installs use the repository root as the native Antigravity plugin. --- @@ -67,7 +67,9 @@ Antigravity does **not** interpolate environment variables in `mcp_config.json` --- -## 5. Chosen design for Task 26 +## 5. Superseded design for Task 26 + +This section documents the original design only. Do not use it for current installs; use `agy plugin install https://github.com/NodeSource/nsolid-plugin.git` and then `nsolid-plugin setup --harness antigravity`. - Ship a native `plugin.json`. - Ship `scripts/install.js` that the user runs once. diff --git a/docs/qa-guide.md b/docs/qa-guide.md deleted file mode 100644 index 9d5753d..0000000 --- a/docs/qa-guide.md +++ /dev/null @@ -1,370 +0,0 @@ -# N|Solid Plugin QA Guide - -Use from the repository root. Commands are macOS/Linux shell commands unless noted. - -This guide is local/pre-release QA first. Production/release-artifact variants are included where useful, but the default path is to build and install from this checkout. - -## 0. Prerequisites - -```bash -node --version -pnpm --version -``` - -Verify Node is `>=22.3.0` and pnpm is installed. - -Optional harness CLIs for manual install QA: - -```bash -claude --version -codex --version -agy --version -pi --version -opencode --version -``` - -Install any missing harness before running that harness section. - -## 1. Clean repo/build QA - -```bash -pnpm install --frozen-lockfile -pnpm build -pnpm plugin:check -pnpm lint -pnpm test -pnpm test:marketplace -``` - -Expected: every command exits `0`. - -## 2. Real HOME CLI QA setup - -This QA flow uses your real HOME and modifies real harness configs. - -Local build (default — QA the code in this checkout): - -```bash -export REPO_ROOT="$(pwd)" -export REAL_HOME="$(node -e 'console.log(require("node:os").homedir())')" -export HOME="$REAL_HOME" -export CLI="node $REPO_ROOT/packages/core/dist/src/cli.js" -echo "Using real HOME: $HOME" -``` - -Published package (once `nsolid-plugin` is on npm — QA the shipped artifact instead): - -```bash -export REAL_HOME="$(node -e 'console.log(require("node:os").homedir())')" -export HOME="$REAL_HOME" -# stable release: -export CLI="npx -y nsolid-plugin" -# prerelease (published under the next dist-tag): -export CLI="npx -y nsolid-plugin@next" -echo "Using real HOME: $HOME" -``` - -Confirm CLI help: - -```bash -$CLI --help -``` - -Expected: help lists `setup`, `install`, `uninstall`, `logout`, `doctor`, and `restore`. - -## 3. CLI auth/setup flow - -Use a real NodeSource account. - -Production accounts: - -```bash -$CLI logout -$CLI setup --harness claude --yes -``` - -Staging accounts (use this one for the initial QA): - -```bash -$CLI logout -$CLI setup --harness claude --yes --staging -``` - -Expected: browser opens, OAuth completes, credentials are stored. - -Production accounts: - -```bash -test -f "$HOME/.agents/.nodesource-auth.json" -$CLI setup --harness codex --yes -$CLI setup --harness antigravity --yes -``` - -Staging accounts (use this one for the initial QA): - -```bash -test -f "$HOME/.agents/.nodesource-auth.json" -$CLI setup --harness codex --yes --staging -$CLI setup --harness antigravity --yes --staging -``` - -Expected: no second browser if credentials are still valid; output says credentials are ready. - -## 4. CLI doctor / uninstall / logout - -Install the opencode mcp and skills first - -```bash -$CLI install --harness opencode -``` - - -```bash -$CLI doctor --harness opencode || true -$CLI doctor --harness opencode --json || true -$CLI uninstall --harness opencode --keep-credentials -$CLI logout -``` - -Expected: -- `doctor` reports health or clear actionable failures. -- `uninstall` removes fallback OpenCode artifacts. -- `logout` removes credentials or says none were found. - -## 5. Published CLI package QA - -Once `nsolid-plugin` is published to npm, QA the shipped package itself — not the local build — to confirm the `bin` works, the right files ship, and skills/bundle are included. - -```bash -# prerelease: -npx -y nsolid-plugin@next --help -# stable release: -npx -y nsolid-plugin --help - -# Inspect what the package actually ships (files included in the tarball): -npm pack nsolid-plugin@next --dry-run 2>&1 | sed -n '/Tarball Contents/,/Tarball Details/p' -``` - -Expected: -- `--help` runs without a local build; the `nsolid-plugin` bin is executable. -- Tarball contents include `dist/src/`, `skills/`, `bundle.json`, and `scripts/`. -- The `bin` entry resolves to `./dist/src/cli.js` and that file is executable. -- No `test/`, no source `*.ts`, no `tsconfig` shipped. - -## 6. Claude Code native install QA - -Claude installs from the GitHub plugin root. The local path can be the repo checkout itself, or a clone for closer-to-release coverage. -Set `PLUGIN_REF` to the branch or tag under test (e.g., `export PLUGIN_REF=cesar/github-install-test`). - -Production accounts: - -```bash -claude plugin marketplace add NodeSource/nsolid-plugin@$PLUGIN_REF -claude plugin install nsolid-plugin@nodesource -$CLI setup --harness claude --yes -claude plugin list -claude plugin details nsolid-plugin@nodesource -``` - -Staging accounts (use this one for the initial QA): - -```bash -claude plugin marketplace add NodeSource/nsolid-plugin@$PLUGIN_REF -claude plugin install nsolid-plugin@nodesource -$CLI setup --harness claude --yes --staging -claude plugin list -claude plugin details nsolid-plugin@nodesource -``` - -Expected: -- Marketplace add/plugin install does not open browser. -- `setup` is the only step that may open browser. -- Plugin appears in Claude plugin list as `nsolid-plugin@nodesource`. -- `claude plugin details` shows the N|Solid component inventory. -- mcps are listed and connected, check with `/mcp` -- skills are listed, check with `/skills` - -Cleanup: - -```bash -claude plugin uninstall nsolid-plugin || true -claude plugin marketplace remove nodesource || true -$CLI uninstall --harness claude --keep-credentials || true -``` - -## 7. Codex native install QA - -Codex installs from the GitHub plugin root. The marketplace name is `nodesource`. -Set `PLUGIN_REF` to the branch or tag under test. - -Production accounts: - -```bash -codex plugin marketplace add NodeSource/nsolid-plugin@$PLUGIN_REF -codex plugin add nsolid-plugin@nodesource -$CLI setup --harness codex --yes -codex /plugins -``` - -Staging accounts (use this one for the initial QA): - -```bash -codex plugin marketplace add NodeSource/nsolid-plugin@$PLUGIN_REF -codex plugin add nsolid-plugin@nodesource -$CLI setup --harness codex --yes --staging -codex /plugins -``` - -Expected: -- Marketplace/plugin install does not open browser. -- `setup` is the only step that may open browser. -- Plugin appears in Codex plugins UI/list. -- Local pre-release reruns clear only the N|Solid Codex plugin cache so recopied assets are recopied. -- mcps are listed and connected, check with `/mcp` -- skills are listed, check with `/skills` - -Cleanup: - -```bash -codex plugin remove nsolid-plugin@nodesource -$CLI uninstall --harness codex --keep-credentials || true -``` - -If `codex plugin remove nsolid-plugin@nodesource` is unavailable, remove it through `codex /plugins`. - -## 8. Antigravity native install QA - -Antigravity installs directly from the GitHub plugin root (a git URL). -Set `PLUGIN_REF` to the branch or tag under test. - -Production accounts: - -```bash -agy plugin install https://github.com/nodesource/nsolid-plugin/tree/$PLUGIN_REF -agy plugin list -$CLI setup --harness antigravity --yes -``` - -Staging accounts (use this one for the initial QA): - -```bash - -agy plugin install https://github.com/nodesource/nsolid-plugin/tree/$PLUGIN_REF -agy plugin list -$CLI setup --harness antigravity --yes --staging -``` - -Expected: -- Validate/install succeeds. -- Native install does not open browser. -- `setup` is the only step that may open browser. -- Plugin appears in `agy plugin list`. -- mcps are listed and connected, check with `/mcp` -- skills are listed, check with `/skills` - -Cleanup: - -```bash -agy plugin uninstall nsolid-plugin || true -$CLI uninstall --harness antigravity --keep-credentials || true -``` - -## 9. Pi package install QA - -Local pre-release package QA with production accounts: - -```bash -pnpm plugin:materialize -pi install ./packages/pi-plugin --no-approve -$CLI setup --harness pi --yes -pi install npm:pi-mcp-adapter -$CLI doctor --harness pi || true -``` - -Local pre-release package QA with staging accounts (use this one for the initial qa): - -```bash -pnpm plugin:materialize -pi install ./packages/pi-plugin --no-approve -$CLI setup --harness pi --yes --staging -pi install npm:pi-mcp-adapter -$CLI doctor --harness pi || true -``` - -Production package QA uses the published Pi package instead of the local package directory: - -```bash -$CLI uninstall --harness pi --keep-credentials || true -# stable release: -pi install npm:nsolid-pi-plugin -# prerelease (published under the next dist-tag) — pin the exact version, e.g.: -# pi install npm:nsolid-pi-plugin@1.0.0-next.0 -$CLI setup --harness pi --yes -pi install npm:pi-mcp-adapter -$CLI doctor --harness pi || true -``` - -Expected: -- Pi package installs from npm for production QA or from `./packages/pi-plugin` for local pre-release QA. -- Pi package activation is side-effect free: no browser, no user-level skill copy, no MCP config write. -- `setup` is the only step that may open browser. -- Pi `setup` writes/refreshes MCP config but skips user-level skill copies because the Pi package owns skills. -- `pi-mcp-adapter` is installed so Pi can read `~/.pi/agent/mcp.json` and expose MCP-backed package skills. -- `packages/pi-plugin/skills/` exists only after local materialization/pack steps. -- Pi should not report N|Solid skill conflicts from stale fallback copies in `~/.pi/agent/skills` or `~/.agents/skills`. -- `~/.pi/agent/mcp.json` NodeSource entries include `"auth": false` so `pi-mcp-adapter` does not attempt OAuth dynamic registration; authentication uses the NodeSource service-token headers written by setup. - -Reload Pi if needed: - -```text -/reload -``` - -Cleanup: - -```bash -$CLI uninstall --harness pi --keep-credentials || true -pnpm plugin:clean -``` - -## 10. OpenCode CLI-only install QA - -OpenCode is CLI-only. `setup --harness opencode` is the primary onboarding command because it authenticates and then performs the direct install; `install --harness opencode` is a no-browser fallback/repair path. - -Production accounts: - -```bash -$CLI setup --harness opencode --yes -$CLI doctor --harness opencode || true -opencode --version -``` - -Staging accounts (use this one for the initial qa): - -```bash -$CLI setup --harness opencode --yes --staging -$CLI doctor --harness opencode || true -opencode --version -``` - -Expected: -- Skills are written under `$HOME/.config/opencode/skills/`. -- MCP config is merged into `$HOME/.config/opencode/opencode.jsonc`. -- mcps are listed and connected, check with `/mcp` -- skills are listed, check with `/skills` - -Cleanup: - -```bash -$CLI uninstall --harness opencode --keep-credentials || true -``` - -## 11. Final cleanup - -```bash -$CLI logout || true -pnpm plugin:clean -pnpm plugin:check -``` - -Expected: cleanup completes and `pnpm plugin:check` exits `0`. diff --git a/packages/core/README.md b/packages/core/README.md index d5a6097..4f0ba56 100644 --- a/packages/core/README.md +++ b/packages/core/README.md @@ -11,7 +11,7 @@ Install/setup semantics are intentionally split: 1. `setup()` authenticates with NodeSource and may open a browser. 2. `install()` is a fallback direct asset installer and never starts auth/browser login. 3. Runtime MCP wrappers fail with `Run: nsolid-plugin setup --harness ` if credentials are missing or expired. -4. OpenCode `setup --harness opencode` authenticates, copies skills directly to `~/.config/opencode/skills/`, and writes MCP config; `install --harness opencode` is no-browser fallback/repair. +4. OpenCode uses the direct CLI path: run `setup --harness opencode` for auth, then `install --harness opencode` to copy skills and write MCP config. 5. Pi package owns skills, while setup writes Pi MCP config for adapter/runtime compatibility. ## Public API @@ -58,13 +58,13 @@ A thin CLI is provided as `nsolid-plugin`: ```bash nsolid-plugin setup --harness claude # explicit auth/setup; may open browser -nsolid-plugin setup --harness opencode # explicit auth/setup + direct OpenCode install +nsolid-plugin setup --harness opencode # explicit auth/setup nsolid-plugin setup --harness pi # explicit auth/setup + Pi MCP config nsolid-plugin install --harness claude # fallback direct install; no browser nsolid-plugin install --harness antigravity # fallback direct install; no browser nsolid-plugin install --harness codex # fallback direct install; no browser nsolid-plugin install --harness pi # MCP config only; skills come from pi package -nsolid-plugin install --harness opencode # OpenCode: no-browser fallback/repair +nsolid-plugin install --harness opencode # OpenCode: copy skills + write MCP config nsolid-plugin uninstall --harness claude nsolid-plugin doctor --harness claude nsolid-plugin doctor --harness claude --json @@ -73,7 +73,7 @@ nsolid-plugin restore --harness claude --list nsolid-plugin restore --harness claude --backup ~/.agents/.config-backup/claude/1234567890.json ``` -Use `--verbose` (or `NSOLID_PLUGIN_VERBOSE=1`) for detailed, timestamped logs written to stderr. Verbose mode redacts tokens and auth headers. For Claude Code, Codex, and Antigravity, prefer native GitHub plugin install from the repository root; `install --harness` is a fallback direct installer only. For Pi, install `nsolid-pi-plugin` for package-owned skills; CLI install/setup only writes MCP config. OpenCode is CLI-only and uses `setup --harness opencode` for primary onboarding because it authenticates and then installs user-level skills plus MCP config. +Use `--verbose` (or `NSOLID_PLUGIN_VERBOSE=1`) for detailed, timestamped logs written to stderr. Verbose mode redacts tokens and auth headers. For Claude Code, Codex, and Antigravity, prefer native GitHub plugin install from the repository root; `install --harness` is a fallback direct installer only. For Pi, install `nsolid-pi-plugin` for package-owned skills; CLI install/setup only writes MCP config. OpenCode is CLI-only and uses `setup --harness opencode` for auth followed by `install --harness opencode` to copy user-level skills and write MCP config. ## Config backups diff --git a/packages/core/src/auth/mcp-url.ts b/packages/core/src/auth/mcp-url.ts index c4e03d5..0f58cdd 100644 --- a/packages/core/src/auth/mcp-url.ts +++ b/packages/core/src/auth/mcp-url.ts @@ -9,9 +9,7 @@ export function deriveMcpUrlFromConsoleUrl (consoleUrl: string): string | null { const host = parsed.hostname let mcpHost: string | null = null - if (host.endsWith('.staging.saas.nodesource.io')) { - mcpHost = host.replace(/\.staging\.saas\.nodesource\.io$/, '.mcp.staging.saas.nodesource.io') - } else if (host.endsWith('.saas.nodesource.io')) { + if (host.endsWith('.saas.nodesource.io')) { mcpHost = host.replace(/\.saas\.nodesource\.io$/, '.mcp.saas.nodesource.io') } diff --git a/packages/core/src/auth/token-validator.ts b/packages/core/src/auth/token-validator.ts index ca02461..5d87258 100644 --- a/packages/core/src/auth/token-validator.ts +++ b/packages/core/src/auth/token-validator.ts @@ -9,8 +9,6 @@ export function deriveAccountsApiUrl (accountsUrl: string): string { const url = new URL(accountsUrl) if (url.hostname === 'accounts.nodesource.com') { url.hostname = 'api.nodesource.com' - } else if (url.hostname === 'staging.accounts.nodesource.com') { - url.hostname = 'staging.api.nodesource.com' } return url.origin } diff --git a/packages/core/src/cli.ts b/packages/core/src/cli.ts index 9ad5b6b..4c6ca0e 100644 --- a/packages/core/src/cli.ts +++ b/packages/core/src/cli.ts @@ -74,14 +74,13 @@ Options: --no-color Disable colored output --quiet Suppress step-by-step progress output (install only) --yes Skip interactive confirmation prompts - --staging Use staging accounts URL for setup (dev/QA only) - --accounts-url Explicit origin-only accounts URL override for setup (wins over --staging) + --accounts-url Explicit origin-only accounts URL override for setup --help Show this help message Distribution notes: Claude/Codex/Antigravity: install from the GitHub plugin root; setup is auth-only. Pi: use pi install for package-owned skills; CLI install/setup only writes MCP config. - OpenCode: use setup --harness opencode for auth + direct install; install is fallback/repair. + OpenCode: run setup --harness opencode for auth, then install --harness opencode for skills/MCP config. Auth: only setup/login may open a browser.`) } @@ -238,7 +237,6 @@ async function main (): Promise { verbose: { type: 'boolean' }, json: { type: 'boolean' }, 'no-color': { type: 'boolean' }, - staging: { type: 'boolean' }, 'accounts-url': { type: 'string' }, 'keep-credentials': { type: 'boolean' }, quiet: { type: 'boolean' }, @@ -283,9 +281,6 @@ async function main (): Promise { switch (command) { case 'setup': { - if (values.staging === true) { - process.env.NSOLID_STAGING = '1' - } if (values['accounts-url']) { process.env.NSOLID_ACCOUNTS_URL = values['accounts-url'] } diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 21af4f8..c288ade 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -46,7 +46,6 @@ import { restoreConfigBackup, type BackupEntry } from './utils/backup.js' import { toPluginError } from './errors.js' const KNOWN_MCP_SERVERS = ['ns-benchmark', 'nsolid-console', 'ncm'] -const STAGING_ACCOUNTS_URL = 'https://staging.accounts.nodesource.com' const PLUGIN_OWNED_HARNESSES = new Set(['claude', 'codex', 'antigravity']) /** * Harnesses that install the nsolid plugin/package natively (owning skills and @@ -106,10 +105,8 @@ async function resolveInstallProgress ( export function resolveAccountsUrl (defaultUrl: string, logger?: Logger): string { const explicit = process.env.NSOLID_ACCOUNTS_URL - const staging = process.env.NSOLID_STAGING let url = defaultUrl - if (staging === '1' || staging === 'true') url = STAGING_ACCOUNTS_URL - if (explicit) url = explicit // explicit wins over the staging shortcut + if (explicit) url = explicit if (url === defaultUrl) return url diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts index 3feddf6..b1a000f 100644 --- a/packages/core/src/types.ts +++ b/packages/core/src/types.ts @@ -23,7 +23,7 @@ export interface Credentials { mcpUrl: string; expiresAt: string; permissions?: string[]; - /** Auth origin used to mint/validate the token (persisted for staging QA). */ + /** Auth origin used to mint/validate the token. */ accountsUrl?: string; } diff --git a/packages/core/test/integration/auth/accounts-url-override.test.ts b/packages/core/test/integration/auth/accounts-url-override.test.ts index 47709b6..265dfe5 100644 --- a/packages/core/test/integration/auth/accounts-url-override.test.ts +++ b/packages/core/test/integration/auth/accounts-url-override.test.ts @@ -2,19 +2,14 @@ import { describe, it, beforeEach, afterEach } from 'node:test' import assert from 'node:assert/strict' import { resolveAccountsUrl } from '../../../src/index.js' -let originalStaging: string | undefined let originalAccountsUrl: string | undefined beforeEach(() => { - originalStaging = process.env.NSOLID_STAGING originalAccountsUrl = process.env.NSOLID_ACCOUNTS_URL - delete process.env.NSOLID_STAGING delete process.env.NSOLID_ACCOUNTS_URL }) afterEach(() => { - if (originalStaging !== undefined) process.env.NSOLID_STAGING = originalStaging - else delete process.env.NSOLID_STAGING if (originalAccountsUrl !== undefined) process.env.NSOLID_ACCOUNTS_URL = originalAccountsUrl else delete process.env.NSOLID_ACCOUNTS_URL }) @@ -25,20 +20,7 @@ describe('resolveAccountsUrl', () => { assert.strictEqual(result, 'https://accounts.nodesource.com') }) - it('overrides to staging when NSOLID_STAGING=1', () => { - process.env.NSOLID_STAGING = '1' - const result = resolveAccountsUrl('https://accounts.nodesource.com') - assert.strictEqual(result, 'https://staging.accounts.nodesource.com') - }) - - it('overrides to staging when NSOLID_STAGING=true', () => { - process.env.NSOLID_STAGING = 'true' - const result = resolveAccountsUrl('https://accounts.nodesource.com') - assert.strictEqual(result, 'https://staging.accounts.nodesource.com') - }) - - it('explicit NSOLID_ACCOUNTS_URL wins over NSOLID_STAGING', () => { - process.env.NSOLID_STAGING = '1' + it('uses explicit NSOLID_ACCOUNTS_URL override', () => { process.env.NSOLID_ACCOUNTS_URL = 'https://custom.accounts.example.com' const result = resolveAccountsUrl('https://accounts.nodesource.com') assert.strictEqual(result, 'https://custom.accounts.example.com') @@ -84,7 +66,7 @@ describe('resolveAccountsUrl', () => { warn: (msg: string, meta?: Record) => { warnings.push(`${msg} ${JSON.stringify(meta)}`) }, error: () => {}, } - process.env.NSOLID_STAGING = '1' + process.env.NSOLID_ACCOUNTS_URL = 'https://custom.accounts.example.com' resolveAccountsUrl('https://accounts.nodesource.com', logger) assert.ok(warnings.some((w) => w.includes('auth.accountsUrl.overridden'))) }) diff --git a/packages/core/test/integration/auth/auth-manager.test.ts b/packages/core/test/integration/auth/auth-manager.test.ts index b8c8862..c34e2d6 100644 --- a/packages/core/test/integration/auth/auth-manager.test.ts +++ b/packages/core/test/integration/auth/auth-manager.test.ts @@ -18,7 +18,6 @@ let tmpDir: string let originalHome: string | undefined let originalUserProfile: string | undefined let originalFetch: typeof globalThis.fetch -let originalStaging: string | undefined let originalAccountsUrl: string | undefined const authConfig: AuthConfig = { @@ -79,9 +78,7 @@ beforeEach(() => { originalFetch = globalThis.fetch execFileCalls.length = 0 - originalStaging = process.env.NSOLID_STAGING originalAccountsUrl = process.env.NSOLID_ACCOUNTS_URL - delete process.env.NSOLID_STAGING delete process.env.NSOLID_ACCOUNTS_URL }) @@ -93,8 +90,6 @@ afterEach(() => { } globalThis.fetch = originalFetch - if (originalStaging !== undefined) process.env.NSOLID_STAGING = originalStaging - else delete process.env.NSOLID_STAGING if (originalAccountsUrl !== undefined) process.env.NSOLID_ACCOUNTS_URL = originalAccountsUrl else delete process.env.NSOLID_ACCOUNTS_URL }) @@ -196,27 +191,6 @@ describe('ensureAuthenticated', () => { assert.strictEqual(result.saasToken, 'oauth-saas-token') }) - it('derives staging MCP host from OAuth console URL', { timeout: 10000 }, async () => { - globalThis.fetch = mock.fn(async () => ({ - ok: true, - status: 200, - headers: new Headers({ 'content-type': 'application/json' }), - json: async () => ({ permissions: [] }), - })) as unknown as typeof fetch - - const { ensureAuthenticated } = await import('../../../src/auth/auth-manager.js') - const promise = ensureAuthenticated(authConfig) - - const state = await pollForState(getStateFromExecFileCall) - await sendCallback(8767, state, { - consoleId: 'org-456', - url: 'https://org-456.staging.saas.nodesource.io', - }) - const result = await promise - - assert.strictEqual(result.mcpUrl, 'https://org-456.mcp.staging.saas.nodesource.io/') - }) - it('re-authenticates when credentials are expired', { timeout: 10000 }, async () => { const { saveCredentials } = await import('../../../src/auth/token-storage.js') const expiredCreds: Credentials = { @@ -622,35 +596,6 @@ describe('ensureAuthenticated - accountsUrl override', () => { saveCredentials(expiredCreds) } - it('builds staging /sign-in URL when passed a staging accountsUrl', { timeout: 10000 }, async () => { - await seedExpiredCredentials() - - globalThis.fetch = mock.fn(async () => ({ - ok: true, - status: 200, - headers: new Headers({ 'content-type': 'application/json' }), - json: async () => ({ permissions: [] }), - })) as unknown as typeof fetch - - const stagingConfig: AuthConfig = { - ...authConfig, - accountsUrl: 'https://staging.accounts.nodesource.com', - } - - const { ensureAuthenticated } = await import('../../../src/auth/auth-manager.js') - const promise = ensureAuthenticated(stagingConfig) - - await new Promise((resolve) => setTimeout(resolve, 50)) - const signInUrl = getUrlFromExecFileCall() - assert.strictEqual(signInUrl.host, 'staging.accounts.nodesource.com') - assert.strictEqual(signInUrl.pathname, '/sign-in') - assert.strictEqual(signInUrl.searchParams.get('extension'), 'nsolid-plugin') - assert.strictEqual(signInUrl.searchParams.get('port'), '8767') - const state = getStateFromExecFileCall() - await sendCallback(8767, state) - await promise - }) - it('builds explicit /sign-in URL when passed an explicit accountsUrl', { timeout: 10000 }, async () => { await seedExpiredCredentials() diff --git a/packages/core/test/integration/cli-help.test.ts b/packages/core/test/integration/cli-help.test.ts index 113b9e9..1020283 100644 --- a/packages/core/test/integration/cli-help.test.ts +++ b/packages/core/test/integration/cli-help.test.ts @@ -8,7 +8,7 @@ const __dirname = dirname(fileURLToPath(import.meta.url)) const CLI_PATH = join(__dirname, '..', '..', 'src', 'cli.ts') describe('CLI help', () => { - it('describes Codex as a root native-plugin harness with fallback install only', () => { + it('describes native plugin harnesses and the OpenCode setup/install split', () => { const result = spawnSync(process.execPath, ['--import', 'tsx/esm', CLI_PATH, '--help'], { encoding: 'utf-8', }) @@ -17,6 +17,7 @@ describe('CLI help', () => { const output = result.stdout assert.match(output, /Claude\/Codex\/Antigravity: install from the GitHub plugin root/, 'help must group Codex with root native plugin harnesses') assert.match(output, /setup is auth-only/, 'help must identify setup as auth-only for native plugin harnesses') + assert.match(output, /OpenCode: run setup --harness opencode for auth, then install --harness opencode for skills\/MCP config\./, 'help must describe OpenCode setup then install') assert.doesNotMatch(output, /OpenCode\/Codex/, 'help must not list Codex as a user-level skill harness') }) }) diff --git a/packages/core/test/integration/installer.test.ts b/packages/core/test/integration/installer.test.ts index b607ae3..6c281bf 100644 --- a/packages/core/test/integration/installer.test.ts +++ b/packages/core/test/integration/installer.test.ts @@ -278,7 +278,7 @@ describe('install()', () => { const bundlePath = writeBundle(bundle) const skillsSource = createSkillSource('ns-test-skill') seedCredentials({ - consoleUrl: 'https://test-org.staging.saas.nodesource.io', + consoleUrl: 'https://test-org.saas.nodesource.io', mcpUrl: 'https://custom-mcp.example.com/entry', }) @@ -292,7 +292,7 @@ describe('install()', () => { assert.strictEqual(servers['nsolid-console'].url, 'https://custom-mcp.example.com/entry') }) - it('derives staging console MCP URL without appending /mcp when no explicit MCP URL is stored', async () => { + it('derives console MCP URL without appending /mcp when no explicit MCP URL is stored', async () => { const { install } = await import('../../src/index.js') const { readJsonFile } = await import('../../src/utils/config.js') const bundle = createBundle({ @@ -308,7 +308,7 @@ describe('install()', () => { const bundlePath = writeBundle(bundle) const skillsSource = createSkillSource('ns-test-skill') seedCredentials({ - consoleUrl: 'https://test-org.staging.saas.nodesource.io', + consoleUrl: 'https://test-org.saas.nodesource.io', mcpUrl: '', }) @@ -319,7 +319,7 @@ describe('install()', () => { assert.ok(claudeConfig?.mcpServers && typeof claudeConfig.mcpServers === 'object') const servers = claudeConfig.mcpServers as Record assert.strictEqual(servers['nsolid-console'].type, 'http') - assert.strictEqual(servers['nsolid-console'].url, 'https://test-org.mcp.staging.saas.nodesource.io/') + assert.strictEqual(servers['nsolid-console'].url, 'https://test-org.mcp.saas.nodesource.io/') }) it('writes MCP config for Pi', async () => { diff --git a/packages/core/test/unit/auth/token-validator.test.ts b/packages/core/test/unit/auth/token-validator.test.ts index ae3b1c0..9896dbb 100644 --- a/packages/core/test/unit/auth/token-validator.test.ts +++ b/packages/core/test/unit/auth/token-validator.test.ts @@ -13,17 +13,13 @@ afterEach(() => { }) describe('validateToken', () => { - it('derives the API origin from production and staging accounts origins', async () => { + it('derives the API origin from production accounts origin', async () => { const { deriveAccountsApiUrl } = await import('../../../src/auth/token-validator.js') assert.strictEqual( deriveAccountsApiUrl('https://accounts.nodesource.com'), 'https://api.nodesource.com' ) - assert.strictEqual( - deriveAccountsApiUrl('https://staging.accounts.nodesource.com'), - 'https://staging.api.nodesource.com' - ) assert.strictEqual( deriveAccountsApiUrl('https://accounts.example.com'), 'https://accounts.example.com' diff --git a/packages/pi-plugin/README.md b/packages/pi-plugin/README.md index ea24f77..38ccb74 100644 --- a/packages/pi-plugin/README.md +++ b/packages/pi-plugin/README.md @@ -15,7 +15,7 @@ Or for local development from the repository root: ```bash pnpm plugin:materialize pi install ./packages/pi-plugin --no-approve -nsolid-plugin setup --harness pi --staging +nsolid-plugin setup --harness pi pi install npm:pi-mcp-adapter /reload ``` @@ -30,7 +30,7 @@ pi list ## What's Included -- 15 package-owned AI skills for Node.js performance and security analysis +- 16 package-owned AI skills for Node.js performance and security analysis - Side-effect-free package activation: no browser auth, no user-level skill copy, no MCP config mutation - MCP configuration written to `~/.pi/agent/mcp.json` only by explicit `nsolid-plugin setup --harness pi` diff --git a/scripts/materialize-github-marketplace.mjs b/scripts/materialize-github-marketplace.mjs index 875bac4..e5c7d08 100644 --- a/scripts/materialize-github-marketplace.mjs +++ b/scripts/materialize-github-marketplace.mjs @@ -152,8 +152,8 @@ function validateCanonicalSkills () { function generateSharedWrapper () { return generateClaudeWrapper() .replace( - "const SETUP_COMMAND = 'npx -y @nodesource/nsolid-plugin setup --harness claude'", - "const SETUP_COMMAND = 'npx -y nsolid-plugin setup --harness '" + "const SETUP_COMMAND = 'npx -y nsolid-plugin setup --harness claude'", + "const SETUP_COMMAND = 'npx -y nsolid-plugin setup --harness '" ) } diff --git a/scripts/mcp-wrapper.js b/scripts/mcp-wrapper.js index 92d0269..a726b77 100644 --- a/scripts/mcp-wrapper.js +++ b/scripts/mcp-wrapper.js @@ -8,7 +8,7 @@ import path from 'node:path' import { pathToFileURL } from 'node:url' const AUTH_FILE = path.join(os.homedir(), '.agents', '.nodesource-auth.json') -const SETUP_COMMAND = 'npx -y nsolid-plugin setup --harness ' +const SETUP_COMMAND = 'npx -y nsolid-plugin setup --harness ' const SERVER_NAMES = new Set(["nsolid-console","ns-benchmark","ncm"]) const serverName = process.argv[2] @@ -93,9 +93,7 @@ function deriveMcpUrlFromConsoleUrl (consoleUrl) { const host = parsed.hostname let mcpHost = null - if (host.endsWith('.staging.saas.nodesource.io')) { - mcpHost = host.replace(/\.staging\.saas\.nodesource\.io$/, '.mcp.staging.saas.nodesource.io') - } else if (host.endsWith('.saas.nodesource.io')) { + if (host.endsWith('.saas.nodesource.io')) { mcpHost = host.replace(/\.saas\.nodesource\.io$/, '.mcp.saas.nodesource.io') } diff --git a/scripts/plugin-generators.mjs b/scripts/plugin-generators.mjs index 2933682..42b5e5f 100644 --- a/scripts/plugin-generators.mjs +++ b/scripts/plugin-generators.mjs @@ -152,7 +152,7 @@ import path from 'node:path' import { pathToFileURL } from 'node:url' const AUTH_FILE = path.join(os.homedir(), '.agents', '.nodesource-auth.json') -const SETUP_COMMAND = 'npx -y @nodesource/nsolid-plugin setup --harness ${harness}' +const SETUP_COMMAND = 'npx -y nsolid-plugin setup --harness ${harness}' const SERVER_NAMES = new Set(${JSON.stringify(serverNames)}) const serverName = process.argv[2] @@ -237,9 +237,7 @@ function deriveMcpUrlFromConsoleUrl (consoleUrl) { const host = parsed.hostname let mcpHost = null - if (host.endsWith('.staging.saas.nodesource.io')) { - mcpHost = host.replace(/\\.staging\\.saas\\.nodesource\\.io$/, '.mcp.staging.saas.nodesource.io') - } else if (host.endsWith('.saas.nodesource.io')) { + if (host.endsWith('.saas.nodesource.io')) { mcpHost = host.replace(/\\.saas\\.nodesource\\.io$/, '.mcp.saas.nodesource.io') } From 4a5ed153a56830d17308da9d746745c00a37a7bf Mon Sep 17 00:00:00 2001 From: Cesar-M-Diaz Date: Tue, 7 Jul 2026 20:33:34 +0200 Subject: [PATCH 2/4] v1.0.0 --- packages/core/package.json | 4 ++-- .../test/integration/plugin-assets.test.ts | 18 +++++++++++++++++ packages/pi-plugin/package.json | 2 +- scripts/sync-plugin-assets.mjs | 20 ++++++++++++++++--- 4 files changed, 38 insertions(+), 6 deletions(-) diff --git a/packages/core/package.json b/packages/core/package.json index 982451a..c1b6be0 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,6 +1,6 @@ { "name": "nsolid-plugin", - "version": "1.0.0-next.0", + "version": "1.0.0", "type": "module", "main": "dist/src/index.js", "types": "dist/src/index.d.ts", @@ -48,4 +48,4 @@ "bundle.json", "scripts/" ] -} \ No newline at end of file +} diff --git a/packages/core/test/integration/plugin-assets.test.ts b/packages/core/test/integration/plugin-assets.test.ts index 986d3ab..c3b1623 100644 --- a/packages/core/test/integration/plugin-assets.test.ts +++ b/packages/core/test/integration/plugin-assets.test.ts @@ -104,6 +104,24 @@ describe('plugin source hygiene', () => { assert.strictEqual(check.status, 0, outputText(check.stderr)) }) + it('cleans core package skills materialized by core prepack', () => { + writeSkill(root, 'ns-alpha', '# alpha\n') + writeBundle(root, ['ns-alpha'], ['ns-one']) + mkdirSync(join(root, 'packages', 'core', 'skills', 'ns-alpha'), { recursive: true }) + writeFileSync(join(root, 'packages', 'core', 'skills', 'ns-alpha', 'SKILL.md'), '# alpha\n') + + const checkWithMaterializedCore = runSync(root, ['--check']) + assert.notStrictEqual(checkWithMaterializedCore.status, 0) + assert.match(outputText(checkWithMaterializedCore.stderr), /packages\/core\/skills/) + + const clean = runSync(root) + assert.strictEqual(clean.status, 0, outputText(clean.stderr)) + assert.strictEqual(existsSync(join(root, 'packages', 'core', 'skills')), false) + + const check = runSync(root, ['--check']) + assert.strictEqual(check.status, 0, outputText(check.stderr)) + }) + it('fails check when a bundle skill is missing from root skills', () => { writeBundle(root, ['ns-missing'], ['ns-one']) diff --git a/packages/pi-plugin/package.json b/packages/pi-plugin/package.json index 0284ce3..d3d086c 100644 --- a/packages/pi-plugin/package.json +++ b/packages/pi-plugin/package.json @@ -1,6 +1,6 @@ { "name": "nsolid-pi-plugin", - "version": "1.0.0-next.0", + "version": "1.0.0", "type": "module", "description": "N|Solid Plugin for Pi Agent", "keywords": [ diff --git a/scripts/sync-plugin-assets.mjs b/scripts/sync-plugin-assets.mjs index 287ab33..45c1428 100644 --- a/scripts/sync-plugin-assets.mjs +++ b/scripts/sync-plugin-assets.mjs @@ -38,6 +38,7 @@ const CHECK_MODE = process.argv.includes('--check') const MATERIALIZE_SKILLS = process.argv.includes('--materialize-skills') const CORE_SKILLS_DIR = path.join(ROOT, 'skills') +const CORE_PACKAGE_SKILLS_DIR = path.join(ROOT, 'packages', 'core', 'skills') const PI_PLUGIN_DIR = path.join(ROOT, 'packages', 'pi-plugin') const bundle = loadBundle(ROOT) @@ -55,6 +56,9 @@ const piSkillDrift = MATERIALIZE_SKILLS : cleanPiSkills() if (piSkillDrift) driftDetected = true +const corePackageSkillDrift = cleanCorePackageSkills() +if (corePackageSkillDrift) driftDetected = true + const hygieneDrift = checkSourceHygiene() if (hygieneDrift) driftDetected = true @@ -99,6 +103,19 @@ function cleanPiSkills () { return true } +function cleanCorePackageSkills () { + if (!existsSync(CORE_PACKAGE_SKILLS_DIR)) return false + + if (CHECK_MODE) { + console.error(`Materialized skill dir present in source tree: ${path.relative(ROOT, CORE_PACKAGE_SKILLS_DIR)}`) + return true + } + + rmSync(CORE_PACKAGE_SKILLS_DIR, { recursive: true, force: true }) + console.log(`Removed materialized skill dir: ${path.relative(ROOT, CORE_PACKAGE_SKILLS_DIR)}`) + return true +} + function materializePiSkills () { const destSkillsDir = path.join(PI_PLUGIN_DIR, 'skills') let drift = false @@ -145,9 +162,6 @@ function materializePiSkills () { function checkSourceHygiene () { let drift = false // Reject any package-local skills dirs in removed/legacy plugin packages. - // NOTE: packages/core is intentionally excluded — it legitimately materializes - // a skills/ dir during prepack (skills:sync). Its presence is governed by - // packages/core/scripts/sync-shared-skill-assets.mjs, not this hygiene check. const packageRoots = [ path.join(ROOT, 'packages', 'claude-plugin'), path.join(ROOT, 'packages', 'codex-plugin'), From cef0515f2c1c748208a520563994f2de3c3b403a Mon Sep 17 00:00:00 2001 From: Cesar-M-Diaz Date: Tue, 7 Jul 2026 20:42:55 +0200 Subject: [PATCH 3/4] fix readme --- README.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/README.md b/README.md index d338d77..03853ad 100644 --- a/README.md +++ b/README.md @@ -293,10 +293,6 @@ Tokens and auth headers are redacted automatically. nsolid-plugin uninstall --harness ``` -```powershell -nsolid-plugin uninstall --harness -``` - Credentials are preserved. ### Pi MCP not working From ace2ec3dbfb93dccac815e3314ca937b88899879 Mon Sep 17 00:00:00 2001 From: Cesar-M-Diaz Date: Tue, 7 Jul 2026 21:03:27 +0200 Subject: [PATCH 4/4] fix windows test --- packages/core/test/integration/plugin-assets.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/test/integration/plugin-assets.test.ts b/packages/core/test/integration/plugin-assets.test.ts index c3b1623..80f8159 100644 --- a/packages/core/test/integration/plugin-assets.test.ts +++ b/packages/core/test/integration/plugin-assets.test.ts @@ -112,7 +112,7 @@ describe('plugin source hygiene', () => { const checkWithMaterializedCore = runSync(root, ['--check']) assert.notStrictEqual(checkWithMaterializedCore.status, 0) - assert.match(outputText(checkWithMaterializedCore.stderr), /packages\/core\/skills/) + assert.match(outputText(checkWithMaterializedCore.stderr), /packages[\\/]core[\\/]skills/) const clean = runSync(root) assert.strictEqual(clean.status, 0, outputText(clean.stderr))