Skip to content

Commit 4b6b6e0

Browse files
authored
feat(integrations): native GitHub integration
Squashed PR #128: native GitHub integration with two-way metadata sync, create-and-link, and dual-source mention autocomplete.
1 parent c81aedb commit 4b6b6e0

154 files changed

Lines changed: 9170 additions & 3181 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

AGENTS.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,7 @@ kagan/
2121
│ ├── chat/ # CLI chat REPL: ACP streaming, commands, sessions
2222
│ ├── crypto/ # X25519 key exchange, TLS, tokens, QR
2323
│ ├── wire/ # (compat shim) Re-exports envelope types
24-
│ ├── integrations/ # GitHub integration
25-
│ └── plugins/ # Plugin system (entry-point based)
24+
│ └── integrations/ # Typed native integrations (GitHub, future: Jira/Linear)
2625
├── packages/
2726
│ ├── vscode/ # VS Code extension: chat participant, tree view, SCM, reviews
2827
│ ├── web/ # React 19 + jotai + Tailwind CSS 4 web dashboard (SPA)
@@ -48,6 +47,7 @@ kagan/
4847
| Wire protocol change | `src/kagan/server/responses.py` | Response models → JSON Schema → TypeScript via `scripts/generate_wire_types.py` |
4948
| Web UI feature | `packages/web/src/` | React 19 + jotai + Tailwind CSS 4 |
5049
| API endpoint | `src/kagan/server/_routes.py` | Starlette routes via FastMCP |
50+
| Add integration | `src/kagan/core/integrations/` | Implement Integration protocol, register in `all_enabled()` |
5151
| VS Code feature | `packages/vscode/src/providers/` | One provider per VS Code API surface |
5252
| VS Code command | `packages/vscode/src/commands/` | Register in `extension.ts` |
5353
| Modify prompt system | `src/kagan/core/_prompts.py` | Three-layer resolution: dotfile → defaults + behavioral → additional instructions |

docs/concepts/architecture-overview.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,9 +79,9 @@ Interactive runs open in your preferred editor or terminal multiplexer:
7979

8080
Set globally via `attached_launcher`, or override per task.
8181

82-
## Plugins
82+
## Integrations
8383

84-
GitHub import is a native feature. A plugin system exists for third-party integrations but is early-stage — see [Plugins](../reference/plugins.md) for current status and how to request new integrations.
84+
GitHub import is a native integration ([guide](../guides/github.md)). New trackers (Jira, Linear, …) are added as new modules under `kagan.core.integrations`; there is no plugin system.
8585

8686
## Data
8787

docs/guides/github.md

Lines changed: 94 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1,102 +1,143 @@
11
---
2-
title: Import from GitHub
3-
description: Bring GitHub issues into Kagan as tasks in a few steps
2+
title: GitHub integration
3+
description: Two-way metadata sync, create-and-link, and `#`-mention autocomplete for GitHub Issues
44
icon: material/github
55
tags:
66
- github
7-
- import
7+
- integration
88
---
99

10-
# Import from GitHub
10+
# GitHub integration
1111

12-
Kagan has built-in GitHub issue import. Bring existing issues onto your board in a few steps.
12+
Kagan integrates with GitHub Issues at three points:
13+
14+
1. **Import** issues from a repository as Kagan tasks.
15+
2. **Create-and-link** — when creating a Kagan task, optionally link it to an existing issue or create a fresh issue from the task.
16+
3. **`#`-mention autocomplete** — type `#` anywhere in a Kagan text field to insert a link to a Kagan task or a GitHub issue.
17+
18+
The integration uses the GitHub CLI (`gh`) for auth — no token plumbing.
1319

1420
## What you need
1521

1622
- GitHub CLI installed: <https://cli.github.com>
17-
- Signed in on your machine: `gh auth login`
18-
- A Kagan project already created
23+
- Signed in: `gh auth login`
24+
- A Kagan project linked to a Git repo whose `origin` points at GitHub
1925

20-
## Import in the TUI
26+
## How sync works
2127

22-
GitHub import uses a two-step flow:
28+
Once a task is linked to a GitHub issue, Kagan keeps the following fields in sync **bidirectionally** on every pull/push:
2329

24-
**Step 1 — Filter:**
30+
| Field | Direction |
31+
| ------------------------- | -------------------------------------------- |
32+
| Title | both ways |
33+
| Body / description | both ways, **verbatim** — no scaffolding |
34+
| Priority labels | both ways (`priority:critical/high/medium/low`) |
35+
| Acceptance criteria | both ways via a tagged comment (see below) |
2536

