jirasify
Terminal UI for logging work and moving Jira issues, built with Textual.
The distribution name on PyPI is jirasify; the main CLI command is jirasify-tui; the importable Python module is jiratui.
Install
pip install jirasify
Configure
On first run, jirasify-tui writes a template config to ~/.local/etc/jiratui/config.yaml and prints a usage page. Edit the file (or answer the interactive prompt to fill it) with your Jira URL, project key, and username.
Your Jira token is read from the environment variable named by authentication.token_env (default: JIRATOKEN):
export JIRATOKEN=your_token
- Self-hosted Jira (Data Center / Server): use a Personal Access Token. It is sent as
Authorization: Bearer <token>. - Atlassian Cloud (URL contains
atlassian.net): use an API token from your Atlassian account. It is combined withassignee.username(your account email) via HTTP Basic auth.
Verify connectivity before launching the TUI:
jirasify-tui --test
Prints the configured URL and token env var, then hits /rest/api/2/myself. Exits with a non-zero status on missing config, unset token, or auth failure.
Usage
jirasify-tui
| Key | Action |
|---|---|
l |
Log view |
o |
Overview |
r |
Monthly report |
t/w |
Today's / this week's report |
m |
This month's report |
s |
Change status of selected issue |
e |
Set estimate on selected issue |
h |
Help |
Ctrl+R |
Refresh |
q |
Quit |
Time input accepts 30m, 1h, 2h30m, 1d (a day = 8h).
jirasify
pip install jirasify also installs a jirasify command that turns a
structured Markdown file into Jira user stories using the same
~/.local/etc/jiratui/config.yaml and JIRATOKEN env var as jirasify-tui.
Expected Markdown layout:
# My Epic Name - User Stories
## 1. Project Overview
Free text.
---
# Phase 1 - Foundations
## User Story 001 - Inventory Environment
**Estimate:** 15 h
> As a developer, I will document ... so that ...
### Scope
- Item A
- Item B
### Substories
- Break out first sub-piece
- Break out second sub-piece
### Acceptance Criteria
- Criterion 1
- Criterion 2
- The top-level
# ...heading (any H1 that is not a# Phase ...) is used as the Epic name. A trailing- User Storiessuffix is stripped. - Stories are only picked up under a
## User Story ...heading inside a# Phase ...section. - Recognised subsections:
Scope,Validate,Include,Examples,Substories,Acceptance Criteria. - Each
### Substoriesbullet becomes a Sub-task in Jira under the parent story. Substories are excluded from the story's Jira description so they aren't duplicated.
Commands
jirasify --example # write ./example.md (plain markdown starter)
jirasify --template --file stories.md.j2 # write an Ansible/Jinja2 template
jirasify --file stories.md # parse + write .jirastories.state.json
jirasify --file stories.md --create # ensure Epic exists, create missing stories linked to it (idempotent)
jirasify --file stories.md --status # key/status/assignee/epic/estimate/used per story, grouped by phase with totals
jirasify --file stories.md --epic VOS-100 # relink stories to a specific epic
jirasify --file stories.md --epic VOS-100 --story VOS-142 # relink a single story
jirasify --reverse VOS-1234 # print a markdown reconstruction of an Epic and its stories
jirasify --reverse VOS-1234 --save # write it to <Epic_Name>.md (spaces -> _)
--create skips stories that already have an issue_key recorded in
.jirastories.state.json, and also skips summaries that already exist in the
target project, so re-runs won't duplicate issues. If no Epic with the H1's
name exists, one is created before the stories. Substories are handled the
same way — each story tracks its sub_keys map, and existing subtasks are
adopted by summary before creating new ones.
--reverse also emits each fetched story's subtasks as a ### Substories
block, so round-tripping --create then --reverse preserves the
sub-structure.
Ansible / Jinja2 template
--template emits a .j2 scaffold expecting these variables:
epic_name(string)project_overview(string, optional)phases: list of{ name, stories: [ { id, summary, estimate, description, scope?, acceptance_criteria? } ] }
Render it with ansible.builtin.template (or jinja2.Template) to produce
a stories.md, then run jirasify --file stories.md --create.
jirasify-list
pip install jirasify also installs a jirasify-list command for read-only
listing of Jira objects, using the same config as jirasify-tui.
jirasify-list --epics # Epics in jira.project (config), excluding Cancelled and Done
jirasify-list --epics --key VOS # override the project key
Status filters (mutually exclusive)
jirasify-list --epics --active # status = Implementing
jirasify-list --epics --planning # status = Planning
jirasify-list --epics --done # status = Done
jirasify-list --epics --cancelled # status = Cancelled
Without any of these flags, Cancelled and Done epics are hidden by default.
Parent Link filter
jirasify-list --epics --parent VOS-100 # exact parent (JQL: "Parent Link" = VOS-100)
jirasify-list --epics --parent VOS # any parent whose key starts with VOS-
Full hierarchy
jirasify-list --epics --full # also show stories under each epic and subtasks under each story
--full issues one extra batched JQL for all epic children ("Epic Link" in (…))
and one for all subtasks (parent in (…)), regardless of item count.
Output
Epics are grouped by their Parent Link and printed as a tree with box-drawing
branches (├──, └──, │):
VOS-100 Digital Platform Modernization
├── VOS-215 Implementing Jane Doe Artifactory PyPI
│ ├── VOS-231 In Progress Bob Provision PyPI local repo
│ │ └── VOS-232 To Do Alice Configure retention
│ └── VOS-233 To Do Bob Assemble virtual PyPI
└── VOS-220 To Do Bob Sisyphos Onboarding
VOS-105 Infrastructure 2026
└── VOS-311 Implementing Alice VMware 9.1 Enablement
(no parent)
└── VOS-999 Planning Unassigned Ad-hoc Epic
Columns per row: issue key, status, assignee (truncated to 32 chars with …),
and the label — Epic Name for epics (falls back to summary when the Epic Name
custom field is absent), summary for stories and subtasks. Without --full
only the epic level is shown.
Rows are colorized when stdout is a terminal: cyan parent header, magenta epic, green story, blue subtask. Piping or redirecting produces plain output.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file jirasify-2.1.4.tar.gz.
File metadata
- Download URL: jirasify-2.1.4.tar.gz
- Upload date:
- Size: 22.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.12.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ed78ba5b845245a9a6b2e4aa9127ea15346e146c0978611b19392ab90cb5d4f8
|
|
| MD5 |
9ff0493d05c0f0a6c9ec9a391a7d052a
|
|
| BLAKE2b-256 |
789c3414fb2e85da1e9c6ac6710c4449d12e5d40cbe7c4529c039049e6f6dbb9
|
File details
Details for the file jirasify-2.1.4-py3-none-any.whl.
File metadata
- Download URL: jirasify-2.1.4-py3-none-any.whl
- Upload date:
- Size: 25.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.12.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
05ff4dce716bdaee635b8b5f69a93fb161e27a274b2e96d1bd8357fbd4857e88
|
|
| MD5 |
93af5e0f3bc230632c749b9958685e21
|
|
| BLAKE2b-256 |
d1b3832659d41a904534f909b69e1a259bc6799d490f0b2e196ebc4e410bced5
|