direct-cli 0.2.7__tar.gz → 0.2.8__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 (127) hide show
  1. {direct_cli-0.2.7 → direct_cli-0.2.8}/AGENTS.md +1 -0
  2. {direct_cli-0.2.7 → direct_cli-0.2.8}/PKG-INFO +34 -14
  3. {direct_cli-0.2.7 → direct_cli-0.2.8}/README.md +33 -13
  4. {direct_cli-0.2.7 → direct_cli-0.2.8}/direct_cli/__init__.py +4 -3
  5. direct_cli-0.2.8/direct_cli/_deprecated.py +11 -0
  6. {direct_cli-0.2.7 → direct_cli-0.2.8}/direct_cli/cli.py +2 -0
  7. {direct_cli-0.2.7 → direct_cli-0.2.8}/direct_cli/commands/bidmodifiers.py +0 -61
  8. {direct_cli-0.2.7 → direct_cli-0.2.8}/direct_cli/wsdl_coverage.py +1 -4
  9. {direct_cli-0.2.7 → direct_cli-0.2.8}/direct_cli.egg-info/PKG-INFO +34 -14
  10. {direct_cli-0.2.7 → direct_cli-0.2.8}/direct_cli.egg-info/SOURCES.txt +1 -0
  11. direct_cli-0.2.8/direct_cli.egg-info/entry_points.txt +3 -0
  12. {direct_cli-0.2.7 → direct_cli-0.2.8}/pyproject.toml +2 -2
  13. {direct_cli-0.2.7 → direct_cli-0.2.8}/tests/test_api_coverage.py +1 -2
  14. {direct_cli-0.2.7 → direct_cli-0.2.8}/tests/test_cli.py +26 -1
  15. {direct_cli-0.2.7 → direct_cli-0.2.8}/tests/test_dry_run.py +1 -24
  16. {direct_cli-0.2.7 → direct_cli-0.2.8}/tests/test_integration_write.py +0 -67
  17. direct_cli-0.2.7/direct_cli.egg-info/entry_points.txt +0 -3
  18. {direct_cli-0.2.7 → direct_cli-0.2.8}/.env.example +0 -0
  19. {direct_cli-0.2.7 → direct_cli-0.2.8}/.github/copilot-instructions.md +0 -0
  20. {direct_cli-0.2.7 → direct_cli-0.2.8}/.github/workflows/api-coverage.yml +0 -0
  21. {direct_cli-0.2.7 → direct_cli-0.2.8}/.github/workflows/claude-code-review.yml +0 -0
  22. {direct_cli-0.2.7 → direct_cli-0.2.8}/.github/workflows/claude.yml +0 -0
  23. {direct_cli-0.2.7 → direct_cli-0.2.8}/.gitignore +0 -0
  24. {direct_cli-0.2.7 → direct_cli-0.2.8}/CLAUDE.md +0 -0
  25. {direct_cli-0.2.7 → direct_cli-0.2.8}/MANIFEST.in +0 -0
  26. {direct_cli-0.2.7 → direct_cli-0.2.8}/direct_cli/api.py +0 -0
  27. {direct_cli-0.2.7 → direct_cli-0.2.8}/direct_cli/auth.py +0 -0
  28. {direct_cli-0.2.7 → direct_cli-0.2.8}/direct_cli/commands/__init__.py +0 -0
  29. {direct_cli-0.2.7 → direct_cli-0.2.8}/direct_cli/commands/adextensions.py +0 -0
  30. {direct_cli-0.2.7 → direct_cli-0.2.8}/direct_cli/commands/adgroups.py +0 -0
  31. {direct_cli-0.2.7 → direct_cli-0.2.8}/direct_cli/commands/adimages.py +0 -0
  32. {direct_cli-0.2.7 → direct_cli-0.2.8}/direct_cli/commands/ads.py +0 -0
  33. {direct_cli-0.2.7 → direct_cli-0.2.8}/direct_cli/commands/advideos.py +0 -0
  34. {direct_cli-0.2.7 → direct_cli-0.2.8}/direct_cli/commands/agencyclients.py +0 -0
  35. {direct_cli-0.2.7 → direct_cli-0.2.8}/direct_cli/commands/audiencetargets.py +0 -0
  36. {direct_cli-0.2.7 → direct_cli-0.2.8}/direct_cli/commands/bids.py +0 -0
  37. {direct_cli-0.2.7 → direct_cli-0.2.8}/direct_cli/commands/businesses.py +0 -0
  38. {direct_cli-0.2.7 → direct_cli-0.2.8}/direct_cli/commands/campaigns.py +0 -0
  39. {direct_cli-0.2.7 → direct_cli-0.2.8}/direct_cli/commands/changes.py +0 -0
  40. {direct_cli-0.2.7 → direct_cli-0.2.8}/direct_cli/commands/clients.py +0 -0
  41. {direct_cli-0.2.7 → direct_cli-0.2.8}/direct_cli/commands/creatives.py +0 -0
  42. {direct_cli-0.2.7 → direct_cli-0.2.8}/direct_cli/commands/dictionaries.py +0 -0
  43. {direct_cli-0.2.7 → direct_cli-0.2.8}/direct_cli/commands/dynamicads.py +0 -0
  44. {direct_cli-0.2.7 → direct_cli-0.2.8}/direct_cli/commands/feeds.py +0 -0
  45. {direct_cli-0.2.7 → direct_cli-0.2.8}/direct_cli/commands/keywordbids.py +0 -0
  46. {direct_cli-0.2.7 → direct_cli-0.2.8}/direct_cli/commands/keywords.py +0 -0
  47. {direct_cli-0.2.7 → direct_cli-0.2.8}/direct_cli/commands/keywordsresearch.py +0 -0
  48. {direct_cli-0.2.7 → direct_cli-0.2.8}/direct_cli/commands/leads.py +0 -0
  49. {direct_cli-0.2.7 → direct_cli-0.2.8}/direct_cli/commands/negativekeywordsharedsets.py +0 -0
  50. {direct_cli-0.2.7 → direct_cli-0.2.8}/direct_cli/commands/reports.py +0 -0
  51. {direct_cli-0.2.7 → direct_cli-0.2.8}/direct_cli/commands/retargeting.py +0 -0
  52. {direct_cli-0.2.7 → direct_cli-0.2.8}/direct_cli/commands/sitelinks.py +0 -0
  53. {direct_cli-0.2.7 → direct_cli-0.2.8}/direct_cli/commands/smartadtargets.py +0 -0
  54. {direct_cli-0.2.7 → direct_cli-0.2.8}/direct_cli/commands/turbopages.py +0 -0
  55. {direct_cli-0.2.7 → direct_cli-0.2.8}/direct_cli/commands/vcards.py +0 -0
  56. {direct_cli-0.2.7 → direct_cli-0.2.8}/direct_cli/output.py +0 -0
  57. {direct_cli-0.2.7 → direct_cli-0.2.8}/direct_cli/reports_coverage.py +0 -0
  58. {direct_cli-0.2.7 → direct_cli-0.2.8}/direct_cli/utils.py +0 -0
  59. {direct_cli-0.2.7 → direct_cli-0.2.8}/direct_cli.egg-info/dependency_links.txt +0 -0
  60. {direct_cli-0.2.7 → direct_cli-0.2.8}/direct_cli.egg-info/requires.txt +0 -0
  61. {direct_cli-0.2.7 → direct_cli-0.2.8}/direct_cli.egg-info/top_level.txt +0 -0
  62. {direct_cli-0.2.7 → direct_cli-0.2.8}/docs/superpowers/plans/2026-04-12-issue-32-completion.md +0 -0
  63. {direct_cli-0.2.7 → direct_cli-0.2.8}/scripts/build_api_coverage_report.py +0 -0
  64. {direct_cli-0.2.7 → direct_cli-0.2.8}/scripts/check_reports_drift.py +0 -0
  65. {direct_cli-0.2.7 → direct_cli-0.2.8}/scripts/check_wsdl_drift.py +0 -0
  66. {direct_cli-0.2.7 → direct_cli-0.2.8}/scripts/refresh_reports_cache.py +0 -0
  67. {direct_cli-0.2.7 → direct_cli-0.2.8}/scripts/refresh_wsdl_cache.py +0 -0
  68. {direct_cli-0.2.7 → direct_cli-0.2.8}/scripts/release_pypi.sh +0 -0
  69. {direct_cli-0.2.7 → direct_cli-0.2.8}/setup.cfg +0 -0
  70. {direct_cli-0.2.7 → direct_cli-0.2.8}/setup.py +0 -0
  71. {direct_cli-0.2.7 → direct_cli-0.2.8}/tests/__init__.py +0 -0
  72. {direct_cli-0.2.7 → direct_cli-0.2.8}/tests/cassettes/test_integration_write/TestWriteAdExtensions.test_add_delete.yaml +0 -0
  73. {direct_cli-0.2.7 → direct_cli-0.2.8}/tests/cassettes/test_integration_write/TestWriteAdGroups.test_add_update_delete.yaml +0 -0
  74. {direct_cli-0.2.7 → direct_cli-0.2.8}/tests/cassettes/test_integration_write/TestWriteAdImages.test_add_delete.yaml +0 -0
  75. {direct_cli-0.2.7 → direct_cli-0.2.8}/tests/cassettes/test_integration_write/TestWriteAds.test_add_text_ad_update_delete.yaml +0 -0
  76. {direct_cli-0.2.7 → direct_cli-0.2.8}/tests/cassettes/test_integration_write/TestWriteAudienceTargets.test_add_delete.yaml +0 -0
  77. {direct_cli-0.2.7 → direct_cli-0.2.8}/tests/cassettes/test_integration_write/TestWriteBidModifiersAdd.test_add_delete_mobile.yaml +0 -0
  78. {direct_cli-0.2.7 → direct_cli-0.2.8}/tests/cassettes/test_integration_write/TestWriteBidModifiersSet.test_set_without_id_is_rejected.yaml +0 -0
  79. {direct_cli-0.2.7 → direct_cli-0.2.8}/tests/cassettes/test_integration_write/TestWriteBids.test_set_bid.yaml +0 -0
  80. {direct_cli-0.2.7 → direct_cli-0.2.8}/tests/cassettes/test_integration_write/TestWriteCampaigns.test_campaign_lifecycle.yaml +0 -0
  81. {direct_cli-0.2.7 → direct_cli-0.2.8}/tests/cassettes/test_integration_write/TestWriteDynamicAds.test_add_update_delete.yaml +0 -0
  82. {direct_cli-0.2.7 → direct_cli-0.2.8}/tests/cassettes/test_integration_write/TestWriteFeeds.test_add_update_delete.yaml +0 -0
  83. {direct_cli-0.2.7 → direct_cli-0.2.8}/tests/cassettes/test_integration_write/TestWriteKeywordBids.test_set_keyword_bid.yaml +0 -0
  84. {direct_cli-0.2.7 → direct_cli-0.2.8}/tests/cassettes/test_integration_write/TestWriteKeywords.test_add_update_delete.yaml +0 -0
  85. {direct_cli-0.2.7 → direct_cli-0.2.8}/tests/cassettes/test_integration_write/TestWriteNegativeKeywordSharedSets.test_add_update_delete.yaml +0 -0
  86. {direct_cli-0.2.7 → direct_cli-0.2.8}/tests/cassettes/test_integration_write/TestWriteRetargeting.test_add_delete.yaml +0 -0
  87. {direct_cli-0.2.7 → direct_cli-0.2.8}/tests/cassettes/test_integration_write/TestWriteSitelinks.test_add_delete.yaml +0 -0
  88. {direct_cli-0.2.7 → direct_cli-0.2.8}/tests/cassettes/test_integration_write/TestWriteSmartAdTargets.test_add_update_delete.yaml +0 -0
  89. {direct_cli-0.2.7 → direct_cli-0.2.8}/tests/cassettes/test_integration_write/TestWriteVCards.test_add_delete.yaml +0 -0
  90. {direct_cli-0.2.7 → direct_cli-0.2.8}/tests/conftest.py +0 -0
  91. {direct_cli-0.2.7 → direct_cli-0.2.8}/tests/reports_cache/raw/fields-list.html +0 -0
  92. {direct_cli-0.2.7 → direct_cli-0.2.8}/tests/reports_cache/raw/headers.html +0 -0
  93. {direct_cli-0.2.7 → direct_cli-0.2.8}/tests/reports_cache/raw/spec.html +0 -0
  94. {direct_cli-0.2.7 → direct_cli-0.2.8}/tests/reports_cache/raw/type.html +0 -0
  95. {direct_cli-0.2.7 → direct_cli-0.2.8}/tests/reports_cache/spec.json +0 -0
  96. {direct_cli-0.2.7 → direct_cli-0.2.8}/tests/test_auth_bw.py +0 -0
  97. {direct_cli-0.2.7 → direct_cli-0.2.8}/tests/test_auth_op.py +0 -0
  98. {direct_cli-0.2.7 → direct_cli-0.2.8}/tests/test_comprehensive.py +0 -0
  99. {direct_cli-0.2.7 → direct_cli-0.2.8}/tests/test_integration.py +0 -0
  100. {direct_cli-0.2.7 → direct_cli-0.2.8}/tests/test_reports_drift.py +0 -0
  101. {direct_cli-0.2.7 → direct_cli-0.2.8}/tests/wsdl_cache/adextensions.xml +0 -0
  102. {direct_cli-0.2.7 → direct_cli-0.2.8}/tests/wsdl_cache/adgroups.xml +0 -0
  103. {direct_cli-0.2.7 → direct_cli-0.2.8}/tests/wsdl_cache/adimages.xml +0 -0
  104. {direct_cli-0.2.7 → direct_cli-0.2.8}/tests/wsdl_cache/ads.xml +0 -0
  105. {direct_cli-0.2.7 → direct_cli-0.2.8}/tests/wsdl_cache/advideos.xml +0 -0
  106. {direct_cli-0.2.7 → direct_cli-0.2.8}/tests/wsdl_cache/agencyclients.xml +0 -0
  107. {direct_cli-0.2.7 → direct_cli-0.2.8}/tests/wsdl_cache/audiencetargets.xml +0 -0
  108. {direct_cli-0.2.7 → direct_cli-0.2.8}/tests/wsdl_cache/bidmodifiers.xml +0 -0
  109. {direct_cli-0.2.7 → direct_cli-0.2.8}/tests/wsdl_cache/bids.xml +0 -0
  110. {direct_cli-0.2.7 → direct_cli-0.2.8}/tests/wsdl_cache/businesses.xml +0 -0
  111. {direct_cli-0.2.7 → direct_cli-0.2.8}/tests/wsdl_cache/campaigns.xml +0 -0
  112. {direct_cli-0.2.7 → direct_cli-0.2.8}/tests/wsdl_cache/changes.xml +0 -0
  113. {direct_cli-0.2.7 → direct_cli-0.2.8}/tests/wsdl_cache/clients.xml +0 -0
  114. {direct_cli-0.2.7 → direct_cli-0.2.8}/tests/wsdl_cache/creatives.xml +0 -0
  115. {direct_cli-0.2.7 → direct_cli-0.2.8}/tests/wsdl_cache/dictionaries.xml +0 -0
  116. {direct_cli-0.2.7 → direct_cli-0.2.8}/tests/wsdl_cache/dynamictextadtargets.xml +0 -0
  117. {direct_cli-0.2.7 → direct_cli-0.2.8}/tests/wsdl_cache/feeds.xml +0 -0
  118. {direct_cli-0.2.7 → direct_cli-0.2.8}/tests/wsdl_cache/keywordbids.xml +0 -0
  119. {direct_cli-0.2.7 → direct_cli-0.2.8}/tests/wsdl_cache/keywords.xml +0 -0
  120. {direct_cli-0.2.7 → direct_cli-0.2.8}/tests/wsdl_cache/keywordsresearch.xml +0 -0
  121. {direct_cli-0.2.7 → direct_cli-0.2.8}/tests/wsdl_cache/leads.xml +0 -0
  122. {direct_cli-0.2.7 → direct_cli-0.2.8}/tests/wsdl_cache/negativekeywordsharedsets.xml +0 -0
  123. {direct_cli-0.2.7 → direct_cli-0.2.8}/tests/wsdl_cache/retargetinglists.xml +0 -0
  124. {direct_cli-0.2.7 → direct_cli-0.2.8}/tests/wsdl_cache/sitelinks.xml +0 -0
  125. {direct_cli-0.2.7 → direct_cli-0.2.8}/tests/wsdl_cache/smartadtargets.xml +0 -0
  126. {direct_cli-0.2.7 → direct_cli-0.2.8}/tests/wsdl_cache/turbopages.xml +0 -0
  127. {direct_cli-0.2.7 → direct_cli-0.2.8}/tests/wsdl_cache/vcards.xml +0 -0
