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,175 @@
1
+ """Model auto-selection for last30days skill."""
2
+
3
+ import re
4
+ from typing import Dict, List, Optional, Tuple
5
+
6
+ from . import cache, http
7
+
8
+ # OpenAI API
9
+ OPENAI_MODELS_URL = "https://api.openai.com/v1/models"
10
+ OPENAI_FALLBACK_MODELS = ["gpt-5.2", "gpt-5.1", "gpt-5", "gpt-4o"]
11
+
12
+ # xAI API - Agent Tools API requires grok-4 family
13
+ XAI_MODELS_URL = "https://api.x.ai/v1/models"
14
+ XAI_ALIASES = {
15
+ "latest": "grok-4-1-fast", # Required for x_search tool
16
+ "stable": "grok-4-1-fast",
17
+ }
18
+
19
+
20
+ def parse_version(model_id: str) -> Optional[Tuple[int, ...]]:
21
+ """Parse semantic version from model ID.
22
+
23
+ Examples:
24
+ gpt-5 -> (5,)
25
+ gpt-5.2 -> (5, 2)
26
+ gpt-5.2.1 -> (5, 2, 1)
27
+ """
28
+ match = re.search(r'(\d+(?:\.\d+)*)', model_id)
29
+ if match:
30
+ return tuple(int(x) for x in match.group(1).split('.'))
31
+ return None
32
+
33
+
34
+ def is_mainline_openai_model(model_id: str) -> bool:
35
+ """Check if model is a mainline GPT model (not mini/nano/chat/codex/pro)."""
36
+ model_lower = model_id.lower()
37
+
38
+ # Must be gpt-5 series
39
+ if not re.match(r'^gpt-5(\.\d+)*$', model_lower):
40
+ return False
41
+
42
+ # Exclude variants
43
+ excludes = ['mini', 'nano', 'chat', 'codex', 'pro', 'preview', 'turbo']
44
+ for exc in excludes:
45
+ if exc in model_lower:
46
+ return False
47
+
48
+ return True
49
+
50
+
51
+ def select_openai_model(
52
+ api_key: str,
53
+ policy: str = "auto",
54
+ pin: Optional[str] = None,
55
+ mock_models: Optional[List[Dict]] = None,
56
+ ) -> str:
57
+ """Select the best OpenAI model based on policy.
58
+
59
+ Args:
60
+ api_key: OpenAI API key
61
+ policy: 'auto' or 'pinned'
62
+ pin: Model to use if policy is 'pinned'
63
+ mock_models: Mock model list for testing
64
+
65
+ Returns:
66
+ Selected model ID
67
+ """
68
+ if policy == "pinned" and pin:
69
+ return pin
70
+
71
+ # Check cache first
72
+ cached = cache.get_cached_model("openai")
73
+ if cached:
74
+ return cached
75
+
76
+ # Fetch model list
77
+ if mock_models is not None:
78
+ models = mock_models
79
+ else:
80
+ try:
81
+ headers = {"Authorization": f"Bearer {api_key}"}
82
+ response = http.get(OPENAI_MODELS_URL, headers=headers)
83
+ models = response.get("data", [])
84
+ except http.HTTPError:
85
+ # Fall back to known models
86
+ return OPENAI_FALLBACK_MODELS[0]
87
+
88
+ # Filter to mainline models
89
+ candidates = [m for m in models if is_mainline_openai_model(m.get("id", ""))]
90
+
91
+ if not candidates:
92
+ # No gpt-5 models found, use fallback
93
+ return OPENAI_FALLBACK_MODELS[0]
94
+
95
+ # Sort by version (descending), then by created timestamp
96
+ def sort_key(m):
97
+ version = parse_version(m.get("id", "")) or (0,)
98
+ created = m.get("created", 0)
99
+ return (version, created)
100
+
101
+ candidates.sort(key=sort_key, reverse=True)
102
+ selected = candidates[0]["id"]
103
+
104
+ # Cache the selection
105
+ cache.set_cached_model("openai", selected)
106
+
107
+ return selected
108
+
109
+
110
+ def select_xai_model(
111
+ api_key: str,
112
+ policy: str = "latest",
113
+ pin: Optional[str] = None,
114
+ mock_models: Optional[List[Dict]] = None,
115
+ ) -> str:
116
+ """Select the best xAI model based on policy.
117
+
118
+ Args:
119
+ api_key: xAI API key
120
+ policy: 'latest', 'stable', or 'pinned'
121
+ pin: Model to use if policy is 'pinned'
122
+ mock_models: Mock model list for testing
123
+
124
+ Returns:
125
+ Selected model ID
126
+ """
127
+ if policy == "pinned" and pin:
128
+ return pin
129
+
130
+ # Use alias system
131
+ if policy in XAI_ALIASES:
132
+ alias = XAI_ALIASES[policy]
133
+
134
+ # Check cache first
135
+ cached = cache.get_cached_model("xai")
136
+ if cached:
137
+ return cached
138
+
139
+ # Cache the alias
140
+ cache.set_cached_model("xai", alias)
141
+ return alias
142
+
143
+ # Default to latest
144
+ return XAI_ALIASES["latest"]
145
+
146
+
147
+ def get_models(
148
+ config: Dict,
149
+ mock_openai_models: Optional[List[Dict]] = None,
150
+ mock_xai_models: Optional[List[Dict]] = None,
151
+ ) -> Dict[str, Optional[str]]:
152
+ """Get selected models for both providers.
153
+
154
+ Returns:
155
+ Dict with 'openai' and 'xai' keys
156
+ """
157
+ result = {"openai": None, "xai": None}
158
+
159
+ if config.get("OPENAI_API_KEY"):
160
+ result["openai"] = select_openai_model(
161
+ config["OPENAI_API_KEY"],
162
+ config.get("OPENAI_MODEL_POLICY", "auto"),
163
+ config.get("OPENAI_MODEL_PIN"),
164
+ mock_openai_models,
165
+ )
166
+
167
+ if config.get("XAI_API_KEY"):
168
+ result["xai"] = select_xai_model(
169
+ config["XAI_API_KEY"],
170
+ config.get("XAI_MODEL_POLICY", "latest"),
171
+ config.get("XAI_MODEL_PIN"),
172
+ mock_xai_models,
173
+ )
174
+
175
+ return result
@@ -0,0 +1,160 @@
1
+ """Normalization of raw API data to canonical schema."""
2
+
3
+ from typing import Any, Dict, List, TypeVar, Union
4
+
5
+ from . import dates, schema
6
+
7
+ T = TypeVar("T", schema.RedditItem, schema.XItem, schema.WebSearchItem)
8
+
9
+
10
+ def filter_by_date_range(
11
+ items: List[T],
12
+ from_date: str,
13
+ to_date: str,
14
+ require_date: bool = False,
15
+ ) -> List[T]:
16
+ """Hard filter: Remove items outside the date range.
17
+
18
+ This is the safety net - even if the prompt lets old content through,
19
+ this filter will exclude it.
20
+
21
+ Args:
22
+ items: List of items to filter
23
+ from_date: Start date (YYYY-MM-DD) - exclude items before this
24
+ to_date: End date (YYYY-MM-DD) - exclude items after this
25
+ require_date: If True, also remove items with no date
26
+
27
+ Returns:
28
+ Filtered list with only items in range (or unknown dates if not required)
29
+ """
30
+ result = []
31
+ for item in items:
32
+ if item.date is None:
33
+ if not require_date:
34
+ result.append(item) # Keep unknown dates (with scoring penalty)
35
+ continue
36
+
37
+ # Hard filter: if date is before from_date, exclude
38
+ if item.date < from_date:
39
+ continue # DROP - too old
40
+
41
+ # Hard filter: if date is after to_date, exclude (likely parsing error)
42
+ if item.date > to_date:
43
+ continue # DROP - future date
44
+
45
+ result.append(item)
46
+
47
+ return result
48
+
49
+
50
+ def normalize_reddit_items(
51
+ items: List[Dict[str, Any]],
52
+ from_date: str,
53
+ to_date: str,
54
+ ) -> List[schema.RedditItem]:
55
+ """Normalize raw Reddit items to schema.
56
+
57
+ Args:
58
+ items: Raw Reddit items from API
59
+ from_date: Start of date range
60
+ to_date: End of date range
61
+
62
+ Returns:
63
+ List of RedditItem objects
64
+ """
65
+ normalized = []
66
+
67
+ for item in items:
68
+ # Parse engagement
69
+ engagement = None
70
+ eng_raw = item.get("engagement")
71
+ if isinstance(eng_raw, dict):
72
+ engagement = schema.Engagement(
73
+ score=eng_raw.get("score"),
74
+ num_comments=eng_raw.get("num_comments"),
75
+ upvote_ratio=eng_raw.get("upvote_ratio"),
76
+ )
77
+
78
+ # Parse comments
79
+ top_comments = []
80
+ for c in item.get("top_comments", []):
81
+ top_comments.append(schema.Comment(
82
+ score=c.get("score", 0),
83
+ date=c.get("date"),
84
+ author=c.get("author", ""),
85
+ excerpt=c.get("excerpt", ""),
86
+ url=c.get("url", ""),
87
+ ))
88
+
89
+ # Determine date confidence
90
+ date_str = item.get("date")
91
+ date_confidence = dates.get_date_confidence(date_str, from_date, to_date)
92
+
93
+ normalized.append(schema.RedditItem(
94
+ id=item.get("id", ""),
95
+ title=item.get("title", ""),
96
+ url=item.get("url", ""),
97
+ subreddit=item.get("subreddit", ""),
98
+ date=date_str,
99
+ date_confidence=date_confidence,
100
+ engagement=engagement,
101
+ top_comments=top_comments,
102
+ comment_insights=item.get("comment_insights", []),
103
+ relevance=item.get("relevance", 0.5),
104
+ why_relevant=item.get("why_relevant", ""),
105
+ ))
106
+
107
+ return normalized
108
+
109
+
110
+ def normalize_x_items(
111
+ items: List[Dict[str, Any]],
112
+ from_date: str,
113
+ to_date: str,
114
+ ) -> List[schema.XItem]:
115
+ """Normalize raw X items to schema.
116
+
117
+ Args:
118
+ items: Raw X items from API
119
+ from_date: Start of date range
120
+ to_date: End of date range
121
+
122
+ Returns:
123
+ List of XItem objects
124
+ """
125
+ normalized = []
126
+
127
+ for item in items:
128
+ # Parse engagement
129
+ engagement = None
130
+ eng_raw = item.get("engagement")
131
+ if isinstance(eng_raw, dict):
132
+ engagement = schema.Engagement(
133
+ likes=eng_raw.get("likes"),
134
+ reposts=eng_raw.get("reposts"),
135
+ replies=eng_raw.get("replies"),
136
+ quotes=eng_raw.get("quotes"),
137
+ )
138
+
139
+ # Determine date confidence
140
+ date_str = item.get("date")
141
+ date_confidence = dates.get_date_confidence(date_str, from_date, to_date)
142
+
143
+ normalized.append(schema.XItem(
144
+ id=item.get("id", ""),
145
+ text=item.get("text", ""),
146
+ url=item.get("url", ""),
147
+ author_handle=item.get("author_handle", ""),
148
+ date=date_str,
149
+ date_confidence=date_confidence,
150
+ engagement=engagement,
151
+ relevance=item.get("relevance", 0.5),
152
+ why_relevant=item.get("why_relevant", ""),
153
+ ))
154
+
155
+ return normalized
156
+
157
+
158
+ def items_to_dicts(items: List) -> List[Dict[str, Any]]:
159
+ """Convert schema items to dicts for JSON serialization."""
160
+ return [item.to_dict() for item in items]
@@ -0,0 +1,230 @@
1
+ """OpenAI Responses API client for Reddit 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"[REDDIT ERROR] {msg}\n")
14
+ sys.stderr.flush()
15
+
16
+ OPENAI_RESPONSES_URL = "https://api.openai.com/v1/responses"
17
+
18
+ # Depth configurations: (min, max) threads to request
19
+ # Request MORE than needed since many get filtered by date
20
+ DEPTH_CONFIG = {
21
+ "quick": (15, 25),
22
+ "default": (30, 50),
23
+ "deep": (70, 100),
24
+ }
25
+
26
+ REDDIT_SEARCH_PROMPT = """Find Reddit discussion threads about: {topic}
27
+
28
+ STEP 1: EXTRACT THE CORE SUBJECT
29
+ Get the MAIN NOUN/PRODUCT/TOPIC:
30
+ - "best nano banana prompting practices" → "nano banana"
31
+ - "killer features of clawdbot" → "clawdbot"
32
+ - "top Claude Code skills" → "Claude Code"
33
+ DO NOT include "best", "top", "tips", "practices", "features" in your search.
34
+
35
+ STEP 2: SEARCH BROADLY
36
+ Search for the core subject:
37
+ 1. "[core subject] site:reddit.com"
38
+ 2. "reddit [core subject]"
39
+ 3. "[core subject] reddit"
40
+
41
+ Return as many relevant threads as you find. We filter by date server-side.
42
+
43
+ STEP 3: INCLUDE ALL MATCHES
44
+ - Include ALL threads about the core subject
45
+ - Set date to "YYYY-MM-DD" if you can determine it, otherwise null
46
+ - We verify dates and filter old content server-side
47
+ - DO NOT pre-filter aggressively - include anything relevant
48
+
49
+ REQUIRED: URLs must contain "/r/" AND "/comments/"
50
+ REJECT: developers.reddit.com, business.reddit.com
51
+
52
+ Find {min_items}-{max_items} threads. Return MORE rather than fewer.
53
+
54
+ Return JSON:
55
+ {{
56
+ "items": [
57
+ {{
58
+ "title": "Thread title",
59
+ "url": "https://www.reddit.com/r/sub/comments/xyz/title/",
60
+ "subreddit": "subreddit_name",
61
+ "date": "YYYY-MM-DD or null",
62
+ "why_relevant": "Why relevant",
63
+ "relevance": 0.85
64
+ }}
65
+ ]
66
+ }}"""
67
+
68
+
69
+ def _extract_core_subject(topic: str) -> str:
70
+ """Extract core subject from verbose query for retry."""
71
+ noise = ['best', 'top', 'how to', 'tips for', 'practices', 'features',
72
+ 'killer', 'guide', 'tutorial', 'recommendations', 'advice',
73
+ 'prompting', 'using', 'for', 'with', 'the', 'of', 'in', 'on']
74
+ words = topic.lower().split()
75
+ result = [w for w in words if w not in noise]
76
+ return ' '.join(result[:3]) or topic # Keep max 3 words
77
+
78
+
79
+ def search_reddit(
80
+ api_key: str,
81
+ model: str,
82
+ topic: str,
83
+ from_date: str,
84
+ to_date: str,
85
+ depth: str = "default",
86
+ mock_response: Optional[Dict] = None,
87
+ _retry: bool = False,
88
+ ) -> Dict[str, Any]:
89
+ """Search Reddit for relevant threads using OpenAI Responses API.
90
+
91
+ Args:
92
+ api_key: OpenAI API key
93
+ model: Model to use
94
+ topic: Search topic
95
+ from_date: Start date (YYYY-MM-DD) - only include threads after this
96
+ to_date: End date (YYYY-MM-DD) - only include threads before this
97
+ depth: Research depth - "quick", "default", or "deep"
98
+ mock_response: Mock response for testing
99
+
100
+ Returns:
101
+ Raw API response
102
+ """
103
+ if mock_response is not None:
104
+ return mock_response
105
+
106
+ min_items, max_items = DEPTH_CONFIG.get(depth, DEPTH_CONFIG["default"])
107
+
108
+ headers = {
109
+ "Authorization": f"Bearer {api_key}",
110
+ "Content-Type": "application/json",
111
+ }
112
+
113
+ # Adjust timeout based on depth (generous for OpenAI web_search which can be slow)
114
+ timeout = 90 if depth == "quick" else 120 if depth == "default" else 180
115
+
116
+ # Note: allowed_domains accepts base domain, not subdomains
117
+ # We rely on prompt to filter out developers.reddit.com, etc.
118
+ payload = {
119
+ "model": model,
120
+ "tools": [
121
+ {
122
+ "type": "web_search",
123
+ "filters": {
124
+ "allowed_domains": ["reddit.com"]
125
+ }
126
+ }
127
+ ],
128
+ "include": ["web_search_call.action.sources"],
129
+ "input": REDDIT_SEARCH_PROMPT.format(
130
+ topic=topic,
131
+ from_date=from_date,
132
+ to_date=to_date,
133
+ min_items=min_items,
134
+ max_items=max_items,
135
+ ),
136
+ }
137
+
138
+ return http.post(OPENAI_RESPONSES_URL, payload, headers=headers, timeout=timeout)
139
+
140
+
141
+ def parse_reddit_response(response: Dict[str, Any]) -> List[Dict[str, Any]]:
142
+ """Parse OpenAI response to extract Reddit items.
143
+
144
+ Args:
145
+ response: Raw API response
146
+
147
+ Returns:
148
+ List of item dicts
149
+ """
150
+ items = []
151
+
152
+ # Check for API errors first
153
+ if "error" in response and response["error"]:
154
+ error = response["error"]
155
+ err_msg = error.get("message", str(error)) if isinstance(error, dict) else str(error)
156
+ _log_error(f"OpenAI API error: {err_msg}")
157
+ if http.DEBUG:
158
+ _log_error(f"Full error response: {json.dumps(response, indent=2)[:1000]}")
159
+ return items
160
+
161
+ # Try to find the output text
162
+ output_text = ""
163
+ if "output" in response:
164
+ output = response["output"]
165
+ if isinstance(output, str):
166
+ output_text = output
167
+ elif isinstance(output, list):
168
+ for item in output:
169
+ if isinstance(item, dict):
170
+ if item.get("type") == "message":
171
+ content = item.get("content", [])
172
+ for c in content:
173
+ if isinstance(c, dict) and c.get("type") == "output_text":
174
+ output_text = c.get("text", "")
175
+ break
176
+ elif "text" in item:
177
+ output_text = item["text"]
178
+ elif isinstance(item, str):
179
+ output_text = item
180
+ if output_text:
181
+ break
182
+
183
+ # Also check for choices (older format)
184
+ if not output_text and "choices" in response:
185
+ for choice in response["choices"]:
186
+ if "message" in choice:
187
+ output_text = choice["message"].get("content", "")
188
+ break
189
+
190
+ if not output_text:
191
+ print(f"[REDDIT WARNING] No output text found in OpenAI response. Keys present: {list(response.keys())}", flush=True)
192
+ return items
193
+
194
+ # Extract JSON from the response
195
+ json_match = re.search(r'\{[\s\S]*"items"[\s\S]*\}', output_text)
196
+ if json_match:
197
+ try:
198
+ data = json.loads(json_match.group())
199
+ items = data.get("items", [])
200
+ except json.JSONDecodeError:
201
+ pass
202
+
203
+ # Validate and clean items
204
+ clean_items = []
205
+ for i, item in enumerate(items):
206
+ if not isinstance(item, dict):
207
+ continue
208
+
209
+ url = item.get("url", "")
210
+ if not url or "reddit.com" not in url:
211
+ continue
212
+
213
+ clean_item = {
214
+ "id": f"R{i+1}",
215
+ "title": str(item.get("title", "")).strip(),
216
+ "url": url,
217
+ "subreddit": str(item.get("subreddit", "")).strip().lstrip("r/"),
218
+ "date": item.get("date"),
219
+ "why_relevant": str(item.get("why_relevant", "")).strip(),
220
+ "relevance": min(1.0, max(0.0, float(item.get("relevance", 0.5)))),
221
+ }
222
+
223
+ # Validate date format
224
+ if clean_item["date"]:
225
+ if not re.match(r'^\d{4}-\d{2}-\d{2}$', str(clean_item["date"])):
226
+ clean_item["date"] = None
227
+
228
+ clean_items.append(clean_item)
229
+
230
+ return clean_items