thoughtleaders-cli 0.9.2__tar.gz → 0.9.4__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (157) hide show
  1. {thoughtleaders_cli-0.9.2 → thoughtleaders_cli-0.9.4}/.claude-plugin/plugin.json +1 -1
  2. {thoughtleaders_cli-0.9.2 → thoughtleaders_cli-0.9.4}/.github/workflows/ci.yml +2 -2
  3. {thoughtleaders_cli-0.9.2 → thoughtleaders_cli-0.9.4}/.github/workflows/cli-integration.yml +2 -2
  4. {thoughtleaders_cli-0.9.2 → thoughtleaders_cli-0.9.4}/.github/workflows/python-publish.yml +1 -1
  5. {thoughtleaders_cli-0.9.2 → thoughtleaders_cli-0.9.4}/API.md +4 -0
  6. {thoughtleaders_cli-0.9.2 → thoughtleaders_cli-0.9.4}/PKG-INFO +16 -1
  7. {thoughtleaders_cli-0.9.2 → thoughtleaders_cli-0.9.4}/README.md +15 -0
  8. {thoughtleaders_cli-0.9.2 → thoughtleaders_cli-0.9.4}/pyproject.toml +1 -1
  9. {thoughtleaders_cli-0.9.2 → thoughtleaders_cli-0.9.4}/skills/tl/SKILL.md +17 -9
  10. thoughtleaders_cli-0.9.4/skills/tl-create-workflow/SKILL.md +245 -0
  11. thoughtleaders_cli-0.9.4/skills/tl-create-workflow/references/creating-in-app.md +95 -0
  12. thoughtleaders_cli-0.9.4/skills/tl-create-workflow/references/methodology.md +70 -0
  13. thoughtleaders_cli-0.9.4/skills/tl-create-workflow/references/pitfalls.md +68 -0
  14. thoughtleaders_cli-0.9.4/skills/tl-create-workflow/references/workflow-model.md +89 -0
  15. {thoughtleaders_cli-0.9.2 → thoughtleaders_cli-0.9.4}/src/tl_cli/__init__.py +1 -1
  16. {thoughtleaders_cli-0.9.2 → thoughtleaders_cli-0.9.4}/src/tl_cli/client/http.py +12 -8
  17. {thoughtleaders_cli-0.9.2 → thoughtleaders_cli-0.9.4}/src/tl_cli/commands/balance.py +6 -0
  18. {thoughtleaders_cli-0.9.2 → thoughtleaders_cli-0.9.4}/src/tl_cli/commands/db.py +5 -3
  19. {thoughtleaders_cli-0.9.2 → thoughtleaders_cli-0.9.4}/src/tl_cli/commands/setup.py +30 -1
  20. thoughtleaders_cli-0.9.4/src/tl_cli/commands/skills.py +437 -0
  21. thoughtleaders_cli-0.9.4/src/tl_cli/commands/workflows.py +159 -0
  22. {thoughtleaders_cli-0.9.2 → thoughtleaders_cli-0.9.4}/src/tl_cli/main.py +11 -0
  23. thoughtleaders_cli-0.9.4/src/tl_cli/skill_registry.py +312 -0
  24. thoughtleaders_cli-0.9.4/tests/test_setup_marker_respect.py +214 -0
  25. thoughtleaders_cli-0.9.4/tests/test_skill_registry.py +376 -0
  26. thoughtleaders_cli-0.9.4/tests/test_skills_command.py +420 -0
  27. thoughtleaders_cli-0.9.4/tests/test_workflows.py +79 -0
  28. {thoughtleaders_cli-0.9.2 → thoughtleaders_cli-0.9.4}/uv.lock +7 -19
  29. {thoughtleaders_cli-0.9.2 → thoughtleaders_cli-0.9.4}/.claude-plugin/marketplace.json +0 -0
  30. {thoughtleaders_cli-0.9.2 → thoughtleaders_cli-0.9.4}/.github/dependabot.yml +0 -0
  31. {thoughtleaders_cli-0.9.2 → thoughtleaders_cli-0.9.4}/.gitignore +0 -0
  32. {thoughtleaders_cli-0.9.2 → thoughtleaders_cli-0.9.4}/AGENTS.md +0 -0
  33. {thoughtleaders_cli-0.9.2 → thoughtleaders_cli-0.9.4}/CLAUDE.md +0 -0
  34. {thoughtleaders_cli-0.9.2 → thoughtleaders_cli-0.9.4}/LICENSE +0 -0
  35. {thoughtleaders_cli-0.9.2 → thoughtleaders_cli-0.9.4}/agents/keyword-context-classifier.md +0 -0
  36. {thoughtleaders_cli-0.9.2 → thoughtleaders_cli-0.9.4}/agents/keyword-entity-resolver.md +0 -0
  37. {thoughtleaders_cli-0.9.2 → thoughtleaders_cli-0.9.4}/agents/keyword-relevance-validator.md +0 -0
  38. {thoughtleaders_cli-0.9.2 → thoughtleaders_cli-0.9.4}/agents/tl-analyst.md +0 -0
  39. {thoughtleaders_cli-0.9.2 → thoughtleaders_cli-0.9.4}/agents/youtube-comment-classifier.md +0 -0
  40. {thoughtleaders_cli-0.9.2 → thoughtleaders_cli-0.9.4}/hooks/hooks.json +0 -0
  41. {thoughtleaders_cli-0.9.2 → thoughtleaders_cli-0.9.4}/hooks/scripts/load-tl-skill.mjs +0 -0
  42. {thoughtleaders_cli-0.9.2 → thoughtleaders_cli-0.9.4}/hooks/scripts/post-usage.sh +0 -0
  43. {thoughtleaders_cli-0.9.2 → thoughtleaders_cli-0.9.4}/hooks/scripts/pre-check.sh +0 -0
  44. {thoughtleaders_cli-0.9.2 → thoughtleaders_cli-0.9.4}/skills/tl/references/business-glossary.md +0 -0
  45. {thoughtleaders_cli-0.9.2 → thoughtleaders_cli-0.9.4}/skills/tl/references/elasticsearch-schema.md +0 -0
  46. {thoughtleaders_cli-0.9.2 → thoughtleaders_cli-0.9.4}/skills/tl/references/firebolt-schema.md +0 -0
  47. {thoughtleaders_cli-0.9.2 → thoughtleaders_cli-0.9.4}/skills/tl/references/postgres-schema.md +0 -0
  48. {thoughtleaders_cli-0.9.2 → thoughtleaders_cli-0.9.4}/skills/tl-channel-authenticity/.gitignore +0 -0
  49. {thoughtleaders_cli-0.9.2 → thoughtleaders_cli-0.9.4}/skills/tl-channel-authenticity/SKILL.md +0 -0
  50. {thoughtleaders_cli-0.9.2 → thoughtleaders_cli-0.9.4}/skills/tl-channel-authenticity/references/comment-patterns.md +0 -0
  51. {thoughtleaders_cli-0.9.2 → thoughtleaders_cli-0.9.4}/skills/tl-channel-authenticity/references/peer-cohort.md +0 -0
  52. {thoughtleaders_cli-0.9.2 → thoughtleaders_cli-0.9.4}/skills/tl-channel-authenticity/references/red-flags.md +0 -0
  53. {thoughtleaders_cli-0.9.2 → thoughtleaders_cli-0.9.4}/skills/tl-channel-authenticity/references/scoring.md +0 -0
  54. {thoughtleaders_cli-0.9.2 → thoughtleaders_cli-0.9.4}/skills/tl-channel-authenticity/scripts/_io_utf8.py +0 -0
  55. {thoughtleaders_cli-0.9.2 → thoughtleaders_cli-0.9.4}/skills/tl-channel-authenticity/scripts/analyze_channel.py +0 -0
  56. {thoughtleaders_cli-0.9.2 → thoughtleaders_cli-0.9.4}/skills/tl-channel-authenticity/scripts/anomaly_detector.py +0 -0
  57. {thoughtleaders_cli-0.9.2 → thoughtleaders_cli-0.9.4}/skills/tl-channel-authenticity/scripts/comment_analyzer.py +0 -0
  58. {thoughtleaders_cli-0.9.2 → thoughtleaders_cli-0.9.4}/skills/tl-channel-authenticity/scripts/comment_scraper.py +0 -0
  59. {thoughtleaders_cli-0.9.2 → thoughtleaders_cli-0.9.4}/skills/tl-channel-authenticity/scripts/engagement_ratios.py +0 -0
  60. {thoughtleaders_cli-0.9.2 → thoughtleaders_cli-0.9.4}/skills/tl-channel-authenticity/scripts/peer_cohort.py +0 -0
  61. {thoughtleaders_cli-0.9.2 → thoughtleaders_cli-0.9.4}/skills/tl-channel-authenticity/scripts/report.py +0 -0
  62. {thoughtleaders_cli-0.9.2 → thoughtleaders_cli-0.9.4}/skills/tl-channel-authenticity/scripts/resolve_channel.py +0 -0
  63. {thoughtleaders_cli-0.9.2 → thoughtleaders_cli-0.9.4}/skills/tl-channel-authenticity/scripts/score.py +0 -0
  64. {thoughtleaders_cli-0.9.2 → thoughtleaders_cli-0.9.4}/skills/tl-channel-authenticity/scripts/tl_cli.py +0 -0
  65. {thoughtleaders_cli-0.9.2 → thoughtleaders_cli-0.9.4}/skills/tl-channel-authenticity/scripts/video_integrity.py +0 -0
  66. {thoughtleaders_cli-0.9.2 → thoughtleaders_cli-0.9.4}/skills/tl-channel-authenticity/scripts/view_curves.py +0 -0
  67. {thoughtleaders_cli-0.9.2 → thoughtleaders_cli-0.9.4}/skills/tl-keyword-research/SKILL.md +0 -0
  68. {thoughtleaders_cli-0.9.2 → thoughtleaders_cli-0.9.4}/skills/tl-keyword-research/references/elasticsearch-content-search.md +0 -0
  69. {thoughtleaders_cli-0.9.2 → thoughtleaders_cli-0.9.4}/skills/tl-keyword-research/references/help.md +0 -0
  70. {thoughtleaders_cli-0.9.2 → thoughtleaders_cli-0.9.4}/skills/tl-keyword-research/scripts/build_report.py +0 -0
  71. {thoughtleaders_cli-0.9.2 → thoughtleaders_cli-0.9.4}/skills/tl-keyword-research/scripts/expand_entities.py +0 -0
  72. {thoughtleaders_cli-0.9.2 → thoughtleaders_cli-0.9.4}/skills/tl-keyword-research/scripts/fetch_context.py +0 -0
  73. {thoughtleaders_cli-0.9.2 → thoughtleaders_cli-0.9.4}/skills/tl-keyword-research/scripts/probe.py +0 -0
  74. {thoughtleaders_cli-0.9.2 → thoughtleaders_cli-0.9.4}/skills/tl-keyword-research/scripts/search_channels.py +0 -0
  75. {thoughtleaders_cli-0.9.2 → thoughtleaders_cli-0.9.4}/skills/tl-keyword-research/scripts/search_videos.py +0 -0
  76. {thoughtleaders_cli-0.9.2 → thoughtleaders_cli-0.9.4}/skills/tl-keyword-research/scripts/select_keywords.py +0 -0
  77. {thoughtleaders_cli-0.9.2 → thoughtleaders_cli-0.9.4}/skills/tl-save-report/SKILL.md +0 -0
  78. {thoughtleaders_cli-0.9.2 → thoughtleaders_cli-0.9.4}/skills/tl-save-report/references/columns_brands.md +0 -0
  79. {thoughtleaders_cli-0.9.2 → thoughtleaders_cli-0.9.4}/skills/tl-save-report/references/columns_channels.md +0 -0
  80. {thoughtleaders_cli-0.9.2 → thoughtleaders_cli-0.9.4}/skills/tl-save-report/references/columns_content.md +0 -0
  81. {thoughtleaders_cli-0.9.2 → thoughtleaders_cli-0.9.4}/skills/tl-save-report/references/columns_sponsorships.md +0 -0
  82. {thoughtleaders_cli-0.9.2 → thoughtleaders_cli-0.9.4}/skills/tl-save-report/references/intelligence_filterset_schema.json +0 -0
  83. {thoughtleaders_cli-0.9.2 → thoughtleaders_cli-0.9.4}/skills/tl-save-report/references/intelligence_widget_schema.json +0 -0
  84. {thoughtleaders_cli-0.9.2 → thoughtleaders_cli-0.9.4}/skills/tl-save-report/references/report_glossary.md +0 -0
  85. {thoughtleaders_cli-0.9.2 → thoughtleaders_cli-0.9.4}/skills/tl-save-report/references/sortable_columns.json +0 -0
  86. {thoughtleaders_cli-0.9.2 → thoughtleaders_cli-0.9.4}/skills/tl-save-report/references/sponsorship_filterset_schema.json +0 -0
  87. {thoughtleaders_cli-0.9.2 → thoughtleaders_cli-0.9.4}/skills/tl-save-report/references/sponsorship_widget_schema.json +0 -0
  88. {thoughtleaders_cli-0.9.2 → thoughtleaders_cli-0.9.4}/skills/tl-save-report/references/widgets.md +0 -0
  89. {thoughtleaders_cli-0.9.2 → thoughtleaders_cli-0.9.4}/skills/tl-top-partnerships/SKILL.md +0 -0
  90. {thoughtleaders_cli-0.9.2 → thoughtleaders_cli-0.9.4}/skills/tl-top-partnerships/scripts/top_partnerships.py +0 -0
  91. {thoughtleaders_cli-0.9.2 → thoughtleaders_cli-0.9.4}/skills/tl-views-guarantee/SKILL.md +0 -0
  92. {thoughtleaders_cli-0.9.2 → thoughtleaders_cli-0.9.4}/skills/tl-views-guarantee/scripts/vg.py +0 -0
  93. {thoughtleaders_cli-0.9.2 → thoughtleaders_cli-0.9.4}/src/tl_cli/_completions.py +0 -0
  94. {thoughtleaders_cli-0.9.2 → thoughtleaders_cli-0.9.4}/src/tl_cli/_typer_utils.py +0 -0
  95. {thoughtleaders_cli-0.9.2 → thoughtleaders_cli-0.9.4}/src/tl_cli/auth/__init__.py +0 -0
  96. {thoughtleaders_cli-0.9.2 → thoughtleaders_cli-0.9.4}/src/tl_cli/auth/commands.py +0 -0
  97. {thoughtleaders_cli-0.9.2 → thoughtleaders_cli-0.9.4}/src/tl_cli/auth/login.py +0 -0
  98. {thoughtleaders_cli-0.9.2 → thoughtleaders_cli-0.9.4}/src/tl_cli/auth/pkce.py +0 -0
  99. {thoughtleaders_cli-0.9.2 → thoughtleaders_cli-0.9.4}/src/tl_cli/auth/token_store.py +0 -0
  100. {thoughtleaders_cli-0.9.2 → thoughtleaders_cli-0.9.4}/src/tl_cli/client/__init__.py +0 -0
  101. {thoughtleaders_cli-0.9.2 → thoughtleaders_cli-0.9.4}/src/tl_cli/client/errors.py +0 -0
  102. {thoughtleaders_cli-0.9.2 → thoughtleaders_cli-0.9.4}/src/tl_cli/commands/__init__.py +0 -0
  103. {thoughtleaders_cli-0.9.2 → thoughtleaders_cli-0.9.4}/src/tl_cli/commands/_comments_common.py +0 -0
  104. {thoughtleaders_cli-0.9.2 → thoughtleaders_cli-0.9.4}/src/tl_cli/commands/brands.py +0 -0
  105. {thoughtleaders_cli-0.9.2 → thoughtleaders_cli-0.9.4}/src/tl_cli/commands/bulk_import.py +0 -0
  106. {thoughtleaders_cli-0.9.2 → thoughtleaders_cli-0.9.4}/src/tl_cli/commands/changelog.py +0 -0
  107. {thoughtleaders_cli-0.9.2 → thoughtleaders_cli-0.9.4}/src/tl_cli/commands/channels.py +0 -0
  108. {thoughtleaders_cli-0.9.2 → thoughtleaders_cli-0.9.4}/src/tl_cli/commands/credits.py +0 -0
  109. {thoughtleaders_cli-0.9.2 → thoughtleaders_cli-0.9.4}/src/tl_cli/commands/deals.py +0 -0
  110. {thoughtleaders_cli-0.9.2 → thoughtleaders_cli-0.9.4}/src/tl_cli/commands/describe.py +0 -0
  111. {thoughtleaders_cli-0.9.2 → thoughtleaders_cli-0.9.4}/src/tl_cli/commands/doctor.py +0 -0
  112. {thoughtleaders_cli-0.9.2 → thoughtleaders_cli-0.9.4}/src/tl_cli/commands/matches.py +0 -0
  113. {thoughtleaders_cli-0.9.2 → thoughtleaders_cli-0.9.4}/src/tl_cli/commands/profiles.py +0 -0
  114. {thoughtleaders_cli-0.9.2 → thoughtleaders_cli-0.9.4}/src/tl_cli/commands/proposals.py +0 -0
  115. {thoughtleaders_cli-0.9.2 → thoughtleaders_cli-0.9.4}/src/tl_cli/commands/recommender.py +0 -0
  116. {thoughtleaders_cli-0.9.2 → thoughtleaders_cli-0.9.4}/src/tl_cli/commands/reports.py +0 -0
  117. {thoughtleaders_cli-0.9.2 → thoughtleaders_cli-0.9.4}/src/tl_cli/commands/schema.py +0 -0
  118. {thoughtleaders_cli-0.9.2 → thoughtleaders_cli-0.9.4}/src/tl_cli/commands/snapshots.py +0 -0
  119. {thoughtleaders_cli-0.9.2 → thoughtleaders_cli-0.9.4}/src/tl_cli/commands/sponsorships.py +0 -0
  120. {thoughtleaders_cli-0.9.2 → thoughtleaders_cli-0.9.4}/src/tl_cli/commands/uploads.py +0 -0
  121. {thoughtleaders_cli-0.9.2 → thoughtleaders_cli-0.9.4}/src/tl_cli/commands/whoami.py +0 -0
  122. {thoughtleaders_cli-0.9.2 → thoughtleaders_cli-0.9.4}/src/tl_cli/config.py +0 -0
  123. {thoughtleaders_cli-0.9.2 → thoughtleaders_cli-0.9.4}/src/tl_cli/filters.py +0 -0
  124. {thoughtleaders_cli-0.9.2 → thoughtleaders_cli-0.9.4}/src/tl_cli/hints.py +0 -0
  125. {thoughtleaders_cli-0.9.2 → thoughtleaders_cli-0.9.4}/src/tl_cli/output/__init__.py +0 -0
  126. {thoughtleaders_cli-0.9.2 → thoughtleaders_cli-0.9.4}/src/tl_cli/output/formatter.py +0 -0
  127. {thoughtleaders_cli-0.9.2 → thoughtleaders_cli-0.9.4}/src/tl_cli/query_history.py +0 -0
  128. {thoughtleaders_cli-0.9.2 → thoughtleaders_cli-0.9.4}/src/tl_cli/self_update.py +0 -0
  129. {thoughtleaders_cli-0.9.2 → thoughtleaders_cli-0.9.4}/tests/__init__.py +0 -0
  130. {thoughtleaders_cli-0.9.2 → thoughtleaders_cli-0.9.4}/tests/test_auth.py +0 -0
  131. {thoughtleaders_cli-0.9.2 → thoughtleaders_cli-0.9.4}/tests/test_brands_winner_channels.py +0 -0
  132. {thoughtleaders_cli-0.9.2 → thoughtleaders_cli-0.9.4}/tests/test_build_report.py +0 -0
  133. {thoughtleaders_cli-0.9.2 → thoughtleaders_cli-0.9.4}/tests/test_channels_lookalike.py +0 -0
  134. {thoughtleaders_cli-0.9.2 → thoughtleaders_cli-0.9.4}/tests/test_db_repeat_warning.py +0 -0
  135. {thoughtleaders_cli-0.9.2 → thoughtleaders_cli-0.9.4}/tests/test_describe.py +0 -0
  136. {thoughtleaders_cli-0.9.2 → thoughtleaders_cli-0.9.4}/tests/test_error_hints.py +0 -0
  137. {thoughtleaders_cli-0.9.2 → thoughtleaders_cli-0.9.4}/tests/test_expand_entities.py +0 -0
  138. {thoughtleaders_cli-0.9.2 → thoughtleaders_cli-0.9.4}/tests/test_filters.py +0 -0
  139. {thoughtleaders_cli-0.9.2 → thoughtleaders_cli-0.9.4}/tests/test_http_auth.py +0 -0
  140. {thoughtleaders_cli-0.9.2 → thoughtleaders_cli-0.9.4}/tests/test_output.py +0 -0
  141. {thoughtleaders_cli-0.9.2 → thoughtleaders_cli-0.9.4}/tests/test_probe.py +0 -0
  142. {thoughtleaders_cli-0.9.2 → thoughtleaders_cli-0.9.4}/tests/test_profiles.py +0 -0
  143. {thoughtleaders_cli-0.9.2 → thoughtleaders_cli-0.9.4}/tests/test_reports.py +0 -0
  144. {thoughtleaders_cli-0.9.2 → thoughtleaders_cli-0.9.4}/tests/test_scripts_executable.py +0 -0
  145. {thoughtleaders_cli-0.9.2 → thoughtleaders_cli-0.9.4}/tests/test_search_channels.py +0 -0
  146. {thoughtleaders_cli-0.9.2 → thoughtleaders_cli-0.9.4}/tests/test_search_videos.py +0 -0
  147. {thoughtleaders_cli-0.9.2 → thoughtleaders_cli-0.9.4}/tests/test_select_keywords.py +0 -0
  148. {thoughtleaders_cli-0.9.2 → thoughtleaders_cli-0.9.4}/tests/test_setup.py +0 -0
  149. {thoughtleaders_cli-0.9.2 → thoughtleaders_cli-0.9.4}/tests/test_sponsorships.py +0 -0
  150. {thoughtleaders_cli-0.9.2 → thoughtleaders_cli-0.9.4}/tests_cli/AGENTS.md +0 -0
  151. {thoughtleaders_cli-0.9.2 → thoughtleaders_cli-0.9.4}/tests_cli/conftest.py +0 -0
  152. {thoughtleaders_cli-0.9.2 → thoughtleaders_cli-0.9.4}/tests_cli/test_balance.py +0 -0
  153. {thoughtleaders_cli-0.9.2 → thoughtleaders_cli-0.9.4}/tests_cli/test_db_es.py +0 -0
  154. {thoughtleaders_cli-0.9.2 → thoughtleaders_cli-0.9.4}/tests_cli/test_db_fb.py +0 -0
  155. {thoughtleaders_cli-0.9.2 → thoughtleaders_cli-0.9.4}/tests_cli/test_db_pg.py +0 -0
  156. {thoughtleaders_cli-0.9.2 → thoughtleaders_cli-0.9.4}/tests_cli/test_schema.py +0 -0
  157. {thoughtleaders_cli-0.9.2 → thoughtleaders_cli-0.9.4}/tests_cli/test_whoami.py +0 -0
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tl-cli",
3
- "version": "0.9.2",
3
+ "version": "0.9.4",
4
4
  "description": "ThoughtLeaders CLI — query sponsorship deals, channels, brands, uploads, and intelligence from the terminal",