26-
1. Open your project board in `kagan`
27-
1. Open Actions with `.`
28-
1. Run `github import`
29-
1. Enter your repository in `owner/repo` format
30-
1. Choose issue state (`open`, `closed`, or `all`)
31-
1. Optionally enter comma-separated labels to filter by (e.g. `bug, feature`)
32-
1. Optionally set a limit (default 100)
33-
1. Press `Enter` to preview matching issues
37+
What does **not** sync:
3438

35-
**Step 2 — Select:**
39+
- **Status / lifecycle.** Moving a Kagan task to `DONE` does not close the GitHub issue. Closing an issue on GitHub does not move the Kagan task. They are independent concepts — a Kanban column is not the same as an issue lifecycle state.
40+
- **Comments** other than the Kagan-managed acceptance-criteria comment.
41+
- **Assignees, milestones, projects** — not in scope for this integration.
3642

37-
1. Review the list of matching issues — already-synced ones are shown with `(synced)` and deselected by default
38-
1. Use `Space` to toggle individual issues, `a` to select all, `n` to deselect all
39-
1. Press `Enter` to import selected issues, or `Esc` to go back and adjust filters
43+
The body is stored exactly as it appears on GitHub. Kagan does not prepend the URL, inject `[label]` tags, or add any other scaffolding to the description. Round-trips are stable.
4044

41-
Kagan shows a summary with created, skipped, and error counts.
45+
## Acceptance criteria
4246

43-
## Preview before import
47+
GitHub already has a checklist convention (`- [ ]` / `- [x]`). Kagan uses it in two ways:
4448

45-
See what issues match your filters before committing to the import:
49+
1. **First import** — checklist lines in the issue body seed the Kagan task's acceptance criteria. The body is left untouched.
50+
2. **Subsequent edits** — when criteria change in Kagan, Kagan upserts a single comment on the issue tagged with `<!-- kagan:acceptance-criteria -->`. The comment body is a clean checklist. On every pull, if that comment exists Kagan re-derives criteria from it; otherwise it falls back to the body seed.
4651

47-
```bash
48-
kagan plugins preview github --repo octocat/hello-world
49-
```
52+
The issue body is never modified by criteria sync — only the tagged comment is.
5053

51-
Optional flags:
54+
## Create and link
5255

53-
- `--state open|closed|all`
54-
- `--label <label>` (repeatable) — filter by one or more labels
55-
- `--limit <n>` — cap the number of issues returned (default 100)
56+
When creating a Kagan task on any surface (TUI, web, VS Code, MCP, chat), the `github_issue` field accepts:
57+
58+
- empty / `none` → no link.
59+
- `42` / `#42` → link to the existing issue in the project's linked repo.
60+
- `owner/repo#42` → link to an issue in a specific repo.
61+
- `new` → create a new GitHub issue from the task's title and description, then link.
5662

57-
Output shows issue number, title, state, labels, and whether the issue is already synced.
63+
The link is stored on the task as `<owner>/<repo>#<number>` and is what every later sync operation keys on.
5864

59-
## Import from CLI
65+
## Import
66+
67+
### CLI
6068

6169
```bash
62-
kagan plugins sync github --repo octocat/hello-world
70+
kagan import github --repo owner/repo
6371
```
6472

6573
Optional flags:
6674

6775
- `--state open|closed|all`
68-
- `--label <label>` (repeatable) — filter by one or more labels
69-
- `--limit <n>` — max issues to fetch (default 100)
70-
- `--issues 1,2,42` — import only specific issue numbers
76+
- `--label <label>` (repeatable)
77+
- `--limit <n>` (default 100)
78+
- `--issues 1,2,42` — import specific numbers only
7179

72-
Examples:
80+
### TUI
7381

