Skip to content

Latest commit

 

History

History
175 lines (122 loc) · 9.89 KB

File metadata and controls

175 lines (122 loc) · 9.89 KB

Contributing to agami-core

Issues and PRs welcome at github.com/AgamiAI/agami-core.

Contributor License Agreement (CLA)

This project is fair-code (source-available) and is offered under a dual-license model: the Agami Functional Use License for the community, and a separate commercial license. For that model to hold, every external contribution has to come in with the rights that let us ship it under both licenses.

So before your first contribution can be merged, you sign a short Contributor License Agreement (CLA.md). In one sentence: you give Agami AI permission to license your contribution on any terms — including the fair-code FUL and a commercial license — and your contribution comes as is, without warranty or liability on your part. The full text is short; please read it — it includes a warranty/liability disclaimer.

How to sign — no separate account, no form. The first time you open a PR, our CLA bot comments on it with a link to the agreement and asks you to sign. You sign by replying with a single comment:

I have read the CLA Document and I hereby sign the CLA

The bot records your signature (stored in this repo) and flips the CLA status check to green; it stays green for all your future PRs. Until it's signed, the check blocks merge — that's the only thing it gates.

Maintainers and first-party (Agami AI) authors are allowlisted, so internal commits aren't gated; the CLA is for contributions from outside the organization.

Running the checks locally

The same gate runs in CI on every PR — ruff (lint + format), the test suite, and gitleaks (secret scan). To catch problems before you push, install the local hooks once.

Prerequisite — install uv (the only thing you need globally):

# macOS / Linux:
curl -LsSf https://astral.sh/uv/install.sh | sh
# Windows (PowerShell):
powershell -c "irm https://astral.sh/uv/install.ps1 | iex"
# any platform, if you prefer a package manager:
#   brew install uv   ·   winget install astral-sh.uv   ·   pipx install uv
# docs: https://docs.astral.sh/uv/getting-started/installation/

Everything else rides on uv — there's nothing else to install globally. From the repo root, a tiny cross-platform task runner (dev.py) wraps it all:

uv run dev.py setup     # once: wire the pre-commit hooks (ruff + gitleaks on commit, tests on push)
uv run dev.py check     # the whole gate locally — ruff + tests + gitleaks (same as CI)
uv run dev.py cover     # did the lines I changed get tested? (patch coverage)

dev.py shells out to uvx (which fetches ruff / pre-commit / pytest on demand), so it runs the same on macOS, Linux, and Windows. Other tasks: test, lint, fmt. After setup, the hooks run automatically — ruff + gitleaks on every commit, the test suite on every push. They're a convenience (bypassable with git commit --no-verify); CI is the real, unbypassable gate.

Prefer the raw tools? dev.py is only a wrapper — the equivalents are below.

Tests on their own

The suite imports the agami-core library, so install it editable with its [model,server] extras (that pulls in pydantic / pyyaml / sqlglot plus the server deps; DB-driver tests skip cleanly without a database). uvx wires it up for the run:

uvx --with pytest-cov --with pytest-xdist --with-editable "packages/agami-core[model,server]" pytest tests/ -q -n auto

-n auto runs one worker per core, the way CI does. A test that passes alone and fails under it is sharing state with another test — fix the sharing rather than dropping the flag.

The privacy test (tests/test_privacy_no_network.py) is a contract: no shipped script may make a network call — adding a network-egress primitive fails the build.

Did I test the code I changed?

uv run dev.py cover reports coverage of the lines your PR touched (fails on changed lines that no test exercises) — the quickest way to confirm a change is tested, regardless of overall coverage. Under the hood that's:

uvx --with pytest-cov --with pytest-xdist --with-editable "packages/agami-core[model,server]" \
  pytest tests/ -q -n auto --cov=plugins --cov=packages/agami-core/src --cov-report=xml
uvx diff-cover coverage.xml --compare-branch=origin/main

End-to-end integration tests (Postgres + MySQL fixtures)

cd tests/integration
docker compose up -d
./test_postgres_e2e_cli.sh        # native CLI (psql)
./test_mysql_e2e_cli.sh
./test_postgres_e2e_duckdb.sh     # DuckDB (skipped if duckdb not on PATH)
docker compose down -v

Version-bump discipline (read this before any release-shaped commit)

