Skip to content

Commit 33344a1

Browse files
committed
feat: af paperclip init — one-command company bootstrap from blueprints
New commands: - `af paperclip init --blueprint dev-shop` — creates a complete Paperclip company with agents, goals, and starter tasks in one command - `af paperclip blueprints` — list available company templates 5 blueprints: dev-shop, marketing-agency, sales-team, content-studio, support-center. Each defines agent roles, starter tasks, and company goals. Init flow: 1. Matches AF marketplace agents to blueprint slots 2. Creates Paperclip company + goal 3. Deploys agents with correct roles and heartbeat config 4. Creates starter tasks assigned to agents New playbook: `af playbook company-from-scratch` — step-by-step guide for creating an AI company from zero using AgenticFlow + Paperclip. Verified by sub-agent: marketing-agency blueprint → 4 agents deployed, 3 tasks created, gateway connected, CMO agent executed task successfully.
1 parent 3340e3d commit 33344a1

File tree

3 files changed

+391
-0
lines changed

3 files changed

+391
-0
lines changed
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
/**
2+
* Pre-built company blueprints for Paperclip deployment.
3+
*
4+
* Each blueprint defines a team of AgenticFlow agents that can be
5+
* deployed to Paperclip as a ready-made company. Agents are sourced
6+
* from the AF marketplace templates or the user's existing agents.
7+
*/
8+
9+
export interface AgentSlot {
10+
/** Paperclip role */
11+
role: string;
12+
/** What this slot does */
13+
title: string;
14+
/** Human description */
15+
description: string;
16+
/** Suggested AF agent name to search for (from marketplace) */
17+
suggestedTemplate?: string;
18+
/** Allow user to skip this slot */
19+
optional?: boolean;
20+
}
21+
22+
export interface CompanyBlueprint {
23+
/** Short slug */
24+
id: string;
25+
/** Display name */
26+
name: string;
27+
/** What this company does */
28+
description: string;
29+
/** Company goal */
30+
goal: string;
31+
/** Starter tasks */
32+
starterTasks: Array<{ title: string; description: string; assigneeRole: string; priority: string }>;
33+
/** Agent slots to fill */
34+
agents: AgentSlot[];
35+
}
36+
37+
export const BLUEPRINTS: Record<string, CompanyBlueprint> = {
38+
"dev-shop": {
39+
id: "dev-shop",
40+
name: "Software Dev Shop",
41+
description: "A lean engineering team that builds and ships software products.",
42+
goal: "Build and ship high-quality software products",
43+
agents: [
44+
{ role: "ceo", title: "CEO / Tech Lead", description: "Strategic direction, delegation, project oversight" },
45+
{ role: "engineer", title: "Senior Engineer", description: "Core development, architecture, code implementation" },
46+
{ role: "designer", title: "UX Designer", description: "UI/UX design, user research, visual design", optional: true },
47+
{ role: "qa", title: "QA Engineer", description: "Testing, bug finding, quality assurance", optional: true },
48+
],
49+
starterTasks: [
50+
{ title: "Define product roadmap", description: "Create a 3-month product roadmap with milestones and deliverables.", assigneeRole: "ceo", priority: "high" },
51+
{ title: "Set up project architecture", description: "Design the technical architecture and set up the development environment.", assigneeRole: "engineer", priority: "high" },
52+
],
53+
},
54+
"marketing-agency": {
55+
id: "marketing-agency",
56+
name: "Marketing Agency",
57+
description: "A full-service marketing team for content, social media, SEO, and campaigns.",
58+
goal: "Drive brand awareness and customer acquisition through multi-channel marketing",
59+
agents: [
60+
{ role: "ceo", title: "Agency Director", description: "Strategy, client relations, campaign oversight" },
61+
{ role: "cmo", title: "Content Strategist", description: "Content planning, blog posts, newsletters", suggestedTemplate: "Content Writer" },
62+
{ role: "designer", title: "Visual Designer", description: "Graphics, social media visuals, brand assets", suggestedTemplate: "Visual designer" },
63+
{ role: "researcher", title: "Market Researcher", description: "Competitive analysis, trend research", suggestedTemplate: "Ari, the Market Researcher", optional: true },
64+
],
65+
starterTasks: [
66+
{ title: "Develop content calendar", description: "Create a 4-week content calendar covering blog posts, social media, and email newsletters.", assigneeRole: "cmo", priority: "high" },
67+
{ title: "Create brand guidelines", description: "Define color palette, typography, logo usage, and visual style for all marketing materials.", assigneeRole: "designer", priority: "high" },
68+
{ title: "Competitive landscape report", description: "Research top 5 competitors and summarize their positioning, pricing, and marketing strategies.", assigneeRole: "researcher", priority: "medium" },
69+
],
70+
},
71+
"sales-team": {
72+
id: "sales-team",
73+
name: "Sales Team",
74+
description: "A sales operation with outreach, research, and customer management.",
75+
goal: "Generate qualified leads and close deals",
76+
agents: [
77+
{ role: "ceo", title: "Sales Director", description: "Pipeline management, strategy, team coordination" },
78+
{ role: "researcher", title: "Sales Researcher", description: "Lead research, company profiling, ICP matching", suggestedTemplate: "Olivia, the Sales Strategist" },
79+
{ role: "general", title: "SDR / Outreach", description: "Email outreach, follow-ups, scheduling", suggestedTemplate: "Rachel, the Support Agent" },
80+
],
81+
starterTasks: [
82+
{ title: "Define ICP and target list", description: "Create ideal customer profile and build a list of 50 target companies.", assigneeRole: "researcher", priority: "high" },
83+
{ title: "Write outreach sequences", description: "Draft 3-step email sequences for cold outreach, follow-up, and re-engagement.", assigneeRole: "general", priority: "high" },
84+
],
85+
},
86+
"content-studio": {
87+
id: "content-studio",
88+
name: "Content Studio",
89+
description: "A creative content production team for video, social, and written content.",
90+
goal: "Produce high-quality content across video, social media, and written formats",
91+
agents: [
92+
{ role: "ceo", title: "Creative Director", description: "Content strategy, quality control, brand voice" },
93+
{ role: "cmo", title: "Social Media Manager", description: "Social media scheduling, engagement, analytics", suggestedTemplate: "Mason, the Social Media Manager" },
94+
{ role: "engineer", title: "Content Writer", description: "Blog posts, articles, scripts, copy", suggestedTemplate: "Content Writer" },
95+
{ role: "designer", title: "Visual Creator", description: "Graphics, thumbnails, social visuals", suggestedTemplate: "Visual designer", optional: true },
96+
],
97+
starterTasks: [
98+
{ title: "Create content pillars", description: "Define 3-5 content themes/pillars that align with the brand and audience.", assigneeRole: "ceo", priority: "high" },
99+
{ title: "Write 5 blog posts", description: "Draft 5 blog posts of 800-1200 words each on the defined content pillars.", assigneeRole: "engineer", priority: "high" },
100+
{ title: "Design social media templates", description: "Create reusable templates for Instagram, Twitter, and LinkedIn posts.", assigneeRole: "designer", priority: "medium" },
101+
],
102+
},
103+
"support-center": {
104+
id: "support-center",
105+
name: "Customer Support Center",
106+
description: "A customer support team with triage, resolution, and escalation.",
107+
goal: "Provide fast, helpful customer support and maintain high satisfaction",
108+
agents: [
109+
{ role: "ceo", title: "Support Manager", description: "Escalation handling, SLA monitoring, team coordination" },
110+
{ role: "general", title: "Support Agent", description: "Ticket triage, first response, common issue resolution", suggestedTemplate: "Rachel, the Support Agent" },
111+
{ role: "researcher", title: "Knowledge Base Manager", description: "FAQ maintenance, documentation, self-service content", optional: true },
112+
],
113+
starterTasks: [
114+
{ title: "Set up support playbook", description: "Create a support playbook with common issues, resolution steps, and escalation criteria.", assigneeRole: "ceo", priority: "high" },
115+
{ title: "Draft FAQ document", description: "Write an FAQ with the top 20 most common customer questions and answers.", assigneeRole: "researcher", priority: "medium" },
116+
],
117+
},
118+
};
119+
120+
export function listBlueprints(): CompanyBlueprint[] {
121+
return Object.values(BLUEPRINTS);
122+
}
123+
124+
export function getBlueprint(id: string): CompanyBlueprint | null {
125+
return BLUEPRINTS[id] ?? null;
126+
}

