thoughtleaders-cli 0.5.0__tar.gz → 0.5.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.5.2}/.claude-plugin/plugin.json +1 -1
- {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.5.2}/PKG-INFO +4 -6
- {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.5.2}/README.md +3 -5
- thoughtleaders_cli-0.5.2/agents/tl-analyst.md +112 -0
- {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.5.2}/commands/tl-sponsorships.md +9 -2
- {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.5.2}/commands/tl.md +2 -2
- {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.5.2}/docs/architecture.md +74 -58
- {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.5.2}/pyproject.toml +1 -1
- {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.5.2}/skills/tl/SKILL.md +46 -19
- {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.5.2}/src/tl_cli/__init__.py +1 -1
- thoughtleaders_cli-0.5.2/src/tl_cli/commands/doctor.py +134 -0
- {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.5.2}/src/tl_cli/commands/setup.py +0 -2
- {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.5.2}/src/tl_cli/output/formatter.py +48 -6
- thoughtleaders_cli-0.5.2/tests/test_output.py +230 -0
- 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.5.2}/.claude-plugin/marketplace.json +0 -0
- {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.5.2}/.github/workflows/python-publish.yml +0 -0
- {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.5.2}/.gitignore +0 -0
- {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.5.2}/AGENTS.md +0 -0
- {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.5.2}/CLAUDE.md +0 -0
- {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.5.2}/LICENSE +0 -0
- {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.5.2}/commands/tl-balance.md +0 -0
- {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.5.2}/commands/tl-reports.md +0 -0
- {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.5.2}/hooks/hooks.json +0 -0
- {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.5.2}/hooks/scripts/post-usage.sh +0 -0
- {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.5.2}/hooks/scripts/pre-check.sh +0 -0
- {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.5.2}/skills/tl/references/business-glossary.md +0 -0
- {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.5.2}/skills/tl/references/elasticsearch-schema.md +0 -0
- {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.5.2}/skills/tl/references/firebolt-schema.md +0 -0
- {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.5.2}/skills/tl/references/postgres-schema.md +0 -0
- {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.5.2}/src/tl_cli/_completions.py +0 -0
- {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.5.2}/src/tl_cli/auth/__init__.py +0 -0
- {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.5.2}/src/tl_cli/auth/commands.py +0 -0
- {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.5.2}/src/tl_cli/auth/login.py +0 -0
- {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.5.2}/src/tl_cli/auth/pkce.py +0 -0
- {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.5.2}/src/tl_cli/auth/token_store.py +0 -0
- {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.5.2}/src/tl_cli/client/__init__.py +0 -0
- {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.5.2}/src/tl_cli/client/errors.py +0 -0
- {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.5.2}/src/tl_cli/client/http.py +0 -0
- {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.5.2}/src/tl_cli/commands/__init__.py +0 -0
- {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.5.2}/src/tl_cli/commands/ask.py +0 -0
- {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.5.2}/src/tl_cli/commands/balance.py +0 -0
- {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.5.2}/src/tl_cli/commands/brands.py +0 -0
- {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.5.2}/src/tl_cli/commands/changelog.py +0 -0
- {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.5.2}/src/tl_cli/commands/channels.py +0 -0
- {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.5.2}/src/tl_cli/commands/comments.py +0 -0
- {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.5.2}/src/tl_cli/commands/db.py +0 -0
- {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.5.2}/src/tl_cli/commands/deals.py +0 -0
- {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.5.2}/src/tl_cli/commands/describe.py +0 -0
- {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.5.2}/src/tl_cli/commands/matches.py +0 -0
- {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.5.2}/src/tl_cli/commands/proposals.py +0 -0
- {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.5.2}/src/tl_cli/commands/reports.py +0 -0
- {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.5.2}/src/tl_cli/commands/schema.py +0 -0
- {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.5.2}/src/tl_cli/commands/snapshots.py +0 -0
- {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.5.2}/src/tl_cli/commands/sponsorships.py +0 -0
- {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.5.2}/src/tl_cli/commands/uploads.py +0 -0
- {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.5.2}/src/tl_cli/commands/whoami.py +0 -0
- {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.5.2}/src/tl_cli/config.py +0 -0
- {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.5.2}/src/tl_cli/filters.py +0 -0
- {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.5.2}/src/tl_cli/hints.py +0 -0
- {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.5.2}/src/tl_cli/main.py +0 -0
- {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.5.2}/src/tl_cli/output/__init__.py +0 -0
- {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.5.2}/src/tl_cli/self_update.py +0 -0
- {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.5.2}/tests/__init__.py +0 -0
- {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.5.2}/tests/test_auth.py +0 -0
- {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.5.2}/tests/test_filters.py +0 -0
- {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.5.2}/tests/test_sponsorships.py +0 -0
- {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.5.2}/uv.lock +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: thoughtleaders-cli
|
|
3
|
-
Version: 0.5.
|
|
3
|
+
Version: 0.5.2
|
|
4
4
|
Summary: ThoughtLeaders CLI — query sponsorship data, channels, brands, and intelligence
|
|
5
5
|
Project-URL: Homepage, https://thoughtleaders.io
|
|
6
6
|
Project-URL: Repository, https://github.com/ThoughtLeaders-io/thoughtleaders-cli
|
|
@@ -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:
|
|
@@ -173,8 +173,6 @@ Talk naturally in Claude Code:
|
|
|
173
173
|
Resource-specific slash commands:
|
|
174
174
|
```
|
|
175
175
|
/tl-sponsorships pending with send dates in April
|
|
176
|
-
/tl-channels cooking channels over 100k subscribers
|
|
177
|
-
/tl-brands Nike
|
|
178
176
|
/tl-reports run my Q1 pipeline
|
|
179
177
|
/tl-balance
|
|
180
178
|
```
|
|
@@ -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:
|
|
@@ -146,8 +146,6 @@ Talk naturally in Claude Code:
|
|
|
146
146
|
Resource-specific slash commands:
|
|
147
147
|
```
|
|
148
148
|
/tl-sponsorships pending with send dates in April
|
|
149
|
-
/tl-channels cooking channels over 100k subscribers
|
|
150
|
-
/tl-brands Nike
|
|
151
149
|
/tl-reports run my Q1 pipeline
|
|
152
150
|
/tl-balance
|
|
153
151
|
```
|
|
@@ -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,8 +10,8 @@ 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
|
|
|
@@ -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,15 +49,26 @@ 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
|
|
@@ -75,10 +88,10 @@ Ranges combine freely across fields (e.g. "created in Jan that published in Marc
|
|
|
75
88
|
| Command | Description |
|
|
76
89
|
|---------|-------------|
|
|
77
90
|
| `tl describe` | List all resources with credit costs |
|
|
78
|
-
| `tl describe
|
|
79
|
-
| `tl describe
|
|
80
|
-
| `tl describe
|
|
81
|
-
| `tl
|
|
91
|
+
| `tl describe show <resource>` | Show fields, filters, and credit rate for a resource |
|
|
92
|
+
| `tl describe show <resource> --filters` | Just the valid filters |
|
|
93
|
+
| `tl describe show <resource> --fields` | Just the data fields |
|
|
94
|
+
| `tl schema pg\|fb\|es` | Show schema docs for raw `tl db` queries (free) |
|
|
82
95
|
|
|
83
96
|
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
97
|
|
|
@@ -106,9 +119,11 @@ Schema metadata from server (`GET /api/cli/v1/describe/<resource>`) — always i
|
|
|
106
119
|
| `tl doctor` | Health check (auth, connectivity, version, balance) |
|
|
107
120
|
| `tl whoami` | Show current user, profile, org, and brands (free) |
|
|
108
121
|
| `tl balance` | Show credit balance and recent usage |
|
|
122
|
+
| `tl changelog` | Show release notes |
|
|
123
|
+
| `tl update` | Self-upgrade (when installed via pipx or `uv tool`) |
|
|
109
124
|
|
|
110
125
|
### Global flags (all commands)
|
|
111
|
-
`--json`, `--csv`, `--md`, `--limit N`, `--offset N`
|
|
126
|
+
`--json`, `--csv`, `--md`, `--toon` (token-efficient for LLMs), `--limit N`, `--offset N`
|
|
112
127
|
|
|
113
128
|
### Agent support flag
|
|
114
129
|
`--help --agent` returns structured JSON help (flags, gotchas, subcommands) for any command — optimized for AI agents to parse.
|
|
@@ -125,8 +140,6 @@ Schema metadata from server (`GET /api/cli/v1/describe/<resource>`) — always i
|
|
|
125
140
|
|---------|-------------|
|
|
126
141
|
| `/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
142
|
| `/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
143
|
| `/tl-reports` | List and run saved reports |
|
|
131
144
|
| `/tl-balance` | Check credit balance and recent usage |
|
|
132
145
|
|
|
@@ -136,12 +149,13 @@ Each slash command's markdown file instructs Claude to:
|
|
|
136
149
|
3. Execute the command and present results
|
|
137
150
|
4. Show breadcrumbs for follow-up actions
|
|
138
151
|
|
|
139
|
-
#### Skill: `tl
|
|
140
|
-
Teaches Claude how to use the CLI effectively. Triggers on data questions about sponsorships, channels, brands, uploads, metrics.
|
|
152
|
+
#### Skill: `tl`
|
|
153
|
+
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
154
|
|
|
142
155
|
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
|
|
156
|
+
- Always run `tl describe show <resource> --json` (or `tl schema pg|fb|es`) first to discover fields, filters, and credit costs
|
|
157
|
+
- Use structured commands for single-record lookups and simple filtered lists; **switch to `tl db pg|fb|es` for aggregations, joins, and complex filtering**
|
|
158
|
+
- Don't use `tl ask` — the user's Claude IS the AI layer
|
|
145
159
|
- Check `tl balance --json` before expensive queries and warn the user about credit cost
|
|
146
160
|
- Use `--json` output for parsing
|
|
147
161
|
- Chain commands for multi-step analysis (e.g., get brand → find channels → check snapshots)
|
|
@@ -179,12 +193,6 @@ After a `tl` command completes:
|
|
|
179
193
|
- If `balance_remaining` < 500 credits, emits a warning to stderr
|
|
180
194
|
- If command returned 402, provides a clear "deposit more credits" message with link
|
|
181
195
|
|
|
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
196
|
## Usage Metering & Pricing
|
|
189
197
|
|
|
190
198
|
### Prepaid credit balance
|
|
@@ -194,26 +202,31 @@ When Claude finishes a session:
|
|
|
194
202
|
- Customers can opt-in to allow overage (keep working, settle later) — configurable in dashboard
|
|
195
203
|
- `tl auth status` and `tl balance` show current credit balance
|
|
196
204
|
|
|
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
|
|
205
|
+
### Credit rates — pricing intent
|
|
206
|
+
|
|
207
|
+
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:
|
|
208
|
+
|
|
213
209
|
```
|
|
214
|
-
|
|
210
|
+
list cost = setup + mult × scale × n^exp
|
|
215
211
|
```
|
|
216
212
|
|
|
213
|
+
`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`.
|
|
214
|
+
|
|
215
|
+
Rationale per resource — pricing follows data value:
|
|
216
|
+
|
|
217
|
+
| Resource | Why it's priced where it is |
|
|
218
|
+
|----------|-----------------------------|
|
|
219
|
+
| **Brand intelligence** | Core IP — competitive intelligence on who sponsors whom |
|
|
220
|
+
| **Channels** | Rich profile data, demographics, scores |
|
|
221
|
+
| **Sponsorships** | User's own sponsorship data |
|
|
222
|
+
| **Snapshots** (Firebolt) | Time-series data points, high volume → cheap |
|
|
223
|
+
| **Uploads** (video) | Individual video data |
|
|
224
|
+
| **Raw `tl db pg\|fb\|es`** | No role scoping, wider blast radius → 1.4× multiplier |
|
|
225
|
+
| **Reports run** | Charged on what the report returns |
|
|
226
|
+
| **Comments / proposal create** | Operational — free, more proposals = more future data consumption |
|
|
227
|
+
| **Describe / schema / auth / doctor / whoami / balance / changelog** | System + discoverability must be free |
|
|
228
|
+
| **`tl ask` surcharge** | LLM cost surcharge (waived if user provides own key via `--llm-key`) |
|
|
229
|
+
|
|
217
230
|
### Server-side metering implementation
|
|
218
231
|
|
|
219
232
|
**New Django model**: `CliCreditAccount`
|
|
@@ -292,26 +305,29 @@ tl-cli/
|
|
|
292
305
|
│ │ ├── matches.py # tl matches (shortcut: status:match)
|
|
293
306
|
│ │ ├── proposals.py # tl proposals (shortcut: status:proposal)
|
|
294
307
|
│ │ ├── 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
|
-
│ │
|
|
308
|
+
│ │ ├── channels.py # tl channels (list/show/history/similar)
|
|
309
|
+
│ │ ├── brands.py # tl brands (show/history/similar)
|
|
310
|
+
│ │ ├── snapshots.py # tl snapshots (Firebolt metrics)
|
|
311
|
+
│ │ ├── reports.py # tl reports / tl reports run
|
|
312
|
+
│ │ ├── comments.py # tl comments (list/add)
|
|
313
|
+
│ │ ├── db.py # tl db pg|fb|es (raw read-only queries)
|
|
314
|
+
│ │ ├── schema.py # tl schema pg|fb|es (live schema docs)
|
|
315
|
+
│ │ ├── describe.py # tl describe list/show (schema/filter/pricing discovery)
|
|
316
|
+
│ │ ├── changelog.py # tl changelog (release notes)
|
|
317
|
+
│ │ ├── ask.py # tl ask (optional AI fallback)
|
|
318
|
+
│ │ ├── setup.py # tl setup claude / tl setup opencode
|
|
319
|
+
│ │ ├── balance.py # tl balance
|
|
320
|
+
│ │ ├── doctor.py # tl doctor
|
|
321
|
+
│ │ └── whoami.py # tl whoami
|
|
322
|
+
│ ├── self_update.py # tl update (pipx/uv self-upgrade)
|
|
323
|
+
│ ├── hints.py # Inline tips & post-error suggestions
|
|
306
324
|
│ ├── filters.py # key:value filter parser (parsing only)
|
|
307
|
-
│ └── _completions.py
|
|
325
|
+
│ └── _completions.py # Shell completion helpers
|
|
308
326
|
├── .claude-plugin/
|
|
309
327
|
│ └── plugin.json # Claude Code plugin manifest
|
|
310
328
|
├── commands/ # Slash commands for Claude Code
|
|
311
329
|
│ ├── tl.md # /tl — smart router
|
|
312
330
|
│ ├── 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
331
|
│ ├── tl-reports.md # /tl-reports — saved reports
|
|
316
332
|
│ └── tl-balance.md # /tl-balance — credit balance
|
|
317
333
|
├── skills/
|
|
@@ -323,8 +339,7 @@ tl-cli/
|
|
|
323
339
|
│ ├── hooks.json # Hook event bindings
|
|
324
340
|
│ └── scripts/
|
|
325
341
|
│ ├── pre-check.sh # Auth + credit guard before tl commands
|
|
326
|
-
│
|
|
327
|
-
│ └── session-summary.sh # Credit usage summary on session end
|
|
342
|
+
│ └── post-usage.sh # Low balance warning after tl commands
|
|
328
343
|
├── tests/
|
|
329
344
|
│ ├── __init__.py
|
|
330
345
|
│ ├── test_auth.py
|
|
@@ -334,7 +349,7 @@ tl-cli/
|
|
|
334
349
|
│ └── test_commands.py
|
|
335
350
|
└── .github/
|
|
336
351
|
└── workflows/
|
|
337
|
-
└──
|
|
352
|
+
└── python-publish.yml # PyPI publish on release (Trusted Publishing)
|
|
338
353
|
```
|
|
339
354
|
|
|
340
355
|
### Server Side (in existing thoughtleaders repo)
|
|
@@ -450,17 +465,18 @@ Auth0 token → Django User → Profile → Organization → Plan. Plan types de
|
|
|
450
465
|
```toml
|
|
451
466
|
[project]
|
|
452
467
|
name = "thoughtleaders-cli"
|
|
453
|
-
requires-python = ">=3.
|
|
468
|
+
requires-python = ">=3.12"
|
|
454
469
|
dependencies = [
|
|
455
470
|
"typer[all]>=0.12",
|
|
456
471
|
"rich>=13.0",
|
|
457
472
|
"httpx>=0.27",
|
|
458
473
|
"keyring>=25.0",
|
|
459
474
|
"authlib>=1.3",
|
|
475
|
+
"toon-format>=0.9.0b1",
|
|
460
476
|
]
|
|
461
477
|
```
|
|
462
478
|
|
|
463
|
-
Entry point: `tl = "tl_cli.main:
|
|
479
|
+
Entry point: `tl = "tl_cli.main:cli"`
|
|
464
480
|
|
|
465
481
|
## Auth Strategy
|
|
466
482
|
|
|
@@ -514,7 +530,7 @@ Build each command + its server endpoint together:
|
|
|
514
530
|
|
|
515
531
|
### Step 3: Plugin + commands + agent + hooks
|
|
516
532
|
1. `.claude-plugin/plugin.json`
|
|
517
|
-
2. `commands/` — slash commands (`/tl`, `/tl-sponsorships`, `/tl-
|
|
533
|
+
2. `commands/` — slash commands (`/tl`, `/tl-sponsorships`, `/tl-reports`, `/tl-balance`)
|
|
518
534
|
3. `skills/tl/SKILL.md` — comprehensive skill file
|
|
519
535
|
4. `agents/tl-analyst.md` — multi-step analysis agent
|
|
520
536
|
5. `hooks/` — pre-check, post-usage, session-summary
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: tl
|
|
3
|
-
description: Query and analyze ThoughtLeaders business data using the `tl` CLI
|
|
3
|
+
description: Query and analyze ThoughtLeaders business data using the `tl` CLI. Default to raw database queries via `tl db pg|fb|es` for anything non-trivial (joins, aggregations, multi-condition filters, anything that would otherwise need post-processing); use the structured resource commands (sponsorships, deals, channels, brands, uploads, snapshots, reports) only for trivially simple lookups (single-record show by ID, plain filtered lists). Triggers on questions about deals, sponsorships, pipeline, revenue, brands, channels, MSN, TPP, uploads/videos, transcripts, brand mentions, view-curves, sales numbers, reports, or any cross-source business analysis ("how many deals", "pipeline report", "weighted pipeline", "channel data", "brand lookup", "view curve", "find mentions of", "investigate this video", "query the database"). You ARE the AI layer — do not use `tl ask`.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# ThoughtLeaders Data Analyst
|
|
@@ -9,9 +9,35 @@ You have access to the `tl` CLI which queries ThoughtLeaders' sponsorship platfo
|
|
|
9
9
|
|
|
10
10
|
## Core Principles
|
|
11
11
|
|
|
12
|
-
**You are the intelligence layer.**
|
|
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.
|
|
13
13
|
|
|
14
|
-
|
|
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.
|
|
15
|
+
|
|
16
|
+
Decision rule:
|
|
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
|
+
**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:
|
|
24
|
+
|
|
25
|
+
- **`jq`** — filter, project, and transform JSON. The default for `tl … --json` post-processing.
|
|
26
|
+
```bash
|
|
27
|
+
tl sponsorships list status:sold --json | jq '.results[] | select(.price > 5000) | {id, brand, price}'
|
|
28
|
+
```
|
|
29
|
+
- **`yq`** — same idea for YAML/TOML, useful when reading config files or `--md` blocks.
|
|
30
|
+
- **`rg`** — fast text search across CLI output, transcripts, and the codebase. Better than `grep` for searching large `--csv` exports or transcript dumps.
|
|
31
|
+
```bash
|
|
32
|
+
tl db es '{"size":500,"query":{"term":{"channel.id":5607}},"_source":["id","transcript"]}' --json | rg -o "NordVPN[^.]*"
|
|
33
|
+
```
|
|
34
|
+
- **`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.
|
|
35
|
+
```bash
|
|
36
|
+
tl deals list purchase-date-start:2026-01 --csv > deals.csv
|
|
37
|
+
duckdb -c "SELECT brand, SUM(price) AS revenue FROM 'deals.csv' GROUP BY brand ORDER BY revenue DESC LIMIT 10"
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
The pattern is always: server-side narrowing first (filter in the `tl db` query or the structured filters), 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.
|
|
15
41
|
|
|
16
42
|
Always assume there will be more than 1 page of results. You MUST always use `--limit` and `--offset` options in the `tl list` commands to retrieve the entire data set (all pages, until the total records are fetched). You must also always use pagination in scripts you write to collect results. The maximum number of results per page is 500.
|
|
17
43
|
|
|
@@ -135,7 +161,7 @@ The marginal per-row cost is exactly proportional to `mult` — a 1.4× resource
|
|
|
135
161
|
|
|
136
162
|
### Raw queries (`tl db`)
|
|
137
163
|
|
|
138
|
-
|
|
164
|
+
`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.
|
|
139
165
|
|
|
140
166
|
```bash
|
|
141
167
|
tl db pg "<SELECT ...>" # PostgreSQL — read-only SELECT
|
|
@@ -145,27 +171,28 @@ tl db es "<JSON body>" # Elasticsearch — search bodies against the server
|
|
|
145
171
|
|
|
146
172
|
All three honour `--json`/`--csv`/`--md`/`--toon` and accept `-` to read from stdin (`cat q.sql | tl db fb -`). They share the list-curve at `mult=1.4` (raw queries, no role scoping, wider blast radius).
|
|
147
173
|
|
|
148
|
-
|
|
174
|
+
Reasons to write a raw query (the common case):
|
|
149
175
|
|
|
150
|
-
- **Aggregations** (counts, sums, avgs, group-bys, percentiles, time histograms) —
|
|
151
|
-
- **Joins
|
|
152
|
-
- **
|
|
176
|
+
- **Aggregations** (counts, sums, avgs, group-bys, percentiles, time histograms) — one `tl db pg` `GROUP BY` or `tl db es` agg body, not a paginated walk + Python reduce.
|
|
177
|
+
- **Joins / cross-table data** — `tl db pg` returns brand+channel+deal in one row instead of two structured walks stitched in `jq`.
|
|
178
|
+
- **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.
|
|
179
|
+
- **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.
|
|
153
180
|
|
|
154
|
-
Structured commands
|
|
181
|
+
Structured commands are still the right tool for: single-record `show` by ID, plain filtered `list` (one or two filters that the structured vocabulary already supports), saved `tl reports run`, and `tl snapshots channel|video` (these wrap interpolation logic you'd otherwise reimplement).
|
|
155
182
|
|
|
156
183
|
| Need | Use |
|
|
157
184
|
|---|---|
|
|
158
|
-
|
|
|
159
|
-
|
|
|
160
|
-
|
|
|
185
|
+
| **Aggregations** (counts, sums, group-by, histograms, percentiles) | **`tl db pg` `GROUP BY`** or **`tl db es` agg query** |
|
|
186
|
+
| **Joins / cross-table data** | **`tl db pg`** |
|
|
187
|
+
| **Multi-condition filtering** the structured filters can't express | **`tl db pg` / `tl db es`** |
|
|
188
|
+
| **Fields the structured commands don't expose** (raw `publish_status`, `weighted_price`, `media_selling_network_join_date`, etc.) | **`tl db pg`** |
|
|
189
|
+
| Transcript / brand-mention search inside video content | **`tl db es`** (no structured equivalent for content text) |
|
|
190
|
+
| Custom Firebolt shape (milestone-age slices, multi-channel growth comparisons) | **`tl db fb`** |
|
|
191
|
+
| Single-record detail lookup by ID | `tl <resource> show <id>` |
|
|
192
|
+
| Plain filtered list with one or two simple filters | `tl <resource> list` |
|
|
193
|
+
| Channel/brand similarity (vector KNN, server-implemented) | `tl channels similar`, `tl brands similar` |
|
|
161
194
|
| Saved reports | `tl reports`, `tl reports run` |
|
|
162
|
-
| Time-series view-curve / channel growth (default shape) | `tl snapshots channel`, `tl snapshots video` |
|
|
163
|
-
| **Aggregations** (counts, sums, group-by, histograms, percentiles) | **`tl db es` agg query** or **`tl db pg` `GROUP BY`** — do not paginate-and-roll-up client-side |
|
|
164
|
-
| **Joins / cross-table data** | **`tl db pg`** — single SQL query beats two paginated structured walks |
|
|
165
|
-
| **Complex filtering** the structured filters can't express | **`tl db pg` / `tl db es`** rather than over-fetching and post-filtering |
|
|
166
|
-
| Transcript / brand-mention search inside video content | `tl db es` (no structured equivalent for content text) |
|
|
167
|
-
| Custom Firebolt shape (milestone-age slices, multi-channel growth comparisons) | `tl db fb` |
|
|
168
|
-
| Anything requiring a Postgres column the structured commands don't expose | `tl db pg` |
|
|
195
|
+
| Time-series view-curve / channel growth (default shape with interpolation) | `tl snapshots channel`, `tl snapshots video` |
|
|
169
196
|
|
|
170
197
|
#### `tl db es` — Elasticsearch
|
|
171
198
|
|