npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@circle-fin/arc-studio-cli

v1.0.5

Published

Arc Studio from the terminal: build and deploy onchain apps, or drive Arc Studio as a subagent from any AI coding tool

Readme

Arc Studio CLI

Arc Studio from the terminal: a standalone coding-agent experience (arc-studio opens an interactive session) and a subagent surface for AI coding tools (arc-studio run --output json). The agent, inference, contracts pipeline, and sandboxes all stay server-side. Plan: docs/plans/012-origin-cli.md · launch: docs/plans/013-origin-cli-launch.md.

Install

npm install -g @circle-fin/arc-studio-cli
# or
pnpm add -g @circle-fin/arc-studio-cli

Requires Node.js ≥ 20. After install, arc-studio login opens your browser to authorize — the token lands in the macOS Keychain (falls back to ~/.arc-studio/credentials.json, 0600, on other platforms).

Try it (team)

Two paths, depending on what you have:

A. Against staging — no local backend needed.

  1. Build the CLI from this branch: pnpm install && pnpm --filter @circlefin/arc-studio-cli build, then alias arc-studio="node $PWD/cli/bin/arc-studio.mjs".
  2. arc-studio login --api-url https://studio-staging.arc.io — opens your browser to authorize the CLI; the token lands in your keychain and server-rotated refreshes are adopted automatically.
  3. Play: arc-studio (TUI), arc-studio run … --output json, run --file, pull/ls/cat, deployments, attach, open, cost, apps.

Advanced (only if staging device-link is down): arc-studio login --cookie --api-url https://studio-staging.arc.io reads the __session cookie from stdin with echo off — paste the cookie value at the prompt, never on the command line (it lands in shell history). The cookie is equivalent to your login: don't share it or paste it into Slack; use your own, and let the CLI own it after pasting (heavy parallel use of the same browser session can race Okta's refresh-token rotation). Not available on staging until this branch deploys: arc-studio tokens, arc-studio run --detach.

B. Fully local — everything works, including device-link login. pnpm db:migrate (applies V020), pnpm dev (note the port), build + alias as above, then arc-studio login --api-url http://localhost:<port> → browser opens /cli-auth → Authorize. Needs a working .env.local (E2B key + an E2B_TEMPLATE tag that exists in your E2B account — publish via pnpm build in sandbox-templates/origin-e2b-template if not). Full runbook: docs/plans/014-origin-cli-eng-design.md §11.

Setup

pnpm install
pnpm --filter @circlefin/arc-studio-cli build     # compile to dist/ (bin: arc-studio)
arc-studio login                                  # opens your browser; click Authorize

arc-studio login uses a device-link flow: the browser page mints a revocable personal access token that lands in the macOS Keychain (file fallback ~/.arc-studio/credentials.json, 0600, elsewhere). --paste for SSH/headless; arc-studio tokens list|revoke to manage; ARC_STUDIO_API_URL / --api-url to target another backend. During development run via npx tsx src/index.ts <cmd> from cli/.

Interactive session

arc-studio        # bare = the session: onboarding on first run, resumes this directory's app after

Claude Code-style TUI: streamed responses, live todo checklist, file/command activity, deployments and preview surfaced at turn end, and plan-mode questions as an interactive picker (arrow keys + space/enter, esc to skip when allowed). Type / for a command palette; slash commands: /help /new /sessions /preview /open /deployments /cost /ls /cat /pull /exit. arc-studio chat remains as a plain-text REPL for dumb terminals.

URLs (/preview, /open) render as clickable OSC 8 hyperlinks on terminals that support them; set ARC_STUDIO_NO_HYPERLINKS=1 to force plain-text URLs instead (also disabled automatically by NO_COLOR, non-TTY output, and terminals known not to support them).

Subagent / scripting surface

