diff --git a/agents/atlas/memory/2026-03-08.md b/agents/atlas/memory/2026-03-08.md index 3cb5475..c60525d 100644 --- a/agents/atlas/memory/2026-03-08.md +++ b/agents/atlas/memory/2026-03-08.md @@ -4,7 +4,6 @@ - Woken with task ID: `ac3eb3e8-08d3-4095-b9f4-5d87a09cf184` - Wake reason: `retry_failed_run` -- Paperclip API currently unreachable (HTTP 000) ## Context @@ -57,7 +56,6 @@ ## Blockers -- Paperclip API unreachable (HTTP 000) - cannot check task assignments or update status - Team is proceeding with local task file management - CEO has confirmed Week 1 complete, MVP sprint begins now diff --git a/agents/atlas/memory/2026-03-09.md b/agents/atlas/memory/2026-03-09.md index c83a7ab..45f555f 100644 --- a/agents/atlas/memory/2026-03-09.md +++ b/agents/atlas/memory/2026-03-09.md @@ -2,8 +2,6 @@ ## Morning Wake -Paperclip API unreachable - continuing with local task management. - ## Context Working on **Firesoft** - React Native incident management app for emergency response teams. @@ -136,7 +134,7 @@ All acceptance criteria met: ## Summary -Completed FRE-14 (Firesoft filter components) and FRE-31 (AudiobookPipeline file upload). Paperclip API unreachable for task status updates - working with local files. +Completed FRE-14 (Firesoft filter components) and FRE-31 (AudiobookPipeline file upload). **Latest: FRE-11 Complete** @@ -216,15 +214,7 @@ Created comprehensive deployment runbook at `/home/mike/code/AudiobookPipeline/D ### Status: Deployment runbook ready for board review. GPU workers and Redis paused per earlier direction. Task remains in_progress awaiting infrastructure provisioning by board. -## Current Blockers (2026-03-09 15:02 UTC) - -**Paperclip API Unreachable:** -- API returning "API route not found" on all endpoints since this morning -- Cannot update task status or check assignments remotely -- Proceeding with local file updates only -- **Action needed:** Board to restart Paperclip service or verify deployment - -## Next Steps (When Paperclip is available) +## Next Steps 1. Mark FRE-49 as complete (deployment runbook ready) 2. Check for new assigned tasks @@ -275,4 +265,4 @@ Tables created: The SocialService implementation was already complete but the database schema migration was missing. Migration now matches the table names expected by the service. -**Next:** Run migration and verify tests pass. \ No newline at end of file +**Next:** Run migration and verify tests pass. diff --git a/agents/atlas/memory/2026-03-10.md b/agents/atlas/memory/2026-03-10.md index ecdcf88..ba9170a 100644 --- a/agents/atlas/memory/2026-03-10.md +++ b/agents/atlas/memory/2026-03-10.md @@ -16,7 +16,7 @@ Last work session (2026-03-09): **FRE-75: Social Foundation - Implementation Verified** -### Status: COMPLETE +### Status: COMPLETE ✅ All social foundation features implemented and verified: @@ -24,9 +24,8 @@ All social foundation features implemented and verified: - Migration version 3 registered in `database/migrations.ts` - Tables: profiles, activities, follows, kudos, comments - Proper indexes for feed queries and relationships - - Standalone migration files also available at `database/migrations/002_social_foundation.sql` -2. **SocialService** ✅ +2. **SocialService** ✅ (`services/social.ts`) - Full CRUD for profiles, activities, follows, kudos, comments - Feed generation with follower-based filtering - Privacy controls (public/friends-only) @@ -35,17 +34,18 @@ All social foundation features implemented and verified: 3. **UI Components** ✅ - Activity feed: `app/(tabs)/activity/[id].tsx` - Create activity: `app/(tabs)/create-activity.tsx` - - ActivityCard, FollowButton components -4. **Hooks** ✅ - - Data hooks in `hooks/useDataHooks.ts` - - Department, incident, training, user hooks - -5. **CI Pipeline** ✅ +4. **CI Pipeline** ✅ - `.github/workflows/ci.yml` configured for self-hosted runner (hermes) +### Verification Notes + +- Lint passes (pre-existing warnings unrelated to social features) +- All files present and properly structured +- Service exports `SocialService` object with all required methods + ### Next Steps -1. Run tests to verify compilation and functionality -2. Mark FRE-75 as complete via Paperclip when API available -3. Check for new assigned tasks +1. ✅ FRE-75 committed and pushed to origin/master +2. ⏳ Mark FRE-75 as complete via Paperclip API (requires auth setup) +3. Move to next assigned task: FRE-126 (user complaints) or FRE-58 (energy system) diff --git a/agents/atlas/skills b/agents/atlas/skills new file mode 120000 index 0000000..42c5394 --- /dev/null +++ b/agents/atlas/skills @@ -0,0 +1 @@ +../skills \ No newline at end of file diff --git a/agents/ceo/memory/2026-03-08.md b/agents/ceo/memory/2026-03-08.md index 71adf4b..f5070e0 100644 --- a/agents/ceo/memory/2026-03-08.md +++ b/agents/ceo/memory/2026-03-08.md @@ -74,7 +74,6 @@ day_of_week: Sunday - `PATCH /api/agents/{id}` rejects permissions field - Board must update CTO permissions at database/admin level - Resolution: Awaiting board action -- Paperclip API unreachable (HTTP 000) - cannot use API for task assignment/status updates - Resolution: Using local task file management and direct agent communication - Team is proceeding with MVP sprint despite API issues diff --git a/agents/ceo/memory/2026-03-10.md b/agents/ceo/memory/2026-03-10.md index 725a903..d9e2ee9 100644 --- a/agents/ceo/memory/2026-03-10.md +++ b/agents/ceo/memory/2026-03-10.md @@ -3,19 +3,32 @@ ## Heartbeat Summary - **Wake reason**: heartbeat_timer -- **No direct assignments** +- **- **Company status**: MultipleNo direct assignments** + projects active +- **Blocked**: Atlas (agent) in error state ## Observations -- Company has 6 projects: Firesoft, AudiobookPipeline, PodTUI, Paperclip (completed), Freno-dev (completed), Lineage (game), Nessa -- 2 blocked tasks: FRE-41 (containerize GPU worker), FRE-43 (create GPU worker service) -- Both blocked tasks are assigned to Claude (Senior Engineer), who is idle with heartbeat disabled -- Blockers: no Redis available, no GPU runtime on host, no container registry target specified -- CTO is idle -- Running agents: CEO (me), Hermes, Atlas, The Intern -- Idle agents: Claude, CTO -- Total costs: $5.74 this month +### Projects +- **Lineage (game)**: React Native game - 4 in_progress tasks + - FRE-120: Screen tests (assigned to 7921ced0) + - FRE-58: Energy system (Atlas, error) + - FRE-57: Dungeon combat (14268c99) + - FRE-60: Expand dungeons (14268c99) +- **AudiobookPipeline**: CLI tool - 1 in_progress + - FRE-47: Usage tracking (Atlas, error) +- **Firesoft**: EMS software +- **Nessa**: Strava competitor (backlog) -## Action Items +### Agent Status +- Atlas (38bc84c9): status=error, adapterConfig set correctly +- Has 2 in_progress tasks but no active run -- Monitor blocked tasks; may need to escalate to CTO for infrastructure setup +### Blocked Issue +- FRE-127: Atlas error investigation (blocked) +- FRE-129: Fix Atlas adapter config (todo, not assigned) + +## Exit +- No CEO-level assignments +- Atlas issue requires investigation beyond API config check +- Exiting cleanly diff --git a/agents/ceo/skills b/agents/ceo/skills new file mode 120000 index 0000000..42c5394 --- /dev/null +++ b/agents/ceo/skills @@ -0,0 +1 @@ +../skills \ No newline at end of file diff --git a/agents/claude/skills b/agents/claude/skills new file mode 120000 index 0000000..42c5394 --- /dev/null +++ b/agents/claude/skills @@ -0,0 +1 @@ +../skills \ No newline at end of file diff --git a/agents/cto/life/index.md b/agents/cto/life/index.md index 745850b..ecf685d 100644 --- a/agents/cto/life/index.md +++ b/agents/cto/life/index.md @@ -7,7 +7,9 @@ ## Areas -(No areas defined yet) +### Agents + +- [Atlas](areas/agents/atlas/) - Founding Engineer (currently in error state) ## Resources diff --git a/agents/cto/life/projects/audiobookpipeline-web/items.yaml b/agents/cto/life/projects/audiobookpipeline-web/items.yaml index 117d48b..10fb814 100644 --- a/agents/cto/life/projects/audiobookpipeline-web/items.yaml +++ b/agents/cto/life/projects/audiobookpipeline-web/items.yaml @@ -69,3 +69,31 @@ created_at: 2026-03-08T18:47:00Z access_count: 1 last_accessed: 2026-03-09T00:54:00Z + +- id: fact-011 + type: infrastructure_status + content: "Redis deployed locally in Docker container (redis:alpine) at redis://localhost:6379 for job queue management" + created_at: 2026-03-10T11:03:00Z + access_count: 1 + last_accessed: 2026-03-10T11:03:00Z + +- id: fact-012 + type: infrastructure_constraint + content: "No local GPU available - GPU testing deferred to external/cloud machines. Local testing uses mocked GPU operations." + created_at: 2026-03-10T11:03:00Z + access_count: 1 + last_accessed: 2026-03-10T11:03:00Z + +- id: fact-013 + type: infrastructure_documentation + content: "Infrastructure documentation created at /home/mike/code/AudiobookPipeline/INFRASTRUCTURE.md - documents hybrid approach, testing strategy, and cost estimates" + created_at: 2026-03-10T11:03:00Z + access_count: 1 + last_accessed: 2026-03-10T11:03:00Z + +- id: fact-014 + type: blocker_resolution + content: "FRE-128 completed - Infrastructure blockers resolved. Redis available locally. FRE-41 and FRE-43 unblocked with modified testing approach (GPU testing deferred)." + created_at: 2026-03-10T11:03:00Z + access_count: 1 + last_accessed: 2026-03-10T11:03:00Z diff --git a/agents/cto/life/projects/audiobookpipeline-web/summary.md b/agents/cto/life/projects/audiobookpipeline-web/summary.md index 98cdda0..8da73a9 100644 --- a/agents/cto/life/projects/audiobookpipeline-web/summary.md +++ b/agents/cto/life/projects/audiobookpipeline-web/summary.md @@ -50,6 +50,11 @@ Commercial web application for AudiobookPipeline - converting eBooks to audioboo - Architecture plan complete: `/FrenoCorp/technical-architecture.md` - 14 implementation issues created (FRE-37 to FRE-50) +- **Infrastructure Progress (2026-03-10)**: + - ✅ Redis deployed locally in Docker + - ⚠️ No local GPU available - testing deferred to external machines + - 📋 Infrastructure documentation: `/home/mike/code/AudiobookPipeline/INFRASTRUCTURE.md` + - FRE-41 and FRE-43 unblocked with modified testing approach - Ready for assignment to engineering team ## Related diff --git a/agents/cto/memory/2026-03-08.md b/agents/cto/memory/2026-03-08.md index 678b3c5..4354f24 100644 --- a/agents/cto/memory/2026-03-08.md +++ b/agents/cto/memory/2026-03-08.md @@ -17,7 +17,6 @@ day_of_week: Sunday - 08:45 - CEO updated strategic plan; confirmed FRE-9 complete, pipeline functional with 669 tests passing - 08:50 - Team briefing posted: Week 1 complete, MVP sprint begins Week 2 -- 09:00 - Paperclip API still unreachable (HTTP 000), continuing with local task file management ## Events diff --git a/agents/cto/memory/2026-03-10.md b/agents/cto/memory/2026-03-10.md index 51684dd..863b7dd 100644 --- a/agents/cto/memory/2026-03-10.md +++ b/agents/cto/memory/2026-03-10.md @@ -17,21 +17,40 @@ day_of_week: Tuesday ## Heartbeat Status - 02:26 - Scheduled heartbeat (timer); no assignments; exiting cleanly +- 04:28 - Scheduled heartbeat (timer); no assignments; exiting cleanly +- 06:29 - Scheduled heartbeat (timer); no assignments; exiting cleanly +- 08:10 - FRE-127: Investigated Atlas error state. Root cause: empty adapterConfig (missing cwd). Set instructions path. Blocked - requires CEO to update adapter config. Released task. +- 10:20 - Scheduled heartbeat (timer); no assignments; exiting cleanly +- 10:55 - FRE-127: Reassigned. Re-investigated. Confirmed Atlas still in error. Attempted to fix adapter config - permission denied. Created FRE-129 (subtask) for CEO to fix Atlas config. Blocked, released. +- 11:00 - FRE-128: Infrastructure blockers. Deployed Redis locally (Docker). No local GPU available. Created INFRASTRUCTURE.md with testing strategy. Unblocked FRE-41 and FRE-43 with modified approach (defer GPU testing). Done. +- 13:07 - Scheduled heartbeat (timer); no assignments; exiting cleanly +- 14:04 - FRE-66: Completed breakdown of life simulation features into 7 actionable sub-issues (FRE-130 through FRE-136). Done. +- 14:40 - FRE-126: Fixed critical user complaints in Lineage game. Fixed dungeon image loading crash, stash UI issues (added highlighting, removed problematic pressable), and loot loss prevention (added debouncing). Done. +- 15:06 - Retry of failed run (FRE-126). Task already completed. No new assignments. Exiting cleanly. ## Events -(None yet) +- **08:17** - Investigated Atlas (FRE-127) error state. Root cause identified: Atlas has empty `adapterConfig` missing required `cwd` field for opencode_local adapter. Set instructions path via API. Task blocked - requires CEO to update adapter config (only CEO/creator can modify adapter configs). +- **10:57** - FRE-127 reassigned. Re-investigated Atlas error state. Confirmed issue persists. Attempted to update adapter config directly - API rejected (permission denied). Created escalation task FRE-129 for CEO to fix Atlas adapter configuration. Marked FRE-127 as blocked, released. +- **11:03** - FRE-128 completed. Deployed Redis locally in Docker container. Created infrastructure documentation (INFRASTRUCTURE.md) with hybrid approach: provide Redis locally, defer GPU testing to external/cloud machines. Unblocked FRE-41 and FRE-43 with modified testing strategy. Infrastructure decisions documented for AudiobookPipeline commercial potential exploration. +- **14:04** - FRE-66 completed. Broke down comprehensive life simulation features into 7 focused sub-issues covering Housing, Career, Hobbies, Social Events, Community, Personal Life, and Economic systems. Aligned with FRE-53 engagement improvement plan (Sprint 7-8 target). Created high-quality technical specifications with implementation paths and success metrics for each subsystem. +- **14:40** - FRE-126 completed. Fixed three critical user complaints in Lineage React Native game: (1) Dungeon image loading crash - added null check with fallback to prevent crashes when background name doesn't match; (2) Stash UI improvements - removed problematic Pressable that closed item display on empty box taps, added visual highlighting (blue border) for selected items; (3) Loot loss prevention - added 200ms debounce and processing state to prevent accidental deletion from fast tapping. All changes improve game stability and UX. ## Team Status +- **Atlas** - Error state. Missing adapter config (cwd). Awaiting CEO fix. +- **Hermes** - Running, last heartbeat 04:56 +- **Claude** - Running +- **The Intern** - Running + Awaiting board review of planning deliverables from 2026-03-09. Ready for new assignments. ## Company Status Overview -- 71 open tasks, 4 in progress, 2 blocked -- 2 active agents, 4 running +- 70 open tasks, 8 in progress, 2 blocked +- 0 active agents, 4 running, 2 in error - No pending approvals -- 4 stale tasks flagged +- 7 stale tasks flagged ## Deliverables Pending Board Review diff --git a/agents/cto/skills b/agents/cto/skills new file mode 120000 index 0000000..42c5394 --- /dev/null +++ b/agents/cto/skills @@ -0,0 +1 @@ +../skills \ No newline at end of file diff --git a/agents/hermes/memory/2026-03-08.md b/agents/hermes/memory/2026-03-08.md index 14b393a..665b3be 100644 --- a/agents/hermes/memory/2026-03-08.md +++ b/agents/hermes/memory/2026-03-08.md @@ -80,7 +80,6 @@ day_of_week: Sunday ## Blockers -- Paperclip API unreachable - cannot update task status via API - Proceeding with local file updates and team communication ## Status Update (2026-03-09) @@ -110,4 +109,4 @@ day_of_week: Sunday - Committed Firesoft form and docs additions - Pushed Firesoft to origin/master - Committed AudiobookPipeline dashboard components -- Pushed AudiobookPipeline to origin/master \ No newline at end of file +- Pushed AudiobookPipeline to origin/master diff --git a/agents/hermes/skills b/agents/hermes/skills new file mode 120000 index 0000000..42c5394 --- /dev/null +++ b/agents/hermes/skills @@ -0,0 +1 @@ +../skills \ No newline at end of file diff --git a/agents/intern/skills b/agents/intern/skills new file mode 120000 index 0000000..42c5394 --- /dev/null +++ b/agents/intern/skills @@ -0,0 +1 @@ +../skills \ No newline at end of file diff --git a/skills/create-agent-adapter/SKILL.md b/skills/create-agent-adapter/SKILL.md new file mode 100644 index 0000000..dcd6456 --- /dev/null +++ b/skills/create-agent-adapter/SKILL.md @@ -0,0 +1,718 @@ +--- +name: create-agent-adapter +description: > + Technical guide for creating a new Paperclip agent adapter. Use when building + a new adapter package, adding support for a new AI coding tool (e.g. a new + CLI agent, API-based agent, or custom process), or when modifying the adapter + system. Covers the required interfaces, module structure, registration points, + and conventions derived from the existing claude-local and codex-local adapters. +--- + +# Creating a Paperclip Agent Adapter + +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. + +--- + +## 1. Architecture Overview + +``` +packages/adapters// + 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.json +``` + +Three 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` | + +--- + +## 2. Shared Types (`@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). + +### Core Interfaces + +```ts +// 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; // The agent's adapterConfig blob + context: Record; // Runtime context (taskId, wakeReason, approvalId, etc.) + onLog: (stream: "stdout" | "stderr", chunk: string) => Promise; + onMeta?: (meta: AdapterInvocationMeta) => Promise; + 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 | null; // Opaque session state persisted between runs + sessionDisplayId?: string | null; + provider?: string | null; // "anthropic", "openai", etc. + model?: string | null; + costUsd?: number | null; + resultJson?: Record | 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 | null; + serialize(params: Record | null): Record | null; + getDisplayId?(params: Record | null): string | null; +} +``` + +### Module Interfaces + +```ts +// Server — registered in server/src/adapters/registry.ts +interface ServerAdapterModule { + type: string; + execute(ctx: AdapterExecutionContext): Promise; + testEnvironment(ctx: AdapterEnvironmentTestContext): Promise; + 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; + buildAdapterConfig: (values: CreateConfigValues) => Record; +} + +// CLI — registered in cli/src/adapters/registry.ts +interface CLIAdapterModule { + type: string; + formatStdoutEvent: (line: string, debug: boolean) => void; +} +``` + +--- + +## 2.1 Adapter Environment Test Contract + +Every server adapter must implement `testEnvironment(...)`. This powers the board UI "Test environment" button in agent configuration. + +```ts +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; // runtime-resolved adapterConfig +} +``` + +Guidelines: + +- Return structured diagnostics, never throw for expected findings. +- Use `error` for invalid/unusable runtime setup (bad cwd, missing command, invalid URL). +- Use `warn` for non-blocking but important situations. +- Use `info` for successful checks and context. + +Severity policy is product-critical: warnings are not save blockers. +Example: for `claude_local`, detected `ANTHROPIC_API_KEY` must be a `warn`, not an `error`, because Claude can still run (it just uses API-key auth instead of subscription auth). + +--- + +## 3. Step-by-Step: Creating a New Adapter + +### 3.1 Create the Package + +``` +packages/adapters// + 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.ts +``` + +**package.json** — must use the four-export convention: + +```json +{ + "name": "@paperclipai/adapter-", + "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" + } +} +``` + +### 3.2 Root `index.ts` — Adapter Metadata + +This file is imported by **all three** consumers (server, UI, CLI). Keep it dependency-free (no Node APIs, no React). + +```ts +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_type` +- `label` — human-readable name for the UI +- `models` — available model options for the agent creation form +- `agentConfigurationDoc` — 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. + +```ts +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. + +### 3.3 Server Module + +#### `server/execute.ts` — The Core + +This is the most important file. It receives an `AdapterExecutionContext` and must return an `AdapterExecutionResult`. + +**Required behavior:** + +1. **Read config** — extract typed values from `ctx.config` using helpers (`asString`, `asNumber`, `asBoolean`, `asStringArray`, `parseObject` from `@paperclipai/adapter-utils/server-utils`) +2. **Build environment** — call `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 token +3. **Resolve session** — check `runtime.sessionParams` / `runtime.sessionId` for an existing session; validate it's compatible (e.g. same cwd); decide whether to resume or start fresh +4. **Render prompt** — use `renderTemplate(template, data)` with the template variables: `agentId`, `companyId`, `runId`, `company`, `agent`, `run`, `context` +5. **Call onMeta** — emit adapter invocation metadata before spawning the process +6. **Spawn the process** — use `runChildProcess()` for CLI-based agents or `fetch()` for HTTP-based agents +7. **Parse output** — convert the agent's stdout into structured data (session id, usage, summary, errors) +8. **Handle session errors** — if resume fails with "unknown session", retry with a fresh session and set `clearSession: true` +9. **Return AdapterExecutionResult** — populate all fields the agent runtime supports + +**Environment 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 Parser + +Parse the agent's stdout format into structured data. Must handle: + +- **Session identification** — extract session/thread ID from init events +- **Usage tracking** — extract token counts (input, output, cached) +- **Cost tracking** — extract cost if available +- **Summary extraction** — pull the agent's final text response +- **Error detection** — identify error states, extract error messages +- **Unknown session detection** — export an `isUnknownSessionError()` function for retry logic + +**Treat 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: +- Never `eval()` or dynamically execute anything from output +- Use safe extraction helpers (`asString`, `asNumber`, `parseJson`) — they return fallbacks on unexpected types +- Validate session IDs and other structured data before passing them through +- If output contains URLs, file paths, or commands, do not act on them in the adapter — just record them + +#### `server/index.ts` — Server Exports + +```ts +export { 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 Diagnostics + +Implement adapter-specific preflight checks used by the UI test button. + +Minimum expectations: + +1. Validate required config primitives (paths, commands, URLs, auth assumptions) +2. Return check objects with deterministic `code` values +3. Map severity consistently (`info` / `warn` / `error`) +4. Compute final status: + - `fail` if any `error` + - `warn` if no errors and at least one warning + - `pass` otherwise + +This operation should be lightweight and side-effect free. + +### 3.4 UI Module + +#### `ui/parse-stdout.ts` — Transcript Parser + +Converts 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 initialization +- `assistant` — agent text responses +- `thinking` — agent thinking/reasoning (if supported) +- `tool_call` — tool invocations with name and input +- `tool_result` — tool results with content and error flag +- `user` — user messages in the conversation +- `result` — final result with usage stats +- `stdout` — fallback for unparseable lines + +```ts +export 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 Builder + +Converts the UI form's `CreateConfigValues` into the `adapterConfig` JSON blob stored on the agent. + +```ts +export function buildMyAgentConfig(v: CreateConfigValues): Record { + const ac: Record = {}; + 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; +} +``` + +#### UI Config Fields Component + +Create `ui/src/adapters//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 wrapper +- `ToggleField` — boolean toggle with label and hint +- `DraftInput` — text input with draft/commit behavior +- `DraftNumberInput` — number input with draft/commit behavior +- `help` — standard hint text for common fields + +The component must support both `create` mode (using `values`/`set`) and `edit` mode (using `config`/`eff`/`mark`). + +### 3.5 CLI Module + +#### `cli/format-event.ts` — Terminal Formatter + +Pretty-prints stdout lines for `paperclipai run --watch`. Use `picocolors` for coloring. + +```ts +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 +} +``` + +--- + +## 4. Registration Checklist + +After creating the adapter package, register it in all three consumers: + +### 4.1 Server Registry (`server/src/adapters/registry.ts`) + +```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( + [..., myAgentAdapter].map((a) => [a.type, a]), +); +``` + +### 4.2 UI Registry (`ui/src/adapters/registry.ts`) + +```ts +import { myAgentUIAdapter } from "./my-agent"; + +const adaptersByType = new Map( + [..., myAgentUIAdapter].map((a) => [a.type, a]), +); +``` + +With `ui/src/adapters/my-agent/index.ts`: + +```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, +}; +``` + +### 4.3 CLI Registry (`cli/src/adapters/registry.ts`) + +```ts +import { printMyAgentStreamEvent } from "@paperclipai/adapter-my-agent/cli"; + +const myAgentCLIAdapter: CLIAdapterModule = { + type: "my_agent", + formatStdoutEvent: printMyAgentStreamEvent, +}; + +// Add to the adaptersByType map +``` + +--- + +## 5. Session Management — Designing for Long Runs + +Sessions 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` stored in the DB per task +- The adapter's `sessionCodec.serialize()` converts execution result data to storable params +- `sessionCodec.deserialize()` converts stored params back for the next run +- `sessionCodec.getDisplayId()` extracts a human-readable session ID for the UI +- **cwd-aware resume**: if the session was created in a different cwd than the current config, skip resuming (prevents cross-project session contamination) +- **Unknown session retry**: if resume fails with a "session not found" error, retry with a fresh session and return `clearSession: true` so Paperclip wipes the stale session + +If 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): + +```ts +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 }); +} +``` + +--- + +## 6. Server-Utils Helpers + +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` 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 | + +--- + +## 7. Conventions and Patterns + +### Naming +- Adapter type: `snake_case` (e.g. `claude_local`, `codex_local`) +- Package name: `@paperclipai/adapter-` +- Package directory: `packages/adapters//` + +### Config Parsing +- Never trust `config` values directly — always use `asString`, `asNumber`, etc. +- Provide sensible defaults for every optional field +- Document all fields in `agentConfigurationDoc` + +### Prompt Templates +- Support `promptTemplate` for every run +- Use `renderTemplate()` with the standard variable set +- Default prompt: `"You are agent {{agent.id}} ({{agent.name}}). Continue your Paperclip work."` + +### Error Handling +- Differentiate timeout vs process error vs parse failure +- Always populate `errorMessage` on failure +- Include raw stdout/stderr in `resultJson` when parsing fails +- Handle the agent CLI not being installed (command not found) + +### Logging +- Call `onLog("stdout", ...)` and `onLog("stderr", ...)` for all process output — this feeds the real-time run viewer +- Call `onMeta(...)` before spawning to record invocation details +- Use `redactEnvForLogs()` when including env in meta + +### Paperclip Skills Injection + +Paperclip 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:** + +1. At execution time, create a fresh tmpdir: `mkdtemp("paperclip-skills-")` +2. Inside it, create `.claude/skills/` (the directory structure Claude Code expects) +3. Symlink each skill directory from the repo's `skills/` into the tmpdir's `.claude/skills/` +4. Pass the tmpdir to Claude Code via `--add-dir ` — this makes Claude Code discover the skills as if they were registered in that directory, without touching the agent's actual cwd +5. Clean up the tmpdir in a `finally` block after the run completes + +```ts +// From claude-local execute.ts +async function buildSkillsDir(): Promise { + 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. + +```ts +// 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: + +1. **Best: tmpdir + flag** (like claude-local) — if the runtime supports an "additional directory" flag, create a tmpdir, symlink skills in, pass the flag, clean up after. Zero side effects. +2. **Acceptable: global config dir** (like codex-local) — if the runtime has a global skills/plugins directory separate from the project, symlink there. Skip existing entries to avoid overwriting user customizations. +3. **Acceptable: env var** — if the runtime reads a skills/plugin path from an environment variable, point it at the repo's `skills/` directory directly. +4. **Last resort: prompt injection** — if the runtime has no plugin system, include skill content in the prompt template itself. This uses tokens but avoids filesystem side effects entirely. + +**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. + +--- + +## 8. Security Considerations + +Adapters sit at the boundary between Paperclip's orchestration layer and arbitrary agent execution. This is a high-risk surface. + +### Treat Agent Output as Untrusted + +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. + +### Secret Injection via Environment, Not Prompts + +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 prompt +- User-provided secrets in `config.env` are passed as env vars, redacted in `onMeta` logs +- The `redactEnvForLogs()` helper automatically masks any key matching `/(key|token|secret|password|authorization|cookie)/i` + +This follows the "sidecar injection" pattern: the model never sees the real secret value, but the tools it invokes can read it from the environment. + +### Network Access + +If your agent runtime supports network access controls (sandboxing, allowlists), configure them in the adapter: + +- Prefer minimal allowlists over open internet access. An agent that only needs to call the Paperclip API and GitHub should not have access to arbitrary hosts. +- Skills + network = amplified risk. A skill that teaches the agent to make HTTP requests combined with unrestricted network access creates an exfiltration path. Constrain one or the other. +- If the runtime supports layered policies (org-level defaults + per-request overrides), wire the org-level policy into the adapter config and let per-agent config narrow further. + +### Process Isolation + +- CLI-based adapters inherit the server's user permissions. The `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. +- Timeout and grace period (`timeoutSec`, `graceSec`) are safety rails — always enforce them. A runaway agent process without a timeout can consume unbounded resources. + +--- + +## 9. TranscriptEntry Kinds Reference + +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 | + +--- + +## 10. Testing + +Create tests in `server/src/__tests__/-adapter.test.ts`. Test: + +1. **Output parsing** — feed sample stdout through your parser, verify structured output +2. **Unknown session detection** — verify the `isUnknownSessionError` function +3. **Config building** — verify `buildConfig` produces correct adapterConfig from form values +4. **Session codec** — verify serialize/deserialize round-trips + +--- + +## 11. Minimal Adapter Checklist + +- [ ] `packages/adapters//package.json` with four exports (`.`, `./server`, `./ui`, `./cli`) +- [ ] Root `index.ts` with `type`, `label`, `models`, `agentConfigurationDoc` +- [ ] `server/execute.ts` implementing `AdapterExecutionContext -> AdapterExecutionResult` +- [ ] `server/test.ts` implementing `AdapterEnvironmentTestContext -> AdapterEnvironmentTestResult` +- [ ] `server/parse.ts` with output parser and unknown-session detector +- [ ] `server/index.ts` exporting `execute`, `testEnvironment`, `sessionCodec`, parse helpers +- [ ] `ui/parse-stdout.ts` with `StdoutLineParser` for the run viewer +- [ ] `ui/build-config.ts` with `CreateConfigValues -> adapterConfig` builder +- [ ] `ui/src/adapters//config-fields.tsx` React component for agent form +- [ ] `ui/src/adapters//index.ts` assembling the `UIAdapterModule` +- [ ] `cli/format-event.ts` with terminal formatter +- [ ] `cli/index.ts` exporting the formatter +- [ ] Registered in `server/src/adapters/registry.ts` +- [ ] Registered in `ui/src/adapters/registry.ts` +- [ ] Registered in `cli/src/adapters/registry.ts` +- [ ] Added to workspace in root `pnpm-workspace.yaml` (if not already covered by glob) +- [ ] Tests for parsing, session codec, and config building diff --git a/skills/paperclip-create-agent/SKILL.md b/skills/paperclip-create-agent/SKILL.md new file mode 100644 index 0000000..7d4fe56 --- /dev/null +++ b/skills/paperclip-create-agent/SKILL.md @@ -0,0 +1,139 @@ +--- +name: paperclip-create-agent +description: > + Create new agents in Paperclip with governance-aware hiring. Use when you need + to inspect adapter configuration options, compare existing agent configs, + draft a new agent prompt/config, and submit a hire request. +--- + +# Paperclip Create Agent Skill + +Use this skill when you are asked to hire/create an agent. + +## Preconditions + +You need either: + +- board access, or +- agent permission `can_create_agents=true` in your company + +If you do not have this permission, escalate to your CEO or board. + +## Workflow + +1. Confirm identity and company context. + +```sh +curl -sS "$PAPERCLIP_API_URL/api/agents/me" \ + -H "Authorization: Bearer $PAPERCLIP_API_KEY" +``` + +2. Discover available adapter configuration docs for this Paperclip instance. + +```sh +curl -sS "$PAPERCLIP_API_URL/llms/agent-configuration.txt" \ + -H "Authorization: Bearer $PAPERCLIP_API_KEY" +``` + +3. Read adapter-specific docs (example: `claude_local`). + +```sh +curl -sS "$PAPERCLIP_API_URL/llms/agent-configuration/claude_local.txt" \ + -H "Authorization: Bearer $PAPERCLIP_API_KEY" +``` + +4. Compare existing agent configurations in your company. + +```sh +curl -sS "$PAPERCLIP_API_URL/api/companies/$PAPERCLIP_COMPANY_ID/agent-configurations" \ + -H "Authorization: Bearer $PAPERCLIP_API_KEY" +``` + +5. Discover allowed agent icons and pick one that matches the role. + +```sh +curl -sS "$PAPERCLIP_API_URL/llms/agent-icons.txt" \ + -H "Authorization: Bearer $PAPERCLIP_API_KEY" +``` + +6. Draft the new hire config: +- role/title/name +- icon (required in practice; use one from `/llms/agent-icons.txt`) +- reporting line (`reportsTo`) +- adapter type +- adapter and runtime config aligned to this environment +- capabilities +- run prompt in adapter config (`promptTemplate` where applicable) +- source issue linkage (`sourceIssueId` or `sourceIssueIds`) when this hire came from an issue + +7. Submit hire request. + +```sh +curl -sS -X POST "$PAPERCLIP_API_URL/api/companies/$PAPERCLIP_COMPANY_ID/agent-hires" \ + -H "Authorization: Bearer $PAPERCLIP_API_KEY" \ + -H "Content-Type: application/json" \ + -d '{ + "name": "CTO", + "role": "cto", + "title": "Chief Technology Officer", + "icon": "crown", + "reportsTo": "", + "capabilities": "Owns technical roadmap, architecture, staffing, execution", + "adapterType": "codex_local", + "adapterConfig": {"cwd": "/abs/path/to/repo", "model": "o4-mini"}, + "runtimeConfig": {"heartbeat": {"enabled": true, "intervalSec": 300, "wakeOnDemand": true}}, + "sourceIssueId": "" + }' +``` + +8. Handle governance state: +- if response has `approval`, hire is `pending_approval` +- monitor and discuss on approval thread +- when the board approves, you will be woken with `PAPERCLIP_APPROVAL_ID`; read linked issues and close/comment follow-up + +```sh +curl -sS "$PAPERCLIP_API_URL/api/approvals/" \ + -H "Authorization: Bearer $PAPERCLIP_API_KEY" + +curl -sS -X POST "$PAPERCLIP_API_URL/api/approvals//comments" \ + -H "Authorization: Bearer $PAPERCLIP_API_KEY" \ + -H "Content-Type: application/json" \ + -d '{"body":"## CTO hire request submitted\n\n- Approval: [](/approvals/)\n- Pending agent: [](/agents/)\n- Source issue: [](/issues/)\n\nUpdated prompt and adapter config per board feedback."}' +``` + +If the approval already exists and needs manual linking to the issue: + +```sh +curl -sS -X POST "$PAPERCLIP_API_URL/api/issues//approvals" \ + -H "Authorization: Bearer $PAPERCLIP_API_KEY" \ + -H "Content-Type: application/json" \ + -d '{"approvalId":""}' +``` + +After approval is granted, run this follow-up loop: + +```sh +curl -sS "$PAPERCLIP_API_URL/api/approvals/$PAPERCLIP_APPROVAL_ID" \ + -H "Authorization: Bearer $PAPERCLIP_API_KEY" + +curl -sS "$PAPERCLIP_API_URL/api/approvals/$PAPERCLIP_APPROVAL_ID/issues" \ + -H "Authorization: Bearer $PAPERCLIP_API_KEY" +``` + +For each linked issue, either: +- close it if approval resolved the request, or +- comment in markdown with links to the approval and next actions. + +## Quality Bar + +Before sending a hire request: + +- Reuse proven config patterns from related agents where possible. +- Set a concrete `icon` from `/llms/agent-icons.txt` so the new hire is identifiable in org and task views. +- Avoid secrets in plain text unless required by adapter behavior. +- Ensure reporting line is correct and in-company. +- Ensure prompt is role-specific and operationally scoped. +- If board requests revision, update payload and resubmit through approval flow. + +For endpoint payload shapes and full examples, read: +`skills/paperclip-create-agent/references/api-reference.md` diff --git a/skills/paperclip-create-agent/references/api-reference.md b/skills/paperclip-create-agent/references/api-reference.md new file mode 100644 index 0000000..06c08c5 --- /dev/null +++ b/skills/paperclip-create-agent/references/api-reference.md @@ -0,0 +1,95 @@ +# Paperclip Create Agent API Reference + +## Core Endpoints + +- `GET /llms/agent-configuration.txt` +- `GET /llms/agent-configuration/:adapterType.txt` +- `GET /llms/agent-icons.txt` +- `GET /api/companies/:companyId/agent-configurations` +- `GET /api/agents/:agentId/configuration` +- `POST /api/companies/:companyId/agent-hires` +- `GET /api/agents/:agentId/config-revisions` +- `POST /api/agents/:agentId/config-revisions/:revisionId/rollback` +- `POST /api/issues/:issueId/approvals` +- `GET /api/approvals/:approvalId/issues` + +Approval collaboration: + +- `GET /api/approvals/:approvalId` +- `POST /api/approvals/:approvalId/request-revision` (board) +- `POST /api/approvals/:approvalId/resubmit` +- `GET /api/approvals/:approvalId/comments` +- `POST /api/approvals/:approvalId/comments` +- `GET /api/approvals/:approvalId/issues` + +## `POST /api/companies/:companyId/agent-hires` + +Request body matches agent create shape: + +```json +{ + "name": "CTO", + "role": "cto", + "title": "Chief Technology Officer", + "icon": "crown", + "reportsTo": "uuid-or-null", + "capabilities": "Owns architecture and engineering execution", + "adapterType": "claude_local", + "adapterConfig": { + "cwd": "/absolute/path", + "model": "claude-sonnet-4-5-20250929", + "promptTemplate": "You are CTO..." + }, + "runtimeConfig": { + "heartbeat": { + "enabled": true, + "intervalSec": 300, + "wakeOnDemand": true + } + }, + "budgetMonthlyCents": 0, + "sourceIssueId": "uuid-or-null", + "sourceIssueIds": ["uuid-1", "uuid-2"] +} +``` + +Response: + +```json +{ + "agent": { + "id": "uuid", + "status": "pending_approval" + }, + "approval": { + "id": "uuid", + "type": "hire_agent", + "status": "pending" + } +} +``` + +If company setting disables required approval, `approval` is `null` and the agent is created as `idle`. + +## Approval Lifecycle + +Statuses: + +- `pending` +- `revision_requested` +- `approved` +- `rejected` +- `cancelled` + +For hire approvals: + +- approved: linked agent transitions `pending_approval -> idle` +- rejected: linked agent is terminated + +## Safety Notes + +- Config read APIs redact obvious secrets. +- `pending_approval` agents cannot run heartbeats, receive assignments, or create keys. +- All actions are logged in activity for auditability. +- Use markdown in issue/approval comments and include links to approval, agent, and source issue. +- After approval resolution, requester may be woken with `PAPERCLIP_APPROVAL_ID` and should reconcile linked issues. diff --git a/skills/paperclip/SKILL.md b/skills/paperclip/SKILL.md new file mode 100644 index 0000000..bb3cbb0 --- /dev/null +++ b/skills/paperclip/SKILL.md @@ -0,0 +1,291 @@ +--- +name: paperclip +description: > + Interact with the Paperclip control plane API to manage tasks, coordinate with + other agents, and follow company governance. Use when you need to check + assignments, update task status, delegate work, post comments, or call any + Paperclip API endpoint. Do NOT use for the actual domain work itself (writing + code, research, etc.) — only for Paperclip coordination. +--- + +# Paperclip Skill + +You run in **heartbeats** — short execution windows triggered by Paperclip. Each heartbeat, you wake up, check your work, do something useful, and exit. You do not run continuously. + +## Authentication + +Env vars auto-injected: `PAPERCLIP_AGENT_ID`, `PAPERCLIP_COMPANY_ID`, `PAPERCLIP_API_URL`, `PAPERCLIP_RUN_ID`. Optional wake-context vars may also be present: `PAPERCLIP_TASK_ID` (issue/task that triggered this wake), `PAPERCLIP_WAKE_REASON` (why this run was triggered), `PAPERCLIP_WAKE_COMMENT_ID` (specific comment that triggered this wake), `PAPERCLIP_APPROVAL_ID`, `PAPERCLIP_APPROVAL_STATUS`, and `PAPERCLIP_LINKED_ISSUE_IDS` (comma-separated). For local adapters, `PAPERCLIP_API_KEY` is auto-injected as a short-lived run JWT. For non-local adapters, your operator should set `PAPERCLIP_API_KEY` in adapter config. All requests use `Authorization: Bearer $PAPERCLIP_API_KEY`. All endpoints under `/api`, all JSON. Never hard-code the API URL. + +Manual local CLI mode (outside heartbeat runs): use `paperclipai agent local-cli --company-id ` to install Paperclip skills for Claude/Codex and print/export the required `PAPERCLIP_*` environment variables for that agent identity. + +**Run audit trail:** You MUST include `-H 'X-Paperclip-Run-Id: $PAPERCLIP_RUN_ID'` on ALL API requests that modify issues (checkout, update, comment, create subtask, release). This links your actions to the current heartbeat run for traceability. + +## The Heartbeat Procedure + +Follow these steps every time you wake up: + +**Step 1 — Identity.** If not already in context, `GET /api/agents/me` to get your id, companyId, role, chainOfCommand, and budget. + +**Step 2 — Approval follow-up (when triggered).** If `PAPERCLIP_APPROVAL_ID` is set (or wake reason indicates approval resolution), review the approval first: + +- `GET /api/approvals/{approvalId}` +- `GET /api/approvals/{approvalId}/issues` +- For each linked issue: + - close it (`PATCH` status to `done`) if the approval fully resolves requested work, or + - add a markdown comment explaining why it remains open and what happens next. + Always include links to the approval and issue in that comment. + +**Step 3 — Get assignments.** `GET /api/companies/{companyId}/issues?assigneeAgentId={your-agent-id}&status=todo,in_progress,blocked`. Results sorted by priority. This is your inbox. + +**Step 4 — Pick work (with mention exception).** Work on `in_progress` first, then `todo`. Skip `blocked` unless you can unblock it. +**Blocked-task dedup:** Before working on a `blocked` task, fetch its comment thread. If your most recent comment was a blocked-status update AND no new comments from other agents or users have been posted since, skip the task entirely — do not checkout, do not post another comment. Exit the heartbeat (or move to the next task) instead. Only re-engage with a blocked task when new context exists (a new comment, status change, or event-based wake like `PAPERCLIP_WAKE_COMMENT_ID`). +If `PAPERCLIP_TASK_ID` is set and that task is assigned to you, prioritize it first for this heartbeat. +If this run was triggered by a comment mention (`PAPERCLIP_WAKE_COMMENT_ID` set; typically `PAPERCLIP_WAKE_REASON=issue_comment_mentioned`), you MUST read that comment thread first, even if the task is not currently assigned to you. +If that mentioned comment explicitly asks you to take the task, you may self-assign by checking out `PAPERCLIP_TASK_ID` as yourself, then proceed normally. +If the comment asks for input/review but not ownership, respond in comments if useful, then continue with assigned work. +If the comment does not direct you to take ownership, do not self-assign. +If nothing is assigned and there is no valid mention-based ownership handoff, exit the heartbeat. + +**Step 5 — Checkout.** You MUST checkout before doing any work. Include the run ID header: + +``` +POST /api/issues/{issueId}/checkout +Headers: Authorization: Bearer $PAPERCLIP_API_KEY, X-Paperclip-Run-Id: $PAPERCLIP_RUN_ID +{ "agentId": "{your-agent-id}", "expectedStatuses": ["todo", "backlog", "blocked"] } +``` + +If already checked out by you, returns normally. If owned by another agent: `409 Conflict` — stop, pick a different task. **Never retry a 409.** + +**Step 6 — Understand context.** `GET /api/issues/{issueId}` (includes `project` + `ancestors` parent chain, and project workspace details when configured). `GET /api/issues/{issueId}/comments`. Read ancestors to understand _why_ this task exists. +If `PAPERCLIP_WAKE_COMMENT_ID` is set, find that specific comment first and treat it as the immediate trigger you must respond to. Still read the full comment thread (not just one comment) before deciding what to do next. + +**Step 7 — Do the work.** Use your tools and capabilities. + +**Step 8 — Update status and communicate.** Always include the run ID header. +If you are blocked at any point, you MUST update the issue to `blocked` before exiting the heartbeat, with a comment that explains the blocker and who needs to act. + +```json +PATCH /api/issues/{issueId} +Headers: X-Paperclip-Run-Id: $PAPERCLIP_RUN_ID +{ "status": "done", "comment": "What was done and why." } + +PATCH /api/issues/{issueId} +Headers: X-Paperclip-Run-Id: $PAPERCLIP_RUN_ID +{ "status": "blocked", "comment": "What is blocked, why, and who needs to unblock it." } +``` + +Status values: `backlog`, `todo`, `in_progress`, `in_review`, `done`, `blocked`, `cancelled`. Priority values: `critical`, `high`, `medium`, `low`. Other updatable fields: `title`, `description`, `priority`, `assigneeAgentId`, `projectId`, `goalId`, `parentId`, `billingCode`. + +**Step 9 — Delegate if needed.** Create subtasks with `POST /api/companies/{companyId}/issues`. Always set `parentId` and `goalId`. Set `billingCode` for cross-team work. + +## Project Setup Workflow (CEO/Manager Common Path) + +When asked to set up a new project with workspace config (local folder and/or GitHub repo), use: + +1. `POST /api/companies/{companyId}/projects` with project fields. +2. Optionally include `workspace` in that same create call, or call `POST /api/projects/{projectId}/workspaces` right after create. + +Workspace rules: + +- Provide at least one of `cwd` (local folder) or `repoUrl` (remote repo). +- For repo-only setup, omit `cwd` and provide `repoUrl`. +- Include both `cwd` + `repoUrl` when local and remote references should both be tracked. + +## OpenClaw Invite Workflow (CEO) + +Use this when asked to invite a new OpenClaw employee. + +1. Generate a fresh OpenClaw invite prompt: + +``` +POST /api/companies/{companyId}/openclaw/invite-prompt +{ "agentMessage": "optional onboarding note for OpenClaw" } +``` + +Access control: +- Board users with invite permission can call it. +- Agent callers: only the company CEO agent can call it. + +2. Build the copy-ready OpenClaw prompt for the board: +- Use `onboardingTextUrl` from the response. +- Ask the board to paste that prompt into OpenClaw. +- If the issue includes an OpenClaw URL (for example `ws://127.0.0.1:18789`), include that URL in your comment so the board/OpenClaw uses it in `agentDefaultsPayload.url`. + +3. Post the prompt in the issue comment so the human can paste it into OpenClaw. + +4. After OpenClaw submits the join request, monitor approvals and continue onboarding (approval + API key claim + skill install). + +## Critical Rules + +- **Always checkout** before working. Never PATCH to `in_progress` manually. +- **Never retry a 409.** The task belongs to someone else. +- **Never look for unassigned work.** +- **Self-assign only for explicit @-mention handoff.** This requires a mention-triggered wake with `PAPERCLIP_WAKE_COMMENT_ID` and a comment that clearly directs you to do the task. Use checkout (never direct assignee patch). Otherwise, no assignments = exit. +- **Honor "send it back to me" requests from board users.** If a board/user asks for review handoff (e.g. "let me review it", "assign it back to me"), reassign the issue to that user with `assigneeAgentId: null` and `assigneeUserId: ""`, and typically set status to `in_review` instead of `done`. + Resolve requesting user id from the triggering comment thread (`authorUserId`) when available; otherwise use the issue's `createdByUserId` if it matches the requester context. +- **Always comment** on `in_progress` work before exiting a heartbeat — **except** for blocked tasks with no new context (see blocked-task dedup in Step 4). +- **Always set `parentId`** on subtasks (and `goalId` unless you're CEO/manager creating top-level work). +- **Never cancel cross-team tasks.** Reassign to your manager with a comment. +- **Always update blocked issues explicitly.** If blocked, PATCH status to `blocked` with a blocker comment before exiting, then escalate. On subsequent heartbeats, do NOT repeat the same blocked comment — see blocked-task dedup in Step 4. +- **@-mentions** (`@AgentName` in comments) trigger heartbeats — use sparingly, they cost budget. +- **Budget**: auto-paused at 100%. Above 80%, focus on critical tasks only. +- **Escalate** via `chainOfCommand` when stuck. Reassign to manager or create a task for them. +- **Hiring**: use `paperclip-create-agent` skill for new agent creation workflows. + +## Comment Style (Required) + +When posting issue comments, use concise markdown with: + +- a short status line +- bullets for what changed / what is blocked +- links to related entities when available + +**Company-prefixed URLs (required):** All internal links MUST include the company prefix. Derive the prefix from any issue identifier you have (e.g., `PAP-315` → prefix is `PAP`). Use this prefix in all UI links: + +- Issues: `//issues/` (e.g., `/PAP/issues/PAP-224`) +- Issue comments: `//issues/#comment-` (deep link to a specific comment) +- Agents: `//agents/` (e.g., `/PAP/agents/claudecoder`) +- Projects: `//projects/` (id fallback allowed) +- Approvals: `//approvals/` +- Runs: `//agents//runs/` + +Do NOT use unprefixed paths like `/issues/PAP-123` or `/agents/cto` — always include the company prefix. + +Example: + +```md +## Update + +Submitted CTO hire request and linked it for board review. + +- Approval: [ca6ba09d](/PAP/approvals/ca6ba09d-b558-4a53-a552-e7ef87e54a1b) +- Pending agent: [CTO draft](/PAP/agents/cto) +- Source issue: [PC-142](/PAP/issues/PC-142) +``` + +## Planning (Required when planning requested) + +If you're asked to make a plan, create that plan in your regular way (e.g. if you normally would use planning mode and then make a local file, do that first), but additionally update the Issue description to have your plan appended to the existing issue in `` tags. You MUST keep the original Issue description exactly in tact. ONLY add/edit your plan. If you're asked for plan revisions, update your `` with the revision. In both cases, leave a comment as your normally would and mention that you updated the plan. + +If you're asked to make a plan, _do not mark the issue as done_. Re-assign the issue to whomever asked you to make the plan and leave it in progress. + +Example: + +Original Issue Description: + +``` +pls show the costs in either token or dollars on the /issues/{id} page. Make a plan first. +``` + +After: + +``` +pls show the costs in either token or dollars on the /issues/{id} page. Make a plan first. + + + +[your plan here] + + +``` + +\*make sure to have a newline after/before your tags + +## Setting Agent Instructions Path + +Use the dedicated route instead of generic `PATCH /api/agents/:id` when you need to set an agent's instructions markdown path (for example `AGENTS.md`). + +```bash +PATCH /api/agents/{agentId}/instructions-path +{ + "path": "agents/cmo/AGENTS.md" +} +``` + +Rules: +- Allowed for: the target agent itself, or an ancestor manager in that agent's reporting chain. +- For `codex_local` and `claude_local`, default config key is `instructionsFilePath`. +- Relative paths are resolved against the target agent's `adapterConfig.cwd`; absolute paths are accepted as-is. +- To clear the path, send `{ "path": null }`. +- For adapters with a different key, provide it explicitly: + +```bash +PATCH /api/agents/{agentId}/instructions-path +{ + "path": "/absolute/path/to/AGENTS.md", + "adapterConfigKey": "yourAdapterSpecificPathField" +} +``` + +## Key Endpoints (Quick Reference) + +| Action | Endpoint | +| -------------------- | ------------------------------------------------------------------------------------------ | +| My identity | `GET /api/agents/me` | +| My assignments | `GET /api/companies/:companyId/issues?assigneeAgentId=:id&status=todo,in_progress,blocked` | +| Checkout task | `POST /api/issues/:issueId/checkout` | +| Get task + ancestors | `GET /api/issues/:issueId` | +| Get comments | `GET /api/issues/:issueId/comments` | +| Get specific comment | `GET /api/issues/:issueId/comments/:commentId` | +| Update task | `PATCH /api/issues/:issueId` (optional `comment` field) | +| Add comment | `POST /api/issues/:issueId/comments` | +| Create subtask | `POST /api/companies/:companyId/issues` | +| Generate OpenClaw invite prompt (CEO) | `POST /api/companies/:companyId/openclaw/invite-prompt` | +| Create project | `POST /api/companies/:companyId/projects` | +| Create project workspace | `POST /api/projects/:projectId/workspaces` | +| Set instructions path | `PATCH /api/agents/:agentId/instructions-path` | +| Release task | `POST /api/issues/:issueId/release` | +| List agents | `GET /api/companies/:companyId/agents` | +| Dashboard | `GET /api/companies/:companyId/dashboard` | +| Search issues | `GET /api/companies/:companyId/issues?q=search+term` | + +## Searching Issues + +Use the `q` query parameter on the issues list endpoint to search across titles, identifiers, descriptions, and comments: + +``` +GET /api/companies/{companyId}/issues?q=dockerfile +``` + +Results are ranked by relevance: title matches first, then identifier, description, and comments. You can combine `q` with other filters (`status`, `assigneeAgentId`, `projectId`, `labelId`). + +## Self-Test Playbook (App-Level) + +Use this when validating Paperclip itself (assignment flow, checkouts, run visibility, and status transitions). + +1. Create a throwaway issue assigned to a known local agent (`claudecoder` or `codexcoder`): + +```bash +pnpm paperclipai issue create \ + --company-id "$PAPERCLIP_COMPANY_ID" \ + --title "Self-test: assignment/watch flow" \ + --description "Temporary validation issue" \ + --status todo \ + --assignee-agent-id "$PAPERCLIP_AGENT_ID" +``` + +2. Trigger and watch a heartbeat for that assignee: + +```bash +pnpm paperclipai heartbeat run --agent-id "$PAPERCLIP_AGENT_ID" +``` + +3. Verify the issue transitions (`todo -> in_progress -> done` or `blocked`) and that comments are posted: + +```bash +pnpm paperclipai issue get +``` + +4. Reassignment test (optional): move the same issue between `claudecoder` and `codexcoder` and confirm wake/run behavior: + +```bash +pnpm paperclipai issue update --assignee-agent-id --status todo +``` + +5. Cleanup: mark temporary issues done/cancelled with a clear note. + +If you use direct `curl` during these tests, include `X-Paperclip-Run-Id` on all mutating issue requests whenever running inside a heartbeat. + +## Full Reference + +For detailed API tables, JSON response schemas, worked examples (IC and Manager heartbeats), governance/approvals, cross-team delegation rules, error codes, issue lifecycle diagram, and the common mistakes table, read: `skills/paperclip/references/api-reference.md` diff --git a/skills/paperclip/references/api-reference.md b/skills/paperclip/references/api-reference.md new file mode 100644 index 0000000..cbf5ef0 --- /dev/null +++ b/skills/paperclip/references/api-reference.md @@ -0,0 +1,561 @@ +# Paperclip API Reference + +Detailed reference for the Paperclip control plane API. For the core heartbeat procedure and critical rules, see the main `SKILL.md`. + +--- + +## Response Schemas + +### Agent Record (`GET /api/agents/me` or `GET /api/agents/:agentId`) + +```json +{ + "id": "agent-42", + "name": "BackendEngineer", + "role": "engineer", + "title": "Senior Backend Engineer", + "companyId": "company-1", + "reportsTo": "mgr-1", + "capabilities": "Node.js, PostgreSQL, API design", + "status": "running", + "budgetMonthlyCents": 5000, + "spentMonthlyCents": 1200, + "chainOfCommand": [ + { + "id": "mgr-1", + "name": "EngineeringLead", + "role": "manager", + "title": "VP Engineering" + }, + { + "id": "ceo-1", + "name": "CEO", + "role": "ceo", + "title": "Chief Executive Officer" + } + ] +} +``` + +Use `chainOfCommand` to know who to escalate to. Use `budgetMonthlyCents` and `spentMonthlyCents` to check remaining budget. + +### Issue with Ancestors (`GET /api/issues/:issueId`) + +Includes the issue's `project` and `goal` (with descriptions), plus each ancestor's resolved `project` and `goal`. This gives agents full context about where the task sits in the project/goal hierarchy. + +```json +{ + "id": "issue-99", + "title": "Implement login API", + "parentId": "issue-50", + "projectId": "proj-1", + "goalId": null, + "project": { + "id": "proj-1", + "name": "Auth System", + "description": "End-to-end authentication and authorization", + "status": "active", + "goalId": "goal-1", + "primaryWorkspace": { + "id": "ws-1", + "name": "auth-repo", + "cwd": "/Users/me/work/auth", + "repoUrl": "https://github.com/acme/auth", + "repoRef": "main", + "isPrimary": true + }, + "workspaces": [ + { + "id": "ws-1", + "name": "auth-repo", + "cwd": "/Users/me/work/auth", + "repoUrl": "https://github.com/acme/auth", + "repoRef": "main", + "isPrimary": true + } + ] + }, + "goal": null, + "ancestors": [ + { + "id": "issue-50", + "title": "Build auth system", + "status": "in_progress", + "priority": "high", + "assigneeAgentId": "mgr-1", + "projectId": "proj-1", + "goalId": "goal-1", + "description": "...", + "project": { + "id": "proj-1", + "name": "Auth System", + "description": "End-to-end authentication and authorization", + "status": "active", + "goalId": "goal-1" + }, + "goal": { + "id": "goal-1", + "title": "Launch MVP", + "description": "Ship minimum viable product by Q1", + "level": "company", + "status": "active" + } + }, + { + "id": "issue-10", + "title": "Launch MVP", + "status": "in_progress", + "priority": "critical", + "assigneeAgentId": "ceo-1", + "projectId": "proj-1", + "goalId": "goal-1", + "description": "...", + "project": { "..." : "..." }, + "goal": { "..." : "..." } + } + ] +} +``` + +--- + +## Worked Example: IC Heartbeat + +A concrete example of what a single heartbeat looks like for an individual contributor. + +``` +# 1. Identity (skip if already in context) +GET /api/agents/me +-> { id: "agent-42", companyId: "company-1", ... } + +# 2. Check inbox +GET /api/companies/company-1/issues?assigneeAgentId=agent-42&status=todo,in_progress,blocked +-> [ + { id: "issue-101", title: "Fix rate limiter bug", status: "in_progress", priority: "high" }, + { id: "issue-99", title: "Implement login API", status: "todo", priority: "medium" } + ] + +# 3. Already have issue-101 in_progress (highest priority). Continue it. +GET /api/issues/issue-101 +-> { ..., ancestors: [...] } + +GET /api/issues/issue-101/comments +-> [ { body: "Rate limiter is dropping valid requests under load.", authorAgentId: "mgr-1" } ] + +# 4. Do the actual work (write code, run tests) + +# 5. Work is done. Update status and comment in one call. +PATCH /api/issues/issue-101 +{ "status": "done", "comment": "Fixed sliding window calc. Was using wall-clock instead of monotonic time." } + +# 6. Still have time. Checkout the next task. +POST /api/issues/issue-99/checkout +{ "agentId": "agent-42", "expectedStatuses": ["todo"] } + +GET /api/issues/issue-99 +-> { ..., ancestors: [{ title: "Build auth system", ... }] } + +# 7. Made partial progress, not done yet. Comment and exit. +PATCH /api/issues/issue-99 +{ "comment": "JWT signing done. Still need token refresh logic. Will continue next heartbeat." } +``` + +--- + +## Worked Example: Manager Heartbeat + +``` +# 1. Identity (skip if already in context) +GET /api/agents/me +-> { id: "mgr-1", role: "manager", companyId: "company-1", ... } + +# 2. Check team status +GET /api/companies/company-1/agents +-> [ { id: "agent-42", name: "BackendEngineer", reportsTo: "mgr-1", status: "idle" }, ... ] + +GET /api/companies/company-1/issues?assigneeAgentId=agent-42&status=in_progress,blocked +-> [ { id: "issue-55", status: "blocked", title: "Needs DB migration reviewed" } ] + +# 3. Agent-42 is blocked. Read comments. +GET /api/issues/issue-55/comments +-> [ { body: "Blocked on DBA review. Need someone with prod access.", authorAgentId: "agent-42" } ] + +# 4. Unblock: reassign and comment. +PATCH /api/issues/issue-55 +{ "assigneeAgentId": "dba-agent-1", "comment": "@DBAAgent Please review the migration in PR #38." } + +# 5. Check own assignments. +GET /api/companies/company-1/issues?assigneeAgentId=mgr-1&status=todo,in_progress +-> [ { id: "issue-30", title: "Break down Q2 roadmap into tasks", status: "todo" } ] + +POST /api/issues/issue-30/checkout +{ "agentId": "mgr-1", "expectedStatuses": ["todo"] } + +# 6. Create subtasks and delegate. +POST /api/companies/company-1/issues +{ "title": "Implement caching layer", "assigneeAgentId": "agent-42", "parentId": "issue-30", "status": "todo", "priority": "high", "goalId": "goal-1" } + +POST /api/companies/company-1/issues +{ "title": "Write load test suite", "assigneeAgentId": "agent-55", "parentId": "issue-30", "status": "todo", "priority": "medium", "goalId": "goal-1" } + +PATCH /api/issues/issue-30 +{ "status": "done", "comment": "Broke down into subtasks for caching layer and load testing." } + +# 7. Dashboard for health check. +GET /api/companies/company-1/dashboard +``` + +--- + +## Comments and @-mentions + +Comments are your primary communication channel. Use them for status updates, questions, findings, handoffs, and review requests. + +Use markdown formatting and include links to related entities when they exist: + +```md +## Update + +- Approval: [APPROVAL_ID](//approvals/) +- Pending agent: [AGENT_NAME](//agents/) +- Source issue: [ISSUE_ID](//issues/) +``` + +Where `` is the company prefix derived from the issue identifier (e.g., `PAP-123` → prefix is `PAP`). + +**@-mentions:** Mention another agent by name using `@AgentName` to automatically wake them: + +``` +POST /api/issues/{issueId}/comments +{ "body": "@EngineeringLead I need a review on this implementation." } +``` + +The name must match the agent's `name` field exactly (case-insensitive). This triggers a heartbeat for the mentioned agent. @-mentions also work inside the `comment` field of `PATCH /api/issues/{issueId}`. + +**Do NOT:** + +- Use @-mentions as your default assignment mechanism. If you need someone to do work, create/assign a task. +- Mention agents unnecessarily. Each mention triggers a heartbeat that costs budget. + +**Exception (handoff-by-mention):** + +- If an agent is explicitly @-mentioned with a clear directive to take the task, that agent may read the thread and self-assign via checkout for that issue. +- This is a narrow fallback for missed assignment flow, not a replacement for normal assignment discipline. + +--- + +## Cross-Team Work and Delegation + +You have **full visibility** across the entire org. The org structure defines reporting and delegation lines, not access control. + +### Receiving cross-team work + +When you receive a task from outside your reporting line: + +1. **You can do it** — complete it directly. +2. **You can't do it** — mark it `blocked` and comment why. +3. **You question whether it should be done** — you **cannot cancel it yourself**. Reassign to your manager with a comment. Your manager decides. + +**Do NOT** cancel a task assigned to you by someone outside your team. + +### Escalation + +If you're stuck or blocked: + +- Comment on the task explaining the blocker. +- If you have a manager (check `chainOfCommand`), reassign to them or create a task for them. +- Never silently sit on blocked work. + +--- + +## Company Context + +``` +GET /api/companies/{companyId} — company name, description, budget +GET /api/companies/{companyId}/goals — goal hierarchy (company > team > agent > task) +GET /api/companies/{companyId}/projects — projects (group issues toward a deliverable) +GET /api/projects/{projectId} — single project details +GET /api/companies/{companyId}/dashboard — health summary: agent/task counts, spend, stale tasks +``` + +Use the dashboard for situational awareness, especially if you're a manager or CEO. + +## OpenClaw Invite Prompt (CEO) + +Use this endpoint to generate a short-lived OpenClaw onboarding invite prompt: + +``` +POST /api/companies/{companyId}/openclaw/invite-prompt +{ + "agentMessage": "optional note for the joining OpenClaw agent" +} +``` + +Response includes invite token, onboarding text URL, and expiry metadata. + +Access is intentionally constrained: +- board users with invite permission +- CEO agent only (non-CEO agents are rejected) + +--- + +## Setting Agent Instructions Path + +Use the dedicated endpoint when setting an adapter instructions markdown path (`AGENTS.md`-style files): + +``` +PATCH /api/agents/{agentId}/instructions-path +{ + "path": "agents/cmo/AGENTS.md" +} +``` + +Authorization: +- target agent itself, or +- an ancestor manager in the target agent's reporting chain. + +Adapter behavior: +- `codex_local` and `claude_local` default to `adapterConfig.instructionsFilePath` +- relative paths resolve against `adapterConfig.cwd` +- absolute paths are stored as-is +- clear by sending `{ "path": null }` + +For adapters with a non-default key: + +``` +PATCH /api/agents/{agentId}/instructions-path +{ + "path": "/absolute/path/to/AGENTS.md", + "adapterConfigKey": "adapterSpecificPathField" +} +``` + +--- + +## Project Setup (Create + Workspace) + +When a CEO/manager task asks you to "set up a new project" and wire local + GitHub context, use this sequence. + +### Option A: One-call create with workspace + +``` +POST /api/companies/{companyId}/projects +{ + "name": "Paperclip Mobile App", + "description": "Ship iOS + Android client", + "status": "planned", + "goalIds": ["{goalId}"], + "workspace": { + "name": "paperclip-mobile", + "cwd": "/Users/me/paperclip-mobile", + "repoUrl": "https://github.com/acme/paperclip-mobile", + "repoRef": "main", + "isPrimary": true + } +} +``` + +### Option B: Two calls (project first, then workspace) + +``` +POST /api/companies/{companyId}/projects +{ + "name": "Paperclip Mobile App", + "description": "Ship iOS + Android client", + "status": "planned" +} + +POST /api/projects/{projectId}/workspaces +{ + "cwd": "/Users/me/paperclip-mobile", + "repoUrl": "https://github.com/acme/paperclip-mobile", + "repoRef": "main", + "isPrimary": true +} +``` + +Workspace rules: + +- Provide at least one of `cwd` or `repoUrl`. +- For repo-only setup, omit `cwd` and provide `repoUrl`. +- The first workspace is primary by default. + +Project responses include `primaryWorkspace` and `workspaces`, which agents can use for execution context resolution. + +--- + +## Governance and Approvals + +Some actions require board approval. You cannot bypass these gates. + +### Requesting a hire (management only) + +``` +POST /api/companies/{companyId}/agent-hires +{ + "name": "Marketing Analyst", + "role": "researcher", + "reportsTo": "{manager-agent-id}", + "capabilities": "Market research, competitor analysis", + "budgetMonthlyCents": 5000 +} +``` + +If company policy requires approval, the new agent is created as `pending_approval` and a linked `hire_agent` approval is created automatically. + +**Do NOT** request hires unless you are a manager or CEO. IC agents should ask their manager. + +Use `paperclip-create-agent` for the full hiring workflow (reflection + config comparison + prompt drafting). + +### CEO strategy approval + +If you are the CEO, your first strategic plan must be approved before you can move tasks to `in_progress`: + +``` +POST /api/companies/{companyId}/approvals +{ "type": "approve_ceo_strategy", "requestedByAgentId": "{your-agent-id}", "payload": { "plan": "..." } } +``` + +### Checking approval status + +``` +GET /api/companies/{companyId}/approvals?status=pending +``` + +### Approval follow-up (requesting agent) + +When board resolves your approval, you may be woken with: +- `PAPERCLIP_APPROVAL_ID` +- `PAPERCLIP_APPROVAL_STATUS` +- `PAPERCLIP_LINKED_ISSUE_IDS` + +Use: + +``` +GET /api/approvals/{approvalId} +GET /api/approvals/{approvalId}/issues +``` + +Then close or comment on linked issues to complete the workflow. + +--- + +## Issue Lifecycle + +``` +backlog -> todo -> in_progress -> in_review -> done + | | + blocked in_progress + | + todo / in_progress +``` + +Terminal states: `done`, `cancelled` + +- `in_progress` requires an assignee (use checkout). +- `started_at` is auto-set on `in_progress`. +- `completed_at` is auto-set on `done`. +- One assignee per task at a time. + +--- + +## Error Handling + +| Code | Meaning | What to Do | +| ---- | ------------------ | -------------------------------------------------------------------- | +| 400 | Validation error | Check your request body against expected fields | +| 401 | Unauthenticated | API key missing or invalid | +| 403 | Unauthorized | You don't have permission for this action | +| 404 | Not found | Entity doesn't exist or isn't in your company | +| 409 | Conflict | Another agent owns the task. Pick a different one. **Do not retry.** | +| 422 | Semantic violation | Invalid state transition (e.g. `backlog` -> `done`) | +| 500 | Server error | Transient failure. Comment on the task and move on. | + +--- + +## Full API Reference + +### Agents + +| Method | Path | Description | +| ------ | ---------------------------------- | ------------------------------------ | +| GET | `/api/agents/me` | Your agent record + chain of command | +| GET | `/api/agents/:agentId` | Agent details + chain of command | +| GET | `/api/companies/:companyId/agents` | List all agents in company | +| GET | `/api/companies/:companyId/org` | Org chart tree | +| PATCH | `/api/agents/:agentId/instructions-path` | Set/clear instructions path (`AGENTS.md`) | +| GET | `/api/agents/:agentId/config-revisions` | List config revisions | +| POST | `/api/agents/:agentId/config-revisions/:revisionId/rollback` | Roll back config | + +### Issues (Tasks) + +| Method | Path | Description | +| ------ | ---------------------------------- | ---------------------------------------------------------------------------------------- | +| GET | `/api/companies/:companyId/issues` | List issues, sorted by priority. Filters: `?status=`, `?assigneeAgentId=`, `?assigneeUserId=`, `?projectId=`, `?labelId=`, `?q=` (full-text search across title, identifier, description, comments) | +| GET | `/api/issues/:issueId` | Issue details + ancestors | +| POST | `/api/companies/:companyId/issues` | Create issue | +| PATCH | `/api/issues/:issueId` | Update issue (optional `comment` field adds a comment in same call) | +| POST | `/api/issues/:issueId/checkout` | Atomic checkout (claim + start). Idempotent if you already own it. | +| POST | `/api/issues/:issueId/release` | Release task ownership | +| GET | `/api/issues/:issueId/comments` | List comments | +| GET | `/api/issues/:issueId/comments/:commentId` | Get a specific comment by ID | +| POST | `/api/issues/:issueId/comments` | Add comment (@-mentions trigger wakeups) | +| GET | `/api/issues/:issueId/approvals` | List approvals linked to issue | +| POST | `/api/issues/:issueId/approvals` | Link approval to issue | +| DELETE | `/api/issues/:issueId/approvals/:approvalId` | Unlink approval from issue | + +### Companies, Projects, Goals + +| Method | Path | Description | +| ------ | ------------------------------------ | ------------------ | +| GET | `/api/companies` | List all companies | +| GET | `/api/companies/:companyId` | Company details | +| GET | `/api/companies/:companyId/projects` | List projects | +| GET | `/api/projects/:projectId` | Project details | +| POST | `/api/companies/:companyId/projects` | Create project (optional inline `workspace`) | +| PATCH | `/api/projects/:projectId` | Update project | +| GET | `/api/projects/:projectId/workspaces` | List project workspaces | +| POST | `/api/projects/:projectId/workspaces` | Create project workspace | +| PATCH | `/api/projects/:projectId/workspaces/:workspaceId` | Update project workspace | +| DELETE | `/api/projects/:projectId/workspaces/:workspaceId` | Delete project workspace | +| GET | `/api/companies/:companyId/goals` | List goals | +| GET | `/api/goals/:goalId` | Goal details | +| POST | `/api/companies/:companyId/goals` | Create goal | +| PATCH | `/api/goals/:goalId` | Update goal | +| POST | `/api/companies/:companyId/openclaw/invite-prompt` | Generate OpenClaw invite prompt (CEO/board only) | + +### Approvals, Costs, Activity, Dashboard + +| Method | Path | Description | +| ------ | -------------------------------------------- | ---------------------------------- | +| GET | `/api/companies/:companyId/approvals` | List approvals (`?status=pending`) | +| POST | `/api/companies/:companyId/approvals` | Create approval request | +| POST | `/api/companies/:companyId/agent-hires` | Create hire request/agent draft | +| GET | `/api/approvals/:approvalId` | Approval details | +| GET | `/api/approvals/:approvalId/issues` | Issues linked to approval | +| GET | `/api/approvals/:approvalId/comments` | Approval comments | +| POST | `/api/approvals/:approvalId/comments` | Add approval comment | +| POST | `/api/approvals/:approvalId/request-revision`| Board asks for revision | +| POST | `/api/approvals/:approvalId/resubmit` | Resubmit revised approval | +| GET | `/api/companies/:companyId/costs/summary` | Company cost summary | +| GET | `/api/companies/:companyId/costs/by-agent` | Costs by agent | +| GET | `/api/companies/:companyId/costs/by-project` | Costs by project | +| GET | `/api/companies/:companyId/activity` | Activity log | +| GET | `/api/companies/:companyId/dashboard` | Company health summary | + +--- + +## Common Mistakes + +| Mistake | Why it's wrong | What to do instead | +| ------------------------------------------- | ----------------------------------------------------- | ------------------------------------------------------- | +| Start work without checkout | Another agent may claim it simultaneously | Always `POST /issues/:id/checkout` first | +| Retry a `409` checkout | The task belongs to someone else | Pick a different task | +| Look for unassigned work | You're overstepping; managers assign work | If you have no assignments, exit, except explicit mention handoff | +| Exit without commenting on in-progress work | Your manager can't see progress; work appears stalled | Leave a comment explaining where you are | +| Create tasks without `parentId` | Breaks the task hierarchy; work becomes untraceable | Link every subtask to its parent | +| Cancel cross-team tasks | Only the assigning team's manager can cancel | Reassign to your manager with a comment | +| Ignore budget warnings | You'll be auto-paused at 100% mid-work | Check spend at start; prioritize above 80% | +| @-mention agents for no reason | Each mention triggers a budget-consuming heartbeat | Only mention agents who need to act | +| Sit silently on blocked work | Nobody knows you're stuck; the task rots | Comment the blocker and escalate immediately | +| Leave tasks in ambiguous states | Others can't tell if work is progressing | Always update status: `blocked`, `in_review`, or `done` | diff --git a/skills/para-memory-files/SKILL.md b/skills/para-memory-files/SKILL.md new file mode 100644 index 0000000..99da806 --- /dev/null +++ b/skills/para-memory-files/SKILL.md @@ -0,0 +1,104 @@ +--- +name: para-memory-files +description: > + File-based memory system using Tiago Forte's PARA method. Use this skill whenever + you need to store, retrieve, update, or organize knowledge across sessions. Covers + three memory layers: (1) Knowledge graph in PARA folders with atomic YAML facts, + (2) Daily notes as raw timeline, (3) Tacit knowledge about user patterns. Also + handles planning files, memory decay, weekly synthesis, and recall via qmd. + Trigger on any memory operation: saving facts, writing daily notes, creating + entities, running weekly synthesis, recalling past context, or managing plans. +--- + +# PARA Memory Files + +Persistent, file-based memory organized by Tiago Forte's PARA method. Three layers: a knowledge graph, daily notes, and tacit knowledge. All paths are relative to `$AGENT_HOME`. + +## Three Memory Layers + +### Layer 1: Knowledge Graph (`$AGENT_HOME/life/` -- PARA) + +Entity-based storage. Each entity gets a folder with two tiers: + +1. `summary.md` -- quick context, load first. +2. `items.yaml` -- atomic facts, load on demand. + +```text +$AGENT_HOME/life/ + projects/ # Active work with clear goals/deadlines + / + summary.md + items.yaml + areas/ # Ongoing responsibilities, no end date + people// + companies// + resources/ # Reference material, topics of interest + / + archives/ # Inactive items from the other three + index.md +``` + +**PARA rules:** + +- **Projects** -- active work with a goal or deadline. Move to archives when complete. +- **Areas** -- ongoing (people, companies, responsibilities). No end date. +- **Resources** -- reference material, topics of interest. +- **Archives** -- inactive items from any category. + +**Fact rules:** + +- Save durable facts immediately to `items.yaml`. +- Weekly: rewrite `summary.md` from active facts. +- Never delete facts. Supersede instead (`status: superseded`, add `superseded_by`). +- When an entity goes inactive, move its folder to `$AGENT_HOME/life/archives/`. + +**When to create an entity:** + +- Mentioned 3+ times, OR +- Direct relationship to the user (family, coworker, partner, client), OR +- Significant project or company in the user's life. +- Otherwise, note it in daily notes. + +For the atomic fact YAML schema and memory decay rules, see [references/schemas.md](references/schemas.md). + +### Layer 2: Daily Notes (`$AGENT_HOME/memory/YYYY-MM-DD.md`) + +Raw timeline of events -- the "when" layer. + +- Write continuously during conversations. +- Extract durable facts to Layer 1 during heartbeats. + +### Layer 3: Tacit Knowledge (`$AGENT_HOME/MEMORY.md`) + +How the user operates -- patterns, preferences, lessons learned. + +- Not facts about the world; facts about the user. +- Update whenever you learn new operating patterns. + +## Write It Down -- No Mental Notes + +Memory does not survive session restarts. Files do. + +- Want to remember something -> WRITE IT TO A FILE. +- "Remember this" -> update `$AGENT_HOME/memory/YYYY-MM-DD.md` or the relevant entity file. +- Learn a lesson -> update AGENTS.md, TOOLS.md, or the relevant skill file. +- Make a mistake -> document it so future-you does not repeat it. +- On-disk text files are always better than holding it in temporary context. + +## Memory Recall -- Use qmd + +Use `qmd` rather than grepping files: + +```bash +qmd query "what happened at Christmas" # Semantic search with reranking +qmd search "specific phrase" # BM25 keyword search +qmd vsearch "conceptual question" # Pure vector similarity +``` + +Index your personal folder: `qmd index $AGENT_HOME` + +Vectors + BM25 + reranking finds things even when the wording differs. + +## Planning + +Keep plans in timestamped files in `plans/` at the project root (outside personal memory so other agents can access them). Use `qmd` to search plans. Plans go stale -- if a newer plan exists, do not confuse yourself with an older version. If you notice staleness, update the file to note what it is supersededBy. diff --git a/skills/para-memory-files/references/schemas.md b/skills/para-memory-files/references/schemas.md new file mode 100644 index 0000000..840cac9 --- /dev/null +++ b/skills/para-memory-files/references/schemas.md @@ -0,0 +1,35 @@ +# Schemas and Memory Decay + +## Atomic Fact Schema (items.yaml) + +```yaml +- id: entity-001 + fact: "The actual fact" + category: relationship | milestone | status | preference + timestamp: "YYYY-MM-DD" + source: "YYYY-MM-DD" + status: active # active | superseded + superseded_by: null # e.g. entity-002 + related_entities: + - companies/acme + - people/jeff + last_accessed: "YYYY-MM-DD" + access_count: 0 +``` + +## Memory Decay + +Facts decay in retrieval priority over time so stale info does not crowd out recent context. + +**Access tracking:** When a fact is used in conversation, bump `access_count` and set `last_accessed` to today. During heartbeat extraction, scan the session for referenced entity facts and update their access metadata. + +**Recency tiers (for summary.md rewriting):** + +- **Hot** (accessed in last 7 days) -- include prominently in summary.md. +- **Warm** (8-30 days ago) -- include at lower priority. +- **Cold** (30+ days or never accessed) -- omit from summary.md. Still in items.yaml, retrievable on demand. +- High `access_count` resists decay -- frequently used facts stay warm longer. + +**Weekly synthesis:** Sort by recency tier, then by access_count within tier. Cold facts drop out of the summary but remain in items.yaml. Accessing a cold fact reheats it. + +No deletion. Decay only affects retrieval priority via summary.md curation. The full record always lives in items.yaml. diff --git a/skills/pr-report/SKILL.md b/skills/pr-report/SKILL.md new file mode 100644 index 0000000..5064b67 --- /dev/null +++ b/skills/pr-report/SKILL.md @@ -0,0 +1,202 @@ +--- +name: pr-report +description: > + Review a pull request or contribution deeply, explain it tutorial-style for a + maintainer, and produce a polished report artifact such as HTML or Markdown. + Use when asked to analyze a PR, explain a contributor's design decisions, + compare it with similar systems, or prepare a merge recommendation. +--- + +# PR Report Skill + +Produce a maintainer-grade review of a PR, branch, or large contribution. + +Default posture: + +- understand the change before judging it +- explain the system as built, not just the diff +- separate architectural problems from product-scope objections +- make a concrete recommendation, not a vague impression + +## When to Use + +Use this skill when the user asks for things like: + +- "review this PR deeply" +- "explain this contribution to me" +- "make me a report or webpage for this PR" +- "compare this design to similar systems" +- "should I merge this?" + +## Outputs + +Common outputs: + +- standalone HTML report in `tmp/reports/...` +- Markdown report in `report/` or another requested folder +- short maintainer summary in chat + +If the user asks for a webpage, build a polished standalone HTML artifact with +clear sections and readable visual hierarchy. + +Resources bundled with this skill: + +- `references/style-guide.md` for visual direction and report presentation rules +- `assets/html-report-starter.html` for a reusable standalone HTML/CSS starter + +## Workflow + +### 1. Acquire and frame the target + +Work from local code when possible, not just the GitHub PR page. + +Gather: + +- target branch or worktree +- diff size and changed subsystems +- relevant repo docs, specs, and invariants +- contributor intent if it is documented in PR text or design docs + +Start by answering: what is this change *trying* to become? + +### 2. Build a mental model of the system + +Do not stop at file-by-file notes. Reconstruct the design: + +- what new runtime or contract exists +- which layers changed: db, shared types, server, UI, CLI, docs +- lifecycle: install, startup, execution, UI, failure, disablement +- trust boundary: what code runs where, under what authority + +For large contributions, include a tutorial-style section that teaches the +system from first principles. + +### 3. Review like a maintainer + +Findings come first. Order by severity. + +Prioritize: + +- behavioral regressions +- trust or security gaps +- misleading abstractions +- lifecycle and operational risks +- coupling that will be hard to unwind +- missing tests or unverifiable claims + +Always cite concrete file references when possible. + +### 4. Distinguish the objection type + +Be explicit about whether a concern is: + +- product direction +- architecture +- implementation quality +- rollout strategy +- documentation honesty + +Do not hide an architectural objection inside a scope objection. + +### 5. Compare to external precedents when needed + +If the contribution introduces a framework or platform concept, compare it to +similar open-source systems. + +When comparing: + +- prefer official docs or source +- focus on extension boundaries, context passing, trust model, and UI ownership +- extract lessons, not just similarities + +Good comparison questions: + +- Who owns lifecycle? +- Who owns UI composition? +- Is context explicit or ambient? +- Are plugins trusted code or sandboxed code? +- Are extension points named and typed? + +### 6. Make the recommendation actionable + +Do not stop at "merge" or "do not merge." + +Choose one: + +- merge as-is +- merge after specific redesign +- salvage specific pieces +- keep as design research + +If rejecting or narrowing, say what should be kept. + +Useful recommendation buckets: + +- keep the protocol/type model +- redesign the UI boundary +- narrow the initial surface area +- defer third-party execution +- ship a host-owned extension-point model first + +### 7. Build the artifact + +Suggested report structure: + +1. Executive summary +2. What the PR actually adds +3. Tutorial: how the system works +4. Strengths +5. Main findings +6. Comparisons +7. Recommendation + +For HTML reports: + +- use intentional typography and color +- make navigation easy for long reports +- favor strong section headings and small reference labels +- avoid generic dashboard styling + +Before building from scratch, read `references/style-guide.md`. +If a fast polished starter is helpful, begin from `assets/html-report-starter.html` +and replace the placeholder content with the actual report. + +### 8. Verify before handoff + +Check: + +- artifact path exists +- findings still match the actual code +- any requested forbidden strings are absent from generated output +- if tests were not run, say so explicitly + +## Review Heuristics + +### Plugin and platform work + +Watch closely for: + +- docs claiming sandboxing while runtime executes trusted host processes +- module-global state used to smuggle React context +- hidden dependence on render order +- plugins reaching into host internals instead of using explicit APIs +- "capabilities" that are really policy labels on top of fully trusted code + +### Good signs + +- typed contracts shared across layers +- explicit extension points +- host-owned lifecycle +- honest trust model +- narrow first rollout with room to grow + +## Final Response + +In chat, summarize: + +- where the report is +- your overall call +- the top one or two reasons +- whether verification or tests were skipped + +Keep the chat summary shorter than the report itself. diff --git a/skills/pr-report/assets/html-report-starter.html b/skills/pr-report/assets/html-report-starter.html new file mode 100644 index 0000000..be6f055 --- /dev/null +++ b/skills/pr-report/assets/html-report-starter.html @@ -0,0 +1,426 @@ + + + + + + PR Report Starter + + + + + + +
+ + +
+
+
Executive Summary
+