@@ -106,6 +106,7 @@ Valid canonical examples:
106
106
  direct campaigns get --ids 1,2,3
107
107
  direct changes check-campaigns --timestamp 2026-04-14T00:00:00
108
108
  direct keywordsresearch has-search-volume --keywords "buy laptop,buy desktop"
109
+ direct smartadtargets update --id 456 --priority HIGH
109
110
  direct dynamicads set-bids --id 789 --bid 12.5
110
111
  direct dictionaries get-geo-regions --region-ids 225 --fields GeoRegionId,GeoRegionName
111
112
  ```
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: direct-cli
3
- Version: 0.2.7
3
+ Version: 0.2.8
4
4
  Summary: Command-line interface for Yandex Direct API
5
5
  Author: axisrow
6
6
  License: MIT
@@ -67,6 +67,8 @@ direct --token YOUR_TOKEN --login YOUR_LOGIN campaigns get
67
67
  ```
68
68
 
69
69
  Install with `pip install direct-cli`, then run commands with `direct`.
70
+ Invoking the deprecated `direct-cli` entrypoint exits with
71
+ `use direct instead of direct-cli`.
70
72
 
71
73
  ### Global Options
72
74
 
@@ -101,9 +103,10 @@ Naming rules:
101
103
  - multiword commands use kebab-case
