Setup

Two paths. A project with no tests yet, and a project that already has a suite and a house style. The second one is the common case and it is the one most of this page is about.

What you need

Always

  • Claude Code
  • python3, any 3.8 or later, for qa-context and qa-policy. Both are stdlib only, nothing to install
  • git

Only for the stack you actually use

ProfileNeeds
ts-playwrightNode 18+
py-pytest-playwrightPython 3.9+, a virtualenv
mobile-appium-wdioNode 18+, Java, Android SDK with an emulator or a connected device
your ownwhatever your runner already needs

Only if you want the thing it enables

ForYou need
/qa:explore in a real browserNode, for the Playwright MCP. It ships enabled and installs itself on first use
Jira or Confluence as the source of requirements and the home for bugsThe Atlassian MCP, connected once per machine
GitHub Issues insteadThe gh CLI, authenticated. No MCP
/qa:explore on a deviceAn Appium or WebdriverIO MCP, plus a running Appium server
No server, no database, no API key for the framework itself. It is a plugin, two Python scripts and a folder of markdown.

Install

/plugin marketplace add eduramos21/qa-automation-framework
/plugin install qa@eduramos21-qa

If the install summary says Run /reload-plugins to activate, do that.

To try it without installing, clone and point Claude Code at the clone:

git clone https://github.com/eduramos21/qa-automation-framework
cd qa-automation-framework
claude --plugin-dir .

Check it loaded. /help should list /qa:audit, /qa:refine and the rest, and qa-context should be on the path:

$ qa-context
No qa.config.yml found in /your/repo or any parent.

That error is the correct output before setup. It means the tooling is there and the project is not configured yet.

Connecting Jira

Two steps. The first is once per machine, the second is once per project.

1. Connect the MCP

claude mcp add --transport http atlassian https://mcp.atlassian.com/v1/mcp

Then /mcp inside Claude Code to run the OAuth flow. Check it took:

$ claude mcp list
atlassian: https://mcp.atlassian.com/v1/mcp - ✔ Connected

If you already reach Jira through a claude.ai connector, it is connected and there is nothing to do here.

2. Point the project at it

# qa.config.yml
tracker:
  provider: jira
  project_key: SHOP
  mcp: atlassian

requirements:
  source: jira          # or confluence, if the specs live there

What that turns on

CommandWith a tracker configured
/qa:refine SHOP-142Fetches the issue, its linked issues and its comments. The comments matter: half the decisions on any ticket live there and never make it into the description
/qa:exploreFiles what it finds into SHOP, using the bug report shape, with the trace or screenshot attached
/qa:auditChecks coverage against the real requirements rather than guessing what the suite was supposed to cover

GitHub Issues instead

tracker:
  provider: github-issues

requirements:
  source: github-issues

No MCP. It shells out to gh issue view --comments and gh issue create, so you need gh auth login done and nothing else.

Neither

tracker:
  provider: none

requirements:
  source: markdown
  path: docs/requirements/

Requirements are read from files and bug reports are written to files. Everything still works, it just does not file anything for you. This is what both demo projects in the repo use.

Path A, a project with no tests yet

cd your-repo
claude
/qa:init

init reads the repo, works out the language, runner and CI, asks about what it cannot see, writes qa.config.yml and drops in a pipeline file. Then:

/qa:refine SHOP-142        # or a path to a markdown requirement
/qa:design qa/requirements/SHOP-142.md
/qa:generate qa/design/SHOP-142.md

That is the whole loop. The worked example on the overview page follows exactly this, start to finish, with real output.

Path B, a repo that already has tests

The common case, and the one with real friction. Being upfront about it is more useful than pretending there is none.

Start with the audit, not the config

cd your-repo
claude
/qa:init          # five minutes, mostly confirming what it already worked out
/qa:audit

/qa:audit is the thing to run first on an existing suite. It needs nothing but the config, it reads code you already have, it writes nothing, and it hands back nine scores with evidence and a ranked list of findings. If the tool is going to be useful to you, you will know from that one report.

Then fix the profile, because the shipped one is not your house style

If init matched your runner, the profile knows how to run your tests. It does not know how you write them. The shipped CONVENTIONS.md holds one particular set of opinions, and if your repo does something else, generated code will fight your reviewers.

