thoughtleaders-cli 0.6.7__tar.gz → 0.6.9__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 (90) hide show
  1. {thoughtleaders_cli-0.6.7 → thoughtleaders_cli-0.6.9}/.claude-plugin/plugin.json +1 -1
  2. {thoughtleaders_cli-0.6.7 → thoughtleaders_cli-0.6.9}/PKG-INFO +3 -3
  3. {thoughtleaders_cli-0.6.7 → thoughtleaders_cli-0.6.9}/pyproject.toml +10 -3
  4. {thoughtleaders_cli-0.6.7 → thoughtleaders_cli-0.6.9}/skills/tl/SKILL.md +40 -12
  5. thoughtleaders_cli-0.6.9/skills/tl-report-builder/SKILL.md +1397 -0
  6. thoughtleaders_cli-0.6.9/skills/tl-report-builder/examples/e2e_findings.md +264 -0
  7. thoughtleaders_cli-0.6.9/skills/tl-report-builder/examples/golden_queries.md +150 -0
  8. thoughtleaders_cli-0.6.9/skills/tl-report-builder/references/columns_brands.md +106 -0
  9. thoughtleaders_cli-0.6.9/skills/tl-report-builder/references/columns_channels.md +131 -0
  10. thoughtleaders_cli-0.6.9/skills/tl-report-builder/references/columns_content.md +98 -0
  11. thoughtleaders_cli-0.6.9/skills/tl-report-builder/references/columns_sponsorships.md +116 -0
  12. thoughtleaders_cli-0.6.9/skills/tl-report-builder/references/intelligence_filterset_schema.json +392 -0
  13. thoughtleaders_cli-0.6.9/skills/tl-report-builder/references/intelligence_widget_schema.json +194 -0
  14. thoughtleaders_cli-0.6.9/skills/tl-report-builder/references/report_glossary.md +181 -0
  15. thoughtleaders_cli-0.6.9/skills/tl-report-builder/references/sortable_columns.json +64 -0
  16. thoughtleaders_cli-0.6.9/skills/tl-report-builder/references/sponsorship_filterset_schema.json +217 -0
  17. thoughtleaders_cli-0.6.9/skills/tl-report-builder/references/sponsorship_widget_schema.json +165 -0
  18. thoughtleaders_cli-0.6.9/skills/tl-report-builder/references/widgets.md +252 -0
  19. thoughtleaders_cli-0.6.9/skills/tl-report-builder/tools/column_builder.md +384 -0
  20. thoughtleaders_cli-0.6.9/skills/tl-report-builder/tools/database_query.md +210 -0
  21. thoughtleaders_cli-0.6.9/skills/tl-report-builder/tools/keyword_research.md +217 -0
  22. thoughtleaders_cli-0.6.9/skills/tl-report-builder/tools/name_resolver.md +162 -0
  23. thoughtleaders_cli-0.6.9/skills/tl-report-builder/tools/sample_judge.md +223 -0
  24. thoughtleaders_cli-0.6.9/skills/tl-report-builder/tools/similar_channels.md +61 -0
  25. thoughtleaders_cli-0.6.9/skills/tl-report-builder/tools/topic_matcher.md +269 -0
  26. thoughtleaders_cli-0.6.9/skills/tl-report-builder/tools/widget_builder.md +278 -0
  27. {thoughtleaders_cli-0.6.7 → thoughtleaders_cli-0.6.9}/src/tl_cli/__init__.py +1 -1
  28. {thoughtleaders_cli-0.6.7 → thoughtleaders_cli-0.6.9}/src/tl_cli/commands/channels.py +38 -0
  29. {thoughtleaders_cli-0.6.7 → thoughtleaders_cli-0.6.9}/src/tl_cli/commands/sponsorships.py +38 -0
  30. {thoughtleaders_cli-0.6.7 → thoughtleaders_cli-0.6.9}/.claude-plugin/marketplace.json +0 -0
  31. {thoughtleaders_cli-0.6.7 → thoughtleaders_cli-0.6.9}/.github/workflows/python-publish.yml +0 -0
  32. {thoughtleaders_cli-0.6.7 → thoughtleaders_cli-0.6.9}/.gitignore +0 -0
  33. {thoughtleaders_cli-0.6.7 → thoughtleaders_cli-0.6.9}/AGENTS.md +0 -0
  34. {thoughtleaders_cli-0.6.7 → thoughtleaders_cli-0.6.9}/CLAUDE.md +0 -0
  35. {thoughtleaders_cli-0.6.7 → thoughtleaders_cli-0.6.9}/LICENSE +0 -0
  36. {thoughtleaders_cli-0.6.7 → thoughtleaders_cli-0.6.9}/README.md +0 -0
  37. {thoughtleaders_cli-0.6.7 → thoughtleaders_cli-0.6.9}/agents/tl-analyst.md +0 -0
  38. {thoughtleaders_cli-0.6.7 → thoughtleaders_cli-0.6.9}/commands/tl-balance.md +0 -0
  39. {thoughtleaders_cli-0.6.7 → thoughtleaders_cli-0.6.9}/commands/tl-reports.md +0 -0
  40. {thoughtleaders_cli-0.6.7 → thoughtleaders_cli-0.6.9}/commands/tl-sponsorships.md +0 -0
  41. {thoughtleaders_cli-0.6.7 → thoughtleaders_cli-0.6.9}/commands/tl.md +0 -0
  42. {thoughtleaders_cli-0.6.7 → thoughtleaders_cli-0.6.9}/docs/architecture.md +0 -0
  43. {thoughtleaders_cli-0.6.7 → thoughtleaders_cli-0.6.9}/hooks/hooks.json +0 -0
  44. {thoughtleaders_cli-0.6.7 → thoughtleaders_cli-0.6.9}/hooks/scripts/post-usage.sh +0 -0
  45. {thoughtleaders_cli-0.6.7 → thoughtleaders_cli-0.6.9}/hooks/scripts/pre-check.sh +0 -0
  46. {thoughtleaders_cli-0.6.7 → thoughtleaders_cli-0.6.9}/skills/tl/references/business-glossary.md +0 -0
  47. {thoughtleaders_cli-0.6.7 → thoughtleaders_cli-0.6.9}/skills/tl/references/elasticsearch-schema.md +0 -0
  48. {thoughtleaders_cli-0.6.7 → thoughtleaders_cli-0.6.9}/skills/tl/references/firebolt-schema.md +0 -0
  49. {thoughtleaders_cli-0.6.7 → thoughtleaders_cli-0.6.9}/skills/tl/references/postgres-schema.md +0 -0
  50. {thoughtleaders_cli-0.6.7 → thoughtleaders_cli-0.6.9}/src/tl_cli/_completions.py +0 -0
  51. {thoughtleaders_cli-0.6.7 → thoughtleaders_cli-0.6.9}/src/tl_cli/auth/__init__.py +0 -0
  52. {thoughtleaders_cli-0.6.7 → thoughtleaders_cli-0.6.9}/src/tl_cli/auth/commands.py +0 -0
  53. {thoughtleaders_cli-0.6.7 → thoughtleaders_cli-0.6.9}/src/tl_cli/auth/login.py +0 -0
  54. {thoughtleaders_cli-0.6.7 → thoughtleaders_cli-0.6.9}/src/tl_cli/auth/pkce.py +0 -0
  55. {thoughtleaders_cli-0.6.7 → thoughtleaders_cli-0.6.9}/src/tl_cli/auth/token_store.py +0 -0
  56. {thoughtleaders_cli-0.6.7 → thoughtleaders_cli-0.6.9}/src/tl_cli/client/__init__.py +0 -0
  57. {thoughtleaders_cli-0.6.7 → thoughtleaders_cli-0.6.9}/src/tl_cli/client/errors.py +0 -0
  58. {thoughtleaders_cli-0.6.7 → thoughtleaders_cli-0.6.9}/src/tl_cli/client/http.py +0 -0
  59. {thoughtleaders_cli-0.6.7 → thoughtleaders_cli-0.6.9}/src/tl_cli/commands/__init__.py +0 -0
  60. {thoughtleaders_cli-0.6.7 → thoughtleaders_cli-0.6.9}/src/tl_cli/commands/ask.py +0 -0
  61. {thoughtleaders_cli-0.6.7 → thoughtleaders_cli-0.6.9}/src/tl_cli/commands/balance.py +0 -0
  62. {thoughtleaders_cli-0.6.7 → thoughtleaders_cli-0.6.9}/src/tl_cli/commands/brands.py +0 -0
  63. {thoughtleaders_cli-0.6.7 → thoughtleaders_cli-0.6.9}/src/tl_cli/commands/changelog.py +0 -0
  64. {thoughtleaders_cli-0.6.7 → thoughtleaders_cli-0.6.9}/src/tl_cli/commands/comments.py +0 -0
  65. {thoughtleaders_cli-0.6.7 → thoughtleaders_cli-0.6.9}/src/tl_cli/commands/db.py +0 -0
  66. {thoughtleaders_cli-0.6.7 → thoughtleaders_cli-0.6.9}/src/tl_cli/commands/deals.py +0 -0
  67. {thoughtleaders_cli-0.6.7 → thoughtleaders_cli-0.6.9}/src/tl_cli/commands/describe.py +0 -0
  68. {thoughtleaders_cli-0.6.7 → thoughtleaders_cli-0.6.9}/src/tl_cli/commands/doctor.py +0 -0
  69. {thoughtleaders_cli-0.6.7 → thoughtleaders_cli-0.6.9}/src/tl_cli/commands/matches.py +0 -0
  70. {thoughtleaders_cli-0.6.7 → thoughtleaders_cli-0.6.9}/src/tl_cli/commands/proposals.py +0 -0
  71. {thoughtleaders_cli-0.6.7 → thoughtleaders_cli-0.6.9}/src/tl_cli/commands/recommender.py +0 -0
  72. {thoughtleaders_cli-0.6.7 → thoughtleaders_cli-0.6.9}/src/tl_cli/commands/reports.py +0 -0
  73. {thoughtleaders_cli-0.6.7 → thoughtleaders_cli-0.6.9}/src/tl_cli/commands/schema.py +0 -0
  74. {thoughtleaders_cli-0.6.7 → thoughtleaders_cli-0.6.9}/src/tl_cli/commands/setup.py +0 -0
  75. {thoughtleaders_cli-0.6.7 → thoughtleaders_cli-0.6.9}/src/tl_cli/commands/snapshots.py +0 -0
  76. {thoughtleaders_cli-0.6.7 → thoughtleaders_cli-0.6.9}/src/tl_cli/commands/uploads.py +0 -0
  77. {thoughtleaders_cli-0.6.7 → thoughtleaders_cli-0.6.9}/src/tl_cli/commands/whoami.py +0 -0
  78. {thoughtleaders_cli-0.6.7 → thoughtleaders_cli-0.6.9}/src/tl_cli/config.py +0 -0
  79. {thoughtleaders_cli-0.6.7 → thoughtleaders_cli-0.6.9}/src/tl_cli/filters.py +0 -0
  80. {thoughtleaders_cli-0.6.7 → thoughtleaders_cli-0.6.9}/src/tl_cli/hints.py +0 -0
  81. {thoughtleaders_cli-0.6.7 → thoughtleaders_cli-0.6.9}/src/tl_cli/main.py +0 -0
  82. {thoughtleaders_cli-0.6.7 → thoughtleaders_cli-0.6.9}/src/tl_cli/output/__init__.py +0 -0
  83. {thoughtleaders_cli-0.6.7 → thoughtleaders_cli-0.6.9}/src/tl_cli/output/formatter.py +0 -0
  84. {thoughtleaders_cli-0.6.7 → thoughtleaders_cli-0.6.9}/src/tl_cli/self_update.py +0 -0
  85. {thoughtleaders_cli-0.6.7 → thoughtleaders_cli-0.6.9}/tests/__init__.py +0 -0
  86. {thoughtleaders_cli-0.6.7 → thoughtleaders_cli-0.6.9}/tests/test_auth.py +0 -0
  87. {thoughtleaders_cli-0.6.7 → thoughtleaders_cli-0.6.9}/tests/test_filters.py +0 -0
  88. {thoughtleaders_cli-0.6.7 → thoughtleaders_cli-0.6.9}/tests/test_output.py +0 -0
  89. {thoughtleaders_cli-0.6.7 → thoughtleaders_cli-0.6.9}/tests/test_sponsorships.py +0 -0
  90. {thoughtleaders_cli-0.6.7 → thoughtleaders_cli-0.6.9}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tl-cli",
