Skip to content

Project tool

The project tool reads your config files, runs the right lint, typecheck, test, build and format commands in 23 ecosystems, and checks code before commits.

Tools6 minutes to read
Copy & share
On this page11 sections

The project tool lets the agent lint, typecheck, test, build and format your code without you telling it which commands your project uses. It reads the config files and lockfiles in the folder and picks the matching commands.

You do not call it yourself. The agent calls it when it needs to check its work. The results show up in the chat like any other tool call.

ActionsLink to this section

ActionWhat it runsExample
lintThe project's linterproject(action: "lint")
typecheckThe type checkerproject(action: "typecheck")
testThe test suite, or one test fileproject(action: "test", file: "src/foo.test.ts")
buildThe buildproject(action: "build")
formatThe formatterproject(action: "format")
checkTypecheck, lint and test at the same time, with one combined reportproject(action: "check")
runA dev server or a package scriptproject(action: "run", script: "dev")
listThe packages in a monorepoproject(action: "list")

ParametersLink to this section

ParamRequiredWhat it does
actionyesOne of the actions above
filenoLimit the run to one file (useful for test and lint)
fixnoLet the linter fix what it can (for example biome --write, eslint --fix, ruff --fix)
scriptnoThe script to start with run
flagsnoExtra command-line flags
envnoEnvironment variables for the command
cwdnoThe folder to run in, for one package of a monorepo
timeoutnoTime limit in milliseconds. By default it uses your toolTimeout setting (2 minutes unless you changed it)

How the toolchain is detectedLink to this section

Empryo looks for well-known files in the working folder:

bun.lock         -> bun test, bun run lint, bunx tsc --noEmit
Cargo.toml       -> cargo test, cargo clippy, cargo check
go.mod           -> go test, golangci-lint run, go build
pyproject.toml   -> pytest, ruff check, mypy/pyright
composer.json    -> phpunit, phpstan/psalm
Gemfile          -> rspec, rubocop
build.gradle     -> gradle test, gradle check

In a JavaScript or TypeScript project, a script in package.json (such as lint, typecheck, test or build) wins over the detected tool.

JS/TS linter detectionLink to this section

When package.json has no lint script, Empryo picks the first linter it finds:

  1. biome.json / biome.jsonc -> biome check .
  2. oxlintrc.json / .oxlintrc.json -> oxlint .
  3. eslint.config.js / .eslintrc* -> eslint .

Python type checker detectionLink to this section

  • If pyrightconfig.json exists, it runs pyright.
  • Otherwise it runs mypy ..

When the project uses uv, Poetry or Pipenv (it has a uv.lock, poetry.lock or Pipfile.lock), the commands run through that tool, for example uv run pytest.

Full ecosystem supportLink to this section

EcosystemLintTypecheckTestBuild
JS/TS (Bun)biome / oxlint / eslinttscbun testbun run build
JS/TS (npm)biome / oxlint / eslinttscnpm testnpm run build
JS/TS (pnpm)biome / oxlint / eslinttscpnpm testpnpm build
JS/TS (Yarn)biome / oxlint / eslinttscyarn testyarn build
Denodeno lintdeno checkdeno test--
Rustcargo clippycargo checkcargo testcargo build
Gogolangci-lint / go vetgo buildgo testgo build
Pythonruff / flake8pyright / mypypytest--
PHPpint / php-cs-fixerphpstan / psalmphpunit--
Rubyrubocop--rspec / rails test--
Swiftswiftlintswift buildswift testswift build
Elixircredodialyzermix testmix compile
Java/Kotlin (Gradle)gradle check (or spotless / ktlint)gradle compileJava / compileKotlingradle testgradle build
C/C++ (CMake)clang-tidycmake buildctestcmake build
Dart/Flutterdart analyzedart analyzeflutter testflutter build
Zigzig fmt --checkzig buildzig build testzig build
Haskellhlintstack buildstack teststack build
Scalascalafmt --checksbt compilesbt testsbt compile
.NET/C#dotnet formatdotnet builddotnet testdotnet build
iOS/Xcodeswiftlintxcodebuild buildxcodebuild testxcodebuild build
Java (Maven)checkstylemvn compilemvn testmvn package
C/C++ (Make)----make testmake
Clojureclj-kondo--lein test / clj -M:testlein uberjar

A -- means Empryo found no standard command for that step, so the action reports that nothing is configured.

Pre-commit checksLink to this section

When the agent commits with git commit -m "…" through the shell tool, Empryo first runs your linter and type checker. If they fail, the commit does not happen and the agent gets the errors so it can fix them.

What runs depends on the project:

  • JS/TS: your linter and tsc --noEmit, for example biome check . && tsc --noEmit
  • Deno: deno lint && deno check .
  • Rust: cargo clippy && cargo check
  • Go: golangci-lint run && go build ./... (or go vet when there is no golangci-lint config)
  • Python: ruff check (or flake8) followed by pyright or mypy

This check does not depend on the agent remembering to lint. Empryo runs it on every agent commit.

The checks have a 15-second limit. Commits you make yourself, outside Empryo, are not affected.

Monorepo discoveryLink to this section

project(action: "list") finds the packages in a workspace.

Supported workspace formatsLink to this section

  • pnpm: pnpm-workspace.yaml
  • npm/yarn: the workspaces field in package.json
  • Cargo: [workspace] members in Cargo.toml
  • Go: use directives in go.work

Example outputLink to this section

5 packages:
  @myapp/web -- packages/web (biome) [lint, typecheck, test]
  @myapp/api -- packages/api (biome) [lint, typecheck, test]
  @myapp/shared -- packages/shared (biome) [lint, typecheck]
  @myapp/cli -- packages/cli (biome) [lint]
  @myapp/docs -- packages/docs [test]

Use project(action: "lint", cwd: "<path>") to target a specific package.

Each package is detected on its own, so the list shows what each one actually supports. When the agent tests a single file, Empryo runs the test from the package that owns that file instead of the whole workspace.

When the agent uses the shell insteadLink to this section

If the agent runs a command like bun run lint or cargo clippy through the shell, the command still runs. The result adds a short reminder to use the project tool next time, because its output is structured and shows up properly in the UI.

Command succeeded. Next time use project(action: "lint") -- it auto-detects
the toolchain, results are structured, and output is visible in the UI.