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,124 @@
1
+ """Date utilities for last30days skill."""
2
+
3
+ from datetime import datetime, timedelta, timezone
4
+ from typing import Optional, Tuple
5
+
6
+
7
+ def get_date_range(days: int = 30) -> Tuple[str, str]:
8
+ """Get the date range for the last N days.
9
+
10
+ Returns:
11
+ Tuple of (from_date, to_date) as YYYY-MM-DD strings
12
+ """
13
+ today = datetime.now(timezone.utc).date()
14
+ from_date = today - timedelta(days=days)
15
+ return from_date.isoformat(), today.isoformat()
16
+
17
+
18
+ def parse_date(date_str: Optional[str]) -> Optional[datetime]:
19
+ """Parse a date string in various formats.
20
+
21
+ Supports: YYYY-MM-DD, ISO 8601, Unix timestamp
22
+ """
23
+ if not date_str:
24
+ return None
25
+
26
+ # Try Unix timestamp (from Reddit)
27
+ try:
28
+ ts = float(date_str)
29
+ return datetime.fromtimestamp(ts, tz=timezone.utc)
30
+ except (ValueError, TypeError):
31
+ pass
32
+
33
+ # Try ISO formats
34
+ formats = [
35
+ "%Y-%m-%d",
36
+ "%Y-%m-%dT%H:%M:%S",
37
+ "%Y-%m-%dT%H:%M:%SZ",
38
+ "%Y-%m-%dT%H:%M:%S%z",
39
+ "%Y-%m-%dT%H:%M:%S.%f%z",
40
+ ]
41
+
42
+ for fmt in formats:
43
+ try:
44
+ return datetime.strptime(date_str, fmt).replace(tzinfo=timezone.utc)
45
+ except ValueError:
46
+ continue
47
+
48
+ return None
49
+
50
+
51
+ def timestamp_to_date(ts: Optional[float]) -> Optional[str]:
52
+ """Convert Unix timestamp to YYYY-MM-DD string."""
53
+ if ts is None:
54
+ return None
55
+ try:
56
+ dt = datetime.fromtimestamp(ts, tz=timezone.utc)
57
+ return dt.date().isoformat()
58
+ except (ValueError, TypeError, OSError):
59
+ return None
60
+
61
+
62
+ def get_date_confidence(date_str: Optional[str], from_date: str, to_date: str) -> str:
63
+ """Determine confidence level for a date.
64
+
65
+ Args:
66
+ date_str: The date to check (YYYY-MM-DD or None)
67
+ from_date: Start of valid range (YYYY-MM-DD)
68
+ to_date: End of valid range (YYYY-MM-DD)
69
+
70
+ Returns:
71
+ 'high', 'med', or 'low'
72
+ """
73
+ if not date_str:
74
+ return 'low'
75
+
76
+ try:
77
+ dt = datetime.strptime(date_str, "%Y-%m-%d").date()
78
+ start = datetime.strptime(from_date, "%Y-%m-%d").date()
79
+ end = datetime.strptime(to_date, "%Y-%m-%d").date()
80
+
81
+ if start <= dt <= end:
82
+ return 'high'
83
+ elif dt < start:
84
+ # Older than range
85
+ return 'low'
86
+ else:
87
+ # Future date (suspicious)
88
+ return 'low'
89
+ except ValueError:
90
+ return 'low'
91
+
92
+
93
+ def days_ago(date_str: Optional[str]) -> Optional[int]:
94
+ """Calculate how many days ago a date is.
95
+
96
+ Returns None if date is invalid or missing.
97
+ """
98
+ if not date_str:
99
+ return None
100
+
101
+ try:
102
+ dt = datetime.strptime(date_str, "%Y-%m-%d").date()
103
+ today = datetime.now(timezone.utc).date()
104
+ delta = today - dt
105
+ return delta.days
106
+ except ValueError:
107
+ return None
108
+
109
+
110
+ def recency_score(date_str: Optional[str], max_days: int = 30) -> int:
111
+ """Calculate recency score (0-100).
112
+
113
+ 0 days ago = 100, max_days ago = 0, clamped.
114
+ """
115
+ age = days_ago(date_str)
116
+ if age is None:
117
+ return 0 # Unknown date gets worst score
118
+
119
+ if age < 0:
120
+ return 100 # Future date (treat as today)
121
+ if age >= max_days:
122
+ return 0
123
+
124
+ return int(100 * (1 - age / max_days))
@@ -0,0 +1,120 @@
1
+ """Near-duplicate detection for last30days skill."""
2
+
3
+ import re
4
+ from typing import List, Set, Tuple, Union
5
+
6
+ from . import schema
7
+
8
+
9
+ def normalize_text(text: str) -> str:
10
+ """Normalize text for comparison.
11
+
12
+ - Lowercase
13
+ - Remove punctuation
14
+ - Collapse whitespace
15
+ """
16
+ text = text.lower()
17
+ text = re.sub(r'[^\w\s]', ' ', text)
18
+ text = re.sub(r'\s+', ' ', text)
19
+ return text.strip()
20
+
21
+
22
+ def get_ngrams(text: str, n: int = 3) -> Set[str]:
23
+ """Get character n-grams from text."""
24
+ text = normalize_text(text)
25
+ if len(text) < n:
26
+ return {text}
27
+ return {text[i:i+n] for i in range(len(text) - n + 1)}
28
+
29
+
30
+ def jaccard_similarity(set1: Set[str], set2: Set[str]) -> float:
31
+ """Compute Jaccard similarity between two sets."""
32
+ if not set1 or not set2:
33
+ return 0.0
34
+ intersection = len(set1 & set2)
35
+ union = len(set1 | set2)
36
+ return intersection / union if union > 0 else 0.0
37
+
38
+
39
+ def get_item_text(item: Union[schema.RedditItem, schema.XItem]) -> str:
40
+ """Get comparable text from an item."""
41
+ if isinstance(item, schema.RedditItem):
42
+ return item.title
43
+ else:
44
+ return item.text
45
+
46
+
47
+ def find_duplicates(
48
+ items: List[Union[schema.RedditItem, schema.XItem]],
49
+ threshold: float = 0.7,
50
+ ) -> List[Tuple[int, int]]:
51
+ """Find near-duplicate pairs in items.
52
+
53
+ Args:
54
+ items: List of items to check
55
+ threshold: Similarity threshold (0-1)
56
+
57
+ Returns:
58
+ List of (i, j) index pairs where i < j and items are similar
59
+ """
60
+ duplicates = []
61
+
62
+ # Pre-compute n-grams
63
+ ngrams = [get_ngrams(get_item_text(item)) for item in items]
64
+
65
+ for i in range(len(items)):
66
+ for j in range(i + 1, len(items)):
67
+ similarity = jaccard_similarity(ngrams[i], ngrams[j])
68
+ if similarity >= threshold:
69
+ duplicates.append((i, j))
70
+
71
+ return duplicates
72
+
73
+
74
+ def dedupe_items(
75
+ items: List[Union[schema.RedditItem, schema.XItem]],
76
+ threshold: float = 0.7,
77
+ ) -> List[Union[schema.RedditItem, schema.XItem]]:
78
+ """Remove near-duplicates, keeping highest-scored item.
79
+
80
+ Args:
81
+ items: List of items (should be pre-sorted by score descending)
82
+ threshold: Similarity threshold
83
+
84
+ Returns:
85
+ Deduplicated items
86
+ """
87
+ if len(items) <= 1:
88
+ return items
89
+
90
+ # Find duplicate pairs
91
+ dup_pairs = find_duplicates(items, threshold)
92
+
93
+ # Mark indices to remove (always remove the lower-scored one)
94
+ # Since items are pre-sorted by score, the second index is always lower
95
+ to_remove = set()
96
+ for i, j in dup_pairs:
97
+ # Keep the higher-scored one (lower index in sorted list)
98
+ if items[i].score >= items[j].score:
99
+ to_remove.add(j)
100
+ else:
101
+ to_remove.add(i)
102
+
103
+ # Return items not marked for removal
104
+ return [item for idx, item in enumerate(items) if idx not in to_remove]
105
+
106
+
107
+ def dedupe_reddit(
108
+ items: List[schema.RedditItem],
109
+ threshold: float = 0.7,
110
+ ) -> List[schema.RedditItem]:
111
+ """Dedupe Reddit items."""
112
+ return dedupe_items(items, threshold)
113
+
114
+
115
+ def dedupe_x(
116
+ items: List[schema.XItem],
117
+ threshold: float = 0.7,
118
+ ) -> List[schema.XItem]:
119
+ """Dedupe X items."""
120
+ return dedupe_items(items, threshold)
@@ -0,0 +1,149 @@
1
+ """Environment and API key management for last30days skill."""
2
+
3
+ import os
4
+ from pathlib import Path
5
+ from typing import Optional, Dict, Any
6
+
7
+ CONFIG_DIR = Path.home() / ".config" / "last30days"
8
+ CONFIG_FILE = CONFIG_DIR / ".env"
9
+
10
+
11
+ def load_env_file(path: Path) -> Dict[str, str]:
12
+ """Load environment variables from a file."""
13
+ env = {}
14
+ if not path.exists():
15
+ return env
16
+
17
+ with open(path, 'r') as f:
18
+ for line in f:
19
+ line = line.strip()
20
+ if not line or line.startswith('#'):
21
+ continue
22
+ if '=' in line:
23
+ key, _, value = line.partition('=')
24
+ key = key.strip()
25
+ value = value.strip()
26
+ # Remove quotes if present
27
+ if value and value[0] in ('"', "'") and value[-1] == value[0]:
28
+ value = value[1:-1]
29
+ if key and value:
30
+ env[key] = value
31
+ return env
32
+
33
+
34
+ def get_config() -> Dict[str, Any]:
35
+ """Load configuration from ~/.config/last30days/.env and environment."""
36
+ # Load from config file first
37
+ file_env = load_env_file(CONFIG_FILE)
38
+
39
+ # Environment variables override file
40
+ config = {
41
+ 'OPENAI_API_KEY': os.environ.get('OPENAI_API_KEY') or file_env.get('OPENAI_API_KEY'),
42
+ 'XAI_API_KEY': os.environ.get('XAI_API_KEY') or file_env.get('XAI_API_KEY'),
43
+ 'OPENAI_MODEL_POLICY': os.environ.get('OPENAI_MODEL_POLICY') or file_env.get('OPENAI_MODEL_POLICY', 'auto'),
44
+ 'OPENAI_MODEL_PIN': os.environ.get('OPENAI_MODEL_PIN') or file_env.get('OPENAI_MODEL_PIN'),
45
+ 'XAI_MODEL_POLICY': os.environ.get('XAI_MODEL_POLICY') or file_env.get('XAI_MODEL_POLICY', 'latest'),
46
+ 'XAI_MODEL_PIN': os.environ.get('XAI_MODEL_PIN') or file_env.get('XAI_MODEL_PIN'),
47
+ }
48
+
49
+ return config
50
+
51
+
52
+ def config_exists() -> bool:
53
+ """Check if configuration file exists."""
54
+ return CONFIG_FILE.exists()
55
+
56
+
57
+ def get_available_sources(config: Dict[str, Any]) -> str:
58
+ """Determine which sources are available based on API keys.
59
+
60
+ Returns: 'both', 'reddit', 'x', or 'web' (fallback when no keys)
61
+ """
62
+ has_openai = bool(config.get('OPENAI_API_KEY'))
63
+ has_xai = bool(config.get('XAI_API_KEY'))
64
+
65
+ if has_openai and has_xai:
66
+ return 'both'
67
+ elif has_openai:
68
+ return 'reddit'
69
+ elif has_xai:
70
+ return 'x'
71
+ else:
72
+ return 'web' # Fallback: WebSearch only (no API keys needed)
73
+
74
+
75
+ def get_missing_keys(config: Dict[str, Any]) -> str:
76
+ """Determine which API keys are missing.
77
+
78
+ Returns: 'both', 'reddit', 'x', or 'none'
79
+ """
80
+ has_openai = bool(config.get('OPENAI_API_KEY'))
81
+ has_xai = bool(config.get('XAI_API_KEY'))
82
+
83
+ if has_openai and has_xai:
84
+ return 'none'
85
+ elif has_openai:
86
+ return 'x' # Missing xAI key
87
+ elif has_xai:
88
+ return 'reddit' # Missing OpenAI key
89
+ else:
90
+ return 'both' # Missing both keys
91
+
92
+
93
+ def validate_sources(requested: str, available: str, include_web: bool = False) -> tuple[str, Optional[str]]:
94
+ """Validate requested sources against available keys.
95
+
96
+ Args:
97
+ requested: 'auto', 'reddit', 'x', 'both', or 'web'
98
+ available: Result from get_available_sources()
99
+ include_web: If True, add WebSearch to available sources
100
+
101
+ Returns:
102
+ Tuple of (effective_sources, error_message)
103
+ """
104
+ # WebSearch-only mode (no API keys)
105
+ if available == 'web':
106
+ if requested == 'auto':
107
+ return 'web', None
108
+ elif requested == 'web':
109
+ return 'web', None
110
+ else:
111
+ return 'web', f"No API keys configured. Using WebSearch fallback. Add keys to ~/.config/last30days/.env for Reddit/X."
112
+
113
+ if requested == 'auto':
114
+ # Add web to sources if include_web is set
115
+ if include_web:
116
+ if available == 'both':
117
+ return 'all', None # reddit + x + web
118
+ elif available == 'reddit':
119
+ return 'reddit-web', None
120
+ elif available == 'x':
121
+ return 'x-web', None
122
+ return available, None
123
+
124
+ if requested == 'web':
125
+ return 'web', None
126
+
127
+ if requested == 'both':
128
+ if available not in ('both',):
129
+ missing = 'xAI' if available == 'reddit' else 'OpenAI'
130
+ return 'none', f"Requested both sources but {missing} key is missing. Use --sources=auto to use available keys."
131
+ if include_web:
132
+ return 'all', None
133
+ return 'both', None
134
+
135
+ if requested == 'reddit':
136
+ if available == 'x':
137
+ return 'none', "Requested Reddit but only xAI key is available."
138
+ if include_web:
139
+ return 'reddit-web', None
140
+ return 'reddit', None
141
+
142
+ if requested == 'x':
143
+ if available == 'reddit':
144
+ return 'none', "Requested X but only OpenAI key is available."
145
+ if include_web:
146
+ return 'x-web', None
147
+ return 'x', None
148
+
149
+ return requested, None
@@ -0,0 +1,152 @@
1
+ """HTTP utilities for last30days skill (stdlib only)."""
2
+
3
+ import json
4
+ import os
5
+ import sys
6
+ import time
7
+ import urllib.error
8
+ import urllib.request
9
+ from typing import Any, Dict, Optional
10
+ from urllib.parse import urlencode
11
+
12
+ DEFAULT_TIMEOUT = 30
13
+ DEBUG = os.environ.get("LAST30DAYS_DEBUG", "").lower() in ("1", "true", "yes")
14
+
15
+
16
+ def log(msg: str):
17
+ """Log debug message to stderr."""
18
+ if DEBUG:
19
+ sys.stderr.write(f"[DEBUG] {msg}\n")
20
+ sys.stderr.flush()
21
+ MAX_RETRIES = 3
22
+ RETRY_DELAY = 1.0
23
+ USER_AGENT = "last30days-skill/1.0 (Claude Code Skill)"
24
+
25
+
26
+ class HTTPError(Exception):
27
+ """HTTP request error with status code."""
28
+ def __init__(self, message: str, status_code: Optional[int] = None, body: Optional[str] = None):
29
+ super().__init__(message)
30
+ self.status_code = status_code
31
+ self.body = body
32
+
33
+
34
+ def request(
35
+ method: str,
36
+ url: str,
37
+ headers: Optional[Dict[str, str]] = None,
38
+ json_data: Optional[Dict[str, Any]] = None,
39
+ timeout: int = DEFAULT_TIMEOUT,
40
+ retries: int = MAX_RETRIES,
41
+ ) -> Dict[str, Any]:
42
+ """Make an HTTP request and return JSON response.
43
+
44
+ Args:
45
+ method: HTTP method (GET, POST, etc.)
46
+ url: Request URL
47
+ headers: Optional headers dict
48
+ json_data: Optional JSON body (for POST)
49
+ timeout: Request timeout in seconds
50
+ retries: Number of retries on failure
51
+
52
+ Returns:
53
+ Parsed JSON response
54
+
55
+ Raises:
56
+ HTTPError: On request failure
57
+ """
58
+ headers = headers or {}
59
+ headers.setdefault("User-Agent", USER_AGENT)
60
+
61
+ data = None
62
+ if json_data is not None:
63
+ data = json.dumps(json_data).encode('utf-8')
64
+ headers.setdefault("Content-Type", "application/json")
65
+
66
+ req = urllib.request.Request(url, data=data, headers=headers, method=method)
67
+
68
+ log(f"{method} {url}")
69
+ if json_data:
70
+ log(f"Payload keys: {list(json_data.keys())}")
71
+
72
+ last_error = None
73
+ for attempt in range(retries):
74
+ try:
75
+ with urllib.request.urlopen(req, timeout=timeout) as response:
76
+ body = response.read().decode('utf-8')
77
+ log(f"Response: {response.status} ({len(body)} bytes)")
78
+ return json.loads(body) if body else {}
79
+ except urllib.error.HTTPError as e:
80
+ body = None
81
+ try:
82
+ body = e.read().decode('utf-8')
83
+ except:
84
+ pass
85
+ log(f"HTTP Error {e.code}: {e.reason}")
86
+ if body:
87
+ log(f"Error body: {body[:500]}")
88
+ last_error = HTTPError(f"HTTP {e.code}: {e.reason}", e.code, body)
89
+
90
+ # Don't retry client errors (4xx) except rate limits
91
+ if 400 <= e.code < 500 and e.code != 429:
92
+ raise last_error
93
+
94
+ if attempt < retries - 1:
95
+ time.sleep(RETRY_DELAY * (attempt + 1))
96
+ except urllib.error.URLError as e:
97
+ log(f"URL Error: {e.reason}")
98
+ last_error = HTTPError(f"URL Error: {e.reason}")
99
+ if attempt < retries - 1:
100
+ time.sleep(RETRY_DELAY * (attempt + 1))
101
+ except json.JSONDecodeError as e:
102
+ log(f"JSON decode error: {e}")
103
+ last_error = HTTPError(f"Invalid JSON response: {e}")
104
+ raise last_error
105
+ except (OSError, TimeoutError, ConnectionResetError) as e:
106
+ # Handle socket-level errors (connection reset, timeout, etc.)
107
+ log(f"Connection error: {type(e).__name__}: {e}")
108
+ last_error = HTTPError(f"Connection error: {type(e).__name__}: {e}")
109
+ if attempt < retries - 1:
110
+ time.sleep(RETRY_DELAY * (attempt + 1))
111
+
112
+ if last_error:
113
+ raise last_error
114
+ raise HTTPError("Request failed with no error details")
115
+
116
+
117
+ def get(url: str, headers: Optional[Dict[str, str]] = None, **kwargs) -> Dict[str, Any]:
118
+ """Make a GET request."""
119
+ return request("GET", url, headers=headers, **kwargs)
120
+
121
+
122
+ def post(url: str, json_data: Dict[str, Any], headers: Optional[Dict[str, str]] = None, **kwargs) -> Dict[str, Any]:
123
+ """Make a POST request with JSON body."""
124
+ return request("POST", url, headers=headers, json_data=json_data, **kwargs)
125
+
126
+
127
+ def get_reddit_json(path: str) -> Dict[str, Any]:
128
+ """Fetch Reddit thread JSON.
129
+
130
+ Args:
131
+ path: Reddit path (e.g., /r/subreddit/comments/id/title)
132
+
133
+ Returns:
134
+ Parsed JSON response
135
+ """
136
+ # Ensure path starts with /
137
+ if not path.startswith('/'):
138
+ path = '/' + path
139
+
140
+ # Remove trailing slash and add .json
141
+ path = path.rstrip('/')
142
+ if not path.endswith('.json'):
143
+ path = path + '.json'
144
+
145
+ url = f"https://www.reddit.com{path}?raw_json=1"
146
+
147
+ headers = {
148
+ "User-Agent": USER_AGENT,
149
+ "Accept": "application/json",
150
+ }
151
+
152
+ return get(url, headers=headers)