5
5
  "author": {
6
6
  "name": "ThoughtLeaders",
@@ -26,8 +26,8 @@ jobs:
26
26
  # Matches `requires-python = ">=3.12"`.
27
27
  python-version: ["3.12", "3.13", "3.14"]
28
28
  steps:
29
- - uses: actions/checkout@v6
30
- - uses: astral-sh/setup-uv@v6
29
+ - uses: actions/checkout@v7
30
+ - uses: astral-sh/setup-uv@v7
31
31
  with:
32
32
  enable-cache: true
33
33
  - name: Sync locked dependencies
@@ -29,8 +29,8 @@ jobs:
29
29
  name: Live read-only CLI tests
30
30
  runs-on: ubuntu-latest
31
31
  steps:
32
- - uses: actions/checkout@v6
33
- - uses: astral-sh/setup-uv@v6
32
+ - uses: actions/checkout@v7
33
+ - uses: astral-sh/setup-uv@v7
34
34
  with:
35
35
  enable-cache: true
36
36
  - name: Sync locked dependencies (installs the `tl` CLI)
@@ -11,7 +11,7 @@ jobs:
11
11
  release-build:
12
12
  runs-on: ubuntu-latest
13
13
  steps:
14
- - uses: actions/checkout@v6
14
+ - uses: actions/checkout@v7
15
15
  - uses: actions/setup-python@v6
16
16
  with:
17
17
  python-version: "3.12"
@@ -142,6 +142,8 @@ Useful for verifying the API key resolves to the user you expect before kicking
142
142
 
143
143
  `GET /balance` — credit balance plus the last 10 metered calls for the org. Free.
144
144
 
145
+ `balance` is the combined total of two pools: `topup_balance` (plan-granted credits, reset to the plan allowance each top-up period, spent first) and `purchased_balance` (bought credits — they survive top-up resets, are spent last, and keep working after the per-user session quota is exhausted).
146
+
145
147
  ```bash
146
148
  curl -sS "$TL_API_BASE/balance" \
147
149
  -H "Authorization: Bearer $TL_API_KEY" \
@@ -155,6 +157,8 @@ print(get('/balance'))
155
157
  ```json
156
158
  {
157
159
  "balance": 9995.88,
160
+ "topup_balance": 8995.88,
161
+ "purchased_balance": 1000.00,
158
162
  "allow_overage": false,
159
163
  "recent_usage": [
160
164
  {
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: thoughtleaders-cli
3
- Version: 0.9.2
3
+ Version: 0.9.4
4
4
  Summary: ThoughtLeaders CLI — query sponsorship data, channels, brands, and intelligence
5
5
  Project-URL: Homepage, https://thoughtleaders.io
6
6
  Project-URL: Repository, https://github.com/ThoughtLeaders-io/thoughtleaders-cli
@@ -284,6 +284,21 @@ The plugin ships several focused skills (installed by all the `tl setup *` comma
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.
285
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`).
286
286
 
287
+ ## Distributed skills
288
+
289
+ Beyond the bundled set above, an organization can be granted additional skills that aren't part of a CLI release — `tl skill` fetches and installs them on demand into the same directories `tl setup` uses (Claude Code's standalone skills directory, OpenCode's skills directory, and the directory shared by Gemini and Codex), so every supported agent picks them up automatically.
290
+
291
+ ```bash
292
+ tl skill list # skills available to your organization, with installed/latest versions
293
+ tl skill list --all # full catalog (full-access accounts only)
294
+ tl skill download my-skill # fetch and install into every AI-agent skill directory
295
+ tl skill download my-skill --force # overwrite a directory tl doesn't already manage
296
+ tl skill update # refresh every downloaded skill to its latest version
297
+ tl skill remove my-skill # uninstall a downloaded skill
298
+ ```
299
+
300
+ A directory `tl skill download` doesn't already manage (no prior `tl`-managed install there) is left alone unless `--force` is passed — it never overwrites a skill you installed some other way. `tl` warns once a day, on any command, when a downloaded skill has a newer version available. `tl setup` and self-update re-syncs also respect these directories: a downloaded skill is never rmtree'd or overwritten, even if its name collides with a bundled skill.
301
+
287
302
  ## Output Formats
288
303
 
289
304
  By default, output is a styled table in the terminal and JSON when piped.
@@ -256,6 +256,21 @@ The plugin ships several focused skills (installed by all the `tl setup *` comma
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.
257
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`).
258
258
 
259
+ ## Distributed skills
260
+
261
+ Beyond the bundled set above, an organization can be granted additional skills that aren't part of a CLI release — `tl skill` fetches and installs them on demand into the same directories `tl setup` uses (Claude Code's standalone skills directory, OpenCode's skills directory, and the directory shared by Gemini and Codex), so every supported agent picks them up automatically.
262
+
263
+ ```bash
264
+ tl skill list # skills available to your organization, with installed/latest versions
265
+ tl skill list --all # full catalog (full-access accounts only)
266
+ tl skill download my-skill # fetch and install into every AI-agent skill directory
267
+ tl skill download my-skill --force # overwrite a directory tl doesn't already manage
268
+ tl skill update # refresh every downloaded skill to its latest version
269
+ tl skill remove my-skill # uninstall a downloaded skill
270
+ ```
271
+
272
+ A directory `tl skill download` doesn't already manage (no prior `tl`-managed install there) is left alone unless `--force` is passed — it never overwrites a skill you installed some other way. `tl` warns once a day, on any command, when a downloaded skill has a newer version available. `tl setup` and self-update re-syncs also respect these directories: a downloaded skill is never rmtree'd or overwritten, even if its name collides with a bundled skill.
273
+
259
274
  ## Output Formats
260
275
 
261
276
  By default, output is a styled table in the terminal and JSON when piped.
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "thoughtleaders-cli"
7
- version = "0.9.2"
7
+ version = "0.9.4"
8
8
  description = "ThoughtLeaders CLI — query sponsorship data, channels, brands, and intelligence"
9
9
  readme = "README.md"
10
10
  license = "MIT"
@@ -242,7 +242,6 @@ tl channels update 12345 '{"demographic_male_share": 62}'
242
242
  tl channels update 12345 '{"demographic_geo": {"US": 60, "UK": 12, "CA": 8}}'
243
243
  tl channels update 12345 '{"demographic_male_share": 55, "demographic_usa_share": 70}'
244
244
  tl channels update 12345 '{"outreach_email": "press@creator.com"}'
245
- tl channels update 12345 '{"media_selling_network_join_date": "2026-01-15"}'
246
245
  ```
247
246
 
248
247
  **Channel contact emails.** Besides demographics, `tl channels update` accepts one
@@ -252,14 +251,8 @@ contact field:
252
251
 
253
252
  The channel's full email archive (`all_emails` — a JSON object keyed by email address,
254
253
  each value recording when and where the address was found) is **not editable here**;
255
- sending it returns a 400. It is append-only: new addresses are added with the
256
- internal CLI's `tl-internal channels add-email` command (superuser-only), which never
257
- modifies or removes existing entries.
258
-
259
- **MSN membership.** `tl channels update` also accepts **`media_selling_network_join_date`** —
260
- the date the channel joined the Media Selling Network, as a `YYYY-MM-DD` string. This date is
261
- itself the membership flag: set it to add the channel to MSN (it then reads as `is_msn` and
262
- appears in MSN-filtered queries), or send `null` to remove the channel from the network.
254
+ sending it returns a 400. It is append-only and managed through an internal-only
255
+ process that never modifies or removes existing entries — not available via this CLI.
263
256
 
264
257
  **Profile notes.** `tl profiles update` (superuser only) edits a brand/publisher profile's
265
258
  **`superuser_notes`** — the internal free-text notes field on the customer record
@@ -509,6 +502,21 @@ tl changelog since v0.4.10 # Notes from v0.4.10 to latest
509
502
  tl changelog --md > CHANGELOG.md # Capture for a doc
510
503
  ```
511
504
 
505
+ #### Distributed skills — beyond the bundled set
506
+
507
+ An organization can be granted additional skills that aren't part of a CLI release. `tl skill` fetches and installs them on demand into the same directories `tl setup` uses (Claude Code's standalone skills directory, OpenCode's skills directory, and the directory shared by Gemini and Codex), so every supported agent picks them up automatically — no restart or reconfiguration needed beyond re-reading the skill listing.
508
+
509
+ ```bash
510
+ tl skill list # Skills available to your org, with installed/latest versions (free)
511
+ tl skill list --all # Full catalog (full-access accounts only)
512
+ tl skill download <name> # Fetch and install into every AI-agent skill directory on this machine
513
+ tl skill download <name> --force # Overwrite a directory tl doesn't already manage
514
+ tl skill update # Refresh every downloaded skill to its latest version
515
+ tl skill remove <name> # Uninstall a downloaded skill
516
+ ```
517
+
518
+ If a user asks to find, add, or refresh a skill by capability ("is there a skill for X", "get me the latest Y skill"), check `tl skill list` before saying no — the bundled set isn't the whole catalog. `tl skill download` never overwrites a directory it doesn't already manage unless `--force` is passed, so it's safe to run without clobbering a manually-installed skill of the same name. `tl` warns once a day, on any command, when a downloaded skill has a newer version available — surface that nag to the user rather than silently ignoring it, and offer to run `tl skill update`.
519
+
512
520
  #### Channel & video discovery — pick the path for the question shape
513
521
 
514
522
  Four first-class paths, each with a different signal. **Pick by the SHAPE of the user's question, not by habit.** "Recommender first" is the right default only for path 2 — for paths 1, 3, and 4 the recommender is the wrong tool.
@@ -0,0 +1,245 @@
1
+ ---
2
+ name: tl-create-workflow
3
+ tl-blurb: design & stand up a sponsorship pipeline (workflow) from a goal
4
+ description: >
5
+ Turn a sponsorship / sourcing goal into a structured, data-populated
6
+ **Workflow** — an ordered funnel of report-stages that channels (or brands)
7
+ move through, e.g. Sourced → Qualified → Get face on screen → Reach out →
8
+ Contacted → Sold. Invoke when the user wants to build or set up a workflow or
9
+ pipeline, design an outreach / acquisition funnel for a brand or campaign,
10
+ turn an existing report into a workflow, or organise channel sourcing into
11
+ stages. Triggers: "create a workflow", "build a pipeline / funnel", "set up
12
+ outreach for <brand>", "turn this report into a workflow", "organise my
13
+ sourcing into stages", "workflow for finding channels to sponsor <brand>",
14
+ "build an acquisition funnel". It designs the funnel from the goal + the TL
15
+ methodology, sources the entry stage with real data (delegating to
16
+ tl-keyword-research / tl channels / tl recommender / guide-brand research),
17
+ defines each stage as a query-or-list report, and creates it with
18
+ "tl workflow create" (or, until the backend endpoint is deployed, hands back the
19
+ create-ready blueprint + in-app assembly steps). Also answers
20
+ HELP asks about how workflows work ("how do workflows work", "what's a
21
+ query vs a list stage", "explain workflow stages") for free.
22
+ ---
23
+
24
+ # tl-create-workflow — a sponsorship goal → a populated pipeline
25
+
26
+ Turn a fuzzy goal ("I need channels to sponsor **Magic Spoon**", "set up our
27
+ outreach funnel", "organise my sourcing") into a **Workflow**: an ordered chain
28
+ of report-**stages** that entities move through, with the **entry stage already
29
+ populated with the right channels** and every stage typed correctly. The value
30
+ is not drawing boxes — it's (a) designing the funnel from the **ThoughtLeaders
31
+ methodology** rather than a generic CRM template, (b) **sourcing the entry
32
+ stage with real data** so the pipeline starts full, not empty, and (c) getting
33
+ the **query-vs-list mechanics right** so the workflow doesn't break the way
34
+ hand-built ones do.
35
+
36
+ A workflow is **not a new kind of object** — every stage IS a saved Report
37
+ (campaign), chained in order. Understanding that is the whole game; read
38
+ `references/workflow-model.md` before you design anything.
39
+
40
+ > `<SKILL_DIR>` below is this skill's directory (the one holding `SKILL.md`).
41
+
42
+ ## Hard rules
43
+
44
+ - **A stage is a Report.** A workflow = ordered Reports linked by
45
+ `workflow_step_number`. There is no separate "stage" object. Design in terms
46
+ of reports.
47
+ - **The entry stage is a QUERY; every later stage is a LIST.** This is the
48
+ single most important rule and the #1 cause of broken hand-built workflows —
49
+ see `references/workflow-model.md` (query vs list) and
50
+ `references/pitfalls.md`. Never make stage 2+ a query.
51
+ - **Creating the workflow.** `tl workflow create --file <blueprint.json>` builds
52
+ it in one call — it POSTs `{name, report_type, steps}` to the Bearer endpoint
53
+ `/api/cli/v1/workflows/build` (the twin of the web builder). The command ships
54
+ in this repo; the endpoint ships with backend **PR #4192** — **until that is
55
+ deployed the command errors**, and the user stands the workflow up in the web
56
+ app from the blueprint (Convert → Add stage). Both paths are in
57
+ `references/creating-in-app.md`. **Never claim a workflow was created unless a
58
+ `tl workflow create` call actually returned one** — otherwise you prepared a
59
+ blueprint.
60
+ - **Source with real data, never placeholder channels.** The entry stage must
61
+ be filled from the index (delegate to `tl-keyword-research` /
62
+ `tl channels find` / `tl recommender` / guide-brand research), never a made-up
63
+ list. An empty pipeline is a failed deliverable.
64
+ - Do all data processing with the `utf-8` encoding explicitly in any script you
65
+ write.
66
+
67
+ ## Setup check
68
+
69
+ ```bash
70
+ tl whoami # confirms the CLI is authed and shows plan/limits
71
+ ```
72
+ If this errors, tell the user to run `tl auth login` (or set `TL_API_KEY`).
73
+ Sourcing stages needs intelligence access on the org's plan; if `tl whoami`
74
+ shows no intelligence flag, say so — you can still design the funnel, but you
75
+ can't populate it from the index.
76
+
77
+ ## When to invoke / skip
78
+
79
+ **Invoke** when the user wants to **stand up a pipeline**: build/set up a
80
+ workflow, design an outreach or acquisition funnel for a brand/campaign, turn a
81
+ report into a workflow, or organise sourcing into stages.
82
+
83
+ **Skip when:**
84
+ - They just want to *find* channels/brands/videos → `tl channels find`,
85
+ `tl-keyword-research`, `tl recommender`. (You'll *use* these, but a lookup
86
+ alone isn't a workflow.)
87
+ - They want to persist one flat list/report they already have →
88
+ `tl-save-report`.
89
+ - They're asking about an **existing** workflow's data (counts, who's in which
90
+ stage) → query it with `tl` directly.
91
+
92
+ ## Help mode — explain workflows on request, for free
93
+
94
+ When the user asks what a workflow is, how stages work, query vs list, or "how
95
+ do I build one" as a *question* (not a request to build): **run nothing**. Read
96
+ `references/workflow-model.md` (+ `references/pitfalls.md` for the gotchas) and
97
+ explain, sized to the ask. Close by offering to design one. Mid-run questions
98
+ get the same treatment — answer, then resume.
99
+
100
+ ## The build (you orchestrate; the tl CLI + sibling skills do the data work)
101
+
102
+ | Stage | What happens | Tooling |
103
+ |---|---|---|
104
+ | 0 Frame | goal, entity type, funnel shape, stage list — interview, don't assume | you + `references/methodology.md` |
105
+ | 1 Source the entry stage | fill stage 1 with the right channels/brands (a QUERY) | `tl-keyword-research` · `tl channels find` · `tl recommender` · guide-brand research |
106
+ | 2 Define the stages | each stage as a report config (query for stage 1, lists after) | you + `references/workflow-model.md` |
107
+ | 3 Blueprint | emit the create-ready plan: stage names, types, the entry filter, links | you |
108
+ | 4 Assemble | the exact in-app steps to build it (Convert → Add stage → link) | `references/creating-in-app.md` |
109
+ | 5 Deliver | blueprint + entry-stage report link + how to work the funnel | `tl-save-report` (for the entry report) |
110
+
111
+ **Narrate the run** — one line per stage transition, and surface every
112
+ assumption so the user can correct it. Sourcing spends credits (it's real ES
113
+ work); say roughly how much as you go, exactly like `tl-keyword-research`.
114
+
115
+ ### Stage 0 — Frame the funnel (interview; never assume)
116
+
117
+ Two things decide everything, and both are the user's:
118
+
119
+ 1. **The goal & entity.** What is this pipeline *for*, and does it move
120
+ **channels** (usual), **brands**, or **sponsorships**? "Find channels to
121
+ sponsor Magic Spoon" → a channel-acquisition funnel. "Work our brand
122
+ prospects" → a brand funnel. The entity type fixes the workflow's
123
+ `report_type` and can't be mixed later.
124
+ 2. **The funnel shape.** How many stages and what they mean. Don't invent a
125
+ generic "Lead → MQL → SQL" — derive it from the **ThoughtLeaders
126
+ methodology** (`references/methodology.md`): the real sourcing funnel is
127
+ *find the pool → qualify on value-vs-price → enrich (face-on-screen,
128
+ contacts) → outreach → pipeline (proposed/pending/sold)*. Offer the canonical
129
+ funnel below and let them cut/rename stages — the huddle's real stages are
130
+ good defaults:
131
+
132
+ > **Sourced** (query) → **Qualify** (list) → **Get face on screen** (list) →
133
+ > **Reach out** (list) → **Contacted** (list) → **Proposed** (list)
134
+
135
+ Stage names are just report titles — pick names the team will recognise
136
+ (they persist on the campaign). Fewer, meaningful stages beat many empty
137
+ ones.
138
+
139
+ State the goal, entity, and stage list back to the user before sourcing
140
+ anything.
141
+
142
+ ### Stage 1 — Source the entry stage (the QUERY)
143
+
144
+ Stage 1 is the **pool**: the channels the funnel starts from, selected by a
145
+ *filter* (a query), not an explicit list. Pick the sourcing path from the goal —
146
+ this is where the methodology becomes data:
147
+
148
+ - **Sponsor a specific brand / product** → find the brand's category and its
149
+ **guide brands** (proven sponsors / competitors), then their **winner
150
+ channels** (TRUE renewals — the real signal), then **look-alikes**. Use
151
+ `tl brands` research + `tl channels similar` / `tl recommender`.
152
+ - **A topic / niche** ("cooking channels for a kitchenware brand") →
153
+ **`tl-keyword-research`** to turn the topic into a validated content filter +
154
+ the channels it selects. Take its `report_link` / filter set as stage 1.
155
+ - **A known shortlist** → then stage 1 is really a *list*, and a workflow may be
156
+ overkill — say so; `tl-save-report` might be all they need.
157
+
158
+ Deliver stage 1 as a **query report** (a filter set), and — because it's the
159
+ entry — it's fine and correct for it to be a query. Populate it, eyeball the
160
+ count and a sample, and confirm the breadth with the user (too broad → narrow
161
+ the filter; too thin → widen), exactly like keyword-research's breadth check.
162
+
163
+ ### Stage 2 — Define the stages (query first, lists after)
164
+
165
+ For each downstream stage, the report is a **list**: it starts empty and fills
166
+ as the user bulk-selects entities in the previous stage and **Moves** them
167
+ forward. You don't pre-populate lists — you just create the (empty) list
168
+ reports with the right names and types.
169
+
170
+ - **Types must alternate correctly:** stage 1 = query; stages 2..N = lists.
171
+ Re-check `references/workflow-model.md` if unsure how the platform infers
172
+ query-vs-list from the FilterSet.
173
+ - **Columns per stage:** if a stage needs a specific column the team acts on
174
+ (e.g. *Face On Screen* on the "Get face on screen" stage, *Outreach email* on
175
+ "Reach out"), note it — columns are set per report.
176
+ - **Linked reports (include/exclude), sparingly:** a stage can pull in another
177
+ report's entities (e.g. exclude "no email / captcha"). Keep this to **≤1–2
178
+ layers of nesting** — deep chains are the documented performance + confusion
179
+ trap (`references/pitfalls.md`). Prefer a flat stage over a clever nest.
180
+
181
+ ### Stage 3 — Emit the blueprint
182
+
183
+ Hand the user a compact, create-ready plan:
184
+
185
+ ```
186
+ Workflow: "Magic Spoon — channel sourcing" (report_type: channels)
187
+ 1. Sourced [QUERY] ← <entry filter summary> · ~<N> channels · <report_link>
188
+ 2. Qualify [list] empty; move channels that pass value-vs-price
189
+ 3. Get face on screen [list] empty; column: Face On Screen
190
+ 4. Reach out [list] empty; column: Outreach email; exclude → "No email / captcha"
191
+ 5. Contacted [list] empty
192
+ ```
193
+
194
+ Include the **entry report link** (from keyword-research / `tl reports create`)
195
+ so stage 1 exists as a real, openable report before assembly.
196
+
197
+ ### Stage 4 — Assemble in the web app
198
+
199
+ Workflows are built in-app (no CLI endpoint). Walk the user through
200
+ `references/creating-in-app.md`:
201
+
202
+ 1. Open the **entry (Sourced) report** → **Convert to workflow** → name it. That
203
+ report becomes **stage 1**.
204
+ 2. **Add stage** for each downstream stage, in order, with the names from the
205
+ blueprint (they save on the campaign — renames persist).
206
+ 3. For any stage with a **linked report** (e.g. exclude "no email"), add it on
207
+ that stage — keep nesting shallow.
208
+ 4. Set per-stage **columns** where noted.
209
+ 5. Work the funnel: on a stage, **filter** (e.g. by *Face On Screen*),
210
+ **bulk-select**, **Move** to the next stage. Moving is non-destructive.
211
+
212
+ ### Stage 5 — Deliver
213
+
214
+ Show: the funnel (stages, types, per-stage columns/links), the **entry-stage
215
+ report link** (populated), and the one-paragraph "how to work this funnel"
216
+ (filter → select → move). Offer to **save the entry report** via
217
+ `tl-save-report` if it isn't already saved. Never save or create anything the
218
+ user didn't ask for.
219
+
220
+ ## Cost
221
+
222
+ Framing and blueprinting are free (no queries). **Sourcing stage 1 spends
223
+ credits** — that's real ES work delegated to `tl-keyword-research` /
224
+ `tl channels` / `tl recommender`; their own costs apply (keyword-research: ~10–20
225
+ credits quick, ~60–120 deep). Run `tl describe show db` for live rates. The
226
+ in-app assembly costs nothing (it's the user clicking in the platform).
227
+
228
+ ## Self-check before you finish
229
+
230
+ 1. You established the **goal**, the **entity type** (channels / brands /
231
+ sponsorships → the workflow's `report_type`), and the **stage list** with the
232
+ user — derived from the methodology, not a generic CRM funnel — and stated
233
+ them back before sourcing.
234
+ 2. **Stage 1 is a QUERY and it's populated** from real index data (not a
235
+ placeholder list), with its breadth confirmed against the goal (narrowed /
236
+ widened as needed). **Every stage after 1 is a LIST.** No stage 2+ is a query.
237
+ 3. Any **linked reports** are ≤1–2 nesting layers; you preferred a flat stage
238
+ over a deep nest, and flagged per-stage **columns** the team acts on.
239
+ 4. The blueprint is **create-ready**: named stages, correct types, the entry
240
+ filter summary + a working **report link**, and the in-app assembly steps.
241
+ 5. You were explicit that the **Workflow is assembled in the web app** (the CLI
242
+ can't create it) — you prepared it, you didn't claim to have created it.
243
+ 6. You narrated the run and its credit spend, and saved / created **nothing**
244
+ without the user's say-so.
245
+ 7. If the user requests a diagram of the funnel, create it as an SVG graphic.
@@ -0,0 +1,95 @@
1
+ # Creating the workflow
2
+
3
+ **`tl workflow create` builds the workflow from a blueprint in one call** — it
4
+ POSTs `{name, report_type, steps}` to the Bearer endpoint
5
+ `/api/cli/v1/workflows/build` (the twin of the web "New Workflow" builder). The
6
+ result is the same `Workflow` / stage-`Campaign` / `FilterSet` objects the rest of
7
+ the platform uses, so it shows up in the web app's workflow list/detail
8
+ immediately, where the team moves / edits / duplicates it.
9
+
10
+ > **Availability.** The `tl workflow` command ships in this repo; the endpoint it
11
+ > calls ships with backend **thoughtleaders PR #4192** (`create_full_workflow`).
12
+ > Until that backend change is deployed, `tl workflow create` returns an error —
13
+ > use the **manual in-app assembly** at the bottom of this file. The blueprint is
14
+ > the exact same input either way, so nothing is wasted.
15
+
16
+ ## Create it directly (preferred)
17
+
18
+ 1. **Build + save the entry (Sourced) report first**, so it exists with an **id**.
19
+ It's a *query* report populated by this skill (`tl-keyword-research`,
20
+ `tl channels`, `tl recommender`, or `tl reports create`) and saved
21
+ (`tl-save-report` / `tl reports create`). This is the only stage that starts
22
+ with data, and it must be a saved **query** so the stage stays live.
23
+ 2. **Write the blueprint to a file** and run `tl workflow create`:
24
+
25
+ ```bash
26
+ tl workflow create --file blueprint.json # add --yes to skip the confirm
27
+ ```
28
+
29
+ `blueprint.json`:
30
+
31
+ ```json
32
+ {
33
+ "name": "Q3 Creator Outreach",
34
+ "report_type": 3,
35
+ "steps": [
36
+ { "title": "Sourced", "include_report_ids": [<entryReportId>], "exclude_report_ids": [] },
37
+ { "title": "Qualify", "include_report_ids": [], "exclude_report_ids": [] },
38
+ { "title": "Get face on screen", "include_report_ids": [], "exclude_report_ids": [] },
39
+ { "title": "Reach out", "include_report_ids": [], "exclude_report_ids": [] }
40
+ ]
41
+ }
42
+ ```
43
+
44
+ - `report_type`: **1** content · **2** brands · **3** channels · **8** sponsorships.
45
+ - Stages are created **in order**; the first is the entry stage (link the saved
46
+ query report via `include_report_ids`), the rest are empty **lists** channels
47
+ move into. Keep any linked-report nesting shallow (≤1–2).
48
+ - Only reports you may edit are linked (others are dropped); the workflow is
49
+ owned by you. One atomic call creates the workflow + stage campaigns +
50
+ include/exclude report links + the exclude-earlier-stages chaining.
51
+ - Use `--config '<json>'` for inline JSON, or `--name` / `--report-type` to
52
+ supply/override those fields. `--json` / `--toon` for machine output.
53
+ 3. The command prints the new workflow **id** and an **"Open in app"** link
54
+ (`/#/workflows/<report_type>/<id>`). Hand that to the user to work the funnel:
55
+ on a stage, filter → bulk-select → **Move** to the next stage (Move / Remove
56
+ are non-destructive; moved channels leave the source stage).
57
+
58
+ ## The endpoints (reference)
59
+
60
+ | Action | Request | Auth |
61
+ |--------|---------|------|
62
+ | **Build a full workflow** (`tl workflow create`) | `POST /api/cli/v1/workflows/build` · `{ name, report_type, steps[] }` | **Bearer (CLI)** |
63
+ | Convert one report → 1-stage workflow | `POST /api/workflows` · `{ campaignId, workflowName }` | session |
64
+ | Add a stage | `POST /api/workflows/add-step` · `{ campaignTitle, workflowId }` | session |
65
+ | Delete a stage (any same-org collaborator) | `DELETE /api/workflows/delete-step?stepId=` | session |
66
+ | Rename / delete the workflow (delete is owner-only) | `PATCH` / `DELETE /api/workflows/:id` | session |
67
+ | Fetch a workflow + stages | `GET /api/workflows/:id` | session |
68
+ | Link a report / move entities on a stage | `PATCH` the stage filterset's `add_relation` action | session |
69
+
70
+ Only the **build** endpoint is on the CLI's Bearer surface; the rest are the web
71
+ app's session-authenticated management routes (used from the web UI).
72
+
73
+ ## Assemble it in the web app (fallback until the endpoint is live)
74
+
75
+ If the build endpoint isn't deployed yet, the user stands the workflow up from
76
+ the blueprint by hand — same design, more clicks:
77
+
78
+ 1. Open the saved entry report → **Convert to workflow** → name it. It becomes
79
+ **stage 1**.
80
+ 2. **Add stage** for each downstream stage, in blueprint order (each is an empty
81
+ **list**; names persist across reloads).
82
+ 3. **Link** supporting include/exclude reports where the blueprint calls for it
83
+ (nesting ≤1–2 layers).
84
+ 4. Set per-stage **columns** the team acts on (Face On Screen, Outreach email).
85
+ 5. **Work the funnel:** on a stage, filter → select → **Move** to the next stage.
86
+
87
+ ## What to hand the user
88
+
89
+ - The **entry report link** (populated, openable).
90
+ - Either the **"Open in app" workflow link** (from `tl workflow create`) or the
91
+ **blueprint + in-app assembly steps** (fallback).
92
+ - The one-line "how to work the funnel": *filter a stage → select → Move to next.*
93
+
94
+ Never claim a workflow was created unless a `tl workflow create` call actually
95
+ returned one — otherwise you prepared a blueprint.
@@ -0,0 +1,70 @@
1
+ # Funnel design — the ThoughtLeaders methodology, not a generic CRM
2
+
3
+ Don't design a workflow like a generic sales CRM ("Lead → MQL → SQL → Won").
4
+ Design it from how ThoughtLeaders actually sources and closes sponsorships. The
5
+ funnel stages fall out of the methodology.
6
+
7
+ ## The sourcing methodology (how the pool is found)
8
+
9
+ The core play for finding the right channels for a brand:
10
+
11
+ 1. **Identify the brands** — through competitor research and **Guide Brands**
12
+ (brands that are *proven* sponsors in the category). A guide brand is a
13
+ working example of "who already pays to sponsor this kind of content".
14
+ 2. **Find the winner channels** — the channels those guide brands *renewed*
15
+ with. A **TRUE renewal** (the brand came back and paid again) is the real
16
+ signal that the channel converts, not a vanity metric.
17
+ 3. **Find look-alike channels** — channels similar to the winners (same
18
+ audience/niche/shape) that haven't been booked yet. This is the addressable
19
+ pool.
20
+ 4. **Analyse value vs price** — for each candidate, is the projected value
21
+ (views, audience fit) worth the price? This is the qualification gate.
22
+
23
+ The entry stage of a workflow is the output of steps 1–3 (the look-alike pool),
24
+ expressed as a **query**. Qualification (step 4) is the first downstream list
25
+ stage.
26
+
27
+ ## Terminology to keep straight (TL uses its own words)
28
+
29
+ - **Brands** = the sponsors (usually companies, sometimes a single product).
30
+ - **Channels** = the creators being sponsored (YouTube channels, sometimes
31
+ podcasts).
32
+ - **Sponsorship** is the umbrella; it narrows through a funnel of its own:
33
+ *Sponsorships ⊃ Matches ⊃ Proposals ⊃ Deals (sold)*.
34
+ - Pricing words: **PV** = Projected Views (a pricing estimate); **VG** = View
35
+ Guarantee (the contractual floor). Don't invent CPM/"margin" language — TL
36
+ says **Net revenue** / **TL profit**, and avoid "flight" / "hero channel".
37
+ - **Send date** = the expected publication date of a sponsored video.
38
+ - Two channel networks exist: **MSN** (the large ~11K opted-in Media Selling
39
+ Network) and **TPP** (the small ~169 directly-managed VIP channels). Sourcing
40
+ usually works over MSN; enrichment tasks ("get face on screen") are often
41
+ assigned to MSN managers.
42
+
43
+ ## The canonical acquisition funnel
44
+
45
+ A good default channel-acquisition workflow, with each stage's type and the
46
+ column the team acts on. Offer this, then let the user cut / rename / reorder.
47
+
48
+ | # | Stage | Type | What it means | Acted-on column |
49
+ |---|-------|------|---------------|-----------------|
50
+ | 1 | **Sourced** | query | The pool: look-alike channels for the brand's guide-brand winners (or a topic filter). | — |
51
+ | 2 | **Qualify** | list | Passed value-vs-price (and, if relevant, an authenticity check). | Projected views / price |
52
+ | 3 | **Get face on screen** | list | Assigned to MSN managers to fill in face-on-screen + enrich. | Face On Screen |
53
+ | 4 | **Reach out** | list | Has an outreach email; ready to contact. | Outreach email |
54
+ | 5 | **Contacted** | list | Outreach sent. | — |
55
+ | 6 | **Proposed** | list | A proposal is out. | — |
56
+
57
+ Notes:
58
+ - Stages 5–6 mirror the sponsorship sub-funnel (Match → Proposal → Deal); stop
59
+ wherever the team's process stops. Don't add stages the team won't work.
60
+ - For a **brand-prospecting** workflow (report_type = brands) the shape rebases:
61
+ *Prospects (query: brands in a category / competitors of a guide brand) →
62
+ Qualified → Pitching → Won*.
63
+
64
+ ## Sizing the pool (breadth)
65
+
66
+ There is no universal right number. A niche brand with a few dozen good
67
+ look-alikes is a complete answer; a broad consumer brand returning a few dozen
68
+ is probably too narrow. State your breadth judgement and offer to narrow/widen —
69
+ exactly the calibration `tl-keyword-research` does. The pool is the query stage;
70
+ everything downstream is a subset of it.
@@ -0,0 +1,68 @@
1
+ # Pitfalls — the failure modes hand-built workflows hit
2
+
3
+ These come from real workflows the team built (and abandoned). Design around
4
+ them from the start; most of a good workflow is just not stepping on these.
5
+
6
+ ## 1. A query where a list belongs (the big one)
7
+
8
+ Making stage 2+ a **query** instead of a **list** is the #1 breakage. A query
9
+ recomputes its members from the index every time — it has nothing to *hold* the
10
+ entities you move into it, so "moved" channels vanish on the next load. **Only
11
+ the entry stage is a query; everything after it is a list.** If the user wants
12
+ to "filter" a mid-funnel stage, that's a transient **view filter** on the
13
+ list's rows (narrow what's shown so you can bulk-select), not a query stage.
14
+
15
+ ## 2. Empty pipeline
16
+
17
+ Delivering a funnel whose entry stage is empty (or filled with placeholder
18
+ channels) is a non-deliverable. The entry stage must be **sourced from real
19
+ index data** — `tl-keyword-research`, `tl channels`, `tl recommender`,
20
+ guide-brand look-alikes — with its breadth sense-checked against the goal.
21
+
22
+ ## 3. Over-nesting linked reports
23
+
24
+ A stage can include/exclude other reports, which can themselves include others.
25
+ There's **no depth limit in the platform**, and deep/wide nests resolve
26
+ exponentially (slow) and become impossible to reason about ("this report
27
+ excludes that one, which includes this other one…"). **Cap it at 1–2 layers.**
28
+ Prefer a flat stage with its own filters over a clever nest. Historically people
29
+ added manual exclude-hacks to make a nested workflow usable — that's a smell,
30
+ not a pattern to copy.
31
+
32
+ ## 4. Meaningless / churning stage names
33
+
34
+ Stages are reports; their names are the report titles and they **persist** on
35
+ the campaign (a real historical bug reverted them, now fixed). Pick names the
36
+ team already uses for its process ("Get face on screen", "Reach out",
37
+ "Contacted") — not generic CRM labels. Fewer meaningful stages beat many empty
38
+ ones nobody works.
39
+
40
+ ## 5. Designing for one person on a shared object
41
+
42
+ A workflow is **shared** across the team by default. Don't design it around one
43
+ AM's private view; design the stages so several people can work them. (The
44
+ platform's own per-user view feature is what lets each person hide the reports
45
+ that aren't theirs — not something you configure here, but keep the shared
46
+ reality in mind when naming/structuring stages.)
47
+
48
+ ## 6. Claiming you created it
49
+
50
+ The CLI can't create the Workflow object — you **design, source, and
51
+ blueprint**, and the user assembles it in the app (Convert → Add stage → …).
52
+ Never say "I created your workflow." Say "here's your populated blueprint and
53
+ the steps to stand it up."
54
+
55
+ ## 7. Too many stages
56
+
57
+ A funnel with eight stages the team won't actually move channels through is
58
+ worse than four they will. Map stages to the team's *real* process steps and
59
+ stop where the process stops. Every stage is a report someone has to maintain.
60
+
61
+ ## Quick checklist
62
+
63
+ - [ ] Entry stage is a **query**, populated from real data, breadth confirmed.
64
+ - [ ] Every later stage is a **list**.
65
+ - [ ] Nesting ≤ 1–2 layers; flat preferred.
66
+ - [ ] Stage names are the team's real process words.
67
+ - [ ] Per-stage columns the team acts on are noted.
68
+ - [ ] You blueprinted + handed off assembly — you didn't claim to have created it.