Thank you for your interest in contributing to Argent! This guide covers everything you need to get started.
- Requirements
- Setting up the dev environment
- Project structure
- Building
- Running the project
- Running tests
- Code style
- Submitting a pull request
- macOS with Xcode installed (required for
xcrun simctland iOS simulator support) - Node.js 20.19+ (the lint toolchain's floor; the published package needs 20.12+)
- The
simulator-serverandax-servicebinaries inpackages/native-devtools-ios/bin/(arm64 macOS, installed separately vianpx @swmansion/argent install)
-
Fork and clone the repository:
git clone https://github.com/software-mansion/argent.git cd argent -
Install dependencies (npm workspaces installs all packages at once):
npm install
-
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.jsonto point at the local MCP, and starts the tool-server from source viats-node. PressCtrl+Cto stop — the script automatically restores your global Argent configuration.To use a different port:
PORT=4000 npm run dev
Note:
packages/argent-privateis 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 devwill use the pre-built dylibs committed to the repository — everything else works normally.
That's it, no separate install steps per package are needed, except packages/docs. It is excluded from the workspace glob and keeps its own lockfile, so npm run lint from the root needs npm install run inside packages/docs first.
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.).
npm run devBuilds everything and starts the tool-server from source. See Setting up the dev environment for details.
Build all packages at once using TypeScript project references:
npm run buildTo build a specific package:
npm run build -w @argent/registry
npm run build -w @argent/tool-serverTo build and bundle the distributable MCP package:
npm run build -w @swmansion/argent
# or, to also produce a .tgz tarball:
npm run pack:mcpFor 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 startThis builds the registry, then starts the tools server on port 3001.
Verify the tools server is up:
curl http://localhost:3001/toolsTests 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-serverRun tests in watch mode during development:
npm run test:watch -w @argent/registry
npm run test:watch -w @argent/tool-server- TypeScript strict mode is enabled across all packages (
"strict": trueintsconfig.base.json). All code must compile without errors. - Target: ES2022 with CommonJS modules (except
@swmansion/argentwhich 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.
- Create a branch from
mainwith a descriptive name (e.g.feat/add-screenshot-tool,fix/session-leak). - Make your changes. Keep the scope of a PR small and focused — it's easier to review.
- Ensure the build passes:
npm run build
- Ensure tests pass for the packages you touched.
- Check for dead code, in a tree with no compiled output:
npx tsc --build --clean npm run knip
--cleanremoves whattscemitted; the few non-TypeScript assetsnpm run buildcopies intopackages/tool-server/distare left behind. Knip does not look at them, so the report is the same either way. Run it this way round, not straight after step 3. The gate is counted against an unbuilt tree, because that is what CI analyses; withpackages/*/distpresent knip finds fewer issues, so a built-tree run carries phantom headroom and can pass locally while the Dead Code job fails. This leaves the tree unbuilt, so runnpm run buildagain before re-running any test suite. The gate runs at--max-issues 0, so the report must come back empty and there is no ceiling to raise: when knip names an export, type or class member you added, drop theexportkeyword if the symbol is still used inside its own file, or delete it if nothing uses it. A class member has noexportkeyword to drop, so it is delete or tag. When the only caller is somewhere knip cannot see — another workspace, because the unbuilt tree breaks cross-workspace edges, or theargent-privatesubmodule, which CI does not check out — tag the declaration/** @public */and name that caller in the comment. That last case is whatUnused exported class membersalmost always is here: the three tagged members inpackages/registryare each called from another workspace, and pass 2 reports all three the moment a tag comes off. Look for it before you delete. This gate stays green on a wrong delete either way, but the two cases differ after that: deleting a symbol another workspace calls turnsnpm run buildand that workspace's tests red, so that mistake is caught, while deleting one onlyargent-privatereaches breaks nothing here — itrequire()s the builtdist/*.js, and CI never checks that submodule out. - Write a clear PR title — it becomes part of the release changelog. Use the same prefix convention as commit messages (
feat:,fix:, etc.). - Open the PR against
mainand fill in the description with context on what changed and why. - A maintainer will review and may request changes. Address feedback with new commits (don't force-push after review starts).
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.