Turn any technical book PDF into a Claude Code skill — ready to study, reference, and use while you work.
This report presents the forensic synthetic code analysis of virgiliojr94/book-to-skill, a Python project with 8,603 GitHub stars. SynthScan v2.0 examined 5,617 lines of code across 41 source files, recording 179 pattern matches distributed across 9 syntactic categories. The overall adjusted score of 37.4 places this repository in the Strong 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).
This chart maps the temporal evolution of the adjusted synthetic code score across successive scan runs. An upward trajectory indicates ongoing incorporation of AI-generated code or expanding LLM-assisted scaffolding; a stable or declining trajectory may reflect active human refactoring, code removal, or the adoption of stricter authorship policies. The dashed secondary line (right axis) independently tracks total raw pattern hit count, which can diverge from the normalised score when codebase size changes significantly between scans.
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 179 distinct pattern matches across 9 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 |
|---|---|---|---|---|
| LOW | book_to_skill/config.py | 37 | def supported_formats_message() -> str: | CODE |
| LOW | book_to_skill/utils.py | 121 | def _structural_chapter_count(text: str) -> int: | CODE |
| LOW | book_to_skill/parsers/docx.py | 8 | def extract_docx_with_python_docx(docx_path: str) -> str | None: | CODE |
| LOW | book_to_skill/parsers/docx.py | 26 | def extract_docx_with_zipfile(docx_path: str) -> str | None: | CODE |
| LOW | book_to_skill/parsers/calibre.py | 10 | def extract_with_ebook_convert(input_path: str) -> str | None: | CODE |
| LOW⚡ | tests/test_discovery_tax.py | 44 | def test_detects_three_chapters(self): | STRING |
| LOW⚡ | tests/test_discovery_tax.py | 50 | def test_best_chapter_picks_largest_body_over_toc_line(self): | STRING |
| LOW⚡ | tests/test_discovery_tax.py | 58 | def test_cross_reference_does_not_split(self): | STRING |
| LOW⚡ | tests/test_discovery_tax.py | 64 | def test_chapter_with_title_splits(self): | STRING |
| LOW⚡ | tests/test_discovery_tax.py | 69 | def test_repeated_cross_ref_does_not_refragment(self): | STRING |
| LOW⚡ | tests/test_book_to_skill.py | 180 | def test_epub_extract_with_ebooklib_returns_str_or_none(self): | CODE |
| LOW⚡ | tests/test_book_to_skill.py | 190 | def test_epub_extraction_via_zipfile_fallback(self, tmp_path): | CODE |
| LOW⚡ | tests/test_book_to_skill.py | 225 | def test_zipfile_fallback_resolves_oebps_paths(self, tmp_path): | CODE |
| LOW⚡ | tests/test_book_to_skill.py | 248 | def test_container_xml_locates_opf(self, tmp_path): | CODE |
| LOW⚡ | tests/test_book_to_skill.py | 258 | def test_count_chapters_with_oebps(self, tmp_path): | CODE |
| LOW⚡ | tests/test_book_to_skill.py | 266 | def test_root_level_opf_still_works(self, tmp_path): | CODE |
| LOW⚡ | tests/test_book_to_skill.py | 284 | def test_extract_single_file_raises_on_missing(self, tmp_path): | CODE |
| LOW⚡ | tests/test_book_to_skill.py | 290 | def test_extract_single_file_raises_on_unsupported(self, tmp_path): | CODE |
| LOW⚡ | tests/test_book_to_skill.py | 296 | def test_batch_continues_past_bad_files(self, tmp_path): | CODE |
| LOW⚡ | tests/test_book_to_skill.py | 368 | def test_extraction_error_is_not_system_exit(self): | CODE |
| LOW⚡ | tests/test_book_to_skill.py | 382 | def test_explicit_files_preserve_order(self, tmp_path): | CODE |
| LOW⚡ | tests/test_book_to_skill.py | 436 | def test_deduplication_preserves_first_occurrence(self, tmp_path): | CODE |
| LOW⚡ | tests/test_book_to_skill.py | 451 | def test_glob_filters_unsupported_extensions(self, tmp_path): | CODE |
| LOW⚡ | tests/test_book_to_skill.py | 578 | def test_toc_spanish_accented(self): | CODE |
| LOW⚡ | tests/test_book_to_skill.py | 581 | def test_toc_traditional_chinese(self): | CODE |
| LOW⚡ | tests/test_book_to_skill.py | 584 | def test_toc_italian_sommario(self): | CODE |
| LOW⚡ | tests/test_book_to_skill.py | 587 | def test_toc_inline_word_is_not_toc(self): | CODE |
| LOW⚡ | tests/test_book_to_skill.py | 592 | def test_numbered_list_items_are_not_chapters(self): | CODE |
| LOW⚡ | tests/test_book_to_skill.py | 601 | def test_inline_cross_references_are_not_chapters(self): | CODE |
| LOW⚡ | tests/test_book_to_skill.py | 608 | def test_years_are_not_chapters(self): | CODE |
| LOW⚡ | tests/test_book_to_skill.py | 612 | def test_real_headings_with_titles_count(self): | CODE |
| LOW⚡ | tests/test_book_to_skill.py | 620 | def test_distinct_numbering_dedups_toc_and_body(self): | CODE |
| LOW⚡ | tests/test_book_to_skill.py | 625 | def test_roman_numeral_chapters(self): | CODE |
| LOW⚡ | tests/test_book_to_skill.py | 629 | def test_roman_requires_title_after_separator(self): | CODE |
| LOW⚡ | tests/test_book_to_skill.py | 633 | def test_roman_rejects_non_canonical(self): | CODE |
| LOW⚡ | tests/test_book_to_skill.py | 637 | def test_scans_full_text_not_just_head(self): | CODE |
| LOW⚡ | tests/test_book_to_skill.py | 648 | def test_japanese_fullwidth_digit_chapters(self): | CODE |
| LOW⚡ | tests/test_book_to_skill.py | 654 | def test_fullwidth_multi_digit_chapter(self): | CODE |
| LOW⚡ | tests/test_book_to_skill.py | 659 | def test_chinese_di_n_jiang_lecture(self): | CODE |
| LOW⚡ | tests/test_book_to_skill.py | 664 | def test_markdown_cjk_ordinal_heading(self): | CODE |
| LOW⚡ | tests/test_book_to_skill.py | 669 | def test_markdown_di_n_jiang_heading(self): | CODE |
| LOW⚡ | tests/test_book_to_skill.py | 673 | def test_chinese_dedups_toc_and_body(self): | CODE |
| LOW⚡ | tests/test_book_to_skill.py | 678 | def test_cjk_detection_does_not_affect_latin(self): | CODE |
| LOW⚡ | tests/test_book_to_skill.py | 682 | def test_markdown_atx_chapters(self): | CODE |
| LOW⚡ | tests/test_book_to_skill.py | 690 | def test_asciidoc_section_headings(self): | CODE |
| LOW⚡ | tests/test_book_to_skill.py | 694 | def test_asciidoc_deeper_levels(self): | CODE |
| LOW⚡ | tests/test_book_to_skill.py | 699 | def test_markdown_prefixed_chapter_word(self): | CODE |
| LOW⚡ | tests/test_book_to_skill.py | 705 | def test_headings_inside_code_fence_are_ignored(self): | CODE |
| LOW⚡ | tests/test_book_to_skill.py | 709 | def test_plain_prose_has_no_structural_chapters(self): | CODE |
| LOW⚡ | tests/test_book_to_skill.py | 714 | def test_numeric_chapters_win_over_markdown_subsections(self): | CODE |
| LOW⚡ | tests/test_book_to_skill.py | 720 | def test_chinese_numeral_parsing(self): | CODE |
| LOW⚡ | tests/test_book_to_skill.py | 744 | def test_german_kapitel_with_title(self): | CODE |
| LOW⚡ | tests/test_book_to_skill.py | 748 | def test_european_lowercase_cross_reference_not_chapter(self): | CODE |
| LOW⚡ | tests/test_book_to_skill.py | 754 | def test_german_kapitel_umlaut_title(self): | CODE |
| LOW⚡ | tests/test_book_to_skill.py | 759 | def test_roman_heading_umlaut_title(self): | CODE |
| LOW⚡ | tests/test_book_to_skill.py | 764 | def test_setext_rst_equals_three_sections(self): | CODE |
| LOW⚡ | tests/test_book_to_skill.py | 770 | def test_setext_rst_dash_two_sections(self): | CODE |
| LOW⚡ | tests/test_book_to_skill.py | 778 | def test_setext_equals_top_level_wins_over_dash(self): | CODE |
| LOW⚡ | tests/test_book_to_skill.py | 783 | def test_setext_thematic_break_under_paragraph_not_heading(self): | CODE |
| LOW⚡ | tests/test_book_to_skill.py | 787 | def test_setext_horizontal_rule_with_blank_above_not_heading(self): | CODE |
| 48 more matches not shown… | ||||
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| MEDIUM⚡ | tests/test_book_to_skill.py | 218 | # ═══════════════════════════════════════════════════════════════════════════ | COMMENT |
| MEDIUM⚡ | tests/test_book_to_skill.py | 220 | # ═══════════════════════════════════════════════════════════════════════════ | COMMENT |
| MEDIUM⚡ | tests/test_book_to_skill.py | 277 | # ═══════════════════════════════════════════════════════════════════════════ | COMMENT |
| MEDIUM⚡ | tests/test_book_to_skill.py | 279 | # ═══════════════════════════════════════════════════════════════════════════ | COMMENT |
| MEDIUM⚡ | tests/test_book_to_skill.py | 375 | # ═══════════════════════════════════════════════════════════════════════════ | COMMENT |
| MEDIUM⚡ | tests/test_book_to_skill.py | 377 | # ═══════════════════════════════════════════════════════════════════════════ | COMMENT |
| MEDIUM⚡ | tests/test_book_to_skill.py | 444 | # ═══════════════════════════════════════════════════════════════════════════ | COMMENT |
| MEDIUM⚡ | tests/test_book_to_skill.py | 446 | # ═══════════════════════════════════════════════════════════════════════════ | COMMENT |
| MEDIUM⚡ | tests/test_book_to_skill.py | 642 | # ── Chinese (CJK) chapter headings ────────────────────────────────────── | COMMENT |
| MEDIUM⚡ | tests/test_book_to_skill.py | 969 | # --------------------------------------------------------------------------- | COMMENT |
| MEDIUM⚡ | tests/test_book_to_skill.py | 971 | # --------------------------------------------------------------------------- | COMMENT |
| MEDIUM | tests/test_book_to_skill.py | 20 | # --------------------------------------------------------------------------- | COMMENT |
| MEDIUM | tests/test_book_to_skill.py | 22 | # --------------------------------------------------------------------------- | COMMENT |
| MEDIUM | tests/test_book_to_skill.py | 43 | # ═══════════════════════════════════════════════════════════════════════════ | COMMENT |
| MEDIUM | tests/test_book_to_skill.py | 45 | # ═══════════════════════════════════════════════════════════════════════════ | COMMENT |
| MEDIUM | tests/test_book_to_skill.py | 495 | # ═══════════════════════════════════════════════════════════════════════════ | COMMENT |
| MEDIUM | tests/test_book_to_skill.py | 497 | # ═══════════════════════════════════════════════════════════════════════════ | COMMENT |
| MEDIUM⚡ | tests/test_book_to_skill.py | 173 | # ═══════════════════════════════════════════════════════════════════════════ | STRING |
| MEDIUM⚡ | tests/test_book_to_skill.py | 175 | # ═══════════════════════════════════════════════════════════════════════════ | STRING |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | tools/discovery_tax.py | 54 | except Exception: | CODE |
| LOW | tools/discovery_tax.py | 62 | except Exception: | CODE |
| MEDIUM | tools/discovery_tax.py | 58 | def token_method() -> str: | CODE |
| LOW | book_to_skill/utils.py | 554 | except Exception: | CODE |
| LOW | book_to_skill/dependencies.py | 94 | except Exception as exc: | CODE |
| LOW | book_to_skill/parsers/docx.py | 21 | except Exception as e: | CODE |
| LOW | book_to_skill/parsers/docx.py | 66 | except Exception as e: | CODE |
| LOW | book_to_skill/parsers/docx.py | 91 | except Exception as e: | CODE |
| LOW | book_to_skill/parsers/calibre.py | 24 | except Exception as e: | CODE |
| LOW | book_to_skill/parsers/pdf.py | 20 | except Exception as e: | CODE |
| LOW | book_to_skill/parsers/pdf.py | 34 | except Exception: | CODE |
| LOW | book_to_skill/parsers/pdf.py | 39 | except Exception as e: | CODE |
| LOW | book_to_skill/parsers/pdf.py | 50 | except Exception as e: | CODE |
| LOW | book_to_skill/parsers/pdf.py | 76 | except Exception as e: | CODE |
| LOW | book_to_skill/parsers/pdf.py | 92 | except Exception: | CODE |
| LOW | book_to_skill/parsers/pdf.py | 99 | except Exception: | CODE |
| LOW | book_to_skill/parsers/rtf.py | 44 | except Exception as e: | CODE |
| LOW | book_to_skill/parsers/text.py | 19 | except Exception as e: | CODE |
| LOW | book_to_skill/parsers/epub.py | 24 | except Exception as e: | CODE |
| LOW | book_to_skill/parsers/epub.py | 106 | except Exception: | CODE |
| LOW | book_to_skill/parsers/epub.py | 109 | except Exception as e: | CODE |
| LOW | book_to_skill/parsers/epub.py | 123 | except Exception: | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| MEDIUM⚡ | tests/test_book_to_skill.py | 298 | # Create a valid text file | COMMENT |
| MEDIUM⚡ | tests/test_book_to_skill.py | 300 | # Create a file that will fail (unsupported extension, garbage bytes) | COMMENT |
| MEDIUM⚡ | tests/test_book_to_skill.py | 869 | # Create a malicious DOCX | COMMENT |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | tools/discovery_tax.py | 31 | CODE | |
| LOW | book_to_skill/__init__.py | 1 | CODE | |
| LOW | book_to_skill/__init__.py | 1 | CODE | |
| LOW | book_to_skill/__init__.py | 1 | CODE | |
| LOW | book_to_skill/__init__.py | 2 | CODE | |
| LOW | book_to_skill/utils.py | 1 | CODE | |
| LOW | book_to_skill/dependencies.py | 1 | CODE | |
| LOW | book_to_skill/parsers/docx.py | 1 | CODE | |
| LOW | book_to_skill/parsers/calibre.py | 1 | CODE | |
| LOW | book_to_skill/parsers/html.py | 1 | CODE | |
| LOW | book_to_skill/parsers/pdf.py | 1 | CODE | |
| LOW | book_to_skill/parsers/text.py | 1 | CODE | |
| LOW | book_to_skill/parsers/epub.py | 1 | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | tools/validate_skill.py | 102 | CODE | |
| LOW | book_to_skill/utils.py | 286 | CODE | |
| LOW | book_to_skill/utils.py | 321 | CODE | |
| LOW | book_to_skill/utils.py | 369 | CODE | |
| LOW | book_to_skill/parsers/docx.py | 8 | CODE | |
| LOW | book_to_skill/parsers/docx.py | 26 | CODE | |
| LOW | book_to_skill/parsers/docx.py | 71 | CODE | |
| LOW | book_to_skill/parsers/docx.py | 36 | CODE | |
| LOW | book_to_skill/parsers/pdf.py | 25 | CODE | |
| LOW | book_to_skill/parsers/pdf.py | 81 | CODE | |
| LOW | book_to_skill/parsers/epub.py | 49 | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | book_to_skill/utils.py | 330 | # Check if it has glob wildcards | COMMENT |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | book_to_skill/__init__.py | 4 | __all__ = ["resolve_input_files", "extract_single_file", "main", "ExtractionError"] | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | book_to_skill/utils.py | 61 | ) | COMMENT |