74-
```bash
75-
# Import only bug issues
76-
kagan plugins sync github --repo octocat/hello-world --label bug
82+
Press `.` for Actions → run `github import` → enter repo (auto-detected from `origin`), state, optional labels → preview list with toggles → import.
7783

78-
# Import multiple label filters
79-
kagan plugins sync github --repo octocat/hello-world --label bug --label priority:high
84+
### Web
8085

81-
# Import specific issues by number
82-
kagan plugins sync github --repo octocat/hello-world --issues 12,34,56
83-
```
86+
Click **Import from GitHub** on the board toolbar. Same fields, same preview, same toggles.
87+
88+
### VS Code
89+
90+
Command palette → **Kagan: Import from GitHub**. Same flow.
91+
92+
### MCP
93+
94+
`github_preview` and `github_sync` tools mirror the HTTP routes 1:1. Useful for orchestrator agents and external MCP clients.
95+
96+
### Chat
97+
98+
Ask the orchestrator: *"import open issues from owner/repo"*. The orchestrator calls `github_sync` via MCP.
99+
100+
## `#`-mention autocomplete
101+
102+
Type `#` in any Kagan text field — task description, acceptance criterion, chat message, code editor (VS Code) — and a typeahead opens listing matches from two sources:
103+
104+
- **Kagan tasks** in the current project (matched by short id and title).
105+
- **GitHub issues** in the linked repo (matched by number and title).
106+
107+
Pick a Kagan task → inserts `kagan#<short_id>`. Pick a GitHub issue → inserts `#<number>`. Both forms are clickable in rendered markdown:
108+
109+
- `kagan#abc12345` → opens the task in-app.
110+
- `#42` → opens the issue on GitHub.
111+
112+
If the project has no linked GitHub repo, the typeahead still works — Kagan-only.
113+
114+
## Cross-client parity
115+
116+
Every capability above is reachable from every client:
117+
118+
| Capability | CLI | TUI | Web | VS Code | Chat | MCP |
119+
| ---------------- | --- | --- | --- | ------- | ---- | --- |
120+
| Import |||||||
121+
| Create-and-link |||||||
122+
| `#`-mention |||||||
123+
124+
(CLI does not have a generic `task create` command today — task creation is via the TUI or any of the API-driven clients.)
84125

85126
## Label mapping
86127

87-
These labels map automatically when importing:
128+
These labels auto-map to Kagan priorities on import and on push-back:
88129

89130
- `priority:critical`, `priority:high`, `priority:medium`, `priority:low`
90131

91-
Other labels are kept in the task description as tags.
132+
If the label doesn't exist on the repo when Kagan first sets it, Kagan creates it (`gh label create`) with a sensible default colour.
92133

93134
## Troubleshooting
94135

95-
- `GitHub CLI (gh) not found` -> install from <https://cli.github.com>
96-
- `GitHub CLI not authenticated` -> run `gh auth login`
97-
- `repo must be in owner/repo format` -> use `owner/repo`, for example `octocat/hello-world`
136+
- `GitHub CLI (gh) not found` install from <https://cli.github.com>.
137+
- `GitHub CLI not authenticated` `gh auth login`.
138+
- `repo must be in owner/repo format` use `owner/repo`, e.g. `octocat/hello-world`.
98139

99-
For a full environment check, run:
140+
For a full environment check:
100141

101142
```bash
102143
kagan doctor

docs/index.md

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,10 +84,9 @@ ______________________________________________________________________
8484
| Use chat REPL or TUI overlay | [Chat guide](guides/chat.md) |
8585
| Understand ACP chat sessions | [ACP session lifecycle](guides/acp-session-lifecycle.md) |
8686
| Work across multiple repos | [MCP setup — Multi-repo](guides/mcp-setup.md#multi-repo) |
87-
| Import tasks from GitHub | [Import from GitHub](guides/github.md) |
87+
| Sync tasks with GitHub Issues | [GitHub integration](guides/github.md) |
8888
| Use the web dashboard | [Web dashboard](guides/web-dashboard.md) |
8989
| Install the VS Code extension | [VS Code extension](guides/vscode-extension.md) |
90-
| Extend with plugins | [Plugins](reference/plugins.md) (early stage) |
9190
| View analytics & metrics | [Analytics](guides/analytics.md) |
9291
| Fix a known issue | [Troubleshooting](troubleshooting.md) |
9392
| All CLI flags | [CLI reference](reference/cli.md) |

docs/internal/README.md

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ docs/internal/
1515
cli.md # Click CLI
1616
mcp.md # MCP server
1717
server.md # HTTP API server (REST + SSE)
18-
plugins.md # Plugin system
1918
features/ # What the system does (behavioral catalogs)
2019
core.md # Core domain behaviors
2120
chat.md # REPL and orchestrator behaviors
@@ -24,7 +23,6 @@ docs/internal/
2423
cli.md # CLI behaviors
2524
mcp.md # MCP tool behaviors
2625
server.md # Server features (REST, SSE)
27-
plugins.md # Plugin behaviors
2826
github_import_user_experience.md # Layman-first GitHub import rollout plan
2927
testing.md # Acceptance test commandments
3028
```