arc-studio run "write and deploy an ERC-20 called DEMO on Arc testnet" --output json
arc-studio run "port this vault" --file src/IVault.sol            # reference (read-only)
arc-studio run "add a pause guard" --file src/Vault.sol:contracts/Vault.sol   # round-trip
arc-studio run "audit these" --file contracts/     # directory expanded recursively
arc-studio run "audit these" --file "contracts/**/*.sol"   # quoted globs too
arc-studio run "long deploy" --detach            # 202, survives disconnect
arc-studio run "long deploy" --timeout 90        # bound the attached wait (0 waits indefinitely)
arc-studio run --prompt-file ./prompt.txt --output json   # long/multi-line prompt, no shell quoting
arc-studio run - < prompt.txt                    # or read the prompt from stdin
arc-studio attach [--session <name>]             # wait for the detached turn's result
arc-studio clone circlefin/stablecoin-evm        # pull a GitHub repo into the app sandbox
arc-studio pull --out ./onchain --paths "contracts/**"
arc-studio pull --changed --dry-run --diff       # what would this turn change locally?
arc-studio run/pull/ls/cat --app <appId>         # target any owned app by id (e.g. web-created, no local session)
arc-studio deployments/preview/open/cost --app <appId>   # same, for inspection
arc-studio ls / cat <path>                       # inspect the workspace without pulling
arc-studio deployments --json / preview / open / cost --json / apps --json / sessions --json
arc-studio pause --app <appId>                    # free a sandbox slot after a 429 active-sandbox-limit
arc-studio doctor                                 # support diagnostics
arc-studio agent-guide                            # full integration doc for AI agents

--output json emits one result document: status (completed|needs_input|budget_exceeded|error → exit 0/3/4/1), ids, finalText, todos, filesChanged, fileDiffs (per-file unified hunks + line counts), contextFiles, workspaceFiles, deployments (address/network/explorer/txHash), artifacts, token-less previewUrl, webUrl, costUsd, questions, traceId, budget. Answer needs_input via --answers-json '[...]'. A budget_exceeded turn still emits the document, with budget carrying {scope, usdLimit, windowMs, resetsAt}, whether the server refused it pre-flight or aborted it mid-stream.

Getting code in and out

--file sends local files into the sandbox before the turn starts and tells the agent they are there — the way to hand Arc Studio an existing codebase without pasting it into a prompt (prompts are capped at 10,000 characters). Repeatable; takes a file, a directory (recursive), or a quoted glob. Two forms:

  • Reference--file src/IVault.sol lands read-only in context/, for things the output must honor (an interface, a spec, an ABI). Relative paths keep their shape so sibling imports still resolve.
  • Round-trip--file src/Vault.sol:contracts/Vault.sol lands at that workspace path, the agent edits it in place, and pull --changed returns it there with real before/after diffs.

Destinations are sandbox-relative, and the sandbox is a Vite + Foundry app: Solidity under contracts/, web code under src/. A trailing slash means "into this directory" like cp (V.sol:contracts/contracts/V.sol); a bare V.sol:contracts is rejected as ambiguous; for a directory or glob the destination is a path prefix. Limits: 100 files, 1 MB each, 10 MB total (internal Circle users: 1,000 files, 10 MB each, 100 MB total). Credential files (.env*, .npmrc, keys, certs) are refused as both source and destination, matched case-insensitively so .ENV cannot slip past on a case-insensitive filesystem.

A successful round-trip run records the bytes it uploaded in ./.arc-studio/, so a later pull --out . overwrites your file cleanly instead of reading your own upload as a local edit and demanding --force. It is written only when the destination is also a real path in the directory you ran from, and only helps a pull back into that same tree — pull reads its manifest from <out>/.arc-studio/, so pulling elsewhere falls back to normal conflict detection.

A directory walk skips dot-directories and the usual dependency/build output (node_modules, dist, build, out, cache, artifacts, broadcast, target, coverage, typechain-types, .next, .turbo), but .gitignore is not consulted and lib/ is walked (it is real source in plenty of projects, and Foundry keeps its dependencies there as submodules). So --file . on a contracts repo will usually trip the 100-file cap — pass the directory or glob you actually mean (--file contracts/, --file "contracts/**/*.sol") rather than the repo root.

