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,175 @@
|
|
|
1
|
+
"""Model auto-selection for last30days skill."""
|
|
2
|
+
|
|
3
|
+
import re
|
|
4
|
+
from typing import Dict, List, Optional, Tuple
|
|
5
|
+
|
|
6
|
+
from . import cache, http
|
|
7
|
+
|
|
8
|
+
# OpenAI API
|
|
9
|
+
OPENAI_MODELS_URL = "https://api.openai.com/v1/models"
|
|
10
|
+
OPENAI_FALLBACK_MODELS = ["gpt-5.2", "gpt-5.1", "gpt-5", "gpt-4o"]
|
|
11
|
+
|
|
12
|
+
# xAI API - Agent Tools API requires grok-4 family
|
|
13
|
+
XAI_MODELS_URL = "https://api.x.ai/v1/models"
|
|
14
|
+
XAI_ALIASES = {
|
|
15
|
+
"latest": "grok-4-1-fast", # Required for x_search tool
|
|
16
|
+
"stable": "grok-4-1-fast",
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def parse_version(model_id: str) -> Optional[Tuple[int, ...]]:
|
|
21
|
+
"""Parse semantic version from model ID.
|
|
22
|
+
|
|
23
|
+
Examples:
|
|
24
|
+
gpt-5 -> (5,)
|
|
25
|
+
gpt-5.2 -> (5, 2)
|
|
26
|
+
gpt-5.2.1 -> (5, 2, 1)
|
|
27
|
+
"""
|
|
28
|
+
match = re.search(r'(\d+(?:\.\d+)*)', model_id)
|
|
29
|
+
if match:
|
|
30
|
+
return tuple(int(x) for x in match.group(1).split('.'))
|
|
31
|
+
return None
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def is_mainline_openai_model(model_id: str) -> bool:
|
|
35
|
+
"""Check if model is a mainline GPT model (not mini/nano/chat/codex/pro)."""
|
|
36
|
+
model_lower = model_id.lower()
|
|
37
|
+
|
|
38
|
+
# Must be gpt-5 series
|
|
39
|
+
if not re.match(r'^gpt-5(\.\d+)*$', model_lower):
|
|
40
|
+
return False
|
|
41
|
+
|
|
42
|
+
# Exclude variants
|
|
43
|
+
excludes = ['mini', 'nano', 'chat', 'codex', 'pro', 'preview', 'turbo']
|
|
44
|
+
for exc in excludes:
|
|
45
|
+
if exc in model_lower:
|
|
46
|
+
return False
|
|
47
|
+
|
|
48
|
+
return True
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def select_openai_model(
|
|
52
|
+
api_key: str,
|
|
53
|
+
policy: str = "auto",
|
|
54
|
+
pin: Optional[str] = None,
|
|
55
|
+
mock_models: Optional[List[Dict]] = None,
|
|
56
|
+
) -> str:
|
|
57
|
+
"""Select the best OpenAI model based on policy.
|
|
58
|
+
|
|
59
|
+
Args:
|
|
60
|
+
api_key: OpenAI API key
|
|
61
|
+
policy: 'auto' or 'pinned'
|
|
62
|
+
pin: Model to use if policy is 'pinned'
|
|
63
|
+
mock_models: Mock model list for testing
|
|
64
|
+
|
|
65
|
+
Returns:
|
|
66
|
+
Selected model ID
|
|
67
|
+
"""
|
|
68
|
+
if policy == "pinned" and pin:
|
|
69
|
+
return pin
|
|
70
|
+
|
|
71
|
+
# Check cache first
|
|
72
|
+
cached = cache.get_cached_model("openai")
|
|
73
|
+
if cached:
|
|
74
|
+
return cached
|
|
75
|
+
|
|
76
|
+
# Fetch model list
|
|
77
|
+
if mock_models is not None:
|
|
78
|
+
models = mock_models
|
|
79
|
+
else:
|
|
80
|
+
try:
|
|
81
|
+
headers = {"Authorization": f"Bearer {api_key}"}
|
|
82
|
+
response = http.get(OPENAI_MODELS_URL, headers=headers)
|
|
83
|
+
models = response.get("data", [])
|
|
84
|
+
except http.HTTPError:
|
|
85
|
+
# Fall back to known models
|
|
86
|
+
return OPENAI_FALLBACK_MODELS[0]
|
|
87
|
+
|
|
88
|
+
# Filter to mainline models
|
|
89
|
+
candidates = [m for m in models if is_mainline_openai_model(m.get("id", ""))]
|
|
90
|
+
|
|
91
|
+
if not candidates:
|
|
92
|
+
# No gpt-5 models found, use fallback
|
|
93
|
+
return OPENAI_FALLBACK_MODELS[0]
|
|
94
|
+
|
|
95
|
+
# Sort by version (descending), then by created timestamp
|
|
96
|
+
def sort_key(m):
|
|
97
|
+
version = parse_version(m.get("id", "")) or (0,)
|
|
98
|
+
created = m.get("created", 0)
|
|
99
|
+
return (version, created)
|
|
100
|
+
|
|
101
|
+
candidates.sort(key=sort_key, reverse=True)
|
|
102
|
+
selected = candidates[0]["id"]
|
|
103
|
+
|
|
104
|
+
# Cache the selection
|
|
105
|
+
cache.set_cached_model("openai", selected)
|
|
106
|
+
|
|
107
|
+
return selected
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def select_xai_model(
|
|
111
|
+
api_key: str,
|
|
112
|
+
policy: str = "latest",
|
|
113
|
+
pin: Optional[str] = None,
|
|
114
|
+
mock_models: Optional[List[Dict]] = None,
|
|
115
|
+
) -> str:
|
|
116
|
+
"""Select the best xAI model based on policy.
|
|
117
|
+
|
|
118
|
+
Args:
|
|
119
|
+
api_key: xAI API key
|
|
120
|
+
policy: 'latest', 'stable', or 'pinned'
|
|
121
|
+
pin: Model to use if policy is 'pinned'
|
|
122
|
+
mock_models: Mock model list for testing
|
|
123
|
+
|
|
124
|
+
Returns:
|
|
125
|
+
Selected model ID
|
|
126
|
+
"""
|
|
127
|
+
if policy == "pinned" and pin:
|
|
128
|
+
return pin
|
|
129
|
+
|
|
130
|
+
# Use alias system
|
|
131
|
+
if policy in XAI_ALIASES:
|
|
132
|
+
alias = XAI_ALIASES[policy]
|
|
133
|
+
|
|
134
|
+
# Check cache first
|
|
135
|
+
cached = cache.get_cached_model("xai")
|
|
136
|
+
if cached:
|
|
137
|
+
return cached
|
|
138
|
+
|
|
139
|
+
# Cache the alias
|
|
140
|
+
cache.set_cached_model("xai", alias)
|
|
141
|
+
return alias
|
|
142
|
+
|
|
143
|
+
# Default to latest
|
|
144
|
+
return XAI_ALIASES["latest"]
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def get_models(
|
|
148
|
+
config: Dict,
|
|
149
|
+
mock_openai_models: Optional[List[Dict]] = None,
|
|
150
|
+
mock_xai_models: Optional[List[Dict]] = None,
|
|
151
|
+
) -> Dict[str, Optional[str]]:
|
|
152
|
+
"""Get selected models for both providers.
|
|
153
|
+
|
|
154
|
+
Returns:
|
|
155
|
+
Dict with 'openai' and 'xai' keys
|
|
156
|
+
"""
|
|
157
|
+
result = {"openai": None, "xai": None}
|
|
158
|
+
|
|
159
|
+
if config.get("OPENAI_API_KEY"):
|
|
160
|
+
result["openai"] = select_openai_model(
|
|
161
|
+
config["OPENAI_API_KEY"],
|
|
162
|
+
config.get("OPENAI_MODEL_POLICY", "auto"),
|
|
163
|
+
config.get("OPENAI_MODEL_PIN"),
|
|
164
|
+
mock_openai_models,
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
if config.get("XAI_API_KEY"):
|
|
168
|
+
result["xai"] = select_xai_model(
|
|
169
|
+
config["XAI_API_KEY"],
|
|
170
|
+
config.get("XAI_MODEL_POLICY", "latest"),
|
|
171
|
+
config.get("XAI_MODEL_PIN"),
|
|
172
|
+
mock_xai_models,
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
return result
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
"""Normalization of raw API data to canonical schema."""
|
|
2
|
+
|
|
3
|
+
from typing import Any, Dict, List, TypeVar, Union
|
|
4
|
+
|
|
5
|
+
from . import dates, schema
|
|
6
|
+
|
|
7
|
+
T = TypeVar("T", schema.RedditItem, schema.XItem, schema.WebSearchItem)
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def filter_by_date_range(
|
|
11
|
+
items: List[T],
|
|
12
|
+
from_date: str,
|
|
13
|
+
to_date: str,
|
|
14
|
+
require_date: bool = False,
|
|
15
|
+
) -> List[T]:
|
|
16
|
+
"""Hard filter: Remove items outside the date range.
|
|
17
|
+
|
|
18
|
+
This is the safety net - even if the prompt lets old content through,
|
|
19
|
+
this filter will exclude it.
|
|
20
|
+
|
|
21
|
+
Args:
|
|
22
|
+
items: List of items to filter
|
|
23
|
+
from_date: Start date (YYYY-MM-DD) - exclude items before this
|
|
24
|
+
to_date: End date (YYYY-MM-DD) - exclude items after this
|
|
25
|
+
require_date: If True, also remove items with no date
|
|
26
|
+
|
|
27
|
+
Returns:
|
|
28
|
+
Filtered list with only items in range (or unknown dates if not required)
|
|
29
|
+
"""
|
|
30
|
+
result = []
|
|
31
|
+
for item in items:
|
|
32
|
+
if item.date is None:
|
|
33
|
+
if not require_date:
|
|
34
|
+
result.append(item) # Keep unknown dates (with scoring penalty)
|
|
35
|
+
continue
|
|
36
|
+
|
|
37
|
+
# Hard filter: if date is before from_date, exclude
|
|
38
|
+
if item.date < from_date:
|
|
39
|
+
continue # DROP - too old
|
|
40
|
+
|
|
41
|
+
# Hard filter: if date is after to_date, exclude (likely parsing error)
|
|
42
|
+
if item.date > to_date:
|
|
43
|
+
continue # DROP - future date
|
|
44
|
+
|
|
45
|
+
result.append(item)
|
|
46
|
+
|
|
47
|
+
return result
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def normalize_reddit_items(
|
|
51
|
+
items: List[Dict[str, Any]],
|
|
52
|
+
from_date: str,
|
|
53
|
+
to_date: str,
|
|
54
|
+
) -> List[schema.RedditItem]:
|
|
55
|
+
"""Normalize raw Reddit items to schema.
|
|
56
|
+
|
|
57
|
+
Args:
|
|
58
|
+
items: Raw Reddit items from API
|
|
59
|
+
from_date: Start of date range
|
|
60
|
+
to_date: End of date range
|
|
61
|
+
|
|
62
|
+
Returns:
|
|
63
|
+
List of RedditItem objects
|
|
64
|
+
"""
|
|
65
|
+
normalized = []
|
|
66
|
+
|
|
67
|
+
for item in items:
|
|
68
|
+
# Parse engagement
|
|
69
|
+
engagement = None
|
|
70
|
+
eng_raw = item.get("engagement")
|
|
71
|
+
if isinstance(eng_raw, dict):
|
|
72
|
+
engagement = schema.Engagement(
|
|
73
|
+
score=eng_raw.get("score"),
|
|
74
|
+
num_comments=eng_raw.get("num_comments"),
|
|
75
|
+
upvote_ratio=eng_raw.get("upvote_ratio"),
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
# Parse comments
|
|
79
|
+
top_comments = []
|
|
80
|
+
for c in item.get("top_comments", []):
|
|
81
|
+
top_comments.append(schema.Comment(
|
|
82
|
+
score=c.get("score", 0),
|
|
83
|
+
date=c.get("date"),
|
|
84
|
+
author=c.get("author", ""),
|
|
85
|
+
excerpt=c.get("excerpt", ""),
|
|
86
|
+
url=c.get("url", ""),
|
|
87
|
+
))
|
|
88
|
+
|
|
89
|
+
# Determine date confidence
|
|
90
|
+
date_str = item.get("date")
|
|
91
|
+
date_confidence = dates.get_date_confidence(date_str, from_date, to_date)
|
|
92
|
+
|
|
93
|
+
normalized.append(schema.RedditItem(
|
|
94
|
+
id=item.get("id", ""),
|
|
95
|
+
title=item.get("title", ""),
|
|
96
|
+
url=item.get("url", ""),
|
|
97
|
+
subreddit=item.get("subreddit", ""),
|
|
98
|
+
date=date_str,
|
|
99
|
+
date_confidence=date_confidence,
|
|
100
|
+
engagement=engagement,
|
|
101
|
+
top_comments=top_comments,
|
|
102
|
+
comment_insights=item.get("comment_insights", []),
|
|
103
|
+
relevance=item.get("relevance", 0.5),
|
|
104
|
+
why_relevant=item.get("why_relevant", ""),
|
|
105
|
+
))
|
|
106
|
+
|
|
107
|
+
return normalized
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def normalize_x_items(
|
|
111
|
+
items: List[Dict[str, Any]],
|
|
112
|
+
from_date: str,
|
|
113
|
+
to_date: str,
|
|
114
|
+
) -> List[schema.XItem]:
|
|
115
|
+
"""Normalize raw X items to schema.
|
|
116
|
+
|
|
117
|
+
Args:
|
|
118
|
+
items: Raw X items from API
|
|
119
|
+
from_date: Start of date range
|
|
120
|
+
to_date: End of date range
|
|
121
|
+
|
|
122
|
+
Returns:
|
|
123
|
+
List of XItem objects
|
|
124
|
+
"""
|
|
125
|
+
normalized = []
|
|
126
|
+
|
|
127
|
+
for item in items:
|
|
128
|
+
# Parse engagement
|
|
129
|
+
engagement = None
|
|
130
|
+
eng_raw = item.get("engagement")
|
|
131
|
+
if isinstance(eng_raw, dict):
|
|
132
|
+
engagement = schema.Engagement(
|
|
133
|
+
likes=eng_raw.get("likes"),
|
|
134
|
+
reposts=eng_raw.get("reposts"),
|
|
135
|
+
replies=eng_raw.get("replies"),
|
|
136
|
+
quotes=eng_raw.get("quotes"),
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
# Determine date confidence
|
|
140
|
+
date_str = item.get("date")
|
|
141
|
+
date_confidence = dates.get_date_confidence(date_str, from_date, to_date)
|
|
142
|
+
|
|
143
|
+
normalized.append(schema.XItem(
|
|
144
|
+
id=item.get("id", ""),
|
|
145
|
+
text=item.get("text", ""),
|
|
146
|
+
url=item.get("url", ""),
|
|
147
|
+
author_handle=item.get("author_handle", ""),
|
|
148
|
+
date=date_str,
|
|
149
|
+
date_confidence=date_confidence,
|
|
150
|
+
engagement=engagement,
|
|
151
|
+
relevance=item.get("relevance", 0.5),
|
|
152
|
+
why_relevant=item.get("why_relevant", ""),
|
|
153
|
+
))
|
|
154
|
+
|
|
155
|
+
return normalized
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
def items_to_dicts(items: List) -> List[Dict[str, Any]]:
|
|
159
|
+
"""Convert schema items to dicts for JSON serialization."""
|
|
160
|
+
return [item.to_dict() for item in items]
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
"""OpenAI Responses API client for Reddit discovery."""
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
import re
|
|
5
|
+
import sys
|
|
6
|
+
from typing import Any, Dict, List, Optional
|
|
7
|
+
|
|
8
|
+
from . import http
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def _log_error(msg: str):
|
|
12
|
+
"""Log error to stderr."""
|
|
13
|
+
sys.stderr.write(f"[REDDIT ERROR] {msg}\n")
|
|
14
|
+
sys.stderr.flush()
|
|
15
|
+
|
|
16
|
+
OPENAI_RESPONSES_URL = "https://api.openai.com/v1/responses"
|
|
17
|
+
|
|
18
|
+
# Depth configurations: (min, max) threads to request
|
|
19
|
+
# Request MORE than needed since many get filtered by date
|
|
20
|
+
DEPTH_CONFIG = {
|
|
21
|
+
"quick": (15, 25),
|
|
22
|
+
"default": (30, 50),
|
|
23
|
+
"deep": (70, 100),
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
REDDIT_SEARCH_PROMPT = """Find Reddit discussion threads about: {topic}
|
|
27
|
+
|
|
28
|
+
STEP 1: EXTRACT THE CORE SUBJECT
|
|
29
|
+
Get the MAIN NOUN/PRODUCT/TOPIC:
|
|
30
|
+
- "best nano banana prompting practices" → "nano banana"
|
|
31
|
+
- "killer features of clawdbot" → "clawdbot"
|
|
32
|
+
- "top Claude Code skills" → "Claude Code"
|
|
33
|
+
DO NOT include "best", "top", "tips", "practices", "features" in your search.
|
|
34
|
+
|
|
35
|
+
STEP 2: SEARCH BROADLY
|
|
36
|
+
Search for the core subject:
|
|
37
|
+
1. "[core subject] site:reddit.com"
|
|
38
|
+
2. "reddit [core subject]"
|
|
39
|
+
3. "[core subject] reddit"
|
|
40
|
+
|
|
41
|
+
Return as many relevant threads as you find. We filter by date server-side.
|
|
42
|
+
|
|
43
|
+
STEP 3: INCLUDE ALL MATCHES
|
|
44
|
+
- Include ALL threads about the core subject
|
|
45
|
+
- Set date to "YYYY-MM-DD" if you can determine it, otherwise null
|
|
46
|
+
- We verify dates and filter old content server-side
|
|
47
|
+
- DO NOT pre-filter aggressively - include anything relevant
|
|
48
|
+
|
|
49
|
+
REQUIRED: URLs must contain "/r/" AND "/comments/"
|
|
50
|
+
REJECT: developers.reddit.com, business.reddit.com
|
|
51
|
+
|
|
52
|
+
Find {min_items}-{max_items} threads. Return MORE rather than fewer.
|
|
53
|
+
|
|
54
|
+
Return JSON:
|
|
55
|
+
{{
|
|
56
|
+
"items": [
|
|
57
|
+
{{
|
|
58
|
+
"title": "Thread title",
|
|
59
|
+
"url": "https://www.reddit.com/r/sub/comments/xyz/title/",
|
|
60
|
+
"subreddit": "subreddit_name",
|
|
61
|
+
"date": "YYYY-MM-DD or null",
|
|
62
|
+
"why_relevant": "Why relevant",
|
|
63
|
+
"relevance": 0.85
|
|
64
|
+
}}
|
|
65
|
+
]
|
|
66
|
+
}}"""
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def _extract_core_subject(topic: str) -> str:
|
|
70
|
+
"""Extract core subject from verbose query for retry."""
|
|
71
|
+
noise = ['best', 'top', 'how to', 'tips for', 'practices', 'features',
|
|
72
|
+
'killer', 'guide', 'tutorial', 'recommendations', 'advice',
|
|
73
|
+
'prompting', 'using', 'for', 'with', 'the', 'of', 'in', 'on']
|
|
74
|
+
words = topic.lower().split()
|
|
75
|
+
result = [w for w in words if w not in noise]
|
|
76
|
+
return ' '.join(result[:3]) or topic # Keep max 3 words
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def search_reddit(
|
|
80
|
+
api_key: str,
|
|
81
|
+
model: str,
|
|
82
|
+
topic: str,
|
|
83
|
+
from_date: str,
|
|
84
|
+
to_date: str,
|
|
85
|
+
depth: str = "default",
|
|
86
|
+
mock_response: Optional[Dict] = None,
|
|
87
|
+
_retry: bool = False,
|
|
88
|
+
) -> Dict[str, Any]:
|
|
89
|
+
"""Search Reddit for relevant threads using OpenAI Responses API.
|
|
90
|
+
|
|
91
|
+
Args:
|
|
92
|
+
api_key: OpenAI API key
|
|
93
|
+
model: Model to use
|
|
94
|
+
topic: Search topic
|
|
95
|
+
from_date: Start date (YYYY-MM-DD) - only include threads after this
|
|
96
|
+
to_date: End date (YYYY-MM-DD) - only include threads before this
|
|
97
|
+
depth: Research depth - "quick", "default", or "deep"
|
|
98
|
+
mock_response: Mock response for testing
|
|
99
|
+
|
|
100
|
+
Returns:
|
|
101
|
+
Raw API response
|
|
102
|
+
"""
|
|
103
|
+
if mock_response is not None:
|
|
104
|
+
return mock_response
|
|
105
|
+
|
|
106
|
+
min_items, max_items = DEPTH_CONFIG.get(depth, DEPTH_CONFIG["default"])
|
|
107
|
+
|
|
108
|
+
headers = {
|
|
109
|
+
"Authorization": f"Bearer {api_key}",
|
|
110
|
+
"Content-Type": "application/json",
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
# Adjust timeout based on depth (generous for OpenAI web_search which can be slow)
|
|
114
|
+
timeout = 90 if depth == "quick" else 120 if depth == "default" else 180
|
|
115
|
+
|
|
116
|
+
# Note: allowed_domains accepts base domain, not subdomains
|
|
117
|
+
# We rely on prompt to filter out developers.reddit.com, etc.
|
|
118
|
+
payload = {
|
|
119
|
+
"model": model,
|
|
120
|
+
"tools": [
|
|
121
|
+
{
|
|
122
|
+
"type": "web_search",
|
|
123
|
+
"filters": {
|
|
124
|
+
"allowed_domains": ["reddit.com"]
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
],
|
|
128
|
+
"include": ["web_search_call.action.sources"],
|
|
129
|
+
"input": REDDIT_SEARCH_PROMPT.format(
|
|
130
|
+
topic=topic,
|
|
131
|
+
from_date=from_date,
|
|
132
|
+
to_date=to_date,
|
|
133
|
+
min_items=min_items,
|
|
134
|
+
max_items=max_items,
|
|
135
|
+
),
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
return http.post(OPENAI_RESPONSES_URL, payload, headers=headers, timeout=timeout)
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def parse_reddit_response(response: Dict[str, Any]) -> List[Dict[str, Any]]:
|
|
142
|
+
"""Parse OpenAI response to extract Reddit items.
|
|
143
|
+
|
|
144
|
+
Args:
|
|
145
|
+
response: Raw API response
|
|
146
|
+
|
|
147
|
+
Returns:
|
|
148
|
+
List of item dicts
|
|
149
|
+
"""
|
|
150
|
+
items = []
|
|
151
|
+
|
|
152
|
+
# Check for API errors first
|
|
153
|
+
if "error" in response and response["error"]:
|
|
154
|
+
error = response["error"]
|
|
155
|
+
err_msg = error.get("message", str(error)) if isinstance(error, dict) else str(error)
|
|
156
|
+
_log_error(f"OpenAI API error: {err_msg}")
|
|
157
|
+
if http.DEBUG:
|
|
158
|
+
_log_error(f"Full error response: {json.dumps(response, indent=2)[:1000]}")
|
|
159
|
+
return items
|
|
160
|
+
|
|
161
|
+
# Try to find the output text
|
|
162
|
+
output_text = ""
|
|
163
|
+
if "output" in response:
|
|
164
|
+
output = response["output"]
|
|
165
|
+
if isinstance(output, str):
|
|
166
|
+
output_text = output
|
|
167
|
+
elif isinstance(output, list):
|
|
168
|
+
for item in output:
|
|
169
|
+
if isinstance(item, dict):
|
|
170
|
+
if item.get("type") == "message":
|
|
171
|
+
content = item.get("content", [])
|
|
172
|
+
for c in content:
|
|
173
|
+
if isinstance(c, dict) and c.get("type") == "output_text":
|
|
174
|
+
output_text = c.get("text", "")
|
|
175
|
+
break
|
|
176
|
+
elif "text" in item:
|
|
177
|
+
output_text = item["text"]
|
|
178
|
+
elif isinstance(item, str):
|
|
179
|
+
output_text = item
|
|
180
|
+
if output_text:
|
|
181
|
+
break
|
|
182
|
+
|
|
183
|
+
# Also check for choices (older format)
|
|
184
|
+
if not output_text and "choices" in response:
|
|
185
|
+
for choice in response["choices"]:
|
|
186
|
+
if "message" in choice:
|
|
187
|
+
output_text = choice["message"].get("content", "")
|
|
188
|
+
break
|
|
189
|
+
|
|
190
|
+
if not output_text:
|
|
191
|
+
print(f"[REDDIT WARNING] No output text found in OpenAI response. Keys present: {list(response.keys())}", flush=True)
|
|
192
|
+
return items
|
|
193
|
+
|
|
194
|
+
# Extract JSON from the response
|
|
195
|
+
json_match = re.search(r'\{[\s\S]*"items"[\s\S]*\}', output_text)
|
|
196
|
+
if json_match:
|
|
197
|
+
try:
|
|
198
|
+
data = json.loads(json_match.group())
|
|
199
|
+
items = data.get("items", [])
|
|
200
|
+
except json.JSONDecodeError:
|
|
201
|
+
pass
|
|
202
|
+
|
|
203
|
+
# Validate and clean items
|
|
204
|
+
clean_items = []
|
|
205
|
+
for i, item in enumerate(items):
|
|
206
|
+
if not isinstance(item, dict):
|
|
207
|
+
continue
|
|
208
|
+
|
|
209
|
+
url = item.get("url", "")
|
|
210
|
+
if not url or "reddit.com" not in url:
|
|
211
|
+
continue
|
|
212
|
+
|
|
213
|
+
clean_item = {
|
|
214
|
+
"id": f"R{i+1}",
|
|
215
|
+
"title": str(item.get("title", "")).strip(),
|
|
216
|
+
"url": url,
|
|
217
|
+
"subreddit": str(item.get("subreddit", "")).strip().lstrip("r/"),
|
|
218
|
+
"date": item.get("date"),
|
|
219
|
+
"why_relevant": str(item.get("why_relevant", "")).strip(),
|
|
220
|
+
"relevance": min(1.0, max(0.0, float(item.get("relevance", 0.5)))),
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
# Validate date format
|
|
224
|
+
if clean_item["date"]:
|
|
225
|
+
if not re.match(r'^\d{4}-\d{2}-\d{2}$', str(clean_item["date"])):
|
|
226
|
+
clean_item["date"] = None
|
|
227
|
+
|
|
228
|
+
clean_items.append(clean_item)
|
|
229
|
+
|
|
230
|
+
return clean_items
|