{"owner":"TideDra","repo":"zotero-arxiv-daily","hasSkills":true,"totalSkillsCount":2,"totalTokensCount":1920,"categories":["claude-rule","copilot-instructions"],"hasMcp":false,"mcpConfig":null,"found":["CLAUDE.md",".github/copilot-instructions.md"],"skills":{"CLAUDE.md":"# CLAUDE.md\n\nThis file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.\n\n## Project Overview\n\nZotero-arXiv-Daily recommends new arXiv/bioRxiv/medRxiv/chemRxiv papers based on a user's Zotero library. It computes embedding similarity between new papers and the user's existing library, generates TLDRs via LLM, and delivers results by email. Designed to run as a GitHub Actions workflow at zero cost.\n\n## Commands\n\n```bash\n# Run the application\nuv run src/zotero_arxiv_daily/main.py\n\n# Run tests (excludes slow tests by default)\nuv run pytest\n\n# Run all tests including slow ones\nuv run pytest -m \"\"\n\n# Run a single test\nuv run pytest tests/test_utils.py::TestGlobMatch -v\n\n# Install/sync dependencies\nuv sync\n```\n\nNo linter or formatter is configured.\n\n## Architecture\n\nThe app follows a linear pipeline orchestrated by `Executor` (`src/zotero_arxiv_daily/executor.py`):\n\n1. **Fetch Zotero corpus** — retrieves user's library papers via pyzotero API\n2. **Filter corpus** — applies `include_path` glob patterns to select relevant collections\n3. **Retrieve new papers** — fetches from configured sources (arXiv RSS, bioRxiv/medRxiv REST API, chemRxiv via Crossref REST API)\n4. **Rerank** — scores candidates by weighted similarity to corpus (newer Zotero papers weighted higher)\n5. **Generate TLDRs + affiliations** — via OpenAI-compatible LLM API\n6. **Render + send email** — HTML email via SMTP\n\n### Plugin Systems\n\n**Retrievers** (`src/zotero_arxiv_daily/retriever/`): Register via `@register_retriever` decorator, discovered by `get_retriever_cls()`. Each retriever implements `_retrieve_raw_papers()` and `convert_to_paper()`.\n\n**Rerankers** (`src/zotero_arxiv_daily/reranker/`): Register via `@register_reranker` decorator, discovered by `get_reranker_cls()`. Two implementations: `local` (sentence-transformers) and `api` (OpenAI-compatible embeddings endpoint).\n\n### Configuration\n\nUses Hydra + OmegaConf. Config is composed from `config/base.yaml` (defaults) + `config/custom.yaml` (user overrides). Environment variables are interpolated via `${oc.env:VAR_NAME,default}` syntax. Entry point uses `@hydra.main`.\n\n### Data Classes\n\n`Paper` and `CorpusPaper` in `src/zotero_arxiv_daily/protocol.py`. `Paper` has LLM-powered methods (`generate_tldr`, `generate_affiliations`) that call the OpenAI API directly.\n\n## Testing\n\nTests marked `@pytest.mark.slow` require heavy dependencies (e.g., sentence-transformers model download) and are skipped locally by default (`addopts = \"-m 'not slow'\"` in pyproject.toml). All other tests run with pure Python stubs (no Docker containers needed).\n\n```bash\n# Run tests (excludes slow tests)\nuv run pytest\n\n# Run all tests including slow ones\nuv run pytest -m \"\"\n\n# Run with coverage\nuv run pytest --cov=src/zotero_arxiv_daily --cov-report=term-missing\n```\n\n## gstack\n\nUse the `/browse` skill from gstack for all web browsing. Never use `mcp__claude-in-chrome__*` tools.\n\nAvailable skills: `/office-hours`, `/plan-ceo-review`, `/plan-eng-review`, `/plan-design-review`, `/design-consultation`, `/design-shotgun`, `/design-html`, `/review`, `/ship`, `/land-and-deploy`, `/canary`, `/benchmark`, `/browse`, `/connect-chrome`, `/qa`, `/qa-only`, `/design-review`, `/setup-browser-cookies`, `/setup-deploy`, `/retro`, `/investigate`, `/document-release`, `/codex`, `/cso`, `/autoplan`, `/plan-devex-review`, `/devex-review`, `/careful`, `/freeze`, `/guard`, `/unfreeze`, `/gstack-upgrade`, `/learn`.\n\nIf gstack skills aren't working, run `cd .claude/skills/gstack && ./setup` to build the binary and register skills.\n\n## Git Workflow\n\n- PRs should target the `dev` branch, not `main`\n- Current development branch: `dev`\n",".github/copilot-instructions.md":"# Copilot Instructions\n\n## Project Overview\n\nZotero-arXiv-Daily recommends new arXiv/bioRxiv/medRxiv papers based on a user's Zotero library. It computes embedding similarity between new papers and the user's existing library, generates TLDRs via LLM, and delivers results by email. Designed to run as a GitHub Actions workflow at zero cost.\n\n## Commands\n\n```bash\n# Install/sync dependencies\nuv sync\n\n# Run the application\nuv run src/zotero_arxiv_daily/main.py\n\n# Run tests (excludes slow tests by default)\nuv run pytest\n\n# Run all tests including slow ones\nuv run pytest -m \"\"\n\n# Run a single test\nuv run pytest tests/test_utils.py::TestGlobMatch -v\n```\n\nNo linter or formatter is configured.\n\n## Architecture\n\nThe app is a linear pipeline orchestrated by `Executor` (`src/zotero_arxiv_daily/executor.py`):\n\n1. **Fetch Zotero corpus** → pyzotero API\n2. **Filter corpus** → `include_path` / `ignore_path` glob patterns\n3. **Retrieve new papers** → from configured sources (arXiv RSS, bioRxiv/medRxiv REST API)\n4. **Rerank** → weighted embedding similarity to corpus (newer Zotero papers weighted higher)\n5. **Generate TLDRs + affiliations** → OpenAI-compatible LLM API\n6. **Render + send email** → HTML email via SMTP\n\n### Plugin Systems\n\n**Retrievers** (`src/zotero_arxiv_daily/retriever/`): Register via `@register_retriever(\"name\")` decorator on a `BaseRetriever` subclass. Each retriever implements `_retrieve_raw_papers()` and `convert_to_paper()`. Discovered at runtime via `get_retriever_cls(name)` from a module-level `registered_retrievers` dict.\n\n**Rerankers** (`src/zotero_arxiv_daily/reranker/`): Register via `@register_reranker(\"name\")` decorator on a `BaseReranker` subclass. Two implementations: `local` (sentence-transformers) and `api` (OpenAI-compatible embeddings endpoint). Discovered via `get_reranker_cls(name)`.\n\nWhen adding a new retriever or reranker, follow the existing pattern: create a new file, subclass the base, apply the registration decorator, and implement the abstract methods.\n\n### Configuration\n\nUses Hydra + OmegaConf. Config composes from `config/base.yaml` (defaults with `???` placeholders for required values) + `config/custom.yaml` (user overrides). The composition order is defined in `config/default.yaml`. Environment variables are interpolated via `${oc.env:VAR_NAME,default}` syntax. Entry point uses `@hydra.main(config_name=\"default\")`.\n\n### Data Classes\n\n`Paper` and `CorpusPaper` in `src/zotero_arxiv_daily/protocol.py`. `Paper` has LLM-powered methods (`generate_tldr`, `generate_affiliations`) that call the OpenAI API directly with `tiktoken`-based token truncation.\n\n## Testing Conventions\n\n- Tests use **pytest monkeypatch + `SimpleNamespace`** for stubs — not `unittest.mock`.\n- A session-scoped Hydra config in `tests/conftest.py` is deep-copied per test via the `config` fixture.\n- Canned response factories live in `tests/canned_responses.py` (e.g., `make_stub_openai_client()`, `make_stub_zotero_client()`).\n- Tests marked `@pytest.mark.slow` require heavy dependencies (model downloads) and are excluded by default (`addopts = \"-m 'not slow'\"` in pyproject.toml).\n- Monkeypatching targets the module-level import path (e.g., `\"zotero_arxiv_daily.executor.zotero.Zotero\"`).\n\n## Coding Conventions\n\n- **Logging:** `loguru.logger` throughout — never `print()` or stdlib `logging`.\n- **Type hints:** Modern Python 3.10+ syntax (`list[Paper]`, `str | None`).\n- **Constants:** Module-level `UPPER_SNAKE_CASE`.\n- **Private methods:** Prefixed with `_` (e.g., `_retrieve_raw_papers`).\n- **Error handling:** Graceful degradation with try/except and fallback logic; log warnings rather than raising.\n- **Config injection:** All major components receive `DictConfig` at init and store it as `self.config`.\n\n## Git Workflow\n\n- PRs should target the **`dev`** branch, not `main`.\n"},"files":{"CLAUDE.md":"# CLAUDE.md\n\nThis file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.\n\n## Project Overview\n\nZotero-arXiv-Daily recommends new arXiv/bioRxiv/medRxiv/chemRxiv papers based on a user's Zotero library. It computes embedding similarity between new papers and the user's existing library, generates TLDRs via LLM, and delivers results by email. Designed to run as a GitHub Actions workflow at zero cost.\n\n## Commands\n\n```bash\n# Run the application\nuv run src/zotero_arxiv_daily/main.py\n\n# Run tests (excludes slow tests by default)\nuv run pytest\n\n# Run all tests including slow ones\nuv run pytest -m \"\"\n\n# Run a single test\nuv run pytest tests/test_utils.py::TestGlobMatch -v\n\n# Install/sync dependencies\nuv sync\n```\n\nNo linter or formatter is configured.\n\n## Architecture\n\nThe app follows a linear pipeline orchestrated by `Executor` (`src/zotero_arxiv_daily/executor.py`):\n\n1. **Fetch Zotero corpus** — retrieves user's library papers via pyzotero API\n2. **Filter corpus** — applies `include_path` glob patterns to select relevant collections\n3. **Retrieve new papers** — fetches from configured sources (arXiv RSS, bioRxiv/medRxiv REST API, chemRxiv via Crossref REST API)\n4. **Rerank** — scores candidates by weighted similarity to corpus (newer Zotero papers weighted higher)\n5. **Generate TLDRs + affiliations** — via OpenAI-compatible LLM API\n6. **Render + send email** — HTML email via SMTP\n\n### Plugin Systems\n\n**Retrievers** (`src/zotero_arxiv_daily/retriever/`): Register via `@register_retriever` decorator, discovered by `get_retriever_cls()`. Each retriever implements `_retrieve_raw_papers()` and `convert_to_paper()`.\n\n**Rerankers** (`src/zotero_arxiv_daily/reranker/`): Register via `@register_reranker` decorator, discovered by `get_reranker_cls()`. Two implementations: `local` (sentence-transformers) and `api` (OpenAI-compatible embeddings endpoint).\n\n### Configuration\n\nUses Hydra + OmegaConf. Config is composed from `config/base.yaml` (defaults) + `config/custom.yaml` (user overrides). Environment variables are interpolated via `${oc.env:VAR_NAME,default}` syntax. Entry point uses `@hydra.main`.\n\n### Data Classes\n\n`Paper` and `CorpusPaper` in `src/zotero_arxiv_daily/protocol.py`. `Paper` has LLM-powered methods (`generate_tldr`, `generate_affiliations`) that call the OpenAI API directly.\n\n## Testing\n\nTests marked `@pytest.mark.slow` require heavy dependencies (e.g., sentence-transformers model download) and are skipped locally by default (`addopts = \"-m 'not slow'\"` in pyproject.toml). All other tests run with pure Python stubs (no Docker containers needed).\n\n```bash\n# Run tests (excludes slow tests)\nuv run pytest\n\n# Run all tests including slow ones\nuv run pytest -m \"\"\n\n# Run with coverage\nuv run pytest --cov=src/zotero_arxiv_daily --cov-report=term-missing\n```\n\n## gstack\n\nUse the `/browse` skill from gstack for all web browsing. Never use `mcp__claude-in-chrome__*` tools.\n\nAvailable skills: `/office-hours`, `/plan-ceo-review`, `/plan-eng-review`, `/plan-design-review`, `/design-consultation`, `/design-shotgun`, `/design-html`, `/review`, `/ship`, `/land-and-deploy`, `/canary`, `/benchmark`, `/browse`, `/connect-chrome`, `/qa`, `/qa-only`, `/design-review`, `/setup-browser-cookies`, `/setup-deploy`, `/retro`, `/investigate`, `/document-release`, `/codex`, `/cso`, `/autoplan`, `/plan-devex-review`, `/devex-review`, `/careful`, `/freeze`, `/guard`, `/unfreeze`, `/gstack-upgrade`, `/learn`.\n\nIf gstack skills aren't working, run `cd .claude/skills/gstack && ./setup` to build the binary and register skills.\n\n## Git Workflow\n\n- PRs should target the `dev` branch, not `main`\n- Current development branch: `dev`\n",".github/copilot-instructions.md":"# Copilot Instructions\n\n## Project Overview\n\nZotero-arXiv-Daily recommends new arXiv/bioRxiv/medRxiv papers based on a user's Zotero library. It computes embedding similarity between new papers and the user's existing library, generates TLDRs via LLM, and delivers results by email. Designed to run as a GitHub Actions workflow at zero cost.\n\n## Commands\n\n```bash\n# Install/sync dependencies\nuv sync\n\n# Run the application\nuv run src/zotero_arxiv_daily/main.py\n\n# Run tests (excludes slow tests by default)\nuv run pytest\n\n# Run all tests including slow ones\nuv run pytest -m \"\"\n\n# Run a single test\nuv run pytest tests/test_utils.py::TestGlobMatch -v\n```\n\nNo linter or formatter is configured.\n\n## Architecture\n\nThe app is a linear pipeline orchestrated by `Executor` (`src/zotero_arxiv_daily/executor.py`):\n\n1. **Fetch Zotero corpus** → pyzotero API\n2. **Filter corpus** → `include_path` / `ignore_path` glob patterns\n3. **Retrieve new papers** → from configured sources (arXiv RSS, bioRxiv/medRxiv REST API)\n4. **Rerank** → weighted embedding similarity to corpus (newer Zotero papers weighted higher)\n5. **Generate TLDRs + affiliations** → OpenAI-compatible LLM API\n6. **Render + send email** → HTML email via SMTP\n\n### Plugin Systems\n\n**Retrievers** (`src/zotero_arxiv_daily/retriever/`): Register via `@register_retriever(\"name\")` decorator on a `BaseRetriever` subclass. Each retriever implements `_retrieve_raw_papers()` and `convert_to_paper()`. Discovered at runtime via `get_retriever_cls(name)` from a module-level `registered_retrievers` dict.\n\n**Rerankers** (`src/zotero_arxiv_daily/reranker/`): Register via `@register_reranker(\"name\")` decorator on a `BaseReranker` subclass. Two implementations: `local` (sentence-transformers) and `api` (OpenAI-compatible embeddings endpoint). Discovered via `get_reranker_cls(name)`.\n\nWhen adding a new retriever or reranker, follow the existing pattern: create a new file, subclass the base, apply the registration decorator, and implement the abstract methods.\n\n### Configuration\n\nUses Hydra + OmegaConf. Config composes from `config/base.yaml` (defaults with `???` placeholders for required values) + `config/custom.yaml` (user overrides). The composition order is defined in `config/default.yaml`. Environment variables are interpolated via `${oc.env:VAR_NAME,default}` syntax. Entry point uses `@hydra.main(config_name=\"default\")`.\n\n### Data Classes\n\n`Paper` and `CorpusPaper` in `src/zotero_arxiv_daily/protocol.py`. `Paper` has LLM-powered methods (`generate_tldr`, `generate_affiliations`) that call the OpenAI API directly with `tiktoken`-based token truncation.\n\n## Testing Conventions\n\n- Tests use **pytest monkeypatch + `SimpleNamespace`** for stubs — not `unittest.mock`.\n- A session-scoped Hydra config in `tests/conftest.py` is deep-copied per test via the `config` fixture.\n- Canned response factories live in `tests/canned_responses.py` (e.g., `make_stub_openai_client()`, `make_stub_zotero_client()`).\n- Tests marked `@pytest.mark.slow` require heavy dependencies (model downloads) and are excluded by default (`addopts = \"-m 'not slow'\"` in pyproject.toml).\n- Monkeypatching targets the module-level import path (e.g., `\"zotero_arxiv_daily.executor.zotero.Zotero\"`).\n\n## Coding Conventions\n\n- **Logging:** `loguru.logger` throughout — never `print()` or stdlib `logging`.\n- **Type hints:** Modern Python 3.10+ syntax (`list[Paper]`, `str | None`).\n- **Constants:** Module-level `UPPER_SNAKE_CASE`.\n- **Private methods:** Prefixed with `_` (e.g., `_retrieve_raw_papers`).\n- **Error handling:** Graceful degradation with try/except and fallback logic; log warnings rather than raising.\n- **Config injection:** All major components receive `DictConfig` at init and store it as `self.config`.\n\n## Git Workflow\n\n- PRs should target the **`dev`** branch, not `main`.\n"},"items":[{"name":"CLAUDE.md","path":"CLAUDE.md","rawUrl":"https://raw.githubusercontent.com/TideDra/zotero-arxiv-daily/HEAD/CLAUDE.md","title":"Claude Agent Guidelines & System Prompt","category":"claude-rule","format":"markdown","content":"# CLAUDE.md\n\nThis file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.\n\n## Project Overview\n\nZotero-arXiv-Daily recommends new arXiv/bioRxiv/medRxiv/chemRxiv papers based on a user's Zotero library. It computes embedding similarity between new papers and the user's existing library, generates TLDRs via LLM, and delivers results by email. Designed to run as a GitHub Actions workflow at zero cost.\n\n## Commands\n\n```bash\n# Run the application\nuv run src/zotero_arxiv_daily/main.py\n\n# Run tests (excludes slow tests by default)\nuv run pytest\n\n# Run all tests including slow ones\nuv run pytest -m \"\"\n\n# Run a single test\nuv run pytest tests/test_utils.py::TestGlobMatch -v\n\n# Install/sync dependencies\nuv sync\n```\n\nNo linter or formatter is configured.\n\n## Architecture\n\nThe app follows a linear pipeline orchestrated by `Executor` (`src/zotero_arxiv_daily/executor.py`):\n\n1. **Fetch Zotero corpus** — retrieves user's library papers via pyzotero API\n2. **Filter corpus** — applies `include_path` glob patterns to select relevant collections\n3. **Retrieve new papers** — fetches from configured sources (arXiv RSS, bioRxiv/medRxiv REST API, chemRxiv via Crossref REST API)\n4. **Rerank** — scores candidates by weighted similarity to corpus (newer Zotero papers weighted higher)\n5. **Generate TLDRs + affiliations** — via OpenAI-compatible LLM API\n6. **Render + send email** — HTML email via SMTP\n\n### Plugin Systems\n\n**Retrievers** (`src/zotero_arxiv_daily/retriever/`): Register via `@register_retriever` decorator, discovered by `get_retriever_cls()`. Each retriever implements `_retrieve_raw_papers()` and `convert_to_paper()`.\n\n**Rerankers** (`src/zotero_arxiv_daily/reranker/`): Register via `@register_reranker` decorator, discovered by `get_reranker_cls()`. Two implementations: `local` (sentence-transformers) and `api` (OpenAI-compatible embeddings endpoint).\n\n### Configuration\n\nUses Hydra + OmegaConf. Config is composed from `config/base.yaml` (defaults) + `config/custom.yaml` (user overrides). Environment variables are interpolated via `${oc.env:VAR_NAME,default}` syntax. Entry point uses `@hydra.main`.\n\n### Data Classes\n\n`Paper` and `CorpusPaper` in `src/zotero_arxiv_daily/protocol.py`. `Paper` has LLM-powered methods (`generate_tldr`, `generate_affiliations`) that call the OpenAI API directly.\n\n## Testing\n\nTests marked `@pytest.mark.slow` require heavy dependencies (e.g., sentence-transformers model download) and are skipped locally by default (`addopts = \"-m 'not slow'\"` in pyproject.toml). All other tests run with pure Python stubs (no Docker containers needed).\n\n```bash\n# Run tests (excludes slow tests)\nuv run pytest\n\n# Run all tests including slow ones\nuv run pytest -m \"\"\n\n# Run with coverage\nuv run pytest --cov=src/zotero_arxiv_daily --cov-report=term-missing\n```\n\n## gstack\n\nUse the `/browse` skill from gstack for all web browsing. Never use `mcp__claude-in-chrome__*` tools.\n\nAvailable skills: `/office-hours`, `/plan-ceo-review`, `/plan-eng-review`, `/plan-design-review`, `/design-consultation`, `/design-shotgun`, `/design-html`, `/review`, `/ship`, `/land-and-deploy`, `/canary`, `/benchmark`, `/browse`, `/connect-chrome`, `/qa`, `/qa-only`, `/design-review`, `/setup-browser-cookies`, `/setup-deploy`, `/retro`, `/investigate`, `/document-release`, `/codex`, `/cso`, `/autoplan`, `/plan-devex-review`, `/devex-review`, `/careful`, `/freeze`, `/guard`, `/unfreeze`, `/gstack-upgrade`, `/learn`.\n\nIf gstack skills aren't working, run `cd .claude/skills/gstack && ./setup` to build the binary and register skills.\n\n## Git Workflow\n\n- PRs should target the `dev` branch, not `main`\n- Current development branch: `dev`\n","isInternal":false,"tokens":950,"sizeBytes":3714},{"name":"copilot-instructions.md","path":".github/copilot-instructions.md","rawUrl":"https://raw.githubusercontent.com/TideDra/zotero-arxiv-daily/HEAD/.github/copilot-instructions.md","title":"GitHub Copilot Instructions","category":"copilot-instructions","format":"markdown","content":"# Copilot Instructions\n\n## Project Overview\n\nZotero-arXiv-Daily recommends new arXiv/bioRxiv/medRxiv papers based on a user's Zotero library. It computes embedding similarity between new papers and the user's existing library, generates TLDRs via LLM, and delivers results by email. Designed to run as a GitHub Actions workflow at zero cost.\n\n## Commands\n\n```bash\n# Install/sync dependencies\nuv sync\n\n# Run the application\nuv run src/zotero_arxiv_daily/main.py\n\n# Run tests (excludes slow tests by default)\nuv run pytest\n\n# Run all tests including slow ones\nuv run pytest -m \"\"\n\n# Run a single test\nuv run pytest tests/test_utils.py::TestGlobMatch -v\n```\n\nNo linter or formatter is configured.\n\n## Architecture\n\nThe app is a linear pipeline orchestrated by `Executor` (`src/zotero_arxiv_daily/executor.py`):\n\n1. **Fetch Zotero corpus** → pyzotero API\n2. **Filter corpus** → `include_path` / `ignore_path` glob patterns\n3. **Retrieve new papers** → from configured sources (arXiv RSS, bioRxiv/medRxiv REST API)\n4. **Rerank** → weighted embedding similarity to corpus (newer Zotero papers weighted higher)\n5. **Generate TLDRs + affiliations** → OpenAI-compatible LLM API\n6. **Render + send email** → HTML email via SMTP\n\n### Plugin Systems\n\n**Retrievers** (`src/zotero_arxiv_daily/retriever/`): Register via `@register_retriever(\"name\")` decorator on a `BaseRetriever` subclass. Each retriever implements `_retrieve_raw_papers()` and `convert_to_paper()`. Discovered at runtime via `get_retriever_cls(name)` from a module-level `registered_retrievers` dict.\n\n**Rerankers** (`src/zotero_arxiv_daily/reranker/`): Register via `@register_reranker(\"name\")` decorator on a `BaseReranker` subclass. Two implementations: `local` (sentence-transformers) and `api` (OpenAI-compatible embeddings endpoint). Discovered via `get_reranker_cls(name)`.\n\nWhen adding a new retriever or reranker, follow the existing pattern: create a new file, subclass the base, apply the registration decorator, and implement the abstract methods.\n\n### Configuration\n\nUses Hydra + OmegaConf. Config composes from `config/base.yaml` (defaults with `???` placeholders for required values) + `config/custom.yaml` (user overrides). The composition order is defined in `config/default.yaml`. Environment variables are interpolated via `${oc.env:VAR_NAME,default}` syntax. Entry point uses `@hydra.main(config_name=\"default\")`.\n\n### Data Classes\n\n`Paper` and `CorpusPaper` in `src/zotero_arxiv_daily/protocol.py`. `Paper` has LLM-powered methods (`generate_tldr`, `generate_affiliations`) that call the OpenAI API directly with `tiktoken`-based token truncation.\n\n## Testing Conventions\n\n- Tests use **pytest monkeypatch + `SimpleNamespace`** for stubs — not `unittest.mock`.\n- A session-scoped Hydra config in `tests/conftest.py` is deep-copied per test via the `config` fixture.\n- Canned response factories live in `tests/canned_responses.py` (e.g., `make_stub_openai_client()`, `make_stub_zotero_client()`).\n- Tests marked `@pytest.mark.slow` require heavy dependencies (model downloads) and are excluded by default (`addopts = \"-m 'not slow'\"` in pyproject.toml).\n- Monkeypatching targets the module-level import path (e.g., `\"zotero_arxiv_daily.executor.zotero.Zotero\"`).\n\n## Coding Conventions\n\n- **Logging:** `loguru.logger` throughout — never `print()` or stdlib `logging`.\n- **Type hints:** Modern Python 3.10+ syntax (`list[Paper]`, `str | None`).\n- **Constants:** Module-level `UPPER_SNAKE_CASE`.\n- **Private methods:** Prefixed with `_` (e.g., `_retrieve_raw_papers`).\n- **Error handling:** Graceful degradation with try/except and fallback logic; log warnings rather than raising.\n- **Config injection:** All major components receive `DictConfig` at init and store it as `self.config`.\n\n## Git Workflow\n\n- PRs should target the **`dev`** branch, not `main`.\n","isInternal":false,"tokens":970,"sizeBytes":3848}],"systemPromptSnippet":"<yakaai_skills repo=\"TideDra/zotero-arxiv-daily\">\n<!-- File: CLAUDE.md (Tokens: ~950 | Category: claude-rule) -->\n# CLAUDE.md\n\nThis file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.\n\n## Project Overview\n\nZotero-arXiv-Daily recommends new arXiv/bioRxiv/medRxiv/chemRxiv papers based on a user's Zotero library. It computes embedding similarity between new papers and the user's existing library, generates TLDRs via LLM, and delivers results by email. Designed to run as a GitHub Actions workflow at zero cost.\n\n## Commands\n\n```bash\n# Run the application\nuv run src/zotero_arxiv_daily/main.py\n\n# Run tests (excludes slow tests by default)\nuv run pytest\n\n# Run all tests including slow ones\nuv run pytest -m \"\"\n\n# Run a single test\nuv run pytest tests/test_utils.py::TestGlobMatch -v\n\n# Install/sync dependencies\nuv sync\n```\n\nNo linter or formatter is configured.\n\n## Architecture\n\nThe app follows a linear pipeline orchestrated by `Executor` (`src/zotero_arxiv_daily/executor.py`):\n\n1. **Fetch Zotero corpus** — retrieves user's library papers via pyzotero API\n2. **Filter corpus** — applies `include_path` glob patterns to select relevant collections\n3. **Retrieve new papers** — fetches from configured sources (arXiv RSS, bioRxiv/medRxiv REST API, chemRxiv via Crossref REST API)\n4. **Rerank** — scores candidates by weighted similarity to corpus (newer Zotero papers weighted higher)\n5. **Generate TLDRs + affiliations** — via OpenAI-compatible LLM API\n6. **Render + send email** — HTML email via SMTP\n\n### Plugin Systems\n\n**Retrievers** (`src/zotero_arxiv_daily/retriever/`): Register via `@register_retriever` decorator, discovered by `get_retriever_cls()`. Each retriever implements `_retrieve_raw_papers()` and `convert_to_paper()`.\n\n**Rerankers** (`src/zotero_arxiv_daily/reranker/`): Register via `@register_reranker` decorator, discovered by `get_reranker_cls()`. Two implementations: `local` (sentence-transformers) and `api` (OpenAI-compatible embeddings endpoint).\n\n### Configuration\n\nUses Hydra + OmegaConf. Config is composed from `config/base.yaml` (defaults) + `config/custom.yaml` (user overrides). Environment variables are interpolated via `${oc.env:VAR_NAME,default}` syntax. Entry point uses `@hydra.main`.\n\n### Data Classes\n\n`Paper` and `CorpusPaper` in `src/zotero_arxiv_daily/protocol.py`. `Paper` has LLM-powered methods (`generate_tldr`, `generate_affiliations`) that call the OpenAI API directly.\n\n## Testing\n\nTests marked `@pytest.mark.slow` require heavy dependencies (e.g., sentence-transformers model download) and are skipped locally by default (`addopts = \"-m 'not slow'\"` in pyproject.toml). All other tests run with pure Python stubs (no Docker containers needed).\n\n```bash\n# Run tests (excludes slow tests)\nuv run pytest\n\n# Run all tests including slow ones\nuv run pytest -m \"\"\n\n# Run with coverage\nuv run pytest --cov=src/zotero_arxiv_daily --cov-report=term-missing\n```\n\n## gstack\n\nUse the `/browse` skill from gstack for all web browsing. Never use `mcp__claude-in-chrome__*` tools.\n\nAvailable skills: `/office-hours`, `/plan-ceo-review`, `/plan-eng-review`, `/plan-design-review`, `/design-consultation`, `/design-shotgun`, `/design-html`, `/review`, `/ship`, `/land-and-deploy`, `/canary`, `/benchmark`, `/browse`, `/connect-chrome`, `/qa`, `/qa-only`, `/design-review`, `/setup-browser-cookies`, `/setup-deploy`, `/retro`, `/investigate`, `/document-release`, `/codex`, `/cso`, `/autoplan`, `/plan-devex-review`, `/devex-review`, `/careful`, `/freeze`, `/guard`, `/unfreeze`, `/gstack-upgrade`, `/learn`.\n\nIf gstack skills aren't working, run `cd .claude/skills/gstack && ./setup` to build the binary and register skills.\n\n## Git Workflow\n\n- PRs should target the `dev` branch, not `main`\n- Current development branch: `dev`\n\n<!-- File: .github/copilot-instructions.md (Tokens: ~970 | Category: copilot-instructions) -->\n# Copilot Instructions\n\n## Project Overview\n\nZotero-arXiv-Daily recommends new arXiv/bioRxiv/medRxiv papers based on a user's Zotero library. It computes embedding similarity between new papers and the user's existing library, generates TLDRs via LLM, and delivers results by email. Designed to run as a GitHub Actions workflow at zero cost.\n\n## Commands\n\n```bash\n# Install/sync dependencies\nuv sync\n\n# Run the application\nuv run src/zotero_arxiv_daily/main.py\n\n# Run tests (excludes slow tests by default)\nuv run pytest\n\n# Run all tests including slow ones\nuv run pytest -m \"\"\n\n# Run a single test\nuv run pytest tests/test_utils.py::TestGlobMatch -v\n```\n\nNo linter or formatter is configured.\n\n## Architecture\n\nThe app is a linear pipeline orchestrated by `Executor` (`src/zotero_arxiv_daily/executor.py`):\n\n1. **Fetch Zotero corpus** → pyzotero API\n2. **Filter corpus** → `include_path` / `ignore_path` glob patterns\n3. **Retrieve new papers** → from configured sources (arXiv RSS, bioRxiv/medRxiv REST API)\n4. **Rerank** → weighted embedding similarity to corpus (newer Zotero papers weighted higher)\n5. **Generate TLDRs + affiliations** → OpenAI-compatible LLM API\n6. **Render + send email** → HTML email via SMTP\n\n### Plugin Systems\n\n**Retrievers** (`src/zotero_arxiv_daily/retriever/`): Register via `@register_retriever(\"name\")` decorator on a `BaseRetriever` subclass. Each retriever implements `_retrieve_raw_papers()` and `convert_to_paper()`. Discovered at runtime via `get_retriever_cls(name)` from a module-level `registered_retrievers` dict.\n\n**Rerankers** (`src/zotero_arxiv_daily/reranker/`): Register via `@register_reranker(\"name\")` decorator on a `BaseReranker` subclass. Two implementations: `local` (sentence-transformers) and `api` (OpenAI-compatible embeddings endpoint). Discovered via `get_reranker_cls(name)`.\n\nWhen adding a new retriever or reranker, follow the existing pattern: create a new file, subclass the base, apply the registration decorator, and implement the abstract methods.\n\n### Configuration\n\nUses Hydra + OmegaConf. Config composes from `config/base.yaml` (defaults with `???` placeholders for required values) + `config/custom.yaml` (user overrides). The composition order is defined in `config/default.yaml`. Environment variables are interpolated via `${oc.env:VAR_NAME,default}` syntax. Entry point uses `@hydra.main(config_name=\"default\")`.\n\n### Data Classes\n\n`Paper` and `CorpusPaper` in `src/zotero_arxiv_daily/protocol.py`. `Paper` has LLM-powered methods (`generate_tldr`, `generate_affiliations`) that call the OpenAI API directly with `tiktoken`-based token truncation.\n\n## Testing Conventions\n\n- Tests use **pytest monkeypatch + `SimpleNamespace`** for stubs — not `unittest.mock`.\n- A session-scoped Hydra config in `tests/conftest.py` is deep-copied per test via the `config` fixture.\n- Canned response factories live in `tests/canned_responses.py` (e.g., `make_stub_openai_client()`, `make_stub_zotero_client()`).\n- Tests marked `@pytest.mark.slow` require heavy dependencies (model downloads) and are excluded by default (`addopts = \"-m 'not slow'\"` in pyproject.toml).\n- Monkeypatching targets the module-level import path (e.g., `\"zotero_arxiv_daily.executor.zotero.Zotero\"`).\n\n## Coding Conventions\n\n- **Logging:** `loguru.logger` throughout — never `print()` or stdlib `logging`.\n- **Type hints:** Modern Python 3.10+ syntax (`list[Paper]`, `str | None`).\n- **Constants:** Module-level `UPPER_SNAKE_CASE`.\n- **Private methods:** Prefixed with `_` (e.g., `_retrieve_raw_papers`).\n- **Error handling:** Graceful degradation with try/except and fallback logic; log warnings rather than raising.\n- **Config injection:** All major components receive `DictConfig` at init and store it as `self.config`.\n\n## Git Workflow\n\n- PRs should target the **`dev`** branch, not `main`.\n</yakaai_skills>"}