polycoding 0.1.0__py3-none-any.whl
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.
- cli/__init__.py +53 -0
- cli/db.py +67 -0
- cli/flow.py +187 -0
- cli/main.py +44 -0
- cli/project.py +166 -0
- cli/server.py +127 -0
- cli/utils.py +70 -0
- cli/worker.py +124 -0
- github_app/__init__.py +13 -0
- github_app/app.py +224 -0
- github_app/auth.py +137 -0
- github_app/config.py +38 -0
- github_app/installation_manager.py +194 -0
- github_app/label_mapper.py +112 -0
- github_app/models.py +112 -0
- github_app/webhook_handler.py +217 -0
- persistence/__init__.py +5 -0
- persistence/config.py +12 -0
- persistence/postgres.py +346 -0
- persistence/registry.py +111 -0
- persistence/tasks.py +178 -0
- polycoding-0.1.0.dist-info/METADATA +225 -0
- polycoding-0.1.0.dist-info/RECORD +41 -0
- polycoding-0.1.0.dist-info/WHEEL +4 -0
- polycoding-0.1.0.dist-info/entry_points.txt +3 -0
- polycoding-0.1.0.dist-info/licenses/LICENSE +20 -0
- project_manager/README.md +668 -0
- project_manager/__init__.py +29 -0
- project_manager/base.py +202 -0
- project_manager/config.py +36 -0
- project_manager/conversation/__init__.py +19 -0
- project_manager/conversation/flow.py +233 -0
- project_manager/conversation/types.py +64 -0
- project_manager/flow_runner.py +160 -0
- project_manager/git_utils.py +30 -0
- project_manager/github.py +367 -0
- project_manager/github_conversation.py +144 -0
- project_manager/github_projects_client.py +329 -0
- project_manager/hooks.py +377 -0
- project_manager/module.py +66 -0
- project_manager/types.py +79 -0
|
@@ -0,0 +1,668 @@
|
|
|
1
|
+
# Project Manager
|
|
2
|
+
|
|
3
|
+
Abstraction layer for project management providers (GitHub, GitLab, Jira, etc.) with **event-driven webhook support**.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
The Project Manager package provides a provider-agnostic interface for managing issues and project boards. It supports both polling and webhook-driven architectures, currently with GitHub Projects V2 support.
|
|
8
|
+
|
|
9
|
+
## Architecture
|
|
10
|
+
|
|
11
|
+
```
|
|
12
|
+
┌─────────────────────────────────────────────┐
|
|
13
|
+
│ ProjectManager (ABC) │
|
|
14
|
+
├─────────────────────────────────────────────┤
|
|
15
|
+
│ - get_open_issues() │
|
|
16
|
+
│ - get_project_items() │
|
|
17
|
+
│ - add_issue_to_project() │
|
|
18
|
+
│ - update_issue_status() │
|
|
19
|
+
│ - add_comment() │
|
|
20
|
+
│ - sync_issues_to_project() │
|
|
21
|
+
└─────────────────────────────────────────────┘
|
|
22
|
+
▲
|
|
23
|
+
│
|
|
24
|
+
┌───────────┴───────────┐
|
|
25
|
+
│ │
|
|
26
|
+
┌───────────────┐ ┌─────────────────┐
|
|
27
|
+
│ GitHub │ │ Future: GitLab, │
|
|
28
|
+
│ ProjectManager│ │ Jira, etc. │
|
|
29
|
+
└───────────────┘ └─────────────────┘
|
|
30
|
+
│
|
|
31
|
+
│
|
|
32
|
+
┌───────┴────────┐
|
|
33
|
+
│ FlowRunner │
|
|
34
|
+
├────────────────┤
|
|
35
|
+
│ - trigger_flow │
|
|
36
|
+
│ - complete_flow│
|
|
37
|
+
│ - is_running │
|
|
38
|
+
└───────┬────────┘
|
|
39
|
+
│
|
|
40
|
+
│
|
|
41
|
+
┌───────┴──────────────┐
|
|
42
|
+
│ Webhook (FastAPI) │
|
|
43
|
+
├──────────────────────┤
|
|
44
|
+
│ POST /webhook/github │
|
|
45
|
+
│ POST /trigger │
|
|
46
|
+
│ GET /health │
|
|
47
|
+
└──────────────────────┘
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Event-Driven Architecture
|
|
51
|
+
|
|
52
|
+
### Flow Execution Model
|
|
53
|
+
|
|
54
|
+
```
|
|
55
|
+
Webhook Event → Add to Project → Check if Ready
|
|
56
|
+
↓
|
|
57
|
+
If Ready & Not Running
|
|
58
|
+
↓
|
|
59
|
+
Start Flow → Mark In Progress
|
|
60
|
+
↓
|
|
61
|
+
Execute Callback
|
|
62
|
+
↓
|
|
63
|
+
Complete Flow → Check Next
|
|
64
|
+
↓
|
|
65
|
+
Trigger Next Ready Issue
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### Single-Flight Execution
|
|
69
|
+
|
|
70
|
+
Only **one flow runs at a time**. When a webhook receives an event:
|
|
71
|
+
|
|
72
|
+
- If a flow is running → Issue is queued (added to project, marked Ready)
|
|
73
|
+
- If no flow is running → Flow starts immediately
|
|
74
|
+
- When flow completes → Automatically triggers next Ready issue
|
|
75
|
+
|
|
76
|
+
This ensures:
|
|
77
|
+
|
|
78
|
+
- No concurrent flow execution
|
|
79
|
+
- Predictable resource usage
|
|
80
|
+
- Ordered processing of issues
|
|
81
|
+
|
|
82
|
+
## Usage
|
|
83
|
+
|
|
84
|
+
### Configuration via Environment Variables
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
# Required
|
|
88
|
+
export PROJECT_PROVIDER=github
|
|
89
|
+
export REPO_OWNER=xeroc
|
|
90
|
+
export REPO_NAME=demo
|
|
91
|
+
export PROJECT_IDENTIFIER=1
|
|
92
|
+
export GITHUB_TOKEN=ghp_xxx
|
|
93
|
+
|
|
94
|
+
# Webhook (optional)
|
|
95
|
+
export GITHUB_WEBHOOK_SECRET=your-secret
|
|
96
|
+
|
|
97
|
+
# Optional: Custom status mappings
|
|
98
|
+
export STATUS_TODO="Todo"
|
|
99
|
+
export STATUS_READY="Ready"
|
|
100
|
+
export STATUS_IN_PROGRESS="In progress"
|
|
101
|
+
export STATUS_REVIEWING="Reviewing"
|
|
102
|
+
export STATUS_DONE="Done"
|
|
103
|
+
export STATUS_BLOCKED="Blocked"
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### Webhook Mode (Recommended)
|
|
107
|
+
|
|
108
|
+
Start the webhook server:
|
|
109
|
+
|
|
110
|
+
```bash
|
|
111
|
+
python -m project_manager.cli webhook
|
|
112
|
+
|
|
113
|
+
# Custom host/port
|
|
114
|
+
python -m project_manager.cli webhook --host 0.0.0.0 --port 8080
|
|
115
|
+
|
|
116
|
+
# With secret
|
|
117
|
+
python -m project_manager.cli webhook --secret your-secret
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
**Endpoints:**
|
|
121
|
+
|
|
122
|
+
- `POST /webhook/github` - GitHub webhook endpoint
|
|
123
|
+
- `POST /trigger?issue_number=42` - Manual trigger (optional issue number)
|
|
124
|
+
- `GET /health` - Health check with flow status
|
|
125
|
+
|
|
126
|
+
**Configure GitHub Webhook:**
|
|
127
|
+
|
|
128
|
+
1. Go to repository Settings → Webhooks
|
|
129
|
+
2. Add webhook: `http://your-server:8000/webhook/github`
|
|
130
|
+
3. Content type: `application/json`
|
|
131
|
+
4. Secret: Match `GITHUB_WEBHOOK_SECRET`
|
|
132
|
+
5. Events: Select "Issues"
|
|
133
|
+
|
|
134
|
+
**Flow:**
|
|
135
|
+
|
|
136
|
+
1. Issue opened → GitHub sends webhook
|
|
137
|
+
2. Issue added to project
|
|
138
|
+
3. If ready and no flow running → Flow starts
|
|
139
|
+
4. Flow completes → Next ready issue processed
|
|
140
|
+
|
|
141
|
+
### Polling Mode (Legacy)
|
|
142
|
+
|
|
143
|
+
```bash
|
|
144
|
+
# Continuous polling
|
|
145
|
+
python -m project_manager.cli watch
|
|
146
|
+
|
|
147
|
+
# One-time run
|
|
148
|
+
python -m project_manager.cli watch --once
|
|
149
|
+
|
|
150
|
+
# Custom poll interval
|
|
151
|
+
python -m project_manager.cli watch --interval 60
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
### Other Commands
|
|
155
|
+
|
|
156
|
+
```bash
|
|
157
|
+
# Sync issues
|
|
158
|
+
python -m project_manager.cli sync
|
|
159
|
+
|
|
160
|
+
# List items
|
|
161
|
+
python -m project_manager.cli list
|
|
162
|
+
|
|
163
|
+
# Check flow status
|
|
164
|
+
python -m project_manager.cli status
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
### Programmatic Usage
|
|
168
|
+
|
|
169
|
+
#### Webhook Server
|
|
170
|
+
|
|
171
|
+
```python
|
|
172
|
+
from project_manager import GitHubProjectManager, FlowRunner, FlowStateManager
|
|
173
|
+
from project_manager.webhook import create_webhook_app
|
|
174
|
+
import uvicorn
|
|
175
|
+
|
|
176
|
+
config = ProjectConfig(
|
|
177
|
+
provider="github",
|
|
178
|
+
repo_owner="xeroc",
|
|
179
|
+
repo_name="demo",
|
|
180
|
+
project_identifier="1",
|
|
181
|
+
)
|
|
182
|
+
|
|
183
|
+
manager = GitHubProjectManager(config)
|
|
184
|
+
state_manager = FlowStateManager()
|
|
185
|
+
|
|
186
|
+
def on_issue_ready(item):
|
|
187
|
+
print(f"Processing: {item.title}")
|
|
188
|
+
# Your flow logic here
|
|
189
|
+
|
|
190
|
+
flow_runner = FlowRunner(
|
|
191
|
+
manager=manager,
|
|
192
|
+
state_manager=state_manager,
|
|
193
|
+
on_issue_ready=on_issue_ready,
|
|
194
|
+
)
|
|
195
|
+
|
|
196
|
+
app = create_webhook_app(flow_runner, webhook_secret="secret")
|
|
197
|
+
uvicorn.run(app, host="0.0.0.0", port=8000)
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
#### Manual Flow Control
|
|
201
|
+
|
|
202
|
+
```python
|
|
203
|
+
from project_manager import GitHubProjectManager, FlowRunner, FlowStateManager
|
|
204
|
+
from project_manager.types import ProjectConfig, IssueStatus
|
|
205
|
+
|
|
206
|
+
config = ProjectConfig(
|
|
207
|
+
provider="github",
|
|
208
|
+
repo_owner="xeroc",
|
|
209
|
+
repo_name="demo",
|
|
210
|
+
project_identifier="1",
|
|
211
|
+
)
|
|
212
|
+
|
|
213
|
+
manager = GitHubProjectManager(config)
|
|
214
|
+
state_manager = FlowStateManager()
|
|
215
|
+
|
|
216
|
+
def on_issue_ready(item):
|
|
217
|
+
print(f"Processing issue #{item.issue_number}")
|
|
218
|
+
# Your flow logic
|
|
219
|
+
# When done, call: flow_runner.complete_flow(item.issue_number)
|
|
220
|
+
|
|
221
|
+
flow_runner = FlowRunner(
|
|
222
|
+
manager=manager,
|
|
223
|
+
state_manager=state_manager,
|
|
224
|
+
on_issue_ready=on_issue_ready,
|
|
225
|
+
)
|
|
226
|
+
|
|
227
|
+
# Trigger next ready issue
|
|
228
|
+
flow_runner.trigger_flow()
|
|
229
|
+
|
|
230
|
+
# Trigger specific issue
|
|
231
|
+
flow_runner.trigger_flow(issue_number=42)
|
|
232
|
+
|
|
233
|
+
# Check if running
|
|
234
|
+
if flow_runner.is_flow_running():
|
|
235
|
+
print("Flow already in progress")
|
|
236
|
+
|
|
237
|
+
# Mark complete (usually called by on_issue_ready callback)
|
|
238
|
+
flow_runner.complete_flow(issue_number=42, success=True)
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
## Components
|
|
242
|
+
|
|
243
|
+
### Types (`types.py`)
|
|
244
|
+
|
|
245
|
+
- **Issue**: Generic issue representation
|
|
246
|
+
- **ProjectItem**: Generic project item representation
|
|
247
|
+
- **StatusMapping**: Maps standardized statuses to provider-specific values
|
|
248
|
+
- **ProjectConfig**: Configuration for project manager
|
|
249
|
+
|
|
250
|
+
### Base Class (`base.py`)
|
|
251
|
+
|
|
252
|
+
Abstract base class defining the provider interface.
|
|
253
|
+
|
|
254
|
+
### GitHub Implementation (`github.py`)
|
|
255
|
+
|
|
256
|
+
GitHub Projects V2 implementation using GraphQL API.
|
|
257
|
+
|
|
258
|
+
### Flow State (`flow_state.py`)
|
|
259
|
+
|
|
260
|
+
File-based state management for tracking running flows.
|
|
261
|
+
|
|
262
|
+
### Flow Runner (`flow_runner.py`)
|
|
263
|
+
|
|
264
|
+
Manages flow execution with single-flight guarantee.
|
|
265
|
+
|
|
266
|
+
### Webhook (`webhook.py`)
|
|
267
|
+
|
|
268
|
+
FastAPI webhook server for GitHub events.
|
|
269
|
+
|
|
270
|
+
### CLI (`cli.py`)
|
|
271
|
+
|
|
272
|
+
Command-line tools for project management.
|
|
273
|
+
|
|
274
|
+
## Webhook Flow Example
|
|
275
|
+
|
|
276
|
+
```bash
|
|
277
|
+
# Terminal 1: Start webhook server
|
|
278
|
+
export REPO_OWNER=xeroc
|
|
279
|
+
export REPO_NAME=demo
|
|
280
|
+
export PROJECT_IDENTIFIER=1
|
|
281
|
+
export GITHUB_TOKEN=ghp_xxx
|
|
282
|
+
export GITHUB_WEBHOOK_SECRET=mysecret
|
|
283
|
+
|
|
284
|
+
python -m project_manager.cli webhook --verbose
|
|
285
|
+
|
|
286
|
+
# Terminal 2: Check status
|
|
287
|
+
python -m project_manager.cli status
|
|
288
|
+
|
|
289
|
+
# Terminal 3: Manual trigger (optional)
|
|
290
|
+
curl -X POST http://localhost:8000/trigger
|
|
291
|
+
curl -X POST http://localhost:8000/trigger?issue_number=42
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
## API Reference
|
|
295
|
+
|
|
296
|
+
### FlowRunner
|
|
297
|
+
|
|
298
|
+
```python
|
|
299
|
+
runner = FlowRunner(
|
|
300
|
+
manager: ProjectManager,
|
|
301
|
+
state_manager: FlowStateManager | None = None,
|
|
302
|
+
on_issue_ready: Callable[[ProjectItem], None] | None = None,
|
|
303
|
+
)
|
|
304
|
+
|
|
305
|
+
runner.is_flow_running() -> bool
|
|
306
|
+
runner.trigger_flow(issue_number: int | None = None) -> bool
|
|
307
|
+
runner.complete_flow(issue_number: int, success: bool = True) -> None
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
### FlowStateManager
|
|
311
|
+
|
|
312
|
+
```python
|
|
313
|
+
state = FlowStateManager(state_dir: str | None = None)
|
|
314
|
+
|
|
315
|
+
state.is_flow_running() -> bool
|
|
316
|
+
state.get_running_flow() -> FlowState | None
|
|
317
|
+
state.start_flow(issue_number: int) -> FlowState
|
|
318
|
+
state.complete_flow(issue_number: int, success: bool = True) -> None
|
|
319
|
+
state.clear_state() -> None
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
### Webhook Endpoints
|
|
323
|
+
|
|
324
|
+
**POST /webhook/github**
|
|
325
|
+
|
|
326
|
+
- GitHub webhook endpoint
|
|
327
|
+
- Validates signature
|
|
328
|
+
- Handles issue events (opened, reopened, labeled)
|
|
329
|
+
- Returns: `{"status": "triggered"|"queued"|"ignored", ...}`
|
|
330
|
+
|
|
331
|
+
**POST /trigger**
|
|
332
|
+
|
|
333
|
+
- Manual trigger
|
|
334
|
+
- Query params: `issue_number` (optional)
|
|
335
|
+
- Returns: `{"status": "triggered"|"already_running", ...}`
|
|
336
|
+
|
|
337
|
+
**GET /health**
|
|
338
|
+
|
|
339
|
+
- Health check
|
|
340
|
+
- Returns: `{"status": "healthy", "flow_running": bool, "current_flow": {...}|null}`
|
|
341
|
+
|
|
342
|
+
## Future Enhancements
|
|
343
|
+
|
|
344
|
+
- [ ] GitLab support
|
|
345
|
+
- [ ] Jira support
|
|
346
|
+
- [ ] Database-backed state management (PostgreSQL)
|
|
347
|
+
- [ ] Multiple concurrent flows with resource limits
|
|
348
|
+
- [ ] Flow priority queue
|
|
349
|
+
- [ ] Retry failed flows
|
|
350
|
+
- [ ] Flow metrics and observability
|
|
351
|
+
- [ ] Issue assignment and labeling
|
|
352
|
+
- [ ] Multi-project support
|
|
353
|
+
|
|
354
|
+
## Extending to New Providers
|
|
355
|
+
|
|
356
|
+
To add a new provider (e.g., GitLab):
|
|
357
|
+
|
|
358
|
+
1. Create `gitlab.py` implementing `ProjectManager`
|
|
359
|
+
2. Implement all abstract methods
|
|
360
|
+
3. Register in `cli.py`'s `create_manager_from_env()`
|
|
361
|
+
4. Add provider-specific configuration as needed
|
|
362
|
+
|
|
363
|
+
Example:
|
|
364
|
+
|
|
365
|
+
```python
|
|
366
|
+
# gitlab.py
|
|
367
|
+
from .base import ProjectManager
|
|
368
|
+
from .types import Issue, ProjectItem, ProjectConfig
|
|
369
|
+
|
|
370
|
+
class GitLabProjectManager(ProjectManager):
|
|
371
|
+
def __init__(self, config: ProjectConfig):
|
|
372
|
+
super().__init__(config)
|
|
373
|
+
# Initialize GitLab client
|
|
374
|
+
|
|
375
|
+
def get_open_issues(self) -> list[Issue]:
|
|
376
|
+
# GitLab-specific implementation
|
|
377
|
+
pass
|
|
378
|
+
|
|
379
|
+
# ... implement other abstract methods
|
|
380
|
+
```
|
|
381
|
+
|
|
382
|
+
## Migration Guide
|
|
383
|
+
|
|
384
|
+
### From github_issues/main.py
|
|
385
|
+
|
|
386
|
+
**Old (Polling):**
|
|
387
|
+
|
|
388
|
+
```python
|
|
389
|
+
# Hard-coded configuration
|
|
390
|
+
REPO_OWNER = "xeroc"
|
|
391
|
+
REPO_NAME = "demo"
|
|
392
|
+
PROJECT_NUMBER = 1
|
|
393
|
+
|
|
394
|
+
while True:
|
|
395
|
+
run_cycle()
|
|
396
|
+
time.sleep(300)
|
|
397
|
+
```
|
|
398
|
+
|
|
399
|
+
**New (Webhook):**
|
|
400
|
+
|
|
401
|
+
```bash
|
|
402
|
+
# Environment variables
|
|
403
|
+
export REPO_OWNER=xeroc
|
|
404
|
+
export REPO_NAME=demo
|
|
405
|
+
export PROJECT_IDENTIFIER=1
|
|
406
|
+
|
|
407
|
+
# Start webhook server
|
|
408
|
+
python -m project_manager.cli webhook
|
|
409
|
+
```
|
|
410
|
+
|
|
411
|
+
**New (Polling):**
|
|
412
|
+
|
|
413
|
+
```bash
|
|
414
|
+
python -m project_manager.cli watch
|
|
415
|
+
```
|
|
416
|
+
|
|
417
|
+
### From feature_dev/github_status.py
|
|
418
|
+
|
|
419
|
+
**Old:**
|
|
420
|
+
|
|
421
|
+
```python
|
|
422
|
+
from github import Github
|
|
423
|
+
from github_issues.github_project import GitHubProjectsClient
|
|
424
|
+
|
|
425
|
+
class ProjectStatusManager:
|
|
426
|
+
def __init__(self):
|
|
427
|
+
self.github_client = Github(token)
|
|
428
|
+
self.projects_client = GitHubProjectsClient(token, REPO_NAME)
|
|
429
|
+
```
|
|
430
|
+
|
|
431
|
+
**New:**
|
|
432
|
+
|
|
433
|
+
```python
|
|
434
|
+
from project_manager import GitHubProjectManager
|
|
435
|
+
|
|
436
|
+
class ProjectStatusManager:
|
|
437
|
+
def __init__(self):
|
|
438
|
+
config = ProjectConfig(
|
|
439
|
+
provider="github",
|
|
440
|
+
repo_owner=repo_owner,
|
|
441
|
+
repo_name=repo_name,
|
|
442
|
+
project_identifier=project_identifier,
|
|
443
|
+
)
|
|
444
|
+
self.manager = GitHubProjectManager(config)
|
|
445
|
+
```
|
|
446
|
+
|
|
447
|
+
┌─────────────────────────────────────────────┐
|
|
448
|
+
│ ProjectManager (ABC) │
|
|
449
|
+
├─────────────────────────────────────────────┤
|
|
450
|
+
│ - get_open_issues() │
|
|
451
|
+
│ - get_project_items() │
|
|
452
|
+
│ - add_issue_to_project() │
|
|
453
|
+
│ - update_issue_status() │
|
|
454
|
+
│ - add_comment() │
|
|
455
|
+
│ - sync_issues_to_project() │
|
|
456
|
+
└─────────────────────────────────────────────┘
|
|
457
|
+
▲
|
|
458
|
+
│
|
|
459
|
+
┌───────────┴───────────┐
|
|
460
|
+
│ │
|
|
461
|
+
┌───────────────┐ ┌─────────────────┐
|
|
462
|
+
│ GitHub │ │ Future: GitLab, │
|
|
463
|
+
│ ProjectManager│ │ Jira, etc. │
|
|
464
|
+
└───────────────┘ └─────────────────┘
|
|
465
|
+
|
|
466
|
+
````
|
|
467
|
+
|
|
468
|
+
## Usage
|
|
469
|
+
|
|
470
|
+
### Configuration via Environment Variables
|
|
471
|
+
|
|
472
|
+
```bash
|
|
473
|
+
# Required
|
|
474
|
+
export PROJECT_PROVIDER=github
|
|
475
|
+
export REPO_OWNER=xeroc
|
|
476
|
+
export REPO_NAME=demo
|
|
477
|
+
export PROJECT_IDENTIFIER=1
|
|
478
|
+
export GITHUB_TOKEN=ghp_xxx
|
|
479
|
+
|
|
480
|
+
# Optional: Custom status mappings
|
|
481
|
+
export STATUS_TODO="Todo"
|
|
482
|
+
export STATUS_READY="Ready"
|
|
483
|
+
export STATUS_IN_PROGRESS="In progress"
|
|
484
|
+
export STATUS_REVIEWING="Reviewing"
|
|
485
|
+
export STATUS_DONE="Done"
|
|
486
|
+
export STATUS_BLOCKED="Blocked"
|
|
487
|
+
````
|
|
488
|
+
|
|
489
|
+
### CLI Commands
|
|
490
|
+
|
|
491
|
+
#### Watch Repository
|
|
492
|
+
|
|
493
|
+
Poll for issues and process them automatically:
|
|
494
|
+
|
|
495
|
+
```bash
|
|
496
|
+
# With environment variables configured
|
|
497
|
+
python -m project_manager.cli watch
|
|
498
|
+
|
|
499
|
+
# One-time run
|
|
500
|
+
python -m project_manager.cli watch --once
|
|
501
|
+
|
|
502
|
+
# Custom poll interval (default: 300 seconds)
|
|
503
|
+
python -m project_manager.cli watch --interval 60
|
|
504
|
+
|
|
505
|
+
# Verbose logging
|
|
506
|
+
python -m project_manager.cli watch --verbose
|
|
507
|
+
```
|
|
508
|
+
|
|
509
|
+
#### Sync Issues
|
|
510
|
+
|
|
511
|
+
Sync all open issues to the project board:
|
|
512
|
+
|
|
513
|
+
```bash
|
|
514
|
+
python -m project_manager.cli sync
|
|
515
|
+
```
|
|
516
|
+
|
|
517
|
+
#### List Project Items
|
|
518
|
+
|
|
519
|
+
List all items in the project:
|
|
520
|
+
|
|
521
|
+
```bash
|
|
522
|
+
python -m project_manager.cli list
|
|
523
|
+
```
|
|
524
|
+
|
|
525
|
+
### Programmatic Usage
|
|
526
|
+
|
|
527
|
+
```python
|
|
528
|
+
from project_manager import GitHubProjectManager
|
|
529
|
+
from project_manager.types import ProjectConfig, IssueStatus
|
|
530
|
+
|
|
531
|
+
# Create manager
|
|
532
|
+
config = ProjectConfig(
|
|
533
|
+
provider="github",
|
|
534
|
+
repo_owner="xeroc",
|
|
535
|
+
repo_name="demo",
|
|
536
|
+
project_identifier="1",
|
|
537
|
+
)
|
|
538
|
+
manager = GitHubProjectManager(config)
|
|
539
|
+
|
|
540
|
+
# Get all open issues
|
|
541
|
+
issues = manager.get_open_issues()
|
|
542
|
+
|
|
543
|
+
# Get project items
|
|
544
|
+
items = manager.get_project_items()
|
|
545
|
+
|
|
546
|
+
# Update status
|
|
547
|
+
manager.update_issue_status(
|
|
548
|
+
issue_number=42,
|
|
549
|
+
status=manager.config.status_mapping.to_provider_status(IssueStatus.IN_PROGRESS)
|
|
550
|
+
)
|
|
551
|
+
|
|
552
|
+
# Add comment
|
|
553
|
+
manager.add_comment(issue_number=42, comment="Working on this...")
|
|
554
|
+
|
|
555
|
+
# Sync issues to project
|
|
556
|
+
added = manager.sync_issues_to_project()
|
|
557
|
+
```
|
|
558
|
+
|
|
559
|
+
## Components
|
|
560
|
+
|
|
561
|
+
### Types (`types.py`)
|
|
562
|
+
|
|
563
|
+
- **Issue**: Generic issue representation
|
|
564
|
+
- **ProjectItem**: Generic project item representation
|
|
565
|
+
- **StatusMapping**: Maps standardized statuses to provider-specific values
|
|
566
|
+
- **ProjectConfig**: Configuration for project manager
|
|
567
|
+
|
|
568
|
+
### Base Class (`base.py`)
|
|
569
|
+
|
|
570
|
+
Abstract base class defining the provider interface.
|
|
571
|
+
|
|
572
|
+
### GitHub Implementation (`github.py`)
|
|
573
|
+
|
|
574
|
+
GitHub Projects V2 implementation using GraphQL API.
|
|
575
|
+
|
|
576
|
+
### CLI (`cli.py`)
|
|
577
|
+
|
|
578
|
+
Command-line tools for project management.
|
|
579
|
+
|
|
580
|
+
## Future Enhancements
|
|
581
|
+
|
|
582
|
+
- [ ] GitLab support
|
|
583
|
+
- [ ] Jira support
|
|
584
|
+
- [ ] Webhook endpoint for real-time notifications
|
|
585
|
+
- [ ] FastAPI integration for webhook server
|
|
586
|
+
- [ ] Database-backed state management
|
|
587
|
+
- [ ] Issue assignment and labeling
|
|
588
|
+
- [ ] Multi-project support
|
|
589
|
+
|
|
590
|
+
## Extending to New Providers
|
|
591
|
+
|
|
592
|
+
To add a new provider (e.g., GitLab):
|
|
593
|
+
|
|
594
|
+
1. Create `gitlab.py` implementing `ProjectManager`
|
|
595
|
+
2. Implement all abstract methods
|
|
596
|
+
3. Register in `cli.py`'s `create_manager_from_env()`
|
|
597
|
+
4. Add provider-specific configuration as needed
|
|
598
|
+
|
|
599
|
+
Example:
|
|
600
|
+
|
|
601
|
+
```python
|
|
602
|
+
# gitlab.py
|
|
603
|
+
from .base import ProjectManager
|
|
604
|
+
from .types import Issue, ProjectItem, ProjectConfig
|
|
605
|
+
|
|
606
|
+
class GitLabProjectManager(ProjectManager):
|
|
607
|
+
def __init__(self, config: ProjectConfig):
|
|
608
|
+
super().__init__(config)
|
|
609
|
+
# Initialize GitLab client
|
|
610
|
+
|
|
611
|
+
def get_open_issues(self) -> list[Issue]:
|
|
612
|
+
# GitLab-specific implementation
|
|
613
|
+
pass
|
|
614
|
+
|
|
615
|
+
# ... implement other abstract methods
|
|
616
|
+
```
|
|
617
|
+
|
|
618
|
+
## Migration Guide
|
|
619
|
+
|
|
620
|
+
### From github_issues/main.py
|
|
621
|
+
|
|
622
|
+
Old:
|
|
623
|
+
|
|
624
|
+
```python
|
|
625
|
+
# Hard-coded configuration
|
|
626
|
+
REPO_OWNER = "xeroc"
|
|
627
|
+
REPO_NAME = "demo"
|
|
628
|
+
PROJECT_NUMBER = 1
|
|
629
|
+
```
|
|
630
|
+
|
|
631
|
+
New:
|
|
632
|
+
|
|
633
|
+
```bash
|
|
634
|
+
# Environment variables
|
|
635
|
+
export REPO_OWNER=xeroc
|
|
636
|
+
export REPO_NAME=demo
|
|
637
|
+
export PROJECT_IDENTIFIER=1
|
|
638
|
+
```
|
|
639
|
+
|
|
640
|
+
### From feature_dev/github_status.py
|
|
641
|
+
|
|
642
|
+
Old:
|
|
643
|
+
|
|
644
|
+
```python
|
|
645
|
+
from github import Github
|
|
646
|
+
from github_issues.github_project import GitHubProjectsClient
|
|
647
|
+
|
|
648
|
+
class ProjectStatusManager:
|
|
649
|
+
def __init__(self):
|
|
650
|
+
self.github_client = Github(token)
|
|
651
|
+
self.projects_client = GitHubProjectsClient(token, REPO_NAME)
|
|
652
|
+
```
|
|
653
|
+
|
|
654
|
+
New:
|
|
655
|
+
|
|
656
|
+
```python
|
|
657
|
+
from project_manager import GitHubProjectManager
|
|
658
|
+
|
|
659
|
+
class ProjectStatusManager:
|
|
660
|
+
def __init__(self):
|
|
661
|
+
config = ProjectConfig(
|
|
662
|
+
provider="github",
|
|
663
|
+
repo_owner=repo_owner,
|
|
664
|
+
repo_name=repo_name,
|
|
665
|
+
project_identifier=project_identifier,
|
|
666
|
+
)
|
|
667
|
+
self.manager = GitHubProjectManager(config)
|
|
668
|
+
```
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"""Project management abstraction layer."""
|
|
2
|
+
|
|
3
|
+
from .base import ProjectManager
|
|
4
|
+
from .flow_runner import FlowRunner
|
|
5
|
+
from .git_utils import get_github_repo_from_local
|
|
6
|
+
from .github import GitHubProjectManager
|
|
7
|
+
from .github_conversation import GitHubConversationManager
|
|
8
|
+
from .module import ProjectManagerModule
|
|
9
|
+
from .types import (
|
|
10
|
+
Issue,
|
|
11
|
+
IssueStatus,
|
|
12
|
+
ProjectConfig,
|
|
13
|
+
ProjectItem,
|
|
14
|
+
StatusMapping,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
__all__ = [
|
|
18
|
+
"ProjectManager",
|
|
19
|
+
"GitHubProjectManager",
|
|
20
|
+
"GitHubConversationManager",
|
|
21
|
+
"FlowRunner",
|
|
22
|
+
"ProjectManagerModule",
|
|
23
|
+
"Issue",
|
|
24
|
+
"IssueStatus",
|
|
25
|
+
"ProjectConfig",
|
|
26
|
+
"ProjectItem",
|
|
27
|
+
"StatusMapping",
|
|
28
|
+
"get_github_repo_from_local",
|
|
29
|
+
]
|