102
104
  - examples: `get`, `set-bids`, `check-campaigns`, `has-search-volume`
103
105
 
104
- direct-cli owns the public naming contract. `tapi-yandex-direct` may
105
- influence the internal transport layer, but it does not define canonical CLI
106
- names.
106
+ The `direct` executable defines the public naming contract. The
107
+ `direct-cli` package name and deprecated shim do not define canonical CLI
108
+ names. `tapi-yandex-direct` may influence the internal transport layer, but it
109
+ does not define canonical CLI names.
107
110
 
108
111
  The current policy is canonical-only. Historical aliases are not preserved in
109
112
  the runtime CLI by default. If compatibility is ever needed, an alias must be
@@ -120,6 +123,7 @@ be supported.
120
123
  - If the API requires a complex object, the CLI must expose explicit flags or subcommands instead of forwarding raw JSON.
121
124
 
122
125
  #### Command Formatting Rules
126
+
123
127
  - Every canonical CLI command must be written strictly on a single line.
124
128
  - Multi-line command formatting is not allowed.
125
129
  - Shell line continuation using `\` is forbidden in canonical documentation, help text, tests, and examples.
@@ -184,6 +188,7 @@ Valid canonical examples:
184
188
  direct campaigns get --ids 1,2,3
185
189
  direct changes check-campaigns --timestamp 2026-04-14T00:00:00
186
190
  direct keywordsresearch has-search-volume --keywords "buy laptop,buy desktop"
191
+ direct smartadtargets update --id 456 --priority HIGH
187
192
  direct dynamicads set-bids --id 789 --bid 12.5 --context-bid 9 --priority HIGH
188
193
  direct dictionaries get-geo-regions --name Moscow --region-ids 225,187 --exact-names Москва,Санкт-Петербург --fields GeoRegionId,GeoRegionName
189
194
  ```
@@ -272,10 +277,14 @@ Available report types: `CAMPAIGN_PERFORMANCE_REPORT`, `ADGROUP_PERFORMANCE_REPO
272
277
  # Reference dictionaries and changes
273
278
  direct dictionaries get --names Currencies,GeoRegions
274
279
  direct dictionaries get-geo-regions --name Moscow --region-ids 225,187 --exact-names Москва,Санкт-Петербург --fields GeoRegionId,GeoRegionName
280
+
281
+ # Client info
282
+ direct clients get --fields ClientId,Login,Currency
283
+
284
+ # Changes
275
285
  direct changes check --campaign-ids 1,2,3 --timestamp 2026-04-14T00:00:00
276
286
  direct changes check-campaigns --timestamp 2026-04-14T00:00:00
277
287
  direct changes check-dictionaries
278
- direct clients get --fields ClientId,Login,Currency
279
288
 
280
289
  # Keyword research and retargeting
281
290
  direct keywordsresearch has-search-volume --keywords "buy laptop,buy desktop"
@@ -501,7 +510,9 @@ YANDEX_DIRECT_LOGIN=ваш_логин_на_яндексе
501
510
  direct --token ВАШ_ТОКЕН --login ВАШ_ЛОГИН campaigns get
502
511
  ```
