Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
c7bba56
docs: spec for session ↔ ticket linking
JayantDevkar May 14, 2026
03d4b24
docs: revise session-ticket spec per code review
JayantDevkar May 14, 2026
c2d68db
feat(api): session-ticket linking — slice (a) backend
JayantDevkar May 17, 2026
42797b9
feat(frontend): session-ticket linking — slice (b) dashboard linking
JayantDevkar May 18, 2026
3db34c9
feat: session-ticket linking — slice (c) /tickets index + detail
JayantDevkar May 18, 2026
bc73e8f
feat(hooks): session-ticket linking — slice (d) branch-detect hook
JayantDevkar May 18, 2026
1e2d5e0
feat(commands): session-ticket linking — slice (e) slash command
JayantDevkar May 18, 2026
3f32964
refactor: convert link-ticket-to-session from command to skill
JayantDevkar May 18, 2026
6a1d703
fix(api): create ticket tables idempotently regardless of schema version
JayantDevkar May 18, 2026
659c591
docs: design brief for ticket-linking UI
JayantDevkar May 18, 2026
0131492
feat(frontend): surface project ↔ ticket relationship both ways
JayantDevkar May 19, 2026
9907061
feat(frontend): apply Claude Design pass 1 — provider language, 5-sta…
JayantDevkar May 19, 2026
1e78d03
refactor(frontend): design polish round 2 — kill hero-metric AI-slop,…
JayantDevkar May 19, 2026
cf2a1bc
polish(frontend): R3 close-out — 5 nits resolved after R2 critique loop
JayantDevkar May 19, 2026
e52ecda
feat: live-session fallback + Tickets-tab migration on session page
JayantDevkar May 19, 2026
fd99c41
ui: replace Archived card with Tickets on the homepage grid
JayantDevkar May 19, 2026
1a334f8
ui+docs: /tickets icon-left header + nav redesign brief
JayantDevkar May 19, 2026
cf3a30d
feat(nav): implement Direction C — icon-first compact header
JayantDevkar May 19, 2026
80ef02b
ui(nav): consistent + distinct route colors across all 12 sections
JayantDevkar May 19, 2026
b2f5532
feat(api): aggregate tickets across project checkouts via git_identity
JayantDevkar May 19, 2026
b68e7fd
refactor: unify slug vs encoded_name across dashboard data flow
JayantDevkar May 19, 2026
8bc724c
refactor(frontend): centralize remaining inline project URL builders
JayantDevkar May 19, 2026
75f532b
docs: feature definitions for agent-walkie-talkie and sync v4
JayantDevkar May 19, 2026
9ca9794
docs+release: v0.2.0 ticket linking release
JayantDevkar May 19, 2026
4597711
fix(tickets): preserve GitHub issue vs PR distinction end-to-end
JayantDevkar May 19, 2026
9781a77
docs: design brief for /tickets kanban board view
JayantDevkar May 19, 2026
956e017
refactor(tickets): address code-review findings on PR-vs-issue fix
JayantDevkar May 19, 2026
cd9ce50
ci(api): fix Python Lint + Unit Test failures from PR-vs-issue cleanup
JayantDevkar May 19, 2026
1480e83
ci(api): apply `ruff format` to satisfy the formatter-check CI step
JayantDevkar May 19, 2026
80bd247
fix(tickets): honest empty-state copy + inline install hint for the s…
JayantDevkar May 19, 2026
8a857b8
skill(link-ticket-to-session): teach the agent issue vs PR semantics
JayantDevkar May 19, 2026
f0d979d
feat(tickets): add Issue/PR sub-filter under the GitHub provider chip
JayantDevkar May 19, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,11 @@ out/
.DS_Store
Thumbs.db

# Screenshots/images
# Screenshots/images — ignored at the repo root (we accidentally collect a lot
# of them there), but ALLOWED under docs/design-briefs/ so design iteration
# artifacts (mockups, critique notes) stay versioned with the feature.
*.png
!docs/design-briefs/**/*.png

# Python
__pycache__/
Expand Down
153 changes: 153 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
# Changelog

All notable changes to Claude Code Karma are documented here.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/)
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [0.2.0] — 2026-05-19