Claude Code's plugin marketplace caches each plugin by version number. The cache key for any user who installed agami-core@1.1.0 is pinned to that version — Claude Code does not re-fetch source files until the version changes, even if the upstream main branch has moved on.

This has a real consequence: if we rename a skill, change file layouts, or remove a Bash invocation pattern from .claude/settings.json and don't bump the version, every user who installed an earlier version stays on the old code forever. They'll see stale slash commands, hit broken file paths, and have no obvious way to invalidate their cache short of deleting ~/.claude/plugins/cache/agami/agami-core/<old-version>/ by hand.

So: bump the version on any commit that changes user-visible behavior.

When to bump what

The version lives in four places across three files. They must always match:

  • .claude-plugin/marketplace.jsonmetadata.version and plugins[0].version
  • plugins/agami/.claude-plugin/plugin.jsonversion
  • packages/agami-core/pyproject.tomlversion, the one published to PyPI. release-pypi.yml fails the publish when the release tag doesn't match it, so a bump that misses this file doesn't ship a wrong version — it doesn't ship at all.

Use semver:

Change Bump
Bug fix, doc-only change, internal refactor with no user-visible behavior shift patch (1.1.0 → 1.1.1)
New feature, new skill, new database type, new optional flag — anything additive minor (1.1.0 → 1.2.0)
Skill rename, file-layout change, removed flag, default behavior change, anything that breaks an existing install major (1.1.0 → 2.0.0)

Semver is the contract: a major bump is the signal that says "this will break your config — you'll need to reinstall / re-migrate". Don't fold a breaking change into a minor bump.

The most common mistake is renaming a skill (e.g., initagami-init) or changing a file path (e.g., <artifacts_dir>/local/<profile>/<artifacts_dir>/<profile>/) without bumping. Users installed at the old version see neither rename. Always bump on those changes.

What "user-visible behavior" means

  • Slash command name changed → bump
  • File the skill writes moved → bump
  • Allowlisted Bash command removed from .claude/settings.json → bump
  • New required flag → bump
  • New SKILL.md when_to_use trigger phrase added → patch (additive)
  • New optional flag → patch (additive)
  • Typo fix in a SKILL.md description → patch
  • Test-only or scripts/README-only change → no bump needed

When in doubt, bump. Patch bumps are cheap; stale caches in the wild are not.

Migrations are NOT a substitute for version bumps

We do auto-migrate on layout changes (e.g., v1.0 single-file → v1.1 directory → v1.2 split). That doesn't replace the version bump — it complements it. Without the bump, the new code that does the migration never reaches the user's machine. Bump first; migration code runs on next invocation.

Files to touch on a release

When opening a PR that warrants a version bump, the checklist is:

  1. .claude-plugin/marketplace.json — bump both metadata.version and plugins[0].version to the same string.
  2. plugins/agami/.claude-plugin/plugin.json — bump version.
  3. packages/agami-core/pyproject.toml — bump version to that same string.
  4. Add a note to the PR description summarizing the user-visible change and which version it lands in.

Publishing is a separate, deliberate step: merging the bump ships nothing. release-pypi.yml and release-image.yml both trigger on release: published, so PyPI and GHCR only move once a GitHub release is cut with a v<version> tag matching packages/agami-core/pyproject.toml.

Record notable user-visible changes in CHANGELOG.md (Keep a Changelog format) under the version that ships them.

CI requires a changelog entry — write it in the PR that ships the change

A PR that touches plugins/ or packages/ must also touch CHANGELOG.md, or the changelog entry check fails. Add your entry under ## [Unreleased]; the release PR later renames that heading to the version.

Tests, docs, dev/ tooling and workflows are not gated — none of them reach a user's machine on an install. If a shipped change genuinely has nothing user-visible to say (an internal refactor, a comment, a docstring typo), apply the no-changelog label to the PR and the check passes.

The rule exists because of what 0.7.0 cost: 90 commits landed between v0.6.9 and v0.7.0 with an empty [Unreleased] section, so a minor release — two new skills, four new library modules, a new CI exit contract — reached the cut with no notes at all. Reconstructing them from nine merged PRs was slow, and worse, it was wrong in a way writing-at-the-time wouldn't have been: the notes shipped a CLI flag that does not exist, because a summary written weeks later describes what someone remembers building rather than what shipped. You are the only person who will ever have the context to write your entry correctly, and that moment is the PR.

A community Discord will land soon

Once it's live, the link will appear here and in agami-connect/SKILL.md.