diff --git a/PUBLISHING.md b/PUBLISHING.md new file mode 100644 index 00000000..6a860b9b --- /dev/null +++ b/PUBLISHING.md @@ -0,0 +1,61 @@ +# Publishing Primer to PyPI + +The PyPI project is `useprimer` (matches the domain); the Python module is +still `primer`. Users install with `pip install useprimer` and invoke with +`primer ...`. + +## One-time setup + +1. Create a PyPI account at https://pypi.org and enable 2FA. +2. Go to https://pypi.org/manage/account/token/ and create an **API token** + scoped to the `useprimer` project (first release can use an "Entire + account" token, then narrow after upload). +3. Store the token in `~/.pypirc` or export as `TWINE_PASSWORD` at release + time. Never commit it. + +```ini +# ~/.pypirc +[pypi] +username = __token__ +password = pypi-XXXXXXXXXXXXXX +``` + +## Release workflow + +```bash +# 1. Bump version in pyproject.toml (e.g. 0.2.0 → 0.2.1) +$EDITOR pyproject.toml + +# 2. Clean + build wheel and sdist +rm -rf dist build *.egg-info +python -m build # produces dist/useprimer-X.Y.Z-py3-none-any.whl + # + dist/useprimer-X.Y.Z.tar.gz + +# 3. Sanity check +python -m zipfile -l dist/useprimer-*.whl | head -20 +python -m twine check dist/* + +# 4. Upload to TestPyPI first (strongly recommended) +python -m twine upload --repository testpypi dist/* +pip install --index-url https://test.pypi.org/simple/ useprimer + +# 5. Upload to PyPI +python -m twine upload dist/* + +# 6. Tag and push +git tag -a vX.Y.Z -m "vX.Y.Z" +git push --tags +``` + +## Verify after upload + +- https://pypi.org/project/useprimer/ — project page renders README correctly +- Project-URL links all clickable (Homepage, Docs, Repo, Issues, Demo) +- `pip install useprimer` in a clean venv → `primer --version` works + +## Why this matters beyond distribution + +PyPI surfaces the six `[project.urls]` entries as inbound links from +pypi.org — a very high-authority domain Google trusts. That's an +SEO signal for `useprimer.dev` in addition to the developer-distribution +benefit. diff --git a/README.md b/README.md index 9d42fd68..155cb1c1 100644 --- a/README.md +++ b/README.md @@ -139,7 +139,7 @@ curl -fsSL https://useprimer.dev/install.sh | sh **Or install manually:** ```bash -pip install primer # Install +pip install useprimer # Install (Python module is `primer`) primer init # Initialize database and config primer server start # Start API + dashboard primer hook install # Register the SessionEnd hook diff --git a/pyproject.toml b/pyproject.toml index c2f7e9ef..afe5ebdf 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -3,10 +3,50 @@ requires = ["hatchling"] build-backend = "hatchling.build" [project] -name = "primer" +name = "useprimer" version = "0.2.0" description = "Agent harness intelligence — measure what makes your AI coding tools effective" +readme = "README.md" +license = { text = "MIT" } requires-python = ">=3.12" +authors = [ + { name = "Charles C. Figueiredo", email = "ccf@ccf.io" }, +] +maintainers = [ + { name = "Charles C. Figueiredo", email = "ccf@ccf.io" }, +] +keywords = [ + "agent-harness", + "claude-code", + "cursor", + "codex", + "gemini", + "ai-engineering", + "developer-tools", + "analytics", + "observability", + "harness-intelligence", + "workflow-intelligence", + "finops", + "devex", + "mcp", +] +classifiers = [ + "Development Status :: 4 - Beta", + "Environment :: Web Environment", + "Framework :: FastAPI", + "Intended Audience :: Developers", + "Intended Audience :: Information Technology", + "License :: OSI Approved :: MIT License", + "Operating System :: OS Independent", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", + "Topic :: Software Development", + "Topic :: Software Development :: Libraries :: Python Modules", + "Topic :: System :: Monitoring", + "Typing :: Typed", +] dependencies = [ "fastapi>=0.115.0", "uvicorn[standard]>=0.32.0", @@ -29,6 +69,14 @@ dependencies = [ "opentelemetry-exporter-otlp-proto-http>=1.28.0", ] +[project.urls] +Homepage = "https://useprimer.dev" +Documentation = "https://useprimer.dev/docs/" +Repository = "https://github.com/ccf/primer" +Issues = "https://github.com/ccf/primer/issues" +Changelog = "https://github.com/ccf/primer/releases" +"Live Demo" = "https://demo.useprimer.dev" + [project.scripts] primer = "primer.cli.main:cli" @@ -66,6 +114,22 @@ ignore = ["S101", "S104", "S106", "S607", "B008"] exclude_dirs = ["tests", "alembic"] skips = ["B101", "B104", "B105", "B404", "B603", "B607"] +[tool.hatch.build.targets.wheel] +# PyPI project name is "useprimer" (matches useprimer.dev) but the importable +# Python module is still `primer` — same pattern as PyYAML → `yaml`. Tell +# hatch to include src/primer/ in the wheel. +packages = ["src/primer"] + [tool.hatch.build.targets.wheel.force-include] "alembic" = "primer/_alembic" "alembic.ini" = "primer/_alembic.ini" + +[tool.hatch.build.targets.sdist] +include = [ + "src/primer", + "alembic", + "alembic.ini", + "README.md", + "LICENSE", + "pyproject.toml", +] diff --git a/src/primer/cli/main.py b/src/primer/cli/main.py index 2b221286..5d46c0ba 100644 --- a/src/primer/cli/main.py +++ b/src/primer/cli/main.py @@ -17,7 +17,7 @@ @click.group() -@click.version_option(package_name="primer") +@click.version_option(package_name="useprimer") def cli() -> None: """Primer — Claude Code usage insights for your team.""" from primer.cli.config import load_config_into_env