thoughtleaders-cli 0.6.3__tar.gz → 0.6.4__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 (68) hide show
  1. {thoughtleaders_cli-0.6.3 → thoughtleaders_cli-0.6.4}/.claude-plugin/plugin.json +1 -1
  2. {thoughtleaders_cli-0.6.3 → thoughtleaders_cli-0.6.4}/PKG-INFO +1 -1
  3. {thoughtleaders_cli-0.6.3 → thoughtleaders_cli-0.6.4}/pyproject.toml +1 -1
  4. {thoughtleaders_cli-0.6.3 → thoughtleaders_cli-0.6.4}/skills/tl/references/elasticsearch-schema.md +15 -1
  5. {thoughtleaders_cli-0.6.3 → thoughtleaders_cli-0.6.4}/skills/tl/references/postgres-schema.md +62 -2
  6. {thoughtleaders_cli-0.6.3 → thoughtleaders_cli-0.6.4}/src/tl_cli/__init__.py +1 -1
  7. {thoughtleaders_cli-0.6.3 → thoughtleaders_cli-0.6.4}/uv.lock +1 -1
  8. {thoughtleaders_cli-0.6.3 → thoughtleaders_cli-0.6.4}/.claude-plugin/marketplace.json +0 -0
  9. {thoughtleaders_cli-0.6.3 → thoughtleaders_cli-0.6.4}/.github/workflows/python-publish.yml +0 -0
  10. {thoughtleaders_cli-0.6.3 → thoughtleaders_cli-0.6.4}/.gitignore +0 -0
  11. {thoughtleaders_cli-0.6.3 → thoughtleaders_cli-0.6.4}/AGENTS.md +0 -0
  12. {thoughtleaders_cli-0.6.3 → thoughtleaders_cli-0.6.4}/CLAUDE.md +0 -0
  13. {thoughtleaders_cli-0.6.3 → thoughtleaders_cli-0.6.4}/LICENSE +0 -0
  14. {thoughtleaders_cli-0.6.3 → thoughtleaders_cli-0.6.4}/README.md +0 -0
  15. {thoughtleaders_cli-0.6.3 → thoughtleaders_cli-0.6.4}/agents/tl-analyst.md +0 -0
  16. {thoughtleaders_cli-0.6.3 → thoughtleaders_cli-0.6.4}/commands/tl-balance.md +0 -0
  17. {thoughtleaders_cli-0.6.3 → thoughtleaders_cli-0.6.4}/commands/tl-reports.md +0 -0
  18. {thoughtleaders_cli-0.6.3 → thoughtleaders_cli-0.6.4}/commands/tl-sponsorships.md +0 -0
  19. {thoughtleaders_cli-0.6.3 → thoughtleaders_cli-0.6.4}/commands/tl.md +0 -0
  20. {thoughtleaders_cli-0.6.3 → thoughtleaders_cli-0.6.4}/docs/architecture.md +0 -0
  21. {thoughtleaders_cli-0.6.3 → thoughtleaders_cli-0.6.4}/hooks/hooks.json +0 -0
  22. {thoughtleaders_cli-0.6.3 → thoughtleaders_cli-0.6.4}/hooks/scripts/post-usage.sh +0 -0
  23. {thoughtleaders_cli-0.6.3 → thoughtleaders_cli-0.6.4}/hooks/scripts/pre-check.sh +0 -0
  24. {thoughtleaders_cli-0.6.3 → thoughtleaders_cli-0.6.4}/skills/tl/SKILL.md +0 -0
  25. {thoughtleaders_cli-0.6.3 → thoughtleaders_cli-0.6.4}/skills/tl/references/business-glossary.md +0 -0
  26. {thoughtleaders_cli-0.6.3 → thoughtleaders_cli-0.6.4}/skills/tl/references/firebolt-schema.md +0 -0
  27. {thoughtleaders_cli-0.6.3 → thoughtleaders_cli-0.6.4}/src/tl_cli/_completions.py +0 -0
  28. {thoughtleaders_cli-0.6.3 → thoughtleaders_cli-0.6.4}/src/tl_cli/auth/__init__.py +0 -0
  29. {thoughtleaders_cli-0.6.3 → thoughtleaders_cli-0.6.4}/src/tl_cli/auth/commands.py +0 -0
  30. {thoughtleaders_cli-0.6.3 → thoughtleaders_cli-0.6.4}/src/tl_cli/auth/login.py +0 -0
  31. {thoughtleaders_cli-0.6.3 → thoughtleaders_cli-0.6.4}/src/tl_cli/auth/pkce.py +0 -0
  32. {thoughtleaders_cli-0.6.3 → thoughtleaders_cli-0.6.4}/src/tl_cli/auth/token_store.py +0 -0
  33. {thoughtleaders_cli-0.6.3 → thoughtleaders_cli-0.6.4}/src/tl_cli/client/__init__.py +0 -0
  34. {thoughtleaders_cli-0.6.3 → thoughtleaders_cli-0.6.4}/src/tl_cli/client/errors.py +0 -0
  35. {thoughtleaders_cli-0.6.3 → thoughtleaders_cli-0.6.4}/src/tl_cli/client/http.py +0 -0
  36. {thoughtleaders_cli-0.6.3 → thoughtleaders_cli-0.6.4}/src/tl_cli/commands/__init__.py +0 -0
  37. {thoughtleaders_cli-0.6.3 → thoughtleaders_cli-0.6.4}/src/tl_cli/commands/ask.py +0 -0
  38. {thoughtleaders_cli-0.6.3 → thoughtleaders_cli-0.6.4}/src/tl_cli/commands/balance.py +0 -0
  39. {thoughtleaders_cli-0.6.3 → thoughtleaders_cli-0.6.4}/src/tl_cli/commands/brands.py +0 -0
  40. {thoughtleaders_cli-0.6.3 → thoughtleaders_cli-0.6.4}/src/tl_cli/commands/changelog.py +0 -0
  41. {thoughtleaders_cli-0.6.3 → thoughtleaders_cli-0.6.4}/src/tl_cli/commands/channels.py +0 -0
  42. {thoughtleaders_cli-0.6.3 → thoughtleaders_cli-0.6.4}/src/tl_cli/commands/comments.py +0 -0
  43. {thoughtleaders_cli-0.6.3 → thoughtleaders_cli-0.6.4}/src/tl_cli/commands/db.py +0 -0
  44. {thoughtleaders_cli-0.6.3 → thoughtleaders_cli-0.6.4}/src/tl_cli/commands/deals.py +0 -0
  45. {thoughtleaders_cli-0.6.3 → thoughtleaders_cli-0.6.4}/src/tl_cli/commands/describe.py +0 -0
  46. {thoughtleaders_cli-0.6.3 → thoughtleaders_cli-0.6.4}/src/tl_cli/commands/doctor.py +0 -0
  47. {thoughtleaders_cli-0.6.3 → thoughtleaders_cli-0.6.4}/src/tl_cli/commands/matches.py +0 -0
  48. {thoughtleaders_cli-0.6.3 → thoughtleaders_cli-0.6.4}/src/tl_cli/commands/proposals.py +0 -0
  49. {thoughtleaders_cli-0.6.3 → thoughtleaders_cli-0.6.4}/src/tl_cli/commands/recommender.py +0 -0
  50. {thoughtleaders_cli-0.6.3 → thoughtleaders_cli-0.6.4}/src/tl_cli/commands/reports.py +0 -0
  51. {thoughtleaders_cli-0.6.3 → thoughtleaders_cli-0.6.4}/src/tl_cli/commands/schema.py +0 -0
  52. {thoughtleaders_cli-0.6.3 → thoughtleaders_cli-0.6.4}/src/tl_cli/commands/setup.py +0 -0
  53. {thoughtleaders_cli-0.6.3 → thoughtleaders_cli-0.6.4}/src/tl_cli/commands/snapshots.py +0 -0
  54. {thoughtleaders_cli-0.6.3 → thoughtleaders_cli-0.6.4}/src/tl_cli/commands/sponsorships.py +0 -0
  55. {thoughtleaders_cli-0.6.3 → thoughtleaders_cli-0.6.4}/src/tl_cli/commands/uploads.py +0 -0
  56. {thoughtleaders_cli-0.6.3 → thoughtleaders_cli-0.6.4}/src/tl_cli/commands/whoami.py +0 -0
  57. {thoughtleaders_cli-0.6.3 → thoughtleaders_cli-0.6.4}/src/tl_cli/config.py +0 -0
  58. {thoughtleaders_cli-0.6.3 → thoughtleaders_cli-0.6.4}/src/tl_cli/filters.py +0 -0
  59. {thoughtleaders_cli-0.6.3 → thoughtleaders_cli-0.6.4}/src/tl_cli/hints.py +0 -0
  60. {thoughtleaders_cli-0.6.3 → thoughtleaders_cli-0.6.4}/src/tl_cli/main.py +0 -0
  61. {thoughtleaders_cli-0.6.3 → thoughtleaders_cli-0.6.4}/src/tl_cli/output/__init__.py +0 -0
  62. {thoughtleaders_cli-0.6.3 → thoughtleaders_cli-0.6.4}/src/tl_cli/output/formatter.py +0 -0
  63. {thoughtleaders_cli-0.6.3 → thoughtleaders_cli-0.6.4}/src/tl_cli/self_update.py +0 -0
  64. {thoughtleaders_cli-0.6.3 → thoughtleaders_cli-0.6.4}/tests/__init__.py +0 -0
  65. {thoughtleaders_cli-0.6.3 → thoughtleaders_cli-0.6.4}/tests/test_auth.py +0 -0
  66. {thoughtleaders_cli-0.6.3 → thoughtleaders_cli-0.6.4}/tests/test_filters.py +0 -0
  67. {thoughtleaders_cli-0.6.3 → thoughtleaders_cli-0.6.4}/tests/test_output.py +0 -0
  68. {thoughtleaders_cli-0.6.3 → thoughtleaders_cli-0.6.4}/tests/test_sponsorships.py +0 -0
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tl-cli",
3
- "version": "0.6.3",
3
+ "version": "0.6.4",
4
4
  "description": "ThoughtLeaders CLI — query sponsorship deals, channels, brands, uploads, and intelligence from the terminal",
