thoughtleaders-cli 0.9.9__tar.gz → 0.9.11__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 (163) hide show
  1. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/.claude-plugin/plugin.json +1 -1
  2. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/PKG-INFO +1 -1
  3. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/pyproject.toml +1 -1
  4. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/skills/tl/references/elasticsearch-schema.md +2 -1
  5. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/src/tl_cli/__init__.py +1 -1
  6. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/src/tl_cli/commands/setup.py +93 -18
  7. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/src/tl_cli/self_update.py +5 -1
  8. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/tests/test_setup.py +135 -0
  9. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/.claude-plugin/marketplace.json +0 -0
  10. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/.github/dependabot.yml +0 -0
  11. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/.github/workflows/ci.yml +0 -0
  12. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/.github/workflows/cli-integration.yml +0 -0
  13. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/.github/workflows/python-publish.yml +0 -0
  14. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/.gitignore +0 -0
  15. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/AGENTS.md +0 -0
  16. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/API.md +0 -0
  17. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/CLAUDE.md +0 -0
  18. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/LICENSE +0 -0
  19. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/README.md +0 -0
  20. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/agents/keyword-context-classifier.md +0 -0
  21. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/agents/keyword-entity-resolver.md +0 -0
  22. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/agents/keyword-relevance-validator.md +0 -0
  23. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/agents/tl-analyst.md +0 -0
  24. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/agents/youtube-comment-classifier.md +0 -0
  25. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/hooks/hooks.json +0 -0
  26. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/hooks/scripts/load-tl-skill.mjs +0 -0
  27. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/hooks/scripts/post-usage.sh +0 -0
  28. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/hooks/scripts/pre-check.sh +0 -0
  29. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/skills/tl/SKILL.md +0 -0
  30. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/skills/tl/references/business-glossary.md +0 -0
  31. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/skills/tl/references/firebolt-schema.md +0 -0
  32. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/skills/tl/references/postgres-schema.md +0 -0
  33. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/skills/tl-channel-authenticity/.gitignore +0 -0
  34. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/skills/tl-channel-authenticity/SKILL.md +0 -0
  35. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/skills/tl-channel-authenticity/references/comment-patterns.md +0 -0
  36. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/skills/tl-channel-authenticity/references/peer-cohort.md +0 -0
  37. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/skills/tl-channel-authenticity/references/red-flags.md +0 -0
  38. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/skills/tl-channel-authenticity/references/scoring.md +0 -0
  39. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/skills/tl-channel-authenticity/scripts/_io_utf8.py +0 -0
  40. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/skills/tl-channel-authenticity/scripts/analyze_channel.py +0 -0
  41. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/skills/tl-channel-authenticity/scripts/anomaly_detector.py +0 -0
  42. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/skills/tl-channel-authenticity/scripts/comment_analyzer.py +0 -0
  43. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/skills/tl-channel-authenticity/scripts/comment_scraper.py +0 -0
  44. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/skills/tl-channel-authenticity/scripts/engagement_ratios.py +0 -0
  45. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/skills/tl-channel-authenticity/scripts/peer_cohort.py +0 -0
  46. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/skills/tl-channel-authenticity/scripts/report.py +0 -0
  47. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/skills/tl-channel-authenticity/scripts/resolve_channel.py +0 -0
  48. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/skills/tl-channel-authenticity/scripts/score.py +0 -0
  49. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/skills/tl-channel-authenticity/scripts/tl_cli.py +0 -0
  50. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/skills/tl-channel-authenticity/scripts/video_integrity.py +0 -0
  51. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/skills/tl-channel-authenticity/scripts/view_curves.py +0 -0
  52. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/skills/tl-create-workflow/SKILL.md +0 -0
  53. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/skills/tl-create-workflow/references/creating-in-app.md +0 -0
  54. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/skills/tl-create-workflow/references/methodology.md +0 -0
  55. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/skills/tl-create-workflow/references/pitfalls.md +0 -0
  56. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/skills/tl-create-workflow/references/workflow-model.md +0 -0
  57. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/skills/tl-keyword-research/SKILL.md +0 -0
  58. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/skills/tl-keyword-research/references/elasticsearch-content-search.md +0 -0
  59. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/skills/tl-keyword-research/references/help.md +0 -0
  60. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/skills/tl-keyword-research/scripts/build_report.py +0 -0
  61. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/skills/tl-keyword-research/scripts/expand_entities.py +0 -0
  62. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/skills/tl-keyword-research/scripts/fetch_context.py +0 -0
  63. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/skills/tl-keyword-research/scripts/probe.py +0 -0
  64. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/skills/tl-keyword-research/scripts/search_channels.py +0 -0
  65. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/skills/tl-keyword-research/scripts/search_videos.py +0 -0
  66. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/skills/tl-keyword-research/scripts/select_keywords.py +0 -0
  67. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/skills/tl-save-report/SKILL.md +0 -0
  68. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/skills/tl-save-report/references/columns_brands.md +0 -0
  69. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/skills/tl-save-report/references/columns_channels.md +0 -0
  70. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/skills/tl-save-report/references/columns_content.md +0 -0
  71. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/skills/tl-save-report/references/columns_sponsorships.md +0 -0
  72. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/skills/tl-save-report/references/intelligence_filterset_schema.json +0 -0
  73. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/skills/tl-save-report/references/intelligence_widget_schema.json +0 -0
  74. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/skills/tl-save-report/references/report_glossary.md +0 -0
  75. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/skills/tl-save-report/references/sortable_columns.json +0 -0
  76. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/skills/tl-save-report/references/sponsorship_filterset_schema.json +0 -0
  77. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/skills/tl-save-report/references/sponsorship_widget_schema.json +0 -0
  78. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/skills/tl-save-report/references/widgets.md +0 -0
  79. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/skills/tl-top-partnerships/SKILL.md +0 -0
  80. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/skills/tl-top-partnerships/scripts/top_partnerships.py +0 -0
  81. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/skills/tl-views-guarantee/SKILL.md +0 -0
  82. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/skills/tl-views-guarantee/scripts/vg.py +0 -0
  83. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/src/tl_cli/__main__.py +0 -0
  84. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/src/tl_cli/_completions.py +0 -0
  85. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/src/tl_cli/_typer_utils.py +0 -0
  86. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/src/tl_cli/auth/__init__.py +0 -0
  87. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/src/tl_cli/auth/commands.py +0 -0
  88. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/src/tl_cli/auth/login.py +0 -0
  89. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/src/tl_cli/auth/pkce.py +0 -0
  90. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/src/tl_cli/auth/token_store.py +0 -0
  91. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/src/tl_cli/client/__init__.py +0 -0
  92. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/src/tl_cli/client/errors.py +0 -0
  93. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/src/tl_cli/client/http.py +0 -0
  94. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/src/tl_cli/commands/__init__.py +0 -0
  95. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/src/tl_cli/commands/_comments_common.py +0 -0
  96. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/src/tl_cli/commands/balance.py +0 -0
  97. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/src/tl_cli/commands/brands.py +0 -0
  98. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/src/tl_cli/commands/bulk_import.py +0 -0
  99. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/src/tl_cli/commands/changelog.py +0 -0
  100. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/src/tl_cli/commands/channels.py +0 -0
  101. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/src/tl_cli/commands/credits.py +0 -0
  102. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/src/tl_cli/commands/db.py +0 -0
  103. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/src/tl_cli/commands/deals.py +0 -0
  104. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/src/tl_cli/commands/describe.py +0 -0
  105. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/src/tl_cli/commands/doctor.py +0 -0
  106. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/src/tl_cli/commands/matches.py +0 -0
  107. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/src/tl_cli/commands/memory.py +0 -0
  108. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/src/tl_cli/commands/profiles.py +0 -0
  109. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/src/tl_cli/commands/proposals.py +0 -0
  110. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/src/tl_cli/commands/recommender.py +0 -0
  111. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/src/tl_cli/commands/reports.py +0 -0
  112. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/src/tl_cli/commands/schema.py +0 -0
  113. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/src/tl_cli/commands/skills.py +0 -0
  114. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/src/tl_cli/commands/snapshots.py +0 -0
  115. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/src/tl_cli/commands/sponsorships.py +0 -0
  116. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/src/tl_cli/commands/uploads.py +0 -0
  117. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/src/tl_cli/commands/whoami.py +0 -0
  118. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/src/tl_cli/commands/workflows.py +0 -0
  119. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/src/tl_cli/config.py +0 -0
  120. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/src/tl_cli/filters.py +0 -0
  121. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/src/tl_cli/hints.py +0 -0
  122. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/src/tl_cli/main.py +0 -0
  123. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/src/tl_cli/output/__init__.py +0 -0
  124. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/src/tl_cli/output/formatter.py +0 -0
  125. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/src/tl_cli/query_history.py +0 -0
  126. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/src/tl_cli/skill_registry.py +0 -0
  127. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/tests/__init__.py +0 -0
  128. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/tests/test_auth.py +0 -0
  129. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/tests/test_brands_winner_channels.py +0 -0
  130. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/tests/test_build_report.py +0 -0
  131. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/tests/test_channels_lookalike.py +0 -0
  132. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/tests/test_db_repeat_warning.py +0 -0
  133. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/tests/test_describe.py +0 -0
  134. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/tests/test_error_hints.py +0 -0
  135. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/tests/test_expand_entities.py +0 -0
  136. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/tests/test_filters.py +0 -0
  137. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/tests/test_http_auth.py +0 -0
  138. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/tests/test_memory.py +0 -0
  139. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/tests/test_output.py +0 -0
  140. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/tests/test_probe.py +0 -0
  141. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/tests/test_profiles.py +0 -0
  142. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/tests/test_quota_refusal_message.py +0 -0
  143. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/tests/test_refusal_rendering.py +0 -0
  144. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/tests/test_reports.py +0 -0
  145. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/tests/test_scripts_executable.py +0 -0
  146. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/tests/test_search_channels.py +0 -0
  147. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/tests/test_search_videos.py +0 -0
  148. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/tests/test_select_keywords.py +0 -0
  149. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/tests/test_setup_marker_respect.py +0 -0
  150. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/tests/test_skill_registry.py +0 -0
  151. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/tests/test_skills_command.py +0 -0
  152. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/tests/test_sponsorships.py +0 -0
  153. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/tests/test_whoami_newsletter.py +0 -0
  154. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/tests/test_workflows.py +0 -0
  155. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/tests_cli/AGENTS.md +0 -0
  156. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/tests_cli/conftest.py +0 -0
  157. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/tests_cli/test_balance.py +0 -0
  158. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/tests_cli/test_db_es.py +0 -0
  159. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/tests_cli/test_db_fb.py +0 -0
  160. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/tests_cli/test_db_pg.py +0 -0
  161. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/tests_cli/test_schema.py +0 -0
  162. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/tests_cli/test_whoami.py +0 -0
  163. {thoughtleaders_cli-0.9.9 → thoughtleaders_cli-0.9.11}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tl-cli",
