Skip to content
The docs are now organized by product area. Old links still work. Report a broken link

Claude Code plugin reference

Reference for the Qualflare plugin for Claude Code: every /qf- command, the coverage nudge rules, state files, authentication and the formats it knows.

Last updated

This page lists the 8 slash commands of the Qualflare plugin for Claude Code, the rules of its coverage nudge, its state files and the format slugs it knows, with arguments, defaults and limits.

Installation

CommandDescription
/plugin marketplace add Qualflare/qualflare-claude-codeAdd the Qualflare marketplace
/plugin install qualflare@qualflareInstall the plugin. It stays installed across sessions
/plugin update qualflareUpgrade to the latest version

The plugin is qualflare, from the marketplace qualflare. It needs qf on your PATH, and Node.js for the coverage nudge.

Slash commands

CommandArgumentWhat it does
/qf-init—Sets up the project
/qf-cover[--all] [path or glob]Writes tests for changed code
/qf-run[framework-slug | package/path [slug] | report-path [slug]]Runs tests and uploads the results
/qf-fix[framework-slug or package/path]Fixes failing tests from the last run
/qf-doctor—Checks the setup
/qf-update[framework-slug or package/path]Refreshes file counts
/qf-state—Shows what the plugin knows
/qf-hookon|offTurns the coverage nudge on or off

/qf-init

/qf-init sets up a project once, from its root (a folder with .git/, package.json, go.mod or another manifest). In order, it:

  1. finds a monorepo's packages (npm or pnpm workspaces, or several go.mod or pyproject.toml files);
  2. detects each package's languages, frameworks and test-file counts, and asks you to confirm them;
  3. writes .qualflare/test-state.md;
  4. prints a qf login <identifier> line per identifier not logged in, and waits for done or skip;
  5. asks about the coverage nudge and writes .qualflare/config.json;
  6. writes a Qualflare block between <!-- BEGIN qualflare-ai --> and <!-- END qualflare-ai --> in CLAUDE.md, changing nothing outside it.

An identifier is the package name lowercased, with characters outside [a-z0-9_-] turned into -, runs of - collapsed and the ends trimmed. pkg- is prefixed when the result is empty, starts with anything but a letter or digit, or is a qf command name such as login or help, and the identifier is cut to 63 characters: @acme/web becomes acme-web.

/qf-cover

/qf-cover writes tests for source files changed in the session that lack tests; a path or glob narrows them, and --all takes every source file in the path. It proposes 2 to 5 cases per function (a happy path, an edge case and, for error handling, an error case) and writes them once you approve. With --all, or in a project with no tests, it asks you to narrow to 8 files or fewer per round. It writes jest (Vitest imports when Vitest is a dependency), playwright, pytest, Go, RSpec, PHPUnit, JUnit or TestNG tests, and appends to an existing test file without removing a test.

/qf-run

/qf-run runs the frameworks in test-state.md and uploads each result with qf <identifier> collect. The first matching rule reads its argument:

  1. an exact slug from test-state.md runs that framework (/qf-run cypress runs Cypress even with a cypress/ folder);
  2. a package path, or its start, runs that package, narrowed by a slug after it;
  3. an existing file, a glob, or a directory plus a slug is a report: it is uploaded, and nothing runs;
  4. anything else is read as a slug.
SlugWhat /qf-run runsUploads
jestnpx vitest run with Vitest, else npx jestJest JSON
mochanpx mochaMocha JSON
pythonpytestJUnit XML
golanggo test ./... -jsonGo test JSON
playwrightnpx playwright testPlaywright JSON
cypressnpx cypress run; needs mochawesome as a dependencyMochawesome JSON, one per spec
rspecbundle exec rspecRSpec JSON
phpunit./vendor/bin/phpunitJUnit XML
junitmvn clean test or gradle cleanTest testJUnit XML from every module
cucumbercucumber-js, bundle exec cucumber or mvn clean testCucumber JSON
webdriverionpx wdio run with the @qualflare/webdriverio reporterOne JSON per worker
appiumnpx wdio run with the @qualflare/appium reporterOne JSON per worker
flutterflutter test qf ≥ 0.2.0 (requires qf 0.2.0 or later)Flutter JSON

The CLI reads qualflare_flutter data (labels, steps, screenshots) qf ≥ 0.3.0 (requires qf 0.3.0 or later). Other slugs need a report you produce, uploaded with /qf-run <report-path> <slug>:

