Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions .claude-plugin/marketplace.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"name": "browserstack-ai-tfa",
"owner": {
"name": "BrowserStack",
"url": "https://www.browserstack.com"
},
"description": "BrowserStack's test-failure-analysis plugins for Claude Code, Cursor and Codex.",
"plugins": [
{
"name": "tfa-rca",
"source": "./",
"description": "Point it at a red BrowserStack build: it reads every failed test, clusters them by failure signature, gathers evidence from the tools you already have, and lands a root cause per test naming the pull request most likely responsible. Requires a GitHub route; everything else degrades to a recorded gap.",
"category": "testing",
"keywords": [
"browserstack",
"test-failure-analysis",
"root-cause-analysis",
"flaky-tests",
"ci",
"observability"
]
}
]
}
11 changes: 11 additions & 0 deletions .claude-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"name": "tfa-rca",
"description": "Drive collaborative root-cause analysis over all failed tests of a build, generic across product and infra.",
"version": "0.1.0",
"author": {
"name": "BrowserStack",
"url": "https://www.browserstack.com"
},
"homepage": "https://github.com/browserstack/ai-tfa-plugins",
"license": "MIT"
}
13 changes: 13 additions & 0 deletions .cursor-mcp.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"mcpServers": {
"bstack": {
"command": "npx",
"args": ["-y", "@browserstack/mcp-server@1.2.27-beta.1"],
"env": {
"BROWSERSTACK_USERNAME": "${BROWSERSTACK_USERNAME}",
"BROWSERSTACK_ACCESS_KEY": "${BROWSERSTACK_ACCESS_KEY}",
"O11Y_TFA_RCA_BASE_URL": "${O11Y_TFA_RCA_BASE_URL}"
}
}
}
}
8 changes: 8 additions & 0 deletions .cursor-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"name": "tfa-rca",
"description": "Collaborative root-cause analysis over all failed tests of a BrowserStack build, generic across product and infra.",
"version": "0.1.0",
"mcpServers": "../.cursor-mcp.json",
"skills": "./skills/",
"author": { "name": "BrowserStack" }
}
8 changes: 8 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# BrowserStack credentials — used by the bundled bstack MCP server for
# listTestIds + tfaRcaTurn. Per-user; never commit real values.
BROWSERSTACK_USERNAME=
BROWSERSTACK_ACCESS_KEY=

# Observability base URL the TFA RCA chat runs against. Optional —
# the bstack MCP server defaults to its rengg-tfa staging URL when unset.
# O11Y_TFA_RCA_BASE_URL=https://api-observability-rengg-tfa.bsstag.com
8 changes: 8 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
node_modules/
.env
.DS_Store
*.code-workspace
# Per-run RCA batch state (the CSV/WAL spine + report) is workspace-local.
.rca/
# Planning docs (brainstorm/ideation/plan) stay local — not pushed.
docs/
14 changes: 14 additions & 0 deletions .mcp.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"mcpServers": {
"bstack": {
"type": "stdio",
"command": "npx",
"args": ["-y", "@browserstack/mcp-server@1.2.27-beta.1"],
"env": {
"BROWSERSTACK_USERNAME": "${BROWSERSTACK_USERNAME}",
"BROWSERSTACK_ACCESS_KEY": "${BROWSERSTACK_ACCESS_KEY}",
"O11Y_TFA_RCA_BASE_URL": "${O11Y_TFA_RCA_BASE_URL}"
}
}
}
}
104 changes: 104 additions & 0 deletions INTEGRATION.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
# Multi-client integration (Claude Code · Cursor · Codex)

This plugin is built so the **MCP core is truly cross-client** and the **harness
layer ports via the cross-vendor Agent Skills standard**. Only one piece is
genuinely Claude-Code-specific (the batch *dynamic workflow*); on Cursor and
Codex that role is filled by the sequential harness or subagents. Every path is
autonomous after the single `/rca-build` gate — no host ever prompts mid-run. The
setup interview is a phase of that same skill: it runs on a repo's first invocation
and never again, so it is the one interactive surface and it is not per build.

## What transfers, what doesn't

| Layer | Claude Code | Cursor | Codex |
|---|---|---|---|
| `bstack` MCP server (`listTestIds` + `tfaRcaTurn` + `triggerRcaReport`) | `.mcp.json` (auto-discovered) | `.cursor-mcp.json` / `.cursor/mcp.json` | `~/.codex/config.toml` `[mcp_servers.bstack]` |
| `rca-build` skill (`SKILL.md`) | plugin `skills/` | Agent Skills (`.cursor/skills/` or cursor-plugin `"skills":"./skills/"`) | Agent Skills (`.agents/skills/`) |
| `ai-tfa-coordinator` agent | plugin `agents/` | `.cursor/agents/` (also reads `.claude/agents/`) | `.codex/agents/` |
| Per-test RCA **loop** | `agents/ai-tfa-coordinator.md` | same skill/agent | same skill/agent |
| Batch orchestration | dynamic workflow `workflows/rca-batch.mjs` (or subagents) | subagents, or **sequential** `lib/loop.mjs` | subagents, or **sequential** `lib/loop.mjs` |

