Skip to content

xera Configuration Reference

Every project has a single root config: xera.config.ts. This file is committed to your repo. Secrets live in .env (gitignored). The XERA_AUTH_KEY environment variable is auto-generated by xera initdo not regenerate it, or your cached auth state becomes unreadable.

xera init flags

xera init is interactive by default. Pass flags to skip prompts — useful for CI pipelines or scripted onboarding.

Flags (all optional):
  -y, --yes                         Accept all defaults for any unflagged field
  --shape web|api|mixed             Project shape (default: web)
  --tracker jira|github             Issue tracker (default: jira)
  --editor <list>                   Editor(s) to scaffold: claude,cursor,codex or "all" (default: auto-detect or all)

  Jira (used when --tracker jira):
  --ju, --jira-base-url <url>       Jira workspace URL
  --pk, --project-keys <keys>       Project key(s), comma-separated (e.g. PROJ,OPS)
  --sf, --story-field <field>       Jira field id for user story (default: description)
  --ac, --ac-field <field>          Jira field id for acceptance criteria

  GitHub (used when --tracker github):
  --gr, --github-repo <owner/repo>  GitHub repository (e.g. xera-ai/xera)

  Web (shape: web | mixed):
  --su, --staging-url <url>         Web app staging URL
  --auth-enabled / --no-auth-enabled  Whether login is required (default: true)
  --ro, --roles <roles>             Test user roles, comma-separated (default: admin,regular)

  HTTP (shape: api | mixed):
  --au, --api-base-url <url>        API base URL
  --op, --openapi-path <path>       OpenAPI spec path or URL (default: ./openapi.yaml)
  --as, --auth-strategy <strategy>  bearer | apiKey | basic | oauth-cc | none (default: bearer)
  --hr, --http-roles <roles>        HTTP roles, comma-separated (default: user)

Any unflagged field still prompts interactively unless --yes is set.

Examples:

bash
# Fully interactive
xera init

# Non-interactive web project with defaults
xera init -y --shape web --pk MYPROJ --ju https://myco.atlassian.net --su https://staging.example.com

# Non-interactive API project
xera init -y --shape api --pk MYPROJ --ju https://myco.atlassian.net --au https://api.staging.example.com --as bearer

# Mixed project
xera init -y --shape mixed --pk MYPROJ --ju https://myco.atlassian.net \
  --su https://staging.example.com \
  --au https://api.staging.example.com --as bearer

# GitHub Issues instead of Jira (uses gh CLI or the GitHub MCP — no token needed)
xera init -y --shape web --tracker github --gr xera-ai/xera --su https://staging.example.com

Issue tracker: jira vs github

Exactly one of jira or github must be configured in xera.config.ts.

Jira (default)

ts
jira: {
  baseUrl: 'https://myco.atlassian.net',
  projectKeys: ['PROJ'],
  fields: {
    story: 'description',
    acceptanceCriteria: 'customfield_10100',  // optional
  },
}

xera uses the Atlassian MCP when available in your editor session, and falls back to the REST API when JIRA_EMAIL + JIRA_API_TOKEN are set in .env. Ticket keys: PROJ-123.

GitHub Issues

ts
github: { repo: 'xera-ai/xera' }

No token env vars needed. xera uses the GitHub MCP when available (the skill calls mcp__github__get_issue / mcp__github__add_issue_comment) and falls back to the gh CLI, which carries your existing auth. Ticket keys: GH-<number> — e.g. /xera-fetch GH-42 resolves issue 42 in the configured repo. Acceptance criteria are extracted from the issue body (no separate AC field exists on GitHub).

Precedence (highest to lowest)

  1. CLI flag (e.g. --env=prod)
  2. ENV var (e.g. XERA_ENV=prod)
  3. .env file in project root
  4. xera.config.ts
  5. Built-in defaults

Full schema

ts
import { defineConfig } from '@xera-ai/core';

