Skip to content

Installing Thomas in another project

Thomas's step implementations are plain TypeScript, loaded directly by tsx's ESM loader — there is no compiled dist/ build to consume. That means a consuming project can point its own cucumber.mjs at the installed package's .ts sources the same way Thomas points at its own.

Install

This package is not published to a registry yet — install it as a git dependency:

npm install github:DeepSpaceCartel/thomas

Installing the agent skill

A separate, lighter-weight install: the thomas agent skill (the step catalog and extending methodology, written for an agent rather than for tsx/cucumber) can be pulled into another project's own skill directories via the skills CLI:

npx skills add DeepSpaceCartel/thomas

This complements the npm install above rather than replacing it — it gets an agent working in the consuming project the same guidance an agent gets working in this one, but assumes that project follows the same layout (features/, charts/, docs/reference/*) this skill's references point at.

Wire up your own cucumber.mjs

import { register } from 'tsx/esm/api';
register();

export default {
  import: [
    'node_modules/thomas/features/support/**/*.ts',
    'node_modules/thomas/features/step_definitions/**/*.ts',
    'features/step_definitions/**/*.ts', // your own project-specific steps
  ],
  paths: ['features/**/*.feature'],
};

Your project needs @cucumber/cucumber and tsx installed directly (Thomas declares both as peerDependencies, not bundled dependencies, so your project's own versions are what actually run).

Install with npm install --install-links (or set install-links=true in your project's .npmrc, so a plain npm install keeps doing the right thing) if you're consuming Thomas via a local file: path (e.g. co-developing it alongside your project) rather than a git/registry install. A plain file: dependency is a raw symlink — if Thomas's own node_modules happens to have @cucumber/cucumber installed too (true whenever Thomas's own test suite has been run standalone), your project ends up loading two separate @cucumber/cucumber instances, and step registration silently attaches to the wrong one ("You're calling functions ... on an instance of Cucumber that isn't running"). Confirmed live consuming Thomas as "thomas": "file:../../thomas" from a sibling project — --install-links materializes a real, package.json "files"-scoped copy instead of a symlink, which sidesteps this entirely. A registry/git install doesn't hit this (no local node_modules to leak through), but --install-links is a harmless no-op there.

Using only some of the steps

The exports map lets you cherry-pick a single domain instead of importing everything — for example, a project that only needs the Helm steps:

export default {
  import: [
    'node_modules/thomas/features/support/helm/**/*.ts',
    'node_modules/thomas/features/support/resources/**/*.ts', // Directory/File/URL/OCI Artifact - see docs/reference/RESOURCES.md
    'node_modules/thomas/features/support/{assert_condition,query,run_command,attempt,world}.ts',
    'node_modules/thomas/features/step_definitions/{directory,helm,helm-repo,helm-release,common}.step.ts',
  ],
  paths: ['features/**/*.feature'],
};

Pointing steps at your own resources

Every alias's PROPERTY rows are just data — point Directory/File paths, HelmRelease.namespace, and Deployment/Service/Pod label selectors at your own project's charts and cluster namespace. Nothing in the step implementations hardcodes Thomas's own charts/ fixtures or the thomas-helm-test namespace; those only appear in Thomas's own .feature files.

See BDD conventions and the per-tool references (Helm, Kubernetes, REST) for what's available once wired up.