wyattowalsh docs
DevelopmentArchitecture

Architecture

Codebase layout, module dependencies, and design decisions.

Directory layout

wyattowalsh/
├── scripts/
│   ├── cli/                    # Typer CLI package
│   │   ├── _app.py             # Root app, --version, sub-app registration
│   │   ├── generate.py         # Generate subcommands (banner, qr, word-cloud, …)
│   │   ├── config_cmd.py       # Config subcommands (view, save, generate-default)
│   │   ├── settings_cmd.py     # show-settings command
│   │   └── dev.py              # Dev tools (format, lint, test, clean, docs)
│   ├── config.py               # Pydantic config models + load/save
│   ├── utils.py                # Logger (Loguru), Rich console
│   ├── banner.py               # SVG banner (~1730 lines)
│   ├── banner_patterns.py      # PatternType enum (zero-dep)
│   ├── qr.py                   # QR code generator
│   ├── word_clouds.py          # Word cloud orchestrator
│   ├── word_cloud_renderers.py # Pure-SVG rendering backends
│   ├── techs.py                # Technology parser
│   ├── readme_sections.py      # README assembler (~1548 lines)
│   ├── readme_svg.py           # SVG rendering helpers for README cards
│   ├── skills.py               # Skills badge generator
│   ├── generative.py           # Generative art entry
│   ├── animated_art.py         # Animated art CLI compatibility shim over scripts/art/*
│   ├── fetch_metrics.py        # GitHub metrics fetcher
│   ├── fetch_history.py        # GitHub history fetcher
│   ├── _github_http.py         # Shared GitHub HTTP helpers
│   └── art/
│       ├── shared.py           # seed_hash, _hex_slice, Noise2D
│       ├── _dev_profiles.py    # Mock profiles for local testing
│       ├── ink_garden.py       # Ink garden SVG (~2027 lines)
│       ├── topography.py       # Topographic contour art (~1170 lines)
│       └── animate.py          # Animation primitives
├── tests/
│   └── fixtures/
│       └── ink_garden/         # Golden SVG files
├── .github/
│   ├── assets/
│   │   ├── img/                # Generated SVGs/PNGs
│   │   ├── topics.md           # Starred-repo topics (auto-updated)
│   │   └── languages.md        # Starred-repo languages (auto-updated)
│   └── workflows/
│       └── profile-updater.yml # Main CI workflow
├── docs/                       # This Fumadocs site
├── config.yaml                 # ProjectConfig (edit this)
└── skills.yaml                 # Skills badge definitions

Module dependency graph

cli/
  ├── _app.py
  │   ├── generate.py (generate sub-app)
  │   ├── config_cmd.py (config sub-app)
  │   ├── dev.py (dev sub-app)
  │   └── settings_cmd.py (show-settings)
  └── generate.py
      ├── banner.py (lazy — imported inside command body)
      ├── qr.py (lazy)
      ├── word_clouds.py (lazy)
      ├── techs.py (lazy)
      ├── readme_sections.py (lazy)
      ├── skills.py (lazy)
      ├── generative.py (lazy)
      └── animated_art.py (lazy compatibility shim over scripts/art/*)

banner.py
  ├── banner_patterns.py (PatternType)
  ├── svgwrite
  └── numpy

ink_garden.py / topography.py
  ├── art/shared.py (seed_hash, Noise2D)
  └── numpy

fetch_metrics.py + fetch_history.py
  └── _github_http.py (_headers, _graphql)

Lazy imports

All heavy domain modules (banner, qr, word_clouds, art) are lazily imported inside CLI command function bodies in cli/generate.py. This keeps import scripts.cli fast even when optional extras aren't installed.

Configuration flow

config.yaml → load_config() → ProjectConfig (Pydantic v2)

                          ┌─────────┼─────────┐
                    BannerSettings  QRSettings  WordCloudSettings ...

                    generate_banner(BannerConfig(**banner_settings))

ProjectConfig uses mode="json" serialization to produce safe-loadable YAML (avoids Python-object YAML tags from Pydantic's HttpUrl type).

Logging

All modules use get_logger(module=__name__) from scripts/utils.py — a Loguru logger with structured JSON sinks in logs/json/ and plain text in logs/text/. Both directories are gitignored. Loguru calls use keyword arguments instead of f-strings: logger.info("msg: {key}", key=value).

utils.py calls loguru_logger.remove() at module import time. Importing any scripts.* module resets all Loguru handlers. Do not add file I/O, network calls, or subprocess calls to utils.py — it is imported by every other module.

Large modules

The five largest modules are intentionally monolithic — empirical co-change analysis shows they always change together with their art siblings, not in isolation (Parnas's "change together, stay together"):

ModuleLinesRationale
art/ink_garden.py~2027Garden growth simulation
banner.py~1730Single rendering pipeline
readme_sections.py~1548One coherent assembler
readme_svg.py~1330SVG card rendering helpers
word_cloud_renderers.py~1230Tightly coupled rendering variants
art/topography.py~1170Topographic contour map art