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.
@@ -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
+ ]