Metadata-Version: 2.4
Name: gh-task
Version: 0.1.1
Summary: Python + CLI helpers for GitHub Projects task ownership
Author: yieldthought
License: MIT
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: requests>=2.31
Dynamic: license-file

# gh-task

Python + CLI helpers for taking, moving, and releasing GitHub Project (v2) issues using a lightweight ownership label protocol.

## Install (local)

```bash
pip install -e .
```

## Auth

Uses `GH_TOKEN` or `GITHUB_TOKEN` if set, otherwise falls back to `gh auth token`. Ensure the token has the `project` scope for Project v2 access.

## Python usage

```python
from gh_task import Project

project = Project(project="https://github.com/users/yieldthought/projects/3/views/1", name="my-name")

# list status columns
print(project.list())

# list issue numbers in Backlog
print(project.list("Backlog"))

# take a specific issue
issue_id = project.take(2)

# take the first available issue in Backlog
issue_id = project.take("Backlog")

# move and release
project.move(2, "In review")
project.release(2)

# context-managed lease (auto-release on exit)
with project.lease("Backlog") as issue:
    print(issue.number)
```

## CLI

```bash
# list status columns
 gh-task list -p yieldthought/projects/3 -n my-name

# list issues in Backlog
 gh-task list -p yieldthought/projects/3 -n my-name Backlog

# take a specific issue
 gh-task take -p yieldthought/projects/3 -n my-name -i 2

# take the first available issue in Backlog
 gh-task take -p yieldthought/projects/3 -n my-name Backlog

# move and release
 gh-task move -p yieldthought/projects/3 -n my-name -i 2 -s "In review"
 gh-task release -p yieldthought/projects/3 -n my-name -i 2
```

## Ownership protocol

`take()` performs the following:
1. Verify there is no label starting with `owner:`.
2. Add a comment `Taking: <name>`.
3. Wait 1s (configurable) and fetch all comments beginning with `Taking:`.
4. If the earliest `Taking:` comment is yours, add `owner: <name>` label.
5. Remove your `Taking:` comment (best-effort).

If any step fails, the comment is removed and a `TakeError` is raised.
