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.
Files changed (72) hide show
  1. {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.5.2}/.claude-plugin/plugin.json +1 -1
  2. {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.5.2}/PKG-INFO +4 -6
  3. {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.5.2}/README.md +3 -5
  4. thoughtleaders_cli-0.5.2/agents/tl-analyst.md +112 -0
  5. {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.5.2}/commands/tl-sponsorships.md +9 -2
  6. {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.5.2}/commands/tl.md +2 -2
  7. {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.5.2}/docs/architecture.md +74 -58
  8. {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.5.2}/pyproject.toml +1 -1
  9. {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.5.2}/skills/tl/SKILL.md +46 -19
  10. {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.5.2}/src/tl_cli/__init__.py +1 -1
  11. thoughtleaders_cli-0.5.2/src/tl_cli/commands/doctor.py +134 -0
  12. {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.5.2}/src/tl_cli/commands/setup.py +0 -2
  13. {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.5.2}/src/tl_cli/output/formatter.py +48 -6
  14. thoughtleaders_cli-0.5.2/tests/test_output.py +230 -0
  15. thoughtleaders_cli-0.5.0/agents/tl-analyst.md +0 -66
  16. thoughtleaders_cli-0.5.0/commands/tl-brands.md +0 -16
  17. thoughtleaders_cli-0.5.0/commands/tl-channels.md +0 -31
  18. thoughtleaders_cli-0.5.0/src/tl_cli/commands/doctor.py +0 -70
  19. thoughtleaders_cli-0.5.0/tests/test_output.py +0 -19
  20. {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.5.2}/.claude-plugin/marketplace.json +0 -0
  21. {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.5.2}/.github/workflows/python-publish.yml +0 -0
  22. {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.5.2}/.gitignore +0 -0
  23. {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.5.2}/AGENTS.md +0 -0
  24. {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.5.2}/CLAUDE.md +0 -0
  25. {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.5.2}/LICENSE +0 -0
  26. {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.5.2}/commands/tl-balance.md +0 -0
  27. {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.5.2}/commands/tl-reports.md +0 -0
  28. {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.5.2}/hooks/hooks.json +0 -0
  29. {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.5.2}/hooks/scripts/post-usage.sh +0 -0
  30. {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.5.2}/hooks/scripts/pre-check.sh +0 -0
  31. {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.5.2}/skills/tl/references/business-glossary.md +0 -0
  32. {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.5.2}/skills/tl/references/elasticsearch-schema.md +0 -0
  33. {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.5.2}/skills/tl/references/firebolt-schema.md +0 -0
  34. {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.5.2}/skills/tl/references/postgres-schema.md +0 -0
  35. {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.5.2}/src/tl_cli/_completions.py +0 -0
  36. {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.5.2}/src/tl_cli/auth/__init__.py +0 -0
  37. {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.5.2}/src/tl_cli/auth/commands.py +0 -0
  38. {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.5.2}/src/tl_cli/auth/login.py +0 -0
  39. {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.5.2}/src/tl_cli/auth/pkce.py +0 -0
  40. {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.5.2}/src/tl_cli/auth/token_store.py +0 -0
  41. {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.5.2}/src/tl_cli/client/__init__.py +0 -0
  42. {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.5.2}/src/tl_cli/client/errors.py +0 -0
  43. {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.5.2}/src/tl_cli/client/http.py +0 -0
  44. {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.5.2}/src/tl_cli/commands/__init__.py +0 -0
  45. {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.5.2}/src/tl_cli/commands/ask.py +0 -0
  46. {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.5.2}/src/tl_cli/commands/balance.py +0 -0
  47. {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.5.2}/src/tl_cli/commands/brands.py +0 -0
  48. {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.5.2}/src/tl_cli/commands/changelog.py +0 -0
  49. {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.5.2}/src/tl_cli/commands/channels.py +0 -0
  50. {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.5.2}/src/tl_cli/commands/comments.py +0 -0
  51. {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.5.2}/src/tl_cli/commands/db.py +0 -0
  52. {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.5.2}/src/tl_cli/commands/deals.py +0 -0
  53. {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.5.2}/src/tl_cli/commands/describe.py +0 -0
  54. {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.5.2}/src/tl_cli/commands/matches.py +0 -0
  55. {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.5.2}/src/tl_cli/commands/proposals.py +0 -0
  56. {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.5.2}/src/tl_cli/commands/reports.py +0 -0
  57. {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.5.2}/src/tl_cli/commands/schema.py +0 -0
  58. {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.5.2}/src/tl_cli/commands/snapshots.py +0 -0
  59. {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.5.2}/src/tl_cli/commands/sponsorships.py +0 -0
  60. {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.5.2}/src/tl_cli/commands/uploads.py +0 -0
  61. {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.5.2}/src/tl_cli/commands/whoami.py +0 -0
  62. {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.5.2}/src/tl_cli/config.py +0 -0
  63. {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.5.2}/src/tl_cli/filters.py +0 -0
  64. {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.5.2}/src/tl_cli/hints.py +0 -0
  65. {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.5.2}/src/tl_cli/main.py +0 -0
  66. {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.5.2}/src/tl_cli/output/__init__.py +0 -0
  67. {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.5.2}/src/tl_cli/self_update.py +0 -0
  68. {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.5.2}/tests/__init__.py +0 -0
  69. {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.5.2}/tests/test_auth.py +0 -0
  70. {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.5.2}/tests/test_filters.py +0 -0
  71. {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.5.2}/tests/test_sponsorships.py +0 -0
  72. {thoughtleaders_cli-0.5.0 → thoughtleaders_cli-0.5.2}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tl-cli",
