@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.
Files changed (371) hide show
  1. package/README.md +126 -0
  2. package/package.json +53 -0
  3. package/skills/1password/SKILL.md +70 -0
  4. package/skills/1password/references/cli-examples.md +29 -0
  5. package/skills/1password/references/get-started.md +17 -0
  6. package/skills/apple-notes/SKILL.md +77 -0
  7. package/skills/apple-reminders/SKILL.md +96 -0
  8. package/skills/bear-notes/SKILL.md +107 -0
  9. package/skills/bird/SKILL.md +224 -0
  10. package/skills/blogwatcher/SKILL.md +69 -0
  11. package/skills/blucli/SKILL.md +47 -0
  12. package/skills/bluebubbles/SKILL.md +131 -0
  13. package/skills/camsnap/SKILL.md +45 -0
  14. package/skills/canvas/SKILL.md +203 -0
  15. package/skills/clawhub/SKILL.md +77 -0
  16. package/skills/coding-agent/SKILL.md +284 -0
  17. package/skills/discord/SKILL.md +578 -0
  18. package/skills/eightctl/SKILL.md +50 -0
  19. package/skills/food-order/SKILL.md +48 -0
  20. package/skills/gemini/SKILL.md +43 -0
  21. package/skills/gifgrep/SKILL.md +79 -0
  22. package/skills/github/SKILL.md +77 -0
  23. package/skills/gog/SKILL.md +116 -0
  24. package/skills/goplaces/SKILL.md +52 -0
  25. package/skills/healthcheck/SKILL.md +245 -0
  26. package/skills/himalaya/SKILL.md +257 -0
  27. package/skills/himalaya/references/configuration.md +184 -0
  28. package/skills/himalaya/references/message-composition.md +199 -0
  29. package/skills/imsg/SKILL.md +74 -0
  30. package/skills/local-places/SERVER_README.md +101 -0
  31. package/skills/local-places/SKILL.md +102 -0
  32. package/skills/local-places/pyproject.toml +21 -0
  33. package/skills/local-places/src/local_places/__init__.py +2 -0
  34. package/skills/local-places/src/local_places/google_places.py +314 -0
  35. package/skills/local-places/src/local_places/main.py +65 -0
  36. package/skills/local-places/src/local_places/schemas.py +107 -0
  37. package/skills/mcporter/SKILL.md +61 -0
  38. package/skills/model-usage/SKILL.md +69 -0
  39. package/skills/model-usage/references/codexbar-cli.md +33 -0
  40. package/skills/model-usage/scripts/model_usage.py +310 -0
  41. package/skills/nano-banana-pro/SKILL.md +58 -0
  42. package/skills/nano-banana-pro/scripts/generate_image.py +184 -0
  43. package/skills/nano-pdf/SKILL.md +38 -0
  44. package/skills/notion/SKILL.md +172 -0
  45. package/skills/obsidian/SKILL.md +81 -0
  46. package/skills/openai-image-gen/SKILL.md +89 -0
  47. package/skills/openai-image-gen/scripts/gen.py +240 -0
  48. package/skills/openai-whisper/SKILL.md +38 -0
  49. package/skills/openai-whisper-api/SKILL.md +52 -0
  50. package/skills/openai-whisper-api/scripts/transcribe.sh +85 -0
  51. package/skills/openhue/SKILL.md +51 -0
  52. package/skills/oracle/SKILL.md +125 -0
  53. package/skills/ordercli/SKILL.md +78 -0
  54. package/skills/peekaboo/SKILL.md +190 -0
  55. package/skills/sag/SKILL.md +87 -0
  56. package/skills/security-ask-questions-if-underspecified/.claude-plugin/plugin.json +10 -0
  57. package/skills/security-ask-questions-if-underspecified/README.md +24 -0
  58. package/skills/security-ask-questions-if-underspecified/skills/ask-questions-if-underspecified/SKILL.md +85 -0
  59. package/skills/security-audit-context-building/.claude-plugin/plugin.json +10 -0
  60. package/skills/security-audit-context-building/README.md +58 -0
  61. package/skills/security-audit-context-building/commands/audit-context.md +21 -0
  62. package/skills/security-audit-context-building/skills/audit-context-building/SKILL.md +297 -0
  63. package/skills/security-audit-context-building/skills/audit-context-building/resources/COMPLETENESS_CHECKLIST.md +47 -0
  64. package/skills/security-audit-context-building/skills/audit-context-building/resources/FUNCTION_MICRO_ANALYSIS_EXAMPLE.md +355 -0
  65. package/skills/security-audit-context-building/skills/audit-context-building/resources/OUTPUT_REQUIREMENTS.md +71 -0
  66. package/skills/security-building-secure-contracts/.claude-plugin/plugin.json +10 -0
  67. package/skills/security-building-secure-contracts/README.md +241 -0
  68. package/skills/security-building-secure-contracts/skills/algorand-vulnerability-scanner/SKILL.md +284 -0
  69. package/skills/security-building-secure-contracts/skills/algorand-vulnerability-scanner/resources/VULNERABILITY_PATTERNS.md +405 -0
  70. package/skills/security-building-secure-contracts/skills/audit-prep-assistant/SKILL.md +409 -0
  71. package/skills/security-building-secure-contracts/skills/cairo-vulnerability-scanner/SKILL.md +329 -0
  72. package/skills/security-building-secure-contracts/skills/cairo-vulnerability-scanner/resources/VULNERABILITY_PATTERNS.md +722 -0
  73. package/skills/security-building-secure-contracts/skills/code-maturity-assessor/SKILL.md +218 -0
  74. package/skills/security-building-secure-contracts/skills/code-maturity-assessor/resources/ASSESSMENT_CRITERIA.md +355 -0
  75. package/skills/security-building-secure-contracts/skills/code-maturity-assessor/resources/EXAMPLE_REPORT.md +248 -0
  76. package/skills/security-building-secure-contracts/skills/code-maturity-assessor/resources/REPORT_FORMAT.md +33 -0
  77. package/skills/security-building-secure-contracts/skills/cosmos-vulnerability-scanner/SKILL.md +334 -0
  78. package/skills/security-building-secure-contracts/skills/cosmos-vulnerability-scanner/resources/VULNERABILITY_PATTERNS.md +740 -0
  79. package/skills/security-building-secure-contracts/skills/guidelines-advisor/SKILL.md +252 -0
  80. package/skills/security-building-secure-contracts/skills/guidelines-advisor/resources/ASSESSMENT_AREAS.md +329 -0
  81. package/skills/security-building-secure-contracts/skills/guidelines-advisor/resources/DELIVERABLES.md +118 -0
  82. package/skills/security-building-secure-contracts/skills/guidelines-advisor/resources/EXAMPLE_REPORT.md +298 -0
  83. package/skills/security-building-secure-contracts/skills/secure-workflow-guide/SKILL.md +161 -0
  84. package/skills/security-building-secure-contracts/skills/secure-workflow-guide/resources/EXAMPLE_REPORT.md +279 -0
  85. package/skills/security-building-secure-contracts/skills/secure-workflow-guide/resources/WORKFLOW_STEPS.md +132 -0
  86. package/skills/security-building-secure-contracts/skills/solana-vulnerability-scanner/SKILL.md +389 -0
  87. package/skills/security-building-secure-contracts/skills/solana-vulnerability-scanner/resources/VULNERABILITY_PATTERNS.md +669 -0
  88. package/skills/security-building-secure-contracts/skills/substrate-vulnerability-scanner/SKILL.md +298 -0
  89. package/skills/security-building-secure-contracts/skills/substrate-vulnerability-scanner/resources/VULNERABILITY_PATTERNS.md +791 -0
  90. package/skills/security-building-secure-contracts/skills/token-integration-analyzer/SKILL.md +362 -0
  91. package/skills/security-building-secure-contracts/skills/token-integration-analyzer/resources/ASSESSMENT_CATEGORIES.md +571 -0
  92. package/skills/security-building-secure-contracts/skills/token-integration-analyzer/resources/REPORT_TEMPLATES.md +141 -0
  93. package/skills/security-building-secure-contracts/skills/ton-vulnerability-scanner/SKILL.md +388 -0
  94. package/skills/security-building-secure-contracts/skills/ton-vulnerability-scanner/resources/VULNERABILITY_PATTERNS.md +595 -0
  95. package/skills/security-burpsuite-project-parser/.claude-plugin/plugin.json +10 -0
  96. package/skills/security-burpsuite-project-parser/README.md +103 -0
  97. package/skills/security-burpsuite-project-parser/commands/burp-search.md +18 -0
  98. package/skills/security-burpsuite-project-parser/skills/SKILL.md +358 -0
  99. package/skills/security-burpsuite-project-parser/skills/scripts/burp-search.sh +99 -0
  100. package/skills/security-claude-in-chrome-troubleshooting/.claude-plugin/plugin.json +8 -0
  101. package/skills/security-claude-in-chrome-troubleshooting/README.md +31 -0
  102. package/skills/security-claude-in-chrome-troubleshooting/skills/claude-in-chrome-troubleshooting/SKILL.md +251 -0
  103. package/skills/security-constant-time-analysis/.claude-plugin/plugin.json +9 -0
  104. package/skills/security-constant-time-analysis/README.md +381 -0
  105. package/skills/security-constant-time-analysis/commands/ct-check.md +20 -0
  106. package/skills/security-constant-time-analysis/ct_analyzer/__init__.py +49 -0
  107. package/skills/security-constant-time-analysis/ct_analyzer/analyzer.py +1284 -0
  108. package/skills/security-constant-time-analysis/ct_analyzer/script_analyzers.py +3081 -0
  109. package/skills/security-constant-time-analysis/ct_analyzer/tests/__init__.py +1 -0
  110. package/skills/security-constant-time-analysis/ct_analyzer/tests/test_analyzer.py +1397 -0
  111. package/skills/security-constant-time-analysis/ct_analyzer/tests/test_samples/bn_excerpt.js +205 -0
  112. package/skills/security-constant-time-analysis/ct_analyzer/tests/test_samples/decompose_constant_time.c +181 -0
  113. package/skills/security-constant-time-analysis/ct_analyzer/tests/test_samples/decompose_vulnerable.c +74 -0
  114. package/skills/security-constant-time-analysis/ct_analyzer/tests/test_samples/decompose_vulnerable.go +78 -0
  115. package/skills/security-constant-time-analysis/ct_analyzer/tests/test_samples/decompose_vulnerable.rs +92 -0
  116. package/skills/security-constant-time-analysis/ct_analyzer/tests/test_samples/vulnerable.cs +174 -0
  117. package/skills/security-constant-time-analysis/ct_analyzer/tests/test_samples/vulnerable.java +161 -0
  118. package/skills/security-constant-time-analysis/ct_analyzer/tests/test_samples/vulnerable.kt +181 -0
  119. package/skills/security-constant-time-analysis/ct_analyzer/tests/test_samples/vulnerable.php +140 -0
  120. package/skills/security-constant-time-analysis/ct_analyzer/tests/test_samples/vulnerable.py +252 -0
  121. package/skills/security-constant-time-analysis/ct_analyzer/tests/test_samples/vulnerable.rb +188 -0
  122. package/skills/security-constant-time-analysis/ct_analyzer/tests/test_samples/vulnerable.swift +199 -0
  123. package/skills/security-constant-time-analysis/ct_analyzer/tests/test_samples/vulnerable.ts +154 -0
  124. package/skills/security-constant-time-analysis/pyproject.toml +52 -0
  125. package/skills/security-constant-time-analysis/skills/constant-time-analysis/README.md +90 -0
  126. package/skills/security-constant-time-analysis/skills/constant-time-analysis/SKILL.md +219 -0
  127. package/skills/security-constant-time-analysis/skills/constant-time-analysis/references/compiled.md +129 -0
  128. package/skills/security-constant-time-analysis/skills/constant-time-analysis/references/javascript.md +136 -0
  129. package/skills/security-constant-time-analysis/skills/constant-time-analysis/references/kotlin.md +252 -0
  130. package/skills/security-constant-time-analysis/skills/constant-time-analysis/references/php.md +172 -0
  131. package/skills/security-constant-time-analysis/skills/constant-time-analysis/references/python.md +179 -0
  132. package/skills/security-constant-time-analysis/skills/constant-time-analysis/references/ruby.md +198 -0
  133. package/skills/security-constant-time-analysis/skills/constant-time-analysis/references/swift.md +288 -0
  134. package/skills/security-constant-time-analysis/skills/constant-time-analysis/references/vm-compiled.md +354 -0
  135. package/skills/security-constant-time-analysis/uv.lock +8 -0
  136. package/skills/security-culture-index/.claude-plugin/plugin.json +8 -0
  137. package/skills/security-culture-index/README.md +79 -0
  138. package/skills/security-culture-index/skills/interpreting-culture-index/SKILL.md +293 -0
  139. package/skills/security-culture-index/skills/interpreting-culture-index/references/anti-patterns.md +255 -0
  140. package/skills/security-culture-index/skills/interpreting-culture-index/references/conversation-starters.md +408 -0
  141. package/skills/security-culture-index/skills/interpreting-culture-index/references/interview-trait-signals.md +253 -0
  142. package/skills/security-culture-index/skills/interpreting-culture-index/references/motivators.md +158 -0
  143. package/skills/security-culture-index/skills/interpreting-culture-index/references/patterns-archetypes.md +147 -0
  144. package/skills/security-culture-index/skills/interpreting-culture-index/references/primary-traits.md +307 -0
  145. package/skills/security-culture-index/skills/interpreting-culture-index/references/secondary-traits.md +228 -0
  146. package/skills/security-culture-index/skills/interpreting-culture-index/references/team-composition.md +148 -0
  147. package/skills/security-culture-index/skills/interpreting-culture-index/scripts/check_deps.py +108 -0
  148. package/skills/security-culture-index/skills/interpreting-culture-index/scripts/culture_index/__init__.py +20 -0
  149. package/skills/security-culture-index/skills/interpreting-culture-index/scripts/culture_index/constants.py +122 -0
  150. package/skills/security-culture-index/skills/interpreting-culture-index/scripts/culture_index/extract.py +187 -0
  151. package/skills/security-culture-index/skills/interpreting-culture-index/scripts/culture_index/models.py +16 -0
  152. package/skills/security-culture-index/skills/interpreting-culture-index/scripts/culture_index/opencv_extractor.py +520 -0
  153. package/skills/security-culture-index/skills/interpreting-culture-index/scripts/extract_pdf.py +237 -0
  154. package/skills/security-culture-index/skills/interpreting-culture-index/scripts/pyproject.toml +18 -0
  155. package/skills/security-culture-index/skills/interpreting-culture-index/templates/burnout-report.md +113 -0
  156. package/skills/security-culture-index/skills/interpreting-culture-index/templates/comparison-report.md +103 -0
  157. package/skills/security-culture-index/skills/interpreting-culture-index/templates/hiring-profile.md +127 -0
  158. package/skills/security-culture-index/skills/interpreting-culture-index/templates/individual-report.md +85 -0
  159. package/skills/security-culture-index/skills/interpreting-culture-index/templates/predicted-profile.md +165 -0
  160. package/skills/security-culture-index/skills/interpreting-culture-index/templates/team-report.md +109 -0
  161. package/skills/security-culture-index/skills/interpreting-culture-index/workflows/analyze-team.md +188 -0
  162. package/skills/security-culture-index/skills/interpreting-culture-index/workflows/coach-manager.md +267 -0
  163. package/skills/security-culture-index/skills/interpreting-culture-index/workflows/compare-profiles.md +188 -0
  164. package/skills/security-culture-index/skills/interpreting-culture-index/workflows/define-hiring-profile.md +220 -0
  165. package/skills/security-culture-index/skills/interpreting-culture-index/workflows/detect-burnout.md +206 -0
  166. package/skills/security-culture-index/skills/interpreting-culture-index/workflows/extract-from-pdf.md +121 -0
  167. package/skills/security-culture-index/skills/interpreting-culture-index/workflows/interpret-individual.md +183 -0
  168. package/skills/security-culture-index/skills/interpreting-culture-index/workflows/interview-debrief.md +234 -0
  169. package/skills/security-culture-index/skills/interpreting-culture-index/workflows/mediate-conflict.md +306 -0
  170. package/skills/security-culture-index/skills/interpreting-culture-index/workflows/plan-onboarding.md +322 -0
  171. package/skills/security-culture-index/skills/interpreting-culture-index/workflows/predict-from-interview.md +250 -0
  172. package/skills/security-differential-review/.claude-plugin/plugin.json +10 -0
  173. package/skills/security-differential-review/README.md +109 -0
  174. package/skills/security-differential-review/commands/diff-review.md +21 -0
  175. package/skills/security-differential-review/skills/differential-review/SKILL.md +220 -0
  176. package/skills/security-differential-review/skills/differential-review/adversarial.md +203 -0
  177. package/skills/security-differential-review/skills/differential-review/methodology.md +234 -0
  178. package/skills/security-differential-review/skills/differential-review/patterns.md +300 -0
  179. package/skills/security-differential-review/skills/differential-review/reporting.md +369 -0
  180. package/skills/security-dwarf-expert/.claude-plugin/plugin.json +10 -0
  181. package/skills/security-dwarf-expert/README.md +38 -0
  182. package/skills/security-dwarf-expert/skills/dwarf-expert/SKILL.md +93 -0
  183. package/skills/security-dwarf-expert/skills/dwarf-expert/reference/coding.md +31 -0
  184. package/skills/security-dwarf-expert/skills/dwarf-expert/reference/dwarfdump.md +50 -0
  185. package/skills/security-dwarf-expert/skills/dwarf-expert/reference/readelf.md +8 -0
  186. package/skills/security-entry-point-analyzer/.claude-plugin/plugin.json +10 -0
  187. package/skills/security-entry-point-analyzer/README.md +74 -0
  188. package/skills/security-entry-point-analyzer/commands/entry-points.md +18 -0
  189. package/skills/security-entry-point-analyzer/skills/entry-point-analyzer/SKILL.md +251 -0
  190. package/skills/security-entry-point-analyzer/skills/entry-point-analyzer/references/cosmwasm.md +182 -0
  191. package/skills/security-entry-point-analyzer/skills/entry-point-analyzer/references/move-aptos.md +107 -0
  192. package/skills/security-entry-point-analyzer/skills/entry-point-analyzer/references/move-sui.md +87 -0
  193. package/skills/security-entry-point-analyzer/skills/entry-point-analyzer/references/solana.md +155 -0
  194. package/skills/security-entry-point-analyzer/skills/entry-point-analyzer/references/solidity.md +135 -0
  195. package/skills/security-entry-point-analyzer/skills/entry-point-analyzer/references/ton.md +185 -0
  196. package/skills/security-entry-point-analyzer/skills/entry-point-analyzer/references/vyper.md +141 -0
  197. package/skills/security-firebase-apk-scanner/.claude-plugin/plugin.json +10 -0
  198. package/skills/security-firebase-apk-scanner/README.md +85 -0
  199. package/skills/security-firebase-apk-scanner/commands/scan-apk.md +18 -0
  200. package/skills/security-firebase-apk-scanner/scanner.sh +1408 -0
  201. package/skills/security-firebase-apk-scanner/skills/firebase-apk-scanner/SKILL.md +197 -0
  202. package/skills/security-firebase-apk-scanner/skills/firebase-apk-scanner/references/vulnerabilities.md +803 -0
  203. package/skills/security-fix-review/.claude-plugin/plugin.json +13 -0
  204. package/skills/security-fix-review/README.md +118 -0
  205. package/skills/security-fix-review/commands/fix-review.md +24 -0
  206. package/skills/security-fix-review/skills/fix-review/SKILL.md +264 -0
  207. package/skills/security-fix-review/skills/fix-review/references/bug-detection.md +408 -0
  208. package/skills/security-fix-review/skills/fix-review/references/finding-matching.md +298 -0
  209. package/skills/security-fix-review/skills/fix-review/references/report-parsing.md +398 -0
  210. package/skills/security-insecure-defaults/.claude-plugin/plugin.json +10 -0
  211. package/skills/security-insecure-defaults/README.md +45 -0
  212. package/skills/security-insecure-defaults/skills/insecure-defaults/SKILL.md +117 -0
  213. package/skills/security-insecure-defaults/skills/insecure-defaults/references/examples.md +409 -0
  214. package/skills/security-modern-python/.claude-plugin/plugin.json +10 -0
  215. package/skills/security-modern-python/README.md +58 -0
  216. package/skills/security-modern-python/hooks/hooks.json +16 -0
  217. package/skills/security-modern-python/hooks/intercept-legacy-python.bats +388 -0
  218. package/skills/security-modern-python/hooks/intercept-legacy-python.sh +109 -0
  219. package/skills/security-modern-python/hooks/test_helper.bash +75 -0
  220. package/skills/security-modern-python/skills/modern-python/SKILL.md +333 -0
  221. package/skills/security-modern-python/skills/modern-python/references/dependabot.md +43 -0
  222. package/skills/security-modern-python/skills/modern-python/references/migration-checklist.md +141 -0
  223. package/skills/security-modern-python/skills/modern-python/references/pep723-scripts.md +259 -0
  224. package/skills/security-modern-python/skills/modern-python/references/prek.md +211 -0
  225. package/skills/security-modern-python/skills/modern-python/references/pyproject.md +254 -0
  226. package/skills/security-modern-python/skills/modern-python/references/ruff-config.md +240 -0
  227. package/skills/security-modern-python/skills/modern-python/references/security-setup.md +255 -0
  228. package/skills/security-modern-python/skills/modern-python/references/testing.md +284 -0
  229. package/skills/security-modern-python/skills/modern-python/references/uv-commands.md +200 -0
  230. package/skills/security-modern-python/skills/modern-python/templates/dependabot.yml +36 -0
  231. package/skills/security-modern-python/skills/modern-python/templates/pre-commit-config.yaml +66 -0
  232. package/skills/security-property-based-testing/.claude-plugin/plugin.json +9 -0
  233. package/skills/security-property-based-testing/README.md +47 -0
  234. package/skills/security-property-based-testing/skills/property-based-testing/README.md +88 -0
  235. package/skills/security-property-based-testing/skills/property-based-testing/SKILL.md +109 -0
  236. package/skills/security-property-based-testing/skills/property-based-testing/references/design.md +191 -0
  237. package/skills/security-property-based-testing/skills/property-based-testing/references/generating.md +200 -0
  238. package/skills/security-property-based-testing/skills/property-based-testing/references/libraries.md +130 -0
  239. package/skills/security-property-based-testing/skills/property-based-testing/references/refactoring.md +181 -0
  240. package/skills/security-property-based-testing/skills/property-based-testing/references/reviewing.md +209 -0
  241. package/skills/security-property-based-testing/skills/property-based-testing/references/strategies.md +124 -0
  242. package/skills/semgrep-rule-creator/.claude-plugin/plugin.json +8 -0
  243. package/skills/semgrep-rule-creator/README.md +43 -0
  244. package/skills/semgrep-rule-creator/commands/semgrep-rule.md +26 -0
  245. package/skills/semgrep-rule-creator/skills/semgrep-rule-creator/SKILL.md +168 -0
  246. package/skills/semgrep-rule-creator/skills/semgrep-rule-creator/references/quick-reference.md +203 -0
  247. package/skills/semgrep-rule-creator/skills/semgrep-rule-creator/references/workflow.md +240 -0
  248. package/skills/semgrep-rule-variant-creator/.claude-plugin/plugin.json +9 -0
  249. package/skills/semgrep-rule-variant-creator/README.md +86 -0
  250. package/skills/semgrep-rule-variant-creator/skills/semgrep-rule-variant-creator/SKILL.md +205 -0
  251. package/skills/semgrep-rule-variant-creator/skills/semgrep-rule-variant-creator/references/applicability-analysis.md +250 -0
  252. package/skills/semgrep-rule-variant-creator/skills/semgrep-rule-variant-creator/references/language-syntax-guide.md +324 -0
  253. package/skills/semgrep-rule-variant-creator/skills/semgrep-rule-variant-creator/references/workflow.md +518 -0
  254. package/skills/session-logs/SKILL.md +115 -0
  255. package/skills/sharp-edges/.claude-plugin/plugin.json +10 -0
  256. package/skills/sharp-edges/README.md +48 -0
  257. package/skills/sharp-edges/skills/sharp-edges/SKILL.md +292 -0
  258. package/skills/sharp-edges/skills/sharp-edges/references/auth-patterns.md +252 -0
  259. package/skills/sharp-edges/skills/sharp-edges/references/case-studies.md +274 -0
  260. package/skills/sharp-edges/skills/sharp-edges/references/config-patterns.md +333 -0
  261. package/skills/sharp-edges/skills/sharp-edges/references/crypto-apis.md +190 -0
  262. package/skills/sharp-edges/skills/sharp-edges/references/lang-c.md +205 -0
  263. package/skills/sharp-edges/skills/sharp-edges/references/lang-csharp.md +285 -0
  264. package/skills/sharp-edges/skills/sharp-edges/references/lang-go.md +270 -0
  265. package/skills/sharp-edges/skills/sharp-edges/references/lang-java.md +263 -0
  266. package/skills/sharp-edges/skills/sharp-edges/references/lang-javascript.md +269 -0
  267. package/skills/sharp-edges/skills/sharp-edges/references/lang-kotlin.md +265 -0
  268. package/skills/sharp-edges/skills/sharp-edges/references/lang-php.md +245 -0
  269. package/skills/sharp-edges/skills/sharp-edges/references/lang-python.md +274 -0
  270. package/skills/sharp-edges/skills/sharp-edges/references/lang-ruby.md +273 -0
  271. package/skills/sharp-edges/skills/sharp-edges/references/lang-rust.md +272 -0
  272. package/skills/sharp-edges/skills/sharp-edges/references/lang-swift.md +287 -0
  273. package/skills/sharp-edges/skills/sharp-edges/references/language-specific.md +588 -0
  274. package/skills/sherpa-onnx-tts/SKILL.md +103 -0
  275. package/skills/sherpa-onnx-tts/bin/sherpa-onnx-tts +178 -0
  276. package/skills/skill-creator/SKILL.md +370 -0
  277. package/skills/skill-creator/license.txt +202 -0
  278. package/skills/skill-creator/scripts/init_skill.py +378 -0
  279. package/skills/skill-creator/scripts/package_skill.py +111 -0
  280. package/skills/skill-creator/scripts/quick_validate.py +101 -0
  281. package/skills/slack/SKILL.md +144 -0
  282. package/skills/songsee/SKILL.md +49 -0
  283. package/skills/sonoscli/SKILL.md +46 -0
  284. package/skills/spec-to-code-compliance/.claude-plugin/plugin.json +10 -0
  285. package/skills/spec-to-code-compliance/README.md +67 -0
  286. package/skills/spec-to-code-compliance/commands/spec-compliance.md +22 -0
  287. package/skills/spec-to-code-compliance/skills/spec-to-code-compliance/SKILL.md +349 -0
  288. package/skills/spec-to-code-compliance/skills/spec-to-code-compliance/resources/COMPLETENESS_CHECKLIST.md +69 -0
  289. package/skills/spec-to-code-compliance/skills/spec-to-code-compliance/resources/IR_EXAMPLES.md +417 -0
  290. package/skills/spec-to-code-compliance/skills/spec-to-code-compliance/resources/OUTPUT_REQUIREMENTS.md +105 -0
  291. package/skills/spotify-player/SKILL.md +64 -0
  292. package/skills/static-analysis/.claude-plugin/plugin.json +8 -0
  293. package/skills/static-analysis/README.md +59 -0
  294. package/skills/static-analysis/skills/codeql/SKILL.md +315 -0
  295. package/skills/static-analysis/skills/sarif-parsing/SKILL.md +479 -0
  296. package/skills/static-analysis/skills/sarif-parsing/resources/jq-queries.md +162 -0
  297. package/skills/static-analysis/skills/sarif-parsing/resources/sarif_helpers.py +331 -0
  298. package/skills/static-analysis/skills/semgrep/SKILL.md +337 -0
  299. package/skills/summarize/SKILL.md +87 -0
  300. package/skills/testing-handbook-skills/.claude-plugin/plugin.json +8 -0
  301. package/skills/testing-handbook-skills/README.md +241 -0
  302. package/skills/testing-handbook-skills/scripts/pyproject.toml +8 -0
  303. package/skills/testing-handbook-skills/scripts/validate-skills.py +657 -0
  304. package/skills/testing-handbook-skills/skills/address-sanitizer/SKILL.md +341 -0
  305. package/skills/testing-handbook-skills/skills/aflpp/SKILL.md +640 -0
  306. package/skills/testing-handbook-skills/skills/atheris/SKILL.md +515 -0
  307. package/skills/testing-handbook-skills/skills/cargo-fuzz/SKILL.md +454 -0
  308. package/skills/testing-handbook-skills/skills/codeql/SKILL.md +549 -0
  309. package/skills/testing-handbook-skills/skills/constant-time-testing/SKILL.md +507 -0
  310. package/skills/testing-handbook-skills/skills/coverage-analysis/SKILL.md +607 -0
  311. package/skills/testing-handbook-skills/skills/fuzzing-dictionary/SKILL.md +297 -0
  312. package/skills/testing-handbook-skills/skills/fuzzing-obstacles/SKILL.md +426 -0
  313. package/skills/testing-handbook-skills/skills/harness-writing/SKILL.md +614 -0
  314. package/skills/testing-handbook-skills/skills/libafl/SKILL.md +625 -0
  315. package/skills/testing-handbook-skills/skills/libfuzzer/SKILL.md +795 -0
  316. package/skills/testing-handbook-skills/skills/ossfuzz/SKILL.md +426 -0
  317. package/skills/testing-handbook-skills/skills/ruzzy/SKILL.md +443 -0
  318. package/skills/testing-handbook-skills/skills/semgrep/SKILL.md +601 -0
  319. package/skills/testing-handbook-skills/skills/testing-handbook-generator/SKILL.md +372 -0
  320. package/skills/testing-handbook-skills/skills/testing-handbook-generator/agent-prompt.md +280 -0
  321. package/skills/testing-handbook-skills/skills/testing-handbook-generator/discovery.md +452 -0
  322. package/skills/testing-handbook-skills/skills/testing-handbook-generator/templates/domain-skill.md +504 -0
  323. package/skills/testing-handbook-skills/skills/testing-handbook-generator/templates/fuzzer-skill.md +454 -0
  324. package/skills/testing-handbook-skills/skills/testing-handbook-generator/templates/technique-skill.md +527 -0
  325. package/skills/testing-handbook-skills/skills/testing-handbook-generator/templates/tool-skill.md +366 -0
  326. package/skills/testing-handbook-skills/skills/testing-handbook-generator/testing.md +482 -0
  327. package/skills/testing-handbook-skills/skills/wycheproof/SKILL.md +533 -0
  328. package/skills/things-mac/SKILL.md +86 -0
  329. package/skills/tmux/SKILL.md +135 -0
  330. package/skills/tmux/scripts/find-sessions.sh +112 -0
  331. package/skills/tmux/scripts/wait-for-text.sh +83 -0
  332. package/skills/trello/SKILL.md +95 -0
  333. package/skills/variant-analysis/.claude-plugin/plugin.json +8 -0
  334. package/skills/variant-analysis/README.md +41 -0
  335. package/skills/variant-analysis/commands/variants.md +23 -0
  336. package/skills/variant-analysis/skills/variant-analysis/METHODOLOGY.md +327 -0
  337. package/skills/variant-analysis/skills/variant-analysis/SKILL.md +142 -0
  338. package/skills/variant-analysis/skills/variant-analysis/resources/codeql/cpp.ql +119 -0
  339. package/skills/variant-analysis/skills/variant-analysis/resources/codeql/go.ql +69 -0
  340. package/skills/variant-analysis/skills/variant-analysis/resources/codeql/java.ql +71 -0
  341. package/skills/variant-analysis/skills/variant-analysis/resources/codeql/javascript.ql +63 -0
  342. package/skills/variant-analysis/skills/variant-analysis/resources/codeql/python.ql +80 -0
  343. package/skills/variant-analysis/skills/variant-analysis/resources/semgrep/cpp.yaml +98 -0
  344. package/skills/variant-analysis/skills/variant-analysis/resources/semgrep/go.yaml +63 -0
  345. package/skills/variant-analysis/skills/variant-analysis/resources/semgrep/java.yaml +61 -0
  346. package/skills/variant-analysis/skills/variant-analysis/resources/semgrep/javascript.yaml +60 -0
  347. package/skills/variant-analysis/skills/variant-analysis/resources/semgrep/python.yaml +72 -0
  348. package/skills/variant-analysis/skills/variant-analysis/resources/variant-report-template.md +75 -0
  349. package/skills/video-frames/SKILL.md +46 -0
  350. package/skills/video-frames/scripts/frame.sh +81 -0
  351. package/skills/voice-call/SKILL.md +45 -0
  352. package/skills/wacli/SKILL.md +72 -0
  353. package/skills/weather/SKILL.md +54 -0
  354. package/skills/yara-authoring/.claude-plugin/plugin.json +9 -0
  355. package/skills/yara-authoring/README.md +131 -0
  356. package/skills/yara-authoring/skills/yara-rule-authoring/SKILL.md +645 -0
  357. package/skills/yara-authoring/skills/yara-rule-authoring/examples/MAL_Mac_ProtonRAT_Jan25.yar +99 -0
  358. package/skills/yara-authoring/skills/yara-rule-authoring/examples/MAL_NPM_SupplyChain_Jan25.yar +170 -0
  359. package/skills/yara-authoring/skills/yara-rule-authoring/examples/MAL_Win_Remcos_Jan25.yar +103 -0
  360. package/skills/yara-authoring/skills/yara-rule-authoring/examples/SUSP_CRX_SuspiciousPermissions.yar +134 -0
  361. package/skills/yara-authoring/skills/yara-rule-authoring/examples/SUSP_JS_Obfuscation_Jan25.yar +185 -0
  362. package/skills/yara-authoring/skills/yara-rule-authoring/references/crx-module.md +214 -0
  363. package/skills/yara-authoring/skills/yara-rule-authoring/references/dex-module.md +383 -0
  364. package/skills/yara-authoring/skills/yara-rule-authoring/references/performance.md +333 -0
  365. package/skills/yara-authoring/skills/yara-rule-authoring/references/strings.md +433 -0
  366. package/skills/yara-authoring/skills/yara-rule-authoring/references/style-guide.md +257 -0
  367. package/skills/yara-authoring/skills/yara-rule-authoring/references/testing.md +399 -0
  368. package/skills/yara-authoring/skills/yara-rule-authoring/scripts/atom_analyzer.py +526 -0
  369. package/skills/yara-authoring/skills/yara-rule-authoring/scripts/pyproject.toml +25 -0
  370. package/skills/yara-authoring/skills/yara-rule-authoring/scripts/yara_lint.py +631 -0
  371. package/skills/yara-authoring/skills/yara-rule-authoring/workflows/rule-development.md +493 -0