503
512
 
504
- Установка остаётся через `pip install direct-cli`, а запуск команд теперь идет через `direct`.
513
+ Установка остаётся через `pip install direct-cli`, а запуск команд теперь идет
514
+ через `direct`. Вызов deprecated entrypoint `direct-cli` завершается ошибкой с
515
+ подсказкой `use direct instead of direct-cli`.
505
516
 
506
517
  ### Глобальные опции
507
518
 
@@ -530,17 +541,20 @@ Command naming rules:
530
541
  - в документации и примерах каноническими считаются `get`,
531
542
  `check-dictionaries` и `has-search-volume`
532
543
 
533
- `direct-cli` владеет публичным naming contract. `tapi-yandex-direct` может
534
- влиять на внутренний transport layer, но не определяет канонические CLI-имена.
544
+ Публичный naming contract задаёт исполняемый файл `direct`. Имя пакета
545
+ `direct-cli` и deprecated shim не определяют канонические CLI-имена.
546
+ `tapi-yandex-direct` может влиять на внутренний transport layer, но не
547
+ определяет канонические CLI-имена.
535
548
 
536
549
  Текущая политика — canonical-only. Исторические aliases по умолчанию не
537
550
  сохраняются в runtime CLI. Если совместимость когда-нибудь понадобится, alias
538
551
  должен быть добавлен как явное exception-правило с конкретным legacy syntax из
539
552
  `tapi-yandex-direct`, который действительно нужно поддержать.
540
553
 
541
- `direct-cli` — это транспортный слой над API Яндекс Директа. Канонические
542
- имена CLI-групп следуют нормализованным Python-именам из
543
- `tapi-yandex-direct`, а имена подкоманд — это kebab-case проекции API-методов.
554
+ `direct` — это канонический transport entrypoint над API Яндекс Директа,
555
+ устанавливаемый пакетом `direct-cli`. Канонические имена CLI-групп следуют
556
+ нормализованным Python-именам из `tapi-yandex-direct`, а имена подкоманд —
557
+ это kebab-case проекции API-методов.
544
558
 
545
559
  Базовые соответствия:
546
560
 
@@ -575,8 +589,10 @@ Naming rules:
575
589
  - multiword commands use kebab-case
576
590
  - examples: `get`, `set-bids`, `check-campaigns`, `has-search-volume`
577
591
 
578
- `direct-cli` владеет публичным naming contract. `tapi-yandex-direct` может
579
- влиять на внутренний transport layer, но не определяет канонические CLI-имена.
592
+ Публичный naming contract задаёт исполняемый файл `direct`. Имя пакета
593
+ `direct-cli` и deprecated shim не определяют канонические CLI-имена.
594
+ `tapi-yandex-direct` может влиять на внутренний transport layer, но не
595
+ определяет канонические CLI-имена.
580
596
 
581
597
  Текущая политика — canonical-only. Исторические aliases по умолчанию не
582
598
  сохраняются в runtime CLI. Если совместимость когда-нибудь понадобится, alias
@@ -746,10 +762,14 @@ direct reports list-types
746
762
  # Справочники и изменения
747
763
  direct dictionaries get --names Currencies,GeoRegions
748
764
  direct dictionaries get-geo-regions --name Москва --region-ids 225,187 --exact-names Москва,Санкт-Петербург --fields GeoRegionId,GeoRegionName
765
+
766
+ # Информация о клиенте
767
+ direct clients get --fields ClientId,Login,Currency
768
+
769
+ # Изменения
749
770
  direct changes check --campaign-ids 1,2,3 --timestamp 2026-04-14T00:00:00
750
771
  direct changes check-campaigns --timestamp 2026-04-14T00:00:00
751
772
  direct changes check-dictionaries
752
- direct clients get --fields ClientId,Login,Currency
753
773
 
754
774
  # Исследование ключевых слов и ретаргетинг
755
775
  direct keywordsresearch has-search-volume --keywords "купить ноутбук,купить компьютер"
@@ -30,6 +30,8 @@ direct --token YOUR_TOKEN --login YOUR_LOGIN campaigns get
30
30
  ```
31
31
 
32
32
  Install with `pip install direct-cli`, then run commands with `direct`.
33
+ Invoking the deprecated `direct-cli` entrypoint exits with
34
+ `use direct instead of direct-cli`.
33
35
 
34
36
  ### Global Options
35
37
 
@@ -64,9 +66,10 @@ Naming rules:
64
66
  - multiword commands use kebab-case
65
67
  - examples: `get`, `set-bids`, `check-campaigns`, `has-search-volume`
66
68
 
67
- direct-cli owns the public naming contract. `tapi-yandex-direct` may
68
- influence the internal transport layer, but it does not define canonical CLI
69
- names.
69
+ The `direct` executable defines the public naming contract. The
70
+ `direct-cli` package name and deprecated shim do not define canonical CLI
71
+ names. `tapi-yandex-direct` may influence the internal transport layer, but it
72
+ does not define canonical CLI names.
70
73
 
71
74
  The current policy is canonical-only. Historical aliases are not preserved in
72
75
  the runtime CLI by default. If compatibility is ever needed, an alias must be
@@ -83,6 +86,7 @@ be supported.
83
86
  - If the API requires a complex object, the CLI must expose explicit flags or subcommands instead of forwarding raw JSON.
84
87
 
85
88
  #### Command Formatting Rules
89
+
86
90
  - Every canonical CLI command must be written strictly on a single line.
87
91
  - Multi-line command formatting is not allowed.
88
92
  - Shell line continuation using `\` is forbidden in canonical documentation, help text, tests, and examples.
@@ -147,6 +151,7 @@ Valid canonical examples:
147
151
  direct campaigns get --ids 1,2,3
148
152
  direct changes check-campaigns --timestamp 2026-04-14T00:00:00
149
153
  direct keywordsresearch has-search-volume --keywords "buy laptop,buy desktop"
154
+ direct smartadtargets update --id 456 --priority HIGH
150
155
  direct dynamicads set-bids --id 789 --bid 12.5 --context-bid 9 --priority HIGH
151
156
  direct dictionaries get-geo-regions --name Moscow --region-ids 225,187 --exact-names Москва,Санкт-Петербург --fields GeoRegionId,GeoRegionName
152
157
  ```