export default defineConfig({
  jira: {
    baseUrl: 'https://thanhtrinity.atlassian.net',
    projectKeys: ['JIRA', 'XERA'],
    fields: {
      story: 'description',
      acceptanceCriteria: 'customfield_10001',
    },
  },
  web: {
    baseUrl: {
      local:   'http://localhost:3000',
      staging: 'https://staging.example.com',
      prod:    'https://example.com',
    },
    defaultEnv: 'staging',
    auth: {
      strategy: 'storageState',
      ttl: '8h',
      refreshBuffer: '30m',
      setupScript: './shared/auth-setup.ts',
      roles: {
        admin:   { envEmail: 'TEST_ADMIN_EMAIL',   envPassword: 'TEST_ADMIN_PWD' },
        regular: { envEmail: 'TEST_USER_EMAIL',    envPassword: 'TEST_USER_PWD' },
      },
    },
    testData: {
      users: {
        admin:   { fromAuth: 'admin' },
        regular: { fromAuth: 'regular' },
      },
    },
  },
  // v0.7 — http adapter (optional; at least one of web/http required)
  http: {
    baseUrl: {
      dev:     'https://api.dev.example.com',
      staging: 'https://api.staging.example.com',
    },
    defaultEnv: 'dev',
    spec: './openapi.yaml',          // path or URL; optional (enables CONTRACT_DRIFT)
    auth: {
      strategy: 'bearer',            // 'bearer' | 'apiKey' | 'basic' | 'oauth-cc' | 'custom' | 'none'
      ttl: '8h',
      refreshBuffer: '30m',
      roles: {
        user:  { tokenEnv: 'USER_BEARER_TOKEN' },
        admin: { tokenEnv: 'ADMIN_BEARER_TOKEN' },
      },
    },
  },
  ai: {
    livePageSnapshot: true,
    confidenceThreshold: 'medium',
    maxRetries: { typecheck: 2, lint: 2, validateFeature: 2 },
  },
  reporting: {
    language: 'en',
    postComment: true,
    transition: { onPass: null, onFail: null },
    artifactLinks: 'git',
  },
  adapters: ['web'],
});

Field-by-field

jira

  • baseUrl: your Atlassian Cloud workspace URL.
  • projectKeys: prefixes valid for ticket keys, e.g. ['JIRA'] matches JIRA-123.
  • fields.story: Jira field id holding the user story. Default description. Use xera init to detect.
  • fields.acceptanceCriteria: optional. If unset, xera reads AC from the story body.

web

  • baseUrl: map of environment name → URL. Must include defaultEnv.
  • defaultEnv: which environment xera targets by default.
  • spec: optional path or URL to an OpenAPI 3 document. When set (and the xeraNetwork recorder is active), /xera-report matches the network calls a web test made against the contract and emits CONTRACT_DRIFT on a mismatch. Mixed projects can configure http.spec once and it applies to web too (http.spec wins). Web drift is scoped to documented endpoints (status/schema mismatch) — page/asset loads are ignored.
  • auth.strategy: storageState (browser login form), apiToken (Bearer), or none.
  • auth.ttl: how long cached auth state is valid (8h, 30m, etc.).
  • auth.refreshBuffer: refresh proactively this far before expiry.
  • auth.setupScript: path to your defineAuthSetup-exported function.
  • auth.roles: declares which env vars hold credentials for each role.

Web CONTRACT_DRIFT recorder (opt-in). Detecting CONTRACT_DRIFT on web tests needs the network calls captured. @xera-ai/web exports attachNetworkRecorder(page, { logPath: process.env.XERA_NETWORK_LOG, scenario, baseUrl }) (and the xeraNetwork fixture) — attach it in your base Playwright test. It's a no-op unless XERA_NETWORK_LOG is set, which xera:exec does automatically, so plain playwright test runs are unaffected. Bodies are scrubbed at capture.

ai

  • livePageSnapshot: probe staging via Playwright MCP during POM generation. Disable for offline workflows.
  • confidenceThreshold: minimum confidence for classifier to commit a verdict.
  • maxRetries: per-gate retry caps in skills.

reporting

  • language: tracker comment language. en or vi.
  • postComment: master switch that gates publishing the comment to whichever tracker (jira or github) is configured. Legacy alias postToJira is accepted for backwards-compat.
  • transition: optional Jira status transitions on pass/fail. Default disabled. GitHub tracker ignores this — GitHub has no equivalent.
  • artifactLinks: where tracker links should point. git (committed paths in repo) or local (filesystem).

