Files
software-mansion__argent/CONTRIBUTING.md
T
filip131311 9489a220b0 chore(argent): declare the Node version the package actually needs (#653)
Fixes #640.

```json
"engines": { "node": ">=20.12.0" }
```

## Why 20.12.0 and not 20

My first answer was `>=20` — CI's lowest tested line, satisfied by every
runtime dependency, and the
version the issue reporter (and I) installed successfully. **It is
wrong**, and shipping it would
have told npm it is safe to install onto runtimes where the CLI cannot
execute a single line.

The mistake was looking at first-party `src/`. The published artifacts
are four esbuild bundles with
their dependencies **inlined**, and that is where the real floor lives.
Confirmed in
`packages/argent/dist/`:

| blocker | added in | consequence below it |
|---|---|---|
| `import { styleText } from "node:util"` (via inlined `@clack/*`, in
`installer.mjs` and `cli-cmds.mjs`) | **20.12.0** | a **link-time**
`SyntaxError` — `argent init` and every CLI subcommand fail to load |
| `import.meta.dirname` at module scope (`bundled-paths.js`, `cli.js`) |
20.11.0 | `TypeError` during module init |

So 20.12.0 is the lowest version on which the package actually loads. It
is also the lowest line CI
exercises (7 workflows pin Node 20), and it satisfies every runtime
dependency's own constraint
(`@fails-components/webtransport` `>=20`, `@modelcontextprotocol/sdk`
`>=18`).

`electron`'s `>=22.12.0` does **not** apply — it is an
`optionalDependency`, and npm does not
engine-check those. Verified: installing `0.18.0` on Node 20.19.5
produces no `EBADENGINE` at all and
electron is never materialized.

I did not go to `>=22` on EOL grounds: it would refuse a runtime seven
CI workflows still test and
that users demonstrably run.

## Docs corrected alongside

- `README.md` said **20.11** — one minor below where the package can
load.
- `CONTRIBUTING.md` said **Node.js 18+** — predates what the repo's own
lint toolchain needs
  (`eslint@10` requires `^20.19.0 || ^22.13.0 || >=24`).

`packages/argent/README.md` is generated by `scripts/sync-readme.cjs` on
`prepack`, so it follows
automatically and is deliberately untouched.

## What a reviewer should know

**Enforcement is package-manager-dependent**, which matters because
`argent init` installs with
whichever PM the project uses:

| | behaviour on too-old Node |
|---|---|
| npm (default) | `npm warn EBADENGINE` — installs anyway |
| npm + `engine-strict=true` | hard error, install aborts |
| **yarn 1.x** | **hard error by default** |
| **pnpm** | **silently ignores it** |

So this achieves the issue's "warn" but only conditionally its "refuse".
There is no `.npmrc` in the
repo, so contributors are unaffected either way.

**Nothing in the repo validates `engines`** — `repo-hygiene`,
`check-workspace-versions.mjs` and
`pack-mcp.cjs` all ignore unknown manifest keys, and
`next-canary-version.mjs` round-trips the
manifest through `JSON.parse`/`stringify`, which preserves the new field
and stays prettier-clean.

**One inconsistency I did not resolve:**
`packages/argent/scripts/bundle-tools.cjs:383` builds with
`target: "node22"`, which now sits above the declared floor. In practice
the node20→node22 syntax gap
is negligible (tsc targets ES2022 and I found no post-20 *syntax*), so I
left the shipped artifact
alone rather than change build output in a manifest-only PR — but it
should either be lowered to
match or consciously kept. Your call.

## Separate bug found while verifying this

`ios-remote` is broken on the **entire Node 20 line**, independent of
this change: the inlined
`@moq/*` client uses `Promise.withResolvers` (Node 22+) ungated — 6
occurrences in
`dist/tool-server.cjs`, reached from `utils/moq-client.ts` via the
simulator-server blueprint.
Reproduced on Node 20.19.5: `TypeError: Promise.withResolvers is not a
function`.

I deliberately did **not** raise the floor to `>=22` to paper over it —
that would refuse Node 20 for
every user to hide one broken path. `moq-client.ts` already has an
`ensurePolyfill()` that polyfills
`WebSocket`/`WebTransport`; this belongs there. Filed separately.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2026-08-03 11:01:09 +02:00

7.3 KiB

Contributing to Argent

Thank you for your interest in contributing to Argent! This guide covers everything you need to get started.

Table of contents


Requirements

  • macOS with Xcode installed (required for xcrun simctl and iOS simulator support)
  • Node.js 20.19+ (the lint toolchain's floor; the published package needs 20.12+)
  • The simulator-server and ax-service binaries in packages/native-devtools-ios/bin/ (arm64 macOS, installed separately via npx @swmansion/argent install)

Setting up the dev environment

  1. Fork and clone the repository:

    git clone https://github.com/software-mansion/argent.git
    cd argent
    
  2. Install dependencies (npm workspaces installs all packages at once):

    npm install
    
  3. Start the dev environment:

    npm run dev
    

    This builds the native devtools dylibs (if the private submodule is available, otherwise uses pre-built binaries), compiles the MCP TypeScript, patches ~/.claude.json to point at the local MCP, and starts the tool-server from source via ts-node. Press Ctrl+C to stop — the script automatically restores your global Argent configuration.

    To use a different port:

    PORT=4000 npm run dev
    

    Note: packages/argent-private is a private git submodule that holds the ObjC source for the native devtools dylibs. If you don't have SSH access to it, npm run dev will use the pre-built dylibs committed to the repository — everything else works normally.

That's it — no separate install steps per package are needed.


Project structure

This is an npm workspaces monorepo. All packages live under packages/:

Package Path Purpose
@argent/registry packages/registry Core library: dependency-aware service lifecycle, blueprints, tools, URNs
@argent/tool-server packages/tool-server HTTP API over the registry (port 3001). Registers all blueprints and tools
@swmansion/argent packages/argent Published bin shell — minimal dispatcher + bundled subcommands + native runtime assets
@argent/tools-client packages/argent-tools-client HTTP client + tool-server launcher shared by @argent/mcp and @argent/cli
@argent/mcp packages/argent-mcp MCP stdio protocol adapter — proxies Claude/Cursor calls to the tool-server
@argent/cli packages/argent-cli Shell CLI commands: argent run, argent tools, argent server, argent enable, argent disable, argent flags
@argent/installer packages/argent-installer Workspace setup logic: argent init, update, uninstall
@argent/skills packages/skills Markdown skill files (prefixed argent-*) that instruct AI agents how to use Argent tools
@argent/native-devtools-ios packages/native-devtools-ios Pre-built dylibs for iOS simulator injection (view hierarchy, network inspection). ObjC source lives in packages/argent-private (private submodule)

The tsconfig.json at the root uses TypeScript project references; tsconfig.base.json holds shared compiler options (strict, ES2022, etc.).


Building

npm run dev

Builds everything and starts the tool-server from source. See Setting up the dev environment for details.

Full build

Build all packages at once using TypeScript project references:

npm run build

To build a specific package:

npm run build -w @argent/registry
npm run build -w @argent/tool-server

To build and bundle the distributable MCP package:

npm run build -w @swmansion/argent
# or, to also produce a .tgz tarball:
npm run pack:mcp

Running the project

For day-to-day development, use npm run dev (see Setting up the dev environment).

Production-like start (builds bundles first, then starts from compiled output):

npm run start

This builds the registry, then starts the tools server on port 3001.

Verify the tools server is up:

curl http://localhost:3001/tools

Running tests

Tests are written with Vitest. Each package has its own test suite.

Run tests for a specific package:

npm test -w @argent/registry
npm test -w @argent/tool-server

Run tests in watch mode during development:

npm run test:watch -w @argent/registry
npm run test:watch -w @argent/tool-server

Code style

  • TypeScript strict mode is enabled across all packages ("strict": true in tsconfig.base.json). All code must compile without errors.
  • Target: ES2022 with CommonJS modules (except @swmansion/argent which uses ESM).
  • Prefer explicit types over any. Use Zod schemas for runtime validation where the codebase already does so.
  • Keep commits focused. Prefix commit messages with a type: feat:, fix:, chore:, docs:, refactor:, test:. This feeds the auto-generated changelog on release.

Submitting a pull request

  1. Create a branch from main with a descriptive name (e.g. feat/add-screenshot-tool, fix/session-leak).
  2. Make your changes. Keep the scope of a PR small and focused — it's easier to review.
  3. Ensure the build passes:
    npm run build
    
  4. Ensure tests pass for the packages you touched.
  5. Write a clear PR title — it becomes part of the release changelog. Use the same prefix convention as commit messages (feat:, fix:, etc.).
  6. Open the PR against main and fill in the description with context on what changed and why.
  7. A maintainer will review and may request changes. Address feedback with new commits (don't force-push after review starts).

Questions?

If you're unsure about something, open a GitHub Discussion or leave a comment on the relevant issue before spending time on a large change.