agentleFS
lfnovolfnovo/content-core/AGENTS.md

content-core

AGENTS.md183 starsChanged 4 months agoMITView on GitHub

Library for extracting and summarizing content from URLs, files, and text. If you are an automated coding agent (harny, Claude Code in headless mode, etc.) working on this repo: This is the gate. It returns clean pass/fail in ~15s and requires no credentials or network. CI on PRs and main runs exactly these tests (tests/unit tests/integration) across Python 3.10/3.11/3.12, plus a packaging gate (.github/workflows/package.yml) that builds the wheel and exercises the library, the content-core CLI and the content-core-mcp server from…

  • Where files are headingNew AGENTS.md files have more rules about keeping secrets out of the code, and fewer about code style.

    Of the AGENTS.md files we read, the 31,439 first seen in the last six months, against the older ones: the kind of rule that gained most share, and the kind that lost most. About files like this in general, not this one, and an estimate, since a kind is a pattern in the words.

  • In this repository2 other files it gives its agents.

The file

We split this AGENTS.md into the rules it gives an agent

6 rules. About 0 are common, 3 are distinctive, and 3 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈3

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 3

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Distinctive:

    If you are an automated coding agent (harny, Claude Code in headless mode, etc.) working on this repo:

    line 26
  2. Distinctive:

    If you believe a task genuinely requires lint or type cleanup, do it as a *separate task* with explicit scope, not as a side effect of an …

    line 48
  3. Very specific:

    Avoid touching extraction.py (orchestrator) or config.py unless the task explicitly targets routing or configuration.

    line 53
  4. Very specific:

    Error handling: Use custom exceptions from common/exceptions.py

    line 144
  5. Distinctive:

    When you change a specific processor or module, run only the relevant tests for fast feedback:

    line 162
  6. Very specific:

    Follow .maintainer/release/runbook.md for candidate validation and publication.

    line 228

≈ “Common” is an estimate: at least 1 in 5 of the 40,246 AGENTS.md files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
AGENTS.md

Content Core

Library for extracting and summarizing content from URLs, files, and text.

Commands

  • Install dependencies: uv sync --group dev
  • Run tests: make test (unit + integration) or uv run pytest -v
  • Run single test: uv run pytest -k "test_name"
  • Run e2e tests: make test-e2e (requires network + API keys)
  • Run all tests: make test-all
  • Linting: make ruff (runs ruff check . --fix)
  • Build package: uv build

CLI

content-core extract <source> [--format text|json] [--engine ENGINE]
content-core summarize [content] [--context CONTEXT]
content-core mcp
content-core config list|set|delete

For Automated Agents

If you are an automated coding agent (harny, Claude Code in headless mode, etc.) working on this repo:

Install command

uv sync --group dev

Validator command

uv run pytest tests/unit tests/integration -v

This is the gate. It returns clean pass/fail in ~15s and requires no credentials or network. CI on PRs and main runs exactly these tests (tests/unit tests/integration) across Python 3.10/3.11/3.12, plus a packaging gate (.github/workflows/package.yml) that builds the wheel and exercises the library, the content-core CLI and the content-core-mcp server from the built artifact. The packaging gate needs no credentials either, but it is not reproducible from a plain pytest run — if you touch packaging metadata (pyproject.toml, entry points, package data), expect it to be the check that catches you. For targeted feedback during iteration, use uv run pytest -k "<keyword>" per the table in the Testing section below.

Do NOT use as gates

  • ruff check — available via make ruff but NOT enforced in CI. Including it as a validator gate would surface unaudited pre-existing findings unrelated to your task.
  • mypy — not enforced in CI. No baseline configured. Same risk.
  • make test-e2e / make test-e2e-heavy — require network access, API keys for LLM/STT providers, and large model downloads. Will fail in a sandboxed worktree.

If you believe a task genuinely requires lint or type cleanup, do it as a separate task with explicit scope, not as a side effect of an unrelated change.