The "tickets" release. Karma now understands work as well as activity:
attach Linear / Jira / GitHub Issues to Claude Code sessions, then see
the dashboard pivot around tickets the same way it pivots around projects.

### Added

- **Session ↔ Ticket linking** for Linear, Jira, and GitHub Issues.
Karma stays read-only — it stores the link and caches metadata, never
writes back to your ticket provider.
- **Three ways to link a session to a ticket:**
- **Dashboard paste** — open a session, paste a ticket URL or key into
the Tickets section, done.
- **Slash command / skill** — `/link-ticket-to-session ABC-123` in a
Claude Code session. The agent fetches title + status via your
Linear / Atlassian / GitHub MCP server (if installed) and posts the
link to karma. Honors `KARMA_API_URL` for non-default hosts.
- **Branch-name auto-detect** (opt-in hook) — when you start a session
on `feat/LINEAR-123-foo`, the link is created automatically. Silent
on every failure; never blocks `SessionStart`.
- **`/tickets`** index page with provider, project, and search filters.
- **`/tickets/{provider}/{key}`** detail page listing every linked
session for a ticket, with live-session enrichment for unindexed
active sessions.
- **Tickets tab on every project page** showing every ticket touched by
any session in that project — and across **every checkout of the same
repo** (worktrees, subdir CWDs, submodules) via the new `git_identity`
column. So a session linked from `claude-karma/frontend/` shows the
ticket on the main `claude-karma` project too.
- **GitHub key heuristic** — tickets like `owner/repo#42` surface under
any project whose `git_identity` is `owner/repo`, even if no local
session has linked them yet. Useful when a teammate has linked the
ticket on a different machine.
- **Cross-encoded ticket aggregation** powered by `projects.git_identity`
(canonical `owner/repo` lowercase, derived from `git remote get-url
origin` at index time). Lets karma treat every checkout of the same
repo as one logical project for ticket views.

### Changed

- **Frontend route param renamed `[project_slug]` → `[project_id]`.**
The route accepts either form (slug or `encoded_name`), so existing
URLs and bookmarks continue to work. The new name is honest about
what it accepts.
- **All API endpoints with a project filter now accept either form**
uniformly via the new `safely_resolve_project()` helper. Applied to
`/tickets`, `/skills`, `/skills/usage`, `/commands` (5 endpoints),
`/agents` filters, and `/live-sessions/project/{id}`. Endpoints that
previously matched `encoded_name` exactly and returned an empty list
for slugs now resolve cleanly.

### Fixed

