Contributing

Running from Source

In development, use tsx to run TypeScript directly:

npm run dev                          # Runs: npx tsx src/cli.ts
npm run dev -- hello --name World    # Pass args after --

Set AGENT_APPS_ROOT to your local checkout so Desktop, Extension, and the contrib skill find your development build:

export AGENT_APPS_ROOT=/path/to/your/AgentApps

The skills/ skills are written in TypeScript and compiled by tsc during the build. In dev mode, tsx handles them transparently.

The Build

npm run build

npm run build does three things:

  1. tsc — compiles src/**/*.ts and skills/**/*.ts to JavaScript in dist/. The rewriteRelativeImportExtensions option rewrites .ts.js in import paths.
  2. Copy non-TS files — copies non-TypeScript files from skills/ into dist/skills/ (e.g., default.skill.md). TypeScript source files are not copied — tsc already compiled them to .js.
  3. chmod +x dist/src/cli.js — makes the CLI entry point executable.

The result: dist/ contains pure compiled JavaScript that runs on Node 22+ with no flags or transpilers.

Writing skills/ Skills

Bundled skills in skills/ are TypeScript. They use .ts imports (which tsc rewrites to .js in output). All imports use .ts extensions in source. See the Conventions specification for directory structure, import rules, and module boundaries.

Running Tests

Work fast-feedback-first. Coverage levels from fastest to most thorough:

npm run check                        # TypeScript + Biome + knip + eslint + madge (~10s)
npm run test:suite:smoke             # Unit tests (~23s)
npm run test:suite:regression        # Unit + integration (~3m)
npm run build                        # Required before acceptance tests
npm run test:suite:all        # Regression + build + acceptance (~10m)
npm run test:acceptance              # Vitest acceptance tests, real CLI end-to-end (~2.5 min)

Individual tiers:

npm run test:unit                    # Unit tests with coverage
npm run test:integration             # Integration tests (real bootstrap + disk I/O)
npm run test:acceptance              # Acceptance tests (real CLI end-to-end)

Tag-based filtering (vitest 4.1 native tags):

npx vitest --tags-filter="unit"                    # just unit tests
npx vitest --tags-filter="!external && !slow"      # fast local tests
npx vitest --tags-filter="integration && network"  # port-using integration
npx vitest --list-tags                             # show all defined tags

Code Quality Tools

All five tools run in parallel via lefthook on every git commit:

Known limitation: Lefthook runs npm run check against the working tree, not the staged files. If you have unstaged changes that fix (or break) something, the check result reflects the working tree — not what you're actually committing. This is inherent to running whole-project tools (tsc, knip) that need the full codebase. Rely on CI for the authoritative staged-files check.

Tool What it does How to run
TypeScript strict Type errors at compile time npm run check
Biome Linting + formatting npm run check / npm run lint:fix
Knip Unused exports, deps, files npm run check
ESLint Type-aware rules (floating promises, await-thenable) npm run check
madge Circular dependency detection npm run check
vitest coverage (v8) Line/branch/function/statement thresholds npm run test:unit (always-on)

Additional tools (manual)

Tool What it does How to run
attw Validates package exports match TS types npx attw --pack
npm audit Known CVE detection in dependencies npm audit --omit=dev

Known blockers

  • npm audit: Unfixable critical vuln in fast-xml-parser (transitive via @strands-agents/sdk → AWS SDK). Once upstream fixes, add to check.

Test Tags

Tests are tagged using vitest 4.1 native tags (vitest.config.ts). Tags control filtering, timeouts, and retry behavior:

Tag Meaning
unit Pure logic, mocked dependencies
integration Real bootstrap + disk I/O
acceptance CLI acceptance tests against dist/
external Requires AWS credentials (Bedrock, S3, SES)
slow Takes >30s per test (hub, compile)
flaky Known intermittent — auto-retried 2× with 1s delay
network Uses network ports (web, websocket)

Tag a new test with { tags: ['unit', 'slow'] } in the test options, or use /** @module-tag unit */ JSDoc at the top of a file to tag all tests in that file. strictTags: true catches typos.

Source vs. Compiled

Context TypeScript? How it runs
Development (npm run dev) Yes tsx handles .ts imports
Unit/integration tests Yes vitest handles .ts imports
Production (dist/) No Pure compiled JavaScript
E2E/acceptance tests No Exercise the built dist/src/cli.js
User projects .skill.js by default Users can use .skill.ts with their own build step

Debugging with --log

The --log flag configures logging. It's repeatable and additive:

npx tsx src/cli.ts --log level=debug hello --name World   # Debug logs
npx tsx src/cli.ts --log scope=pipeline hello --name World # Filter to pipeline
npx tsx src/cli.ts --log reporter=json hello --name World  # Structured JSON output
npx tsx src/cli.ts --log                                   # List scopes and reporters
npx tsx src/cli.ts -vvvv hello --name World                # Shorthand for trace

Available reporters: json, agent, pipeline, events, cascade, tools, status, mcp-log. Reporters run alongside the default stderr output.

Available scopes: user, pipeline, agent, cascade, cycle, lookup, events, gateway, mcp, boot, cli, config, delegate, schedule, observe, trust, directive, annotations, deploy, hub, roles.

Bare --log <scope> is shorthand for --log scope=<scope>. E.g., --log agent filters to agent-related logs.

Distribution

Agent Apps is distributed via a git-based release strategy:

  1. Contributors work on mainline. Source TypeScript lives in src/ and skills/. The dist/ directory is gitignored.
  2. npm run build compiles TypeScript to dist/.
  3. npm run release runs scripts/release.sh, which:
    • Verifies the working tree is clean and dist/ exists.
    • Clones the release branch into a temp directory (or creates it fresh).
    • Copies dist/, skills/, docs/, scripts/, package.json, and README.md.
    • Commits and pushes to the release branch.

The release branch ships pre-built JavaScript in dist/. The bin field points to dist/src/cli.js, and exports points to dist/src/index.js. Users get a working CLI immediately with zero build steps.

The package.json files field declares what ships in an npm publish tarball:

  • dist/src/ — compiled runtime (JavaScript + type declarations + source maps).
  • dist/skills/ — compiled bundled tools (JavaScript + non-TS artifacts like default.skill.md).
  • skills/ — source bundled tools (TypeScript). Included so users can read skill source code and override built-in tools by placing replacements earlier in their search path.
  • docs/ — documentation.

The skills/ source ships alongside dist/skills/ for transparency. The compiled dist/skills/ versions are what actually execute at runtime.

Why Not a prepare Script

When npm installs from a git URL, it runs lifecycle scripts but only installs production dependencies. TypeScript is a devDependency, so a prepare script calling tsc would fail. The release branch avoids this by shipping pre-built artifacts.

Testing Distribution

The distribution acceptance test exercises the full install → init → hub → update flow against the real release branch. It modifies global state (~/.agent-apps/cli, global npm install), so it requires explicit opt-in:

AGENT_APPS_TEST_DISTRO=1 bash test/acceptance/run.sh --only distribution --include-external

The test verifies: clone + install, agent-apps on PATH, init, hello, custom skills, hub operations, update, and post-update functionality.

See Also

Submitting Changes

  1. Create a branch from mainline.
  2. Make your changes. Run npm run check and npm run test:suite:smoke before committing.
  3. Commit with a descriptive message. Use conventional commit prefixes: feat:, fix:, docs:, refactor:, test:.
  4. Push and open a code review. Include what changed, why, and how to test it.
  5. Address review feedback. npm run check runs automatically on commit via lefthook — all five tools must pass.

Ask AI