# SkillPatch skill: hyperexecute-skill

This skill enables agents to operate HyperExecute end-to-end for cloud test execution on the LambdaTest grid. It covers analyzing projects, authoring and validating hyperexecute.yaml configs, running CLI jobs, debugging failures, summarizing artifacts, and wiring CI/CD pipelines. It supports Playwright, Cypress, Selenium, Pytest, and WebdriverIO frameworks with autosplit and matrix execution strategies.

You (the agent) were given this URL and asked to use this skill. This is a **public** skill — no SkillPatch account, API key, or prior setup is required. Two ways to use it:

**1. Use it right now** — the skill's complete file tree (SKILL.md + all reference files) is inlined below; read `SKILL.md` first, then follow it, consulting the other files as it directs.

**2. Install the exact package onto disk** (recommended if you can run a shell — this reproduces the skill byte-for-byte, including any binary assets that can't be inlined):

```bash
mkdir -p .claude/skills/hyperexecute-skill
curl -sSL https://skillpatch.dev/install_skill/hyperexecute-skill | tar -xz -C .claude/skills/
```

(`.claude/skills/` is Claude Code's convention; use whatever directory your agent loads skills from.)


---

## Skill files (11)

- `SKILL.md`
- `reference/ci-cd.md`
- `reference/cli.md`
- `reference/frameworks.md`
- `reference/security.md`
- `reference/troubleshooting.md`
- `reference/yaml-patterns.md`
- `scripts/build-command.js`
- `scripts/doctor.js`
- `scripts/summarize-artifacts.js`
- `scripts/validate-config.js`


### `SKILL.md`

```markdown
---
name: hyperexecute-skill
description: >
  Operates HyperExecute end-to-end for TestMu AI/LambdaTest cloud test
  execution: analyze projects, create YAML, validate locally, run CLI jobs,
  debug failures, and wire CI. Use when the user mentions HyperExecute,
  hyperexecute.yaml, HyperExecute CLI, autosplit, matrix execution,
  LambdaTest grid, cloud test runs, CI test orchestration, or migrating
  Playwright/Cypress/Selenium/Pytest/WebdriverIO tests to HyperExecute.
languages:
  - YAML
  - JavaScript
category: cloud-testing
license: MIT
metadata:
  author: TestMu AI
  version: "2.0"
---

# HyperExecute Operator

## Quick Start

1. Locate the HyperExecute CLI. If missing, ask before downloading it unless the user explicitly approved an autonomous HyperExecute session.
2. Run `hyperexecute analyze` when the CLI is available; use local inspection only as fallback.
3. Create or repair `hyperexecute.yaml` from the analyze output, project test commands, and templates in `reference/`.
4. Run `node scripts/doctor.js --config hyperexecute.yaml` and `node scripts/validate-config.js hyperexecute.yaml`.
5. Validate with the official CLI: `./hyperexecute --user "$LT_USERNAME" --key "$LT_ACCESS_KEY" --config hyperexecute.yaml --validate`.
6. Ask before a real cloud job unless the user has explicitly opted into an autonomous HyperExecute session.
7. For failures, download logs/artifacts/reports and use `reference/troubleshooting.md`.

## Operating Rules

- Treat the official HyperExecute CLI as the source of truth for analyze, validation, execution, logs, reports, and artifacts.
- Use `LT_USERNAME` and `LT_ACCESS_KEY` from local environment variables or CI secrets; never hardcode credentials in YAML or docs.
- Use `--job-secret-file` only for extra job-scoped secrets, preferably outside the repo or ignored by `.gitignore`/`.hyperexecuteignore`.
- Prefer template-driven YAML over generator scripts because test commands, paths, and payload boundaries are project-specific.
- Run safe local checks automatically; run real HyperExecute cloud jobs only after confirmation unless the user opted into autonomous mode.
- In autonomous mode, validate first, run, inspect output, download logs/artifacts when useful, and retry only for actionable config/environment fixes.

## Workflow

- First run: analyze project, author YAML, run helper checks, run CLI validate, then request confirmation for the cloud job.
- Debug: reproduce the failing CLI command, add `--verbose` when useful, download logs/artifacts/reports, fix one cause at a time.
- CI: use CI secrets, add a validation stage before execution, set `CI=true` for quieter logs, and keep downloaded artifacts available for failed jobs.
- Performance: tune `autosplit`, `concurrency`, cache keys, retries, smart ordering, and matrix/hybrid scope after one successful run.

## Helper Scripts

- `scripts/doctor.js`: checks CLI readiness, credentials, config presence, and optional official validation.
- `scripts/validate-config.js`: lightweight config linting for common mistakes before official CLI validation.
- `scripts/build-command.js`: prints safe validate/run/debug/download commands using environment variable references.
- `scripts/summarize-artifacts.js`: summarizes downloaded logs, reports, and artifacts for triage.

## References

- CLI usage and flags: [reference/cli.md](reference/cli.md)
- YAML patterns: [reference/yaml-patterns.md](reference/yaml-patterns.md)
- Framework recipes: [reference/frameworks.md](reference/frameworks.md)
- CI/CD integration: [reference/ci-cd.md](reference/ci-cd.md)
- Security rules: [reference/security.md](reference/security.md)
- Troubleshooting: [reference/troubleshooting.md](reference/troubleshooting.md)

```


### `reference/ci-cd.md`

````markdown
# CI/CD Integration

Use CI secrets for `LT_USERNAME` and `LT_ACCESS_KEY`. Add a validation stage before the real job.

## GitHub Actions

```yaml
name: HyperExecute

on:
  push:
    branches: [main]
  pull_request:

jobs:
  hyperexecute:
    runs-on: ubuntu-latest
    env:
      CI: "true"
      LT_USERNAME: ${{ secrets.LT_USERNAME }}
      LT_ACCESS_KEY: ${{ secrets.LT_ACCESS_KEY }}
    steps:
      - uses: actions/checkout@v4
      - name: Download HyperExecute CLI
        run: |
          curl -O https://downloads.lambdatest.com/hyperexecute/linux/hyperexecute
          chmod u+x ./hyperexecute
      - name: Validate HyperExecute config
        run: ./hyperexecute --user "$LT_USERNAME" --key "$LT_ACCESS_KEY" --config hyperexecute.yaml --validate
      - name: Run HyperExecute job
        run: ./hyperexecute --user "$LT_USERNAME" --key "$LT_ACCESS_KEY" --config hyperexecute.yaml --download-logs --download-artifacts
```

## Jenkins

```groovy
pipeline {
  agent any
  environment {
    CI = 'true'
    LT_USERNAME = credentials('lt-username')
    LT_ACCESS_KEY = credentials('lt-access-key')
  }
  stages {
    stage('Download HyperExecute CLI') {
      steps {
        sh 'curl -O https://downloads.lambdatest.com/hyperexecute/linux/hyperexecute && chmod u+x ./hyperexecute'
      }
    }
    stage('Validate HyperExecute config') {
      steps {
        sh './hyperexecute --user "$LT_USERNAME" --key "$LT_ACCESS_KEY" --config hyperexecute.yaml --validate'
      }
    }
    stage('Run HyperExecute job') {
      steps {
        sh './hyperexecute --user "$LT_USERNAME" --key "$LT_ACCESS_KEY" --config hyperexecute.yaml --download-logs --download-artifacts'
      }
    }
  }
}
```

## CI Rules

- Keep credentials in CI secrets, not YAML.
- Prefer validation before execution.
- Set `CI=true` for quieter logs.
- Download logs and artifacts on failing workflows.
- Use `--target-path` for monorepos to avoid uploading unrelated files.

````


### `reference/cli.md`

````markdown
# HyperExecute CLI

Use the official HyperExecute CLI for project analysis, YAML validation, job execution, and result collection.

## Download

Ask before downloading the CLI unless the user explicitly approved an autonomous HyperExecute session.

| Platform | URL |
| --- | --- |
| Linux | `https://downloads.lambdatest.com/hyperexecute/linux/hyperexecute` |
| macOS | `https://downloads.lambdatest.com/hyperexecute/darwin/hyperexecute` |
| Windows | `https://downloads.lambdatest.com/hyperexecute/windows/hyperexecute.exe` |

After download on Linux/macOS, run `chmod u+x ./hyperexecute`.

## Verify Binary

- Linux: download the official signature and public key, then run `openssl dgst -sha256 -verify <PUBLIC_KEY_PATH> -signature <SIGNATURE_PATH> <CLI_BINARY_PATH>`.
- macOS: run `codesign -dvvv <PATH_TO_CLI>` and inspect the signer.
- Windows: open file properties, inspect Digital Signatures, and verify the publisher.

## Core Commands

```bash
./hyperexecute analyze
./hyperexecute --user "$LT_USERNAME" --key "$LT_ACCESS_KEY" --config hyperexecute.yaml --validate
./hyperexecute --user "$LT_USERNAME" --key "$LT_ACCESS_KEY" --config hyperexecute.yaml
./hyperexecute --user "$LT_USERNAME" --key "$LT_ACCESS_KEY" --config hyperexecute.yaml --verbose
```

## Useful Flags

| Flag | Use |
| --- | --- |
| `analyze` | Detect language, framework, and environment details. |
| `--validate` | Validate YAML without running tests. |
| `--config` | Point to the HyperExecute YAML file. |
| `--user`, `--key` | Pass credentials from `LT_USERNAME` and `LT_ACCESS_KEY`. |
| `--runson` | Override OS; use comma-separated values for matrix/hybrid. |
| `--concurrency` | Override concurrent sessions. |
| `--vars` | Pass non-secret runtime variables. |
| `--job-secret-file` | Pass additional job-scoped secrets. |
| `--target-path` | Upload selected payload paths. |
| `--use-zip` | Upload an existing zip payload. Ensure `hyperexecute.yaml` is at zip root. |
| `--download-logs` | Download console logs for the job. |
| `--download-artifacts` | Download framework artifacts. |
| `--download-artifacts-path` | Choose artifact download directory. |
| `--download-artifacts-zip` | Download artifacts as a zip. |
| `--download-report` | Download reports. |
| `--force-clean-artifacts` | Replace stale local artifacts with current artifacts. |
| `--auto-proxy` | Use detected system proxy settings. |
| `--scan` | Show network logs locally. |
| `--tests-per-tunnel` | Limit tests through a tunnel. |
| `--no-track` | Disable job progress tracking. |
| `update` | Update the CLI binary. |
| `completion` | Generate shell completion. |

## Quiet CI Logs

Set `CI=true` before execution when CI logs are too noisy.

## Minimal Images

On Alpine/minimal Linux images, ensure required system tools are present, commonly `libc6-compat`, `git`, and `bash`.

````


### `reference/frameworks.md`

````markdown
# Framework Recipes

Run `hyperexecute analyze` first when possible. Use these recipes to author or repair `hyperexecute.yaml`.

## Playwright

Signals: `playwright.config.*`, `@playwright/test`, `tests/**/*.spec.*`.

```yaml
pre:
  - npm ci
  - npx playwright install --with-deps
testDiscovery:
  type: raw
  mode: dynamic
  command: find tests -name '*.spec.ts' -o -name '*.spec.js'
testRunnerCommand: npx playwright test $test --reporter=list
framework:
  name: playwright
uploadArtefacts:
  - name: playwright-output
    path: [playwright-report/**, test-results/**]
```

Common fixes: install browsers in `pre`, constrain projects with `--project`, upload `test-results/**` for traces.

## Cypress

Signals: `cypress.config.*`, `cypress/e2e`, `cypress/integration`.

```yaml
pre:
  - npm ci
  - npx cypress install
testDiscovery:
  type: raw
  mode: dynamic
  command: find cypress/e2e -name '*.cy.js' -o -name '*.cy.ts'
testRunnerCommand: npx cypress run --spec $test
framework:
  name: cypress
uploadArtefacts:
  - name: cypress-output
    path: [cypress/screenshots/**, cypress/videos/**, cypress/reports/**]
```

Common fixes: verify spec path quoting, install Cypress binary, upload screenshots/videos.

## Selenium Java Maven

Signals: `pom.xml`, `src/test/java`, JUnit/TestNG dependencies.

```yaml
pre:
  - mvn -Dmaven.test.skip=true clean install
testDiscovery:
  type: raw
  mode: dynamic
  command: find src/test/java -name '*Test.java'
testRunnerCommand: mvn test -Dtest=$test
framework:
  name: selenium
uploadArtefacts:
  - name: java-test-output
    path: [target/surefire-reports/**, target/failsafe-reports/**, target/allure-results/**]
```

Common fixes: align discovery output with `-Dtest`, handle modules with `-pl`, use TestNG suite XML when classes are not enough.

## Pytest

Signals: `pytest.ini`, `pyproject.toml`, `requirements.txt`, `tests/test_*.py`.

```yaml
pre:
  - pip install -r requirements.txt
testDiscovery:
  type: raw
  mode: dynamic
  command: find tests -name 'test_*.py' -o -name '*_test.py'
testRunnerCommand: pytest $test -v
uploadArtefacts:
  - name: pytest-output
    path: [reports/**, htmlcov/**, .pytest_cache/**]
```

Common fixes: install browser/system dependencies for Selenium or Playwright Python tests, verify marker filters.

## WebdriverIO

Signals: `wdio.conf.*`, `@wdio/cli`, `specs` in config.

```yaml
pre:
  - npm ci
testDiscovery:
  type: raw
  mode: dynamic
  command: find test specs tests -name '*.spec.js' -o -name '*.spec.ts'
testRunnerCommand: npx wdio run wdio.conf.js --spec $test
framework:
  name: webdriverio
uploadArtefacts:
  - name: wdio-output
    path: [allure-results/**, reports/**, screenshots/**]
```

Common fixes: avoid excessive WebDriver calls, use stable selectors, and preserve screenshots/reports for failures.

````


### `reference/security.md`

```markdown
# Security

## Credentials

- Read `LT_USERNAME` and `LT_ACCESS_KEY` from environment variables or CI secrets.
- Never hardcode access keys in `hyperexecute.yaml`, shell scripts, docs, or committed `.env` files.
- Do not print secret values. Commands should reference `$LT_USERNAME` and `$LT_ACCESS_KEY` rather than embedding values.

## Job Secret Files

Use `--job-secret-file` for extra job-scoped secrets that should not appear in YAML or `--vars`.

Recommended:

- Store the file outside the repository when possible.
- If it must be inside the repo, add it to `.gitignore` and `.hyperexecuteignore`.
- Treat the file as sensitive and never commit sample real values.

## CLI Binary

Downloading the CLI crosses a binary trust boundary. Ask before download unless the user approved an autonomous HyperExecute session.

Verify the binary when security matters:

- Linux: verify signature with `openssl`.
- macOS: inspect code signing with `codesign -dvvv`.
- Windows: verify the digital signature publisher.

## Payload Hygiene

- Use `.hyperexecuteignore` to keep local secrets, large artifacts, and unrelated directories out of uploaded payloads.
- In monorepos, prefer `--target-path` to limit uploaded files.
- When using `--use-zip`, ensure `hyperexecute.yaml` is at the root of the unzipped payload.

```


### `reference/troubleshooting.md`

```markdown
# Troubleshooting

Debug one failure class at a time. Prefer official CLI validation before cloud execution.

| Symptom | Likely Cause | Action |
| --- | --- | --- |
| CLI not executable | Missing execute bit | Run `chmod u+x ./hyperexecute`. |
| macOS blocks CLI | Gatekeeper/security prompt | Allow the binary in Security & Privacy, then retry. |
| Alpine/minimal image fails | Missing system libraries/tools | Install `libc6-compat`, `git`, and `bash`. |
| Credentials missing | Env/CI secrets absent | Export `LT_USERNAME` and `LT_ACCESS_KEY` or configure CI secrets. |
| YAML validation fails | Missing/invalid YAML fields | Check `version`, `runson`, and either `testSuites` or `testDiscovery` plus `testRunnerCommand`. |
| No tests discovered | Discovery command returns empty | Run the discovery command locally and fix paths/globs. |
| Browser not found | Browser install missing | Add Playwright/Cypress browser install commands to `pre`. |
| Payload cannot find config | Bad zip layout | Put `hyperexecute.yaml` at the root of the zip payload. |
| Proxy/network failure | Corporate proxy/firewall | Try `--auto-proxy`, `--scan`, or tunnel/proxy options. |
| Artifacts missing | Wrong upload/download paths | Verify framework output paths and `uploadArtefacts` globs. |
| Logs too noisy in CI | Normal CLI progress output | Set `CI=true`. |
| Matrix too large | Cross-product explosion | Reduce dimensions or use narrower configs. |
| Autosplit uneven | Large test files or poor discovery unit | Split large files or use finer discovery units. |

## Debug Loop

1. Run `node scripts/doctor.js --config hyperexecute.yaml`.
2. Run `node scripts/validate-config.js hyperexecute.yaml`.
3. Run official validation with `--validate`.
4. Re-run with `--verbose` only when extra logs are useful.
5. Download logs/artifacts/reports after a real job failure.
6. Fix the smallest likely cause and retry only after validation passes.

```


### `reference/yaml-patterns.md`

````markdown
# YAML Patterns

Use these as templates, not generated truth. Adapt commands to the project.

## Autosplit

```yaml
version: 0.1
runson: linux
concurrency: 10
autosplit: true
retryOnFailure: true
maxRetries: 2

pre:
  - npm ci

testDiscovery:
  type: raw
  mode: dynamic
  command: find tests -name '*.spec.ts'

testRunnerCommand: npx playwright test $test
```

Use autosplit when tests can be split by file, class, or spec path.

## Matrix

```yaml
version: 0.1
runson: linux
concurrency: 10

matrix:
  browser: [chromium, firefox, webkit]

testSuites:
  - npx playwright test --project=$browser
```

Use matrix for browser, OS, environment, or capability combinations. Keep the cross-product small until one run succeeds.

## Manual Suites

```yaml
version: 0.1
runson: linux
concurrency: 3
autosplit: false

testSuites:
  - npm run test:smoke
  - npm run test:checkout
  - npm run test:account
```

Use manual suites when discovery is unreliable or suites map to business flows.

## Setup, Cache, And Artifacts

```yaml
pre:
  - npm ci

cacheKey: npm-{{ checksum "package-lock.json" }}
cacheDirectories:
  - node_modules

uploadArtefacts:
  - name: test-output
    path:
      - playwright-report/**
      - test-results/**
```

Only cache directories that are safe to reuse between jobs.

## Tunnel

```yaml
tunnel: true
tunnelOpts:
  global: true
```

Use tunnel for private staging environments. For corporate networks, combine with proxy options or CLI `--auto-proxy`.

## Timeouts

```yaml
globalTimeout: 120
testSuiteTimeout: 90
testSuiteStep: 90
```

Increase timeouts after confirming whether failures are slow tests, dependency setup, or cloud infrastructure delays.

````


### `scripts/build-command.js`

```
#!/usr/bin/env node

const path = require('path');

const args = process.argv.slice(2);
const action = args[0] || 'run';

function readFlag(name, fallback) {
  const index = args.indexOf(name);
  return index >= 0 && args[index + 1] ? args[index + 1] : fallback;
}

function hasFlag(name) {
  return args.includes(name);
}

function quote(value) {
  return `'${String(value).replace(/'/g, `'"'"'`)}'`;
}

