Skip to content

Sync

Bidirectional synchronization between local markdown files and Confluence pages.

  • Authenticated profile with Confluence access (atlcli auth login)
  • Space permission: View for pull, Edit for push operations
  • Initialized sync directory (atlcli wiki docs init)

atlcli provides powerful sync capabilities:

  • Watch: Real-time bidirectional sync with file watching and remote polling
  • Pull: Download Confluence pages as markdown
  • Push: Upload local changes to Confluence
  • Conflict resolution: Three-way merge with automatic and manual resolution

Set up a directory for sync:

Terminal window
atlcli wiki docs init ./docs --space TEAM

Options:

FlagDescription
--spaceSpace key (required)
--page-idSync single page by ID
--ancestorSync tree under specific page
Terminal window
# Sync entire space
atlcli wiki docs init ./docs --space TEAM
# Sync specific page tree
atlcli wiki docs init ./docs --ancestor 12345
# Sync single page only
atlcli wiki docs init ./docs --page-id 12345

The most powerful sync mode - automatically syncs changes in real-time using file watching and remote polling:

Terminal window
atlcli wiki docs sync ./docs

Watch mode combines two mechanisms for bidirectional sync:

  1. Local file watching: Detects changes to local markdown files instantly
  2. Remote polling: Periodically checks Confluence for remote changes
sequenceDiagram
participant L as Local Files
participant A as atlcli sync
participant C as Confluence
Note over L,C: Bidirectional Sync Flow
L->>A: File changed (watched)
A->>C: Push to remote
C-->>A: OK
loop Every 30s (configurable)
A->>C: Poll for changes
C-->>A: Changed pages
end
A->>L: Pull if changed
Terminal window
atlcli wiki docs sync <dir> [options]
FlagDescription
--space <key>Sync entire space
--ancestor <id>Sync page tree under parent ID
--page-id <id>Sync single page
--poll-interval <ms>Remote polling interval (default: 30000)
--no-pollDisable remote polling (local watch only)
--no-watchDisable file watching (poll only)
--on-conflict <mode>Conflict handling: merge, local, remote
--auto-createAuto-create pages for new local files
--label <label>Only sync pages with this label
--dry-runPreview changes without syncing
--jsonJSON line output for scripting

The poller periodically checks Confluence for changes. Efficiency depends on sync scope:

ScopeAPI CallsBest For
--page-idMinimal (1 page)Editing single document
--ancestorModerate (tree)Working on a section
--spaceHigher (all pages)Full documentation sync
Terminal window
# Poll single page (most efficient)
atlcli wiki docs sync ./docs --page-id 12345
# Poll page tree
atlcli wiki docs sync ./docs --ancestor 12345
# Poll entire space
atlcli wiki docs sync ./docs --space TEAM

The poller detects three types of remote changes:

EventDescriptionAction
createdNew page added in scopePull to local
changedPage content or title updatedPull or merge
deletedPage removed from scopeNotify user
Terminal window
# Fast polling (every 10 seconds) - more API calls
atlcli wiki docs sync ./docs --poll-interval 10000
# Slow polling (every 2 minutes) - fewer API calls
atlcli wiki docs sync ./docs --poll-interval 120000
# Disable remote polling entirely
atlcli wiki docs sync ./docs --no-poll

Note: Lower intervals mean faster remote change detection but more API requests. The default (30 seconds) balances responsiveness with API usage.

For instant remote change detection without polling overhead, use webhooks:

Terminal window
atlcli wiki docs sync ./docs \
--webhook-port 8080 \
--webhook-url https://your-server.com:8080/webhook
FlagDescription
--webhook-portLocal webhook server port
--webhook-urlPublic URL to register with Confluence

Webhook Events:

The webhook server handles these Confluence events:

EventDescription
page_createdNew page created
page_updatedPage content changed
page_removedPage deleted
page_trashedPage moved to trash
page_restoredPage restored from trash
page_movedPage moved to different parent

Webhooks vs Polling:

FeaturePollingWebhooks
Latency0-30s (configurable)Instant
SetupNoneRequires public URL
API usageRegular requestsOn-demand only
ReliabilityAlways worksRequires connectivity
FilteringBy scopeBy page ID and space

For local development, use polling. For production servers with public URLs, webhooks provide better performance.

Create Confluence pages automatically for new local files:

Terminal window
atlcli wiki docs sync ./docs --space TEAM --auto-create

Auto-create triggers in two scenarios:

  1. During initial sync: atlcli creates untracked local files (no frontmatter ID) as new pages
  2. During file watching: New files added while sync is running are automatically created

How it works:

AspectBehavior
TitleFrom frontmatter title, or filename converted to title case
ParentSpace home page (for --space) or ancestor (for --ancestor)
ContentFull markdown content converted to Confluence storage format

Example workflow:

Terminal window
# Start sync with auto-create
atlcli wiki docs sync ./docs --space TEAM --auto-create
# In another terminal, create a new file
echo "# My New Feature" > ./docs/my-new-feature.md
# Sync automatically:
# 1. Detects new file
# 2. Creates page "My New Feature" in Confluence
# 3. Updates local file with frontmatter containing page ID

After auto-create, the file is updated with frontmatter:

---
atlcli:
id: "12345678"
title: "My New Feature"
---
# My New Feature

When sync runs, atlcli creates a lock file at .atlcli/.sync.lock. This:

  • Prevents concurrent sync operations
  • Signals to other tools that sync is active
  • Is automatically removed on clean shutdown

Download pages from Confluence to local markdown files:

Terminal window
atlcli wiki docs pull ./docs

Options:

FlagDescription
--spaceFilter by space key
--page-idPull specific page
--ancestorPull page tree
--labelFilter by label
--forceOverwrite local modifications
--commentsExport comments to sidecar files
--no-attachmentsSkip downloading attachments
--limit <n>Maximum pages to pull
Terminal window
# Pull using saved scope from init
atlcli wiki docs pull ./docs
# Pull pages with specific label
atlcli wiki docs pull ./docs --label api-docs
# Pull with comments exported
atlcli wiki docs pull ./docs --comments
# Force pull (overwrite local changes)
atlcli wiki docs pull ./docs --force
# Pull without attachments
atlcli wiki docs pull ./docs --no-attachments

When using --comments, page comments are saved to sidecar files:

docs/
├── architecture.md
├── architecture.comments.json # Comments for architecture.md
└── api-reference.md

The comments file contains both footer comments and inline comments with their full thread hierarchy.

Upload local changes to Confluence:

Terminal window
atlcli wiki docs push ./docs

Options:

FlagDescription
--page-id <id>Push specific page by ID
--validateRun validation before push
--strictTreat warnings as errors
--jsonJSON output
Terminal window
# Push all tracked files
atlcli wiki docs push ./docs
# Push single file
atlcli wiki docs push ./docs/page.md
# Push by page ID
atlcli wiki docs push --page-id 12345
# Validate before pushing
atlcli wiki docs push ./docs --validate
# Strict validation (fail on warnings)
atlcli wiki docs push ./docs --validate --strict

Pre-push validation checks for:

CheckSeverityDescription
Broken linksErrorTarget file not found
Untracked pagesWarningLink to page without ID
Unclosed macrosError:::info without closing :::
Page sizeWarningContent exceeds 500KB
Terminal window
# Run validation separately
atlcli wiki docs check ./docs
# Strict mode (warnings are errors)
atlcli wiki docs check ./docs --strict
# JSON output for CI/scripts
atlcli wiki docs check ./docs --json

Add a new local file to Confluence tracking:

Terminal window
atlcli wiki docs add <file> [options]
FlagDescription
--title <t>Page title (default: from H1 or filename)
--parent <id>Parent page ID
--template <name>Use template for initial content
Terminal window
# Add file with auto-detected title
atlcli wiki docs add ./docs/new-page.md
# Add with specific parent
atlcli wiki docs add ./docs/guide.md --parent 12345
# Add using a template
atlcli wiki docs add ./docs/meeting.md --template meeting-notes

Compare local file with remote Confluence page:

Terminal window
atlcli wiki docs diff <file>

Shows unified diff with colored output:

  • Green: additions (local has, remote doesn’t)
  • Red: deletions (remote has, local doesn’t)
  • Cyan: file headers and hunk markers
Terminal window
# Compare single file
atlcli wiki docs diff ./docs/api-reference.md
# JSON output with statistics
atlcli wiki docs diff ./docs/api-reference.md --json

atlcli detects conflicts when:

  • Local file was modified since last sync
  • Remote page was modified since last sync
Conflict detected: docs/api.md
Local: Modified 2025-01-14 10:30 (version 5 → local changes)
Remote: Modified 2025-01-14 10:35 (version 5 → version 6)

Control how conflicts are handled in sync mode:

Terminal window
atlcli wiki docs sync ./docs --on-conflict <strategy>
StrategyBehavior
mergeAttempt three-way merge (default)
localKeep local version, overwrite remote
remoteKeep remote version, overwrite local

With --on-conflict merge, atlcli attempts automatic merging:

  1. Uses the common ancestor (base) version
  2. Computes diffs from both local and remote
  3. Merges non-conflicting changes
  4. Marks conflicting sections with markers

When merge cannot auto-resolve, conflict markers are inserted:

## API Reference
<<<<<<< LOCAL
This endpoint returns user data in JSON format.
=======
This endpoint returns user data. Response format is JSON.
>>>>>>> REMOTE
### Authentication
Terminal window
# Edit file to resolve conflicts manually
vim docs/api.md
# Then resolve with chosen strategy
atlcli wiki docs resolve docs/api.md --accept local
atlcli wiki docs resolve docs/api.md --accept remote
atlcli wiki docs resolve docs/api.md --accept merged # After manual edit

Check sync status of all tracked files:

Terminal window
atlcli wiki docs status ./docs

Output:

Sync status for ./docs:
synced: 12 files
local-modified: 2 files
remote-modified: 1 files
conflict: 1 files
untracked: 3 files
Last sync: 2025-01-14T10:30:00Z
Modified:
api-reference.md (local changes)
getting-started.md (remote changes)
Conflicts:
troubleshooting.md

