diff --git a/README.md b/README.md
index e3309b3..e114a97 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,7 @@
# polylane
Agent-focused CLI for the Polylane platform.
-Investigate production issues, explore cloud infrastructure, search code, run automations, and drive threads — from any agent or terminal.
+Investigate production issues, explore cloud infrastructure, search code, and drive threads — from any agent or terminal.
Your coding agent writes the code. It can't see production. Polylane is the missing sense.
diff --git a/codegen/parse-spec.ts b/codegen/parse-spec.ts
index b7c3ea4..dcf96c5 100644
--- a/codegen/parse-spec.ts
+++ b/codegen/parse-spec.ts
@@ -10,10 +10,23 @@ import { schemaToTypescript, type TypeGenOptions } from './type-utils';
const CLIENT_OPTS: TypeGenOptions = { refPrefix: 'T.' };
+// The automations feature is retired; drop its endpoints and schemas from the
+// generated client even while the API still serves them.
+const EXCLUDED_PATH = /\/automations(\/|$)/;
+const EXCLUDED_SCHEMAS = new Set([
+ 'Automation',
+ 'AutomationExecution',
+ 'AutomationNotification',
+ 'AutomationActionExecution',
+ 'TriggerAutomationBody',
+ 'CreateAutomationFromTemplateBody',
+]);
+
export function parseSpec(spec: OpenAPISpec): ParsedSpec {
const schemas = new Map();
if (spec.components?.schemas) {
for (const [name, schema] of Object.entries(spec.components.schemas)) {
+ if (EXCLUDED_SCHEMAS.has(name)) continue;
schemas.set(name, schema);
}
}
@@ -22,7 +35,7 @@ export function parseSpec(spec: OpenAPISpec): ParsedSpec {
const methods: Array = ['get', 'post', 'put', 'patch', 'delete'];
for (const [path, pathItem] of Object.entries(spec.paths)) {
- if (!pathItem) continue;
+ if (!pathItem || EXCLUDED_PATH.test(path)) continue;
for (const method of methods) {
const op = pathItem[method];
if (!op) continue;
diff --git a/skill/SKILL.md b/skill/SKILL.md
index d08d2fa..d8b4966 100644
--- a/skill/SKILL.md
+++ b/skill/SKILL.md
@@ -1,6 +1,6 @@
---
name: polylane-cli
-description: Use `polylane` to investigate production issues, explore cloud infrastructure (logs / metrics / dependency graphs), search code, save memories, run automations from a catalog, connect observability tools and cloud accounts, and drive threads with the Polylane agent. Use when the user wants to debug a production issue, look up a service, search their codebase, manage integrations, connect a cloud provider, or talk to the Polylane agent from the terminal.
+description: Use `polylane` to investigate production issues, explore cloud infrastructure (logs / metrics / dependency graphs), search code, save memories, connect observability tools and cloud accounts, and drive threads with the Polylane agent. Use when the user wants to debug a production issue, look up a service, search their codebase, manage integrations, connect a cloud provider, or talk to the Polylane agent from the terminal.
---
# Polylane CLI — Agent Skill Guide
@@ -10,8 +10,7 @@ description: Use `polylane` to investigate production issues, explore cloud infr
> **Full docs: ** (model-readable, no auth required). Start at
> , and see for an
> agent-oriented index. This skill is the quick reference; the docs are the source of truth for
-> concepts, the automation schema (triggers / actions / destinations), and the full API. When a task
-> goes beyond what's below, read the docs rather than guessing.
+> concepts and the full API. When a task goes beyond what's below, read the docs rather than guessing.
## Prerequisites
@@ -137,15 +136,10 @@ polylane integration catalog --category cloud
polylane integration connect --type # see --help for that type
polylane cloud connect --provider # see --help for that provider
-# 5. Add automations from templates
-polylane automation catalog
-polylane automation from-template
-
-# 6. Verify
+# 5. Verify
polylane integration list
polylane cloud list
polylane service list # cloud infra discovered from connected accounts
-polylane automation list
```
`integration connect` and `cloud connect` dispatch on `--type` / `--provider`. Some options open a browser for an install URL; others take API credentials directly. Use `--help` on each to see the required flags and optional `--no-browser`. Browser flows wait in the terminal until the connection appears (interactive TTY only); with `--output json` or in non-interactive runs they print the URL and exit, so poll `integration list` / `cloud list` to confirm.
@@ -215,59 +209,6 @@ polylane thread continue ""
Attach resources as context by passing their IDs — the CLI infers the resource type from the ID prefix.
-### Automations
-
-An automation is **trigger → instructions → tools → actions**: a typed event fires, an agent runs
-with your instructions and a set of skills (tools), and optional actions apply side-effects (open an
-issue, roll back a deploy, open a PR) — each gated by a `mode` (`smart` = act only when warranted,
-`always`). Full schema and every trigger/action type: .
-
-```bash
-polylane automation catalog # browse prebuilt templates
-polylane automation from-template # create from a template
-polylane automation from-template --pass-count 2 --provider cloudflare
-polylane automation create ... # create an ARBITRARY automation (see below)
-
-polylane automation list
-polylane automation trigger # manual run
-polylane automation executions # list runs
-polylane automation execution # one run + result
-polylane automation rerun
-```
-
-**Templates vs. custom.** Use `from-template` when a catalog entry matches as-is. Use
-`automation create` for anything else — a scoped trigger, custom instructions, a combination no
-template covers. `automation create` is a first-class command; you do **not** need `polylane api call`.
-
-```bash
-# Build from flags. --trigger/--action take JSON, or a bare type for filterless ones (e.g. `webhook`).
-# --tool attaches a skill by slug (repeatable); see `polylane skill list`.
-polylane automation create \
- --name "Triage Datadog alerts" \
- --trigger '{"type":"alert","filters":{"sources":["datadog"]}}' \
- --instructions "Correlate the alert with recent deploys and open an issue with the findings." \
- --tool investigate-errors --tool investigate-latency \
- --action '{"type":"openIssue","mode":"smart","defaultSeverity":"high"}' \
- --delay 600000 # wait 10m (e.g. let a deploy settle) before the run
-
-# Or pass a full JSON body (--body-file '-' reads stdin); flags layer on top.
-polylane automation create --body-file automation.json
-
-# Scope a template's broad trigger: crib its body, then narrow the trigger.
-polylane automation catalog --output json # find the slug + shape
-polylane automation create --body-file template.json \
- --trigger '{"type":"cloudflare.deployment","filters":{"environments":["production"]}}'
-```
-
-Run `polylane automation create --help` for every flag. Common trigger types: `alert`, `cron`
-(`{"type":"cron","expression":"0 9 * * *"}`), `webhook`, `github.*` (push/pull_request/deployment/…),
-`{cloudflare,vercel,render,fly}.deployment`, `slack.message`, `polylane.*` (issue.triaged,
-cloud_account.synced, codebase.synced, …). Actions include `openIssue`,
-`rollback{Cloudflare,Vercel,Render,Fly}Deployment`, `submitPr`, `commentPr`, `mergePr`,
-`commentGithubIssue`, `createGithubIssue`, `autofix`, `handoffTo{Devin,Cursor,Factory,Conductor}`. The
-authoritative, always-current list of types and filters is in the docs and in
-`polylane api describe automations.post`.
-
---
## Piping patterns
diff --git a/src/auth/oauth.ts b/src/auth/oauth.ts
index 6215a69..922ea6f 100644
--- a/src/auth/oauth.ts
+++ b/src/auth/oauth.ts
@@ -47,9 +47,6 @@ export const DEFAULT_SCOPES = [
'autofixes:delete',
'autofixes:read',
'autofixes:write',
- 'automations:delete',
- 'automations:read',
- 'automations:write',
'billing:read',
'billing:write',
'cloud_accounts:delete',
diff --git a/src/commands/auth/signup.ts b/src/commands/auth/signup.ts
index 51e08b7..0795da0 100644
--- a/src/commands/auth/signup.ts
+++ b/src/commands/auth/signup.ts
@@ -123,15 +123,10 @@ export function nextSteps(landing?: Landing): string {
` 4. Connect a cloud account`,
` polylane cloud connect --provider # see --help`,
``,
- ` 5. Add an automation from the catalog`,
- ` polylane automation catalog`,
- ` polylane automation from-template `,
- ``,
- ` 6. Verify what's wired up`,
+ ` 5. Verify what's wired up`,
` polylane integration list`,
` polylane cloud list`,
` polylane service list`,
- ` polylane automation list`,
``,
`Once things are connected, try:`,
` polylane thread ask "summarise production"`,
diff --git a/src/commands/autofix/list.ts b/src/commands/autofix/list.ts
index 79919c0..a355407 100644
--- a/src/commands/autofix/list.ts
+++ b/src/commands/autofix/list.ts
@@ -13,7 +13,7 @@ export const autofixListCommand: Command = {
operationId: 'autofixes.list',
options: [
{ flag: '--status ', description: 'Filter by status (started, branch_pushed, pr_opened, merged, failed, no_fix_needed)', type: 'string' },
- { flag: '--origin ', description: 'Filter by origin (automation, chat, investigation, system)', type: 'string' },
+ { flag: '--origin ', description: 'Filter by origin (chat, investigation, system)', type: 'string' },
{ flag: '--repo ', description: 'Filter by repo name', type: 'string' },
{ flag: '--limit ', description: 'Max items (default 20)', type: 'number' },
{ flag: '--full', description: 'Return full objects', type: 'boolean' },
diff --git a/src/commands/automation/catalog.ts b/src/commands/automation/catalog.ts
deleted file mode 100644
index ba00a5d..0000000
--- a/src/commands/automation/catalog.ts
+++ /dev/null
@@ -1,51 +0,0 @@
-import type { Command } from '../../command';
-import type { Config } from '../../config/schema';
-import { PolylaneAPI } from '../../generated/client';
-import { formatList } from '../../output/formatter';
-import { projectItems } from '../../output/project';
-import { getArgBoolean } from '../helpers';
-
-const FIELDS = ['slug', 'name', 'category', 'triggerTypes', 'description', '_html_url'];
-
-export const automationCatalogCommand: Command = {
- name: 'automation catalog',
- description: 'List public automation templates',
- operationId: 'automations.public.catalog',
- options: [
- { flag: '--full', description: 'Return full objects', type: 'boolean' },
- ],
- async execute(config: Config, _flags, args: Record): Promise {
- const full = getArgBoolean(args, 'full') === true;
- const api = new PolylaneAPI(config);
- const result = await api.automationsPublicCatalog();
-
- if (full) {
- formatList(config, {
- items: result.items as unknown as Array>,
- count: result.items.length,
- });
- return;
- }
-
- const projected = projectItems(result.items as unknown as Array>, FIELDS);
- formatList(
- config,
- { items: projected, count: projected.length },
- {
- headers: ['Slug', 'Name', 'Category', 'Triggers', 'Description', 'Console'],
- rows: (item) => [
- String(item.slug ?? ''),
- String(item.name ?? ''),
- String(item.category ?? ''),
- Array.isArray(item.triggerTypes) ? item.triggerTypes.join(',') : '',
- truncate(String(item.description ?? ''), 60),
- String(item._html_url ?? ''),
- ],
- }
- );
- },
-};
-
-function truncate(s: string, max: number): string {
- return s.length > max ? s.slice(0, max - 1) + '…' : s;
-}
diff --git a/src/commands/automation/create.ts b/src/commands/automation/create.ts
deleted file mode 100644
index 4fda052..0000000
--- a/src/commands/automation/create.ts
+++ /dev/null
@@ -1,142 +0,0 @@
-import { readFileSync, readSync } from 'node:fs';
-import type { Command } from '../../command';
-import type { Config } from '../../config/schema';
-import { PolylaneAPI } from '../../generated/client';
-import { formatOutput } from '../../output/formatter';
-import { requireWorkspace, getArgString, getArgArray, getArgNumber, getArgBoolean, parseJsonArg } from '../helpers';
-import { CLIError } from '../../errors/base';
-import { ExitCode } from '../../errors/codes';
-
-type Automation = Parameters[0];
-
-function readBodyFromFile(path: string): string {
- if (path === '-') {
- const chunks: Buffer[] = [];
- const buf = Buffer.alloc(65536);
- while (true) {
- let bytesRead = 0;
- try {
- bytesRead = readSync(0, buf, 0, buf.length, null);
- } catch {
- break;
- }
- if (bytesRead === 0) break;
- chunks.push(Buffer.from(buf.subarray(0, bytesRead)));
- }
- return Buffer.concat(chunks).toString('utf-8');
- }
- return readFileSync(path, 'utf-8');
-}
-
-function resolveBaseBody(args: Record): Record {
- const raw = getArgString(args, 'body');
- if (raw !== undefined) {
- const parsed = parseJsonArg(raw, '--body');
- if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed)) {
- throw new CLIError('--body must be a JSON object', ExitCode.USAGE);
- }
- return parsed as Record;
- }
- const file = getArgString(args, 'bodyFile');
- if (file !== undefined) {
- const parsed = parseJsonArg(readBodyFromFile(file), '--body-file');
- if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed)) {
- throw new CLIError('--body-file must contain a JSON object', ExitCode.USAGE);
- }
- return parsed as Record;
- }
- return {};
-}
-
-// A repeatable flag value is either inline JSON (`{"type":"cron","expression":"0 9 * * *"}`)
-// or a bare type shorthand (`webhook`) that expands to `{ type: }`.
-function parseTypedFlag(values: string[] | undefined, flag: string): Array> | undefined {
- if (!values || values.length === 0) return undefined;
- return values.map((v) => {
- const trimmed = v.trim();
- if (trimmed.startsWith('{')) {
- const parsed = parseJsonArg(trimmed, flag);
- if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed)) {
- throw new CLIError(`${flag} JSON must be an object`, ExitCode.USAGE);
- }
- return parsed as Record;
- }
- return { type: trimmed };
- });
-}
-
-export const automationCreateCommand: Command = {
- name: 'automation create',
- description: 'Create an arbitrary automation (trigger → instructions → tools → actions)',
- operationId: 'automations.post',
- usage: 'polylane automation create [--name ...] [--trigger ]... [--instructions ...] [--body-file ]',
- options: [
- { flag: '--name ', description: 'Automation name', type: 'string' },
- { flag: '--instructions ', description: 'Agent instructions (the system prompt for the run)', type: 'string' },
- { flag: '--trigger ', description: 'Trigger as JSON, or a bare type for filterless triggers (repeatable). e.g. \'{"type":"alert","filters":{"sources":["datadog"]}}\' or webhook', type: 'array' },
- { flag: '--action ', description: 'Action as JSON, or a bare type (repeatable). e.g. \'{"type":"openIssue","mode":"smart"}\'', type: 'array' },
- { flag: '--destination ', description: 'Notification destination as JSON (repeatable). e.g. \'{"type":"slack","name":"ops","channelId":"C123"}\'', type: 'array' },
- { flag: '--tool ', description: 'Skill slug to attach as a tool (repeatable). Alias: --skill', type: 'array' },
- { flag: '--skill ', description: 'Alias for --tool', type: 'array' },
- { flag: '--delay ', description: 'Delay before the run, in milliseconds (e.g. post-deploy wait)', type: 'number' },
- { flag: '--passes ', description: 'Number of parallel agent passes', type: 'number' },
- { flag: '--disabled', description: 'Create the automation in a disabled state', type: 'boolean' },
- { flag: '--body ', description: 'Full automation body as JSON; flags layer on top of it', type: 'string' },
- { flag: '--body-file ', description: 'Full automation body from a file ("-" for stdin); flags layer on top', type: 'string' },
- ],
- examples: [
- 'polylane automation create --name "Triage Datadog alerts" --trigger \'{"type":"alert","filters":{"sources":["datadog"]}}\' --instructions "Correlate the alert with recent deploys and open an issue with findings." --action \'{"type":"openIssue","mode":"smart"}\' --tool investigate-errors',
- 'polylane automation catalog --output json | jq \'...\' # crib a template body, then:',
- 'polylane automation create --body-file my-automation.json',
- 'polylane automation create --body-file template.json --trigger \'{"type":"cloudflare.deployment","filters":{"environments":["production"]}}\' # take a template body, narrow the trigger',
- ],
- async execute(config: Config, _flags, args: Record): Promise {
- const workspaceId = await requireWorkspace(config);
-
- const body = resolveBaseBody(args);
-
- // Flags layer on top of any --body/--body-file base: scalars override, arrays replace when provided.
- const name = getArgString(args, 'name');
- if (name !== undefined) body.name = name;
-
- const instructions = getArgString(args, 'instructions');
- const passes = getArgNumber(args, 'passes');
- if (instructions !== undefined || passes !== undefined) {
- const agentConfig = (typeof body.agentConfig === 'object' && body.agentConfig !== null ? body.agentConfig : {}) as Record;
- if (instructions !== undefined) agentConfig.instructions = instructions;
- if (passes !== undefined) agentConfig.passes = { count: passes };
- body.agentConfig = agentConfig;
- }
-
- const triggers = parseTypedFlag(getArgArray(args, 'trigger'), '--trigger');
- if (triggers !== undefined) body.triggers = triggers;
-
- const actions = parseTypedFlag(getArgArray(args, 'action'), '--action');
- if (actions !== undefined) body.actions = actions;
-
- const destinations = parseTypedFlag(getArgArray(args, 'destination'), '--destination');
- if (destinations !== undefined) body.destinations = destinations;
-
- const tools = [...(getArgArray(args, 'tool') ?? []), ...(getArgArray(args, 'skill') ?? [])];
- if (tools.length > 0) body.skills = tools.map((slug) => ({ skillSlug: slug }));
-
- const delay = getArgNumber(args, 'delay');
- if (delay !== undefined) body.delayMs = delay;
-
- const disabled = getArgBoolean(args, 'disabled');
- if (disabled !== undefined) body.disabled = disabled;
-
- if (!Array.isArray(body.triggers) || body.triggers.length === 0) {
- throw new CLIError(
- 'An automation needs at least one trigger',
- ExitCode.USAGE,
- "Pass --trigger '' (or a bare type like webhook), or supply --body/--body-file.\n" +
- 'See trigger and action types at https://docs.polylane.com/fix-and-automate/automations/triggers'
- );
- }
-
- const api = new PolylaneAPI(config);
- const automation = await api.automationsPost({ workspaceId, ...body } as Automation);
- formatOutput(config, automation);
- },
-};
diff --git a/src/commands/automation/execution.ts b/src/commands/automation/execution.ts
deleted file mode 100644
index 7d93d36..0000000
--- a/src/commands/automation/execution.ts
+++ /dev/null
@@ -1,23 +0,0 @@
-import type { Command } from '../../command';
-import type { Config } from '../../config/schema';
-import { PolylaneAPI } from '../../generated/client';
-import { formatOutput } from '../../output/formatter';
-import { requireWorkspace, requirePositional } from '../helpers';
-
-export const automationExecutionCommand: Command = {
- name: 'automation execution',
- description: 'Show an automation execution (includes result)',
- operationId: 'automations.executions.get',
- positional: [
- { name: 'automation-id', description: 'The automation ID' },
- { name: 'execution-id', description: 'The execution ID' },
- ],
- async execute(config: Config, _flags, args: Record): Promise {
- const workspaceId = await requireWorkspace(config);
- const automationId = requirePositional(args, 0, 'automation-id');
- const executionId = requirePositional(args, 1, 'execution-id');
- const api = new PolylaneAPI(config);
- const result = await api.automationsExecutionsGet(workspaceId, automationId, executionId);
- formatOutput(config, result);
- },
-};
diff --git a/src/commands/automation/executions.ts b/src/commands/automation/executions.ts
deleted file mode 100644
index deca4b6..0000000
--- a/src/commands/automation/executions.ts
+++ /dev/null
@@ -1,52 +0,0 @@
-import type { Command } from '../../command';
-import type { Config } from '../../config/schema';
-import { PolylaneAPI } from '../../generated/client';
-import { formatList } from '../../output/formatter';
-import { projectItems } from '../../output/project';
-import { requireWorkspace, requirePositional, getArgNumber, getArgBoolean } from '../helpers';
-
-const FIELDS = ['id', 'status', 'summary', 'passThreadIds', 'durationMs', 'triggeredAt'];
-
-export const automationExecutionsCommand: Command = {
- name: 'automation executions',
- description: 'List executions for an automation',
- operationId: 'automations.executions.list',
- positional: [{ name: 'automation-id', description: 'The automation ID' }],
- options: [
- { flag: '--limit ', description: 'Max items (default 20)', type: 'number' },
- { flag: '--full', description: 'Return full objects', type: 'boolean' },
- ],
- async execute(config: Config, _flags, args: Record): Promise {
- const workspaceId = await requireWorkspace(config);
- const automationId = requirePositional(args, 0, 'automation-id');
- const limit = getArgNumber(args, 'limit') ?? 20;
- const full = getArgBoolean(args, 'full') === true;
-
- const api = new PolylaneAPI(config);
- const result = await api.automationsExecutionsList(workspaceId, automationId, { perPage: limit });
-
- if (full) {
- formatList(config, result as { items: Array>; count: number });
- return;
- }
-
- formatList(
- config,
- {
- items: projectItems(result.items as unknown as Array>, FIELDS),
- count: result.count,
- },
- {
- headers: ['ID', 'Status', 'Summary', 'Threads', 'Duration (ms)', 'Triggered'],
- rows: (item) => [
- String(item.id ?? ''),
- String(item.status ?? ''),
- String(item.summary ?? ''),
- Array.isArray(item.passThreadIds) ? item.passThreadIds.join(',') : '',
- item.durationMs === null || item.durationMs === undefined ? '' : String(item.durationMs),
- String(item.triggeredAt ?? ''),
- ],
- }
- );
- },
-};
diff --git a/src/commands/automation/find.ts b/src/commands/automation/find.ts
deleted file mode 100644
index 480c05c..0000000
--- a/src/commands/automation/find.ts
+++ /dev/null
@@ -1,40 +0,0 @@
-import type { Command } from '../../command';
-import type { Config } from '../../config/schema';
-import { PolylaneAPI } from '../../generated/client';
-import { formatList } from '../../output/formatter';
-import { projectItems } from '../../output/project';
-import { requireWorkspace, requirePositional, getAllPositional } from '../helpers';
-
-const FIELDS = ['id', 'name', 'triggerTypes', 'disabled', 'source', 'created'];
-
-export const automationFindCommand: Command = {
- name: 'automation find',
- description: 'Search automations by prompt',
- operationId: 'automations.search',
- positional: [{ name: 'prompt', description: 'Search query', variadic: true }],
- async execute(config: Config, _flags, args: Record): Promise {
- const workspaceId = await requireWorkspace(config);
- requirePositional(args, 0, 'prompt');
- const prompt = getAllPositional(args).join(' ');
-
- const api = new PolylaneAPI(config);
- const items = await api.automationsSearch({ workspaceId, prompt });
- const projected = projectItems(items as unknown as Array>, FIELDS);
-
- formatList(
- config,
- { items: projected, count: projected.length },
- {
- headers: ['ID', 'Name', 'Triggers', 'Disabled', 'Source', 'Created'],
- rows: (item) => [
- String(item.id ?? ''),
- String(item.name ?? ''),
- Array.isArray(item.triggerTypes) ? item.triggerTypes.join(',') : '',
- item.disabled ? 'yes' : 'no',
- String(item.source ?? ''),
- String(item.created ?? ''),
- ],
- }
- );
- },
-};
diff --git a/src/commands/automation/from-template.ts b/src/commands/automation/from-template.ts
deleted file mode 100644
index 0bd3f9e..0000000
--- a/src/commands/automation/from-template.ts
+++ /dev/null
@@ -1,40 +0,0 @@
-import type { Command } from '../../command';
-import type { Config } from '../../config/schema';
-import { PolylaneAPI } from '../../generated/client';
-import type { CreateAutomationFromTemplateBody } from '../../generated/types';
-import { formatOutput } from '../../output/formatter';
-import { requireWorkspace, requirePositional, getArgString, getArgNumber } from '../helpers';
-
-type Provider = NonNullable;
-
-export const automationFromTemplateCommand: Command = {
- name: 'automation from-template',
- description: 'Create an automation from a catalog template slug',
- operationId: 'automations.postFromTemplate',
- positional: [{ name: 'template-slug', description: 'Slug from `polylane automation catalog`' }],
- options: [
- { flag: '--pass-count ', description: "Override the template's number of parallel agent passes (1-10)", type: 'number' },
- { flag: '--provider ', description: 'Sanity-check the template targets this provider; fails on mismatch', type: 'string' },
- ],
- examples: [
- 'polylane automation catalog',
- 'polylane automation from-template alert-triage',
- 'polylane automation from-template babysit-cloudflare-deployment --pass-count 2 --provider cloudflare',
- '# To scope a template (e.g. narrow the trigger), crib its body and use `automation create`:',
- 'polylane automation create --body-file template.json --trigger \'{"type":"cloudflare.deployment","filters":{"environments":["production"]}}\'',
- ],
- async execute(config: Config, _flags, args: Record): Promise {
- const workspaceId = await requireWorkspace(config);
- const templateSlug = requirePositional(args, 0, 'template-slug');
- const passCount = getArgNumber(args, 'passCount');
- const provider = getArgString(args, 'provider') as Provider | undefined;
- const api = new PolylaneAPI(config);
- const automation = await api.automationsPostFromTemplate({
- workspaceId,
- templateSlug,
- ...(passCount !== undefined ? { passCount } : {}),
- ...(provider !== undefined ? { provider } : {}),
- });
- formatOutput(config, automation);
- },
-};
diff --git a/src/commands/automation/index.ts b/src/commands/automation/index.ts
deleted file mode 100644
index 58f5e22..0000000
--- a/src/commands/automation/index.ts
+++ /dev/null
@@ -1,23 +0,0 @@
-import { automationListCommand } from './list';
-import { automationFindCommand } from './find';
-import { automationShowCommand } from './show';
-import { automationTriggerCommand } from './trigger';
-import { automationExecutionsCommand } from './executions';
-import { automationExecutionCommand } from './execution';
-import { automationRerunCommand } from './rerun';
-import { automationCatalogCommand } from './catalog';
-import { automationFromTemplateCommand } from './from-template';
-import { automationCreateCommand } from './create';
-
-export const automationCommands = [
- automationListCommand,
- automationFindCommand,
- automationShowCommand,
- automationTriggerCommand,
- automationExecutionsCommand,
- automationExecutionCommand,
- automationRerunCommand,
- automationCatalogCommand,
- automationCreateCommand,
- automationFromTemplateCommand,
-];
diff --git a/src/commands/automation/list.ts b/src/commands/automation/list.ts
deleted file mode 100644
index d561920..0000000
--- a/src/commands/automation/list.ts
+++ /dev/null
@@ -1,54 +0,0 @@
-import type { Command } from '../../command';
-import type { Config } from '../../config/schema';
-import { PolylaneAPI } from '../../generated/client';
-import { formatList } from '../../output/formatter';
-import { projectItems } from '../../output/project';
-import { requireWorkspace, getArgNumber, getArgBoolean } from '../helpers';
-
-const FIELDS = ['id', 'name', 'triggerTypes', 'disabled', 'source', 'created'];
-
-export const automationListCommand: Command = {
- name: 'automation list',
- description: 'List automations',
- operationId: 'automations.list',
- options: [
- { flag: '--limit ', description: 'Max items (default 20)', type: 'number' },
- { flag: '--full', description: 'Return full objects', type: 'boolean' },
- ],
- async execute(config: Config, _flags, args: Record): Promise {
- const workspaceId = await requireWorkspace(config);
- const limit = getArgNumber(args, 'limit') ?? 20;
- const full = getArgBoolean(args, 'full') === true;
-
- const api = new PolylaneAPI(config);
- const result = await api.automationsList(workspaceId, { perPage: limit });
-
- if (full) {
- formatList(config, result as { items: Array>; count: number });
- return;
- }
-
- formatList(
- config,
- {
- items: projectItems(result.items as unknown as Array>, FIELDS),
- count: result.count,
- },
- {
- headers: ['ID', 'Name', 'Triggers', 'Disabled', 'Source', 'Created'],
- rows: (item) => [
- String(item.id ?? ''),
- truncate(String(item.name ?? ''), 40),
- Array.isArray(item.triggerTypes) ? item.triggerTypes.join(',') : '',
- item.disabled ? 'yes' : 'no',
- String(item.source ?? ''),
- String(item.created ?? ''),
- ],
- }
- );
- },
-};
-
-function truncate(s: string, max: number): string {
- return s.length > max ? s.slice(0, max - 1) + '…' : s;
-}
diff --git a/src/commands/automation/rerun.ts b/src/commands/automation/rerun.ts
deleted file mode 100644
index b0b9143..0000000
--- a/src/commands/automation/rerun.ts
+++ /dev/null
@@ -1,23 +0,0 @@
-import type { Command } from '../../command';
-import type { Config } from '../../config/schema';
-import { PolylaneAPI } from '../../generated/client';
-import { formatOutput } from '../../output/formatter';
-import { requireWorkspace, requirePositional } from '../helpers';
-
-export const automationRerunCommand: Command = {
- name: 'automation rerun',
- description: 'Rerun an automation execution',
- operationId: 'automations.executions.rerun',
- positional: [
- { name: 'automation-id', description: 'The automation ID' },
- { name: 'execution-id', description: 'The execution to rerun' },
- ],
- async execute(config: Config, _flags, args: Record): Promise {
- const workspaceId = await requireWorkspace(config);
- const automationId = requirePositional(args, 0, 'automation-id');
- const executionId = requirePositional(args, 1, 'execution-id');
- const api = new PolylaneAPI(config);
- const execution = await api.automationsExecutionsRerun({ workspaceId, automationId, executionId });
- formatOutput(config, execution);
- },
-};
diff --git a/src/commands/automation/show.ts b/src/commands/automation/show.ts
deleted file mode 100644
index 2663685..0000000
--- a/src/commands/automation/show.ts
+++ /dev/null
@@ -1,19 +0,0 @@
-import type { Command } from '../../command';
-import type { Config } from '../../config/schema';
-import { PolylaneAPI } from '../../generated/client';
-import { formatOutput } from '../../output/formatter';
-import { requireWorkspace, requirePositional } from '../helpers';
-
-export const automationShowCommand: Command = {
- name: 'automation show',
- description: 'Show an automation with its full configuration',
- operationId: 'automations.get',
- positional: [{ name: 'automation-id', description: 'The automation ID' }],
- async execute(config: Config, _flags, args: Record): Promise {
- const workspaceId = await requireWorkspace(config);
- const id = requirePositional(args, 0, 'automation-id');
- const api = new PolylaneAPI(config);
- const result = await api.automationsGet(workspaceId, id);
- formatOutput(config, result);
- },
-};
diff --git a/src/commands/automation/trigger.ts b/src/commands/automation/trigger.ts
deleted file mode 100644
index 9b062d6..0000000
--- a/src/commands/automation/trigger.ts
+++ /dev/null
@@ -1,20 +0,0 @@
-import type { Command } from '../../command';
-import type { Config } from '../../config/schema';
-import { PolylaneAPI } from '../../generated/client';
-import { formatOutput } from '../../output/formatter';
-import { requireWorkspace, requirePositional } from '../helpers';
-
-export const automationTriggerCommand: Command = {
- name: 'automation trigger',
- description: 'Manually trigger an automation to create an execution',
- operationId: 'automations.trigger',
- positional: [{ name: 'automation-id', description: 'The automation ID' }],
- examples: ['polylane automation trigger auto_xxx'],
- async execute(config: Config, _flags, args: Record): Promise {
- const workspaceId = await requireWorkspace(config);
- const id = requirePositional(args, 0, 'automation-id');
- const api = new PolylaneAPI(config);
- const execution = await api.automationsTrigger({ workspaceId, id });
- formatOutput(config, execution);
- },
-};
diff --git a/src/commands/feed/list.ts b/src/commands/feed/list.ts
index 79f166d..5c46eae 100644
--- a/src/commands/feed/list.ts
+++ b/src/commands/feed/list.ts
@@ -15,9 +15,9 @@ import { ExitCode } from '../../errors/codes';
type Category = NonNullable[1]>['category']>;
-const CATEGORY_COVERAGE: Record = {
+// Retired category; the Exclude keeps this compiling until the API drops it too.
+const CATEGORY_COVERAGE: Record, true> = {
autofix: true,
- automation: true,
change: true,
release: true,
issue: true,
@@ -43,7 +43,7 @@ const FIELDS = [
export const feedListCommand: Command = {
name: 'feed list',
- description: 'Workspace agent activity feed (autofixes, automations, changes, releases, issues…)',
+ description: 'Workspace agent activity feed (autofixes, changes, releases, issues…)',
operationId: 'feed.list',
options: [
{ flag: '--category ', description: VALID_CATEGORIES.join(' | '), type: 'string' },
diff --git a/src/commands/index.ts b/src/commands/index.ts
index c992dd0..3aa47cb 100644
--- a/src/commands/index.ts
+++ b/src/commands/index.ts
@@ -12,7 +12,6 @@ import { serviceCommands } from './service';
import { repoCommands } from './repo';
import { memoryCommands } from './memory';
import { threadCommands } from './thread';
-import { automationCommands } from './automation';
import { integrationCommands } from './integration';
import { cloudCommands } from './cloud';
import { workspaceCommands } from './workspace';
@@ -38,7 +37,6 @@ export function registerAllCommands(): void {
...memoryCommands,
...threadCommands,
...skillCommands,
- ...automationCommands,
...integrationCommands,
...cloudCommands,
...workspaceCommands,
diff --git a/src/commands/thread/list.ts b/src/commands/thread/list.ts
index 442f1dc..4aa3395 100644
--- a/src/commands/thread/list.ts
+++ b/src/commands/thread/list.ts
@@ -8,7 +8,7 @@ import { CLIError } from '../../errors/base';
import { ExitCode } from '../../errors/codes';
const FIELDS = ['id', 'name', 'type', 'created'];
-const VALID_TYPES = ['chat', 'hypothesis', 'automation', 'autofix', 'investigation', 'pr_review'] as const;
+const VALID_TYPES = ['chat', 'hypothesis', 'autofix', 'investigation', 'pr_review'] as const;
type ThreadType = (typeof VALID_TYPES)[number];
export const threadListCommand: Command = {
@@ -16,7 +16,7 @@ export const threadListCommand: Command = {
description: 'List conversation threads',
operationId: 'threads.list',
options: [
- { flag: '--type ', description: 'Filter by thread type (chat | hypothesis | automation | autofix | investigation | pr_review; comma-separated)', type: 'string' },
+ { flag: '--type ', description: 'Filter by thread type (chat | hypothesis | autofix | investigation | pr_review; comma-separated)', type: 'string' },
{ flag: '--labels ', description: 'Filter by labels (comma-separated)', type: 'string' },
{ flag: '--limit ', description: 'Max items (default 20)', type: 'number' },
{ flag: '--full', description: 'Return full objects', type: 'boolean' },
diff --git a/src/registry.ts b/src/registry.ts
index ed646be..8ed1b70 100644
--- a/src/registry.ts
+++ b/src/registry.ts
@@ -26,7 +26,6 @@ const RESOURCE_ORDER: Record = {
thread: { name: 'thread', description: 'Conversation threads', order: 60 },
artifact: { name: 'artifact', description: 'Per-thread artifacts (reports, diagrams, PRs)', order: 61 },
skill: { name: 'skill', description: 'Reusable agent skills (catalog, install, docs)', order: 63 },
- automation: { name: 'automation', description: 'Automations and executions', order: 65 },
autofix: { name: 'autofix', description: 'Automated code fixes (read-only)', order: 66 },
integration: { name: 'integration', description: 'Integrations (code hosts, chat, observability, issue tracking, coding agents, MCP)', order: 68 },
cloud: { name: 'cloud', description: 'Cloud accounts (aws, cloudflare, vercel, clickhouse, turso, ...)', order: 69 },
diff --git a/test/resolve.test.ts b/test/resolve.test.ts
index 314b7fd..d52b014 100644
--- a/test/resolve.test.ts
+++ b/test/resolve.test.ts
@@ -48,7 +48,6 @@ describe('command resolution', () => {
'artifact',
'auth',
'autofix',
- 'automation',
'cloud',
'config',
'feed',