Skip to content

feat(agents): expand AR CLI surface#8237

Draft
VaibhavAcharya wants to merge 9 commits intomainfrom
feat/agents-cli-revamp
Draft

feat(agents): expand AR CLI surface#8237
VaibhavAcharya wants to merge 9 commits intomainfrom
feat/agents-cli-revamp

Conversation

@VaibhavAcharya
Copy link
Copy Markdown
Contributor

@VaibhavAcharya VaibhavAcharya commented May 7, 2026

Summary

This expands the agents CLI from the small "create / list / show / stop" surface into the full set of operations the agent runners API supports, so day-to-day workflows like reviewing the diff, opening a PR, committing to a branch, publishing to production, reverting to a session, or archiving a task all live in the CLI now. The static model whitelist is gone, replaced with a live fetch from the AI gateway so adding a new model upstream does not require shipping a CLI patch. The API calls are pulled out into a typed module so the individual command files stay focused on UX. Existing commands grow filters, watch mode, JSON/NDJSON output, attachments, and a few quality-of-life touches like detecting the local git branch and warning about uncommitted changes. Docs are regenerated, the original integration tests are updated to match the new contract, and there is now coverage for every new subcommand.

Expands netlify agents to use the full agent_runners API surface. Adds 7 new commands, extends create/list/show/stop with new flags, centralizes the API calls into a typed api.ts module, and replaces the static model whitelist with a live fetch from /api/v1/ai-gateway/providers.

New commands: follow-up, open, diff, pr, commit, publish, revert, archive, redeploy. New flags on existing commands: --watch, --session, --mode, --from-deploy, --parent, --dev-server-image, --attach, --branch/--user/--title/--since/--until/--page/--per-page/--account, --ndjson, --yes. Smarter defaults (local git branch detection, dirty/unpushed warnings) and renderer upgrades (hierarchical sessions, token + credit usage, conditional action hints, append-only watch ticker).

Test plan

  • npm run lint, npm run typecheck, npx prettier --check src/commands/agents/ — all clean
  • All 14 commands exercised end-to-end against the live API on a test site
  • Real diffs, file attachments (S3 upload), follow-up sessions, redeploy, archive, revert, publish all verified
  • Error paths verified for fake model IDs, invalid dates, missing files, no-repo PR/commit attempts
  • Integration test coverage added for every new subcommand

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 7, 2026

Review Change Stack

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: ad6c36c7-759d-4456-9ded-c2c130bea69c

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

This PR adds a typed Agents API client and expanded TypeScript types/constants, implements nine new agents subcommands (archive, commit, diff, follow-up, open, pr, publish, redeploy, revert), refactors agents:create/list/show/stop with multi-step flows (git detection, attachments, watch/polling, pagination/filters), adds attachment upload support, updates CLI wiring/examples, refreshes docs, and adds comprehensive integration tests and mock-server enhancements.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title 'feat(agents): expand AR CLI surface' clearly and concisely summarizes the main objective of the PR—expanding the agent runner CLI to cover more operations and flags.
Description check ✅ Passed The description is directly related to the changeset, providing a comprehensive summary of the expanded agents CLI surface, new commands, flags, and improvements made throughout the PR.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/agents-cli-revamp

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 7, 2026

📊 Benchmark results

Comparing with 07fa4d1

  • Dependency count: 1,061 (no change)
  • Package size: 357 MB (no change)
  • Number of ts-expect-error directives: 355 (no change)

@VaibhavAcharya
Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 8, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 9

🧹 Nitpick comments (2)
docs/index.md (1)

28-28: 💤 Low value

Use smart apostrophes per documentation style guide.

