Adds the executor a flow `script:` step runs on: a fresh Node child per step, a protocol over its IPC channel, and the two watchdogs that make a hung or runaway script the runner's problem rather than the host's. The step itself is not here. This branch stops at the executor and its unit tests; `feat/flow-script-step` stacks the YAML directive, the runner integration, and the reference docs on top. ## What it does - **One child per step.** Spawned with an old-space heap limit, an explicit working directory, and an environment built from an allowlist rather than copied from the tool server. - **A deadline and a lifeline.** A separate watchdog holds the deadline, so a script that wedges the event loop still dies on time; a second one reaps the whole process tree when the parent goes away, so a grandchild cannot outlive the run. - **A concurrency queue.** Slots are bounded per server. A step that never gets one is refused with a message that says so, and an aborted run frees its slot immediately. - **Log budgets.** 64 KiB per step and 256 KiB per run, counted on the bytes the report keeps: redaction and V8 frame collapsing both run before anything is counted, so what the limits bound is the size of the report rather than the size of the script's writes. - **Secret hold-back.** The scrub walks a chunk and stops where a value could still begin, so neither half of a value split across two chunks is released on its own, and a shorter value is never taken where the longer one containing it has not arrived yet. - **A failure taxonomy.** Twelve kinds, split into what the script did (it threw, it did not load, it exited non-zero, it wrote an unusable `output`) and what the host did to it (a limit, a signal, a spawn that failed, a queue slot it never got). ## Docs `packages/docs/docs/reference/configuration.mdx` lists the two configuration keys this branch adds, `scripts.maxTimeoutMs` and `scripts.heapLimitMb`. The `script:` step itself is documented with the step, on `feat/flow-script-step`. ## Verification `npm run build`, `npx eslint . --max-warnings 0`, `npx prettier --check .`, `npm run knip`, `npm run typecheck:scripts`, the test typecheck across every workspace, the tool-server suite (4719 passed, 1 skipped) and `npm run test:scripts` (92 tests) are green, as is `npx docusaurus build`. The executor's own behaviour is covered by the eight `test/flows/script/` files added here, against real child processes, and the compiled `dist/` executor was driven against its copied runner assets over every path the review reached. Each review fix carries the run that reproduced it before the change and the mutation that proves the new test fails without it: the prefix-secret leak and all three npm `node-options` routes were reproduced end to end through the executor against real child processes, and the five coverage findings were confirmed by re-applying the exact mutation each thread named. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added reliable execution of trusted flow scripts with concurrency control, cancellation, timeouts, memory limits, logging, and detailed failure reporting. * Added safeguards for process cleanup, watchdog termination, output validation, and secret redaction. * Added global configuration for maximum script runtime and memory usage. * **Bug Fixes** * Improved handling of script failures, stalled processes, malformed output, and child-process termination. * **Documentation** * Documented script resource limits and global configuration behavior. * **Chores** * Updated builds and packaging to include required flow-script runtime assets. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Hubert Gancarczyk <claude-hubert.gancarczyk@swmansion.com>
9.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
- Setting up the dev environment
- Project structure
- Building
- Running the project
- Running tests
- Code style
- Submitting a pull request
Requirements
- 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)
Setting up the dev environment
-
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 devThis 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 devNote:
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.
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
Local development (recommended)
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": 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.
Submitting a pull request
- 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).
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.