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.
- package/package.json +1 -1
- package/templates/.agents/agents/backend-specialist.md +263 -0
- package/templates/.agents/agents/code-archaeologist.md +106 -0
- package/templates/.agents/agents/database-architect.md +226 -0
- package/templates/.agents/agents/debugger.md +225 -0
- package/templates/.agents/agents/devops-engineer.md +242 -0
- package/templates/.agents/agents/documentation-writer.md +104 -0
- package/templates/.agents/agents/explorer-agent.md +73 -0
- package/templates/.agents/agents/frontend-specialist.md +593 -0
- package/templates/.agents/agents/game-developer.md +162 -0
- package/templates/.agents/agents/mobile-developer.md +377 -0
- package/templates/.agents/agents/orchestrator.md +416 -0
- package/templates/.agents/agents/penetration-tester.md +188 -0
- package/templates/.agents/agents/performance-optimizer.md +187 -0
- package/templates/.agents/agents/product-manager.md +112 -0
- package/templates/.agents/agents/product-owner.md +95 -0
- package/templates/.agents/agents/project-planner.md +406 -0
- package/templates/.agents/agents/qa-automation-engineer.md +103 -0
- package/templates/.agents/agents/security-auditor.md +170 -0
- package/templates/.agents/agents/seo-specialist.md +111 -0
- package/templates/.agents/agents/test-engineer.md +158 -0
- package/templates/.agents/rules/GEMINI.md +219 -0
- package/templates/.agents/scripts/auto_preview.py +148 -0
- package/templates/.agents/scripts/checklist.py +217 -0
- package/templates/.agents/scripts/session_manager.py +120 -0
- package/templates/.agents/scripts/verify_all.py +327 -0
- package/templates/.agents/workflows/brainstorm.md +113 -0
- package/templates/.agents/workflows/create.md +59 -0
- package/templates/.agents/workflows/debug.md +103 -0
- package/templates/.agents/workflows/deploy.md +176 -0
- package/templates/.agents/workflows/enhance.md +63 -0
- package/templates/.agents/workflows/orchestrate.md +237 -0
- package/templates/.agents/workflows/plan.md +89 -0
- package/templates/.agents/workflows/preview.md +81 -0
- package/templates/.agents/workflows/setup-brain.md +39 -0
- package/templates/.agents/workflows/status.md +86 -0
- package/templates/.agents/workflows/test.md +144 -0
- package/templates/.agents/workflows/ui-ux-pro-max.md +296 -0
- package/templates/skills_normal/api-patterns/scripts/api_validator.py +211 -0
- package/templates/skills_normal/database-design/scripts/schema_validator.py +172 -0
- package/templates/skills_normal/frontend-design/scripts/accessibility_checker.py +183 -0
- package/templates/skills_normal/frontend-design/scripts/ux_audit.py +722 -0
- package/templates/skills_normal/git-pushing/scripts/smart_commit.sh +19 -0
- package/templates/skills_normal/lint-and-validate/scripts/lint_runner.py +184 -0
- package/templates/skills_normal/lint-and-validate/scripts/type_coverage.py +173 -0
- package/templates/skills_normal/performance-profiling/scripts/lighthouse_audit.py +76 -0
- package/templates/skills_normal/senior-fullstack/scripts/code_quality_analyzer.py +114 -0
- package/templates/skills_normal/senior-fullstack/scripts/fullstack_scaffolder.py +114 -0
- package/templates/skills_normal/senior-fullstack/scripts/project_scaffolder.py +114 -0
- package/templates/skills_normal/seo-fundamentals/scripts/seo_checker.py +219 -0
- package/templates/skills_normal/testing-patterns/scripts/test_runner.py +219 -0
- package/templates/skills_normal/vulnerability-scanner/scripts/security_scan.py +458 -0
- package/templates/vault/007/scripts/config.py +472 -0
- package/templates/vault/007/scripts/full_audit.py +1306 -0
- package/templates/vault/007/scripts/quick_scan.py +481 -0
- package/templates/vault/007/scripts/requirements.txt +26 -0
- package/templates/vault/007/scripts/scanners/__init__.py +0 -0
- package/templates/vault/007/scripts/scanners/dependency_scanner.py +1305 -0
- package/templates/vault/007/scripts/scanners/injection_scanner.py +1104 -0
- package/templates/vault/007/scripts/scanners/secrets_scanner.py +1008 -0
- package/templates/vault/007/scripts/score_calculator.py +693 -0
- package/templates/vault/agent-orchestrator/scripts/match_skills.py +329 -0
- package/templates/vault/agent-orchestrator/scripts/orchestrate.py +304 -0
- package/templates/vault/agent-orchestrator/scripts/requirements.txt +1 -0
- package/templates/vault/agent-orchestrator/scripts/scan_registry.py +508 -0
- package/templates/vault/ai-studio-image/scripts/config.py +613 -0
- package/templates/vault/ai-studio-image/scripts/generate.py +630 -0
- package/templates/vault/ai-studio-image/scripts/prompt_engine.py +424 -0
- package/templates/vault/ai-studio-image/scripts/requirements.txt +4 -0
- package/templates/vault/ai-studio-image/scripts/templates.py +349 -0
- package/templates/vault/android_ui_verification/scripts/verify_ui.sh +32 -0
- package/templates/vault/apify-audience-analysis/reference/scripts/run_actor.js +363 -0
- package/templates/vault/apify-brand-reputation-monitoring/reference/scripts/run_actor.js +363 -0
- package/templates/vault/apify-competitor-intelligence/reference/scripts/run_actor.js +363 -0
- package/templates/vault/apify-content-analytics/reference/scripts/run_actor.js +363 -0
- package/templates/vault/apify-ecommerce/reference/scripts/package.json +3 -0
- package/templates/vault/apify-ecommerce/reference/scripts/run_actor.js +369 -0
- package/templates/vault/apify-influencer-discovery/reference/scripts/run_actor.js +363 -0
- package/templates/vault/apify-lead-generation/reference/scripts/run_actor.js +363 -0
- package/templates/vault/apify-market-research/reference/scripts/run_actor.js +363 -0
- package/templates/vault/apify-trend-analysis/reference/scripts/run_actor.js +363 -0
- package/templates/vault/apify-ultimate-scraper/reference/scripts/run_actor.js +363 -0
- package/templates/vault/audio-transcriber/scripts/install-requirements.sh +190 -0
- package/templates/vault/audio-transcriber/scripts/transcribe.py +486 -0
- package/templates/vault/claude-monitor/scripts/api_bench.py +240 -0
- package/templates/vault/claude-monitor/scripts/config.py +69 -0
- package/templates/vault/claude-monitor/scripts/health_check.py +362 -0
- package/templates/vault/claude-monitor/scripts/monitor.py +296 -0
- package/templates/vault/content-creator/scripts/brand_voice_analyzer.py +185 -0
- package/templates/vault/content-creator/scripts/seo_optimizer.py +419 -0
- package/templates/vault/context-agent/scripts/active_context.py +227 -0
- package/templates/vault/context-agent/scripts/compressor.py +149 -0
- package/templates/vault/context-agent/scripts/config.py +69 -0
- package/templates/vault/context-agent/scripts/context_loader.py +155 -0
- package/templates/vault/context-agent/scripts/context_manager.py +302 -0
- package/templates/vault/context-agent/scripts/models.py +103 -0
- package/templates/vault/context-agent/scripts/project_registry.py +132 -0
- package/templates/vault/context-agent/scripts/requirements.txt +6 -0
- package/templates/vault/context-agent/scripts/search.py +115 -0
- package/templates/vault/context-agent/scripts/session_parser.py +206 -0
- package/templates/vault/context-agent/scripts/session_summary.py +319 -0
- package/templates/vault/context-guardian/scripts/context_snapshot.py +229 -0
- package/templates/vault/docx/ooxml/scripts/pack.py +159 -0
- package/templates/vault/docx/ooxml/scripts/unpack.py +29 -0
- package/templates/vault/docx/ooxml/scripts/validate.py +69 -0
- package/templates/vault/docx/ooxml/scripts/validation/__init__.py +15 -0
- package/templates/vault/docx/ooxml/scripts/validation/base.py +951 -0
- package/templates/vault/docx/ooxml/scripts/validation/docx.py +274 -0
- package/templates/vault/docx/ooxml/scripts/validation/pptx.py +315 -0
- package/templates/vault/docx/ooxml/scripts/validation/redlining.py +279 -0
- package/templates/vault/docx/scripts/__init__.py +1 -0
- package/templates/vault/docx/scripts/document.py +1276 -0
- package/templates/vault/docx/scripts/templates/comments.xml +3 -0
- package/templates/vault/docx/scripts/templates/commentsExtended.xml +3 -0
- package/templates/vault/docx/scripts/templates/commentsExtensible.xml +3 -0
- package/templates/vault/docx/scripts/templates/commentsIds.xml +3 -0
- package/templates/vault/docx/scripts/templates/people.xml +3 -0
- package/templates/vault/docx/scripts/utilities.py +374 -0
- package/templates/vault/docx-official/ooxml/scripts/pack.py +159 -0
- package/templates/vault/docx-official/ooxml/scripts/unpack.py +29 -0
- package/templates/vault/docx-official/ooxml/scripts/validate.py +69 -0
- package/templates/vault/docx-official/ooxml/scripts/validation/__init__.py +15 -0
- package/templates/vault/docx-official/ooxml/scripts/validation/base.py +951 -0
- package/templates/vault/docx-official/ooxml/scripts/validation/docx.py +274 -0
- package/templates/vault/docx-official/ooxml/scripts/validation/pptx.py +315 -0
- package/templates/vault/docx-official/ooxml/scripts/validation/redlining.py +279 -0
- package/templates/vault/docx-official/scripts/__init__.py +1 -0
- package/templates/vault/docx-official/scripts/document.py +1276 -0
- package/templates/vault/docx-official/scripts/templates/comments.xml +3 -0
- package/templates/vault/docx-official/scripts/templates/commentsExtended.xml +3 -0
- package/templates/vault/docx-official/scripts/templates/commentsExtensible.xml +3 -0
- package/templates/vault/docx-official/scripts/templates/commentsIds.xml +3 -0
- package/templates/vault/docx-official/scripts/templates/people.xml +3 -0
- package/templates/vault/docx-official/scripts/utilities.py +374 -0
- package/templates/vault/geo-fundamentals/scripts/geo_checker.py +289 -0
- package/templates/vault/helm-chart-scaffolding/scripts/validate-chart.sh +244 -0
- package/templates/vault/i18n-localization/scripts/i18n_checker.py +241 -0
- package/templates/vault/instagram/scripts/account_setup.py +233 -0
- package/templates/vault/instagram/scripts/analyze.py +221 -0
- package/templates/vault/instagram/scripts/api_client.py +444 -0
- package/templates/vault/instagram/scripts/auth.py +411 -0
- package/templates/vault/instagram/scripts/comments.py +160 -0
- package/templates/vault/instagram/scripts/config.py +111 -0
- package/templates/vault/instagram/scripts/db.py +467 -0
- package/templates/vault/instagram/scripts/export.py +138 -0
- package/templates/vault/instagram/scripts/governance.py +233 -0
- package/templates/vault/instagram/scripts/hashtags.py +114 -0
- package/templates/vault/instagram/scripts/insights.py +170 -0
- package/templates/vault/instagram/scripts/media.py +65 -0
- package/templates/vault/instagram/scripts/messages.py +103 -0
- package/templates/vault/instagram/scripts/profile.py +58 -0
- package/templates/vault/instagram/scripts/publish.py +449 -0
- package/templates/vault/instagram/scripts/requirements.txt +5 -0
- package/templates/vault/instagram/scripts/run_all.py +189 -0
- package/templates/vault/instagram/scripts/schedule.py +189 -0
- package/templates/vault/instagram/scripts/serve_api.py +234 -0
- package/templates/vault/instagram/scripts/templates.py +155 -0
- package/templates/vault/junta-leiloeiros/scripts/db.py +216 -0
- package/templates/vault/junta-leiloeiros/scripts/export.py +137 -0
- package/templates/vault/junta-leiloeiros/scripts/requirements.txt +15 -0
- package/templates/vault/junta-leiloeiros/scripts/run_all.py +190 -0
- package/templates/vault/junta-leiloeiros/scripts/scraper/__init__.py +4 -0
- package/templates/vault/junta-leiloeiros/scripts/scraper/base_scraper.py +209 -0
- package/templates/vault/junta-leiloeiros/scripts/scraper/generic_scraper.py +110 -0
- package/templates/vault/junta-leiloeiros/scripts/scraper/jucap.py +110 -0
- package/templates/vault/junta-leiloeiros/scripts/scraper/juceac.py +72 -0
- package/templates/vault/junta-leiloeiros/scripts/scraper/juceal.py +72 -0
- package/templates/vault/junta-leiloeiros/scripts/scraper/juceb.py +68 -0
- package/templates/vault/junta-leiloeiros/scripts/scraper/jucec.py +63 -0
- package/templates/vault/junta-leiloeiros/scripts/scraper/jucema.py +211 -0
- package/templates/vault/junta-leiloeiros/scripts/scraper/jucemg.py +218 -0
- package/templates/vault/junta-leiloeiros/scripts/scraper/jucep.py +70 -0
- package/templates/vault/junta-leiloeiros/scripts/scraper/jucepa.py +74 -0
- package/templates/vault/junta-leiloeiros/scripts/scraper/jucepar.py +80 -0
- package/templates/vault/junta-leiloeiros/scripts/scraper/jucepe.py +78 -0
- package/templates/vault/junta-leiloeiros/scripts/scraper/jucepi.py +69 -0
- package/templates/vault/junta-leiloeiros/scripts/scraper/jucer.py +256 -0
- package/templates/vault/junta-leiloeiros/scripts/scraper/jucerja.py +170 -0
- package/templates/vault/junta-leiloeiros/scripts/scraper/jucern.py +71 -0
- package/templates/vault/junta-leiloeiros/scripts/scraper/jucesc.py +89 -0
- package/templates/vault/junta-leiloeiros/scripts/scraper/jucesp.py +233 -0
- package/templates/vault/junta-leiloeiros/scripts/scraper/jucetins.py +134 -0
- package/templates/vault/junta-leiloeiros/scripts/scraper/jucis_df.py +63 -0
- package/templates/vault/junta-leiloeiros/scripts/scraper/jucisrs.py +299 -0
- package/templates/vault/junta-leiloeiros/scripts/scraper/states.py +99 -0
- package/templates/vault/junta-leiloeiros/scripts/serve_api.py +164 -0
- package/templates/vault/junta-leiloeiros/scripts/web_scraper_fallback.py +233 -0
- package/templates/vault/last30days/scripts/last30days.py +521 -0
- package/templates/vault/last30days/scripts/lib/__init__.py +1 -0
- package/templates/vault/last30days/scripts/lib/cache.py +152 -0
- package/templates/vault/last30days/scripts/lib/dates.py +124 -0
- package/templates/vault/last30days/scripts/lib/dedupe.py +120 -0
- package/templates/vault/last30days/scripts/lib/env.py +149 -0
- package/templates/vault/last30days/scripts/lib/http.py +152 -0
- package/templates/vault/last30days/scripts/lib/models.py +175 -0
- package/templates/vault/last30days/scripts/lib/normalize.py +160 -0
- package/templates/vault/last30days/scripts/lib/openai_reddit.py +230 -0
- package/templates/vault/last30days/scripts/lib/reddit_enrich.py +232 -0
- package/templates/vault/last30days/scripts/lib/render.py +383 -0
- package/templates/vault/last30days/scripts/lib/schema.py +336 -0
- package/templates/vault/last30days/scripts/lib/score.py +311 -0
- package/templates/vault/last30days/scripts/lib/ui.py +324 -0
- package/templates/vault/last30days/scripts/lib/websearch.py +401 -0
- package/templates/vault/last30days/scripts/lib/xai_x.py +217 -0
- package/templates/vault/leiloeiro-avaliacao/scripts/governance.py +106 -0
- package/templates/vault/leiloeiro-avaliacao/scripts/requirements.txt +1 -0
- package/templates/vault/leiloeiro-edital/scripts/governance.py +106 -0
- package/templates/vault/leiloeiro-edital/scripts/requirements.txt +1 -0
- package/templates/vault/leiloeiro-ia/scripts/governance.py +106 -0
- package/templates/vault/leiloeiro-ia/scripts/requirements.txt +1 -0
- package/templates/vault/leiloeiro-juridico/scripts/governance.py +106 -0
- package/templates/vault/leiloeiro-juridico/scripts/requirements.txt +1 -0
- package/templates/vault/leiloeiro-mercado/scripts/governance.py +106 -0
- package/templates/vault/leiloeiro-mercado/scripts/requirements.txt +1 -0
- package/templates/vault/leiloeiro-risco/scripts/governance.py +106 -0
- package/templates/vault/leiloeiro-risco/scripts/requirements.txt +1 -0
- package/templates/vault/loki-mode/examples/todo-app-generated/backend/src/db/database.ts +24 -0
- package/templates/vault/loki-mode/examples/todo-app-generated/backend/src/db/db.ts +35 -0
- package/templates/vault/loki-mode/examples/todo-app-generated/backend/src/db/index.ts +2 -0
- package/templates/vault/loki-mode/examples/todo-app-generated/backend/src/db/migrations.ts +31 -0
- package/templates/vault/loki-mode/examples/todo-app-generated/backend/src/db/schema.sql +8 -0
- package/templates/vault/loki-mode/examples/todo-app-generated/backend/src/index.ts +44 -0
- package/templates/vault/loki-mode/examples/todo-app-generated/backend/src/routes/todos.ts +155 -0
- package/templates/vault/loki-mode/examples/todo-app-generated/backend/src/types/index.ts +35 -0
- package/templates/vault/loki-mode/examples/todo-app-generated/frontend/src/App.css +384 -0
- package/templates/vault/loki-mode/examples/todo-app-generated/frontend/src/App.tsx +81 -0
- package/templates/vault/loki-mode/examples/todo-app-generated/frontend/src/api/todos.ts +57 -0
- package/templates/vault/loki-mode/examples/todo-app-generated/frontend/src/components/ConfirmDialog.tsx +26 -0
- package/templates/vault/loki-mode/examples/todo-app-generated/frontend/src/components/EmptyState.tsx +8 -0
- package/templates/vault/loki-mode/examples/todo-app-generated/frontend/src/components/TodoForm.tsx +43 -0
- package/templates/vault/loki-mode/examples/todo-app-generated/frontend/src/components/TodoItem.tsx +36 -0
- package/templates/vault/loki-mode/examples/todo-app-generated/frontend/src/components/TodoList.tsx +27 -0
- package/templates/vault/loki-mode/examples/todo-app-generated/frontend/src/hooks/useTodos.ts +81 -0
- package/templates/vault/loki-mode/examples/todo-app-generated/frontend/src/index.css +48 -0
- package/templates/vault/loki-mode/examples/todo-app-generated/frontend/src/main.tsx +10 -0
- package/templates/vault/loki-mode/examples/todo-app-generated/frontend/src/vite-env.d.ts +1 -0
- package/templates/vault/loki-mode/scripts/export-to-vibe-kanban.sh +178 -0
- package/templates/vault/loki-mode/scripts/loki-wrapper.sh +281 -0
- package/templates/vault/loki-mode/scripts/take-screenshots.js +55 -0
- package/templates/vault/matematico-tao/scripts/complexity_analyzer.py +544 -0
- package/templates/vault/matematico-tao/scripts/dependency_graph.py +538 -0
- package/templates/vault/mcp-builder/scripts/connections.py +151 -0
- package/templates/vault/mcp-builder/scripts/evaluation.py +373 -0
- package/templates/vault/mcp-builder/scripts/example_evaluation.xml +22 -0
- package/templates/vault/mcp-builder/scripts/requirements.txt +2 -0
- package/templates/vault/mobile-design/scripts/mobile_audit.py +670 -0
- package/templates/vault/notebooklm/scripts/__init__.py +81 -0
- package/templates/vault/notebooklm/scripts/ask_question.py +256 -0
- package/templates/vault/notebooklm/scripts/auth_manager.py +358 -0
- package/templates/vault/notebooklm/scripts/browser_session.py +255 -0
- package/templates/vault/notebooklm/scripts/browser_utils.py +107 -0
- package/templates/vault/notebooklm/scripts/cleanup_manager.py +302 -0
- package/templates/vault/notebooklm/scripts/config.py +44 -0
- package/templates/vault/notebooklm/scripts/notebook_manager.py +410 -0
- package/templates/vault/notebooklm/scripts/run.py +102 -0
- package/templates/vault/notebooklm/scripts/setup_environment.py +204 -0
- package/templates/vault/pdf/scripts/check_bounding_boxes.py +70 -0
- package/templates/vault/pdf/scripts/check_bounding_boxes_test.py +226 -0
- package/templates/vault/pdf/scripts/check_fillable_fields.py +12 -0
- package/templates/vault/pdf/scripts/convert_pdf_to_images.py +35 -0
- package/templates/vault/pdf/scripts/create_validation_image.py +41 -0
- package/templates/vault/pdf/scripts/extract_form_field_info.py +152 -0
- package/templates/vault/pdf/scripts/fill_fillable_fields.py +114 -0
- package/templates/vault/pdf/scripts/fill_pdf_form_with_annotations.py +108 -0
- package/templates/vault/pdf-official/scripts/check_bounding_boxes.py +70 -0
- package/templates/vault/pdf-official/scripts/check_bounding_boxes_test.py +226 -0
- package/templates/vault/pdf-official/scripts/check_fillable_fields.py +12 -0
- package/templates/vault/pdf-official/scripts/convert_pdf_to_images.py +35 -0
- package/templates/vault/pdf-official/scripts/create_validation_image.py +41 -0
- package/templates/vault/pdf-official/scripts/extract_form_field_info.py +152 -0
- package/templates/vault/pdf-official/scripts/fill_fillable_fields.py +114 -0
- package/templates/vault/pdf-official/scripts/fill_pdf_form_with_annotations.py +108 -0
- package/templates/vault/planning-with-files/scripts/check-complete.sh +44 -0
- package/templates/vault/planning-with-files/scripts/init-session.sh +120 -0
- package/templates/vault/pptx/ooxml/scripts/pack.py +159 -0
- package/templates/vault/pptx/ooxml/scripts/unpack.py +29 -0
- package/templates/vault/pptx/ooxml/scripts/validate.py +69 -0
- package/templates/vault/pptx/ooxml/scripts/validation/__init__.py +15 -0
- package/templates/vault/pptx/ooxml/scripts/validation/base.py +951 -0
- package/templates/vault/pptx/ooxml/scripts/validation/docx.py +274 -0
- package/templates/vault/pptx/ooxml/scripts/validation/pptx.py +315 -0
- package/templates/vault/pptx/ooxml/scripts/validation/redlining.py +279 -0
- package/templates/vault/pptx/scripts/html2pptx.js +979 -0
- package/templates/vault/pptx/scripts/inventory.py +1020 -0
- package/templates/vault/pptx/scripts/rearrange.py +231 -0
- package/templates/vault/pptx/scripts/replace.py +385 -0
- package/templates/vault/pptx/scripts/thumbnail.py +450 -0
- package/templates/vault/pptx-official/ooxml/scripts/pack.py +159 -0
- package/templates/vault/pptx-official/ooxml/scripts/unpack.py +29 -0
- package/templates/vault/pptx-official/ooxml/scripts/validate.py +69 -0
- package/templates/vault/pptx-official/ooxml/scripts/validation/__init__.py +15 -0
- package/templates/vault/pptx-official/ooxml/scripts/validation/base.py +951 -0
- package/templates/vault/pptx-official/ooxml/scripts/validation/docx.py +274 -0
- package/templates/vault/pptx-official/ooxml/scripts/validation/pptx.py +315 -0
- package/templates/vault/pptx-official/ooxml/scripts/validation/redlining.py +279 -0
- package/templates/vault/pptx-official/scripts/html2pptx.js +979 -0
- package/templates/vault/pptx-official/scripts/inventory.py +1020 -0
- package/templates/vault/pptx-official/scripts/rearrange.py +231 -0
- package/templates/vault/pptx-official/scripts/replace.py +385 -0
- package/templates/vault/pptx-official/scripts/thumbnail.py +450 -0
- package/templates/vault/product-manager-toolkit/scripts/customer_interview_analyzer.py +441 -0
- package/templates/vault/product-manager-toolkit/scripts/rice_prioritizer.py +296 -0
- package/templates/vault/prompt-engineering-patterns/scripts/optimize-prompt.py +279 -0
- package/templates/vault/scripts/.skill_cache.json +7538 -0
- package/templates/vault/scripts/skill_search.py +228 -0
- package/templates/vault/senior-architect/scripts/architecture_diagram_generator.py +114 -0
- package/templates/vault/senior-architect/scripts/dependency_analyzer.py +114 -0
- package/templates/vault/senior-architect/scripts/project_architect.py +114 -0
- package/templates/vault/shopify-development/scripts/requirements.txt +19 -0
- package/templates/vault/shopify-development/scripts/shopify_graphql.py +428 -0
- package/templates/vault/shopify-development/scripts/shopify_init.py +441 -0
- package/templates/vault/shopify-development/scripts/tests/test_shopify_init.py +379 -0
- package/templates/vault/skill-creator/scripts/init_skill.py +303 -0
- package/templates/vault/skill-creator/scripts/package_skill.py +110 -0
- package/templates/vault/skill-creator/scripts/quick_validate.py +95 -0
- package/templates/vault/skill-installer/scripts/detect_skills.py +318 -0
- package/templates/vault/skill-installer/scripts/install_skill.py +1708 -0
- package/templates/vault/skill-installer/scripts/package_skill.py +417 -0
- package/templates/vault/skill-installer/scripts/requirements.txt +1 -0
- package/templates/vault/skill-installer/scripts/validate_skill.py +430 -0
- package/templates/vault/skill-sentinel/scripts/analyzers/__init__.py +13 -0
- package/templates/vault/skill-sentinel/scripts/analyzers/code_quality.py +247 -0
- package/templates/vault/skill-sentinel/scripts/analyzers/cross_skill.py +134 -0
- package/templates/vault/skill-sentinel/scripts/analyzers/dependencies.py +121 -0
- package/templates/vault/skill-sentinel/scripts/analyzers/documentation.py +189 -0
- package/templates/vault/skill-sentinel/scripts/analyzers/governance_audit.py +153 -0
- package/templates/vault/skill-sentinel/scripts/analyzers/performance.py +164 -0
- package/templates/vault/skill-sentinel/scripts/analyzers/security.py +189 -0
- package/templates/vault/skill-sentinel/scripts/config.py +158 -0
- package/templates/vault/skill-sentinel/scripts/cost_optimizer.py +146 -0
- package/templates/vault/skill-sentinel/scripts/db.py +354 -0
- package/templates/vault/skill-sentinel/scripts/governance.py +58 -0
- package/templates/vault/skill-sentinel/scripts/recommender.py +228 -0
- package/templates/vault/skill-sentinel/scripts/report_generator.py +224 -0
- package/templates/vault/skill-sentinel/scripts/requirements.txt +1 -0
- package/templates/vault/skill-sentinel/scripts/run_audit.py +290 -0
- package/templates/vault/skill-sentinel/scripts/scanner.py +271 -0
- package/templates/vault/stability-ai/scripts/config.py +266 -0
- package/templates/vault/stability-ai/scripts/generate.py +687 -0
- package/templates/vault/stability-ai/scripts/requirements.txt +4 -0
- package/templates/vault/stability-ai/scripts/styles.py +174 -0
- package/templates/vault/telegram/assets/boilerplate/nodejs/src/bot-client.ts +86 -0
- package/templates/vault/telegram/assets/boilerplate/nodejs/src/handlers.ts +79 -0
- package/templates/vault/telegram/assets/boilerplate/nodejs/src/index.ts +32 -0
- package/templates/vault/telegram/scripts/send_message.py +143 -0
- package/templates/vault/telegram/scripts/setup_project.py +103 -0
- package/templates/vault/telegram/scripts/test_bot.py +144 -0
- package/templates/vault/typescript-expert/scripts/ts_diagnostic.py +203 -0
- package/templates/vault/ui-ux-pro-max/scripts/__pycache__/core.cpython-314.pyc +0 -0
- package/templates/vault/ui-ux-pro-max/scripts/__pycache__/design_system.cpython-314.pyc +0 -0
- package/templates/vault/ui-ux-pro-max/scripts/core.py +257 -0
- package/templates/vault/ui-ux-pro-max/scripts/design_system.py +487 -0
- package/templates/vault/ui-ux-pro-max/scripts/search.py +76 -0
- package/templates/vault/videodb/scripts/ws_listener.py +204 -0
- package/templates/vault/web-artifacts-builder/scripts/bundle-artifact.sh +54 -0
- package/templates/vault/web-artifacts-builder/scripts/init-artifact.sh +322 -0
- package/templates/vault/web-artifacts-builder/scripts/shadcn-components.tar.gz +0 -0
- package/templates/vault/webapp-testing/scripts/with_server.py +106 -0
- package/templates/vault/whatsapp-cloud-api/assets/boilerplate/nodejs/src/index.ts +125 -0
- package/templates/vault/whatsapp-cloud-api/assets/boilerplate/nodejs/src/template-manager.ts +67 -0
- package/templates/vault/whatsapp-cloud-api/assets/boilerplate/nodejs/src/types.ts +216 -0
- package/templates/vault/whatsapp-cloud-api/assets/boilerplate/nodejs/src/webhook-handler.ts +173 -0
- package/templates/vault/whatsapp-cloud-api/assets/boilerplate/nodejs/src/whatsapp-client.ts +193 -0
- package/templates/vault/whatsapp-cloud-api/scripts/send_test_message.py +137 -0
- package/templates/vault/whatsapp-cloud-api/scripts/setup_project.py +118 -0
- package/templates/vault/whatsapp-cloud-api/scripts/validate_config.py +190 -0
- package/templates/vault/youtube-summarizer/scripts/extract-transcript.py +65 -0
- package/templates/vault/youtube-summarizer/scripts/install-dependencies.sh +28 -0
|
@@ -0,0 +1,538 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Prof. Euler — Dependency Graph Analyzer
|
|
4
|
+
Gera grafo de dependências de projetos Kotlin/Android com análise matemática
|
|
5
|
+
de grafos: componentes, ciclos, centralidade, instabilidade.
|
|
6
|
+
|
|
7
|
+
Uso:
|
|
8
|
+
python dependency_graph.py [path] [--format dot|json|text] [--output FILE]
|
|
9
|
+
python dependency_graph.py C:/Users/renat/earbudllm
|
|
10
|
+
python dependency_graph.py C:/Users/renat/earbudllm --format dot --output deps.dot
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
import os
|
|
14
|
+
import re
|
|
15
|
+
import sys
|
|
16
|
+
import json
|
|
17
|
+
import argparse
|
|
18
|
+
from pathlib import Path
|
|
19
|
+
from dataclasses import dataclass, field
|
|
20
|
+
from typing import Dict, List, Set, Optional, Tuple
|
|
21
|
+
|
|
22
|
+
# Fix Windows terminal encoding (cp1252 doesn't support emojis/unicode)
|
|
23
|
+
if sys.platform == 'win32':
|
|
24
|
+
try:
|
|
25
|
+
sys.stdout.reconfigure(encoding='utf-8', errors='replace')
|
|
26
|
+
sys.stderr.reconfigure(encoding='utf-8', errors='replace')
|
|
27
|
+
except AttributeError:
|
|
28
|
+
pass # Python < 3.7 fallback
|
|
29
|
+
from collections import defaultdict, deque
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
@dataclass
|
|
33
|
+
class Node:
|
|
34
|
+
id: str
|
|
35
|
+
module: str
|
|
36
|
+
package: str
|
|
37
|
+
kind: str # 'class', 'interface', 'object', 'enum', 'data class'
|
|
38
|
+
is_abstract: bool = False
|
|
39
|
+
is_open: bool = False
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
@dataclass
|
|
43
|
+
class Edge:
|
|
44
|
+
src: str # node id
|
|
45
|
+
dst: str # node id
|
|
46
|
+
kind: str # 'implements', 'extends', 'uses', 'imports', 'delegates_to'
|
|
47
|
+
weight: float = 1.0
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class DependencyGraph:
|
|
51
|
+
"""Grafo de dependências com análise matemática completa."""
|
|
52
|
+
|
|
53
|
+
def __init__(self):
|
|
54
|
+
self.nodes: Dict[str, Node] = {}
|
|
55
|
+
self.edges: List[Edge] = []
|
|
56
|
+
self._adj: Dict[str, Set[str]] = defaultdict(set) # adjacência direta
|
|
57
|
+
self._radj: Dict[str, Set[str]] = defaultdict(set) # adjacência reversa
|
|
58
|
+
|
|
59
|
+
def add_node(self, node: Node) -> None:
|
|
60
|
+
self.nodes[node.id] = node
|
|
61
|
+
|
|
62
|
+
def add_edge(self, edge: Edge) -> None:
|
|
63
|
+
if edge.src != edge.dst: # sem self-loops
|
|
64
|
+
self.edges.append(edge)
|
|
65
|
+
self._adj[edge.src].add(edge.dst)
|
|
66
|
+
self._radj[edge.dst].add(edge.src)
|
|
67
|
+
|
|
68
|
+
def successors(self, node_id: str) -> Set[str]:
|
|
69
|
+
return self._adj.get(node_id, set())
|
|
70
|
+
|
|
71
|
+
def predecessors(self, node_id: str) -> Set[str]:
|
|
72
|
+
return self._radj.get(node_id, set())
|
|
73
|
+
|
|
74
|
+
# ─── Algoritmos de Grafos ────────────────────────────────────────────────
|
|
75
|
+
|
|
76
|
+
def find_cycles(self) -> List[List[str]]:
|
|
77
|
+
"""Detecta ciclos usando DFS com coloração (branco/cinza/preto)."""
|
|
78
|
+
WHITE, GRAY, BLACK = 0, 1, 2
|
|
79
|
+
color = {n: WHITE for n in self.nodes}
|
|
80
|
+
cycles = []
|
|
81
|
+
path = []
|
|
82
|
+
|
|
83
|
+
def dfs(node: str) -> None:
|
|
84
|
+
color[node] = GRAY
|
|
85
|
+
path.append(node)
|
|
86
|
+
|
|
87
|
+
for neighbor in self._adj.get(node, set()):
|
|
88
|
+
if neighbor not in self.nodes:
|
|
89
|
+
continue
|
|
90
|
+
if color[neighbor] == GRAY:
|
|
91
|
+
# Ciclo encontrado! Extrair o ciclo do path
|
|
92
|
+
cycle_start = path.index(neighbor)
|
|
93
|
+
cycle = path[cycle_start:] + [neighbor]
|
|
94
|
+
cycles.append(cycle)
|
|
95
|
+
elif color[neighbor] == WHITE:
|
|
96
|
+
dfs(neighbor)
|
|
97
|
+
|
|
98
|
+
path.pop()
|
|
99
|
+
color[node] = BLACK
|
|
100
|
+
|
|
101
|
+
for node in list(self.nodes.keys()):
|
|
102
|
+
if color[node] == WHITE:
|
|
103
|
+
dfs(node)
|
|
104
|
+
|
|
105
|
+
# Deduplicar ciclos
|
|
106
|
+
unique = []
|
|
107
|
+
seen = set()
|
|
108
|
+
for cycle in cycles:
|
|
109
|
+
key = frozenset(cycle)
|
|
110
|
+
if key not in seen:
|
|
111
|
+
seen.add(key)
|
|
112
|
+
unique.append(cycle)
|
|
113
|
+
|
|
114
|
+
return unique
|
|
115
|
+
|
|
116
|
+
def strongly_connected_components(self) -> List[List[str]]:
|
|
117
|
+
"""Algoritmo de Kosaraju para SCCs."""
|
|
118
|
+
visited = set()
|
|
119
|
+
finish_order = []
|
|
120
|
+
|
|
121
|
+
def dfs1(node: str) -> None:
|
|
122
|
+
visited.add(node)
|
|
123
|
+
for neighbor in self._adj.get(node, set()):
|
|
124
|
+
if neighbor in self.nodes and neighbor not in visited:
|
|
125
|
+
dfs1(neighbor)
|
|
126
|
+
finish_order.append(node)
|
|
127
|
+
|
|
128
|
+
def dfs2(node: str, component: List[str]) -> None:
|
|
129
|
+
visited.add(node)
|
|
130
|
+
component.append(node)
|
|
131
|
+
for neighbor in self._radj.get(node, set()):
|
|
132
|
+
if neighbor in self.nodes and neighbor not in visited:
|
|
133
|
+
dfs2(neighbor, component)
|
|
134
|
+
|
|
135
|
+
# Fase 1: DFS no grafo original
|
|
136
|
+
for node in self.nodes:
|
|
137
|
+
if node not in visited:
|
|
138
|
+
dfs1(node)
|
|
139
|
+
|
|
140
|
+
# Fase 2: DFS no grafo transposto na ordem inversa de finalização
|
|
141
|
+
visited.clear()
|
|
142
|
+
sccs = []
|
|
143
|
+
for node in reversed(finish_order):
|
|
144
|
+
if node not in visited:
|
|
145
|
+
component = []
|
|
146
|
+
dfs2(node, component)
|
|
147
|
+
sccs.append(component)
|
|
148
|
+
|
|
149
|
+
return sccs
|
|
150
|
+
|
|
151
|
+
def topological_sort(self) -> Optional[List[str]]:
|
|
152
|
+
"""Ordenação topológica (só válida se DAG)."""
|
|
153
|
+
in_degree = defaultdict(int)
|
|
154
|
+
for edge in self.edges:
|
|
155
|
+
if edge.src in self.nodes and edge.dst in self.nodes:
|
|
156
|
+
in_degree[edge.dst] += 1
|
|
157
|
+
|
|
158
|
+
queue = deque([n for n in self.nodes if in_degree[n] == 0])
|
|
159
|
+
result = []
|
|
160
|
+
|
|
161
|
+
while queue:
|
|
162
|
+
node = queue.popleft()
|
|
163
|
+
result.append(node)
|
|
164
|
+
for neighbor in self._adj.get(node, set()):
|
|
165
|
+
if neighbor in self.nodes:
|
|
166
|
+
in_degree[neighbor] -= 1
|
|
167
|
+
if in_degree[neighbor] == 0:
|
|
168
|
+
queue.append(neighbor)
|
|
169
|
+
|
|
170
|
+
if len(result) == len(self.nodes):
|
|
171
|
+
return result
|
|
172
|
+
return None # Há ciclos → não é DAG
|
|
173
|
+
|
|
174
|
+
def betweenness_centrality(self) -> Dict[str, float]:
|
|
175
|
+
"""
|
|
176
|
+
Centralidade de betweenness: nós que fazem "ponte" entre outros.
|
|
177
|
+
Alto betweenness = single point of failure.
|
|
178
|
+
|
|
179
|
+
Algoritmo de Brandes: O(V·E)
|
|
180
|
+
"""
|
|
181
|
+
betweenness = defaultdict(float)
|
|
182
|
+
nodes = list(self.nodes.keys())
|
|
183
|
+
|
|
184
|
+
for s in nodes:
|
|
185
|
+
# BFS para encontrar caminhos mais curtos de s para todos
|
|
186
|
+
stack = []
|
|
187
|
+
pred = defaultdict(list)
|
|
188
|
+
sigma = defaultdict(int)
|
|
189
|
+
sigma[s] = 1
|
|
190
|
+
dist = defaultdict(lambda: -1)
|
|
191
|
+
dist[s] = 0
|
|
192
|
+
queue = deque([s])
|
|
193
|
+
|
|
194
|
+
while queue:
|
|
195
|
+
v = queue.popleft()
|
|
196
|
+
stack.append(v)
|
|
197
|
+
for w in self._adj.get(v, set()):
|
|
198
|
+
if w not in self.nodes:
|
|
199
|
+
continue
|
|
200
|
+
if dist[w] < 0:
|
|
201
|
+
queue.append(w)
|
|
202
|
+
dist[w] = dist[v] + 1
|
|
203
|
+
if dist[w] == dist[v] + 1:
|
|
204
|
+
sigma[w] += sigma[v]
|
|
205
|
+
pred[w].append(v)
|
|
206
|
+
|
|
207
|
+
# Acumulação
|
|
208
|
+
delta = defaultdict(float)
|
|
209
|
+
while stack:
|
|
210
|
+
w = stack.pop()
|
|
211
|
+
for v in pred[w]:
|
|
212
|
+
if sigma[w] > 0:
|
|
213
|
+
delta[v] += (sigma[v] / sigma[w]) * (1 + delta[w])
|
|
214
|
+
if w != s:
|
|
215
|
+
betweenness[w] += delta[w]
|
|
216
|
+
|
|
217
|
+
# Normalizar
|
|
218
|
+
n = len(nodes)
|
|
219
|
+
if n > 2:
|
|
220
|
+
factor = 2 / ((n - 1) * (n - 2))
|
|
221
|
+
for node in betweenness:
|
|
222
|
+
betweenness[node] *= factor
|
|
223
|
+
|
|
224
|
+
return dict(betweenness)
|
|
225
|
+
|
|
226
|
+
def page_rank(self, damping: float = 0.85, iterations: int = 100) -> Dict[str, float]:
|
|
227
|
+
"""
|
|
228
|
+
PageRank para identificar classes/módulos mais "importantes".
|
|
229
|
+
Nodes com alto PageRank são frequentemente usados por outros.
|
|
230
|
+
"""
|
|
231
|
+
n = len(self.nodes)
|
|
232
|
+
if n == 0:
|
|
233
|
+
return {}
|
|
234
|
+
|
|
235
|
+
rank = {node: 1.0 / n for node in self.nodes}
|
|
236
|
+
|
|
237
|
+
for _ in range(iterations):
|
|
238
|
+
new_rank = {}
|
|
239
|
+
for node in self.nodes:
|
|
240
|
+
incoming_sum = sum(
|
|
241
|
+
rank.get(pred, 0) / max(len(self._adj.get(pred, set())), 1)
|
|
242
|
+
for pred in self._radj.get(node, set())
|
|
243
|
+
if pred in self.nodes
|
|
244
|
+
)
|
|
245
|
+
new_rank[node] = (1 - damping) / n + damping * incoming_sum
|
|
246
|
+
rank = new_rank
|
|
247
|
+
|
|
248
|
+
return rank
|
|
249
|
+
|
|
250
|
+
def coupling_metrics(self) -> Dict[str, Dict]:
|
|
251
|
+
"""Calcula Ca, Ce, instabilidade e abstração por módulo."""
|
|
252
|
+
module_nodes: Dict[str, Set[str]] = defaultdict(set)
|
|
253
|
+
for node_id, node in self.nodes.items():
|
|
254
|
+
module_nodes[node.module].add(node_id)
|
|
255
|
+
|
|
256
|
+
metrics = {}
|
|
257
|
+
for module, nodes in module_nodes.items():
|
|
258
|
+
# Ce: dependências efetivas (de fora do módulo)
|
|
259
|
+
efferent = set()
|
|
260
|
+
for n in nodes:
|
|
261
|
+
for dep in self._adj.get(n, set()):
|
|
262
|
+
if dep in self.nodes and self.nodes[dep].module != module:
|
|
263
|
+
efferent.add(self.nodes[dep].module)
|
|
264
|
+
|
|
265
|
+
# Ca: acoplamentos aferentes
|
|
266
|
+
afferent = set()
|
|
267
|
+
for n in nodes:
|
|
268
|
+
for dep in self._radj.get(n, set()):
|
|
269
|
+
if dep in self.nodes and self.nodes[dep].module != module:
|
|
270
|
+
afferent.add(self.nodes[dep].module)
|
|
271
|
+
|
|
272
|
+
ca = len(afferent)
|
|
273
|
+
ce = len(efferent)
|
|
274
|
+
instability = ce / (ca + ce) if (ca + ce) > 0 else 0.0
|
|
275
|
+
|
|
276
|
+
# Abstração: razão de interfaces/abstratas para total
|
|
277
|
+
abstracts = sum(1 for n in nodes if
|
|
278
|
+
n in self.nodes and
|
|
279
|
+
(self.nodes[n].is_abstract or self.nodes[n].kind == 'interface'))
|
|
280
|
+
abstraction = abstracts / max(len(nodes), 1)
|
|
281
|
+
|
|
282
|
+
# Distância da sequência principal: D = |A + I - 1|
|
|
283
|
+
# Sequência principal: A + I = 1 (ideal)
|
|
284
|
+
distance = abs(abstraction + instability - 1)
|
|
285
|
+
|
|
286
|
+
metrics[module] = {
|
|
287
|
+
'Ca': ca,
|
|
288
|
+
'Ce': ce,
|
|
289
|
+
'instability': round(instability, 3),
|
|
290
|
+
'abstraction': round(abstraction, 3),
|
|
291
|
+
'distance_from_main_sequence': round(distance, 3),
|
|
292
|
+
'total_classes': len(nodes),
|
|
293
|
+
'abstract_classes': abstracts,
|
|
294
|
+
'depends_on_modules': list(efferent),
|
|
295
|
+
'used_by_modules': list(afferent),
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
return metrics
|
|
299
|
+
|
|
300
|
+
|
|
301
|
+
class ProjectAnalyzer:
|
|
302
|
+
"""Analisa um projeto Kotlin/Android completo."""
|
|
303
|
+
|
|
304
|
+
def __init__(self, project_root: str):
|
|
305
|
+
self.project_root = Path(project_root)
|
|
306
|
+
self.graph = DependencyGraph()
|
|
307
|
+
self.module_files: Dict[str, List[Path]] = defaultdict(list)
|
|
308
|
+
|
|
309
|
+
def analyze(self) -> None:
|
|
310
|
+
"""Analisa todos os arquivos Kotlin."""
|
|
311
|
+
kt_files = list(self.project_root.glob("**/*.kt"))
|
|
312
|
+
|
|
313
|
+
for kt_file in kt_files:
|
|
314
|
+
module = self._detect_module(kt_file)
|
|
315
|
+
self.module_files[module].append(kt_file)
|
|
316
|
+
self._analyze_file(kt_file, module)
|
|
317
|
+
|
|
318
|
+
def _detect_module(self, file_path: Path) -> str:
|
|
319
|
+
known = ['app', 'bluetooth', 'audio', 'voice', 'llm', 'integrations', 'core-logging']
|
|
320
|
+
for part in file_path.parts:
|
|
321
|
+
if part in known:
|
|
322
|
+
return part
|
|
323
|
+
return 'unknown'
|
|
324
|
+
|
|
325
|
+
def _analyze_file(self, file_path: Path, module: str) -> None:
|
|
326
|
+
try:
|
|
327
|
+
content = file_path.read_text(encoding='utf-8', errors='ignore')
|
|
328
|
+
except Exception:
|
|
329
|
+
return
|
|
330
|
+
|
|
331
|
+
# Extrair package
|
|
332
|
+
pkg_match = re.search(r'^package\s+(.+)$', content, re.MULTILINE)
|
|
333
|
+
package = pkg_match.group(1).strip() if pkg_match else 'unknown'
|
|
334
|
+
|
|
335
|
+
# Extrair declarações de classe/interface/object
|
|
336
|
+
class_patterns = [
|
|
337
|
+
(r'(?:abstract\s+)?(?:open\s+)?(?:data\s+)?class\s+(\w+)', 'class'),
|
|
338
|
+
(r'interface\s+(\w+)', 'interface'),
|
|
339
|
+
(r'object\s+(\w+)', 'object'),
|
|
340
|
+
(r'enum\s+class\s+(\w+)', 'enum'),
|
|
341
|
+
]
|
|
342
|
+
|
|
343
|
+
for pattern, kind in class_patterns:
|
|
344
|
+
for match in re.finditer(pattern, content):
|
|
345
|
+
class_name = match.group(1)
|
|
346
|
+
node_id = f"{package}.{class_name}"
|
|
347
|
+
is_abstract = 'abstract' in content[max(0, match.start()-20):match.start()]
|
|
348
|
+
is_open = 'open' in content[max(0, match.start()-10):match.start()]
|
|
349
|
+
|
|
350
|
+
node = Node(
|
|
351
|
+
id=node_id,
|
|
352
|
+
module=module,
|
|
353
|
+
package=package,
|
|
354
|
+
kind=kind,
|
|
355
|
+
is_abstract=is_abstract,
|
|
356
|
+
is_open=is_open
|
|
357
|
+
)
|
|
358
|
+
self.graph.add_node(node)
|
|
359
|
+
|
|
360
|
+
# Extrair dependências via imports
|
|
361
|
+
imports = re.findall(r'^import\s+(.+)$', content, re.MULTILINE)
|
|
362
|
+
for imp in imports:
|
|
363
|
+
imp = imp.strip()
|
|
364
|
+
if imp.startswith('com.earllm'):
|
|
365
|
+
dep_id = imp
|
|
366
|
+
if dep_id != node_id:
|
|
367
|
+
edge = Edge(src=node_id, dst=dep_id, kind='imports')
|
|
368
|
+
self.graph.add_edge(edge)
|
|
369
|
+
|
|
370
|
+
def generate_full_report(self) -> Dict:
|
|
371
|
+
"""Gera relatório matemático completo do grafo."""
|
|
372
|
+
cycles = self.graph.find_cycles()
|
|
373
|
+
sccs = self.graph.strongly_connected_components()
|
|
374
|
+
topo = self.graph.topological_sort()
|
|
375
|
+
betweenness = self.graph.betweenness_centrality()
|
|
376
|
+
pagerank = self.graph.page_rank()
|
|
377
|
+
coupling = self.graph.coupling_metrics()
|
|
378
|
+
|
|
379
|
+
# Top nodes por betweenness (single points of failure)
|
|
380
|
+
top_betweenness = sorted(
|
|
381
|
+
betweenness.items(), key=lambda x: x[1], reverse=True
|
|
382
|
+
)[:10]
|
|
383
|
+
|
|
384
|
+
# Top nodes por pagerank (mais influentes)
|
|
385
|
+
top_pagerank = sorted(
|
|
386
|
+
pagerank.items(), key=lambda x: x[1], reverse=True
|
|
387
|
+
)[:10]
|
|
388
|
+
|
|
389
|
+
# SCCs com mais de 1 nó (ciclos reais)
|
|
390
|
+
real_sccs = [scc for scc in sccs if len(scc) > 1]
|
|
391
|
+
|
|
392
|
+
return {
|
|
393
|
+
'graph_summary': {
|
|
394
|
+
'nodes': len(self.graph.nodes),
|
|
395
|
+
'edges': len(self.graph.edges),
|
|
396
|
+
'is_dag': topo is not None,
|
|
397
|
+
'cycles_found': len(cycles),
|
|
398
|
+
'strongly_connected_components': len(real_sccs),
|
|
399
|
+
},
|
|
400
|
+
'cycles': cycles[:10], # primeiros 10 ciclos
|
|
401
|
+
'real_sccs': real_sccs[:5],
|
|
402
|
+
'topological_order': topo[:20] if topo else None,
|
|
403
|
+
'top_betweenness_centrality': [
|
|
404
|
+
{'node': n, 'betweenness': round(b, 4)} for n, b in top_betweenness
|
|
405
|
+
],
|
|
406
|
+
'top_pagerank': [
|
|
407
|
+
{'node': n, 'pagerank': round(pr, 4)} for n, pr in top_pagerank
|
|
408
|
+
],
|
|
409
|
+
'module_coupling': coupling,
|
|
410
|
+
'modules': {mod: len(files) for mod, files in self.module_files.items()},
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
def to_dot(self) -> str:
|
|
414
|
+
"""Exporta grafo em formato DOT para visualização (Graphviz)."""
|
|
415
|
+
lines = ['digraph AuriDependencies {']
|
|
416
|
+
lines.append(' rankdir=LR;')
|
|
417
|
+
lines.append(' node [shape=box, fontname="Arial"];')
|
|
418
|
+
|
|
419
|
+
# Cores por módulo
|
|
420
|
+
module_colors = {
|
|
421
|
+
'app': '#4CAF50',
|
|
422
|
+
'bluetooth': '#2196F3',
|
|
423
|
+
'audio': '#FF9800',
|
|
424
|
+
'voice': '#9C27B0',
|
|
425
|
+
'llm': '#F44336',
|
|
426
|
+
'integrations': '#00BCD4',
|
|
427
|
+
'core-logging': '#607D8B',
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
# Adicionar nós por módulo
|
|
431
|
+
modules_seen = defaultdict(list)
|
|
432
|
+
for node_id, node in self.graph.nodes.items():
|
|
433
|
+
modules_seen[node.module].append((node_id, node))
|
|
434
|
+
|
|
435
|
+
for module, nodes in modules_seen.items():
|
|
436
|
+
color = module_colors.get(module, '#9E9E9E')
|
|
437
|
+
lines.append(f' subgraph cluster_{module.replace("-", "_")} {{')
|
|
438
|
+
lines.append(f' label="{module}";')
|
|
439
|
+
lines.append(f' style=filled;')
|
|
440
|
+
lines.append(f' fillcolor="{color}20";') # 20 = ~12% opacity
|
|
441
|
+
for node_id, node in nodes[:20]: # limitar para visualização
|
|
442
|
+
short_name = node_id.split('.')[-1]
|
|
443
|
+
shape = 'diamond' if node.kind == 'interface' else 'box'
|
|
444
|
+
lines.append(f' "{node_id}" [label="{short_name}", shape={shape}];')
|
|
445
|
+
lines.append(' }')
|
|
446
|
+
|
|
447
|
+
# Adicionar arestas (amostra)
|
|
448
|
+
for edge in self.edges[:100]: # limitar para visualização
|
|
449
|
+
lines.append(f' "{edge.src}" -> "{edge.dst}" [label="{edge.kind}"];')
|
|
450
|
+
|
|
451
|
+
lines.append('}')
|
|
452
|
+
return '\n'.join(lines)
|
|
453
|
+
|
|
454
|
+
@property
|
|
455
|
+
def edges(self):
|
|
456
|
+
return self.graph.edges
|
|
457
|
+
|
|
458
|
+
def print_report(self, report: Dict) -> None:
|
|
459
|
+
print("\n" + "="*70)
|
|
460
|
+
print(" PROF. EULER — ANÁLISE DE GRAFOS DE DEPENDÊNCIAS")
|
|
461
|
+
print("="*70)
|
|
462
|
+
|
|
463
|
+
gs = report['graph_summary']
|
|
464
|
+
print(f"\n📊 RESUMO DO GRAFO G = (V={gs['nodes']}, E={gs['edges']}):")
|
|
465
|
+
print(f" É DAG (sem ciclos): {'✅ SIM' if gs['is_dag'] else '❌ NÃO'}")
|
|
466
|
+
print(f" Ciclos detectados: {gs['cycles_found']}")
|
|
467
|
+
print(f" SCCs com mais de 1 nó: {gs['strongly_connected_components']}")
|
|
468
|
+
|
|
469
|
+
if report['cycles']:
|
|
470
|
+
print(f"\n❌ CICLOS DE DEPENDÊNCIA (devem ser eliminados):")
|
|
471
|
+
for i, cycle in enumerate(report['cycles'][:5], 1):
|
|
472
|
+
print(f" {i}. {' → '.join(c.split('.')[-1] for c in cycle)}")
|
|
473
|
+
|
|
474
|
+
if report['top_betweenness_centrality']:
|
|
475
|
+
print(f"\n⚠️ TOP NÓDULOS CRÍTICOS (single points of failure — alto betweenness):")
|
|
476
|
+
for item in report['top_betweenness_centrality'][:5]:
|
|
477
|
+
short = item['node'].split('.')[-1]
|
|
478
|
+
print(f" {short:<35} betweenness={item['betweenness']:.4f}")
|
|
479
|
+
|
|
480
|
+
print(f"\n📦 ACOPLAMENTO DE MÓDULOS (Princípio de Martin):")
|
|
481
|
+
print(f" {'Módulo':<20} {'Ca':>5} {'Ce':>5} {'I':>8} {'A':>8} {'D':>8} Status")
|
|
482
|
+
print(f" {'-'*20} {'-'*5} {'-'*5} {'-'*8} {'-'*8} {'-'*8} {'-'*20}")
|
|
483
|
+
for mod, data in sorted(report['module_coupling'].items()):
|
|
484
|
+
i = data['instability']
|
|
485
|
+
a = data['abstraction']
|
|
486
|
+
d = data['distance_from_main_sequence']
|
|
487
|
+
status = "✅ OK" if d < 0.3 else ("⚠️ ZONA DE PROBLEMA" if d < 0.5 else "❌ FORA DA SEQ.")
|
|
488
|
+
print(f" {mod:<20} {data['Ca']:>5} {data['Ce']:>5} {i:>8.3f} {a:>8.3f} {d:>8.3f} {status}")
|
|
489
|
+
|
|
490
|
+
print("\n I=instabilidade, A=abstração, D=distância da sequência principal")
|
|
491
|
+
print(" D ideal < 0.3 (zona de exclusão = zona de dor/inutilidade)")
|
|
492
|
+
print("\n" + "="*70 + "\n")
|
|
493
|
+
|
|
494
|
+
|
|
495
|
+
def main():
|
|
496
|
+
parser = argparse.ArgumentParser(
|
|
497
|
+
description='Prof. Euler — Análise de Grafos de Dependências'
|
|
498
|
+
)
|
|
499
|
+
parser.add_argument('path', nargs='?',
|
|
500
|
+
default=r'C:\Users\renat\earbudllm',
|
|
501
|
+
help='Caminho raiz do projeto')
|
|
502
|
+
parser.add_argument('--format', '-f', choices=['text', 'json', 'dot'],
|
|
503
|
+
default='text', help='Formato de saída')
|
|
504
|
+
parser.add_argument('--output', '-o', help='Arquivo de saída')
|
|
505
|
+
|
|
506
|
+
args = parser.parse_args()
|
|
507
|
+
|
|
508
|
+
print(f"🔬 Prof. Euler analisando grafos: {args.path}")
|
|
509
|
+
|
|
510
|
+
analyzer = ProjectAnalyzer(args.path)
|
|
511
|
+
analyzer.analyze()
|
|
512
|
+
report = analyzer.generate_full_report()
|
|
513
|
+
|
|
514
|
+
if args.format == 'json':
|
|
515
|
+
output = json.dumps(report, indent=2, ensure_ascii=False)
|
|
516
|
+
print(output)
|
|
517
|
+
elif args.format == 'dot':
|
|
518
|
+
output = analyzer.to_dot()
|
|
519
|
+
print(output)
|
|
520
|
+
if args.output:
|
|
521
|
+
Path(args.output).write_text(output)
|
|
522
|
+
print(f"\n✅ Arquivo DOT salvo: {args.output}")
|
|
523
|
+
print(" Para visualizar: dot -Tpng deps.dot -o deps.png")
|
|
524
|
+
else:
|
|
525
|
+
analyzer.print_report(report)
|
|
526
|
+
|
|
527
|
+
if args.output and args.format != 'dot':
|
|
528
|
+
Path(args.output).write_text(
|
|
529
|
+
json.dumps(report, indent=2, ensure_ascii=False),
|
|
530
|
+
encoding='utf-8'
|
|
531
|
+
)
|
|
532
|
+
print(f"✅ Relatório salvo: {args.output}")
|
|
533
|
+
|
|
534
|
+
return report
|
|
535
|
+
|
|
536
|
+
|
|
537
|
+
if __name__ == '__main__':
|
|
538
|
+
main()
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
"""Lightweight connection handling for MCP servers."""
|
|
2
|
+
|
|
3
|
+
from abc import ABC, abstractmethod
|
|
4
|
+
from contextlib import AsyncExitStack
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
from mcp import ClientSession, StdioServerParameters
|
|
8
|
+
from mcp.client.sse import sse_client
|
|
9
|
+
from mcp.client.stdio import stdio_client
|
|
10
|
+
from mcp.client.streamable_http import streamablehttp_client
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class MCPConnection(ABC):
|
|
14
|
+
"""Base class for MCP server connections."""
|
|
15
|
+
|
|
16
|
+
def __init__(self):
|
|
17
|
+
self.session = None
|
|
18
|
+
self._stack = None
|
|
19
|
+
|
|
20
|
+
@abstractmethod
|
|
21
|
+
def _create_context(self):
|
|
22
|
+
"""Create the connection context based on connection type."""
|
|
23
|
+
|
|
24
|
+
async def __aenter__(self):
|
|
25
|
+
"""Initialize MCP server connection."""
|
|
26
|
+
self._stack = AsyncExitStack()
|
|
27
|
+
await self._stack.__aenter__()
|
|
28
|
+
|
|
29
|
+
try:
|
|
30
|
+
ctx = self._create_context()
|
|
31
|
+
result = await self._stack.enter_async_context(ctx)
|
|
32
|
+
|
|
33
|
+
if len(result) == 2:
|
|
34
|
+
read, write = result
|
|
35
|
+
elif len(result) == 3:
|
|
36
|
+
read, write, _ = result
|
|
37
|
+
else:
|
|
38
|
+
raise ValueError(f"Unexpected context result: {result}")
|
|
39
|
+
|
|
40
|
+
session_ctx = ClientSession(read, write)
|
|
41
|
+
self.session = await self._stack.enter_async_context(session_ctx)
|
|
42
|
+
await self.session.initialize()
|
|
43
|
+
return self
|
|
44
|
+
except BaseException:
|
|
45
|
+
await self._stack.__aexit__(None, None, None)
|
|
46
|
+
raise
|
|
47
|
+
|
|
48
|
+
async def __aexit__(self, exc_type, exc_val, exc_tb):
|
|
49
|
+
"""Clean up MCP server connection resources."""
|
|
50
|
+
if self._stack:
|
|
51
|
+
await self._stack.__aexit__(exc_type, exc_val, exc_tb)
|
|
52
|
+
self.session = None
|
|
53
|
+
self._stack = None
|
|
54
|
+
|
|
55
|
+
async def list_tools(self) -> list[dict[str, Any]]:
|
|
56
|
+
"""Retrieve available tools from the MCP server."""
|
|
57
|
+
response = await self.session.list_tools()
|
|
58
|
+
return [
|
|
59
|
+
{
|
|
60
|
+
"name": tool.name,
|
|
61
|
+
"description": tool.description,
|
|
62
|
+
"input_schema": tool.inputSchema,
|
|
63
|
+
}
|
|
64
|
+
for tool in response.tools
|
|
65
|
+
]
|
|
66
|
+
|
|
67
|
+
async def call_tool(self, tool_name: str, arguments: dict[str, Any]) -> Any:
|
|
68
|
+
"""Call a tool on the MCP server with provided arguments."""
|
|
69
|
+
result = await self.session.call_tool(tool_name, arguments=arguments)
|
|
70
|
+
return result.content
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
class MCPConnectionStdio(MCPConnection):
|
|
74
|
+
"""MCP connection using standard input/output."""
|
|
75
|
+
|
|
76
|
+
def __init__(self, command: str, args: list[str] = None, env: dict[str, str] = None):
|
|
77
|
+
super().__init__()
|
|
78
|
+
self.command = command
|
|
79
|
+
self.args = args or []
|
|
80
|
+
self.env = env
|
|
81
|
+
|
|
82
|
+
def _create_context(self):
|
|
83
|
+
return stdio_client(
|
|
84
|
+
StdioServerParameters(command=self.command, args=self.args, env=self.env)
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
class MCPConnectionSSE(MCPConnection):
|
|
89
|
+
"""MCP connection using Server-Sent Events."""
|
|
90
|
+
|
|
91
|
+
def __init__(self, url: str, headers: dict[str, str] = None):
|
|
92
|
+
super().__init__()
|
|
93
|
+
self.url = url
|
|
94
|
+
self.headers = headers or {}
|
|
95
|
+
|
|
96
|
+
def _create_context(self):
|
|
97
|
+
return sse_client(url=self.url, headers=self.headers)
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
class MCPConnectionHTTP(MCPConnection):
|
|
101
|
+
"""MCP connection using Streamable HTTP."""
|
|
102
|
+
|
|
103
|
+
def __init__(self, url: str, headers: dict[str, str] = None):
|
|
104
|
+
super().__init__()
|
|
105
|
+
self.url = url
|
|
106
|
+
self.headers = headers or {}
|
|
107
|
+
|
|
108
|
+
def _create_context(self):
|
|
109
|
+
return streamablehttp_client(url=self.url, headers=self.headers)
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def create_connection(
|
|
113
|
+
transport: str,
|
|
114
|
+
command: str = None,
|
|
115
|
+
args: list[str] = None,
|
|
116
|
+
env: dict[str, str] = None,
|
|
117
|
+
url: str = None,
|
|
118
|
+
headers: dict[str, str] = None,
|
|
119
|
+
) -> MCPConnection:
|
|
120
|
+
"""Factory function to create the appropriate MCP connection.
|
|
121
|
+
|
|
122
|
+
Args:
|
|
123
|
+
transport: Connection type ("stdio", "sse", or "http")
|
|
124
|
+
command: Command to run (stdio only)
|
|
125
|
+
args: Command arguments (stdio only)
|
|
126
|
+
env: Environment variables (stdio only)
|
|
127
|
+
url: Server URL (sse and http only)
|
|
128
|
+
headers: HTTP headers (sse and http only)
|
|
129
|
+
|
|
130
|
+
Returns:
|
|
131
|
+
MCPConnection instance
|
|
132
|
+
"""
|
|
133
|
+
transport = transport.lower()
|
|
134
|
+
|
|
135
|
+
if transport == "stdio":
|
|
136
|
+
if not command:
|
|
137
|
+
raise ValueError("Command is required for stdio transport")
|
|
138
|
+
return MCPConnectionStdio(command=command, args=args, env=env)
|
|
139
|
+
|
|
140
|
+
elif transport == "sse":
|
|
141
|
+
if not url:
|
|
142
|
+
raise ValueError("URL is required for sse transport")
|
|
143
|
+
return MCPConnectionSSE(url=url, headers=headers)
|
|
144
|
+
|
|
145
|
+
elif transport in ["http", "streamable_http", "streamable-http"]:
|
|
146
|
+
if not url:
|
|
147
|
+
raise ValueError("URL is required for http transport")
|
|
148
|
+
return MCPConnectionHTTP(url=url, headers=headers)
|
|
149
|
+
|
|
150
|
+
else:
|
|
151
|
+
raise ValueError(f"Unsupported transport type: {transport}. Use 'stdio', 'sse', or 'http'")
|