mdfetch 0.9.2__tar.gz → 0.9.4__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- mdfetch-0.9.4/.github/dependabot.yml +21 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/.github/workflows/ci.yml +4 -4
- {mdfetch-0.9.2 → mdfetch-0.9.4}/.github/workflows/integration.yml +3 -3
- {mdfetch-0.9.2 → mdfetch-0.9.4}/.github/workflows/publish.yml +7 -7
- {mdfetch-0.9.2 → mdfetch-0.9.4}/PKG-INFO +12 -2
- {mdfetch-0.9.2 → mdfetch-0.9.4}/README.md +10 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/pyproject.toml +1 -1
- mdfetch-0.9.4/src/mdfetch/fallback.py +53 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/tests/unit/test_fallback.py +27 -3
- {mdfetch-0.9.2 → mdfetch-0.9.4}/uv.lock +28 -9
- mdfetch-0.9.2/.vscode/settings.json +0 -10
- mdfetch-0.9.2/src/mdfetch/fallback.py +0 -64
- mdfetch-0.9.2/tests/integration_log.txt +0 -1
- {mdfetch-0.9.2 → mdfetch-0.9.4}/.agents/skills/speckit-analyze/SKILL.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/.agents/skills/speckit-checklist/SKILL.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/.agents/skills/speckit-clarify/SKILL.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/.agents/skills/speckit-constitution/SKILL.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/.agents/skills/speckit-converge/SKILL.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/.agents/skills/speckit-implement/SKILL.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/.agents/skills/speckit-plan/SKILL.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/.agents/skills/speckit-specify/SKILL.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/.agents/skills/speckit-tasks/SKILL.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/.agents/skills/speckit-taskstoissues/SKILL.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/.claude/skills/speckit-analyze/SKILL.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/.claude/skills/speckit-archive-run/SKILL.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/.claude/skills/speckit-checklist/SKILL.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/.claude/skills/speckit-clarify/SKILL.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/.claude/skills/speckit-constitution/SKILL.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/.claude/skills/speckit-converge/SKILL.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/.claude/skills/speckit-git-commit/SKILL.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/.claude/skills/speckit-git-feature/SKILL.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/.claude/skills/speckit-git-initialize/SKILL.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/.claude/skills/speckit-git-remote/SKILL.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/.claude/skills/speckit-git-validate/SKILL.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/.claude/skills/speckit-implement/SKILL.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/.claude/skills/speckit-plan/SKILL.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/.claude/skills/speckit-reconcile-run/SKILL.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/.claude/skills/speckit-specify/SKILL.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/.claude/skills/speckit-tasks/SKILL.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/.claude/skills/speckit-taskstoissues/SKILL.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/.gemini/commands/speckit.analyze.toml +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/.gemini/commands/speckit.checklist.toml +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/.gemini/commands/speckit.clarify.toml +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/.gemini/commands/speckit.constitution.toml +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/.gemini/commands/speckit.implement.toml +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/.gemini/commands/speckit.plan.toml +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/.gemini/commands/speckit.specify.toml +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/.gemini/commands/speckit.tasks.toml +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/.gemini/commands/speckit.taskstoissues.toml +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/.gitattributes +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/.github/copilot-instructions.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/.github/scripts/generate_resources.py +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/.gitignore +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/.python-version +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/.specify/extensions/.registry +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/.specify/extensions/archive/LICENSE +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/.specify/extensions/archive/README.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/.specify/extensions/archive/commands/archive.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/.specify/extensions/archive/extension.yml +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/.specify/extensions/git/README.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/.specify/extensions/git/commands/speckit.git.commit.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/.specify/extensions/git/commands/speckit.git.feature.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/.specify/extensions/git/commands/speckit.git.initialize.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/.specify/extensions/git/commands/speckit.git.remote.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/.specify/extensions/git/commands/speckit.git.validate.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/.specify/extensions/git/config-template.yml +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/.specify/extensions/git/extension.yml +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/.specify/extensions/git/git-config.yml +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/.specify/extensions/git/scripts/bash/auto-commit.sh +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/.specify/extensions/git/scripts/bash/create-new-feature.sh +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/.specify/extensions/git/scripts/bash/git-common.sh +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/.specify/extensions/git/scripts/bash/initialize-repo.sh +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/.specify/extensions/git/scripts/powershell/auto-commit.ps1 +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/.specify/extensions/git/scripts/powershell/create-new-feature.ps1 +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/.specify/extensions/git/scripts/powershell/git-common.ps1 +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/.specify/extensions/git/scripts/powershell/initialize-repo.ps1 +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/.specify/extensions/reconcile/LICENSE +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/.specify/extensions/reconcile/README.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/.specify/extensions/reconcile/commands/reconcile.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/.specify/extensions/reconcile/extension.yml +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/.specify/extensions.yml +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/.specify/feature.json +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/.specify/init-options.json +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/.specify/integration.json +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/.specify/integrations/agy.manifest.json +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/.specify/integrations/claude.manifest.json +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/.specify/integrations/gemini.manifest.json +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/.specify/integrations/speckit.manifest.json +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/.specify/memory/changelog.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/.specify/memory/constitution.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/.specify/memory/plan.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/.specify/memory/spec.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/.specify/scripts/bash/check-prerequisites.sh +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/.specify/scripts/bash/common.sh +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/.specify/scripts/bash/create-new-feature.sh +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/.specify/scripts/bash/setup-plan.sh +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/.specify/scripts/bash/setup-tasks.sh +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/.specify/templates/checklist-template.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/.specify/templates/constitution-template.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/.specify/templates/plan-template.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/.specify/templates/spec-template.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/.specify/templates/tasks-template.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/.specify/workflows/speckit/workflow.yml +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/.specify/workflows/workflow-registry.json +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/CLAUDE.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/GEMINI.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/LICENSE +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/Makefile +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/001-mdfetch-medium-extractor/checklists/requirements.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/001-mdfetch-medium-extractor/contracts/api.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/001-mdfetch-medium-extractor/data-model.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/001-mdfetch-medium-extractor/plan.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/001-mdfetch-medium-extractor/quickstart.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/001-mdfetch-medium-extractor/research.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/001-mdfetch-medium-extractor/spec.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/001-mdfetch-medium-extractor/tasks.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/002-devto-provider/checklists/requirements.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/002-devto-provider/contracts/public-api.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/002-devto-provider/data-model.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/002-devto-provider/plan.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/002-devto-provider/quickstart.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/002-devto-provider/research.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/002-devto-provider/spec.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/002-devto-provider/tasks.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/003-medium-freedium-fallback/checklists/requirements.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/003-medium-freedium-fallback/contracts/extract-api.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/003-medium-freedium-fallback/plan.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/003-medium-freedium-fallback/research.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/003-medium-freedium-fallback/spec.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/003-medium-freedium-fallback/tasks.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/004-remove-backoff/checklists/requirements.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/004-remove-backoff/plan.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/004-remove-backoff/research.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/004-remove-backoff/spec.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/004-remove-backoff/tasks.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/005-substack-provider/checklists/requirements.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/005-substack-provider/contracts/extractor-api.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/005-substack-provider/data-model.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/005-substack-provider/plan.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/005-substack-provider/quickstart.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/005-substack-provider/research.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/005-substack-provider/spec.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/005-substack-provider/tasks.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/006-thenewstack-provider/checklists/requirements.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/006-thenewstack-provider/contracts/public-api.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/006-thenewstack-provider/data-model.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/006-thenewstack-provider/plan.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/006-thenewstack-provider/quickstart.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/006-thenewstack-provider/research.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/006-thenewstack-provider/spec.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/006-thenewstack-provider/tasks.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/007-dzone-provider/checklists/requirements.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/007-dzone-provider/contracts/public-api.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/007-dzone-provider/data-model.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/007-dzone-provider/plan.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/007-dzone-provider/quickstart.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/007-dzone-provider/research.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/007-dzone-provider/spec.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/007-dzone-provider/tasks.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/008-mdfetch-cli/checklists/requirements.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/008-mdfetch-cli/contracts/cli.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/008-mdfetch-cli/data-model.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/008-mdfetch-cli/plan.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/008-mdfetch-cli/quickstart.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/008-mdfetch-cli/research.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/008-mdfetch-cli/spec.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/008-mdfetch-cli/tasks.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/009-homebrew-tap-formula/checklists/requirements.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/009-homebrew-tap-formula/contracts/formula.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/009-homebrew-tap-formula/contracts/tap-update-job.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/009-homebrew-tap-formula/data-model.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/009-homebrew-tap-formula/plan.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/009-homebrew-tap-formula/quickstart.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/009-homebrew-tap-formula/research.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/009-homebrew-tap-formula/spec.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/009-homebrew-tap-formula/tasks.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/010-boomi-blog-provider/checklists/requirements.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/010-boomi-blog-provider/contracts/extractor-contract.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/010-boomi-blog-provider/data-model.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/010-boomi-blog-provider/plan.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/010-boomi-blog-provider/quickstart.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/010-boomi-blog-provider/research.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/010-boomi-blog-provider/spec.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/010-boomi-blog-provider/tasks.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/011-konghq-blog-provider/checklists/requirements.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/011-konghq-blog-provider/contracts/extractor-contract.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/011-konghq-blog-provider/data-model.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/011-konghq-blog-provider/plan.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/011-konghq-blog-provider/quickstart.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/011-konghq-blog-provider/research.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/011-konghq-blog-provider/spec.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/011-konghq-blog-provider/tasks.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/012-list-platforms/checklists/requirements.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/012-list-platforms/contracts/cli.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/012-list-platforms/data-model.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/012-list-platforms/plan.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/012-list-platforms/quickstart.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/012-list-platforms/research.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/012-list-platforms/spec.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/012-list-platforms/tasks.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/013-tavily-fallback/checklists/requirements.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/013-tavily-fallback/contracts/api.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/013-tavily-fallback/contracts/cli.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/013-tavily-fallback/data-model.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/013-tavily-fallback/plan.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/013-tavily-fallback/quickstart.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/013-tavily-fallback/research.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/013-tavily-fallback/spec.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/013-tavily-fallback/tasks.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/src/mdfetch/__init__.py +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/src/mdfetch/base.py +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/src/mdfetch/cli.py +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/src/mdfetch/exceptions.py +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/src/mdfetch/providers/__init__.py +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/src/mdfetch/providers/boomi.py +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/src/mdfetch/providers/devto.py +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/src/mdfetch/providers/dzone.py +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/src/mdfetch/providers/kong.py +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/src/mdfetch/providers/medium.py +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/src/mdfetch/providers/substack.py +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/src/mdfetch/providers/thenewstack.py +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/src/mdfetch/router.py +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/tests/__init__.py +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/tests/conftest.py +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/tests/integration/__init__.py +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/tests/integration/conftest.py +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/tests/integration/snapshots/architecting-the-asynchronous-agent.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/tests/integration/snapshots/boomi-data-consistency-saas-on-prem.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/tests/integration/snapshots/boomi-gartner-magic-quadrant-ipaas-2026.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/tests/integration/snapshots/boomi-real-time-vs-batch-data-integration.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/tests/integration/snapshots/devto-integration-digest-december-2025.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/tests/integration/snapshots/devto-integration-digest-july-2025.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/tests/integration/snapshots/devto-integration-digest-march-2026.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/tests/integration/snapshots/dzone-image-classification-pipeline-camel-djl.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/tests/integration/snapshots/dzone-integration-patterns-fail-production.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/tests/integration/snapshots/dzone-kiro-feature-to-requirements-design-tasks.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/tests/integration/snapshots/from-drift-to-parity.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/tests/integration/snapshots/integration-digest-december-2025.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/tests/integration/snapshots/kong-ai-gateway-vs-litellm.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/tests/integration/snapshots/kong-gateway-3-14.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/tests/integration/snapshots/kong-insomnia-12-6.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/tests/integration/snapshots/substack-api-trends-2025.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/tests/integration/snapshots/substack-kafka-topic-types.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/tests/integration/snapshots/thenewstack-api-mcp-agent.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/tests/integration/snapshots/thenewstack-async-apis.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/tests/integration/snapshots/thenewstack-developer-portal-api.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/tests/integration/snapshots/thenewstack-json-schema-ai.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/tests/integration/snapshots/thenewstack-mcp-api-governance.md +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/tests/integration/test_boomi_integration.py +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/tests/integration/test_cli_integration.py +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/tests/integration/test_devto_integration.py +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/tests/integration/test_dzone_integration.py +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/tests/integration/test_fallback_integration.py +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/tests/integration/test_kong_integration.py +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/tests/integration/test_medium_integration.py +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/tests/integration/test_substack_integration.py +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/tests/integration/test_thenewstack_integration.py +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/tests/unit/__init__.py +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/tests/unit/test_boomi_extractor.py +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/tests/unit/test_cli.py +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/tests/unit/test_devto_extractor.py +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/tests/unit/test_dzone_extractor.py +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/tests/unit/test_fetch_errors.py +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/tests/unit/test_kong_extractor.py +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/tests/unit/test_medium_extractor.py +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/tests/unit/test_router.py +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/tests/unit/test_silent.py +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/tests/unit/test_substack_extractor.py +0 -0
- {mdfetch-0.9.2 → mdfetch-0.9.4}/tests/unit/test_thenewstack_extractor.py +0 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
version: 2
|
|
2
|
+
updates:
|
|
3
|
+
- package-ecosystem: uv
|
|
4
|
+
directory: /
|
|
5
|
+
schedule:
|
|
6
|
+
interval: weekly
|
|
7
|
+
open-pull-requests-limit: 5
|
|
8
|
+
groups:
|
|
9
|
+
python-deps:
|
|
10
|
+
patterns:
|
|
11
|
+
- "*"
|
|
12
|
+
|
|
13
|
+
- package-ecosystem: github-actions
|
|
14
|
+
directory: /
|
|
15
|
+
schedule:
|
|
16
|
+
interval: weekly
|
|
17
|
+
open-pull-requests-limit: 5
|
|
18
|
+
groups:
|
|
19
|
+
actions:
|
|
20
|
+
patterns:
|
|
21
|
+
- "*"
|
|
@@ -12,9 +12,9 @@ jobs:
|
|
|
12
12
|
lint:
|
|
13
13
|
runs-on: ubuntu-latest
|
|
14
14
|
steps:
|
|
15
|
-
- uses: actions/checkout@
|
|
15
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
16
16
|
- name: Install uv
|
|
17
|
-
uses: astral-sh/setup-uv@
|
|
17
|
+
uses: astral-sh/setup-uv@c18668ad3cf93ea998bef934396af7bb5c839dc7 # v10.2.0
|
|
18
18
|
with:
|
|
19
19
|
enable-cache: true
|
|
20
20
|
cache-dependency-glob: "uv.lock"
|
|
@@ -36,9 +36,9 @@ jobs:
|
|
|
36
36
|
matrix:
|
|
37
37
|
python-version: ["3.12", "3.13", "3.14"]
|
|
38
38
|
steps:
|
|
39
|
-
- uses: actions/checkout@
|
|
39
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
40
40
|
- name: Install uv
|
|
41
|
-
uses: astral-sh/setup-uv@
|
|
41
|
+
uses: astral-sh/setup-uv@c18668ad3cf93ea998bef934396af7bb5c839dc7 # v10.2.0
|
|
42
42
|
with:
|
|
43
43
|
enable-cache: true
|
|
44
44
|
cache-dependency-glob: "uv.lock"
|
|
@@ -14,10 +14,10 @@ jobs:
|
|
|
14
14
|
runs-on: [self-hosted, Linux, ARM64]
|
|
15
15
|
timeout-minutes: 30
|
|
16
16
|
steps:
|
|
17
|
-
- uses: actions/checkout@
|
|
17
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
18
18
|
|
|
19
19
|
- name: Install uv
|
|
20
|
-
uses: astral-sh/setup-uv@
|
|
20
|
+
uses: astral-sh/setup-uv@c18668ad3cf93ea998bef934396af7bb5c839dc7 # v10.2.0
|
|
21
21
|
with:
|
|
22
22
|
enable-cache: true
|
|
23
23
|
cache-dependency-glob: "uv.lock"
|
|
@@ -34,7 +34,7 @@ jobs:
|
|
|
34
34
|
|
|
35
35
|
- name: Create issue on failure
|
|
36
36
|
if: failure() && steps.integration.outcome == 'failure'
|
|
37
|
-
uses: actions/github-script@
|
|
37
|
+
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
|
|
38
38
|
with:
|
|
39
39
|
script: |
|
|
40
40
|
const runUrl = `${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`;
|
|
@@ -13,9 +13,9 @@ jobs:
|
|
|
13
13
|
runs-on: ubuntu-latest
|
|
14
14
|
if: github.event.release.target_commitish == 'main'
|
|
15
15
|
steps:
|
|
16
|
-
- uses: actions/checkout@
|
|
16
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
17
17
|
- name: Install uv
|
|
18
|
-
uses: astral-sh/setup-uv@
|
|
18
|
+
uses: astral-sh/setup-uv@c18668ad3cf93ea998bef934396af7bb5c839dc7 # v10.2.0
|
|
19
19
|
- name: Set up Python
|
|
20
20
|
run: uv python pin 3.12
|
|
21
21
|
- name: Validate version
|
|
@@ -29,7 +29,7 @@ jobs:
|
|
|
29
29
|
- name: Build package
|
|
30
30
|
run: make build
|
|
31
31
|
- name: Store the distribution packages
|
|
32
|
-
uses: actions/upload-artifact@
|
|
32
|
+
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
|
33
33
|
with:
|
|
34
34
|
name: python-package-distributions
|
|
35
35
|
path: dist/
|
|
@@ -45,12 +45,12 @@ jobs:
|
|
|
45
45
|
id-token: write
|
|
46
46
|
steps:
|
|
47
47
|
- name: Download all the dists
|
|
48
|
-
uses: actions/download-artifact@
|
|
48
|
+
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
|
49
49
|
with:
|
|
50
50
|
name: python-package-distributions
|
|
51
51
|
path: dist/
|
|
52
52
|
- name: Publish package distributions to PyPI
|
|
53
|
-
uses: pypa/gh-action-pypi-publish@
|
|
53
|
+
uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # v1.14.2
|
|
54
54
|
with:
|
|
55
55
|
packages-dir: dist/
|
|
56
56
|
|
|
@@ -65,10 +65,10 @@ jobs:
|
|
|
65
65
|
contents: read
|
|
66
66
|
|
|
67
67
|
steps:
|
|
68
|
-
- uses: actions/checkout@
|
|
68
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
69
69
|
|
|
70
70
|
- name: Install uv
|
|
71
|
-
uses: astral-sh/setup-uv@
|
|
71
|
+
uses: astral-sh/setup-uv@c18668ad3cf93ea998bef934396af7bb5c839dc7 # v10.2.0
|
|
72
72
|
|
|
73
73
|
- name: Set up Python
|
|
74
74
|
run: uv python pin 3.12
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
2
|
Name: mdfetch
|
|
3
|
-
Version: 0.9.
|
|
3
|
+
Version: 0.9.4
|
|
4
4
|
Summary: Extract article content from web platforms and return it as clean Markdown.
|
|
5
5
|
Project-URL: Homepage, https://github.com/stn1slv/md-fetch
|
|
6
6
|
Project-URL: Source, https://github.com/stn1slv/md-fetch
|
|
@@ -63,6 +63,10 @@ md-fetch https://medium.com/example/article
|
|
|
63
63
|
# Fetch and save Markdown to a file
|
|
64
64
|
md-fetch https://dev.to/example/article --output article.md
|
|
65
65
|
|
|
66
|
+
# Fallback to Tavily API if the primary extractor fails (requires TAVILY_API_KEY)
|
|
67
|
+
export TAVILY_API_KEY="tvly-..."
|
|
68
|
+
md-fetch https://example.com/unsupported-blog --tavily-fallback
|
|
69
|
+
|
|
66
70
|
# List all supported platforms (no URL or network needed)
|
|
67
71
|
md-fetch --list-platforms
|
|
68
72
|
```
|
|
@@ -80,6 +84,9 @@ markdown = extract("https://thenewstack.io/article-slug")
|
|
|
80
84
|
markdown = extract("https://dzone.com/articles/article-slug")
|
|
81
85
|
markdown = extract("https://boomi.com/blog/article-slug")
|
|
82
86
|
markdown = extract("https://konghq.com/blog/category/article-slug")
|
|
87
|
+
|
|
88
|
+
# Fallback to Tavily API if the primary extractor fails (requires TAVILY_API_KEY env var)
|
|
89
|
+
markdown = extract("https://example.com/unsupported-blog", tavily_fallback=True)
|
|
83
90
|
print(markdown)
|
|
84
91
|
```
|
|
85
92
|
|
|
@@ -94,6 +101,7 @@ from mdfetch import (
|
|
|
94
101
|
FetchError,
|
|
95
102
|
HTTPStatusError,
|
|
96
103
|
EmptyContentError,
|
|
104
|
+
MissingAPIKeyError,
|
|
97
105
|
)
|
|
98
106
|
|
|
99
107
|
url = "https://medium.com/some-publication/article-slug-abc123"
|
|
@@ -112,6 +120,8 @@ except FetchError as e:
|
|
|
112
120
|
print(f"Network error: {e.message}")
|
|
113
121
|
except EmptyContentError as e:
|
|
114
122
|
print(f"No content: {e.message}")
|
|
123
|
+
except MissingAPIKeyError as e:
|
|
124
|
+
print(f"Missing API Key: {e.message}")
|
|
115
125
|
```
|
|
116
126
|
|
|
117
127
|
## Supported platforms
|
|
@@ -29,6 +29,10 @@ md-fetch https://medium.com/example/article
|
|
|
29
29
|
# Fetch and save Markdown to a file
|
|
30
30
|
md-fetch https://dev.to/example/article --output article.md
|
|
31
31
|
|
|
32
|
+
# Fallback to Tavily API if the primary extractor fails (requires TAVILY_API_KEY)
|
|
33
|
+
export TAVILY_API_KEY="tvly-..."
|
|
34
|
+
md-fetch https://example.com/unsupported-blog --tavily-fallback
|
|
35
|
+
|
|
32
36
|
# List all supported platforms (no URL or network needed)
|
|
33
37
|
md-fetch --list-platforms
|
|
34
38
|
```
|
|
@@ -46,6 +50,9 @@ markdown = extract("https://thenewstack.io/article-slug")
|
|
|
46
50
|
markdown = extract("https://dzone.com/articles/article-slug")
|
|
47
51
|
markdown = extract("https://boomi.com/blog/article-slug")
|
|
48
52
|
markdown = extract("https://konghq.com/blog/category/article-slug")
|
|
53
|
+
|
|
54
|
+
# Fallback to Tavily API if the primary extractor fails (requires TAVILY_API_KEY env var)
|
|
55
|
+
markdown = extract("https://example.com/unsupported-blog", tavily_fallback=True)
|
|
49
56
|
print(markdown)
|
|
50
57
|
```
|
|
51
58
|
|
|
@@ -60,6 +67,7 @@ from mdfetch import (
|
|
|
60
67
|
FetchError,
|
|
61
68
|
HTTPStatusError,
|
|
62
69
|
EmptyContentError,
|
|
70
|
+
MissingAPIKeyError,
|
|
63
71
|
)
|
|
64
72
|
|
|
65
73
|
url = "https://medium.com/some-publication/article-slug-abc123"
|
|
@@ -78,6 +86,8 @@ except FetchError as e:
|
|
|
78
86
|
print(f"Network error: {e.message}")
|
|
79
87
|
except EmptyContentError as e:
|
|
80
88
|
print(f"No content: {e.message}")
|
|
89
|
+
except MissingAPIKeyError as e:
|
|
90
|
+
print(f"Missing API Key: {e.message}")
|
|
81
91
|
```
|
|
82
92
|
|
|
83
93
|
## Supported platforms
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"""Fallback extraction mechanisms for mdfetch."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
from mdfetch.exceptions import EmptyContentError, FetchError, MissingAPIKeyError
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def _extract(client: Any, url: str, depth: str, timeout: float) -> str:
|
|
11
|
+
response = client.extract(urls=[url], extract_depth=depth, timeout=timeout)
|
|
12
|
+
results = response.get("results", [])
|
|
13
|
+
if not results:
|
|
14
|
+
raise EmptyContentError(f"Tavily returned no results for {url}", url=url)
|
|
15
|
+
|
|
16
|
+
raw_content = results[0].get("raw_content")
|
|
17
|
+
if not raw_content:
|
|
18
|
+
raise EmptyContentError(f"Tavily returned empty content for {url}", url=url)
|
|
19
|
+
|
|
20
|
+
return str(raw_content)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def tavily_extract(url: str, timeout: float = 30.0) -> str:
|
|
24
|
+
"""Extract content using the Tavily API fallback.
|
|
25
|
+
|
|
26
|
+
Tries the cheaper "basic" depth first. If it fails or returns nothing, retries
|
|
27
|
+
once with "advanced". Empty content from the retry raises EmptyContentError;
|
|
28
|
+
any other failure raises FetchError.
|
|
29
|
+
"""
|
|
30
|
+
try:
|
|
31
|
+
from tavily import TavilyClient # type: ignore[import-untyped]
|
|
32
|
+
except ImportError as e:
|
|
33
|
+
raise MissingAPIKeyError(
|
|
34
|
+
"tavily-python package is not installed. Please install it "
|
|
35
|
+
f"to use the fallback feature. {e}"
|
|
36
|
+
) from e
|
|
37
|
+
|
|
38
|
+
try:
|
|
39
|
+
client = TavilyClient()
|
|
40
|
+
except Exception as e:
|
|
41
|
+
raise MissingAPIKeyError(str(e)) from e
|
|
42
|
+
|
|
43
|
+
try:
|
|
44
|
+
return _extract(client, url, "basic", timeout)
|
|
45
|
+
except Exception:
|
|
46
|
+
pass # any basic-depth failure is retried with advanced below
|
|
47
|
+
|
|
48
|
+
try:
|
|
49
|
+
return _extract(client, url, "advanced", timeout)
|
|
50
|
+
except EmptyContentError:
|
|
51
|
+
raise
|
|
52
|
+
except Exception as e:
|
|
53
|
+
raise FetchError(f"Tavily fallback failed: {e}", url=url) from e
|
|
@@ -7,7 +7,7 @@ from unittest.mock import MagicMock, patch
|
|
|
7
7
|
import pytest
|
|
8
8
|
|
|
9
9
|
from mdfetch import extract
|
|
10
|
-
from mdfetch.exceptions import FetchError, MissingAPIKeyError
|
|
10
|
+
from mdfetch.exceptions import EmptyContentError, FetchError, MissingAPIKeyError
|
|
11
11
|
from mdfetch.fallback import tavily_extract
|
|
12
12
|
|
|
13
13
|
|
|
@@ -21,7 +21,7 @@ class TestTavilyExtract:
|
|
|
21
21
|
|
|
22
22
|
assert result == "# Hello"
|
|
23
23
|
mock_instance.extract.assert_called_once_with(
|
|
24
|
-
urls=["https://example.com"], extract_depth="basic"
|
|
24
|
+
urls=["https://example.com"], extract_depth="basic", timeout=30.0
|
|
25
25
|
)
|
|
26
26
|
|
|
27
27
|
@patch("tavily.TavilyClient")
|
|
@@ -38,7 +38,7 @@ class TestTavilyExtract:
|
|
|
38
38
|
assert result == "# Advanced"
|
|
39
39
|
assert mock_instance.extract.call_count == 2
|
|
40
40
|
mock_instance.extract.assert_any_call(
|
|
41
|
-
urls=["https://example.com"], extract_depth="advanced"
|
|
41
|
+
urls=["https://example.com"], extract_depth="advanced", timeout=30.0
|
|
42
42
|
)
|
|
43
43
|
|
|
44
44
|
@patch("tavily.TavilyClient")
|
|
@@ -63,6 +63,30 @@ class TestTavilyExtract:
|
|
|
63
63
|
with pytest.raises(FetchError):
|
|
64
64
|
tavily_extract("https://example.com")
|
|
65
65
|
|
|
66
|
+
@patch("tavily.TavilyClient")
|
|
67
|
+
def test_empty_advanced_after_basic_error_raises_empty_content(
|
|
68
|
+
self, mock_tavily_client: MagicMock
|
|
69
|
+
) -> None:
|
|
70
|
+
mock_instance = mock_tavily_client.return_value
|
|
71
|
+
mock_instance.extract.side_effect = [
|
|
72
|
+
Exception("API Error"),
|
|
73
|
+
{"results": []},
|
|
74
|
+
]
|
|
75
|
+
|
|
76
|
+
with pytest.raises(EmptyContentError):
|
|
77
|
+
tavily_extract("https://example.com")
|
|
78
|
+
|
|
79
|
+
@patch("tavily.TavilyClient")
|
|
80
|
+
def test_timeout_is_passed_to_tavily(self, mock_tavily_client: MagicMock) -> None:
|
|
81
|
+
mock_instance = mock_tavily_client.return_value
|
|
82
|
+
mock_instance.extract.return_value = {"results": [{"raw_content": "# Hello"}]}
|
|
83
|
+
|
|
84
|
+
tavily_extract("https://example.com", timeout=5.0)
|
|
85
|
+
|
|
86
|
+
mock_instance.extract.assert_called_once_with(
|
|
87
|
+
urls=["https://example.com"], extract_depth="basic", timeout=5.0
|
|
88
|
+
)
|
|
89
|
+
|
|
66
90
|
|
|
67
91
|
class TestExtractRouting:
|
|
68
92
|
@patch("mdfetch.tavily_extract")
|
|
@@ -8,15 +8,33 @@ resolution-markers = [
|
|
|
8
8
|
|
|
9
9
|
[[package]]
|
|
10
10
|
name = "anyio"
|
|
11
|
-
version = "4.
|
|
11
|
+
version = "4.14.2"
|
|
12
12
|
source = { registry = "https://pypi.org/simple" }
|
|
13
|
+
resolution-markers = [
|
|
14
|
+
"python_full_version < '3.15'",
|
|
15
|
+
]
|
|
16
|
+
dependencies = [
|
|
17
|
+
{ name = "idna" },
|
|
18
|
+
{ name = "typing-extensions", marker = "python_full_version < '3.13' or python_full_version >= '3.15'" },
|
|
19
|
+
]
|
|
20
|
+
sdist = { url = "https://files.pythonhosted.org/packages/61/cc/a381afa6efea9f496eff839d4a6a1aed3bfafc7b3ab4b0d1b243a12573dd/anyio-4.14.2.tar.gz", hash = "sha256:cfa139f3ed1a23ee8f88a145ddb5ac7605b8bbfd8592baacd7ce3d8bb4313c7f", size = 260176, upload-time = "2026-07-12T20:29:07.082Z" }
|
|
21
|
+
wheels = [
|
|
22
|
+
{ url = "https://files.pythonhosted.org/packages/da/35/f2287558c17e29fafc8ef3daf819bb9834061cfa43bff8014f7df7f63bdc/anyio-4.14.2-py3-none-any.whl", hash = "sha256:9f505dda5ac9f0c8309b5e8bd445a8c2bf7246f3ce950121e45ea15bc41d1494", size = 125813, upload-time = "2026-07-12T20:29:05.763Z" },
|
|
23
|
+
]
|
|
24
|
+
|
|
25
|
+
[[package]]
|
|
26
|
+
name = "anyio"
|
|
27
|
+
version = "4.15.1"
|
|
28
|
+
source = { registry = "https://pypi.org/simple" }
|
|
29
|
+
resolution-markers = [
|
|
30
|
+
"python_full_version >= '3.15'",
|
|
31
|
+
]
|
|
13
32
|
dependencies = [
|
|
14
33
|
{ name = "idna" },
|
|
15
|
-
{ name = "typing-extensions", marker = "python_full_version < '3.13'" },
|
|
16
34
|
]
|
|
17
|
-
sdist = { url = "https://files.pythonhosted.org/packages/
|
|
35
|
+
sdist = { url = "https://files.pythonhosted.org/packages/a9/d2/f4d173e22df740bc37b1db102b386ba719b66e95b0f0d751f556b387e6d2/anyio-4.15.1.tar.gz", hash = "sha256:9f28306018cbd6d329e64a36d58256edff76dd996fe423bc957326e578b82a94", size = 276966, upload-time = "2026-09-05T10:42:39.44Z" }
|
|
18
36
|
wheels = [
|
|
19
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
37
|
+
{ url = "https://files.pythonhosted.org/packages/12/b8/4bd346e22b28902df4d651910f5242c28d84e4a5c2435ca5c3f797ed7e2e/anyio-4.15.1-py3-none-any.whl", hash = "sha256:6152fdbbf9a77fdec97731721bebf7c4c44f7c29b424b0065826173efc7ed101", size = 132079, upload-time = "2026-09-05T10:42:37.923Z" },
|
|
20
38
|
]
|
|
21
39
|
|
|
22
40
|
[[package]]
|
|
@@ -188,7 +206,8 @@ name = "httpx"
|
|
|
188
206
|
version = "0.28.1"
|
|
189
207
|
source = { registry = "https://pypi.org/simple" }
|
|
190
208
|
dependencies = [
|
|
191
|
-
{ name = "anyio" },
|
|
209
|
+
{ name = "anyio", version = "4.14.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.15'" },
|
|
210
|
+
{ name = "anyio", version = "4.15.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.15'" },
|
|
192
211
|
{ name = "certifi" },
|
|
193
212
|
{ name = "httpcore" },
|
|
194
213
|
{ name = "idna" },
|
|
@@ -371,7 +390,7 @@ wheels = [
|
|
|
371
390
|
|
|
372
391
|
[[package]]
|
|
373
392
|
name = "mdfetch"
|
|
374
|
-
version = "0.9.
|
|
393
|
+
version = "0.9.4"
|
|
375
394
|
source = { editable = "." }
|
|
376
395
|
dependencies = [
|
|
377
396
|
{ name = "beautifulsoup4" },
|
|
@@ -661,11 +680,11 @@ wheels = [
|
|
|
661
680
|
|
|
662
681
|
[[package]]
|
|
663
682
|
name = "soupsieve"
|
|
664
|
-
version = "2.
|
|
683
|
+
version = "2.10"
|
|
665
684
|
source = { registry = "https://pypi.org/simple" }
|
|
666
|
-
sdist = { url = "https://files.pythonhosted.org/packages/
|
|
685
|
+
sdist = { url = "https://files.pythonhosted.org/packages/71/c3/1b817965ac12dc002d7c9cd7dfffdd7d4fbf9b45763ef2ffe7b86ee94670/soupsieve-2.10.tar.gz", hash = "sha256:49e9380d7d2905463583bafe285e818c7366a9ed7b3aee221c1ac79c905d8bc0", size = 124337, upload-time = "2026-09-24T02:36:35.048Z" }
|
|
667
686
|
wheels = [
|
|
668
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
687
|
+
{ url = "https://files.pythonhosted.org/packages/66/87/5ed59e1d0290564e2027ed066c52b059892c4637741337fc0967183c8d4d/soupsieve-2.10-py3-none-any.whl", hash = "sha256:8596eb8967d744174820280fa62b4542a2e955bfaccca73ed8a13c6eb8e9b502", size = 38019, upload-time = "2026-09-24T02:36:33.709Z" },
|
|
669
688
|
]
|
|
670
689
|
|
|
671
690
|
[[package]]
|
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
"""Fallback extraction mechanisms for mdfetch."""
|
|
2
|
-
|
|
3
|
-
from __future__ import annotations
|
|
4
|
-
|
|
5
|
-
from mdfetch.exceptions import EmptyContentError, FetchError, MissingAPIKeyError
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
def tavily_extract(url: str, timeout: float = 30.0) -> str:
|
|
9
|
-
"""Extract content using the Tavily API fallback."""
|
|
10
|
-
try:
|
|
11
|
-
from tavily import TavilyClient # type: ignore[import-untyped]
|
|
12
|
-
except ImportError as e:
|
|
13
|
-
raise MissingAPIKeyError(
|
|
14
|
-
"tavily-python package is not installed. Please install it "
|
|
15
|
-
f"to use the fallback feature. {e}"
|
|
16
|
-
) from e
|
|
17
|
-
|
|
18
|
-
try:
|
|
19
|
-
client = TavilyClient()
|
|
20
|
-
except Exception as e:
|
|
21
|
-
raise MissingAPIKeyError(str(e)) from e
|
|
22
|
-
|
|
23
|
-
try:
|
|
24
|
-
response = client.extract(urls=[url], extract_depth="basic")
|
|
25
|
-
results = response.get("results", [])
|
|
26
|
-
if not results:
|
|
27
|
-
raise EmptyContentError(f"Tavily returned no results for {url}", url=url)
|
|
28
|
-
|
|
29
|
-
raw_content = results[0].get("raw_content")
|
|
30
|
-
if not raw_content:
|
|
31
|
-
raise EmptyContentError(f"Tavily returned empty content for {url}", url=url)
|
|
32
|
-
|
|
33
|
-
return str(raw_content)
|
|
34
|
-
except EmptyContentError:
|
|
35
|
-
try:
|
|
36
|
-
response = client.extract(urls=[url], extract_depth="advanced")
|
|
37
|
-
results = response.get("results", [])
|
|
38
|
-
if not results:
|
|
39
|
-
raise EmptyContentError(f"Tavily returned no results for {url}", url=url)
|
|
40
|
-
|
|
41
|
-
raw_content = results[0].get("raw_content")
|
|
42
|
-
if not raw_content:
|
|
43
|
-
raise EmptyContentError(f"Tavily returned empty content for {url}", url=url)
|
|
44
|
-
|
|
45
|
-
return str(raw_content)
|
|
46
|
-
except Exception as e:
|
|
47
|
-
if isinstance(e, EmptyContentError):
|
|
48
|
-
raise
|
|
49
|
-
raise FetchError(f"Tavily fallback failed: {e}", url=url) from e
|
|
50
|
-
except Exception:
|
|
51
|
-
# If basic threw an API error, fallback to advanced as well
|
|
52
|
-
try:
|
|
53
|
-
response = client.extract(urls=[url], extract_depth="advanced")
|
|
54
|
-
results = response.get("results", [])
|
|
55
|
-
if not results:
|
|
56
|
-
raise EmptyContentError(f"Tavily returned no results for {url}", url=url)
|
|
57
|
-
|
|
58
|
-
raw_content = results[0].get("raw_content")
|
|
59
|
-
if not raw_content:
|
|
60
|
-
raise EmptyContentError(f"Tavily returned empty content for {url}", url=url)
|
|
61
|
-
|
|
62
|
-
return str(raw_content)
|
|
63
|
-
except Exception as e:
|
|
64
|
-
raise FetchError(f"Tavily fallback failed: {e}", url=url) from e
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
[{"path":".github","blob_href":"https://github.com/stn1slv/md-fetch/blob/9104266a16d8dbd63c1bb011df63a137f53987b5/.github","start_line":2,"start_column":null,"end_line":2,"end_column":null,"annotation_level":"warning","title":"","message":"Node.js 20 is deprecated. The following actions target Node.js 20 but are being forced to run on Node.js 24: actions/checkout@v4, actions/github-script@v7, astral-sh/setup-uv@v4. For more information see: https://github.blog/changelog/2025-09-19-deprecation-of-node-20-on-github-actions-runners/","raw_details":""},{"path":".github","blob_href":"https://github.com/stn1slv/md-fetch/blob/9104266a16d8dbd63c1bb011df63a137f53987b5/.github","start_line":133,"start_column":null,"end_line":133,"end_column":null,"annotation_level":"failure","title":"","message":"Process completed with exit code 2.","raw_details":""},{"path":".github","blob_href":"https://github.com/stn1slv/md-fetch/blob/9104266a16d8dbd63c1bb011df63a137f53987b5/.github","start_line":22,"start_column":null,"end_line":22,"end_column":null,"annotation_level":"warning","title":"","message":"Failed to restore: Cache service responded with 400","raw_details":""}]
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mdfetch-0.9.2 → mdfetch-0.9.4}/.specify/extensions/git/scripts/powershell/create-new-feature.ps1
RENAMED
|
File without changes
|
|
File without changes
|