http (v0.7+)

  • baseUrl: map of environment name → URL for the API target. Must include defaultEnv.
  • defaultEnv: which environment xera targets by default.
  • spec: optional path or URL to an OpenAPI 3 document. When set, AI generation uses the schema to derive request bodies and the classifier emits CONTRACT_DRIFT on schema mismatches. It also powers /xera-feature <KEY> --from-spec (v0.18) — generate Gherkin directly from the spec with no fetched ticket, with --tag/--operation/--path filters; the synthetic ticket then flows through /xera-script/xera-exec/xera-report unchanged. When unset, xera still works — schema-derived edge cases, CONTRACT_DRIFT detection, and --from-spec are disabled (doctor warns).
  • auth.strategy: which preset to apply. bearer reads tokenEnv and prefixes Authorization: Bearer .... apiKey reads tokenEnv and attaches X-API-Key. basic base64-encodes userEnv:passEnv. oauth-cc performs an OAuth client_credentials handshake against tokenUrl. custom defers to the body of your defineHttpAuthSetup function (e.g. a login endpoint that returns a session token). none disables auth.
  • auth.ttl / auth.refreshBuffer: same semantics as web.auth.
  • auth.roles.<name>: per-role env-var references. Fields used depend on strategy: tokenEnv (bearer / apiKey), userEnv+passEnv (basic), tokenUrl+clientIdEnv+clientSecretEnv+optional scope (oauth-cc).

adapters

  • Array of adapter ids to enable. ['web'], ['http'], or ['web', 'http'] (mixed). At least one of the corresponding config blocks must be present. The first element is the default adapter for new tickets when meta.json.adapter is absent.

Environment variables

JIRA_EMAIL=
JIRA_API_TOKEN=
TEST_<ROLE>_EMAIL=
TEST_<ROLE>_PWD=
XERA_AUTH_KEY=               # 64-char hex, generated by `xera init`
XERA_ENV=staging             # optional override

coverage (v0.8.0+)

Configure the coverage gap report.

FieldTypeDefaultDescription
staleAfterDaysnumber30Window for both "recent activity" (risk formula) and "PASS recency" (STALE detection). Single threshold prevents two-knob mismatch.
criticalAreasstring[][]Area slugs treated as critical. risk(area) = recent_tickets × 2 + recent_bugs when area ∈ criticalAreas (otherwise multiplier = 1). Use to flag business-critical areas that may not have high ticket activity.
autoSnapshotOnCoveragebooleantrueEmit a coverage.snapshot event each time /xera-coverage runs. Powers the Trend tab in the HTML viewer (v0.8.1+). Disable if you don't want to track history.

Example:

ts
import { defineConfig } from '@xera-ai/core';

export default defineConfig({
  adapters: ['web', 'http'],
  coverage: {
    staleAfterDays: 30,
    criticalAreas: ['checkout', 'auth', 'billing'],
    autoSnapshotOnCoverage: true,
  },
  // ...
});

Risk formula reference

risk(area) = recent_tickets × critical_boost + recent_bugs

  recent_tickets   = #tickets with modifies→area, fetchedAt within staleAfterDays
  critical_boost   = 2 if area ∈ criticalAreas, else 1
  recent_bugs      = #run.classified events for scenarios touching area,
                     classification ∈ {REAL_BUG, TEST_OUTDATED}, within staleAfterDays

Weight constants are not user-configurable in v0.8 (see packages/core/src/coverage/risk.ts RISK_WEIGHTS). Tracked for future config knob.

xera doctor

Validates everything above and prints what is missing. Run after any config change.

