thoughtleaders-cli 0.5.0__tar.gz → 0.6.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.
- {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.6.0}/.claude-plugin/plugin.json +1 -1
- {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.6.0}/AGENTS.md +3 -1
- {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.6.0}/PKG-INFO +24 -13
- {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.6.0}/README.md +23 -12
- thoughtleaders_cli-0.6.0/agents/tl-analyst.md +112 -0
- {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.6.0}/commands/tl-sponsorships.md +9 -2
- {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.6.0}/commands/tl.md +4 -4
- {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.6.0}/docs/architecture.md +80 -59
- {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.6.0}/pyproject.toml +1 -1
- {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.6.0}/skills/tl/SKILL.md +123 -59
- {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.6.0}/skills/tl/references/firebolt-schema.md +6 -5
- {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.6.0}/src/tl_cli/__init__.py +1 -1
- thoughtleaders_cli-0.6.0/src/tl_cli/commands/doctor.py +134 -0
- thoughtleaders_cli-0.6.0/src/tl_cli/commands/recommender.py +247 -0
- {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.6.0}/src/tl_cli/commands/setup.py +0 -2
- {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.6.0}/src/tl_cli/main.py +2 -0
- {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.6.0}/src/tl_cli/output/formatter.py +48 -6
- thoughtleaders_cli-0.6.0/tests/test_output.py +230 -0
- {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.6.0}/uv.lock +2 -2
- thoughtleaders_cli-0.5.0/agents/tl-analyst.md +0 -66
- thoughtleaders_cli-0.5.0/commands/tl-brands.md +0 -16
- thoughtleaders_cli-0.5.0/commands/tl-channels.md +0 -31
- thoughtleaders_cli-0.5.0/src/tl_cli/commands/doctor.py +0 -70
- thoughtleaders_cli-0.5.0/tests/test_output.py +0 -19
- {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.6.0}/.claude-plugin/marketplace.json +0 -0
- {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.6.0}/.github/workflows/python-publish.yml +0 -0
- {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.6.0}/.gitignore +0 -0
- {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.6.0}/CLAUDE.md +0 -0
- {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.6.0}/LICENSE +0 -0
- {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.6.0}/commands/tl-balance.md +0 -0
- {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.6.0}/commands/tl-reports.md +0 -0
- {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.6.0}/hooks/hooks.json +0 -0
- {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.6.0}/hooks/scripts/post-usage.sh +0 -0
- {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.6.0}/hooks/scripts/pre-check.sh +0 -0
- {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.6.0}/skills/tl/references/business-glossary.md +0 -0
- {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.6.0}/skills/tl/references/elasticsearch-schema.md +0 -0
- {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.6.0}/skills/tl/references/postgres-schema.md +0 -0
- {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.6.0}/src/tl_cli/_completions.py +0 -0
- {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.6.0}/src/tl_cli/auth/__init__.py +0 -0
- {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.6.0}/src/tl_cli/auth/commands.py +0 -0
- {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.6.0}/src/tl_cli/auth/login.py +0 -0
- {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.6.0}/src/tl_cli/auth/pkce.py +0 -0
- {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.6.0}/src/tl_cli/auth/token_store.py +0 -0
- {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.6.0}/src/tl_cli/client/__init__.py +0 -0
- {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.6.0}/src/tl_cli/client/errors.py +0 -0
- {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.6.0}/src/tl_cli/client/http.py +0 -0
- {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.6.0}/src/tl_cli/commands/__init__.py +0 -0
- {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.6.0}/src/tl_cli/commands/ask.py +0 -0
- {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.6.0}/src/tl_cli/commands/balance.py +0 -0
- {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.6.0}/src/tl_cli/commands/brands.py +0 -0
- {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.6.0}/src/tl_cli/commands/changelog.py +0 -0
- {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.6.0}/src/tl_cli/commands/channels.py +0 -0
- {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.6.0}/src/tl_cli/commands/comments.py +0 -0
- {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.6.0}/src/tl_cli/commands/db.py +0 -0
- {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.6.0}/src/tl_cli/commands/deals.py +0 -0
- {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.6.0}/src/tl_cli/commands/describe.py +0 -0
- {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.6.0}/src/tl_cli/commands/matches.py +0 -0
- {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.6.0}/src/tl_cli/commands/proposals.py +0 -0
- {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.6.0}/src/tl_cli/commands/reports.py +0 -0
- {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.6.0}/src/tl_cli/commands/schema.py +0 -0
- {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.6.0}/src/tl_cli/commands/snapshots.py +0 -0
- {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.6.0}/src/tl_cli/commands/sponsorships.py +0 -0
- {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.6.0}/src/tl_cli/commands/uploads.py +0 -0
- {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.6.0}/src/tl_cli/commands/whoami.py +0 -0
- {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.6.0}/src/tl_cli/config.py +0 -0
- {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.6.0}/src/tl_cli/filters.py +0 -0
- {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.6.0}/src/tl_cli/hints.py +0 -0
- {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.6.0}/src/tl_cli/output/__init__.py +0 -0
- {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.6.0}/src/tl_cli/self_update.py +0 -0
- {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.6.0}/tests/__init__.py +0 -0
- {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.6.0}/tests/test_auth.py +0 -0
- {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.6.0}/tests/test_filters.py +0 -0
- {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.6.0}/tests/test_sponsorships.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Project Overview
|
|
2
2
|
|
|
3
|
-
**tl-cli** is a Python CLI for querying ThoughtLeaders sponsorship data (sponsorships, channels, brands, uploads, snapshots, reports). Built with Typer + Rich + httpx. Designed as an "agent-first tool" — the CLI handles structured commands and output, while the user's AI agent (Claude) provides intelligence.
|
|
3
|
+
**tl-cli** is a Python CLI for querying ThoughtLeaders sponsorship data (sponsorships, channels, brands, uploads, snapshots, reports, vector recommender). Built with Typer + Rich + httpx. Designed as an "agent-first tool" — the CLI handles structured commands and output, while the user's AI agent (Claude) provides intelligence.
|
|
4
4
|
|
|
5
5
|
# Architecture
|
|
6
6
|
|
|
@@ -20,6 +20,8 @@ When adding a new data command, follow this pattern. See `sponsorships.py` for t
|
|
|
20
20
|
|
|
21
21
|
`deals`, `matches`, and `proposals` are shortcut commands that delegate to sponsorships' `do_list`/`do_show`/`do_create` with a pre-set status filter. They reject explicit `status:` filters — users should use `tl sponsorships list` for finer-grained status filtering.
|
|
22
22
|
|
|
23
|
+
`recommender` (`commands/recommender.py`) wraps the vector-recommender API at `/api/cli/v1/recommender/*` — `tags` (free), `top`, `inspect-channel`, `inspect-brand`, `similar-to-profile` (all 50 credits flat, Intelligence-gated). Channel→channel and brand→brand similarity stay on `tl channels similar` / `tl brands similar`. When updating the SKILL or examples, prefer steering category/topic discovery (e.g. "Cooking channels") to `tl recommender top "<tag>"` rather than `WHERE content_category = <code>` SQL — the recommender is ranked, not equality-based, and returns matching brand profiles alongside channels. The underlying recommender code uses "element"/"field_name" terminology; the CLI/API layer renames these to "tag" at the boundary.
|
|
24
|
+
|
|
23
25
|
## Filter Parsing (`filters.py`)
|
|
24
26
|
|
|
25
27
|
`parse_filters()` handles `key:value` and `key:"quoted value"` syntax. Returns `dict[str, str]` passed as query params. Date filter keys (listed in `DATE_FILTER_KEYS` — e.g. `since`, `created-at`, `created-at-start`, `publish-date-end`) accept keywords `today`, `yesterday`, `tomorrow`. Sponsorship date fields (`created-at`, `publish-date`, `purchase-date`, `send-date`) each expose three filter shapes: bare `<field>:<date>` matches within that date/period, and `<field>-start:` / `<field>-end:` give inclusive lower/upper bounds (both sides inclusive; partial dates expand to the whole period). Empty-string values result in `IS NULL` queries on the backend.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: thoughtleaders-cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.6.0
|
|
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
|
|
@@ -43,11 +43,11 @@ pip install -e .
|
|
|
43
43
|
### As a user
|
|
44
44
|
|
|
45
45
|
```bash
|
|
46
|
-
pipx install
|
|
46
|
+
pipx install thoughtleaders-cli
|
|
47
47
|
# or
|
|
48
|
-
uv tool install
|
|
48
|
+
uv tool install thoughtleaders-cli
|
|
49
49
|
# or (but try to avoid it because just "pip" will not create a new venv for the product - only "uv" and "pipx" will do that)
|
|
50
|
-
pip install
|
|
50
|
+
pip install thoughtleaders-cli
|
|
51
51
|
```
|
|
52
52
|
|
|
53
53
|
Then set up:
|
|
@@ -81,13 +81,16 @@ tl uploads list q:code --csv
|
|
|
81
81
|
# Show upload details (supports colon-containing IDs)
|
|
82
82
|
tl uploads show 1174310:0BehkmVa7ak
|
|
83
83
|
|
|
84
|
-
# Search channels
|
|
85
|
-
#
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
tl
|
|
90
|
-
|
|
84
|
+
# Search channels via raw SQL — `tl db pg` against thoughtleaders_channel
|
|
85
|
+
# (run `tl schema pg` once to confirm the live column set).
|
|
86
|
+
tl db pg "SELECT id, channel_name, total_views FROM thoughtleaders_channel
|
|
87
|
+
WHERE content_category = <COOKING_CODE> AND total_views >= 100000
|
|
88
|
+
ORDER BY total_views DESC LIMIT 50 OFFSET 0"
|
|
89
|
+
tl db pg "SELECT id, channel_name FROM thoughtleaders_channel
|
|
90
|
+
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`.
|
|
91
94
|
|
|
92
95
|
# Show channel detail — accepts numeric ID or channel name.
|
|
93
96
|
# Names that match more than one active channel print a candidate list
|
|
@@ -102,6 +105,16 @@ tl channels show "Economics Explained"
|
|
|
102
105
|
tl channels similar 12345 --limit 10
|
|
103
106
|
tl channels similar "Tremending girls" min-score:0.85 --limit 5
|
|
104
107
|
|
|
108
|
+
# Vector recommender — discovery by category/demographic tag (Intelligence plan).
|
|
109
|
+
# `tags` is free; `top`, `inspect-*`, and `similar-to-profile` cost 50 credits flat.
|
|
110
|
+
tl recommender tags # List every tag (free)
|
|
111
|
+
tl recommender tags cooking # Search tag names by substring
|
|
112
|
+
tl recommender top "Cooking" msn:yes --limit 50 # Top channels & brand profiles for a tag
|
|
113
|
+
tl recommender top "USA share" mbn:yes # Demographic tag, MBN brands only
|
|
114
|
+
tl recommender inspect-channel 12345 # Per-tag breakdown of a channel's vector
|
|
115
|
+
tl recommender inspect-brand Nike # Per-tag breakdown of a brand's ideal vector
|
|
116
|
+
tl recommender similar-to-profile 842 # Channels closest to a brand profile
|
|
117
|
+
|
|
105
118
|
# Brand intelligence
|
|
106
119
|
tl brands show Nike
|
|
107
120
|
|
|
@@ -173,8 +186,6 @@ Talk naturally in Claude Code:
|
|
|
173
186
|
Resource-specific slash commands:
|
|
174
187
|
```
|
|
175
188
|
/tl-sponsorships pending with send dates in April
|
|
176
|
-
/tl-channels cooking channels over 100k subscribers
|
|
177
|
-
/tl-brands Nike
|
|
178
189
|
/tl-reports run my Q1 pipeline
|
|
179
190
|
/tl-balance
|
|
180
191
|
```
|
|
@@ -16,11 +16,11 @@ pip install -e .
|
|
|
16
16
|
### As a user
|
|
17
17
|
|
|
18
18
|
```bash
|
|
19
|
-
pipx install
|
|
19
|
+
pipx install thoughtleaders-cli
|
|
20
20
|
# or
|
|
21
|
-
uv tool install
|
|
21
|
+
uv tool install thoughtleaders-cli
|
|
22
22
|
# or (but try to avoid it because just "pip" will not create a new venv for the product - only "uv" and "pipx" will do that)
|
|
23
|
-
pip install
|
|
23
|
+
pip install thoughtleaders-cli
|
|
24
24
|
```
|
|
25
25
|
|
|
26
26
|
Then set up:
|
|
@@ -54,13 +54,16 @@ tl uploads list q:code --csv
|
|
|
54
54
|
# Show upload details (supports colon-containing IDs)
|
|
55
55
|
tl uploads show 1174310:0BehkmVa7ak
|
|
56
56
|
|
|
57
|
-
# Search channels
|
|
58
|
-
#
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
tl
|
|
63
|
-
|
|
57
|
+
# Search channels via raw SQL — `tl db pg` against thoughtleaders_channel
|
|
58
|
+
# (run `tl schema pg` once to confirm the live column set).
|
|
59
|
+
tl db pg "SELECT id, channel_name, total_views FROM thoughtleaders_channel
|
|
60
|
+
WHERE content_category = <COOKING_CODE> AND total_views >= 100000
|
|
61
|
+
ORDER BY total_views DESC LIMIT 50 OFFSET 0"
|
|
62
|
+
tl db pg "SELECT id, channel_name FROM thoughtleaders_channel
|
|
63
|
+
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`.
|
|
64
67
|
|
|
65
68
|
# Show channel detail — accepts numeric ID or channel name.
|
|
66
69
|
# Names that match more than one active channel print a candidate list
|
|
@@ -75,6 +78,16 @@ tl channels show "Economics Explained"
|
|
|
75
78
|
tl channels similar 12345 --limit 10
|
|
76
79
|
tl channels similar "Tremending girls" min-score:0.85 --limit 5
|
|
77
80
|
|
|
81
|
+
# Vector recommender — discovery by category/demographic tag (Intelligence plan).
|
|
82
|
+
# `tags` is free; `top`, `inspect-*`, and `similar-to-profile` cost 50 credits flat.
|
|
83
|
+
tl recommender tags # List every tag (free)
|
|
84
|
+
tl recommender tags cooking # Search tag names by substring
|
|
85
|
+
tl recommender top "Cooking" msn:yes --limit 50 # Top channels & brand profiles for a tag
|
|
86
|
+
tl recommender top "USA share" mbn:yes # Demographic tag, MBN brands only
|
|
87
|
+
tl recommender inspect-channel 12345 # Per-tag breakdown of a channel's vector
|
|
88
|
+
tl recommender inspect-brand Nike # Per-tag breakdown of a brand's ideal vector
|
|
89
|
+
tl recommender similar-to-profile 842 # Channels closest to a brand profile
|
|
90
|
+
|
|
78
91
|
# Brand intelligence
|
|
79
92
|
tl brands show Nike
|
|
80
93
|
|
|
@@ -146,8 +159,6 @@ Talk naturally in Claude Code:
|
|
|
146
159
|
Resource-specific slash commands:
|
|
147
160
|
```
|
|
148
161
|
/tl-sponsorships pending with send dates in April
|
|
149
|
-
/tl-channels cooking channels over 100k subscribers
|
|
150
|
-
/tl-brands Nike
|
|
151
162
|
/tl-reports run my Q1 pipeline
|
|
152
163
|
/tl-balance
|
|
153
164
|
```
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: tl-analyst
|
|
3
|
+
description: Use when the user asks to analyze, compare, investigate, or summarize ThoughtLeaders data across multiple dimensions. Chains tl CLI commands to answer complex questions that require multiple queries, cross-referencing, or aggregation. Triggers on "analyze", "compare", "investigate", "deep dive", "cross-reference", "trend", "correlation".
|
|
4
|
+
tools: [Bash, Read]
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# TL Data Analyst Agent
|
|
8
|
+
|
|
9
|
+
You are an autonomous data analyst for ThoughtLeaders. You answer complex questions that require cross-referencing, aggregation, or multi-step reasoning.
|
|
10
|
+
|
|
11
|
+
## Default to raw database queries
|
|
12
|
+
|
|
13
|
+
For anything beyond a trivially simple lookup, write a single raw query against the right engine instead of chaining structured `tl <resource>` commands:
|
|
14
|
+
|
|
15
|
+
- **Postgres (`tl db pg`)** — joins, aggregations, multi-condition filters, fields the structured commands don't expose. Default for any deal/pipeline/brand/channel question that involves more than one filter or one aggregation.
|
|
16
|
+
- **Elasticsearch (`tl db es`)** — transcript / brand-mention text search, video-level aggregations, demographic country-share filters that compose with content predicates.
|
|
17
|
+
- **Firebolt (`tl db fb`)** — custom time-series shapes (multi-channel growth comparisons, milestone-age slices). For default shapes, prefer `tl snapshots`.
|
|
18
|
+
|
|
19
|
+
Reserve structured commands for: single-record `show` by ID, plain filtered `list` with one or two filters that the structured vocabulary already supports, `tl channels similar` / `tl brands similar` (vector KNN), `tl reports run`, and `tl snapshots`.
|
|
20
|
+
|
|
21
|
+
One raw query beats N paginated structured walks stitched in `jq`/Python — on cost, latency, and the ES `from+size = 10000` cap.
|
|
22
|
+
|
|
23
|
+
## Before Starting Any Analysis
|
|
24
|
+
|
|
25
|
+
1. **Check auth**: `tl auth status`
|
|
26
|
+
2. **Check balance**: `tl balance --json` — estimate total cost for your planned queries
|
|
27
|
+
3. **Discover schema**:
|
|
28
|
+
- For raw queries: `tl schema pg|fb|es` — live tables/columns visible to the caller.
|
|
29
|
+
- For structured commands: `tl describe show <resource> --json`.
|
|
30
|
+
4. **Check saved reports**: `tl reports --json` — a saved report might already answer the question
|
|
31
|
+
|
|
32
|
+
If estimated cost > 200 credits, ask the user to confirm before proceeding.
|
|
33
|
+
|
|
34
|
+
## Analysis Patterns
|
|
35
|
+
|
|
36
|
+
### Aggregation / pipeline analysis (raw PG)
|
|
37
|
+
"What's our best performing brand this quarter?"
|
|
38
|
+
```sql
|
|
39
|
+
tl db pg "SELECT b.name, SUM(a.weighted_price) AS pipeline, COUNT(*) AS deals
|
|
40
|
+
FROM thoughtleaders_adlink a
|
|
41
|
+
JOIN thoughtleaders_profile p ON a.creator_profile_id = p.id
|
|
42
|
+
JOIN thoughtleaders_profile_brands pb ON p.id = pb.profile_id
|
|
43
|
+
JOIN thoughtleaders_brand b ON pb.brand_id = b.id
|
|
44
|
+
WHERE a.publish_status = 3
|
|
45
|
+
AND a.purchase_date >= date_trunc('quarter', CURRENT_DATE)
|
|
46
|
+
GROUP BY b.name
|
|
47
|
+
ORDER BY pipeline DESC
|
|
48
|
+
LIMIT 20 OFFSET 0"
|
|
49
|
+
```
|
|
50
|
+
One query → ranked list. No client-side aggregation, no paginated walk.
|
|
51
|
+
|
|
52
|
+
### Cross-resource analysis (raw PG)
|
|
53
|
+
"Show me deal slippage this month"
|
|
54
|
+
```sql
|
|
55
|
+
tl db pg "SELECT a.id, a.send_date, a.publish_status, b.name AS brand, ch.channel_name
|
|
56
|
+
FROM thoughtleaders_adlink a
|
|
57
|
+
JOIN thoughtleaders_adspot s ON a.ad_spot_id = s.id
|
|
58
|
+
JOIN thoughtleaders_channel ch ON s.channel_id = ch.id
|
|
59
|
+
JOIN thoughtleaders_profile p ON a.creator_profile_id = p.id
|
|
60
|
+
JOIN thoughtleaders_profile_brands pb ON p.id = pb.profile_id
|
|
61
|
+
JOIN thoughtleaders_brand b ON pb.brand_id = b.id
|
|
62
|
+
WHERE a.publish_status = 2
|
|
63
|
+
AND a.send_date < CURRENT_DATE
|
|
64
|
+
ORDER BY a.send_date
|
|
65
|
+
LIMIT 100 OFFSET 0"
|
|
66
|
+
```
|
|
67
|
+
Then suggest `tl comments add <id> "..."` for each.
|
|
68
|
+
|
|
69
|
+
### Multi-step research (mix raw + similarity)
|
|
70
|
+
"Find channels similar to the ones Nike sponsors and compare their pricing"
|
|
71
|
+
1. `tl db pg` to find the top channels Nike has sponsored (one aggregation, ranked).
|
|
72
|
+
2. `tl channels similar <top-channel-id> --json --limit 20` per seed — vector KNN is server-side and has no SQL equivalent. The `msn:` filter is tri-state with default `msn:yes` (MSN channels only); use `msn:both` to broaden, `msn:no` for non-MSN only.
|
|
73
|
+
3. Union + dedupe + compile comparison table.
|
|
74
|
+
|
|
75
|
+
### Report comparison (saved reports)
|
|
76
|
+
"Compare Q1 to Q4 performance"
|
|
77
|
+
1. `tl reports --json` → find relevant report ID
|
|
78
|
+
2. `tl reports run <id> --since 2026-01-01 --until 2026-03-31 --json`
|
|
79
|
+
3. `tl reports run <id> --since 2025-10-01 --until 2025-12-31 --json`
|
|
80
|
+
4. Compute deltas and trends
|
|
81
|
+
|
|
82
|
+
### Channel deep dive (one raw query + targeted structured calls)
|
|
83
|
+
"Give me a full picture of channel 12345"
|
|
84
|
+
1. `tl channels show 12345 --json` → profile, scores, demographics (structured — wraps several joins already)
|
|
85
|
+
2. `tl snapshots channel 12345 --json` → growth over time (snapshots wrap interpolation logic)
|
|
86
|
+
3. `tl db pg "SELECT id, send_date, publish_status, price FROM thoughtleaders_adlink WHERE ad_spot_id IN (SELECT id FROM thoughtleaders_adspot WHERE channel_id = 12345) ORDER BY send_date DESC LIMIT 100 OFFSET 0"` — deal history with the columns you actually want, no over-fetch.
|
|
87
|
+
4. `tl uploads list channel:12345 --json` → recent content
|
|
88
|
+
|
|
89
|
+
### Transcript / brand-mention search (raw ES)
|
|
90
|
+
"Where has 'NordVPN' been mentioned organically in the last 90 days?"
|
|
91
|
+
```bash
|
|
92
|
+
tl db es '{"size": 0, "track_total_hits": true,
|
|
93
|
+
"query": {"bool": {"must": [
|
|
94
|
+
{"term": {"organic_brand_mentions": "5612"}},
|
|
95
|
+
{"range": {"publication_date": {"gte": "now-90d"}}}
|
|
96
|
+
]}},
|
|
97
|
+
"aggs": {"by_channel": {"terms": {"field": "channel.id", "size": 50}}}}'
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### Demographics screenshots check (trivially simple — structured)
|
|
101
|
+
"Does channel X have demographics screenshots uploaded?"
|
|
102
|
+
1. `tl channels show <id-or-name> --json` → check `demographics_updated_at`. Non-null = screenshots on file (timestamp = last OCR pass). Null = none uploaded.
|
|
103
|
+
|
|
104
|
+
## Rules
|
|
105
|
+
|
|
106
|
+
- **Always resolve numeric codes to human-readable labels** in your output. Never show "Status 3" — show "Sold". Status mapping: 0=Proposed, 1=Unavailable, 2=Pending, 3=Sold, 4=Rejected by Advertiser, 5=Rejected by Publisher, 6=Proposal Approved, 7=Matched, 8=Reached Out, 9=Rejected by Agency.
|
|
107
|
+
- Always use `--json` for output you need to parse
|
|
108
|
+
- For raw `tl db pg`, prefer one well-targeted query over multiple structured walks; remember the LIMIT/OFFSET injected defaults (LIMIT 50, OFFSET 0) and the OFFSET ≥ 10000 → 403 ceiling.
|
|
109
|
+
- Always include `--limit` on structured list queries to control credit spend
|
|
110
|
+
- For `tl snapshots video`, always include `--channel` (required for Firebolt performance)
|
|
111
|
+
- Present final results as a clear summary with tables when appropriate
|
|
112
|
+
- Show total credits consumed at the end of your analysis
|
|
@@ -7,17 +7,24 @@ description: Quick sponsorship lookup. Query, filter, or show details for sponso
|
|
|
7
7
|
|
|
8
8
|
The user wants to query sponsorships.
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
For **trivially simple lookups** (single ID, one or two filters the structured vocabulary already supports), use `tl sponsorships`:
|
|
11
|
+
1. Run `tl describe show sponsorships --json` to discover filters
|
|
11
12
|
2. Translate the user's request into a `tl sponsorships` command
|
|
12
13
|
3. Execute and present results
|
|
13
14
|
|
|
15
|
+
For **anything non-trivial** — aggregations (totals, group-bys, percentiles), joins (sponsorship + brand + channel + owner), multi-condition filtering the structured filters can't express, or fields the structured commands don't expose (raw `publish_status`, `weighted_price`, `tx_data`, etc.) — drop down to `tl db pg` against `thoughtleaders_adlink`. Run `tl schema pg` first to see the live column list.
|
|
16
|
+
|
|
14
17
|
If no specific request is given, run `tl sponsorships list --limit 10` to show recent sponsorships.
|
|
15
18
|
|
|
16
|
-
Examples:
|
|
19
|
+
Examples (trivial — structured):
|
|
17
20
|
- "/tl-sponsorships pending with send dates in April" → `tl sponsorships list status:pending send-date:2026-04`
|
|
18
21
|
- "/tl-sponsorships Nike" → `tl sponsorships list brand:"Nike"`
|
|
19
22
|
- "/tl-sponsorships sold deals on mobile-first channels" → `tl sponsorships list status:sold primary-device:mobile`
|
|
20
23
|
- "/tl-sponsorships deals on channels with majority US audience" → `tl sponsorships list min-us-share:50`
|
|
21
24
|
- "/tl-sponsorships 12345" → `tl sponsorships show 12345`
|
|
22
25
|
|
|
26
|
+
Examples (non-trivial — raw `tl db pg`):
|
|
27
|
+
- "/tl-sponsorships total weighted pipeline by sales rep" → `tl db pg "SELECT owner_sales_id, SUM(weighted_price) AS pipeline FROM thoughtleaders_adlink WHERE publish_status IN (0,2,6,7,8) GROUP BY owner_sales_id ORDER BY pipeline DESC LIMIT 100 OFFSET 0"`
|
|
28
|
+
- "/tl-sponsorships sold deals this month with brand and channel name" → join `thoughtleaders_adlink` ↔ `adspot` ↔ `channel` ↔ `profile` ↔ `profile_brands` ↔ `brand` (see `references/postgres-schema.md`).
|
|
29
|
+
|
|
23
30
|
`tl sponsorships show <id> --json` returns extended detail fields beyond the list view, including: `impressions_guarantee`, `integration`, `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`.
|
|
@@ -10,16 +10,16 @@ The user wants to query ThoughtLeaders data. Translate their request into the ri
|
|
|
10
10
|
## Steps
|
|
11
11
|
|
|
12
12
|
1. Identify which resource(s) the request is about (sponsorships, deals, channels, brands, uploads, snapshots, reports)
|
|
13
|
-
2.
|
|
14
|
-
3. Translate the user's natural language into a `tl` command
|
|
13
|
+
2. Discover the appropriate database structure, with `tl schema pg` and other commands, and formulate a raw database query solution first. Only use other commands like `tl sponsorships` if the user query is simple enough for it (run `tl describe show sponsorships` to see what it can do).
|
|
14
|
+
3. Translate the user's natural language into a `tl` command
|
|
15
15
|
4. Execute the command
|
|
16
16
|
5. Present results clearly
|
|
17
17
|
|
|
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
|
|
22
|
-
- "/tl mobile-first US cooking channels" → `tl
|
|
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"`
|
|
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`
|
|
@@ -2,12 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
## Context
|
|
4
4
|
|
|
5
|
-
We're building a Python CLI (`tl`) for ThoughtLeaders that lets external customers query their sponsorship data. Inspired by Basecamp's CLI approach — agent-first, pipe-friendly, distributed as both a PyPI package and a Claude Code plugin.
|
|
5
|
+
We're building a Python CLI (`tl`) for ThoughtLeaders that lets external customers query their sponsorship data. Inspired by Basecamp's CLI approach — agent-first, pipe-friendly, distributed as both a PyPI package (`thoughtleaders-cli`) and a Claude Code plugin.
|
|
6
6
|
|
|
7
7
|
The CLI talks to Django API endpoints (not directly to databases) so permissions are enforced server-side and no DB credentials leave our infrastructure.
|
|
8
8
|
|
|
9
9
|
**Design philosophy** (following Basecamp): The CLI is a dumb tool — structured commands are the primary interface. AI intelligence comes from the user's own agent (Claude Code, etc.) using the CLI as a tool. `tl ask` exists as an optional fallback for users without an AI agent. The skill file + `tl describe` teach agents how to use the CLI effectively.
|
|
10
10
|
|
|
11
|
+
**Intended audience: AI agents.** The primary user of `tl` is an AI coding agent (Claude Code, OpenCode) acting on behalf of a human. Every surface — the structured commands, the discovery commands (`tl describe`, `tl schema`), the breadcrumbs in JSON envelopes, the skill file — is shaped to be parseable and self-explanatory to an agent. Where the structured commands hit a ceiling (joins, aggregations, filters they don't expose), we steer agents toward **raw database queries** via `tl db pg|fb|es`. One server-side aggregation beats N client-side roll-ups on cost, latency, and the `from+size = 10000` cap; raw queries are the right tool for heavy data work, not a last-resort escape hatch. The skill file's "When to use raw vs structured" guidance and the live `tl schema pg|fb|es` output are how we teach agents to make this trade-off.
|
|
12
|
+
|
|
11
13
|
**Business model**: Prepaid credit balance. Customers deposit funds, credits are deducted per result returned. Different data types cost different amounts based on value. No subscriptions — pure pay-as-you-go.
|
|
12
14
|
|
|
13
15
|
## Architecture
|
|
@@ -47,22 +49,38 @@ All data commands use explicit subcommands: `list`, `show`, `create`/`add`. Runn
|
|
|
47
49
|
| `tl uploads list [filters...]` | List video uploads (ES) |
|
|
48
50
|
| `tl uploads show <id> [<id>...]` | Show upload detail(s) by ID |
|
|
49
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`). |
|
|
50
|
-
| `tl channels show <id>` |
|
|
51
|
-
| `tl channels
|
|
52
|
+
| `tl channels show <id-or-name>` | Channel detail, including active adspots with price/cost/CPM |
|
|
53
|
+
| `tl channels history <id-or-name>` | Sponsorship history (videos with detected sponsors) |
|
|
54
|
+
| `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. |
|
|
52
55
|
| `tl brands show <brand>` | Brand intelligence report |
|
|
53
|
-
| `tl brands
|
|
56
|
+
| `tl brands history <brand> [--channel <id>]` | Brand sponsorship history; videos where the brand was detected |
|
|
57
|
+
| `tl brands similar <brand>` | Find similar brands (profile vector KNN, 50 credits) |
|
|
54
58
|
| `tl snapshots channel <id>` | Channel metrics over time (Firebolt channel_metrics) |
|
|
55
59
|
| `tl snapshots video <id> --channel <id>` | Video view curve (Firebolt article_metrics, --channel required) |
|
|
56
60
|
| `tl comments list <adlink-id>` | List comments on a sponsorship (free) |
|
|
57
61
|
| `tl comments add <adlink-id> "message"` | Add a comment (free) |
|
|
58
62
|
|
|
63
|
+
### Raw database access (escape hatch for joins / aggregations / complex filters)
|
|
64
|
+
|
|
65
|
+
| Command | Description |
|
|
66
|
+
|---------|-------------|
|
|
67
|
+
| `tl db pg "<SQL>"` | Raw read-only PostgreSQL `SELECT`. LIMIT/OFFSET optional — server fills in `LIMIT 50 OFFSET 0` defaults; explicit OFFSET ≥ 10,000 returns 403. Single statement; sqlglot-validated against an allowlist of functions. |
|
|
68
|
+
| `tl db fb "<SQL>"` | Raw read-only Firebolt `SELECT`. Single-table only; WHERE must filter the leading index column with an equality or `IN` literal. |
|
|
69
|
+
| `tl db es '<json>'` | Raw Elasticsearch search body against the server-fixed index alias. Top-level keys, query types, size/depth limits all gated server-side. |
|
|
70
|
+
| `tl schema pg\|fb\|es` | Print live schema docs for the matching `tl db` engine (free). PG returns the role-scoped tables/columns from `information_schema`; FB returns the live column types of accepted tables; ES returns a maintained Markdown reference. |
|
|
71
|
+
|
|
59
72
|
### Flexible filtering (all list commands)
|
|
60
73
|
Filters are passed as `key:value` pairs after `list`:
|
|
61
74
|
```bash
|
|
62
75
|
tl sponsorships list status:sold brand:"Nike" purchase-date:2026-01
|
|
63
76
|
tl sponsorships list status:pending send-date:2026-03
|
|
64
77
|
tl uploads list channel:12345 type:longform since:2026-03
|
|
65
|
-
tl channels list
|
|
78
|
+
# Channel discovery is raw SQL — the structured `tl channels list` covers
|
|
79
|
+
# only narrow lookups. Default to:
|
|
80
|
+
tl db pg "SELECT id, channel_name, total_views FROM thoughtleaders_channel
|
|
81
|
+
WHERE content_category = <COOKING_CODE> AND language = 'en'
|
|
82
|
+
AND total_views >= 1000000
|
|
83
|
+
ORDER BY total_views DESC LIMIT 50 OFFSET 0"
|
|
66
84
|
```
|
|
67
85
|
|
|
68
86
|
Sponsorship date filtering (on `created-at`, `publish-date`, `purchase-date`, `send-date`) exposes three shapes per field:
|
|
@@ -75,10 +93,10 @@ Ranges combine freely across fields (e.g. "created in Jan that published in Marc
|
|
|
75
93
|
| Command | Description |
|
|
76
94
|
|---------|-------------|
|
|
77
95
|
| `tl describe` | List all resources with credit costs |
|
|
78
|
-
| `tl describe
|
|
79
|
-
| `tl describe
|
|
80
|
-
| `tl describe
|
|
81
|
-
| `tl
|
|
96
|
+
| `tl describe show <resource>` | Show fields, filters, and credit rate for a resource |
|
|
97
|
+
| `tl describe show <resource> --filters` | Just the valid filters |
|
|
98
|
+
| `tl describe show <resource> --fields` | Just the data fields |
|
|
99
|
+
| `tl schema pg\|fb\|es` | Show schema docs for raw `tl db` queries (free) |
|
|
82
100
|
|
|
83
101
|
Schema metadata from server (`GET /api/cli/v1/describe/<resource>`) — always in sync. Includes credit cost per result. Free, no credits charged. Agent-friendly: `tl describe sponsorships --json`.
|
|
84
102
|
|
|
@@ -106,9 +124,11 @@ Schema metadata from server (`GET /api/cli/v1/describe/<resource>`) — always i
|
|
|
106
124
|
| `tl doctor` | Health check (auth, connectivity, version, balance) |
|
|
107
125
|
| `tl whoami` | Show current user, profile, org, and brands (free) |
|
|
108
126
|
| `tl balance` | Show credit balance and recent usage |
|
|
127
|
+
| `tl changelog` | Show release notes |
|
|
128
|
+
| `tl update` | Self-upgrade (when installed via pipx or `uv tool`) |
|
|
109
129
|
|
|
110
130
|
### Global flags (all commands)
|
|
111
|
-
`--json`, `--csv`, `--md`, `--limit N`, `--offset N`
|
|
131
|
+
`--json`, `--csv`, `--md`, `--toon` (token-efficient for LLMs), `--limit N`, `--offset N`
|
|
112
132
|
|
|
113
133
|
### Agent support flag
|
|
114
134
|
`--help --agent` returns structured JSON help (flags, gotchas, subcommands) for any command — optimized for AI agents to parse.
|
|
@@ -125,8 +145,6 @@ Schema metadata from server (`GET /api/cli/v1/describe/<resource>`) — always i
|
|
|
125
145
|
|---------|-------------|
|
|
126
146
|
| `/tl <request>` | Smart router — Claude interprets the request and runs the right `tl` command(s). E.g., `/tl sold sponsorships for Nike in Q1` → `tl sponsorships status:sold brand:"Nike" purchase-date-start:2026-01-01 purchase-date-end:2026-03-31` |
|
|
127
147
|
| `/tl-sponsorships [query]` | Quick sponsorship lookup. E.g., `/tl-sponsorships pending with send dates in April` |
|
|
128
|
-
| `/tl-channels [query]` | Channel search. E.g., `/tl-channels cooking channels over 100k subs` |
|
|
129
|
-
| `/tl-brands [query]` | Brand intelligence. E.g., `/tl-brands Nike` |
|
|
130
148
|
| `/tl-reports` | List and run saved reports |
|
|
131
149
|
| `/tl-balance` | Check credit balance and recent usage |
|
|
132
150
|
|
|
@@ -136,12 +154,13 @@ Each slash command's markdown file instructs Claude to:
|
|
|
136
154
|
3. Execute the command and present results
|
|
137
155
|
4. Show breadcrumbs for follow-up actions
|
|
138
156
|
|
|
139
|
-
#### Skill: `tl
|
|
140
|
-
Teaches Claude how to use the CLI effectively. Triggers on data questions about sponsorships, channels, brands, uploads, metrics.
|
|
157
|
+
#### Skill: `tl`
|
|
158
|
+
Teaches Claude how to use the CLI effectively. Triggers on data questions about sponsorships, channels, brands, uploads, metrics. Authoritative source lives at `skills/tl/SKILL.md` with engine-specific reference files under `skills/tl/references/` (`postgres-schema.md`, `firebolt-schema.md`, `elasticsearch-schema.md`, `business-glossary.md`).
|
|
141
159
|
|
|
142
160
|
Key instructions in the skill:
|
|
143
|
-
- Always run `tl describe show <resource> --json` first to discover fields, filters, and credit costs
|
|
144
|
-
- Use structured commands
|
|
161
|
+
- Always run `tl describe show <resource> --json` (or `tl schema pg|fb|es`) first to discover fields, filters, and credit costs
|
|
162
|
+
- Use structured commands for single-record lookups and simple filtered lists; **switch to `tl db pg|fb|es` for aggregations, joins, and complex filtering**
|
|
163
|
+
- Don't use `tl ask` — the user's Claude IS the AI layer
|
|
145
164
|
- Check `tl balance --json` before expensive queries and warn the user about credit cost
|
|
146
165
|
- Use `--json` output for parsing
|
|
147
166
|
- Chain commands for multi-step analysis (e.g., get brand → find channels → check snapshots)
|
|
@@ -179,12 +198,6 @@ After a `tl` command completes:
|
|
|
179
198
|
- If `balance_remaining` < 500 credits, emits a warning to stderr
|
|
180
199
|
- If command returned 402, provides a clear "deposit more credits" message with link
|
|
181
200
|
|
|
182
|
-
**Stop hook** (`hooks/scripts/session-summary.sh`):
|
|
183
|
-
When Claude finishes a session:
|
|
184
|
-
- Summarizes total credits consumed during the session
|
|
185
|
-
- Shows starting vs ending balance
|
|
186
|
-
- Lists the commands that were run (parsed from session)
|
|
187
|
-
|
|
188
201
|
## Usage Metering & Pricing
|
|
189
202
|
|
|
190
203
|
### Prepaid credit balance
|
|
@@ -194,26 +207,31 @@ When Claude finishes a session:
|
|
|
194
207
|
- Customers can opt-in to allow overage (keep working, settle later) — configurable in dashboard
|
|
195
208
|
- `tl auth status` and `tl balance` show current credit balance
|
|
196
209
|
|
|
197
|
-
### Credit rates
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
| **Brand intelligence** | 5 credits | 8 credits | Core IP — competitive intelligence on who sponsors whom |
|
|
202
|
-
| **Channel** | 3 credits | 5 credits | Rich profile data, demographics, scores |
|
|
203
|
-
| **Sponsorship** | 2 credits | 3 credits | User's own sponsorship data |
|
|
204
|
-
| **Snapshot** (Firebolt) | 1 credit | 1 credit | Time-series data points, high volume |
|
|
205
|
-
| **Upload** (video) | 1 credit | 2 credits | Individual video data |
|
|
206
|
-
| **Report run** | Sum of result credits | — | Charged based on what the report returns |
|
|
207
|
-
| **Comment** | 0 | 0 | Operational — don't charge |
|
|
208
|
-
| **Sponsorship create** | 0 | — | Free — more proposals = more future data consumption |
|
|
209
|
-
| **Describe / auth / doctor** | 0 | 0 | System + discoverability must be free |
|
|
210
|
-
| **`tl ask` surcharge** | +2 credits per result | — | LLM cost surcharge (waived if user provides own key) |
|
|
211
|
-
|
|
212
|
-
### Credit formula per request
|
|
210
|
+
### Credit rates — pricing intent
|
|
211
|
+
|
|
212
|
+
Detail and history endpoints charge a flat `rate × results` (linear). List endpoints (and raw `tl db`) use a non-linear curve so a 0-row pull still pays a small floor and a 500-row pull doesn't pay 500× a 1-row pull:
|
|
213
|
+
|
|
213
214
|
```
|
|
214
|
-
|
|
215
|
+
list cost = setup + mult × scale × n^exp
|
|
215
216
|
```
|
|
216
217
|
|
|
218
|
+
`setup`, `scale`, and `exp` are global shape parameters; `mult` is a per-resource complexity factor (lighter reads use 1.0, raw `tl db` uses 1.4). The live values are in `thoughtleaders/cli_metering.py` (`CLI_LIST_COST_*` settings + `CREDIT_RATES`); the live values are reported by `tl describe`.
|
|
219
|
+
|
|
220
|
+
Rationale per resource — pricing follows data value:
|
|
221
|
+
|
|
222
|
+
| Resource | Why it's priced where it is |
|
|
223
|
+
|----------|-----------------------------|
|
|
224
|
+
| **Brand intelligence** | Core IP — competitive intelligence on who sponsors whom |
|
|
225
|
+
| **Channels** | Rich profile data, demographics, scores |
|
|
226
|
+
| **Sponsorships** | User's own sponsorship data |
|
|
227
|
+
| **Snapshots** (Firebolt) | Time-series data points, high volume → cheap |
|
|
228
|
+
| **Uploads** (video) | Individual video data |
|
|
229
|
+
| **Raw `tl db pg\|fb\|es`** | No role scoping, wider blast radius → 1.4× multiplier |
|
|
230
|
+
| **Reports run** | Charged on what the report returns |
|
|
231
|
+
| **Comments / proposal create** | Operational — free, more proposals = more future data consumption |
|
|
232
|
+
| **Describe / schema / auth / doctor / whoami / balance / changelog** | System + discoverability must be free |
|
|
233
|
+
| **`tl ask` surcharge** | LLM cost surcharge (waived if user provides own key via `--llm-key`) |
|
|
234
|
+
|
|
217
235
|
### Server-side metering implementation
|
|
218
236
|
|
|
219
237
|
**New Django model**: `CliCreditAccount`
|
|
@@ -292,26 +310,29 @@ tl-cli/
|
|
|
292
310
|
│ │ ├── matches.py # tl matches (shortcut: status:match)
|
|
293
311
|
│ │ ├── proposals.py # tl proposals (shortcut: status:proposal)
|
|
294
312
|
│ │ ├── uploads.py # tl uploads (list/show)
|
|
295
|
-
│ │ ├── channels.py
|
|
296
|
-
│ │ ├── brands.py
|
|
297
|
-
│ │ ├── snapshots.py
|
|
298
|
-
│ │ ├── reports.py
|
|
299
|
-
│ │ ├── comments.py
|
|
300
|
-
│ │ ├──
|
|
301
|
-
│ │ ├──
|
|
302
|
-
│ │ ├──
|
|
303
|
-
│ │ ├──
|
|
304
|
-
│ │ ├──
|
|
305
|
-
│ │
|
|
313
|
+
│ │ ├── channels.py # tl channels (list/show/history/similar)
|
|
314
|
+
│ │ ├── brands.py # tl brands (show/history/similar)
|
|
315
|
+
│ │ ├── snapshots.py # tl snapshots (Firebolt metrics)
|
|
316
|
+
│ │ ├── reports.py # tl reports / tl reports run
|
|
317
|
+
│ │ ├── comments.py # tl comments (list/add)
|
|
318
|
+
│ │ ├── db.py # tl db pg|fb|es (raw read-only queries)
|
|
319
|
+
│ │ ├── schema.py # tl schema pg|fb|es (live schema docs)
|
|
320
|
+
│ │ ├── describe.py # tl describe list/show (schema/filter/pricing discovery)
|
|
321
|
+
│ │ ├── changelog.py # tl changelog (release notes)
|
|
322
|
+
│ │ ├── ask.py # tl ask (optional AI fallback)
|
|
323
|
+
│ │ ├── setup.py # tl setup claude / tl setup opencode
|
|
324
|
+
│ │ ├── balance.py # tl balance
|
|
325
|
+
│ │ ├── doctor.py # tl doctor
|
|
326
|
+
│ │ └── whoami.py # tl whoami
|
|
327
|
+
│ ├── self_update.py # tl update (pipx/uv self-upgrade)
|
|
328
|
+
│ ├── hints.py # Inline tips & post-error suggestions
|
|
306
329
|
│ ├── filters.py # key:value filter parser (parsing only)
|
|
307
|
-
│ └── _completions.py
|
|
330
|
+
│ └── _completions.py # Shell completion helpers
|
|
308
331
|
├── .claude-plugin/
|
|
309
332
|
│ └── plugin.json # Claude Code plugin manifest
|
|
310
333
|
├── commands/ # Slash commands for Claude Code
|
|
311
334
|
│ ├── tl.md # /tl — smart router
|
|
312
335
|
│ ├── tl-sponsorships.md # /tl-sponsorships — quick sponsorship lookup
|
|
313
|
-
│ ├── tl-channels.md # /tl-channels — channel search
|
|
314
|
-
│ ├── tl-brands.md # /tl-brands — brand intelligence
|
|
315
336
|
│ ├── tl-reports.md # /tl-reports — saved reports
|
|
316
337
|
│ └── tl-balance.md # /tl-balance — credit balance
|
|
317
338
|
├── skills/
|
|
@@ -323,8 +344,7 @@ tl-cli/
|
|
|
323
344
|
│ ├── hooks.json # Hook event bindings
|
|
324
345
|
│ └── scripts/
|
|
325
346
|
│ ├── pre-check.sh # Auth + credit guard before tl commands
|
|
326
|
-
│
|
|
327
|
-
│ └── session-summary.sh # Credit usage summary on session end
|
|
347
|
+
│ └── post-usage.sh # Low balance warning after tl commands
|
|
328
348
|
├── tests/
|
|
329
349
|
│ ├── __init__.py
|
|
330
350
|
│ ├── test_auth.py
|
|
@@ -334,7 +354,7 @@ tl-cli/
|
|
|
334
354
|
│ └── test_commands.py
|
|
335
355
|
└── .github/
|
|
336
356
|
└── workflows/
|
|
337
|
-
└──
|
|
357
|
+
└── python-publish.yml # PyPI publish on release (Trusted Publishing)
|
|
338
358
|
```
|
|
339
359
|
|
|
340
360
|
### Server Side (in existing thoughtleaders repo)
|
|
@@ -450,17 +470,18 @@ Auth0 token → Django User → Profile → Organization → Plan. Plan types de
|
|
|
450
470
|
```toml
|
|
451
471
|
[project]
|
|
452
472
|
name = "thoughtleaders-cli"
|
|
453
|
-
requires-python = ">=3.
|
|
473
|
+
requires-python = ">=3.12"
|
|
454
474
|
dependencies = [
|
|
455
475
|
"typer[all]>=0.12",
|
|
456
476
|
"rich>=13.0",
|
|
457
477
|
"httpx>=0.27",
|
|
458
478
|
"keyring>=25.0",
|
|
459
479
|
"authlib>=1.3",
|
|
480
|
+
"toon-format>=0.9.0b1",
|
|
460
481
|
]
|
|
461
482
|
```
|
|
462
483
|
|
|
463
|
-
Entry point: `tl = "tl_cli.main:
|
|
484
|
+
Entry point: `tl = "tl_cli.main:cli"`
|
|
464
485
|
|
|
465
486
|
## Auth Strategy
|
|
466
487
|
|
|
@@ -514,7 +535,7 @@ Build each command + its server endpoint together:
|
|
|
514
535
|
|
|
515
536
|
### Step 3: Plugin + commands + agent + hooks
|
|
516
537
|
1. `.claude-plugin/plugin.json`
|
|
517
|
-
2. `commands/` — slash commands (`/tl`, `/tl-sponsorships`, `/tl-
|
|
538
|
+
2. `commands/` — slash commands (`/tl`, `/tl-sponsorships`, `/tl-reports`, `/tl-balance`)
|
|
518
539
|
3. `skills/tl/SKILL.md` — comprehensive skill file
|
|
519
540
|
4. `agents/tl-analyst.md` — multi-step analysis agent
|
|
520
541
|
5. `hooks/` — pre-check, post-usage, session-summary
|