thoughtleaders-cli 0.7.8__tar.gz → 0.7.10__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.7.8 → thoughtleaders_cli-0.7.10}/.claude-plugin/plugin.json +1 -1
- {thoughtleaders_cli-0.7.8 → thoughtleaders_cli-0.7.10}/AGENTS.md +1 -1
- {thoughtleaders_cli-0.7.8 → thoughtleaders_cli-0.7.10}/PKG-INFO +1 -3
- {thoughtleaders_cli-0.7.8 → thoughtleaders_cli-0.7.10}/README.md +0 -2
- {thoughtleaders_cli-0.7.8 → thoughtleaders_cli-0.7.10}/pyproject.toml +1 -1
- {thoughtleaders_cli-0.7.8 → thoughtleaders_cli-0.7.10}/skills/tl-save-report/SKILL.md +2 -2
- {thoughtleaders_cli-0.7.8 → thoughtleaders_cli-0.7.10}/src/tl_cli/__init__.py +1 -1
- {thoughtleaders_cli-0.7.8 → thoughtleaders_cli-0.7.10}/src/tl_cli/commands/reports.py +3 -4
- thoughtleaders_cli-0.7.8/skills/tl-import/SKILL.md +0 -289
- thoughtleaders_cli-0.7.8/skills/tl-report-builder/SKILL.md +0 -1455
- thoughtleaders_cli-0.7.8/skills/tl-report-builder/examples/e2e_findings.md +0 -269
- thoughtleaders_cli-0.7.8/skills/tl-report-builder/examples/golden_queries.md +0 -150
- thoughtleaders_cli-0.7.8/skills/tl-report-builder/references/columns_brands.md +0 -82
- thoughtleaders_cli-0.7.8/skills/tl-report-builder/references/columns_channels.md +0 -99
- thoughtleaders_cli-0.7.8/skills/tl-report-builder/references/columns_content.md +0 -78
- thoughtleaders_cli-0.7.8/skills/tl-report-builder/references/columns_sponsorships.md +0 -96
- thoughtleaders_cli-0.7.8/skills/tl-report-builder/references/intelligence_filterset_schema.json +0 -407
- thoughtleaders_cli-0.7.8/skills/tl-report-builder/references/intelligence_widget_schema.json +0 -194
- thoughtleaders_cli-0.7.8/skills/tl-report-builder/references/report_glossary.md +0 -145
- thoughtleaders_cli-0.7.8/skills/tl-report-builder/references/sponsorship_filterset_schema.json +0 -217
- thoughtleaders_cli-0.7.8/skills/tl-report-builder/references/sponsorship_widget_schema.json +0 -165
- thoughtleaders_cli-0.7.8/skills/tl-report-builder/references/widgets.md +0 -184
- thoughtleaders_cli-0.7.8/skills/tl-report-builder/tools/column_builder.md +0 -384
- thoughtleaders_cli-0.7.8/skills/tl-report-builder/tools/database_query.md +0 -210
- thoughtleaders_cli-0.7.8/skills/tl-report-builder/tools/name_resolver.md +0 -162
- thoughtleaders_cli-0.7.8/skills/tl-report-builder/tools/sample_judge.md +0 -223
- thoughtleaders_cli-0.7.8/skills/tl-report-builder/tools/similar_channels.md +0 -61
- thoughtleaders_cli-0.7.8/skills/tl-report-builder/tools/topic_matcher.md +0 -289
- thoughtleaders_cli-0.7.8/skills/tl-report-builder/tools/widget_builder.md +0 -278
- thoughtleaders_cli-0.7.8/skills/tl-save-report/references/sortable_columns.json +0 -64
- {thoughtleaders_cli-0.7.8 → thoughtleaders_cli-0.7.10}/.claude-plugin/marketplace.json +0 -0
- {thoughtleaders_cli-0.7.8 → thoughtleaders_cli-0.7.10}/.github/workflows/python-publish.yml +0 -0
- {thoughtleaders_cli-0.7.8 → thoughtleaders_cli-0.7.10}/.gitignore +0 -0
- {thoughtleaders_cli-0.7.8 → thoughtleaders_cli-0.7.10}/API.md +0 -0
- {thoughtleaders_cli-0.7.8 → thoughtleaders_cli-0.7.10}/CLAUDE.md +0 -0
- {thoughtleaders_cli-0.7.8 → thoughtleaders_cli-0.7.10}/LICENSE +0 -0
- {thoughtleaders_cli-0.7.8 → thoughtleaders_cli-0.7.10}/agents/tl-analyst.md +0 -0
- {thoughtleaders_cli-0.7.8 → thoughtleaders_cli-0.7.10}/agents/youtube-comment-classifier.md +0 -0
- {thoughtleaders_cli-0.7.8 → thoughtleaders_cli-0.7.10}/hooks/hooks.json +0 -0
- {thoughtleaders_cli-0.7.8 → thoughtleaders_cli-0.7.10}/hooks/scripts/load-tl-skill.mjs +0 -0
- {thoughtleaders_cli-0.7.8 → thoughtleaders_cli-0.7.10}/hooks/scripts/post-usage.sh +0 -0
- {thoughtleaders_cli-0.7.8 → thoughtleaders_cli-0.7.10}/hooks/scripts/pre-check.sh +0 -0
- {thoughtleaders_cli-0.7.8 → thoughtleaders_cli-0.7.10}/skills/tl/SKILL.md +0 -0
- {thoughtleaders_cli-0.7.8 → thoughtleaders_cli-0.7.10}/skills/tl/references/business-glossary.md +0 -0
- {thoughtleaders_cli-0.7.8 → thoughtleaders_cli-0.7.10}/skills/tl/references/elasticsearch-schema.md +0 -0
- {thoughtleaders_cli-0.7.8 → thoughtleaders_cli-0.7.10}/skills/tl/references/firebolt-schema.md +0 -0
- {thoughtleaders_cli-0.7.8 → thoughtleaders_cli-0.7.10}/skills/tl/references/postgres-schema.md +0 -0
- {thoughtleaders_cli-0.7.8 → thoughtleaders_cli-0.7.10}/skills/tl-channel-authenticity/.gitignore +0 -0
- {thoughtleaders_cli-0.7.8 → thoughtleaders_cli-0.7.10}/skills/tl-channel-authenticity/SKILL.md +0 -0
- {thoughtleaders_cli-0.7.8 → thoughtleaders_cli-0.7.10}/skills/tl-channel-authenticity/references/comment-patterns.md +0 -0
- {thoughtleaders_cli-0.7.8 → thoughtleaders_cli-0.7.10}/skills/tl-channel-authenticity/references/peer-cohort.md +0 -0
- {thoughtleaders_cli-0.7.8 → thoughtleaders_cli-0.7.10}/skills/tl-channel-authenticity/references/red-flags.md +0 -0
- {thoughtleaders_cli-0.7.8 → thoughtleaders_cli-0.7.10}/skills/tl-channel-authenticity/references/scoring.md +0 -0
- {thoughtleaders_cli-0.7.8 → thoughtleaders_cli-0.7.10}/skills/tl-channel-authenticity/scripts/_io_utf8.py +0 -0
- {thoughtleaders_cli-0.7.8 → thoughtleaders_cli-0.7.10}/skills/tl-channel-authenticity/scripts/analyze_channel.py +0 -0
- {thoughtleaders_cli-0.7.8 → thoughtleaders_cli-0.7.10}/skills/tl-channel-authenticity/scripts/anomaly_detector.py +0 -0
- {thoughtleaders_cli-0.7.8 → thoughtleaders_cli-0.7.10}/skills/tl-channel-authenticity/scripts/comment_analyzer.py +0 -0
- {thoughtleaders_cli-0.7.8 → thoughtleaders_cli-0.7.10}/skills/tl-channel-authenticity/scripts/comment_scraper.py +0 -0
- {thoughtleaders_cli-0.7.8 → thoughtleaders_cli-0.7.10}/skills/tl-channel-authenticity/scripts/engagement_ratios.py +0 -0
- {thoughtleaders_cli-0.7.8 → thoughtleaders_cli-0.7.10}/skills/tl-channel-authenticity/scripts/peer_cohort.py +0 -0
- {thoughtleaders_cli-0.7.8 → thoughtleaders_cli-0.7.10}/skills/tl-channel-authenticity/scripts/report.py +0 -0
- {thoughtleaders_cli-0.7.8 → thoughtleaders_cli-0.7.10}/skills/tl-channel-authenticity/scripts/resolve_channel.py +0 -0
- {thoughtleaders_cli-0.7.8 → thoughtleaders_cli-0.7.10}/skills/tl-channel-authenticity/scripts/score.py +0 -0
- {thoughtleaders_cli-0.7.8 → thoughtleaders_cli-0.7.10}/skills/tl-channel-authenticity/scripts/tl_cli.py +0 -0
- {thoughtleaders_cli-0.7.8 → thoughtleaders_cli-0.7.10}/skills/tl-channel-authenticity/scripts/video_integrity.py +0 -0
- {thoughtleaders_cli-0.7.8 → thoughtleaders_cli-0.7.10}/skills/tl-channel-authenticity/scripts/view_curves.py +0 -0
- {thoughtleaders_cli-0.7.8 → thoughtleaders_cli-0.7.10}/skills/tl-keyword-research/SKILL.md +0 -0
- {thoughtleaders_cli-0.7.8 → thoughtleaders_cli-0.7.10}/skills/tl-keyword-research/scripts/probe.py +0 -0
- {thoughtleaders_cli-0.7.8 → thoughtleaders_cli-0.7.10}/skills/tl-save-report/references/columns_brands.md +0 -0
- {thoughtleaders_cli-0.7.8 → thoughtleaders_cli-0.7.10}/skills/tl-save-report/references/columns_channels.md +0 -0
- {thoughtleaders_cli-0.7.8 → thoughtleaders_cli-0.7.10}/skills/tl-save-report/references/columns_content.md +0 -0
- {thoughtleaders_cli-0.7.8 → thoughtleaders_cli-0.7.10}/skills/tl-save-report/references/columns_sponsorships.md +0 -0
- {thoughtleaders_cli-0.7.8 → thoughtleaders_cli-0.7.10}/skills/tl-save-report/references/intelligence_filterset_schema.json +0 -0
- {thoughtleaders_cli-0.7.8 → thoughtleaders_cli-0.7.10}/skills/tl-save-report/references/intelligence_widget_schema.json +0 -0
- {thoughtleaders_cli-0.7.8 → thoughtleaders_cli-0.7.10}/skills/tl-save-report/references/report_glossary.md +0 -0
- {thoughtleaders_cli-0.7.8/skills/tl-report-builder → thoughtleaders_cli-0.7.10/skills/tl-save-report}/references/sortable_columns.json +0 -0
- {thoughtleaders_cli-0.7.8 → thoughtleaders_cli-0.7.10}/skills/tl-save-report/references/sponsorship_filterset_schema.json +0 -0
- {thoughtleaders_cli-0.7.8 → thoughtleaders_cli-0.7.10}/skills/tl-save-report/references/sponsorship_widget_schema.json +0 -0
- {thoughtleaders_cli-0.7.8 → thoughtleaders_cli-0.7.10}/skills/tl-save-report/references/widgets.md +0 -0
- {thoughtleaders_cli-0.7.8 → thoughtleaders_cli-0.7.10}/skills/tl-top-partnerships/SKILL.md +0 -0
- {thoughtleaders_cli-0.7.8 → thoughtleaders_cli-0.7.10}/skills/tl-top-partnerships/scripts/top_partnerships.py +0 -0
- {thoughtleaders_cli-0.7.8 → thoughtleaders_cli-0.7.10}/skills/tl-views-guarantee/SKILL.md +0 -0
- {thoughtleaders_cli-0.7.8 → thoughtleaders_cli-0.7.10}/skills/tl-views-guarantee/scripts/vg.py +0 -0
- {thoughtleaders_cli-0.7.8 → thoughtleaders_cli-0.7.10}/src/tl_cli/_completions.py +0 -0
- {thoughtleaders_cli-0.7.8 → thoughtleaders_cli-0.7.10}/src/tl_cli/_typer_utils.py +0 -0
- {thoughtleaders_cli-0.7.8 → thoughtleaders_cli-0.7.10}/src/tl_cli/auth/__init__.py +0 -0
- {thoughtleaders_cli-0.7.8 → thoughtleaders_cli-0.7.10}/src/tl_cli/auth/commands.py +0 -0
- {thoughtleaders_cli-0.7.8 → thoughtleaders_cli-0.7.10}/src/tl_cli/auth/login.py +0 -0
- {thoughtleaders_cli-0.7.8 → thoughtleaders_cli-0.7.10}/src/tl_cli/auth/pkce.py +0 -0
- {thoughtleaders_cli-0.7.8 → thoughtleaders_cli-0.7.10}/src/tl_cli/auth/token_store.py +0 -0
- {thoughtleaders_cli-0.7.8 → thoughtleaders_cli-0.7.10}/src/tl_cli/client/__init__.py +0 -0
- {thoughtleaders_cli-0.7.8 → thoughtleaders_cli-0.7.10}/src/tl_cli/client/errors.py +0 -0
- {thoughtleaders_cli-0.7.8 → thoughtleaders_cli-0.7.10}/src/tl_cli/client/http.py +0 -0
- {thoughtleaders_cli-0.7.8 → thoughtleaders_cli-0.7.10}/src/tl_cli/commands/__init__.py +0 -0
- {thoughtleaders_cli-0.7.8 → thoughtleaders_cli-0.7.10}/src/tl_cli/commands/_comments_common.py +0 -0
- {thoughtleaders_cli-0.7.8 → thoughtleaders_cli-0.7.10}/src/tl_cli/commands/balance.py +0 -0
- {thoughtleaders_cli-0.7.8 → thoughtleaders_cli-0.7.10}/src/tl_cli/commands/brands.py +0 -0
- {thoughtleaders_cli-0.7.8 → thoughtleaders_cli-0.7.10}/src/tl_cli/commands/bulk_import.py +0 -0
- {thoughtleaders_cli-0.7.8 → thoughtleaders_cli-0.7.10}/src/tl_cli/commands/changelog.py +0 -0
- {thoughtleaders_cli-0.7.8 → thoughtleaders_cli-0.7.10}/src/tl_cli/commands/channels.py +0 -0
- {thoughtleaders_cli-0.7.8 → thoughtleaders_cli-0.7.10}/src/tl_cli/commands/credits.py +0 -0
- {thoughtleaders_cli-0.7.8 → thoughtleaders_cli-0.7.10}/src/tl_cli/commands/db.py +0 -0
- {thoughtleaders_cli-0.7.8 → thoughtleaders_cli-0.7.10}/src/tl_cli/commands/deals.py +0 -0
- {thoughtleaders_cli-0.7.8 → thoughtleaders_cli-0.7.10}/src/tl_cli/commands/describe.py +0 -0
- {thoughtleaders_cli-0.7.8 → thoughtleaders_cli-0.7.10}/src/tl_cli/commands/doctor.py +0 -0
- {thoughtleaders_cli-0.7.8 → thoughtleaders_cli-0.7.10}/src/tl_cli/commands/matches.py +0 -0
- {thoughtleaders_cli-0.7.8 → thoughtleaders_cli-0.7.10}/src/tl_cli/commands/proposals.py +0 -0
- {thoughtleaders_cli-0.7.8 → thoughtleaders_cli-0.7.10}/src/tl_cli/commands/recommender.py +0 -0
- {thoughtleaders_cli-0.7.8 → thoughtleaders_cli-0.7.10}/src/tl_cli/commands/schema.py +0 -0
- {thoughtleaders_cli-0.7.8 → thoughtleaders_cli-0.7.10}/src/tl_cli/commands/setup.py +0 -0
- {thoughtleaders_cli-0.7.8 → thoughtleaders_cli-0.7.10}/src/tl_cli/commands/snapshots.py +0 -0
- {thoughtleaders_cli-0.7.8 → thoughtleaders_cli-0.7.10}/src/tl_cli/commands/sponsorships.py +0 -0
- {thoughtleaders_cli-0.7.8 → thoughtleaders_cli-0.7.10}/src/tl_cli/commands/uploads.py +0 -0
- {thoughtleaders_cli-0.7.8 → thoughtleaders_cli-0.7.10}/src/tl_cli/commands/whoami.py +0 -0
- {thoughtleaders_cli-0.7.8 → thoughtleaders_cli-0.7.10}/src/tl_cli/config.py +0 -0
- {thoughtleaders_cli-0.7.8 → thoughtleaders_cli-0.7.10}/src/tl_cli/filters.py +0 -0
- {thoughtleaders_cli-0.7.8 → thoughtleaders_cli-0.7.10}/src/tl_cli/hints.py +0 -0
- {thoughtleaders_cli-0.7.8 → thoughtleaders_cli-0.7.10}/src/tl_cli/main.py +0 -0
- {thoughtleaders_cli-0.7.8 → thoughtleaders_cli-0.7.10}/src/tl_cli/output/__init__.py +0 -0
- {thoughtleaders_cli-0.7.8 → thoughtleaders_cli-0.7.10}/src/tl_cli/output/formatter.py +0 -0
- {thoughtleaders_cli-0.7.8 → thoughtleaders_cli-0.7.10}/src/tl_cli/self_update.py +0 -0
- {thoughtleaders_cli-0.7.8 → thoughtleaders_cli-0.7.10}/tests/__init__.py +0 -0
- {thoughtleaders_cli-0.7.8 → thoughtleaders_cli-0.7.10}/tests/test_auth.py +0 -0
- {thoughtleaders_cli-0.7.8 → thoughtleaders_cli-0.7.10}/tests/test_describe.py +0 -0
- {thoughtleaders_cli-0.7.8 → thoughtleaders_cli-0.7.10}/tests/test_filters.py +0 -0
- {thoughtleaders_cli-0.7.8 → thoughtleaders_cli-0.7.10}/tests/test_http_auth.py +0 -0
- {thoughtleaders_cli-0.7.8 → thoughtleaders_cli-0.7.10}/tests/test_output.py +0 -0
- {thoughtleaders_cli-0.7.8 → thoughtleaders_cli-0.7.10}/tests/test_reports.py +0 -0
- {thoughtleaders_cli-0.7.8 → thoughtleaders_cli-0.7.10}/tests/test_setup.py +0 -0
- {thoughtleaders_cli-0.7.8 → thoughtleaders_cli-0.7.10}/tests/test_sponsorships.py +0 -0
- {thoughtleaders_cli-0.7.8 → thoughtleaders_cli-0.7.10}/uv.lock +0 -0
|
@@ -53,7 +53,7 @@ This repo is also a Claude Code plugin, and can directly be installed as one.
|
|
|
53
53
|
|
|
54
54
|
- **`tl`** — the main skill for querying ThoughtLeaders data. Default for any sponsorship / channel / brand / upload / report question.
|
|
55
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. Returns `{operator, keywords:[{keyword,count}]}` from a ranked ES probe over `title` / `summary` / `transcript`; the caller then runs the actual content search with the surviving high-count terms. **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
|
-
- **`tl-
|
|
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
|
|
59
59
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: thoughtleaders-cli
|
|
3
|
-
Version: 0.7.
|
|
3
|
+
Version: 0.7.10
|
|
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
|
|
@@ -280,8 +280,6 @@ The plugin ships several focused skills (installed by all the `tl setup *` comma
|
|
|
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
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.
|
|
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
|
-
- **`tl-report-builder`** — builds a brand-new TL report config from scratch (channels / brands / sponsorships / videos) through a guided multi-phase flow. Manual-invocation-only: reach it via `/tl-report-builder` or by naming it explicitly — natural-language report requests route to `tl`, `tl-save-report`, or `tl-import` instead.
|
|
284
|
-
- **`tl-import`** / **`bulk-import`** — superuser-only; bulk-add or exclude lists of channels, brands, videos, or sponsorships against a report.
|
|
285
283
|
- **`tl-channel-authenticity`** — vets a YouTube channel for non-organic views and bot/spam comments before booking (or after delivering) a sponsorship.
|
|
286
284
|
- **`tl-views-guarantee`** — sizes a multi-video sponsorship buy for a channel, returning the video bundle size, views guarantee, and likelihood to hit.
|
|
287
285
|
- **`tl-top-partnerships`** — brand-user performance report. Ranks a brand's sold sponsorships by live eCPM vs the sold-date projection, aggregates per channel, and delivers a two-tab Google Sheet ("By Deal" / "By Channel") via `gws`. Uses only public CLI commands (`tl whoami`, `tl sponsorships list`).
|
|
@@ -252,8 +252,6 @@ The plugin ships several focused skills (installed by all the `tl setup *` comma
|
|
|
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
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.
|
|
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
|
-
- **`tl-report-builder`** — builds a brand-new TL report config from scratch (channels / brands / sponsorships / videos) through a guided multi-phase flow. Manual-invocation-only: reach it via `/tl-report-builder` or by naming it explicitly — natural-language report requests route to `tl`, `tl-save-report`, or `tl-import` instead.
|
|
256
|
-
- **`tl-import`** / **`bulk-import`** — superuser-only; bulk-add or exclude lists of channels, brands, videos, or sponsorships against a report.
|
|
257
255
|
- **`tl-channel-authenticity`** — vets a YouTube channel for non-organic views and bot/spam comments before booking (or after delivering) a sponsorship.
|
|
258
256
|
- **`tl-views-guarantee`** — sizes a multi-video sponsorship buy for a channel, returning the video bundle size, views guarantee, and likelihood to hit.
|
|
259
257
|
- **`tl-top-partnerships`** — brand-user performance report. Ranks a brand's sold sponsorships by live eCPM vs the sold-date projection, aggregates per channel, and delivers a two-tab Google Sheet ("By Deal" / "By Channel") via `gws`. Uses only public CLI commands (`tl whoami`, `tl sponsorships list`).
|
|
@@ -56,7 +56,7 @@ The entity being saved must be one of: **channels**, **brands**, **videos / uplo
|
|
|
56
56
|
|
|
57
57
|
**Skip when**:
|
|
58
58
|
|
|
59
|
-
- The user wants to **add to an existing report** (`"add these channels to report 1234"`) →
|
|
59
|
+
- The user wants to **add to an existing report** (`"add these channels to report 1234"`) → use the `tl bulk-import` command, not this skill.
|
|
60
60
|
- The user only wants the data **shown / counted / analysed in chat** without saving → stay in `tl`; don't invoke this skill.
|
|
61
61
|
- The user wants to build a report **from scratch** with no prior session exploration to capture — that's a different shape of request (the user has a goal, not a result set). Run the appropriate `tl db pg|fb|es` queries to produce a result set first; then this skill takes over for the save.
|
|
62
62
|
|
|
@@ -524,4 +524,4 @@ The above maps the visible CLI output to the underlying cause — match on a sub
|
|
|
524
524
|
|
|
525
525
|
- **No discovery-side work** — no keyword research, no live-data sample validation, no result-set re-evaluation. The session already produced the data; re-running discovery would be wasted effort. Name resolution (`tl brands find` / `tl channels find` to turn names into IDs before they land in the FilterSet) is the one exception — it's required by the FilterSet schema, not discovery. If the user comes in with no prior session, run the relevant `tl db pg|fb|es` queries first to produce a result set, then invoke this skill on the result.
|
|
526
526
|
- **No editing of existing reports.** If the user wants to refine an already-saved report's columns, widgets, title, or description, run `tl reports update <id>` directly. For FilterSet refinements, the platform requires saving a new variant.
|
|
527
|
-
- **No bulk-importing into an existing report.**
|
|
527
|
+
- **No bulk-importing into an existing report.** Use the `tl bulk-import` command for that. Save-report only creates new reports.
|
|
@@ -404,8 +404,8 @@ def create_report(
|
|
|
404
404
|
|
|
405
405
|
With --config '<json>' or --config-file <path>, skips the orchestration
|
|
406
406
|
pipeline and saves the provided config directly. Useful when an external
|
|
407
|
-
agent
|
|
408
|
-
|
|
407
|
+
agent has already produced a validated config and you just want to persist
|
|
408
|
+
it. Prefer --config-file when
|
|
409
409
|
the config might contain apostrophes, dollar signs, or backticks — file
|
|
410
410
|
transport sidesteps shell quoting entirely.
|
|
411
411
|
|
|
@@ -518,8 +518,7 @@ ENTITY_TO_REPORT_TYPE = {
|
|
|
518
518
|
|
|
519
519
|
# FilterSet M2M field per entity is identical to the entity name, except
|
|
520
520
|
# article IDs are composite strings (`<channel_id>:<youtube_id>`) and the
|
|
521
|
-
# others are integers.
|
|
522
|
-
# skills/tl-report-builder/references/ for the catalogue.
|
|
521
|
+
# others are integers.
|
|
523
522
|
|
|
524
523
|
|
|
525
524
|
def _read_ids(path: str, entity: str) -> list:
|
|
@@ -1,289 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: tl-import
|
|
3
|
-
tl-blurb: bulk-import lists into a report
|
|
4
|
-
description: Import a list of channels, brands, uploads (videos), or sponsorships into a ThoughtLeaders report — either an existing report (caller supplies `campaign_id` or a TL report URL) or a fresh new one (skill creates a minimal container, then populates). Superuser-only. **Trigger on explicit intent to import the listed entities into a report**, NOT on the mere presence of a list (a user can paste a list and want analysis, comparison, or similar-channel discovery — those go to `tl-cli:tl`). The deciding question is: *would the user be satisfied if those exact entities ended up as the report's contents, no transformation?* If yes, this is the skill. Phrasings: "import these channels into report 1234", "add brands to campaign 5678", "exclude these channels from report Z", "bulk-add these videos to report X", "create a new report with these channels: <list>", "make a campaign containing these brands: <list>".
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
# tl-import
|
|
8
|
-
|
|
9
|
-
Imports a list of identifiers (channels / brands / articles / sponsorships) into a report. Two flows depending on whether the user references an existing report or wants a new one — see "Decide which flow" below. Both end in the same step: `tl bulk-import` submits the identifiers, polls until done, and the skill renders a per-row result table.
|
|
10
|
-
|
|
11
|
-
## When to use
|
|
12
|
-
|
|
13
|
-
The deciding test is the **user's intent**, not just what they pasted. The user must want the listed entities to land in a report as-given — no filtering, no analysis, no similarity expansion on top.
|
|
14
|
-
|
|
15
|
-
Trigger on:
|
|
16
|
-
|
|
17
|
-
- "Import @mkbhd, @veritasium into report 1234" → **existing-report flow**
|
|
18
|
-
- "Add these brands to campaign 5678" → **existing-report flow**
|
|
19
|
-
- "Bulk-add this list of channels to report 999" → **existing-report flow**
|
|
20
|
-
- "Exclude these channels from report Z" → **existing-report flow**
|
|
21
|
-
- "Create a new report with these channels: \<list\>" → **new-report flow**
|
|
22
|
-
- "Make a campaign containing these brands: \<list\>" → **new-report flow**
|
|
23
|
-
- "Build me a report from these adlinks: \<list\>" → **new-report flow** *(the verb "build" doesn't matter — what matters is that the user wants exactly those adlinks in the report.)*
|
|
24
|
-
|
|
25
|
-
**Do NOT trigger** when the user pastes a list but wants something other than direct import — those belong to `tl-cli:tl` (analysis / discovery) or `tl-cli:tl-save-report` (persist a session's result set):
|
|
26
|
-
|
|
27
|
-
- *"Find me channels similar to these: \<list\>"* — discovery using the list as a seed, not as the answer.
|
|
28
|
-
- *"Build a report of TPP channels in the same niche as these: \<list\>"* — discovery with filters and similarity expansion.
|
|
29
|
-
- *"Compare engagement across these channels"* — analysis on top of the list.
|
|
30
|
-
- *"Show me which of these have sponsored fintech brands"* — filtered lookup.
|
|
31
|
-
|
|
32
|
-
If you're about to do anything beyond "put these exact entities into a report", the wrong skill is running.
|
|
33
|
-
|
|
34
|
-
Single-identifier requests still work for the import intent (the command accepts one). The reason to keep this skill separate from other report-edit flows: it's the only path that auto-creates channels from YouTube URLs / handles, and brands from website domains.
|
|
35
|
-
|
|
36
|
-
## Decide which flow
|
|
37
|
-
|
|
38
|
-
Look at the user's request and pick exactly one of three responses:
|
|
39
|
-
|
|
40
|
-
| Signal | Response |
|
|
41
|
-
|---|---|
|
|
42
|
-
| User references an existing report (campaign ID number, `?campaign=<id>` in a pasted URL, "report X", "this campaign") | **Existing-report flow** — skip to "Inputs to gather" |
|
|
43
|
-
| User explicitly asks for a new report ("new report", "a new campaign", "create a report with…", "make a campaign of…") | **New-report flow** — read "Create a fresh container first" below, then continue |
|
|
44
|
-
| User provides a list with no destination cue at all (no campaign reference AND no "new" wording) | **Ambiguous — ask once** before proceeding: *"Should I add these to an existing report (give me the report ID or URL), or create a new one?"* Wait for the answer. Then dispatch to the matching flow above. |
|
|
45
|
-
|
|
46
|
-
Never silently create a new report when the destination is ambiguous; never silently use an existing report when none was referenced. The skill's only acceptable action without a clear destination is to ask.
|
|
47
|
-
|
|
48
|
-
## Create a fresh container first (new-report flow only)
|
|
49
|
-
|
|
50
|
-
The user wants the report to contain exactly the identifiers they're about to import — nothing else. No keyword research, no discovery query, no review pipeline. Just a minimal container that holds the list. **The persistence step is `tl reports create --config-file`** with a tiny config — no upstream discovery / review phases.
|
|
51
|
-
|
|
52
|
-
Steps:
|
|
53
|
-
|
|
54
|
-
1. **Title.** If the user gave one (e.g. *"create a Q1 cohort report with…"* → title *"Q1 cohort"*), use it. Otherwise ask once: *"What should I name the new report?"* Title must be ≤ 60 chars, non-empty.
|
|
55
|
-
2. **Description.** Auto-generate a 1-sentence description; don't ask the user. Format: `"Bulk-imported list of <N> <entity> (<YYYY-MM-DD>)."`. Required by the platform on save (not optional).
|
|
56
|
-
3. **Map entity → `report_type`:**
|
|
57
|
-
- `channels` → **3** (THOUGHTLEADERS)
|
|
58
|
-
- `brands` → **2** (BRANDS)
|
|
59
|
-
- `articles` (uploads/videos) → **1** (CONTENT)
|
|
60
|
-
- `sponsorships` (adlinks/deals) → **8** (CAMPAIGN_MANAGEMENT)
|
|
61
|
-
4. **Pick default columns.** Read the matching columns reference file in the sibling `tl-save-report` skill and use its **"Defaults — always include"** section — that's where the canonical column list lives per type; do NOT restate it here. The four files:
|
|
62
|
-
- channels → `../tl-save-report/references/columns_channels.md`
|
|
63
|
-
- brands → `../tl-save-report/references/columns_brands.md`
|
|
64
|
-
- articles → `../tl-save-report/references/columns_content.md`
|
|
65
|
-
- sponsorships → `../tl-save-report/references/columns_sponsorships.md`
|
|
66
|
-
|
|
67
|
-
Convert each display name from the "Defaults — always include" list into a column entry shape **`{"display": true, "width": "default"}`** — the `width` field is required by the dashboard's column renderer; without it, columns sometimes resolve but cells render empty. Use `"wide"` for narrative columns (e.g. `TL Channel Summary`, `Channel Description`, `Topic Descriptions`); use `"narrow"` for short numeric columns (e.g. `Status`, `Country`); `"default"` everywhere else is safe.
|
|
68
|
-
|
|
69
|
-
5. **Pick `dataset_structure`.** This block tells the dashboard's data plane how to query each row's cell values. **Without it the report saves but rows render empty** — see the bottom-of-section sanity check. Shape:
|
|
70
|
-
|
|
71
|
-
```json
|
|
72
|
-
"dataset_structure": {
|
|
73
|
-
"report_type": <same as the top-level report_type>,
|
|
74
|
-
"page_size": 50,
|
|
75
|
-
"sort": "<backend_code field, optionally -prefixed for descending>"
|
|
76
|
-
}
|
|
77
|
-
```
|
|
78
|
-
|
|
79
|
-
Per-type default sort. **Critical invariant:** the `sort` field must reference a `backend_code` whose display-name column is in the column set you emitted in step 4. The dashboard's renderer rejects sorts pointing at columns that aren't present in the report. So pick the intersection of (a) the type's "Defaults — always include" columns from `columns_<type>.md` and (b) sortable columns from `../tl-save-report/references/sortable_columns.json`:
|
|
80
|
-
|
|
81
|
-
| report_type | entity | default `sort` | maps to (must be in column set) |
|
|
82
|
-
|---|---|---|---|
|
|
83
|
-
| 3 | channels | `-reach` | `Subscribers` (in defaults) |
|
|
84
|
-
| 2 | brands | `-doc_count` | `Mentions` (in defaults) |
|
|
85
|
-
| 1 | articles | `-publication_date` | `Date` (in defaults) |
|
|
86
|
-
| 8 | sponsorships | `-send_date` | `Scheduled Date` (in defaults) |
|
|
87
|
-
|
|
88
|
-
If the user explicitly asked for a different sort, honor that — but if their preferred sort column isn't in the type's defaults, **add that column to the column set in step 4** before emitting the config. Sort pointing at an absent column re-creates the original render-failure bug.
|
|
89
|
-
|
|
90
|
-
6. **Compose the minimal config:**
|
|
91
|
-
|
|
92
|
-
```json
|
|
93
|
-
{
|
|
94
|
-
"report_title": "<from step 1>",
|
|
95
|
-
"report_description": "<from step 2>",
|
|
96
|
-
"report_type": <from step 3>,
|
|
97
|
-
"type": 2,
|
|
98
|
-
"filterset": {},
|
|
99
|
-
"columns": <from step 4>,
|
|
100
|
-
"dataset_structure": <from step 5>
|
|
101
|
-
}
|
|
102
|
-
```
|
|
103
|
-
|
|
104
|
-
`type: 2` is DYNAMIC (the only valid campaign type for save). `filterset: {}` is intentional — no keyword/topic/demographic filters; the report's contents will come entirely from the include list bulk-import populates next. **`dataset_structure` is what makes the rows render with actual values** — leave it out and the dashboard shows row numbers but blank cells.
|
|
105
|
-
7. **Persist with `tl reports create --config-file`.** Write the config dict to a temp file using your file-writing tool — **do not use shell `echo` or heredocs**, those break on titles containing apostrophes, dollar signs, backticks, etc. The whole point of `--config-file` is to bypass shell quoting entirely. Pick any temp path the agent's filesystem tool can write to (e.g. `/tmp/tl-import-container.json` on Unix, the OS temp dir on Windows).
|
|
106
|
-
|
|
107
|
-
Then run:
|
|
108
|
-
|
|
109
|
-
```bash
|
|
110
|
-
tl reports create --config-file <path-you-just-wrote> --yes --json
|
|
111
|
-
```
|
|
112
|
-
|
|
113
|
-
With `--yes --json` the CLI emits a single JSON document on stdout containing the save response — parse it with one `json.loads()` and pull out `campaign_id` (and `report_url` for the summary). If `tl reports create` returns HTTP 400 with `Missing required field: report_title` or `…report_description`, the config is malformed — re-check step 1/2.
|
|
114
|
-
|
|
115
|
-
8. **Run bulk-import, capture the result — but DO NOT render the success summary yet.** Hand off to the bulk-import path with the new `campaign_id` and execute "Inputs to gather" + the bulk-import call + the JSON-envelope parse. **Stop before** rendering the per-row classification table or any "import done" message. Step 9 below must run first; only then do you render the summary. If you find yourself about to emit the success markdown straight out of the bulk-import flow, stop — you skipped step 9.
|
|
116
|
-
|
|
117
|
-
9. **Post-import render check (must execute before any success summary is emitted).** The save accepts the config; the renderer can still drop columns silently (e.g. `sort` points at a column you didn't emit, or `width` is missing on entries that needed it). Run:
|
|
118
|
-
|
|
119
|
-
```bash
|
|
120
|
-
tl reports run <campaign_id> --limit 3 --json
|
|
121
|
-
```
|
|
122
|
-
|
|
123
|
-
- If `results` is non-empty AND each row has fields beyond just an ID → render works. Now surface the bulk-import success summary (headline + per-row classification table) plus the new report URL.
|
|
124
|
-
- If `results` is non-empty but each row is mostly null/empty fields → the config has a render bug. Surface to the user **instead of** the normal success message: *"The bulk-import succeeded but the report is rendering with empty cells. Add columns via the dashboard UI, or delete and re-run the import."* Don't hide this — the import already happened; the user needs to know they have a partially-broken report. Still include the bulk-import's `inputs` classification table so they see what landed.
|
|
125
|
-
- If `results` is unexpectedly empty (shouldn't happen post-bulk-import unless every row failed) → surface the bulk-import's `inputs` table to explain which rows failed; skip the "Created [report] and imported N" headline since N=0.
|
|
126
|
-
|
|
127
|
-
*Cost: a small report-run credit. Worth it — silently handing the user a report whose cells are blank is worse than telling them upfront.*
|
|
128
|
-
|
|
129
|
-
Once step 9 passes, surface the new report URL alongside the bulk-import results in the final summary, e.g. *"Created [Q1 cohort](https://app.thoughtleaders.io/#/thoughtleaders?campaign=23859) and imported 50 channels:"* followed by the per-row table.
|
|
130
|
-
|
|
131
|
-
## Inputs to gather
|
|
132
|
-
|
|
133
|
-
Before running, confirm:
|
|
134
|
-
|
|
135
|
-
1. **Report ID** (`--campaign` / `-c`) — required. If the user pastes a TL URL (e.g. `https://app.thoughtleaders.io/#/thoughtleaders?campaign=23859&...`), the integer after `campaign=` is the ID. In the new-report flow, use the `campaign_id` returned by `tl reports create` above.
|
|
136
|
-
2. **Entity type** — one of `channels` / `brands` / `articles` / `sponsorships`. Infer from context, but translate user-facing vocabulary:
|
|
137
|
-
- YouTube URLs / handles / `UC…` IDs → `channels`
|
|
138
|
-
- Domains / brand slugs → `brands`
|
|
139
|
-
- "videos" / "uploads" / video URLs / video IDs → `articles` *(the CLI surfaces them as uploads — `tl uploads show <id>` — but `bulk-import` expects `articles`; same concept, legacy naming)*
|
|
140
|
-
- "adlinks" / "deals" / "sponsorships" / numeric AdLink IDs → `sponsorships`
|
|
141
|
-
3. **Identifiers** — the list. Accepted shapes per entity:
|
|
142
|
-
- **channels**: numeric DB IDs, YouTube channel IDs (`UC…`), `@handles`, full YouTube URLs (`/@…`, `/channel/UC…`, `/user/…`)
|
|
143
|
-
- **brands**: numeric IDs, slugs, websites / domains (`example.com`)
|
|
144
|
-
- **articles** (uploads): video IDs or video URLs
|
|
145
|
-
- **sponsorships** (adlinks): numeric AdLink IDs only
|
|
146
|
-
4. **Include vs exclude** — default is include (add to the report). Pass `--exclude` only if the user explicitly wants to remove from the report.
|
|
147
|
-
|
|
148
|
-
## How to invoke
|
|
149
|
-
|
|
150
|
-
The command reads identifiers from a file (`--ids-file`) or stdin:
|
|
151
|
-
|
|
152
|
-
```bash
|
|
153
|
-
# small list — stdin
|
|
154
|
-
echo '@mkbhd
|
|
155
|
-
@veritasium
|
|
156
|
-
@lemmino' | tl bulk-import channels --campaign 1234
|
|
157
|
-
|
|
158
|
-
# larger list — file
|
|
159
|
-
tl bulk-import channels --campaign 1234 --ids-file ./channels.txt
|
|
160
|
-
|
|
161
|
-
# exclusion
|
|
162
|
-
tl bulk-import brands --campaign 5678 -f ./brands.txt --exclude
|
|
163
|
-
```
|
|
164
|
-
|
|
165
|
-
Short flags: `-c` for `--campaign`, `-f` for `--ids-file`.
|
|
166
|
-
|
|
167
|
-
## Output: the `inputs` envelope
|
|
168
|
-
|
|
169
|
-
`tl bulk-import` returns a JSON envelope. Use the **`inputs`** array as the source of truth for what to render — it has one row per submitted identifier, in input order, with everything you need to classify and display.
|
|
170
|
-
|
|
171
|
-
```json
|
|
172
|
-
{
|
|
173
|
-
"task_id": "...",
|
|
174
|
-
"mode": "include",
|
|
175
|
-
"inputs": [
|
|
176
|
-
{"input": "@mkbhd", "resolved_id": 4587, "reason": "Success", "newly_created": false},
|
|
177
|
-
{"input": "@veritasium", "resolved_id": 1209, "reason": "Duplicate", "newly_created": false},
|
|
178
|
-
{"input": "@OfficialSaharTV", "resolved_id": 1328906, "reason": "Success", "newly_created": true},
|
|
179
|
-
{"input": "https://bad-url", "resolved_id": null, "reason": "Not found", "newly_created": false}
|
|
180
|
-
],
|
|
181
|
-
"success_ids": [4587, 1328906],
|
|
182
|
-
"newly_created_ids": [1328906],
|
|
183
|
-
"failed_ids": [...],
|
|
184
|
-
...
|
|
185
|
-
}
|
|
186
|
-
```
|
|
187
|
-
|
|
188
|
-
Each `inputs` row's `input` field echoes the raw identifier the user submitted (unchanged). `resolved_id` is the entity ID it matched/created, or `null` for failures. `reason` and `newly_created` drive the row's display label below.
|
|
189
|
-
|
|
190
|
-
**`mode` echoes back the operation mode** (`"include"` or `"exclude"`). You need this for labelling because the semantics flip:
|
|
191
|
-
|
|
192
|
-
- include + Success = identifier was just added to the report
|
|
193
|
-
- exclude + Success = identifier was just removed from the report
|
|
194
|
-
- include + Duplicate = identifier was already in the report (no-op)
|
|
195
|
-
- exclude + Duplicate = identifier was already excluded (no-op)
|
|
196
|
-
|
|
197
|
-
Don't use `success_ids` / `failed_ids` for display — they lose input mapping and miss the include/exclude direction. `inputs` is the canonical surface.
|
|
198
|
-
|
|
199
|
-
## Classify each row
|
|
200
|
-
|
|
201
|
-
| `reason` | `newly_created` | `mode` | Icon | Label |
|
|
202
|
-
|---|---|---|---|---|
|
|
203
|
-
| `Success` | `true` | `include` | 🆕 | Created in TL |
|
|
204
|
-
| `Success` | `true` | `exclude` | ⚠️ | Created in TL — unexpected for exclude, verify report state |
|
|
205
|
-
| `Success` | `false` | `include` | ✅ | Added |
|
|
206
|
-
| `Success` | `false` | `exclude` | ✂️ | Excluded |
|
|
207
|
-
| `Duplicate` | any | `include` | ↺ | Already in report |
|
|
208
|
-
| `Duplicate` | any | `exclude` | ↺ | Already excluded |
|
|
209
|
-
| `Not found` | any | any | ❌ | Not found |
|
|
210
|
-
| `Cannot parse` | any | any | ❌ | Bad format |
|
|
211
|
-
| `Multiple matches found` | any | any | ❌ | Ambiguous (multiple matches) |
|
|
212
|
-
| `Limit exceeded` | any | any | ❌ | Auto-create cap hit |
|
|
213
|
-
| starts with `Error:` | any | any | ❌ | Error (show reason verbatim) |
|
|
214
|
-
| anything else | any | any | ❌ | Failed (show reason verbatim) |
|
|
215
|
-
|
|
216
|
-
For 🆕 rows: mention that enrichment (subscriber stats, AI description, demographics for channels; metadata for brands) is queued and will populate over the next few minutes — these entities just entered the database.
|
|
217
|
-
|
|
218
|
-
For ⚠️ rows: if an exclude import returns `newly_created: true`, treat it as unexpected. Tell the user the channel was created but does not appear to have been excluded — ask them to verify the report and re-submit the exclude against the returned `resolved_id` if needed.
|
|
219
|
-
|
|
220
|
-
## Display
|
|
221
|
-
|
|
222
|
-
Per-row markdown table. **Headline first** with the gain count, then the table.
|
|
223
|
-
|
|
224
|
-
For include mode:
|
|
225
|
-
|
|
226
|
-
```markdown
|
|
227
|
-
**Bulk-import to report 23859 — done.** Report gained **2** rows; **1** was already there; **1** failed.
|
|
228
|
-
|
|
229
|
-
| # | Status | Input | ID | Reason |
|
|
230
|
-
|---|---|---|---|---|
|
|
231
|
-
| 1 | ✅ Added | `@mkbhd` | 4587 | Success |
|
|
232
|
-
| 2 | ↺ Already in report | `@veritasium` | 1209 | Duplicate |
|
|
233
|
-
| 3 | 🆕 Created in TL | `@OfficialSaharTV` | 1328906 | Success — enrichment queued |
|
|
234
|
-
| 4 | ❌ Not found | `https://bad-url` | — | Not found |
|
|
235
|
-
```
|
|
236
|
-
|
|
237
|
-
For exclude mode, headline uses "lost" wording:
|
|
238
|
-
|
|
239
|
-
```markdown
|
|
240
|
-
**Bulk-import (exclude) to report 23859 — done.** Report lost **N** rows; **M** were already excluded.
|
|
241
|
-
```
|
|
242
|
-
|
|
243
|
-
Display rules:
|
|
244
|
-
|
|
245
|
-
- **Use the user's raw `input` value** in the Input column (it's `inputs[i].input` — the raw submitted string, unchanged).
|
|
246
|
-
- **Omit any column that's uniformly empty** — for sponsorships, the "Input" and "ID" columns are usually identical (both numeric); the Reason column carries the signal.
|
|
247
|
-
- **Small imports (≤30 rows):** render the full table.
|
|
248
|
-
- **Large imports (>30 rows):** lead with a summary table of bucket counts; render the per-row table only for **non-bulk-success rows** — i.e. omit the dominant happy-path bucket, which is ✅ Added in include mode and ✂️ Excluded in exclude mode. The rows the user cares about (already-present, newly-created, failed, unexpected) all stay. Offer to dump the omitted rows on request.
|
|
249
|
-
|
|
250
|
-
Summary table (include mode example):
|
|
251
|
-
|
|
252
|
-
```markdown
|
|
253
|
-
| Bucket | Count |
|
|
254
|
-
|---|---|
|
|
255
|
-
| ✅ Added | 142 |
|
|
256
|
-
| ↺ Already in report | 7 |
|
|
257
|
-
| 🆕 Created in TL | 3 |
|
|
258
|
-
| ❌ Failed | 2 |
|
|
259
|
-
| **Total submitted** | **154** |
|
|
260
|
-
```
|
|
261
|
-
|
|
262
|
-
Summary table (exclude mode example):
|
|
263
|
-
|
|
264
|
-
```markdown
|
|
265
|
-
| Bucket | Count |
|
|
266
|
-
|---|---|
|
|
267
|
-
| ✂️ Excluded | 142 |
|
|
268
|
-
| ↺ Already excluded | 7 |
|
|
269
|
-
| ❌ Failed | 2 |
|
|
270
|
-
| **Total submitted** | **151** |
|
|
271
|
-
```
|
|
272
|
-
|
|
273
|
-
- **Never look up entity names** with extra `tl channels show <id>` / `tl brands show <id>` calls just to populate a "Name" column — those are metered. The user's input column is enough for them to identify each row. If the user explicitly asks "what are these channels called?", then look them up.
|
|
274
|
-
|
|
275
|
-
## Errors at the command level (before the per-row results)
|
|
276
|
-
|
|
277
|
-
These are envelope-level failures, distinct from per-row `reason` values:
|
|
278
|
-
|
|
279
|
-
- **403** → caller isn't a superuser. Stop and tell the user; this command is gated.
|
|
280
|
-
- **400** → bad input shape (missing field, unknown entity, all-empty identifiers). Show the `detail` verbatim.
|
|
281
|
-
- **402** → out of credits. Tell the user to top up.
|
|
282
|
-
- **Connection failed** → transient network issue. Retry once; if it persists, surface to the user.
|
|
283
|
-
|
|
284
|
-
## What this skill does NOT do
|
|
285
|
-
|
|
286
|
-
- Doesn't run a discovery pipeline (keyword research, topic matching, validation cycles, review). When a user gives a fixed list of identifiers, they've already done the discovery themselves — the report is a container for their list, not a query result. Use the `tl` skill to *find* channels/brands/etc. by criteria first; if the user then wants to save those criteria as a live report, use `tl-save-report`.
|
|
287
|
-
- Doesn't change existing report metadata (title, description, columns, filters) after creation. For that, use the platform UI or a dedicated edit flow. The new-report flow in this skill sets minimum-required metadata once at creation and never revisits it.
|
|
288
|
-
- Doesn't validate identifiers ahead of time — submit and let the per-row `reason` tell the user which ones failed. Pre-checking with `tl channels show` / etc. is wasteful (metered) and adds latency.
|
|
289
|
-
- Doesn't sweep duplicates from the user's input list — submit them as-is. The response will mark the second occurrence as `Duplicate`, which is more informative than silently deduping.
|