docs/internal/architecture/cli.md

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@ src/kagan/cli/
2727
├── serve.py # kagan serve [--port] [--host] [--readonly]
2828
├── update.py # kagan update [--check-only] [--prerelease]
2929
├── tools.py # kagan tools enhance [prompt]
30-
├── plugins.py # kagan plugins sync|list|check
3130
├── imports.py # kagan import github (subgroup)
3231
└── web.py # kagan web (dashboard wrapper)
3332
```
@@ -59,7 +58,7 @@ src/kagan/cli/
5958
| `reset` | Reset database/state | `--force` to skip confirmation |
6059
| `update` | Self-update via pipx/pip | `--check-only`, `--prerelease` |
6160
| `tools` | LLM tool utilities | Subgroup: `enhance` |
62-
| `plugins` | Plugin management | Subgroup: `sync`, `list`, `check` |
61+
6362
| `import` | Import from external sources | Subgroup: `github` |
6463
| `serve` | Start HTTP API server | Blocks until stopped |
6564
| `web` | Start server + open browser | Convenience wrapper around `serve` |

docs/internal/architecture/core.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,26 @@ kagan/core/
9494

9595
~42 files. Flat structure with private modules (underscore prefix) and adapters sub-package.
9696

97+
## Integrations
98+
99+
Native integrations live in `kagan.core.integrations`. Each integration is a plain class that
100+
satisfies the `Integration` typing.Protocol (defined in `_base.py`): three methods — `preflight`,
101+
`preview`, and `sync`. No ABCs, no metaclasses, no entry-point discovery.
102+
103+
The module exports `all_enabled(client) -> list[Integration]`. Today it returns `[github]`.
104+
Adding a new integration (Jira, Linear, Azure DevOps) means: create a submodule, implement the
105+
three methods, register in `all_enabled()`. That is the complete API surface change required.
106+
107+
The old entry-point plugin system (ABC hierarchy, dynamic discovery, community-plugin env flag)
108+
was removed in the `refactor/native-integrations` branch. There are no backwards-compat shims.
109+
110+
The GitHub integration stores the canonical task-to-issue link on `Task.github_issue` as
111+
`<owner>/<repo>#<number>`. Body / title / priority / acceptance-criteria sync bidirectionally;
112+
status lifecycles (kanban column ↔ issue open/closed) are intentionally decoupled. Acceptance
113+
criteria sync via a comment tagged `<!-- kagan:acceptance-criteria -->` rather than rewriting
114+
the issue body. A separate `kagan.core.integrations.mentions` module powers `#`-mention
115+
autocomplete with dual-source results (kagan tasks from the local DB + GitHub issues from `gh`).
116+
97117
## Frontend Construction
98118

99119
Every frontend creates a `KaganCore` the same way. The constructor takes only `db_path`

docs/internal/architecture/mcp.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ There should be one obvious way to do it.
3434

3535
1. **One `MCPServer` instance** — created with lifespan that owns a `KaganCore`
3636
1. **Tools are plain functions**`@mcp.tool()` decorator, type hints drive the schema
37-
1. **Toolsets group by domain** — one file per domain (tasks, sessions, projects, review, settings, personas, diagnostics, plugins)
37+
1. **Toolsets group by domain** — one file per domain (tasks, sessions, projects, review, settings, personas, diagnostics, integrations)
3838
1. **Access control is a filter** — tools registered once, filtered at registration time
3939
1. **python-sdk is the framework** — no wrapper abstractions over `MCPServer`
4040
1. **STDIO transport only** — hosts launch `kagan mcp` as a subprocess
@@ -67,10 +67,10 @@ sessions.py settings.py personas.py
6767
verify_step persona_trust
6868
checkpoint_create
6969
insight_add
70-
toolsets/ toolsets/
71-
diagnostics.py plugins.py
72-
audit_list plugins_sync
73-
plugins_preflight
70+
toolsets/ toolsets/
71+
diagnostics.py integrations.py
72+
audit_list integration_sync
73+
integration_preflight
7474
7575
▼ (lifespan context)
7676
┌───────────────┐
@@ -101,7 +101,7 @@ src/kagan/server/mcp/
101101
├── settings.py # settings_get, settings_set
102102
├── personas.py # persona_inspect, persona_import, persona_export, persona_trust
103103
├── diagnostics.py # audit_list, diagnostics_get_instrumentation
104-
└── plugins.py # plugins_preview, plugins_sync, plugins_preflight
104+
└── integrations.py # integration_preview, integration_sync, integration_preflight
105105
```
106106

107107
______________________________________________________________________

0 commit comments

Comments
 (0)