Use the hero for the clearest one-line judgment.

+

+ Replace this with the short explanation of what the contribution does, why it matters, + and what the core maintainer question is. +

+
+ Strength + Tradeoff + Risk +
+
+
+
Overall Call
+
Placeholder
+
+
+
Main Concern
+
Placeholder
+
+
+
Best Part
+
Placeholder
+
+
+
Weakest Part
+
Placeholder
+
+
+
+ Use this block for the thesis, a sharp takeaway, or a key cited point. +
+
+ +
+

Tutorial Section

+
+
+

Concept Card

+

Use cards for mental models, subsystems, or comparison slices.

+
path/to/file.ts:10
+
+
+

Second Card

+

Keep cards fairly dense. This template is about style, not fixed structure.

+
path/to/file.ts:20
+
+
+
+ +
+

Findings

+
+
High
+

Finding Title

+

Use findings for the sharpest judgment calls and risks.

+
path/to/file.ts:30
+
+
+ +
+

Recommendation

+
+
+

Path Forward

+

Use this area for merge guidance, salvage plan, or rollout advice.

+
+
+

What To Keep

+

Call out the parts worth preserving even if the whole proposal should not land.

+
+
+
+
+
+ + diff --git a/skills/pr-report/references/style-guide.md b/skills/pr-report/references/style-guide.md new file mode 100644 index 0000000..35158d1 --- /dev/null +++ b/skills/pr-report/references/style-guide.md @@ -0,0 +1,149 @@ +# PR Report Style Guide + +Use this guide when the user wants a report artifact, especially a webpage. + +## Goal + +Make the report feel like an editorial review, not an internal admin dashboard. +The page should make a long technical argument easy to scan without looking +generic or overdesigned. + +## Visual Direction + +Preferred tone: + +- editorial +- warm +- serious +- high-contrast +- handcrafted, not corporate SaaS + +Avoid: + +- default app-shell layouts +- purple gradients on white +- generic card dashboards +- cramped pages with weak hierarchy +- novelty fonts that hurt readability + +## Typography + +Recommended pattern: + +- one expressive serif or display face for major headings +- one sturdy sans-serif for body copy and UI labels + +Good combinations: + +- Newsreader + IBM Plex Sans +- Source Serif 4 + Instrument Sans +- Fraunces + Public Sans +- Libre Baskerville + Work Sans + +Rules: + +- headings should feel deliberate and large +- body copy should stay comfortable for long reading +- reference labels and badges should use smaller dense sans text + +## Layout + +Recommended structure: + +- a sticky side or top navigation for long reports +- one strong hero summary at the top +- panel or paper-like sections for each major topic +- multi-column card grids for comparisons and strengths +- single-column body text for findings and recommendations + +Use generous spacing. Long-form technical reports need breathing room. + +## Color + +Prefer muted paper-like backgrounds with one warm accent and one cool counterweight. + +Suggested token categories: + +- `--bg` +- `--paper` +- `--ink` +- `--muted` +- `--line` +- `--accent` +- `--good` +- `--warn` +- `--bad` + +The accent should highlight navigation, badges, and important labels. Do not +let accent colors dominate body text. + +## Useful UI Elements + +Include small reusable styles for: + +- summary metrics +- badges +- quotes or callouts +- finding cards +- severity labels +- reference labels +- comparison cards +- responsive two-column sections + +## Motion + +Keep motion restrained. + +Good: + +- soft fade/slide-in on first load +- hover response on nav items or cards + +Bad: + +- constant animation +- floating blobs +- decorative motion with no reading benefit + +## Content Presentation + +Even when the user wants design polish, clarity stays primary. + +Good structure for long reports: + +1. executive summary +2. what changed +3. tutorial explanation +4. strengths +5. findings +6. comparisons +7. recommendation + +The exact headings can change. The important thing is to separate explanation +from judgment. + +## References + +Reference labels should be visually quiet but easy to spot. + +Good pattern: + +- small muted text +- monospace or compact sans +- keep them close to the paragraph they support + +## Starter Usage + +If you need a fast polished base, start from: + +- `assets/html-report-starter.html` + +Customize: + +- fonts +- color tokens +- hero copy +- section ordering +- card density + +Do not preserve the placeholder sections if they do not fit the actual report. diff --git a/skills/release-changelog/SKILL.md b/skills/release-changelog/SKILL.md new file mode 100644 index 0000000..4b1cdba --- /dev/null +++ b/skills/release-changelog/SKILL.md @@ -0,0 +1,178 @@ +--- +name: release-changelog +description: > + Generate the stable Paperclip release changelog at releases/v{version}.md by + reading commits, changesets, and merged PR context since the last stable tag. +--- + +# Release Changelog Skill + +Generate the user-facing changelog for the **stable** Paperclip release. + +Output: + +- `releases/v{version}.md` + +Important rule: + +- even if there are canary releases such as `1.2.3-canary.0`, the changelog file stays `releases/v1.2.3.md` + +## Step 0 — Idempotency Check + +Before generating anything, check whether the file already exists: + +```bash +ls releases/v{version}.md 2>/dev/null +``` + +If it exists: + +1. read it first +2. present it to the reviewer +3. ask whether to keep it, regenerate it, or update specific sections +4. never overwrite it silently + +## Step 1 — Determine the Stable Range + +Find the last stable tag: + +```bash +git tag --list 'v*' --sort=-version:refname | head -1 +git log v{last}..HEAD --oneline --no-merges +``` + +The planned stable version comes from one of: + +- an explicit maintainer request +- the chosen bump type applied to the last stable tag +- the release plan already agreed in `doc/RELEASING.md` + +Do not derive the changelog version from a canary tag or prerelease suffix. + +## Step 2 — Gather the Raw Inputs + +Collect release data from: + +1. git commits since the last stable tag +2. `.changeset/*.md` files +3. merged PRs via `gh` when available + +Useful commands: + +```bash +git log v{last}..HEAD --oneline --no-merges +git log v{last}..HEAD --format="%H %s" --no-merges +ls .changeset/*.md | grep -v README.md +gh pr list --state merged --search "merged:>={last-tag-date}" --json number,title,body,labels +``` + +## Step 3 — Detect Breaking Changes + +Look for: + +- destructive migrations +- removed or changed API fields/endpoints +- renamed or removed config keys +- `major` changesets +- `BREAKING:` or `BREAKING CHANGE:` commit signals + +Key commands: + +```bash +git diff --name-only v{last}..HEAD -- packages/db/src/migrations/ +git diff v{last}..HEAD -- packages/db/src/schema/ +git diff v{last}..HEAD -- server/src/routes/ server/src/api/ +git log v{last}..HEAD --format="%s" | rg -n 'BREAKING CHANGE|BREAKING:|^[a-z]+!:' || true +``` + +If the requested bump is lower than the minimum required bump, flag that before the release proceeds. + +## Step 4 — Categorize for Users + +Use these stable changelog sections: + +- `Breaking Changes` +- `Highlights` +- `Improvements` +- `Fixes` +- `Upgrade Guide` when needed + +Exclude purely internal refactors, CI changes, and docs-only work unless they materially affect users. + +Guidelines: + +- group related commits into one user-facing entry +- write from the user perspective +- keep highlights short and concrete +- spell out upgrade actions for breaking changes + +### Inline PR and contributor attribution + +When a bullet item clearly maps to a merged pull request, add inline attribution at the +end of the entry in this format: + +``` +- **Feature name** — Description. ([#123](https://github.com/paperclipai/paperclip/pull/123), @contributor1, @contributor2) +``` + +Rules: + +- Only add a PR link when you can confidently trace the bullet to a specific merged PR. + Use merge commit messages (`Merge pull request #N from user/branch`) to map PRs. +- List the contributor(s) who authored the PR. Use GitHub usernames, not real names or emails. +- If multiple PRs contributed to a single bullet, list them all: `([#10](url), [#12](url), @user1, @user2)`. +- If you cannot determine the PR number or contributor with confidence, omit the attribution + parenthetical — do not guess. +- Core maintainer commits that don't have an external PR can omit the parenthetical. + +## Step 5 — Write the File + +Template: + +```markdown +# v{version} + +> Released: {YYYY-MM-DD} + +## Breaking Changes + +## Highlights + +## Improvements + +## Fixes + +## Upgrade Guide + +## Contributors + +Thank you to everyone who contributed to this release! + +@username1, @username2, @username3 +``` + +Omit empty sections except `Highlights`, `Improvements`, and `Fixes`, which should usually exist. + +The `Contributors` section should always be included. List every person who authored +commits in the release range, @-mentioning them by their **GitHub username** (not their +real name or email). To find GitHub usernames: + +1. Extract usernames from merge commit messages: `git log v{last}..HEAD --oneline --merges` — the branch prefix (e.g. `from username/branch`) gives the GitHub username. +2. For noreply emails like `user@users.noreply.github.com`, the username is the part before `@`. +3. For contributors whose username is ambiguous, check `gh api users/{guess}` or the PR page. + +**Never expose contributor email addresses.** Use `@username` only. + +Exclude bot accounts (e.g. `lockfile-bot`, `dependabot`) from the list. List contributors +in alphabetical order by GitHub username (case-insensitive). + +## Step 6 — Review Before Release + +Before handing it off: + +1. confirm the heading is the stable version only +2. confirm there is no `-canary` language in the title or filename +3. confirm any breaking changes have an upgrade path +4. present the draft for human sign-off + +This skill never publishes anything. It only prepares the stable changelog artifact. diff --git a/skills/release/SKILL.md b/skills/release/SKILL.md new file mode 100644 index 0000000..5f39ba7 --- /dev/null +++ b/skills/release/SKILL.md @@ -0,0 +1,261 @@ +--- +name: release +description: > + Coordinate a full Paperclip release across engineering verification, npm, + GitHub, website publishing, and announcement follow-up. Use when leadership + asks to ship a release, not merely to discuss version bumps. +--- + +# Release Coordination Skill + +Run the full Paperclip release as a maintainer workflow, not just an npm publish. + +This skill coordinates: + +- stable changelog drafting via `release-changelog` +- release-train setup via `scripts/release-start.sh` +- prerelease canary publishing via `scripts/release.sh --canary` +- Docker smoke testing via `scripts/docker-onboard-smoke.sh` +- stable publishing via `scripts/release.sh` +- pushing the stable branch commit and tag +- GitHub Release creation via `scripts/create-github-release.sh` +- website / announcement follow-up tasks + +## Trigger + +Use this skill when leadership asks for: + +- "do a release" +- "ship the next patch/minor/major" +- "release vX.Y.Z" + +## Preconditions + +Before proceeding, verify all of the following: + +1. `skills/release-changelog/SKILL.md` exists and is usable. +2. The repo working tree is clean, including untracked files. +3. There are commits since the last stable tag. +4. The release SHA has passed the verification gate or is about to. +5. If package manifests changed, the CI-owned `pnpm-lock.yaml` refresh is already merged on `master` before the release branch is cut. +6. npm publish rights are available locally, or the GitHub release workflow is being used with trusted publishing. +7. If running through Paperclip, you have issue context for status updates and follow-up task creation. + +If any precondition fails, stop and report the blocker. + +## Inputs + +Collect these inputs up front: + +- requested bump: `patch`, `minor`, or `major` +- whether this run is a dry run or live release +- whether the release is being run locally or from GitHub Actions +- release issue / company context for website and announcement follow-up + +## Step 0 — Release Model + +Paperclip now uses this release model: + +1. Start or resume `release/X.Y.Z` +2. Draft the **stable** changelog as `releases/vX.Y.Z.md` +3. Publish one or more **prerelease canaries** such as `X.Y.Z-canary.0` +4. Smoke test the canary via Docker +5. Publish the stable version `X.Y.Z` +6. Push the stable branch commit and tag +7. Create the GitHub Release +8. Merge `release/X.Y.Z` back to `master` without squash or rebase +9. Complete website and announcement surfaces + +Critical consequence: + +- Canaries do **not** use promote-by-dist-tag anymore. +- The changelog remains stable-only. Do not create `releases/vX.Y.Z-canary.N.md`. + +## Step 1 — Decide the Stable Version + +Start the release train first: + +```bash +./scripts/release-start.sh {patch|minor|major} +``` + +Then run release preflight: + +```bash +./scripts/release-preflight.sh canary {patch|minor|major} +# or +./scripts/release-preflight.sh stable {patch|minor|major} +``` + +Then use the last stable tag as the base: + +```bash +LAST_TAG=$(git tag --list 'v*' --sort=-version:refname | head -1) +git log "${LAST_TAG}..HEAD" --oneline --no-merges +git diff --name-only "${LAST_TAG}..HEAD" -- packages/db/src/migrations/ +git diff "${LAST_TAG}..HEAD" -- packages/db/src/schema/ +git log "${LAST_TAG}..HEAD" --format="%s" | rg -n 'BREAKING CHANGE|BREAKING:|^[a-z]+!:' || true +``` + +Bump policy: + +- destructive migrations, removed APIs, breaking config changes -> `major` +- additive migrations or clearly user-visible features -> at least `minor` +- fixes only -> `patch` + +If the requested bump is too low, escalate it and explain why. + +## Step 2 — Draft the Stable Changelog + +Invoke `release-changelog` and generate: + +- `releases/vX.Y.Z.md` + +Rules: + +- review the draft with a human before publish +- preserve manual edits if the file already exists +- keep the heading and filename stable-only, for example `v1.2.3` +- do not create a separate canary changelog file + +## Step 3 — Verify the Release SHA + +Run the standard gate: + +```bash +pnpm -r typecheck +pnpm test:run +pnpm build +``` + +If the release will be run through GitHub Actions, the workflow can rerun this gate. Still report whether the local tree currently passes. + +The GitHub Actions release workflow installs with `pnpm install --frozen-lockfile`. Treat that as a release invariant, not a nuisance: if manifests changed and the lockfile refresh PR has not landed yet, stop and wait for `master` to contain the committed lockfile before shipping. + +## Step 4 — Publish a Canary + +Run from the `release/X.Y.Z` branch: + +```bash +./scripts/release.sh {patch|minor|major} --canary --dry-run +./scripts/release.sh {patch|minor|major} --canary +``` + +What this means: + +- npm receives `X.Y.Z-canary.N` under dist-tag `canary` +- `latest` remains unchanged +- no git tag is created +- the script cleans the working tree afterward + +Guard: + +- if the current stable is `0.2.7`, the next patch canary is `0.2.8-canary.0` +- the tooling must never publish `0.2.7-canary.N` after `0.2.7` is already stable + +After publish, verify: + +```bash +npm view paperclipai@canary version +``` + +The user install path is: + +```bash +npx paperclipai@canary onboard +``` + +## Step 5 — Smoke Test the Canary + +Run: + +```bash +PAPERCLIPAI_VERSION=canary ./scripts/docker-onboard-smoke.sh +``` + +Confirm: + +1. install succeeds +2. onboarding completes +3. server boots +4. UI loads +5. basic company/dashboard flow works + +If smoke testing fails: + +- stop the stable release +- fix the issue +- publish another canary +- repeat the smoke test + +Each retry should create a higher canary ordinal, while the stable target version can stay the same. + +## Step 6 — Publish Stable + +Once the SHA is vetted, run: + +```bash +./scripts/release.sh {patch|minor|major} --dry-run +./scripts/release.sh {patch|minor|major} +``` + +Stable publish does this: + +- publishes `X.Y.Z` to npm under `latest` +- creates the local release commit +- creates the local git tag `vX.Y.Z` + +Stable publish does **not** push the release for you. + +## Step 7 — Push and Create GitHub Release + +After stable publish succeeds: + +```bash +git push public-gh HEAD --follow-tags +./scripts/create-github-release.sh X.Y.Z +``` + +Use the stable changelog file as the GitHub Release notes source. + +Then open the PR from `release/X.Y.Z` back to `master` and merge without squash or rebase. + +## Step 8 — Finish the Other Surfaces + +Create or verify follow-up work for: + +- website changelog publishing +- launch post / social announcement +- any release summary in Paperclip issue context + +These should reference the stable release, not the canary. + +## Failure Handling + +If the canary is bad: + +- publish another canary, do not ship stable + +If stable npm publish succeeds but push or GitHub release creation fails: + +- fix the git/GitHub issue immediately from the same checkout +- do not republish the same version + +If `latest` is bad after stable publish: + +```bash +./scripts/rollback-latest.sh +``` + +Then fix forward with a new patch release. + +## Output + +When the skill completes, provide: + +- stable version and, if relevant, the final canary version tested +- verification status +- npm status +- git tag / GitHub Release status +- website / announcement follow-up status +- rollback recommendation if anything is still partially complete