sellerclaw-cli 3.1.2__tar.gz → 3.2.0__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 (133) hide show
  1. {sellerclaw_cli-3.1.2 → sellerclaw_cli-3.2.0}/PKG-INFO +7 -4
  2. {sellerclaw_cli-3.1.2 → sellerclaw_cli-3.2.0}/README.md +6 -3
  3. {sellerclaw_cli-3.1.2 → sellerclaw_cli-3.2.0}/pyproject.toml +1 -1
  4. {sellerclaw_cli-3.1.2 → sellerclaw_cli-3.2.0}/sellerclaw_cli/_command_group.py +28 -14
  5. {sellerclaw_cli-3.1.2 → sellerclaw_cli-3.2.0}/sellerclaw_cli/commands/listings.py +11 -0
  6. {sellerclaw_cli-3.1.2 → sellerclaw_cli-3.2.0}/sellerclaw_cli/commands/orders.py +9 -1
  7. {sellerclaw_cli-3.1.2 → sellerclaw_cli-3.2.0}/sellerclaw_cli/commands/research_seo.py +10 -2
  8. {sellerclaw_cli-3.1.2 → sellerclaw_cli-3.2.0}/sellerclaw_cli/guides/listings.md +1 -0
  9. {sellerclaw_cli-3.1.2 → sellerclaw_cli-3.2.0}/sellerclaw_cli/guides/orders.md +1 -1
  10. {sellerclaw_cli-3.1.2 → sellerclaw_cli-3.2.0}/sellerclaw_cli/guides/research.md +4 -2
  11. {sellerclaw_cli-3.1.2 → sellerclaw_cli-3.2.0}/sellerclaw_cli/mcp_apps.py +501 -97
  12. {sellerclaw_cli-3.1.2 → sellerclaw_cli-3.2.0}/sellerclaw_cli/mcp_server.py +20 -11
  13. {sellerclaw_cli-3.1.2 → sellerclaw_cli-3.2.0}/.gitignore +0 -0
  14. {sellerclaw_cli-3.1.2 → sellerclaw_cli-3.2.0}/LICENSE +0 -0
  15. {sellerclaw_cli-3.1.2 → sellerclaw_cli-3.2.0}/plugin/claude/README.md +0 -0
  16. {sellerclaw_cli-3.1.2 → sellerclaw_cli-3.2.0}/sellerclaw_cli/__init__.py +0 -0
  17. {sellerclaw_cli-3.1.2 → sellerclaw_cli-3.2.0}/sellerclaw_cli/__main__.py +0 -0
  18. {sellerclaw_cli-3.1.2 → sellerclaw_cli-3.2.0}/sellerclaw_cli/_agent_id.py +0 -0
  19. {sellerclaw_cli-3.1.2 → sellerclaw_cli-3.2.0}/sellerclaw_cli/_auth.py +0 -0
  20. {sellerclaw_cli-3.1.2 → sellerclaw_cli-3.2.0}/sellerclaw_cli/_client.py +0 -0
  21. {sellerclaw_cli-3.1.2 → sellerclaw_cli-3.2.0}/sellerclaw_cli/_config.py +0 -0
  22. {sellerclaw_cli-3.1.2 → sellerclaw_cli-3.2.0}/sellerclaw_cli/_errors.py +0 -0
  23. {sellerclaw_cli-3.1.2 → sellerclaw_cli-3.2.0}/sellerclaw_cli/_generated/.gitkeep +0 -0
  24. {sellerclaw_cli-3.1.2 → sellerclaw_cli-3.2.0}/sellerclaw_cli/_job_wait.py +0 -0
  25. {sellerclaw_cli-3.1.2 → sellerclaw_cli-3.2.0}/sellerclaw_cli/_output.py +0 -0
  26. {sellerclaw_cli-3.1.2 → sellerclaw_cli-3.2.0}/sellerclaw_cli/_runtime.py +0 -0
  27. {sellerclaw_cli-3.1.2 → sellerclaw_cli-3.2.0}/sellerclaw_cli/_session_key.py +0 -0
  28. {sellerclaw_cli-3.1.2 → sellerclaw_cli-3.2.0}/sellerclaw_cli/_task_context.py +0 -0
  29. {sellerclaw_cli-3.1.2 → sellerclaw_cli-3.2.0}/sellerclaw_cli/assets/icon.png +0 -0
  30. {sellerclaw_cli-3.1.2 → sellerclaw_cli-3.2.0}/sellerclaw_cli/cli.py +0 -0
  31. {sellerclaw_cli-3.1.2 → sellerclaw_cli-3.2.0}/sellerclaw_cli/commands/__init__.py +0 -0
  32. {sellerclaw_cli-3.1.2 → sellerclaw_cli-3.2.0}/sellerclaw_cli/commands/_auth_cli.py +0 -0
  33. {sellerclaw_cli-3.1.2 → sellerclaw_cli-3.2.0}/sellerclaw_cli/commands/_discover.py +0 -0
  34. {sellerclaw_cli-3.1.2 → sellerclaw_cli-3.2.0}/sellerclaw_cli/commands/_listing_shape.py +0 -0
  35. {sellerclaw_cli-3.1.2 → sellerclaw_cli-3.2.0}/sellerclaw_cli/commands/account.py +0 -0
  36. {sellerclaw_cli-3.1.2 → sellerclaw_cli-3.2.0}/sellerclaw_cli/commands/action_requests.py +0 -0
  37. {sellerclaw_cli-3.1.2 → sellerclaw_cli-3.2.0}/sellerclaw_cli/commands/ad_accounts.py +0 -0
  38. {sellerclaw_cli-3.1.2 → sellerclaw_cli-3.2.0}/sellerclaw_cli/commands/agent_files.py +0 -0
  39. {sellerclaw_cli-3.1.2 → sellerclaw_cli-3.2.0}/sellerclaw_cli/commands/amazon.py +0 -0
  40. {sellerclaw_cli-3.1.2 → sellerclaw_cli-3.2.0}/sellerclaw_cli/commands/amazon_fba.py +0 -0
  41. {sellerclaw_cli-3.1.2 → sellerclaw_cli-3.2.0}/sellerclaw_cli/commands/amazon_listings.py +0 -0
  42. {sellerclaw_cli-3.1.2 → sellerclaw_cli-3.2.0}/sellerclaw_cli/commands/amazon_orders.py +0 -0
  43. {sellerclaw_cli-3.1.2 → sellerclaw_cli-3.2.0}/sellerclaw_cli/commands/amazon_store.py +0 -0
  44. {sellerclaw_cli-3.1.2 → sellerclaw_cli-3.2.0}/sellerclaw_cli/commands/analytics.py +0 -0
  45. {sellerclaw_cli-3.1.2 → sellerclaw_cli-3.2.0}/sellerclaw_cli/commands/attributes.py +0 -0
  46. {sellerclaw_cli-3.1.2 → sellerclaw_cli-3.2.0}/sellerclaw_cli/commands/bigcommerce.py +0 -0
  47. {sellerclaw_cli-3.1.2 → sellerclaw_cli-3.2.0}/sellerclaw_cli/commands/bigcommerce_listings.py +0 -0
  48. {sellerclaw_cli-3.1.2 → sellerclaw_cli-3.2.0}/sellerclaw_cli/commands/bigcommerce_orders.py +0 -0
  49. {sellerclaw_cli-3.1.2 → sellerclaw_cli-3.2.0}/sellerclaw_cli/commands/bigcommerce_store.py +0 -0
  50. {sellerclaw_cli-3.1.2 → sellerclaw_cli-3.2.0}/sellerclaw_cli/commands/catalog.py +0 -0
  51. {sellerclaw_cli-3.1.2 → sellerclaw_cli-3.2.0}/sellerclaw_cli/commands/catalog_file.py +0 -0
  52. {sellerclaw_cli-3.1.2 → sellerclaw_cli-3.2.0}/sellerclaw_cli/commands/categories.py +0 -0
  53. {sellerclaw_cli-3.1.2 → sellerclaw_cli-3.2.0}/sellerclaw_cli/commands/channels.py +0 -0
  54. {sellerclaw_cli-3.1.2 → sellerclaw_cli-3.2.0}/sellerclaw_cli/commands/chats.py +0 -0
  55. {sellerclaw_cli-3.1.2 → sellerclaw_cli-3.2.0}/sellerclaw_cli/commands/competitors.py +0 -0
  56. {sellerclaw_cli-3.1.2 → sellerclaw_cli-3.2.0}/sellerclaw_cli/commands/ebay.py +0 -0
  57. {sellerclaw_cli-3.1.2 → sellerclaw_cli-3.2.0}/sellerclaw_cli/commands/ebay_feedback.py +0 -0
  58. {sellerclaw_cli-3.1.2 → sellerclaw_cli-3.2.0}/sellerclaw_cli/commands/ebay_finances.py +0 -0
  59. {sellerclaw_cli-3.1.2 → sellerclaw_cli-3.2.0}/sellerclaw_cli/commands/ebay_listings.py +0 -0
  60. {sellerclaw_cli-3.1.2 → sellerclaw_cli-3.2.0}/sellerclaw_cli/commands/ebay_orders.py +0 -0
  61. {sellerclaw_cli-3.1.2 → sellerclaw_cli-3.2.0}/sellerclaw_cli/commands/ebay_promoted.py +0 -0
  62. {sellerclaw_cli-3.1.2 → sellerclaw_cli-3.2.0}/sellerclaw_cli/commands/ebay_shipping.py +0 -0
  63. {sellerclaw_cli-3.1.2 → sellerclaw_cli-3.2.0}/sellerclaw_cli/commands/ebay_store.py +0 -0
  64. {sellerclaw_cli-3.1.2 → sellerclaw_cli-3.2.0}/sellerclaw_cli/commands/email.py +0 -0
  65. {sellerclaw_cli-3.1.2 → sellerclaw_cli-3.2.0}/sellerclaw_cli/commands/etsy.py +0 -0
  66. {sellerclaw_cli-3.1.2 → sellerclaw_cli-3.2.0}/sellerclaw_cli/commands/etsy_finances.py +0 -0
  67. {sellerclaw_cli-3.1.2 → sellerclaw_cli-3.2.0}/sellerclaw_cli/commands/etsy_listings.py +0 -0
  68. {sellerclaw_cli-3.1.2 → sellerclaw_cli-3.2.0}/sellerclaw_cli/commands/etsy_orders.py +0 -0
  69. {sellerclaw_cli-3.1.2 → sellerclaw_cli-3.2.0}/sellerclaw_cli/commands/etsy_store.py +0 -0
  70. {sellerclaw_cli-3.1.2 → sellerclaw_cli-3.2.0}/sellerclaw_cli/commands/facebook_ads.py +0 -0
  71. {sellerclaw_cli-3.1.2 → sellerclaw_cli-3.2.0}/sellerclaw_cli/commands/goals.py +0 -0
  72. {sellerclaw_cli-3.1.2 → sellerclaw_cli-3.2.0}/sellerclaw_cli/commands/google_ads.py +0 -0
  73. {sellerclaw_cli-3.1.2 → sellerclaw_cli-3.2.0}/sellerclaw_cli/commands/integrations.py +0 -0
  74. {sellerclaw_cli-3.1.2 → sellerclaw_cli-3.2.0}/sellerclaw_cli/commands/kb.py +0 -0
  75. {sellerclaw_cli-3.1.2 → sellerclaw_cli-3.2.0}/sellerclaw_cli/commands/klaviyo.py +0 -0
  76. {sellerclaw_cli-3.1.2 → sellerclaw_cli-3.2.0}/sellerclaw_cli/commands/listing_problems.py +0 -0
  77. {sellerclaw_cli-3.1.2 → sellerclaw_cli-3.2.0}/sellerclaw_cli/commands/media.py +0 -0
  78. {sellerclaw_cli-3.1.2 → sellerclaw_cli-3.2.0}/sellerclaw_cli/commands/models.py +0 -0
  79. {sellerclaw_cli-3.1.2 → sellerclaw_cli-3.2.0}/sellerclaw_cli/commands/pdf.py +0 -0
  80. {sellerclaw_cli-3.1.2 → sellerclaw_cli-3.2.0}/sellerclaw_cli/commands/price_list.py +0 -0
  81. {sellerclaw_cli-3.1.2 → sellerclaw_cli-3.2.0}/sellerclaw_cli/commands/research_catalog.py +0 -0
  82. {sellerclaw_cli-3.1.2 → sellerclaw_cli-3.2.0}/sellerclaw_cli/commands/research_social.py +0 -0
  83. {sellerclaw_cli-3.1.2 → sellerclaw_cli-3.2.0}/sellerclaw_cli/commands/research_trends.py +0 -0
  84. {sellerclaw_cli-3.1.2 → sellerclaw_cli-3.2.0}/sellerclaw_cli/commands/reviews.py +0 -0
  85. {sellerclaw_cli-3.1.2 → sellerclaw_cli-3.2.0}/sellerclaw_cli/commands/sellercart.py +0 -0
  86. {sellerclaw_cli-3.1.2 → sellerclaw_cli-3.2.0}/sellerclaw_cli/commands/sellercart_domain.py +0 -0
  87. {sellerclaw_cli-3.1.2 → sellerclaw_cli-3.2.0}/sellerclaw_cli/commands/sellercart_media.py +0 -0
  88. {sellerclaw_cli-3.1.2 → sellerclaw_cli-3.2.0}/sellerclaw_cli/commands/sellercart_menus.py +0 -0
  89. {sellerclaw_cli-3.1.2 → sellerclaw_cli-3.2.0}/sellerclaw_cli/commands/sellercart_pages.py +0 -0
  90. {sellerclaw_cli-3.1.2 → sellerclaw_cli-3.2.0}/sellerclaw_cli/commands/sellercart_payouts.py +0 -0
  91. {sellerclaw_cli-3.1.2 → sellerclaw_cli-3.2.0}/sellerclaw_cli/commands/sellercart_products.py +0 -0
  92. {sellerclaw_cli-3.1.2 → sellerclaw_cli-3.2.0}/sellerclaw_cli/commands/sheets.py +0 -0
  93. {sellerclaw_cli-3.1.2 → sellerclaw_cli-3.2.0}/sellerclaw_cli/commands/shopify.py +0 -0
  94. {sellerclaw_cli-3.1.2 → sellerclaw_cli-3.2.0}/sellerclaw_cli/commands/shopify_collections.py +0 -0
  95. {sellerclaw_cli-3.1.2 → sellerclaw_cli-3.2.0}/sellerclaw_cli/commands/shopify_finances.py +0 -0
  96. {sellerclaw_cli-3.1.2 → sellerclaw_cli-3.2.0}/sellerclaw_cli/commands/shopify_listings.py +0 -0
  97. {sellerclaw_cli-3.1.2 → sellerclaw_cli-3.2.0}/sellerclaw_cli/commands/shopify_menus.py +0 -0
  98. {sellerclaw_cli-3.1.2 → sellerclaw_cli-3.2.0}/sellerclaw_cli/commands/shopify_orders.py +0 -0
  99. {sellerclaw_cli-3.1.2 → sellerclaw_cli-3.2.0}/sellerclaw_cli/commands/shopify_pages.py +0 -0
  100. {sellerclaw_cli-3.1.2 → sellerclaw_cli-3.2.0}/sellerclaw_cli/commands/shopify_store.py +0 -0
  101. {sellerclaw_cli-3.1.2 → sellerclaw_cli-3.2.0}/sellerclaw_cli/commands/shopify_themes.py +0 -0
  102. {sellerclaw_cli-3.1.2 → sellerclaw_cli-3.2.0}/sellerclaw_cli/commands/social.py +0 -0
  103. {sellerclaw_cli-3.1.2 → sellerclaw_cli-3.2.0}/sellerclaw_cli/commands/spreadsheet.py +0 -0
  104. {sellerclaw_cli-3.1.2 → sellerclaw_cli-3.2.0}/sellerclaw_cli/commands/store_audit.py +0 -0
  105. {sellerclaw_cli-3.1.2 → sellerclaw_cli-3.2.0}/sellerclaw_cli/commands/subagent_tasks.py +0 -0
  106. {sellerclaw_cli-3.1.2 → sellerclaw_cli-3.2.0}/sellerclaw_cli/commands/suppliers.py +0 -0
  107. {sellerclaw_cli-3.1.2 → sellerclaw_cli-3.2.0}/sellerclaw_cli/commands/team.py +0 -0
  108. {sellerclaw_cli-3.1.2 → sellerclaw_cli-3.2.0}/sellerclaw_cli/commands/team_tasks.py +0 -0
  109. {sellerclaw_cli-3.1.2 → sellerclaw_cli-3.2.0}/sellerclaw_cli/commands/tiktok_shop_listings.py +0 -0
  110. {sellerclaw_cli-3.1.2 → sellerclaw_cli-3.2.0}/sellerclaw_cli/commands/tiktok_shop_orders.py +0 -0
  111. {sellerclaw_cli-3.1.2 → sellerclaw_cli-3.2.0}/sellerclaw_cli/commands/tiktok_shop_store.py +0 -0
  112. {sellerclaw_cli-3.1.2 → sellerclaw_cli-3.2.0}/sellerclaw_cli/commands/walmart.py +0 -0
  113. {sellerclaw_cli-3.1.2 → sellerclaw_cli-3.2.0}/sellerclaw_cli/commands/walmart_listings.py +0 -0
  114. {sellerclaw_cli-3.1.2 → sellerclaw_cli-3.2.0}/sellerclaw_cli/commands/walmart_orders.py +0 -0
  115. {sellerclaw_cli-3.1.2 → sellerclaw_cli-3.2.0}/sellerclaw_cli/commands/walmart_store.py +0 -0
  116. {sellerclaw_cli-3.1.2 → sellerclaw_cli-3.2.0}/sellerclaw_cli/commands/web.py +0 -0
  117. {sellerclaw_cli-3.1.2 → sellerclaw_cli-3.2.0}/sellerclaw_cli/commands/wix.py +0 -0
  118. {sellerclaw_cli-3.1.2 → sellerclaw_cli-3.2.0}/sellerclaw_cli/commands/wix_listings.py +0 -0
  119. {sellerclaw_cli-3.1.2 → sellerclaw_cli-3.2.0}/sellerclaw_cli/commands/wix_orders.py +0 -0
  120. {sellerclaw_cli-3.1.2 → sellerclaw_cli-3.2.0}/sellerclaw_cli/commands/wix_store.py +0 -0
  121. {sellerclaw_cli-3.1.2 → sellerclaw_cli-3.2.0}/sellerclaw_cli/commands/woocommerce.py +0 -0
  122. {sellerclaw_cli-3.1.2 → sellerclaw_cli-3.2.0}/sellerclaw_cli/commands/woocommerce_listings.py +0 -0
  123. {sellerclaw_cli-3.1.2 → sellerclaw_cli-3.2.0}/sellerclaw_cli/commands/woocommerce_orders.py +0 -0
  124. {sellerclaw_cli-3.1.2 → sellerclaw_cli-3.2.0}/sellerclaw_cli/commands/woocommerce_store.py +0 -0
  125. {sellerclaw_cli-3.1.2 → sellerclaw_cli-3.2.0}/sellerclaw_cli/guides/__init__.py +0 -0
  126. {sellerclaw_cli-3.1.2 → sellerclaw_cli-3.2.0}/sellerclaw_cli/guides/ads.md +0 -0
  127. {sellerclaw_cli-3.1.2 → sellerclaw_cli-3.2.0}/sellerclaw_cli/guides/analytics.md +0 -0
  128. {sellerclaw_cli-3.1.2 → sellerclaw_cli-3.2.0}/sellerclaw_cli/guides/catalog.md +0 -0
  129. {sellerclaw_cli-3.1.2 → sellerclaw_cli-3.2.0}/sellerclaw_cli/guides/email.md +0 -0
  130. {sellerclaw_cli-3.1.2 → sellerclaw_cli-3.2.0}/sellerclaw_cli/guides/start.md +0 -0
  131. {sellerclaw_cli-3.1.2 → sellerclaw_cli-3.2.0}/sellerclaw_cli/guides/storefront.md +0 -0
  132. {sellerclaw_cli-3.1.2 → sellerclaw_cli-3.2.0}/sellerclaw_cli/guides/suppliers.md +0 -0
  133. {sellerclaw_cli-3.1.2 → sellerclaw_cli-3.2.0}/sellerclaw_cli/guides/topics.json +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: sellerclaw-cli
