import { z } from 'zod'; /** * Core type definitions for Qualiow Exploratory Testing Skills. * Derived from the YAML data files in data/. */ interface AuthConfig { strategy: 'none' | 'storage_state' | 'credentials' | 'token' | 'in-app' | 'interactive-sso'; state_file?: string; login_url?: string; credentials?: { username: string; password: string; }; token?: string; identity_provider?: string; static_otp?: string; test_email_pattern?: string; } interface BrowserConfig { headless: boolean; viewport: { width: number; height: number; }; engine?: 'chromium' | 'webkit' | 'firefox'; channel?: string; device?: string; } interface ScopeConfig { start_pages?: string[]; max_depth: number; include_patterns?: string[]; exclude_patterns?: string[]; } interface SafetyConfig { read_only: boolean; no_form_submit: boolean; no_file_upload?: boolean; no_delete_actions?: boolean; } interface EnvironmentConfig { kind: 'dev' | 'ephemeral' | 'staging' | 'production'; destroyed_automatically?: boolean; } interface BackendConfig { provider?: 'aws'; /** Env var NAME holding the AWS profile — never the credential itself. */ aws_profile_env?: string; region_env?: string; region?: string; /** Expected account id; the live lane stops if the caller identity differs. */ account_id?: string; env_suffix?: string; /** Logical name -> deployed resource name. Asserted, confirmed before use. */ resources?: Record; notes?: string; } interface ApiSurfaceConfig { /** Defaults to the target's base_url when omitted. */ base_url?: string; auth?: 'session-cookie' | 'bearer-env' | 'api-key-env' | 'none'; /** Env var NAME holding the credential — never the credential itself. */ token_env?: string; /** Header carrying the credential for `api-key-env`. Defaults to `X-Api-Key`. */ header_name?: string; /** Persistent browser profile directory holding the authenticated session. */ browser_profile?: string; /** Endpoint returning the deployed build, used to fingerprint the environment. */ version_endpoint?: string; /** Logical name -> "VERB /path". */ endpoints?: Record; /** The only endpoints the read-only API lane may call without asking. */ probe_allowlist?: string[]; /** The only endpoints phase 4 may call through the real write path, in a dev * or ephemeral environment. Absent means the API lane is read-only here. */ write_allowlist?: string[]; /** Other target ids to run the same matrix against, for parity comparison. */ parity_targets?: string[]; /** Flag name -> where its deployed value is declared for this environment. */ feature_flags?: Record; notes?: string; } interface SourceBranchConfig { repo_path: string; branch: string; base_branch?: string; components?: Record; } interface TargetConfig { id: string; name: string; base_url: string; domain: string; auth: AuthConfig; browser: BrowserConfig; scope: ScopeConfig; safety?: SafetyConfig; environment?: EnvironmentConfig; backend?: BackendConfig; api?: ApiSurfaceConfig; source?: SourceBranchConfig; notes?: string; } interface MobileDeviceConfig { name?: string; udid?: string; avd?: string; serial?: string; } interface MobileAppConfig { bundle_id?: string; package?: string; app_paths?: string[]; apk_paths?: string[]; } interface MobileWebConfig { base_url: string; start_url?: string; } interface SourceRepoConfig { path: string; build_commands?: Record; } interface MobileScopeConfig { start_screen?: string; start_url?: string; include_patterns?: string[]; exclude_patterns?: string[]; } interface MobileTargetConfig { id: string; name: string; platform: 'ios' | 'android'; domain: string; device: MobileDeviceConfig; app: MobileAppConfig; web?: MobileWebConfig; auth: AuthConfig; scope?: MobileScopeConfig; safety?: SafetyConfig; source_repo?: SourceRepoConfig; notes?: string; } type AnyTargetConfig = TargetConfig | MobileTargetConfig; interface KnowledgeManifestEntry { id: string; file: string; type: string; priority: 'high' | 'medium' | 'low'; tags: string[]; domains: string[]; } interface KnowledgeManifestStats { total_entries: number; heuristics: number; techniques: number; checklists: number; references: number; domain_profiles: number; custom_entries: number; } interface LoadingStrategy { always: string[]; by_domain: Record; by_tag: Record; } interface KnowledgeManifest { version: string; last_updated: string; active_releases: string[]; stats: KnowledgeManifestStats; loading_strategy: LoadingStrategy; entries: KnowledgeManifestEntry[]; } interface KnowledgeEntryContent { summary: string; [key: string]: unknown; } interface KnowledgeEntry { id: string; version: string; type: 'heuristic' | 'technique' | 'checklist' | 'reference'; name: string; description: string; author?: string; source?: string; tags: string[]; domains: string[]; priority: 'high' | 'medium' | 'low'; added: string; content: KnowledgeEntryContent; } interface Journey { name: string; steps: string[]; } interface RiskRanking { p0: string[]; p1: string[]; p2: string[]; p3: string[]; } interface DomainConfig { id: string; name: string; risk_ranking: RiskRanking; completeness_checklist: string[]; data_integrity_checks: string[]; journeys: Journey[]; must_test_patterns: Record; common_bugs: string[]; compliance: string[]; guidance: string; } interface SeverityCounts { critical: number; high: number; medium: number; low: number; } interface SessionMetrics { session_id: string; target: string; date: string; duration_min: number; bugs_found: number; severity_counts: SeverityCounts; pages_explored: number; kind?: 'explore' | 'quick' | 'mobile' | 'backend'; domain?: string; started_at?: string; completed_at?: string; phases_completed?: number; total_phases?: number; coverage?: Record; evidence?: Record; areas_not_tested?: string[]; blocked_by?: string | null; } type BugSeverity = 'critical' | 'high' | 'medium' | 'low'; interface BugReport { id: string; title: string; severity: BugSeverity; url: string; component: string; expected: string; actual: string; steps: string[]; business_impact: string; evidence?: string[]; } interface ValidationError { path: string; message: string; } interface ValidationResult { file: string; valid: boolean; errors?: ValidationError[]; } declare const AuthConfigSchema: z.ZodObject<{ strategy: z.ZodEnum<{ none: "none"; storage_state: "storage_state"; credentials: "credentials"; token: "token"; "in-app": "in-app"; "interactive-sso": "interactive-sso"; }>; state_file: z.ZodOptional; login_url: z.ZodOptional; credentials: z.ZodOptional>; token: z.ZodOptional; identity_provider: z.ZodOptional; static_otp: z.ZodOptional; test_email_pattern: z.ZodOptional; notes: z.ZodOptional; }, z.core.$strict>; declare const BrowserConfigSchema: z.ZodObject<{ headless: z.ZodBoolean; viewport: z.ZodObject<{ width: z.ZodNumber; height: z.ZodNumber; }, z.core.$strict>; engine: z.ZodOptional>; channel: z.ZodOptional; device: z.ZodOptional; }, z.core.$strict>; declare const ScopeConfigSchema: z.ZodObject<{ start_pages: z.ZodOptional>; max_depth: z.ZodNumber; include_patterns: z.ZodOptional>; exclude_patterns: z.ZodOptional>; }, z.core.$strict>; declare const SafetyConfigSchema: z.ZodObject<{ read_only: z.ZodBoolean; no_form_submit: z.ZodBoolean; no_file_upload: z.ZodOptional; no_delete_actions: z.ZodOptional; }, z.core.$strict>; declare const WebTargetConfigSchema: z.ZodObject<{ id: z.ZodString; name: z.ZodString; base_url: z.ZodUnion, z.ZodString]>; domain: z.ZodString; auth: z.ZodObject<{ strategy: z.ZodEnum<{ none: "none"; storage_state: "storage_state"; credentials: "credentials"; token: "token"; "in-app": "in-app"; "interactive-sso": "interactive-sso"; }>; state_file: z.ZodOptional; login_url: z.ZodOptional; credentials: z.ZodOptional>; token: z.ZodOptional; identity_provider: z.ZodOptional; static_otp: z.ZodOptional; test_email_pattern: z.ZodOptional; notes: z.ZodOptional; }, z.core.$strict>; browser: z.ZodObject<{ headless: z.ZodBoolean; viewport: z.ZodObject<{ width: z.ZodNumber; height: z.ZodNumber; }, z.core.$strict>; engine: z.ZodOptional>; channel: z.ZodOptional; device: z.ZodOptional; }, z.core.$strict>; scope: z.ZodObject<{ start_pages: z.ZodOptional>; max_depth: z.ZodNumber; include_patterns: z.ZodOptional>; exclude_patterns: z.ZodOptional>; }, z.core.$strict>; safety: z.ZodOptional; no_delete_actions: z.ZodOptional; }, z.core.$strict>>; environment: z.ZodOptional; destroyed_automatically: z.ZodOptional; }, z.core.$strict>>; backend: z.ZodOptional>; aws_profile_env: z.ZodOptional; region_env: z.ZodOptional; region: z.ZodOptional; account_id: z.ZodOptional; env_suffix: z.ZodOptional; resources: z.ZodOptional>; notes: z.ZodOptional; }, z.core.$strict>>; api: z.ZodOptional; auth: z.ZodOptional>; token_env: z.ZodOptional; header_name: z.ZodOptional; browser_profile: z.ZodOptional; version_endpoint: z.ZodOptional; endpoints: z.ZodOptional>; probe_allowlist: z.ZodOptional>; write_allowlist: z.ZodOptional>; parity_targets: z.ZodOptional>; feature_flags: z.ZodOptional>; notes: z.ZodOptional; }, z.core.$strict>>; source: z.ZodOptional; components: z.ZodOptional>; }, z.core.$strict>>; notes: z.ZodOptional; }, z.core.$strict>; declare const MobileDeviceConfigSchema: z.ZodObject<{ name: z.ZodOptional; udid: z.ZodOptional; avd: z.ZodOptional; serial: z.ZodOptional; }, z.core.$strict>; declare const MobileAppConfigSchema: z.ZodObject<{ bundle_id: z.ZodOptional; package: z.ZodOptional; app_paths: z.ZodOptional>; apk_paths: z.ZodOptional>; }, z.core.$strict>; declare const MobileWebConfigSchema: z.ZodObject<{ base_url: z.ZodString; start_url: z.ZodOptional; }, z.core.$strict>; declare const SourceRepoConfigSchema: z.ZodObject<{ path: z.ZodString; build_commands: z.ZodOptional>; }, z.core.$strict>; declare const MobileScopeConfigSchema: z.ZodObject<{ start_screen: z.ZodOptional; start_url: z.ZodOptional; include_patterns: z.ZodOptional>; exclude_patterns: z.ZodOptional>; }, z.core.$strict>; declare const MobileTargetConfigSchema: z.ZodObject<{ id: z.ZodString; name: z.ZodString; platform: z.ZodEnum<{ ios: "ios"; android: "android"; }>; domain: z.ZodString; device: z.ZodObject<{ name: z.ZodOptional; udid: z.ZodOptional; avd: z.ZodOptional; serial: z.ZodOptional; }, z.core.$strict>; app: z.ZodObject<{ bundle_id: z.ZodOptional; package: z.ZodOptional; app_paths: z.ZodOptional>; apk_paths: z.ZodOptional>; }, z.core.$strict>; web: z.ZodOptional; }, z.core.$strict>>; auth: z.ZodObject<{ strategy: z.ZodEnum<{ none: "none"; storage_state: "storage_state"; credentials: "credentials"; token: "token"; "in-app": "in-app"; "interactive-sso": "interactive-sso"; }>; state_file: z.ZodOptional; login_url: z.ZodOptional; credentials: z.ZodOptional>; token: z.ZodOptional; identity_provider: z.ZodOptional; static_otp: z.ZodOptional; test_email_pattern: z.ZodOptional; notes: z.ZodOptional; }, z.core.$strict>; scope: z.ZodOptional; start_url: z.ZodOptional; include_patterns: z.ZodOptional>; exclude_patterns: z.ZodOptional>; }, z.core.$strict>>; safety: z.ZodOptional; no_delete_actions: z.ZodOptional; }, z.core.$strict>>; source_repo: z.ZodOptional>; }, z.core.$strict>>; notes: z.ZodOptional; }, z.core.$strict>; declare const TargetConfigSchema: z.ZodPipe | undefined; notes?: string | undefined; } | undefined; api?: { base_url?: string | undefined; auth?: "none" | "session-cookie" | "bearer-env" | "api-key-env" | undefined; token_env?: string | undefined; header_name?: string | undefined; browser_profile?: string | undefined; version_endpoint?: string | undefined; endpoints?: Record | undefined; probe_allowlist?: string[] | undefined; write_allowlist?: string[] | undefined; parity_targets?: string[] | undefined; feature_flags?: Record | undefined; notes?: string | undefined; } | undefined; source?: { repo_path: string; branch: string; base_branch?: string | undefined; components?: Record | undefined; } | undefined; notes?: string | undefined; } | { id: string; name: string; platform: "ios" | "android"; domain: string; device: { name?: string | undefined; udid?: string | undefined; avd?: string | undefined; serial?: string | undefined; }; app: { bundle_id?: string | undefined; package?: string | undefined; app_paths?: string[] | undefined; apk_paths?: string[] | undefined; }; auth: { strategy: "none" | "storage_state" | "credentials" | "token" | "in-app" | "interactive-sso"; state_file?: string | undefined; login_url?: string | undefined; credentials?: { username: string; password: string; } | undefined; token?: string | undefined; identity_provider?: string | undefined; static_otp?: string | undefined; test_email_pattern?: string | undefined; notes?: string | undefined; }; web?: { base_url: string; start_url?: string | undefined; } | undefined; scope?: { start_screen?: string | undefined; start_url?: string | undefined; include_patterns?: string[] | undefined; exclude_patterns?: string[] | undefined; } | undefined; safety?: { read_only: boolean; no_form_submit: boolean; no_file_upload?: boolean | undefined; no_delete_actions?: boolean | undefined; } | undefined; source_repo?: { path: string; build_commands?: Record | undefined; } | undefined; notes?: string | undefined; }, unknown>>; declare const KnowledgeEntryContentSchema: z.ZodObject<{ summary: z.ZodString; }, z.core.$loose>; declare const KnowledgeEntrySchema: z.ZodObject<{ id: z.ZodString; version: z.ZodString; type: z.ZodEnum<{ heuristic: "heuristic"; technique: "technique"; checklist: "checklist"; reference: "reference"; }>; name: z.ZodString; description: z.ZodString; author: z.ZodOptional; source: z.ZodOptional; tags: z.ZodArray; domains: z.ZodArray; priority: z.ZodEnum<{ high: "high"; medium: "medium"; low: "low"; }>; added: z.ZodString; content: z.ZodObject<{ summary: z.ZodString; }, z.core.$loose>; }, z.core.$strip>; declare const KnowledgeManifestStatsSchema: z.ZodObject<{ total_entries: z.ZodNumber; heuristics: z.ZodNumber; techniques: z.ZodNumber; checklists: z.ZodNumber; references: z.ZodNumber; domain_profiles: z.ZodNumber; custom_entries: z.ZodNumber; }, z.core.$strip>; declare const KnowledgeManifestEntrySchema: z.ZodObject<{ id: z.ZodString; file: z.ZodString; type: z.ZodString; priority: z.ZodEnum<{ high: "high"; medium: "medium"; low: "low"; }>; tags: z.ZodArray; domains: z.ZodArray; }, z.core.$strip>; declare const LoadingStrategySchema: z.ZodObject<{ always: z.ZodArray; by_domain: z.ZodRecord>; by_tag: z.ZodRecord>; by_skill: z.ZodOptional>>; }, z.core.$strip>; declare const KnowledgeManifestSchema: z.ZodObject<{ version: z.ZodString; last_updated: z.ZodString; active_releases: z.ZodArray; stats: z.ZodObject<{ total_entries: z.ZodNumber; heuristics: z.ZodNumber; techniques: z.ZodNumber; checklists: z.ZodNumber; references: z.ZodNumber; domain_profiles: z.ZodNumber; custom_entries: z.ZodNumber; }, z.core.$strip>; loading_strategy: z.ZodObject<{ always: z.ZodArray; by_domain: z.ZodRecord>; by_tag: z.ZodRecord>; by_skill: z.ZodOptional>>; }, z.core.$strip>; entries: z.ZodArray; tags: z.ZodArray; domains: z.ZodArray; }, z.core.$strip>>; }, z.core.$strip>; declare const JourneySchema: z.ZodObject<{ name: z.ZodString; steps: z.ZodArray; }, z.core.$strict>; declare const DomainConfigSchema: z.ZodObject<{ id: z.ZodString; name: z.ZodString; risk_ranking: z.ZodObject<{ p0: z.ZodArray; p1: z.ZodArray; p2: z.ZodArray; p3: z.ZodArray; }, z.core.$strict>; completeness_checklist: z.ZodArray; data_integrity_checks: z.ZodArray; journeys: z.ZodArray; }, z.core.$strict>>; must_test_patterns: z.ZodRecord>; common_bugs: z.ZodArray; compliance: z.ZodArray; guidance: z.ZodString; }, z.core.$strict>; declare const SeverityCountsSchema: z.ZodObject<{ critical: z.ZodNumber; high: z.ZodNumber; medium: z.ZodNumber; low: z.ZodNumber; }, z.core.$strip>; /** * Canonical shape of a session's `stats.json`. The first block is required * (and is what `metrics.jsonl` records); the rest is optional context a * session skill may add. */ declare const SessionMetricsSchema: z.ZodObject<{ session_id: z.ZodString; target: z.ZodString; date: z.ZodString; duration_min: z.ZodNumber; bugs_found: z.ZodNumber; severity_counts: z.ZodObject<{ critical: z.ZodNumber; high: z.ZodNumber; medium: z.ZodNumber; low: z.ZodNumber; }, z.core.$strip>; pages_explored: z.ZodNumber; kind: z.ZodOptional>; domain: z.ZodOptional; started_at: z.ZodOptional; completed_at: z.ZodOptional; phases_completed: z.ZodOptional; total_phases: z.ZodOptional; coverage: z.ZodOptional>; evidence: z.ZodOptional>; areas_not_tested: z.ZodOptional>; blocked_by: z.ZodOptional>; }, z.core.$strip>; declare const KnowledgeReleaseSchema: z.ZodObject<{ version: z.ZodString; date: z.ZodString; author: z.ZodString; status: z.ZodEnum<{ active: "active"; deprecated: "deprecated"; }>; notes: z.ZodString; entry_count: z.ZodNumber; entries: z.ZodArray; }, z.core.$strict>; /** Schema for `data/knowledge/changelog.yml`. */ declare const KnowledgeChangelogSchema: z.ZodObject<{ releases: z.ZodArray; name: z.ZodString; description: z.ZodString; }, z.core.$strict>>; entries_modified: z.ZodDefault>; entries_removed: z.ZodDefault>; origin: z.ZodOptional; }, z.core.$strict>>; }, z.core.$strict>; /** * Credential and secret redaction. * * Applied to every report and bug field before it is written to disk or handed * to a formatter. Patterns are ordered specific-first. Key/value patterns * require an actual `=` or `:` operator so ordinary prose ("Password field * accepts…", "the secret sauce") is left intact; card numbers are only redacted * when they pass a Luhn check, so millisecond timestamps and order ids survive. */ interface RedactOptions { emails?: boolean; } /** Redacts secrets, returning the cleaned text and which categories fired. */ declare function redact(text: string, opts?: RedactOptions): { text: string; redactions: string[]; }; /** True when the text contains any detectable secret. */ declare function containsSecrets(text: string): boolean; declare const REDACTION_CATEGORIES: string[]; declare function validateTargetConfig(filePath: string): ValidationResult; declare function validateKnowledgeEntry(filePath: string): ValidationResult; declare function validateDomainConfig(filePath: string): ValidationResult; /** * Cross-checks the whole knowledge base: manifest, releases, changelog, and the * consistency between the registry, the entry files and the computed stats. */ declare function validateKnowledgeBase(dataDir: string): ValidationResult[]; /** * Validates every config file under `dataDir` (targets, domains, knowledge * entries and the knowledge-base cross-check). Also validates a project-local * `qa/target.yml` when `cwd` is given and the file exists. */ declare function validateAllConfigs(dataDir: string, opts?: { cwd?: string; }): ValidationResult[]; /** * Appends a single session metrics record to the JSONL file. * Creates the output directory and file if they do not exist. */ declare function appendSessionMetrics(outputDir: string, metrics: SessionMetrics): void; /** * Reads all session metrics from the JSONL file. * Returns an empty array if the file does not exist or is empty. */ declare function readAllMetrics(outputDir: string): SessionMetrics[]; /** Appends metrics only when the session_id has not been recorded yet. */ declare function appendSessionMetricsDeduped(outputDir: string, metrics: SessionMetrics): boolean; /** * Session directory naming — the single scheme used by every session skill, * the `explore` CLI command, the report resolver and the list command. * * Scheme: output/sessions/--/ * Date-first so a lexicographic sort is chronological. */ declare const SESSION_KINDS: readonly ["explore", "quick", "mobile", "backend"]; type SessionKind = (typeof SESSION_KINDS)[number]; declare const SESSION_DIR_RE: RegExp; /** * DISCOVERY ONLY — directories written before the current scheme existed: * a `YYYY-MM-DD` prefix, an optional `HHmm`, then any remainder * (`2026-05-22-1045-demo-target`, `2026-07-09-quick-preprod-product-search`). * * Never use this to create or validate a new directory name: `SESSION_DIR_RE` * stays the only writer. It exists so `prune` and the unindexed scan can see * output an upgraded project already has on disk. */ declare const LEGACY_SESSION_DIR_RE: RegExp; /** * Turns an arbitrary target id, ticket or URL into a filesystem-safe slug: * lowercase, alphanumerics and single dashes, no leading/trailing dash, * capped at 40 chars. A full URL is reduced to its hostname first. */ declare function slugify(input: string): string; /** `2026-09-08-1813` for the given date (local time). */ declare function sessionTimestamp(date?: Date): string; /** Full session directory name, e.g. `2026-09-08-1813-explore-parabank`. */ declare function sessionDirName(kind: SessionKind, target: string, date?: Date): string; interface ParsedSessionDir { timestamp: string; kind: SessionKind; slug: string; } /** Parses a directory name back into its parts, or null if it does not match. */ declare function parseSessionDirName(name: string): ParsedSessionDir | null; interface DiscoveredSessionDir { /** The directory name as it is on disk. */ name: string; /** Start of the session in local time; midnight when the name carries no `HHmm`. */ timestamp: Date; /** True when the name predates the current `--` scheme. */ legacy: boolean; } /** * Describes a session directory found on disk — current scheme or legacy — so * `prune` and the unindexed scan can treat both. Returns null when the name * carries no date prefix at all, which is what keeps unrelated directories out. */ declare function describeSessionDir(name: string): DiscoveredSessionDir | null; /** * Minimal GitHub-flavoured-markdown table parser. * * Splits pipe-delimited rows and drops only the leading/trailing empty cells * produced by the outer pipes — internal empty cells are preserved, so column * indices stay aligned even when a cell is blank. */ interface ParsedTable { headers: string[]; rows: Record[]; } /** * Finds the first markdown table under a heading matching `headingPrefix` * (case-insensitive, matched at the start of the heading text), or the first * table in the document when no prefix is given. Returns null if none. * * Header cells are matched case-insensitively; the returned row objects are * keyed by the lower-cased header name. */ declare function parseMarkdownTable(md: string, opts?: { headingPrefix?: string; }): ParsedTable | null; /** * The single confidentiality header used on every generated artefact * (templates, session reports, formatter output). */ declare const CONFIDENTIALITY_LINES: readonly ["CONFIDENTIAL: This report may contain internal URLs, security vulnerabilities,", "and application details. Do not share outside your organization without review."]; /** Markdown blockquote form, e.g. for the top of a `.md` artefact. */ declare const CONFIDENTIALITY_HEADER_MD: string; /** Plain one-line form, e.g. for an HTML banner or a CSV/JSON note. */ declare const CONFIDENTIALITY_NOTICE: string; /** True when `text` already begins (ignoring blank lines) with the header. */ declare function hasConfidentialityHeader(text: string): boolean; /** * Canonical headers for the session index and consolidated bug list, shared by * `qualiow init` (which writes them) and `qualiow list` (which reads them). */ declare const INDEX_COLUMNS: readonly ["Date", "Kind", "Target", "Bugs", "Duration", "Status", "Report"]; declare const ALL_BUGS_COLUMNS: readonly ["ID", "Session", "Title", "Severity", "Status", "Report"]; declare const INDEX_MD_HEADER: string; declare const ALL_BUGS_MD_HEADER: string; /** * True when a table's own header is the canonical column set — same names in the * same order, compared case-insensitively. */ declare function headersMatchColumns(headers: readonly string[], columns: readonly string[]): boolean; /** * Canonical columns a table's header does not carry, in canonical order — what a * file written by an earlier version is missing. */ declare function missingColumns(headers: readonly string[], columns: readonly string[]): string[]; /** * Renders one row in a table's OWN column order, taking each cell from `values` * keyed by lower-cased column name. A column with no value is left empty, so an * index written by an earlier version keeps its width instead of gaining cells * that would shift every value one column to the right when read back. */ declare function buildTableRow(headers: readonly string[], values: Record): string; /** * .gitignore merge for `qualiow init`. * * Adds the Qualiow ignore block by exact-line comparison (so an unrelated * substring like `.env.example` never suppresses `.env`), writes the section * header at most once, and is idempotent: a second run adds nothing. */ declare const QUALIOW_GITIGNORE_HEADER = "# Qualiow"; declare const QUALIOW_GITIGNORE_ENTRIES: readonly [".auth/", ".env", "qa/.env", "output/sessions/*/", "!output/sessions/INDEX.md", "output/context/*.md", "data/targets/local-*.yml", ".playwright-cli/", "*.trace.zip", "*.webm"]; /** * Returns the new .gitignore content, or null when nothing needs adding. * Compares against existing lines exactly (trimmed of trailing whitespace). */ declare function mergeGitignore(existing: string): string | null; /** * Path resolution shared by the CLI commands. * * The package root is where `data/` and `package.json` live — the git checkout * in development, or `node_modules/qualiow-exploratory-testing/` when installed. */ /** Walk up from the built file looking for a dir with data/ and package.json. */ declare function getPackageRoot(): string; /** The `version` field of the package's own package.json (or "0.0.0"). */ declare function getPackageVersion(pkgRoot?: string): string; /** Where `init` copies skills from: the published `skills/` tree, or the * canonical `.claude/skills/` in a git checkout. */ declare function resolveSkillsSource(pkgRoot: string): string | null; /** * Resolve a target config path. With a name: `data/targets/.yml` under * cwd, else under the package root. Without a name: a project-local * `qa/target.yml`, else `data/targets/_default.yml`. Returns the first path * that exists, or the most-specific candidate when none exist (so callers can * report a useful "not found"). */ declare function resolveTargetPath(cwd: string, name: string | undefined, pkgRoot?: string): string; /** * Resolve the `data/` directory the read-only commands read from. * * Order, first existing wins: * 1. an explicit directory (`--data`), resolved against `cwd` * 2. `/data`, but only when it carries `knowledge/manifest.yml` — the * marker that this is a qualiow project rather than any `data/` folder * 3. `$CLAUDE_PLUGIN_ROOT/data`, set by Claude Code under a plugin install * 4. `/data`, the package's own shipped data * * An explicit directory is returned as given even when it does not exist, so a * typo surfaces as a "not found" naming the path the caller asked for instead * of silently falling back to the package data. */ declare function resolveDataDir(cwd: string, explicit?: string, pkgRoot?: string): string; /** Resolve a domain config, trying `.yml` then `_.yml`. */ declare function resolveDomainPath(dataDir: string, id: string): string; /** * Knowledge-base manifest synchronisation. * * Rebuilds the manifest `entries:` registry and `stats:` from the release * directories on disk, so the manifest can never drift from the files. Other * manifest nodes (version, active_releases, loading_strategy) and the file's * comments are preserved. */ interface RegistryEntry { id: string; file: string; type: string; priority: string; tags: string[]; domains: string[]; } interface ManifestStats { total_entries: number; heuristics: number; techniques: number; checklists: number; references: number; domain_profiles: number; custom_entries: number; } /** Sorted list of release dir names (v0.1.0 < v0.2.0 …). */ declare function listReleaseDirs(knowledgeDir: string): string[]; /** Builds the registry array from the entry files, release order then id. */ declare function buildManifestRegistry(dataDir: string): RegistryEntry[]; /** Computes stats from the registry, the domain files and the custom dir. */ declare function computeStats(dataDir: string, registry: RegistryEntry[]): ManifestStats; interface SyncResult { changed: boolean; stats: ManifestStats; entryCount: number; } /** * Rewrites (or, with `check`, only compares) the manifest's `entries` and * `stats` from disk. Returns whether the on-disk manifest was already in sync. */ declare function syncKnowledgeManifest(dataDir: string, opts?: { check?: boolean; }): SyncResult; /** * Session and bug report parser. * Reads a session directory and extracts structured data * from markdown files produced during exploratory testing. */ interface ParsedBug { id: string; title: string; severity: 'critical' | 'high' | 'medium' | 'low'; priority: string; component: string; url: string; environment: string; reproduction_rate: string; summary: string; expected: string; actual: string; steps: string[]; business_impact: string; evidence: { screenshots: string[]; videos: string[]; logs: string[]; console_errors: string[]; network_failures: string[]; }; } interface ParsedSession { id: string; charter?: string; bugs: ParsedBug[]; report?: string; phases: { name: string; content: string; }[]; } /** * Parses a single bug report markdown file into structured data. */ declare function parseBugReport(filePath: string): Promise; /** * Parses an entire session directory into structured data. * Reads the session report, charter, phase files, and all bug reports. */ declare function parseSession(sessionDir: string): Promise; /** * HTML report generator. * Produces a standalone dark-mode HTML report from session data. * The template is inline to simplify distribution. */ /** * Generates a self-contained dark-mode HTML report from a session directory. */ declare function generateHtmlReport(sessionDir: string): Promise; /** * JSON report generator. * Produces structured, redacted JSON from session data for programmatic * consumption. Carries a classification block so downstream tooling can * honour the confidentiality of the content. */ /** Generates a structured, redacted JSON report from a session directory. */ declare function generateJsonReport(sessionDir: string): Promise; /** * Jira CSV export generator. * Produces a CSV file importable via Jira bulk import. Every field is redacted * and formula-neutralised before it is written. */ /** * Escapes a value for a CSV field: always quoted, internal quotes doubled, * and a leading `= + - @ \t \r` neutralised with an apostrophe so the cell * is never evaluated as a formula when the file is opened in a spreadsheet. */ declare function csvEscape(value: string): string; /** * Generates a CSV string importable by Jira bulk import from a session directory. * * Columns: Summary, Priority, Description, Component, Labels. */ declare function generateJiraExport(sessionDir: string): Promise; /** * Markdown summary generator for `qualiow report -f md`. * Produces a short, redacted `session-summary.md` next to the session report * (never overwriting `session-report.md`). */ declare function generateMarkdownSummary(sessionDir: string): Promise; /** * Shared report-extraction helpers used by the HTML and JSON formatters. * Everything user-authored (the report body and every bug field) is redacted * on the way out. */ interface ReportMeta { target: string; date: string; duration: string; sessionId: string; summary: string; } interface CoverageEntry { area: string; risk: string; status: string; bugsFound: string; notes: string; } /** Loads a session and redacts its report body and every bug. */ declare function loadSessionForOutput(sessionDir: string): Promise<{ session: ParsedSession; report: string; }>; declare function extractReportMeta(report: string, sessionId: string): ReportMeta; /** * Extracts the coverage-map table. Accepts the canonical header * `| Area | Risk | Status | Bugs | Notes |` and the legacy `| Area | Status |`. */ declare function extractCoverage(report: string): CoverageEntry[]; declare function extractListSection(report: string, heading: string): string[]; interface InitOptions { includeExamples: boolean; force: boolean; dryRun: boolean; hooks?: boolean; } type CopyStatus = 'installed' | 'unchanged' | 'overwritten' | 'skipped'; interface CopyRecord { dest: string; status: CopyStatus; } interface InitResult { copies: CopyRecord[]; createdDirs: string[]; createdFiles: string[]; gitignoreUpdated: boolean; } declare function runInit(options: InitOptions, ctx: { cwd: string; pkgRoot?: string; log?: (line: string) => void; }): Promise; interface ExploreOptions { target?: string; context?: string; timeBox: string; dryRun: boolean; } declare function parseTimeBox(value: string): number; declare function runExplore(url: string | undefined, options: ExploreOptions, ctx: { cwd: string; now?: Date; log?: (line: string) => void; }): Promise<{ sessionDir: string; }>; interface ReportOptions { session: string; format: string; output?: string; stdout: boolean; } declare function runReport(options: ReportOptions, ctx: { cwd: string; log?: (line: string) => void; }): Promise<{ outputPath?: string; content: string; }>; /** * Resolves `latest`, an exact directory name, or a unique substring match. * Throws when nothing matches or when a substring matches more than one session. */ declare function resolveSessionDir(sessionsDir: string, sessionId: string): string; interface ListOptions { /** Knowledge only: keep entries carrying this domain (or `all`). */ domain?: string; /** Knowledge only: keep entries carrying this tag. */ tag?: string; /** Knowledge only: keep entries of this type. */ type?: string; /** Knowledge only: print one entry's YAML file instead of the listing. */ entry?: string; /** Knowledge only: print the release changelog instead of the listing. */ changelog?: boolean; /** Knowledge only: print stats, releases and loading-strategy counts. */ stats?: boolean; /** Knowledge only: read the knowledge base from this data directory. */ data?: string; } declare function runList(type: string, cwd: string, options?: ListOptions, log?: (line: string) => void): Promise; interface SessionRow { date: string; kind: string; target: string; bugs: string; duration: string; status: string; report: string; } /** Parses INDEX.md rows, ignoring placeholder rows whose first cell starts with `_`. */ declare function readSessionIndex(indexPath: string): SessionRow[]; interface ValidateOptions { targets?: boolean; knowledge?: boolean; domains?: boolean; kb?: boolean; all?: boolean; } /** Runs the selected validations, prints results, returns the failure count. */ declare function runValidate(options: ValidateOptions, cwd: string): number; declare function runKb(mode: 'sync' | 'check', cwd: string): SyncResult; interface KbDigestOptions { domain?: string; tag?: string[]; /** `--for `; commander stores it under the flag's own name. */ for?: string; entry?: string; data?: string; maxLines?: number; } /** * Builds the digest a session loads instead of reading the manifest and the * release files whole. Returns the text; the action prints it. */ declare function runKbDigest(options: KbDigestOptions, cwd: string): string; export { ALL_BUGS_COLUMNS, ALL_BUGS_MD_HEADER, type AnyTargetConfig, type AuthConfig, AuthConfigSchema, type BrowserConfig, BrowserConfigSchema, type BugReport, type BugSeverity, CONFIDENTIALITY_HEADER_MD, CONFIDENTIALITY_LINES, CONFIDENTIALITY_NOTICE, type CopyRecord, type CopyStatus, type CoverageEntry, type DiscoveredSessionDir, type DomainConfig, DomainConfigSchema, INDEX_COLUMNS, INDEX_MD_HEADER, type InitOptions, type InitResult, type Journey, JourneySchema, type KbDigestOptions, KnowledgeChangelogSchema, type KnowledgeEntry, type KnowledgeEntryContent, KnowledgeEntryContentSchema, KnowledgeEntrySchema, type KnowledgeManifest, type KnowledgeManifestEntry, KnowledgeManifestEntrySchema, KnowledgeManifestSchema, type KnowledgeManifestStats, KnowledgeManifestStatsSchema, KnowledgeReleaseSchema, LEGACY_SESSION_DIR_RE, type ListOptions, type LoadingStrategy, LoadingStrategySchema, type ManifestStats, type MobileAppConfig, MobileAppConfigSchema, type MobileDeviceConfig, MobileDeviceConfigSchema, type MobileScopeConfig, MobileScopeConfigSchema, type MobileTargetConfig, MobileTargetConfigSchema, type MobileWebConfig, MobileWebConfigSchema, type ParsedBug, type ParsedSession, type ParsedSessionDir, type ParsedTable, QUALIOW_GITIGNORE_ENTRIES, QUALIOW_GITIGNORE_HEADER, REDACTION_CATEGORIES, type RedactOptions, type RegistryEntry, type ReportMeta, type RiskRanking, SESSION_DIR_RE, SESSION_KINDS, type SafetyConfig, SafetyConfigSchema, type ScopeConfig, ScopeConfigSchema, type SessionKind, type SessionMetrics, SessionMetricsSchema, type SessionRow, type SeverityCounts, SeverityCountsSchema, type SourceRepoConfig, SourceRepoConfigSchema, type SyncResult, type TargetConfig, TargetConfigSchema, type ValidationError, type ValidationResult, WebTargetConfigSchema, appendSessionMetrics, appendSessionMetricsDeduped, buildManifestRegistry, buildTableRow, computeStats, containsSecrets, csvEscape, describeSessionDir, extractCoverage, extractListSection, extractReportMeta, generateHtmlReport, generateJiraExport, generateJsonReport, generateMarkdownSummary, getPackageRoot, getPackageVersion, hasConfidentialityHeader, headersMatchColumns, listReleaseDirs, loadSessionForOutput, mergeGitignore, missingColumns, parseBugReport, parseMarkdownTable, parseSession, parseSessionDirName, parseTimeBox, readAllMetrics, readSessionIndex, redact, resolveDataDir, resolveDomainPath, resolveSessionDir, resolveSkillsSource, resolveTargetPath, runExplore, runInit, runKb, runKbDigest, runList, runReport, runValidate, sessionDirName, sessionTimestamp, slugify, syncKnowledgeManifest, validateAllConfigs, validateDomainConfig, validateKnowledgeBase, validateKnowledgeEntry, validateTargetConfig };