The linter prefers smart apostrophes (') over straight quotes in user-facing text.

✏️ Suggested fix
-| [`agents:commit`](/commands/agents#agentscommit) | Commit an agent task's changes directly to a branch  |
+| [`agents:commit`](/commands/agents#agentscommit) | Commit an agent task's changes directly to a branch  |
-| [`agents:publish`](/commands/agents#agentspublish) | Publish an agent task's changes to production  |
+| [`agents:publish`](/commands/agents#agentspublish) | Publish an agent task's changes to production  |

Also applies to: 35-35

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/index.md` at line 28, Replace the straight apostrophe in the user-facing
table entry for `agents:commit` — the text "Commit an agent task's changes
directly to a branch" — with a smart apostrophe (’) to match the documentation
style guide; update the same phrase occurrence noted at the other location as
well so both instances use the smart apostrophe.
src/commands/agents/agents-list.ts (1)

154-163: 💤 Low value

Low-risk ReDoS pattern, but easily mitigated.

The RegExp is constructed from status.toUpperCase() where status comes from runner.state. While expected values are safe alphanumeric strings from AGENT_STATES, the API response could theoretically contain unexpected characters. The practical risk is low since states are typically controlled server-side, but escaping is a simple fix.

♻️ Optional: Escape regex metacharacters
+const escapeRegex = (str: string): string => str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
+
 const colorizeStatuses = (tableOutput: string, runners: AgentRunner[]): string => {
   let output = tableOutput
   const statuses = new Set(runners.map((runner) => runner.state ?? 'unknown'))
   for (const status of statuses) {
     const plain = status.toUpperCase()
     const colored = formatStatus(status)
-    output = output.replace(new RegExp(`\\b${plain}\\b`, 'g'), colored)
+    output = output.replace(new RegExp(`\\b${escapeRegex(plain)}\\b`, 'g'), colored)
   }
   return output
 }

This addresses the static analysis warning about regex construction from variables.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/commands/agents/agents-list.ts` around lines 154 - 163, The RegExp in
colorizeStatuses is built from untrusted status strings (from runners[i].state)
and should escape regex metacharacters to avoid ReDoS or incorrect matching;
update colorizeStatuses to escape status (before calling status.toUpperCase())
when creating new RegExp (or build the regex via RegExp(escape(status), 'g')),
or alternatively use a safe literal-based replace strategy (e.g., split/join or
indexOf loop) and still call formatStatus(status) for replacement; reference the
colorizeStatuses function, the status variable derived from runner.state, and
formatStatus to locate and change the code.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/commands/agents.md`:
- Line 31: Replace the straight ASCII apostrophe in each occurrence of the
phrase "task's" with a typographic (smart) apostrophe so it reads "task’s" to
satisfy the docs linter; locate the occurrences by searching for the literal
token "task's" (e.g., near the `agents:commit` command entry and other places
where "task's" appears) and update them to "task’s" everywhere in the document.

In `@src/commands/agents/agents-commit.ts`:
- Around line 20-23: The check should use process.stdin.isTTY not
process.stdout.isTTY and the branch flag must be trimmed before validation:
change the gating condition where targetBranch is computed to test
process.stdin.isTTY, and normalize options.branch (e.g., assign trimmed value to
targetBranch or similar) so whitespace-only values don't bypass validation;
update any uses of targetBranch/options.branch (including the inquirer prompt
validator referenced around the prompt) to rely on the trimmed value.

In `@src/commands/agents/agents-create.ts`:
- Around line 64-75: The code interpolates the git ref name (upstream) into a
shell string (used by run) causing possible command injection; change the logic
in agents-create.ts to avoid shell interpolation by calling a safe API (e.g.,
child_process.spawnSync/execFileSync or your run wrapper variant that accepts
args) to run git rev-list with upstream passed as an argument array instead of
via template string; update the block that computes hasUnpushedCommits (the
upstream variable, the run call that executes `git rev-list --count
${upstream}..HEAD`, and any run helper used) to pass upstream as a separate
argument so special characters are not interpreted by the shell.

In `@src/commands/agents/agents-diff.ts`:
- Around line 20-23: The current parsing uses Number.parseInt which accepts
malformed inputs like "1abc" or "12.34"; update the validation in the block
handling input/name to first check the raw input with a strict regex (e.g.
/^\d+$/) and throw the same Error(`--${name} must be a positive integer`) if it
doesn't match, then safely parse with Number.parseInt(input, 10) (or Number) and
ensure value > 0; reference the variables input, name and the existing
Number.parseInt usage to locate and change the logic.

In `@src/commands/agents/agents-redeploy.ts`:
- Around line 23-27: The auto-selection currently calls
api.listAgentRunnerSessions(id, { page: 1, per_page: 20 }) and only inspects
that first page (sessions -> latestDone), which can miss completed sessions;
change the logic in agents-redeploy.ts to paginate through
listAgentRunnerSessions results until a session with state === 'done' is found
(or all pages are exhausted) instead of relying solely on page=1, per_page=20;
implement a loop/request pattern using api.listAgentRunnerSessions(id, { page:
n, per_page: m }) to fetch subsequent pages, stop early when latestDone is
located, and preserve the existing behavior of allowing an explicit --session
override.

In `@src/commands/agents/api.ts`:
- Around line 200-206: The listAiGatewayProviders function currently calls fetch
without the Authorization header; update it to pass the same request init used
by other methods (call getInit() and use that as the second argument to fetch or
merge it into an init object) so the Authorization header is included when
fetching `${baseUrl}/ai-gateway/providers`; keep the existing providersCache and
existing throwForStatus usage intact and ensure the response handling (await
response.json()) remains unchanged.

In `@src/commands/agents/attachments.ts`:
- Around line 51-55: The fetch call uploading attachments (the PUT to uploadUrl)
can hang indefinitely; add a timeout/signal to abort stalled uploads by creating
an AbortController (or using AbortSignal.timeout) and pass its signal into the
fetch options; ensure you clear or handle the timeout appropriately and surface
the abort error in the surrounding function (e.g., the upload logic in
src/commands/agents/attachments.ts where putResponse is awaited) so the command
fails fast instead of hanging.

In `@tests/integration/commands/agents/agents-archive.test.ts`:
- Around line 18-23: The mock route for the accounts endpoint in the baseRoutes
array doesn't include the minimal=true query so the test request GET
/api/v1/accounts?minimal=true isn't matched; update the accounts entry in
baseRoutes (the object with path 'accounts') to include the query (e.g., change
path to 'accounts?minimal=true' or otherwise match query shape) so the mocked
response is used and the CI warning is resolved.

In `@tests/integration/commands/agents/agents-follow-up.test.ts`:
- Around line 81-99: The test 'should pass agent and model in the request body'
currently mocks both provider endpoints which masks wrong API paths; remove the
redundant mock for the generic 'ai-gateway/providers' and keep only the specific
'agent_runners/ai-gateway/providers' route in the routes array so the test only
matches the intended endpoint (ensure the route object with path
'ai-gateway/providers' is deleted and the rest of the routes, including
'agent_runners/ai-gateway/providers', remain).

---

Nitpick comments:
In `@docs/index.md`:
- Line 28: Replace the straight apostrophe in the user-facing table entry for
`agents:commit` — the text "Commit an agent task's changes directly to a branch"
— with a smart apostrophe (’) to match the documentation style guide; update the
same phrase occurrence noted at the other location as well so both instances use
the smart apostrophe.

In `@src/commands/agents/agents-list.ts`:
- Around line 154-163: The RegExp in colorizeStatuses is built from untrusted
status strings (from runners[i].state) and should escape regex metacharacters to
avoid ReDoS or incorrect matching; update colorizeStatuses to escape status
(before calling status.toUpperCase()) when creating new RegExp (or build the
regex via RegExp(escape(status), 'g')), or alternatively use a safe
literal-based replace strategy (e.g., split/join or indexOf loop) and still call
formatStatus(status) for replacement; reference the colorizeStatuses function,
the status variable derived from runner.state, and formatStatus to locate and
change the code.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 0895fa4d-bea9-42a9-aecf-86df6c49931e

📥 Commits

Reviewing files that changed from the base of the PR and between 07fa4d1 and f934bc0.

📒 Files selected for processing (34)
  • docs/commands/agents.md
  • docs/index.md
  • src/commands/agents/agents-archive.ts
  • src/commands/agents/agents-commit.ts
  • src/commands/agents/agents-create.ts
  • src/commands/agents/agents-diff.ts
  • src/commands/agents/agents-follow-up.ts
  • src/commands/agents/agents-list.ts
  • src/commands/agents/agents-open.ts
  • src/commands/agents/agents-pr.ts
  • src/commands/agents/agents-publish.ts
  • src/commands/agents/agents-redeploy.ts
  • src/commands/agents/agents-revert.ts
  • src/commands/agents/agents-show.ts
  • src/commands/agents/agents-stop.ts
  • src/commands/agents/agents.ts
  • src/commands/agents/api.ts
  • src/commands/agents/attachments.ts
  • src/commands/agents/constants.ts
  • src/commands/agents/types.ts
  • src/commands/agents/utils.ts
  • tests/integration/commands/agents/agents-archive.test.ts
  • tests/integration/commands/agents/agents-commit.test.ts
  • tests/integration/commands/agents/agents-diff.test.ts
  • tests/integration/commands/agents/agents-follow-up.test.ts
  • tests/integration/commands/agents/agents-list.test.ts
  • tests/integration/commands/agents/agents-open.test.ts
  • tests/integration/commands/agents/agents-pr.test.ts
  • tests/integration/commands/agents/agents-publish.test.ts
  • tests/integration/commands/agents/agents-redeploy.test.ts
  • tests/integration/commands/agents/agents-revert.test.ts
  • tests/integration/commands/agents/agents-show.test.ts
  • tests/integration/commands/agents/agents-stop.test.ts
  • tests/integration/utils/mock-api.ts

Comment thread docs/commands/agents.md Outdated
Comment thread src/commands/agents/agents-commit.ts
Comment thread src/commands/agents/agents-create.ts
Comment thread src/commands/agents/agents-diff.ts Outdated
Comment thread src/commands/agents/agents-redeploy.ts Outdated
Comment thread src/commands/agents/api.ts
Comment thread src/commands/agents/attachments.ts Outdated
Comment thread tests/integration/commands/agents/agents-archive.test.ts
Comment thread tests/integration/commands/agents/agents-follow-up.test.ts
@VaibhavAcharya
Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 8, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant