@milkyway-666/jira-tempo
v0.1.0-alpha.0
Published
Manage your work on one Jira Data Center project from a local repo — see a kanban of your work, log your time, move your tickets. Agent-driven, runs as a local MCP server.
Maintainers
Readme
jira-tempo
Manage your work on one Jira project from a local repo. You join a project, you make a repo for it, and from then on that repo is where you see your board, track your time, and move your tickets — by talking to an agent.
you ──talk──▶ Claude ──MCP tools──▶ jira-tempo ──HTTP──▶ Jira DC + Tempo
▲ │
└────────look──── board.html ◀─────────┘view · log · move — the three things v0 does:
- view — a kanban of your work in your project's real Jira statuses, not a fixed five
- log — narrate what you did; it writes correct worklogs to Tempo. Run it twice and the second run does nothing
- move — "move 412 to QA" transitions the issue and the card moves
Your breakdown, estimates, notes and hours live in the repo as an append-only log and stay yours. Jira stays the system of record for the work itself.
Status
v0 is code complete against fakes — 73 tests passing, typecheck clean, CLI builds.
It has not yet run against a real Jira. The next step is jt init on the live instance, which
will confirm the one thing fakes can't: Tempo's actual payload shapes. The DC plugin API has
drifted across versions, so those are written to spec, read defensively, and probed at init
rather than assumed.
Two specified-but-unwritten test areas are tracked in
roadmap.md: board.html rendering, and half of track.ts.
Requirements
- Node ≥ 20
- Jira Data Center 9.17.5 on-prem — Cloud is out of scope
- Tempo Timesheets (detected at
init; native Jira worklogs are the fallback) - A Personal Access Token, and the VPN
Quick start
npm install && npm run build # in this repo
mkdir -p ~/work/manage/eng && cd ~/work/manage/eng
git init
node /path/to/jira-tempo/dist/bin.js initThen point your agent at it with .mcp.json and talk to it. Full walkthrough:
docs/getting-started.md.
Docs
Start at docs/.
| | | |---|---| | getting-started.md | set it up and use it | | features/ | what each capability does, and where it stops | | design.md | why it's shaped this way — including the Decisions table | | c4/ | structure at four zoom levels: context → container → component → code | | architecture.md | layering and the dependency rule | | contributing.md | how to add something | | test-cases/ | what must be true, as checkable statements | | roadmap.md | what ships when | | deploy.md | releasing to npm |
How work happens here
Docs → tests → code, in that order, every time.
- Docs decide what it does and why — the cheapest place to be wrong. Four decisions on this project reversed during the doc stage; each cost a paragraph instead of a rewrite.
- Tests state it as something checkable — where a vague doc gets found out. "Push should be idempotent" sounds finished until you try to write the assertion and realise nobody said what the unit of idempotency is.
- Code makes it pass — the easy part, if the first two were honest.
docs/contributing.md has the rules and a worked example.
Development
npm run typecheck
npm test # everything, ~300ms
npm run coverage
npm run buildScopes, each runnable on its own (npm run test:unit, …):
| Scope | Covers | Knows about |
|---|---|---|
| unit | replay, rollup, board — pure folds over event arrays | nothing external |
| adapter | Jira JSON mapping, date formats, error translation | Jira's wire format |
| init | jt init, store creation, version refusal, corrupt lines | the filesystem |
| integration | the use cases: pull, push, verify, move, offline tracking | ports only |
| e2e | the MCP server over the real protocol | ports only |
The whole suite runs with the VPN off, by design. Every external thing is a port
(src/domain/ports.ts) with a fake in test/fakes.ts, so
nothing mocks a module — tests pass a different object in. Reaching for vi.mock means a
dependency isn't injected yet.
Layering, enforced by convention and checked in review:
delivery/ → adapters/ → app/ → domain/ imports point inwardA Jira upgrade is a new file in adapters/jira/; the rollup, ledger, replay and views never see a
Jira payload, so they can't break.
License
MIT
