Metadata-Version: 2.4
Name: cac-jira
Version: 1.2.0
Summary: A command-line interface for interacting with Jira
Author-email: Ryan Punt <ryan@mirum.org>
License-Expression: MIT
Project-URL: homepage, https://mirum.org/cac-jira/
Project-URL: repository, https://github.com/rpunt/cac-jira
Keywords: jira,cli,atlassian,project-management,command-lint,python,cli-tool
Classifier: Programming Language :: Python :: 3
Classifier: Operating System :: OS Independent
Requires-Python: <4.0,>=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: cac-core<3.0.0,>=2.0.1
Requires-Dist: tabulate>=0.9.0
Requires-Dist: jira<4.0.0,>=3.10.5
Requires-Dist: pyyaml>=6.0.2
Requires-Dist: keyring>=25.5.0
Provides-Extra: dev
Requires-Dist: pyright>=1.1.390; extra == "dev"
Requires-Dist: types-pyyaml>=6.0.12; extra == "dev"
Requires-Dist: types-tabulate>=0.9.0; extra == "dev"
Provides-Extra: test
Requires-Dist: pytest>=7.3.1; extra == "test"
Requires-Dist: pytest-cov>=4.0.0; extra == "test"
Provides-Extra: lint
Requires-Dist: ruff>=0.9.0; extra == "lint"
Requires-Dist: black<27.0,>=23.3; extra == "lint"
Requires-Dist: isort>=5.12.0; extra == "lint"
Requires-Dist: pylint>=2.17.0; extra == "lint"
Provides-Extra: docs
Requires-Dist: sphinx>=7.0.0; extra == "docs"
Requires-Dist: sphinx-rtd-theme>=1.2.0; extra == "docs"
Provides-Extra: all
Requires-Dist: pyright>=1.1.390; extra == "all"
Requires-Dist: types-pyyaml>=6.0.12; extra == "all"
Requires-Dist: types-tabulate>=0.9.0; extra == "all"
Requires-Dist: pytest>=7.3.1; extra == "all"
Requires-Dist: pytest-cov>=4.0.0; extra == "all"
Requires-Dist: ruff>=0.9.0; extra == "all"
Requires-Dist: black<27.0,>=23.3; extra == "all"
Requires-Dist: isort>=5.12.0; extra == "all"
Requires-Dist: pylint>=2.17.0; extra == "all"
Requires-Dist: sphinx>=7.0.0; extra == "all"
Requires-Dist: sphinx-rtd-theme>=1.2.0; extra == "all"
Dynamic: license-file

# Jira CLI

A command-line interface for interacting with Jira.