3
- "version": "0.6.7",
3
+ "version": "0.6.9",
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.7
3
+ Version: 0.6.9
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
@@ -21,8 +21,8 @@ Requires-Dist: authlib>=1.3
21
21
  Requires-Dist: httpx>=0.27
22
22
  Requires-Dist: keyring>=25.0
23
23
  Requires-Dist: rich>=13.0
24
- Requires-Dist: toon-format>=0.9.0b1
25
- Requires-Dist: typer[all]>=0.12
24
+ Requires-Dist: toon-format==0.9.0b1
25
+ Requires-Dist: typer>=0.16
26
26
  Description-Content-Type: text/markdown
27
27
 
28
28
  # tl cli
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "thoughtleaders-cli"
7
- version = "0.6.7"
7
+ version = "0.6.9"
8
8
  description = "ThoughtLeaders CLI — query sponsorship data, channels, brands, and intelligence"
9
9
  readme = "README.md"
10
10
  license = "MIT"
@@ -23,12 +23,19 @@ classifiers = [
23
23
  "Topic :: Office/Business",
24
24
  ]
25
25
  dependencies = [
26
- "typer[all]>=0.12",
26
+ # `typer` >= 0.16 made click/rich/shellingham required — the `[all]` extra
27
+ # was removed and uv warns about it. Drop the extra to silence the warning;
28
+ # behaviour is identical because the bundled deps are pulled either way.
29
+ "typer>=0.16",
27
30
  "rich>=13.0",
28
31
  "httpx>=0.27",
29
32
  "keyring>=25.0",
30
33
  "authlib>=1.3",
31
- "toon-format>=0.9.0b1",
34
+ # `toon-format` only publishes pre-releases (0.9.0b1 is the only modern
35
+ # release; 0.1.0 is the lone stable). pip/pipx accept `>=0.9.0b1` and pull
36
+ # the beta, but uv refuses pre-releases under `>=` when a stable exists.
37
+ # An exact pin is the one form uv will resolve without `--prerelease=allow`.
38
+ "toon-format==0.9.0b1",
32
39
  ]