@@ -235,10 +240,14 @@ Available report types: `CAMPAIGN_PERFORMANCE_REPORT`, `ADGROUP_PERFORMANCE_REPO
235
240
  # Reference dictionaries and changes
236
241
  direct dictionaries get --names Currencies,GeoRegions
237
242
  direct dictionaries get-geo-regions --name Moscow --region-ids 225,187 --exact-names Москва,Санкт-Петербург --fields GeoRegionId,GeoRegionName
243
+
244
+ # Client info
245
+ direct clients get --fields ClientId,Login,Currency
246
+
247
+ # Changes
238
248
  direct changes check --campaign-ids 1,2,3 --timestamp 2026-04-14T00:00:00
239
249
  direct changes check-campaigns --timestamp 2026-04-14T00:00:00
240
250
  direct changes check-dictionaries
241
- direct clients get --fields ClientId,Login,Currency
242
251
 
243
252
  # Keyword research and retargeting
244
253
  direct keywordsresearch has-search-volume --keywords "buy laptop,buy desktop"
@@ -464,7 +473,9 @@ YANDEX_DIRECT_LOGIN=ваш_логин_на_яндексе
464
473
  direct --token ВАШ_ТОКЕН --login ВАШ_ЛОГИН campaigns get
465
474
  ```
466
475
 
467
- Установка остаётся через `pip install direct-cli`, а запуск команд теперь идет через `direct`.
476
+ Установка остаётся через `pip install direct-cli`, а запуск команд теперь идет
477
+ через `direct`. Вызов deprecated entrypoint `direct-cli` завершается ошибкой с
478
+ подсказкой `use direct instead of direct-cli`.
468
479
 
469
480
  ### Глобальные опции
470
481
 
@@ -493,17 +504,20 @@ Command naming rules:
493
504
  - в документации и примерах каноническими считаются `get`,
494
505
  `check-dictionaries` и `has-search-volume`
495
506
 
496
- `direct-cli` владеет публичным naming contract. `tapi-yandex-direct` может
497
- влиять на внутренний transport layer, но не определяет канонические CLI-имена.
507
+ Публичный naming contract задаёт исполняемый файл `direct`. Имя пакета
508
+ `direct-cli` и deprecated shim не определяют канонические CLI-имена.
509
+ `tapi-yandex-direct` может влиять на внутренний transport layer, но не
510
+ определяет канонические CLI-имена.
498
511
 
499
512
  Текущая политика — canonical-only. Исторические aliases по умолчанию не
500
513
  сохраняются в runtime CLI. Если совместимость когда-нибудь понадобится, alias
501
514
  должен быть добавлен как явное exception-правило с конкретным legacy syntax из
502
515
  `tapi-yandex-direct`, который действительно нужно поддержать.
503
516
 
504
- `direct-cli` — это транспортный слой над API Яндекс Директа. Канонические
505
- имена CLI-групп следуют нормализованным Python-именам из
506
- `tapi-yandex-direct`, а имена подкоманд — это kebab-case проекции API-методов.
517
+ `direct` — это канонический transport entrypoint над API Яндекс Директа,
518
+ устанавливаемый пакетом `direct-cli`. Канонические имена CLI-групп следуют
519
+ нормализованным Python-именам из `tapi-yandex-direct`, а имена подкоманд —
520
+ это kebab-case проекции API-методов.
507
521
 
508
522
  Базовые соответствия:
509
523
 
@@ -538,8 +552,10 @@ Naming rules:
538
552
  - multiword commands use kebab-case
539
553
  - examples: `get`, `set-bids`, `check-campaigns`, `has-search-volume`
540
554
 
541
- `direct-cli` владеет публичным naming contract. `tapi-yandex-direct` может
542
- влиять на внутренний transport layer, но не определяет канонические CLI-имена.
555
+ Публичный naming contract задаёт исполняемый файл `direct`. Имя пакета
556
+ `direct-cli` и deprecated shim не определяют канонические CLI-имена.
557
+ `tapi-yandex-direct` может влиять на внутренний transport layer, но не
558
+ определяет канонические CLI-имена.
543
559
 
544
560
  Текущая политика — canonical-only. Исторические aliases по умолчанию не
545
561
  сохраняются в runtime CLI. Если совместимость когда-нибудь понадобится, alias
@@ -709,10 +725,14 @@ direct reports list-types
709
725
  # Справочники и изменения
710
726
  direct dictionaries get --names Currencies,GeoRegions
711
727
  direct dictionaries get-geo-regions --name Москва --region-ids 225,187 --exact-names Москва,Санкт-Петербург --fields GeoRegionId,GeoRegionName
728
+
729
+ # Информация о клиенте
730
+ direct clients get --fields ClientId,Login,Currency
731
+
732
+ # Изменения
712
733
  direct changes check --campaign-ids 1,2,3 --timestamp 2026-04-14T00:00:00
713
734
  direct changes check-campaigns --timestamp 2026-04-14T00:00:00
714
735
  direct changes check-dictionaries
715
- direct clients get --fields ClientId,Login,Currency
716
736
 
717
737
  # Исследование ключевых слов и ретаргетинг
718
738
  direct keywordsresearch has-search-volume --keywords "купить ноутбук,купить компьютер"
@@ -2,13 +2,14 @@
2
2
  Direct CLI - Command-line interface for Yandex Direct API