3
- Version: 3.1.2
3
+ Version: 3.2.0
4
4
  Summary: Command-line client and MCP server for the SellerClaw Agent API — run your stores, orders, listings, ads and suppliers from a terminal, scripts, or Claude. LLM-friendly JSON, browser sign-in.
5
5
  Project-URL: Homepage, https://sellerclaw.ai
6
6
  Project-URL: Documentation, https://github.com/sellerai-com/sellerclaw-cli#readme
@@ -525,10 +525,13 @@ New CLI commands appear automatically — there is nothing per-command to mainta
525
525
  Alongside those four, a client that supports [MCP Apps](https://modelcontextprotocol.io) gets
526
526
  **interactive cards** for the questions a seller asks most — what needs them today
527
527
  (`sellerclaw_attention`), how a store is doing (`sellerclaw_store_summary`), the orders or one
528
- order (`sellerclaw_orders`), listings or one listing (`sellerclaw_listings`), ads across every
528
+ order (`sellerclaw_orders`), listings or one listing (`sellerclaw_listings`), a catalog product
529
+ with its supplier and every store it is listed in (`sellerclaw_products`), ads across every
529
530
  platform (`sellerclaw_ads`), the health of every connection (`sellerclaw_connections`), and a
530
- request waiting on the owner (`sellerclaw_approval`). The cards show; changing anything still goes
531
- through `sellerclaw_run`. A client without cards gets the same answers as a short text summary plus
531
+ request waiting on the owner (`sellerclaw_approval`). The order, listing and product cards find what
532
+ the owner names in their own words — an order number, a title, a SKU — and open it when only one
533
+ matches. The cards show;
534
+ changing anything still goes through `sellerclaw_run`. A client without cards gets the same answers as a short text summary plus
532
535
  the full structured result.
533
536
 
534
537
  The guides live in the package ([`sellerclaw_cli/guides/`](sellerclaw_cli/guides/)) and are the same
@@ -490,10 +490,13 @@ New CLI commands appear automatically — there is nothing per-command to mainta
490
490
  Alongside those four, a client that supports [MCP Apps](https://modelcontextprotocol.io) gets
491
491
  **interactive cards** for the questions a seller asks most — what needs them today
492
492
  (`sellerclaw_attention`), how a store is doing (`sellerclaw_store_summary`), the orders or one
493
- order (`sellerclaw_orders`), listings or one listing (`sellerclaw_listings`), ads across every
493
+ order (`sellerclaw_orders`), listings or one listing (`sellerclaw_listings`), a catalog product
494
+ with its supplier and every store it is listed in (`sellerclaw_products`), ads across every
494
495
  platform (`sellerclaw_ads`), the health of every connection (`sellerclaw_connections`), and a
495
- request waiting on the owner (`sellerclaw_approval`). The cards show; changing anything still goes
496
- through `sellerclaw_run`. A client without cards gets the same answers as a short text summary plus
496
+ request waiting on the owner (`sellerclaw_approval`). The order, listing and product cards find what
497
+ the owner names in their own words — an order number, a title, a SKU — and open it when only one
498
+ matches. The cards show;
499
+ changing anything still goes through `sellerclaw_run`. A client without cards gets the same answers as a short text summary plus
497
500
  the full structured result.
498
501
 
499
502
  The guides live in the package ([`sellerclaw_cli/guides/`](sellerclaw_cli/guides/)) and are the same
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "sellerclaw-cli"
7
- version = "3.1.2"
7
+ version = "3.2.0"
8
8
  description = "Command-line client and MCP server for the SellerClaw Agent API — run your stores, orders, listings, ads and suppliers from a terminal, scripts, or Claude. LLM-friendly JSON, browser sign-in."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.11"
@@ -18,10 +18,11 @@ import difflib
18
18
  import inspect
19
19
  import mimetypes
20
20
  import re
21
- from collections.abc import Sequence
21
+ from collections.abc import Mapping, Sequence
22
22
  from dataclasses import dataclass, replace
23
23
  from pathlib import Path
24
24
  from typing import Any
25
+ from urllib.parse import quote
25
26
 
26
27
  import typer
27
28
 
@@ -237,12 +238,14 @@ def _validate_flag(f: Flag, value: object) -> None:
237
238
  """Reject out-of-range / out-of-choice values locally (clear error, no server round-trip)."""
238
239
  if value is None or value == [] or value is False:
239
240
  return
240
- if f.choices and isinstance(value, str) and value not in f.choices:
241
- emit_error(
242
- UserInputError(
243
- f"{f.primary_option}: must be one of {', '.join(f.choices)} (got {value!r})"
241
+ # A repeatable flag arrives as a list; each value is held to the same choices as a single one.
242
+ for item in value if isinstance(value, list) else [value]:
243
+ if f.choices and isinstance(item, str) and item not in f.choices:
244
+ emit_error(
245
+ UserInputError(
246
+ f"{f.primary_option}: must be one of {', '.join(f.choices)} (got {item!r})"
247
+ )
244
248
  )
245
- )
246
249
  if isinstance(value, int) and not isinstance(value, bool):
247
250
  if f.minimum is not None and value < f.minimum:
248
251
  emit_error(UserInputError(f"{f.primary_option}: must be >= {f.minimum} (got {value})"))
@@ -348,6 +351,19 @@ def positionals_of(path: str) -> list[str]:
348
351
  return _PATH_PARAM_RE.findall(path)
349
352
 
350
353
 
354
+ def fill_path(path: str, values: Mapping[str, Any]) -> str:
355
+ """``path`` with each ``{placeholder}`` replaced by its value, encoded as one path segment.
356
+
357
+ What people call things does not stay inside a URL's rules: an order number is ``#1001``, and
358
+ put in the path as it is, ``#`` starts the fragment — the request reaches ``/agent/orders/``
359
+ and asks for something else entirely. Encoded, every value is one segment whatever it holds,
360
+ and the server reads it back decoded.
361
+ """
362
+ for name, value in values.items():
363
+ path = path.replace("{" + name + "}", quote(str(value), safe=""))
364
+ return path
365
+
366
+
351
367
  def command_help(cmd: Cmd) -> str:
352
368
  """Help blurb shown by ``<group> <cmd> --help`` and embedded in discovery output."""
353
369
  pieces: list[str] = []
@@ -634,9 +650,10 @@ def _expand_id_prefix(cmd: Cmd, positionals: dict[str, Any], prefix: str) -> str
634
650
  list_path = cmd.resolve_list_path
635
651
  if not list_path:
636
652
  return None
637
- for name, value in positionals.items():
638
- if name != cmd.active_positional:
639
- list_path = list_path.replace("{" + name + "}", str(value))
653
+ list_path = fill_path(
654
+ list_path,
655
+ {name: value for name, value in positionals.items() if name != cmd.active_positional},
656
+ )
640
657
  try:
641
658
  with Client.from_env() as client:
642
659
  data = client.request("GET", list_path)
@@ -717,9 +734,7 @@ def _make_callback(group: str, cmd: Cmd):
717
734
  cmd, values, values.get(cmd.active_positional)
718
735
  )
719
736
  values[cmd.active_positional] = resolved_active
720
- path = cmd.path
721
- for name in positionals:
722
- path = path.replace("{" + name + "}", str(values[name]))
737
+ path = fill_path(cmd.path, {name: values[name] for name in positionals})
723
738
  body: Any = None
724
739
  if cmd.takes_body:
725
740
  raw_body = kwargs.pop("body")
@@ -754,8 +769,7 @@ def _make_callback(group: str, cmd: Cmd):
754
769
  params[f.query_key] = value
755
770
  poll_path = cmd.job_poll_path
756
771
  if poll_path is not None:
757
- for name in positionals:
758
- poll_path = poll_path.replace("{" + name + "}", str(values[name]))
772
+ poll_path = fill_path(poll_path, {name: values[name] for name in positionals})
759
773
  run_operation(
760
774
  ctx,
761
775
  cmd.method,
@@ -130,6 +130,17 @@ SPECS = (
130
130
  ),
131
131
  choices=("owner", "agent", "sync", "marketplace", "unknown"),
132
132
  ),
133
+ flag(
134
+ "sale_state",
135
+ repeatable=True,
136
+ help=(
137
+ "Whether a shopper can buy it; repeat to combine. --sale-state out_of_stock "
138
+ "--sale-state not_selling = 'what is not selling': live listings with nothing "
139
+ "to sell, or that the channel hides, is reviewing, refused or lost. A live "
140
+ "listing nobody can buy is still status 'active', so --status cannot ask this."
141
+ ),
142
+ choices=("selling", "out_of_stock", "not_selling", "not_published"),
143
+ ),
133
144
  flag("limit", type=int, minimum=1, maximum=200, default=25, help="Max results per page."),
134
145
  flag("offset", type=int, minimum=0, default=0, help="Results to skip (paging)."),
135
146
  ),
@@ -66,7 +66,15 @@ SPECS = (
66
66
  ),
67
67
  ),
