feat(sdk): expose the flows CLI as a mountable relay CLI surface

Merged
AG
2mo ago
+1944-113

Part of a cross-repo change mounting every Relay product into the agent-relay
CLI. This repo's half: @relayflows/sdk gains a ./relay-cli export that
agent-relay mounts as agent-relay flows, without relay reimplementing a
single flows command.

What this does

  • @relayflows/sdk/relay-cli exports createRelayCliSurface() returning a
    contract-v1 surface. run delegates straight to the runCli this package
    already ships, and commands is projected from the same CLI_VERBS table
    parseArgs dispatches on — so the declared tree and the dispatcher read from
    one source rather than two that can rot apart. A drift test walks both.
  • runCli no longer touches process state. Output routes through the
    injected CliIo, and it returns an exit code rather than exiting.
    packages/relayflows/bin/flows.js keeps working unchanged on the same path.
  • Structurally typed against @agent-relay/cli-surface without importing
    it, so the published package gains no runtime dependency on relay.

Why the declared tree matters

agent-relay renders help from this spec rather than forwarding --help to the
product — forwarding would print flows run ... when the user typed
agent-relay flows run. That makes commands user-visible output, not an
internal detail, so descriptions and flags are written to be read.

v1 → v2 mapping

relay is deprecating its v1 relayflows surface against this one. The scoping
that came out of it:

  • agent-relay cloud run --relayflow-version v1 → agent-relay flows run --cloud
  • agent-relay node workflow run|logs|sync (pinned to @relayflows/cli 1.0.1)
    → agent-relay flows run|replay|sync
  • cloud schedule/schedules have no v2 equivalent — tick is a local
    cadence, not hosted scheduling — so relay keeps them supported and visible
    rather than pointing at a command that cannot do the job.

Verification

  • tests/relay-cli-surface.test.ts — 29 tests.
  • Verified mounted: agent-relay flows --help lists all 14 commands, and
    agent-relay flows check <fixture> runs the real compiler to CHECK PASSED.

Release

Nothing is published. agent-relay pins the published version and currently
reports "upgrade @relayflows/sdk" until this ships.

🤖 Generated with Claude Code


[!NOTE]
Medium Risk
Changes cloud sync patch application (exclusions, multi-path, dry-run) and embeddable CLI cancellation; mistakes could skip or mis-apply agent output in local trees, though behavior is heavily tested.

Overview
Adds @relayflows/sdk/relay-cli so agent-relay can mount flows as agent-relay flows: createRelayCliSurface() projects CLI_VERBS into the host command tree and delegates run to runCli, with drift and contract tests so declared help and parsing stay aligned. runCli now accepts an optional AbortSignal (no global SIGINT/SIGTERM when embedded); the standalone flows binary behavior is unchanged.

flows sync is extended to match documented Cloud behavior: runtime paths (.agent-bin/**, trajectories, relayfile state, etc.) are skipped via git apply --exclude on both check and apply; --dry-run prints or JSON-embeds the patch; multi-path runs are inspectable in dry-run but applying stays sync_unsupported. Parser/help fixes include build --verify + --json, bundle deploy --json, and serve-webhook defaulting --data-dir.

Reviewed by Cursor Bugbot for commit d5f987ca7e209ceada5fd1203b1deb60ad75281e. Bugbot is set up for automated code reviews on this repo. Configure here.


Summary by cubic

Exposes the flows CLI as a mountable relay CLI surface (agent-relay flows) and turns flows sync into a drop-in replacement for agent-relay cloud sync. The parsers now accept what the command surface advertises, so users following the host's help no longer hit exit 2.

New Features

  • createRelayCliSurface() returns a contract-v1 surface; run delegates to runCli, and commands projects from the same CLI_VERBS table that gates parseArgs.
  • Compile-time assertions force every ParsedArgs variant to be claimed by a declared verb, and vice versa.
  • The surface claims the answer, schedule, schedules, unschedule verbs and the --no-connect flag on the cloud-handoff verbs.
  • runCli accepts an optional AbortSignal; with one supplied it installs no process signal handlers, while the standalone flows binary keeps its SIGINT/SIGTERM behavior unchanged.
  • The surface is structurally typed against @agent-relay/cli-surface without importing it, so the published package gains no runtime dependency on relay.
  • @agent-relay/cli-surface is a devDependency pinned to ^12.2.4; the sibling-checkout alias was dropped so tests resolve the published package.

Bug Fixes

  • flows sync excludes the agent runtime's own bookkeeping paths from applied patches, reports them as skipped, and uses identical exclusions on both git apply invocations.
  • flows sync --dry-run prints the patch and applies nothing; under --json the diff travels in the patch field.
  • Multi-path runs are shown per path with --dry-run; applying stays refused (sync_unsupported) since they target different repositories.
  • serve-webhook now defaults --data-dir like the other verbs; build --verify is an ordinary flag usable with --json (--out stays refused); deploy --json works on the bundle form.
  • run --cloud passes the caller's signal through to the connect preflight, so submissions there no longer die with a ReferenceError and aborted submissions surface as submission_aborted.
  • An empty multi-path patch now reports NO CHANGES instead of patch_conflict.