project-hub 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. project_hub-0.1.0/.gitlab-ci.yml +24 -0
  2. project_hub-0.1.0/CLAUDE.md +118 -0
  3. project_hub-0.1.0/LICENSE +24 -0
  4. project_hub-0.1.0/PKG-INFO +142 -0
  5. project_hub-0.1.0/README.md +115 -0
  6. project_hub-0.1.0/project_hub/__init__.py +1 -0
  7. project_hub-0.1.0/project_hub/cli.py +301 -0
  8. project_hub-0.1.0/project_hub/core/__init__.py +0 -0
  9. project_hub-0.1.0/project_hub/core/cache.py +612 -0
  10. project_hub-0.1.0/project_hub/core/config.py +76 -0
  11. project_hub-0.1.0/project_hub/core/discovery.py +73 -0
  12. project_hub-0.1.0/project_hub/core/events.py +171 -0
  13. project_hub-0.1.0/project_hub/core/models.py +161 -0
  14. project_hub-0.1.0/project_hub/core/workspace.py +394 -0
  15. project_hub-0.1.0/project_hub/git/__init__.py +0 -0
  16. project_hub-0.1.0/project_hub/git/ops.py +184 -0
  17. project_hub-0.1.0/project_hub/gitlab/__init__.py +0 -0
  18. project_hub-0.1.0/project_hub/gitlab/auth.py +98 -0
  19. project_hub-0.1.0/project_hub/gitlab/client.py +271 -0
  20. project_hub-0.1.0/project_hub/mcp/__init__.py +0 -0
  21. project_hub-0.1.0/project_hub/mcp/server.py +643 -0
  22. project_hub-0.1.0/project_hub/web/__init__.py +0 -0
  23. project_hub-0.1.0/project_hub/web/app.py +310 -0
  24. project_hub-0.1.0/project_hub/web/static/app.js +200 -0
  25. project_hub-0.1.0/project_hub/web/static/htmx.min.js +1 -0
  26. project_hub-0.1.0/project_hub/web/static/pico.min.css +4 -0
  27. project_hub-0.1.0/project_hub/web/static/sortable.min.js +2 -0
  28. project_hub-0.1.0/project_hub/web/static/style.css +1147 -0
  29. project_hub-0.1.0/project_hub/web/static/tablesort.css +1 -0
  30. project_hub-0.1.0/project_hub/web/static/tablesort.min.js +3 -0
  31. project_hub-0.1.0/project_hub/web/templates/base.html +63 -0
  32. project_hub-0.1.0/project_hub/web/templates/dashboard.html +9 -0
  33. project_hub-0.1.0/project_hub/web/templates/dashboard_fragment.html +78 -0
  34. project_hub-0.1.0/project_hub/web/templates/events_panel_fragment.html +30 -0
  35. project_hub-0.1.0/project_hub/web/templates/loading.html +23 -0
  36. project_hub-0.1.0/project_hub/web/templates/mrs.html +23 -0
  37. project_hub-0.1.0/project_hub/web/templates/mrs_fragment.html +74 -0
  38. project_hub-0.1.0/project_hub/web/templates/pipeline_jobs_fragment.html +16 -0
  39. project_hub-0.1.0/project_hub/web/templates/pipelines.html +39 -0
  40. project_hub-0.1.0/project_hub/web/templates/pipelines_fragment.html +56 -0
  41. project_hub-0.1.0/project_hub/web/templates/repo.html +65 -0
  42. project_hub-0.1.0/project_hub/web/templates/repo_mrs_fragment.html +24 -0
  43. project_hub-0.1.0/project_hub/web/templates/repo_pipelines_fragment.html +30 -0
  44. project_hub-0.1.0/project_hub/web/templates/repo_vulns_fragment.html +22 -0
  45. project_hub-0.1.0/project_hub/web/templates/settings.html +56 -0
  46. project_hub-0.1.0/project_hub/web/templates/sync_status_fragment.html +6 -0
  47. project_hub-0.1.0/project_hub/web/templates/toast.html +4 -0
  48. project_hub-0.1.0/project_hub/web/templates/vulns.html +58 -0
  49. project_hub-0.1.0/project_hub/web/templates/vulns_fragment.html +72 -0
  50. project_hub-0.1.0/pyproject.toml +51 -0
  51. project_hub-0.1.0/tests/conftest.py +34 -0
  52. project_hub-0.1.0/tests/test_cache.py +155 -0
  53. project_hub-0.1.0/tests/test_cli.py +49 -0
  54. project_hub-0.1.0/tests/test_config.py +67 -0
  55. project_hub-0.1.0/tests/test_discovery.py +66 -0
  56. project_hub-0.1.0/tests/test_dormancy.py +35 -0
  57. project_hub-0.1.0/tests/test_events.py +233 -0
  58. project_hub-0.1.0/tests/test_git_ops.py +78 -0
  59. project_hub-0.1.0/tests/test_gitlab_auth.py +112 -0
  60. project_hub-0.1.0/tests/test_gitlab_client.py +252 -0
  61. project_hub-0.1.0/tests/test_mcp_tools.py +272 -0
  62. project_hub-0.1.0/tests/test_pull_status.py +34 -0
  63. project_hub-0.1.0/tests/test_ui_state.py +58 -0
  64. project_hub-0.1.0/tests/test_web.py +194 -0
  65. project_hub-0.1.0/tests/test_web_redesign.py +115 -0
  66. project_hub-0.1.0/tests/test_workspace.py +386 -0
  67. project_hub-0.1.0/uv.lock +1491 -0