Arities (since v0.16.1, see #149 / #153):

  • xera doctor — warn-only; exits 0 even if checks fail.
  • xera doctor --strict — env + config + auth checks; exits non-zero on any failure. Used by /xera-run Step 0 before any ticket artifact exists.
  • xera doctor --strict <TICKET> — adds per-ticket checks (artifact dir, graph-input.json, story.md acceptanceCriteria). Used by /xera-run Step 1.6 after fetch materializes .xera/<TICKET>/.
  • xera doctor --logs <TICKET> — pretty-print .xera/<TICKET>/xera.log.
  • xera doctor --auto-enrich — cron-friendly: backfill graph data for unprocessed tickets without prompting.

Graph (v0.6+)

The project knowledge graph stores event-sourced records under .xera/graph/events/. Configuration:

typescript
// xera.config.ts
export default defineConfig({
  graph: {
    redactionRules: 'default',  // 'default' | 'strict' | 'off' — applies to ticket text in events
  },
  cost: {
    dailyCapUsd: 5,  // soft warning threshold; doctor flags when exceeded
  },
});

Files

  • .xera/graph/events/<yyyy-mm>/*.jsonl — committed event log, one file per skill invocation
  • .xera/graph/snapshot.json — gitignored, regenerable in < 1s
  • .xera/cost-log.jsonl — gitignored, per-machine LLM cost log

TEST_OUTDATED classifier (v0.6.1+)

The TEST_OUTDATED classifier overrides the existing 4-bucket classification when the graph indicates a recent ticket has modified the scenario's SUT area and an LLM judges the failure is intentional.

This classifier is currently not user-configurable — confidence threshold and notification routing use built-in defaults. Tuning hooks (testOutdated.threshold, report.testOutdatedNotify) are tracked for a future release; the schema rejects them today so misconfiguration surfaces immediately rather than silently.

Auto-impact analysis from /xera-run (v0.6.2+)

When /xera-run <TICKET> runs, it can auto-call /xera-impact <TICKET> after fetch and prompt to re-run high-risk scenarios before generating new code.

typescript
// xera.config.ts
export default defineConfig({
  run: {
    autoImpact: {
      enabled: true,        // set false to disable the auto-trigger
      threshold: 6.0,       // minimum risk score to count a scenario as "high-risk"
    },
  },
});

The risk score formula:

score = priority_weight × 3
      + edge_type_weight
      + edge_confidence × 2
      − days_since_last_pass × 0.1

with P0=3, P1=2, P2=1; modifies-same-area edge weight 5; jira-linked.blocks weight 4. Tune threshold to the noise level you can tolerate.

HTML viewer (v0.6.3+)

Generate a single self-contained HTML file visualizing the project knowledge graph:

bash
npx xera-internal graph-render                            # full snapshot
npx xera-internal graph-render --since 90d                # filter to recent activity
npx xera-internal graph-render --ticket ABC-200 --depth 2 # ego-graph centered on one ticket
npx xera-internal graph-render --out custom-path.html     # custom output location

The viewer is a single self-contained HTML file (~700 KB total — vendored vis-network is the bulk). Open it in any browser; works offline. The file is automatically gitignored.

Performance modes (auto-selected based on graph size):

  • < 500 nodes: full mode — all node types visible
  • 500–2000 nodes: ticket-only — scenarios, POMs, and areas hidden
  • > 2000 nodes: text-fallback — writes a placeholder text file

CI publishing: xera init scaffolds .github/workflows/xera-graph.yml which renders the viewer on every PR, uploads it as an artifact, and posts a sticky comment with the artifact link. Reviewers click → open in browser, no clone required.

v0.6.4 changes

run.autoImpact.threshold default raised 6.0 → 8.0. This means /xera-run only prompts to re-run impacted scenarios when at least one has a risk score ≥ 8.0 (i.e. P0 scenario in a heavily-shared SUT area). Below threshold, the step is silent. Set threshold: 6.0 to restore the v0.6.2/v0.6.3 chatty behavior.

xera-internal disputes lists classification.disputed events for review by the QA lead:

bash
npx xera-internal disputes                       # all disputes, text format
npx xera-internal disputes --since 7d            # past week only
npx xera-internal disputes --format json         # machine-readable

xera doctor --auto-enrich runs non-interactive backfill of unbackfilled tickets, intended for CI:

bash
npx xera-internal doctor --auto-enrich           # cron-friendly

Released under the Apache 2.0 License.