kd-cli 2.8.1__tar.gz → 2.9.1__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.8.1/kd_cli.egg-info → kd_cli-2.9.1}/PKG-INFO +1 -1
  2. {kd_cli-2.8.1 → kd_cli-2.9.1}/kd/cli/namespaces/tag/__init__.py +95 -24
  3. {kd_cli-2.8.1 → kd_cli-2.9.1}/kd/client.py +427 -27
  4. {kd_cli-2.8.1 → kd_cli-2.9.1}/kd/resources/CHANGELOG.md +48 -0
  5. {kd_cli-2.8.1 → kd_cli-2.9.1}/kd/resources/error-messages.yaml +61 -0
  6. {kd_cli-2.8.1 → kd_cli-2.9.1}/kd/resources/operating-guide.md +18 -0
  7. {kd_cli-2.8.1 → kd_cli-2.9.1/kd_cli.egg-info}/PKG-INFO +1 -1
  8. {kd_cli-2.8.1 → kd_cli-2.9.1}/pyproject.toml +1 -1
  9. {kd_cli-2.8.1 → kd_cli-2.9.1}/LICENSE +0 -0
  10. {kd_cli-2.8.1 → kd_cli-2.9.1}/MANIFEST.in +0 -0
  11. {kd_cli-2.8.1 → kd_cli-2.9.1}/README.md +0 -0
  12. {kd_cli-2.8.1 → kd_cli-2.9.1}/kd/__init__.py +0 -0
  13. {kd_cli-2.8.1 → kd_cli-2.9.1}/kd/api/__init__.py +0 -0
  14. {kd_cli-2.8.1 → kd_cli-2.9.1}/kd/api/articles.py +0 -0
  15. {kd_cli-2.8.1 → kd_cli-2.9.1}/kd/api/http.py +0 -0
  16. {kd_cli-2.8.1 → kd_cli-2.9.1}/kd/api/hub.py +0 -0
  17. {kd_cli-2.8.1 → kd_cli-2.9.1}/kd/api/saved_queries.py +0 -0
  18. {kd_cli-2.8.1 → kd_cli-2.9.1}/kd/api/tags.py +0 -0
  19. {kd_cli-2.8.1 → kd_cli-2.9.1}/kd/api/youtrack.py +0 -0
  20. {kd_cli-2.8.1 → kd_cli-2.9.1}/kd/article_marker.py +0 -0
  21. {kd_cli-2.8.1 → kd_cli-2.9.1}/kd/auth.py +0 -0
  22. {kd_cli-2.8.1 → kd_cli-2.9.1}/kd/cache.py +0 -0
  23. {kd_cli-2.8.1 → kd_cli-2.9.1}/kd/cli/__init__.py +0 -0
  24. {kd_cli-2.8.1 → kd_cli-2.9.1}/kd/cli/core/__init__.py +0 -0
  25. {kd_cli-2.8.1 → kd_cli-2.9.1}/kd/cli/core/command_base.py +0 -0
  26. {kd_cli-2.8.1 → kd_cli-2.9.1}/kd/cli/core/command_parser.py +0 -0
  27. {kd_cli-2.8.1 → kd_cli-2.9.1}/kd/cli/core/confirm.py +0 -0
  28. {kd_cli-2.8.1 → kd_cli-2.9.1}/kd/cli/core/namespaces.py +0 -0
  29. {kd_cli-2.8.1 → kd_cli-2.9.1}/kd/cli/core/option_registry.py +0 -0
  30. {kd_cli-2.8.1 → kd_cli-2.9.1}/kd/cli/input.py +0 -0
  31. {kd_cli-2.8.1 → kd_cli-2.9.1}/kd/cli/main.py +0 -0
  32. {kd_cli-2.8.1 → kd_cli-2.9.1}/kd/cli/namespaces/__init__.py +0 -0
  33. {kd_cli-2.8.1 → kd_cli-2.9.1}/kd/cli/namespaces/alias/__init__.py +0 -0
  34. {kd_cli-2.8.1 → kd_cli-2.9.1}/kd/cli/namespaces/article/__init__.py +0 -0
  35. {kd_cli-2.8.1 → kd_cli-2.9.1}/kd/cli/namespaces/cache/__init__.py +0 -0
  36. {kd_cli-2.8.1 → kd_cli-2.9.1}/kd/cli/namespaces/init.py +0 -0
  37. {kd_cli-2.8.1 → kd_cli-2.9.1}/kd/cli/namespaces/issue/__init__.py +0 -0
  38. {kd_cli-2.8.1 → kd_cli-2.9.1}/kd/cli/namespaces/primer/__init__.py +0 -0
  39. {kd_cli-2.8.1 → kd_cli-2.9.1}/kd/cli/namespaces/project/__init__.py +0 -0
  40. {kd_cli-2.8.1 → kd_cli-2.9.1}/kd/cli/namespaces/query/__init__.py +0 -0
  41. {kd_cli-2.8.1 → kd_cli-2.9.1}/kd/cli/namespaces/release_notes.py +0 -0
  42. {kd_cli-2.8.1 → kd_cli-2.9.1}/kd/cli/namespaces/search/__init__.py +0 -0
  43. {kd_cli-2.8.1 → kd_cli-2.9.1}/kd/cli/namespaces/system/__init__.py +0 -0
  44. {kd_cli-2.8.1 → kd_cli-2.9.1}/kd/cli/namespaces/time/__init__.py +0 -0
  45. {kd_cli-2.8.1 → kd_cli-2.9.1}/kd/cli/namespaces/user/__init__.py +0 -0
  46. {kd_cli-2.8.1 → kd_cli-2.9.1}/kd/cli/output.py +0 -0
  47. {kd_cli-2.8.1 → kd_cli-2.9.1}/kd/cli/tree.py +0 -0
  48. {kd_cli-2.8.1 → kd_cli-2.9.1}/kd/config.py +0 -0
  49. {kd_cli-2.8.1 → kd_cli-2.9.1}/kd/config_read.py +0 -0
  50. {kd_cli-2.8.1 → kd_cli-2.9.1}/kd/config_schema.py +0 -0
  51. {kd_cli-2.8.1 → kd_cli-2.9.1}/kd/connection.py +0 -0
  52. {kd_cli-2.8.1 → kd_cli-2.9.1}/kd/debug.py +0 -0
  53. {kd_cli-2.8.1 → kd_cli-2.9.1}/kd/exceptions.py +0 -0
  54. {kd_cli-2.8.1 → kd_cli-2.9.1}/kd/history.py +0 -0
  55. {kd_cli-2.8.1 → kd_cli-2.9.1}/kd/locking.py +0 -0
  56. {kd_cli-2.8.1 → kd_cli-2.9.1}/kd/models/__init__.py +0 -0
  57. {kd_cli-2.8.1 → kd_cli-2.9.1}/kd/models/article.py +0 -0
  58. {kd_cli-2.8.1 → kd_cli-2.9.1}/kd/models/assist.py +0 -0
  59. {kd_cli-2.8.1 → kd_cli-2.9.1}/kd/models/attachment.py +0 -0
  60. {kd_cli-2.8.1 → kd_cli-2.9.1}/kd/models/comment.py +0 -0
  61. {kd_cli-2.8.1 → kd_cli-2.9.1}/kd/models/common.py +0 -0
  62. {kd_cli-2.8.1 → kd_cli-2.9.1}/kd/models/issue.py +0 -0
  63. {kd_cli-2.8.1 → kd_cli-2.9.1}/kd/models/project.py +0 -0
  64. {kd_cli-2.8.1 → kd_cli-2.9.1}/kd/models/saved_query.py +0 -0
  65. {kd_cli-2.8.1 → kd_cli-2.9.1}/kd/models/search.py +0 -0
  66. {kd_cli-2.8.1 → kd_cli-2.9.1}/kd/models/tag.py +0 -0
  67. {kd_cli-2.8.1 → kd_cli-2.9.1}/kd/models/tag_entities.py +0 -0
  68. {kd_cli-2.8.1 → kd_cli-2.9.1}/kd/models/user.py +0 -0
  69. {kd_cli-2.8.1 → kd_cli-2.9.1}/kd/models/work_item.py +0 -0
  70. {kd_cli-2.8.1 → kd_cli-2.9.1}/kd/operating_guide.py +0 -0
  71. {kd_cli-2.8.1 → kd_cli-2.9.1}/kd/primer.py +0 -0
  72. {kd_cli-2.8.1 → kd_cli-2.9.1}/kd/query_parser.py +0 -0
  73. {kd_cli-2.8.1 → kd_cli-2.9.1}/kd/release_notes.py +0 -0
  74. {kd_cli-2.8.1 → kd_cli-2.9.1}/kd/resources/__init__.py +0 -0
  75. {kd_cli-2.8.1 → kd_cli-2.9.1}/kd/resources/command-synonyms.yaml +0 -0
  76. {kd_cli-2.8.1 → kd_cli-2.9.1}/kd/resources/config-template.yaml +0 -0
  77. {kd_cli-2.8.1 → kd_cli-2.9.1}/kd/resources/primers/__init__.py +0 -0
  78. {kd_cli-2.8.1 → kd_cli-2.9.1}/kd/resources/primers/hitl.yaml +0 -0
  79. {kd_cli-2.8.1 → kd_cli-2.9.1}/kd/resources/sdlc-doctrine.md +0 -0
  80. {kd_cli-2.8.1 → kd_cli-2.9.1}/kd/sdlc.py +0 -0
  81. {kd_cli-2.8.1 → kd_cli-2.9.1}/kd_cli.egg-info/SOURCES.txt +0 -0
  82. {kd_cli-2.8.1 → kd_cli-2.9.1}/kd_cli.egg-info/dependency_links.txt +0 -0
  83. {kd_cli-2.8.1 → kd_cli-2.9.1}/kd_cli.egg-info/entry_points.txt +0 -0
  84. {kd_cli-2.8.1 → kd_cli-2.9.1}/kd_cli.egg-info/requires.txt +0 -0
  85. {kd_cli-2.8.1 → kd_cli-2.9.1}/kd_cli.egg-info/top_level.txt +0 -0
  86. {kd_cli-2.8.1 → kd_cli-2.9.1}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: kd-cli