3
3
  """
4
4
 
5
+ from importlib.metadata import PackageNotFoundError, version
6
+
5
7
  __author__ = "Pavel Maksimov"
6
8
  __email__ = "vur21@ya.ru"
7
9
 
8
- # Version will be set by setuptools_scm
9
10
  try:
10
- from ._version import version as __version__
11
- except ImportError:
11
+ __version__ = version("direct-cli")
12
+ except PackageNotFoundError:
12
13
  __version__ = "unknown"
13
14
 
14
15
  __all__ = ["__version__", "__author__", "__email__"]
@@ -0,0 +1,11 @@
1
+ import click
2
+
3
+ DEPRECATED_ENTRYPOINT_MESSAGE = (
4
+ "direct-cli is deprecated; use direct instead of direct-cli."
5
+ )
6
+
7
+
8
+ def deprecated_main() -> None:
9
+ """Fail fast for the deprecated `direct-cli` entrypoint."""
10
+ click.echo(f"Error: {DEPRECATED_ENTRYPOINT_MESSAGE}", err=True)
11
+ raise SystemExit(2)
@@ -6,6 +6,7 @@ Direct CLI - Command-line interface for Yandex Direct API
6
6
  import click
7
7
  from dotenv import load_dotenv
8
8
 
9
+ from . import __version__
9
10
  from .auth import get_credentials
10
11
 
11
12
  from .commands.campaigns import campaigns
@@ -42,6 +43,7 @@ load_dotenv()
42
43
 
43
44
 
44
45
  @click.group(name="direct")
46
+ @click.version_option(__version__, prog_name="direct")
45
47
  @click.option("--token", envvar="YANDEX_DIRECT_TOKEN", help="API access token")
46
48
  @click.option("--login", envvar="YANDEX_DIRECT_LOGIN", help="Client login")
47
49
  @click.option("--sandbox", is_flag=True, help="Use sandbox API")
@@ -337,67 +337,6 @@ def set(ctx, modifier_id, campaign_id, modifier_type, value, dry_run):
337
337
  raise click.Abort()
338
338
 
339
339
 
340
- @bidmodifiers.command()
341
- @click.option("--campaign-id", type=int, help="Campaign ID (mutually exclusive with --adgroup-id)")
342
- @click.option("--adgroup-id", type=int, help="Ad group ID (mutually exclusive with --campaign-id)")
343
- @click.option(
344
- "--type",
345
- "modifier_type",
346
- required=True,
347
- type=click.Choice([
348
- "DEMOGRAPHICS_ADJUSTMENT",
349
- "RETARGETING_ADJUSTMENT",
350
- "REGIONAL_ADJUSTMENT",
351
- "SERP_LAYOUT_ADJUSTMENT",
352
- "INCOME_GRADE_ADJUSTMENT",
353
- ], case_sensitive=False),
354
- help="Adjustment type to toggle",
355
- )
356
- @click.option("--enabled/--disabled", "enabled", default=True, help="Enable or disable")
357
- @click.option("--dry-run", is_flag=True, help="Show request without sending")
358
- @click.pass_context
359
- def toggle(ctx, campaign_id, adgroup_id, modifier_type, enabled, dry_run):
360
- """Toggle bid modifier state (enable/disable by type)"""
361
- try:
362
- if not campaign_id and not adgroup_id:
363
- raise click.UsageError("Either --campaign-id or --adgroup-id is required.")
364
- if campaign_id and adgroup_id:
365
- raise click.UsageError("Use either --campaign-id or --adgroup-id, not both.")
366
-
367
- item = {
368
- "Type": modifier_type.upper(),
369
- "Enabled": "YES" if enabled else "NO",
370
- }
371
- if campaign_id:
372
- item["CampaignId"] = campaign_id
373
- else:
374
- item["AdGroupId"] = adgroup_id
375
-
376
- body = {
377
- "method": "toggle",
378
- "params": {"BidModifierToggleItems": [item]},
379
- }
380
-
381
- if dry_run:
382
- format_output(body, "json", None)
383
- return
384
-
385
- client = create_client(
386
- token=ctx.obj.get("token"),
387
- login=ctx.obj.get("login"),
388
- sandbox=ctx.obj.get("sandbox"),
389
- )
390
-
391
- result = client.bidmodifiers().post(data=body)
392
- format_output(result().extract(), "json", None)
393
-
394
- except click.UsageError:
395
- raise
396
- except Exception as e:
397
- print_error(str(e))
398
- raise click.Abort()
399
-
400
-
401
340
  @bidmodifiers.command()
402
341
  @click.option("--id", "modifier_id", required=True, type=int, help="Modifier ID")
403
342
  @click.option("--dry-run", is_flag=True, help="Show request without sending")
@@ -109,10 +109,7 @@ INTENTIONAL_EXTRA_METHODS = {
109
109
  "CLI guard command: the Yandex Direct API does not support deleting "
110
110
  "agency clients, so the command aborts with an explicit message."
111
111
  ),
112
- ("bidmodifiers", "toggle"): (
113
- "JSON-only helper command; no matching SOAP/WSDL operation exists."
114
- ),
115
- ("keywords", "archive"): (
112
+ ("keywords", "archive"): (
116
113
  "Legacy lifecycle command preserved for compatibility with existing CLI users."
117
114
  ),
118
115
  ("keywords", "unarchive"): (
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: direct-cli
3
- Version: 0.2.7
3
+ Version: 0.2.8
4
4
  Summary: Command-line interface for Yandex Direct API
5
5
  Author: axisrow
6
6
  License: MIT
@@ -67,6 +67,8 @@ direct --token YOUR_TOKEN --login YOUR_LOGIN campaigns get
67
67
  ```
68
68
 
69
69
  Install with `pip install direct-cli`, then run commands with `direct`.
70
+ Invoking the deprecated `direct-cli` entrypoint exits with
71
+ `use direct instead of direct-cli`.
70
72
 
71
73
  ### Global Options
72
74
 
@@ -101,9 +103,10 @@ Naming rules:
101
103
  - multiword commands use kebab-case
102
104
  - examples: `get`, `set-bids`, `check-campaigns`, `has-search-volume`
103
105
 
104
- direct-cli owns the public naming contract. `tapi-yandex-direct` may
105
- influence the internal transport layer, but it does not define canonical CLI
106
- names.
106
+ The `direct` executable defines the public naming contract. The
107
+ `direct-cli` package name and deprecated shim do not define canonical CLI
108
+ names. `tapi-yandex-direct` may influence the internal transport layer, but it
109
+ does not define canonical CLI names.
107
110
 
108
111
  The current policy is canonical-only. Historical aliases are not preserved in
109
112
  the runtime CLI by default. If compatibility is ever needed, an alias must be
@@ -120,6 +123,7 @@ be supported.
120
123
  - If the API requires a complex object, the CLI must expose explicit flags or subcommands instead of forwarding raw JSON.
121
124
 
122
125
  #### Command Formatting Rules
126
+
123
127
  - Every canonical CLI command must be written strictly on a single line.
124
128
  - Multi-line command formatting is not allowed.
125
129
  - Shell line continuation using `\` is forbidden in canonical documentation, help text, tests, and examples.
@@ -184,6 +188,7 @@ Valid canonical examples:
184
188
  direct campaigns get --ids 1,2,3
185
189
  direct changes check-campaigns --timestamp 2026-04-14T00:00:00
186
190
  direct keywordsresearch has-search-volume --keywords "buy laptop,buy desktop"
191
+ direct smartadtargets update --id 456 --priority HIGH
187
192
  direct dynamicads set-bids --id 789 --bid 12.5 --context-bid 9 --priority HIGH
188
193
  direct dictionaries get-geo-regions --name Moscow --region-ids 225,187 --exact-names Москва,Санкт-Петербург --fields GeoRegionId,GeoRegionName
189
194
  ```
@@ -272,10 +277,14 @@ Available report types: `CAMPAIGN_PERFORMANCE_REPORT`, `ADGROUP_PERFORMANCE_REPO
272
277
  # Reference dictionaries and changes
273
278
  direct dictionaries get --names Currencies,GeoRegions
274
279
  direct dictionaries get-geo-regions --name Moscow --region-ids 225,187 --exact-names Москва,Санкт-Петербург --fields GeoRegionId,GeoRegionName
280
+
281
+ # Client info
282
+ direct clients get --fields ClientId,Login,Currency
283
+
284
+ # Changes
275
285
  direct changes check --campaign-ids 1,2,3 --timestamp 2026-04-14T00:00:00
276
286
  direct changes check-campaigns --timestamp 2026-04-14T00:00:00
277
287
  direct changes check-dictionaries
278
- direct clients get --fields ClientId,Login,Currency
279
288
 
280
289
  # Keyword research and retargeting
281
290
  direct keywordsresearch has-search-volume --keywords "buy laptop,buy desktop"
@@ -501,7 +510,9 @@ YANDEX_DIRECT_LOGIN=ваш_логин_на_яндексе
501
510
  direct --token ВАШ_ТОКЕН --login ВАШ_ЛОГИН campaigns get
502
511
  ```
503
512
 
504
- Установка остаётся через `pip install direct-cli`, а запуск команд теперь идет через `direct`.
513
+ Установка остаётся через `pip install direct-cli`, а запуск команд теперь идет
514
+ через `direct`. Вызов deprecated entrypoint `direct-cli` завершается ошибкой с
515
+ подсказкой `use direct instead of direct-cli`.
505
516
 
506
517
  ### Глобальные опции
507
518
 
@@ -530,17 +541,20 @@ Command naming rules:
530
541
  - в документации и примерах каноническими считаются `get`,
531
542
  `check-dictionaries` и `has-search-volume`
532
543
 
533
- `direct-cli` владеет публичным naming contract. `tapi-yandex-direct` может
534
- влиять на внутренний transport layer, но не определяет канонические CLI-имена.
544
+ Публичный naming contract задаёт исполняемый файл `direct`. Имя пакета
545
+ `direct-cli` и deprecated shim не определяют канонические CLI-имена.
546
+ `tapi-yandex-direct` может влиять на внутренний transport layer, но не
547
+ определяет канонические CLI-имена.
535
548
 
536
549
  Текущая политика — canonical-only. Исторические aliases по умолчанию не
537
550
  сохраняются в runtime CLI. Если совместимость когда-нибудь понадобится, alias
538
551
  должен быть добавлен как явное exception-правило с конкретным legacy syntax из
539
552
  `tapi-yandex-direct`, который действительно нужно поддержать.
540
553
 
541
- `direct-cli` — это транспортный слой над API Яндекс Директа. Канонические
542
- имена CLI-групп следуют нормализованным Python-именам из
543
- `tapi-yandex-direct`, а имена подкоманд — это kebab-case проекции API-методов.
554
+ `direct` — это канонический transport entrypoint над API Яндекс Директа,
555
+ устанавливаемый пакетом `direct-cli`. Канонические имена CLI-групп следуют
556
+ нормализованным Python-именам из `tapi-yandex-direct`, а имена подкоманд —
557
+ это kebab-case проекции API-методов.
544
558
 
545
559
  Базовые соответствия:
546
560
 
@@ -575,8 +589,10 @@ Naming rules:
575
589
  - multiword commands use kebab-case
576
590
  - examples: `get`, `set-bids`, `check-campaigns`, `has-search-volume`
577
591
 
578
- `direct-cli` владеет публичным naming contract. `tapi-yandex-direct` может
579
- влиять на внутренний transport layer, но не определяет канонические CLI-имена.
592
+ Публичный naming contract задаёт исполняемый файл `direct`. Имя пакета
593
+ `direct-cli` и deprecated shim не определяют канонические CLI-имена.
594
+ `tapi-yandex-direct` может влиять на внутренний transport layer, но не
595
+ определяет канонические CLI-имена.
580
596
 
581
597
  Текущая политика — canonical-only. Исторические aliases по умолчанию не
582
598
  сохраняются в runtime CLI. Если совместимость когда-нибудь понадобится, alias
@@ -746,10 +762,14 @@ direct reports list-types
746
762
  # Справочники и изменения
747
763
  direct dictionaries get --names Currencies,GeoRegions
748
764
  direct dictionaries get-geo-regions --name Москва --region-ids 225,187 --exact-names Москва,Санкт-Петербург --fields GeoRegionId,GeoRegionName
765
+
766
+ # Информация о клиенте
767
+ direct clients get --fields ClientId,Login,Currency
768
+
769
+ # Изменения
749
770
  direct changes check --campaign-ids 1,2,3 --timestamp 2026-04-14T00:00:00
750
771
  direct changes check-campaigns --timestamp 2026-04-14T00:00:00
751
772
  direct changes check-dictionaries
752
- direct clients get --fields ClientId,Login,Currency
753
773
 
754
774
  # Исследование ключевых слов и ретаргетинг
755
775
  direct keywordsresearch has-search-volume --keywords "купить ноутбук,купить компьютер"
@@ -12,6 +12,7 @@ setup.py
12
12
  .github/workflows/claude-code-review.yml
13
13
  .github/workflows/claude.yml
14
14
  direct_cli/__init__.py
15
+ direct_cli/_deprecated.py
15
16
  direct_cli/api.py
16
17
  direct_cli/auth.py
17
18
  direct_cli/cli.py
@@ -0,0 +1,3 @@
1
+ [console_scripts]
2
+ direct = direct_cli.cli:cli
3
+ direct-cli = direct_cli._deprecated:deprecated_main
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "direct-cli"
7
- version = "0.2.7"
7
+ version = "0.2.8"
8
8
  description = "Command-line interface for Yandex Direct API"
9
9
  readme = "README.md"
10
10
  license = {text = "MIT"}
@@ -48,7 +48,7 @@ dev = [
48
48
 
49
49
  [project.scripts]
50
50
  direct = "direct_cli.cli:cli"
51
- direct-cli = "direct_cli.cli:cli"
51
+ direct-cli = "direct_cli._deprecated:deprecated_main"
52
52
 
53
53
  [project.urls]
54
54
  Homepage = "https://github.com/axisrow/direct-cli"
@@ -49,8 +49,7 @@ DRY_RUN_PAYLOAD_EXCLUSIONS = {
49
49
  "bidmodifiers.add": "Requires modifier-type-specific typed flag fixtures.",
50
50
  "bidmodifiers.delete": "Helper/legacy surface; not part of strict WSDL parity claim.",
51
51
  "bidmodifiers.set": "Requires modifier-type-specific typed flag fixtures.",
52
- "bidmodifiers.toggle": "Intentional CLI helper without matching WSDL method.",
53
- "campaigns.add": "Requires campaign-type-specific typed payload variants; covered by focused dry-run tests.",
52
+ "campaigns.add": "Requires campaign-type-specific typed payload variants; covered by focused dry-run tests.",
54
53
  "campaigns.suspend": "Same lifecycle payload family as covered campaigns.delete/archive/resume.",
55
54
  "campaigns.unarchive": "Same lifecycle payload family as covered campaigns.delete/archive/resume.",
56
55
  "campaigns.update": "Requires typed budget/date/status variants; covered by focused dry-run tests.",
@@ -2,12 +2,18 @@
2
2
  Tests for Direct CLI
3
3
  """