68
68
  ),
69
- Cmd("get", "GET", "/agent/orders/{order_id}", summary="Get one order by id."),
69
+ Cmd(
70
+ "get",
71
+ "GET",
72
+ "/agent/orders/{order_id}",
73
+ summary=(
74
+ "Get one order by its id, its whole order number (#1001, the # optional) or the "
75
+ "marketplace's order id. A number two stores share answers 409 naming both."
76
+ ),
77
+ ),
70
78
  Cmd(
71
79
  "search",
72
80
  "GET",
@@ -87,7 +87,15 @@ SPECS = (
87
87
  "/agent/research/seo/serp-competitors",
88
88
  summary="serp competitors research.",
89
89
  body=(
90
- body_field("domain", required=True, help="Seed domain to find competitors for."),
90
+ body_field(
91
+ "keywords",
92
+ repeatable=True,
93
+ required=True,
94
+ help=(
95
+ "Keywords whose Google results to read (1-200); the domains ranking for them "
96
+ "are the competitors. Not a domain."
97
+ ),
98
+ ),
91
99
  body_field("location_name", help="Location name (e.g. 'United States')."),
92
100
  body_field("location_code", type=int, help="Numeric location code (overrides name)."),
93
101
  body_field("language_name", help="Language name (e.g. 'English')."),
@@ -121,7 +129,7 @@ SPECS = (
121
129
  body_field("location_code", type=int, help="Numeric location code (overrides name)."),
122
130
  body_field("language_name", help="Language name (e.g. 'English (United States)')."),
123
131
  body_field("language_code", help="Marketplace language code (e.g. 'en_US')."),
124
- body_field("depth", type=int, help="Number of reviews to fetch (1-100). Defaults to 10."),
132
+ body_field("depth", type=int, help="Number of reviews to fetch (1-50). Defaults to 10."),
125
133
  ),
126
134
  ),
127
135
  Cmd(
@@ -8,6 +8,7 @@ for `sellerclaw_describe` only for a command not shown here, or when a call erro
8
8
  ```text
9
9
  sellerclaw_run(group="channels", command="list") # store ids
10
10
  sellerclaw_run(group="listings", command="search", flags={"q": "wireless mouse"}) # find across all stores
11
+ sellerclaw_run(group="listings", command="search", flags={"sale_state": ["out_of_stock", "not_selling"]}) # what shoppers cannot buy
11
12
  sellerclaw_run(group="shopify-listings", command="summary", positionals={"store_id": STORE_ID})
12
13
  ```
13
14
 
@@ -36,7 +36,7 @@ Creating the fulfillment tells the *channel* and the buyer. The SellerClaw order
36
36
  separately — do it right after, or the order stays in the seller's open work:
37
37
 
38
38
  ```text
39
- # ORDER_ID here is the internal SellerClaw order id, not the channel's.
39
+ # ORDER_ID: the SellerClaw order id, or the order number the owner quotes (#1001).
40
40
  sellerclaw_run(group="orders", command="set-shipped", positionals={"order_id": ORDER_ID},
41
41
  body={"tracking_number": "1Z999AA10123456784", "tracking_carrier": "UPS"})
42
42
  ```
@@ -13,7 +13,7 @@ sellerclaw_run(group="research-trends", command="related-queries", flags={"keywo
13
13
 
14
14
  # SEO / keyword research (body-driven)
15
15
  sellerclaw_run(group="research-seo", command="keyword-ideas", body={"keyword": "wireless mouse"})
16
- sellerclaw_run(group="research-seo", command="serp-competitors", body={"keyword": "wireless mouse"})
16
+ sellerclaw_run(group="research-seo", command="serp-competitors", body={"keywords": ["wireless mouse", "bluetooth mouse"]})
17
17
 
18
18
  # What the marketplace itself already lists (query, gtin, or a competitor's storefront)
19
19
  sellerclaw_run(group="research-catalog", command="ebay-search",
@@ -40,7 +40,9 @@ sellerclaw_run(group="web", command="scrape", flags={"url": "https://rival.examp
40
40
 
41
41
  ## A listing on a marketplace is data, not a page
42
42
 
43
- `listing-get` returns a rival's price, stock, seller, condition, rating and specifications as fields.
43
+ `listing-get` returns a rival's price, stock, seller, condition, rating and specifications as fields,
44
+ plus the seller's own selling points (`features`) and `description` where the storefront publishes
45
+ them — most publish no specifications, so capacity, materials and what is in the box are read there.
44
46
  Scraping the same page returns markdown a price still has to be guessed out of — and Amazon, Etsy and
45
47
  eBay answer plain fetches with a bot wall often enough that the guess is frequently of nothing. Reach
46
48
  for `web scrape` only for storefronts this does not cover.