A Python application that synchronizes JIRA tickets to your local SQLite database and integrates with Things 3 for task management.
It is the spiritual successor to hackerdude/jiratotaskmanagers and it follows the same philosophy of the "one tasklist" system. By mapping active tickets to Things Today status it also keeps you sharp on updating your Jira tickets to their correct statuses.
My belief is that we should actively plan our day and what we want to do with our time. This app helps me to sync up Jira to my beloved Things todo app
- One-way sync: Fetch tickets from JIRA → Store in local database → Sync to Things 3
- Smart status mapping: Automatically schedule tasks in Things based on JIRA status
- Incremental updates: Only syncs changed tickets to minimize calls to Things
- Robust(ish) sync tracking: Tracks sync status per ticket ('synced', 'not synced', 'unknown')
- Flexible configuration: Configurable status mappings, tags, and projects
This application uses both xcall and pyThings to work. They are bundled in this repository under libraries. We also use the Python Jira module for auth.
- Clone the repository with submodules:
git clone git@github.com:laufwerkcode/jira2things.git
cd jira2things- Create a
.venv
python3 -m venv .venv
source .venv/bin/activate- Install dependencies:
pip install -r requirements.txt- Create your configuration file (see Configuration section below)
# Default: Update database from JIRA and sync unsynced tickets to Things
python main.py
# Update database only (fetch from JIRA, no Things sync)
python main.py --update-db
# Sync unsynced tickets to Things only (no JIRA fetch)
python main.py --sync-to-things
# Force resync ALL tickets to Things (regardless of sync status)
python main.py --resync-to-things
# Enable verbose logging
python main.py --verbose
# Use custom config file
python main.py --config my-config-
Copy the example config file:
cp config.example config
-
Edit the
configfile with your actual values (see sections below) -
Important: Never commit your
configfile to version control as it contains sensitive tokens!
The config file uses a simple KEY=VALUE format. Here are the available options:
JIRA_BASE_URL=https://your-company.atlassian.net
JIRA_API_TOKEN=your_api_token_here
JIRA_USER_EMAIL=your.email@company.com# Custom JQL query (default shown)
JIRA_JQL_QUERY=assignee = currentUser() AND updated >= -14d# Required for Things sync
THINGS_AUTH_TOKEN=your_things_auth_token
# Optional Things settings
THINGS_PROJECT=Work Projects
THINGS_TAGS=['jira', 'work']
JIRA_TYPE_TAG=trueConfigure how JIRA statuses map to Things scheduling:
# Tickets with these statuses go to "Today" in Things
TODAY_STATUS=['In Progress', 'Active', 'Doing']
# Tickets with these statuses go to "Someday" in Things
SOMEDAY_STATUS=['Backlog', 'Future']
# Tickets with these statuses go to "Anytime" in Things (default)
ANYTIME_STATUS=['To Do', 'Open', 'New']
# Tickets with these statuses are marked as completed in Things
COMPLETED_STATUS=['Done', 'Closed', 'Resolved']The app maps JIRA ticket statuses to Things 3 scheduling areas:
- Today: Urgent/active work (appears in Today view)
- Anytime: Ready to work on (appears in Anytime list)
- Someday: Future/backlog items (appears in Someday list)
- Completed: Automatically marked as done in Things
If a status isn't configured, tickets default to Anytime.
- Go to Atlassian Account Settings
- Click "Create API token"
- Give it a label and copy the token
- Add it to your config file as
JIRA_API_TOKEN
- Enable Things URL scheme in Things 3 preferences
- Get your token from under the Manage button
- Add it to your config file as
THINGS_AUTH_TOKEN
If you configure THINGS_TAGS or enable JIRA_TYPE_TAG=true, you must create these tags in Things 3 first. The app cannot create new tags - it can only assign existing ones.
The app creates a local SQLite database (jira_tasks.db) to track tickets and sync status. This enables efficient incremental syncing and offline access to your ticket data.
Each ticket has a sync status:
- 'not synced': Needs to be synced to Things (new or changed tickets)
- 'synced': Successfully synced to Things
- 'unknown': Sync failed - needs troubleshooting
- "No issues found": Check your JQL query and JIRA permissions
- Things sync fails: Verify auth token and ensure tags exist in Things
- Config errors: Ensure all required fields are present and properly formatted
- Use
--verbosefor detailed logging when debugging issues
- Initial setup:
- Copy
config.exampletoconfig - Configure JIRA and Things credentials in the
configfile
- Copy
- First run:
python main.py(syncs all matching tickets) - Daily use: Run periodically to sync new/changed tickets
- Troubleshooting: Use
--resync-to-thingsif Things gets out of sync
- Python 3.7 or higher
- A JIRA account with API token access
- Access to the JIRA instance you want to query
- Things 3 installed on your Mac
- things.py library installed (bundled with this repo in librarys/pyThings)
By default we use the following query to not overburden the Jira API:
currentUser() AND updated >= -14d
The SQLite database contains a single table jira_tickets with the following columns:
ticket_id: The JIRA issue key (e.g., "PROJ-123")summary: Issue titledescription: Full issue descriptionhas_subtasks: Boolean indicating if the issue has subtaskscreated_at: Timestamp when the record was created in the database
The script includes basic error handling and will print error messages if:
- JIRA connection fails
- Authentication fails
- JQL query is invalid
- Database operations fail
Feel free to submit issues and pull requests for additional features or improvements. I'm looking for testers!
I used LLMs to enhance the codebase and to check for errors. The banner image is also generated.
