CLAUDE.md · git:20260603.884702b · 2026-06-03 · sha256 d291044204d9d4a1

CLAUDE.md git:20260603.884702bA

Immutable. This exact content never changes and is served at /api/v1/blob/d291044204d9d4a1.

# CLAUDE.md — Rust Jira CLI

Guide for Claude (and contributors) when working in this repo.

---

## Claude rules — MUST follow

Claude may:
- Create and edit files on the filesystem
- Run `cargo` commands for build/test/check
- Run `git` commands for read and write operations when explicitly requested by the repo owner, including `git add`, `git commit`, and `git push`
- Do not manually bump versions, edit generated changelogs, create/push tags, or rewrite git history unless explicitly requested by the repo owner

---

## Project overview

Rust CLI for Atlassian Jira (`jirac` binary). Focus: full custom field via dynamic introspection, attachment upload, Jira REST API v3, interactive TUI (ratatui), single binary.

### Workspace structure

```
crates/
├── jira-core/                  # PUBLIC LIBRARY (crates.io: "jira-core")
│   └── src/
│       ├── adf.rs              # Atlassian Document Format parser
│       ├── auth.rs             # auth + multi-profile (Cloud + Data Center)
│       ├── client.rs           # JiraClient — REST API surface
│       ├── config.rs           # JiraConfig loader (figment: toml + env)
│       ├── error.rs            # JiraError + Result alias
│       ├── field_cache.rs      # custom-field id ↔ name resolution cache
│       └── model/              # attachment, comment, field, issue, sprint, worklog
├── jira/                       # BINARY (crates.io: "jira-commands", binary: jirac)
│   └── src/
│       ├── main.rs
│       ├── datetime.rs         # datetime helpers
│       ├── cli/                # clap commands: api, auth, issue, plan
│       └── tui/                # ratatui TUI — modular split
│           ├── app.rs          # event loop coordinator
│           ├── column.rs       # column layout
│           ├── keys.rs         # keybinding map
│           ├── mode.rs         # app mode state
│           ├── panel.rs        # split-pane logic
│           ├── picker.rs       # native pickers
│           ├── prefs.rs        # prefs overlay
│           ├── prompts.rs      # inline prompts
│           ├── render.rs       # frame render
│           └── theme.rs        # color theme
└── jira-mcp/                   # MCP SERVER (crates.io: "jira-mcp", binary: jirac-mcp)
    └── src/
        ├── main.rs
        ├── lib.rs
        ├── app.rs              # MCP app wiring
        ├── server.rs           # rmcp server impl
        ├── models.rs           # MCP tool I/O types
        └── error.rs
plugin/
├── .claude-plugin/             # Claude Code plugin metadata (plugin.json)
└── skills/                     # 12 skills: api, attach, bulk-transition, comment, create-issue, fields, jql, list-issues, transition, update-issue, view-issue, worklog
```

### Crate responsibilities

- **`jira-core`** — public API: `JiraClient`, model types (split under `model/`), ADF parser, auth (Cloud + Data Center multi-profile), `FieldCache` for custom-field resolution, error types. Library dependency.
- **`jira/`** — clap commands (`cli/{api,auth,issue,mcp,plan}.rs`), modular TUI (ratatui + crossterm, 10 submodules under `tui/`), interactive prompts (inquire), datetime helpers. Binary: `jirac`. `mcp` subcommand: `jirac mcp doctor` + `jirac mcp install --client <target>` to register `jirac-mcp` into supported clients (claude-code, claude-desktop, cursor, gemini-cli, codex, generic-json).
- **`jira-mcp/`** — MCP server via `rmcp`, split into `app.rs` (wiring) + `server.rs` (impl) + `models.rs` (I/O types). Exposes `jira-core` as MCP tools for LLM clients. Binary: `jirac-mcp`.

---

## Jira API — implementation rules

### Endpoint rules

| Use                                         | Do NOT use                                           |
| ------------------------------------------- | ---------------------------------------------------- |
| `GET/POST /rest/api/3/search/jql`           | `/rest/api/3/search` (dead since Oct 2025)           |
| `POST /rest/api/3/search/approximate-count` | `/rest/api/3/fieldconfiguration*` (removed Jul 2026) |
| `GET /rest/api/3/projects/fields`           |                                                      |
| `GET /rest/api/3/priorityscheme`            |                                                      |

### Implementation principles

- **Pagination**: cursor-based (`next_page_token`), not offset (`startAt`). Max 500 iterations as safeguard.
- **Rate limiting**: handle 429 with `Retry-After` header, retry after delay.
- **Field resolution**: always runtime via API, never hardcode `customfield_*`.
- **Async tasks**: submit → poll → complete pattern for heavy operations (archive, bulk ops).
- **Tier detection**: check `server_info.is_premium()` before using premium features (Plans API).
- **Base URL**: Platform API (`/rest/api/3`) vs Agile API (`/rest/agile/1.0`) — don't mix, use client methods.

