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:
tsc— compilessrc/**/*.tsandskills/**/*.tsto JavaScript indist/. TherewriteRelativeImportExtensionsoption rewrites.ts→.jsin import paths.- Copy non-TS files — copies non-TypeScript files from
skills/intodist/skills/(e.g.,default.skill.md). TypeScript source files are not copied —tscalready compiled them to.js. 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 checkagainst 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 tocheck.
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:
- Contributors work on
mainline. Source TypeScript lives insrc/andskills/. Thedist/directory is gitignored. npm run buildcompiles TypeScript todist/.npm run releaserunsscripts/release.sh, which:- Verifies the working tree is clean and
dist/exists. - Clones the
releasebranch into a temp directory (or creates it fresh). - Copies
dist/,skills/,docs/,scripts/,package.json, andREADME.md. - Commits and pushes to the
releasebranch.
- Verifies the working tree is clean and
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 likedefault.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
- Specification — Complete technical reference
Submitting Changes
- Create a branch from
mainline. - Make your changes. Run
npm run checkandnpm run test:suite:smokebefore committing. - Commit with a descriptive message. Use conventional commit prefixes:
feat:,fix:,docs:,refactor:,test:. - Push and open a code review. Include what changed, why, and how to test it.
- Address review feedback.
npm run checkruns automatically on commit via lefthook — all five tools must pass.