const cli = readFlag('--cli', './hyperexecute');
const config = readFlag('--config', 'hyperexecute.yaml');
const artifactPath = readFlag('--download-artifacts-path', 'artifacts');
const secretFile = readFlag('--job-secret-file', '');
const runson = readFlag('--runson', '');
const concurrency = readFlag('--concurrency', '');

const command = [quote(cli), '--user "$LT_USERNAME"', '--key "$LT_ACCESS_KEY"'];

if (action === 'analyze') {
  console.log(`${quote(cli)} analyze`);
  process.exit(0);
}

command.push('--config', quote(config));

if (action === 'validate') command.push('--validate');
if (action === 'debug') command.push('--verbose', '--download-logs', '--download-report', '--download-artifacts');
if (action === 'download') command.push('--download-logs', '--download-report', '--download-artifacts', '--download-artifacts-path', quote(artifactPath));
if (secretFile) command.push('--job-secret-file', quote(secretFile));
if (runson) command.push('--runson', quote(runson));
if (concurrency) command.push('--concurrency', quote(concurrency));
if (hasFlag('--auto-proxy')) command.push('--auto-proxy');
if (hasFlag('--scan')) command.push('--scan');
if (hasFlag('--no-track')) command.push('--no-track');

if (!['run', 'validate', 'debug', 'download'].includes(action)) {
  console.error(`Unknown action: ${action}`);
  console.error('Usage: node scripts/build-command.js [analyze|validate|run|debug|download] --config hyperexecute.yaml');
  process.exit(1);
}