4
4
 
5
+ import io
5
6
  import os
6
7
  import unittest
8
+ from contextlib import redirect_stderr
9
+ from importlib.metadata import version
7
10
  from pathlib import Path
8
11
  from unittest.mock import patch
12
+
9
13
  from click.testing import CliRunner
14
+
10
15
  from direct_cli.cli import cli
16
+ from direct_cli._deprecated import DEPRECATED_ENTRYPOINT_MESSAGE, deprecated_main
11
17
 
12
18
 
13
19
  class TestCLI(unittest.TestCase):
@@ -23,6 +29,13 @@ class TestCLI(unittest.TestCase):
23
29
  self.assertIn("Command-line interface for Yandex Direct API", result.output)
24
30
  self.assertIn("Usage: direct", result.output)
25
31
 
32
+ def test_cli_version(self):
33
+ """Test CLI version command"""
34
+ result = self.runner.invoke(cli, ["--version"])
35
+ self.assertEqual(result.exit_code, 0)
36
+ expected = f"direct, version {version('direct-cli')}"
37
+ self.assertEqual(result.output.strip(), expected)
38
+
26
39
  def test_campaigns_help(self):
27
40
  """Test campaigns help"""
28
41
  result = self.runner.invoke(cli, ["campaigns", "--help"])