---

## Smoke test

Claude runs this before reporting to repo owner. Fix until all green.

```bash
cargo fmt --all -- --check && \
cargo clippy --all-targets --all-features -- -D warnings && \
cargo test --all && \
cargo build --all
```

---

## Release flow — release-please (automated)

**Never manually bump versions, update CHANGELOG, or push tags.**

### How it works

1. Push commits to `main` with **Conventional Commits**
2. release-please creates/updates a Release PR (version bump + CHANGELOG)
3. Merge Release PR → pushes tag → `release.yml` triggers build + publish

### Conventional Commits

Format: `<type>(<scope>): <description>` — in English.

| Type                              | Bump       |
| --------------------------------- | ---------- |
| `feat:`                           | MINOR      |
| `fix:`, `perf:`, `refactor:`      | PATCH      |
| `feat!:` / `BREAKING CHANGE:`     | MAJOR      |
| `chore:`, `docs:`, `ci:`, `test:` | No release |

### crates.io publish order

`jira-core` → sparse index ready → `jira-mcp` → `jira-commands`. Never publish manually.

### CI workflows

See `.github/workflows/` for details — actual files are source of truth.
- **ci.yml**: fmt + clippy + test + build (matrix: ubuntu/macos/windows)
- **security.yml**: `cargo audit`
- **release-please.yml**: auto version bump + CHANGELOG + tag (both lanes)
- **release-tag.yml**: build binaries + publish jirac (trigger: tag `v*`)
- **release-tag-mcp.yml**: build binaries + publish jirac-mcp (trigger: tag `jira-mcp-v*`)
- **release-recover.yml**: recovery workflow for failed releases
- **clawhub-publish-jirac.yml**: publish the `clawhub/jirac` skill to ClawHub
- **clawhub-publish-jirac-plugin.yml**: publish the `clawhub/jirac-plugin` wrapper package to ClawHub
- **pr-automerge.yml**: auto-merge release-please PRs
- **winget-submit.yml**: submit to Windows Package Manager

### Release strategy

Uses `simple` release type with a `VERSION` file at repo root. Cargo.toml files updated via `generic` updater in `release-please-config.json`. Plugin version in `plugin/.claude-plugin/plugin.json` is separate and NOT auto-bumped by release-please.

### Dual release lanes (jira-commands vs jira-mcp)

Two independent lanes via `separate-pull-requests: true`:

| Lane            | Component        | Tag                  | Version source             | Excludes                                  |
| --------------- | ---------------- | -------------------- | -------------------------- | ----------------------------------------- |
| jira-commands   | jira + jira-core | `vX.Y.Z`             | `/VERSION`                 | `crates/jira-mcp`, `packaging/npm-mcp`    |
| jira-mcp        | jira-mcp         | `jira-mcp-vX.Y.Z`    | `crates/jira-mcp/VERSION`  | —                                         |

**Commit scoping → which lane bumps:**

- Edits ONLY under `crates/jira-mcp/**` or `packaging/npm-mcp/**` → jira-mcp lane only. Use `feat(jira-mcp):` / `fix(jira-mcp):` (or `feat(mcp):`).
- Edits to root files (`README.md`, `INSTALL.md`, `.github/workflows/**`, `release-please-config.json`, root `Cargo.toml`, `crates/jira/**`, `crates/jira-core/**`, `packaging/npm/**`) → jira-commands lane bumps regardless of scope name.
- Mixed PRs bump BOTH lanes → two release PRs open in parallel.

**Manifest race (avoid):** if two release PRs are open at once, merging one stale-overwrites the other's manifest entry. Mitigation:

1. Merge release PRs one at a time. After the first merges, **wait for release-please.yml to rebase the second PR** before merging it (it picks up updated manifest).
2. `release-please.yml` has a `Verify manifest matches VERSION files` guard step — fails fast if state drifts.

To recover from a stomped manifest: edit `.release-please-manifest.json` to match the latest published tag for each component, commit as `fix(release): correct manifest after parallel release PR race`.

### Plugin marketplace

When adding/changing skills, update `plugin/skills/<skill>/SKILL.md` and the table in README.

**Manual bump required:** changes to `plugin/` (skills, metadata) must also bump the version in `plugin/.claude-plugin/plugin.json` — release-please does NOT touch this file.

**Manual bump required:** changes to `clawhub/jirac/` (SKILL.md, references) must also bump `clawhub/jirac/VERSION` — release-please does NOT touch this file. CI publishes to ClawHub on push to `main` only when `clawhub/jirac/**` changes.