- **`/projects → card → Tickets tab` showed empty** for sessions linked
via the dashboard. Cause: project cards link via slug, the tickets
API matched `encoded_name` exactly. Fix: unified data flow with a
clean "slug at the boundary, encoded_name inside" architecture across
every project-by-identifier endpoint. Locked in by a regression test.
- **GitHub PRs were stored with `/issues/N` URLs** instead of `/pull/N`.
Cause: the ticket parser hard-coded `/issues/` in the canonical URL
it built, throwing away which path segment the input URL had used.
Fix: parser now captures `(?P<kind>issues|pull)` and uses the
captured segment in the canonical URL. The frontend grew a small
`PR` pill (with GitHub's pull-request glyph) next to the GH provider
badge so issues and PRs are visually distinguishable everywhere.
Old rows self-heal on re-link; users wanting immediate repair can hit
the new `POST /admin/repair-github-urls` endpoint (see Upgrading).

### Internal

- **Schema v12.** Adds `projects.git_identity TEXT` + index. Migration
runs automatically on first start (idempotent against any phantom
column from out-of-band branches) and nudges session mtimes so the
periodic indexer backfills `git_identity` within ~5 minutes. To
populate immediately: `POST /admin/reindex`.
- **New service module** `api/services/git_identity.py` —
`normalize_git_url()` (pure parser, handles https / ssh / scp-style
with or without `.git`) and `read_git_identity()` (timeout-guarded
`git remote get-url origin` shellout).
- **New helper** `safely_resolve_project()` in `api/routers/projects.py`
— filter-friendly variant of `resolve_project_identifier` that returns
the raw input verbatim on unknown identifiers (so downstream queries
yield empty lists, not 404s).
- **New frontend helper** `src/lib/utils/project-url.ts` —
`projectHref()` + `projectHrefFromSession()` centralize the
`slug || encoded_name` policy in one place. Migrated 5 call sites
(`GlobalSessionCard`, `LiveSessionsTerminal`, `LiveSessionsSection`,
`CommandPalette`, plans page, `ConversationOverview`).

### Tests

- 1580 passing (was 1474). Added: 40 ticket endpoint tests, 23
`normalize_git_url` parser tests, 7 `safely_resolve_project` /
`resolve_project_identifier` unit tests, plus parser, enrichment,
branch-detector hook (299 LOC), and schema-migration regression tests.

### Upgrading from 0.1.x

No manual steps required. On first start:

1. Schema migrates v11 → v12 automatically. The migration is idempotent
and safe to re-run.
2. `projects.git_identity` is backfilled by the periodic indexer (default
interval: 5 minutes). To trigger immediately:
`curl -X POST http://localhost:8000/admin/reindex`.
3. The route rename `[project_slug]` → `[project_id]` is internal —
existing URLs continue to work.

**Optional: repair stale GitHub PR URLs.** If you linked GitHub PRs in
0.1.x, their stored URLs point at `/issues/N` even though they were
PRs. Links still work (GitHub redirects), but the new `PR` pill won't
show. Rows self-heal on re-link, or you can repair them in one shot:

```bash
curl -X POST http://localhost:8000/admin/repair-github-urls
# {"status":"ok","rewritten":N}
```

The repair is conservative — it only rewrites rows whose
`status='MERGED'` (a state unique to PRs). Open or closed-unmerged PRs
remain ambiguous from cached data alone and self-heal on next re-link.

If you used the syncthing prototype in an earlier branch, the
`sync_*` tables and any `jayantdevkar-claude-code-karma`-style project
rows are left over from that prototype (not part of this branch's
schema). Safe to delete manually if you want a clean dashboard:

```sql
DROP TABLE IF EXISTS sync_subscriptions;
DROP TABLE IF EXISTS sync_removed_members;
DROP TABLE IF EXISTS sync_events;
DROP TABLE IF EXISTS sync_projects;
DROP TABLE IF EXISTS sync_members;
DROP TABLE IF EXISTS sync_teams;
DELETE FROM projects WHERE encoded_name NOT LIKE '-%';
```

---

## [0.1.0] — Earlier

Initial public release. The first 224 stars 🌟.

See [git history](https://github.com/JayantDevkar/claude-code-karma/commits/main)
for changes prior to the introduction of this changelog.
103 changes: 103 additions & 0 deletions FEATURES.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
- [Agents](#agents)
- [Skills](#skills)
- [Plans](#plans)
- [Tickets](#tickets)
- [Settings](#settings)
- [Additional Features](#additional-features)

Expand Down Expand Up @@ -442,6 +443,108 @@ Browse implementation plans from Claude Code plan mode sessions.

---

## Tickets

**Routes:** `/tickets`, `/tickets/[provider]/[external_key]`

Attach Claude Code sessions to tickets in Linear, Jira, or GitHub Issues
and audit what work was done for each ticket across sessions. Karma is
**read-only** — it stores the link and caches metadata (title, status)
but never writes back to your ticket provider.

### Three Ways to Link

| Path | Where | How |
|------|-------|-----|
| **Dashboard paste** | Tickets section on any session page | Paste a URL or key (e.g. `LINEAR-123`, `octocat/repo#42`, full Linear/Jira/GitHub URL). Karma stores the link; if you previously linked the same ticket from another session, metadata is reused. |
| **Slash command / skill** | Inside any Claude Code session | `/link-ticket-to-session ABC-123` or natural language ("link this session to LINEAR-42"). The agent uses your Linear/Atlassian/GitHub MCP server to fetch title + status, then POSTs the link to karma. If no MCP is installed, the link still works — just without cached title/status. |
| **Branch-name auto-detect** | Hook fired at `SessionStart` | Opt-in hook (`ticket_branch_detector.py`) watches your git branch. Branches matching configured regex patterns (e.g. `feat/LINEAR-123-foo`) auto-create the link silently. Failures never block `SessionStart`. |

See [SETUP.md → Tier 4](SETUP.md#tier-4-ticket-linking-optional)
for installation steps.

### Tickets Index (`/tickets`)

- **Provider columns** — Linear / Jira / GitHub badges with brand colors
- **Search** — Filter by external key or title substring
- **Provider filter** — Show only Linear / Jira / GitHub tickets
- **Project filter** — Restrict to tickets touched by sessions in a
specific project
- **Session counts** — How many sessions each ticket spans
- **Last linked** — Most recent link timestamp per ticket

### Ticket Detail (`/tickets/[provider]/[external_key]`)

- **Header** — Ticket key + cached title + status badge + external link
to provider
- **Linked sessions list** — Every session this ticket is attached to,
with title, project, and timestamp
- **Live-session enrichment** — Sessions still being written (not yet
indexed) are surfaced via the live-sessions tracker so you don't miss
in-flight work
- **Orphan-safe** — Links to sessions that aren't in the index yet still
appear (e.g. just-started sessions); their fields populate as soon as
the indexer catches up
- **GitHub-style keys** — Keys containing `/` and `#` (like
`owner/repo#42`) are URL-encoded transparently

### Project Tickets Tab

Every project page now has a **Tickets** tab showing every ticket touched
by any session in that project — and **across every checkout of the same
git repo**. This means a ticket linked from a session inside
`claude-karma/frontend/` also shows up on the main `claude-karma`
project's Tickets tab (and on `claude-karma/docs/design/`, any worktree,
etc.).

The aggregation key is `git_identity` (canonical `owner/repo`
lowercase), derived at index time from each project's `git remote
get-url origin`. For projects without a local git remote (sync-imported
or freshly-`git init`'d), the tab falls back to per-encoded_name match.

For GitHub specifically, tickets whose `external_key` starts with the
project's `git_identity` (e.g. `octocat/repo#42` under a project with
`git_identity=octocat/repo`) surface on the tab **even if no local
session has linked them yet** — useful when a teammate has linked the
ticket on a different machine.

### Session Tickets Section

On any session page, the Tickets section provides:

- **Link input** with five UI states: idle, valid ref, fetching, linked,
error — clear feedback at every step
- **Linked ticket badges** with provider color, key, title preview, and
status dot
- **Unlink** action with optimistic update + undo toast (6s grace period
before the DELETE actually fires)
- **External link** to open the ticket in its provider

### Link Source Precedence

When the same `(session, ticket)` pair is touched by multiple link paths,
karma upgrades but never downgrades the `link_source`:

```
slash_command > dashboard > branch
```

So a branch auto-link can be upgraded to `slash_command` later, but
re-running the branch hook won't override an explicit dashboard link.

### Schema & Storage

- **Tables:** `tickets` (unique on `(provider, external_key)`),
`session_tickets` (unique on `(session_uuid, ticket_id)` plus a
partial unique index on `(session_slug, ticket_id)` for resumed-session
dedup)
- **Metadata cap:** 64 KB per ticket (enforced by SQL CHECK constraint)
- **Orphan cleanup:** Background asyncio task removes
`session_tickets` rows whose `session_uuid` never materialized after
a 7-day TTL

---

## Settings

**Route:** `/settings`
Expand Down
16 changes: 16 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,22 @@ Track which skills are invoked across sessions, grouped by plugin or shown indiv
<img src="docs/screenshots/skill-detail.png" alt="Skill Detail with History" width="100%" />
</p>

### Ticket Linking (Linear / Jira / GitHub Issues)

Attach your Claude Code sessions to the tickets they were about. Karma stays read-only — it stores the link and caches the title/status, but never writes back to your ticket provider.

Three ways to link:

- **Paste a URL or key** into the Tickets section on any session page
- **Type `/link-ticket-to-session ABC-123`** (or ask the agent in natural language) in any Claude Code session — uses your Linear / Atlassian / GitHub MCP server to fetch the title
- **Auto-detect from your branch name** — opt-in `SessionStart` hook that watches for keys like `feat/LINEAR-123-foo` and links silently in the background

Then browse:

- A `/tickets` index showing every ticket touched, filterable by provider and project
- A ticket detail page listing every session linked to a given ticket
- A **Tickets tab on every project page** that aggregates across all checkouts of the same git repo — so a ticket linked from `claude-karma/frontend/` also shows on the main `claude-karma` project

### And More

- **Plans Browser** — View implementation plans and their execution status
Expand Down
Loading
Loading