arc-studio clone <repo> is the other way in, and the right one for a whole repository. It takes owner/repo, an https GitHub URL, or a git@ address, and clones server-side into the app's sandbox — no upload, so the file and byte caps above do not apply. The app needs a live sandbox first, so run one turn before you clone. A public repository needs nothing else. Cloning a private repository is not supported from the CLI: the command reports a 404 that names the private case, because an anonymous clone cannot tell "private" from "absent". When the target directory already exists with uncommitted changes, the command stops and asks for --force.

run --app <appId> targets an app you already own without a local session — useful for one built in the web workspace. It opens a new thread (files persist, chat history doesn't) and validates the id first, because /api/chat would otherwise create a fresh app from a typo'd UUID. The inspection commands take --app too; cost --app bills the app's most recently updated thread, since --app names no thread.

Surviving a long turn

An attached run holds the HTTP stream, and the server cancels the turn when that connection closes — so a client-side kill ends real work. Three things make that recoverable. The session is saved as soon as the app exists, not at the end, and run prints the session name, app id and web URL to stderr at that moment. A quiet tool call (deploys and audits go minutes without output) produces a periodic "still connected" line, so silence is distinguishable from a hang. And --timeout <minutes> (default 30, 0 waits indefinitely) bounds the wait: on expiry, or on any dropped stream, run reads the persisted turn state and still prints a result document instead of throwing, so the caller always gets a status plus ids to act on — and the error names the recovery path (arc-studio attach, or app-level arc-studio ls/pull --app). For work that may outlast the client, prefer --detach and arc-studio attach.

pull is diff-aware and non-destructive. It records a hash manifest in <out>/.arc-studio/, so it can tell a file you edited locally from one it pulled and left alone — a locally-modified file is skipped (exit 2) unless you pass --force. .env* and context/ are excluded by default (--all includes them); --changed limits the pull to the files the session's last turn wrote — quick, but it misses files created by shell commands, so prefer --paths when the output has siblings it imports. It warns when a delivered file references context/, since that directory is not delivered and such a file will not build in your project.

fileDiffs are reconstructed from the turn's write/edit tool calls. An edit_file diff covers the replaced fragment only, so it carries isFragment: true and no absolute line numbers; files produced by shell commands (compiler output, generated ABIs) have no per-turn diff — pull --dry-run is what surfaces those. Long lines and oversized diffs are clipped so a minified file can't bloat the result document (truncated: true). After arc-studio attach, diffs are rebuilt from stored 8-line previews and are always truncated.

Binary files are staged byte-exact (base64 on the wire), but pull/cat read through a text API, so a binary pulled back is not byte-exact — a pre-existing limit of /api/sandbox-file-batch-read, not of staging.

Symlinks inside a --file directory are skipped (this is also what stops a symlink loop from hanging the walk). Only a root-level context/ is excluded from pull — a nested src/context/ is treated as ordinary app code.

The AI Toolkit asset lives at agent-skills/arc-studio/ — the skill, the subagent definition, and the CLI bundled as a single executable. It sits inside this package (and in files) so the same directory serves both channels: the Toolkit installs it internally, and npm pack ships it for the public release. claude-plugins/arc-studio/ wraps it for the Toolkit catalog, symlinking the skill in at skills/arc-studio and keeping its own copy of the subagent (every host skips a symlinked agent file on a raw checkout). The catalog publishes the plugin only: the sync drops a standalone skill whose name matches one bundled in a plugin from the same repo, which is what makes the plugin a single install carrying skill + CLI + subagent. Rebuild the bundle with pnpm --filter @circlefin/arc-studio-cli bundle after touching src/tests/plugins/arc-studio-cli-asset.test.ts rebuilds it and fails if the committed copy does not match. Tests: pnpm --filter @circlefin/arc-studio-cli test.