@elizaos/skills 2.0.0-alpha.3
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/README.md +126 -0
- package/package.json +53 -0
- package/skills/1password/SKILL.md +70 -0
- package/skills/1password/references/cli-examples.md +29 -0
- package/skills/1password/references/get-started.md +17 -0
- package/skills/apple-notes/SKILL.md +77 -0
- package/skills/apple-reminders/SKILL.md +96 -0
- package/skills/bear-notes/SKILL.md +107 -0
- package/skills/bird/SKILL.md +224 -0
- package/skills/blogwatcher/SKILL.md +69 -0
- package/skills/blucli/SKILL.md +47 -0
- package/skills/bluebubbles/SKILL.md +131 -0
- package/skills/camsnap/SKILL.md +45 -0
- package/skills/canvas/SKILL.md +203 -0
- package/skills/clawhub/SKILL.md +77 -0
- package/skills/coding-agent/SKILL.md +284 -0
- package/skills/discord/SKILL.md +578 -0
- package/skills/eightctl/SKILL.md +50 -0
- package/skills/food-order/SKILL.md +48 -0
- package/skills/gemini/SKILL.md +43 -0
- package/skills/gifgrep/SKILL.md +79 -0
- package/skills/github/SKILL.md +77 -0
- package/skills/gog/SKILL.md +116 -0
- package/skills/goplaces/SKILL.md +52 -0
- package/skills/healthcheck/SKILL.md +245 -0
- package/skills/himalaya/SKILL.md +257 -0
- package/skills/himalaya/references/configuration.md +184 -0
- package/skills/himalaya/references/message-composition.md +199 -0
- package/skills/imsg/SKILL.md +74 -0
- package/skills/local-places/SERVER_README.md +101 -0
- package/skills/local-places/SKILL.md +102 -0
- package/skills/local-places/pyproject.toml +21 -0
- package/skills/local-places/src/local_places/__init__.py +2 -0
- package/skills/local-places/src/local_places/google_places.py +314 -0
- package/skills/local-places/src/local_places/main.py +65 -0
- package/skills/local-places/src/local_places/schemas.py +107 -0
- package/skills/mcporter/SKILL.md +61 -0
- package/skills/model-usage/SKILL.md +69 -0
- package/skills/model-usage/references/codexbar-cli.md +33 -0
- package/skills/model-usage/scripts/model_usage.py +310 -0
- package/skills/nano-banana-pro/SKILL.md +58 -0
- package/skills/nano-banana-pro/scripts/generate_image.py +184 -0
- package/skills/nano-pdf/SKILL.md +38 -0
- package/skills/notion/SKILL.md +172 -0
- package/skills/obsidian/SKILL.md +81 -0
- package/skills/openai-image-gen/SKILL.md +89 -0
- package/skills/openai-image-gen/scripts/gen.py +240 -0
- package/skills/openai-whisper/SKILL.md +38 -0
- package/skills/openai-whisper-api/SKILL.md +52 -0
- package/skills/openai-whisper-api/scripts/transcribe.sh +85 -0
- package/skills/openhue/SKILL.md +51 -0
- package/skills/oracle/SKILL.md +125 -0
- package/skills/ordercli/SKILL.md +78 -0
- package/skills/peekaboo/SKILL.md +190 -0
- package/skills/sag/SKILL.md +87 -0
- package/skills/security-ask-questions-if-underspecified/.claude-plugin/plugin.json +10 -0
- package/skills/security-ask-questions-if-underspecified/README.md +24 -0
- package/skills/security-ask-questions-if-underspecified/skills/ask-questions-if-underspecified/SKILL.md +85 -0
- package/skills/security-audit-context-building/.claude-plugin/plugin.json +10 -0
- package/skills/security-audit-context-building/README.md +58 -0
- package/skills/security-audit-context-building/commands/audit-context.md +21 -0
- package/skills/security-audit-context-building/skills/audit-context-building/SKILL.md +297 -0
- package/skills/security-audit-context-building/skills/audit-context-building/resources/COMPLETENESS_CHECKLIST.md +47 -0
- package/skills/security-audit-context-building/skills/audit-context-building/resources/FUNCTION_MICRO_ANALYSIS_EXAMPLE.md +355 -0
- package/skills/security-audit-context-building/skills/audit-context-building/resources/OUTPUT_REQUIREMENTS.md +71 -0
- package/skills/security-building-secure-contracts/.claude-plugin/plugin.json +10 -0
- package/skills/security-building-secure-contracts/README.md +241 -0
- package/skills/security-building-secure-contracts/skills/algorand-vulnerability-scanner/SKILL.md +284 -0
- package/skills/security-building-secure-contracts/skills/algorand-vulnerability-scanner/resources/VULNERABILITY_PATTERNS.md +405 -0
- package/skills/security-building-secure-contracts/skills/audit-prep-assistant/SKILL.md +409 -0
- package/skills/security-building-secure-contracts/skills/cairo-vulnerability-scanner/SKILL.md +329 -0
- package/skills/security-building-secure-contracts/skills/cairo-vulnerability-scanner/resources/VULNERABILITY_PATTERNS.md +722 -0
- package/skills/security-building-secure-contracts/skills/code-maturity-assessor/SKILL.md +218 -0
- package/skills/security-building-secure-contracts/skills/code-maturity-assessor/resources/ASSESSMENT_CRITERIA.md +355 -0
- package/skills/security-building-secure-contracts/skills/code-maturity-assessor/resources/EXAMPLE_REPORT.md +248 -0
- package/skills/security-building-secure-contracts/skills/code-maturity-assessor/resources/REPORT_FORMAT.md +33 -0
- package/skills/security-building-secure-contracts/skills/cosmos-vulnerability-scanner/SKILL.md +334 -0
- package/skills/security-building-secure-contracts/skills/cosmos-vulnerability-scanner/resources/VULNERABILITY_PATTERNS.md +740 -0
- package/skills/security-building-secure-contracts/skills/guidelines-advisor/SKILL.md +252 -0
- package/skills/security-building-secure-contracts/skills/guidelines-advisor/resources/ASSESSMENT_AREAS.md +329 -0
- package/skills/security-building-secure-contracts/skills/guidelines-advisor/resources/DELIVERABLES.md +118 -0
- package/skills/security-building-secure-contracts/skills/guidelines-advisor/resources/EXAMPLE_REPORT.md +298 -0
- package/skills/security-building-secure-contracts/skills/secure-workflow-guide/SKILL.md +161 -0
- package/skills/security-building-secure-contracts/skills/secure-workflow-guide/resources/EXAMPLE_REPORT.md +279 -0
- package/skills/security-building-secure-contracts/skills/secure-workflow-guide/resources/WORKFLOW_STEPS.md +132 -0
- package/skills/security-building-secure-contracts/skills/solana-vulnerability-scanner/SKILL.md +389 -0
- package/skills/security-building-secure-contracts/skills/solana-vulnerability-scanner/resources/VULNERABILITY_PATTERNS.md +669 -0
- package/skills/security-building-secure-contracts/skills/substrate-vulnerability-scanner/SKILL.md +298 -0
- package/skills/security-building-secure-contracts/skills/substrate-vulnerability-scanner/resources/VULNERABILITY_PATTERNS.md +791 -0
- package/skills/security-building-secure-contracts/skills/token-integration-analyzer/SKILL.md +362 -0
- package/skills/security-building-secure-contracts/skills/token-integration-analyzer/resources/ASSESSMENT_CATEGORIES.md +571 -0
- package/skills/security-building-secure-contracts/skills/token-integration-analyzer/resources/REPORT_TEMPLATES.md +141 -0
- package/skills/security-building-secure-contracts/skills/ton-vulnerability-scanner/SKILL.md +388 -0
- package/skills/security-building-secure-contracts/skills/ton-vulnerability-scanner/resources/VULNERABILITY_PATTERNS.md +595 -0
- package/skills/security-burpsuite-project-parser/.claude-plugin/plugin.json +10 -0
- package/skills/security-burpsuite-project-parser/README.md +103 -0
- package/skills/security-burpsuite-project-parser/commands/burp-search.md +18 -0
- package/skills/security-burpsuite-project-parser/skills/SKILL.md +358 -0
- package/skills/security-burpsuite-project-parser/skills/scripts/burp-search.sh +99 -0
- package/skills/security-claude-in-chrome-troubleshooting/.claude-plugin/plugin.json +8 -0
- package/skills/security-claude-in-chrome-troubleshooting/README.md +31 -0
- package/skills/security-claude-in-chrome-troubleshooting/skills/claude-in-chrome-troubleshooting/SKILL.md +251 -0
- package/skills/security-constant-time-analysis/.claude-plugin/plugin.json +9 -0
- package/skills/security-constant-time-analysis/README.md +381 -0
- package/skills/security-constant-time-analysis/commands/ct-check.md +20 -0
- package/skills/security-constant-time-analysis/ct_analyzer/__init__.py +49 -0
- package/skills/security-constant-time-analysis/ct_analyzer/analyzer.py +1284 -0
- package/skills/security-constant-time-analysis/ct_analyzer/script_analyzers.py +3081 -0
- package/skills/security-constant-time-analysis/ct_analyzer/tests/__init__.py +1 -0
- package/skills/security-constant-time-analysis/ct_analyzer/tests/test_analyzer.py +1397 -0
- package/skills/security-constant-time-analysis/ct_analyzer/tests/test_samples/bn_excerpt.js +205 -0
- package/skills/security-constant-time-analysis/ct_analyzer/tests/test_samples/decompose_constant_time.c +181 -0
- package/skills/security-constant-time-analysis/ct_analyzer/tests/test_samples/decompose_vulnerable.c +74 -0
- package/skills/security-constant-time-analysis/ct_analyzer/tests/test_samples/decompose_vulnerable.go +78 -0
- package/skills/security-constant-time-analysis/ct_analyzer/tests/test_samples/decompose_vulnerable.rs +92 -0
- package/skills/security-constant-time-analysis/ct_analyzer/tests/test_samples/vulnerable.cs +174 -0
- package/skills/security-constant-time-analysis/ct_analyzer/tests/test_samples/vulnerable.java +161 -0
- package/skills/security-constant-time-analysis/ct_analyzer/tests/test_samples/vulnerable.kt +181 -0
- package/skills/security-constant-time-analysis/ct_analyzer/tests/test_samples/vulnerable.php +140 -0
- package/skills/security-constant-time-analysis/ct_analyzer/tests/test_samples/vulnerable.py +252 -0
- package/skills/security-constant-time-analysis/ct_analyzer/tests/test_samples/vulnerable.rb +188 -0
- package/skills/security-constant-time-analysis/ct_analyzer/tests/test_samples/vulnerable.swift +199 -0
- package/skills/security-constant-time-analysis/ct_analyzer/tests/test_samples/vulnerable.ts +154 -0
- package/skills/security-constant-time-analysis/pyproject.toml +52 -0
- package/skills/security-constant-time-analysis/skills/constant-time-analysis/README.md +90 -0
- package/skills/security-constant-time-analysis/skills/constant-time-analysis/SKILL.md +219 -0
- package/skills/security-constant-time-analysis/skills/constant-time-analysis/references/compiled.md +129 -0
- package/skills/security-constant-time-analysis/skills/constant-time-analysis/references/javascript.md +136 -0
- package/skills/security-constant-time-analysis/skills/constant-time-analysis/references/kotlin.md +252 -0
- package/skills/security-constant-time-analysis/skills/constant-time-analysis/references/php.md +172 -0
- package/skills/security-constant-time-analysis/skills/constant-time-analysis/references/python.md +179 -0
- package/skills/security-constant-time-analysis/skills/constant-time-analysis/references/ruby.md +198 -0
- package/skills/security-constant-time-analysis/skills/constant-time-analysis/references/swift.md +288 -0
- package/skills/security-constant-time-analysis/skills/constant-time-analysis/references/vm-compiled.md +354 -0
- package/skills/security-constant-time-analysis/uv.lock +8 -0
- package/skills/security-culture-index/.claude-plugin/plugin.json +8 -0
- package/skills/security-culture-index/README.md +79 -0
- package/skills/security-culture-index/skills/interpreting-culture-index/SKILL.md +293 -0
- package/skills/security-culture-index/skills/interpreting-culture-index/references/anti-patterns.md +255 -0
- package/skills/security-culture-index/skills/interpreting-culture-index/references/conversation-starters.md +408 -0
- package/skills/security-culture-index/skills/interpreting-culture-index/references/interview-trait-signals.md +253 -0
- package/skills/security-culture-index/skills/interpreting-culture-index/references/motivators.md +158 -0
- package/skills/security-culture-index/skills/interpreting-culture-index/references/patterns-archetypes.md +147 -0
- package/skills/security-culture-index/skills/interpreting-culture-index/references/primary-traits.md +307 -0
- package/skills/security-culture-index/skills/interpreting-culture-index/references/secondary-traits.md +228 -0
- package/skills/security-culture-index/skills/interpreting-culture-index/references/team-composition.md +148 -0
- package/skills/security-culture-index/skills/interpreting-culture-index/scripts/check_deps.py +108 -0
- package/skills/security-culture-index/skills/interpreting-culture-index/scripts/culture_index/__init__.py +20 -0
- package/skills/security-culture-index/skills/interpreting-culture-index/scripts/culture_index/constants.py +122 -0
- package/skills/security-culture-index/skills/interpreting-culture-index/scripts/culture_index/extract.py +187 -0
- package/skills/security-culture-index/skills/interpreting-culture-index/scripts/culture_index/models.py +16 -0
- package/skills/security-culture-index/skills/interpreting-culture-index/scripts/culture_index/opencv_extractor.py +520 -0
- package/skills/security-culture-index/skills/interpreting-culture-index/scripts/extract_pdf.py +237 -0
- package/skills/security-culture-index/skills/interpreting-culture-index/scripts/pyproject.toml +18 -0
- package/skills/security-culture-index/skills/interpreting-culture-index/templates/burnout-report.md +113 -0
- package/skills/security-culture-index/skills/interpreting-culture-index/templates/comparison-report.md +103 -0
- package/skills/security-culture-index/skills/interpreting-culture-index/templates/hiring-profile.md +127 -0
- package/skills/security-culture-index/skills/interpreting-culture-index/templates/individual-report.md +85 -0
- package/skills/security-culture-index/skills/interpreting-culture-index/templates/predicted-profile.md +165 -0
- package/skills/security-culture-index/skills/interpreting-culture-index/templates/team-report.md +109 -0
- package/skills/security-culture-index/skills/interpreting-culture-index/workflows/analyze-team.md +188 -0
- package/skills/security-culture-index/skills/interpreting-culture-index/workflows/coach-manager.md +267 -0
- package/skills/security-culture-index/skills/interpreting-culture-index/workflows/compare-profiles.md +188 -0
- package/skills/security-culture-index/skills/interpreting-culture-index/workflows/define-hiring-profile.md +220 -0
- package/skills/security-culture-index/skills/interpreting-culture-index/workflows/detect-burnout.md +206 -0
- package/skills/security-culture-index/skills/interpreting-culture-index/workflows/extract-from-pdf.md +121 -0
- package/skills/security-culture-index/skills/interpreting-culture-index/workflows/interpret-individual.md +183 -0
- package/skills/security-culture-index/skills/interpreting-culture-index/workflows/interview-debrief.md +234 -0
- package/skills/security-culture-index/skills/interpreting-culture-index/workflows/mediate-conflict.md +306 -0
- package/skills/security-culture-index/skills/interpreting-culture-index/workflows/plan-onboarding.md +322 -0
- package/skills/security-culture-index/skills/interpreting-culture-index/workflows/predict-from-interview.md +250 -0
- package/skills/security-differential-review/.claude-plugin/plugin.json +10 -0
- package/skills/security-differential-review/README.md +109 -0
- package/skills/security-differential-review/commands/diff-review.md +21 -0
- package/skills/security-differential-review/skills/differential-review/SKILL.md +220 -0
- package/skills/security-differential-review/skills/differential-review/adversarial.md +203 -0
- package/skills/security-differential-review/skills/differential-review/methodology.md +234 -0
- package/skills/security-differential-review/skills/differential-review/patterns.md +300 -0
- package/skills/security-differential-review/skills/differential-review/reporting.md +369 -0
- package/skills/security-dwarf-expert/.claude-plugin/plugin.json +10 -0
- package/skills/security-dwarf-expert/README.md +38 -0
- package/skills/security-dwarf-expert/skills/dwarf-expert/SKILL.md +93 -0
- package/skills/security-dwarf-expert/skills/dwarf-expert/reference/coding.md +31 -0
- package/skills/security-dwarf-expert/skills/dwarf-expert/reference/dwarfdump.md +50 -0
- package/skills/security-dwarf-expert/skills/dwarf-expert/reference/readelf.md +8 -0
- package/skills/security-entry-point-analyzer/.claude-plugin/plugin.json +10 -0
- package/skills/security-entry-point-analyzer/README.md +74 -0
- package/skills/security-entry-point-analyzer/commands/entry-points.md +18 -0
- package/skills/security-entry-point-analyzer/skills/entry-point-analyzer/SKILL.md +251 -0
- package/skills/security-entry-point-analyzer/skills/entry-point-analyzer/references/cosmwasm.md +182 -0
- package/skills/security-entry-point-analyzer/skills/entry-point-analyzer/references/move-aptos.md +107 -0
- package/skills/security-entry-point-analyzer/skills/entry-point-analyzer/references/move-sui.md +87 -0
- package/skills/security-entry-point-analyzer/skills/entry-point-analyzer/references/solana.md +155 -0
- package/skills/security-entry-point-analyzer/skills/entry-point-analyzer/references/solidity.md +135 -0
- package/skills/security-entry-point-analyzer/skills/entry-point-analyzer/references/ton.md +185 -0
- package/skills/security-entry-point-analyzer/skills/entry-point-analyzer/references/vyper.md +141 -0
- package/skills/security-firebase-apk-scanner/.claude-plugin/plugin.json +10 -0
- package/skills/security-firebase-apk-scanner/README.md +85 -0
- package/skills/security-firebase-apk-scanner/commands/scan-apk.md +18 -0
- package/skills/security-firebase-apk-scanner/scanner.sh +1408 -0
- package/skills/security-firebase-apk-scanner/skills/firebase-apk-scanner/SKILL.md +197 -0
- package/skills/security-firebase-apk-scanner/skills/firebase-apk-scanner/references/vulnerabilities.md +803 -0
- package/skills/security-fix-review/.claude-plugin/plugin.json +13 -0
- package/skills/security-fix-review/README.md +118 -0
- package/skills/security-fix-review/commands/fix-review.md +24 -0
- package/skills/security-fix-review/skills/fix-review/SKILL.md +264 -0
- package/skills/security-fix-review/skills/fix-review/references/bug-detection.md +408 -0
- package/skills/security-fix-review/skills/fix-review/references/finding-matching.md +298 -0
- package/skills/security-fix-review/skills/fix-review/references/report-parsing.md +398 -0
- package/skills/security-insecure-defaults/.claude-plugin/plugin.json +10 -0
- package/skills/security-insecure-defaults/README.md +45 -0
- package/skills/security-insecure-defaults/skills/insecure-defaults/SKILL.md +117 -0
- package/skills/security-insecure-defaults/skills/insecure-defaults/references/examples.md +409 -0
- package/skills/security-modern-python/.claude-plugin/plugin.json +10 -0
- package/skills/security-modern-python/README.md +58 -0
- package/skills/security-modern-python/hooks/hooks.json +16 -0
- package/skills/security-modern-python/hooks/intercept-legacy-python.bats +388 -0
- package/skills/security-modern-python/hooks/intercept-legacy-python.sh +109 -0
- package/skills/security-modern-python/hooks/test_helper.bash +75 -0
- package/skills/security-modern-python/skills/modern-python/SKILL.md +333 -0
- package/skills/security-modern-python/skills/modern-python/references/dependabot.md +43 -0
- package/skills/security-modern-python/skills/modern-python/references/migration-checklist.md +141 -0
- package/skills/security-modern-python/skills/modern-python/references/pep723-scripts.md +259 -0
- package/skills/security-modern-python/skills/modern-python/references/prek.md +211 -0
- package/skills/security-modern-python/skills/modern-python/references/pyproject.md +254 -0
- package/skills/security-modern-python/skills/modern-python/references/ruff-config.md +240 -0
- package/skills/security-modern-python/skills/modern-python/references/security-setup.md +255 -0
- package/skills/security-modern-python/skills/modern-python/references/testing.md +284 -0
- package/skills/security-modern-python/skills/modern-python/references/uv-commands.md +200 -0
- package/skills/security-modern-python/skills/modern-python/templates/dependabot.yml +36 -0
- package/skills/security-modern-python/skills/modern-python/templates/pre-commit-config.yaml +66 -0
- package/skills/security-property-based-testing/.claude-plugin/plugin.json +9 -0
- package/skills/security-property-based-testing/README.md +47 -0
- package/skills/security-property-based-testing/skills/property-based-testing/README.md +88 -0
- package/skills/security-property-based-testing/skills/property-based-testing/SKILL.md +109 -0
- package/skills/security-property-based-testing/skills/property-based-testing/references/design.md +191 -0
- package/skills/security-property-based-testing/skills/property-based-testing/references/generating.md +200 -0
- package/skills/security-property-based-testing/skills/property-based-testing/references/libraries.md +130 -0
- package/skills/security-property-based-testing/skills/property-based-testing/references/refactoring.md +181 -0
- package/skills/security-property-based-testing/skills/property-based-testing/references/reviewing.md +209 -0
- package/skills/security-property-based-testing/skills/property-based-testing/references/strategies.md +124 -0
- package/skills/semgrep-rule-creator/.claude-plugin/plugin.json +8 -0
- package/skills/semgrep-rule-creator/README.md +43 -0
- package/skills/semgrep-rule-creator/commands/semgrep-rule.md +26 -0
- package/skills/semgrep-rule-creator/skills/semgrep-rule-creator/SKILL.md +168 -0
- package/skills/semgrep-rule-creator/skills/semgrep-rule-creator/references/quick-reference.md +203 -0
- package/skills/semgrep-rule-creator/skills/semgrep-rule-creator/references/workflow.md +240 -0
- package/skills/semgrep-rule-variant-creator/.claude-plugin/plugin.json +9 -0
- package/skills/semgrep-rule-variant-creator/README.md +86 -0
- package/skills/semgrep-rule-variant-creator/skills/semgrep-rule-variant-creator/SKILL.md +205 -0
- package/skills/semgrep-rule-variant-creator/skills/semgrep-rule-variant-creator/references/applicability-analysis.md +250 -0
- package/skills/semgrep-rule-variant-creator/skills/semgrep-rule-variant-creator/references/language-syntax-guide.md +324 -0
- package/skills/semgrep-rule-variant-creator/skills/semgrep-rule-variant-creator/references/workflow.md +518 -0
- package/skills/session-logs/SKILL.md +115 -0
- package/skills/sharp-edges/.claude-plugin/plugin.json +10 -0
- package/skills/sharp-edges/README.md +48 -0
- package/skills/sharp-edges/skills/sharp-edges/SKILL.md +292 -0
- package/skills/sharp-edges/skills/sharp-edges/references/auth-patterns.md +252 -0
- package/skills/sharp-edges/skills/sharp-edges/references/case-studies.md +274 -0
- package/skills/sharp-edges/skills/sharp-edges/references/config-patterns.md +333 -0
- package/skills/sharp-edges/skills/sharp-edges/references/crypto-apis.md +190 -0
- package/skills/sharp-edges/skills/sharp-edges/references/lang-c.md +205 -0
- package/skills/sharp-edges/skills/sharp-edges/references/lang-csharp.md +285 -0
- package/skills/sharp-edges/skills/sharp-edges/references/lang-go.md +270 -0
- package/skills/sharp-edges/skills/sharp-edges/references/lang-java.md +263 -0
- package/skills/sharp-edges/skills/sharp-edges/references/lang-javascript.md +269 -0
- package/skills/sharp-edges/skills/sharp-edges/references/lang-kotlin.md +265 -0
- package/skills/sharp-edges/skills/sharp-edges/references/lang-php.md +245 -0
- package/skills/sharp-edges/skills/sharp-edges/references/lang-python.md +274 -0
- package/skills/sharp-edges/skills/sharp-edges/references/lang-ruby.md +273 -0
- package/skills/sharp-edges/skills/sharp-edges/references/lang-rust.md +272 -0
- package/skills/sharp-edges/skills/sharp-edges/references/lang-swift.md +287 -0
- package/skills/sharp-edges/skills/sharp-edges/references/language-specific.md +588 -0
- package/skills/sherpa-onnx-tts/SKILL.md +103 -0
- package/skills/sherpa-onnx-tts/bin/sherpa-onnx-tts +178 -0
- package/skills/skill-creator/SKILL.md +370 -0
- package/skills/skill-creator/license.txt +202 -0
- package/skills/skill-creator/scripts/init_skill.py +378 -0
- package/skills/skill-creator/scripts/package_skill.py +111 -0
- package/skills/skill-creator/scripts/quick_validate.py +101 -0
- package/skills/slack/SKILL.md +144 -0
- package/skills/songsee/SKILL.md +49 -0
- package/skills/sonoscli/SKILL.md +46 -0
- package/skills/spec-to-code-compliance/.claude-plugin/plugin.json +10 -0
- package/skills/spec-to-code-compliance/README.md +67 -0
- package/skills/spec-to-code-compliance/commands/spec-compliance.md +22 -0
- package/skills/spec-to-code-compliance/skills/spec-to-code-compliance/SKILL.md +349 -0
- package/skills/spec-to-code-compliance/skills/spec-to-code-compliance/resources/COMPLETENESS_CHECKLIST.md +69 -0
- package/skills/spec-to-code-compliance/skills/spec-to-code-compliance/resources/IR_EXAMPLES.md +417 -0
- package/skills/spec-to-code-compliance/skills/spec-to-code-compliance/resources/OUTPUT_REQUIREMENTS.md +105 -0
- package/skills/spotify-player/SKILL.md +64 -0
- package/skills/static-analysis/.claude-plugin/plugin.json +8 -0
- package/skills/static-analysis/README.md +59 -0
- package/skills/static-analysis/skills/codeql/SKILL.md +315 -0
- package/skills/static-analysis/skills/sarif-parsing/SKILL.md +479 -0
- package/skills/static-analysis/skills/sarif-parsing/resources/jq-queries.md +162 -0
- package/skills/static-analysis/skills/sarif-parsing/resources/sarif_helpers.py +331 -0
- package/skills/static-analysis/skills/semgrep/SKILL.md +337 -0
- package/skills/summarize/SKILL.md +87 -0
- package/skills/testing-handbook-skills/.claude-plugin/plugin.json +8 -0
- package/skills/testing-handbook-skills/README.md +241 -0
- package/skills/testing-handbook-skills/scripts/pyproject.toml +8 -0
- package/skills/testing-handbook-skills/scripts/validate-skills.py +657 -0
- package/skills/testing-handbook-skills/skills/address-sanitizer/SKILL.md +341 -0
- package/skills/testing-handbook-skills/skills/aflpp/SKILL.md +640 -0
- package/skills/testing-handbook-skills/skills/atheris/SKILL.md +515 -0
- package/skills/testing-handbook-skills/skills/cargo-fuzz/SKILL.md +454 -0
- package/skills/testing-handbook-skills/skills/codeql/SKILL.md +549 -0
- package/skills/testing-handbook-skills/skills/constant-time-testing/SKILL.md +507 -0
- package/skills/testing-handbook-skills/skills/coverage-analysis/SKILL.md +607 -0
- package/skills/testing-handbook-skills/skills/fuzzing-dictionary/SKILL.md +297 -0
- package/skills/testing-handbook-skills/skills/fuzzing-obstacles/SKILL.md +426 -0
- package/skills/testing-handbook-skills/skills/harness-writing/SKILL.md +614 -0
- package/skills/testing-handbook-skills/skills/libafl/SKILL.md +625 -0
- package/skills/testing-handbook-skills/skills/libfuzzer/SKILL.md +795 -0
- package/skills/testing-handbook-skills/skills/ossfuzz/SKILL.md +426 -0
- package/skills/testing-handbook-skills/skills/ruzzy/SKILL.md +443 -0
- package/skills/testing-handbook-skills/skills/semgrep/SKILL.md +601 -0
- package/skills/testing-handbook-skills/skills/testing-handbook-generator/SKILL.md +372 -0
- package/skills/testing-handbook-skills/skills/testing-handbook-generator/agent-prompt.md +280 -0
- package/skills/testing-handbook-skills/skills/testing-handbook-generator/discovery.md +452 -0
- package/skills/testing-handbook-skills/skills/testing-handbook-generator/templates/domain-skill.md +504 -0
- package/skills/testing-handbook-skills/skills/testing-handbook-generator/templates/fuzzer-skill.md +454 -0
- package/skills/testing-handbook-skills/skills/testing-handbook-generator/templates/technique-skill.md +527 -0
- package/skills/testing-handbook-skills/skills/testing-handbook-generator/templates/tool-skill.md +366 -0
- package/skills/testing-handbook-skills/skills/testing-handbook-generator/testing.md +482 -0
- package/skills/testing-handbook-skills/skills/wycheproof/SKILL.md +533 -0
- package/skills/things-mac/SKILL.md +86 -0
- package/skills/tmux/SKILL.md +135 -0
- package/skills/tmux/scripts/find-sessions.sh +112 -0
- package/skills/tmux/scripts/wait-for-text.sh +83 -0
- package/skills/trello/SKILL.md +95 -0
- package/skills/variant-analysis/.claude-plugin/plugin.json +8 -0
- package/skills/variant-analysis/README.md +41 -0
- package/skills/variant-analysis/commands/variants.md +23 -0
- package/skills/variant-analysis/skills/variant-analysis/METHODOLOGY.md +327 -0
- package/skills/variant-analysis/skills/variant-analysis/SKILL.md +142 -0
- package/skills/variant-analysis/skills/variant-analysis/resources/codeql/cpp.ql +119 -0
- package/skills/variant-analysis/skills/variant-analysis/resources/codeql/go.ql +69 -0
- package/skills/variant-analysis/skills/variant-analysis/resources/codeql/java.ql +71 -0
- package/skills/variant-analysis/skills/variant-analysis/resources/codeql/javascript.ql +63 -0
- package/skills/variant-analysis/skills/variant-analysis/resources/codeql/python.ql +80 -0
- package/skills/variant-analysis/skills/variant-analysis/resources/semgrep/cpp.yaml +98 -0
- package/skills/variant-analysis/skills/variant-analysis/resources/semgrep/go.yaml +63 -0
- package/skills/variant-analysis/skills/variant-analysis/resources/semgrep/java.yaml +61 -0
- package/skills/variant-analysis/skills/variant-analysis/resources/semgrep/javascript.yaml +60 -0
- package/skills/variant-analysis/skills/variant-analysis/resources/semgrep/python.yaml +72 -0
- package/skills/variant-analysis/skills/variant-analysis/resources/variant-report-template.md +75 -0
- package/skills/video-frames/SKILL.md +46 -0
- package/skills/video-frames/scripts/frame.sh +81 -0
- package/skills/voice-call/SKILL.md +45 -0
- package/skills/wacli/SKILL.md +72 -0
- package/skills/weather/SKILL.md +54 -0
- package/skills/yara-authoring/.claude-plugin/plugin.json +9 -0
- package/skills/yara-authoring/README.md +131 -0
- package/skills/yara-authoring/skills/yara-rule-authoring/SKILL.md +645 -0
- package/skills/yara-authoring/skills/yara-rule-authoring/examples/MAL_Mac_ProtonRAT_Jan25.yar +99 -0
- package/skills/yara-authoring/skills/yara-rule-authoring/examples/MAL_NPM_SupplyChain_Jan25.yar +170 -0
- package/skills/yara-authoring/skills/yara-rule-authoring/examples/MAL_Win_Remcos_Jan25.yar +103 -0
- package/skills/yara-authoring/skills/yara-rule-authoring/examples/SUSP_CRX_SuspiciousPermissions.yar +134 -0
- package/skills/yara-authoring/skills/yara-rule-authoring/examples/SUSP_JS_Obfuscation_Jan25.yar +185 -0
- package/skills/yara-authoring/skills/yara-rule-authoring/references/crx-module.md +214 -0
- package/skills/yara-authoring/skills/yara-rule-authoring/references/dex-module.md +383 -0
- package/skills/yara-authoring/skills/yara-rule-authoring/references/performance.md +333 -0
- package/skills/yara-authoring/skills/yara-rule-authoring/references/strings.md +433 -0
- package/skills/yara-authoring/skills/yara-rule-authoring/references/style-guide.md +257 -0
- package/skills/yara-authoring/skills/yara-rule-authoring/references/testing.md +399 -0
- package/skills/yara-authoring/skills/yara-rule-authoring/scripts/atom_analyzer.py +526 -0
- package/skills/yara-authoring/skills/yara-rule-authoring/scripts/pyproject.toml +25 -0
- package/skills/yara-authoring/skills/yara-rule-authoring/scripts/yara_lint.py +631 -0
- package/skills/yara-authoring/skills/yara-rule-authoring/workflows/rule-development.md +493 -0
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
# Local Places
|
|
2
|
+
|
|
3
|
+
This repo is a fusion of two pieces:
|
|
4
|
+
|
|
5
|
+
- A FastAPI server that exposes endpoints for searching and resolving places via the Google Maps Places API.
|
|
6
|
+
- A companion agent skill that explains how to use the API and can call it to find places efficiently.
|
|
7
|
+
|
|
8
|
+
Together, the skill and server let an agent turn natural-language place queries into structured results quickly.
|
|
9
|
+
|
|
10
|
+
## Run locally
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
# copy skill definition into the relevant folder (where the agent looks for it)
|
|
14
|
+
# then run the server
|
|
15
|
+
|
|
16
|
+
uv venv
|
|
17
|
+
uv pip install -e ".[dev]"
|
|
18
|
+
uv run --env-file .env uvicorn local_places.main:app --host 0.0.0.0 --reload
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Open the API docs at http://127.0.0.1:8000/docs.
|
|
22
|
+
|
|
23
|
+
## Places API
|
|
24
|
+
|
|
25
|
+
Set the Google Places API key before running:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
export GOOGLE_PLACES_API_KEY="your-key"
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Endpoints:
|
|
32
|
+
|
|
33
|
+
- `POST /places/search` (free-text query + filters)
|
|
34
|
+
- `GET /places/{place_id}` (place details)
|
|
35
|
+
- `POST /locations/resolve` (resolve a user-provided location string)
|
|
36
|
+
|
|
37
|
+
Example search request:
|
|
38
|
+
|
|
39
|
+
```json
|
|
40
|
+
{
|
|
41
|
+
"query": "italian restaurant",
|
|
42
|
+
"filters": {
|
|
43
|
+
"types": ["restaurant"],
|
|
44
|
+
"open_now": true,
|
|
45
|
+
"min_rating": 4.0,
|
|
46
|
+
"price_levels": [1, 2]
|
|
47
|
+
},
|
|
48
|
+
"limit": 10
|
|
49
|
+
}
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Notes:
|
|
53
|
+
|
|
54
|
+
- `filters.types` supports a single type (mapped to Google `includedType`).
|
|
55
|
+
|
|
56
|
+
Example search request (curl):
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
curl -X POST http://127.0.0.1:8000/places/search \
|
|
60
|
+
-H "Content-Type: application/json" \
|
|
61
|
+
-d '{
|
|
62
|
+
"query": "italian restaurant",
|
|
63
|
+
"location_bias": {
|
|
64
|
+
"lat": 40.8065,
|
|
65
|
+
"lng": -73.9719,
|
|
66
|
+
"radius_m": 3000
|
|
67
|
+
},
|
|
68
|
+
"filters": {
|
|
69
|
+
"types": ["restaurant"],
|
|
70
|
+
"open_now": true,
|
|
71
|
+
"min_rating": 4.0,
|
|
72
|
+
"price_levels": [1, 2, 3]
|
|
73
|
+
},
|
|
74
|
+
"limit": 10
|
|
75
|
+
}'
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Example resolve request (curl):
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
curl -X POST http://127.0.0.1:8000/locations/resolve \
|
|
82
|
+
-H "Content-Type: application/json" \
|
|
83
|
+
-d '{
|
|
84
|
+
"location_text": "Riverside Park, New York",
|
|
85
|
+
"limit": 5
|
|
86
|
+
}'
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## Test
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
uv run pytest
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## OpenAPI
|
|
96
|
+
|
|
97
|
+
Generate the OpenAPI schema:
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
uv run python scripts/generate_openapi.py
|
|
101
|
+
```
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: local-places
|
|
3
|
+
description: Search for places (restaurants, cafes, etc.) via Google Places API proxy on localhost.
|
|
4
|
+
homepage: https://github.com/Hyaxia/local_places
|
|
5
|
+
metadata:
|
|
6
|
+
{
|
|
7
|
+
"otto":
|
|
8
|
+
{
|
|
9
|
+
"emoji": "📍",
|
|
10
|
+
"requires": { "bins": ["uv"], "env": ["GOOGLE_PLACES_API_KEY"] },
|
|
11
|
+
"primaryEnv": "GOOGLE_PLACES_API_KEY",
|
|
12
|
+
},
|
|
13
|
+
}
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
# 📍 Local Places
|
|
17
|
+
|
|
18
|
+
_Find places, Go fast_
|
|
19
|
+
|
|
20
|
+
Search for nearby places using a local Google Places API proxy. Two-step flow: resolve location first, then search.
|
|
21
|
+
|
|
22
|
+
## Setup
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
cd {baseDir}
|
|
26
|
+
echo "GOOGLE_PLACES_API_KEY=your-key" > .env
|
|
27
|
+
uv venv && uv pip install -e ".[dev]"
|
|
28
|
+
uv run --env-file .env uvicorn local_places.main:app --host 127.0.0.1 --port 8000
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Requires `GOOGLE_PLACES_API_KEY` in `.env` or environment.
|
|
32
|
+
|
|
33
|
+
## Quick Start
|
|
34
|
+
|
|
35
|
+
1. **Check server:** `curl http://127.0.0.1:8000/ping`
|
|
36
|
+
|
|
37
|
+
2. **Resolve location:**
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
curl -X POST http://127.0.0.1:8000/locations/resolve \
|
|
41
|
+
-H "Content-Type: application/json" \
|
|
42
|
+
-d '{"location_text": "Soho, London", "limit": 5}'
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
3. **Search places:**
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
curl -X POST http://127.0.0.1:8000/places/search \
|
|
49
|
+
-H "Content-Type: application/json" \
|
|
50
|
+
-d '{
|
|
51
|
+
"query": "coffee shop",
|
|
52
|
+
"location_bias": {"lat": 51.5137, "lng": -0.1366, "radius_m": 1000},
|
|
53
|
+
"filters": {"open_now": true, "min_rating": 4.0},
|
|
54
|
+
"limit": 10
|
|
55
|
+
}'
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
4. **Get details:**
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
curl http://127.0.0.1:8000/places/{place_id}
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Conversation Flow
|
|
65
|
+
|
|
66
|
+
1. If user says "near me" or gives vague location → resolve it first
|
|
67
|
+
2. If multiple results → show numbered list, ask user to pick
|
|
68
|
+
3. Ask for preferences: type, open now, rating, price level
|
|
69
|
+
4. Search with `location_bias` from chosen location
|
|
70
|
+
5. Present results with name, rating, address, open status
|
|
71
|
+
6. Offer to fetch details or refine search
|
|
72
|
+
|
|
73
|
+
## Filter Constraints
|
|
74
|
+
|
|
75
|
+
- `filters.types`: exactly ONE type (e.g., "restaurant", "cafe", "gym")
|
|
76
|
+
- `filters.price_levels`: integers 0-4 (0=free, 4=very expensive)
|
|
77
|
+
- `filters.min_rating`: 0-5 in 0.5 increments
|
|
78
|
+
- `filters.open_now`: boolean
|
|
79
|
+
- `limit`: 1-20 for search, 1-10 for resolve
|
|
80
|
+
- `location_bias.radius_m`: must be > 0
|
|
81
|
+
|
|
82
|
+
## Response Format
|
|
83
|
+
|
|
84
|
+
```json
|
|
85
|
+
{
|
|
86
|
+
"results": [
|
|
87
|
+
{
|
|
88
|
+
"place_id": "ChIJ...",
|
|
89
|
+
"name": "Coffee Shop",
|
|
90
|
+
"address": "123 Main St",
|
|
91
|
+
"location": { "lat": 51.5, "lng": -0.1 },
|
|
92
|
+
"rating": 4.6,
|
|
93
|
+
"price_level": 2,
|
|
94
|
+
"types": ["cafe", "food"],
|
|
95
|
+
"open_now": true
|
|
96
|
+
}
|
|
97
|
+
],
|
|
98
|
+
"next_page_token": "..."
|
|
99
|
+
}
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
Use `next_page_token` as `page_token` in next request for more results.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "my-api"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "FastAPI server"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
requires-python = ">=3.11"
|
|
7
|
+
dependencies = ["fastapi>=0.110.0", "httpx>=0.27.0", "uvicorn[standard]>=0.29.0"]
|
|
8
|
+
|
|
9
|
+
[project.optional-dependencies]
|
|
10
|
+
dev = ["pytest>=8.0.0"]
|
|
11
|
+
|
|
12
|
+
[build-system]
|
|
13
|
+
requires = ["hatchling"]
|
|
14
|
+
build-backend = "hatchling.build"
|
|
15
|
+
|
|
16
|
+
[tool.hatch.build.targets.wheel]
|
|
17
|
+
packages = ["src/local_places"]
|
|
18
|
+
|
|
19
|
+
[tool.pytest.ini_options]
|
|
20
|
+
addopts = "-q"
|
|
21
|
+
testpaths = ["tests"]
|
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
import os
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
import httpx
|
|
8
|
+
from fastapi import HTTPException
|
|
9
|
+
|
|
10
|
+
from local_places.schemas import (
|
|
11
|
+
LatLng,
|
|
12
|
+
LocationResolveRequest,
|
|
13
|
+
LocationResolveResponse,
|
|
14
|
+
PlaceDetails,
|
|
15
|
+
PlaceSummary,
|
|
16
|
+
ResolvedLocation,
|
|
17
|
+
SearchRequest,
|
|
18
|
+
SearchResponse,
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
GOOGLE_PLACES_BASE_URL = os.getenv(
|
|
22
|
+
"GOOGLE_PLACES_BASE_URL", "https://places.googleapis.com/v1"
|
|
23
|
+
)
|
|
24
|
+
logger = logging.getLogger("local_places.google_places")
|
|
25
|
+
|
|
26
|
+
_PRICE_LEVEL_TO_ENUM = {
|
|
27
|
+
0: "PRICE_LEVEL_FREE",
|
|
28
|
+
1: "PRICE_LEVEL_INEXPENSIVE",
|
|
29
|
+
2: "PRICE_LEVEL_MODERATE",
|
|
30
|
+
3: "PRICE_LEVEL_EXPENSIVE",
|
|
31
|
+
4: "PRICE_LEVEL_VERY_EXPENSIVE",
|
|
32
|
+
}
|
|
33
|
+
_ENUM_TO_PRICE_LEVEL = {value: key for key, value in _PRICE_LEVEL_TO_ENUM.items()}
|
|
34
|
+
|
|
35
|
+
_SEARCH_FIELD_MASK = (
|
|
36
|
+
"places.id,"
|
|
37
|
+
"places.displayName,"
|
|
38
|
+
"places.formattedAddress,"
|
|
39
|
+
"places.location,"
|
|
40
|
+
"places.rating,"
|
|
41
|
+
"places.priceLevel,"
|
|
42
|
+
"places.types,"
|
|
43
|
+
"places.currentOpeningHours,"
|
|
44
|
+
"nextPageToken"
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
_DETAILS_FIELD_MASK = (
|
|
48
|
+
"id,"
|
|
49
|
+
"displayName,"
|
|
50
|
+
"formattedAddress,"
|
|
51
|
+
"location,"
|
|
52
|
+
"rating,"
|
|
53
|
+
"priceLevel,"
|
|
54
|
+
"types,"
|
|
55
|
+
"regularOpeningHours,"
|
|
56
|
+
"currentOpeningHours,"
|
|
57
|
+
"nationalPhoneNumber,"
|
|
58
|
+
"websiteUri"
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
_RESOLVE_FIELD_MASK = (
|
|
62
|
+
"places.id,"
|
|
63
|
+
"places.displayName,"
|
|
64
|
+
"places.formattedAddress,"
|
|
65
|
+
"places.location,"
|
|
66
|
+
"places.types"
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
class _GoogleResponse:
|
|
71
|
+
def __init__(self, response: httpx.Response):
|
|
72
|
+
self.status_code = response.status_code
|
|
73
|
+
self._response = response
|
|
74
|
+
|
|
75
|
+
def json(self) -> dict[str, Any]:
|
|
76
|
+
return self._response.json()
|
|
77
|
+
|
|
78
|
+
@property
|
|
79
|
+
def text(self) -> str:
|
|
80
|
+
return self._response.text
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def _api_headers(field_mask: str) -> dict[str, str]:
|
|
84
|
+
api_key = os.getenv("GOOGLE_PLACES_API_KEY")
|
|
85
|
+
if not api_key:
|
|
86
|
+
raise HTTPException(
|
|
87
|
+
status_code=500,
|
|
88
|
+
detail="GOOGLE_PLACES_API_KEY is not set.",
|
|
89
|
+
)
|
|
90
|
+
return {
|
|
91
|
+
"Content-Type": "application/json",
|
|
92
|
+
"X-Goog-Api-Key": api_key,
|
|
93
|
+
"X-Goog-FieldMask": field_mask,
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def _request(
|
|
98
|
+
method: str, url: str, payload: dict[str, Any] | None, field_mask: str
|
|
99
|
+
) -> _GoogleResponse:
|
|
100
|
+
try:
|
|
101
|
+
with httpx.Client(timeout=10.0) as client:
|
|
102
|
+
response = client.request(
|
|
103
|
+
method=method,
|
|
104
|
+
url=url,
|
|
105
|
+
headers=_api_headers(field_mask),
|
|
106
|
+
json=payload,
|
|
107
|
+
)
|
|
108
|
+
except httpx.HTTPError as exc:
|
|
109
|
+
raise HTTPException(status_code=502, detail="Google Places API unavailable.") from exc
|
|
110
|
+
|
|
111
|
+
return _GoogleResponse(response)
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def _build_text_query(request: SearchRequest) -> str:
|
|
115
|
+
keyword = request.filters.keyword if request.filters else None
|
|
116
|
+
if keyword:
|
|
117
|
+
return f"{request.query} {keyword}".strip()
|
|
118
|
+
return request.query
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def _build_search_body(request: SearchRequest) -> dict[str, Any]:
|
|
122
|
+
body: dict[str, Any] = {
|
|
123
|
+
"textQuery": _build_text_query(request),
|
|
124
|
+
"pageSize": request.limit,
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
if request.page_token:
|
|
128
|
+
body["pageToken"] = request.page_token
|
|
129
|
+
|
|
130
|
+
if request.location_bias:
|
|
131
|
+
body["locationBias"] = {
|
|
132
|
+
"circle": {
|
|
133
|
+
"center": {
|
|
134
|
+
"latitude": request.location_bias.lat,
|
|
135
|
+
"longitude": request.location_bias.lng,
|
|
136
|
+
},
|
|
137
|
+
"radius": request.location_bias.radius_m,
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
if request.filters:
|
|
142
|
+
filters = request.filters
|
|
143
|
+
if filters.types:
|
|
144
|
+
body["includedType"] = filters.types[0]
|
|
145
|
+
if filters.open_now is not None:
|
|
146
|
+
body["openNow"] = filters.open_now
|
|
147
|
+
if filters.min_rating is not None:
|
|
148
|
+
body["minRating"] = filters.min_rating
|
|
149
|
+
if filters.price_levels:
|
|
150
|
+
body["priceLevels"] = [
|
|
151
|
+
_PRICE_LEVEL_TO_ENUM[level] for level in filters.price_levels
|
|
152
|
+
]
|
|
153
|
+
|
|
154
|
+
return body
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
def _parse_lat_lng(raw: dict[str, Any] | None) -> LatLng | None:
|
|
158
|
+
if not raw:
|
|
159
|
+
return None
|
|
160
|
+
latitude = raw.get("latitude")
|
|
161
|
+
longitude = raw.get("longitude")
|
|
162
|
+
if latitude is None or longitude is None:
|
|
163
|
+
return None
|
|
164
|
+
return LatLng(lat=latitude, lng=longitude)
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
def _parse_display_name(raw: dict[str, Any] | None) -> str | None:
|
|
168
|
+
if not raw:
|
|
169
|
+
return None
|
|
170
|
+
return raw.get("text")
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
def _parse_open_now(raw: dict[str, Any] | None) -> bool | None:
|
|
174
|
+
if not raw:
|
|
175
|
+
return None
|
|
176
|
+
return raw.get("openNow")
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
def _parse_hours(raw: dict[str, Any] | None) -> list[str] | None:
|
|
180
|
+
if not raw:
|
|
181
|
+
return None
|
|
182
|
+
return raw.get("weekdayDescriptions")
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
def _parse_price_level(raw: str | None) -> int | None:
|
|
186
|
+
if not raw:
|
|
187
|
+
return None
|
|
188
|
+
return _ENUM_TO_PRICE_LEVEL.get(raw)
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
def search_places(request: SearchRequest) -> SearchResponse:
|
|
192
|
+
url = f"{GOOGLE_PLACES_BASE_URL}/places:searchText"
|
|
193
|
+
response = _request("POST", url, _build_search_body(request), _SEARCH_FIELD_MASK)
|
|
194
|
+
|
|
195
|
+
if response.status_code >= 400:
|
|
196
|
+
logger.error(
|
|
197
|
+
"Google Places API error %s. response=%s",
|
|
198
|
+
response.status_code,
|
|
199
|
+
response.text,
|
|
200
|
+
)
|
|
201
|
+
raise HTTPException(
|
|
202
|
+
status_code=502,
|
|
203
|
+
detail=f"Google Places API error ({response.status_code}).",
|
|
204
|
+
)
|
|
205
|
+
|
|
206
|
+
try:
|
|
207
|
+
payload = response.json()
|
|
208
|
+
except ValueError as exc:
|
|
209
|
+
logger.error(
|
|
210
|
+
"Google Places API returned invalid JSON. response=%s",
|
|
211
|
+
response.text,
|
|
212
|
+
)
|
|
213
|
+
raise HTTPException(status_code=502, detail="Invalid Google response.") from exc
|
|
214
|
+
|
|
215
|
+
places = payload.get("places", [])
|
|
216
|
+
results = []
|
|
217
|
+
for place in places:
|
|
218
|
+
results.append(
|
|
219
|
+
PlaceSummary(
|
|
220
|
+
place_id=place.get("id", ""),
|
|
221
|
+
name=_parse_display_name(place.get("displayName")),
|
|
222
|
+
address=place.get("formattedAddress"),
|
|
223
|
+
location=_parse_lat_lng(place.get("location")),
|
|
224
|
+
rating=place.get("rating"),
|
|
225
|
+
price_level=_parse_price_level(place.get("priceLevel")),
|
|
226
|
+
types=place.get("types"),
|
|
227
|
+
open_now=_parse_open_now(place.get("currentOpeningHours")),
|
|
228
|
+
)
|
|
229
|
+
)
|
|
230
|
+
|
|
231
|
+
return SearchResponse(
|
|
232
|
+
results=results,
|
|
233
|
+
next_page_token=payload.get("nextPageToken"),
|
|
234
|
+
)
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
def get_place_details(place_id: str) -> PlaceDetails:
|
|
238
|
+
url = f"{GOOGLE_PLACES_BASE_URL}/places/{place_id}"
|
|
239
|
+
response = _request("GET", url, None, _DETAILS_FIELD_MASK)
|
|
240
|
+
|
|
241
|
+
if response.status_code >= 400:
|
|
242
|
+
logger.error(
|
|
243
|
+
"Google Places API error %s. response=%s",
|
|
244
|
+
response.status_code,
|
|
245
|
+
response.text,
|
|
246
|
+
)
|
|
247
|
+
raise HTTPException(
|
|
248
|
+
status_code=502,
|
|
249
|
+
detail=f"Google Places API error ({response.status_code}).",
|
|
250
|
+
)
|
|
251
|
+
|
|
252
|
+
try:
|
|
253
|
+
payload = response.json()
|
|
254
|
+
except ValueError as exc:
|
|
255
|
+
logger.error(
|
|
256
|
+
"Google Places API returned invalid JSON. response=%s",
|
|
257
|
+
response.text,
|
|
258
|
+
)
|
|
259
|
+
raise HTTPException(status_code=502, detail="Invalid Google response.") from exc
|
|
260
|
+
|
|
261
|
+
return PlaceDetails(
|
|
262
|
+
place_id=payload.get("id", place_id),
|
|
263
|
+
name=_parse_display_name(payload.get("displayName")),
|
|
264
|
+
address=payload.get("formattedAddress"),
|
|
265
|
+
location=_parse_lat_lng(payload.get("location")),
|
|
266
|
+
rating=payload.get("rating"),
|
|
267
|
+
price_level=_parse_price_level(payload.get("priceLevel")),
|
|
268
|
+
types=payload.get("types"),
|
|
269
|
+
phone=payload.get("nationalPhoneNumber"),
|
|
270
|
+
website=payload.get("websiteUri"),
|
|
271
|
+
hours=_parse_hours(payload.get("regularOpeningHours")),
|
|
272
|
+
open_now=_parse_open_now(payload.get("currentOpeningHours")),
|
|
273
|
+
)
|
|
274
|
+
|
|
275
|
+
|
|
276
|
+
def resolve_locations(request: LocationResolveRequest) -> LocationResolveResponse:
|
|
277
|
+
url = f"{GOOGLE_PLACES_BASE_URL}/places:searchText"
|
|
278
|
+
body = {"textQuery": request.location_text, "pageSize": request.limit}
|
|
279
|
+
response = _request("POST", url, body, _RESOLVE_FIELD_MASK)
|
|
280
|
+
|
|
281
|
+
if response.status_code >= 400:
|
|
282
|
+
logger.error(
|
|
283
|
+
"Google Places API error %s. response=%s",
|
|
284
|
+
response.status_code,
|
|
285
|
+
response.text,
|
|
286
|
+
)
|
|
287
|
+
raise HTTPException(
|
|
288
|
+
status_code=502,
|
|
289
|
+
detail=f"Google Places API error ({response.status_code}).",
|
|
290
|
+
)
|
|
291
|
+
|
|
292
|
+
try:
|
|
293
|
+
payload = response.json()
|
|
294
|
+
except ValueError as exc:
|
|
295
|
+
logger.error(
|
|
296
|
+
"Google Places API returned invalid JSON. response=%s",
|
|
297
|
+
response.text,
|
|
298
|
+
)
|
|
299
|
+
raise HTTPException(status_code=502, detail="Invalid Google response.") from exc
|
|
300
|
+
|
|
301
|
+
places = payload.get("places", [])
|
|
302
|
+
results = []
|
|
303
|
+
for place in places:
|
|
304
|
+
results.append(
|
|
305
|
+
ResolvedLocation(
|
|
306
|
+
place_id=place.get("id", ""),
|
|
307
|
+
name=_parse_display_name(place.get("displayName")),
|
|
308
|
+
address=place.get("formattedAddress"),
|
|
309
|
+
location=_parse_lat_lng(place.get("location")),
|
|
310
|
+
types=place.get("types"),
|
|
311
|
+
)
|
|
312
|
+
)
|
|
313
|
+
|
|
314
|
+
return LocationResolveResponse(results=results)
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
import os
|
|
3
|
+
|
|
4
|
+
from fastapi import FastAPI, Request
|
|
5
|
+
from fastapi.encoders import jsonable_encoder
|
|
6
|
+
from fastapi.exceptions import RequestValidationError
|
|
7
|
+
from fastapi.responses import JSONResponse
|
|
8
|
+
|
|
9
|
+
from local_places.google_places import get_place_details, resolve_locations, search_places
|
|
10
|
+
from local_places.schemas import (
|
|
11
|
+
LocationResolveRequest,
|
|
12
|
+
LocationResolveResponse,
|
|
13
|
+
PlaceDetails,
|
|
14
|
+
SearchRequest,
|
|
15
|
+
SearchResponse,
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
app = FastAPI(
|
|
19
|
+
title="My API",
|
|
20
|
+
servers=[{"url": os.getenv("OPENAPI_SERVER_URL", "http://maxims-macbook-air:8000")}],
|
|
21
|
+
)
|
|
22
|
+
logger = logging.getLogger("local_places.validation")
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
@app.get("/ping")
|
|
26
|
+
def ping() -> dict[str, str]:
|
|
27
|
+
return {"message": "pong"}
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@app.exception_handler(RequestValidationError)
|
|
31
|
+
async def validation_exception_handler(
|
|
32
|
+
request: Request, exc: RequestValidationError
|
|
33
|
+
) -> JSONResponse:
|
|
34
|
+
logger.error(
|
|
35
|
+
"Validation error on %s %s. body=%s errors=%s",
|
|
36
|
+
request.method,
|
|
37
|
+
request.url.path,
|
|
38
|
+
exc.body,
|
|
39
|
+
exc.errors(),
|
|
40
|
+
)
|
|
41
|
+
return JSONResponse(
|
|
42
|
+
status_code=422,
|
|
43
|
+
content=jsonable_encoder({"detail": exc.errors()}),
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
@app.post("/places/search", response_model=SearchResponse)
|
|
48
|
+
def places_search(request: SearchRequest) -> SearchResponse:
|
|
49
|
+
return search_places(request)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
@app.get("/places/{place_id}", response_model=PlaceDetails)
|
|
53
|
+
def places_details(place_id: str) -> PlaceDetails:
|
|
54
|
+
return get_place_details(place_id)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
@app.post("/locations/resolve", response_model=LocationResolveResponse)
|
|
58
|
+
def locations_resolve(request: LocationResolveRequest) -> LocationResolveResponse:
|
|
59
|
+
return resolve_locations(request)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
if __name__ == "__main__":
|
|
63
|
+
import uvicorn
|
|
64
|
+
|
|
65
|
+
uvicorn.run("local_places.main:app", host="0.0.0.0", port=8000)
|