JIRA ticket context manager for git workflows
Project description
jira-git-helper
A terminal-based JIRA ticket context manager for git workflows, invoked as jg.
jg keeps track of which JIRA ticket you're working on so that branch names,
commit messages, and PR lookups are automatically prefixed — without you having
to type the ticket key every time.
How it works
jg maintains an active ticket for each terminal session (e.g. SWY-1234).
Once set, commands like jg commit, jg branch, and jg push automatically use
that ticket — you never have to copy-paste it again.
$ jg set # pick a ticket interactively
$ jg branch fix # creates SWY-1234-fix and switches to it
$ jg add # stage files and commit — ticket prefix added automatically
$ jg push # pushes branch and opens the linked PR
Each terminal window can track a different ticket independently.
Installation
uv tool install jira-git-helper
Or with pipx:
pipx install jira-git-helper
Quick start
1. Connect to JIRA
jg config set server https://yourcompany.atlassian.net
jg config set email you@yourcompany.com
jg config set token <your-jira-api-token>
Generate a token at: https://id.atlassian.com/manage-profile/security/api-tokens
2. Set up the shell hook
The hook lets each terminal track its own ticket independently. See Shell hook for full details and shell-specific instructions.
3. (Optional) Show the active ticket in your prompt
See Prompt integration for fish/Tide, bash, and zsh instructions.
4. (Optional) Scope tickets to your projects
jg config set projects SWY
# or multiple:
jg config set projects SWY,DOPS
5. Pick a ticket and start working
jg set # opens an interactive picker
jg # shows the active ticket at any time
Configuration
Config is stored in ~/.config/jira-git-helper/config. Use jg config set/get/list to manage it.
Required
| Key | Description |
|---|---|
server |
Your JIRA instance URL, e.g. https://yourcompany.atlassian.net |
email |
Your JIRA account email |
token |
Your JIRA API token |
Optional
| Key | Description |
|---|---|
projects |
Comma-separated project keys to scope the ticket picker, e.g. SWY or SWY,DOPS |
fields.<PROJECT> |
Comma-separated JIRA field IDs to show as extra columns in jg set (see below) |
fmt_on_add |
Set to true to run formatters automatically before the commit prompt in jg add |
open_on_push |
Set to true to open the matching PR (or new-PR URL) in the browser after jg push |
Project scoping
Without projects set, jg set shows all tickets assigned to you across JIRA.
With projects set, results are scoped to just those projects:
# Single project
jg config set projects SWY
# Multiple projects — results from all projects are merged into one list
jg config set projects SWY,DOPS
Named filters
Each project can have any number of named JQL filters, managed interactively inside
jg set by pressing f. One filter can be marked as the default — it is loaded
automatically every time jg set opens.
JQL resolution order (for a given project):
- Session-active filter (set via Enter in the filter modal — not persisted across runs)
- Persisted default filter (set via Space in the filter modal — saved to config)
- Built-in default:
project = PROJECT AND assignee = currentUser() ORDER BY updated DESC
Filters are visible in jg config list.
Extra columns in jg set
Use fields.<PROJECT> to add custom JIRA fields as columns in the ticket picker. The easiest
way to discover field IDs is the built-in field picker: open jg set, press d on any ticket,
then space to toggle fields and Enter to save.
Or set them manually:
jg config set fields.SWY customfield_10234,customfield_10567
Field values are also included in filter-bar searches.
View your current config
jg config list
This shows all standard keys plus any named filters configured for each project.
Shell hook
The hook does three things:
- Seeds
JG_TICKETfrom the last-used ticket when a new shell opens (so you don't start from scratch every time). - Keeps terminals isolated —
jg setin one terminal updates only that terminal'sJG_TICKET. Other open terminals are unaffected. - Updates
JG_TICKETafterjg set, and clears it afterjg clear.
Without the hook, all terminals share the same ticket via the state file.
Fish — add to ~/.config/fish/config.fish:
eval (jg hook)
Bash — add to ~/.bashrc:
eval "$(jg hook --shell bash)"
Zsh — add to ~/.zshrc:
eval "$(jg hook --shell zsh)"
How the hook keeps shells isolated
The hook sets a JG_TICKET environment variable that is local to each shell session.
When JG_TICKET is defined (even if empty), jg commands use it exclusively and
never read the shared state file. This prevents one shell from accidentally seeing
another shell's ticket.
jg set→ writes the ticket to the state file and updatesJG_TICKETin the current shell.jg clear→ deletes the state file and setsJG_TICKETto an empty string. The empty string is important — it tellsjg"there is no ticket" without falling back to the state file (which another shell may have since written to).- New shell → the seed block reads the state file once and sets
JG_TICKET. After that, the state file is not consulted again in that shell.
Updating the hook after upgrading
The hook is generated at eval time. After upgrading jg, re-source the hook in any
open shells to pick up changes:
# fish
source (jg hook | psub)
# bash / zsh
eval "$(jg hook --shell bash)" # or zsh
Or simply restart your terminal.
Prompt integration
Fish / Tide
Display the active ticket in your Tide prompt. Run once to install the prompt item:
jg setup
Then follow the printed instructions to add jg to your Tide prompt items.
The
jg setupcommand writes~/.config/fish/functions/_tide_item_jg.fish, which reads the shell-local$JG_TICKETvariable — so each terminal shows its own ticket.
Bash
The hook defines a __jg_ps1 helper. Splice it into your PS1 in ~/.bashrc
(after the eval line):
PS1='$(__jg_ps1)\$ '
Or anywhere inside an existing prompt string, e.g.:
PS1='\u@\h $(__jg_ps1)\$ '
Zsh
Same helper, different variable. Add to ~/.zshrc (after the eval line):
PROMPT='$(__jg_ps1)%% '
__jg_ps1 prints the active ticket followed by a space, or nothing if no ticket is set.
Commands
jg
Show the active ticket for the current session.
$ jg
SWY-1234
jg set [TICKET]
Set the active ticket. With no argument, opens an interactive picker that fetches tickets from JIRA based on your configured projects and JQL.
jg set # interactive picker
jg set SWY-1234 # set directly without opening the picker
Flags:
| Flag | Description |
|---|---|
--jql "..." |
Use a raw JQL query instead of configured project JQL. Useful for one-off searches without changing your config. |
--max N |
Maximum number of tickets to fetch (default: 200) |
Examples:
# Show only high-priority tickets, one-off
jg set --jql "project = SWY AND priority = Highest ORDER BY created DESC"
# Fetch more results than the default
jg set --max 500
Interactive picker controls:
| Key | Action |
|---|---|
↑ / ↓ |
Move between tickets |
/ |
Open filter bar — type to narrow by key, summary, assignee, status, or any custom column |
Enter |
Select the highlighted ticket (or confirm filter and return to list) |
t |
Toggle between flat table view and hierarchical tree view (epics → tasks → subtasks) |
i |
Open a detail panel for the highlighted ticket (summary, status, assignee, description, etc.) |
o |
Open the highlighted ticket in your browser |
c |
Copy the ticket URL to the clipboard |
d |
Open the field picker — browse all fields on the ticket, space to toggle columns, Enter to save |
f |
Open the filter manager for the current ticket's project |
r |
Refresh — re-query JIRA and reload the list |
Escape |
Close filter / cancel |
Tree view (t):
Press t to switch to a hierarchical view that groups tickets by epic → task → subtask. Selecting a ticket in tree view sets it as the active ticket, same as the flat table. The filter bar works in tree view too — non-matching branches are hidden, and parent nodes with matching descendants are shown dimmed.
Filter manager (f):
Pressing f opens a per-project filter list. The active filter is shown in a status
bar above the footer — * means no custom filter (built-in default JQL), otherwise
the filter name is shown, e.g. SWY: Sprint DOPS: *.
| Marker | Meaning |
|---|---|
● |
Persisted default, currently active |
▶ |
Session-activated (Enter) — active this run only, not persisted |
○ |
Persisted default, but currently overridden by a session-activated filter |
| Key | Action |
|---|---|
Enter |
Activate filter for this session (not saved as default) |
Space |
Set filter as the persisted default (saved to config) |
n |
Create a new filter — prompts for a name, then a JQL query |
e |
Edit the JQL of the selected filter |
d |
Delete the selected filter (with confirmation) |
Escape |
Close the filter manager |
jg clear
Clear the active ticket for the current session.
jg clear
jg info [TICKET]
Show a rich summary panel for a ticket, including: summary, status, priority, assignee, reporter, labels, URL, and a description excerpt (truncated at 800 chars).
jg info # uses the active ticket
jg info SWY-5678 # look up any ticket by key
jg debug <ticket>
Dump every raw JIRA field for a ticket, formatted as syntax-highlighted JSON. Only non-null fields are shown.
jg debug SWY-1234
Useful for discovering custom field IDs, inspecting the API shape of a specific ticket type, or diagnosing issues with the tree view hierarchy.
jg open [TICKET]
Open a ticket in your browser.
jg open # opens the active ticket
jg open SWY-5678 # open any ticket by key
jg branch [name]
Work with git branches scoped to the active ticket.
With no arguments — fetches from origin, then shows an interactive picker with all local and remote branches matching the active ticket. Each branch shows its tracking status (tracked, local, or remote). If no matching branches exist, goes straight to the new branch prompt.
jg branch
With a name — creates a new branch named TICKET-branch-name (using git switch -C)
and switches to it.
jg branch my-feature # creates SWY-1234-my-feature
Interactive picker controls:
| Key | Action |
|---|---|
↑ / ↓ |
Move between branches |
/ |
Open filter bar — type to narrow by branch name or tracking status |
Enter |
Switch to the highlighted branch (or confirm filter and return to list) |
n |
Create a new branch — opens the branch prompt with ticket info and suffix input |
Escape |
Close filter / cancel |
Tracking column:
| Value | Colour | Meaning |
|---|---|---|
tracked |
green | Local branch with remote upstream |
local |
amber | Local-only branch |
remote |
cyan | Remote-only branch (no local checkout) |
Status column:
| Value | Colour | Meaning |
|---|---|---|
never pushed |
amber | Local branch that was never pushed |
remote deleted |
red | Remote upstream was deleted (e.g. after PR merge) |
remote only |
cyan | Exists on remote but no local checkout |
| (empty) | Healthy tracked branch |
jg add
An interactive TUI for staging files and committing — all in one step.
jg add
If run on main/master, a branch prompt screen appears first — it shows the active ticket's info and a suffix input. After the branch is created you are switched to it and the staging screen opens.
The screen is split into sections: Staged (always shown), plus Modified, Deleted, and Untracked sections that appear only when they have files. Use Space to toggle files between staged/unstaged, then Enter to open the commit message prompt. The commit message is automatically prefixed with the active ticket key.
File picker controls:
| Key | Action |
|---|---|
↑ / ↓ |
Move between files |
Space |
Stage or unstage the highlighted file |
f |
Run formatters — opens a results modal, then reloads git status on close |
/ |
Open filter bar for the focused section |
Enter |
Open commit message prompt (or confirm filter and return to list) |
Escape |
Close filter / cancel |
Commit message prompt:
Type a message and press Enter to commit. Press Escape to skip the commit and just apply the staged changes — useful when you want to stage files now and commit later with jg commit.
If fmt_on_add is set to true in config, formatters run automatically before the commit prompt opens. If a formatter modifies a staged file (dropping it back to modified), the commit is paused and a warning is shown so you can review and re-stage before trying again:
jg config set fmt_on_add true
Note: If no ticket is set,
jg addwill prompt you to pick one interactively before proceeding.Note: If the current branch is not prefixed with the active ticket key,
jg addwill prompt for a branch suffix and create the branch automatically before committing.
jg commit <message>
Commit with the active ticket key automatically prepended to the message.
jg commit "fix login redirect"
# runs: git commit -m "SWY-1234 fix login redirect"
Any extra arguments after the message are passed through to git commit:
jg commit "fix login redirect" --no-verify
jg commit "fix login redirect" --amend
Note: Refuses to run on
mainormaster. Usejg branch <name>to create a feature branch first.
jg fmt
Run formatters against all modified, staged, and untracked files in the current repo.
jg fmt
Results are shown as a table with columns for status, file, formatter name, exit code, and a note:
File Formatter Exit Note
✓ main.hcl eof 0
✓ main.hcl terragrunt-hcl-fmt 0
✗ broken.hcl terragrunt-hcl-fmt 1 invalid HCL on line 4
— logo.png — — skipped (binary)
Built-in formatter: eof
The eof formatter always runs on every text file. It ensures each file ends with exactly one newline, stripping any extra trailing newlines. It cannot be removed.
Binary file detection
Before running any formatter, jg fmt uses git ls-files --eol to identify binary files. Binary files are shown in the table as skipped (binary) and no formatter is run on them, even if their extension would otherwise match.
User-configured formatters
Each formatter has a name, a file glob (matched against the filename), and a command. Use {} in the command as a placeholder for the absolute file path — similar to find -exec.
jg fmt add # interactive prompts for name, glob, and command
jg fmt add terragrunt-hcl-fmt # supply the name upfront
jg fmt edit terragrunt-hcl-fmt # edit glob and command (current values pre-filled)
jg fmt list # list all configured formatters
jg fmt delete terragrunt-hcl-fmt # remove a formatter by name
Example — adding a HCL formatter:
Formatter name: terragrunt-hcl-fmt
File glob (e.g. *.hcl, *.tf): *.hcl
Command (use {} for the filename): /usr/local/bin/terragrunt hcl fmt {}
Formatting only files changed in the current branch:
jg fmt diff
Runs all formatters over files changed between the current branch and the default branch. Useful before opening a PR to ensure only your changes are formatted, without touching unrelated files. Exits with a warning if run on main/master or a detached HEAD.
Formatter configuration is stored in ~/.config/jira-git-helper/config under the fmt key and is also visible in jg config list.
jg push
Push the current branch to origin (git push -u origin HEAD).
jg push
When open_on_push is enabled, jg push looks up open PRs linked to the active
ticket in JIRA and opens the one matching the current branch. If no matching PR
exists but GitHub printed a "Create a pull request" URL during the push, it opens
that instead.
jg config set open_on_push true
jg reset
Switch to the default branch and pull the latest from origin.
jg reset
The default branch is detected from origin/HEAD, falling back to main or master.
If uncommitted changes would block the branch switch, jg reset offers to stash them
and continue. After a successful pull it then offers to restore the stash.
jg sync
Rebase the current feature branch onto the latest default branch from origin, without leaving your branch.
jg sync
Equivalent to git fetch origin && git rebase origin/main. The default branch is auto-detected from origin/HEAD, falling back to main/master.
If a conflict occurs, the standard git rebase flow applies — resolve conflicts and run git rebase --continue, or git rebase --abort to cancel.
Use
jg resetwhen you want to switch to the default branch and pull. Usejg syncwhen you want to stay on your feature branch and bring it up to date.
jg prune
Interactively prune local branches that have no remote counterpart — either because the remote was deleted (typically after a PR merge) or because the branch was never pushed.
jg prune
Runs git fetch --prune first to refresh remote-tracking refs, then opens an interactive DataTable listing every prunable local branch. The current branch and default branch are always excluded.
Status column:
| Status | Colour | Meaning |
|---|---|---|
remote deleted |
amber | Had a remote tracking branch that was deleted |
never pushed |
cyan | Local-only branch that was never pushed |
Controls:
| Key | Action |
|---|---|
↑ / ↓ |
Navigate branches |
Space |
Toggle selection |
a |
Select / deselect all |
d |
View diff against default branch in a full-screen viewer |
x |
Delete selected branches (confirmation prompt) |
s |
Switch to the highlighted branch and exit |
Escape |
Quit without changes |
Uses git branch -D (force delete) so branches merged via squash or rebase are handled correctly alongside standard merges.
jg prs [TICKET]
Browse all GitHub PRs linked to a ticket in an interactive TUI.
jg prs # uses the active ticket
jg prs SWY-5678 # browse PRs for any ticket
PRs are fetched from JIRA and supplemented with GitHub CLI results (if gh is installed and authenticated). Columns shown: Source, Status, Author, Repo, Source branch, Title, Updated. PRs are sorted with GitHub PRs first, then open before merged/declined, then by last-updated date. Status is colour-coded: green (open), yellow (draft), blue (merged), red (declined).
Controls:
| Key | Action |
|---|---|
↑ / ↓ |
Move between PRs |
/ |
Open filter bar — searches status, author, repo, branch, and title |
o |
Open the highlighted PR in your browser |
d |
View the PR diff inline (uses delta if installed) |
s |
Switch to the PR's source branch (checks out the remote tracking branch if not found locally) |
Escape |
Close filter / quit |
Requires:
ghCLI installed and authenticated.
jg config get <key>
Print a single config value.
jg config get server
jg config get jql.SWY
Exits with a non-zero status if the key is not set.
jg config set <key> <value>
Set a config value. Standard keys are server, email, token, and projects.
jg config set server https://yourcompany.atlassian.net
jg config set email you@yourcompany.com
jg config set token <api-token>
jg config set projects SWY,DOPS
Named JQL filters are managed interactively with the f key inside jg set.
jg config list
List all configured values. Masks the token value for safety. Automatically
shows any jql.<PROJECT> keys you have set.
jg config list
jg hook [--shell fish|bash|zsh]
Print the shell hook function to stdout. Intended to be evaluated in your shell startup file (see Shell hook above).
jg hook # fish (default)
jg hook --shell bash
jg hook --shell zsh
| Flag | Description |
|---|---|
--shell fish|bash|zsh |
Shell to emit the hook for (default: fish) |
The bash/zsh hook also defines __jg_ps1 for prompt integration (see
Prompt integration).
jg setup
Configure fish/Tide prompt integration. Creates
~/.config/fish/functions/_tide_item_jg.fish and prints the follow-up set -U
commands needed to activate and style the prompt item.
jg setup
Fish/Tide only. For bash/zsh prompt integration, see Prompt integration.
jg version
Print the installed version.
jg version
# or
jg --version
Requirements
- Python 3.10+
ghCLI — required for PR diff viewing injg prs— https://cli.github.com
License
MIT
Project details
Release history Release notifications | RSS feed
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 jira_git_helper-0.23.0.tar.gz.
File metadata
- Download URL: jira_git_helper-0.23.0.tar.gz
- Upload date:
- Size: 74.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.10.3 {"installer":{"name":"uv","version":"0.10.3","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f262bca1825ee3e2751d84aae393ce7d4e7c68bbf99c07ad1b9fe5550c9eced3
|
|
| MD5 |
dab8156a7d464abfa439fc1d691ad15f
|
|
| BLAKE2b-256 |
3de048640f1642171b7cb24414ca86d6371eda122d5c3c29f60f1c2933194a21
|
File details
Details for the file jira_git_helper-0.23.0-py3-none-any.whl.
File metadata
- Download URL: jira_git_helper-0.23.0-py3-none-any.whl
- Upload date:
- Size: 48.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.10.3 {"installer":{"name":"uv","version":"0.10.3","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ee22b2b862ee9a27b83b145a76eba39bea1eca11ec39c1ed07b466ce9f9b86d5
|
|
| MD5 |
32131a6c49a210e0b04316d9dcbb2e7c
|
|
| BLAKE2b-256 |
9382fb25ea8062406b1b787cef266ca4d63f6b4dba5487660497df1f4331d39f
|