console.log(command.join(' '));

if (path.basename(config) !== config) {
  console.error('Note: config path includes directories; ensure payload layout matches CLI upload options.');
}

```


### `scripts/doctor.js`

```
#!/usr/bin/env node

const fs = require('fs');
const path = require('path');
const { spawnSync } = require('child_process');

const args = process.argv.slice(2);

function readFlag(name, fallback) {
  const index = args.indexOf(name);
  return index >= 0 && args[index + 1] ? args[index + 1] : fallback;
}

function hasFlag(name) {
  return args.includes(name);
}

function ok(message) {
  console.log(`OK: ${message}`);
}

function warn(message) {
  console.warn(`WARN: ${message}`);
}

function fail(message) {
  console.error(`ERROR: ${message}`);
  process.exitCode = 1;
}

const cli = readFlag('--cli', './hyperexecute');
const config = readFlag('--config', 'hyperexecute.yaml');
const cliPath = path.resolve(cli);
const configPath = path.resolve(config);

if (fs.existsSync(cliPath)) {
  ok(`HyperExecute CLI found at ${cli}`);
  try {
    fs.accessSync(cliPath, fs.constants.X_OK);
    ok('HyperExecute CLI is executable');
  } catch {
    fail(`HyperExecute CLI is not executable. Run: chmod u+x ${cli}`);
  }
} else {
  warn(`HyperExecute CLI not found at ${cli}. Ask before downloading unless autonomous mode was approved.`);
}