Scope guidance

  • Single processor changes (processors/url/*.py, processors/document/*.py, processors/media/*.py) are the natural unit of work — keep changes confined to one file plus its matching test in tests/unit/.
  • Avoid touching extraction.py (orchestrator) or config.py unless the task explicitly targets routing or configuration.
  • New processors follow the function contract documented in ARCHITECTURE.md ("Two-layer contract"): a module-level async def extract_<fmt>_file(file_path, config) -> ExtractionOutput that raises on failure, with per-unit degradation in the inner parsing layer.

Codebase Structure

src/content_core/
├── __init__.py              # Public API: extract_content, summarize, ContentCoreConfig
├── config.py                # ContentCoreConfig (pydantic-settings, env prefix CCORE_)
├── extraction.py            # Main orchestrator — routes input to processors
├── models.py                # ModelFactory for Esperanto LLM/STT models
├── cli.py                   # Click CLI: extract, summarize, mcp subcommands
├── logging.py               # Loguru configuration
├── templated_message.py     # LLM prompt execution with Jinja templates
│
├── common/
│   ├── exceptions.py        # Exception hierarchy (ContentCoreError base)
│   ├── retry.py             # Self-contained retry decorators with tenacity
│   ├── state.py             # ExtractionInput, ExtractionOutput data models
│   └── types.py             # Type aliases (DocumentEngine, UrlEngine)
│
├── content/
│   ├── summary/core.py      # Content summarization via LLM
│   └── identification/      # File type detection (pure Python)
│
├── processors/
│   ├── text.py              # Plain text + HTML-to-markdown (also .html/.htm files, <title> extraction)
│   ├── url/                 # URL extraction engines
│   │   ├── __init__.py      # Engine router + fallback chain
│   │   ├── bs4.py           # BeautifulSoup + readability
│   │   ├── jina.py          # Jina Reader API
│   │   ├── firecrawl.py     # Firecrawl SDK
│   │   ├── reddit.py        # Reddit posts via public JSON endpoint
│   │   ├── youtube.py       # YouTube transcript extraction
│   │   └── crawl4ai.py      # Crawl4AI browser automation
│   ├── document/            # Document extraction
│   │   ├── __init__.py      # Document type router
│   │   ├── pdf.py           # PDF via pdfplumber
│   │   ├── epub.py          # EPUB via fast-ebook
│   │   ├── docx.py          # python-docx
│   │   ├── pptx.py          # python-pptx
│   │   ├── xlsx.py          # openpyxl
│   │   ├── odf.py           # ODT/ODS/ODP via odfpy
│   │   └── docling.py       # Optional Docling integration
│   └── media/               # Audio/video processing
│       ├── __init__.py      # Video→audio pipeline
│       ├── audio.py         # Transcription via Esperanto STT
│       └── video.py         # Video-to-audio extraction
│
├── mcp/
│   └── server.py            # MCP server: extract_content, summarize_content
│
└── tools/                   # Optional LangChain tool wrappers (requires langchain-core)
    ├── extract.py
    └── summarize.py

Architecture

Data flow: Input -> extraction.py orchestrator -> Processor -> ExtractionOutput

  1. ExtractionInput received (content, URL, or file path)
  2. extraction.py identifies source type and routes to the appropriate processor
  3. Processor extracts content and returns ExtractionOutput

Key patterns:

  • Plain async Python orchestration (no LangGraph)
  • Configuration via ContentCoreConfig (pydantic-settings, env vars with CCORE_ prefix)
  • Processors are async functions taking simple params + config, returning ExtractionOutput
  • Retry decorators handle transient failures for network/API operations

Configuration

Via constructor or environment variables (CCORE_ prefix):

from content_core import ContentCoreConfig
config = ContentCoreConfig(url_engine="firecrawl", audio_concurrency=5)

Key settings: url_engine, document_engine, audio_provider, audio_model, firecrawl_api_url, youtube_languages, youtube_cookies_file, youtube_proxy, llm_provider, llm_model, docling_ocr, docling_formulas, docling_vision

Docling enrichment flags (docling_ocr, docling_formulas, docling_vision) control OCR, formula extraction, and image/chart processing when document_engine="docling". These are also exposed as CLI flags (--formulas, --pictures, --no-ocr) and MCP parameters.

docling_vision_url (plus docling_vision_model, _api_key, _prompt, _max_tokens, _timeout, _token_param) sends Docling's image descriptions to an OpenAI-compatible endpoint instead of the inline model. It only applies when docling_vision is on; without the URL, nothing is sent over the network.

Priority: constructor args > env vars (CCORE_*) > config file (~/.content-core/config.toml) > defaults

Code Style

  • Formatting: PEP 8, enforced by ruff
  • Error handling: Use custom exceptions from common/exceptions.py
  • Tests: Three tiers — see below

Testing

When to use each command

CommandWhen to useSpeed
make testAfter any code change — default validation~12s
uv run pytest -k "keyword"After changing a specific area (see table below)<2s
make test-e2eBefore a release — validates real APIs and network~30s
make test-e2e-heavyDocling e2e tests — downloads large modelsminutes
make test-e2e-allAll e2e tests (basic + heavy)minutes
make test-allFull validation, all tiersvaries

Targeted test keywords by area

When you change a specific processor or module, run only the relevant tests for fast feedback:

Area changedTest command
extraction.py (orchestrator/routing)uv run pytest -k "routing"
config.pyuv run pytest -k "config"
processors/url/ (any URL engine)uv run pytest -k "url_engine"
processors/url/youtube.pyuv run pytest -k "youtube"
processors/url/reddit.pyuv run pytest -k "reddit"
processors/document/pdf.pyuv run pytest -k "pdf"
processors/document/docx.py or pptx.py or xlsx.py or odf.pyuv run pytest -k "office"
processors/document/docling.pyuv run pytest -k "docling"
processors/text.pyuv run pytest -k "text_processing or html_file"
processors/media/audio.pyuv run pytest -k "audio or media_pipeline"
processors/media/video.pyuv run pytest -k "media_pipeline"
mcp/server.pyuv run pytest -k "mcp"
cli.pyuv run pytest -k "cli"
common/retry.pyuv run pytest -k "retry"
common/exceptions.pyuv run pytest -k "exceptions"
content/identification/uv run pytest -k "file_detector"
common/state.py (models)uv run pytest -k "models"

Test structure

tests/
├── unit/              # Fast, mocked, no I/O or network (~263 tests)
│   ├── test_routing.py            # Orchestrator: input → correct processor
│   ├── test_config_v2.py          # ContentCoreConfig defaults, env vars, validation
│   ├── test_url_engine_select.py  # URL engine: auto/firecrawl/jina/simple
│   ├── test_youtube_parsing.py    # YouTube ID extraction, transcript fallbacks
│   ├── test_reddit_extraction.py  # Reddit URL detection, JSON parsing, formatting
│   ├── test_pdf_extraction.py     # PDF text cleaning, extraction with mocked pdfplumber
│   ├── test_pdf_helpers.py        # Formula detection, table conversion helpers
│   ├── test_epub_extraction.py    # EPUB extraction with mocked fast-ebook
│   ├── test_office_extraction.py  # DOCX/PPTX/XLSX/ODT/ODS/ODP routing and extraction
│   ├── test_docling_extraction.py # Docling output formats with mocked converter
│   ├── test_text_processing.py    # HTML detection, markdown conversion, <title>/<head> handling
│   ├── test_html_file_extraction.py # Local .html file → markdown via text processor
│   ├── test_media_pipeline.py     # Audio transcription, video pipeline, stream selection
│   ├── test_audio_concurrency.py  # Semaphore, ordering, concurrency limits
│   ├── test_mcp_v2.py             # MCP tools: extract + summarize
│   ├── test_cli.py                # CLI: _build_input, commands, config subcommands
│   ├── test_config_file.py        # TOML config file: read/write, set/delete, precedence
│   ├── test_models_v2.py          # ExtractionInput/Output data models
│   ├── test_retry.py              # Retry decorators, exception classification
│   ├── test_exceptions.py         # Exception taxonomy + public exports
│   └── test_file_detector*.py     # MIME detection, performance, edge cases
│
├── integration/       # Local files, no network (~22 tests)
│   ├── test_extraction.py   # Real file extraction (PDF, DOCX, PPTX, XLSX, ODT/ODS/ODP, EPUB, HTML, etc.)
│   └── test_cli_v2.py       # CLI subcommands via CliRunner with real extraction
│
└── e2e/
    ├── [e2e] test_url_engines.py   # Firecrawl, Jina, Crawl4AI, BS4 with real URLs
    ├── [e2e] test_youtube.py       # Real YouTube transcript extraction
    ├── [e2e] test_remote.py        # Remote PDF download from arxiv
    ├── [e2e] test_media.py         # Audio/video transcription via STT API
    └── [e2e_heavy] test_docling.py # Docling extraction with enrichment flags

Maintainer workflows

Maintainer profile lives in .maintainer/; do not run release, triage or discussions workflows without it. PyPI publication is triggered by a published GitHub Release (release: published). make tag prepares the tag; make release publishes its reviewed draft release. Follow .maintainer/release/runbook.md for candidate validation and publication.

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.