Metadata-Version: 2.4
Name: issue-kit-mcp
Version: 0.1.0
Summary: Opinionated backlog-standards toolkit: spec-driven cross-repo lint and conformance reports for GitHub issues (MCP server + CLI).
Project-URL: Homepage, https://github.com/ignatenkofi/issue-kit-mcp
Project-URL: Repository, https://github.com/ignatenkofi/issue-kit-mcp
Project-URL: Issues, https://github.com/ignatenkofi/issue-kit-mcp/issues
Project-URL: Changelog, https://github.com/ignatenkofi/issue-kit-mcp/blob/main/CHANGELOG.md
Author: ignatenkofi
License: MIT
License-File: LICENSE
Keywords: backlog,claude,github,issues,labels,lint,mcp,model-context-protocol
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Topic :: Software Development :: Quality Assurance
Requires-Python: >=3.10
Requires-Dist: httpx>=0.27
Requires-Dist: mcp<2,>=1.2.0
Requires-Dist: pydantic>=2.6
Requires-Dist: pyyaml>=6
Provides-Extra: dev
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
Requires-Dist: pytest-cov>=5.0; extra == 'dev'
Requires-Dist: pytest>=8.0; extra == 'dev'
Requires-Dist: respx>=0.21; extra == 'dev'
Requires-Dist: ruff>=0.5.0; extra == 'dev'
Description-Content-Type: text/markdown

# issue-kit

Opinionated backlog-standards toolkit: a single spec file (`issue-standard.yml`)
declares your issue standard — label taxonomy with colors, the required
`.github/ISSUE_TEMPLATE` set, lint rules — and issue-kit checks any number of
repositories against it. MCP server and CLI over one engine.

Phase M0 is strictly **read-only**: it lints and reports, it never mutates a
repository. The only write is `standard_init`, which puts the default spec on
your local disk.

Design docs live in [ARCHITECTURE.md](ARCHITECTURE.md), [ROADMAP.md](ROADMAP.md),
and [adr/](adr/).

## Install

```bash
pip install issue-kit-mcp
# or from a checkout:
pip install -e '.[dev]'
```

Requires Python 3.10+.

## CLI

```bash
# Write the default spec (standard 2026-07-14) to ./issue-standard.yml
issue-kit init [--path PATH] [--force]

# Lint one or more repos against the spec
issue-kit lint OWNER/REPO [OWNER/REPO ...] [--repo OWNER/REPO] [--repos-file FILE]
               [--spec PATH] [--provider github|snapshot] [--snapshots DIR]
               [--format json|text|markdown] [--ci]

# Aggregate conformance dashboard
issue-kit report OWNER/REPO ... [--repos-file FILE] [--spec PATH]
                 [--provider github|snapshot] [--snapshots DIR]
                 [--format json|text|markdown]

# Capture a repo's issues/labels/templates into a snapshot bundle (GET-only)
issue-kit snapshot OWNER/REPO --out DIR [--format json|text|markdown]
```

`standard-init` / `standard-lint` / `standard-report` / `standard-snapshot`
are accepted aliases — the same naming the MCP tools use (`snapshot` is
CLI-only in M0; it has no MCP double yet).

### Exit codes

Shared CI contract (never masked):

| code | meaning |
|---|---|
| 0 | conformant, or advisory-only findings |
| 1 | error-severity findings present |
| 2 | config error: invalid spec, provider/auth failure, invalid arguments |

stdout carries the report in the chosen format (parseable even at exit 1);
stderr carries diagnostics. Default format is `text` on a TTY, `json` otherwise.

### Spec resolution

`--spec PATH` > `ISSUE_KIT_SPEC` env > `./issue-standard.yml` > built-in
default spec. An explicit path that does not exist is a config error (exit 2).

### GitHub token

The GitHub provider is read-only (issues, labels, contents) and takes its
token from the same env chain MCP clients use:
`GITHUB_PERSONAL_ACCESS_TOKEN` > `GITHUB_TOKEN` > `GH_TOKEN`. A fine-grained
PAT with Issues:read, Contents:read, Metadata:read is enough. Without a token
the provider runs anonymously (60 req/h, public repos only); an auth failure
on a private repo is a config error, never a silent pass.

### Snapshots (offline runs)

`--provider snapshot --snapshots DIR` reads repo state from bundle JSONs
instead of the network. Bundle format (fixed contract):

```json
{
  "repo": "owner/name",
  "fetched_at": "2026-07-14T12:00:00Z",
  "issues": [
    {"number": 1, "title": "...", "state": "open",
     "labels": [{"name": "bug", "color": "d73a4a", "description": ""}],
     "body": "..."}
  ],
  "labels": [{"name": "bug", "color": "d73a4a", "description": ""}],
  "templates": {"bug.md": "---\nname: ...\n---\n..."}
}
```

`labels` may be `null` — the label inventory was unavailable at capture time;
label lint then covers only labels that appear on issues. `templates` may be
`null`, which is treated as "no templates". Keep local snapshot captures in
`.snapshots/` (gitignored).

Produce a bundle with the toolkit itself instead of hand-assembling one:

```bash
issue-kit snapshot OWNER/REPO --out .snapshots
issue-kit lint OWNER/REPO --provider snapshot --snapshots .snapshots
```

`snapshot` is strictly read-only (GET issues, labels, contents), writes one
`<owner>__<repo>.json` under `--out`, and serializes deterministically (sorted
keys, `ensure_ascii=False`, indent 2) so re-captures diff cleanly. The label
inventory is captured in full (not `null`), so an offline `--provider snapshot`
run reproduces the live findings at capture time.

## MCP server

```json
{
  "mcpServers": {
    "issue-kit": {
      "command": "python",
      "args": ["-m", "issue_kit"]
    }
  }
}
```

Tools (names are a public contract): `standard_init`, `standard_lint`,
`standard_report`. Each returns the same JSON object the CLI prints with
`--format json`.

## Lint rules (M0)

| rule | severity | meaning |
|---|---|---|
| `missing_type` | error | OPEN issue without a type label |
| `multiple_type` | error | more than one type label |
| `missing_priority` | error | no P1/P2/P3 label |
| `multiple_priority` | error | more than one priority label |
| `nonstandard_label` | advisory | repo label outside the spec (may be a legitimate domain label) |
| `missing_template` | error | no template whose `labels` contains the type — matched by content, not filename: `.md` front matter or a GitHub Issue Forms `*.yml`/`*.yaml` top-level `labels` (`config.yml` excluded) |
| `missing_template_config` | advisory | no `config.yml` in `.github/ISSUE_TEMPLATE/` |
| `missing_body_section` | advisory | soft body-structure check (`problem`, `todo`) |

Title prefixes like `[P1]` are intentionally **not** mapped to priority
labels in M0 — recognizing them as relabel hints is M2 material
(`issues_relabel`). Issues by `dependabot` and pull requests are excluded
from checks and from the conformance denominator.

Reports contain issue numbers, titles, labels, and rule details — never
issue bodies.

## Development

```bash
pip install -e '.[dev]'
ruff check src tests
pytest -q
```

## License

MIT — see [LICENSE](LICENSE).
