opencode-skills-collection 3.1.0 → 3.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (291) hide show
  1. package/bundled-skills/.antigravity-install-manifest.json +84 -1
  2. package/bundled-skills/2slides-ppt-generator/SKILL.md +8 -7
  3. package/bundled-skills/android-cli/SKILL.md +19 -7
  4. package/bundled-skills/android-ui-journey-testing/SKILL.md +191 -0
  5. package/bundled-skills/apple-notes-search/SKILL.md +12 -2
  6. package/bundled-skills/ask-matt/SKILL.md +92 -0
  7. package/bundled-skills/atlas-ledger/SKILL.md +8 -0
  8. package/bundled-skills/bugs-are-annoying/SKILL.md +137 -0
  9. package/bundled-skills/codebase-design/DEEPENING.md +37 -0
  10. package/bundled-skills/codebase-design/DESIGN-IT-TWICE.md +44 -0
  11. package/bundled-skills/codebase-design/SKILL.md +145 -0
  12. package/bundled-skills/codex-fable5/SKILL.md +10 -2
  13. package/bundled-skills/competitor-analysis/LICENSE.txt +21 -0
  14. package/bundled-skills/competitor-analysis/SKILL.md +434 -0
  15. package/bundled-skills/competitor-analysis/references/battle-card-subagent.md +127 -0
  16. package/bundled-skills/competitor-analysis/references/battle-card.md +91 -0
  17. package/bundled-skills/competitor-analysis/references/example-research.md +130 -0
  18. package/bundled-skills/competitor-analysis/references/report-template.html +127 -0
  19. package/bundled-skills/competitor-analysis/references/research-patterns.md +217 -0
  20. package/bundled-skills/competitor-analysis/references/workflow.md +434 -0
  21. package/bundled-skills/competitor-analysis/scripts/capture_screenshots.mjs +142 -0
  22. package/bundled-skills/competitor-analysis/scripts/compile_report.mjs +929 -0
  23. package/bundled-skills/competitor-analysis/scripts/extract_vs_names.mjs +140 -0
  24. package/bundled-skills/competitor-analysis/scripts/gate_candidates.mjs +224 -0
  25. package/bundled-skills/competitor-analysis/scripts/list_urls.mjs +90 -0
  26. package/bundled-skills/competitor-analysis/scripts/md_utils.mjs +50 -0
  27. package/bundled-skills/competitor-analysis/scripts/merge_partials.mjs +291 -0
  28. package/bundled-skills/competitor-analysis/scripts/package.json +6 -0
  29. package/bundled-skills/design-it/3d-ui/SKILL.md +259 -0
  30. package/bundled-skills/design-it/SKILL.md +170 -0
  31. package/bundled-skills/design-it/ai-native-ui/SKILL.md +295 -0
  32. package/bundled-skills/design-it/aurora-ui/SKILL.md +307 -0
  33. package/bundled-skills/design-it/bento-ui/SKILL.md +314 -0
  34. package/bundled-skills/design-it/brutalism/SKILL.md +270 -0
  35. package/bundled-skills/design-it/brutalist-typography/SKILL.md +287 -0
  36. package/bundled-skills/design-it/card-based-design/SKILL.md +262 -0
  37. package/bundled-skills/design-it/claymorphism/SKILL.md +287 -0
  38. package/bundled-skills/design-it/color-blocking/SKILL.md +278 -0
  39. package/bundled-skills/design-it/command-center-ui/SKILL.md +345 -0
  40. package/bundled-skills/design-it/cyber-y2k/SKILL.md +312 -0
  41. package/bundled-skills/design-it/cyberpunk-ui/SKILL.md +262 -0
  42. package/bundled-skills/design-it/dark-mode/SKILL.md +289 -0
  43. package/bundled-skills/design-it/dashboard-design/SKILL.md +331 -0
  44. package/bundled-skills/design-it/data-dense-design/SKILL.md +322 -0
  45. package/bundled-skills/design-it/duotone-design/SKILL.md +248 -0
  46. package/bundled-skills/design-it/editorial-design/SKILL.md +328 -0
  47. package/bundled-skills/design-it/flat-design/SKILL.md +221 -0
  48. package/bundled-skills/design-it/flat-design-2/SKILL.md +240 -0
  49. package/bundled-skills/design-it/floating-ui/SKILL.md +299 -0
  50. package/bundled-skills/design-it/frutiger-aero/SKILL.md +274 -0
  51. package/bundled-skills/design-it/glassmorphism/SKILL.md +272 -0
  52. package/bundled-skills/design-it/gradient-design/SKILL.md +309 -0
  53. package/bundled-skills/design-it/high-contrast/SKILL.md +288 -0
  54. package/bundled-skills/design-it/holographic-ui/SKILL.md +310 -0
  55. package/bundled-skills/design-it/isometric-design/SKILL.md +228 -0
  56. package/bundled-skills/design-it/layered-design/SKILL.md +247 -0
  57. package/bundled-skills/design-it/material-design/SKILL.md +275 -0
  58. package/bundled-skills/design-it/maximalism/SKILL.md +297 -0
  59. package/bundled-skills/design-it/minimalism/SKILL.md +267 -0
  60. package/bundled-skills/design-it/monochromatic-ui/SKILL.md +296 -0
  61. package/bundled-skills/design-it/neo-brutalism/SKILL.md +270 -0
  62. package/bundled-skills/design-it/neumorphism/SKILL.md +248 -0
  63. package/bundled-skills/design-it/retro-design/SKILL.md +283 -0
  64. package/bundled-skills/design-it/retro-futurism/SKILL.md +259 -0
  65. package/bundled-skills/design-it/sci-fi-interface/SKILL.md +309 -0
  66. package/bundled-skills/design-it/skeuomorphism/SKILL.md +280 -0
  67. package/bundled-skills/design-it/soft-pastel/SKILL.md +307 -0
  68. package/bundled-skills/design-it/spatial-computing-ui/SKILL.md +300 -0
  69. package/bundled-skills/design-it/spatial-design/SKILL.md +268 -0
  70. package/bundled-skills/design-it/swiss-design/SKILL.md +293 -0
  71. package/bundled-skills/design-it/synthwave/SKILL.md +257 -0
  72. package/bundled-skills/design-it/tile-design/SKILL.md +297 -0
  73. package/bundled-skills/design-it/typography-first/SKILL.md +247 -0
  74. package/bundled-skills/design-it/vaporwave/SKILL.md +331 -0
  75. package/bundled-skills/design-it/vibrant-maximalism/SKILL.md +291 -0
  76. package/bundled-skills/design-it/widget-based-design/SKILL.md +274 -0
  77. package/bundled-skills/design-it/y2k-design/SKILL.md +268 -0
  78. package/bundled-skills/diagnosing-bugs/SKILL.md +165 -0
  79. package/bundled-skills/diagnosing-bugs/scripts/hitl-loop.template.sh +41 -0
  80. package/bundled-skills/docs/contributors/skill-scoring.md +235 -0
  81. package/bundled-skills/docs/integrations/jetski-cortex.md +3 -3
  82. package/bundled-skills/docs/integrations/jetski-gemini-loader/README.md +1 -1
  83. package/bundled-skills/docs/maintainers/repo-growth-seo.md +3 -3
  84. package/bundled-skills/docs/maintainers/skills-update-guide.md +1 -1
  85. package/bundled-skills/docs/users/bundles.md +145 -1
  86. package/bundled-skills/docs/users/claude-code-skills.md +1 -1
  87. package/bundled-skills/docs/users/gemini-cli-skills.md +1 -1
  88. package/bundled-skills/docs/users/getting-started.md +1 -1
  89. package/bundled-skills/docs/users/kiro-integration.md +1 -1
  90. package/bundled-skills/docs/users/specialized-plugin-roadmap.md +11 -4
  91. package/bundled-skills/docs/users/usage.md +4 -4
  92. package/bundled-skills/docs/users/visual-guide.md +4 -4
  93. package/bundled-skills/domain-modeling/ADR-FORMAT.md +47 -0
  94. package/bundled-skills/domain-modeling/CONTEXT-FORMAT.md +60 -0
  95. package/bundled-skills/domain-modeling/SKILL.md +105 -0
  96. package/bundled-skills/dos-verify-done-claims/SKILL.md +16 -4
  97. package/bundled-skills/ecl-harness-engineer/agents/creator-config.md +1 -1
  98. package/bundled-skills/ecl-harness-engineer/references/environment-config-guide.md +2 -2
  99. package/bundled-skills/ecl-harness-engineer/references/environment-detection-guide.md +4 -4
  100. package/bundled-skills/event-staffing-ordering/SKILL.md +4 -0
  101. package/bundled-skills/grill-me/SKILL.md +36 -0
  102. package/bundled-skills/grill-with-docs/SKILL.md +36 -0
  103. package/bundled-skills/grilling/SKILL.md +39 -0
  104. package/bundled-skills/handoff/SKILL.md +45 -0
  105. package/bundled-skills/image-generator/.env.example +7 -0
  106. package/bundled-skills/image-generator/SKILL.md +509 -0
  107. package/bundled-skills/improve-codebase-architecture/HTML-REPORT.md +123 -0
  108. package/bundled-skills/improve-codebase-architecture/SKILL.md +97 -0
  109. package/bundled-skills/learn/SKILL.md +156 -0
  110. package/bundled-skills/lesson-generator/SKILL.md +90 -0
  111. package/bundled-skills/llm-council/.env.example +7 -0
  112. package/bundled-skills/llm-council/SKILL.md +602 -0
  113. package/bundled-skills/loop-library/SKILL.md +208 -0
  114. package/bundled-skills/loop-library/agents/openai.yaml +4 -0
  115. package/bundled-skills/loop-library/references/catalog.md +270 -0
  116. package/bundled-skills/lovable-cleanup/SKILL.md +9 -7
  117. package/bundled-skills/macos-screen-recorder/SKILL.md +9 -1
  118. package/bundled-skills/mailtrap-managing-contacts/SKILL.md +112 -0
  119. package/bundled-skills/mailtrap-sending-emails/SKILL.md +167 -0
  120. package/bundled-skills/mailtrap-setting-up-sending-domain/SKILL.md +77 -0
  121. package/bundled-skills/mailtrap-testing-with-sandbox/SKILL.md +110 -0
  122. package/bundled-skills/prototype/LOGIC.md +79 -0
  123. package/bundled-skills/prototype/SKILL.md +62 -0
  124. package/bundled-skills/prototype/UI.md +112 -0
  125. package/bundled-skills/screenstudio-alt/SKILL.md +9 -1
  126. package/bundled-skills/setup-matt-pocock-skills/SKILL.md +158 -0
  127. package/bundled-skills/setup-matt-pocock-skills/domain.md +51 -0
  128. package/bundled-skills/setup-matt-pocock-skills/issue-tracker-github.md +34 -0
  129. package/bundled-skills/setup-matt-pocock-skills/issue-tracker-gitlab.md +35 -0
  130. package/bundled-skills/setup-matt-pocock-skills/issue-tracker-local.md +19 -0
  131. package/bundled-skills/setup-matt-pocock-skills/triage-labels.md +15 -0
  132. package/bundled-skills/survey-generator/LICENSE +21 -0
  133. package/bundled-skills/survey-generator/SKILL.md +143 -0
  134. package/bundled-skills/survey-generator/build_artifact.py +208 -0
  135. package/bundled-skills/survey-generator/examples/agentic-engineering/research_bundle.json +1196 -0
  136. package/bundled-skills/survey-generator/examples/agentic-engineering/survey.html +706 -0
  137. package/bundled-skills/survey-generator/style_spec.json +85 -0
  138. package/bundled-skills/survey-generator/templates/research_bundle_template.json +69 -0
  139. package/bundled-skills/tdd/SKILL.md +139 -0
  140. package/bundled-skills/tdd/mocking.md +59 -0
  141. package/bundled-skills/tdd/refactoring.md +10 -0
  142. package/bundled-skills/tdd/tests.md +61 -0
  143. package/bundled-skills/teach/GLOSSARY-FORMAT.md +35 -0
  144. package/bundled-skills/teach/LEARNING-RECORD-FORMAT.md +46 -0
  145. package/bundled-skills/teach/MISSION-FORMAT.md +31 -0
  146. package/bundled-skills/teach/RESOURCES-FORMAT.md +32 -0
  147. package/bundled-skills/teach/SKILL.md +169 -0
  148. package/bundled-skills/to-issues/SKILL.md +115 -0
  149. package/bundled-skills/to-prd/SKILL.md +104 -0
  150. package/bundled-skills/tools-page-seo-optimizer/SKILL.md +616 -0
  151. package/bundled-skills/triage/AGENT-BRIEF.md +207 -0
  152. package/bundled-skills/triage/OUT-OF-SCOPE.md +105 -0
  153. package/bundled-skills/triage/SKILL.md +143 -0
  154. package/bundled-skills/vibecode-production-qa-validator/SKILL.md +371 -141
  155. package/bundled-skills/wiki-builder/SKILL.md +157 -0
  156. package/bundled-skills/wiki-builder/agents/openai.yaml +5 -0
  157. package/bundled-skills/wiki-builder/references/wiki-flavors.md +98 -0
  158. package/bundled-skills/wiki-builder/scripts/init_wiki.sh +105 -0
  159. package/bundled-skills/wiki-builder/templates/index.md +20 -0
  160. package/bundled-skills/wiki-builder/templates/maintenance-log.md +7 -0
  161. package/bundled-skills/wiki-builder/templates/prompts/compile-concept-page.md +12 -0
  162. package/bundled-skills/wiki-builder/templates/prompts/compile-index.md +11 -0
  163. package/bundled-skills/wiki-builder/templates/prompts/compile-source-page.md +12 -0
  164. package/bundled-skills/wiki-builder/templates/prompts/lint-wiki.md +10 -0
  165. package/bundled-skills/wiki-builder/templates/prompts/query-and-file.md +11 -0
  166. package/bundled-skills/wiki-builder/templates/sources.md +9 -0
  167. package/bundled-skills/wiki-builder/templates/wiki.config.md +53 -0
  168. package/bundled-skills/writing-great-skills/GLOSSARY.md +181 -0
  169. package/bundled-skills/writing-great-skills/SKILL.md +111 -0
  170. package/bundled-skills/yao-meta-skill/SKILL.md +86 -0
  171. package/bundled-skills/yao-meta-skill/agents/interface.yaml +26 -0
  172. package/bundled-skills/yao-meta-skill/manifest.json +24 -0
  173. package/bundled-skills/yao-meta-skill/references/artifact-design-doctrine.md +49 -0
  174. package/bundled-skills/yao-meta-skill/references/authoring-discipline.md +78 -0
  175. package/bundled-skills/yao-meta-skill/references/autonomous-adaptation.md +65 -0
  176. package/bundled-skills/yao-meta-skill/references/distribution-registry-method.md +60 -0
  177. package/bundled-skills/yao-meta-skill/references/eval-playbook.md +69 -0
  178. package/bundled-skills/yao-meta-skill/references/gate-selection.md +68 -0
  179. package/bundled-skills/yao-meta-skill/references/governance.md +134 -0
  180. package/bundled-skills/yao-meta-skill/references/human-review-template.md +54 -0
  181. package/bundled-skills/yao-meta-skill/references/intent-dialogue.md +138 -0
  182. package/bundled-skills/yao-meta-skill/references/iteration-philosophy.md +30 -0
  183. package/bundled-skills/yao-meta-skill/references/non-skill-decision-tree.md +39 -0
  184. package/bundled-skills/yao-meta-skill/references/operating-modes.md +107 -0
  185. package/bundled-skills/yao-meta-skill/references/output-eval-method.md +113 -0
  186. package/bundled-skills/yao-meta-skill/references/output-quality-risk.md +41 -0
  187. package/bundled-skills/yao-meta-skill/references/output-visual-quality.md +53 -0
  188. package/bundled-skills/yao-meta-skill/references/packaging-contracts.md +70 -0
  189. package/bundled-skills/yao-meta-skill/references/pattern-extraction-doctrine.md +76 -0
  190. package/bundled-skills/yao-meta-skill/references/platform-capability-matrix.md +49 -0
  191. package/bundled-skills/yao-meta-skill/references/prompt-engineering-doctrine.md +76 -0
  192. package/bundled-skills/yao-meta-skill/references/qa-ladder.md +57 -0
  193. package/bundled-skills/yao-meta-skill/references/reference-scan.md +126 -0
  194. package/bundled-skills/yao-meta-skill/references/regression-cause-taxonomy.md +80 -0
  195. package/bundled-skills/yao-meta-skill/references/resource-boundaries.md +120 -0
  196. package/bundled-skills/yao-meta-skill/references/review-studio-method.md +87 -0
  197. package/bundled-skills/yao-meta-skill/references/review-waiver-method.md +76 -0
  198. package/bundled-skills/yao-meta-skill/references/runtime-conformance-method.md +21 -0
  199. package/bundled-skills/yao-meta-skill/references/skill-archetypes.md +86 -0
  200. package/bundled-skills/yao-meta-skill/references/skill-atlas-method.md +35 -0
  201. package/bundled-skills/yao-meta-skill/references/skill-engineering-method.md +210 -0
  202. package/bundled-skills/yao-meta-skill/references/skill-ir-method.md +41 -0
  203. package/bundled-skills/yao-meta-skill/references/skillops-decision-policy.md +53 -0
  204. package/bundled-skills/yao-meta-skill/references/systems-thinking-doctrine.md +75 -0
  205. package/bundled-skills/yao-meta-skill/references/telemetry-drift-method.md +182 -0
  206. package/bundled-skills/yao-meta-skill/references/trust-security-method.md +79 -0
  207. package/bundled-skills/yao-meta-skill/references/user-memory-policy.md +35 -0
  208. package/bundled-skills/youtube-notetaker/SKILL.md +209 -0
  209. package/bundled-skills/youtube-notetaker/reference/artifact.html +269 -0
  210. package/bundled-skills/youtube-notetaker/scripts/contact_sheet.py +53 -0
  211. package/bundled-skills/youtube-notetaker/scripts/detect_slides.sh +19 -0
  212. package/bundled-skills/youtube-notetaker/scripts/download.sh +24 -0
  213. package/bundled-skills/youtube-notetaker/scripts/extract_slides.py +43 -0
  214. package/bundled-skills/youtube-notetaker/scripts/serve.py +222 -0
  215. package/bundled-skills/youtube-notetaker/scripts/setup.sh +27 -0
  216. package/bundled-skills/youtube-notetaker/scripts/verify.sh +31 -0
  217. package/bundled-skills/youtube-notetaker/scripts/vtt_to_transcript.py +59 -0
  218. package/bundled-skills/youtube-notetaker/scripts/write_library_item.py +69 -0
  219. package/package.json +1 -1
  220. package/skills_index.json +2013 -330
  221. package/bundled-skills/ai-md/SKILL.md +0 -523
  222. package/bundled-skills/atlas-contract/SKILL.md +0 -650
  223. package/bundled-skills/busybox-on-windows/SKILL.md +0 -40
  224. package/bundled-skills/monte-carlo-prevent/SKILL.md +0 -257
  225. package/bundled-skills/monte-carlo-prevent/references/TROUBLESHOOTING.md +0 -23
  226. package/bundled-skills/monte-carlo-prevent/references/parameters.md +0 -32
  227. package/bundled-skills/monte-carlo-prevent/references/workflows.md +0 -478
  228. package/bundled-skills/monte-carlo-push-ingestion/SKILL.md +0 -372
  229. package/bundled-skills/monte-carlo-push-ingestion/references/anomaly-detection.md +0 -87
  230. package/bundled-skills/monte-carlo-push-ingestion/references/custom-lineage.md +0 -203
  231. package/bundled-skills/monte-carlo-push-ingestion/references/direct-http-api.md +0 -207
  232. package/bundled-skills/monte-carlo-push-ingestion/references/prerequisites.md +0 -150
  233. package/bundled-skills/monte-carlo-push-ingestion/references/push-lineage.md +0 -160
  234. package/bundled-skills/monte-carlo-push-ingestion/references/push-metadata.md +0 -158
  235. package/bundled-skills/monte-carlo-push-ingestion/references/push-query-logs.md +0 -219
  236. package/bundled-skills/monte-carlo-push-ingestion/references/validation.md +0 -257
  237. package/bundled-skills/monte-carlo-push-ingestion/scripts/sample_verify.py +0 -357
  238. package/bundled-skills/monte-carlo-push-ingestion/scripts/templates/bigquery/collect_and_push_lineage.py +0 -70
  239. package/bundled-skills/monte-carlo-push-ingestion/scripts/templates/bigquery/collect_and_push_metadata.py +0 -65
  240. package/bundled-skills/monte-carlo-push-ingestion/scripts/templates/bigquery/collect_and_push_query_logs.py +0 -70
  241. package/bundled-skills/monte-carlo-push-ingestion/scripts/templates/bigquery/collect_lineage.py +0 -214
  242. package/bundled-skills/monte-carlo-push-ingestion/scripts/templates/bigquery/collect_metadata.py +0 -160
  243. package/bundled-skills/monte-carlo-push-ingestion/scripts/templates/bigquery/collect_query_logs.py +0 -164
  244. package/bundled-skills/monte-carlo-push-ingestion/scripts/templates/bigquery/push_lineage.py +0 -198
  245. package/bundled-skills/monte-carlo-push-ingestion/scripts/templates/bigquery/push_metadata.py +0 -193
  246. package/bundled-skills/monte-carlo-push-ingestion/scripts/templates/bigquery/push_query_logs.py +0 -207
  247. package/bundled-skills/monte-carlo-push-ingestion/scripts/templates/bigquery-iceberg/collect_and_push_metadata.py +0 -71
  248. package/bundled-skills/monte-carlo-push-ingestion/scripts/templates/bigquery-iceberg/collect_and_push_query_logs.py +0 -64
  249. package/bundled-skills/monte-carlo-push-ingestion/scripts/templates/bigquery-iceberg/collect_metadata.py +0 -253
  250. package/bundled-skills/monte-carlo-push-ingestion/scripts/templates/bigquery-iceberg/collect_query_logs.py +0 -149
  251. package/bundled-skills/monte-carlo-push-ingestion/scripts/templates/bigquery-iceberg/push_metadata.py +0 -190
  252. package/bundled-skills/monte-carlo-push-ingestion/scripts/templates/bigquery-iceberg/push_query_logs.py +0 -208
  253. package/bundled-skills/monte-carlo-push-ingestion/scripts/templates/databricks/collect_and_push_lineage.py +0 -83
  254. package/bundled-skills/monte-carlo-push-ingestion/scripts/templates/databricks/collect_and_push_metadata.py +0 -77
  255. package/bundled-skills/monte-carlo-push-ingestion/scripts/templates/databricks/collect_and_push_query_logs.py +0 -83
  256. package/bundled-skills/monte-carlo-push-ingestion/scripts/templates/databricks/collect_lineage.py +0 -240
  257. package/bundled-skills/monte-carlo-push-ingestion/scripts/templates/databricks/collect_metadata.py +0 -212
  258. package/bundled-skills/monte-carlo-push-ingestion/scripts/templates/databricks/collect_query_logs.py +0 -204
  259. package/bundled-skills/monte-carlo-push-ingestion/scripts/templates/databricks/push_lineage.py +0 -192
  260. package/bundled-skills/monte-carlo-push-ingestion/scripts/templates/databricks/push_metadata.py +0 -178
  261. package/bundled-skills/monte-carlo-push-ingestion/scripts/templates/databricks/push_query_logs.py +0 -200
  262. package/bundled-skills/monte-carlo-push-ingestion/scripts/templates/hive/collect_and_push_lineage.py +0 -119
  263. package/bundled-skills/monte-carlo-push-ingestion/scripts/templates/hive/collect_and_push_metadata.py +0 -119
  264. package/bundled-skills/monte-carlo-push-ingestion/scripts/templates/hive/collect_and_push_query_logs.py +0 -117
  265. package/bundled-skills/monte-carlo-push-ingestion/scripts/templates/hive/collect_lineage.py +0 -265
  266. package/bundled-skills/monte-carlo-push-ingestion/scripts/templates/hive/collect_metadata.py +0 -313
  267. package/bundled-skills/monte-carlo-push-ingestion/scripts/templates/hive/collect_query_logs.py +0 -284
  268. package/bundled-skills/monte-carlo-push-ingestion/scripts/templates/hive/push_lineage.py +0 -309
  269. package/bundled-skills/monte-carlo-push-ingestion/scripts/templates/hive/push_metadata.py +0 -245
  270. package/bundled-skills/monte-carlo-push-ingestion/scripts/templates/hive/push_query_logs.py +0 -255
  271. package/bundled-skills/monte-carlo-push-ingestion/scripts/templates/redshift/collect_and_push_lineage.py +0 -78
  272. package/bundled-skills/monte-carlo-push-ingestion/scripts/templates/redshift/collect_and_push_metadata.py +0 -80
  273. package/bundled-skills/monte-carlo-push-ingestion/scripts/templates/redshift/collect_and_push_query_logs.py +0 -88
  274. package/bundled-skills/monte-carlo-push-ingestion/scripts/templates/redshift/collect_lineage.py +0 -235
  275. package/bundled-skills/monte-carlo-push-ingestion/scripts/templates/redshift/collect_metadata.py +0 -219
  276. package/bundled-skills/monte-carlo-push-ingestion/scripts/templates/redshift/collect_query_logs.py +0 -239
  277. package/bundled-skills/monte-carlo-push-ingestion/scripts/templates/redshift/push_lineage.py +0 -178
  278. package/bundled-skills/monte-carlo-push-ingestion/scripts/templates/redshift/push_metadata.py +0 -178
  279. package/bundled-skills/monte-carlo-push-ingestion/scripts/templates/redshift/push_query_logs.py +0 -196
  280. package/bundled-skills/monte-carlo-push-ingestion/scripts/templates/snowflake/collect_and_push_lineage.py +0 -154
  281. package/bundled-skills/monte-carlo-push-ingestion/scripts/templates/snowflake/collect_and_push_metadata.py +0 -137
  282. package/bundled-skills/monte-carlo-push-ingestion/scripts/templates/snowflake/collect_and_push_query_logs.py +0 -137
  283. package/bundled-skills/monte-carlo-push-ingestion/scripts/templates/snowflake/collect_lineage.py +0 -349
  284. package/bundled-skills/monte-carlo-push-ingestion/scripts/templates/snowflake/collect_metadata.py +0 -329
  285. package/bundled-skills/monte-carlo-push-ingestion/scripts/templates/snowflake/collect_query_logs.py +0 -254
  286. package/bundled-skills/monte-carlo-push-ingestion/scripts/templates/snowflake/push_lineage.py +0 -307
  287. package/bundled-skills/monte-carlo-push-ingestion/scripts/templates/snowflake/push_metadata.py +0 -228
  288. package/bundled-skills/monte-carlo-push-ingestion/scripts/templates/snowflake/push_query_logs.py +0 -248
  289. package/bundled-skills/monte-carlo-push-ingestion/scripts/test_template_sdk_usage.py +0 -340
  290. package/bundled-skills/skill-optimizer/SKILL.md +0 -271
  291. package/bundled-skills/using-superpowers/SKILL.md +0 -98