33
40
 
34
41
  [project.scripts]
@@ -5,24 +5,17 @@ description: Query and analyze ThoughtLeaders business data using the `tl` CLI.
5
5
 
6
6
  # ThoughtLeaders Data Analyst
7
7
 
8
- You have access to the `tl` CLI which queries ThoughtLeaders' sponsorship platform data. Run it to answer questions about deals, channels, brands, uploads, metrics, and more.
8
+ Run the `tl` CLI to query ThoughtLeaders' sponsorship platform data. Use it to answer questions about deals, channels, brands, uploads, metrics, etc.
9
9
 
10
10
  ## Core Principles
11
11
 
12
- **You are the intelligence layer.** Don't use `tl ask` that's a server-side LLM fallback for users without Claude. Translate the user's question into the right `tl` invocation yourself.
12
+ **Default to raw database queries.** For anything beyond a trivially simple lookup, reach for `tl db pg|fb|es`. Avoid the structured `tl <resource>` commands (`sponsorships list`, `channels show`, `brands history`, etc.).
13
13
 
14
- **Default to raw database queries.** For anything beyond a trivially simple lookup, reach for `tl db pg|fb|es` first. The structured `tl <resource>` commands (`sponsorships list`, `channels show`, `brands history`, etc.) are convenient shorthands for single-record lookups and plain filtered lists — but the moment the question involves joins, aggregations, multi-condition filtering, or anything you'd otherwise post-process client-side, the right tool is a single raw query. One server-side aggregation beats N paginated client-side roll-ups on cost, latency, and the `from+size = 10000` ES cap.
14
+ Always run `tl schema pg|fb|es` before writing a raw query.
15
15
 
16
- Decision rule:
16
+ **When you only need the schema of one table, you MUST call `tl schema pg <table>` (or `tl schema fb <table>`) — never the unscoped form**, to reduce token counts. ES has no per-table form (the index is a single document shape) — `tl schema es` is the only call there.
17
17
 
18
- - **Trivially simple** (use a structured command): "show sponsorship 12345", "list deals for Nike", "show channel 5607", "run report 42".
19
- - **Anything else** (use `tl db pg|fb|es`): aggregations, joins, conditions the structured filters don't expose, comparing across resources, computing rates / shares / percentiles, anything that would otherwise need a paginated walk + client-side reduce.
20
-
21
- Always run `tl describe show <resource>` before using a structured command, and `tl schema pg|fb|es` before writing a raw query.
22
-
23
- **When you only need the schema of one table, you MUST call `tl schema pg <table>` (or `tl schema fb <table>`) — never the unscoped form.** The unscoped `tl schema pg` returns *every* table visible to your role, which is dozens of tables and tens of thousands of tokens; the single-table form returns only the section you need, in the same markdown layout. Reaching for the unscoped form when you already know the table name is a tokens/latency tax with zero benefit. ES has no per-table form (the index is a single document shape) — `tl schema es` is the only call there.
24
-
25
- **Process data with shell tools, not your context window.** Don't pull large result sets into your reasoning context just to filter, sort, count, or extract a field — that wastes tokens and slows you down. Pipe `tl … --json` (or `--csv`) into `jq`, `yq`, `rg`, or `duckdb` and read only the answer back. Pick the tool by shape:
18
+ **Process data with shell tools, not your context window.** Don't pull large result sets into your reasoning context just to filter, sort, count, or extract a field — that wastes tokens and slows you down. Pipe `tl … --json` (or `--csv`, or `--toon`) into `jq`, `yq`, `rg`, or `duckdb`, as appropriate, and read only the answer back. Pick the tool by shape:
26
19
 