3
- Version: 2.8.1
3
+ Version: 2.9.1
4
4
  Summary: Knowledge Dispatcher for issue trackers and knowledge bases
5
5
  Author: Sergiy Gladkyy
6
6
  License-Expression: MIT
@@ -138,8 +138,18 @@ class TagShowCommand(Command):
138
138
  "show",
139
139
  "Show tag details",
140
140
  requires_client=True,
141
- usage="kd /tag show TAG_NAME",
142
- examples=["kd /tag show sprint-47"],
141
+ usage="kd /tag show TAG_NAME_OR_ID",
142
+ long_description=(
143
+ "Accepts either a display name or the tag-id form "
144
+ "(NN-NN); the id form skips name resolution and is the "
145
+ "disambiguator when two visible tags share a name. A "
146
+ "name that resolves to multiple visible tags refuses "
147
+ "with a candidate list — pass the tag id to pick."
148
+ ),
149
+ examples=[
150
+ "kd /tag show sprint-47",
151
+ "kd /tag show 10-178",
152
+ ],
143
153
  see_also=["/tag list", "/tag issues"],
144
154
  )
145
155
 
@@ -162,13 +172,16 @@ class TagCreateCommand(Command):
162
172
  requires_client=True,
163
173
  usage="kd /tag create TAG_NAME",
