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.
Files changed (86) hide show
  1. {kd_cli-2.6.5/kd_cli.egg-info → kd_cli-2.6.6}/PKG-INFO +1 -1
  2. {kd_cli-2.6.5 → kd_cli-2.6.6}/kd/cache.py +91 -6
  3. {kd_cli-2.6.5 → kd_cli-2.6.6}/kd/cli/namespaces/cache/__init__.py +5 -2
  4. {kd_cli-2.6.5 → kd_cli-2.6.6}/kd/cli/namespaces/primer/__init__.py +397 -13
  5. {kd_cli-2.6.5 → kd_cli-2.6.6}/kd/cli/output.py +34 -3
  6. {kd_cli-2.6.5 → kd_cli-2.6.6}/kd/primer.py +176 -2
  7. {kd_cli-2.6.5 → kd_cli-2.6.6}/kd/resources/CHANGELOG.md +29 -0
  8. {kd_cli-2.6.5 → kd_cli-2.6.6}/kd/resources/operating-guide.md +33 -3
  9. {kd_cli-2.6.5 → kd_cli-2.6.6/kd_cli.egg-info}/PKG-INFO +1 -1
  10. {kd_cli-2.6.5 → kd_cli-2.6.6}/pyproject.toml +1 -1
  11. {kd_cli-2.6.5 → kd_cli-2.6.6}/LICENSE +0 -0
  12. {kd_cli-2.6.5 → kd_cli-2.6.6}/MANIFEST.in +0 -0
  13. {kd_cli-2.6.5 → kd_cli-2.6.6}/README.md +0 -0
  14. {kd_cli-2.6.5 → kd_cli-2.6.6}/kd/__init__.py +0 -0
  15. {kd_cli-2.6.5 → kd_cli-2.6.6}/kd/api/__init__.py +0 -0
  16. {kd_cli-2.6.5 → kd_cli-2.6.6}/kd/api/articles.py +0 -0
  17. {kd_cli-2.6.5 → kd_cli-2.6.6}/kd/api/http.py +0 -0
  18. {kd_cli-2.6.5 → kd_cli-2.6.6}/kd/api/hub.py +0 -0
  19. {kd_cli-2.6.5 → kd_cli-2.6.6}/kd/api/saved_queries.py +0 -0
  20. {kd_cli-2.6.5 → kd_cli-2.6.6}/kd/api/tags.py +0 -0
  21. {kd_cli-2.6.5 → kd_cli-2.6.6}/kd/api/youtrack.py +0 -0
  22. {kd_cli-2.6.5 → kd_cli-2.6.6}/kd/article_marker.py +0 -0
  23. {kd_cli-2.6.5 → kd_cli-2.6.6}/kd/auth.py +0 -0
  24. {kd_cli-2.6.5 → kd_cli-2.6.6}/kd/cli/__init__.py +0 -0
  25. {kd_cli-2.6.5 → kd_cli-2.6.6}/kd/cli/core/__init__.py +0 -0
  26. {kd_cli-2.6.5 → kd_cli-2.6.6}/kd/cli/core/command_base.py +0 -0
  27. {kd_cli-2.6.5 → kd_cli-2.6.6}/kd/cli/core/command_parser.py +0 -0
  28. {kd_cli-2.6.5 → kd_cli-2.6.6}/kd/cli/core/confirm.py +0 -0
  29. {kd_cli-2.6.5 → kd_cli-2.6.6}/kd/cli/core/namespaces.py +0 -0
  30. {kd_cli-2.6.5 → kd_cli-2.6.6}/kd/cli/core/option_registry.py +0 -0
  31. {kd_cli-2.6.5 → kd_cli-2.6.6}/kd/cli/input.py +0 -0
  32. {kd_cli-2.6.5 → kd_cli-2.6.6}/kd/cli/main.py +0 -0
  33. {kd_cli-2.6.5 → kd_cli-2.6.6}/kd/cli/namespaces/__init__.py +0 -0
  34. {kd_cli-2.6.5 → kd_cli-2.6.6}/kd/cli/namespaces/alias/__init__.py +0 -0
  35. {kd_cli-2.6.5 → kd_cli-2.6.6}/kd/cli/namespaces/article/__init__.py +0 -0
  36. {kd_cli-2.6.5 → kd_cli-2.6.6}/kd/cli/namespaces/init.py +0 -0
  37. {kd_cli-2.6.5 → kd_cli-2.6.6}/kd/cli/namespaces/issue/__init__.py +0 -0
  38. {kd_cli-2.6.5 → kd_cli-2.6.6}/kd/cli/namespaces/project/__init__.py +0 -0
  39. {kd_cli-2.6.5 → kd_cli-2.6.6}/kd/cli/namespaces/query/__init__.py +0 -0
  40. {kd_cli-2.6.5 → kd_cli-2.6.6}/kd/cli/namespaces/release_notes.py +0 -0
  41. {kd_cli-2.6.5 → kd_cli-2.6.6}/kd/cli/namespaces/search/__init__.py +0 -0
  42. {kd_cli-2.6.5 → kd_cli-2.6.6}/kd/cli/namespaces/system/__init__.py +0 -0
  43. {kd_cli-2.6.5 → kd_cli-2.6.6}/kd/cli/namespaces/tag/__init__.py +0 -0
  44. {kd_cli-2.6.5 → kd_cli-2.6.6}/kd/cli/namespaces/time/__init__.py +0 -0
  45. {kd_cli-2.6.5 → kd_cli-2.6.6}/kd/cli/namespaces/user/__init__.py +0 -0
  46. {kd_cli-2.6.5 → kd_cli-2.6.6}/kd/cli/tree.py +0 -0
  47. {kd_cli-2.6.5 → kd_cli-2.6.6}/kd/client.py +0 -0
  48. {kd_cli-2.6.5 → kd_cli-2.6.6}/kd/config.py +0 -0
  49. {kd_cli-2.6.5 → kd_cli-2.6.6}/kd/config_read.py +0 -0
  50. {kd_cli-2.6.5 → kd_cli-2.6.6}/kd/config_schema.py +0 -0
  51. {kd_cli-2.6.5 → kd_cli-2.6.6}/kd/connection.py +0 -0
  52. {kd_cli-2.6.5 → kd_cli-2.6.6}/kd/debug.py +0 -0
  53. {kd_cli-2.6.5 → kd_cli-2.6.6}/kd/exceptions.py +0 -0
  54. {kd_cli-2.6.5 → kd_cli-2.6.6}/kd/history.py +0 -0
  55. {kd_cli-2.6.5 → kd_cli-2.6.6}/kd/locking.py +0 -0
  56. {kd_cli-2.6.5 → kd_cli-2.6.6}/kd/models/__init__.py +0 -0
  57. {kd_cli-2.6.5 → kd_cli-2.6.6}/kd/models/article.py +0 -0
  58. {kd_cli-2.6.5 → kd_cli-2.6.6}/kd/models/assist.py +0 -0
  59. {kd_cli-2.6.5 → kd_cli-2.6.6}/kd/models/attachment.py +0 -0
  60. {kd_cli-2.6.5 → kd_cli-2.6.6}/kd/models/comment.py +0 -0
  61. {kd_cli-2.6.5 → kd_cli-2.6.6}/kd/models/common.py +0 -0
  62. {kd_cli-2.6.5 → kd_cli-2.6.6}/kd/models/issue.py +0 -0
  63. {kd_cli-2.6.5 → kd_cli-2.6.6}/kd/models/project.py +0 -0
  64. {kd_cli-2.6.5 → kd_cli-2.6.6}/kd/models/saved_query.py +0 -0
  65. {kd_cli-2.6.5 → kd_cli-2.6.6}/kd/models/search.py +0 -0
  66. {kd_cli-2.6.5 → kd_cli-2.6.6}/kd/models/tag.py +0 -0
  67. {kd_cli-2.6.5 → kd_cli-2.6.6}/kd/models/tag_entities.py +0 -0
  68. {kd_cli-2.6.5 → kd_cli-2.6.6}/kd/models/user.py +0 -0
  69. {kd_cli-2.6.5 → kd_cli-2.6.6}/kd/models/work_item.py +0 -0
  70. {kd_cli-2.6.5 → kd_cli-2.6.6}/kd/operating_guide.py +0 -0
  71. {kd_cli-2.6.5 → kd_cli-2.6.6}/kd/query_parser.py +0 -0
  72. {kd_cli-2.6.5 → kd_cli-2.6.6}/kd/release_notes.py +0 -0
  73. {kd_cli-2.6.5 → kd_cli-2.6.6}/kd/resources/__init__.py +0 -0
  74. {kd_cli-2.6.5 → kd_cli-2.6.6}/kd/resources/command-synonyms.yaml +0 -0
  75. {kd_cli-2.6.5 → kd_cli-2.6.6}/kd/resources/config-template.yaml +0 -0
  76. {kd_cli-2.6.5 → kd_cli-2.6.6}/kd/resources/error-messages.yaml +0 -0
  77. {kd_cli-2.6.5 → kd_cli-2.6.6}/kd/resources/primers/__init__.py +0 -0
  78. {kd_cli-2.6.5 → kd_cli-2.6.6}/kd/resources/primers/hitl.yaml +0 -0
  79. {kd_cli-2.6.5 → kd_cli-2.6.6}/kd/resources/sdlc-doctrine.md +0 -0
  80. {kd_cli-2.6.5 → kd_cli-2.6.6}/kd/sdlc.py +0 -0
  81. {kd_cli-2.6.5 → kd_cli-2.6.6}/kd_cli.egg-info/SOURCES.txt +0 -0
  82. {kd_cli-2.6.5 → kd_cli-2.6.6}/kd_cli.egg-info/dependency_links.txt +0 -0
  83. {kd_cli-2.6.5 → kd_cli-2.6.6}/kd_cli.egg-info/entry_points.txt +0 -0
  84. {kd_cli-2.6.5 → kd_cli-2.6.6}/kd_cli.egg-info/requires.txt +0 -0
  85. {kd_cli-2.6.5 → kd_cli-2.6.6}/kd_cli.egg-info/top_level.txt +0 -0
  86. {kd_cli-2.6.5 → kd_cli-2.6.6}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: kd-cli
3
- Version: 2.6.5
3
+ Version: 2.6.6
4
4
  Summary: Knowledge Dispatcher for issue trackers and knowledge bases
5
5
  Author: Sergiy Gladkyy
6
6
  License-Expression: MIT
@@ -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 PRIMER_FILENAME, PrimerArticle, PrimerTag, load_primer, require_workspace_dir
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 or tags. "
1968
- "Pin a tag with: kd /primer/tag add TAG_NAME, or pin an article with: "
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(articles=article_report, queries=query_report)
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": "single-entity refresh",
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 plus the primer-scope "
135
- "tag-entities query; --issue ID hydrates a single issue "
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 "