@@ -112,6 +125,14 @@ class TestCLI(unittest.TestCase):
112
125
  self.assertNotIn("--notification-json", result.output)
113
126
  self.assertNotIn("--send-invite-to-json", result.output)
114
127
 
128
+ def test_deprecated_direct_cli_entrypoint_exits_with_hint(self):
129
+ stderr = io.StringIO()
130
+ with self.assertRaises(SystemExit) as context:
131
+ with redirect_stderr(stderr):
132
+ deprecated_main()
133
+ self.assertEqual(context.exception.code, 2)
134
+ self.assertIn(DEPRECATED_ENTRYPOINT_MESSAGE, stderr.getvalue())
135
+
115
136
 
116
137
  class TestAuth(unittest.TestCase):
117
138
  """Test authentication"""
@@ -146,7 +167,11 @@ class TestReadmeContract(unittest.TestCase):
146
167
  self.assertIn("direct <group> <command> [flags]", self.content)
147
168
  self.assertIn("Group naming rules", self.content)
148
169
  self.assertIn("Command naming rules", self.content)
149
- self.assertIn("direct-cli owns the public naming contract", self.content)
170
+ self.assertIn(
171
+ "The `direct` executable defines the public naming contract",
172
+ self.content,
173
+ )
174
+ self.assertIn("use direct instead of direct-cli", self.content)
150
175
 
151
176
  def test_readme_contains_canonical_command_examples(self):
152
177
  """README must include canonical examples for renamed commands."""
@@ -39,7 +39,7 @@ Coverage scope
39
39
  --------------
40
40
 
41
41
  The suite covers both payload-building write commands (``add``,
42
- ``update``, ``set``, ``toggle``) and the main single-action lifecycle
42
+ ``update``, ``set``) and the main single-action lifecycle
43
43
  commands that now expose ``--dry-run`` (``delete``, ``archive``,
44
44
  ``unarchive``, ``suspend``, ``resume``, ``moderate``) so that trivial
45
45
  ``SelectionCriteria`` regressions are also caught in CI.
@@ -726,29 +726,6 @@ def test_bidmodifiers_set_without_any_key_errors():
726
726
  assert "--id" in combined or "--campaign-id" in combined
727
727
 
728
728
 
729
- def test_bidmodifiers_toggle_enable():
730
- body = _dry_run(
731
- "bidmodifiers", "toggle",
732
- "--campaign-id", "777",
733
- "--type", "DEMOGRAPHICS_ADJUSTMENT",
734
- "--enabled",
735
- )
736
- assert body["method"] == "toggle"
737
- item = body["params"]["BidModifierToggleItems"][0]
738
- assert item == {"CampaignId": 777, "Type": "DEMOGRAPHICS_ADJUSTMENT", "Enabled": "YES"}
739
-
740
-
741
- def test_bidmodifiers_toggle_disable():
742
- body = _dry_run(
743
- "bidmodifiers", "toggle",
744
- "--campaign-id", "777",
745
- "--type", "DEMOGRAPHICS_ADJUSTMENT",
746
- "--disabled",
747
- )
748
- item = body["params"]["BidModifierToggleItems"][0]
749
- assert item == {"CampaignId": 777, "Type": "DEMOGRAPHICS_ADJUSTMENT", "Enabled": "NO"}
750
-
751
-
752
729
  def test_bidmodifiers_add_mobile_uses_nested_object():
753
730
  body = _dry_run(
754
731
  "bidmodifiers",
@@ -394,73 +394,6 @@ class TestWriteBidModifiersSet:
394
394
  )
395
395
 
396
396
 
397
- @pytest.mark.integration_write
398
- @pytest.mark.vcr
399
- @pytest.mark.skip(
400
- reason=(
401
- "No stable VCR cassette for bidmodifiers toggle. "
402
- "Replay mode fails without a recording and live sandbox rewrite is not stable. "
403
- "Toggle request shape remains covered by dry-run tests."
404
- )
405
- )
406
- @pytest.mark.sandbox_limitation(
407
- reason=(
408
- "Sandbox/VCR coverage for bidmodifiers toggle is unstable and may require "
409
- "existing DEMOGRAPHICS_ADJUSTMENT state"
410
- )
411
- )
412
- class TestWriteBidModifiers:
413
- def test_toggle_existing(self, sandbox_campaign):
414
- """Toggle a supported bid modifier type off and back on."""
415
- cid = sandbox_campaign
416
-
417
- r = _invoke(
418
- "bidmodifiers", "toggle",
419
- "--campaign-id", str(cid),
420
- "--type", "DEMOGRAPHICS_ADJUSTMENT",
421
- "--disabled",
422
- )
423
- if r.exit_code != 0:
424
- if _is_sandbox_error(r.output):
425
- pytest.skip(
426
- f"bidmodifiers toggle not supported (sandbox): {r.output[:200]}"
427
- )
428
- pytest.fail(
429
- f"bidmodifiers toggle --disabled failed: {r.output[:500]}"
430
- )
431
-
432
- if _has_result_errors(r.output, "ToggleResults"):
433
- if _is_sandbox_error(r.output):
434
- pytest.skip(
435
- f"bidmodifiers toggle rejected by sandbox: {r.output[:200]}"
436
- )
437
- pytest.fail(
438
- f"bidmodifiers toggle --disabled returned errors: {r.output[:500]}"
439
- )
440
-
441
- r = _invoke(
442
- "bidmodifiers", "toggle",
443
- "--campaign-id", str(cid),
444
- "--type", "DEMOGRAPHICS_ADJUSTMENT",
445
- "--enabled",
446
- )
447
- if r.exit_code != 0:
448
- if _is_sandbox_error(r.output):
449
- pytest.skip(
450
- f"bidmodifiers toggle on not supported (sandbox): {r.output[:200]}"
451
- )
452
- pytest.fail(f"bidmodifiers toggle --enabled failed: {r.output[:500]}")
453
-
454
- if _has_result_errors(r.output, "ToggleResults"):
455
- if _is_sandbox_error(r.output):
456
- pytest.skip(
457
- f"bidmodifiers toggle rejected by sandbox: {r.output[:200]}"
458
- )
459
- pytest.fail(
460
- f"bidmodifiers toggle --enabled returned errors: {r.output[:500]}"
461
- )
462
-
463
-
464
397
  # ── feeds ────────────────────────────────────────────────────────────────
465
398
 
466
399
 
@@ -1,3 +0,0 @@
1
- [console_scripts]
2
- direct = direct_cli.cli:cli
3
- direct-cli = direct_cli.cli:cli
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes