A command-line tool that exports Jira Cloud issues into clean, portable Markdown files with all attachments.
Jarkdown bridges the gap between Jira and your local development environment. It makes it easy to archive tickets, share them with others, or work with issues offline.
Full documentation is available at jarkdown.readthedocs.io, including:
- Getting Started for Beginners - Step-by-step guide for new users
- Installation Guide
- Usage Guide
- Configuration
- Contributing
- Complete Export: Fetches all issue metadata, descriptions, comments, and downloads all attachments locally.
- Bulk Export: Export multiple issues concurrently with configurable parallelism (
--concurrency, default: 3). - JQL Query Export: Export issues matching JQL queries with
jarkdown query 'project = FOO AND status = Done'. - Rich Metadata: Exports comprehensive YAML frontmatter including project, status category, time tracking, votes, watches, issue links, due dates, and more.
- Custom Field Support: Type-aware rendering of Jira custom fields with configurable include/exclude filtering.
- ADF Parsing: Full Atlassian Document Format support for descriptions and comments (paragraphs, headings, lists, code blocks, tables, media, mentions, panels, and more).
- Async Processing: Built on
aiohttpfor concurrent API calls and efficient bulk operations. - Retry & Rate Limiting: Exponential backoff with jitter for transient errors (429, 503, 504) and
Retry-Afterheader support. - Preserves Formatting: Converts Jira's HTML to GitHub-flavored Markdown, keeping headings, lists, code blocks, and tables intact.
- Embeds Local Links: Automatically references downloaded attachments with local links in the Markdown file.
- Field Filtering: Include or exclude custom fields via CLI flags or
.jarkdown.tomlconfig file.
Install as an isolated tool with uv:
uv tool install jarkdownpipx install jarkdownpip install jarkdownAfter installation, run the setup wizard to configure your Jira credentials:
jarkdown setupOr create a .env file manually in the directory where you'll run jarkdown:
JIRA_DOMAIN=your-company.atlassian.net
JIRA_EMAIL=your-email@example.com
JIRA_API_TOKEN=your-api-token-here
- Go to https://id.atlassian.com/manage-profile/security/api-tokens
- Click "Create API token"
- Give it a name (e.g., "jarkdown")
- Copy the token and paste it into your
.envfile
If you want to contribute to the project or need the latest development version:
- Clone the repository:
git clone https://github.com/chrisbyboston/jarkdown.git
cd jarkdown- Install dependencies (uv creates and manages the virtual environment automatically):
uv sync --dev- Set up your
.envfile as described above
# Export a single issue (subcommand form)
jarkdown export PROJ-123
# Backward-compatible shorthand
jarkdown PROJ-123
# Export to a specific directory
jarkdown export PROJ-123 --output ~/Documents/jira-exports
# Enable verbose logging
jarkdown export PROJ-123 --verbose# Export multiple issues concurrently
jarkdown bulk PROJ-1 PROJ-2 PROJ-3
# Control concurrency and output location
jarkdown bulk PROJ-1 PROJ-2 PROJ-3 --concurrency 5 --output ~/exports
# Group into a named batch directory
jarkdown bulk PROJ-1 PROJ-2 --batch-name sprint-23# Export all issues matching a JQL query
jarkdown query 'project = FOO AND status = Done'
# Limit results (--limit and --max-results are equivalent)
jarkdown query 'project = FOO AND sprint in openSprints()' --limit 100
jarkdown query 'project = FOO AND sprint in openSprints()' --max-results 100
# With concurrency control
jarkdown query 'assignee = currentUser()' --concurrency 5# Include only specific custom fields
jarkdown export PROJ-123 --include-fields "Story Points,Sprint"
# Exclude specific custom fields
jarkdown export PROJ-123 --exclude-fields "Internal Notes,Dev Notes"Or configure in .jarkdown.toml:
[fields]
exclude = ["Internal Notes", "Dev Notes"]# Interactive setup wizard
jarkdown setup
# Show version
jarkdown --version
# Show help
jarkdown --help| Flag | Applies To | Default |
|---|---|---|
--output / -o |
all subcommands | Current working directory |
--concurrency |
bulk, query |
3 |
--max-results / --limit |
query |
50 |
--max-results |
bulk |
No limit (all supplied keys exported) |
--include-json |
all subcommands | Off (not saved) |
--verbose / -v |
all subcommands | Off |
--include-fields |
all subcommands | All custom fields included |
--exclude-fields |
all subcommands | No fields excluded |
--refresh-fields |
all subcommands | Off (cache used; auto-refreshes after 24 h) |
--batch-name |
bulk, query |
None (issues written directly to output dir) |
The tool creates a directory named after the issue key containing:
- A markdown file with the issue content
- All attachments downloaded from the issue
Single issue example:
PROJ-123/
├── PROJ-123.md # Issue content in markdown
├── diagram.png # Downloaded attachments
├── report.pdf
└── ...
Pass --include-json to also save the raw Jira API response:
PROJ-123/
├── PROJ-123.md
├── PROJ-123.json # Raw Jira API response (opt-in)
└── ...
Bulk/query export example:
output-dir/
├── index.md # Summary table of all exported issues
├── PROJ-1/
│ ├── PROJ-1.md
│ └── ...
├── PROJ-2/
│ ├── PROJ-2.md
│ └── ...
└── ...
The generated markdown includes:
---
key: PROJ-123
summary: Issue title
type: Bug
status: In Progress
status_category: In Progress
priority: High
resolution: null
project: My Project
project_key: PROJ
assignee: John Doe
reporter: Jane Smith
creator: Jane Smith
labels:
- backend
- performance
components:
- API
- Database
parent_key: PROJ-100
parent_summary: Parent epic title
affects_versions:
- '1.0'
fix_versions:
- '1.1'
created_at: '2025-01-15T10:30:00.000+0000'
updated_at: '2025-01-20T14:45:00.000+0000'
resolved_at: null
duedate: '2025-02-01'
original_estimate: 2d
time_spent: 1d 4h
remaining_estimate: 4h
progress: 60
aggregate_progress: 45
votes: 3
watches: 5
---- Issue title with link to Jira
- Description with preserved formatting
- Environment section
- Linked Issues grouped by relationship type
- Subtasks with status
- Worklogs table with author, time, and comments
- Custom Fields section (when present)
- Comments section with all comments (author, date, and content)
- Attachments section with all files
Images are embedded inline, other files are linked.
- Python 3.8+
- Jira Cloud instance
- Jira API token with read permissions
aiohttp- Async HTTP client for API callsmarkdownify- HTML to Markdown conversionpython-dotenv- Environment variable managementPyYAML- YAML frontmatter generationplatformdirs- XDG-compliant cache directory pathstomli- TOML config parsing (Python < 3.11; built-intomllibon 3.11+)
- Requires Jira Cloud (not Server/Data Center)
- Attachment downloads are sequential per issue
- Parallel attachment downloads within a single issue
- Incremental/delta export (only re-export changed issues)
- Alternative output formats (HTML, PDF)
- Hierarchical export (epics with all linked stories)
We welcome contributions! Here's how to get started:
- Fork and clone the repository:
git clone https://github.com/YOUR-USERNAME/jarkdown.git
cd jarkdown- Install dependencies (uv manages the virtual environment automatically):
uv sync --dev- Install pre-commit hooks:
pre-commit install- Create a feature branch:
git checkout -b feature/your-feature-name- Make your changes and ensure tests pass:
uv run pytest
uv run pytest --cov=src/jarkdown --cov-report=term-missing- Commit your changes (pre-commit hooks will run automatically):
git add .
git commit -m "feat: describe your change"- Push and create a pull request:
git push origin feature/your-feature-nameThen open a pull request on GitHub.
- We use
rufffor linting and formatting - Pre-commit hooks ensure code quality
- Write clear, descriptive commit messages
- Add tests for new functionality
- Update documentation as needed
Run the test suite:
# Run all tests
uv run pytest
# Run with coverage
uv run pytest --cov=src/jarkdown --cov-report=term-missing
# Run specific test file
uv run pytest tests/test_cli.pyPlease use the GitHub issue tracker to report bugs or request features.
This project is licensed under the MIT License - see the LICENSE file for details.