if (fs.existsSync(configPath)) ok(`Config file found at ${config}`);
else fail(`Config file not found at ${config}`);

if (process.env.LT_USERNAME) ok('LT_USERNAME is set');
else warn('LT_USERNAME is not set. Export it locally or configure CI secrets before official validation/run.');

if (process.env.LT_ACCESS_KEY) ok('LT_ACCESS_KEY is set');
else warn('LT_ACCESS_KEY is not set. Export it locally or configure CI secrets before official validation/run.');

if (fs.existsSync(path.resolve('.hyperexecuteignore'))) ok('.hyperexecuteignore exists');
else warn('No .hyperexecuteignore found. Consider excluding secrets, local artifacts, and unrelated monorepo files.');

if (fs.existsSync(path.resolve('.gitignore'))) ok('.gitignore exists');
else warn('No .gitignore found. Ensure local secrets are not committed.');

if (hasFlag('--validate-cli')) {
  if (!fs.existsSync(cliPath)) fail('Cannot run official validation because CLI is missing.');
  else if (!process.env.LT_USERNAME || !process.env.LT_ACCESS_KEY) fail('Cannot run official validation because credentials are missing.');
  else if (!fs.existsSync(configPath)) fail('Cannot run official validation because config is missing.');
  else {
    const result = spawnSync(cliPath, ['--user', process.env.LT_USERNAME, '--key', process.env.LT_ACCESS_KEY, '--config', config, '--validate'], {
      stdio: 'inherit',
      shell: false,
    });
    if (result.status === 0) ok('Official HyperExecute validation passed');
    else fail(`Official HyperExecute validation failed with exit code ${result.status}`);
  }
}

