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,336 @@
1
+ """Data schemas for last30days skill."""
2
+
3
+ from dataclasses import dataclass, field, asdict
4
+ from typing import Any, Dict, List, Optional
5
+ from datetime import datetime, timezone
6
+
7
+
8
+ @dataclass
9
+ class Engagement:
10
+ """Engagement metrics."""
11
+ # Reddit fields
12
+ score: Optional[int] = None
13
+ num_comments: Optional[int] = None
14
+ upvote_ratio: Optional[float] = None
15
+
16
+ # X fields
17
+ likes: Optional[int] = None
18
+ reposts: Optional[int] = None
19
+ replies: Optional[int] = None
20
+ quotes: Optional[int] = None
21
+
22
+ def to_dict(self) -> Dict[str, Any]:
23
+ d = {}
24
+ if self.score is not None:
25
+ d['score'] = self.score
26
+ if self.num_comments is not None:
27
+ d['num_comments'] = self.num_comments
28
+ if self.upvote_ratio is not None:
29
+ d['upvote_ratio'] = self.upvote_ratio
30
+ if self.likes is not None:
31
+ d['likes'] = self.likes
32
+ if self.reposts is not None:
33
+ d['reposts'] = self.reposts
34
+ if self.replies is not None:
35
+ d['replies'] = self.replies
36
+ if self.quotes is not None:
37
+ d['quotes'] = self.quotes
38
+ return d if d else None
39
+
40
+
41
+ @dataclass
42
+ class Comment:
43
+ """Reddit comment."""
44
+ score: int
45
+ date: Optional[str]
46
+ author: str
47
+ excerpt: str
48
+ url: str
49
+
50
+ def to_dict(self) -> Dict[str, Any]:
51
+ return {
52
+ 'score': self.score,
53
+ 'date': self.date,
54
+ 'author': self.author,
55
+ 'excerpt': self.excerpt,
56
+ 'url': self.url,
57
+ }
58
+
59
+
60
+ @dataclass
61
+ class SubScores:
62
+ """Component scores."""
63
+ relevance: int = 0
64
+ recency: int = 0
65
+ engagement: int = 0
66
+
67
+ def to_dict(self) -> Dict[str, int]:
68
+ return {
69
+ 'relevance': self.relevance,
70
+ 'recency': self.recency,
71
+ 'engagement': self.engagement,
72
+ }
73
+
74
+
75
+ @dataclass
76
+ class RedditItem:
77
+ """Normalized Reddit item."""
78
+ id: str
79
+ title: str
80
+ url: str
81
+ subreddit: str
82
+ date: Optional[str] = None
83
+ date_confidence: str = "low"
84
+ engagement: Optional[Engagement] = None
85
+ top_comments: List[Comment] = field(default_factory=list)
86
+ comment_insights: List[str] = field(default_factory=list)
87
+ relevance: float = 0.5
88
+ why_relevant: str = ""
89
+ subs: SubScores = field(default_factory=SubScores)
90
+ score: int = 0
91
+
92
+ def to_dict(self) -> Dict[str, Any]:
93
+ return {
94
+ 'id': self.id,
95
+ 'title': self.title,
96
+ 'url': self.url,
97
+ 'subreddit': self.subreddit,
98
+ 'date': self.date,
99
+ 'date_confidence': self.date_confidence,
100
+ 'engagement': self.engagement.to_dict() if self.engagement else None,
101
+ 'top_comments': [c.to_dict() for c in self.top_comments],
102
+ 'comment_insights': self.comment_insights,
103
+ 'relevance': self.relevance,
104
+ 'why_relevant': self.why_relevant,
105
+ 'subs': self.subs.to_dict(),
106
+ 'score': self.score,
107
+ }
108
+
109
+
110
+ @dataclass
111
+ class XItem:
112
+ """Normalized X item."""
113
+ id: str
114
+ text: str
115
+ url: str
116
+ author_handle: str
117
+ date: Optional[str] = None
118
+ date_confidence: str = "low"
119
+ engagement: Optional[Engagement] = None
120
+ relevance: float = 0.5
121
+ why_relevant: str = ""
122
+ subs: SubScores = field(default_factory=SubScores)
123
+ score: int = 0
124
+
125
+ def to_dict(self) -> Dict[str, Any]:
126
+ return {
127
+ 'id': self.id,
128
+ 'text': self.text,
129
+ 'url': self.url,
130
+ 'author_handle': self.author_handle,
131
+ 'date': self.date,
132
+ 'date_confidence': self.date_confidence,
133
+ 'engagement': self.engagement.to_dict() if self.engagement else None,
134
+ 'relevance': self.relevance,
135
+ 'why_relevant': self.why_relevant,
136
+ 'subs': self.subs.to_dict(),
137
+ 'score': self.score,
138
+ }
139
+
140
+
141
+ @dataclass
142
+ class WebSearchItem:
143
+ """Normalized web search item (no engagement metrics)."""
144
+ id: str
145
+ title: str
146
+ url: str
147
+ source_domain: str # e.g., "medium.com", "github.com"
148
+ snippet: str
149
+ date: Optional[str] = None
150
+ date_confidence: str = "low"
151
+ relevance: float = 0.5
152
+ why_relevant: str = ""
153
+ subs: SubScores = field(default_factory=SubScores)
154
+ score: int = 0
155
+
156
+ def to_dict(self) -> Dict[str, Any]:
157
+ return {
158
+ 'id': self.id,
159
+ 'title': self.title,
160
+ 'url': self.url,
161
+ 'source_domain': self.source_domain,
162
+ 'snippet': self.snippet,
163
+ 'date': self.date,
164
+ 'date_confidence': self.date_confidence,
165
+ 'relevance': self.relevance,
166
+ 'why_relevant': self.why_relevant,
167
+ 'subs': self.subs.to_dict(),
168
+ 'score': self.score,
169
+ }
170
+
171
+
172
+ @dataclass
173
+ class Report:
174
+ """Full research report."""
175
+ topic: str
176
+ range_from: str
177
+ range_to: str
178
+ generated_at: str
179
+ mode: str # 'reddit-only', 'x-only', 'both', 'web-only', etc.
180
+ openai_model_used: Optional[str] = None
181
+ xai_model_used: Optional[str] = None
182
+ reddit: List[RedditItem] = field(default_factory=list)
183
+ x: List[XItem] = field(default_factory=list)
184
+ web: List[WebSearchItem] = field(default_factory=list)
185
+ best_practices: List[str] = field(default_factory=list)
186
+ prompt_pack: List[str] = field(default_factory=list)
187
+ context_snippet_md: str = ""
188
+ # Status tracking
189
+ reddit_error: Optional[str] = None
190
+ x_error: Optional[str] = None
191
+ web_error: Optional[str] = None
192
+ # Cache info
193
+ from_cache: bool = False
194
+ cache_age_hours: Optional[float] = None
195
+
196
+ def to_dict(self) -> Dict[str, Any]:
197
+ d = {
198
+ 'topic': self.topic,
199
+ 'range': {
200
+ 'from': self.range_from,
201
+ 'to': self.range_to,
202
+ },
203
+ 'generated_at': self.generated_at,
204
+ 'mode': self.mode,
205
+ 'openai_model_used': self.openai_model_used,
206
+ 'xai_model_used': self.xai_model_used,
207
+ 'reddit': [r.to_dict() for r in self.reddit],
208
+ 'x': [x.to_dict() for x in self.x],
209
+ 'web': [w.to_dict() for w in self.web],
210
+ 'best_practices': self.best_practices,
211
+ 'prompt_pack': self.prompt_pack,
212
+ 'context_snippet_md': self.context_snippet_md,
213
+ }
214
+ if self.reddit_error:
215
+ d['reddit_error'] = self.reddit_error
216
+ if self.x_error:
217
+ d['x_error'] = self.x_error
218
+ if self.web_error:
219
+ d['web_error'] = self.web_error
220
+ if self.from_cache:
221
+ d['from_cache'] = self.from_cache
222
+ if self.cache_age_hours is not None:
223
+ d['cache_age_hours'] = self.cache_age_hours
224
+ return d
225
+
226
+ @classmethod
227
+ def from_dict(cls, data: Dict[str, Any]) -> "Report":
228
+ """Create Report from serialized dict (handles cache format)."""
229
+ # Handle range field conversion
230
+ range_data = data.get('range', {})
231
+ range_from = range_data.get('from', data.get('range_from', ''))
232
+ range_to = range_data.get('to', data.get('range_to', ''))
233
+
234
+ # Reconstruct Reddit items
235
+ reddit_items = []
236
+ for r in data.get('reddit', []):
237
+ eng = None
238
+ if r.get('engagement'):
239
+ eng = Engagement(**r['engagement'])
240
+ comments = [Comment(**c) for c in r.get('top_comments', [])]
241
+ subs = SubScores(**r.get('subs', {})) if r.get('subs') else SubScores()
242
+ reddit_items.append(RedditItem(
243
+ id=r['id'],
244
+ title=r['title'],
245
+ url=r['url'],
246
+ subreddit=r['subreddit'],
247
+ date=r.get('date'),
248
+ date_confidence=r.get('date_confidence', 'low'),
249
+ engagement=eng,
250
+ top_comments=comments,
251
+ comment_insights=r.get('comment_insights', []),
252
+ relevance=r.get('relevance', 0.5),
253
+ why_relevant=r.get('why_relevant', ''),
254
+ subs=subs,
255
+ score=r.get('score', 0),
256
+ ))
257
+
258
+ # Reconstruct X items
259
+ x_items = []
260
+ for x in data.get('x', []):
261
+ eng = None
262
+ if x.get('engagement'):
263
+ eng = Engagement(**x['engagement'])
264
+ subs = SubScores(**x.get('subs', {})) if x.get('subs') else SubScores()
265
+ x_items.append(XItem(
266
+ id=x['id'],
267
+ text=x['text'],
268
+ url=x['url'],
269
+ author_handle=x['author_handle'],
270
+ date=x.get('date'),
271
+ date_confidence=x.get('date_confidence', 'low'),
272
+ engagement=eng,
273
+ relevance=x.get('relevance', 0.5),
274
+ why_relevant=x.get('why_relevant', ''),
275
+ subs=subs,
276
+ score=x.get('score', 0),
277
+ ))
278
+
279
+ # Reconstruct Web items
280
+ web_items = []
281
+ for w in data.get('web', []):
282
+ subs = SubScores(**w.get('subs', {})) if w.get('subs') else SubScores()
283
+ web_items.append(WebSearchItem(
284
+ id=w['id'],
285
+ title=w['title'],
286
+ url=w['url'],
287
+ source_domain=w.get('source_domain', ''),
288
+ snippet=w.get('snippet', ''),
289
+ date=w.get('date'),
290
+ date_confidence=w.get('date_confidence', 'low'),
291
+ relevance=w.get('relevance', 0.5),
292
+ why_relevant=w.get('why_relevant', ''),
293
+ subs=subs,
294
+ score=w.get('score', 0),
295
+ ))
296
+
297
+ return cls(
298
+ topic=data['topic'],
299
+ range_from=range_from,
300
+ range_to=range_to,
301
+ generated_at=data['generated_at'],
302
+ mode=data['mode'],
303
+ openai_model_used=data.get('openai_model_used'),
304
+ xai_model_used=data.get('xai_model_used'),
305
+ reddit=reddit_items,
306
+ x=x_items,
307
+ web=web_items,
308
+ best_practices=data.get('best_practices', []),
309
+ prompt_pack=data.get('prompt_pack', []),
310
+ context_snippet_md=data.get('context_snippet_md', ''),
311
+ reddit_error=data.get('reddit_error'),
312
+ x_error=data.get('x_error'),
313
+ web_error=data.get('web_error'),
314
+ from_cache=data.get('from_cache', False),
315
+ cache_age_hours=data.get('cache_age_hours'),
316
+ )
317
+
318
+
319
+ def create_report(
320
+ topic: str,
321
+ from_date: str,
322
+ to_date: str,
323
+ mode: str,
324
+ openai_model: Optional[str] = None,
325
+ xai_model: Optional[str] = None,
326
+ ) -> Report:
327
+ """Create a new report with metadata."""
328
+ return Report(
329
+ topic=topic,
330
+ range_from=from_date,
331
+ range_to=to_date,
332
+ generated_at=datetime.now(timezone.utc).isoformat(),
333
+ mode=mode,
334
+ openai_model_used=openai_model,
335
+ xai_model_used=xai_model,
336
+ )
@@ -0,0 +1,311 @@
1
+ """Popularity-aware scoring for last30days skill."""
2
+
3
+ import math
4
+ from typing import List, Optional, Union
5
+
6
+ from . import dates, schema
7
+
8
+ # Score weights for Reddit/X (has engagement)
9
+ WEIGHT_RELEVANCE = 0.45
10
+ WEIGHT_RECENCY = 0.25
11
+ WEIGHT_ENGAGEMENT = 0.30
12
+
13
+ # WebSearch weights (no engagement, reweighted to 100%)
14
+ WEBSEARCH_WEIGHT_RELEVANCE = 0.55
15
+ WEBSEARCH_WEIGHT_RECENCY = 0.45
16
+ WEBSEARCH_SOURCE_PENALTY = 15 # Points deducted for lacking engagement
17
+
18
+ # WebSearch date confidence adjustments
19
+ WEBSEARCH_VERIFIED_BONUS = 10 # Bonus for URL-verified recent date (high confidence)
20
+ WEBSEARCH_NO_DATE_PENALTY = 20 # Heavy penalty for no date signals (low confidence)
21
+
22
+ # Default engagement score for unknown
23
+ DEFAULT_ENGAGEMENT = 35
24
+ UNKNOWN_ENGAGEMENT_PENALTY = 10
25
+
26
+
27
+ def log1p_safe(x: Optional[int]) -> float:
28
+ """Safe log1p that handles None and negative values."""
29
+ if x is None or x < 0:
30
+ return 0.0
31
+ return math.log1p(x)
32
+
33
+
34
+ def compute_reddit_engagement_raw(engagement: Optional[schema.Engagement]) -> Optional[float]:
35
+ """Compute raw engagement score for Reddit item.
36
+
37
+ Formula: 0.55*log1p(score) + 0.40*log1p(num_comments) + 0.05*(upvote_ratio*10)
38
+ """
39
+ if engagement is None:
40
+ return None
41
+
42
+ if engagement.score is None and engagement.num_comments is None:
43
+ return None
44
+
45
+ score = log1p_safe(engagement.score)
46
+ comments = log1p_safe(engagement.num_comments)
47
+ ratio = (engagement.upvote_ratio or 0.5) * 10
48
+
49
+ return 0.55 * score + 0.40 * comments + 0.05 * ratio
50
+
51
+
52
+ def compute_x_engagement_raw(engagement: Optional[schema.Engagement]) -> Optional[float]:
53
+ """Compute raw engagement score for X item.
54
+
55
+ Formula: 0.55*log1p(likes) + 0.25*log1p(reposts) + 0.15*log1p(replies) + 0.05*log1p(quotes)
56
+ """
57
+ if engagement is None:
58
+ return None
59
+
60
+ if engagement.likes is None and engagement.reposts is None:
61
+ return None
62
+
63
+ likes = log1p_safe(engagement.likes)
64
+ reposts = log1p_safe(engagement.reposts)
65
+ replies = log1p_safe(engagement.replies)
66
+ quotes = log1p_safe(engagement.quotes)
67
+
68
+ return 0.55 * likes + 0.25 * reposts + 0.15 * replies + 0.05 * quotes
69
+
70
+
71
+ def normalize_to_100(values: List[float], default: float = 50) -> List[float]:
72
+ """Normalize a list of values to 0-100 scale.
73
+
74
+ Args:
75
+ values: Raw values (None values are preserved)
76
+ default: Default value for None entries
77
+
78
+ Returns:
79
+ Normalized values
80
+ """
81
+ # Filter out None
82
+ valid = [v for v in values if v is not None]
83
+ if not valid:
84
+ return [default if v is None else 50 for v in values]
85
+
86
+ min_val = min(valid)
87
+ max_val = max(valid)
88
+ range_val = max_val - min_val
89
+
90
+ if range_val == 0:
91
+ return [50 if v is None else 50 for v in values]
92
+
93
+ result = []
94
+ for v in values:
95
+ if v is None:
96
+ result.append(None)
97
+ else:
98
+ normalized = ((v - min_val) / range_val) * 100
99
+ result.append(normalized)
100
+
101
+ return result
102
+
103
+
104
+ def score_reddit_items(items: List[schema.RedditItem]) -> List[schema.RedditItem]:
105
+ """Compute scores for Reddit items.
106
+
107
+ Args:
108
+ items: List of Reddit items
109
+
110
+ Returns:
111
+ Items with updated scores
112
+ """
113
+ if not items:
114
+ return items
115
+
116
+ # Compute raw engagement scores
117
+ eng_raw = [compute_reddit_engagement_raw(item.engagement) for item in items]
118
+
119
+ # Normalize engagement to 0-100
120
+ eng_normalized = normalize_to_100(eng_raw)
121
+
122
+ for i, item in enumerate(items):
123
+ # Relevance subscore (model-provided, convert to 0-100)
124
+ rel_score = int(item.relevance * 100)
125
+
126
+ # Recency subscore
127
+ rec_score = dates.recency_score(item.date)
128
+
129
+ # Engagement subscore
130
+ if eng_normalized[i] is not None:
131
+ eng_score = int(eng_normalized[i])
132
+ else:
133
+ eng_score = DEFAULT_ENGAGEMENT
134
+
135
+ # Store subscores
136
+ item.subs = schema.SubScores(
137
+ relevance=rel_score,
138
+ recency=rec_score,
139
+ engagement=eng_score,
140
+ )
141
+
142
+ # Compute overall score
143
+ overall = (
144
+ WEIGHT_RELEVANCE * rel_score +
145
+ WEIGHT_RECENCY * rec_score +
146
+ WEIGHT_ENGAGEMENT * eng_score
147
+ )
148
+
149
+ # Apply penalty for unknown engagement
150
+ if eng_raw[i] is None:
151
+ overall -= UNKNOWN_ENGAGEMENT_PENALTY
152
+
153
+ # Apply penalty for low date confidence
154
+ if item.date_confidence == "low":
155
+ overall -= 10
156
+ elif item.date_confidence == "med":
157
+ overall -= 5
158
+
159
+ item.score = max(0, min(100, int(overall)))
160
+
161
+ return items
162
+
163
+
164
+ def score_x_items(items: List[schema.XItem]) -> List[schema.XItem]:
165
+ """Compute scores for X items.
166
+
167
+ Args:
168
+ items: List of X items
169
+
170
+ Returns:
171
+ Items with updated scores
172
+ """
173
+ if not items:
174
+ return items
175
+
176
+ # Compute raw engagement scores
177
+ eng_raw = [compute_x_engagement_raw(item.engagement) for item in items]
178
+
179
+ # Normalize engagement to 0-100
180
+ eng_normalized = normalize_to_100(eng_raw)
181
+
182
+ for i, item in enumerate(items):
183
+ # Relevance subscore (model-provided, convert to 0-100)
184
+ rel_score = int(item.relevance * 100)
185
+
186
+ # Recency subscore
187
+ rec_score = dates.recency_score(item.date)
188
+
189
+ # Engagement subscore
190
+ if eng_normalized[i] is not None:
191
+ eng_score = int(eng_normalized[i])
192
+ else:
193
+ eng_score = DEFAULT_ENGAGEMENT
194
+
195
+ # Store subscores
196
+ item.subs = schema.SubScores(
197
+ relevance=rel_score,
198
+ recency=rec_score,
199
+ engagement=eng_score,
200
+ )
201
+
202
+ # Compute overall score
203
+ overall = (
204
+ WEIGHT_RELEVANCE * rel_score +
205
+ WEIGHT_RECENCY * rec_score +
206
+ WEIGHT_ENGAGEMENT * eng_score
207
+ )
208
+
209
+ # Apply penalty for unknown engagement
210
+ if eng_raw[i] is None:
211
+ overall -= UNKNOWN_ENGAGEMENT_PENALTY
212
+
213
+ # Apply penalty for low date confidence
214
+ if item.date_confidence == "low":
215
+ overall -= 10
216
+ elif item.date_confidence == "med":
217
+ overall -= 5
218
+
219
+ item.score = max(0, min(100, int(overall)))
220
+
221
+ return items
222
+
223
+
224
+ def score_websearch_items(items: List[schema.WebSearchItem]) -> List[schema.WebSearchItem]:
225
+ """Compute scores for WebSearch items WITHOUT engagement metrics.
226
+
227
+ Uses reweighted formula: 55% relevance + 45% recency - 15pt source penalty.
228
+ This ensures WebSearch items rank below comparable Reddit/X items.
229
+
230
+ Date confidence adjustments:
231
+ - High confidence (URL-verified date): +10 bonus
232
+ - Med confidence (snippet-extracted date): no change
233
+ - Low confidence (no date signals): -20 penalty
234
+
235
+ Args:
236
+ items: List of WebSearch items
237
+
238
+ Returns:
239
+ Items with updated scores
240
+ """
241
+ if not items:
242
+ return items
243
+
244
+ for item in items:
245
+ # Relevance subscore (model-provided, convert to 0-100)
246
+ rel_score = int(item.relevance * 100)
247
+
248
+ # Recency subscore
249
+ rec_score = dates.recency_score(item.date)
250
+
251
+ # Store subscores (engagement is 0 for WebSearch - no data)
252
+ item.subs = schema.SubScores(
253
+ relevance=rel_score,
254
+ recency=rec_score,
255
+ engagement=0, # Explicitly zero - no engagement data available
256
+ )
257
+
258
+ # Compute overall score using WebSearch weights
259
+ overall = (
260
+ WEBSEARCH_WEIGHT_RELEVANCE * rel_score +
261
+ WEBSEARCH_WEIGHT_RECENCY * rec_score
262
+ )
263
+
264
+ # Apply source penalty (WebSearch < Reddit/X for same relevance/recency)
265
+ overall -= WEBSEARCH_SOURCE_PENALTY
266
+
267
+ # Apply date confidence adjustments
268
+ # High confidence (URL-verified): reward with bonus
269
+ # Med confidence (snippet-extracted): neutral
270
+ # Low confidence (no date signals): heavy penalty
271
+ if item.date_confidence == "high":
272
+ overall += WEBSEARCH_VERIFIED_BONUS # Reward verified recent dates
273
+ elif item.date_confidence == "low":
274
+ overall -= WEBSEARCH_NO_DATE_PENALTY # Heavy penalty for unknown
275
+
276
+ item.score = max(0, min(100, int(overall)))
277
+
278
+ return items
279
+
280
+
281
+ def sort_items(items: List[Union[schema.RedditItem, schema.XItem, schema.WebSearchItem]]) -> List:
282
+ """Sort items by score (descending), then date, then source priority.
283
+
284
+ Args:
285
+ items: List of items to sort
286
+
287
+ Returns:
288
+ Sorted items
289
+ """
290
+ def sort_key(item):
291
+ # Primary: score descending (negate for descending)
292
+ score = -item.score
293
+
294
+ # Secondary: date descending (recent first)
295
+ date = item.date or "0000-00-00"
296
+ date_key = -int(date.replace("-", ""))
297
+
298
+ # Tertiary: source priority (Reddit > X > WebSearch)
299
+ if isinstance(item, schema.RedditItem):
300
+ source_priority = 0
301
+ elif isinstance(item, schema.XItem):
302
+ source_priority = 1
303
+ else: # WebSearchItem
304
+ source_priority = 2
305
+
306
+ # Quaternary: title/text for stability
307
+ text = getattr(item, "title", "") or getattr(item, "text", "")
308
+
309
+ return (score, date_key, source_priority, text)
310
+
311
+ return sorted(items, key=sort_key)