@vibe-agent-toolkit/vat-development-agents 0.1.22-rc.3 → 0.1.22
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.
- package/dist/.claude/plugins/marketplaces/vat-skills/plugins/vibe-agent-toolkit/.claude-plugin/plugin.json +1 -0
- package/dist/.claude/plugins/marketplaces/vat-skills/plugins/vibe-agent-toolkit/skills/org-admin/SKILL.md +67 -1
- package/dist/generated/resources/skills/vat-claude-org-admin.d.ts +1 -0
- package/dist/generated/resources/skills/vat-claude-org-admin.js +12 -7
- package/dist/skills/org-admin/SKILL.md +67 -1
- package/package.json +4 -4
|
@@ -93,8 +93,20 @@ const keys = await client.get<{ data: ApiKey[]; has_more: boolean }>(
|
|
|
93
93
|
|
|
94
94
|
**Skills (beta — uses regular API key):**
|
|
95
95
|
```typescript
|
|
96
|
+
// List skills
|
|
96
97
|
const skills = await client.getSkills<{ data: Skill[]; has_more: boolean }>('/v1/skills');
|
|
97
98
|
// Adds anthropic-beta: skills-2025-10-02 header automatically
|
|
99
|
+
|
|
100
|
+
// Upload a skill (multipart/form-data)
|
|
101
|
+
import { buildMultipartFormData } from '@vibe-agent-toolkit/claude-marketplace';
|
|
102
|
+
const multipart = buildMultipartFormData(
|
|
103
|
+
{ display_title: 'My Skill' },
|
|
104
|
+
[{ fieldName: 'files[]', filename: 'SKILL.md', content: Buffer.from(skillContent) }],
|
|
105
|
+
);
|
|
106
|
+
const created = await client.uploadSkill<{ id: string; latest_version: string }>(multipart);
|
|
107
|
+
|
|
108
|
+
// Delete a skill
|
|
109
|
+
const deleted = await client.deleteSkill<{ id: string; type: string }>(skillId);
|
|
98
110
|
```
|
|
99
111
|
|
|
100
112
|
### Report Endpoints — Pagination Quirk
|
|
@@ -254,10 +266,65 @@ vat claude org usage [--from DT] [--to DT] Token usage report
|
|
|
254
266
|
vat claude org cost [--from DT] [--group-by F] USD cost report
|
|
255
267
|
vat claude org code-analytics [--from DATE] Claude Code metrics (YYYY-MM-DD)
|
|
256
268
|
vat claude org skills list Workspace skills (needs ANTHROPIC_API_KEY)
|
|
269
|
+
vat claude org skills install <source> Upload skill dir or ZIP to org
|
|
270
|
+
vat claude org skills delete <skill-id> Delete a skill (versions first)
|
|
271
|
+
vat claude org skills versions list <skill-id> List skill versions
|
|
272
|
+
vat claude org skills versions delete <id> <ver> Delete a skill version
|
|
257
273
|
```
|
|
258
274
|
|
|
259
275
|
Exit codes: `0` success, `1` expected failure (stubs), `2` system error (missing key, API error).
|
|
260
276
|
|
|
277
|
+
**Skill deletion lifecycle:** The API requires all versions to be deleted before the skill itself.
|
|
278
|
+
Use `versions list` to find versions, `versions delete` each one, then `delete` the skill.
|
|
279
|
+
|
|
280
|
+
## Enterprise Skill Distribution
|
|
281
|
+
|
|
282
|
+
### Skills API (claude.ai / Console)
|
|
283
|
+
|
|
284
|
+
Skills uploaded via `POST /v1/skills` are **workspace-scoped** and **automatically available to
|
|
285
|
+
all workspace members**. There is no per-user enable/disable via the API — visibility is
|
|
286
|
+
workspace-level only. The admin UI may have additional controls not exposed in the API.
|
|
287
|
+
|
|
288
|
+
**Upload from npm package:**
|
|
289
|
+
```bash
|
|
290
|
+
# Upload all skills from a package
|
|
291
|
+
vat claude org skills install --from-npm @scope/my-skills-package@1.0.0
|
|
292
|
+
|
|
293
|
+
# Upload a single skill
|
|
294
|
+
vat claude org skills install --from-npm @scope/my-skills-package@1.0.0 --skill my-skill
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
The package must contain `dist/skills/<name>/SKILL.md` (produced by `vat skills build`).
|
|
298
|
+
If skills are in a sub-dependency, the command searches `node_modules/*/dist/skills/` too.
|
|
299
|
+
|
|
300
|
+
**Duplicate titles are rejected** — the API enforces unique `display_title` per workspace.
|
|
301
|
+
When uploading multiple skills, failures are non-fatal; partial results are reported.
|
|
302
|
+
|
|
303
|
+
### Managed Settings (Claude Code plugins)
|
|
304
|
+
|
|
305
|
+
For enterprise-wide Claude Code plugin deployment (not the Skills API), use managed settings
|
|
306
|
+
pushed via MDM (Jamf, Intune, SCCM, etc.):
|
|
307
|
+
|
|
308
|
+
| Platform | Path |
|
|
309
|
+
|---|---|
|
|
310
|
+
| macOS | `/Library/Application Support/ClaudeCode/managed-settings.json` |
|
|
311
|
+
| Linux | `/etc/claude-code/managed-settings.json` |
|
|
312
|
+
| Windows | `C:\Program Files\ClaudeCode\managed-settings.json` |
|
|
313
|
+
|
|
314
|
+
Managed settings are **highest priority** in the settings cascade — they override user settings.
|
|
315
|
+
Use this to force-enable plugins, lock down permissions, or configure organization defaults.
|
|
316
|
+
|
|
317
|
+
```json
|
|
318
|
+
{
|
|
319
|
+
"enabledPlugins": {
|
|
320
|
+
"my-plugin@my-marketplace": true
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
Combine with `npm install -g <package>` (via IT software deployment) to install the plugin
|
|
326
|
+
binary, then managed settings to enable it across all machines.
|
|
327
|
+
|
|
261
328
|
## Not Yet Implemented
|
|
262
329
|
|
|
263
330
|
These commands exist with the correct CLI shape but return structured
|
|
@@ -268,4 +335,3 @@ These commands exist with the correct CLI shape but return structured
|
|
|
268
335
|
- `org workspaces create/archive` — workspace lifecycle
|
|
269
336
|
- `org workspaces members add/update/remove` — workspace membership
|
|
270
337
|
- `org api-keys update` — rename keys
|
|
271
|
-
- `org skills install/delete` — enterprise skill distribution via API
|
|
@@ -7,7 +7,7 @@ export const meta = {
|
|
|
7
7
|
description: "Anthropic org administration for Enterprise/Team admins. Requires ANTHROPIC_ADMIN_API_KEY. Use for org user management, API key auditing, cost/usage reporting, workspace administration, and enterprise skill distribution via the Anthropic Admin API. Not for regular users — admin access required."
|
|
8
8
|
};
|
|
9
9
|
|
|
10
|
-
export const text = "\n# Claude Org Administration\n\n**For Anthropic org admins only.** Requires \`ANTHROPIC_ADMIN_API_KEY\` (Enterprise/Team plan).\nIf you don\'t have an admin key, this skill is not for you — use \`vibe-agent-toolkit:distribution\`\nfor local plugin management instead.\n\n## Two Ways to Use It\n\n### CLI — Quick Queries\n\n\`\`\`bash\nvat claude org info # Org identity\nvat claude org users list # All members\nvat claude org api-keys list --status active # Active API keys\nvat claude org cost --group-by description # Cost breakdown by model\nvat claude org usage --from 2025-01-01T00:00:00Z # Token usage since Jan\nvat claude org skills list # Workspace-scoped skills (requires ANTHROPIC_API_KEY)\n\`\`\`\n\n### Library — Scripts and Automation\n\n\`\`\`typescript\nimport { OrgApiClient, createOrgApiClientFromEnv } from \'@vibe-agent-toolkit/claude-marketplace\';\n\n// Reads ANTHROPIC_ADMIN_API_KEY and optionally ANTHROPIC_API_KEY from env\nconst client = createOrgApiClientFromEnv();\n\n// Or construct directly\nconst client = new OrgApiClient({\n adminApiKey: \'sk-ant-admin-...\',\n apiKey: \'sk-ant-api03-...\', // only needed for skills endpoints\n});\n\`\`\`\n\n## API Reference\n\n### Auth: Two Keys, Two Surfaces\n\n| Key | Env Var | Endpoints | Who Has It |\n|---|---|---|---|\n| Admin API key | \`ANTHROPIC_ADMIN_API_KEY\` | \`/v1/organizations/*\` | Org admins only |\n| Regular API key | \`ANTHROPIC_API_KEY\` | \`/v1/skills\` (beta) | Any workspace member |\n\n### Endpoints and Methods\n\n**Org identity:**\n\`\`\`typescript\nconst org = await client.get<{ id: string; type: string; name: string }>(\'/v1/organizations/me\');\n\`\`\`\n\n**Users:**\n\`\`\`typescript\n// List (paginated with limit/after_id)\nconst users = await client.get<{ data: OrgUser[]; has_more: boolean }>(\n \'/v1/organizations/users\', { limit: 100 }\n);\n\n// Get single user\nconst user = await client.get<OrgUser>(\'/v1/organizations/users/user_abc123\');\n\`\`\`\n\n**Invites:**\n\`\`\`typescript\nconst invites = await client.get<{ data: OrgInvite[]; has_more: boolean }>(\n \'/v1/organizations/invites\'\n);\n\`\`\`\n\n**Workspaces:**\n\`\`\`typescript\nconst workspaces = await client.get<{ data: OrgWorkspace[]; has_more: boolean }>(\n \'/v1/organizations/workspaces\'\n);\n\n// Workspace members\nconst members = await client.get<{ data: WorkspaceMember[]; has_more: boolean }>(\n \'/v1/organizations/workspaces/ws_abc/members\'\n);\n\`\`\`\n\n**API keys:**\n\`\`\`typescript\nconst keys = await client.get<{ data: ApiKey[]; has_more: boolean }>(\n \'/v1/organizations/api_keys\',\n { status: \'active\', workspace_id: \'ws_abc\' } // both filters optional\n);\n\`\`\`\n\n**Skills (beta — uses regular API key):**\n\`\`\`typescript\nconst skills = await client.getSkills<{ data: Skill[]; has_more: boolean }>(\'/v1/skills\');\n// Adds anthropic-beta: skills-2025-10-02 header automatically\n\`\`\`\n\n### Report Endpoints — Pagination Quirk\n\nUsage, cost, and code-analytics endpoints have a **non-standard pagination model**.\nThey return \`has_more: true\` with a \`next_page\` cursor, but the API **rejects \`next_page\`\nas a query parameter**. Paginate by advancing \`starting_at\` to the last bucket\'s \`ending_at\`.\n\n**Usage report (daily token buckets):**\n\`\`\`typescript\ninterface UsageBucket {\n starting_at: string;\n ending_at: string;\n results: Array<{\n uncached_input_tokens: number;\n cache_read_input_tokens: number;\n output_tokens: number;\n model: string | null;\n workspace_id: string | null;\n api_key_id: string | null;\n service_tier: string | null;\n // ... other dimension fields, null if not applicable\n }>;\n}\n\n// First page\nlet resp = await client.get<{ data: UsageBucket[]; has_more: boolean }>(\n \'/v1/organizations/usage_report/messages\',\n { starting_at: \'2025-01-01T00:00:00Z\', ending_at: \'2025-12-31T23:59:59Z\' }\n);\n\n// Subsequent pages — advance starting_at, do NOT pass next_page\nconst lastBucket = resp.data.at(-1);\nresp = await client.get<{ data: UsageBucket[]; has_more: boolean }>(\n \'/v1/organizations/usage_report/messages\',\n { starting_at: lastBucket.ending_at, ending_at: \'2025-12-31T23:59:59Z\' }\n);\n\`\`\`\n\n**Cost report:**\n\`\`\`typescript\n// amount is a STRING, not a number — parse before arithmetic\ninterface CostResult {\n currency: string;\n amount: string; // \"7.0812\" — use parseFloat()\n description: string | null;\n model: string | null;\n token_type: string | null;\n // ...\n}\n\n// group_by[] needs URLSearchParams for repeated params\nconst qs = new URLSearchParams();\nqs.set(\'starting_at\', \'2025-03-01T00:00:00Z\');\nqs.set(\'ending_at\', \'2025-03-31T23:59:59Z\');\nqs.append(\'group_by[]\', \'description\');\nqs.append(\'group_by[]\', \'workspace_id\');\nconst resp = await client.get<{ data: CostBucket[] }>(\n \`/v1/organizations/cost_report?${qs.toString()}\`\n);\n\n// Sum costs\nconst total = resp.data\n .flatMap(b => b.results)\n .reduce((sum, r) => sum + parseFloat(r.amount), 0);\n\`\`\`\n\n**Code analytics (Claude Code productivity):**\n\`\`\`typescript\n// Date-only format YYYY-MM-DD. No ending_at parameter.\nconst resp = await client.get<{ data: unknown[] }>(\n \'/v1/organizations/usage_report/claude_code\',\n { starting_at: \'2025-03-01\' } // NOT ISO datetime\n);\n// Returns empty data[] when no Claude Code enterprise seats are active\n\`\`\`\n\n## Common Recipes\n\n### Monthly cost summary script\n\n\`\`\`typescript\nimport { createOrgApiClientFromEnv } from \'@vibe-agent-toolkit/claude-marketplace\';\n\nconst client = createOrgApiClientFromEnv();\nconst qs = new URLSearchParams();\nqs.set(\'starting_at\', \'2025-03-01T00:00:00Z\');\nqs.set(\'ending_at\', \'2025-04-01T00:00:00Z\');\nqs.append(\'group_by[]\', \'description\');\n\nconst allResults = [];\nlet startingAt = \'2025-03-01T00:00:00Z\';\nlet hasMore = true;\n\nwhile (hasMore) {\n qs.set(\'starting_at\', startingAt);\n const resp = await client.get(\`/v1/organizations/cost_report?${qs.toString()}\`);\n allResults.push(...resp.data);\n const last = resp.data.at(-1);\n hasMore = resp.has_more && last;\n if (last) startingAt = last.ending_at;\n}\n\nconst byDescription = new Map();\nfor (const bucket of allResults) {\n for (const r of bucket.results) {\n const key = r.description ?? \'unclassified\';\n byDescription.set(key, (byDescription.get(key) ?? 0) + parseFloat(r.amount));\n }\n}\nfor (const [desc, total] of byDescription) {\n console.log(\`${desc}: $${total.toFixed(2)}\`);\n}\n\`\`\`\n\n### API key security audit\n\n\`\`\`typescript\nconst client = createOrgApiClientFromEnv();\nconst { data: keys } = await client.get(\'/v1/organizations/api_keys\', { limit: 100 });\n\nconst issues = [];\nfor (const key of keys) {\n if (key.status === \'active\' && !key.expires_at) {\n issues.push(\`${key.name}: active with no expiry\`);\n }\n if (key.status === \'active\' && !key.workspace_id) {\n issues.push(\`${key.name}: not scoped to a workspace\`);\n }\n}\nconsole.log(issues.length ? issues.join(\'\\n\') : \'All keys look good\');\n\`\`\`\n\n### List org users who haven\'t accepted invites\n\n\`\`\`typescript\nconst client = createOrgApiClientFromEnv();\nconst { data: invites } = await client.get(\'/v1/organizations/invites\');\nconst pending = invites.filter(i => i.status !== \'accepted\');\nconsole.log(\`${pending.length} pending invites:\`, pending.map(i => i.email));\n\`\`\`\n\n## CLI Reference\n\nAll commands require \`ANTHROPIC_ADMIN_API_KEY\` unless noted.\n\n\`\`\`\nvat claude org info Org identity (id, name)\nvat claude org users list [--limit N] List members\nvat claude org users get <user-id> Get single member\nvat claude org invites list List invitations\nvat claude org workspaces list List API workspaces\nvat claude org workspaces get <id> Get single workspace\nvat claude org workspaces members list <id> Workspace members\nvat claude org api-keys list [--status S] API key inventory\nvat claude org usage [--from DT] [--to DT] Token usage report\nvat claude org cost [--from DT] [--group-by F] USD cost report\nvat claude org code-analytics [--from DATE] Claude Code metrics (YYYY-MM-DD)\nvat claude org skills list Workspace skills (needs ANTHROPIC_API_KEY)\n\`\`\`\n\nExit codes: \`0\` success, \`1\` expected failure (stubs), \`2\` system error (missing key, API error).\n\n## Not Yet Implemented\n\nThese commands exist with the correct CLI shape but return structured\n\`not-yet-implemented\` stubs (exit 1). Coming in a future release:\n\n- \`org users update/remove\` — role changes, offboarding\n- \`org invites create/delete\` — programmatic invitations\n- \`org workspaces create/archive\` — workspace lifecycle\n- \`org workspaces members add/update/remove\` — workspace membership\n- \`org api-keys update\` — rename keys\n- \`org skills install/delete\` — enterprise skill distribution via API\n";
|
|
10
|
+
export const text = "\n# Claude Org Administration\n\n**For Anthropic org admins only.** Requires \`ANTHROPIC_ADMIN_API_KEY\` (Enterprise/Team plan).\nIf you don\'t have an admin key, this skill is not for you — use \`vibe-agent-toolkit:distribution\`\nfor local plugin management instead.\n\n## Two Ways to Use It\n\n### CLI — Quick Queries\n\n\`\`\`bash\nvat claude org info # Org identity\nvat claude org users list # All members\nvat claude org api-keys list --status active # Active API keys\nvat claude org cost --group-by description # Cost breakdown by model\nvat claude org usage --from 2025-01-01T00:00:00Z # Token usage since Jan\nvat claude org skills list # Workspace-scoped skills (requires ANTHROPIC_API_KEY)\n\`\`\`\n\n### Library — Scripts and Automation\n\n\`\`\`typescript\nimport { OrgApiClient, createOrgApiClientFromEnv } from \'@vibe-agent-toolkit/claude-marketplace\';\n\n// Reads ANTHROPIC_ADMIN_API_KEY and optionally ANTHROPIC_API_KEY from env\nconst client = createOrgApiClientFromEnv();\n\n// Or construct directly\nconst client = new OrgApiClient({\n adminApiKey: \'sk-ant-admin-...\',\n apiKey: \'sk-ant-api03-...\', // only needed for skills endpoints\n});\n\`\`\`\n\n## API Reference\n\n### Auth: Two Keys, Two Surfaces\n\n| Key | Env Var | Endpoints | Who Has It |\n|---|---|---|---|\n| Admin API key | \`ANTHROPIC_ADMIN_API_KEY\` | \`/v1/organizations/*\` | Org admins only |\n| Regular API key | \`ANTHROPIC_API_KEY\` | \`/v1/skills\` (beta) | Any workspace member |\n\n### Endpoints and Methods\n\n**Org identity:**\n\`\`\`typescript\nconst org = await client.get<{ id: string; type: string; name: string }>(\'/v1/organizations/me\');\n\`\`\`\n\n**Users:**\n\`\`\`typescript\n// List (paginated with limit/after_id)\nconst users = await client.get<{ data: OrgUser[]; has_more: boolean }>(\n \'/v1/organizations/users\', { limit: 100 }\n);\n\n// Get single user\nconst user = await client.get<OrgUser>(\'/v1/organizations/users/user_abc123\');\n\`\`\`\n\n**Invites:**\n\`\`\`typescript\nconst invites = await client.get<{ data: OrgInvite[]; has_more: boolean }>(\n \'/v1/organizations/invites\'\n);\n\`\`\`\n\n**Workspaces:**\n\`\`\`typescript\nconst workspaces = await client.get<{ data: OrgWorkspace[]; has_more: boolean }>(\n \'/v1/organizations/workspaces\'\n);\n\n// Workspace members\nconst members = await client.get<{ data: WorkspaceMember[]; has_more: boolean }>(\n \'/v1/organizations/workspaces/ws_abc/members\'\n);\n\`\`\`\n\n**API keys:**\n\`\`\`typescript\nconst keys = await client.get<{ data: ApiKey[]; has_more: boolean }>(\n \'/v1/organizations/api_keys\',\n { status: \'active\', workspace_id: \'ws_abc\' } // both filters optional\n);\n\`\`\`\n\n**Skills (beta — uses regular API key):**\n\`\`\`typescript\n// List skills\nconst skills = await client.getSkills<{ data: Skill[]; has_more: boolean }>(\'/v1/skills\');\n// Adds anthropic-beta: skills-2025-10-02 header automatically\n\n// Upload a skill (multipart/form-data)\nimport { buildMultipartFormData } from \'@vibe-agent-toolkit/claude-marketplace\';\nconst multipart = buildMultipartFormData(\n { display_title: \'My Skill\' },\n [{ fieldName: \'files[]\', filename: \'SKILL.md\', content: Buffer.from(skillContent) }],\n);\nconst created = await client.uploadSkill<{ id: string; latest_version: string }>(multipart);\n\n// Delete a skill\nconst deleted = await client.deleteSkill<{ id: string; type: string }>(skillId);\n\`\`\`\n\n### Report Endpoints — Pagination Quirk\n\nUsage, cost, and code-analytics endpoints have a **non-standard pagination model**.\nThey return \`has_more: true\` with a \`next_page\` cursor, but the API **rejects \`next_page\`\nas a query parameter**. Paginate by advancing \`starting_at\` to the last bucket\'s \`ending_at\`.\n\n**Usage report (daily token buckets):**\n\`\`\`typescript\ninterface UsageBucket {\n starting_at: string;\n ending_at: string;\n results: Array<{\n uncached_input_tokens: number;\n cache_read_input_tokens: number;\n output_tokens: number;\n model: string | null;\n workspace_id: string | null;\n api_key_id: string | null;\n service_tier: string | null;\n // ... other dimension fields, null if not applicable\n }>;\n}\n\n// First page\nlet resp = await client.get<{ data: UsageBucket[]; has_more: boolean }>(\n \'/v1/organizations/usage_report/messages\',\n { starting_at: \'2025-01-01T00:00:00Z\', ending_at: \'2025-12-31T23:59:59Z\' }\n);\n\n// Subsequent pages — advance starting_at, do NOT pass next_page\nconst lastBucket = resp.data.at(-1);\nresp = await client.get<{ data: UsageBucket[]; has_more: boolean }>(\n \'/v1/organizations/usage_report/messages\',\n { starting_at: lastBucket.ending_at, ending_at: \'2025-12-31T23:59:59Z\' }\n);\n\`\`\`\n\n**Cost report:**\n\`\`\`typescript\n// amount is a STRING, not a number — parse before arithmetic\ninterface CostResult {\n currency: string;\n amount: string; // \"7.0812\" — use parseFloat()\n description: string | null;\n model: string | null;\n token_type: string | null;\n // ...\n}\n\n// group_by[] needs URLSearchParams for repeated params\nconst qs = new URLSearchParams();\nqs.set(\'starting_at\', \'2025-03-01T00:00:00Z\');\nqs.set(\'ending_at\', \'2025-03-31T23:59:59Z\');\nqs.append(\'group_by[]\', \'description\');\nqs.append(\'group_by[]\', \'workspace_id\');\nconst resp = await client.get<{ data: CostBucket[] }>(\n \`/v1/organizations/cost_report?${qs.toString()}\`\n);\n\n// Sum costs\nconst total = resp.data\n .flatMap(b => b.results)\n .reduce((sum, r) => sum + parseFloat(r.amount), 0);\n\`\`\`\n\n**Code analytics (Claude Code productivity):**\n\`\`\`typescript\n// Date-only format YYYY-MM-DD. No ending_at parameter.\nconst resp = await client.get<{ data: unknown[] }>(\n \'/v1/organizations/usage_report/claude_code\',\n { starting_at: \'2025-03-01\' } // NOT ISO datetime\n);\n// Returns empty data[] when no Claude Code enterprise seats are active\n\`\`\`\n\n## Common Recipes\n\n### Monthly cost summary script\n\n\`\`\`typescript\nimport { createOrgApiClientFromEnv } from \'@vibe-agent-toolkit/claude-marketplace\';\n\nconst client = createOrgApiClientFromEnv();\nconst qs = new URLSearchParams();\nqs.set(\'starting_at\', \'2025-03-01T00:00:00Z\');\nqs.set(\'ending_at\', \'2025-04-01T00:00:00Z\');\nqs.append(\'group_by[]\', \'description\');\n\nconst allResults = [];\nlet startingAt = \'2025-03-01T00:00:00Z\';\nlet hasMore = true;\n\nwhile (hasMore) {\n qs.set(\'starting_at\', startingAt);\n const resp = await client.get(\`/v1/organizations/cost_report?${qs.toString()}\`);\n allResults.push(...resp.data);\n const last = resp.data.at(-1);\n hasMore = resp.has_more && last;\n if (last) startingAt = last.ending_at;\n}\n\nconst byDescription = new Map();\nfor (const bucket of allResults) {\n for (const r of bucket.results) {\n const key = r.description ?? \'unclassified\';\n byDescription.set(key, (byDescription.get(key) ?? 0) + parseFloat(r.amount));\n }\n}\nfor (const [desc, total] of byDescription) {\n console.log(\`${desc}: $${total.toFixed(2)}\`);\n}\n\`\`\`\n\n### API key security audit\n\n\`\`\`typescript\nconst client = createOrgApiClientFromEnv();\nconst { data: keys } = await client.get(\'/v1/organizations/api_keys\', { limit: 100 });\n\nconst issues = [];\nfor (const key of keys) {\n if (key.status === \'active\' && !key.expires_at) {\n issues.push(\`${key.name}: active with no expiry\`);\n }\n if (key.status === \'active\' && !key.workspace_id) {\n issues.push(\`${key.name}: not scoped to a workspace\`);\n }\n}\nconsole.log(issues.length ? issues.join(\'\\n\') : \'All keys look good\');\n\`\`\`\n\n### List org users who haven\'t accepted invites\n\n\`\`\`typescript\nconst client = createOrgApiClientFromEnv();\nconst { data: invites } = await client.get(\'/v1/organizations/invites\');\nconst pending = invites.filter(i => i.status !== \'accepted\');\nconsole.log(\`${pending.length} pending invites:\`, pending.map(i => i.email));\n\`\`\`\n\n## CLI Reference\n\nAll commands require \`ANTHROPIC_ADMIN_API_KEY\` unless noted.\n\n\`\`\`\nvat claude org info Org identity (id, name)\nvat claude org users list [--limit N] List members\nvat claude org users get <user-id> Get single member\nvat claude org invites list List invitations\nvat claude org workspaces list List API workspaces\nvat claude org workspaces get <id> Get single workspace\nvat claude org workspaces members list <id> Workspace members\nvat claude org api-keys list [--status S] API key inventory\nvat claude org usage [--from DT] [--to DT] Token usage report\nvat claude org cost [--from DT] [--group-by F] USD cost report\nvat claude org code-analytics [--from DATE] Claude Code metrics (YYYY-MM-DD)\nvat claude org skills list Workspace skills (needs ANTHROPIC_API_KEY)\nvat claude org skills install <source> Upload skill dir or ZIP to org\nvat claude org skills delete <skill-id> Delete a skill (versions first)\nvat claude org skills versions list <skill-id> List skill versions\nvat claude org skills versions delete <id> <ver> Delete a skill version\n\`\`\`\n\nExit codes: \`0\` success, \`1\` expected failure (stubs), \`2\` system error (missing key, API error).\n\n**Skill deletion lifecycle:** The API requires all versions to be deleted before the skill itself.\nUse \`versions list\` to find versions, \`versions delete\` each one, then \`delete\` the skill.\n\n## Enterprise Skill Distribution\n\n### Skills API (claude.ai / Console)\n\nSkills uploaded via \`POST /v1/skills\` are **workspace-scoped** and **automatically available to\nall workspace members**. There is no per-user enable/disable via the API — visibility is\nworkspace-level only. The admin UI may have additional controls not exposed in the API.\n\n**Upload from npm package:**\n\`\`\`bash\n# Upload all skills from a package\nvat claude org skills install --from-npm @scope/my-skills-package@1.0.0\n\n# Upload a single skill\nvat claude org skills install --from-npm @scope/my-skills-package@1.0.0 --skill my-skill\n\`\`\`\n\nThe package must contain \`dist/skills/<name>/SKILL.md\` (produced by \`vat skills build\`).\nIf skills are in a sub-dependency, the command searches \`node_modules/*/dist/skills/\` too.\n\n**Duplicate titles are rejected** — the API enforces unique \`display_title\` per workspace.\nWhen uploading multiple skills, failures are non-fatal; partial results are reported.\n\n### Managed Settings (Claude Code plugins)\n\nFor enterprise-wide Claude Code plugin deployment (not the Skills API), use managed settings\npushed via MDM (Jamf, Intune, SCCM, etc.):\n\n| Platform | Path |\n|---|---|\n| macOS | \`/Library/Application Support/ClaudeCode/managed-settings.json\` |\n| Linux | \`/etc/claude-code/managed-settings.json\` |\n| Windows | \`C:\\Program Files\\ClaudeCode\\managed-settings.json\` |\n\nManaged settings are **highest priority** in the settings cascade — they override user settings.\nUse this to force-enable plugins, lock down permissions, or configure organization defaults.\n\n\`\`\`json\n{\n \"enabledPlugins\": {\n \"my-plugin@my-marketplace\": true\n }\n}\n\`\`\`\n\nCombine with \`npm install -g <package>\` (via IT software deployment) to install the plugin\nbinary, then managed settings to enable it across all machines.\n\n## Not Yet Implemented\n\nThese commands exist with the correct CLI shape but return structured\n\`not-yet-implemented\` stubs (exit 1). Coming in a future release:\n\n- \`org users update/remove\` — role changes, offboarding\n- \`org invites create/delete\` — programmatic invitations\n- \`org workspaces create/archive\` — workspace lifecycle\n- \`org workspaces members add/update/remove\` — workspace membership\n- \`org api-keys update\` — rename keys\n";
|
|
11
11
|
|
|
12
12
|
export const fragments = {
|
|
13
13
|
twoWaysToUseIt: {
|
|
@@ -17,8 +17,8 @@ export const fragments = {
|
|
|
17
17
|
},
|
|
18
18
|
apiReference: {
|
|
19
19
|
header: "## API Reference",
|
|
20
|
-
body: "### Auth: Two Keys, Two Surfaces\n\n| Key | Env Var | Endpoints | Who Has It |\n|---|---|---|---|\n| Admin API key | \`ANTHROPIC_ADMIN_API_KEY\` | \`/v1/organizations/*\` | Org admins only |\n| Regular API key | \`ANTHROPIC_API_KEY\` | \`/v1/skills\` (beta) | Any workspace member |\n\n### Endpoints and Methods\n\n**Org identity:**\n\`\`\`typescript\nconst org = await client.get<{ id: string; type: string; name: string }>(\'/v1/organizations/me\');\n\`\`\`\n\n**Users:**\n\`\`\`typescript\n// List (paginated with limit/after_id)\nconst users = await client.get<{ data: OrgUser[]; has_more: boolean }>(\n \'/v1/organizations/users\', { limit: 100 }\n);\n\n// Get single user\nconst user = await client.get<OrgUser>(\'/v1/organizations/users/user_abc123\');\n\`\`\`\n\n**Invites:**\n\`\`\`typescript\nconst invites = await client.get<{ data: OrgInvite[]; has_more: boolean }>(\n \'/v1/organizations/invites\'\n);\n\`\`\`\n\n**Workspaces:**\n\`\`\`typescript\nconst workspaces = await client.get<{ data: OrgWorkspace[]; has_more: boolean }>(\n \'/v1/organizations/workspaces\'\n);\n\n// Workspace members\nconst members = await client.get<{ data: WorkspaceMember[]; has_more: boolean }>(\n \'/v1/organizations/workspaces/ws_abc/members\'\n);\n\`\`\`\n\n**API keys:**\n\`\`\`typescript\nconst keys = await client.get<{ data: ApiKey[]; has_more: boolean }>(\n \'/v1/organizations/api_keys\',\n { status: \'active\', workspace_id: \'ws_abc\' } // both filters optional\n);\n\`\`\`\n\n**Skills (beta — uses regular API key):**\n\`\`\`typescript\nconst skills = await client.getSkills<{ data: Skill[]; has_more: boolean }>(\'/v1/skills\');\n// Adds anthropic-beta: skills-2025-10-02 header automatically\n\`\`\`\n\n### Report Endpoints — Pagination Quirk\n\nUsage, cost, and code-analytics endpoints have a **non-standard pagination model**.\nThey return \`has_more: true\` with a \`next_page\` cursor, but the API **rejects \`next_page\`\nas a query parameter**. Paginate by advancing \`starting_at\` to the last bucket\'s \`ending_at\`.\n\n**Usage report (daily token buckets):**\n\`\`\`typescript\ninterface UsageBucket {\n starting_at: string;\n ending_at: string;\n results: Array<{\n uncached_input_tokens: number;\n cache_read_input_tokens: number;\n output_tokens: number;\n model: string | null;\n workspace_id: string | null;\n api_key_id: string | null;\n service_tier: string | null;\n // ... other dimension fields, null if not applicable\n }>;\n}\n\n// First page\nlet resp = await client.get<{ data: UsageBucket[]; has_more: boolean }>(\n \'/v1/organizations/usage_report/messages\',\n { starting_at: \'2025-01-01T00:00:00Z\', ending_at: \'2025-12-31T23:59:59Z\' }\n);\n\n// Subsequent pages — advance starting_at, do NOT pass next_page\nconst lastBucket = resp.data.at(-1);\nresp = await client.get<{ data: UsageBucket[]; has_more: boolean }>(\n \'/v1/organizations/usage_report/messages\',\n { starting_at: lastBucket.ending_at, ending_at: \'2025-12-31T23:59:59Z\' }\n);\n\`\`\`\n\n**Cost report:**\n\`\`\`typescript\n// amount is a STRING, not a number — parse before arithmetic\ninterface CostResult {\n currency: string;\n amount: string; // \"7.0812\" — use parseFloat()\n description: string | null;\n model: string | null;\n token_type: string | null;\n // ...\n}\n\n// group_by[] needs URLSearchParams for repeated params\nconst qs = new URLSearchParams();\nqs.set(\'starting_at\', \'2025-03-01T00:00:00Z\');\nqs.set(\'ending_at\', \'2025-03-31T23:59:59Z\');\nqs.append(\'group_by[]\', \'description\');\nqs.append(\'group_by[]\', \'workspace_id\');\nconst resp = await client.get<{ data: CostBucket[] }>(\n \`/v1/organizations/cost_report?${qs.toString()}\`\n);\n\n// Sum costs\nconst total = resp.data\n .flatMap(b => b.results)\n .reduce((sum, r) => sum + parseFloat(r.amount), 0);\n\`\`\`\n\n**Code analytics (Claude Code productivity):**\n\`\`\`typescript\n// Date-only format YYYY-MM-DD. No ending_at parameter.\nconst resp = await client.get<{ data: unknown[] }>(\n \'/v1/organizations/usage_report/claude_code\',\n { starting_at: \'2025-03-01\' } // NOT ISO datetime\n);\n// Returns empty data[] when no Claude Code enterprise seats are active\n\`\`\`",
|
|
21
|
-
text: "## API Reference\n\n### Auth: Two Keys, Two Surfaces\n\n| Key | Env Var | Endpoints | Who Has It |\n|---|---|---|---|\n| Admin API key | \`ANTHROPIC_ADMIN_API_KEY\` | \`/v1/organizations/*\` | Org admins only |\n| Regular API key | \`ANTHROPIC_API_KEY\` | \`/v1/skills\` (beta) | Any workspace member |\n\n### Endpoints and Methods\n\n**Org identity:**\n\`\`\`typescript\nconst org = await client.get<{ id: string; type: string; name: string }>(\'/v1/organizations/me\');\n\`\`\`\n\n**Users:**\n\`\`\`typescript\n// List (paginated with limit/after_id)\nconst users = await client.get<{ data: OrgUser[]; has_more: boolean }>(\n \'/v1/organizations/users\', { limit: 100 }\n);\n\n// Get single user\nconst user = await client.get<OrgUser>(\'/v1/organizations/users/user_abc123\');\n\`\`\`\n\n**Invites:**\n\`\`\`typescript\nconst invites = await client.get<{ data: OrgInvite[]; has_more: boolean }>(\n \'/v1/organizations/invites\'\n);\n\`\`\`\n\n**Workspaces:**\n\`\`\`typescript\nconst workspaces = await client.get<{ data: OrgWorkspace[]; has_more: boolean }>(\n \'/v1/organizations/workspaces\'\n);\n\n// Workspace members\nconst members = await client.get<{ data: WorkspaceMember[]; has_more: boolean }>(\n \'/v1/organizations/workspaces/ws_abc/members\'\n);\n\`\`\`\n\n**API keys:**\n\`\`\`typescript\nconst keys = await client.get<{ data: ApiKey[]; has_more: boolean }>(\n \'/v1/organizations/api_keys\',\n { status: \'active\', workspace_id: \'ws_abc\' } // both filters optional\n);\n\`\`\`\n\n**Skills (beta — uses regular API key):**\n\`\`\`typescript\nconst skills = await client.getSkills<{ data: Skill[]; has_more: boolean }>(\'/v1/skills\');\n// Adds anthropic-beta: skills-2025-10-02 header automatically\n\`\`\`\n\n### Report Endpoints — Pagination Quirk\n\nUsage, cost, and code-analytics endpoints have a **non-standard pagination model**.\nThey return \`has_more: true\` with a \`next_page\` cursor, but the API **rejects \`next_page\`\nas a query parameter**. Paginate by advancing \`starting_at\` to the last bucket\'s \`ending_at\`.\n\n**Usage report (daily token buckets):**\n\`\`\`typescript\ninterface UsageBucket {\n starting_at: string;\n ending_at: string;\n results: Array<{\n uncached_input_tokens: number;\n cache_read_input_tokens: number;\n output_tokens: number;\n model: string | null;\n workspace_id: string | null;\n api_key_id: string | null;\n service_tier: string | null;\n // ... other dimension fields, null if not applicable\n }>;\n}\n\n// First page\nlet resp = await client.get<{ data: UsageBucket[]; has_more: boolean }>(\n \'/v1/organizations/usage_report/messages\',\n { starting_at: \'2025-01-01T00:00:00Z\', ending_at: \'2025-12-31T23:59:59Z\' }\n);\n\n// Subsequent pages — advance starting_at, do NOT pass next_page\nconst lastBucket = resp.data.at(-1);\nresp = await client.get<{ data: UsageBucket[]; has_more: boolean }>(\n \'/v1/organizations/usage_report/messages\',\n { starting_at: lastBucket.ending_at, ending_at: \'2025-12-31T23:59:59Z\' }\n);\n\`\`\`\n\n**Cost report:**\n\`\`\`typescript\n// amount is a STRING, not a number — parse before arithmetic\ninterface CostResult {\n currency: string;\n amount: string; // \"7.0812\" — use parseFloat()\n description: string | null;\n model: string | null;\n token_type: string | null;\n // ...\n}\n\n// group_by[] needs URLSearchParams for repeated params\nconst qs = new URLSearchParams();\nqs.set(\'starting_at\', \'2025-03-01T00:00:00Z\');\nqs.set(\'ending_at\', \'2025-03-31T23:59:59Z\');\nqs.append(\'group_by[]\', \'description\');\nqs.append(\'group_by[]\', \'workspace_id\');\nconst resp = await client.get<{ data: CostBucket[] }>(\n \`/v1/organizations/cost_report?${qs.toString()}\`\n);\n\n// Sum costs\nconst total = resp.data\n .flatMap(b => b.results)\n .reduce((sum, r) => sum + parseFloat(r.amount), 0);\n\`\`\`\n\n**Code analytics (Claude Code productivity):**\n\`\`\`typescript\n// Date-only format YYYY-MM-DD. No ending_at parameter.\nconst resp = await client.get<{ data: unknown[] }>(\n \'/v1/organizations/usage_report/claude_code\',\n { starting_at: \'2025-03-01\' } // NOT ISO datetime\n);\n// Returns empty data[] when no Claude Code enterprise seats are active\n\`\`\`"
|
|
20
|
+
body: "### Auth: Two Keys, Two Surfaces\n\n| Key | Env Var | Endpoints | Who Has It |\n|---|---|---|---|\n| Admin API key | \`ANTHROPIC_ADMIN_API_KEY\` | \`/v1/organizations/*\` | Org admins only |\n| Regular API key | \`ANTHROPIC_API_KEY\` | \`/v1/skills\` (beta) | Any workspace member |\n\n### Endpoints and Methods\n\n**Org identity:**\n\`\`\`typescript\nconst org = await client.get<{ id: string; type: string; name: string }>(\'/v1/organizations/me\');\n\`\`\`\n\n**Users:**\n\`\`\`typescript\n// List (paginated with limit/after_id)\nconst users = await client.get<{ data: OrgUser[]; has_more: boolean }>(\n \'/v1/organizations/users\', { limit: 100 }\n);\n\n// Get single user\nconst user = await client.get<OrgUser>(\'/v1/organizations/users/user_abc123\');\n\`\`\`\n\n**Invites:**\n\`\`\`typescript\nconst invites = await client.get<{ data: OrgInvite[]; has_more: boolean }>(\n \'/v1/organizations/invites\'\n);\n\`\`\`\n\n**Workspaces:**\n\`\`\`typescript\nconst workspaces = await client.get<{ data: OrgWorkspace[]; has_more: boolean }>(\n \'/v1/organizations/workspaces\'\n);\n\n// Workspace members\nconst members = await client.get<{ data: WorkspaceMember[]; has_more: boolean }>(\n \'/v1/organizations/workspaces/ws_abc/members\'\n);\n\`\`\`\n\n**API keys:**\n\`\`\`typescript\nconst keys = await client.get<{ data: ApiKey[]; has_more: boolean }>(\n \'/v1/organizations/api_keys\',\n { status: \'active\', workspace_id: \'ws_abc\' } // both filters optional\n);\n\`\`\`\n\n**Skills (beta — uses regular API key):**\n\`\`\`typescript\n// List skills\nconst skills = await client.getSkills<{ data: Skill[]; has_more: boolean }>(\'/v1/skills\');\n// Adds anthropic-beta: skills-2025-10-02 header automatically\n\n// Upload a skill (multipart/form-data)\nimport { buildMultipartFormData } from \'@vibe-agent-toolkit/claude-marketplace\';\nconst multipart = buildMultipartFormData(\n { display_title: \'My Skill\' },\n [{ fieldName: \'files[]\', filename: \'SKILL.md\', content: Buffer.from(skillContent) }],\n);\nconst created = await client.uploadSkill<{ id: string; latest_version: string }>(multipart);\n\n// Delete a skill\nconst deleted = await client.deleteSkill<{ id: string; type: string }>(skillId);\n\`\`\`\n\n### Report Endpoints — Pagination Quirk\n\nUsage, cost, and code-analytics endpoints have a **non-standard pagination model**.\nThey return \`has_more: true\` with a \`next_page\` cursor, but the API **rejects \`next_page\`\nas a query parameter**. Paginate by advancing \`starting_at\` to the last bucket\'s \`ending_at\`.\n\n**Usage report (daily token buckets):**\n\`\`\`typescript\ninterface UsageBucket {\n starting_at: string;\n ending_at: string;\n results: Array<{\n uncached_input_tokens: number;\n cache_read_input_tokens: number;\n output_tokens: number;\n model: string | null;\n workspace_id: string | null;\n api_key_id: string | null;\n service_tier: string | null;\n // ... other dimension fields, null if not applicable\n }>;\n}\n\n// First page\nlet resp = await client.get<{ data: UsageBucket[]; has_more: boolean }>(\n \'/v1/organizations/usage_report/messages\',\n { starting_at: \'2025-01-01T00:00:00Z\', ending_at: \'2025-12-31T23:59:59Z\' }\n);\n\n// Subsequent pages — advance starting_at, do NOT pass next_page\nconst lastBucket = resp.data.at(-1);\nresp = await client.get<{ data: UsageBucket[]; has_more: boolean }>(\n \'/v1/organizations/usage_report/messages\',\n { starting_at: lastBucket.ending_at, ending_at: \'2025-12-31T23:59:59Z\' }\n);\n\`\`\`\n\n**Cost report:**\n\`\`\`typescript\n// amount is a STRING, not a number — parse before arithmetic\ninterface CostResult {\n currency: string;\n amount: string; // \"7.0812\" — use parseFloat()\n description: string | null;\n model: string | null;\n token_type: string | null;\n // ...\n}\n\n// group_by[] needs URLSearchParams for repeated params\nconst qs = new URLSearchParams();\nqs.set(\'starting_at\', \'2025-03-01T00:00:00Z\');\nqs.set(\'ending_at\', \'2025-03-31T23:59:59Z\');\nqs.append(\'group_by[]\', \'description\');\nqs.append(\'group_by[]\', \'workspace_id\');\nconst resp = await client.get<{ data: CostBucket[] }>(\n \`/v1/organizations/cost_report?${qs.toString()}\`\n);\n\n// Sum costs\nconst total = resp.data\n .flatMap(b => b.results)\n .reduce((sum, r) => sum + parseFloat(r.amount), 0);\n\`\`\`\n\n**Code analytics (Claude Code productivity):**\n\`\`\`typescript\n// Date-only format YYYY-MM-DD. No ending_at parameter.\nconst resp = await client.get<{ data: unknown[] }>(\n \'/v1/organizations/usage_report/claude_code\',\n { starting_at: \'2025-03-01\' } // NOT ISO datetime\n);\n// Returns empty data[] when no Claude Code enterprise seats are active\n\`\`\`",
|
|
21
|
+
text: "## API Reference\n\n### Auth: Two Keys, Two Surfaces\n\n| Key | Env Var | Endpoints | Who Has It |\n|---|---|---|---|\n| Admin API key | \`ANTHROPIC_ADMIN_API_KEY\` | \`/v1/organizations/*\` | Org admins only |\n| Regular API key | \`ANTHROPIC_API_KEY\` | \`/v1/skills\` (beta) | Any workspace member |\n\n### Endpoints and Methods\n\n**Org identity:**\n\`\`\`typescript\nconst org = await client.get<{ id: string; type: string; name: string }>(\'/v1/organizations/me\');\n\`\`\`\n\n**Users:**\n\`\`\`typescript\n// List (paginated with limit/after_id)\nconst users = await client.get<{ data: OrgUser[]; has_more: boolean }>(\n \'/v1/organizations/users\', { limit: 100 }\n);\n\n// Get single user\nconst user = await client.get<OrgUser>(\'/v1/organizations/users/user_abc123\');\n\`\`\`\n\n**Invites:**\n\`\`\`typescript\nconst invites = await client.get<{ data: OrgInvite[]; has_more: boolean }>(\n \'/v1/organizations/invites\'\n);\n\`\`\`\n\n**Workspaces:**\n\`\`\`typescript\nconst workspaces = await client.get<{ data: OrgWorkspace[]; has_more: boolean }>(\n \'/v1/organizations/workspaces\'\n);\n\n// Workspace members\nconst members = await client.get<{ data: WorkspaceMember[]; has_more: boolean }>(\n \'/v1/organizations/workspaces/ws_abc/members\'\n);\n\`\`\`\n\n**API keys:**\n\`\`\`typescript\nconst keys = await client.get<{ data: ApiKey[]; has_more: boolean }>(\n \'/v1/organizations/api_keys\',\n { status: \'active\', workspace_id: \'ws_abc\' } // both filters optional\n);\n\`\`\`\n\n**Skills (beta — uses regular API key):**\n\`\`\`typescript\n// List skills\nconst skills = await client.getSkills<{ data: Skill[]; has_more: boolean }>(\'/v1/skills\');\n// Adds anthropic-beta: skills-2025-10-02 header automatically\n\n// Upload a skill (multipart/form-data)\nimport { buildMultipartFormData } from \'@vibe-agent-toolkit/claude-marketplace\';\nconst multipart = buildMultipartFormData(\n { display_title: \'My Skill\' },\n [{ fieldName: \'files[]\', filename: \'SKILL.md\', content: Buffer.from(skillContent) }],\n);\nconst created = await client.uploadSkill<{ id: string; latest_version: string }>(multipart);\n\n// Delete a skill\nconst deleted = await client.deleteSkill<{ id: string; type: string }>(skillId);\n\`\`\`\n\n### Report Endpoints — Pagination Quirk\n\nUsage, cost, and code-analytics endpoints have a **non-standard pagination model**.\nThey return \`has_more: true\` with a \`next_page\` cursor, but the API **rejects \`next_page\`\nas a query parameter**. Paginate by advancing \`starting_at\` to the last bucket\'s \`ending_at\`.\n\n**Usage report (daily token buckets):**\n\`\`\`typescript\ninterface UsageBucket {\n starting_at: string;\n ending_at: string;\n results: Array<{\n uncached_input_tokens: number;\n cache_read_input_tokens: number;\n output_tokens: number;\n model: string | null;\n workspace_id: string | null;\n api_key_id: string | null;\n service_tier: string | null;\n // ... other dimension fields, null if not applicable\n }>;\n}\n\n// First page\nlet resp = await client.get<{ data: UsageBucket[]; has_more: boolean }>(\n \'/v1/organizations/usage_report/messages\',\n { starting_at: \'2025-01-01T00:00:00Z\', ending_at: \'2025-12-31T23:59:59Z\' }\n);\n\n// Subsequent pages — advance starting_at, do NOT pass next_page\nconst lastBucket = resp.data.at(-1);\nresp = await client.get<{ data: UsageBucket[]; has_more: boolean }>(\n \'/v1/organizations/usage_report/messages\',\n { starting_at: lastBucket.ending_at, ending_at: \'2025-12-31T23:59:59Z\' }\n);\n\`\`\`\n\n**Cost report:**\n\`\`\`typescript\n// amount is a STRING, not a number — parse before arithmetic\ninterface CostResult {\n currency: string;\n amount: string; // \"7.0812\" — use parseFloat()\n description: string | null;\n model: string | null;\n token_type: string | null;\n // ...\n}\n\n// group_by[] needs URLSearchParams for repeated params\nconst qs = new URLSearchParams();\nqs.set(\'starting_at\', \'2025-03-01T00:00:00Z\');\nqs.set(\'ending_at\', \'2025-03-31T23:59:59Z\');\nqs.append(\'group_by[]\', \'description\');\nqs.append(\'group_by[]\', \'workspace_id\');\nconst resp = await client.get<{ data: CostBucket[] }>(\n \`/v1/organizations/cost_report?${qs.toString()}\`\n);\n\n// Sum costs\nconst total = resp.data\n .flatMap(b => b.results)\n .reduce((sum, r) => sum + parseFloat(r.amount), 0);\n\`\`\`\n\n**Code analytics (Claude Code productivity):**\n\`\`\`typescript\n// Date-only format YYYY-MM-DD. No ending_at parameter.\nconst resp = await client.get<{ data: unknown[] }>(\n \'/v1/organizations/usage_report/claude_code\',\n { starting_at: \'2025-03-01\' } // NOT ISO datetime\n);\n// Returns empty data[] when no Claude Code enterprise seats are active\n\`\`\`"
|
|
22
22
|
},
|
|
23
23
|
commonRecipes: {
|
|
24
24
|
header: "## Common Recipes",
|
|
@@ -27,12 +27,17 @@ export const fragments = {
|
|
|
27
27
|
},
|
|
28
28
|
cliReference: {
|
|
29
29
|
header: "## CLI Reference",
|
|
30
|
-
body: "All commands require \`ANTHROPIC_ADMIN_API_KEY\` unless noted.\n\n\`\`\`\nvat claude org info Org identity (id, name)\nvat claude org users list [--limit N] List members\nvat claude org users get <user-id> Get single member\nvat claude org invites list List invitations\nvat claude org workspaces list List API workspaces\nvat claude org workspaces get <id> Get single workspace\nvat claude org workspaces members list <id> Workspace members\nvat claude org api-keys list [--status S] API key inventory\nvat claude org usage [--from DT] [--to DT] Token usage report\nvat claude org cost [--from DT] [--group-by F] USD cost report\nvat claude org code-analytics [--from DATE] Claude Code metrics (YYYY-MM-DD)\nvat claude org skills list Workspace skills (needs ANTHROPIC_API_KEY)\n\`\`\`\n\nExit codes: \`0\` success, \`1\` expected failure (stubs), \`2\` system error (missing key, API error).",
|
|
31
|
-
text: "## CLI Reference\n\nAll commands require \`ANTHROPIC_ADMIN_API_KEY\` unless noted.\n\n\`\`\`\nvat claude org info Org identity (id, name)\nvat claude org users list [--limit N] List members\nvat claude org users get <user-id> Get single member\nvat claude org invites list List invitations\nvat claude org workspaces list List API workspaces\nvat claude org workspaces get <id> Get single workspace\nvat claude org workspaces members list <id> Workspace members\nvat claude org api-keys list [--status S] API key inventory\nvat claude org usage [--from DT] [--to DT] Token usage report\nvat claude org cost [--from DT] [--group-by F] USD cost report\nvat claude org code-analytics [--from DATE] Claude Code metrics (YYYY-MM-DD)\nvat claude org skills list Workspace skills (needs ANTHROPIC_API_KEY)\n\`\`\`\n\nExit codes: \`0\` success, \`1\` expected failure (stubs), \`2\` system error (missing key, API error)."
|
|
30
|
+
body: "All commands require \`ANTHROPIC_ADMIN_API_KEY\` unless noted.\n\n\`\`\`\nvat claude org info Org identity (id, name)\nvat claude org users list [--limit N] List members\nvat claude org users get <user-id> Get single member\nvat claude org invites list List invitations\nvat claude org workspaces list List API workspaces\nvat claude org workspaces get <id> Get single workspace\nvat claude org workspaces members list <id> Workspace members\nvat claude org api-keys list [--status S] API key inventory\nvat claude org usage [--from DT] [--to DT] Token usage report\nvat claude org cost [--from DT] [--group-by F] USD cost report\nvat claude org code-analytics [--from DATE] Claude Code metrics (YYYY-MM-DD)\nvat claude org skills list Workspace skills (needs ANTHROPIC_API_KEY)\nvat claude org skills install <source> Upload skill dir or ZIP to org\nvat claude org skills delete <skill-id> Delete a skill (versions first)\nvat claude org skills versions list <skill-id> List skill versions\nvat claude org skills versions delete <id> <ver> Delete a skill version\n\`\`\`\n\nExit codes: \`0\` success, \`1\` expected failure (stubs), \`2\` system error (missing key, API error).\n\n**Skill deletion lifecycle:** The API requires all versions to be deleted before the skill itself.\nUse \`versions list\` to find versions, \`versions delete\` each one, then \`delete\` the skill.",
|
|
31
|
+
text: "## CLI Reference\n\nAll commands require \`ANTHROPIC_ADMIN_API_KEY\` unless noted.\n\n\`\`\`\nvat claude org info Org identity (id, name)\nvat claude org users list [--limit N] List members\nvat claude org users get <user-id> Get single member\nvat claude org invites list List invitations\nvat claude org workspaces list List API workspaces\nvat claude org workspaces get <id> Get single workspace\nvat claude org workspaces members list <id> Workspace members\nvat claude org api-keys list [--status S] API key inventory\nvat claude org usage [--from DT] [--to DT] Token usage report\nvat claude org cost [--from DT] [--group-by F] USD cost report\nvat claude org code-analytics [--from DATE] Claude Code metrics (YYYY-MM-DD)\nvat claude org skills list Workspace skills (needs ANTHROPIC_API_KEY)\nvat claude org skills install <source> Upload skill dir or ZIP to org\nvat claude org skills delete <skill-id> Delete a skill (versions first)\nvat claude org skills versions list <skill-id> List skill versions\nvat claude org skills versions delete <id> <ver> Delete a skill version\n\`\`\`\n\nExit codes: \`0\` success, \`1\` expected failure (stubs), \`2\` system error (missing key, API error).\n\n**Skill deletion lifecycle:** The API requires all versions to be deleted before the skill itself.\nUse \`versions list\` to find versions, \`versions delete\` each one, then \`delete\` the skill."
|
|
32
|
+
},
|
|
33
|
+
enterpriseSkillDistribution: {
|
|
34
|
+
header: "## Enterprise Skill Distribution",
|
|
35
|
+
body: "### Skills API (claude.ai / Console)\n\nSkills uploaded via \`POST /v1/skills\` are **workspace-scoped** and **automatically available to\nall workspace members**. There is no per-user enable/disable via the API — visibility is\nworkspace-level only. The admin UI may have additional controls not exposed in the API.\n\n**Upload from npm package:**\n\`\`\`bash\n# Upload all skills from a package\nvat claude org skills install --from-npm @scope/my-skills-package@1.0.0\n\n# Upload a single skill\nvat claude org skills install --from-npm @scope/my-skills-package@1.0.0 --skill my-skill\n\`\`\`\n\nThe package must contain \`dist/skills/<name>/SKILL.md\` (produced by \`vat skills build\`).\nIf skills are in a sub-dependency, the command searches \`node_modules/*/dist/skills/\` too.\n\n**Duplicate titles are rejected** — the API enforces unique \`display_title\` per workspace.\nWhen uploading multiple skills, failures are non-fatal; partial results are reported.\n\n### Managed Settings (Claude Code plugins)\n\nFor enterprise-wide Claude Code plugin deployment (not the Skills API), use managed settings\npushed via MDM (Jamf, Intune, SCCM, etc.):\n\n| Platform | Path |\n|---|---|\n| macOS | \`/Library/Application Support/ClaudeCode/managed-settings.json\` |\n| Linux | \`/etc/claude-code/managed-settings.json\` |\n| Windows | \`C:\\Program Files\\ClaudeCode\\managed-settings.json\` |\n\nManaged settings are **highest priority** in the settings cascade — they override user settings.\nUse this to force-enable plugins, lock down permissions, or configure organization defaults.\n\n\`\`\`json\n{\n \"enabledPlugins\": {\n \"my-plugin@my-marketplace\": true\n }\n}\n\`\`\`\n\nCombine with \`npm install -g <package>\` (via IT software deployment) to install the plugin\nbinary, then managed settings to enable it across all machines.",
|
|
36
|
+
text: "## Enterprise Skill Distribution\n\n### Skills API (claude.ai / Console)\n\nSkills uploaded via \`POST /v1/skills\` are **workspace-scoped** and **automatically available to\nall workspace members**. There is no per-user enable/disable via the API — visibility is\nworkspace-level only. The admin UI may have additional controls not exposed in the API.\n\n**Upload from npm package:**\n\`\`\`bash\n# Upload all skills from a package\nvat claude org skills install --from-npm @scope/my-skills-package@1.0.0\n\n# Upload a single skill\nvat claude org skills install --from-npm @scope/my-skills-package@1.0.0 --skill my-skill\n\`\`\`\n\nThe package must contain \`dist/skills/<name>/SKILL.md\` (produced by \`vat skills build\`).\nIf skills are in a sub-dependency, the command searches \`node_modules/*/dist/skills/\` too.\n\n**Duplicate titles are rejected** — the API enforces unique \`display_title\` per workspace.\nWhen uploading multiple skills, failures are non-fatal; partial results are reported.\n\n### Managed Settings (Claude Code plugins)\n\nFor enterprise-wide Claude Code plugin deployment (not the Skills API), use managed settings\npushed via MDM (Jamf, Intune, SCCM, etc.):\n\n| Platform | Path |\n|---|---|\n| macOS | \`/Library/Application Support/ClaudeCode/managed-settings.json\` |\n| Linux | \`/etc/claude-code/managed-settings.json\` |\n| Windows | \`C:\\Program Files\\ClaudeCode\\managed-settings.json\` |\n\nManaged settings are **highest priority** in the settings cascade — they override user settings.\nUse this to force-enable plugins, lock down permissions, or configure organization defaults.\n\n\`\`\`json\n{\n \"enabledPlugins\": {\n \"my-plugin@my-marketplace\": true\n }\n}\n\`\`\`\n\nCombine with \`npm install -g <package>\` (via IT software deployment) to install the plugin\nbinary, then managed settings to enable it across all machines."
|
|
32
37
|
},
|
|
33
38
|
notYetImplemented: {
|
|
34
39
|
header: "## Not Yet Implemented",
|
|
35
|
-
body: "These commands exist with the correct CLI shape but return structured\n\`not-yet-implemented\` stubs (exit 1). Coming in a future release:\n\n- \`org users update/remove\` — role changes, offboarding\n- \`org invites create/delete\` — programmatic invitations\n- \`org workspaces create/archive\` — workspace lifecycle\n- \`org workspaces members add/update/remove\` — workspace membership\n- \`org api-keys update\` — rename keys
|
|
36
|
-
text: "## Not Yet Implemented\n\nThese commands exist with the correct CLI shape but return structured\n\`not-yet-implemented\` stubs (exit 1). Coming in a future release:\n\n- \`org users update/remove\` — role changes, offboarding\n- \`org invites create/delete\` — programmatic invitations\n- \`org workspaces create/archive\` — workspace lifecycle\n- \`org workspaces members add/update/remove\` — workspace membership\n- \`org api-keys update\` — rename keys
|
|
40
|
+
body: "These commands exist with the correct CLI shape but return structured\n\`not-yet-implemented\` stubs (exit 1). Coming in a future release:\n\n- \`org users update/remove\` — role changes, offboarding\n- \`org invites create/delete\` — programmatic invitations\n- \`org workspaces create/archive\` — workspace lifecycle\n- \`org workspaces members add/update/remove\` — workspace membership\n- \`org api-keys update\` — rename keys",
|
|
41
|
+
text: "## Not Yet Implemented\n\nThese commands exist with the correct CLI shape but return structured\n\`not-yet-implemented\` stubs (exit 1). Coming in a future release:\n\n- \`org users update/remove\` — role changes, offboarding\n- \`org invites create/delete\` — programmatic invitations\n- \`org workspaces create/archive\` — workspace lifecycle\n- \`org workspaces members add/update/remove\` — workspace membership\n- \`org api-keys update\` — rename keys"
|
|
37
42
|
}
|
|
38
43
|
};
|
|
@@ -93,8 +93,20 @@ const keys = await client.get<{ data: ApiKey[]; has_more: boolean }>(
|
|
|
93
93
|
|
|
94
94
|
**Skills (beta — uses regular API key):**
|
|
95
95
|
```typescript
|
|
96
|
+
// List skills
|
|
96
97
|
const skills = await client.getSkills<{ data: Skill[]; has_more: boolean }>('/v1/skills');
|
|
97
98
|
// Adds anthropic-beta: skills-2025-10-02 header automatically
|
|
99
|
+
|
|
100
|
+
// Upload a skill (multipart/form-data)
|
|
101
|
+
import { buildMultipartFormData } from '@vibe-agent-toolkit/claude-marketplace';
|
|
102
|
+
const multipart = buildMultipartFormData(
|
|
103
|
+
{ display_title: 'My Skill' },
|
|
104
|
+
[{ fieldName: 'files[]', filename: 'SKILL.md', content: Buffer.from(skillContent) }],
|
|
105
|
+
);
|
|
106
|
+
const created = await client.uploadSkill<{ id: string; latest_version: string }>(multipart);
|
|
107
|
+
|
|
108
|
+
// Delete a skill
|
|
109
|
+
const deleted = await client.deleteSkill<{ id: string; type: string }>(skillId);
|
|
98
110
|
```
|
|
99
111
|
|
|
100
112
|
### Report Endpoints — Pagination Quirk
|
|
@@ -254,10 +266,65 @@ vat claude org usage [--from DT] [--to DT] Token usage report
|
|
|
254
266
|
vat claude org cost [--from DT] [--group-by F] USD cost report
|
|
255
267
|
vat claude org code-analytics [--from DATE] Claude Code metrics (YYYY-MM-DD)
|
|
256
268
|
vat claude org skills list Workspace skills (needs ANTHROPIC_API_KEY)
|
|
269
|
+
vat claude org skills install <source> Upload skill dir or ZIP to org
|
|
270
|
+
vat claude org skills delete <skill-id> Delete a skill (versions first)
|
|
271
|
+
vat claude org skills versions list <skill-id> List skill versions
|
|
272
|
+
vat claude org skills versions delete <id> <ver> Delete a skill version
|
|
257
273
|
```
|
|
258
274
|
|
|
259
275
|
Exit codes: `0` success, `1` expected failure (stubs), `2` system error (missing key, API error).
|
|
260
276
|
|
|
277
|
+
**Skill deletion lifecycle:** The API requires all versions to be deleted before the skill itself.
|
|
278
|
+
Use `versions list` to find versions, `versions delete` each one, then `delete` the skill.
|
|
279
|
+
|
|
280
|
+
## Enterprise Skill Distribution
|
|
281
|
+
|
|
282
|
+
### Skills API (claude.ai / Console)
|
|
283
|
+
|
|
284
|
+
Skills uploaded via `POST /v1/skills` are **workspace-scoped** and **automatically available to
|
|
285
|
+
all workspace members**. There is no per-user enable/disable via the API — visibility is
|
|
286
|
+
workspace-level only. The admin UI may have additional controls not exposed in the API.
|
|
287
|
+
|
|
288
|
+
**Upload from npm package:**
|
|
289
|
+
```bash
|
|
290
|
+
# Upload all skills from a package
|
|
291
|
+
vat claude org skills install --from-npm @scope/my-skills-package@1.0.0
|
|
292
|
+
|
|
293
|
+
# Upload a single skill
|
|
294
|
+
vat claude org skills install --from-npm @scope/my-skills-package@1.0.0 --skill my-skill
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
The package must contain `dist/skills/<name>/SKILL.md` (produced by `vat skills build`).
|
|
298
|
+
If skills are in a sub-dependency, the command searches `node_modules/*/dist/skills/` too.
|
|
299
|
+
|
|
300
|
+
**Duplicate titles are rejected** — the API enforces unique `display_title` per workspace.
|
|
301
|
+
When uploading multiple skills, failures are non-fatal; partial results are reported.
|
|
302
|
+
|
|
303
|
+
### Managed Settings (Claude Code plugins)
|
|
304
|
+
|
|
305
|
+
For enterprise-wide Claude Code plugin deployment (not the Skills API), use managed settings
|
|
306
|
+
pushed via MDM (Jamf, Intune, SCCM, etc.):
|
|
307
|
+
|
|
308
|
+
| Platform | Path |
|
|
309
|
+
|---|---|
|
|
310
|
+
| macOS | `/Library/Application Support/ClaudeCode/managed-settings.json` |
|
|
311
|
+
| Linux | `/etc/claude-code/managed-settings.json` |
|
|
312
|
+
| Windows | `C:\Program Files\ClaudeCode\managed-settings.json` |
|
|
313
|
+
|
|
314
|
+
Managed settings are **highest priority** in the settings cascade — they override user settings.
|
|
315
|
+
Use this to force-enable plugins, lock down permissions, or configure organization defaults.
|
|
316
|
+
|
|
317
|
+
```json
|
|
318
|
+
{
|
|
319
|
+
"enabledPlugins": {
|
|
320
|
+
"my-plugin@my-marketplace": true
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
Combine with `npm install -g <package>` (via IT software deployment) to install the plugin
|
|
326
|
+
binary, then managed settings to enable it across all machines.
|
|
327
|
+
|
|
261
328
|
## Not Yet Implemented
|
|
262
329
|
|
|
263
330
|
These commands exist with the correct CLI shape but return structured
|
|
@@ -268,4 +335,3 @@ These commands exist with the correct CLI shape but return structured
|
|
|
268
335
|
- `org workspaces create/archive` — workspace lifecycle
|
|
269
336
|
- `org workspaces members add/update/remove` — workspace membership
|
|
270
337
|
- `org api-keys update` — rename keys
|
|
271
|
-
- `org skills install/delete` — enterprise skill distribution via API
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vibe-agent-toolkit/vat-development-agents",
|
|
3
|
-
"version": "0.1.22
|
|
3
|
+
"version": "0.1.22",
|
|
4
4
|
"description": "VAT development agents - dogfooding the vibe-agent-toolkit",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"keywords": [
|
|
@@ -64,13 +64,13 @@
|
|
|
64
64
|
"postinstall": "vat claude plugin install --npm-postinstall || exit 0"
|
|
65
65
|
},
|
|
66
66
|
"dependencies": {
|
|
67
|
-
"@vibe-agent-toolkit/agent-schema": "0.1.22
|
|
68
|
-
"@vibe-agent-toolkit/cli": "0.1.22
|
|
67
|
+
"@vibe-agent-toolkit/agent-schema": "0.1.22",
|
|
68
|
+
"@vibe-agent-toolkit/cli": "0.1.22",
|
|
69
69
|
"yaml": "^2.8.2"
|
|
70
70
|
},
|
|
71
71
|
"devDependencies": {
|
|
72
72
|
"@types/node": "^25.0.3",
|
|
73
|
-
"@vibe-agent-toolkit/resource-compiler": "0.1.22
|
|
73
|
+
"@vibe-agent-toolkit/resource-compiler": "0.1.22",
|
|
74
74
|
"ts-patch": "^3.2.1",
|
|
75
75
|
"typescript": "^5.7.3"
|
|
76
76
|
},
|