thoughtleaders-cli 0.6.1__tar.gz → 0.6.2__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 (68) hide show
  1. {thoughtleaders_cli-0.6.1 → thoughtleaders_cli-0.6.2}/.claude-plugin/plugin.json +1 -1
  2. {thoughtleaders_cli-0.6.1 → thoughtleaders_cli-0.6.2}/PKG-INFO +7 -4
  3. {thoughtleaders_cli-0.6.1 → thoughtleaders_cli-0.6.2}/README.md +6 -3
  4. {thoughtleaders_cli-0.6.1 → thoughtleaders_cli-0.6.2}/commands/tl.md +2 -2
  5. {thoughtleaders_cli-0.6.1 → thoughtleaders_cli-0.6.2}/docs/architecture.md +4 -4
  6. {thoughtleaders_cli-0.6.1 → thoughtleaders_cli-0.6.2}/pyproject.toml +1 -1
  7. {thoughtleaders_cli-0.6.1 → thoughtleaders_cli-0.6.2}/skills/tl/SKILL.md +4 -5
  8. {thoughtleaders_cli-0.6.1 → thoughtleaders_cli-0.6.2}/src/tl_cli/__init__.py +1 -1
  9. {thoughtleaders_cli-0.6.1 → thoughtleaders_cli-0.6.2}/src/tl_cli/commands/channels.py +1 -45
  10. {thoughtleaders_cli-0.6.1 → thoughtleaders_cli-0.6.2}/.claude-plugin/marketplace.json +0 -0
  11. {thoughtleaders_cli-0.6.1 → thoughtleaders_cli-0.6.2}/.github/workflows/python-publish.yml +0 -0
  12. {thoughtleaders_cli-0.6.1 → thoughtleaders_cli-0.6.2}/.gitignore +0 -0
  13. {thoughtleaders_cli-0.6.1 → thoughtleaders_cli-0.6.2}/AGENTS.md +0 -0
  14. {thoughtleaders_cli-0.6.1 → thoughtleaders_cli-0.6.2}/CLAUDE.md +0 -0
  15. {thoughtleaders_cli-0.6.1 → thoughtleaders_cli-0.6.2}/LICENSE +0 -0
  16. {thoughtleaders_cli-0.6.1 → thoughtleaders_cli-0.6.2}/agents/tl-analyst.md +0 -0
  17. {thoughtleaders_cli-0.6.1 → thoughtleaders_cli-0.6.2}/commands/tl-balance.md +0 -0
  18. {thoughtleaders_cli-0.6.1 → thoughtleaders_cli-0.6.2}/commands/tl-reports.md +0 -0
  19. {thoughtleaders_cli-0.6.1 → thoughtleaders_cli-0.6.2}/commands/tl-sponsorships.md +0 -0
  20. {thoughtleaders_cli-0.6.1 → thoughtleaders_cli-0.6.2}/hooks/hooks.json +0 -0
  21. {thoughtleaders_cli-0.6.1 → thoughtleaders_cli-0.6.2}/hooks/scripts/post-usage.sh +0 -0
  22. {thoughtleaders_cli-0.6.1 → thoughtleaders_cli-0.6.2}/hooks/scripts/pre-check.sh +0 -0
  23. {thoughtleaders_cli-0.6.1 → thoughtleaders_cli-0.6.2}/skills/tl/references/business-glossary.md +0 -0
  24. {thoughtleaders_cli-0.6.1 → thoughtleaders_cli-0.6.2}/skills/tl/references/elasticsearch-schema.md +0 -0
  25. {thoughtleaders_cli-0.6.1 → thoughtleaders_cli-0.6.2}/skills/tl/references/firebolt-schema.md +0 -0
  26. {thoughtleaders_cli-0.6.1 → thoughtleaders_cli-0.6.2}/skills/tl/references/postgres-schema.md +0 -0
  27. {thoughtleaders_cli-0.6.1 → thoughtleaders_cli-0.6.2}/src/tl_cli/_completions.py +0 -0
  28. {thoughtleaders_cli-0.6.1 → thoughtleaders_cli-0.6.2}/src/tl_cli/auth/__init__.py +0 -0
  29. {thoughtleaders_cli-0.6.1 → thoughtleaders_cli-0.6.2}/src/tl_cli/auth/commands.py +0 -0
  30. {thoughtleaders_cli-0.6.1 → thoughtleaders_cli-0.6.2}/src/tl_cli/auth/login.py +0 -0
  31. {thoughtleaders_cli-0.6.1 → thoughtleaders_cli-0.6.2}/src/tl_cli/auth/pkce.py +0 -0
  32. {thoughtleaders_cli-0.6.1 → thoughtleaders_cli-0.6.2}/src/tl_cli/auth/token_store.py +0 -0
  33. {thoughtleaders_cli-0.6.1 → thoughtleaders_cli-0.6.2}/src/tl_cli/client/__init__.py +0 -0
  34. {thoughtleaders_cli-0.6.1 → thoughtleaders_cli-0.6.2}/src/tl_cli/client/errors.py +0 -0
  35. {thoughtleaders_cli-0.6.1 → thoughtleaders_cli-0.6.2}/src/tl_cli/client/http.py +0 -0
  36. {thoughtleaders_cli-0.6.1 → thoughtleaders_cli-0.6.2}/src/tl_cli/commands/__init__.py +0 -0
  37. {thoughtleaders_cli-0.6.1 → thoughtleaders_cli-0.6.2}/src/tl_cli/commands/ask.py +0 -0
  38. {thoughtleaders_cli-0.6.1 → thoughtleaders_cli-0.6.2}/src/tl_cli/commands/balance.py +0 -0
  39. {thoughtleaders_cli-0.6.1 → thoughtleaders_cli-0.6.2}/src/tl_cli/commands/brands.py +0 -0
  40. {thoughtleaders_cli-0.6.1 → thoughtleaders_cli-0.6.2}/src/tl_cli/commands/changelog.py +0 -0
  41. {thoughtleaders_cli-0.6.1 → thoughtleaders_cli-0.6.2}/src/tl_cli/commands/comments.py +0 -0
  42. {thoughtleaders_cli-0.6.1 → thoughtleaders_cli-0.6.2}/src/tl_cli/commands/db.py +0 -0
  43. {thoughtleaders_cli-0.6.1 → thoughtleaders_cli-0.6.2}/src/tl_cli/commands/deals.py +0 -0
  44. {thoughtleaders_cli-0.6.1 → thoughtleaders_cli-0.6.2}/src/tl_cli/commands/describe.py +0 -0
  45. {thoughtleaders_cli-0.6.1 → thoughtleaders_cli-0.6.2}/src/tl_cli/commands/doctor.py +0 -0
  46. {thoughtleaders_cli-0.6.1 → thoughtleaders_cli-0.6.2}/src/tl_cli/commands/matches.py +0 -0
  47. {thoughtleaders_cli-0.6.1 → thoughtleaders_cli-0.6.2}/src/tl_cli/commands/proposals.py +0 -0
  48. {thoughtleaders_cli-0.6.1 → thoughtleaders_cli-0.6.2}/src/tl_cli/commands/recommender.py +0 -0
  49. {thoughtleaders_cli-0.6.1 → thoughtleaders_cli-0.6.2}/src/tl_cli/commands/reports.py +0 -0
  50. {thoughtleaders_cli-0.6.1 → thoughtleaders_cli-0.6.2}/src/tl_cli/commands/schema.py +0 -0
  51. {thoughtleaders_cli-0.6.1 → thoughtleaders_cli-0.6.2}/src/tl_cli/commands/setup.py +0 -0
  52. {thoughtleaders_cli-0.6.1 → thoughtleaders_cli-0.6.2}/src/tl_cli/commands/snapshots.py +0 -0
  53. {thoughtleaders_cli-0.6.1 → thoughtleaders_cli-0.6.2}/src/tl_cli/commands/sponsorships.py +0 -0
  54. {thoughtleaders_cli-0.6.1 → thoughtleaders_cli-0.6.2}/src/tl_cli/commands/uploads.py +0 -0
  55. {thoughtleaders_cli-0.6.1 → thoughtleaders_cli-0.6.2}/src/tl_cli/commands/whoami.py +0 -0
  56. {thoughtleaders_cli-0.6.1 → thoughtleaders_cli-0.6.2}/src/tl_cli/config.py +0 -0
  57. {thoughtleaders_cli-0.6.1 → thoughtleaders_cli-0.6.2}/src/tl_cli/filters.py +0 -0
  58. {thoughtleaders_cli-0.6.1 → thoughtleaders_cli-0.6.2}/src/tl_cli/hints.py +0 -0
  59. {thoughtleaders_cli-0.6.1 → thoughtleaders_cli-0.6.2}/src/tl_cli/main.py +0 -0
  60. {thoughtleaders_cli-0.6.1 → thoughtleaders_cli-0.6.2}/src/tl_cli/output/__init__.py +0 -0
  61. {thoughtleaders_cli-0.6.1 → thoughtleaders_cli-0.6.2}/src/tl_cli/output/formatter.py +0 -0
  62. {thoughtleaders_cli-0.6.1 → thoughtleaders_cli-0.6.2}/src/tl_cli/self_update.py +0 -0
  63. {thoughtleaders_cli-0.6.1 → thoughtleaders_cli-0.6.2}/tests/__init__.py +0 -0
  64. {thoughtleaders_cli-0.6.1 → thoughtleaders_cli-0.6.2}/tests/test_auth.py +0 -0
  65. {thoughtleaders_cli-0.6.1 → thoughtleaders_cli-0.6.2}/tests/test_filters.py +0 -0
  66. {thoughtleaders_cli-0.6.1 → thoughtleaders_cli-0.6.2}/tests/test_output.py +0 -0
  67. {thoughtleaders_cli-0.6.1 → thoughtleaders_cli-0.6.2}/tests/test_sponsorships.py +0 -0
  68. {thoughtleaders_cli-0.6.1 → thoughtleaders_cli-0.6.2}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tl-cli",
