bps-kit 1.0.1 → 1.0.2

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 (368) hide show
  1. package/package.json +1 -1
  2. package/templates/.agents/agents/backend-specialist.md +263 -0
  3. package/templates/.agents/agents/code-archaeologist.md +106 -0
  4. package/templates/.agents/agents/database-architect.md +226 -0
  5. package/templates/.agents/agents/debugger.md +225 -0
  6. package/templates/.agents/agents/devops-engineer.md +242 -0
  7. package/templates/.agents/agents/documentation-writer.md +104 -0
  8. package/templates/.agents/agents/explorer-agent.md +73 -0
  9. package/templates/.agents/agents/frontend-specialist.md +593 -0
  10. package/templates/.agents/agents/game-developer.md +162 -0
  11. package/templates/.agents/agents/mobile-developer.md +377 -0
  12. package/templates/.agents/agents/orchestrator.md +416 -0
  13. package/templates/.agents/agents/penetration-tester.md +188 -0
  14. package/templates/.agents/agents/performance-optimizer.md +187 -0
  15. package/templates/.agents/agents/product-manager.md +112 -0
  16. package/templates/.agents/agents/product-owner.md +95 -0
  17. package/templates/.agents/agents/project-planner.md +406 -0
  18. package/templates/.agents/agents/qa-automation-engineer.md +103 -0
  19. package/templates/.agents/agents/security-auditor.md +170 -0
  20. package/templates/.agents/agents/seo-specialist.md +111 -0
  21. package/templates/.agents/agents/test-engineer.md +158 -0
  22. package/templates/.agents/rules/GEMINI.md +219 -0
  23. package/templates/.agents/scripts/auto_preview.py +148 -0
  24. package/templates/.agents/scripts/checklist.py +217 -0
  25. package/templates/.agents/scripts/session_manager.py +120 -0
  26. package/templates/.agents/scripts/verify_all.py +327 -0
  27. package/templates/.agents/workflows/brainstorm.md +113 -0
  28. package/templates/.agents/workflows/create.md +59 -0
  29. package/templates/.agents/workflows/debug.md +103 -0
  30. package/templates/.agents/workflows/deploy.md +176 -0
  31. package/templates/.agents/workflows/enhance.md +63 -0
  32. package/templates/.agents/workflows/orchestrate.md +237 -0
  33. package/templates/.agents/workflows/plan.md +89 -0
  34. package/templates/.agents/workflows/preview.md +81 -0
  35. package/templates/.agents/workflows/setup-brain.md +39 -0
  36. package/templates/.agents/workflows/status.md +86 -0
  37. package/templates/.agents/workflows/test.md +144 -0
  38. package/templates/.agents/workflows/ui-ux-pro-max.md +296 -0
  39. package/templates/skills_normal/api-patterns/scripts/api_validator.py +211 -0
  40. package/templates/skills_normal/database-design/scripts/schema_validator.py +172 -0
  41. package/templates/skills_normal/frontend-design/scripts/accessibility_checker.py +183 -0
  42. package/templates/skills_normal/frontend-design/scripts/ux_audit.py +722 -0
  43. package/templates/skills_normal/git-pushing/scripts/smart_commit.sh +19 -0
  44. package/templates/skills_normal/lint-and-validate/scripts/lint_runner.py +184 -0
  45. package/templates/skills_normal/lint-and-validate/scripts/type_coverage.py +173 -0
  46. package/templates/skills_normal/performance-profiling/scripts/lighthouse_audit.py +76 -0
  47. package/templates/skills_normal/senior-fullstack/scripts/code_quality_analyzer.py +114 -0
  48. package/templates/skills_normal/senior-fullstack/scripts/fullstack_scaffolder.py +114 -0
  49. package/templates/skills_normal/senior-fullstack/scripts/project_scaffolder.py +114 -0
  50. package/templates/skills_normal/seo-fundamentals/scripts/seo_checker.py +219 -0
  51. package/templates/skills_normal/testing-patterns/scripts/test_runner.py +219 -0
  52. package/templates/skills_normal/vulnerability-scanner/scripts/security_scan.py +458 -0
  53. package/templates/vault/007/scripts/config.py +472 -0
  54. package/templates/vault/007/scripts/full_audit.py +1306 -0
  55. package/templates/vault/007/scripts/quick_scan.py +481 -0
  56. package/templates/vault/007/scripts/requirements.txt +26 -0
  57. package/templates/vault/007/scripts/scanners/__init__.py +0 -0
  58. package/templates/vault/007/scripts/scanners/dependency_scanner.py +1305 -0
  59. package/templates/vault/007/scripts/scanners/injection_scanner.py +1104 -0
  60. package/templates/vault/007/scripts/scanners/secrets_scanner.py +1008 -0
  61. package/templates/vault/007/scripts/score_calculator.py +693 -0
  62. package/templates/vault/agent-orchestrator/scripts/match_skills.py +329 -0
  63. package/templates/vault/agent-orchestrator/scripts/orchestrate.py +304 -0
  64. package/templates/vault/agent-orchestrator/scripts/requirements.txt +1 -0
  65. package/templates/vault/agent-orchestrator/scripts/scan_registry.py +508 -0
  66. package/templates/vault/ai-studio-image/scripts/config.py +613 -0
  67. package/templates/vault/ai-studio-image/scripts/generate.py +630 -0
  68. package/templates/vault/ai-studio-image/scripts/prompt_engine.py +424 -0
  69. package/templates/vault/ai-studio-image/scripts/requirements.txt +4 -0
  70. package/templates/vault/ai-studio-image/scripts/templates.py +349 -0
  71. package/templates/vault/android_ui_verification/scripts/verify_ui.sh +32 -0
  72. package/templates/vault/apify-audience-analysis/reference/scripts/run_actor.js +363 -0
  73. package/templates/vault/apify-brand-reputation-monitoring/reference/scripts/run_actor.js +363 -0
  74. package/templates/vault/apify-competitor-intelligence/reference/scripts/run_actor.js +363 -0
  75. package/templates/vault/apify-content-analytics/reference/scripts/run_actor.js +363 -0
  76. package/templates/vault/apify-ecommerce/reference/scripts/package.json +3 -0
  77. package/templates/vault/apify-ecommerce/reference/scripts/run_actor.js +369 -0
  78. package/templates/vault/apify-influencer-discovery/reference/scripts/run_actor.js +363 -0
  79. package/templates/vault/apify-lead-generation/reference/scripts/run_actor.js +363 -0
  80. package/templates/vault/apify-market-research/reference/scripts/run_actor.js +363 -0
  81. package/templates/vault/apify-trend-analysis/reference/scripts/run_actor.js +363 -0
  82. package/templates/vault/apify-ultimate-scraper/reference/scripts/run_actor.js +363 -0
  83. package/templates/vault/audio-transcriber/scripts/install-requirements.sh +190 -0
  84. package/templates/vault/audio-transcriber/scripts/transcribe.py +486 -0
  85. package/templates/vault/claude-monitor/scripts/api_bench.py +240 -0
  86. package/templates/vault/claude-monitor/scripts/config.py +69 -0
  87. package/templates/vault/claude-monitor/scripts/health_check.py +362 -0
  88. package/templates/vault/claude-monitor/scripts/monitor.py +296 -0
  89. package/templates/vault/content-creator/scripts/brand_voice_analyzer.py +185 -0
  90. package/templates/vault/content-creator/scripts/seo_optimizer.py +419 -0
  91. package/templates/vault/context-agent/scripts/active_context.py +227 -0
  92. package/templates/vault/context-agent/scripts/compressor.py +149 -0
  93. package/templates/vault/context-agent/scripts/config.py +69 -0
  94. package/templates/vault/context-agent/scripts/context_loader.py +155 -0
  95. package/templates/vault/context-agent/scripts/context_manager.py +302 -0
  96. package/templates/vault/context-agent/scripts/models.py +103 -0
  97. package/templates/vault/context-agent/scripts/project_registry.py +132 -0
  98. package/templates/vault/context-agent/scripts/requirements.txt +6 -0
  99. package/templates/vault/context-agent/scripts/search.py +115 -0
  100. package/templates/vault/context-agent/scripts/session_parser.py +206 -0
  101. package/templates/vault/context-agent/scripts/session_summary.py +319 -0
  102. package/templates/vault/context-guardian/scripts/context_snapshot.py +229 -0
  103. package/templates/vault/docx/ooxml/scripts/pack.py +159 -0
  104. package/templates/vault/docx/ooxml/scripts/unpack.py +29 -0
  105. package/templates/vault/docx/ooxml/scripts/validate.py +69 -0
  106. package/templates/vault/docx/ooxml/scripts/validation/__init__.py +15 -0
  107. package/templates/vault/docx/ooxml/scripts/validation/base.py +951 -0
  108. package/templates/vault/docx/ooxml/scripts/validation/docx.py +274 -0
  109. package/templates/vault/docx/ooxml/scripts/validation/pptx.py +315 -0
  110. package/templates/vault/docx/ooxml/scripts/validation/redlining.py +279 -0
  111. package/templates/vault/docx/scripts/__init__.py +1 -0
  112. package/templates/vault/docx/scripts/document.py +1276 -0
  113. package/templates/vault/docx/scripts/templates/comments.xml +3 -0
  114. package/templates/vault/docx/scripts/templates/commentsExtended.xml +3 -0
  115. package/templates/vault/docx/scripts/templates/commentsExtensible.xml +3 -0
  116. package/templates/vault/docx/scripts/templates/commentsIds.xml +3 -0
  117. package/templates/vault/docx/scripts/templates/people.xml +3 -0
  118. package/templates/vault/docx/scripts/utilities.py +374 -0
  119. package/templates/vault/docx-official/ooxml/scripts/pack.py +159 -0
  120. package/templates/vault/docx-official/ooxml/scripts/unpack.py +29 -0
  121. package/templates/vault/docx-official/ooxml/scripts/validate.py +69 -0
  122. package/templates/vault/docx-official/ooxml/scripts/validation/__init__.py +15 -0
  123. package/templates/vault/docx-official/ooxml/scripts/validation/base.py +951 -0
  124. package/templates/vault/docx-official/ooxml/scripts/validation/docx.py +274 -0
  125. package/templates/vault/docx-official/ooxml/scripts/validation/pptx.py +315 -0
  126. package/templates/vault/docx-official/ooxml/scripts/validation/redlining.py +279 -0
  127. package/templates/vault/docx-official/scripts/__init__.py +1 -0
  128. package/templates/vault/docx-official/scripts/document.py +1276 -0
  129. package/templates/vault/docx-official/scripts/templates/comments.xml +3 -0
  130. package/templates/vault/docx-official/scripts/templates/commentsExtended.xml +3 -0
  131. package/templates/vault/docx-official/scripts/templates/commentsExtensible.xml +3 -0
  132. package/templates/vault/docx-official/scripts/templates/commentsIds.xml +3 -0
  133. package/templates/vault/docx-official/scripts/templates/people.xml +3 -0
  134. package/templates/vault/docx-official/scripts/utilities.py +374 -0
  135. package/templates/vault/geo-fundamentals/scripts/geo_checker.py +289 -0
  136. package/templates/vault/helm-chart-scaffolding/scripts/validate-chart.sh +244 -0
  137. package/templates/vault/i18n-localization/scripts/i18n_checker.py +241 -0
  138. package/templates/vault/instagram/scripts/account_setup.py +233 -0
  139. package/templates/vault/instagram/scripts/analyze.py +221 -0
  140. package/templates/vault/instagram/scripts/api_client.py +444 -0
  141. package/templates/vault/instagram/scripts/auth.py +411 -0
  142. package/templates/vault/instagram/scripts/comments.py +160 -0
  143. package/templates/vault/instagram/scripts/config.py +111 -0
  144. package/templates/vault/instagram/scripts/db.py +467 -0
  145. package/templates/vault/instagram/scripts/export.py +138 -0
  146. package/templates/vault/instagram/scripts/governance.py +233 -0
  147. package/templates/vault/instagram/scripts/hashtags.py +114 -0
  148. package/templates/vault/instagram/scripts/insights.py +170 -0
  149. package/templates/vault/instagram/scripts/media.py +65 -0
  150. package/templates/vault/instagram/scripts/messages.py +103 -0
  151. package/templates/vault/instagram/scripts/profile.py +58 -0
  152. package/templates/vault/instagram/scripts/publish.py +449 -0
  153. package/templates/vault/instagram/scripts/requirements.txt +5 -0
  154. package/templates/vault/instagram/scripts/run_all.py +189 -0
  155. package/templates/vault/instagram/scripts/schedule.py +189 -0
  156. package/templates/vault/instagram/scripts/serve_api.py +234 -0
  157. package/templates/vault/instagram/scripts/templates.py +155 -0
  158. package/templates/vault/junta-leiloeiros/scripts/db.py +216 -0
  159. package/templates/vault/junta-leiloeiros/scripts/export.py +137 -0
  160. package/templates/vault/junta-leiloeiros/scripts/requirements.txt +15 -0
  161. package/templates/vault/junta-leiloeiros/scripts/run_all.py +190 -0
  162. package/templates/vault/junta-leiloeiros/scripts/scraper/__init__.py +4 -0
  163. package/templates/vault/junta-leiloeiros/scripts/scraper/base_scraper.py +209 -0
  164. package/templates/vault/junta-leiloeiros/scripts/scraper/generic_scraper.py +110 -0
  165. package/templates/vault/junta-leiloeiros/scripts/scraper/jucap.py +110 -0
  166. package/templates/vault/junta-leiloeiros/scripts/scraper/juceac.py +72 -0
  167. package/templates/vault/junta-leiloeiros/scripts/scraper/juceal.py +72 -0
  168. package/templates/vault/junta-leiloeiros/scripts/scraper/juceb.py +68 -0
  169. package/templates/vault/junta-leiloeiros/scripts/scraper/jucec.py +63 -0
  170. package/templates/vault/junta-leiloeiros/scripts/scraper/jucema.py +211 -0
  171. package/templates/vault/junta-leiloeiros/scripts/scraper/jucemg.py +218 -0
  172. package/templates/vault/junta-leiloeiros/scripts/scraper/jucep.py +70 -0
  173. package/templates/vault/junta-leiloeiros/scripts/scraper/jucepa.py +74 -0
  174. package/templates/vault/junta-leiloeiros/scripts/scraper/jucepar.py +80 -0
  175. package/templates/vault/junta-leiloeiros/scripts/scraper/jucepe.py +78 -0
  176. package/templates/vault/junta-leiloeiros/scripts/scraper/jucepi.py +69 -0
  177. package/templates/vault/junta-leiloeiros/scripts/scraper/jucer.py +256 -0
  178. package/templates/vault/junta-leiloeiros/scripts/scraper/jucerja.py +170 -0
  179. package/templates/vault/junta-leiloeiros/scripts/scraper/jucern.py +71 -0
  180. package/templates/vault/junta-leiloeiros/scripts/scraper/jucesc.py +89 -0
  181. package/templates/vault/junta-leiloeiros/scripts/scraper/jucesp.py +233 -0
  182. package/templates/vault/junta-leiloeiros/scripts/scraper/jucetins.py +134 -0
  183. package/templates/vault/junta-leiloeiros/scripts/scraper/jucis_df.py +63 -0
  184. package/templates/vault/junta-leiloeiros/scripts/scraper/jucisrs.py +299 -0
  185. package/templates/vault/junta-leiloeiros/scripts/scraper/states.py +99 -0
  186. package/templates/vault/junta-leiloeiros/scripts/serve_api.py +164 -0
  187. package/templates/vault/junta-leiloeiros/scripts/web_scraper_fallback.py +233 -0
  188. package/templates/vault/last30days/scripts/last30days.py +521 -0
  189. package/templates/vault/last30days/scripts/lib/__init__.py +1 -0
  190. package/templates/vault/last30days/scripts/lib/cache.py +152 -0
  191. package/templates/vault/last30days/scripts/lib/dates.py +124 -0
  192. package/templates/vault/last30days/scripts/lib/dedupe.py +120 -0
  193. package/templates/vault/last30days/scripts/lib/env.py +149 -0
  194. package/templates/vault/last30days/scripts/lib/http.py +152 -0
  195. package/templates/vault/last30days/scripts/lib/models.py +175 -0
  196. package/templates/vault/last30days/scripts/lib/normalize.py +160 -0
  197. package/templates/vault/last30days/scripts/lib/openai_reddit.py +230 -0
  198. package/templates/vault/last30days/scripts/lib/reddit_enrich.py +232 -0
  199. package/templates/vault/last30days/scripts/lib/render.py +383 -0
  200. package/templates/vault/last30days/scripts/lib/schema.py +336 -0
  201. package/templates/vault/last30days/scripts/lib/score.py +311 -0
  202. package/templates/vault/last30days/scripts/lib/ui.py +324 -0
  203. package/templates/vault/last30days/scripts/lib/websearch.py +401 -0
  204. package/templates/vault/last30days/scripts/lib/xai_x.py +217 -0
  205. package/templates/vault/leiloeiro-avaliacao/scripts/governance.py +106 -0
  206. package/templates/vault/leiloeiro-avaliacao/scripts/requirements.txt +1 -0
  207. package/templates/vault/leiloeiro-edital/scripts/governance.py +106 -0
  208. package/templates/vault/leiloeiro-edital/scripts/requirements.txt +1 -0
  209. package/templates/vault/leiloeiro-ia/scripts/governance.py +106 -0
  210. package/templates/vault/leiloeiro-ia/scripts/requirements.txt +1 -0
  211. package/templates/vault/leiloeiro-juridico/scripts/governance.py +106 -0
  212. package/templates/vault/leiloeiro-juridico/scripts/requirements.txt +1 -0
  213. package/templates/vault/leiloeiro-mercado/scripts/governance.py +106 -0
  214. package/templates/vault/leiloeiro-mercado/scripts/requirements.txt +1 -0
  215. package/templates/vault/leiloeiro-risco/scripts/governance.py +106 -0
  216. package/templates/vault/leiloeiro-risco/scripts/requirements.txt +1 -0
  217. package/templates/vault/loki-mode/examples/todo-app-generated/backend/src/db/database.ts +24 -0
  218. package/templates/vault/loki-mode/examples/todo-app-generated/backend/src/db/db.ts +35 -0
  219. package/templates/vault/loki-mode/examples/todo-app-generated/backend/src/db/index.ts +2 -0
  220. package/templates/vault/loki-mode/examples/todo-app-generated/backend/src/db/migrations.ts +31 -0
  221. package/templates/vault/loki-mode/examples/todo-app-generated/backend/src/db/schema.sql +8 -0
  222. package/templates/vault/loki-mode/examples/todo-app-generated/backend/src/index.ts +44 -0
  223. package/templates/vault/loki-mode/examples/todo-app-generated/backend/src/routes/todos.ts +155 -0
  224. package/templates/vault/loki-mode/examples/todo-app-generated/backend/src/types/index.ts +35 -0
  225. package/templates/vault/loki-mode/examples/todo-app-generated/frontend/src/App.css +384 -0
  226. package/templates/vault/loki-mode/examples/todo-app-generated/frontend/src/App.tsx +81 -0
  227. package/templates/vault/loki-mode/examples/todo-app-generated/frontend/src/api/todos.ts +57 -0
  228. package/templates/vault/loki-mode/examples/todo-app-generated/frontend/src/components/ConfirmDialog.tsx +26 -0
  229. package/templates/vault/loki-mode/examples/todo-app-generated/frontend/src/components/EmptyState.tsx +8 -0
  230. package/templates/vault/loki-mode/examples/todo-app-generated/frontend/src/components/TodoForm.tsx +43 -0
  231. package/templates/vault/loki-mode/examples/todo-app-generated/frontend/src/components/TodoItem.tsx +36 -0
  232. package/templates/vault/loki-mode/examples/todo-app-generated/frontend/src/components/TodoList.tsx +27 -0
  233. package/templates/vault/loki-mode/examples/todo-app-generated/frontend/src/hooks/useTodos.ts +81 -0
  234. package/templates/vault/loki-mode/examples/todo-app-generated/frontend/src/index.css +48 -0
  235. package/templates/vault/loki-mode/examples/todo-app-generated/frontend/src/main.tsx +10 -0
  236. package/templates/vault/loki-mode/examples/todo-app-generated/frontend/src/vite-env.d.ts +1 -0
  237. package/templates/vault/loki-mode/scripts/export-to-vibe-kanban.sh +178 -0
  238. package/templates/vault/loki-mode/scripts/loki-wrapper.sh +281 -0
  239. package/templates/vault/loki-mode/scripts/take-screenshots.js +55 -0
  240. package/templates/vault/matematico-tao/scripts/complexity_analyzer.py +544 -0
  241. package/templates/vault/matematico-tao/scripts/dependency_graph.py +538 -0
  242. package/templates/vault/mcp-builder/scripts/connections.py +151 -0
  243. package/templates/vault/mcp-builder/scripts/evaluation.py +373 -0
  244. package/templates/vault/mcp-builder/scripts/example_evaluation.xml +22 -0
  245. package/templates/vault/mcp-builder/scripts/requirements.txt +2 -0
  246. package/templates/vault/mobile-design/scripts/mobile_audit.py +670 -0
  247. package/templates/vault/notebooklm/scripts/__init__.py +81 -0
  248. package/templates/vault/notebooklm/scripts/ask_question.py +256 -0
  249. package/templates/vault/notebooklm/scripts/auth_manager.py +358 -0
  250. package/templates/vault/notebooklm/scripts/browser_session.py +255 -0
  251. package/templates/vault/notebooklm/scripts/browser_utils.py +107 -0
  252. package/templates/vault/notebooklm/scripts/cleanup_manager.py +302 -0
  253. package/templates/vault/notebooklm/scripts/config.py +44 -0
  254. package/templates/vault/notebooklm/scripts/notebook_manager.py +410 -0
  255. package/templates/vault/notebooklm/scripts/run.py +102 -0
  256. package/templates/vault/notebooklm/scripts/setup_environment.py +204 -0
  257. package/templates/vault/pdf/scripts/check_bounding_boxes.py +70 -0
  258. package/templates/vault/pdf/scripts/check_bounding_boxes_test.py +226 -0
  259. package/templates/vault/pdf/scripts/check_fillable_fields.py +12 -0
  260. package/templates/vault/pdf/scripts/convert_pdf_to_images.py +35 -0
  261. package/templates/vault/pdf/scripts/create_validation_image.py +41 -0
  262. package/templates/vault/pdf/scripts/extract_form_field_info.py +152 -0
  263. package/templates/vault/pdf/scripts/fill_fillable_fields.py +114 -0
  264. package/templates/vault/pdf/scripts/fill_pdf_form_with_annotations.py +108 -0
  265. package/templates/vault/pdf-official/scripts/check_bounding_boxes.py +70 -0
  266. package/templates/vault/pdf-official/scripts/check_bounding_boxes_test.py +226 -0
  267. package/templates/vault/pdf-official/scripts/check_fillable_fields.py +12 -0
  268. package/templates/vault/pdf-official/scripts/convert_pdf_to_images.py +35 -0
  269. package/templates/vault/pdf-official/scripts/create_validation_image.py +41 -0
  270. package/templates/vault/pdf-official/scripts/extract_form_field_info.py +152 -0
  271. package/templates/vault/pdf-official/scripts/fill_fillable_fields.py +114 -0
  272. package/templates/vault/pdf-official/scripts/fill_pdf_form_with_annotations.py +108 -0
  273. package/templates/vault/planning-with-files/scripts/check-complete.sh +44 -0
  274. package/templates/vault/planning-with-files/scripts/init-session.sh +120 -0
  275. package/templates/vault/pptx/ooxml/scripts/pack.py +159 -0
  276. package/templates/vault/pptx/ooxml/scripts/unpack.py +29 -0
  277. package/templates/vault/pptx/ooxml/scripts/validate.py +69 -0
  278. package/templates/vault/pptx/ooxml/scripts/validation/__init__.py +15 -0
  279. package/templates/vault/pptx/ooxml/scripts/validation/base.py +951 -0
  280. package/templates/vault/pptx/ooxml/scripts/validation/docx.py +274 -0
  281. package/templates/vault/pptx/ooxml/scripts/validation/pptx.py +315 -0
  282. package/templates/vault/pptx/ooxml/scripts/validation/redlining.py +279 -0
  283. package/templates/vault/pptx/scripts/html2pptx.js +979 -0
  284. package/templates/vault/pptx/scripts/inventory.py +1020 -0
  285. package/templates/vault/pptx/scripts/rearrange.py +231 -0
  286. package/templates/vault/pptx/scripts/replace.py +385 -0
  287. package/templates/vault/pptx/scripts/thumbnail.py +450 -0
  288. package/templates/vault/pptx-official/ooxml/scripts/pack.py +159 -0
  289. package/templates/vault/pptx-official/ooxml/scripts/unpack.py +29 -0
  290. package/templates/vault/pptx-official/ooxml/scripts/validate.py +69 -0
  291. package/templates/vault/pptx-official/ooxml/scripts/validation/__init__.py +15 -0
  292. package/templates/vault/pptx-official/ooxml/scripts/validation/base.py +951 -0
  293. package/templates/vault/pptx-official/ooxml/scripts/validation/docx.py +274 -0
  294. package/templates/vault/pptx-official/ooxml/scripts/validation/pptx.py +315 -0
  295. package/templates/vault/pptx-official/ooxml/scripts/validation/redlining.py +279 -0
  296. package/templates/vault/pptx-official/scripts/html2pptx.js +979 -0
  297. package/templates/vault/pptx-official/scripts/inventory.py +1020 -0
  298. package/templates/vault/pptx-official/scripts/rearrange.py +231 -0
  299. package/templates/vault/pptx-official/scripts/replace.py +385 -0
  300. package/templates/vault/pptx-official/scripts/thumbnail.py +450 -0
  301. package/templates/vault/product-manager-toolkit/scripts/customer_interview_analyzer.py +441 -0
  302. package/templates/vault/product-manager-toolkit/scripts/rice_prioritizer.py +296 -0
  303. package/templates/vault/prompt-engineering-patterns/scripts/optimize-prompt.py +279 -0
  304. package/templates/vault/scripts/.skill_cache.json +7538 -0
  305. package/templates/vault/scripts/skill_search.py +228 -0
  306. package/templates/vault/senior-architect/scripts/architecture_diagram_generator.py +114 -0
  307. package/templates/vault/senior-architect/scripts/dependency_analyzer.py +114 -0
  308. package/templates/vault/senior-architect/scripts/project_architect.py +114 -0
  309. package/templates/vault/shopify-development/scripts/requirements.txt +19 -0
  310. package/templates/vault/shopify-development/scripts/shopify_graphql.py +428 -0
  311. package/templates/vault/shopify-development/scripts/shopify_init.py +441 -0
  312. package/templates/vault/shopify-development/scripts/tests/test_shopify_init.py +379 -0
  313. package/templates/vault/skill-creator/scripts/init_skill.py +303 -0
  314. package/templates/vault/skill-creator/scripts/package_skill.py +110 -0
  315. package/templates/vault/skill-creator/scripts/quick_validate.py +95 -0
  316. package/templates/vault/skill-installer/scripts/detect_skills.py +318 -0
  317. package/templates/vault/skill-installer/scripts/install_skill.py +1708 -0
  318. package/templates/vault/skill-installer/scripts/package_skill.py +417 -0
  319. package/templates/vault/skill-installer/scripts/requirements.txt +1 -0
  320. package/templates/vault/skill-installer/scripts/validate_skill.py +430 -0
  321. package/templates/vault/skill-sentinel/scripts/analyzers/__init__.py +13 -0
  322. package/templates/vault/skill-sentinel/scripts/analyzers/code_quality.py +247 -0
  323. package/templates/vault/skill-sentinel/scripts/analyzers/cross_skill.py +134 -0
  324. package/templates/vault/skill-sentinel/scripts/analyzers/dependencies.py +121 -0
  325. package/templates/vault/skill-sentinel/scripts/analyzers/documentation.py +189 -0
  326. package/templates/vault/skill-sentinel/scripts/analyzers/governance_audit.py +153 -0
  327. package/templates/vault/skill-sentinel/scripts/analyzers/performance.py +164 -0
  328. package/templates/vault/skill-sentinel/scripts/analyzers/security.py +189 -0
  329. package/templates/vault/skill-sentinel/scripts/config.py +158 -0
  330. package/templates/vault/skill-sentinel/scripts/cost_optimizer.py +146 -0
  331. package/templates/vault/skill-sentinel/scripts/db.py +354 -0
  332. package/templates/vault/skill-sentinel/scripts/governance.py +58 -0
  333. package/templates/vault/skill-sentinel/scripts/recommender.py +228 -0
  334. package/templates/vault/skill-sentinel/scripts/report_generator.py +224 -0
  335. package/templates/vault/skill-sentinel/scripts/requirements.txt +1 -0
  336. package/templates/vault/skill-sentinel/scripts/run_audit.py +290 -0
  337. package/templates/vault/skill-sentinel/scripts/scanner.py +271 -0
  338. package/templates/vault/stability-ai/scripts/config.py +266 -0
  339. package/templates/vault/stability-ai/scripts/generate.py +687 -0
  340. package/templates/vault/stability-ai/scripts/requirements.txt +4 -0
  341. package/templates/vault/stability-ai/scripts/styles.py +174 -0
  342. package/templates/vault/telegram/assets/boilerplate/nodejs/src/bot-client.ts +86 -0
  343. package/templates/vault/telegram/assets/boilerplate/nodejs/src/handlers.ts +79 -0
  344. package/templates/vault/telegram/assets/boilerplate/nodejs/src/index.ts +32 -0
  345. package/templates/vault/telegram/scripts/send_message.py +143 -0
  346. package/templates/vault/telegram/scripts/setup_project.py +103 -0
  347. package/templates/vault/telegram/scripts/test_bot.py +144 -0
  348. package/templates/vault/typescript-expert/scripts/ts_diagnostic.py +203 -0
  349. package/templates/vault/ui-ux-pro-max/scripts/__pycache__/core.cpython-314.pyc +0 -0
  350. package/templates/vault/ui-ux-pro-max/scripts/__pycache__/design_system.cpython-314.pyc +0 -0
  351. package/templates/vault/ui-ux-pro-max/scripts/core.py +257 -0
  352. package/templates/vault/ui-ux-pro-max/scripts/design_system.py +487 -0
  353. package/templates/vault/ui-ux-pro-max/scripts/search.py +76 -0
  354. package/templates/vault/videodb/scripts/ws_listener.py +204 -0
  355. package/templates/vault/web-artifacts-builder/scripts/bundle-artifact.sh +54 -0
  356. package/templates/vault/web-artifacts-builder/scripts/init-artifact.sh +322 -0
  357. package/templates/vault/web-artifacts-builder/scripts/shadcn-components.tar.gz +0 -0
  358. package/templates/vault/webapp-testing/scripts/with_server.py +106 -0
  359. package/templates/vault/whatsapp-cloud-api/assets/boilerplate/nodejs/src/index.ts +125 -0
  360. package/templates/vault/whatsapp-cloud-api/assets/boilerplate/nodejs/src/template-manager.ts +67 -0
  361. package/templates/vault/whatsapp-cloud-api/assets/boilerplate/nodejs/src/types.ts +216 -0
  362. package/templates/vault/whatsapp-cloud-api/assets/boilerplate/nodejs/src/webhook-handler.ts +173 -0
  363. package/templates/vault/whatsapp-cloud-api/assets/boilerplate/nodejs/src/whatsapp-client.ts +193 -0
  364. package/templates/vault/whatsapp-cloud-api/scripts/send_test_message.py +137 -0
  365. package/templates/vault/whatsapp-cloud-api/scripts/setup_project.py +118 -0
  366. package/templates/vault/whatsapp-cloud-api/scripts/validate_config.py +190 -0
  367. package/templates/vault/youtube-summarizer/scripts/extract-transcript.py +65 -0
  368. package/templates/vault/youtube-summarizer/scripts/install-dependencies.sh +28 -0
