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.
- {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/.claude-plugin/plugin.json +1 -1
- {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/AGENTS.md +1 -1
- {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/PKG-INFO +2 -2
- {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/README.md +1 -1
- 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
- thoughtleaders_cli-0.9.0/agents/keyword-entity-resolver.md +113 -0
- thoughtleaders_cli-0.9.0/agents/keyword-relevance-validator.md +70 -0
- {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/pyproject.toml +1 -1
- {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/skills/tl/SKILL.md +1 -7
- {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/skills/tl/references/elasticsearch-schema.md +3 -2
- thoughtleaders_cli-0.9.0/skills/tl-keyword-research/SKILL.md +657 -0
- thoughtleaders_cli-0.9.0/skills/tl-keyword-research/references/elasticsearch-content-search.md +434 -0
- thoughtleaders_cli-0.9.0/skills/tl-keyword-research/references/help.md +124 -0
- thoughtleaders_cli-0.9.0/skills/tl-keyword-research/scripts/build_report.py +489 -0
- thoughtleaders_cli-0.9.0/skills/tl-keyword-research/scripts/expand_entities.py +281 -0
- {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/skills/tl-keyword-research/scripts/fetch_context.py +2 -2
- thoughtleaders_cli-0.9.0/skills/tl-keyword-research/scripts/probe.py +596 -0
- thoughtleaders_cli-0.9.0/skills/tl-keyword-research/scripts/search_channels.py +521 -0
- thoughtleaders_cli-0.9.0/skills/tl-keyword-research/scripts/search_videos.py +342 -0
- thoughtleaders_cli-0.9.0/skills/tl-keyword-research/scripts/select_keywords.py +224 -0
- {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/src/tl_cli/__init__.py +1 -1
- thoughtleaders_cli-0.9.0/tests/test_build_report.py +378 -0
- thoughtleaders_cli-0.9.0/tests/test_expand_entities.py +175 -0
- thoughtleaders_cli-0.9.0/tests/test_probe.py +486 -0
- thoughtleaders_cli-0.9.0/tests/test_scripts_executable.py +26 -0
- thoughtleaders_cli-0.9.0/tests/test_search_channels.py +290 -0
- thoughtleaders_cli-0.9.0/tests/test_search_videos.py +159 -0
- thoughtleaders_cli-0.9.0/tests/test_select_keywords.py +186 -0
- thoughtleaders_cli-0.8.1/skills/tl-keyword-research/SKILL.md +0 -309
- thoughtleaders_cli-0.8.1/skills/tl-keyword-research/scripts/probe.py +0 -156
- thoughtleaders_cli-0.8.1/skills/tl-keyword-research/scripts/search_channels.py +0 -268
- {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/.claude-plugin/marketplace.json +0 -0
- {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/.github/dependabot.yml +0 -0
- {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/.github/workflows/ci.yml +0 -0
- {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/.github/workflows/cli-integration.yml +0 -0
- {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/.github/workflows/python-publish.yml +0 -0
- {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/.gitignore +0 -0
- {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/API.md +0 -0
- {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/CLAUDE.md +0 -0
- {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/LICENSE +0 -0
- {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/agents/tl-analyst.md +0 -0
- {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/agents/youtube-comment-classifier.md +0 -0
- {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/hooks/hooks.json +0 -0
- {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/hooks/scripts/load-tl-skill.mjs +0 -0
- {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/hooks/scripts/post-usage.sh +0 -0
- {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/hooks/scripts/pre-check.sh +0 -0
- {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/skills/tl/references/business-glossary.md +0 -0
- {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/skills/tl/references/firebolt-schema.md +0 -0
- {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/skills/tl/references/postgres-schema.md +0 -0
- {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/skills/tl-channel-authenticity/.gitignore +0 -0
- {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/skills/tl-channel-authenticity/SKILL.md +0 -0
- {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/skills/tl-channel-authenticity/references/comment-patterns.md +0 -0
- {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/skills/tl-channel-authenticity/references/peer-cohort.md +0 -0
- {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/skills/tl-channel-authenticity/references/red-flags.md +0 -0
- {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/skills/tl-channel-authenticity/references/scoring.md +0 -0
- {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/skills/tl-channel-authenticity/scripts/_io_utf8.py +0 -0
- {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/skills/tl-channel-authenticity/scripts/analyze_channel.py +0 -0
- {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/skills/tl-channel-authenticity/scripts/anomaly_detector.py +0 -0
- {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/skills/tl-channel-authenticity/scripts/comment_analyzer.py +0 -0
- {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/skills/tl-channel-authenticity/scripts/comment_scraper.py +0 -0
- {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/skills/tl-channel-authenticity/scripts/engagement_ratios.py +0 -0
- {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/skills/tl-channel-authenticity/scripts/peer_cohort.py +0 -0
- {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/skills/tl-channel-authenticity/scripts/report.py +0 -0
- {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/skills/tl-channel-authenticity/scripts/resolve_channel.py +0 -0
- {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/skills/tl-channel-authenticity/scripts/score.py +0 -0
- {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/skills/tl-channel-authenticity/scripts/tl_cli.py +0 -0
- {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/skills/tl-channel-authenticity/scripts/video_integrity.py +0 -0
- {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/skills/tl-channel-authenticity/scripts/view_curves.py +0 -0
- {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/skills/tl-save-report/SKILL.md +0 -0
- {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/skills/tl-save-report/references/columns_brands.md +0 -0
- {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/skills/tl-save-report/references/columns_channels.md +0 -0
- {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/skills/tl-save-report/references/columns_content.md +0 -0
- {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/skills/tl-save-report/references/columns_sponsorships.md +0 -0
- {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/skills/tl-save-report/references/intelligence_filterset_schema.json +0 -0
- {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/skills/tl-save-report/references/intelligence_widget_schema.json +0 -0
- {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/skills/tl-save-report/references/report_glossary.md +0 -0
- {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/skills/tl-save-report/references/sortable_columns.json +0 -0
- {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/skills/tl-save-report/references/sponsorship_filterset_schema.json +0 -0
- {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/skills/tl-save-report/references/sponsorship_widget_schema.json +0 -0
- {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/skills/tl-save-report/references/widgets.md +0 -0
- {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/skills/tl-top-partnerships/SKILL.md +0 -0
- {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/skills/tl-top-partnerships/scripts/top_partnerships.py +0 -0
- {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/skills/tl-views-guarantee/SKILL.md +0 -0
- {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/skills/tl-views-guarantee/scripts/vg.py +0 -0
- {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/src/tl_cli/_completions.py +0 -0
- {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/src/tl_cli/_typer_utils.py +0 -0
- {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/src/tl_cli/auth/__init__.py +0 -0
- {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/src/tl_cli/auth/commands.py +0 -0
- {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/src/tl_cli/auth/login.py +0 -0
- {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/src/tl_cli/auth/pkce.py +0 -0
- {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/src/tl_cli/auth/token_store.py +0 -0
- {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/src/tl_cli/client/__init__.py +0 -0
- {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/src/tl_cli/client/errors.py +0 -0
- {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/src/tl_cli/client/http.py +0 -0
- {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/src/tl_cli/commands/__init__.py +0 -0
- {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/src/tl_cli/commands/_comments_common.py +0 -0
- {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/src/tl_cli/commands/balance.py +0 -0
- {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/src/tl_cli/commands/brands.py +0 -0
- {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/src/tl_cli/commands/bulk_import.py +0 -0
- {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/src/tl_cli/commands/changelog.py +0 -0
- {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/src/tl_cli/commands/channels.py +0 -0
- {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/src/tl_cli/commands/credits.py +0 -0
- {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/src/tl_cli/commands/db.py +0 -0
- {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/src/tl_cli/commands/deals.py +0 -0
- {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/src/tl_cli/commands/describe.py +0 -0
- {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/src/tl_cli/commands/doctor.py +0 -0
- {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/src/tl_cli/commands/matches.py +0 -0
- {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/src/tl_cli/commands/proposals.py +0 -0
- {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/src/tl_cli/commands/recommender.py +0 -0
- {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/src/tl_cli/commands/reports.py +0 -0
- {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/src/tl_cli/commands/schema.py +0 -0
- {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/src/tl_cli/commands/setup.py +0 -0
- {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/src/tl_cli/commands/snapshots.py +0 -0
- {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/src/tl_cli/commands/sponsorships.py +0 -0
- {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/src/tl_cli/commands/uploads.py +0 -0
- {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/src/tl_cli/commands/whoami.py +0 -0
- {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/src/tl_cli/config.py +0 -0
- {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/src/tl_cli/filters.py +0 -0
- {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/src/tl_cli/hints.py +0 -0
- {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/src/tl_cli/main.py +0 -0
- {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/src/tl_cli/output/__init__.py +0 -0
- {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/src/tl_cli/output/formatter.py +0 -0
- {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/src/tl_cli/query_history.py +0 -0
- {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/src/tl_cli/self_update.py +0 -0
- {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/tests/__init__.py +0 -0
- {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/tests/test_auth.py +0 -0
- {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/tests/test_brands_winner_channels.py +0 -0
- {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/tests/test_channels_lookalike.py +0 -0
- {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/tests/test_db_repeat_warning.py +0 -0
- {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/tests/test_describe.py +0 -0
- {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/tests/test_filters.py +0 -0
- {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/tests/test_http_auth.py +0 -0
- {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/tests/test_output.py +0 -0
- {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/tests/test_reports.py +0 -0
- {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/tests/test_setup.py +0 -0
- {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/tests/test_sponsorships.py +0 -0
- {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/tests_cli/AGENTS.md +0 -0
- {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/tests_cli/conftest.py +0 -0
- {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/tests_cli/test_balance.py +0 -0
- {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/tests_cli/test_db_es.py +0 -0
- {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/tests_cli/test_db_fb.py +0 -0
- {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/tests_cli/test_db_pg.py +0 -0
- {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/tests_cli/test_schema.py +0 -0
- {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/tests_cli/test_whoami.py +0 -0
- {thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/uv.lock +0 -0
|
@@ -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
|
|
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.
|
|
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`** —
|
|
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`** —
|
|
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 `[]`.
|
|
@@ -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
|
|
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
|
|
{thoughtleaders_cli-0.8.1 → thoughtleaders_cli-0.9.0}/skills/tl/references/elasticsearch-schema.md
RENAMED
|
@@ -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
|
|
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
|
-
- **
|
|
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
|
|