kd-cli 2.6.5__tar.gz → 2.6.6__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.
- {kd_cli-2.6.5/kd_cli.egg-info → kd_cli-2.6.6}/PKG-INFO +1 -1
- {kd_cli-2.6.5 → kd_cli-2.6.6}/kd/cache.py +91 -6
- {kd_cli-2.6.5 → kd_cli-2.6.6}/kd/cli/namespaces/cache/__init__.py +5 -2
- {kd_cli-2.6.5 → kd_cli-2.6.6}/kd/cli/namespaces/primer/__init__.py +397 -13
- {kd_cli-2.6.5 → kd_cli-2.6.6}/kd/cli/output.py +34 -3
- {kd_cli-2.6.5 → kd_cli-2.6.6}/kd/primer.py +176 -2
- {kd_cli-2.6.5 → kd_cli-2.6.6}/kd/resources/CHANGELOG.md +29 -0
- {kd_cli-2.6.5 → kd_cli-2.6.6}/kd/resources/operating-guide.md +33 -3
- {kd_cli-2.6.5 → kd_cli-2.6.6/kd_cli.egg-info}/PKG-INFO +1 -1
- {kd_cli-2.6.5 → kd_cli-2.6.6}/pyproject.toml +1 -1
- {kd_cli-2.6.5 → kd_cli-2.6.6}/LICENSE +0 -0
- {kd_cli-2.6.5 → kd_cli-2.6.6}/MANIFEST.in +0 -0
- {kd_cli-2.6.5 → kd_cli-2.6.6}/README.md +0 -0
- {kd_cli-2.6.5 → kd_cli-2.6.6}/kd/__init__.py +0 -0
- {kd_cli-2.6.5 → kd_cli-2.6.6}/kd/api/__init__.py +0 -0
- {kd_cli-2.6.5 → kd_cli-2.6.6}/kd/api/articles.py +0 -0
- {kd_cli-2.6.5 → kd_cli-2.6.6}/kd/api/http.py +0 -0
- {kd_cli-2.6.5 → kd_cli-2.6.6}/kd/api/hub.py +0 -0
- {kd_cli-2.6.5 → kd_cli-2.6.6}/kd/api/saved_queries.py +0 -0
- {kd_cli-2.6.5 → kd_cli-2.6.6}/kd/api/tags.py +0 -0
- {kd_cli-2.6.5 → kd_cli-2.6.6}/kd/api/youtrack.py +0 -0
- {kd_cli-2.6.5 → kd_cli-2.6.6}/kd/article_marker.py +0 -0
- {kd_cli-2.6.5 → kd_cli-2.6.6}/kd/auth.py +0 -0
- {kd_cli-2.6.5 → kd_cli-2.6.6}/kd/cli/__init__.py +0 -0
- {kd_cli-2.6.5 → kd_cli-2.6.6}/kd/cli/core/__init__.py +0 -0
- {kd_cli-2.6.5 → kd_cli-2.6.6}/kd/cli/core/command_base.py +0 -0
- {kd_cli-2.6.5 → kd_cli-2.6.6}/kd/cli/core/command_parser.py +0 -0
- {kd_cli-2.6.5 → kd_cli-2.6.6}/kd/cli/core/confirm.py +0 -0
- {kd_cli-2.6.5 → kd_cli-2.6.6}/kd/cli/core/namespaces.py +0 -0
- {kd_cli-2.6.5 → kd_cli-2.6.6}/kd/cli/core/option_registry.py +0 -0
- {kd_cli-2.6.5 → kd_cli-2.6.6}/kd/cli/input.py +0 -0
- {kd_cli-2.6.5 → kd_cli-2.6.6}/kd/cli/main.py +0 -0
- {kd_cli-2.6.5 → kd_cli-2.6.6}/kd/cli/namespaces/__init__.py +0 -0
- {kd_cli-2.6.5 → kd_cli-2.6.6}/kd/cli/namespaces/alias/__init__.py +0 -0
- {kd_cli-2.6.5 → kd_cli-2.6.6}/kd/cli/namespaces/article/__init__.py +0 -0
- {kd_cli-2.6.5 → kd_cli-2.6.6}/kd/cli/namespaces/init.py +0 -0
- {kd_cli-2.6.5 → kd_cli-2.6.6}/kd/cli/namespaces/issue/__init__.py +0 -0
- {kd_cli-2.6.5 → kd_cli-2.6.6}/kd/cli/namespaces/project/__init__.py +0 -0
- {kd_cli-2.6.5 → kd_cli-2.6.6}/kd/cli/namespaces/query/__init__.py +0 -0
- {kd_cli-2.6.5 → kd_cli-2.6.6}/kd/cli/namespaces/release_notes.py +0 -0
- {kd_cli-2.6.5 → kd_cli-2.6.6}/kd/cli/namespaces/search/__init__.py +0 -0
- {kd_cli-2.6.5 → kd_cli-2.6.6}/kd/cli/namespaces/system/__init__.py +0 -0
- {kd_cli-2.6.5 → kd_cli-2.6.6}/kd/cli/namespaces/tag/__init__.py +0 -0
- {kd_cli-2.6.5 → kd_cli-2.6.6}/kd/cli/namespaces/time/__init__.py +0 -0
- {kd_cli-2.6.5 → kd_cli-2.6.6}/kd/cli/namespaces/user/__init__.py +0 -0
- {kd_cli-2.6.5 → kd_cli-2.6.6}/kd/cli/tree.py +0 -0
- {kd_cli-2.6.5 → kd_cli-2.6.6}/kd/client.py +0 -0
- {kd_cli-2.6.5 → kd_cli-2.6.6}/kd/config.py +0 -0
- {kd_cli-2.6.5 → kd_cli-2.6.6}/kd/config_read.py +0 -0
- {kd_cli-2.6.5 → kd_cli-2.6.6}/kd/config_schema.py +0 -0
- {kd_cli-2.6.5 → kd_cli-2.6.6}/kd/connection.py +0 -0
- {kd_cli-2.6.5 → kd_cli-2.6.6}/kd/debug.py +0 -0
- {kd_cli-2.6.5 → kd_cli-2.6.6}/kd/exceptions.py +0 -0
- {kd_cli-2.6.5 → kd_cli-2.6.6}/kd/history.py +0 -0
- {kd_cli-2.6.5 → kd_cli-2.6.6}/kd/locking.py +0 -0
- {kd_cli-2.6.5 → kd_cli-2.6.6}/kd/models/__init__.py +0 -0
- {kd_cli-2.6.5 → kd_cli-2.6.6}/kd/models/article.py +0 -0
- {kd_cli-2.6.5 → kd_cli-2.6.6}/kd/models/assist.py +0 -0
- {kd_cli-2.6.5 → kd_cli-2.6.6}/kd/models/attachment.py +0 -0
- {kd_cli-2.6.5 → kd_cli-2.6.6}/kd/models/comment.py +0 -0
- {kd_cli-2.6.5 → kd_cli-2.6.6}/kd/models/common.py +0 -0
- {kd_cli-2.6.5 → kd_cli-2.6.6}/kd/models/issue.py +0 -0
- {kd_cli-2.6.5 → kd_cli-2.6.6}/kd/models/project.py +0 -0
- {kd_cli-2.6.5 → kd_cli-2.6.6}/kd/models/saved_query.py +0 -0
- {kd_cli-2.6.5 → kd_cli-2.6.6}/kd/models/search.py +0 -0
- {kd_cli-2.6.5 → kd_cli-2.6.6}/kd/models/tag.py +0 -0
- {kd_cli-2.6.5 → kd_cli-2.6.6}/kd/models/tag_entities.py +0 -0
- {kd_cli-2.6.5 → kd_cli-2.6.6}/kd/models/user.py +0 -0
- {kd_cli-2.6.5 → kd_cli-2.6.6}/kd/models/work_item.py +0 -0
- {kd_cli-2.6.5 → kd_cli-2.6.6}/kd/operating_guide.py +0 -0
- {kd_cli-2.6.5 → kd_cli-2.6.6}/kd/query_parser.py +0 -0
- {kd_cli-2.6.5 → kd_cli-2.6.6}/kd/release_notes.py +0 -0
- {kd_cli-2.6.5 → kd_cli-2.6.6}/kd/resources/__init__.py +0 -0
- {kd_cli-2.6.5 → kd_cli-2.6.6}/kd/resources/command-synonyms.yaml +0 -0
- {kd_cli-2.6.5 → kd_cli-2.6.6}/kd/resources/config-template.yaml +0 -0
- {kd_cli-2.6.5 → kd_cli-2.6.6}/kd/resources/error-messages.yaml +0 -0
- {kd_cli-2.6.5 → kd_cli-2.6.6}/kd/resources/primers/__init__.py +0 -0
- {kd_cli-2.6.5 → kd_cli-2.6.6}/kd/resources/primers/hitl.yaml +0 -0
- {kd_cli-2.6.5 → kd_cli-2.6.6}/kd/resources/sdlc-doctrine.md +0 -0
- {kd_cli-2.6.5 → kd_cli-2.6.6}/kd/sdlc.py +0 -0
- {kd_cli-2.6.5 → kd_cli-2.6.6}/kd_cli.egg-info/SOURCES.txt +0 -0
- {kd_cli-2.6.5 → kd_cli-2.6.6}/kd_cli.egg-info/dependency_links.txt +0 -0
- {kd_cli-2.6.5 → kd_cli-2.6.6}/kd_cli.egg-info/entry_points.txt +0 -0
- {kd_cli-2.6.5 → kd_cli-2.6.6}/kd_cli.egg-info/requires.txt +0 -0
- {kd_cli-2.6.5 → kd_cli-2.6.6}/kd_cli.egg-info/top_level.txt +0 -0
- {kd_cli-2.6.5 → kd_cli-2.6.6}/setup.cfg +0 -0
|
@@ -30,7 +30,14 @@ from kd.connection import ConnectionContext
|
|
|
30
30
|
from kd.exceptions import APIError, ConfigError, ValidationError
|
|
31
31
|
from kd.history import atomic_write_text
|
|
32
32
|
from kd.models.tag_entities import TagEntitiesResult, tag_entities_result_to_dict
|
|
33
|
-
from kd.primer import
|
|
33
|
+
from kd.primer import (
|
|
34
|
+
PRIMER_FILENAME,
|
|
35
|
+
PrimerArticle,
|
|
36
|
+
PrimerIssue,
|
|
37
|
+
PrimerTag,
|
|
38
|
+
load_primer,
|
|
39
|
+
require_workspace_dir,
|
|
40
|
+
)
|
|
34
41
|
from kd.query_parser import youtrack_query_literal
|
|
35
42
|
|
|
36
43
|
# Public surface: names imported by external callers (CLI, client) and the
|
|
@@ -90,6 +97,7 @@ __all__ = [
|
|
|
90
97
|
"HydratedQuery",
|
|
91
98
|
"HydratedQueryRun",
|
|
92
99
|
"IssueRefreshReport",
|
|
100
|
+
"IssueScopeRefreshReport",
|
|
93
101
|
"MilestoneRefreshReport",
|
|
94
102
|
"QueryNameRefreshReport",
|
|
95
103
|
"QueryRefreshReport",
|
|
@@ -285,11 +293,26 @@ class QueryRefreshReport:
|
|
|
285
293
|
failed: list[FailedQuery]
|
|
286
294
|
|
|
287
295
|
|
|
296
|
+
@dataclass
|
|
297
|
+
class IssueScopeRefreshReport:
|
|
298
|
+
"""Per-pinned-issue half of :class:`RefreshReport`.
|
|
299
|
+
|
|
300
|
+
Parallels :class:`ArticleRefreshReport`. Reuses :class:`HydratedIssue`
|
|
301
|
+
and :class:`FailedIssue` from the single-issue refresh path, with the
|
|
302
|
+
same per-id isolation contract.
|
|
303
|
+
"""
|
|
304
|
+
|
|
305
|
+
skipped_no_pinned: bool
|
|
306
|
+
hydrated: list[HydratedIssue]
|
|
307
|
+
failed: list[FailedIssue]
|
|
308
|
+
|
|
309
|
+
|
|
288
310
|
@dataclass
|
|
289
311
|
class RefreshReport:
|
|
290
312
|
"""Result of a single ``refresh_primer_scope`` invocation."""
|
|
291
313
|
|
|
292
314
|
articles: ArticleRefreshReport
|
|
315
|
+
issues: IssueScopeRefreshReport
|
|
293
316
|
queries: QueryRefreshReport
|
|
294
317
|
|
|
295
318
|
|
|
@@ -1881,6 +1904,57 @@ def _refresh_primer_articles(
|
|
|
1881
1904
|
return ArticleRefreshReport(skipped_no_pinned=False, hydrated=hydrated, failed=failed)
|
|
1882
1905
|
|
|
1883
1906
|
|
|
1907
|
+
def _refresh_primer_issues(
|
|
1908
|
+
client: Any,
|
|
1909
|
+
pinned: list[PrimerIssue],
|
|
1910
|
+
fetched_at: datetime,
|
|
1911
|
+
cache_root: Path,
|
|
1912
|
+
) -> IssueScopeRefreshReport:
|
|
1913
|
+
"""Hydrate each pinned issue into ``cache_root/issues/``.
|
|
1914
|
+
|
|
1915
|
+
Mirrors :func:`_refresh_primer_articles`. Per-id ``APIError`` (and
|
|
1916
|
+
subclasses) are isolated into ``failed``; ``AuthError`` and
|
|
1917
|
+
``ConfigError`` propagate as command-level failures and abort the
|
|
1918
|
+
run. Issues are fetched with ``detail="rich"`` so the cache payload
|
|
1919
|
+
matches the single-issue refresh shape (proposal-39 §Issue Cache
|
|
1920
|
+
Shape); ``include_comments`` stays off — the primer-scope contract
|
|
1921
|
+
is "what does the agent need to start work," not "full comment log."
|
|
1922
|
+
"""
|
|
1923
|
+
if not pinned:
|
|
1924
|
+
return IssueScopeRefreshReport(skipped_no_pinned=True, hydrated=[], failed=[])
|
|
1925
|
+
|
|
1926
|
+
hydrated: list[HydratedIssue] = []
|
|
1927
|
+
failed: list[FailedIssue] = []
|
|
1928
|
+
for entry in pinned:
|
|
1929
|
+
try:
|
|
1930
|
+
issue = client.issues.get(entry.id, detail="rich")
|
|
1931
|
+
payload = _issue_to_cache_payload(
|
|
1932
|
+
issue,
|
|
1933
|
+
fetched_at=fetched_at,
|
|
1934
|
+
include_comments=False,
|
|
1935
|
+
command=PRIMER_SCOPE_REFRESH_COMMAND,
|
|
1936
|
+
reason="primer pinned issue",
|
|
1937
|
+
)
|
|
1938
|
+
write_cached_issue(
|
|
1939
|
+
issue.id_readable,
|
|
1940
|
+
payload,
|
|
1941
|
+
fetched_at=fetched_at,
|
|
1942
|
+
cache_root=cache_root,
|
|
1943
|
+
)
|
|
1944
|
+
hydrated.append(
|
|
1945
|
+
HydratedIssue(
|
|
1946
|
+
id=issue.id_readable,
|
|
1947
|
+
summary=issue.summary,
|
|
1948
|
+
upstream_updated=_ms_to_iso(issue.updated),
|
|
1949
|
+
path=_report_path(cache_root / ISSUES_DIRNAME / f"{issue.id_readable}.yaml"),
|
|
1950
|
+
comments_count=None,
|
|
1951
|
+
)
|
|
1952
|
+
)
|
|
1953
|
+
except APIError as e:
|
|
1954
|
+
failed.append(FailedIssue(id=entry.id, reason=f"{type(e).__name__}: {e}"))
|
|
1955
|
+
return IssueScopeRefreshReport(skipped_no_pinned=False, hydrated=hydrated, failed=failed)
|
|
1956
|
+
|
|
1957
|
+
|
|
1884
1958
|
def _refresh_primer_query(
|
|
1885
1959
|
client: Any,
|
|
1886
1960
|
pinned_tags: list[PrimerTag],
|
|
@@ -1962,17 +2036,23 @@ def refresh_primer_scope(
|
|
|
1962
2036
|
root = connection_cache_dir(connection, workspace_dir.parent)
|
|
1963
2037
|
when = fetched_at or datetime.now(timezone.utc)
|
|
1964
2038
|
|
|
1965
|
-
if not primer.articles and not primer.tags:
|
|
2039
|
+
if not primer.articles and not primer.tags and not primer.issues:
|
|
1966
2040
|
raise ValidationError(
|
|
1967
|
-
"--primer-scope set but .kd/primer.yaml has no pinned articles
|
|
1968
|
-
"Pin a tag with: kd /primer/tag add TAG_NAME,
|
|
2041
|
+
"--primer-scope set but .kd/primer.yaml has no pinned articles, tags, "
|
|
2042
|
+
"or issues. Pin a tag with: kd /primer/tag add TAG_NAME, an issue "
|
|
2043
|
+
"with: kd /primer/issue add ISSUE-ID, or an article with: "
|
|
1969
2044
|
"kd /primer/article add ARTICLE-ID"
|
|
1970
2045
|
)
|
|
1971
2046
|
|
|
1972
2047
|
write_scope_manifest(connection, cache_root=root, fetched_at=when)
|
|
1973
2048
|
article_report = _refresh_primer_articles(client, primer.articles, when, root)
|
|
2049
|
+
issue_report = _refresh_primer_issues(client, primer.issues, when, root)
|
|
1974
2050
|
query_report = _refresh_primer_query(client, primer.tags, when, root)
|
|
1975
|
-
return RefreshReport(
|
|
2051
|
+
return RefreshReport(
|
|
2052
|
+
articles=article_report,
|
|
2053
|
+
issues=issue_report,
|
|
2054
|
+
queries=query_report,
|
|
2055
|
+
)
|
|
1976
2056
|
|
|
1977
2057
|
|
|
1978
2058
|
# ---------------------------------------------------------------------------
|
|
@@ -2027,6 +2107,7 @@ def _issue_to_cache_payload(
|
|
|
2027
2107
|
fetched_at: datetime,
|
|
2028
2108
|
include_comments: bool,
|
|
2029
2109
|
command: str,
|
|
2110
|
+
reason: str = "single-entity refresh",
|
|
2030
2111
|
) -> dict[str, Any]:
|
|
2031
2112
|
"""Build the proposal-39 §Issue Cache Shape dict from an SDK ``Issue``.
|
|
2032
2113
|
|
|
@@ -2037,6 +2118,10 @@ def _issue_to_cache_payload(
|
|
|
2037
2118
|
:func:`_project_key_from_id`. ``links`` and ``attachments`` are
|
|
2038
2119
|
deliberately not projected (deferred to S7+).
|
|
2039
2120
|
|
|
2121
|
+
``reason`` defaults to ``"single-entity refresh"`` for the existing
|
|
2122
|
+
single-issue refresh path; primer-scope refresh passes
|
|
2123
|
+
``"primer pinned issue"`` to record why the entry was hydrated.
|
|
2124
|
+
|
|
2040
2125
|
Not shared with ``kd/cli/namespaces/issue/__init__.py:_issue_to_detail``
|
|
2041
2126
|
— that helper truncates comments to the last 5 (display logic),
|
|
2042
2127
|
while cache projection must include all comments when
|
|
@@ -2053,7 +2138,7 @@ def _issue_to_cache_payload(
|
|
|
2053
2138
|
"fetched_at": _format_iso(fetched_at),
|
|
2054
2139
|
"source": {
|
|
2055
2140
|
"command": command,
|
|
2056
|
-
"reason":
|
|
2141
|
+
"reason": reason,
|
|
2057
2142
|
},
|
|
2058
2143
|
}
|
|
2059
2144
|
for cf in issue.custom_fields:
|
|
@@ -131,8 +131,11 @@ class CacheRefreshCommand(Command):
|
|
|
131
131
|
"hydration verb — the cache is rehydratable upstream-"
|
|
132
132
|
"mirror data and no command silently reads through it. "
|
|
133
133
|
"Selectors are mutually exclusive: --primer-scope "
|
|
134
|
-
"hydrates primer-pinned articles
|
|
135
|
-
"
|
|
134
|
+
"hydrates primer-pinned articles, primer-pinned issues "
|
|
135
|
+
"(via the same single-issue projection at "
|
|
136
|
+
".kd/cache/issues/<id>.yaml; comments not projected), "
|
|
137
|
+
"plus the primer-scope tag-entities query; --issue ID "
|
|
138
|
+
"hydrates a single issue "
|
|
136
139
|
"into .kd/cache/issues/<id>.yaml; --article ID hydrates "
|
|
137
140
|
"a single article; --query NAME runs a saved query and "
|
|
138
141
|
"writes the issue list to .kd/cache/queries/<slug>.yaml "
|