AgentStack
Browse Sign in
Browse Why AgentStack Sell Docs
Sign in
SKILL verified Apache-2.0 Self-run

Confluence Publisher

skill-eugenelim-agent-ready-repo-confluence-publisher · by eugenelim

Publish content to a Confluence page (Atlassian Cloud or Server/Data Center) by creating a new page or updating an existing one. Accepts Markdown (default), raw Confluence storage XHTML, or plain text. Resolves the target by page ID, URL, frontmatter `confluence_id`, or space + title lookup. Handles optimistic-locking 409s with one retry. Use when the user wants to push a report, design doc, or o…

— No reviews yet
0 installs
33 views
0.0% view→install

Install

$ agentstack add skill-eugenelim-agent-ready-repo-confluence-publisher

✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

  • ✓ Prompt-injection patterns
  • ✓ Secret / credential exfiltration
  • ✓ Dangerous shell & filesystem operations
  • ✓ Untrusted network calls
  • ✓ Known-malicious package signatures

What it can access

  • ✓ Network access No
  • ✓ Filesystem access No
  • ✓ Shell / process execution No
  • ● Environment & secrets Used
  • ✓ Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

View the full security report →

Verified badge

Passed review? Show it. Paste this badge into your README, it links to the public security report.

AgentStack Verified badge Links to your public security report.
[![AgentStack Verified](https://agentstack.voostack.com/badges/verified.svg)](https://agentstack.voostack.com/security/report/skill-eugenelim-agent-ready-repo-confluence-publisher)

Reliability & compatibility

✓ Security review passed
0 installs to date
— no reviews yet
● 2mo ago

Declared compatibility

Claude CodeClaude Desktop

Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.

Preview Execution monitoring

We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.

How agent discovery & health will work →
Are you the author of Confluence Publisher? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

Confluence Publisher

Publish a single page to Confluence — create or update — from Markdown, storage XHTML, or plain text. Companion to confluence-crawler: same credentials namespace, same flavor support, opposite direction.

Instructions

You are a Confluence publishing agent. Authentication, REST mechanics, optimistic-locking retries, and the Markdown→storage conversion live in scripts/. Do not re-implement any of that; invoke the script with the right flags and report the result.

Flavor support

Same as the crawler:

  • Atlassian Cloud (*.atlassian.net) — Basic auth with email + API

token. Base URL must include /wiki.

  • Confluence Server / Data Center — Bearer auth with a Personal

Access Token.

Flavor is auto-detected from the base URL; override via CONFLUENCE_FLAVOR=cloud|server if needed.

Configuration location

Credentials are resolved by the build-projected credentials_shim.load_credentials through Tier 1 (env) → Tier 2 (OS keyring) → Tier 3 dotfile. The dotfile lives at ~/.agentbundle/credentials.env. The declared schema is at references/creds-schema.toml and shares the confluence namespace with confluence-crawler — if either skill has been configured, this one works.

| Key | Required | Notes | |---|---|---| | CONFLUENCE_BASE_URL | yes | Cloud: https://.atlassian.net/wiki. Server: https://confluence.corp.example.com. | | CONFLUENCE_API_TOKEN | yes | Cloud API token or Server PAT. | | CONFLUENCE_EMAIL | Cloud only | Atlassian account email. | | CONFLUENCE_FLAVOR | no | cloud or server. Auto-detected from URL host. |

Populate any tier by running credential-setup skill.

Security rules (non-negotiable)

  • Secrets live only in ~/.agentbundle/credentials.env

(mode 0600 on POSIX; DACL-restricted on Windows), the OS keyring, or process environment variables. Never read that file, print it, or echo the token.

  • Never put the token on the command line. The primitive

refuses flags like --token / --api-token / --bearer / --pat / --password and exits — do not work around it.

  • If --check reports missing or invalid creds, tell the user to run

credential-setup skill themselves. It's interactive — do not run it for them.

Step 1: Verify the environment

python -m pip install -r requirements.txt
python scripts/publish_page.py --check
  • Exit code 0 → authenticated, proceed.
  • Exit code 2 → the user must act (credentials missing/invalid/expired). Tell

the user to run credential-setup skill themselves (interactive — they run it, not you). Stop here.

  • Any other non-zero → see When a request fails.

When a request fails

The CLI uses a banded exit-code contract; read the stderr message for the specific cause, then act on the band:

| Exit | Band | What to do | |---|---|---| | 0 | success | proceed | | 1 | functional error — server 5xx, transport, keychain hard-fail, unexpected | surface the message to the user; don't loop or retry blindly | | 2 | user must act — credentials (401/403), a publish conflict, or a target/input the user must fix | follow the NEED-INPUT: message: re-auth via credential-setup, resolve the conflict, or fix the target — then retry |

Tier2HardFailError (OS keyring unavailable) or an unprojected shim surface as exit 1 with a message naming the cause.

Step 2: Decide how to identify the target page

In order of robustness — use whichever the user gave:

  1. By page ID or URL (preferred).

--page-id 12345 or --url https://acme.atlassian.net/wiki/spaces/ENG/pages/12345/Some+Title. The page ID is parsed out of the URL. Idempotent.

  1. By frontmatter — if the input file was produced by

confluence-crawler it carries confluence_id (and optionally version, space_key) in YAML frontmatter. --from-frontmatter reads it. This is the round-trip case (crawl → edit → publish back).

  1. By space + title — --space ENG --title "My Page" [--parent-id 999].

Looks up by title; if found, updates; if not, creates. Title lookups are fragile (titles change); prefer modes 1 and 2 when an ID is available.

If none of these are supplied, the script exits 2 and asks which. Do not guess.

Step 3: Publish

Pick the form that matches the user's request:

# Update an existing page by ID, from Markdown:
python scripts/publish_page.py --page-id 12345 --input report.md

# Same, but from a Confluence URL:
python scripts/publish_page.py --url 'https://acme.atlassian.net/wiki/spaces/ENG/pages/12345/Foo' --input report.md

# Round-trip case — the markdown came from confluence-crawler:
python scripts/publish_page.py --from-frontmatter --input crawled/eng-handbook.md

# Lookup-then-upsert by title:
python scripts/publish_page.py --space ENG --title "Q2 Report" --parent-id 999 --input report.md

# Plain text body (one paragraph per line):
python scripts/publish_page.py --page-id 12345 --input - --input-format text   # stdin

# Already-rendered storage XHTML:
python scripts/publish_page.py --page-id 12345 --input snippet.xhtml --input-format storage

# Dry-run — print what would be sent, do not call write APIs:
python scripts/publish_page.py --page-id 12345 --input report.md --dry-run

Flags:

| Flag | Meaning | |---|---| | --check | Verify credentials and connectivity, then exit. | | --page-id ID | Update this page (preferred). | | --url URL | Parse page ID from a Confluence URL. | | --from-frontmatter | Read confluence_id (and optional version) from input file's YAML frontmatter. | | --space KEY --title TITLE | Lookup-then-upsert by title. --parent-id ID optional. | | --input PATH or - | Source file (or - for stdin). Required. | | --input-format | markdown (default), storage, text. | | --version-comment TEXT | Recorded on the new page version. Defaults to a generic message. | | --attach PATH (repeatable) | Upload file as a page attachment; Markdown image refs whose target filename matches an attachment get rewritten to `. | | --label LABEL (repeatable) | Apply labels after publish. | | --dry-run | Print the rendered storage XHTML and planned operation; no writes. | | --insecure | Disable TLS verification (Server/DC w/ self-signed). User-requested only. | | --verbose` | Debug logging. |

Step 4: Interpret the output

On success the script prints:

OK:  page 12345 (version 8) — https://acme.atlassian.net/wiki/spaces/ENG/pages/12345/Foo

On a 409 (someone else edited between read and write) the script re-reads the page once and retries with the new version number. If the second attempt still conflicts, it surfaces the error — tell the user a human edited concurrently and ask them to re-run.

Behavior notes

  • Update vs create. --page-id/--url always updates; never

creates a new page at a specific ID. --from-frontmatter updates the page named in the frontmatter. --space + --title updates if a page with that title exists in the space, otherwise creates one (under --parent-id if given, otherwise at the space root).

  • Title. On update, the title is taken from --title if given, the

first # H1 of the markdown if not (markdown input only), and the existing page title as a final fallback. On create, --title is required (or the first H1 if --input-format markdown). Heads-up: for markdown input, the H1 overrides the existing page title even on a routine re-publish — if you don't want a rename, pass --title explicitly or strip the H1.

  • Attachment ordering. On an update of an existing page,

attachments upload before the body update so `` references resolve immediately. On a create, attachments upload after the page is created (the page must exist first); the body's image refs render broken for the subsecond gap between create and the attachment uploads. Failure semantics are not symmetric: if an update's attachment uploads partly succeed and then raise, the body update is skipped — the page still shows the prior body but now has the new attachments orphaned on it; re-running is idempotent because Confluence dedupes attachment uploads by filename. On create, an attachment failure after a successful create leaves the page in place with the body referencing un-uploaded files.

  • Version comment. Recorded on the new version; helps reviewers see

why an agent edited. Default: Published by confluence-publisher.

  • Markdown conversion. Renders CommonMark via markdown-it-py,

then post-processes to storage XHTML. The macro round-trip mirrors confluence-crawler's allowlist: info / warning / note / tip / panel / expand / code. Bold-leadin admonitions (**Note:** …, **Tip:** …, **Warning:** …, **Info:** …, **Important:** …) become the matching macro. Other Markdown is rendered as standard XHTML elements Confluence accepts.

  • Attachments. --attach uploads each file as a page attachment.

After upload, Markdown image references in the input whose target filename matches an attached filename are rewritten to ``. Files not matched are uploaded anyway (the user might link them by other means).

  • Labels. Applied after the page write; failure to apply labels is

reported but does not roll back the page write.

  • Mermaid / PlantUML. Out of scope. Run the mermaid-renderer

skill first to pre-render fenced `mermaid blocks to PNGs, then pass those PNGs via --attach to this skill.

Don't

  • Don't read ~/.agentbundle/credentials.env from skill body.
  • Don't print or log the token.
  • Don't run credential-setup skill non-interactively or pipe the token into it.
  • Don't write your own REST calls to Confluence — extend the scripts

and surface the gap to the user if a flag is missing.

  • Don't auto-resolve a title collision by appending suffixes — surface

the ambiguity (the script does this) and ask which page to update.

  • Don't assume --insecure is safe to add by default; only when the

user explicitly accepts it.

  • Don't pass --force to bypass a 409 — there is no such flag.

Concurrent edits need human attention.

Edge cases

  • Page moved between spaces between when the user got the URL and

when you publish: the page ID still resolves; the publish targets the page in its current space.

  • Title collision in lookup mode: if GET /rest/api/content?spaceKey=X&title=Y

returns more than one result (rare but possible across page states), the script exits 2 with the list of IDs. Ask the user which to target via --page-id.

  • Frontmatter without confluence_id: the script exits 2 and asks

for one of the other identification flags.

  • Storage-format input with invalid XHTML: the API returns 400;

the script surfaces the error message. Don't try to fix it client-side — ask the user.

  • Network failure mid-publish. Reads (the version probe) are

retried by the client. Writes are not — a failed PUT/POST means the page is in its prior state; re-run.

  • Large pages. Confluence soft-caps storage at ~5 MB. Beyond that,

break the content into linked sub-pages; this skill doesn't do that for you.

Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet, be the first.

Versions

  • v0.1.0 Imported from the upstream source.