agentic-devtools 0.2.258__py3-none-any.whl → 0.2.259__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.
- agentic_devtools/_version.py +2 -2
- agentic_devtools/adapters/__init__.py +7 -0
- agentic_devtools/adapters/base.py +47 -101
- agentic_devtools/adapters/exceptions.py +24 -0
- agentic_devtools/adapters/github_adapter.py +9 -0
- agentic_devtools/adapters/jira_adapter.py +9 -0
- agentic_devtools/adapters/markdown_adapter.py +9 -0
- agentic_devtools/adapters/types.py +205 -0
- {agentic_devtools-0.2.258.dist-info → agentic_devtools-0.2.259.dist-info}/METADATA +1 -1
- {agentic_devtools-0.2.258.dist-info → agentic_devtools-0.2.259.dist-info}/RECORD +13 -11
- {agentic_devtools-0.2.258.dist-info → agentic_devtools-0.2.259.dist-info}/WHEEL +0 -0
- {agentic_devtools-0.2.258.dist-info → agentic_devtools-0.2.259.dist-info}/entry_points.txt +0 -0
- {agentic_devtools-0.2.258.dist-info → agentic_devtools-0.2.259.dist-info}/licenses/LICENSE +0 -0
agentic_devtools/_version.py
CHANGED
|
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
|
|
|
18
18
|
commit_id: str | None
|
|
19
19
|
__commit_id__: str | None
|
|
20
20
|
|
|
21
|
-
__version__ = version = '0.2.
|
|
22
|
-
__version_tuple__ = version_tuple = (0, 2,
|
|
21
|
+
__version__ = version = '0.2.259'
|
|
22
|
+
__version_tuple__ = version_tuple = (0, 2, 259)
|
|
23
23
|
|
|
24
24
|
__commit_id__ = commit_id = None
|
|
@@ -19,10 +19,13 @@ from agentic_devtools.adapters.base import (
|
|
|
19
19
|
CommentResult,
|
|
20
20
|
IssueAdapter,
|
|
21
21
|
IssueDetail,
|
|
22
|
+
IssueDetailWithRaw,
|
|
22
23
|
IssueFilters,
|
|
23
24
|
IssueResult,
|
|
24
25
|
IssueSummary,
|
|
26
|
+
NormalizedIssue,
|
|
25
27
|
)
|
|
28
|
+
from agentic_devtools.adapters.exceptions import AdapterError, AdapterValidationError
|
|
26
29
|
from agentic_devtools.adapters.github_adapter import GitHubIssuesAdapter
|
|
27
30
|
from agentic_devtools.adapters.jira_adapter import JiraAdapter
|
|
28
31
|
from agentic_devtools.adapters.markdown_adapter import MarkdownAdapter
|
|
@@ -30,16 +33,20 @@ from agentic_devtools.config import load_platform_config
|
|
|
30
33
|
from agentic_devtools.tools.jira import JiraConfig
|
|
31
34
|
|
|
32
35
|
__all__ = [
|
|
36
|
+
"AdapterError",
|
|
37
|
+
"AdapterValidationError",
|
|
33
38
|
"Comment",
|
|
34
39
|
"CommentResult",
|
|
35
40
|
"GitHubIssuesAdapter",
|
|
36
41
|
"IssueAdapter",
|
|
37
42
|
"IssueDetail",
|
|
43
|
+
"IssueDetailWithRaw",
|
|
38
44
|
"IssueFilters",
|
|
39
45
|
"IssueResult",
|
|
40
46
|
"IssueSummary",
|
|
41
47
|
"JiraAdapter",
|
|
42
48
|
"MarkdownAdapter",
|
|
49
|
+
"NormalizedIssue",
|
|
43
50
|
"get_adapter",
|
|
44
51
|
]
|
|
45
52
|
|
|
@@ -2,114 +2,42 @@
|
|
|
2
2
|
|
|
3
3
|
Defines the ``IssueAdapter`` ABC that all platform-specific adapters must
|
|
4
4
|
implement, plus the shared ``TypedDict`` result types used across adapters.
|
|
5
|
+
|
|
6
|
+
Type definitions live in :mod:`agentic_devtools.adapters.types` and are
|
|
7
|
+
re-exported here for backward compatibility.
|
|
5
8
|
"""
|
|
6
9
|
|
|
7
10
|
from __future__ import annotations
|
|
8
11
|
|
|
9
12
|
from abc import ABC, abstractmethod
|
|
10
13
|
|
|
11
|
-
from
|
|
12
|
-
|
|
14
|
+
from agentic_devtools.adapters.types import (
|
|
15
|
+
Comment,
|
|
16
|
+
CommentResult,
|
|
17
|
+
IssueDetail,
|
|
18
|
+
IssueDetailWithRaw,
|
|
19
|
+
IssueFilters,
|
|
20
|
+
IssueResult,
|
|
21
|
+
IssueSummary,
|
|
22
|
+
NormalizedIssue,
|
|
23
|
+
_IssueDetailRequired,
|
|
24
|
+
)
|
|
13
25
|
from agentic_devtools.orchestration.execution.types import JSONValue
|
|
14
26
|
|
|
15
|
-
#
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
""
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
"""Result of creating an issue."""
|
|
30
|
-
|
|
31
|
-
issue_id: str
|
|
32
|
-
url: str
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
class _IssueDetailRequired(TypedDict, total=True):
|
|
36
|
-
"""Required fields for issue details (private implementation base)."""
|
|
37
|
-
|
|
38
|
-
issue_id: str
|
|
39
|
-
title: str
|
|
40
|
-
description: str
|
|
41
|
-
status: str
|
|
42
|
-
labels: list[str]
|
|
43
|
-
url: str
|
|
44
|
-
comments: list[Comment]
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
class IssueDetail(_IssueDetailRequired, total=False):
|
|
48
|
-
"""Full detail of a single issue.
|
|
49
|
-
|
|
50
|
-
Required fields (always present):
|
|
51
|
-
issue_id: Platform-specific unique issue identifier.
|
|
52
|
-
title: Issue title / summary text.
|
|
53
|
-
description: Full issue description body.
|
|
54
|
-
status: Current workflow status (e.g. "open", "in_progress", "closed").
|
|
55
|
-
labels: List of labels/tags applied to the issue.
|
|
56
|
-
url: Web URL for the issue on its platform.
|
|
57
|
-
comments: List of :class:`Comment` objects on this issue.
|
|
58
|
-
|
|
59
|
-
Optional normalization fields (present when adapter populates them):
|
|
60
|
-
issue_type: The issue type classification (e.g. "bug", "story", "task",
|
|
61
|
-
"epic"). Adapter-specific; no enum enforced.
|
|
62
|
-
Example: ``"bug"``
|
|
63
|
-
priority: Priority level as a human-readable string (e.g. "high",
|
|
64
|
-
"medium", "low", "P1"). Adapter-specific naming.
|
|
65
|
-
Example: ``"high"``
|
|
66
|
-
assignees: List of usernames or display names assigned to the issue.
|
|
67
|
-
An empty list indicates no assignees; key absence means the adapter
|
|
68
|
-
did not fetch assignee data.
|
|
69
|
-
Example: ``["alice", "bob"]``
|
|
70
|
-
custom_fields: Arbitrary key-value mapping of provider-specific custom
|
|
71
|
-
fields. Values may be nested dicts, lists, or scalar types.
|
|
72
|
-
Must be JSON-serializable for persistence.
|
|
73
|
-
Example: ``{"sprint": "Sprint 42", "story_points": 5}``
|
|
74
|
-
provider: Identifier for the originating platform adapter (e.g.
|
|
75
|
-
"github", "jira", "markdown"). Useful for downstream normalization.
|
|
76
|
-
Example: ``"jira"``
|
|
77
|
-
raw_metadata: Opaque provider-specific metadata preserved verbatim
|
|
78
|
-
from the API response. Structure varies by adapter; must be
|
|
79
|
-
JSON-serializable.
|
|
80
|
-
Example: ``{"api_version": "3", "expand": "changelog"}``
|
|
81
|
-
"""
|
|
82
|
-
|
|
83
|
-
issue_type: str
|
|
84
|
-
priority: str
|
|
85
|
-
assignees: list[str]
|
|
86
|
-
custom_fields: dict[str, JSONValue]
|
|
87
|
-
provider: str
|
|
88
|
-
raw_metadata: JSONValue
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
class CommentResult(TypedDict):
|
|
92
|
-
"""Result of adding a comment."""
|
|
93
|
-
|
|
94
|
-
comment_id: str
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
class IssueSummary(TypedDict):
|
|
98
|
-
"""Summary of an issue for list results."""
|
|
99
|
-
|
|
100
|
-
issue_id: str
|
|
101
|
-
title: str
|
|
102
|
-
status: str
|
|
103
|
-
labels: list[str]
|
|
104
|
-
url: str
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
class IssueFilters(TypedDict, total=False):
|
|
108
|
-
"""Optional filters for listing issues."""
|
|
109
|
-
|
|
110
|
-
labels: list[str]
|
|
111
|
-
state: str
|
|
112
|
-
assignee: str
|
|
27
|
+
# Re-export all symbols for backward compatibility
|
|
28
|
+
__all__ = [
|
|
29
|
+
"Comment",
|
|
30
|
+
"CommentResult",
|
|
31
|
+
"IssueAdapter",
|
|
32
|
+
"IssueDetail",
|
|
33
|
+
"IssueDetailWithRaw",
|
|
34
|
+
"IssueFilters",
|
|
35
|
+
"IssueResult",
|
|
36
|
+
"IssueSummary",
|
|
37
|
+
"JSONValue",
|
|
38
|
+
"NormalizedIssue",
|
|
39
|
+
"_IssueDetailRequired",
|
|
40
|
+
]
|
|
113
41
|
|
|
114
42
|
|
|
115
43
|
# ---------------------------------------------------------------------------
|
|
@@ -120,7 +48,8 @@ class IssueFilters(TypedDict, total=False):
|
|
|
120
48
|
class IssueAdapter(ABC):
|
|
121
49
|
"""Abstract base class for issue-tracking platform adapters.
|
|
122
50
|
|
|
123
|
-
Concrete subclasses must implement all
|
|
51
|
+
Concrete subclasses must implement all abstract methods including
|
|
52
|
+
:meth:`normalize`.
|
|
124
53
|
"""
|
|
125
54
|
|
|
126
55
|
@abstractmethod
|
|
@@ -169,3 +98,20 @@ class IssueAdapter(ABC):
|
|
|
169
98
|
Returns:
|
|
170
99
|
A list of :class:`IssueSummary` items.
|
|
171
100
|
"""
|
|
101
|
+
|
|
102
|
+
@abstractmethod
|
|
103
|
+
def normalize(self, issue_detail: IssueDetailWithRaw) -> NormalizedIssue:
|
|
104
|
+
"""Normalize a raw issue detail into a provider-agnostic representation.
|
|
105
|
+
|
|
106
|
+
Transforms platform-specific :class:`IssueDetailWithRaw` data into a
|
|
107
|
+
uniform :class:`NormalizedIssue` instance suitable for downstream
|
|
108
|
+
consumers (template renderers, spec generators, etc.).
|
|
109
|
+
|
|
110
|
+
Args:
|
|
111
|
+
issue_detail: An :class:`IssueDetailWithRaw` containing the full
|
|
112
|
+
issue data and an optional raw provider response payload.
|
|
113
|
+
|
|
114
|
+
Returns:
|
|
115
|
+
A :class:`NormalizedIssue` with validated identity fields and
|
|
116
|
+
coerced optional fields.
|
|
117
|
+
"""
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"""Exception hierarchy for issue adapters.
|
|
2
|
+
|
|
3
|
+
Provides a minimal exception tree for adapter-level errors:
|
|
4
|
+
|
|
5
|
+
- :class:`AdapterError` — base class for all adapter exceptions.
|
|
6
|
+
- :class:`AdapterValidationError` — raised when identity field validation
|
|
7
|
+
fails during :class:`~agentic_devtools.adapters.types.NormalizedIssue`
|
|
8
|
+
construction.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class AdapterError(Exception):
|
|
15
|
+
"""Base exception for all issue adapter errors."""
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class AdapterValidationError(AdapterError):
|
|
19
|
+
"""Raised when adapter identity field validation fails.
|
|
20
|
+
|
|
21
|
+
Typically triggered during :class:`NormalizedIssue` construction when
|
|
22
|
+
required identity fields (``issue_id``, ``title``, ``url``, ``provider``)
|
|
23
|
+
are ``None``, empty, or whitespace-only.
|
|
24
|
+
"""
|
|
@@ -15,9 +15,11 @@ from agentic_devtools.adapters.base import (
|
|
|
15
15
|
CommentResult,
|
|
16
16
|
IssueAdapter,
|
|
17
17
|
IssueDetail,
|
|
18
|
+
IssueDetailWithRaw,
|
|
18
19
|
IssueFilters,
|
|
19
20
|
IssueResult,
|
|
20
21
|
IssueSummary,
|
|
22
|
+
NormalizedIssue,
|
|
21
23
|
)
|
|
22
24
|
from agentic_devtools.cli.subprocess_utils import run_safe
|
|
23
25
|
|
|
@@ -166,3 +168,10 @@ class GitHubIssuesAdapter(IssueAdapter):
|
|
|
166
168
|
)
|
|
167
169
|
)
|
|
168
170
|
return summaries
|
|
171
|
+
|
|
172
|
+
def normalize(self, issue_detail: IssueDetailWithRaw) -> NormalizedIssue:
|
|
173
|
+
"""Normalize a GitHub issue detail into a provider-agnostic representation.
|
|
174
|
+
|
|
175
|
+
Not yet implemented — tracked in #1774.
|
|
176
|
+
"""
|
|
177
|
+
raise NotImplementedError("GitHubIssuesAdapter.normalize() is not yet implemented. See #1774.")
|
|
@@ -11,9 +11,11 @@ from agentic_devtools.adapters.base import (
|
|
|
11
11
|
CommentResult,
|
|
12
12
|
IssueAdapter,
|
|
13
13
|
IssueDetail,
|
|
14
|
+
IssueDetailWithRaw,
|
|
14
15
|
IssueFilters,
|
|
15
16
|
IssueResult,
|
|
16
17
|
IssueSummary,
|
|
18
|
+
NormalizedIssue,
|
|
17
19
|
)
|
|
18
20
|
from agentic_devtools.tools.jira import JiraConfig
|
|
19
21
|
from agentic_devtools.tools.jira import add_comment as jira_add_comment
|
|
@@ -117,3 +119,10 @@ class JiraAdapter(IssueAdapter):
|
|
|
117
119
|
def list_issues(self, filters: IssueFilters | None = None) -> list[IssueSummary]:
|
|
118
120
|
"""Not yet implemented for Jira."""
|
|
119
121
|
raise NotImplementedError("JiraAdapter.list_issues is not yet implemented. Full Jira search is a future issue.")
|
|
122
|
+
|
|
123
|
+
def normalize(self, issue_detail: IssueDetailWithRaw) -> NormalizedIssue:
|
|
124
|
+
"""Normalize a Jira issue detail into a provider-agnostic representation.
|
|
125
|
+
|
|
126
|
+
Not yet implemented — tracked in #1775.
|
|
127
|
+
"""
|
|
128
|
+
raise NotImplementedError("JiraAdapter.normalize() is not yet implemented. See #1775.")
|
|
@@ -19,9 +19,11 @@ from agentic_devtools.adapters.base import (
|
|
|
19
19
|
CommentResult,
|
|
20
20
|
IssueAdapter,
|
|
21
21
|
IssueDetail,
|
|
22
|
+
IssueDetailWithRaw,
|
|
22
23
|
IssueFilters,
|
|
23
24
|
IssueResult,
|
|
24
25
|
IssueSummary,
|
|
26
|
+
NormalizedIssue,
|
|
25
27
|
)
|
|
26
28
|
|
|
27
29
|
_ID_PATTERN = re.compile(r"^\d{3}$")
|
|
@@ -284,3 +286,10 @@ class MarkdownAdapter(IssueAdapter):
|
|
|
284
286
|
)
|
|
285
287
|
)
|
|
286
288
|
return summaries
|
|
289
|
+
|
|
290
|
+
def normalize(self, issue_detail: IssueDetailWithRaw) -> NormalizedIssue:
|
|
291
|
+
"""Normalize a Markdown issue detail into a provider-agnostic representation.
|
|
292
|
+
|
|
293
|
+
Not yet implemented — tracked in #1776.
|
|
294
|
+
"""
|
|
295
|
+
raise NotImplementedError("MarkdownAdapter.normalize() is not yet implemented. See #1776.")
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
"""Shared type definitions for issue adapters.
|
|
2
|
+
|
|
3
|
+
Contains all TypedDict result types originally defined in ``base.py``, plus:
|
|
4
|
+
|
|
5
|
+
- :class:`IssueDetailWithRaw` — extends :class:`IssueDetail` with a ``raw`` field.
|
|
6
|
+
- :class:`NormalizedIssue` — frozen dataclass representing a provider-agnostic
|
|
7
|
+
normalized issue with identity validation and coercion.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
from dataclasses import dataclass, field
|
|
13
|
+
from typing import Any
|
|
14
|
+
|
|
15
|
+
from typing_extensions import TypedDict
|
|
16
|
+
|
|
17
|
+
from agentic_devtools.adapters.exceptions import AdapterValidationError
|
|
18
|
+
from agentic_devtools.orchestration.execution.types import JSONValue
|
|
19
|
+
|
|
20
|
+
# ---------------------------------------------------------------------------
|
|
21
|
+
# Shared result TypedDicts (moved from base.py)
|
|
22
|
+
# ---------------------------------------------------------------------------
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class Comment(TypedDict):
|
|
26
|
+
"""A single comment on an issue."""
|
|
27
|
+
|
|
28
|
+
comment_id: str
|
|
29
|
+
body: str
|
|
30
|
+
created_at: str
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class IssueResult(TypedDict):
|
|
34
|
+
"""Result of creating an issue."""
|
|
35
|
+
|
|
36
|
+
issue_id: str
|
|
37
|
+
url: str
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class _IssueDetailRequired(TypedDict, total=True):
|
|
41
|
+
"""Required fields for issue details (private implementation base)."""
|
|
42
|
+
|
|
43
|
+
issue_id: str
|
|
44
|
+
title: str
|
|
45
|
+
description: str
|
|
46
|
+
status: str
|
|
47
|
+
labels: list[str]
|
|
48
|
+
url: str
|
|
49
|
+
comments: list[Comment]
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
class IssueDetail(_IssueDetailRequired, total=False):
|
|
53
|
+
"""Full detail of a single issue.
|
|
54
|
+
|
|
55
|
+
Required fields (always present):
|
|
56
|
+
issue_id: Platform-specific unique issue identifier.
|
|
57
|
+
title: Issue title / summary text.
|
|
58
|
+
description: Full issue description body.
|
|
59
|
+
status: Current workflow status (e.g. "open", "in_progress", "closed").
|
|
60
|
+
labels: List of labels/tags applied to the issue.
|
|
61
|
+
url: Web URL for the issue on its platform.
|
|
62
|
+
comments: List of :class:`Comment` objects on this issue.
|
|
63
|
+
|
|
64
|
+
Optional normalization fields (present when adapter populates them):
|
|
65
|
+
issue_type: The issue type classification (e.g. "bug", "story", "task",
|
|
66
|
+
"epic"). Adapter-specific; no enum enforced.
|
|
67
|
+
Example: ``"bug"``
|
|
68
|
+
priority: Priority level as a human-readable string (e.g. "high",
|
|
69
|
+
"medium", "low", "P1"). Adapter-specific naming.
|
|
70
|
+
Example: ``"high"``
|
|
71
|
+
assignees: List of usernames or display names assigned to the issue.
|
|
72
|
+
An empty list indicates no assignees; key absence means the adapter
|
|
73
|
+
did not fetch assignee data.
|
|
74
|
+
Example: ``["alice", "bob"]``
|
|
75
|
+
custom_fields: Arbitrary key-value mapping of provider-specific custom
|
|
76
|
+
fields. Values may be nested dicts, lists, or scalar types.
|
|
77
|
+
Must be JSON-serializable for persistence.
|
|
78
|
+
Example: ``{"sprint": "Sprint 42", "story_points": 5}``
|
|
79
|
+
provider: Identifier for the originating platform adapter (e.g.
|
|
80
|
+
"github", "jira", "markdown"). Useful for downstream normalization.
|
|
81
|
+
Example: ``"jira"``
|
|
82
|
+
raw_metadata: Opaque provider-specific metadata preserved verbatim
|
|
83
|
+
from the API response. Structure varies by adapter; must be
|
|
84
|
+
JSON-serializable.
|
|
85
|
+
Example: ``{"api_version": "3", "expand": "changelog"}``
|
|
86
|
+
"""
|
|
87
|
+
|
|
88
|
+
issue_type: str
|
|
89
|
+
priority: str
|
|
90
|
+
assignees: list[str]
|
|
91
|
+
custom_fields: dict[str, JSONValue]
|
|
92
|
+
provider: str
|
|
93
|
+
raw_metadata: JSONValue
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
class CommentResult(TypedDict):
|
|
97
|
+
"""Result of adding a comment."""
|
|
98
|
+
|
|
99
|
+
comment_id: str
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
class IssueSummary(TypedDict):
|
|
103
|
+
"""Summary of an issue for list results."""
|
|
104
|
+
|
|
105
|
+
issue_id: str
|
|
106
|
+
title: str
|
|
107
|
+
status: str
|
|
108
|
+
labels: list[str]
|
|
109
|
+
url: str
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
class IssueFilters(TypedDict, total=False):
|
|
113
|
+
"""Optional filters for listing issues."""
|
|
114
|
+
|
|
115
|
+
labels: list[str]
|
|
116
|
+
state: str
|
|
117
|
+
assignee: str
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
# ---------------------------------------------------------------------------
|
|
121
|
+
# Extended TypedDict: IssueDetailWithRaw
|
|
122
|
+
# ---------------------------------------------------------------------------
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
class IssueDetailWithRaw(IssueDetail, total=False):
|
|
126
|
+
"""Issue detail extended with raw provider response data.
|
|
127
|
+
|
|
128
|
+
Inherits all fields from :class:`IssueDetail` and optionally adds a
|
|
129
|
+
``raw`` dict containing the unmodified provider API response, enabling
|
|
130
|
+
downstream normalizers to access fields not covered by the base schema.
|
|
131
|
+
"""
|
|
132
|
+
|
|
133
|
+
raw: dict[str, Any]
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
# ---------------------------------------------------------------------------
|
|
137
|
+
# Normalized issue dataclass
|
|
138
|
+
# ---------------------------------------------------------------------------
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
@dataclass(frozen=True)
|
|
142
|
+
class NormalizedIssue:
|
|
143
|
+
"""Provider-agnostic normalized representation of an issue.
|
|
144
|
+
|
|
145
|
+
All adapters' ``normalize()`` method returns an instance of this class,
|
|
146
|
+
ensuring a uniform contract for downstream consumers (template renderers,
|
|
147
|
+
spec generators, etc.).
|
|
148
|
+
|
|
149
|
+
Identity fields (``issue_id``, ``title``, ``url``, ``provider``) are
|
|
150
|
+
validated in ``__post_init__`` as non-empty strings —
|
|
151
|
+
``None``, non-string, empty, or whitespace-only values raise
|
|
152
|
+
:class:`AdapterValidationError`.
|
|
153
|
+
|
|
154
|
+
Coercion rules applied in ``__post_init__``:
|
|
155
|
+
- ``description``: ``None`` → ``""``
|
|
156
|
+
- ``comments``: ``None`` → ``[]``
|
|
157
|
+
- ``labels``: non-list → ``[]``
|
|
158
|
+
- ``status``: ``None`` or non-string → ``"unknown"``; lowercased
|
|
159
|
+
"""
|
|
160
|
+
|
|
161
|
+
issue_id: str
|
|
162
|
+
title: str
|
|
163
|
+
url: str
|
|
164
|
+
provider: str
|
|
165
|
+
description: str
|
|
166
|
+
status: str
|
|
167
|
+
labels: list[str] = field(default_factory=list)
|
|
168
|
+
comments: list[Comment] = field(default_factory=list)
|
|
169
|
+
created_at: str = ""
|
|
170
|
+
updated_at: str = ""
|
|
171
|
+
raw: dict[str, Any] = field(default_factory=dict)
|
|
172
|
+
|
|
173
|
+
def __post_init__(self) -> None:
|
|
174
|
+
"""Validate identity fields and coerce optional fields."""
|
|
175
|
+
# Identity validation
|
|
176
|
+
for field_name in ("issue_id", "title", "url", "provider"):
|
|
177
|
+
value = getattr(self, field_name)
|
|
178
|
+
if not isinstance(value, str) or not value.strip():
|
|
179
|
+
raise AdapterValidationError(f"NormalizedIssue.{field_name} must be a non-empty string; got {value!r}")
|
|
180
|
+
|
|
181
|
+
# Coerce description
|
|
182
|
+
if getattr(self, "description") is None:
|
|
183
|
+
object.__setattr__(self, "description", "")
|
|
184
|
+
|
|
185
|
+
# Coerce comments then defensive-copy to prevent external mutation
|
|
186
|
+
comments = getattr(self, "comments")
|
|
187
|
+
object.__setattr__(self, "comments", [] if comments is None else list(comments))
|
|
188
|
+
|
|
189
|
+
# Coerce labels then defensive-copy to prevent external mutation
|
|
190
|
+
labels = getattr(self, "labels")
|
|
191
|
+
object.__setattr__(self, "labels", [] if labels is None or not isinstance(labels, list) else list(labels))
|
|
192
|
+
|
|
193
|
+
# Coerce status
|
|
194
|
+
status = getattr(self, "status")
|
|
195
|
+
if status is None or not isinstance(status, str):
|
|
196
|
+
object.__setattr__(self, "status", "unknown")
|
|
197
|
+
else:
|
|
198
|
+
object.__setattr__(self, "status", status.lower())
|
|
199
|
+
|
|
200
|
+
# Defensive-copy raw to prevent external mutation
|
|
201
|
+
object.__setattr__(self, "raw", dict(getattr(self, "raw")))
|
|
202
|
+
|
|
203
|
+
def __hash__(self) -> int:
|
|
204
|
+
"""NormalizedIssue is not hashable due to mutable fields."""
|
|
205
|
+
raise TypeError(f"unhashable type: '{type(self).__name__}'")
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
agentic_devtools/__init__.py,sha256=J_Zw_vWKghk-cLmqI83hXQmSiS8zMhGIHM5WPLDkZuo,242
|
|
2
|
-
agentic_devtools/_version.py,sha256=
|
|
2
|
+
agentic_devtools/_version.py,sha256=oYVy2gkppQXpBIPMhcfNgGDqrHlFOJg-qvATCFO1Ccg,524
|
|
3
3
|
agentic_devtools/agdt_gitignore.py,sha256=Jcc9BUiCIqCG9tUuHc5Jua5tPT_QtTnEK6Gr0v_RrKQ,1559
|
|
4
4
|
agentic_devtools/background_tasks.py,sha256=QCiCSTcU8HSFa_g_fa2NWXhhRZU4zo6YgsPSIVVmdOU,16971
|
|
5
5
|
agentic_devtools/config.py,sha256=X6zv8Twviv9LMiV7TYy_6SQeNezJHV8lBNvIxLkOqLY,8049
|
|
@@ -14,11 +14,13 @@ agentic_devtools/_bundled_skills/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRk
|
|
|
14
14
|
agentic_devtools/_bundled_skills/agents/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
15
15
|
agentic_devtools/_bundled_skills/prompts/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
16
16
|
agentic_devtools/_bundled_skills/workflow-analysis/SKILL.md,sha256=imOFULtiGqsFEljGx0ly7_ntnZAmLWuYAF0HL82MIVA,21153
|
|
17
|
-
agentic_devtools/adapters/__init__.py,sha256=
|
|
18
|
-
agentic_devtools/adapters/base.py,sha256=
|
|
19
|
-
agentic_devtools/adapters/
|
|
20
|
-
agentic_devtools/adapters/
|
|
21
|
-
agentic_devtools/adapters/
|
|
17
|
+
agentic_devtools/adapters/__init__.py,sha256=5Qy6VAi7HjpSbVFUlVTETarv0Gdt7XcO5vY7VgUsSyU,6420
|
|
18
|
+
agentic_devtools/adapters/base.py,sha256=TEOCW79kCN1jBYir7mlShRIxRRv3O6CnqoCHvMDTDPA,3449
|
|
19
|
+
agentic_devtools/adapters/exceptions.py,sha256=SJJz53MjiOk2DWAmddThHvbtk0yfovU5DlyW77su1rA,777
|
|
20
|
+
agentic_devtools/adapters/github_adapter.py,sha256=7IOKiTB1PtwAaLqaoM26pQg0yNThouxTi_-gBUfie4w,6913
|
|
21
|
+
agentic_devtools/adapters/jira_adapter.py,sha256=AYYqtx12fZO44Dk7YfW6Lq_Tk_LHVUn6KN9DDQim1WY,5278
|
|
22
|
+
agentic_devtools/adapters/markdown_adapter.py,sha256=poSKmq0-XBL1-537re6uUdGMzQDt18BEc38M0pBU74I,11608
|
|
23
|
+
agentic_devtools/adapters/types.py,sha256=oXZylcbHdJ-yPeRiIL2yw6pzfrK0-sp0fe7unHaVBZQ,7231
|
|
22
24
|
agentic_devtools/cli/__init__.py,sha256=VgHQrGk0NuMaGMdvooFwz3nGY1NzVi6kX5Fr7wF-Tdw,40
|
|
23
25
|
agentic_devtools/cli/activity_log.py,sha256=Cv-LEF2utB4UqJ4k_UZkbz8uEAnku8XwPt93CSkKTA0,11474
|
|
24
26
|
agentic_devtools/cli/apply_thread_autofix_suggestions.py,sha256=-zQ7oWdNFj_ajuUKXT3nDYUR8YGA6oGEN1bZtjIwcj4,10176
|
|
@@ -745,8 +747,8 @@ agentic_devtools/_bundled_skills/prompts/speckit.plan.prompt.md,sha256=IJja5r2Sd
|
|
|
745
747
|
agentic_devtools/_bundled_skills/prompts/speckit.specify.prompt.md,sha256=eyzE3GRi2hyW30a6xPYOU7q6MJf0skrD-baEGURYqpg,31
|
|
746
748
|
agentic_devtools/_bundled_skills/prompts/speckit.tasks.prompt.md,sha256=iPxXwon5nV6dNcJV8-JoP3PssKUVXctNiG-C9SsRhB8,29
|
|
747
749
|
agentic_devtools/_bundled_skills/prompts/speckit.taskstoissues.prompt.md,sha256=L5Y21PMSoUcPAAdHy2Jnf-wGVdi04jV_pPvyOJZfpm0,37
|
|
748
|
-
agentic_devtools-0.2.
|
|
749
|
-
agentic_devtools-0.2.
|
|
750
|
-
agentic_devtools-0.2.
|
|
751
|
-
agentic_devtools-0.2.
|
|
752
|
-
agentic_devtools-0.2.
|
|
750
|
+
agentic_devtools-0.2.259.dist-info/METADATA,sha256=UUs-zlNJ_-0uM2dnS_Zsj9sSGMSEI931gMZ2wnj6ZLY,29707
|
|
751
|
+
agentic_devtools-0.2.259.dist-info/WHEEL,sha256=mffPy8wBnZQn2VnJUU5jE99KsxaSfiyMHV9Yt0aLVxs,87
|
|
752
|
+
agentic_devtools-0.2.259.dist-info/entry_points.txt,sha256=w34XSOdR4kX3N64ZmSlfcUinrIp_mSXiPfNNew1efzg,10749
|
|
753
|
+
agentic_devtools-0.2.259.dist-info/licenses/LICENSE,sha256=yBEDdICksxhBYLWoERKp9MTqwGnUF6Ryj9BTLwXTc6k,1082
|
|
754
|
+
agentic_devtools-0.2.259.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|