164
174
  long_description=(
165
- "Idempotent: if a tag with the requested name is already "
166
- "visible, the command returns ``action: noop`` and does "
167
- "not POST. Otherwise the tag is created with "
175
+ "Idempotent: if exactly one tag with the requested name "
176
+ "is already visible, the command returns ``action: noop`` "
177
+ "and does not POST. Otherwise the tag is created with "
168
178
  "``action: created``. Returns a parseable single-line ack: "
169
179
  "the last stdout line is ``ok: <NAME> created`` or "
170
180
  "``ok: <NAME> noop``. Name resolution is case-insensitive "
171
- "(same resolver /tag show uses). Pass --quiet to suppress "
181
+ "(same resolver /tag show uses). When the name collides "
182
+ "with multiple visible tags, create refuses with a "
183
+ "candidate list rather than picking one — inspect them "
184
+ "with ``kd /tag show <id>``. Pass --quiet to suppress "
172
185
  "the detail block above the ack."
173
186
  ),
174
187
  examples=['kd /tag create "sprint-47"'],
@@ -206,17 +219,25 @@ class TagRenameCommand(Command):
206
219
  "rename",
207
220
  "Rename a tag",
208
221
  requires_client=True,
209
- usage="kd /tag rename TAG_NAME NEW_NAME",
222
+ usage="kd /tag rename TAG_NAME_OR_ID NEW_NAME",
210
223
  long_description=(
211
224
  "Returns a parseable single-line ack: the last stdout line "
212
225
  "is ``ok: <NEW_NAME> renamed old_name=<TAG_NAME>``. The "
213
226
  "tail-line ID is the **new name**, since scripts that "
214
227
  "follow the rename refer to the tag by its new identity. "
215
228
  "The previous name is preserved in ``old_name=`` so "
216
- "audit/log consumers can correlate. Pass --quiet to "
217
- "suppress the detail block above the ack."
229
+ "audit/log consumers can correlate. The first argument "
230
+ "accepts either a display name or the tag-id form "
231
+ "(NN-NN); the id form skips name resolution and is the "
232
+ "disambiguator when two visible tags share a name. A "
233
+ "name that resolves to multiple visible tags refuses "
234
+ "with a candidate list — pass the tag id to pick. Pass "
235
+ "--quiet to suppress the detail block above the ack."
218
236
  ),
