Agent Builder
shareAI-lab/learn-claude-code
Design and build AI agents for any domain. An agent skill from shareAI-lab/learn-claude-code.
Create or modify Paperclip agent adapters across server, UI, and CLI surfaces.
$ npx skills add paperclipai/paperclip --skill create-agent-adapter -a claude-codeProject install by default; add -g for ~/.claude/skills/.
$ gh skill install paperclipai/paperclip create-agent-adapter --agent claude-codeProject scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).
$ git clone --depth 1 https://github.com/paperclipai/paperclip.git skills-src && mkdir -p .claude/skills && cp -r skills-src/.agents/skills/create-agent-adapter .claude/skills/create-agent-adapter && rm -rf skills-srcUse ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.
Claude Code skills documentation · loads skills from .claude/skills/
Install the "create-agent-adapter" agent skill from https://github.com/paperclipai/paperclip/tree/master/.agents/skills/create-agent-adapter into .claude/skills/create-agent-adapter/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "create-agent-adapter", then confirm the skill loads.Claude Code copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$skill-installer install https://github.com/paperclipai/paperclip/tree/master/.agents/skills/create-agent-adapterType this inside Codex. $skill-installer <name> installs a curated skill from openai/skills. The installer writes to $CODEX_HOME/skills (default ~/.codex/skills). Restart Codex if the skill does not show up.
$ npx skills add paperclipai/paperclip --skill create-agent-adapter -a codexProject install goes to .agents/skills/; add -g for ~/.codex/skills/.
$ gh skill install paperclipai/paperclip create-agent-adapter --agent codexProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/paperclipai/paperclip.git skills-src && mkdir -p .agents/skills && cp -r skills-src/.agents/skills/create-agent-adapter .agents/skills/create-agent-adapter && rm -rf skills-srcUse ~/.agents/skills/ instead of .agents/skills for a personal install.
Codex skills documentation · loads skills from .agents/skills/
Install the "create-agent-adapter" agent skill from https://github.com/paperclipai/paperclip/tree/master/.agents/skills/create-agent-adapter into .agents/skills/create-agent-adapter/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "create-agent-adapter", then confirm the skill loads.Codex copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add paperclipai/paperclip --skill create-agent-adapter -a cursorProject install goes to .agents/skills/; add -g for ~/.cursor/skills/.
$ gh skill install paperclipai/paperclip create-agent-adapter --agent cursorProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/paperclipai/paperclip.git skills-src && mkdir -p .cursor/skills && cp -r skills-src/.agents/skills/create-agent-adapter .cursor/skills/create-agent-adapter && rm -rf skills-srcUse ~/.cursor/skills/ instead of .cursor/skills for a personal install.
Cursor skills documentation · loads skills from .cursor/skills/, .agents/skills/, .claude/skills/, .codex/skills/
Install the "create-agent-adapter" agent skill from https://github.com/paperclipai/paperclip/tree/master/.agents/skills/create-agent-adapter into .cursor/skills/create-agent-adapter/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "create-agent-adapter", then confirm the skill loads.Cursor copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gemini skills install https://github.com/paperclipai/paperclip.git --path .agents/skills/create-agent-adapter--scope user (default) or --scope workspace; --path is the subfolder of the repo that holds the skill; --consent skips the security confirmation prompt.
$ npx skills add paperclipai/paperclip --skill create-agent-adapter -a gemini-cliProject install goes to .agents/skills/; add -g for ~/.gemini/skills/.
$ gh skill install paperclipai/paperclip create-agent-adapter --agent gemini-cliProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/paperclipai/paperclip.git skills-src && mkdir -p .gemini/skills && cp -r skills-src/.agents/skills/create-agent-adapter .gemini/skills/create-agent-adapter && rm -rf skills-srcUse ~/.gemini/skills/ instead of .gemini/skills for a personal install, then run /skills reload.
Gemini CLI skills documentation · loads skills from .gemini/skills/, .agents/skills/
Install the "create-agent-adapter" agent skill from https://github.com/paperclipai/paperclip/tree/master/.agents/skills/create-agent-adapter into .gemini/skills/create-agent-adapter/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "create-agent-adapter", then confirm the skill loads.Gemini CLI copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gh skill install paperclipai/paperclip create-agent-adapterInstalls for Copilot at project scope by default; add --scope user for a personal install. Preview a skill first with gh skill preview. Needs GitHub CLI 2.90.0 or later (public preview).
$ npx skills add paperclipai/paperclip --skill create-agent-adapter -a github-copilotProject install goes to .agents/skills/; add -g for ~/.copilot/skills/.
$ git clone --depth 1 https://github.com/paperclipai/paperclip.git skills-src && mkdir -p .github/skills && cp -r skills-src/.agents/skills/create-agent-adapter .github/skills/create-agent-adapter && rm -rf skills-srcUse ~/.copilot/skills/ instead of .github/skills for a personal install. Commit .github/skills so cloud agent and code review can use it.
GitHub Copilot skills documentation · loads skills from .github/skills/, .claude/skills/, .agents/skills/
Install the "create-agent-adapter" agent skill from https://github.com/paperclipai/paperclip/tree/master/.agents/skills/create-agent-adapter into .github/skills/create-agent-adapter/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "create-agent-adapter", then confirm the skill loads.GitHub Copilot copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add paperclipai/paperclip --skill create-agent-adapter -a opencodeOpenCode documents no install command of its own. Project install goes to .agents/skills/; add -g for ~/.config/opencode/skills/.
$ gh skill install paperclipai/paperclip create-agent-adapter --agent opencodeProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/paperclipai/paperclip.git skills-src && mkdir -p .opencode/skills && cp -r skills-src/.agents/skills/create-agent-adapter .opencode/skills/create-agent-adapter && rm -rf skills-srcUse ~/.config/opencode/skills/ instead of .opencode/skills for a personal install.
OpenCode skills documentation · loads skills from .opencode/skills/, .claude/skills/, .agents/skills/
Install the "create-agent-adapter" agent skill from https://github.com/paperclipai/paperclip/tree/master/.agents/skills/create-agent-adapter into .opencode/skills/create-agent-adapter/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "create-agent-adapter", then confirm the skill loads.OpenCode copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
create-agent-adapterCreate or modify Paperclip agent adapters across server, UI, and CLI surfaces.
Create Agent Adapter is an agent skill from paperclipai/paperclip. Create or modify Paperclip agent adapters across server, UI, and CLI surfaces. Use when adding support for a new CLI agent, API agent, custom process, or adapter package.
Its SKILL.md is about 8k tokens, which your agent loads only when the skill is triggered. It is a single SKILL.md file with no bundled scripts.
It sits in AI & LLM Engineering, covering Building AI agents. The repository describes itself as: The open-source app everyone uses to manage agents at work. The licence is MIT.
11 steps, taken from the step headings in SKILL.md.
Read from SKILL.md and the folder at commit b9750b1. It shows what the files ask for, not the result of running them.
Pre-approves nothing: there is no allowed-tools line, so your agent's usual permission prompts apply.
From allowed-tools in the SKILL.md frontmatter.
No scripts in the folder and no shell commands in SKILL.md (its code samples are typescript and json).
From the folder's file list and the shell code blocks in SKILL.md.
No URLs in SKILL.md.
From URLs in SKILL.md, links to its own repository left out.
Names these keys or tokens, usually read from environment variables:
PAPERCLIP_API_KEYANTHROPIC_API_KEYFrom names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.
Create Agent Adapter loads about 8k tokens when it runs. Until then it costs about 48 tokens; SKILL.md has 2,532 words of instructions outside code blocks.
Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.
The automated check found no risky patterns in SKILL.md.
Automated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); files beside SKILL.md are not scanned.
The full file from paperclipai/paperclip at commit b9750b1, republished under its MIT licence (© paperclipai). 2,532 words, ~7,956 tokens.
.claude/skills/create-agent-adapter/SKILL.md (or your agent's skills folder).An adapter bridges Paperclip's orchestration layer to a specific AI agent runtime (Claude Code, Codex CLI, a custom process, an HTTP endpoint, etc.). Each adapter is a self-contained package that provides implementations for three consumers: the server, the UI, and the CLI.
packages/adapters/<name>/
src/
index.ts # Shared metadata (type, label, models, agentConfigurationDoc)
server/
index.ts # Server exports: execute, sessionCodec, parse helpers
execute.ts # Core execution logic (AdapterExecutionContext -> AdapterExecutionResult)
parse.ts # Stdout/result parsing for the agent's output format
ui/
index.ts # UI exports: parseStdoutLine, buildConfig
parse-stdout.ts # Line-by-line stdout -> TranscriptEntry[] for the run viewer
build-config.ts # CreateConfigValues -> adapterConfig JSON for agent creation form
cli/
index.ts # CLI exports: formatStdoutEvent
format-event.ts # Colored terminal output for `paperclipai run --watch`
package.json
tsconfig.jsonThree separate registries consume adapter modules:
| Registry | Location | Interface |
|---|---|---|
| Server | server/src/adapters/registry.ts | ServerAdapterModule |
| UI | ui/src/adapters/registry.ts | UIAdapterModule |
| CLI | cli/src/adapters/registry.ts | CLIAdapterModule |
@paperclipai/adapter-utils)All adapter interfaces live in packages/adapter-utils/src/types.ts. Import from @paperclipai/adapter-utils (types) or @paperclipai/adapter-utils/server-utils (runtime helpers).
// The execute function signature — every adapter must implement this
interface AdapterExecutionContext {
runId: string;
agent: AdapterAgent; // { id, companyId, name, adapterType, adapterConfig }
runtime: AdapterRuntime; // { sessionId, sessionParams, sessionDisplayId, taskKey }
config: Record<string, unknown>; // The agent's adapterConfig blob
context: Record<string, unknown>; // Runtime context (taskId, wakeReason, approvalId, etc.)
onLog: (stream: "stdout" | "stderr", chunk: string) => Promise<void>;
onMeta?: (meta: AdapterInvocationMeta) => Promise<void>;
authToken?: string;
}
interface AdapterExecutionResult {
exitCode: number | null;
signal: string | null;
timedOut: boolean;
errorMessage?: string | null;
usage?: UsageSummary; // { inputTokens, outputTokens, cachedInputTokens? }
sessionId?: string | null; // Legacy — prefer sessionParams
sessionParams?: Record<string, unknown> | null; // Opaque session state persisted between runs
sessionDisplayId?: string | null;
provider?: string | null; // "anthropic", "openai", etc.
model?: string | null;
costUsd?: number | null;
resultJson?: Record<string, unknown> | null;
summary?: string | null; // Human-readable summary of what the agent did
clearSession?: boolean; // true = tell Paperclip to forget the stored session
}
interface AdapterSessionCodec {
deserialize(raw: unknown): Record<string, unknown> | null;
serialize(params: Record<string, unknown> | null): Record<string, unknown> | null;
getDisplayId?(params: Record<string, unknown> | null): string | null;
}// Server — registered in server/src/adapters/registry.ts
interface ServerAdapterModule {
type: string;
execute(ctx: AdapterExecutionContext): Promise<AdapterExecutionResult>;
testEnvironment(ctx: AdapterEnvironmentTestContext): Promise<AdapterEnvironmentTestResult>;
sessionCodec?: AdapterSessionCodec;
supportsLocalAgentJwt?: boolean;
models?: { id: string; label: string }[];
agentConfigurationDoc?: string;
}
// UI — registered in ui/src/adapters/registry.ts
interface UIAdapterModule {
type: string;
label: string;
parseStdoutLine: (line: string, ts: string) => TranscriptEntry[];
ConfigFields: ComponentType<AdapterConfigFieldsProps>;
buildAdapterConfig: (values: CreateConfigValues) => Record<string, unknown>;
}
// CLI — registered in cli/src/adapters/registry.ts
interface CLIAdapterModule {
type: string;
formatStdoutEvent: (line: string, debug: boolean) => void;
}Every server adapter must implement testEnvironment(...). This powers the board UI "Test environment" button in agent configuration.
type AdapterEnvironmentCheckLevel = "info" | "warn" | "error";
type AdapterEnvironmentTestStatus = "pass" | "warn" | "fail";
interface AdapterEnvironmentCheck {
code: string;
level: AdapterEnvironmentCheckLevel;
message: string;
detail?: string | null;
hint?: string | null;
}
interface AdapterEnvironmentTestResult {
adapterType: string;
status: AdapterEnvironmentTestStatus;
checks: AdapterEnvironmentCheck[];
testedAt: string; // ISO timestamp
}
interface AdapterEnvironmentTestContext {
companyId: string;
adapterType: string;
config: Record<string, unknown>; // runtime-resolved adapterConfig
}Guidelines:
error for invalid/unusable runtime setup (bad cwd, missing command, invalid URL).warn for non-blocking but important situations.info for successful checks and context.Severity policy is product-critical: warnings are not save blockers.
Example: for claude_local, an explicitly configured ANTHROPIC_API_KEY or selected managed API connection is info: the user chose that authentication. An ambient server key overriding subscription login remains warn, not error.
packages/adapters/<name>/
package.json
tsconfig.json
src/
index.ts
server/index.ts
server/execute.ts
server/parse.ts
ui/index.ts
ui/parse-stdout.ts
ui/build-config.ts
cli/index.ts
cli/format-event.tspackage.json — must use the four-export convention:
{
"name": "@paperclipai/adapter-<name>",
"version": "0.0.1",
"private": true,
"type": "module",
"exports": {
".": "./src/index.ts",
"./server": "./src/server/index.ts",
"./ui": "./src/ui/index.ts",
"./cli": "./src/cli/index.ts"
},
"dependencies": {
"@paperclipai/adapter-utils": "workspace:*",
"picocolors": "^1.1.1"
},
"devDependencies": {
"typescript": "^5.7.3"
}
}index.ts — Adapter MetadataThis file is imported by all three consumers (server, UI, CLI). Keep it dependency-free (no Node APIs, no React).
export const type = "my_agent"; // snake_case, globally unique
export const label = "My Agent (local)";
export const models = [
{ id: "model-a", label: "Model A" },
{ id: "model-b", label: "Model B" },
];
export const agentConfigurationDoc = `# my_agent agent configuration
...document all config fields here...
`;Required exports:
type — the adapter type key, stored in agents.adapter_typelabel — human-readable name for the UImodels — available model options for the agent creation formagentConfigurationDoc — markdown describing all adapterConfig fields (used by LLM agents configuring other agents)Writing agentConfigurationDoc as routing logic:
The agentConfigurationDoc is read by LLM agents (including Paperclip agents that create other agents). Write it as routing logic, not marketing copy. Include concrete "use when" and "don't use when" guidance so an LLM can decide whether this adapter is appropriate for a given task.
export const agentConfigurationDoc = `# my_agent agent configuration
Adapter: my_agent
Use when:
- The agent needs to run MyAgent CLI locally on the host machine
- You need session persistence across runs (MyAgent supports thread resumption)
- The task requires MyAgent-specific tools (e.g. web search, code execution)
Don't use when:
- You need a simple one-shot script execution (use the "process" adapter instead)
- The agent doesn't need conversational context between runs (process adapter is simpler)
- MyAgent CLI is not installed on the host
Core fields:
- cwd (string, required): absolute working directory for the agent process
...
`;Adding explicit negative cases improves adapter selection accuracy. One concrete anti-pattern is worth more than three paragraphs of description.
server/execute.ts — The CoreThis is the most important file. It receives an AdapterExecutionContext and must return an AdapterExecutionResult.
Required behavior:
ctx.config using helpers (asString, asNumber, asBoolean, asStringArray, parseObject from @paperclipai/adapter-utils/server-utils)buildPaperclipEnv(agent) then layer in PAPERCLIP_RUN_ID, context vars (PAPERCLIP_TASK_ID, PAPERCLIP_WAKE_REASON, PAPERCLIP_WAKE_COMMENT_ID, PAPERCLIP_APPROVAL_ID, PAPERCLIP_APPROVAL_STATUS, PAPERCLIP_LINKED_ISSUE_IDS), user env overrides, and auth tokenruntime.sessionParams / runtime.sessionId for an existing session; validate it's compatible (e.g. same cwd); decide whether to resume or start freshrenderTemplate(template, data) with the template variables: agentId, companyId, runId, company, agent, run, contextrunChildProcess() for CLI-based agents or fetch() for HTTP-based agentsclearSession: trueEnvironment variables the server always injects:
| Variable | Source |
|---|---|
PAPERCLIP_AGENT_ID | agent.id |
PAPERCLIP_COMPANY_ID | agent.companyId |
PAPERCLIP_API_URL | Server's own URL |
PAPERCLIP_RUN_ID | Current run id |
PAPERCLIP_TASK_ID | context.taskId or context.issueId |
PAPERCLIP_WAKE_REASON | context.wakeReason |
PAPERCLIP_WAKE_COMMENT_ID | context.wakeCommentId or context.commentId |
PAPERCLIP_APPROVAL_ID | context.approvalId |
PAPERCLIP_APPROVAL_STATUS | context.approvalStatus |
PAPERCLIP_LINKED_ISSUE_IDS | context.issueIds (comma-separated) |
PAPERCLIP_API_KEY | authToken (if no explicit key in config) |
server/parse.ts — Output ParserParse the agent's stdout format into structured data. Must handle:
is<Agent>UnknownSessionError() function for retry logicTreat agent output as untrusted. The stdout you're parsing comes from an LLM-driven process that may have executed arbitrary tool calls, fetched external content, or been influenced by prompt injection in the files it read. Parse defensively:
eval() or dynamically execute anything from outputasString, asNumber, parseJson) — they return fallbacks on unexpected typesserver/index.ts — Server Exportsexport { execute } from "./execute.js";
export { testEnvironment } from "./test.js";
export { parseMyAgentOutput, isMyAgentUnknownSessionError } from "./parse.js";
// Session codec — required for session persistence
export const sessionCodec: AdapterSessionCodec = {
deserialize(raw) { /* raw DB JSON -> typed params or null */ },
serialize(params) { /* typed params -> JSON for DB storage */ },
getDisplayId(params) { /* -> human-readable session id string */ },
};server/test.ts — Environment DiagnosticsImplement adapter-specific preflight checks used by the UI test button.
Minimum expectations:
code valuesinfo / warn / error)fail if any errorwarn if no errors and at least one warningpass otherwiseThis operation should be lightweight and side-effect free.
ui/parse-stdout.ts — Transcript ParserConverts individual stdout lines into TranscriptEntry[] for the run detail viewer. Must handle the agent's streaming output format and produce entries of these kinds:
init — model/session initializationassistant — agent text responsesthinking — agent thinking/reasoning (if supported)tool_call — tool invocations with name and inputtool_result — tool results with content and error flaguser — user messages in the conversationresult — final result with usage statsstdout — fallback for unparseable linesexport function parseMyAgentStdoutLine(line: string, ts: string): TranscriptEntry[] {
// Parse JSON line, map to appropriate TranscriptEntry kind(s)
// Return [{ kind: "stdout", ts, text: line }] as fallback
}ui/build-config.ts — Config BuilderConverts the UI form's CreateConfigValues into the adapterConfig JSON blob stored on the agent.
export function buildMyAgentConfig(v: CreateConfigValues): Record<string, unknown> {
const ac: Record<string, unknown> = {};
if (v.cwd) ac.cwd = v.cwd;
if (v.promptTemplate) ac.promptTemplate = v.promptTemplate;
if (v.model) ac.model = v.model;
ac.timeoutSec = 0;
ac.graceSec = 15;
// ... adapter-specific fields
return ac;
}Create ui/src/adapters/<name>/config-fields.tsx with a React component implementing AdapterConfigFieldsProps. This renders adapter-specific form fields in the agent creation/edit form.
Use the shared primitives from ui/src/components/agent-config-primitives:
Field — labeled form field wrapperToggleField — boolean toggle with label and hintDraftInput — text input with draft/commit behaviorDraftNumberInput — number input with draft/commit behaviorhelp — standard hint text for common fieldsThe component must support both create mode (using values/set) and edit mode (using config/eff/mark).
cli/format-event.ts — Terminal FormatterPretty-prints stdout lines for paperclipai run --watch. Use picocolors for coloring.
import pc from "picocolors";
export function printMyAgentStreamEvent(raw: string, debug: boolean): void {
// Parse JSON line from agent stdout
// Print colored output: blue for system, green for assistant, yellow for tools
// In debug mode, print unrecognized lines in gray
}After creating the adapter package, register it in all three consumers:
server/src/adapters/registry.ts)import { execute as myExecute, sessionCodec as mySessionCodec } from "@paperclipai/adapter-my-agent/server";
import { agentConfigurationDoc as myDoc, models as myModels } from "@paperclipai/adapter-my-agent";
const myAgentAdapter: ServerAdapterModule = {
type: "my_agent",
execute: myExecute,
sessionCodec: mySessionCodec,
models: myModels,
supportsLocalAgentJwt: true, // true if agent can use Paperclip API
agentConfigurationDoc: myDoc,
};
// Add to the adaptersByType map
const adaptersByType = new Map<string, ServerAdapterModule>(
[..., myAgentAdapter].map((a) => [a.type, a]),
);ui/src/adapters/registry.ts)import { myAgentUIAdapter } from "./my-agent";
const adaptersByType = new Map<string, UIAdapterModule>(
[..., myAgentUIAdapter].map((a) => [a.type, a]),
);With ui/src/adapters/my-agent/index.ts:
import type { UIAdapterModule } from "../types";
import { parseMyAgentStdoutLine } from "@paperclipai/adapter-my-agent/ui";
import { MyAgentConfigFields } from "./config-fields";
import { buildMyAgentConfig } from "@paperclipai/adapter-my-agent/ui";
export const myAgentUIAdapter: UIAdapterModule = {
type: "my_agent",
label: "My Agent",
parseStdoutLine: parseMyAgentStdoutLine,
ConfigFields: MyAgentConfigFields,
buildAdapterConfig: buildMyAgentConfig,
};cli/src/adapters/registry.ts)import { printMyAgentStreamEvent } from "@paperclipai/adapter-my-agent/cli";
const myAgentCLIAdapter: CLIAdapterModule = {
type: "my_agent",
formatStdoutEvent: printMyAgentStreamEvent,
};
// Add to the adaptersByType mapSessions allow agents to maintain conversation context across runs. The system is codec-based — each adapter defines how to serialize/deserialize its session state.
Design for long runs from the start. Treat session reuse as the default primitive, not an optimization to add later. An agent working on an issue may be woken dozens of times — for the initial assignment, approval callbacks, re-assignments, manual nudges. Each wake should resume the existing conversation so the agent retains full context about what it has already done, what files it has read, and what decisions it has made. Starting fresh each time wastes tokens on re-reading the same files and risks contradictory decisions.
Key concepts:
sessionParams is an opaque Record<string, unknown> stored in the DB per tasksessionCodec.serialize() converts execution result data to storable paramssessionCodec.deserialize() converts stored params back for the next runsessionCodec.getDisplayId() extracts a human-readable session ID for the UIclearSession: true so Paperclip wipes the stale sessionIf the agent runtime supports any form of context compaction or conversation compression (e.g. Claude Code's automatic context management, or Codex's previous_response_id chaining), lean on it. Adapters that support session resume get compaction for free — the agent runtime handles context window management internally across resumes.
Pattern (from both claude-local and codex-local):
const canResumeSession =
runtimeSessionId.length > 0 &&
(runtimeSessionCwd.length === 0 || path.resolve(runtimeSessionCwd) === path.resolve(cwd));
const sessionId = canResumeSession ? runtimeSessionId : null;
// ... run attempt ...
// If resume failed with unknown session, retry fresh
if (sessionId && !proc.timedOut && exitCode !== 0 && isUnknownSessionError(output)) {
const retry = await runAttempt(null);
return toResult(retry, { clearSessionOnMissingSession: true });
}Import from @paperclipai/adapter-utils/server-utils:
| Helper | Purpose |
|---|---|
asString(val, fallback) | Safe string extraction |
asNumber(val, fallback) | Safe number extraction |
asBoolean(val, fallback) | Safe boolean extraction |
asStringArray(val) | Safe string array extraction |
parseObject(val) | Safe Record<string, unknown> extraction |
parseJson(str) | Safe JSON.parse returning Record or null |
renderTemplate(tmpl, data) | {{path.to.value}} template rendering |
buildPaperclipEnv(agent) | Standard PAPERCLIP_* env vars |
redactEnvForLogs(env) | Redact sensitive keys for onMeta |
ensureAbsoluteDirectory(cwd) | Validate cwd exists and is absolute |
ensureCommandResolvable(cmd, cwd, env) | Validate command is in PATH |
ensurePathInEnv(env) | Ensure PATH exists in env |
runChildProcess(runId, cmd, args, opts) | Spawn with timeout, logging, capture |
snake_case (e.g. claude_local, codex_local)@paperclipai/adapter-<kebab-name>packages/adapters/<kebab-name>/config values directly — always use asString, asNumber, etc.agentConfigurationDocpromptTemplate for every runrenderTemplate() with the standard variable setDEFAULT_PAPERCLIP_AGENT_PROMPT_TEMPLATE from @paperclipai/adapter-utils/server-utils so local adapters share Paperclip's execution contract: act in the same heartbeat, avoid planning-only exits unless requested, leave durable progress and a next action, use child issues instead of polling, mark blockers with owner/action, and respect governance boundaries.errorMessage on failureresultJson when parsing failsonLog("stdout", ...) and onLog("stderr", ...) for all process output — this feeds the real-time run vieweronMeta(...) before spawning to record invocation detailsredactEnvForLogs() when including env in metaPaperclip ships shared skills (in the repo's top-level skills/ directory) that agents need at runtime — things like the paperclip API skill and the paperclip-create-agent workflow skill. Each adapter is responsible for making these skills discoverable by its agent runtime without polluting the agent's working directory.
The constraint: never copy or symlink skills into the agent's cwd. The cwd is the user's project checkout — writing .claude/skills/ or any other files into it would contaminate the repo with Paperclip internals, break git status, and potentially leak into commits.
The pattern: create a clean, isolated location for skills and tell the agent runtime to look there.
How claude-local does it:
mkdtemp("paperclip-skills-").claude/skills/ (the directory structure Claude Code expects)skills/ into the tmpdir's .claude/skills/--add-dir <tmpdir> — this makes Claude Code discover the skills as if they were registered in that directory, without touching the agent's actual cwdfinally block after the run completes// From claude-local execute.ts
async function buildSkillsDir(): Promise<string> {
const tmp = await fs.mkdtemp(path.join(os.tmpdir(), "paperclip-skills-"));
const target = path.join(tmp, ".claude", "skills");
await fs.mkdir(target, { recursive: true });
const entries = await fs.readdir(PAPERCLIP_SKILLS_DIR, { withFileTypes: true });
for (const entry of entries) {
if (entry.isDirectory()) {
await fs.symlink(
path.join(PAPERCLIP_SKILLS_DIR, entry.name),
path.join(target, entry.name),
);
}
}
return tmp;
}
// In execute(): pass --add-dir to Claude Code
const skillsDir = await buildSkillsDir();
args.push("--add-dir", skillsDir);
// ... run process ...
// In finally: fs.rm(skillsDir, { recursive: true, force: true })How codex-local does it:
Codex has a global personal skills directory ($CODEX_HOME/skills or ~/.codex/skills). The adapter symlinks Paperclip skills there if they don't already exist. This is acceptable because it's the agent tool's own config directory, not the user's project.
// From codex-local execute.ts
async function ensureCodexSkillsInjected(onLog) {
const skillsHome = path.join(codexHomeDir(), "skills");
await fs.mkdir(skillsHome, { recursive: true });
for (const entry of entries) {
const target = path.join(skillsHome, entry.name);
const existing = await fs.lstat(target).catch(() => null);
if (existing) continue; // Don't overwrite user's own skills
await fs.symlink(source, target);
}
}For a new adapter: figure out how your agent runtime discovers skills/plugins, then choose the cleanest injection path:
skills/ directory directly.Skills as loaded procedures, not prompt bloat. The Paperclip skills (like paperclip and paperclip-create-agent) are designed as on-demand procedures: the agent sees skill metadata (name + description) in its context, but only loads the full SKILL.md content when it decides to invoke a skill. This keeps the base prompt small. When writing agentConfigurationDoc or prompt templates for your adapter, do not inline skill content — let the agent runtime's skill discovery do the work. The descriptions in each SKILL.md frontmatter act as routing logic: they tell the agent when to load the full skill, not what the skill contains.
Explicit vs. fuzzy skill invocation. For production workflows where reliability matters (e.g. an agent that must always call the Paperclip API to report status), use explicit instructions in the prompt template: "Use the paperclip skill to report your progress." Fuzzy routing (letting the model decide based on description matching) is fine for exploratory tasks but unreliable for mandatory procedures.
Adapters sit at the boundary between Paperclip's orchestration layer and arbitrary agent execution. This is a high-risk surface.
The agent process runs LLM-driven code that reads external files, fetches URLs, and executes tools. Its output may be influenced by prompt injection from the content it processes. The adapter's parse layer is a trust boundary — validate everything, execute nothing.
Never put secrets (API keys, tokens) into prompt templates or config fields that flow through the LLM. Instead, inject them as environment variables that the agent's tools can read directly:
PAPERCLIP_API_KEY is injected by the server into the process environment, not the promptconfig.env are passed as env vars, redacted in onMeta logsredactEnvForLogs() helper automatically masks any key matching /(key|token|secret|password|authorization|cookie)/iThis follows the "sidecar injection" pattern: the model never sees the real secret value, but the tools it invokes can read it from the environment.
If your agent runtime supports network access controls (sandboxing, allowlists), configure them in the adapter:
cwd and env config determine what the agent process can access on the filesystem.dangerouslySkipPermissions / dangerouslyBypassApprovalsAndSandbox flags exist for development convenience but must be documented as dangerous in agentConfigurationDoc. Production deployments should not use them.timeoutSec, graceSec) are safety rails — always enforce them. A runaway agent process without a timeout can consume unbounded resources.The UI run viewer displays these entry kinds:
| Kind | Fields | Usage |
|---|---|---|
init | model, sessionId | Agent initialization |
assistant | text | Agent text response |
thinking | text | Agent reasoning/thinking |
user | text | User message |
tool_call | name, input | Tool invocation |
tool_result | toolUseId, content, isError | Tool result |
result | text, inputTokens, outputTokens, cachedTokens, costUsd, subtype, isError, errors | Final result with usage |
stderr | text | Stderr output |
system | text | System messages |
stdout | text | Raw stdout fallback |
Create tests in server/src/__tests__/<adapter-name>-adapter.test.ts. Test:
is<Agent>UnknownSessionError functionbuildConfig produces correct adapterConfig from form valuespackages/adapters/<name>/package.json with four exports (., ./server, ./ui, ./cli)index.ts with type, label, models, agentConfigurationDocserver/execute.ts implementing AdapterExecutionContext -> AdapterExecutionResultserver/test.ts implementing AdapterEnvironmentTestContext -> AdapterEnvironmentTestResultserver/parse.ts with output parser and unknown-session detectorserver/index.ts exporting execute, testEnvironment, sessionCodec, parse helpersui/parse-stdout.ts with StdoutLineParser for the run viewerui/build-config.ts with CreateConfigValues -> adapterConfig builderui/src/adapters/<name>/config-fields.tsx React component for agent formui/src/adapters/<name>/index.ts assembling the UIAdapterModulecli/format-event.ts with terminal formattercli/index.ts exporting the formatterserver/src/adapters/registry.tsui/src/adapters/registry.tscli/src/adapters/registry.tspnpm-workspace.yaml (if not already covered by glob)© paperclipai, MIT. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file
Just SKILL.md in .agents/skills/create-agent-adapter of paperclipai/paperclip.
Open the folder on GitHubat commit b9750b1
Create Agent Adapter next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.
| Skill | Stars | Used in | Tokens | Auto-check | Licence | Repo updated |
|---|---|---|---|---|---|---|
| Create Agent Adapter this skillpaperclipai/paperclip | 99k | — | ~8k | Automated safety check: Pass | MIT | |
| Agent BuildershareAI-lab/learn-claude-code | 78k | 5 repos | ~1.2k | Automated safety check: Pass | MIT | |
| Senior Prompt Engineermaslennikov-ig/claude-code-orchestrator-kit | 260 | 3 repos | ~1.4k | Automated safety check: Pass | Custom licence | |
| Create Agentgnekt/My-Brain-Is-Full-Crew | 3.9k | — | ~3.1k | Automated safety check: Pass | Custom licence | |
| Ms Agent Framework RAGshuyu-labs/WebCode | 278 | — | ~1.1k | Automated safety check: Pass | Custom licence | |
| Google Agents CLI Adk Codepifferologo/cloud-agents-cli | 129 | 1 repos | ~768 | Automated safety check: Pass | Apache-2.0 |
shareAI-lab/learn-claude-code
Design and build AI agents for any domain. An agent skill from shareAI-lab/learn-claude-code.
maslennikov-ig/claude-code-orchestrator-kit
Provides reference guides and Python scripts for prompt optimization, RAG evaluation, and agent orchestration when building or tuning LLM systems.
gnekt/My-Brain-Is-Full-Crew
Create a new custom agent from scratch. An agent skill from gnekt/My-Brain-Is-Full-Crew.
shuyu-labs/WebCode
Comprehensive guide for building Agentic RAG systems using Microsoft Agent Framework in C.
pifferologo/cloud-agents-cli
This skill should be used when the user wants to "write agent code", "build an agent with ADK", "add a tool", "create a callback", "define an agent", "use state management", or needs ADK (Agent…
block/berd
Create, edit, or inspect Berd agents/personas stored as Agent Markdown files with YAML frontmatter under ~/.agents/agents.
paperclipai/paperclip
Scan a Paperclip user's Mine inbox, classify reversible archive candidates, request checkbox confirmation, and archive only accepted selections.
paperclipai/paperclip
Interact with the Paperclip control plane API for task coordination and governance.
paperclipai/paperclip
A skill your agent uses for Paperclip-managed tasks and heartbeats: reading task context, delivering task documents or files, updating completion or blockers, coordinating or delegating work, and…
paperclipai/paperclip
Paperclip UI design system guide for building consistent, reusable frontend components.
paperclipai/paperclip
Publish static HTML pages and asset folders to the Paperclip S3/CloudFront page host.
paperclipai/paperclip
Create new agents in Paperclip with governance-aware hiring.
Categories
Create or modify Paperclip agent adapters across server, UI, and CLI surfaces. Create Agent Adapter is an agent skill from paperclipai/paperclip. Create or modify Paperclip agent adapters across server, UI, and CLI surfaces.
Create Agent Adapter fits situations like: adding support for a new CLI agent; adapter package.
Run `npx skills add paperclipai/paperclip --skill create-agent-adapter -a claude-code`. Or copy the skill folder (.agents/skills/create-agent-adapter in paperclipai/paperclip) into .claude/skills/create-agent-adapter in your project. Claude Code loads it when a task matches its description.
Run `npx skills add paperclipai/paperclip --skill create-agent-adapter -a codex`. Or copy the skill folder (.agents/skills/create-agent-adapter in paperclipai/paperclip) into .agents/skills/create-agent-adapter in your project. Codex loads it when a task matches its description.
Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add paperclipai/paperclip --skill create-agent-adapter -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/create-agent-adapter, .gemini/skills/create-agent-adapter, .github/skills/create-agent-adapter and .opencode/skills/create-agent-adapter in your project.
Going by SKILL.md and its folder, Create Agent Adapter needs credentials named PAPERCLIP_API_KEY and ANTHROPIC_API_KEY. Our summary lists: A credential in ANTHROPIC_API_KEY; A credential in PAPERCLIP_API_KEY.
SKILL.md contains no URLs. Any network use would come from the scripts or tools the agent runs. This is read from the text; nothing was executed.
Our automated static check of SKILL.md found no risky patterns, such as piping downloads into a shell, reading credential files or hidden Unicode. It is not a guarantee. Review the folder before installing.
Create Agent Adapter is published under the MIT licence (the repository's licence). It allows redistribution, so the full SKILL.md is shown on this page.
About 8k tokens (SKILL.md is roughly 32k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full.
Skills that share tags, products or a category with Create Agent Adapter: Agent Builder (shareAI-lab/learn-claude-code, 78k stars), Senior Prompt Engineer (maslennikov-ig/claude-code-orchestrator-kit, 260 stars), Create Agent (gnekt/My-Brain-Is-Full-Crew, 3.9k stars) and Ms Agent Framework RAG (shuyu-labs/WebCode, 278 stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.
paperclipai (a GitHub organization) maintains it in paperclipai/paperclip, which has 98,967 GitHub stars. The repository holds 60 skills in this directory. The repository was last updated on October 9, 2026.
Source: paperclipai/paperclip on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.