Options:

FlagDescription
--jsonJSON output

Create .atlcliignore to exclude files from sync:

Terminal window
# .atlcliignore (gitignore syntax)
# Ignore drafts
drafts/
*.draft.md
# Ignore specific files
internal-notes.md
# Ignore by pattern
temp-*.md
# Negation (include despite earlier rule)
!important-draft.md

Default ignores (always excluded):

  • .atlcli/ - sync state directory
  • *.meta.json - legacy metadata files
  • *.base - base versions for merge
  • .git/ - git directory
  • node_modules/ - node dependencies

Note: Patterns from .gitignore are also respected and merged with .atlcliignore.

After init and pull:

docs/
├── .atlcli/
│ ├── config.json # Sync configuration
│ ├── state.json # Sync state (versions, timestamps)
│ ├── .sync.lock # Lock file (when sync running)
│ └── cache/ # Base versions for 3-way merge
│ └── 12345.md # Base content by page ID
├── getting-started.md
├── api-reference.md
├── api-reference.attachments/
│ └── diagram.png # Attachments for api-reference.md
├── guides/
│ ├── installation.md
│ └── configuration.md
├── .atlcliignore # Ignore patterns
└── .gitignore # Git ignore (also respected)

.atlcli/state.json tracks sync state per page:

{
"lastSync": "2025-01-14T10:00:00Z",
"pages": {
"12345": {
"path": "api-reference.md",
"title": "API Reference",
"version": 5,
"localHash": "abc123",
"remoteHash": "abc123",
"baseHash": "abc123",
"syncState": "synced",
"parentId": "12340",
"ancestors": ["12340", "12300"]
}
}
}

Local files use YAML frontmatter with the atlcli namespace:

---
atlcli:
id: "12345"
title: "API Reference"
---
# API Reference
Content here...

The frontmatter fields:

FieldRequiredDescription
idYesConfluence page ID (set automatically on pull/create)
titleNoPage title override (defaults to first H1 heading)

See File Format for details.

atlcli maps Confluence page hierarchy to directory structure:

Confluence: Local:
├── Parent Page ├── parent-page.md
│ ├── Child A ├── parent-page/
│ │ └── Grandchild │ ├── child-a.md
│ └── Child B │ ├── child-a/
│ │ └── grandchild.md
│ └── child-b.md

When pages move in Confluence, sync detects this and moves local files to match.

atlcli supports Confluence Cloud folders (introduced September 2024):

Confluence: Local:
├── My Folder (folder) ├── my-folder/
│ ├── Page A │ ├── index.md (type: folder)
│ └── Page B │ ├── page-a.md
│ └── page-b.md

atlcli represents folders as directories with an index.md file containing type: folder in frontmatter. See Folders for full details on folder support and limitations.

Most commands support --json for scripting:

Terminal window
atlcli wiki docs status ./docs --json
{
"schemaVersion": "1",
"dir": "./docs",
"stats": {
"synced": 12,
"localModified": 2,
"remoteModified": 1,
"conflict": 1,
"untracked": 3
},
"lastSync": "2025-01-14T10:00:00Z"
}

Sync command emits JSON lines (one event per line):

Terminal window
atlcli wiki docs sync ./docs --json
{"schemaVersion":"1","type":"status","message":"Starting initial sync..."}
{"schemaVersion":"1","type":"pull","file":"api-reference.md","pageId":"12345","message":"Pulled: API Reference"}
{"schemaVersion":"1","type":"push","file":"guide.md","pageId":"12346","message":"Pushed: Guide"}
  1. Use watch mode for real-time collaboration on documentation
  2. Use labels to sync subsets of pages (--label architecture)
  3. Enable webhooks for production servers with public URLs
  4. Commit to Git - track markdown files in Git for version history
  5. Use .atlcliignore for drafts and local-only content
  6. Run validation before pushing (--validate)

If sync fails due to lock:

Terminal window
# Check if sync is running
ps aux | grep "atlcli wiki docs sync"
# Force remove stale lock (only if no sync is active)
rm ./docs/.atlcli/.sync.lock

If you get stuck in merge conflicts:

Terminal window
# Reset to remote version
atlcli wiki docs pull ./docs --force
# Or force push local version
atlcli wiki docs resolve ./docs/file.md --accept local
atlcli wiki docs push ./docs
Error: You don't have permission to edit page 12345

Check your Confluence permissions for the space/page.

If you see 429 errors, increase poll interval:

Terminal window
atlcli wiki docs sync ./docs --poll-interval 60000
  • File Format - Frontmatter structure and directory conventions
  • Ignore Patterns - Exclude files from sync with .atlcliignore
  • Validation - Pre-push validation rules
  • Attachments - Sync images and file attachments
  • Webhooks - Real-time sync without polling
  • Pages - Direct page operations (create, move, delete)
Jira and Confluence are trademarks of Atlassian Corporation Plc. atlcli is not affiliated with, endorsed by, or sponsored by Atlassian.