# project-hub

Multi-repo workspace manager that unifies local git state and GitLab forge data (MRs, pipelines, vulnerabilities) into a single **MCP server + web dashboard**.

Designed for engineers managing many repos under one parent directory.

## Features

- **Unified view** of git status, merge requests, pipelines, and vulnerabilities across all repos
- **MCP server** (stdio) for Claude Code integration — ask "fix all vulns" and the agent has everything it needs
- **Web dashboard** (htmx) with real-time sync, sortable tables, persistent filters, event notifications
- **Smart defaults** — MCP returns only actionable vulns (high/critical + detected) with scanner solutions
- **Non-destructive** — conflict detection via `git merge-tree`, no worktree mutations
- **Background refresh** — async TTL-based cycle, diffs against SQLite cache to generate events

## Quick start

```bash
cd ~/Projects                    # parent dir containing your repos
uvx project-hub init             # creates .project-hub/
uvx project-hub auth login gitlab.com  # store your PAT
uvx project-hub                  # launch dashboard (opens browser)
```

### Use as MCP server (Claude Code)

From PyPI:

```bash
claude mcp add --scope user project-hub -- uvx project-hub --mcp
```

From source (picks up local changes without cache issues):

```bash
claude mcp add --scope user project-hub -- \
  uv run --project /path/to/project-hub project-hub --mcp
```

Installing with `--scope user` is safe — the server adapts to the current directory:

| Directory state | Behavior |
|----------------|----------|
| Has `.project-hub/` | Active — full tools + refresh |
| Parent has `.project-hub/` | Disabled — zero tools (sub-repo) |
| Has `.no-project-hub` | Disabled — zero tools (opt-out) |
| Neither | Dormant — only `init` tool |

```bash
touch ~/some-project/.no-project-hub   # force disable in a specific dir
```

Then in Claude Code, the agent can call tools like `list_repos`, `get_vulnerabilities`, `pull_safe`, etc.

## MCP tools

| Tool | Description |
|------|-------------|
| `init` | Bootstrap `.project-hub/` workspace |
| `list_repos` | List discovered repos with forge info |
| `repo_status` | Git status (branch, ahead/behind, dirty) |
| `fetch_all` | Trigger full refresh cycle |
| `pull_safe` | Pull with conflict detection |
| `list_mrs` | Merge requests (substring repo filter) |
| `list_pipelines` | Pipelines (substring repo filter) |
| `get_vulnerabilities` | Vulns — defaults to high/critical + detected, includes solutions |
| `get_pipeline_jobs` | Jobs for a specific pipeline |
| `acknowledge_alerts` | Mark events as seen |
| `auth_status` | Check forge authentication |
| `auth_login` | Store PAT for a forge instance |
| `configure_forge` | Configure forge host mapping |

## Web dashboard

| Route | Description |
|-------|-------------|
| `/` | Dashboard with repo cards, events panel |
| `/mrs` | Merge requests table (mine/all filter) |
| `/pipelines` | Pipelines table (failed only filter) |
| `/vulns` | Vulnerabilities table (severity + state filters) |
| `/settings` | Exclude/include repos, rescan workspace |

All filters and table sort are persisted in localStorage.

## Architecture

Single Python process, dual interfaces: MCP (stdio) + HTTP (FastAPI/htmx). Background asyncio task refreshes git+forge data on a TTL cycle, diffs with SQLite cache to generate events.

```
project_hub/
  cli.py              # Click entry points
  core/
    workspace.py       # Orchestrator: discover -> refresh loop -> stop
    cache.py           # SQLite (WAL mode, aiosqlite)
    config.py          # YAML config with defaults
    discovery.py       # Scan for .git/ repos, classify forges
    models.py          # Dataclasses: Repo, MR, Pipeline, Vulnerability, Event
    events.py          # Diff old vs new state -> Event list
  gitlab/
    client.py          # python-gitlab + GraphQL (vulnerabilities)
    auth.py            # PAT resolution chain
  git/
    ops.py             # fetch, status, pull_safe (merge-tree conflict detection)
  mcp/
    server.py          # FastMCP tools + resources
  web/
    app.py             # FastAPI factory, routes, htmx fragments
    templates/         # Jinja2 templates
    static/            # CSS + vendored JS
```

## Requirements

- Python 3.10+
- Git 2.38+ (for `git merge-tree --write-tree`)
- GitLab PAT with `read_api` scope

## Testing

```bash
uv run pytest tests/ -v
```

## License

[Unlicense](https://unlicense.org/) — public domain.