@@ -0,0 +1,140 @@
1
+ #!/usr/bin/env node
2
+
3
+ // Parses "X vs Y" patterns from `browse cloud search` result titles across discovery batch files.
4
+ // Produces a ranked list of candidate competitor names, with an example title each,
5
+ // and attempts to resolve each name to a domain from the result URL pool.
6
+ //
7
+ // Usage: node extract_vs_names.mjs <directory> [--prefix competitor] [--seed "Exa,Tavily,SerpAPI"]
8
+ //
9
+ // Output: newline-delimited JSON to stdout, one object per candidate:
10
+ // { "name": "serper", "hits": 3, "domain": "serper.dev", "example": "Tavily vs Serper..." }
11
+
12
+ import { readdirSync, readFileSync } from 'fs';
13
+ import { join } from 'path';
14
+
15
+ const args = process.argv.slice(2);
16
+
17
+ if (args.includes('--help') || args.includes('-h') || args.length === 0) {
18
+ console.error(`Usage: node extract_vs_names.mjs <directory> [--prefix <prefix>] [--seed "<csv>"]
19
+
20
+ Reads all <prefix>_discovery_batch_*.json files, parses "X vs Y" patterns from result
21
+ titles, and outputs a ranked list of candidate competitor names as newline-delimited JSON.
22
+
23
+ Options:
24
+ --prefix <prefix> Batch file prefix (default: "competitor")
25
+ --seed "<csv>" Comma-separated list of seed names to exclude from output
26
+ (you already know these; want the OTHER side of the comparison)
27
+ --help, -h Show this help message`);
28
+ process.exit(args.includes('--help') || args.includes('-h') ? 0 : 1);
29
+ }
30
+
31
+ const dir = args[0];
32
+ const prefixIdx = args.indexOf('--prefix');
33
+ const prefix = prefixIdx !== -1 && args[prefixIdx + 1] ? args[prefixIdx + 1] : 'competitor';
34
+ const seedIdx = args.indexOf('--seed');
35
+ const seeds = seedIdx !== -1 && args[seedIdx + 1]
36
+ ? args[seedIdx + 1].split(',').map(s => s.trim().toLowerCase()).filter(Boolean)
37
+ : [];
38
+ const seedSet = new Set(seeds);
39
+
40
+ // Escape regex metacharacters in the user-supplied prefix so a value like
41
+ // "comp.+" matches the literal filename, not as a regex pattern.
42
+ const escapedPrefix = prefix.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
43
+ const pattern = new RegExp(`^${escapedPrefix}_discovery_batch_.*\\.json$`);
44
+
45
+ let files;
46
+ try {
47
+ files = readdirSync(dir).filter(f => pattern.test(f)).sort();
48
+ } catch (err) {
49
+ console.error(`Error reading directory ${dir}: ${err.message}`);
50
+ process.exit(1);
51
+ }
52
+
53
+ if (files.length === 0) {
54
+ console.error(`No ${prefix}_discovery_batch_*.json files found in ${dir}`);
55
+ process.exit(1);
56
+ }
57
+
58
+ const allResults = [];
59
+ for (const f of files) {
60
+ try {
61
+ const d = JSON.parse(readFileSync(join(dir, f), 'utf-8'));
62
+ const rs = Array.isArray(d) ? d : d.results || [];
63
+ allResults.push(...rs);
64
+ } catch {}
65
+ }
66
+
67
+ // Build a lookup of hostname -> candidate root domain from all result URLs.
68
+ // Used later to try to resolve "serper" -> "serper.dev".
69
+ // Exclude any host whose root-base equals a seed name — otherwise a short extracted token
70
+ // like "exa" can match the user's own domain (exa.ai).
71
+ const hostMap = new Map();
72
+ for (const r of allResults) {
73
+ if (!r.url) continue;
74
+ try {
75
+ const h = new URL(r.url).hostname.replace(/^www\./, '');
76
+ const root = h.split('.').slice(-2).join('.');
77
+ const rootBase = root.split('.')[0];
78
+ if (seedSet.has(rootBase)) continue;
79
+ if (!hostMap.has(root)) hostMap.set(root, h);
80
+ } catch {}
81
+ }
82
+
83
+ // Extract names from "X vs Y" patterns.
84
+ const counts = new Map();
85
+ for (const r of allResults) {
86
+ const title = (r.title || '').toLowerCase();
87
+ const ms = [...title.matchAll(/\b([a-z][\w.\-]{2,})\s+(?:vs\.?|versus)\s+([a-z][\w.\-]{2,})/g)];
88
+ for (const m of ms) {
89
+ for (const raw of [m[1], m[2]]) {
90
+ const name = raw.replace(/[^a-z0-9.\-]/g, '').trim();
91
+ if (!name || name.length < 3) continue;
92
+ if (seedSet.has(name)) continue;
93
+ // Reject obvious non-product tokens
94
+ if (['the', 'and', 'for', 'with', 'best', 'top', 'better', 'using', 'choosing'].includes(name)) continue;
95
+ if (!counts.has(name)) counts.set(name, { name, hits: 0, example: r.title });
96
+ counts.get(name).hits += 1;
97
+ }
98
+ }
99
+ }
100
+
101
+ // Try to resolve each name to a domain.
102
+ // Strategy:
103
+ // 1. Exact match on rootBase wins outright.
104
+ // 2. Otherwise allow rootBase.startsWith(needle) ONLY when the suffix is a known
105
+ // branding token (e.g. "serp" → "serpapi.com"). Bidirectional startsWith
106
+ // was too loose: "serp" matched serpstack.com, "exa" matched example.com.
107
+ // 3. Among multiple suffix matches, prefer the shortest suffix (most specific —
108
+ // "serp" should match "serpapi" before "serpapilabs"). Deterministic.
109
+ const BRAND_SUFFIXES = ['api','search','app','ai','io','hq','co','dev','tech','cloud','agent','agents','labs','lab'];
110
+
111
+ function resolveDomain(name) {
112
+ const needle = name.replace(/\./g, '');
113
+ let exact = null;
114
+ let bestSuffix = null; // { host, suffixLen }
115
+ for (const [root, host] of hostMap.entries()) {
116
+ const rootBase = root.split('.')[0];
117
+ if (rootBase === needle) { exact = host; break; }
118
+ if (rootBase.length > needle.length && rootBase.startsWith(needle)) {
119
+ const suffix = rootBase.slice(needle.length).replace(/^[\-_]/, '');
120
+ if (BRAND_SUFFIXES.includes(suffix)) {
121
+ if (!bestSuffix || suffix.length < bestSuffix.suffixLen) {
122
+ bestSuffix = { host, suffixLen: suffix.length };
123
+ }
124
+ }
125
+ }
126
+ }
127
+ if (exact) return exact;
128
+ if (bestSuffix) return bestSuffix.host;
129
+ return null;
130
+ }
131
+
132
+ const ranked = [...counts.values()]
133
+ .map(c => ({ ...c, domain: resolveDomain(c.name) }))
134
+ .sort((a, b) => b.hits - a.hits);
135
+
136
+ for (const c of ranked) {
137
+ console.log(JSON.stringify(c));
138
+ }
139
+
140
+ console.error(`Extracted ${ranked.length} candidate names from ${files.length} batch files`);
@@ -0,0 +1,224 @@
1
+ #!/usr/bin/env node
2
+
3
+ // Category-fit gate. For each candidate URL, fetch the homepage hero via `browse cloud fetch`,
4
+ // extract visible text, and decide whether the candidate is in the same category as
5
+ // the user's company based on include/exclude keyword rules.
6
+ //
7
+ // Usage:
8
+ // cat urls.txt | node gate_candidates.mjs \
9
+ // --include "web search api,neural search,retrieval api,semantic search,search for agents" \
10
+ // --exclude "vector database,observability,analytics,enterprise search appliance,site search widget" \
11
+ // --concurrency 6
12
+ //
13
+ // Output: newline-delimited JSON to stdout with one object per URL:
14
+ // { "url": "https://foo.com", "status": "PASS" | "REJECT" | "UNKNOWN",
15
+ // "matched_includes": [...], "matched_excludes": [...], "title": "...", "hero": "..." }
16
+
17
+ import { execFile } from 'child_process';
18
+ import { promisify } from 'util';
19
+ import { readFileSync } from 'fs';
20
+
21
+ // Async execFile so the worker pool actually parallelizes. spawnSync blocks the entire
22
+ // event loop, which silently turns --concurrency N into N=1 — every URL fetched serially
23
+ // regardless of the flag. With promisified execFile, N workers can wait on N pending
24
+ // `browse cloud fetch` processes concurrently.
25
+ const execFileAsync = promisify(execFile);
26
+
27
+ const args = process.argv.slice(2);
28
+
29
+ if (args.includes('--help') || args.includes('-h')) {
30
+ console.error(`Usage: cat urls.txt | node gate_candidates.mjs [options]
31
+
32
+ Reads URLs from stdin (one per line) OR from --input <file>. For each URL, fetches
33
+ the homepage via \`browse cloud fetch --allow-redirects\`, extracts the first N chars of visible
34
+ text (the hero / tagline area), and classifies against include/exclude keyword rules.
35
+
36
+ Options:
37
+ --include "<csv>" Required. Comma-separated keywords; candidate PASSES if any match.
38
+ --exclude "<csv>" Comma-separated keywords; candidate REJECTS if any match.
39
+ --input <file> Read URLs from file instead of stdin.
40
+ --concurrency <n> Max parallel fetches (default: 6).
41
+ --hero-chars <n> Chars of visible text to examine (default: 800).
42
+ --help, -h Show this help message.`);
43
+ process.exit(args.includes('--help') || args.includes('-h') ? 0 : 1);
44
+ }
45
+
46
+ function flag(name) {
47
+ const i = args.indexOf(name);
48
+ return i !== -1 ? args[i + 1] : null;
49
+ }
50
+
51
+ const includes = (flag('--include') || '').split(',').map(s => s.trim().toLowerCase()).filter(Boolean);
52
+ const excludes = (flag('--exclude') || '').split(',').map(s => s.trim().toLowerCase()).filter(Boolean);
53
+ // Floor at 1: `--concurrency 0` or a non-numeric value makes parseInt yield 0/NaN, which would
54
+ // spawn zero workers — the script would exit "successfully" having gated nothing, making
55
+ // discovery look empty with no error. Always run at least one worker.
56
+ const concurrency = Math.max(1, parseInt(flag('--concurrency') || '6', 10) || 0);
57
+ const heroChars = parseInt(flag('--hero-chars') || '800', 10);
58
+ const inputFile = flag('--input');
59
+
60
+ function stripHtml(html) {
61
+ const withoutActiveContent = removeElementContent(removeElementContent(html, 'script'), 'style');
62
+ return withoutActiveContent
63
+ .replace(/<[^>]*>/g, ' ')
64
+ .replace(/&lt;/g, '<')
65
+ .replace(/&gt;/g, '>')
66
+ .replace(/&quot;/g, '"')
67
+ .replace(/&#x27;/g, "'")
68
+ .replace(/&nbsp;/g, ' ')
69
+ .replace(/&amp;/g, '&')
70
+ .replace(/\s+/g, ' ')
71
+ .trim();
72
+ }
73
+
74
+ function removeElementContent(html, tagName) {
75
+ let out = '';
76
+ let cursor = 0;
77
+ const lower = html.toLowerCase();
78
+ const openNeedle = `<${tagName}`;
79
+ const closeNeedle = `</${tagName}`;
80
+ while (cursor < html.length) {
81
+ const start = lower.indexOf(openNeedle, cursor);
82
+ if (start === -1) {
83
+ out += html.slice(cursor);
84
+ break;
85
+ }
86
+ out += html.slice(cursor, start);
87
+ const close = lower.indexOf(closeNeedle, start + openNeedle.length);
88
+ if (close === -1) {
89
+ cursor = html.length;
90
+ break;
91
+ }
92
+ const closeEnd = html.indexOf('>', close + closeNeedle.length);
93
+ cursor = closeEnd === -1 ? html.length : closeEnd + 1;
94
+ out += ' ';
95
+ }
96
+ return out;
97
+ }
98
+
99
+ if (args.includes('--self-test')) {
100
+ console.assert(stripHtml('<p>A&amp;lt;B</p>') === 'A&lt;B');
101
+ console.assert(stripHtml('<script>alert(1)</script ignored><p>ok</p>') === 'ok');
102
+ process.exit(0);
103
+ }
104
+
105
+ if (includes.length === 0) {
106
+ console.error('Error: --include is required');
107
+ process.exit(1);
108
+ }
109
+
110
+ let urls;
111
+ if (inputFile) {
112
+ urls = readFileSync(inputFile, 'utf-8').split('\n').map(l => l.trim()).filter(Boolean);
113
+ } else {
114
+ const stdin = readFileSync(0, 'utf-8');
115
+ urls = stdin.split('\n').map(l => l.trim()).filter(Boolean);
116
+ }
117
+
118
+ if (urls.length === 0) {
119
+ console.error('Error: no URLs provided (pipe via stdin or use --input)');
120
+ process.exit(1);
121
+ }
122
+
123
+ // Position-aware classification:
124
+ // 1. Exclude term in <title> → REJECT (their primary identity is the excluded category)
125
+ // 2. Include term in <title> → PASS (their primary identity matches)
126
+ // 3. Include in early hero (200ch) → PASS iff no exclude in early hero
127
+ // 4. Otherwise → REJECT (default conservative)
128
+ // Rationale: <title> is the single strongest signal of what a company sells.
129
+ // Mid/late hero mentions (e.g. "we also support web scraping use cases") shouldn't
130
+ // disqualify a real competitor that self-identifies in its title as a cloud browser.
131
+ function classify(title, heroFull, includes, excludes) {
132
+ const titleLower = (title || '').toLowerCase();
133
+ const heroLower = heroFull.toLowerCase();
134
+ const heroEarly = heroLower.slice(0, 200);
135
+
136
+ const incTitle = includes.filter(k => titleLower.includes(k));
137
+ const excTitle = excludes.filter(k => titleLower.includes(k));
138
+ const incEarly = includes.filter(k => heroEarly.includes(k));
139
+ const excEarly = excludes.filter(k => heroEarly.includes(k));
140
+ const incHero = includes.filter(k => heroLower.includes(k));
141
+ const excHero = excludes.filter(k => heroLower.includes(k));
142
+
143
+ let status, reason;
144
+ if (incTitle.length > 0 && excTitle.length > 0) {
145
+ // Hybrid-identity title (e.g. "Browser Automation & Web Scraping API").
146
+ // Break the tie by the early hero — whichever category has more mentions wins.
147
+ if (incEarly.length > excEarly.length) { status = 'PASS'; reason = `title-hybrid→hero200 leans include(${incEarly[0] || incTitle[0]})`; }
148
+ else if (excEarly.length > incEarly.length) { status = 'REJECT'; reason = `title-hybrid→hero200 leans exclude(${excEarly[0] || excTitle[0]})`; }
149
+ else { status = 'PASS'; reason = `title-hybrid→tie, defaulting include(${incTitle[0]})`; }
150
+ }
151
+ else if (excTitle.length > 0) { status = 'REJECT'; reason = `title→exclude(${excTitle[0]})`; }
152
+ else if (incTitle.length > 0) { status = 'PASS'; reason = `title→include(${incTitle[0]})`; }
153
+ else if (incEarly.length > 0 && excEarly.length === 0) { status = 'PASS'; reason = `hero200→include(${incEarly[0]})`; }
154
+ else if (excEarly.length > 0) { status = 'REJECT'; reason = `hero200→exclude(${excEarly[0]})`; }
155
+ else if (incHero.length > 0 && excHero.length === 0) { status = 'PASS'; reason = `hero→include(${incHero[0]})`; }
156
+ // Late-hero conflict: both include AND exclude appear in chars 200–800 (nothing in
157
+ // title or early hero). This is genuine ambiguous signal, not absence — return UNKNOWN
158
+ // so the candidate surfaces in the user-confirmation bucket at Step 4.5 instead of
159
+ // being silently dropped as REJECT.
160
+ else if (incHero.length > 0 && excHero.length > 0) { status = 'UNKNOWN'; reason = `hero→conflict(include:${incHero[0]}, exclude:${excHero[0]})`; }
161
+ else { status = 'REJECT'; reason = 'no category signal'; }
162
+
163
+ return {
164
+ status, reason,
165
+ matched_includes: [...new Set([...incTitle, ...incEarly, ...incHero])],
166
+ matched_excludes: [...new Set([...excTitle, ...excEarly, ...excHero])],
167
+ };
168
+ }
169
+
170
+ async function gateOne(url) {
171
+ let stdout;
172
+ try {
173
+ // --format raw returns the JSON envelope with raw HTML in `.content` (the default
174
+ // is markdown, which has no <title> tag for the position-aware classifier to read).
175
+ const r = await execFileAsync('browse', ['cloud', 'fetch', '--allow-redirects', '--format', 'raw', url], {
176
+ maxBuffer: 4 * 1024 * 1024,
177
+ timeout: 20000,
178
+ });
179
+ stdout = r.stdout;
180
+ } catch (err) {
181
+ // Non-zero exit, timeout, or spawn failure all surface here.
182
+ return { url, status: 'UNKNOWN', reason: `browse cloud fetch failed: ${err.message}`, matched_includes: [], matched_excludes: [], title: '', hero: '' };
183
+ }
184
+ let resp;
185
+ try { resp = JSON.parse(stdout); } catch {
186
+ return { url, status: 'UNKNOWN', reason: 'non-JSON response', matched_includes: [], matched_excludes: [], title: '', hero: '' };
187
+ }
188
+ const html = resp.content || '';
189
+ const titleM = html.match(/<title[^>]*>([^<]*)<\/title>/i);
190
+ const title = titleM ? titleM[1].trim() : '';
191
+ const heroFull = stripHtml(html).slice(0, heroChars);
192
+ const c = classify(title, heroFull, includes, excludes);
193
+ return {
194
+ url,
195
+ status: c.status,
196
+ reason: c.reason,
197
+ matched_includes: c.matched_includes,
198
+ matched_excludes: c.matched_excludes,
199
+ title,
200
+ hero: heroFull.slice(0, 240),
201
+ };
202
+ }
203
+
204
+ // Run with bounded concurrency
205
+ const results = [];
206
+ async function runAll() {
207
+ const queue = [...urls];
208
+ const workers = Array(Math.min(concurrency, queue.length)).fill(0).map(async () => {
209
+ while (queue.length > 0) {
210
+ const u = queue.shift();
211
+ const r = await gateOne(u);
212
+ results.push(r);
213
+ console.log(JSON.stringify(r));
214
+ }
215
+ });
216
+ await Promise.all(workers);
217
+ }
218
+
219
+ await runAll();
220
+
221
+ const pass = results.filter(r => r.status === 'PASS').length;
222
+ const reject = results.filter(r => r.status === 'REJECT').length;
223
+ const unknown = results.filter(r => r.status === 'UNKNOWN').length;
224
+ console.error(`\nGate: ${pass} PASS / ${reject} REJECT / ${unknown} UNKNOWN (of ${results.length})`);
@@ -0,0 +1,90 @@
1
+ #!/usr/bin/env node
2
+
3
+ // Deduplicates discovery URLs from `browse cloud search` JSON output files.
4
+ // Usage: node list_urls.mjs /tmp [--prefix competitor]
5
+ // Reads all {prefix}_discovery_batch_*.json files, deduplicates by domain,
6
+ // outputs one URL per line to stdout, stats to stderr.
7
+
8
+ import { readdirSync, readFileSync } from 'fs';
9
+ import { join } from 'path';
10
+
11
+ const args = process.argv.slice(2);
12
+
13
+ if (args.includes('--help') || args.includes('-h') || args.length === 0) {
14
+ console.error(`Usage: node list_urls.mjs <directory> [--prefix <prefix>]
15
+
16
+ Reads all <prefix>_discovery_batch_*.json files from <directory>,
17
+ deduplicates URLs by domain, and outputs one URL per line to stdout.
18
+
19
+ Options:
20
+ --prefix <prefix> Batch file prefix (default: "competitor")
21
+ --help, -h Show this help message
22
+
23
+ Examples:
24
+ node list_urls.mjs /tmp
25
+ node list_urls.mjs /tmp --prefix competitor`);
26
+ process.exit(args.includes('--help') || args.includes('-h') ? 0 : 1);
27
+ }
28
+
29
+ const dir = args[0];
30
+ const prefixIdx = args.indexOf('--prefix');
31
+ const prefix = prefixIdx !== -1 && args[prefixIdx + 1] ? args[prefixIdx + 1] : 'competitor';
32
+
33
+ // Escape regex metacharacters in the user-supplied prefix so a value like
34
+ // "comp.+" matches the literal filename, not as a regex pattern.
35
+ const escapedPrefix = prefix.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
36
+ const pattern = new RegExp(`^${escapedPrefix}_discovery_batch_.*\\.json$`);
37
+
38
+ let files;
39
+ try {
40
+ files = readdirSync(dir)
41
+ .filter(f => pattern.test(f))
42
+ .sort();
43
+ } catch (err) {
44
+ console.error(`Error reading directory ${dir}: ${err.message}`);
45
+ process.exit(1);
46
+ }
47
+
48
+ if (files.length === 0) {
49
+ console.error(`No ${prefix}_discovery_batch_*.json files found in ${dir}`);
50
+ process.exit(1);
51
+ }
52
+
53
+ // Dedup by hostname, but prefer the site root over a deep link. The first search hit for a
54
+ // domain is often a blog/doc/comparison path; gating + enrichment want the homepage, so when
55
+ // multiple URLs share a host we keep the shallowest path (fewest segments). First-seen host
56
+ // order is preserved (Map.set on an existing key keeps its position).
57
+ const byDomain = new Map(); // hostname -> { url, depth }
58
+ let totalResults = 0;
59
+
60
+ for (const file of files) {
61
+ try {
62
+ const data = JSON.parse(readFileSync(join(dir, file), 'utf-8'));
63
+ const results = Array.isArray(data) ? data : (data.results || []);
64
+ totalResults += results.length;
65
+
66
+ for (const result of results) {
67
+ const url = result.url;
68
+ if (!url) continue;
69
+
70
+ try {
71
+ const u = new URL(url);
72
+ const hostname = u.hostname.replace(/^www\./, '');
73
+ const depth = u.pathname.replace(/\/+$/, '').split('/').filter(Boolean).length;
74
+ const existing = byDomain.get(hostname);
75
+ if (!existing || depth < existing.depth) byDomain.set(hostname, { url, depth });
76
+ } catch {
77
+ // Skip invalid URLs
78
+ }
79
+ }
80
+ } catch (err) {
81
+ console.error(`Warning: Failed to parse ${file}: ${err.message}`);
82
+ }
83
+ }
84
+
85
+ const urls = [...byDomain.values()].map(v => v.url);
86
+ for (const url of urls) {
87
+ console.log(url);
88
+ }
89
+
90
+ console.error(`\n${files.length} files, ${totalResults} total results, ${urls.length} unique domains`);
@@ -0,0 +1,50 @@
1
+ // Shared markdown parsing helpers for competitor-analysis scripts.
2
+ // Used by compile_report.mjs, merge_partials.mjs, and capture_screenshots.mjs.
3
+
4
+ // Parses YAML-ish frontmatter delimited by `---` lines.
5
+ // Returns an object of fields, or null if no frontmatter delimiter is found.
6
+ export function parseFrontmatter(content) {
7
+ content = content.replace(/\r\n/g, '\n'); // tolerate CRLF — anchors below assume LF
8
+ const m = content.match(/^---\n([\s\S]*?)\n---/);
9
+ if (!m) return null;
10
+ const fields = {};
11
+ for (const line of m[1].split('\n')) {
12
+ const idx = line.indexOf(':');
13
+ if (idx > 0) {
14
+ const k = line.slice(0, idx).trim();
15
+ const v = line.slice(idx + 1).trim().replace(/^["']|["']$/g, '');
16
+ if (k && v) fields[k] = v;
17
+ }
18
+ }
19
+ return fields;
20
+ }
21
+
22
+ // Returns the body text after the closing `---` of the frontmatter, trimmed.
23
+ // If no frontmatter is present, returns the full content trimmed — so callers
24
+ // that don't gate on parseFrontmatter still get usable text.
25
+ export function parseBody(content) {
26
+ content = content.replace(/\r\n/g, '\n'); // tolerate CRLF — anchors below assume LF
27
+ const m = content.match(/^---\n[\s\S]*?\n---\n([\s\S]*)/);
28
+ return m ? m[1].trim() : content.trim();
29
+ }
30
+
31
+ // Splits a markdown body into sections keyed by `## Heading` line.
32
+ // Content before the first `## ` is dropped (matches existing behavior).
33
+ export function parseSections(body) {
34
+ const sections = {};
35
+ const lines = body.replace(/\r\n/g, '\n').split('\n');
36
+ let currentKey = null;
37
+ let buffer = [];
38
+ for (const line of lines) {
39
+ const m = line.match(/^## (.+)$/);
40
+ if (m) {
41
+ if (currentKey !== null) sections[currentKey] = buffer.join('\n').trim();
42
+ currentKey = m[1].trim();
43
+ buffer = [];
44
+ } else if (currentKey !== null) {
45
+ buffer.push(line);
46
+ }
47
+ }
48
+ if (currentKey !== null) sections[currentKey] = buffer.join('\n').trim();
49
+ return sections;
50
+ }