219
- examples=['kd /tag rename sprint-47 "sprint-48"'],
237
+ examples=[
238
+ 'kd /tag rename sprint-47 "sprint-48"',
239
+ 'kd /tag rename 10-178 "lifecycle:final"',
240
+ ],
220
241
  see_also=["/tag show"],
221
242
  )
222
243
  self.display_hint = "mutation"
@@ -244,15 +265,23 @@ class TagDeleteCommand(Command):
244
265
  "delete",
245
266
  "Delete a tag",
246
267
  requires_client=True,
247
- usage="kd /tag delete TAG_NAME",
268
+ usage="kd /tag delete TAG_NAME_OR_ID",
248
269
  long_description=(
249
270
  "Returns a parseable single-line ack: the last stdout line "
250
271
  "is ``ok: <NAME> deleted``. --confirm is required and is "
251
272
  "**not** softened by --quiet — the safety gate fires "
252
- "before any envelope is rendered. Pass --quiet to suppress "
253
- "the detail block above the ack."
273
+ "before any envelope is rendered. The argument accepts "
274
+ "either a display name or the tag-id form (NN-NN); the "
275
+ "id form skips name resolution and is the disambiguator "
276
+ "when two visible tags share a name. A name that "
277
+ "resolves to multiple visible tags refuses with a "
278
+ "candidate list — pass the tag id to pick. Pass "
279
+ "--quiet to suppress the detail block above the ack."
254
280
  ),