5
5
  "author": {
6
6
  "name": "ThoughtLeaders",
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: thoughtleaders-cli
3
- Version: 0.6.3
3
+ Version: 0.6.4
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
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "thoughtleaders-cli"
7
- version = "0.6.3"
7
+ version = "0.6.4"
8
8
  description = "ThoughtLeaders CLI — query sponsorship data, channels, brands, and intelligence"
9
9
  readme = "README.md"
10
10
  license = "MIT"
@@ -90,7 +90,21 @@ Raw mappings (read-only links — out of band, not via `tl`):
90
90
  | `banner_ads` | object | Banner ad data |
91
91
  | `not_sponsored_by` | object | Explicitly not sponsored by |
92
92
 
93
- #### Channel Fields (on video docs via `channel.id`, or on channel parent docs)
93
+ #### Channel Fields
94
+
95
+ > ⚠️ **The embedded `channel.*` object on video (article) docs is a denormalized SUBSET — NOT the full channel schema.** The full field list below exists only on **channel parent docs** (`doc_type: "channel"`). On article docs, `channel.*` contains at most **6 fields**: `id`, `country`, `language`, `content_category`, `format`, `publication_id`. **`reach`, `subscribers`, `impression`, `channel_name`, `sponsorship_score`, `is_tl_channel`, etc. are NOT on the embedded object.** Filtering article docs by `channel.reach` returns zero results silently — query the parent channel doc, or join PG `thoughtleaders_channel` for those fields.
96
+ >
97
+ > Also: `channel.country` is missing on ~14% of article docs even when `channel` itself exists, so a bare `{"term": {"channel.country": "US"}}` filter silently drops those rows. **A bare `exists` clause does NOT fix this** — in a filter context it also rejects missing values, just explicitly. To include the missing-country rows alongside US (treat them as "country unknown"), use a `should` split:
98
+ > ```json
99
+ > {"bool": {"should": [
100
+ > {"term": {"channel.country": "US"}},
101
+ > {"bool": {"filter": [{"exists": {"field": "channel.id"}}],
102
+ > "must_not": [{"exists": {"field": "channel.country"}}]}}
103
+ > ], "minimum_should_match": 1}}
104
+ > ```
105
+ > To **separately count** the missing rows, use a `filters` aggregation with `exists` / `must_not exists` branches.
106
+
107
+ The full table below applies to **channel parent docs only**:
94
108
 
95
109
  | Field | Type | Description |
96
110
  |-------|------|-------------|
@@ -56,8 +56,8 @@ The main deals table. Each row = one sponsorship deal between a brand and a YouT
56
56
  | `draft_expected_date` | date | Expected draft delivery |
57
57
  | `actual_end_date` | timestamptz | Actual end date |
58
58
  | `scheduled_end_date` | timestamptz | Scheduled end date |
59
- | `rejection_reason` | int | Rejection reason code |
60
- | `rejection_reason_details` | text | Free-text rejection details |
59
+ | `rejection_reason` | int | Rejection reason code (1–24). See "`rejection_reason` Constants" below for the code → label mapping. Set when `publish_status IN (4, 5, 9)` (closed-lost). |
60
+ | `rejection_reason_details` | text | Free-text rejection details. Often empty (~78% of lost deals). When populated, contains AM/agency notes like *"english content only"*, *"isn't talking about stocks"*, *"channel does not exist"*. Use as supplementary context, not primary classification. |
61
61
  | `payment_status` | int | 0=Unpaid, 1=Paid |
62
62
  | `performance_grade` | int | Performance rating (see business-glossary) |
63
63
  | `article_id` | varchar | Compound `<channel_id>:<youtube_id>` — links to ES `_id` and ES `id` field |
@@ -82,6 +82,66 @@ The main deals table. Each row = one sponsorship deal between a brand and a YouT
82
82
  | -1 | CLIENT_SIDE_AVAILABLE | Client Side Available | — |
83
83
  | -2 | CLIENT_SIDE_TAKEN | Client Side Taken | — |
84
84
 
85
+ #### `rejection_reason` Constants
86
+
87
+ Source of truth: `thoughtleaders.models.AdLink.REJECTION_REASON` (Django `IntegerField` choices in the main `thoughtleaders` repo).
88
+
89
+ **Storage model:** `thoughtleaders_adlink.rejection_reason` is an `IntegerField`. **Postgres stores only the integer code.** The labels live in the Django `IntegerField.choices` tuple in application code — they are NOT a queryable column or a join key. The integer is the only thing you can `WHERE` against; the labels below are display mappings only.
90
+
91
+ The **Enum Label** column is the verbatim string from the Django choices tuple (some have typos / internal vocabulary). The **AM-friendly label** is the recommended phrasing for AM-facing reports and proposals — use it when surfacing rejection reasons in any human-readable output.
92
+
93
+ **Grouping — be careful, codes 18–24 are NOT homogeneous:**
94
+ - **Codes 1–9** — brand-side rejections (brand said no)
95
+ - **Codes 10–17** — publisher-side rejections (channel said no)
96
+ - **Code 18 (`DEMOGRAPHICS_NO_MATCH`)** — audience-fit mismatch. Neither side is "wrong"; the channel may be excellent but its audience doesn't align with the brand's target. Don't bucket as "channel quality."
97
+ - **Codes 19, 21, 22, 24** (`NOT_BRAND_SAFE`, `HIGH_VOLATILITY`, `LOW_ENGAGEMENT`, `NO_FACE_ON_SCREEN`) — channel-quality / channel-performance signals (production and delivery).
98
+ - **Code 20 (`POOR_BRAND_HISTORY`)** — **brand-quality**, NOT channel quality. The brand has a poor sponsorship track record (e.g., known to ghost / pay late / be difficult). Do not include when reporting on channel quality.
99
+ - **Code 23 (`DUPLICATE_PROPOSAL`)** — **process/timing**, NOT channel quality. Channel was pitched too recently. Outreach-cadence issue.
100
+
101
+ ⚠️ Naïve "codes 18–24 = channel quality" bucketing will misattribute brand-quality (20), audience-fit (18), and process (23) failures to channel quality and skew rejection-rate analysis.
102
+
103
+ | Code | Constant | Enum Label (verbatim from Django) | AM-friendly label |
104
+ |------|----------|---------------------|-------------------|
105
+ | 1 | OTHER | Other (brand) | Brand declined — other reason |
106
+ | 2 | COMPETITOR | Channel works with competitor (brand) | Channel runs a competitor |
107
+ | 3 | NO_MATCH | Doesn't fit together (brand) | Brand says channel isn't a fit |
108
+ | 4 | DISLIKE | Doesn't like the channel | Brand doesn't want this channel |
109
+ | 5 | PRICING | Price is unreasonable | Brand says price is too high |
110
+ | 6 | WORKING_TOGETHER | Already working together with the channel | Already running with this channel |
111
+ | 7 | TIMING | Timing is off (brand) | Brand timing — not now |
112
+ | 8 | NO_RESPONSE | Channel did not respond | Channel never replied |
113
+ | 9 | DO_NOT_CONTACT | Do not contact channel | Channel is on do-not-contact list |
114
+ | 10 | PUBLISHER_OTHER | Other (publisher) | Channel declined — other reason |
115
+ | 11 | PUBLISHER_COMPETITOR | Works with competitor (publisher) | Channel already runs the competitor |
116
+ | 12 | PUBLISHER_NO_MATCH | Doesn't fit together (publisher) | Channel says brand isn't a fit |
117
+ | 13 | PUBLISHER_DISLIKE | Doesn't like the brand | Channel doesn't want this brand |
118
+ | 14 | PUBLISHER_PRICING | Brand Price is too low | Channel says price is too low |
119
+ | 15 | PUBLISHER_WORKING_TOGETHER | Already working together with the brand | Channel already running with brand |
120
+ | 16 | PUBLISHER_TIMING | Timing is off (publisher) | Channel timing — not now |
121
+ | 17 | PUBLISHER_NO_RESPONSE | Brand did not respond | Brand never replied |
122
+ | 18 | DEMOGRAPHICS_NO_MATCH | Demographics don't fit | Audience demographics don't match |
123
+ | 19 | NOT_BRAND_SAFE | Not brand safe | Brand-safety concern |
124
+ | 20 | POOR_BRAND_HISTORY | Poor brand sponsorship history | Brand has a poor sponsorship track record |
125
+ | 21 | HIGH_VOLATILITY | High Volatility | Channel views are too volatile |
126
+ | 22 | LOW_ENGAGEMENT | Low engagement/Low views | Low engagement or low views |
127
+ | 23 | DUPLICATE_PROPOSAL | Duplicate proposal | Already pitched recently |
128
+ | 24 | NO_FACE_ON_SCREEN | No face on screen | Channel doesn't show a host on screen |
129
+
130
+ #### Which date column for which question?
131
+
132
+ `thoughtleaders_adlink` has multiple timestamps. Picking the wrong one silently distorts trend analysis (e.g. grouping by `created_at` mixes outreach-blast batches with steady-state activity; grouping by `purchase_date` drops everything that didn't sell because rejected/pipeline rows have NULL `purchase_date`).
133
+
134
+ | Question | Use |
135
+ |---|---|
136
+ | "How many deals **sold** in year X?" | `purchase_date` (only set on sold/transacted deals) |
137
+ | "How many deals **created** in year X?" (incl. pipeline + lost) | `created_at` |
138
+ | "How much was **active outreach** in window X?" | `outreach_date` (sparse — falls back to `created_at` if null) |
139
+ | "When did the ad **go live on YouTube**?" | `publish_date` — null means not yet published; sold deals can still be canceled until this is set |
140
+ | "Latest activity / pipeline aging" | `updated_at` |
141
+ | "When was the deal **proposed/presented/rejected**?" | `proposal_approved_date` / `presented_date` / `rejected_date` (each only set when that stage was reached) |
142
+
143
+ **Default for "deals over time" reporting:** `created_at` if you want all flow, `purchase_date` if you want only revenue.
144
+
85
145
  #### Pipeline Stages
86
146
 
87
147
  - **Active pipeline** = statuses with weight > 0: 0, 2, 6, 7, 8.
@@ -1,3 +1,3 @@
1
1
  """ThoughtLeaders CLI — query sponsorship data, channels, brands, and intelligence."""
2
2
 
3
- __version__ = "0.6.3"
3
+ __version__ = "0.6.4"
@@ -388,7 +388,7 @@ wheels = [
388
388
 
389
389
  [[package]]
390
390
  name = "thoughtleaders-cli"
391
- version = "0.6.3"
391
+ version = "0.6.4"
392
392
  source = { editable = "." }
393
393
  dependencies = [
394
394
  { name = "authlib" },