Claude
Skills
Sign in
Back

confluence-docs

Included with Lifetime
$97 forever

Atlassian Confluence integration for enterprise documentation. Create and update pages via API, manage spaces and permissions, handle content migration, and sync between Markdown and Confluence.

Backend & APIs

What this skill does


# Confluence Integration Skill

Atlassian Confluence integration for enterprise documentation.

## Capabilities

- Page creation and updates via API
- Space management and permissions
- Macro and template management
- Content migration (Markdown to Confluence)
- Attachment handling
- Label and metadata management
- Confluence Cloud and Server support
- Confluence-to-Markdown export

## Usage

Invoke this skill when you need to:
- Sync documentation to Confluence
- Migrate content between formats
- Manage Confluence spaces programmatically
- Automate page updates from CI/CD
- Export Confluence to Markdown

## Inputs

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| action | string | Yes | create, update, migrate, export |
| baseUrl | string | Yes | Confluence instance URL |
| spaceKey | string | Yes | Target space key |
| sourcePath | string | No | Source Markdown files |
| pageId | string | No | Specific page ID for updates |
| parentPageId | string | No | Parent page for hierarchy |

### Input Example

```json
{
  "action": "migrate",
  "baseUrl": "https://company.atlassian.net/wiki",
  "spaceKey": "DOCS",
  "sourcePath": "./docs",
  "parentPageId": "123456"
}
```

## Configuration

### confluence.config.json

```json
{
  "baseUrl": "https://company.atlassian.net/wiki",
  "auth": {
    "type": "token",
    "email": "${CONFLUENCE_EMAIL}",
    "token": "${CONFLUENCE_TOKEN}"
  },
  "space": {
    "key": "DOCS",
    "name": "Documentation"
  },
  "migration": {
    "preserveStructure": true,
    "convertTables": true,
    "uploadImages": true,
    "macroMapping": {
      "note": "info",
      "warning": "warning",
      "code": "code"
    }
  },
  "sync": {
    "dryRun": false,
    "updateExisting": true,
    "createMissing": true,
    "archiveRemoved": false
  }
}
```

## API Integration

### Confluence REST API Client

```javascript
const ConfluenceClient = require('confluence-api');

class ConfluenceManager {
  constructor(config) {
    this.client = new ConfluenceClient({
      username: config.email,
      password: config.token,
      baseUrl: config.baseUrl
    });
  }

  // Create a new page
  async createPage(spaceKey, title, content, parentId = null) {
    const page = {
      type: 'page',
      title,
      space: { key: spaceKey },
      body: {
        storage: {
          value: content,
          representation: 'storage'
        }
      }
    };

    if (parentId) {
      page.ancestors = [{ id: parentId }];
    }

    return await this.client.postContent(page);
  }

  // Update existing page
  async updatePage(pageId, title, content, version) {
    const page = {
      id: pageId,
      type: 'page',
      title,
      version: { number: version + 1 },
      body: {
        storage: {
          value: content,
          representation: 'storage'
        }
      }
    };

    return await this.client.putContent(page);
  }

  // Get page by title
  async getPageByTitle(spaceKey, title) {
    const result = await this.client.getContentBySpaceKey(spaceKey, {
      title,
      expand: 'version,body.storage'
    });
    return result.results[0] || null;
  }

  // Upload attachment
  async uploadAttachment(pageId, filePath, comment = '') {
    const form = new FormData();
    form.append('file', fs.createReadStream(filePath));
    form.append('comment', comment);

    return await this.client.createAttachment(pageId, form);
  }

  // Add labels
  async addLabels(pageId, labels) {
    const labelPayload = labels.map(name => ({
      prefix: 'global',
      name
    }));

    return await this.client.postLabels(pageId, labelPayload);
  }
}
```

## Markdown to Confluence Conversion

### Converter

