Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
dfe5792
feat(plugin): add Rstack Context workflows
ScriptedAlchemy Aug 13, 2026
8839fd8
chore(plugin): version Rstack Context integration
ScriptedAlchemy Aug 13, 2026
b207fc1
fix(plugin): resolve package-local Rstack
ScriptedAlchemy Aug 13, 2026
af6ec09
fix(skill): gate Rsdoctor JSON capture
ScriptedAlchemy Aug 13, 2026
10e6fde
fix(skill): match snapshot capture selections
ScriptedAlchemy Aug 13, 2026
41e0629
fix(skill): gate impact artifact capture
ScriptedAlchemy Aug 13, 2026
0c81a2a
Merge remote-tracking branch 'origin/main' into codex/rstack-context-…
ScriptedAlchemy Aug 13, 2026
f71f56c
fix(plugin): align portable manifest
ScriptedAlchemy Aug 13, 2026
32a7280
fix(plugin): discover nested Rstack packages
ScriptedAlchemy Aug 13, 2026
6555287
feat(plugin): guide related Rstest evidence
ScriptedAlchemy Aug 14, 2026
7aa4001
fix(plugin): parse commented pnpm workspaces
ScriptedAlchemy Aug 14, 2026
79de40a
chore(plugin): refresh Codex cache
ScriptedAlchemy Aug 14, 2026
88acbe9
docs(plugin): link standalone context runtime
ScriptedAlchemy Aug 14, 2026
82d09be
chore(plugin): refresh context bundle
ScriptedAlchemy Aug 14, 2026
4c82de0
fix(plugin): clear inherited inline launcher arguments
ScriptedAlchemy Aug 14, 2026
77ef146
test(plugin): validate real Rstack MCP runtime
ScriptedAlchemy Aug 14, 2026
c5d200a
docs(plugin): improve evidence recovery
ScriptedAlchemy Aug 14, 2026
604a968
docs(plugin): surface test startup failures
ScriptedAlchemy Aug 14, 2026
32a0824
docs(plugin): clarify code evidence inputs
ScriptedAlchemy Aug 14, 2026
7a846d1
fix(plugin): isolate workspace MCP runtime
ScriptedAlchemy Aug 14, 2026
a169d07
fix(plugin): expose MCP from portable repository
ScriptedAlchemy Aug 14, 2026
520a23d
docs(plugin): bound build evidence summaries
ScriptedAlchemy Aug 14, 2026
3b10604
docs(plugin): explain optional execution coverage
ScriptedAlchemy Aug 14, 2026
9b9c4da
docs(plugin): tighten dogfood capture guidance
ScriptedAlchemy Aug 14, 2026
7d01e4e
fix: distinguish optimizer retention from roots
ScriptedAlchemy Aug 17, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .agents/plugins/marketplace.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
"name": "rstack",
"source": {
"source": "local",
"path": "./"
"path": "./plugins/rstack"
},
"policy": {
"installation": "AVAILABLE",
Expand Down
6 changes: 3 additions & 3 deletions .claude-plugin/marketplace.json
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
{
"$schema": "https://json.schemastore.org/claude-code-marketplace.json",
"name": "rstack",
"description": "Agent Skills for debugging, tracing, upgrading, and analyzing Rstack projects.",
"description": "Agent Skills and project context tools for debugging, tracing, upgrading, and analyzing Rstack projects.",
"owner": {
"name": "RstackJS"
},
"plugins": [
{
"name": "rstack",
"description": "Agent Skills for debugging, tracing, upgrading, and analyzing Rstack projects.",
"version": "0.1.0",
"description": "Agent Skills and project context tools for debugging, tracing, upgrading, and analyzing Rstack projects.",
"version": "0.2.4",
"source": "./",
"author": {
"name": "RstackJS"
Expand Down
5 changes: 3 additions & 2 deletions .claude-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
{
"name": "rstack",
"version": "0.1.0",
"description": "Agent Skills for debugging, tracing, upgrading, and analyzing Rstack projects.",
"version": "0.2.4",
"description": "Agent Skills and project context tools for debugging, tracing, upgrading, and analyzing Rstack projects.",
"author": {
"name": "RstackJS"
},
"homepage": "https://github.com/rstackjs/agent-skills",
"repository": "https://github.com/rstackjs/agent-skills",
"license": "MIT",
"mcpServers": "./.mcp.json",
"keywords": [
"rspack",
"rsbuild",
Expand Down
40 changes: 0 additions & 40 deletions .codex-plugin/plugin.json

This file was deleted.

12 changes: 12 additions & 0 deletions .mcp.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"mcpServers": {
"rstack": {
"command": "node",
"args": [
"--input-type=module",
"--eval",
"import { spawn } from 'node:child_process'; import { globSync, readFileSync } from 'node:fs'; import { createRequire } from 'node:module'; import { dirname, join, resolve } from 'node:path'; const cwd = process.cwd(); const roots = [cwd]; const patterns = []; try { const rootPackage = JSON.parse(readFileSync(join(cwd, 'package.json'), 'utf8')); const workspaces = Array.isArray(rootPackage.workspaces) ? rootPackage.workspaces : rootPackage.workspaces?.packages; if (Array.isArray(workspaces)) patterns.push(...workspaces); } catch (error) { if (error?.code !== 'ENOENT') throw error; } try { const lines = readFileSync(join(cwd, 'pnpm-workspace.yaml'), 'utf8').split(/\\r?\\n/u); let inPackages = false; for (const line of lines) { if (/^packages:\\s*$/u.test(line)) { inPackages = true; continue; } if (inPackages && /^\\S/u.test(line)) break; const match = inPackages ? /^\\s*-\\s*(?:['\"]([^'\"]+)['\"]|([^#]+?))(?:\\s+#.*)?\\s*$/u.exec(line) : null; if (match) patterns.push((match[1] ?? match[2]).trim()); } } catch (error) { if (error?.code !== 'ENOENT') throw error; } const includes = [...new Set(patterns.filter((pattern) => typeof pattern === 'string' && !pattern.startsWith('!')).map((pattern) => `${pattern.replace(/\\/$/u, '')}/package.json`))]; const excludes = patterns.filter((pattern) => typeof pattern === 'string' && pattern.startsWith('!')).map((pattern) => `${pattern.slice(1).replace(/\\/$/u, '')}/package.json`); if (includes.length > 0) for (const packageJson of globSync(includes, { cwd, exclude: [...excludes, '**/node_modules/**'] })) roots.push(dirname(resolve(cwd, packageJson))); for (const packageJson of globSync('*/package.json', { cwd, exclude: ['node_modules/**'] })) roots.push(dirname(resolve(cwd, packageJson))); let packageJsonPath; let localRequire; for (const root of new Set(roots)) { const candidate = createRequire(join(root, 'package.json')); try { packageJsonPath = candidate.resolve('rstack/package.json'); localRequire = candidate; break; } catch (error) { if (error?.code !== 'MODULE_NOT_FOUND') throw error; } } const run = async (command, args) => { const child = spawn(command, args, { stdio: 'inherit' }); const { code, signal } = await new Promise((resolve, reject) => { child.once('error', reject); child.once('exit', (code, signal) => resolve({ code, signal })); }); if (signal) process.kill(process.pid, signal); else process.exitCode = code ?? 1; }; try { if (packageJsonPath) { const { bin } = localRequire(packageJsonPath); const binPath = typeof bin === 'string' ? bin : (bin.rs ?? bin.rstack); if (!binPath) throw new Error('The workspace-local rstack package does not declare an rs binary.'); await run(process.execPath, [resolve(dirname(packageJsonPath), binPath), 'mcp']); } else { await run('rs', ['mcp']); } } catch (error) { if (error?.code !== 'ENOENT') throw error; console.error('Unable to launch Rstack MCP server: install rstack in this repository, a declared workspace package, an immediate child package, or put rs on PATH.'); process.exitCode = 1; }"
]
}
}
}
3 changes: 3 additions & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,6 @@ skills-lock.yaml
skills/**/scripts/*.js
skills/**/scripts/*.cjs
skills/**/scripts/*.mjs
plugins/rstack/skills/**/scripts/*.js
plugins/rstack/skills/**/scripts/*.cjs
plugins/rstack/skills/**/scripts/*.mjs
44 changes: 44 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ A collection of Agent Skills for [Rstack](https://rspack.rs/guide/start/ecosyste
## Table of Contents

- [Usage](#usage)
- [Rstack Context](#rstack-context)
- [Rspack Skills](#rspack-skills)
- [Rsbuild Skills](#rsbuild-skills)
- [Rslib Skills](#rslib-skills)
Expand Down Expand Up @@ -71,6 +72,49 @@ Install any skill with:
npx skills add rstackjs/agent-skills --skill <skill-name>
```

## Rstack Context

The Codex and Claude Code plugin includes one local MCP server named `rstack`. It starts the
workspace-local `rstack` package's `rs mcp` command, falling back to an `rs` executable on `PATH`.
Install Rstack CLI in the project when you want build, lint, test, coverage, or Rsdoctor evidence:

```bash
pnpm add -D rstack
```

The MCP is intentionally bound to the checkout containing the Codex project/session root. It may
start at that checkout's monorepo root, then discovers checkout-local contexts recorded by Rstack
commands and identifies packages, products, environments, targets, configs, and variants by
`contextId`; it does not treat the server's current working directory as the selected package. The
tools do not accept a workspace argument. To inspect an external checkout, start a new Codex session
rooted at that checkout.

`rs test` reads the test configuration registered by Rstack; it does not automatically adopt a
standalone `rstest.config.*`. Keep an existing Rstest config as the source of truth with a minimal
bridge:

```ts
// rstack.config.ts
import { define } from 'rstack';
import rstestConfig from './rstest.config';

define.test(rstestConfig);
```

The plugin adds these context workflows:

- `analyze-build`: summarize a matching Rsdoctor artifact with build context.
- `assess-change-impact`: trace artifact-scoped module dependents and affected chunks.
- `debug-dev-cycle`: inspect stored Rslint or Rstest failures, with consent-gated capture.
- `explain-dead-code`: explain one artifact module's roots, shipment, and retention.
- `find-unused-code`: prioritize artifact-scoped unreachable module candidates.
- `review-context-change`: compare compatible lint or test snapshots.

The plugin contains agent guidance and the MCP launcher. The evidence engine is implemented and
published by [`rstackjs/context`](https://github.com/rstackjs/context), while Rstack CLI provides
the `rs mcp` command and configuration adapters. Installing this repository does not duplicate or
independently version the runtime.

## Rspack Skills

### rspack-best-practices
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@
"format": "prettier -w .",
"lint": "rslint && prettier -c . && pnpm run check-spell",
"lint:write": "rslint --fix && prettier -w .",
"prepare": "skills-package-manager install && simple-git-hooks && pnpm build"
"prepare": "skills-package-manager install && simple-git-hooks && pnpm build",
"test:plugin": "node scripts/test-rstack-context-plugin.mjs"
},
"simple-git-hooks": {
"pre-commit": "pnpm run lint:write"
Expand Down
4 changes: 2 additions & 2 deletions plugin.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
{
"$schema": "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json",
"name": "rstack",
"version": "0.1.0",
"description": "Agent Skills for debugging, tracing, upgrading, and analyzing Rstack projects.",
"version": "0.2.4",
"description": "Agent Skills and project context tools for debugging, tracing, upgrading, and analyzing Rstack projects.",
"author": {
"name": "RstackJS",
"url": "https://github.com/rstackjs"
Expand Down
46 changes: 46 additions & 0 deletions plugins/rstack/.codex-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
{
"name": "rstack",
"version": "0.2.4+codex.20260814103637",
"description": "Agent Skills and project context tools for debugging, tracing, upgrading, and analyzing Rstack projects.",
"author": {
"name": "RstackJS",
"url": "https://github.com/rstackjs"
},
"homepage": "https://github.com/rstackjs/agent-skills",
"repository": "https://github.com/rstackjs/agent-skills",
"license": "MIT",
"keywords": [
"rspack",
"rsbuild",
"rslib",
"rstest",
"rspress",
"rsdoctor",
"rslint",
"storybook",
"skills"
],
"skills": "./skills/",
"mcpServers": "./.mcp.json",
"interface": {
"displayName": "Rstack",
"shortDescription": "Debug, analyze, migrate, and optimize Rstack projects.",
"longDescription": "Use Rstack skills and checkout-local project context to configure, migrate, debug, trace, optimize, and document projects with Rspack, Rsbuild, Rslib, Rstest, Rspress, Rsdoctor, and Rslint.",
"developerName": "RstackJS",
"category": "Developer Tools",
"capabilities": [
"Project context",
"Build analysis",
"Lint and test capture",
"Write"
],
"composerIcon": "./assets/rspack-logo.png",
"logo": "./assets/rspack-logo.png",
"defaultPrompt": [
"Show the current Rstack build, lint, and test context.",
"Find artifact-scoped unused-code candidates.",
"Debug and optimize this Rspack build."
],
"websiteURL": "https://github.com/rstackjs/agent-skills"
}
}
12 changes: 12 additions & 0 deletions plugins/rstack/.mcp.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"mcpServers": {
"rstack": {
"command": "node",
"args": [
"--input-type=module",
"--eval",
"import { spawn } from 'node:child_process'; import { globSync, readFileSync } from 'node:fs'; import { createRequire } from 'node:module'; import { dirname, join, resolve } from 'node:path'; const cwd = process.cwd(); const roots = [cwd]; const patterns = []; try { const rootPackage = JSON.parse(readFileSync(join(cwd, 'package.json'), 'utf8')); const workspaces = Array.isArray(rootPackage.workspaces) ? rootPackage.workspaces : rootPackage.workspaces?.packages; if (Array.isArray(workspaces)) patterns.push(...workspaces); } catch (error) { if (error?.code !== 'ENOENT') throw error; } try { const lines = readFileSync(join(cwd, 'pnpm-workspace.yaml'), 'utf8').split(/\\r?\\n/u); let inPackages = false; for (const line of lines) { if (/^packages:\\s*$/u.test(line)) { inPackages = true; continue; } if (inPackages && /^\\S/u.test(line)) break; const match = inPackages ? /^\\s*-\\s*(?:['\"]([^'\"]+)['\"]|([^#]+?))(?:\\s+#.*)?\\s*$/u.exec(line) : null; if (match) patterns.push((match[1] ?? match[2]).trim()); } } catch (error) { if (error?.code !== 'ENOENT') throw error; } const includes = [...new Set(patterns.filter((pattern) => typeof pattern === 'string' && !pattern.startsWith('!')).map((pattern) => `${pattern.replace(/\\/$/u, '')}/package.json`))]; const excludes = patterns.filter((pattern) => typeof pattern === 'string' && pattern.startsWith('!')).map((pattern) => `${pattern.slice(1).replace(/\\/$/u, '')}/package.json`); if (includes.length > 0) for (const packageJson of globSync(includes, { cwd, exclude: [...excludes, '**/node_modules/**'] })) roots.push(dirname(resolve(cwd, packageJson))); for (const packageJson of globSync('*/package.json', { cwd, exclude: ['node_modules/**'] })) roots.push(dirname(resolve(cwd, packageJson))); let packageJsonPath; let localRequire; for (const root of new Set(roots)) { const candidate = createRequire(join(root, 'package.json')); try { packageJsonPath = candidate.resolve('rstack/package.json'); localRequire = candidate; break; } catch (error) { if (error?.code !== 'MODULE_NOT_FOUND') throw error; } } const run = async (command, args) => { const child = spawn(command, args, { stdio: 'inherit' }); const { code, signal } = await new Promise((resolve, reject) => { child.once('error', reject); child.once('exit', (code, signal) => resolve({ code, signal })); }); if (signal) process.kill(process.pid, signal); else process.exitCode = code ?? 1; }; try { if (packageJsonPath) { const { bin } = localRequire(packageJsonPath); const binPath = typeof bin === 'string' ? bin : (bin.rs ?? bin.rstack); if (!binPath) throw new Error('The workspace-local rstack package does not declare an rs binary.'); await run(process.execPath, [resolve(dirname(packageJsonPath), binPath), 'mcp']); } else { await run('rs', ['mcp']); } } catch (error) { if (error?.code !== 'ENOENT') throw error; console.error('Unable to launch Rstack MCP server: install rstack in this repository, a declared workspace package, an immediate child package, or put rs on PATH.'); process.exitCode = 1; }"
]
}
}
}
7 changes: 7 additions & 0 deletions plugins/rstack/assets/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Plugin assets

`rspack-logo.png` is the official Rspack favicon, copied without modification from
[Rstack Design Resources](https://github.com/rstackjs/rstack-design-resources/blob/main/rspack/favicon-128x128.png).

The asset is licensed under
[CC BY-NC-SA 4.0](https://creativecommons.org/licenses/by-nc-sa/4.0/).
Binary file added plugins/rstack/assets/rspack-logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
21 changes: 21 additions & 0 deletions plugins/rstack/skills/analyze-build/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
---
name: analyze-build
description: Use when summarizing Rstack build health, errors, chunks, packages, bundle opportunities, or build-wide tree-shaking evidence from an Rsdoctor artifact.
---

# Analyze an Rstack build

1. Call `project_status` to establish available contexts and latest build observations. Analysis may still proceed from an explicit artifact when no context exists.
2. Obtain the explicit Rsdoctor `dataFile`. If it is missing, identify the product and inspect the matching `@rsdoctor/rspack-plugin` or `@rsdoctor/webpack-plugin` version before offering a capture. Use `RSDOCTOR_OUTPUT=json` only when the plugin version is at least `1.5.11`. For a missing, unknown, or older plugin, follow the `rsdoctor-analysis` Generation Gate: install/register the plugin when missing, configure `output.mode='brief'` with JSON output, and build with `RSDOCTOR=true` without `RSDOCTOR_OUTPUT`. Use `rs build` for an application or `rs lib` for a library, include `RSTACK_CONTEXT=1`, and ask before installing, configuring, building, or capturing.
3. Call `rsdoctor_analyze` with the narrowest suitable tool: `build_summary`, `errors_list`, `chunks_list`, `bundle_optimize`, one `tree_shaking_*` view, or one `packages_*` view. When artifact metadata is returned, compare its compiler environment and compilation hash with the build summaries from `project_status` before selecting a context.
4. When a context exists, call `product_roots` with the selected `contextId`, `dataFile`, and `rootLimit: 20`. Use `rootSummary` for complete counts and treat the returned roots as representatives; request more only when the investigation needs them. Use context-bound claims only when `artifactBinding` is `exact`; report `mismatch` or `explicit-unverified` as artifact-only evidence.
5. Treat omitted sections as unavailable. Reserve zero or healthy labels for evidence the tool actually returned.
6. Call `report_link` only when an optional navigable report would materially help.

Never require a GUI or infer source execution or repository-wide dead code from an artifact.

When freshness is `partial` with no changed paths, say that the recorded inputs are unchanged but the captured input set is incomplete; do not present it as fresh proof.

If Rstack Context is unavailable, use the `rsdoctor-analysis` skill with the explicit artifact instead.

The MCP is intentionally limited to the checkout containing the Codex project/session root and has no workspace argument. For an external checkout, ask the user to start a new Codex session rooted at that checkout.
16 changes: 16 additions & 0 deletions plugins/rstack/skills/assess-change-impact/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
---
name: assess-change-impact
description: Use when estimating artifact-scoped dependents, affected product roots, or bundled chunks for one Rstack module.
---

# Assess artifact module impact

1. Call `project_status` and select the context whose package root, product, environment, and target match the build. Deduplicate repeated runs by `contextId`.
2. Confirm the subject is an artifact module ID, exact path/name, or unique suffix. Route function, class, export, and other source-symbol questions to source analysis.
3. Obtain the explicit Rsdoctor `dataFile`. If it is missing, inspect the matching Rsdoctor plugin version before offering a capture. Use `RSDOCTOR_OUTPUT=json` only when the plugin version is at least `1.5.11`. For a missing, unknown, or older plugin, follow the `rsdoctor-analysis` Generation Gate: install/register the plugin when missing, configure `output.mode='brief'` with JSON output, and build with `RSDOCTOR=true` without `RSDOCTOR_OUTPUT`. Use `rs build` for an application or `rs lib` for a library, include `RSTACK_CONTEXT=1`, and ask before installing, configuring, building, or capturing.
4. Call `module_impact` with `direction: "dependents"` and an optional `maxDepth` from 1 to 16.
5. Report visited dependents, `totalVisited` versus `returned`, reached product roots by kind, distinct chunks, truncation, bounds, and provenance.

Describe only the explicit artifact graph. Source-only, test-only, runtime-created, and external consumers may be unobserved.

The MCP is intentionally limited to the checkout containing the Codex project/session root and has no workspace argument. For an external checkout, ask the user to start a new Codex session rooted at that checkout.
Loading