thoughtleaders-cli 0.6.44__tar.gz → 0.6.46__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.
- {thoughtleaders_cli-0.6.44 → thoughtleaders_cli-0.6.46}/.claude-plugin/plugin.json +1 -1
- {thoughtleaders_cli-0.6.44 → thoughtleaders_cli-0.6.46}/PKG-INFO +1 -1
- {thoughtleaders_cli-0.6.44 → thoughtleaders_cli-0.6.46}/pyproject.toml +1 -1
- {thoughtleaders_cli-0.6.44 → thoughtleaders_cli-0.6.46}/skills/tl/SKILL.md +100 -65
- {thoughtleaders_cli-0.6.44 → thoughtleaders_cli-0.6.46}/skills/tl/references/firebolt-schema.md +12 -3
- {thoughtleaders_cli-0.6.44 → thoughtleaders_cli-0.6.46}/skills/tl/references/postgres-schema.md +42 -125
- {thoughtleaders_cli-0.6.44 → thoughtleaders_cli-0.6.46}/skills/tl-report-builder/SKILL.md +2 -4
- {thoughtleaders_cli-0.6.44 → thoughtleaders_cli-0.6.46}/src/tl_cli/__init__.py +1 -1
- {thoughtleaders_cli-0.6.44 → thoughtleaders_cli-0.6.46}/src/tl_cli/commands/_comments_common.py +6 -4
- {thoughtleaders_cli-0.6.44 → thoughtleaders_cli-0.6.46}/src/tl_cli/commands/brands.py +39 -5
- {thoughtleaders_cli-0.6.44 → thoughtleaders_cli-0.6.46}/src/tl_cli/commands/channels.py +55 -11
- {thoughtleaders_cli-0.6.44 → thoughtleaders_cli-0.6.46}/src/tl_cli/commands/recommender.py +77 -8
- {thoughtleaders_cli-0.6.44 → thoughtleaders_cli-0.6.46}/.claude-plugin/marketplace.json +0 -0
- {thoughtleaders_cli-0.6.44 → thoughtleaders_cli-0.6.46}/.github/workflows/python-publish.yml +0 -0
- {thoughtleaders_cli-0.6.44 → thoughtleaders_cli-0.6.46}/.gitignore +0 -0
- {thoughtleaders_cli-0.6.44 → thoughtleaders_cli-0.6.46}/AGENTS.md +0 -0
- {thoughtleaders_cli-0.6.44 → thoughtleaders_cli-0.6.46}/CLAUDE.md +0 -0
- {thoughtleaders_cli-0.6.44 → thoughtleaders_cli-0.6.46}/LICENSE +0 -0
- {thoughtleaders_cli-0.6.44 → thoughtleaders_cli-0.6.46}/README.md +0 -0
- {thoughtleaders_cli-0.6.44 → thoughtleaders_cli-0.6.46}/agents/tl-analyst.md +0 -0
- {thoughtleaders_cli-0.6.44 → thoughtleaders_cli-0.6.46}/docs/architecture.md +0 -0
- {thoughtleaders_cli-0.6.44 → thoughtleaders_cli-0.6.46}/hooks/hooks.json +0 -0
- {thoughtleaders_cli-0.6.44 → thoughtleaders_cli-0.6.46}/hooks/scripts/load-tl-skill.mjs +0 -0
- {thoughtleaders_cli-0.6.44 → thoughtleaders_cli-0.6.46}/hooks/scripts/post-usage.sh +0 -0
- {thoughtleaders_cli-0.6.44 → thoughtleaders_cli-0.6.46}/hooks/scripts/pre-check.sh +0 -0
- {thoughtleaders_cli-0.6.44 → thoughtleaders_cli-0.6.46}/skills/tl/references/business-glossary.md +0 -0
- {thoughtleaders_cli-0.6.44 → thoughtleaders_cli-0.6.46}/skills/tl/references/elasticsearch-schema.md +0 -0
- {thoughtleaders_cli-0.6.44 → thoughtleaders_cli-0.6.46}/skills/tl-import/SKILL.md +0 -0
- {thoughtleaders_cli-0.6.44 → thoughtleaders_cli-0.6.46}/skills/tl-report-builder/examples/e2e_findings.md +0 -0
- {thoughtleaders_cli-0.6.44 → thoughtleaders_cli-0.6.46}/skills/tl-report-builder/examples/golden_queries.md +0 -0
- {thoughtleaders_cli-0.6.44 → thoughtleaders_cli-0.6.46}/skills/tl-report-builder/references/columns_brands.md +0 -0
- {thoughtleaders_cli-0.6.44 → thoughtleaders_cli-0.6.46}/skills/tl-report-builder/references/columns_channels.md +0 -0
- {thoughtleaders_cli-0.6.44 → thoughtleaders_cli-0.6.46}/skills/tl-report-builder/references/columns_content.md +0 -0
- {thoughtleaders_cli-0.6.44 → thoughtleaders_cli-0.6.46}/skills/tl-report-builder/references/columns_sponsorships.md +0 -0
- {thoughtleaders_cli-0.6.44 → thoughtleaders_cli-0.6.46}/skills/tl-report-builder/references/intelligence_filterset_schema.json +0 -0
- {thoughtleaders_cli-0.6.44 → thoughtleaders_cli-0.6.46}/skills/tl-report-builder/references/intelligence_widget_schema.json +0 -0
- {thoughtleaders_cli-0.6.44 → thoughtleaders_cli-0.6.46}/skills/tl-report-builder/references/report_glossary.md +0 -0
- {thoughtleaders_cli-0.6.44 → thoughtleaders_cli-0.6.46}/skills/tl-report-builder/references/sortable_columns.json +0 -0
- {thoughtleaders_cli-0.6.44 → thoughtleaders_cli-0.6.46}/skills/tl-report-builder/references/sponsorship_filterset_schema.json +0 -0
- {thoughtleaders_cli-0.6.44 → thoughtleaders_cli-0.6.46}/skills/tl-report-builder/references/sponsorship_widget_schema.json +0 -0
- {thoughtleaders_cli-0.6.44 → thoughtleaders_cli-0.6.46}/skills/tl-report-builder/references/widgets.md +0 -0
- {thoughtleaders_cli-0.6.44 → thoughtleaders_cli-0.6.46}/skills/tl-report-builder/tools/column_builder.md +0 -0
- {thoughtleaders_cli-0.6.44 → thoughtleaders_cli-0.6.46}/skills/tl-report-builder/tools/database_query.md +0 -0
- {thoughtleaders_cli-0.6.44 → thoughtleaders_cli-0.6.46}/skills/tl-report-builder/tools/keyword_research.md +0 -0
- {thoughtleaders_cli-0.6.44 → thoughtleaders_cli-0.6.46}/skills/tl-report-builder/tools/name_resolver.md +0 -0
- {thoughtleaders_cli-0.6.44 → thoughtleaders_cli-0.6.46}/skills/tl-report-builder/tools/sample_judge.md +0 -0
- {thoughtleaders_cli-0.6.44 → thoughtleaders_cli-0.6.46}/skills/tl-report-builder/tools/similar_channels.md +0 -0
- {thoughtleaders_cli-0.6.44 → thoughtleaders_cli-0.6.46}/skills/tl-report-builder/tools/topic_matcher.md +0 -0
- {thoughtleaders_cli-0.6.44 → thoughtleaders_cli-0.6.46}/skills/tl-report-builder/tools/widget_builder.md +0 -0
- {thoughtleaders_cli-0.6.44 → thoughtleaders_cli-0.6.46}/src/tl_cli/_completions.py +0 -0
- {thoughtleaders_cli-0.6.44 → thoughtleaders_cli-0.6.46}/src/tl_cli/auth/__init__.py +0 -0
- {thoughtleaders_cli-0.6.44 → thoughtleaders_cli-0.6.46}/src/tl_cli/auth/commands.py +0 -0
- {thoughtleaders_cli-0.6.44 → thoughtleaders_cli-0.6.46}/src/tl_cli/auth/finalize.py +0 -0
- {thoughtleaders_cli-0.6.44 → thoughtleaders_cli-0.6.46}/src/tl_cli/auth/login.py +0 -0
- {thoughtleaders_cli-0.6.44 → thoughtleaders_cli-0.6.46}/src/tl_cli/auth/pkce.py +0 -0
- {thoughtleaders_cli-0.6.44 → thoughtleaders_cli-0.6.46}/src/tl_cli/auth/token_store.py +0 -0
- {thoughtleaders_cli-0.6.44 → thoughtleaders_cli-0.6.46}/src/tl_cli/client/__init__.py +0 -0
- {thoughtleaders_cli-0.6.44 → thoughtleaders_cli-0.6.46}/src/tl_cli/client/errors.py +0 -0
- {thoughtleaders_cli-0.6.44 → thoughtleaders_cli-0.6.46}/src/tl_cli/client/http.py +0 -0
- {thoughtleaders_cli-0.6.44 → thoughtleaders_cli-0.6.46}/src/tl_cli/commands/__init__.py +0 -0
- {thoughtleaders_cli-0.6.44 → thoughtleaders_cli-0.6.46}/src/tl_cli/commands/ask.py +0 -0
- {thoughtleaders_cli-0.6.44 → thoughtleaders_cli-0.6.46}/src/tl_cli/commands/balance.py +0 -0
- {thoughtleaders_cli-0.6.44 → thoughtleaders_cli-0.6.46}/src/tl_cli/commands/bulk_import.py +0 -0
- {thoughtleaders_cli-0.6.44 → thoughtleaders_cli-0.6.46}/src/tl_cli/commands/changelog.py +0 -0
- {thoughtleaders_cli-0.6.44 → thoughtleaders_cli-0.6.46}/src/tl_cli/commands/credits.py +0 -0
- {thoughtleaders_cli-0.6.44 → thoughtleaders_cli-0.6.46}/src/tl_cli/commands/db.py +0 -0
- {thoughtleaders_cli-0.6.44 → thoughtleaders_cli-0.6.46}/src/tl_cli/commands/deals.py +0 -0
- {thoughtleaders_cli-0.6.44 → thoughtleaders_cli-0.6.46}/src/tl_cli/commands/describe.py +0 -0
- {thoughtleaders_cli-0.6.44 → thoughtleaders_cli-0.6.46}/src/tl_cli/commands/doctor.py +0 -0
- {thoughtleaders_cli-0.6.44 → thoughtleaders_cli-0.6.46}/src/tl_cli/commands/matches.py +0 -0
- {thoughtleaders_cli-0.6.44 → thoughtleaders_cli-0.6.46}/src/tl_cli/commands/proposals.py +0 -0
- {thoughtleaders_cli-0.6.44 → thoughtleaders_cli-0.6.46}/src/tl_cli/commands/reports.py +0 -0
- {thoughtleaders_cli-0.6.44 → thoughtleaders_cli-0.6.46}/src/tl_cli/commands/schema.py +0 -0
- {thoughtleaders_cli-0.6.44 → thoughtleaders_cli-0.6.46}/src/tl_cli/commands/setup.py +0 -0
- {thoughtleaders_cli-0.6.44 → thoughtleaders_cli-0.6.46}/src/tl_cli/commands/snapshots.py +0 -0
- {thoughtleaders_cli-0.6.44 → thoughtleaders_cli-0.6.46}/src/tl_cli/commands/sponsorships.py +0 -0
- {thoughtleaders_cli-0.6.44 → thoughtleaders_cli-0.6.46}/src/tl_cli/commands/uploads.py +0 -0
- {thoughtleaders_cli-0.6.44 → thoughtleaders_cli-0.6.46}/src/tl_cli/commands/whoami.py +0 -0
- {thoughtleaders_cli-0.6.44 → thoughtleaders_cli-0.6.46}/src/tl_cli/config.py +0 -0
- {thoughtleaders_cli-0.6.44 → thoughtleaders_cli-0.6.46}/src/tl_cli/filters.py +0 -0
- {thoughtleaders_cli-0.6.44 → thoughtleaders_cli-0.6.46}/src/tl_cli/hints.py +0 -0
- {thoughtleaders_cli-0.6.44 → thoughtleaders_cli-0.6.46}/src/tl_cli/main.py +0 -0
- {thoughtleaders_cli-0.6.44 → thoughtleaders_cli-0.6.46}/src/tl_cli/output/__init__.py +0 -0
- {thoughtleaders_cli-0.6.44 → thoughtleaders_cli-0.6.46}/src/tl_cli/output/formatter.py +0 -0
- {thoughtleaders_cli-0.6.44 → thoughtleaders_cli-0.6.46}/src/tl_cli/self_update.py +0 -0
- {thoughtleaders_cli-0.6.44 → thoughtleaders_cli-0.6.46}/tests/__init__.py +0 -0
- {thoughtleaders_cli-0.6.44 → thoughtleaders_cli-0.6.46}/tests/test_auth.py +0 -0
- {thoughtleaders_cli-0.6.44 → thoughtleaders_cli-0.6.46}/tests/test_filters.py +0 -0
- {thoughtleaders_cli-0.6.44 → thoughtleaders_cli-0.6.46}/tests/test_http_auth.py +0 -0
- {thoughtleaders_cli-0.6.44 → thoughtleaders_cli-0.6.46}/tests/test_output.py +0 -0
- {thoughtleaders_cli-0.6.44 → thoughtleaders_cli-0.6.46}/tests/test_reports.py +0 -0
- {thoughtleaders_cli-0.6.44 → thoughtleaders_cli-0.6.46}/tests/test_sponsorships.py +0 -0
- {thoughtleaders_cli-0.6.44 → thoughtleaders_cli-0.6.46}/uv.lock +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: thoughtleaders-cli
|
|
3
|
-
Version: 0.6.
|
|
3
|
+
Version: 0.6.46
|
|
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
|
|
@@ -1,41 +1,47 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: tl
|
|
3
3
|
description: |
|
|
4
|
-
Query and analyze
|
|
4
|
+
Query and analyze YouTube sponsorship data using the `tl` CLI. Use this skill for data exploration and questions about channels, brands and sponsorships: counts, metrics, trends, time-series, distributions, single-record drill-downs, revenue / pipeline-weighting math, view-curve analysis, cross-source business questions. Examples: "How many deals did we close last quarter?", "What's the weighted pipeline by sales owner?", "Show me the view curve for video X", "Find mentions of Surfshark in transcripts", "Investigate this video".
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
# ThoughtLeaders Data Analyst
|
|
8
8
|
|
|
9
|
-
Run the `tl` CLI to query ThoughtLeaders' sponsorship platform data. Use it to answer questions about deals, channels, brands, uploads, metrics, etc.
|
|
10
|
-
|
|
11
9
|
## Core Principles
|
|
12
10
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
Always run `tl schema pg|fb|es` before writing a raw query.
|
|
11
|
+
Run the `tl` CLI to query ThoughtLeaders' sponsorship platform data. Use it to answer questions about deals, channels, brands, uploads, metrics, etc. Use raw database queries via `tl db pg|fb|es` for everything.
|
|
16
12
|
|
|
17
|
-
|
|
13
|
+
Always run `tl schema pg|fb|es` before writing a raw query. When you only need the schema of one table, you MUST call `tl schema pg <table>` (or `tl schema fb <table>`). Avoid calling the unscoped form, to reduce token counts. ES has no per-table form (the index is a single document shape), so `tl schema es` is the only call there.
|
|
18
14
|
|
|
19
|
-
**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
|
|
15
|
+
**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:
|
|
20
16
|
|
|
21
17
|
- **`jq`** — filter, project, and transform JSON. The default for `tl … --json` post-processing.
|
|
22
18
|
```bash
|
|
23
|
-
tl
|
|
19
|
+
tl db pg "SELECT id, weighted_price FROM thoughtleaders_adlink
|
|
20
|
+
WHERE publish_status = 3 AND price > 5000
|
|
21
|
+
LIMIT 500 OFFSET 0" --json \
|
|
22
|
+
| jq '.results[] | {id, price: .weighted_price}'
|
|
24
23
|
```
|
|
25
24
|
- **`yq`** — same idea for YAML/TOML, useful when reading config files or `--md` blocks.
|
|
26
|
-
- **`rg`** — fast text search across CLI output, transcripts, and the codebase. Better than `grep` for searching large `--csv` exports or transcript dumps.
|
|
25
|
+
- **`rg`** — fast text search across CLI output, transcripts, and the codebase. Better than `grep` for searching large `--csv` exports or transcript dumps from ES.
|
|
27
26
|
```bash
|
|
28
27
|
tl db es '{"size":500,"query":{"term":{"channel.id":5607}},"_source":["id","transcript"]}' --json | rg -o "NordVPN[^.]*"
|
|
29
28
|
```
|
|
30
29
|
- **`duckdb`** — embedded analytical SQL over CSV/JSON files. Use when you need joins, aggregations, or window functions across multiple `tl` exports without spinning up a database.
|
|
31
30
|
```bash
|
|
32
|
-
tl
|
|
31
|
+
tl db pg "SELECT al.id, b.name AS brand, al.weighted_price AS price
|
|
32
|
+
FROM thoughtleaders_adlink al
|
|
33
|
+
JOIN thoughtleaders_profile p ON p.id = al.creator_profile_id
|
|
34
|
+
JOIN thoughtleaders_profile_brands pb ON pb.profile_id = p.id
|
|
35
|
+
JOIN thoughtleaders_brand b ON b.id = pb.brand_id
|
|
36
|
+
WHERE al.publish_status = 3
|
|
37
|
+
AND al.purchase_date >= '2026-01-01'
|
|
38
|
+
LIMIT 500 OFFSET 0" --csv > deals.csv
|
|
33
39
|
duckdb -c "SELECT brand, SUM(price) AS revenue FROM 'deals.csv' GROUP BY brand ORDER BY revenue DESC LIMIT 10"
|
|
34
40
|
```
|
|
35
41
|
|
|
36
|
-
The pattern is always: server-side narrowing first (
|
|
42
|
+
The pattern is always: server-side narrowing first (usually by filters in the `tl db` query, but could be from similarity / recommender searches), then shell tool to shape the result, then read only the final summary into context. If `tl doctor` reports any of these as missing, ask the user to install them — `tl-internal setup` installs all four by default.
|
|
37
43
|
|
|
38
|
-
Always assume there will be more than 1 page of results. You MUST always
|
|
44
|
+
Always assume there will be more than 1 page of results. You MUST always pass `LIMIT` and `OFFSET` to every `tl db pg|fb|es` query (and use the response envelope's `next_offset` / breadcrumbs to walk forward) so the entire data set is retrieved. The maximum number of rows per page is 500.
|
|
39
45
|
|
|
40
46
|
Retry after 5 seconds if the server returns a "connection denied" or a "server error" on any request.
|
|
41
47
|
|
|
@@ -49,7 +55,7 @@ This section defines business terminology. Any other skill files, command, and p
|
|
|
49
55
|
|
|
50
56
|
ThoughtLeaders is a sponsorship marketplace connecting **Brands** (advertisers / media buyers) with **Channels** (YouTube creators, podcasters / media sellers).
|
|
51
57
|
|
|
52
|
-
The centre of the data model
|
|
58
|
+
The centre of the data model are **Sponsorships** — business relationships between brands and channels. Sponsorships statuses form a sales funnel, from broad to narrow:
|
|
53
59
|
|
|
54
60
|
- **Sponsorships** — the broadest category, encompassing all stages, stored in the `thoughtleaders_adlink` table.
|
|
55
61
|
- **Matches** — possible brand-channel pairings that ThoughtLeaders thinks could work
|
|
@@ -58,33 +64,33 @@ The centre of the data model is **Sponsorships** — business relationships betw
|
|
|
58
64
|
|
|
59
65
|
Sponsorships are sometimes called "Ads" or "Ad campaigns". **"AdLink"** is another name for the same thing — it's the term the database uses (`thoughtleaders_adlink`) and shows up across internal code, schema docs, and AM Slack threads. Treat "sponsorship" and "adlink" as interchangeable; the user-facing word is "sponsorship," the engineering/DB word is "adlink."
|
|
60
66
|
|
|
61
|
-
The CLI has shortcut commands for each type: `tl matches`, `tl proposals`, `tl deals`. These filter `tl sponsorships` by status.
|
|
62
|
-
|
|
63
67
|
Other key concepts:
|
|
68
|
+
- **Channels** — YouTube channels, but could also be podcasts
|
|
69
|
+
- **Brands** — Entities (usually companies / organizations, but could be narrowed down to individual brands of a company)
|
|
64
70
|
- **Uploads** — YouTube videos indexed from Elasticsearch
|
|
65
71
|
- **Snapshots** — historical time-series metrics for channels and videos (Firebolt)
|
|
66
72
|
- **Reports** — saved report configurations that can be re-run
|
|
67
|
-
- **Comments** — notes attached to sponsorships
|
|
68
|
-
- **Adspots** — types of ads a channel
|
|
69
|
-
- **Profiles** —
|
|
73
|
+
- **Comments** — notes attached to sponsorships, channels, or brands
|
|
74
|
+
- **Adspots** — types of ads a channel is willing to publish (e.g. mention, dedicated video, product placement). Returned by `tl channels show`; each carries price/cost.
|
|
75
|
+
- **Profiles** — actors that own sponsorship records on behalf of either side of a deal. A profile is either buyer-side or seller-side:
|
|
70
76
|
- *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.
|
|
71
77
|
- *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`.
|
|
72
78
|
- **How to tell them apart** — three signals on the `thoughtleaders_profile` row, used in this order:
|
|
73
79
|
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.
|
|
74
80
|
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.
|
|
75
81
|
- 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.
|
|
76
|
-
- **MSN** (Media Selling Network) — the ~
|
|
82
|
+
- **MSN** (Media Selling Network) — the ~12k 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.
|
|
77
83
|
- **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.
|
|
78
|
-
- **TPP** (ThoughtLeaders Partner Program, a.k.a. "TL channels") — the ~
|
|
79
|
-
- **`demographics_updated_at`** (on
|
|
84
|
+
- **TPP** (ThoughtLeaders Partner Program, a.k.a. "TL channels") — the ~170 channels TL has the closest working relationship with. A channel is in the TPP group if `channel.is_tl_channel` is True. **Prefer TPP channels when booking**: they respond fastest, are the easiest to close, and don't need an outreach round-trip — treat them as immediately bookable. TPP is a strict subset of MSN, so the same booking rules (one active mention adspot, etc.) apply.
|
|
85
|
+
- **`demographics_updated_at`** (on channels) — If non-null, the channel has demographics screenshots on file. If null, no demographics screenshots have been uploaded. Use this to check whether a channel has demographics data from screenshots.
|
|
80
86
|
- **`impression`** (on channels) — projected views per video on that channel. Forward-looking estimate. May be null when not yet computed.
|
|
81
87
|
- **`views`** (on sponsorships) — actual view count of the sold and published sponsored video, accessible when `article_id` is set.
|
|
82
|
-
- **`impressions_guarantee`** (on sponsorships) — projected/guaranteed impressions for the sponsorship. Numeric
|
|
83
|
-
- **Sponsorship detail fields** (returned by `tl sponsorships show <id> --json`) —
|
|
88
|
+
- **`impressions_guarantee`** (on sponsorships) — projected/guaranteed impressions for the sponsorship. Numeric.
|
|
89
|
+
- **Sponsorship detail fields** (returned by `tl sponsorships show <id> --json`) — the detail payload includes `integration` (raw int), `publish_count`, `common_name`, `outreach_email`, nested `publisher` (`first_name`, `last_name`, `email`), nested `brand_contact` (`first_name`, `last_name`, `email`), and `brand.organization_name`. Use these when generating IOs, contracts, or outreach.
|
|
84
90
|
- **CPM** has two distinct meanings depending on level — pick the one the user actually wants:
|
|
85
91
|
- **Channel CPM** = `(adspot.price / channel.impression) * 1000` — projected price per thousand projected views. Used for pricing decisions **before** a sponsorship is sold. Available for channels with active adspots via `tl channels show <channel_id>`.
|
|
86
92
|
- **Sponsorship CPM** = calculated in either of two ways: if `views` is present, then CPM is `(sponsorship.price / sponsorship.views) × 1000`, meaning realized cost per thousand actual views, computed post-publication. If `views` is null, Compute from the sponsorship's `price` and the channel's `impression` fields.
|
|
87
|
-
-
|
|
93
|
+
- Where possible, calculate the correct CPM in a SQL expression.
|
|
88
94
|
- **Sponsorship dates** — each sponsorship has four distinct dates, useful for different queries:
|
|
89
95
|
- **`created_at`** — when the sponsorship record was created in the system
|
|
90
96
|
- **`purchase_date`** — when the sponsorship was purchased (i.e. when the deal was made); These make up bookings.
|
|
@@ -97,13 +103,13 @@ Users see data scoped by their organization and plan:
|
|
|
97
103
|
- **Media sellers** see sponsorships where their org is the publisher. They see `cost` but never `price`.
|
|
98
104
|
- **Intelligence plan** is required for accessing information not strictly related to the user's organisation.
|
|
99
105
|
|
|
100
|
-
When querying sponsorship bookings,
|
|
106
|
+
When querying sponsorship bookings, filter the rows with `publish_status = 3` (sold) and use `purchase_date` for the date range. For all-flow / not-yet-sold inclusive queries, drop the `publish_status` predicate and filter by `created_at` instead.
|
|
101
107
|
|
|
102
108
|
## Methodology
|
|
103
109
|
|
|
104
110
|
Where possible, if searching for a sponsorship match between channels and brands, first search for what do similar brands sponsor / which brands is the channel usually sponsored by. The similarity judgement should be preferably based on similar topics, similar upload frequency, similar channel sizes, and only after all that, on demographics.
|
|
105
111
|
|
|
106
|
-
Use the `tl channels similar` and `tl brands similar` commands to
|
|
112
|
+
Use the `tl channels similar` and `tl brands similar` commands to find channels or brands similar to a particular channel or brand. For category- or topic-driven discovery (e.g. "Find me Cooking channels", "Who scores high on USA share?"), use `tl recommender top-channels "<tag>"` (or `top-brands`/`top-profiles`) against the recommender — that's faster, ranked by category-strength. Run `tl recommender tags` to discover the valid tag names.
|
|
107
113
|
|
|
108
114
|
## Workflow
|
|
109
115
|
|
|
@@ -129,35 +135,44 @@ Notes:
|
|
|
129
135
|
|
|
130
136
|
Unless the user specifically asks for running a specific report or showing the result of a specific report, find the data by using other, low-level commands.
|
|
131
137
|
|
|
132
|
-
1. **Discover first**:
|
|
133
|
-
2. **Check
|
|
134
|
-
3. **
|
|
135
|
-
4. **
|
|
136
|
-
5. **
|
|
137
|
-
6. **
|
|
138
|
-
7. **Format results**: When the user asks for a list or tabular data, present the results as a well-formatted markdown table. Pick the most relevant columns and use clear headers.
|
|
138
|
+
1. **Discover first**: Use `tl schema pg`, `tl schema es`, and `tl schema fb` to find information about the main database (pg), the articles / uploads database (es), and the channel metrics database (fb).
|
|
139
|
+
2. **Check credits**: Run `tl balance --json` before expensive queries. Warn the user if a query will cost many credits.
|
|
140
|
+
3. **Decide the method of discovery**: If the user want to explore certain topics, use the recommender commands. If it's more about filtering, construct a query for PG or ES.
|
|
141
|
+
4. **Always use --json**: Parse JSON output for multi-step analysis.
|
|
142
|
+
5. **Chain commands**: For complex questions, chain multiple `tl` commands, shell commands, and other tools.
|
|
143
|
+
6. **Format results**: When the user asks for a list or tabular data, present the results as a well-formatted markdown table. Pick the most relevant columns and use clear headers. In this case, ask the user if they want to save the list as a report, and invoke the `tl-save-report` skill.
|
|
139
144
|
|
|
140
|
-
Prefer writing
|
|
145
|
+
Prefer writing shell code, `jq` commands, or `duckdb` commands that fetch or analysise large sets of data instead of analysing it yourself. Create temporary files in `/tmp` that can be analysed later in different ways. Before analysing a potentially large result set, first try fetching just a single result with `LIMIT 1` without `jq` etc, to see the shape of the data and any error messages.
|
|
141
146
|
|
|
142
147
|
## Available Commands
|
|
143
148
|
|
|
144
|
-
Note that if you're working on Windows, you
|
|
149
|
+
Note that if you're working on Windows, you must set up UTF-8 in the console, because all of these commands return UTF-8 data.
|
|
145
150
|
|
|
146
151
|
### Data queries
|
|
152
|
+
|
|
153
|
+
**Filtered queries go through `tl db pg|fb|es`.** Write the SELECT/ES body yourself, and freely perform joins and aggregations. The show/create/update commands exist because they target a single record by ID. Where needed, write Python scripts or duckdb queries to join data from different databases.
|
|
154
|
+
|
|
155
|
+
Filter-to-SQL examples (deals/matches/proposals all live on `thoughtleaders_adlink`, differentiated by `publish_status`):
|
|
156
|
+
|
|
157
|
+
| Want | Raw-DB equivalent |
|
|
158
|
+
| --- | --- |
|
|
159
|
+
| All sponsorships matching filters | `tl db pg "SELECT … FROM thoughtleaders_adlink WHERE …"` |
|
|
160
|
+
| Sold deals (`publish_status=3`) | `tl db pg "SELECT … FROM thoughtleaders_adlink WHERE publish_status = 3"` |
|
|
161
|
+
| Matched (`publish_status=7`) | `tl db pg "SELECT … FROM thoughtleaders_adlink WHERE publish_status = 7"` |
|
|
162
|
+
| Proposed (`publish_status=0`) | `tl db pg "SELECT … FROM thoughtleaders_adlink WHERE publish_status = 0"` |
|
|
163
|
+
| Video uploads from ElasticSearch | `tl db es '{"size":N,"query":{"term":{"channel.id":<id>}}}'` |
|
|
164
|
+
|
|
165
|
+
Single-record / mutation commands remain:
|
|
166
|
+
|
|
147
167
|
```bash
|
|
148
|
-
tl sponsorships list [filters...] # Sponsorships
|
|
149
168
|
tl sponsorships show <id> # Sponsorship detail
|
|
150
169
|
tl sponsorships create --channel <id> --brand <id> # Create proposal
|
|
151
170
|
tl sponsorships update <id> '<json>' # Update a sponsorship
|
|
152
|
-
tl deals list [filters...] # Shortcut: agreed-upon sponsorships (status:deal)
|
|
153
171
|
tl deals show <id> # Deal detail
|
|
154
|
-
tl matches list [filters...] # Shortcut: possible brand-channel pairings (status:match)
|
|
155
172
|
tl matches show <id> # Match detail
|
|
156
173
|
tl matches create --channel <id> --brand <id> # Create match
|
|
157
|
-
tl proposals list [filters...] # Shortcut: proposed matches (status:proposal)
|
|
158
174
|
tl proposals show <id> # Proposal detail
|
|
159
175
|
tl proposals create --channel <id> --brand <id> # Create proposal
|
|
160
|
-
tl uploads list [filters...] # Video uploads from ES
|
|
161
176
|
tl uploads show <id> # Upload detail
|
|
162
177
|
tl channels show <id-or-name> # Channel detail (accepts numeric ID or name) — for channel search use raw SQL on thoughtleaders_channel
|
|
163
178
|
tl channels find <query> # Resolve a string to {id, name}; accepts name/slug, YouTube URL/handle/ID, video URL (queues a scrape if no match)
|
|
@@ -177,7 +192,9 @@ tl recommender top-profiles "<tag>" # Top brand profiles loaded on a similari
|
|
|
177
192
|
tl recommender top-brands "<tag>" # Top brands (deduped from profiles) loaded on a similarity tag
|
|
178
193
|
tl recommender inspect-channel <ref> # Show a channel's similarity-profile breakdown (Intelligence)
|
|
179
194
|
tl recommender inspect-brand <ref> # Show a brand profile's ideal similarity-profile breakdown (Intelligence)
|
|
180
|
-
tl recommender
|
|
195
|
+
tl recommender channels-for-profile <id> # Find channels closest to a brand profile's ideal profile (Intelligence)
|
|
196
|
+
tl recommender channels-for-brand <ref> # Same as above but takes a brand ref; uses the brand's newest profile with a vector (Intelligence)
|
|
197
|
+
tl recommender brands-for-channel <ref> # Brands most likely to sponsor a channel; runs the channel's vector against the brand-profile index (Intelligence)
|
|
181
198
|
tl snapshots channel <id> # Channel metrics over time (Firebolt-backed)
|
|
182
199
|
tl snapshots video <id> --channel <id> # Video view curve (--channel required!)
|
|
183
200
|
tl reports # List saved reports
|
|
@@ -205,8 +222,6 @@ tl channels update 12345 '{"demographic_geo": {"US": 60, "UK": 12, "CA": 8}}'
|
|
|
205
222
|
tl channels update 12345 '{"demographic_male_share": 55, "demographic_usa_share": 70}'
|
|
206
223
|
```
|
|
207
224
|
|
|
208
|
-
Each call costs 2 credits. If a request is rejected with a 400, the response body names the offending key — read it and retry with a smaller body. If the user wants to edit something the API rejects, the change has to be made in the app or by a human with DB access.
|
|
209
|
-
|
|
210
225
|
### Creating and vetting sponsorships
|
|
211
226
|
|
|
212
227
|
This is the end-to-end workflow for proposing a sponsorship, then moving it through the funnel as the two sides respond. Three create commands plus `tl sponsorships update` cover every state transition the CLI exposes.
|
|
@@ -306,7 +321,7 @@ Reasons to write a raw query (the common case):
|
|
|
306
321
|
- **Multi-condition filtering** — compound boolean, `NOT IN`/`EXISTS`, `WHERE col IS NULL` on hidden fields, mixed range + enum + text predicates: write the SQL/ES body, don't over-fetch and post-filter.
|
|
307
322
|
- **Fields the structured commands don't expose** — e.g. `media_selling_network_join_date` (only the `msn` boolean is surfaced), `weighted_price`, `tx_data`, raw `publish_status` integer, etc.
|
|
308
323
|
|
|
309
|
-
Structured commands are still the right tool for: single-record `show` by ID,
|
|
324
|
+
Structured commands are still the right tool for: single-record `show` by ID, saved `tl reports run`, and `tl snapshots channel|video` (these wrap interpolation logic you'd otherwise reimplement). Anything that would have been a "filtered list" goes through `tl db pg|fb|es`.
|
|
310
325
|
|
|
311
326
|
| Need | Use |
|
|
312
327
|
|---|---|
|
|
@@ -317,8 +332,7 @@ Structured commands are still the right tool for: single-record `show` by ID, pl
|
|
|
317
332
|
| Transcript / brand-mention search inside video content | **`tl db es`** (no structured equivalent for content text) |
|
|
318
333
|
| Custom Firebolt shape (milestone-age slices, multi-channel growth comparisons) | **`tl db fb`** |
|
|
319
334
|
| Single-record detail lookup by ID | `tl <resource> show <id>` |
|
|
320
|
-
|
|
|
321
|
-
| Channel/brand similarity (server-implemented similarity search) | `tl channels similar`, `tl brands similar` |
|
|
335
|
+
| Channel/brand similarity (server-implemented similarity search) | `tl channels similar`, `tl brands similar`, `tl recommender ...` |
|
|
322
336
|
| Saved reports | `tl reports`, `tl reports run` |
|
|
323
337
|
| Time-series view-curve / channel growth (default shape with interpolation) | `tl snapshots channel`, `tl snapshots video` |
|
|
324
338
|
|
|
@@ -435,17 +449,6 @@ tl changelog since v0.4.10 # Notes from v0.4.10 to latest
|
|
|
435
449
|
tl changelog --md > CHANGELOG.md # Capture for a doc
|
|
436
450
|
```
|
|
437
451
|
|
|
438
|
-
`tl changelog` summaries are LLM-generated server-side from full commit messages and cached per version, so repeat calls are fast and don't re-bill the LLM. The release date and a 2–4 sentence prose summary come back per version.
|
|
439
|
-
|
|
440
|
-
### Filter syntax
|
|
441
|
-
Structured list commands accept `key:value` filters (use them for trivially simple lookups):
|
|
442
|
-
```bash
|
|
443
|
-
tl sponsorships list status:sold brand:"Nike" purchase-date:2026-01
|
|
444
|
-
tl uploads list channel:12345 type:longform
|
|
445
|
-
```
|
|
446
|
-
|
|
447
|
-
Date filters accept keywords: `today`, `yesterday`, `tomorrow`.
|
|
448
|
-
|
|
449
452
|
#### Channel discovery — recommender first, raw SQL second
|
|
450
453
|
|
|
451
454
|
For category- or demographic-driven discovery, **use the recommender, not `content_category` SQL.** The recommender ranks channels by how strongly they load on a category/demographic tag (similarity scores), instead of forcing exact equality on a single integer code. It also returns the matching brand profiles alongside the channels — useful when the user actually wants to know "who buys this kind of inventory."
|
|
@@ -455,7 +458,7 @@ For category- or demographic-driven discovery, **use the recommender, not `conte
|
|
|
455
458
|
tl recommender tags cooking
|
|
456
459
|
tl recommender tags "usa"
|
|
457
460
|
|
|
458
|
-
# Top channels & profiles loaded on a similarity tag (
|
|
461
|
+
# Top channels & profiles loaded on a similarity tag (Intelligence)
|
|
459
462
|
tl recommender top-channels "Cooking" msn:yes --limit 50
|
|
460
463
|
tl recommender top-channels "Tech" --limit 30
|
|
461
464
|
tl recommender top-brands "USA share" mbn:yes --limit 50
|
|
@@ -511,8 +514,8 @@ While analysing results, you must always examine the `results` field in the JSON
|
|
|
511
514
|
|
|
512
515
|
Every query costs credits. Before running expensive queries:
|
|
513
516
|
1. Check the credit rate: `tl describe show <resource> --json | jq '.credits'` and the user balance.
|
|
514
|
-
2. **
|
|
515
|
-
3. Estimate cost from the formula or the table; for non-
|
|
517
|
+
2. **Multi-row endpoints (snapshots, comments, reports, `tl db pg|fb|es`) are priced non-linearly:** `cost = 1 + mult × 0.126 × n^1.2`, where `mult` is the per-resource complexity factor (1.0 for cheap reads, 1.2 for snapshots, 1.3 for reports, 1.4 for raw db). Detail/history/similar endpoints are linear (`rate × results`).
|
|
518
|
+
3. Estimate cost from the formula or the table; for non-row-priced endpoints use `results × rate`.
|
|
516
519
|
4. If estimated cost is more than 10% of the remaining balance, ask the user to confirm the operation before running.
|
|
517
520
|
|
|
518
521
|
## Data Scoping
|
|
@@ -520,7 +523,7 @@ Every query costs credits. Before running expensive queries:
|
|
|
520
523
|
Users only see data their plan allows:
|
|
521
524
|
- **Media buyers** see deals where their org is the brand. They see `price` but never `cost`.
|
|
522
525
|
- **Media sellers** see deals where their org is the publisher. They see `cost` but never `price`.
|
|
523
|
-
- **Intelligence plan** required for `tl brands`, the full `tl recommender` surface, and
|
|
526
|
+
- **Intelligence plan** required for `tl brands`, the full `tl recommender` surface, and `tl db es` access to full transcript / brand-mention data.
|
|
524
527
|
- **Paid plan** required for `tl snapshots`.
|
|
525
528
|
|
|
526
529
|
## Important: Status Labels
|
|
@@ -535,7 +538,15 @@ When presenting sponsorship status data, always use human-readable labels — ne
|
|
|
535
538
|
|
|
536
539
|
"Show me my sold sponsorships this quarter":
|
|
537
540
|
```bash
|
|
538
|
-
tl
|
|
541
|
+
tl db pg "SELECT al.id, al.weighted_price, al.purchase_date, b.name AS brand
|
|
542
|
+
FROM thoughtleaders_adlink al
|
|
543
|
+
JOIN thoughtleaders_profile p ON p.id = al.creator_profile_id
|
|
544
|
+
JOIN thoughtleaders_profile_brands pb ON pb.profile_id = p.id
|
|
545
|
+
JOIN thoughtleaders_brand b ON b.id = pb.brand_id
|
|
546
|
+
WHERE al.publish_status = 3
|
|
547
|
+
AND al.purchase_date >= '2026-01-01'
|
|
548
|
+
ORDER BY al.purchase_date DESC
|
|
549
|
+
LIMIT 500 OFFSET 0" --json
|
|
539
550
|
```
|
|
540
551
|
|
|
541
552
|
"What channels does Nike sponsor?":
|
|
@@ -587,7 +598,14 @@ tl recommender top-channels "Cooking" msn:yes --limit 100 --json \
|
|
|
587
598
|
|
|
588
599
|
"Show sold sponsorships targeting mobile US audiences":
|
|
589
600
|
```bash
|
|
590
|
-
tl
|
|
601
|
+
tl db pg "SELECT al.id, c.channel_name, c.demographic_device_primary, c.demographic_usa_share, al.weighted_price
|
|
602
|
+
FROM thoughtleaders_adlink al
|
|
603
|
+
JOIN thoughtleaders_adspot s ON s.id = al.ad_spot_id
|
|
604
|
+
JOIN thoughtleaders_channel c ON c.id = s.channel_id
|
|
605
|
+
WHERE al.publish_status = 3
|
|
606
|
+
AND c.demographic_device_primary = 'mobile'
|
|
607
|
+
AND c.demographic_usa_share >= 60
|
|
608
|
+
LIMIT 500 OFFSET 0" --json
|
|
591
609
|
```
|
|
592
610
|
|
|
593
611
|
"Find channels similar to one I know" (similarity recommender, 25 credits per call):
|
|
@@ -612,6 +630,23 @@ tl recommender top-brands "USA share" mbn:yes --limit 30 # Top brands (ded
|
|
|
612
630
|
tl recommender top-channels "Tech" exclude-for-profile:842 # Drop channels already proposed for profile 842
|
|
613
631
|
tl recommender inspect-channel 29834 # Per-tag breakdown of a channel's vector
|
|
614
632
|
tl recommender inspect-brand Nike # Per-tag breakdown of a brand's ideal profile
|
|
615
|
-
tl recommender
|
|
633
|
+
tl recommender channels-for-profile 842 --limit 30 # Channels closest to a brand profile's ideal profile
|
|
634
|
+
tl recommender channels-for-profile 842 msn:yes language:en # Same, filtered to English MSN channels
|
|
635
|
+
tl recommender channels-for-brand Nike --limit 30 # Same, but takes a brand ref (uses the brand's newest profile with a vector)
|
|
636
|
+
tl recommender channels-for-brand 6037 msn:yes language:en --limit 30
|
|
637
|
+
tl recommender brands-for-channel 29834 --limit 30 # Brands likely to sponsor this channel
|
|
638
|
+
tl recommender brands-for-channel "MrBeast" mbn:yes --limit 30 # Same, restricted to MBN brand profiles
|
|
616
639
|
```
|
|
640
|
+
|
|
641
|
+
**Filters on the recommender commands:**
|
|
642
|
+
|
|
643
|
+
| Command | Filters |
|
|
644
|
+
| --- | --- |
|
|
645
|
+
| `top-channels` | `msn:<yes\|no\|all>` (default all), `exclude-for-profile:<id>` |
|
|
646
|
+
| `top-profiles` | `mbn:<yes\|no\|all>` (default all), `exclude-for-channel:<id>` |
|
|
647
|
+
| `top-brands` | `mbn:<yes\|no\|all>` (default all) |
|
|
648
|
+
| `channels-for-profile` | `language:<iso>` (default `en`), `msn:<yes\|no>` (default `no`) |
|
|
649
|
+
| `channels-for-brand` | same as `channels-for-profile` |
|
|
650
|
+
| `brands-for-channel` | `mbn:<yes\|no\|all>` (default `all`) |
|
|
651
|
+
|
|
617
652
|
Use `tl recommender top` for category/topic discovery (it's ranked) and `tl channels similar` / `tl brands similar` for 1:1 lookalike searches.
|
{thoughtleaders_cli-0.6.44 → thoughtleaders_cli-0.6.46}/skills/tl/references/firebolt-schema.md
RENAMED
|
@@ -120,9 +120,18 @@ Every Firebolt workflow has two steps:
|
|
|
120
120
|
tl recommender top-channels "Tech" msn:yes --limit 50 --json \
|
|
121
121
|
| jq '.results[].channel_id'
|
|
122
122
|
|
|
123
|
-
# Or videos for a specific brand's deals (Postgres side, via
|
|
124
|
-
tl
|
|
125
|
-
|
|
123
|
+
# Or videos for a specific brand's deals (Postgres side, via raw SQL)
|
|
124
|
+
tl db pg "SELECT al.id, al.article_id, s.channel_id
|
|
125
|
+
FROM thoughtleaders_adlink al
|
|
126
|
+
JOIN thoughtleaders_adspot s ON s.id = al.ad_spot_id
|
|
127
|
+
JOIN thoughtleaders_profile p ON p.id = al.creator_profile_id
|
|
128
|
+
JOIN thoughtleaders_profile_brands pb ON pb.profile_id = p.id
|
|
129
|
+
JOIN thoughtleaders_brand b ON b.id = pb.brand_id
|
|
130
|
+
WHERE al.publish_status = 3
|
|
131
|
+
AND b.name = 'Nike'
|
|
132
|
+
AND al.article_id IS NOT NULL
|
|
133
|
+
LIMIT 500 OFFSET 0" --json \
|
|
134
|
+
| jq -r '.results[] | "\(.channel_id):\(.article_id)"'
|
|
126
135
|
|
|
127
136
|
# Or videos via Elasticsearch content search
|
|
128
137
|
tl db es '{
|