```javascript
const marked = require('marked');

class MarkdownToConfluence {
  constructor(options = {}) {
    this.options = options;
    this.attachments = [];
  }

  convert(markdown, metadata = {}) {
    // Parse front matter
    const { content, frontMatter } = this.parseFrontMatter(markdown);

    // Convert markdown to HTML
    let html = marked.parse(content);

    // Convert to Confluence storage format
    html = this.convertToStorageFormat(html);

    // Handle macros
    html = this.convertMacros(html);

    // Handle code blocks
    html = this.convertCodeBlocks(html);

    // Handle images
    html = this.convertImages(html);

    // Handle tables
    html = this.convertTables(html);

    return {
      title: frontMatter.title || metadata.title,
      content: html,
      labels: frontMatter.tags || [],
      attachments: this.attachments
    };
  }

  convertMacros(html) {
    // Convert admonitions to Confluence macros
    const macroMap = {
      'note': 'info',
      'warning': 'warning',
      'tip': 'tip',
      'danger': 'warning'
    };

    for (const [mdType, confType] of Object.entries(macroMap)) {
      const regex = new RegExp(`<div class="${mdType}">([\\s\\S]*?)</div>`, 'g');
      html = html.replace(regex, (match, content) => {
        return `<ac:structured-macro ac:name="${confType}">
          <ac:rich-text-body>${content}</ac:rich-text-body>
        </ac:structured-macro>`;
      });
    }

    return html;
  }

  convertCodeBlocks(html) {
    // Convert code blocks to Confluence code macro
    return html.replace(
      /<pre><code class="language-(\w+)">([\s\S]*?)<\/code><\/pre>/g,
      (match, language, code) => {
        const decodedCode = this.decodeHtml(code);
        return `<ac:structured-macro ac:name="code">
          <ac:parameter ac:name="language">${language}</ac:parameter>
          <ac:plain-text-body><![CDATA[${decodedCode}]]></ac:plain-text-body>
        </ac:structured-macro>`;
      }
    );
  }

  convertImages(html) {
    // Convert images to Confluence attachments
    return html.replace(
      /<img src="([^"]+)" alt="([^"]*)"[^>]*>/g,
      (match, src, alt) => {
        if (src.startsWith('http')) {
          // External image
          return `<ac:image><ri:url ri:value="${src}" /></ac:image>`;
        } else {
          // Local attachment
          const filename = path.basename(src);
          this.attachments.push({ src, filename });
          return `<ac:image><ri:attachment ri:filename="${filename}" /></ac:image>`;
        }
      }
    );
  }

  convertTables(html) {
    // Confluence uses standard HTML tables but needs specific attributes
    return html.replace(/<table>/g, '<table class="wrapped">');
  }
}
```

## Confluence to Markdown Export

### Exporter

```javascript
class ConfluenceToMarkdown {
  constructor(client) {
    this.client = client;
  }

  async exportSpace(spaceKey, outputDir) {
    const pages = await this.getAllPages(spaceKey);
    const structure = this.buildHierarchy(pages);

    for (const page of pages) {
      const markdown = await this.exportPage(page);
      const filePath = this.getFilePath(page, structure, outputDir);

      await fs.mkdir(path.dirname(filePath), { recursive: true });
      await fs.writeFile(filePath, markdown);
    }

    return { exported: pages.length };
  }

  async exportPage(page) {
    const content = page.body.storage.value;

    // Convert Confluence storage format to Markdown
    let markdown = this.convertToMarkdown(content);

    // Add front matter
    const frontMatter = {
      title: page.title,
      confluence_id: page.id,
      last_modified: page.version.when
    };

    return `---
${yaml.stringify(frontMatter)}---

${markdown}`;
  }

  convertToMarkdown(storage) {
    let md = storage;

    // Convert code macro
    md = md.replace(
      /<ac:structured-macro ac:name="code"[^>]*>[\s\S]*?<ac:parameter ac:name="language">(\w+)<\/ac:parameter>[\s\S]*?<ac:plain-text-body><!\[CDATA\[([\s\S]*?)\]\]><\/ac:plain-text-body>[\s\S]*?<\/ac:structured-macro>/g,
      (match, lang, code) => `\`\`\`${lang}\n${code}\n\`\`\``
    );

    // Convert info macro
    md = md.replace(
      /<ac:structured-macro ac:name="(info|warning|tip)"[^>]*>[\s\S]*?<ac:rich-text-body>([\s\S]*?)<\

Related in Backend & APIs