Commit Graph

106 Commits

Author SHA1 Message Date
Hubert Gancarczyk 4a11317706 chore: bump to 0.20.0 (#765)
Version-only bump of the workspace to 0.20.0, same shape as #741 (the
0.19.0 bump).

## What changed

- All 16 packages under `packages/*` → `0.20.0`
- `package-lock.json` — regenerated with `npm install
--package-lock-only --ignore-scripts` on Node 24, matching what the
Lockfile job runs. Version-only: 16 insertions, 16 deletions, nothing
else.
- `server.json` — the MCP registry manifest, in **both** places it
carries a version: the top-level `version` and `packages[0].version`. It
lives outside `packages/*`, so a workspace sweep leaves it behind and
the registry then rejects the publish because `packages[].version` names
a version npm has never seen.

## Verification

- `npm run check:versions` → "All workspace packages and server.json are
at 0.20.0."
- `npx prettier --check` clean on every touched file
- No `"0.19.0"` string left anywhere in the repo

No code changes.
2026-08-10 19:36:14 +02:00
Ignacy Łątka c876fe8dbc ci: add knip anti-dead-code gate (#572)
## What

Adds a CI gate for dead code, using [Knip](https://knip.dev).

We didn't have one. ESLint's `no-unused-vars` only sees unused locals
inside a module, so whole files nobody imports, exports nobody uses, and
dependencies declared but never required all slipped through. Knip
covers that gap in one tool (roughly `ts-prune` + `depcheck` +
`unimported` combined).

The new `.github/workflows/knip.yml` workflow ("Dead Code", job `Knip`)
runs `npm run knip` on push and PR to `main`. `CONTRIBUTING.md`'s
pull-request checklist gains it as a step, with the reason it has to run
against a tree with no build output. Knip reads source directly, mapping
each package's `dist` entry back to `src`, so the job needs no workspace
build. It installs with `--ignore-scripts`: an analysis that only reads
`package.json` and source gains nothing from the seven packages here
that carry a native install script, six of which reach this runner
(`fsevents` is darwin-only). Five try a prebuilt binary before
compiling, and `dtrace-provider` always runs node-gyp but ends in `||
node suppress-error.js` — and off mac/solaris/freebsd its `binding.gyp`
picks a stub target with no sources, so on ubuntu-latest node-gyp builds
nothing for it and `node-pty` is the only package that reaches a
compiler at all — an `optionalDependency`, whose failure npm ignores.
The exposure is a bump that leaves a package with no prebuild for the
runner, which is exactly what `dependabot.yml` pins `tree-sitter`
against (0.25.0 ships no Node 24 prebuild and its compile breaks `npm
ci`).

While adding it I checked whether any other installed checker was
missing a gate. None were: ESLint (`lint.yml`), Prettier (`format.yml`),
`tsc` build + `typecheck:tests` + `typecheck:scripts` + vitest
(`unit-tests.yml`), the workspace-version / focused-test / merge-marker
checks (`repo-hygiene.yml`) and lockfile sync (`lockfile.yml`) are all
already enforced. Dead code was the only gap.

## How it's enforced

`npm run knip` is two invocations:

```
knip --exclude exports,types && knip --include exports,types,classMembers,nsExports,nsTypes --max-issues 206
```

Every issue type knip reports is an error — that is its default
severity, so `knip.jsonc` sets no `rules` block at all. The pre-existing
backlog is bounded by the `--max-issues` ceiling on the second pass
instead of being hidden behind a non-failing severity: it cannot grow
unnoticed, and the number comes down as follow-up passes pay it off.
When a change legitimately adds an export nothing imports yet, the
number moves up in the same commit — that bump is what review should
see, rather than the contributor deleting someone else's backlog to make
room. Two PRs making that identical one-line edit merge without
conflicting, so `main` lands one short of the truth and its
push-triggered run is what catches it. Splitting the passes also keeps a
real failure out from under the backlog — the enforced classes report on
their own, and the second pass only runs once the first is clean.

The second pass names `classMembers`, `nsExports` and `nsTypes`
explicitly because they sit in knip's own `defaultExcludedIssueTypes`:
`--exclude exports,types` never covered them and `--include
exports,types` did not ask for them, so a new unused class member cost
nothing and moving an export behind opaque namespace use took it out of
the ceiling's scope entirely. `classMembers` has 12 findings on the
unbuilt tree the ceiling counts (9 on a built one); the other two have
none today. None of the three is gated at zero — all five parked classes
share the one ceiling, so a new `nsExports` finding passes if something
else in the backlog is retired in the same PR. Naming `classMembers`
also turns knip's `isSkipLibs` off (`create-options.js:122`), which
makes it search declaration files for external references and
**suppresses** 44 export/type findings the same run reports without it —
57/181 becomes 44/150. That, not the 12 findings it adds, is why the
ceiling is lower than the two counts alone would suggest;
`--include-libs` with no `classMembers` reproduces 44/150 exactly.

The ceiling counts what CI counts, against a tree with no build output.
Whether a workspace's `main`/`types` path resolves to an existing file
changes the result, and not by way of anything in that file — a
zero-byte `packages/registry/dist/index.js` moves the count just as a
real build does, while a zero-byte `dist/zzz.js` does nothing. A built
tree reports fewer issues — 43/147/9 = 199 built, against 44/150/12 =
206 unbuilt — so `npm run knip` passing after a local `tsc --build` is
not evidence the job will pass, which is how the Knip job first went red
on 199. The ceiling is 206 and it is exact: 206 passes, 205 fails.
Remove every `dist/` before trusting a number from it.

The ceiling is one total across those classes, so it bounds the backlog
rather than each class: paying off one parked export licences one new
one elsewhere in the same PR. Knip has no per-class ceiling.

`treatConfigHintsAsErrors` is on, so an exemption that stops suppressing
anything fails the job rather than printing a hint below the backlog.

## Config

`knip.jsonc` documents the false positives so the gate is both green and
meaningful.

- **`@swmansion/argent`** is a thin dispatcher whose real payload is an
esbuild bundle of the other workspaces
(`packages/argent/scripts/bundle-tools.cjs`). Knip can't follow imports
across that boundary, so packages consumed only by the bundled-in code
read as unused; 11 are exempted by name here, though not all for that
reason — the http3-quiche transport is pulled in by
`@fails-components/webtransport` itself at runtime, and no first-party
file imports it. They're exempted **by name**, not behind a catch-all:
`ignoreDependencies` also suppresses `unlisted`, and a pattern that
matches everything would take this workspace's own `src/` and `scripts/`
out of that check — where an import with no matching declaration is a
genuine bug rather than a bundler artifact. (It reaches `unresolved`
only nominally: knip matches the list against a package name parsed out
of the specifier, and an unresolvable relative path yields none, so even
a catch-all leaves those reported. `ignoreUnresolved` is the option that
does silence them.)
- **`tool-server`** reaches a few packages in ways static analysis can't
see. The exemptions cover two distinct classes, kept apart in the config
so each stated reason matches the finding it silences:
- *declared but loaded too dynamically to detect* (would be reported as
**unused dependencies**): `tree-sitter` / `tree-sitter-typescript` via
dynamic `_require`, and the http3-quiche transport as a runtime
companion of `@fails-components/webtransport`;
- *imported but deliberately absent from that `package.json`* (would be
reported as **unlisted dependencies**): `sharp` (kept out so the ~30 MB
native binary isn't a cost every consumer pays) and `electron` (an
`optionalDependency` of the published package at one call site, the
Electron main-process API at the other).
- Root `scripts/`: knip resolves those from `package.json` scripts
**and** from `run:` steps in `.github/workflows` via its github-actions
plugin — multi-line `run: |` blocks included. What the plugin doesn't
parse is a PowerShell body, so `scripts/ci/windows-chromium-e2e.mjs` —
invoked at `windows-e2e.yml:176`, in the `windows-latest` job where
PowerShell is the default shell — is the one entry declared by hand.

### Known limits

- `ignoreDependencies` exempts a package **name across its whole
workspace**, in both directions; knip has nothing narrower. While
`sharp` is listed for `tool-server`, an undeclared `import "sharp"`
anywhere in that workspace passes too. The list is held to packages that
genuinely cannot be declared, for exactly that reason.
- Knip analyzes JavaScript and TypeScript only (`DEFAULT_EXTENSIONS`),
so **only 11 of the 30 files under `scripts/` are in the gate at all**.
The other 19 — 15 `.sh` files, `e2e-full/lib/report.py`,
`ci/e2e-chromium-page.html`, and e2e-full's `README.md` and
`results/.gitignore` — are invisible to it however they are reached —
including the whole `scripts/e2e-full/` harness (13 files), invoked from
nothing outside its own directory, its only mention elsewhere being a
`.prettierignore` entry.
- Reachability's widest hole is the workspace entry. Whatever
`src/index.ts` re-exports is reached, so a single `export { x } from
"./dead-module";` takes that module out of `files` altogether — verified
against a module reported as an unused file one run earlier. The same
line applied to parked exports retires them from the count without
deleting anything: re-exporting `equalsCI`, `includesCI` and `Settled`
from `tool-server`'s entry takes 206 to 203 and frees three ceiling
slots. The ceiling bounds what knip can see, not what is dead.
- Test files are entry points, so a production module kept alive only by
its own test still passes `files`. Being reached by a test says nothing
about being reachable in production, which is what the check
approximates, and two modules sit in exactly that gap: `tools/paste` is
imported by two test files and registered by nothing, so it is
unreachable over MCP and HTTP, and `utils/ios-profiler/lifecycle.ts` is
a re-export barrel whose header keeps it "for source compatibility with
existing imports", of which its own test is now the only one — its four
production consumers all import `profiler-shared/lifecycle` directly.
That single test import is exactly what keeps `files` from reporting it:
repoint it and knip returns `Unused files (1)`. `knip --production` is
the lens that reports the class; the repo isn't clean under it — 13
unused files (those two, seven root scripts,
`packages/argent/scripts/{bundle-tools,observe,sync-readme}.cjs` and
`packages/registry/tests/helpers.ts`) with the export/type backlog at
227/206 — measured with `classMembers` off, so comparable to the 57/181
above rather than to the ceiling's 44/150 — so a production-mode pass is
a follow-up of its own.
- The same entry treatment hides the test trees themselves. Root
`package.json`'s `test:scripts` runs `node --test`, which is what makes
knip's node plugin contribute its test entries, and that plugin
registers four entry patterns in **every** workspace: `**/test/**`, plus
`*{.,-,_}test.<ext>`, `test-*.<ext>` and `test.<ext>` matched anywhere.
So none of the **380** JS/TS files under `packages/*/test/` can be
reported unused, and under `packages/*/tests/` only a name none of those
three match can — of today's 15 that is `registry/tests/helpers.ts`
alone, which is why it is the lone `tests/` entry in the `--production`
list above. A `tests/test-utils.ts` or `tests/test.ts` would be just as
invisible. A dead `zz.test.ts` planted in `packages/registry/tests/`
passes; a dead `zz-helper.ts` in the same directory fails.
- `knip@5.88.1` is the last 5.x release ever published — 6.0.0 landed
the next day and `latest` is now 6.x. The pin is deliberate: knip 6
removed the `classMembers` issue type outright, and the shipped script
hard-errors on it (`ERROR: Invalid issue type: classMembers`, exit 2).
Upgrading is therefore not a version bump — it means re-deriving the
ceiling, rewriting the paragraph that explains it, and resolving the
pass-1 findings knip 6 adds (six unlisted binaries: `tail`, `plutil`,
`xctrace`, `netstat`, `pkill`, `pgrep`; two unresolved imports:
`/usr/bin/osascript`, `/usr/libexec/PlistBuddy`). Dependabot's
`npm-minor-patch` group cannot move it, since no 5.x exists above
5.88.1; the `npm-major` group will keep offering the 6.x jump.
- Knip folds `optionalDependencies` into the declared set, so importing
`electron` or `node-pty` is not `unlisted` — they pass because it read
them. What it never reports is an optionalDependency nothing uses, so
only that direction is outside the gate, along with `argent-cli`'s
runtime `req("node-pty")`, resolved through a
`createRequire(import.meta.url)` in `lens-pty.ts`, which knip can't see
either.

## Dead code this found

- `@types/semver` and `@argent/telemetry` from `packages/argent` — both
were being suppressed by exemptions whose stated bundler-boundary reason
could not apply. esbuild strips types and never resolves `@types/*`, and
`packages/argent` names `semver` nowhere; `@argent/telemetry` is aliased
to an absolute source path by `bundle-tools.cjs`, so the declaration
bought nothing. Removing them is proved safe by building: four of the
five bundles still emit and telemetry is inlined in each with no runtime
`require` of it. The fifth, preview-window's main, sits behind a
downloaded native asset absent from a script-free install, so it is
covered by mechanism rather than observation — `bundle-tools.cjs` passes
the same `alias` map to every build, and esbuild's `alias` bypasses node
resolution entirely: deleting `node_modules/@argent/telemetry` outright
leaves all four emitted bundles byte-identical.
Removed alongside, but **not** gate-reported: the `benchmark` npm script
in `packages/argent`, whose `scripts/benchmark.cjs` does not exist. A
dangling script target is a blind spot of this gate — restoring the
script leaves knip at exit 0.

- Four files with no importers: the `render-hook` injected script,
react-profiler's `source-map` + `snippet-extractor`, and
`scripts/setup-in-workspace.cjs` (referenced nowhere, and its own usage
string names a `scripts/setup-project.cjs` that doesn't exist).
- `superagent` in `tool-server`'s `test/http-streaming.test.ts` —
imported for a type but declared in no manifest in the repo, resolving
only through npm hoisting of supertest's own dependency. Reverting that
import to `superagent` reports `Unlisted dependencies (1) … :3:32` and
exits 1; `@types/supertest/index.d.ts:10` re-exports the identical type
(`type Response = superagent.Response`).
- `@clack/prompts` and `@moq/lite` from `tool-server`'s `package.json` —
declared, imported nowhere. (`@moq/net` is the one actually used;
`@moq/lite` isn't a peer of it.)

Also deleted here, but **not** something this gate reported:
`src/tools/simulator/simulator-server.ts`. It sat in the `files` blind
spot above — `interaction-messages.test.ts` imported it, which is enough
to count as reached. `setup-registry.ts` never registers it, so it was
unreachable over MCP and HTTP, and that catalog roll-call was its only
reference; the import and the catalog count (77 → 76) went with it.

## Lockfile

Adding knip moves two transitive packages: `@napi-rs/wasm-runtime` 1.1.5
→ 1.1.6 and `@tybys/wasm-util` 0.10.2 → 0.10.3. Knip pulls in
`@oxc-resolver/binding-wasm32-wasi`, which floors
`@napi-rs/wasm-runtime` at `^1.1.6`, lifting the hoisted entry that had
resolved to 1.1.5 for `@rolldown/binding-wasm32-wasi`'s `^1.1.4`, which
then cascades to `@tybys/wasm-util`. No runtime impact — both are
support packages for the wasm32-wasi fallback bindings, which don't get
installed where a native binding exists; `npm ls` reports both
extraneous on darwin-arm64. Those two are the only resolved versions
that change in the diff; the rest is knip's own tree (49 packages added)
and `@moq/lite` removed.

## Verification

- `npm run knip` exits 0.
- **Coverage of the enforced set.** With no `rules` block, knip's
included set is 15 issue types: ten enforced at zero tolerance by the
first pass, plus the five the second pass names under the ceiling.
Eleven mutations cover all ten, each flipping the gate to exit 1 and
back to 0 on revert:

  | class | mutation |
  | --- | --- |
| `files` | a throwaway `.mjs` in `scripts/`; a throwaway `.ts` in
`packages/argent/src` |
| `dependencies` | a bogus declared dep in
`packages/tool-server/package.json` |
  | `devDependencies` | a bogus declared devDep in the same manifest |
| `optionalPeerDependencies` | `bytes` moved to an optional
`peerDependency` there |
  | `unlisted` | an undeclared import in `packages/argent/src/cli.ts` |
  | `unresolved` | an unresolvable relative specifier in the same file |
| `binaries` | a root `package.json` script invoking a nonexistent
binary |
| `duplicates` | `export default sleep` alongside the existing named
export in `utils/timing.ts` |
| `enumMembers` | an unused member on an enum a second module imports
and uses |
| `catalog` | a `catalog` block in the root `package.json` naming a
package nothing uses |

One nuance worth recording: `duplicates` is narrower than it looks. It
reports a symbol exported both by name and as `default`
(`sleep|default`); a second *named* alias of the same symbol is not a
duplicate, and a freshly added symbol nothing references is not reported
at all — so several naive probes read as a miss.

`enumMembers` needed a shape the repo does not currently contain, not an
unreachable one. The only exported enum here, `ServiceState`, is
re-exported through `@argent/registry`'s entry, where knip counts
exports as used, so an unused member added to it is **not** reported —
but an enum in a non-entry module that another module imports and uses
does report one (`Blue ZzColor …/zz-enum.ts:3:3`, exit 1; deleting the
unused member returns exit 0). That is the ninth of the ten covered.

`catalog` is reachable here despite the repo using npm workspaces:
knip's catalog loader falls back to `manifest.catalog` when there is no
pnpm or yarn workspace file, so a `catalog` block in the root
`package.json` reports `Unused catalog entries (1)` and exits 1.
- The two new mechanisms discriminate as well: one extra unused export
takes the backlog to 207 and fails the ceiling, and a stale
`ignoreDependencies` entry produces `Configuration hints (1) … Remove
from ignoreDependencies` and exit 1.
- `npm ci --ignore-scripts` in a clean worktree, followed by `npx knip`,
is green — the job doesn't need any of the postinstalls it now skips.
- `tsc --build`, `typecheck:tests` (all workspaces),
`typecheck:scripts`, `test:scripts`, `eslint . --max-warnings 0`,
`prettier --check .` and the full workspace vitest suite (4711 passed, 3
skipped across 13 workspaces; 3070 of those are `@argent/tool-server`
alone) are green; `package-lock.json` is untouched by the review pass.
2026-08-10 17:09:16 +02:00
Juliusz Wajgelt 67a4481868 refactor(tool-server): use @swmansion/argent-cloud-sdk for MoQ + input protocol (#743)
## Summary

argent and the other client of the same device backend each hand-rolled
the same MoQ client and `DataChannelCommand` protobuf encoder, with no
shared source — argent carried a vendored copy of the `.proto` as an
inline string that could silently drift from the server schema. Both now
use `@swmansion/argent-cloud-sdk`, where the encoder is checked
byte-for-byte against the canonical `.proto` in tests, so drift fails
the build instead of shipping.

- **`utils/moq-client.ts`** (195 → ~50 lines) is now a thin adapter over
the SDK's `MoqDeviceSession`. It keeps the `MoqClient` interface exactly
(`sendControl`, `screenshot` → `Buffer`, `close`), so
`blueprints/simulator-server.ts` and `createMoqTransport` are untouched.
argent still resolves its endpoint through `sim-remote moq-info`
(preserving daemon-based session sharing) and still publishes on its own
`"argent"` broadcast path.
- **`utils/datachannel-proto.ts`** (201 lines → re-exports) so its ~10
import sites keep working unchanged.
- **Dependencies:** drops `protobufjs` and `@moq/lite`, both now unused.
`@moq/net` is kept and still dedupes to a single copy.

No behavioural change intended.

## Verified end-to-end against live infrastructure

Ran the migrated client against a real remote simulator (iPhone 17 Pro,
iOS 26.5) on a live staging machine, driving this branch's
`moq-client.ts` through the published SDK **0.1.2**:

| Check | Result |
|---|---|
| MoQ session open (WebTransport, cert-pinned) | **PASS** |
| Screenshot | **PASS** — valid PNG 1206×2622 |
| Sequential screenshots | **PASS** |
| Concurrent screenshots | **PASS** |
| Input round-trip (tap an app icon) | **PASS** — tapping Settings
opened Settings, confirmed visually in the returned screenshot |
| Screenshot after concurrent burst | **PASS** |
| Session teardown | **PASS** |

Plus the existing suite: full workspace `tsc --build`, **3372 tests
passing** (1 skipped), and `npm ls @moq/net` confirming a single deduped
copy.

### A bug this surfaced, now fixed

Concurrent `screenshot()` calls hung under SDK **0.1.0**. The server
answers one screenshot per session at a time; 0.1.0 had dropped the
serialisation the old hand-rolled client had, so a second in-flight
request was never answered and its promise never settled — a regression
against pre-SDK behaviour that only showed up under live traffic, with
no unit test catching it.

Fixed in SDK 0.1.1 (queue requests, plus a 30s request timeout so a
dropped response errors instead of hanging). This PR depends on `^0.1.2`
— a republish of 0.1.1 through the release workflow carrying npm build
provenance, whose shipped `dist` is byte-identical (verified by SHA256),
so the live results above hold for it unchanged.

## Notes for reviewers

- The SDK subscribes to the screenshot track lazily, so a video-only
consumer doesn't open a subscription it never reads. argent requests
screenshots, so this path is exercised.
- Screenshot responses are matched by the request id the server echoes,
so a late or duplicate frame can't be handed to the wrong waiter.
2026-08-07 12:51:19 +02:00
filip131311 566663bde4 chore: bump to 0.19.0 (#741)
Version-only bump of the workspace to 0.19.0, same shape as #603 (the
0.18.1 bump).

## What changed

- All 16 packages under `packages/*` → `0.19.0`
- `package-lock.json` — regenerated with `npm install
--package-lock-only --ignore-scripts` on Node 24, matching what the
Lockfile job runs. Version-only: 16 insertions, 16 deletions, nothing
else.
- `server.json` — the MCP registry manifest, in **both** places it
carries a version: the top-level `version` and `packages[0].version`. It
lives outside `packages/*`, so a workspace sweep leaves it behind and
the registry then rejects the publish because `packages[].version` names
a version npm has never seen.

## Verification

- `npm run check:versions` → "All workspace packages and server.json are
at 0.19.0."
- `npx prettier --check` clean on every touched file
- No `"0.18.1"` string left anywhere in the repo

No code changes.
2026-08-05 20:13:09 +02:00
Jakub Piasecki a644d52d39 feat(flows): run CLI flows from explicit paths (#568)
`argent flow run` now takes an explicit YAML path instead of a
saved-flow name:

```
argent flow run .argent/flows/checkout.yaml --platform ios
```

The path may point anywhere on the local filesystem; the filename stem
names the run's report/artifacts, so it must be `[A-Za-z0-9_-]+` with a
lowercase `.yaml` extension. A bare name fails with the exact
replacement command, and `argent flow list` prints paths instead of
names.

Because sibling `run:` files and `__baselines__` resolve beside the
YAML, the CLI now requires the auto-started local tool server and
refuses `ARGENT_TOOLS_URL` / `argent link` routing. The `flow-execute`
tool stays remotely callable via `name` + `project_root`, and gains a
`flow_path` parameter (exactly one of `name`/`flow_path`) that is only
honored when the file-input boundary verified it as a real host path —
uploads and boundary bypasses are rejected, so it can't execute
arbitrary server YAML.

Also: the flow-name pattern is now a single shared export from
`@argent/registry`, and the recorder composes a `flow_path` call into a
`run:` step when it targets a sibling of the recording.
2026-08-05 14:51:42 +02:00
filip131311 19782f4e1a feat(secrets): resolve {{secret:…}} from .env files, not just the environment (#734)
## Why

A credential could only reach a typing tool through an
`ARGENT_SECRET_<NAME>` environment variable. That is the right CI
contract — and stays the highest-priority source — but interactively it
is poor ergonomics: an editor-hosted tool-server inherits the
environment its editor was launched with, so changing a credential means
restarting the editor, and there is nowhere to write one down that
survives a reboot. "Is loading these from a `.env` file on the roadmap?"
is the recurring ask.

## What

`{{secret:NAME}}` now resolves through an ordered chain, first match
wins, re-read on every resolution — so editing a file applies to the
next keystroke with **no server restart** (the same runtime-read design
`ios.additionalDeviceSets` already uses).

| # | Source | Which keys it exposes |
| - | ------ | --------------------- |
| 1 | `ARGENT_SECRET_<NAME>` in the environment | prefixed variables
only — unchanged, still wins |
| 2 | `<project>/.argent/secrets.env` | every key (gitignore it) |
| 3 | `<project>/.env.local`, then `<project>/.env` | **only
`ARGENT_SECRET_`-prefixed keys** |
| 4 | `~/.argent/secrets.env` | every key, any project |

Covers `keyboard`, `paste`, `run-sequence` keyboard steps, and flow
`type:` steps — all four already funnel through the same resolution
point, so nothing else changed.

Also adds `argent secrets [list] [--json]` so a setup is verifiable
without an agent: names only, never values, shadowed names marked, and a
warning when a linked remote tool-server means resolution happens on a
different machine.

## The one design decision worth reviewing

**Row 3 is why this isn't just "read the `.env`".** An agent composes
the tool call, so anything reachable through `{{secret:…}}` is
exfiltratable by a prompt-injected one — type it into a field, then read
the field back. "Which values may argent type" has to stay an allowlist
the user wrote deliberately.

So: a *dedicated* argent secrets file exists for no other purpose and
therefore **is** the allowlist; a file the app *shares* keeps the prefix
as the allowlist, exactly as the process environment does. Your
`STRIPE_SECRET_KEY` sitting in the same `.env` stays unreachable —
verified live, not just asserted. Adding `ARGENT_SECRET_` to one line is
the deliberate act that exposes it.

Two smaller calls, both reversible:

- The chain lives in `@argent/configuration-core`, not the tool-server,
so the CLI reads the same code; the tool-server keeps only the
placeholder engine.
- The project scope is discovered by the **cwd walk-up every other
project-scoped configuration uses**. A per-request project root (header
→ `ToolContext`) was considered and rejected as new protocol surface
across two client paths; reusing flows' module-global
`setActiveProjectRoot` was rejected as a cross-project leak on a reused
server. The cost is that a tool-server whose cwd sits outside the
project skips project files — mitigated by the failure now listing every
path it consulted, and by `~/.argent/secrets.env` needing no project at
all.

## Verification

Unit + integration: tool-server 3122, configuration-core 122 (19 new),
argent-cli 370 (6 new). Lint and prettier clean. All four publish
bundles build.

Live, driving this branch's tool-server on :3001 against iPhone 17 Pro /
iOS 26.4 and the Electron testbed:

- global `~/.argent/secrets.env` → typed correctly; the response echoed
only the placeholder
- prefixed key in the project `.env` → typed; **unprefixed
`STRIPE_SECRET_KEY` in that same file → rejected**
- `.argent/secrets.env` beat `.env`; the environment variable beat both
- edited a secrets file mid-session → applied on the very next call,
same server pid
- a chromium flow with `type: { text: "{{secret:APP_PASSWORD}}" }` → 3/3
pass, asserting on the greeting the app rendered, so the *correct value*
provably landed
- an undefined name inside a flow surfaces the full source list in the
step report

Sample of the failure output (no value ever appears):

```
Unknown secret "LOGIN_PASSWORD" — no source on the machine running the tool-server defines
LOGIN_PASSWORD. Available secrets: APP_USER.
Sources consulted, first match wins:
  1. environment (ARGENT_SECRET_*) — 0 secrets
  2. /path/to/project/.argent/secrets.env — found, 1 secret
  3. /path/to/project/.env.local — not found
  4. /path/to/project/.env — found, 1 secret
  5. /Users/me/.argent/secrets.env — not found
To make it available, ask the user to add `LOGIN_PASSWORD=…` to …
```

## Out of scope

No general flow variable/parameter system — a non-secret external value
(a test account's email, a staging tenant id) can use the same
placeholder, but argent treats every such value as a secret: redacted
from errors, never echoed, so it is not for something a report should
show. No config key pointing at an arbitrary secrets file. Remote
(`argent link`) still resolves on the remote host, which the CLI now
says out loud.
2026-08-04 15:23:12 +02:00
filip131311 9489a220b0 chore(argent): declare the Node version the package actually needs (#653)
Fixes #640.

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

## Why 20.12.0 and not 20

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

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

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

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

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

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

## Docs corrected alongside

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

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

## What a reviewer should know

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

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

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

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

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

## Separate bug found while verifying this

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

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

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2026-08-03 11:01:09 +02:00
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
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
Ignacy Łątka 78d3184ba8 fix(scripts): stop extract-tools dropping tools with long descriptions (#436)
## Defect

`scripts/extract-tools.mjs` extracts MCP tool definitions by regex to
feed the downstream `spidershield scan --tools-json` security scan. For
each `id:` match it scanned only the next 2000 chars (`const descWindow
= afterId.slice(0, 2000)`) for the `description:` value, and the
description regexes require the string's closing delimiter to fall
inside that window.

Any tool whose description's closing delimiter sits past ~2000 chars is
therefore matched as `null` and **silently dropped** - never emitted,
never security-scanned. Two shipping tools hit this:

- `run-sequence`
(`packages/tool-server/src/tools/run-sequence/index.ts`) - a ~4285-char
multi-line template-literal description whose closing backtick sits
~4324 chars past its `id:`.
- `describe` (`packages/tool-server/src/tools/describe/index.ts`) - a
~2195-char description whose closing backtick sits ~2252 chars past its
`id:`.

**Security impact:** `describe` is the accessibility / DOM tree tool,
exactly the kind of tool a `spidershield` scan should cover, and it was
being silently excluded alongside `run-sequence`. A dropped tool is not
a scan finding, so the gap was invisible.

### Repro (before)

```
$ node scripts/extract-tools.mjs | grep -c '"run-sequence"'
0
$ node scripts/extract-tools.mjs | grep -c '"describe"'
0
$ node scripts/extract-tools.mjs | grep -c '"gesture-tap"'
1
# total tools emitted: 70  (run-sequence AND describe both missing)
```

### After

```
$ node scripts/extract-tools.mjs | grep -c '"run-sequence"'
1
$ node scripts/extract-tools.mjs | grep -c '"describe"'
1
# total tools emitted: 72  (both captured, run-sequence's full 4285-char description intact)
```

## Fix

- Drop the fixed 2000-char lookahead. Each tool's `description:` is
resolved at the **same object-literal brace level** as its `id:` by a
forward scan that tracks brace depth over real code only: comments,
string literals, template literals (with `${...}` interpolations skipped
opaquely, recursively), and regex literals (prev-token heuristic incl.
regex-permitting keywords like `return`) are skipped whole, so nothing
inside them can open a fake string, shift the depth, or match as a
`description:`/`id:` token. The value is then parsed to its actual
closing delimiter, so a description of any length is captured in full.
- Because matching is by brace scope rather than raw position, a stray
`id:`/`description:` token can't corrupt extraction: a description-less
tool can't borrow the next tool's description, an `id:` appearing inside
description text or a comment can't truncate or overwrite anything, and
an `id:` key nested inside a value object (e.g. `defaultPayload: { id:
"example" }`) is neither emitted as a spurious tool nor allowed to drop
the real one.
- Captured text follows full JS cooked-string semantics: named escapes,
`\xNN`, `\uNNNN`, `\u{...}`, identity escapes, line continuations, and
template `\r\n`/`\r` -> `\n` normalization - the emitted (and
security-scanned) text is the string the runtime renders.
- A description value that is not a single plain literal - a
concatenation, a `${...}` interpolation, a method/operator suffix
(`"...".slice(0, 3)`), `as const` - is never emitted as a
truncated/wrong literal: it warn-skips on **stderr** (stdout stays valid
JSON for the scanner). Non-interpolated template-literal ids (`` id: `x`
``) are extracted; interpolated ids stay out of scope like
const-reference ids.
- Duplicate ids keep the first occurrence and warn (exported as a pure,
unit-tested function).
- The extractor stays dependency-free (node: builtins only): the
tool-description-quality workflow runs it on a bare checkout without
`npm ci`.
- Output JSON shape is unchanged.

## CI guard

Adds `scripts/extract-tools.test.mjs` (node's built-in test runner,
wired into `npm run test:scripts`; 36 tests). Ground truth: the
real-tree tests parse every tool source file with the actual
**TypeScript parser** (a root devDependency, test-only) and require the
extractor's output to equal the AST **exactly - names and descriptions
byte-for-byte**. Any drop, corruption, or fabrication on a shape that
enters the tree fails CI loudly, with no reliance on stderr warning
formats and no false red from `id:`-like text in comments or strings.

Two policy gates keep un-extractable shapes out of the tree instead of
letting them silently leave the scan: a `description` that cannot be
read statically (non-literal value, shorthand/getter/method/computed
member, or a spread sibling that may carry/override it) and a
plain-literal description written **before** its `id:` (the scan is
forward-only) each fail with an actionable, file-naming message.

Fixture tests pin the parsing rules independent of any real tool's
wording - long descriptions (>4000-char value, >2000 chars of siblings
between `id:` and `description:`), embedded `id:`/`description:` tokens,
comments, keyword-position and sibling regex literals, division, nested
templates and interpolation content, template/`as const` ids, the full
escape suite, CRLF cooking, single-quoted forms, trim, warn-skip
classes, and the duplicate-id warning. Each new fixture was verified to
fail under a targeted mutation of the extractor.

Note: tools registered with a const-reference / dynamic id (e.g. `id:
AWAIT_UI_ELEMENT_TOOL_ID`) rather than a static literal stay out of
scope for this extractor - a separate, pre-existing limitation unrelated
to the description window, and one this static/lexical extractor cannot
close soundly (it has no tool-scope knowledge, so a blanket warning on
every `id: <identifier>` would also fire on legitimate nested non-tool
keys like `{ id: someVar }` in a payload/example object). The full set
currently taken out of the scan this way is `app-map-crawl`,
`app-map-export`, `app-map-navigate`, `app-map-query`,
`await-screen-idle`, and `await-ui-element`. A proper fix is a follow-up
covering const-ref id resolution plus a real-tree emit-count and
empty-stderr assertion so this silent-omission class trips CI if it ever
regresses.

## Verification

- `node scripts/extract-tools.mjs` -> 72 tools, `run-sequence` and
`describe` both present (total 70 -> 72), byte-identical to the
TypeScript-AST parse of the tree, no stderr warnings
- `npm run test:scripts` -> all pass (`extract-tools.test.mjs`: 36
tests)
- mutation checks: window-bounding, depth-anchor removal, trim removal,
dedup neutralization, single-quote-branch neutralization each fail the
suite
- `npm run typecheck:scripts` -> clean
- `prettier` + `eslint` on changed files -> 0 problems
- lockfile: `npm install --package-lock-only --ignore-scripts` -> no
diff (idempotent)
2026-07-20 12:56:44 +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
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
Stanisław Chmiela a7775e4ba6 feat(tool-server): add tool-server event log (#367)
We want to display a nicely parsed and rendered log of events that
happened over a span of an Argent session.

Current log file is not designed to be machine-readable, so adding a
JSONL-formatted log of events (gated by `tool-server-event-log`).

I eventually want this 😋
https://x.com/j3nnings/status/2069072177584849192

When enabled, the tool server writes Bunyan-compatible records to
`ARGENT_EVENT_LOG` or `~/.argent/tool-server-events.jsonl`. The log
captures tool-server lifecycle events and registry lifecycle events,
including tool invocations and others.

To make messages nicer, added `interaction` object to tool definition.
This allows tools to define a nicer message in log (print "Screenshot
capture completed." instead of "Tool `screenshot` completed.") Adding
these to the existing tools is a follow up PR matter I think.
2026-07-10 11:52:28 +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
Hubert Gancarczyk 3393d22edd feat(telemetry): derive distinct_id from a stable host fingerprint (#444)
## Summary

Replaces the random-UUID telemetry `distinct_id` with a stable
per-machine id derived from the host fingerprint, so PostHog's native
"Unique users" counts machines (funnels / retention / breakdowns work
with no HogQL). Every Argent entry point - installer, CLI, tool-server,
MCP - derives the id from one shared code path, so a given machine
reports a single identity across every event and across install /
uninstall / reinstall.

## What changed

- `distinct_id = uuidv5(hostFingerprint, NAMESPACE)`, where the
fingerprint comes from `simulator-server fingerprint`. Deterministic,
keeps the UUID shape, and never stores the raw hardware value in PostHog
(only the SHA-1-derived UUID leaves the process).
- The fingerprint is resolved in `buildPayload` - the single `capture()`
path shared by every entry point - so installer / CLI / tool-server /
MCP all produce the same id. Resolved lazily, once per process,
memoized.
- One-time, local-only migration: the first time a fingerprint resolves,
`~/.argent/telemetry-id` is rewritten from the legacy random UUID to the
derived id. No PostHog `alias` / `$identify` event is emitted.
- Fallback: if the fingerprint can't be resolved (unsupported platform,
binary absent), the stored or a fresh random UUID is used - identically
for every entry point on that machine, self-healing to the derived id on
the next successful resolve.
- Hardening: never throws; the fingerprint is validated (exactly 64 hex,
case-insensitive) before hashing; corrupt / empty / unreadable id files
self-heal; the binary is spawned at most once per process.

## Why the id is stable across install -> uninstall -> reinstall

Uninstall deletes `~/.argent/telemetry-id`. Because the id is
`uuidv5(fingerprint)` and the fingerprint is deterministic per machine,
a fresh install recomputes the identical id. Previously only the
tool-server resolved the fingerprint while the installer emitted under a
random UUID, so this cycle produced a different random id each time.

## Bundling note for reviewers

`@argent/telemetry` now depends on `@argent/native-devtools-ios` to
locate the binary. That package computes its bin dir from `__dirname` at
module load, which is undefined in an ES module. The installer / CLI /
MCP ship as ESM bundles, so `bundle-tools.cjs`'s ESM banner is extended
to shim `__dirname` / `__filename` (the CJS tool-server bundle already
has them natively). Without this shim those bundles would crash on load,
so the banner change is load-bearing.

## Testing

- Unit: 219 telemetry tests - RFC 4122 v5 vectors, migration,
idempotency, case/length validation, corrupt-file self-heal,
spawn-at-most-once, and all fallbacks.
- E2E on a real machine + booted iPhone 16 Pro: installer, CLI, and
tool-server runtimes all emit the same derived id; the id is stable
across a delete/recreate (uninstall/reinstall) cycle; a legacy v4 id
migrates locally with no alias/identify emitted; verified from an ESM
bundle placed in the real install's `dist/` so the binary resolves via
the bundled `__dirname` with no env override; tool calls against the
device carry the same id.
2026-07-07 16:28:29 +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
Ignacy Łątka a92c859d07 feat(argent-lens): argent lens — bind a claude session to the preview window (#416)
## What

Adds `argent lens` — open Argent Lens bound to a fresh `claude` session.

## Why

The Lens preview window and the agent communicate through a shared,
process-wide tool-server that carries **no per-agent identity** (only a
coarse telemetry header). So when the user requests changes in the
window, there's no reliable way to route them to the *specific* agent
that opened it.

`argent lens` sidesteps the problem by inverting ownership: the human
launches Lens, and Lens spawns (and therefore owns) exactly one `claude`
terminal. The binding is 1:1 by construction — no identity plumbing
required.

## How it works

1. `argent lens` ensures a tool-server and **begins a CLI Lens
session**, which opens the preview window up front (no
`await_user_selection` needed) and keeps it open across rounds.
2. It spawns a tracked `claude` terminal in the current directory — a
TypeScript port of the macOS applet's `osascript` spawn/track mechanism
— seeded to propose variants without blocking.
3. A watcher polls `GET /preview/outcome`; on each submitted round it
types a one-line feedback summary into that terminal, queuing it to the
agent as its next prompt. Requesting changes therefore "talks back" to
the bound agent instead of closing the window.

macOS only (drives Terminal/iTerm via `osascript`); off-darwin it exits
with a clear message.

## Changes

- **CLI** `lens-terminal.ts` — `osascript` spawn/track + a new
write-into-running-session path (the "outputting" channel; injects input
into the running TUI, which a tty write cannot do).
- **CLI** `lens.ts` — the command + feedback formatter.
- **tool-server** — a `cliSession` flag in the variant store (opens the
window, suppresses auto-close on submit); `POST /preview/cli-session`
and `GET /preview/outcome` routes.
- **UI** — relabels the submit action to "Request changes" while a CLI
session is active.

Behind the existing `argent-lens` feature flag.

## Verification

- Build + typecheck across the workspace; ESLint (`--max-warnings 0`) +
Prettier clean.
- 34 new unit tests; full `@argent/cli` suite **118 passing**,
tool-server variant/preview suites **46 passing**.
- **E2E (observed on this machine):**
- **tool-server HTTP** — `cli-session` toggles `cliSession` true/false;
submitted feedback round-trips through `/preview/outcome`.
- **terminal injection** — spawned a window running `cat`, confirmed
`writeToSession` lands text in the running program's **stdin** (both
iTerm and Terminal), with embedded newlines flattened.
- **full relay** — the real `watchAndRelay` loop against a real
tool-server typed two distinct submission rounds' feedback into a
stand-in agent terminal (dedup on `completedAt` verified).
- `argent lens --help` and the feature-flag gate (exit 1 with guidance,
no spawn).

Note: a real end-to-end with a live `claude` agent was not run (it would
drive a paid interactive session); the agent-facing channel is proven by
injecting into a stand-in process's stdin via the exact same code path.

---------

Co-authored-by: Filip131311 <contact@filipkaminski.com>
2026-07-01 19:57:56 +02:00
Juliusz Wajgelt 20f5a1282c feat: initial sim-remote support (#291)
## Summary

Adds **initial support for remote iOS simulators** ("sim-remote") —
simulators that run in the cloud (radon-cloud orchestrator) and are
driven from the dev's machine over a QUIC/MoQ tunnel, alongside the
existing local iOS simulators. A remote sim is exposed as a new device
platform, `ios-remote`, and the existing
interaction/inspection/profiling tools work against it through the same
tool surface.

To get there without scattering `isRemote` branches across every tool,
the iOS code paths are refactored behind a small set of
host/transport/backend abstractions so each tool stays
transport-agnostic.

## How it works

- **Discovery** — `list-devices` enumerates remote sims via the
`sim-remote` CLI in addition to local `simctl`. They classify as
`platform: "ios-remote"`. Enumeration is best-effort: if `sim-remote`
isn't installed or the user isn't logged in, it silently returns none.
- **Transport** — remote interaction goes over a MoQ relay
(WebTransport/QUIC). `moq-client.ts` subscribes to the server-published
`simulator` broadcast (catalog/video/screenshot) and publishes a
`control` track carrying protobuf-encoded input commands (touch / key /
button / rotate / wheel / screenshot). WebTransport is provided in Node
by `@fails-components/webtransport`, polyfilled lazily on first use.
- **Native devtools over the tunnel** — unix sockets don't bridge the
QUIC tunnel, so on remote sims `ax-service` and `native-devtools` are
forced onto TCP with OS-assigned ephemeral ports, and a reverse proxy is
wired before the in-sim daemon dials back.

## Key components

New (`packages/tool-server/src/`):
- `utils/sim-remote.ts` — thin wrapper around the `sim-remote` CLI
(simctl verbs, `moq-info`, proxy), propagating CLI stderr verbatim.
- `utils/moq-client.ts` — pure-JS MoQ client for the remote
simulator-server relay.
- `utils/datachannel-proto.ts` + `proto/datachannel.proto` — vendored
protobuf schema + encoders for the control channel.
- `utils/ios-host.ts` — `IosHost` abstraction (`localIosHost` /
`remoteIosHost`, picked by `pickIosHost(device)`) capturing the
local-vs-remote differences (transport, env setup, proxying,
`requiresTcp`).
- `utils/simctl-backend.ts` — strategy for simctl verbs so one iOS
handler serves both `xcrun simctl` (local) and `sim-remote simctl`
(remote).
- `utils/ax-prefs.ts` — AX-pref helpers extracted from `ax-service`.
-
`tools/{launch-app,open-url,reinstall-app,restart-app}/platforms/ios-remote.ts`
+ shared handlers.

Refactors:
- **Strategy pattern** to limit `isRemote`/platform branching inside
tool and blueprint implementations.
- `simulator-client.ts` gains a transport abstraction
(`SimulatorServerTransport`) so call sites stay transport-agnostic;
local sims back it with WebSocket+HTTP, remote sims with the MoQ client.
- `ax-service` / `native-devtools` blueprints updated for ephemeral TCP
ports and the `ios-remote` platform.
- Dependency checks: tools that pick a platform path internally now gate
on the right CLI (`sim-remote` vs `xcrun`).

## New dependencies

`@moq/lite`, `@moq/net`, `@fails-components/webtransport`,
`@fails-components/webtransport-transport-http3-quiche`, `protobufjs`.

## Testing

- Existing unit suite passes (CI green); the native-devtools
status/describe tests now stub the platform dep-check so they exercise
their logic, not the host toolchain.
- E2E device-smoke jobs (local iOS sim, Android, Chromium, remote VVD)
pass.

---

<sub>Branch rebased onto latest `main` (linear history). `sim-remote` is
opt-in — only required when driving a remote iOS simulator.</sub>
2026-07-01 15:57:47 +02:00
dependabot[bot] 0718e622ab chore(deps)(deps): bump the npm-minor-patch group across 2 directories with 9 updates (#422)
Bumps the npm-minor-patch group with 9 updates in the / directory:

| Package | From | To |
| --- | --- | --- |
| [globals](https://github.com/sindresorhus/globals) | `17.6.0` |
`17.7.0` |
| [prettier](https://github.com/prettier/prettier) | `3.8.4` | `3.8.5` |
| [semver](https://github.com/npm/node-semver) | `7.8.4` | `7.8.5` |
|
[typescript-eslint](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/typescript-eslint)
| `8.61.1` | `8.62.0` |
|
[@clack/prompts](https://github.com/bombshell-dev/clack/tree/HEAD/packages/prompts)
| `1.5.1` | `1.6.0` |
|
[posthog-node](https://github.com/PostHog/posthog-js/tree/HEAD/packages/node)
| `5.35.0` | `5.38.6` |
| [smol-toml](https://github.com/squirrelchat/smol-toml) | `1.6.1` |
`1.7.0` |
| [electron](https://github.com/electron/electron) | `42.4.1` | `42.5.0`
|
|
[fast-xml-parser](https://github.com/NaturalIntelligence/fast-xml-parser)
| `5.9.2` | `5.9.3` |

Bumps the npm-minor-patch group with 1 update in the /packages/telemetry
directory:
[posthog-node](https://github.com/PostHog/posthog-js/tree/HEAD/packages/node).

Updates `globals` from 17.6.0 to 17.7.0
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/sindresorhus/globals/releases">globals's
releases</a>.</em></p>
<blockquote>
<h2>v17.7.0</h2>
<ul>
<li>Update globals (2026-06-22) (<a
href="https://redirect.github.com/sindresorhus/globals/issues/345">#345</a>)
33b75f9</li>
</ul>
<hr />
<p><a
href="https://github.com/sindresorhus/globals/compare/v17.6.0...v17.7.0">https://github.com/sindresorhus/globals/compare/v17.6.0...v17.7.0</a></p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/sindresorhus/globals/commit/a19670cc86c1218e915657c55ea02ba3e7623834"><code>a19670c</code></a>
17.7.0</li>
<li><a
href="https://github.com/sindresorhus/globals/commit/9611620bad0ea45531ae0261b986ce694374e42c"><code>9611620</code></a>
Update actions (<a
href="https://redirect.github.com/sindresorhus/globals/issues/346">#346</a>)</li>
<li><a
href="https://github.com/sindresorhus/globals/commit/33b75f9ddeb6f9ddd81251b043667eb8104a8d15"><code>33b75f9</code></a>
Update globals (2026-06-22) (<a
href="https://redirect.github.com/sindresorhus/globals/issues/345">#345</a>)</li>
<li><a
href="https://github.com/sindresorhus/globals/commit/887dd529bde24486b7f9e31d1b2c515df2b7cf5c"><code>887dd52</code></a>
Fix build script (<a
href="https://redirect.github.com/sindresorhus/globals/issues/344">#344</a>)</li>
<li>See full diff in <a
href="https://github.com/sindresorhus/globals/compare/v17.6.0...v17.7.0">compare
view</a></li>
</ul>
</details>
<br />

Updates `prettier` from 3.8.4 to 3.8.5
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/prettier/prettier/releases">prettier's
releases</a>.</em></p>
<blockquote>
<h2>3.8.5</h2>
<ul>
<li>Fix Flow variance annotation print (<a
href="https://redirect.github.com/prettier/prettier/pull/19022">#19022</a>
by <a
href="https://github.com/marcoww6"><code>@​marcoww6</code></a>)</li>
</ul>
<p>🔗 <a
href="https://github.com/prettier/prettier/blob/3.8.5/CHANGELOG.md#385">Changelog</a></p>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/prettier/prettier/blob/main/CHANGELOG.md">prettier's
changelog</a>.</em></p>
<blockquote>
<h1>3.8.5</h1>
<p><a
href="https://github.com/prettier/prettier/compare/3.8.4...3.8.5">diff</a></p>
<h4>Flow: Support <code>readonly</code> as a variance annotation (<a
href="https://redirect.github.com/prettier/prettier/pull/19022">#19022</a>
by <a
href="https://github.com/marcoww6"><code>@​marcoww6</code></a>)</h4>
<p>Flow now accepts <code>readonly</code> as a property variance
annotation, equivalent to <code>+</code> (covariant/read-only).</p>
<!-- raw HTML omitted -->
<pre lang="jsx"><code>// Input
type T = {
  readonly foo: string,
};
<p>// Prettier 3.8.4<br />
SyntaxError</p>
<p>// Prettier 3.8.5<br />
type T = {<br />
readonly foo: string,<br />
};<br />
</code></pre></p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/prettier/prettier/commit/1ce8c47015ab1e9dfa99f6238df60ff1e70e1498"><code>1ce8c47</code></a>
Release 3.8.5</li>
<li><a
href="https://github.com/prettier/prettier/commit/74923fa8316cd78f8461e77290915155efbcd714"><code>74923fa</code></a>
Fix variance annotation</li>
<li><a
href="https://github.com/prettier/prettier/commit/8b4eb339d43edba35a509ff324d4cdb4416b3efc"><code>8b4eb33</code></a>
Bump Prettier dependency to 3.8.4</li>
<li><a
href="https://github.com/prettier/prettier/commit/e9cbdfa1b755e68ea6f97b0b321524a5169a85d1"><code>e9cbdfa</code></a>
Clean changelog_unreleased</li>
<li>See full diff in <a
href="https://github.com/prettier/prettier/compare/3.8.4...3.8.5">compare
view</a></li>
</ul>
</details>
<br />

Updates `semver` from 7.8.4 to 7.8.5
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/npm/node-semver/releases">semver's
releases</a>.</em></p>
<blockquote>
<h2>v7.8.5</h2>
<h2><a
href="https://github.com/npm/node-semver/compare/v7.8.4...v7.8.5">7.8.5</a>
(2026-06-19)</h2>
<h3>Bug Fixes</h3>
<ul>
<li><a
href="https://github.com/npm/node-semver/commit/9c8692ae05416e9dbe88d95ffe2b80e6964550fe"><code>9c8692a</code></a>
<a href="https://redirect.github.com/npm/node-semver/pull/878">#878</a>
include prereleases in tilde range lower bound with includePrerelease
(<a
href="https://redirect.github.com/npm/node-semver/issues/878">#878</a>)
(<a
href="https://github.com/chatman-media"><code>@​chatman-media</code></a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/npm/node-semver/blob/main/CHANGELOG.md">semver's
changelog</a>.</em></p>
<blockquote>
<h2><a
href="https://github.com/npm/node-semver/compare/v7.8.4...v7.8.5">7.8.5</a>
(2026-06-19)</h2>
<h3>Bug Fixes</h3>
<ul>
<li><a
href="https://github.com/npm/node-semver/commit/9c8692ae05416e9dbe88d95ffe2b80e6964550fe"><code>9c8692a</code></a>
<a href="https://redirect.github.com/npm/node-semver/pull/878">#878</a>
include prereleases in tilde range lower bound with includePrerelease
(<a
href="https://redirect.github.com/npm/node-semver/issues/878">#878</a>)
(<a
href="https://github.com/chatman-media"><code>@​chatman-media</code></a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/npm/node-semver/commit/6e05b7637396ac66522cff8731f07cfe0ef49a29"><code>6e05b76</code></a>
chore: release 7.8.5 (<a
href="https://redirect.github.com/npm/node-semver/issues/879">#879</a>)</li>
<li><a
href="https://github.com/npm/node-semver/commit/9c8692ae05416e9dbe88d95ffe2b80e6964550fe"><code>9c8692a</code></a>
fix: include prereleases in tilde range lower bound with
includePrerelease (#...</li>
<li>See full diff in <a
href="https://github.com/npm/node-semver/compare/v7.8.4...v7.8.5">compare
view</a></li>
</ul>
</details>
<br />

Updates `typescript-eslint` from 8.61.1 to 8.62.0
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/typescript-eslint/typescript-eslint/releases">typescript-eslint's
releases</a>.</em></p>
<blockquote>
<h2>v8.62.0</h2>
<h2>8.62.0 (2026-06-22)</h2>
<h3>🚀 Features</h3>
<ul>
<li>remove redundant package.json &quot;files&quot; (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12444">#12444</a>)</li>
</ul>
<h3>🩹 Fixes</h3>
<ul>
<li>add &quot;files&quot; to rule-schema-to-typescript-types (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12441">#12441</a>)</li>
</ul>
<h3>❤️ Thank You</h3>
<ul>
<li>Kirk Waiblinger <a
href="https://github.com/kirkwaiblinger"><code>@​kirkwaiblinger</code></a></li>
</ul>
<p>See <a
href="https://github.com/typescript-eslint/typescript-eslint/releases/tag/v8.62.0">GitHub
Releases</a> for more information.</p>
<p>You can read about our <a
href="https://typescript-eslint.io/users/versioning">versioning
strategy</a> and <a
href="https://typescript-eslint.io/users/releases">releases</a> on our
website.</p>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/typescript-eslint/CHANGELOG.md">typescript-eslint's
changelog</a>.</em></p>
<blockquote>
<h2>8.62.0 (2026-06-22)</h2>
<h3>🚀 Features</h3>
<ul>
<li>remove redundant package.json &quot;files&quot; (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12444">#12444</a>)</li>
</ul>
<h3>❤️ Thank You</h3>
<ul>
<li>Kirk Waiblinger <a
href="https://github.com/kirkwaiblinger"><code>@​kirkwaiblinger</code></a></li>
</ul>
<p>See <a
href="https://github.com/typescript-eslint/typescript-eslint/releases/tag/v8.62.0">GitHub
Releases</a> for more information.</p>
<p>You can read about our <a
href="https://typescript-eslint.io/users/versioning">versioning
strategy</a> and <a
href="https://typescript-eslint.io/users/releases">releases</a> on our
website.</p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/typescript-eslint/typescript-eslint/commit/54e285728e5d8cb83fadb8041189f0c3b4ab436a"><code>54e2857</code></a>
chore(release): publish 8.62.0</li>
<li><a
href="https://github.com/typescript-eslint/typescript-eslint/commit/81e4c2654c3f4d923766a888691add2c45b5d64a"><code>81e4c26</code></a>
feat: remove redundant package.json &quot;files&quot; (<a
href="https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/typescript-eslint/issues/12444">#12444</a>)</li>
<li>See full diff in <a
href="https://github.com/typescript-eslint/typescript-eslint/commits/v8.62.0/packages/typescript-eslint">compare
view</a></li>
</ul>
</details>
<br />

Updates `@clack/prompts` from 1.5.1 to 1.6.0
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/bombshell-dev/clack/releases">@​clack/prompts's
releases</a>.</em></p>
<blockquote>
<h2><code>@​clack/prompts</code><a
href="https://github.com/1"><code>@​1</code></a>.6.0</h2>
<h3>Minor Changes</h3>
<ul>
<li>
<p><a
href="https://redirect.github.com/bombshell-dev/clack/pull/568">#568</a>
<a
href="https://github.com/bombshell-dev/clack/commit/f87933fb7b3f4c401b9e51a152b95cb8e7200fe5"><code>f87933f</code></a>
Thanks <a
href="https://github.com/florian-lefebvre"><code>@​florian-lefebvre</code></a>!
- Updates default formatter of <code>note()</code> to note dim lines
anymore</p>
<p>If you want the old behavior, provide a <code>format()</code>
function:</p>
<pre lang="diff"><code>import { note } from '@clack/prompts';
+import { styleText } from 'node:util';
<p>note(
'You can edit the file src/index.jsx',
'Next steps.'</p>
<ul>
<li>{ format: (text) =&gt; styleText('dim', text) }
);
</code></pre></li>
</ul>
</li>
<li>
<p><a
href="https://redirect.github.com/bombshell-dev/clack/pull/567">#567</a>
<a
href="https://github.com/bombshell-dev/clack/commit/cc6aab50186cff8a02dc98e9cfd3897c29a33b15"><code>cc6aab5</code></a>
Thanks <a
href="https://github.com/dreyfus92"><code>@​dreyfus92</code></a>! - Add
keyboard instruction footers to <code>select</code>,
<code>multiselect</code>, and <code>groupMultiselect</code> in the
active state, matching autocomplete. No option — always shown.</p>
</li>
</ul>
<h3>Patch Changes</h3>
<ul>
<li>Updated dependencies [<a
href="https://github.com/bombshell-dev/clack/commit/2f2b52f77cbfa9af618c6d929249ab8395fc37a1"><code>2f2b52f</code></a>,
<a
href="https://github.com/bombshell-dev/clack/commit/e1b6ee71a76e17a3c33ba7ee6e5fb34e886233bb"><code>e1b6ee7</code></a>]:
<ul>
<li><code>@​clack/core</code><a
href="https://github.com/1"><code>@​1</code></a>.4.2</li>
</ul>
</li>
</ul>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/bombshell-dev/clack/blob/main/packages/prompts/CHANGELOG.md">@​clack/prompts's
changelog</a>.</em></p>
<blockquote>
<h2>1.6.0</h2>
<h3>Minor Changes</h3>
<ul>
<li>
<p><a
href="https://redirect.github.com/bombshell-dev/clack/pull/568">#568</a>
<a
href="https://github.com/bombshell-dev/clack/commit/f87933fb7b3f4c401b9e51a152b95cb8e7200fe5"><code>f87933f</code></a>
Thanks <a
href="https://github.com/florian-lefebvre"><code>@​florian-lefebvre</code></a>!
- Updates default formatter of <code>note()</code> to note dim lines
anymore</p>
<p>If you want the old behavior, provide a <code>format()</code>
function:</p>
<pre lang="diff"><code>import { note } from '@clack/prompts';
+import { styleText } from 'node:util';
<p>note(
'You can edit the file src/index.jsx',
'Next steps.'</p>
<ul>
<li>{ format: (text) =&gt; styleText('dim', text) }
);
</code></pre></li>
</ul>
</li>
<li>
<p><a
href="https://redirect.github.com/bombshell-dev/clack/pull/567">#567</a>
<a
href="https://github.com/bombshell-dev/clack/commit/cc6aab50186cff8a02dc98e9cfd3897c29a33b15"><code>cc6aab5</code></a>
Thanks <a
href="https://github.com/dreyfus92"><code>@​dreyfus92</code></a>! - Add
keyboard instruction footers to <code>select</code>,
<code>multiselect</code>, and <code>groupMultiselect</code> in the
active state, matching autocomplete. No option — always shown.</p>
</li>
</ul>
<h3>Patch Changes</h3>
<ul>
<li>Updated dependencies [<a
href="https://github.com/bombshell-dev/clack/commit/2f2b52f77cbfa9af618c6d929249ab8395fc37a1"><code>2f2b52f</code></a>,
<a
href="https://github.com/bombshell-dev/clack/commit/e1b6ee71a76e17a3c33ba7ee6e5fb34e886233bb"><code>e1b6ee7</code></a>]:
<ul>
<li><code>@​clack/core</code><a
href="https://github.com/1"><code>@​1</code></a>.4.2</li>
</ul>
</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/bombshell-dev/clack/commit/0e700562dfb54bd693d98c5b7e28d34213fcb226"><code>0e70056</code></a>
[ci] release (<a
href="https://github.com/bombshell-dev/clack/tree/HEAD/packages/prompts/issues/562">#562</a>)</li>
<li><a
href="https://github.com/bombshell-dev/clack/commit/f87933fb7b3f4c401b9e51a152b95cb8e7200fe5"><code>f87933f</code></a>
fix(prompts): do not dim note contents (<a
href="https://github.com/bombshell-dev/clack/tree/HEAD/packages/prompts/issues/568">#568</a>)</li>
<li><a
href="https://github.com/bombshell-dev/clack/commit/cc6aab50186cff8a02dc98e9cfd3897c29a33b15"><code>cc6aab5</code></a>
feat(prompts): add instructions footer for <code>select</code>,
<code>multi-select</code> &amp; `group-...</li>
<li>See full diff in <a
href="https://github.com/bombshell-dev/clack/commits/@clack/prompts@1.6.0/packages/prompts">compare
view</a></li>
</ul>
</details>
<br />

Updates `posthog-node` from 5.35.0 to 5.38.6
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/PostHog/posthog-js/releases">posthog-node's
releases</a>.</em></p>
<blockquote>
<h2>posthog-node@5.38.6</h2>
<h2>5.38.6</h2>
<h3>Patch Changes</h3>
<ul>
<li><a
href="https://redirect.github.com/PostHog/posthog-js/pull/3896">#3896</a>
<a
href="https://github.com/PostHog/posthog-js/commit/606ef43d69fd09a00a67df2a404d8739cc50c654"><code>606ef43</code></a>
Thanks <a
href="https://github.com/GauthierPLM"><code>@​GauthierPLM</code></a>! -
Only stamp $groups from the top-level <code>groups</code> when present
(2026-06-26)</li>
</ul>
<h2>posthog-node@5.38.5</h2>
<h2>5.38.5</h2>
<h3>Patch Changes</h3>
<ul>
<li><a
href="https://redirect.github.com/PostHog/posthog-js/pull/3955">#3955</a>
<a
href="https://github.com/PostHog/posthog-js/commit/8e9689a9018d184b80f32fc4874686d7243970e2"><code>8e9689a</code></a>
Thanks <a
href="https://github.com/marandaneto"><code>@​marandaneto</code></a>! -
Dedupe feature flag called events per returned flag value.
(2026-06-24)</li>
<li>Updated dependencies [<a
href="https://github.com/PostHog/posthog-js/commit/8e9689a9018d184b80f32fc4874686d7243970e2"><code>8e9689a</code></a>]:
<ul>
<li><code>@​posthog/core</code><a
href="https://github.com/1"><code>@​1</code></a>.37.3</li>
</ul>
</li>
</ul>
<h2>posthog-node@5.38.4</h2>
<h2>5.38.4</h2>
<h3>Patch Changes</h3>
<ul>
<li><a
href="https://redirect.github.com/PostHog/posthog-js/pull/3938">#3938</a>
<a
href="https://github.com/PostHog/posthog-js/commit/6391106fd4bcbddb9a63a13338929a4c1918a70a"><code>6391106</code></a>
Thanks <a
href="https://github.com/dustinbyrne"><code>@​dustinbyrne</code></a>! -
Respect <code>disable_capture_url_hashes</code> when capturing Express
and NestJS request URLs.
(2026-06-23)</li>
</ul>
<h2>posthog-node@5.38.3</h2>
<h2>5.38.3</h2>
<h3>Patch Changes</h3>
<ul>
<li><a
href="https://redirect.github.com/PostHog/posthog-js/pull/3937">#3937</a>
<a
href="https://github.com/PostHog/posthog-js/commit/4d59d0e10f92ad3444e14dc171997c21aba57929"><code>4d59d0e</code></a>
Thanks <a
href="https://github.com/marandaneto"><code>@​marandaneto</code></a>! -
Deprecate the Node SDK Sentry integration and point users to PostHog
Error Tracking for Node.
(2026-06-23)</li>
<li>Updated dependencies [<a
href="https://github.com/PostHog/posthog-js/commit/440e370fda48d629352f3280471a228ee973dcb0"><code>440e370</code></a>]:
<ul>
<li><code>@​posthog/core</code><a
href="https://github.com/1"><code>@​1</code></a>.37.0</li>
</ul>
</li>
</ul>
<h2>posthog-node@5.38.2</h2>
<h2>5.38.2</h2>
<h3>Patch Changes</h3>
<ul>
<li><a
href="https://redirect.github.com/PostHog/posthog-js/pull/3903">#3903</a>
<a
href="https://github.com/PostHog/posthog-js/commit/6b21f77291aeea64ce8229eb28196d1acacc20ce"><code>6b21f77</code></a>
Thanks <a
href="https://github.com/marandaneto"><code>@​marandaneto</code></a>! -
Validate custom event UUID overrides and generate new UUIDs when
invalid.
(2026-06-19)</li>
<li>Updated dependencies [<a
href="https://github.com/PostHog/posthog-js/commit/6b21f77291aeea64ce8229eb28196d1acacc20ce"><code>6b21f77</code></a>]:
<ul>
<li><code>@​posthog/core</code><a
href="https://github.com/1"><code>@​1</code></a>.35.3</li>
</ul>
</li>
</ul>
<h2>posthog-node@5.38.1</h2>
<h2>5.38.1</h2>
<h3>Patch Changes</h3>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/PostHog/posthog-js/blob/main/packages/node/CHANGELOG.md">posthog-node's
changelog</a>.</em></p>
<blockquote>
<h2>5.38.6</h2>
<h3>Patch Changes</h3>
<ul>
<li><a
href="https://redirect.github.com/PostHog/posthog-js/pull/3896">#3896</a>
<a
href="https://github.com/PostHog/posthog-js/commit/606ef43d69fd09a00a67df2a404d8739cc50c654"><code>606ef43</code></a>
Thanks <a
href="https://github.com/GauthierPLM"><code>@​GauthierPLM</code></a>! -
Only stamp $groups from the top-level <code>groups</code> when present
(2026-06-26)</li>
</ul>
<h2>5.38.5</h2>
<h3>Patch Changes</h3>
<ul>
<li><a
href="https://redirect.github.com/PostHog/posthog-js/pull/3955">#3955</a>
<a
href="https://github.com/PostHog/posthog-js/commit/8e9689a9018d184b80f32fc4874686d7243970e2"><code>8e9689a</code></a>
Thanks <a
href="https://github.com/marandaneto"><code>@​marandaneto</code></a>! -
Dedupe feature flag called events per returned flag value.
(2026-06-24)</li>
<li>Updated dependencies [<a
href="https://github.com/PostHog/posthog-js/commit/8e9689a9018d184b80f32fc4874686d7243970e2"><code>8e9689a</code></a>]:
<ul>
<li><code>@​posthog/core</code><a
href="https://github.com/1"><code>@​1</code></a>.37.3</li>
</ul>
</li>
</ul>
<h2>5.38.4</h2>
<h3>Patch Changes</h3>
<ul>
<li><a
href="https://redirect.github.com/PostHog/posthog-js/pull/3938">#3938</a>
<a
href="https://github.com/PostHog/posthog-js/commit/6391106fd4bcbddb9a63a13338929a4c1918a70a"><code>6391106</code></a>
Thanks <a
href="https://github.com/dustinbyrne"><code>@​dustinbyrne</code></a>! -
Respect <code>disable_capture_url_hashes</code> when capturing Express
and NestJS request URLs.
(2026-06-23)</li>
</ul>
<h2>5.38.3</h2>
<h3>Patch Changes</h3>
<ul>
<li><a
href="https://redirect.github.com/PostHog/posthog-js/pull/3937">#3937</a>
<a
href="https://github.com/PostHog/posthog-js/commit/4d59d0e10f92ad3444e14dc171997c21aba57929"><code>4d59d0e</code></a>
Thanks <a
href="https://github.com/marandaneto"><code>@​marandaneto</code></a>! -
Deprecate the Node SDK Sentry integration and point users to PostHog
Error Tracking for Node.
(2026-06-23)</li>
<li>Updated dependencies [<a
href="https://github.com/PostHog/posthog-js/commit/440e370fda48d629352f3280471a228ee973dcb0"><code>440e370</code></a>]:
<ul>
<li><code>@​posthog/core</code><a
href="https://github.com/1"><code>@​1</code></a>.37.0</li>
</ul>
</li>
</ul>
<h2>5.38.2</h2>
<h3>Patch Changes</h3>
<ul>
<li><a
href="https://redirect.github.com/PostHog/posthog-js/pull/3903">#3903</a>
<a
href="https://github.com/PostHog/posthog-js/commit/6b21f77291aeea64ce8229eb28196d1acacc20ce"><code>6b21f77</code></a>
Thanks <a
href="https://github.com/marandaneto"><code>@​marandaneto</code></a>! -
Validate custom event UUID overrides and generate new UUIDs when
invalid.
(2026-06-19)</li>
<li>Updated dependencies [<a
href="https://github.com/PostHog/posthog-js/commit/6b21f77291aeea64ce8229eb28196d1acacc20ce"><code>6b21f77</code></a>]:
<ul>
<li><code>@​posthog/core</code><a
href="https://github.com/1"><code>@​1</code></a>.35.3</li>
</ul>
</li>
</ul>
<h2>5.38.1</h2>
<h3>Patch Changes</h3>
<ul>
<li><a
href="https://redirect.github.com/PostHog/posthog-js/pull/3886">#3886</a>
<a
href="https://github.com/PostHog/posthog-js/commit/e6d7fe2a5f10d29b3df69392f584970e7a7a4561"><code>e6d7fe2</code></a>
Thanks <a
href="https://github.com/marandaneto"><code>@​marandaneto</code></a>! -
Stop sending deprecated no-op top-level <code>type</code>,
<code>library</code>, and <code>library_version</code> fields in event
batch payloads. Use <code>properties.$lib</code> and
<code>properties.$lib_version</code> for SDK metadata; legacy queued
<code>library</code> and <code>library_version</code> values are used as
fallbacks when the official <code>$</code> properties are missing.
(2026-06-18)</li>
<li>Updated dependencies [<a
href="https://github.com/PostHog/posthog-js/commit/e6d7fe2a5f10d29b3df69392f584970e7a7a4561"><code>e6d7fe2</code></a>]:
<ul>
<li><code>@​posthog/core</code><a
href="https://github.com/1"><code>@​1</code></a>.35.2</li>
</ul>
</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/PostHog/posthog-js/commit/d2faa824af1af2cb50b47178fb953bfc8812b389"><code>d2faa82</code></a>
chore: update versions and lockfile [version bump]</li>
<li><a
href="https://github.com/PostHog/posthog-js/commit/606ef43d69fd09a00a67df2a404d8739cc50c654"><code>606ef43</code></a>
fix: preserve $groups passed via properties (<a
href="https://github.com/PostHog/posthog-js/tree/HEAD/packages/node/issues/3896">#3896</a>)</li>
<li><a
href="https://github.com/PostHog/posthog-js/commit/5c4838f1671ece80d153aaeb6d946bb3e8c8240a"><code>5c4838f</code></a>
chore: update versions and lockfile [version bump]</li>
<li><a
href="https://github.com/PostHog/posthog-js/commit/8e9689a9018d184b80f32fc4874686d7243970e2"><code>8e9689a</code></a>
fix: Dedupe feature flag called events by response (<a
href="https://github.com/PostHog/posthog-js/tree/HEAD/packages/node/issues/3955">#3955</a>)</li>
<li><a
href="https://github.com/PostHog/posthog-js/commit/2a60c21134dd118c2367e56129bc9427079c3841"><code>2a60c21</code></a>
chore: update versions and lockfile [version bump]</li>
<li><a
href="https://github.com/PostHog/posthog-js/commit/6391106fd4bcbddb9a63a13338929a4c1918a70a"><code>6391106</code></a>
fix(node): respect URL hash capture config (<a
href="https://github.com/PostHog/posthog-js/tree/HEAD/packages/node/issues/3938">#3938</a>)</li>
<li><a
href="https://github.com/PostHog/posthog-js/commit/a3136c6de3114ee3d58e59a0c86f2b9c4f59a2b0"><code>a3136c6</code></a>
chore: update versions and lockfile [version bump]</li>
<li><a
href="https://github.com/PostHog/posthog-js/commit/bf6947d3e12ac512c99185be9b8b134c04eb563a"><code>bf6947d</code></a>
feat(react-native): support session replay event triggers (<a
href="https://github.com/PostHog/posthog-js/tree/HEAD/packages/node/issues/3932">#3932</a>)</li>
<li><a
href="https://github.com/PostHog/posthog-js/commit/2628a1af5f06f73cb2252de002e0da4b2d1a25a5"><code>2628a1a</code></a>
chore: update versions and lockfile [version bump]</li>
<li><a
href="https://github.com/PostHog/posthog-js/commit/4d59d0e10f92ad3444e14dc171997c21aba57929"><code>4d59d0e</code></a>
chore(node): mark Sentry integration deprecated (<a
href="https://github.com/PostHog/posthog-js/tree/HEAD/packages/node/issues/3937">#3937</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/PostHog/posthog-js/commits/posthog-node@5.38.6/packages/node">compare
view</a></li>
</ul>
</details>
<br />

Updates `smol-toml` from 1.6.1 to 1.7.0
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/squirrelchat/smol-toml/releases">smol-toml's
releases</a>.</em></p>
<blockquote>
<h2>v1.7.0</h2>
<p>This version slightly changes the behaviour of stringify: integers
beyond the safe range are always emitted as float numbers.</p>
<p>String decode logic has been rewritten, it is a bit faster now and
uses a single-pass approach instead of a dual-pass approach as it did
previously. The code should be a bit smaller too, though I didn't
actually measure that.</p>
<p>The package is now published with source-maps, declaration-maps, and
a copy of the original TypeScript source files. This will improve your
DX if you're like me and like Ctrl+Click'ing things a lot. ;)</p>
<h2>What's Changed</h2>
<ul>
<li>improve performance of the string decode logic by <a
href="https://github.com/cyyynthia"><code>@​cyyynthia</code></a></li>
<li>fix(stringify): emit integer-valued numbers beyond the safe-integer
range as floats by <a
href="https://github.com/spokodev"><code>@​spokodev</code></a> in <a
href="https://redirect.github.com/squirrelchat/smol-toml/pull/56">squirrelchat/smol-toml#56</a></li>
<li>publish source-maps, declaration-maps, and original source files by
<a href="https://github.com/cyyynthia"><code>@​cyyynthia</code></a></li>
<li>miscellaneous repository maintenance tasks by <a
href="https://github.com/cyyynthia"><code>@​cyyynthia</code></a></li>
</ul>
<h2>New Contributors</h2>
<ul>
<li><a href="https://github.com/spokodev"><code>@​spokodev</code></a>
made their first contribution in <a
href="https://redirect.github.com/squirrelchat/smol-toml/pull/56">squirrelchat/smol-toml#56</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/squirrelchat/smol-toml/compare/v1.6.1...v1.7.0">https://github.com/squirrelchat/smol-toml/compare/v1.6.1...v1.7.0</a></p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/squirrelchat/smol-toml/commit/a62f06faf4e45ab0a581def86c116f9706edc9df"><code>a62f06f</code></a>
revert: keep using vite 7</li>
<li><a
href="https://github.com/squirrelchat/smol-toml/commit/89aa9a394ffc544dce77ab0ff24dfd8204e1f091"><code>89aa9a3</code></a>
chore: remove prepare script</li>
<li><a
href="https://github.com/squirrelchat/smol-toml/commit/17c797475f1516da6614e31a1be9f4958bf296c0"><code>17c7974</code></a>
chore: make devEngine more lax w/ node version</li>
<li><a
href="https://github.com/squirrelchat/smol-toml/commit/e5280a34630eb25086dca746bedda28ea21ec3d9"><code>e5280a3</code></a>
ci: checkout repo first</li>
<li><a
href="https://github.com/squirrelchat/smol-toml/commit/241c256ebda07b2d225c91b7468ed8c90d0e71df"><code>241c256</code></a>
chore: version bump</li>
<li><a
href="https://github.com/squirrelchat/smol-toml/commit/0bfe7f4667fa46122663b7b6114d9514dbd1a91f"><code>0bfe7f4</code></a>
chore: build cjs with rolldown instead of esbuild</li>
<li><a
href="https://github.com/squirrelchat/smol-toml/commit/e0620ab2ac89c40d291f33df41b110277b1acedf"><code>e0620ab</code></a>
chore: fmt</li>
<li><a
href="https://github.com/squirrelchat/smol-toml/commit/96114cb58312084286ae61470ba0f017b87b8023"><code>96114cb</code></a>
test: add tests for large integers</li>
<li><a
href="https://github.com/squirrelchat/smol-toml/commit/f4537b6b3763e761069efdd7d688711c8409645c"><code>f4537b6</code></a>
fix: handle missed edge-cases in string parse</li>
<li><a
href="https://github.com/squirrelchat/smol-toml/commit/7b39aed388cdc811189a6a3683fbee2f8d344244"><code>7b39aed</code></a>
chore: include source files in published package</li>
<li>Additional commits viewable in <a
href="https://github.com/squirrelchat/smol-toml/compare/v1.6.1...v1.7.0">compare
view</a></li>
</ul>
</details>
<br />

Updates `electron` from 42.4.1 to 42.5.0
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/electron/electron/releases">electron's
releases</a>.</em></p>
<blockquote>
<h2>electron v42.5.0</h2>
<h1>Release Notes for v42.5.0</h1>
<h2>Fixes</h2>
<ul>
<li>Fixed Windows edge case in
<code>NativeWindowViews::MoveBehindTaskBarIfNeeded()</code>. <a
href="https://redirect.github.com/electron/electron/pull/52022">#52022</a>
<!-- raw HTML omitted -->(Also in <a
href="https://redirect.github.com/electron/electron/pull/52023">41</a>,
<a
href="https://redirect.github.com/electron/electron/pull/52021">43</a>)<!--
raw HTML omitted --></li>
<li>Fixed an issue where clicking the maximize button could
progressively shrink the window in some Wayland environments. <a
href="https://redirect.github.com/electron/electron/pull/52045">#52045</a>
<!-- raw HTML omitted -->(Also in <a
href="https://redirect.github.com/electron/electron/pull/52039">43</a>)<!--
raw HTML omitted --></li>
</ul>
<h2>Other Changes</h2>
<ul>
<li>Backported fixes from upstream Chromium. <a
href="https://redirect.github.com/electron/electron/pull/51975">#51975</a></li>
<li>Updated Chromium to 148.0.7778.271. <a
href="https://redirect.github.com/electron/electron/pull/52038">#52038</a></li>
<li>Updated Node.js to v24.17.0. <a
href="https://redirect.github.com/electron/electron/pull/52066">#52066</a></li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/electron/electron/commit/d7bccf5b2f27969b4e7f34cf877f55cf0813be7a"><code>d7bccf5</code></a>
fix: UAF in api::UtilityProcessWrapper (<a
href="https://redirect.github.com/electron/electron/issues/52094">#52094</a>)</li>
<li><a
href="https://github.com/electron/electron/commit/fb151dcdf1ef7685cb5254b571d9ec50a480c1ae"><code>fb151dc</code></a>
chore: bump chromium to 148.0.7778.271 (42-x-y) (<a
href="https://redirect.github.com/electron/electron/issues/52038">#52038</a>)</li>
<li><a
href="https://github.com/electron/electron/commit/526fa0df025b50aea9a8011efa52753db8e7839a"><code>526fa0d</code></a>
chore: bump node to v24.17.0 (42-x-y) (<a
href="https://redirect.github.com/electron/electron/issues/52066">#52066</a>)</li>
<li><a
href="https://github.com/electron/electron/commit/22b84b5c304c512005c55569f6344d3e0db12fad"><code>22b84b5</code></a>
fix: frameless window shrinks on maximize() when compositor declines (<a
href="https://redirect.github.com/electron/electron/issues/52045">#52045</a>)</li>
<li><a
href="https://github.com/electron/electron/commit/15e8d1cf5db75ed5db00d2f4889deca1ee2c1004"><code>15e8d1c</code></a>
docs: fix heap dump screenshot in contentTracing (<a
href="https://redirect.github.com/electron/electron/issues/52031">#52031</a>)</li>
<li><a
href="https://github.com/electron/electron/commit/8902c75dca77c80793fe69971d53b7226d921bf9"><code>8902c75</code></a>
build: bump <code>@​electron/docs-parser</code> to 3.0.1 (<a
href="https://redirect.github.com/electron/electron/issues/52052">#52052</a>)</li>
<li><a
href="https://github.com/electron/electron/commit/30bf7db164d941ee167cbc2f42f7629b1a6fe452"><code>30bf7db</code></a>
test: use node16 moduleResolution in tsconfig and clean up imports (<a
href="https://redirect.github.com/electron/electron/issues/52040">#52040</a>)</li>
<li><a
href="https://github.com/electron/electron/commit/d251baeeb3b0c92a66eaca6ab76542b4bb02e46a"><code>d251bae</code></a>
ci: bump linux 64k kernel version (<a
href="https://redirect.github.com/electron/electron/issues/52037">#52037</a>)</li>
<li><a
href="https://github.com/electron/electron/commit/ad73d520595a149149ef60bfbee16fd520b68ee7"><code>ad73d52</code></a>
fix: win: check for valid taskbar handle (<a
href="https://redirect.github.com/electron/electron/issues/52022">#52022</a>)</li>
<li><a
href="https://github.com/electron/electron/commit/6abd0348c9ec3fdd8eef4ca4029fbd8747000815"><code>6abd034</code></a>
ci: replace dorny/paths-filter usage (reland) (<a
href="https://redirect.github.com/electron/electron/issues/52033">#52033</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/electron/electron/compare/v42.4.1...v42.5.0">compare
view</a></li>
</ul>
</details>
<br />

Updates `fast-xml-parser` from 5.9.2 to 5.9.3
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/NaturalIntelligence/fast-xml-parser/releases">fast-xml-parser's
releases</a>.</em></p>
<blockquote>
<h2>v5.9.3</h2>
<h2>What's Changed</h2>
<ul>
<li>Harden GitHub Actions workflows by <a
href="https://github.com/martincostello"><code>@​martincostello</code></a>
in <a
href="https://redirect.github.com/NaturalIntelligence/fast-xml-parser/pull/841">NaturalIntelligence/fast-xml-parser#841</a></li>
<li>GitHub Actions workflow fixes by <a
href="https://github.com/martincostello"><code>@​martincostello</code></a>
in <a
href="https://redirect.github.com/NaturalIntelligence/fast-xml-parser/pull/844">NaturalIntelligence/fast-xml-parser#844</a></li>
</ul>
<h2>New Contributors</h2>
<ul>
<li><a
href="https://github.com/martincostello"><code>@​martincostello</code></a>
made their first contribution in <a
href="https://redirect.github.com/NaturalIntelligence/fast-xml-parser/pull/841">NaturalIntelligence/fast-xml-parser#841</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/NaturalIntelligence/fast-xml-parser/compare/v5.9.2...v5.9.3">https://github.com/NaturalIntelligence/fast-xml-parser/compare/v5.9.2...v5.9.3</a></p>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/NaturalIntelligence/fast-xml-parser/blob/master/CHANGELOG.md">fast-xml-parser's
changelog</a>.</em></p>
<blockquote>
<p><!-- raw HTML omitted -->Note: If you find missing information about
particular minor version, that version must have been changed without
any functional change in this library.<!-- raw HTML omitted --></p>
<p>Note: Due to some last quick changes on v4, detail of v4.5.3 &amp;
v4.5.4 are not updated here. v4.5.4x is the last tag of v4 in github
repository. I'm extremely sorry for the confusion</p>
<p>*<em>5.9.3 / 2026-06-19</em></p>
<ul>
<li>update strnum</li>
</ul>
<p>*<em>5.9.2 / 2026-06-17</em></p>
<ul>
<li>dummy release to test changes in github action</li>
</ul>
<p>*<em>5.9.1 / 2026-06-17</em></p>
<ul>
<li>dummy release to test release from github action</li>
</ul>
<p>*<em>5.9.0 / 2026-06-15</em></p>
<ul>
<li>update strnum to 2.3.0
<ul>
<li>you can set hex, binary, enotation, infinity, unicode</li>
</ul>
</li>
<li>validate unsafe HTML or XML data in doctype entities unsing
'is-unsafe' library.
User can override rules by overriding EntityDecoder.</li>
</ul>
<p>*<em>5.8.0 / 2026-05-12</em></p>
<ul>
<li>integrate xml-naming to validate DOCTYPE entity name and notation
name (using qname becaue of backward compatibility)
<ul>
<li>This will consider xml-version as well. '1.0' is default</li>
</ul>
</li>
<li>update strnum to 2.3.0
<ul>
<li>You can set octal and binary parsing which is bydeault off</li>
</ul>
</li>
<li>update fast-xml-builder to 1.2.0
<ul>
<li>can sanitize tag names if found invalid</li>
<li>fix format output</li>
</ul>
</li>
</ul>
<p><strong>5.7.3 / 2006-05-05</strong></p>
<ul>
<li>fix: alwaysCreateTextNode should create text node when attributes
are present for self closing node</li>
<li>fix stop node expression when ns prefix is removed (found by <a
href="https://github.com/iruizsalinas">iruizsalinas</a>)</li>
<li>update XML Builder to 1.1.7</li>
<li>mark addEntity deprecated</li>
</ul>
<p><strong>5.7.2 / 2026-04-25</strong></p>
<ul>
<li>allow numerical external entity for backward compatibility</li>
<li>fix <a
href="https://redirect.github.com/NaturalIntelligence/fast-xml-parser/issues/705">#705</a>:
attributesGroupName working with preserveOrder</li>
<li>fix <a
href="https://redirect.github.com/NaturalIntelligence/fast-xml-parser/issues/817">#817</a>:
stackoverflow when tag expression is very long</li>
</ul>
<p><strong>5.7.1 / 2026-04-20</strong></p>
<ul>
<li>fix typo in CJS typing file</li>
</ul>
<p><strong>5.7.0 / 2026-04-17</strong></p>
<ul>
<li>Use <code>@nodable/entities</code> v2.1.0
<ul>
<li>breaking changes
<ul>
<li>single entity scan. You're not allowed to user entity value to form
another entity name.</li>
<li>you cant add numeric external entity</li>
<li>entity error message when expantion limit is crossed might
change</li>
</ul>
</li>
</ul>
</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/NaturalIntelligence/fast-xml-parser/commit/93a09f108d6f457a84e607a6806a7bfd6abc3e55"><code>93a09f1</code></a>
5.9.3</li>
<li><a
href="https://github.com/NaturalIntelligence/fast-xml-parser/commit/9c1905edd7ea28db70770f758e293fcf079060c5"><code>9c1905e</code></a>
update strnum</li>
<li><a
href="https://github.com/NaturalIntelligence/fast-xml-parser/commit/8d71292a9aaaa8f4a24294e636bd191cfbf4d8a7"><code>8d71292</code></a>
GitHub Actions workflow fixes (<a
href="https://redirect.github.com/NaturalIntelligence/fast-xml-parser/issues/844">#844</a>)</li>
<li><a
href="https://github.com/NaturalIntelligence/fast-xml-parser/commit/784a0442b697e1498e1f3c3fc1a057ad57424858"><code>784a044</code></a>
Harden GitHub Actions workflows (<a
href="https://redirect.github.com/NaturalIntelligence/fast-xml-parser/issues/841">#841</a>)</li>
<li>See full diff in <a
href="https://github.com/NaturalIntelligence/fast-xml-parser/compare/v5.9.2...v5.9.3">compare
view</a></li>
</ul>
</details>
<br />

Updates `posthog-node` from 5.35.0 to 5.38.6
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/PostHog/posthog-js/releases">posthog-node's
releases</a>.</em></p>
<blockquote>
<h2>posthog-node@5.38.6</h2>
<h2>5.38.6</h2>
<h3>Patch Changes</h3>
<ul>
<li><a
href="https://redirect.github.com/PostHog/posthog-js/pull/3896">#3896</a>
<a
href="https://github.com/PostHog/posthog-js/commit/606ef43d69fd09a00a67df2a404d8739cc50c654"><code>606ef43</code></a>
Thanks <a
href="https://github.com/GauthierPLM"><code>@​GauthierPLM</code></a>! -
Only stamp $groups from the top-level <code>groups</code> when present
(2026-06-26)</li>
</ul>
<h2>posthog-node@5.38.5</h2>
<h2>5.38.5</h2>
<h3>Patch Changes</h3>
<ul>
<li><a
href="https://redirect.github.com/PostHog/posthog-js/pull/3955">#3955</a>
<a
href="https://github.com/PostHog/posthog-js/commit/8e9689a9018d184b80f32fc4874686d7243970e2"><code>8e9689a</code></a>
Thanks <a
href="https://github.com/marandaneto"><code>@​marandaneto</code></a>! -
Dedupe feature flag called events per returned flag value.
(2026-06-24)</li>
<li>Updated dependencies [<a
href="https://github.com/PostHog/posthog-js/commit/8e9689a9018d184b80f32fc4874686d7243970e2"><code>8e9689a</code></a>]:
<ul>
<li><code>@​posthog/core</code><a
href="https://github.com/1"><code>@​1</code></a>.37.3</li>
</ul>
</li>
</ul>
<h2>posthog-node@5.38.4</h2>
<h2>5.38.4</h2>
<h3>Patch Changes</h3>
<ul>
<li><a
href="https://redirect.github.com/PostHog/posthog-js/pull/3938">#3938</a>
<a
href="https://github.com/PostHog/posthog-js/commit/6391106fd4bcbddb9a63a13338929a4c1918a70a"><code>6391106</code></a>
Thanks <a
href="https://github.com/dustinbyrne"><code>@​dustinbyrne</code></a>! -
Respect <code>disable_capture_url_hashes</code> when capturing Express
and NestJS request URLs.
(2026-06-23)</li>
</ul>
<h2>posthog-node@5.38.3</h2>
<h2>5.38.3</h2>
<h3>Patch Changes</h3>
<ul>
<li><a
href="https://redirect.github.com/PostHog/posthog-js/pull/3937">#3937</a>
<a
href="https://github.com/PostHog/posthog-js/commit/4d59d0e10f92ad3444e14dc171997c21aba57929"><code>4d59d0e</code></a>
Thanks <a
href="https://github.com/marandaneto"><code>@​marandaneto</code></a>! -
Deprecate the Node SDK Sentry integration and point users to PostHog
Error Tracking for Node.
(2026-06-23)</li>
<li>Updated dependencies [<a
href="https://github.com/PostHog/posthog-js/commit/440e370fda48d629352f3280471a228ee973dcb0"><code>440e370</code></a>]:
<ul>
<li><code>@​posthog/core</code><a
href="https://github.com/1"><code>@​1</code></a>.37.0</li>
</ul>
</li>
</ul>
<h2>posthog-node@5.38.2</h2>
<h2>5.38.2</h2>
<h3>Patch Changes</h3>
<ul>
<li><a
href="https://redirect.github.com/PostHog/posthog-js/pull/3903">#3903</a>
<a
href="https://github.com/PostHog/posthog-js/commit/6b21f77291aeea64ce8229eb28196d1acacc20ce"><code>6b21f77</code></a>
Thanks <a
href="https://github.com/marandaneto"><code>@​marandaneto</code></a>! -
Validate custom event UUID overrides and generate new UUIDs when
invalid.
(2026-06-19)</li>
<li>Updated dependencies [<a
href="https://github.com/PostHog/posthog-js/commit/6b21f77291aeea64ce8229eb28196d1acacc20ce"><code>6b21f77</code></a>]:
<ul>
<li><code>@​posthog/core</code><a
href="https://github.com/1"><code>@​1</code></a>.35.3</li>
</ul>
</li>
</ul>
<h2>posthog-node@5.38.1</h2>
<h2>5.38.1</h2>
<h3>Patch Changes</h3>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/PostHog/posthog-js/blob/main/packages/node/CHANGELOG.md">posthog-node's
changelog</a>.</em></p>
<blockquote>
<h2>5.38.6</h2>
<h3>Patch Changes</h3>
<ul>
<li><a
href="https://redirect.github.com/PostHog/posthog-js/pull/3896">#3896</a>
<a
href="https://github.com/PostHog/posthog-js/commit/606ef43d69fd09a00a67df2a404d8739cc50c654"><code>606ef43</code></a>
Thanks <a
href="https://github.com/GauthierPLM"><code>@​GauthierPLM</code></a>! -
Only stamp $groups from the top-level <code>groups</code> when present
(2026-06-26)</li>
</ul>
<h2>5.38.5</h2>
<h3>Patch Changes</h3>
<ul>
<li><a
href="https://redirect.github.com/PostHog/posthog-js/pull/3955">#3955</a>
<a
href="https://github.com/PostHog/posthog-js/commit/8e9689a9018d184b80f32fc4874686d7243970e2"><code>8e9689a</code></a>
Thanks <a
href="https://github.com/marandaneto"><code>@​marandaneto</code></a>! -
Dedupe feature flag called events per returned flag value.
(2026-06-24)</li>
<li>Updated dependencies [<a
href="https://github.com/PostHog/posthog-js/commit/8e9689a9018d184b80f32fc4874686d7243970e2"><code>8e9689a</code></a>]:
<ul>
<li><code>@​posthog/core</code><a
href="https://github.com/1"><code>@​1</code></a>.37.3</li>
</ul>
</li>
</ul>
<h2>5.38.4</h2>
<h3>Patch Changes</h3>
<ul>
<li><a
href="https://redirect.github.com/PostHog/posthog-js/pull/3938">#3938</a>
<a
href="https://github.com/PostHog/posthog-js/commit/6391106fd4bcbddb9a63a13338929a4c1918a70a"><code>6391106</code></a>
Thanks <a
href="https://github.com/dustinbyrne"><code>@​dustinbyrne</code></a>! -
Respect <code>disable_capture_url_hashes</code> when capturing Express
and NestJS request URLs.
(2026-06-23)</li>
</ul>
<h2>5.38.3</h2>
<h3>Patch Changes</h3>
<ul>
<li><a
href="https://redirect.github.com/PostHog/posthog-js/pull/3937">#3937</a>
<a
href="https://github.com/PostHog/posthog-js/commit/4d59d0e10f92ad3444e14dc171997c21aba57929"><code>4d59d0e</code></a>
Thanks <a
href="https://github.com/marandaneto"><code>@​marandaneto</code></a>! -
Deprecate the Node SDK Sentry integration and point users to PostHog
Error Tracking for Node.
(2026-06-23)</li>
<li>Updated dependencies [<a
href="https://github.com/PostHog/posthog-js/commit/440e370fda48d629352f3280471a228ee973dcb0"><code>440e370</code></a>]:
<ul>
<li><code>@​posthog/core</code><a
href="https://github.com/1"><code>@​1</code></a>.37.0</li>
</ul>
</li>
</ul>
<h2>5.38.2</h2>
<h3>Patch Changes</h3>
<ul>
<li><a
href="https://redirect.github.com/PostHog/posthog-js/pull/3903">#3903</a>
<a
href="https://github.com/PostHog/posthog-js/commit/6b21f77291aeea64ce8229eb28196d1acacc20ce"><code>6b21f77</code></a>
Thanks <a
href="https://github.com/marandaneto"><code>@​marandaneto</code></a>! -
Validate custom event UUID overrides and generate new UUIDs when
invalid.
(2026-06-19)</li>
<li>Updated dependencies [<a
href="https://github.com/PostHog/posthog-js/commit/6b21f77291aeea64ce8229eb28196d1acacc20ce"><code>6b21f77</code></a>]:
<ul>
<li><code>@​posthog/core</code><a
href="https://github.com/1"><code>@​1</code></a>.35.3</li>
</ul>
</li>
</ul>
<h2>5.38.1</h2>
<h3>Patch Changes</h3>
<ul>
<li><a
href="https://redirect.github.com/PostHog/posthog-js/pull/3886">#3886</a>
<a
href="https://github.com/PostHog/posthog-js/commit/e6d7fe2a5f10d29b3df69392f584970e7a7a4561"><code>e6d7fe2</code></a>
Thanks <a
href="https://github.com/marandaneto"><code>@​marandaneto</code></a>! -
Stop sending deprecated no-op top-level <code>type</code>,
<code>library</code>, and <code>library_version</code> fields in event
batch payloads. Use <code>properties.$lib</code> and
<code>properties.$lib_version</code> for SDK metadata; legacy queued
<code>library</code> and <code>library_version</code> values are used as
fallbacks when the official <code>$</code> properties are missing.
(2026-06-18)</li>
<li>Updated dependencies [<a
href="https://github.com/PostHog/posthog-js/commit/e6d7fe2a5f10d29b3df69392f584970e7a7a4561"><code>e6d7fe2</code></a>]:
<ul>
<li><code>@​posthog/core</code><a
href="https://github.com/1"><code>@​1</code></a>.35.2</li>
</ul>
</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/PostHog/posthog-js/commit/d2faa824af1af2cb50b47178fb953bfc8812b389"><code>d2faa82</code></a>
chore: update versions and lockfile [version bump]</li>
<li><a
href="https://github.com/PostHog/posthog-js/commit/606ef43d69fd09a00a67df2a404d8739cc50c654"><code>606ef43</code></a>
fix: preserve $groups passed via properties (<a
href="https://github.com/PostHog/posthog-js/tree/HEAD/packages/node/issues/3896">#3896</a>)</li>
<li><a
href="https://github.com/PostHog/posthog-js/commit/5c4838f1671ece80d153aaeb6d946bb3e8c8240a"><code>5c4838f</code></a>
chore: update versions and lockfile [version bump]</li>
<li><a
href="https://github.com/PostHog/posthog-js/commit/8e9689a9018d184b80f32fc4874686d7243970e2"><code>8e9689a</code></a>
fix: Dedupe feature flag called events by response (<a
href="https://github.com/PostHog/posthog-js/tree/HEAD/packages/node/issues/3955">#3955</a>)</li>
<li><a
href="https://github.com/PostHog/posthog-js/commit/2a60c21134dd118c2367e56129bc9427079c3841"><code>2a60c21</code></a>
chore: update versions and lockfile [version bump]</li>
<li><a
href="https://github.com/PostHog/posthog-js/commit/6391106fd4bcbddb9a63a13338929a4c1918a70a"><code>6391106</code></a>
fix(node): respect URL hash capture config (<a
href="https://github.com/PostHog/posthog-js/tree/HEAD/packages/node/issues/3938">#3938</a>)</li>
<li><a
href="https://github.com/PostHog/posthog-js/commit/a3136c6de3114ee3d58e59a0c86f2b9c4f59a2b0"><code>a3136c6</code></a>
chore: update versions and lockfile [version bump]</li>
<li><a
href="https://github.com/PostHog/posthog-js/commit/bf6947d3e12ac512c99185be9b8b134c04eb563a"><code>bf6947d</code></a>
feat(react-native): support session replay event triggers (<a
href="https://github.com/PostHog/posthog-js/tree/HEAD/packages/node/issues/3932">#3932</a>)</li>
<li><a
href="https://github.com/PostHog/posthog-js/commit/2628a1af5f06f73cb2252de002e0da4b2d1a25a5"><code>2628a1a</code></a>
chore: update versions and lockfile [version bump]</li>
<li><a
href="https://github.com/PostHog/posthog-js/commit/4d59d0e10f92ad3444e14dc171997c21aba57929"><code>4d59d0e</code></a>
chore(node): mark Sentry integration deprecated (<a
href="https://github.com/PostHog/posthog-js/tree/HEAD/packages/node/issues/3937">#3937</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/PostHog/posthog-js/commits/posthog-node@5.38.6/packages/node">compare
view</a></li>
</ul>
</details>
<br />

Updates `posthog-node` from 5.35.0 to 5.38.6
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/PostHog/posthog-js/releases">posthog-node's
releases</a>.</em></p>
<blockquote>
<h2>posthog-node@5.38.6</h2>
<h2>5.38.6</h2>
<h3>Patch Changes</h3>
<ul>
<li><a
href="https://redirect.github.com/PostHog/posthog-js/pull/3896">#3896</a>
<a
href="https://github.com/PostHog/posthog-js/commit/606ef43d69fd09a00a67df2a404d8739cc50c654"><code>606ef43</code></a>
Thanks <a
href="https://github.com/GauthierPLM"><code>@​GauthierPLM</code></a>! -
Only stamp $groups from the top-level <code>groups</code> when present
(2026-06-26)</li>
</ul>
<h2>posthog-node@5.38.5</h2>
<h2>5.38.5</h2>
<h3>Patch Changes</h3>
<ul>
<li><a
href="https://redirect.github.com/PostHog/posthog-js/pull/3955">#3955</a>
<a
href="https://github.com/PostHog/posthog-js/commit/8e9689a9018d184b80f32fc4874686d7243970e2"><code>8e9689a</code></a>
Thanks <a
href="https://github.com/marandaneto"><code>@​marandaneto</code></a>! -
Dedupe feature flag called events per returned flag value.
(2026-06-24)</li>
<li>Updated dependencies [<a
href="https://github.com/PostHog/posthog-js/commit/8e9689a9018d184b80f32fc4874686d7243970e2"><code>8e9689a</code></a>]:
<ul>
<li><code>@​posthog/core</code><a
href="https://github.com/1"><code>@​1</code></a>.37.3</li>
</ul>
</li>
</ul>
<h2>posthog-node@5.38.4</h2>
<h2>5.38.4</h2>
<h3>Patch Changes</h3>
<ul>
<li><a
href="https://redirect.github.com/PostHog/posthog-js/pull/3938">#3938</a>
<a
href="https://github.com/PostHog/posthog-js/commit/6391106fd4bcbddb9a63a13338929a4c1918a70a"><code>6391106</code></a>
Thanks <a
href="https://github.com/dustinbyrne"><code>@​dustinbyrne</code></a>! -
Respect <code>disable_capture_url_hashes</code> when capturing Express
and NestJS request URLs.
(2026-06-23)</li>
</ul>
<h2>posthog-node@5.38.3</h2>
<h2>5.38.3</h2>
<h3>Patch Changes</h3>
<ul>
<li><a
href="https://redirect.github.com/PostHog/posthog-js/pull/3937">#3937</a>
<a
href="https://github.com/PostHog/posthog-js/commit/4d59d0e10f92ad3444e14dc171997c21aba57929"><code>4d59d0e</code></a>
Thanks <a
href="https://github.com/marandaneto"><code>@​marandaneto</code></a>! -
Deprecate the Node SDK Sentry integration and point users to PostHog
Error Tracking for Node.
(2026-06-23)</li>
<li>Updated dependencies [<a
href="https://github.com/PostHog/posthog-js/commit/440e370fda48d629352f3280471a228ee973dcb0"><code>440e370</code></a>]:
<ul>
<li><code>@​posthog/core</code><a
href="https://github.com/1"><code>@​1</code></a>.37.0</li>
</ul>
</li>
</ul>
<h2>posthog-node@5.38.2</h2>
<h2>5.38.2</h2>
<h3>Patch Changes</h3>
<ul>
<li><a
href="https://redirect.github.com/PostHog/posthog-js/pull/3903">#3903</a>
<a
href="https://github.com/PostHog/posthog-js/commit/6b21f77291aeea64ce8229eb28196d1acacc20ce"><code>6b21f77</code></a>
Thanks <a
href="https://github.com/marandaneto"><code>@​marandaneto</code></a>! -
Validate custom event UUID overrides and generate new UUIDs when
invalid.
(2026-06-19)</li>
<li>Updated dependencies [<a
href="https://github.com/PostHog/posthog-js/commit/6b21f77291aeea64ce8229eb28196d1acacc20ce"><code>6b21f77</code></a>]:
<ul>
<li><code>@​posthog/core</code><a
href="https://github.com/1"><code>@​1</code></a>.35.3</li>
</ul>
</li>
</ul>
<h2>posthog-node@5.38.1</h2>
<h2>5.38.1</h2>
<h3>Patch Changes</h3>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/PostHog/posthog-js/blob/main/packages/node/CHANGELOG.md">posthog-node's
changelog</a>.</em></p>
<blockquote>
<h2>5.38.6</h2>
<h3>Patch Changes</h3>
<ul>
<li><a
href="https://redirect.github.com/PostHog/posthog-js/pull/3896">#3896</a>
<a
href="https://github.com/PostHog/posthog-js/commit/606ef43d69fd09a00a67df2a404d8739cc50c654"><code>606ef43</code></a>
Thanks <a
href="https://github.com/GauthierPLM"><code>@​GauthierPLM</code></a>! -
Only stamp $groups from the top-level <code>groups</code> when present
(2026-06-26)</li>
</ul>
<h2>5.38.5</h2>
<h3>Patch Changes</h3>
<ul>
<li><a
href="https://redirect.github.com/PostHog/posthog-js/pull/3955">#3955</a>
<a
href="https://github.com/PostHog/posthog-js/commit/8e9689a9018d184b80f32fc4874686d7243970e2"><code>8e9689a</code></a>
Thanks <a
href="https://github.com/marandaneto"><code>@​marandaneto</code></a>! -
Dedupe feature flag called events per returned flag value.
(2026-06-24)</li>
<li>Updated dependencies [<a
href="https://github.com/PostHog/posthog-js/commit/8e9689a9018d184b80f32fc4874686d7243970e2"><code>8e9689a</code></a>]:
<ul>
<li><code>@​posthog/core</code><a
href="https://github.com/1"><code>@​1</code></a>.37.3</li>
</ul>
</li>
</ul>
<h2>5.38.4</h2>
<h3>Patch Changes</h3>
<ul>
<li><a
href="https://redirect.github.com/PostHog/posthog-js/pull/3938">#3938</a>
<a
href="https://github.com/PostHog/posthog-js/commit/6391106fd4bcbddb9a63a13338929a4c1918a70a"><code>6391106</code></a>
Thanks <a
href="https://github.com/dustinbyrne"><code>@​dustinbyrne</code></a>! -
Respect <code>disable_capture_url_hashes</code> when capturing Express
and NestJS request URLs.
(2026-06-23)</li>
</ul>
<h2>5.38.3</h2>
<h3>Patch Changes</h3>
<ul>
<li><a
href="https://redirect.github.com/PostHog/posthog-js/pull/3937">#3937</a>
<a
href="https://github.com/PostHog/posthog-js/commit/4d59d0e10f92ad3444e14dc171997c21aba57929"><code>4d59d0e</code></a>
Thanks <a
href="https://github.com/marandaneto"><code>@​marandaneto</code></a>! -
Deprecate the Node SDK Sentry integration and point users to PostHog
Error Tracking for Node.
(2026-06-23)</li>
<li>Updated dependencies [<a
href="https://github.com/PostHog/posthog-js/commit/440e370fda48d629352f3280471a228ee973dcb0"><code>440e370</code></a>]:
<ul>
<li><code>@​posthog/core</code><a
href="https://github.com/1"><code>@​1</code></a>.37.0</li>
</ul>
</li>
</ul>
<h2>5.38.2</h2>
<h3>Patch Changes</h3>
<ul>
<li><a
href="https://redirect.github.com/PostHog/posthog-js/pull/3903">#3903</a>
<a
href="https://github.com/PostHog/posthog-js/commit/6b21f77291aeea64ce8229eb28196d1acacc20ce"><code>6b21f77</code></a>
Thanks <a
href="https://github.com/marandaneto"><code>@​marandaneto</code></a>! -
Validate custom event UUID overrides and generate new UUIDs when
invalid.
(2026-06-19)</li>
<li>Updated dependencies [<a
href="https://github.com/PostHog/posthog-js/commit/6b21f77291aeea64ce8229eb28196d1acacc20ce"><code>6b21f77</code></a>]:
<ul>
<li><code>@​posthog/core</code><a
href="https://github.com/1"><code>@​1</code></a>.35.3</li>
</ul>
</li>
</ul>
<h2>5.38.1</h2>
<h3>Patch Changes</h3>
<ul>
<li><a
href="https://redirect.github.com/PostHog/posthog-js/pull/3886">#3886</a>
<a
href="https://github.com/PostHog/posthog-js/commit/e6d7fe2a5f10d29b3df69392f584970e7a7a4561"><code>e6d7fe2</code></a>
Thanks <a
href="https://github.com/marandaneto"><code>@​marandaneto</code></a>! -
Stop sending deprecated no-op top-level <code>type</code>,
<code>library</code>, and <code>library_version</code> fields in event
batch payloads. Use <code>properties.$lib</code> and
<code>properties.$lib_version</code> for SDK metadata; legacy queued
<code>library</code> and <code>library_version</code> values are used as
fallbacks when the official <code>$</code> properties are missing.
(2026-06-18)</li>
<li>Updated dependencies [<a
href="https://github.com/PostHog/posthog-js/commit/e6d7fe2a5f10d29b3df69392f584970e7a7a4561"><code>e6d7fe2</code></a>]:
<ul>
<li><code>@​posthog/core</code><a
href="https://github.com/1"><code>@​1</code></a>.35.2</li>
</ul>
</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/PostHog/posthog-js/commit/d2faa824af1af2cb50b47178fb953bfc8812b389"><code>d2faa82</code></a>
chore: update versions and lockfile [version bump]</li>
<li><a
href="https://github.com/PostHog/posthog-js/commit/606ef43d69fd09a00a67df2a404d8739cc50c654"><code>606ef43</code></a>
fix: preserve $groups passed via properties (<a
href="https://github.com/PostHog/posthog-js/tree/HEAD/packages/node/issues/3896">#3896</a>)</li>
<li><a
href="https://github.com/PostHog/posthog-js/commit/5c4838f1671ece80d153aaeb6d946bb3e8c8240a"><code>5c4838f</code></a>
chore: update versions and lockfile [version bump]</li>
<li><a
href="https://github.com/PostHog/posthog-js/commit/8e9689a9018d184b80f32fc4874686d7243970e2"><code>8e9689a</code></a>
fix: Dedupe feature flag called events by response (<a
href="https://github.com/PostHog/posthog-js/tree/HEAD/packages/node/issues/3955">#3955</a>)</li>
<li><a
href="https://github.com/PostHog/posthog-js/commit/2a60c21134dd118c2367e56129bc9427079c3841"><code>2a60c21</code></a>
chore: update versions and lockfile [version bump]</li>
<li><a
href="https://github.com/PostHog/posthog-js/commit/6391106fd4bcbddb9a63a13338929a4c1918a70a"><code>6391106</code></a>
fix(node): respect URL hash capture config (<a
href="https://github.com/PostHog/posthog-js/tree/HEAD/packages/node/issues/3938">#3938</a>)</li>
<li><a
href="https://github.com/PostHog/posthog-js/commit/a3136c6de3114ee3d58e59a0c86f2b9c4f59a2b0"><code>a3136c6</code></a>
chore: update versions and lockfile [version bump]</li>
<li><a
href="https://github.com/PostHog/posthog-js/commit/bf6947d3e12ac512c99185be9b8b134c04eb563a"><code>bf6947d</code></a>
feat(react-native): support session replay event triggers (<a
href="https://github.com/PostHog/posthog-js/tree/HEAD/packages/node/issues/3932">#3932</a>)</li>
<li><a
href="https://github.com/PostHog/posthog-js/commit/2628a1af5f06f73cb2252de002e0da4b2d1a25a5"><code>2628a1a</code></a>
chore: update versions and lockfile [version bump]</li>
<li><a
href="https://github.com/PostHog/posthog-js/commit/4d59d0e10f92ad3444e14dc171997c21aba57929"><code>4d59d0e</code></a>
chore(node): mark Sentry integration deprecated (<a
href="https://github.com/PostHog/posthog-js/tree/HEAD/packages/node/issues/3937">#3937</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/PostHog/posthog-js/commits/posthog-node@5.38.6/packages/node">compare
view</a></li>
</ul>
</details>
<br />

Updates `posthog-node` from 5.35.0 to 5.38.6
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/PostHog/posthog-js/releases">posthog-node's
releases</a>.</em></p>
<blockquote>
<h2>posthog-node@5.38.6</h2>
<h2>5.38.6</h2>
<h3>Patch Changes</h3>
<ul>
<li><a
href="https://redirect.github.com/PostHog/posthog-js/pull/3896">#3896</a>
<a
href="https://github.com/PostHog/posthog-js/commit/606ef43d69fd09a00a67df2a404d8739cc50c654"><code>606ef43</code></a>
Thanks <a
href="https://github.com/GauthierPLM"><code>@​GauthierPLM</code></a>! -
Only stamp $groups from the top-level <code>groups</code> when present
(2026-06-26)</li>
</ul>
<h2>posthog-node@5.38.5</h2>
<h2>5.38.5</h2>
<h3>Patch Changes</h3>
<ul>
<li><a
href="https://redirect.github.com/PostHog/posthog-js/pull/3955">#3955</a>
<a
href="https://github.com/PostHog/posthog-js/commit/8e9689a9018d184b80f32fc4874686d7243970e2"><code>8e9689a</code></a>
Thanks <a
href="https://github.com/marandaneto"><code>@​marandaneto</code></a>! -
Dedupe feature flag called events per returned flag value.
(2026-06-24)</li>
<li>Updated dependencies [<a
href="https://github.com/PostHog/posthog-js/commit/8e9689a9018d184b80f32fc4874686d7243970e2"><code>8e9689a</code></a>]:
<ul>
<li><code>@​posthog/core</code><a
href="https://github.com/1"><code>@​1</code></a>.37.3</li>
</ul>
</li>
</ul>
<h2>posthog-node@5.38.4</h2>
<h2>5.38.4</h2>
<h3>Patch Changes</h3>
<ul>
<li><a
href="https://redirect.github.com/PostHog/posthog-js/pull/3938">#3938</a>
<a
href="https://github.com/PostHog/posthog-js/commit/6391106fd4bcbddb9a63a13338929a4c1918a70a"><code>6391106</code></a>
Thanks <a
href="https://github.com/dustinbyrne"><code>@​dustinbyrne</code></a>! -
Respect <code>disable_capture_url_hashes</code> when capturing Express
and NestJS request URLs.
(2026-06-23)</li>
</ul>
<h2>posthog-node@5.38.3</h2>
<h2>5.38.3</h2>
<h3>Patch Changes</h3>
<ul>
<li><a
href="https://redirect.github.com/PostHog/posthog-js/pull/3937">#3937</a>
<a
href="https://github.com/PostHog/posthog-js/commit/4d59d0e10f92ad3444e14dc171997c21aba57929"><code>4d59d0e</code></a>
Thanks <a
href="https://github.com/marandaneto"><code>@​marandaneto</code></a>! -
Deprecate the Node SDK Sentry integration and point users to PostHog
Error Tracking for Node.
(2026-06-23)</li>
<li>Updated dependencies [<a
href="https://github.com/PostHog/posthog-js/commit/440e370fda48d629352f3280471a228ee973dcb0"><code>440e370</code></a>]:
<ul>
<li><code>@​posthog/core</code><a
href="https://github.com/1"><code>@​1</code></a>.37.0</li>
</ul>
</li>
</ul>
<h2>posthog-node@5.38.2</h2>
<h2>5.38.2</h2>
<h3>Patch Changes</h3>
<ul>
<li><a
href="https://redirect.github.com/PostHog/posthog-js/pull/3903">#3903</a>
<a
href="https://github.com/PostHog/posthog-js/commit/6b21f77291aeea64ce8229eb28196d1acacc20ce"><code>6b21f77</code></a>
Thanks <a
href="https://github.com/marandaneto"><code>@​marandaneto</code></a>! -
Validate custom event UUID overrides and generate new UUIDs when
invalid.
(2026-06-19)</li>
<li>Updated dependencies [<a
href="https://github.com/PostHog/posthog-js/commit/6b21f77291aeea64ce8229eb28196d1acacc20ce"><code>6b21f77</code></a>]:
<ul>
<li><code>@​posthog/core</code><a
href="https://github.com/1"><code>@​1</code></a>.35.3</li>
</ul>
</li>
</ul>
<h2>posthog-node@5.38.1</h2>
<h2>5.38.1</h2>
<h3>Patch Changes</h3>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/PostHog/posthog-js/blob/main/packages/node/CHANGELOG.md">posthog-node's
changelog</a>.</em></p>
<blockquote>
<h2>5.38.6</h2>
<h3>Patch Changes</h3>
<ul>
<li><a
href="https://redirect.github.com/PostHog/posthog-js/pull/3896">#3896</a>
<a
href="https://github.com/PostHog/posthog-js/commit/606ef43d69fd09a00a67df2a404d8739cc50c654"><code>606ef43</code></a>
Thanks <a
href="https://github.com/GauthierPLM"><code>@​GauthierPLM</code></a>! -
Only stamp $groups from the top-level <code>groups</code> when present
(2026-06-26)</li>
</ul>
<h2>5.38.5</h2>
<h3>Patch Changes</h3>
<ul>
<li><a
href="https://redirect.github.com/PostHog/posthog-js/pull/3955">#3955</a>
<a
href="https://github.com/PostHog/posthog-js/commit/8e9689a9018d184b80f32fc4874686d7243970e2"><code>8e9689a</code></a>
Thanks <a
href="https://github.com/marandaneto"><code>@​marandaneto</code></a>! -
Dedupe feature flag called events per returned flag value.
(2026-06-24)</li>
<li>Updated dependencies [<a
href="https://github.com/PostHog/posthog-js/commit/8e9689a9018d184b80f32fc4874686d7243970e2"><code>8e9689a</code></a>]:
<ul>
<li><code>@​posthog/core</code><a
href="https://github.com/1"><code>@​1</code></a>.37.3</li>
</ul>
</li>
</ul>
<h2>5.38.4</h2>
<h3>Patch Changes</h3>
<ul>
<li><a
href="https://redirect.github.com/PostHog/posthog-js/pull/3938">#3938</a>
<a
href="https://github.com/PostHog/posthog-js/commit/6391106fd4bcbddb9a63a13338929a4c1918a70a"><code>6391106</code></a>
Thanks <a
href="https://github.com/dustinbyrne"><code>@​dustinbyrne</code></a>! -
Respect <code>disable_capture_url_hashes</code> when capturing Express
and NestJS request URLs.
(2026-06-23)</li>
</ul>
<h2>5.38.3</h2>
<h3>Patch Changes</h3>
<ul>
<li><a
href="https://redirect.github.com/PostHog/posthog-js/pull/3937">#3937</a>
<a
href="https://github.com/PostHog/posthog-js/commit/4d59d0e10f92ad3444e14dc171997c21aba57929"><code>4d59d0e</code></a>
Thanks <a
href="https://github.com/marandaneto"><code>@​marandaneto</code></a>! -
Deprecate the Node SDK Sentry integration and point users to PostHog
Error Tracking for Node.
(2026-06-23)</li>
<li>Updated dependencies [<a
href="https://github.com/PostHog/posthog-js/commit/440e370fda48d629352f3280471a228ee973dcb0"><code>440e370</code></a>]:
<ul>
<li><code>@​posthog/core</code><a
href="https://github.com/1"><code>@​1</code></a>.37.0</li>
</ul>
</li>
</ul>
<h2>5.38.2</h2>
<h3>Patch Changes</h3>
<ul>
<li><a
href="https://redirect.github.com/PostHog/posthog-js/pull/3903">#3903</a>
<a
href="https://github.com/PostHog/posthog-js/commit/6b21f77291aeea64ce8229eb28196d1acacc20ce"><code>6b21f77</code></a>
Thanks <a
href="https://github.com/marandaneto"><code>@​marandaneto</code></a>! -
Validate custom event UUID overrides and generate new UUIDs when
invalid.
(2026-06-19)</li>
<li>Updated dependencies [<a
href="https://github.com/PostHog/posthog-js/commit/6b21f77291aeea64ce8229eb28196d1acacc20ce"><code>6b21f77</code></a>]:
<ul>
<li><code>@​posthog/core</code><a
href="https://github.com/1"><code>@​1</code></a>.35.3</li>
</ul>
</li>
</ul>
<h2>5.38.1</h2>
<h3>Patch Changes</h3>
<ul>
<li><a
href="https://redirect.github.com/PostHog/posthog-js/pull/3886">#3886</a>
<a
href="https://github.com/PostHog/posthog-js/commit/e6d7fe2a5f10d29b3df69392f584970e7a7a4561"><code>e6d7fe2</code></a>
Thanks <a
href="https://github.com/marandaneto"><code>@​marandaneto</code></a>! -
Stop sending deprecated no-op top-level <code>type<...

_Description has been truncated_

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-26 18:30:38 +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 fa5af59931 refactor: replace hand-rolled parsers with standard packages (#373)
## What & why

Swept the repo for hand-rolled regex/string parsers that reimplement a
solved problem, and replaced the genuinely fragile ones with
battle-tested packages (or deps already in the tree). Each swap is
paired with a regression test for an input the old code got wrong.

| Site | Was | Now |
|------|-----|-----|
| `adb.ts` `resolveAvdPath`, `linux-preflight.ts` `readMb` | per-key
anchored regex over AVD `.ini` / `config.ini` | **`ini`** |
| `workspace-reader.ts` `extractEnvKeys` | split-on-first-`=` line loop
| **`dotenv`** (`parse`) |
| `uninstall.ts` `readBundledSkillName` | `^name:(.+)$` capture +
outer-quote strip | **`yaml`** (already a dep) |
| `mcp-configs.ts` `escapeRegExp` | hand-rolled char-class escape |
**`escape-string-regexp`** |
| `export.ts` xctrace version, `session-context.ts` RN minor |
`match(/(\d+)\./)` / `split(".")[1]` | **`semver`** `coerce` (already a
dep) |
| `source-resolver.ts` `parseDebugStack` | `at fn (file:l:c)` regex
(required both line+col) | **`stacktrace-parser`** |

### Bugs these fix (covered by new tests)
- **ini**: a quoted `path = "/Users/My Name/.../X.avd"` kept its quotes,
failed the `startsWith("/")` guard → AVD unresolved. Inline comments /
trailing junk after a sizing value made the anchored regex return `null`
→ sizing warning silently lost.
- **dotenv**: `export FOO=bar` produced the key `"export FOO"` (dropped
by the identifier filter); a multi-line quoted value leaked its
continuation lines (`FAKE=...`) as phantom keys.
- **yaml**: a `name:` value with a trailing `# comment` was taken
verbatim (comment included); odd quoting only had one outer pair
stripped.
- **stacktrace-parser**: a `:line`-only frame (no column, as Hermes/JSC
can emit) became an unusable `unknown` frame.

## Deliberately left alone (not "flaky", or no safe swap)

To avoid churn and regressions, I did **not** touch:
- **`uiautomator-parser.ts`** hand-rolled XML parser — a deliberate,
documented choice with a dedicated hardening suite, including a
*leniency* test for malformed dumps (stray `</node>`). A spec XML parser
would reject inputs this is built to recover from.
- **`ios-profiler/pipeline/xml-parser.ts`** (xctrace XML) — the most
tangled parser, but it has no real `.trace` fixtures and only thin
entity-decode tests. A rewrite can't be proven equivalent without
device-generated traces, so swapping it blind risks silently breaking
iOS CPU/hang/leak reports. Best done separately with a captured fixture.
- **`http.ts` `extractHostname`** — DNS-rebinding guard; `URL.hostname`
keeps the IPv6 brackets the loopback check compares without, so the
"fix" isn't simpler and the path is security-sensitive.
- **`adb.ts` `shellQuote`** — the POSIX single-quote escape is the
standard correct form.
- **`parseAdbDevices`, `android-screen.ts` `wm size`, `detect-app.ts`
dumpsys** — tool-specific CLI-output scraping with no clean package
equivalent (and documented edge-case handling).
- **`workspace-reader.ts` `extractMetroPort`** — a best-effort heuristic
over JS config; a correct replacement needs an AST parse, out of scope
here.
- **`config-parse.ts` Yarn/npm age-gate parsing** — a precise,
unit-tested domain parser; `ms` would introduce a bare-number ambiguity.

## Verification
- `tsc --build` clean across the workspace graph.
- Tests: tool-server **1205**, installer **264**, update-core **31** —
all pass (incl. the new regression tests).
- `prettier --check` clean on all changed files.
- Production esbuild bundle (`bundle-tools.cjs`) emits a valid
`tool-server.cjs`/`mcp-server.mjs` with
`ini`/`dotenv`/`stacktrace-parser` inlined (`node --check` OK). *(The
script's final step errored only on the auth-gated
`trace-processor.wasm` download, unrelated to bundling.)*

New runtime deps: `ini`, `dotenv`, `stacktrace-parser` (tool-server),
`escape-string-regexp` (installer) — all small, pure-JS. Lockfile change
is purely additive.
2026-06-25 13:29:10 +02:00
Ignacy Łątka 270c9a158a chore: bump to 0.13.0 (#412)
as per title
2026-06-24 20:27:08 +02:00
Ignacy Łątka 10ecc9e53d fix(release): auto-sync the npm 'next' dist-tag to the highest published version (#357)
Auto-sync the npm `next` dist-tag to the highest published version (prerelease or stable) so it stops drifting behind `latest`.

- New dependency-free `scripts/sync-next-dist-tag.mjs` (SemVer comparator verified against the `semver` lib in `scripts/sync-next-dist-tag.test.mjs`).
- Runs as a post-publish step in `publish.yml` and `publish-next.yml` (with `continue-on-error` so a transient registry blip never fails an already-successful release), plus a manual `Sync 'next' dist-tag` workflow with a dry-run toggle.
- Only ever advances `next` (never regresses it) to survive a stale post-publish packument read.
- Package-name arg parsing rejects flag/blank tokens so the scoped `@swmansion/argent` default reliably wins.
2026-06-24 12:29:45 +02:00
Piotr Krawiec 0bed3624a0 feat(vega): Amazon Vega (Fire TV) platform support (#366)
## Summary

Adds **Vega (Amazon Fire TV)** as a third Argent platform alongside iOS
and Android. Vega apps are React Native 0.72 + Hermes running on a QEMU
**Virtual Device (VVD)**, driven by a **D-pad tv-remote rather than
touch**. This PR extends Argent's platform abstraction so the existing
tool surface gates correctly on `vega`, and adds the Vega-specific tools
needed to actually drive a Fire TV app end to end.

### Platform abstraction

- `Platform` gains `vega`; `DeviceKind` gains `vvd` (the QEMU VVD);
`ToolCapability` gains a `vega { vvd, device }` block; `ToolDependency`
gains `vega`.
- `assertSupported()` selects the `vega` capability matrix for Vega
devices; `dispatchByPlatform()` gains an optional `vega` branch, and a
Vega dispatch with no branch throws `NotImplementedOnPlatformError`
(501).
- New `vega-cli` util (modeled on `adb.ts`) resolves `vega`/`kepler`
from `PATH` or `~/vega/bin` with the same hardened exec/quoting;
`check-deps` resolves and hints the `vega` dependency.
- The `simulator-server` blueprint is never wired for `vega` (Vega uses
`adb` + the `vega`/`kepler` CLI, not the proprietary binary).

### How Vega is wired

- **Input / screenshot / describe** go over `adb` (input via
`inputd-cli`, screenshots host-side via the Android emulator console,
element tree via the on-device automation toolkit).
- **App lifecycle** goes over the `vega`/`kepler` CLI.
- v1 is **Virtual-Device-only**: every Vega capability is declared
`vvd`. Physical Fire TV is out of scope and untested — the tool-server
does not connect to or detect hardware, so `resolveDevice` classifies
every `amazon-` serial as a VVD by shape. A physical device is therefore
not classified as `device` and not rejected at the capability gate;
supporting and gating real hardware is deferred to a version where it
can be tested.
- Vega runs **React Native 0.72**, which predates the new React Native
DevTools. The CDP/Hermes debugger surface (`debugger-*`, React/native
profilers) depends on integration points the new DevTools provides, so
**debugging and profiling are out of scope for this PR** — Vega
profiling/crash analysis is handled by the
`amazon-devices-buildertools-mcp` server instead.

### Also includes

- `argent-vega` skill documenting the full Vega workflow (target
discovery, the describe → compute-path → `tv-remote` navigation loop,
text injection).
- Vega VVD end-to-end CI workflow.

## Feature support by platform

Legend: ✅ supported · ❌ not supported · ⚠️ degraded / conditional · —
not applicable

| Feature (tool) | iOS | Android | Chromium | **Vega (VVD)** | Vega
notes |
|---|:---:|:---:|:---:|:---:|---|
| **Device discovery** (`list-devices`) | ✅ | ✅ | ✅ | **✅** | Tagged
`platform:"vega"`, `kind:"vvd"`; pass `serial` as `udid` |
| **Boot / start device** | ✅ | ✅ | — | ✅ |  |
| **Launch app** (`launch-app`) | ✅ | ✅ | ✅ | **✅** | `bundleId` =
interactive-component app id from `manifest.toml` |
| **Restart app** (`restart-app`) | ✅ | ✅ | ✅ | **✅** | Terminate +
relaunch |
| **Reinstall app** (`reinstall-app`) | ✅ | ✅ | ❌ | **✅** | Uninstall +
install a `.vpkg` |
| **Screenshot** (`screenshot`) | ✅ | ✅ | ✅ | **✅** | Host-side via
emulator console (needs `adb`); rotation ignored (fixed landscape) |
| **Describe / element tree** (`describe`) | ✅ | ✅ | ✅ | **✅** |
On-device automation toolkit; surfaces `[focused]`/`[selected]` +
normalized frames |
| **Touch gestures** (`gesture-tap`/`swipe`/`scroll`/`drag`/`custom`) |
✅ | ✅ | ✅ | **❌** | Vega is tv-remote-driven; gestures error — use
`tv-remote` |
| **Pinch / rotate gesture** (`gesture-pinch`/`gesture-rotate`) | ✅ | ❌
| ⚠️ | **❌** | N/A on a TV |
| **Remote / D-pad** (`tv-remote`) | — | — | — | **✅** | Vega-only tool;
single key, repeat, or a whole path in one call |
| **Hardware buttons** (`button`) | ✅ | ✅ | — | **❌** | Remote keys live
in `remote` instead |
| **Text input** (`keyboard`) | ✅ | ✅ | ✅ | **✅** | Whole-string
injection via `inputd-cli send_text` |
| **Rotate orientation** (`rotate`) | ✅ | ✅ | — | **❌** | TV framebuffer
is fixed landscape |
| **Open URL / deep link** (`open-url`) | ✅ | ✅ | ✅ | **❌** | Not wired
on Vega |
| **JS debugger** (`debugger-*`) | ✅ | ✅ | ✅ | **❌** | RN 0.72 predates
the new RN DevTools — out of scope for this PR |
| **Native devtools / AX** (`native-describe-screen`, etc.) | ✅ | ❌ | —
| **❌** | iOS-only |
| **Native profiler** (`native-profiler-*`) | ✅ | ✅ | — | **❌** | Use
`amazon-devices-buildertools-mcp` (Perfetto) instead |
| **React profiler** (`react-profiler-*`) | ✅ | ✅ | — | **❌** | Out of
scope; RN 0.72 / new RN DevTools not supported |
| **Screenshot diff** (`screenshot-diff`) | ✅ | ✅ | ✅ | **✅** |
Host-side image comparison; platform-agnostic |
| **Flows record/replay** (`flow-*`) | ✅ | ✅ | ✅ | **✅** | Records any
tool-call sequence |

## Test plan

- [x] `list-devices` surfaces connected Vega devices (`platform:"vega"`,
`kind:"vvd"`)
- [x] `launch-app`, `restart-app`, `reinstall-app`,
`list-installed-apps` work on Vega
- [x] `tv-remote` drives focus (single key, `repeat`, and multi-key
path) and `keyboard` injects whole strings
- [x] `screenshot` returns a frame via the host-side VVD path
- [x] `describe` returns the on-screen element tree with `[focused]`
state and normalized coordinates
- [x] Vega VVD e2e CI workflow passes
- [ ] Test multiple platforms being driven at the same time
    - [x] Android + Vega
    - [ ] iOS + Vega
    - [ ] chrome + Vega

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-22 20:31:45 +02:00
filip131311 af75eeea1f chore: repo hygiene — gitignore argent init artifacts + resync package-lock for npm 11 (#383)
Two related repo-hygiene fixes surfaced while triaging a "lock files
problem on main".

## 1. `package-lock.json` out of sync (fixes the failing CI check)

The `lockfile.yml` job (`package-lock.json in sync`) was **failing on
every PR**, including this one — but it's **pre-existing drift on
`main`**, not caused by the `.gitignore` change.

Root cause: the committed lockfile carried **26× `"peer": true,`** and
**1× `"dev": true,`** markers written by an older npm. CI pins **Node 24
(npm 11)**, which no longer emits those markers, so `npm install
--package-lock-only` produced a 27-line diff and the check failed.
(Locally under Node 22 / npm 10 the markers are preserved, so the drift
was invisible there — which is why the first pass looked green.)

Fix: regenerated with **Node 24 / npm 11.12.1** → **27 deletions, no
version/resolved/integrity changes** (pure metadata normalization).
Matches exactly what CI expects.

## 2. gitignore `argent init` agent-harness artifacts

Running `argent init` inside the repo writes generated config into the
repo root that was never ignored:

- `.agents/`, `.codex/config.toml`, `.gemini/`, `.opencode/`,
`opencode.json`
- `skills-lock.json`

These appeared as untracked clutter on every branch and could be
committed by accident. `skills-lock.json` is the worst offender — it
bakes in **machine-specific absolute paths**
(`/Users/<name>/.nvm/.../@swmansion/argent/skills`). These are the same
class of artifact as the already-ignored `.claude/`, `.cursor/`,
`.vscode/`, `.mcp.json`, just the newer harnesses. `git log --all`
confirms none were ever tracked, so nothing is removed from history.
2026-06-19 22:43:48 +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
Hubert Gancarczyk 8483fe23f9 feat: add telemetry (#274)
## Summary

Adds anonymous, opt-out telemetry for Argent installer, update/uninstall
flows, CLI telemetry controls, tool-server lifecycle, and tool
invocation health signals.
## What Changed
- Added a new private `@argent/telemetry` package
- Introduced typed telemetry events with runtime property
allowlisting/sanitization.
- Added lazy anonymous identity creation in `~/.argent/telemetry-id`.
- Added consent handling via: `argent telemetry enable`, `argent
telemetry disable`, `argent telemetry status`
- Added local debug logging with `ARGENT_TELEMETRY_DEBUG=1`.
- Tracks installer funnel events for init, global install/update
decisions, editor selection, allowlist decisions, skill installation,
rules/agents copy, update, and uninstall.
- Tracks tool-server start/stop and tool invocation/complete/fail
events.
- Adds per-tool invocation IDs so request metadata can be correlated
without relying on ordering.
- Records only coarse platform metadata (`ios` / `android`) for device
tools
- Ensures short-lived commands drain telemetry with bounded shutdown
before exit.
- Wires telemetry package into bundling and injects full Argent CLI
version metadata at build time.
- Added central `FAILURE_CODES` and `FailureSignal` helpers in
`@argent/registry`.
- Attached structured, sanitized failure metadata across registry
errors, tool-server tools/services, HTTP validation/dependency gates,
installer flows, and `argent run`.
- Added telemetry allowlisting for safe failure fields only: static
error codes/stages, coarse area/kind/command, bounded exit codes,
allowlisted signals/spawn codes, and coarse network failure classes.
- Split CLI-local `argent run` failures into `cli:run_fail` so they are
not double-counted as tool failures.
- Added installer telemetry for failed package actions, update/uninstall
failures, rules/agents copy results, and skill refresh results.
- Made telemetry consent writes atomic and kept telemetry status
side-effect free via `peekAnonId()`.
- Adds a coarse, anonymous signal to tool telemetry: which AI coding
tool is driving the MCP server (Claude Code, Codex, Cursor, Gemini, VS
Code, Windsurf, Zed, opencode, Copilot, or other).

## Tests Added / Updated

- Telemetry unit coverage for consent precedence, identity creation,
base props, sanitization, PostHog config, debug logging, shutdown,
opt-out, and forget behavior.
- Registry/tool-server tests for invocation IDs, platform metadata
boundaries, startup failure flushing, and tool lifecycle telemetry.
- Installer tests for final telemetry drain, update action
classification, and uninstall identity cleanup ordering.

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-19 18:22:34 +02:00
Ignacy Łątka dc0930d50c ci: prevent package-lock drift + add repo-hygiene checks (#362)
## Why

We hit a mismatch between a locally-generated `package-lock.json` and
the one on `main`. `npm ci` validates the lockfile loosely and accepts a
stale lock that a fresh `npm install` would rewrite, so the drift
slipped through. This adds CI to catch that, plus a few related
common-mistake guards.

## What

**`Lockfile` workflow** (Node 20) — runs `npm install
--package-lock-only --ignore-scripts` and fails if `package-lock.json`
changes. This is the gap `npm ci` leaves: it asserts the lock is exactly
what npm produces, not merely "consistent enough".

**`Repo Hygiene` workflow** — three fast static guards:
- **Merge-conflict markers** — fail on any `<<<<<<<` / `=======` /
`>>>>>>>` left in a tracked file.
- **Focused tests** — fail on `describe/it/test/bench.only` in
`*.test.ts(x)` / `*.spec.ts(x)`. Uses a POSIX-ERE word-boundary pattern
so it actually fires under `git grep -E` (a `\b` pattern silently
matches nothing there).
- **Workspace version sync** — fail when `packages/*` versions drift,
via `scripts/check-workspace-versions.mjs` (also `npm run
check:versions`).

To make versions consistent, `@argent/ui` is bumped `0.0.1 → 0.12.0` (it
is `private`, so no publish impact).

## Verification (Node 20 / npm 10.8.2, matching CI)

- Lockfile check: no false positive on `main`; idempotent; catches a
tampered lock.
- Merge-marker & `.only` greps: proven to catch real violations and
reject lookalikes (`await.only`, `audit.only`, `foo.it.only`); clean
across the repo.
- `check-workspace-versions.mjs`: reports all packages at `0.12.0`;
passes `eslint` and `typecheck:scripts`.
- All new/changed files pass `prettier --check`; both workflow YAMLs
parse.

## Note: npm version coupling

The check runs on Node 20 (npm 10.8.2), matching the committed lock's
format. npm 11 (Node 24) regenerates the same lock minus cosmetic
`"peer": true` markers — no resolution change, but a contributor who
regenerates on npm 11 would trip the strict diff. The repo pins no
toolchain today (CI mixes Node 20 and 24). Pinning npm/Node repo-wide
would fully close this; left out of scope here — happy to add it as a
follow-up.
2026-06-19 15:19:19 +02:00
Ignacy Łątka d987c25ad0 fix(argent): declare + ship tree-sitter so react-profiler-component-source finds components (#287)
## Problem

`react-profiler-component-source` returned `found:false` for **every**
component in a standard TS/TSX Expo project.

## Two root causes (both verified live against the Bluesky app)

1. **tree-sitter never declared/shipped.** `@swmansion/argent` didn't
depend on `tree-sitter`/`tree-sitter-typescript`, so the parser
`require()` threw, was swallowed, and the AST index was always empty.
Fixed by adding them as runtime deps and keeping them `external` in the
CJS tools bundle so the native addons are `require()`d at runtime (not
inlined). Verified: `HostingProvider`, `LoggedOutLayout`, `Login` now
resolve with correct file:line.

2. **memo()/forwardRef() components were skipped.** The AST matcher's
`variable_declarator` branch only indexed
`arrow_function`/`function_expression` value nodes; a wrapped component
(`const X = memo(...)` / `forwardRef(...)` / `React.memo(...)`) has a
`call_expression` value node, so it was never indexed — and profiler
findings are disproportionately memo-wrapped, i.e. exactly the
components users look up. Now recognises memo/forwardRef wrapper calls
(incl. `React.`-prefixed and nested `memo(forwardRef(...))`) and indexes
the declared name; `memo` sets `isMemoized`. Verified live:
`ThreadItemReadMore`, `CenteredView`, `ScrollView`, `BookmarkButton`,
`Logo` now resolve.

## Tests
`ast-index.test.ts` covers the idiomatic forms
(default-export/plain/arrow) and the memo/forwardRef/React.memo/nested
wrappers; the wrapped-form test fails on pre-fix code. Rebased onto
latest main (re-merged the `bundle-tools.cjs` helper refactor; bundle
still externalizes tree-sitter). CI green.
2026-06-18 13:20:38 +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
Ignacy Łątka 56033b62ca chore(deps): bump express to 5.x and migrate route params (fixes #314) (#354)
## What

Bumps `express` `^4.19.2` → `^5.2.1` and `@types/express` `^4.17.21` →
`^5.0.6` in `@argent/tool-server`, with the source change Express 5
requires. This is the **express-5 half of dependabot #314**, which can't
merge as-is because it breaks the build.

## Why #314 broke

Express 5 / `@types/express` 5 widen route param values from `string` to
`string | string[]`, so every single-segment param read fails to
typecheck — 9 `TS2345` errors across `artifacts.ts`, `http.ts`,
`preview.ts`:

```
packages/tool-server/src/artifacts.ts(61,42): error TS2345: Argument of type 'string | string[]' is not assignable to parameter of type 'string'.
... (9 total, incl. preview.ts variant-image/describe routes added by Argent Lens)
```

## Fix

Narrow each param read with `as string`. Every tool-server route uses
single-segment params, which path-to-regexp v8 always resolves to a
string, so the cast is sound:

- `artifacts.ts` — `id`
- `http.ts` — `deviceId`, `name`
- `preview.ts` — `udid` (×2), `elementId`, `variantId`

No removed Express 5 APIs (`app.del`, `res.sendfile`, `req.param()`,
numeric `res.send`/`res.json` status) are used, and no route relies on
the changed wildcard/optional path syntax. Lockfile regenerated on
**Node 20 / npm 10.8.2** to match the repo's package-lock toolchain.

## Verification (local)

- Reproduced the 9 `TS2345` errors on express 5, then `tsc --build`
clean after the narrowings.
- Full CI-equivalent on **Node 20** (the unit-tests toolchain): `tsc
--build`, `eslint --max-warnings 0`, `prettier --check`,
`typecheck:tests`/`typecheck:scripts`, full unit suite incl. **1250
tool-server tests** — all green. `test/artifacts.test.ts` boots the real
Express app via supertest and exercises `GET /artifacts/:id` under
Express 5.
- `npm ci` from the committed lockfile succeeds; the lock is idempotent
under `npm install --package-lock-only --ignore-scripts` (Node 20).
- **Live device E2E** (out-of-process tool-server under Express 5,
booted iPhone 16 Pro Max): `GET /artifacts/:id` → `200 image/png` (331
KB PNG, ws + simulator-server); `GET /preview/simulator-server/:udid` →
`200`; `GET /preview/variant-image/:elementId/:variantId` → route
matched + params parsed. Unknown artifact id → `404`.

Supersedes #314 (Dependabot auto-closes it once express reaches 5.x on
`main`).
2026-06-18 11:50:30 +02:00
Filip131311 8b25855e9a package-lock reconsiliation 2026-06-17 16:02:26 +02:00
Filip131311 37d28fef8f seciurity and npm i 2026-06-17 15:45:37 +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 f3d108a5a1 feat: Argent Lens (#271)
Adds a UI for proposing and choosing between multiple agent-generated
variants on a running app.

**MCP tools** (`packages/tool-server/src/tools/variants/`)
- `propose_variant` — non-blocking; agent stages a variant for an
on-screen element (text/label/identifier/role matcher).
- `await_user_selection` — one blocking call; parks until the human
submits, then returns the picks + free-form annotations + a global
comment.

**Preview UI** (`packages/ui`)
- Floating cards anchored to live element frames via the existing
`describe` tree, with connector lines.
- Comment-mode inspector: hover spotlight (blur+dim everything else),
right-click any element to pin a comment.
- Sticky padded hitbox + trajectory-aware dismissal so focus doesn't
drop on cursor jitter.
- Footbar [Complete] [Clear], confirm modal for destructive actions,
green toast on submit.
- Single source of feel constants in `theme.css`.

**Electron host** (`packages/preview-window`)
- New workspace package. Frameless transparent BrowserWindow at fixed
size; the squeeze open/close is a CSS `transform: scaleY` on `<html>`,
GPU-composited — no OS-level resize.
- Tool-server spawns it on `await_user_selection` park, reuses it across
re-awaits, and asks it to animate-close on submit (1s grace so the toast
is visible).
- Stdio-line IPC (no extra ports). Ephemeral throughout — the renderer
hits the tool-server's already-ephemeral `/preview/*` HTTP.

---------

Co-authored-by: Filip131311 <contact@filipkaminski.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-06-17 15:22:04 +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
Stanisław Chmiela 6e4d8babec feat: GET /artifacts/ endpoint (#347)
EAS is going to poll this endpoint for new artifacts and upload as
session artifacts for later user inspection.

I'm open to better ways to integrate, this seems the easiest to merge
and move forward.
2026-06-16 15:46:41 +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
Paweł Fornagiel 92cc69a75c feat: extract configuration-core and migrate auto-screenshot to feature flag (#295)
## Summary

- Introduces `@argent/configuration-core` as the shared source of truth
for feature-flag storage, the registry, and `isFlagEnabled`, so MCP and
CLI can import the same primitives without coupling runtime code to
CLI-only modules.
- Replaces `ARGENT_AUTO_SCREENSHOT` with the off-by-default
`disable-auto-screenshot` flag (auto-screenshot stays on unless enabled
via `argent enable disable-auto-screenshot`); renames
`ARGENT_AUTO_SHUTDOWN` to `ARGENT_TOOL_SERVER_SHUTDOWN_ON_MCP_EXIT`.
- Keeps `@argent/cli` as the command layer (`enable` / `disable` /
`flags`) with backward-compat re-exports, colored flag state output
(picocolors), and `--help` listings of available flags.

---------

Co-authored-by: Filip131311 <contact@filipkaminski.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-10 15:01:25 +02:00
Paweł Fornagiel 3ccd89f333 feat: add native android profiling (#275)
# Android native profiling via in-process Perfetto WASM

Connected with:
https://github.com/software-mansion/argent-private/pull/16

## TL;DR

Adds **Android** to the native profiler (previously iOS-only) and does
it with a
brand-new engine: instead of shipping Perfetto's `trace_processor_shell`
native
binary and forking it per query, we now run **Perfetto's trace-processor
compiled
to WebAssembly, in-process** inside the tool-server.

- **One ~13 MB `trace_processor.wasm`** runs on every OS/arch — no
per-platform
  binary, no subprocess, fully offline.
- The native profiler is refactored from an iOS-only implementation into
a
**platform-dispatch facade** (`platforms/ios.ts` +
`platforms/android.ts`) over
a new **`profiler-shared/`** layer, so iOS and Android share the
aggregator,
  types, lifecycle, and time-alignment code.
- New **release/pack pipeline** fetches + sha256-verifies the WASM
bundle from
`argent-private-releases`, and bundles it (plus SQL queries and the
Perfetto
  TraceConfig) into the published `@swmansion/argent` package.

End-to-end on a 76 MB trace with 1013 jank rows: **6.3 s** total analyze
(the older per-hang-subprocess approach was projected at ~47 minutes and
never
finished within the tool-call deadline).

---

## TESTING MANUALLY

To test manually run the following packing command checked-out on this
branch:
```
npm run pack:mcp -- argent-@pFornagiel-native-android-devtools-perfetto
```

This will bundle the package with the new binaries. After this PR is
fixed and the binaries are resolved from the main release, the
`argent-@pFornagiel-native-android-devtools-perfetto` release can be
deleted.

## Why

Android needed native CPU/hang/memory profiling to reach parity with
iOS. The
natural engine is Perfetto's trace-processor, but the obvious ways to
ship it are
both bad:

- **`trace_processor_shell` binary** — per-platform (a mac-arm64 pack
shipped a
mac-arm64-only tarball), pinned an OS/arch matrix, and cost a fresh ~1.3
s
subprocess fork **per query**. With one subprocess forked per detected
hang, a
  large trace projected to ~47 minutes.
- **`get.perfetto.dev/trace_processor` Python launcher** — adds a
runtime Python 3
dependency, resolves "latest" at first run (defeats version pinning),
and makes
  the first analyze slow via a network fetch.

The WASM engine is one cross-platform artifact, in-process and
warm-cached: the
trace is parsed **once** and reused across the whole analyze run plus
every
drill-down query.

---

## The three big pieces

### 1. In-process Perfetto WASM engine

New package surface in `@argent/native-devtools-android`:

| File | Role |
| --- | --- |
| `src/wasm-trace-processor.ts` (+476) | The whole engine: boot, glue
patching, warm-cache, fault containment, chunked parse, cell decoding |
| `src/perfetto-engine.d.ts` (+49) | Hand-written types for the
version-agnostic RPC decoder (imported by path, so tsc can't see its own
`.d.ts`) |
| `src/errors.ts` (+68) | `TraceProcessorUnavailableError`
(`wasm_load_failed` / `wasm_path_invalid`) |
| `src/bundled-meta.ts` (+12) | `PERFETTO_VERSION` stamp, regenerated at
pack time |
| `src/index.ts` | Re-exports + package-root accessors
(`traceProcessorQueriesDir`, `traceConfigPath`) |

Public surface: `resolveTraceProcessorAssets()`,
`queryWarm<Row>(tracePath, sql)`,
`ensureTraceProcessorReady(tracePath)`, `disposeWarmEngine(tracePath)`.

Highlights of how it works:

- **Boots Google's prebuilt web/worker wasm under Node.** Installs a
minimal
worker scope (`self`, `WorkerGlobalScope`, `location`), reads the wasm
bytes,
patches the Emscripten glue (`useMemory64=false`, hand the bytes in
directly,
expose the bridge), and drives it over a `MessageChannel` + `EngineBase`
subclass. Glue patches are applied **by unique string anchors that throw
if they
  don't match** — the canary for glue-format drift on a Perfetto bump.
- **Warm-engine cache, one per trace path.** LRU with `MAX_WARM_ENGINES
= 3`
(bounds resident memory at ~26–76 MB trace + wasm heap each), 5-min idle
dispose
  (timer `unref()`'d), no failed-promise caching.
- **Fault containment (`fatal` promise).** `EngineBase.fail()` throws
synchronously
inside the `MessagePort.onmessage` handler (e.g. an RPC framing error);
left
unhandled it becomes a Node `uncaughtException` and would **kill the
entire
tool-server**, wiping every device + profiler session. Each engine
exposes a
`fatal: Promise<never>`; the handler try/catches, rejects `fatal`,
evicts the
engine, and every load/query is `Promise.race`'d against it — so a
contained
  fault is a clean rejection and the server stays up.
- **Chunked trace parse.** Perfetto's RPC ring buffer rejects any frame
> 64 MiB,
  so the trace is fed in 32 MiB chunks before `notifyEof()`.

### 2. Android native profiler + the shared/dispatch refactor

**Capture** (`utils/android-profiler/capture.ts`, `detect-app.ts`):
spawns
`adb shell perfetto --txt -c - --background-wait -o
/data/.../argent-<ts>.pftrace`,
piping the TraceConfig on **stdin** (SELinux denies `shell:s0` writes
under
`/data/misc/perfetto-traces/`, so the config can't be staged as a file),
waits for
the PID on stdout, then on stop sends `kill -TERM`, polls `/proc/<pid>`,
and
`adb pull`s the trace.

**Pipeline** (`utils/android-profiler/pipeline/`): PerfettoSQL does the
parsing and
aggregation, so the Android pipeline is intentionally **two files, not
iOS's four**.
The per-hang annotation pass is **batched** — `runBatchedHangFolds`
inlines all
hang windows as a `VALUES` table into one SQL script (two derived views
unioned in
a single terminal SELECT, because the engine returns only the final
statement's
rows) and runs it once against the warm engine.

**The platform-dispatch refactor** —
`native-profiler-start/stop/analyze` shrank
from monolithic iOS files (~160–330 lines each) into **thin routers**
(~60–110
lines) that branch on `api.platform`:

```
native-profiler-start ─┬─ ios?    → platforms/ios.ts      (xctrace)
                        └─ android → platforms/android.ts  (adb perfetto)
```

A new **`utils/profiler-shared/`** layer holds everything both backends
use:

| File | What was hoisted |
| --- | --- |
| `types.ts` | Unified `Bottleneck = CpuHotspot \| UiHang \| MemoryLeak
\| MemoryRssGrowth`, each now carrying a `platform` field; shared
`NativeProfilerAnalyzeResult`; `RECORDING_CAP_MS` |
| `aggregate.ts` | Generic `aggregateCpuHotspots(AggregatorInputRow[])`
— burst windowing (`BURST_GAP_MS=500`), severity banding (RED >15%,
YELLOW 3–15%, <3% dropped). iOS feeds it via a pre-pass; Android feeds
it precomputed SQL rows |
| `lifecycle.ts` | `shutdownChild` SIGINT→SIGTERM→SIGKILL ladder +
`waitForChildExit` (no PID-reuse hazard) |
| `thread.ts` | `normalizeThreadName` for both xctrace and Perfetto
thread names |
| `time-align.ts` | New `buildPerfettoAnchor()` + `windowsOverlap()`
alongside the existing iOS/React anchors |
| `format.ts` | `formatBytes` (moved out of the combined report) |

iOS was refactored to fit this shape: `ios-profiler/lifecycle.ts` and
`types.ts`
become thin re-exports; `pipeline/02-aggregate.ts` shrank from ~200 to
~60 lines
(keeps its iOS-only dominant-function pre-pass, delegates
grouping/bursting/
severity to the shared aggregator). **No iOS behavior change** — same
outputs,
shared internals.

**Session blueprint** (`native-profiler-session.ts`): generic
`capturePid` /
`captureProcess` (was `xctracePid` / `xctraceProcess`), new `platform`
and
`androidOnDeviceTracePath` fields, and **platform-branched dispose**
(iOS SIGKILLs
xctrace; Android `kill -KILL`s perfetto, `rm`s the device trace, and
disposes the
warm WASM engine).

**Drill-down tools**: `profiler-load.ts` learns to restore Android
`.pftrace`
sessions (with an optional metadata sidecar / `app_process` override);
`profiler-stack-query.ts` splits into iOS and Android branches. Unlike
iOS (which
caches `parsedData` in memory), Android **re-queries the `.pftrace`**
per
drill-down — the warm engine is the cache, so each query costs only SQL
execution
time.

### 3. Packaging & release pipeline

The WASM bundle's three third-party artifacts (`trace_processor.wasm`,
`engine_bundle.node.js`, `engine.mjs`) + `LICENSE` are **never
committed**
(`.gitignore` lists the generated files but not the dir). They're built
and
sha256-checksummed in `argent-private` CI, published to
`argent-private-releases`, and on our side:

- **`scripts/download-trace-processor.sh`** (+88, new) — `gh release
download` →
verify tarball sha256 (**FATAL on mismatch** — the blobs are unsigned) →
extract
  → re-verify via `SHA256SUMS` → assert all four present.
- **`scripts/pack-mcp.cjs`** (+60, new) — single pack entry point:
downloads
simulator-server, native binaries, **and** the trace-processor bundle,
then
builds and `npm pack`s `@swmansion/argent`. `package.json`'s `pack` /
`pack:mcp`
  now call this script.
- **`packages/argent/scripts/bundle-tools.cjs`** (heavily reworked,
+574/-...) —
copies `assets/trace-processor/`, `assets/queries/`, and
`argent.tracecfg.pbtxt`
into `packages/argent/assets/`, and regenerates `bundled-meta.ts` to
stamp the
  Perfetto version.
- **`publish.yml` / `publish-next.yml`** — run the download step and
then **verify**:
  all four WASM files present, the wasm magic-byte check
(`head -c4 … | grep $'\x00asm'`), plus the queries dir, TraceConfig,
manifest,
  and Android helper APK.

Asset reorg: `native-devtools-android` now ships `dist/`, `bin/`, and
`assets/`
(SQL queries, TraceConfig, manifest moved under `assets/`); `.gitignore`
swaps the
old per-file `packages/argent/manifest.json` entry for the whole
`packages/argent/assets/` dir.

---

## Testing

15 new test files under `packages/tool-server/test/android-perfetto/`
cover the
engine and pipeline, including:

- `dispatch.test.ts` — start/stop/analyze route to the right platform
backend.
- `aggregate-shared.test.ts` — the shared aggregator on both iOS and
Android-shaped input.
- `hang-folds-batched.test.ts` / `hang-fold.test.ts` — the batched
per-hang SQL fold.
- `hang-severity.test.ts` — jank-reason → RED/YELLOW classification.
- `dispose.test.ts` / `dispose-warm-engine.test.ts` — session dispose
kills the daemon, rms the device trace, and tears down the warm engine.
- `stop-recovery.test.ts` — `profilingActive` is forced false even if
`adb pull` fails (so a retry-start doesn't wedge).
- `run-tp.test.ts` — `renderSqlTemplate` token rendering + the "unused
substitution" guard.
- `manifest-hint.test.ts` — zero-callstack detection emits the manifest
hint.
- `profiler-load.test.ts` — `.pftrace` session restore with/without the
metadata sidecar.

Plus `native-profiler-analyze-failure.test.ts` for the
engine-unavailable banner,
and updated iOS tests for the shared-lifecycle refactor.
`@argent/native-devtools-android` gains its own vitest setup
(`vitest.config.ts`, `tsconfig.test.json`, `test/errors.test.ts`).

---

## Notes & follow-ups

- **Manifest requirement.** Perfetto's `linux.perf` data source needs
`/proc/<pid>/mem` access, granted only to a **debuggable** or
`<profileable
android:shell="true"/>` app. A release build without either silently
produces
zero callstacks; the pipeline detects this and emits a **manifest hint**
instead
  of an "All clear" report.
- **Version pinning lives in the `argent-private` submodule**
(`PERFETTO_VERSION`
/ `PERFETTO_WASM_TAG` / `LYNX_TRACE_PROCESSOR_VERSION`). Bumping is
documented in
`argent-private/docs/ANDROID_PERFETTO.md`. JS↔engine version skew is
structurally
  impossible (version-agnostic decoder + vendored wasm).
- **`disposeWarmEngine` is exported but not wired into session-stop** —
warm
engines are reclaimed only by the idle timer + LRU cap. Acceptable today
(bounded
memory); wire it into session dispose if explicit teardown is ever
needed.
- **Submodule bump.** This PR moves `packages/argent-private` to
`22e55f90` — see the companion `argent-private` PR description,
including its
merge-hygiene note (that branch should pick up latest `argent-private`
main
  before the pointer is finalized).

### Companion docs added in this branch
- `utils/android-profiler/ANDROID_PROFILER_REFERENCE.md` — stack,
capture, queries, manifest requirement.
- `utils/android-profiler/PIPELINE_DESIGN.md` — the "why" (two-file
pipeline, shared-aggregator hoist, re-query-don't-cache, batched fold).

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-09 16:06:24 +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
Ignacy Łątka 80661fad8f feat: Linux support (#249)
This PR makes Linux work.

# AI Summary 

<details>
Brings Argent up on Linux as a host: per-platform npm bundle, renderer
selection that actually works on hybrid-GPU / Wayland / containerized
Linux, hot-boot guarding that survives the SwiftShader composite gap, a
real Linux preflight, and a Wayland-headless E2E job in CI. macOS stays
on
the path it was on before (no `-gpu` flag was passed; AVD config
decided);
nothing about the iOS Simulator behavior changes.

The diff is broader than "ship the Linux artifact" — calling it out
below
because several pieces affect macOS too and reviewers should be primed
for
that rather than skim past expecting "just packaging."

### What's actually in here

**npm packaging (the original framing — still load-bearing)**

| File | Change |
|---|---|
| `packages/argent/scripts/argent-simulator-server.cjs` (new) | Node
dispatcher exposed as the `argent-simulator-server` bin entry. Joins
`process.platform` into the bin path and execs. Forwards
SIGTERM/SIGINT/SIGHUP parent→child so non-TTY supervisors can't orphan
the binary. |
| `packages/argent/scripts/bundle-tools.cjs` | Copies per-platform
binaries from `packages/native-devtools-ios/bin/<platform>/` into
`packages/argent/bin/<platform>/`. darwin required, linux best-effort. |
| `packages/argent/package.json` | `bin.argent-simulator-server` points
at the dispatcher. |
| `packages/native-devtools-ios/src/index.ts` | Resolver joins
`process.platform` into the simulator-server bin path. iOS-only dylibs
and `ax-service` throw `"requires a macOS host"` on Linux callers so
they get the root cause, not "file not found". |
| `scripts/download-simulator-server.sh` | Downloads
`simulator-server-argent-{macos,linux}` into platform-keyed subdirs.
**Purges each platform dir before its download attempt** so a failed
download can't leave stale binaries in place from a prior run. |
| `scripts/dev.cjs` | Local dev copies the current host's binary into
`bin/<process.platform>/` to match the resolver layout. |
| `.github/workflows/publish{,-next}.yml` | Replaces single `test -x
darwin/...` with a per-platform inventory loop. darwin always required;
Linux gated by `ARGENT_REQUIRE_LINUX_BINARY=1` (flip once radon#118
lands). |

**Linux runtime — emulator boot path**

| File | Change |
|---|---|
| `packages/tool-server/src/tools/devices/boot-device.ts` |
`selectGpuMode()`: Linux defaults to `-gpu swiftshader` (`auto` lands on
lavapipe with ~10× cold-boot slowdown; `-gpu host` produces silent
black-window failures on Optimus / NVIDIA+libglvnd / Wayland-on-hybrid /
containers). macOS keeps `auto` (ANGLE→Metal).
`ARGENT_EMULATOR_GPU_MODE` overrides, **validated against `emulator
-help-gpu`'s whitelist** so a typoed value fails fast instead of burning
the whole boot budget. `ARGENT_EMULATOR_NO_WINDOW=1` appends
`-no-window` for headless contexts (CI / containers / Wayland sessions
with no Qt wayland plugin). |
| `packages/tool-server/src/tools/devices/boot-device.ts` (cont.) |
**Cold-boot:** `awaitFirstRealFrame` polls SwiftShader's lockscreen
composite (PNG byte-count via `screencap -p`); blocks `booted:true`
until a real frame shows up. Without this, callers chaining `boot-device
→ screenshot` get an all-black PNG because `sys.boot_completed=1` fires
5–60 s before the first composite. |
| `packages/tool-server/src/tools/devices/boot-device.ts` (cont.) |
**Hot-boot:** `assertScreencapAlive` polls within an 8 s budget instead
of single-probing. The earlier single-probe caught the sticky-blank
state but also killed the emulator on every transient snapshot-restore
blank, forcing "every boot is cold" cycles. Sticky vs transient is
discriminated by whether the budget exhausts. |
| `packages/tool-server/src/utils/linux-preflight.ts` (new) |
`linuxBootDiagnostics()` checks `/dev/kvm` access. `diagnoseAvdSizing()`
warns when a target AVD's `hw.ramSize`/`vm.heapSize` is below the floor
an aggressive RN/Expo app needs (2G/228M avdmanager defaults wedge under
load). |
| `packages/tool-server/src/utils/android-binary.ts` |
`defaultAndroidRoots()` probes typical SDK install locations when
`ANDROID_HOME`/`ANDROID_SDK_ROOT` are unset — **cross-platform**,
benefits macOS launches from GUI-launched MCP hosts (Claude Code Desktop
etc.) where shell rc env doesn't propagate. |

**Cross-platform fixes that landed alongside**

| File | Change |
|---|---|
| `packages/argent-installer/src/utils.ts` | `getLatestVersion()` now
redirects `npm view`'s stderr to `ignore` so `EBADDEVENGINES` warnings
from a consumer project's `package.json` (e.g. Bluesky's pnpm pin) don't
leak to the user's terminal. **Cross-platform fix**, not Linux-specific.
|

**CI**

| File | Change |
|---|---|
| `.github/workflows/wayland-e2e.yml` (new) | Boots an AVD through the
tool-server under headless Weston; asserts `hw.gpu.mode = swiftshader`
and that screenshots + gesture-tap work without a display. Currently
runs on `pull_request` for the affected paths + `workflow_dispatch`.
Pinned to `radon-daily` for the simulator-server until radon#118 ships a
Linux asset to `radon-main`. |

### Tests added in this PR

- `packages/argent/test/dispatcher.test.ts` — e2e against tmpdir-staged
dispatcher + fake `<platform>/simulator-server`: missing-binary error
path, exit-code passthrough, argv passthrough, SIGTERM forwarding.
- `packages/tool-server/test/native-devtools-ios-resolver.test.ts` —
`requireDarwin` gates for dylibs and `ax-service` on Linux, per-platform
`simulator-server` path resolution, missing-binary error,
`ARGENT_NATIVE_DEVTOOLS_DIR` override.
- `packages/tool-server/test/linux-preflight.test.ts` — KVM diagnostic
shape + `diagnoseAvdSizing` (ram low, heap low, both, neither, missing
keys).
- `packages/tool-server/test/boot-device-hotboot.test.ts` — extended
with `it.each([linux→swiftshader, darwin→auto])` so a single CI runner
exercises both GPU branches (was runner-platform-dependent, regressions
could pass on both runners individually). New cases for the wedged-frame
fallback (under fake timers because the polling budget is short but
real-time enough to exceed vitest's 5 s default), the GPU-mode whitelist
rejection, and the cold-boot composite-wait timeout.
- `packages/tool-server/test/android-binary.test.ts` — extended for
`defaultAndroidRoots` probe ordering and HOME pinning.

### Verification

- `tsc --build`: clean apart from pre-existing
`@argent/native-devtools-android` import errors unrelated to this
branch.
- `vitest run` in `packages/tool-server` and `packages/argent`: 689 + 11
= 700 tests passing; the 4 pre-existing test-file load failures
(native-devtools-android missing) reproduce on `main` and are out of
scope.
- `npm pack -w @swmansion/argent`: produces `swmansion-argent-0.8.0.tgz`
with the per-platform layout.
- Wayland-e2e: triggered on this branch — link will be in a PR comment
once the run completes.
- Manually verified end-to-end on a Wayland-on-hybrid-GPU developer
laptop: AVD boots through the tool-server with `-gpu swiftshader`,
screencap returns real frames, gesture tools work.

### Dependencies / what still gates this

- **radon#118** must land before the Linux artifact ships through the
default channel. The download script tolerates the asset being absent
today (best-effort), so this PR is technically safe to merge before —
Linux installs just won't get a working `simulator-server` until the
upstream artifact is published. Once it lands:
  - Drop the `radon-daily` arg in `.github/workflows/wayland-e2e.yml`.
  - Flip `ARGENT_REQUIRE_LINUX_BINARY` to `1` in both publish workflows.
- **wayland-e2e is not a required status check** today (no branch
protection on `main` requires it). Worth turning on before merging
anything else that touches the Linux boot path.

### Related

- radon#118 — produces the `simulator-server-argent-linux` artifact this
PR consumes.
- argent-private#11 — cross-builds the iOS Simulator dylibs on Linux.
Strictly orthogonal: those dylibs are macOS-runtime-only regardless of
where they're built, and this PR keeps them under `dylibs/` with a clean
error path on Linux callers.

</details>

Fixes #246
2026-06-03 15:55:39 +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 ee97c443b7 feat(tool-server): make the update reminder configurable and release-age aware (#276)
## Why

The tools-server shows an update reminder (a `note` appended to every
tool response) once a newer `@swmansion/argent` lands on npm. Two pain
points:

1. **No way to turn it off.** Suppression is in-memory only
(`dismiss-update`, 30-min auto), so it comes back on every server
restart.
2. **It nags about versions you can't install yet.** Registries with a
**minimum-release-age** (package-cooldown) security policy refuse to
install a version until it has been public for N days. Until then the
reminder repeats every day for the whole cooldown window with nothing
the user can do about it — exactly the "reminded for 5+ days" situation.

## What's in this PR

**1. A config option to disable the reminder**
- New env var **`ARGENT_DISABLE_UPDATE_NOTIFICATIONS=1`** (`"1"` or
`"true"`).
- When set, `startUpdateChecker()` is a no-op: **no registry request**
is made and **no note** is ever attached.
- The opt-out is also surfaced inside the reminder text itself, so the
agent can tell the user how to silence it permanently.

**2. Minimum-release-age awareness** (answers "detect the policy and
check the update is actually installable first")
- The checker now fetches the **npm packument**
(`registry.npmjs.org/@swmansion/argent`) instead of `/latest`, because
only the packument carries the `time` map of version → publish
timestamp.
- It detects the machine's policy and only reminds once the latest
version has **aged past** it. Notification is gated on a new
**`updateInstallable`** flag (newer **and** old enough) rather than
`updateAvailable`.
- `updateAvailable` keeps its pure version-comparison meaning; with no
policy, `updateInstallable === updateAvailable` (no behavior change for
users without a policy).

### Is detecting the user's min-age policy possible? Yes — partially.

Each package manager stores it differently, and the tools-server has no
reliable signal for *which* PM installed argent, so we probe every PM
present and take the **most restrictive** (largest) value. Erring toward
over-waiting is intentional: a slightly late reminder beats nagging
about a forbidden version.

| PM | Config key | Unit | Detect via | Min PM version |
|----|-----------|------|-----------|----------------|
| npm | `min-release-age` | days | `npm config get min-release-age` |
11.10.0 |
| pnpm | `minimumReleaseAge` | minutes | `pnpm config get
minimumReleaseAge` | 10.16 |
| yarn (berry) | `npmMinimalAgeGate` | minutes | `yarn config get
npmMinimalAgeGate` | 4.10 |
| bun | `minimumReleaseAge` (bunfig.toml `[install]`) | seconds | no
`config get` — not auto-probed | 1.3 |
| yarn classic | — (unsupported) | — | — | — |

Because bun has no `config get` for this and auto-detection is
best-effort, there's also an explicit override:

- **`ARGENT_MIN_RELEASE_AGE_DAYS`** — positive number of days; wins
outright and skips probing (useful for bun users, locked-down CI, or
pinning the value).

When a policy holds an update back, a single stderr line explains the
silence; no reminder reaches the agent.

## Conservative edge handling
- Network/registry failure → state unchanged (never crashes the server).
- Policy in effect but publish time missing/unparseable → treated as
**not** installable (stay silent rather than nag).
- PM probe errors / PM absent → treated as no policy (0).
- Probes shell out via PATH (`exec`, 3s timeout, constant commands only
— no caller input) so Windows `.cmd`/`.ps1` shims resolve.

## Tests
- `test/min-release-age.test.ts` (new): unit-level parsing, override
precedence, unit normalization, most-restrictive-wins, PM-absent.
- `test/update-checker.test.ts`: rewritten for the packument shape; new
cases for held-by-policy, aged-past-policy, unknown publish time, and
the disable flag (probe mocked so tests never spawn a real PM).
- `test/http-update-note.test.ts`: gate now keys off
`updateInstallable`; added a "available but held by policy → no note"
case.
- Full tool-server suite green (**732 tests**), `tsc --noEmit` and
`typecheck:tests` clean.

## Follow-up (not in this PR)
The env-var reference lives in the `argent-private` submodule
(`docs/environment-variables.md`), which is a separate repo — needs a
companion PR. Suggested rows for the tools-server table:

```
| `ARGENT_DISABLE_UPDATE_NOTIFICATIONS` | `"1"`/`"true"` or unset | disabled | When truthy, disables the update reminder entirely (no registry check, no note). |
| `ARGENT_MIN_RELEASE_AGE_DAYS`         | number                  | _(auto-detect)_ | Minimum age (days) a newer version must reach before it is advertised. Overrides auto-detection of the npm/pnpm/yarn minimum-release-age policy. |
```

## Open questions for review
- **Most-restrictive-across-PMs** vs. detecting a single PM — is "max
wins" the behavior we want, or should it follow `npm_config_user_agent`?
- Should an update held by policy stay fully silent (current), or emit a
softened "available, installable in ~N days" note?

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-01 17:15:12 +02:00
Hubert Gancarczyk de0dbbda66 feat(tools): implement screenshot-diff tool (#229)
## Summary

Adds a new `screenshot-diff` tool for comparing mobile screenshots
during UI QA and visual regression checks. The tool can compare two
saved PNGs, or compare one saved PNG against one live full-resolution
device capture, then returns a compact text summary and generated diff
artifacts.

## Context

Mobile UI validation often needs more than a single screenshot: small
layout, color, typography,
copy, clipping, and regression changes are easy to miss manually. This
feature gives agents a
deterministic before/after comparison path while keeping
`screenshot-diff` as supporting evidence.

## What Changed

- Registered a new `screenshot-diff` tool in the tool server.
- Supports saved PNG comparisons and one-sided live capture via
`captureBaseline` or
`captureCurrent`.
- Adds PNG diffing with fixed internal defaults, changed-region
detection, status-bar-band
ignoring, full-size diff output, and downscaled context diff output.
- Adds optional OCR/text analysis for moved, appeared, disappeared,
changed, and restyled text.
- Updates MCP content rendering to attach the downscaled context diff
image plus the text summary.

1. Capture a stable full-resolution baseline.
2. Perform an interaction or apply a UI change.
3. Compare the current screen against the baseline.
4. Review a summarized diff plus generated artifacts.
2026-06-01 15:04:25 +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