Commit Graph

43 Commits

Author SHA1 Message Date
filip131311 23b9ae5c87 chore: bump to 0.18.1 (#603)
Patch bump. First release whose npm tarball carries `mcpName` (added in
#598), which unblocks the MCP registry publish (`mcp-publisher publish`
rejects `0.18.0` because its manifest predates the field).

`server.json` is bumped alongside `packages/*` for the first time —
`scripts/check-workspace-versions.mjs` now enforces it (`All workspace
packages and server.json are at 0.18.1` locally).

| Commit | Change |
| --- | --- |
| `eea6c27c` | `style`: fix prettier formatting on main (#604) |
| `7e4ece69` | `chore`: server.json for the official MCP registry (#598)
|
| `fc09612f` | `feat(config)`: `recordings.directory` — configurable
save location for screen recordings (#601) |
| `cccda92d` | `feat(config)`: `ios.additionalDeviceSets` — additive
array of extra CoreSimulator device sets (#542) |

Version-only diff, rebased on main as of #604.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2026-07-31 15:49:05 +02:00
filip131311 eea6c27cf0 style: fix prettier formatting on main (#604)
## Summary

`npx prettier --check .` fails on current `main` with one violation:
`packages/argent-tools-client/test/artifacts.test.ts` (a single `expect`
line wrapped where it now fits the 100-char print width). This PR is the
`prettier --write` output for that file — whitespace-only, no behavior
change.

## Test plan

- [x] `npx prettier --check .` passes on the branch (was failing on
`main`)
- [x] Diff is whitespace-only (1 file, 1 reflowed statement)

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2026-07-31 13:13:12 +02:00
filip131311 fc09612f30 feat(config): recordings.directory — configurable save location for screen recordings (#601)
## Summary

Adds a `recordings.directory` configuration entry (built on the #534
config system) that lets users choose where finished screen recordings
(mp4) are saved, instead of the fixed `<project>/.argent/recordings/`.

- **Schema entry** (`configuration-core`): scopes `project` + `global`,
`prioritize-local` merge (project wins when both are set). Picked up
automatically by `argent config set/get/unset/list`.
- **Value semantics**: absolute path, `~`-prefixed (expands to home), or
relative — relative anchors at the project root (home dir when not in a
project).
- **Client-side wiring** (`argent-tools-client` `durableSaveTarget`):
the wire `saveDir` hint stays `.argent/recordings` (wire contract
unchanged → old/new client-server interop). Past the existing client
allowlist, the recordings hint is remapped to the configured directory.

## Security model unchanged

The allowlist exists because `saveDir` arrives from a possibly-hostile
remote tool-server. That gate still runs first and is unchanged: a
server can only *select* the sanctioned recordings category, never name
a destination. The configured directory comes from the client's own
trusted config files. Exclusive never-overwrite writes (`name (2).mp4`),
the durable download size caps, and the symlink real-path guard all
remain; for a configured dir the realpath check degenerates to "the dir
resolves to itself" (a user-chosen symlink is legitimate, matching how
the default base may be symlinked). A test asserts hostile wire values
(`.git`, `..`, absolute) stay rejected even with config set.

## `argent link` interaction

Materialization always runs on the **client** (co-located → copy from
`hostPath`; remote → download over `/artifacts/:id`), and the config is
read there at materialize time — so with a remote tool-server the mp4
lands on the client host per the *client's* config, on both paths.

## Testing

- New unit tests: 10 in `argent-tools-client` (absolute/`~`/relative
resolution, local-vs-global precedence, both materialization paths,
hostile wire values with config set) + 4 in `configuration-core` (scope
writes, trim, precedence, validation).
- Suites green: tools-client 188, configuration-core 87, tool-server
screen-recording 62, CLI config 16; full workspace vitest 4727 pass (3
pre-existing `scripts/*.test.mjs` node:test files don't parse under root
vitest — unrelated).
- **Live E2E** on an iPhone 15 Pro simulator via the branch tool-server:
real recording captured, then materialized through the branch client
with configs written by the real `argent config` CLI in a sandboxed
HOME/project. 7 scenarios green: default location, global-only (`~`
path), project-only (relative), both-set → project wins (co-located
**and** remote download with Bearer auth), unset fallbacks, `argent
config list` rendering. Remote-downloaded mp4 ffprobe-verified
(byte-exact size, 4.17 s duration); name-collision suffixing observed
live.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2026-07-31 12:36:31 +02:00
Ignacy Łątka 646f726bc4 chore: bump to 0.18.0 (#596)
bump

debated myself whether this should be a QoL patch or a minor release and
i think the latter makes more sense

| Commit | Change |
| --- | --- |
| `7189333a` | `feat(tool-server)`: human-readable log message (#582) |
| `5ac69349` | `feat(flow)`: CSS combinator scopes for flow selectors
(#569) |
| `e23f0e28` | `feat(flow)`: snapshot `cropOn` for single-element visual
diffs (#516) |
| `6a53e66a` | `feat(flow)`: rotate directive (#546) |
| `68ceb314` | `ci`: cut over npm publishing to the unified
trusted-publishing workflow (#571) |
| `2546788a` | `ci`: unified publish-npm workflow (#565) |
2026-07-30 23:47:28 +02:00
Ignacy Łątka a0460eeefe chore: bump workspace packages to 0.17.0 (#554)
## What

Lockstep-bump all 16 `packages/*` workspace packages (and the matching
`package-lock.json` entries) from `0.16.1` → **0.17.0**.

Diff is exactly 32 version-field swaps across 17 files — no formatting
or
dependency churn.

## Verification

- **Lockfile CI gate reproduced locally on the exact CI toolchain**
(Node
24.14.1 / npm 11.11.0, matching `lockfile.yml`'s `node-version: "24"`):
`npm install --package-lock-only --ignore-scripts` on top of the commit,
then `git diff --exit-code -- package-lock.json` → **clean**. npm's own
  regeneration reproduced the hand-edit byte-for-byte, so none of the
  `"peer": true` churn that #513/#539 had to keep out reappears here.
- `node scripts/check-workspace-versions.mjs` → `All workspace packages
are
  at 0.17.0.`
- Remaining `repo-hygiene.yml` checks pass: no conflict markers, no
focused
  tests.
- `prettier --check` on all 17 changed files → `All matched files use
  Prettier code style!`
- **Runtime version actually reports 0.17.0**: `getInstalledVersion()`
(`argent-installer/src/utils.ts:384`) and
`getGloballyInstalledVersion()`
(`topology.ts:94`) both read `version` straight out of the resolved
package
root's `package.json` at runtime, so the bump is the whole mechanism —
no
generated constant or second version file to keep in sync. Exercised
that
  resolution against the bumped tree: `PACKAGE_ROOT` → `packages/argent`
  (`@swmansion/argent`) → `0.17.0`.
- Verified all 16 lockfile hits belong to workspace packages (parsed the
  lockfile's `packages{}` and matched every `0.16.1` entry to a
`packages/*` path) — no unrelated dependency happened to sit at 0.16.1.
- No test asserts a release version string; the only `0.1x.x` matches
under
  `*.test.ts` are historical references inside comments.

## Note

`packages/argent-private` needs no pin change here. Its release version
is
carried purely as a git tag — the repo has no version field on the
release
line (its only `package.json` is a `private: true` research CLI on an
independent `0.1.0` line) — and `v0.17.0` is now tagged at `2d6c292`,
which
is **already** the commit this superproject pins. `v0.16.0` and
`v0.16.1`
sit on that same commit, so the submodule entry is correct as-is and
stays
out of this diff.
2026-07-23 23:33:49 +02:00
Ignacy Łątka 98ae93145b feat(screen-recording): save recordings to the project's .argent/recordings (#551)
Stacked on #549 (touch visualizer).

## What

Screen recordings now land durably under **`.argent/recordings/`** on
the client, instead of the disposable temp-artifact cache — and for a
remote `argent link` tool-server they land on the **client** host, not
the server.

## Why

Two rough edges in the recording flow:
1. The finished video was only ever written to the OS temp-artifact
cache, so it was easy to lose and not obviously part of the project.
2. Over `argent link`, the video was downloaded into that same temp
cache; there was no project-scoped home for it.

## How

- New optional `saveDir` hint on the artifact wire contract
(`@argent/registry` + `@argent/tools-client`).
- `screen-recording-stop` tags its video with `saveDir:
".argent/recordings"` and drops the internal `argent-` temp prefix from
the filename.
- The client materializer honours `saveDir`: **copies** the file when
the tool-server is co-located, **downloads** it over `/artifacts/:id`
when it is remote. Either way the bytes end up in the durable directory.
- `saveDir` resolves against the client's **project root** (nearest
ancestor with `.git`/`package.json`/`.argent`), falling back to the
global **`~/.argent`** when run outside a project — matching how the
rest of argent scopes per-project config. Adds `findProjectRoot` to
`configuration-core` (a null-returning variant of `resolveProjectRoot`)
to drive that choice.
- Hardened: absolute paths and `..` escapes are rejected, directory
bundles excluded — a tool-server can't steer a write outside the base
directory.

## Verification

- Unit: 28 cases in `argent-tools-client/test/artifacts.test.ts`
(co-located copy, remote download, project-root anchoring incl. from a
subdirectory, global fallback, path-traversal rejection, integrity).
configuration-core suite green (55).
- E2E against a booted iOS 18.5 sim + a worktree tool-server, driving
the real client materializer path:
- in a project, co-located server →
`<projectRoot>/.argent/recordings/…mp4` (valid h264)
- in a project, forced remote download (`GET /artifacts/:id`) → same
durable dir, temp cache left empty
  - outside a project (HOME redirected) → `~/.argent/recordings/…mp4`
- Root `tsc --build`, prettier, and eslint all clean.

MoQ / `sim-remote` recording support lands in a separate PR stacked on
this one.

---------

Co-authored-by: Ignacy Łątka <claude-hubert.gancarczyk@swmansion.com>
2026-07-23 22:43:01 +02:00
filip131311 1f5b155229 chore: bump workspace packages to 0.16.1 (#539)
## What

Lockstep-bump all 16 `packages/*` workspace packages (and the matching
`package-lock.json` entries) from `0.16.0` → **0.16.1**.

## Verification

- `node scripts/check-workspace-versions.mjs` → `All workspace packages
are at 0.16.1.`
- Lockfile diff is exactly 16 version-field swaps — no unrelated churn
(the `"peer": true` regeneration noise from a newer npm was again kept
out, same as #513). The one remaining `0.16.0` in the lockfile is the
unrelated `@humanfs/types` dev dependency.
- `packages/argent-private` submodule left pinned at `v0.16.0` — no
`v0.16.1` tag exists there yet.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2026-07-22 11:03:13 +02:00
filip131311 b50d51a2de chore: bump workspace packages to 0.16.0 (#513)
## What

Lockstep-bump all 16 `packages/*` workspace packages (and the matching
`package-lock.json` entries) from `0.15.0` → **0.16.0**.

## Why

The `packages/argent-private` submodule is already pinned at
**v0.16.0**, while the workspace packages sat at 0.15.0. This aligns the
whole repo on the single 0.16.0 version.

## Verification

- `node scripts/check-workspace-versions.mjs` → `All workspace packages
are at 0.16.0.`
- `git submodule status` → clean (no `+`), pinned at `v0.16.0`.
- Lockfile diff is exactly 17 version-field swaps (no unrelated churn —
a full `npm install --package-lock-only` regeneration was rejected
because it introduced unrelated `"peer": true` churn from a newer npm).
2026-07-15 20:53:36 +02:00
filip131311 07427917e2 fix(launcher,lens): drop the npm postinstall — its jobs move to runtime (#510)
## Why

Shipping *any* `postinstall` makes pnpm 10+ wall the install behind its
build gate: `ERR_PNPM_IGNORED_BUILDS`, `pnpm install` exits 1, and `pnpm
exec argent` is unusable until the user runs `pnpm approve-builds` —
their first experience of argent is a bricked CLI. The same script is
also silently skipped by `--ignore-scripts`, Yarn PnP, and locked-down
CI, so anything correctness-relevant in it was already unreliable (the
codebase says as much in several places, e.g. `runTrustingDisk`).

Live-verified during the 0.15.0 QA: with **all builds declined** in pnpm
11, argent installs, inits, and serves MCP fine — the postinstall was
never load-bearing for install correctness. Removing it means pnpm users
never see the approve-builds wall for argent itself.

## What the postinstall did, and where it went

| Job | New home |
|---|---|
| SIGTERM a tool-server stranded by an upgrade that replaced its bundle
dir (pnpm/yarn version-pinned layouts) | `sweepDeadStateFiles` in the
launcher's slow path now retires **live** servers whose recorded bundle
is gone from disk, with the same pid-identity guard as
`killToolServerForInstallDir` (never signal a recycled pid; keep
unidentifiable live records reachable for `server stop`). Same-bundle
in-place bumps were already self-healed by the disk-authoritative
version gate. |
| `chmod +x` node-pty's macOS `spawn-helper` prebuilds (shipped without
the exec bit; first `pty.spawn` otherwise fails `posix_spawnp`) |
`ensureSpawnHelperExecutable()` in `lens-pty.ts`, run when the module
loads, best-effort with the AppleScript fallback intact. |
| Print the `argent init` hint | Dropped — init flows drive the install,
and the hint never printed on pnpm/PnP anyway. |

`ARGENT_SKIP_POSTINSTALL` in `update.ts` is kept deliberately:
downgrades and pinned installs fetch older versions whose postinstall
still exists and honors it.

## Testing

- New `launcher-sweep.test.ts` (6 tests) exercises the sweep against
**real processes**: live-server-with-deleted-bundle is terminated and
its record unlinked; live server with intact bundle untouched;
recycled-pid record kept, not signalled; dead-pid record unlinked;
legacy slot untouched; a SIGTERM-trapping orphan doesn't hold the sweep
(and the spawn lock above it) hostage for its kill grace window — the
record is unlinked and the SIGKILL escalation runs in the background.
- New `ensureSpawnHelperExecutable` tests (real fs, darwin-gated) verify
the +x restore and silent failure; a regression test pins that
`loadNodePty` tolerates req seams without `resolve`.
- Full suites green in all touched packages: tools-client 154, installer
535, cli 248, mcp 76, argent 60.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2026-07-15 20:26:39 +02:00
Jakub Piasecki 551c781fb0 feat(flow): support running end-to-end flows without agent (#429)
# Flow E2E

This PR turns flows from replayable tool-call recordings into a
declarative, portable e2e test system that runs headlessly via `argent
flow run <name>` — no LLM in the loop, non-zero exit on failure,
suitable for CI.

Agents can still replay flows via `flow-execute` and get the full output
from each step after it finishes.

## Flow format

Flows are YAML files in `.argent/flows/`, now built from **directives**
interpreted by the runner rather than raw tool calls:

- `launch` — terminate + relaunch the app under test (per-platform app
id map supported); its presence marks a flow as **e2e** (controls its
own start state) vs. a **fragment** (runs against current state,
declares an `executionPrerequisite`, composable via `run:` with
cycle/depth guards).
- `tap`, `type` (focus handshake + optional submit), `scroll-to`
(momentum-free, increments by half the container, `within:` for nested
scrollers), `await`/`assert` (`visible`/`exists`/`hidden`/`text`
conditions), `wait`, `snapshot`, `echo`, and raw `tool:` as escape hatch
— still fully supported, so previously recorded flows keep working
unchanged.
- **Selectors** are `{ text, identifier, role }`; a bare string is a
loose selector resolving identifier-first, then visible text. Any
directive failure hard-stops the flow; later steps report `skip`.

## Execution engine

- Flows store no device id — the runner binds a device at replay
(explicit `--device`/`--platform` or the single booted one).
- Selector resolution runs against **platform-unified UI trees**: iOS
native view hierarchy (full testID coverage, gated on native-devtools
connecting), Android full accessibility hierarchy (full resource-id
coverage), Chromium CDP DOM, and the Vega automation toolkit. A shared
flatten/text-hoisting pass lets `text` assertions read what an
identified container visibly shows. Vega is remote-driven, so touch
directives fail with guidance to use `tv-remote`/`keyboard`.
- Steps wait for the tree to **settle** (two consecutive identical
reads) before acting; `launch` additionally gates on platform
tree-source readiness instead of silently degrading.

## Visual snapshots

`snapshot` diffs a full-resolution screenshot against baselines in
`.argent/flows/__baselines__/<flow>/`, keyed by platform + resolution,
with configurable `maxMismatch` and `--update-baselines`. The status bar
is pinned for the whole run (iOS `simctl status_bar`, Android demo mode)
so clocks/battery don't drive diffs. Baseline/current/diff images are
returned as artifacts on failure (and the written baseline on first
capture/update); a clean pass carries none, keeping reports and agent
context lean.

## Recording

The record-live workflow (`flow-start-recording` → `flow-add-step` →
`flow-finish-recording`) now emits directives automatically: coordinate
taps are rewritten to portable selector `tap:` steps (selector captured
from the pre-tap tree), a leading `restart-app` becomes `launch:`,
`flow-execute` of a sibling fragment becomes `run:`, and device ids are
stripped. The skill instructs the agent to polish the saved YAML
afterwards (fold tap+keyboard into `type:`, swipes-to-reach into
`scroll-to:`, awaits into sugar form).

## Also included

- `argent flow run` / `argent flow list` CLI with per-step report
rendering, artifact materialization, and `--json` output.
- New structured failure codes for flow validation, device resolution,
composition, and devtools readiness.
- Extensive unit test coverage (tree adapters, selector matching,
scrolling, composition, type focus, snapshots, status bar).
2026-07-13 17:15:01 +02:00
Gabe Debes 10a78d0527 feat(file-inputs): add tar-upload kind for streaming remote file/directory uploads (#443)
## What

A new `tar-upload` file-input kind so a tool that reads a local file or
directory (e.g. `reinstall-app`'s app bundle) works when the tool-server
runs on a different machine than the caller.

## Why

`reinstall-app` assumes the bundle is on the tool-server's filesystem.
That breaks with a cloud-hosted simulator — the bundle is on your
machine, not the server's. The existing `file` kind inlines base64 and
is capped at 32 MB, too small for a real `.app`/`.apk`; `tar-upload`
streams instead.

## How

When you're not linked to a remote server, an existing local path is
used in place.

When you are (e.g. `argent link`), the client tars the path, streams it
to `POST /upload`, and the tool call carries `uploadId` plus a SHA-256
`contentHash`. The server always extracts from that upload — even if the
same path already exists on disk — verifies the hash, and vets tar
members before extracting.

The client only uploads when the path is on its own machine (a line on
stderr so you know it's working). Same command local or remote:

```
argent run reinstall-app --udid <udid> --bundleId <id> --appPath ./MyApp.app
```

## Shared extraction

Per review feedback, the tar pack/unpack logic is unified in a small
`@argent/archive` package used by **both** directions — this upload path
and the pre-existing artifact download (`streamDirectoryAsTarGz` /
`downloadAndExtractArchive`). The shared extractor rejects
path-traversal entries, symlinks whose target escapes the extract dir,
and non-file/dir members (hardlinks, devices) — while still allowing a
bundle's internal symlinks. This also hardens the artifact-download
path, which previously extracted without those checks.

## Tests

Client + server unit and HTTP round-trip tests: dir and file upload,
skip-when-not-local, co-located no-op, hash required, path-traversal /
escaping-symlink / hardlink rejection, sidecar handling, single-member
resolution, `/upload` auth + size caps, and artifact download
extraction. All suites pass.

---------

Co-authored-by: Filip131311 <contact@filipkaminski.com>
2026-07-10 12:29:48 +02:00
filip131311 2a01e407b3 chore: release 0.15.0 (submodule + workspace bump) (#484)
## What

- Bump the `packages/argent-private` submodule pointer from
`v0.11.0-8-g2953e89` to **v0.15.0** (`41484e4`). The superproject pin
had drifted ~4 releases behind the checked-out submodule.
- Lockstep-bump all `packages/*` workspace packages from `0.14.0` →
**0.15.0** (and the matching `package-lock.json` entries) to line up
with the submodule.

## Why

The submodule and the workspace were out of sync: the working tree had
`argent-private` checked out at v0.15.0 while the superproject still
pinned the v0.11 era, and the JS packages sat at 0.14.0. This adopts
v0.15.0 as the single, consistent version across the repo.

## Verification

- `node scripts/check-workspace-versions.mjs` → `All workspace packages
are at 0.15.0.`
- `git submodule status` → clean (no `+`), pinned at `v0.15.0`.
- Lockfile diff is exactly 15 version-field swaps (no unrelated churn).
2026-07-08 17:31:45 +02:00
filip131311 7bd017a796 feat(installer): committable repo-local (--local) install mode (#428)
## Summary

Adds an opt-in **committable / repo-local** install mode so a team can
version Argent _with the repo_. `argent init --local` installs
`@swmansion/argent` as a project **devDependency** and writes MCP
configs that launch the project-local copy — so after `git clone && npm
install`, every teammate has the same Argent version and MCP wiring with
**no global install and no per-developer `argent init`**.

**Global mode stays the default** and is unchanged; everything here is
additive and backward-compatible.

Motivation: the workflow some users already do by hand (#207).
Supersedes the earlier, since-closed #232 — this version is Windows-safe
(`node`, not the `.bin` shim), handles Yarn PnP, and closes the
cross-install tool-server hazards rather than leaving them.

## What changed

**`init`**
- `--local` / `--global` flags + an interactive "How should argent be
installed?" prompt. Mutually exclusive; non-interactive (`--yes`)
defaults to **global**.
- Local mode installs the devDependency with the package manager
detected from the project's **lockfile** (run with `cwd = projectRoot`).
- MCP entry points at the local copy: `node
node_modules/@swmansion/argent/dist/cli.js mcp` (the bin path is derived
from the installed `package.json` `bin` and existence-checked — forward
slashes, committable, Windows-safe). Yarn PnP → `yarn argent mcp`; an
`npx --no-install argent mcp` fallback is used only when the bin can't
be resolved.
- Records the mode in a committed `<root>/.argent/install.json`.
- Drops global-only adapters (Windsurf/Hermes) in local mode; prints
team-setup / commit guidance.

**`update`** — detects the project's mode; in local mode reads the
installed version from `node_modules` (not the global binary / npx
cache) and bumps the **devDependency + lockfile**, then re-emits the
local MCP command.

**`uninstall`** — scoped to the project's mode: a local-mode uninstall
removes the **local devDependency only and never the shared global
install**, and prunes `.argent/install.json`. Global-mode behavior is
unchanged.

**Cross-install hardening (the gate that makes per-project pinning
safe)**
- `launcher`: a tool-server is reused only when its `bundlePath` matches
the caller's — a repo-local pin never silently runs against (or kills) a
different version's server.
- `postinstall`: only stops a tool-server spawned from *this package's
own* bundle, so a devDependency install can't tear down an unrelated
editor session.

**Telemetry** — adds an `install_mode` dimension to
init/update/uninstall events.

## Testing

- New unit tests: install-mode helpers + record (`utils`), mode-aware
`getMcpEntry`/`resolveLocalCommandMode` incl. the opencode array form
(`mcp-configs`), local-only uninstall guard (`uninstall`), and
bundlePath-gated tool-server reuse (`launcher`).
- All existing suites green (installer 346, tools-client 115, telemetry
191, registry 83, argent 13, tool-server 1621). Build + lint clean.
- **Live end-to-end** in an isolated sandbox: `init --local` → real MCP
stdio handshake serves **69 tools from `node_modules`**; `update
--local` reads the local version; `uninstall --local` removes the local
install and **leaves the global install intact**; `init --global`
unchanged; flag conflict rejected.

## Notes / known limitations

- Recommended on mainstream platforms (macOS, Linux x64, Windows x64)
with prebuilt native binaries; Linux arm64 / Windows arm need a C/C++
toolchain to build `tree-sitter` on `npm install` (documented in the
README).
- Concurrent sessions of two *different* Argent versions on one machine
each spawn their own tool-server; an orphaned one idle-times-out (no
session is ever broken).

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2026-07-08 12:24:19 +02:00
filip131311 ba769f6941 fix(tools-client): unref spawned tool-server's stdout pipe so argent run exits (#473)
## Problem

If the tool-server is **not** already running the first time `argent run
<tool>` is called, the CLI prints its result but **never exits** — it
hangs indefinitely, even when the tool call succeeded.

## Root cause

`spawnToolsServer` (in `packages/argent-tools-client/src/launcher.ts`)
launches the tool-server `detached` with `stdio: ["ignore", "pipe",
logFd]` and calls `child.unref()`. But `child.unref()` only detaches the
**process** handle — the piped `child.stdout` is a *separate*
`net.Socket` handle. After reading the "listening" banner the code calls
`child.stdout.resume()` (so the child doesn't hit EPIPE) but leaves that
socket **ref'd**, which keeps the short-lived caller's event loop alive
forever.

The server-reuse path never opens that pipe, so only the first
(spawning) invocation hangs — exactly matching the report.

## Fix

Unref the stdout socket right after the ready banner is read:

```ts
child.stdout?.resume();
(child.stdout as { unref?: () => void } | null)?.unref?.();
```

Safe for both callers:
- **`argent run`** (short-lived): the loop drains the pipe while
working, then exits. The tool-server already installs an EPIPE handler
on stdout (`packages/tool-server/src/index.ts`), so it tolerates the
parent closing the read end.
- **MCP launcher** (long-lived): its loop stays alive by other means, so
the pipe still drains normally; the unref is a no-op for it.

## Testing

- New regression test `test/launcher-exit.test.ts` — **fails without the
fix**, passes with it.
- End-to-end control: a real subprocess calling the launcher **hangs**
with the unref removed and **exits cleanly** with it in place.
- Full local CI parity: `tsc --build`, `typecheck:tests` (all
workspaces), `typecheck:scripts`, full test suite (12 packages, ~2000
tests) — all green. ESLint + Prettier clean.
2026-07-06 17:13:45 +02:00
filip131311 c5b9857fa5 chore: bump all workspace packages to 0.14.0 (#454)
Bumps every package under `packages/*` from 0.13.0 → 0.14.0 in lockstep.

- All 14 workspace packages bumped to `0.14.0`.
- `package-lock.json` regenerated to match (also picks up npm's `"peer":
true` normalization).
- `npm run check:versions` passes: *All workspace packages are at
0.14.0.*

Internal deps use `file:../` references, so no dependency version
specifiers needed changing.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2026-07-01 20:36:58 +02:00
Hubert Gancarczyk 6bc5376746 fix(tool-server): stop list-devices from hanging - wedged-VVD stall and CLI socket leak (#419)
## Summary

`list-devices` (an `alwaysLoad` tool called at session start and
frequently after)
could appear to **hang**. Investigation found **two independent
causes**, both fixed here:

1. **Server-side (Vega):** a wedged Vega VVD turned `list-devices` into
a ~40s stall
   and leaked `vega` CLI subprocesses.
2. **Client-side (CLI):** every `argent run …` lingered a constant **~6s
after it had
already printed its result**, because a health-check `fetch` never
drained its
response body and kept an undici keep-alive socket ref'd until the
server closed it.

Net effect for `argent run list-devices`: **6.15s → 0.91s**, and the
worst-case
wedged-VVD stall drops from ~40s to a bounded short call.

---

## Problem 1 — wedged Vega VVD stalls `list-devices` (~40s) and leaks
processes

On a host with the Vega SDK installed and a **running-but-unresponsive
VVD**,
`list-devices`' Vega discovery path made **two serial 20s CLI calls**
that both hung:

- `vega device list` → SIGKILL at 20s
- then (recovery) `vega device info` → SIGKILL at another 20s → **~40s
total**

Three compounding defects:

- `execFileAsync` resolves on stdio **close**, not child **exit**. The
`vega` launcher
forks a `python3 → node → vda` worker tree; an orphaned worker holds the
stdout pipe
open, so the call stayed pending for the full timeout even after the
launcher was
  killed.
- The `device info` recovery fired even when `device list` had **hung**,
doubling the
  cost and spawning a second hanging subprocess.
- On timeout we killed only the **direct child**, so the `python3 → node
→ vda` worker
tree was **orphaned, not reaped** — leaking a process tree per call (a
wedged host had
  accumulated ~269 of them).

### Fix

- **`runVega` (`vega-cli.ts`):** switched from `execFileAsync` to
`spawn` with
**`detached: true`**, making the launcher its own **process-group
leader**, and on
timeout SIGKILL the **whole process group** (`process.kill(-pid)`) —
which reaps the
entire `python3 → node → vda` tree, **not just the launcher**. We also
destroy our
stdio stream ends so a pipe-holding worker can't keep the call pending.
(`execFile`
silently drops `detached`, which is why the switch to `spawn` was
required.)
- **Success path doesn't depend on stdio EOF either:** we prefer `close`
(fully
drained), but if a grandchild holds the stdout pipe open after a **clean
exit**, we
fall back to `exit` + a short (~1s) drain grace and resolve with the
captured
output — so a finished call can't be stalled into the timeout and then
rejected,
discarding valid data. The output cap is measured in real UTF-8 bytes.
For that
guarantee to hold at the boundary, the `exit` handler **disarms the main
timeout
timer**: once the child has terminated the wall-clock deadline is moot,
so a clean
exit landing within the drain grace of the deadline resolves with its
output instead
of being mis-rejected as a `timeout` (which would also wrongly suppress
the
    `listVegaDevices` recovery for a still-running VVD).
- **Belt-and-suspenders for a worker that escapes the group:** an
earlier
investigation reported the worker tree `setsid()`-ing into its own
session (which
would defeat a group-only SIGKILL). To be safe regardless of which is
true, on
timeout we **snapshot the launcher's descendants from the process table
*before*
killing** (a setsid'd worker keeps its `ppid` pointing at the launcher
until the
launcher dies, so a `ppid` tree-walk still reaches it) and SIGKILL those
pids
individually after the group kill. The snapshot is **precise** — only
this
launcher's own descendants, by pid, so a concurrent `vega` call's
workers are never
touched — and bounded, and the group kill runs regardless of whether it
succeeds.
- A real-subprocess test confirms **0 orphans** after a timeout,
including a worker
deliberately spawned into its **own** process group (so the group kill
alone cannot
    reach it — the test fails without the descendant sweep).
- **`listVegaDevices` (`vega-devices.ts`):** skip the `device info`
recovery only when
  `device list` **timed out** (a wedged agent — keyed off the failure's
`error_kind: "timeout"`), so a hung list no longer pays for a second
timeout wait. A
*fast* (non-timeout) list failure still recovers a genuinely-running VVD
rather than
mis-reporting it as stopped. Tightened discovery timeouts (20s → 6s/4s —
a generous
multiple of the ~1s healthy time so a cold-start `vega` launcher isn't
spuriously
timed out, with their sum (10s) kept under the 15s branch deadline with
margin).
- **`list-devices.ts`:** added a 15s `withDeadline` backstop around the
Android and Vega
branches so no single branch can stall the whole fan-out (returns
partial results +
logs a note). Sized to sit ~5s above the Vega branch's ~10s worst case,
so the
backstop never truncates a slow-but-completing branch (which would drop
a real
  device); an invariant test guards that ordering.
- **`readAvdName` (`adb.ts`):** run the two `getprop` probes
concurrently so a wedged
  emulator costs 5s, not 10s.

---

## Problem 2 — `argent run` hangs ~6s after printing output (CLI socket
leak)

Independent of Vega. The tool-server returns `list-devices` in <1s, but
the CLI process
took a **constant ~6s** to exit. Any path that calls `process.exit()`
was instant; any
path that returned naturally (`argent run …`, `argent run … --help`,
`argent tools`)
lingered ~6s.

**Root cause:** `isToolsServerHealthy` (`launcher.ts`) did
`const res = await fetch('/tools'); return res.ok;` and **never drained
the body**.
`/tools` is a large (~112KB) payload, so undici keeps the keep-alive
socket **ref'd**
until the tool-server's idle `keepAliveTimeout` (~5s) closes it.
`ensureToolsServer`
runs this health check on every invocation, and `run()`'s success path
has no
`process.exit(0)`, so the ref'd socket holds the event loop open.
(Confirmed
telemetry-independent — `DO_NOT_TRACK` made no difference.) The same
undrained-body
pattern existed in `link.ts` (`argent link`).

### Fix

Drain the response body before returning in both health-check-style
fetches:

```ts
await res.body?.cancel().catch(() => {});
return res.ok;
```

(`fetchTools`/`callTool` were already fine — they `await res.json()`.)

---

## Testing

**Empirical, end-to-end** (on a machine reproducing the wedged VVD):

| Command | Before | After |
| --- | --- | --- |
| `argent run list-devices` | 6.15s | **0.91s** |
| `argent run list-devices --help` | 6.14s | **0.16s** |
| `argent tools` | 6.15s | **0.16s** |
| handle probe after run | 2 sockets ref'd for ~6s | **none — immediate
exit** |

- Raw `POST /tools/list-devices` was ~0.7s throughout — confirming the
~5.4s was pure
  CLI teardown, not tool work.
- **Process-group reap proven empirically:** the live `vega → bash →
python3 → node`
tree all shares the detached launcher's PGID; a group-SIGKILL left **0
survivors**.
A real-subprocess test (unmocked `runVega` vs. a fake hanging `vega` on
`PATH`) confirms
  it settles at its deadline **and** leaves **0 orphaned processes**.

**Automated:**

- `vega-cli-timeout.test.ts` (real-subprocess test) — `runVega` rejects
on its deadline,
**reaps the entire worker tree (0 orphans)** — the fake's hung worker is
spawned into its
**own** process group, so the test fails without the descendant sweep —
and returns
normally when the CLI responds. Also covers the boundary case: a **clean
exit whose
drain grace outlasts the deadline** resolves with its captured output
rather than being
rejected as a `timeout` (guards the `exit`-handler timer disarm above).
- `vega-adb-connect-recovery.test.ts` — a *timed-out* `device list` does
not trigger a
second `device info` call (no stacking); a *fast, non-timeout* failure
**still** recovers
  a running VVD.
- `list-devices-deadline.test.ts` — `withDeadline` returns the branch
value on the happy
path and the fallback (with a logged note) when a branch exceeds the
deadline.
- `list-devices.test.ts` — added a `spawn` mock (vega moved off
`execFile`).
- `launcher-helpers.test.ts` — `isToolsServerHealthy` cancels (drains)
its response body.
- Suites green: **tool-server (1610), argent-tools-client (109),
argent-cli (84)**.
- eslint (`--max-warnings 0`), prettier, and `tsc` clean.

---
2026-07-01 11:02:33 +02:00
filip131311 5f9dd48e2a fix(tool-server): prevent duplicate tool-servers after an nvm node switch (#385)
## Problem

Switching Node versions with **nvm** can leave **two (or more)
`tool-server` processes alive at once** while
`~/.argent/tool-server.json` tracks only one. The extra servers are
orphaned (untracked, on leaked ports) until their 30‑minute idle
timeout.

This was reproduced live: a machine had **three** `node
…/dist/tool-server.cjs start` processes running, all spawned within the
same minute, with state tracking only the last one.

## Root cause

`@argent/tools-client` `ensureToolsServer()` ran a non-atomic
`read state → check alive+healthy → clearState → spawn → writeState`
sequence with:

1. **No cross-process serialization.** An nvm switch relaunches `argent
mcp` under the new Node while the *previous* MCP's 30s health monitor
reconnects — several launchers run `ensureToolsServer()` at nearly the
same time, all observe "no healthy server", and each spawns its own
**detached** server on a fresh free port. Last‑writer‑wins on the state
file orphans the rest.
2. **No kill-before-respawn.** When the tracked server was alive but
unhealthy, the old code just `clearState()`d and spawned a new one,
leaving the old detached process running untracked.

## Fix (`packages/argent-tools-client/src/launcher.ts`)

- **O_EXCL spawn lock** (`~/.argent/tool-server.lock`) serialises the
spawn decision across processes: per-acquisition nonce, stale‑steal
(dead holder / aged lock), bounded wait, and a hot‑spin‑proof reclaim
loop. Inside the lock, `ensureToolsServer` **double‑checks** for a
peer‑spawned healthy server before spawning, so a burst of concurrent
launches collapses to a single server.
- **Kill-before-respawn** terminates a wedged server we own before
spawning its replacement, with three guards so it can never signal the
wrong process:
- `managed === "autospawn"` — a user's `argent server start` server is
now tagged `managed: "cli"` and is **never** killed by the auto‑spawn
path;
- a **structural** `node <bundlePath> start` command-line identity match
(not a bare substring);
- a re-check of that identity immediately before each signal (covers PID
reuse across the SIGTERM grace window).
- **`spawnToolsServer` reaps its detached child on every reject path**,
so a readiness timeout can no longer leak a child that binds its port
moments later as an untracked second server.

The lock-free fast path (reuse a healthy tracked server) is unchanged,
so steady‑state behaviour and latency are unaffected; the lock is only
taken when a spawn is actually needed.

## Tests

New
`packages/argent-tools-client/test/launcher-duplicate-spawn.test.ts`:

- a 5× concurrent launch burst collapses to **one** server;
- an alive‑but‑unhealthy `autospawn` server is terminated (not orphaned)
on respawn;
- a recycled pid whose command line doesn't match is **never**
signalled;
- a `managed: "cli"` server is left alive instead of being killed;
- a readiness timeout reaps the spawned child (no orphan).

Each case was verified to **fail against the pre-fix code** and pass
with the fix. Existing `launcher-spawn` / `launcher-state` suites are
unchanged and green.

> Note: the `@swmansion/argent` `dispatcher › forwards SIGTERM to the
child binary` unit test is a pre-existing flake (alternates pass/fail
independent of this change, which doesn't touch that package).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Radoniusz Cloudiusz <radoniusz@radoniusz.local>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-29 17:20:37 +02:00
dependabot[bot] 02695e6704 chore(deps)(deps): bump the npm-major group across 1 directory with 3 updates (#413)
Bumps the npm-major group with 3 updates in the / directory:
[@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node),
[dotenv](https://github.com/motdotla/dotenv) and
[ini](https://github.com/npm/ini).

Updates `@types/node` from 25.9.3 to 26.0.1
<details>
<summary>Commits</summary>
<ul>
<li>See full diff in <a
href="https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node">compare
view</a></li>
</ul>
</details>
<br />

Updates `dotenv` from 16.6.1 to 17.4.2
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/motdotla/dotenv/blob/master/CHANGELOG.md">dotenv's
changelog</a>.</em></p>
<blockquote>
<h2><a
href="https://github.com/motdotla/dotenv/compare/v17.4.1...v17.4.2">17.4.2</a>
(2026-04-12)</h2>
<h3>Changed</h3>
<ul>
<li>Improved skill files - tightened up details (<a
href="https://redirect.github.com/motdotla/dotenv/pull/1009">#1009</a>)</li>
</ul>
<h2><a
href="https://github.com/motdotla/dotenv/compare/v17.4.0...v17.4.1">17.4.1</a>
(2026-04-05)</h2>
<h3>Changed</h3>
<ul>
<li>Change text <code>injecting</code> to <code>injected</code> (<a
href="https://redirect.github.com/motdotla/dotenv/pull/1005">#1005</a>)</li>
</ul>
<h2><a
href="https://github.com/motdotla/dotenv/compare/v17.3.1...v17.4.0">17.4.0</a>
(2026-04-01)</h2>
<h3>Added</h3>
<ul>
<li>Add <code>skills/</code> folder with focused agent skills:
<code>skills/dotenv/SKILL.md</code> (core usage) and
<code>skills/dotenvx/SKILL.md</code> (encryption, multiple environments,
variable expansion) for AI coding agent discovery via the skills.sh
ecosystem (<code>npx skills add motdotla/dotenv</code>)</li>
</ul>
<h3>Changed</h3>
<ul>
<li>Tighten up logs: <code>◇ injecting env (14) from .env</code> (<a
href="https://redirect.github.com/motdotla/dotenv/pull/1003">#1003</a>)</li>
</ul>
<h2><a
href="https://github.com/motdotla/dotenv/compare/v17.3.0...v17.3.1">17.3.1</a>
(2026-02-12)</h2>
<h3>Changed</h3>
<ul>
<li>Fix as2 example command in README and update spanish README</li>
</ul>
<h2><a
href="https://github.com/motdotla/dotenv/compare/v17.2.4...v17.3.0">17.3.0</a>
(2026-02-12)</h2>
<h3>Added</h3>
<ul>
<li>Add a new README section on dotenv’s approach to the agentic
future.</li>
</ul>
<h3>Changed</h3>
<ul>
<li>Rewrite README to get humans started more quickly with less noise
while simultaneously making more accessible for llms and agents to go
deeper into details.</li>
</ul>
<h2><a
href="https://github.com/motdotla/dotenv/compare/v17.2.3...v17.2.4">17.2.4</a>
(2026-02-05)</h2>
<h3>Changed</h3>
<ul>
<li>Make <code>DotenvPopulateInput</code> accept
<code>NodeJS.ProcessEnv</code> type (<a
href="https://redirect.github.com/motdotla/dotenv/pull/915">#915</a>)</li>
</ul>
<ul>
<li>Give back to dotenv by checking out my newest project <a
href="https://github.com/vestauth/vestauth">vestauth</a>. It is auth for
agents. Thank you for using my software.</li>
</ul>
<h2><a
href="https://github.com/motdotla/dotenv/compare/v17.2.2...v17.2.3">17.2.3</a>
(2025-09-29)</h2>
<h3>Changed</h3>
<ul>
<li>Fixed typescript error definition (<a
href="https://redirect.github.com/motdotla/dotenv/pull/912">#912</a>)</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/motdotla/dotenv/commit/f116f70310abab44fbfddbaeb833698b5bf84a9b"><code>f116f70</code></a>
17.4.2</li>
<li><a
href="https://github.com/motdotla/dotenv/commit/3a8161274fdd745239b86e604f4a7e972a1d3902"><code>3a81612</code></a>
fix visual order of faq</li>
<li><a
href="https://github.com/motdotla/dotenv/commit/13f55a89e136b2024e68d277b836dd5260fc16cf"><code>13f55a8</code></a>
Merge branch 'skill'</li>
<li><a
href="https://github.com/motdotla/dotenv/commit/4bbbf73f0906bd69975c48bf310a84b686e5b1b4"><code>4bbbf73</code></a>
reorganize faq</li>
<li><a
href="https://github.com/motdotla/dotenv/commit/c3da64bb2ba1d0e02f8b9b2b7ccb7e6f7a51d56c"><code>c3da64b</code></a>
Merge pull request <a
href="https://redirect.github.com/motdotla/dotenv/issues/1009">#1009</a>
from motdotla/skill</li>
<li><a
href="https://github.com/motdotla/dotenv/commit/6f743b173fbd6c26f7eab7040d251f9a6c8b977d"><code>6f743b1</code></a>
update source</li>
<li><a
href="https://github.com/motdotla/dotenv/commit/fc2c6247e858a32d4024cb06a5b0c79aa35851f5"><code>fc2c624</code></a>
update skill</li>
<li><a
href="https://github.com/motdotla/dotenv/commit/972315ba74bb2bbba4483d112e853fd26006ef8a"><code>972315b</code></a>
Tighten up skill</li>
<li><a
href="https://github.com/motdotla/dotenv/commit/2795fce3d1ed07b4c570f1e06ab1c0d533c86997"><code>2795fce</code></a>
reorganize faq</li>
<li><a
href="https://github.com/motdotla/dotenv/commit/d5495d4ae8e4e41ef9a682c9e00c81552794274e"><code>d5495d4</code></a>
adjust skill</li>
<li>Additional commits viewable in <a
href="https://github.com/motdotla/dotenv/compare/v16.6.1...v17.4.2">compare
view</a></li>
</ul>
</details>
<br />

Updates `ini` from 4.1.3 to 7.0.0
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a href="https://github.com/npm/ini/releases">ini's
releases</a>.</em></p>
<blockquote>
<h2>v7.0.0</h2>
<h2><a
href="https://github.com/npm/ini/compare/v6.0.0...v7.0.0">7.0.0</a>
(2026-05-08)</h2>
<h3>⚠️ BREAKING CHANGES</h3>
<ul>
<li><code>ini</code> now supports node <code>^22.22.2 || ^24.15.0 ||
&gt;=26.0.0</code></li>
<li>template-oss-apply</li>
</ul>
<h3>Features</h3>
<ul>
<li><a
href="https://github.com/npm/ini/commit/55b6841261aad580ce979c1ada97885a0617b965"><code>55b6841</code></a>
<a href="https://redirect.github.com/npm/ini/pull/301">#301</a> bump to
new node engine range (<a
href="https://github.com/owlstronaut"><code>@​owlstronaut</code></a>)</li>
<li><a
href="https://github.com/npm/ini/commit/2b11ba8283afa33211844be6fa29794970ccf7b7"><code>2b11ba8</code></a>
<a href="https://redirect.github.com/npm/ini/pull/301">#301</a>
template-oss-apply (<a
href="https://github.com/owlstronaut"><code>@​owlstronaut</code></a>)</li>
</ul>
<h3>Chores</h3>
<ul>
<li><a
href="https://github.com/npm/ini/commit/f6ed5bee3d7b1c175b687c703213217b23df9199"><code>f6ed5be</code></a>
<a href="https://redirect.github.com/npm/ini/pull/301">#301</a>
template-oss-apply (<a
href="https://github.com/owlstronaut"><code>@​owlstronaut</code></a>)</li>
<li><a
href="https://github.com/npm/ini/commit/a2c835ec4690c5e67651a403d0f952aaff018f42"><code>a2c835e</code></a>
<a href="https://redirect.github.com/npm/ini/pull/293">#293</a> bump
<code>@​npmcli/eslint-config</code> from 5.1.0 to 6.0.0 (<a
href="https://redirect.github.com/npm/ini/issues/293">#293</a>) (<a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot])</li>
<li><a
href="https://github.com/npm/ini/commit/3661dce193f03675ec6ddc0938e2483d0ab41f38"><code>3661dce</code></a>
<a href="https://redirect.github.com/npm/ini/pull/296">#296</a> bump
<code>@​npmcli/template-oss</code> from 4.28.0 to 4.28.1 (<a
href="https://redirect.github.com/npm/ini/issues/296">#296</a>) (<a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot],
<a
href="https://github.com/npm-cli-bot"><code>@​npm-cli-bot</code></a>)</li>
</ul>
<h2>v6.0.0</h2>
<h2><a
href="https://github.com/npm/ini/compare/v5.0.0...v6.0.0">6.0.0</a>
(2025-10-22)</h2>
<h3>⚠️ BREAKING CHANGES</h3>
<ul>
<li><code>ini</code> now supports node <code>^20.17.0 ||
&gt;=22.9.0</code></li>
</ul>
<h3>Bug Fixes</h3>
<ul>
<li><a
href="https://github.com/npm/ini/commit/6d395a4aaf7d7a5e32e250809cb7bd04c6a27b66"><code>6d395a4</code></a>
<a href="https://redirect.github.com/npm/ini/pull/291">#291</a> align to
npm 11 node engine range (<a
href="https://redirect.github.com/npm/ini/issues/291">#291</a>) (<a
href="https://github.com/owlstronaut"><code>@​owlstronaut</code></a>)</li>
</ul>
<h3>Chores</h3>
<ul>
<li><a
href="https://github.com/npm/ini/commit/23a36a9ca194d97486ed367f63d13a2cde1d1ce5"><code>23a36a9</code></a>
<a href="https://redirect.github.com/npm/ini/pull/290">#290</a> bump
<code>@​npmcli/template-oss</code> from 4.26.0 to 4.27.1 (<a
href="https://redirect.github.com/npm/ini/issues/290">#290</a>) (<a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot],
<a
href="https://github.com/npm-cli-bot"><code>@​npm-cli-bot</code></a>)</li>
</ul>
<h2>v5.0.0</h2>
<h2><a
href="https://github.com/npm/ini/compare/v4.1.3...v5.0.0">5.0.0</a>
(2024-09-03)</h2>
<h3>⚠️ BREAKING CHANGES</h3>
<ul>
<li><code>ini</code> now supports node <code>^18.17.0 ||
&gt;=20.5.0</code></li>
</ul>
<h3>Bug Fixes</h3>
<ul>
<li><a
href="https://github.com/npm/ini/commit/3eca645de8586b8349acd79211b01a9199d8fd32"><code>3eca645</code></a>
<a href="https://redirect.github.com/npm/ini/pull/279">#279</a> align to
npm 10 node engine range (<a
href="https://github.com/hashtagchris"><code>@​hashtagchris</code></a>)</li>
</ul>
<h3>Chores</h3>
<ul>
<li><a
href="https://github.com/npm/ini/commit/c89e2090d7989bb91255ec46eaaf5f8cb1c34b34"><code>c89e209</code></a>
<a href="https://redirect.github.com/npm/ini/pull/279">#279</a> run
template-oss-apply (<a
href="https://github.com/hashtagchris"><code>@​hashtagchris</code></a>)</li>
<li><a
href="https://github.com/npm/ini/commit/fc447500efe6523746fd9db9d3123485eed14b76"><code>fc44750</code></a>
<a href="https://redirect.github.com/npm/ini/pull/276">#276</a> bump
<code>@​npmcli/eslint-config</code> from 4.0.5 to 5.0.0 (<a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot])</li>
<li><a
href="https://github.com/npm/ini/commit/21c20bb1729005ab46e0f248cea1a881c6ab5b49"><code>21c20bb</code></a>
<a href="https://redirect.github.com/npm/ini/pull/277">#277</a>
postinstall for dependabot template-oss PR (<a
href="https://github.com/hashtagchris"><code>@​hashtagchris</code></a>)</li>
<li><a
href="https://github.com/npm/ini/commit/44b3b503fdd923fb230b6a4d859014b9364ee466"><code>44b3b50</code></a>
<a href="https://redirect.github.com/npm/ini/pull/277">#277</a> bump
<code>@​npmcli/template-oss</code> from 4.23.1 to 4.23.3 (<a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot])</li>
</ul>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/npm/ini/blob/main/CHANGELOG.md">ini's
changelog</a>.</em></p>
<blockquote>
<h2><a
href="https://github.com/npm/ini/compare/v6.0.0...v7.0.0">7.0.0</a>
(2026-05-08)</h2>
<h3>⚠️ BREAKING CHANGES</h3>
<ul>
<li><code>ini</code> now supports node <code>^22.22.2 || ^24.15.0 ||
&gt;=26.0.0</code></li>
<li>template-oss-apply</li>
</ul>
<h3>Features</h3>
<ul>
<li><a
href="https://github.com/npm/ini/commit/55b6841261aad580ce979c1ada97885a0617b965"><code>55b6841</code></a>
<a href="https://redirect.github.com/npm/ini/pull/301">#301</a> bump to
new node engine range (<a
href="https://github.com/owlstronaut"><code>@​owlstronaut</code></a>)</li>
<li><a
href="https://github.com/npm/ini/commit/2b11ba8283afa33211844be6fa29794970ccf7b7"><code>2b11ba8</code></a>
<a href="https://redirect.github.com/npm/ini/pull/301">#301</a>
template-oss-apply (<a
href="https://github.com/owlstronaut"><code>@​owlstronaut</code></a>)</li>
</ul>
<h3>Chores</h3>
<ul>
<li><a
href="https://github.com/npm/ini/commit/f6ed5bee3d7b1c175b687c703213217b23df9199"><code>f6ed5be</code></a>
<a href="https://redirect.github.com/npm/ini/pull/301">#301</a>
template-oss-apply (<a
href="https://github.com/owlstronaut"><code>@​owlstronaut</code></a>)</li>
<li><a
href="https://github.com/npm/ini/commit/a2c835ec4690c5e67651a403d0f952aaff018f42"><code>a2c835e</code></a>
<a href="https://redirect.github.com/npm/ini/pull/293">#293</a> bump
<code>@​npmcli/eslint-config</code> from 5.1.0 to 6.0.0 (<a
href="https://redirect.github.com/npm/ini/issues/293">#293</a>) (<a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot])</li>
<li><a
href="https://github.com/npm/ini/commit/3661dce193f03675ec6ddc0938e2483d0ab41f38"><code>3661dce</code></a>
<a href="https://redirect.github.com/npm/ini/pull/296">#296</a> bump
<code>@​npmcli/template-oss</code> from 4.28.0 to 4.28.1 (<a
href="https://redirect.github.com/npm/ini/issues/296">#296</a>) (<a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot],
<a
href="https://github.com/npm-cli-bot"><code>@​npm-cli-bot</code></a>)</li>
</ul>
<h2><a
href="https://github.com/npm/ini/compare/v5.0.0...v6.0.0">6.0.0</a>
(2025-10-22)</h2>
<h3>⚠️ BREAKING CHANGES</h3>
<ul>
<li><code>ini</code> now supports node <code>^20.17.0 ||
&gt;=22.9.0</code></li>
</ul>
<h3>Bug Fixes</h3>
<ul>
<li><a
href="https://github.com/npm/ini/commit/6d395a4aaf7d7a5e32e250809cb7bd04c6a27b66"><code>6d395a4</code></a>
<a href="https://redirect.github.com/npm/ini/pull/291">#291</a> align to
npm 11 node engine range (<a
href="https://redirect.github.com/npm/ini/issues/291">#291</a>) (<a
href="https://github.com/owlstronaut"><code>@​owlstronaut</code></a>)</li>
</ul>
<h3>Chores</h3>
<ul>
<li><a
href="https://github.com/npm/ini/commit/23a36a9ca194d97486ed367f63d13a2cde1d1ce5"><code>23a36a9</code></a>
<a href="https://redirect.github.com/npm/ini/pull/290">#290</a> bump
<code>@​npmcli/template-oss</code> from 4.26.0 to 4.27.1 (<a
href="https://redirect.github.com/npm/ini/issues/290">#290</a>) (<a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot],
<a
href="https://github.com/npm-cli-bot"><code>@​npm-cli-bot</code></a>)</li>
</ul>
<h2><a
href="https://github.com/npm/ini/compare/v4.1.3...v5.0.0">5.0.0</a>
(2024-09-03)</h2>
<h3>⚠️ BREAKING CHANGES</h3>
<ul>
<li><code>ini</code> now supports node <code>^18.17.0 ||
&gt;=20.5.0</code></li>
</ul>
<h3>Bug Fixes</h3>
<ul>
<li><a
href="https://github.com/npm/ini/commit/3eca645de8586b8349acd79211b01a9199d8fd32"><code>3eca645</code></a>
<a href="https://redirect.github.com/npm/ini/pull/279">#279</a> align to
npm 10 node engine range (<a
href="https://github.com/hashtagchris"><code>@​hashtagchris</code></a>)</li>
</ul>
<h3>Chores</h3>
<ul>
<li><a
href="https://github.com/npm/ini/commit/c89e2090d7989bb91255ec46eaaf5f8cb1c34b34"><code>c89e209</code></a>
<a href="https://redirect.github.com/npm/ini/pull/279">#279</a> run
template-oss-apply (<a
href="https://github.com/hashtagchris"><code>@​hashtagchris</code></a>)</li>
<li><a
href="https://github.com/npm/ini/commit/fc447500efe6523746fd9db9d3123485eed14b76"><code>fc44750</code></a>
<a href="https://redirect.github.com/npm/ini/pull/276">#276</a> bump
<code>@​npmcli/eslint-config</code> from 4.0.5 to 5.0.0 (<a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot])</li>
<li><a
href="https://github.com/npm/ini/commit/21c20bb1729005ab46e0f248cea1a881c6ab5b49"><code>21c20bb</code></a>
<a href="https://redirect.github.com/npm/ini/pull/277">#277</a>
postinstall for dependabot template-oss PR (<a
href="https://github.com/hashtagchris"><code>@​hashtagchris</code></a>)</li>
<li><a
href="https://github.com/npm/ini/commit/44b3b503fdd923fb230b6a4d859014b9364ee466"><code>44b3b50</code></a>
<a href="https://redirect.github.com/npm/ini/pull/277">#277</a> bump
<code>@​npmcli/template-oss</code> from 4.23.1 to 4.23.3 (<a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot])</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/npm/ini/commit/847941ced4fb8465f0ccb383fd8b15c7e5aa09fc"><code>847941c</code></a>
chore: release 7.0.0 (<a
href="https://redirect.github.com/npm/ini/issues/302">#302</a>)</li>
<li><a
href="https://github.com/npm/ini/commit/f6ed5bee3d7b1c175b687c703213217b23df9199"><code>f6ed5be</code></a>
chore: template-oss-apply</li>
<li><a
href="https://github.com/npm/ini/commit/55b6841261aad580ce979c1ada97885a0617b965"><code>55b6841</code></a>
feat!: bump to new node engine range</li>
<li><a
href="https://github.com/npm/ini/commit/2b11ba8283afa33211844be6fa29794970ccf7b7"><code>2b11ba8</code></a>
feat!: template-oss-apply</li>
<li><a
href="https://github.com/npm/ini/commit/e8d16cbbbb91d503fe14163024bd2a01a0446d32"><code>e8d16cb</code></a>
deps &amp; engine update</li>
<li><a
href="https://github.com/npm/ini/commit/3661dce193f03675ec6ddc0938e2483d0ab41f38"><code>3661dce</code></a>
chore: bump <code>@​npmcli/template-oss</code> from 4.28.0 to 4.28.1 (<a
href="https://redirect.github.com/npm/ini/issues/296">#296</a>)</li>
<li><a
href="https://github.com/npm/ini/commit/5d67f4b487a00aa3e2119776192030356e0161ba"><code>5d67f4b</code></a>
chore: bump <code>@​npmcli/template-oss</code> from 4.27.1 to 4.28.0 (<a
href="https://redirect.github.com/npm/ini/issues/294">#294</a>)</li>
<li><a
href="https://github.com/npm/ini/commit/a2c835ec4690c5e67651a403d0f952aaff018f42"><code>a2c835e</code></a>
chore: bump <code>@​npmcli/eslint-config</code> from 5.1.0 to 6.0.0 (<a
href="https://redirect.github.com/npm/ini/issues/293">#293</a>)</li>
<li><a
href="https://github.com/npm/ini/commit/180a8d5c72d7f13ed70c53619132f3a8ee5ac6ed"><code>180a8d5</code></a>
chore: release 6.0.0 (<a
href="https://redirect.github.com/npm/ini/issues/292">#292</a>)</li>
<li><a
href="https://github.com/npm/ini/commit/6d395a4aaf7d7a5e32e250809cb7bd04c6a27b66"><code>6d395a4</code></a>
fix!: align to npm 11 node engine range (<a
href="https://redirect.github.com/npm/ini/issues/291">#291</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/npm/ini/compare/v4.1.3...v7.0.0">compare
view</a></li>
</ul>
</details>
<details>
<summary>Maintainer changes</summary>
<p>This version was pushed to npm by <a
href="https://www.npmjs.com/~GitHub%20Actions">GitHub Actions</a>, a new
releaser for ini since your current version.</p>
</details>
<br />

Updates `@types/node` from 25.9.3 to 26.0.1
<details>
<summary>Commits</summary>
<ul>
<li>See full diff in <a
href="https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node">compare
view</a></li>
</ul>
</details>
<br />

Updates `@types/node` from 25.9.3 to 26.0.1
<details>
<summary>Commits</summary>
<ul>
<li>See full diff in <a
href="https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node">compare
view</a></li>
</ul>
</details>
<br />

Updates `@types/node` from 25.9.3 to 26.0.1
<details>
<summary>Commits</summary>
<ul>
<li>See full diff in <a
href="https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node">compare
view</a></li>
</ul>
</details>
<br />

Updates `@types/node` from 25.9.3 to 26.0.1
<details>
<summary>Commits</summary>
<ul>
<li>See full diff in <a
href="https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node">compare
view</a></li>
</ul>
</details>
<br />

Updates `@types/node` from 25.9.3 to 26.0.1
<details>
<summary>Commits</summary>
<ul>
<li>See full diff in <a
href="https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node">compare
view</a></li>
</ul>
</details>
<br />

Updates `@types/node` from 25.9.3 to 26.0.1
<details>
<summary>Commits</summary>
<ul>
<li>See full diff in <a
href="https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node">compare
view</a></li>
</ul>
</details>
<br />

Updates `@types/node` from 25.9.3 to 26.0.1
<details>
<summary>Commits</summary>
<ul>
<li>See full diff in <a
href="https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node">compare
view</a></li>
</ul>
</details>
<br />


Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore <dependency name> major version` will close this
group update PR and stop Dependabot creating any more for the specific
dependency's major version (unless you unignore this specific
dependency's major version or upgrade to it yourself)
- `@dependabot ignore <dependency name> minor version` will close this
group update PR and stop Dependabot creating any more for the specific
dependency's minor version (unless you unignore this specific
dependency's minor version or upgrade to it yourself)
- `@dependabot ignore <dependency name>` will close this group update PR
and stop Dependabot creating any more for the specific dependency
(unless you unignore this specific dependency or upgrade to it yourself)
- `@dependabot unignore <dependency name>` will remove all of the ignore
conditions of the specified dependency
- `@dependabot unignore <dependency name> <ignore condition>` will
remove the ignore condition of the specified dependency and ignore
conditions


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-26 17:40:19 +02:00
Ignacy Łątka 270c9a158a chore: bump to 0.13.0 (#412)
as per title
2026-06-24 20:27:08 +02:00
filip131311 31bb59fb60 chore: bump all workspace packages to 0.12.1 (#381)
Bumps every workspace package under `packages/*` from `0.12.0` →
`0.12.1`, keeping the monorepo's lockstep versioning intact.

## Changes
- `version` bumped to `0.12.1` in all 15 workspace `package.json` files
- `package-lock.json` workspace entries synced to match

## Verification
- `npm run check:versions` → `All workspace packages are at 0.12.1.`

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2026-06-19 21:49:02 +02:00
Ignacy Łątka 02e500884f chore(deps): npm-minor-patch group excluding tree-sitter (fixes #348) (#355)
## What

Applies the safe updates from dependabot #348 (`npm-minor-patch` group)
and **excludes the one bump that breaks the build**, `tree-sitter`.

| Package | From | To |
| --- | --- | --- |
| prettier | 3.8.3 | 3.8.4 |
| @clack/prompts | 1.1.0 / 1.4.0 | 1.5.1 |
| @types/node | 25.9.0 | 25.9.3 |
| esbuild | 0.28.0 | 0.28.1 |
| semver | 7.7.4 / 7.8.0 | 7.8.4 |
| vitest | 4.1.6 | 4.1.9 |
| tree-sitter | 0.21.1 | **held at 0.21.1** |

(`ws` was part of #348's group but is already at 8.21.0 on `main`, so
it's untouched here.)

## Why tree-sitter is excluded

`tree-sitter@0.25.0` ships **no Node 24 prebuilds**, and its
`binding.gyp` forces `-std=c++17` while Node 22+ v8 (cppgc) headers
require C++20, so the native build fails `npm ci`/`npm install` on
current Node. That's exactly why #348's `ESLint` job (Node 24) fails at
install:

```
.../node_modules/tree-sitter ... node-gyp-build
.../include/node/v8config.h:13:2: error: #error "C++20 or later required."
```

(#348's `Unit tests` job is pinned to Node 20, where it still compiles —
which is why that PR looks half-green.) There is no tree-sitter release
between 0.22.4 and 0.25.0, so there's no newer working version.
`^0.21.1` ships N-API prebuilds (no compile) and installs cleanly on
Node 20 / 22 / 24.

This PR also adds a Dependabot `ignore` for tree-sitter minor/major so
the group stops re-proposing the broken bump. Patch updates stay
allowed; drop the ignore once upstream restores prebuilt binaries.

## Verification (local)

- Lockfile regenerated on **Node 20 / npm 10.8.2** (`npm install
--package-lock-only --ignore-scripts`), idempotent under a re-run —
won't trip the strict lockfile check. tree-sitter resolves to 0.21.1
(prebuilt, no native compile).
- Full CI-equivalent on **Node 20**: `tsc --build`, `eslint
--max-warnings 0`, `prettier --check`,
`typecheck:tests`/`typecheck:scripts`, full unit suite incl. **1250
tool-server tests** — all green.
- `npm ci` from the committed lockfile succeeds.
- **Live device E2E**: ran the built tool-server with the bumped deps,
captured a real screenshot of a booted iPhone 16 Pro Max
(`simulator-client` uses `ws`), then downloaded the PNG artifact over
HTTP — `200`, 331 KB PNG.

Supersedes #348 (Dependabot auto-closes it once the safe bumps land and
tree-sitter is ignored).
2026-06-18 11:50:43 +02:00
filip131311 c832109573 chore: bump versions to 0.12.0 (#358)
Bumps the package suite from `0.11.0` to **`0.12.0`**.

## Changed
- All release packages `0.11.0 → 0.12.0`: `@swmansion/argent`,
`@argent/cli`, `@argent/installer`, `@argent/mcp`,
`@argent/tools-client`, `@argent/native-devtools-android`,
`@argent/native-devtools-ios`, `@argent/registry`, `@argent/skills`,
`@argent/tool-server`, `@argent/update-core`,
`@argent/configuration-core`.
- `@argent/preview-window` `0.9.0 → 0.12.0` — aligned to the suite (was
lagging a release behind; `private`/unpublished).
- Regenerated the matching `package-lock.json` workspace version
entries.

## Not changed
- `@argent/ui` stays at `0.0.1` (separate pre-release track).
- The private workspace root has no version.
- Internal deps use `file:../` specifiers, so no dependency ranges
needed updating.

No source/runtime changes — `argent --version` reads from `package.json`
at runtime.
2026-06-17 15:42:11 +02:00
Ignacy Łątka acfe80ab6b chore: add ESLint with typescript-eslint (#349)
## What

Adds type-aware linting to the workspace. Until now the only static gate
on JS/TS was Prettier (formatting) and `tsc` — there was no linter, so
the whole class of bugs `tsc` doesn't catch (floating promises, misused
promises, throwing non-Errors, unsafe stringification, dead code) went
unguarded across ~450 TS files.

- **`eslint.config.mjs`** — flat config. `recommendedTypeChecked` runs
on the TS sources via an explicit `project` list (each package keeps
tests in a separate `tsconfig.test.json` that `projectService` can't
auto-discover, so it's listed explicitly). Type-checked rules are
disabled for the loosely-typed plain-JS build/dev scripts. The
`argent-private` submodule and build artifacts are ignored (mirrors
`.prettierignore`).
- **`lint` / `lint:fix`** npm scripts.
- **`.github/workflows/lint.yml`** — builds the workspace (type-aware
rules need the referenced projects' declarations) then runs `eslint .`.
Node 24 (ESLint 10 requires `^20.19 || ^22.13 || >=24`).

## Rule calibration

This is an adoption PR, so it's deliberately calibrated rather than
maximal:

- **Kept on (bug-catchers):** `no-floating-promises`,
`no-misused-promises` (with `checksVoidReturn: false` to drop the noisy
callback variant), `only-throw-error`, `no-base-to-string`,
`prefer-promise-reject-errors`, `no-unused-vars` (`_`-prefix escape
hatch), plus the ESLint recommended set.
- **Parked off as documented debt:** the high-volume style/`any`-family
rules — `no-unsafe-*`, `require-await`, `no-unnecessary-type-assertion`,
`restrict-template-expressions`, `unbound-method`, `no-explicit-any`,
etc. These flag broad classes of pre-existing code and are out of scope
here; each is commented as a ratchet target for follow-up passes.

All findings from the kept-on rules are fixed in this PR (dead
code/imports, error-cause chaining, **3 genuine floating promises**,
non-Error throws, unsafe stringification). Stale unused `eslint-disable`
directives left over in the tree were removed by `--fix`.

## Verification

Locally, all green: `eslint .` (0 errors), `tsc --build`,
`typecheck:tests`/`typecheck:scripts`, the full `vitest` suite (no
behavior change from the fixes), and `prettier --check`.

## Not included (intentionally)

Other static-analysis gates discussed (CodeQL, dependency-audit,
ShellCheck, actionlint) are out of scope for this PR.
2026-06-17 12:20:55 +02:00
filip131311 c01f41cd5f feat(remote): input-side file boundary — flows, screenshot-diff, profiler reports, workspace tools (#319)
## Why

PR #278 fixed the **output** half of the remote file boundary: files a
tool *produces* travel to the client as `ArtifactHandle`s. But seven
tools still break when the tool-server runs on a different machine than
the agent, in one (or both) of two ways:

- **Output-side leftovers** — `native-profiler-analyze` and
`screenshot-diff` still returned raw host paths (and embedded them in
report/summary prose), so the agent can't open them remotely.
- **Input-side gap** — nothing moved a file the *agent* owns onto the
tool-server. Any tool taking a client-local path as a parameter
(`screenshot-diff` baselines, the three flow tools' `project_root`,
`react-profiler-component-source`, `gather-workspace-data`) silently
read/wrote the wrong machine's disk. Flows were fully broken in remote
mode: the YAML landed on the server's disk under the agent's path.

## What

### Output side (apply the existing pattern)
- **native-profiler-analyze** — `reportFile` is now an `ArtifactHandle`
(mirrors `react-profiler-analyze`'s `fileArtifact()`); the inline report
now says *"use the Read tool on the `reportFile` path in this result"*
instead of embedding the server path.
- **screenshot-diff** — `diffPath` / `contextDiffPath` are now
`ArtifactHandle`s; the MCP adapter renders the context-diff image from
materialized bytes (legacy string-path results from older servers still
render co-located). The summary references the result fields instead of
embedding host paths.

### Input side (new, symmetric mechanism)
A declarative **file-input boundary**, mirroring the artifact gate in
the opposite direction:

- A tool declares `fileInputs: [{ target, path: "${param}…", kind }]` on
its `ToolDefinition`; the declaration ships to the client via `GET
/tools`.
- Before sending a call, the client (`@argent/tools-client`, shared by
CLI and MCP) interpolates each template from the args and replaces the
target arg with a wrapper: `{ __argentFileInput, path, size, mtimeMs,
content? }`. File bytes are base64-inlined **only when routed to an
external tool-server** (`argent link` / `ARGENT_TOOLS_URL`) — unlinked
local calls never pay for encoding.
- The server resolves wrappers **before zod validation**, so tools
always execute against a plain local path:
- path matches on the server's own filesystem (existence for dirs,
size+mtime for files) → used **in place**, zero copies — the exact dual
of the artifact materializer's co-located gate;
- otherwise `kind: "file"` content is materialized into a temp file
(with size-integrity check, 32 MB cap);
- `kind: "directory"` (trees that can't ride in a call) → 422 with
actionable remote-mode guidance;
- `kind: "probe"` → never fails; the tool learns host-presence via
`ctx.fileInputs` and adapts.
- The reverse channel: a tool whose output belongs in the **agent's
project** returns a `__argentClientFile` directive (path + content); the
client writes it and rewrites the directive to the path. Writes are
constrained to `**/.argent/flows/<safe-name>.yaml` so a tool-server
can't direct writes anywhere else on the client machine.

### Tool migrations
| Tool | Boundary use |
|---|---|
| `flow-start-recording` | `probe` on `project_root`: present → host
persistence exactly as before; absent (remote) → recording kept in
server memory, YAML returned as a client-write directive |
| `flow-add-step` / `flow-add-echo` / `flow-finish-recording` | follow
the session's persist mode; every mutation returns `savedTo` (path
locally, directive remotely) so the agent-side file is always current |
| `flow-execute` / `flow-read-prerequisite` | `file` input derived from
`${project_root}/.argent/flows/${name}.yaml` into an internal
`flow_file` param — read in place locally, uploaded when remote |
| `screenshot-diff` | `baselinePath`/`currentPath` as `file` inputs;
`outputDir` now **optional** (temp default) and probe-ignored when
absent on the host |
| `react-profiler-component-source`, `gather-workspace-data` |
`directory` gate — unchanged locally; clear error remotely instead of a
silent empty AST index / all-nulls snapshot |

### Version skew
Both halves degrade to today's behavior: an old client sends plain
strings (the resolver passes them through, tools take their legacy
paths), and an old server doesn't advertise `fileInputs` (the new client
never wraps). Wrappers on undeclared params fail the tool's own schema
validation, so nothing can be smuggled through free-form args.

## Tests

**Unit** (all green: registry 68, tool-server 992, tools-client 107,
argent-mcp 66, argent-cli 117):
- `tool-server/test/file-inputs.test.ts` — in-place gate, stat-mismatch
→ upload, truncated-upload rejection, directory gate, probe semantics,
undeclared-target smuggling.
- `argent-tools-client/test/file-inputs.test.ts` — wrapping (stat-only
vs inlined content), multi-param template derivation, explicit-override
respect, directive writes incl. path-validation refusals (relative,
traversal, outside `.argent/flows`, bad charset/extension).
- `tool-server/test/flows/flow-remote-recording.test.ts` — remote
recording stays in memory + returns directives, nothing written on the
host, replay/read from a boundary-resolved `flow_file`.
- Updated existing screenshot-diff / native-profiler tests to the
artifact-handle contract.

**Local / co-located (unlink scenario)** — isolated tool-server + CLI on
an iPhone 17 Pro simulator: full flow
record→finish→read-prerequisite→execute loop (YAML written via the same
path as before, in-place gate hit, no byte copies); screenshot-diff with
and without `outputDir`; wire-level checks of the unlinked client shape
(wrapper without content → in-place read) and the legacy plain-string
shape; missing-file → 422 with a precise message.

**Remote (Lima Linux VM as the agent machine, tool-server on the host
Mac, `--host` bind + bearer auth)** — via both the CLI and the real MCP
stdio server:
- flow recording from the VM: YAML lands **in the VM's project**,
`savedTo` rewritten to the VM path, *no* agent-path directories created
on the host; replay from the VM uploads the YAML and drives the host
simulator.
- `screenshot` materializes into the VM temp cache; `screenshot-diff`
round-trips both directions (VM baseline uploaded, live capture on host,
diff artifacts downloaded back; MCP returns `image,text` content with
the context diff inlined).
- `gather-workspace-data` / `react-profiler-component-source` with
VM-only paths → the new actionable 422.

## Notes
- `native-profiler-analyze` was not exercised live (xctrace export is
unusable on this machine's iOS 26.4 simulators — known, unrelated); its
change is the same one-line `fileArtifact` pattern
`react-profiler-analyze` already ships, covered by unit tests.
- `react-profiler-analyze`'s own `project_root` (AST resolve stage) is
left as-is deliberately: it degrades gracefully (report still renders)
and gating it would break currently-working remote analysis. Tracked as
follow-up alongside nested invocations (`run-sequence`/`flow-execute`
steps bypass the HTTP boundary by design).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-06-12 17:14:04 +02:00
filip131311 fa86905fbd chore: bump versions to 0.11.0 (#322)
Bumps the package suite from `0.10.0` to **`0.11.0`**.

## Changed
- All release packages `0.10.0 → 0.11.0`: `@swmansion/argent`,
`@argent/cli`, `@argent/installer`, `@argent/mcp`,
`@argent/tools-client`, `@argent/native-devtools-android`,
`@argent/native-devtools-ios`, `@argent/registry`, `@argent/skills`,
`@argent/tool-server`, `@argent/update-core`.
- `@argent/configuration-core` `0.9.0 → 0.11.0` — was lagging a release
behind (extracted in #295); aligned to the suite. (It's
`private`/unpublished, so this is internal-only.)
- Regenerated `package-lock.json` to reflect the new workspace versions.

## Not changed
- `@argent/ui` stays at `0.0.1` (separate pre-release track).
- The private workspace root has no version.
- Internal deps use `file:../` specifiers, so no dependency ranges
needed updating.

No source/runtime changes — `argent --version` reads from `package.json`
at runtime.
2026-06-10 17:25:27 +02:00
filip131311 20cb452fd4 feat(remote): artifact transport for files produced by a remote tool-server (#278)
## Why

Files produced on the **tool-server host** (screenshots, profiler
exports) were surfaced to the agent as host paths / `127.0.0.1` media
URLs, which the MCP client (`argent-mcp`) then fetched a *second* time
to get the bytes. When the tool-server runs **remotely**, that localhost
URL resolves to the client's own machine — nothing there — so
screenshots fail with `(Screenshot unavailable: no valid PNG …)`.
Profiler exports have the same shape: paths that only exist on the
remote host.

## What

A reusable **artifact boundary layer** so any file-producing tool works
regardless of where the tool-server runs:

**Foundation**
- **tool-server** (`src/artifacts.ts`, `http.ts`): in-process artifact
registry + `GET /artifacts/:id` streaming route.
`registerArtifact(hostPath)` returns a wire handle (`{ __argentArtifact,
id, filename, mimeType, size }`) instead of a raw path.
- **argent-mcp** (`src/artifacts.ts`, `content.ts`, `mcp-server.ts`): a
client materializer deep-walks tool results for handles, downloads each
over the remote-aware `TOOLS_URL`, writes it into a **structured
cache**, and rewrites the result to the **local** path (+ inline image
rendering).

**Cache layout** (override root with `ARGENT_ARTIFACTS_DIR`):
```
~/.argent/artifacts/<project>/<session>/<device>/<filename>
```
- `project` = `basename(cwd)` + short hash of the full path (readable +
collision-safe)
- `session` = minted once per argent-mcp process (groups a run, easy GC)
- `device` = udid/serial when device-scoped, omitted otherwise

**Consumers wired**
- **screenshot** → returns an image artifact; rendered inline from
downloaded bytes with **no second `127.0.0.1` fetch**. Fixes
`auto-screenshot` and `flow-execute` screenshots via the same path.
- **native-profiler-stop** → `exportedFiles` (XML) become downloadable
artifacts materialized client-side.

## Deferred (follow-ups)
- **`.trace` bundle** — it's an Instruments bundle (a directory);
streaming needs archiving (no archiver dep yet). `native-profiler-stop`
now returns a `traceFileNote` explaining it stays on the profiling host.
- **Project source on remote** — `react-profiler-component-source` /
`react-profiler-analyze` read the user's source tree on the tool-server
host. The profiler analysis chains already work remotely
(server-internal); tracked separately.

## Compatibility
Legacy `{ url, path }` screenshot rendering is retained as a fallback
when no artifact context is present (older tool-servers / unit tests).

## Tests
- tool-server: artifact registry (infer/override), `GET /artifacts/:id`
(200/404/410), updated `stop-recovery` assertions.
- argent-mcp: materializer
(download/rewrite/nested/failure/device-scoping), artifact-aware
`toMcpContent`.
- argent-mcp typecheck + full suite green; tool-server artifact/profiler
tests green. (Note: a full local tool-server typecheck trips a
pre-existing zod-v4-vs-v3 mismatch in `run-sequence` that's unrelated to
this change and resolves on this branch's own lockfile.)

## Notes
Routes around the `argent-private` native simulator-server binary (which
emits the `127.0.0.1` media URL) rather than changing it — no submodule
change.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: pFornagiel <pawel.fornagiel2@gmail.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: Paweł Fornagiel <85541429+pFornagiel@users.noreply.github.com>
2026-06-10 12:04:30 +02:00
filip131311 d87fbcc973 chore: bump version to 0.10.0 (#301)
Bumps the version from `0.9.0` to `0.10.0` across all versioned packages
in the workspace.

## Packages bumped (0.9.0 → 0.10.0)
- `@swmansion/argent`
- `@argent/cli`
- `@argent/installer`
- `@argent/mcp`
- `@argent/tools-client`
- `@argent/native-devtools-android`
- `@argent/native-devtools-ios`
- `@argent/registry`
- `@argent/skills`
- `@argent/tool-server`
- `@argent/update-core`

`package-lock.json` updated to match.

## Left unchanged
- `@argent/ui` — on a separate version track (`0.0.1`)
- `packages/argent-private` — has no `version` field

Internal dependencies use `file:` references, so no dependency
specifiers required updating.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2026-06-03 17:48:42 +02:00
filip131311 8efd7c6346 feat: add argent server start for long-lived tool-server (#182)
## Human summary: 

This adds `argent server start` command to enable us to spawn tools
server in arbitrary location. we want this to enable using remote
tools-server via our mcp not only local ones.

## Summary

- Adds `argent server start` to spawn a tool-server that does **not**
auto-shutdown on idle, with configurable bind port and host — the
missing piece for running tool-server on a remote/dedicated host.
- Foreground by default so process supervisors (systemd, Docker,
supervisord) own the lifecycle; `--detach` keeps the existing
daemon-spawn behavior. Refuses to clobber a healthy running server
unless `--force`.
- Phase 2 (consumer-side `--tools-url` flag pointing the MCP / CLI at a
remote tool-server) is now a one-flag change away — `tools-client.ts`
and `mcp-server.ts` already honor `ARGENT_TOOLS_URL`. Out of scope for
this PR.

## What changed

- **`tool-server`** — read `HOST` env var (default `127.0.0.1` so the
existing auto-spawn path is unaffected), bind to it, attach an `error`
listener on `app.listen` so `EADDRINUSE` / `EACCES` exit cleanly instead
of routing through `uncaughtException`.
- **`argent-tools-client`** — broaden the readiness regex to match any
host; extend `buildToolsServerEnv` with optional `{ host,
idleTimeoutMinutes }`; record `host` in `~/.argent/tool-server.json`
(backward-compat: missing field defaults to `127.0.0.1`); export
`spawnToolsServer`, `findFreePort`, `isToolsServerHealthy`,
`isToolsServerProcessAlive`, the state read/write/clear helpers, and
`formatToolsServerUrl` so the CLI can reuse them.
- **`argent-cli`** — new `server start [flags]`: `--port`/`-p` (default
3001, `0` = pick free), `--host` (default `127.0.0.1`), `--idle-timeout`
(default `0` = never), `--detach`/`-d`, `--force`, `--help`. Warns when
host is non-loopback. Foreground mode forwards `SIGINT`/`SIGTERM` to the
child and writes the state file so local `argent run` / `argent mcp` can
attach.
- **`argent` dispatcher** — pass `BUNDLED_RUNTIME_PATHS` to `server()`
so the new subcommand can find the bundled tool-server.

## Test plan

- [x] `argent server --help` lists the new `start` subcommand
- [x] `argent server start --help` shows full flag reference
- [x] `argent server start --detach --port 0` → status alive/healthy,
`/tools` reachable, state file populated with `host` field
- [x] Second `argent server start --detach` refuses (`exit 1`) with
helpful message
- [x] `argent server start --detach --force` kills the old PID and
starts a new one
- [x] Foreground `argent server start --port 0` runs with inherited
stdio; child PID is the actual tool-server (not the wrapper); `argent
server stop` from a sibling shell tears down both
- [x] Existing auto-spawn path (`argent tools`) still works — verifies
the regex broadening + default-host change is backward-compatible
- [x] `tool-server` (444 tests) + `argent-tools-client` (1) + `argent`
dispatcher (1) suites pass
- [ ] Manual smoke on `--host 0.0.0.0` — could not exercise locally
(sandbox declined the public-interface bind); the warning + bind path is
identical to loopback besides the listen address

---------

Co-authored-by: pFornagiel <pawel.fornagiel2@gmail.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: Paweł Fornagiel <85541429+pFornagiel@users.noreply.github.com>
2026-06-03 17:44:15 +02:00
Ignacy Łątka 74c2065ca0 fix: multiple minor bugs (#194)
## Human Summary

This PR aims to fix bugs that are benign for now, could become serious
in the future:

- multiple CSRF via CORS misconfiguration on `tool-server`
- multiple SSRF that were possible due to the CSRF
- path traversal via flow creator
- multiple shell injections
- lack of auth on `MCP/CLI <-> tool-server` path (bug details discussed
in person). After the other fixes this is somewhat redundant, but
doesn't hurt and protects against theoretical access attempts from
within other processes running on the user's device.

## AI Summary

<details>
Bundles six independent fixes that together close a CSRF cluster on the
local tool-server. The vulnerabilities chain because the HTTP API has no
auth and serves wildcard CORS, so any web origin (Firefox
unconditionally; Chrome via PNA-disabled or loopback-served) can drive
the tool-server from a cross-origin tab. Each individual fix lives on
its own branch (linked below) and stands alone — this PR exists so the
cluster can be reviewed and shipped as one piece.

### Fixes

1. **`fix(http): require auth token + drop wildcard CORS`** —
per-process 32-byte hex Bearer token, generated by the launcher,
persisted in `~/.argent/tool-server.json` with mode `0600`, required on
every route except `/preview`. Constant-time compare. Wildcard CORS
dropped. Threaded through MCP server + CLI + tools-client.
Backwards-compat: when `ARGENT_AUTH_TOKEN` is unset (e.g. `npm run
dev`), the server logs a one-shot warning and skips the gate.
2. **`fix(http): reject non-loopback Host headers`** — middleware
(before the auth gate) returns 403 for any request whose Host is not
`127.0.0.1`, `localhost`, or `::1`. Closes DNS-rebinding bypass.
3. **`fix(ios-profiler-start): prevent shell injection via device_id`**
— `detectRunningApp(udid)` no longer interpolates `params.device_id`
into `execSync` template strings. Both `xcrun simctl spawn ${udid} …`
and the piped `xcrun simctl listapps ${udid} | plutil …` are rewritten
to `execFileSync` array form, with the pipe to `plutil` happening in JS
(input option) rather than the shell.
4. **`fix(flows): validate name to close path-traversal in
getFlowPath`** — `getFlowPath()` requires `^[A-Za-z0-9_\\-]+$` and
verifies `path.relative(getFlowsDir(), filePath)` does not start with
`..`. Centralizing the check in `getFlowPath` covers every caller
(start-recording, add-step, insert-echo, finish-recording, run,
read-prerequisite) without per-tool churn.
5. **`fix(source-resolver): reject paths outside projectRoot +
non-source extensions`** — `readSourceFragment()` previously accepted
any `file` field on the SourceLocation it was given (the field traces
back to a React fiber's `_debugSource.fileName`, which is
attacker-controllable). Now: containment check via
`path.relative(projectRoot, abs)` plus an extension allowlist
(`.js/.jsx/.ts/.tsx/.mjs/.cjs/.json`).
6. **`fix(source-maps): reject non-loopback URLs (close SSRF)`** —
`doRegister()` validates `sourceMapURL` before fetching: only
`http://`/`https://` URLs whose hostname is loopback. Closes a
blind-SSRF probe of the host network (cloud metadata at
`169.254.169.254`, intranet pages, etc.).

### Individual branches

- `fix/auth-token-drop-cors`
- `fix/host-header-validation`
- `fix/profiler-command-injection`
- `fix/flow-name-validation`
- `fix/source-resolver-traversal`
- `fix/source-map-ssrf`

If you want to land them as separate PRs, the integration branch can be
dropped after each lands.

## Test plan

Unit tests (already in CI):

- [x] `npm run build` passes (workspace tsc clean)
- [x] `npm run test -w @argent/tool-server` — **510 passed (was 473 on
`main`)**, +37 new tests across:
- `test/http-auth.test.ts` — 10 tests (auth, CORS removal, dev-mode
escape hatch)
- `test/http-host-validation.test.ts` — 7 tests (loopback allow,
DNS-rebinding reject)
- `test/flows/flow-utils.test.ts` — +5 tests (path-traversal name
rejection)
- `test/metro/source-resolver.test.ts` — +5 tests (containment +
extension allowlist)
- `test/metro/source-maps-ssrf.test.ts` — 10 tests (loopback-only allow)

Live end-to-end (run locally against this branch's bundled artifacts):

- [x] Launcher spawns tool-server; `~/.argent/tool-server.json` contains
a 64-char hex token and is mode `0600`
- [x] `GET /tools` without `Authorization` → **401**
- [x] `GET /tools` with wrong token → **401**
- [x] `GET /tools` with correct token → **200** (61 tools)
- [x] Request with `Host: attacker.example` → **403**
- [x] No `Access-Control-Allow-Origin` header on responses
- [x] Cross-origin command-injection probe via `device_id=\"x;touch
/tmp/marker;#\"` does NOT create the marker file (Vuln 4 closed)
- [x] `flow-start-recording` with `name=\"../../../tmp/escape\"`
rejected with `Invalid flow name`
- [x] `flow-start-recording` with a valid name still works
- [x] `tools-client.fetchTools()` and
`tools-client.callTool('list-simulators')` succeed via the auth-token
threading (the path the MCP server uses internally)
- [x] MCP-stdio entry point (`startMcpServer`) end-to-end: `initialize`
→ `tools/list` (61 tools) → `tools/call('list-simulators')` returns
content. Confirms the launcher → token → fetch → tool-server flow is
intact for the production `argent mcp` path.

## Notes for reviewers

- `/preview` is intentionally exempt from the auth gate (it was not
gated before; tightening it deserves a separate PR).
- The `ARGENT_AUTH_TOKEN`-unset escape hatch keeps `npm run dev`
workable; production `argent mcp` always passes a token via env, so the
warning never fires there.
- The auth bundle changes the public return shape of `ensureToolsServer`
from `Promise<string>` to `Promise<{ url, token }>`. The two in-tree
callers (`argent-mcp/mcp-server.ts`,
`argent-tools-client/tools-client.ts`) were updated; external consumers
using `ARGENT_TOOLS_URL` should also set `ARGENT_AUTH_TOKEN` to match
the server.

</details>
2026-06-03 15:57:42 +02:00
filip131311 d670751db3 chore: bump all package versions to 0.9.0 (#281)
## Summary
- Bump all 11 workspace packages from `0.8.1` → `0.9.0` (`package.json`
+ `package-lock.json`)
- Includes `@argent/update-core`, which was added since the last bump
(#267)
- `@argent/ui` left at `0.0.1` (independent versioning)

## Notes
- Ran `npm install` to update `package-lock.json` — resolved cleanly
- Ran `npm run format` (prettier) — no files needed reformatting
- No `lint` script / eslint config exists in this repo, so there was
nothing to lint

## Test plan
- [ ] Verify `npm install` resolves cleanly
- [ ] Verify `argent --version` reports `0.9.0`
2026-06-01 17:23:13 +02:00
filip131311 05fb58552d chore: bump all package versions to 0.8.1 (#267)
## Summary
- Bump all 10 workspace packages to version `0.8.1` (`package.json` +
`package-lock.json`)
- Includes `native-devtools-android` (`0.7.1` → `0.8.1`), all others
from `0.8.0` → `0.8.1`
- `ui` package left at `0.0.1` (independent versioning)

## Test plan
- [ ] Verify `npm install` resolves cleanly
- [ ] Verify `argent --version` reports `0.8.1`
2026-05-27 16:37:17 +02:00
filip131311 09a18a65ef bump to v0.8.0 (#245) 2026-05-21 17:46:28 +02:00
Ignacy Łątka 2180dd8e6a ci: typecheck test files in CI (#202)
We've had lots of type errors in our tests, scripts and other non-core
code despite all CI being green. This PR fixes that.

This PR also removes some redundant features like `@ts-check` inline
directives which have been replaced by proper ts config files.

<details>

## AI Summary

Audit of every CI workflow against every package's `package.json` and
`tsconfig` surfaced several gaps where CI did partial coverage. This PR
closes them and fixes every pre-existing type error the new gates
surfaced.

## Gaps closed

| # | Gap | Fix |
|---|---|---|
| 1 | `tsc --build` only covers `src/**`; test files (`test/**`,
`tests/**`) never typecheck. Vitest transforms tests with esbuild —
strips types instead of checking them. | New `tsconfig.test.json` per
test-bearing package (`composite: false`, `noEmit`, `rootDir: "."`,
includes `src/**` + `test/**` (or `tests/**`) + `vitest.config.ts`). New
`typecheck:tests` npm script per package. New CI step `npm run
typecheck:tests --workspaces --if-present`. |
| 2 | Build/publish scripts (`scripts/*.{cjs,mjs}`,
`packages/argent/scripts/*.cjs`, `packages/skills/scripts/install.js`) —
JS, never typechecked. `sync-readme.cjs` runs in `prepack`;
`postinstall.cjs` runs on every install; `bundle-tools.cjs` is
build-critical. A typo only surfaces at runtime. | Root
`tsconfig.scripts.json` with `allowJs` + `checkJs` + `noImplicitReturns`
covers all script paths. Root `typecheck:scripts` npm script. New CI
step. Per-file `// @ts-check` comments removed — tsconfig drives
inclusion. |
| 3 | `argent-cli` had no `typecheck:tests` script. When tests get added
there they'd silently bypass the gate. | Added `tsconfig.test.json` +
`typecheck:tests` script. `--if-present` would have skipped it anyway,
so this is future-proofing. |
| 4 | Local `prettier --check .` walked into the `argent-private`
submodule and reported violations there. CI didn't see this because
`actions/checkout@v4` doesn't init submodules — so local `prettier
--check` did not match CI behavior. | New `.prettierignore` excludes the
submodule plus `dist/`, `node_modules/`, lock files, `tsbuildinfo`.
No-op in CI; aligns local with remote. |

## Test type errors fixed

These were already present. The `npm test` passed because esbuild
stripped the offending types:

- **argent-installer**: widen `scope` literal type for
dead-code-elimination guard; non-null `addAllowlist!`/`removeAllowlist!`
(optional methods on adapter).
- **argent-mcp**: add `.js` extensions for NodeNext module resolution.
- **registry**: broaden `StaticBlueprintResult` blueprint api from `{
id; deps? }` to `Record<string, unknown>` so existing tests can pass
arbitrary api shapes.
- **tool-server**:
- non-null `zodSchema!` on tool defs that always carry one
(`launchAppTool`, `restartAppTool`, etc.)
- cast sentinel `"ignored"` strings to `DeviceInfo` in factory-rejection
tests
- match the 4-arg `dispatchByPlatform<IosServices, AndroidServices,
Params, Result>` signature (tests still passed the old 3-arg shape)
- `assertFlowRunResult` type guard for the `FlowRunResult |
FlowPrerequisiteNotice` union before `.steps` access
- swap broken `typeof import("supertest").default` for static `import
supertest` (supertest is `export = supertest`)
- fill in the 4 fields missing from a `NativeProfilerSessionApi` mock
(`xctraceProcess`, `recordingTimedOut`, `recordingExitedUnexpectedly`,
`lastExitInfo`)
- cast a vitest `Mock` to a plain function at one direct call site where
the `Procedure | Constructable` union loses callability
- fix `update-checker.test.ts` reading `../../package.json` (off-by-one
— went past tool-server; was relying on a Vite resolver quirk)
- replace one cross-package `../../../registry/src/index` import with
`@argent/registry` so registry's private `services` field isn't compared
between src and dist declarations

All fixes are type-level. Full `npm test --workspaces --if-present`
still reports **991 passed across 87 test files**.

</details>
2026-05-20 17:03:34 +02:00
Ignacy Łątka fbe7b087e7 chore(deps): batch-apply Dependabot updates (#214–#227) (#228)
This PR consolidates every open Dependabot PR that requires **no
source-code changes** into a single branch with one regenerated,
coherent lockfile.

| PR(s) | Update | Status |
|---|---|---|
| #214 | GitHub Actions group — checkout@v6, setup-node@v6,
setup-python@v6, github-script@v9, upload-artifact@v7 | ✅ Applied |
| #215 | prettier 3.8.3, @clack/prompts 1.4.0, semver 7.8.0, yaml 2.9.0,
@modelcontextprotocol/sdk 1.29.0, esbuild 0.28.0 | ✅ Applied |
| #216 / #221 / #224 / #227 | typescript 6.0.3 | ✅ Applied |
| #217 / #222 / #223 | @types/node 25.9.0 | ✅ Applied |
| #220 / #226 | vitest 4.1.6 | ✅ Applied |
| #218 / #225 | zod 4 | ⏸️ Excluded — requires code migration |
| #219 | express 5 | ⏸️ Excluded — requires code migration |
| #215 (partial) | tree-sitter 0.25.0 | ⏸️ Excluded — see below |

**tree-sitter 0.25.0** ships no prebuilt binaries and its `binding.gyp`
hard-codes `-std=c++17`, which fails to compile against Node 20+/24
C++20 `cppgc` headers — a hard `npm install` blocker (would also break
CI). Kept at `^0.21.1`; all other #215 updates applied.

zod 4 and express 5 are excluded here because they require real source
changes; tracked separately.

Verified: `tsc --build` clean, esbuild bundles, **1010 unit tests
pass**, **zero source files modified**.
2026-05-19 12:57:07 +02:00
pFornagiel 12f6ff1115 chore: bump to 0.7.1 2026-05-15 16:55:57 +02:00
Ignacy Łątka bea43f555a feat: per-step delayMs on flow steps (#198)
- Tool steps in a flow YAML may carry an optional `delayMs` that sleeps
that long *before* the step runs; `flow-add-step` exposes the same
field.
- New `longRunning?: boolean` field on `ToolDefinition`; when set, the
MCP fetch wrapper bypasses its 30s per-request timeout (`fetchTimeoutMs:
null`) so long invocations aren't aborted. `run-sequence` and
`flow-execute` opt in.
2026-05-13 18:32:42 +02:00
pFornagiel 60ae7cb767 chore: bump versions to 0.7.0 2026-05-11 16:44:03 +02:00
filip131311 626c3274e2 chore: bump package versions to 0.6.1 (#193)
## Summary
- Bumps all 9 published packages from `0.6.0` → `0.6.1`.
- Affected: `@swmansion/argent`, `@argent/cli`, `@argent/installer`,
`@argent/mcp`, `@argent/tools-client`, `@argent/native-devtools-ios`,
`@argent/registry`, `@argent/skills`, `@argent/tool-server`.
- `packages/ui` (`0.0.1`) is on a separate version track and left as-is.
- Lockfile updated via `npm install`; `npm run format` was a no-op.

## Test plan
- [ ] CI green
- [ ] Run the stable publish workflow against this commit and confirm
publish succeeds
2026-05-08 12:48:23 +02:00
filip131311 bb3f484d04 chore: bump package versions to 0.6.0 (#178)
## Summary
- Bumps all 9 published packages from `0.6.0-next.2` → `0.6.0` (stable)
in preparation for the `0.6.0` release.
- Affected: `@swmansion/argent`, `@argent/cli`, `@argent/installer`,
`@argent/mcp`, `@argent/tools-client`, `@argent/native-devtools-ios`,
`@argent/registry`, `@argent/skills`, `@argent/tool-server`.
`packages/ui` (`0.0.1`) is on a separate version track and left as-is.
- Lockfile updated via `npm install`; `npm run format` was a no-op.

## Test plan
- [ ] CI green
- [ ] Run the stable publish workflow against this commit and confirm
publish succeeds
2026-04-30 16:02:33 +02:00
filip131311 f004c08a19 chore: bump package versions to 0.6.0-next.2 (#177)
## Summary
- Bumps all 9 published packages from `0.6.0-next.1` → `0.6.0-next.2` in
preparation for the next prerelease publish.
- Affected: `@swmansion/argent`, `@argent/cli`, `@argent/installer`,
`@argent/mcp`, `@argent/tools-client`, `@argent/native-devtools-ios`,
`@argent/registry`, `@argent/skills`, `@argent/tool-server`.
`packages/ui` (`0.0.1`) is on a separate version track and left as-is.
- Lockfile updated via `npm install`; `npm run format` was a no-op.

## Test plan
- [ ] CI green
- [ ] Run `Publish prerelease to npm (next)` workflow against this
commit and confirm publish succeeds
2026-04-30 15:51:32 +02:00
filip131311 1f1f55f72e chore: bump package versions to 0.6.0-next.1 (#174)
## Summary
- Bumps all 9 published packages from `0.6.0-next.0` → `0.6.0-next.1` in
preparation for the next prerelease publish (the `0.6.0-next.0` train
was blocked by the build-config and update-checker fixes in #172 and
#173).
- Affected: `@swmansion/argent`, `@argent/cli`, `@argent/installer`,
`@argent/mcp`, `@argent/tools-client`, `@argent/native-devtools-ios`,
`@argent/registry`, `@argent/skills`, `@argent/tool-server`.
`packages/ui` (`0.0.1`) is on a separate version track and left as-is,
matching #171.
- Lockfile updated via `npm install` — only the 9 workspace `version`
entries changed.

## Test plan
- [ ] CI green
- [ ] Run `Publish prerelease to npm (next)` workflow against this
commit (after #172 and #173 land) and confirm publish succeeds
2026-04-30 13:08:22 +02:00
filip131311 be31093f6c chore: bump package versions to 0.6.0-next.0 (#171)
## Summary
- Bump all published packages to `0.6.0-next.0` in preparation for the
next prerelease train.
- Affected: `@swmansion/argent`, `@argent/cli`, `@argent/installer`,
`@argent/mcp`, `@argent/tools-client`, `@argent/native-devtools-ios`,
`@argent/registry`, `@argent/skills`, `@argent/tool-server`.
`@argent/native-devtools-ios` was at `0.5.1`; aligned with the rest.
`packages/ui` (`0.0.1`) is on a separate version track and left as-is.

## Test plan
- [ ] CI green
- [ ] `npm next` prerelease publish workflow picks up the new version
2026-04-30 12:45:57 +02:00
filip131311 6355620c4d refactor(mcp): split mcp package into argent, argent-cli, argent-installer, argent-mcp, argent-tools-client (#162)
## Summary

- Splits the monolithic `packages/mcp` package into five focused
packages:
- `argent` — the user-facing CLI entry (`argent` binary,
init/uninstall/update flows, bundled-paths, scripts).
- `argent-cli` — CLI runner internals (flag-parser, run, server, tools).
- `argent-installer` — `init`/`update`/`uninstall` flows, MCP config
adapters, skills installer.
- `argent-mcp` — MCP server itself (mcp-server, tool-mapping,
auto-screenshot, content adapters).
  - `argent-tools-client` — HTTP client + launcher for the tool-server.
- Adds Gemini agent definitions, VSCode MCP config, and per-package
`tsconfig.json` / `vitest.config.ts`.
- Updates root `tsconfig.json`, `scripts/dev.cjs`, CI workflows, and
`package.json` to wire the new packages together.

## Test plan

- [ ] `npm install` resolves the new workspaces cleanly
- [ ] `npm run build` succeeds across all packages
- [ ] `npm test` passes (renamed test files keep their assertions)
- [ ] `npx argent init` still produces the expected MCP config in a
sample project
- [ ] `npx argent update` / `argent uninstall` exercise the installer
package
- [ ] CI workflows (`publish.yml`, `unit-tests.yml`) run on this branch
2026-04-28 08:36:24 +02:00