Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion skill/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -262,7 +262,7 @@ Run `polylane automation create --help` for every flag. Common trigger types: `a
`{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}`. The
`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`.

Expand Down
13 changes: 11 additions & 2 deletions src/commands/integration/connect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ type ConnectableType =
| 'devin'
| 'cursor'
| 'factory'
| 'conductor'
| 'mcp';

// Mirrors each type's subcategory in the integrations catalog
Expand All @@ -62,6 +63,7 @@ const TYPE_OPTIONS: Array<{ value: ConnectableType; label: string; hint: string;
{ value: 'devin', label: 'Devin', hint: 'API key · coding agent', category: 'code-agent' },
{ value: 'cursor', label: 'Cursor', hint: 'API key · coding agent', category: 'code-agent' },
{ value: 'factory', label: 'Factory', hint: 'API key · coding agent', category: 'code-agent' },
{ value: 'conductor', label: 'Conductor', hint: 'API key · coding agent', category: 'code-agent' },
{ value: 'mcp', label: 'MCP server', hint: 'any MCP server by URL', category: 'protocol' },
];

Expand Down Expand Up @@ -125,6 +127,13 @@ const CODE_AGENTS = {
link: 'https://app.factory.ai/settings/api-keys',
linkLabel: 'Create API key',
},
conductor: {
name: 'Conductor',
instructions:
'Create an API key in Conductor under Users > API keys. The key starts with sk_. Handed-off autofixes run as agents in Conductor Cloud workspaces on your account.',
link: 'https://app.conductor.build/users/api-keys',
linkLabel: 'Create API key',
},
} as const;

function isCodeAgentType(value: Integration['type']): value is keyof typeof CODE_AGENTS {
Expand Down Expand Up @@ -567,7 +576,7 @@ async function connectType(

export const integrationConnectCommand: Command = {
name: 'integration connect',
description: 'Connect an integration (GitHub, Slack, Sentry, Datadog, Honeycomb, Axiom, Better Stack, Devin, Cursor, Factory, MCP)',
description: 'Connect an integration (GitHub, Slack, Sentry, Datadog, Honeycomb, Axiom, Better Stack, Devin, Cursor, Factory, Conductor, MCP)',
operationId: 'integrations.connect',
options: [
{
Expand All @@ -582,7 +591,7 @@ export const integrationConnectCommand: Command = {
},
{ flag: '--site <site>', description: 'Datadog site (e.g. us5.datadoghq.com)', type: 'string' },
{ flag: '--region <region>', description: 'Honeycomb (us|eu) or Axiom (us-east-1|eu-central-1)', type: 'string' },
{ flag: '--api-key <key>', description: 'API key (Datadog / Honeycomb / Devin / Cursor / Factory)', type: 'string' },
{ flag: '--api-key <key>', description: 'API key (Datadog / Honeycomb / Devin / Cursor / Factory / Conductor)', type: 'string' },
{ flag: '--app-key <key>', description: 'App key (Datadog only)', type: 'string' },
{ flag: '--api-token <token>', description: 'API token (Axiom / Better Stack global token)', type: 'string' },
{ flag: '--uptime-api-token <token>', description: 'Uptime API token (Better Stack only)', type: 'string' },
Expand Down
11 changes: 8 additions & 3 deletions test/integration-connect-category.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,19 @@ import { isCLIError } from '../src/errors/base';
describe('typeOptionsForCategory', () => {
it('returns every option when no category is given', () => {
const all = typeOptionsForCategory(undefined);
assert.equal(all.length, 11);
assert.equal(all.length, 12);
});

it('narrows to exactly the observability integrations', () => {
const types = typeOptionsForCategory('observability').map((o) => o.value);
assert.deepEqual(types.sort(), ['axiom', 'betterstack', 'datadog', 'honeycomb', 'sentry']);
});

it('narrows to exactly the code agents', () => {
const types = typeOptionsForCategory('code-agent').map((o) => o.value);
assert.deepEqual(types.sort(), ['conductor', 'cursor', 'devin', 'factory']);
});

it('covers every option with a known category', () => {
for (const category of CONNECT_CATEGORIES) {
assert.ok(typeOptionsForCategory(category).length > 0, `empty category: ${category}`);
Expand All @@ -42,9 +47,9 @@ describe('typeOptionsForCategory', () => {

describe('resolveTypeOptions', () => {
it('lets --type win over the filter', () => {
assert.equal(resolveTypeOptions('observability', true).length, 11);
assert.equal(resolveTypeOptions('observability', true).length, 12);
assert.equal(resolveTypeOptions('observability', false).length, 5);
assert.equal(resolveTypeOptions(undefined, false).length, 11);
assert.equal(resolveTypeOptions(undefined, false).length, 12);
});

it('rejects an unknown category even when --type is present', () => {
Expand Down
Loading