Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 61 additions & 0 deletions PUBLISHING.md
Original file line number Diff line number Diff line change
@@ -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.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
66 changes: 65 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -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"

Expand Down Expand Up @@ -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",
]
2 changes: 1 addition & 1 deletion src/primer/cli/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down