So before generating anything:

/qa:profile

That reads your suite, weighted towards the most recently touched files because recent code is the current style, and drafts a profile.yaml and a CONVENTIONS.md that describe what your repo actually does. Every example in it is lifted from your code, with the path it came from.

It writes into <your-repo>/profiles/, which wins over the plugin's copy. Nothing is forked, nothing is patched, and updating the plugin later does not overwrite your description.

Where your suite contradicts itself, and any suite over a year old does, it asks which pattern new tests should follow rather than picking silently. That question takes ten seconds to answer and is the highest value part of the process.

It describes, it does not correct. If your page objects contain assertions, the conventions will say your page objects contain assertions. Things worth changing go in a separate report, never into the conventions file. Mixing "what we do" with "what we should do" produces code your reviewers reject, and then the tool gets uninstalled.

No profile matches your runner at all

Java plus Selenium, Cypress, Robot Framework, C# plus NUnit. Same command, /qa:profile, starting from the blank template instead of a near match. Meanwhile /qa:refine, /qa:design and /qa:explore already work, because none of them writes code.

The policy hook will not fight your legacy code

Worth knowing, because the obvious implementation of this would be unusable. policies.forbid lists patterns like fixed sleeps. A suite that has been around a while has plenty.

The edit hook only reports violations on lines an edit actually touched. Editing a legacy file that already contains three sleeps, for an unrelated reason, reports nothing. Adding a new sleep reports that one. A brand new file is checked in full.

Pre-existing violations are still real and still show up in an audit, which is where a decision about them can be made, rather than being forced on you one file at a time.

# the full picture, any time you want it
qa-policy --stack web

# only what this edit introduced, which is what the hook uses
qa-policy --changed-only path/to/spec.ts

Running the check at commit time, without the plugin

qa-policy is a standalone, stdlib only Python 3 script. It does not need Claude Code, the plugin, or anything installed beyond Python to run — the --hook flag is the only part coupled to the edit hook's JSON. A developer who wants just this check can pull it on its own.

Add this repo as a pre-commit hook source:

# .pre-commit-config.yaml, in the target project
repos:
  - repo: https://github.com/eduramos21/qa-automation-framework
    rev: <tag or commit>
    hooks:
      - id: qa-policy

Or wire it into husky, lint-staged, or a raw git hook directly, since it is just a script that takes staged file paths as arguments:

bin/qa-policy --changed-only <staged files>
Either way the target repo still needs a qa.config.yml with a policies.forbid list — that is what qa-policy checks against. A bare policies: block with no stacks: is enough, the rest of the setup here is not required. With the plugin installed, the same thing is /qa:prevalidate.

A monorepo with several suites

stacks:
  web:    { profile: acme-web,    root: apps/web/e2e/ }
  api:    { profile: acme-api,    root: services/api/tests/ }
  mobile: { profile: acme-mobile, root: apps/mobile/e2e/ }

Every command takes the stack name. qa-context api, /qa:audit api, qa-policy --stack api.

Roughly what it costs

StepTimeWorth it when
/qa:init5 minutesalways, everything else needs it
/qa:auditone runalways, and it is the thing that tells you whether to continue
/qa:profile20 to 40 minutes, mostly you answering questionsbefore generating or fixing any code
Writing a profile from the blank template by handhalf a dayonly if you would rather not have it read your suite

When something is wrong

OutputMeansDo
No qa.config.yml foundNot set up, or you are in the wrong directory/qa:init, or qa-context --from <path>
profile 'x' NOT FOUNDThe config names a profile that does not existCheck the spelling, or /qa:profile
declares no stacksThe config is a stubFill in the stacks block
qa-context: command not foundThe plugin is not loaded/reload-plugins, or run bin/qa-context from a clone
Generated tests do not look like your repoThe profile's conventions are not yours/qa:profile
The hook complains about code you did not writeShould not happen, the hook is changed-onlyCheck the file is committed. Git cannot tell what changed in a file it has never seen, so an untracked file is checked in full
getByTestId finds nothingpolicies.test_id_attribute and the runner config disagreeMake them match. This one is silent and wastes an afternoon