Skip to content

Commit e49c96f

Browse files
committed
Scaffold project: pyproject, README, license, CI, smoke test
1 parent 9319439 commit e49c96f

14 files changed

Lines changed: 973 additions & 0 deletions

File tree

.github/workflows/test.yml

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
name: test
2+
on:
3+
push:
4+
branches: [main]
5+
pull_request:
6+
branches: [main]
7+
8+
jobs:
9+
test:
10+
runs-on: ubuntu-latest
11+
steps:
12+
- uses: actions/checkout@v4
13+
- name: Install uv
14+
uses: astral-sh/setup-uv@v3
15+
with:
16+
version: "latest"
17+
- name: Set up Python
18+
run: uv python install 3.11
19+
- name: Install deps
20+
run: uv sync --all-extras
21+
- name: Lint
22+
run: uv run ruff check src tests
23+
- name: Test
24+
run: uv run pytest --cov=sift --cov-report=term

.gitignore

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,3 +216,12 @@ __marimo__/
216216

217217
# Streamlit
218218
.streamlit/secrets.toml
219+
220+
# sift
221+
.venv/
222+
.vault-ingest/
223+
*.egg-info/
224+
dist/
225+
.coverage
226+
.pytest_cache/
227+
.ruff_cache/

.python-version

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
3.11

README.md

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
# sift
2+
3+
[![Build](https://github.com/carloscae/sift/actions/workflows/test.yml/badge.svg)](https://github.com/carloscae/sift/actions/workflows/test.yml)
4+
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
5+
[![Python 3.11+](https://img.shields.io/badge/python-3.11+-blue.svg)](https://python.org)
6+
7+
**The working bridge between social media and your second brain.**
8+
9+
Drop a link to a TikTok, an X thread, a YouTube short, an Instagram reel, a Reddit comment — or a voice note, screenshot, PDF — into your Obsidian vault. Get a clean, transcribed, summarized markdown note back. URL in, note out. Local-first, your own AI keys, opinionated.
10+
11+
We maintain extractors for the platforms that fight scraping the hardest, so you don't have to.
12+
13+
## Status
14+
15+
Pre-alpha. v0.1.0 currently supports YouTube, TikTok, and generic article URLs, with OpenRouter as the AI backend. Roadmap → Instagram, X (video + text threads), Reddit, BlueSky, plus a local-model backend that runs fully offline.
16+
17+
## Quickstart
18+
19+
```bash
20+
pip install sift
21+
sift init ~/Documents/MyVault
22+
export OPENROUTER_API_KEY=sk-or-...
23+
sift add https://tiktok.com/@user/video/123 --now
24+
```
25+
26+
Open `~/Documents/MyVault/captures/` — your note is there.
27+
28+
## Why not just use \<obvious alternative\>?
29+
30+
Most "save to second brain" tools either stop at clipping the URL (and leave you to actually read/watch the thing later) or work only for clean articles, breaking entirely on video platforms. Sift commits to maintaining the gnarly per-platform extraction layer — the part that breaks every few weeks when TikTok rotates its anti-scrape defenses. That maintenance commitment is the differentiator.
31+
32+
## Configuration
33+
34+
See `vault-ingest.yaml.example` for a full config. Defaults work for most users.
35+
36+
## License
37+
38+
MIT

pyproject.toml

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
[project]
2+
name = "sift"
3+
version = "0.1.0"
4+
description = "The working bridge between social media and your second brain. URL in, clean vault note out."
5+
readme = "README.md"
6+
license = { text = "MIT" }
7+
authors = [{ name = "Carlos Vargas", email = "carlos@carloscae.com" }]
8+
requires-python = ">=3.11"
9+
dependencies = [
10+
"click>=8.1",
11+
"pydantic>=2.6",
12+
"pyyaml>=6.0",
13+
"httpx>=0.27",
14+
"structlog>=24.1",
15+
"yt-dlp",
16+
"readability-lxml>=0.8.1",
17+
"lxml>=5.0",
18+
]
19+
20+
[project.scripts]
21+
sift = "sift.cli:main"
22+
23+
[project.urls]
24+
Homepage = "https://github.com/carloscae/sift"
25+
Issues = "https://github.com/carloscae/sift/issues"
26+
27+
[build-system]
28+
requires = ["hatchling"]
29+
build-backend = "hatchling.build"
30+
31+
[tool.uv]
32+
dev-dependencies = [
33+
"pytest>=8.0",
34+
"pytest-cov>=5.0",
35+
"respx>=0.21",
36+
"ruff>=0.5",
37+
]
38+
39+
[tool.pytest.ini_options]
40+
testpaths = ["tests"]
41+
addopts = "-v --tb=short"
42+
43+
[tool.ruff]
44+
target-version = "py311"
45+
line-length = 100
46+
47+
[tool.ruff.lint]
48+
select = ["E", "F", "I", "B", "UP", "SIM"]

src/sift/__init__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
from sift.version import __version__
2+
3+
__all__ = ["__version__"]

src/sift/version.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
__version__ = "0.1.0"

tests/__init__.py

Whitespace-only changes.

tests/conftest.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
from pathlib import Path
2+
3+
import pytest
4+
5+
6+
@pytest.fixture
7+
def tmp_vault(tmp_path: Path) -> Path:
8+
"""A scratch vault dir with raw/, captures/, .vault-ingest/ created."""
9+
(tmp_path / "raw").mkdir()
10+
(tmp_path / "captures").mkdir()
11+
(tmp_path / ".vault-ingest").mkdir()
12+
return tmp_path
13+
14+
15+
@pytest.fixture(autouse=True)
16+
def _clear_extractor_registry():
17+
"""Drop any registered extractors before each test so module-level state
18+
from `sift.extractors` side-effect imports doesn't leak between tests."""
19+
try:
20+
from sift.extractors.base import clear_registry
21+
clear_registry()
22+
except ImportError:
23+
# base.py doesn't exist yet (Phase 0–1 tests run before Phase 2).
24+
pass
25+
yield

tests/enricher/__init__.py

Whitespace-only changes.

0 commit comments

Comments
 (0)