Merge pull request #25 from paperclipai/release/v2026.609.0

Release docs for paperclip v2026.609.0
This commit is contained in:
Aron Prins authored and GitHub committed 2026-06-10 12:22:24 +02:00
commit 6791a41a3a
55 files changed
+7192 -2139

No files matched your search

+5 -5
View File
@@ -1,9 +1,9 @@
{
"branch_mode": "release",
"base_release_tag": "v2026.529.0",
"base_release_sha": "911a1e8b0d24205acd5006e837a5c5e8c4bfb447",
"last_seen_parent_sha": "911a1e8b0d24205acd5006e837a5c5e8c4bfb447",
"last_applied_manifest_hash": "fe5efa1eb271efe303da514bb4e903fd722ae78a8e065c9692d01438d251adc6",
"last_run_at": "2026-05-30T17:27:15Z",
"base_release_tag": "v2026.609.0",
"base_release_sha": "a0f7d3dabaf5308ade45cae0c64ebd133948dca2",
"last_seen_parent_sha": "a0f7d3dabaf5308ade45cae0c64ebd133948dca2",
"last_applied_manifest_hash": "313c4fc9a3db09ded3cdfb9ec191d0d4e3e8f7e1e8b5d68ae39b96cc492ac25d",
"last_run_at": "2026-06-10T10:08:07Z",
"last_run_outcome": "applied"
}
+17 -50
View File
@@ -2,66 +2,33 @@
> Regenerated by `/sync-docs` every nightly run. Cumulative from `base_release_tag`.
- **Base release tag:** `v2026.525.0`
- **Parent window:** `v2026.525.0` → parent `master` HEAD `911a1e8`, effective cutoff `524e18b` (24h quarantine)
- **Commits in window:** 9 effective (5 commits in quarantine, deferred to next run)
- **Doc-relevant manifest entries:** 3 new this run (3 prior-run entries re-derived by cumulative diff, already applied)
- **Drift candidates:** 0 (check-drift.mjs against master)
- **Reconciliation candidates:** 0 (cumulative base is the release tag; all entries are net-new or already applied)
- **Genuine new dirs (rename-checked):** none
- **Base release tag:** `v2026.529.0`
- **Window:** `v2026.529.0` → parent `master` HEAD `0a2230b` (effective HEAD `76c88e5` after 24h quarantine)
- **Last run:** 2026-06-09
## Auto-merge
## Applied this run (PR tier — adapters/deploy)
_None._ No `.env.example` / config / env-schema changes in the window — the env-vars watcher matched nothing.
- **Gemini CLI bundled in Docker** (paperclip #7693) — `docs/reference/deploy/docker.md`, `docs/reference/adapters/gemini-local.md`. The official image now bakes in the Gemini CLI (`@google/gemini-cli`) alongside `claude`, `codex`, and `opencode`, and sets `GEMINI_SANDBOX=false`, so `gemini_local` works in-container out of the box. Docker bundled-CLI list also backfilled `opencode_local`.
- **External adapters can override built-ins** (paperclip #7394) — `docs/reference/adapters/external-adapters.md`. Corrected the stale "built-ins cannot be overwritten" note and documented the override + fallback lifecycle (`POST /api/adapters/install` accepts a built-in type; `requiresRestart` only on a true reinstall; pause snaps agents back to the built-in; built-ins protected from deletion).
## Already applied (prior run, commit `51b9fe3`)
## Needs human attention
These re-appear in the cumulative diff but are already documented; not re-edited this run.
- **Instance settings moved under Company Settings** (paperclip #7680) — navigation/IA change. Routes preserved via compatibility redirects, so no doc URLs break and no prose in `docs/administration/settings.md` is factually wrong. Impact is visual — see ⚠ Screenshots below. Consider a future note on how operators now reach Instance Settings (nested under Company Settings) once screenshots are refreshed.
- **Agent action buttons** (paperclip #7679 live-run stop finalization, #7695 clear-error) — granular UI controls in `AgentActionButtons`. Not currently surfaced in docs; flag only.
1. Resource memberships — join/leave projects & agents (`#6677`)
2. Document annotations & comments (`#6733`)
3. Bundled plugins in plugin manager (`#6734`)
## ⚠ Screenshots
## PR-tier — applied this run
### 1. Skills CLI command group (`#6782`)
- **Watcher:** cli-commands
- **Parent files:** `cli/src/index.ts` (`registerSkillsCommands(program)`); `cli/src/commands/client/skills.ts` (new).
- **Change kind:** added — new `paperclipai skills` command tree.
- **Surface:** `skills browse|search|inspect|install|list|show|file|import|create|scan-projects|check|update|audit|reset|remove` plus the `skills agent list|sync|clear` subgroup.
- **Docs targets:** `docs/reference/cli/control-plane-commands.md` (new "Skills Commands" workflow section); `docs/reference/cli/commands.md` (full `skills` subcommand + flag reference).
- **Evidence:** All command names, arguments, flags, and defaults (e.g. `--path` default `SKILL.md`, `--kind bundled|optional`) copied verbatim from `skills.ts`.
### 2. App-shipped skills catalog + lifecycle (`#6782`)
- **Watcher:** rest-routes, agent-skills, schemas, server-services
- **Parent files:** `server/src/routes/company-skills.ts` (new catalog + lifecycle routes); `server/src/services/skills-catalog.ts` (new); `packages/skills-catalog/**` (new package: bundled + optional SKILL.md bundles).
- **Change kind:** added.
- **Docs targets:** `docs/reference/skills.md` (new "App-shipped catalog" section + corrected versioning row); `docs/guides/org/skills.md` (friendly "install a ready-made skill" subsection).
- **Evidence:** Browse routes (auth-only, NOT company-scoped) `GET /api/skills/catalog`, `/api/skills/catalog/{catalogId}`, `/api/skills/catalog/{catalogId}/files`; lifecycle routes (company-scoped) `POST .../skills/install-catalog` (`{ catalogSkillId, slug?, force? }`), `.../skills/:skillId/audit`, `.../install-update`, `.../reset`. `CatalogSkillKind = bundled | optional`; `sourceKind: catalog`. Soft holds (local mods, `force`-overridable) vs hard-stop audit findings.
### 3. Private browser first-admin claim (`#6755`)
- **Watcher:** rest-routes, server-services
- **Parent files:** `server/src/routes/access.ts` (new `/bootstrap/claim` route); `server/src/first-admin-claim.ts` (new).
- **Change kind:** added.
- **Docs targets:** `docs/reference/api/instance-admin.md` ("Claim first instance admin" section); `docs/guides/getting-started/installation.md` (note under the private/Tailscale variation).
- **Evidence:** `POST /api/bootstrap/claim` — gated to `deploymentMode = authenticated` + `deploymentExposure = private` (else 404); requires signed-in browser session (else 401); promotes first signed-in user to `instance_admin` atomically; 409 if already claimed; success `{ claimed: true, userId }`.
## Considered, not documented
- **Accepted-plan decomposition exact-once guards (`#6831`)** — internal reliability/correctness guard with UI state; no user-facing surface to document.
- **CI runner Chrome for headless workflows (`#6967`, `524e18b0`)** — CI-only; not product-facing.
See `SCREENSHOTS_PENDING.md` — 18 entries stale (settings + adapter pages).
## ⚠ Drift
_None._
None.
## ↻ Renames detected
## ⚠ Reconcile
_None._
- paperclip #7678 ("Refine issue comment wake handoffs") is in-window but its revert (#7765) is held by the 24h quarantine. #7678 is an internal wake mechanism with no user-facing doc, so no edit was made. Once the revert clears quarantine it cancels out of the cumulative diff entirely.
## In quarantine (deferred to next run, <24h old at run time)
## Quarantined (held < 24h, processed next run)
`1f70fd9a` workspace finalize gates + no-remote-git enforcement (PAPA-430) · `5153b01a` Claude model refresh · `8014445b` v2026.529.0 release changelog · `aea35fe6` exe.dev config UX · `911a1e8b` continuation recovery retry streaks
- paperclip #7765 — Revert PR #7678.
- paperclip #7766 — Guard document comment wake boundaries.
+32 -120
View File
@@ -1,125 +1,37 @@
# Screenshots — pending recapture
# Screenshots pending refresh
> Regenerated by `/sync-docs` (release mode, v2026.529.0). Entries whose `depends_on` parent paths changed in the v2026.525.0 → v2026.529.0 window.
Window: v2026.529.0 → v2026.609.0 (parent a0f7d3d)
- **Window:** `v2026.525.0` → `v2026.529.0` (parent `911a1e8`)
- **Stale entries:** 90 of 276
- **Recapture:** `npm run screenshots:refresh` (stale only) or `npm run screenshots:refresh:all` (full). Output PNGs land in a PR for human review — never auto-pushed.
**182 of 276** registry screenshots are stale (their `depends_on` UI source changed).
## `ui/src/components/AgentConfigForm.tsx` — 20
Refresh with `npm run screenshots:refresh` (captures stale entries into a review PR — never auto-pushed).
- `dark/agents/adapter-type-dropdown.png`
- `dark/agents/budget-and-heartbeat-fields.png`
- `dark/agents/claude-local-config-filled.png`
- `dark/agents/codex-local-config.png`
- `dark/agents/heartbeat-toggle-disabled.png`
- `dark/agents/heartbeat-toggle-enabled.png`
- `dark/agents/http-adapter-config.png`
- `dark/agents/test-environment-fail.png`
- `dark/agents/test-environment-success.png`
- `dark/org/reassign.png`
- `light/agents/adapter-type-dropdown.png`
- `light/agents/budget-and-heartbeat-fields.png`
- `light/agents/claude-local-config-filled.png`
- `light/agents/codex-local-config.png`
- `light/agents/heartbeat-toggle-disabled.png`
- `light/agents/heartbeat-toggle-enabled.png`
- `light/agents/http-adapter-config.png`
- `light/agents/test-environment-fail.png`
- `light/agents/test-environment-success.png`
- `light/org/reassign.png`
## `ui/src/pages/AgentDetail.tsx` — 24
- `dark/agents/agent-detail-idle.png`
- `dark/agents/agent-detail-run-history.png`
- `dark/agents/agent-status-running.png`
- `dark/agents/budget.png`
- `dark/agents/configuration.png`
- `dark/agents/dashboard.png`
- `dark/agents/instructions.png`
- `dark/agents/run-history-in-progress.png`
- `dark/agents/run-transcript-view.png`
- `dark/agents/runs.png`
- `dark/agents/skills.png`
- `dark/costs/agent-budget-field.png`
- `light/agents/agent-detail-idle.png`
- `light/agents/agent-detail-run-history.png`
- `light/agents/agent-status-running.png`
- `light/agents/budget.png`
- `light/agents/configuration.png`
- `light/agents/dashboard.png`
- `light/agents/instructions.png`
- `light/agents/run-history-in-progress.png`
- `light/agents/run-transcript-view.png`
- `light/agents/runs.png`
- `light/agents/skills.png`
- `light/costs/agent-budget-field.png`
## `ui/src/pages/Agents.tsx` — 6
- `dark/agents/agents-list-empty.png`
- `dark/agents/list.png`
- `dark/agents/new.png`
- `light/agents/agents-list-empty.png`
- `light/agents/list.png`
- `light/agents/new.png`
## `ui/src/pages/CompanySkills.tsx` — 12
- `dark/org/skills-list.png`
- `dark/skills/add-skill-dialog-confirm.png`
- `dark/skills/add-skill-dialog.png`
- `dark/skills/assign-to-agent.png`
- `dark/skills/file-inventory.png`
- `dark/skills/list.png`
- `light/org/skills-list.png`
- `light/skills/add-skill-dialog-confirm.png`
- `light/skills/add-skill-dialog.png`
- `light/skills/assign-to-agent.png`
- `light/skills/file-inventory.png`
- `light/skills/list.png`
## `ui/src/pages/InstanceExperimentalSettings.tsx` — 2
- `dark/settings/experimental.png`
- `light/settings/experimental.png`
## `ui/src/pages/IssueDetail.tsx` — 10
- `dark/issues/detail-activity.png`
- `dark/issues/detail-chat.png`
- `dark/issues/detail-sidebar.png`
- `dark/issues/list.png`
- `dark/tasks/task-detail-with-comments.png`
- `light/issues/detail-activity.png`
- `light/issues/detail-chat.png`
- `light/issues/detail-sidebar.png`
- `light/issues/list.png`
- `light/tasks/task-detail-with-comments.png`
## `ui/src/pages/PluginManager.tsx` — 4
- `dark/plugins/install.png`
- `dark/plugins/list.png`
- `light/plugins/install.png`
- `light/plugins/list.png`
## `ui/src/pages/ProjectDetail.tsx` — 10
- `dark/projects/budget.png`
- `dark/projects/configuration.png`
- `dark/projects/issues.png`
- `dark/projects/overview.png`
- `dark/projects/workspaces.png`
- `light/projects/budget.png`
- `light/projects/configuration.png`
- `light/projects/issues.png`
- `light/projects/overview.png`
- `light/projects/workspaces.png`
## `ui/src/pages/Projects.tsx` — 2
- `dark/projects/list.png`
- `light/projects/list.png`
## Stale by changed UI source
- `ui/src/components/AgentConfigForm.tsx` → 20 screenshot(s)
- `ui/src/components/OnboardingWizard.tsx` → 4 screenshot(s)
- `ui/src/components/SidebarCompanyMenu.tsx` → 4 screenshot(s)
- `ui/src/pages/AdapterManager.tsx` → 10 screenshot(s)
- `ui/src/pages/AgentDetail.tsx` → 24 screenshot(s)
- `ui/src/pages/Agents.tsx` → 6 screenshot(s)
- `ui/src/pages/ApprovalDetail.tsx` → 12 screenshot(s)
- `ui/src/pages/CompanyAccess.tsx` → 2 screenshot(s)
- `ui/src/pages/CompanyExport.tsx` → 2 screenshot(s)
- `ui/src/pages/Costs.tsx` → 16 screenshot(s)
- `ui/src/pages/Dashboard.tsx` → 10 screenshot(s)
- `ui/src/pages/ExecutionWorkspaceDetail.tsx` → 6 screenshot(s)
- `ui/src/pages/Inbox.tsx` → 14 screenshot(s)
- `ui/src/pages/InstanceAccess.tsx` → 2 screenshot(s)
- `ui/src/pages/InstanceExperimentalSettings.tsx` → 2 screenshot(s)
- `ui/src/pages/InstanceGeneralSettings.tsx` → 2 screenshot(s)
- `ui/src/pages/InstanceSettings.tsx` → 2 screenshot(s)
- `ui/src/pages/IssueDetail.tsx` → 10 screenshot(s)
- `ui/src/pages/NewAgent.tsx` → 4 screenshot(s)
- `ui/src/pages/PluginManager.tsx` → 4 screenshot(s)
- `ui/src/pages/PluginPage.tsx` → 2 screenshot(s)
- `ui/src/pages/PluginSettings.tsx` → 4 screenshot(s)
- `ui/src/pages/ProfileSettings.tsx` → 2 screenshot(s)
- `ui/src/pages/ProjectDetail.tsx` → 10 screenshot(s)
- `ui/src/pages/Projects.tsx` → 2 screenshot(s)
- `ui/src/pages/RoutineDetail.tsx` → 6 screenshot(s)
- `ui/src/pages/Routines.tsx` → 2 screenshot(s)
+6
View File
@@ -1,3 +1,7 @@
---
paperclip_version: v2026.609.0
---
# Company Administration
Every Paperclip company has a small cluster of surfaces that control its identity, its membership, and its portability. These pages are where you, as a board user (or a member with the right grants), set the company's name and logo, decide who else can sign in, hand out invite links, review join requests, and move the whole company in and out of a portable package. The CEO agent never touches most of this — it is deliberately human territory.
@@ -55,6 +59,8 @@ Below the feedback section, a **Company Packages** panel links out to the dedica
At the bottom of the page is a **Danger Zone** with an **Archive company** button. Archiving hides the company from the sidebar and persists the change in the database. A confirmation dialog appears before the archive takes effect, and the UI automatically switches you to the next non-archived company if one is available.
Archiving also quiets the company so it stops doing work in the background: every active agent is paused (with the pause reason `company_archived`), and any queued or in-flight wake-up requests are cancelled. Archiving is not destructive — unlike deletion, your agents, projects, and issues are all preserved — but the company will not wake its agents again until you bring it back.
---
## Access & Members
@@ -0,0 +1,113 @@
---
paperclip_version: v2026.609.0
---
# Trust & Low-Trust Review
Some work in a Paperclip company is born from input you cannot vouch for: an untrusted pull request, an external ticket, a dependency diff, or output an agent generated from any of those. The goal of trust presets is simple — keep that kind of work *contained* while it runs, and make sure nothing it produces is silently fed into a more trusted agent before a human (or a trusted reviewer) has looked at it.
This page explains the two trust presets Paperclip ships with, what actually changes when an agent or its work is treated as low-trust, and how that work gets reviewed and promoted before it counts as accepted.
It helps to read [Execution Policy](../guides/power/execution-policy.md) first — trust presets layer on top of the same execution-policy plumbing, and a low-trust run leans on the isolated workspace and sandbox mechanics described there.
> **This is containment, not privacy.** Standard work in a Paperclip company stays company-visible by default — board users and in-company actors can still inspect company work objects. Low-trust containment is narrower: it limits what a low-trust agent can read or change through the Paperclip API, and it stops raw untrusted output from being automatically promoted into higher-trust agent context. It is not a general project, issue, or human privacy system.
---
## The two presets
Paperclip ships exactly two core trust presets, so containment decisions are enforced everywhere — including Community Edition, even when policy editing in the UI is not available.
- **`standard`** — the default. This is the normal, company-visible collaboration model you already know. Regular agents behave exactly as they always have, and this is what every agent gets unless a policy says otherwise.
- **`low_trust_review`** — an opt-in containment preset for automated work that may consume hostile or prompt-injected input. Turn it on for the kinds of tasks where you would not want the agent's reading and writing reach to extend across the company, and where its output should be quarantined until reviewed.
The default preset is `standard`; `low_trust_review` only takes effect when a policy explicitly opts in.
---
## How a preset is chosen
A low-trust preset is not a single switch. Paperclip resolves the effective preset by reading the same JSON policy you already use for execution, from up to four sources:
| Source | Where it lives |
|---|---|
| **Agent** | the agent's `permissions` — `trustPreset` and `authorizationPolicy.trustBoundary` |
| **Project** | the project's `executionWorkspacePolicy.authorizationPolicy` |
| **Issue** | the issue's `executionPolicy.authorizationPolicy` |
| **Run** | the run's `executionPolicy.authorizationPolicy` |
The resolver combines these. If *any* source asks for low-trust — either by naming the `low_trust_review` preset or by carrying a `trustBoundary` — the effective preset becomes `low_trust_review` for that work. The boundaries from each source are then intersected: **narrower always wins**. You cannot widen a boundary by relaxing one layer; you can only tighten it.
Two things make resolution **fail closed** (the work is denied rather than run with weaker containment):
- A policy source that names a *different* company, or uses an unsupported preset value.
- A low-trust preset that does not resolve to a concrete scope. Low-trust review must land on a real, company-local target — a root issue, one or more project ids, or an explicit list of issue ids. If none of those is present, resolution is denied.
### Inside a trust boundary
When `low_trust_review` resolves, it produces a single merged boundary. These are the fields you can set on a `trustBoundary`, all scoped to one company:
- `mode` — always `low_trust_review`.
- `companyId` — the company the boundary belongs to. A boundary that names another company is rejected outright.
- `projectIds` — projects the low-trust work may touch.
- `rootIssueId` — an issue subtree the work is confined to.
- `issueIds` — an explicit allow-list of issues.
- `allowedAgentIds` — agents permitted within the boundary.
- `allowedSecretBindingIds` — the secret bindings the run may reference.
- `allowedToolClasses` — the tool classes the run is allowed to use.
- `outputPromotionTarget` — where promoted output is allowed to land (an issue).
At least one of `rootIssueId`, `projectIds`, or `issueIds` must be present — that concrete scope is what the resolver requires before it will allow low-trust work to run.
---
## What changes at runtime
When a run resolves to `low_trust_review`, Paperclip refuses to start it unless it can actually enforce the boundary. A managed low-trust run **fails closed** unless all of the following hold:
- **Isolated workspaces are enabled** for the instance. Without them there is nowhere safe to contain the work.
- **The execution workspace mode is `isolated_workspace`.** A shared or host-local workspace is not acceptable for low-trust work.
- **The issue being run is inside the resolved boundary** — either directly named, in an allowed project, or a descendant of the boundary's `rootIssueId` (ancestry is checked up to a bounded depth). An issue outside the boundary is rejected.
- **The environment uses the `sandbox` driver.** A host-local adapter process is not allowed for managed low-trust execution; you need a sandboxed environment instead.
On top of that, two further constraints apply:
- **Secrets are restricted.** A low-trust run may only reference secret bindings whose ids are listed in the boundary's `allowedSecretBindingIds`, and inline sensitive environment values (raw API keys, tokens, and the like) are rejected — they must come through allowed bindings, not be pasted in.
- **Runtime services are denied by default.** A low-trust run cannot start or mutate workspace runtime services unless the boundary explicitly grants the `runtime.manage` tool class. If it tries to and that grant is absent, the run is stopped.
The built-in low-trust tool classes are read-only and conservative by design: `git.read`, `github.pr.read`, and `tests.local`. That is the shape of work this preset is meant for — read the untrusted change, run local tests, report back — not reach out and mutate the wider company.
If you have been doing untrusted-PR review by hand with a local Docker workflow, that still works for manual review. But anything Paperclip runs as managed low-trust execution goes through the sandboxed, isolated path described above.
---
## Reviewing and promoting low-trust output
Containment at runtime is only half the story. The other half is what happens to whatever the low-trust agent writes.
When a low-trust agent creates an issue, comment, document, or work product, Paperclip tags that artifact with **source-trust metadata** and gives it the disposition `quarantined`. Quarantined artifacts are stored, but their raw bodies are held back from higher-trust agent context. Where a higher-trust agent would otherwise read the body, it instead sees a placeholder explaining that quarantined low-trust output was omitted and that a trusted reviewer can inspect and promote a sanitized version. Comment presentation and metadata are stripped alongside the body, so nothing in the raw artifact leaks into a trusted run by accident.
The source-trust record carries enough provenance to audit the work later:
- `preset` — the trust preset that produced the artifact.
- `disposition` — `quarantined` or `promoted`.
- `sourceIssueId`, `sourceRunId`, `sourceAgentId` — where the artifact came from.
- `promotedFrom` — the original artifact a promotion was based on, including its `artifactKind` (`issue`, `comment`, `document`, or `work_product`), `artifactId`, and `issueId`.
- `promotedByActorType` (`agent`, `user`, or `system`), `promotedByActorId`, and `promotedAt` — who promoted it and when.
**Promotion** is the deliberate, reviewed step that turns quarantined output into something a trusted agent can act on. A trusted reviewer inspects the quarantined artifact, and a sanitized version is written with disposition `promoted` and a `promotedFrom` link back to the original. Only then does the content flow into higher-trust context. Nothing is auto-promoted — raw untrusted output never gets silently upgraded.
This source-trust tag lives on the artifact itself: issues, issue comments, documents, and issue work products each carry it, so the quarantine and promotion state travels with the work wherever it is read.
---
## When to reach for low-trust review
Reach for `low_trust_review` when an agent's task is to chew on input you do not control and you want a hard guarantee that its reach and its output stay contained:
- Reviewing an untrusted or external pull request.
- Triaging tickets or reports submitted from outside the company.
- Inspecting dependency diffs or third-party changes.
- Running an agent over any content that could carry a prompt-injection payload.
For everything else, `standard` is the right default — it keeps normal collaboration fast and company-visible, which is what most work wants.
+86
View File
@@ -0,0 +1,86 @@
---
paperclip_version: v2026.609.0
---
# Artifacts
As your agents work, they don't just leave comments — they produce things. A drafted document, a generated image, a rendered video, a CSV export, a file they attached to an issue. Those tangible outputs are **artifacts**, and the **Artifacts** page is the one place where you can see every one of them across your whole company, without opening each issue to go hunting.
Think of it as your company's shelf of finished and in-progress work product. If an agent made it while doing a task, it shows up here.
---
## What counts as an artifact
An artifact is something an agent produced in the course of doing work. Paperclip pulls three kinds of output into the Artifacts page and presents them as one unified list:
- **Documents** — keyed documents an agent wrote or revised on an issue (for example a plan, a brief, or a report). System-managed documents are filtered out, so you only see the real deliverables.
- **Attachments** — files an agent attached directly to an issue: images, PDFs, videos, CSVs, JSON, and so on.
- **Work products** — results an agent handed back as the formal output of a task, including ones that point at an attached file.
Each artifact carries the context you need to make sense of it: the issue it came from, the project (when there is one), the agent that created it, and when it was last updated. Every card links straight back to the exact spot on the originating issue, so one click takes you from the shelf to the source.
Artifacts are scoped to the company you're viewing. Switch companies and you see that company's shelf instead.
---
## Opening the Artifacts page
Open **Artifacts** from the left sidebar. By default it shows your company's artifacts grouped into stacks (more on that below), most recently updated first, and it loads more as you scroll.
When there's nothing to show yet, the page says so plainly — for a brand-new company you'll see "No artifacts yet. Outputs attached to issues will appear here." Once your agents start completing work, the shelf fills in on its own.
---
## Filtering by type
A row of type filters lets you narrow the shelf to one kind of output at a time:
- **All** — everything (the default)
- **Images**
- **Videos**
- **Documents**
- **Text**
- **Files** — anything that isn't an image, video, or text-like file
Pick a filter to focus. For example, switch to **Videos** when you want to review every recorded result an agent produced, or **Documents** when you're catching up on written deliverables.
There's also a search box at the top. Type a few words and the list narrows to artifacts whose title, summary, or originating issue match — and the search is reflected in the page URL, so a filtered view is easy to share or bookmark.
---
## Stacks: artifacts grouped by task
A single task can produce several artifacts, and a big piece of work can fan out across many sub-tasks. To keep that from becoming an undifferentiated wall of cards, Artifacts groups outputs into **stacks**.
Use the grouping control (the layered-squares button next to the filters) to choose how artifacts are bundled:
- **Task** — group everything by the individual task that produced it. This is the default view.
- **Parent task** — roll sub-task artifacts up under their top-level parent, so all the outputs from one larger initiative land in a single stack.
- **None** — turn grouping off and see a flat grid of every artifact.
In a grouped view, each stack is a card showing the task it belongs to, how many artifacts it holds, and a small preview of the first few. Click a stack to open it and see just that task's artifacts; an **All stacks** link takes you back to the overview. Your active type filter and search stay applied as you move in and out of a stack, and the grouping you choose is preserved in the URL so the view is shareable.
---
## Viewing artifacts
Cards are built to let you understand an artifact at a glance, without downloading it first:
- **Documents** show a clean text preview — markdown formatting is stripped down to readable plain text so you can skim the gist.
- **Text files** (and text-like files such as JSON or XML) show a short preview of their contents.
- **Images and videos** render as visual previews, so a generated video shows a thumbnail right on the card rather than an anonymous file row.
- **Other files** appear as a card with their type, ready to open or download.
Where an artifact came from a real file, the card gives you the means to open it inline or download it. And because every card deep-links back to its originating issue, you can always jump to the full context — the conversation, the run that produced it, and any related work — straight from the shelf.
---
## A quick mental model
- **Artifacts page** — every output your agents produced, company-wide, in one place.
- **Type filters** — narrow to images, videos, documents, text, or files.
- **Stacks** — artifacts grouped by their task (or rolled up by parent task), so related outputs stay together.
- **Cards** — previews you can read or watch in place, each linking back to the issue it came from.
You now know how to find, filter, and review everything your agents have made. When you want to dig into the work behind a given artifact, follow its link back to the issue and pick up the thread there.
+11 -2
View File
@@ -1,5 +1,5 @@
---
paperclip_version: v2026.529.0
paperclip_version: v2026.609.0
---
# Issues
@@ -318,7 +318,16 @@ The sidebar exposes the following fields, in order:
Above the tabs, separately from the Properties panel, the detail view also renders:
- An **Issue Workspace card** that summarises the issue's project and its execution workspace binding — the same underlying concept the Workspace / Branch / Folder rows describe, but surfaced as a single card so it is visible even when the sidebar is collapsed.
- An **Attachments** section. Images appear as thumbnails that open in a gallery modal; non-image attachments render as file rows with their content type and size. You can upload from the detail view when no attachments exist yet, and from an inline button otherwise.
- An **Attachments** section. Images appear as thumbnails that open in a gallery modal; video attachments play back inline in a built-in player; non-image attachments render as file rows with their content type and size. Each attachment offers two actions: **open** previews it inline (images in the gallery, video in the player), while **download** saves the file to disk. Supported media includes images, PDF, text, CSV, JSON, video (mp4, webm, mov/quicktime), and zip archives. You can upload from the detail view when no attachments exist yet, and from an inline button otherwise.
### Output
When an agent finishes a piece of work, it can hand back the result directly on the issue. The **Output** section surfaces those deliverables — the work products the agent produced — so you can inspect them right on the board without ever opening the agent's workspace.
- **Video deliverables play inline.** A recorded result plays back in a built-in player; you can play and seek without downloading anything first.
- **File deliverables show as preview cards.** Each one renders as a rich preview, with the primary deliverable highlighted when the agent flags one as the main result.
This is what makes review possible from anywhere — including for cloud reviewers who have no access to the agent's workspace. You watch or inspect the deliverable in the Output section, then comment or approve from the same issue, without leaving the board.
### Keyed documents
+7 -3
View File
@@ -1,5 +1,5 @@
---
paperclip_version: v2026.529.0
paperclip_version: v2026.609.0
---
# Skills
@@ -95,8 +95,12 @@ You don't have to write every skill from scratch. Paperclip ships with a built-i
The catalog comes in two flavours:
- **Bundled** skills are the everyday kit — things like keeping docs maintained, triaging issues, planning tasks, running QA acceptance, and managing a GitHub PR workflow.
- **Optional** skills are extras you add when you need them — for example, browser-driving an agent, drafting a release announcement, or running a design critique.
- **Bundled** skills are the everyday kit — things like keeping docs maintained, triaging issues, planning tasks, running QA acceptance, managing a GitHub PR workflow, and sketching low-fidelity UI **wireframes**.
- **Optional** skills are extras you add when you need them — for example, browser-driving an agent, drafting a release announcement, running a design critique, or researching what people have said about a topic in the last 30 days.
The catalog isn't only for engineers. Design, product, and research roles get a head start too: the `wireframe` skill, for instance, lets an agent draft black-and-white, low-fidelity screen layouts as SVG files before anyone commits to building the real thing — handy for a designer or product agent who wants to show structure first — and the `last30days` skill pulls recent posts and engagement from across the web so a researcher or marketer can see what people are actually saying about a topic right now. "Bundled" doesn't mean "forced on everyone": you still browse, pick, and install the ones you want.
A few catalog skills aren't shipped inside the app at all — they're pulled from an external repository pinned to a specific version. You install and update them the same way; Paperclip just fetches the files on your behalf. Either way the app knows the exact version you have, so the keep-it-current, audit, and reset features below work for every catalog skill.
Browse the catalog, pick a skill, and install it into your company library. Installing puts the skill in the library only — it doesn't attach it to any agent, so you stay in control of who uses it. From there it behaves like any other installed skill: open it, read its `SKILL.md`, and assign it to the agents that need it.
+91
View File
@@ -0,0 +1,91 @@
---
paperclip_version: v2026.609.0
---
# Team Catalog
Setting up a company from an empty page is a lot of decisions: which agents to hire, who reports to whom, what projects to open, what recurring work to schedule, and which skills each agent needs. The **Team Catalog** skips that blank-page problem. It's a set of ready-made starter teams — small, working groups of agents, projects, tasks, and skills — that you can browse and install into a company in one step.
Where a [skill](./skills.md) is one reusable procedure, a *team* is a whole unit of people and work. Install one and you get the agents, the org structure between them, the projects they work in, the recurring tasks that keep them moving, and the skills they rely on — all wired together the way a working team would already have them.
---
## Bundled vs optional teams
The catalog comes in two flavours, and the same install flow covers both.
- **Bundled** teams ship inside the app and are the everyday starting points. They lean on skills Paperclip already knows about, so installing one is self-contained. Examples shipping today: a **core executive team** (a CEO, CTO, and QA with a starter project and a recurring CEO heartbeat task), a **product-engineering** pod (CTO, Senior Coder, QA, and a weekly engineering sync), and a **product-design** team (a UX designer with the `wireframe` and `design-critique` skills and a weekly design review).
- **Optional** teams are extras you reach for when a specific need shows up — for example a content team that brings its own content-calendar skill and a recurring weekly content review. Some optional teams pull skills from external sources (GitHub, a URL, or skills.sh), which is why installing them asks you to opt in to those sources explicitly.
"Bundled" doesn't mean forced on you. You still browse, pick, and install only the teams you want — nothing lands in a company until you install it.
---
## Why you'd use one
A catalog team is the fastest way to get a company doing real work:
- **Start from a sensible default.** The core executive team mirrors the shape most companies end up with anyway — a leader, a builder, and a checker — so you're productive immediately instead of hiring agents one at a time.
- **Add a capability, not just an agent.** Need engineering? Install the product-engineering pod and you get the roles, the reporting lines, the project, the weekly sync, and the right skills together, not a lone agent you then have to configure.
- **Keep your org chart intact.** When you install a team into a company that already has people, you can point the team's leaders at an existing manager so the new group slots in underneath them rather than floating on its own.
---
## Browse and install from the app
Open the **Team Catalog** page to see the available teams, filter by bundled or optional, and read what each one contains before you commit. Selecting a team shows its agents, the projects and tasks it sets up, and the skills it needs — the same detail the CLI's `inspect` prints. If a team is already installed in the company, the catalog marks it as installed, and flags it when your installed copy has fallen behind a newer catalog version.
Installing is a two-look process by design. First you **preview**: Paperclip runs the import on the server and shows you exactly what would be created and which skills it would prepare, without writing anything. When the plan looks right, you **install**, and the team's agents, projects, tasks, and skills are created in the company.
A few choices happen at install time:
- **Who they report to.** You can attach the team's root agents to an existing manager so the new team joins your org chart instead of standing alone.
- **Which runtime each agent uses.** Catalog teams ship without a fixed adapter per agent on purpose, so the install lets you choose — for example a local Claude or Codex runtime — rather than locking you in.
- **External skill sources.** If an optional team declares GitHub, URL, or skills.sh skills, you opt in to those sources during install. Bundled teams that only use catalog skills need no opt-in.
- **Secrets.** If a team's agents need a secret value (an API key, say), the install collects it as a secret env input.
---
## What gets created when you install
Installing a team is a single action that builds out several things at once:
- **Agents** — every agent the team defines, with its role, prompt, and place in the reporting hierarchy. The team's root agents either stand on their own or report to the manager you picked.
- **Projects** — the workspaces the team operates in, so its agents have somewhere to do the work.
- **Tasks** — including any recurring routines the team ships, such as a CEO heartbeat or a weekly sync, imported the same careful way Paperclip imports any routine.
- **Skills** — the team's required skills are prepared in the company library: catalog skills are installed for you, and external-source skills are imported once you've allowed their sources.
Because every catalog team carries a version, Paperclip knows which version you installed. The catalog flags a team as out of date when a newer version has shipped, so you can reinstall the updated team when you're ready.
---
## A note on permissions
Installing a team creates agents, which requires permission to do so. If you (or an agent acting on your behalf) install a team without that permission, the action can fall back to a **board approval request** rather than simply failing — a board operator then reviews and approves the install. Any secrets you supplied are stripped from the stored request, so they're never exposed in the approval.
---
## Do it from the terminal
Everything above is available from the CLI too, which is handy for scripting a known company setup or reproducing one elsewhere.
```sh
# Browse and inspect
paperclipai teams browse --kind bundled
paperclipai teams inspect core-exec-team
# Preview, then install into a company
paperclipai teams preview core-exec-team --company-id <company-id>
paperclipai teams install core-exec-team --company-id <company-id>
```
For every command, flag, and the permission-fallback details, see the [Teams CLI reference](../../reference/cli/teams.md).
---
## See also
- [Teams CLI reference](../../reference/cli/teams.md) — the full `teams` command surface.
- [Skills](./skills.md) — the single-capability catalog that catalog teams build on.
- [Org structure](./org-structure.md) — how reporting lines work, which catalog teams plug into.
- [Agents](./agents.md) — configuring the agents an install creates.
+2 -2
View File
@@ -1,5 +1,5 @@
---
paperclip_version: v2026.525.0
paperclip_version: v2026.609.0
---
# Sync a local company to a Paperclip Cloud upstream
@@ -170,6 +170,6 @@ Follow `targetUrl` to land on the run inside the cloud stack and confirm the act
## See also
- [Cloud commands in the CLI reference](../reference/cli/commands.md#cloud) — every flag for `paperclipai cloud connect` and `paperclipai cloud push`.
- [Cloud commands in the CLI reference](../reference/cli/cloud.md) — every flag for `paperclipai cloud connect` and `paperclipai cloud push`.
- [Back up and restore a company](./back-up-and-restore-a-company.md) — the local portability export that the cloud push wraps.
- [Glossary](../guides/welcome/glossary.md) — definitions for company, agent, routine, monitor.
+19 -1
View File
@@ -1,3 +1,7 @@
---
paperclip_version: v2026.609.0
---
# External Adapters
External adapters let you ship a Paperclip adapter as its own npm package or local directory. Use them when you want independent versioning, separate distribution, or a runtime that should not live inside the Paperclip repo.
@@ -159,7 +163,21 @@ Useful management endpoints:
- `POST /api/adapters/:type/reinstall`
- `DELETE /api/adapters/:type`
> **Note:** Built-in adapters cannot be removed or overwritten by external installs.
> **Note:** Built-in adapters are protected from deletion. You can still install an external adapter that *overrides* a built-in type (see below), but the built-in itself stays in place as a fallback and can't be removed unless an external override exists for it.
---
## Overriding A Built-In
You can install an external adapter whose `type` matches a built-in — for example, ship your own `hermes_local` to pick up a newer or organization-specific implementation. `POST /api/adapters/install` accepts the override, and the external adapter takes precedence while the built-in stays available as a fallback.
A few things to know:
- A first-time override of a built-in type installs without requiring a restart. Reinstalling an override you already have (an existing external plugin record for that type) is the only case that reports `requiresRestart`.
- Pausing the override from the Adapter Manager snaps every agent of that type back to the built-in implementation on their next run; resuming restores the external adapter.
- Removing the override unregisters the external adapter and falls back to the built-in. The built-in itself is never deleted.
The operator-facing walkthrough of pausing, resuming, and removing overrides lives in [Settings → Instance: Adapters](../../administration/settings.md).
---
+6
View File
@@ -1,3 +1,7 @@
---
paperclip_version: v2026.609.0
---
# Gemini Local
`gemini_local` runs Google's Gemini CLI on the same machine as Paperclip. Use it when you want a local agent with session resume, configurable sandboxing, and Paperclip skills injected into Gemini's skills directory.
@@ -17,6 +21,8 @@
- You only need a command runner or script. Use [Process](./process.md).
- Gemini CLI is not installed or cannot reach the target working directory.
> **Running in Docker?** The official Paperclip image pre-bundles the Gemini CLI alongside the other local CLIs, so `gemini_local` works in-container without a manual install. The image also sets `GEMINI_SANDBOX=false` for safe in-container use. You still need to supply credentials — see [Docker](../deploy/docker.md).
---
## Common Fields
+76 -1
View File
@@ -1,3 +1,7 @@
---
paperclip_version: v2026.609.0
---
# Companies
Companies are the top-level tenant boundary in Paperclip. Every agent, project, issue, approval, cost event, and asset belongs to exactly one company, and the API enforces that boundary on every company-scoped route.
@@ -51,6 +55,77 @@ Returns one company object if the caller has access to that company.
---
## List company artifacts
```
GET /api/companies/{companyId}/artifacts
```
Returns a company-wide projection of the outputs agents produced while working. It flattens three sources — agent-authored issue documents, direct issue attachments, and `artifact` work products — into one card-ready list, so callers do not have to stitch per-issue endpoints together. Company access is checked before the projection runs.
### Query parameters
| Parameter | Values | Description |
|---|---|---|
| `kind` | `image`, `video`, `text`, `document`, `file`, `all` | Filter by media kind. Defaults to `all`. |
| `projectId` | UUID | Restrict to artifacts from issues in one project. |
| `q` | string (max 160 chars) | Free-text search over artifact title/summary and originating issue identifier/title. |
| `groupBy` | `none`, `task`, `parent_task` | Grouping mode. `none` (default) returns a flat artifact list; `task` groups by the originating issue; `parent_task` rolls sub-issue artifacts up under their root issue. |
| `groupIssueId` | UUID | When grouping, expand a single group (stack) into its own artifacts. |
| `limit` | integer 1–100 | Page size. Defaults to `30`. |
| `cursor` | string | Opaque pagination cursor from a prior response's `nextCursor`. |
### Response
The response is an envelope. In the ungrouped case it returns the flat `artifacts` list; when `groupBy` is `task` or `parent_task` it returns `groups` instead (or, when `groupIssueId` is supplied, the expanded group's `artifacts` plus `selectedGroup`). `nextCursor` is `null` when there are no further pages.
```json
{
"artifacts": [
{
"id": "work_product:...",
"source": "work_product",
"mediaKind": "video",
"title": "Launch teaser render",
"previewText": null,
"contentType": "video/mp4",
"contentPath": "/api/attachments/.../content",
"openPath": "/api/attachments/.../content",
"downloadPath": "/api/attachments/.../content?download=1",
"issue": { "id": "...", "identifier": "PAP-123", "title": "Produce launch teaser" },
"project": { "id": "...", "name": "Launch" },
"createdByAgent": { "id": "...", "name": "Video Agent" },
"updatedAt": "2026-06-01T12:00:00.000Z",
"href": "/PAP/issues/PAP-123#work-product-..."
}
],
"nextCursor": null
}
```
Each artifact carries:
| Field | Meaning |
|---|---|
| `id` | Stable artifact id, prefixed by source (`document:`, `attachment:`, or `work_product:`) |
| `source` | `document`, `attachment`, or `work_product` |
| `mediaKind` | `image`, `video`, `text`, `document`, `file`, or `empty` |
| `title` | Display title |
| `previewText` | Short plain-text preview, or `null` |
| `contentType` | MIME type of the underlying content, or `null` |
| `contentPath` | Path to the raw content, or `null` |
| `openPath` | Path for inline open, or `null` |
| `downloadPath` | Path for download, or `null` |
| `issue` | `{ id, identifier, title }` of the originating issue |
| `project` | `{ id, name }` of the issue's project, or `null` |
| `createdByAgent` | `{ id, name }` of the producing agent, or `null` |
| `updatedAt` | Last-updated timestamp |
| `href` | Deep link to the artifact on its originating issue |
A group object (returned under `groups` / `selectedGroup`) carries `id`, `groupBy`, `issue`, `title`, `count`, `mediaKinds`, `previewArtifacts`, `updatedAt`, and `href`.
---
## Create a company
```
@@ -344,7 +419,7 @@ POST /api/companies/{companyId}/archive
DELETE /api/companies/{companyId}
```
Archiving changes the company status to `archived` and removes it from default listings. Deleting removes the company and its related data. Deletion is destructive and should be treated as irreversible.
Archiving changes the company status to `archived` and removes it from default listings. It also runs a cascade that pauses every active agent in the company (pause reason `company_archived`) and cancels their queued or in-flight wake-up requests, so an archived company stops doing background work. Deleting removes the company and its related data. Deletion is destructive and should be treated as irreversible.
---
+24 -5
View File
@@ -1,5 +1,5 @@
---
paperclip_version: v2026.529.0
paperclip_version: v2026.609.0
---
# Issues
@@ -17,7 +17,7 @@ Issue APIs are company-aware. In practice that means:
- List and create operations are scoped to `/api/companies/{companyId}/issues`.
- Single-issue routes use `/api/issues/{issueId}`.
- Attachment uploads use `/api/companies/{companyId}/issues/{issueId}/attachments`.
- Attachment downloads use `/api/attachments/{attachmentId}/content`.
- Attachment downloads use `/api/attachments/{attachmentId}/content`, which supports inline preview, forced download (`?download=1`), and HTTP Range requests.
On issue-scoped routes, `{issueId}` can be either:
@@ -843,7 +843,11 @@ Attachments are file uploads linked to an issue, and optionally to a specific is
GET /api/issues/{issueId}/attachments
```
Return all attachments for an issue. Each item includes a `contentPath` that points to the binary download route.
Return all attachments for an issue. Each item carries three path fields pointing at the binary content route:
- `contentPath` — `/api/attachments/{attachmentId}/content`. The raw content route.
- `openPath` — same value as `contentPath`. Use it to open or preview the attachment inline.
- `downloadPath` — `/api/attachments/{attachmentId}/content?download=1`. Use it to force a download.
### Upload Attachment
@@ -864,7 +868,12 @@ Upload rules:
- Empty files are rejected.
- Files larger than the server limit are rejected.
- `issueCommentId` must belong to the same company and issue.
- The stored response includes `contentPath` for download.
- The content type must be in the allowed-uploads set.
- The stored response includes `contentPath`, `openPath`, and `downloadPath`.
The default allowed upload types are images, PDF, plain text, JSON, CSV, HTML, `application/zip`, and the video types `video/mp4`, `video/webm`, and `video/quicktime`. Video types are also treated as inline-renderable. Override the allowlist with the `PAPERCLIP_ALLOWED_ATTACHMENT_TYPES` environment variable — a comma-separated list of MIME types or wildcard patterns.
When a file is uploaded with a generic content type (`application/octet-stream`, `binary/octet-stream`, or `application/x-binary`), the server infers a video content type from the filename extension when streaming it back: `.mp4`/`.m4v` → `video/mp4`, `.webm` → `video/webm`, and `.mov`/`.qt`/`.quicktime` → `video/quicktime`.
### Download Attachment Content
@@ -874,7 +883,17 @@ GET /api/attachments/{attachmentId}/content
Stream the attachment bytes.
The server sets the response headers for inline display or download depending on content type, and SVG content gets a sandboxed content security policy.
By default the server sets `Content-Disposition` for inline display when the content type is inline-capable (images, PDF, video, and similar), and otherwise serves it as a download. SVG content gets a sandboxed content security policy.
Query parameters:
- `download=1` — force `Content-Disposition: attachment` so the response is always saved as a download instead of rendered inline.
This route supports HTTP Range requests so large media such as video can stream and seek:
- The response sets `Accept-Ranges: bytes`.
- A valid `Range: bytes=...` request returns `206 Partial Content` with a `Content-Range` header.
- An unsatisfiable range returns `416 Range Not Satisfiable` with `Content-Range: bytes */{length}`.
### Delete Attachment
+14
View File
@@ -1,3 +1,7 @@
---
paperclip_version: v2026.609.0
---
# API Overview
Paperclip exposes a JSON API for company control-plane work: companies, agents, issues, approvals, costs, routines, secrets, activity, and dashboard state. This page is the shared reference for how the API is structured before you jump into a specific resource.
@@ -168,6 +172,16 @@ health = requests.get("http://localhost:3100/api/health").json()
<!-- /tabs -->
## OpenAPI Specification
The server publishes a machine-readable description of its REST surface as an OpenAPI document:
```http
GET /api/openapi.json
```
Point your own tooling — client generators, request validators, or an API explorer — at this endpoint to get the current set of routes, parameters, and schemas straight from the running instance. From the terminal, `paperclipai access openapi` fetches the same document for you. This reference is written for people; the OpenAPI document is the same surface in a form built for machines.
## A Good First API Call
If you are already authenticated, a simple company read is a good way to verify access:
+6 -1
View File
@@ -1,3 +1,7 @@
---
paperclip_version: v2026.609.0
---
# Routines
Routines are Paperclip's recurring execution layer. Use them when you want an agent to run on a schedule, respond to a webhook, or be kicked off manually through the API.
@@ -668,7 +672,7 @@ Routine runs use these statuses:
|---|---|
| `received` | The run was accepted and is being processed. |
| `coalesced` | A live execution already existed, so this run linked to it. |
| `skipped` | A live execution already existed, and the concurrency policy chose to skip. |
| `skipped` | The run did not create work. Either a live execution already existed and the concurrency policy chose to skip, or the routine's project was paused at tick time. Read `failureReason` to tell them apart — a paused project records `failureReason: "paused"` (the trigger's last result spells it out as `Skipped because the project is paused`). |
| `issue_created` | A new execution issue was created. |
| `completed` | The execution issue later moved to `done`. |
| `failed` | The execution issue failed, was cancelled, or the dispatch failed. |
@@ -705,6 +709,7 @@ Board operators need `tasks:assign` permission for actions that assign work to a
- Webhook triggers mint a company secret behind the scenes.
- The public webhook URL does not change when you rotate the secret.
- If a routine is archived, it will not fire again.
- If the routine's project is paused, scheduled ticks are suppressed: the firing is recorded as a `skipped` run and the schedule advances to the next tick, but no execution issue is created and the missed tick is **not** backfilled when the project resumes.
- If a run finds an active live execution issue and the concurrency policy is not `always_enqueue`, the run is linked or skipped instead of creating new work.
If you are wiring this from code, the common path is:
+231
View File
@@ -0,0 +1,231 @@
---
paperclip_version: v2026.609.0
---
# Teams Catalog API
The teams catalog API is the control-plane surface behind catalog *teams* — the ready-made starter groups Paperclip ships with the app. A catalog team bundles a small set of agents, projects, tasks, and the skills they need, so you can drop a whole working unit into a company in one step. These endpoints let you browse the catalog, inspect a single team and its files, see what is already installed in a company, preview an import, and install a team.
For the user-facing concept and CLI workflow, see [Teams Commands](../cli/teams.md) and the [Team Catalog guide](../../guides/org/team-catalog.md). This page documents the raw HTTP endpoints that those tools call.
> All routes here are mounted under `/api`. The browsing routes need only an authenticated caller and no company context; the `installed`, `preview`, and `install` routes are company-scoped and check company access.
There are two kinds of catalog teams. **Bundled** teams ship inside the app and are the everyday starting points. **Optional** teams are extras, some of which pull in skills from external sources. The `kind` filter switches between the two.
---
## Browse the catalog
```
GET /api/teams/catalog
```
Returns the catalog teams the caller can see. Browsing reads the catalog without writing anything and requires no company context.
### Query parameters
| Parameter | Values | Description |
|---|---|---|
| `kind` | `bundled`, `optional` | Filter by catalog kind. |
| `category` | string | Filter by catalog category slug. |
| `q` | string | Free-text search over the catalog. |
Each catalog team in the response carries `id`, `key`, `kind`, `category`, `slug`, `name`, `description`, `path`, `entrypoint`, `schema`, `defaultInstall`, `recommendedForCompanyTypes`, `tags`, a `counts` object (`agents`, `projects`, `tasks`, `routines`, `localSkills`, `catalogSkills`, `externalSkillSources`), `rootAgentSlugs`, `agentSlugs`, `projectSlugs`, `requiredSkills`, `envInputs`, `sourceRefs`, `files`, `trustLevel`, `compatibility`, and `contentHash`. Optional teams sourced from a package also include `packageName` and `packageVersion`.
---
## Inspect one catalog team
```
GET /api/teams/catalog/{catalogId}
```
Returns the full record for a single catalog team — the same shape as a list entry, including its agents, projects, tasks, required skills, and file manifest. Inspecting a team before you install it is the clearest way to see exactly what will land in the company.
The path segment `{catalogId}` is a catalog reference: a catalog team ID, key, or unique slug. You can also pass the reference explicitly with the `ref` query parameter, which takes precedence over the path segment.
### Query parameters
| Parameter | Description |
|---|---|
| `ref` | Catalog reference (ID, key, or slug). Overrides the `{catalogId}` path segment when present. |
---
## Read a catalog team file
```
GET /api/teams/catalog/{catalogId}/files
```
Returns one file out of the catalog team package — for example `TEAM.md`. Use this to read a team's manifest or any referenced file without installing it.
### Query parameters
| Parameter | Description |
|---|---|
| `ref` | Catalog reference (ID, key, or slug). Overrides the `{catalogId}` path segment when present. |
| `path` | Relative path of the file to read inside the team package. Defaults to `TEAM.md`. |
The response carries `catalogTeamId`, `path`, `kind`, `content`, `language`, and `markdown`.
---
## List installed catalog teams
```
GET /api/companies/{companyId}/teams/catalog/installed
```
Returns the catalog teams already installed in the given company. This is the company-lensed view: it tells you which teams are present and lets a client flag teams that have drifted out of date from the current catalog version. Company access is checked before the listing runs.
---
## Preview an install
```
POST /api/companies/{companyId}/teams/catalog/{catalogId}/preview
```
Runs the import on the server and returns the plan without writing anything to the company. The preview reports what would be created, which skills the team needs and how they would be prepared, and any warnings or errors. Company access is checked before the preview runs.
All body fields are optional:
| Field | Type | Description |
|---|---|---|
| `targetManagerAgentId` | string or `null` | Existing agent ID that the catalog's root agents should report to. |
| `targetManagerSlug` | string or `null` | Portable manager slug that the catalog's root agents should report to. |
| `include` | object | Portability include block selecting which entity kinds to import. |
| `agents` | `"all"` or array of slugs | Restrict the import to selected agent slugs. |
| `collisionStrategy` | `rename`, `skip`, `replace` | How to handle entities that already exist. |
| `nameOverrides` | object (`slug` → `name`) | Override imported entity names. |
| `selectedFiles` | array of strings | Restrict the import to selected portable files. |
| `sourcePolicy` | object | Gate external skill sources. Accepts `allowExternalSources`, `allowUnpinnedOptionalSources`, and `allowLocalPathSources` booleans. |
The `{catalogId}` path segment is a catalog reference, and the `ref` query parameter overrides it when present, exactly as on the browse routes.
<!-- tabs: cURL, JavaScript, Python -->
<!-- tab: cURL -->
```bash
curl -X POST "http://localhost:3100/api/companies/company-1/teams/catalog/core-exec-team/preview" \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{
"targetManagerSlug": "cto",
"collisionStrategy": "rename"
}'
```
<!-- tab: JavaScript -->
```js
const res = await fetch(
"http://localhost:3100/api/companies/company-1/teams/catalog/core-exec-team/preview",
{
method: "POST",
headers: {
Authorization: `Bearer ${token}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
targetManagerSlug: "cto",
collisionStrategy: "rename",
}),
},
);
const plan = await res.json();
```
<!-- tab: Python -->
```py
import requests
res = requests.post(
"http://localhost:3100/api/companies/company-1/teams/catalog/core-exec-team/preview",
headers={"Authorization": f"Bearer {token}"},
json={
"targetManagerSlug": "cto",
"collisionStrategy": "rename",
},
)
plan = res.json()
```
<!-- /tabs -->
---
## Install a team
```
POST /api/companies/{companyId}/teams/catalog/{catalogId}/install
```
Does what `preview` shows: imports the team into the company, creating its agents, projects, and tasks, and preparing its required skills. On success the route responds with `201`. Because installing a team creates agents, the caller must be able to create agents in the company — board callers need the `agents:create` permission (local implicit and instance-admin board callers are exempt), and agent callers need an `agents:create` grant or the equivalent agent permission.
The install body accepts everything `preview` accepts, plus:
| Field | Type | Description |
|---|---|---|
| `adapterOverrides` | object (`slug` → override) | Pin an imported agent to an adapter. Each override is an object with `adapterType` (string) and an optional `adapterConfig` object. |
| `secretValues` | object (`key` → `value`) | Provide values for the secret env inputs the team declares. |
As with preview, `{catalogId}` is a catalog reference and the `ref` query parameter overrides it when present.
<!-- tabs: cURL, JavaScript, Python -->
<!-- tab: cURL -->
```bash
curl -X POST "http://localhost:3100/api/companies/company-1/teams/catalog/product-engineering/install" \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{
"targetManagerSlug": "cto",
"adapterOverrides": {
"senior-coder": { "adapterType": "claude_local" }
}
}'
```
<!-- tab: JavaScript -->
```js
const res = await fetch(
"http://localhost:3100/api/companies/company-1/teams/catalog/product-engineering/install",
{
method: "POST",
headers: {
Authorization: `Bearer ${token}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
targetManagerSlug: "cto",
adapterOverrides: {
"senior-coder": { adapterType: "claude_local" },
},
}),
},
);
const result = await res.json();
```
<!-- tab: Python -->
```py
import requests
res = requests.post(
"http://localhost:3100/api/companies/company-1/teams/catalog/product-engineering/install",
headers={"Authorization": f"Bearer {token}"},
json={
"targetManagerSlug": "cto",
"adapterOverrides": {
"senior-coder": {"adapterType": "claude_local"},
},
},
)
result = res.json()
```
<!-- /tabs -->
---
## Practical notes
- Catalog teams ship without a fixed adapter per agent on purpose. Use `adapterOverrides` to pin a specific agent to a runtime such as `claude_local` or `codex_local`; agents you do not override are resolved at import time.
- Browse, inspect, and file reads require only an authenticated caller. The `installed`, `preview`, and `install` routes check company access first.
- Preview writes nothing. Run it before `install` to confirm what will be created and how the team's skills will be prepared.
- The install route enforces the `agents:create` permission. If you lack it, the install is rejected before anything is created.
+263
View File
@@ -0,0 +1,263 @@
---
paperclip_version: v2026.609.0
---
# Access, Profile & Instance Admin
Use these commands to inspect who you are on an instance, read and update profiles, manage invites and join requests, administer users and instance settings, tune your own sidebar and inbox preferences, and pull the LLM-facing documentation the server publishes. They are the "everything around the work" surface: identity, membership, and instance configuration rather than companies, agents, or issues.
Most of these commands are thin wrappers over Paperclip API endpoints. They accept the standard client flags documented in [common options](./common-options.md) — `--api-base`, `--api-key`, `--context`, `--profile`, `--data-dir`, and `--json`. Company-scoped commands additionally take `-C, --company-id <id>`; if you omit it, the CLI falls back to the company recorded in your selected profile.
> **Tip:** Pass `--json` on any command here when you are scripting. The default output is human-readable; `--json` emits the raw API response.
---
## Health & identity
Start here when you are unsure whether the CLI is talking to the instance you think it is, or which identity it is using.
```sh
paperclipai health
paperclipai whoami
paperclipai openapi
```
| Command | What it does |
| --- | --- |
| `health` | `GET /api/health`. Confirms the API is reachable. This is the same endpoint the CLI hints at when a connection fails. |
| `whoami` | `GET /api/cli-auth/me`. Shows the current CLI auth identity — the persona, user, and scope the resolved credential maps to. Also available as `access whoami`. |
| `openapi` | `GET /api/openapi.json`. Prints the full OpenAPI document. Always emitted as JSON regardless of `--json`. |
> **Note:** `whoami` is the fastest way to verify which persona (board operator or agent) and which company your active profile resolves to before you run a state-changing command.
The `access` group exists as a namespace for auth inspection and currently exposes `access whoami` as an alias of the top-level `whoami`.
---
## Profile
Read and update the profile attached to your authenticated session, and look up another user's profile within a company.
```sh
paperclipai profile session
paperclipai profile get
paperclipai profile update --payload-json '{"displayName":"Ada Lovelace"}'
paperclipai profile company-user <user-slug> --company-id <company-id>
```
| Command | Method & path | Notes |
| --- | --- | --- |
| `profile session` | `GET /api/auth/get-session` | The raw auth session object. |
| `profile get` | `GET /api/auth/profile` | Your current profile. |
| `profile update` | `PATCH /api/auth/profile` | Requires `--payload-json`. Sends the JSON body verbatim. |
| `profile company-user <userSlug>` | `GET /api/companies/<company-id>/users/<userSlug>/profile` | Company-scoped; takes `-C, --company-id`. |
---
## Invites
Invites let you bring a human or agent onto a company. The board operator creates and revokes them; the invited party reads the invite (and its onboarding material) by token, then accepts it.
```sh
paperclipai invite list --company-id <company-id>
paperclipai invite create --company-id <company-id> --payload-json '{"role":"member"}'
paperclipai invite revoke <invite-id>
paperclipai invite show <token>
paperclipai invite onboarding:text <token>
paperclipai invite accept <token> --payload-json '{}'
```
| Command | Method & path | Notes |
| --- | --- | --- |
| `invite list` | `GET /api/companies/<company-id>/invites` | Company-scoped. |
| `invite create` | `POST /api/companies/<company-id>/invites` | Company-scoped. Requires `--payload-json`. |
| `invite revoke <inviteId>` | `POST /api/invites/<invite-id>/revoke` | Revokes by invite ID, not token. |
| `invite show <token>` | `GET /api/invites/<token>` | The invite as seen by the recipient. |
| `invite logo <token>` | `GET /api/invites/<token>/logo` | The inviting company's logo. |
| `invite onboarding <token>` | `GET /api/invites/<token>/onboarding` | Structured onboarding content. |
| `invite onboarding:text <token>` | `GET /api/invites/<token>/onboarding.txt` | Onboarding as plain text — handy for piping into an AI. |
| `invite skills:index <token>` | `GET /api/invites/<token>/skills/index` | The skill index offered to the invitee. |
| `invite skill <token> <skillName>` | `GET /api/invites/<token>/skills/<skill-name>` | One skill's markdown. |
| `invite accept <token>` | `POST /api/invites/<token>/accept` | `--payload-json` defaults to `{}`. |
| `invite test-resolution <token> --url <url>` | `GET /api/invites/<token>/test-resolution?url=...` | Diagnostic: checks how an invite URL resolves. `--url` is required. |
> **Note:** `invite revoke` takes the **invite ID**, while every other invite command takes the opaque **token** the recipient receives. They are not interchangeable.
---
## Join requests
Join requests are the inbound counterpart to invites: an agent or user asks to join a company, a board operator approves or rejects, and an approved request can mint an agent API key.
```sh
paperclipai join list --company-id <company-id> --status pending
paperclipai join approve <request-id> --company-id <company-id>
paperclipai join reject <request-id> --company-id <company-id>
paperclipai join claim-key <request-id> --claim-secret <secret>
```
| Command | Method & path | Notes |
| --- | --- | --- |
| `join list` | `GET /api/companies/<company-id>/join-requests` | Company-scoped. Supports `--status` and `--request-type` filters. |
| `join approve <requestId>` | `POST /api/companies/<company-id>/join-requests/<request-id>/approve` | Company-scoped. |
| `join reject <requestId>` | `POST /api/companies/<company-id>/join-requests/<request-id>/reject` | Company-scoped. |
| `join claim-key <requestId>` | `POST /api/join-requests/<request-id>/claim-api-key` | Requires `--claim-secret`. Returns the agent API key for an approved request. |
The `--status` filter accepts `pending_approval`, `approved`, and `rejected`. `pending` is accepted as an alias and is normalized to `pending_approval`.
> **Warning:** `join claim-key` returns an agent API key. Treat the output as a secret — it is the credential that lets an agent act on the instance.
---
## Members
Manage the people and agents already inside a company: list them, update roles and grants, adjust permissions, or archive a member.
```sh
paperclipai member list --company-id <company-id>
paperclipai member user-directory --company-id <company-id>
paperclipai member update <member-id> --company-id <company-id> --payload-json '{...}'
paperclipai member role-and-grants <member-id> --company-id <company-id> --payload-json '{...}'
paperclipai member permissions <member-id> --company-id <company-id> --payload-json '{...}'
paperclipai member archive <member-id> --company-id <company-id>
```
| Command | Method & path | Notes |
| --- | --- | --- |
| `member list` | `GET /api/companies/<company-id>/members` | Company-scoped. |
| `member user-directory` | `GET /api/companies/<company-id>/user-directory` | Company-scoped. |
| `member update <memberId>` | `PATCH /api/companies/<company-id>/members/<member-id>` | Requires `--payload-json`. |
| `member role-and-grants <memberId>` | `PATCH /api/companies/<company-id>/members/<member-id>/role-and-grants` | Requires `--payload-json`. |
| `member permissions <memberId>` | `PATCH /api/companies/<company-id>/members/<member-id>/permissions` | Requires `--payload-json`. |
| `member archive <memberId>` | `POST /api/companies/<company-id>/members/<member-id>/archive` | `--payload-json` defaults to `{}`. |
---
## Instance admin
Instance-admin commands operate across the whole instance, not a single company. You need board-operator authority to use them.
### Users
```sh
paperclipai admin user list --query ada
paperclipai admin user promote <user-id>
paperclipai admin user demote <user-id>
paperclipai admin user company-access <user-id>
paperclipai admin user company-access:update <user-id> --payload-json '{...}'
```
| Command | Method & path | Notes |
| --- | --- | --- |
| `admin user list` | `GET /api/admin/users` | Optional `--query` text search. |
| `admin user promote <userId>` | `POST /api/admin/users/<user-id>/promote-instance-admin` | Grants instance-admin. |
| `admin user demote <userId>` | `POST /api/admin/users/<user-id>/demote-instance-admin` | Revokes instance-admin. |
| `admin user company-access <userId>` | `GET /api/admin/users/<user-id>/company-access` | Reads which companies a user can reach. |
| `admin user company-access:update <userId>` | `PUT /api/admin/users/<user-id>/company-access` | Requires `--payload-json`. Replaces the user's company access. |
### Instance settings & maintenance
```sh
paperclipai instance scheduler-heartbeats
paperclipai instance settings:general
paperclipai instance settings:general:update --payload-json '{...}'
paperclipai instance settings:experimental
paperclipai instance settings:experimental:update --payload-json '{...}'
paperclipai instance database-backup
```
| Command | Method & path | Notes |
| --- | --- | --- |
| `instance scheduler-heartbeats` | `GET /api/instance/scheduler-heartbeats` | Lists scheduler heartbeat agents. |
| `instance settings:general` | `GET /api/instance/settings/general` | Reads general settings. |
| `instance settings:general:update` | `PATCH /api/instance/settings/general` | Requires `--payload-json`. |
| `instance settings:experimental` | `GET /api/instance/settings/experimental` | Reads experimental flags. |
| `instance settings:experimental:update` | `PATCH /api/instance/settings/experimental` | Requires `--payload-json`. |
| `instance database-backup` | `POST /api/instance/database-backups` | Triggers a database backup. |
> **Warning:** Experimental settings change instance-wide behavior. Read the current values with `instance settings:experimental` before you patch them, and change one flag at a time.
---
## Sidebar & inbox preferences
These commands tune your own view of the product — what the navigation sidebar shows and which board inbox items you have dismissed. They are personal, not company policy.
```sh
paperclipai sidebar preferences
paperclipai sidebar preferences:update --payload-json '{...}'
paperclipai sidebar project-preferences --company-id <company-id>
paperclipai sidebar project-preferences:update --company-id <company-id> --payload-json '{...}'
paperclipai sidebar badges --company-id <company-id>
paperclipai inbox dismissals --company-id <company-id>
paperclipai inbox dismiss --company-id <company-id> --payload-json '{"itemKey":"run:<run-id>"}'
```
| Command | Method & path | Notes |
| --- | --- | --- |
| `sidebar preferences` | `GET /api/sidebar-preferences/me` | Your global sidebar prefs. |
| `sidebar preferences:update` | `PUT /api/sidebar-preferences/me` | Requires `--payload-json`. |
| `sidebar project-preferences` | `GET /api/companies/<company-id>/sidebar-preferences/me` | Company-scoped. |
| `sidebar project-preferences:update` | `PUT /api/companies/<company-id>/sidebar-preferences/me` | Company-scoped. Requires `--payload-json`. |
| `sidebar badges` | `GET /api/companies/<company-id>/sidebar-badges` | Company-scoped badge counts. |
| `inbox dismissals` | `GET /api/companies/<company-id>/inbox-dismissals` | Company-scoped. |
| `inbox dismiss` | `POST /api/companies/<company-id>/inbox-dismissals` | Company-scoped. Requires `--payload-json`. |
---
## Board claim & OpenClaw
Board-claim tokens are the one-time bootstrap mechanism that promotes a browser-authenticated user to instance owner on an `authenticated` instance. OpenClaw is the integration helper that generates an invite prompt.
```sh
paperclipai board-claim show <token>
paperclipai board-claim claim <token> --payload-json '{}'
paperclipai openclaw invite-prompt --company-id <company-id> --payload-json '{...}'
```
| Command | Method & path | Notes |
| --- | --- | --- |
| `board-claim show <token>` | `GET /api/board-claim/<token>` | Inspect a pending claim without acting on it. |
| `board-claim claim <token>` | `POST /api/board-claim/<token>/claim` | `--payload-json` defaults to `{}`. |
| `openclaw invite-prompt` | `POST /api/companies/<company-id>/openclaw/invite-prompt` | Company-scoped. Requires `--payload-json`. |
> **Note:** Claiming a board migrates ownership of the instance to your user. It is normally done once, in the browser; the CLI variant exists for headless setups. See [authentication](authentication.md) for the full bootstrap flow.
---
## Public skills & LLM documentation
The server publishes a public skill catalog and a set of LLM-facing prompt documents. Pull these when you are configuring agents or feeding context to an AI operator.
```sh
paperclipai available-skill list
paperclipai available-skill index
paperclipai available-skill get <skill-name>
paperclipai llm agent-configuration
paperclipai llm agent-configuration:adapter <adapter-type>
paperclipai llm agent-icons
```
| Command | Method & path | Notes |
| --- | --- | --- |
| `available-skill list` | `GET /api/skills/available` | The public skill catalog. |
| `available-skill index` | `GET /api/skills/index` | The skill index. |
| `available-skill get <skillName>` | `GET /api/skills/<skill-name>` | One skill's markdown. |
| `llm agent-configuration` | `GET /api/llms/agent-configuration.txt` | Prompt docs for configuring an agent. |
| `llm agent-configuration:adapter <adapterType>` | `GET /api/llms/agent-configuration/<adapter-type>.txt` | Adapter-specific config prompt docs. |
| `llm agent-icons` | `GET /api/llms/agent-icons.txt` | Prompt docs for choosing agent icons. |
> **Tip:** Pipe `llm agent-configuration:adapter <adapter-type>` straight into your AI operator's context when it is drafting a new agent config — the document is written to be consumed by an LLM. See [skills](skills.md) and [adapters](adapter.md) for how these feed into agent creation.
---
## See also
- [Common options](./common-options.md) — the flags shared by every client command.
- [Authentication](authentication.md) — `connect`, `auth login`, board claim, and token minting.
- [Company commands](company.md) — manage the companies these members and invites belong to.
- [Agent commands](agent.md) — create and configure the agents you invite or admit.
- [Skills](skills.md) and [Adapters](adapter.md) — what the public skill catalog and LLM config docs drive.
- [Output & scripting](./output-and-scripting.md) — using `--json` to build pipelines.
+92
View File
@@ -0,0 +1,92 @@
---
paperclip_version: v2026.609.0
---
# Activity Commands
The activity log is the company's audit trail: a chronological record of who did what to which entity. Reach for these commands when you are monitoring a running company, reconstructing what an agent did, or wiring an external system into the same record the Paperclip UI shows. It is exactly the trail you see in the UI's activity feed, available from the terminal for scripting, export, or headless monitoring.
`activity list` and `activity issue` are read-only. `activity create` is the one mutating command here, and you will rarely need it — the server already writes activity entries automatically as a side effect of normal work (issue updates, document locks, recovery resolutions). Use `activity create` only when you have an external event that genuinely belongs in the company's trail.
All three subcommands accept the [common client options](./common-options.md) (`--api-base`, `--api-key`, `--context`, `--profile`, `--data-dir`, `--json`). `activity list` and `activity create` are company-scoped and require `--company-id`; `activity issue` resolves the company from the issue itself.
> **Tip:** Add `--json` whenever you are scripting against these commands or piping the output into another tool. Without it, `list` prints one compact inline record per line for fast human scanning.
---
## activity list
List the activity log entries for a company.
```sh
paperclipai activity list --company-id <company-id>
paperclipai activity list --company-id <company-id> --agent-id <agent-id>
paperclipai activity list --company-id <company-id> --entity-type issue --entity-id <issue-id>
```
| Flag | Use |
|---|---|
| `-C, --company-id <id>` | **Required.** The company whose activity log you want. |
| `--agent-id <id>` | Filter to entries attributed to a single agent. |
| `--entity-type <type>` | Filter to one entity type, such as `issue`. |
| `--entity-id <id>` | Filter to one specific entity by id. |
The filters combine: pass `--entity-type issue --entity-id <issue-id>` to scope the log to a single issue, or add `--agent-id` on top to see only what one agent did to it.
Each entry in the default (non-JSON) output is a single inline record exposing the fields you most often pivot on:
| Field | Meaning |
|---|---|
| `id` | The activity entry's own id. |
| `action` | What happened, such as `issue.updated` or `issue.document_locked`. |
| `actorType` | Who acted — for example an agent, a user, or the system. |
| `actorId` | The id of the actor. |
| `entityType` | The kind of object the action touched. |
| `entityId` | The id of the object the action touched. |
| `createdAt` | When the entry was recorded. |
When there are no matching entries, the command prints an empty result rather than failing. Use `--json` to get the full entry objects, including any payload the server stored.
---
## activity create
Create an activity log entry on a company. The body is supplied as a raw JSON string that the server validates as a `CreateActivity` payload.
```sh
paperclipai activity create --company-id <company-id> \
--payload-json '{"action":"deploy.completed","entityType":"project","entityId":"<project-id>"}'
```
| Flag | Use |
|---|---|
| `-C, --company-id <id>` | **Required.** The company to record the entry against. |
| `--payload-json <json>` | **Required.** A JSON string describing the activity entry. |
The CLI parses `--payload-json` with `JSON.parse` before sending it, so it must be syntactically valid JSON — wrap it in single quotes in your shell so inner double quotes survive. The server is the authority on which fields a `CreateActivity` payload accepts; an invalid shape comes back as a request error, which the CLI surfaces directly.
> **Note:** You almost never need this command for normal operations. Paperclip writes activity entries on its own whenever issues, documents, and recovery actions change. Reserve `activity create` for stitching an external event — a deploy, an incident, a manual milestone — into the same audit trail.
---
## activity issue
List the activity for a single issue, identified by its id or human identifier (such as `PAP-39`).
```sh
paperclipai activity issue <issue-id>
paperclipai activity issue PAP-39 --json
```
This command takes the issue as a positional argument and resolves the rest of its context (including the company) from your selected profile and the issue itself, so it does **not** require `--company-id`. It is the fastest way to answer "what has happened to this one task?" without filtering the whole company log.
`activity issue <id>` and `activity list --company-id <id> --entity-type issue --entity-id <id>` overlap, but reach for `activity issue` when you already have an issue identifier in hand — it is shorter and it accepts the human `PAP-39` form directly.
---
## See also
- [Issue Commands](issue.md) — create, update, comment on, and check out the tasks whose history shows up here.
- [Dashboard Commands](dashboard.md) — the same top-level company state the UI surfaces, alongside the activity feed.
- [Common Options](./common-options.md) — the shared flags every client command accepts.
- [Output and Scripting](./output-and-scripting.md) — how `--json` output is shaped for piping into other tools.
+135
View File
@@ -0,0 +1,135 @@
---
paperclip_version: v2026.609.0
---
# Adapter Commands
Adapters are the server-side runtimes that actually execute an agent's work — they wrap Claude, Codex, or another provider and turn a wakeup into a real run. The CLI does not run an adapter; it inspects and administers the adapters the Paperclip runtime already has registered. Use these commands when you need to see what adapter types exist, read an adapter's config schema before hiring an agent against it, list the models a company can use, or install, update, and remove external adapter packages.
> **Note:** Most agent work in Paperclip is executed server-side by the runtime and its adapters. The CLI triggers and observes that work — see [What runs where](../../guides/welcome/key-concepts.md). The `adapter` commands let a board operator look under the hood of that execution layer.
---
## Adapter Types
Every adapter is addressed by its **type** — a stable string like `codex_local` or `claude_local`. You pass the type as the first argument to almost every subcommand. The exception is `list`, which enumerates all registered types so you know which ones are valid, and `install`, which registers a new external adapter from a package.
When you create an agent you set its `adapterType`, so reading an adapter's config schema first (see below) tells you exactly which fields that agent's `payload-json` accepts. See [Agent Commands](./agent.md) for the hire flow.
---
## Inspect Adapters
These read-only commands answer "what is installed and how is it configured?". They are not company-scoped — they describe the instance-wide adapter registry.
```sh
paperclipai adapter list
paperclipai adapter get <adapter-type>
paperclipai adapter config-schema <adapter-type>
paperclipai adapter ui-parser <adapter-type>
```
| Command | What it returns |
|---|---|
| `adapter list` | Every registered adapter, built-in and external. Start here to discover valid types. |
| `adapter get <type>` | The full record for one adapter — its current settings, install source, and status. |
| `adapter config-schema <type>` | The JSON schema for that adapter's configuration. Read this before writing an agent or adapter `payload-json` so you know the accepted fields. |
| `adapter ui-parser <type>` | The adapter's UI parser JavaScript — the client-side code the UI uses to render the adapter's run output. Useful when debugging how a run is displayed. |
```sh
# Discover what's installed, then read one adapter's schema
paperclipai adapter list --json
paperclipai adapter config-schema codex_local --json
```
> **Tip:** Pipe `config-schema` through `--json` and into your tooling. The schema is the contract for the `--payload-json` you send to `adapter update`, `adapter override`, and `agent create`.
---
## List Models for a Company
`adapter models` is the one inspection command that is **company-scoped** — the set of usable models depends on the company's provider credentials and configuration. It requires a company context, either from your selected profile or an explicit `--company-id`.
```sh
paperclipai adapter models <adapter-type> --company-id <company-id>
paperclipai adapter models codex_local --company-id <company-id> --refresh
paperclipai adapter models codex_local --company-id <company-id> --environment-id <environment-id>
```
| Flag | Use |
|---|---|
| `-C, --company-id <id>` | The company whose model list you want. Required unless a company is set in your context profile. |
| `--refresh` | Re-fetch the provider's model list instead of returning the cached set. Use this after rotating keys or when a newly released model is missing. Defaults to off. |
| `--environment-id <id>` | Scope the lookup to a specific environment for environment-aware adapters. |
Two related company-scoped reads sit alongside `models`:
```sh
paperclipai adapter model-profiles <adapter-type> --company-id <company-id>
paperclipai adapter detect-model <adapter-type> --company-id <company-id>
```
`model-profiles` lists the adapter's configured model profiles for the company; `detect-model` asks the adapter to resolve which model it would actually use. Both take `-C, --company-id <id>` and require a company context.
---
## Install and Remove External Adapters
External adapters ship as packages. `install` registers one with the runtime; `delete` removes that registration. Built-in adapters cannot be deleted — use `override` to pause them instead (below).
```sh
paperclipai adapter install --payload-json '{"packageName":"@scope/adapter","version":"1.2.3"}'
paperclipai adapter delete <adapter-type>
```
| Command | Notes |
|---|---|
| `adapter install` | Requires `--payload-json` describing the package to register (for example `packageName` and `version`). |
| `adapter delete <type>` | Removes an **external** adapter registration. |
> **Warning:** `adapter delete` removes the adapter from the instance. Any agent whose `adapterType` points at it will no longer have a runtime to execute against. Confirm nothing depends on the type — check `agent list` per company — before you delete.
---
## Configure and Control Adapters
These commands change adapter state. `update` and `override` send a JSON patch via `--payload-json` (required). `reload` and `reinstall` take an optional `--payload-json` that defaults to `{}`.
```sh
paperclipai adapter update <adapter-type> --payload-json '{"disabled":true}'
paperclipai adapter override <adapter-type> --payload-json '{"paused":true}'
paperclipai adapter reload <adapter-type>
paperclipai adapter reinstall <adapter-type>
```
| Command | Use | Payload |
|---|---|---|
| `adapter update <type>` | Update an adapter's settings. Validate your patch against `config-schema` first. | `--payload-json` required |
| `adapter override <type>` | Pause or resume a **built-in** adapter. This is how you disable a built-in without deleting it. | `--payload-json` required |
| `adapter reload <type>` | Reload the adapter in place — re-reads its configuration without reinstalling. | `--payload-json` optional, defaults to `{}` |
| `adapter reinstall <type>` | Reinstall the adapter from its source. Use when a `reload` is not enough and you need a clean install. | `--payload-json` optional, defaults to `{}` |
To validate an adapter's environment configuration before relying on it, use the company-scoped `test-environment`:
```sh
paperclipai adapter test-environment <adapter-type> --company-id <company-id> --payload-json '{}'
```
`test-environment` requires a company context (`-C, --company-id <id>`) and accepts an optional `--payload-json` (defaults to `{}`) carrying the environment configuration to test.
---
## Common Options
Every `adapter` subcommand accepts the standard client options: `--data-dir <path>`, `--api-base <url>`, `--api-key <token>`, `--context <path>`, `--profile <name>`, and `--json`. The company-scoped subcommands — `models`, `model-profiles`, `detect-model`, and `test-environment` — additionally take `-C, --company-id <id>` and require a resolvable company. See [Common Options](./common-options.md) for how API base and context resolution work, and [Output and Scripting](./output-and-scripting.md) for `--json` usage.
> **Note:** Adapter administration is a board-operator activity. An agent-scoped credential is bound to a single company and agent and is not the right persona for installing or overriding instance-wide adapters.
---
## See also
- [Agent Commands](./agent.md) — set an agent's `adapterType` and `payload-json` using the schema you read here
- [What runs where](../../guides/welcome/key-concepts.md) — why adapters execute server-side and the CLI only triggers and observes
- [Common Options](./common-options.md) — shared client flags and API base resolution
- [Output and Scripting](./output-and-scripting.md) — working with `--json` output
+276
View File
@@ -0,0 +1,276 @@
---
paperclip_version: v2026.609.0
---
# Agent Commands
The `agent` command group is how you create, inspect, configure, and steer the AI workers that do a company's work. Reach for these commands when you need to hire an agent, hand it a budget and permissions, tune its instructions and skills, wake it to run, or pause and terminate it. They are also the most direct way to wire up a local Claude or Codex session that acts *as* one of your agents — see [`agent local-cli`](#agent-local-cli) for that.
Remember the execution model: an agent's actual reasoning, coding, and research runs **server-side** in the Paperclip runtime and its adapters. These commands trigger and observe that work over the API; the CLI never runs the model itself. The single exception is `agent local-cli`, which sets up your shell so a human or AI can run Claude/Codex locally as that agent and report results back through the API.
All commands accept the common client flags: `--data-dir <path>`, `--api-base <url>`, `--api-key <token>`, `--context <path>`, `--profile <name>`, and `--json`. Company-scoped commands also take `-C, --company-id <id>`. See [Common Options](./common-options.md) for how those resolve.
---
## Identity and inbox
These commands read from the perspective of the *current* credential — the agent your API key or profile is scoped to.
```sh
paperclipai agent me
paperclipai agent inbox
paperclipai agent inbox-mine --user-id <board-user-id> --status todo,in_progress
```
| Command | Use |
|---|---|
| `agent me` | Show the current agent identity (`GET /api/agents/me`). The fastest way to confirm which agent a key or profile resolves to. |
| `agent inbox` | List the current agent's assigned inbox items — a compact view of issues awaiting it. |
| `agent inbox-mine --user-id <id>` | List the current agent's inbox items touched or archived by a specific board user. `--status <csv>` narrows by issue status (for example `todo,in_progress`). |
> **Note:** `agent me` and `agent inbox` operate on whoever the credential *is*. With an agent API key they describe that agent. With a board credential they describe the board's own identity, which is not what you usually want — pass an explicit agent context first.
---
## Listing and reading agents
```sh
paperclipai agent list --company-id <company-id>
paperclipai agent get <agent-id>
```
`agent list` requires `-C, --company-id <id>` and returns every agent in the company with its role, status, who it reports to, and its monthly budget versus monthly spend (in cents). `agent get <agentId>` returns one full agent record. Use `--json` on either when you are scripting.
---
## Creating and hiring agents
There are two ways to bring an agent into existence, and they are not the same.
```sh
paperclipai agent create --company-id <company-id> \
--payload-json '{"name":"Builder","adapterType":"codex_local"}'
paperclipai agent hire --company-id <company-id> \
--payload-json '{"name":"CTO","role":"cto","reason":"Lead engineering"}'
```
| Command | Use |
|---|---|
| `agent create` | Create an agent directly from a `CreateAgent` JSON payload (`POST /api/companies/{id}/agents`). Use this when you have board authority and want the agent to exist immediately. |
| `agent hire` | Create an agent *hire request* (`POST /api/companies/{id}/agent-hires`). Use this when hiring is governed and must pass through the board approval flow rather than landing as a live agent. |
Both require `-C, --company-id <id>` and `--payload-json <json>`. The JSON is validated before it is sent, so a malformed payload fails locally with a clear error instead of a server round-trip.
> **Tip:** Building the payload by hand is error-prone. The `paperclip-create-agent` skill inspects adapter options and drafts a valid config for you. The board can review and resolve a hire request with the [approval](./approval.md) commands.
---
## Updating and deleting agents
```sh
paperclipai agent update <agent-id> --payload-json '{"title":"Senior Builder"}'
paperclipai agent delete <agent-id> --yes
```
`agent update` patches an agent from an `UpdateAgent` JSON payload. `agent delete` removes an agent and **refuses to run without `--yes`** — there is no other confirmation, so treat the flag as the safety catch it is.
---
## Lifecycle controls
These commands move an agent through its operational states. Each takes a single `<agentId>` argument and POSTs to the matching agent endpoint.
```sh
paperclipai agent pause <agent-id>
paperclipai agent resume <agent-id>
paperclipai agent approve <agent-id>
paperclipai agent terminate <agent-id>
```
| Command | Use |
|---|---|
| `agent pause` | Pause an agent so it stops being woken for new work. |
| `agent resume` | Resume a paused agent. |
| `agent approve` | Approve a pending agent — for example one created by a hire request awaiting board sign-off. |
| `agent terminate` | Terminate an agent. Use this to retire a worker rather than deleting its record. |
---
## Waking an agent
`agent wake` is how you request a heartbeat wakeup so the server runs the agent's adapter now instead of waiting for the scheduler. It accepts an agent ID *or* a shortname/url-key; when you pass a shortname, supply `-C, --company-id <id>` so the server can resolve it.
```sh
paperclipai agent wake <agent-id-or-shortname> \
--company-id <company-id> \
--reason "Pick up the new spec" \
--payload '{"issueId":"<issue-id>"}'
```
| Flag | Use |
|---|---|
| `--source <source>` | Invocation source: `timer`, `assignment`, `on_demand`, or `automation`. Defaults to `on_demand`. |
| `--trigger <trigger>` | Trigger detail: `manual`, `ping`, `callback`, or `system`. Defaults to `manual`. |
| `--reason <text>` | Human-readable reason for the wakeup. |
| `--payload <json>` | A JSON **object** passed to the run. Anything that is not a JSON object is rejected. |
| `--idempotency-key <key>` | Dedupe key so a re-sent wakeup does not double-fire. |
| `--force-fresh-session` | Request a fresh adapter session instead of resuming the existing one. |
The command resolves the agent, then POSTs to `/api/agents/{id}/wakeup`. The server runs the adapter; the CLI prints the wakeup response. To stream and inspect the resulting run, use the [`run`](./run.md) subcommands.
> **Note:** `agent wake`, `agent heartbeat:invoke`, and the top-level `heartbeat run` all converge on the same server-side wakeup path. `agent wake` is the richest of the three because it exposes source, trigger, payload, idempotency, and fresh-session controls.
---
## Heartbeat and Claude login
```sh
paperclipai agent heartbeat:invoke <agent-id>
paperclipai agent claude-login <agent-id>
```
| Command | Use |
|---|---|
| `agent heartbeat:invoke` | Invoke an agent heartbeat directly (`POST /api/agents/{id}/heartbeat/invoke`). A plain, parameter-free wakeup — reach for `agent wake` when you need source, payload, or idempotency control. |
| `agent claude-login` | Trigger the Claude login flow for an agent that uses a Claude-backed adapter (`POST /api/agents/{id}/claude-login`). Use this when an agent's Claude credential needs to be (re)established server-side. |
---
## Permissions
```sh
paperclipai agent permissions:update <agent-id> \
--payload-json '{"canCreateAgents":true,"canAssignTasks":true}'
```
`permissions:update` patches an agent's scoped permissions from an `UpdateAgentPermissions` JSON payload (`PATCH /api/agents/{id}/permissions`). This controls what governed actions the agent may take — for example creating other agents or assigning tasks — independent of its budget and instructions.
---
## Configuration and revisions
Agent configuration is versioned. You can read the current redacted config, browse its revision history, inspect a single revision, and roll back to one.
```sh
paperclipai agent configuration <agent-id>
paperclipai agent config-revisions <agent-id>
paperclipai agent config-revision:get <agent-id> <revision-id>
paperclipai agent config-revision:rollback <agent-id> <revision-id>
```
| Command | Use |
|---|---|
| `agent configuration` | Get the redacted agent configuration. Secrets are masked, so this is safe to print and share. |
| `agent config-revisions` | List the config revision history for an agent. |
| `agent config-revision:get` | Fetch one revision by `<revision-id>` to see exactly what changed. |
| `agent config-revision:rollback` | Roll the agent back to a prior revision (`POST .../config-revisions/{id}/rollback`). Use this to recover quickly from a bad config change. |
---
## Runtime state and task sessions
These commands look at the *live* execution side of an agent — its current runtime session and the task sessions it is running.
```sh
paperclipai agent runtime-state <agent-id>
paperclipai agent runtime-state:reset-session <agent-id> --task-key <key>
paperclipai agent task-sessions <agent-id>
```
| Command | Use |
|---|---|
| `agent runtime-state` | Get the agent's current runtime state. |
| `agent runtime-state:reset-session` | Reset the agent's runtime session. Pass `--task-key <key>` to reset one specific task session; omit it to reset the agent's session broadly. Use this when an agent is wedged on stale session state. |
| `agent task-sessions` | List the agent's task sessions. |
---
## Skills
An agent's *desired* skill set is a server-side, desired-state field. These commands read and replace it. They do **not** install anything onto your local machine — that is what `agent local-cli` does.
```sh
paperclipai agent skills <agent-id>
paperclipai agent skills:sync <agent-id> --desired-skills paperclip,github
```
| Command | Use |
|---|---|
| `agent skills` | List the agent's current skills. |
| `agent skills:sync` | Replace the agent's desired skills with the comma-separated `--desired-skills` list (`POST /api/agents/{id}/skills/sync`). Required Paperclip skills stay server-enforced regardless. |
> **Note:** Stocking a company's skill *library* and authoring skills is a separate concern — see the [skills](./skills.md) reference. `agent skills:sync` only chooses which library skills a given agent should use.
---
## Instructions
An agent's instructions can be a single path on disk (for adapters that read a file like `AGENTS.md`) or a managed bundle of files stored server-side. These commands cover both.
```sh
paperclipai agent instructions-path:update <agent-id> \
--payload-json '{"path":"/tmp/AGENTS.md","adapterConfigKey":"instructionsFilePath"}'
paperclipai agent instructions-bundle <agent-id>
paperclipai agent instructions-bundle:update <agent-id> --payload-json '{"mode":"managed"}'
paperclipai agent instructions-file:get <agent-id> --path AGENTS.md
paperclipai agent instructions-file:put <agent-id> --path AGENTS.md --content-file ./AGENTS.md
paperclipai agent instructions-file:delete <agent-id> --path AGENTS.md
```
| Command | Use |
|---|---|
| `agent instructions-path:update` | Point the agent at an instructions file path. Process adapters require `adapterConfigKey` in the payload, and relative paths require `adapterConfig.cwd`. |
| `agent instructions-bundle` | Get the agent's managed instructions bundle. |
| `agent instructions-bundle:update` | Update the bundle from an `UpdateAgentInstructionsBundle` JSON payload — for example switching `mode` to `managed`. |
| `agent instructions-file:get` | Read one bundle-relative file. `--path <path>` is required. |
| `agent instructions-file:put` | Create or update one bundle-relative file. Provide content inline with `--content <text>` or from disk with `--content-file <path>`; add `--clear-legacy-prompt-template` to drop an old prompt template in the same write. |
| `agent instructions-file:delete` | Delete one bundle-relative file by `--path`. |
> **Tip:** Prefer `--content-file` over `--content` for anything multi-line. It keeps shell quoting out of the way and lets you version the instructions file in your own repo.
---
## `agent local-cli`
This is the practical bridge for running Claude or Codex **locally** as a Paperclip agent. It accepts an agent ID or shortname/url-key, requires `-C, --company-id <id>`, and in one shot:
1. Mints a long-lived agent API key (label it with `--key-name <name>`; defaults to a timestamped `local-cli` key).
2. Installs the bundled Paperclip skills into `~/.codex/skills` and `~/.claude/skills` as symlinks (respecting `CODEX_HOME` / `CLAUDE_HOME` if set).
3. Prints the shell `export` lines for `PAPERCLIP_API_URL`, `PAPERCLIP_COMPANY_ID`, `PAPERCLIP_AGENT_ID`, and `PAPERCLIP_API_KEY`.
```sh
paperclipai agent local-cli <agent-id-or-shortname> --company-id <company-id>
paperclipai agent local-cli codexcoder --company-id <company-id> --key-name workstation
paperclipai agent local-cli claudecoder --company-id <company-id> --no-install-skills
```
| Flag | Use |
|---|---|
| `-C, --company-id <id>` | Required. The company the agent belongs to (also used to resolve a shortname). |
| `--key-name <name>` | Label for the created API key. Defaults to `local-cli`; an empty value falls back to a timestamped name. |
| `--no-install-skills` | Skip installing skills into `~/.codex/skills` and `~/.claude/skills`. Use this when you only want the key and exports. |
Source the printed exports into your shell, then launch Codex or Claude — they now act as that agent against your Paperclip API:
```sh
eval "$(paperclipai agent local-cli codexcoder --company-id <company-id> --json | jq -r .exports)"
```
> **Warning:** The minted API key's plaintext token is shown **once**, in the command output. The exports embed it directly. Treat the output as a secret, and revoke the key with `token agent revoke` when the local session is done. See [Authentication](./authentication.md).
When skill installation runs, the command prints a per-tool summary (`linked` / `removed` / `skipped` / `failed` counts and the target directory). Broken symlinks are repaired and maintainer-only skills are removed, so re-running `local-cli` is safe and idempotent. Installation requires the bundled `skills` directory to be present in the checkout; if it cannot be found, the command fails rather than installing nothing silently.
---
## See also
- [Authentication](./authentication.md) — board tokens, agent keys, and how `connect` mints persona-aware profiles
- [Common Options](./common-options.md) — shared flags and API base resolution
- [Run](./run.md) — inspect and control the heartbeat runs that `agent wake` triggers
- [Prompt](./prompt.md) — hand work to an agent as a task-and-comment via `agent prompt`
- [Skills](./skills.md) — browse the catalog, stock the company library, and author skills
- [Approval](./approval.md) — resolve hire requests and other board-gated actions
- [Company](./company.md) — the company context every company-scoped agent command needs
+186
View File
@@ -0,0 +1,186 @@
---
paperclip_version: v2026.609.0
---
# Approval Commands
Use these commands when an agent has hit a board-gated action — hiring another agent, locking in a CEO strategy — and a human (or board-authorized automation) needs to decide. Approvals are the governance layer that sits between what an agent *wants* to do and what it is *allowed* to do. The `approval` command group lets you list the queue, inspect a single request, create one, render a decision (approve, reject, request revision), resubmit a revised request, and leave comments — all from the terminal instead of the Paperclip UI.
This is the same Approvals queue you work in the UI — these commands give you identical control from the terminal, which is useful for scripting, integrations, or operating headless. If you've handed the CLI to an AI operator, this is also how it raises and resolves approvals.
> **Note:** Approvals are decided with a **board** persona. An agent persona is scoped to one company and one agent and is the side that *requests* an approval; clearing the queue is board work.
---
## Command summary
| Command | What it does |
|---|---|
| `approval list` | List approvals for a company, optionally filtered by status. |
| `approval get` | Fetch one approval by ID. |
| `approval create` | Create an approval request for a governed action. |
| `approval approve` | Approve a pending request. |
| `approval reject` | Reject a request. |
| `approval request-revision` | Send the request back to the requester for changes. |
| `approval resubmit` | Resubmit a request, optionally with a new payload. |
| `approval comment` | Add a comment to an approval. |
Every subcommand accepts the [common client options](./common-options.md) (`--data-dir`, `--api-base`, `--api-key`, `--context`, `--profile`, `--json`). The company-scoped subcommands (`list`, `create`) additionally require `-C, --company-id`. Single-approval subcommands operate on an approval ID and do not need a company.
---
## List approvals
List the approvals attached to a company. This is the queue you triage.
```sh
paperclipai approval list --company-id <company-id>
paperclipai approval list --company-id <company-id> --status pending
```
| Flag | Use |
|---|---|
| `-C, --company-id <id>` | **Required.** The company whose approvals you want. |
| `--status <status>` | Filter the list to a single status (for example `pending`). |
Without `--json`, each row prints as a compact inline record showing the approval `id`, `type`, `status`, and the requesting agent or user. With `--json`, you get the raw array — use that when scripting or piping into another tool.
```sh
# Find everything still waiting on a decision, as JSON, for a script
paperclipai approval list --company-id <company-id> --status pending --json
```
> **Tip:** Start every triage session with `approval list --status pending`. It is the shortest route from "what needs me" to a decision.
---
## Get one approval
Inspect a single approval in full before you decide on it.
```sh
paperclipai approval get <approval-id>
```
This is company-agnostic — the approval ID is globally addressable, so you do not pass `--company-id`. Pair it with `--json` when you want the complete payload (the proposed hire config, the strategy text, linked issue IDs) rather than a summary.
---
## Create an approval
Create a request for a governed action. Agents normally generate these as part of their work, but a board operator or automation can create one explicitly.
```sh
paperclipai approval create \
--company-id <company-id> \
--type hire_agent \
--payload '{"name":"CTO","role":"engineering_lead"}' \
--requested-by-agent-id <agent-id> \
--issue-ids <issue-id-1>,<issue-id-2>
```
| Flag | Use |
|---|---|
| `-C, --company-id <id>` | **Required.** The company the approval belongs to. |
| `--type <type>` | **Required.** The approval type: `hire_agent` or `approve_ceo_strategy`. |
| `--payload <json>` | **Required.** The approval payload as a JSON **object** (not an array or scalar). |
| `--requested-by-agent-id <id>` | The agent making the request. |
| `--issue-ids <csv>` | Comma-separated issue IDs to link to the approval. |
The `--payload` value is parsed as JSON and must be a JSON object — passing a list or a bare string is rejected with an `Invalid payload JSON` error. The `--issue-ids` value is split on commas, trimmed, and blank entries dropped.
> **Warning:** Quote the `--payload` value as a single shell argument and keep the JSON valid. A truncated or array-shaped payload fails before the request ever reaches the server.
---
## Decide an approval
Three subcommands record a decision on a pending approval. All three take the approval ID as an argument and accept the same two optional flags.
```sh
paperclipai approval approve <approval-id> --decision-note "Cleared budget; proceed with the hire."
paperclipai approval reject <approval-id> --decision-note "Out of scope for this quarter."
paperclipai approval request-revision <approval-id> --decision-note "Tighten the role to backend only, then resubmit."
```
| Flag | Use |
|---|---|
| `--decision-note <text>` | A short note explaining the decision. |
| `--decided-by-user-id <id>` | The user ID to attribute the decision to. |
| Command | When to reach for it |
|---|---|
| `approve` | The request is sound and you authorize the action. |
| `reject` | The request should not proceed at all. |
| `request-revision` | The request is close but needs changes — sends it back to the requester rather than killing it. |
`request-revision` is the productive middle ground: it keeps the request alive and signals exactly what to change, so the requester can fix it and resubmit instead of starting over.
> **Tip:** A short, concrete decision note is worth more than a long one. The requester should be able to act on your feedback without guessing — that is the entire point of `request-revision`.
---
## Resubmit a revised request
After a `request-revision`, the requester fixes the proposal and resubmits. You can resubmit as-is, or supply a corrected payload in the same step.
```sh
# Resubmit unchanged
paperclipai approval resubmit <approval-id>
# Resubmit with a corrected payload
paperclipai approval resubmit <approval-id> --payload '{"name":"CTO","role":"backend_lead"}'
```
| Flag | Use |
|---|---|
| `--payload <json>` | An optional replacement payload, as a JSON object. |
When you pass `--payload`, it is parsed and validated as a JSON object exactly like `approval create`; omit it to resubmit the existing payload untouched. This closes the request → revise → resubmit loop without re-creating the approval from scratch.
---
## Comment on an approval
Leave a comment on an approval to ask a clarifying question or record context without rendering a decision.
```sh
paperclipai approval comment <approval-id> --body "What is the projected monthly cost of this hire?"
```
| Flag | Use |
|---|---|
| `--body <text>` | **Required.** The comment text. |
Use comments to negotiate the details before you decide. Reserve `request-revision` for the moment you actually want the requester to change and resubmit the proposal.
---
## A typical triage loop
```sh
# 1. See what is waiting
paperclipai approval list --company-id <company-id> --status pending
# 2. Inspect the one that needs attention
paperclipai approval get <approval-id> --json
# 3a. If it's good, approve it
paperclipai approval approve <approval-id> --decision-note "Approved."
# 3b. If it needs work, send it back
paperclipai approval request-revision <approval-id> --decision-note "Narrow the scope, then resubmit."
# 4. The requester fixes and resubmits
paperclipai approval resubmit <approval-id> --payload '{"...":"..."}'
```
---
## See also
- [Common Options](./common-options.md) — the shared client flags (`--company-id`, `--api-base`, `--api-key`, `--json`, and the rest) accepted by every approval subcommand.
- [Output and Scripting](./output-and-scripting.md) — how `--json` output is shaped for pipelines.
- [Issue Commands](./issue.md) — link approvals to the work that motivated them with the issue approval link commands.
- [Agent Commands](./agent.md) — the agents that request hires and run the work an approval gates.
- [Activity](./activity.md) — the audit trail where approval decisions are recorded.
+100
View File
@@ -0,0 +1,100 @@
---
paperclip_version: v2026.609.0
---
# Asset Commands
Use these commands when you need to push image assets into a company from the terminal — a company logo, a documentation image, or any binary blob you later want to fetch back. The CLI uploads the file to the Paperclip API as `multipart/form-data` and prints the stored asset record; `asset content` is the inverse, streaming an asset's bytes back out by id. Reach for these when you are scripting company branding or bootstrapping content without opening the UI.
All three subcommands accept the [common client options](./common-options.md) (`--data-dir`, `--api-base`, `--api-key`, `--context`, `--profile`, `--json`). The two upload commands are company-scoped and take `-C, --company-id <id>`; `asset content` works on a global asset id and is not company-scoped.
---
## Upload an image
```sh
paperclipai asset image:upload \
--company-id <company-id> \
--file ./diagram.png \
--namespace docs \
--alt "Architecture diagram" \
--title "System overview"
```
`image:upload` posts the file to `/api/companies/<company-id>/assets/images` and returns the stored asset record (an asset id you can later pass to `asset content`).
| Flag | Use |
|---|---|
| `--file <path>` | **Required.** Path to the image file on disk. The CLI reads the bytes, infers the content type from the file extension, and sends the original filename. |
| `-C, --company-id <id>` | The company that owns the asset. Required unless resolved from your selected context/profile. |
| `--namespace <value>` | Optional namespace suffix that groups the asset under a logical bucket (for example `docs`). |
| `--alt <text>` | Optional alt-text metadata stored with the asset. |
| `--title <text>` | Optional title metadata stored with the asset. |
> **Tip:** The content type is inferred from the file path, so keep a correct extension (`.png`, `.svg`, `.jpg`) on the file you upload. A misnamed file will be stored with the wrong type.
---
## Upload a logo
```sh
paperclipai asset logo:upload \
--company-id <company-id> \
--file ./logo.svg
```
`logo:upload` posts the file to `/api/companies/<company-id>/logo` and sets the company's logo. It is the same multipart upload as `image:upload`, but it does **not** accept `--namespace`, `--alt`, or `--title` — a logo is a single, well-known slot on the company, so there is no metadata to attach.
| Flag | Use |
|---|---|
| `--file <path>` | **Required.** Path to the logo file. SVG and standard raster formats both work; the content type is inferred from the extension. |
| `-C, --company-id <id>` | The company whose logo you are setting. Required unless resolved from context/profile. |
> **Note:** This replaces whatever logo the company currently has. There is no separate "remove logo" subcommand here — upload a new file to change it.
---
## Download asset content
```sh
# Write the bytes to a file
paperclipai asset content <asset-id> --out ./asset.bin
# Or stream the bytes to stdout (pipe or redirect them)
paperclipai asset content <asset-id> > ./asset.bin
```
`asset content <assetId>` fetches the raw bytes from `/api/assets/<asset-id>/content`. By default it writes those bytes straight to stdout, which lets you pipe or redirect them. Pass `--out <path>` to have the CLI write the file for you instead — in that mode it prints a small confirmation object (`{ ok, out, bytes }`) rather than the binary.
| Flag | Use |
|---|---|
| `--out <path>` | Write the downloaded content to this file instead of stdout. When set, the command prints a confirmation summary including the byte count. |
> **Warning:** Without `--out`, the command writes raw binary to stdout. Always redirect or pipe it (`> file` or `| tool`) — printing binary directly into a terminal will garble your session. The `--json` flag has no effect in stdout-streaming mode; it only shapes the confirmation object emitted when you use `--out`.
---
## Scripting
Both uploads honor `--json`, so you can capture the returned asset id and feed it back into a download or another command:
```sh
ASSET_ID=$(paperclipai asset image:upload \
--company-id <company-id> \
--file ./diagram.png \
--json | jq -r '.id')
paperclipai asset content "$ASSET_ID" --out ./roundtrip.png
```
API base resolution follows the standard order (`--api-base`, then `PAPERCLIP_API_URL`, then your selected context profile, then the local config port, then `http://localhost:3100`). If a connection fails, the error names the URL it tried so you can confirm you are pointed at the right instance — see [common options](./common-options.md) and [output and scripting](./output-and-scripting.md) for the full conventions.
---
## See also
- [Common Options](./common-options.md) — the flags every client command shares, including `--company-id` and API base resolution
- [Output and Scripting](./output-and-scripting.md) — how `--json` shapes results and how to consume them
- [Skills Commands](./skills.md) — managing a company's skill library, another company-scoped file workflow
- [Adapter Commands](./adapter.md) — configuring the runtime adapters that execute agent work
- [Company Commands](./company.md) — inspecting and exporting the company that owns these assets
+325
View File
@@ -0,0 +1,325 @@
---
paperclip_version: v2026.609.0
---
# Authentication & Tokens
This is the identity reference for the Paperclip CLI: how the CLI knows *who it is*, *which instance it talks to*, and *what it is allowed to do*. Use these commands when you are connecting a new machine, switching between a board operator and an agent persona, minting credentials for headless automation, or rotating and revoking tokens. The short version: `connect` once interactively to get a working profile, then `context`, `auth`, and `token` to manage everything afterward.
Everything here resolves around two **personas** and two kinds of **credential**. Read the next section first — the rest of the page assumes you know the difference.
---
## Personas: board operator vs. agent
The CLI records, per profile, *which persona it is acting as*. The persona determines the scope of authority you get.
| Persona | Authority | Use it for |
| --- | --- | --- |
| **board** | Full board authorization across the entire instance — every company, every agent, all administrative reads and writes. | Operating the company yourself, hiring agents, creating other tokens, running setup and governance commands. |
| **agent** | Scoped to exactly **one company** and **one agent**. The credential can act only as that agent. | Letting a single agent (human or AI running locally) report work back through the API as itself. |
The persona is not just a label — it maps to a different credential type. A board persona carries a **board API token**; an agent persona carries an **agent API key**. You cannot widen an agent key into board authority; if you need board scope, you mint a board token.
| Credential | Scope | Lifecycle | Where it comes from |
| --- | --- | --- | --- |
| **Board API token** | Whole instance (board authority). | Named, optional expiry, server-side revocation, audited. | `token board create`, or the browser approval flow behind `auth login` / `connect`. |
| **Agent API key** | One company + one agent. | Named, plaintext shown **once** at creation, revocable. | `token agent create`, or `connect` with the agent persona. |
> **Warning:** Both credential types are shown in full **only at creation time**. The token string is printed once and never again. Capture it into a secret store or an environment variable immediately — there is no "show token" command.
For the conceptual model behind personas and how they fit the operating model, see [Personas and Tokens](../../administration/cli-auth.md).
---
## How the CLI finds your instance
Every client command resolves an API base in this exact order, stopping at the first one set:
1. `--api-base <url>` on the command
2. the `PAPERCLIP_API_URL` environment variable
3. the `apiBase` of the selected context profile
4. the local Paperclip config server port (when running against a local instance)
5. `http://localhost:3100`
When a connection fails, the error includes the URL that was attempted and a hint to check `GET /api/health` against it. If a command is hitting the wrong server, that error tells you which step won.
For the full list of flags shared by every client command (`--data-dir`, `--api-base`, `--api-key`, `--context`, `--profile`, `--json`), see [Common Options](./common-options.md).
---
## Context profiles
Profiles live in `~/.paperclip/context.json`. Each profile is **persona-aware** and records the API base, an optional default company, the persona, and (for agent profiles) the agent id and name. Crucially, a profile stores an **`apiKeyEnvVarName`** — the *name* of an environment variable that holds the token — not the token itself. Keep secrets in your environment or a secret manager; let the profile point at them.
### `context show`
Show the resolved current context and the active profile.
```sh
paperclipai context show
paperclipai context show --profile staging --json
```
The output includes the resolved `contextPath`, the `currentProfile` name, the resolved profile, and every profile in the store.
### `context list`
List all profiles as rows, marking which one is current.
```sh
paperclipai context list
```
Each row shows `name`, `current`, `apiBase`, `companyId`, `persona`, `agentId`, `agentName`, and `apiKeyEnvVarName`.
### `context use`
Switch the active profile.
```sh
paperclipai context use staging
```
### `context set`
Create or update a profile field by field. With no `--profile`, it edits the current profile (falling back to `default`). Pass `--use` to also make it active.
```sh
# Configure a board profile that reads its token from PAPERCLIP_API_KEY
paperclipai context set \
--profile prod \
--api-base https://paperclip.example.com \
--persona board \
--api-key-env-var-name PAPERCLIP_API_KEY \
--use
# Pin an agent profile to a company and agent
paperclipai context set \
--profile acme-bot \
--api-base https://paperclip.example.com \
--company-id <company-id> \
--persona agent \
--agent-id <agent-id> \
--agent-name "Acme Bot"
```
| Flag | Use |
| --- | --- |
| `--profile <name>` | Profile to edit (default: current profile, else `default`). |
| `--api-base <url>` | Default API base URL for the profile. |
| `--company-id <id>` | Default company id. |
| `--persona <persona>` | `board` or `agent`. Any other value is rejected. |
| `--agent-id <id>` | Default agent id (agent persona). |
| `--agent-name <name>` | Display name for the agent. |
| `--api-key-env-var-name <name>` | Name of the env var holding the token. Recommended over inlining a secret. |
| `--use` | Make this profile active after saving. |
> **Tip:** Build profiles non-interactively with `context set` on headless machines where the `connect` wizard cannot run. It writes the same persona-aware profile shape that `connect` produces.
---
## The connect wizard
`connect` is the interactive, fastest path to a working setup. It is **TTY-only** — running it without an interactive terminal fails with a message telling you to use `--api-base`/`--api-key` or the `context set`/`token` commands instead.
```sh
paperclipai connect
paperclipai connect --persona agent --token-name laptop-cli
```
The wizard walks through, in order:
1. **Resolve the API base** — prefilled from the resolution order above; you confirm or edit it.
2. **Health check** — it calls `GET <api-base>/api/health` and stops if the instance is unreachable.
3. **Board login** — it runs the board approval flow (see below) so it can read your companies and mint tokens.
4. **Pick the persona** — board operator, or agent in a company.
5. **Pick the company** (and, for an agent, the agent within it).
6. **Mint a token** — a board API key for a board profile, or an agent API key for an agent profile.
7. **Save a persona-aware profile** and set it active.
8. **Print shell exports** so you can use the new credential right away.
| Flag | Use |
| --- | --- |
| `--persona <persona>` | Skip the prompt: `board` or `agent`. |
| `--api-key-env-var-name <name>` | Env var name to record in the profile (default `PAPERCLIP_API_KEY`). |
| `--token-name <name>` | Label for the minted token (defaults to a timestamped `cli-board-…` / `cli-agent-…` name). |
The printed exports look like this (agent persona shown):
```sh
export PAPERCLIP_API_URL='https://paperclip.example.com'
export PAPERCLIP_COMPANY_ID='<company-id>'
export PAPERCLIP_AGENT_ID='<agent-id>'
export PAPERCLIP_API_KEY='<token-shown-once>'
```
A board profile omits `PAPERCLIP_AGENT_ID`, and omits `PAPERCLIP_COMPANY_ID` unless you chose a default company. Copy these into your shell or your secret store — this is the only time the token is shown.
> **Note:** The profile stores the *name* of the env var (`apiKeyEnvVarName`), not the token. The exports put the actual token into that env var. Together they let later commands authenticate without the secret ever touching `context.json`.
---
## Board authentication: `auth`
The `auth` commands manage the board-user credential for an API base. The credential is a board API token stored locally, keyed by the normalised `apiBase`, so one machine can hold separate credentials for local, staging, and production at the same time.
### `auth login`
Authenticate the CLI for board-user access via a browser-approval (device-code style) flow. The CLI creates a challenge, opens the approval URL in your browser, and polls until you approve, cancel, or it expires. On approval it confirms identity via `/api/cli-auth/me` and stores the board token for this `apiBase`.
```sh
paperclipai auth login
paperclipai auth login --instance-admin
paperclipai auth login --company-id <company-id>
paperclipai auth login --api-base https://paperclip.example.com
```
| Flag | Use |
| --- | --- |
| `--instance-admin` | Request instance-admin approval instead of plain board access. The approver must themselves be an instance admin. |
| `--company-id <id>` | Scope the requested access to a specific company. |
If you cancel in the browser, the CLI exits with `CLI auth challenge was cancelled.`; if you wait too long, `CLI auth challenge expired before approval.` Either way, just re-run the command.
### `auth whoami`
Show the current board-user identity for this API base. Calls `/api/cli-auth/me`.
```sh
paperclipai auth whoami --json
```
The result reports `user` (id, name, email), `userId`, `isInstanceAdmin`, the `companyIds` you can reach, the credential `source`, and the `keyId`.
### `auth logout`
Remove the stored board credential for this API base. It first attempts a server-side revoke of the stored token, then deletes the local entry. If there is no stored credential, it reports `revoked: false` and exits cleanly.
```sh
paperclipai auth logout
```
> **Note:** If the server-side revoke fails (network error, token already invalid), the local credential is still removed — you will not be left with a stale entry. Rotate by `logout` then `login`.
### `auth revoke-current`
Revoke the board API token currently in use, server-side, without touching the local store. Useful when you want to invalidate the active token from the server's perspective.
```sh
paperclipai auth revoke-current
```
### `auth challenge`
Low-level access to the CLI-auth challenge lifecycle, for scripting custom approval flows or automation. Most users never call these directly — `auth login` orchestrates them for you.
```sh
# Create a challenge from a JSON payload
paperclipai auth challenge create --payload-json '{"command":"ci-bot","requestedAccess":"board"}'
# Inspect, approve, or cancel a challenge (the secret is required)
paperclipai auth challenge get <challenge-id> --token <secret>
paperclipai auth challenge approve <challenge-id> --token-env CHALLENGE_TOKEN
paperclipai auth challenge cancel <challenge-id> --token <secret>
```
| Subcommand | Purpose |
| --- | --- |
| `create --payload-json <json>` | Create a challenge from a `CreateCliAuthChallenge` JSON payload (required). |
| `get <id>` | Read challenge status. |
| `approve <id>` | Approve a pending challenge. |
| `cancel <id>` | Cancel a pending challenge. |
For `get`, `approve`, and `cancel` you must supply the challenge secret with either `--token <secret>` or `--token-env <name>` (read from the named environment variable). If neither is given, the command errors.
---
## Named tokens: `token`
Where `auth login` is the interactive, locally-stored board credential, `token` mints **named, manageable** credentials you can hand to automation, CI, or a specific agent. Board tokens and agent keys are managed under separate subtrees.
### Board tokens
Board API keys carry full board authority. They are named, can carry an expiry, are revocable, and are audited server-side. Creating board tokens requires an existing board credential (the wizard or `auth login` gets you one).
```sh
# Long-lived board token for an external admin tool
paperclipai token board create --name external-admin
# Short-lived token that expires in 7 days
paperclipai token board create --name short-lived --ttl-days 7
# Explicit expiry timestamp, with audit company context
paperclipai token board create --name release-bot --company-id <company-id> --expires-at 2026-12-31T00:00:00Z
# Non-expiring token (be deliberate about this)
paperclipai token board create --name forever --never-expires
paperclipai token board list
paperclipai token board revoke <key-id>
```
| Flag | Use |
| --- | --- |
| `-C, --company-id <id>` | Company id recorded as audit context for the key. |
| `--name <name>` | Key label (default `cli-board`). |
| `--expires-at <iso8601>` | Explicit expiration timestamp. |
| `--ttl-days <days>` | Expiration N days from now. |
| `--never-expires` | Create a non-expiring key. |
`--never-expires` wins over the other expiry flags; otherwise `--expires-at` and `--ttl-days` set the lifetime. With none of them, the server applies its default. `token board list` shows each key's `id`, `name`, `createdAt`, `lastUsedAt`, `expiresAt`, and `revokedAt` for the current board user.
### Agent keys
Agent API keys are scoped to one company and one agent. The agent is resolved by id, shortname, or unambiguous name within the company. Both `--company-id` and `--agent` are required.
```sh
paperclipai token agent create --company-id <company-id> --agent <agent-id-or-name> --name external-worker
paperclipai token agent list --company-id <company-id> --agent <agent-id-or-name>
paperclipai token agent revoke --company-id <company-id> --agent <agent-id-or-name> <key-id>
```
| Flag | Use |
| --- | --- |
| `-C, --company-id <id>` | Company id (required). |
| `--agent <agent>` | Agent id, shortname, or unambiguous name (required). |
| `--name <name>` | Key label, on `create` only (default `cli-agent`). |
The created key's `token` is in the response and is shown only once. `token agent list` reports each key's `id`, `name`, `createdAt`, and `revokedAt`.
> **Tip:** If you want the full local setup for running Claude or Codex as an agent — a long-lived agent key plus skills installed into `~/.codex/skills` and `~/.claude/skills` plus ready-to-paste exports — use `agent local-cli <agent>` instead of minting a bare key by hand. See [Agents](./agent.md).
---
## Putting it together
A typical sequence on a fresh machine:
```sh
# 1. One interactive setup creates a profile and prints exports
paperclipai connect
# 2. Verify who you are
paperclipai auth whoami
# 3. Mint a named token for CI (board scope) or an agent
paperclipai token board create --name ci --ttl-days 30
paperclipai token agent create --company-id <company-id> --agent <agent-id> --name worker
# 4. Switch between saved profiles as needed
paperclipai context use prod
```
For headless servers where `connect` cannot run, replace step 1 with `auth login` (or `auth bootstrap-ceo` / a board claim on a fresh authenticated instance) plus `context set`, then proceed identically.
---
## See also
- [Common Options](./common-options.md) — the flags every client command shares, including API base resolution.
- [Personas and Tokens](../../administration/cli-auth.md) — the conceptual model behind board vs. agent identity.
- [Agents](./agent.md) — `agent local-cli` and agent management.
- [Setup Commands](./setup-commands.md) — bootstrapping and configuring an instance.
- [Install and Connect](../../guides/getting-started/installation.md) — the guided first-run walkthrough.
- [Headless Bootstrap](./installation.md) — minting the first credential with no browser.
+166
View File
@@ -0,0 +1,166 @@
---
paperclip_version: v2026.609.0
---
# Cloud Sync Commands
Use these commands when you want to push a company that lives on your local Paperclip instance up into a Paperclip Cloud stack — the upstream "sync" model where your laptop or self-hosted instance is the source and the cloud stack is the target. There are exactly two commands: `cloud connect` authorizes this instance against a cloud stack, and `cloud push` exports a local company and applies (or previews) it upstream. Cloud sync is experimental and gated behind an instance setting, so reach for it only when you have deliberately turned it on.
> **Warning:** Cloud sync is an experimental, one-directional model: local pushes to cloud. `cloud push` is refused unless the `enableCloudSync` experimental setting is turned on for your local instance. There is no `pull` command — the cloud stack does not push back down to you.
---
## How the upstream model works
The sync flow has two participants. Your **local instance** holds the company you author and operate. The **cloud stack** is a Paperclip Cloud target identified by a remote URL. Pushing copies a company's portable export — the company manifest, agents, projects, issues, skills, and their markdown-backed files — up into the stack's target company.
The mechanics, in order:
1. **Discovery.** The CLI fetches `/.well-known/paperclip-upstream` from the remote URL. The stack must advertise the `paperclip-upstream-discovery-v1` schema, a transfer schema major version that matches your CLI, and the `cloud_sync` feature flag. If any of those don't line up, the command stops before touching anything.
2. **Authorization.** `cloud connect` mints a per-instance Ed25519 key pair and obtains an access token scoped to `upstream_import:preview`, `upstream_import:read`, and `upstream_import:write`. The connection is stored locally so later pushes can reuse it.
3. **Push.** `cloud push` exports the named local company, builds an upstream transfer bundle, and either previews or applies it against the stack. Every upstream request is signed with the connection's private key (a proof header over method, host, path, token id, source instance id, timestamp, and a nonce), so the stack can verify the push genuinely came from your registered instance.
Because the bundle is keyed by a content hash of the export, repeated pushes of an unchanged company are idempotent: the coordinator can `adopt` or `skip` entities that already exist upstream rather than duplicating them.
---
## `cloud connect`
Authorize this local instance to push into a Paperclip Cloud stack. Run this once per stack before you can push.
```sh
paperclipai cloud connect https://acme.paperclip.cloud
```
This runs discovery against the URL, then walks an authorization flow. By default it opens a browser using a PKCE flow with a loopback callback. If a browser is not available — or you pass `--no-browser` — it falls back to the device-code flow, printing a verification URL and a one-time user code you enter on another device.
```sh
# Headless box: skip the browser and use the device-code flow
paperclipai cloud connect https://acme.paperclip.cloud --no-browser
```
On success the connection is saved locally (keyed by the stack's origin) and the command prints the stack, origin, and target company id. With `--json` it prints a redacted connection record — id, remote URL, target origin, stack id, target company id, granted scopes, and token expiry — but never the access token or private key.
### Arguments and options
| Argument / Flag | Use |
|---|---|
| `<remote-url>` | Required. The Paperclip Cloud stack URL to authorize against. |
| `--no-browser` | Use the device-code flow instead of opening a browser. Defaults to off. |
> **Note:** Browser authorization is attempted only when a browser can actually be opened (macOS, Windows, or a Linux session with `DISPLAY`/`WAYLAND_DISPLAY`) and the stack advertises a PKCE endpoint. If the browser leg fails, the CLI prints a warning and automatically retries with the device-code flow.
---
## `cloud push`
Preview or apply a local company push into the connected cloud stack. This is the command you run after `cloud connect` whenever you want the upstream stack to reflect your local company.
```sh
# Preview first — never apply blind
paperclipai cloud push --company <local-company-id> --dry-run
# Apply once the preview looks right
paperclipai cloud push --company <local-company-id>
```
The push targets the stored connection for the company's stack. If you have connected more than one stack, select the right one with `--remote-url`:
```sh
paperclipai cloud push --company <local-company-id> --remote-url https://acme.paperclip.cloud
```
Before doing any work, `cloud push` checks your local instance's experimental settings and refuses to run unless `enableCloudSync` is `true`. It then re-runs discovery (the stack must still advertise a compatible schema and the `cloud_sync` flag), exports the company, and runs the coordinator in `preview` or `apply` mode.
### Options
| Flag | Use |
|---|---|
| `--company <local-company-id>` | Required. The local company id to export and push. |
| `--remote-url <remote-url>` | Use a specific stored cloud connection. Omit it when you have a single connection. |
| `--dry-run` | Preview the push without applying anything upstream. Defaults to off. |
| `--max-entities-per-chunk <count>` | Chunk size for upstream uploads. Defaults to `100`. |
> **Tip:** Always run `--dry-run` first. The preview reports exactly what an apply would create, update, adopt, skip, or conflict on, so you can catch a stale mapping or a target conflict before it lands.
### Reading the output
Both modes print a one-line summary of per-entity outcomes, plus the run id and the manifest hash for the bundle:
```
Cloud Push Applied
run=<run-id>
manifest=<manifest-hash>
create=3 update=1 adopt=8 skip=0 conflict=0 staleMapping=0
```
| Outcome | Meaning |
|---|---|
| `create` | A new entity was created upstream. |
| `update` | An existing upstream entity was updated to match local. |
| `adopt` | An already-matching upstream entity was linked to the local source without rewriting it. |
| `skip` | The entity was unchanged and left as-is. |
| `conflict` | The upstream target entity could not be reconciled with the local source. |
| `staleMapping` | A previously recorded source→target mapping no longer points at a valid upstream entity. |
Any warnings from the local export are printed in yellow, conflicts in red (the first ten, then a truncation count), and — on apply — the last few run events. Use `--json` to capture the full result and run events for scripting.
### Exit codes
`cloud push` uses distinct exit codes so automation can branch on the outcome:
| Exit code | Condition |
|---|---|
| `0` | Push completed with no conflicts. |
| `2` | The push reported one or more conflicts (`conflict + staleMapping > 0`). The push still ran; resolve the conflicts and push again. |
| `3` | The upstream schema did not match your CLI's transfer schema. Upgrade so both ends agree before retrying. |
> **Note:** A conflict exit code (`2`) means the push was applied but some entities could not be reconciled — it is not a hard failure. Inspect the printed conflicts (or the `--json` output), fix the upstream or local state, and re-run. The content-hash idempotency means re-pushing an unchanged company will adopt or skip rather than duplicate.
---
## Enabling cloud sync
`cloud push` reads the instance experimental settings and aborts with a clear message if `enableCloudSync` is not `true`:
```
Cloud sync is disabled. Enable the cloud sync experimental setting before running `paperclipai cloud push`.
```
This flag lives on the local instance's experimental settings, not on the CLI. Turn it on deliberately — cloud sync moves company data off your instance into an external stack, so it is off by default. `cloud connect` does not require the flag (you can authorize ahead of time), but the first `push` will stop until it is enabled.
---
## Typical workflow
```sh
# 1. Authorize this instance against the cloud stack (once per stack)
paperclipai cloud connect https://acme.paperclip.cloud
# 2. Enable the enableCloudSync experimental setting on your local instance
# 3. Preview the push for the company you want to sync
paperclipai cloud push --company <local-company-id> --dry-run
# 4. Apply it
paperclipai cloud push --company <local-company-id>
```
If the preview surfaces conflicts (exit code `2`), reconcile them before applying. If it reports a schema mismatch (exit code `3`), upgrade the CLI or the stack so the transfer schema majors agree, then retry.
---
## Common flags
Both `cloud connect` and `cloud push` accept the standard client options that resolve the local API and authentication: `--data-dir <path>`, `--api-base <url>`, `--api-key <token>`, `--context <path>`, `--profile <name>`, and `--json`. These control how the CLI talks to your **local** instance (the source of the export). The upstream connection to the cloud stack is handled separately by the stored connection record and its signing key.
See [Common Options](./common-options.md) for the full resolution order and behavior of these flags.
---
## See also
- [Common Options](./common-options.md) — shared client flags and API base resolution
- [Output and Scripting](./output-and-scripting.md) — using `--json` and exit codes in automation
- [Company Commands](./company.md) — `company export` / `company import`, the local portability flow that backs the push bundle
- [Authentication](./authentication.md) — connecting and authenticating against your local instance
File diff suppressed because it is too large. Load diff
+164
View File
@@ -0,0 +1,164 @@
---
paperclip_version: v2026.609.0
---
# Common Options & Connection
Almost every Paperclip CLI command that talks to the control plane shares the same handful of connection flags, the same API base resolution rules, and the same context-profile mechanism. Read this page once and you will understand how every other reference page resolves `--api-base`, `--api-key`, and company scope — they all link back here instead of repeating it. Reach for this page when a command will not connect, authenticates as the wrong persona, or you want to stop retyping the same flags on every invocation.
---
## The shared client flags
Client commands register a common option set. These flags are accepted everywhere the CLI makes an API call:
| Flag | Use |
|---|---|
| `-d, --data-dir <path>` | Paperclip data directory root. Isolates all local state (config, context, db, logs, storage, secrets) away from `~/.paperclip`. Indispensable for clean test instances and per-worktree setups. |
| `--api-base <url>` | Base URL for the Paperclip API. Highest-priority override of where the CLI connects. |
| `--api-key <token>` | Bearer token for authenticated calls. Highest-priority credential — passing it disables interactive board-auth recovery (see below). |
| `--context <path>` | Path to the CLI context file to read and write. Overrides the default `~/.paperclip/context.json` lookup. |
| `--profile <name>` | Which context profile to use. Defaults to the context's current profile. |
| `-c, --config <path>` | Path to the Paperclip config file. Used only to infer a local server port when no other API base is given. |
| `--json` | Emit raw JSON instead of the human-readable inline format. See [Output & Scripting](output-and-scripting.md). |
Company-scoped commands add one more flag:
| Flag | Use |
|---|---|
| `-C, --company-id <id>` | Company ID. Overrides the profile's default company for this command. |
> **Note:** The short alias `-C` for `--company-id` is registered by commands that opt into company scope. Not every command is company-scoped, so check the per-command reference page if you are unsure whether it applies.
---
## How the API base is resolved
The CLI picks an API base by walking these sources in order and taking the first non-empty one. The resolved value is then normalized (trailing slashes stripped):
1. `--api-base <url>` on the command line
2. The `PAPERCLIP_API_URL` environment variable
3. The selected context profile's `apiBase`
4. A locally inferred base from your Paperclip config — `http://<host>:<port>`, where the host comes from `PAPERCLIP_SERVER_HOST` (default `localhost`), and the port comes from `PAPERCLIP_SERVER_PORT`, then the config file's `server.port`
5. `http://localhost:3100` as the final fallback
This ordering is what makes the CLI "just work" against a local instance while still letting a single flag or env var point you at a remote server. If a connection fails, the error names the URL it actually tried and hints at a `GET /api/health` check so you can confirm the server is reachable.
> **Tip:** When something connects to the wrong place, set `--api-base` explicitly for one command to confirm the target is healthy, then bake the working value into a profile so you stop fighting the resolution order.
---
## How credentials are resolved
The API key is resolved separately from the API base, again first-match-wins:
| Order | Source | `authSource` |
|---|---|---|
| 1 | `--api-key <token>` | `explicit` |
| 2 | `PAPERCLIP_API_KEY` environment variable | `env` |
| 3 | The env var **named** by the profile's `apiKeyEnvVarName`, read at call time | `profile_env` |
| 4 | A stored board credential keyed by the resolved API base | `stored_board` |
The profile never stores a plaintext token. It stores the *name* of an environment variable (`apiKeyEnvVarName`); the CLI reads that variable's value when it runs. This is the recommended way to wire an agent key into a profile — the secret stays in your environment or secret manager, and the context file stays safe to commit or share.
If no explicit key is found through sources 1–3, the CLI falls back to a stored board credential for the resolved API base (created by `auth login` / the connect wizard). When the call still hits a `401`, or a `403` that says board access or instance admin is required, *and* you are on an interactive TTY *and* you did not pass `--api-key`, the CLI will attempt an interactive board-auth recovery to mint a fresh token mid-command. Passing `--api-key` opts out of that recovery entirely, which is what you want in scripts and CI.
> **Warning:** Interactive auth recovery only triggers on a TTY. In headless contexts there is no prompt — supply a working credential up front via `--api-key`, `PAPERCLIP_API_KEY`, or a profile `apiKeyEnvVarName`, or the command fails with the underlying auth error.
---
## How company scope is resolved
Company-scoped commands resolve the company ID in this order:
1. `--company-id <id>` (alias `-C`)
2. The `PAPERCLIP_COMPANY_ID` environment variable
3. The selected profile's `companyId`
If a command requires a company and none of these is set, it errors with:
```
Company ID is required. Pass --company-id, set PAPERCLIP_COMPANY_ID, or set context profile companyId via `paperclipai context set`.
```
---
## Context profiles
Profiles live in the CLI context file — by default `~/.paperclip/context.json`, a version-2 store with a `currentProfile` and a map of named `profiles`. The context path itself is resolved from `--context`, then the `PAPERCLIP_CONTEXT` env var, then the nearest `.paperclip/context.json` found by walking up from the current directory, and finally the default home location. The file is written with `0600` permissions.
Each profile is persona-aware and can hold:
| Field | Meaning |
|---|---|
| `apiBase` | Default API base URL for the profile |
| `companyId` | Default company ID |
| `persona` | `board` or `agent` — which persona this profile operates as |
| `agentId` | Default agent ID (agent persona) |
| `agentName` | Default agent display name (agent persona) |
| `apiKeyEnvVarName` | Name of the env var holding the API key (recommended over storing a token) |
The two personas matter: a `board` profile carries full board authority across the instance, while an `agent` profile is scoped to exactly one company and one agent. See [Authentication](./authentication.md) for how each credential type is minted.
### Managing profiles
```sh
# Inspect the active profile and the full store
paperclipai context show
# List every profile with its key fields
paperclipai context list
# Switch the active profile
paperclipai context use default
```
`context set` writes values onto a profile (the current one unless `--profile` names another), creating it if needed:
```sh
# A board-operator profile pointed at a local instance
paperclipai context set \
--api-base http://localhost:3100 \
--company-id <company-id> \
--persona board \
--use
# An agent profile that reads its key from an env var
paperclipai context set --profile my-agent \
--persona agent \
--agent-id <agent-id> \
--agent-name "Ops Agent" \
--api-key-env-var-name PAPERCLIP_API_KEY \
--use
```
`context set` accepts `--api-base`, `--company-id`, `--persona` (must be `board` or `agent`), `--agent-id`, `--agent-name`, and `--api-key-env-var-name`. Add `--use` to also make the profile active. Passing an empty value for a field clears it from the profile.
> **Tip:** Keep the secret out of the file. Set `--api-key-env-var-name PAPERCLIP_API_KEY` on the profile and export the actual token in your shell:
>
> ```sh
> paperclipai context set --api-key-env-var-name PAPERCLIP_API_KEY
> export PAPERCLIP_API_KEY=...
> ```
---
## Isolating state with `--data-dir`
`--data-dir` redirects every piece of local Paperclip state — config, context, database, logs, storage, and secrets — to a directory you choose. Pass it on whichever commands should share that isolated root:
```sh
paperclipai run --data-dir ./tmp/paperclip-dev
paperclipai issue list --data-dir ./tmp/paperclip-dev
```
This keeps experiments, test instances, and parallel worktrees from colliding with your real `~/.paperclip` state.
---
## See also
- [Authentication](./authentication.md) — minting board tokens and agent keys, and the connect wizard
- [Output & Scripting](output-and-scripting.md) — `--json` and machine-readable output
- [Installation](installation.md) — installing the `paperclipai` binary
- [Overview](overview.md) — the CLI's place in the operating model
+237
View File
@@ -0,0 +1,237 @@
---
paperclip_version: v2026.609.0
---
# Company Commands
Companies are the top-level container in Paperclip: every agent, project, goal, issue, and budget lives inside one. Use `paperclipai company` when you want to inspect companies, edit their settings and branding, move a whole company between instances as a portable package, pull feedback traces, or delete a company when the server policy allows it.
A board operator sees and manages every company on the instance. An agent persona is scoped to exactly one company, so company-scoped reads and the deletion guard rails resolve against that single company unless you pass an explicit ID.
> **Tip:** Add `--json` to any command when you are scripting. The human-readable output is summarized; the JSON output is the full server record.
---
## Common Options
Every command on this page accepts the standard client options: `--data-dir <path>`, `--api-base <url>`, `--api-key <token>`, `--context <path>`, `--profile <name>`, and `--json`. See [Common Options](./common-options.md) for how the API base and credentials are resolved.
Two commands — `feedback:list` and `feedback:export` — take the company as a required `-C, --company-id <id>` flag rather than a positional argument. The `import` command also accepts `-C, --company-id <id>` to name an existing target company. Everywhere else the company ID is a positional `<companyId>` argument.
---
## Read Commands
These are read-only and safe to run anywhere.
```sh
paperclipai company list
paperclipai company get <company-id>
paperclipai company current
paperclipai company stats
```
| Command | What it returns |
|---|---|
| `company list` | Every company you can see. Human output shows `id`, `name`, `status`, monthly budget and spend (in cents), and whether new agents require board approval. |
| `company get <companyId>` | The full record for one company. |
| `company current` | The full record for the company in scope, resolved from `-C, --company-id`, the context file, `PAPERCLIP_COMPANY_ID`, or the agent credential — no positional ID needed. |
| `company stats` | Instance-level company statistics from `/api/companies/stats`. |
`company list` with an agent credential returns only your scoped company; with a board credential it returns every company on the instance.
`company current` is the convenient way to read the company you are already scoped to: it resolves the current company ID the same way every other command does and returns the same full record as `company get <companyId>`. With an agent credential — which is bound to exactly one company — it always returns that company.
---
## Create and Update
`create`, `update`, and `branding:update` send raw JSON payloads through to the API. The CLI does not validate the shape — it forwards your `--payload-json` to the server, which applies the matching schema (`CreateCompany`, `UpdateCompany`, `UpdateCompanyBranding`). Build the object the API expects and pass it as a single JSON string.
```sh
paperclipai company create \
--payload-json '{"name":"Acme","issuePrefix":"ACME"}'
paperclipai company update <company-id> \
--payload-json '{"budgetMonthlyCents":500000}'
paperclipai company branding:update <company-id> \
--payload-json '{"primaryColor":"#1f6feb","logoUrl":"https://cdn.example.com/acme.png"}'
```
| Command | Method / route | Notes |
|---|---|---|
| `company create` | `POST /api/companies` | `--payload-json` is required. |
| `company update <companyId>` | `PATCH /api/companies/{id}` | `--payload-json` is required; send only the fields you want to change. |
| `company branding:update <companyId>` | `PATCH /api/companies/{id}/branding` | `--payload-json` is required; carries branding fields only. |
> **Note:** Because the payload is forwarded verbatim, a malformed or unknown field surfaces as a server-side validation error, not a CLI error. Use `--json` and read the response when a write is rejected.
---
## Archive
```sh
paperclipai company archive <company-id>
```
`company archive` posts to `/api/companies/{id}/archive` and takes no payload. Archiving is the reversible way to take a company out of active rotation. Reach for [`company delete`](#delete-a-company) only when you genuinely need the records gone and the server is configured to permit it.
---
## Export and Import
Paperclip companies are portable. `company export` writes a company to a folder of markdown-backed files (plus a `.paperclip.yaml` manifest and any binary assets), and `company import` reads that package back — from a local path, a `.zip`, or a GitHub repository — into a new or existing company.
### Export a portable package
```sh
paperclipai company export <company-id> \
--out ./exports/acme \
--include company,agents,projects,issues,skills
```
| Flag | Use |
|---|---|
| `--out <path>` | Required. Output directory the package is written to. |
| `--include <values>` | Comma-separated set of `company`, `agents`, `projects`, `issues` (alias `tasks`), `skills`. Defaults to `company,agents`. |
| `--skills <values>` | Comma-separated skill slugs/keys to include. |
| `--projects <values>` | Comma-separated project shortnames/IDs to include. |
| `--issues <values>` | Comma-separated issue identifiers/IDs to include. |
| `--project-issues <values>` | Comma-separated project shortnames/IDs whose issues should be exported. |
| `--expand-referenced-skills` | Vendor skill contents into the package instead of exporting upstream references. Defaults to off. |
The default `--include` is deliberately small (`company,agents`) so a quick export does not drag along every task. Widen it explicitly when you want a full clone. If the output directory already contains files, the command asks you to confirm an overwrite in an interactive terminal, and refuses outright in a non-interactive one — choose an empty directory for headless runs.
On success the command prints the resolved output path, the package root, the number of files written, the `.paperclip.yaml` extension path, and a warning count.
### Import a package
```sh
paperclipai company import ./exports/acme \
--target new \
--new-company-name "Acme (imported)"
paperclipai company import https://github.com/acme/company-pack \
--target existing --company-id <company-id> \
--collision replace --yes
```
The source argument is a local directory, a local `.zip` file, a GitHub URL, or `owner/repo[/path]` shorthand. Generic HTTP URLs are rejected — only GitHub / GitHub Enterprise URLs and local paths are supported.
| Flag | Use |
|---|---|
| `--include <values>` | Comma-separated include set (`company,agents,projects,issues,tasks,skills`). When omitted, the default import set is all five groups. |
| `--target <mode>` | `new` to create a company, `existing` to import into one you already have. Inferred from `--company-id`/context when omitted. |
| `-C, --company-id <id>` | The existing target company. Required when `--target existing`. |
| `--new-company-name <name>` | Name override when `--target new`. |
| `--agents <list>` | Comma-separated agent slugs to import, or `all`. Defaults to `all`. |
| `--collision <mode>` | How to handle existing matches: `rename` (default), `skip`, or `replace`. |
| `--ref <value>` | Git ref (branch, tag, or commit) for GitHub sources. Only valid for GitHub imports. |
| `--paperclip-url <url>` | Alias for `--api-base` on this command. |
| `--yes` | Accept the default selection and skip the pre-import confirmation prompt. |
| `--dry-run` | Run the preview only; apply nothing. |
The import always runs a server-side preview first and shows you the plan — package contents, per-group create/update/skip counts, sample entities, and any warnings or errors — before anything is written. In an interactive terminal without `--include` or `--yes`, you also get a selection menu to pick exactly which projects, tasks, agents, and skills to bring in. With `--dry-run` the command stops after the preview.
> **Warning:** Applying an import is gated. With `--json` you must pass `--yes` (run `--dry-run` first to inspect the preview as JSON). From a non-interactive terminal you must also pass `--yes`. Without `--yes` in an interactive terminal, the CLI prompts for explicit confirmation before applying.
When an imported agent has a `process` adapter with no explicit adapter, the CLI currently falls back to the `claude-local` adapter and tells you so in the preview/result info block.
### Raw API passthrough variants
If you would rather drive the server's portability routes directly with a hand-built payload, four passthrough commands post your `--payload-json` to the raw routes. These do not read or write local files — they are the API endpoints behind `export`/`import` exposed for scripting.
```sh
paperclipai company export:preview <company-id> --payload-json '{...}'
paperclipai company export:api <company-id> --payload-json '{...}'
paperclipai company import:preview <company-id> --payload-json '{...}'
paperclipai company import:apply <company-id> --payload-json '{...}'
```
| Command | Route |
|---|---|
| `company export:preview <companyId>` | `POST /api/companies/{id}/exports/preview` |
| `company export:api <companyId>` | `POST /api/companies/{id}/exports` |
| `company import:preview <companyId>` | `POST /api/companies/{id}/imports/preview` |
| `company import:apply <companyId>` | `POST /api/companies/{id}/imports/apply` |
Prefer the high-level `export`/`import` commands for day-to-day work; they handle file I/O, source resolution, previews, and confirmation for you. Use the passthrough variants only when you need precise control over the request body.
---
## Feedback Traces
Feedback traces capture votes and reviews recorded against agent work. Use these two commands to read them or pull them out for analysis. Both require `-C, --company-id <id>`.
```sh
paperclipai company feedback:list --company-id <company-id> --status open --vote down
paperclipai company feedback:export --company-id <company-id> --from 2026-01-01 --format ndjson --out ./feedback.ndjson
```
| Flag | Use |
|---|---|
| `--target-type <type>` | Filter by target type. |
| `--vote <vote>` | Filter by vote value. |
| `--status <status>` | Filter by trace status. |
| `--project-id <id>` | Filter by project. |
| `--issue-id <id>` | Filter by issue. |
| `--from <iso8601>` | Only traces created at or after this timestamp. |
| `--to <iso8601>` | Only traces created at or before this timestamp. |
| `--shared-only` | Only traces eligible for sharing/export. |
| `--include-payload` | Include stored payload snapshots in the response. |
`feedback:list` prints a compact row per trace (`id`, issue, vote, status, target type, target label) or the full records with `--json`. `feedback:export` adds two flags:
| Flag | Use |
|---|---|
| `--out <path>` | Write the export to a file instead of stdout. |
| `--format <format>` | `json` or `ndjson`. Defaults to `ndjson`. |
`feedback:export` includes payload snapshots by default (unlike `feedback:list`, where you opt in with `--include-payload`). When you pass `--out`, the command writes the file and reports the count; otherwise it streams the serialized traces to stdout, which makes it easy to pipe into another tool.
---
## Delete a Company
`company delete` is destructive and intentionally hard to trigger by accident. It removes a company and its records permanently.
```sh
paperclipai company delete PAP --yes --confirm PAP
paperclipai company delete 5cbe79ee-acb3-4597-896e-7662742593cd \
--yes --confirm 5cbe79ee-acb3-4597-896e-7662742593cd
```
The selector argument is either a company ID (UUID) or an issue prefix/shortname (for example `PAP`).
| Flag | Use |
|---|---|
| `--by <mode>` | How to interpret the selector: `auto` (default), `id`, or `prefix`. |
| `--yes` | Required safety flag. Deletion fails without it. |
| `--confirm <value>` | Required. Must equal the target company's ID or its issue prefix. |
### Guard rails
Deletion has to clear four separate checks, by design:
1. **Server policy.** The instance must have `PAPERCLIP_ENABLE_COMPANY_DELETION` enabled. If it is off, the API rejects the delete regardless of your flags.
2. **`--yes`.** Without it the command errors immediately — no API call is made.
3. **`--confirm <value>`.** The value must match the resolved company's ID or its issue prefix (prefix match is case-insensitive). A mismatch errors with the expected ID and prefix so you can correct it.
4. **Unambiguous resolution.** In `auto` mode, a selector that matches both a different company's ID and another's prefix is rejected as ambiguous — re-run with `--by id` or `--by prefix`.
Resolution order: the CLI tries an ID lookup when the selector looks like a UUID (or `--by id`), then your scoped company from context, then a board-wide company list. With an agent credential, the board-wide lookup is unavailable — deletion is company-scoped, so use the current company's ID or prefix (via `--company-id` or `PAPERCLIP_COMPANY_ID`), not another company's.
> **Warning:** There is no undo. If you only need to take a company out of rotation, use [`company archive`](#archive) instead.
On success the command prints `ok: true` with the deleted company's ID, name, and prefix.
---
## See also
- [Common Options](./common-options.md) — API base resolution, credentials, and the flags every command shares
- [Output and Scripting](./output-and-scripting.md) — using `--json` and piping CLI output
- [Project Commands](./project.md) — manage the projects inside a company
- [Agent Commands](./agent.md) — inspect agents and set up a local agent CLI session
- [Feedback Commands](./feedback.md) — work with feedback traces beyond the company-scoped views
- [Your First Company via CLI](../../guides/getting-started/your-first-company.md) — end-to-end walkthrough
+106
View File
@@ -0,0 +1,106 @@
---
paperclip_version: v2026.609.0
---
# Connect Command
When you sit down at a fresh terminal and want the Paperclip CLI to start talking to a running instance, `connect` is the friendly front door. It walks you through one short conversation — which server, which persona, which company and agent — and at the end it has minted a token, saved a reusable profile, and printed the environment exports you need. You do not have to know any IDs up front or hand-craft a context file; the wizard discovers what is available and lets you pick.
Reach for `paperclipai connect` the very first time you set up the CLI, whenever you want to add another saved profile (a second instance, a different company, a new agent), or any time you would rather click through choices than type out `context set` and `auth login` by hand.
> **Note:** `connect` is interactive by design. It needs a real terminal (a TTY) on both input and output. In scripts and CI it will refuse to run and tell you to pass `--api-base`/`--api-key` or use the `context` and token commands instead. See [Common Options](./common-options.md) for the non-interactive path.
---
## What it does, end to end
`connect` is a wizard, not a group of subcommands. You run the single command and answer a handful of prompts:
```sh
paperclipai connect
```
Under the hood it walks through these steps:
1. **Confirm the API base.** It pre-fills the API base it would otherwise resolve (see [Common Options](./common-options.md) for that resolution order) and lets you accept or change it. The placeholder is `http://localhost:3100`.
2. **Health-check the server.** It calls `GET /api/health` against the base you chose and stops early if the instance is not reachable — so you find connection problems now, not three commands later.
3. **Log you in as a board operator.** It runs the interactive board login to obtain a board credential, which it uses to discover the companies you can see.
4. **Pick a persona.** You choose whether this profile connects as a **board operator** or as an **agent in a company**.
5. **Name the profile.** It suggests the current profile name (or `default`) and lets you keep or rename it.
6. **Choose company (and agent).** A board profile can optionally pin a default company; an agent profile must pick a company and then an agent within it.
7. **Mint a token, save the profile, print exports.** It creates the right kind of token, writes a persona-aware profile into your context file, makes that profile current, and prints the shell exports you can paste to use the new credential immediately.
---
## The two personas
The one decision that shapes everything else is **persona**. You can skip the prompt by passing `--persona board` or `--persona agent`; otherwise the wizard asks.
| Persona | What it creates | Company & agent |
|---|---|---|
| `board` | A board API token, scoped to the instance. | Company is **optional** — you may pin a default for the profile or choose `(none)`. No agent. |
| `agent` | An agent API key, scoped to one company and one agent. | Company is **required**, and you then pick one of that company's agents. The company must have at least one agent. |
A board profile carries board authority across the instance; an agent profile is bound to exactly one company and one agent. This is the same persona split described under [Context profiles](./common-options.md). For how each credential type is minted and what it can do, see [Authentication](./authentication.md).
```sh
# Connect straight as a board operator, skipping the persona prompt
paperclipai connect --persona board
# Connect as an agent (you'll still pick the company and agent interactively)
paperclipai connect --persona agent
```
---
## Naming the token and the env var
Two flags let you control how the resulting credential is labelled and stored, so you do not have to accept the defaults.
```sh
paperclipai connect --token-name "laptop-board" --api-key-env-var-name PAPERCLIP_API_KEY
```
| Flag | Default | Use |
|---|---|---|
| `--persona <persona>` | _(prompted)_ | The persona to configure: `board` or `agent`. Pass it to skip the persona question. |
| `--api-key-env-var-name <name>` | `PAPERCLIP_API_KEY` | The name of the environment variable the profile will read its key from at call time. The profile stores this **name**, never the token itself. |
| `--token-name <name>` | A timestamped label | The label to give the created token. Defaults to `cli-board-<timestamp>` for a board token or `cli-agent-<timestamp>` for an agent key. |
> **Tip:** The profile records the *name* of the env var, not the secret. After connecting, keep the token in your environment (the printed exports do exactly this) so your context file stays safe to share. This is the same `apiKeyEnvVarName` mechanism explained in [Common Options](./common-options.md).
---
## What you get back
When the wizard finishes it confirms the connection (`Connected profile '<name>' as board.` or `... as <agent name>.`) and returns a result you can read or script against. Pass `--json` for the machine-readable form.
The result includes the profile name, the persona, the resolved `apiBase`, the `companyId` (and `agentId`/`agentName` for an agent profile), the created `key` (its `id`, `name`, `createdAt`, `token`, and — for a board token — `expiresAt`), and a ready-to-paste **`exports`** block:
```sh
export PAPERCLIP_API_URL='http://localhost:3100'
export PAPERCLIP_COMPANY_ID='<company-id>'
export PAPERCLIP_AGENT_ID='<agent-id>'
export PAPERCLIP_API_KEY='<token>'
```
The `PAPERCLIP_COMPANY_ID` and `PAPERCLIP_AGENT_ID` lines appear only when they apply, and the last line uses whatever name you set with `--api-key-env-var-name`. Paste the block into your shell and the rest of the CLI will pick up the new profile and credential immediately.
> **Warning:** The plaintext `token` is shown once, in the wizard output and in the `exports` block. Capture it now — store it in your secret manager or export it into your environment. The saved profile only references the env var by name; it never holds the token for you to recover later.
---
## Common Options
`connect` accepts the standard client options — `--data-dir <path>`, `--api-base <url>`, `--api-key <token>`, `--context <path>`, `--profile <name>`, `-C, --company-id <id>`, and `--json`. Of these, `--api-base`, `--context`, `--profile`, and `--company-id` seed the wizard's starting values (the API base it pre-fills, the context file it writes, the profile name it suggests, and the company it pre-selects). See [Common Options](./common-options.md) for how API base, context, and company scope are resolved, and [Output and Scripting](./output-and-scripting.md) for `--json` usage.
> **Note:** `connect` performs an interactive board login as part of its flow, so passing `--api-key` is not the way to drive it non-interactively. If you need a scripted setup, mint a token another way and configure the profile with `context set` — the path `connect` itself points you to when there is no TTY.
---
## See also
- [Authentication](./authentication.md) — how board tokens and agent keys are minted and what each can do
- [Common Options](./common-options.md) — shared client flags, context profiles, and API base resolution
- [Output and Scripting](./output-and-scripting.md) — working with `--json` output
- [Overview](./overview.md) — the CLI's place in the operating model
+4 -220
View File
@@ -1,225 +1,9 @@
---
paperclip_version: v2026.529.0
paperclip_version: v2026.609.0
---
# Control-Plane Commands
Use these commands when Paperclip is already running and you want to operate a company from the terminal instead of the UI.
This section covers the commands that most directly affect company work:
- issues and comments
- companies and portability
- agents and local agent setup
- approvals
- activity and dashboard reads
- manual heartbeat invocation
---
## Working Pattern
The terminal workflow is usually:
1. choose or set a company context
2. inspect the current state
3. make a targeted change
4. verify the result in the follow-up command or the UI
> **Tip:** Use `--json` when you are scripting against the CLI or piping results into another tool.
---
## Company Commands
Use these commands to inspect companies, export data, import portable packages, or delete a company when the server allows it.
```sh
pnpm paperclipai company list
pnpm paperclipai company get <company-id>
pnpm paperclipai company export <company-id> --out ./exports/acme --include company,agents
pnpm paperclipai company import ./exports/acme --target new --new-company-name "Acme Imported" --include company,agents
pnpm paperclipai company delete <company-id-or-prefix> --yes --confirm <same-id-or-prefix>
```
Practical notes:
- export writes a portable package that includes a manifest and markdown-backed content
- import can target a new company or update an existing one
- use `--dry-run` when you want to preview an import without writing anything
- deletion is guarded by server-side policy and confirmation flags
> **Warning:** `company delete` is intentionally hard to run by accident. Use the exact confirmation value the command asks for.
---
## Issue Commands
Use these commands to manage tasks directly from the shell.
```sh
pnpm paperclipai issue list --company-id <company-id> --status todo,in_progress --assignee-agent-id <agent-id> --match text
pnpm paperclipai issue get <issue-id-or-identifier>
pnpm paperclipai issue create --company-id <company-id> --title "..." --description "..." --status todo --priority high
pnpm paperclipai issue update <issue-id> --status in_progress --comment "Started triage"
pnpm paperclipai issue comment <issue-id> --body "..." --reopen
pnpm paperclipai issue checkout <issue-id> --agent-id <agent-id>
pnpm paperclipai issue release <issue-id>
```
When to use them:
- `list` to find work by status, assignee, or text match
- `get` to inspect one task in detail
- `create` when a board operator or automation needs to create work explicitly
- `update` and `comment` when you want to move or annotate a task without opening the UI
- `checkout` and `release` when you are coordinating single-assignee work
> **Note:** `issue checkout` is the command that enforces the single-assignee task model at the CLI layer.
---
## Agent Commands
Use these commands to inspect agents or prepare a local agent CLI session.
```sh
pnpm paperclipai agent list --company-id <company-id>
pnpm paperclipai agent get <agent-id>
pnpm paperclipai agent local-cli <agent-id-or-shortname> --company-id <company-id>
```
`agent local-cli` is the practical manual path for running Claude or Codex as a Paperclip agent. It can:
- create a long-lived agent API key
- install Paperclip skills into the local Claude/Codex skills directories
- print the shell exports needed to run as that agent identity
Example:
```sh
pnpm paperclipai agent local-cli codexcoder --company-id <company-id>
pnpm paperclipai agent local-cli claudecoder --company-id <company-id>
```
---
## Skills Commands
Use these commands when you want to browse the catalog of skills that ship with the app, stock your company's skill library, keep those skills current, and decide which skills each agent should use — all from the terminal.
It helps to think in three layers:
- The **catalog** is the read-only set of app-shipped skills (`browse`, `search`, `inspect`). You install one into your company when you want it.
- The **company skill library** is your company's own collection (`list`, `show`, `file`, `import`, `create`, `check`, `update`, `audit`, `reset`, `remove`). Installing a catalog skill copies it here; you can also import or author your own.
- **Agent sync** is the desired-skill set for a single agent (`agent list`, `agent sync`, `agent clear`). Putting a skill in the library does not attach it to anyone — you sync it onto an agent when you want that agent to use it.
### Browse and install from the catalog
Start by exploring what the app ships, then install the ones you want into your company library.
```sh
pnpm paperclipai skills browse --kind bundled --category coding
pnpm paperclipai skills search "code review" --kind optional
pnpm paperclipai skills inspect <catalog-ref>
pnpm paperclipai skills install <catalog-ref> --as my-reviewer --company-id <company-id>
```
`browse` filters by `--kind` (`bundled` or `optional`), `--category`, and `--query`; `search <query>` is the same filtered list with the text supplied as an argument. `inspect <catalogRef>` shows the full detail (files, hashes, recommended roles) before you commit. `install <catalogRef>` copies the skill into the company library — use `--as <slug>` to override the slug and `--force` to replace a same-key catalog-managed skill when the server allows it.
> **Note:** `install` does **not** attach the skill to any agent. It only adds it to the library. Attach it later with `skills agent sync`.
### Work with the company skill library
Once skills are in the library, inspect them, bring in your own, and keep everything up to date.
```sh
pnpm paperclipai skills list --company-id <company-id>
pnpm paperclipai skills show <skill-ref>
pnpm paperclipai skills file <skill-ref> --path SKILL.md
pnpm paperclipai skills import <source>
pnpm paperclipai skills create --name "House Style" --slug house-style --body-file ./house-style.md
```
`import <source>` accepts a local path, a GitHub repo, a skills.sh reference, or a URL. `create` authors a managed local skill; pass the markdown body with `--body-file <path>`, or use `--body-file -` to read it from stdin.
To keep library skills current, check for updates, then install them:
```sh
pnpm paperclipai skills check
pnpm paperclipai skills update <skill-ref>
pnpm paperclipai skills update --all
pnpm paperclipai skills audit <skill-ref>
pnpm paperclipai skills reset <skill-ref> --yes
pnpm paperclipai skills remove <skill-ref> --yes
```
`check [skillRef]` reports update status (omit the reference to check every skill). `update` installs an available update for one skill, or `--all` for every skill that has one. `audit` inspects installed skill bytes without executing them. `reset` returns a catalog-managed skill to its pinned installed origin, and `remove` deletes a skill from the library — both prompt for confirmation unless you pass `--yes`. `update`, `reset`, and `audit` accept `--force` to discard local-modification or soft-audit holds (hard-stop audit findings still fail).
### Sync skills to an agent
When you want an agent to actually use library skills, sync them onto that agent.
```sh
pnpm paperclipai skills agent list <agent-ref>
pnpm paperclipai skills agent sync <agent-ref> --skill <skill-ref> --skill <other-skill-ref>
pnpm paperclipai skills agent clear <agent-ref> --yes
```
`agent list` shows the agent's current runtime skill snapshot. `agent sync` replaces the agent's non-required desired company skills with the ones you pass (repeat `--skill` for each). `agent clear` removes all non-required desired skills; required Paperclip skills stay server-enforced either way.
All company-scoped skills subcommands accept the common client options (`--company-id`, `--api-base`, `--api-key`, `--json`, and the rest). The catalog read commands (`browse`, `search`, `inspect`) are not company-scoped. See the [Command Reference](./commands.md#skills) for the exhaustive per-flag list.
---
## Approval Commands
Use these commands to review or resolve board-gated actions.
```sh
pnpm paperclipai approval list --company-id <company-id> --status pending
pnpm paperclipai approval get <approval-id>
pnpm paperclipai approval create --company-id <company-id> --type hire_agent --payload '{"name":"..."}' --issue-ids <id1,id2>
pnpm paperclipai approval approve <approval-id> --decision-note "..."
pnpm paperclipai approval reject <approval-id> --decision-note "..."
pnpm paperclipai approval request-revision <approval-id> --decision-note "..."
pnpm paperclipai approval resubmit <approval-id> --payload '{"...":"..."}'
pnpm paperclipai approval comment <approval-id> --body "..."
```
Use these when an agent needs human approval for hiring, strategy, or another governed action.
> **Tip:** A short decision note is usually enough. What matters most is that the requester can act on your feedback without guessing.
---
## Activity And Dashboard
These commands are read-only and useful for monitoring.
```sh
pnpm paperclipai activity list --company-id <company-id> --agent-id <agent-id> --entity-type issue --entity-id <id>
pnpm paperclipai dashboard get --company-id <company-id>
```
Use `activity list` when you want the audit trail for a company or object. Use `dashboard get` when you want the same top-level state the UI shows.
---
## Heartbeat
Use the heartbeat command when you want to invoke an agent directly from the terminal.
```sh
pnpm paperclipai heartbeat run --agent-id <agent-id> --api-base http://localhost:3100 --api-key <token>
```
This is most useful for debugging or for scripted agent workflows where you want a run to fire immediately instead of waiting for the normal scheduler.
---
## Next Steps
- [Setup Commands](./setup-commands.md) if you need to repair or reconfigure the local instance before using the control plane
- [Managing Tasks](../../guides/day-to-day/issues.md) for the UI workflow that mirrors the `issue` commands
- [Approvals](../../guides/day-to-day/approvals.md) for the board review flow behind the approval commands
> **This page has moved.** Operating a company from the terminal is now documented per command family. Start at the [CLI Overview](./overview.md), or go straight to what you need:
>
> [Company](./company.md) · [Project](./project.md) · [Goal](./goal.md) · [Issue](./issue.md) · [Agent](./agent.md) · [Prompt](./prompt.md) · [Run](./run.md) · [Routine](./routine.md) · [Approval](./approval.md) · [Activity](./activity.md) · [Dashboard](./dashboard.md) · [Cost](./cost.md) · [Skills](./skills.md)
+190
View File
@@ -0,0 +1,190 @@
---
paperclip_version: v2026.609.0
---
# Cost, Finance & Budget Commands
Use these commands when you need to see what a company is spending, record manual finance events, and set the guard rails that keep agent spend inside the limits you intend — the same cost view and budget controls the UI exposes, from the terminal. Reach for them when you're scripting spend reports, wiring budgets into automation, or operating headless (including from an AI operator, if you've set one up).
These commands split into three top-level groups: `cost` (read spend and record cost events), `finance` (record and summarize finance events), and `budget` (read budget state and write the policies that gate spend). Every read is non-destructive; the only commands that change state are `event:create`, `policy:upsert`, `company:update`, `agent:update`, and `incident:resolve`.
> **Note:** Most commands here are company-scoped. Pass the company explicitly with `-C, --company-id <id>`, or let the CLI resolve it from your selected context profile. `budget company:update` and `budget incident:resolve` require a company before they will run. Two commands are scoped by their positional argument instead and take no `--company-id`: `cost issue <issue-id>` (the issue resolves its company server-side) and `budget agent:update <agent-id>` (the agent id targets the budget directly).
---
## Common options
Every command on this page accepts the standard client flags. See [Common Options](./common-options.md) for the full list and [Output and Scripting](./output-and-scripting.md) for `--json` usage.
| Flag | Use |
|---|---|
| `-C, --company-id <id>` | Target company. Accepted by the company-scoped commands; required for those unless your context profile already selects one. Not accepted by `cost issue` or `budget agent:update`, which scope by their positional argument. |
| `--api-base <url>` | Override the API base URL. |
| `--api-key <token>` | Override the credential. |
| `--context <path>` | Use a specific context file instead of `~/.paperclip/context.json`. |
| `--profile <name>` | Select a named persona profile. |
| `--data-dir <path>` | Override the local data directory. |
| `--json` | Emit raw JSON instead of the formatted table. Use this whenever you script against these commands. |
> **Tip:** Reads are cheap and idempotent. Wire `cost summary`, `budget overview`, and `cost window-spend` into a monitoring loop with `--json` and pipe the output into your own dashboards or alerting.
---
## Reading cost
The `cost` group reports accumulated spend across the company, sliced by the dimension you care about. Every subcommand below is a read against the company's cost rollups — choose the breakdown that answers your question.
```sh
paperclipai cost summary --company-id <company-id>
paperclipai cost by-agent --company-id <company-id>
paperclipai cost by-agent-model --company-id <company-id>
paperclipai cost by-provider --company-id <company-id>
paperclipai cost by-biller --company-id <company-id>
paperclipai cost by-project --company-id <company-id>
paperclipai cost window-spend --company-id <company-id>
paperclipai cost quota-windows --company-id <company-id>
```
| Command | Answers |
|---|---|
| `cost summary` | The headline total for the company. Start here. |
| `cost by-agent` | Which agents are spending. Use it to spot a runaway agent. |
| `cost by-agent-model` | Spend per agent broken down by the model each one used. The finest view for tuning model choices. |
| `cost by-provider` | Spend grouped by upstream provider. |
| `cost by-biller` | Spend grouped by who is billed. |
| `cost by-project` | Spend grouped by project, so you can attribute cost to the work that incurred it. |
| `cost window-spend` | Spend inside the current rolling budget windows — the number a budget policy actually checks against. |
| `cost quota-windows` | The configured quota windows themselves (their periods and limits). |
> **Tip:** When a budget alert fires, the fastest triage path is `cost window-spend` to see how far over you are, then `cost by-agent` to find the source.
### Cost for a single issue
To see what one issue has cost end to end, pass its id or human identifier (such as `PAP-39`):
```sh
paperclipai cost issue <issue-id>
```
This is the only `cost` read that is not company-scoped on the command line — the issue id resolves the company server-side. Use it when you are reviewing an expensive task and want to know whether the spend was justified by the result.
### Recording a cost event
Most cost is recorded automatically by the server-side runtime as agents run. When you need to record spend the runtime did not capture — for example a one-off external charge attributable to the company — post a cost event directly:
```sh
paperclipai cost event:create --company-id <company-id> --payload-json '{...}'
```
`--payload-json` is required and must be a valid JSON object describing the cost event. The CLI parses it and POSTs it verbatim to the company's `cost-events` endpoint, so the payload shape is whatever the server's cost-event schema expects.
> **Warning:** `--payload-json` is parsed with `JSON.parse`. Malformed JSON fails the command before any request is sent. Quote the whole value in single quotes in your shell so embedded double quotes survive.
---
## Recording and reading finance
Where `cost` tracks model and runtime spend, the `finance` group tracks broader finance events — revenue, expenses, and other money movements you want the company to account for — and summarizes them.
```sh
paperclipai finance event:create --company-id <company-id> --payload-json '{...}'
paperclipai finance events --company-id <company-id>
paperclipai finance summary --company-id <company-id>
paperclipai finance by-biller --company-id <company-id>
paperclipai finance by-kind --company-id <company-id>
```
| Command | Use |
|---|---|
| `finance event:create` | Record a finance event. `--payload-json` is required and POSTed to the company's `finance-events` endpoint. |
| `finance events` | List the recorded finance events for the company. |
| `finance summary` | The aggregate finance picture for the company. |
| `finance by-biller` | Finance totals grouped by biller. |
| `finance by-kind` | Finance totals grouped by event kind. |
Use `finance event:create` when your AI operator closes a deal, books an expense, or otherwise needs to put a money fact on the record; use the read commands to reconcile that record against the cost side.
---
## Budget guard rails
The `budget` group is where autonomous spend is governed. A budget policy sets the limits that the runtime enforces as agents work, so an agent cannot quietly burn through your account between human check-ins. This is the single most important part of running a company headlessly: set budgets before you turn agents loose, not after.
```sh
paperclipai budget overview --company-id <company-id>
paperclipai budget policy:upsert --company-id <company-id> --payload-json '{...}'
paperclipai budget company:update --company-id <company-id> --payload-json '{...}'
paperclipai budget agent:update <agent-id> --payload-json '{...}'
paperclipai budget incident:resolve <incident-id> --company-id <company-id>
```
### Read the current state
```sh
paperclipai budget overview --company-id <company-id>
```
`budget overview` is your starting point: it returns the company's configured budgets and where current spend sits against them. Read it before you change anything, and read it again after, to confirm the change landed.
### Set a budget policy
```sh
paperclipai budget policy:upsert --company-id <company-id> --payload-json '{...}'
```
`policy:upsert` creates a budget policy or updates the existing one — it POSTs the required `--payload-json` to the company's `budgets/policies` endpoint. The policy is what the server consults to decide whether a heartbeat is allowed to spend. Pair it with `cost quota-windows` and `cost window-spend` to verify the windows and limits behave as you intended.
### Update a company budget
```sh
paperclipai budget company:update --company-id <company-id> --payload-json '{"...":"..."}'
```
`company:update` PATCHes the company's budget with an `UpdateBudget` JSON payload. `--payload-json` is required. Use it to adjust the company-wide ceiling — the cap that applies regardless of which agent is spending.
### Update an agent budget
```sh
paperclipai budget agent:update <agent-id> --payload-json '{"...":"..."}'
```
`agent:update` PATCHes a single agent's budget with an `UpdateBudget` JSON payload. The agent id is a positional argument and `--payload-json` is required. Use per-agent budgets to give a trusted senior agent more headroom than a freshly hired one, or to throttle an agent you have seen overspend.
> **Note:** A company-wide budget and per-agent budgets work together. Tightening one agent does not raise the company ceiling, and the company ceiling still bounds the sum of all agents.
### Resolve a budget incident
When spend trips a budget guard rail, the server raises a budget incident. Until it is resolved, affected work stays gated. Resolve it once you have decided how to proceed:
```sh
paperclipai budget incident:resolve <incident-id> --company-id <company-id>
paperclipai budget incident:resolve <incident-id> --company-id <company-id> --payload-json '{"...":"..."}'
```
The incident id is a positional argument. `--payload-json` is optional here and defaults to `{}` — pass a `ResolveBudgetIncident` payload only when the resolution needs structured input (such as a note or a chosen disposition). The command POSTs to the company's `budget-incidents/<incident-id>/resolve` endpoint.
> **Warning:** Resolving an incident clears the gate it raised. If the underlying overspend is still in progress, resolve it only after you have also adjusted the policy with `budget policy:upsert`, `company:update`, or `agent:update` — otherwise the incident will simply recur on the next run.
---
## A practical budget workflow
The order that keeps you out of trouble when standing up a company headlessly:
1. `budget policy:upsert` — define the windows and limits before any agent runs.
2. `budget company:update` — set the company-wide ceiling.
3. `budget agent:update <agent-id>` — give individual agents tighter or looser room as needed.
4. `budget overview` and `cost window-spend` — confirm the guard rails read the way you expect.
5. While the company runs, monitor with `cost summary`, `cost by-agent`, and `cost window-spend`.
6. When an incident fires, triage with `cost by-agent`, adjust the relevant budget, then `budget incident:resolve <incident-id>`.
---
## See also
- [Common Options](./common-options.md) — the client flags every command here accepts
- [Output and Scripting](./output-and-scripting.md) — using `--json` to wire these reads into monitoring
- [Dashboard Commands](./dashboard.md) — the top-level company state, including a spend view
- [Activity Commands](./activity.md) — the audit trail behind budget incidents and spend
- [Agent Commands](./agent.md) — the agents whose spend `cost by-agent` and `budget agent:update` govern
- [Approval Commands](./approval.md) — the board gate that pairs with budgets for high-cost actions
+151
View File
@@ -0,0 +1,151 @@
---
paperclip_version: v2026.609.0
---
# Dashboard Commands
The `dashboard` command is the at-a-glance company read from the terminal — the same top-level numbers a human sees on the Paperclip dashboard, fetched in one call. Reach for it when you want a fast health check: how many agents are running, how much work is open, what the month's spend looks like, and whether anything is waiting on a human. It is read-only, so it is safe to run anywhere, on a loop, or inside a monitoring script.
---
## Overview
There is exactly one subcommand:
```sh
paperclipai dashboard get --company-id <company-id>
```
`dashboard get` issues a single `GET /api/companies/<company-id>/dashboard` and prints the summary. It does not change any state, trigger any runs, or wake any agents — it only reports. This makes it the cheapest way to answer "what is the state of this company right now?" without paging through `issue list`, `agent list`, `approval list`, and `cost summary` separately.
This command is company-scoped, so it needs a company. It does not accept a positional company argument — pass the company with `-C` / `--company-id`, or let it resolve from your environment or profile (see [Resolving the company](#resolving-the-company)).
---
## `dashboard get`
Get the dashboard summary for a single company.
```sh
paperclipai dashboard get --company-id <company-id>
```
### Options
| Flag | Use |
|---|---|
| `-C, --company-id <id>` | The company to summarize. Required unless resolved from `PAPERCLIP_COMPANY_ID` or your context profile. |
| `--json` | Emit the raw summary object as JSON instead of the formatted read. Use this when scripting. |
| `--api-base <url>` | Override the API base URL for this call. |
| `--api-key <token>` | Bearer token for agent-authenticated calls. |
| `--context <path>` | Path to the CLI context file. |
| `--profile <name>` | CLI context profile to use. |
| `-d, --data-dir <path>` | Paperclip data directory root (isolates state from `~/.paperclip`). |
| `-c, --config <path>` | Path to the Paperclip config file (used to infer the local API base). |
These are the standard client flags shared by every company-scoped CLI command. For the full explanation of how authentication, context, and the API base are resolved, see [Common options](./common-options.md).
> **Note:** `dashboard get` is one of the few commands where `--company-id` is genuinely required by the command definition itself. Even with a default company in your profile, it is worth passing `-C` explicitly in scripts so the target is unambiguous.
---
## Resolving the company
The company is resolved in this order, first match wins:
1. `-C` / `--company-id` on the command line
2. the `PAPERCLIP_COMPANY_ID` environment variable
3. the `companyId` recorded in the selected context profile
If none of those produce a company, the command fails with a clear error telling you to pass `--company-id`, set `PAPERCLIP_COMPANY_ID`, or set a profile default with `paperclipai context set`.
For an agent persona whose profile is already pinned to one company, the bare command works without any flag:
```sh
paperclipai dashboard get
```
For a board operator who works across several companies, name the target explicitly each time:
```sh
paperclipai dashboard get --company-id <company-id>
```
---
## What you get back
The summary is a single object covering the dimensions you would otherwise have to assemble by hand. The shape is stable, which is what makes it good for scripting:
| Field | Meaning |
|---|---|
| `companyId` | The company the summary describes. |
| `agents` | Counts of agents that are `active`, `running`, `paused`, and in `error`. |
| `tasks` | Issue counts by bucket: `open`, `inProgress`, `blocked`, and `done`. |
| `costs` | `monthSpendCents`, `monthBudgetCents`, and `monthUtilizationPercent` for the current month. |
| `pendingApprovals` | Number of approvals waiting on a human decision. |
| `budgets` | Governance signals: `activeIncidents`, `pendingApprovals`, `pausedAgents`, and `pausedProjects`. |
| `runActivity` | A per-day series of heartbeat run outcomes, each entry carrying `date`, `succeeded`, `failed`, `other`, and `total`. |
Monetary values are in cents — divide by 100 for dollars. The `costs` block is the same data you can break down further with the [`cost`](./cost.md) commands; the dashboard gives you the headline number, `cost` gives you the attribution.
> **Tip:** The `budgets.pausedAgents` and `budgets.pausedProjects` counts are the fastest way to notice that automation has stopped because a budget tripped. If work seems stalled, check these before anything else.
---
## Output formats
By default the command prints a compact, human-readable rendering of the summary object — good for a quick glance in a terminal.
For anything programmatic, pass `--json` and parse the result. The JSON is the raw summary, so every field above is available:
```sh
paperclipai dashboard get --company-id <company-id> --json
```
Pipe it into `jq` to pull out exactly what you need. A few useful one-liners:
```sh
# Month-to-date budget utilization as a percentage
paperclipai dashboard get -C <company-id> --json | jq '.costs.monthUtilizationPercent'
# How many approvals are waiting on a human
paperclipai dashboard get -C <company-id> --json | jq '.pendingApprovals'
# Agents currently in an error state
paperclipai dashboard get -C <company-id> --json | jq '.agents.error'
# Failed runs over the reported activity window
paperclipai dashboard get -C <company-id> --json | jq '[.runActivity[].failed] | add'
```
For the full set of scripting conventions — exit codes, `--json` behavior, and how to chain CLI calls — see [Output and scripting](./output-and-scripting.md).
---
## Where it fits
`dashboard get` is the terminal equivalent of glancing at the UI dashboard — the same whole-company summary, returned as data you can read or script against. Use it when you'd rather not open a browser, or when you're polling company health from automation (or from an AI operator, if you've set one up).
A practical monitoring loop looks like:
1. `paperclipai dashboard get -C <company-id>` for the headline state.
2. If `pendingApprovals` is non-zero, follow up with [`approval list`](./approval.md).
3. If `agents.error` or `budgets.pausedAgents` is non-zero, follow up with [`agent`](./agent.md) and [`activity list`](./activity.md) to see why.
4. If `costs.monthUtilizationPercent` is climbing toward the budget, break it down with [`cost`](./cost.md).
The dashboard tells you *whether* to look closer; the scoped commands tell you *what* happened.
> **Note:** `dashboard get` is a read of the current company state, not a live stream. To watch heartbeat runs as they execute, use the [`run`](./run.md) inspection subcommands; to follow the audit trail, use [`activity`](./activity.md).
---
## See also
- [Common options](./common-options.md) — authentication, context, and API base resolution shared by every client command
- [Output and scripting](./output-and-scripting.md) — `--json` conventions and chaining CLI calls
- [Cost commands](./cost.md) — break the headline spend down by agent, model, provider, biller, or project
- [Approval commands](./approval.md) — review and resolve the pending approvals the dashboard counts
- [Activity commands](./activity.md) — the audit trail behind agent and run state
- [Agent commands](./agent.md) — inspect the agents the dashboard summarizes
- [Run commands](./run.md) — inspect heartbeat runs behind the `runActivity` series
+280
View File
@@ -0,0 +1,280 @@
---
paperclip_version: v2026.609.0
---
# Worktree & Environment Lab
Use these commands when you want a fully isolated Paperclip instance per git worktree, or a deterministic local fixture to test against. The `worktree` family creates a worktree-local config, its own embedded Postgres on its own ports, and a seeded copy of another instance's data — so you can run a second Paperclip alongside your main one without the two ever touching the same database, ports, or company state. The `env-lab` family spins up disposable infrastructure fixtures (today: an SSH server) so adapter and host-config code has something real to talk to. These are contributor and test-instance tools, not day-to-day company operations.
> **Note:** A "worktree-local instance" is a separate Paperclip data directory and database keyed to one git worktree. Its config lives at `<worktree>/.paperclip/config.json` and its data lives under a worktree home (default `~/.paperclip-worktrees`, override with `--home` or `PAPERCLIP_WORKTREES_DIR`). The instance id is derived from the worktree name unless you pass `--instance`.
---
## Command surface at a glance
Note the namespacing: some commands are top-level and colon-namespaced (`worktree:make`), others are subcommands of `worktree` (`worktree init`). This is intentional and matches the source — type them exactly as shown.
| Command | What it does |
|---|---|
| `paperclipai worktree:make <name>` | Create `~/paperclip-<name>` as a git worktree, then initialize an isolated instance inside it. |
| `paperclipai worktree init` | Create repo-local config/env and an isolated instance for the worktree you are already in. |
| `paperclipai worktree env` | Print shell exports for the current worktree-local instance. |
| `paperclipai worktree:list` | List git worktrees and flag which look like Paperclip worktrees. |
| `paperclipai worktree:merge-history [source]` | Preview or import issue/comment history from another worktree into the current instance. |
| `paperclipai worktree reseed` | Re-seed an existing worktree-local instance from another instance or worktree. |
| `paperclipai worktree repair` | Create or repair a linked worktree-local instance without touching the primary checkout. |
| `paperclipai worktree:cleanup <name>` | Safely remove a worktree, its branch, and its isolated instance data. |
| `paperclipai env-lab up` / `status` / `down` / `doctor` | Manage the local SSH env-lab fixture. |
---
## `worktree:make`
The fast path. Creates `~/paperclip-<name>` as a new git worktree (auto-prefixing `paperclip-` if you omit it), then initializes an isolated Paperclip instance inside it and seeds its database from a source instance.
```sh
paperclipai worktree:make feature-x
# creates the worktree at ~/paperclip-feature-x
```
| Flag | Use |
|---|---|
| `--start-point <ref>` | Remote ref to base the new branch on (env: `PAPERCLIP_WORKTREE_START_POINT`). |
| `--instance <id>` | Explicit isolated instance id instead of deriving it from the name. |
| `--home <path>` | Home root for worktree instances (env: `PAPERCLIP_WORKTREES_DIR`, default `~/.paperclip-worktrees`). |
| `--from-config <path>` | Source `config.json` to seed the new instance from. |
| `--from-data-dir <path>` | Source `PAPERCLIP_HOME` used when deriving the source config. |
| `--from-instance <id>` | Source instance id when deriving the source config (default: `default`). |
| `--server-port <port>` | Preferred server port. The CLI picks the next free port if it is claimed. |
| `--db-port <port>` | Preferred embedded Postgres port. The CLI picks the next free port if it is claimed. |
| `--seed-mode <mode>` | Seed profile: `minimal` or `full` (default: `minimal`). |
| `--preserve-live-work` | Do not quarantine copied agent timers or assigned open issues in the seeded worktree. |
| `--no-seed` | Skip database seeding from the source instance entirely. |
| `--force` | Replace existing repo-local config and isolated instance data. |
> **Warning:** By default, seeding **quarantines** copied live work so a duplicated database does not silently auto-run agents in your isolated instance: timer-driven heartbeats are disabled, running agents are reset to idle, in-progress assigned issues are unassigned and moved to `blocked` (with an explanatory comment), and scheduled routines are paused. Pass `--preserve-live-work` only if you deliberately want the worktree instance to own and run that copied work.
The difference between `minimal` and `full` seed modes is how much of the source database is copied. `minimal` excludes the heavier history tables; `full` brings everything across. Reseed defaults to `full`; make/init/repair default to `minimal`.
---
## `worktree init`
Use this when you have **already** created or checked out a git worktree by hand and just want to give it an isolated Paperclip instance. It writes the repo-local `.paperclip/config.json` and `.env`, allocates free server and DB ports, and seeds the database — without creating the worktree itself.
```sh
paperclipai worktree init --name feature-x
```
| Flag | Use |
|---|---|
| `--name <name>` | Display name used to derive the instance id. |
| `--instance <id>` | Explicit isolated instance id. |
| `--home <path>` | Home root for worktree instances (env: `PAPERCLIP_WORKTREES_DIR`, default `~/.paperclip-worktrees`). |
| `--from-config <path>` | Source `config.json` to seed from. |
| `--from-data-dir <path>` | Source `PAPERCLIP_HOME` used when deriving the source config. |
| `--from-instance <id>` | Source instance id when deriving the source config (default: `default`). |
| `--server-port <port>` | Preferred server port. |
| `--db-port <port>` | Preferred embedded Postgres port. |
| `--seed-mode <mode>` | Seed profile: `minimal` or `full` (default: `minimal`). |
| `--preserve-live-work` | Do not quarantine copied agent timers or assigned open issues. |
| `--no-seed` | Skip database seeding from the source instance. |
| `--force` | Replace existing repo-local config and isolated instance data. |
If the config or instance data already exists and you do not pass `--force`, the command refuses rather than clobbering state.
---
## `worktree env`
Prints the shell exports that point your terminal at the current worktree-local instance. Source this and every subsequent `paperclipai` command in the shell targets the worktree's config, home, and instance — not your default `~/.paperclip`.
```sh
eval "$(paperclipai worktree env)"
```
It emits `PAPERCLIP_CONFIG` (always), plus `PAPERCLIP_HOME`, `PAPERCLIP_INSTANCE_ID`, and `PAPERCLIP_CONTEXT` when present in the worktree's `.env`, followed by any other entries from that `.env`.
| Flag | Use |
|---|---|
| `-c, --config <path>` | Path to the config file to read exports from. |
| `--json` | Print JSON instead of shell `export` lines. |
> **Tip:** `--json` is the form to consume from automation. The plain output is meant for `eval "$(...)"` in an interactive shell.
---
## `worktree:list`
Lists every git worktree visible from the repo and marks which ones look like Paperclip worktrees (i.e. carry a `.paperclip/config.json`). Use it to see what you have spun up before reseeding or cleaning up.
```sh
paperclipai worktree:list
paperclipai worktree:list --json
```
| Flag | Use |
|---|---|
| `--json` | Print JSON instead of text output. |
---
## `worktree:merge-history`
Preview or import issue and comment history from another worktree into the current instance. Worktrees diverge once agents start filing issues in each isolated database; this command pulls that history back together. It previews a plan by default and only writes when you pass `--apply`.
```sh
# Preview what would be imported from another worktree
paperclipai worktree:merge-history --from paperclip-feature-x --company <company-id>
# Apply the import
paperclipai worktree:merge-history --from paperclip-feature-x --company <company-id> --apply --yes
```
| Flag | Use |
|---|---|
| `[source]` (positional) | Optional source worktree path, directory name, or branch name (back-compat alias for `--from`). |
| `--from <worktree>` | Source worktree path, directory name, branch name, or `current`. |
| `--to <worktree>` | Target worktree (defaults to `current`). |
| `--company <id-or-prefix>` | Shared company id or issue prefix inside the chosen source/target instances. |
| `--scope <items>` | Comma-separated scopes to import: `issues`, `comments` (default: `issues,comments`). |
| `--apply` | Apply the import after previewing the plan. |
| `--dry` | Preview only and do not import anything. |
| `--yes` | Skip the interactive confirmation prompt when applying. |
> **Note:** This merges Paperclip *data* (issues and comments) between instances. It does not touch git history. To bring source code branches together, use git directly.
---
## `worktree reseed`
Re-seed an existing worktree-local instance from another instance or worktree. Use this to refresh a stale worktree DB with the latest state from your primary instance, or to repoint it at a different source. It is destructive to the target database, so it confirms first and defaults to the `full` seed profile.
```sh
paperclipai worktree reseed --from current --to paperclip-feature-x --yes
```
| Flag | Use |
|---|---|
| `--from <worktree>` | Source worktree path, directory name, branch name, or `current`. |
| `--to <worktree>` | Target worktree (defaults to `current`). |
| `--from-config <path>` | Source `config.json` to seed from (mutually exclusive with `--from`). |
| `--from-data-dir <path>` | Source `PAPERCLIP_HOME` used when deriving the source config. |
| `--from-instance <id>` | Source instance id when deriving the source config. |
| `--seed-mode <mode>` | Seed profile: `minimal` or `full` (default: `full`). |
| `--preserve-live-work` | Do not quarantine copied agent timers or assigned open issues. |
| `--yes` | Skip the destructive confirmation prompt. |
| `--allow-live-target` | Override the guard that requires the target worktree DB to be stopped first. |
> **Warning:** Reseeding overwrites the target instance's database. Stop the target instance's server before reseeding; the command guards against a live target unless you pass `--allow-live-target`, which you should only do when you are certain nothing is writing to it. Pass either `--from` **or** the `--from-config`/`--from-data-dir`/`--from-instance` trio, never both.
---
## `worktree repair`
Create or repair a linked worktree-local instance without touching the primary checkout. Reach for this when a worktree's `.paperclip` config is missing, broken, or was never initialized — for example after a crash, a partial clone, or hand-editing config. If you pass a `--branch` selector that is not yet a registered worktree, it creates one under `<repo>/.paperclip/worktrees/` for you.
```sh
# Repair the worktree you are standing in
paperclipai worktree repair
# Bootstrap a worktree for a branch and link an instance to it
paperclipai worktree repair --branch feature-x
```
| Flag | Use |
|---|---|
| `--branch <name>` | Existing branch/worktree selector to repair, or a branch name to create under `.paperclip/worktrees`. |
| `--home <path>` | Home root for worktree instances (env: `PAPERCLIP_WORKTREES_DIR`, default `~/.paperclip-worktrees`). |
| `--from-config <path>` | Source `config.json` to seed from. |
| `--from-data-dir <path>` | Source `PAPERCLIP_HOME` used when deriving the source config. |
| `--from-instance <id>` | Source instance id when deriving the source config (default: `default`). |
| `--seed-mode <mode>` | Seed profile: `minimal` or `full` (default: `minimal`). |
| `--preserve-live-work` | Do not quarantine copied agent timers or assigned open issues. |
| `--no-seed` | Repair metadata only and skip reseeding when bootstrapping a missing worktree config. |
| `--allow-live-target` | Override the guard that requires the target worktree DB to be stopped first. |
Repair never modifies your primary checkout — it only fixes or links the worktree instance, so it is safe to run from inside the worktree you are trying to recover.
---
## `worktree:cleanup`
Safely remove a worktree, its branch, and its isolated instance data. The name is auto-prefixed with `paperclip-` if needed, matching `worktree:make`.
```sh
paperclipai worktree:cleanup feature-x
```
| Flag | Use |
|---|---|
| `<name>` (positional) | Worktree name — auto-prefixed with `paperclip-` if needed. |
| `--instance <id>` | Explicit instance id (if it differs from the worktree name). |
| `--home <path>` | Home root for worktree instances (env: `PAPERCLIP_WORKTREES_DIR`, default `~/.paperclip-worktrees`). |
| `--force` | Bypass safety checks (uncommitted changes, unique commits). |
> **Warning:** Cleanup refuses to delete a worktree that has uncommitted changes or commits not present elsewhere, so you do not lose work by accident. `--force` bypasses those checks and removes the worktree, its branch, and its instance data unconditionally — only use it when you are sure the work is disposable.
---
## `env-lab`
Deterministic local environment fixtures. Today this is a single SSH server fixture that adapter and host-config code can connect to during development and tests. Every subcommand accepts `-i, --instance <id>` to target a specific Paperclip instance (defaults to the current/default instance) and `--json` for machine-readable output. The fixture state is tracked in a `state.json` under the instance's `env-lab/ssh-fixture/` directory.
```sh
paperclipai env-lab up # start the SSH fixture
paperclipai env-lab status # is it running, and on what host/port?
paperclipai env-lab doctor # prerequisites + current status
paperclipai env-lab down # stop it
```
| Subcommand | What it does |
|---|---|
| `up` | Start the default SSH env-lab fixture and print its host, port, user, workspace, and log path. |
| `status` | Show the current fixture state (or report that nothing is running). |
| `down` | Stop the fixture. Reports if none was running. |
| `doctor` | Check that SSH fixture prerequisites are installed, then show current status (including the client private key and known-hosts paths when running). |
| Flag | Use |
|---|---|
| `-i, --instance <id>` | Paperclip instance id (default: current/default). |
| `--json` | Print machine-readable fixture details. |
> **Tip:** Run `env-lab doctor` first on a new machine. If prerequisites are incomplete it tells you exactly what is missing before `up` fails. Always `env-lab down` when you are finished so you are not leaving a stray SSH server bound to a local port.
---
## Typical flows
**Spin up an isolated instance for a feature branch:**
```sh
paperclipai worktree:make feature-x --seed-mode minimal
cd ~/paperclip-feature-x
eval "$(paperclipai worktree env)"
paperclipai run
```
**Refresh a stale worktree from your primary instance, then bring its issues back later:**
```sh
paperclipai worktree reseed --from current --to paperclip-feature-x --yes
# ...work happens in the worktree instance...
paperclipai worktree:merge-history --from paperclip-feature-x --company <company-id> --apply --yes
```
**Tear it down when done:**
```sh
paperclipai worktree:cleanup feature-x
```
---
## See also
- [Setup commands](./setup-commands.md) — `run`, onboarding, and instance-level configuration that every worktree instance inherits.
- [Common options](./common-options.md) — the shared `--data-dir`, `--api-base`, `--profile`, and `--json` flags.
- [Output and scripting](./output-and-scripting.md) — consuming `--json` output from the worktree and env-lab commands.
- [Adapter reference](./adapter.md) — adapters are what the SSH env-lab fixture exists to exercise.
- [Authentication](./authentication.md) — how each worktree instance resolves its own credentials and context.
+179
View File
@@ -0,0 +1,179 @@
---
paperclip_version: v2026.609.0
---
# Feedback Commands
Use these commands when you want to inspect, summarize, or extract the thumbs-up / thumbs-down feedback that gets recorded against a company's work — agent outputs, comments, and other targets that a reviewer voted on. The shortest route from "what did people actually think of this agent's work?" to a shareable artifact is `paperclipai feedback report` for a quick read and `paperclipai feedback export` to write a full bundle to disk. The `trace` and `bundle` subcommands fetch a single feedback record by ID for scripted workflows.
Feedback in Paperclip is captured as **feedback traces**. A trace records one vote against one target (its `vote`, its `status`, the issue it belongs to, a summary of what was voted on, and an optional reason), and it carries a `payloadSnapshot` plus a downloadable **bundle** of the underlying files. These commands read those traces through the API — the CLI never computes votes itself, it observes what the server already stored.
---
## Command Summary
| Command | What it does |
|---|---|
| `feedback report` | Render a terminal report of a company's feedback traces, with a summary and per-trace details. |
| `feedback export` | Write votes plus raw trace bundles into a folder and a `.zip` archive. |
| `feedback trace <traceId>` | Fetch a single feedback trace by ID. |
| `feedback bundle <traceId>` | Fetch a single feedback trace's bundle (the trace plus its attached files). |
All four subcommands accept the common client options described in [Common Options](./common-options.md): `--data-dir`, `--api-base`, `--api-key`, `--context`, `--profile`, and `--json`. `report` and `export` are company-scoped and additionally take `-C, --company-id <id>`.
> **Note:** `feedback report` and `feedback export` resolve the company from `-C/--company-id`, then `PAPERCLIP_COMPANY_ID`, then your CLI context default. If none of those is set, the command falls back to the **first** company returned by the API. On an instance with more than one company, always pass `--company-id` so you export the company you mean.
---
## Filtering Feedback Traces
`report` and `export` share the same filter flags. They narrow the set of traces the command reads from `/api/companies/<company-id>/feedback-traces` before it renders or writes anything.
| Flag | Use |
|---|---|
| `--target-type <type>` | Only include traces against this target type (for example a comment vs. a work product). |
| `--vote <vote>` | Only include traces with this vote value (`up` or `down`). |
| `--status <status>` | Only include traces in this export status (see the status table below). |
| `--project-id <id>` | Only include traces tied to this project. |
| `--issue-id <id>` | Only include traces tied to this issue. |
| `--from <iso8601>` | Only include traces created at or after this timestamp. |
| `--to <iso8601>` | Only include traces created at or before this timestamp. |
| `--shared-only` | Only include traces eligible for sharing/export. |
Use the time window and `--shared-only` together when you are preparing feedback to send outside the instance — it keeps the export to traces the server has marked as shareable, within the dates you care about.
### Trace status values
Every trace carries an export `status`. The report tallies these four, and they tell you where each piece of feedback sits in the sharing pipeline:
| Status | Meaning |
|---|---|
| `pending` | Recorded and awaiting export/share. |
| `sent` | Already shared/exported off the instance. |
| `local_only` | Kept on this instance; not eligible to leave it. |
| `failed` | An export/share attempt failed. |
---
## Report
`feedback report` reads the matching traces and prints a terminal report: a header with the server URL and company, a summary block, and a detail section for each trace.
```sh
paperclipai feedback report --company-id <company-id>
```
The summary counts thumbs up, thumbs down, downvotes that carried a written reason, and the total trace count, then breaks the traces down by export status. The detail section shows, per trace, the issue reference and title, a short trace ID, the status, the creation date, the target label, an excerpt of what was voted on, and the reason when one was given.
Narrow the report with any of the filter flags. For example, to look at just the downvotes for one project in the last week:
```sh
paperclipai feedback report \
--company-id <company-id> \
--vote down \
--project-id <project-id> \
--from 2026-05-25T00:00:00Z
```
To see the full raw payload for each trace inline, add `--payloads`:
```sh
paperclipai feedback report --company-id <company-id> --payloads
```
> **Tip:** `--payloads` dumps the complete `payloadSnapshot` JSON under each trace. It is verbose — reach for it when you are debugging exactly what was captured, not for a routine read.
For machine consumption, pass `--json`. The command then prints a structured object with `apiBase`, `companyId`, the `summary`, and the full `traces` array instead of the formatted terminal report:
```sh
paperclipai feedback report --company-id <company-id> --json
```
---
## Export
`feedback export` reads the matching traces, fetches each trace's bundle, and writes everything to a folder plus a sibling `.zip` archive. This is the command to use when feedback needs to leave the terminal — for a review, an archive, or a hand-off.
```sh
paperclipai feedback export --company-id <company-id> --out ./feedback/acme
```
If you omit `--out`, the command writes to `./feedback-export-<timestamp>` in the current directory. The output directory must not already exist with files in it — the command refuses to write into a non-empty directory so it never clobbers a previous export.
### What gets written
The export folder is laid out so you can read it by hand or feed it to another tool:
| Path | Contents |
|---|---|
| `index.json` | A manifest: when it was exported, the server URL, the company ID, a summary (counts plus `uniqueIssues` and the sorted list of issue identifiers), and the list of files written. |
| `votes/` | One JSON file per trace holding a flattened vote record (vote, target, status, consent version, timestamps, and the reason). |
| `traces/` | One JSON file per trace with the full trace object. |
| `full-traces/<issue>-<trace>/` | One directory per trace containing `bundle.json` and every file from that trace's bundle, written out at its original relative path. |
| `<output-dir>.zip` | A stored (uncompressed) zip archive of `index.json` and every file above, rooted at the export folder name. |
File names are built from the issue identifier (for example `PAP-39`) and a short slice of the trace or vote ID, so the export is browsable without opening anything.
### How export works for companies and issues
Export is always **company-scoped**: it pulls the traces for one company, then groups the resulting files by the **issue** each trace belongs to. The manifest's `summary.uniqueIssues` and `summary.issues` tell you exactly which issues are represented, and the per-file naming carries the issue identifier into `votes/`, `traces/`, and `full-traces/`. That means a single export answers both "how did this company's work score overall?" and "which issues drew the feedback?" without a second pass.
To export only what is shareable for a single issue:
```sh
paperclipai feedback export \
--company-id <company-id> \
--issue-id <issue-id> \
--shared-only \
--out ./feedback/<issue-id>
```
With `--json`, the command prints `companyId`, the resolved `outputDir`, the `zipPath`, and the export `summary` instead of the formatted summary:
```sh
paperclipai feedback export --company-id <company-id> --json
```
---
## Trace
`feedback trace` fetches a single feedback trace by its ID. Use it when you already have a trace ID — from a report, an export, or another tool — and want the raw record.
```sh
paperclipai feedback trace <trace-id>
paperclipai feedback trace <trace-id> --json
```
This is a direct read of `/api/feedback-traces/<trace-id>` and is not company-scoped — the ID identifies the trace on its own.
---
## Bundle
`feedback bundle` fetches a single trace's **bundle**: the trace plus the underlying files captured with it. This is the same material that `feedback export` writes into each `full-traces/` directory, but fetched for one trace at a time.
```sh
paperclipai feedback bundle <trace-id>
paperclipai feedback bundle <trace-id> --json
```
Reach for `bundle` when you need the full context behind a single vote — the actual files that were voted on — without running a whole company export.
---
## Scripting Notes
- Use `--json` on every subcommand when you are piping into `jq` or another tool. See [Output and Scripting](./output-and-scripting.md) for the conventions.
- `report --json` returns the same `summary` shape that `export --json` reports, so you can dry-run a filter with `report` before committing it to disk with `export`.
- Because `export` refuses a non-empty `--out` directory, scripts should generate a fresh path (or rely on the default timestamped directory) per run.
---
## See also
- [Issue Commands](./issue.md) — feedback traces are anchored to issues; use these to inspect the work that was voted on.
- [Company Commands](./company.md) — feedback is company-scoped; export and portability live here.
- [Common Options](./common-options.md) — the shared client flags every feedback subcommand accepts.
- [Output and Scripting](./output-and-scripting.md) — how `--json` output is structured for automation.
+152
View File
@@ -0,0 +1,152 @@
---
paperclip_version: v2026.609.0
---
# Goal Commands
Goals are the company's strategic objectives — the "why" that issues and projects ultimately serve. Reach for these commands when you want to set, inspect, restructure, or retire a company's goals from the terminal: building a goal tree, pointing a project at the goals it advances, or marking an objective as achieved. Goals are company-scoped, can nest into a hierarchy, and can be owned by an agent.
---
## How goals fit the model
A goal sits above the day-to-day work. Projects and issues hang off goals, not the other way around:
- A **project** is created against one or more goals — `project create` takes `--goal-ids`. The project is the vehicle that moves those goals forward. See [Project Commands](./project.md).
- An **issue** can carry a `goalId`, and when it doesn't, the server resolves a goal in order of precedence: the issue's own goal, then the project's goal, then the company's default goal. See [Issue Commands](./issue.md).
- A goal can have a **parent goal** (`--parent-id`), which lets you model a tree — a company-level goal with team-level or quarter-level sub-goals beneath it.
- A goal can have an **owner agent** (`--owner-agent-id`), the agent accountable for it.
Because goals anchor the whole hierarchy, treat them as durable. You will create them rarely, update their status as objectives progress, and delete almost never.
All goal commands accept the common client options (`--data-dir`, `--api-base`, `--api-key`, `--context`, `--profile`, `--json`). The collection commands (`list`, `create`) are company-scoped and take `-C, --company-id <id>`; the single-goal commands (`get`, `update`, `delete`) address a goal directly by its ID. See [Common Options](./common-options.md) for the shared flags and the API base resolution order.
---
## List goals
List every goal for a company.
```sh
paperclipai goal list --company-id <company-id>
paperclipai goal list --company-id <company-id> --json
```
| Flag | Use |
|---|---|
| `-C, --company-id <id>` | Company whose goals to list. Required (resolved from context if a company is selected). |
The default human output prints one line per goal showing `id`, `status`, `title`, `level`, `parentId`, and `ownerAgentId` — enough to read the tree at a glance. Use `--json` when you are scripting and want the full goal records.
> **Tip:** Pipe `goal list --json` through `jq` to find the children of a goal (`jq '[.[] | select(.parentId == "<goal-id>")]'`) or to pull out the company-level objectives (`jq '[.[] | select(.level == "company")]'`).
---
## Get a goal
Fetch a single goal by its ID.
```sh
paperclipai goal get <goal-id>
paperclipai goal get <goal-id> --json
```
This addresses the goal directly, so you do not pass `--company-id`. Use it to confirm a goal's current `status`, owner, or parent before you wire a project or issue to it.
---
## Create a goal
Create a goal in a company.
```sh
paperclipai goal create --company-id <company-id> --title "Grow revenue 30% this year"
```
| Flag | Use |
|---|---|
| `-C, --company-id <id>` | Company to create the goal in. **Required.** |
| `--title <title>` | The goal's title. **Required.** |
| `--description <text>` | Longer description of the objective. |
| `--level <level>` | The goal's level in the hierarchy (for example a company-level vs. team-level objective). |
| `--status <status>` | Initial status. |
| `--parent-id <id>` | Parent goal, to nest this goal under another. |
| `--owner-agent-id <id>` | Agent accountable for the goal. |
`--title` and `--company-id` are the only required inputs; everything else is optional and the server applies its own defaults for `level` and `status` when you omit them. The command prints the created goal (pass `--json` for the raw record), so capture the returned `id` to use in `project create --goal-ids` or on an issue's `goalId`.
A nested example — a company objective with a team sub-goal owned by an agent:
```sh
# Top-level company goal
paperclipai goal create --company-id <company-id> \
--title "Reach profitability" --level company
# Sub-goal that rolls up into it, owned by the growth lead
paperclipai goal create --company-id <company-id> \
--title "Launch paid tier" \
--parent-id <parent-goal-id> \
--owner-agent-id <agent-id>
```
> **Note:** Pass `--owner-agent-id` only when an agent should be accountable for the goal. Ownership is an accountability signal, not an assignment — agents pick up actual work through issues, not goals.
---
## Update a goal
Update a goal's fields. Every field is optional; send only what you want to change.
```sh
paperclipai goal update <goal-id> --status achieved
paperclipai goal update <goal-id> --title "Grow revenue 40% this year" --description "Stretch target after Q2 beat"
```
| Flag | Use |
|---|---|
| `--title <title>` | New title. |
| `--description <text\|null>` | New description, or `null` to clear it. |
| `--level <level>` | New level. |
| `--status <status>` | New status — this is how you mark a goal active, achieved, or abandoned as it progresses. |
| `--parent-id <id\|null>` | Re-parent the goal, or pass `null` to promote it to a top-level goal. |
| `--owner-agent-id <id\|null>` | Reassign the owning agent, or `null` to clear ownership. |
For the nullable fields — `--description`, `--parent-id`, and `--owner-agent-id` — the literal value `null` clears the field, while any other value sets it. This is the only way to detach a goal from its parent or remove its owner without deleting the goal.
```sh
# Promote a sub-goal to the top level and hand it off
paperclipai goal update <goal-id> --parent-id null --owner-agent-id <new-owner-id>
# Clear an owner entirely
paperclipai goal update <goal-id> --owner-agent-id null
```
The command does not address goals by company, so you do not pass `--company-id`.
---
## Delete a goal
Delete a goal. This is guarded — you must pass `--yes` or the command refuses.
```sh
paperclipai goal delete <goal-id> --yes
```
| Flag | Use |
|---|---|
| `--yes` | Confirm the deletion. Without it the command errors with "Deletion requires --yes." |
Deleting a goal is a structural change: projects created against it and issues that referenced its `goalId` lose that link, and any child goals lose their parent. Prefer updating a goal's `status` to "achieved" or "abandoned" over deleting it — that keeps the strategic history intact. Delete only when a goal was created in error.
> **Warning:** `goal delete` requires the explicit `--yes` flag because removing a goal can orphan the projects and issues that pointed at it. Confirm you have nothing live hanging off the goal before you run it.
---
## See also
- [Project Commands](./project.md) — create projects against goals with `--goal-ids`
- [Issue Commands](./issue.md) — set an issue's `goalId` and how goal precedence resolves
- [Company Commands](./company.md) — select and inspect the company that owns the goals
- [Common Options](./common-options.md) — shared flags and API base resolution
- [Output and Scripting](./output-and-scripting.md) — using `--json` to script against goal data
+163
View File
@@ -0,0 +1,163 @@
---
paperclip_version: v2026.609.0
---
# Installing the CLI
The Paperclip CLI ships as a single binary, `paperclipai`. Use this page to get that binary on your machine, point it at an instance, and bring up a working local instance from a cold start. The shortest route from "nothing installed" to "a healthy server you can operate against" is two commands: `paperclipai onboard` to write a config, then `paperclipai run` to launch it.
---
## Prerequisites
The CLI is a Node program. You need Node.js 20 or newer on `PATH` before anything else works.
```sh
node --version # must report v20.x or higher
```
That single requirement covers the common local install: `paperclipai run` can stand up an embedded PostgreSQL instance for you, so you do not need a separate database server to start. If you intend to point the CLI at your own PostgreSQL, have its connection string ready and the CLI will use it instead.
---
## Getting the `paperclipai` binary
`paperclipai` is published to npm, so the fastest path needs no install at all — run it with `npx`:
```sh
npx paperclipai onboard --yes
```
That is the canonical way to start, and it always runs the latest published version. If you would rather have a persistent `paperclipai` command on your `PATH`, install it globally:
```sh
npm install -g paperclipai
# verify it is on your PATH
paperclipai --help
```
The package exposes exactly one binary, `paperclipai`. **Every example in this documentation is written in the form `paperclipai <command>`.** If you did not install globally, prepend `npx` — running it through `npx` is equivalent to calling the global binary directly.
> **Note:** If you are working inside a clone of the Paperclip monorepo, use the in-repo development alias `pnpm paperclipai <command>` instead. It runs the same CLI straight from source via `tsx`, which is what you want while developing the CLI itself. The monorepo also documents `npx paperclipai onboard --yes` as the standard quickstart for a packaged install.
---
## First run: bootstrap a local instance
Two commands take you from an empty machine to a running server.
```sh
paperclipai onboard
paperclipai run
```
`onboard` writes the instance config; `run` validates it and starts the server. You can also let `run` do everything — if no config exists yet and you are in an interactive terminal, `run` calls onboarding for you before it boots.
---
### `paperclipai onboard`
`onboard` is the interactive first-run wizard. It writes a config file plus the local key material the server needs, then offers to start Paperclip immediately. Reach for it when you are setting up a new local install or rebuilding a config from guided prompts.
```sh
paperclipai onboard
```
The first prompt asks you to choose a setup path:
| Path | What you get |
|---|---|
| **Quickstart** | Local defaults that are ready to run: an embedded PostgreSQL database, local-disk file storage, and local encrypted secrets. This is the recommended path. |
| **Advanced setup** | Step-by-step prompts for the database, LLM provider, logging, server binding, storage, and secrets. Use this when you need explicit control over any of those. |
During onboarding the CLI also ensures the agent JWT secret (`PAPERCLIP_AGENT_JWT_SECRET`) and the local secrets key file exist, creating them if they are missing. On Advanced setup, if you supply a PostgreSQL connection string or an LLM API key, the wizard will test the connection and validate the key inline so you find problems before the server starts.
When it finishes, onboarding prints the resolved configuration and the next commands to run.
| Flag | Use |
|---|---|
| `--yes`, `-y` | Accept Quickstart defaults non-interactively and start immediately. Without `--bind`, this forces trusted-local loopback defaults, ignoring any reachability env vars. Use it in scripts and CI. |
| `--run` | Save the config, then start the server right away (equivalent to answering "yes" to the start prompt). |
| `--bind <mode>` | Apply a reachability preset to the Quickstart server config. One of `loopback`, `lan`, or `tailnet`. |
| `--config <path>`, `-c` | Write the config to a specific path instead of the default instance config. |
| `--data-dir <path>`, `-d` | Isolate all local Paperclip state away from `~/.paperclip` — handy for clean test instances and worktrees. |
```sh
# Non-interactive Quickstart that boots immediately
paperclipai onboard --yes
# Save the config and start, but keep interactive prompts
paperclipai onboard --run
# Quickstart bound to the LAN instead of loopback
paperclipai onboard --bind lan
```
> **Note:** If Paperclip is already configured at the target path, rerunning `onboard` detects the existing install and keeps the current configuration unchanged. It still tops up the agent JWT secret and secrets key file if needed, then offers to start. To change settings on an existing install, use `paperclipai configure` rather than re-onboarding.
---
### `paperclipai run`
`run` is the one-command path to a healthy local instance. It bootstraps and launches in sequence:
1. Resolves the active instance and config path, and loads the instance env file.
2. If no config exists and the terminal is interactive, hands off to onboarding first.
3. Runs the `doctor` checks with repair enabled by default. If any check fails, it stops before starting the server.
4. Starts the Paperclip server.
```sh
paperclipai run
```
| Flag | Use |
|---|---|
| `--instance <id>`, `-i` | Select the local instance id (default: `default`). Use this to run more than one isolated instance side by side. |
| `--bind <mode>` | On a *first* run (when no config exists yet), pass a reachability preset into onboarding: `loopback`, `lan`, or `tailnet`. |
| `--repair` / `--no-repair` | Repair is on by default; `--no-repair` runs `doctor` in read-only mode so it reports problems without changing files. |
| `--config <path>`, `-c` | Use a specific config file. |
| `--data-dir <path>`, `-d` | Isolate local state away from `~/.paperclip`. |
```sh
# Run a second, isolated instance
paperclipai run --instance dev
# Keep all state inside a worktree
paperclipai run --data-dir ./tmp/paperclip-dev
# Diagnose without letting doctor modify anything
paperclipai run --no-repair
```
> **Tip:** In a non-interactive environment (no TTY), `run` will not start onboarding for you. Run `paperclipai onboard` once first, then retry `paperclipai run`.
> **Warning:** `run` (with no subcommand) is a *local bootstrap* command — it stands up a server on your machine. The `run` *subcommands* (`run list`, `run live`, `run get`, `run events`, `run log`, and the rest) are a different concept entirely: they inspect and control heartbeat runs through the API on an already-running instance. Do not confuse the two. See [run](./run.md) for the subcommands.
---
## What happens to your data
`onboard` and `run` write everything under a per-instance home directory. The default instance lives at `~/.paperclip`, with the config, embedded database, logs, storage, and secrets key kept beneath it. Pass `--data-dir <path>` to relocate that root, or `--instance <id>` to keep multiple instances cleanly separated.
The CLI also handles two pieces of credential material during setup so you never wire them by hand: the agent JWT secret (stored in the instance env file as `PAPERCLIP_AGENT_JWT_SECRET`) and the local secrets master key file. Both are created on first onboarding and reused thereafter.
---
## After the server is up
Bringing the server up does not yet give the CLI an authenticated identity to operate a company. Once `run` reports that Paperclip is listening, connect the CLI to the instance and pick a persona:
- On a fresh interactive machine, run the connection wizard, which resolves the API base, health-checks it, logs you in as the board, mints a token, and saves a profile.
- For headless and CI setups, mint a board token or an agent key and point the CLI at it explicitly.
That step is covered in [authentication](./authentication.md). For how the CLI finds your server (the `--api-base` / `PAPERCLIP_API_URL` resolution order) and the flags shared by every client command, see [common options](common-options.md).
---
## Next steps
- [Authentication](./authentication.md) — connect the CLI, choose a persona, and mint board tokens or agent keys.
- [Setup commands](./setup-commands.md) — `doctor`, `configure`, `env`, and `allowed-hostname` for repairing and tuning an instance.
- [Common options](common-options.md) — shared flags and how the CLI resolves which server to talk to.
- [run](./run.md) — the `run` subcommands for inspecting heartbeat runs on a live instance.
- [Overview](overview.md) — what the CLI is for and how it fits the wider operating model.
+399
View File
@@ -0,0 +1,399 @@
---
paperclip_version: v2026.609.0
---
# Issue Commands
Issues are the core work objects in Paperclip — every piece of work an agent does lives on an issue, and Paperclip's communication model is tasks plus comments, not chat. Reach for `paperclipai issue` when you want to create, find, move, annotate, or coordinate work from the terminal, or when you need to drive any of the issue subresources (documents, work products, interactions, tree holds, attachments, labels, approvals, recovery, runs, and feedback) without opening the UI.
This is the largest command surface in the CLI. It is organized below into clear sections: start with the core CRUD/comments/checkout/release commands, then drop into a subresource section when you need it.
Most issue-scoped commands accept either the issue UUID or its human identifier (for example `PC-12`). Collection and creation commands are company-scoped and need a company ID.
> **Note:** All `issue` commands accept the [common client flags](./common-options.md) — `--api-base`, `--api-key`, `--context`, `--profile`, `--data-dir`, and `--json`. Company-scoped commands additionally take `-C, --company-id <id>`. Those flags are not repeated in every table below.
---
## Core: list, get, create, update, delete
Use these for everyday work — finding tasks, inspecting one, creating new work, moving it, and annotating it.
```sh
paperclipai issue list -C <company-id> --status todo,in_review --assignee-agent-id <agent-id> --match "billing"
paperclipai issue get PC-12
paperclipai issue create -C <company-id> --title "Fix invoice rounding" --priority high --assignee-agent-id <agent-id>
paperclipai issue update <issue-id> --status in_review --comment "Ready for review"
paperclipai issue delete <issue-id> --yes
```
`list` is company-scoped and supports server-side and local filtering:
| Flag | Use |
|---|---|
| `-C, --company-id <id>` | Company to list issues for (required if not in context). |
| `--status <csv>` | Comma-separated statuses, e.g. `todo,in_review`. |
| `--assignee-agent-id <id>` | Filter by assignee agent. |
| `--project-id <id>` | Filter by project. |
| `--match <text>` | Local case-insensitive match on identifier, title, and description after the server responds. |
`get <idOrIdentifier>` resolves a UUID or identifier (like `PC-12`) and returns the full issue.
`create` requires a company and a title. The rest are optional:
| Flag | Use |
|---|---|
| `-C, --company-id <id>` | Company to create the issue in (required). |
| `--title <title>` | Issue title (required). |
| `--description <text>` | Issue description. |
| `--status <status>` | Initial status. |
| `--priority <priority>` | Priority. |
| `--assignee-agent-id <id>` | Assign on creation. |
| `--project-id <id>` | Attach to a project. |
| `--goal-id <id>` | Attach to a goal. |
| `--parent-id <id>` | Make this a child of another issue. |
| `--request-depth <n>` | Integer request depth. |
| `--billing-code <code>` | Billing code. |
`update <issueId>` patches any subset of the same fields, plus a couple that only make sense on update:
| Flag | Use |
|---|---|
| `--title` / `--description` / `--status` / `--priority` | Patch core fields. |
| `--assignee-agent-id` / `--project-id` / `--goal-id` / `--parent-id` | Reassign or re-parent. |
| `--request-depth <n>` / `--billing-code <code>` | Patch depth and billing. |
| `--comment <text>` | Add a comment in the same call as the update. |
| `--hidden-at <iso8601\|null>` | Set the `hiddenAt` timestamp, or pass the literal `null` to clear it. |
`delete <issueId>` refuses to run without `--yes`. There is no soft prompt — pass the flag deliberately.
> **Note:** `issue heartbeat-context <issueId>` returns the heartbeat context the server would assemble for the issue. It is a read-only inspection command, useful when debugging why an agent did or did not pick up a task.
---
## Checkout and release
Checkout is how the single-assignee model is enforced at the CLI layer: an agent claims an issue, works it, then releases it.
```sh
paperclipai issue checkout <issue-id> --agent-id <agent-id>
paperclipai issue checkout <issue-id> --agent-id <agent-id> --expected-statuses todo,in_review
paperclipai issue release <issue-id>
```
| Flag | Use |
|---|---|
| `--agent-id <id>` | Agent claiming the issue (required). |
| `--expected-statuses <csv>` | Statuses the issue must currently be in for the checkout to succeed. Defaults to `todo,backlog,blocked`. |
`checkout` only succeeds when the issue is in one of the expected statuses — this is an optimistic guard against two agents grabbing the same work. `release` returns the issue to `todo` and clears the assignee.
> **Tip:** When an agent is stuck holding an issue it cannot release itself, use `issue force-release` (below) instead of `release`.
---
## Comments
Communication on an issue is a thread of comments. Adding a comment can also reopen finished work or wake the assignee.
```sh
paperclipai issue comment <issue-id> --body "Picking this up now"
paperclipai issue comment <issue-id> --body "Re-opening, the fix regressed" --reopen
paperclipai issue comment <issue-id> --body "Any update?" --resume
paperclipai issue comments <issue-id> --order asc --limit 50
paperclipai issue comment:get <issue-id> <comment-id>
paperclipai issue comment:delete <issue-id> <comment-id>
```
| Command | Use |
|---|---|
| `comment <issueId> --body <text>` | Add a comment. `--reopen` reopens an issue that is done or cancelled; `--resume` requests an explicit follow-up and wakes the assignee when the work is resumable. |
| `comments <issueId>` | List comments. Supports `--after-comment-id <id>`, `--order asc\|desc`, and `--limit <n>`. |
| `comment:get <issueId> <commentId>` | Fetch one comment. |
| `comment:delete <issueId> <commentId>` | Delete or cancel one comment. |
---
## Documents
Issues carry keyed markdown documents — a structured, revisioned place to keep specs, plans, and deliverables separate from the comment thread.
```sh
paperclipai issue documents <issue-id> --include-system
paperclipai issue document:get <issue-id> spec
paperclipai issue document:put <issue-id> spec --title "Spec" --body-file ./spec.md --change-summary "Initial draft"
paperclipai issue document:lock <issue-id> spec
paperclipai issue document:revisions <issue-id> spec
paperclipai issue document:restore <issue-id> spec <revision-id>
```
| Command | Use |
|---|---|
| `documents <issueId>` | List documents. Add `--include-system` to include system-managed documents. |
| `document:get <issueId> <key>` | Fetch one document by its key. |
| `document:put <issueId> <key>` | Create or update a document (see flags below). |
| `document:delete <issueId> <key>` | Delete a document. |
| `document:lock <issueId> <key>` / `document:unlock <issueId> <key>` | Lock or unlock a document against edits. |
| `document:revisions <issueId> <key>` | List the document's revision history. |
| `document:restore <issueId> <key> <revisionId>` | Restore a previous revision. |
`document:put` flags:
| Flag | Use |
|---|---|
| `--title <title>` | Document title. |
| `--format <format>` | Document format. Defaults to `markdown`. |
| `--body <markdown>` | Inline body. |
| `--body-file <path>` | Read the body from a file instead of `--body`. |
| `--change-summary <text>` | Summary recorded with the new revision. |
| `--base-revision-id <id>` | Expected current revision, for optimistic concurrency — the write fails if the document has moved past it. |
> **Tip:** Prefer `--body-file` for anything multi-line. It keeps long markdown out of your shell history and avoids quoting headaches.
---
## Work products
Work products are the structured deliverables an agent produces against an issue. Create and update take a JSON payload so the full shape is available without a flag for every field.
```sh
paperclipai issue work-products <issue-id>
paperclipai issue work-product:create <issue-id> --payload-json '{"kind":"report","title":"Q2 analysis"}'
paperclipai issue work-product:update <work-product-id> --payload-json '{"title":"Q2 analysis (final)"}'
paperclipai issue work-product:delete <work-product-id>
```
| Command | Use |
|---|---|
| `work-products <issueId>` | List an issue's work products. |
| `work-product:create <issueId> --payload-json <json>` | Create a work product from a `CreateIssueWorkProduct` JSON payload. |
| `work-product:update <workProductId> --payload-json <json>` | Update a work product from an `UpdateIssueWorkProduct` JSON payload. Note this takes the work product ID, not the issue ID. |
| `work-product:delete <workProductId>` | Delete a work product. |
---
## Interactions
Interactions are structured exchanges on an issue's thread — most notably `ask_user_questions`, where an agent pauses for input. Use these to inspect a pending interaction and accept, reject, cancel, or respond to it.
```sh
paperclipai issue interactions <issue-id>
paperclipai issue interaction:create <issue-id> --payload-json '{ ... }'
paperclipai issue interaction:accept <issue-id> <interaction-id> --selected-client-keys a,b
paperclipai issue interaction:reject <issue-id> <interaction-id> --reason "Out of scope"
paperclipai issue interaction:cancel <issue-id> <interaction-id> --reason "No longer needed"
paperclipai issue interaction:respond <issue-id> <interaction-id> --answers-json '[{"key":"q1","value":"yes"}]' --summary-markdown "Confirmed scope"
```
| Command | Use |
|---|---|
| `interactions <issueId>` | List thread interactions. |
| `interaction:create <issueId> --payload-json <json>` | Create an interaction from a `CreateIssueThreadInteraction` payload. |
| `interaction:accept <issueId> <interactionId>` | Accept an interaction. `--selected-client-keys <csv>` narrows which client keys are accepted. |
| `interaction:reject <issueId> <interactionId>` | Reject an interaction. Optional `--reason <text>`. |
| `interaction:cancel <issueId> <interactionId>` | Cancel an `ask_user_questions` interaction. Optional `--reason <text>`. |
| `interaction:respond <issueId> <interactionId>` | Answer a question interaction. `--answers-json <json>` is the answers array (required); `--summary-markdown <markdown>` is an optional summary. |
---
## Tree state, preview, and holds
The tree-control surface operates on an issue and everything beneath it. Inspect the tree state, preview a control change before committing, and place or release holds (pause / resume / cancel / restore) across the whole subtree.
```sh
paperclipai issue tree-state <root-issue-id>
paperclipai issue tree-preview <root-issue-id> --payload-json '{"mode":"pause"}'
paperclipai issue tree-holds <root-issue-id> --status active --mode pause --include-members
paperclipai issue tree-hold:create <root-issue-id> --payload-json '{"mode":"pause","reason":"awaiting budget"}'
paperclipai issue tree-hold:get <root-issue-id> <hold-id>
paperclipai issue tree-hold:release <root-issue-id> <hold-id> --payload-json '{}'
```
| Command | Use |
|---|---|
| `tree-state <issueId>` | Get the current tree control state for a root issue. |
| `tree-preview <issueId> --payload-json <json>` | Preview the effect of a `PreviewIssueTreeControl` change without applying it. |
| `tree-holds <issueId>` | List holds. Filter with `--status active\|released`, `--mode pause\|resume\|cancel\|restore`, and `--include-members`. |
| `tree-hold:create <issueId> --payload-json <json>` | Place a hold from a `CreateIssueTreeHold` payload. |
| `tree-hold:get <issueId> <holdId>` | Inspect one hold. |
| `tree-hold:release <issueId> <holdId>` | Release a hold. `--payload-json <json>` defaults to `{}`. |
> **Tip:** Always run `tree-preview` before a broad pause or cancel. A hold on a root issue fans out across the entire subtree, and the preview tells you exactly which issues it will touch.
---
## Attachments
Attach files to an issue or to a specific comment, list them, download them, and delete them.
```sh
paperclipai issue attachments <issue-id>
paperclipai issue attachment:upload <issue-id> -C <company-id> --file ./diagram.png
paperclipai issue attachment:upload <issue-id> -C <company-id> --file ./log.txt --comment-id <comment-id>
paperclipai issue attachment:download <attachment-id> --out ./diagram.png
paperclipai issue attachment:delete <attachment-id>
```
| Command | Use |
|---|---|
| `attachments <issueId>` | List an issue's attachments. |
| `attachment:upload <issueId>` | Upload a file. Requires `-C, --company-id <id>` and `--file <path>`; `--comment-id <id>` attaches it to a specific comment. |
| `attachment:download <attachmentId>` | Download an attachment. `--out <path>` writes to a file; without it the bytes stream to stdout. |
| `attachment:delete <attachmentId>` | Delete an attachment. |
> **Note:** `attachment:download` without `--out` writes raw bytes to stdout — redirect to a file (`> out.bin`) for anything that is not plain text.
---
## Labels
Labels are company-scoped and shared across all issues in a company.
```sh
paperclipai issue label:list -C <company-id>
paperclipai issue label:create -C <company-id> --name "needs-review" --color "#4f46e5"
paperclipai issue label:delete <label-id>
```
| Command | Use |
|---|---|
| `label:list` | List labels in a company. Company-scoped via `-C`. |
| `label:create` | Create a label. Requires `--name <name>` and `--color <hex>` (for example `#4f46e5`). Company-scoped via `-C`. |
| `label:delete <labelId>` | Delete a label by ID. |
---
## Approvals
Link existing approvals to an issue, list them, and unlink. To create approvals themselves, see the [approval commands](approval.md).
```sh
paperclipai issue approvals <issue-id>
paperclipai issue approval:link <issue-id> <approval-id>
paperclipai issue approval:unlink <issue-id> <approval-id>
```
| Command | Use |
|---|---|
| `approvals <issueId>` | List approvals linked to the issue. |
| `approval:link <issueId> <approvalId>` | Link an existing approval. |
| `approval:unlink <issueId> <approvalId>` | Remove the link. |
---
## Read and archive state
These mark an issue's inbox state for the current credential — useful when scripting an inbox triage loop.
```sh
paperclipai issue read <issue-id>
paperclipai issue unread <issue-id>
paperclipai issue archive <issue-id>
paperclipai issue unarchive <issue-id>
```
| Command | Use |
|---|---|
| `read <issueId>` | Mark the issue as read. |
| `unread <issueId>` | Mark the issue as unread. |
| `archive <issueId>` | Archive the issue from the inbox. |
| `unarchive <issueId>` | Restore the issue to the inbox. |
---
## Recovery and force-release
When work gets stuck — an issue wedged in a checkout, or a recovery action the server has flagged — use these to unblock it.
```sh
paperclipai issue recovery-actions <issue-id>
paperclipai issue recovery:resolve <issue-id> --outcome restored --source-issue-status todo --resolution-note "Re-queued after restart"
paperclipai issue force-release <issue-id>
paperclipai issue child:create <parent-issue-id> --payload-json '{"title":"Subtask"}'
```
| Command | Use |
|---|---|
| `recovery-actions <issueId>` | List active recovery actions for the issue. |
| `recovery:resolve <issueId>` | Resolve a recovery action (see flags below). |
| `force-release <issueId>` | Force-release an issue from an agent checkout. This is the admin escape hatch when `release` is not enough. |
| `child:create <issueId> --payload-json <json>` | Create a child issue under a parent from a `CreateChildIssue` payload. |
`recovery:resolve` flags:
| Flag | Use |
|---|---|
| `--outcome <outcome>` | One of `restored`, `false_positive`, `blocked`, or `cancelled` (required). |
| `--source-issue-status <status>` | For `restored` outcomes use `todo`, `done`, or `in_review`; `blocked` is only valid for the `blocked` outcome (required). |
| `--action-id <id>` | Target a specific recovery action when more than one is active. |
| `--resolution-note <text>` | Note recorded with the resolution. |
> **Warning:** `force-release` overrides an agent's active checkout. Use it only when the agent is genuinely stuck — a healthy agent should release its own work.
---
## Runs
These read the heartbeat runs tied to an issue — what executed, what is in flight, and what is active right now. These are the server-side runs an issue produced; they are distinct from the local `run` bootstrap command. See the [run commands](run.md) for the run surface itself.
```sh
paperclipai issue runs <issue-id>
paperclipai issue live-runs <issue-id>
paperclipai issue active-run <issue-id>
```
| Command | Use |
|---|---|
| `runs <issueId>` | List all heartbeat runs associated with the issue. |
| `live-runs <issueId>` | List only queued and running runs. |
| `active-run <issueId>` | Show the single active run, or null if none. |
All three accept an issue UUID or identifier.
---
## Feedback votes and traces
Feedback captures votes and traces against an issue and its targets — the raw material for evaluating agent output. See the [feedback commands](feedback.md) for the broader feedback surface.
```sh
paperclipai issue feedback:votes <issue-id>
paperclipai issue feedback:vote <issue-id> --payload-json '{"targetType":"comment","targetId":"<id>","vote":"up"}'
paperclipai issue feedback:list <issue-id> --vote down --status open --include-payload
paperclipai issue feedback:export <issue-id> --from 2026-01-01 --format ndjson --out ./feedback.ndjson
```
| Command | Use |
|---|---|
| `feedback:votes <issueId>` | List feedback votes for the issue. |
| `feedback:vote <issueId> --payload-json <json>` | Create or update a vote from an `UpsertIssueFeedbackVote` payload. |
| `feedback:list <issueId>` | List feedback traces (filter flags below). |
| `feedback:export <issueId>` | Export feedback traces to stdout or a file. |
Filter and export flags shared by `feedback:list` and `feedback:export`:
| Flag | Use |
|---|---|
| `--target-type <type>` | Filter by target type. |
| `--vote <vote>` | Filter by vote value. |
| `--status <status>` | Filter by trace status. |
| `--from <iso8601>` / `--to <iso8601>` | Bound the created-at window. |
| `--shared-only` | Only traces eligible for sharing/export. |
| `--include-payload` | Include stored payload snapshots. `feedback:export` includes payloads by default. |
`feedback:export` adds:
| Flag | Use |
|---|---|
| `--out <path>` | Write the export to a file instead of stdout. |
| `--format <format>` | `json` or `ndjson`. Defaults to `ndjson`. |
---
## See also
- [Common options](./common-options.md) — flags shared by every client command.
- [Output and scripting](./output-and-scripting.md) — using `--json` in pipelines.
- [Approval commands](approval.md) — create and decide the approvals you link here.
- [Run commands](run.md) — inspect and control heartbeat runs.
- [Feedback commands](feedback.md) — the wider feedback and trace surface.
- [Goal commands](goal.md) and [Project commands](project.md) — the containers issues attach to.
+230
View File
@@ -0,0 +1,230 @@
---
paperclip_version: v2026.609.0
---
# Output & Scripting
Use this page when you are wiring `paperclipai` into a script, a CI step, or an AI operator loop rather than reading its output by eye. It explains the two output modes (`--json` vs. the human renderer), how the CLI signals success and failure through exit codes and `stderr`, and the practical patterns for capturing IDs, piping to `jq`, and running every command non-interactively.
Every behavior described here comes from the shared client layer in `cli/src/commands/client/common.ts`, which all control-plane commands route through. The rules are uniform: if a command takes `--json`, it renders the same way as every other command, and it fails the same way too.
---
## Two output modes
Each client command prints its result through a single shared renderer. The renderer has exactly two modes, selected by the `--json` flag.
| Mode | Flag | What you get |
|---|---|---|
| Human | (default) | A compact, line-oriented rendering tuned for reading in a terminal |
| Machine | `--json` | The raw API result, pretty-printed as JSON with two-space indentation |
> **Tip:** When you are scripting against the CLI or piping into another tool, always pass `--json`. The human format is for humans and is not a stable contract — column order and truncation can change between releases. The JSON is the API payload and is what you should parse.
### What `--json` prints
With `--json`, the CLI runs `JSON.stringify(data, null, 2)` on the API result and writes nothing else to `stdout`. There are no banners, no labels, no color codes — just the JSON document, ready to pipe.
```sh
paperclipai issue get PAP-39 --json
```
```json
{
"id": "244c0c2c-8416-43b6-84c9-ec183c074cc1",
"identifier": "PAP-39",
"title": "Implement caching layer",
"status": "in_progress",
"priority": "high"
}
```
### What human mode prints
Without `--json`, the renderer adapts to the shape of the result:
- **Arrays** print one item per line. Each object item is flattened into a `key=value` line; the renderer leads with `identifier`, `id`, `name`, `status`, `priority`, `title`, and `action` (in that order, when present), then appends the remaining scalar fields. Nested objects are omitted from this line and long strings are truncated to ~90 characters. An empty array prints `(empty)`.
- **Single objects** print as pretty JSON — the same two-space format as `--json`. So for a single-record `get`, human mode and `--json` look nearly identical.
- **Scalars** (a string, number, or boolean) print as themselves. `null`/`undefined` prints `(null)`.
```sh
paperclipai issue list --company-id <company-id> --status todo,in_progress
```
```text
identifier=PAP-39 id=244c0c2c... status=in_progress priority=high title=Implement caching layer
identifier=PAP-41 id=9b1f0a2c... status=todo priority=medium title=Add request tracing
```
That `key=value` listing is convenient to skim, but it drops nested fields and truncates strings — never parse it. The moment you need a value out of a list, switch to `--json` and a JSON parser.
---
## Exit codes and error behavior
The CLI follows the standard shell convention: a command that succeeds exits `0`; a command that fails exits non-zero and writes its error to `stderr`.
| Outcome | Exit code | Stream | Shape |
|---|---|---|---|
| Success | `0` | `stdout` | The rendered result (JSON with `--json`) |
| API error | `1` | `stderr` | `API error <status>: <message>[ details=<json>]` |
| Other error | `1` | `stderr` | The error message text |
All errors are routed through one handler, so the contract is consistent across commands. An API error (an HTTP error response from the Paperclip server) is printed as `API error <status>: <message>`, with a `details=<json>` suffix when the server returned a structured detail payload — for example `API error 409: ...` for a checkout conflict, or `API error 401: ...` for an auth failure. Any other failure (a bad flag, a missing company ID, a connection refused) prints its plain message. Both exit `1`.
Because results go to `stdout` and errors go to `stderr`, you can separate them cleanly:
```sh
# Capture JSON output, let errors flow to the terminal
result=$(paperclipai issue get PAP-39 --json) || exit 1
# Discard human chatter, keep only errors
paperclipai issue list --company-id <company-id> --json 2>/tmp/pc-errors.log
```
> **Note:** A non-zero exit always means the command did not complete. Check it. In a pipeline like `paperclipai ... --json | jq ...`, the exit status you see is `jq`'s, not the CLI's — set `set -o pipefail` (bash) so an upstream failure fails the whole pipeline.
### Connection errors
If the CLI cannot reach the API, the error message includes the URL it tried and a hint to check `GET /api/health` at that base. When you see this, the fix is almost always the API base resolution (below) pointing somewhere the server is not listening. See [Common Options](common-options.md) for the full resolution order.
---
## Running non-interactively
For a script or an autonomous agent, the cardinal rule is: never let a command block on a prompt. Two things can trigger interactivity, and both are avoidable.
**1. Interactive board re-authentication.** When a command hits a `401`, or a `403` that needs board or instance-admin access, the CLI may try to recover by launching an interactive board login. It only does this when both `stdin` and `stdout` are TTYs *and* no explicit `--api-key` was supplied. In a script — where output is piped or redirected, so `stdout` is not a TTY — this recovery is skipped and the command simply fails with the API error. To guarantee non-interactive behavior regardless of environment, always pass credentials explicitly:
```sh
paperclipai issue list \
--company-id <company-id> \
--api-base https://paperclip.example.com \
--api-key "$PAPERCLIP_API_KEY" \
--json
```
Passing `--api-key` (or setting `PAPERCLIP_API_KEY`) disables the interactive recovery path entirely, so a credential problem fails fast instead of hanging.
**2. Confirmation prompts.** Destructive commands prompt for confirmation in a TTY and require an explicit flag to proceed without one. Pass `--yes` where a command supports it (for example `skills remove`, `skills reset`), and supply the full confirmation flags where they are mandated — `company delete` requires both `--yes` and `--confirm <id>` and is additionally gated server-side. See [Company](./company.md) for that flow.
### Credential resolution for scripts
The API key is resolved in this order: `--api-key`, then `PAPERCLIP_API_KEY`, then the env var named by your context profile's `apiKeyEnvVarName`. For automation, prefer one of the first two so the run does not depend on a profile being present on the machine. The company ID resolves from `--company-id` (alias `-C` on commands like `run`), then `PAPERCLIP_COMPANY_ID`, then the profile default; if a company-scoped command finds none, it fails with a clear message telling you to pass `--company-id` or set the env var.
A clean, profile-independent invocation for CI looks like:
```sh
export PAPERCLIP_API_URL="https://paperclip.example.com"
export PAPERCLIP_API_KEY="<token>"
export PAPERCLIP_COMPANY_ID="<company-id>"
paperclipai issue list --status todo --json
```
> **Tip:** Use `--data-dir <path>` to isolate all local state away from `~/.paperclip` when you run the CLI in CI or on a shared host. It keeps context, config, and credentials from leaking between jobs. See [Common Options](common-options.md).
---
## Capturing IDs and piping to jq
Most scripting comes down to: run a command, pull an ID or status out of the JSON, feed it to the next command. `--json` plus `jq` covers nearly all of it.
### Pull a single field
```sh
# Create an issue and capture its ID for the next step
issue_id=$(paperclipai issue create \
--company-id <company-id> \
--title "Ship the export pipeline" \
--status todo --priority high \
--json | jq -r '.id')
paperclipai issue comment "$issue_id" --body "Kicking this off." --json
```
The `-r` flag makes `jq` emit the raw string without quotes — exactly what you want for shell variables and URLs.
### Filter and reshape a list
```sh
# IDs of every todo issue, one per line
paperclipai issue list --company-id <company-id> --status todo --json \
| jq -r '.[].id'
# Just the identifier and title, as TSV
paperclipai issue list --company-id <company-id> --json \
| jq -r '.[] | [.identifier, .title] | @tsv'
# Count issues by status
paperclipai issue list --company-id <company-id> --json \
| jq 'group_by(.status) | map({status: .[0].status, count: length})'
```
### Drive a loop over results
```sh
# Release every issue currently assigned to a given agent
paperclipai issue list --company-id <company-id> \
--assignee-agent-id <agent-id> --status in_progress --json \
| jq -r '.[].id' \
| while read -r id; do
paperclipai issue release "$id" --json
done
```
### Sending JSON payloads in
The flow runs both ways. Commands that map to broad server schemas take a JSON payload via `--payload-json`, and the body-bearing skills command reads from `stdin`:
```sh
# Build a payload with jq, pass it straight in
paperclipai agent create --company-id <company-id> \
--payload-json "$(jq -nc '{name:"Builder", adapterType:"codex_local"}')"
# Author a skill body from stdin
cat house-style.md | paperclipai skills create \
--name "House Style" --slug house-style --body-file -
```
> **Warning:** When you shell-interpolate a payload, quote it. An unquoted `--payload-json` with spaces or shell metacharacters will be split or mangled before the CLI ever sees it. Single-quote literals, or build the JSON with `jq -nc` and double-quote the substitution as shown above.
---
## A complete scripting pattern
Putting it together, a robust script invocation: fails fast on errors, never prompts, parses only JSON, and separates output from diagnostics.
```sh
#!/usr/bin/env bash
set -euo pipefail
export PAPERCLIP_API_URL="https://paperclip.example.com"
export PAPERCLIP_API_KEY="<token>"
export PAPERCLIP_COMPANY_ID="<company-id>"
# Find the highest-priority open issue, or exit cleanly if none.
issue_id=$(paperclipai issue list --status todo --json \
| jq -r 'sort_by(.priority) | reverse | .[0].id // empty')
if [ -z "$issue_id" ]; then
echo "No open work." >&2
exit 0
fi
paperclipai issue comment "$issue_id" \
--body "Picked up by the nightly script." --json >/dev/null
echo "Commented on $issue_id"
```
`set -euo pipefail` makes the script abort on the first failed command (including inside the `jq` pipeline), so a `401` or a `409` stops the run instead of silently continuing with empty data. Every CLI call passes `--json`, so nothing depends on the human renderer. That is the whole discipline: explicit credentials, `--json` everywhere, exit codes checked.
---
## See also
- [Common Options](common-options.md) — the full flag set, API base resolution order, and context profiles
- [Authentication](./authentication.md) — board tokens vs. agent keys, and how `--api-key` resolves
- [Issue](./issue.md) — the command surface most scripts drive
- [Run](./run.md) — inspecting and controlling heartbeat runs from the terminal
+165 -50
View File
@@ -1,88 +1,203 @@
---
paperclip_version: v2026.609.0
---
# CLI Overview
The Paperclip CLI is the fast path for bootstrapping a local instance and operating a company from the terminal.
The Paperclip CLI (`paperclipai`) stands up a Paperclip instance and gives you a full-parity, terminal-based way to operate a company against it. Paperclip is UI-first — the web UI is the primary surface most operators use — and the CLI is the alternative for when the terminal is the better tool: installing and configuring an instance, automation and CI, scripted or bulk operations, and operating headless without a browser. Reach for this page first: it explains the two layers the CLI is built from, who you authenticate as, and where every reference page and guide lives.
Use it when you want to:
- install or repair a local Paperclip setup
- manage config, secrets, storage, and hostnames
- work against the control plane without opening the UI
- script repeatable company operations from shell automation
Keep one thing in mind as you read: the CLI and the UI are two surfaces over the same control plane and the same data. Anything in this documentation can equally be done on screen; the CLI just lets you do it from the shell. (If you want to go further and hand the CLI to an AI so it operates a company without a browser at all, that's the optional Autonomous Operation track — one use of the CLI, not its premise.)
---
## Command Families
## The two layers
The CLI is split into two practical layers:
Everything the CLI does falls into one of two layers, and they have different prerequisites.
1. [Setup commands](./setup-commands.md) handle onboarding, diagnostics, and local instance configuration.
2. [Control-plane commands](./control-plane-commands.md) handle companies, issues, agents, approvals, activity, dashboards, and heartbeats.
| Layer | What it does | Talks to | Needs a credential? |
|---|---|---|---|
| **Setup** | Onboard, diagnose, configure, and run a local instance | Local config files and the local server process | No |
| **Control plane** | Manage companies, agents, issues, goals, runs, budgets, and more | The Paperclip server API | Yes |
> **Tip:** If you are starting from scratch, read the setup section first. If Paperclip is already running and you just want to manage work, jump straight to the control-plane section. If you already know the command and just need a flag, the [Command Reference](./commands.md) covers every subcommand and option in one searchable page.
**Setup commands** get a server up and healthy. They work against config on disk (under `~/.paperclip` by default) and the local process. The headline commands are `onboard`, `doctor`, `configure`, `env`, `allowed-hostname`, `db:backup`, and `run` (with no subcommand). See [Setup commands](./setup-commands.md) and the [Dev environments](./dev-environments.md) reference for the `env-lab` and `worktree` tooling.
**Control-plane commands** are HTTP clients for the server API. They need to know where the server is ([API base resolution](#how-the-cli-finds-the-server)) and who you are ([credentials](#personas-and-credentials)). This is the bulk of the CLI: companies, projects, goals, issues, agents, prompts, runs, routines, approvals, activity, dashboards, cost, workspaces, access, adapters, assets, skills, secrets, cloud, plugins, and feedback.
> **Note:** `run` is overloaded on purpose. `paperclipai run` with **no subcommand** bootstraps and starts a local instance (a setup action). `paperclipai run <subcommand>` (`list`, `live`, `get`, `events`, `log`, `cancel`, and friends) inspects and controls heartbeat **runs** through the API (a control-plane action). Two different concepts under one verb — see [run reference](./run.md).
---
## Common Options
## What runs where
Most client commands share the same flags:
This is the single most important thing to internalize before you script against the CLI: **the CLI does not run the model.** An agent's actual work — the LLM calls, the coding, the research — executes server-side inside the Paperclip runtime and its adapters. The CLI triggers that work and observes it.
When you call `agent wake` or `heartbeat run`, the CLI POSTs to the server (`/api/agents/:id/wakeup`); the server runs the adapter and the CLI streams the resulting run events and logs back to your terminal. You are conducting, not computing.
The one exception is `agent local-cli <agent>`. It sets up your local environment so a human or a local AI (Claude, Codex) can run *as* that agent on your own machine and report results back through the API. See [Key concepts](../../guides/welcome/key-concepts.md) for the full picture.
---
## Personas and credentials
Every control-plane call is made *as somebody*. Paperclip has exactly two personas, and your CLI profile records which one you are.
| Persona | Authority | Typical use |
|---|---|---|
| **Board operator** | Full board authorization across the whole instance | Bootstrapping, building the org, minting credentials, steering everything |
| **Agent** | Scoped to exactly one company and one agent | An agent operating its own work, or a local CLI acting as that agent |
There are two matching credential types.
| Credential | Created with | Properties |
|---|---|---|
| **Board API token** | `token board create` | Named, supports expiry and revocation, audited server-side |
| **Agent API key** | `token agent create` | Scoped to one company + one agent, plaintext shown **once** at creation |
How you get your first credential depends on the instance mode:
- **`local_trusted`** — the CLI can create and approve its own auth challenge with no browser; the server treats loopback as an implicit board ("local-board"). This is the headless bootstrap path.
- **`authenticated`** — the first credential comes from either `auth bootstrap-ceo` (which mints a one-time DB invite) or a browser board-claim. After that, a board token can mint further board tokens and agent keys non-interactively.
See [Authentication](./authentication.md) for the credential commands and [Installation](./installation.md) for the zero-browser flow.
### The fast ways to authenticate
- **`paperclipai connect`** — the interactive wizard (TTY only). It resolves the API base, health-checks it, logs in as board, lets you pick persona/company/agent, mints a token, saves a persona-aware profile, and prints the shell exports to run. Start here when you have a terminal in front of you.
- **`paperclipai auth login`** — the device-code / browser-approval flow that stores a board credential keyed by API base. Good for non-interactive board login.
- **`paperclipai agent local-cli <agent-id>`** — the all-in-one for handing the CLI to an AI: it creates a long-lived agent API key, installs Paperclip skills into `~/.codex/skills` and `~/.claude/skills`, and prints export lines for `PAPERCLIP_API_URL`, `PAPERCLIP_COMPANY_ID`, `PAPERCLIP_AGENT_ID`, and `PAPERCLIP_API_KEY`.
```sh
paperclipai connect
# or, for an AI operator on this machine:
paperclipai agent local-cli <agent-id> -C <company-id>
```
See [CLI auth](../../administration/cli-auth.md) for the judgement calls.
---
## How the CLI finds the server
Control-plane commands resolve the API base in this exact order. The first source that yields a URL wins.
1. `--api-base <url>`
2. `PAPERCLIP_API_URL`
3. the selected context profile's `apiBase`
4. the local Paperclip config server port
5. `http://localhost:3100`
> **Tip:** If a command can't connect, the error includes the URL it actually tried and a hint to check `GET /api/health`. Most "it won't connect" problems are an API base resolving to the wrong place — read that URL before anything else.
---
## Context profiles
So you aren't retyping the same flags forever, the CLI stores defaults in `~/.paperclip/context.json`. Profiles are persona-aware: a board profile records `persona=board`; an agent profile records `persona=agent` plus its `agentId` and `agentName`.
Crucially, a profile stores an `apiKeyEnvVarName` — the *name* of an environment variable — not a plaintext token. The key stays in your environment; the profile just remembers where to look.
```sh
paperclipai context set --api-base http://localhost:3100 --company-id <company-id>
paperclipai context set --api-key-env-var-name PAPERCLIP_API_KEY
export PAPERCLIP_API_KEY=...
paperclipai context show
paperclipai context use default
```
See [Common options](common-options.md) for the full flag set and [Authentication](./authentication.md) for profile management.
---
## Common flags
Most control-plane commands share the same flags. Learn them once.
| Flag | Use |
|---|---|
| `--data-dir <path>` | Isolate all local Paperclip state away from `~/.paperclip`. Useful for clean test instances and worktrees. |
| `--api-base <url>` | Point the CLI at a remote or non-default Paperclip server. |
| `--api-key <token>` | Authenticate against the API directly. |
| `--context <path>` | Read and write a specific CLI context file. |
| `--profile <name>` | Select which CLI context profile to use. |
| `--json` | Return machine-readable output. |
| `--company-id <id>` | Required by company-scoped commands. |
| `--data-dir <path>` | Isolate all local state (config, context, db, logs, storage, secrets) away from `~/.paperclip`. Ideal for test instances and worktrees. |
| `--api-base <url>` | Point at a remote or non-default server. |
| `--api-key <token>` | Authenticate directly without a profile. |
| `--context <path>` | Read and write a specific context file. |
| `--profile <name>` | Select which context profile to use. |
| `--json` | Emit machine-readable output for scripting. |
The CLI stores profile defaults in `~/.paperclip/context.json`. A profile can hold the API base URL, company ID, and the name of an environment variable that contains the API key.
Company-scoped commands additionally take `--company-id <id>` (with a short `-C` alias on some, like `run`). For scripting and `--json` patterns, see [Output and scripting](output-and-scripting.md).
Example:
---
## The shortest paths
**Fresh local install:**
```sh
pnpm paperclipai context set --api-base http://localhost:3100 --company-id <company-id>
pnpm paperclipai context set --api-key-env-var-name PAPERCLIP_API_KEY
export PAPERCLIP_API_KEY=...
paperclipai onboard
paperclipai run
```
**Already running, just want to operate:** skip onboarding, connect, and go.
```sh
paperclipai connect
paperclipai company list
```
**Hand the wheel to an AI:** set up agent credentials and skills in one shot.
```sh
paperclipai agent local-cli <agent-id> -C <company-id>
```
---
## Context Profiles
## Reference map
Context profiles let you avoid repeating the same flags on every command.
Every command family has its own page.
Useful commands:
**Setup and instance**
```sh
pnpm paperclipai context show
pnpm paperclipai context list
pnpm paperclipai context use default
pnpm paperclipai context set --api-base http://localhost:3100 --company-id <company-id>
```
- [Installation](installation.md) — getting the binary and the in-repo dev alias
- [Setup commands](./setup-commands.md) — `onboard`, `doctor`, `configure`, `env`, `allowed-hostname`, `db:backup`, `run`
- [Dev environments](./dev-environments.md) — `env-lab` and `worktree` tooling
- [Common options](common-options.md) — shared flags, profiles, data-dir
- [Output and scripting](output-and-scripting.md) — `--json` and automation patterns
Use context profiles when you regularly work against the same API base and company. They are especially useful for control-plane commands, where a stable company context saves a lot of repeated typing.
**Identity**
- [Authentication](./authentication.md) — `connect`, `auth`, `token`, `context`
- [Access](./access.md) — access control
**The org**
- [Company](./company.md) · [Project](./project.md) · [Goal](./goal.md) · [Issue](./issue.md) · [Agent](./agent.md)
**Driving and observing work**
- [Prompt](./prompt.md) — task handoff to agents
- [Run](./run.md) — heartbeat run inspection and control
- [Routine](./routine.md) — recurring scheduled work
- [Approval](./approval.md) · [Activity](./activity.md) · [Dashboard](./dashboard.md) · [Cost](./cost.md)
**Platform and extension**
- [Workspace](./workspace.md) · [Adapter](./adapter.md) · [Asset](./asset.md) · [Skills](./skills.md) · [Secrets](./secrets.md) · [Cloud](./cloud.md) · [Plugin](./plugin.md) · [Feedback](./feedback.md)
---
## Local Workflow
## Guides
For a fresh local install, the shortest path is:
If you'd rather follow a path than read a reference, these product guides cover the same ground from the UI side — everything here maps onto a CLI command family above:
```sh
pnpm paperclipai onboard
pnpm paperclipai run
```
- Getting started: [Installation](../../guides/getting-started/installation.md) · [Your first company](../../guides/getting-started/your-first-company.md) · [Hire your first agent](../../guides/getting-started/your-first-agent.md)
- Day to day: [Dashboard](../../guides/day-to-day/dashboard.md) · [Issues](../../guides/day-to-day/issues.md) · [Approvals](../../guides/day-to-day/approvals.md) · [Costs & budgets](../../guides/day-to-day/costs.md)
- The org: [Org structure](../../guides/org/org-structure.md) · [Delegation](../../guides/org/delegation.md) · [Projects](../../guides/projects-workflow/projects.md) · [Goals](../../guides/projects-workflow/goals.md) · [Routines](../../guides/projects-workflow/routines.md)
- Credentials: [CLI auth](../../administration/cli-auth.md)
If you already have a running instance, use the control-plane commands instead of onboarding again.
> **Note:** `pnpm paperclipai run` is a local bootstrap command, not a company-management command. It is meant to get a server up and healthy before you start operating on companies.
> **Coming soon:** a dedicated CLI-operator track — driving and bootstrapping a whole company from the terminal, including handing the CLI to an AI operator — is being written. For now the command pages above plus these guides cover the full surface.
---
## Next Steps
## Next steps
- [Setup Commands](./setup-commands.md) for onboarding, repair, and local configuration
- [Control-Plane Commands](./control-plane-commands.md) for company operations from the terminal
- [Command Reference](./commands.md) for the full list of commands, flags, and environment variables
- [Deployment Overview](../deploy/overview.md) if you need to choose between local, private, and public deployment modes
- New here? Start with [Installation](installation.md), then connect with [Authentication & tokens](./authentication.md).
- Going headless? The [Installation](installation.md) page covers the zero-browser `onboard` → `run` flow.
- Building the company? Jump to [Your first company](../../guides/getting-started/your-first-company.md), then drive it with [Company](./company.md), [Project](./project.md), and [Issue](./issue.md).
+266
View File
@@ -0,0 +1,266 @@
---
paperclip_version: v2026.609.0
---
# Plugin Commands
Plugins extend the Paperclip runtime server-side: they add connectors, workspace integrations, automation jobs, dashboards, and UI surfaces that agents and the board can use. These commands are how you scaffold a plugin, install it into your instance, watch its health, toggle it on and off, and drive its runtime surfaces from the terminal. Reach for them when you are authoring a plugin against the alpha SDK, or operating one that is already installed.
Like every plugin endpoint, the work runs in the Paperclip runtime. The CLI registers the plugin, flips its lifecycle state, and proxies calls to its tools, jobs, config, and bridge channels — it does not execute plugin code on your machine, with one exception: a **local-path install** runs trusted local code from your own disk, which is why those installs print an explicit warning.
> **Note:** Every command except `plugin init` talks to the API and accepts the common client flags. (`plugin init` runs entirely on your machine and never touches the API.) See [Common Options](./common-options.md) for `--api-base`, `--api-key`, `--context`, `--profile`, `--data-dir`, and `--json`.
---
## Lifecycle at a glance
A plugin moves through a small set of statuses. The CLI colorizes them in human output and reports them verbatim in `--json`.
| Status | Meaning |
|---|---|
| `ready` | Installed, enabled, and running. |
| `installed` | Registered but not yet fully started. |
| `upgrade_pending` | A newer version is available or staged. |
| `disabled` | Present but turned off; not running. |
| `error` | The plugin failed to load or run. Check `lastError` and the logs. |
The typical authoring loop is `init` → develop → `install` (local path) → iterate, and the typical operating loop is `list` → `inspect` → `enable`/`disable`/`uninstall`.
---
## Scaffold a plugin — `plugin init`
Create a new local plugin project from a starter template. This command runs entirely on your machine — it writes files and prints the next commands to run. It does not touch the API.
```sh
paperclipai plugin init @acme/plugin-linear
paperclipai plugin init @acme/plugin-linear --template connector --category connector \
--display-name "Linear Connector" --author "Acme"
```
The project is created in a folder named after the package (the scope prefix is stripped, so `@acme/plugin-linear` becomes `plugin-linear/`). Use `--output` to choose the parent directory.
| Flag | Use |
|---|---|
| `--output <dir>` | Parent directory to create the plugin folder in. Defaults to the current directory. |
| `--template <template>` | Starter template: `default`, `connector`, `workspace`, or `environment`. Defaults to `default`. |
| `--category <category>` | Manifest category: `connector`, `workspace`, `automation`, `ui`, or `environment`. |
| `--display-name <name>` | Manifest display name. |
| `--description <description>` | Manifest description. |
| `--author <author>` | Manifest author. |
| `--sdk-path <path>` | Path to a local `@paperclipai/plugin-sdk` package, for SDK development against an unpublished build. |
On success the command prints the scaffold path and the exact next commands to run:
```sh
cd plugin-linear
pnpm install
pnpm dev
paperclipai plugin install plugin-linear
```
Keep `pnpm dev` running while you develop — Paperclip watches the rebuilt `dist` output and reloads the plugin worker.
---
## Install a plugin — `plugin install`
Install a plugin from a local filesystem path or an npm package. The CLI auto-detects local paths (anything absolute, starting with `./`, `../`, `~`, or an existing relative directory) and resolves them to an absolute path so the server can find the plugin regardless of where you ran the command.
```sh
paperclipai plugin install ./my-plugin # local path (auto-detected)
paperclipai plugin install @acme/plugin-linear # npm package
paperclipai plugin install @acme/plugin-linear --version 1.2.0
paperclipai plugin install /abs/path/to/plugin --local # force local-path treatment
```
| Flag | Use |
|---|---|
| `-l, --local` | Treat `<package>` as a local filesystem path even when it does not look like one. |
| `--version <version>` | Pin a specific npm version. npm packages only — combining it with a local path is rejected. |
> **Warning:** Local-path installs run trusted local code from your machine. Only install paths you control. After a local install the CLI reminds you to keep `pnpm dev` running so Paperclip can reload the worker on rebuild.
On success you get the installed plugin's key, version, and status. If the runtime recorded a `lastError` during load, the CLI surfaces it as a warning even though the install call returned a record — always confirm the status is `ready` before relying on the plugin.
---
## List installed plugins — `plugin list`
List every plugin registered on the instance, with its key, status, version, and id. Add `--status` to filter.
```sh
paperclipai plugin list
paperclipai plugin list --status error
paperclipai plugin list --json
```
| Flag | Use |
|---|---|
| `--status <status>` | Filter by status: `ready`, `error`, `disabled`, `installed`, or `upgrade_pending`. |
When a plugin is in `error`, the human output appends a truncated error string; use `plugin inspect` for the full message.
---
## Inspect a plugin — `plugin inspect`
Show the full record for one plugin, identified by its plugin key or database id. If the plugin carries a `lastError`, the command prints the complete error text below the summary.
```sh
paperclipai plugin inspect @acme/plugin-linear
paperclipai plugin inspect <plugin-id> --json
```
A plugin that cannot be found exits non-zero, so this is safe to use as a presence check in scripts.
---
## Enable and disable — `plugin enable` / `plugin disable`
Toggle a plugin's runtime state without uninstalling it. `enable` brings a `disabled` or `error` plugin back online; `disable` stops a running plugin while keeping its config and state intact. Both take a plugin key or id and report the resulting status.
```sh
paperclipai plugin disable @acme/plugin-linear
paperclipai plugin enable @acme/plugin-linear
```
Use `disable` when you want to pause a misbehaving integration without losing its configuration, then `enable` once you have fixed the cause.
---
## Uninstall a plugin — `plugin uninstall`
Remove a plugin by its plugin key or id. By default this unregisters the plugin; add `--force` to hard-purge all of its state and config.
```sh
paperclipai plugin uninstall @acme/plugin-linear
paperclipai plugin uninstall @acme/plugin-linear --force
```
| Flag | Use |
|---|---|
| `--force` | Purge all plugin state and config (hard delete), not just unregister it. |
> **Tip:** Prefer a plain uninstall first if you might reinstall the same plugin and want its configuration back. Reach for `--force` only when you want a clean slate.
---
## Discover examples — `plugin examples`
List the example plugins bundled with the app. Each entry prints its display name, plugin key, description, and a ready-to-run local install command. This is the fastest way to see a working plugin end to end.
```sh
paperclipai plugin examples
paperclipai plugin examples --json
```
Copy the printed `paperclipai plugin install <local-path>` line to install one straight from the bundle.
---
## Runtime surfaces
Once a plugin is `ready`, these commands drive its registered tools, jobs, config, dashboards, and data/action endpoints. Most take a `<pluginId>` (plugin id or key) and POST commands accept a `--payload-json <json>` body (defaults to `{}`).
### Tools and contributions
```sh
paperclipai plugin ui-contributions
paperclipai plugin tools
paperclipai plugin tool:execute --payload-json '{"tool":"...","input":{}}'
```
`ui-contributions` and `tools` are instance-wide reads; `tool:execute` runs a registered plugin tool with the supplied payload.
### Health, logs, and upgrade
```sh
paperclipai plugin health <plugin-id>
paperclipai plugin logs <plugin-id>
paperclipai plugin upgrade <plugin-id> --payload-json '{}'
```
Start with `health` and `logs` whenever a plugin shows `error`. `upgrade` advances a plugin that is `upgrade_pending`.
### Configuration
```sh
paperclipai plugin config <plugin-id>
paperclipai plugin config:set <plugin-id> --payload-json '{"configJson":{"apiKey":"..."}}'
paperclipai plugin config:test <plugin-id> --payload-json '{"configJson":{"apiKey":"..."}}'
```
`config` reads the current config, `config:set` writes it, and `config:test` validates a candidate config (for example, checking that credentials connect) before you commit it.
### Jobs
```sh
paperclipai plugin jobs <plugin-id>
paperclipai plugin job:runs <plugin-id> <job-id>
paperclipai plugin job:trigger <plugin-id> <job-id> --payload-json '{}'
```
`jobs` lists the plugin's automation jobs, `job:runs` lists the run history for one job, and `job:trigger` fires a job immediately instead of waiting for its schedule.
### Webhooks, dashboard, data, and actions
```sh
paperclipai plugin webhook <plugin-id> <endpoint-key> --payload-json '{}'
paperclipai plugin dashboard <plugin-id>
paperclipai plugin data <plugin-id> <key> --payload-json '{}'
paperclipai plugin action <plugin-id> <key> --payload-json '{}'
```
`webhook` delivers a payload to a named webhook endpoint, `dashboard` reads the plugin's dashboard data, and `data`/`action` invoke URL-keyed read and mutation endpoints the plugin exposes.
---
## Bridge channels
The bridge is the plugin's live duplex channel. Use it to push data and actions into a plugin and to stream events back out.
```sh
paperclipai plugin bridge:data <plugin-id> --payload-json '{}'
paperclipai plugin bridge:action <plugin-id> --payload-json '{}'
paperclipai plugin bridge:stream <plugin-id> <channel>
paperclipai plugin bridge:stream <plugin-id> <channel> --duration-ms 10000
```
`bridge:data` and `bridge:action` POST a payload to the bridge. `bridge:stream` opens a streaming connection on a named channel and writes the raw response straight to stdout as it arrives.
| Flag | Use |
|---|---|
| `--duration-ms <ms>` | Stop streaming after this many milliseconds. Omit it to stream until you interrupt the command. |
> **Tip:** `bridge:stream` runs until you cancel it (or `--duration-ms` elapses), which makes it ideal for tailing plugin events during development. Set a duration when you script it so it always terminates.
---
## Local folder bindings
Some plugins bind to a folder on the operator's machine for a specific company. These commands are company-scoped — they require `-C, --company-id <id>`.
```sh
paperclipai plugin local-folders <plugin-id> -C <company-id>
paperclipai plugin local-folder:status <plugin-id> <folder-key> -C <company-id>
paperclipai plugin local-folder:validate <plugin-id> <folder-key> -C <company-id> --payload-json '{}'
paperclipai plugin local-folder:set <plugin-id> <folder-key> -C <company-id> --payload-json '{"path":"/abs/path"}'
```
| Command | Use |
|---|---|
| `local-folders` | List the plugin's local folder bindings for a company. |
| `local-folder:status` | Report the current status of one folder binding. |
| `local-folder:validate` | Check a candidate binding before committing it. |
| `local-folder:set` | Create or update a binding. `--payload-json` is **required** here. |
---
## See also
- [Common Options](./common-options.md) — the client flags every API-backed plugin command accepts
- [Output and Scripting](./output-and-scripting.md) — using `--json` to script against the CLI
- [Adapter Commands](./adapter.md) — configuring the runtime adapters agents execute on
- [Skills Commands](./skills.md) — the other way to extend what agents can do
- [Installation](./installation.md) — installing the CLI and the in-repo development alias
+196
View File
@@ -0,0 +1,196 @@
---
paperclip_version: v2026.609.0
---
# Project Commands
Projects group related work inside a company: they carry a status, an optional lead agent, links to one or more goals, and the execution defaults that issues inherit. Use these commands when you want to read or shape a company's projects from the terminal instead of the UI — including the JSON-valued `env` bindings and the execution-workspace policy that decide how an agent's work runs server-side.
All `project` subcommands accept the common client flags (`--data-dir`, `--api-base`, `--api-key`, `--context`, `--profile`, `--json`). Company scope is provided per command with `-C, --company-id <id>` rather than inherited globally, so you pass it explicitly where it is needed.
---
## At A Glance
```sh
paperclipai project list --company-id <company-id>
paperclipai project get <project-id-or-shortname> [--company-id <company-id>]
paperclipai project create --company-id <company-id> --name "Launch Site" [--goal-ids <id1,id2>] [--lead-agent-id <agent-id>]
paperclipai project update <project-id-or-shortname> --status in_progress [--company-id <company-id>]
paperclipai project delete <project-id-or-shortname> --yes [--company-id <company-id>]
```
`get`, `update`, and `delete` take a project reference that can be either the project UUID or its shortname. When you pass a shortname, supply `--company-id` so the server can resolve it within the right company.
> **Tip:** Add `--json` to any subcommand when you are scripting. The human-readable output of `list` is a compact one-line-per-project summary; the JSON output is the full project record.
---
## List Projects
```sh
paperclipai project list --company-id <company-id>
paperclipai project list --company-id <company-id> --json
```
`list` is company-scoped and requires `--company-id`. The default output prints one line per project with the fields most useful for triage:
| Field | Meaning |
|---|---|
| `id` | The project UUID. |
| `name` | The project's display name. |
| `status` | The project's current status. |
| `urlKey` | The project's stable shortname/URL key. |
| `goalIds` | Comma-separated goal IDs the project is linked to. |
| `leadAgentId` | The agent leading the project, if set. |
When there are no projects, `list` prints an empty result. Use `--json` to get the complete records for scripting.
---
## Get A Project
```sh
paperclipai project get <project-id-or-shortname>
paperclipai project get launch-site --company-id <company-id>
paperclipai project get <project-id> --json
```
`get` returns one project by ID or shortname. The reference argument is required. Passing `--company-id` adds a `companyId` query parameter, which the server needs to resolve a shortname unambiguously — always include it when you pass a shortname rather than a UUID.
---
## Create A Project
```sh
paperclipai project create --company-id <company-id> --name "Launch Site"
```
`create` is company-scoped: both `--company-id` and `--name` are required. The remaining options are optional and map directly onto the project record.
| Flag | Use |
|---|---|
| `-C, --company-id <id>` | Company to create the project in. Required. |
| `--name <name>` | Project name. Required. |
| `--description <text>` | Free-text description. |
| `--status <status>` | Initial project status. |
| `--goal-ids <csv>` | Comma-separated list of goal IDs to link. |
| `--goal-id <id>` | Deprecated single goal ID. Prefer `--goal-ids`. |
| `--lead-agent-id <id>` | Agent that leads the project. |
| `--target-date <date>` | Target/target-completion date. |
| `--color <value>` | Project color. |
| `--env-json <json>` | Project `env` binding as a JSON object. See [JSON fields](#json-fields-env-and-execution-workspace-policy). |
| `--execution-workspace-policy-json <json>` | Execution workspace policy as a JSON object. |
`--goal-ids` is the supported way to attach goals; `--goal-id` exists only for backward compatibility with the old single-goal model. The submitted payload is validated against the server's create-project schema before the request is sent, so an out-of-range value fails locally with a clear error rather than a server round-trip.
```sh
paperclipai project create \
--company-id <company-id> \
--name "Launch Site" \
--description "Public marketing launch" \
--status planned \
--goal-ids <goal-id-1>,<goal-id-2> \
--lead-agent-id <agent-id> \
--color "#3b82f6"
```
---
## Update A Project
```sh
paperclipai project update <project-id-or-shortname> --status in_progress
paperclipai project update launch-site --company-id <company-id> --name "Launch v2"
```
`update` takes a project reference (UUID or shortname) and patches only the fields you pass — anything you omit is left untouched. As with `get`, include `--company-id` when you reference a project by shortname so it resolves to the right company.
| Flag | Use |
|---|---|
| `-C, --company-id <id>` | Company ID for shortname lookup. |
| `--name <name>` | New project name. |
| `--description <text\|null>` | New description, or `null` to clear it. |
| `--status <status>` | New project status. |
| `--goal-ids <csv>` | Replace the linked goal IDs with this comma-separated list. |
| `--goal-id <id\|null>` | Deprecated single goal ID, or `null` to clear. |
| `--lead-agent-id <id\|null>` | New lead agent, or `null` to clear. |
| `--target-date <date\|null>` | New target date, or `null` to clear. |
| `--color <value\|null>` | New color, or `null` to clear. |
| `--env-json <json\|null>` | Replace the `env` binding, or `null` to clear it. |
| `--execution-workspace-policy-json <json\|null>` | Replace the execution workspace policy, or `null` to clear it. |
| `--archived-at <iso8601\|null>` | Archive timestamp, or `null` to unarchive. |
### Clearing vs. omitting a field
Nullable fields follow one rule: pass the literal string `null` to clear the value, omit the flag to leave it unchanged. This applies to `--description`, `--goal-id`, `--lead-agent-id`, `--target-date`, `--color`, `--env-json`, `--execution-workspace-policy-json`, and `--archived-at`.
```sh
# Clear the lead agent and unarchive in one call
paperclipai project update <project-id> --lead-agent-id null --archived-at null
# Archive a project
paperclipai project update <project-id> --archived-at 2026-06-01T00:00:00Z
```
> **Note:** `--goal-ids` replaces the entire set of linked goals on the project. Pass the full list you want, not a delta.
---
## Delete A Project
```sh
paperclipai project delete <project-id-or-shortname> --yes
paperclipai project delete launch-site --company-id <company-id> --yes
```
`delete` removes a project. It refuses to run without `--yes` — the command fails fast with "Deletion requires --yes." if the flag is missing. Pass `--company-id` when you reference the project by shortname.
> **Warning:** Deleting a project is immediate once `--yes` is supplied. Confirm the reference resolves to the right project with `project get` before you delete it, especially when using a shortname.
---
## JSON Fields: `env` And Execution Workspace Policy
Two project fields carry structured configuration that the CLI accepts as raw JSON: the `env` binding (`--env-json`) and the execution workspace policy (`--execution-workspace-policy-json`). On both `create` and `update`, the CLI parses the string you pass with `JSON.parse` and forwards the result to the API. Invalid JSON fails locally with `Invalid JSON: ...` before any request is made, and the literal string `null` clears the field on `update`.
These fields shape how an agent's work actually runs. Remember that the agent's real execution happens server-side in the Paperclip runtime and adapters — the CLI sets these defaults; the server enforces them.
### `--env-json`
The `env` binding declares environment values that issues in the project inherit at execution time. Secret-backed entries reference a named secret rather than embedding the value, which keeps plaintext out of the project record:
```sh
paperclipai project create \
--company-id <company-id> \
--name "Ops" \
--env-json '{"OPENAI_API_KEY":{"kind":"secret","secretName":"openai-api-key"}}'
```
### `--execution-workspace-policy-json`
The execution workspace policy controls how execution workspaces are provisioned for the project's issues — for example whether the policy is enabled and which default mode new work uses:
```sh
paperclipai project update <project-id> \
--execution-workspace-policy-json '{"enabled":true,"defaultMode":"shared_workspace"}'
```
To remove a policy you previously set, pass `null`:
```sh
paperclipai project update <project-id> --execution-workspace-policy-json null
```
> **Tip:** Build these JSON values in a file and pass them inline, or generate them with a tool that guarantees valid JSON. Because the CLI parses the string before sending it, a malformed object is rejected immediately rather than producing a confusing server error.
---
## See also
- [Goal Commands](./goal.md) — manage the goals that projects link to with `--goal-ids`.
- [Issue Commands](./issue.md) — the work objects that live inside projects and inherit their execution defaults.
- [Agent Commands](./agent.md) — the agents you assign as a project's lead.
- [Workspace Commands](./workspace.md) — inspect and operate the execution workspaces shaped by the execution workspace policy.
- [Common Options](./common-options.md) — the shared client flags every command accepts.
- [Output And Scripting](./output-and-scripting.md) — how `--json` output behaves and how to consume it.
+169
View File
@@ -0,0 +1,169 @@
---
paperclip_version: v2026.609.0
---
# Prompt Handoff
Prompt handoff is how you hand a task to an agent from the terminal. Reach for it when you want to say "agent, go do this" without opening the UI. It is the most direct command in the CLI for putting work in front of an agent and getting that agent moving immediately.
Read one thing carefully before you use it: **prompt handoff is not chat.** There is no conversation, no streamed reply, no session to keep open. Paperclip's communication model is **tasks and comments**, not messages. A prompt handoff creates a `todo` issue assigned to the target agent and then **wakes** that agent so the server-side runtime picks the work up. The CLI returns the issue (or comment) it created and exits. The actual work runs server-side, on the next heartbeat, against that issue — you observe it through the issue, runs, and activity, not through the prompt command's output.
There are three commands, one per situation:
| Command | Persona | Auth it uses |
|---|---|---|
| `agent-prompt <agent> <agentApiKey> <prompt...>` | agent | An agent API key passed as a positional argument |
| `agent prompt <prompt...>` | agent | The agent profile/context (or `--api-key-env`) |
| `board prompt --agent <agent> <prompt...>` | board | A board credential, targeting any agent in the company |
All three end at the same place: create or update an issue for the agent, then wake it unless you opt out.
---
## What a handoff actually does
Both modes flow through the same logic. The presence of `--issue` decides which path runs.
**No `--issue` (the default) — create a task.** The CLI builds a new issue with:
- `title` — the value of `--title` if you pass one, otherwise the first non-empty line of your prompt (truncated to 100 characters).
- `description` — your full prompt text.
- `status` — `todo`.
- `priority` — `medium`.
- `assigneeAgentId` — the target agent.
It posts that to `POST /api/companies/<company-id>/issues`. Then, unless `--no-wake` is set, it wakes the agent against the new issue.
**With `--issue <id>` — comment on existing work.** The CLI does not create a task. It posts your prompt as a comment to `POST /api/issues/<issue-id>/comments` (the comment's `resume` flag tracks whether the agent should be woken). Then, unless `--no-wake` is set, it wakes the agent against that issue.
> **Note:** Because the default path creates a brand-new `todo` issue every time, running `agent prompt "..."` three times gives you three separate tasks — not a three-message thread. If you mean "continue the same piece of work," you almost always want `--issue`.
The wake is a `POST /api/agents/<agent-id>/wakeup` with `source: "on_demand"`. It does not run the model inline. It tells the Paperclip runtime "there is work for this agent," and the server schedules the run. The prompt command returns once the issue/comment is created and the wakeup is acknowledged.
---
## `agent-prompt` — hand off with an explicit agent key
Use this when you have an agent API key in hand and want to push work to that exact agent in one self-contained command. The agent reference and the key are both positional, so this is the form that copies cleanly into a script or another tool's config.
```sh
paperclipai agent-prompt <agent-id> <agent-api-key> "Draft the Q3 launch checklist and link the source issues"
```
The agent reference can be the agent ID, its shortname (`urlKey`), or its name. The CLI verifies the key actually belongs to that agent: it calls `/api/agents/me` with the key and checks that the agent you named matches the key's identity. If you point the key at the wrong agent, the command fails with a clear message telling you who the key really belongs to — it will not silently file the task against someone else.
| Argument / flag | Use |
|---|---|
| `<agent>` | Target agent: ID, shortname, or name. Must match the API key's own identity. |
| `<agentApiKey>` | The agent API key, passed as a positional argument. |
| `<prompt...>` | The prompt text. Everything after the key is joined into one prompt. |
| `--issue <issueId>` | Comment on this existing issue instead of creating a new task. |
| `--title <title>` | Title for the new issue (ignored when `--issue` is set). |
| `--no-wake` | Create/update the work but do not wake the agent. |
---
## `agent prompt` — hand off as the configured agent
Use this from a session that already has an agent persona — a saved agent profile, or an agent API key sitting in an environment variable. There is no positional key here; the credential comes from your context.
```sh
# Use the active agent profile/context
paperclipai agent prompt "Investigate the failing nightly build and report findings"
# Read the key from a named environment variable
paperclipai agent prompt --api-key-env PAPERCLIP_API_KEY "Investigate the failing nightly build"
# Target a specific saved profile
paperclipai agent prompt --profile my-agent "Investigate the failing nightly build"
```
If you pass `--agent`, the CLI confirms it matches the authenticated agent's own identity; if you omit it, the target defaults to the profile's agent (and falls back to the identity the credential resolves to). Either way an agent persona can only prompt **itself** — that is the point of the agent persona, and it is enforced. To file work for a *different* agent, use `board prompt`.
| Flag | Use |
|---|---|
| `--agent <agent>` | Agent ID, shortname, or name. Defaults to the profile/identity agent. Must match the authenticated agent. |
| `--api-key-env <name>` | Read the agent API key from the named environment variable. Errors if the variable is unset. |
| `--issue <issueId>` | Comment on this existing issue instead of creating a new task. |
| `--title <title>` | Title for the new issue (ignored when `--issue` is set). |
| `--no-wake` | Create/update the work but do not wake the agent. |
> **Note:** `agent prompt` requires an agent profile. If your active profile is a board profile, the command refuses and tells you to use an agent profile or `board prompt`. The persona recorded in the profile is the source of truth.
---
## `board prompt` — hand off to any agent as the board
Use this when you are operating with board authority and want to direct work to any agent in a company. This is the operator's command: it does not require the target agent's key, just your board credential plus the company and the agent you are targeting.
```sh
paperclipai board prompt --company-id <company-id> --agent <agent-name-or-id> \
"Take over the migration rollback and post a status update when staging is green"
```
`--agent` is required and `--company-id` identifies the company (short alias `-C`). The CLI resolves the agent within that company, files the task (or comment), and wakes the agent — exactly the same create-or-comment-then-wake flow as the agent commands, but the actor recorded on the work is the board.
| Flag | Use |
|---|---|
| `--agent <agent>` | Required. Target agent: ID, shortname, or name. |
| `-C, --company-id <id>` | Company the agent belongs to. |
| `--issue <issueId>` | Comment on this existing issue instead of creating a new task. |
| `--title <title>` | Title for the new issue (ignored when `--issue` is set). |
| `--no-wake` | Create/update the work but do not wake the agent. |
> **Note:** `board prompt` requires a board profile. An agent profile is refused with a message pointing you back to the agent prompt commands.
---
## `--issue`: comment instead of create
`--issue <id>` is the single most useful flag here, because it changes the verb from "create" to "continue." When you pass it, the prompt text becomes a **comment** on that issue rather than a fresh `todo` task. `--title` is ignored in this mode — you are not creating an issue, so there is nothing to title.
```sh
# Nudge an agent already working a task, and wake it to resume
paperclipai board prompt --company-id <company-id> --agent <agent-id> --issue PAP-142 \
"The staging DB is back up — retry the migration step and confirm row counts"
```
The issue reference can be a UUID or a human identifier like `PAP-142`. Use `--issue` whenever the work already exists: course-correcting an in-flight task, answering a question the agent raised, or handing back a task after a blocker cleared. Use the default (no `--issue`) only when you genuinely want a new, separate unit of work.
---
## `--no-wake`: stage work without firing a run
By default every handoff wakes the agent. `--no-wake` skips that final step: the issue is still created (or the comment is still posted), the agent is still assigned, but no wakeup is sent, so no run is scheduled by this command.
```sh
# Queue several tasks, then let the normal scheduler pick them up
paperclipai board prompt --company-id <company-id> --agent <agent-id> --no-wake \
"Audit the auth module for unused permission checks"
```
Reach for `--no-wake` when you are staging a batch of work and do not want a burst of runs firing one per command, when you are filing follow-up work an agent should pick up on its own next cycle, or when you are scripting and want to control wakeups separately. Be deliberate about it: without a wake, the agent will not act on the task until something else wakes it (the scheduler, a mention, or an explicit `agent wake`). If you want immediate action, leave the wake on.
---
## Reading the result
Each command prints a small result object describing what it did. The `mode` field is the fastest way to confirm you did what you meant to:
| Field | Meaning |
|---|---|
| `mode` | `issue` if a new task was created, `comment` if it appended to existing work. |
| `actor` | `agent` or `board`, depending on which command you ran. |
| `agent` | The resolved target agent (`id`, `name`, `urlKey`). |
| `issue` / `comment` | The created issue or comment. |
| `wakeup` | The wakeup response, or `null` when `--no-wake` was used. |
Add `--json` to get the raw object for scripting. A `null` `wakeup` with `mode: "issue"` means the task was filed but nothing is running yet — exactly what `--no-wake` produces.
---
## See also
- [Agent commands](./agent.md) — inspect agents, set up `agent local-cli`, and wake agents directly.
- [Issue commands](./issue.md) — list, get, update, and comment on the tasks that handoff creates.
- [Run inspection](./run.md) — watch the server-side run that a wakeup schedules.
- [Authentication](./authentication.md) — mint the agent keys and board tokens these commands consume.
- [Common options](./common-options.md) — `--api-base`, `--api-key`, `--context`, `--profile`, `--json`, and the rest.
- [Output and scripting](./output-and-scripting.md) — work with `--json` results in pipelines.
+266
View File
@@ -0,0 +1,266 @@
---
paperclip_version: v2026.609.0
---
# Routine Commands
Routines are Paperclip's recurring and event-driven work. A routine carries a payload describing the work to spawn, a status, a revision history, and one or more triggers — schedules, webhooks, or public API endpoints — that fire it. Reach for these commands when you want your AI to set up automation that runs on its own (a nightly summary, a webhook that turns inbound events into tasks), inspect what a routine has done, or pause everything in an emergency. The singular `routine` group talks to the control-plane REST API; the plural `routines` group is a local database-maintenance escape hatch.
> **Note:** Like every other work object in Paperclip, the actual work a routine produces runs server-side. These commands create, configure, fire, and observe routines through the API — they do not execute the work themselves. See [What Runs Where](../../guides/welcome/key-concepts.md).
---
## Two command groups
There are two groups here, and they are not interchangeable.
| Group | Talks to | Use it when |
|---|---|---|
| `routine` (singular) | The control-plane REST API | Day-to-day: list, create, update, fire, and inspect routines and their triggers. |
| `routines` (plural) | The configured local database directly | Local maintenance only — pausing every routine in one company on a local instance. |
The `routine` commands take the standard client flags (`--api-base`, `--api-key`, `--context`, `--profile`, `--json`, and `--data-dir`). The single `routines disable-all` command does not go through the API at all — it opens the local Paperclip database and flips routine statuses in place.
---
## `routine list`
List the routines in a company. Company-scoped, so it needs a company id.
```sh
paperclipai routine list --company-id <company-id>
paperclipai routine list --company-id <company-id> --project-id <project-id>
```
| Flag | Use |
|---|---|
| `-C, --company-id <id>` | Company to list routines for. Required (falls back to your context's company). |
| `--project-id <id>` | Filter to routines attached to a single project. |
---
## `routine get`
Fetch one routine by id, including its current payload, status, and triggers.
```sh
paperclipai routine get <routine-id>
```
---
## `routine create`
Create a routine in a company. The routine definition is supplied as a JSON payload, which keeps the CLI in lockstep with the API schema — anything the API accepts, you can send.
```sh
paperclipai routine create --company-id <company-id> --payload-json '{
"name": "Nightly status digest",
"description": "Summarize the day's closed issues",
"status": "active"
}'
```
| Flag | Use |
|---|---|
| `-C, --company-id <id>` | Company to create the routine in. Required. |
| `--payload-json <json>` | Routine definition as a JSON string. Required. |
> **Tip:** Build the JSON payload in a variable or a file and pass it through, rather than hand-typing it inline. That makes the create reproducible and easy to diff against `routine get` afterwards.
---
## `routine update`
Patch an existing routine. Send only the fields you want to change as a JSON payload — this is a partial update against the same schema `create` uses, so it is how you pause a routine (`{"status":"paused"}`), rename it, or rewrite its payload.
```sh
paperclipai routine update <routine-id> --payload-json '{"status": "paused"}'
```
| Flag | Use |
|---|---|
| `--payload-json <json>` | Fields to update as a JSON string. Required. |
Every update creates a new revision, which is what makes the revision commands below useful.
---
## Revisions
Routine edits are versioned. Each `update` (and each trigger change that the server records against the routine) produces a revision you can list and roll back to.
### `routine revisions`
List the revision history for a routine, newest first.
```sh
paperclipai routine revisions <routine-id>
```
### `routine revision:restore`
Restore a prior revision. This does not rewind history — it makes the chosen revision the current definition by creating a new latest revision from it, so the rollback is itself auditable.
```sh
paperclipai routine revision:restore <routine-id> <revision-id>
```
Both arguments are positional: the routine id first, then the revision id you pulled from `routine revisions`.
---
## Runs
A routine *run* is one firing of the routine — what the schedule, webhook, or manual trigger produced.
### `routine runs`
List the recent runs for a routine. Use this to confirm a schedule is actually firing, or to debug why a webhook produced nothing.
```sh
paperclipai routine runs <routine-id>
paperclipai routine runs <routine-id> --limit 50
```
| Flag | Use |
|---|---|
| `--limit <n>` | Cap the number of runs returned. Omit for the server default. |
### `routine run`
Fire a routine immediately, regardless of its schedule. The optional payload is merged into the run as input — useful for testing, for one-off invocations, or for kicking a routine that is configured but not yet on a schedule.
```sh
paperclipai routine run <routine-id>
paperclipai routine run <routine-id> --payload-json '{"reason": "manual smoke test"}'
```
| Flag | Use |
|---|---|
| `--payload-json <json>` | Run input as a JSON string. Defaults to `{}`. |
> **Note:** `routine run` requests a run through the API. Whether it actually executes — and when — is still subject to the routine's concurrency and catch-up settings (see below) and the company's normal heartbeat gating.
---
## Triggers
Triggers are what cause a routine to run. A routine can have several. There are three kinds, and which one you reach for depends on what should drive the routine:
| Trigger kind | Fires the routine when… | Notes |
|---|---|---|
| `schedule` | A cron/recurrence schedule is due. | The standard "run nightly / hourly / weekly" trigger. Its behavior under overlap and missed windows is governed by concurrency and catch-up. |
| `webhook` | An external system POSTs to the trigger's webhook URL. | Carries a secret you can rotate. Use it to turn inbound events into Paperclip work. |
| `api` (public) | A caller fires its public endpoint by `publicId`. | Fired with `routine trigger:fire`; the public id is shareable without exposing the routine id. |
The exact shape of a trigger (its kind, schedule expression, webhook config, concurrency policy, and catch-up policy) is set through the JSON payload on `trigger:create` and `trigger:update`. The CLI passes that payload straight to the API, so the trigger schema is the source of truth for the available fields.
### Concurrency and catch-up
Two trigger settings matter most for scheduled routines, and you set both in the trigger payload:
- **Concurrency** controls what happens when a routine is still running and its next firing comes due. The choice is between letting runs overlap or serializing them so a slow run does not pile up on top of itself.
- **Catch-up** controls what happens to scheduled windows that were missed — for example while the instance was down. The choice is between firing the missed occurrences when the scheduler recovers or skipping straight to the next due window.
Decide these deliberately. A digest that aggregates "since last run" usually wants serialized, no-catch-up behavior so you get one clean run rather than a backlog of overlapping ones; an idempotent fetch can tolerate catch-up. Confirm what fired with `routine runs <routine-id>` after a deploy or an outage.
### `routine trigger:create`
Attach a new trigger to a routine. The routine id is the argument; the trigger definition is the payload.
```sh
paperclipai routine trigger:create <routine-id> --payload-json '{
"kind": "schedule",
"schedule": "0 7 * * *"
}'
```
| Flag | Use |
|---|---|
| `--payload-json <json>` | Trigger definition as a JSON string. Defaults to `{}`. |
### `routine trigger:update`
Patch an existing trigger. The argument here is the *trigger* id, not the routine id. Use it to change a schedule, adjust concurrency or catch-up, or enable/disable the trigger.
```sh
paperclipai routine trigger:update <trigger-id> --payload-json '{"schedule": "0 */2 * * *"}'
```
| Flag | Use |
|---|---|
| `--payload-json <json>` | Fields to update as a JSON string. Required. |
### `routine trigger:delete`
Remove a trigger from its routine.
```sh
paperclipai routine trigger:delete <trigger-id>
```
### `routine trigger:rotate-secret`
Rotate the secret on a webhook trigger. Do this if a secret leaks or on a regular rotation schedule. The old secret stops working once rotated, so update the caller that posts to the webhook in the same change.
```sh
paperclipai routine trigger:rotate-secret <trigger-id>
```
### `routine trigger:fire`
Fire a public (`api`) trigger by its `publicId`. This is the endpoint you hand to an external caller — it is keyed by the public id, not the routine id, so it can be shared without revealing internal ids.
```sh
paperclipai routine trigger:fire <public-id>
paperclipai routine trigger:fire <public-id> --payload-json '{"event": "deploy.finished"}'
```
| Flag | Use |
|---|---|
| `--payload-json <json>` | Payload to deliver with the firing. Defaults to `{}`. |
---
## `routines disable-all`
This is the local maintenance command — the "stop everything" switch. It pauses every non-archived routine in one company by writing directly to the configured local Paperclip database, without going through the API. Reach for it when a local instance is misbehaving and you want all scheduled and webhook-driven work to stop firing immediately.
```sh
paperclipai routines disable-all --company-id <company-id>
paperclipai routines disable-all --company-id <company-id> --json
```
| Flag | Use |
|---|---|
| `-C, --company-id <id>` | Company whose routines to pause. Required (falls back to `PAPERCLIP_COMPANY_ID`). |
| `-c, --config <path>` | Path to the Paperclip config file, if not the default. |
| `-d, --data-dir <path>` | Paperclip data directory root, to isolate state from `~/.paperclip`. |
| `--json` | Output the raw result object instead of a human summary. |
What it does, precisely:
- Reads every routine for the company from the local database.
- Sets each routine whose status is **not** already `paused` or `archived` to `paused`.
- Leaves already-paused and archived routines untouched.
The summary reports four counts — total routines, how many it newly paused, how many were already paused, and how many were archived:
```text
Paused 3 routine(s) for company <company-id> (1 already paused, 0 archived).
```
The `--json` form returns the same counts as `companyId`, `totalRoutines`, `pausedCount`, `alreadyPausedCount`, and `archivedCount`.
> **Warning:** `disable-all` operates on the **local** instance's database (it will start the embedded PostgreSQL cluster if the config uses embedded mode). It is not the way to pause routines on a remote control plane — for that, pause each routine with `routine update <routine-id> --payload-json '{"status":"paused"}'`. There is no bulk re-enable command: turn routines back on individually with `routine update` once you have resolved the problem.
---
## See also
- [Run Commands](./run.md) — inspect and control heartbeat runs, including the runs a routine spawns.
- [Issue Commands](./issue.md) — the tasks routines create; filter by `originKind=routine_execution` to find them.
- [Common Options](./common-options.md) — the client flags every `routine` command accepts.
- [Output And Scripting](./output-and-scripting.md) — using `--json` to pipe routine data into other tools.
- [Routines And Automation](../../guides/projects-workflow/routines.md) — the end-to-end guide to designing automation.
+206
View File
@@ -0,0 +1,206 @@
---
paperclip_version: v2026.609.0
---
# Run (Heartbeat) Commands
A *heartbeat run* is a single server-side execution of an agent: the moment the Paperclip runtime wakes an agent, hands it context, runs the adapter (the LLM, coding, or research work), and records the result. The `run` subcommands let you list, inspect, stream, and control those runs from the terminal. Reach for them whenever you want to see what an agent is doing right now, read its log, follow the issues it touched, cancel a run that has gone sideways, or record a watchdog decision — all without opening the UI.
> **Note:** Do not confuse these subcommands with the bare `run` command. `paperclipai run` (with no subcommand) bootstraps and starts a **local Paperclip instance** on your machine. The `run` *subcommands* documented here are API clients that observe and control heartbeat runs on whatever instance your context points at. They are entirely separate concepts that happen to share a word.
---
## The execution model in one breath
Agents do not run inside the CLI. When an agent wakes (via `agent wake`, `heartbeat run`, a comment mention, a routine, or the scheduler), the **server** runs the adapter and produces a heartbeat run. The CLI is a window onto that run: it reads run records, events, and logs through the API, and it can ask the server to cancel a run or record a watchdog decision. Nothing you type here executes a model — you are observing and steering work that lives server-side.
That framing matters for how you use these commands. `run log` and `run events` are *reads* of an in-flight or finished run; they do not "attach" to a process you own. `run cancel` is a request to the server, not a local kill. Treat every one of these as a control-plane call.
---
## Common options
Every `run` subcommand accepts the standard client options:
| Flag | Use |
|---|---|
| `--data-dir <path>` | Point at a specific local Paperclip data directory. |
| `--api-base <url>` | Override the API base URL. Highest-priority source in the resolution order. |
| `--api-key <token>` | Supply a token explicitly instead of using the saved profile. |
| `--context <path>` | Use a specific `context.json` instead of `~/.paperclip/context.json`. |
| `--profile <name>` | Select a named persona profile from the context file. |
| `--json` | Emit machine-readable JSON instead of the inline text format. Use this whenever you script. |
The company-scoped subcommands — `list` and `live` — also take `-C, --company-id <id>`. The remaining subcommands address a run or operation directly by id and do not need a company.
See [Common Options](./common-options.md) for the full flag reference and the exact API base resolution order, and [Output and Scripting](./output-and-scripting.md) for working with `--json`.
---
## Find runs for a company
Use `run list` to page through heartbeat runs for a company, optionally filtered to one agent. Use `run live` when you only care about what is queued or running right now.
```sh
paperclipai run list --company-id <company-id>
paperclipai run list --company-id <company-id> --agent-id <agent-id> --limit 50
paperclipai run live --company-id <company-id>
paperclipai run live --company-id <company-id> --limit 50 --min-count 5
```
| Command | Flags | Behavior |
|---|---|---|
| `run list` | `--agent-id <id>`, `--limit <n>` | Lists heartbeat runs for the company, newest activity first. Filter to a single agent with `--agent-id`; cap the result count with `--limit`. |
| `run live` | `--limit <n>`, `--min-count <n>` | Lists only queued and running runs. `--min-count <n>` pads the result with recent *completed* runs up to that count, so a quiet company still shows recent history instead of an empty list. |
Both commands print one inline record per run in text mode, showing the run `id`, `status`, `agentId`, `invocationSource`, `triggerDetail`, `startedAt`, `finishedAt`, and `logBytes`. Pass `--json` for the full run objects.
> **Tip:** `run live --json | jq '.[].id'` is the fastest way to grab the id of an in-flight run so you can pipe it straight into `run log` or `run cancel`.
---
## Inspect a single run
Once you have a run id, drill in.
```sh
paperclipai run get <run-id>
paperclipai run events <run-id> --after-seq 0 --limit 200
paperclipai run log <run-id> --offset 0 --limit-bytes 16384
paperclipai run log <run-id> --text
paperclipai run issues <run-id>
```
### `run get`
Returns the full heartbeat run record for `<run-id>`. This is your single source of truth for a run's status, timing, trigger, and byte counters.
### `run events`
Lists the structured event stream for a run — the ordered log of what the runtime did, step by step.
| Flag | Default | Use |
|---|---|---|
| `--after-seq <n>` | `0` | Return only events after this sequence number. Poll incrementally by feeding back the highest `seq` you have already seen. |
| `--limit <n>` | `200` | Maximum events to return. |
In text mode each event prints as an inline record with `seq`, `eventType`, `stream`, `level`, and `message`. To follow a live run, poll on a short interval and advance `--after-seq` past the last `seq` you received.
### `run log`
Reads raw log bytes for a run. Logs are byte-addressed, so you page through them with an offset rather than line numbers.
| Flag | Default | Use |
|---|---|---|
| `--offset <bytes>` | `0` | Start reading at this byte offset. |
| `--limit-bytes <bytes>` | unset | Maximum number of bytes to read. Omit to let the server decide. |
| `--text` | off | When the API returns a `text` field, print only that text to stdout — no surrounding record. |
Use the `logBytes` value from `run get` or `run list` as a cursor: read a chunk, advance `--offset` by the bytes you consumed, and repeat to tail a growing log. `--text` is the right choice when you want to pipe the raw output somewhere or read it as a human; without it you get the structured envelope.
### `run issues`
Lists the issues associated with a run — the tasks the agent created, checked out, commented on, or completed during that execution. Each row shows the issue `identifier`, `id`, `status`, `priority`, `title`, and the `runStatus` linking it back to the run. This is the bridge from "what did this run do" to "which work changed", and the natural jump-off point into the [`issue`](./issue.md) commands.
---
## Cancel a run
```sh
paperclipai run cancel <run-id>
```
Requests cancellation of a queued or running heartbeat run. This is a server-side request: the runtime stops the run and returns the updated record (or `null` if there was nothing to cancel). Use it when a run is stuck, looping, or burning budget on the wrong thing. Because the work runs server-side, cancellation is the correct lever — there is no local process to interrupt.
> **Warning:** Cancelling a run does not roll back what the agent already did. Any issues it moved, comments it posted, or workspace operations it completed remain in place. Check `run issues <run-id>` afterward to see the state you are left with.
---
## Workspace operations
When a run executes commands in an execution workspace — running a build, a test suite, a git operation — each of those shows up as a *workspace operation* attached to the run. Inspect them to see exactly what the agent ran and read the output.
```sh
paperclipai run workspace-operations <run-id>
paperclipai run workspace-log <operation-id> --offset 0 --limit-bytes 16384
paperclipai run workspace-log <operation-id> --text
```
`run workspace-operations <run-id>` lists the operations for a run, each row showing `id`, `status`, `phase`, `command`, `cwd`, and `logBytes`. Take an operation `id` from that list and feed it to `run workspace-log` to read that operation's output.
`run workspace-log` is byte-addressed and takes the same log flags as `run log`:
| Flag | Default | Use |
|---|---|---|
| `--offset <bytes>` | `0` | Start reading at this byte offset. |
| `--limit-bytes <bytes>` | unset | Maximum number of bytes to read. |
| `--text` | off | Print only the `text` field when the API returns one. |
> **Note:** `run log` reads the run's overall log; `run workspace-log` reads the log of one specific command the run executed. When you are debugging a failed build or test, the workspace log is usually where the real error lives.
---
## Watchdog decisions
The watchdog is the server-side monitor that flags runs which look stuck, runaway, or otherwise off the happy path. `run watchdog-decision` records your verdict on a flagged run so the monitor knows how to proceed.
```sh
paperclipai run watchdog-decision <run-id> --decision continue --reason "Long compile is expected here"
paperclipai run watchdog-decision <run-id> --decision snooze --snoozed-until 2026-06-01T18:00:00Z
paperclipai run watchdog-decision <run-id> --decision dismissed_false_positive --evaluation-issue-id <issue-id>
```
`--decision` is required and must be one of:
| Decision | Meaning |
|---|---|
| `continue` | Let the run proceed; you have judged it healthy. |
| `snooze` | Suppress watchdog action until a future time. Provide that time with `--snoozed-until <iso8601>`. |
| `dismissed_false_positive` | The watchdog was wrong; mark the alert as a false positive. |
| Flag | Use |
|---|---|
| `--decision <decision>` | **Required.** `snooze`, `continue`, or `dismissed_false_positive`. |
| `--reason <text>` | Free-text justification for the decision. Record it — the next operator will thank you. |
| `--snoozed-until <iso8601>` | The time to snooze until. Required for a `snooze` decision. |
| `--evaluation-issue-id <id>` | The related watchdog evaluation issue, when one exists. |
> **Tip:** Pair a watchdog decision with a quick `run events` and `run workspace-log` read first. Decide `continue` only after you have seen *why* the run looks slow — a long but legitimate test run looks identical to a hang until you read the log.
---
## A typical observe loop
The terminal workflow for watching a company's runs is usually:
```sh
# 1. See what is live right now.
paperclipai run live --company-id <company-id> --min-count 5
# 2. Drill into a run that looks interesting or stuck.
paperclipai run get <run-id>
paperclipai run events <run-id> --limit 50
# 3. Read the raw log, or the failing command's log.
paperclipai run log <run-id> --text
paperclipai run workspace-operations <run-id>
paperclipai run workspace-log <operation-id> --text
# 4. See what work it changed, then act.
paperclipai run issues <run-id>
paperclipai run cancel <run-id> # if it has gone wrong
```
This is the read-heavy half of operating a company from the CLI. The mutating half — waking agents, assigning work, posting comments — lives with the [`agent`](./agent.md) and [`issue`](./issue.md) commands.
---
## See also
- [Agent Commands](./agent.md) — wake agents and set up local agent CLI sessions; the source of the runs you observe here.
- [Issue Commands](./issue.md) — manage the tasks a run creates and touches.
- [Prompt Commands](./prompt.md) — hand work to an agent as a task that wakes it and produces a run.
- [Dashboard Commands](./dashboard.md) — the same top-level company state the UI shows.
- [Activity Commands](./activity.md) — the audit trail behind run-driven changes.
- [Common Options](./common-options.md) — shared flags and API base resolution.
- [Output and Scripting](./output-and-scripting.md) — working with `--json` and piping run data.
+279
View File
@@ -0,0 +1,279 @@
---
paperclip_version: v2026.609.0
---
# Secrets Commands
Use these commands when you need to manage API keys, tokens, and other credentials that agents and projects consume at runtime — without ever pasting the raw value into your shell history or leaking it into logs. The `secrets` group declares Paperclip-managed secrets, links to external provider vaults (AWS Secrets Manager and friends), audits where credentials are referenced, and migrates inline env values into proper secret references.
Every command here is company-scoped or operates on a specific secret/provider-config ID, and every command accepts the [common client options](./common-options.md) (`--data-dir`, `--api-base`, `--api-key`, `--context`, `--profile`, `--json`). Company-scoped subcommands require `-C, --company-id <id>`.
> **Warning:** Paperclip never prints secret values. `secrets list`, `declarations`, `usage`, and `access-events` return metadata only — names, keys, providers, status, and references — never the plaintext. The value is shown only at the moment you set it (and even then, prefer `--value-env` so it never lands in your shell history). Treat that as a hard guarantee and design your scripts around it.
---
## When to reach for each command
| Command | Use |
|---|---|
| `secrets list` | See the secret metadata that exists for a company. |
| `secrets declarations` | See the portable env declarations a company export emits (what a target instance must supply). |
| `secrets create` | Store a new Paperclip-managed secret value. |
| `secrets link` | Register an external provider-owned secret without copying its value into Paperclip. |
| `secrets update` | Patch a secret's metadata. |
| `secrets rotate` | Replace a managed secret's value. |
| `secrets usage` | Find where a secret is referenced (which agents/projects). |
| `secrets access-events` | Audit when and by whom a secret was read. |
| `secrets delete` | Remove a secret (guarded by `--yes` + `--confirm`). |
| `secrets doctor` | Run provider health checks through the API. |
| `secrets providers` | List the secret provider descriptors available to a company. |
| `secrets provider-configs` (+ subcommands) | Manage per-company provider vault configs. |
| `secrets remote-import` / `remote-import:preview` | Pull secrets in from a configured remote source. |
| `secrets migrate-inline-env` | Convert inline sensitive agent env values into secret references. |
---
## List and inspect secrets
`secrets list` returns metadata for every secret defined for a company. The human-readable output shows the secret `id`, `name`, `key`, `provider`, `status`, `managedMode`, `latestVersion`, and whether an `externalRef` is set — never the value.
```sh
paperclipai secrets list --company-id <company-id>
paperclipai secrets list --company-id <company-id> --json
```
`secrets declarations` is a different lens: it asks the company export pipeline what portable env *inputs* a company package would require, so you can see exactly which keys a target instance must satisfy before an imported company will run. Each row is a `key`, a `scope` (`company`, `project:<slug>`, or `agent:<slug>`), a `kind` (`secret` or `plain`), a `requirement`, a `portability` flag, whether it `hasDefault`, and a `description`.
```sh
paperclipai secrets declarations --company-id <company-id>
paperclipai secrets declarations --company-id <company-id> --include agents,projects --kind secret
```
| Flag | Use |
|---|---|
| `--include <values>` | Comma-separated include set for the export preview. One or more of `company,agents,projects,issues,tasks,skills`. Default: `company,agents,projects`. |
| `--kind <kind>` | Filter declarations to `all`, `secret`, or `plain`. Default: `all`. |
> **Tip:** `declarations` is the command to run before you hand a company export to someone else. It tells the recipient which credentials they will need to provide on their side — it does not reveal yours.
---
## Create, link, update, and rotate
### Create a Paperclip-managed secret
`secrets create` stores a value that Paperclip encrypts and manages itself. You must supply the value through exactly one of `--value` or `--value-env`. Always prefer `--value-env`: it reads the value from a named environment variable so the plaintext never appears in your shell history, your scrollback, or the process argument list.
```sh
# Preferred: the value is read from the env var, not typed on the command line
export ANTHROPIC_API_KEY=sk-ant-...
paperclipai secrets create --company-id <company-id> --name anthropic-api-key --value-env ANTHROPIC_API_KEY
```
```sh
# Discouraged: --value puts the secret into shell history
paperclipai secrets create --company-id <company-id> --name anthropic-api-key --value 'sk-ant-...'
```
| Flag | Use |
|---|---|
| `--name <name>` | Required. Secret display name. |
| `--key <key>` | Portable secret key (the stable key other resources reference). |
| `--provider <provider>` | Secret provider id. Omit to use the company default (managed local encryption). |
| `--value <value>` | The secret value. Mutually exclusive with `--value-env`. |
| `--value-env <name>` | Read the value from this environment variable. Mutually exclusive with `--value`. Errors if the variable is empty or unset. |
| `--description <text>` | Free-text description. |
Passing both `--value` and `--value-env`, or neither, is an error.
### Link an external provider-owned secret
`secrets link` registers a secret that lives in an external vault (for example AWS Secrets Manager) by reference. Paperclip records the pointer — the provider, the external ref, and an optional version ref — but never copies the value into its own store. This is the right choice when your organization's source of truth is a managed vault and you want Paperclip to resolve the value at runtime rather than hold a second copy.
```sh
paperclipai secrets link \
--company-id <company-id> \
--name prod-stripe-key \
--provider aws_secrets_manager \
--external-ref arn:aws:secretsmanager:us-east-1:123456789012:secret:prod/stripe-AbCdEf
```
| Flag | Use |
|---|---|
| `--name <name>` | Required. Secret display name. |
| `--provider <provider>` | Required. Secret provider id (e.g. `aws_secrets_manager`). |
| `--external-ref <ref>` | Required. Provider secret ARN, name, path, or reference. |
| `--key <key>` | Portable secret key. |
| `--provider-version-ref <ref>` | Provider version id or label to pin to. |
| `--description <text>` | Free-text description. |
A linked secret is stored with `managedMode: external_reference`, which is why `secrets list` shows `externalRef=yes` for it.
### Update metadata
`secrets update` patches a secret's metadata. It takes a raw JSON payload so you can set whatever the server's `UpdateSecret` shape allows.
```sh
paperclipai secrets update <secret-id> --payload-json '{"description":"Rotated quarterly by ops"}'
```
| Flag | Use |
|---|---|
| `--payload-json <json>` | Required. JSON object matching the server's update-secret payload. |
> **Note:** `update` changes metadata, not the value. To change the value, use `rotate`.
### Rotate the value
`secrets rotate` replaces the value of a Paperclip-managed secret and bumps its version. As with `create`, supply the new value through one of `--value` or `--value-env`, and prefer `--value-env`.
```sh
export ANTHROPIC_API_KEY=sk-ant-new...
paperclipai secrets rotate <secret-id> --value-env ANTHROPIC_API_KEY
```
| Flag | Use |
|---|---|
| `--value <value>` | New secret value. Mutually exclusive with `--value-env`. |
| `--value-env <name>` | Read the new value from this environment variable. Mutually exclusive with `--value`. |
---
## Audit usage and access
Before you rotate or delete a credential, find out who depends on it and who has been reading it.
`secrets usage` reports where a secret is referenced — the agents and projects whose env bindings point at it.
```sh
paperclipai secrets usage <secret-id>
```
`secrets access-events` returns the access audit trail for a secret, so you can confirm which runs read it and when.
```sh
paperclipai secrets access-events <secret-id> --json
```
> **Tip:** Run `secrets usage` first whenever you plan to delete a secret. Deleting a secret that an agent's env still references will break that agent's runs.
---
## Delete a secret
`secrets delete` is intentionally hard to run by accident. You must pass `--yes` and repeat the secret ID with `--confirm`, exactly matching the positional argument.
```sh
paperclipai secrets delete <secret-id> --yes --confirm <secret-id>
```
| Flag | Use |
|---|---|
| `--yes` | Required safety flag confirming the destructive action. |
| `--confirm <secretId>` | Required. Must repeat the same secret ID passed as the argument. |
If `--yes` is missing, or `--confirm` does not match the argument, the command refuses to proceed.
---
## Provider health and descriptors
`secrets doctor` runs provider health checks through the Paperclip API and prints one row per provider with a `status` of `ok`, `warn`, or `error`, plus any warnings, missing-config hints, detected credential sources, and backup guidance.
```sh
paperclipai secrets doctor --company-id <company-id>
```
For AWS-backed secrets, `doctor` reports missing non-secret provider env and the expected AWS SDK runtime credential source. Do not store AWS bootstrap credentials *inside* Paperclip secrets — let the runtime resolve them from the standard AWS credential chain, and use `doctor` to confirm that chain is wired up.
`secrets providers` lists the provider descriptors available to a company — the set of providers you can target with `create`, `link`, and provider-config commands.
```sh
paperclipai secrets providers --company-id <company-id>
```
---
## Provider vault configs
A company can have multiple provider vault configs (for example several AWS Secrets Manager instances), with one marked as the default. These are also configurable from the board UI under `Company Settings → Secrets → Provider vaults`, but every operation is available from the CLI. The mutating subcommands take a raw `--payload-json` so they track the server contract exactly.
```sh
# List existing configs
paperclipai secrets provider-configs --company-id <company-id>
# Create a config
paperclipai secrets provider-config:create --company-id <company-id> --payload-json '{ ... }'
# Preview which secrets a vault would discover, without importing them
paperclipai secrets provider-config:discovery-preview --company-id <company-id> --payload-json '{ ... }'
# Inspect, update, default, health-check, or delete a single config
paperclipai secrets provider-config:get <config-id>
paperclipai secrets provider-config:update <config-id> --payload-json '{ ... }'
paperclipai secrets provider-config:default <config-id>
paperclipai secrets provider-config:health <config-id>
paperclipai secrets provider-config:delete <config-id>
```
| Subcommand | Scope | Required flags | Use |
|---|---|---|---|
| `provider-configs` | company | `-C` | List company provider vault configs. |
| `provider-config:create` | company | `-C`, `--payload-json` | Create a vault config. |
| `provider-config:discovery-preview` | company | `-C`, `--payload-json` | Preview vault secret discovery without importing. |
| `provider-config:get <configId>` | config | — | Show one config. |
| `provider-config:update <configId>` | config | `--payload-json` | Patch one config. |
| `provider-config:default <configId>` | config | — | Set this config as the company default vault. |
| `provider-config:health <configId>` | config | — | Health-check one config. |
| `provider-config:delete <configId>` | config | — | Delete one config. |
---
## Remote import
When a provider vault is configured, you can pull selected secrets into Paperclip's registry. Always preview first: `remote-import:preview` shows what *would* be imported, and `remote-import` performs the selected import. Both take a `--payload-json` describing the source and selection.
```sh
paperclipai secrets remote-import:preview --company-id <company-id> --payload-json '{ ... }'
paperclipai secrets remote-import --company-id <company-id> --payload-json '{ ... }'
```
| Subcommand | Required flags | Use |
|---|---|---|
| `remote-import:preview` | `-C`, `--payload-json` | Preview which remote secrets would be imported. |
| `remote-import` | `-C`, `--payload-json` | Import the selected remote secrets. |
---
## Migrate inline env into secret references
Older agent configs may have stored sensitive values inline in their adapter env (for example a literal `API_KEY` string). `secrets migrate-inline-env` finds those sensitive inline values, creates (or rotates) Paperclip-managed secrets for them, and rewrites the agents' env bindings to reference the secret instead.
It is a dry run by default. Run it without `--apply` to see what it would do, then re-run with `--apply` to commit.
```sh
# Dry run: report agents to update, secrets to create, secrets to rotate
paperclipai secrets migrate-inline-env --company-id <company-id>
# Commit the migration
paperclipai secrets migrate-inline-env --company-id <company-id> --apply
```
| Flag | Use |
|---|---|
| `--apply` | Persist changes. Without it, the command only reports the planned migration. |
The dry-run output reports `agentsToUpdate`, `secretsToCreate`, `secretsToRotate`, and the full candidate list. With `--apply`, newly created secrets use the managed local-encrypted provider, existing same-named secrets are rotated to the current inline value, and each affected agent's env binding is replaced with a `secret_ref` pointing at `version: latest`.
> **Note:** "Sensitive" is detected by env key name — keys matching patterns like `*_token`, `api_key`, `password`, `secret`, `private_key`, `jwt`, and similar. Inline values that are not flagged as sensitive are left untouched, so review the dry run before applying.
---
## See also
- [Common Options](./common-options.md) — the shared client flags every secrets command accepts.
- [Output and Scripting](./output-and-scripting.md) — using `--json` to pipe secret metadata into other tools.
- [Agent Commands](./agent.md) — agents whose env bindings reference these secrets.
- [Project Commands](./project.md) — project env declarations that consume secrets.
- [Company Commands](./company.md) — exports/imports that emit the env declarations `secrets declarations` previews.
- [Access Commands](./access.md) — broader authorization and credential context.
+247 -99
View File
@@ -1,179 +1,327 @@
---
paperclip_version: v2026.609.0
---
# Setup Commands
Use these commands when you are bootstrapping a local Paperclip instance, repairing a configuration, or changing the instance-level settings that the server runs on.
Use these commands when you are standing up a Paperclip instance, repairing it, or changing the settings the server runs on. They touch the **install and launch** — config files, the local database, secrets, storage, host binding, and the bootstrap credential. They do **not** create companies, file issues, or wake agents. That work belongs to the control-plane commands documented under [`company`](./company.md), [`issue`](./issue.md), [`agent`](./agent.md), and the rest of the reference.
They are different from control-plane commands:
- setup commands change how Paperclip is installed or launched
- control-plane commands manage the company and its work
The mental split is simple: setup commands answer "is this instance configured and running?"; control-plane commands answer "what is the company doing?". The one command that lives in both worlds is `run`, and this page is careful to separate its two meanings.
---
## When To Use
## When to reach for these
Use setup commands when you need to:
Reach for setup commands when you need to:
- create the initial config and local data directories
- validate database, storage, secrets, or host configuration
- change deployment mode, storage provider, or secrets settings
- allow a private hostname in authenticated/private mode
- create the initial config and local data directories ([`onboard`](#paperclipai-onboard))
- verify and repair an install before or after a config change ([`doctor`](#paperclipai-doctor))
- print the environment block a remote deployment will need ([`env`](#paperclipai-env))
- change deployment mode, database, storage, secrets, or logging ([`configure`](#paperclipai-configure))
- take a one-off database snapshot ([`db:backup`](#paperclipai-dbbackup))
- trust a private hostname in authenticated mode ([`allowed-hostname`](#paperclipai-allowed-hostname))
- bootstrap and launch a local server in one step ([`run`](#paperclipai-run-local-bootstrap))
- mint the first board credential headlessly ([`auth bootstrap-ceo`](#paperclipai-auth-bootstrap-ceo))
- trigger one agent heartbeat for debugging ([`heartbeat run`](#paperclipai-heartbeat-run))
- pause every routine in a company during an incident ([`routines disable-all`](#paperclipai-routines-disable-all))
Do not use them for task, agent, or approval management. Those belong to [Control-Plane Commands](./control-plane-commands.md).
> **Note:** Examples use the installed binary `paperclipai`. Inside the monorepo you can substitute `pnpm paperclipai`; see [Installation](./installation.md).
---
## `paperclipai run`
## `paperclipai run` (local bootstrap)
The quickest way to start Paperclip locally.
> **Warning:** `paperclipai run` with **no subcommand** is a setup command: it bootstraps and starts a local server. The `run` **subcommands** (`run list`, `run get`, `run events`, `run log`, `run cancel`, and friends) are a *different feature* — they inspect and control heartbeat runs over the API and are documented in [Runs](./run.md). Do not confuse the two.
The bare `run` is the shortest route from "nothing" to "a healthy local instance accepting requests". It is a thin orchestrator:
1. Resolves the instance id and config path, creates `~/.paperclip` and the instance root if missing.
2. If no config exists and you are in a TTY, it hands off to [`onboard`](#paperclipai-onboard) interactively. In a non-interactive shell it errors and tells you to run `onboard` first.
3. Runs [`doctor`](#paperclipai-doctor) with repair enabled by default; if any check fails it stops without starting the server.
4. Starts the Paperclip server.
5. If the instance is in `authenticated` mode with the embedded database, it generates a bootstrap CEO invite after startup (see [`auth bootstrap-ceo`](#paperclipai-auth-bootstrap-ceo)).
```sh
pnpm paperclipai run
paperclipai run
paperclipai run --instance dev
paperclipai run --data-dir ./tmp/paperclip-dev
paperclipai run --no-repair
```
What it does:
| Flag | Use |
|---|---|
| `-c, --config <path>` | Path to the config file. |
| `-d, --data-dir <path>` | Isolate all local state from `~/.paperclip`. |
| `-i, --instance <id>` | Local instance id (default: `default`). |
| `--bind <mode>` | On first run only, pass an onboarding reachability preset: `loopback`, `lan`, or `tailnet`. |
| `--repair` | Attempt automatic repairs during doctor. Enabled by default. |
| `--no-repair` | Disable automatic repairs during doctor. |
1. resolves the active instance and config
2. runs `paperclipai doctor` with repair enabled by default
3. starts the server when the checks pass
Use this when you want a single command that bootstraps and launches a healthy local instance.
```sh
pnpm paperclipai run --instance dev
pnpm paperclipai run --data-dir ./tmp/paperclip-dev
```
> **Tip:** If the config file does not exist yet, `run` will trigger onboarding interactively in a terminal session. In non-interactive environments, run `paperclipai onboard` first.
> **Tip:** `--bind` only matters on the very first `run`, when it forwards the preset to onboarding. Once a config exists, `run` reuses it; change binding later with [`configure --section server`](#paperclipai-configure).
---
## `paperclipai onboard`
Interactive first-time setup.
Interactive first-run setup. Use it to create a brand-new local install or to rebuild the config from guided prompts. It writes the config (default `~/.paperclip/instances/<id>/config.json`), provisions the `PAPERCLIP_AGENT_JWT_SECRET` into the adjacent `.env` file, and creates the local secrets key file.
```sh
pnpm paperclipai onboard
paperclipai onboard
paperclipai onboard --yes
paperclipai onboard --run
paperclipai onboard --bind lan
```
Use this when you are creating a new local install or want to rebuild the config from guided prompts.
The first prompt offers two paths:
- `Quickstart` for local defaults: embedded database, local disk storage, and default secrets
- `Advanced setup` for explicit configuration of server, database, logging, storage, secrets, and related options
| Path | What you get |
|---|---|
| **Quickstart** | Local defaults, ready to run: embedded PostgreSQL, file logging, local-disk storage, local-encrypted secrets, loopback binding. Honors environment overrides (for example `DATABASE_URL`, `PAPERCLIP_PUBLIC_URL`, `PAPERCLIP_DEPLOYMENT_MODE`) where they apply. |
| **Advanced setup** | Step-by-step prompts for database, LLM provider, logging, server/auth, storage, and secrets. Tests the database connection and validates the LLM API key when you supply one. |
Useful forms:
| Flag | Use |
|---|---|
| `-c, --config <path>` | Path to the config file. |
| `-d, --data-dir <path>` | Isolate all local state from `~/.paperclip`. |
| `--bind <mode>` | Quickstart reachability preset: `loopback`, `lan`, or `tailnet`. |
| `-y, --yes` | Accept Quickstart defaults non-interactively and start immediately. Without `--bind`, this forces trusted-local loopback defaults and ignores conflicting reachability env vars. |
| `--run` | Start the server immediately after saving the config. |
```sh
pnpm paperclipai onboard --run
pnpm paperclipai onboard --yes
```
> **Note:** If a valid config already exists, `onboard` preserves it unchanged, ensures the agent JWT secret and secrets key exist, and prints next-step commands. Use [`configure`](#paperclipai-configure) to change settings on an existing install rather than re-onboarding.
`--run` starts the server after onboarding.
`--yes` accepts the defaults non-interactively and then starts Paperclip.
> **Note:** If Paperclip is already configured, rerunning `onboard` preserves the existing config unless you are intentionally starting over with a clean instance.
When the instance is `authenticated` with the embedded database, the bootstrap CEO invite is deferred until the server is running — `onboard` reminds you to run `paperclipai run` then [`auth bootstrap-ceo`](#paperclipai-auth-bootstrap-ceo).
---
## `paperclipai doctor`
Health checks with optional repair.
Diagnostic checks with optional repair. Run it before starting the server, after editing config, or any time the instance misbehaves. `run` invokes `doctor` for you; call it directly when you only want the report.
```sh
pnpm paperclipai doctor
pnpm paperclipai doctor --repair
paperclipai doctor
paperclipai doctor --repair
paperclipai doctor --repair --yes
```
Use `doctor` when you want to verify an existing install before starting the server or after changing config.
It loads the config's `.env`, then runs these checks in order, stopping early only if the config itself is invalid:
It checks:
- configuration validity
- deployment/auth mode compatibility
- agent JWT secrets
- secrets adapter configuration
- storage configuration
- config validity (must pass first)
- deployment / auth mode compatibility
- agent JWT secret
- secrets adapter
- storage
- database connectivity
- logging and port checks
- LLM provider
- log directory
- listen port
> **Warning:** `--repair` can create or update local files when the check knows how to fix the problem. Review the output before rerunning in a shared or production-like environment.
Each line reports `✓ pass`, `! warn`, or `✗ fail` with a repair hint. The summary counts passed/warned/failed, and a clear "fix and re-run" message is printed when anything fails.
---
| Flag | Use |
|---|---|
| `-c, --config <path>` | Path to the config file. |
| `-d, --data-dir <path>` | Isolate all local state from `~/.paperclip`. |
| `--repair` (alias `--fix`) | Attempt to repair fixable issues. |
| `-y, --yes` | Skip the per-repair confirmation prompts. |
## `paperclipai configure`
Update instance configuration sections.
```sh
pnpm paperclipai configure --section server
pnpm paperclipai configure --section database
pnpm paperclipai configure --section storage
pnpm paperclipai configure --section secrets
pnpm paperclipai configure --section logging
pnpm paperclipai configure --section llm
```
Use this command when you want to change how the instance runs without rebuilding the install from scratch.
Common cases:
- switch deployment mode or host binding
- change database mode or backup settings
- change the storage provider
- update secrets provider or strict mode
> **Warning:** `--repair` can create or update local files (the JWT `.env`, the secrets key, the log directory) when a check knows how to fix the problem. Review the output before running it against a shared or production-like instance, and pair it with `--yes` only when you trust the repairs.
---
## `paperclipai env`
Show the resolved environment configuration.
Print the environment variables a deployment needs, with each value's source and a ready-to-paste `export` block. Use it to inspect what the instance actually resolves after config, defaults, and environment overrides are merged — and to seed the env of a remote/containerized deployment.
```sh
pnpm paperclipai env
paperclipai env
```
This is useful when you want to inspect what the instance will actually see after config, defaults, and environment overrides are merged.
It reads the config (warning, not failing, if the file is missing or unparseable), then prints two sections, **Required** and **Optional**, marking each variable as `set`, `default`, or `missing`. The required variables are `PAPERCLIP_AGENT_JWT_SECRET` and `DATABASE_URL`; optional entries cover `PORT`, `PAPERCLIP_PUBLIC_URL`, `BETTER_AUTH_TRUSTED_ORIGINS`, the agent-JWT and heartbeat-scheduler tunables, and the full secrets/storage provider set. Missing values appear in the export block as `<set-this-value>` so you can fill them in.
| Flag | Use |
|---|---|
| `-c, --config <path>` | Path to the config file. |
| `-d, --data-dir <path>` | Isolate all local state from `~/.paperclip`. |
> **Tip:** Pipe the export block into your shell or a deployment manifest. Anything still marked `missing` must be supplied before the server will start cleanly.
---
## `paperclipai configure`
Update one or more configuration sections on an existing install without rebuilding it. Each save rewrites the config and stamps its metadata. Run it interactively to pick sections from a menu, or target one directly with `--section`.
```sh
paperclipai configure
paperclipai configure --section server
paperclipai configure --section database
paperclipai configure --section storage
paperclipai configure --section secrets
paperclipai configure --section logging
paperclipai configure --section llm
```
| Section | Changes |
|---|---|
| `server` | Deployment mode, exposure, host binding, port, served UI, and auth base URL. |
| `database` | Embedded vs. external PostgreSQL, connection string, backup settings. |
| `storage` | Storage provider (`local_disk` or `s3`) and its options. |
| `secrets` | Secrets provider, strict mode, and the local key file (created on demand). |
| `logging` | Logging mode and directory. |
| `llm` | LLM provider and API key. |
| Flag | Use |
|---|---|
| `-c, --config <path>` | Path to the config file. |
| `-d, --data-dir <path>` | Isolate all local state from `~/.paperclip`. |
| `-s, --section <section>` | Configure one section and exit: `llm`, `database`, `logging`, `server`, `storage`, or `secrets`. |
> **Note:** `configure` requires an existing config — it errors and tells you to run `onboard` first if none is found. When you pass `--section`, it configures that section once and exits; without it, it loops so you can edit several sections in a row.
---
## `paperclipai db:backup`
Create a one-off snapshot of the instance database using the current config. This is a manual backup on top of (and independent of) the scheduled backups the server runs. It resolves the connection string in order: `DATABASE_URL`, then a configured `postgres` connection string, then the embedded-postgres default.
```sh
paperclipai db:backup
paperclipai db:backup --dir /backups/paperclip
paperclipai db:backup --retention-days 14
paperclipai db:backup --json
```
| Flag | Use |
|---|---|
| `-c, --config <path>` | Path to the config file. |
| `-d, --data-dir <path>` | Isolate all local state from `~/.paperclip`. |
| `--dir <path>` | Backup output directory; overrides the configured backup dir. |
| `--retention-days <days>` | Retention window for daily-pruning (positive integer). Defaults to the config value or 30. |
| `--filename-prefix <prefix>` | Backup filename prefix (default `paperclip`). |
| `--json` | Print backup metadata (file, size, pruned count, dir, retention, source) as JSON. |
> **Tip:** For the embedded database you can run a backup without the server up — `db:backup` connects on the embedded port directly. Verify where it landed with the printed `Backup dir`.
---
## `paperclipai allowed-hostname`
Allow a private hostname for authenticated/private mode.
Add a hostname to the instance's allowlist so it is accepted in authenticated/private mode. Use it when Paperclip rejects a host that should be trusted on a private network (for example a Tailscale machine name).
```sh
pnpm paperclipai allowed-hostname my-tailscale-host
paperclipai allowed-hostname dotta-macbook-pro
paperclipai allowed-hostname my-host.ts.net
```
Use this when Paperclip rejects a hostname that should be trusted on a private network.
| Argument / Flag | Use |
|---|---|
| `<host>` | The hostname to allow (normalized and lowercased). |
| `-c, --config <path>` | Path to the config file. |
| `-d, --data-dir <path>` | Isolate all local state from `~/.paperclip`. |
The command updates the local config and the new hostname takes effect after a server restart.
> **Note:** This is enforced only in authenticated/private mode.
The command updates the local config in place. A new entry takes effect only after you **restart the server**. Allowed hostnames are enforced only in `authenticated` + `private` mode; in any other mode the command notes that the setting is inert.
---
## Local Paths
## `paperclipai auth bootstrap-ceo`
The default local instance root is `~/.paperclip/instances/default`.
Mint the **first** board credential headlessly by inserting a one-time bootstrap invite into the database and printing its URL. This is how an `authenticated` instance gets its initial instance admin without a pre-existing login. On a `local_trusted` instance it is unnecessary — loopback is treated as implicit board authority — and the command says so and exits.
```sh
paperclipai auth bootstrap-ceo
paperclipai auth bootstrap-ceo --expires-hours 24
paperclipai auth bootstrap-ceo --force
paperclipai auth bootstrap-ceo --base-url https://paperclip.example.com
```
| Flag | Use |
|---|---|
| `-c, --config <path>` | Path to the config file. |
| `-d, --data-dir <path>` | Isolate all local state from `~/.paperclip`. |
| `--force` | Create a new invite even if an instance admin already exists. |
| `--expires-hours <hours>` | Invite lifetime in hours (clamped between 1 and 720; default 72). |
| `--base-url <url>` | Public base URL used to render the invite link. |
If an admin already exists it refuses unless you pass `--force`. Each run also revokes any outstanding, unaccepted bootstrap-CEO invites before issuing a fresh one. With the embedded database, the server must be running (or the embedded cluster started) for the insert to succeed; the command tells you to start the server and retry if it cannot connect.
> **Note:** After this first credential exists, you no longer need `bootstrap-ceo`. A board token can mint further board tokens and agent keys non-interactively — see [Authentication](./authentication.md).
---
## `paperclipai heartbeat run`
Trigger exactly **one** heartbeat for a single agent and stream its live logs to your terminal. This is a debugging and observation tool, not the normal way work happens — it POSTs to the server's wakeup endpoint and the server-side runtime runs the adapter. The CLI only triggers and tails; the model executes server-side.
```sh
paperclipai heartbeat run --agent-id <agent-id>
paperclipai heartbeat run --agent-id <agent-id> --source on_demand --trigger manual
paperclipai heartbeat run --agent-id <agent-id> --timeout-ms 120000 --debug
```
| Flag | Use |
|---|---|
| `-a, --agent-id <agentId>` | **Required.** Agent to invoke. |
| `-c, --config <path>` | Path to the config file. |
| `-d, --data-dir <path>` | Isolate all local state from `~/.paperclip`. |
| `--context <path>` | Path to the CLI context file. |
| `--profile <name>` | CLI context profile name. |
| `--api-base <url>` | Base URL for the Paperclip server API. |
| `--api-key <token>` | Bearer token for agent-authenticated calls. |
| `--source <source>` | Invocation source: `timer`, `assignment`, `on_demand`, or `automation` (default `on_demand`). |
| `--trigger <trigger>` | Trigger detail: `manual`, `ping`, `callback`, or `system` (default `manual`). |
| `--timeout-ms <ms>` | Max time to wait before giving up (default `0`, meaning no client-side timeout). |
| `--json` | Output raw JSON where applicable. |
| `--debug` | Show raw adapter stdout/stderr JSON chunks instead of formatted events. |
The command polls run events and logs until the run reaches a terminal status (`succeeded`, `failed`, `cancelled`, `timed_out`) and exits non-zero on anything but success, printing error and result details. To inspect runs you triggered elsewhere, use the [`run`](./run.md) subcommands.
> **Tip:** For routine agent wake-ups during normal operation, prefer [`agent wake`](./agent.md) and the prompt-handoff flow in [`prompt`](./prompt.md). Reach for `heartbeat run` when you specifically want to watch a single run unfold.
---
## `paperclipai routines disable-all`
Pause every non-archived routine in one company on the configured local instance. This is an incident/maintenance switch: it stops scheduled automation from firing while you investigate, without deleting anything. It operates directly on the local database (starting the embedded cluster and applying pending migrations if needed), so it works even when the server is down.
```sh
paperclipai routines disable-all --company-id <company-id>
paperclipai routines disable-all --company-id <company-id> --json
```
| Flag | Use |
|---|---|
| `-c, --config <path>` | Path to the config file. |
| `-d, --data-dir <path>` | Isolate all local state from `~/.paperclip`. |
| `-C, --company-id <id>` | Company whose routines to pause. Falls back to `PAPERCLIP_COMPANY_ID`. |
| `--json` | Output the result counts as JSON. |
It reports how many routines it paused, how many were already paused, and how many were archived (left untouched). Routines in any state other than `paused`/`archived` are flipped to `paused`. To create, edit, or resume individual routines through the API, use the [`routine`](./routine.md) commands.
> **Warning:** This pauses *all* of a company's routines at once. Re-enabling is a per-routine operation via [`routine`](./routine.md); there is no single "enable-all" counterpart, so use this deliberately.
---
## Local paths
A local instance keeps its state under `~/.paperclip/instances/<instance-id>` (default instance id `default`):
| Data | Path |
|---|---|
| Config | `~/.paperclip/instances/default/config.json` |
| Database | `~/.paperclip/instances/default/db` |
| Database (embedded) | `~/.paperclip/instances/default/db` |
| Logs | `~/.paperclip/instances/default/logs` |
| Storage | `~/.paperclip/instances/default/data/storage` |
| Storage (local disk) | `~/.paperclip/instances/default/data/storage` |
| Secrets key | `~/.paperclip/instances/default/secrets/master.key` |
If you need a different local root, set the instance env vars:
```sh
PAPERCLIP_HOME=/custom/home PAPERCLIP_INSTANCE_ID=dev pnpm paperclipai run
```
Use `--data-dir` (or `--instance`) to run an isolated instance — handy for clean test setups and worktrees — without disturbing your primary install.
---
## Next Step
## See also
- [Control-Plane Commands](./control-plane-commands.md) for issues, agents, companies, approvals, and activity
- [Deployment Overview](../deploy/overview.md) for the runtime modes that shape setup behavior
- [Installation](./installation.md) — install the CLI and pick a deployment shape
- [Authentication](./authentication.md) — board tokens, agent keys, and the login flows after bootstrap
- [Runs](./run.md) — inspecting and controlling heartbeat runs (the `run` *subcommands*)
- [Routines](./routine.md) — create, edit, and resume scheduled automation
- [Agents](./agent.md) — waking agents and `agent local-cli` for local execution
- [Common options](./common-options.md) — shared flags and context/profile resolution
+225
View File
@@ -0,0 +1,225 @@
---
paperclip_version: v2026.609.0
---
# Skills Commands
Use these commands to put capabilities in front of your agents: browse the app-shipped catalog, install skills into a company's library, attach the ones an agent should actually use, and keep installed skills in sync with their pinned origins. Skills are how you teach an agent a repeatable procedure (open a pull request, run a browser, follow a review checklist) without rewriting its prompt every time.
There are two command groups. The plural `paperclipai skills` group is the one you reach for in practice — it understands catalog references, resolves skills by `id`/`key`/`slug`, and prints readable tables. The singular `paperclipai skill` group is a thin, payload-driven wrapper over the same company-skill API endpoints; use it only when you need raw control over the request body in a script.
---
## The three operations
`paperclipai skills` spans three distinct things. Keeping them straight is the whole mental model:
| Operation | Commands | What it changes |
| --- | --- | --- |
| **Company install** | `skills install`, `import`, `create`, `scan-projects` | Adds or updates a skill in the company's library. Available to the whole company; does **not** attach it to any agent. |
| **Agent attach** | `skills agent sync`, `skills agent clear` | Replaces an agent's *desired* (non-required) company-skill set. A desired-state operation — it is not additive. |
| **Adapter runtime sync** | reported by `skills agent list`; triggered by `sync`/`clear` | The server-side adapter reconciles the desired set with files on disk and returns an `AgentSkillSnapshot`. |
> **Note:** Required Paperclip runtime skills (heartbeat and friends) are enforced by the server. They always remain on an agent — `skills agent clear` only removes the *non-required* desired skills you added.
> **Tip:** Add `--json` to any command to print the raw API result for scripting. Every company-scoped command takes `--company-id <company-id>` (and respects your selected profile/context). See [common options](./common-options.md).
---
## Catalog: browse before you install
The Paperclip app ships a curated catalog of skills. Catalog commands let you read it without writing anything to a company — there is no company context required to browse, search, or inspect.
```sh
paperclipai skills browse --kind bundled --category github
paperclipai skills search "pull request" --kind bundled
paperclipai skills inspect github-pr-workflow
```
| Command | Use |
| --- | --- |
| `browse` | List catalog skills, optionally filtered by `--kind`, `--category`, or `--query`. |
| `search <query>` | Same as `browse` but takes the search text as a positional argument. |
| `inspect <catalogRef>` | Show one catalog skill in full, including its file manifest (path, kind, size, sha256). |
Browse/search filters:
| Flag | Use |
| --- | --- |
| `--kind <kind>` | Filter by catalog kind: `bundled` or `optional`. |
| `--category <slug>` | Filter by catalog category slug. |
| `--query <text>` | Free-text search (`browse` only; `search` uses its positional argument). |
A `<catalogRef>` is a catalog skill `id`, `key`, or a unique `slug`. Inspect a skill before installing it so you know exactly which files will land in the company library.
---
## Catalog install into the company library
`skills install` materializes a catalog skill into a company-managed skill. This is a **company install** — it does not attach the skill to any agent.
```sh
paperclipai skills install github-pr-workflow --company-id <company-id>
paperclipai skills install github-pr-workflow --as review-prs --company-id <company-id>
```
| Flag | Use |
| --- | --- |
| `--as <slug>` | Override the company skill slug. |
| `--force` | Replace a same-key catalog-managed skill when the server allows it. Never bypasses hard validation or hard-stop audit findings. |
After install the command reminds you that nothing is attached yet. To make an agent use it, run an [agent attach](#agent-attach-desired-skills) afterward.
> **Note:** Catalog commands are for the app-shipped catalog only. GitHub, skills.sh, local-path, and plain-URL sources go through `skills import` instead.
---
## Company library
These commands manage the skills that exist in a company, regardless of where they came from. All require a company context.
```sh
paperclipai skills list --company-id <company-id>
paperclipai skills show <skill-ref> --company-id <company-id>
paperclipai skills file <skill-ref> --path SKILL.md --company-id <company-id>
paperclipai skills import <source> --company-id <company-id>
paperclipai skills create --name "Review PRs" --slug review-prs --body-file SKILL.md --company-id <company-id>
paperclipai skills scan-projects --project-id <project-id> --company-id <company-id>
```
| Command | Use |
| --- | --- |
| `list` | List every skill in the company library. |
| `show <skillRef>` | Show full detail for one company skill. |
| `file <skillRef>` | Print one skill file. Defaults to `SKILL.md`; pass `--path <path>` for another file. In human mode the raw content is written to stdout so you can pipe it. |
| `import <source>` | Import skills from a local path, GitHub, skills.sh, or a URL source. Prints the imported skills plus any warnings. |
| `create` | Create a managed local skill from flags and an optional markdown body. |
| `scan-projects` | Scan project workspaces for skills and import or update what it finds. |
A `<skillRef>` resolves against the company library by `id`, canonical `key`, or unique `slug`. An ambiguous slug is rejected — use an `id` or `key` instead.
`skills create` options:
| Flag | Use |
| --- | --- |
| `--name <name>` | Required. Skill name. |
| `--slug <slug>` | Optional explicit slug. |
| `--description <text>` | Optional description. |
| `--body-file <path>` | Markdown body file. Use `-` to read the body from stdin. |
`skills scan-projects` options (each may be repeated):
| Flag | Use |
| --- | --- |
| `--project-id <id>` | Project ID to scan; repeat for several. |
| `--workspace-id <id>` | Workspace ID to scan; repeat for several. |
With no `--project-id`/`--workspace-id` the scan covers the company's projects and workspaces. The summary line reports projects/workspaces scanned plus discovered, imported, updated, skipped, conflicts, and warnings counts.
---
## Maintenance loop: check, update, audit, reset
Catalog-installed skills are pinned to an origin. Over time the catalog moves on, or someone edits the installed bytes locally. These four commands are the maintenance loop. The skill reference is optional on `check`, `update`, and `audit`; omit it to operate over the whole library.
```sh
paperclipai skills check --company-id <company-id>
paperclipai skills update <skill-ref> --company-id <company-id>
paperclipai skills update --all --company-id <company-id>
paperclipai skills audit <skill-ref> --company-id <company-id>
paperclipai skills reset <skill-ref> --yes --company-id <company-id>
```
| Command | Use |
| --- | --- |
| `check [skillRef]` | Report update status: `supported`, `hasUpdate`, `currentRef`/`latestRef`, `installedHash`/`originHash`, any `updateHoldReason`, and the `auditVerdict`. |
| `update [skillRef]` | Install the pinned update for one skill. |
| `update --all` | Check every skill and update only those with `hasUpdate=true`; supported-but-current and unsupported skills are skipped with a reason. |
| `audit [skillRef]` | Re-scan installed bytes and report findings (severity, code, path, message) without executing anything. |
| `reset <skillRef>` | Reinstall a catalog-managed skill from its pinned origin, discarding local edits. |
`update` and `reset` accept `--force`:
| Flag | Use |
| --- | --- |
| `--all` | (`update` only) Update every skill that has an available update. |
| `--force` | Discard local-modification and soft-audit holds. Hard-stop audit findings still block the operation. |
| `--yes` | (`reset` only) Confirm without the interactive prompt. Required when not in a TTY. |
> **Warning:** Pass either a skill reference or `--all` to `update`, never both — the command rejects that combination.
---
## Remove a skill
```sh
paperclipai skills remove <skill-ref> --yes --company-id <company-id>
```
`skills remove` deletes a skill from the company library. It prompts for confirmation in an interactive terminal and requires `--yes` when run non-interactively.
---
## Agent attach: desired skills
Once a skill is in the library, attaching it to an agent is a desired-state replacement, not an addition. `skills agent sync` sets the agent's full non-required desired set; anything you omit is dropped. The adapter then reconciles and the command returns an `AgentSkillSnapshot`.
```sh
paperclipai skills agent list <agent-ref> --company-id <company-id>
paperclipai skills agent sync <agent-ref> --skill review-prs --skill agent-browser --company-id <company-id>
paperclipai skills agent clear <agent-ref> --yes --company-id <company-id>
```
| Command | Use |
| --- | --- |
| `agent list <agentRef>` | Show the agent's runtime skill snapshot: adapter type, whether sync is `supported`, the `mode`, the desired count, and per-skill runtime entries (state, origin, managed/required flags). |
| `agent sync <agentRef>` | Replace the agent's non-required desired company skills and sync runtime state. Requires at least one `--skill`. |
| `agent clear <agentRef>` | Clear the non-required desired set (sends an empty list). Prompts in a TTY; requires `--yes` otherwise. |
| Flag | Use |
| --- | --- |
| `--skill <skillRef>` | (`sync`) A desired company skill `id`, `key`, or `slug`. Repeat for several. At least one is required. |
| `--yes` | (`clear`) Confirm without the interactive prompt. |
An `<agentRef>` is an agent `id` or shortname/url-key. Because `sync` is a full replacement, list the complete desired set every time. Required Paperclip skills survive both `sync` and `clear` — the server keeps enforcing them.
---
## The singular `skill` group
`paperclipai skill` is a lower-level wrapper over the same company-skill API. It does not resolve `key`/`slug` references — every subcommand takes a literal `<skillId>` — and the create/import/scan/file-update operations are driven by a raw JSON payload. Reach for it only when you are scripting against a known skill ID and want direct control of the request body.
```sh
paperclipai skill list -C <company-id>
paperclipai skill get <skill-id> -C <company-id>
paperclipai skill file <skill-id> --path SKILL.md -C <company-id>
paperclipai skill file:update <skill-id> --payload-json '{"path":"SKILL.md","content":"..."}' -C <company-id>
paperclipai skill update-status <skill-id> -C <company-id>
paperclipai skill install-update <skill-id> -C <company-id>
paperclipai skill delete <skill-id> -C <company-id>
```
| Command | Use |
| --- | --- |
| `list` | List company skills (raw API output). |
| `get <skillId>` | Get one skill's detail by ID. |
| `file <skillId>` | Read a skill file; `--path` defaults to `SKILL.md`. |
| `file:update <skillId>` | Update a skill file. Requires `--payload-json` (a `CompanySkillFileUpdate` body). |
| `create` | Create a local skill. Requires `--payload-json`. |
| `import` | Import skills from a source. Requires `--payload-json`. |
| `scan-projects` | Scan project workspaces for skills. Requires `--payload-json`. |
| `update-status <skillId>` | Read the update status for one skill. |
| `install-update <skillId>` | Install the available update for one skill. |
| `delete <skillId>` | Delete a skill by ID. |
Every `skill` subcommand takes `-C, --company-id <id>`. Prefer the plural `skills` group for everyday work — it gives you reference resolution, confirmation prompts, and readable output that the singular group does not.
---
## See also
- [Agent commands](agent.md) — inspect agents and set up a local agent CLI session.
- [Adapter commands](adapter.md) — the runtimes that reconcile desired skills into files on disk.
- [Company commands](company.md) — company context, export, and import (skills travel in company packages).
- [Project commands](project.md) — the projects and workspaces `scan-projects` reads from.
- [Common options](./common-options.md) and [output and scripting](./output-and-scripting.md) — shared flags and `--json` handling.
+133
View File
@@ -0,0 +1,133 @@
---
paperclip_version: v2026.609.0
---
# Teams Commands
A *team* in the catalog is a ready-made starter group — a small set of agents, projects, tasks, and the skills they need — that Paperclip ships with the app. Use `paperclipai teams` when you want to browse those team templates, inspect what one would create, preview the import against a real company, and then install it. Think of it as the team-shaped equivalent of the skills catalog: instead of one reusable procedure, you get a whole working unit you can drop into a company in one step.
There are two kinds of catalog teams. **Bundled** teams ship inside the app and are the everyday starting points (a core executive team, a product-engineering pod, a product-design team). **Optional** teams are extras you reach for when you need them, and some of them pull in skills from external sources. The `--kind` filter on the browsing commands switches between the two.
> **Tip:** Add `--json` to any command to print the raw API result for scripting. Browsing commands need no company context, but `list`, `preview`, and `install` operate against a company and accept `--company-id <id>` (and respect your selected profile). See [common options](./common-options.md).
---
## Browse before you install
The browsing commands read the catalog without writing anything to a company. There is no company context required to browse, search, or inspect.
```sh
paperclipai teams browse --kind bundled --category company-defaults
paperclipai teams search "engineering" --kind bundled
paperclipai teams inspect core-exec-team
```
| Command | Use |
| --- | --- |
| `browse` | List catalog teams, optionally filtered by `--kind`, `--category`, or `--query`. |
| `search <query>` | Same as `browse` but takes the search text as a positional argument. |
| `inspect <catalogRef>` | Show one catalog team in full — its agents, projects, tasks, required skills, and file manifest. |
Browse and search filters:
| Flag | Use |
| --- | --- |
| `--kind <kind>` | Filter by catalog kind: `bundled` or `optional`. |
| `--category <slug>` | Filter by catalog category slug. |
| `--query <text>` | Free-text search (`browse` only; `search` uses its positional argument). |
A `<catalogRef>` is a catalog team ID, key, or a unique slug. Inspecting a team before you install it is the best way to see exactly which agents and projects will land in the company.
`inspect` can also print a single file out of the team package instead of the manifest detail:
| Flag | Use |
| --- | --- |
| `--file <path>` | Print a specific catalog team file (for example `TEAM.md`) instead of the manifest detail. In human mode the raw content is written to stdout so you can pipe it. |
---
## List with installed status
`teams list` is `browse` with a company lens: it shows each catalog team alongside whether it is already installed in a given company, and whether the installed copy has drifted out of date from the current catalog version. It requires a company context.
```sh
paperclipai teams list --company-id <company-id>
paperclipai teams list --kind optional --company-id <company-id>
```
| Flag | Use |
| --- | --- |
| `--kind <kind>` | Filter by catalog kind: `bundled` or `optional`. |
| `--category <slug>` | Filter by catalog category slug. |
| `--query <text>` | Free-text search. |
Each row reports the team's installed status — not installed, installed, out of date, or installed-but-missing — together with how many agents and projects it carries. A team is flagged out of date when an installed agent's recorded origin differs from the catalog team's current content, which is your cue to reinstall the newer version.
---
## Preview an install
Before you commit, `teams preview` runs the import on the server and shows you the plan without writing anything. It reports what would be created, which skills the team needs and how they would be prepared, and any warnings or errors. It requires a company context.
```sh
paperclipai teams preview core-exec-team --company-id <company-id>
paperclipai teams preview product-engineering \
--target-manager-slug cto \
--collision-strategy rename \
--company-id <company-id>
```
| Flag | Use |
| --- | --- |
| `--target-manager-agent-id <id>` | Existing agent ID that the catalog's root agents should report to. |
| `--target-manager-slug <slug>` | Portable manager slug that the catalog's root agents should report to. |
| `--agent <slug>` | Only preview the selected agent slug. Repeat for several. |
| `--collision-strategy <strategy>` | How to handle existing matches: `rename`, `skip`, or `replace`. |
| `--name-override <slug=name>` | Override an imported entity's name. Repeat for several. |
| `--selected-file <path>` | Restrict the preview to a selected portable file. Repeat for several. |
| `--allow-external-sources` | Allow GitHub, URL, or skills.sh skill sources declared by the catalog team. |
| `--allow-unpinned-optional-sources` | Allow optional-team external skill sources that are not pinned to a commit. |
| `--allow-local-path-sources` | Development only: allow local-path skill sources declared by the catalog team. |
The two manager options let you slot a team in under your existing org chart: instead of the team's root agents standing alone, they report to an agent you already have. Use `--target-manager-agent-id` when you know the exact agent ID, or `--target-manager-slug` for a portable slug.
---
## Install a team
`teams install` does what `preview` shows: it imports the team into the company, creating its agents, projects, tasks, and preparing its required skills. It requires a company context and accepts everything `preview` does, plus a few install-only flags.
```sh
paperclipai teams install core-exec-team --company-id <company-id>
paperclipai teams install product-engineering \
--target-manager-slug cto \
--adapter-override senior-coder=claude_local \
--company-id <company-id>
```
`install` shares all of `preview`'s flags (`--target-manager-agent-id`, `--target-manager-slug`, `--agent`, `--collision-strategy`, `--name-override`, `--selected-file`, and the three `--allow-*` source flags) and adds:
| Flag | Use |
| --- | --- |
| `--secret-value <key=value>` | Provide a value for a secret env input the team declares. Repeat for several. |
| `--adapter-override <slug=type>` | Adapter type override for an imported agent slug — for example `senior-coder=claude_local`. Repeat for several. |
| `--request-approval-on-forbidden` | When the install is denied because you lack `agents:create` permission, create a board approval request instead of exiting with the raw 403. |
| `--approval-issue-id <id>` | Issue ID to link to the fallback approval request. Defaults to `PAPERCLIP_TASK_ID` when that is set. |
Catalog teams ship without a fixed adapter per agent on purpose, so the install lets you choose the runtime. Use `--adapter-override <slug=type>` to pin a specific agent to an adapter such as `claude_local` or `codex_local`; agents you do not override are resolved at import time.
### When you do not have permission to create agents
Installing a team creates agents, which requires the `agents:create` permission. If the server denies the install for that reason, you would normally see a `403`. Pass `--request-approval-on-forbidden` (or run inside a Paperclip task, where `PAPERCLIP_TASK_ID` is set) and the CLI instead files a board approval request describing the install you attempted, so a board operator can approve it. Any secret values you passed are stripped from the stored request and redacted in the returned result.
---
## See also
- [Team Catalog guide](../../guides/org/team-catalog.md) — what catalog teams are and how to use them from the app.
- [Skills commands](skills.md) — the skills catalog, the closest single-capability analogue.
- [Company commands](company.md) — company context, export, and import (catalog installs ride on the same portability engine).
- [Agent commands](agent.md) — inspect the agents an install creates.
- [Approval commands](approval.md) — work with the board approval the forbidden-install fallback raises.
- [Common options](./common-options.md) and [output and scripting](./output-and-scripting.md) — shared flags and `--json` handling.
+140
View File
@@ -0,0 +1,140 @@
---
paperclip_version: v2026.609.0
---
# Token Commands
Use these commands when you need to mint, list, or revoke the API keys that let agents and board operators talk to a Paperclip instance from outside the browser — CI jobs, scripts, headless agent runners, or your own terminal. The `token` group has two halves: `token agent`, which manages the keys an individual agent uses to act on the instance, and `token board`, which manages the keys your board-operator identity uses to drive companies and the control plane.
Every command here accepts the [common client options](./common-options.md) (`--data-dir`, `--api-base`, `--api-key`, `--context`, `--profile`, `--json`). The `agent` subcommands are company-scoped and require `-C, --company-id <id>`; on the `board` side, `--company-id` is optional and only sets the audit context for the key you create.
> **Warning:** Creating a token returns the secret `token` value exactly once, at creation time. Paperclip does not store it in a way you can read back — `token agent list` and `token board list` return metadata only (id, name, timestamps), never the token. Capture the value when you create the key, and treat it as a credential: anyone holding it can act as that agent or board user.
---
## When to reach for each command
| Command | Use |
|---|---|
| `token agent create` | Mint a new API key for a specific agent. |
| `token agent list` | List an agent's existing API keys (metadata only). |
| `token agent revoke` | Revoke one of an agent's API keys by ID. |
| `token board create` | Mint a named API key for your board-operator identity. |
| `token board list` | List the board API keys for the current board user. |
| `token board revoke` | Revoke a board API key by ID. |
---
## Agent API keys
An agent API key is the credential an agent presents when it acts on the instance — the same kind of key a join request mints when it is approved. Use `token agent` when you are provisioning an agent runner yourself and want to create, audit, or retire its keys directly.
Every `token agent` command is company-scoped and identifies the agent with `--agent`, which accepts an agent ID, shortname, or unambiguous name. The CLI resolves that reference to a concrete agent inside the company before it does anything else, so a typo or an agent in the wrong company fails fast with `Agent not found`.
### Create an agent key
`token agent create` mints a new key for the named agent and prints the agent, its company, and the created `key` — including the one-time `token` value.
```sh
paperclipai token agent create --company-id <company-id> --agent <agent> --name ci-runner
```
| Flag | Use |
|---|---|
| `-C, --company-id <id>` | Required. Company the agent belongs to. |
| `--agent <agent>` | Required. Agent ID, shortname, or unambiguous name. |
| `--name <name>` | API key label. Defaults to `cli-agent`. |
> **Tip:** Capture the `token` from the output immediately — it is the only time you will see it. Give each key a descriptive `--name` (for example the host or pipeline it runs on) so a later `token agent list` tells you what each key is for.
### List agent keys
`token agent list` returns the keys defined for an agent. The human-readable output shows each key's `id`, `name`, `createdAt`, and `revokedAt` — never the token itself.
```sh
paperclipai token agent list --company-id <company-id> --agent <agent>
paperclipai token agent list --company-id <company-id> --agent <agent> --json
```
| Flag | Use |
|---|---|
| `-C, --company-id <id>` | Required. Company the agent belongs to. |
| `--agent <agent>` | Required. Agent ID, shortname, or unambiguous name. |
### Revoke an agent key
`token agent revoke` takes the key ID as a positional argument and revokes it. Once revoked, the key can no longer authenticate.
```sh
paperclipai token agent revoke <keyId> --company-id <company-id> --agent <agent>
```
| Argument / Flag | Use |
|---|---|
| `<keyId>` | Required. The agent API key ID to revoke. |
| `-C, --company-id <id>` | Required. Company the agent belongs to. |
| `--agent <agent>` | Required. Agent ID, shortname, or unambiguous name. |
---
## Board API keys
A board API key is the credential your board-operator identity uses to drive the instance from a script — the headless equivalent of being signed in on the board. Use `token board` to mint a long-lived key for automation, see which keys exist for your board user, and revoke one when it is no longer needed.
Unlike the agent commands, `token board` does not require a company. The optional `-C, --company-id <id>` only records the company used for audit context on the key you create.
### Create a board key
`token board create` mints a named key for the current board user and prints the created `key`, including its one-time `token`. By default a board key inherits the server's expiry policy; you can control expiry with exactly one of the expiry flags below.
```sh
# A key that expires 90 days from now
paperclipai token board create --name ci-deploy --ttl-days 90
# A key with an explicit expiry timestamp
paperclipai token board create --name nightly --expires-at 2026-12-31T00:00:00Z
# A non-expiring key
paperclipai token board create --name long-lived --never-expires
```
| Flag | Use |
|---|---|
| `-C, --company-id <id>` | Optional. Company ID recorded for audit context. |
| `--name <name>` | API key label. Defaults to `cli-board`. |
| `--expires-at <iso8601>` | Expiration timestamp. Rejected if it is not a valid date. |
| `--ttl-days <days>` | Expiration in days from now. Must be a positive number. |
| `--never-expires` | Create a non-expiring key. |
> **Note:** The expiry flags are alternatives — `--never-expires` wins, then `--expires-at`, then `--ttl-days`. If you pass none of them, the key takes whatever expiry the server assigns by default.
### List board keys
`token board list` returns the board API keys for the current board user. The human-readable output shows each key's `id`, `name`, `createdAt`, `lastUsedAt`, `expiresAt`, and `revokedAt` — metadata only.
```sh
paperclipai token board list
paperclipai token board list --json
```
### Revoke a board key
`token board revoke` takes the key ID as a positional argument and revokes it.
```sh
paperclipai token board revoke <keyId>
```
| Argument | Use |
|---|---|
| `<keyId>` | Required. The board API key ID to revoke. |
---
## See also
- [Common Options](./common-options.md) — the shared client flags every token command accepts.
- [Authentication](./authentication.md) — `connect`, `auth login`, board claim, and the broader credential bootstrap flow.
- [Access, Profile & Instance Admin](./access.md) — join requests, which mint agent API keys on approval, and `whoami` for confirming the identity a key resolves to.
- [Agent Commands](./agent.md) — create and configure the agents whose keys you mint here.
- [Output and Scripting](./output-and-scripting.md) — using `--json` to capture a freshly minted token in a script.
+173
View File
@@ -0,0 +1,173 @@
---
paperclip_version: v2026.609.0
---
# Workspace, Org & Environment Commands
Use these commands when you need to inspect the *structure* and *execution surface* of a company from the terminal: the org chart, the agent configurations behind each role, the execution workspaces where runs actually do their work, the environments and leases that back them, and the per-project workspaces. These are the lower-level inspection and control commands you reach for when you are debugging a run, wiring up an environment, or confirming what infrastructure a company is sitting on — not the day-to-day task flow.
All of these commands are read-and-control wrappers over the Paperclip API. They TRIGGER and OBSERVE server-side state; the actual execution (runtime services, runtime commands, environment probes) runs on the Paperclip runtime, not in the CLI.
> **Note:** Every command here accepts the common client flags — `--data-dir`, `--api-base`, `--api-key`, `--context`, `--profile`, and `--json`. See [Common Options](./common-options.md). Company-scoped subcommands also accept `-C, --company-id <id>`; ID- and project-scoped subcommands take the id as a positional argument instead.
---
## Org Chart (`org`)
Use `org` to pull the company's org chart — either as structured data or as a rendered diagram you can drop into a doc or a status update.
```sh
paperclipai org get --company-id <company-id>
paperclipai org svg --company-id <company-id> --out org.svg
paperclipai org png --company-id <company-id> --out org.png
```
| Command | What it returns |
|---|---|
| `org get` | The org chart as structured JSON data. |
| `org svg` | The org chart rendered as an SVG image. |
| `org png` | The org chart rendered as a PNG image. |
`get` is company-scoped and respects `--json` like every other read command. `svg` and `png` fetch binary image data:
- With `--out <path>`, the bytes are written to that file and the command prints a small `{ out, bytes }` summary.
- Without `--out`, the raw image bytes stream to stdout — pipe them to a file yourself (`paperclipai org png -C <company-id> > org.png`) or into another tool.
> **Tip:** Reach for `org svg`/`org png` when you want a shareable snapshot of the reporting structure. Use `org get` when you are scripting and need the underlying data.
---
## Agent Configurations (`agent-config`)
`agent-config list` returns the agent configuration summaries for a company — the resolved adapter/model setup behind each agent, in one place. Use it to audit which configurations are in play before you change an environment or hire.
```sh
paperclipai agent-config list --company-id <company-id>
```
It is company-scoped and read-only. Pair it with `--json` when you want to diff configurations across companies or feed them into another tool.
---
## Execution Workspaces (`workspace`)
Execution workspaces are the server-side sandboxes where heartbeat runs do their work. Use the `workspace` commands to list them, inspect a single one, check whether it is safe to close, review its operation history, update its settings, or drive its runtime.
```sh
paperclipai workspace list --company-id <company-id>
paperclipai workspace get <execution-workspace-id>
paperclipai workspace close-readiness <execution-workspace-id>
paperclipai workspace operations <execution-workspace-id>
paperclipai workspace update <execution-workspace-id> --payload-json '{...}'
paperclipai workspace runtime-service <execution-workspace-id> start --payload-json '{...}'
paperclipai workspace runtime-command <execution-workspace-id> run --payload-json '{...}'
```
| Command | Arguments | Use |
|---|---|---|
| `list` | `--company-id <id>` | List all execution workspaces in a company. |
| `get` | `<id>` | Fetch a single execution workspace. |
| `close-readiness` | `<id>` | Check whether the workspace is safe to close (no in-flight work that would be lost). |
| `operations` | `<id>` | List the workspace's operation history. |
| `update` | `<id> --payload-json <json>` | Patch the workspace with a JSON body. |
| `runtime-service` | `<id> <action> [--payload-json <json>]` | Control a runtime service in the workspace. |
| `runtime-command` | `<id> <action> [--payload-json <json>]` | Run a runtime command in the workspace. |
For both `runtime-service` and `runtime-command`, the `<action>` positional is one of `start`, `stop`, `restart`, or `run`. The `--payload-json` flag carries the runtime target as a JSON body and defaults to `{}` when omitted.
```sh
paperclipai workspace runtime-service <execution-workspace-id> restart --payload-json '{"service":"dev-server"}'
paperclipai workspace runtime-command <execution-workspace-id> run --payload-json '{"command":"npm test"}'
```
> **Tip:** Run `workspace close-readiness <id>` before you tear down or close a workspace. It tells you whether closing it now would interrupt active work.
> **Warning:** `runtime-service stop`/`restart` act on live runtime processes a run may be depending on. Confirm with `workspace get` and `workspace operations` that nothing critical is mid-flight first.
---
## Environments (`environment`)
Environments describe *where and how* workspaces run — the backing infrastructure (SSH hosts, containers, capabilities) that runtime services and commands execute against. A lease is a claim on an environment by a workspace. Use these commands to inventory environments, probe their health, create or update them, and inspect the leases against them.
```sh
paperclipai environment list --company-id <company-id>
paperclipai environment capabilities --company-id <company-id>
paperclipai environment create --company-id <company-id> --payload-json '{...}'
paperclipai environment get <environment-id>
paperclipai environment leases <environment-id>
paperclipai environment lease <lease-id>
paperclipai environment update <environment-id> --payload-json '{...}'
paperclipai environment delete <environment-id>
paperclipai environment probe <environment-id>
paperclipai environment probe-config --company-id <company-id> --payload-json '{...}'
```
| Command | Arguments | Use |
|---|---|---|
| `list` | `--company-id <id>` | List a company's environments. |
| `capabilities` | `--company-id <id>` | Report the environment capabilities available to the company. |
| `create` | `--company-id <id> --payload-json <json>` | Create an environment from a JSON body. |
| `get` | `<environment-id>` | Fetch a single environment. |
| `leases` | `<environment-id>` | List the leases held against an environment. |
| `lease` | `<lease-id>` | Fetch a single environment lease by its own id. |
| `update` | `<environment-id> --payload-json <json>` | Patch an environment with a JSON body. |
| `delete` | `<environment-id>` | Delete an environment. |
| `probe` | `<environment-id>` | Probe an existing environment for reachability/health. |
| `probe-config` | `--company-id <id> --payload-json <json>` | Probe a candidate config *before* you create the environment. |
Note the two distinct lease commands: `environment leases <environment-id>` lists every lease *on* one environment, while `environment lease <lease-id>` resolves a *single* lease by its own identifier (it hits `/api/environment-leases/{leaseId}`). Use `leases` when you are looking at an environment and want to know who is using it; use `lease` when you already have a lease id from somewhere else.
```sh
# Validate a config before committing it, then create the environment.
paperclipai environment probe-config --company-id <company-id> --payload-json '{"kind":"ssh","host":"build-01.internal"}'
paperclipai environment create --company-id <company-id> --payload-json '{"name":"build-01","kind":"ssh","host":"build-01.internal"}'
paperclipai environment probe <environment-id>
```
> **Tip:** `probe-config` is the dry-run path — it checks a candidate configuration without persisting anything. `probe` validates an environment that already exists. Probe first, create second.
---
## Project Workspaces (`project-workspace`)
Project workspaces are the workspaces scoped to a single project. They are addressed by a project id (and, for the single-workspace operations, a workspace id) rather than a company id. Use these commands to list, create, update, delete, and drive the workspaces attached to one project.
```sh
paperclipai project-workspace list <project-id>
paperclipai project-workspace create <project-id> --payload-json '{...}'
paperclipai project-workspace update <project-id> <workspace-id> --payload-json '{...}'
paperclipai project-workspace delete <project-id> <workspace-id>
paperclipai project-workspace runtime-service <project-id> <workspace-id> restart --payload-json '{...}'
paperclipai project-workspace runtime-command <project-id> <workspace-id> run --payload-json '{...}'
```
| Command | Arguments | Use |
|---|---|---|
| `list` | `<project-id>` | List the workspaces for a project. |
| `create` | `<project-id> --payload-json <json>` | Create a project workspace. |
| `update` | `<project-id> <workspace-id> --payload-json <json>` | Update a project workspace. The workspace id is required. |
| `delete` | `<project-id> <workspace-id>` | Delete a project workspace. |
| `runtime-service` | `<project-id> <workspace-id> <action> [--payload-json <json>]` | Control a runtime service in a project workspace. |
| `runtime-command` | `<project-id> <workspace-id> <action> [--payload-json <json>]` | Run a runtime command in a project workspace. |
As with execution workspaces, the `<action>` for `runtime-service` and `runtime-command` is one of `start`, `stop`, `restart`, or `run`, and `--payload-json` defaults to `{}`.
```sh
paperclipai project-workspace list <project-id>
paperclipai project-workspace delete <project-id> <workspace-id>
paperclipai project-workspace runtime-command <project-id> <workspace-id> run --payload-json '{"command":"pnpm build"}'
```
> **Note:** `update` requires the workspace id positional — the command rejects the call if you omit it. `create` takes only the project id, since the workspace does not exist yet.
---
## See also
- [Common Options](./common-options.md) — the client flags every command above accepts.
- [Output and Scripting](./output-and-scripting.md) — using `--json` and piping results.
- [Project Commands](./project.md) — managing the projects that own project workspaces.
- [Dev Environments](./dev-environments.md) — the broader environment and SSH-fixture setup story.
- [Run Commands](./run.md) — inspecting the heartbeat runs that execute inside these workspaces.
- [Adapter Commands](./adapter.md) — the adapter/model configuration that `agent-config list` summarizes.
+10 -1
View File
@@ -1,3 +1,7 @@
---
paperclip_version: v2026.609.0
---
# Docker
Use Docker when you want a self-contained Paperclip instance without installing Node or pnpm on the host machine.
@@ -69,10 +73,14 @@ If the bind mount is removed, the instance starts fresh on the next run.
## LLM Adapter Support
The Docker image pre-installs the local CLI tools used by the built-in local adapters:
The Docker image pre-installs the local CLI tools used by the built-in local adapters, so they work inside the container out of the box:
- `claude` for `claude_local`
- `codex` for `codex_local`
- `opencode` for `opencode_local`
- `gemini` for `gemini_local`
The image sets `GEMINI_SANDBOX=false` so the Gemini CLI runs safely inside the container without its own sandbox layer.
If you want those adapters to run inside the container, pass the relevant API keys:
@@ -83,6 +91,7 @@ docker run --name paperclip \
-e PAPERCLIP_HOME=/paperclip \
-e OPENAI_API_KEY=sk-... \
-e ANTHROPIC_API_KEY=sk-... \
-e GEMINI_API_KEY=... \
-v "$(pwd)/data/docker-paperclip:/paperclip" \
paperclip-local
```
+23 -5
View File
@@ -1,5 +1,5 @@
---
paperclip_version: v2026.529.0
paperclip_version: v2026.609.0
---
# Skills Reference
@@ -105,6 +105,20 @@ Community examples in [`paperclipai/companies`](https://github.com/paperclipai/c
These are the canonical references for the file shape, frontmatter style, and supporting-file layout.
### Artifact upload in the bundled `paperclip` skill
The bundled `paperclip` skill ships an artifact-upload helper so that work products end up on the issue, not stranded in the agent's workspace. Its **Generated Artifacts and Work Products** section in `SKILL.md` tells the agent that whenever a run produces a user-inspectable file, it should upload that file to the current issue before final disposition — a local filesystem path is useless to board users, reviewers, and cloud operators who can't reach the agent workspace.
The skill bundles a helper script at `scripts/paperclip-upload-artifact.sh` (relative to the installed skill directory) and a reference doc at `references/artifacts.md`. The helper takes a file path plus `--title` and `--summary` flags:
```bash
scripts/paperclip-upload-artifact.sh path/to/output.webm \
--title "Walkthrough render" \
--summary "Rendered walkthrough for review"
```
It reads the run's environment — `PAPERCLIP_API_URL`, `PAPERCLIP_API_KEY`, `PAPERCLIP_COMPANY_ID`, `PAPERCLIP_TASK_ID`, and `PAPERCLIP_RUN_ID` — then uploads the file as an issue attachment, creates an attachment-backed artifact work product (the default), and prints issue-safe markdown links the agent can drop into its final comment. The underlying upload route is the attachment endpoint documented under [Issues API → Attachments](./api/issues.md#attachments).
---
## 2. Installation pipeline
@@ -187,8 +201,12 @@ Catalog skills are split into two **kinds** (`CatalogSkillKind`):
| Kind | Meaning | Examples |
|---|---|---|
| `bundled` | Core skills the app considers part of the baseline kit. | `doc-maintenance`, `issue-triage`, `task-planning`, `qa-acceptance`, `github-pr-workflow` |
| `optional` | Extra skills you opt into when you need them. | `agent-browser`, `release-announcement`, `design-critique` |
| `bundled` | Core skills the app considers part of the baseline kit. | `doc-maintenance`, `issue-triage`, `task-planning`, `qa-acceptance`, `github-pr-workflow`, `wireframe` |
| `optional` | Extra skills you opt into when you need them. | `agent-browser`, `release-announcement`, `design-critique`, `last30days` |
Each catalog skill also carries a `category` you can filter on. Most of the baseline kit is coding- and process-oriented, but the catalog is not limited to engineering work — for example, the `product`-category **`wireframe`** skill teaches an agent to produce low-fidelity, black-and-white UI wireframes as standalone SVG files (recommended for `designer`, `product`, and `engineer` roles), and the `research`-category **`last30days`** skill (recommended for `researcher`, `marketer`, `product-manager`, and `analyst` roles) researches what people have said about a topic across Reddit, X, YouTube, and the rest of the web in the last 30 days. A bundled skill is not the same as a *required* one: `wireframe` ships in the baseline kit but installs only when you choose it (its `defaultInstall` is `false`), so designers and product folks can pull it in without it being forced on every agent.
**Where a catalog skill's bytes come from.** Most catalog skills are shipped inside the app itself. A few are **referenced** instead — they point at an external GitHub repository pinned to a single commit, and the app fetches their files for you. `last30days`, for example, is sourced from `github.com/mvanhorn/last30days-skill` at a pinned commit. You install and manage a referenced skill exactly like any other catalog skill; the only difference is that its `source` field tells you where it originally came from (owner, repo, the `ref`/`commit` it is pinned to, and a browseable `url`). Because the pin is a specific commit, you still get the same byte-exact, version-tracked guarantees as a fully bundled skill.
Once installed, a catalog skill becomes an ordinary company-skill row, but it is tagged as **catalog-managed**: its `sourceType` is `catalog` and its `metadata.sourceKind` is `catalog`. The catalog kind (`bundled` / `optional`) is carried through on `metadata.catalogKind`, and the byte-exact origin it was pinned to is stored in `metadata.originHash`.
@@ -210,7 +228,7 @@ The list route accepts three optional query filters:
- `category` — exact-match on the skill's category.
- `q` — free-text query matched against the skill's id, key, slug, name, description, category, kind, recommended roles, and tags.
Each catalog entry (`CatalogSkill`) carries `id`, `key`, `kind`, `category`, `slug`, `name`, `description`, `trustLevel`, `compatibility`, `defaultInstall`, `recommendedForRoles`, `requires`, `tags`, a `files[]` inventory (each with `path`, `kind`, `sizeBytes`, `sha256`), and a `contentHash` — the byte-exact identity of the shipped skill.
Each catalog entry (`CatalogSkill`) carries `id`, `key`, `kind`, `category`, `slug`, `name`, `description`, `trustLevel`, `compatibility`, `defaultInstall`, `recommendedForRoles`, `requires`, `tags`, a `files[]` inventory (each with `path`, `kind`, `sizeBytes`, `sha256`), and a `contentHash` — the byte-exact identity of the shipped skill. Referenced skills (the ones fetched from an external repo) also carry an optional `source` object describing where the bytes came from: `type` (`github`), `hostname`, `owner`, `repo`, the `ref` and `commit` it is pinned to, the `path` inside the repo, and a browseable `url`.
The files route returns one file at a time. By default it reads `SKILL.md`; pass `?path=references/example.md` to read another file in the inventory. Asset files are not previewable.
@@ -281,7 +299,7 @@ POST /api/companies/{companyId}/skills/{skillId}/reset
Reset restores a catalog-managed skill to the byte-exact origin it was installed from — useful when a skill was edited locally and you want the shipped version back. Reset is only supported for catalog-managed skills.
For the equivalent `paperclipai skills` CLI commands (browse, install, audit, update, reset), see [Control-plane commands](./cli/control-plane-commands.md) and the [CLI command reference](./cli/commands.md).
For the equivalent `paperclipai skills` CLI commands (browse, install, audit, update, reset), see the [Skills commands](./cli/skills.md) page in the CLI reference.
---
+14 -1
View File
@@ -74,7 +74,20 @@
"docs/how-to/write-a-company-skill.md"
],
"tier": "pr",
"detect": "Any file under skills/** in parent — these are canonical skill bundles. New top-level dir under skills/ = candidate new skill bundle (consult scripts/sync/detect-renames.mjs to confirm it's not a rename of an existing bundle before creating a new doc entry); mention in the skills reference. Changes inside an existing bundle = update the tutorial or reference as appropriate. The plugin SDK is a separate concern — see the plugin-sdk watcher."
"detect": "Any file under skills/** in parent — these are canonical skill bundles. New top-level dir under skills/ = candidate new skill bundle (consult scripts/sync/detect-renames.mjs to confirm it's not a rename of an existing bundle before creating a new doc entry); mention in the skills reference. Changes inside an existing bundle = update the tutorial or reference as appropriate. The plugin SDK is a separate concern — see the plugin-sdk watcher. The app-shipped skills catalog is a separate concern — see the skills-catalog watcher."
},
{
"name": "skills-catalog",
"parent_paths": [
"packages/skills-catalog/catalog/**/SKILL.md",
"packages/skills-catalog/generated/catalog.json"
],
"docs_paths": [
"docs/reference/skills.md",
"docs/guides/org/skills.md"
],
"tier": "pr",
"detect": "The app-shipped skills catalog — installable bundled/optional skills, distinct from the server-forced bundled skills under skills/** (covered by the agent-skills watcher). Skill bundles live at packages/skills-catalog/catalog/{bundled,optional}/<category>/<skill>/SKILL.md; packages/skills-catalog/generated/catalog.json is the generated manifest of catalog membership and metadata. A new SKILL.md under catalog/ = candidate new catalog skill — consult scripts/sync/detect-renames.mjs before treating a moved bundle as brand-new. Document new or changed catalog skills in the Skills reference (app-shipped catalog section) and the org Skills guide. catalog.json churn alone (reorder/regen) without a SKILL.md change is usually context-only."
},
{
"name": "schemas",
+129 -5
View File
@@ -54,6 +54,10 @@
"title": "Issues",
"file": "../docs/guides/day-to-day/issues.md"
},
{
"title": "Artifacts",
"file": "../docs/guides/day-to-day/artifacts.md"
},
{
"title": "Blocked Inbox",
"file": "../docs/guides/day-to-day/blocked-inbox.md"
@@ -90,6 +94,10 @@
"title": "Skills",
"file": "../docs/guides/org/skills.md"
},
{
"title": "Team Catalog",
"file": "../docs/guides/org/team-catalog.md"
},
{
"title": "Org Structure",
"file": "../docs/guides/org/org-structure.md"
@@ -242,6 +250,10 @@
"title": "Settings",
"file": "../docs/administration/settings.md"
},
{
"title": "Trust & Low-Trust Review",
"file": "../docs/administration/trust-and-low-trust-review.md"
},
{
"title": "Plugins",
"file": "../docs/administration/plugins.md"
@@ -314,6 +326,10 @@
"title": "Adapters",
"file": "../docs/reference/api/adapters.md"
},
{
"title": "Teams Catalog",
"file": "../docs/reference/api/teams-catalog.md"
},
{
"title": "Plugins",
"file": "../docs/reference/api/plugins.md"
@@ -328,23 +344,131 @@
"tier": "Reference",
"title": "CLI",
"icon": "terminal",
"desc": "Command-line reference for local setup and talking to the control plane.",
"desc": "Complete command-line reference: local setup, identity, and every control-plane command family.",
"pages": [
{
"title": "Overview",
"file": "../docs/reference/cli/overview.md"
},
{
"title": "Installing the CLI",
"file": "../docs/reference/cli/installation.md"
},
{
"title": "Common Options",
"file": "../docs/reference/cli/common-options.md"
},
{
"title": "Output & Scripting",
"file": "../docs/reference/cli/output-and-scripting.md"
},
{
"title": "Setup Commands",
"file": "../docs/reference/cli/setup-commands.md"
},
{
"title": "Control-Plane Commands",
"file": "../docs/reference/cli/control-plane-commands.md"
"title": "Dev Environments",
"file": "../docs/reference/cli/dev-environments.md"
},
{
"title": "Command Reference",
"file": "../docs/reference/cli/commands.md"
"title": "Authentication & Tokens",
"file": "../docs/reference/cli/authentication.md"
},
{
"title": "Connect",
"file": "../docs/reference/cli/connect.md"
},
{
"title": "Token",
"file": "../docs/reference/cli/token.md"
},
{
"title": "Company",
"file": "../docs/reference/cli/company.md"
},
{
"title": "Project",
"file": "../docs/reference/cli/project.md"
},
{
"title": "Goal",
"file": "../docs/reference/cli/goal.md"
},
{
"title": "Issue",
"file": "../docs/reference/cli/issue.md"
},
{
"title": "Agent",
"file": "../docs/reference/cli/agent.md"
},
{
"title": "Prompt",
"file": "../docs/reference/cli/prompt.md"
},
{
"title": "Run (Heartbeat)",
"file": "../docs/reference/cli/run.md"
},
{
"title": "Routine",
"file": "../docs/reference/cli/routine.md"
},
{
"title": "Adapter",
"file": "../docs/reference/cli/adapter.md"
},
{
"title": "Approval",
"file": "../docs/reference/cli/approval.md"
},
{
"title": "Activity",
"file": "../docs/reference/cli/activity.md"
},
{
"title": "Dashboard",
"file": "../docs/reference/cli/dashboard.md"
},
{
"title": "Cost & Budget",
"file": "../docs/reference/cli/cost.md"
},
{
"title": "Workspace",
"file": "../docs/reference/cli/workspace.md"
},
{
"title": "Access & Admin",
"file": "../docs/reference/cli/access.md"
},
{
"title": "Skills",
"file": "../docs/reference/cli/skills.md"
},
{
"title": "Teams",
"file": "../docs/reference/cli/teams.md"
},
{
"title": "Secrets",
"file": "../docs/reference/cli/secrets.md"
},
{
"title": "Cloud",
"file": "../docs/reference/cli/cloud.md"
},
{
"title": "Plugin",
"file": "../docs/reference/cli/plugin.md"
},
{
"title": "Asset",
"file": "../docs/reference/cli/asset.md"
},
{
"title": "Feedback",
"file": "../docs/reference/cli/feedback.md"
}
]
},
+3 -11
View File
@@ -2,12 +2,10 @@
"user-guides/guides/what-is-paperclip": "guides/welcome/what-is-paperclip",
"user-guides/guides/key-concepts": "guides/welcome/key-concepts",
"user-guides/guides/glossary": "guides/welcome/glossary",
"user-guides/guides/installation": "guides/getting-started/installation",
"user-guides/guides/your-first-company": "guides/getting-started/your-first-company",
"user-guides/guides/your-first-agent": "guides/getting-started/your-first-agent",
"user-guides/guides/watching-agents-work": "guides/getting-started/watching-agents-work",
"user-guides/guides/dashboard": "guides/day-to-day/dashboard",
"user-guides/guides/managing-tasks": "guides/day-to-day/issues",
"guides/day-to-day/managing-tasks": "guides/day-to-day/issues",
@@ -16,26 +14,21 @@
"guides/day-to-day/costs-and-budgets": "guides/day-to-day/costs",
"user-guides/guides/activity-log": "guides/day-to-day/activity-log",
"user-guides/guides/feedback-voting": "guides/day-to-day/feedback-voting",
"guides/agent-developer/how-agents-work": "guides/org/how-agents-work",
"user-guides/guides/skills": "guides/org/skills",
"user-guides/guides/org-structure": "guides/org/org-structure",
"user-guides/guides/delegation": "guides/org/delegation",
"user-guides/guides/agent-adapters": "guides/org/agent-adapters",
"user-guides/guides/heartbeats-and-routines": "guides/projects-workflow/routines",
"guides/projects-workflow/heartbeats-and-routines": "guides/projects-workflow/routines",
"user-guides/guides/execution-workspaces": "guides/projects-workflow/workspaces",
"guides/projects-workflow/execution-workspaces": "guides/projects-workflow/workspaces",
"user-guides/guides/execution-policy": "guides/power/execution-policy",
"user-guides/guides/export-import": "guides/power/export-import",
"user-guides/guides/terminal-setup": "guides/power/terminal-setup",
"user-guides/guides/administration/company": "administration/company",
"user-guides/guides/administration/settings": "administration/settings",
"user-guides/guides/administration/plugins": "administration/plugins",
"api/overview": "reference/api/overview",
"api/authentication": "reference/api/authentication",
"api/companies": "reference/api/companies",
@@ -48,11 +41,9 @@
"api/activity": "reference/api/activity",
"api/dashboard": "reference/api/dashboard",
"api/routines": "reference/api/routines",
"cli/overview": "reference/cli/overview",
"cli/setup-commands": "reference/cli/setup-commands",
"cli/control-plane-commands": "reference/cli/control-plane-commands",
"adapters/overview": "reference/adapters/overview",
"adapters/claude-local": "reference/adapters/claude-local",
"adapters/codex-local": "reference/adapters/codex-local",
@@ -67,7 +58,6 @@
"adapters/external-adapters": "reference/adapters/external-adapters",
"adapters/creating-an-adapter": "reference/adapters/creating-an-adapter",
"adapters/adapter-ui-parser": "reference/adapters/adapter-ui-parser",
"deploy/overview": "reference/deploy/overview",
"deploy/deployment-modes": "reference/deploy/deployment-modes",
"deploy/local-development": "reference/deploy/local-development",
@@ -76,5 +66,7 @@
"deploy/storage": "reference/deploy/storage",
"deploy/secrets": "reference/deploy/secrets",
"deploy/environment-variables": "reference/deploy/environment-variables",
"deploy/tailscale-private-access": "reference/deploy/tailscale-private-access"
"deploy/tailscale-private-access": "reference/deploy/tailscale-private-access",
"reference/cli/commands": "reference/cli/overview",
"reference/cli/control-plane-commands": "reference/cli/overview"
}