Skip to content
← Public packages

@kentcdodds/devin

Start, monitor, and manage Devin sessions, knowledge, playbooks, and schedules via the Devin v3 API

src/playbooks.ts

117 lines · 2.9 KB · TypeScript
import {
	clampInt,
	compact,
	devinOrgApi,
	type Paginated,
	trimString,
} from './lib/client.ts'

export type Playbook = {
	playbook_id: string
	title: string
	body: string
	macro: string | null
	structured_output_schema: Record<string, unknown> | null
	access_type: 'org' | 'enterprise' | string
	org_id: string | null
	created_by?: string | null
	updated_by?: string | null
	created_at: number
	updated_at: number
}

export async function listPlaybooks(
	params: { orgId?: string; first?: number; after?: string } = {},
) {
	return devinOrgApi<Paginated<Playbook>>({
		orgId: params.orgId,
		path: '/playbooks',
		query: compact({
			first: params.first === undefined ? undefined : clampInt(params.first, 1, 200, 50),
			after: params.after,
		}),
	})
}

export async function getPlaybook(params: {
	playbookId: string
	orgId?: string
}) {
	return devinOrgApi<Playbook>({
		orgId: params.orgId,
		path: `/playbooks/${encodeURIComponent(params.playbookId)}`,
	})
}

export type PlaybookInput = {
	orgId?: string
	title: string
	body: string
	/** Slack-style macro, must start with "!" (e.g. "!triage"). */
	macro?: string
	structuredOutputSchema?: Record<string, unknown>
}

function playbookBody(params: PlaybookInput) {
	const title = trimString(params.title)
	const body = trimString(params.body)
	if (!title || !body) throw new Error('Playbooks require title and body')
	return compact({
		title,
		body,
		macro: params.macro,
		structured_output_schema: params.structuredOutputSchema,
	})
}

export async function createPlaybook(params: PlaybookInput) {
	return devinOrgApi<Playbook>({
		orgId: params.orgId,
		path: '/playbooks',
		method: 'POST',
		body: playbookBody(params),
	})
}

/** Full replace — Devin's PUT takes the same shape as create. */
export async function updatePlaybook(
	params: PlaybookInput & { playbookId: string },
) {
	return devinOrgApi<Playbook>({
		orgId: params.orgId,
		path: `/playbooks/${encodeURIComponent(params.playbookId)}`,
		method: 'PUT',
		body: playbookBody(params),
	})
}

/** Destructive — confirm the playbook id before calling. */
export async function deletePlaybook(params: {
	playbookId: string
	orgId?: string
}) {
	return devinOrgApi<unknown>({
		orgId: params.orgId,
		path: `/playbooks/${encodeURIComponent(params.playbookId)}`,
		method: 'DELETE',
	})
}

/**
 * List Devin playbooks for the configured org (default `./playbooks` action).
 * Use when browsing playbooks; named exports cover get/create/update/delete.
 *
 * @param params.orgId - Optional org override (else stored destinOrgId)
 * @param params.first - Page size (1–200)
 * @returns Paginated playbooks
 *
 * @example
 * import listPlaybooks from 'kody:@kentcdodds/devin/playbooks'
 * const page = await listPlaybooks({ first: 20 })
 * // => { items: [{ playbook_id: '...', title: '...', ... }], ... }
 */
export default async function listPlaybooksDefault(
	params: { orgId?: string; first?: number; after?: string } = {},
) {
	return await listPlaybooks(params)
}