Skip to main content

Agent-first CLI to publish Markdown to Confluence

Project description

confpub

Agent-first CLI to publish Markdown to Confluence.

Publish one file or an entire documentation tree — from the terminal, a CI pipeline, or an LLM agent. Every command returns structured JSON. Every error has a stable code. One call to confpub guide gives an agent everything it needs to drive the tool zero-shot.

Installation

Run directly with uvx (no install needed):

uvx confpub-cli --help

Or install permanently:

uv tool install confpub-cli   # recommended
pip install confpub-cli        # alternative

Once installed, the command is available as both confpub and confpub-cli.


Quick Start

Publish a single file

export CONFPUB_URL=https://yourorg.atlassian.net/wiki
export CONFPUB_TOKEN=your-api-token
export CONFPUB_USER=you@example.com

confpub page publish README.md --space DEV --parent "Engineering"

Publish a documentation tree

Create a confpub.yaml manifest:

schema_version: "1.0"
space: DEV
parent: "Engineering"

pages:
  - title: "Architecture Overview"
    file: docs/architecture.md
    assets:
      - docs/diagrams/*.png
    children:
      - title: "API Reference"
        file: docs/api.md
      - title: "Deployment Guide"
        file: docs/deploy.md

Then run the transactional workflow:

confpub plan create   --manifest confpub.yaml        # Plan (no writes)
confpub plan validate --plan confpub-plan.json       # Check for drift
confpub plan apply    --plan confpub-plan.json       # Apply to Confluence
confpub plan verify   --assertions verify.json       # Assert post-conditions

Or preview first with --dry-run:

confpub plan apply --plan confpub-plan.json --dry-run

Features

  • Structured JSON output — every command returns the same envelope shape on stdout
  • Transactional workflow — plan → validate → apply → verify with fingerprint-based conflict detection
  • Markdown → Confluence — code blocks become code macros, > [!NOTE] becomes Info panels, tables stay tables, task lists, math, definition lists, footnotes, panels, expand/collapse, page layouts, and {macro} syntax for Status, TOC, Jira, Anchor, Children, and more
  • Asset handling — images are uploaded as attachments and URLs are rewritten automatically; JS/CSS files in ::: html blocks are auto-discovered and uploaded
  • Idempotent — a lockfile tracks page IDs so re-publishing updates in place
  • Full page lifecycle — publish, pull, move, delete, export (PDF/Word), version history, labels, comments, and page properties
  • Installable skillconfpub skill install drops a publishing skill into Claude Code, GitHub Copilot, Cursor, Windsurf, or AGENTS.md — with 14 document templates and full syntax references
  • Agent-readyconfpub guide returns the full CLI schema; LLM=true suppresses interactive behavior
  • Cloud + Server — works with Confluence Cloud (*.atlassian.net) and Server/Data Center

Commands

All commands follow a noun verb pattern. Verbs telegraph mutation intent.

Command Mutates Description
confpub guide No Machine-readable CLI schema
confpub search No Search Confluence content using CQL
Page
confpub page list No List pages in a space
confpub page inspect No Detailed view of one page
confpub page publish Yes Publish a single Markdown file
confpub page pull No Pull Confluence pages to local Markdown
confpub page delete Yes Delete a page (supports --cascade)
confpub page move Yes Move a page under a new parent
confpub page history No Show version history of a page
confpub page version No Get a specific page version
confpub page export No Export a page as PDF or Word
Space
confpub space list No List accessible spaces
confpub space inspect No Detailed view of one space
Attachment
confpub attachment list No List attachments on a page
confpub attachment upload Yes Upload a file as an attachment
confpub attachment download No Download an attachment
confpub attachment delete Yes Delete an attachment
Label
confpub label list No List labels on a page
confpub label add Yes Add labels to a page
confpub label remove Yes Remove labels from a page
Comment
confpub comment list No List comments on a page
confpub comment add Yes Add a comment to a page
Property
confpub property list No List all properties on a page
confpub property get No Get a single page property
confpub property set Yes Set a page property (create or update)
confpub property delete Yes Delete a page property
Plan
confpub plan create No Generate a plan artifact from a manifest
confpub plan validate No Check a plan against current state
confpub plan apply Yes Execute a plan (supports --dry-run)
confpub plan verify No Assert post-conditions hold
Skill
confpub skill install Yes Install confpub skill into coding agents
confpub skill inspect No Detect agents and show skill status
Config / Auth
confpub auth inspect No Show credential status
confpub config set Yes Write a config value
confpub config inspect No Show current config

Structured Envelope

Every command — success or failure — returns this exact shape on stdout:

{
  "schema_version": "1.0",
  "request_id": "req_20260228_143000_7f3a",
  "ok": true,
  "command": "page.publish",
  "target": {
    "space": "DEV",
    "title": "Architecture Overview"
  },
  "result": { "..." : "..." },
  "warnings": [],
  "errors": [],
  "metrics": {
    "duration_ms": 842
  }
}

On failure, ok is false, result is null, and errors contains structured error objects:

{
  "ok": false,
  "errors": [
    {
      "code": "ERR_CONFLICT_FINGERPRINT",
      "message": "Page was modified externally since plan was created",
      "retryable": false,
      "suggested_action": "fix_input",
      "details": {
        "page_id": "123456",
        "plan_fingerprint": "sha256:abc123",
        "current_fingerprint": "sha256:def456"
      }
    }
  ]
}

Invariants:

  • stdout is exclusively JSON — one object, no preamble, no epilogue
  • errors and warnings are always arrays (possibly empty)
  • result is always present (null on failure)
  • stderr gets progress events, diagnostics, and debug logs

Pulling Pages

Pull Confluence pages back to local Markdown files:

# Pull a single page by title
confpub page pull --space DEV --title "Architecture Overview" --output docs/

# Pull a single page by ID
confpub page pull --page-id 123456 --output docs/

# Pull a page and all its children recursively
confpub page pull --space DEV --title "Engineering" --recursive --output docs/

# Generate a manifest even for a single page
confpub page pull --page-id 123456 --manifest --output docs/

Pull flags

Flag Description
--space Confluence space key
--title Page title
--page-id Confluence page ID (alternative to --space + --title)
--output / -o Output directory (default: .)
--recursive / -r Pull child pages recursively
--force Overwrite existing local files
--layout flat (default) or nested directory structure
--no-attachments Skip downloading attachments
--manifest Generate confpub.yaml manifest

Recursive pulls automatically generate a confpub.yaml manifest and a confpub.lock lockfile for round-tripping back to Confluence.


Searching

Search Confluence content using CQL (Confluence Query Language):

# Search by CQL
confpub search --cql 'label = "api-docs"'

# Filter by space and type
confpub search --space DEV --type page --limit 10

# Combine CQL with filters
confpub search --space DEV --cql 'title ~ "deploy"'

Exit Codes

Code Meaning Action
0 Success
10 Validation error Fix input, do not retry
20 Auth / permission Re-authenticate or escalate
40 Conflict Re-plan, do not blindly retry
50 I/O error Retry with backoff
90 Internal error File a bug

Error Codes

Stable across versions. An agent can branch on these without parsing messages.

ERR_VALIDATION_REQUIRED          Missing required argument
ERR_VALIDATION_MANIFEST          Manifest fails schema validation
ERR_VALIDATION_MARKDOWN          Unparseable Markdown
ERR_VALIDATION_ASSET_MISSING     Referenced image not found on disk
ERR_VALIDATION_NOT_FOUND         Page or resource not found
ERR_VALIDATION_SPACE_MISMATCH    Space key mismatch between manifest and target
ERR_VALIDATION_LABEL             Invalid label format
ERR_VALIDATION_SPACE_KEY         Space key looks like expanded shell path

ERR_AUTH_REQUIRED                No credentials configured
ERR_AUTH_EXPIRED                 Token has expired
ERR_AUTH_FORBIDDEN               Lacks permission to write

ERR_CONFLICT_FINGERPRINT         Page changed since plan was created
ERR_CONFLICT_LOCK                Another confpub process holds the lock
ERR_CONFLICT_PAGE_EXISTS         Title exists with unexpected ID
ERR_CONFLICT_FILE_EXISTS         Local file already exists (pull)

ERR_IO_FILE_NOT_FOUND            Source file missing
ERR_IO_CONNECTION                Confluence unreachable
ERR_IO_TIMEOUT                   Request timed out

ERR_INTERNAL_CONVERTER           Markdown → Confluence conversion crashed
ERR_INTERNAL_REVERSE_CONVERTER   Confluence → Markdown conversion crashed
ERR_INTERNAL_SDK                 Unexpected API response

Authentication

Credentials are resolved in this order (highest precedence first):

CLI flags     →  --token / --user
Env vars      →  CONFPUB_TOKEN / CONFPUB_USER / CONFPUB_URL
Config file   →  ~/.config/confpub/config.json
OS keychain   →  via keyring

Cloud vs Server is auto-detected from the URL: *.atlassian.net uses token + email auth; everything else uses PAT.

# Check current auth status
confpub auth inspect

# Set config values
confpub config set base_url https://yourorg.atlassian.net/wiki
confpub config set user you@example.com

When LLM=true or stdin is non-interactive, confpub never prompts — it returns a structured ERR_AUTH_REQUIRED error instead.


Markdown Conversion

confpub converts Markdown to Confluence Storage Format (and back via page pull):

Markdown Confluence Output
# Heading <h1>Heading</h1>
**bold** <strong>bold</strong>
`code` <code>code</code>
Fenced code block <ac:structured-macro ac:name="code"> with language param
> [!NOTE] Confluence Info macro
> [!WARNING] Confluence Warning macro
> [!TIP] Confluence Tip macro
![img](photo.png) Upload attachment + <ac:image> reference
Tables Standard XHTML <table>
~~strikethrough~~ <del>strikethrough</del>
- [ ] task / - [x] done <ac:task-list> with task status
$E=mc^2$ LaTeX math macro (inline)
$$...$$ LaTeX math macro (block)
Term + : Definition <dl><dt><dd> definition list
[^1] footnotes Superscript links + numbered list
::: panel Title Confluence Panel macro
::: expand Title Confluence Expand macro
:::: layout two-equal Confluence page layout
---yaml--- front matter Silently stripped
{status:Done|colour=Green} Confluence Status lozenge
{toc} Table of Contents macro
{anchor:name} Anchor macro
{children} Children Display macro
{jira:PROJECT-123} Jira issue link/table
{recently-updated} Recently Updated macro
{excerpt-include:Page} Excerpt Include macro
{include:Page} Include Page macro
::: excerpt Excerpt macro (body)
::: html HTML macro (preserves <style>, <script>, <iframe>)

HTML Macro

Confluence strips <style>, <script>, <iframe>, and other tags from normal page content. The HTML macro is the only way to embed arbitrary HTML. confpub supports it via ::: html fenced blocks:

::: html
<style>
  .card { border: 2px solid #0052CC; border-radius: 8px; padding: 16px; }
  .card h3 { color: #0052CC; margin-top: 0; }
</style>
<div class="card">
  <h3>Custom Styled Card</h3>
  <p>This HTML is preserved verbatim by the HTML macro.</p>
</div>
:::

The macro name is auto-detected: html-macro for Confluence Cloud, html for Data Center/Server. Override with --html-macro-name or html_macro_name in front-matter.

Interactive JavaScript Applications

::: html blocks can reference external JavaScript and CSS files. confpub automatically:

  1. Discovers <script src="..."> and <link href="..."> references inside ::: html blocks
  2. Uploads the referenced files as page attachments
  3. Rewrites the URLs in the published HTML to point to the Confluence attachment download path
::: html
<div id="dashboard"></div>
<script src="app.js"></script>
:::

Place app.js next to the .md file. This pattern works for any single-file JavaScript application — bundle your TypeScript, React, or Vue app into a single .js file and reference it from a ::: html block.

Missing files produce warnings (not errors), so partial publishes still succeed.


Manifest Format

schema_version: "1.0"
space: DEV
parent: "Architecture Notes"

confluence:
  base_url: https://yourorg.atlassian.net/wiki
  auth:
    type: token  # Credentials via CONFPUB_TOKEN + CONFPUB_USER

conflict_strategy: fail     # fail | overwrite | skip
on_removal: leave           # leave | delete
version_comment: "Published by confpub @ {timestamp}"

labels:
  - architecture
  - auto-published

assertions:
  - type: page.exists
    title: "Overview"
  - type: page.parent
    title: "Components"
    expected_parent: "Overview"

pages:
  - title: "Overview"
    file: overview.md

  - title: "Component Design"
    file: components/design.md
    assets:
      - components/diagrams/*.png
    children:
      - title: "API Reference"
        file: components/api.md

Lockfile

After the first successful apply, confpub writes confpub.lock alongside the manifest. Commit this to version control — it maps page titles to Confluence page IDs for idempotent re-publishing.

{
  "schema_version": "1.0",
  "last_updated": "2026-02-28T14:35:00Z",
  "pages": {
    "Overview":          { "page_id": "123456", "version": 5 },
    "Component Design":  { "page_id": "123457", "version": 1 },
    "API Reference":     { "page_id": "123458", "version": 1 }
  }
}

Agent Integration

An LLM agent can drive confpub entirely from one bootstrap call:

# Step 1: Learn the CLI
confpub guide

# Step 2: Check credentials
confpub auth inspect

# Step 3: Explore
confpub space list
confpub page list --space DEV

# Step 4: Publish
confpub page publish doc.md --space DEV --parent "Docs" --dry-run
confpub page publish doc.md --space DEV --parent "Docs"

The guide command returns the complete schema — all commands with flags, all error codes with exit codes and retry hints, auth precedence, and concurrency rules:

confpub guide                          # Full schema
confpub guide --section auth           # Just auth info
confpub guide --section error_codes    # Just error codes
confpub guide --section commands       # Just commands

Environment variables for agents

Variable Effect
LLM=true Suppress interactive prompts; return structured errors instead
CONFPUB_TOKEN API token
CONFPUB_USER Email / username
CONFPUB_URL Confluence base URL
CONFPUB_SPACE Default space key
CONFPUB_SSL_VERIFY SSL verification (true/false or CA bundle path)

Skills

confpub ships an installable skill that teaches coding agents how to write professional Confluence pages — including extended syntax, design principles, and 14 ready-to-use document templates.

Installing the skill

# Auto-detect agents in the current repo and install
confpub skill install

# Install for specific agents
confpub skill install --agent claude --agent copilot

# Preview without writing files
confpub skill install --dry-run

# Check which agents are detected
confpub skill inspect

Supported agents

Agent Detection
Claude Code .claude/ directory or CLAUDE.md
GitHub Copilot .github/copilot-instructions.md
Cursor .cursor/rules/ or .cursorrules
Windsurf .windsurfrules or .windsurf/
AGENTS.md AGENTS.md in repo root

What the skill includes

The skill installs a main SKILL.md with a syntax cheat sheet and design philosophy, plus a references/ directory with detailed guides and document templates:

Syntax and design references — containers (panels, expand, admonitions, excerpts), macros (status, TOC, children, Jira, anchors, includes), formatting (code blocks, math, footnotes, tasks), HTML macro, layouts, design principles, design styling, page management, and publishing workflow.

Document templates — ADR, API docs, change request, design doc, meeting notes, onboarding guide, post-mortem, RAID log, release notes, retrospective, RFC, runbook, service catalog, and sprint status.

Once installed, agents can create polished Confluence pages that use the full range of confpub's extended Markdown syntax — panels, status lozenges, layouts, macros, and more — without the user needing to explain the syntax.


Development

# Clone and install with dev dependencies
git clone https://github.com/ThomasRohde/confpub-cli.git
cd confpub-cli
uv pip install -e ".[dev]"

# Run tests
pytest tests/ -v

# Run with coverage
pytest tests/ -v --cov=confpub

Releasing

Version is defined in confpub/__init__.py. Use uvx hatch to bump it:

uvx hatch version patch    # 0.2.1 → 0.2.2
uvx hatch version minor    # 0.2.1 → 0.3.0
uvx hatch version major    # 0.2.1 → 1.0.0

Then commit and push to main — GitHub Actions will publish to PyPI automatically.

Project Structure

confpub/
├── cli.py                # Typer app, commands, envelope wrapping
├── envelope.py           # Pydantic envelope model
├── errors.py             # Error codes, exit codes, ConfpubError
├── output.py             # TOON / LLM=true / isatty logic
├── config.py             # Credential precedence
├── confluence.py         # atlassian-python-api wrapper
├── converter.py          # Markdown → Confluence Storage Format
├── reverse_converter.py  # Confluence Storage Format → Markdown
├── manifest.py           # Manifest + plan artifact models
├── lockfile.py           # confpub.lock persistence
├── front_matter.py       # YAML front-matter parsing
├── html_macro_plugin.py  # ::: html block parser plugin
├── macro_plugin.py       # {macro} inline syntax parser
├── assets.py             # Asset discovery, upload, URL rewriting
├── planner.py            # plan.create
├── validator.py          # plan.validate
├── applier.py            # plan.apply
├── verifier.py           # plan.verify
├── publish.py            # page.publish shortcut
├── puller.py             # page.pull workflow
├── guide.py              # Machine-readable CLI schema
├── skill_installer.py    # Skill installation logic
└── skill_data/           # Skill content (SKILL.md + references/)

Technology Stack

Concern Choice
CLI framework Typer
Confluence API atlassian-python-api
Markdown parsing markdown-it-py
HTML → Markdown markdownify + BeautifulSoup4
Validation Pydantic v2
JSON serialization orjson
Credentials keyring + env vars

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

confpub_cli-1.14.1.tar.gz (256.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

confpub_cli-1.14.1-py3-none-any.whl (126.4 kB view details)

Uploaded Python 3

File details

Details for the file confpub_cli-1.14.1.tar.gz.

File metadata

  • Download URL: confpub_cli-1.14.1.tar.gz
  • Upload date:
  • Size: 256.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for confpub_cli-1.14.1.tar.gz
Algorithm Hash digest
SHA256 ed789087c3e3c65be955fa744f80f00ad7865aadc79ff1345947aeb1056cdfe4
MD5 0140dfeb72b22a854f2103b82d97a01e
BLAKE2b-256 d43ae77396cbb967e64b2b14088e20283c94af74c991c5ad36190b994dab05cb

See more details on using hashes here.

Provenance

The following attestation bundles were made for confpub_cli-1.14.1.tar.gz:

Publisher: publish.yml on ThomasRohde/confpub-cli

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file confpub_cli-1.14.1-py3-none-any.whl.

File metadata

  • Download URL: confpub_cli-1.14.1-py3-none-any.whl
  • Upload date:
  • Size: 126.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for confpub_cli-1.14.1-py3-none-any.whl
Algorithm Hash digest
SHA256 faee40bbbe2b4dc9a97d85c6ad2d530fd3c0df06d8177cc10fad26844baa2eca
MD5 6bd8e27bc2b199d828748fef252d8bd7
BLAKE2b-256 b1792c892483926ae918e73fb5018cdabe04e7f70020315d3ef859871f3d3a0e

See more details on using hashes here.

Provenance

The following attestation bundles were made for confpub_cli-1.14.1-py3-none-any.whl:

Publisher: publish.yml on ThomasRohde/confpub-cli

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page