This project uses [UV](https://github.com/astral-sh/uv) for dependency management.

## Installation

```bash
pip install cac-jira
```

## Authentication

cac-jira supports two authentication methods: **Basic Auth** (default) and **Personal Access Tokens (PAT)**.

### Basic Auth (default)

On first-run, you'll be prompted for a Jira API token; generate one [here](https://id.atlassian.com/manage-profile/security/api-tokens). This will be stored in your system credential store (e.g. Keychain on Mac OS) in an item called `cac-jira`.

### Personal Access Token (PAT)

Some Jira Server/Data Center instances have disabled HTTP Basic Authentication; these require Personal Access Tokens, which use `Bearer` token authentication.

> **Note:** PAT authentication is only supported on Jira Server/Data Center. Jira Cloud does not support PATs — use Basic Auth with an API token instead.

To use PAT authentication, set `auth_method: pat` in your config file. On the next run you'll be prompted for your PAT, which is stored in your system credential store. `username` is optional when using PAT authentication.

```yaml
server: https://your-jira-instance.example.com
project: YOUR_PROJECT_KEY
auth_method: pat
```

## Configuration

On first-run, you'll be prompted for your `server`, `username` (basic auth only), and default `project`, and a configuration file will be generated at `~/.config/cac_jira/config.yaml`.

```yaml
server: https://your-jira-instance.atlassian.net
project: YOUR_PROJECT_KEY  # Optional default project
username: your.email@example.com
auth_method: basic  # or 'pat' for Personal Access Token
```

## Usage

The Jira CLI follows a command-action pattern for all operations:

```bash
jira <command> <action> [options]
```

### Global Options

- `--verbose`: Enable debug output (includes a traceback for unexpected errors)
- `--output [table|json]`: Control output format (default table)
- `--help`: Show command help
<!-- --suppress-output: Hide command output -->
<!-- --version: Display version information -->

Commands exit `0` on success and non-zero on failure (invalid input, a
not-found issue/project, or a Jira API error), so they compose safely in
scripts. `--help` and argument parsing work offline and do not require
credentials — the Jira connection is only established when a command runs.

### Examples

#### Issue Commands

List issues in a project:

```bash
jira issue list --project PROJ
```

List only issues assigned to you (and optionally include completed ones):

```bash
jira issue list --project PROJ --mine
jira issue list --project PROJ --done   # include issues that are resolved
```

Create a new issue:

```bash
jira issue create --project PROJ --type Task --title "Fix login bug" --description "Users can't log in"
```

Create a new issue of a type that requires custom fields:

```bash
#
# This assumes the name of the custom fields is "Custom Field One" and "Custom Field Two";
# the field name will be swapped to lower-case, and spaces replaced with underscores
#
jira issue create --project PROJ --type Custom\ Issue\ Type --title "Issue Title" --description "Issue description" \
  --field custom_field_one custom_field_value \
  --field custom_field_two custom_field_value
```

Create and assign to yourself:

```bash
jira issue create --project PROJ --type Bug --title "Server crash" --assign
```

Create and immediately start work:

```bash
jira issue create --project PROJ --type Story --title "Add login feature" --begin
```

Add an issue to an epic:

```bash
jira issue create --project PROJ --type Task --title "Subtask" --epic PROJ-100
```

Label an issue:

```bash
jira issue label --issue ISSUE_KEY --labels label1,label2
```

Transition an issue:

```bash
jira issue begin --issue ISSUE_KEY    # Start work
jira issue block --issue ISSUE_KEY    # Mark as blocked
jira issue close --issue ISSUE_KEY    # Mark as complete
```

Delete an issue (prompts for confirmation; pass `--force` to skip it, e.g. in scripts):

```bash
jira issue delete --issue ISSUE_KEY
jira issue delete --issue ISSUE_KEY --force
```

#### Project Commands

List all projects:

```bash
jira project list
```

Filter projects by name or key (case-insensitive, partial match):

```bash
jira project list --name "Core"
jira project list --key COR
```

Show a single project by its key:

```bash
jira project show PROJ
```

#### Advanced Examples

Update an issue's title or description:

```bash
jira issue update --issue ISSUE_KEY --title "New issue title" --description "new issue description"
```

Add a comment to an issue:

```bash
jira issue comment --issue ISSUE_KEY --comment "This is a comment."
```

List all issue IDs matching a label:

```bash
jira issue list --output json | jq -r '.[] | select(.Labels | contains("production")) | .ID'
```

## Shell Completion

`jira` supports tab-completion of commands, actions, and options via
[argcomplete](https://kislyuk.github.io/argcomplete/).

### Enabling completion

The recommended approach is per-command registration. Add the appropriate line
to your shell startup file:

```bash
# bash (~/.bashrc) or zsh (~/.zshrc)
eval "$(register-python-argcomplete jira)"
```

Then restart your shell (or `source` the file). Tab-completion works
immediately:

```bash
jira <TAB>                 # -> issue  project
jira issue <TAB>           # -> assign attach begin ... show update
jira issue show --<TAB>    # -> --issue --output --project --verbose
```

<details>
<summary>Alternative: global activation</summary>

To enable argcomplete for every marker-tagged program at once (instead of
per-command), run this once and restart your shell:

```bash
activate-global-python-argcomplete
```

</details>

## Development

### Setup Development Environment

```bash
# Install dependencies including dev dependencies
uv sync

# Activate the venv
source .venv/bin/activate

# Run tests
uv run pytest
```

### Project Structure

- `cac_jira/__init__.py` - Module init: the `CONFIG`/`JIRA_CLIENT` globals and
  the `main` console-script entry point (`main = make_main("cac_jira", "jira", ...)`)
- `cac_jira/commands/` - Command implementations (auto-discovered at runtime)
  - `issue/` - Issue-related commands
  - `project/` - Project-related commands
- `cac_jira/core/client.py` - Thin wrapper around the `jira` Python client

Command discovery, argument parsing, shell completion, and dispatch are all
provided by the shared runner in [`cac-core`](https://github.com/rpunt/cac-core)
(`cac_core.cli.run` / `make_main`); this project only supplies the `commands/`
tree and its Jira client.

### Adding New Commands

1. Create a new action module in the appropriate command directory.
2. Define a class that inherits from the command's base class, following the
   `{Command}{Action}` naming convention (e.g. `commands/issue/create.py` →
   `IssueCreate`).
3. Implement `define_arguments()` and `execute()` methods.

`execute()` contains the command's logic and returns an exit code (`0`/`None`
for success, non-zero for validation failures). It does not need to wrap Jira
calls in try/except: the shared `run()` template (from `cac-core`) catches
errors and maps them to a non-zero exit code, and `JiraCommand.handle_exception`
renders `JIRAError`s using their human-readable Jira message.