255
- examples=["kd /tag delete sprint-47 --confirm"],
281
+ examples=[
282
+ "kd /tag delete sprint-47 --confirm",
283
+ "kd /tag delete 10-178 --confirm",
284
+ ],
256
285
  see_also=["/tag list", "/tag create"],
257
286
  )
258
287
  self.display_hint = "mutation"
@@ -298,13 +327,20 @@ class TagAddCommand(Command):
298
327
  "add",
299
328
  "Tag an issue or article",
300
329
  requires_client=True,
301
- usage="kd /tag add ENTITY_ID TAG_NAME",
330
+ usage="kd /tag add ENTITY_ID TAG_NAME_OR_ID",
302
331
  long_description=(
303
- "Entity type is auto-detected from the ID format: FB-123 = issue, FB-A-1 = article."
332
+ "Entity type is auto-detected from the ID format: FB-123 = "
333
+ "issue, FB-A-1 = article. The tag argument accepts either a "
334
+ "display name or the tag-id form (NN-NN); the id form skips "
335
+ "name resolution and is the disambiguator when two tags share "
336
+ "a name. Adding a name already attached to the entity is an "
337
+ "idempotent re-tag; adding a name that has multiple visible "
338
+ "tags refuses with a candidate list — pass the tag id to pick."
304
339
  ),
305
340
  examples=[
306
341
  "kd /tag add FB-123 sprint-47",
307
342
  "kd /tag add FB-A-1 reviewed",
343
+ "kd /tag add FB-A-1 10-178",
308
344
  ],
309
345
  see_also=["/tag remove", "/tag issues"],
310
346
  )
@@ -327,10 +363,15 @@ class TagRemoveCommand(Command):
327
363
  "remove",
328
364
  "Untag an issue or article",
329
365
  requires_client=True,
330
- usage="kd /tag remove ENTITY_ID TAG_NAME",
366
+ usage="kd /tag remove ENTITY_ID TAG_NAME_OR_ID",
331
367
  long_description=(
332
368
  "Entity type is auto-detected from the ID format: FB-123 = "
333
- "issue, FB-A-1 = article. "
369
+ "issue, FB-A-1 = article. The tag argument is resolved "
370
+ "against the tags attached to the entity — names that are "
371
+ "not attached refuse loudly even when a same-name tag "
372
+ "exists globally. Pass the tag-id form (NN-NN) to skip name "
373
+ "resolution; this is the disambiguator when multiple "
374
+ "same-name tags are attached. "
334
375
  "Returns a parseable single-line ack: the last stdout line "
335
376
  "is ``ok: <ENTITY_ID> removed tag=<TAG_NAME>``. The "
336
377
  "entity ID is the primary tail-line ID (``$2``); the "
@@ -340,6 +381,7 @@ class TagRemoveCommand(Command):
340
381
  examples=[
341
382
  "kd /tag remove FB-123 sprint-47",
342
383
  "kd /tag remove FB-A-1 reviewed",
384
+ "kd /tag remove FB-A-1 10-178",
343
385
  ],
344
386
  see_also=["/tag add", "/tag issues"],
345
387
  )
@@ -363,16 +405,26 @@ class TagIssuesCommand(Command):
363
405
  "issues",
364
406
  "List issues with a tag",
365
407
  requires_client=True,