@@ -0,0 +1,520 @@
1
+ """OpenCV-based extraction for Culture Index profiles.
2
+
3
+ Uses HSV color detection, shape analysis, and OCR to extract trait values,
4
+ arrow positions, and EU values from Culture Index PDF charts.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import re
10
+ from pathlib import Path
11
+ from typing import TypedDict
12
+
13
+ import cv2
14
+ import numpy as np
15
+ from pdf2image import convert_from_path
16
+
17
+ from culture_index.constants import (
18
+ ARCHETYPES,
19
+ OPENCV_A_MIN_AREA,
20
+ OPENCV_ARCHETYPE_REGION_Y_END,
21
+ OPENCV_ARROW_AREA_MAX,
22
+ OPENCV_ARROW_AREA_MIN,
23
+ OPENCV_ARROW_ASPECT_MAX,
24
+ OPENCV_ARROW_HUE_HIGH,
25
+ OPENCV_ARROW_HUE_LOW,
26
+ OPENCV_ARROW_MIN_HEIGHT,
27
+ OPENCV_ARROW_SAT_MIN,
28
+ OPENCV_ARROW_VAL_MIN,
29
+ OPENCV_CHART_X_END,
30
+ OPENCV_CHART_X_START,
31
+ OPENCV_COMPANY_REGION_Y_END,
32
+ OPENCV_DOT_AREA_MAX,
33
+ OPENCV_DOT_AREA_MIN,
34
+ OPENCV_DOT_ASPECT_MAX,
35
+ OPENCV_DOT_ASPECT_MIN,
36
+ OPENCV_EU_REGION_X_END,
37
+ OPENCV_EU_REGION_X_START,
38
+ OPENCV_HEADER_DOT_Y_MAX,
39
+ OPENCV_HUE_A_HIGH,
40
+ OPENCV_HUE_A_LOW,
41
+ OPENCV_HUE_B_MAX,
42
+ OPENCV_HUE_B_MIN,
43
+ OPENCV_HUE_C_MAX,
44
+ OPENCV_HUE_C_MIN,
45
+ OPENCV_HUE_D_MAX,
46
+ OPENCV_HUE_D_MIN,
47
+ OPENCV_HUE_I_MIN,
48
+ OPENCV_HUE_L_MAX,
49
+ OPENCV_HUE_L_MIN,
50
+ OPENCV_JOB_Y_END,
51
+ OPENCV_JOB_Y_START,
52
+ OPENCV_METADATA_X_START,
53
+ OPENCV_METADATA_Y_END,
54
+ OPENCV_METADATA_Y_START,
55
+ OPENCV_MIN_CONTOUR_AREA,
56
+ OPENCV_NAME_REGION_Y_END,
57
+ OPENCV_SATURATION_MIN,
58
+ OPENCV_SURVEY_Y_END,
59
+ OPENCV_SURVEY_Y_START,
60
+ OPENCV_VALUE_MIN,
61
+ )
62
+
63
+ # Check pytesseract availability at module load
64
+ try:
65
+ import pytesseract
66
+
67
+ PYTESSERACT_AVAILABLE = True
68
+ except ImportError:
69
+ PYTESSERACT_AVAILABLE = False
70
+ pytesseract = None # type: ignore[assignment]
71
+
72
+ # Track extraction warnings for reporting
73
+ _extraction_warnings: list[str] = []
74
+
75
+
76
+ def get_extraction_warnings() -> list[str]:
77
+ """Get warnings accumulated during extraction.
78
+
79
+ Returns:
80
+ List of warning messages from the last extraction.
81
+ """
82
+ return _extraction_warnings.copy()
83
+
84
+
85
+ def clear_extraction_warnings() -> None:
86
+ """Clear accumulated extraction warnings."""
87
+ _extraction_warnings.clear()
88
+
89
+
90
+ class DotData(TypedDict):
91
+ """Data for a detected trait dot."""
92
+
93
+ x: int
94
+ y: int
95
+ area: float
96
+ hue: int
97
+
98
+
99
+ class ArrowData(TypedDict):
100
+ """Data for a detected arrow (population mean indicator)."""
101
+
102
+ x: int
103
+ y: int
104
+ w: int
105
+ h: int
106
+ area: float
107
+
108
+
109
+ def pixel_to_scale(x: int, x_start: int, x_end: int, as_float: bool = False) -> int | float:
110
+ """Convert x pixel coordinate to 0-10 scale value.
111
+
112
+ Args:
113
+ x: X coordinate in pixels.
114
+ x_start: X coordinate for position 0.
115
+ x_end: X coordinate for position 10.
116
+ as_float: If True, return tenths precision for arrows.
117
+
118
+ Returns:
119
+ Scale value 0-10 (int when as_float=False, float when as_float=True).
120
+ """
121
+ relative = (x - x_start) / (x_end - x_start)
122
+ if as_float:
123
+ return round(max(0.0, min(10.0, relative * 10)), 1)
124
+ return max(0, min(10, round(relative * 10)))
125
+
126
+
127
+ def _extract_text_from_region(img_rgb: np.ndarray, region: tuple[int, int, int, int]) -> str:
128
+ """Extract text from a region using OCR.
129
+
130
+ Args:
131
+ img_rgb: RGB image array.
132
+ region: (x1, y1, x2, y2) bounding box.
133
+
134
+ Returns:
135
+ Extracted text, or empty string if OCR unavailable or fails.
136
+ """
137
+ if not PYTESSERACT_AVAILABLE:
138
+ if "pytesseract not installed" not in str(_extraction_warnings):
139
+ _extraction_warnings.append(
140
+ "pytesseract not installed - text extraction disabled. "
141
+ "Install with: pip install pytesseract && brew install tesseract"
142
+ )
143
+ return ""
144
+
145
+ x1, y1, x2, y2 = region
146
+ roi = img_rgb[y1:y2, x1:x2]
147
+ gray = cv2.cvtColor(roi, cv2.COLOR_RGB2GRAY)
148
+ return pytesseract.image_to_string(gray, config="--psm 6")
149
+
150
+
151
+ def _extract_eu(img_rgb: np.ndarray, region: tuple[int, int, int, int]) -> int | None:
152
+ """Extract EU value from a region using OCR.
153
+
154
+ Args:
155
+ img_rgb: RGB image array.
156
+ region: (x1, y1, x2, y2) bounding box.
157
+
158
+ Returns:
159
+ EU value as integer, or None if not found.
160
+ """
161
+ text = _extract_text_from_region(img_rgb, region)
162
+ if not text:
163
+ return None
164
+ match = re.search(r"EU\s*=?\s*(\d+)", text)
165
+ return int(match.group(1)) if match else None
166
+
167
+
168
+ def _is_valid_name(text: str) -> bool:
169
+ """Check if text looks like a person's name (2-4 words).
170
+
171
+ Args:
172
+ text: Candidate name string.
173
+
174
+ Returns:
175
+ True if text appears to be a valid name.
176
+ """
177
+ words = text.split()
178
+ if not (2 <= len(words) <= 4):
179
+ return False
180
+ for word in words:
181
+ cleaned = word.replace("'", "").replace("-", "")
182
+ if not cleaned or not cleaned.isalpha():
183
+ return False
184
+ return True
185
+
186
+
187
+ def _clean_ocr_value(value: str, field_key: str) -> str:
188
+ """Clean common OCR artifacts from metadata values.
189
+
190
+ Args:
191
+ value: Raw OCR value.
192
+ field_key: The field key (email, phone, etc.) for field-specific cleanup.
193
+
194
+ Returns:
195
+ Cleaned value string.
196
+ """
197
+ value = value.lstrip("| ")
198
+ if field_key == "email":
199
+ value = re.sub(r"\s+@", "@", value)
200
+ return value.strip()
201
+
202
+
203
+ def _parse_metadata_column(text: str) -> dict[str, str]:
204
+ """Parse structured metadata from right column text.
205
+
206
+ Args:
207
+ text: Raw OCR text from metadata column.
208
+
209
+ Returns:
210
+ Dict with parsed metadata fields.
211
+ """
212
+ result: dict[str, str] = {}
213
+ lines = [line.strip() for line in text.split("\n") if line.strip()]
214
+
215
+ field_mapping = {
216
+ "survey date": "date",
217
+ "email": "email",
218
+ "phone": "phone",
219
+ "job title": "job_title",
220
+ "location": "location",
221
+ "administered by": "administered_by",
222
+ "survey type": "survey_type",
223
+ "survey id": "survey_id",
224
+ }
225
+
226
+ i = 0
227
+ while i < len(lines) - 1:
228
+ line_lower = lines[i].lower()
229
+ for label, key in field_mapping.items():
230
+ if label in line_lower:
231
+ value = lines[i + 1].strip()
232
+ if value and not any(lbl in value.lower() for lbl in field_mapping):
233
+ result[key] = _clean_ocr_value(value, key)
234
+ i += 1
235
+ break
236
+ i += 1
237
+
238
+ return result
239
+
240
+
241
+ def _parse_name_from_filename(stem: str) -> str:
242
+ """Parse person's name from PDF filename.
243
+
244
+ Args:
245
+ stem: PDF filename without extension.
246
+
247
+ Returns:
248
+ Parsed name string.
249
+ """
250
+ match = re.match(r"^(.+?)_\((\d+)\)$", stem)
251
+ if match:
252
+ return match.group(1).replace("_", " ")
253
+ return stem
254
+
255
+
256
+ def _extract_header_info(img_rgb: np.ndarray, height: int, width: int) -> dict[str, str]:
257
+ """Extract name, archetype, company, and all metadata from landscape layout.
258
+
259
+ Args:
260
+ img_rgb: RGB image array.
261
+ height: Image height in pixels.
262
+ width: Image width in pixels.
263
+
264
+ Returns:
265
+ Dict with extracted header info and metadata.
266
+ """
267
+ result: dict[str, str] = {}
268
+
269
+ # Name: top-left corner
270
+ name_region = (0, 0, int(width * 0.5), int(height * OPENCV_NAME_REGION_Y_END))
271
+ name_text = _extract_text_from_region(img_rgb, name_region)
272
+ if name_text:
273
+ for line in name_text.split("\n"):
274
+ line = line.strip()
275
+ if line and _is_valid_name(line):
276
+ result["name"] = line.title()
277
+ break
278
+
279
+ # Company: below name
280
+ company_region = (
281
+ 0,
282
+ int(height * OPENCV_NAME_REGION_Y_END),
283
+ int(width * 0.5),
284
+ int(height * OPENCV_COMPANY_REGION_Y_END),
285
+ )
286
+ company_text = _extract_text_from_region(img_rgb, company_region)
287
+ if company_text:
288
+ company_line = company_text.strip().split("\n")[0].strip()
289
+ company_line = _clean_ocr_value(company_line, "company")
290
+ if company_line and len(company_line) > 1:
291
+ result["company"] = company_line
292
+
293
+ # Archetype: below company
294
+ archetype_region = (
295
+ 0,
296
+ int(height * OPENCV_COMPANY_REGION_Y_END),
297
+ int(width * 0.5),
298
+ int(height * OPENCV_ARCHETYPE_REGION_Y_END),
299
+ )
300
+ archetype_text = _extract_text_from_region(img_rgb, archetype_region)
301
+ for archetype in ARCHETYPES:
302
+ if archetype.upper() in archetype_text.upper():
303
+ result["archetype"] = archetype
304
+ break
305
+
306
+ # Metadata column (right side)
307
+ metadata_region = (
308
+ int(width * OPENCV_METADATA_X_START),
309
+ int(height * OPENCV_METADATA_Y_START),
310
+ width,
311
+ int(height * OPENCV_METADATA_Y_END),
312
+ )
313
+ metadata_text = _extract_text_from_region(img_rgb, metadata_region)
314
+ result.update(_parse_metadata_column(metadata_text))
315
+
316
+ return result
317
+
318
+
319
+ def _classify_contour(
320
+ contour: np.ndarray, img_hsv: np.ndarray
321
+ ) -> tuple[str | None, DotData | ArrowData | dict[str, object]]:
322
+ """Classify a contour as dot, arrow, or noise.
323
+
324
+ Args:
325
+ contour: OpenCV contour.
326
+ img_hsv: HSV image array.
327
+
328
+ Returns:
329
+ Tuple of (type, data) where type is "dot", "arrow", or None.
330
+ Data is DotData for dots, ArrowData for arrows, empty dict for noise.
331
+ """
332
+ area = cv2.contourArea(contour)
333
+ if area < OPENCV_MIN_CONTOUR_AREA:
334
+ return None, {}
335
+
336
+ x, y, w, h = cv2.boundingRect(contour)
337
+ aspect = w / h if h > 0 else 0
338
+ cx, cy = x + w // 2, y + h // 2
339
+ hsv = img_hsv[cy, cx]
340
+ hue, sat, val = int(hsv[0]), int(hsv[1]), int(hsv[2])
341
+
342
+ # Arrow: bright red, tall thin shape
343
+ is_red_hue = hue <= OPENCV_ARROW_HUE_LOW or hue >= OPENCV_ARROW_HUE_HIGH
344
+ is_bright = sat > OPENCV_ARROW_SAT_MIN and val > OPENCV_ARROW_VAL_MIN
345
+ is_tall_thin = aspect < OPENCV_ARROW_ASPECT_MAX and h > OPENCV_ARROW_MIN_HEIGHT
346
+ is_arrow_sized = OPENCV_ARROW_AREA_MIN < area < OPENCV_ARROW_AREA_MAX
347
+
348
+ if is_red_hue and is_bright and is_tall_thin and is_arrow_sized:
349
+ return "arrow", ArrowData(x=cx, y=cy, w=w, h=h, area=area)
350
+
351
+ # Dot: circular shape
352
+ is_dot_sized = OPENCV_DOT_AREA_MIN < area < OPENCV_DOT_AREA_MAX
353
+ is_circular = OPENCV_DOT_ASPECT_MIN < aspect < OPENCV_DOT_ASPECT_MAX
354
+ if is_dot_sized and is_circular:
355
+ return "dot", DotData(x=cx, y=cy, area=area, hue=hue)
356
+
357
+ return None, {}
358
+
359
+
360
+ def map_dot_to_trait(dot: DotData, x_start: int, x_end: int) -> tuple[str | None, int]:
361
+ """Map a detected dot to its trait letter based on hue.
362
+
363
+ Args:
364
+ dot: DotData with hue, area, x keys.
365
+ x_start: X coordinate for position 0.
366
+ x_end: X coordinate for position 10.
367
+
368
+ Returns:
369
+ Tuple of (trait_letter, scale_value) or (None, 0) if invalid.
370
+ """
371
+ h, area = dot["hue"], dot["area"]
372
+ val = pixel_to_scale(dot["x"], x_start, x_end)
373
+ # Ensure val is int for return type
374
+ val = int(val)
375
+
376
+ if h >= OPENCV_HUE_A_HIGH or (h <= OPENCV_HUE_A_LOW and area > OPENCV_A_MIN_AREA):
377
+ return "a", val
378
+ if OPENCV_HUE_B_MIN <= h <= OPENCV_HUE_B_MAX:
379
+ return "b", val
380
+ if OPENCV_HUE_C_MIN <= h <= OPENCV_HUE_C_MAX:
381
+ return "c", val
382
+ if OPENCV_HUE_D_MIN <= h <= OPENCV_HUE_D_MAX:
383
+ return "d", val
384
+ if OPENCV_HUE_L_MIN <= h <= OPENCV_HUE_L_MAX:
385
+ return "l", val
386
+ if OPENCV_HUE_I_MIN <= h < OPENCV_HUE_C_MIN:
387
+ return "i", val
388
+ return None, 0
389
+
390
+
391
+ def _extract_chart(
392
+ dots: list[DotData],
393
+ arrows: list[ArrowData],
394
+ eu_region: tuple[int, int, int, int],
395
+ img_rgb: np.ndarray,
396
+ x_start: int,
397
+ x_end: int,
398
+ ) -> dict[str, int | float | None]:
399
+ """Extract trait values, arrow position, and EU from one chart.
400
+
401
+ Args:
402
+ dots: List of detected dots in this chart region.
403
+ arrows: List of detected arrows in this chart region.
404
+ eu_region: (x1, y1, x2, y2) for EU text area.
405
+ img_rgb: RGB image for OCR.
406
+ x_start: X coordinate for position 0.
407
+ x_end: X coordinate for position 10.
408
+
409
+ Returns:
410
+ Dict with trait values, arrow, and EU.
411
+ """
412
+ result: dict[str, int | float | None] = {"a": 0, "b": 0, "c": 0, "d": 0, "l": 0, "i": 0}
413
+
414
+ for dot in dots:
415
+ trait, val = map_dot_to_trait(dot, x_start, x_end)
416
+ if trait:
417
+ result[trait] = val
418
+
419
+ if arrows:
420
+ sorted_arrows = sorted(arrows, key=lambda a: a["area"], reverse=True)
421
+ result["arrow"] = pixel_to_scale(sorted_arrows[0]["x"], x_start, x_end, as_float=True)
422
+
423
+ eu = _extract_eu(img_rgb, eu_region)
424
+ if eu is not None:
425
+ result["eu"] = eu
426
+
427
+ return result
428
+
429
+
430
+ def extract_with_opencv(pdf_path: Path) -> dict[str, object]:
431
+ """Extract chart values and metadata using OpenCV color detection and OCR.
432
+
433
+ Supports the new landscape PDF format with charts on left and metadata on right.
434
+
435
+ Args:
436
+ pdf_path: Path to the PDF file.
437
+
438
+ Returns:
439
+ Dictionary with survey_traits, job_behaviors, metadata, and _warnings list.
440
+ """
441
+ # Clear warnings from previous extraction
442
+ clear_extraction_warnings()
443
+
444
+ images = convert_from_path(str(pdf_path), dpi=300)
445
+ img_rgb = np.array(images[0])
446
+ img_hsv = cv2.cvtColor(cv2.cvtColor(img_rgb, cv2.COLOR_RGB2BGR), cv2.COLOR_BGR2HSV)
447
+ height, width = img_rgb.shape[:2]
448
+
449
+ # Detect colored elements
450
+ sat, val = img_hsv[:, :, 1], img_hsv[:, :, 2]
451
+ mask = (((sat > OPENCV_SATURATION_MIN) & (val > OPENCV_VALUE_MIN)) * 255).astype(np.uint8)
452
+ contours, _ = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
453
+
454
+ header_dot_y_max = int(height * OPENCV_HEADER_DOT_Y_MAX)
455
+
456
+ dots: list[DotData] = []
457
+ arrows: list[ArrowData] = []
458
+ for contour in contours:
459
+ elem_type, data = _classify_contour(contour, img_hsv)
460
+ if elem_type == "dot" and isinstance(data, dict) and "hue" in data:
461
+ if data["y"] < header_dot_y_max:
462
+ continue
463
+ dots.append(data) # type: ignore[arg-type]
464
+ elif elem_type == "arrow" and isinstance(data, dict) and "w" in data:
465
+ arrows.append(data) # type: ignore[arg-type]
466
+
467
+ # Chart region boundaries
468
+ survey_y = (int(height * OPENCV_SURVEY_Y_START), int(height * OPENCV_SURVEY_Y_END))
469
+ job_y = (int(height * OPENCV_JOB_Y_START), int(height * OPENCV_JOB_Y_END))
470
+
471
+ # Filter by region and extract
472
+ survey_dots = [d for d in dots if survey_y[0] < d["y"] < survey_y[1]]
473
+ survey_arrows = [a for a in arrows if survey_y[0] < a["y"] < survey_y[1]]
474
+ job_dots = [d for d in dots if job_y[0] < d["y"] < job_y[1]]
475
+ job_arrows = [a for a in arrows if job_y[0] < a["y"] < job_y[1]]
476
+
477
+ survey_eu_region = (OPENCV_EU_REGION_X_START, survey_y[0], OPENCV_EU_REGION_X_END, survey_y[1])
478
+ job_eu_region = (OPENCV_EU_REGION_X_START, job_y[0], OPENCV_EU_REGION_X_END, job_y[1])
479
+
480
+ survey_data = _extract_chart(
481
+ survey_dots,
482
+ survey_arrows,
483
+ survey_eu_region,
484
+ img_rgb,
485
+ OPENCV_CHART_X_START,
486
+ OPENCV_CHART_X_END,
487
+ )
488
+ job_data = _extract_chart(
489
+ job_dots,
490
+ job_arrows,
491
+ job_eu_region,
492
+ img_rgb,
493
+ OPENCV_CHART_X_START,
494
+ OPENCV_CHART_X_END,
495
+ )
496
+
497
+ header_info = _extract_header_info(img_rgb, height, width)
498
+
499
+ # Fallback: parse name from filename if OCR failed
500
+ if "name" not in header_info:
501
+ filename_name = _parse_name_from_filename(pdf_path.stem)
502
+ if _is_valid_name(filename_name):
503
+ header_info["name"] = filename_name
504
+ else:
505
+ _extraction_warnings.append(
506
+ f"Could not extract name from PDF or filename: {pdf_path.name}"
507
+ )
508
+ header_info["name"] = "Unknown" # Ensure name field always exists
509
+
510
+ # Add warnings to result if any
511
+ result: dict[str, object] = {
512
+ "survey_traits": survey_data,
513
+ "job_behaviors": job_data,
514
+ **header_info,
515
+ }
516
+
517
+ if _extraction_warnings:
518
+ result["_warnings"] = get_extraction_warnings()
519
+
520
+ return result