@iamlbccc/tdxd 1.1.0 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,98 @@
1
+ # Generated Artifacts and Work Products
2
+
3
+ When work produces a user-inspectable file, upload true deliverables to the current issue before final disposition. Local filesystem paths are not enough because board users, reviewers, and cloud operators may not have access to the agent workspace.
4
+
5
+ Use the helper bundled with this skill. From an installed `paperclip` skill directory, the helper lives at `scripts/paperclip-upload-artifact.sh`:
6
+
7
+ ```bash
8
+ scripts/paperclip-upload-artifact.sh path/to/output.webm \
9
+ --title "Walkthrough render" \
10
+ --summary "Rendered walkthrough for review"
11
+ ```
12
+
13
+ The helper uses `PAPERCLIP_API_URL`, `PAPERCLIP_API_KEY`, `PAPERCLIP_COMPANY_ID`, `PAPERCLIP_TASK_ID`, and `PAPERCLIP_RUN_ID`. It uploads the file as an issue attachment, creates an attachment-backed artifact work product by default, and prints issue-safe markdown links for your final comment.
14
+
15
+ ## Workspace-Only File References
16
+
17
+ Use a workspace-only reference only when the file should stay in the project or
18
+ execution workspace, such as a source file, committed report, generated index,
19
+ or other file whose value is tied to the checkout. This is not a substitute for
20
+ uploading a deliverable file that a board user should be able to inspect outside
21
+ the workspace.
22
+
23
+ Annotate the work product with `metadata.resourceRef`:
24
+
25
+ ```json
26
+ {
27
+ "type": "document",
28
+ "provider": "workspace",
29
+ "title": "Regression test plan",
30
+ "status": "ready_for_review",
31
+ "reviewState": "needs_board_review",
32
+ "summary": "Markdown plan committed in the execution workspace.",
33
+ "metadata": {
34
+ "resourceRef": {
35
+ "kind": "workspace_file",
36
+ "issueId": "<issue-id>",
37
+ "workspaceKind": "execution_workspace",
38
+ "workspaceId": "<execution-workspace-id>",
39
+ "relativePath": "doc/plans/regression-test-plan.md",
40
+ "line": 1,
41
+ "displayPath": "doc/plans/regression-test-plan.md"
42
+ }
43
+ }
44
+ }
45
+ ```
46
+
47
+ `workspaceKind` is `execution_workspace` for the current issue checkout or
48
+ `project_workspace` for a shared project workspace. `line` and `column` are
49
+ optional positive integers. `relativePath` must be relative to the selected
50
+ workspace root; do not use host-local absolute paths in `resourceRef`.
51
+
52
+ Create the work product with:
53
+
54
+ ```bash
55
+ curl -sS -X POST \
56
+ "$PAPERCLIP_API_URL/api/issues/$PAPERCLIP_TASK_ID/work-products" \
57
+ -H "Authorization: Bearer $PAPERCLIP_API_KEY" \
58
+ -H "X-Paperclip-Run-Id: $PAPERCLIP_RUN_ID" \
59
+ -H "Content-Type: application/json" \
60
+ --data-binary @workspace-file-work-product.json
61
+ ```
62
+
63
+ If the helper is unavailable, use the Paperclip API directly:
64
+
65
+ ```bash
66
+ curl -sS -X POST \
67
+ "$PAPERCLIP_API_URL/api/companies/$PAPERCLIP_COMPANY_ID/issues/$PAPERCLIP_TASK_ID/attachments" \
68
+ -H "Authorization: Bearer $PAPERCLIP_API_KEY" \
69
+ -H "X-Paperclip-Run-Id: $PAPERCLIP_RUN_ID" \
70
+ -F 'file=@"path/to/output.webm";type=video/webm'
71
+ ```
72
+
73
+ Then create a work product when the file is the deliverable. The server canonicalizes attachment-backed artifact metadata from the `attachmentId`:
74
+
75
+ ```bash
76
+ curl -sS -X POST \
77
+ "$PAPERCLIP_API_URL/api/issues/$PAPERCLIP_TASK_ID/work-products" \
78
+ -H "Authorization: Bearer $PAPERCLIP_API_KEY" \
79
+ -H "X-Paperclip-Run-Id: $PAPERCLIP_RUN_ID" \
80
+ -H "Content-Type: application/json" \
81
+ --data-binary '{
82
+ "type": "artifact",
83
+ "provider": "paperclip",
84
+ "title": "Walkthrough render",
85
+ "status": "ready_for_review",
86
+ "reviewState": "needs_board_review",
87
+ "isPrimary": true,
88
+ "metadata": { "attachmentId": "<uploaded-attachment-id>" }
89
+ }'
90
+ ```
91
+
92
+ In your final issue comment, link the uploaded attachment or work product and
93
+ describe what it contains. If the output is workspace-only, name the work
94
+ product and the relative path that was recorded in `metadata.resourceRef`.
95
+ Browse/search is the fallback for recovering a workspace file when the issue
96
+ chip or link cannot open it; it is not the preferred deliverable path. Do not
97
+ leave artifact-producing work `in_progress` with only a local path or a
98
+ `Remaining` note.
@@ -0,0 +1,295 @@
1
+ # Cases
2
+
3
+ Cases are agent-owned work records for durable outputs such as blog posts,
4
+ research packets, release notes, incidents, QA runs, or generated asset sets.
5
+ They are company-scoped and live beside issues: issues coordinate work, while
6
+ cases preserve the structured object an agent is producing.
7
+
8
+ Cases are experimental and must be enabled with `experimental.enableCases`.
9
+ If a route returns `403 Cases are disabled`, stop and report that the operator
10
+ must enable cases before the skill can use this surface.
11
+
12
+ ## Core Model
13
+
14
+ A case has:
15
+
16
+ - `identifier`: server-assigned display id such as `PAP-C42`
17
+ - `caseType`: skill-owned type such as `blog_post`, `image_assets`, or `incident`
18
+ - `key`: optional deterministic upsert key inside `(companyId, caseType)`
19
+ - `title` and optional `summary`
20
+ - `status`: `draft`, `in_progress`, `in_review`, `approved`, `done`, or `cancelled`
21
+ - `fields`: JSON object owned by the skill using the case
22
+ - `parentCaseId`: optional parent case for child work
23
+ - documents, attachments, issue links, labels, and events
24
+
25
+ Use deterministic `caseType` + `key` when a skill may be retried. Repeating
26
+ `POST /api/companies/:companyId/cases` with the same `caseType` and `key`
27
+ upserts the same case instead of creating a duplicate.
28
+
29
+ ## Upsert Semantics
30
+
31
+ `POST /api/companies/:companyId/cases` creates or upserts a case.
32
+
33
+ Request:
34
+
35
+ ```json
36
+ {
37
+ "caseType": "blog_post",
38
+ "key": "launch-announcement",
39
+ "title": "Launch announcement",
40
+ "summary": "Draft launch post for operators.",
41
+ "status": "draft",
42
+ "fields": {
43
+ "slug": "launch-announcement",
44
+ "target_audience": "operators"
45
+ }
46
+ }
47
+ ```
48
+
49
+ Response:
50
+
51
+ - `201` when a new case was created
52
+ - `200` when an existing `(caseType, key)` case was updated
53
+
54
+ Field behavior on upsert:
55
+
56
+ - `title` is required and replaces the previous title.
57
+ - `projectId`, `summary`, `status`, `fields`, and `parentCaseId` replace the
58
+ previous value when present.
59
+ - Omitted optional values preserve the previous value during upsert.
60
+ - `fields` is replaced as a whole object when provided. It is not deep-merged.
61
+ Send the complete desired JSON object each time.
62
+ - Concurrent retries with the same `(caseType, key)` converge to one case.
63
+
64
+ Do not use a random `key` for retryable skills. Use a stable content slug,
65
+ external id, source URL hash, or parent-derived request key.
66
+
67
+ ## Read And Search
68
+
69
+ Get a case by UUID or identifier:
70
+
71
+ ```http
72
+ GET /api/cases/PAP-C42
73
+ ```
74
+
75
+ List cases for a company:
76
+
77
+ ```http
78
+ GET /api/companies/:companyId/cases?type=blog_post&status=active&q=launch
79
+ ```
80
+
81
+ Useful filters:
82
+
83
+ - `type`: exact `caseType`
84
+ - `status`: exact lifecycle status, or `active` for non-terminal cases
85
+ - `projectId` / `project`: project UUID
86
+ - `labelId` / `label`: label UUID
87
+ - `q`: identifier, title, summary, or key search
88
+ - `limit`: 1-200, default 100
89
+
90
+ ## Documents
91
+
92
+ Use case documents for rich bodies such as drafts, briefs, reports, or plans.
93
+
94
+ ```http
95
+ PUT /api/cases/:caseIdOrIdentifier/documents/body
96
+ Content-Type: application/json
97
+
98
+ {
99
+ "title": "Launch announcement body",
100
+ "format": "markdown",
101
+ "body": "# Launch announcement\n\nDraft copy...",
102
+ "changeSummary": "Initial draft"
103
+ }
104
+ ```
105
+
106
+ Updating an existing case document requires `baseRevisionId`:
107
+
108
+ ```json
109
+ {
110
+ "baseRevisionId": "latest-revision-uuid",
111
+ "body": "Updated body"
112
+ }
113
+ ```
114
+
115
+ If you get `409 stale_base_revision`, refetch the case detail, read the latest
116
+ document revision id, merge intentionally, and retry with that `baseRevisionId`.
117
+
118
+ ## Fields
119
+
120
+ Each skill owns the schema of `fields` for the `caseType` it creates. Keep fields
121
+ small, typed, and stable enough for other agents to inspect.
122
+
123
+ Examples:
124
+
125
+ ```json
126
+ {
127
+ "slug": "launch-announcement",
128
+ "target_audience": "operators",
129
+ "publish_url": "https://example.com/blog/launch-announcement"
130
+ }
131
+ ```
132
+
133
+ Patch fields or status with:
134
+
135
+ ```http
136
+ PATCH /api/cases/:caseIdOrIdentifier
137
+ Content-Type: application/json
138
+
139
+ {
140
+ "status": "in_review",
141
+ "fields": {
142
+ "slug": "launch-announcement",
143
+ "target_audience": "operators",
144
+ "publish_url": "https://example.com/blog/launch-announcement"
145
+ }
146
+ }
147
+ ```
148
+
149
+ Remember: `fields` replaces the whole object when present.
150
+
151
+ ## Issue Links
152
+
153
+ Link cases to issues explicitly when needed:
154
+
155
+ ```http
156
+ POST /api/cases/:caseIdOrIdentifier/links
157
+ Content-Type: application/json
158
+
159
+ {
160
+ "issueId": "issue-uuid",
161
+ "role": "reference"
162
+ }
163
+ ```
164
+
165
+ Roles:
166
+
167
+ - `origin`: the issue/run that created the case
168
+ - `work`: an issue/run that changed the case
169
+ - `reference`: related issue context
170
+
171
+ Agent run writes auto-link the run's issue when Paperclip can resolve it from
172
+ the run JWT or `X-Paperclip-Run-Id`. Creation/upsert writes use `origin`; later
173
+ document, patch, and attachment writes use `work` when no link already exists.
174
+ You do not need to manually link the current issue before writing the case.
175
+
176
+ ## Child Cases
177
+
178
+ Create child cases by setting `parentCaseId` to the parent case UUID.
179
+
180
+ ```json
181
+ {
182
+ "caseType": "image_assets",
183
+ "key": "launch-announcement:hero-images",
184
+ "title": "Hero images for launch announcement",
185
+ "parentCaseId": "parent-case-uuid",
186
+ "fields": {
187
+ "required_assets": ["hero", "social-card"]
188
+ }
189
+ }
190
+ ```
191
+
192
+ Use child cases when the output has independently inspectable pieces or when
193
+ another agent can work on a bounded part without editing the parent case body.
194
+
195
+ ## Attachments
196
+
197
+ Attach generated files with multipart form data:
198
+
199
+ ```http
200
+ POST /api/cases/:caseIdOrIdentifier/attachments
201
+ Content-Type: multipart/form-data
202
+
203
+ file=@hero.png
204
+ ```
205
+
206
+ The server records an asset and adds an `attachment_added` case event.
207
+
208
+ ## Lifecycle
209
+
210
+ Use the lifecycle consistently:
211
+
212
+ - `draft`: case exists but useful work has not started
213
+ - `in_progress`: an agent is actively producing or revising it
214
+ - `in_review`: ready for reviewer, board, or downstream approval
215
+ - `approved`: accepted but not finally shipped or archived
216
+ - `done`: complete and no further action remains
217
+ - `cancelled`: intentionally abandoned
218
+
219
+ Terminal statuses are `done` and `cancelled`; setting either records
220
+ `completedAt`. Moving back to a non-terminal status clears `completedAt`.
221
+
222
+ ## Worked Blog Post Example
223
+
224
+ Create or upsert the parent blog post:
225
+
226
+ ```http
227
+ POST /api/companies/:companyId/cases
228
+ Content-Type: application/json
229
+
230
+ {
231
+ "caseType": "blog_post",
232
+ "key": "paperclip-cases-launch",
233
+ "title": "Introducing Paperclip Cases",
234
+ "summary": "Blog post explaining the cases surface for agent outputs.",
235
+ "status": "in_progress",
236
+ "fields": {
237
+ "slug": "paperclip-cases-launch",
238
+ "target_audience": "AI company operators",
239
+ "publish_url": null
240
+ }
241
+ }
242
+ ```
243
+
244
+ Write the body:
245
+
246
+ ```http
247
+ PUT /api/cases/PAP-C42/documents/body
248
+ Content-Type: application/json
249
+
250
+ {
251
+ "title": "Introducing Paperclip Cases",
252
+ "format": "markdown",
253
+ "body": "# Introducing Paperclip Cases\n\n..."
254
+ }
255
+ ```
256
+
257
+ Create the child image-assets case:
258
+
259
+ ```http
260
+ POST /api/companies/:companyId/cases
261
+ Content-Type: application/json
262
+
263
+ {
264
+ "caseType": "image_assets",
265
+ "key": "paperclip-cases-launch:image-assets",
266
+ "title": "Image assets for Introducing Paperclip Cases",
267
+ "parentCaseId": "parent-case-uuid",
268
+ "status": "in_progress",
269
+ "fields": {
270
+ "slug": "paperclip-cases-launch",
271
+ "required_assets": ["hero", "social-card"],
272
+ "publish_url": null
273
+ }
274
+ }
275
+ ```
276
+
277
+ Attach generated assets to the child, then patch both cases as they move through
278
+ review:
279
+
280
+ ```http
281
+ PATCH /api/cases/PAP-C42
282
+ Content-Type: application/json
283
+
284
+ {
285
+ "status": "in_review",
286
+ "fields": {
287
+ "slug": "paperclip-cases-launch",
288
+ "target_audience": "AI company operators",
289
+ "publish_url": "https://example.com/blog/paperclip-cases-launch"
290
+ }
291
+ }
292
+ ```
293
+
294
+ If the same skill retries the example with the same keys, it updates the parent
295
+ and child cases rather than creating duplicates.
@@ -0,0 +1,266 @@
1
+ # Company Skills Workflow
2
+
3
+ Use this reference when a board user, CEO, or manager asks you to find a skill, install it into the company library, or assign it to an agent.
4
+
5
+ ## What Exists
6
+
7
+ - App-shipped catalog: a curated set of company skills in `@paperclipai/skills-catalog`, browseable and installable without leaving Paperclip.
8
+ - Company skill library: install, inspect, update, audit, reset, and read company skills for the whole company.
9
+ - Agent skill assignment: add or remove company skills on an existing agent.
10
+ - Hire/create composition: pass `desiredSkills` when creating or hiring an agent so the same assignment model applies immediately.
11
+
12
+ The canonical model is:
13
+
14
+ 1. add the skill to the company library — either from the app catalog (`skills install`), an external source (`skills import`), or a managed local skill (`skills create`/`skills scan-projects`)
15
+ 2. attach the company skill to the agent (`skills agent sync`)
16
+ 3. optionally do step 2 during hire/create with `desiredSkills`
17
+
18
+ Catalog install ≠ agent attach. Installing a catalog skill only adds the row to
19
+ `company_skills`. The agent will not use it until you sync the agent's desired
20
+ set.
21
+
22
+ ## Permission Model
23
+
24
+ - Company skill reads: any same-company actor
25
+ - Company skill mutations: open to same-company actors by default. Missing `skills:create` grants and `canCreateSkills` settings do not deny ordinary skill work; only an explicit company skill policy restriction does. Core safety and company-boundary checks always remain enforced.
26
+ - Agent skill assignment: same permission model as updating that agent
27
+ - Team installs continue to require `agents:create` because they import or create agents in addition to attaching skills.
28
+
29
+ ## Core Endpoints
30
+
31
+ App-shipped catalog (read-only browse + company install):
32
+
33
+ - `GET /api/skills/catalog`
34
+ - `GET /api/skills/catalog/:catalogId`
35
+ - `GET /api/skills/catalog/ref?ref=<id|key|slug>`
36
+ - `GET /api/skills/catalog/:catalogId/files?path=SKILL.md`
37
+ - `POST /api/companies/:companyId/skills/install-catalog`
38
+
39
+ Company library:
40
+
41
+ - `GET /api/companies/:companyId/skills`
42
+ - `GET /api/companies/:companyId/skills/:skillId`
43
+ - `GET /api/companies/:companyId/skills/:skillId/files?path=SKILL.md`
44
+ - `POST /api/companies/:companyId/skills` (managed local create)
45
+ - `POST /api/companies/:companyId/skills/import`
46
+ - `POST /api/companies/:companyId/skills/scan-projects`
47
+ - `GET /api/companies/:companyId/skills/:skillId/update-status`
48
+ - `POST /api/companies/:companyId/skills/:skillId/install-update`
49
+ - `POST /api/companies/:companyId/skills/:skillId/audit`
50
+ - `POST /api/companies/:companyId/skills/:skillId/reset`
51
+ - `DELETE /api/companies/:companyId/skills/:skillId`
52
+
53
+ Agent attach and hire/create composition:
54
+
55
+ - `GET /api/agents/:agentId/skills`
56
+ - `POST /api/agents/:agentId/skills/sync`
57
+ - `POST /api/companies/:companyId/agent-hires`
58
+ - `POST /api/companies/:companyId/agents`
59
+
60
+ If a board user, CEO, or manager is driving locally, prefer the
61
+ `paperclipai skills` CLI documented in `doc/CLI.md` — it wraps every endpoint
62
+ above, accepts company skill or catalog refs by `id`/`key`/`slug`, and prints
63
+ the same JSON these endpoints return when called with `--json`.
64
+
65
+ ## Install A Skill Into The Company
66
+
67
+ Two paths cover the common cases:
68
+
69
+ 1. **App-shipped catalog** (preferred when the right skill exists in the
70
+ bundled/optional catalog) — browse it first, then install with the catalog
71
+ install endpoint. No external network fetch happens.
72
+ 2. **External source** (skills.sh, GitHub, local path, or URL) — use the
73
+ import endpoint below.
74
+
75
+ ### App-shipped catalog
76
+
77
+ Browse, inspect, and install catalog skills before reaching for an external
78
+ source. Bundled skills are the curated defaults for any company; optional
79
+ skills are role- or domain-specific.
80
+
81
+ ```sh
82
+ curl -sS "$PAPERCLIP_API_URL/api/skills/catalog?kind=bundled" \
83
+ -H "Authorization: Bearer $PAPERCLIP_API_KEY"
84
+
85
+ curl -sS "$PAPERCLIP_API_URL/api/skills/catalog/ref?ref=github-pr-workflow" \
86
+ -H "Authorization: Bearer $PAPERCLIP_API_KEY"
87
+
88
+ curl -sS -X POST "$PAPERCLIP_API_URL/api/companies/$PAPERCLIP_COMPANY_ID/skills/install-catalog" \
89
+ -H "Authorization: Bearer $PAPERCLIP_API_KEY" \
90
+ -H "Content-Type: application/json" \
91
+ -d '{
92
+ "catalogSkillId": "paperclipai:bundled:software-development:github-pr-workflow"
93
+ }'
94
+ ```
95
+
96
+ The install response records provenance (`catalogId`, `catalogKey`,
97
+ `packageVersion`, `originHash`) on the company skill so update/audit/reset
98
+ flows know the pinned origin. `force: true` may replace a same-key
99
+ catalog-managed skill but never bypasses hard-stop audit findings.
100
+
101
+ ### External source import
102
+
103
+ Import using a **skills.sh URL**, a key-style source string, a GitHub URL, or a local path.
104
+
105
+ ### Source types (in order of preference)
106
+
107
+ | Source format | Example | When to use |
108
+ |---|---|---|
109
+ | **skills.sh URL** | `https://skills.sh/google-labs-code/stitch-skills/design-md` | When a user gives you a `skills.sh` link. This is the managed skill registry — **always prefer it when available**. |
110
+ | **Key-style string** | `google-labs-code/stitch-skills/design-md` | Shorthand for the same skill — `org/repo/skill-name` format. Equivalent to the skills.sh URL. |
111
+ | **GitHub URL** | `https://github.com/vercel-labs/agent-browser` | When the skill is in a GitHub repo but not on skills.sh. |
112
+ | **Local path** | `/abs/path/to/skill-dir` | When the skill is on disk (dev/testing only). |
113
+
114
+ **Critical:** If a user gives you a `https://skills.sh/...` URL, use that URL or its key-style equivalent (`org/repo/skill-name`) as the `source`. Do **not** convert it to a GitHub URL — skills.sh is the managed registry and the source of truth for versioning, discovery, and updates.
115
+
116
+ ### Example: skills.sh import (preferred)
117
+
118
+ ```sh
119
+ curl -sS -X POST "$PAPERCLIP_API_URL/api/companies/$PAPERCLIP_COMPANY_ID/skills/import" \
120
+ -H "Authorization: Bearer $PAPERCLIP_API_KEY" \
121
+ -H "Content-Type: application/json" \
122
+ -d '{
123
+ "source": "https://skills.sh/google-labs-code/stitch-skills/design-md"
124
+ }'
125
+ ```
126
+
127
+ Or equivalently using the key-style string:
128
+
129
+ ```sh
130
+ curl -sS -X POST "$PAPERCLIP_API_URL/api/companies/$PAPERCLIP_COMPANY_ID/skills/import" \
131
+ -H "Authorization: Bearer $PAPERCLIP_API_KEY" \
132
+ -H "Content-Type: application/json" \
133
+ -d '{
134
+ "source": "google-labs-code/stitch-skills/design-md"
135
+ }'
136
+ ```
137
+
138
+ ### Example: GitHub import
139
+
140
+ ```sh
141
+ curl -sS -X POST "$PAPERCLIP_API_URL/api/companies/$PAPERCLIP_COMPANY_ID/skills/import" \
142
+ -H "Authorization: Bearer $PAPERCLIP_API_KEY" \
143
+ -H "Content-Type: application/json" \
144
+ -d '{
145
+ "source": "https://github.com/vercel-labs/agent-browser"
146
+ }'
147
+ ```
148
+
149
+ You can also use source strings such as:
150
+
151
+ - `google-labs-code/stitch-skills/design-md`
152
+ - `vercel-labs/agent-browser/agent-browser`
153
+ - `npx skills add https://github.com/vercel-labs/agent-browser --skill agent-browser`
154
+
155
+ If the task is to discover skills from the company project workspaces first:
156
+
157
+ ```sh
158
+ curl -sS -X POST "$PAPERCLIP_API_URL/api/companies/$PAPERCLIP_COMPANY_ID/skills/scan-projects" \
159
+ -H "Authorization: Bearer $PAPERCLIP_API_KEY" \
160
+ -H "Content-Type: application/json" \
161
+ -d '{}'
162
+ ```
163
+
164
+ ## Inspect What Was Installed
165
+
166
+ ```sh
167
+ curl -sS "$PAPERCLIP_API_URL/api/companies/$PAPERCLIP_COMPANY_ID/skills" \
168
+ -H "Authorization: Bearer $PAPERCLIP_API_KEY"
169
+ ```
170
+
171
+ Read the skill entry and its `SKILL.md`:
172
+
173
+ ```sh
174
+ curl -sS "$PAPERCLIP_API_URL/api/companies/$PAPERCLIP_COMPANY_ID/skills/<skill-id>" \
175
+ -H "Authorization: Bearer $PAPERCLIP_API_KEY"
176
+
177
+ curl -sS "$PAPERCLIP_API_URL/api/companies/$PAPERCLIP_COMPANY_ID/skills/<skill-id>/files?path=SKILL.md" \
178
+ -H "Authorization: Bearer $PAPERCLIP_API_KEY"
179
+ ```
180
+
181
+ ## Assign Skills To An Existing Agent
182
+
183
+ `desiredSkills` accepts:
184
+
185
+ - exact company skill key
186
+ - exact company skill id
187
+ - exact slug when it is unique in the company
188
+
189
+ The server persists canonical company skill keys.
190
+
191
+ The request must include a merge mode:
192
+
193
+ - `add` adds the named skills and keeps every other assignment.
194
+ - `remove` removes only the named skills.
195
+ - `replace` overwrites the complete desired skill set. Use it only after explicit confirmation.
196
+
197
+ ```sh
198
+ curl -sS -X POST "$PAPERCLIP_API_URL/api/agents/<agent-id>/skills/sync" \
199
+ -H "Authorization: Bearer $PAPERCLIP_API_KEY" \
200
+ -H "Content-Type: application/json" \
201
+ -d '{
202
+ "mode": "add",
203
+ "desiredSkills": [
204
+ "vercel-labs/agent-browser/agent-browser"
205
+ ]
206
+ }'
207
+ ```
208
+
209
+ If you need the current state first:
210
+
211
+ ```sh
212
+ curl -sS "$PAPERCLIP_API_URL/api/agents/<agent-id>/skills" \
213
+ -H "Authorization: Bearer $PAPERCLIP_API_KEY"
214
+ ```
215
+
216
+ ## Include Skills During Hire Or Create
217
+
218
+ Use the same company skill keys or references in `desiredSkills` when hiring or creating an agent:
219
+
220
+ ```sh
221
+ curl -sS -X POST "$PAPERCLIP_API_URL/api/companies/$PAPERCLIP_COMPANY_ID/agent-hires" \
222
+ -H "Authorization: Bearer $PAPERCLIP_API_KEY" \
223
+ -H "Content-Type: application/json" \
224
+ -d '{
225
+ "name": "QA Browser Agent",
226
+ "role": "qa",
227
+ "adapterType": "codex_local",
228
+ "adapterConfig": {
229
+ "cwd": "/abs/path/to/repo"
230
+ },
231
+ "desiredSkills": [
232
+ "agent-browser"
233
+ ]
234
+ }'
235
+ ```
236
+
237
+ For direct create without approval:
238
+
239
+ ```sh
240
+ curl -sS -X POST "$PAPERCLIP_API_URL/api/companies/$PAPERCLIP_COMPANY_ID/agents" \
241
+ -H "Authorization: Bearer $PAPERCLIP_API_KEY" \
242
+ -H "Content-Type: application/json" \
243
+ -d '{
244
+ "name": "QA Browser Agent",
245
+ "role": "qa",
246
+ "adapterType": "codex_local",
247
+ "adapterConfig": {
248
+ "cwd": "/abs/path/to/repo"
249
+ },
250
+ "desiredSkills": [
251
+ "agent-browser"
252
+ ]
253
+ }'
254
+ ```
255
+
256
+ ## Notes
257
+
258
+ - Built-in Paperclip runtime skills are still added automatically when required by the adapter.
259
+ - If a reference is missing or ambiguous, the API returns `422`.
260
+ - Prefer linking back to the relevant issue, approval, and agent when you comment about skill changes.
261
+ - Use company portability routes when you need whole-package import/export, not just a skill:
262
+ - `POST /api/companies/:companyId/imports/preview`
263
+ - `POST /api/companies/:companyId/imports/apply`
264
+ - `POST /api/companies/:companyId/exports/preview`
265
+ - `POST /api/companies/:companyId/exports`
266
+ - Use skill-only import when the task is specifically to add a skill to the company library without importing the surrounding company/team/package structure.