3
- "version": "0.6.1",
3
+ "version": "0.6.2",
4
4
  "description": "ThoughtLeaders CLI — query sponsorship deals, channels, brands, uploads, and intelligence from the terminal",
5
5
  "author": {
6
6
  "name": "ThoughtLeaders",
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: thoughtleaders-cli
3
- Version: 0.6.1
3
+ Version: 0.6.2
4
4
  Summary: ThoughtLeaders CLI — query sponsorship data, channels, brands, and intelligence
5
5
  Project-URL: Homepage, https://thoughtleaders.io
6
6
  Project-URL: Repository, https://github.com/ThoughtLeaders-io/thoughtleaders-cli
@@ -83,14 +83,17 @@ tl uploads show 1174310:0BehkmVa7ak
83
83
 
84
84
  # Search channels via raw SQL — `tl db pg` against thoughtleaders_channel
85
85
  # (run `tl schema pg` once to confirm the live column set).
86
+ # NOTE: For topic / category discovery, prefer the vector recommender over
87
+ # `content_category` equality — `tl recommender top-channels "<tag>"`
88
+ # returns channels ranked by how strongly they load on the topic, not just
89
+ # rows where the single category code matches exactly.
86
90
  tl db pg "SELECT id, channel_name, total_views FROM thoughtleaders_channel
87
91
  WHERE content_category = <COOKING_CODE> AND total_views >= 100000
88
92
  ORDER BY total_views DESC LIMIT 50 OFFSET 0"
89
93
  tl db pg "SELECT id, channel_name FROM thoughtleaders_channel
90
94
  WHERE is_tl_channel = TRUE LIMIT 200 OFFSET 0" # all TPP channels (~169)
91
- # MSN status (media_selling_network_join_date) is scrubbed from the
92
- # advertiser sandbox view for MSN-only / non-MSN lookups, the
93
- # structured filter is the right tool: `tl channels list msn:yes|no`.
95
+ # MSN status: filter on `media_selling_network_join_date IS [NOT] NULL`
96
+ # in the same raw SQL query (column is scrubbed from advertiser sandboxes).
94
97
 
95
98
  # Show channel detail — accepts numeric ID or channel name.
96
99
  # Names that match more than one active channel print a candidate list
@@ -56,14 +56,17 @@ tl uploads show 1174310:0BehkmVa7ak
56
56
 
57
57
  # Search channels via raw SQL — `tl db pg` against thoughtleaders_channel
58
58
  # (run `tl schema pg` once to confirm the live column set).
59
+ # NOTE: For topic / category discovery, prefer the vector recommender over
60
+ # `content_category` equality — `tl recommender top-channels "<tag>"`
61
+ # returns channels ranked by how strongly they load on the topic, not just
62
+ # rows where the single category code matches exactly.
59
63
  tl db pg "SELECT id, channel_name, total_views FROM thoughtleaders_channel
60
64
  WHERE content_category = <COOKING_CODE> AND total_views >= 100000
61
65
  ORDER BY total_views DESC LIMIT 50 OFFSET 0"
62
66
  tl db pg "SELECT id, channel_name FROM thoughtleaders_channel
63
67
  WHERE is_tl_channel = TRUE LIMIT 200 OFFSET 0" # all TPP channels (~169)
64
- # MSN status (media_selling_network_join_date) is scrubbed from the
65
- # advertiser sandbox view for MSN-only / non-MSN lookups, the
66
- # structured filter is the right tool: `tl channels list msn:yes|no`.
68
+ # MSN status: filter on `media_selling_network_join_date IS [NOT] NULL`
69
+ # in the same raw SQL query (column is scrubbed from advertiser sandboxes).
67
70
 
68
71
  # Show channel detail — accepts numeric ID or channel name.
69
72
  # Names that match more than one active channel print a candidate list
@@ -18,8 +18,8 @@ The user wants to query ThoughtLeaders data. Translate their request into the ri
18
18
  ## Examples
19
19
 
20
20
  - "/tl sold sponsorships for Nike in Q1" → `tl sponsorships list status:sold brand:"Nike" purchase-date-start:2026-01-01 purchase-date-end:2026-03-31`
21
- - "/tl cooking channels over 100k subs" → `tl db pg "SELECT id, channel_name, total_views FROM thoughtleaders_channel WHERE content_category = <COOKING_CODE> AND total_views >= 100000 ORDER BY total_views DESC LIMIT 50 OFFSET 0"`
22
- - "/tl mobile-first US cooking channels" → `tl db pg "SELECT id, channel_name, demographic_usa_share FROM thoughtleaders_channel WHERE content_category = <COOKING_CODE> AND demographic_device_primary = 'mobile' AND demographic_usa_share >= 50 ORDER BY total_views DESC LIMIT 50 OFFSET 0"`
21
+ - "/tl cooking channels over 100k subs" → `tl recommender top-channels "cooking" --limit 50` (then post-filter by `subscribers >= 100000` on the resulting IDs)
22
+ - "/tl mobile-first US cooking channels" → `tl recommender top-channels "cooking" --limit 100` (then narrow by `demographic_device_primary = 'mobile'` / `demographic_usa_share >= 50` with raw SQL on the resulting IDs)
23
23
  - "/tl Nike's sponsorship activity" → `tl brands show Nike`
24
24
  - "/tl run my Q1 report" → `tl reports --json` then `tl reports run <id>`
25
25
  - "/tl check my balance" → `tl balance`
@@ -48,7 +48,6 @@ All data commands use explicit subcommands: `list`, `show`, `create`/`add`. Runn
48
48
  | `tl proposals create --channel <id> --brand <id>` | Create a proposal (free) |
49
49
  | `tl uploads list [filters...]` | List video uploads (ES) |
50
50
  | `tl uploads show <id> [<id>...]` | Show upload detail(s) by ID |
51
- | `tl channels list [filters...]` | Search channels. Responses carry boolean `msn` (Media Selling Network) and `tpp` (TL-managed) fields; filterable via `msn:` / `tpp:` tri-state (`yes` / `no` / `both`, default `both`). |
52
51
  | `tl channels show <id-or-name>` | Channel detail, including active adspots with price/cost/CPM |
53
52
  | `tl channels history <id-or-name>` | Sponsorship history (videos with detected sponsors) |
54
53
  | `tl channels similar <id-or-name>` | Vector-similarity recommender. 50 credits; Intelligence plan. Tri-state `msn:` (default `yes`) and `tpp:` (default `both`) filters. Ambiguous names return 400 + candidates list. Hidden `look-alike` alias. |
@@ -75,8 +74,10 @@ Filters are passed as `key:value` pairs after `list`:
75
74
  tl sponsorships list status:sold brand:"Nike" purchase-date:2026-01
76
75
  tl sponsorships list status:pending send-date:2026-03
77
76
  tl uploads list channel:12345 type:longform since:2026-03
78
- # Channel discovery is raw SQL — the structured `tl channels list` covers
79
- # only narrow lookups. Default to:
77
+ # Channel discovery is raw SQL — default to:
78
+ # (For topic/category lookups, `tl recommender top-channels "<tag>"` is a
79
+ # better starting point than `content_category` equality — it ranks by
80
+ # how strongly each channel loads on the topic, not exact-match.)
80
81
  tl db pg "SELECT id, channel_name, total_views FROM thoughtleaders_channel
81
82
  WHERE content_category = <COOKING_CODE> AND language = 'en'
82
83
  AND total_views >= 1000000
@@ -387,7 +388,6 @@ Most CLI endpoints can reuse existing views/utilities rather than being built fr
387
388
  | **`POST /api/cli/v1/sponsorships`** | `api/create-bulk-proposal` (`CreateBulkProposalView`) + MCP `save_proposals_for_email` in `mcp/proposals.py` | Adapt for single-proposal creation from CLI params |
388
389
  | **`GET /api/cli/v1/uploads`** | `api/articles` (`ArticlesView`) — full ES article search with configurable columns, filters, aggregation. Already handles channel format, brand filters, content type. | Translate CLI filters → ArticlesView params. Restrict to VIDEO format. |
389
390
  | **`GET /api/cli/v1/uploads/<id>`** | `api/articles/<id>` (`SingleArticleView`) | Add CLI envelope |
390
- | **`GET /api/cli/v1/channels`** | `api/v2/external-youtube-thoughtleaders` (`ExternalYoutubeThoughtleadersView`) — rich ES-powered channel search with configurable columns, aggregation. Also `api/v1/channels/dropdown` for simple name search. | Translate CLI filters → existing view params |
391
391
  | **`GET /api/cli/v1/channels/<id>`** | `api/v1/channels/<id>` (`ChannelAPIViewSet.retrieve`) — returns channel detail with loyalty metrics | Add CLI envelope + breadcrumbs linking to snapshots |
392
392
  | **`GET /api/cli/v1/brands/<query>`** | `api/v1/brands` (`BrandsViewSet`) for brand lookup + `api/articles` with `sponsored_brand_mentions` filter for intelligence data. Also `api/brand/<id>/matcher` (`BrandChannelMatcherAPI`) for channel discovery. | Combine brand lookup + ES brand mention query |
393
393
  | **`GET /api/cli/v1/snapshots/channel/<id>`** | `api/v2/channel-history` (`ChannelHistoryView`) — already queries Firebolt `channel_metrics` with pagination, uses `get_firebolt_query_results()` utility. Also `api/v2/external-channel-total-views` for time-series with granularity. | Direct reuse — just translate params + add CLI envelope |
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "thoughtleaders-cli"
7
- version = "0.6.1"
7
+ version = "0.6.2"
8
8
  description = "ThoughtLeaders CLI — query sponsorship data, channels, brands, and intelligence"
9
9
  readme = "README.md"
10
10
  license = "MIT"
@@ -130,8 +130,7 @@ tl proposals show <id> # Proposal detail (2 credits)
130
130
  tl proposals create --channel <id> --brand <id> # Create proposal (free)
131
131
  tl uploads list [filters...] # Video uploads from ES — list curve, mult 1.0
132
132
  tl uploads show <id> # Upload detail (2 credits)
133
- tl channels list [filters...] # Channel searchlist curve, mult 1.0
134
- tl channels show <id-or-name> # Channel detail (2 credits; accepts numeric ID or name)
133
+ tl channels show <id-or-name> # Channel detail (2 credits; accepts numeric ID or name) for channel search use raw SQL on thoughtleaders_channel
135
134
  tl channels history <id-or-name> # Sponsorship history (5 credits/result, linear)
136
135
  tl channels similar <id-or-name> # Vector-similarity recommender (50 credits flat; Intelligence plan)
137
136
  tl brands show <id-or-name> # Brand detail (1 credit)
@@ -155,7 +154,7 @@ tl comments add <adlink-id> "msg" # Add comment (free)
155
154
 
156
155
  **"List curve"** above means non-linear pricing: `cost = 1 + mult × 0.126 × n^1.2`. The flat 1-credit setup applies to every list call; the `mult` reflects per-resource complexity. `tl db {pg,fb,es}` shares the same curve at mult=1.4. Concrete totals:
157
156
 
158
- | Rows | mult=1.0 (channels, brands, comments, uploads, sponsorships) | mult=1.2 (snapshots) | mult=1.3 (reports) | mult=1.4 (db.pg / db.fb / db.es) |
157
+ | Rows | mult=1.0 (comments, uploads, sponsorships) | mult=1.2 (snapshots) | mult=1.3 (reports) | mult=1.4 (db.pg / db.fb / db.es) |
159
158
  |---:|---:|---:|---:|---:|
160
159
  | 1 | 1 | 1 | 1 | 1 |
161
160
  | 10 | 3 | 3 | 4 | 4 |
@@ -360,7 +359,7 @@ tl db pg "SELECT id, channel_name, demographic_device_primary, total_views
360
359
 
361
360
  For per-country share beyond the recommender's "USA share" tag, use the `demographic_geo` jsonb in raw SQL: `(demographic_geo->>'gb')::int >= 25`. Same pattern with `demographic_device->>'mobile'` for non-primary device shares.
362
361
 
363
- **MSN status (`media_selling_network_join_date`) is scrubbed from the advertiser sandbox view.** Raw SQL can't filter on it from an advertiser context; use the structured `tl channels list msn:yes|no|both` for MSN-specific lookups, then drop down to SQL on the resulting IDs (`WHERE id IN (...)`) for further analysis.
362
+ **MSN status (`media_selling_network_join_date`) is scrubbed from the advertiser sandbox view.** Raw SQL can't filter on it from an advertiser context. For MSN-only / non-MSN lookups, run the same raw SQL with `media_selling_network_join_date IS [NOT] NULL` from a context that has access to it (full-access role), or rely on the recommender's MSN-aware filters: `tl recommender top-channels "<tag>" msn:yes|no|all`.
364
363
 
365
364
  ### Output flags
366
365
  - `--json` — structured JSON (use this for parsing)
@@ -398,7 +397,7 @@ Every query costs credits. Before running expensive queries:
398
397
  Users only see data their plan allows:
399
398
  - **Media buyers** see deals where their org is the brand. They see `price` but never `cost`.
400
399
  - **Media sellers** see deals where their org is the publisher. They see `cost` but never `price`.
401
- - **Intelligence plan** required for `tl brands`, full `tl channels list` search, and full `tl uploads list`.
400
+ - **Intelligence plan** required for `tl brands`, the full `tl recommender` surface, and full `tl uploads list`.
402
401
  - **Paid plan** required for `tl snapshots`.
403
402
 
404
403
  ## Important: Status Labels
@@ -1,3 +1,3 @@
1
1
  """ThoughtLeaders CLI — query sponsorship data, channels, brands, and intelligence."""
2
2
 
3
- __version__ = "0.6.1"
3
+ __version__ = "0.6.2"
@@ -11,7 +11,7 @@ from tl_cli.filters import parse_filters
11
11
  from tl_cli.hints import detail_hint
12
12
  from tl_cli.output.formatter import detect_format, output, output_single
13
13
 
14
- app = typer.Typer(help="YouTube channels (search, detail, and similar-channel recommendations)")
14
+ app = typer.Typer(help="YouTube channels (detail, history, and similar-channel recommendations)")
15
15
 
16
16
  # Columns for the `similar` endpoint result table. The server enriches every
17
17
  # row so the user can size up each suggestion without follow-up queries.
@@ -25,50 +25,6 @@ SIMILAR_COLUMN_CONFIG = {
25
25
  }
26
26
 
27
27
 
28
- @app.callback(invoke_without_command=True)
29
- def channels(ctx: typer.Context) -> None:
30
- """YouTube channels — search and detail."""
31
- if ctx.invoked_subcommand is None:
32
- ctx.invoke(list_cmd, args=[], json_output=False, csv_output=False, md_output=False, limit=50, offset=0)
33
-
34
-
35
- @app.command("list")
36
- def list_cmd(
37
- args: list[str] = typer.Argument(None, help="Filters (key:value pairs). Run 'tl describe show channels' for available filters."),
38
- json_output: bool = typer.Option(False, "--json", help="JSON output"),
39
- csv_output: bool = typer.Option(False, "--csv", help="CSV output"),
40
- md_output: bool = typer.Option(False, "--md", help="Markdown output"),
41
- toon_output: bool = typer.Option(False, "--toon", help="TOON output (token-efficient for LLMs)"),
42
- limit: int = typer.Option(50, "--limit", "-l", help="Max results"),
43
- offset: int = typer.Option(0, "--offset", help="Pagination offset"),
44
- ) -> None:
45
- """Search channels with optional filters.
46
-
47
- Examples:
48
- tl channels list # List channels
49
- tl channels list category:cooking min-subs:100k # Search with filters
50
- """
51
- fmt = detect_format(json_output, csv_output, md_output, toon_output)
52
- filters = parse_filters(args or [])
53
-
54
- client = get_client()
55
- try:
56
- params = {**filters, "limit": str(limit), "offset": str(offset)}
57
- data = client.get("/channels", params=params)
58
- for r in data.get("results", []):
59
- r["channel_id"] = r.pop("id", None)
60
- output(
61
- data,
62
- fmt,
63
- columns=["channel_id", "name", "url", "msn", "tpp", "subscribers", "gender", "countries", "category", "sponsorship_score", "trend"],
64
- title="Channels",
65
- )
66
- except ApiError as e:
67
- handle_api_error(e)
68
- finally:
69
- client.close()
70
-
71
-
72
28
  @app.command("show")
73
29
  def show_cmd(
74
30
  channel_ref: str = typer.Argument(..., help="Channel ID (numeric) or name (partial match, must be unique)"),