366
- usage="kd /tag issues TAG_NAME [--tag TAG ...] [--project P] [--limit N]",
408
+ usage="kd /tag issues TAG_NAME_OR_ID [--tag TAG ...] [--project P] [--limit N]",
367
409
  long_description=(
368
- "Lists issues tagged with the given tag. Single-tag uses the "
410
+ "Lists issues tagged with the given tag. Single-tag uses the "
369
411
  "fast server-side tag-issues endpoint; multi-tag composes a "
370
412
  "YouTrack query and uses /issues?query=... with AND semantics. "
371
413
  "Use --project to scope results to a single project (forces "
372
- "the composed-query path)."
414
+ "the composed-query path). The tag argument accepts either a "
415
+ "display name or the tag-id form (NN-NN) on the fast path "
416
+ "(single tag, no --project); the id form skips name "
417
+ "resolution and is the disambiguator when two visible tags "
418
+ "share a name. On the composed-query path (--project set or "
419
+ "multiple --tag arguments), the id form is rejected because "
420
+ "YouTrack's ``tag:`` predicate is name-keyed — use the name "
421
+ "form (which refuses on duplicate-name collision) or the "
422
+ "fast path. A name that resolves to multiple visible tags "
423
+ "refuses with a candidate list before the query is composed."
373
424
  ),
374
425
  examples=[
375
426
  "kd /tag issues sprint-47",
427
+ "kd /tag issues 10-178",
376
428
  "kd /tag issues blocked --limit 100",
377
429
  "kd /tag issues kind:note --tag component:authservice",
378
430
  "kd /tag issues kind:note --tag component:authservice --project KDCLI",
@@ -414,16 +466,22 @@ class TagArticlesCommand(Command):
414
466
  "articles",
415
467
  "List articles with a tag",
416
468
  requires_client=True,
417
- usage="kd /tag articles TAG_NAME [--tag TAG ...] [--project P] [--limit N]",
469
+ usage="kd /tag articles TAG_NAME_OR_ID [--tag TAG ...] [--project P] [--limit N]",
418
470
  long_description=(
419
471
  "Lists articles tagged with the given tag. Uses client-side "
420
472
  "filtering because the YouTrack API has no server-side "
421
473
  "article-by-tag endpoint. Use --project to scope the scan "
422
474
  "to a single project. Repeat --tag to require multiple tags "
423
- "(AND semantics)."
475
+ "(AND semantics). Every tag argument accepts either a "
476
+ "display name or the tag-id form (NN-NN); the id form "
477
+ "filters precisely by id and is the disambiguator when "
478
+ "two visible tags share a name. A name that resolves to "
479
+ "multiple visible tags refuses with a candidate list — "
480
+ "pass the tag id to pick."
424
481
  ),
425
482
  examples=[
426
483
  "kd /tag articles reviewed",
484
+ "kd /tag articles 10-178",
427
485
  'kd /tag articles "type:guide" --project FB',
428
486
  "kd /tag articles type:case --tag component:authservice -p CMCSA",
429
487
  "kd /tag articles blocked --limit 100",
@@ -484,10 +542,23 @@ class TagEntitiesCommand(Command):
484
542
  "--limit-issues and --limit-articles override per kind. "
485
543
  "--primer-scope substitutes the primer's pinned tags as an "
486
544
  "OR scope (match=any); mutually exclusive with the positional "
487
- "TAG and --tag arguments."
545
+ "TAG and --tag arguments. "
546
+ "The tag argument accepts either a display name or the "
547
+ "tag-id form (NN-NN); the id form skips name resolution "
548
+ "and is the disambiguator when two visible tags share a "
549
+ "name. Id-form scope: allowed when the issue half routes "
550
+ "through the fast path (single tag, no --project) or when "
551
+ "only --kind article is requested (article filtering is "
552
+ "client-side by id). With --project or multiple tags AND "
553
+ "the issue kind included, id-form is rejected because "
554
+ "YouTrack's ``tag:`` predicate is name-keyed. Names are "
555
+ "pre-validated before the issue/article fanout — a "
556
+ "duplicate-name refusal is the whole-command outcome, not "
557
+ "a partial-success row."
488
558
  ),
489
559
  examples=[
490
560
  "kd /tag entities kind:note",
561
+ "kd /tag entities 10-178",
491
562
  "kd /tag entities kind:note --tag component:authservice --project KDCLI",
492
563
  "kd /tag entities kind:note --kind article --limit-articles 5",
493
564
  "kd /tag entities kind:note --limit 20 -o json",