The dynamic workflow (`workflows/rca-batch.mjs`) uses Claude Code's Workflow
runtime, which Cursor/Codex don't have. The same batch still runs there via
**subagents** (both hosts support subagents) or the **sequential thin-client
harness** `lib/loop.mjs` (`runRcaLoop`) — the conformance-tested loop that
drives `tfaRcaTurn` over the same contract without any host-specific
orchestration. On every host the run finishes the same way: glimpse table →
`triggerRcaReport(buildUuid)` → "Full report on the Test Observability UI:
<viewReport>". No local report file is ever written.

## Claude Code

```bash
cp .env.example .env # BROWSERSTACK_USERNAME / BROWSERSTACK_ACCESS_KEY
claude --plugin-dir ./
/rca-build <build-id>
```

`.claude-plugin/plugin.json` + root `.mcp.json` + `skills/` + `agents/` are
auto-discovered. (No `commands/rca-build.md` on purpose — a command and skill
with the same name collide and the skill body fails to load.)

## Cursor

The repo ships Cursor parity files mirroring `slack-mcp-plugin`:
`.cursor-plugin/plugin.json` (points at `../.cursor-mcp.json` and `./skills/`)
and `.cursor-mcp.json` (the stdio `bstack` server).

**Wire the MCP server** — either:
- copy `.cursor-mcp.json`'s `bstack` entry into your project `.cursor/mcp.json`
(top-level `mcpServers`), or
- Cursor → Settings → Cursor Settings → **MCP** → paste the same JSON, or
- use an **Add to Cursor** deeplink:
`cursor://anysphere.cursor-deeplink/mcp/install?name=bstack&config=<base64-of-the-bstack-entry-body>`

Set `BROWSERSTACK_USERNAME` / `BROWSERSTACK_ACCESS_KEY` / `O11Y_TFA_RCA_BASE_URL`
in your environment (or replace the `${…}` placeholders with literals).

**Skill + agent discovery** — Cursor reads `.cursor/skills/` and `.cursor/agents/`
(and also `.claude/agents/`). The simplest no-duplication setup is to symlink the
shared trees:

```bash
mkdir -p .cursor
ln -s ../skills .cursor/skills
ln -s ../agents .cursor/agents
```

Then drive it from Agent chat: invoke the `rca-build` skill with a build id.

## Codex

Codex reads the global `~/.codex/config.toml` (no per-project MCP file).

**Wire the MCP server** — either copy the block from `codex-mcp.example.toml`
into `~/.codex/config.toml`, or:

```bash
codex mcp add bstack \
--env BROWSERSTACK_USERNAME=… --env BROWSERSTACK_ACCESS_KEY=… \
--env O11Y_TFA_RCA_BASE_URL=https://api-observability-rengg-tfa.bsstag.com \
-- npx -y @browserstack/mcp-server@1.2.27-beta.1
```

**Skill + agent discovery** — Codex reads `.agents/skills/` (skills) and
`.codex/agents/` (subagents). Symlink the shared trees:

```bash
mkdir -p .agents .codex
ln -s ../skills .agents/skills
ln -s ../agents .codex/agents
```

Then run the `rca-build` skill; the coordinator + `tfaRcaTurn` loop are identical.

## Notes

- The `bstack` server is **stdio** (`npx @browserstack/mcp-server@1.2.27-beta.1`), not a remote
OAuth server — so the configs use `command`/`args`/`env`, unlike Slack's
`url`+`oauth`/`auth` shape.
- Env-var interpolation (`${VAR}`) is honored by Claude Code's `.mcp.json`; on
Cursor/Codex, replace the placeholders with literals if your client doesn't
expand them.
- Everything in `lib/` and the `SKILL.md`/agent prose is host-agnostic — only the
MCP wiring file and the dynamic workflow are host-specific.
141 changes: 140 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,141 @@
# ai-tfa-plugins
Test Failure Analysis Plugins

Root-cause analysis for a whole BrowserStack build, from inside your coding agent.

Point it at a red build. It reads every failed test, groups them by failure
signature, gathers evidence from the tools you already have — your code, your logs,
your cluster, your metrics, your CI — and works with BrowserStack's analysis agent
to land a root cause per test, naming the pull request that most likely caused it.

Works in **Claude Code**, **Cursor** and **Codex**.

> **The report lands on the Test Observability dashboard, not in your terminal.**
> Your agent prints a short status table and a link. That link is the deliverable.

---

## Before you start

