{"id":51339665,"url":"https://github.com/semcod/todocs","last_synced_at":"2026-07-02T06:04:47.685Z","repository":{"id":342959656,"uuid":"1175772830","full_name":"semcod/todocs","owner":"semcod","description":"Static-analysis documentation generator for project portfolios — WordPress-ready markdown articles without LLM","archived":false,"fork":false,"pushed_at":"2026-06-28T23:21:07.000Z","size":3023,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-29T01:13:10.309Z","etag":null,"topics":["code-metrics","documentation","llm","markdown","python","semcod","static-analysis","wordpress"],"latest_commit_sha":null,"homepage":"https://semcod.github.io/todocs/","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/semcod.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"docs/CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-03-08T06:26:58.000Z","updated_at":"2026-06-28T23:21:29.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/semcod/todocs","commit_stats":null,"previous_names":["wronai/todocs","semcod/todocs"],"tags_count":14,"template":false,"template_full_name":null,"purl":"pkg:github/semcod/todocs","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/semcod%2Ftodocs","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/semcod%2Ftodocs/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/semcod%2Ftodocs/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/semcod%2Ftodocs/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/semcod","download_url":"https://codeload.github.com/semcod/todocs/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/semcod%2Ftodocs/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":35035006,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-07-02T02:00:06.368Z","response_time":173,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["code-metrics","documentation","llm","markdown","python","semcod","static-analysis","wordpress"],"created_at":"2026-07-02T06:04:46.688Z","updated_at":"2026-07-02T06:04:47.673Z","avatar_url":"https://github.com/semcod.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# todocs\n\n[![Version](https://img.shields.io/badge/version-0.1.14-blue)](https://github.com/wronai/todocs)\n[![Python](https://img.shields.io/badge/python-3.10+-3776AB)](https://python.org)\n[![License: Apache-2.0](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)\n[![Tests](https://img.shields.io/badge/tests-63%20passed-brightgreen)](tests/)\n[![Coverage](https://img.shields.io/badge/coverage-88%25-green)](tests/)\n\n\n## AI Cost Tracking\n\n![AI Cost](https://img.shields.io/badge/AI%20Cost-$2.55-green) ![AI Model](https://img.shields.io/badge/AI%20Model-openrouter%2Fqwen%2Fqwen3-coder-next-lightgrey)\n\nThis project uses AI-generated code. Total cost: **$2.5500** with **17** AI commits.\n\nGenerated on 2026-06-29 using [openrouter/qwen/qwen3-coder-next](https://openrouter.ai/models/openrouter/qwen/qwen3-coder-next)\n\n---\n\nStatic-analysis documentation generator for project portfolios — WordPress-ready markdown articles **without LLM**.\n\n## What it does\n\n`todocs` scans a directory of projects and for each one produces a detailed status article by:\n\n1. **Extracting metadata** from `pyproject.toml`, `setup.py`, `setup.cfg`, `package.json`\n2. **Analyzing code** using Python AST + radon for cyclomatic complexity, maintainability index, and hotspot detection\n3. **Building import graphs** — module dependency analysis, cycle detection, fan-in/fan-out hub identification\n4. **Detecting API surface** — CLI commands (Click/Typer), REST endpoints (FastAPI/Flask), public classes/functions\n5. **Parsing README** sections (description, installation, usage, features, architecture)\n6. **Parsing CHANGELOG** for recent version entries with category extraction\n7. **Parsing .toon files** — code maps, analysis data, flow pipelines (wronai ecosystem)\n8. **Extracting build targets** from Makefile and Taskfile.yml with descriptions\n9. **Analyzing Docker** infrastructure — Dockerfile base images, docker-compose services, ports, dependencies\n10. **Detecting tech stack** — languages, frameworks, build tools, CI/CD, test frameworks\n11. **Scoring maturity** (0-100, A+ to F grade) based on 16 quality indicators\n12. **Generating WordPress-compatible markdown** with YAML frontmatter, badges, tables, and metrics\n13. **Generating cross-project** comparison, category summaries, and health reports\n\nNo API keys. No LLM calls. Pure static analysis + AST + NLP regex parsing.\n\n## Installation\n\n```bash\npip install todocs\n```\n\nOr from source:\n\n```bash\ngit clone https://github.com/wronai/todocs.git\ncd todocs\npip install -e \".[dev]\"\n```\n\n## CLI Usage\n\n### Generate articles for all projects\n\n```bash\ntodocs generate /path/to/organization --output articles/ --org-name WronAI\n```\n\n### Inspect a single project\n\n```bash\ntodocs inspect /path/to/project\ntodocs inspect /path/to/project --format json\ntodocs inspect /path/to/project --format markdown -o report.md\n```\n\n### Cross-project comparison\n\n```bash\ntodocs compare /path/to/org -o comparison.md\n```\n\n### Organization health report\n\n```bash\ntodocs health /path/to/org -o health-report.md\n```\n\n### Full options\n\n```bash\ntodocs generate /path/to/org \\\n  --output articles/ \\\n  --org-name \"WronAI\" \\\n  --org-url \"https://github.com/wronai\" \\\n  --exclude venv --exclude node_modules \\\n  --json-report report.json \\\n  --verbose\n```\n\n### Controlling Scan Depth (max_depth)\n\nBy default, todocs scans **3 levels deep** into project directories. This prevents scanning deep dependency folders while capturing all relevant source code.\n\n#### Adjusting Depth via Python API\n\n```python\nfrom todocs import scan_project, scan_organization\n\n# Scan only top-level files (depth = 1)\nprofile = scan_project(\"/path/to/project\", max_depth=1)\n\n# Scan deeper (depth = 5) for large monorepos\nprofile = scan_project(\"/path/to/project\", max_depth=5)\n\n# Apply to organization scan\nprofiles = scan_organization(\"/path/to/org\", max_depth=4)\n```\n\n#### How Depth Works\n\n```\nproject_root/          # depth 0 (always scanned)\n├── src/               # depth 1 (always scanned)\n│   ├── core/          # depth 2 (scanned with default max_depth=3)\n│   │   ├── models.py  # depth 3 (scanned with default)\n│   │   └── utils/     # depth 3 (scanned with default)\n│   │       └── helper.py  # depth 4 (skipped with default, scanned with max_depth=4+)\n│   └── api/           # depth 2\n├── tests/             # depth 1\n└── vendor/            # depth 1 (skipped — in skip list)\n    └── deep_lib/      # depth 2 (skipped — in skip list)\n```\n\n#### What Gets Skipped Automatically\n\nRegardless of depth, these are always excluded:\n- `.git/`, `__pycache__/`, `.venv/`, `venv/`, `node_modules/`\n- `dist/`, `build/`, `target/`, `.eggs/`\n- Paths matching your `.gitignore` patterns\n- Hidden directories (starting with `.`)\n\n#### CLI Depth Control\n\nComing in v0.2.0: `--max-depth` CLI flag\n```bash\ntodocs generate /path/to/org --max-depth 5\n```\n\n## Output Structure\n\n```\narticles/\n├── _index.md                        # Portfolio overview\n├── _comparison.md                   # Cross-project comparison tables\n├── _health-report.md                # Organization health report\n├── _category-llm-and-ai-agents.md   # Per-category summaries\n├── allama.md                        # Individual project articles\n├── broxeen.md\n├── weekly.md\n└── ...\n```\n\n## Python API\n\n```python\nfrom todocs import scan_organization, generate_articles\n\nprofiles = scan_organization(\"/path/to/org\")\narticles = generate_articles(profiles, \"articles/\", org_name=\"WronAI\")\n```\n\n### Using individual analyzers\n\n```python\nfrom todocs.analyzers.import_graph import ImportGraphAnalyzer\nfrom todocs.analyzers.api_surface import APISurfaceAnalyzer\nfrom todocs.extractors.toon_parser import ToonParser\n\nig = ImportGraphAnalyzer(\"/path/to/project\")\ngraph = ig.build_graph()\nhubs = ig.get_hub_modules(top_n=5)\n\napi = APISurfaceAnalyzer(\"/path/to/project\")\nsurface = api.analyze()\n\ntoon = ToonParser(\"/path/to/project\")\ndata = toon.parse_all()\n```\n\n## Architecture\n\n```\ntodocs/\n├── analyzers/           # AST + radon: structure, metrics, imports, API, maturity\n│   ├── api_surface.py   # APISurfaceAnalyzer with extracted symbol scanners\n│   ├── code_metrics.py  # CodeMetricsAnalyzer with radon integration\n│   ├── dependencies.py  # DependencyAnalyzer for runtime/dev deps\n│   ├── import_graph.py  # ImportGraphAnalyzer with cycle detection\n│   ├── maturity.py      # MaturityScorer with 16 quality indicators\n│   └── structure.py     # StructureAnalyzer with tech stack detection\n├── extractors/          # Parsers: metadata, README, CHANGELOG, TOON, Makefile, Docker\n│   ├── changelog_parser.py\n│   ├── docker_parser.py\n│   ├── makefile_parser.py\n│   ├── metadata.py\n│   ├── readme_parser.py\n│   └── toon_parser.py   # TOON file parser (refactored into section parsers)\n├── generators/          # Article + comparison/category/health generators\n│   ├── article.py              # Main ArticleGenerator (146L, refactored)\n│   ├── article_sections.py     # 13 section rendering functions\n│   └── comparison.py           # ComparisonGenerator for cross-project analysis\n├── cli.py               # Click CLI: generate/inspect/compare/health\n├── core.py              # Data models + orchestration pipeline\nexamples/                # Usage examples (see below)\ntests/                   # 63 tests, 88% coverage\n```\n\n## Project Status (v0.1.3)\n\nAfter refactoring (2026-03-08):\n\n| Metric | Value |\n|--------|-------|\n| Source files | 24 (+1 new: article_sections.py) |\n| Source lines | ~4,100 |\n| Test files | 5 |\n| Test lines | 1,560 |\n| Test coverage | 88% (63 tests passing) |\n| High-CC functions (≥15) | Reduced from 15 to 7 |\n| Maturity Grade | B+ (71/100) |\n\n**Refactoring highlights:**\n- `article.py`: 560L → 146L (split into `article_sections.py`)\n- `custom_analysis.py` main(): CC 33 → 8 (extracted 6 helpers)\n- `_scan_public_symbols`: CC 27 → 8 (extracted 4 helpers)\n- `parse_analysis` \u0026 `parse_map`: CC 25/24 → ~6 (section parsers)\n- `detect_tech_stack`: CC 19 → 6 (6 detection methods)\n- `_parse_makefile`: CC 19 → 8 (extracted 4 helpers)\n- `_parse_sections`: CC 22 → 8 (description + heading extractors)\n\n## Examples\n\n### Basic: Scan single project\n```python\nfrom todocs.core import scan_project\n\nprofile = scan_project(\"/path/to/project\")\nprint(f\"{profile.name}: {profile.maturity.grade} ({profile.code_stats.source_lines} lines)\")\n```\n\n### Using refactored article sections directly\n```python\nfrom todocs.generators.article_sections import (\n    render_metrics, render_tech_stack, render_maturity\n)\nfrom todocs.core import scan_project\n\nprofile = scan_project(\"/path/to/project\")\n\n# Generate individual sections\nmetrics_md = render_metrics(profile)\ntech_md = render_tech_stack(profile)\nmaturity_md = render_maturity(profile)\n\nprint(metrics_md)\n```\n\n### Custom API surface analysis (using refactored helpers)\n```python\nfrom todocs.analyzers.api_surface import APISurfaceAnalyzer\n\napi = APISurfaceAnalyzer(\"/path/to/project\")\nsurface = api.analyze()\n\n# Access specific components\nprint(f\"CLI commands: {len(surface['cli_commands'])}\")\nprint(f\"Public classes: {len(surface['public_classes'])}\")\nprint(f\"REST endpoints: {len(surface['rest_endpoints'])}\")\nprint(f\"Entry points: {surface['entry_points']}\")\n```\n\n### Organization health report\n```python\nfrom todocs import scan_organization\nfrom todocs.generators.comparison import ComparisonGenerator\nfrom pathlib import Path\n\n# Scan organization\nprofiles = scan_organization(\"/path/to/org\")\n\n# Generate health report\ngen = ComparisonGenerator()\nreport_path = Path(\"health-report.md\")\nreport_path.write_text(gen.generate_health_report(profiles, org_name=\"WronAI\"))\n```\n\n### Category-based analysis\n```python\nfrom todocs import scan_organization\nfrom todocs.generators.comparison import ComparisonGenerator\n\nprofiles = scan_organization(\"/path/to/org\")\ngen = ComparisonGenerator()\n\n# Generate articles grouped by category\ncategory_articles = gen.generate_category_articles(\n    profiles,\n    output_dir=\"articles/\",\n    org_name=\"WronAI\"\n)\n\nfor path in category_articles:\n    print(f\"Generated: {path}\")\n```\n\n### See also: `examples/` directory\n- `scan_single.py` — Basic single project scan\n- `scan_org.py` — Organization-wide scan  \n- `custom_analysis.py` — Using individual analyzers (refactored with 6 helpers)\n- `article_sections_demo.py` — New: Direct section rendering\n- `api_surface_deep.py` — New: Deep API analysis with refactored helpers\n\n## License\n\nLicensed under Apache-2.0.\n## Author\n\nTom Sapletta\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsemcod%2Ftodocs","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsemcod%2Ftodocs","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsemcod%2Ftodocs/lists"}