diff --git a/CLAUDE.md b/CLAUDE.md index db52d03..c0ad47f 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -11,7 +11,7 @@ cf configure --profile myprofile --delete # remove a profile ## Key Patterns - **All output is JSON** on stdout. Errors are JSON on stderr. -- **Exit codes are semantic**: 0=ok, 1=error, 2=auth, 3=not_found, 4=validation, 5=rate_limited, 6=permission +- **Exit codes are semantic**: 0=ok, 1=error, 2=auth, 3=not_found, 4=validation, 5=rate_limited, 6=conflict, 7=server - **Use `--preset`** for common field sets: `cf pages get --id 12345 --preset agent` (presets: agent, brief, titles, meta, tree, search, diff) - **Use `--jq`** to reduce output tokens: `cf pages get --id 12345 --jq '{id: .id, title: .title}'` - **Use `--fields`** to limit Confluence response fields: `cf pages get --id 12345 --fields id,title,status` @@ -48,8 +48,8 @@ cf blogposts list --jq '.results[] | {id, title}' cf workflow comment --id 12345 --body "Reviewed and approved" # Labels -cf labels add --page-id 12345 --name "reviewed" -cf labels remove --page-id 12345 --name "draft" +cf labels add --page-id 12345 --label "reviewed" +cf labels remove --page-id 12345 --label "draft" # Attachments cf attachments upload --page-id 12345 --file ./diagram.png @@ -79,8 +79,8 @@ echo '{"spaceId":"123"}' | cf raw POST /wiki/api/v2/pages --body - # stdin # Batch operations echo '[{"command":"pages get","args":{"id":"12345"},"jq":".title"},{"command":"pages get","args":{"id":"67890"},"jq":".title"}]' | cf batch -# Watch for changes (NDJSON stream — always use --max-events in automated contexts) -cf watch --cql "space = DEV" --interval 30s --max-events 50 +# Watch for changes (NDJSON stream — always use --max-polls in automated contexts) +cf watch --cql "space = DEV" --interval 30s --max-polls 50 ``` diff --git a/go.mod b/go.mod index 603e6b5..6574ac4 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/sofq/confluence-cli -go 1.25.8 +go 1.25.9 require ( github.com/itchyny/gojq v0.12.19 diff --git a/skill/confluence-cli/SKILL.md b/skill/confluence-cli/SKILL.md index 78c332a..d25ec70 100644 --- a/skill/confluence-cli/SKILL.md +++ b/skill/confluence-cli/SKILL.md @@ -1,23 +1,29 @@ --- name: confluence-cli -description: "How to use `cf`, the agent-friendly Confluence CLI, to interact with Confluence Cloud. Use this skill whenever the user asks to work with Confluence pages, spaces, blog posts, comments, labels, attachments, or any Confluence operation — searching content, creating pages, managing spaces, exporting page trees, or automating Confluence workflows. Also use when you see `cf` commands in the codebase or the user mentions Confluence in the context of CLI tooling. Even if the user just says 'check my Confluence pages' or 'update the wiki', this skill applies." +description: "Confluence Cloud CLI (`cf`) — interact with Confluence pages, spaces, blog posts, comments, labels, attachments, and any Confluence Cloud API operation through structured JSON output and semantic exit codes. Use this skill whenever the user asks about Confluence, Atlassian wiki, knowledge base pages, or any wiki/documentation management that involves Confluence — creating pages, searching content, exporting page trees, managing spaces, or automating workflows. Also trigger when you see `cf` commands in the codebase, or the user says things like 'check the wiki', 'update the docs on Confluence', 'publish to Confluence', or 'search our knowledge base'. Even casual mentions of Confluence or Atlassian wiki operations should trigger this skill." +compatibility: + tools: ["Bash"] + requirements: ["cf CLI binary (brew install sofq/tap/cf)"] --- -# cf — Confluence CLI for AI Agents +# cf — Confluence Cloud CLI for AI Agents `cf` is a Confluence Cloud CLI designed for AI agents. Every command returns structured JSON on stdout, errors as JSON on stderr, and semantic exit codes — so you can parse, branch, and retry reliably. -**Sections:** [Setup](#setup) · [Discovering Commands](#discovering-commands) · [Common Operations](#common-operations) · [Token Efficiency](#token-efficiency) · [Batch Operations](#batch-operations) · [Error Handling](#error-handling) · [Global Flags](#global-flags) · [Common Agent Patterns](#common-agent-patterns) · [Security](#security) · [Troubleshooting](#troubleshooting) +**Sections:** [Setup](#setup) · [Discovering Commands](#discovering-commands) · [Common Operations](#common-operations) · [Token Efficiency](#token-efficiency) · [Batch Operations](#batch-operations) · [Error Handling](#error-handling) · [Global Flags](#global-flags) · [Common Agent Patterns](#common-agent-patterns) + +**Reference files** (read when needed): +- `references/presets.md` — Full preset reference table and custom preset config +- `references/batch-commands.md` — Batch command name mapping table and examples +- `references/security.md` — Operation policies, batch limits, audit logging +- `references/troubleshooting.md` — Installation, auth errors, content format notes ## Setup If `cf` is not configured yet, help the user set it up: ```bash -# Check if cf is installed -cf version - -# Configure with Confluence Cloud credentials +cf version # check if installed cf configure --base-url https://yoursite.atlassian.net --token YOUR_API_TOKEN ``` @@ -43,10 +49,10 @@ If you get exit code 2 (auth error), the token is likely expired or wrong. Ask t ## Discovering Commands -`cf` has 200+ commands auto-generated from Confluence's OpenAPI spec. You don't need to memorize them — discover at runtime: +`cf` has 200+ commands auto-generated from Confluence's OpenAPI spec. Discover at runtime: ```bash -cf schema # resource → verbs mapping (default, most useful overview) +cf schema # resource → verbs mapping (most useful overview) cf schema --list # all resource names only (pages, spaces, comments, ...) cf schema pages # all operations for a resource cf schema pages get # full schema with all flags for one operation @@ -54,41 +60,28 @@ cf schema pages get # full schema with all flags for one operation Always use `cf schema` to discover the exact command name and flags before running an unfamiliar operation. -`cf schema` (no flags) defaults to the compact resource→verbs mapping, which is the most useful starting point. - ## Common Operations -### Get a page +### Pages ```bash cf pages get --id 12345 -``` - -### Search content with CQL -```bash -cf search search-content \ - --cql "space = DEV AND type = page AND lastModified > now('-7d')" \ - --jq '.results[] | {id, title}' -``` - -### Create a page -```bash -# Content uses Confluence storage format (XHTML, not Markdown) cf pages create --spaceId 123456 --title "Deploy Runbook" \ --body "
Follow these steps...
" -``` - -### Update a page -```bash cf pages update --id 12345 --version-number 3 \ --title "Deploy Runbook v2" --body "What we s
cf blogposts list --jq '.results[] | {id, title}'
```
-### Comments
+### Comments, Labels, Attachments
```bash
cf workflow comment --id 12345 --body "Reviewed and approved"
-```
-
-### Labels
-```bash
-cf labels add --page-id 12345 --name "reviewed"
-cf labels remove --page-id 12345 --name "draft"
-cf labels list --page-id 12345 --jq '.results[].name'
-```
-
-### Attachments
-```bash
+cf labels add --page-id 12345 --label "reviewed"
+cf labels remove --page-id 12345 --label "draft"
cf attachments upload --page-id 12345 --file ./diagram.png
cf attachments list --page-id 12345 --jq '.results[] | {id, title}'
-cf attachments delete --id 67890
```
### Workflow commands
```bash
-# Move a page under another page
cf workflow move --id 12345 --target 67890 --position append
-
-# Copy a page (async — waits for completion by default)
cf workflow copy --id 12345 --target 67890 --title "Copy of Runbook"
-
-# Publish a draft
cf workflow publish --id 12345
-
-# Archive a page
cf workflow archive --id 12345
-
-# Restrict access
cf workflow restrict --id 12345 --user "john@company.com" --operation read
```
### Diff — version comparison
```bash
-# All changes
-cf diff --id 12345
-
-# Changes in last 2 hours
-cf diff --id 12345 --since 2h
-
-# Changes since a specific date
-cf diff --id 12345 --since 2026-01-01
+cf diff --id 12345 # all changes
+cf diff --id 12345 --since 2h # changes in last 2 hours
+cf diff --id 12345 --since 2026-01-01 # changes since date
```
### Export — page content extraction
@@ -157,74 +126,47 @@ cf export --id 12345 --format storage # raw Confluence storage format
### Watch for changes (NDJSON stream)
```bash
-# Poll a CQL query and emit events as NDJSON (one JSON object per line)
-cf watch --cql "space = DEV" --interval 30s --max-events 50
-
-# Watch a specific space
-cf watch --cql "space = DEV AND type = page" --interval 1m --max-events 20
+cf watch --cql "space = DEV" --interval 30s --max-polls 50
```
-Events: `initial` (first poll), `created`, `updated`, `removed`.
-
-**Important:** Always use `--max-events` when calling from an automated/agent context — agents cannot send Ctrl-C (SIGINT) to stop the stream.
+Events: `initial`, `created`, `updated`, `removed`. Always use `--max-polls` in automated contexts — agents cannot send Ctrl-C to stop the stream.
### Raw API call (escape hatch)
```bash
-# For any endpoint not covered by generated commands
cf raw GET /wiki/api/v2/pages/12345
cf raw POST /wiki/api/v2/pages --body '{"spaceId":"123","title":"New Page"}'
-cf raw POST /wiki/api/v2/pages --body @request.json
-# Read body from stdin (must use --body - explicitly)
-echo '{"spaceId":"123","title":"New"}' | cf raw POST /wiki/api/v2/pages --body -
+echo '{"spaceId":"123"}' | cf raw POST /wiki/api/v2/pages --body -
```
-**Note:** POST/PUT/PATCH require `--body`. Without it, `cf raw` will error instead of hanging on stdin.
+POST/PUT/PATCH require `--body`. Without it, `cf raw` will error instead of hanging on stdin.
## Token Efficiency
Confluence responses can be large (8K+ tokens for a single page). Always minimize output:
```bash
-# --preset: use a named preset for common field combinations
+# --preset: named preset for common field combinations
cf pages get --id 12345 --preset agent # id, title, status, spaceId
cf pages get --id 12345 --preset brief # id, title, status
-cf pages get --id 12345 --preset meta # id, title, version, dates
-
-# List all available presets
-cf preset list
-# --fields: tell Confluence to return only these fields (server-side filtering)
+# --fields: server-side field filtering
cf pages get --id 12345 --fields id,title,status
-# --jq: filter the JSON response (client-side filtering)
+# --jq: client-side JSON filtering
cf pages get --id 12345 --jq '{id: .id, title: .title}'
-# Combine both for maximum efficiency (~50 tokens vs ~8,000)
+# Combine for maximum efficiency (~50 tokens vs ~8,000)
cf pages get --id 12345 --fields id,title --jq '{id: .id, title: .title}'
-# Cache read-heavy data to avoid redundant API calls
+# Cache read-heavy data
cf spaces list --cache 5m --jq '[.results[].key]'
```
-**Always use `--preset` or `--fields` + `--jq`.** `--preset` gives you common field sets with zero effort. `--fields` reduces what Confluence sends back, `--jq` shapes the output into exactly what you need.
-
-### Preset reference
-
-| Preset | Description |
-|--------|-------------|
-| `brief` | id, title, status |
-| `titles` | id, title only |
-| `agent` | id, title, status, spaceId — optimized for AI agents |
-| `tree` | hierarchical page tree view |
-| `meta` | id, title, version, created/modified dates |
-| `search` | search result fields |
-| `diff` | version comparison fields |
-
-User-defined presets can include a `jq` filter; store them in profile config or `~/.config/cf/presets.json`.
+Always use `--preset` or `--fields` + `--jq`. Run `cf preset list` for available presets. See `references/presets.md` for the full preset table.
## Batch Operations
-When you need multiple Confluence calls, use `cf batch` to run them in a single process:
+Run multiple Confluence calls in a single process:
```bash
echo '[
@@ -234,85 +176,30 @@ echo '[
]' | cf batch
```
-**Batch exit code:** The process exit code is the highest-severity exit code from all operations. Check individual `exit_code` fields for per-operation status.
-
-### Batch command names
-
-Batch uses `"resource verb"` strings matching `cf schema` output:
-
-| CLI command | Batch `"command"` string |
-|---|---|
-| `cf pages get` | `"pages get"` |
-| `cf pages create` | `"pages create"` |
-| `cf pages update` | `"pages update"` |
-| `cf workflow move` | `"workflow move"` |
-| `cf workflow copy` | `"workflow copy"` |
-| `cf workflow archive` | `"workflow archive"` |
-| `cf workflow comment` | `"workflow comment"` |
-| `cf search search-content` | `"search search-content"` |
-| `cf diff` | `"diff diff"` |
-| `cf export` | `"export export"` |
-
-Note: `"diff diff"` and `"export export"` are correct — the resource and verb share the same name.
-
-```bash
-echo '[
- {"command": "pages get", "args": {"id": "12345"}, "jq": ".title"},
- {"command": "workflow comment", "args": {"id": "12345", "body": "Reviewed"}},
- {"command": "diff diff", "args": {"id": "12345", "since": "2h"}}
-]' | cf batch
-
-# Or read from a file
-cf batch --input ops.json
-```
+Batch exit code is the highest-severity code from all operations. See `references/batch-commands.md` for the full command name mapping table.
## Error Handling
-Errors are structured JSON on stderr. Branch on `exit_code` and `error_type`:
-
-| Exit code | error_type | Meaning | What to do |
-|-----------|-----------|---------|------------|
-| 0 | — | Success | Parse stdout as JSON |
-| 1 | `connection_error` | Network/unknown error | Check connectivity, retry |
-| 2 | `auth_failed` | Auth failed (401/403) | Check token/credentials |
-| 3 | `not_found` | Resource not found (404) | Verify page ID / resource ID |
-| 4 | `validation_error` | Bad request (400/422) | Fix the request payload |
-| 5 | `rate_limited` | Rate limited (429) | Wait `retry_after` seconds, then retry |
-| 6 | `permission_denied` | Permission denied | Check access / space permissions |
-
-Error JSON includes optional `hint` (actionable recovery text) and `retry_after` (integer seconds for rate limits):
-
-```json
-{
- "error_type": "rate_limited",
- "status": 429,
- "message": "Rate limit exceeded",
- "hint": "You are being rate limited. Wait before retrying.",
- "retry_after": 30,
- "request": {"method": "GET", "path": "/wiki/api/v2/pages/12345"}
-}
-```
-
-### Retry pattern for agents
+Errors are structured JSON on stderr. Branch on `exit_code`:
-For rate limits (exit 5) and general errors (exit 1), retry with backoff:
+| Exit code | Meaning | Action |
+|-----------|---------|--------|
+| 0 | Success | Parse stdout as JSON |
+| 1 | Network/unknown error | Check connectivity, retry with backoff |
+| 2 | Auth failed (401/403) | Check token/credentials |
+| 3 | Not found (404) | Verify resource ID — do not retry |
+| 4 | Bad request (400/422) | Fix the request payload — do not retry |
+| 5 | Rate limited (429) | Wait `retry_after` seconds from stderr JSON, then retry |
+| 6 | Conflict (409) | Resource conflict — resolve and retry |
+| 7 | Server error (5xx) | Confluence server issue — retry with backoff |
-```
-1. Run the command
-2. If exit code is 5 (rate_limited):
- - Parse stderr JSON, read "retry_after" (integer seconds)
- - Wait that many seconds, then retry (max 3 retries)
-3. If exit code is 1 (connection error):
- - Retry with exponential backoff: wait 1s, 2s, 4s (max 3 retries)
-4. If exit code is 3 (not_found) or 4 (validation_error):
- - Do NOT retry — fix the request or report to user
-```
+For rate limits (exit 5), parse `retry_after` from stderr JSON and wait. For connection errors (exit 1), retry with exponential backoff (1s, 2s, 4s, max 3 retries). Do not retry exit codes 3 or 4.
## Global Flags
| Flag | Description |
|------|-------------|
-| `--preset Updated content Content Project documentation root Root Setup guide System design Operations guide Setup Design test test Content Content` | comma-separated fields to return (GET only) |
| `--cache
Title
Title