| You need | Why |
|---|---|
| A BrowserStack account with Test Observability | The build, its test logs, and the dashboard the report lands on |
| `BROWSERSTACK_USERNAME` + `BROWSERSTACK_ACCESS_KEY` | From your [account settings](https://www.browserstack.com/accounts/profile/details) |
| **GitHub access** — the `gh` CLI signed in, or a GitHub MCP server | Required. Without your code and its merged PRs there is no culprit PR to name, and that is the point of the run |
| Anything else you use — logs, metrics, a cluster, CI | Optional. Each one you skip is recorded and shown in the report as evidence that was not available |

GitHub is the only hard requirement. Everything else is offered, never forced.

## Install

In Claude Code:

```
/plugin marketplace add browserstack/ai-tfa-plugins
/plugin install tfa-rca@browserstack-ai-tfa
```

Then set your credentials — the plugin needs them before it can read anything:

```bash
export BROWSERSTACK_USERNAME=... # from your account settings
export BROWSERSTACK_ACCESS_KEY=...
```

Ask Claude to **run the plugin's setup** and it will check both of these, confirm your
GitHub route, and tell you exactly what is missing if anything is.

<details>
<summary>Installing from a clone instead (for development)</summary>

```bash
git clone https://github.com/browserstack/ai-tfa-plugins.git
cd ai-tfa-plugins
cp .env.example .env # add your BrowserStack username + access key
claude --plugin-dir ./
```

</details>

Everything wires itself on load — the BrowserStack MCP server, the `rca-build`
skill, and the analysis agent are all found by convention.

**Using Cursor or Codex?** See **[INTEGRATION.md](INTEGRATION.md)** for the
per-client setup. The core is identical; only the batching differs.

## Run it

```
/tfa-rca:rca-build <build-id>
```

The build id is the one in your Test Observability URL. A dashboard link works too.

You can hand it more, and anything you supply wins over what it would work out for
itself:

```
/tfa-rca:rca-build <build-id> https://github.com/org/repo/pull/9254 .../pull/7900
```

- **A list of PRs** becomes *the* set of suspects. It is treated as complete — the
run stops searching for candidates of its own and spends its effort deciding which
of yours is to blame, reporting each one it rules out and why. Paste the merged-PR
list straight out of your release thread; good and bad together is exactly right.
- **Anything else you pin** — a CI run, an environment, a branch — overrides what
the build's metadata says. Pasting a whole regression-bot message works; it reads
what is in it.

Pinned values apply to this run only and are never written to disk.

## The first run asks you some questions

The first time you run this in a directory, it interviews you — once. It says what
BrowserStack already has and what only you can supply, then asks about your side:
which repos, which branch, where your logs are, what runs your services. It reads
your project first so it only asks what it genuinely cannot see, and it **proves
every answer with a real read** before keeping it.

The result is saved to `.rca-context.json` next to where you ran it. **Commit it** —
a teammate who clones the repo inherits the whole setup and is asked only for their
own credentials.

Every run after that asks **at most one question**, and usually none. On a repeat run
it first shows you the setup it has on file — repos, branches, connectors, when each
was last verified — so you can correct anything or point it at a different
environment before it starts.

## What you get

When every test has an answer, your agent prints a short table — one line per test
with its cluster and confidence — then:

```
Full report on the Test Observability UI: <link>
```

The dashboard report holds the real detail: root cause per test, the evidence behind
it, and linked pull requests on application bugs.

## When it stops and asks

It refuses rather than guessing when guessing would give you a confident wrong
answer. What each one means:

| It says | What happened | What to do |
|---|---|---|
| It starts interviewing you | No setup here yet | Answer — it is once per directory |
| **GitHub could not be verified** | No working `gh` or GitHub MCP server, or it cannot see the repo | Sign in (`gh auth login`) and re-run. This is the one thing that blocks a run |
| **This build does not match your saved setup** | The build's name is not one your `.rca-context.json` describes — often a sibling suite | It offers to add this build to the existing setup, make a new one for it, or use the existing one just this once |
| **Two saved setups both match** | Two profiles claim the same build name | Narrow one of their patterns, or re-run naming the one you want |
| **Your setup file could not be read** | Usually a merge conflict left in `.rca-context.json` | Fix the file. Nothing is overwritten until you do |
| **Run this from your own directory** | You ran it from inside the plugin's own clone | `cd` to your project and re-run — your setup belongs with your code, not ours |
| **This would discard a teammate's setup** | A write would have removed something already verified | Nothing was written. Re-run, or reconcile the file by hand |

Missing logs, metrics or CI never block a run. They are recorded, declared to the
analysis agent as evidence it does not have, and reflected in the confidence of the
result.

## Contributing

`npm test` runs the suite (no build step, no dependencies).
[INTEGRATION.md](INTEGRATION.md) covers the per-client wiring.
Loading
Loading