3
- "version": "0.5.0",
3
+ "version": "0.5.2",
4
4
  "description": "ThoughtLeaders CLI — query sponsorship deals, channels, brands, uploads, and intelligence from the terminal",
5
5
  "author": {
6
6
  "name": "ThoughtLeaders",
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: thoughtleaders-cli
3
- Version: 0.5.0
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 git+https://github.com/ThoughtLeaders-io/thoughtleaders-cli.git
46
+ pipx install thoughtleaders-cli
47
47
  # or
48
- uv tool install git+https://github.com/ThoughtLeaders-io/thoughtleaders-cli.git
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 git+https://github.com/ThoughtLeaders-io/thoughtleaders-cli.git
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 git+https://github.com/ThoughtLeaders-io/thoughtleaders-cli.git
19
+ pipx install thoughtleaders-cli
20
20
  # or
21
- uv tool install git+https://github.com/ThoughtLeaders-io/thoughtleaders-cli.git
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 git+https://github.com/ThoughtLeaders-io/thoughtleaders-cli.git
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
- 1. Run `tl describe sponsorships --json` to discover filters
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. Run `tl describe show <resource> --json` to discover available filters
14
- 3. Translate the user's natural language into a `tl` command with appropriate filters
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>` | Show channel detail, including active adspots with price/cost/CPM |
51
- | `tl channels similar <id-or-name>` | Vector-similarity recommender. 50 credits; Intelligence plan. Tri-state `msn:` filter (default `yes`) and `tpp:` filter (default `both`) — each takes `yes` / `no` / `both`. Ambiguous names return 400 + candidates list. Hidden `look-alike` alias. |
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 show <brand> --channel <id>` | Brand mentions on a specific channel |
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 sponsorships` | Show fields, filters, and credit rate for sponsorships |
79
- | `tl describe sponsorships --filters` | Just the valid filters |
80
- | `tl describe sponsorships --fields` | Just the data fields |
81
- | `tl docs` | Open full docs in browser |
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-data-analyst`
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, not `tl ask` (the user's Claude IS the AI layer)
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 by data value
198
-
199
- | Resource | List (per result) | Detail (single) | Rationale |
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
- credits = sum(results × rate_per_result) + surcharge_if_ask
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 # tl channels (list/show)
296
- │ │ ├── brands.py # tl brands show (brand intelligence)
297
- │ │ ├── snapshots.py # tl snapshots (Firebolt metrics)
298
- │ │ ├── reports.py # tl reports / tl reports run
299
- │ │ ├── comments.py # tl comments (list/add)
300
- │ │ ├── describe.py # tl describe list/show (schema/filter/pricing discovery)
301
- │ │ ├── ask.py # tl ask (optional AI fallback)
302
- │ │ ├── setup.py # tl setup claude / tl setup opencode
303
- │ │ ├── balance.py # tl balance
304
- │ │ ├── doctor.py # tl doctor
305
- │ │ └── whoami.py # tl whoami
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 # Shell completion helpers
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
- ├── post-usage.sh # Low balance warning after tl commands
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
- └── release.yml # PyPI publish on tag
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.10"
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:app"`
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-channels`, `/tl-brands`, `/tl-reports`, `/tl-balance`)
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
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "thoughtleaders-cli"
7
- version = "0.5.0"
7
+ version = "0.5.2"
8
8
  description = "ThoughtLeaders CLI — query sponsorship data, channels, brands, and intelligence"
9
9
  readme = "README.md"
10
10
  license = "MIT"
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: tl
3
- description: Query and analyze ThoughtLeaders business data using the `tl` CLI structured resource commands (sponsorships, deals, channels, brands, uploads, snapshots, reports) plus raw PostgreSQL / Elasticsearch / Firebolt access via `tl db pg|fb|es`. 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"). Use structured tl commands — you ARE the AI layer, not tl ask.
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.** Use structured `tl` commands, not `tl ask`. The `tl ask` command is a server-side LLM fallback for users without Claude but the user has you. Translate their questions into the right `tl` commands.
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
- Always run `tl describe show <resource>` before running a query against that resource. For raw-db queries, use `tl schema pg|fb|es` to get the database schemas.
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
- When a structured `tl <resource>` command can answer the question, prefer it authoritative, role-scoped, paginated, breadcrumbed. Drop down to `tl db pg|fb|es` only when the high-level command can't express what you need.
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
- **Reach for raw queries don't simulate them client-side.** The structured `list` commands are great for filtered records. The moment the question turns into **aggregation, joining, or complex multi-condition filtering**, switch to `tl db`:
174
+ Reasons to write a raw query (the common case):
149
175
 
150
- - **Aggregations** (counts, sums, avgs, group-bys, percentiles, time histograms) — push them into a single `tl db es` agg query or `tl db pg` `GROUP BY`. One server-side aggregation is faster, cheaper (one call vs N pages), and avoids the `from+size=10000` deep-pagination cap in ES.
151
- - **Joins** "X plus the related Y" belongs in `tl db pg` once it ships. Until then, doing the join client-side via two paginated structured walks is a workaround — flag it as such.
152
- - **Complex filtering** the structured filter vocabulary can't express (compound boolean, `NOT IN`/`EXISTS`, `WHERE col IS NULL` on hidden fields, mixed range + enum + text predicates) write it as one query rather than over-fetching and post-filtering.
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 stay best for: single-record lookups (`tl <resource> show`), role-scoped filtered lists with simple filters, and anything where breadcrumbs/role-scoping matter.
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
- | Single-record detail lookup | `tl <resource> show <id>` |
159
- | Simple filtered list of records | Structured `tl <resource> list` |
160
- | Channel/brand similarity, history | `tl channels similar / history`, `tl brands similar / history` |
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
 
@@ -1,3 +1,3 @@
1
1
  """ThoughtLeaders CLI — query sponsorship data, channels, brands, and intelligence."""
2
2
 
3
- __version__ = "0.5.0"
3
+ __version__ = "0.5.2"