if (process.exitCode) {
  console.error('HyperExecute doctor found blocking issues.');
} else {
  console.log('HyperExecute doctor finished without blocking issues.');
}

```


### `scripts/summarize-artifacts.js`

```
#!/usr/bin/env node

const fs = require('fs');
const path = require('path');

const root = path.resolve(process.argv[2] || 'artifacts');
const interesting = /(?:log|report|result|junit|surefire|allure|playwright|cypress|screenshot|video|trace)/i;
const summary = {
  root,
  files: 0,
  directories: 0,
  bytes: 0,
  extensions: {},
  interesting: [],
};

function walk(current) {
  const entries = fs.readdirSync(current, { withFileTypes: true });
  for (const entry of entries) {
    const fullPath = path.join(current, entry.name);
    if (entry.isDirectory()) {
      summary.directories += 1;
      walk(fullPath);
      continue;
    }
    if (!entry.isFile()) continue;

    const stat = fs.statSync(fullPath);
    const ext = path.extname(entry.name).toLowerCase() || '(none)';
    const relative = path.relative(root, fullPath);

    summary.files += 1;
    summary.bytes += stat.size;
    summary.extensions[ext] = (summary.extensions[ext] || 0) + 1;

    if (interesting.test(relative) && summary.interesting.length < 25) {
      summary.interesting.push(`${relative} (${stat.size} bytes)`);
    }
  }
}

