@step-forge/step-forge 0.0.24 → 0.0.25-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/RUNTIME.md +19 -10
- package/dist/cli.cjs +369 -98
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +369 -98
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.cjs","names":["path","useColor","wrap","userFrames","relativeFrame","relativeLocation","write","BasicWorld","path","globFiles","compileRegistry","globalRegistry","parseFeatureFiles","runHooksParallel","globalHookRegistry","runHooks","runScenario","useColor","fs","path","globFiles","parseFeatureCatalog","path","path"],"sources":["../../src/runtime/config.ts","../../src/runtime/filter.ts","../../src/runtime/reporters.ts","../../src/runtime/runner.ts","../../src/runtime/prompt.ts","../../src/runtime/watcher.ts","../../src/runtime/interactive.ts","../../src/runtime/cli.ts"],"sourcesContent":["import * as path from \"node:path\";\nimport { pathToFileURL } from \"node:url\";\nimport { access } from \"node:fs/promises\";\n\n/**\n * User-facing runner configuration. The same shape is accepted from a\n * `step-forge.config.ts` file (default export) and from CLI flags, with flags\n * taking precedence. Everything is optional; {@link resolveConfig} fills in\n * defaults.\n */\nexport interface RunnerOptions {\n /** Feature-file glob(s), relative to `cwd`. Defaults to every `.feature` file. */\n features?: string | string[];\n /** Step-module glob(s), relative to `cwd`. Defaults to every `.steps.ts` file. */\n steps?: string | string[];\n /**\n * Module that default-exports a world factory `() => world`, relative to\n * `cwd`. When omitted each scenario gets a fresh `BasicWorld`.\n */\n world?: string;\n /**\n * Max scenarios in flight at once. Defaults to `1` (serial), matching\n * Cucumber's default execution model — scenarios often share module-level\n * state via hooks, which only holds under serial execution. Raise it to opt\n * into parallelism for isolated, I/O-bound suites.\n */\n concurrency?: number;\n /** Reporter name. Default `pretty`. */\n reporter?: \"pretty\" | \"progress\";\n /**\n * Verbose output: report every scenario (pass, fail, skip) instead of only\n * failures. Passed to whichever reporter is active. Default `false`.\n */\n verbose?: boolean;\n /** Only run scenarios whose name matches this (string → substring/regex). */\n name?: string;\n /** Cucumber tag expression, e.g. `@smoke and not @wip`. */\n tags?: string;\n}\n\n/** Fully-resolved config: no optionals, globs kept as arrays, paths absolute. */\nexport interface ResolvedConfig {\n cwd: string;\n features: string[];\n steps: string[];\n world?: string;\n concurrency: number;\n reporter: \"pretty\" | \"progress\";\n verbose: boolean;\n name?: string;\n tags?: string;\n}\n\nconst DEFAULT_FEATURES = \"**/*.feature\";\nconst DEFAULT_STEPS = \"**/*.steps.ts\";\nconst CONFIG_BASENAMES = [\n \"step-forge.config.ts\",\n \"step-forge.config.mts\",\n \"step-forge.config.js\",\n \"step-forge.config.mjs\",\n];\n\nfunction toArray<T>(value: T | T[] | undefined): T[] {\n if (value === undefined) return [];\n return Array.isArray(value) ? value : [value];\n}\n\nasync function exists(p: string): Promise<boolean> {\n try {\n await access(p);\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Locate and import a `step-forge.config.*` file from `cwd`, returning its\n * default export (or `{}` if none is present). Imported by absolute file URL so\n * Bun transpiles the TypeScript config natively.\n */\nexport async function loadConfigFile(cwd: string): Promise<RunnerOptions> {\n for (const basename of CONFIG_BASENAMES) {\n const candidate = path.join(cwd, basename);\n if (!(await exists(candidate))) continue;\n const mod = await import(pathToFileURL(candidate).href);\n return (mod.default ?? mod) as RunnerOptions;\n }\n return {};\n}\n\n/**\n * Merge file config with CLI overrides and apply defaults. CLI overrides win\n * field-by-field; array globs are normalised to arrays and default when neither\n * source provides them.\n */\nexport function resolveConfig(\n cwd: string,\n file: RunnerOptions,\n cli: RunnerOptions\n): ResolvedConfig {\n const pick = <K extends keyof RunnerOptions>(key: K): RunnerOptions[K] =>\n cli[key] ?? file[key];\n\n const features = toArray(pick(\"features\"));\n const steps = toArray(pick(\"steps\"));\n\n return {\n cwd,\n features: features.length ? features : [DEFAULT_FEATURES],\n steps: steps.length ? steps : [DEFAULT_STEPS],\n world: pick(\"world\"),\n concurrency: pick(\"concurrency\") ?? 1,\n reporter: pick(\"reporter\") ?? \"pretty\",\n verbose: pick(\"verbose\") ?? false,\n name: pick(\"name\"),\n tags: pick(\"tags\"),\n };\n}\n","import { ParsedScenario } from \"../analyzer/types\";\n\n/**\n * A predicate over a scenario's tags, compiled from a Cucumber tag expression.\n * Supports `and`, `or`, `not`, parentheses, and bare `@tag` atoms — the common\n * subset of Cucumber's tag-expression language. Kept dependency-free; swap for\n * `@cucumber/tag-expressions` if fuller parity is needed.\n */\nexport type TagPredicate = (tags: readonly string[]) => boolean;\n\n/** Tokenise a tag expression into atoms, operators, and parentheses. */\nfunction tokenize(expr: string): string[] {\n const tokens: string[] = [];\n const re = /\\s*(\\(|\\)|@[^\\s()]+|\\band\\b|\\bor\\b|\\bnot\\b)\\s*/gy;\n let index = 0;\n while (index < expr.length) {\n re.lastIndex = index;\n const m = re.exec(expr);\n if (!m || m.index !== index) {\n throw new Error(`Invalid tag expression near: ${expr.slice(index)}`);\n }\n tokens.push(m[1]);\n index = re.lastIndex;\n }\n return tokens;\n}\n\n/**\n * Compile a tag expression into a predicate via recursive descent\n * (or → and → not → primary). Throws on malformed input so a bad `--tags` flag\n * fails loudly rather than silently matching nothing.\n */\nexport function compileTagExpression(expr: string): TagPredicate {\n const tokens = tokenize(expr);\n let pos = 0;\n\n const peek = () => tokens[pos];\n const next = () => tokens[pos++];\n\n const parseOr = (): TagPredicate => {\n let left = parseAnd();\n while (peek() === \"or\") {\n next();\n const right = parseAnd();\n const l = left;\n left = tags => l(tags) || right(tags);\n }\n return left;\n };\n\n const parseAnd = (): TagPredicate => {\n let left = parseNot();\n while (peek() === \"and\") {\n next();\n const right = parseNot();\n const l = left;\n left = tags => l(tags) && right(tags);\n }\n return left;\n };\n\n const parseNot = (): TagPredicate => {\n if (peek() === \"not\") {\n next();\n const operand = parseNot();\n return tags => !operand(tags);\n }\n return parsePrimary();\n };\n\n const parsePrimary = (): TagPredicate => {\n const token = next();\n if (token === \"(\") {\n const inner = parseOr();\n if (next() !== \")\") throw new Error(\"Unbalanced parentheses in tags\");\n return inner;\n }\n if (token === undefined || !token.startsWith(\"@\")) {\n throw new Error(`Expected a tag, got: ${token ?? \"end of input\"}`);\n }\n return tags => tags.includes(token);\n };\n\n const predicate = parseOr();\n if (pos !== tokens.length) {\n throw new Error(`Unexpected token in tag expression: ${peek()}`);\n }\n return predicate;\n}\n\nexport interface SelectOptions {\n /** Substring or `/regex/flags` match against the scenario (or outline) name. */\n name?: string;\n /** Cucumber tag expression. */\n tags?: string;\n}\n\n/** Parse a `/pattern/flags` string into a RegExp, else treat it as a substring. */\nfunction toNameMatcher(name: string): (candidate: string) => boolean {\n const delim = /^\\/(.*)\\/([a-z]*)$/.exec(name);\n if (delim) {\n const re = new RegExp(delim[1], delim[2]);\n return candidate => re.test(candidate);\n }\n return candidate => candidate.includes(name);\n}\n\n/**\n * Narrow scenarios by name and tags, then apply `@only` focus. `@skip` is *not*\n * applied here — skipped scenarios stay in the set so the runner can report them\n * as skipped rather than silently dropping them. Returns scenarios in input\n * order.\n *\n * `@only` semantics mirror the Vitest plugin: if any surviving scenario is\n * tagged `@only` (and not `@skip`), the run is focused to just those.\n */\nexport function selectScenarios(\n scenarios: ParsedScenario[],\n options: SelectOptions\n): ParsedScenario[] {\n let selected = scenarios;\n\n if (options.name) {\n const matches = toNameMatcher(options.name);\n selected = selected.filter(\n s => matches(s.name) || (s.outline ? matches(s.outline.name) : false)\n );\n }\n\n if (options.tags) {\n const predicate = compileTagExpression(options.tags);\n selected = selected.filter(s => predicate(s.tags));\n }\n\n const focused = selected.filter(\n s => s.tags.includes(\"@only\") && !s.tags.includes(\"@skip\")\n );\n return focused.length ? focused : selected;\n}\n","import { relative } from \"node:path\";\nimport { relativeFrame, relativeLocation, userFrames } from \"../sourceLocation\";\nimport { ScenarioResult, StepResult } from \"./engine\";\n\n/**\n * A reporter observes the run. `onScenarioEnd` fires as each scenario finishes\n * (completion order, non-deterministic under concurrency); `onComplete` fires\n * once with every result for the end-of-run output.\n *\n * Every reporter accepts a `verbose` flag: without it a reporter reports only\n * failures (plus a summary); with it, it reports every scenario. A reporter may\n * render the same either way — `verbose` is a request, not a requirement.\n */\nexport interface Reporter {\n onScenarioEnd?(result: ScenarioResult): void;\n onComplete(results: ScenarioResult[], durationMs: number): void;\n}\n\nexport interface ReporterOptions {\n cwd?: string;\n verbose?: boolean;\n}\n\n// --- ANSI colouring -------------------------------------------------------\nconst useColor =\n !process.env.NO_COLOR && (process.stdout.isTTY ?? false) === true;\n\nconst wrap = (open: number, close: number) => (s: string) =>\n useColor ? `\\x1b[${open}m${s}\\x1b[${close}m` : s;\n\nconst c = {\n green: wrap(32, 39),\n red: wrap(31, 39),\n yellow: wrap(33, 39),\n dim: wrap(2, 22),\n bold: wrap(1, 22),\n};\n\nconst STATUS_MARK: Record<StepResult[\"status\"], string> = {\n passed: c.green(\"✓\"),\n failed: c.red(\"✗\"),\n skipped: c.yellow(\"-\"),\n};\n\n/** ✓ / ✗ / - for a whole scenario (skipped = every step skipped). */\nfunction scenarioMark(result: ScenarioResult): string {\n if (result.status === \"failed\") return c.red(\"✗\");\n if (result.steps.every(s => s.status === \"skipped\")) return c.yellow(\"-\");\n return c.green(\"✓\");\n}\n\n/** Dot per scenario for the live heartbeat: `.` pass / `F` fail / `-` skip. */\nfunction scenarioDot(result: ScenarioResult): string {\n if (result.status === \"failed\") return c.red(\"F\");\n if (result.steps.every(s => s.status === \"skipped\")) return c.yellow(\"-\");\n return c.green(\".\");\n}\n\n/** Indent every non-empty line of a block by `pad` spaces. */\nfunction indent(text: string, pad: number): string {\n const prefix = \" \".repeat(pad);\n return text\n .split(\"\\n\")\n .map(line => (line ? prefix + line : line))\n .join(\"\\n\");\n}\n\n/**\n * The error, trimmed to user frames: `Name: message` followed by only the\n * caller's own stack frames (library/engine and `node_modules` frames removed),\n * source-mapped by Bun to the original `.ts`. Falls back to just the message\n * when nothing user-owned is left (e.g. an undefined-step error).\n */\nfunction renderError(error: Error, cwd: string): string {\n const header = `${error.name}: ${error.message}`;\n const frames = userFrames(error.stack).map(\n f => ` at ${relativeFrame(f, cwd)}`\n );\n return frames.length ? `${header}\\n${frames.join(\"\\n\")}` : header;\n}\n\n/**\n * The detail block shown beneath a failing step: the `.feature` line, the step\n * definition location, and the trimmed error — the two coordinates that make a\n * failure easy to chase (where in the feature, which step definition).\n */\nfunction failureDetail(\n stepResult: StepResult,\n result: ScenarioResult,\n cwd: string\n): string {\n const lines = [\n `feature: ${relative(cwd, result.scenario.file)}:${stepResult.step.line}`,\n ];\n if (stepResult.source) {\n lines.push(`defined: ${relativeLocation(stepResult.source, cwd)}`);\n }\n if (stepResult.error) lines.push(renderError(stepResult.error, cwd));\n return indent(c.red(lines.join(\"\\n\")), 6);\n}\n\n/**\n * One scenario as a Cucumber-style block: a marked header with its `.feature`\n * location, then each step with its mark (and, in `verbose`, the step\n * definition location as a trailing comment). A failing step is followed by its\n * failure detail; a scenario-level error (a failing hook, no step to blame) is\n * appended at the end.\n */\nfunction renderScenario(\n result: ScenarioResult,\n cwd: string,\n opts: { stepSource: boolean }\n): string {\n const label = result.scenario.outline\n ? `${result.scenario.outline.name} › ${result.scenario.name}`\n : result.scenario.name;\n const lines: string[] = [`${scenarioMark(result)} ${c.bold(label)}`];\n\n const loc = result.scenario.line\n ? `${relative(cwd, result.scenario.file)}:${result.scenario.line}`\n : relative(cwd, result.scenario.file);\n lines.push(indent(c.dim(loc), 4));\n lines.push(\"\");\n\n for (const step of result.scenario.steps) {\n const sr = result.steps.find(s => s.step === step);\n const status = sr?.status ?? \"skipped\";\n const comment =\n opts.stepSource && sr?.source\n ? c.dim(` # ${relativeLocation(sr.source, cwd)}`)\n : \"\";\n lines.push(\n ` ${STATUS_MARK[status]} ${c.dim(step.effectiveKeyword)} ${step.text}${comment}`\n );\n if (sr?.status === \"failed\") lines.push(failureDetail(sr, result, cwd));\n }\n\n // A hook failure has no failing step; surface its error at scenario level.\n const stepFailed = result.steps.some(s => s.status === \"failed\");\n if (result.error && !stepFailed) {\n lines.push(indent(c.red(renderError(result.error, cwd)), 4));\n }\n\n return lines.join(\"\\n\");\n}\n\n// --- Summary --------------------------------------------------------------\ninterface Totals {\n scenarios: { passed: number; failed: number; skipped: number };\n steps: { passed: number; failed: number; skipped: number };\n}\n\nfunction tally(results: ScenarioResult[]): Totals {\n const totals: Totals = {\n scenarios: { passed: 0, failed: 0, skipped: 0 },\n steps: { passed: 0, failed: 0, skipped: 0 },\n };\n for (const result of results) {\n if (result.status === \"failed\") totals.scenarios.failed++;\n else if (result.steps.every(s => s.status === \"skipped\"))\n totals.scenarios.skipped++;\n else totals.scenarios.passed++;\n for (const step of result.steps) totals.steps[step.status]++;\n }\n return totals;\n}\n\nfunction renderSummary(results: ScenarioResult[], durationMs: number): string {\n const t = tally(results);\n const scenarioTotal =\n t.scenarios.passed + t.scenarios.failed + t.scenarios.skipped;\n const stepTotal = t.steps.passed + t.steps.failed + t.steps.skipped;\n\n const part = (n: number, label: string, color: (s: string) => string) =>\n n > 0 ? color(`${n} ${label}`) : null;\n\n const line = (\n total: number,\n noun: string,\n counts: { passed: number; failed: number; skipped: number }\n ) => {\n const parts = [\n part(counts.passed, \"passed\", c.green),\n part(counts.failed, \"failed\", c.red),\n part(counts.skipped, \"skipped\", c.yellow),\n ].filter((x): x is string => x !== null);\n return `${total} ${noun}${total === 1 ? \"\" : \"s\"} (${parts.join(\", \")})`;\n };\n\n return [\n line(scenarioTotal, \"scenario\", t.scenarios),\n line(stepTotal, \"step\", t.steps),\n c.dim(`${(durationMs / 1000).toFixed(2)}s`),\n ].join(\"\\n\");\n}\n\nfunction write(text: string): void {\n process.stdout.write(text);\n}\n\n/** Feature-grouped tree of every scenario (used by `--verbose`). */\nfunction renderFullTree(results: ScenarioResult[], cwd: string): string {\n const groups = new Map<string, ScenarioResult[]>();\n for (const r of results) {\n const bucket = groups.get(r.scenario.file) ?? [];\n bucket.push(r);\n groups.set(r.scenario.file, bucket);\n }\n const out: string[] = [];\n for (const [file, group] of groups) {\n out.push(c.bold(`Feature: ${relative(cwd, file)}`), \"\");\n for (const r of group)\n out.push(indent(renderScenario(r, cwd, { stepSource: true }), 2), \"\");\n }\n return out.join(\"\\n\");\n}\n\n/** The failing scenarios only (used by the default, non-verbose output). */\nfunction renderFailures(results: ScenarioResult[], cwd: string): string {\n const failed = results.filter(r => r.status === \"failed\");\n if (!failed.length) return \"\";\n return (\n failed\n .map(r => renderScenario(r, cwd, { stepSource: false }))\n .join(\"\\n\\n\") + \"\\n\\n\"\n );\n}\n\n/**\n * Default reporter. Non-verbose: a dot per scenario as a heartbeat, then the\n * failing scenarios in Cucumber-style detail, then the summary. Verbose: the\n * full feature → scenario → step tree instead of just failures.\n */\nexport function prettyReporter(opts: ReporterOptions = {}): Reporter {\n const cwd = opts.cwd ?? process.cwd();\n const verbose = opts.verbose ?? false;\n return {\n onScenarioEnd(result) {\n if (!verbose) write(scenarioDot(result));\n },\n onComplete(results, durationMs) {\n const body = verbose\n ? `\\n${renderFullTree(results, cwd)}\\n`\n : `\\n\\n${renderFailures(results, cwd)}`;\n write(`${body}${renderSummary(results, durationMs)}\\n`);\n },\n };\n}\n\n/**\n * Compact reporter: a dot per scenario, then failures in detail and the\n * summary. Always minimal — `verbose` is accepted for interface parity but does\n * not expand the output (use `pretty --verbose` for the full tree).\n */\nexport function progressReporter(opts: ReporterOptions = {}): Reporter {\n const cwd = opts.cwd ?? process.cwd();\n return {\n onScenarioEnd(result) {\n write(scenarioDot(result));\n },\n onComplete(results, durationMs) {\n write(\n `\\n\\n${renderFailures(results, cwd)}${renderSummary(results, durationMs)}\\n`\n );\n },\n };\n}\n\nexport function makeReporter(\n name: \"pretty\" | \"progress\",\n opts: ReporterOptions = {}\n): Reporter {\n return name === \"progress\" ? progressReporter(opts) : prettyReporter(opts);\n}\n","/* eslint-disable @typescript-eslint/no-explicit-any */\nimport * as path from \"node:path\";\nimport { pathToFileURL } from \"node:url\";\nimport { ParsedScenario } from \"../analyzer/types\";\nimport { parseFeatureFiles } from \"../analyzer/gherkinParser\";\nimport { globFiles } from \"../globFiles\";\nimport { BasicWorld, MergeableWorld } from \"../world\";\nimport {\n compileRegistry,\n CompiledStep,\n runScenario,\n ScenarioResult,\n} from \"./engine\";\nimport { globalHookRegistry, runHooks, runHooksParallel } from \"./hooks\";\nimport { globalRegistry } from \"./registry\";\nimport { selectScenarios } from \"./filter\";\nimport { makeReporter, Reporter } from \"./reporters\";\nimport { ResolvedConfig } from \"./config\";\n\ntype WorldFactory = () => MergeableWorld<any, any, any>;\n\n/**\n * Import every step-definition module so its `.step(...)` calls self-register\n * into the shared `globalRegistry`. Imported by file URL so Bun transpiles the\n * TypeScript natively.\n */\nasync function importSteps(files: string[]): Promise<void> {\n for (const file of files) {\n await import(pathToFileURL(file).href);\n }\n}\n\n/** Load the configured world factory, or default to a fresh `BasicWorld`. */\nasync function loadWorldFactory(\n world: string | undefined,\n cwd: string\n): Promise<WorldFactory> {\n if (!world) return () => new BasicWorld();\n const resolved = path.resolve(cwd, world);\n const mod = await import(pathToFileURL(resolved).href);\n const factory = mod.default ?? mod;\n if (typeof factory !== \"function\") {\n throw new Error(\n `World module ${world} must default-export a factory function () => world`\n );\n }\n return factory as WorldFactory;\n}\n\n/** A scenario carrying `@skip` never runs: report it with all steps skipped. */\nfunction skippedResult(scenario: ParsedScenario): ScenarioResult {\n return {\n scenario,\n status: \"passed\",\n steps: scenario.steps.map(step => ({ step, status: \"skipped\" as const })),\n };\n}\n\n/**\n * Run `items` through `worker`, at most `limit` in flight. Results come back in\n * input order even though completion order is not deterministic. A minimal\n * dependency-free pool — the whole point of the single-process model.\n */\nasync function runPool<T, R>(\n items: T[],\n limit: number,\n worker: (item: T, index: number) => Promise<R>\n): Promise<R[]> {\n const results = new Array<R>(items.length);\n let cursor = 0;\n const runNext = async (): Promise<void> => {\n while (cursor < items.length) {\n const index = cursor++;\n results[index] = await worker(items[index], index);\n }\n };\n const workers = Array.from(\n { length: Math.min(Math.max(1, limit), items.length) },\n runNext\n );\n await Promise.all(workers);\n return results;\n}\n\nexport interface RunResult {\n results: ScenarioResult[];\n passed: boolean;\n durationMs: number;\n}\n\n/**\n * Execute a whole run end to end: discover and import steps, load the world,\n * parse and select scenarios, then run them concurrently against a\n * compiled-once step table with global/feature hooks around the batch. Never\n * throws for test failures — inspect `RunResult.passed`.\n */\nexport async function run(\n config: ResolvedConfig,\n reporter: Reporter = makeReporter(config.reporter, {\n cwd: config.cwd,\n verbose: config.verbose,\n })\n): Promise<RunResult> {\n const start =\n typeof performance !== \"undefined\" ? performance.now() : Date.now();\n\n const [featureFiles, stepFiles] = await Promise.all([\n globFiles(config.features, config.cwd),\n globFiles(config.steps, config.cwd),\n ]);\n\n await importSteps(stepFiles);\n const makeWorld = await loadWorldFactory(config.world, config.cwd);\n\n const compiled: CompiledStep[] = compileRegistry(globalRegistry);\n const allScenarios = parseFeatureFiles(featureFiles);\n const selected = selectScenarios(allScenarios, {\n name: config.name,\n tags: config.tags,\n });\n\n // Global `beforeAll` hooks run once, in parallel, before any scenario starts;\n // feature before/after bracket the whole batch inside them. (Hooks aren't\n // file-scoped in the registry, so per-file bracketing would be meaningless\n // under concurrent execution.)\n await runHooksParallel(\"global\", \"before\", globalHookRegistry);\n await runHooks(\"feature\", \"before\", globalHookRegistry);\n\n const results = await runPool(\n selected,\n config.concurrency,\n async scenario => {\n const result = scenario.tags.includes(\"@skip\")\n ? skippedResult(scenario)\n : await runScenario(scenario, compiled, makeWorld, globalHookRegistry);\n reporter.onScenarioEnd?.(result);\n return result;\n }\n );\n\n await runHooks(\"feature\", \"after\", globalHookRegistry);\n // Global `afterAll` hooks run once, in parallel, after every scenario is done.\n await runHooksParallel(\"global\", \"after\", globalHookRegistry);\n\n const durationMs =\n (typeof performance !== \"undefined\" ? performance.now() : Date.now()) -\n start;\n reporter.onComplete(results, durationMs);\n\n return {\n results,\n passed: results.every(r => r.status !== \"failed\"),\n durationMs,\n };\n}\n","import * as readline from \"node:readline\";\n\n/**\n * A hand-rolled, dependency-free typeahead prompt for interactive mode. It owns\n * the terminal's raw-mode input and renders a block pinned to the bottom of the\n * screen: a scrollable list of suggestions above a `> query` input line. Run\n * output is meant to scroll *above* this block — {@link Prompt.printAbove} erases\n * the block, lets a caller write, then redraws it lower down.\n *\n * The prompt is pure view + input: it filters/ranks {@link Suggestion}s by the\n * typed query and reports intent through callbacks ({@link PromptHandlers}), but\n * holds no run/watch logic. The orchestrator wires those callbacks.\n */\nexport interface Suggestion {\n /** Human label shown after the badge, e.g. a scenario name. */\n label: string;\n /** Short kind marker shown before the label, e.g. `#tag` / `feature`. */\n badge: string;\n /** Text the query is matched against (case-insensitively). */\n search: string;\n /** Opaque payload handed back to {@link PromptHandlers.onSubmit}. */\n value: unknown;\n}\n\nexport interface PromptHandlers {\n /** Enter: `value` is the highlighted suggestion, or `null` when none matches. */\n onSubmit(value: unknown | null, query: string): void;\n /** The query text changed (insert/delete) — used to suspend auto-runs. */\n onEdit(query: string): void;\n /** Escape pressed. */\n onEscape(): void;\n /** Ctrl-C / Ctrl-D — the caller should tear down and exit. */\n onQuit(): void;\n}\n\nexport interface PromptOptions {\n handlers: PromptHandlers;\n /** Input prefix. Default `\"› \"`. */\n prefix?: string;\n /** Max suggestion rows shown at once. Default `10`. */\n maxVisible?: number;\n /** Initial query text. */\n initialQuery?: string;\n}\n\n// --- ANSI ------------------------------------------------------------------\nconst useColor =\n !process.env.NO_COLOR && (process.stdout.isTTY ?? false) === true;\nconst wrap = (open: number, close: number) => (s: string) =>\n useColor ? `\\x1b[${open}m${s}\\x1b[${close}m` : s;\nconst color = {\n green: wrap(32, 39),\n cyan: wrap(36, 39),\n dim: wrap(2, 22),\n bold: wrap(1, 22),\n inverse: wrap(7, 27),\n};\n\n/** Ranked match: lower rank sorts first; `-1` means \"no match\" (excluded). */\nfunction rank(search: string, query: string): number {\n if (!query) return 0;\n const idx = search.indexOf(query);\n if (idx === -1) return -1;\n if (idx === 0) return 0; // prefix\n // word-boundary (space, `:`, `›`, `@`) scores above a bare substring.\n return /[\\s:›@/]/.test(search[idx - 1]) ? 1 : 2;\n}\n\nexport class Prompt {\n private readonly handlers: PromptHandlers;\n private readonly prefix: string;\n private readonly maxVisible: number;\n\n private all: Suggestion[] = [];\n private matches: Suggestion[] = [];\n private query: string;\n private cursor: number; // caret index within `query`\n private selected = 0; // index into `matches`\n private window = 0; // first visible match index\n\n /** Optional dim status line shown just above the input (set by the caller). */\n status = \"\";\n\n /** Lines the pinned block currently occupies, so a redraw can erase them. */\n private rendered = 0;\n private started = false;\n /** While true the block is hidden so a run's output can own the screen. */\n private suspended = false;\n private keyListener?: (str: string, key: readline.Key) => void;\n\n constructor(options: PromptOptions) {\n this.handlers = options.handlers;\n this.prefix = options.prefix ?? \"› \";\n this.maxVisible = options.maxVisible ?? 10;\n this.query = options.initialQuery ?? \"\";\n this.cursor = this.query.length;\n }\n\n /** Replace the suggestion pool (e.g. after the feature cache rebuilds). */\n setSuggestions(suggestions: Suggestion[]): void {\n this.all = suggestions;\n this.refilter();\n if (this.started) this.render();\n }\n\n get value(): string {\n return this.query;\n }\n\n /** Enter raw mode, attach the keypress listener, and draw the block. */\n start(): void {\n if (this.started) return;\n this.started = true;\n readline.emitKeypressEvents(process.stdin);\n if (process.stdin.isTTY) process.stdin.setRawMode(true);\n this.keyListener = (str, key) => this.onKey(str, key ?? {});\n process.stdin.on(\"keypress\", this.keyListener);\n process.stdin.resume();\n this.render();\n }\n\n /** Erase the block, restore cooked mode, and detach the listener. */\n stop(): void {\n if (!this.started) return;\n this.started = false;\n this.erase();\n write(\"\\x1b[?25h\"); // ensure the cursor is visible again\n if (this.keyListener) process.stdin.off(\"keypress\", this.keyListener);\n if (process.stdin.isTTY) process.stdin.setRawMode(false);\n process.stdin.pause();\n }\n\n /**\n * Erase the pinned block and hide it for the duration of an (async) run, so\n * everything written to stdout in the meantime scrolls where the prompt was.\n * Keypresses still update state but don't repaint until {@link endOutput}.\n */\n beginOutput(): void {\n if (!this.started) return;\n this.erase();\n this.suspended = true;\n }\n\n /** Redraw the pinned block below whatever output {@link beginOutput} let through. */\n endOutput(): void {\n if (!this.started) return;\n this.suspended = false;\n this.render();\n }\n\n /** Force a redraw (after mutating `status`, say). No-op while suspended. */\n redraw(): void {\n if (this.started && !this.suspended) this.render();\n }\n\n // --- input ---------------------------------------------------------------\n private onKey(str: string, key: readline.Key): void {\n if (key.ctrl && (key.name === \"c\" || key.name === \"d\")) {\n this.handlers.onQuit();\n return;\n }\n switch (key.name) {\n case \"return\":\n case \"enter\": {\n const value = this.matches[this.selected]?.value ?? null;\n this.handlers.onSubmit(value, this.query);\n return;\n }\n case \"escape\":\n this.handlers.onEscape();\n return;\n case \"up\":\n this.move(-1);\n return;\n case \"down\":\n this.move(1);\n return;\n case \"left\":\n this.cursor = Math.max(0, this.cursor - 1);\n this.render();\n return;\n case \"right\":\n this.cursor = Math.min(this.query.length, this.cursor + 1);\n this.render();\n return;\n case \"home\":\n this.cursor = 0;\n this.render();\n return;\n case \"end\":\n this.cursor = this.query.length;\n this.render();\n return;\n case \"backspace\":\n this.deleteBefore();\n return;\n case \"delete\":\n this.deleteAfter();\n return;\n }\n // Any single printable character (space included) is inserted.\n if (str && str.length === 1 && str >= \" \" && !key.ctrl && !key.meta) {\n this.insert(str);\n }\n }\n\n private insert(ch: string): void {\n this.query =\n this.query.slice(0, this.cursor) + ch + this.query.slice(this.cursor);\n this.cursor += ch.length;\n this.afterEdit();\n }\n\n private deleteBefore(): void {\n if (this.cursor === 0) return;\n this.query =\n this.query.slice(0, this.cursor - 1) + this.query.slice(this.cursor);\n this.cursor--;\n this.afterEdit();\n }\n\n private deleteAfter(): void {\n if (this.cursor >= this.query.length) return;\n this.query =\n this.query.slice(0, this.cursor) + this.query.slice(this.cursor + 1);\n this.afterEdit();\n }\n\n private afterEdit(): void {\n this.refilter();\n this.render();\n this.handlers.onEdit(this.query);\n }\n\n private move(delta: number): void {\n if (this.matches.length === 0) return;\n this.selected = Math.min(\n this.matches.length - 1,\n Math.max(0, this.selected + delta)\n );\n this.reWindow();\n this.render();\n }\n\n private refilter(): void {\n const q = this.query.trim().toLowerCase();\n this.matches = this.all\n .map((s, index) => ({ s, index, r: rank(s.search, q) }))\n .filter(m => m.r !== -1)\n .sort((a, b) => a.r - b.r || a.index - b.index)\n .map(m => m.s);\n this.selected = 0;\n this.window = 0;\n }\n\n private reWindow(): void {\n if (this.selected < this.window) this.window = this.selected;\n else if (this.selected >= this.window + this.maxVisible)\n this.window = this.selected - this.maxVisible + 1;\n }\n\n // --- rendering -----------------------------------------------------------\n /** Build the block's lines, top (suggestions) to bottom (input). */\n private buildLines(): string[] {\n const lines: string[] = [];\n const total = this.matches.length;\n\n if (this.query.trim() && total === 0) {\n lines.push(color.dim(\" no matching tag, feature, or scenario\"));\n }\n\n const end = Math.min(this.window + this.maxVisible, total);\n for (let i = this.window; i < end; i++) {\n const s = this.matches[i];\n const active = i === this.selected;\n const badge = color.cyan(s.badge.padEnd(9));\n const row = `${badge} ${s.label}`;\n lines.push(active ? color.inverse(`❯ ${row}`) : ` ${row}`);\n }\n if (total > end) {\n lines.push(color.dim(` …and ${total - end} more`));\n }\n\n if (this.status) lines.push(color.dim(this.status));\n lines.push(`${color.bold(this.prefix)}${this.query}`);\n return lines;\n }\n\n private render(): void {\n if (this.suspended) return; // a run owns the screen; don't repaint over it\n write(\"\\x1b[?25l\"); // hide caret while we repaint\n this.moveToBlockStart();\n write(\"\\x1b[0J\"); // clear from here to end of screen\n const lines = this.buildLines();\n write(lines.join(\"\\r\\n\"));\n this.rendered = lines.length;\n // Park the caret on the input line at the right column.\n write(`\\r\\x1b[${stringWidth(this.prefix) + this.cursor}C`);\n write(\"\\x1b[?25h\");\n }\n\n /** Erase the block and leave the caret at the block's top-left. */\n private erase(): void {\n this.moveToBlockStart();\n write(\"\\x1b[0J\");\n this.rendered = 0;\n }\n\n /** Move the caret to column 0 of the block's first line. */\n private moveToBlockStart(): void {\n if (this.rendered > 1) write(`\\x1b[${this.rendered - 1}A`);\n write(\"\\r\");\n }\n}\n\n/** Visible width, ignoring the ANSI escapes our color helpers may inject. */\nfunction stringWidth(s: string): number {\n // eslint-disable-next-line no-control-regex\n return s.replace(/\\x1b\\[[0-9;]*m/g, \"\").length;\n}\n\nfunction write(s: string): void {\n process.stdout.write(s);\n}\n","import * as fs from \"node:fs\";\nimport * as path from \"node:path\";\n\n/** Glob metacharacters — the same set {@link globFiles} treats as \"magic\". */\nconst MAGIC = /[*?[\\]{}!()]/;\n\n/** File extensions worth reacting to: features and step/world modules. */\nconst WATCHED_EXT = /\\.(feature|ts|mts|js|mjs|cts|cjs)$/;\n\nexport interface Watcher {\n /** Stop watching and release every underlying `fs.watch` handle. */\n close(): void;\n}\n\n/**\n * Watch the directories implied by feature/step globs and invoke `onChange`\n * (debounced, coalesced) whenever a relevant file changes. Roots are the literal\n * directory prefixes of each glob — e.g. `features/**\\/*.feature` watches\n * `features/` recursively — deduped so nested roots aren't watched twice.\n *\n * Recursive watching is supported on macOS and Windows and on modern Linux\n * (Node ≥ 20 / Bun); on older Linux only the top level of each root is seen.\n */\nexport function watchFeatures(\n globs: string[],\n cwd: string,\n onChange: () => void,\n debounceMs = 120\n): Watcher {\n const roots = watchRoots(globs, cwd);\n const watchers: fs.FSWatcher[] = [];\n let timer: ReturnType<typeof setTimeout> | undefined;\n\n const schedule = (filename: string | null) => {\n // `fs.watch` may report a null filename; when we do get one, ignore churn\n // from unrelated files (editor swap files, `.git`, build output).\n if (filename && !WATCHED_EXT.test(filename)) return;\n if (timer) clearTimeout(timer);\n timer = setTimeout(onChange, debounceMs);\n };\n\n for (const root of roots) {\n try {\n const w = fs.watch(root, { recursive: true }, (_event, filename) =>\n schedule(filename)\n );\n w.on(\"error\", () => {}); // a removed/renamed root shouldn't crash the loop\n watchers.push(w);\n } catch {\n // Root doesn't exist yet (or can't be watched) — skip it silently.\n }\n }\n\n return {\n close() {\n if (timer) clearTimeout(timer);\n for (const w of watchers) w.close();\n },\n };\n}\n\n/**\n * The set of directories to watch: each glob's literal prefix (the path up to\n * its first magic character), resolved against `cwd`, deduped, with any root\n * that nests inside another dropped. Falls back to `cwd` when nothing resolves.\n */\nexport function watchRoots(globs: string[], cwd: string): string[] {\n const dirs = new Set<string>();\n for (const glob of globs) {\n dirs.add(path.resolve(cwd, literalPrefix(glob)));\n }\n const roots = [...dirs].sort();\n // Drop any root contained in an earlier (shorter) one, so recursive watches\n // don't overlap.\n const pruned = roots.filter(\n (r, i) => !roots.some((other, j) => j < i && isInside(r, other))\n );\n return pruned.length ? pruned : [cwd];\n}\n\n/** The directory portion of a glob before its first magic segment. */\nfunction literalPrefix(glob: string): string {\n const segments = glob.split(\"/\");\n const literal: string[] = [];\n for (const seg of segments) {\n if (MAGIC.test(seg)) break;\n literal.push(seg);\n }\n const joined = literal.join(\"/\");\n // A bare `*.feature` has no literal prefix → watch cwd (\".\").\n if (!joined) return \".\";\n // If the whole pattern was literal it points at a file; watch its directory.\n return MAGIC.test(glob) ? joined : path.dirname(joined);\n}\n\n/** True when `child` is `parent` itself or nested beneath it. */\nfunction isInside(child: string, parent: string): boolean {\n if (child === parent) return true;\n const rel = path.relative(parent, child);\n return !!rel && !rel.startsWith(\"..\") && !path.isAbsolute(rel);\n}\n","import { spawn } from \"node:child_process\";\nimport * as path from \"node:path\";\nimport { ParsedScenario } from \"../analyzer/types\";\nimport { ParsedFeature, parseFeatureCatalog } from \"../analyzer/gherkinParser\";\nimport { globFiles } from \"../globFiles\";\nimport { ResolvedConfig } from \"./config\";\nimport { Prompt, Suggestion } from \"./prompt\";\nimport { watchFeatures, Watcher } from \"./watcher\";\n\n/**\n * A population the user can pick and run: a whole tag, a whole feature file, a\n * single scenario, or a whole scenario outline (all its example rows). Scenarios\n * and outlines are keyed by name — an outline is one choice that runs every row —\n * so selection is stable across edits that shift line numbers. Resolved against\n * the freshly-parsed features on every run.\n */\ntype Choice =\n | { kind: \"tag\"; tag: string }\n | { kind: \"feature\"; file: string; name: string }\n | { kind: \"scenario\"; file: string; name: string };\n\nconst useColor =\n !process.env.NO_COLOR && (process.stdout.isTTY ?? false) === true;\nconst bold = (s: string) => (useColor ? `\\x1b[1m${s}\\x1b[22m` : s);\nconst dim = (s: string) => (useColor ? `\\x1b[2m${s}\\x1b[22m` : s);\nconst red = (s: string) => (useColor ? `\\x1b[31m${s}\\x1b[39m` : s);\nconst yellow = (s: string) => (useColor ? `\\x1b[33m${s}\\x1b[39m` : s);\n\n/**\n * Entry point for `step-forge -i`. Brings up the always-live typeahead prompt,\n * watches the configured feature/step directories, and re-runs the armed\n * population on every relevant file change. Requires a TTY.\n */\nexport async function runInteractive(config: ResolvedConfig): Promise<void> {\n if (!process.stdin.isTTY) {\n process.stderr.write(\n \"Interactive mode (-i) requires a TTY; stdin is not a terminal.\\n\"\n );\n process.exitCode = 1;\n return;\n }\n await new InteractiveSession(config).start();\n}\n\nclass InteractiveSession {\n private readonly config: ResolvedConfig;\n private readonly prompt: Prompt;\n private watcher?: Watcher;\n private catalog: ParsedFeature[] = [];\n\n // State machine: `armed` is the committed population; `dirty` means the query\n // was edited since the last commit, which suspends file-change re-runs.\n private armed: Choice | null = null;\n private dirty = false;\n private running = false;\n private rerunQueued = false;\n /** Increments per run so back-to-back identical output is still distinguishable. */\n private runCount = 0;\n\n private done!: () => void;\n\n constructor(config: ResolvedConfig) {\n this.config = config;\n this.prompt = new Prompt({\n initialQuery: config.tags ?? config.name ?? \"\",\n handlers: {\n onSubmit: value => this.onSubmit(value),\n onEdit: () => this.onEdit(),\n onEscape: () => this.onEscape(),\n onQuit: () => this.onQuit(),\n },\n });\n }\n\n async start(): Promise<void> {\n await this.rebuildCatalog();\n this.prompt.status = this.status();\n this.prompt.start();\n this.watcher = watchFeatures(\n [...this.config.features, ...this.config.steps],\n this.config.cwd,\n () => void this.onFsChange()\n );\n await new Promise<void>(resolve => {\n this.done = resolve;\n });\n }\n\n // --- prompt events -------------------------------------------------------\n private onSubmit(value: unknown | null): void {\n if (!value) return; // nothing highlighted (empty query / no matches)\n this.armed = value as Choice;\n this.dirty = false;\n this.prompt.status = this.status();\n this.triggerRun();\n }\n\n private onEdit(): void {\n // Editing the query suspends auto-runs until the next Enter re-commits.\n if (this.armed) this.dirty = true;\n this.prompt.status = this.status();\n this.prompt.redraw();\n }\n\n private onEscape(): void {\n if (!this.armed) return;\n this.armed = null;\n this.dirty = false;\n this.prompt.status = this.status();\n this.prompt.redraw();\n }\n\n private onQuit(): void {\n this.watcher?.close();\n this.prompt.stop();\n process.stdout.write(\"\\n\");\n process.exitCode = 0;\n this.done?.();\n }\n\n // --- filesystem watch ----------------------------------------------------\n private async onFsChange(): Promise<void> {\n await this.rebuildCatalog();\n if (this.armed && !this.dirty) this.triggerRun();\n }\n\n private async rebuildCatalog(): Promise<void> {\n try {\n const featureFiles = await globFiles(\n this.config.features,\n this.config.cwd\n );\n this.catalog = parseFeatureCatalog(featureFiles);\n } catch {\n // A half-written feature mid-save may fail to parse; keep the old cache.\n }\n this.prompt.setSuggestions(buildSuggestions(this.catalog));\n }\n\n // --- run cycle -----------------------------------------------------------\n private triggerRun(): void {\n if (!this.armed) return;\n if (this.running) {\n this.rerunQueued = true; // coalesce: run once more when the current ends\n return;\n }\n this.running = true;\n this.prompt.beginOutput();\n void this.runLoop();\n }\n\n private async runLoop(): Promise<void> {\n try {\n do {\n this.rerunQueued = false;\n if (this.armed) await this.executeOnce(this.armed);\n } while (this.rerunQueued && this.armed && !this.dirty);\n } catch (err) {\n process.stdout.write(\n red(`\\n run failed: ${err instanceof Error ? err.message : err}\\n`)\n );\n } finally {\n this.running = false;\n this.prompt.status = this.status();\n this.prompt.endOutput(); // redraws the prompt below the run's output\n }\n }\n\n /**\n * One run of `choice`: resolve its scenarios against the current parse, then\n * shell out to a **fresh** `step-forge` process scoped to just that\n * population. A child process is used deliberately — Bun caches ES modules for\n * the life of a process and ignores query-string cache-busting, so re-running\n * in-process would never pick up edited step code. A new process re-reads\n * every step/feature file, which is exactly what a watch loop needs.\n */\n private async executeOnce(choice: Choice): Promise<void> {\n const selected = resolveScenarios(choice, this.scenarios());\n\n const count = `${selected.length} scenario${selected.length === 1 ? \"\" : \"s\"}`;\n process.stdout.write(\n `\\n${bold(`▶ ${choiceLabel(choice)}`)} ${dim(\n `(${count} · run #${++this.runCount} · ${clock()})`\n )}\\n\\n`\n );\n\n if (selected.length === 0) {\n process.stdout.write(yellow(\" no scenarios match this selection\\n\"));\n return;\n }\n\n // A single scenario is analyzed (dependency/undefined/ambiguous checks) and\n // run verbose; a broader population uses the configured reporter.\n const single = selected.length === 1;\n if (single) await this.analyzeScenario(selected[0]);\n\n await this.spawnRun(runArgs(choice, single, this.config));\n }\n\n /** Every scenario across the current catalog, flattened. */\n private scenarios(): ParsedScenario[] {\n return this.catalog.flatMap(f => f.scenarios);\n }\n\n /**\n * Run `step-forge` as a child process with `args`, streaming its output to the\n * terminal (where the erased prompt was). Re-invokes the very CLI that's\n * running us (`process.execPath` + `argv[1]`), so it works identically from\n * the source tree and the built bin. Never rejects — a spawn error is reported\n * and swallowed so the watch loop survives.\n */\n private spawnRun(args: string[]): Promise<void> {\n return new Promise(resolve => {\n const child = spawn(process.execPath, [process.argv[1], ...args], {\n cwd: this.config.cwd,\n // Own stdin ourselves (raw-mode prompt); let the child inherit our\n // stdout/stderr so its reporter output lands above the prompt live.\n stdio: [\"ignore\", \"inherit\", \"inherit\"],\n });\n child.on(\"error\", err => {\n process.stdout.write(red(` could not start runner: ${err.message}\\n`));\n resolve();\n });\n child.on(\"close\", () => resolve());\n });\n }\n\n /**\n * Run the static analyzer over a single scenario and print any dependency /\n * undefined / ambiguous diagnostics. The analyzer needs the optional\n * `typescript` peer for AST extraction; if it's absent we note that and skip,\n * never failing the run.\n */\n private async analyzeScenario(scenario: ParsedScenario): Promise<void> {\n let analyzer: typeof import(\"../analyzer/index\");\n try {\n analyzer = await import(\"../analyzer/index\");\n } catch {\n process.stdout.write(\n dim(\" analysis skipped: install `typescript` to enable it\\n\\n\")\n );\n return;\n }\n try {\n const stepFiles = await globFiles(this.config.steps, this.config.cwd);\n const defs = analyzer.extractStepDefinitions(stepFiles);\n const matched = analyzer.matchScenarioSteps(scenario, defs);\n const diagnostics = analyzer.defaultRules.flatMap(rule =>\n rule.check(scenario, matched)\n );\n printDiagnostics(diagnostics, this.config.cwd);\n } catch (err) {\n process.stdout.write(\n dim(\n ` analysis unavailable: ${err instanceof Error ? err.message : err}\\n\\n`\n )\n );\n }\n }\n\n // --- status line ---------------------------------------------------------\n private status(): string {\n if (!this.armed) {\n return \" type to filter · ↑↓ move · enter run · ctrl-c quit\";\n }\n if (this.dirty) {\n return \" ⏸ suspended (query edited) · enter runs the selection · esc cancel\";\n }\n return ` ▶ watching ${choiceLabel(this.armed)} · save a file to re-run · enter forces · esc change · ctrl-c quit`;\n }\n}\n\n// --- choices ---------------------------------------------------------------\n/** Build the typeahead pool: every tag, feature, and scenario in the catalog. */\nfunction buildSuggestions(catalog: ParsedFeature[]): Suggestion[] {\n const suggestions: Suggestion[] = [];\n\n const tags = new Set<string>();\n for (const feature of catalog) {\n for (const scenario of feature.scenarios) {\n for (const tag of scenario.tags) tags.add(tag);\n }\n }\n for (const tag of [...tags].sort()) {\n suggestions.push({\n badge: \"#tag\",\n label: tag,\n search: tag.toLowerCase(),\n value: { kind: \"tag\", tag } satisfies Choice,\n });\n }\n\n for (const feature of catalog) {\n const featureLabel = feature.name || path.basename(feature.file);\n suggestions.push({\n badge: \"feature\",\n label: featureLabel,\n search: `${feature.name} ${feature.file}`.toLowerCase(),\n value: {\n kind: \"feature\",\n file: feature.file,\n name: feature.name,\n } satisfies Choice,\n });\n\n // Each row of a scenario outline shares the outline's name; collapse them\n // into a single choice (selected by the outline name, which runs every row)\n // rather than one entry per row. Regular scenarios stay one entry each.\n const seen = new Set<string>();\n for (const scenario of feature.scenarios) {\n const name = scenario.outline ? scenario.outline.name : scenario.name;\n if (seen.has(name)) continue;\n seen.add(name);\n suggestions.push({\n badge: scenario.outline ? \"outline\" : \"scenario\",\n label: name,\n search: `${featureLabel} ${name}`.toLowerCase(),\n value: { kind: \"scenario\", file: scenario.file, name } satisfies Choice,\n });\n }\n }\n\n return suggestions;\n}\n\n/** Resolve a choice to its scenarios against the current parse. */\nfunction resolveScenarios(\n choice: Choice,\n scenarios: ParsedScenario[]\n): ParsedScenario[] {\n switch (choice.kind) {\n case \"tag\":\n return scenarios.filter(s => s.tags.includes(choice.tag));\n case \"feature\":\n return scenarios.filter(s => s.file === choice.file);\n case \"scenario\":\n // Matches a regular scenario by its name, or every row of an outline by\n // the shared outline name — mirroring the runner's `-n` (name || outline).\n return scenarios.filter(\n s =>\n s.file === choice.file &&\n (s.name === choice.name || s.outline?.name === choice.name)\n );\n }\n}\n\nfunction choiceLabel(choice: Choice): string {\n switch (choice.kind) {\n case \"tag\":\n return choice.tag;\n case \"feature\":\n return choice.name || path.basename(choice.file);\n case \"scenario\":\n return choice.name;\n }\n}\n\n/**\n * The `step-forge` argv that reproduces this selection in a child process. The\n * base flags forward the resolved config (steps/world/concurrency) so the child\n * matches the parent's setup regardless of its own config file; the scope flags\n * narrow to the chosen population:\n * - tag → all configured features, filtered by `-t <tag>`\n * - feature → that single feature file\n * - scenario → that feature file, name-anchored with `-n \"/^…$/\"` (the name is\n * the outline name for an outline, so all its rows run)\n * A single scenario runs verbose; a population uses the configured reporter.\n */\nfunction runArgs(\n choice: Choice,\n single: boolean,\n config: ResolvedConfig\n): string[] {\n const args: string[] = [];\n for (const glob of config.steps) args.push(\"-s\", glob);\n if (config.world) args.push(\"-w\", config.world);\n args.push(\"-c\", String(config.concurrency));\n\n switch (choice.kind) {\n case \"tag\":\n args.push(...config.features, \"-t\", choice.tag);\n break;\n case \"feature\":\n args.push(choice.file);\n break;\n case \"scenario\":\n args.push(choice.file, \"-n\", `/^${escapeRegExp(choice.name)}$/`);\n break;\n }\n\n if (single) {\n args.push(\"-v\");\n } else {\n args.push(\"-r\", config.reporter);\n if (config.verbose) args.push(\"-v\");\n }\n return args;\n}\n\n/** Escape a scenario name for use inside an anchored `-n` regex. */\nfunction escapeRegExp(text: string): string {\n return text.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n}\n\n/** Local wall-clock time as `HH:MM:SS`, so each run's header is dated. */\nfunction clock(): string {\n const d = new Date();\n return [d.getHours(), d.getMinutes(), d.getSeconds()]\n .map(n => String(n).padStart(2, \"0\"))\n .join(\":\");\n}\n\n// --- diagnostics -----------------------------------------------------------\nfunction printDiagnostics(\n diagnostics: import(\"../analyzer/types\").Diagnostic[],\n cwd: string\n): void {\n if (diagnostics.length === 0) return; // clean scenario: say nothing, let the run speak\n process.stdout.write(bold(\" analyzer:\\n\"));\n for (const d of diagnostics) {\n const mark =\n d.severity === \"error\"\n ? red(\"✗\")\n : d.severity === \"warning\"\n ? yellow(\"!\")\n : dim(\"i\");\n const loc = `${path.relative(cwd, d.file)}:${d.range.startLine}`;\n process.stdout.write(` ${mark} ${d.message} ${dim(`(${loc})`)}\\n`);\n }\n process.stdout.write(\"\\n\");\n}\n","#!/usr/bin/env bun\nimport { parseArgs } from \"node:util\";\nimport * as path from \"node:path\";\nimport { loadConfigFile, resolveConfig, RunnerOptions } from \"./config\";\nimport { run } from \"./runner\";\nimport { runInteractive } from \"./interactive\";\n\nconst HELP = `step-forge — native TypeScript runner for Gherkin step definitions\n\nUsage:\n step-forge [options] [feature globs...]\n\nOptions:\n -t, --tags <expr> Tag expression, e.g. \"@smoke and not @wip\"\n -n, --name <pattern> Only scenarios whose name matches (substring or /regex/)\n -s, --steps <glob> Step-definition module glob (repeatable)\n -w, --world <module> World factory module (default export () => world)\n -c, --concurrency <n> Max scenarios in flight (default: 1, i.e. serial)\n -r, --reporter <name> \"pretty\" (default) or \"progress\"\n -v, --verbose Report every scenario, not just failures\n -i, --interactive Interactive watch mode: pick a tag/feature/scenario\n and re-run it on every file change\n --config <path> Config file directory (default: cwd)\n -h, --help Show this help\n\nPositional arguments are feature globs and override the configured features.\n`;\n\n/** Parse argv into config overrides. Positional args become feature globs. */\nfunction parseCli(argv: string[]): {\n cwd: string;\n overrides: RunnerOptions;\n interactive: boolean;\n} {\n const { values, positionals } = parseArgs({\n args: argv,\n allowPositionals: true,\n options: {\n tags: { type: \"string\", short: \"t\" },\n name: { type: \"string\", short: \"n\" },\n steps: { type: \"string\", short: \"s\", multiple: true },\n world: { type: \"string\", short: \"w\" },\n concurrency: { type: \"string\", short: \"c\" },\n reporter: { type: \"string\", short: \"r\" },\n verbose: { type: \"boolean\", short: \"v\" },\n interactive: { type: \"boolean\", short: \"i\" },\n config: { type: \"string\" },\n help: { type: \"boolean\", short: \"h\" },\n },\n });\n\n if (values.help) {\n process.stdout.write(HELP);\n process.exit(0);\n }\n\n const overrides: RunnerOptions = {};\n if (positionals.length) overrides.features = positionals;\n if (values.tags) overrides.tags = values.tags;\n if (values.name) overrides.name = values.name;\n if (values.steps) overrides.steps = values.steps;\n if (values.world) overrides.world = values.world;\n if (values.verbose) overrides.verbose = true;\n if (values.reporter) {\n if (values.reporter !== \"pretty\" && values.reporter !== \"progress\") {\n throw new Error(`Unknown reporter: ${values.reporter}`);\n }\n overrides.reporter = values.reporter;\n }\n if (values.concurrency !== undefined) {\n const n = Number(values.concurrency);\n if (!Number.isInteger(n) || n < 1) {\n throw new Error(`--concurrency must be a positive integer`);\n }\n overrides.concurrency = n;\n }\n\n const cwd = values.config\n ? path.resolve(process.cwd(), values.config)\n : process.cwd();\n return { cwd, overrides, interactive: values.interactive ?? false };\n}\n\nasync function main(): Promise<void> {\n const { cwd, overrides, interactive } = parseCli(process.argv.slice(2));\n const fileConfig = await loadConfigFile(cwd);\n const config = resolveConfig(cwd, fileConfig, overrides);\n if (interactive) {\n await runInteractive(config);\n return; // interactive mode manages its own lifecycle and exit code\n }\n const { passed } = await run(config);\n process.exitCode = passed ? 0 : 1;\n}\n\nmain().catch(err => {\n process.stderr.write(`${err instanceof Error ? err.stack : String(err)}\\n`);\n process.exitCode = 1;\n});\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"cli.cjs","names":["path","useColor","wrap","userFrames","relativeFrame","relativeLocation","write","BasicWorld","path","globFiles","compileRegistry","globalRegistry","parseFeatureFiles","runHooksParallel","globalHookRegistry","runHooks","runScenario","useColor","fs","path","globFiles","parseFeatureCatalog","path","path"],"sources":["../../src/runtime/config.ts","../../src/runtime/filter.ts","../../src/runtime/reporters.ts","../../src/runtime/runner.ts","../../src/runtime/prompt.ts","../../src/runtime/watcher.ts","../../src/runtime/interactive.ts","../../src/runtime/cli.ts"],"sourcesContent":["import * as path from \"node:path\";\nimport { pathToFileURL } from \"node:url\";\nimport { access } from \"node:fs/promises\";\n\n/**\n * User-facing runner configuration. The same shape is accepted from a\n * `step-forge.config.ts` file (default export) and from CLI flags, with flags\n * taking precedence. Everything is optional; {@link resolveConfig} fills in\n * defaults.\n */\nexport interface RunnerOptions {\n /** Feature-file glob(s), relative to `cwd`. Defaults to every `.feature` file. */\n features?: string | string[];\n /** Step-module glob(s), relative to `cwd`. Defaults to every `.steps.ts` file. */\n steps?: string | string[];\n /**\n * Module that default-exports a world factory `() => world`, relative to\n * `cwd`. When omitted each scenario gets a fresh `BasicWorld`.\n */\n world?: string;\n /**\n * Max scenarios in flight at once. Defaults to `1` (serial), matching\n * Cucumber's default execution model — scenarios often share module-level\n * state via hooks, which only holds under serial execution. Raise it to opt\n * into parallelism for isolated, I/O-bound suites.\n */\n concurrency?: number;\n /** Reporter name. Default `pretty`. */\n reporter?: \"pretty\" | \"progress\";\n /**\n * Verbose output: report every scenario (pass, fail, skip) instead of only\n * failures. Passed to whichever reporter is active. Default `false`.\n */\n verbose?: boolean;\n /** Only run scenarios whose name matches this (string → substring/regex). */\n name?: string;\n /** Cucumber tag expression, e.g. `@smoke and not @wip`. */\n tags?: string;\n}\n\n/** Fully-resolved config: no optionals, globs kept as arrays, paths absolute. */\nexport interface ResolvedConfig {\n cwd: string;\n features: string[];\n steps: string[];\n world?: string;\n concurrency: number;\n reporter: \"pretty\" | \"progress\";\n verbose: boolean;\n name?: string;\n tags?: string;\n}\n\nconst DEFAULT_FEATURES = \"**/*.feature\";\nconst DEFAULT_STEPS = \"**/*.steps.ts\";\nconst CONFIG_BASENAMES = [\n \"step-forge.config.ts\",\n \"step-forge.config.mts\",\n \"step-forge.config.js\",\n \"step-forge.config.mjs\",\n];\n\nfunction toArray<T>(value: T | T[] | undefined): T[] {\n if (value === undefined) return [];\n return Array.isArray(value) ? value : [value];\n}\n\nasync function exists(p: string): Promise<boolean> {\n try {\n await access(p);\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Locate and import a `step-forge.config.*` file from `cwd`, returning its\n * default export (or `{}` if none is present). Imported by absolute file URL so\n * Bun transpiles the TypeScript config natively.\n */\nexport async function loadConfigFile(cwd: string): Promise<RunnerOptions> {\n for (const basename of CONFIG_BASENAMES) {\n const candidate = path.join(cwd, basename);\n if (!(await exists(candidate))) continue;\n const mod = await import(pathToFileURL(candidate).href);\n return (mod.default ?? mod) as RunnerOptions;\n }\n return {};\n}\n\n/**\n * Merge file config with CLI overrides and apply defaults. CLI overrides win\n * field-by-field; array globs are normalised to arrays and default when neither\n * source provides them.\n */\nexport function resolveConfig(\n cwd: string,\n file: RunnerOptions,\n cli: RunnerOptions\n): ResolvedConfig {\n const pick = <K extends keyof RunnerOptions>(key: K): RunnerOptions[K] =>\n cli[key] ?? file[key];\n\n const features = toArray(pick(\"features\"));\n const steps = toArray(pick(\"steps\"));\n\n return {\n cwd,\n features: features.length ? features : [DEFAULT_FEATURES],\n steps: steps.length ? steps : [DEFAULT_STEPS],\n world: pick(\"world\"),\n concurrency: pick(\"concurrency\") ?? 1,\n reporter: pick(\"reporter\") ?? \"pretty\",\n verbose: pick(\"verbose\") ?? false,\n name: pick(\"name\"),\n tags: pick(\"tags\"),\n };\n}\n","import { ParsedScenario } from \"../analyzer/types\";\n\n/**\n * A predicate over a scenario's tags, compiled from a Cucumber tag expression.\n * Supports `and`, `or`, `not`, parentheses, and bare `@tag` atoms — the common\n * subset of Cucumber's tag-expression language. Kept dependency-free; swap for\n * `@cucumber/tag-expressions` if fuller parity is needed.\n */\nexport type TagPredicate = (tags: readonly string[]) => boolean;\n\n/** Tokenise a tag expression into atoms, operators, and parentheses. */\nfunction tokenize(expr: string): string[] {\n const tokens: string[] = [];\n const re = /\\s*(\\(|\\)|@[^\\s()]+|\\band\\b|\\bor\\b|\\bnot\\b)\\s*/gy;\n let index = 0;\n while (index < expr.length) {\n re.lastIndex = index;\n const m = re.exec(expr);\n if (!m || m.index !== index) {\n throw new Error(`Invalid tag expression near: ${expr.slice(index)}`);\n }\n tokens.push(m[1]);\n index = re.lastIndex;\n }\n return tokens;\n}\n\n/**\n * Compile a tag expression into a predicate via recursive descent\n * (or → and → not → primary). Throws on malformed input so a bad `--tags` flag\n * fails loudly rather than silently matching nothing.\n */\nexport function compileTagExpression(expr: string): TagPredicate {\n const tokens = tokenize(expr);\n let pos = 0;\n\n const peek = () => tokens[pos];\n const next = () => tokens[pos++];\n\n const parseOr = (): TagPredicate => {\n let left = parseAnd();\n while (peek() === \"or\") {\n next();\n const right = parseAnd();\n const l = left;\n left = tags => l(tags) || right(tags);\n }\n return left;\n };\n\n const parseAnd = (): TagPredicate => {\n let left = parseNot();\n while (peek() === \"and\") {\n next();\n const right = parseNot();\n const l = left;\n left = tags => l(tags) && right(tags);\n }\n return left;\n };\n\n const parseNot = (): TagPredicate => {\n if (peek() === \"not\") {\n next();\n const operand = parseNot();\n return tags => !operand(tags);\n }\n return parsePrimary();\n };\n\n const parsePrimary = (): TagPredicate => {\n const token = next();\n if (token === \"(\") {\n const inner = parseOr();\n if (next() !== \")\") throw new Error(\"Unbalanced parentheses in tags\");\n return inner;\n }\n if (token === undefined || !token.startsWith(\"@\")) {\n throw new Error(`Expected a tag, got: ${token ?? \"end of input\"}`);\n }\n return tags => tags.includes(token);\n };\n\n const predicate = parseOr();\n if (pos !== tokens.length) {\n throw new Error(`Unexpected token in tag expression: ${peek()}`);\n }\n return predicate;\n}\n\nexport interface SelectOptions {\n /** Substring or `/regex/flags` match against the scenario (or outline) name. */\n name?: string;\n /** Cucumber tag expression. */\n tags?: string;\n}\n\n/** Parse a `/pattern/flags` string into a RegExp, else treat it as a substring. */\nfunction toNameMatcher(name: string): (candidate: string) => boolean {\n const delim = /^\\/(.*)\\/([a-z]*)$/.exec(name);\n if (delim) {\n const re = new RegExp(delim[1], delim[2]);\n return candidate => re.test(candidate);\n }\n return candidate => candidate.includes(name);\n}\n\n/**\n * Narrow scenarios by name and tags, then apply `@only` focus. `@skip` is *not*\n * applied here — skipped scenarios stay in the set so the runner can report them\n * as skipped rather than silently dropping them. Returns scenarios in input\n * order.\n *\n * `@only` semantics mirror the Vitest plugin: if any surviving scenario is\n * tagged `@only` (and not `@skip`), the run is focused to just those.\n */\nexport function selectScenarios(\n scenarios: ParsedScenario[],\n options: SelectOptions\n): ParsedScenario[] {\n let selected = scenarios;\n\n if (options.name) {\n const matches = toNameMatcher(options.name);\n selected = selected.filter(\n s => matches(s.name) || (s.outline ? matches(s.outline.name) : false)\n );\n }\n\n if (options.tags) {\n const predicate = compileTagExpression(options.tags);\n selected = selected.filter(s => predicate(s.tags));\n }\n\n const focused = selected.filter(\n s => s.tags.includes(\"@only\") && !s.tags.includes(\"@skip\")\n );\n return focused.length ? focused : selected;\n}\n","import { relative } from \"node:path\";\nimport { relativeFrame, relativeLocation, userFrames } from \"../sourceLocation\";\nimport { ScenarioResult, StepResult } from \"./engine\";\n\n/**\n * A reporter observes the run. `onScenarioEnd` fires as each scenario finishes\n * (completion order, non-deterministic under concurrency); `onComplete` fires\n * once with every result for the end-of-run output.\n *\n * Every reporter accepts a `verbose` flag: without it a reporter reports only\n * failures (plus a summary); with it, it reports every scenario. A reporter may\n * render the same either way — `verbose` is a request, not a requirement.\n */\nexport interface Reporter {\n onScenarioEnd?(result: ScenarioResult): void;\n onComplete(results: ScenarioResult[], durationMs: number): void;\n}\n\nexport interface ReporterOptions {\n cwd?: string;\n verbose?: boolean;\n}\n\n/**\n * One NDJSON line emitted by {@link eventsReporter}. This is the internal\n * parent↔child channel for `step-forge -i`: the TUI spawns a child runner with\n * `--events` and renders the results region itself from these events, so it can\n * pin the layout and reorder it (stats above dots above failures). Not a public\n * or stable schema — it exists only for interactive mode.\n */\nexport type RunEvent =\n | {\n t: \"scenario\";\n status: \"passed\" | \"failed\" | \"skipped\";\n name: string;\n /** Per-scenario step counts, so the parent can tally steps live. */\n steps: { passed: number; failed: number; skipped: number };\n /** Rendered Cucumber failure block; present only when the scenario failed. */\n detail?: string;\n }\n | { t: \"complete\"; durationMs: number };\n\n// --- ANSI colouring -------------------------------------------------------\nconst useColor =\n !process.env.NO_COLOR &&\n (!!process.env.FORCE_COLOR || (process.stdout.isTTY ?? false) === true);\n\nconst wrap = (open: number, close: number) => (s: string) =>\n useColor ? `\\x1b[${open}m${s}\\x1b[${close}m` : s;\n\nconst c = {\n green: wrap(32, 39),\n red: wrap(31, 39),\n yellow: wrap(33, 39),\n dim: wrap(2, 22),\n bold: wrap(1, 22),\n};\n\nconst STATUS_MARK: Record<StepResult[\"status\"], string> = {\n passed: c.green(\"✓\"),\n failed: c.red(\"✗\"),\n skipped: c.yellow(\"-\"),\n};\n\n/** Display status of a whole scenario (skipped = every step skipped). */\nfunction scenarioStatus(\n result: ScenarioResult\n): \"passed\" | \"failed\" | \"skipped\" {\n if (result.status === \"failed\") return \"failed\";\n if (result.steps.every(s => s.status === \"skipped\")) return \"skipped\";\n return \"passed\";\n}\n\n/** The scenario's display name, prefixed with its outline name for outline rows. */\nfunction scenarioLabel(result: ScenarioResult): string {\n return result.scenario.outline\n ? `${result.scenario.outline.name} › ${result.scenario.name}`\n : result.scenario.name;\n}\n\n/** ✓ / ✗ / - for a whole scenario (skipped = every step skipped). */\nfunction scenarioMark(result: ScenarioResult): string {\n const status = scenarioStatus(result);\n if (status === \"failed\") return c.red(\"✗\");\n if (status === \"skipped\") return c.yellow(\"-\");\n return c.green(\"✓\");\n}\n\n/** Dot per scenario for the live heartbeat: `.` pass / `F` fail / `-` skip. */\nfunction scenarioDot(result: ScenarioResult): string {\n const status = scenarioStatus(result);\n if (status === \"failed\") return c.red(\"F\");\n if (status === \"skipped\") return c.yellow(\"-\");\n return c.green(\".\");\n}\n\n/** Indent every non-empty line of a block by `pad` spaces. */\nfunction indent(text: string, pad: number): string {\n const prefix = \" \".repeat(pad);\n return text\n .split(\"\\n\")\n .map(line => (line ? prefix + line : line))\n .join(\"\\n\");\n}\n\n/**\n * The error, trimmed to user frames: `Name: message` followed by only the\n * caller's own stack frames (library/engine and `node_modules` frames removed),\n * source-mapped by Bun to the original `.ts`. Falls back to just the message\n * when nothing user-owned is left (e.g. an undefined-step error).\n */\nfunction renderError(error: Error, cwd: string): string {\n const header = `${error.name}: ${error.message}`;\n const frames = userFrames(error.stack).map(\n f => ` at ${relativeFrame(f, cwd)}`\n );\n return frames.length ? `${header}\\n${frames.join(\"\\n\")}` : header;\n}\n\n/**\n * The detail block shown beneath a failing step: the `.feature` line, the step\n * definition location, and the trimmed error — the two coordinates that make a\n * failure easy to chase (where in the feature, which step definition).\n */\nfunction failureDetail(\n stepResult: StepResult,\n result: ScenarioResult,\n cwd: string\n): string {\n const lines = [\n `feature: ${relative(cwd, result.scenario.file)}:${stepResult.step.line}`,\n ];\n if (stepResult.source) {\n lines.push(`defined: ${relativeLocation(stepResult.source, cwd)}`);\n }\n if (stepResult.error) lines.push(renderError(stepResult.error, cwd));\n return indent(c.red(lines.join(\"\\n\")), 6);\n}\n\n/**\n * One scenario as a Cucumber-style block: a marked header with its `.feature`\n * location, then each step with its mark (and, in `verbose`, the step\n * definition location as a trailing comment). A failing step is followed by its\n * failure detail; a scenario-level error (a failing hook, no step to blame) is\n * appended at the end.\n */\nfunction renderScenario(\n result: ScenarioResult,\n cwd: string,\n opts: { stepSource: boolean }\n): string {\n const lines: string[] = [\n `${scenarioMark(result)} ${c.bold(scenarioLabel(result))}`,\n ];\n\n const loc = result.scenario.line\n ? `${relative(cwd, result.scenario.file)}:${result.scenario.line}`\n : relative(cwd, result.scenario.file);\n lines.push(indent(c.dim(loc), 4));\n lines.push(\"\");\n\n for (const step of result.scenario.steps) {\n const sr = result.steps.find(s => s.step === step);\n const status = sr?.status ?? \"skipped\";\n const comment =\n opts.stepSource && sr?.source\n ? c.dim(` # ${relativeLocation(sr.source, cwd)}`)\n : \"\";\n lines.push(\n ` ${STATUS_MARK[status]} ${c.dim(step.effectiveKeyword)} ${step.text}${comment}`\n );\n if (sr?.status === \"failed\") lines.push(failureDetail(sr, result, cwd));\n }\n\n // A hook failure has no failing step; surface its error at scenario level.\n const stepFailed = result.steps.some(s => s.status === \"failed\");\n if (result.error && !stepFailed) {\n lines.push(indent(c.red(renderError(result.error, cwd)), 4));\n }\n\n return lines.join(\"\\n\");\n}\n\n// --- Summary --------------------------------------------------------------\ninterface Totals {\n scenarios: { passed: number; failed: number; skipped: number };\n steps: { passed: number; failed: number; skipped: number };\n}\n\nfunction tally(results: ScenarioResult[]): Totals {\n const totals: Totals = {\n scenarios: { passed: 0, failed: 0, skipped: 0 },\n steps: { passed: 0, failed: 0, skipped: 0 },\n };\n for (const result of results) {\n if (result.status === \"failed\") totals.scenarios.failed++;\n else if (result.steps.every(s => s.status === \"skipped\"))\n totals.scenarios.skipped++;\n else totals.scenarios.passed++;\n for (const step of result.steps) totals.steps[step.status]++;\n }\n return totals;\n}\n\nfunction renderSummary(results: ScenarioResult[], durationMs: number): string {\n const t = tally(results);\n const scenarioTotal =\n t.scenarios.passed + t.scenarios.failed + t.scenarios.skipped;\n const stepTotal = t.steps.passed + t.steps.failed + t.steps.skipped;\n\n const part = (n: number, label: string, color: (s: string) => string) =>\n n > 0 ? color(`${n} ${label}`) : null;\n\n const line = (\n total: number,\n noun: string,\n counts: { passed: number; failed: number; skipped: number }\n ) => {\n const parts = [\n part(counts.passed, \"passed\", c.green),\n part(counts.failed, \"failed\", c.red),\n part(counts.skipped, \"skipped\", c.yellow),\n ].filter((x): x is string => x !== null);\n return `${total} ${noun}${total === 1 ? \"\" : \"s\"} (${parts.join(\", \")})`;\n };\n\n return [\n line(scenarioTotal, \"scenario\", t.scenarios),\n line(stepTotal, \"step\", t.steps),\n c.dim(`${(durationMs / 1000).toFixed(2)}s`),\n ].join(\"\\n\");\n}\n\nfunction write(text: string): void {\n process.stdout.write(text);\n}\n\n/** Feature-grouped tree of every scenario (used by `--verbose`). */\nfunction renderFullTree(results: ScenarioResult[], cwd: string): string {\n const groups = new Map<string, ScenarioResult[]>();\n for (const r of results) {\n const bucket = groups.get(r.scenario.file) ?? [];\n bucket.push(r);\n groups.set(r.scenario.file, bucket);\n }\n const out: string[] = [];\n for (const [file, group] of groups) {\n out.push(c.bold(`Feature: ${relative(cwd, file)}`), \"\");\n for (const r of group)\n out.push(indent(renderScenario(r, cwd, { stepSource: true }), 2), \"\");\n }\n return out.join(\"\\n\");\n}\n\n/** The failing scenarios only (used by the default, non-verbose output). */\nfunction renderFailures(results: ScenarioResult[], cwd: string): string {\n const failed = results.filter(r => r.status === \"failed\");\n if (!failed.length) return \"\";\n return (\n failed\n .map(r => renderScenario(r, cwd, { stepSource: false }))\n .join(\"\\n\\n\") + \"\\n\\n\"\n );\n}\n\n/**\n * Default reporter. Non-verbose: a dot per scenario as a heartbeat, then the\n * failing scenarios in Cucumber-style detail, then the summary. Verbose: the\n * full feature → scenario → step tree instead of just failures.\n */\nexport function prettyReporter(opts: ReporterOptions = {}): Reporter {\n const cwd = opts.cwd ?? process.cwd();\n const verbose = opts.verbose ?? false;\n return {\n onScenarioEnd(result) {\n if (!verbose) write(scenarioDot(result));\n },\n onComplete(results, durationMs) {\n const body = verbose\n ? `\\n${renderFullTree(results, cwd)}\\n`\n : `\\n\\n${renderFailures(results, cwd)}`;\n write(`${body}${renderSummary(results, durationMs)}\\n`);\n },\n };\n}\n\n/**\n * Compact reporter: a dot per scenario, then failures in detail and the\n * summary. Always minimal — `verbose` is accepted for interface parity but does\n * not expand the output (use `pretty --verbose` for the full tree).\n */\nexport function progressReporter(opts: ReporterOptions = {}): Reporter {\n const cwd = opts.cwd ?? process.cwd();\n return {\n onScenarioEnd(result) {\n write(scenarioDot(result));\n },\n onComplete(results, durationMs) {\n write(\n `\\n\\n${renderFailures(results, cwd)}${renderSummary(results, durationMs)}\\n`\n );\n },\n };\n}\n\nexport function makeReporter(\n name: \"pretty\" | \"progress\",\n opts: ReporterOptions = {}\n): Reporter {\n return name === \"progress\" ? progressReporter(opts) : prettyReporter(opts);\n}\n\n/**\n * Internal reporter for interactive mode. Emits one {@link RunEvent} per line\n * (NDJSON) and nothing human-facing, so a parent process can parse the stream\n * and own the rendering. Failure detail reuses {@link renderScenario} verbatim,\n * so the TUI's failures pane matches the non-interactive output.\n */\nexport function eventsReporter(opts: ReporterOptions = {}): Reporter {\n const cwd = opts.cwd ?? process.cwd();\n const emit = (evt: RunEvent): void => write(`${JSON.stringify(evt)}\\n`);\n return {\n onScenarioEnd(result) {\n const steps = { passed: 0, failed: 0, skipped: 0 };\n for (const s of result.steps) steps[s.status]++;\n const status = scenarioStatus(result);\n emit({\n t: \"scenario\",\n status,\n name: scenarioLabel(result),\n steps,\n detail:\n status === \"failed\"\n ? renderScenario(result, cwd, { stepSource: false })\n : undefined,\n });\n },\n onComplete(_results, durationMs) {\n emit({ t: \"complete\", durationMs });\n },\n };\n}\n","/* eslint-disable @typescript-eslint/no-explicit-any */\nimport * as path from \"node:path\";\nimport { pathToFileURL } from \"node:url\";\nimport { ParsedScenario } from \"../analyzer/types\";\nimport { parseFeatureFiles } from \"../analyzer/gherkinParser\";\nimport { globFiles } from \"../globFiles\";\nimport { BasicWorld, MergeableWorld } from \"../world\";\nimport {\n compileRegistry,\n CompiledStep,\n runScenario,\n ScenarioResult,\n} from \"./engine\";\nimport { globalHookRegistry, runHooks, runHooksParallel } from \"./hooks\";\nimport { globalRegistry } from \"./registry\";\nimport { selectScenarios } from \"./filter\";\nimport { makeReporter, Reporter } from \"./reporters\";\nimport { ResolvedConfig } from \"./config\";\n\ntype WorldFactory = () => MergeableWorld<any, any, any>;\n\n/**\n * Import every step-definition module so its `.step(...)` calls self-register\n * into the shared `globalRegistry`. Imported by file URL so Bun transpiles the\n * TypeScript natively.\n */\nasync function importSteps(files: string[]): Promise<void> {\n for (const file of files) {\n await import(pathToFileURL(file).href);\n }\n}\n\n/** Load the configured world factory, or default to a fresh `BasicWorld`. */\nasync function loadWorldFactory(\n world: string | undefined,\n cwd: string\n): Promise<WorldFactory> {\n if (!world) return () => new BasicWorld();\n const resolved = path.resolve(cwd, world);\n const mod = await import(pathToFileURL(resolved).href);\n const factory = mod.default ?? mod;\n if (typeof factory !== \"function\") {\n throw new Error(\n `World module ${world} must default-export a factory function () => world`\n );\n }\n return factory as WorldFactory;\n}\n\n/** A scenario carrying `@skip` never runs: report it with all steps skipped. */\nfunction skippedResult(scenario: ParsedScenario): ScenarioResult {\n return {\n scenario,\n status: \"passed\",\n steps: scenario.steps.map(step => ({ step, status: \"skipped\" as const })),\n };\n}\n\n/**\n * Run `items` through `worker`, at most `limit` in flight. Results come back in\n * input order even though completion order is not deterministic. A minimal\n * dependency-free pool — the whole point of the single-process model.\n */\nasync function runPool<T, R>(\n items: T[],\n limit: number,\n worker: (item: T, index: number) => Promise<R>\n): Promise<R[]> {\n const results = new Array<R>(items.length);\n let cursor = 0;\n const runNext = async (): Promise<void> => {\n while (cursor < items.length) {\n const index = cursor++;\n results[index] = await worker(items[index], index);\n }\n };\n const workers = Array.from(\n { length: Math.min(Math.max(1, limit), items.length) },\n runNext\n );\n await Promise.all(workers);\n return results;\n}\n\nexport interface RunResult {\n results: ScenarioResult[];\n passed: boolean;\n durationMs: number;\n}\n\n/**\n * Execute a whole run end to end: discover and import steps, load the world,\n * parse and select scenarios, then run them concurrently against a\n * compiled-once step table with global/feature hooks around the batch. Never\n * throws for test failures — inspect `RunResult.passed`.\n */\nexport async function run(\n config: ResolvedConfig,\n reporter: Reporter = makeReporter(config.reporter, {\n cwd: config.cwd,\n verbose: config.verbose,\n })\n): Promise<RunResult> {\n const start =\n typeof performance !== \"undefined\" ? performance.now() : Date.now();\n\n const [featureFiles, stepFiles] = await Promise.all([\n globFiles(config.features, config.cwd),\n globFiles(config.steps, config.cwd),\n ]);\n\n await importSteps(stepFiles);\n const makeWorld = await loadWorldFactory(config.world, config.cwd);\n\n const compiled: CompiledStep[] = compileRegistry(globalRegistry);\n const allScenarios = parseFeatureFiles(featureFiles);\n const selected = selectScenarios(allScenarios, {\n name: config.name,\n tags: config.tags,\n });\n\n // Global `beforeAll` hooks run once, in parallel, before any scenario starts;\n // feature before/after bracket the whole batch inside them. (Hooks aren't\n // file-scoped in the registry, so per-file bracketing would be meaningless\n // under concurrent execution.)\n await runHooksParallel(\"global\", \"before\", globalHookRegistry);\n await runHooks(\"feature\", \"before\", globalHookRegistry);\n\n const results = await runPool(\n selected,\n config.concurrency,\n async scenario => {\n const result = scenario.tags.includes(\"@skip\")\n ? skippedResult(scenario)\n : await runScenario(scenario, compiled, makeWorld, globalHookRegistry);\n reporter.onScenarioEnd?.(result);\n return result;\n }\n );\n\n await runHooks(\"feature\", \"after\", globalHookRegistry);\n // Global `afterAll` hooks run once, in parallel, after every scenario is done.\n await runHooksParallel(\"global\", \"after\", globalHookRegistry);\n\n const durationMs =\n (typeof performance !== \"undefined\" ? performance.now() : Date.now()) -\n start;\n reporter.onComplete(results, durationMs);\n\n return {\n results,\n passed: results.every(r => r.status !== \"failed\"),\n durationMs,\n };\n}\n","import * as readline from \"node:readline\";\n\n/**\n * A hand-rolled, dependency-free full-screen TUI for interactive mode. It owns\n * the terminal's raw-mode input and the **alternate screen buffer**, and paints\n * three stacked regions top-to-bottom:\n *\n * 1. prompt — the `› query` input line plus a status hint\n * 2. selection — the scrollable typeahead list of suggestions\n * 3. results — the current run, itself ordered stats → dots → failures\n *\n * The prompt/selection are pure view + input: they filter/rank {@link Suggestion}s\n * by the typed query and report intent through callbacks ({@link PromptHandlers}).\n * The results region is a small model the orchestrator drives as a run streams\n * ({@link Prompt.resetResults} / {@link Prompt.scenario} / {@link Prompt.complete}),\n * so stats can sit *above* the dots and the failures pane can scroll on its own.\n */\nexport interface Suggestion {\n /** Human label shown after the badge, e.g. a scenario name. */\n label: string;\n /** Short kind marker shown before the label, e.g. `#tag` / `feature`. */\n badge: string;\n /** Text the query is matched against (case-insensitively). */\n search: string;\n /** Opaque payload handed back to {@link PromptHandlers.onSubmit}. */\n value: unknown;\n}\n\nexport interface PromptHandlers {\n /** Enter: `value` is the highlighted suggestion, or `null` when none matches. */\n onSubmit(value: unknown | null, query: string): void;\n /** The query text changed (insert/delete) — used to suspend auto-runs. */\n onEdit(query: string): void;\n /** Escape pressed. */\n onEscape(): void;\n /** Ctrl-C / Ctrl-D — the caller should tear down and exit. */\n onQuit(): void;\n}\n\nexport interface PromptOptions {\n handlers: PromptHandlers;\n /** Input prefix. Default `\"› \"`. */\n prefix?: string;\n /** Max suggestion rows shown at once. Default `8`. */\n maxVisible?: number;\n /** Initial query text. */\n initialQuery?: string;\n}\n\n/** Header describing the run currently shown in the results region. */\nexport interface RunHeader {\n label: string;\n scenarioCount: number;\n runCount: number;\n clock: string;\n}\n\ninterface Counts {\n passed: number;\n failed: number;\n skipped: number;\n}\n\n/** Mutable model of the run currently rendered in the results region. */\ninterface ResultsState {\n header?: RunHeader;\n running: boolean;\n durationMs?: number;\n scenarios: Counts;\n steps: Counts;\n /** One rendered dot char per finished scenario (`.`/`F`/`-`). */\n dots: string[];\n /** Rendered Cucumber failure blocks (each multi-line). */\n failures: string[];\n /** Extra lines above the dots: analyzer diagnostics, errors, empty notices. */\n notes: string[];\n}\n\nfunction freshResults(): ResultsState {\n return {\n running: false,\n scenarios: { passed: 0, failed: 0, skipped: 0 },\n steps: { passed: 0, failed: 0, skipped: 0 },\n dots: [],\n failures: [],\n notes: [],\n };\n}\n\n// --- ANSI ------------------------------------------------------------------\nconst useColor =\n !process.env.NO_COLOR && (process.stdout.isTTY ?? false) === true;\nconst wrap = (open: number, close: number) => (s: string) =>\n useColor ? `\\x1b[${open}m${s}\\x1b[${close}m` : s;\nconst color = {\n green: wrap(32, 39),\n red: wrap(31, 39),\n yellow: wrap(33, 39),\n cyan: wrap(36, 39),\n dim: wrap(2, 22),\n bold: wrap(1, 22),\n inverse: wrap(7, 27),\n};\n\n/** Ranked match: lower rank sorts first; `-1` means \"no match\" (excluded). */\nfunction rank(search: string, query: string): number {\n if (!query) return 0;\n const idx = search.indexOf(query);\n if (idx === -1) return -1;\n if (idx === 0) return 0; // prefix\n // word-boundary (space, `:`, `›`, `@`) scores above a bare substring.\n return /[\\s:›@/]/.test(search[idx - 1]) ? 1 : 2;\n}\n\nexport class Prompt {\n private readonly handlers: PromptHandlers;\n private readonly prefix: string;\n private readonly maxVisible: number;\n\n private all: Suggestion[] = [];\n private matches: Suggestion[] = [];\n private query: string;\n private cursor: number; // caret index within `query`\n private selected = 0; // index into `matches`\n private window = 0; // first visible match index\n\n /** Optional dim status line shown just below the input (set by the caller). */\n status = \"\";\n\n private results = freshResults();\n private failScroll = 0; // top line offset of the failures pane\n\n private started = false;\n private keyListener?: (str: string, key: readline.Key) => void;\n private resizeListener?: () => void;\n private signalCleanup?: () => void;\n\n constructor(options: PromptOptions) {\n this.handlers = options.handlers;\n this.prefix = options.prefix ?? \"› \";\n this.maxVisible = options.maxVisible ?? 8;\n this.query = options.initialQuery ?? \"\";\n this.cursor = this.query.length;\n }\n\n /** Replace the suggestion pool (e.g. after the feature cache rebuilds). */\n setSuggestions(suggestions: Suggestion[]): void {\n this.all = suggestions;\n this.refilter();\n if (this.started) this.render();\n }\n\n get value(): string {\n return this.query;\n }\n\n // --- results model (driven by the orchestrator as a run streams) ---------\n /** Start a fresh run: reset counts/dots/failures and record its header. */\n resetResults(header: RunHeader): void {\n this.results = freshResults();\n this.results.header = header;\n this.results.running = true;\n this.failScroll = 0;\n this.render();\n }\n\n /** Append a note line above the dots (diagnostics, errors, empty notices). */\n note(line: string): void {\n this.results.notes.push(line);\n this.render();\n }\n\n /** Push a standalone block into the scrollable failures pane (e.g. a crash). */\n failureBlock(text: string): void {\n this.results.failures.push(text);\n this.render();\n }\n\n /** Record one finished scenario: tally it, add its dot, keep any failure block. */\n scenario(status: keyof Counts, steps: Counts, detail?: string): void {\n this.results.scenarios[status]++;\n this.results.steps.passed += steps.passed;\n this.results.steps.failed += steps.failed;\n this.results.steps.skipped += steps.skipped;\n this.results.dots.push(dotFor(status));\n if (detail) this.results.failures.push(detail);\n this.render();\n }\n\n /** Mark the run finished and record its wall-clock duration. */\n complete(durationMs: number): void {\n this.results.running = false;\n this.results.durationMs = durationMs;\n this.render();\n }\n\n // --- lifecycle -----------------------------------------------------------\n /** Enter the alternate screen + raw mode, attach listeners, and paint. */\n start(): void {\n if (this.started) return;\n this.started = true;\n readline.emitKeypressEvents(process.stdin);\n if (process.stdin.isTTY) process.stdin.setRawMode(true);\n write(\"\\x1b[?1049h\"); // switch to the alternate screen buffer\n this.keyListener = (str, key) => this.onKey(str, key ?? {});\n process.stdin.on(\"keypress\", this.keyListener);\n process.stdin.resume();\n this.resizeListener = () => this.render();\n process.stdout.on(\"resize\", this.resizeListener);\n this.installSafetyNet();\n this.render();\n }\n\n /** Restore the terminal: leave the alt buffer, cooked mode, cursor visible. */\n stop(): void {\n if (!this.started) return;\n this.started = false;\n if (this.keyListener) process.stdin.off(\"keypress\", this.keyListener);\n if (this.resizeListener) process.stdout.off(\"resize\", this.resizeListener);\n this.signalCleanup?.();\n this.restoreTerminal();\n process.stdin.pause();\n }\n\n /** Force a repaint (after mutating `status`, say). No-op until started. */\n redraw(): void {\n if (this.started) this.render();\n }\n\n /**\n * Leave raw mode and the alternate screen buffer and show the cursor.\n * Idempotent and synchronous so it is safe from an `exit`/signal handler —\n * a crash or `kill` must never strand the terminal in raw/alt-buffer mode.\n */\n private restoreTerminal(): void {\n if (process.stdin.isTTY) process.stdin.setRawMode(false);\n write(\"\\x1b[?25h\"); // show cursor\n write(\"\\x1b[?1049l\"); // leave the alternate screen buffer\n }\n\n /**\n * Register process-level handlers so the terminal is always restored, even on\n * `kill` (SIGTERM), a crash (`exit` fires with a non-zero code), or a stray\n * SIGINT. In raw mode Ctrl-C arrives as a keypress (handled by `onKey`), not a\n * signal, so these are a safety net rather than the normal quit path.\n */\n private installSafetyNet(): void {\n const onExit = (): void => this.restoreTerminal();\n const onSignal = (): void => {\n this.restoreTerminal();\n process.exit(130);\n };\n process.on(\"exit\", onExit);\n process.on(\"SIGINT\", onSignal);\n process.on(\"SIGTERM\", onSignal);\n this.signalCleanup = () => {\n process.off(\"exit\", onExit);\n process.off(\"SIGINT\", onSignal);\n process.off(\"SIGTERM\", onSignal);\n };\n }\n\n // --- input ---------------------------------------------------------------\n private onKey(str: string, key: readline.Key): void {\n if (key.ctrl && (key.name === \"c\" || key.name === \"d\")) {\n this.handlers.onQuit();\n return;\n }\n switch (key.name) {\n case \"return\":\n case \"enter\": {\n const value = this.matches[this.selected]?.value ?? null;\n this.handlers.onSubmit(value, this.query);\n return;\n }\n case \"escape\":\n this.handlers.onEscape();\n return;\n case \"up\":\n this.move(-1);\n return;\n case \"down\":\n this.move(1);\n return;\n case \"pageup\":\n this.scrollFailures(-1);\n return;\n case \"pagedown\":\n this.scrollFailures(1);\n return;\n case \"left\":\n this.cursor = Math.max(0, this.cursor - 1);\n this.render();\n return;\n case \"right\":\n this.cursor = Math.min(this.query.length, this.cursor + 1);\n this.render();\n return;\n case \"home\":\n this.cursor = 0;\n this.render();\n return;\n case \"end\":\n this.cursor = this.query.length;\n this.render();\n return;\n case \"backspace\":\n this.deleteBefore();\n return;\n case \"delete\":\n this.deleteAfter();\n return;\n }\n // Any single printable character (space included) is inserted.\n if (str && str.length === 1 && str >= \" \" && !key.ctrl && !key.meta) {\n this.insert(str);\n }\n }\n\n private insert(ch: string): void {\n this.query =\n this.query.slice(0, this.cursor) + ch + this.query.slice(this.cursor);\n this.cursor += ch.length;\n this.afterEdit();\n }\n\n private deleteBefore(): void {\n if (this.cursor === 0) return;\n this.query =\n this.query.slice(0, this.cursor - 1) + this.query.slice(this.cursor);\n this.cursor--;\n this.afterEdit();\n }\n\n private deleteAfter(): void {\n if (this.cursor >= this.query.length) return;\n this.query =\n this.query.slice(0, this.cursor) + this.query.slice(this.cursor + 1);\n this.afterEdit();\n }\n\n private afterEdit(): void {\n this.refilter();\n this.render();\n this.handlers.onEdit(this.query);\n }\n\n private move(delta: number): void {\n if (this.matches.length === 0) return;\n this.selected = Math.min(\n this.matches.length - 1,\n Math.max(0, this.selected + delta)\n );\n this.reWindow();\n this.render();\n }\n\n /** Scroll the failures pane by a page (clamped in {@link render}). */\n private scrollFailures(pages: number): void {\n this.failScroll = Math.max(0, this.failScroll + pages * this.failPage);\n this.render();\n }\n private failPage = 1; // last-rendered pane height, for page scrolling\n\n private refilter(): void {\n const q = this.query.trim().toLowerCase();\n this.matches = this.all\n .map((s, index) => ({ s, index, r: rank(s.search, q) }))\n .filter(m => m.r !== -1)\n .sort((a, b) => a.r - b.r || a.index - b.index)\n .map(m => m.s);\n this.selected = 0;\n this.window = 0;\n }\n\n private reWindow(): void {\n if (this.selected < this.window) this.window = this.selected;\n else if (this.selected >= this.window + this.maxVisible)\n this.window = this.selected - this.maxVisible + 1;\n }\n\n // --- rendering -----------------------------------------------------------\n private rows(): number {\n return process.stdout.rows && process.stdout.rows > 0\n ? process.stdout.rows\n : 24;\n }\n private cols(): number {\n return process.stdout.columns && process.stdout.columns > 0\n ? process.stdout.columns\n : 80;\n }\n\n /** Prompt + selection lines. The input line is always row 0. */\n private topLines(): string[] {\n const lines: string[] = [];\n lines.push(`${color.bold(this.prefix)}${this.query}`);\n if (this.status) lines.push(color.dim(this.status));\n lines.push(\"\");\n\n const total = this.matches.length;\n if (this.query.trim() && total === 0) {\n lines.push(color.dim(\" no matching tag, feature, or scenario\"));\n }\n const end = Math.min(this.window + this.maxVisible, total);\n for (let i = this.window; i < end; i++) {\n const s = this.matches[i];\n const active = i === this.selected;\n const row = `${color.cyan(s.badge.padEnd(9))} ${s.label}`;\n lines.push(active ? color.inverse(`❯ ${row}`) : ` ${row}`);\n }\n if (total > end) lines.push(color.dim(` …and ${total - end} more`));\n return lines;\n }\n\n /** Stats (header + summary) and the wrapped dots, above the failures pane. */\n private resultLines(cols: number): string[] {\n const lines: string[] = [\"\", divider(\"results\", cols)];\n const r = this.results;\n if (!r.header) {\n lines.push(\n color.dim(\" no run yet — press enter to run the highlighted selection\")\n );\n return lines;\n }\n\n // --- stats (top): header, live tallies, duration -----------------------\n const done = r.scenarios.passed + r.scenarios.failed + r.scenarios.skipped;\n lines.push(\n ` ${color.bold(`▶ ${r.header.label}`)} ${color.dim(\n `(run #${r.header.runCount} · ${r.header.clock})`\n )}`\n );\n const scenarioTotal = r.running\n ? `${done}/${r.header.scenarioCount}`\n : done;\n lines.push(\n ` ${countLine(String(scenarioTotal), \"scenario\", r.scenarios)}`\n );\n const stepTotal = r.steps.passed + r.steps.failed + r.steps.skipped;\n lines.push(` ${countLine(String(stepTotal), \"step\", r.steps)}`);\n lines.push(\n r.running\n ? color.dim(\" running…\")\n : color.dim(` ${((r.durationMs ?? 0) / 1000).toFixed(2)}s`)\n );\n\n for (const n of r.notes) lines.push(n);\n\n // --- dots (middle): the live heartbeat, wrapped, tail-capped -----------\n if (r.dots.length) {\n lines.push(\"\");\n const per = Math.max(1, cols - 2);\n const dotRows: string[] = [];\n for (let i = 0; i < r.dots.length; i += per) {\n dotRows.push(` ${r.dots.slice(i, i + per).join(\"\")}`);\n }\n const maxDotRows = 6;\n if (dotRows.length > maxDotRows) {\n const hidden = dotRows.length - maxDotRows;\n lines.push(\n color.dim(` …${hidden} earlier row${hidden === 1 ? \"\" : \"s\"}`)\n );\n lines.push(...dotRows.slice(-maxDotRows));\n } else {\n lines.push(...dotRows);\n }\n }\n return lines;\n }\n\n private render(): void {\n if (!this.started) return;\n const cols = this.cols();\n const rows = this.rows();\n\n const top = this.topLines();\n const caretCol = Math.min(stringWidth(this.prefix) + this.cursor + 1, cols);\n\n const above = [...top, ...this.resultLines(cols)];\n\n // The failures pane fills the rest of the screen and scrolls on its own.\n const failLines = flattenFailures(this.results.failures);\n const lines = [...above];\n if (failLines.length) {\n lines.push(\n \"\",\n divider(`failures (${this.results.failures.length})`, cols)\n );\n // Reserve one row for the scroll indicator.\n const pane = Math.max(1, rows - lines.length - 1);\n this.failPage = pane;\n const maxScroll = Math.max(0, failLines.length - pane);\n const off = Math.min(this.failScroll, maxScroll);\n this.failScroll = off;\n lines.push(...failLines.slice(off, off + pane));\n if (maxScroll > 0) {\n lines.push(\n color.dim(\n ` [${off + 1}-${off + Math.min(pane, failLines.length - off)}/${failLines.length}] · PgUp/PgDn to scroll`\n )\n );\n }\n }\n\n // Repaint: home, write each clipped line clearing to EOL, wipe below.\n write(\"\\x1b[?25l\\x1b[H\");\n const visible = lines.slice(0, rows).map(l => `${clip(l, cols)}\\x1b[K`);\n write(visible.join(\"\\r\\n\"));\n write(\"\\x1b[J\"); // clear anything left below our content\n // Park the caret on the input line at the right column.\n write(`\\x1b[1;${Math.max(1, caretCol)}H`);\n write(\"\\x1b[?25h\");\n }\n}\n\n/** A green `.` / red `F` / yellow `-` for a finished scenario. */\nfunction dotFor(status: keyof Counts): string {\n if (status === \"failed\") return color.red(\"F\");\n if (status === \"skipped\") return color.yellow(\"-\");\n return color.green(\".\");\n}\n\n/** `<total> <noun>s (X passed, Y failed, Z skipped)`, zero parts omitted. */\nfunction countLine(total: string, noun: string, counts: Counts): string {\n const part = (n: number, label: string, paint: (s: string) => string) =>\n n > 0 ? paint(`${n} ${label}`) : null;\n const parts = [\n part(counts.passed, \"passed\", color.green),\n part(counts.failed, \"failed\", color.red),\n part(counts.skipped, \"skipped\", color.yellow),\n ].filter((x): x is string => x !== null);\n const plural = total === \"1\" ? \"\" : \"s\";\n const detail = parts.length ? ` (${parts.join(\", \")})` : \"\";\n return `${total} ${noun}${plural}${detail}`;\n}\n\n/** Flatten failure blocks into lines, a blank line between blocks. */\nfunction flattenFailures(blocks: string[]): string[] {\n const out: string[] = [];\n blocks.forEach((block, i) => {\n if (i > 0) out.push(\"\");\n out.push(...block.split(\"\\n\"));\n });\n return out;\n}\n\n/** A dim `── label ─────` rule spanning the given width. */\nfunction divider(label: string, cols: number): string {\n const head = `── ${label} `;\n const fill = Math.max(0, cols - head.length);\n return color.dim(head + \"─\".repeat(fill));\n}\n\n/** Visible width, ignoring the ANSI escapes our color helpers may inject. */\nfunction stringWidth(s: string): number {\n // eslint-disable-next-line no-control-regex\n return s.replace(/\\x1b\\[[0-9;]*m/g, \"\").length;\n}\n\n/**\n * Truncate `s` to `max` visible columns, preserving ANSI color escapes (which\n * have zero width) and re-resetting at the cut so a clipped color can't bleed.\n */\nfunction clip(s: string, max: number): string {\n let width = 0;\n let out = \"\";\n // eslint-disable-next-line no-control-regex\n const escape = /^\\x1b\\[[0-9;]*m/;\n let i = 0;\n while (i < s.length) {\n const rest = s.slice(i);\n const m = escape.exec(rest);\n if (m) {\n out += m[0];\n i += m[0].length;\n continue;\n }\n if (width >= max) return `${out}\\x1b[0m`;\n out += s[i];\n width++;\n i++;\n }\n return out;\n}\n\nfunction write(s: string): void {\n process.stdout.write(s);\n}\n","import * as fs from \"node:fs\";\nimport * as path from \"node:path\";\n\n/** Glob metacharacters — the same set {@link globFiles} treats as \"magic\". */\nconst MAGIC = /[*?[\\]{}!()]/;\n\n/** File extensions worth reacting to: features and step/world modules. */\nconst WATCHED_EXT = /\\.(feature|ts|mts|js|mjs|cts|cjs)$/;\n\nexport interface Watcher {\n /** Stop watching and release every underlying `fs.watch` handle. */\n close(): void;\n}\n\n/**\n * Watch the directories implied by feature/step globs and invoke `onChange`\n * (debounced, coalesced) whenever a relevant file changes. Roots are the literal\n * directory prefixes of each glob — e.g. `features/**\\/*.feature` watches\n * `features/` recursively — deduped so nested roots aren't watched twice.\n *\n * Recursive watching is supported on macOS and Windows and on modern Linux\n * (Node ≥ 20 / Bun); on older Linux only the top level of each root is seen.\n */\nexport function watchFeatures(\n globs: string[],\n cwd: string,\n onChange: () => void,\n debounceMs = 120\n): Watcher {\n const roots = watchRoots(globs, cwd);\n const watchers: fs.FSWatcher[] = [];\n let timer: ReturnType<typeof setTimeout> | undefined;\n\n const schedule = (filename: string | null) => {\n // `fs.watch` may report a null filename; when we do get one, ignore churn\n // from unrelated files (editor swap files, `.git`, build output).\n if (filename && !WATCHED_EXT.test(filename)) return;\n if (timer) clearTimeout(timer);\n timer = setTimeout(onChange, debounceMs);\n };\n\n for (const root of roots) {\n try {\n const w = fs.watch(root, { recursive: true }, (_event, filename) =>\n schedule(filename)\n );\n w.on(\"error\", () => {}); // a removed/renamed root shouldn't crash the loop\n watchers.push(w);\n } catch {\n // Root doesn't exist yet (or can't be watched) — skip it silently.\n }\n }\n\n return {\n close() {\n if (timer) clearTimeout(timer);\n for (const w of watchers) w.close();\n },\n };\n}\n\n/**\n * The set of directories to watch: each glob's literal prefix (the path up to\n * its first magic character), resolved against `cwd`, deduped, with any root\n * that nests inside another dropped. Falls back to `cwd` when nothing resolves.\n */\nexport function watchRoots(globs: string[], cwd: string): string[] {\n const dirs = new Set<string>();\n for (const glob of globs) {\n dirs.add(path.resolve(cwd, literalPrefix(glob)));\n }\n const roots = [...dirs].sort();\n // Drop any root contained in an earlier (shorter) one, so recursive watches\n // don't overlap.\n const pruned = roots.filter(\n (r, i) => !roots.some((other, j) => j < i && isInside(r, other))\n );\n return pruned.length ? pruned : [cwd];\n}\n\n/** The directory portion of a glob before its first magic segment. */\nfunction literalPrefix(glob: string): string {\n const segments = glob.split(\"/\");\n const literal: string[] = [];\n for (const seg of segments) {\n if (MAGIC.test(seg)) break;\n literal.push(seg);\n }\n const joined = literal.join(\"/\");\n // A bare `*.feature` has no literal prefix → watch cwd (\".\").\n if (!joined) return \".\";\n // If the whole pattern was literal it points at a file; watch its directory.\n return MAGIC.test(glob) ? joined : path.dirname(joined);\n}\n\n/** True when `child` is `parent` itself or nested beneath it. */\nfunction isInside(child: string, parent: string): boolean {\n if (child === parent) return true;\n const rel = path.relative(parent, child);\n return !!rel && !rel.startsWith(\"..\") && !path.isAbsolute(rel);\n}\n","import { spawn } from \"node:child_process\";\nimport * as path from \"node:path\";\nimport { ParsedScenario } from \"../analyzer/types\";\nimport { ParsedFeature, parseFeatureCatalog } from \"../analyzer/gherkinParser\";\nimport { globFiles } from \"../globFiles\";\nimport { ResolvedConfig } from \"./config\";\nimport { Prompt, Suggestion } from \"./prompt\";\nimport { RunEvent } from \"./reporters\";\nimport { watchFeatures, Watcher } from \"./watcher\";\n\n/**\n * A population the user can pick and run: a whole tag, a whole feature file, a\n * single scenario, or a whole scenario outline (all its example rows). Scenarios\n * and outlines are keyed by name — an outline is one choice that runs every row —\n * so selection is stable across edits that shift line numbers. Resolved against\n * the freshly-parsed features on every run.\n */\ntype Choice =\n | { kind: \"tag\"; tag: string }\n | { kind: \"feature\"; file: string; name: string }\n | { kind: \"scenario\"; file: string; name: string };\n\nconst useColor =\n !process.env.NO_COLOR && (process.stdout.isTTY ?? false) === true;\nconst bold = (s: string) => (useColor ? `\\x1b[1m${s}\\x1b[22m` : s);\nconst dim = (s: string) => (useColor ? `\\x1b[2m${s}\\x1b[22m` : s);\nconst red = (s: string) => (useColor ? `\\x1b[31m${s}\\x1b[39m` : s);\nconst yellow = (s: string) => (useColor ? `\\x1b[33m${s}\\x1b[39m` : s);\n\n/**\n * Entry point for `step-forge -i`. Brings up the always-live typeahead prompt,\n * watches the configured feature/step directories, and re-runs the armed\n * population on every relevant file change. Requires a TTY.\n */\nexport async function runInteractive(config: ResolvedConfig): Promise<void> {\n if (!process.stdin.isTTY) {\n process.stderr.write(\n \"Interactive mode (-i) requires a TTY; stdin is not a terminal.\\n\"\n );\n process.exitCode = 1;\n return;\n }\n await new InteractiveSession(config).start();\n}\n\nclass InteractiveSession {\n private readonly config: ResolvedConfig;\n private readonly prompt: Prompt;\n private watcher?: Watcher;\n private catalog: ParsedFeature[] = [];\n\n // State machine: `armed` is the committed population; `dirty` means the query\n // was edited since the last commit, which suspends file-change re-runs.\n private armed: Choice | null = null;\n private dirty = false;\n private running = false;\n private rerunQueued = false;\n /** Increments per run so back-to-back identical output is still distinguishable. */\n private runCount = 0;\n\n private done!: () => void;\n\n constructor(config: ResolvedConfig) {\n this.config = config;\n this.prompt = new Prompt({\n initialQuery: config.tags ?? config.name ?? \"\",\n handlers: {\n onSubmit: value => this.onSubmit(value),\n onEdit: () => this.onEdit(),\n onEscape: () => this.onEscape(),\n onQuit: () => this.onQuit(),\n },\n });\n }\n\n async start(): Promise<void> {\n await this.rebuildCatalog();\n this.prompt.status = this.status();\n this.prompt.start();\n this.watcher = watchFeatures(\n [...this.config.features, ...this.config.steps],\n this.config.cwd,\n () => void this.onFsChange()\n );\n await new Promise<void>(resolve => {\n this.done = resolve;\n });\n }\n\n // --- prompt events -------------------------------------------------------\n private onSubmit(value: unknown | null): void {\n if (!value) return; // nothing highlighted (empty query / no matches)\n this.armed = value as Choice;\n this.dirty = false;\n this.prompt.status = this.status();\n this.triggerRun();\n }\n\n private onEdit(): void {\n // Editing the query suspends auto-runs until the next Enter re-commits.\n if (this.armed) this.dirty = true;\n this.prompt.status = this.status();\n this.prompt.redraw();\n }\n\n private onEscape(): void {\n if (!this.armed) return;\n this.armed = null;\n this.dirty = false;\n this.prompt.status = this.status();\n this.prompt.redraw();\n }\n\n private onQuit(): void {\n this.watcher?.close();\n this.prompt.stop();\n process.stdout.write(\"\\n\");\n process.exitCode = 0;\n this.done?.();\n }\n\n // --- filesystem watch ----------------------------------------------------\n private async onFsChange(): Promise<void> {\n await this.rebuildCatalog();\n if (this.armed && !this.dirty) this.triggerRun();\n }\n\n private async rebuildCatalog(): Promise<void> {\n try {\n const featureFiles = await globFiles(\n this.config.features,\n this.config.cwd\n );\n this.catalog = parseFeatureCatalog(featureFiles);\n } catch {\n // A half-written feature mid-save may fail to parse; keep the old cache.\n }\n this.prompt.setSuggestions(buildSuggestions(this.catalog));\n }\n\n // --- run cycle -----------------------------------------------------------\n private triggerRun(): void {\n if (!this.armed) return;\n if (this.running) {\n this.rerunQueued = true; // coalesce: run once more when the current ends\n return;\n }\n this.running = true;\n void this.runLoop();\n }\n\n private async runLoop(): Promise<void> {\n try {\n do {\n this.rerunQueued = false;\n if (this.armed) await this.executeOnce(this.armed);\n } while (this.rerunQueued && this.armed && !this.dirty);\n } catch (err) {\n this.prompt.failureBlock(\n red(`run failed: ${err instanceof Error ? err.message : err}`)\n );\n this.prompt.complete(0);\n } finally {\n this.running = false;\n this.prompt.status = this.status();\n this.prompt.redraw();\n }\n }\n\n /**\n * One run of `choice`: resolve its scenarios against the current parse, then\n * shell out to a **fresh** `step-forge` process scoped to just that\n * population. A child process is used deliberately — Bun caches ES modules for\n * the life of a process and ignores query-string cache-busting, so re-running\n * in-process would never pick up edited step code. A new process re-reads\n * every step/feature file, which is exactly what a watch loop needs.\n */\n private async executeOnce(choice: Choice): Promise<void> {\n const selected = resolveScenarios(choice, this.scenarios());\n\n this.prompt.resetResults({\n label: choiceLabel(choice),\n scenarioCount: selected.length,\n runCount: ++this.runCount,\n clock: clock(),\n });\n\n if (selected.length === 0) {\n this.prompt.note(yellow(\" no scenarios match this selection\"));\n this.prompt.complete(0);\n return;\n }\n\n // A single scenario is analyzed (dependency/undefined/ambiguous checks);\n // its diagnostics feed the results region as notes above the dots.\n const single = selected.length === 1;\n if (single) {\n for (const line of await this.analyzeScenario(selected[0])) {\n this.prompt.note(line);\n }\n }\n\n await this.spawnRun(runArgs(choice, this.config));\n }\n\n /** Every scenario across the current catalog, flattened. */\n private scenarios(): ParsedScenario[] {\n return this.catalog.flatMap(f => f.scenarios);\n }\n\n /**\n * Run `step-forge --events` as a child process and feed its NDJSON event\n * stream into the results region as it arrives — live tallies, dots, and\n * failure blocks. Re-invokes the very CLI that's running us (`process.execPath`\n * + `argv[1]`), so it works identically from the source tree and the built bin.\n * `FORCE_COLOR` keeps the child's rendered failure blocks coloured even though\n * its stdout is a pipe. Never rejects — errors are surfaced and swallowed so\n * the watch loop survives.\n */\n private spawnRun(args: string[]): Promise<void> {\n return new Promise(resolve => {\n const child = spawn(process.execPath, [process.argv[1], ...args], {\n cwd: this.config.cwd,\n env: { ...process.env, FORCE_COLOR: \"1\" },\n // Own stdin ourselves (raw-mode prompt); capture stdout (the event\n // stream) and stderr (a crash) so the parent owns all rendering.\n stdio: [\"ignore\", \"pipe\", \"pipe\"],\n });\n\n let completed = false;\n let durationMs = 0;\n let stdoutBuf = \"\";\n let stderr = \"\";\n\n child.stdout.setEncoding(\"utf8\");\n child.stdout.on(\"data\", (chunk: string) => {\n stdoutBuf += chunk;\n let nl: number;\n while ((nl = stdoutBuf.indexOf(\"\\n\")) !== -1) {\n const line = stdoutBuf.slice(0, nl);\n stdoutBuf = stdoutBuf.slice(nl + 1);\n if (!line.trim()) continue;\n let evt: RunEvent;\n try {\n evt = JSON.parse(line) as RunEvent;\n } catch {\n continue; // ignore any non-event line\n }\n if (evt.t === \"scenario\") {\n this.prompt.scenario(evt.status, evt.steps, evt.detail);\n } else if (evt.t === \"complete\") {\n completed = true;\n durationMs = evt.durationMs;\n }\n }\n });\n\n child.stderr.setEncoding(\"utf8\");\n child.stderr.on(\"data\", (chunk: string) => {\n stderr += chunk;\n });\n\n child.on(\"error\", err => {\n this.prompt.failureBlock(red(`could not start runner: ${err.message}`));\n this.prompt.complete(0);\n resolve();\n });\n child.on(\"close\", () => {\n if (completed) {\n this.prompt.complete(durationMs);\n } else {\n const detail = stderr.trim() || \"(no output)\";\n this.prompt.failureBlock(\n red(`runner exited without reporting results:\\n${detail}`)\n );\n this.prompt.complete(0);\n }\n resolve();\n });\n });\n }\n\n /**\n * Run the static analyzer over a single scenario and return any dependency /\n * undefined / ambiguous diagnostics as note lines for the results region. The\n * analyzer needs the optional `typescript` peer for AST extraction; if it's\n * absent we note that and skip, never failing the run.\n */\n private async analyzeScenario(scenario: ParsedScenario): Promise<string[]> {\n let analyzer: typeof import(\"../analyzer/index\");\n try {\n analyzer = await import(\"../analyzer/index\");\n } catch {\n return [dim(\" analysis skipped: install `typescript` to enable it\")];\n }\n try {\n const stepFiles = await globFiles(this.config.steps, this.config.cwd);\n const defs = analyzer.extractStepDefinitions(stepFiles);\n const matched = analyzer.matchScenarioSteps(scenario, defs);\n const diagnostics = analyzer.defaultRules.flatMap(rule =>\n rule.check(scenario, matched)\n );\n return formatDiagnostics(diagnostics, this.config.cwd);\n } catch (err) {\n return [\n dim(\n ` analysis unavailable: ${err instanceof Error ? err.message : err}`\n ),\n ];\n }\n }\n\n // --- status line ---------------------------------------------------------\n private status(): string {\n if (!this.armed) {\n return \" type to filter · ↑↓ move · enter run · ctrl-c quit\";\n }\n if (this.dirty) {\n return \" ⏸ suspended (query edited) · enter runs the selection · esc cancel\";\n }\n return ` ▶ watching ${choiceLabel(this.armed)} · save a file to re-run · enter forces · esc change · ctrl-c quit`;\n }\n}\n\n// --- choices ---------------------------------------------------------------\n/** Build the typeahead pool: every tag, feature, and scenario in the catalog. */\nfunction buildSuggestions(catalog: ParsedFeature[]): Suggestion[] {\n const suggestions: Suggestion[] = [];\n\n const tags = new Set<string>();\n for (const feature of catalog) {\n for (const scenario of feature.scenarios) {\n for (const tag of scenario.tags) tags.add(tag);\n }\n }\n for (const tag of [...tags].sort()) {\n suggestions.push({\n badge: \"#tag\",\n label: tag,\n search: tag.toLowerCase(),\n value: { kind: \"tag\", tag } satisfies Choice,\n });\n }\n\n for (const feature of catalog) {\n const featureLabel = feature.name || path.basename(feature.file);\n suggestions.push({\n badge: \"feature\",\n label: featureLabel,\n search: `${feature.name} ${feature.file}`.toLowerCase(),\n value: {\n kind: \"feature\",\n file: feature.file,\n name: feature.name,\n } satisfies Choice,\n });\n\n // Each row of a scenario outline shares the outline's name; collapse them\n // into a single choice (selected by the outline name, which runs every row)\n // rather than one entry per row. Regular scenarios stay one entry each.\n const seen = new Set<string>();\n for (const scenario of feature.scenarios) {\n const name = scenario.outline ? scenario.outline.name : scenario.name;\n if (seen.has(name)) continue;\n seen.add(name);\n suggestions.push({\n badge: scenario.outline ? \"outline\" : \"scenario\",\n label: name,\n search: `${featureLabel} ${name}`.toLowerCase(),\n value: { kind: \"scenario\", file: scenario.file, name } satisfies Choice,\n });\n }\n }\n\n return suggestions;\n}\n\n/** Resolve a choice to its scenarios against the current parse. */\nfunction resolveScenarios(\n choice: Choice,\n scenarios: ParsedScenario[]\n): ParsedScenario[] {\n switch (choice.kind) {\n case \"tag\":\n return scenarios.filter(s => s.tags.includes(choice.tag));\n case \"feature\":\n return scenarios.filter(s => s.file === choice.file);\n case \"scenario\":\n // Matches a regular scenario by its name, or every row of an outline by\n // the shared outline name — mirroring the runner's `-n` (name || outline).\n return scenarios.filter(\n s =>\n s.file === choice.file &&\n (s.name === choice.name || s.outline?.name === choice.name)\n );\n }\n}\n\nfunction choiceLabel(choice: Choice): string {\n switch (choice.kind) {\n case \"tag\":\n return choice.tag;\n case \"feature\":\n return choice.name || path.basename(choice.file);\n case \"scenario\":\n return choice.name;\n }\n}\n\n/**\n * The `step-forge` argv that reproduces this selection in a child process. The\n * base flags forward the resolved config (steps/world/concurrency) so the child\n * matches the parent's setup regardless of its own config file; the scope flags\n * narrow to the chosen population:\n * - tag → all configured features, filtered by `-t <tag>`\n * - feature → that single feature file\n * - scenario → that feature file, name-anchored with `-n \"/^…$/\"` (the name is\n * the outline name for an outline, so all its rows run)\n * Always `--events`: the child emits its NDJSON run stream and the TUI renders\n * the results region itself.\n */\nfunction runArgs(choice: Choice, config: ResolvedConfig): string[] {\n const args: string[] = [];\n for (const glob of config.steps) args.push(\"-s\", glob);\n if (config.world) args.push(\"-w\", config.world);\n args.push(\"-c\", String(config.concurrency));\n\n switch (choice.kind) {\n case \"tag\":\n args.push(...config.features, \"-t\", choice.tag);\n break;\n case \"feature\":\n args.push(choice.file);\n break;\n case \"scenario\":\n args.push(choice.file, \"-n\", `/^${escapeRegExp(choice.name)}$/`);\n break;\n }\n\n args.push(\"--events\");\n return args;\n}\n\n/** Escape a scenario name for use inside an anchored `-n` regex. */\nfunction escapeRegExp(text: string): string {\n return text.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n}\n\n/** Local wall-clock time as `HH:MM:SS`, so each run's header is dated. */\nfunction clock(): string {\n const d = new Date();\n return [d.getHours(), d.getMinutes(), d.getSeconds()]\n .map(n => String(n).padStart(2, \"0\"))\n .join(\":\");\n}\n\n// --- diagnostics -----------------------------------------------------------\nfunction formatDiagnostics(\n diagnostics: import(\"../analyzer/types\").Diagnostic[],\n cwd: string\n): string[] {\n if (diagnostics.length === 0) return []; // clean scenario: say nothing, let the run speak\n const lines = [bold(\" analyzer:\")];\n for (const d of diagnostics) {\n const mark =\n d.severity === \"error\"\n ? red(\"✗\")\n : d.severity === \"warning\"\n ? yellow(\"!\")\n : dim(\"i\");\n const loc = `${path.relative(cwd, d.file)}:${d.range.startLine}`;\n lines.push(` ${mark} ${d.message} ${dim(`(${loc})`)}`);\n }\n return lines;\n}\n","#!/usr/bin/env bun\nimport { parseArgs } from \"node:util\";\nimport * as path from \"node:path\";\nimport { loadConfigFile, resolveConfig, RunnerOptions } from \"./config\";\nimport { run } from \"./runner\";\nimport { runInteractive } from \"./interactive\";\nimport { eventsReporter } from \"./reporters\";\n\nconst HELP = `step-forge — native TypeScript runner for Gherkin step definitions\n\nUsage:\n step-forge [options] [feature globs...]\n\nOptions:\n -t, --tags <expr> Tag expression, e.g. \"@smoke and not @wip\"\n -n, --name <pattern> Only scenarios whose name matches (substring or /regex/)\n -s, --steps <glob> Step-definition module glob (repeatable)\n -w, --world <module> World factory module (default export () => world)\n -c, --concurrency <n> Max scenarios in flight (default: 1, i.e. serial)\n -r, --reporter <name> \"pretty\" (default) or \"progress\"\n -v, --verbose Report every scenario, not just failures\n -i, --interactive Interactive watch mode: pick a tag/feature/scenario\n and re-run it on every file change\n --config <path> Config file directory (default: cwd)\n -h, --help Show this help\n\nPositional arguments are feature globs and override the configured features.\n`;\n\n/** Parse argv into config overrides. Positional args become feature globs. */\nfunction parseCli(argv: string[]): {\n cwd: string;\n overrides: RunnerOptions;\n interactive: boolean;\n events: boolean;\n} {\n const { values, positionals } = parseArgs({\n args: argv,\n allowPositionals: true,\n options: {\n tags: { type: \"string\", short: \"t\" },\n name: { type: \"string\", short: \"n\" },\n steps: { type: \"string\", short: \"s\", multiple: true },\n world: { type: \"string\", short: \"w\" },\n concurrency: { type: \"string\", short: \"c\" },\n reporter: { type: \"string\", short: \"r\" },\n verbose: { type: \"boolean\", short: \"v\" },\n interactive: { type: \"boolean\", short: \"i\" },\n // Hidden: emit NDJSON run events instead of human output. Used by the\n // interactive TUI, which spawns a child runner and renders results itself.\n events: { type: \"boolean\" },\n config: { type: \"string\" },\n help: { type: \"boolean\", short: \"h\" },\n },\n });\n\n if (values.help) {\n process.stdout.write(HELP);\n process.exit(0);\n }\n\n const overrides: RunnerOptions = {};\n if (positionals.length) overrides.features = positionals;\n if (values.tags) overrides.tags = values.tags;\n if (values.name) overrides.name = values.name;\n if (values.steps) overrides.steps = values.steps;\n if (values.world) overrides.world = values.world;\n if (values.verbose) overrides.verbose = true;\n if (values.reporter) {\n if (values.reporter !== \"pretty\" && values.reporter !== \"progress\") {\n throw new Error(`Unknown reporter: ${values.reporter}`);\n }\n overrides.reporter = values.reporter;\n }\n if (values.concurrency !== undefined) {\n const n = Number(values.concurrency);\n if (!Number.isInteger(n) || n < 1) {\n throw new Error(`--concurrency must be a positive integer`);\n }\n overrides.concurrency = n;\n }\n\n const cwd = values.config\n ? path.resolve(process.cwd(), values.config)\n : process.cwd();\n return {\n cwd,\n overrides,\n interactive: values.interactive ?? false,\n events: values.events ?? false,\n };\n}\n\nasync function main(): Promise<void> {\n const { cwd, overrides, interactive, events } = parseCli(\n process.argv.slice(2)\n );\n const fileConfig = await loadConfigFile(cwd);\n const config = resolveConfig(cwd, fileConfig, overrides);\n if (interactive) {\n await runInteractive(config);\n return; // interactive mode manages its own lifecycle and exit code\n }\n const { passed } = events\n ? await run(config, eventsReporter({ cwd: config.cwd }))\n : await run(config);\n process.exitCode = passed ? 0 : 1;\n}\n\nmain().catch(err => {\n process.stderr.write(`${err instanceof Error ? err.stack : String(err)}\\n`);\n process.exitCode = 1;\n});\n"],"mappings":"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"}
|