@@ -0,0 +1,401 @@
1
+ """WebSearch module for last30days skill.
2
+
3
+ NOTE: WebSearch uses Claude's built-in WebSearch tool, which runs INSIDE Claude Code.
4
+ Unlike Reddit/X which use external APIs, WebSearch results are obtained by Claude
5
+ directly and passed to this module for normalization and scoring.
6
+
7
+ The typical flow is:
8
+ 1. Claude invokes WebSearch tool with the topic
9
+ 2. Claude passes results to parse_websearch_results()
10
+ 3. Results are normalized into WebSearchItem objects
11
+ """
12
+
13
+ import re
14
+ from datetime import datetime, timedelta
15
+ from typing import Any, Dict, List, Optional, Tuple
16
+ from urllib.parse import urlparse
17
+
18
+ from . import schema
19
+
20
+
21
+ # Month name mappings for date parsing
22
+ MONTH_MAP = {
23
+ "jan": 1, "january": 1,
24
+ "feb": 2, "february": 2,
25
+ "mar": 3, "march": 3,
26
+ "apr": 4, "april": 4,
27
+ "may": 5,
28
+ "jun": 6, "june": 6,
29
+ "jul": 7, "july": 7,
30
+ "aug": 8, "august": 8,
31
+ "sep": 9, "sept": 9, "september": 9,
32
+ "oct": 10, "october": 10,
33
+ "nov": 11, "november": 11,
34
+ "dec": 12, "december": 12,
35
+ }
36
+
37
+
38
+ def extract_date_from_url(url: str) -> Optional[str]:
39
+ """Try to extract a date from URL path.
40
+
41
+ Many sites embed dates in URLs like:
42
+ - /2026/01/24/article-title
43
+ - /2026-01-24/article
44
+ - /blog/20260124/title
45
+
46
+ Args:
47
+ url: URL to parse
48
+
49
+ Returns:
50
+ Date string in YYYY-MM-DD format, or None
51
+ """
52
+ # Pattern 1: /YYYY/MM/DD/ (most common)
53
+ match = re.search(r'/(\d{4})/(\d{2})/(\d{2})/', url)
54
+ if match:
55
+ year, month, day = match.groups()
56
+ if 2020 <= int(year) <= 2030 and 1 <= int(month) <= 12 and 1 <= int(day) <= 31:
57
+ return f"{year}-{month}-{day}"
58
+
59
+ # Pattern 2: /YYYY-MM-DD/ or /YYYY-MM-DD-
60
+ match = re.search(r'/(\d{4})-(\d{2})-(\d{2})[-/]', url)
61
+ if match:
62
+ year, month, day = match.groups()
63
+ if 2020 <= int(year) <= 2030 and 1 <= int(month) <= 12 and 1 <= int(day) <= 31:
64
+ return f"{year}-{month}-{day}"
65
+
66
+ # Pattern 3: /YYYYMMDD/ (compact)
67
+ match = re.search(r'/(\d{4})(\d{2})(\d{2})/', url)
68
+ if match:
69
+ year, month, day = match.groups()
70
+ if 2020 <= int(year) <= 2030 and 1 <= int(month) <= 12 and 1 <= int(day) <= 31:
71
+ return f"{year}-{month}-{day}"
72
+
73
+ return None
74
+
75
+
76
+ def extract_date_from_snippet(text: str) -> Optional[str]:
77
+ """Try to extract a date from text snippet or title.
78
+
79
+ Looks for patterns like:
80
+ - January 24, 2026 or Jan 24, 2026
81
+ - 24 January 2026
82
+ - 2026-01-24
83
+ - "3 days ago", "yesterday", "last week"
84
+
85
+ Args:
86
+ text: Text to parse
87
+
88
+ Returns:
89
+ Date string in YYYY-MM-DD format, or None
90
+ """
91
+ if not text:
92
+ return None
93
+
94
+ text_lower = text.lower()
95
+
96
+ # Pattern 1: Month DD, YYYY (e.g., "January 24, 2026")
97
+ match = re.search(
98
+ r'\b(jan(?:uary)?|feb(?:ruary)?|mar(?:ch)?|apr(?:il)?|may|jun(?:e)?|'
99
+ r'jul(?:y)?|aug(?:ust)?|sep(?:t(?:ember)?)?|oct(?:ober)?|nov(?:ember)?|dec(?:ember)?)'
100
+ r'\s+(\d{1,2})(?:st|nd|rd|th)?,?\s*(\d{4})\b',
101
+ text_lower
102
+ )
103
+ if match:
104
+ month_str, day, year = match.groups()
105
+ month = MONTH_MAP.get(month_str[:3])
106
+ if month and 2020 <= int(year) <= 2030 and 1 <= int(day) <= 31:
107
+ return f"{year}-{month:02d}-{int(day):02d}"
108
+
109
+ # Pattern 2: DD Month YYYY (e.g., "24 January 2026")
110
+ match = re.search(
111
+ r'\b(\d{1,2})(?:st|nd|rd|th)?\s+'
112
+ r'(jan(?:uary)?|feb(?:ruary)?|mar(?:ch)?|apr(?:il)?|may|jun(?:e)?|'
113
+ r'jul(?:y)?|aug(?:ust)?|sep(?:t(?:ember)?)?|oct(?:ober)?|nov(?:ember)?|dec(?:ember)?)'
114
+ r'\s+(\d{4})\b',
115
+ text_lower
116
+ )
117
+ if match:
118
+ day, month_str, year = match.groups()
119
+ month = MONTH_MAP.get(month_str[:3])
120
+ if month and 2020 <= int(year) <= 2030 and 1 <= int(day) <= 31:
121
+ return f"{year}-{month:02d}-{int(day):02d}"
122
+
123
+ # Pattern 3: YYYY-MM-DD (ISO format)
124
+ match = re.search(r'\b(\d{4})-(\d{2})-(\d{2})\b', text)
125
+ if match:
126
+ year, month, day = match.groups()
127
+ if 2020 <= int(year) <= 2030 and 1 <= int(month) <= 12 and 1 <= int(day) <= 31:
128
+ return f"{year}-{month}-{day}"
129
+
130
+ # Pattern 4: Relative dates ("3 days ago", "yesterday", etc.)
131
+ today = datetime.now()
132
+
133
+ if "yesterday" in text_lower:
134
+ date = today - timedelta(days=1)
135
+ return date.strftime("%Y-%m-%d")
136
+
137
+ if "today" in text_lower:
138
+ return today.strftime("%Y-%m-%d")
139
+
140
+ # "N days ago"
141
+ match = re.search(r'\b(\d+)\s*days?\s*ago\b', text_lower)
142
+ if match:
143
+ days = int(match.group(1))
144
+ if days <= 60: # Reasonable range
145
+ date = today - timedelta(days=days)
146
+ return date.strftime("%Y-%m-%d")
147
+
148
+ # "N hours ago" -> today
149
+ match = re.search(r'\b(\d+)\s*hours?\s*ago\b', text_lower)
150
+ if match:
151
+ return today.strftime("%Y-%m-%d")
152
+
153
+ # "last week" -> ~7 days ago
154
+ if "last week" in text_lower:
155
+ date = today - timedelta(days=7)
156
+ return date.strftime("%Y-%m-%d")
157
+
158
+ # "this week" -> ~3 days ago (middle of week)
159
+ if "this week" in text_lower:
160
+ date = today - timedelta(days=3)
161
+ return date.strftime("%Y-%m-%d")
162
+
163
+ return None
164
+
165
+
166
+ def extract_date_signals(
167
+ url: str,
168
+ snippet: str,
169
+ title: str,
170
+ ) -> Tuple[Optional[str], str]:
171
+ """Extract date from any available signal.
172
+
173
+ Tries URL first (most reliable), then snippet, then title.
174
+
175
+ Args:
176
+ url: Page URL
177
+ snippet: Page snippet/description
178
+ title: Page title
179
+
180
+ Returns:
181
+ Tuple of (date_string, confidence)
182
+ - date from URL: 'high' confidence
183
+ - date from snippet/title: 'med' confidence
184
+ - no date found: None, 'low' confidence
185
+ """
186
+ # Try URL first (most reliable)
187
+ url_date = extract_date_from_url(url)
188
+ if url_date:
189
+ return url_date, "high"
190
+
191
+ # Try snippet
192
+ snippet_date = extract_date_from_snippet(snippet)
193
+ if snippet_date:
194
+ return snippet_date, "med"
195
+
196
+ # Try title
197
+ title_date = extract_date_from_snippet(title)
198
+ if title_date:
199
+ return title_date, "med"
200
+
201
+ return None, "low"
202
+
203
+
204
+ # Domains to exclude (Reddit and X are handled separately)
205
+ EXCLUDED_DOMAINS = {
206
+ "reddit.com",
207
+ "www.reddit.com",
208
+ "old.reddit.com",
209
+ "twitter.com",
210
+ "www.twitter.com",
211
+ "x.com",
212
+ "www.x.com",
213
+ "mobile.twitter.com",
214
+ }
215
+
216
+
217
+ def extract_domain(url: str) -> str:
218
+ """Extract the domain from a URL.
219
+
220
+ Args:
221
+ url: Full URL
222
+
223
+ Returns:
224
+ Domain string (e.g., "medium.com")
225
+ """
226
+ try:
227
+ parsed = urlparse(url)
228
+ domain = parsed.netloc.lower()
229
+ # Remove www. prefix for cleaner display
230
+ if domain.startswith("www."):
231
+ domain = domain[4:]
232
+ return domain
233
+ except Exception:
234
+ return ""
235
+
236
+
237
+ def is_excluded_domain(url: str) -> bool:
238
+ """Check if URL is from an excluded domain (Reddit/X).
239
+
240
+ Args:
241
+ url: URL to check
242
+
243
+ Returns:
244
+ True if URL should be excluded
245
+ """
246
+ try:
247
+ parsed = urlparse(url)
248
+ domain = parsed.netloc.lower()
249
+ return domain in EXCLUDED_DOMAINS
250
+ except Exception:
251
+ return False
252
+
253
+
254
+ def parse_websearch_results(
255
+ results: List[Dict[str, Any]],
256
+ topic: str,
257
+ from_date: str = "",
258
+ to_date: str = "",
259
+ ) -> List[Dict[str, Any]]:
260
+ """Parse WebSearch results into normalized format.
261
+
262
+ This function expects results from Claude's WebSearch tool.
263
+ Each result should have: title, url, snippet, and optionally date/relevance.
264
+
265
+ Uses "Date Detective" approach:
266
+ 1. Extract dates from URLs (high confidence)
267
+ 2. Extract dates from snippets/titles (med confidence)
268
+ 3. Hard filter: exclude items with verified old dates
269
+ 4. Keep items with no date signals (with low confidence penalty)
270
+
271
+ Args:
272
+ results: List of WebSearch result dicts
273
+ topic: Original search topic (for context)
274
+ from_date: Start date for filtering (YYYY-MM-DD)
275
+ to_date: End date for filtering (YYYY-MM-DD)
276
+
277
+ Returns:
278
+ List of normalized item dicts ready for WebSearchItem creation
279
+ """
280
+ items = []
281
+
282
+ for i, result in enumerate(results):
283
+ if not isinstance(result, dict):
284
+ continue
285
+
286
+ url = result.get("url", "")
287
+ if not url:
288
+ continue
289
+
290
+ # Skip Reddit/X URLs (handled separately)
291
+ if is_excluded_domain(url):
292
+ continue
293
+
294
+ title = str(result.get("title", "")).strip()
295
+ snippet = str(result.get("snippet", result.get("description", ""))).strip()
296
+
297
+ if not title and not snippet:
298
+ continue
299
+
300
+ # Use Date Detective to extract date signals
301
+ date = result.get("date") # Use provided date if available
302
+ date_confidence = "low"
303
+
304
+ if date and re.match(r'^\d{4}-\d{2}-\d{2}$', str(date)):
305
+ # Provided date is valid
306
+ date_confidence = "med"
307
+ else:
308
+ # Try to extract date from URL/snippet/title
309
+ extracted_date, confidence = extract_date_signals(url, snippet, title)
310
+ if extracted_date:
311
+ date = extracted_date
312
+ date_confidence = confidence
313
+
314
+ # Hard filter: if we found a date and it's too old, skip
315
+ if date and from_date and date < from_date:
316
+ continue # DROP - verified old content
317
+
318
+ # Hard filter: if date is in the future, skip (parsing error)
319
+ if date and to_date and date > to_date:
320
+ continue # DROP - future date
321
+
322
+ # Get relevance if provided, default to 0.5
323
+ relevance = result.get("relevance", 0.5)
324
+ try:
325
+ relevance = min(1.0, max(0.0, float(relevance)))
326
+ except (TypeError, ValueError):
327
+ relevance = 0.5
328
+
329
+ item = {
330
+ "id": f"W{i+1}",
331
+ "title": title[:200], # Truncate long titles
332
+ "url": url,
333
+ "source_domain": extract_domain(url),
334
+ "snippet": snippet[:500], # Truncate long snippets
335
+ "date": date,
336
+ "date_confidence": date_confidence,
337
+ "relevance": relevance,
338
+ "why_relevant": str(result.get("why_relevant", "")).strip(),
339
+ }
340
+
341
+ items.append(item)
342
+
343
+ return items
344
+
345
+
346
+ def normalize_websearch_items(
347
+ items: List[Dict[str, Any]],
348
+ from_date: str,
349
+ to_date: str,
350
+ ) -> List[schema.WebSearchItem]:
351
+ """Convert parsed dicts to WebSearchItem objects.
352
+
353
+ Args:
354
+ items: List of parsed item dicts
355
+ from_date: Start of date range (YYYY-MM-DD)
356
+ to_date: End of date range (YYYY-MM-DD)
357
+
358
+ Returns:
359
+ List of WebSearchItem objects
360
+ """
361
+ result = []
362
+
363
+ for item in items:
364
+ web_item = schema.WebSearchItem(
365
+ id=item["id"],
366
+ title=item["title"],
367
+ url=item["url"],
368
+ source_domain=item["source_domain"],
369
+ snippet=item["snippet"],
370
+ date=item.get("date"),
371
+ date_confidence=item.get("date_confidence", "low"),
372
+ relevance=item.get("relevance", 0.5),
373
+ why_relevant=item.get("why_relevant", ""),
374
+ )
375
+ result.append(web_item)
376
+
377
+ return result
378
+
379
+
380
+ def dedupe_websearch(items: List[schema.WebSearchItem]) -> List[schema.WebSearchItem]:
381
+ """Remove duplicate WebSearch items.
382
+
383
+ Deduplication is based on URL.
384
+
385
+ Args:
386
+ items: List of WebSearchItem objects
387
+
388
+ Returns:
389
+ Deduplicated list
390
+ """
391
+ seen_urls = set()
392
+ result = []
393
+
394
+ for item in items:
395
+ # Normalize URL for comparison
396
+ url_key = item.url.lower().rstrip("/")
397
+ if url_key not in seen_urls:
398
+ seen_urls.add(url_key)
399
+ result.append(item)
400
+
401
+ return result
@@ -0,0 +1,217 @@
1
+ """xAI API client for X (Twitter) discovery."""
2
+
3
+ import json
4
+ import re
5
+ import sys
6
+ from typing import Any, Dict, List, Optional
7
+
8
+ from . import http
9
+
10
+
11
+ def _log_error(msg: str):
12
+ """Log error to stderr."""
13
+ sys.stderr.write(f"[X ERROR] {msg}\n")
14
+ sys.stderr.flush()
15
+
16
+ # xAI uses responses endpoint with Agent Tools API
17
+ XAI_RESPONSES_URL = "https://api.x.ai/v1/responses"
18
+
19
+ # Depth configurations: (min, max) posts to request
20
+ DEPTH_CONFIG = {
21
+ "quick": (8, 12),
22
+ "default": (20, 30),
23
+ "deep": (40, 60),
24
+ }
25
+
26
+ X_SEARCH_PROMPT = """You have access to real-time X (Twitter) data. Search for posts about: {topic}
27
+
28
+ Focus on posts from {from_date} to {to_date}. Find {min_items}-{max_items} high-quality, relevant posts.
29
+
30
+ IMPORTANT: Return ONLY valid JSON in this exact format, no other text:
31
+ {{
32
+ "items": [
33
+ {{
34
+ "text": "Post text content (truncated if long)",
35
+ "url": "https://x.com/user/status/...",
36
+ "author_handle": "username",
37
+ "date": "YYYY-MM-DD or null if unknown",
38
+ "engagement": {{
39
+ "likes": 100,
40
+ "reposts": 25,
41
+ "replies": 15,
42
+ "quotes": 5
43
+ }},
44
+ "why_relevant": "Brief explanation of relevance",
45
+ "relevance": 0.85
46
+ }}
47
+ ]
48
+ }}
49
+
50
+ Rules:
51
+ - relevance is 0.0 to 1.0 (1.0 = highly relevant)
52
+ - date must be YYYY-MM-DD format or null
53
+ - engagement can be null if unknown
54
+ - Include diverse voices/accounts if applicable
55
+ - Prefer posts with substantive content, not just links"""
56
+
57
+
58
+ def search_x(
59
+ api_key: str,
60
+ model: str,
61
+ topic: str,
62
+ from_date: str,
63
+ to_date: str,
64
+ depth: str = "default",
65
+ mock_response: Optional[Dict] = None,
66
+ ) -> Dict[str, Any]:
67
+ """Search X for relevant posts using xAI API with live search.
68
+
69
+ Args:
70
+ api_key: xAI API key
71
+ model: Model to use
72
+ topic: Search topic
73
+ from_date: Start date (YYYY-MM-DD)
74
+ to_date: End date (YYYY-MM-DD)
75
+ depth: Research depth - "quick", "default", or "deep"
76
+ mock_response: Mock response for testing
77
+
78
+ Returns:
79
+ Raw API response
80
+ """
81
+ if mock_response is not None:
82
+ return mock_response
83
+
84
+ min_items, max_items = DEPTH_CONFIG.get(depth, DEPTH_CONFIG["default"])
85
+
86
+ headers = {
87
+ "Authorization": f"Bearer {api_key}",
88
+ "Content-Type": "application/json",
89
+ }
90
+
91
+ # Adjust timeout based on depth (generous for API response time)
92
+ timeout = 90 if depth == "quick" else 120 if depth == "default" else 180
93
+
94
+ # Use Agent Tools API with x_search tool
95
+ payload = {
96
+ "model": model,
97
+ "tools": [
98
+ {"type": "x_search"}
99
+ ],
100
+ "input": [
101
+ {
102
+ "role": "user",
103
+ "content": X_SEARCH_PROMPT.format(
104
+ topic=topic,
105
+ from_date=from_date,
106
+ to_date=to_date,
107
+ min_items=min_items,
108
+ max_items=max_items,
109
+ ),
110
+ }
111
+ ],
112
+ }
113
+
114
+ return http.post(XAI_RESPONSES_URL, payload, headers=headers, timeout=timeout)
115
+
116
+
117
+ def parse_x_response(response: Dict[str, Any]) -> List[Dict[str, Any]]:
118
+ """Parse xAI response to extract X items.
119
+
120
+ Args:
121
+ response: Raw API response
122
+
123
+ Returns:
124
+ List of item dicts
125
+ """
126
+ items = []
127
+
128
+ # Check for API errors first
129
+ if "error" in response and response["error"]:
130
+ error = response["error"]
131
+ err_msg = error.get("message", str(error)) if isinstance(error, dict) else str(error)
132
+ _log_error(f"xAI API error: {err_msg}")
133
+ if http.DEBUG:
134
+ _log_error(f"Full error response: {json.dumps(response, indent=2)[:1000]}")
135
+ return items
136
+
137
+ # Try to find the output text
138
+ output_text = ""
139
+ if "output" in response:
140
+ output = response["output"]
141
+ if isinstance(output, str):
142
+ output_text = output
143
+ elif isinstance(output, list):
144
+ for item in output:
145
+ if isinstance(item, dict):
146
+ if item.get("type") == "message":
147
+ content = item.get("content", [])
148
+ for c in content:
149
+ if isinstance(c, dict) and c.get("type") == "output_text":
150
+ output_text = c.get("text", "")
151
+ break
152
+ elif "text" in item:
153
+ output_text = item["text"]
154
+ elif isinstance(item, str):
155
+ output_text = item
156
+ if output_text:
157
+ break
158
+
159
+ # Also check for choices (older format)
160
+ if not output_text and "choices" in response:
161
+ for choice in response["choices"]:
162
+ if "message" in choice:
163
+ output_text = choice["message"].get("content", "")
164
+ break
165
+
166
+ if not output_text:
167
+ return items
168
+
169
+ # Extract JSON from the response
170
+ json_match = re.search(r'\{[\s\S]*"items"[\s\S]*\}', output_text)
171
+ if json_match:
172
+ try:
173
+ data = json.loads(json_match.group())
174
+ items = data.get("items", [])
175
+ except json.JSONDecodeError:
176
+ pass
177
+
178
+ # Validate and clean items
179
+ clean_items = []
180
+ for i, item in enumerate(items):
181
+ if not isinstance(item, dict):
182
+ continue
183
+
184
+ url = item.get("url", "")
185
+ if not url:
186
+ continue
187
+
188
+ # Parse engagement
189
+ engagement = None
190
+ eng_raw = item.get("engagement")
191
+ if isinstance(eng_raw, dict):
192
+ engagement = {
193
+ "likes": int(eng_raw.get("likes", 0)) if eng_raw.get("likes") else None,
194
+ "reposts": int(eng_raw.get("reposts", 0)) if eng_raw.get("reposts") else None,
195
+ "replies": int(eng_raw.get("replies", 0)) if eng_raw.get("replies") else None,
196
+ "quotes": int(eng_raw.get("quotes", 0)) if eng_raw.get("quotes") else None,
197
+ }
198
+
199
+ clean_item = {
200
+ "id": f"X{i+1}",
201
+ "text": str(item.get("text", "")).strip()[:500], # Truncate long text
202
+ "url": url,
203
+ "author_handle": str(item.get("author_handle", "")).strip().lstrip("@"),
204
+ "date": item.get("date"),
205
+ "engagement": engagement,
206
+ "why_relevant": str(item.get("why_relevant", "")).strip(),
207
+ "relevance": min(1.0, max(0.0, float(item.get("relevance", 0.5)))),
208
+ }
209
+
210
+ # Validate date format
211
+ if clean_item["date"]:
212
+ if not re.match(r'^\d{4}-\d{2}-\d{2}$', str(clean_item["date"])):
213
+ clean_item["date"] = None
214
+
215
+ clean_items.append(clean_item)
216
+
217
+ return clean_items