Universal SEO skill for Claude Code. 25 sub-skills + 18 sub-agents covering technical SEO, E-E-A-T, schema, GEO/AEO, backlinks, local SEO, maps intelligence, semantic clustering, e-commerce SEO, international SEO, Google APIs, and PDF/Excel reporting. Optional DataForSEO, Firecrawl, and Banana extensions.
This report presents the forensic synthetic code analysis of AgriciDaniel/claude-seo, a Python project with 13,074 GitHub stars. SynthScan v2.0 examined 58,140 lines of code across 355 source files, recording 843 pattern matches distributed across 18 syntactic categories. The overall adjusted score of 25.0 places this repository in the Moderate AI signal band.
The scanner applied 160+ deterministic lexical heuristics, multi-line block detectors, abstract syntax tree depth profilers, and a cross-file Jaccard similarity matrix to construct a statistically normalised synthetic code estimate. All matches are individually weighted by severity coefficient and contextual multiplier before summation, and the resulting headline score is temporally discounted to account for the repository's development history relative to the commercial emergence of large language model coding tooling (November 2022 onward).
Longitudinal tracking requires multiple scan runs. Once this repository is re-scanned after new commits land, this chart will visualise how the synthetic code signal evolves over time — enabling you to detect whether AI authorship is growing, stabilising, or being actively corrected by human engineers.
Classifies detected patterns by their diagnostic confidence and structural impact. CRITICAL patterns (coefficient 10) represent definitive synthetic signatures — hallucinated imports, explicit LLM attribution metadata — virtually never produced by human authors. HIGH (5) indicates strong structural tells such as cross-file repetition or cross-linguistic idioms. MEDIUM (2) covers recognisable conversational padding and AI-specific vocabulary. LOW (1) captures subtle indicators like tautological comments and generic boilerplate that require density to carry independent signal.
This horizontal bar chart decomposes the repository's raw synthetic code score by top-level directory, allowing you to pinpoint precisely which modules or components carry the highest AI authorship density. Directories with disproportionately high scores relative to their size warrant targeted manual review: concentrated AI signatures often trace back to mass-generated configuration layers, auto-ported test suites, LLM-scaffolded boilerplate classes, or entire subsystems authored under heavy copilot assistance. Use this view to prioritise your human code-review effort.
The scanner identified 843 distinct pattern matches across 18 syntactic categories. Each entry below represents a discrete location in the source code where the engine recorded a statistically significant AI authorship indicator. Expand any category row to inspect the individual file paths, line numbers, code snippets, and the lexical context (CODE, COMMENT, or STRING) in which each match was detected.
Reading the findings table: The Severity column indicates the diagnostic confidence level (CRITICAL / HIGH / MEDIUM / LOW). The Context column identifies whether the match occurred inside executable code, an inline comment, or a string literal — comment-context matches receive a ×1.5 weight because LLMs systematically over-annotate. The ⚡ bolt icon marks clustered matches: three or more patterns within a 10-line window, each receiving an additional ×1.5 density multiplier as dense clusters constitute far stronger evidence of synthetic authorship than isolated hits.
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| MEDIUM⚡ | tests/test_phase_j_executable.py | 33 | # --------------------------------------------------------------------------- | COMMENT |
| MEDIUM⚡ | tests/test_phase_j_executable.py | 35 | # --------------------------------------------------------------------------- | COMMENT |
| MEDIUM⚡ | tests/test_phase_j_executable.py | 120 | # --------------------------------------------------------------------------- | COMMENT |
| MEDIUM⚡ | tests/test_phase_j_executable.py | 122 | # --------------------------------------------------------------------------- | COMMENT |
| MEDIUM⚡ | tests/test_phase_j_executable.py | 202 | # --------------------------------------------------------------------------- | COMMENT |
| MEDIUM⚡ | tests/test_phase_j_executable.py | 204 | # --------------------------------------------------------------------------- | COMMENT |
| MEDIUM⚡ | tests/test_phase_j_executable.py | 316 | # --------------------------------------------------------------------------- | STRING |
| MEDIUM⚡ | tests/test_phase_j_executable.py | 318 | # --------------------------------------------------------------------------- | STRING |
| MEDIUM⚡ | tests/test_portability.py | 23 | # --------------------------------------------------------------------------- | COMMENT |
| MEDIUM⚡ | tests/test_portability.py | 25 | # --------------------------------------------------------------------------- | COMMENT |
| MEDIUM⚡ | tests/test_portability.py | 99 | # --------------------------------------------------------------------------- | COMMENT |
| MEDIUM⚡ | tests/test_portability.py | 101 | # --------------------------------------------------------------------------- | COMMENT |
| MEDIUM⚡ | tests/test_gbp_lint_and_polish.py | 24 | # --------------------------------------------------------------------------- | COMMENT |
| MEDIUM⚡ | tests/test_gbp_lint_and_polish.py | 26 | # --------------------------------------------------------------------------- | COMMENT |
| MEDIUM⚡ | tests/test_gbp_lint_and_polish.py | 81 | # --------------------------------------------------------------------------- | COMMENT |
| MEDIUM⚡ | tests/test_gbp_lint_and_polish.py | 83 | # --------------------------------------------------------------------------- | COMMENT |
| MEDIUM⚡ | tests/test_gbp_lint_and_polish.py | 101 | # --------------------------------------------------------------------------- | COMMENT |
| MEDIUM⚡ | tests/test_gbp_lint_and_polish.py | 103 | # --------------------------------------------------------------------------- | COMMENT |
| MEDIUM⚡ | tests/test_render_page.py | 116 | # --------------------------------------------------------------------------- | COMMENT |
| MEDIUM⚡ | tests/test_render_page.py | 118 | # --------------------------------------------------------------------------- | COMMENT |
| MEDIUM⚡ | tests/test_render_page.py | 201 | # --------------------------------------------------------------------------- | COMMENT |
| MEDIUM⚡ | tests/test_render_page.py | 203 | # --------------------------------------------------------------------------- | COMMENT |
| MEDIUM⚡ | tests/test_render_page.py | 217 | # --------------------------------------------------------------------------- | COMMENT |
| MEDIUM⚡ | tests/test_render_page.py | 219 | # --------------------------------------------------------------------------- | COMMENT |
| MEDIUM⚡ | tests/test_render_page.py | 239 | # --------------------------------------------------------------------------- | COMMENT |
| MEDIUM⚡ | tests/test_render_page.py | 241 | # --------------------------------------------------------------------------- | COMMENT |
| MEDIUM⚡ | tests/test_render_page.py | 412 | # --------------------------------------------------------------------------- | COMMENT |
| MEDIUM⚡ | tests/test_render_page.py | 414 | # --------------------------------------------------------------------------- | COMMENT |
| MEDIUM⚡ | tests/test_render_page.py | 445 | # --------------------------------------------------------------------------- | COMMENT |
| MEDIUM⚡ | tests/test_render_page.py | 447 | # --------------------------------------------------------------------------- | COMMENT |
| MEDIUM | tests/test_render_page.py | 36 | # --------------------------------------------------------------------------- | COMMENT |
| MEDIUM | tests/test_render_page.py | 38 | # --------------------------------------------------------------------------- | COMMENT |
| MEDIUM | tests/test_render_page.py | 257 | # --------------------------------------------------------------------------- | COMMENT |
| MEDIUM | tests/test_render_page.py | 259 | # --------------------------------------------------------------------------- | COMMENT |
| MEDIUM⚡ | tests/test_technical_depth.py | 33 | # --------------------------------------------------------------------------- | COMMENT |
| MEDIUM⚡ | tests/test_technical_depth.py | 35 | # --------------------------------------------------------------------------- | COMMENT |
| MEDIUM⚡ | tests/test_technical_depth.py | 95 | # --------------------------------------------------------------------------- | COMMENT |
| MEDIUM⚡ | tests/test_technical_depth.py | 97 | # --------------------------------------------------------------------------- | COMMENT |
| MEDIUM⚡ | tests/test_technical_depth.py | 142 | # --------------------------------------------------------------------------- | COMMENT |
| MEDIUM⚡ | tests/test_technical_depth.py | 144 | # --------------------------------------------------------------------------- | COMMENT |
| MEDIUM⚡ | tests/test_technical_depth.py | 160 | # --------------------------------------------------------------------------- | COMMENT |
| MEDIUM⚡ | tests/test_technical_depth.py | 162 | # --------------------------------------------------------------------------- | COMMENT |
| MEDIUM⚡ | tests/test_content_quality.py | 33 | # --------------------------------------------------------------------------- | COMMENT |
| MEDIUM⚡ | tests/test_content_quality.py | 35 | # --------------------------------------------------------------------------- | COMMENT |
| MEDIUM⚡ | tests/test_content_quality.py | 111 | # --------------------------------------------------------------------------- | COMMENT |
| MEDIUM⚡ | tests/test_content_quality.py | 113 | # --------------------------------------------------------------------------- | COMMENT |
| MEDIUM⚡ | tests/test_content_quality.py | 166 | # --------------------------------------------------------------------------- | COMMENT |
| MEDIUM⚡ | tests/test_content_quality.py | 168 | # --------------------------------------------------------------------------- | COMMENT |
| MEDIUM⚡ | tests/test_content_quality.py | 212 | # --------------------------------------------------------------------------- | COMMENT |
| MEDIUM⚡ | tests/test_content_quality.py | 214 | # --------------------------------------------------------------------------- | COMMENT |
| MEDIUM | tests/test_sync_flow.py | 82 | # ── Task 1 tests ────────────────────────────────────────────────────────────── | COMMENT |
| MEDIUM | tests/test_sync_flow.py | 107 | # ── Module-level loader for unit tests (no network calls) ───────────────────── | COMMENT |
| MEDIUM | tests/test_sync_flow.py | 118 | # ── Task 2 tests ────────────────────────────────────────────────────────────── | COMMENT |
| MEDIUM⚡ | tests/test_sync_flow.py | 183 | # ── Task 3 tests ────────────────────────────────────────────────────────────── | COMMENT |
| MEDIUM | tests/test_sync_flow.py | 221 | # ── Task 4 tests ────────────────────────────────────────────────────────────── | COMMENT |
| MEDIUM | tests/test_sync_flow.py | 233 | # ── Task 5 tests ────────────────────────────────────────────────────────────── | COMMENT |
| MEDIUM⚡ | tests/test_schema_v2.py | 28 | # --------------------------------------------------------------------------- | COMMENT |
| MEDIUM⚡ | tests/test_schema_v2.py | 30 | # --------------------------------------------------------------------------- | COMMENT |
| MEDIUM⚡ | tests/test_schema_v2.py | 113 | # --------------------------------------------------------------------------- | COMMENT |
| MEDIUM⚡ | tests/test_schema_v2.py | 115 | # --------------------------------------------------------------------------- | COMMENT |
| 98 more matches not shown… | ||||
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | tests/test_agent_mcp_permissions.py | 16 | def test_dataforseo_agent_mirrors_allow_only_sanctioned_mcp_path(): | CODE |
| LOW | tests/test_agent_mcp_permissions.py | 25 | def test_dataforseo_agent_mirrors_fail_closed_without_mcp(): | CODE |
| LOW | tests/test_agent_mcp_permissions.py | 37 | def test_dataforseo_agent_bodies_stay_mirrored(): | CODE |
| LOW | tests/test_agent_mcp_permissions.py | 43 | def test_dataforseo_installers_use_matching_mcp_server_name(): | CODE |
| LOW | tests/test_audit_instructions.py | 10 | def test_seo_audit_starts_with_render_page_auto() -> None: | CODE |
| LOW | tests/test_audit_instructions.py | 19 | def test_seo_audit_requires_persistent_artifacts() -> None: | CODE |
| LOW | tests/test_audit_instructions.py | 32 | def test_audit_agents_use_renderer_or_capture_script() -> None: | CODE |
| LOW | tests/test_audit_instructions.py | 45 | def test_audit_agents_document_output_dir_findings_contract() -> None: | CODE |
| LOW | tests/test_audit_instructions.py | 68 | def test_seo_audit_report_command_keeps_outputs_in_audit_dir() -> None: | CODE |
| LOW⚡ | tests/test_phase_j_executable.py | 38 | def test_discovery_url_for_normalizes_to_well_known(): | CODE |
| LOW⚡ | tests/test_phase_j_executable.py | 107 | def test_parse_profile_rejects_non_object_root(): | CODE |
| LOW⚡ | tests/test_phase_j_executable.py | 112 | def test_known_capabilities_cover_core_commerce_verbs(): | CODE |
| LOW⚡ | tests/test_phase_j_executable.py | 125 | def test_iptc_vocab_contains_required_merchant_center_value(): | CODE |
| LOW⚡ | tests/test_phase_j_executable.py | 133 | def test_audit_reports_target_not_found(tmp_path): | CODE |
| LOW⚡ | tests/test_phase_j_executable.py | 138 | def test_audit_reports_exiftool_unavailable(tmp_path, monkeypatch): | CODE |
| LOW⚡ | tests/test_phase_j_executable.py | 147 | def test_inject_rejects_unknown_source_type(tmp_path): | CODE |
| LOW⚡ | tests/test_phase_j_executable.py | 155 | def test_inject_reports_missing_exiftool(tmp_path, monkeypatch): | CODE |
| LOW⚡ | tests/test_phase_j_executable.py | 164 | def test_inject_succeeds_when_subprocess_returns_zero(tmp_path, monkeypatch): | CODE |
| LOW⚡ | tests/test_phase_j_executable.py | 207 | def test_analyze_html_counts_semantic_tags_and_widgets(): | CODE |
| LOW | tests/test_phase_j_executable.py | 55 | def test_parse_profile_rejects_malformed_json(): | CODE |
| LOW | tests/test_phase_j_executable.py | 61 | def test_parse_profile_valid_minimal_profile(): | CODE |
| LOW | tests/test_phase_j_executable.py | 82 | def test_parse_profile_flags_missing_fields_and_unknown_capability(): | CODE |
| LOW | tests/test_phase_j_executable.py | 176 | def test_audit_classifies_files_via_mocked_exiftool(tmp_path, monkeypatch): | CODE |
| LOW⚡ | tests/test_phase_j_executable.py | 321 | def test_render_page_result_dict_includes_accessibility_tree_key(): | STRING |
| LOW | tests/test_phase_j_executable.py | 225 | def test_analyze_html_detects_unlabeled_inputs(): | STRING |
| LOW | tests/test_phase_j_executable.py | 240 | def test_analyze_accessibility_tree_handles_none_input(): | STRING |
| LOW | tests/test_phase_j_executable.py | 246 | def test_analyze_accessibility_tree_counts_roles_and_names(): | STRING |
| LOW | tests/test_phase_j_executable.py | 269 | def test_score_clean_page_scores_high(): | STRING |
| LOW | tests/test_phase_j_executable.py | 292 | def test_score_deducts_for_div_onclick_and_unnamed_interactives(): | STRING |
| LOW | tests/test_phase_j_executable.py | 332 | def test_render_page_accepts_extract_accessibility_kwarg(): | STRING |
| LOW⚡ | tests/test_portability.py | 28 | def test_whole_repo_passes_portability_lint() -> None: | CODE |
| LOW⚡ | tests/test_portability.py | 44 | def test_check_one_flags_missing_frontmatter(tmp_path) -> None: | CODE |
| LOW⚡ | tests/test_portability.py | 52 | def test_check_one_flags_missing_name(tmp_path) -> None: | CODE |
| LOW⚡ | tests/test_portability.py | 60 | def test_check_one_flags_camelcase_name(tmp_path) -> None: | CODE |
| LOW⚡ | tests/test_portability.py | 68 | def test_check_one_flags_missing_description(tmp_path) -> None: | CODE |
| LOW⚡ | tests/test_portability.py | 76 | def test_check_one_warns_on_inline_comment_in_tools(tmp_path) -> None: | CODE |
| LOW | tests/test_portability.py | 87 | def test_check_one_clean_skill_returns_no_findings(tmp_path) -> None: | CODE |
| LOW⚡ | tests/test_portability.py | 104 | def test_agents_md_covers_all_target_platforms() -> None: | CODE |
| LOW⚡ | tests/test_portability.py | 111 | def test_agents_md_documents_portability_check() -> None: | CODE |
| LOW⚡ | tests/test_portability.py | 116 | def test_agents_md_tool_mapping_table_includes_codex_cline_aider() -> None: | CODE |
| LOW⚡ | tests/test_fetch_page_decoding.py | 28 | def test_explicit_charset_from_content_type_wins() -> None: | CODE |
| LOW⚡ | tests/test_fetch_page_decoding.py | 33 | def test_meta_charset_is_used_when_header_has_no_charset() -> None: | CODE |
| LOW⚡ | tests/test_fetch_page_decoding.py | 39 | def test_invalid_bytes_are_replaced_not_dropped() -> None: | CODE |
| LOW⚡ | tests/test_gsc_pagination.py | 60 | def test_small_limit_is_one_small_request_plus_aggregate(): | CODE |
| LOW⚡ | tests/test_gsc_pagination.py | 67 | def test_exact_api_page_limit_does_not_fetch_an_extra_dimension_page(): | CODE |
| LOW⚡ | tests/test_gsc_pagination.py | 73 | def test_limit_over_api_page_size_fetches_only_the_remaining_row(): | CODE |
| LOW⚡ | tests/test_gsc_pagination.py | 80 | def test_short_page_stops_before_total_cap(): | CODE |
| LOW⚡ | tests/test_gsc_pagination.py | 86 | def test_invalid_limits_fail_before_building_service(): | CODE |
| LOW⚡ | tests/test_gsc_pagination.py | 94 | def test_invalid_programmatic_dimensions_fail_before_building_service(): | CODE |
| LOW⚡ | tests/test_gsc_pagination.py | 110 | def test_none_programmatic_dimensions_preserve_default(): | CODE |
| LOW⚡ | tests/test_gsc_pagination.py | 120 | def test_blank_cli_dimensions_parse_to_empty_list(): | CODE |
| LOW⚡ | tests/test_gsc_pagination.py | 125 | def test_dimensionless_primary_query_is_reused_for_totals(): | CODE |
| LOW⚡ | tests/test_gsc_pagination.py | 134 | def test_filters_are_copied_to_dimensionless_aggregate(): | CODE |
| LOW | tests/test_banana_api_key_safety.py | 49 | def test_banana_generate_redacts_upstream_http_error_body() -> None: | CODE |
| LOW | tests/test_banana_api_key_safety.py | 68 | def test_banana_edit_redacts_upstream_http_error_body(tmp_path: Path) -> None: | CODE |
| LOW | tests/test_extension_installer_injection.py | 40 | def test_installer_uses_safe_credential_pattern(rel: str, argc: int) -> None: | CODE |
| LOW | tests/test_extension_installer_injection.py | 51 | def test_installer_credential_injection_is_inert(tmp_path: Path, rel: str, argc: int) -> None: | CODE |
| LOW | tests/test_drift_portability.py | 21 | def test_fetch_page_data_uses_tempfile_not_dev_stdout_or_stdin() -> None: | CODE |
| LOW⚡ | tests/test_gbp_lint_and_polish.py | 29 | def test_clean_page_returns_ok() -> None: | CODE |
| LOW⚡ | tests/test_gbp_lint_and_polish.py | 35 | def test_detects_gbp_chat_cta() -> None: | CODE |
| 250 more matches not shown… | ||||
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | extensions/ahrefs/install.sh | 77 | except Exception: | CODE |
| LOW | extensions/dataforseo/install.sh | 146 | except Exception: | CODE |
| LOW | extensions/firecrawl/install.sh | 115 | except Exception: | CODE |
| LOW | extensions/banana/install.sh | 126 | except Exception: | CODE |
| MEDIUM | extensions/banana/scripts/cost_tracker.py | 46 | print(f"Error: Missing pricing config at {PRICING_PATH}.", file=sys.stderr) | CODE |
| MEDIUM | extensions/banana/scripts/cost_tracker.py | 54 | print("Error: pricing.json must include checked_date and models.", file=sys.stderr) | CODE |
| MEDIUM | extensions/banana/scripts/cost_tracker.py | 70 | print(f"Error: No pricing for model '{model}' in {PRICING_PATH}.", file=sys.stderr) | CODE |
| MEDIUM | extensions/banana/scripts/cost_tracker.py | 79 | print(f"Error: No pricing for resolution '{resolution}' in {PRICING_PATH}.", file=sys.stderr) | CODE |
| MEDIUM | extensions/banana/scripts/cost_tracker.py | 163 | print("Error: Pass --confirm to reset the cost ledger.", file=sys.stderr) | CODE |
| MEDIUM | extensions/banana/scripts/presets.py | 32 | print("Error: Preset name must contain only letters, numbers, hyphens, and underscores.", file=sys.stderr) | CODE |
| MEDIUM | extensions/banana/scripts/presets.py | 47 | print(f"Error: Preset '{name}' not found.", file=sys.stderr) | CODE |
| MEDIUM | extensions/banana/scripts/presets.py | 82 | print(f"Error: Preset '{args.name}' already exists. Use a different name.", file=sys.stderr) | CODE |
| MEDIUM | extensions/banana/scripts/presets.py | 109 | print("Error: Pass --confirm to delete the preset.", file=sys.stderr) | CODE |
| MEDIUM | extensions/banana/scripts/presets.py | 113 | print(f"Error: Preset '{args.name}' not found.", file=sys.stderr) | CODE |
| MEDIUM | extensions/banana/scripts/setup_mcp.py | 76 | print("Error: API key cannot be empty.") | CODE |
| MEDIUM | scripts/bing_webmaster.py | 25 | print("Error: requests library required. Install with: pip install requests") | CODE |
| MEDIUM | scripts/bing_webmaster.py | 34 | print("Error: backlinks_auth.py and google_auth.py required in scripts/", file=sys.stderr) | CODE |
| MEDIUM | scripts/bing_webmaster.py | 519 | print(f"Error: {result['error']}", file=sys.stderr) | CODE |
| MEDIUM | scripts/bing_webmaster.py | 523 | print("Error: compare command requires a competitor URL", file=sys.stderr) | CODE |
| MEDIUM | scripts/bing_webmaster.py | 543 | print(f"Error: {result['error']}", file=sys.stderr) | CODE |
| MEDIUM | scripts/bing_webmaster.py | 559 | print(f"Error: {result['error']}", file=sys.stderr) | CODE |
| MEDIUM | scripts/bing_webmaster.py | 606 | print(f"Error: {result['error']}", file=sys.stderr) | CODE |
| MEDIUM | scripts/sitemap_discovery.py | 281 | print(f"Error: {result['error']}", file=sys.stderr) | CODE |
| MEDIUM | scripts/verify_release.py | 117 | print(f"Error: manifest not found: {args.manifest}", file=sys.stderr) | CODE |
| MEDIUM⚡ | scripts/google_auth.py | 482 | print(f"Error exchanging authorization code: {e}", file=sys.stderr) | CODE |
| MEDIUM | scripts/google_auth.py | 175 | print(f"Error loading service account: {e}", file=sys.stderr) | CODE |
| MEDIUM | scripts/google_auth.py | 186 | print(f"Error reading OAuth client file: {e}", file=sys.stderr) | CODE |
| MEDIUM | scripts/google_auth.py | 308 | print(f"Error refreshing OAuth token: {e}", file=sys.stderr) | CODE |
| MEDIUM | scripts/google_auth.py | 358 | print("Error: google-auth required. Install with: pip install google-auth", file=sys.stderr) | CODE |
| MEDIUM | scripts/google_auth.py | 381 | print("Error: Could not load OAuth client credentials.", file=sys.stderr) | CODE |
| MEDIUM | scripts/google_auth.py | 554 | print(f"Error building {api_name} service: {e}", file=sys.stderr) | CODE |
| LOW⚡ | scripts/google_auth.py | 472 | except Exception as e: | CODE |
| LOW⚡ | scripts/google_auth.py | 481 | except Exception as e: | CODE |
| LOW | scripts/google_auth.py | 174 | except Exception as e: | CODE |
| LOW | scripts/google_auth.py | 278 | except Exception: | CODE |
| LOW | scripts/google_auth.py | 307 | except Exception as e: | CODE |
| LOW | scripts/google_auth.py | 418 | except Exception: | CODE |
| LOW | scripts/google_auth.py | 553 | except Exception as e: | CODE |
| MEDIUM | scripts/google_auth.py | 833 | print("Error: --creds is required with --auth", file=sys.stderr) | STRING |
| MEDIUM | scripts/google_auth.py | 840 | print("Error: --creds and --code are required with --exchange", file=sys.stderr) | STRING |
| MEDIUM⚡ | scripts/drift_report.py | 347 | print(f"Error: File not found: {args.file}", file=sys.stderr) | CODE |
| MEDIUM⚡ | scripts/drift_report.py | 352 | print("Error: Provide a JSON file path or use --stdin", file=sys.stderr) | CODE |
| MEDIUM⚡ | scripts/drift_report.py | 358 | print(f"Error: Invalid JSON: {e}", file=sys.stderr) | CODE |
| MEDIUM⚡ | scripts/drift_report.py | 362 | print(f"Error: Comparison has error: {comparison['error']}", file=sys.stderr) | CODE |
| MEDIUM | scripts/iptc_ai_label.py | 206 | print(f"Error: {report['summary']['error']}", file=sys.stderr) | CODE |
| MEDIUM | scripts/iptc_ai_label.py | 220 | print(f"Error: {out['error']}", file=sys.stderr) | CODE |
| MEDIUM⚡ | scripts/google_report.py | 2676 | print(f"Error reading data file: {e}", file=sys.stderr) | CODE |
| MEDIUM⚡ | scripts/google_report.py | 2682 | print(f"Error parsing stdin JSON: {e}", file=sys.stderr) | CODE |
| MEDIUM⚡ | scripts/google_report.py | 2685 | print("Error: Provide --data file or pipe JSON via stdin.", file=sys.stderr) | CODE |
| MEDIUM | scripts/google_report.py | 2697 | print(f"Error: {result['error']}", file=sys.stderr) | CODE |
| LOW | scripts/google_report.py | 2400 | except Exception as e: | CODE |
| MEDIUM | scripts/gbp_deprecation_lint.py | 116 | print(f"Error: url_safety: {exc}", file=sys.stderr) | CODE |
| LOW | scripts/capture_screenshot.py | 151 | except Exception as e: | CODE |
| MEDIUM | scripts/capture_screenshot.py | 173 | print("Error: Output path must be within current directory or home directory", file=sys.stderr) | CODE |
| MEDIUM | scripts/capture_screenshot.py | 182 | print(f"Error: {e}", file=sys.stderr) | CODE |
| LOW | scripts/ga4_report.py | 56 | except Exception as e: | CODE |
| MEDIUM | scripts/ga4_report.py | 57 | print(f"Error building GA4 client: {e}", file=sys.stderr) | CODE |
| LOW | scripts/ga4_report.py | 164 | except Exception as e: | CODE |
| LOW | scripts/ga4_report.py | 224 | except Exception as e: | CODE |
| LOW | scripts/ga4_report.py | 329 | except Exception as e: | CODE |
| 87 more matches not shown… | ||||
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | tests/test_audit_instructions.py | 3 | CODE | |
| LOW | tests/test_phase_j_executable.py | 13 | CODE | |
| LOW | tests/test_phase_j_executable.py | 20 | CODE | |
| LOW | tests/test_portability.py | 7 | CODE | |
| LOW | tests/test_portability.py | 9 | CODE | |
| LOW | tests/test_portability.py | 13 | CODE | |
| LOW | tests/test_fetch_page_decoding.py | 3 | CODE | |
| LOW | tests/test_nlp_analyze.py | 3 | CODE | |
| LOW | tests/test_gsc_pagination.py | 3 | CODE | |
| LOW | tests/test_banana_api_key_safety.py | 3 | CODE | |
| LOW | tests/test_extension_installer_injection.py | 11 | CODE | |
| LOW | tests/test_drift_portability.py | 3 | CODE | |
| LOW | tests/test_gbp_lint_and_polish.py | 8 | CODE | |
| LOW | tests/test_gbp_lint_and_polish.py | 10 | CODE | |
| LOW | tests/test_gbp_lint_and_polish.py | 14 | CODE | |
| LOW | tests/test_gsc_totals_aggregate.py | 6 | CODE | |
| LOW | tests/test_runtime.py | 3 | CODE | |
| LOW | tests/test_cross_platform_hooks.py | 3 | CODE | |
| LOW | tests/test_render_page.py | 19 | CODE | |
| LOW | tests/test_technical_depth.py | 15 | CODE | |
| LOW | tests/test_technical_depth.py | 21 | CODE | |
| LOW | tests/test_sitemap_discovery.py | 3 | CODE | |
| LOW | tests/test_google_api_key_safety.py | 7 | CODE | |
| LOW | tests/test_google_report_full_audit.py | 3 | CODE | |
| LOW | tests/test_moz_api.py | 3 | CODE | |
| LOW | tests/test_schema_hook_policy.py | 8 | CODE | |
| LOW | tests/test_content_quality.py | 14 | CODE | |
| LOW | tests/test_pyproject_metadata.py | 3 | CODE | |
| LOW | tests/test_bing_webmaster.py | 3 | CODE | |
| LOW | tests/test_contributors.py | 3 | CODE | |
| LOW | tests/test_schema_v2.py | 12 | CODE | |
| LOW | tests/test_schema_v2.py | 14 | CODE | |
| LOW | tests/test_schema_v2.py | 18 | CODE | |
| LOW | tests/test_url_safety.py | 12 | CODE | |
| LOW | tests/test_gsc_query.py | 3 | CODE | |
| LOW | tests/test_gsc_query.py | 5 | CODE | |
| LOW | tests/test_parasite_risk_and_extensions.py | 8 | CODE | |
| LOW | tests/test_parasite_risk_and_extensions.py | 10 | CODE | |
| LOW | scripts/sitemap_discovery.py | 9 | CODE | |
| LOW | scripts/verify_release.py | 30 | CODE | |
| LOW | scripts/backlinks_auth.py | 29 | CODE | |
| LOW | scripts/content_quality.py | 47 | CODE | |
| LOW | scripts/schema_generate.py | 47 | CODE | |
| LOW | scripts/iptc_ai_label.py | 33 | CODE | |
| LOW | scripts/iptc_ai_label.py | 37 | CODE | |
| LOW | scripts/google_report.py | 23 | CODE | |
| LOW | scripts/gbp_deprecation_lint.py | 26 | CODE | |
| LOW | scripts/drift_compare.py | 29 | CODE | |
| LOW | scripts/capture_screenshot.py | 16 | CODE | |
| LOW | scripts/ga4_report.py | 18 | CODE | |
| LOW | scripts/url_safety.py | 73 | CODE | |
| LOW | scripts/schema_ecommerce_validate.py | 33 | CODE | |
| LOW | scripts/commoncrawl_graph.py | 20 | CODE | |
| LOW | scripts/commoncrawl_graph.py | 38 | CODE | |
| LOW | scripts/ucp_check.py | 32 | CODE | |
| LOW | scripts/ucp_check.py | 38 | CODE | |
| LOW | scripts/parse_html.py | 25 | CODE | |
| LOW | scripts/dataforseo_costs.py | 29 | CODE | |
| LOW | scripts/indexing_notify.py | 24 | CODE | |
| LOW | scripts/indexing_notify.py | 28 | CODE | |
| 25 more matches not shown… | ||||
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | agents/seo-backlinks.md | 89 | ### Step 1: Automated validation | COMMENT |
| LOW | agents/seo-backlinks.md | 97 | ### Step 2: Manual checks (not automatable) | COMMENT |
| LOW⚡ | scripts/drift_baseline.py | 153 | # Step 1: Fetch the page via fetch_page.py | STRING |
| LOW | scripts/drift_baseline.py | 188 | # Step 2: Parse the HTML via parse_html.py | STRING |
| LOW | scripts/verify_backlinks.py | 130 | # Step 1: HEAD check | COMMENT |
| LOW | scripts/verify_backlinks.py | 153 | # Step 2: Full GET + parse | COMMENT |
| LOW | scripts/verify_backlinks.py | 170 | # Step 3: Parse HTML and find target link | COMMENT |
| LOW | skills/seo-google/references/auth-setup.md | 13 | ## Step 1: Create a Google Cloud Project | COMMENT |
| LOW | skills/seo-google/references/auth-setup.md | 20 | ## Step 2: Enable APIs | COMMENT |
| LOW | skills/seo-google/references/auth-setup.md | 33 | ## Step 3: Create an API Key | COMMENT |
| LOW | skills/seo-google/references/auth-setup.md | 40 | ## Step 4: Create a Service Account | COMMENT |
| LOW⚡ | skills/seo-google/references/auth-setup.md | 64 | ## Step 5: Grant Search Console Access | COMMENT |
| LOW⚡ | skills/seo-google/references/auth-setup.md | 74 | ## Step 6: Grant GA4 Access | COMMENT |
| LOW⚡ | skills/seo-google/references/auth-setup.md | 82 | ## Step 7: Create Config File | COMMENT |
| LOW | skills/seo-google/references/auth-setup.md | 106 | ## Step 8: Verify Setup | COMMENT |
| LOW | skills/seo-cluster/SKILL.md | 45 | ### Step 1: Seed Keyword Expansion | COMMENT |
| LOW | skills/seo-cluster/SKILL.md | 59 | ### Step 2: SERP Overlap Clustering | COMMENT |
| LOW | skills/seo-cluster/SKILL.md | 87 | ### Step 3: Intent Classification | COMMENT |
| LOW | skills/seo-cluster/SKILL.md | 102 | ### Step 4: Hub-and-Spoke Architecture | COMMENT |
| LOW | skills/seo-cluster/SKILL.md | 131 | ### Step 5: Internal Link Matrix | COMMENT |
| LOW | skills/seo-cluster/SKILL.md | 158 | ### Step 6: Interactive Cluster Map | COMMENT |
| LOW⚡ | …lls/seo-cluster/references/serp-overlap-methodology.md | 12 | ### Step 1: Collect SERP Data | COMMENT |
| LOW⚡ | …lls/seo-cluster/references/serp-overlap-methodology.md | 20 | ### Step 2: Pairwise Comparison | COMMENT |
| LOW⚡ | …lls/seo-cluster/references/serp-overlap-methodology.md | 27 | ### Step 3: Apply Thresholds | COMMENT |
| LOW⚡ | …lls/seo-cluster/references/serp-overlap-methodology.md | 36 | ### Step 4: Handle Ambiguous Scores (3-4 Range) | COMMENT |
| LOW | skills/seo-sxo/SKILL.md | 44 | ### Step 1: Target Acquisition | COMMENT |
| LOW | skills/seo-sxo/SKILL.md | 52 | ### Step 2: SERP Backwards Analysis | COMMENT |
| LOW | skills/seo-sxo/SKILL.md | 77 | ### Step 3: Page-Type Mismatch Detection | COMMENT |
| LOW | skills/seo-sxo/SKILL.md | 98 | ### Step 4: User Story Derivation | COMMENT |
| LOW | skills/seo-sxo/SKILL.md | 120 | ### Step 5: Gap Analysis | COMMENT |
| LOW | skills/seo-sxo/SKILL.md | 136 | ### Step 6: Persona-Based Scoring | COMMENT |
| LOW | skills/seo-sxo/SKILL.md | 152 | ### Step 7: Wireframe Generation (Optional) | COMMENT |
| LOW | …rompts/optimize/step-1-the-chatgpt-discovery-prompt.md | 11 | # Step 1: The ChatGPT Discovery Prompt | COMMENT |
| LOW | …pts/optimize/step-2-the-follow-up-qualifying-prompt.md | 11 | # Step 2: The Follow-Up Qualifying Prompt | COMMENT |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | extensions/banana/scripts/batch.py | 35 | CODE | |
| LOW | hooks/validate-schema.py | 124 | CODE | |
| LOW | scripts/bing_webmaster.py | 190 | CODE | |
| LOW | scripts/bing_webmaster.py | 383 | CODE | |
| LOW | scripts/bing_webmaster.py | 474 | CODE | |
| LOW | scripts/sitemap_discovery.py | 74 | CODE | |
| LOW | scripts/sitemap_discovery.py | 183 | CODE | |
| LOW | scripts/sitemap_discovery.py | 272 | CODE | |
| LOW | scripts/backlinks_auth.py | 60 | CODE | |
| LOW | scripts/backlinks_auth.py | 104 | CODE | |
| LOW | scripts/backlinks_auth.py | 370 | CODE | |
| LOW | scripts/google_auth.py | 312 | CODE | |
| LOW | scripts/google_auth.py | 558 | CODE | |
| LOW | scripts/google_auth.py | 644 | CODE | |
| LOW | scripts/google_auth.py | 784 | CODE | |
| LOW | scripts/schema_generate.py | 246 | CODE | |
| LOW | scripts/google_report.py | 1094 | CODE | |
| LOW | scripts/google_report.py | 2057 | CODE | |
| LOW | scripts/google_report.py | 2464 | CODE | |
| LOW | scripts/dataforseo_normalize.py | 88 | CODE | |
| LOW | scripts/ga4_report.py | 397 | CODE | |
| LOW | scripts/schema_ecommerce_validate.py | 113 | CODE | |
| LOW | scripts/commoncrawl_graph.py | 148 | CODE | |
| LOW | scripts/commoncrawl_graph.py | 215 | CODE | |
| LOW | scripts/ucp_check.py | 72 | CODE | |
| LOW | scripts/parse_html.py | 76 | CODE | |
| LOW | scripts/dataforseo_costs.py | 144 | CODE | |
| LOW | scripts/dataforseo_costs.py | 164 | CODE | |
| LOW | scripts/gsc_query.py | 421 | CODE | |
| LOW | scripts/indexing_notify.py | 67 | CODE | |
| LOW | scripts/indexing_notify.py | 250 | CODE | |
| LOW | scripts/analyze_visual.py | 46 | CODE | |
| LOW | scripts/parasite_risk.py | 113 | CODE | |
| LOW | scripts/crux_history.py | 54 | CODE | |
| LOW | scripts/sync_flow.py | 274 | CODE | |
| LOW | scripts/pagespeed_check.py | 82 | CODE | |
| LOW | scripts/pagespeed_check.py | 311 | CODE | |
| LOW | scripts/pagespeed_check.py | 486 | CODE | |
| LOW | scripts/runtime.py | 244 | CODE | |
| LOW | scripts/agent_ux_check.py | 101 | CODE | |
| LOW | scripts/seo_updates.py | 63 | CODE | |
| LOW | scripts/lcp_subparts.py | 90 | CODE | |
| LOW | scripts/keyword_planner.py | 48 | CODE | |
| LOW | scripts/keyword_planner.py | 246 | CODE | |
| LOW | scripts/youtube_search.py | 288 | CODE | |
| LOW | scripts/domain_history.py | 107 | CODE | |
| LOW | scripts/domain_history.py | 216 | CODE | |
| LOW | scripts/moz_api.py | 347 | CODE | |
| LOW | scripts/gsc_inspect.py | 56 | CODE | |
| LOW | scripts/gsc_inspect.py | 177 | CODE | |
| LOW | scripts/gsc_inspect.py | 242 | CODE | |
| LOW | scripts/render_page.py | 168 | CODE | |
| LOW | scripts/render_page.py | 308 | CODE | |
| LOW | scripts/verify_backlinks.py | 95 | CODE | |
| LOW | scripts/consistency_check.py | 142 | CODE | |
| LOW | scripts/unlighthouse_run.py | 128 | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| HIGH | README.md | 20 | - **AI-search first.** Aligned with [Google's AI Optimization Guide](https://developers.google.com/search/docs/fundament | CODE |
| HIGH | README.md | 477 | Most SEO tools treat AI search as a separate optimization discipline. Claude SEO follows [Google's own position](https:/ | CODE |
| HIGH⚡ | tests/test_phase_j_executable.py | 126 | # Google Merchant Center requires this exact label on AI-generated images. | COMMENT |
| HIGH⚡ | scripts/drift_report.py | 309 | Generated by Claude SEO Drift Monitor | {report_time} | CODE |
| HIGH | scripts/google_report.py | 2046 | f' Report generated by Claude SEO — Google SEO Intelligence Skill — ' | CODE |
| HIGH | skills/seo-cluster/templates/cluster-map.html | 315 | <footer>Generated by Claude SEO</footer> | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | tests/test_render_page.py | 73 | + ("Lorem ipsum dolor sit amet, consectetur adipiscing elit. " * 5) | CODE |
| LOW | tests/test_render_page.py | 73 | + ("Lorem ipsum dolor sit amet, consectetur adipiscing elit. " * 5) | CODE |
| LOW⚡ | tests/test_bing_webmaster.py | 46 | result = bing.get_link_counts("https://site.test/", "placeholder") | CODE |
| LOW⚡ | tests/test_bing_webmaster.py | 56 | result = bing.get_link_counts("https://site.test/", "placeholder", max_pages=1) | CODE |
| LOW⚡ | tests/test_bing_webmaster.py | 67 | result = bing.get_link_counts("https://site.test/", "placeholder") | CODE |
| LOW⚡ | tests/test_bing_webmaster.py | 75 | result = bing.get_link_counts("https://site.test/", "placeholder") | CODE |
| LOW⚡ | tests/test_bing_webmaster.py | 90 | result = bing.get_link_details("https://site.test/", "placeholder", max_detail_pages=2) | CODE |
| LOW⚡ | tests/test_bing_webmaster.py | 112 | result = bing.get_link_details("https://site.test/", "placeholder") | CODE |
| LOW⚡ | tests/test_bing_webmaster.py | 125 | result = bing.get_link_details("https://site.test/", "placeholder") | CODE |
| LOW⚡ | tests/test_bing_webmaster.py | 132 | result = bing.get_link_details("https://site.test/", "placeholder", page=-1) | CODE |
| LOW⚡ | tests/test_bing_webmaster.py | 146 | result = bing.get_link_details("https://site.test/", "placeholder") | CODE |
| LOW⚡ | tests/test_bing_webmaster.py | 154 | result = bing.compare_links("https://a.test", "https://b.test", "placeholder") | CODE |
| LOW⚡ | tests/test_bing_webmaster.py | 166 | result = bing.compare_links("https://a.test", "https://b.test", "placeholder") | CODE |
| LOW⚡ | tests/test_bing_webmaster.py | 180 | result = bing._bing_request("GetLinkCounts", "placeholder", params) | CODE |
| LOW⚡ | tests/test_bing_webmaster.py | 186 | assert sent_params == {"siteUrl": "https://site.test/", "page": 0, "apikey": "placeholder"} | CODE |
| LOW | hooks/validate-schema.py | 178 | critical_keywords = ["placeholder", "deprecated", "retired"] | CODE |
| LOW | skills/seo-plan/assets/local-service.md | 76 | "streetAddress": "123 Main St", | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| HIGH | scripts/capture_screenshot.py | 31 | "pip install playwright && playwright install chromium" | CODE |
| HIGH | scripts/analyze_visual.py | 18 | print("Error: playwright required. Install with: pip install playwright && playwright install chromium") | CODE |
| HIGH | scripts/domain_history.py | 44 | "years_registered": float or null, | STRING |
| HIGH | scripts/domain_history.py | 45 | "topical_shift": true|false|null, | STRING |
| HIGH | scripts/render_page.py | 290 | "(document.body && document.body.innerText || '').trim().length," | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| HIGH | docs/MCP-INTEGRATION.md | 57 | "PAGESPEED_API_KEY": "your-api-key" | CODE |
| HIGH | extensions/firecrawl/docs/FIRECRAWL-SETUP.md | 31 | "FIRECRAWL_API_KEY": "fc-your-api-key-here" | CODE |
| HIGH | extensions/banana/docs/BANANA-SETUP.md | 25 | "GOOGLE_AI_API_KEY": "your-api-key-here" | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | extensions/dataforseo/install.sh | 82 | # Check if running from repo or standalone | COMMENT |
| LOW | extensions/firecrawl/install.sh | 64 | # Check if running from repo or standalone | COMMENT |
| LOW | extensions/banana/uninstall.sh | 16 | # Check if standalone banana skill still exists | COMMENT |
| LOW | extensions/banana/install.sh | 50 | # Check if running from repo or standalone | COMMENT |
| LOW | extensions/banana/install.sh | 61 | # Check if nanobanana-mcp is already configured | COMMENT |
| LOW | scripts/google_auth.py | 329 | # Check if token needs refresh | COMMENT |
| LOW | scripts/validate_backlink_report.py | 42 | # Check if it's a @graph wrapper that wasn't flattened | COMMENT |
| LOW | scripts/validate_backlink_report.py | 131 | # Check if ALL H1s are suspicious (likely no real heading) | COMMENT |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| MEDIUM | extensions/ahrefs/skills/seo-ahrefs/SKILL.md | 45 | ## Cost guardrails | COMMENT |
| MEDIUM | extensions/seranking/skills/seo-seranking/SKILL.md | 42 | ## Cost guardrails | COMMENT |
| MEDIUM | skills/seo/references/thinking-framework.md | 121 | ### 6. CONNECT — system orchestration | COMMENT |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW⚡ | scripts/drift_baseline.py | 153 | # Step 1: Fetch the page via fetch_page.py | STRING |
| LOW | scripts/drift_baseline.py | 188 | # Step 2: Parse the HTML via parse_html.py | STRING |
| LOW | scripts/verify_backlinks.py | 130 | # Step 1: HEAD check | COMMENT |
| LOW | scripts/verify_backlinks.py | 153 | # Step 2: Full GET + parse | COMMENT |
| LOW | scripts/verify_backlinks.py | 170 | # Step 3: Parse HTML and find target link | COMMENT |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| MEDIUM⚡ | tests/test_phase_j_executable.py | 139 | # Create a real file so we get past the target-exists check. | COMMENT |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | uninstall.sh | 1 | #!/usr/bin/env bash | COMMENT |
| LOW | scripts/url_safety.py | 101 | "make_safe_playwright_route_handler", | COMMENT |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| MEDIUM | scripts/content_quality.py | 120 | "it's worth noting that", | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | scripts/url_safety.py | 92 | __all__ = [ | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | scripts/drift_baseline.py | 401 | CODE |