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,190 @@
1
+ """
2
+ Orquestrador de scraping — coleta dados de todas as 27 Juntas Comerciais do Brasil.
3
+
4
+ Uso:
5
+ python scripts/run_all.py # todos os estados
6
+ python scripts/run_all.py --estado SP RJ MG # estados específicos
7
+ python scripts/run_all.py --concurrency 5 # paralelismo (default: 5)
8
+ python scripts/run_all.py --dry-run # mostra o que seria coletado
9
+ """
10
+ from __future__ import annotations
11
+
12
+ import argparse
13
+ import asyncio
14
+ import json
15
+ import logging
16
+ import sys
17
+ from datetime import datetime, timezone
18
+ from pathlib import Path
19
+ from typing import List, Optional
20
+
21
+ # Ajusta PYTHONPATH para imports relativos funcionarem
22
+ sys.path.insert(0, str(Path(__file__).parent))
23
+
24
+ from scraper.states import SCRAPERS, get_all_scrapers, get_scraper
25
+ from db import Database
26
+
27
+ logging.basicConfig(
28
+ level=logging.INFO,
29
+ format="%(asctime)s [%(levelname)s] %(message)s",
30
+ datefmt="%H:%M:%S",
31
+ )
32
+ logger = logging.getLogger(__name__)
33
+
34
+ LOG_FILE = Path(__file__).parent.parent / "data" / "scraping_log.json"
35
+
36
+
37
+ async def scrape_state(estado: str, semaphore: asyncio.Semaphore) -> dict:
38
+ """Scrapa um estado e retorna resultado com metadados."""
39
+ async with semaphore:
40
+ scraper = get_scraper(estado)
41
+ if not scraper:
42
+ return {
43
+ "estado": estado,
44
+ "status": "SCRAPER_NAO_ENCONTRADO",
45
+ "count": 0,
46
+ "records": [],
47
+ "error": None,
48
+ "scraped_at": datetime.now(timezone.utc).isoformat(),
49
+ }
50
+
51
+ try:
52
+ records = await scraper.scrape()
53
+ return {
54
+ "estado": estado,
55
+ "junta": scraper.junta,
56
+ "url": scraper.url,
57
+ "status": "OK" if records else "VAZIO",
58
+ "count": len(records),
59
+ "records": [r.to_dict() for r in records],
60
+ "error": None,
61
+ "scraped_at": datetime.now(timezone.utc).isoformat(),
62
+ }
63
+ except Exception as exc:
64
+ logger.exception("[%s] Erro no scraping: %s", estado, exc)
65
+ return {
66
+ "estado": estado,
67
+ "status": "ERRO",
68
+ "count": 0,
69
+ "records": [],
70
+ "error": str(exc),
71
+ "scraped_at": datetime.now(timezone.utc).isoformat(),
72
+ }
73
+
74
+
75
+ async def run(estados: Optional[List[str]], concurrency: int, dry_run: bool) -> None:
76
+ estados_alvo = [e.upper() for e in estados] if estados else list(SCRAPERS.keys())
77
+
78
+ if dry_run:
79
+ print(f"\n[DRY-RUN] Estados que seriam coletados ({len(estados_alvo)}):")
80
+ for uf in estados_alvo:
81
+ s = get_scraper(uf)
82
+ if s:
83
+ print(f" {uf}: {s.junta} -> {s.url}")
84
+ else:
85
+ print(f" {uf}: scraper nao encontrado")
86
+ return
87
+
88
+ logger.info("Iniciando coleta de %d estados (concurrency=%d)", len(estados_alvo), concurrency)
89
+
90
+ semaphore = asyncio.Semaphore(concurrency)
91
+ tasks = [scrape_state(uf, semaphore) for uf in estados_alvo]
92
+ results = await asyncio.gather(*tasks)
93
+
94
+ # Persistir no banco
95
+ db = Database()
96
+ db.init()
97
+
98
+ total_coletados = 0
99
+ total_salvos = 0
100
+ log_entries = []
101
+
102
+ for res in results:
103
+ estado = res["estado"]
104
+ status = res["status"]
105
+ count = res["count"]
106
+ total_coletados += count
107
+
108
+ if res["records"]:
109
+ saved = db.upsert_many(res["records"])
110
+ total_salvos += saved
111
+ logger.info("[%s] %d leiloeiros coletados, %d salvos", estado, count, saved)
112
+ else:
113
+ if status == "OK":
114
+ status = "VAZIO"
115
+ logger.warning("[%s] STATUS=%s error=%s", estado, status, res.get("error"))
116
+
117
+ log_entries.append({
118
+ "estado": estado,
119
+ "junta": res.get("junta", "?"),
120
+ "url": res.get("url", "?"),
121
+ "status": status,
122
+ "count": count,
123
+ "error": res.get("error"),
124
+ "scraped_at": res["scraped_at"],
125
+ })
126
+
127
+ # Salvar log
128
+ LOG_FILE.parent.mkdir(parents=True, exist_ok=True)
129
+ with open(LOG_FILE, "w", encoding="utf-8") as f:
130
+ json.dump(
131
+ {
132
+ "run_at": datetime.now(timezone.utc).isoformat(),
133
+ "total_estados": len(estados_alvo),
134
+ "total_coletados": total_coletados,
135
+ "total_salvos": total_salvos,
136
+ "estados": log_entries,
137
+ },
138
+ f,
139
+ ensure_ascii=False,
140
+ indent=2,
141
+ )
142
+
143
+ # Resumo final
144
+ print("\n" + "=" * 60)
145
+ print(f"RESUMO DA COLETA")
146
+ print("=" * 60)
147
+ ok = [e for e in log_entries if e["status"] == "OK"]
148
+ vazios = [e for e in log_entries if e["status"] == "VAZIO"]
149
+ erros = [e for e in log_entries if e["status"] not in ("OK", "VAZIO")]
150
+
151
+ print(f" OK: {len(ok)} estados | {total_coletados} leiloeiros coletados")
152
+ print(f" VAZIO: {len(vazios)} estados | {[e['estado'] for e in vazios]}")
153
+ print(f" ERRO: {len(erros)} estados | {[e['estado'] for e in erros]}")
154
+ print(f" TOTAL SALVO NO BANCO: {total_salvos}")
155
+ print(f" LOG: {LOG_FILE}")
156
+ print("=" * 60)
157
+
158
+ # Estatísticas do banco
159
+ stats = db.get_stats()
160
+ if stats:
161
+ print("\nESTATÍSTICAS POR ESTADO:")
162
+ print(f"{'UF':<5} {'Junta':<12} {'Total':>6} {'Ativos':>7}")
163
+ print("-" * 35)
164
+ for s in stats:
165
+ print(f"{s['estado']:<5} {s['junta']:<12} {s['total']:>6} {s['ativos']:>7}")
166
+
167
+
168
+ def main():
169
+ parser = argparse.ArgumentParser(
170
+ description="Coleta dados de leiloeiros de todas as Juntas Comerciais do Brasil"
171
+ )
172
+ parser.add_argument(
173
+ "--estado", nargs="*", metavar="UF",
174
+ help="Estados específicos (ex: SP RJ MG). Padrão: todos os 27."
175
+ )
176
+ parser.add_argument(
177
+ "--concurrency", type=int, default=5,
178
+ help="Número de scrapers em paralelo (default: 5)"
179
+ )
180
+ parser.add_argument(
181
+ "--dry-run", action="store_true",
182
+ help="Mostra o que seria coletado sem executar"
183
+ )
184
+ args = parser.parse_args()
185
+
186
+ asyncio.run(run(args.estado, args.concurrency, args.dry_run))
187
+
188
+
189
+ if __name__ == "__main__":
190
+ main()
@@ -0,0 +1,4 @@
1
+ """Scrapers de Juntas Comerciais do Brasil."""
2
+ from .base_scraper import AbstractJuntaScraper, Leiloeiro
3
+
4
+ __all__ = ["AbstractJuntaScraper", "Leiloeiro"]
@@ -0,0 +1,209 @@
1
+ """
2
+ Base abstrata para scrapers de leiloeiros das Juntas Comerciais do Brasil.
3
+ Cada estado herda desta classe e implementa parse_leiloeiros().
4
+ Suporta httpx (sites estáticos) e Playwright (sites com JavaScript).
5
+ """
6
+ from __future__ import annotations
7
+
8
+ import asyncio
9
+ import logging
10
+ from abc import ABC, abstractmethod
11
+ from dataclasses import dataclass, field
12
+ from datetime import datetime, timezone
13
+ from typing import List, Optional
14
+
15
+ import httpx
16
+ from bs4 import BeautifulSoup
17
+
18
+ logger = logging.getLogger(__name__)
19
+
20
+
21
+ @dataclass
22
+ class Leiloeiro:
23
+ estado: str
24
+ junta: str
25
+ nome: str
26
+ matricula: Optional[str] = None
27
+ cpf_cnpj: Optional[str] = None
28
+ situacao: Optional[str] = None
29
+ endereco: Optional[str] = None
30
+ municipio: Optional[str] = None
31
+ telefone: Optional[str] = None
32
+ email: Optional[str] = None
33
+ data_registro: Optional[str] = None
34
+ data_atualizacao: Optional[str] = None
35
+ url_fonte: Optional[str] = None
36
+ scraped_at: str = field(default_factory=lambda: datetime.now(timezone.utc).isoformat())
37
+
38
+ def to_dict(self) -> dict:
39
+ return {
40
+ "estado": self.estado,
41
+ "junta": self.junta,
42
+ "matricula": self.matricula,
43
+ "nome": self.nome,
44
+ "cpf_cnpj": self.cpf_cnpj,
45
+ "situacao": self.situacao,
46
+ "endereco": self.endereco,
47
+ "municipio": self.municipio,
48
+ "telefone": self.telefone,
49
+ "email": self.email,
50
+ "data_registro": self.data_registro,
51
+ "data_atualizacao": self.data_atualizacao,
52
+ "url_fonte": self.url_fonte,
53
+ "scraped_at": self.scraped_at,
54
+ }
55
+
56
+
57
+ class AbstractJuntaScraper(ABC):
58
+ """Classe base para todos os scrapers de Juntas Comerciais."""
59
+
60
+ estado: str # UF ex: "SP"
61
+ junta: str # nome da junta ex: "JUCESP"
62
+ url: str # URL da página de leiloeiros
63
+ rate_limit: float = 2.0 # segundos entre requests
64
+ max_retries: int = 3
65
+ timeout: float = 30.0
66
+
67
+ HEADERS = {
68
+ "User-Agent": (
69
+ "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
70
+ "AppleWebKit/537.36 (KHTML, like Gecko) "
71
+ "Chrome/121.0.0.0 Safari/537.36"
72
+ ),
73
+ "Accept-Language": "pt-BR,pt;q=0.9",
74
+ "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
75
+ }
76
+
77
+ async def fetch_page(
78
+ self,
79
+ url: Optional[str] = None,
80
+ params: Optional[dict] = None,
81
+ data: Optional[dict] = None,
82
+ method: str = "GET",
83
+ ) -> Optional[BeautifulSoup]:
84
+ """Faz o request HTTP com retry e retorna BeautifulSoup ou None."""
85
+ target = url or self.url
86
+ for attempt in range(1, self.max_retries + 1):
87
+ try:
88
+ async with httpx.AsyncClient(
89
+ headers=self.HEADERS,
90
+ timeout=self.timeout,
91
+ follow_redirects=True,
92
+ verify=False, # alguns sites gov têm cert self-signed
93
+ ) as client:
94
+ if method.upper() == "POST":
95
+ resp = await client.post(target, data=data, params=params)
96
+ else:
97
+ resp = await client.get(target, params=params)
98
+
99
+ resp.raise_for_status()
100
+ return BeautifulSoup(resp.text, "lxml")
101
+
102
+ except httpx.HTTPStatusError as exc:
103
+ logger.warning(
104
+ "[%s] HTTP %s em %s (tentativa %d/%d)",
105
+ self.estado, exc.response.status_code, target, attempt, self.max_retries,
106
+ )
107
+ except (httpx.RequestError, httpx.TimeoutException) as exc:
108
+ logger.warning(
109
+ "[%s] Erro de request em %s: %s (tentativa %d/%d)",
110
+ self.estado, target, exc, attempt, self.max_retries,
111
+ )
112
+
113
+ if attempt < self.max_retries:
114
+ await asyncio.sleep(2 ** attempt) # exponential backoff
115
+
116
+ logger.error("[%s] Falha após %d tentativas em %s", self.estado, self.max_retries, target)
117
+ return None
118
+
119
+ @abstractmethod
120
+ async def parse_leiloeiros(self) -> List[Leiloeiro]:
121
+ """Coleta e retorna a lista de leiloeiros do estado."""
122
+ ...
123
+
124
+ async def scrape(self) -> List[Leiloeiro]:
125
+ """Ponto de entrada principal — respeita rate limit e loga resultado."""
126
+ logger.info("[%s] Iniciando scraping de %s", self.estado, self.url)
127
+ await asyncio.sleep(self.rate_limit)
128
+ try:
129
+ results = await self.parse_leiloeiros()
130
+ logger.info("[%s] %d leiloeiros coletados", self.estado, len(results))
131
+ return results
132
+ except Exception as exc:
133
+ logger.exception("[%s] Erro inesperado: %s", self.estado, exc)
134
+ return []
135
+
136
+ # ── helpers comuns ──────────────────────────────────────────────────────
137
+
138
+ @staticmethod
139
+ def clean(text: Optional[str]) -> Optional[str]:
140
+ """Remove espaços extras e retorna None se vazio."""
141
+ if text is None:
142
+ return None
143
+ s = " ".join(text.split()).strip()
144
+ return s if s else None
145
+
146
+ @staticmethod
147
+ def normalize_situacao(raw: Optional[str]) -> Optional[str]:
148
+ """Normaliza status para ATIVO / CANCELADO / SUSPENSO / IRREGULAR."""
149
+ if raw is None:
150
+ return None
151
+ r = raw.upper().strip()
152
+ if any(x in r for x in ("ATIV", "REGULAR", "HABILITAD")):
153
+ return "ATIVO"
154
+ if any(x in r for x in ("CANCEL", "BAIXAD", "EXTINT")):
155
+ return "CANCELADO"
156
+ if "SUSPEND" in r:
157
+ return "SUSPENSO"
158
+ if "IRREG" in r:
159
+ return "IRREGULAR"
160
+ return raw.strip()
161
+
162
+ def make_leiloeiro(self, **kwargs) -> Leiloeiro:
163
+ """Factory que preenche estado/junta/url_fonte automaticamente."""
164
+ kwargs.setdefault("estado", self.estado)
165
+ kwargs.setdefault("junta", self.junta)
166
+ kwargs.setdefault("url_fonte", self.url)
167
+ if "situacao" in kwargs:
168
+ kwargs["situacao"] = self.normalize_situacao(kwargs["situacao"])
169
+ return Leiloeiro(**kwargs)
170
+
171
+ async def fetch_page_js(
172
+ self,
173
+ url: Optional[str] = None,
174
+ wait_selector: Optional[str] = None,
175
+ wait_ms: int = 3000,
176
+ ) -> Optional[BeautifulSoup]:
177
+ """Renderiza página com JavaScript usando Playwright. Retorna BeautifulSoup ou None."""
178
+ target = url or self.url
179
+ try:
180
+ from playwright.async_api import async_playwright
181
+ except ImportError:
182
+ logger.error("[%s] Playwright não instalado. Execute: playwright install chromium", self.estado)
183
+ return None
184
+
185
+ try:
186
+ async with async_playwright() as pw:
187
+ browser = await pw.chromium.launch(headless=True)
188
+ ctx = await browser.new_context(
189
+ user_agent=self.HEADERS["User-Agent"],
190
+ locale="pt-BR",
191
+ ignore_https_errors=True,
192
+ )
193
+ page = await ctx.new_page()
194
+ await page.goto(target, timeout=60000, wait_until="networkidle")
195
+
196
+ if wait_selector:
197
+ try:
198
+ await page.wait_for_selector(wait_selector, timeout=15000)
199
+ except Exception:
200
+ pass # Continua mesmo sem o seletor
201
+ else:
202
+ await page.wait_for_timeout(wait_ms)
203
+
204
+ html = await page.content()
205
+ await browser.close()
206
+ return BeautifulSoup(html, "lxml")
207
+ except Exception as exc:
208
+ logger.error("[%s] Erro Playwright em %s: %s", self.estado, target, exc)
209
+ return None
@@ -0,0 +1,110 @@
1
+ """
2
+ Scraper genérico para juntas que usam formato padrão de tabela HTML.
3
+ Estados sem scraper customizado herdam deste.
4
+ """
5
+ from __future__ import annotations
6
+
7
+ from typing import List, Optional
8
+
9
+ from .base_scraper import AbstractJuntaScraper, Leiloeiro
10
+
11
+
12
+ class GenericJuntaScraper(AbstractJuntaScraper):
13
+ """
14
+ Scraper genérico para juntas com tabela HTML padrão.
15
+ Subclasses definem apenas estado, junta e url.
16
+ """
17
+
18
+ estado: str
19
+ junta: str
20
+ url: str
21
+ municipio_default: Optional[str] = None # para estados com capital única dominante
22
+
23
+ async def parse_leiloeiros(self) -> List[Leiloeiro]:
24
+ soup = await self.fetch_page()
25
+ if not soup:
26
+ return []
27
+
28
+ results: List[Leiloeiro] = []
29
+
30
+ # Tentativa 1: tabela HTML
31
+ tables = soup.find_all("table")
32
+ for table in tables:
33
+ rows = table.find_all("tr")
34
+ if len(rows) < 2:
35
+ continue
36
+
37
+ headers = [self.clean(th.get_text()) for th in rows[0].find_all(["th", "td"])]
38
+ if not headers:
39
+ continue
40
+
41
+ col = {(h or "").lower(): i for i, h in enumerate(headers)}
42
+
43
+ # Verificar se parece uma tabela de leiloeiros
44
+ has_name_col = any(
45
+ "nome" in k or "leiloeiro" in k or "auxiliar" in k
46
+ for k in col.keys()
47
+ )
48
+ if not has_name_col and len(headers) < 2:
49
+ continue
50
+
51
+ def gcol(cells, frags):
52
+ for k, i in col.items():
53
+ if any(f in k for f in frags) and i < len(cells):
54
+ return self.clean(cells[i].get_text())
55
+ return None
56
+
57
+ for row in rows[1:]:
58
+ cells = row.find_all(["td", "th"])
59
+ if not cells:
60
+ continue
61
+ nome = gcol(cells, ["nome", "leiloeiro"]) or self.clean(cells[0].get_text())
62
+ if not nome or len(nome) < 3:
63
+ continue
64
+
65
+ results.append(self.make_leiloeiro(
66
+ nome=nome,
67
+ matricula=gcol(cells, ["matr", "registro", "núm", "numero", "nº"]),
68
+ cpf_cnpj=gcol(cells, ["cpf", "cnpj", "documento"]),
69
+ situacao=gcol(cells, ["situ", "status"]),
70
+ municipio=gcol(cells, ["munic", "cidade"]) or self.municipio_default,
71
+ telefone=gcol(cells, ["tel", "fone", "contato"]),
72
+ email=gcol(cells, ["email", "e-mail"]),
73
+ endereco=gcol(cells, ["ender", "logr", "rua"]),
74
+ data_registro=gcol(cells, ["data", "cadastr"]),
75
+ ))
76
+
77
+ if results:
78
+ break # Parar na primeira tabela com resultados
79
+
80
+ # Tentativa 2: listas (ul/ol li)
81
+ if not results:
82
+ list_items = soup.select("ul.leiloeiros li, ol.leiloeiros li, .lista-leiloeiros li")
83
+ if not list_items:
84
+ list_items = soup.select("ul li, ol li")
85
+
86
+ for li in list_items:
87
+ text = self.clean(li.get_text(" | "))
88
+ if not text or len(text) < 5:
89
+ continue
90
+ results.append(self.make_leiloeiro(nome=text, municipio=self.municipio_default))
91
+
92
+ # Tentativa 3: divs/articles com conteúdo textual
93
+ if not results:
94
+ content = soup.select_one(
95
+ ".conteudo-pagina, .page-content, .entry-content, article, main .content"
96
+ )
97
+ if content:
98
+ import re
99
+ for p in content.find_all(["p", "div", "li"]):
100
+ text = self.clean(p.get_text())
101
+ if not text or len(text) < 5:
102
+ continue
103
+ # Filtrar parágrafos que parecem ser registros de pessoas
104
+ if re.search(r"\b[A-ZÁÉÍÓÚÀÃÕÇ][a-záéíóúàãõç]{2,}", text):
105
+ results.append(self.make_leiloeiro(
106
+ nome=text,
107
+ municipio=self.municipio_default,
108
+ ))
109
+
110
+ return results
@@ -0,0 +1,110 @@
1
+ """
2
+ Scraper JUCAP — Junta Comercial do Amapa
3
+ URL: https://jucap.portal.ap.gov.br/pagina/informacoes/leiloleiros
4
+ (ATENCAO: typo oficial — "leiloleiros" com L extra, URL exata do site)
5
+ Metodo: httpx + BeautifulSoup
6
+ Estrutura: h4/h5 com nome + paragrafos com detalhes (Laravel/Livewire SSR)
7
+ Registros: ~12 leiloeiros
8
+ Nota: www.jucap.ap.gov.br falha por DNS — usar jucap.portal.ap.gov.br
9
+ """
10
+ from __future__ import annotations
11
+
12
+ import re
13
+ from typing import List
14
+
15
+ from .base_scraper import AbstractJuntaScraper, Leiloeiro
16
+
17
+
18
+ class JucapScraper(AbstractJuntaScraper):
19
+ estado = "AP"
20
+ junta = "JUCAP"
21
+ url = "https://jucap.portal.ap.gov.br/pagina/informacoes/leiloleiros"
22
+
23
+ async def parse_leiloeiros(self) -> List[Leiloeiro]:
24
+ soup = await self.fetch_page()
25
+ if not soup:
26
+ soup = await self.fetch_page(url="https://jucap.portal.ap.gov.br/pagina/informacoes/leiloeiros")
27
+ if not soup:
28
+ soup = await self.fetch_page_js(url=self.url, wait_ms=3000)
29
+ if not soup:
30
+ return []
31
+
32
+ results: List[Leiloeiro] = []
33
+
34
+ # Tenta tabela primeiro
35
+ for table in soup.find_all("table"):
36
+ rows = table.find_all("tr")
37
+ if len(rows) < 2:
38
+ continue
39
+ headers = [self.clean(th.get_text()) for th in rows[0].find_all(["th", "td"])]
40
+ col = {(h or "").lower(): i for i, h in enumerate(headers)}
41
+
42
+ def gcol(cells, frags):
43
+ for k, i in col.items():
44
+ if any(f in k for f in frags) and i < len(cells):
45
+ return self.clean(cells[i].get_text())
46
+ return None
47
+
48
+ for row in rows[1:]:
49
+ cells = row.find_all(["td", "th"])
50
+ if not cells:
51
+ continue
52
+ nome = gcol(cells, ["nome", "leiloeiro"]) or self.clean(cells[0].get_text())
53
+ if not nome or len(nome) < 3:
54
+ continue
55
+ results.append(self.make_leiloeiro(
56
+ nome=nome,
57
+ matricula=gcol(cells, ["matr", "registro"]),
58
+ situacao=gcol(cells, ["situ", "status"]),
59
+ municipio=gcol(cells, ["munic", "cidade"]) or "Macapa",
60
+ telefone=gcol(cells, ["tel", "fone"]),
61
+ email=gcol(cells, ["email"]),
62
+ endereco=gcol(cells, ["ender", "logr"]),
63
+ data_registro=gcol(cells, ["data", "posse"]),
64
+ ))
65
+ if results:
66
+ return results
67
+
68
+ # Estrutura Laravel: h4/h5 como nome + p como detalhes
69
+ content = soup.select_one("main, .content, article, #content, .page-content")
70
+ if not content:
71
+ content = soup.body
72
+
73
+ if content:
74
+ current: dict = {}
75
+ for el in content.find_all(["h3", "h4", "h5", "p", "li", "strong"]):
76
+ tag = el.name
77
+ text = self.clean(el.get_text())
78
+ if not text:
79
+ continue
80
+
81
+ if tag in ("h3", "h4", "h5"):
82
+ if current.get("nome"):
83
+ results.append(self.make_leiloeiro(**current))
84
+ current = {"nome": text, "municipio": "Macapa"}
85
+ elif current.get("nome"):
86
+ text_lower = text.lower()
87
+ if "matrícula" in text_lower or "matricula" in text_lower:
88
+ m = re.search(r"\d+", text)
89
+ if m:
90
+ current["matricula"] = m.group()
91
+ elif re.search(r"\d{4,5}[-\s]\d{4}", text):
92
+ current.setdefault("telefone", text)
93
+ elif "@" in text:
94
+ current["email"] = text
95
+ elif any(uf in text for uf in ["/AP", "Macapa", "Macapá", "Santana"]):
96
+ current["endereco"] = text
97
+ elif re.search(r"ativ|regular|cancel|suspen", text_lower):
98
+ current["situacao"] = text
99
+
100
+ if current.get("nome"):
101
+ results.append(self.make_leiloeiro(**current))
102
+
103
+ # Fallback texto plano
104
+ if not results:
105
+ for line in soup.get_text("\n").split("\n"):
106
+ line = self.clean(line)
107
+ if line and len(line) > 5 and re.match(r"^[A-ZÁÉÍÓÚÀÃÕÇ][A-ZÁÉÍÓÚÀÃÕÇ\s]{4,}$", line):
108
+ results.append(self.make_leiloeiro(nome=line, municipio="Macapa"))
109
+
110
+ return results