planpilot 2.2.0__tar.gz → 2.3.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.
- {planpilot-2.2.0 → planpilot-2.3.0}/PKG-INFO +11 -1
- {planpilot-2.2.0 → planpilot-2.3.0}/README.md +10 -0
- {planpilot-2.2.0 → planpilot-2.3.0}/pyproject.toml +2 -2
- {planpilot-2.2.0 → planpilot-2.3.0}/src/planpilot/__init__.py +3 -2
- {planpilot-2.2.0 → planpilot-2.3.0}/src/planpilot/cli.py +163 -38
- {planpilot-2.2.0 → planpilot-2.3.0}/src/planpilot/contracts/sync.py +6 -0
- {planpilot-2.2.0 → planpilot-2.3.0}/src/planpilot/engine/engine.py +62 -9
- {planpilot-2.2.0 → planpilot-2.3.0}/src/planpilot/providers/github/github_gql/__init__.py +22 -17
- {planpilot-2.2.0 → planpilot-2.3.0}/src/planpilot/providers/github/github_gql/client.py +12 -0
- planpilot-2.3.0/src/planpilot/providers/github/github_gql/delete_issue.py +19 -0
- {planpilot-2.2.0 → planpilot-2.3.0}/src/planpilot/providers/github/github_gql/exceptions.py +1 -1
- planpilot-2.3.0/src/planpilot/providers/github/github_gql/input_types.py +9 -0
- {planpilot-2.2.0 → planpilot-2.3.0}/src/planpilot/providers/github/github_gql/operations.py +9 -0
- {planpilot-2.2.0 → planpilot-2.3.0}/src/planpilot/providers/github/item.py +12 -0
- planpilot-2.3.0/src/planpilot/providers/github/operations/delete_issue.graphql +5 -0
- {planpilot-2.2.0 → planpilot-2.3.0}/src/planpilot/providers/github/provider.py +30 -2
- {planpilot-2.2.0 → planpilot-2.3.0}/src/planpilot/renderers/markdown.py +2 -0
- {planpilot-2.2.0 → planpilot-2.3.0}/src/planpilot/sdk.py +228 -2
- planpilot-2.2.0/src/planpilot/providers/github/github_gql/input_types.py +0 -4
- {planpilot-2.2.0 → planpilot-2.3.0}/LICENSE +0 -0
- {planpilot-2.2.0 → planpilot-2.3.0}/src/planpilot/AGENTS.md +0 -0
- {planpilot-2.2.0 → planpilot-2.3.0}/src/planpilot/__main__.py +0 -0
- {planpilot-2.2.0 → planpilot-2.3.0}/src/planpilot/auth/__init__.py +0 -0
- {planpilot-2.2.0 → planpilot-2.3.0}/src/planpilot/auth/base.py +0 -0
- {planpilot-2.2.0 → planpilot-2.3.0}/src/planpilot/auth/factory.py +0 -0
- {planpilot-2.2.0 → planpilot-2.3.0}/src/planpilot/auth/resolvers/__init__.py +0 -0
- {planpilot-2.2.0 → planpilot-2.3.0}/src/planpilot/auth/resolvers/env.py +0 -0
- {planpilot-2.2.0 → planpilot-2.3.0}/src/planpilot/auth/resolvers/gh_cli.py +0 -0
- {planpilot-2.2.0 → planpilot-2.3.0}/src/planpilot/auth/resolvers/static.py +0 -0
- {planpilot-2.2.0 → planpilot-2.3.0}/src/planpilot/contracts/__init__.py +0 -0
- {planpilot-2.2.0 → planpilot-2.3.0}/src/planpilot/contracts/config.py +0 -0
- {planpilot-2.2.0 → planpilot-2.3.0}/src/planpilot/contracts/exceptions.py +0 -0
- {planpilot-2.2.0 → planpilot-2.3.0}/src/planpilot/contracts/item.py +0 -0
- {planpilot-2.2.0 → planpilot-2.3.0}/src/planpilot/contracts/plan.py +0 -0
- {planpilot-2.2.0 → planpilot-2.3.0}/src/planpilot/contracts/provider.py +0 -0
- {planpilot-2.2.0 → planpilot-2.3.0}/src/planpilot/contracts/renderer.py +0 -0
- {planpilot-2.2.0 → planpilot-2.3.0}/src/planpilot/engine/__init__.py +0 -0
- {planpilot-2.2.0 → planpilot-2.3.0}/src/planpilot/engine/progress.py +0 -0
- {planpilot-2.2.0 → planpilot-2.3.0}/src/planpilot/engine/utils.py +0 -0
- {planpilot-2.2.0 → planpilot-2.3.0}/src/planpilot/plan/__init__.py +0 -0
- {planpilot-2.2.0 → planpilot-2.3.0}/src/planpilot/plan/hasher.py +0 -0
- {planpilot-2.2.0 → planpilot-2.3.0}/src/planpilot/plan/loader.py +0 -0
- {planpilot-2.2.0 → planpilot-2.3.0}/src/planpilot/plan/validator.py +0 -0
- {planpilot-2.2.0 → planpilot-2.3.0}/src/planpilot/progress.py +0 -0
- {planpilot-2.2.0 → planpilot-2.3.0}/src/planpilot/providers/AGENTS.md +0 -0
- {planpilot-2.2.0 → planpilot-2.3.0}/src/planpilot/providers/__init__.py +0 -0
- {planpilot-2.2.0 → planpilot-2.3.0}/src/planpilot/providers/base.py +0 -0
- {planpilot-2.2.0 → planpilot-2.3.0}/src/planpilot/providers/dry_run.py +0 -0
- {planpilot-2.2.0 → planpilot-2.3.0}/src/planpilot/providers/factory.py +0 -0
- {planpilot-2.2.0 → planpilot-2.3.0}/src/planpilot/providers/github/AGENTS.md +0 -0
- {planpilot-2.2.0 → planpilot-2.3.0}/src/planpilot/providers/github/__init__.py +0 -0
- {planpilot-2.2.0 → planpilot-2.3.0}/src/planpilot/providers/github/_retrying_transport.py +0 -0
- {planpilot-2.2.0 → planpilot-2.3.0}/src/planpilot/providers/github/github_gql/add_blocked_by.py +0 -0
- {planpilot-2.2.0 → planpilot-2.3.0}/src/planpilot/providers/github/github_gql/add_labels.py +0 -0
- {planpilot-2.2.0 → planpilot-2.3.0}/src/planpilot/providers/github/github_gql/add_project_item.py +0 -0
- {planpilot-2.2.0 → planpilot-2.3.0}/src/planpilot/providers/github/github_gql/add_sub_issue.py +0 -0
- {planpilot-2.2.0 → planpilot-2.3.0}/src/planpilot/providers/github/github_gql/async_base_client.py +0 -0
- {planpilot-2.2.0 → planpilot-2.3.0}/src/planpilot/providers/github/github_gql/base_model.py +0 -0
- {planpilot-2.2.0 → planpilot-2.3.0}/src/planpilot/providers/github/github_gql/close_issue.py +0 -0
- {planpilot-2.2.0 → planpilot-2.3.0}/src/planpilot/providers/github/github_gql/create_issue.py +0 -0
- {planpilot-2.2.0 → planpilot-2.3.0}/src/planpilot/providers/github/github_gql/create_label.py +0 -0
- {planpilot-2.2.0 → planpilot-2.3.0}/src/planpilot/providers/github/github_gql/enums.py +0 -0
- {planpilot-2.2.0 → planpilot-2.3.0}/src/planpilot/providers/github/github_gql/fetch_org_project.py +0 -0
- {planpilot-2.2.0 → planpilot-2.3.0}/src/planpilot/providers/github/github_gql/fetch_project_fields.py +0 -0
- {planpilot-2.2.0 → planpilot-2.3.0}/src/planpilot/providers/github/github_gql/fetch_project_items.py +0 -0
- {planpilot-2.2.0 → planpilot-2.3.0}/src/planpilot/providers/github/github_gql/fetch_relations.py +0 -0
- {planpilot-2.2.0 → planpilot-2.3.0}/src/planpilot/providers/github/github_gql/fetch_repo.py +0 -0
- {planpilot-2.2.0 → planpilot-2.3.0}/src/planpilot/providers/github/github_gql/fetch_user_project.py +0 -0
- {planpilot-2.2.0 → planpilot-2.3.0}/src/planpilot/providers/github/github_gql/find_labels.py +0 -0
- {planpilot-2.2.0 → planpilot-2.3.0}/src/planpilot/providers/github/github_gql/fragments.py +0 -0
- {planpilot-2.2.0 → planpilot-2.3.0}/src/planpilot/providers/github/github_gql/get_issue.py +0 -0
- {planpilot-2.2.0 → planpilot-2.3.0}/src/planpilot/providers/github/github_gql/remove_blocked_by.py +0 -0
- {planpilot-2.2.0 → planpilot-2.3.0}/src/planpilot/providers/github/github_gql/remove_labels.py +0 -0
- {planpilot-2.2.0 → planpilot-2.3.0}/src/planpilot/providers/github/github_gql/remove_sub_issue.py +0 -0
- {planpilot-2.2.0 → planpilot-2.3.0}/src/planpilot/providers/github/github_gql/search_issues.py +0 -0
- {planpilot-2.2.0 → planpilot-2.3.0}/src/planpilot/providers/github/github_gql/update_issue.py +0 -0
- {planpilot-2.2.0 → planpilot-2.3.0}/src/planpilot/providers/github/github_gql/update_project_field.py +0 -0
- {planpilot-2.2.0 → planpilot-2.3.0}/src/planpilot/providers/github/mapper.py +0 -0
- {planpilot-2.2.0 → planpilot-2.3.0}/src/planpilot/providers/github/models.py +0 -0
- {planpilot-2.2.0 → planpilot-2.3.0}/src/planpilot/providers/github/operations/add_blocked_by.graphql +0 -0
- {planpilot-2.2.0 → planpilot-2.3.0}/src/planpilot/providers/github/operations/add_labels.graphql +0 -0
- {planpilot-2.2.0 → planpilot-2.3.0}/src/planpilot/providers/github/operations/add_project_item.graphql +0 -0
- {planpilot-2.2.0 → planpilot-2.3.0}/src/planpilot/providers/github/operations/add_sub_issue.graphql +0 -0
- {planpilot-2.2.0 → planpilot-2.3.0}/src/planpilot/providers/github/operations/close_issue.graphql +0 -0
- {planpilot-2.2.0 → planpilot-2.3.0}/src/planpilot/providers/github/operations/create_issue.graphql +0 -0
- {planpilot-2.2.0 → planpilot-2.3.0}/src/planpilot/providers/github/operations/create_label.graphql +0 -0
- {planpilot-2.2.0 → planpilot-2.3.0}/src/planpilot/providers/github/operations/fetch_org_project.graphql +0 -0
- {planpilot-2.2.0 → planpilot-2.3.0}/src/planpilot/providers/github/operations/fetch_project_fields.graphql +0 -0
- {planpilot-2.2.0 → planpilot-2.3.0}/src/planpilot/providers/github/operations/fetch_project_items.graphql +0 -0
- {planpilot-2.2.0 → planpilot-2.3.0}/src/planpilot/providers/github/operations/fetch_relations.graphql +0 -0
- {planpilot-2.2.0 → planpilot-2.3.0}/src/planpilot/providers/github/operations/fetch_repo.graphql +0 -0
- {planpilot-2.2.0 → planpilot-2.3.0}/src/planpilot/providers/github/operations/fetch_user_project.graphql +0 -0
- {planpilot-2.2.0 → planpilot-2.3.0}/src/planpilot/providers/github/operations/find_labels.graphql +0 -0
- {planpilot-2.2.0 → planpilot-2.3.0}/src/planpilot/providers/github/operations/fragments.graphql +0 -0
- {planpilot-2.2.0 → planpilot-2.3.0}/src/planpilot/providers/github/operations/get_issue.graphql +0 -0
- {planpilot-2.2.0 → planpilot-2.3.0}/src/planpilot/providers/github/operations/remove_blocked_by.graphql +0 -0
- {planpilot-2.2.0 → planpilot-2.3.0}/src/planpilot/providers/github/operations/remove_labels.graphql +0 -0
- {planpilot-2.2.0 → planpilot-2.3.0}/src/planpilot/providers/github/operations/remove_sub_issue.graphql +0 -0
- {planpilot-2.2.0 → planpilot-2.3.0}/src/planpilot/providers/github/operations/search_issues.graphql +0 -0
- {planpilot-2.2.0 → planpilot-2.3.0}/src/planpilot/providers/github/operations/update_issue.graphql +0 -0
- {planpilot-2.2.0 → planpilot-2.3.0}/src/planpilot/providers/github/operations/update_project_field.graphql +0 -0
- {planpilot-2.2.0 → planpilot-2.3.0}/src/planpilot/providers/github/schema.graphql +0 -0
- {planpilot-2.2.0 → planpilot-2.3.0}/src/planpilot/py.typed +0 -0
- {planpilot-2.2.0 → planpilot-2.3.0}/src/planpilot/renderers/__init__.py +0 -0
- {planpilot-2.2.0 → planpilot-2.3.0}/src/planpilot/renderers/factory.py +0 -0
- {planpilot-2.2.0 → planpilot-2.3.0}/src/planpilot/scaffold.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: planpilot
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.3.0
|
|
4
4
|
Summary: Sync roadmap plans (epics, stories, tasks) to GitHub Issues and Projects v2
|
|
5
5
|
License: MIT
|
|
6
6
|
License-File: LICENSE
|
|
@@ -221,6 +221,16 @@ planpilot sync --config ./planpilot.json --apply
|
|
|
221
221
|
| `--apply` | — | Apply mode |
|
|
222
222
|
| `--verbose` | off | Enable verbose logging |
|
|
223
223
|
|
|
224
|
+
### `planpilot clean`
|
|
225
|
+
|
|
226
|
+
| Flag | Default | Description |
|
|
227
|
+
|------|---------|-------------|
|
|
228
|
+
| `--config` | `./planpilot.json` | Path to `planpilot.json` |
|
|
229
|
+
| `--dry-run` | — | Preview which issues would be deleted |
|
|
230
|
+
| `--apply` | — | Execute deletions |
|
|
231
|
+
| `--all` | off | Delete all planpilot-managed issues by label, regardless of current plan hash |
|
|
232
|
+
| `--verbose` | off | Enable verbose logging |
|
|
233
|
+
|
|
224
234
|
### `planpilot map sync`
|
|
225
235
|
|
|
226
236
|
| Flag | Default | Description |
|
|
@@ -187,6 +187,16 @@ planpilot sync --config ./planpilot.json --apply
|
|
|
187
187
|
| `--apply` | — | Apply mode |
|
|
188
188
|
| `--verbose` | off | Enable verbose logging |
|
|
189
189
|
|
|
190
|
+
### `planpilot clean`
|
|
191
|
+
|
|
192
|
+
| Flag | Default | Description |
|
|
193
|
+
|------|---------|-------------|
|
|
194
|
+
| `--config` | `./planpilot.json` | Path to `planpilot.json` |
|
|
195
|
+
| `--dry-run` | — | Preview which issues would be deleted |
|
|
196
|
+
| `--apply` | — | Execute deletions |
|
|
197
|
+
| `--all` | off | Delete all planpilot-managed issues by label, regardless of current plan hash |
|
|
198
|
+
| `--verbose` | off | Enable verbose logging |
|
|
199
|
+
|
|
190
200
|
### `planpilot map sync`
|
|
191
201
|
|
|
192
202
|
| Flag | Default | Description |
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "planpilot"
|
|
3
|
-
version = "2.
|
|
3
|
+
version = "2.3.0"
|
|
4
4
|
description = "Sync roadmap plans (epics, stories, tasks) to GitHub Issues and Projects v2"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
license = "MIT"
|
|
@@ -69,7 +69,7 @@ test-e2e = "pytest -v tests/e2e/test_cli_e2e.py"
|
|
|
69
69
|
coverage = "pytest -v --cov-report=html:.coverage/html"
|
|
70
70
|
coverage-e2e = "pytest -v tests/e2e/test_cli_e2e.py --cov-report=term-missing --cov-report=xml:.coverage/coverage-e2e.xml"
|
|
71
71
|
typecheck = "mypy src/planpilot"
|
|
72
|
-
check = ["lint", "format-check", "test"]
|
|
72
|
+
check = ["lint", "format-check", "typecheck", "test"]
|
|
73
73
|
|
|
74
74
|
[tool.poe.tasks.gen-schema]
|
|
75
75
|
help = "Introspect GitHub GraphQL API and download schema"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"""Public API surface for PlanPilot."""
|
|
2
2
|
|
|
3
|
-
__version__ = "2.
|
|
3
|
+
__version__ = "2.3.0"
|
|
4
4
|
|
|
5
5
|
from planpilot.auth import create_token_resolver
|
|
6
6
|
from planpilot.contracts.config import FieldConfig, PlanPaths, PlanPilotConfig
|
|
@@ -16,7 +16,7 @@ from planpilot.contracts.exceptions import (
|
|
|
16
16
|
from planpilot.contracts.plan import Plan, PlanItem, PlanItemType
|
|
17
17
|
from planpilot.contracts.provider import Provider
|
|
18
18
|
from planpilot.contracts.renderer import BodyRenderer, RenderContext
|
|
19
|
-
from planpilot.contracts.sync import MapSyncResult, SyncEntry, SyncMap, SyncResult
|
|
19
|
+
from planpilot.contracts.sync import CleanResult, MapSyncResult, SyncEntry, SyncMap, SyncResult
|
|
20
20
|
from planpilot.providers import create_provider
|
|
21
21
|
from planpilot.renderers import create_renderer
|
|
22
22
|
from planpilot.scaffold import create_plan_stubs, detect_plan_paths, detect_target, scaffold_config, write_config
|
|
@@ -25,6 +25,7 @@ from planpilot.sdk import PlanPilot, load_config, load_plan
|
|
|
25
25
|
__all__ = [
|
|
26
26
|
"AuthenticationError",
|
|
27
27
|
"BodyRenderer",
|
|
28
|
+
"CleanResult",
|
|
28
29
|
"ConfigError",
|
|
29
30
|
"FieldConfig",
|
|
30
31
|
"MapSyncResult",
|
|
@@ -13,6 +13,7 @@ import httpx
|
|
|
13
13
|
|
|
14
14
|
from planpilot import (
|
|
15
15
|
AuthenticationError,
|
|
16
|
+
CleanResult,
|
|
16
17
|
ConfigError,
|
|
17
18
|
MapSyncResult,
|
|
18
19
|
PlanItemType,
|
|
@@ -31,6 +32,7 @@ from planpilot import (
|
|
|
31
32
|
write_config,
|
|
32
33
|
)
|
|
33
34
|
from planpilot.contracts.exceptions import ProjectURLError
|
|
35
|
+
from planpilot.engine.progress import SyncProgress
|
|
34
36
|
from planpilot.providers.github.mapper import parse_project_url
|
|
35
37
|
|
|
36
38
|
_REQUIRED_CLASSIC_SCOPES = {"repo", "project"}
|
|
@@ -85,20 +87,43 @@ def _check_classic_scopes(*, scopes_header: str | None) -> None:
|
|
|
85
87
|
raise AuthenticationError(f"Token is missing required GitHub scopes: {needed}")
|
|
86
88
|
|
|
87
89
|
|
|
88
|
-
def _validate_github_auth_for_init(*, token: str, target: str) -> str | None:
|
|
90
|
+
def _validate_github_auth_for_init(*, token: str, target: str, progress: SyncProgress | None = None) -> str | None:
|
|
89
91
|
owner, repo = target.split("/", 1)
|
|
90
92
|
with httpx.Client(timeout=10.0) as client:
|
|
93
|
+
if progress is not None:
|
|
94
|
+
progress.phase_start("Init Auth")
|
|
91
95
|
user_resp = client.get("https://api.github.com/user", headers=_github_headers(token))
|
|
92
96
|
if user_resp.status_code != 200:
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
97
|
+
auth_error = AuthenticationError(
|
|
98
|
+
"GitHub authentication failed; verify your token/gh login and network access"
|
|
99
|
+
)
|
|
100
|
+
if progress is not None:
|
|
101
|
+
progress.phase_error("Init Auth", auth_error)
|
|
102
|
+
raise auth_error
|
|
103
|
+
try:
|
|
104
|
+
_check_classic_scopes(scopes_header=user_resp.headers.get("x-oauth-scopes"))
|
|
105
|
+
except AuthenticationError as error:
|
|
106
|
+
if progress is not None:
|
|
107
|
+
progress.phase_error("Init Auth", error)
|
|
108
|
+
raise
|
|
109
|
+
if progress is not None:
|
|
110
|
+
progress.phase_done("Init Auth")
|
|
111
|
+
|
|
112
|
+
if progress is not None:
|
|
113
|
+
progress.phase_start("Init Repo")
|
|
96
114
|
repo_resp = client.get(f"https://api.github.com/repos/{owner}/{repo}", headers=_github_headers(token))
|
|
97
115
|
if repo_resp.status_code != 200:
|
|
98
|
-
|
|
116
|
+
repo_error = AuthenticationError(
|
|
99
117
|
f"Cannot access target repository '{target}'; verify repo scope/permissions and repo visibility"
|
|
100
118
|
)
|
|
101
|
-
|
|
119
|
+
if progress is not None:
|
|
120
|
+
progress.phase_error("Init Repo", repo_error)
|
|
121
|
+
raise repo_error
|
|
122
|
+
if progress is not None:
|
|
123
|
+
progress.phase_done("Init Repo")
|
|
124
|
+
|
|
125
|
+
if progress is not None:
|
|
126
|
+
progress.phase_start("Init Projects")
|
|
102
127
|
viewer_projects_query = {"query": "query { viewer { projectsV2(first: 1) { nodes { id } } } }"}
|
|
103
128
|
projects_resp = client.post(
|
|
104
129
|
"https://api.github.com/graphql",
|
|
@@ -109,19 +134,34 @@ def _validate_github_auth_for_init(*, token: str, target: str) -> str | None:
|
|
|
109
134
|
projects_resp.json() if projects_resp.headers.get("content-type", "").startswith("application/json") else {}
|
|
110
135
|
)
|
|
111
136
|
if projects_resp.status_code != 200 or payload.get("errors"):
|
|
112
|
-
|
|
137
|
+
projects_error = AuthenticationError(
|
|
113
138
|
"Token does not have sufficient project permissions; ensure project access is granted"
|
|
114
139
|
)
|
|
115
|
-
|
|
140
|
+
if progress is not None:
|
|
141
|
+
progress.phase_error("Init Projects", projects_error)
|
|
142
|
+
raise projects_error
|
|
143
|
+
if progress is not None:
|
|
144
|
+
progress.phase_done("Init Projects")
|
|
145
|
+
|
|
146
|
+
if progress is not None:
|
|
147
|
+
progress.phase_start("Init Owner")
|
|
116
148
|
owner_resp = client.get(f"https://api.github.com/users/{owner}", headers=_github_headers(token))
|
|
117
149
|
if owner_resp.status_code != 200:
|
|
150
|
+
if progress is not None:
|
|
151
|
+
progress.phase_done("Init Owner")
|
|
118
152
|
return None
|
|
119
153
|
owner_payload = owner_resp.json()
|
|
120
154
|
owner_type = owner_payload.get("type")
|
|
121
155
|
if owner_type == "Organization":
|
|
156
|
+
if progress is not None:
|
|
157
|
+
progress.phase_done("Init Owner")
|
|
122
158
|
return "org"
|
|
123
159
|
if owner_type == "User":
|
|
160
|
+
if progress is not None:
|
|
161
|
+
progress.phase_done("Init Owner")
|
|
124
162
|
return "user"
|
|
163
|
+
if progress is not None:
|
|
164
|
+
progress.phase_done("Init Owner")
|
|
125
165
|
return None
|
|
126
166
|
|
|
127
167
|
|
|
@@ -173,6 +213,19 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
173
213
|
)
|
|
174
214
|
init_parser.add_argument("--defaults", action="store_true", help="Use defaults without prompting")
|
|
175
215
|
|
|
216
|
+
clean_parser = subparsers.add_parser("clean", help="Delete all issues belonging to a plan")
|
|
217
|
+
clean_parser.add_argument("--config", default="./planpilot.json", help="Path to planpilot.json")
|
|
218
|
+
clean_mode = clean_parser.add_mutually_exclusive_group(required=True)
|
|
219
|
+
clean_mode.add_argument("--dry-run", action="store_true", help="Preview mode")
|
|
220
|
+
clean_mode.add_argument("--apply", action="store_true", help="Apply mode")
|
|
221
|
+
clean_parser.add_argument(
|
|
222
|
+
"--all",
|
|
223
|
+
action="store_true",
|
|
224
|
+
default=False,
|
|
225
|
+
help="Delete all planpilot-managed issues (by label), not just the current plan version",
|
|
226
|
+
)
|
|
227
|
+
clean_parser.add_argument("--verbose", "-v", action="store_true", help="Enable debug logging")
|
|
228
|
+
|
|
176
229
|
map_parser = subparsers.add_parser("map", help="Sync-map operations")
|
|
177
230
|
map_subparsers = map_parser.add_subparsers(dest="map_command", required=True)
|
|
178
231
|
|
|
@@ -210,13 +263,27 @@ async def _run_sync(args: argparse.Namespace) -> SyncResult:
|
|
|
210
263
|
|
|
211
264
|
async def _run_map_sync(args: argparse.Namespace) -> MapSyncResult:
|
|
212
265
|
config = load_config(args.config)
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
266
|
+
if not args.verbose:
|
|
267
|
+
from planpilot.progress import RichSyncProgress
|
|
268
|
+
|
|
269
|
+
with RichSyncProgress() as progress:
|
|
270
|
+
pp = await PlanPilot.from_config(config, progress=progress)
|
|
271
|
+
candidate_plan_ids = await pp.discover_remote_plan_ids()
|
|
272
|
+
selected_plan_id = _resolve_selected_plan_id(
|
|
273
|
+
explicit_plan_id=args.plan_id,
|
|
274
|
+
candidate_plan_ids=candidate_plan_ids,
|
|
275
|
+
)
|
|
276
|
+
with RichSyncProgress() as progress:
|
|
277
|
+
pp = await PlanPilot.from_config(config, progress=progress)
|
|
278
|
+
result = await pp.map_sync(plan_id=selected_plan_id, dry_run=args.dry_run)
|
|
279
|
+
else:
|
|
280
|
+
pp = await PlanPilot.from_config(config)
|
|
281
|
+
candidate_plan_ids = await pp.discover_remote_plan_ids()
|
|
282
|
+
selected_plan_id = _resolve_selected_plan_id(
|
|
283
|
+
explicit_plan_id=args.plan_id,
|
|
284
|
+
candidate_plan_ids=candidate_plan_ids,
|
|
285
|
+
)
|
|
286
|
+
result = await pp.map_sync(plan_id=selected_plan_id, dry_run=args.dry_run)
|
|
220
287
|
result = result.model_copy(update={"candidate_plan_ids": candidate_plan_ids})
|
|
221
288
|
print(_format_map_sync_summary(result, config))
|
|
222
289
|
return result
|
|
@@ -253,15 +320,29 @@ def _format_summary(result: SyncResult, config: PlanPilotConfig) -> str:
|
|
|
253
320
|
created_epics = result.items_created.get(PlanItemType.EPIC, 0)
|
|
254
321
|
created_stories = result.items_created.get(PlanItemType.STORY, 0)
|
|
255
322
|
created_tasks = result.items_created.get(PlanItemType.TASK, 0)
|
|
323
|
+
total_created = created_epics + created_stories + created_tasks
|
|
256
324
|
|
|
257
325
|
total_by_type = {PlanItemType.EPIC: 0, PlanItemType.STORY: 0, PlanItemType.TASK: 0}
|
|
258
326
|
for entry in result.sync_map.entries.values():
|
|
259
327
|
if entry.item_type in total_by_type:
|
|
260
328
|
total_by_type[entry.item_type] += 1
|
|
261
329
|
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
330
|
+
total_items = len(result.sync_map.entries)
|
|
331
|
+
total_matched = total_items - total_created
|
|
332
|
+
|
|
333
|
+
matched_epics = total_by_type[PlanItemType.EPIC] - created_epics
|
|
334
|
+
matched_stories = total_by_type[PlanItemType.STORY] - created_stories
|
|
335
|
+
matched_tasks = total_by_type[PlanItemType.TASK] - created_tasks
|
|
336
|
+
|
|
337
|
+
def _type_breakdown(epics: int, stories: int, tasks: int) -> str:
|
|
338
|
+
parts: list[str] = []
|
|
339
|
+
if epics:
|
|
340
|
+
parts.append(f"{epics} epic{'s' if epics != 1 else ''}")
|
|
341
|
+
if stories:
|
|
342
|
+
parts.append(f"{stories} stor{'ies' if stories != 1 else 'y'}")
|
|
343
|
+
if tasks:
|
|
344
|
+
parts.append(f"{tasks} task{'s' if tasks != 1 else ''}")
|
|
345
|
+
return ", ".join(parts) if parts else "none"
|
|
265
346
|
|
|
266
347
|
lines = [
|
|
267
348
|
"",
|
|
@@ -271,25 +352,22 @@ def _format_summary(result: SyncResult, config: PlanPilotConfig) -> str:
|
|
|
271
352
|
f" Target: {result.sync_map.target}",
|
|
272
353
|
f" Board: {result.sync_map.board_url}",
|
|
273
354
|
"",
|
|
274
|
-
|
|
355
|
+
" Items: {} total ({})".format(
|
|
356
|
+
total_items,
|
|
357
|
+
_type_breakdown(
|
|
358
|
+
total_by_type[PlanItemType.EPIC],
|
|
359
|
+
total_by_type[PlanItemType.STORY],
|
|
360
|
+
total_by_type[PlanItemType.TASK],
|
|
361
|
+
),
|
|
362
|
+
),
|
|
275
363
|
]
|
|
276
364
|
|
|
277
|
-
if
|
|
278
|
-
lines.append(f"
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
PlanItemType.EPIC: "Epic",
|
|
284
|
-
PlanItemType.STORY: "Story",
|
|
285
|
-
PlanItemType.TASK: "Task",
|
|
286
|
-
}
|
|
287
|
-
for item_type in (PlanItemType.EPIC, PlanItemType.STORY, PlanItemType.TASK):
|
|
288
|
-
for item_id in sorted(result.sync_map.entries):
|
|
289
|
-
entry = result.sync_map.entries[item_id]
|
|
290
|
-
if entry.item_type != item_type:
|
|
291
|
-
continue
|
|
292
|
-
lines.append(f" {label_map[item_type]:<5} {item_id:<6} {entry.key:<6} {entry.url}")
|
|
365
|
+
if total_created > 0:
|
|
366
|
+
lines.append(f" Created: {total_created} ({_type_breakdown(created_epics, created_stories, created_tasks)})")
|
|
367
|
+
if total_matched > 0:
|
|
368
|
+
lines.append(f" Matched: {total_matched} ({_type_breakdown(matched_epics, matched_stories, matched_tasks)})")
|
|
369
|
+
if total_created == 0:
|
|
370
|
+
lines.append(" Status: all items up to date")
|
|
293
371
|
|
|
294
372
|
lines.append("")
|
|
295
373
|
sync_map_path = f"{config.sync_path}.dry-run" if result.dry_run else str(config.sync_path)
|
|
@@ -303,6 +381,43 @@ def _format_summary(result: SyncResult, config: PlanPilotConfig) -> str:
|
|
|
303
381
|
return "\n".join(lines)
|
|
304
382
|
|
|
305
383
|
|
|
384
|
+
async def _run_clean(args: argparse.Namespace) -> CleanResult:
|
|
385
|
+
config = load_config(args.config)
|
|
386
|
+
all_plans: bool = getattr(args, "all", False)
|
|
387
|
+
|
|
388
|
+
if not args.verbose:
|
|
389
|
+
from planpilot.progress import RichSyncProgress
|
|
390
|
+
|
|
391
|
+
with RichSyncProgress() as progress:
|
|
392
|
+
pp = await PlanPilot.from_config(config, progress=progress)
|
|
393
|
+
result = await pp.clean(dry_run=args.dry_run, all_plans=all_plans)
|
|
394
|
+
else:
|
|
395
|
+
pp = await PlanPilot.from_config(config)
|
|
396
|
+
result = await pp.clean(dry_run=args.dry_run, all_plans=all_plans)
|
|
397
|
+
|
|
398
|
+
print(_format_clean_summary(result))
|
|
399
|
+
return result
|
|
400
|
+
|
|
401
|
+
|
|
402
|
+
def _format_clean_summary(result: CleanResult) -> str:
|
|
403
|
+
mode = "dry-run" if result.dry_run else "apply"
|
|
404
|
+
|
|
405
|
+
lines = [
|
|
406
|
+
"",
|
|
407
|
+
f"planpilot - clean complete ({mode})",
|
|
408
|
+
"",
|
|
409
|
+
f" Plan ID: {result.plan_id}",
|
|
410
|
+
f" Deleted: {result.items_deleted} issue{'s' if result.items_deleted != 1 else ''}",
|
|
411
|
+
"",
|
|
412
|
+
]
|
|
413
|
+
|
|
414
|
+
if result.dry_run:
|
|
415
|
+
lines.append(" [dry-run] No issues were deleted")
|
|
416
|
+
lines.append("")
|
|
417
|
+
|
|
418
|
+
return "\n".join(lines)
|
|
419
|
+
|
|
420
|
+
|
|
306
421
|
def _format_map_sync_summary(result: MapSyncResult, config: PlanPilotConfig) -> str:
|
|
307
422
|
mode = "dry-run" if result.dry_run else "apply"
|
|
308
423
|
|
|
@@ -434,8 +549,15 @@ def _run_init_interactive(output: Path) -> int:
|
|
|
434
549
|
|
|
435
550
|
owner_type: str | None = None
|
|
436
551
|
if provider == "github":
|
|
437
|
-
|
|
438
|
-
|
|
552
|
+
if sys.stderr.isatty():
|
|
553
|
+
from planpilot.progress import RichSyncProgress
|
|
554
|
+
|
|
555
|
+
with RichSyncProgress() as progress:
|
|
556
|
+
resolved_token = _resolve_init_token(auth=auth, target=target, static_token=auth_token)
|
|
557
|
+
owner_type = _validate_github_auth_for_init(token=resolved_token, target=target, progress=progress)
|
|
558
|
+
else:
|
|
559
|
+
resolved_token = _resolve_init_token(auth=auth, target=target, static_token=auth_token)
|
|
560
|
+
owner_type = _validate_github_auth_for_init(token=resolved_token, target=target)
|
|
439
561
|
|
|
440
562
|
# --- 4. Board URL ---
|
|
441
563
|
default_board_url = _default_board_url_with_owner_type(target, owner_type)
|
|
@@ -623,7 +745,10 @@ def main(argv: list[str] | None = None) -> int:
|
|
|
623
745
|
logging.basicConfig(level=logging.DEBUG, format="%(name)s %(message)s", stream=sys.stderr)
|
|
624
746
|
|
|
625
747
|
try:
|
|
626
|
-
|
|
748
|
+
if args.command == "sync":
|
|
749
|
+
asyncio.run(_run_sync(args))
|
|
750
|
+
elif args.command == "clean":
|
|
751
|
+
asyncio.run(_run_clean(args))
|
|
627
752
|
return 0
|
|
628
753
|
except (ConfigError, PlanLoadError, PlanValidationError) as exc:
|
|
629
754
|
print(f"error: {exc}", file=sys.stderr)
|
|
@@ -28,6 +28,12 @@ class SyncResult(BaseModel):
|
|
|
28
28
|
dry_run: bool = False
|
|
29
29
|
|
|
30
30
|
|
|
31
|
+
class CleanResult(BaseModel):
|
|
32
|
+
plan_id: str
|
|
33
|
+
items_deleted: int
|
|
34
|
+
dry_run: bool = False
|
|
35
|
+
|
|
36
|
+
|
|
31
37
|
class MapSyncResult(BaseModel):
|
|
32
38
|
sync_map: SyncMap
|
|
33
39
|
added: list[str] = Field(default_factory=list)
|
|
@@ -44,14 +44,19 @@ class SyncEngine:
|
|
|
44
44
|
|
|
45
45
|
existing_map = await self._discover(plan_id)
|
|
46
46
|
item_objects: dict[str, Item] = {}
|
|
47
|
+
plan_type_by_id = {item.id: item.type for item in plan.items}
|
|
47
48
|
for item_id, existing_item in existing_map.items():
|
|
48
|
-
|
|
49
|
+
entry = to_sync_entry(existing_item)
|
|
50
|
+
entry.item_type = plan_type_by_id.get(item_id, entry.item_type)
|
|
51
|
+
sync_map.entries[item_id] = entry
|
|
49
52
|
item_objects[item_id] = existing_item
|
|
50
53
|
|
|
51
54
|
try:
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
await self.
|
|
55
|
+
created_ids: set[str] = set()
|
|
56
|
+
updated_ids: set[str] = set()
|
|
57
|
+
await self._upsert(plan, plan_id, existing_map, sync_map, item_objects, items_created, created_ids)
|
|
58
|
+
await self._enrich(plan, plan_id, sync_map, item_objects, updated_ids)
|
|
59
|
+
await self._set_relations(plan, item_objects, created_ids, updated_ids)
|
|
55
60
|
except* (SyncError, ProviderError) as error_group:
|
|
56
61
|
first_error = error_group.exceptions[0]
|
|
57
62
|
raise first_error from error_group
|
|
@@ -88,6 +93,7 @@ class SyncEngine:
|
|
|
88
93
|
sync_map: SyncMap,
|
|
89
94
|
item_objects: dict[str, Item],
|
|
90
95
|
items_created: dict[PlanItemType, int],
|
|
96
|
+
created_ids: set[str],
|
|
91
97
|
) -> None:
|
|
92
98
|
self._progress.phase_start("Create", total=len(plan.items))
|
|
93
99
|
try:
|
|
@@ -104,6 +110,7 @@ class SyncEngine:
|
|
|
104
110
|
sync_map,
|
|
105
111
|
item_objects,
|
|
106
112
|
items_created,
|
|
113
|
+
created_ids,
|
|
107
114
|
)
|
|
108
115
|
)
|
|
109
116
|
self._progress.phase_done("Create")
|
|
@@ -120,10 +127,13 @@ class SyncEngine:
|
|
|
120
127
|
sync_map: SyncMap,
|
|
121
128
|
item_objects: dict[str, Item],
|
|
122
129
|
items_created: dict[PlanItemType, int],
|
|
130
|
+
created_ids: set[str],
|
|
123
131
|
) -> None:
|
|
124
132
|
if plan_item.id in existing_map:
|
|
125
133
|
existing = existing_map[plan_item.id]
|
|
126
|
-
|
|
134
|
+
entry = to_sync_entry(existing)
|
|
135
|
+
entry.item_type = plan_item.type
|
|
136
|
+
sync_map.entries[plan_item.id] = entry
|
|
127
137
|
item_objects[plan_item.id] = existing
|
|
128
138
|
self._progress.item_done("Create")
|
|
129
139
|
return
|
|
@@ -146,6 +156,7 @@ class SyncEngine:
|
|
|
146
156
|
sync_map.entries[plan_item.id] = to_sync_entry(created_item)
|
|
147
157
|
item_objects[plan_item.id] = created_item
|
|
148
158
|
items_created[plan_item.type] += 1
|
|
159
|
+
created_ids.add(plan_item.id)
|
|
149
160
|
self._progress.item_done("Create")
|
|
150
161
|
|
|
151
162
|
async def _enrich(
|
|
@@ -154,12 +165,13 @@ class SyncEngine:
|
|
|
154
165
|
plan_id: str,
|
|
155
166
|
sync_map: SyncMap,
|
|
156
167
|
item_objects: dict[str, Item],
|
|
168
|
+
updated_ids: set[str] | None = None,
|
|
157
169
|
) -> None:
|
|
158
170
|
self._progress.phase_start("Enrich", total=len(plan.items))
|
|
159
171
|
try:
|
|
160
172
|
async with asyncio.TaskGroup() as tg:
|
|
161
173
|
for plan_item in plan.items:
|
|
162
|
-
tg.create_task(self._enrich_item(plan, plan_item, plan_id, sync_map, item_objects))
|
|
174
|
+
tg.create_task(self._enrich_item(plan, plan_item, plan_id, sync_map, item_objects, updated_ids))
|
|
163
175
|
self._progress.phase_done("Enrich")
|
|
164
176
|
except BaseException as exc:
|
|
165
177
|
self._progress.phase_error("Enrich", exc)
|
|
@@ -172,6 +184,7 @@ class SyncEngine:
|
|
|
172
184
|
plan_id: str,
|
|
173
185
|
sync_map: SyncMap,
|
|
174
186
|
item_objects: dict[str, Item],
|
|
187
|
+
updated_ids: set[str] | None = None,
|
|
175
188
|
) -> None:
|
|
176
189
|
entry = sync_map.entries.get(plan_item.id)
|
|
177
190
|
if entry is None:
|
|
@@ -180,19 +193,51 @@ class SyncEngine:
|
|
|
180
193
|
|
|
181
194
|
context = self._build_context(plan, plan_item, plan_id, sync_map)
|
|
182
195
|
body = self._renderer.render(plan_item, context)
|
|
196
|
+
desired_labels = [self._config.label]
|
|
197
|
+
desired_size = plan_item.estimate.tshirt if plan_item.estimate is not None else None
|
|
198
|
+
|
|
199
|
+
existing_item = item_objects.get(plan_item.id)
|
|
200
|
+
labels_match = True
|
|
201
|
+
size_match = True
|
|
202
|
+
if existing_item is not None:
|
|
203
|
+
existing_labels = getattr(existing_item, "labels", None)
|
|
204
|
+
existing_size = getattr(existing_item, "size", None)
|
|
205
|
+
if existing_labels is not None:
|
|
206
|
+
labels_match = set(existing_labels) == set(desired_labels)
|
|
207
|
+
if existing_size is not None:
|
|
208
|
+
size_match = existing_size == desired_size
|
|
209
|
+
if (
|
|
210
|
+
existing_item is not None
|
|
211
|
+
and existing_item.title == plan_item.title
|
|
212
|
+
and existing_item.body.strip() == body.strip()
|
|
213
|
+
and existing_item.item_type == plan_item.type
|
|
214
|
+
and labels_match
|
|
215
|
+
and size_match
|
|
216
|
+
):
|
|
217
|
+
self._progress.item_done("Enrich")
|
|
218
|
+
return
|
|
219
|
+
|
|
183
220
|
update_input = UpdateItemInput(
|
|
184
221
|
title=plan_item.title,
|
|
185
222
|
body=body,
|
|
186
223
|
item_type=plan_item.type,
|
|
187
|
-
labels=
|
|
188
|
-
size=
|
|
224
|
+
labels=desired_labels,
|
|
225
|
+
size=desired_size,
|
|
189
226
|
)
|
|
190
227
|
|
|
191
228
|
updated_item = await self._guarded(self._provider.update_item(entry.id, update_input))
|
|
192
229
|
item_objects[plan_item.id] = updated_item
|
|
230
|
+
if updated_ids is not None:
|
|
231
|
+
updated_ids.add(plan_item.id)
|
|
193
232
|
self._progress.item_done("Enrich")
|
|
194
233
|
|
|
195
|
-
async def _set_relations(
|
|
234
|
+
async def _set_relations(
|
|
235
|
+
self,
|
|
236
|
+
plan: Plan,
|
|
237
|
+
item_objects: dict[str, Item],
|
|
238
|
+
created_ids: set[str],
|
|
239
|
+
updated_ids: set[str] | None = None,
|
|
240
|
+
) -> None:
|
|
196
241
|
by_id = {item.id: item for item in plan.items}
|
|
197
242
|
plan_ids = set(by_id)
|
|
198
243
|
parent_pairs: set[tuple[str, str]] = set()
|
|
@@ -249,6 +294,14 @@ class SyncEngine:
|
|
|
249
294
|
if child_parent in item_objects and blocker_parent in item_objects and child_parent != blocker_parent:
|
|
250
295
|
dependency_pairs.add((child_parent, blocker_parent))
|
|
251
296
|
|
|
297
|
+
# Skip relation pairs where both sides are untouched in this run.
|
|
298
|
+
# If nothing was touched (e.g., custom renderer omits relation context),
|
|
299
|
+
# keep all pairs so relation-only updates still apply.
|
|
300
|
+
touched_ids = created_ids.union(updated_ids or set())
|
|
301
|
+
if touched_ids:
|
|
302
|
+
parent_pairs = {(c, p) for c, p in parent_pairs if c in touched_ids or p in touched_ids}
|
|
303
|
+
dependency_pairs = {(b, k) for b, k in dependency_pairs if b in touched_ids or k in touched_ids}
|
|
304
|
+
|
|
252
305
|
total_relations = len(parent_pairs) + len(dependency_pairs)
|
|
253
306
|
self._progress.phase_start("Relations", total=total_relations)
|
|
254
307
|
try:
|
|
@@ -31,6 +31,7 @@ from .create_label import (
|
|
|
31
31
|
CreateLabelCreateLabel,
|
|
32
32
|
CreateLabelCreateLabelLabel,
|
|
33
33
|
)
|
|
34
|
+
from .delete_issue import DeleteIssue, DeleteIssueDeleteIssue
|
|
34
35
|
from .enums import IssueClosedStateReason, ProjectV2FieldType
|
|
35
36
|
from .exceptions import (
|
|
36
37
|
GraphQLClientError,
|
|
@@ -104,6 +105,7 @@ from .operations import (
|
|
|
104
105
|
CLOSE_ISSUE_GQL,
|
|
105
106
|
CREATE_ISSUE_GQL,
|
|
106
107
|
CREATE_LABEL_GQL,
|
|
108
|
+
DELETE_ISSUE_GQL,
|
|
107
109
|
FETCH_ORG_PROJECT_GQL,
|
|
108
110
|
FETCH_PROJECT_FIELDS_GQL,
|
|
109
111
|
FETCH_PROJECT_ITEMS_GQL,
|
|
@@ -160,23 +162,6 @@ __all__ = [
|
|
|
160
162
|
"ADD_LABELS_GQL",
|
|
161
163
|
"ADD_PROJECT_ITEM_GQL",
|
|
162
164
|
"ADD_SUB_ISSUE_GQL",
|
|
163
|
-
"CLOSE_ISSUE_GQL",
|
|
164
|
-
"CREATE_ISSUE_GQL",
|
|
165
|
-
"CREATE_LABEL_GQL",
|
|
166
|
-
"FETCH_ORG_PROJECT_GQL",
|
|
167
|
-
"FETCH_PROJECT_FIELDS_GQL",
|
|
168
|
-
"FETCH_PROJECT_ITEMS_GQL",
|
|
169
|
-
"FETCH_RELATIONS_GQL",
|
|
170
|
-
"FETCH_REPO_GQL",
|
|
171
|
-
"FETCH_USER_PROJECT_GQL",
|
|
172
|
-
"FIND_LABELS_GQL",
|
|
173
|
-
"GET_ISSUE_GQL",
|
|
174
|
-
"REMOVE_BLOCKED_BY_GQL",
|
|
175
|
-
"REMOVE_LABELS_GQL",
|
|
176
|
-
"REMOVE_SUB_ISSUE_GQL",
|
|
177
|
-
"SEARCH_ISSUES_GQL",
|
|
178
|
-
"UPDATE_ISSUE_GQL",
|
|
179
|
-
"UPDATE_PROJECT_FIELD_GQL",
|
|
180
165
|
"AddBlockedBy",
|
|
181
166
|
"AddBlockedByAddBlockedBy",
|
|
182
167
|
"AddBlockedByAddBlockedByIssue",
|
|
@@ -191,6 +176,9 @@ __all__ = [
|
|
|
191
176
|
"AddSubIssueAddSubIssueSubIssue",
|
|
192
177
|
"AsyncBaseClient",
|
|
193
178
|
"BaseModel",
|
|
179
|
+
"CLOSE_ISSUE_GQL",
|
|
180
|
+
"CREATE_ISSUE_GQL",
|
|
181
|
+
"CREATE_LABEL_GQL",
|
|
194
182
|
"CloseIssue",
|
|
195
183
|
"CloseIssueCloseIssue",
|
|
196
184
|
"CloseIssueCloseIssueIssue",
|
|
@@ -200,6 +188,16 @@ __all__ = [
|
|
|
200
188
|
"CreateLabel",
|
|
201
189
|
"CreateLabelCreateLabel",
|
|
202
190
|
"CreateLabelCreateLabelLabel",
|
|
191
|
+
"DELETE_ISSUE_GQL",
|
|
192
|
+
"DeleteIssue",
|
|
193
|
+
"DeleteIssueDeleteIssue",
|
|
194
|
+
"FETCH_ORG_PROJECT_GQL",
|
|
195
|
+
"FETCH_PROJECT_FIELDS_GQL",
|
|
196
|
+
"FETCH_PROJECT_ITEMS_GQL",
|
|
197
|
+
"FETCH_RELATIONS_GQL",
|
|
198
|
+
"FETCH_REPO_GQL",
|
|
199
|
+
"FETCH_USER_PROJECT_GQL",
|
|
200
|
+
"FIND_LABELS_GQL",
|
|
203
201
|
"FetchOrgProject",
|
|
204
202
|
"FetchOrgProjectOrganization",
|
|
205
203
|
"FetchOrgProjectOrganizationProjectV2",
|
|
@@ -241,6 +239,7 @@ __all__ = [
|
|
|
241
239
|
"FindLabelsRepository",
|
|
242
240
|
"FindLabelsRepositoryLabels",
|
|
243
241
|
"FindLabelsRepositoryLabelsNodes",
|
|
242
|
+
"GET_ISSUE_GQL",
|
|
244
243
|
"GetIssue",
|
|
245
244
|
"GetIssueNodeIssue",
|
|
246
245
|
"GetIssueNodeNode",
|
|
@@ -255,6 +254,9 @@ __all__ = [
|
|
|
255
254
|
"IssueCoreLabels",
|
|
256
255
|
"IssueCoreLabelsNodes",
|
|
257
256
|
"ProjectV2FieldType",
|
|
257
|
+
"REMOVE_BLOCKED_BY_GQL",
|
|
258
|
+
"REMOVE_LABELS_GQL",
|
|
259
|
+
"REMOVE_SUB_ISSUE_GQL",
|
|
258
260
|
"RemoveBlockedBy",
|
|
259
261
|
"RemoveBlockedByRemoveBlockedBy",
|
|
260
262
|
"RemoveBlockedByRemoveBlockedByIssue",
|
|
@@ -264,6 +266,7 @@ __all__ = [
|
|
|
264
266
|
"RemoveSubIssueRemoveSubIssue",
|
|
265
267
|
"RemoveSubIssueRemoveSubIssueIssue",
|
|
266
268
|
"RemoveSubIssueRemoveSubIssueSubIssue",
|
|
269
|
+
"SEARCH_ISSUES_GQL",
|
|
267
270
|
"SearchIssues",
|
|
268
271
|
"SearchIssuesSearch",
|
|
269
272
|
"SearchIssuesSearchNodesApp",
|
|
@@ -275,6 +278,8 @@ __all__ = [
|
|
|
275
278
|
"SearchIssuesSearchNodesRepository",
|
|
276
279
|
"SearchIssuesSearchNodesUser",
|
|
277
280
|
"SearchIssuesSearchPageInfo",
|
|
281
|
+
"UPDATE_ISSUE_GQL",
|
|
282
|
+
"UPDATE_PROJECT_FIELD_GQL",
|
|
278
283
|
"UpdateIssue",
|
|
279
284
|
"UpdateIssueUpdateIssue",
|
|
280
285
|
"UpdateIssueUpdateIssueIssue",
|