Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
9b3829d
feat(agents): expand AR CLI surface with new commands, filters, and l…
VaibhavAcharya May 7, 2026
33399ef
Merge branch 'main' into feat/agents-cli-revamp
VaibhavAcharya May 7, 2026
e9dcb9a
docs(agents): regenerate command docs for new subcommands
VaibhavAcharya May 7, 2026
2cbd424
test(agents): align integration tests with new CLI surface
VaibhavAcharya May 7, 2026
0e3f652
test(agents): add integration tests for new agent subcommands
VaibhavAcharya May 8, 2026
f934bc0
feat(agents): add --yes confirm to archive and bump session list default
VaibhavAcharya May 8, 2026
d9608bb
fix(agents): apply CodeRabbit review feedback
VaibhavAcharya May 8, 2026
59b6595
fix(agents): harden review concerns (concurrency cap, bounded paginat…
VaibhavAcharya May 8, 2026
5584c32
fix(agents): suppress prompts in --json mode; clearer 404 messages
VaibhavAcharya May 8, 2026
c42c935
perf(agents): drop AGENT column from list to remove N+1 session lookup
VaibhavAcharya May 8, 2026
c42005c
fix(agents): align CLI surface with React UI; add rename and sync
VaibhavAcharya May 11, 2026
afd8f61
fix(agents): match deeper UI behavior (publish guard, title sanitizat…
VaibhavAcharya May 11, 2026
525a03d
fix(agents): consistent 404 messages; carry over follow-up agent/mode…
VaibhavAcharya May 11, 2026
bc58a5c
fix(agents): richer show output (sha, per-session user/published/disc…
VaibhavAcharya May 11, 2026
c3b436b
fix(agents): respect NETLIFY_WEB_UI host; bounded watch retries; clea…
VaibhavAcharya May 11, 2026
e540f67
fix(agents): address CodeRabbit review and CI failures
VaibhavAcharya May 11, 2026
dd7f7ea
chore(docs): regenerate docs/commands with Node 24 (matches CI)
VaibhavAcharya May 11, 2026
b8fa5c0
refactor(agents): align api.ts method names with bitballoon @operatio…
VaibhavAcharya May 12, 2026
7727e26
fix(agents): friendlier 404 message when --account lacks agent task a…
VaibhavAcharya May 12, 2026
65e62b6
Merge remote-tracking branch 'origin/main' into agent/cli/pr-8237-ci-fix
VaibhavAcharya May 14, 2026
34263f3
refactor(agents): address review — rename "agent task" to "agent run"…
VaibhavAcharya May 14, 2026
b667d93
chore(docs): regenerate agents.md with Node 24 (matches CI)
VaibhavAcharya May 14, 2026
7a7fb3c
test(help): update snapshot for "agent runs" rename
VaibhavAcharya May 14, 2026
c929c39
Merge branch 'main' into feat/agents-cli-revamp
VaibhavAcharya May 18, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
419 changes: 401 additions & 18 deletions docs/commands/agents.md

Large diffs are not rendered by default.

21 changes: 16 additions & 5 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,25 @@ netlify [command] help
<!-- AUTO-GENERATED-CONTENT:START (GENERATE_COMMANDS_LIST) -->
### [agents](/commands/agents)

Manage Netlify AI agent tasks
Manage Netlify AI agent runs

| Subcommand | description |
|:--------------------------- |:-----|
| [`agents:create`](/commands/agents#agentscreate) | Create and run a new agent task on your site |
| [`agents:list`](/commands/agents#agentslist) | List agent tasks for the current site |
| [`agents:show`](/commands/agents#agentsshow) | Show details of a specific agent task |
| [`agents:stop`](/commands/agents#agentsstop) | Stop a running agent task |
| [`agents:archive`](/commands/agents#agentsarchive) | Archive an agent run |
| [`agents:commit`](/commands/agents#agentscommit) | Commit an agent run’s changes directly to a branch |
| [`agents:create`](/commands/agents#agentscreate) | Create and start a new agent run on your site |
| [`agents:diff`](/commands/agents#agentsdiff) | Print the code changes produced by an agent run |
| [`agents:follow-up`](/commands/agents#agentsfollow-up) | Send a follow-up prompt to an existing agent run |
| [`agents:list`](/commands/agents#agentslist) | List agent runs for the current site |
| [`agents:open`](/commands/agents#agentsopen) | Open the agent run preview, dashboard, or pull request in a browser |
| [`agents:pr`](/commands/agents#agentspr) | Open a pull request for an agent run |
| [`agents:publish`](/commands/agents#agentspublish) | Publish an agent run’s changes to production |
| [`agents:redeploy`](/commands/agents#agentsredeploy) | Redeploy an agent run by reapplying its existing changes (no AI inference) |
| [`agents:rename`](/commands/agents#agentsrename) | Rename an agent run |
| [`agents:revert`](/commands/agents#agentsrevert) | Revert an agent run to a specific session (sessions after it are discarded) |
| [`agents:show`](/commands/agents#agentsshow) | Show details of a specific agent run |
| [`agents:stop`](/commands/agents#agentsstop) | Stop a running agent run |
| [`agents:sync`](/commands/agents#agentssync) | Bring an agent run up to date with the latest code from its base branch |


### [api](/commands/api)
Expand Down
54 changes: 54 additions & 0 deletions src/commands/agents/agents-archive.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import type { OptionValues } from 'commander'
import inquirer from 'inquirer'

import { chalk, exit, log, logAndThrowError, logJson } from '../../utils/command-helpers.js'
import { startSpinner, stopSpinner } from '../../lib/spinner.js'
import type BaseCommand from '../base-command.js'
import { createAgentsApi } from './api.js'

interface AgentArchiveOptions extends OptionValues {
json?: boolean
yes?: boolean
}

export const agentsArchive = async (id: string, options: AgentArchiveOptions, command: BaseCommand) => {
if (!id) return logAndThrowError('Agent run ID is required')
await command.authenticate()
const api = createAgentsApi(command.netlify)

if (!options.yes && !options.json) {
if (!process.stdin.isTTY) {
return logAndThrowError('Refusing to archive without --yes when stdin is not a TTY')
}
const { confirmed } = await inquirer.prompt<{ confirmed: boolean }>([
{
type: 'confirm',
name: 'confirmed',
message: `Archive agent run ${id}?`,
default: false,
},
])
if (!confirmed) return exit()
}

const spinner = startSpinner({ text: 'Archiving agent run...' })
try {
await api.archiveAgentRunner(id)
stopSpinner({ spinner })

const result = { success: true, id }
if (options.json) {
logJson(result)
return result
}

log(`${chalk.green('✓')} Agent run archived.`)
log(` Run ID: ${chalk.cyan(id)}`)
return result
} catch (error_) {
stopSpinner({ spinner, error: true })
const error = error_ as Error & { status?: number }
if (error.status === 404) return logAndThrowError(`Agent run not found: ${id}`)
return logAndThrowError(`Failed to archive: ${error.message}`)
}
}
99 changes: 99 additions & 0 deletions src/commands/agents/agents-commit.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import type { OptionValues } from 'commander'
import inquirer from 'inquirer'

import { chalk, log, logAndThrowError, logJson } from '../../utils/command-helpers.js'
import { startSpinner, stopSpinner } from '../../lib/spinner.js'
import type BaseCommand from '../base-command.js'
import { createAgentsApi } from './api.js'
import type { AgentRunner } from './types.js'

interface AgentCommitOptions extends OptionValues {
branch?: string
json?: boolean
}

const pickDefaultBranch = (runner: AgentRunner): { branch: string; reason: string } | null => {
const prState = runner.pr_state
const hasOpenPr = runner.pr_url && (prState === 'open' || prState === 'draft')
if (hasOpenPr && runner.pr_branch) {
return { branch: runner.pr_branch, reason: 'updating the existing pull request' }
}
if (runner.branch) {
return { branch: runner.branch, reason: "committing to this agent run's branch" }
}
return null
}

export const agentsCommit = async (id: string, options: AgentCommitOptions, command: BaseCommand) => {
if (!id) return logAndThrowError('Agent run ID is required')
await command.authenticate()
const { siteInfo } = command.netlify
if (!siteInfo.build_settings?.repo_url) {
return logAndThrowError(
'This project is not connected to a git repository. Commits are only available for git-backed projects.',
)
}
const api = createAgentsApi(command.netlify)

let targetBranch = options.branch?.trim()

if (!targetBranch) {
const lookupSpinner = startSpinner({ text: 'Looking up agent run...' })
let runner: AgentRunner
try {
runner = await api.getAgentRunner(id)
stopSpinner({ spinner: lookupSpinner })
} catch (error_) {
stopSpinner({ spinner: lookupSpinner, error: true })
const error = error_ as Error & { status?: number }
if (error.status === 404) return logAndThrowError(`Agent run not found: ${id}`)
return logAndThrowError(`Failed to fetch agent run: ${error.message}`)
}

const suggestion = pickDefaultBranch(runner)
if (!suggestion) {
return logAndThrowError('Could not determine a target branch. Pass --branch <name>.')
}

if (options.json || !process.stdin.isTTY) {
targetBranch = suggestion.branch
} else {
log(chalk.dim(`Default: ${suggestion.branch} (${suggestion.reason})`))
const { branchInput } = await inquirer.prompt<{ branchInput: string }>([
{
type: 'input',
name: 'branchInput',
message: 'Which branch should the agent commit to?',
default: suggestion.branch,
validate: (input: string) => (input.trim().length > 0 ? true : 'Branch name is required'),
},
])
targetBranch = branchInput.trim()
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.
}

const spinner = startSpinner({ text: `Committing to ${targetBranch}...` })
try {
const runner = await api.agentRunnerCommitToBranch(id, targetBranch)
stopSpinner({ spinner })

if (options.json) {
logJson(runner)
return runner
}

if (runner.merge_commit_error) {
log(`${chalk.red('✗')} Commit failed: ${runner.merge_commit_error}`)
return runner
}

log(`${chalk.green('✓')} Committed to ${chalk.cyan(targetBranch)}`)
log()
if (runner.merge_commit_sha) log(` SHA: ${chalk.cyan(runner.merge_commit_sha)}`)
return runner
} catch (error_) {
stopSpinner({ spinner, error: true })
const error = error_ as Error
return logAndThrowError(`Failed to commit: ${error.message}`)
}
}
Loading
Loading