SlugsReport the CLI parses
selenium, testcafe, karate, newmanThe tool's JSON report
k6k6 JSON end-of-test summary
testng, maestro, espresso, xctestJUnit XML; for xctest, not an .xcresult bundle
zap, trivy, snykThe tool's JSON output
sonarqubeSonarQube issues export (JSON)

Without a slug, /qf-run detects a report's format with qf validate (local, no login) and asks you to confirm. A report goes to the only identifier, or the one whose package path matches; otherwise /qf-run asks.

Before uploading, /qf-run deletes each framework's previous result, and checks qf version and, with qf projects, every login. It continues past failing tests, passes --branch and --commit from git, and never passes --environment, so results go to QF_ENVIRONMENT, or development when it is unset (Environments). It prints Uploaded to Qualflare ✅ only when every upload succeeded.

/qf-fix

/qf-fix reads the last /qf-run results in .qualflare/results/ and makes the smallest source change for each failing test. It edits source files, not tests, and skips a test whose assertion looks wrong. It then re-runs the affected frameworks once and reports what still fails. It makes no second pass and doesn't upload.

/qf-doctor

/qf-doctor checks the setup and prints a report with a fix for each problem:

CheckRuns or readsResult
CLIqf version --short❌ when qf isn't found
Auth, per identifierqf projects⚠️ when not logged in: qf login <identifier>, with a token from Project Settings > Access Tokens
Slug syncqf list-formats against the plugin's slugs⚠️ for a slug only one side knows
Config.qualflare/config.json❌ when missing or invalid
Hook registrationthe plugin's hooks.json⚠️ when no Stop hook is registered
State file ageGenerated at in test-state.md✅ up to 30 days; ⚠️ 31 to 60 days (/qf-update or /qf-init); ❌ over 60 days (/qf-init)
Plugin versiontest-state.md against the plugin⚠️ when they differ (/qf-init)
File countseach framework's test files, counted again⚠️ when they drift by more than 10 % and more than 5 files (/qf-update); ❌ when none remain of a stored count above 0 (/qf-init)
Framework toolingthe version command of jest or Vitest, mocha, playwright, cypress, golang, python, rspec, phpunit, junit and cucumber⚠️ when the tool isn't found; other slugs are skipped

Example report:

Setup
  CLI                    ✅  qf <version>
  Auth (<identifier>)    ⚠️  not configured
  State file age         ✅  12 day(s) old
File counts
  jest                   ✅  47 live  (stored: 42)
Framework tooling
  jest tooling           ✅  <version>

Overall: 1 warning(s), 0 error(s)
Suggested fixes:
  qf login <identifier>

/qf-update

/qf-update counts each framework's test files again and updates File count and Generated at in test-state.md. It doesn't detect frameworks again; /qf-init does.

/qf-state

/qf-state shows the detected frameworks with live test-file counts, the coverage nudge setting, and whether each identifier is logged in (configured).

/qf-hook

/qf-hook on (or yes) turns the coverage nudge on, and /qf-hook off (or no) turns it off; either creates .qualflare/config.json when it is missing. With no argument, /qf-hook prints the current setting, or says the hook is not configured when there is no config.json.

The coverage nudge (Stop hook)

The plugin registers a Claude Code Stop hook, which runs each time Claude finishes a response, not only at the end of a session. When stopHookEnabled is true in .qualflare/config.json, it reads the session transcript and prints one line when all three conditions below hold:

🔍 Qualflare: 3 source file(s) changed without test updates. Run /qf-cover to add coverage.

When the nudge appears

  • At least one source file was edited (.js, .ts, .go, .py, .rb, .php, .rs, .java, .kt, and their JSX and module variants (.jsx, .tsx, .mjs, .cjs, .mts, .cts)).
  • No test files were edited in the same session.
  • At least one of those edits is substantive. An edit made with the Edit tool is trivial, and ignored, when its old and new text are each a single line and their lengths differ by 40 characters or fewer. Whole-file writes (Write) and multi-edits (MultiEdit) always count.

What the hook ignores

