diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml
new file mode 100644
index 0000000..c126a0e
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/bug_report.yml
@@ -0,0 +1,62 @@
+name: Bug Report
+description: Something is broken or behaving unexpectedly.
+labels: ["bug", "needs-triage"]
+body:
+ - type: markdown
+ attributes:
+ value: |
+ Thank you for taking the time to report a bug. Please fill in as much detail as you can.
+ - type: input
+ id: version
+ attributes:
+ label: React-Sentinel version
+ description: Output of `npx @edgarbrunet/react-sentinel --version` or the version in your `package.json`.
+ placeholder: "0.1.0"
+ validations:
+ required: true
+ - type: input
+ id: node
+ attributes:
+ label: Node.js version
+ description: Output of `node --version`.
+ placeholder: "v20.x.x"
+ validations:
+ required: true
+ - type: input
+ id: client
+ attributes:
+ label: AI client / IDE
+ description: Claude Code, Cursor, GitHub Copilot, etc.
+ placeholder: "Claude Code 1.x"
+ validations:
+ required: true
+ - type: textarea
+ id: description
+ attributes:
+ label: What happened?
+ description: A clear and concise description of the bug.
+ validations:
+ required: true
+ - type: textarea
+ id: steps
+ attributes:
+ label: Steps to reproduce
+ placeholder: |
+ 1. Run `node dist/index.js mcp --headless`
+ 2. Call tool `navigate_replay` with URL …
+ 3. Observe …
+ validations:
+ required: true
+ - type: textarea
+ id: expected
+ attributes:
+ label: Expected behavior
+ description: What should have happened?
+ validations:
+ required: true
+ - type: textarea
+ id: logs
+ attributes:
+ label: Relevant logs or error output
+ description: Paste stderr output or stack traces here. Wrap in triple-backtick code blocks.
+ render: text
diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml
new file mode 100644
index 0000000..284aa63
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/feature_request.yml
@@ -0,0 +1,38 @@
+name: Feature Request
+description: Suggest a new capability or improvement.
+labels: ["enhancement"]
+body:
+ - type: markdown
+ attributes:
+ value: |
+ Before submitting, check open issues and the [docs/](../docs/) folder to see if the feature is already being tracked or documented.
+ - type: textarea
+ id: problem
+ attributes:
+ label: Problem or motivation
+ description: What problem does this solve? What is currently painful or impossible?
+ validations:
+ required: true
+ - type: textarea
+ id: solution
+ attributes:
+ label: Proposed solution
+ description: Describe what you would like to see. Pseudocode, tool names, or UX descriptions are all helpful.
+ validations:
+ required: true
+ - type: textarea
+ id: alternatives
+ attributes:
+ label: Alternatives considered
+ description: What workarounds have you tried? Why are they insufficient?
+ - type: dropdown
+ id: impact
+ attributes:
+ label: Who benefits?
+ options:
+ - AI agent users (tool consumers)
+ - IDE / MCP client integrators
+ - Contributors / maintainers
+ - All of the above
+ validations:
+ required: true
diff --git a/.github/ISSUE_TEMPLATE/good_first_issue.yml b/.github/ISSUE_TEMPLATE/good_first_issue.yml
new file mode 100644
index 0000000..1c27330
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/good_first_issue.yml
@@ -0,0 +1,36 @@
+name: Good First Issue
+description: A clearly scoped task suitable for new contributors.
+labels: ["good first issue", "help wanted"]
+body:
+ - type: markdown
+ attributes:
+ value: |
+ Use this template to propose a task that a new contributor could pick up with minimal context.
+ Keep the scope as small and self-contained as possible.
+ - type: textarea
+ id: task
+ attributes:
+ label: Task description
+ description: What exactly needs to be done? Be precise — include file paths, function names, or test commands when applicable.
+ validations:
+ required: true
+ - type: textarea
+ id: why
+ attributes:
+ label: Why it matters
+ description: Brief rationale — what does completing this improve?
+ validations:
+ required: true
+ - type: textarea
+ id: hints
+ attributes:
+ label: Hints and starting points
+ description: Pointers to relevant files, docs, or functions. Include the commands needed to validate the change.
+ validations:
+ required: true
+ - type: input
+ id: estimate
+ attributes:
+ label: Effort estimate
+ description: Rough estimate (e.g., "1–2 hours", "half a day").
+ placeholder: "1–2 hours"
diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md
new file mode 100644
index 0000000..6a75fc9
--- /dev/null
+++ b/.github/PULL_REQUEST_TEMPLATE.md
@@ -0,0 +1,31 @@
+## Summary
+
+
+
+## Motivation
+
+
+
+## Changes
+
+
+
+-
+
+## Validation
+
+
+
+- [ ] `npm run check` passes (TypeScript strict type-check)
+- [ ] `npm run build` succeeds
+- [ ] `cd examples/test-app && npm run build` succeeds
+- [ ] Manual smoke test against a running React app (describe below)
+- [ ] E2E script: `npm run e2e:smoke` (requires a running test-app)
+
+```
+# paste relevant output here
+```
+
+## Notes for reviewers
+
+
diff --git a/.github/labels.yml b/.github/labels.yml
new file mode 100644
index 0000000..acf9fa6
--- /dev/null
+++ b/.github/labels.yml
@@ -0,0 +1,30 @@
+- name: bug
+ color: d73a4a
+ description: Something is not working as expected
+- name: enhancement
+ color: a2eeef
+ description: New feature or improvement request
+- name: good first issue
+ color: 7057ff
+ description: Easy entry point for new contributors
+- name: help wanted
+ color: 008672
+ description: Maintainer is actively seeking contributions
+- name: needs-triage
+ color: e4e669
+ description: Not yet reviewed by a maintainer
+- name: documentation
+ color: 0075ca
+ description: Improvements or additions to documentation
+- name: dependencies
+ color: 0366d6
+ description: Pull requests that update a dependency
+- name: ci
+ color: bfd4f2
+ description: Related to the CI/CD pipeline
+- name: agent-pack
+ color: f9d0c4
+ description: Agent Pack CLI feature area
+- name: mcp-protocol
+ color: c5def5
+ description: MCP server core or tool definitions
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
new file mode 100644
index 0000000..163063f
--- /dev/null
+++ b/.github/workflows/ci.yml
@@ -0,0 +1,42 @@
+name: CI
+
+on:
+ push:
+ branches: ["main", "sprint/**"]
+ pull_request:
+ branches: ["main"]
+
+jobs:
+ build:
+ name: Type-check & build
+ runs-on: ubuntu-latest
+
+ strategy:
+ matrix:
+ node-version: [20, 22]
+
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Setup Node.js ${{ matrix.node-version }}
+ uses: actions/setup-node@v4
+ with:
+ node-version: ${{ matrix.node-version }}
+ cache: "npm"
+
+ - name: Install root dependencies
+ run: npm ci
+
+ - name: Type-check
+ run: npm run check
+
+ - name: Build library
+ run: npm run build
+
+ - name: Install test-app dependencies
+ working-directory: examples/test-app
+ run: npm install
+
+ - name: Build test-app
+ working-directory: examples/test-app
+ run: npm run build
diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md
new file mode 100644
index 0000000..8ae252d
--- /dev/null
+++ b/CODE_OF_CONDUCT.md
@@ -0,0 +1,30 @@
+# Code of Conduct
+
+## Our pledge
+
+We are committed to making participation in this project a welcoming and respectful experience for everyone, regardless of age, background, gender identity, sexual orientation, disability, ethnicity, or level of experience.
+
+## Our standards
+
+**Expected behavior:**
+
+- Use welcoming and inclusive language.
+- Respect differing viewpoints and experiences.
+- Accept constructive feedback gracefully.
+- Focus on what is best for the community.
+
+**Unacceptable behavior:**
+
+- Harassment, insults, or derogatory comments in any form.
+- Publishing others' private information without permission.
+- Conduct that would be considered inappropriate in a professional setting.
+
+## Enforcement
+
+Instances of unacceptable behavior may be reported by contacting the project maintainer via the email address on the npm package page or through GitHub's private reporting tools. All complaints will be reviewed and investigated promptly.
+
+The project maintainer has the right to remove, edit, or reject contributions that do not align with this Code of Conduct, and may temporarily or permanently ban contributors for other behaviors deemed inappropriate.
+
+## Attribution
+
+This Code of Conduct is adapted from the [Contributor Covenant](https://www.contributor-covenant.org), version 2.1.
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 0000000..e5cce46
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,74 @@
+# Contributing to React-Sentinel
+
+Thank you for your interest in React-Sentinel! This guide covers how to set up the project locally, propose changes, and get feedback.
+
+## Prerequisites
+
+- **Node.js ≥ 20** (enforced by `engines` in `package.json`)
+- **npm** or **pnpm** (pnpm workspace is configured; npm works for the root package)
+- A Chromium-compatible browser (Playwright installs its own)
+
+## Local setup
+
+```bash
+git clone https://github.com/edgarbnt/ReactSentinel.git
+cd ReactSentinel
+npm install
+npm run build
+```
+
+Verify the build:
+
+```bash
+npm run check # TypeScript strict type-check
+npm run build # compile src/ → dist/
+cd examples/test-app && npm run build # compile the demo app
+```
+
+## Project layout
+
+| Path | What it contains |
+|---|---|
+| `src/` | TypeScript source for the MCP server |
+| `dist/` | Compiled output (committed for consumption as a local checkout) |
+| `assets/agent-pack/` | Markdown guidance assets bundled by the CLI |
+| `examples/test-app/` | Minimal Vite/React app used as the E2E fixture |
+| `scripts/` | E2E smoke and diagnostic helpers |
+| `docs/` | Product, workflow, and sprint documentation |
+| `.github/` | CI workflows and issue/PR templates |
+
+## Making a change
+
+1. **Fork** the repository and create a topic branch from `main`.
+2. Keep changes **focused**: one logical concern per pull request.
+3. Run `npm run check && npm run build` before pushing.
+4. Describe the change clearly in the PR body using the provided template.
+
+## Commit style
+
+Follow the [Conventional Commits](https://www.conventionalcommits.org/) prefix pattern:
+
+```
+feat: short description
+fix: short description
+docs: short description
+chore: short description
+```
+
+Include the relevant Jira/issue ID when applicable (e.g., `fix: [SCRUM-355] add CI workflow`).
+
+## Reporting issues
+
+Use the GitHub issue templates:
+
+- **Bug report** — unexpected runtime behavior or crashes.
+- **Feature request** — ideas for new capabilities.
+- **Good first issue** — clearly scoped tasks suitable for new contributors (labeled `good first issue`).
+
+## Security
+
+Please do **not** open a public issue for security vulnerabilities. See [SECURITY.md](SECURITY.md) for the responsible disclosure process.
+
+## License
+
+By contributing, you agree that your contributions will be licensed under the [MIT License](LICENSE) that covers this project.
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..bcb99c0
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2026 Edgar Brunet
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/README.md b/README.md
index e3484ba..e39f215 100644
--- a/README.md
+++ b/README.md
@@ -1,292 +1,156 @@
# React-Sentinel
-> An MCP server that gives AI agents eyes inside the browser — turning guesswork into observation.
+
+
+
-## What it does
+> Give AI agents a runtime debugger for React apps: inspect live state, reproduce bugs, and validate fixes before editing code.
-React-Sentinel bridges AI terminals (Claude, Copilot CLI…) to a live browser runtime via the [Model Context Protocol](https://modelcontextprotocol.io). Instead of reading static source files, the AI can **observe and validate** its actions in real time.
+React-Sentinel is an MCP server for AI coding agents and IDE assistants. Instead of guessing from source code alone, the agent can connect to a live browser session, inspect React runtime data, replay user flows, and turn observations into assertions.
-| Capability | Description |
-|---|---|
-| **Runtime Inspection** | Explore the React Fiber tree, inspect component props, extract simple hook values (`useState`, `useRef`, `useMemo`), surface React context values, and audit network or console signals live |
-| **Replay Sandbox** | Launch an isolated Playwright browser, navigate to a target app, and replay deterministic interaction sequences without touching the developer's live browser |
-| **Interaction Simulation** | Drive either the attached live tab or the replay browser (click, fill, press, navigate) to reproduce bugs before attempting to fix them |
-| **Validation Assertions** | Assert DOM text/visibility, React component presence and simple prop/state values, plus console/network invariants, with structured pass/fail output |
-
-## Stack
-
-- **Runtime:** Node.js 20 LTS
-- **Language:** TypeScript (strict)
-- **Package manager:** pnpm
-- **MCP SDK:** `@modelcontextprotocol/sdk`
-- **Browser automation:** Playwright
-
-## Product workflows
-
-See [docs/workflows.md](docs/workflows.md) for the main MVP workflows:
+## Why it exists
-- live Chrome attach,
-- isolated replay reproduction,
-- replay-only sandbox hot patching,
-- and the minimal local MCP integration.
+Most AI coding tools can edit code quickly, but they still struggle to answer simple runtime questions:
-## Capability status semantics
+- Which prop or state value keeps this button disabled?
+- Did the UI fail because React rendered the wrong branch, or because the API returned bad data?
+- Does a proposed fix actually work in the browser before the agent edits the repository?
-`get_server_info` now exposes three capability states:
+React-Sentinel closes that gap by exposing a browser runtime through MCP.
-- **available** — the feature is backed by one or more MCP tools and is ready for normal use. Example: `apply_patch_then_replay`.
-- **partial** — the feature is usable, but intentionally bounded or narrower than the headline suggests. Example: `runtime_inspection` is limited to readable snapshots and selected hook types, and `shadow_sandbox` only supports script-on-page patches.
-- **planned** — the capability name exists for roadmap continuity, but it is not yet backed by a usable MCP tool.
+## Core capabilities
-The same payload also exposes `capabilityDetails` and `capabilitiesByMode` so an agent can see which tools back each capability and whether it applies to **attach**, **replay**, or **sandbox** mode.
+| Capability | What it gives the agent |
+|---|---|
+| **Runtime inspection** | React tree, component props, selected hook values, context values, render signals |
+| **Replay reproduction** | An isolated Playwright browser for deterministic bug reproduction |
+| **Live attach** | Optional reuse of a real Chrome tab when the bug depends on existing user state |
+| **Assertions** | Structured pass/fail checks for DOM, React state, console output, and network activity |
+| **Shadow sandbox** | Runtime-only patch validation in replay mode before touching source files |
-## Prerequisites
+## Quick start
-- **Node.js ≥ 20** — check with `node --version`
-- **pnpm** — install with `npm install -g pnpm` if needed
-- An MCP-compatible client: [Claude Desktop](https://claude.ai/download) or any terminal that supports MCP stdio transport
+React-Sentinel is being prepared for public npm distribution under **`@edgarbrunet/react-sentinel`**. That is the only public npm package name documented by this repository.
-## Local setup
+### Public npm command
-### 1. Install dependencies
+When the scoped package is available, the recommended public command is:
```bash
-# From the project root
-pnpm install
+npx -y @edgarbrunet/react-sentinel init-mcp --client auto --mode npx
```
-Playwright is already declared in the workspace dependencies. If Chromium is missing on a fresh machine, install it once with:
+Use `--write` when the target environment has a well-known project or user config path:
```bash
-npx playwright install chromium
+npx -y @edgarbrunet/react-sentinel init-mcp --client claude-code --mode npx --write
+npx -y @edgarbrunet/react-sentinel init-mcp --client cursor --mode npx --write
+npx -y @edgarbrunet/react-sentinel init-mcp --client github-copilot --mode npx --write
```
-### 2. Build once and run the local doctor
+### Local checkout flow
-```bash
-npm run build
-node dist/index.js doctor
-```
-
-`doctor` validates the local Node runtime, checks that the replay browser can launch, and warns if Chrome CDP is not available yet. A CDP warning is expected if you only plan to use replay mode.
-
-### 3. Start the MCP server
-
-Stable local CLI:
+If you are running from this repository checkout before public publication:
```bash
-node dist/index.js start --headed
+npm install
+npm run build
+node dist/index.js init-mcp --client auto --mode local
```
-Development mode with hot reload:
-
-```bash
-pnpm dev
-```
+## Supported environments
-The server starts on **stdio transport** — it waits for MCP messages from a connected client.
-You should see in stderr: `[react-sentinel] MCP server started (stdio transport...) ✅`
+| Environment | Status | Recommended setup |
+|---|---|---|
+| **Claude Code** | Supported | `init-mcp --client claude-code --mode npx --write` or `--mode local` from a checkout |
+| **Claude Desktop** | Supported | `init-mcp --client claude-desktop --mode npx --write` |
+| **Cursor** | Supported | `init-mcp --client cursor --mode npx --write` |
+| **GitHub Copilot / VS Code** | Supported | `init-mcp --client github-copilot --mode npx --write` |
+| **Gemini CLI** | Supported | `init-mcp --client gemini-cli --mode npx --write` |
+| **Generic MCP clients** | Supported manually | `init-mcp --client generic-mcp --mode npx` then paste the snippet manually |
+| **Other IDE integrations** | Partial | Reuse the generic MCP command and adapt the client-specific config shape |
-### 4. Start the test app
+See the full setup instructions in [docs/universal-install.md](docs/universal-install.md) and the per-environment guide in [docs/integration-guides.md](docs/integration-guides.md).
-Open a second terminal:
+## When to use React-Sentinel
-```bash
-cd examples/test-app
-pnpm install # first time only
-pnpm dev # starts Vite on http://localhost:5173
-```
+Use it when the agent must understand what the app is doing at runtime, not just what the source code suggests.
-The test app is a minimal React 18 page used as a live inspection fixture.
+- a React bug depends on real state, props, or context;
+- the UI diverges from expected behavior after user interactions;
+- console or network signals may explain the failure faster than static reading;
+- the agent wants to validate a fix hypothesis in replay mode before editing source files.
-### 4b. Run the one-command E2E smoke test
+For runtime workflows and prompt examples, see [docs/agent-runtime-ux.md](docs/agent-runtime-ux.md) and [docs/workflows.md](docs/workflows.md).
-```bash
-npm run e2e:smoke
-```
+## Install modes
-This runner starts from the MCP client side, talks to the server over stdio, and verifies the end-to-end concept against the demo app. See [`docs/scenarios/e2e-smoke.md`](docs/scenarios/e2e-smoke.md) for the exact coverage.
+React-Sentinel keeps the CLI binary name `react-sentinel`, but the public npm package name is scoped.
-### 4c. Run the diagnosis-only benchmark
+| Mode | Best when | Launch shape |
+|---|---|---|
+| **local** | You are running from this checkout or a local package install | `node /absolute/path/to/dist/index.js mcp --headless` |
+| **global** | You installed the CLI binary globally yourself | `react-sentinel mcp --headless` |
+| **npx** | You want a client to fetch the public package on demand | `npx -y @edgarbrunet/react-sentinel mcp --headless` |
-```bash
-npm run e2e:diagnose
-```
+## Product docs
-This benchmark verifies the "find the problem before fixing it" promise: the agent must reproduce a bug, inspect MCP runtime signals, and conclude on the root cause without editing the app. See [`docs/scenarios/diagnosis-benchmark.md`](docs/scenarios/diagnosis-benchmark.md).
+- [docs/universal-install.md](docs/universal-install.md) — package naming, install modes, target selection, limitations
+- [docs/integration-guides.md](docs/integration-guides.md) — Claude, Cursor, Copilot, Gemini, and generic MCP setup guides
+- [docs/agent-runtime-ux.md](docs/agent-runtime-ux.md) — trigger heuristics, mode choice, and example agent prompts
+- [docs/adoption-checklist.md](docs/adoption-checklist.md) — onboarding checklist and validation scenarios
+- [docs/local-ports.md](docs/local-ports.md) — legitimate local URLs, ports, and CDP endpoints used in docs and tests
+- [docs/public-readiness-audit.md](docs/public-readiness-audit.md) — Sprint 15 audit of public-repo cleanup decisions
+- [docs/workflows.md](docs/workflows.md) — deep workflow reference for Attach, Replay, Sandbox, and MCP wiring
+- [docs/local-diagnostics-checklist.md](docs/local-diagnostics-checklist.md) — troubleshooting
-### 5. Connect your MCP client
+## Local development
-React-Sentinel now supports three stdio launch variants for MCP clients:
+### Requirements
-| Variant | Best when | Generated command |
-|---|---|---|
-| **local** | You are running from this checkout or from a local package install | `node /absolute/path/to/ReactSentinel/dist/index.js mcp --headless` |
-| **global** | You installed `react-sentinel` globally | `react-sentinel mcp --headless` |
-| **npx** | You want zero permanent install on the client machine | `npx -y react-sentinel mcp --headless` |
+- Node.js 20+
+- npm (or pnpm if you prefer workspace tooling)
+- Playwright Chromium for replay mode
-Generate a ready-to-paste snippet instead of hand-writing JSON:
+### Build and validate
```bash
-node dist/index.js init-mcp --client claude-desktop --mode local
-node dist/index.js init-mcp --client claude-code --mode npx
+npm install
+npm run check
+npm run build
+cd examples/test-app && npm install && npm run build
```
-Write the config directly to the default client file:
+If Playwright Chromium is missing:
```bash
-node dist/index.js init-mcp --client claude-desktop --mode local --write
-node dist/index.js init-mcp --client claude-code --mode npx --write
+npx playwright install chromium
```
-By default:
-
-- **Claude Desktop** writes to `~/.config/Claude/claude_desktop_config.json` on Linux, `~/Library/Application Support/Claude/claude_desktop_config.json` on macOS, and `%APPDATA%\Claude\claude_desktop_config.json` on Windows.
-- **Claude Code** writes to `.mcp.json` in the current directory so the config stays project-scoped unless you override it with `--config-path`.
-
-Validate an existing config before restarting your client:
+### Start the server locally
```bash
-node dist/index.js doctor --config-path ~/.config/Claude/claude_desktop_config.json
-```
-
-**Claude Desktop** config shape:
-
-```json
-{
- "mcpServers": {
- "react-sentinel": {
- "command": "node",
- "args": ["/absolute/path/to/ReactSentinel/dist/index.js", "mcp", "--headless"]
- }
- }
-}
+node dist/index.js mcp --headless
```
-> Use `--mode local` for this repository checkout, `--mode global` after a global install, and `--mode npx` when the client should fetch React-Sentinel on demand. Add `--headed` to `init-mcp` if you want the generated snippet to open visible replay browsers by default.
-
-### 6. Optional: attach to a live Chrome session
-
-To inspect the browser you are already using, start Chrome with remote debugging enabled:
+### Run the smoke benchmark
```bash
-google-chrome --remote-debugging-port=9222 --user-data-dir=/tmp/react-sentinel-cdp
-```
-
-Then call `get_attach_status` to check whether the CDP endpoint is reachable. If it is not, the tool returns a launch command and tells you to keep using replay mode until Chrome attach is ready.
-
-Once the endpoint is ready, use `get_attach_tabs` to list the available page tabs and `select_attach_tab` to pick one by index, URL, or title. The first `select_attach_tab` response is a consent preview: it explains that React-Sentinel will inspect the selected tab's runtime signals and may run interaction tools in that same tab. Re-run `select_attach_tab` with `confirm: true` to enable live browser mode for that tab. After consent is recorded, the runtime inspection and interaction tools reuse only that live tab instead of opening the isolated sandbox browser. If the tab closes, React-Sentinel clears the selection and asks you to choose a tab again.
-
-If you get stuck during local setup, use the [local diagnostics checklist](docs/local-diagnostics-checklist.md).
-
-## Replay sandbox tools
-
-- `get_server_info` advertises `replay_sandbox` as available and `shadow_sandbox` as partial because the sandbox currently supports script-on-page runtime patches only.
-- `get_session_status` reports whether React-Sentinel is currently using the live attached tab or the isolated replay browser, and exposes the replay headless/headed configuration.
-- `navigate_replay` opens the isolated replay browser, navigates to a URL, waits for `load`, `domcontentloaded`, or `networkidle`, and returns readable navigation errors when the target app is unavailable.
-- `replay_interactions` replays ordered `click`, `type`, `fill`, `wait`, and `press` steps in that replay browser and logs the result of each step.
-- `validate_after_action` now supports richer assertions for DOM, React runtime, console, and network checks after a single interaction.
-- `validate_scenario` runs a multi-step replay plus multiple assertions and returns both a raw JSON report and a readable Markdown report.
-- `apply_runtime_patch` registers an ephemeral JavaScript patch in the replay sandbox without touching local files.
-- `apply_patch_then_replay` applies a patch, runs replay steps, evaluates assertions, and returns an explicit `patch_validated` / `patch_failed` verdict plus a Markdown report.
-- `reset_runtime_patches` removes active replay patches by reloading the clean sandbox page when possible, or by resetting the replay session when stronger cleanup is required.
-
-`navigate_replay` and `replay_interactions` accept `headless` so the same sandbox can run invisibly in automated flows or visibly in a local debugging session.
-
-## Shadow sandbox patch payload (Sprint 9 MVP)
-
-Sprint 9 adds a first ephemeral hot-patch format for the replay sandbox:
-
-```json
-{
- "patch": {
- "type": "script",
- "target": "page",
- "source": "const originalFetch = window.fetch.bind(window); /* ... */",
- "metadata": {
- "id": "mock-error-fix",
- "label": "mock-api-error-fix",
- "source": "ai-generated",
- "expiresWithSession": true
- }
- }
-}
+npm run e2e:smoke
```
-- `type: "script"` is the only supported patch type in the Sprint 9 MVP.
-- `target: "page"` is the only supported target; patches run in the replay page main world.
-- `source` is validated before execution and must stay within the MVP size cap.
-- `metadata.expiresWithSession` is mandatory and locks the patch to the replay session lifetime.
-- `metadata.id` is optional but recommended for stable reporting and deduplication.
-
-## Shadow sandbox limits and safety
-
-- **Replay only:** runtime patches never touch repository files and are never applied to the live attach tab.
-- **Session-scoped:** patches are bound to the current replay session and disappear after `reset_runtime_patches` or replay session shutdown.
-- **Supported input:** the MVP accepts JavaScript script bodies only; top-level ES module imports are not supported inside `source`.
-- **Error surfacing:** syntax and runtime failures are returned with a `[runtime_patch:]` prefix so the failing patch is explicit.
-- **Result preview only:** return values are reduced to a serializable preview; complex objects are stringified to a readable placeholder when needed.
-- **Cleanup fallback:** when the sandbox cannot safely remove an init script in place, React-Sentinel falls back to a full replay-session reset to guarantee a clean state.
-
-## Runtime inspection limits
-
-The hook/state inspector is intentionally bounded so responses stay readable for AI clients:
-
-- **Supported hook cells:** `useState`, `useRef`, and `useMemo` are extracted reliably.
-- **Traversal cap:** hook traversal stops after **25 cells** per component.
-- **Custom / unrecognized hooks:** React-Sentinel does not infer custom hook names. It walks the underlying Fiber hook cells; cells that do not match a known shape and are not serializable primitives are omitted, while primitive unknowns are exposed as `unknown`.
-- **Truncation rules:** long strings, arrays, object keys, `Map`, `Set`, React elements, DOM elements, and cyclic values are shortened or replaced with explicit placeholders such as `[Circular]`, `[MaxDepthReached]`, `[MaxNodesReached]`, `[Function:...]`, `[ReactElement:...]`, and `[HTMLElement:...]`.
-
-These limits are by design: they keep the output stable and compact enough to be useful in the middle of a debugging session.
-
-## Sprint 10 render monitor tools
-
-- `get_render_counts` returns per-component render counters with the component name, path, and first/last observed timestamps.
-- `get_render_hotspots` flags components that crossed a configurable render threshold inside a short time window and adds a probable-cause hint.
-- `get_hook_changes` returns the chronological hook diffs captured for a component, plus the most suspicious unstable hook values.
-
-## Attach vs replay limits
-
-React-Sentinel now exposes two browser session modes:
-
-- **Attach mode** reuses a developer-selected Chrome tab through CDP. It preserves the real browser state, cookies, and extensions, but it requires explicit consent and depends on the tab staying open.
-- **Replay mode** uses an isolated Playwright Chromium session. It is safer for deterministic reproduction and scripted replays, but it does not inherit the user's current browsing state unless the scenario rebuilds it step by step.
-
-Most runtime and interaction tools keep the same behavior in both modes because they resolve through the same runtime bridge. The main difference is which page is being driven: a real user tab in attach mode, or the isolated replay browser in replay mode.
-
-## Available scripts
-
-| Script | Description |
-|---|---|
-| `pnpm dev` | Start MCP server with hot-reload (`tsx watch`) |
-| `pnpm build` | Compile TypeScript to `dist/` |
-| `pnpm start` | Run compiled server (requires `pnpm build` first) |
-| `pnpm typecheck` | Type-check without emitting |
-| `pnpm check` | Type-check and print a ✅ confirmation |
-
## Project structure
+```text
+src/ CLI, MCP server, runtime tools
+docs/ Public product and workflow documentation
+assets/agent-pack/ Reusable agent-pack assets and profile guidance
+examples/test-app/ Local demo app and validation fixture
```
-src/
-├── index.ts # MCP server entry point
-├── tools/ # MCP tool definitions (one file per tool group)
-├── browser/ # Playwright / CDP session manager
-└── diagnostics/ # Runtime signal collectors (console, network, fiber)
-examples/
-└── README.md # Self-contained demo apps (one per bug class)
-```
-
-## Testing
-
-See [docs/test-scenario-sprint1.md](docs/test-scenario-sprint1.md) for the full end-to-end test scenario
-(start MCP server → open React app → invoke `get_runtime_status` → verify response).
-The integrated test app also contains dedicated Sprint 6 fixtures for:
-- hook state inspection (`get_component_state`)
-- component/context inspection (`inspect_component`)
-- compact inspection payload validation
+## Current limits
-Sprint 10 adds a dedicated render-loop fixture:
-- click `#render-loop-start-button` to trigger a short controlled render explosion
-- observe `#render-loop-status`, `#render-loop-step`, and `#render-loop-token`
-- expected diagnosis: a repeated effect driven by an unstable hook value inside `InfiniteLoopScenario`
+- Live attach still depends on Chrome remote debugging.
+- Shadow sandbox currently validates runtime-only script patches in replay mode, not full source rewrites.
+- Some agent environments share the same MCP transport but differ in config shape, prompt UX, and restart behavior.
+- Public npm publication and visibility changes remain gated by manual release checks.
diff --git a/SECURITY.md b/SECURITY.md
new file mode 100644
index 0000000..bcdc6c9
--- /dev/null
+++ b/SECURITY.md
@@ -0,0 +1,49 @@
+# Security Policy
+
+## Supported versions
+
+| Version | Supported |
+|---|---|
+| `0.1.x` (current) | ✅ Active |
+| Earlier pre-releases | ❌ Not supported |
+
+## Scope
+
+React-Sentinel runs as a local MCP stdio server. It opens a Playwright browser, attaches to a Chrome DevTools Protocol endpoint, and never contacts external servers on behalf of the user.
+
+**In scope for security reports:**
+
+- Code execution vulnerabilities triggered by crafted MCP tool input.
+- Unintended credential or secret exposure through CDP, logs, or stdout/stderr.
+- Supply-chain risks (malicious dependency updates).
+
+**Out of scope:**
+
+- Issues in the user's own React application or browser extensions.
+- The Playwright project itself (report those at [github.com/microsoft/playwright](https://github.com/microsoft/playwright)).
+- Social-engineering attacks against the user's AI agent.
+
+## Reporting a vulnerability
+
+**Do not open a public GitHub issue for security vulnerabilities.**
+
+Send a private report using one of these channels:
+
+1. **GitHub private vulnerability reporting** — go to the repository Security tab → "Report a vulnerability".
+2. **Email** — contact the maintainer directly at the email address on the npm package page.
+
+Include:
+
+- A description of the vulnerability and its potential impact.
+- Reproduction steps or a proof-of-concept (attach files rather than pasting secrets).
+- The version of `@edgarbrunet/react-sentinel` you tested against.
+
+## Response timeline
+
+| Stage | Target |
+|---|---|
+| Acknowledgement | Within 3 business days |
+| Initial assessment | Within 7 business days |
+| Fix + release | Depends on severity; critical issues get priority |
+
+Thank you for helping keep React-Sentinel safe.
diff --git a/assets/agent-pack/docs/compatibility.md b/assets/agent-pack/docs/compatibility.md
index 8cbf135..c977112 100644
--- a/assets/agent-pack/docs/compatibility.md
+++ b/assets/agent-pack/docs/compatibility.md
@@ -4,7 +4,8 @@
|---|---|---|---|
| Claude Code | Primary | Project-local `.mcp.json`, commands, skills, heuristics, profile docs | The default install target |
| Generic MCP client | Supported with manual wiring | Standard stdio launch command, arguments, and local docs | Client-specific config path may differ |
+| Cursor | Documented adaptation | Cursor profile notes plus the same MCP launch guidance | `.cursor/mcp.json` is a natural project-local target |
| Gemini CLI | Documented adaptation | Prompt/skill guidance and generic MCP notes | Manual setup expected |
-| Copilot | Documented adaptation | Prompt/skill guidance and generic MCP notes | Manual setup expected |
+| Copilot | Documented adaptation | Prompt/skill guidance and generic MCP notes | VS Code and CLI surfaces may differ |
The pack keeps the Markdown assets portable so the same guidance can move across clients even when their native command surfaces differ.
diff --git a/assets/agent-pack/docs/heuristics.md b/assets/agent-pack/docs/heuristics.md
index 07ad8ca..a3d610e 100644
--- a/assets/agent-pack/docs/heuristics.md
+++ b/assets/agent-pack/docs/heuristics.md
@@ -20,3 +20,13 @@
2. Can Replay reproduce the issue safely? If yes, start there.
3. Does the issue depend on real browser state? If yes, consider Attach.
4. Is the bug already reproduced and the goal is hypothesis testing? If yes, consider Shadow Sandbox.
+
+## Recommended tool order
+
+1. `get_server_info`
+2. `get_session_status`
+3. `get_console_events` and `get_network_events`
+4. `inspect_component` or `get_react_tree`
+5. `get_component_state`
+6. `validate_after_action` or `validate_scenario`
+7. `apply_patch_then_replay` only after the bug is proven in replay mode
diff --git a/assets/agent-pack/profiles/copilot-cli.md b/assets/agent-pack/profiles/copilot-cli.md
index e7e6849..c75975f 100644
--- a/assets/agent-pack/profiles/copilot-cli.md
+++ b/assets/agent-pack/profiles/copilot-cli.md
@@ -13,3 +13,5 @@ Use the **generic MCP profile** as the baseline and keep the pack guidance close
- mapping the pack routines to Copilot chat prompts or custom instructions;
- deciding where to surface the skill text in the client workflow;
- any client-specific packaging beyond standard MCP wiring.
+
+For VS Code workspaces, a project-local `.vscode/mcp.json` file is a reasonable place to carry the same stdio launch command.
diff --git a/assets/agent-pack/profiles/cursor.md b/assets/agent-pack/profiles/cursor.md
new file mode 100644
index 0000000..8062470
--- /dev/null
+++ b/assets/agent-pack/profiles/cursor.md
@@ -0,0 +1,14 @@
+# Cursor adaptation
+
+Use the **generic MCP profile** as the transport baseline and point Cursor at the same stdio launch command.
+
+## What carries over
+
+- the same `mcp` launch command for React-Sentinel;
+- the same runtime-debugging heuristics and command routines;
+- project-local setup through `.cursor/mcp.json` when you want repository-scoped wiring.
+
+## What stays manual
+
+- Cursor-specific prompt recipes or chat habits;
+- any non-MCP project instructions you want Cursor to load automatically.
diff --git a/assets/agent-pack/profiles/generic-mcp.md b/assets/agent-pack/profiles/generic-mcp.md
index ddd5d8b..cb8b9e7 100644
--- a/assets/agent-pack/profiles/generic-mcp.md
+++ b/assets/agent-pack/profiles/generic-mcp.md
@@ -10,6 +10,8 @@ This profile describes the portable stdio setup for any MCP client that can laun
- args: launch React-Sentinel with `mcp` and a replay visibility flag
- env: normal local shell environment, plus any client-specific overrides
+When the client fetches React-Sentinel from npm, the public package name is `@edgarbrunet/react-sentinel`.
+
## Limits
- config file path and JSON shape may differ by client;
diff --git a/assets/agent-pack/skills/react-sentinel-debug.md b/assets/agent-pack/skills/react-sentinel-debug.md
index 7f2fe00..278c866 100644
--- a/assets/agent-pack/skills/react-sentinel-debug.md
+++ b/assets/agent-pack/skills/react-sentinel-debug.md
@@ -19,12 +19,16 @@ Use React-Sentinel only when the task benefits from **observing a live browser r
## Preferred order
-1. `debug-react`
-2. `reproduce-bug`
-3. `validate-fix`
+1. `get_server_info`
+2. `get_session_status`
+3. `debug-react`
+4. `reproduce-bug`
+5. `validate-fix`
## Mode rules
- Prefer **Replay** by default.
- Escalate to **Attach** only for real user state.
- Use **Shadow Sandbox** only after replay already proves the bug.
+- Turn the bug into `validate_after_action` or `validate_scenario` assertions before proposing a source change.
+- Prefer `apply_patch_then_replay` over speculative source edits when testing one runtime hypothesis.
diff --git a/assets/social-preview.svg b/assets/social-preview.svg
new file mode 100644
index 0000000..99893e3
--- /dev/null
+++ b/assets/social-preview.svg
@@ -0,0 +1,64 @@
+
diff --git a/docs/adoption-checklist.md b/docs/adoption-checklist.md
new file mode 100644
index 0000000..4d4b481
--- /dev/null
+++ b/docs/adoption-checklist.md
@@ -0,0 +1,41 @@
+# Universal adoption checklist
+
+This checklist captures the onboarding scenarios React-Sentinel must support for Sprint 15.
+
+## Validation scenarios
+
+| Environment | Validation target | Status |
+|---|---|---|
+| Claude Code | `init-mcp --client claude-code --mode local --write` produces a valid `.mcp.json` entry | Validated |
+| Generic MCP | `init-mcp --client generic-mcp --mode npx` prints a portable snippet that references `@edgarbrunet/react-sentinel` | Validated |
+| Cursor | `init-mcp --client cursor --mode local --write` produces `.cursor/mcp.json` | Validated |
+| GitHub Copilot / VS Code | `init-mcp --client github-copilot --mode local --write` produces `.vscode/mcp.json` with a `servers` root and `type: \"stdio\"` | Validated |
+| Gemini CLI | `init-mcp --client gemini-cli --mode local --write` produces `.gemini/settings.json` | Validated |
+| Copilot CLI | Manual adaptation of the generic MCP snippet is documented | Documentation path |
+
+## Operator checklist
+
+1. Confirm Node.js 20+ is installed.
+2. Confirm the React-Sentinel CLI can build or run from `npx`.
+3. Choose the target environment explicitly or use `--client auto`.
+4. Generate or write the MCP config.
+5. Restart the client or IDE.
+6. Verify the server launches with `mcp` over stdio.
+7. Run a first runtime task such as `get_server_info` or `browser_ping`.
+8. If the setup fails, convert the limitation into a doc note or a CLI help improvement.
+
+## Common failure-to-doc loop
+
+| Failure | Expected response |
+|---|---|
+| Wrong config file shape | Document the correct root key (`mcpServers` vs `servers`) |
+| No standard config path | Use `generic-mcp` and document the client-specific manual path |
+| Missing replay browser | Point the user to `npx playwright install chromium` |
+| Attach mode unavailable | Keep working in replay mode and document the Chrome CDP requirement |
+
+## Exit criteria
+
+- At least one Claude setup is validated end to end.
+- At least one generic MCP snippet is validated.
+- Cursor, Copilot, and Gemini flows are either validated locally or documented honestly as manual adaptations.
+- Every failed onboarding step produces either a doc clarification or a CLI message.
diff --git a/docs/agent-pack.md b/docs/agent-pack.md
index 74ccf24..3b4991d 100644
--- a/docs/agent-pack.md
+++ b/docs/agent-pack.md
@@ -4,7 +4,7 @@
The agent pack turns React-Sentinel into a project-local integration bundle that an AI coding agent can install, inspect, update, and remove without hand-editing every file.
-Claude Code is the primary target. The same pack also exposes a generic MCP profile plus documented adaptations for Gemini CLI and Copilot.
+Claude Code is the primary target. The same pack also exposes a generic MCP profile plus documented adaptations for Cursor, Gemini CLI, and Copilot.
## Target format
@@ -33,7 +33,7 @@ The repository keeps the reusable pack assets under `assets/agent-pack/`. Instal
| `.react-sentinel/agent-pack/commands/validate-fix.md` | Validation routine for assertions or Shadow Sandbox |
| `.react-sentinel/agent-pack/skills/react-sentinel-debug.md` | Reusable agent instructions for when and how to call React-Sentinel |
| `.react-sentinel/agent-pack/docs/heuristics.md` | Trigger signals, non-trigger rules, and mode decision guidance |
-| `.react-sentinel/agent-pack/docs/compatibility.md` | Compatibility matrix across Claude Code, generic MCP, Gemini CLI, and Copilot |
+| `.react-sentinel/agent-pack/docs/compatibility.md` | Compatibility matrix across Claude Code, generic MCP, Cursor, Gemini CLI, and Copilot |
| `.react-sentinel/agent-pack/profiles/*.md` | Per-agent profile notes and integration constraints |
| `.mcp.json` | Claude Code MCP config entry written or updated by install/update flows |
@@ -50,7 +50,7 @@ The MCP entry remains standard stdio JSON and reuses the existing React-Sentinel
- `local` -> `node /absolute/path/to/dist/index.js mcp --headless`
- `global` -> `react-sentinel mcp --headless`
-- `npx` -> `npx -y react-sentinel mcp --headless`
+- `npx` -> `npx -y @edgarbrunet/react-sentinel mcp --headless`
## Compatibility boundaries
@@ -61,6 +61,7 @@ The MCP entry remains standard stdio JSON and reuses the existing React-Sentinel
### Documented, not auto-wired
+- **Cursor** adaptation notes
- **Gemini CLI** adaptation notes
- **Copilot** adaptation notes
diff --git a/docs/agent-runtime-ux.md b/docs/agent-runtime-ux.md
new file mode 100644
index 0000000..7d2d2bb
--- /dev/null
+++ b/docs/agent-runtime-ux.md
@@ -0,0 +1,75 @@
+# Agent runtime UX
+
+React-Sentinel is most useful when the agent knows **when** to call it and **which mode** to choose.
+
+## Trigger signals
+
+Call React-Sentinel when the task involves one or more of these signals:
+
+- the bug is visible in the browser but the root cause is unclear from source code;
+- a React state, prop, context, or render loop likely explains the failure;
+- a console error or network request may be the fastest path to diagnosis;
+- the agent needs proof that a fix works in the browser before editing files;
+- the issue depends on real browser state, cookies, or an authenticated session.
+
+## Non-triggers
+
+Do **not** start with React-Sentinel when:
+
+- the task is pure refactoring with no runtime symptom;
+- the bug is already explained by a compile or type error;
+- the target project cannot run in a browser session yet;
+- a static source edit is obviously enough and no runtime confirmation is needed.
+
+## Default mode choice
+
+| Mode | Default? | Use when | Avoid when |
+|---|---|---|---|
+| Replay | Yes | You want deterministic reproduction in an isolated browser | The bug depends on real user state that replay cannot rebuild cheaply |
+| Attach | No, escalate | You need the real tab, cookies, extensions, or authenticated session | A clean replay scenario is enough |
+| Shadow sandbox | After replay | You want to test a runtime-only fix hypothesis before editing source files | The issue is not already reproducible in replay mode |
+
+## Recommended sequence
+
+1. **Check capability and session status** with `get_server_info` and `get_session_status`.
+2. **Choose replay first** unless the bug clearly depends on a live authenticated tab.
+3. **Inspect fast signals early** with console and network events.
+4. **Locate the React owner** with `inspect_component` or `get_react_tree`.
+5. **Read runtime state** with `get_component_state`.
+6. **Turn the failure into an assertion** with `validate_after_action` or `validate_scenario`.
+7. **Only then test a patch hypothesis** with `apply_patch_then_replay` or `apply_runtime_patch`.
+
+## Capability-to-problem map
+
+| Problem | First tools |
+|---|---|
+| Button disabled unexpectedly | `navigate_replay`, `inspect_component`, `get_component_state` |
+| Error toast after API call | `navigate_replay`, `get_network_events`, `get_console_events`, `validate_after_action` |
+| Infinite re-render / unstable hook | `get_render_hotspots`, `get_hook_changes`, `inspect_component` |
+| Bug only in real logged-in session | `get_attach_status`, `get_attach_tabs`, `select_attach_tab`, runtime tools |
+| Unsure whether a fix idea works | `apply_patch_then_replay`, `validate_scenario` |
+
+## Example prompts
+
+### Claude / Cursor / Copilot
+
+> Reproduce the checkout failure in replay mode, inspect the React tree around the disabled submit button, and tell me which prop or state value is blocking the action.
+
+### Claude Desktop
+
+> Attach to my current Chrome tab, confirm React is detected, inspect the component that owns the visible error banner, and capture the console or network signal that explains it.
+
+### Gemini CLI
+
+> Use replay mode first. Reproduce the bug, validate the failure with an assertion, then try a sandbox patch and report whether the assertion passes.
+
+### Generic MCP client
+
+> Before changing code, get the session status, reproduce the bug, inspect the runtime state that owns it, and convert the bug into a validation scenario.
+
+## Practical heuristics
+
+- Prefer **Replay** over **Attach**.
+- Prefer **Assertions** over informal visual guesses.
+- Prefer **Sandbox validation** over speculative source edits.
+- If React is not detected, switch from component-level assumptions to DOM, console, and network diagnostics.
diff --git a/docs/github-visibility-plan.md b/docs/github-visibility-plan.md
new file mode 100644
index 0000000..25b6905
--- /dev/null
+++ b/docs/github-visibility-plan.md
@@ -0,0 +1,168 @@
+# GitHub visibility plan
+
+This document provides a ready-to-apply configuration for the public GitHub repository. All actions listed here require Edgar's manual approval or execution; none are applied automatically.
+
+---
+
+## Suggested repository description
+
+```
+Runtime debugger for AI agents — MCP server that gives AI tools live React inspection, bug replay, and fix validation.
+```
+
+(140 characters, within GitHub's limit.)
+
+## Suggested website URL
+
+```
+https://www.npmjs.com/package/@edgarbrunet/react-sentinel
+```
+
+Update this once the npm package is published.
+
+## Suggested topics (GitHub repository topics)
+
+Apply these under **Settings → General → Topics**:
+
+```
+mcp react debugging ai playwright devtools typescript model-context-protocol agent browser-automation
+```
+
+Keep the list to 10 or fewer for maximum discoverability. Topics must be lowercase, hyphen-separated.
+
+## Suggested repository labels
+
+Create or rename labels under **Issues → Labels**:
+
+| Name | Color | Description |
+|---|---|---|
+| `bug` | `#d73a4a` | Something is not working as expected |
+| `enhancement` | `#a2eeef` | New feature or improvement request |
+| `good first issue` | `#7057ff` | Easy entry point for new contributors |
+| `help wanted` | `#008672` | Maintainer is actively seeking contributions |
+| `needs-triage` | `#e4e669` | Not yet reviewed by a maintainer |
+| `documentation` | `#0075ca` | Improvements or additions to documentation |
+| `dependencies` | `#0366d6` | Pull requests that update a dependency |
+| `ci` | `#bfd4f2` | Related to the CI/CD pipeline |
+| `agent-pack` | `#f9d0c4` | Agent Pack CLI feature area |
+| `mcp-protocol` | `#c5def5` | MCP server core or tool definitions |
+
+## Social preview image
+
+A placeholder SVG is available at [`assets/social-preview.svg`](../assets/social-preview.svg). To apply it as the repository's Open Graph image:
+
+1. Convert the SVG to a PNG at 1280 × 640 px (GitHub requires PNG or JPEG for social previews).
+2. Go to **Settings → General → Social preview → Edit**.
+3. Upload the converted PNG.
+
+Suggested conversion command (requires Inkscape or a similar tool):
+
+```bash
+inkscape assets/social-preview.svg --export-type=png --export-filename=assets/social-preview.png -w 1280 -h 640
+```
+
+Or using ImageMagick with rsvg-convert:
+
+```bash
+rsvg-convert -w 1280 -h 640 assets/social-preview.svg > assets/social-preview.png
+```
+
+## Draft public issues to create
+
+Create these issues on GitHub after making the repository public. They improve discoverability and signal active maintenance.
+
+---
+
+### Issue 1 — Good first issue (new contributor entry point)
+
+**Title:** `Add --json flag to init-mcp for machine-readable output`
+
+**Labels:** `good first issue`, `enhancement`
+
+**Body:**
+
+```
+### Task
+
+The `init-mcp` command currently prints human-readable text. Add an optional `--json` flag that
+outputs the generated MCP config snippet as a JSON object to stdout, making it easier for
+shell scripts or editor plugins to consume.
+
+### Acceptance criteria
+
+- `node dist/index.js init-mcp --client claude-code --mode npx --json` prints valid JSON.
+- The JSON shape is `{ "client": "", "mode": "", "config": { … } }`.
+- All existing text output is unchanged when `--json` is not passed.
+- `npm run check && npm run build` pass.
+
+### Starting points
+
+- Entry point: `src/index.ts` — look for the `init-mcp` command handler.
+- Config generation: `src/mcp-config.ts`.
+- Run `npm run build && node dist/index.js init-mcp --client claude-code --mode npx` to see current output.
+
+### Effort estimate
+
+1–3 hours.
+```
+
+---
+
+### Issue 2 — Feature request (ecosystem expansion)
+
+**Title:** `Support JetBrains AI Assistant as an MCP client target`
+
+**Labels:** `enhancement`, `help wanted`
+
+**Body:**
+
+```
+### Motivation
+
+JetBrains AI Assistant (IntelliJ, WebStorm, etc.) supports MCP servers via its plugin ecosystem.
+React-Sentinel already supports Claude Code, Cursor, GitHub Copilot, and Gemini CLI. Adding a
+`--client jetbrains` target would make the `init-mcp` command usable for JetBrains users.
+
+### Proposed solution
+
+1. Research the JetBrains AI Assistant MCP config file path and format.
+2. Add a `jetbrains` client entry to `src/mcp-config.ts`.
+3. Update `docs/integration-guides.md` with a JetBrains section.
+4. Add the new client to the supported environments table in `README.md`.
+```
+
+---
+
+### Issue 3 — Documentation improvement
+
+**Title:** `Document how to use React-Sentinel with pnpm and Yarn workspaces`
+
+**Labels:** `documentation`
+
+**Body:**
+
+```
+### Motivation
+
+The current `init-mcp` and agent-pack install flows assume npm. Teams using pnpm workspaces
+or Yarn Berry (PnP) may encounter path or lockfile differences.
+
+### Task
+
+Add a section to `docs/universal-install.md` covering:
+- How to run `npx @edgarbrunet/react-sentinel init-mcp` under pnpm (`pnpm dlx`) and Yarn (`yarn dlx`).
+- Whether `install-agent-pack` works in a monorepo root vs. a sub-package.
+- Known limitations (if any) when Yarn PnP is enabled.
+```
+
+## Pinned discussions / README badges
+
+Consider adding these badges to `README.md` once the package is published:
+
+```markdown
+[](https://www.npmjs.com/package/@edgarbrunet/react-sentinel)
+[](https://github.com/edgarbnt/ReactSentinel/actions/workflows/ci.yml)
+[](LICENSE)
+```
+
+Apply these after the npm package is live and CI has run at least once.
diff --git a/docs/integration-guides.md b/docs/integration-guides.md
new file mode 100644
index 0000000..e4ffaca
--- /dev/null
+++ b/docs/integration-guides.md
@@ -0,0 +1,155 @@
+# Integration guides
+
+This guide explains how to wire React-Sentinel into each supported agent or IDE environment.
+
+## Support matrix
+
+| Environment | Setup status | Best install mode | Notes |
+|---|---|---|---|
+| Claude Code | Supported | `npx` or `local` | Project-local `.mcp.json` |
+| Claude Desktop | Supported | `npx` or `local` | User-level Claude config |
+| Cursor | Supported | `npx` or `local` | Project-local `.cursor/mcp.json` |
+| GitHub Copilot / VS Code | Supported | `npx` | Workspace `.vscode/mcp.json` using the VS Code `servers` shape |
+| Gemini CLI | Supported | `npx` | Project-local `.gemini/settings.json` |
+| Copilot CLI | Partial / manual | `npx` | Reuse the same stdio launch command; CLI-specific setup may be interactive |
+| Generic MCP client | Supported manually | `npx` or `local` | Paste the snippet into the client-specific config |
+| Other IDE integrations | Planned / manual | `npx` or `local` | Start from the generic MCP snippet and adapt the IDE-specific path |
+
+## Claude Code
+
+### Recommended
+
+```bash
+npx -y @edgarbrunet/react-sentinel init-mcp --client claude-code --mode npx --write
+```
+
+### Result
+
+- writes or updates `.mcp.json` in the current project;
+- adds a `react-sentinel` entry under `mcpServers`;
+- keeps the MCP setup local to the repository.
+
+### Limitations
+
+- React-Sentinel only manages the MCP server entry, not Claude-specific prompts or memories.
+
+## Claude Desktop
+
+### Recommended
+
+```bash
+npx -y @edgarbrunet/react-sentinel init-mcp --client claude-desktop --mode npx --write
+```
+
+### Result
+
+- writes the standard `mcpServers` entry into the OS-specific Claude Desktop config file;
+- keeps the launch command on stdio transport;
+- requires a Claude Desktop restart afterward.
+
+### Limitations
+
+- local repository routines still live in this repository or the optional agent pack; Desktop does not automatically import them.
+
+## Cursor
+
+### Recommended
+
+```bash
+npx -y @edgarbrunet/react-sentinel init-mcp --client cursor --mode npx --write
+```
+
+### Result
+
+- writes `.cursor/mcp.json` in the current project;
+- uses the standard `mcpServers` JSON shape;
+- keeps setup shareable inside the repository if you choose to commit the file.
+
+### Limitations
+
+- React-Sentinel wires the MCP transport only. Cursor-specific prompt recipes remain documentation-first.
+
+## GitHub Copilot / VS Code
+
+### Recommended
+
+```bash
+npx -y @edgarbrunet/react-sentinel init-mcp --client github-copilot --mode npx --write
+```
+
+### Result
+
+- writes `.vscode/mcp.json`;
+- uses the VS Code-style `servers` root;
+- sets the server type to `stdio`;
+- reload the workspace so Copilot can detect the MCP server.
+
+### Limitations
+
+- This targets the VS Code workspace integration surface. Other GitHub Copilot entry points can reuse the same stdio command but may need manual setup.
+
+## Copilot CLI
+
+### Recommended
+
+Use the same stdio launch command as the VS Code integration:
+
+```bash
+npx -y @edgarbrunet/react-sentinel init-mcp --client generic-mcp --mode npx
+```
+
+Then either:
+
+1. paste the snippet into your Copilot CLI MCP config flow, or
+2. register the same command through the CLI's interactive MCP setup if you prefer UI-assisted wiring.
+
+### Limitations
+
+- The repository does not assume one universal Copilot CLI config file path.
+- Prompt and routine surfacing remains manual.
+
+## Gemini CLI
+
+### Recommended
+
+```bash
+npx -y @edgarbrunet/react-sentinel init-mcp --client gemini-cli --mode npx --write
+```
+
+### Result
+
+- writes `.gemini/settings.json` in the current project;
+- adds a `mcpServers.react-sentinel` entry;
+- keeps the same stdio command contract used by other MCP clients.
+
+### Limitations
+
+- Gemini-specific command aliases and memory wiring are not managed by React-Sentinel.
+
+## Generic MCP clients
+
+### Recommended
+
+```bash
+npx -y @edgarbrunet/react-sentinel init-mcp --client generic-mcp --mode npx
+```
+
+### Result
+
+- prints a portable `mcpServers` JSON snippet;
+- lets you paste the same launch command into any MCP-compatible client.
+
+### Limitations
+
+- React-Sentinel does not guess the config path or JSON root for arbitrary clients.
+- Some clients may expect additional fields such as `type`, `env`, or client-specific wrappers.
+
+## IDE integration note
+
+For IDEs that do not expose a first-class React-Sentinel target yet:
+
+1. start from the **generic MCP** snippet;
+2. keep the launch command on stdio transport;
+3. document the IDE-specific config path next to the project if you plan to share it with a team.
+
+If an IDE has no stable MCP config path yet, treat it as a manual integration instead of pretending React-Sentinel can auto-wire it safely.
diff --git a/docs/local-diagnostics-checklist.md b/docs/local-diagnostics-checklist.md
index 4f8a99b..8013d92 100644
--- a/docs/local-diagnostics-checklist.md
+++ b/docs/local-diagnostics-checklist.md
@@ -74,3 +74,5 @@ Fix:
1. Start the target app locally
2. Verify the URL in your MCP request
3. Retry with a reachable URL such as the demo app (`http://127.0.0.1:5173`)
+
+See [local-ports.md](local-ports.md) for the full list of legitimate local URLs and ports used in the repository.
diff --git a/docs/local-ports.md b/docs/local-ports.md
new file mode 100644
index 0000000..f3b15b2
--- /dev/null
+++ b/docs/local-ports.md
@@ -0,0 +1,27 @@
+# Local ports and endpoints
+
+These local URLs and ports are legitimate examples used by React-Sentinel for development, smoke tests, and attach-mode guidance.
+
+## Default values
+
+| Port / URL | Purpose | Source |
+|---|---|---|
+| `http://127.0.0.1:5173` | Main demo app dev server | `examples/test-app/package.json`, `scripts/mcp-e2e-utils.ts` |
+| `http://localhost:5173` | Equivalent localhost form used in examples and tool descriptions | `examples/README.md`, `src/tools/diagnostics.ts`, `src/tools/browser.ts` |
+| `http://127.0.0.1:5173/hydration-nextjs.html` | Hydration mismatch fixture | `docs/scenarios/hydration-mismatch.md`, `scripts/e2e-smoke.ts` |
+| `http://localhost:5176/` | Validation-loop scenario example | `docs/scenarios/validation-loop.md` |
+| `http://127.0.0.1:3000` | Common fallback suggestion for Next.js-style local apps | `src/index.ts`, `src/project-detection.ts` |
+| `http://127.0.0.1:9222` | Default Chrome CDP endpoint for attach mode | `src/browser/index.ts` |
+| `http://127.0.0.1:9333` | Alternate CDP endpoint example | `docs/release-mvp.md` |
+
+## Why these values stay in the repo
+
+- They are **local development examples**, not production endpoints.
+- They make the attach and replay workflows reproducible for new users.
+- They are used by docs, project detection hints, and the smoke tooling.
+
+## Guidance
+
+- Prefer `127.0.0.1` in deterministic examples when you want to avoid localhost-resolution ambiguity.
+- Keep `localhost` examples where they are more familiar to end users.
+- If you change one of these defaults, update this file, the relevant docs, and the example app scripts together.
diff --git a/BLUEPRINT.md b/docs/project-history/BLUEPRINT.fr.md
similarity index 100%
rename from BLUEPRINT.md
rename to docs/project-history/BLUEPRINT.fr.md
diff --git a/docs/project-history/README.md b/docs/project-history/README.md
new file mode 100644
index 0000000..3eca2b6
--- /dev/null
+++ b/docs/project-history/README.md
@@ -0,0 +1,6 @@
+# Project history
+
+This directory keeps historical or internal project notes that are useful to preserve, but are not part of the main public onboarding flow.
+
+- French-language internal notes can live here without polluting the public product surface.
+- Sprint reports remain in `docs/sprints/` because the active sprint workflow still writes there.
diff --git a/.agents/rules/coding-style.md b/docs/project-history/agent-rules/coding-style.fr.md
similarity index 99%
rename from .agents/rules/coding-style.md
rename to docs/project-history/agent-rules/coding-style.fr.md
index 360eb92..8483dbe 100644
--- a/.agents/rules/coding-style.md
+++ b/docs/project-history/agent-rules/coding-style.fr.md
@@ -27,4 +27,4 @@ Commentaires en anglais, messages de commit en anglais
6. Sécurité / Sandbox
Le hot-patching n'écrit jamais sur les fichiers locaux de l'user
-Les sessions browser sont toujours headless sauf en mode debug explicite
\ No newline at end of file
+Les sessions browser sont toujours headless sauf en mode debug explicite
diff --git a/docs/public-readiness-audit.md b/docs/public-readiness-audit.md
new file mode 100644
index 0000000..646c32b
--- /dev/null
+++ b/docs/public-readiness-audit.md
@@ -0,0 +1,57 @@
+# Public readiness audit
+
+This audit was produced before the Sprint 15 cleanup changes for public-repo readiness.
+
+## Safe to keep
+
+| Path | Why it stays |
+|---|---|
+| `README.md` | Public product entrypoint in English |
+| `docs/universal-install.md`, `docs/integration-guides.md`, `docs/agent-runtime-ux.md`, `docs/adoption-checklist.md`, `docs/workflows.md` | Public onboarding and workflow docs |
+| `examples/test-app/` and `examples/README.md` | Legitimate demo and validation fixture |
+| `src/`, `scripts/`, `assets/agent-pack/`, `dist/` | Product source, automation, packaged CLI output, and reusable assets |
+| `docs/sprints/` | Historical delivery reports; useful as project history |
+| `.github/copilot-instructions.md` | Automation config, not user-facing product documentation |
+
+## Should document
+
+| Topic | Evidence | Follow-up |
+|---|---|---|
+| Local URLs and ports | `examples/test-app/package.json`, `scripts/mcp-e2e-utils.ts`, `src/browser/index.ts`, `src/project-detection.ts`, `docs/workflows.md`, `docs/local-diagnostics-checklist.md` | Centralize them in `docs/local-ports.md` |
+| Alternate CDP endpoint example | `docs/release-mvp.md`, `src/index.ts` | Keep it as a documented example rather than treating it as suspicious |
+| Historical docs | `docs/sprints/*.md` | Keep them clearly framed as history, not onboarding |
+
+## Should move
+
+| Path | Reason |
+|---|---|
+| `BLUEPRINT.md` | French internal vision note at the repository root; better kept under project history |
+| `.agents/rules/coding-style.md` | Internal agent rule file in French; not part of the public product surface |
+
+## Should remove
+
+No obviously useless temporary files, generated crash dumps, or real secrets were found during this audit.
+
+## Needs human decision
+
+| Topic | Why human validation is required |
+|---|---|
+| GitHub visibility, topics, social preview, and description | Public reputation and settings ownership belong to Edgar |
+| npm publication under `@edgarbrunet/react-sentinel` | Public release is irreversible and must stay manual |
+| Marketplace submissions and launch messaging | Public distribution choices must remain human-approved |
+
+## Secret scan summary
+
+`git grep` produced only false positives:
+
+- `js-tokens` entries in example lockfiles;
+- a demo DOM label named `render-loop-token`;
+- the `username` and `password` parameter names in the browser instrumentation wrapper.
+
+No likely real credentials, access tokens, or private keys were found.
+
+## MCP stdio audit
+
+- `node dist/index.js mcp --headless` produced **0 stdout bytes** during startup.
+- startup and shutdown logs are emitted on **stderr** via `console.error`.
+- normal `console.log` usage in `src/index.ts` is restricted to non-MCP commands such as `doctor`, `init-mcp`, and install helpers.
diff --git a/docs/release-gate.md b/docs/release-gate.md
new file mode 100644
index 0000000..db0b451
--- /dev/null
+++ b/docs/release-gate.md
@@ -0,0 +1,147 @@
+# Manual release gate
+
+This document lists every action that **must stay manual** before and during the public launch of React-Sentinel. No agent, CI job, or automation script should perform these steps without Edgar's explicit approval.
+
+---
+
+## Why this gate exists
+
+React-Sentinel operates in two modes:
+
+1. **Repository mode** — source code visible on GitHub (low risk, mostly reversible).
+2. **Package mode** — a published npm package under `@edgarbrunet/react-sentinel` (irreversible once indexed by registries and consumed by users).
+
+The release gate separates the two and ensures Edgar retains ownership of every public reputation decision.
+
+---
+
+## Checklist: before making the repository public
+
+- [ ] **Read and accept the public-readiness audit** at `docs/public-readiness-audit.md`.
+- [ ] **Apply repository settings manually:**
+ - Repository description (see `docs/github-visibility-plan.md`).
+ - Website URL (npm page once published).
+ - Topics (see `docs/github-visibility-plan.md`).
+ - Social preview image (see `docs/github-visibility-plan.md` for the PNG export step).
+- [ ] **Review default branch protection rules** — ensure `main` requires a passing CI check before merges.
+- [ ] **Verify that no secrets are committed** — run the secret scan below.
+- [ ] **Confirm `LICENSE` file exists** at the repo root (MIT).
+
+### Secret scan command (run locally before going public)
+
+```bash
+git grep -n -E "(password|token|secret|api.?key|private.?key)\s*[:=]\s*['\"]" \
+ -- ':!node_modules' ':!dist' ':!pnpm-lock.yaml' ':!package-lock.json'
+```
+
+Expected result: zero matches (the audit in `docs/public-readiness-audit.md` confirmed this as of Sprint 15).
+
+---
+
+## Checklist: npm publish
+
+npm publication is **irreversible**. Once a version is indexed, it is permanently available in the npm registry.
+
+- [ ] **Ensure the package name is exactly `@edgarbrunet/react-sentinel`** — confirm in `package.json`.
+- [ ] **Bump `version`** in `package.json` to the intended release version (e.g., `0.1.0`).
+- [ ] **Run the full validation suite locally:**
+
+```bash
+# From repo root
+npm run check
+npm run build
+cd examples/test-app && npm run build
+```
+
+- [ ] **Dry-run publish** to inspect what will be uploaded:
+
+```bash
+npm pack --dry-run
+```
+
+This prints the list of files that would be included. Verify no internal docs, French notes, or agent rules are accidentally bundled. Check `package.json` for a `files` allowlist if needed.
+
+- [ ] **Authenticate with the npm registry:**
+
+```bash
+npm login --scope=@edgarbrunet
+```
+
+- [ ] **Publish:**
+
+```bash
+npm publish --access public
+```
+
+- [ ] **Verify the published package on npm:**
+
+```bash
+npm info @edgarbrunet/react-sentinel
+npx -y @edgarbrunet/react-sentinel --version
+```
+
+### Current `package.json` `files` allowlist
+
+React-Sentinel now uses this `files` field in `package.json` to limit what is uploaded:
+
+```json
+"files": [
+ "dist/",
+ "assets/agent-pack/",
+ "README.md",
+ "LICENSE"
+]
+```
+
+Verify that the allowlist still matches the release intent before every publish. It keeps internal docs, sprint reports, and project-history files out of the npm package.
+
+---
+
+## Checklist: post-publish GitHub actions
+
+These steps are manual because they affect public reputation and discoverability:
+
+- [ ] **Create a GitHub Release** tagged `v0.1.0` with a human-written changelog.
+- [ ] **Create the public issues** listed in `docs/github-visibility-plan.md` (bug report, feature request, good first issue).
+- [ ] **Apply repository labels** as specified in `docs/github-visibility-plan.md`.
+- [ ] **Pin or close the planning issues** once they are no longer relevant.
+- [ ] **Update the README badges** (npm version, CI status) once CI has run successfully on `main`.
+
+---
+
+## What automation is allowed to do
+
+| Action | Allowed | Notes |
+|---|---|---|
+| Type-check (`npm run check`) | ✅ CI | Runs on every PR and push |
+| Build (`npm run build`) | ✅ CI | Runs on every PR and push |
+| Test-app build | ✅ CI | Runs on every PR and push |
+| Open pull requests | ✅ Agents | Always reviewed by Edgar before merge |
+| Commit and push to sprint branches | ✅ Agents | Never to `main` directly |
+| `npm publish` | ❌ Never automated | Must be manual |
+| Change repository visibility | ❌ Never automated | Must be manual in GitHub Settings |
+| Apply repository topics/description | ❌ Never automated | Must be manual in GitHub Settings |
+| Create public GitHub issues | ❌ Never automated | Must be manual (drafts provided in `docs/github-visibility-plan.md`) |
+| Upload social preview image | ❌ Never automated | Must be manual in GitHub Settings |
+
+---
+
+## Rollback plan
+
+If a bad version is published to npm:
+
+1. **Deprecate immediately** (does not remove, but warns users):
+
+```bash
+npm deprecate @edgarbrunet/react-sentinel@0.1.0 "This version has a known issue. Please use 0.1.1."
+```
+
+2. **Un-publish is only possible within 72 hours** and only if no other package depends on it:
+
+```bash
+npm unpublish @edgarbrunet/react-sentinel@0.1.0
+```
+
+3. **Publish a patch version** with the fix and update documentation.
+
+npm does not support true deletion after 72 hours; plan accordingly.
diff --git a/docs/sprints/SPRINT1.md b/docs/sprints/SPRINT1.md
index 37fb3d3..8ace4a0 100644
--- a/docs/sprints/SPRINT1.md
+++ b/docs/sprints/SPRINT1.md
@@ -52,7 +52,7 @@
|---|---|
| `README.md` | Prérequis, lancement local étape par étape, config Claude Desktop |
| `docs/sprints/SPRINT1.md` | Ce fichier — inventaire du sprint |
-| `BLUEPRINT.md` | Vision technique long terme du projet |
+| `docs/project-history/BLUEPRINT.fr.md` | Vision technique long terme du projet |
---
diff --git a/docs/sprints/SPRINT15.md b/docs/sprints/SPRINT15.md
new file mode 100644
index 0000000..cac0ba6
--- /dev/null
+++ b/docs/sprints/SPRINT15.md
@@ -0,0 +1,67 @@
+# Sprint 15 Report — Universal Agent Adoption & Public Repo Readiness
+
+## Objective
+
+The objective of Sprint 15 was to turn React-Sentinel into a clearer public product: define a safe public npm package contract, support more agent and IDE environments, rewrite the documentation around user onboarding, and prepare the repository for a public launch without automating sensitive release actions.
+
+## Key Accomplishments
+
+- **Universal install contract**:
+ - Switched the public npm package name to `@edgarbrunet/react-sentinel`.
+ - Kept the CLI binary name `react-sentinel`.
+ - Updated `init-mcp` to support `auto`, `claude-code`, `claude-desktop`, `cursor`, `github-copilot`, `gemini-cli`, and `generic-mcp`.
+ - Added client-aware write targets for `.mcp.json`, `.cursor/mcp.json`, `.vscode/mcp.json`, and `.gemini/settings.json`.
+- **Product documentation refresh**:
+ - Rewrote `README.md` into a product-facing entrypoint with a quick start and supported-environment table.
+ - Added dedicated docs for universal install, integration guides, runtime UX, adoption validation, local ports, public-readiness audit, GitHub visibility planning, and the manual release gate.
+ - Extended the agent-pack docs and assets with a Cursor profile and updated portability notes for Copilot and Gemini.
+- **Adoption validation**:
+ - Validated `init-mcp` flows for Claude Code, Cursor, GitHub Copilot / VS Code, Gemini CLI, generic MCP, and auto-detection in temporary workspaces.
+ - Converted the onboarding findings into a reusable adoption checklist and explicit limitations.
+- **Public repo cleanup**:
+ - Produced a public-readiness audit before cleanup.
+ - Moved the French root blueprint and internal agent rule file into `docs/project-history/`.
+ - Centralized legitimate local URLs, ports, and CDP endpoints in `docs/local-ports.md`.
+- **GitHub visibility assets**:
+ - Added `CONTRIBUTING.md`, `SECURITY.md`, `CODE_OF_CONDUCT.md`, issue templates, a PR template, a declarative labels file, and a baseline CI workflow.
+ - Added a social-preview placeholder SVG and linked it from the README.
+ - Drafted a GitHub visibility plan with repo description, topics, labels, badges, and seeded public issue ideas.
+- **Manual release safety**:
+ - Added `docs/release-gate.md` listing every action that must remain manual for Edgar.
+ - Added an npm `files` allowlist and an MIT `LICENSE` file to keep package publication scoped and predictable.
+
+## Technical Choices
+
+- **Scoped package, stable binary**: the public npm package uses the scope `@edgarbrunet/react-sentinel`, while the command-line binary stays `react-sentinel`. This avoids collisions with the unscoped npm package while keeping CLI ergonomics familiar.
+- **Client-specific MCP config roots**: `init-mcp` now differentiates between `mcpServers`-based configs and the VS Code/Copilot `servers` root. This keeps each target honest instead of pretending one JSON shape works everywhere.
+- **History preserved, not deleted**: internal French notes were moved into `docs/project-history/` instead of being destroyed. The repository stays cleaner at the root while preserving useful project context.
+- **Publish surface explicitly bounded**: the `files` allowlist limits what goes into the npm tarball to `dist/`, `assets/agent-pack/`, `README.md`, and `LICENSE`. This avoids leaking sprint reports, project-history notes, or internal automation files into the package.
+- **Manual-only release steps**: sensitive actions such as npm publish, GitHub visibility changes, topics, social preview, and public issue creation remain manual by design. The repository now prepares these steps instead of automating them unsafely.
+
+## Validation
+
+- **Type-check**: `npm run check`
+- **Build**: `npm run build`
+- **Demo app build**: `cd examples/test-app && npm run build`
+- **Smoke test**: `npm run e2e:smoke`
+ - confirmed MCP connection, replay navigation, runtime inspection, validation tools, patch workflow, and hydration diagnostics.
+- **Multi-client onboarding**:
+ - verified `init-mcp --write` for Claude Code, Cursor, GitHub Copilot / VS Code, and Gemini CLI in temporary workspaces;
+ - verified generic MCP snippet generation uses `@edgarbrunet/react-sentinel`;
+ - verified `doctor --config-path` accepts the Copilot / VS Code config shape.
+- **Package dry-run**: `npm pack --dry-run`
+ - confirmed that `LICENSE`, `README.md`, `assets/agent-pack/`, and `dist/` are included;
+ - confirmed that internal docs and historical files are excluded from the npm package.
+- **MCP stdio cleanliness**:
+ - confirmed `node dist/index.js mcp --headless` writes **0 bytes to stdout** during startup and uses stderr for human-readable startup logs.
+
+## Jira Tickets Completed
+
+- **[SCRUM-327]**: [S15-01][Universal Install] Créer l’installation universelle avec `@edgarbrunet/react-sentinel`
+- **[SCRUM-325]**: [S15-02][Integration Guides] Documenter chaque environnement agent et IDE
+- **[SCRUM-326]**: [S15-03][Agent Runtime UX] Définir comment les agents utilisent React-Sentinel au bon moment
+- **[SCRUM-323]**: [S15-04][README Product] Refaire le README en mode produit installable
+- **[SCRUM-324]**: [S15-05][Adoption Validation] Tester l’onboarding complet sur plusieurs environnements
+- **[SCRUM-348]**: [S15-06][Public Repo Readiness] Nettoyer le repo public avec règles d’automatisation Copilot CLI
+- **[SCRUM-355]**: [S15-07][GitHub Visibility] Optimiser visibilité avec `@edgarbrunet/react-sentinel`
+- **[SCRUM-422]**: [S15-08][Manual Release Gate] Actions sensibles à valider uniquement par Edgar
diff --git a/docs/universal-install.md b/docs/universal-install.md
new file mode 100644
index 0000000..1ae06f6
--- /dev/null
+++ b/docs/universal-install.md
@@ -0,0 +1,74 @@
+# Universal install
+
+React-Sentinel is published publicly as **`@edgarbrunet/react-sentinel`**.
+
+That package name is the only public npm reference that should be used in README text, onboarding docs, screenshots, and launch materials.
+
+## Public command contract
+
+| Use case | Recommended command |
+|---|---|
+| Print a config snippet for the detected environment | `npx -y @edgarbrunet/react-sentinel init-mcp --client auto --mode npx` |
+| Write a Claude Code config | `npx -y @edgarbrunet/react-sentinel init-mcp --client claude-code --mode npx --write` |
+| Write a Cursor config | `npx -y @edgarbrunet/react-sentinel init-mcp --client cursor --mode npx --write` |
+| Write a GitHub Copilot / VS Code config | `npx -y @edgarbrunet/react-sentinel init-mcp --client github-copilot --mode npx --write` |
+| Start the MCP server directly | `npx -y @edgarbrunet/react-sentinel mcp --headless` |
+| Run the local doctor | `npx -y @edgarbrunet/react-sentinel doctor --json` |
+
+## Install modes
+
+| Mode | Meaning | Generated launch command |
+|---|---|---|
+| `local` | Use the compiled CLI from the current checkout | `node /absolute/path/to/dist/index.js mcp --headless` |
+| `global` | Use a globally installed `react-sentinel` binary | `react-sentinel mcp --headless` |
+| `npx` | Fetch the scoped package on demand | `npx -y @edgarbrunet/react-sentinel mcp --headless` |
+
+## Target selection
+
+`init-mcp` now supports these targets:
+
+| Target | Config root | Default write target | Status |
+|---|---|---|---|
+| `claude-code` | `mcpServers` | `.mcp.json` | Supported |
+| `claude-desktop` | `mcpServers` | OS-specific Claude Desktop config | Supported |
+| `cursor` | `mcpServers` | `.cursor/mcp.json` | Supported |
+| `github-copilot` | `servers` | `.vscode/mcp.json` | Supported |
+| `gemini-cli` | `mcpServers` | `.gemini/settings.json` | Supported |
+| `generic-mcp` | `mcpServers` | none | Manual paste |
+| `auto` | detects an existing config if present, otherwise falls back to Claude Code | varies | Supported |
+
+## What `--write` does
+
+When the chosen target has a safe default path:
+
+1. React-Sentinel reads the existing config file if it exists.
+2. It merges or writes a `react-sentinel` server entry.
+3. It prints the resolved target, launch command, and restart instruction.
+
+When the target does **not** have a safe default path:
+
+- `--write` is rejected;
+- use the printed snippet instead and paste it manually into your MCP client config.
+
+## Honest limitations
+
+- `github-copilot` currently targets the workspace-level VS Code MCP file shape. Other Copilot surfaces can reuse the same stdio launch command but may need manual adaptation.
+- `cursor`, `gemini-cli`, and `generic-mcp` share the same stdio launch contract, but their prompt workflows and slash-command surfaces are not standardized by React-Sentinel.
+- `global` mode assumes you chose to install the CLI binary yourself. Public onboarding should prefer `npx`.
+- Public npm naming and CLI binary naming are intentionally different: the package is scoped, the binary stays `react-sentinel`.
+
+## Local repository workflow
+
+Before public publication, use the same CLI from a checkout:
+
+```bash
+npm install
+npm run build
+node dist/index.js init-mcp --client auto --mode local
+```
+
+Then switch to `--write` once you know the target:
+
+```bash
+node dist/index.js init-mcp --client claude-code --mode local --write
+```
diff --git a/docs/workflows.md b/docs/workflows.md
index 1e4b827..3b4b4f8 100644
--- a/docs/workflows.md
+++ b/docs/workflows.md
@@ -158,11 +158,13 @@ Use the **Sandbox / hot patch** flow when you want to test a runtime-only fix hy
The zero-config path is now centered on `init-mcp`: generate or write a client snippet that points to the explicit `mcp` stdio command.
+See [universal-install.md](universal-install.md) for the public npm package contract, [integration-guides.md](integration-guides.md) for environment-specific wiring, and [local-ports.md](local-ports.md) for the repo's documented local URLs and CDP endpoints.
+
### Generate the client snippet
```bash
npm run build
-node dist/index.js init-mcp --client claude-desktop --mode local
+node dist/index.js init-mcp --client auto --mode local
```
### Write the config automatically
@@ -178,7 +180,7 @@ node dist/index.js init-mcp --client claude-code --mode npx --write
|---|---|---|
| **local** | You are inside this checkout or another local package install | `node /absolute/path/to/dist/index.js mcp --headless` |
| **global** | `react-sentinel` is installed globally | `react-sentinel mcp --headless` |
-| **npx** | You want the MCP client to fetch React-Sentinel on demand | `npx -y react-sentinel mcp --headless` |
+| **npx** | You want the MCP client to fetch React-Sentinel on demand | `npx -y @edgarbrunet/react-sentinel mcp --headless` |
### Verify an existing config
@@ -190,6 +192,7 @@ node dist/index.js doctor --config-path ~/.config/Claude/claude_desktop_config.j
- Claude Desktop keeps the classic `mcpServers` JSON shape and needs a restart after config changes.
- Claude Code can use the same snippet format in a project-local `.mcp.json` file; override the file path with `--config-path` if you want a different location.
+- Cursor uses `.cursor/mcp.json`, GitHub Copilot / VS Code uses `.vscode/mcp.json`, and Gemini CLI uses `.gemini/settings.json`.
- For source-based development you can still point a client at `src/index.ts` through `tsx`, but the default workflow now prefers the compiled CLI entrypoint and the dedicated `mcp` command.
### Scope note
diff --git a/examples/README.md b/examples/README.md
index 6ce2142..4fb64e6 100644
--- a/examples/README.md
+++ b/examples/README.md
@@ -29,3 +29,5 @@ pnpm dev
Then point React-Sentinel at `http://localhost:5173` and use the MCP tools to inspect and debug the running application.
For hydration-specific diagnostics, open `http://localhost:5173/hydration-nextjs.html`.
+
+See [../docs/local-ports.md](../docs/local-ports.md) for the full list of documented local ports and endpoints used by the repo.
diff --git a/package-lock.json b/package-lock.json
index 5ea9da9..5c85a42 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,11 +1,11 @@
{
- "name": "react-sentinel",
+ "name": "@edgarbrunet/react-sentinel",
"version": "0.1.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
- "name": "react-sentinel",
+ "name": "@edgarbrunet/react-sentinel",
"version": "0.1.0",
"license": "MIT",
"dependencies": {
diff --git a/package.json b/package.json
index 6fb160b..7afd2fe 100644
--- a/package.json
+++ b/package.json
@@ -1,12 +1,18 @@
{
- "name": "react-sentinel",
+ "name": "@edgarbrunet/react-sentinel",
"version": "0.1.0",
- "description": "Autonomous debugging infrastructure for AI agents — MCP server bridging AI terminals to browser runtime",
+ "description": "Runtime debugger for AI agents — MCP server for live React inspection, bug replay, and fix validation",
"type": "module",
"main": "dist/index.js",
"bin": {
"react-sentinel": "dist/index.js"
},
+ "files": [
+ "dist/",
+ "assets/agent-pack/",
+ "README.md",
+ "LICENSE"
+ ],
"scripts": {
"dev": "tsx watch src/index.ts",
"build": "tsc",
diff --git a/src/agent-pack.ts b/src/agent-pack.ts
index 3870ee0..730c6d1 100644
--- a/src/agent-pack.ts
+++ b/src/agent-pack.ts
@@ -5,7 +5,7 @@ import { fileURLToPath } from "node:url";
import { buildMcpServerConfig, resolveDefaultConfigPath, type McpInstallMode, type McpServerConfig } from "./mcp-config.js";
export type AgentPackFileKind = "readme" | "command" | "skill" | "doc" | "profile";
-export type AgentPackProfileId = "claude-code" | "generic-mcp" | "gemini-cli" | "copilot-cli";
+export type AgentPackProfileId = "claude-code" | "generic-mcp" | "cursor" | "gemini-cli" | "copilot-cli";
export type AgentPackTemplateDefinition = {
relativePath: string;
@@ -61,6 +61,11 @@ const supportedProfiles: AgentPackManifest["profiles"] = [
support: "supported",
summary: "Portable stdio MCP profile with client-specific config path differences.",
},
+ {
+ id: "cursor",
+ support: "documented",
+ summary: "Project-local Cursor adaptation that reuses stdio MCP wiring.",
+ },
{
id: "gemini-cli",
support: "documented",
@@ -122,6 +127,11 @@ const templateDefinitions: AgentPackTemplateDefinition[] = [
description: "Portable MCP stdio integration profile.",
kind: "profile",
},
+ {
+ relativePath: "profiles/cursor.md",
+ description: "Cursor adaptation notes.",
+ kind: "profile",
+ },
{
relativePath: "profiles/gemini-cli.md",
description: "Gemini CLI adaptation notes.",
diff --git a/src/index.ts b/src/index.ts
index cba618e..f35b2e8 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -8,7 +8,7 @@
* Transport: stdio (compatible with all MCP clients out of the box).
*/
-import { mkdir, readFile, writeFile } from "node:fs/promises";
+import { access, mkdir, readFile, writeFile } from "node:fs/promises";
import path from "node:path";
import { parseArgs } from "node:util";
import { createRequire } from "node:module";
@@ -34,9 +34,13 @@ import {
buildMcpConfigDocument,
buildMcpServerConfig,
parseConfigRoot,
+ REACT_SENTINEL_BINARY_NAME,
+ REACT_SENTINEL_PUBLIC_PACKAGE_NAME,
renderMcpConfigDocument,
resolveDefaultConfigPath,
type McpClient,
+ type McpConfigRootKey,
+ type McpServerConfig,
validateServerConfig,
type McpInstallMode,
upsertServerConfig,
@@ -56,6 +60,7 @@ const semverPattern =
/^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?(?:\+[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?$/;
export const REACT_SENTINEL_NAME = "react-sentinel";
+const DEFAULT_PUBLIC_CLIENT = "claude-code";
export const REACT_SENTINEL_VERSION =
typeof packageVersion === "string" && semverPattern.test(packageVersion)
? packageVersion
@@ -91,7 +96,7 @@ type DoctorCommandOptions = {
};
type InitMcpCommandOptions = {
- client: McpClient;
+ client: McpClient | "auto";
mode: McpInstallMode;
serverName: string;
replayHeadless: boolean;
@@ -99,6 +104,17 @@ type InitMcpCommandOptions = {
configPath: string | null;
};
+type InitMcpClientDefinition = {
+ id: McpClient;
+ label: string;
+ rootKey: McpConfigRootKey;
+ supportsWrite: boolean;
+ includeTransportType: boolean;
+ detectPaths: (cwd: string) => string[];
+ nextStep: string;
+ limitation: string;
+};
+
type DetectProjectCommandOptions = {
path: string;
json: boolean;
@@ -119,6 +135,109 @@ type UninstallAgentPackCommandOptions = {
targetDirectory: string;
};
+const initMcpClients: Record = {
+ "claude-code": {
+ id: "claude-code",
+ label: "Claude Code",
+ rootKey: "mcpServers",
+ supportsWrite: true,
+ includeTransportType: false,
+ detectPaths: (cwd) => [resolveDefaultConfigPath({ client: "claude-code", cwd })],
+ nextStep: "Restart Claude Code or reopen the project so the new MCP config is loaded.",
+ limitation: "Project-local setup only; React-Sentinel does not manage global Claude Code config automatically.",
+ },
+ "claude-desktop": {
+ id: "claude-desktop",
+ label: "Claude Desktop",
+ rootKey: "mcpServers",
+ supportsWrite: true,
+ includeTransportType: false,
+ detectPaths: (cwd) => [resolveDefaultConfigPath({ client: "claude-desktop", cwd })],
+ nextStep: "Restart Claude Desktop after saving the config.",
+ limitation: "Desktop integration only covers the MCP server entry; prompts and routines stay in the repository docs.",
+ },
+ cursor: {
+ id: "cursor",
+ label: "Cursor",
+ rootKey: "mcpServers",
+ supportsWrite: true,
+ includeTransportType: false,
+ detectPaths: (cwd) => [resolveDefaultConfigPath({ client: "cursor", cwd })],
+ nextStep: "Restart Cursor or reload the window so the MCP server is picked up.",
+ limitation: "Only the MCP transport is auto-written; any Cursor-specific prompt workflow remains manual.",
+ },
+ "gemini-cli": {
+ id: "gemini-cli",
+ label: "Gemini CLI",
+ rootKey: "mcpServers",
+ supportsWrite: true,
+ includeTransportType: false,
+ detectPaths: (cwd) => [resolveDefaultConfigPath({ client: "gemini-cli", cwd })],
+ nextStep: "Restart Gemini CLI in this project so it reloads .gemini/settings.json.",
+ limitation: "Gemini-specific command aliases and prompt memory remain manual.",
+ },
+ "github-copilot": {
+ id: "github-copilot",
+ label: "GitHub Copilot / VS Code",
+ rootKey: "servers",
+ supportsWrite: true,
+ includeTransportType: true,
+ detectPaths: (cwd) => [resolveDefaultConfigPath({ client: "github-copilot", cwd })],
+ nextStep: "Reload VS Code or reopen the workspace so Copilot can discover the MCP server.",
+ limitation: "The generated file targets VS Code/Copilot workspace config; other Copilot surfaces may require manual adaptation.",
+ },
+ "generic-mcp": {
+ id: "generic-mcp",
+ label: "Generic MCP client",
+ rootKey: "mcpServers",
+ supportsWrite: false,
+ includeTransportType: false,
+ detectPaths: () => [],
+ nextStep: "Paste the JSON snippet into your MCP client's config file and restart that client.",
+ limitation: "No default config path is assumed because generic MCP clients do not share one standard location.",
+ },
+};
+
+function formatInitMcpClientList(): string {
+ return ["auto", ...Object.keys(initMcpClients)].join("|");
+}
+
+function getInitMcpClientDefinition(client: McpClient): InitMcpClientDefinition {
+ return initMcpClients[client];
+}
+
+async function pathExists(targetPath: string): Promise {
+ try {
+ await access(targetPath);
+ return true;
+ } catch {
+ return false;
+ }
+}
+
+async function detectInitMcpClient(cwd: string): Promise {
+ const detectionOrder: McpClient[] = ["cursor", "github-copilot", "gemini-cli", "claude-code", "claude-desktop"];
+
+ for (const client of detectionOrder) {
+ const definition = getInitMcpClientDefinition(client);
+ for (const candidatePath of definition.detectPaths(cwd)) {
+ if (await pathExists(candidatePath)) {
+ return client;
+ }
+ }
+ }
+
+ return DEFAULT_PUBLIC_CLIENT;
+}
+
+function buildLaunchCommandPreview(serverConfig: McpServerConfig): string {
+ return [serverConfig.command, ...serverConfig.args].join(" ");
+}
+
+function inferConfigRootKey(targetPath: string): McpConfigRootKey {
+ return targetPath.endsWith(path.join(".vscode", "mcp.json")) ? "servers" : "mcpServers";
+}
+
function buildServerInfoResponse(): {
name: string;
version: string;
@@ -230,10 +349,12 @@ function formatHelp(): string {
return [
"React-Sentinel CLI",
"",
+ `Public npm package: ${REACT_SENTINEL_PUBLIC_PACKAGE_NAME}`,
+ "",
"Usage:",
" react-sentinel start [--headless|--headed] [--cdp-endpoint ]",
" react-sentinel mcp [--headless|--headed] [--cdp-endpoint ]",
- " react-sentinel init-mcp [--client ] [--mode ]",
+ ` react-sentinel init-mcp [--client <${formatInitMcpClientList()}>] [--mode ]`,
" react-sentinel init-agent-pack [--path ] [--mode ]",
" react-sentinel install-agent-pack [--path ] [--mode ]",
" react-sentinel update-agent-pack [--path ] [--mode ]",
@@ -245,7 +366,7 @@ function formatHelp(): string {
"Commands:",
" start Start the MCP server over stdio (default command).",
" mcp Explicit stdio MCP server command for agent/client configs.",
- " init-mcp Print a ready-to-paste MCP config snippet for Claude-compatible clients.",
+ " init-mcp Print or write a ready-to-paste MCP config snippet for common agent and IDE targets.",
" init-agent-pack Print the Claude Code-first agent-pack manifest prototype.",
" install-agent-pack Install agent-pack files and write the MCP config entry.",
" update-agent-pack Re-install agent-pack files, overwriting managed files and the MCP config entry.",
@@ -263,7 +384,7 @@ function formatHelp(): string {
" --path Base directory scanned by detect-project (defaults to the current directory).",
" --target-url Manual URL fallback used when detect-project should trust a caller-provided target.",
" --config-path Validate an existing MCP config file during doctor, or override the config file path used with init-mcp --write.",
- " --client Target MCP client for init-mcp (claude-code or claude-desktop).",
+ ` --client Target MCP client for init-mcp (${formatInitMcpClientList()}).`,
" --mode Launch mode for init-mcp (local, global, or npx).",
" --server-name Server key used inside the generated mcpServers object.",
" --write Write or merge the generated config into a config file.",
@@ -272,11 +393,13 @@ function formatHelp(): string {
" -v, --version Show the CLI version.",
"",
"Examples:",
- " npx react-sentinel mcp --headed",
- " npx react-sentinel doctor --json",
+ ` npx -y ${REACT_SENTINEL_PUBLIC_PACKAGE_NAME} mcp --headed`,
+ ` npx -y ${REACT_SENTINEL_PUBLIC_PACKAGE_NAME} doctor --json`,
" react-sentinel detect-project --path . --target-url http://127.0.0.1:3000 --json",
" react-sentinel doctor --config-path ~/.config/Claude/claude_desktop_config.json",
+ " react-sentinel init-mcp --client auto --mode npx",
" react-sentinel init-mcp --client claude-desktop --mode local",
+ " react-sentinel init-mcp --client github-copilot --mode npx --write",
" react-sentinel init-agent-pack --path . --mode npx",
" react-sentinel install-agent-pack --path . --mode local",
" react-sentinel update-agent-pack --path . --mode npx",
@@ -351,9 +474,9 @@ function parseInitMcpOptions(args: string[]): { options: InitMcpCommandOptions;
throw new Error("Choose either --headless or --headed, not both.");
}
- const client = parsed.values.client ?? "claude-desktop";
- if (client !== "claude-code" && client !== "claude-desktop") {
- throw new Error(`Invalid value for --client: "${client}". Use "claude-code" or "claude-desktop".`);
+ const client = parsed.values.client ?? "auto";
+ if (client !== "auto" && !(client in initMcpClients)) {
+ throw new Error(`Invalid value for --client: "${client}". Use one of ${formatInitMcpClientList()}.`);
}
const mode = parsed.values.mode ?? "local";
@@ -368,7 +491,7 @@ function parseInitMcpOptions(args: string[]): { options: InitMcpCommandOptions;
return {
options: {
- client,
+ client: client as InitMcpCommandOptions["client"],
mode,
serverName,
replayHeadless: parsed.values.headed ? false : true,
@@ -563,9 +686,10 @@ async function runDoctor(options: DoctorCommandOptions): Promise {
};
if (options.configPath) {
+ const rootKey = inferConfigRootKey(options.configPath);
try {
- const root = parseConfigRoot(await readFile(options.configPath, "utf8"));
- const validation = validateServerConfig(root, options.serverName);
+ const root = parseConfigRoot(await readFile(options.configPath, "utf8"), rootKey);
+ const validation = validateServerConfig(root, options.serverName, rootKey);
configCheck =
validation.status === "pass"
? {
@@ -687,8 +811,13 @@ async function runDoctor(options: DoctorCommandOptions): Promise {
}
async function runInitMcp(options: InitMcpCommandOptions): Promise {
+ const resolvedClient = options.client === "auto" ? await detectInitMcpClient(process.cwd()) : options.client;
+ const clientDefinition = getInitMcpClientDefinition(resolvedClient);
+ const snippetRootKey = options.write
+ ? inferConfigRootKey(options.configPath ?? resolveDefaultConfigPath({ client: resolvedClient }))
+ : clientDefinition.rootKey;
const document = buildMcpConfigDocument({
- client: options.client,
+ rootKey: snippetRootKey,
mode: options.mode,
serverName: options.serverName,
replayHeadless: options.replayHeadless,
@@ -699,11 +828,18 @@ async function runInitMcp(options: InitMcpCommandOptions): Promise {
return;
}
- const targetPath = options.configPath ?? resolveDefaultConfigPath({ client: options.client });
+ if (!clientDefinition.supportsWrite) {
+ throw new Error(
+ `The "${clientDefinition.label}" target does not have a safe default config path. Re-run without --write and paste the snippet manually.`
+ );
+ }
+
+ const targetPath = options.configPath ?? resolveDefaultConfigPath({ client: resolvedClient });
+ const rootKey = inferConfigRootKey(targetPath);
let configRoot: Record = {};
try {
- configRoot = parseConfigRoot(await readFile(targetPath, "utf8"));
+ configRoot = parseConfigRoot(await readFile(targetPath, "utf8"), rootKey);
} catch (error) {
if (!(error instanceof Error) || !("code" in error) || error.code !== "ENOENT") {
throw error;
@@ -712,23 +848,29 @@ async function runInitMcp(options: InitMcpCommandOptions): Promise {
const updatedRoot = upsertServerConfig({
root: configRoot,
+ rootKey,
serverName: options.serverName,
serverConfig: buildMcpServerConfig({
mode: options.mode,
replayHeadless: options.replayHeadless,
+ includeTransportType: clientDefinition.includeTransportType,
}),
});
await mkdir(path.dirname(targetPath), { recursive: true });
await writeFile(targetPath, `${JSON.stringify(updatedRoot, null, 2)}\n`, "utf8");
- const nextStep =
- options.client === "claude-desktop"
- ? "Restart Claude Desktop after saving the config."
- : "Restart Claude Code or reopen the project so the new MCP config is loaded.";
-
console.log(`Wrote MCP config for "${options.serverName}" to ${targetPath}.`);
- console.log(nextStep);
+ console.log(`Target: ${clientDefinition.label}`);
+ console.log(
+ `Launch command: ${buildLaunchCommandPreview(buildMcpServerConfig({
+ mode: options.mode,
+ replayHeadless: options.replayHeadless,
+ includeTransportType: clientDefinition.includeTransportType,
+ }))}`
+ );
+ console.log(`Limit: ${clientDefinition.limitation}`);
+ console.log(clientDefinition.nextStep);
}
async function runDetectProject(options: DetectProjectCommandOptions): Promise {
diff --git a/src/mcp-config.ts b/src/mcp-config.ts
index 87dfaf2..6472a57 100644
--- a/src/mcp-config.ts
+++ b/src/mcp-config.ts
@@ -2,17 +2,23 @@ import os from "node:os";
import path from "node:path";
import { fileURLToPath } from "node:url";
-export type McpClient = "claude-code" | "claude-desktop";
+export type McpClient =
+ | "claude-code"
+ | "claude-desktop"
+ | "cursor"
+ | "gemini-cli"
+ | "github-copilot"
+ | "generic-mcp";
export type McpInstallMode = "local" | "global" | "npx";
+export type McpConfigRootKey = "mcpServers" | "servers";
export type McpServerConfig = {
+ type?: "stdio";
command: string;
args: string[];
};
-export type McpConfigDocument = {
- mcpServers: Record;
-};
+export type McpConfigDocument = Partial>> & JsonObject;
export type JsonObject = Record;
@@ -23,6 +29,11 @@ export type McpConfigValidation = {
args: string[] | null;
};
+export const REACT_SENTINEL_BINARY_NAME = "react-sentinel";
+export const REACT_SENTINEL_PUBLIC_PACKAGE_NAME = "@edgarbrunet/react-sentinel";
+export const REACT_SENTINEL_LEGACY_PUBLIC_PACKAGE_NAME = "react-sentinel";
+const acceptedNpxPackageNames = [REACT_SENTINEL_PUBLIC_PACKAGE_NAME, REACT_SENTINEL_LEGACY_PUBLIC_PACKAGE_NAME];
+
const packageRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
const compiledCliEntry = path.join(packageRoot, "dist", "index.js");
@@ -33,11 +44,13 @@ function createLaunchArgs(replayHeadless: boolean): string[] {
export function buildMcpServerConfig(options: {
mode: McpInstallMode;
replayHeadless: boolean;
+ includeTransportType?: boolean;
}): McpServerConfig {
const launchArgs = createLaunchArgs(options.replayHeadless);
if (options.mode === "local") {
return {
+ ...(options.includeTransportType ? { type: "stdio" as const } : {}),
command: "node",
args: [compiledCliEntry, ...launchArgs],
};
@@ -45,28 +58,33 @@ export function buildMcpServerConfig(options: {
if (options.mode === "global") {
return {
- command: "react-sentinel",
+ ...(options.includeTransportType ? { type: "stdio" as const } : {}),
+ command: REACT_SENTINEL_BINARY_NAME,
args: launchArgs,
};
}
return {
+ ...(options.includeTransportType ? { type: "stdio" as const } : {}),
command: "npx",
- args: ["-y", "react-sentinel", ...launchArgs],
+ args: ["-y", REACT_SENTINEL_PUBLIC_PACKAGE_NAME, ...launchArgs],
};
}
export function buildMcpConfigDocument(options: {
- client: McpClient;
+ rootKey?: McpConfigRootKey;
mode: McpInstallMode;
serverName: string;
replayHeadless: boolean;
}): McpConfigDocument {
+ const rootKey = options.rootKey ?? "mcpServers";
+
return {
- mcpServers: {
+ [rootKey]: {
[options.serverName]: buildMcpServerConfig({
mode: options.mode,
replayHeadless: options.replayHeadless,
+ includeTransportType: rootKey === "servers",
}),
},
};
@@ -91,6 +109,22 @@ export function resolveDefaultConfigPath(options: {
return path.join(cwd, ".mcp.json");
}
+ if (options.client === "cursor") {
+ return path.join(cwd, ".cursor", "mcp.json");
+ }
+
+ if (options.client === "gemini-cli") {
+ return path.join(cwd, ".gemini", "settings.json");
+ }
+
+ if (options.client === "github-copilot") {
+ return path.join(cwd, ".vscode", "mcp.json");
+ }
+
+ if (options.client === "generic-mcp") {
+ return path.join(cwd, ".mcp.json");
+ }
+
if (platform === "darwin") {
return path.join(homeDir, "Library", "Application Support", "Claude", "claude_desktop_config.json");
}
@@ -103,19 +137,19 @@ export function resolveDefaultConfigPath(options: {
return path.join(homeDir, ".config", "Claude", "claude_desktop_config.json");
}
-export function parseConfigRoot(raw: string): JsonObject {
+export function parseConfigRoot(raw: string, rootKey: McpConfigRootKey = "mcpServers"): JsonObject {
const parsed = JSON.parse(raw) as unknown;
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
throw new Error("MCP config root must be a JSON object.");
}
const root = parsed as JsonObject;
- const existingServers = root.mcpServers;
+ const existingServers = root[rootKey];
if (
existingServers !== undefined &&
(typeof existingServers !== "object" || existingServers === null || Array.isArray(existingServers))
) {
- throw new Error("The mcpServers property must be a JSON object.");
+ throw new Error(`The ${rootKey} property must be a JSON object.`);
}
return root;
@@ -123,20 +157,22 @@ export function parseConfigRoot(raw: string): JsonObject {
export function upsertServerConfig(options: {
root: JsonObject;
+ rootKey?: McpConfigRootKey;
serverName: string;
serverConfig: McpServerConfig;
}): JsonObject {
- const existingServers = options.root.mcpServers;
+ const rootKey = options.rootKey ?? "mcpServers";
+ const existingServers = options.root[rootKey];
if (
existingServers !== undefined &&
(typeof existingServers !== "object" || existingServers === null || Array.isArray(existingServers))
) {
- throw new Error("The mcpServers property must be a JSON object.");
+ throw new Error(`The ${rootKey} property must be a JSON object.`);
}
return {
...options.root,
- mcpServers: {
+ [rootKey]: {
...(existingServers as Record | undefined),
[options.serverName]: options.serverConfig,
},
@@ -145,14 +181,16 @@ export function upsertServerConfig(options: {
export function removeServerConfig(options: {
root: JsonObject;
+ rootKey?: McpConfigRootKey;
serverName: string;
}): JsonObject {
- const existingServers = options.root.mcpServers;
+ const rootKey = options.rootKey ?? "mcpServers";
+ const existingServers = options.root[rootKey];
if (
existingServers !== undefined &&
(typeof existingServers !== "object" || existingServers === null || Array.isArray(existingServers))
) {
- throw new Error("The mcpServers property must be a JSON object.");
+ throw new Error(`The ${rootKey} property must be a JSON object.`);
}
if (!existingServers) {
@@ -163,17 +201,21 @@ export function removeServerConfig(options: {
return {
...options.root,
- mcpServers: remainingServers,
+ [rootKey]: remainingServers,
};
}
-export function validateServerConfig(root: JsonObject, serverName: string): McpConfigValidation {
+export function validateServerConfig(
+ root: JsonObject,
+ serverName: string,
+ rootKey: McpConfigRootKey = "mcpServers",
+): McpConfigValidation {
const issues: string[] = [];
- const rawServers = root.mcpServers;
+ const rawServers = root[rootKey];
if (rawServers === undefined) {
return {
status: "fail",
- issues: ["Missing top-level mcpServers object."],
+ issues: [`Missing top-level ${rootKey} object.`],
command: null,
args: null,
};
@@ -182,7 +224,7 @@ export function validateServerConfig(root: JsonObject, serverName: string): McpC
if (typeof rawServers !== "object" || rawServers === null || Array.isArray(rawServers)) {
return {
status: "fail",
- issues: ["The mcpServers property must be a JSON object."],
+ issues: [`The ${rootKey} property must be a JSON object.`],
command: null,
args: null,
};
@@ -192,7 +234,7 @@ export function validateServerConfig(root: JsonObject, serverName: string): McpC
if (serverEntry === undefined) {
return {
status: "fail",
- issues: [`Missing mcpServers.${serverName} entry.`],
+ issues: [`Missing ${rootKey}.${serverName} entry.`],
command: null,
args: null,
};
@@ -201,7 +243,7 @@ export function validateServerConfig(root: JsonObject, serverName: string): McpC
if (typeof serverEntry !== "object" || serverEntry === null || Array.isArray(serverEntry)) {
return {
status: "fail",
- issues: [`The mcpServers.${serverName} entry must be a JSON object.`],
+ issues: [`The ${rootKey}.${serverName} entry must be a JSON object.`],
command: null,
args: null,
};
@@ -216,11 +258,11 @@ export function validateServerConfig(root: JsonObject, serverName: string): McpC
: null;
if (!command || !command.trim()) {
- issues.push(`The mcpServers.${serverName}.command value is missing or empty.`);
+ issues.push(`The ${rootKey}.${serverName}.command value is missing or empty.`);
}
if (!args) {
- issues.push(`The mcpServers.${serverName}.args value must be an array of strings.`);
+ issues.push(`The ${rootKey}.${serverName}.args value must be an array of strings.`);
}
if (command === "node" && args) {
@@ -235,14 +277,16 @@ export function validateServerConfig(root: JsonObject, serverName: string): McpC
}
}
- if (command === "react-sentinel" && args && (!args[0] || (args[0] !== "mcp" && args[0] !== "start"))) {
+ if (command === REACT_SENTINEL_BINARY_NAME && args && (!args[0] || (args[0] !== "mcp" && args[0] !== "start"))) {
issues.push('The global react-sentinel command must receive "mcp" or "start" as its first argument.');
}
if (command === "npx" && args) {
- const packageIndex = args.indexOf("react-sentinel");
+ const packageIndex = args.findIndex((value) => acceptedNpxPackageNames.includes(value));
if (packageIndex === -1) {
- issues.push('The npx command must reference the "react-sentinel" package in args.');
+ issues.push(
+ `The npx command must reference "${REACT_SENTINEL_PUBLIC_PACKAGE_NAME}" (or the legacy "${REACT_SENTINEL_LEGACY_PUBLIC_PACKAGE_NAME}") in args.`
+ );
} else {
const launchCommand = args[packageIndex + 1];
if (!launchCommand || (launchCommand !== "mcp" && launchCommand !== "start")) {