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,430 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Skill Validator - Deep validation of a skill directory.
4
+
5
+ Performs 10 checks on a skill directory to ensure it's properly structured
6
+ and ready for installation.
7
+
8
+ Usage:
9
+ python validate_skill.py "C:\\path\\to\\skill"
10
+ python validate_skill.py "C:\\path\\to\\skill" --strict
11
+ python validate_skill.py "C:\\path\\to\\skill" --registry "C:\\path\\to\\registry.json"
12
+ """
13
+
14
+ import os
15
+ import sys
16
+ import json
17
+ import re
18
+ from pathlib import Path
19
+
20
+ # ── Constants ──────────────────────────────────────────────────────────────
21
+
22
+ FORBIDDEN_PATTERNS = [
23
+ ".env",
24
+ "credentials.json",
25
+ "credentials.yaml",
26
+ "credentials.yml",
27
+ "*.key",
28
+ "*.pem",
29
+ "*.p12",
30
+ "*.pfx",
31
+ ".secrets",
32
+ "secret.json",
33
+ "token.json",
34
+ ".aws/credentials",
35
+ ]
36
+
37
+ MAX_SIZE_MB = 50
38
+ MIN_DESCRIPTION_LENGTH = 50
39
+
40
+ SKILLS_ROOT = Path(r"C:\Users\renat\skills")
41
+ REGISTRY_PATH = SKILLS_ROOT / "agent-orchestrator" / "data" / "registry.json"
42
+
43
+
44
+ # ── YAML Frontmatter Parser ───────────────────────────────────────────────
45
+
46
+ def parse_yaml_frontmatter(path: Path) -> dict:
47
+ """Extract YAML frontmatter from a SKILL.md file.
48
+
49
+ Mirrors the parser from scan_registry.py for consistency.
50
+ """
51
+ try:
52
+ text = path.read_text(encoding="utf-8")
53
+ except Exception:
54
+ return {}
55
+
56
+ match = re.match(r"^---\s*\n(.*?)\n---", text, re.DOTALL)
57
+ if not match:
58
+ return {}
59
+
60
+ try:
61
+ import yaml
62
+ return yaml.safe_load(match.group(1)) or {}
63
+ except Exception:
64
+ # Fallback: manual parsing
65
+ result = {}
66
+ block = match.group(1)
67
+ for key in ("name", "description", "version", "capabilities"):
68
+ m = re.search(rf'^{key}:\s*["\']?(.+?)["\']?\s*$', block, re.MULTILINE)
69
+ if m:
70
+ result[key] = m.group(1).strip()
71
+ else:
72
+ m2 = re.search(
73
+ rf'^{key}:\s*>-?\s*\n((?:\s+.+\n?)+)', block, re.MULTILINE
74
+ )
75
+ if m2:
76
+ lines = m2.group(1).strip().split("\n")
77
+ result[key] = " ".join(line.strip() for line in lines)
78
+ return result
79
+
80
+
81
+ # ── Validation Checks ─────────────────────────────────────────────────────
82
+
83
+ def check_skill_md_exists(skill_dir: Path) -> dict:
84
+ """Check 1: SKILL.md exists."""
85
+ skill_md = skill_dir / "SKILL.md"
86
+ exists = skill_md.exists() and skill_md.is_file()
87
+ return {
88
+ "check": 1,
89
+ "name": "SKILL.md exists",
90
+ "status": "pass" if exists else "fail",
91
+ "message": str(skill_md) if exists else f"SKILL.md not found in {skill_dir}",
92
+ }
93
+
94
+
95
+ def check_frontmatter_parseable(skill_dir: Path) -> dict:
96
+ """Check 2: YAML frontmatter is present and parseable."""
97
+ skill_md = skill_dir / "SKILL.md"
98
+ if not skill_md.exists():
99
+ return {
100
+ "check": 2,
101
+ "name": "Frontmatter parseable",
102
+ "status": "fail",
103
+ "message": "SKILL.md does not exist",
104
+ }
105
+
106
+ try:
107
+ text = skill_md.read_text(encoding="utf-8")
108
+ except Exception as e:
109
+ return {
110
+ "check": 2,
111
+ "name": "Frontmatter parseable",
112
+ "status": "fail",
113
+ "message": f"Cannot read SKILL.md: {e}",
114
+ }
115
+
116
+ match = re.match(r"^---\s*\n(.*?)\n---", text, re.DOTALL)
117
+ if not match:
118
+ return {
119
+ "check": 2,
120
+ "name": "Frontmatter parseable",
121
+ "status": "fail",
122
+ "message": "No YAML frontmatter found (expected --- delimiters)",
123
+ }
124
+
125
+ meta = parse_yaml_frontmatter(skill_md)
126
+ if not meta:
127
+ return {
128
+ "check": 2,
129
+ "name": "Frontmatter parseable",
130
+ "status": "fail",
131
+ "message": "Frontmatter found but could not be parsed",
132
+ }
133
+
134
+ return {
135
+ "check": 2,
136
+ "name": "Frontmatter parseable",
137
+ "status": "pass",
138
+ "message": f"Parsed fields: {', '.join(meta.keys())}",
139
+ }
140
+
141
+
142
+ def check_name_exists(meta: dict) -> dict:
143
+ """Check 3: 'name' field exists and is non-empty."""
144
+ name = meta.get("name", "")
145
+ has_name = bool(name and str(name).strip())
146
+ return {
147
+ "check": 3,
148
+ "name": "Field 'name' present",
149
+ "status": "pass" if has_name else "fail",
150
+ "message": f"name: {name}" if has_name else "Missing or empty 'name' field",
151
+ }
152
+
153
+
154
+ def check_description_exists(meta: dict) -> dict:
155
+ """Check 4: 'description' field exists and is non-empty."""
156
+ desc = meta.get("description", "")
157
+ has_desc = bool(desc and str(desc).strip())
158
+ return {
159
+ "check": 4,
160
+ "name": "Field 'description' present",
161
+ "status": "pass" if has_desc else "fail",
162
+ "message": (
163
+ f"description: {str(desc)[:80]}..."
164
+ if has_desc
165
+ else "Missing or empty 'description' field"
166
+ ),
167
+ }
168
+
169
+
170
+ def check_description_length(meta: dict) -> dict:
171
+ """Check 5: Description has >= 50 characters (warning if shorter)."""
172
+ desc = str(meta.get("description", ""))
173
+ length = len(desc)
174
+ ok = length >= MIN_DESCRIPTION_LENGTH
175
+ return {
176
+ "check": 5,
177
+ "name": "Description length >= 50 chars",
178
+ "status": "pass" if ok else "warn",
179
+ "message": (
180
+ f"Length: {length} chars"
181
+ if ok
182
+ else f"Description only {length} chars (recommend >= {MIN_DESCRIPTION_LENGTH})"
183
+ ),
184
+ }
185
+
186
+
187
+ def check_name_matches_dir(skill_dir: Path, meta: dict) -> dict:
188
+ """Check 6: 'name' matches directory name (warning if mismatch)."""
189
+ name = str(meta.get("name", "")).strip().lower()
190
+ dir_name = skill_dir.name.lower()
191
+
192
+ if not name:
193
+ return {
194
+ "check": 6,
195
+ "name": "Name matches directory",
196
+ "status": "warn",
197
+ "message": "No name field to compare",
198
+ }
199
+
200
+ matches = name == dir_name
201
+ return {
202
+ "check": 6,
203
+ "name": "Name matches directory",
204
+ "status": "pass" if matches else "warn",
205
+ "message": (
206
+ f"'{name}' == '{dir_name}'"
207
+ if matches
208
+ else f"Name '{name}' differs from directory '{dir_name}'"
209
+ ),
210
+ }
211
+
212
+
213
+ def check_forbidden_files(skill_dir: Path) -> dict:
214
+ """Check 7: No forbidden files (.env, credentials, keys, etc.)."""
215
+ found_forbidden = []
216
+
217
+ for root, _dirs, files in os.walk(skill_dir):
218
+ for f in files:
219
+ f_lower = f.lower()
220
+ for pattern in FORBIDDEN_PATTERNS:
221
+ if pattern.startswith("*."):
222
+ ext = pattern[1:] # e.g., ".key"
223
+ if f_lower.endswith(ext):
224
+ found_forbidden.append(os.path.join(root, f))
225
+ break
226
+ else:
227
+ if f_lower == pattern.lower():
228
+ found_forbidden.append(os.path.join(root, f))
229
+ break
230
+
231
+ if found_forbidden:
232
+ return {
233
+ "check": 7,
234
+ "name": "No forbidden files",
235
+ "status": "fail",
236
+ "message": f"Found {len(found_forbidden)} forbidden file(s): {', '.join(found_forbidden[:5])}",
237
+ }
238
+
239
+ return {
240
+ "check": 7,
241
+ "name": "No forbidden files",
242
+ "status": "pass",
243
+ "message": "No forbidden files detected",
244
+ }
245
+
246
+
247
+ def check_total_size(skill_dir: Path) -> dict:
248
+ """Check 8: Total size is reasonable (warn if > 50MB)."""
249
+ total = 0
250
+ for root, _dirs, files in os.walk(skill_dir):
251
+ for f in files:
252
+ try:
253
+ total += os.path.getsize(os.path.join(root, f))
254
+ except OSError:
255
+ pass
256
+
257
+ size_mb = total / (1024 * 1024)
258
+ ok = size_mb <= MAX_SIZE_MB
259
+
260
+ return {
261
+ "check": 8,
262
+ "name": f"Size <= {MAX_SIZE_MB}MB",
263
+ "status": "pass" if ok else "warn",
264
+ "message": f"Total: {size_mb:.1f} MB" + ("" if ok else f" (exceeds {MAX_SIZE_MB}MB)"),
265
+ }
266
+
267
+
268
+ def check_scripts_requirements(skill_dir: Path) -> dict:
269
+ """Check 9: If scripts/ exists, check for requirements.txt."""
270
+ scripts_dir = skill_dir / "scripts"
271
+ if not scripts_dir.exists():
272
+ return {
273
+ "check": 9,
274
+ "name": "scripts/ has requirements.txt",
275
+ "status": "skip",
276
+ "message": "No scripts/ directory (check not applicable)",
277
+ }
278
+
279
+ has_reqs = (scripts_dir / "requirements.txt").exists()
280
+ return {
281
+ "check": 9,
282
+ "name": "scripts/ has requirements.txt",
283
+ "status": "pass" if has_reqs else "warn",
284
+ "message": (
285
+ "requirements.txt found"
286
+ if has_reqs
287
+ else "scripts/ exists but no requirements.txt"
288
+ ),
289
+ }
290
+
291
+
292
+ def check_duplicate_name(meta: dict, registry_path: Path) -> dict:
293
+ """Check 10: Name not duplicated in existing registry."""
294
+ name = str(meta.get("name", "")).strip().lower()
295
+ if not name:
296
+ return {
297
+ "check": 10,
298
+ "name": "No duplicate in registry",
299
+ "status": "warn",
300
+ "message": "No name to check",
301
+ }
302
+
303
+ if not registry_path.exists():
304
+ return {
305
+ "check": 10,
306
+ "name": "No duplicate in registry",
307
+ "status": "pass",
308
+ "message": "No registry.json found (skip check)",
309
+ }
310
+
311
+ try:
312
+ registry = json.loads(registry_path.read_text(encoding="utf-8"))
313
+ existing_names = [
314
+ s.get("name", "").lower() for s in registry.get("skills", [])
315
+ ]
316
+ if name in existing_names:
317
+ return {
318
+ "check": 10,
319
+ "name": "No duplicate in registry",
320
+ "status": "warn",
321
+ "message": f"Skill '{name}' already exists in registry (use --force to overwrite)",
322
+ }
323
+ except Exception as e:
324
+ return {
325
+ "check": 10,
326
+ "name": "No duplicate in registry",
327
+ "status": "warn",
328
+ "message": f"Could not read registry: {e}",
329
+ }
330
+
331
+ return {
332
+ "check": 10,
333
+ "name": "No duplicate in registry",
334
+ "status": "pass",
335
+ "message": f"Name '{name}' not found in registry",
336
+ }
337
+
338
+
339
+ # ── Main Validation ───────────────────────────────────────────────────────
340
+
341
+ def validate(skill_dir: Path, strict: bool = False, registry_path: Path = None) -> dict:
342
+ """Run all 10 validation checks on a skill directory.
343
+
344
+ Returns:
345
+ dict with keys: valid (bool), checks (list), warnings (list), errors (list)
346
+ """
347
+ if registry_path is None:
348
+ registry_path = REGISTRY_PATH
349
+
350
+ skill_dir = Path(skill_dir).resolve()
351
+
352
+ if not skill_dir.exists():
353
+ return {
354
+ "valid": False,
355
+ "skill_dir": str(skill_dir),
356
+ "checks": [],
357
+ "warnings": [],
358
+ "errors": [f"Directory does not exist: {skill_dir}"],
359
+ }
360
+
361
+ # Parse frontmatter once
362
+ skill_md = skill_dir / "SKILL.md"
363
+ meta = parse_yaml_frontmatter(skill_md) if skill_md.exists() else {}
364
+
365
+ # Run all 10 checks
366
+ checks = [
367
+ check_skill_md_exists(skill_dir), # 1
368
+ check_frontmatter_parseable(skill_dir), # 2
369
+ check_name_exists(meta), # 3
370
+ check_description_exists(meta), # 4
371
+ check_description_length(meta), # 5
372
+ check_name_matches_dir(skill_dir, meta), # 6
373
+ check_forbidden_files(skill_dir), # 7
374
+ check_total_size(skill_dir), # 8
375
+ check_scripts_requirements(skill_dir), # 9
376
+ check_duplicate_name(meta, registry_path), # 10
377
+ ]
378
+
379
+ errors = [c for c in checks if c["status"] == "fail"]
380
+ warnings = [c for c in checks if c["status"] == "warn"]
381
+ passed = [c for c in checks if c["status"] in ("pass", "skip")]
382
+
383
+ # In strict mode, warnings are treated as errors
384
+ if strict:
385
+ errors.extend(warnings)
386
+ warnings = []
387
+
388
+ valid = len(errors) == 0
389
+
390
+ return {
391
+ "valid": valid,
392
+ "skill_dir": str(skill_dir),
393
+ "skill_name": meta.get("name", skill_dir.name),
394
+ "total_checks": len(checks),
395
+ "passed": len(passed),
396
+ "warnings_count": len(warnings),
397
+ "errors_count": len(errors),
398
+ "checks": checks,
399
+ "warnings": [f"Check {w['check']}: {w['message']}" for w in warnings],
400
+ "errors": [f"Check {e['check']}: {e['message']}" for e in errors],
401
+ }
402
+
403
+
404
+ # ── CLI Entry Point ───────────────────────────────────────────────────────
405
+
406
+ def main():
407
+ if len(sys.argv) < 2:
408
+ print(json.dumps({
409
+ "valid": False,
410
+ "error": "Usage: python validate_skill.py <skill-directory> [--strict] [--registry <path>]",
411
+ }, indent=2))
412
+ sys.exit(1)
413
+
414
+ skill_dir = Path(sys.argv[1]).resolve()
415
+ strict = "--strict" in sys.argv
416
+ registry_path = None
417
+
418
+ if "--registry" in sys.argv:
419
+ idx = sys.argv.index("--registry")
420
+ if idx + 1 < len(sys.argv):
421
+ registry_path = Path(sys.argv[idx + 1])
422
+
423
+ result = validate(skill_dir, strict=strict, registry_path=registry_path)
424
+ print(json.dumps(result, indent=2, ensure_ascii=False))
425
+
426
+ sys.exit(0 if result["valid"] else 1)
427
+
428
+
429
+ if __name__ == "__main__":
430
+ main()
@@ -0,0 +1,13 @@
1
+ """
2
+ Analyzers: modulos de analise por dimensao.
3
+
4
+ Cada analyzer recebe os dados de uma skill (do scanner) e retorna:
5
+ - score (0-100)
6
+ - findings (lista de problemas/recomendacoes)
7
+ """
8
+ from __future__ import annotations
9
+
10
+ from typing import Any, Dict, List, Tuple
11
+
12
+ # Tipo padrao para resultado de um analyzer
13
+ AnalyzerResult = Tuple[float, List[Dict[str, Any]]]
@@ -0,0 +1,247 @@
1
+ """
2
+ Analyzer de qualidade de codigo.
3
+
4
+ Usa AST (stdlib) para medir complexidade ciclomatica, tamanho de funcoes,
5
+ cobertura de docstrings e padroes de error handling.
6
+ """
7
+ from __future__ import annotations
8
+
9
+ import ast
10
+ from pathlib import Path
11
+ from typing import Any, Dict, List, Tuple
12
+
13
+ from config import (
14
+ MAX_CYCLOMATIC_COMPLEXITY,
15
+ MAX_FILE_LINES,
16
+ MAX_FUNCTION_LINES,
17
+ PENALTY_BARE_EXCEPT,
18
+ PENALTY_BROAD_EXCEPT,
19
+ PENALTY_HIGH_COMPLEXITY,
20
+ PENALTY_LONG_FILE,
21
+ PENALTY_LONG_FUNCTION,
22
+ PENALTY_NO_DOCSTRING,
23
+ )
24
+
25
+
26
+ def _cyclomatic_complexity(node: ast.AST) -> int:
27
+ """Calcula complexidade ciclomatica de uma funcao/metodo."""
28
+ complexity = 1 # base
29
+ for child in ast.walk(node):
30
+ if isinstance(child, (ast.If, ast.IfExp)):
31
+ complexity += 1
32
+ elif isinstance(child, (ast.For, ast.AsyncFor, ast.While)):
33
+ complexity += 1
34
+ elif isinstance(child, ast.ExceptHandler):
35
+ complexity += 1
36
+ elif isinstance(child, (ast.With, ast.AsyncWith)):
37
+ complexity += 1
38
+ elif isinstance(child, ast.BoolOp):
39
+ # cada and/or adiciona um path
40
+ complexity += len(child.values) - 1
41
+ elif isinstance(child, ast.Assert):
42
+ complexity += 1
43
+ return complexity
44
+
45
+
46
+ def _check_except_patterns(node: ast.AST) -> List[Dict[str, Any]]:
47
+ """Verifica padroes de except (bare except, broad except)."""
48
+ issues = []
49
+ for child in ast.walk(node):
50
+ if isinstance(child, ast.ExceptHandler):
51
+ if child.type is None:
52
+ issues.append({
53
+ "type": "bare_except",
54
+ "line": child.lineno,
55
+ "severity": "high",
56
+ })
57
+ elif isinstance(child.type, ast.Name) and child.type.id == "Exception":
58
+ # Verificar se tem logging no corpo
59
+ has_log = False
60
+ for stmt in ast.walk(child):
61
+ if isinstance(stmt, ast.Call):
62
+ func = stmt.func
63
+ if isinstance(func, ast.Attribute) and func.attr in (
64
+ "error", "warning", "exception", "critical"
65
+ ):
66
+ has_log = True
67
+ break
68
+ if isinstance(func, ast.Name) and func.id == "print":
69
+ has_log = True
70
+ break
71
+ if not has_log:
72
+ issues.append({
73
+ "type": "broad_except_no_log",
74
+ "line": child.lineno,
75
+ "severity": "medium",
76
+ })
77
+ return issues
78
+
79
+
80
+ def analyze(skill_data: Dict[str, Any]) -> Tuple[float, List[Dict[str, Any]]]:
81
+ """
82
+ Analisa qualidade de codigo de uma skill.
83
+ Retorna (score, findings).
84
+ """
85
+ score = 100.0
86
+ findings: List[Dict[str, Any]] = []
87
+ skill_name = skill_data["name"]
88
+ skill_path = Path(skill_data["path"])
89
+
90
+ total_functions = 0
91
+ functions_with_docs = 0
92
+
93
+ for rel_path in skill_data.get("python_files", []):
94
+ filepath = skill_path / rel_path
95
+ if not filepath.exists():
96
+ continue
97
+
98
+ try:
99
+ source = filepath.read_text(encoding="utf-8", errors="replace")
100
+ tree = ast.parse(source, filename=str(filepath))
101
+ except SyntaxError as e:
102
+ findings.append({
103
+ "skill_name": skill_name,
104
+ "dimension": "code_quality",
105
+ "severity": "high",
106
+ "category": "syntax_error",
107
+ "title": f"Erro de sintaxe em {rel_path}",
108
+ "description": str(e),
109
+ "file_path": rel_path,
110
+ "line_number": getattr(e, "lineno", None),
111
+ "recommendation": "Corrigir o erro de sintaxe",
112
+ "effort": "low",
113
+ "impact": "high",
114
+ })
115
+ score -= 15
116
+ continue
117
+
118
+ lines = source.splitlines()
119
+ file_lines = len(lines)
120
+
121
+ # Arquivo muito longo
122
+ if file_lines > MAX_FILE_LINES:
123
+ findings.append({
124
+ "skill_name": skill_name,
125
+ "dimension": "code_quality",
126
+ "severity": "medium",
127
+ "category": "long_file",
128
+ "title": f"Arquivo longo: {rel_path} ({file_lines} linhas)",
129
+ "description": f"Arquivo excede {MAX_FILE_LINES} linhas. Considerar dividir em modulos menores.",
130
+ "file_path": rel_path,
131
+ "recommendation": "Extrair funcionalidades em modulos separados",
132
+ "effort": "medium",
133
+ "impact": "medium",
134
+ })
135
+ score -= PENALTY_LONG_FILE
136
+
137
+ # Analisar funcoes e classes
138
+ for node in ast.walk(tree):
139
+ if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)):
140
+ total_functions += 1
141
+ func_name = node.name
142
+ end_line = getattr(node, "end_lineno", node.lineno)
143
+ func_lines = end_line - node.lineno + 1
144
+
145
+ # Docstring
146
+ has_doc = (
147
+ node.body
148
+ and isinstance(node.body[0], ast.Expr)
149
+ and isinstance(node.body[0].value, (ast.Constant, ast.Str))
150
+ )
151
+ if has_doc:
152
+ functions_with_docs += 1
153
+ else:
154
+ if not func_name.startswith("_"):
155
+ score -= PENALTY_NO_DOCSTRING
156
+
157
+ # Funcao longa
158
+ if func_lines > MAX_FUNCTION_LINES:
159
+ findings.append({
160
+ "skill_name": skill_name,
161
+ "dimension": "code_quality",
162
+ "severity": "medium",
163
+ "category": "long_function",
164
+ "title": f"Funcao longa: {func_name} ({func_lines} linhas) em {rel_path}",
165
+ "file_path": rel_path,
166
+ "line_number": node.lineno,
167
+ "recommendation": f"Reduzir {func_name} para < {MAX_FUNCTION_LINES} linhas extraindo sub-funcoes",
168
+ "effort": "medium",
169
+ "impact": "medium",
170
+ })
171
+ score -= PENALTY_LONG_FUNCTION
172
+
173
+ # Complexidade ciclomatica
174
+ complexity = _cyclomatic_complexity(node)
175
+ if complexity > MAX_CYCLOMATIC_COMPLEXITY:
176
+ findings.append({
177
+ "skill_name": skill_name,
178
+ "dimension": "code_quality",
179
+ "severity": "medium",
180
+ "category": "high_complexity",
181
+ "title": f"Alta complexidade: {func_name} (CC={complexity}) em {rel_path}",
182
+ "file_path": rel_path,
183
+ "line_number": node.lineno,
184
+ "recommendation": f"Simplificar {func_name} (CC={complexity} > {MAX_CYCLOMATIC_COMPLEXITY})",
185
+ "effort": "medium",
186
+ "impact": "medium",
187
+ })
188
+ score -= PENALTY_HIGH_COMPLEXITY
189
+
190
+ elif isinstance(node, ast.ClassDef):
191
+ total_functions += 1
192
+ has_doc = (
193
+ node.body
194
+ and isinstance(node.body[0], ast.Expr)
195
+ and isinstance(node.body[0].value, (ast.Constant, ast.Str))
196
+ )
197
+ if has_doc:
198
+ functions_with_docs += 1
199
+
200
+ # Padroes de except
201
+ except_issues = _check_except_patterns(tree)
202
+ for issue in except_issues:
203
+ if issue["type"] == "bare_except":
204
+ findings.append({
205
+ "skill_name": skill_name,
206
+ "dimension": "code_quality",
207
+ "severity": "high",
208
+ "category": "bare_except",
209
+ "title": f"Bare except em {rel_path}:{issue['line']}",
210
+ "file_path": rel_path,
211
+ "line_number": issue["line"],
212
+ "recommendation": "Usar except especifico (ex: except ValueError) em vez de bare except",
213
+ "effort": "low",
214
+ "impact": "high",
215
+ })
216
+ score -= PENALTY_BARE_EXCEPT
217
+ elif issue["type"] == "broad_except_no_log":
218
+ findings.append({
219
+ "skill_name": skill_name,
220
+ "dimension": "code_quality",
221
+ "severity": "medium",
222
+ "category": "broad_except",
223
+ "title": f"except Exception sem logging em {rel_path}:{issue['line']}",
224
+ "file_path": rel_path,
225
+ "line_number": issue["line"],
226
+ "recommendation": "Adicionar logging.error() ou re-raise dentro do except Exception",
227
+ "effort": "low",
228
+ "impact": "medium",
229
+ })
230
+ score -= PENALTY_BROAD_EXCEPT
231
+
232
+ # Bonus/penalidade por cobertura de docstrings
233
+ if total_functions > 0:
234
+ doc_coverage = functions_with_docs / total_functions
235
+ if doc_coverage < 0.3:
236
+ findings.append({
237
+ "skill_name": skill_name,
238
+ "dimension": "code_quality",
239
+ "severity": "low",
240
+ "category": "low_docstring_coverage",
241
+ "title": f"Baixa cobertura de docstrings ({doc_coverage:.0%})",
242
+ "recommendation": "Adicionar docstrings em funcoes publicas para melhorar manutenibilidade",
243
+ "effort": "low",
244
+ "impact": "low",
245
+ })
246
+
247
+ return max(0.0, min(100.0, score)), findings