Excluded categoryFiles
Test files.test. and .spec. files with a js, ts, jsx, tsx, mjs, cjs, mts or cts extension; *_test.go; test_*.py; *_test.py; *_spec.rb under spec/; *Test.php under tests/; and any file under __tests__/, e2e/, cypress/ or playwright/
Config files*.config. js, ts, jsx, tsx, mjs, cjs, mts, cts or json (for example vite.config.ts); .<name>rc. js, ts, mjs, cjs, mts, cts, json, yaml or yml (for example .eslintrc.json); the bare .eslintrc, .prettierrc, .babelrc, .stylelintrc and .commitlintrc; tsconfig.json and tsconfig.<name>.json
Type-only files*.d.ts, *.d.mts, *.d.cts, types.ts, types.tsx, *.types.ts, *.types.tsx
Build and dependency foldersnode_modules/, vendor/, .git/, dist/, build/, .next/, __pycache__/, .cache/

Toggling

/qf-hook on     — enable the nudge
/qf-hook off    — disable the nudge
/qf-hook        — print current status

State files

/qf-init creates a .qualflare/ directory in the project root:

.qualflare/
├── test-state.md    — framework + file-count context (commit this)
├── config.json      — hook preference (commit this)
└── results/         — raw test-runner output uploaded by qf <identifier> collect (gitignore this)

Add .qualflare/results/ to your .gitignore. The other two files should be committed — they give every session the context it needs without re-running setup.

test-state.md structure

Generated by /qf-init, updated by /qf-update. Contains:

  • Project — name, languages, generation timestamp, plugin version
  • Packages — path and CLI identifier for each package (one row per package; (root) for single-package projects)
  • Frameworks in use — slug, language, file count, and top-level paths per (package, framework) pair
  • Frameworks suggested — frameworks detected as possible but not yet installed
  • Conventions — test naming pattern and coverage threshold
  • Notes — free-text notes from setup

config.json fields

FieldTypeDescription
versionnumberSchema version (currently 1)
stopHookEnabledbooleanWhether the coverage nudge is active

Authentication

The plugin uses the same identifier-based auth as the Qualflare CLI. After /qf-init prints your identifiers, register each one in your own terminal, not in Claude Code:

qf login <identifier>

qf login asks for the token at a hidden prompt. Don't paste a token into the chat or pass it as an argument. In CI, set QF_TOKEN and run qf login <identifier> --force.

In a monorepo, each package gets its own identifier — run qf login once per package. The CLI saves tokens in qualflare/config.toml under your user config directory: $XDG_CONFIG_HOME, or ~/.config when it is unset, on Linux, ~/Library/Application Support on macOS, %AppData% on Windows. The file is readable only by you (mode 0600). Create tokens in Project Settings > Access Tokens (https://app.qualflare.com/project/<project-identifier>/settings/access-tokens). Creating one: Editor+ · project (requires the Editor role or higher in the project).

Run qf projects to list currently registered identifiers. For the full CLI authentication reference, see Configuration.

Supported frameworks

The plugin uploads through the Qualflare CLI and knows the format slugs below. It doesn't know allure: upload an Allure results directory yourself with qf <identifier> collect allure-results/ qf ≥ 0.4.0 (requires qf 0.4.0 or later) (see Allure results).

CategoryFrameworks
Generic (JUnit-compatible)junit, ctrf, qualflare-json
Unit Testingpython (pytest), golang, jest, vitest, mocha, rspec, phpunit, testng
BDDcucumber, karate
UI / E2E / Mobileplaywright, cypress, selenium, testcafe, maestro, xctest, espresso, detox, webdriverio, appium, flutter
API Testingnewman, k6
Security Testingzap, trivy, snyk, sonarqube

/qf-run lists the slugs it runs itself. For any other slug, produce the report with your own tooling, then upload it with /qf-run <report-path> <slug>.

/qf-init records Vitest projects under the jest slug, so /qf-run uploads them as jest. Use --format vitest only when you run qf <identifier> collect yourself. Multi-framework monorepos are supported — /qf-init detects each workspace and tracks them separately.

Upload errors

/qf-run handles each upload by the CLI's exit code:

Exit codeMeaningWhat /qf-run does
0UploadedRecords it
3Token rejected (401)Asks you to run qf login <identifier> again; skips that identifier's other uploads
4Access denied or plan limit (402, 403)Records the error
5Not found (404)Records the error
7Rate limit or server error (429, 5xx)Retries once, then records the error
1Anything else, including an environment the project doesn't haveRecords the error; stops as a missing login on Error: no identifier

Limits

  • Identifier: ^[a-z0-9][a-z0-9_-]{0,62}$ (/qf-init).
  • /qf-cover: 2 to 5 cases per function; 8 files per round with --all or no tests yet.
  • /qf-fix: one pass and one re-run.
  • Coverage nudge: needs Node.js; the hook times out after 30 seconds.