packages/cli/src/cli/main.ts

Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import { PaperclipConnector } from "./gateway/connectors/paperclip.js";
2424
import { LinearConnector } from "./gateway/connectors/linear.js";
2525
import { WebhookConnector } from "./gateway/connectors/webhook.js";
2626
import type { ChannelConnector } from "./gateway/connector.js";
27+
import { listBlueprints, getBlueprint } from "./company-blueprints.js";
2728
import {
2829
OperationRegistry,
2930
defaultSpecPath,
@@ -4326,6 +4327,171 @@ export function createProgram(): Command {
43264327
.command("paperclip")
43274328
.description("Publish and control AgenticFlow agents on a Paperclip instance.");
43284329

4330+
// ─── init (bootstrap a company from blueprint) ──────────────────
4331+
pcOpts.both(paperclipCmd
4332+
.command("init")
4333+
.description("Bootstrap a Paperclip company from a pre-built blueprint with AgenticFlow agents."))
4334+
.requiredOption("--blueprint <id>", "Blueprint ID (use --list to see available)")
4335+
.option("--list", "List available blueprints")
4336+
.option("--budget <cents>", "Monthly budget in cents", "50000")
4337+
.action(async (opts: Record<string, unknown>) => {
4338+
// List mode
4339+
if (opts.list) {
4340+
printResult(listBlueprints().map((b) => ({
4341+
id: b.id,
4342+
name: b.name,
4343+
description: b.description,
4344+
agents: b.agents.length,
4345+
tasks: b.starterTasks.length,
4346+
})));
4347+
return;
4348+
}
4349+
4350+
const bp = getBlueprint(opts.blueprint as string);
4351+
if (!bp) {
4352+
fail("blueprint_not_found", `Unknown blueprint: ${opts.blueprint}`,
4353+
`Available: ${listBlueprints().map((b) => b.id).join(", ")}`);
4354+
}
4355+
4356+
const client = buildClient(program.opts());
4357+
const paperclipUrl = resolvePaperclipUrl(opts.paperclipUrl as string | undefined);
4358+
const pc = new PaperclipResource({ baseUrl: paperclipUrl });
4359+
4360+
const healthy = await pc.healthCheck();
4361+
if (!healthy) fail("paperclip_unreachable", `Cannot reach Paperclip at ${paperclipUrl}`);
4362+
4363+
if (!isJsonFlagEnabled()) console.error(`\nBootstrapping "${bp.name}" company...\n`);
4364+
4365+
// 1. List AF agents to match against blueprint slots
4366+
const afAgents = (await client.agents.list({ limit: 100 })) as Array<Record<string, unknown>>;
4367+
4368+
// 2. Create company
4369+
const company = await pc.createCompany({
4370+
name: bp.name,
4371+
description: bp.description,
4372+
budgetMonthlyCents: Number.parseInt(opts.budget as string, 10) || 50000,
4373+
});
4374+
savePaperclipCompanyId(company.id);
4375+
if (!isJsonFlagEnabled()) console.error(` Company: ${company.name} (${company.id})`);
4376+
4377+
// 3. Create goal
4378+
const goal = await pc.createGoal(company.id, {
4379+
title: bp.goal,
4380+
level: "company",
4381+
status: "active",
4382+
});
4383+
if (!isJsonFlagEnabled()) console.error(` Goal: ${bp.goal}`);
4384+
4385+
// 4. Deploy agents — match AF agents to blueprint slots
4386+
const deployed: Array<{ slot: string; afAgent: string; pcAgent: string; role: string }> = [];
4387+
const afBaseUrl = DEFAULT_BASE_URL;
4388+
const afApiKey = resolveToken(program.opts());
4389+
4390+
for (const slot of bp.agents) {
4391+
// Find best matching AF agent
4392+
let match = afAgents.find((a) =>
4393+
slot.suggestedTemplate && (a.name as string)?.toLowerCase().includes(slot.suggestedTemplate!.toLowerCase()),
4394+
);
4395+
if (!match) match = afAgents[0]; // Fallback to first available
4396+
if (!match) {
4397+
if (!isJsonFlagEnabled()) console.error(` Skipped: ${slot.title} (no AF agent available)`);
4398+
continue;
4399+
}
4400+
4401+
const afId = match.id as string;
4402+
const afName = match.name as string;
4403+
const afModel = (match.model as string) ?? "";
4404+
const afDesc = (match.description as string) ?? "";
4405+
const streamUrl = `${afBaseUrl.replace(/\/+$/, "")}/v1/agents/${afId}/stream`;
4406+
4407+
const pcAgent = await pc.createAgent(company.id, {
4408+
name: afName,
4409+
role: slot.role,
4410+
title: slot.title,
4411+
capabilities: slot.description + (afDesc ? ` | AF: ${afDesc}` : ""),
4412+
adapterType: "http",
4413+
adapterConfig: {
4414+
url: streamUrl,
4415+
method: "POST",
4416+
headers: {
4417+
"Content-Type": "application/json",
4418+
...(afApiKey ? { Authorization: `Bearer ${afApiKey}` } : {}),
4419+
},
4420+
payloadTemplate: { messages: [{ content: "Execute your assigned task.", role: "user" }] },
4421+
},
4422+
runtimeConfig: {
4423+
heartbeat: { enabled: true, intervalSec: 0, wakeOnDemand: true },
4424+
},
4425+
metadata: {
4426+
af_agent_id: afId,
4427+
af_model: afModel,
4428+
af_stream_url: streamUrl,
4429+
blueprint: bp.id,
4430+
slot: slot.role,
4431+
deployed_at: new Date().toISOString(),
4432+
},
4433+
});
4434+
4435+
deployed.push({ slot: slot.title, afAgent: afName, pcAgent: pcAgent.id, role: slot.role });
4436+
if (!isJsonFlagEnabled()) console.error(` Agent: ${afName}${slot.role} (${pcAgent.id})`);
4437+
}
4438+
4439+
// 5. Create starter tasks
4440+
const roleToAgent = new Map(deployed.map((d) => [d.role, d.pcAgent]));
4441+
const tasks: Array<{ identifier: string; title: string; assignee: string }> = [];
4442+
4443+
for (const task of bp.starterTasks) {
4444+
const assignee = roleToAgent.get(task.assigneeRole);
4445+
if (!assignee) continue;
4446+
4447+
const issue = await pc.createIssue(company.id, {
4448+
title: task.title,
4449+
description: task.description,
4450+
priority: task.priority,
4451+
assigneeAgentId: assignee,
4452+
goalId: goal.id,
4453+
status: "todo",
4454+
});
4455+
const iss = issue as unknown as Record<string, unknown>;
4456+
tasks.push({ identifier: (iss.identifier as string) ?? "", title: task.title, assignee });
4457+
if (!isJsonFlagEnabled()) console.error(` Task: ${iss.identifier}${task.title}`);
4458+
}
4459+
4460+
if (!isJsonFlagEnabled()) {
4461+
console.error(`\n Done! ${deployed.length} agents deployed, ${tasks.length} tasks created.`);
4462+
console.error(` Next: af gateway serve --channels paperclip && af paperclip connect --company-id ${company.id}`);
4463+
}
4464+
4465+
printResult({
4466+
schema: "agenticflow.paperclip.init.v1",
4467+
blueprint: bp.id,
4468+
company: { id: company.id, name: company.name },
4469+
goal: { id: goal.id, title: bp.goal },
4470+
agents: deployed,
4471+
tasks,
4472+
});
4473+
});
4474+
4475+
// ─── blueprints ─────────────────────────────────────────────────
4476+
paperclipCmd
4477+
.command("blueprints")
4478+
.description("List available company blueprints for `af paperclip init`.")
4479+
.action(() => {
4480+
const bps = listBlueprints();
4481+
if (isJsonFlagEnabled()) {
4482+
printResult(bps.map((b) => ({ id: b.id, name: b.name, description: b.description, agents: b.agents.length })));
4483+
} else {
4484+
console.log("Available company blueprints:\n");
4485+
for (const b of bps) {
4486+
console.log(` ${b.id}`);
4487+
console.log(` ${b.name}${b.description}`);
4488+
console.log(` Agents: ${b.agents.map((a) => a.role).join(", ")}`);
4489+
console.log("");
4490+
}
4491+
console.log("Use: af paperclip init --blueprint <id>");
4492+
}
4493+
});
4494+
43294495
// ─── deploy ─────────────────────────────────────────────────────
43304496
const pcDeploy = paperclipCmd
43314497
.command("deploy")

0 commit comments

Comments
 (0)