thoughtleaders-cli 0.8.1__tar.gz → 0.9.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (145) hide show
  1. {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/.claude-plugin/plugin.json +1 -1
  2. {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/AGENTS.md +1 -1
  3. {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/PKG-INFO +2 -2
  4. {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/README.md +1 -1
  5. thoughtleaders_cli-0.8.1/skills/tl-keyword-research/references/context-classifier.md → thoughtleaders_cli-0.9.0/agents/keyword-context-classifier.md +14 -17
  6. thoughtleaders_cli-0.9.0/agents/keyword-entity-resolver.md +113 -0
  7. thoughtleaders_cli-0.9.0/agents/keyword-relevance-validator.md +70 -0
  8. {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/pyproject.toml +1 -1
  9. {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/skills/tl/SKILL.md +1 -7
  10. {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/skills/tl/references/elasticsearch-schema.md +3 -2
  11. thoughtleaders_cli-0.9.0/skills/tl-keyword-research/SKILL.md +657 -0
  12. thoughtleaders_cli-0.9.0/skills/tl-keyword-research/references/elasticsearch-content-search.md +434 -0
  13. thoughtleaders_cli-0.9.0/skills/tl-keyword-research/references/help.md +124 -0
  14. thoughtleaders_cli-0.9.0/skills/tl-keyword-research/scripts/build_report.py +489 -0
  15. thoughtleaders_cli-0.9.0/skills/tl-keyword-research/scripts/expand_entities.py +281 -0
  16. {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/skills/tl-keyword-research/scripts/fetch_context.py +2 -2
  17. thoughtleaders_cli-0.9.0/skills/tl-keyword-research/scripts/probe.py +596 -0
  18. thoughtleaders_cli-0.9.0/skills/tl-keyword-research/scripts/search_channels.py +521 -0
  19. thoughtleaders_cli-0.9.0/skills/tl-keyword-research/scripts/search_videos.py +342 -0
  20. thoughtleaders_cli-0.9.0/skills/tl-keyword-research/scripts/select_keywords.py +224 -0
  21. {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/src/tl_cli/__init__.py +1 -1
  22. thoughtleaders_cli-0.9.0/tests/test_build_report.py +378 -0
  23. thoughtleaders_cli-0.9.0/tests/test_expand_entities.py +175 -0
  24. thoughtleaders_cli-0.9.0/tests/test_probe.py +486 -0
  25. thoughtleaders_cli-0.9.0/tests/test_scripts_executable.py +26 -0
  26. thoughtleaders_cli-0.9.0/tests/test_search_channels.py +290 -0
  27. thoughtleaders_cli-0.9.0/tests/test_search_videos.py +159 -0
  28. thoughtleaders_cli-0.9.0/tests/test_select_keywords.py +186 -0
  29. thoughtleaders_cli-0.8.1/skills/tl-keyword-research/SKILL.md +0 -309
  30. thoughtleaders_cli-0.8.1/skills/tl-keyword-research/scripts/probe.py +0 -156
  31. thoughtleaders_cli-0.8.1/skills/tl-keyword-research/scripts/search_channels.py +0 -268
  32. {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/.claude-plugin/marketplace.json +0 -0
  33. {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/.github/dependabot.yml +0 -0
  34. {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/.github/workflows/ci.yml +0 -0
  35. {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/.github/workflows/cli-integration.yml +0 -0
  36. {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/.github/workflows/python-publish.yml +0 -0
  37. {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/.gitignore +0 -0
  38. {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/API.md +0 -0
  39. {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/CLAUDE.md +0 -0
  40. {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/LICENSE +0 -0
  41. {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/agents/tl-analyst.md +0 -0
  42. {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/agents/youtube-comment-classifier.md +0 -0
  43. {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/hooks/hooks.json +0 -0
  44. {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/hooks/scripts/load-tl-skill.mjs +0 -0
  45. {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/hooks/scripts/post-usage.sh +0 -0
  46. {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/hooks/scripts/pre-check.sh +0 -0
  47. {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/skills/tl/references/business-glossary.md +0 -0
  48. {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/skills/tl/references/firebolt-schema.md +0 -0
  49. {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/skills/tl/references/postgres-schema.md +0 -0
  50. {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/skills/tl-channel-authenticity/.gitignore +0 -0
  51. {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/skills/tl-channel-authenticity/SKILL.md +0 -0
  52. {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/skills/tl-channel-authenticity/references/comment-patterns.md +0 -0
  53. {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/skills/tl-channel-authenticity/references/peer-cohort.md +0 -0
  54. {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/skills/tl-channel-authenticity/references/red-flags.md +0 -0
  55. {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/skills/tl-channel-authenticity/references/scoring.md +0 -0
  56. {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/skills/tl-channel-authenticity/scripts/_io_utf8.py +0 -0
  57. {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/skills/tl-channel-authenticity/scripts/analyze_channel.py +0 -0
  58. {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/skills/tl-channel-authenticity/scripts/anomaly_detector.py +0 -0
  59. {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/skills/tl-channel-authenticity/scripts/comment_analyzer.py +0 -0
  60. {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/skills/tl-channel-authenticity/scripts/comment_scraper.py +0 -0
  61. {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/skills/tl-channel-authenticity/scripts/engagement_ratios.py +0 -0
  62. {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/skills/tl-channel-authenticity/scripts/peer_cohort.py +0 -0
  63. {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/skills/tl-channel-authenticity/scripts/report.py +0 -0
  64. {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/skills/tl-channel-authenticity/scripts/resolve_channel.py +0 -0
  65. {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/skills/tl-channel-authenticity/scripts/score.py +0 -0
  66. {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/skills/tl-channel-authenticity/scripts/tl_cli.py +0 -0
  67. {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/skills/tl-channel-authenticity/scripts/video_integrity.py +0 -0
  68. {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/skills/tl-channel-authenticity/scripts/view_curves.py +0 -0
  69. {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/skills/tl-save-report/SKILL.md +0 -0
  70. {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/skills/tl-save-report/references/columns_brands.md +0 -0
  71. {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/skills/tl-save-report/references/columns_channels.md +0 -0
  72. {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/skills/tl-save-report/references/columns_content.md +0 -0
  73. {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/skills/tl-save-report/references/columns_sponsorships.md +0 -0
  74. {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/skills/tl-save-report/references/intelligence_filterset_schema.json +0 -0
  75. {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/skills/tl-save-report/references/intelligence_widget_schema.json +0 -0
  76. {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/skills/tl-save-report/references/report_glossary.md +0 -0
  77. {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/skills/tl-save-report/references/sortable_columns.json +0 -0
  78. {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/skills/tl-save-report/references/sponsorship_filterset_schema.json +0 -0
  79. {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/skills/tl-save-report/references/sponsorship_widget_schema.json +0 -0
  80. {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/skills/tl-save-report/references/widgets.md +0 -0
  81. {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/skills/tl-top-partnerships/SKILL.md +0 -0
  82. {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/skills/tl-top-partnerships/scripts/top_partnerships.py +0 -0
  83. {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/skills/tl-views-guarantee/SKILL.md +0 -0
  84. {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/skills/tl-views-guarantee/scripts/vg.py +0 -0
  85. {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/src/tl_cli/_completions.py +0 -0
  86. {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/src/tl_cli/_typer_utils.py +0 -0
  87. {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/src/tl_cli/auth/__init__.py +0 -0
  88. {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/src/tl_cli/auth/commands.py +0 -0
  89. {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/src/tl_cli/auth/login.py +0 -0
  90. {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/src/tl_cli/auth/pkce.py +0 -0
  91. {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/src/tl_cli/auth/token_store.py +0 -0
  92. {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/src/tl_cli/client/__init__.py +0 -0
  93. {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/src/tl_cli/client/errors.py +0 -0
  94. {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/src/tl_cli/client/http.py +0 -0
  95. {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/src/tl_cli/commands/__init__.py +0 -0
  96. {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/src/tl_cli/commands/_comments_common.py +0 -0
  97. {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/src/tl_cli/commands/balance.py +0 -0
  98. {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/src/tl_cli/commands/brands.py +0 -0
  99. {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/src/tl_cli/commands/bulk_import.py +0 -0
  100. {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/src/tl_cli/commands/changelog.py +0 -0
  101. {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/src/tl_cli/commands/channels.py +0 -0
  102. {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/src/tl_cli/commands/credits.py +0 -0
  103. {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/src/tl_cli/commands/db.py +0 -0
  104. {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/src/tl_cli/commands/deals.py +0 -0
  105. {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/src/tl_cli/commands/describe.py +0 -0
  106. {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/src/tl_cli/commands/doctor.py +0 -0
  107. {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/src/tl_cli/commands/matches.py +0 -0
  108. {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/src/tl_cli/commands/proposals.py +0 -0
  109. {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/src/tl_cli/commands/recommender.py +0 -0
  110. {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/src/tl_cli/commands/reports.py +0 -0
  111. {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/src/tl_cli/commands/schema.py +0 -0
  112. {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/src/tl_cli/commands/setup.py +0 -0
  113. {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/src/tl_cli/commands/snapshots.py +0 -0
  114. {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/src/tl_cli/commands/sponsorships.py +0 -0
  115. {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/src/tl_cli/commands/uploads.py +0 -0
  116. {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/src/tl_cli/commands/whoami.py +0 -0
  117. {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/src/tl_cli/config.py +0 -0
  118. {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/src/tl_cli/filters.py +0 -0
  119. {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/src/tl_cli/hints.py +0 -0
  120. {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/src/tl_cli/main.py +0 -0
  121. {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/src/tl_cli/output/__init__.py +0 -0
  122. {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/src/tl_cli/output/formatter.py +0 -0
  123. {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/src/tl_cli/query_history.py +0 -0
  124. {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/src/tl_cli/self_update.py +0 -0
  125. {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/tests/__init__.py +0 -0
  126. {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/tests/test_auth.py +0 -0
  127. {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/tests/test_brands_winner_channels.py +0 -0
  128. {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/tests/test_channels_lookalike.py +0 -0
  129. {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/tests/test_db_repeat_warning.py +0 -0
  130. {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/tests/test_describe.py +0 -0
  131. {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/tests/test_filters.py +0 -0
  132. {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/tests/test_http_auth.py +0 -0
  133. {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/tests/test_output.py +0 -0
  134. {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/tests/test_reports.py +0 -0
  135. {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/tests/test_setup.py +0 -0
  136. {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/tests/test_sponsorships.py +0 -0
  137. {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/tests_cli/AGENTS.md +0 -0
  138. {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/tests_cli/conftest.py +0 -0
  139. {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/tests_cli/test_balance.py +0 -0
  140. {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/tests_cli/test_db_es.py +0 -0
  141. {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/tests_cli/test_db_fb.py +0 -0
  142. {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/tests_cli/test_db_pg.py +0 -0
  143. {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/tests_cli/test_schema.py +0 -0
  144. {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/tests_cli/test_whoami.py +0 -0
  145. {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tl-cli",
3
- "version": "0.8.1",
3
+ "version": "0.9.0",
4
4
  "description": "ThoughtLeaders CLI — query sponsorship deals, channels, brands, uploads, and intelligence from the terminal",
5
5
  "author": {
6
6
  "name": "ThoughtLeaders",
@@ -52,7 +52,7 @@ This repo is also a Claude Code plugin, and can directly be installed as one.
52
52
  ### Bundled skills — when to invoke
53
53
 
54
54
  - **`tl`** — the main skill for querying ThoughtLeaders data. Default for any sponsorship / channel / brand / upload / report question.
55
- - **`tl-keyword-research`** — invoke whenever the user wants to find videos or channels by **content keywords** (topics, concepts, niches) that aren't covered by a curated recommender tag, OR to validate that a candidate channel's content actually touches a given topic. It researches the topic by composing a boolean keyword query (`--any` OR-groups AND'd together, `--not` exclusions) over **≥3 refinement rounds**, returning a **CNF (Conjunctive Normal Form) keyword expression** as the reusable headline artifact alongside the ranked, sponsorability-flagged channels it selects. **Interactive by default** — checkpoints with the user after round 3 unless told to "run autonomously" (or invoked with `autonomous` / `--auto`). The keyword-distribution shape (`{operator, keywords:[{keyword,count}]}`) is an **opt-in mode**, produced only when the user explicitly asks for keyword counts / distribution. **Do not compose keyword sets by hand for `tl db es` content searches — delegate to this skill first.** See `skills/tl/SKILL.md` → *Channel & video discovery* for the four-path decision tree and when to use this vs the recommender / raw SQL.
55
+ - **`tl-keyword-research`** — invoke whenever the user wants to find videos or channels by **content keywords** (topics, concepts, niches) that aren't covered by a curated recommender tag, OR to validate that a candidate channel's content actually touches a given topic. It expands the topic deeply (entity families, ES tokenization variants, a gated web lookup for post-cutoff names), probes each candidate (counts + samples), validates matches against the user's intent with cheap sub-agents, refines a boolean filter over **≥3 rounds**, and context-validates the channels the filter selects. Deliverable: a **validated keyword-group filter set + a clickable report link**, plus whichever results the user chooses (it asks when the request doesn't say): **trend data** the matching videos/uploads sortable by date/views with prevalence numbers — and/or **channel targets** classified by topic intensity (core / recurring / occasional / one-off, so niche topics with no wholly-dedicated channels still get a real market: the recurring-coverage tier), sponsorability-flagged, with the rendered boolean expression recorded for reproducibility. **The user picks the path** — a quick single pass or a deep ≥3-round refinement: honor whichever the request names, ask when it names neither (never silently default); `autonomous` / `--auto` runs the deep path without pauses (otherwise it checkpoints after round 3). The keyword-distribution shape (`{operator, keywords:[{keyword,count}, …]}`) is an **opt-in mode**, produced only when the user explicitly asks for keyword counts / distribution. It also explains itself on request — "help" / "how does this work" / "what are my options" gets the built-in user guide, free, no queries run. **Do not compose keyword sets by hand for `tl db es` content searches — delegate to this skill first.** See `skills/tl/SKILL.md` → *Channel & video discovery* for the four-path decision tree and when to use this vs the recommender / raw SQL.
56
56
  - **`tl-save-report`**, **`adapt-tl-data`**, **`tl-views-guarantee`**, **`tl-top-partnerships`** — narrower workflows; the skill files document their own triggers. `tl-top-partnerships` is brand-user-facing: ranks a brand's sold sponsorships by live eCPM vs the sold-date projection and delivers a two-tab Google Sheet via `gws`.
57
57
 
58
58
  ### Skill content boundaries
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: thoughtleaders-cli
3
- Version: 0.8.1
3
+ Version: 0.9.0
4
4
  Summary: ThoughtLeaders CLI — query sponsorship data, channels, brands, and intelligence
5
5
  Project-URL: Homepage, https://thoughtleaders.io
6
6
  Project-URL: Repository, https://github.com/ThoughtLeaders-io/thoughtleaders-cli
@@ -278,7 +278,7 @@ Each agent discovers the skill automatically and uses it when you ask about spon
278
278
  The plugin ships several focused skills (installed by all the `tl setup *` commands):
279
279
 
280
280
  - **`tl`** — the data-analyst skill. Defaults to raw database queries via `tl db pg|fb|es` for anything non-trivial; uses the structured `tl <resource> show` / `find` / `similar` commands for single-record lookups and similarity / ID-resolution special cases. Comes with full schema references for Postgres, Elasticsearch, and Firebolt under `references/`.
281
- - **`tl-keyword-research`** — broadens and ranks content-search keywords by Elasticsearch document count before a `tl db es` content search, so finding videos or channels by topic isn't bottlenecked on hand-guessed terms.
281
+ - **`tl-keyword-research`** — turns a topic into a validated keyword-group filter set + a clickable report link, plus the results it selects — context-validated channels with sponsorability flags, or the matching videos/uploads for trend reports refining a boolean filter over the live index instead of hand-guessing terms. Keyword-distribution counts remain as an opt-in mode.
282
282
  - **`tl-save-report`** — persists the result set from an in-chat exploration session as a saved TL report ("save this as a report", "turn this into a campaign").
283
283
  - **`tl-channel-authenticity`** — vets a YouTube channel for non-organic views and bot/spam comments before booking (or after delivering) a sponsorship.
284
284
  - **`tl-views-guarantee`** — sizes a multi-video sponsorship buy for a channel, returning the video bundle size, views guarantee, and likelihood to hit.
@@ -250,7 +250,7 @@ Each agent discovers the skill automatically and uses it when you ask about spon
250
250
  The plugin ships several focused skills (installed by all the `tl setup *` commands):
251
251
 
252
252
  - **`tl`** — the data-analyst skill. Defaults to raw database queries via `tl db pg|fb|es` for anything non-trivial; uses the structured `tl <resource> show` / `find` / `similar` commands for single-record lookups and similarity / ID-resolution special cases. Comes with full schema references for Postgres, Elasticsearch, and Firebolt under `references/`.
253
- - **`tl-keyword-research`** — broadens and ranks content-search keywords by Elasticsearch document count before a `tl db es` content search, so finding videos or channels by topic isn't bottlenecked on hand-guessed terms.
253
+ - **`tl-keyword-research`** — turns a topic into a validated keyword-group filter set + a clickable report link, plus the results it selects — context-validated channels with sponsorability flags, or the matching videos/uploads for trend reports refining a boolean filter over the live index instead of hand-guessing terms. Keyword-distribution counts remain as an opt-in mode.
254
254
  - **`tl-save-report`** — persists the result set from an in-chat exploration session as a saved TL report ("save this as a report", "turn this into a campaign").
255
255
  - **`tl-channel-authenticity`** — vets a YouTube channel for non-organic views and bot/spam comments before booking (or after delivering) a sponsorship.
256
256
  - **`tl-views-guarantee`** — sizes a multi-video sponsorship buy for a channel, returning the video bundle size, views guarantee, and likelihood to hit.
@@ -1,21 +1,18 @@
1
- # Keyword Context Classifier (cheap / Haiku)
2
-
3
- This is the prompt for the per-channel context check. The orchestrator spawns one
4
- cheap agent (e.g. **model `haiku`**) per batch of channels with the System role set
5
- to everything below, then puts the `TOPIC` line + the indexed evidence JSON in the
6
- user message, and runs batches in parallel.
7
-
8
- **The orchestrator MUST anchor completeness in the user message** — state the exact
9
- count and the last id, e.g.:
10
- `There are exactly 50 channels (indices 0–49). Return exactly 50 objects. The last channel_id is 778812.`
11
- and **after** collecting results, verify every `channel_id` came back and re-send any
12
- missing ones. Cheap models drop the tail of long lists; steering reduces it, the
13
- re-send guarantees it.
14
-
15
- On promotion to `tl-cli` this can be registered as a subagent
16
- (`agents/keyword-context-classifier.md`, `model: haiku`, `tools: Read`).
17
-
18
1
  ---
2
+ name: keyword-context-classifier
3
+ description: >
4
+ Judges whether candidate YouTube channels genuinely cover a topic, or only
5
+ use the topic's keyword(s) in an unrelated sense, from keyword-in-context
6
+ snippets gathered by the tl-keyword-research skill's fetch_context step.
7
+ Use when you have a JSON array of per-channel snippet evidence and need a
8
+ fast, cheap per-channel on_topic / mixed / off_topic verdict plus adjacent
9
+ discovery terms. Returns strict JSON only.
10
+ model: haiku
11
+ tools: Read
12
+ color: yellow
13
+ ---
14
+
15
+ # Keyword Context Classifier
19
16
 
20
17
  You decide whether a YouTube channel genuinely covers a topic, or only mentions the
21
18
  topic's keyword(s) in an **unrelated sense**. ThoughtLeaders uses your verdict to
@@ -0,0 +1,113 @@
1
+ ---
2
+ name: keyword-entity-resolver
3
+ description: >
4
+ Resolves a content topic into the real-world proper nouns needed to search for
5
+ it — entity families (company → product line → model/version → codename →
6
+ sibling), post-cutoff launch/rename facts, and insider community vocabulary —
7
+ by running a few narrow web lookups and returning ONLY a compact JSON of names
8
+ to probe. Used by the tl-keyword-research skill's Step 0 / Phase 1 when a topic
9
+ postdates the knowledge cutoff, was recently renamed, is a live trend, or is
10
+ insider-jargon-dense. Returns strict JSON only.
11
+ model: sonnet
12
+ tools: WebSearch, WebFetch
13
+ color: green
14
+ ---
15
+
16
+ # Keyword Entity Resolver
17
+
18
+ You turn a content topic into the **real-world proper nouns and insider
19
+ vocabulary** a downstream Elasticsearch search needs — the names a model's own
20
+ memory cannot be trusted to enumerate because the topic is newer than its
21
+ knowledge cutoff, was recently renamed, is a live trend, or is dense with
22
+ community jargon. You are used by the `tl-keyword-research` skill. Your output is
23
+ **not** the final keyword set — every name you return is treated as an
24
+ **unvalidated probe candidate** that must still pass an ES probe and on-topic
25
+ sample validation. So your job is *recall of real names*, not judgement of fit.
26
+
27
+ ## Input
28
+
29
+ A short brief, e.g.:
30
+
31
+ ```
32
+ topic: Cannes Lions 2026
33
+ intent: YouTube videos about the Cannes Lions advertising/creativity festival — winners, campaigns, talks, recaps
34
+ level: topic # or channel
35
+ known: ["cannes lions", "cannes lions festival"] # names the caller already has; do NOT just repeat these
36
+ ```
37
+
38
+ `known` is what the caller already brainstormed — your value is the names it is
39
+ **missing**, so dedupe against it and spend your effort on the gaps.
40
+
41
+ ## Method (cheap and bounded)
42
+
43
+ 1. Run **1–2 `WebSearch` queries** to establish the entity and its current facts
44
+ (e.g. `"Cannes Lions 2026" award categories winners`, `"<product>" launch
45
+ model names siblings`). Read snippets first.
46
+ 2. **At most 1–2 `WebFetch`** of an *authoritative* page only when snippets are
47
+ insufficient — an official site, a Wikipedia article (use its infobox,
48
+ navbox, and "See also"), or a single curated community glossary / wiki /
49
+ tool-roundup. Do **not** open-ended browse, and never fetch random blogs,
50
+ autocomplete, or "related searches".
51
+ 3. Extract names. Stop — do not keep researching for completeness beyond the cap.
52
+
53
+ ## What to extract — and what NOT to
54
+
55
+ Extract **only literal proper nouns and named terms tied to THIS topic**:
56
+
57
+ - **Entity family** — company, product line, model/version names and numbers,
58
+ codenames, and **sibling products shipped or named alongside** the headline
59
+ one. (The classic miss: resolving a launch to its company name only and missing
60
+ the actual product, its version, and its sibling — return the whole family.)
61
+ - **Aliases** — old ↔ new names for any rename/rebrand/relaunch/successor, with
62
+ the change date if you can find it. Return **both** so a search spans the
63
+ pre- and post-rename eras.
64
+ - **Insider vocabulary** — up to **40** community/jargon terms, named sub-areas,
65
+ tools-of-the-niche, award/category names, recurring named events. Pull these
66
+ from curated glossaries / wikis / taxonomy pages, not from your own synonym
67
+ brainstorming.
68
+ - **Hashtags / handles** — the literal hashtag or handle form a community uses
69
+ (e.g. `#canneslions`).
70
+ - **Known collisions** — if a term obviously also means something else (a game,
71
+ a different festival, a foreign institution), note it so the caller can exclude
72
+ that polluter later.
73
+
74
+ Hard prohibitions (these would violate the skill's purpose):
75
+
76
+ - **No synonyms, adjectives, descriptors, or phrasings.** You return *names*, not
77
+ reworded versions of the topic. "creative advertising awards" is not a name;
78
+ "Cannes Lions Grand Prix" is.
79
+ - **No bare over-broad roots.** For "Cannes Lions" do not return "cannes" (the
80
+ city / film festival) or "lions" alone.
81
+ - **No specific YouTube channel names** and **no brands invented from free text** —
82
+ return only brands that genuinely appear as named entities in the topic.
83
+ - **Tie every name to the topic.** If you cannot say *why* a name belongs to this
84
+ topic from a source, drop it. A name you are unsure about is worse than a gap —
85
+ it wastes a probe or pollutes the result.
86
+
87
+ ## Output — STRICT
88
+
89
+ Return ONLY this JSON object, no prose, no markdown fence:
90
+
91
+ ```json
92
+ {
93
+ "entities": [
94
+ {"name": "Cannes Lions Grand Prix", "kind": "category", "note": "top award at the festival"}
95
+ ],
96
+ "insider_terms": ["Titanium Lion", "Film Craft", "Outdoor Lions", "..."],
97
+ "aliases": [{"old": "Cannes Advertising Festival", "new": "Cannes Lions", "since": "2011"}],
98
+ "hashtags": ["#canneslions"],
99
+ "collisions": [{"term": "cannes", "other_meaning": "Cannes Film Festival / the city"}],
100
+ "recency": {"is_post_cutoff": true, "as_of": "2026-06", "note": "2026 edition is current"},
101
+ "notes": "one or two sentences on what you found / any uncertainty",
102
+ "sources": ["https://...", "https://..."]
103
+ }
104
+ ```
105
+
106
+ - `kind` is one of `company | product | model | version | codename | sibling |
107
+ event | category | tool | other`.
108
+ - Every array may be empty (`[]`) if nothing applies; never omit a key.
109
+ - `name` values are bare literal strings — no quoting, no boolean operators. The
110
+ caller's script generates spelling/spacing variants and boolean grouping; you
111
+ supply the canonical names only.
112
+ - Keep it tight: quality and topic-tie matter more than volume (except
113
+ `insider_terms`, where up to 40 is fine if they are genuinely on-topic).
@@ -0,0 +1,70 @@
1
+ ---
2
+ name: keyword-relevance-validator
3
+ description: >
4
+ Judges whether sampled YouTube documents (videos or channels) returned by a
5
+ keyword probe are genuinely about the user's intended topic, for the
6
+ tl-keyword-research skill's validation step. Use when you have a JSON array of
7
+ sample docs (title/summary, or channel name/topic) and need a fast, cheap
8
+ per-sample on-topic / off-topic verdict. Returns strict JSON only.
9
+ model: haiku
10
+ tools: Read
11
+ color: cyan
12
+ ---
13
+
14
+ # Keyword Relevance Validator
15
+
16
+ You decide whether each sampled document actually concerns the user's intended
17
+ topic, or merely contains the keyword incidentally. You are used by the
18
+ `tl-keyword-research` skill to confirm that a candidate keyword brings back
19
+ on-topic content before it goes into a customer's filter set, so a wrong
20
+ "relevant" verdict pollutes the result — be strict.
21
+
22
+ ## Input
23
+
24
+ A single leading line states the intent, then a JSON array of samples:
25
+
26
+ ```
27
+ intent: <one sentence describing what the user is really looking for>
28
+ [{"i": 0, "keyword": "<candidate that matched this doc>", "title": "...", "summary": "..."}, ...]
29
+ ```
30
+
31
+ Each item has an integer `i`, the `keyword` that produced the match, and content
32
+ fields. For **videos** these are `title` and `summary`; for **channels** they
33
+ are `name` and `topic` (the channel's AI topic description). Some fields may be
34
+ empty or in another language — judge on whatever is present.
35
+
36
+ ## How to judge
37
+
38
+ Mark `relevant: true` only when the document is genuinely **about** the intent —
39
+ the topic is the subject of the video/channel, not a passing mention.
40
+
41
+ - A clear match **in the title** is strong evidence of relevance — titles state
42
+ what the content is about.
43
+ - A keyword that appears only as an offhand aside, idiom, or different sense of
44
+ the word is **not** relevant. Example — intent "stock/finance investing":
45
+ "I'm investing in my relationship with my partner" → `relevant: false` (the
46
+ word is there, the topic isn't).
47
+ - Judge against the **intent**, not the bare keyword string. The keyword may be
48
+ broad ("tiktok") while the intent is narrow ("TikTok Shop selling"); a doc
49
+ about TikTok dances does not serve a TikTok-Shop intent → `relevant: false`.
50
+ - When genuinely unsure from the fields present, prefer `relevant: false`.
51
+
52
+ ## Completeness — NON-NEGOTIABLE
53
+
54
+ You MUST return exactly one object for **every** input sample — same `i`
55
+ values, same order, from index 0 through the last one. Do not stop early,
56
+ summarize, or write `...`. A long input is not a reason to shorten the
57
+ output; per-item brevity is how you finish the list, not dropping items.
58
+ Before you finish: count your objects — if the count is less than the input
59
+ length, continue from where you stopped. (The caller diffs your `i` values
60
+ against the batch and re-sends anything missing, so a truncated reply only
61
+ wastes a round-trip.)
62
+
63
+ ## Output — STRICT
64
+
65
+ Return ONLY a JSON array, no prose, no markdown fence:
66
+
67
+ `[{"i": 0, "relevant": true}, {"i": 1, "relevant": false}, ...]`
68
+
69
+ One object per input sample, same `i` values, same length. No extra keys.
70
+ If the input array is empty, return `[]`.
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "thoughtleaders-cli"
7
- version = "0.8.1"
7
+ version = "0.9.0"
8
8
  description = "ThoughtLeaders CLI — query sponsorship data, channels, brands, and intelligence"
9
9
  readme = "README.md"
10
10
  license = "MIT"
@@ -555,13 +555,7 @@ Use `tl recommender top` for category/topic discovery (it's ranked) and `tl chan
555
555
  - **Channel–brand fit check** — does this candidate channel's content actually touch the brand's category? (Use with `channel.id` filter on the downstream ES query.)
556
556
  - **Validating a recommender / SQL shortlist** — sample-check that the top-N channels really cover the niche.
557
557
 
558
- **Do NOT compose keyword sets by hand for `tl db es`.** Always run the skill's script first. It broadens the user input, probes each candidate via `multi_match phrase`, and returns ranked counts:
559
-
560
- ```json
561
- {"operator": "OR", "keywords": [{"keyword": "crypto", "count": 18742}, {"keyword": "bitcoin", "count": 15103}, {"keyword": "rugpull", "count": 0}]}
562
- ```
563
-
564
- Then run the actual content search via `tl db es` (`multi_match` on the `title`, `summary`, `transcript` fields) with the surviving high-count keywords. The skill's full procedure (Phase 1 = seed expansion by you; Phase 2 = the script) is in the `tl-keyword-research` skill file.
558
+ **Do NOT compose keyword sets by hand for `tl db es`.** Always invoke the whole skill. It expands the topic deeply (entity families, tokenization variants, a gated web lookup for post-cutoff names), probes each candidate (document count **+ distinct-channel count + sample docs**), **validates the samples against the user's intent** with cheap sub-agents (so incidental "the word is there but the topic isn't" matches are dropped, not counted), refines a boolean filter over **≥3 rounds**, context-validates the channels the filter selects, and delivers a **validated keyword-group filter set + a clickable report link + the ranked channels with sponsorability flags** — not just counts. The full procedure (expand → probe → validate → refine → channels → deliver) and the ES Boolean/field reference live in the `tl-keyword-research` skill. Keyword-distribution counts remain available as the skill's opt-in mode when the question is literally "how common is X".
565
559
 
566
560
  **Path 4. Pure attribute filter** — user wants channels filtered by metadata like: `is_tpp`, `language`, `demographic_device_primary`, country share in `demographic_geo` JSON, aggregations, joins. Use `tl db pg` with a SELECT on `thoughtleaders_channel`. Run `tl schema pg thoughtleaders_channel` once to confirm the live column set; the columns in the examples are stable.
567
561
 
@@ -24,9 +24,10 @@ See the output of `tl db es`" for the object schema. Highlights:
24
24
  - **Top-level keys** accepted: `query`, `aggs`/`aggregations`, `sort`, `_source`, `size`, `from`, `search_after`, `track_total_hits`, `highlight`, `fields`, `min_score`, `timeout`, `collapse`, `post_filter`. Anything else (incl. `scroll`, `pit`, `runtime_mappings`, `knn`) is not accepted.
25
25
  - `size` ≤ 10,000. `from + size` ≤ 10,000 — to page past 10,000 hits use `search_after` (see *Deep pagination* below), not `from`.
26
26
  - `search_after` must be a non-empty array of ≤ 10 scalar sort values, requires an explicit `sort`, and `from` must be 0 or omitted.
27
- - **Accepted query types** include `term`/`terms`/`match`/`bool`/`nested`/`range`/`exists`/`match_phrase`. `query_string`, `regexp`, `wildcard`, `fuzzy`, `more_like_this`, `has_child`, `has_parent`, `parent_id` are not accepted.
27
+ - **Accepted query types** include `term`/`terms`/`match`/`bool`/`nested`/`range`/`exists`/`match_phrase`/`simple_query_string` (the sanctioned rich-Boolean text surface — `+` required, `|` OR, `-` NOT, `"phrase"`, `"phrase"~N` slop, trailing-`*` prefix, `~N` fuzzy, `(...)` grouping, per-field `^boosts`; always set `default_operator: "and"` and an explicit `fields` list or counts balloon). `query_string`, `regexp`, `wildcard`, `fuzzy`, `more_like_this`, `has_child`, `has_parent`, `parent_id` are not accepted.
28
28
  - **No scripts** — keys that start with `script` (e.g. `script_fields`, `script_score`, `scripted_metric`) or end with `_script` (e.g. `bucket_script`) are not accepted. A field whose name merely contains `script` as a substring (e.g. `transcript`, `description`) is fine.
29
- - **At most one aggregation total** counted recursively (top-level + sub-agg = 2 = not accepted). Run multiple calls for multi-metric work.
29
+ - **Aggregations are bounded, not forbidden**: up to ~50 agg nodes per body, bucket nesting ≤ 3 levels deep (single-bucket framing aggs like `filter`/`nested`/`missing` count toward depth), terms-like bucket `size` ≤ 10,000, and a worst-case total bucket count cap — so a `filter` agg wrapping a `cardinality` (the common counts-plus-recency shape) is fine in one call. Heavy/experimental agg types (e.g. `significant_terms`, script-based pipeline aggs) are not accepted.
30
+ - **`highlight` is accepted in the body but highlight fragments are not returned** — result rows carry only `_id`, `_score`, and the `_source` fields. Request the `_source` fields you need for validation samples instead.
30
31
 
31
32
  ### ElasticSearch document structure ("articles")
32
33