if (!fs.existsSync(root)) {
  console.error(`ERROR: Artifact path not found: ${root}`);
  process.exit(1);
}

walk(root);

console.log(`Artifact root: ${summary.root}`);
console.log(`Directories: ${summary.directories}`);
console.log(`Files: ${summary.files}`);
console.log(`Bytes: ${summary.bytes}`);
console.log('Extensions:');
for (const [ext, count] of Object.entries(summary.extensions).sort()) {
  console.log(`  ${ext}: ${count}`);
}

if (summary.interesting.length) {
  console.log('Interesting files:');
  for (const file of summary.interesting) console.log(`  ${file}`);
} else {
  console.log('Interesting files: none matched known log/report/artifact names.');
}

```


### `scripts/validate-config.js`

```
#!/usr/bin/env node

const fs = require('fs');
const path = require('path');

const configPath = process.argv[2] || 'hyperexecute.yaml';
const absolutePath = path.resolve(configPath);

function fail(message) {
  console.error(`ERROR: ${message}`);
  process.exitCode = 1;
}

function warn(message) {
  console.warn(`WARN: ${message}`);
}

function hasKey(content, key) {
  return new RegExp(`^\\s*${key}\\s*:`, 'm').test(content);
}

function keyValue(content, key) {
  const match = content.match(new RegExp(`^\\s*${key}\\s*:\\s*(.+?)\\s*$`, 'm'));
  return match ? match[1].replace(/^['"]|['"]$/g, '') : '';
}

if (!fs.existsSync(absolutePath)) {
  fail(`Config file not found: ${configPath}`);
  process.exit();
}

const content = fs.readFileSync(absolutePath, 'utf8');

if (!hasKey(content, 'version')) fail('Missing required key: version');
if (!hasKey(content, 'runson')) warn('Missing runson; CLI --runson may override it, but YAML should usually declare it.');

const hasTestSuites = hasKey(content, 'testSuites');
const hasDiscovery = hasKey(content, 'testDiscovery');
const hasRunner = hasKey(content, 'testRunnerCommand');

if (!hasTestSuites && !(hasDiscovery && hasRunner)) {
  fail('Expected either testSuites or testDiscovery plus testRunnerCommand.');
}

if (hasDiscovery && !hasRunner) fail('testDiscovery is present but testRunnerCommand is missing.');

const concurrency = Number(keyValue(content, 'concurrency'));
if (hasKey(content, 'concurrency') && (!Number.isInteger(concurrency) || concurrency < 1)) {
  fail('concurrency should be a positive integer.');
}

if (!hasKey(content, 'pre')) warn('No pre step found; ensure dependencies and browsers are available in the HyperExecute environment.');
if (!hasKey(content, 'uploadArtefacts') && !hasKey(content, 'uploadArtifacts')) warn('No artifact upload paths found; debugging failed jobs may be harder.');
if (hasKey(content, 'retryOnFailure') && !hasKey(content, 'maxRetries')) warn('retryOnFailure is set but maxRetries is missing.');

if (/LT_ACCESS_KEY\s*:\s*[^\s$][^\n]*/.test(content)) {
  fail('LT_ACCESS_KEY appears to be hardcoded. Use ${LT_ACCESS_KEY} or CI secrets.');
}

if (/LT_USERNAME\s*:\s*[^\s$][^\n]*/.test(content)) {
  warn('LT_USERNAME appears to be hardcoded. Prefer ${LT_USERNAME} or CI secrets.');
}

if (/access[_-]?key\s*[:=]\s*['"]?[A-Za-z0-9_-]{12,}/i.test(content)) {
  fail('Possible access key detected in config. Remove secrets from YAML.');
}

if (process.exitCode) {
  console.error('HyperExecute config lint failed. Run the official CLI with --validate after fixing these issues.');
} else {
  console.log('HyperExecute config lint passed. Next: run official CLI validation with --validate.');
}

```