3
- "version": "0.9.9",
3
+ "version": "0.9.11",
4
4
  "description": "ThoughtLeaders CLI — query sponsorship deals, channels, brands, uploads, and intelligence from the terminal",
5
5
  "author": {
6
6
  "name": "ThoughtLeaders",
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: thoughtleaders-cli
3
- Version: 0.9.9
3
+ Version: 0.9.11
4
4
  Summary: ThoughtLeaders CLI — query sponsorship data, channels, brands, and intelligence
5
5
  Project-URL: Homepage, https://thoughtleaders.io
6
6
  Project-URL: Repository, https://github.com/ThoughtLeaders-io/thoughtleaders-cli
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "thoughtleaders-cli"
7
- version = "0.9.9"
7
+ version = "0.9.11"
8
8
  description = "ThoughtLeaders CLI — query sponsorship data, channels, brands, and intelligence"
9
9
  readme = "README.md"
10
10
  license = "MIT"
@@ -108,7 +108,8 @@ Contains a denormalized subset of the PostgreSQL channel data.
108
108
  | `media_selling_network_join_date` | date | MSN join date; non-null = MSN member (~1%) |
109
109
  | `has_outreach_email` | boolean | Has contact email (100%) |
110
110
  | `outreach_email` | text | Contact email (~45%) |
111
- | `social_links` | text | Flat array of the channel's profile URLs (~47%), e.g. `["https://twitter.com/…", "https://instagram.com/…", "https://discord.gg/…"]`. Source is a per-platform map plus a catch-all `_other` map for unrecognized platforms; in ES all of it is flattened into this one URL array (the `_other` URLs are folded in, the platform names are dropped). Occasional stray entries (bare emails, nested arrays) exist. |
111
+ | `social_links` | text | Flat array of the channel's known profile links as **canonical keys** — lowercase `host/path` with no scheme, no `www.`, no query string, and `x.com` spelled as `twitter.com`, e.g. `["twitter.com/mrbeast", "instagram.com/mrbeast", "linktr.ee/foo"]` (~47%, growing). Sourced from the `all_social_links` accumulator (About-page scrapes plus web-search discoveries); platform names are dropped, e-mails never appear. |
112
+ | `social_links.canonical` | text | Subfield of the above with a path-segment analyzer: one token per URL segment, subdomain-insensitive host. `{"match_phrase": {"social_links.canonical": "twitch.tv/ludwig"}}` is the precise way to ask "which channels carry exactly this profile link" — it matches `m.twitch.tv/ludwig` and deep paths under the profile, but never a channel that merely mentions the same words in different links. Prefer it over matching the parent field when the query is a `domain/handle` pair. |
112
113
  | `male_share` | byte | Male audience % — only ~1.6% of channel docs have demographic data |
113
114
  | `usa_share` | byte | US audience % — same ~1.6% coverage |
114
115
  | `device` | object | Audience device demographics where known: `device.primary` (most common device) and `device.share` (per-device % map). Very sparse (~0.2%). |
@@ -1,3 +1,3 @@
1
1
  """ThoughtLeaders CLI — query sponsorship data, channels, brands, and intelligence."""
2
2
 
3
- __version__ = "0.9.9"
3
+ __version__ = "0.9.11"
@@ -160,14 +160,69 @@ def _run_claude(args: list[str], claude_bin: str) -> tuple[bool, str]:
160
160
  return False, str(e)
161
161
 
162
162
 
163
+ def _installed_plugin_version() -> str | None:
164
+ """Version of the plugin Claude Code actually has on disk, or None.
165
+
166
+ Reads Claude Code's own install record rather than trusting the stamp
167
+ written at setup time: `plugin install` is a no-op once the plugin is
168
+ present, so a stamp saying "whatever the CLI version was when setup last
169
+ ran" can name a version that was never installed. Returns None when the
170
+ record is missing or unreadable — callers treat that as "unknown", never
171
+ as "outdated", so a change to Claude Code's bookkeeping cannot turn into
172
+ a false warning for every user at once.
173
+ """
174
+ try:
175
+ record = json.loads((CLAUDE_PLUGINS_DIR / "installed_plugins.json").read_text(encoding="utf-8"))
176
+ entries = record["plugins"][PLUGIN_KEY]
177
+ except (OSError, ValueError, KeyError, TypeError):
178
+ return None
179
+ if not isinstance(entries, list):
180
+ return None
181
+ # One entry per install scope; setup installs at user scope, so prefer it.
182
+ scoped = sorted((e for e in entries if isinstance(e, dict)), key=lambda e: e.get("scope") != "user")
183
+ for entry in scoped:
184
+ version = entry.get("version")
185
+ if isinstance(version, str) and version and version != "unknown":
186
+ return version
187
+ return None
188
+
189
+
163
190
  def _get_installed_plugin_version() -> str | None:
164
- """Try to read the installed plugin version from the cache."""
191
+ """Installed plugin version: Claude Code's record, else the setup stamp.
192
+
193
+ The stamp is the fallback for installs that never reached the plugin
194
+ registry (the standalone-skills path), where the copied skills really are
195
+ the ones this CLI shipped with.
196
+ """
197
+ version = _installed_plugin_version()
198
+ if version:
199
+ return version
165
200
  version_file = CLAUDE_PLUGINS_DIR / "tl-cli" / ".version"
166
201
  if version_file.exists():
167
202
  return version_file.read_text().strip()
168
203
  return None
169
204
 
170
205
 
206
+ def _write_version_stamp(version: str) -> None:
207
+ """Record which plugin version is installed, for `check_plugin_version()`."""
208
+ version_dir = CLAUDE_PLUGINS_DIR / "tl-cli"
209
+ version_dir.mkdir(parents=True, exist_ok=True)
210
+ (version_dir / ".version").write_text(version)
211
+
212
+
213
+ def _update_plugin(claude_bin: str) -> tuple[bool, bool, str]:
214
+ """Pull the installed plugin up to the marketplace's current version.
215
+
216
+ `plugin install` exits successfully without doing anything when the
217
+ plugin is already installed, so this is what actually advances an
218
+ existing install. Returns (ok, changed, output); `changed` is False when
219
+ the plugin was already at the latest version.
220
+ """
221
+ ok, output = _run_claude(["plugin", "update", PLUGIN_KEY], claude_bin)
222
+ changed = ok and "already at the latest" not in output.lower()
223
+ return ok, changed, output
224
+
225
+
171
226
  def check_plugin_version() -> list[str]:
172
227
  """Check if installed plugin versions match CLI version.
173
228
 
@@ -176,11 +231,9 @@ def check_plugin_version() -> list[str]:
176
231
  warnings = []
177
232
 
178
233
  # Claude Code
179
- claude_version_file = CLAUDE_PLUGINS_DIR / "tl-cli" / ".version"
180
- if claude_version_file.exists():
181
- installed = claude_version_file.read_text().strip()
182
- if installed != __version__:
183
- warnings.append(f"Claude Code plugin is outdated (v{installed} vs CLI v{__version__}). Run 'tl setup claude' to update.")
234
+ installed = _get_installed_plugin_version()
235
+ if installed and installed != __version__:
236
+ warnings.append(f"Claude Code plugin is outdated (v{installed} vs CLI v{__version__}). Run 'tl setup claude' to update.")
184
237
 
185
238
  # OpenCode
186
239
  opencode_version_file = OPENCODE_SKILLS_DIR / ".tl-version"
@@ -368,9 +421,9 @@ def setup_claude(
368
421
  ) -> None:
369
422
  """Install the TL CLI plugin for Claude Code.
370
423
 
371
- Registers the ThoughtLeaders marketplace, installs the tl-cli plugin,
372
- and adds a /tl shim command so the plugin's tl skill can be invoked
373
- without the plugin namespace. Standalone skill copies in ~/.claude/skills
424
+ Registers the ThoughtLeaders marketplace, installs and updates the
425
+ tl-cli plugin, and adds a /tl shim command so the plugin's tl skill can
426
+ be invoked without the plugin namespace. Standalone skill copies in ~/.claude/skills
374
427
  are only installed as a fallback when the plugin can't be installed;
375
428
  unmodified copies left by earlier versions are removed.
376
429
 
@@ -445,7 +498,23 @@ def setup_claude(
445
498
  _print_manual_instructions()
446
499
  raise SystemExit(1)
447
500
 
448
- # Step 3: /tl shim command + cleanup of standalone copies from older versions
501
+ # Step 3: Update the plugin. An install that found the plugin already
502
+ # present did nothing, so without this an existing install stays pinned
503
+ # at whatever version it was first installed at.
504
+ console.print("[bold]Updating plugin...[/bold]")
505
+ ok, changed, output = _update_plugin(claude_bin)
506
+ installed_version = _installed_plugin_version()
507
+ version_label = f"v{installed_version}" if installed_version else "version unknown"
508
+ if not ok:
509
+ console.print(f" [yellow]![/yellow] Plugin update failed: {output}")
510
+ console.print(f" Continuing with the installed plugin ({version_label}).")
511
+ elif changed:
512
+ console.print(f" [green]✓[/green] Plugin updated to {version_label}")
513
+ console.print(" [yellow]Restart Claude Code to apply the new version.[/yellow]")
514
+ else:
515
+ console.print(f" [green]✓[/green] Plugin already up to date ({version_label})")
516
+
517
+ # Step 4: /tl shim command + cleanup of standalone copies from older versions
449
518
  console.print("[bold]Installing /tl shortcut...[/bold]")
450
519
  shim = _install_command_shim()
451
520
  console.print(f" [green]✓[/green] /tl command installed: {shim}")
@@ -456,10 +525,7 @@ def setup_claude(
456
525
  console.print(f" [yellow]![/yellow] Kept {kept} modified standalone skill(s) in {CLAUDE_SKILLS_DIR}")
457
526
  console.print(" These differ from the plugin's versions and shadow nothing — remove manually if unwanted.")
458
527
 
459
- # Write version stamp
460
- version_dir = CLAUDE_PLUGINS_DIR / "tl-cli"
461
- version_dir.mkdir(parents=True, exist_ok=True)
462
- (version_dir / ".version").write_text(__version__)
528
+ _write_version_stamp(installed_version or __version__)
463
529
 
464
530
  console.print()
465
531
  console.print("[green]Setup complete![/green]")
@@ -523,6 +589,14 @@ def _setup_noninteractive(fmt: str = "json") -> None:
523
589
 
524
590
  ok, output = _run_claude(["plugin", "install", PLUGIN_KEY], claude_bin)
525
591
  result["plugin_installed"] = ok or "already" in output.lower()
592
+
593
+ # `install` is a no-op on an already-installed plugin — this is what
594
+ # advances it to the marketplace's current version.
595
+ if result["plugin_installed"]:
596
+ ok, changed, output = _update_plugin(claude_bin)
597
+ result["plugin_updated"] = changed
598
+ if not ok:
599
+ result["plugin_update_error"] = output
526
600
  else:
527
601
  result["marketplace_registered"] = False
528
602
  result["plugin_installed"] = False
@@ -541,10 +615,11 @@ def _setup_noninteractive(fmt: str = "json") -> None:
541
615
  result["command_shim_installed"] = False
542
616
  result["standalone_skills_installed"] = _install_standalone_skills(plugin_root)
543
617
 
544
- # Write version stamp
545
- version_dir = CLAUDE_PLUGINS_DIR / "tl-cli"
546
- version_dir.mkdir(parents=True, exist_ok=True)
547
- (version_dir / ".version").write_text(__version__)
618
+ # Stamp what is actually installed. The standalone fallback copied this
619
+ # CLI's own bundled skills, so there __version__ is the honest answer.
620
+ installed_version = _installed_plugin_version() if result["plugin_installed"] else None
621
+ result["plugin_version"] = installed_version
622
+ _write_version_stamp(installed_version or __version__)
548
623
 
549
624
  result["status"] = "ok"
550
625
  _emit_setup_result(result, fmt)
@@ -426,7 +426,11 @@ def _resync_integrations() -> None:
426
426
  subprocess.run(
427
427
  [tl_bin, "setup", tool, "--json"],
428
428
  capture_output=True,
429
- timeout=120,
429
+ # The Claude Code path shells out to `claude` several times
430
+ # (marketplace add + update, plugin install + update), each
431
+ # with its own timeout — a 120s budget for the whole resync
432
+ # can expire mid-sequence on a slow network.
433
+ timeout=300,
430
434
  check=False,
431
435
  )
432
436
  except (OSError, subprocess.TimeoutExpired):
@@ -1,5 +1,6 @@
1
1
  """Tests for `tl setup` helpers."""
2
2
 
3
+ import json
3
4
  import sys
4
5
  from pathlib import Path
5
6
 
@@ -8,8 +9,10 @@ from tl_cli.commands.setup import (
8
9
  _bundled_skill_blurbs,
9
10
  _find_claude_binary,
10
11
  _install_command_shim,
12
+ _installed_plugin_version,
11
13
  _remove_matching_standalone_skills,
12
14
  _trees_identical,
15
+ _update_plugin,
13
16
  )
14
17
 
15
18
 
@@ -191,3 +194,135 @@ class TestFindClaudeBinary:
191
194
  if sys.platform == "win32":
192
195
  monkeypatch.setenv("APPDATA", str(tmp_path / "AppData" / "Roaming"))
193
196
  assert _find_claude_binary() is None
197
+
198
+
199
+ class TestInstalledPluginVersion:
200
+ def _record(self, tmp_path, monkeypatch, payload):
201
+ plugins = tmp_path / "plugins"
202
+ plugins.mkdir(parents=True, exist_ok=True)
203
+ monkeypatch.setattr(setup, "CLAUDE_PLUGINS_DIR", plugins)
204
+ (plugins / "installed_plugins.json").write_text(json.dumps(payload), encoding="utf-8")
205
+ return plugins
206
+
207
+ def test_reads_version_from_install_record(self, tmp_path, monkeypatch):
208
+ self._record(tmp_path, monkeypatch, {"version": 2, "plugins": {setup.PLUGIN_KEY: [{"scope": "user", "version": "0.7.2"}]}})
209
+ assert _installed_plugin_version() == "0.7.2"
210
+
211
+ def test_prefers_user_scope(self, tmp_path, monkeypatch):
212
+ self._record(
213
+ tmp_path,
214
+ monkeypatch,
215
+ {"plugins": {setup.PLUGIN_KEY: [{"scope": "project", "version": "0.1.0"}, {"scope": "user", "version": "0.9.9"}]}},
216
+ )
217
+ assert _installed_plugin_version() == "0.9.9"
218
+
219
+ def test_ignores_unknown_version(self, tmp_path, monkeypatch):
220
+ self._record(tmp_path, monkeypatch, {"plugins": {setup.PLUGIN_KEY: [{"scope": "user", "version": "unknown"}]}})
221
+ assert _installed_plugin_version() is None
222
+
223
+ def test_missing_file(self, tmp_path, monkeypatch):
224
+ monkeypatch.setattr(setup, "CLAUDE_PLUGINS_DIR", tmp_path / "plugins")
225
+ assert _installed_plugin_version() is None
226
+
227
+ def test_unreadable_record_is_unknown_not_outdated(self, tmp_path, monkeypatch):
228
+ plugins = tmp_path / "plugins"
229
+ plugins.mkdir(parents=True)
230
+ monkeypatch.setattr(setup, "CLAUDE_PLUGINS_DIR", plugins)
231
+ (plugins / "installed_plugins.json").write_text("{not json", encoding="utf-8")
232
+ assert _installed_plugin_version() is None
233
+ assert not [w for w in setup.check_plugin_version() if "Claude Code plugin" in w]
234
+
235
+ def test_unexpected_shape_is_unknown(self, tmp_path, monkeypatch):
236
+ self._record(tmp_path, monkeypatch, {"plugins": {setup.PLUGIN_KEY: {"scope": "user", "version": "0.7.2"}}})
237
+ assert _installed_plugin_version() is None
238
+
239
+
240
+ class TestCheckPluginVersion:
241
+ def test_warns_on_record_behind_cli(self, tmp_path, monkeypatch):
242
+ plugins = tmp_path / "plugins"
243
+ plugins.mkdir(parents=True)
244
+ monkeypatch.setattr(setup, "CLAUDE_PLUGINS_DIR", plugins)
245
+ monkeypatch.setattr(setup, "OPENCODE_SKILLS_DIR", tmp_path / "opencode")
246
+ monkeypatch.setattr(setup, "AGENTS_SKILLS_DIR", tmp_path / "agents")
247
+ (plugins / "installed_plugins.json").write_text(
248
+ json.dumps({"plugins": {setup.PLUGIN_KEY: [{"scope": "user", "version": "0.7.2"}]}}), encoding="utf-8"
249
+ )
250
+ # A stamp claiming the current version must not mask the real one.
251
+ (plugins / "tl-cli").mkdir()
252
+ (plugins / "tl-cli" / ".version").write_text(setup.__version__)
253
+
254
+ warnings = setup.check_plugin_version()
255
+ assert any("0.7.2" in w for w in warnings)
256
+
257
+ def test_falls_back_to_stamp_without_record(self, tmp_path, monkeypatch):
258
+ plugins = tmp_path / "plugins"
259
+ (plugins / "tl-cli").mkdir(parents=True)
260
+ monkeypatch.setattr(setup, "CLAUDE_PLUGINS_DIR", plugins)
261
+ monkeypatch.setattr(setup, "OPENCODE_SKILLS_DIR", tmp_path / "opencode")
262
+ monkeypatch.setattr(setup, "AGENTS_SKILLS_DIR", tmp_path / "agents")
263
+ (plugins / "tl-cli" / ".version").write_text("0.1.0")
264
+
265
+ assert any("0.1.0" in w for w in setup.check_plugin_version())
266
+
267
+ def test_silent_when_nothing_installed(self, tmp_path, monkeypatch):
268
+ monkeypatch.setattr(setup, "CLAUDE_PLUGINS_DIR", tmp_path / "plugins")
269
+ monkeypatch.setattr(setup, "OPENCODE_SKILLS_DIR", tmp_path / "opencode")
270
+ monkeypatch.setattr(setup, "AGENTS_SKILLS_DIR", tmp_path / "agents")
271
+ assert setup.check_plugin_version() == []
272
+
273
+
274
+ class TestUpdatePlugin:
275
+ def test_already_latest_is_not_a_change(self, monkeypatch):
276
+ monkeypatch.setattr(setup, "_run_claude", lambda args, b: (True, 'tl-cli is already at the latest version (0.9.9).'))
277
+ assert _update_plugin("claude") == (True, False, 'tl-cli is already at the latest version (0.9.9).')
278
+
279
+ def test_version_advance_is_a_change(self, monkeypatch):
280
+ monkeypatch.setattr(setup, "_run_claude", lambda args, b: (True, 'Plugin "tl-cli" updated from 0.7.2 to 0.9.9.'))
281
+ ok, changed, _ = _update_plugin("claude")
282
+ assert (ok, changed) == (True, True)
283
+
284
+ def test_failure_is_not_a_change(self, monkeypatch):
285
+ monkeypatch.setattr(setup, "_run_claude", lambda args, b: (False, "boom"))
286
+ assert _update_plugin("claude") == (False, False, "boom")
287
+
288
+
289
+ class TestSetupCallSequence:
290
+ def _stub_env(self, tmp_path, monkeypatch, record=None):
291
+ plugin_root = tmp_path / "plugin"
292
+ (plugin_root / "skills").mkdir(parents=True)
293
+ plugins = tmp_path / "plugins"
294
+ plugins.mkdir()
295
+ if record is not None:
296
+ (plugins / "installed_plugins.json").write_text(json.dumps(record), encoding="utf-8")
297
+ monkeypatch.setattr(setup, "_find_plugin_root", lambda: plugin_root)
298
+ monkeypatch.setattr(setup, "_find_claude_binary", lambda: "/usr/bin/claude")
299
+ monkeypatch.setattr(setup, "CLAUDE_PLUGINS_DIR", plugins)
300
+ monkeypatch.setattr(setup, "CLAUDE_SKILLS_DIR", tmp_path / "skills")
301
+ monkeypatch.setattr(setup, "CLAUDE_COMMANDS_DIR", tmp_path / "commands")
302
+ calls = []
303
+ monkeypatch.setattr(setup, "_run_claude", lambda args, b: (calls.append(args), (True, "ok"))[1])
304
+ return calls, plugins
305
+
306
+ def test_interactive_updates_after_install(self, tmp_path, monkeypatch):
307
+ calls, _ = self._stub_env(tmp_path, monkeypatch)
308
+ setup.setup_claude(json_output=False, toon_output=False)
309
+ assert calls.index(["plugin", "update", setup.PLUGIN_KEY]) > calls.index(["plugin", "install", setup.PLUGIN_KEY])
310
+
311
+ def test_noninteractive_updates_after_install(self, tmp_path, monkeypatch, capsys):
312
+ calls, _ = self._stub_env(tmp_path, monkeypatch)
313
+ setup._setup_noninteractive()
314
+ assert calls.index(["plugin", "update", setup.PLUGIN_KEY]) > calls.index(["plugin", "install", setup.PLUGIN_KEY])
315
+ assert json.loads(capsys.readouterr().out)["plugin_updated"] is True
316
+
317
+ def test_stamp_records_installed_version_not_cli_version(self, tmp_path, monkeypatch, capsys):
318
+ record = {"plugins": {setup.PLUGIN_KEY: [{"scope": "user", "version": "0.7.2"}]}}
319
+ _, plugins = self._stub_env(tmp_path, monkeypatch, record=record)
320
+ setup._setup_noninteractive()
321
+ assert (plugins / "tl-cli" / ".version").read_text() == "0.7.2"
322
+ assert json.loads(capsys.readouterr().out)["plugin_version"] == "0.7.2"
323
+
324
+ def test_standalone_fallback_stamps_cli_version(self, tmp_path, monkeypatch, capsys):
325
+ _, plugins = self._stub_env(tmp_path, monkeypatch)
326
+ monkeypatch.setattr(setup, "_find_claude_binary", lambda: None)
327
+ setup._setup_noninteractive()
328
+ assert (plugins / "tl-cli" / ".version").read_text() == setup.__version__