@@ -0,0 +1,24 @@
1
+ image: python:3.12-slim
2
+
3
+ stages:
4
+ - test
5
+ - publish
6
+
7
+ test:
8
+ stage: test
9
+ before_script:
10
+ - apt-get update -qq && apt-get install -y -qq git >/dev/null
11
+ - pip install uv
12
+ script:
13
+ - uv sync
14
+ - uv run pytest tests/ -v
15
+
16
+ publish:
17
+ stage: publish
18
+ rules:
19
+ - if: $CI_COMMIT_TAG =~ /^v\d+/
20
+ before_script:
21
+ - pip install uv
22
+ script:
23
+ - uv build
24
+ - uv publish --token $PYPI_TOKEN
@@ -0,0 +1,118 @@
1
+ # CLAUDE.md
2
+
3
+ This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4
+
5
+ ## What is project-hub
6
+
7
+ 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 on GitLab from a single parent directory.
8
+
9
+ Single Python process, dual interfaces: MCP (stdio for Claude Code) + HTTP (FastAPI/htmx dashboard). Background asyncio task refreshes git+forge data on a TTL cycle, diffs with SQLite cache to generate events.
10
+
11
+ ## Build & Run
12
+
13
+ ```bash
14
+ uv sync # install deps
15
+ uv run project-hub --help # CLI help
16
+ uv run project-hub # standalone dashboard (opens browser)
17
+ uv run project-hub --mcp # MCP mode (stdio, embeds webui)
18
+ uv run project-hub init # bootstrap .project-hub/ workspace
19
+ uv run project-hub auth login <instance> # store PAT
20
+ uv run project-hub auth status
21
+ uv run project-hub exclude <repo>
22
+ uv run project-hub include <repo>
23
+ uv run project-hub sync-now # force immediate refresh
24
+ ```
25
+
26
+ ## Testing
27
+
28
+ ```bash
29
+ uv run pytest tests/ -v # full suite
30
+ uv run pytest tests/test_events.py -v # single file
31
+ uv run pytest tests/test_cache.py -k "test_store_mrs" -v # single test
32
+ ```
33
+
34
+ `asyncio_mode = "auto"` is set in pyproject.toml — async tests run without explicit markers.
35
+
36
+ ## Architecture
37
+
38
+ ```
39
+ project_hub/
40
+ cli.py # Click entry points (standalone / --mcp / auth / init / exclude / include)
41
+ core/
42
+ workspace.py # Workspace orchestrator: start → discover → refresh loop → stop
43
+ # Also: WorkspaceState enum + detect_state() for dormancy
44
+ cache.py # SQLite (WAL mode, aiosqlite) — MRs, pipelines, vulns, events
45
+ config.py # YAML config loading with defaults
46
+ discovery.py # Scan CWD children for .git/, parse remotes, classify forges
47
+ models.py # All dataclasses: Repo, MR, Pipeline, Vulnerability, Event, etc.
48
+ events.py # Diff old vs new state → Event list + filter_events (mine/all/none)
49
+ gitlab/
50
+ client.py # python-gitlab + gql wrapper (one instance per forge host)
51
+ auth.py # PAT resolution: auth.json → GITLAB_TOKEN env → python-gitlab.cfg
52
+ git/
53
+ ops.py # fetch, status, pull_safe (conflict detection via git merge-tree)
54
+ mcp/
55
+ server.py # FastMCP server: lifespan, tools, resources, notifications
56
+ web/
57
+ app.py # FastAPI app factory, routes, htmx fragments
58
+ templates/ # Jinja2: dashboard, MRs, vulns, pipelines, events
59
+ static/ # CSS + vendored htmx.min.js
60
+ ```
61
+
62
+ ### Key design decisions
63
+
64
+ - **Core API is framework-agnostic.** `workspace.py`, `cache.py`, `events.py` have zero MCP/FastAPI imports. Both interfaces are thin adapters.
65
+ - **stdout reserved for MCP protocol.** All logging goes to stderr (`logging.basicConfig(stream=sys.stderr)`). No transitive dependency may write to stdout.
66
+ - **python-gitlab is synchronous.** All forge calls wrapped with `asyncio.to_thread()` and bounded by a semaphore (default 10 concurrent).
67
+ - **Conflict detection is non-destructive.** Uses `git merge-tree --write-tree` (Git 2.38+ required) — in-memory merge, no worktree mutations.
68
+ - **Event persistence.** Events stored in SQLite with `seen` boolean, survive across sessions. `AUTH_EXPIRED` events always bypass watch filters.
69
+
70
+ ### Dormancy (4 states)
71
+
72
+ | Condition | State | MCP behavior |
73
+ |---|---|---|
74
+ | `.no-project-hub` in CWD | Disabled (opt-out) | Zero tools |
75
+ | `.project-hub/` in CWD | Active | Full tools + refresh |
76
+ | `.project-hub/` in parent only | Disabled (sub-repo) | Zero tools |
77
+ | Neither | Dormant | Only `init` tool |
78
+
79
+ `.no-project-hub` always wins. After `init`, the server sends `send_tool_list_changed()` so the client re-fetches tools.
80
+
81
+ ### Refresh cycle (Workspace._do_refresh)
82
+
83
+ 1. `git fetch --all` on all repos (parallel subprocess)
84
+ 2. Fetch MRs, pipelines, vulns per forge repo (parallel, semaphore-bounded)
85
+ 3. Diff new state against cache → generate events
86
+ 4. Filter events by `watch` config (mine/all/none)
87
+ 5. Atomic write to cache + emit MCP resource notifications
88
+
89
+ ### Workspace layout
90
+
91
+ ```
92
+ .project-hub/
93
+ config.yaml # auto-generated on init
94
+ cache.db # SQLite (created on first refresh)
95
+ workspace.md # manual business notes
96
+ ```
97
+
98
+ Auth tokens: `~/.config/project-hub/auth.json` (global, keyed by forge host).
99
+
100
+ ## MCP tools
101
+
102
+ `init`, `configure_forge`, `list_repos`, `repo_status`, `fetch_all`, `pull_safe`, `list_mrs`, `list_pipelines`, `get_pipeline_jobs`, `get_vulnerabilities`, `acknowledge_alerts`, `auth_status`, `auth_login`
103
+
104
+ All tools accepting `repo?` use the **directory name** as identifier (e.g. `pa-services-gateway`).
105
+
106
+ ## MCP resources
107
+
108
+ `workspace://topology`, `workspace://status`, `workspace://alerts`, `workspace://notes`, `workspace://dashboard`
109
+
110
+ ## Web UI routes
111
+
112
+ Full pages: `/`, `/mrs`, `/vulns`, `/pipelines`, `/events`
113
+ Fragments (htmx): `/fragment/{dashboard,mrs,vulns,pipelines,events}`
114
+ Actions: `POST /action/fetch`, `POST /action/pull`, `POST /action/dismiss-event/{id}`
115
+
116
+ ## V1 scope boundaries
117
+
118
+ GitLab only (no GitHub/Forgejo). Single workspace. PAT auth only. No MR actions (approve/merge/comment). No desktop notifications. V1.1 planned: DB maintenance, OAuth device flow, multi-workspace.
@@ -0,0 +1,24 @@
1
+ This is free and unencumbered software released into the public domain.
2
+
3
+ Anyone is free to copy, modify, publish, use, compile, sell, or
4
+ distribute this software, either in source code form or as a compiled
5
+ binary, for any purpose, commercial or non-commercial, and by any
6
+ means.
7
+
8
+ In jurisdictions that recognize copyright laws, the author or authors
9
+ of this software dedicate any and all copyright interest in the
10
+ software to the public domain. We make this dedication for the benefit
11
+ of the public at large and to the detriment of our heirs and
12
+ successors. We intend this dedication to be an overt act of
13
+ relinquishment in perpetuity of all present and future rights to this
14
+ software under copyright law.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19
+ IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
20
+ OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
21
+ ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22
+ OTHER DEALINGS IN THE SOFTWARE.
23
+
24
+ For more information, please refer to <http://unlicense.org/>
@@ -0,0 +1,142 @@
1
+ Metadata-Version: 2.4
2
+ Name: project-hub
3
+ Version: 0.1.0
4
+ Summary: Multi-repo workspace manager with MCP server + web dashboard
5
+ Project-URL: Repository, https://gitlab.com/b.rajaut/project-hub
6
+ Author-email: Baptiste Rajaut <baptiste@rajaut.fr>
7
+ License-Expression: Unlicense
8
+ License-File: LICENSE
9
+ Keywords: dashboard,git,gitlab,mcp,workspace
10
+ Classifier: Development Status :: 3 - Alpha
11
+ Classifier: Environment :: Web Environment
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: License :: OSI Approved :: The Unlicense (Unlicense)
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Topic :: Software Development :: Version Control :: Git
16
+ Requires-Python: >=3.10
17
+ Requires-Dist: aiosqlite>=0.20
18
+ Requires-Dist: click>=8.1
19
+ Requires-Dist: fastapi>=0.115
20
+ Requires-Dist: gql[requests]>=3.0
21
+ Requires-Dist: jinja2>=3.1
22
+ Requires-Dist: mcp<2,>=1.25
23
+ Requires-Dist: python-gitlab>=8.0
24
+ Requires-Dist: pyyaml>=6.0
25
+ Requires-Dist: uvicorn[standard]>=0.30
26
+ Description-Content-Type: text/markdown
27
+
28
+ # project-hub
29
+
30
+ Multi-repo workspace manager that unifies local git state and GitLab forge data (MRs, pipelines, vulnerabilities) into a single **MCP server + web dashboard**.
31
+
32
+ Designed for engineers managing many repos under one parent directory.
33
+
34
+ ## Features
35
+
36
+ - **Unified view** of git status, merge requests, pipelines, and vulnerabilities across all repos
37
+ - **MCP server** (stdio) for Claude Code integration — ask "fix all vulns" and the agent has everything it needs
38
+ - **Web dashboard** (htmx) with real-time sync, sortable tables, persistent filters, event notifications
39
+ - **Smart defaults** — MCP returns only actionable vulns (high/critical + detected) with scanner solutions
40
+ - **Non-destructive** — conflict detection via `git merge-tree`, no worktree mutations
41
+ - **Background refresh** — async TTL-based cycle, diffs against SQLite cache to generate events
42
+
43
+ ## Quick start
44
+
45
+ ```bash
46
+ # Install
47
+ uvx project-hub --help
48
+
49
+ # Or from source
50
+ uv sync
51
+ uv run project-hub --help
52
+ ```
53
+
54
+ ### Bootstrap a workspace
55
+
56
+ ```bash
57
+ cd ~/Projects # parent dir containing your repos
58
+ project-hub init # creates .project-hub/
59
+ project-hub auth login gitlab.com # store your PAT
60
+ project-hub # launch dashboard (opens browser)
61
+ ```
62
+
63
+ ### Use as MCP server (Claude Code)
64
+
65
+ ```bash
66
+ claude mcp add project-hub -- uvx project-hub --mcp
67
+ ```
68
+
69
+ Then in Claude Code, the agent can call tools like `list_repos`, `get_vulnerabilities`, `pull_safe`, etc.
70
+
71
+ ## MCP tools
72
+
73
+ | Tool | Description |
74
+ |------|-------------|
75
+ | `init` | Bootstrap `.project-hub/` workspace |
76
+ | `list_repos` | List discovered repos with forge info |
77
+ | `repo_status` | Git status (branch, ahead/behind, dirty) |
78
+ | `fetch_all` | Trigger full refresh cycle |
79
+ | `pull_safe` | Pull with conflict detection |
80
+ | `list_mrs` | Merge requests (substring repo filter) |
81
+ | `list_pipelines` | Pipelines (substring repo filter) |
82
+ | `get_vulnerabilities` | Vulns — defaults to high/critical + detected, includes solutions |
83
+ | `get_pipeline_jobs` | Jobs for a specific pipeline |
84
+ | `acknowledge_alerts` | Mark events as seen |
85
+ | `auth_status` | Check forge authentication |
86
+ | `auth_login` | Store PAT for a forge instance |
87
+ | `configure_forge` | Configure forge host mapping |
88
+
89
+ ## Web dashboard
90
+
91
+ | Route | Description |
92
+ |-------|-------------|
93
+ | `/` | Dashboard with repo cards, events panel |
94
+ | `/mrs` | Merge requests table (mine/all filter) |
95
+ | `/pipelines` | Pipelines table (failed only filter) |
96
+ | `/vulns` | Vulnerabilities table (severity + state filters) |
97
+ | `/settings` | Exclude/include repos, rescan workspace |
98
+
99
+ All filters and table sort are persisted in localStorage.
100
+
101
+ ## Architecture
102
+
103
+ 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.
104
+
105
+ ```
106
+ project_hub/
107
+ cli.py # Click entry points
108
+ core/
109
+ workspace.py # Orchestrator: discover -> refresh loop -> stop
110
+ cache.py # SQLite (WAL mode, aiosqlite)
111
+ config.py # YAML config with defaults
112
+ discovery.py # Scan for .git/ repos, classify forges
113
+ models.py # Dataclasses: Repo, MR, Pipeline, Vulnerability, Event
114
+ events.py # Diff old vs new state -> Event list
115
+ gitlab/
116
+ client.py # python-gitlab + GraphQL (vulnerabilities)
117
+ auth.py # PAT resolution chain
118
+ git/
119
+ ops.py # fetch, status, pull_safe (merge-tree conflict detection)
120
+ mcp/
121
+ server.py # FastMCP tools + resources
122
+ web/
123
+ app.py # FastAPI factory, routes, htmx fragments
124
+ templates/ # Jinja2 templates
125
+ static/ # CSS + vendored JS
126
+ ```
127
+
128
+ ## Requirements
129
+
130
+ - Python 3.10+
131
+ - Git 2.38+ (for `git merge-tree --write-tree`)
132
+ - GitLab PAT with `read_api` scope
133
+
134
+ ## Testing
135
+
136
+ ```bash
137
+ uv run pytest tests/ -v
138
+ ```
139
+
140
+ ## License
141
+
142
+ [Unlicense](https://unlicense.org/) — public domain.
@@ -0,0 +1,115 @@
1
+ # project-hub
2
+
3
+ Multi-repo workspace manager that unifies local git state and GitLab forge data (MRs, pipelines, vulnerabilities) into a single **MCP server + web dashboard**.
4
+
5
+ Designed for engineers managing many repos under one parent directory.
6
+
7
+ ## Features
8
+
9
+ - **Unified view** of git status, merge requests, pipelines, and vulnerabilities across all repos
10
+ - **MCP server** (stdio) for Claude Code integration — ask "fix all vulns" and the agent has everything it needs
11
+ - **Web dashboard** (htmx) with real-time sync, sortable tables, persistent filters, event notifications
12
+ - **Smart defaults** — MCP returns only actionable vulns (high/critical + detected) with scanner solutions
13
+ - **Non-destructive** — conflict detection via `git merge-tree`, no worktree mutations
14
+ - **Background refresh** — async TTL-based cycle, diffs against SQLite cache to generate events
15
+
16
+ ## Quick start
17
+
18
+ ```bash
19
+ # Install
20
+ uvx project-hub --help
21
+
22
+ # Or from source
23
+ uv sync
24
+ uv run project-hub --help
25
+ ```
26
+
27
+ ### Bootstrap a workspace
28
+
29
+ ```bash
30
+ cd ~/Projects # parent dir containing your repos
31
+ project-hub init # creates .project-hub/
32
+ project-hub auth login gitlab.com # store your PAT
33
+ project-hub # launch dashboard (opens browser)
34
+ ```
35
+
36
+ ### Use as MCP server (Claude Code)
37
+
38
+ ```bash
39
+ claude mcp add project-hub -- uvx project-hub --mcp
40
+ ```
41
+
42
+ Then in Claude Code, the agent can call tools like `list_repos`, `get_vulnerabilities`, `pull_safe`, etc.
43
+
44
+ ## MCP tools
45
+
46
+ | Tool | Description |
47
+ |------|-------------|
48
+ | `init` | Bootstrap `.project-hub/` workspace |
49
+ | `list_repos` | List discovered repos with forge info |
50
+ | `repo_status` | Git status (branch, ahead/behind, dirty) |
51
+ | `fetch_all` | Trigger full refresh cycle |
52
+ | `pull_safe` | Pull with conflict detection |
53
+ | `list_mrs` | Merge requests (substring repo filter) |
54
+ | `list_pipelines` | Pipelines (substring repo filter) |
55
+ | `get_vulnerabilities` | Vulns — defaults to high/critical + detected, includes solutions |
56
+ | `get_pipeline_jobs` | Jobs for a specific pipeline |
57
+ | `acknowledge_alerts` | Mark events as seen |
58
+ | `auth_status` | Check forge authentication |
59
+ | `auth_login` | Store PAT for a forge instance |
60
+ | `configure_forge` | Configure forge host mapping |
61
+
62
+ ## Web dashboard
63
+
64
+ | Route | Description |
65
+ |-------|-------------|
66
+ | `/` | Dashboard with repo cards, events panel |
67
+ | `/mrs` | Merge requests table (mine/all filter) |
68
+ | `/pipelines` | Pipelines table (failed only filter) |
69
+ | `/vulns` | Vulnerabilities table (severity + state filters) |
70
+ | `/settings` | Exclude/include repos, rescan workspace |
71
+
72
+ All filters and table sort are persisted in localStorage.
73
+
74
+ ## Architecture
75
+
76
+ 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.
77
+
78
+ ```
79
+ project_hub/
80
+ cli.py # Click entry points
81
+ core/
82
+ workspace.py # Orchestrator: discover -> refresh loop -> stop
83
+ cache.py # SQLite (WAL mode, aiosqlite)
84
+ config.py # YAML config with defaults
85
+ discovery.py # Scan for .git/ repos, classify forges
86
+ models.py # Dataclasses: Repo, MR, Pipeline, Vulnerability, Event
87
+ events.py # Diff old vs new state -> Event list
88
+ gitlab/
89
+ client.py # python-gitlab + GraphQL (vulnerabilities)
90
+ auth.py # PAT resolution chain
91
+ git/
92
+ ops.py # fetch, status, pull_safe (merge-tree conflict detection)
93
+ mcp/
94
+ server.py # FastMCP tools + resources
95
+ web/
96
+ app.py # FastAPI factory, routes, htmx fragments
97
+ templates/ # Jinja2 templates
98
+ static/ # CSS + vendored JS
99
+ ```
100
+
101
+ ## Requirements
102
+
103
+ - Python 3.10+
104
+ - Git 2.38+ (for `git merge-tree --write-tree`)
105
+ - GitLab PAT with `read_api` scope
106
+
107
+ ## Testing
108
+
109
+ ```bash
110
+ uv run pytest tests/ -v
111
+ ```
112
+
113
+ ## License
114
+
115
+ [Unlicense](https://unlicense.org/) — public domain.
@@ -0,0 +1 @@
1
+ __version__ = "0.1.0"