27
20
  - **`jq`** — filter, project, and transform JSON. The default for `tl … --json` post-processing.
28
21
  ```bash
@@ -68,6 +61,13 @@ Other key concepts:
68
61
  - **Reports** — saved report configurations that can be re-run
69
62
  - **Comments** — notes attached to sponsorships
70
63
  - **Adspots** — types of ads a channel carries (e.g. mention, dedicated video, product placement). Returned by `tl channels show`; each carries price/cost.
64
+ - **Profiles** — per-organization actors that own sponsorship records on behalf of either side of a deal. A profile is buyer-side or seller-side:
65
+ - *Buyer-side (brand) profiles* — represent a sponsoring brand. Each brand profile has an M2M link to at most one `Brand` record (which are the actual advertiser identities). On a sponsorship, `creator_profile` is the buyer-side profile.
66
+ - *Seller-side (publisher) profiles* — attached to a `Publication`, which in turn owns one or more `Channel` records. A channel's adspots therefore inherit ownership through `channel.publication.profile`.
67
+ - **How to tell them apart** — three signals on the `thoughtleaders_profile` row, used in this order:
68
+ 1. **`persona`** (canonical) — `1=Brand`, `4=Media Agency`, `3=Talent Manager` are buyer-side; `2=Creator`, `5=Creator Service` are seller-side. May be null on legacy rows.
69
+ 2. **`is_advertiser` / `is_publisher`** booleans — feature flags; either or both can be true for staff-style profiles, but on normal user profiles they reliably mark side.
70
+ - Org scoping for sponsorships is profile-mediated: a sponsorship belongs to your org if **either** `creator_profile.organization` (brand side) **or** `ad_spot.channel.publication.profile.organization` (publisher side) matches yours.
71
71
  - **MSN** (Media Selling Network) — the ~11k YouTube channels that have opted in to receive sponsorship offers. A channels is in the MSN group if the `channel.media_selling_network_join_date` field is not null.
72
72
  - **MBN** (Media Buying Network) — the brand-side counterpart to MSN: brand profiles that have opted in to receive proposed sponsorships. A profile is in the MBN group if the `profile.media_buying_network_join_date` field is not null.
73
73
  - **TPP** (ThoughtLeaders Partner Program, a.k.a. "TL channels") — the smaller, exclusive ~169 channels TL manages directly. A channel is in the TPP group if the `channel.is_tl_channel` is True.
@@ -141,6 +141,7 @@ Prefer writing Python code, shell code, or `jq` commands that fetche or analysis
141
141
  tl sponsorships list [filters...] # Sponsorships — list curve, mult 1.0
142
142
  tl sponsorships show <id> # Sponsorship detail (2 credits)
143
143
  tl sponsorships create --channel <id> --brand <id> # Create proposal (free)
144
+ tl sponsorships update <id> '<json>' # Update whitelisted fields (2 credits) — only `publish_status` editable; non-full-access users can only edit sponsorships in their own org
144
145
  tl deals list [filters...] # Shortcut: agreed-upon sponsorships (status:deal); same curve as sponsorships list
145
146
  tl deals show <id> # Deal detail (2 credits)
146
147
  tl matches list [filters...] # Shortcut: possible brand-channel pairings (status:match); same curve
@@ -152,6 +153,7 @@ tl proposals create --channel <id> --brand <id> # Create proposal (free)
152
153
  tl uploads list [filters...] # Video uploads from ES — list curve, mult 1.0
153
154
  tl uploads show <id> # Upload detail (2 credits)
154
155
  tl channels show <id-or-name> # Channel detail (2 credits; accepts numeric ID or name) — for channel search use raw SQL on thoughtleaders_channel
156
+ tl channels update <id> '<json>' # Update whitelisted demographic fields (2 credits; full-access only)
155
157
  tl channels history <id-or-name> # Sponsorship history (5 credits/result, linear)
156
158
  tl channels similar <id-or-name> # Similarity recommender (25 credits flat; Intelligence plan)
157
159
  tl brands show <id-or-name> # Brand detail (1 credit)
@@ -188,6 +190,30 @@ tl comments add <adlink-id> "msg" # Add comment (free)
188
190
 
189
191
  The marginal per-row cost is exactly proportional to `mult` — a 1.4× resource costs 1.4× the row part of a 1.0× resource at any size. Splitting a 500-row pull into ten 50-row calls saves ~30% but burns 10 setup floors instead of 1; "narrow the query" is almost always the better move than "fragment the pagination."
190
192
 
193
+ ### Updating records
194
+
195
+ A narrow write surface is exposed for two resources. Each command takes the record id and a single JSON object with the fields to change; the server enforces a hard-coded field whitelist and rejects anything else with a 400. Each call costs 2 credits.
196
+
197
+ ```bash
198
+ tl sponsorships update <id> '<json>' # Edit a sponsorship (adlink)
199
+ tl channels update <id> '<json>' # Edit a channel
200
+ ```
201
+
202
+ **Sponsorships** — only `publish_status` is editable. Accepts either an int code or a status label (`proposed`, `pending`, `sold`, `matched`, `outreach`, `proposal_approved`, `advertiser_reject`, `publisher_reject`, `agency_reject`, `unavailable`). Non-full-access users may only update sponsorships tied to their own organization (either through `creator_profile` or through the channel's `publication`). Trying to edit a sponsorship outside the user's org returns 403.
203
+
204
+ **Channels** — only the demographic fields are editable: `demographic_usa_share` and `demographic_male_share` (integers 0–100), `demographic_age` / `demographic_device` / `demographic_geo` (JSON objects with numeric values). Requires full-access permission; non-full-access users get a 403. The `demographics_updated_at` timestamp is refreshed automatically when any whitelisted demographic field changes.
205
+
206
+ Examples:
207
+ ```bash
208
+ tl sponsorships update 98765 '{"publish_status": "sold"}'
209
+ tl sponsorships update 98765 '{"publish_status": 3}'
210
+ tl channels update 12345 '{"demographic_male_share": 62}'
211
+ tl channels update 12345 '{"demographic_geo": {"US": 60, "UK": 12, "CA": 8}}'
212
+ tl channels update 12345 '{"demographic_male_share": 55, "demographic_usa_share": 70}'
213
+ ```
214
+
215
+ Anything outside these whitelists — price, cost, owner, channel name, etc. — is not editable through the CLI and must be done in the app or by a human with DB access.
216
+
191
217
  ### Raw queries (`tl db`)
192
218
 
193
219
  `tl db pg|fb|es` is the default tool. Reach for it whenever the question is anything beyond a trivially simple lookup — and use the structured commands only for those trivial cases (single-record `show`, plain filtered `list`). Don't paginate-and-reduce in your head when one SQL or ES body would do it server-side.
@@ -310,6 +336,8 @@ See [references/business-glossary.md](references/business-glossary.md) for reven
310
336
  | Arbitrary read-only `SELECT` on Postgres | **Available** via `tl db pg`. | SELECT-only, mandatory `LIMIT ≤ 500` + `OFFSET`, only certain SQL forms are allowed. See `references/postgres-schema.md`. |
311
337
  | Cross-reference helpers ("channels proposed to brand X", "channels sponsored by MBN brands in last N days") | **Available** via `tl db pg`. | Write the join: `thoughtleaders_adlink` ↔ `adspot` ↔ `channel` ↔ `profile` ↔ `profile_brands` ↔ `brand`. Filter by `publish_status` for proposed/sold and by date range as needed. See `references/postgres-schema.md` for the exact column names. |
312
338
  | **AdLink INSERT** with custom price/cost/owner/`weighted_price`/`created_where` | **Unavailable** — `tl sponsorships create` exists but only creates a free *proposal* between a channel and a brand. The `tl db pg` sanitizer accepts SELECT only — no INSERT/UPDATE. | Done in the app or by a human with DB access. |
339
+ | **AdLink UPDATE** of any field other than `publish_status` (price, cost, owner, send_date, …) | **Unavailable** — `tl sponsorships update` only accepts `publish_status` and only for sponsorships in the user's org (full-access bypasses the org check). | Done in the app or by a human with DB access. |
340
+ | **Channel UPDATE** of any field other than the demographic fields (`demographic_usa_share`, `demographic_male_share`, `demographic_age`, `demographic_device`, `demographic_geo`) | **Unavailable** — `tl channels update` only accepts those fields, and only for full-access users. | Done in the app or by a human with DB access. |
313
341
  | Pre-insert validation queries (joining `adspot ↔ channel ↔ profile ↔ org` to confirm MSN, integration=1, persona, plan) | **Available** via `tl db pg`. | One SELECT joining the four tables. Use `thoughtleaders_channel.media_selling_network_join_date IS NOT NULL` for MSN, `thoughtleaders_adspot.integration = 1` for mention adspots, `thoughtleaders_profile.persona` for the persona code (see persona constants in `references/postgres-schema.md`). |
314
342
  | Firebolt cross-table or join queries; filtering on non-indexed columns in WHERE | **Unavailable** — not accepted. | Fetch a wider slice keyed on `channel_id` (and optionally `id`), filter the rest in `jq`/Python. |
315
343
  | ES `query_string`, `regexp`, `wildcard`, `fuzzy`, `more_like_this`, parent/child joins; any `script_*`; multiple aggregations in one body | **Unavailable** — not accepted. | Rewrite using `term`/`terms`/`match`/`bool`/`nested`. For multi-agg dashboards, run multiple `tl db es` calls and combine client-side. For "similar"-style queries, try `tl channels similar` / `tl brands similar` (server-implemented similarity search). |