Metadata-Version: 2.4
Name: agh
Version: 0.5.1
Summary: Self-hosted guidance distribution for coding agents
License-Expression: MIT
Project-URL: Homepage, https://github.com/giulianotesta7/AgentGuidanceHub
Project-URL: Documentation, https://github.com/giulianotesta7/AgentGuidanceHub#readme
Project-URL: Source, https://github.com/giulianotesta7/AgentGuidanceHub
Project-URL: Issues, https://github.com/giulianotesta7/AgentGuidanceHub/issues
Project-URL: Releases, https://github.com/giulianotesta7/AgentGuidanceHub/releases
Project-URL: Container, https://github.com/giulianotesta7/AgentGuidanceHub/pkgs/container/agent-guidance-hub
Keywords: agent-tools,ai-agents,cli,coding-agents,developer-tools,fastapi,self-hosted
Classifier: Development Status :: 3 - Alpha
Classifier: Environment :: Console
Classifier: Framework :: FastAPI
Classifier: Intended Audience :: Developers
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: Software Development
Classifier: Topic :: Software Development :: Build Tools
Classifier: Topic :: System :: Systems Administration
Requires-Python: >=3.11
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: fastapi>=0.110
Requires-Dist: typer>=0.12
Requires-Dist: uvicorn[standard]>=0.27
Dynamic: license-file

<div align="center">

# Agent Guidance Hub (AGH)

<p><strong>Self-hosted guidance distribution for coding agents.</strong></p>

<p>
  <a href="https://pypi.org/project/agh/"><img alt="PyPI" src="https://img.shields.io/pypi/v/agh?color=1f6feb"></a>
  <a href="https://github.com/giulianotesta7/AgentGuidanceHub/pkgs/container/agent-guidance-hub"><img alt="GHCR" src="https://img.shields.io/badge/ghcr-agent--guidance--hub-1f6feb"></a>
  <a href="https://github.com/giulianotesta7/AgentGuidanceHub/actions/workflows/ci.yml"><img alt="CI" src="https://github.com/giulianotesta7/AgentGuidanceHub/actions/workflows/ci.yml/badge.svg"></a>
  <a href="https://github.com/giulianotesta7/AgentGuidanceHub/releases"><img alt="Release" src="https://img.shields.io/github/v/release/giulianotesta7/AgentGuidanceHub"></a>
</p>

<p>
  <a href="#install">install</a> · <a href="#quick-start">quick start</a> · <a href="#how-agh-works">how it works</a> · <a href="#server-operations">server ops</a> · <a href="#development">development</a> · <a href="README.es.md">español</a>
</p>

</div>

[Español](README.es.md)

---

![AGH workspace pull demo](assets/agh-workspace-demo.gif)

**AGH gives teams one place to publish, version, assign, and pull reusable agent instructions and skills into their repos.**

Use it when agent guidance needs the same discipline as infrastructure: reproducible changes, clear ownership, and self-hosted runtime. AGH is early, Docker-first, and published as a PyPI package, Homebrew formula, and GHCR server image.

- **Centralize guidance**: publish shared `AGENTS.md`, `CLAUDE.md`, and skill files once.
- **Version every change**: packages are immutable SemVer releases assigned to projects.
- **Keep repos deterministic**: each workspace records `.agh/lock.toml` and applies only the selected agent target.
- **Run it yourself**: host the server with Docker, SQLite, and persistent `/data` storage.

---

## Install

Linux / macOS:

```bash
brew install giulianotesta7/tap/agh
```

or install with script:

```bash
curl -fsSL https://raw.githubusercontent.com/giulianotesta7/AgentGuidanceHub/main/scripts/install.sh | sh
```

or install with uv:

```bash
uv tool install --force agh
```

from a checkout:

```bash
git clone https://github.com/giulianotesta7/AgentGuidanceHub.git
cd AgentGuidanceHub
uv tool install --force .
```

check the CLI:

```bash
agh --help
```

Run the server with the published Docker image:

```bash
docker compose up -d
curl http://127.0.0.1:8912/api/v1/health
```

The default Compose image is:

```text
ghcr.io/giulianotesta7/agent-guidance-hub:${AGH_IMAGE_TAG:-latest}
```

Pin production deployments with a release tag:

```bash
AGH_IMAGE_TAG=0.2.0 docker compose up -d
```

## Quick start

Read the first owner token on the host running AGH:

```bash
docker run --rm -v agh-data:/data busybox \
  cat /data/secrets/initial_owner_token
```

Then log in from your machine:

```bash
agh login \
  --url <instance-url> \
  --email owner@example.com \
  --token "<initial-owner-token>"
```

Check the saved config. AGH masks the token:

```bash
agh config show
```

Create a project with the repo URL developers use in git remotes:

```bash
agh project create "Agent Guidance Hub" \
  --repo-url https://github.com/giulianotesta7/AgentGuidanceHub.git
```

Work from a linked repo:

```bash
agh sync
agh agent select opencode # or: agh agent select claude
agh pull --dry-run
agh pull
agh agent
agh agent show
```

## How AGH works

```text
Package author ── publish ──▶ AGH server ── assign ──▶ Project
                              │                         │
                              │                         ▼
                         SQLite + /data          Repo workspace
                                                       │
                                                       ├─ AGENTS.md + .opencode/skills/
                                                       └─ CLAUDE.md + .claude/skills/
```

| Piece | What it does |
|-------|--------------|
| Packages | Shared instructions, skills, or both. Published versions are immutable. |
| Projects | One git repository plus the package versions it should use. |
| Workspaces | A local repo linked with `agh sync`, one selected agent, and a committed lockfile. |

<details>
<summary><strong>Package authoring</strong></summary>

A package starts with this shape:

```text
my-package/
├── agh.package.toml
├── instructions/
│   ├── AGENTS.md
│   └── CLAUDE.md
└── skills/
    └── reviewer/
        └── SKILL.md
```

Create a template:

```bash
agh package init ./my-package --domain acme --name onboarding --version 1.0.0
```

The manifest starts as:

```toml
domain = "acme"
name = "onboarding"
version = "1.0.0"
description = "TODO"
```

Useful starter flags:

- `--with-agents` creates `instructions/AGENTS.md`.
- `--with-claude` creates `instructions/CLAUDE.md`.
- `--with-skill NAME` creates `skills/NAME/SKILL.md`.

Allowed files:

- `agh.package.toml`
- `instructions/AGENTS.md`
- `instructions/CLAUDE.md`
- `skills/<name>/SKILL.md`

Rules:

- A package can contain instructions, skills, or both.
- It must include at least one instruction file or skill.
- `version` must be exact SemVer, such as `1.0.0`.
- Published versions are immutable. Publish `1.0.1` for changes.
- Do not publish `latest`. Use `latest` only when assigning packages to projects.
- Use UTF-8 text files. Do not include symlinks.

Publish and list packages:

```bash
agh package publish ./my-package
agh package list
```

Example publish output:

```text
Published acme/onboarding@1.0.0.
Package ID: pkg_...
Checksum: sha256:...
```

</details>

<details>
<summary><strong>Project assignment</strong></summary>

A project is an AGH record linked to one git repository.

```bash
agh project create "Agent Guidance Hub" \
  --repo-url https://github.com/giulianotesta7/AgentGuidanceHub.git
agh project list
agh project get prj_...
agh project update prj_... --name "App API"
agh project delete prj_...
```

Project commands that take a project reference accept `prj_...` ids or exact project names. All-digit values are treated as ids, and project names cannot contain only digits.

Assign packages interactively or directly:

```bash
agh project package add
agh project package add prj_...
agh project package add prj_... acme/onboarding@latest
agh project package add prj_... onboarding@1.0.0
agh project package list prj_...
agh project package update prj_... asn_... --package-ref acme/onboarding@1.0.0
agh project package remove prj_... asn_...
```

- `agh project package add` guides you through project selection, unassigned package selection, and confirmation.
- `agh project package add <project>` skips project selection, shows unassigned packages for that project, and asks for confirmation.
- `agh project package add <project> <package-ref>` assigns directly without prompts, which fits scripts and CI.

`asn_...` identifies the project-to-package assignment. Package inputs accept `pkgv_...`, `domain/name@version`, and exact `name@version` refs. No-domain refs must match a single package domain, otherwise AGH reports a conflict. Use an exact version to pin the project. Use `latest` with domain-qualified refs when the project should resolve to the newest published version during pull.

During workspace pull, AGH writes the resolved concrete version and checksum to `.agh/lock.toml`.

</details>

<details>
<summary><strong>Collection administration</strong></summary>

Collections group skill-only packages that members install through `agh skill ...`. Owners and admins manage collections with the `agh collection` commands. These admin commands are separate from the consumer `agh skill ...` flow.

```bash
agh collection create "Team Skills" --description "Shared review skills"
agh collection list
agh collection get "Team Skills"
agh collection update "Team Skills" --name "Review Skills"
agh collection delete "Review Skills"
```

Collection commands that take a collection reference accept `col_...` ids or exact active collection names. Canonical ids skip name resolution; exact names resolve through the active-collection by-name endpoint.

Assign skill-only packages to a collection:

```bash
agh collection package list "Team Skills"
agh collection package add "Team Skills" acme/reviewer@latest
agh collection package add "Team Skills" acme/reviewer@latest --position 2
agh collection package update "Team Skills" casn_... --package-ref acme/reviewer@1.0.0 --inactive
agh collection package remove "Team Skills" casn_...
```

`casn_...` identifies the collection-to-package assignment. Collection packages must be skill-only: AGH rejects packages that contain `instructions/AGENTS.md`, `instructions/CLAUDE.md`, or no skills, and the CLI surfaces that server validation. Package inputs accept `pkgv_...`, `domain/name@version`, and exact `name@version` refs. Unlike `agh project package add`, `agh collection package add` requires both the collection and the package reference — there is no interactive picker, since only the server can validate skill-only packages.

</details>

<details>
<summary><strong>Global skills</strong></summary>

Global skills are collection-backed tools installed into the selected agent's native user-level skill directory. A collection is an AGH-managed group of skill-only packages made available by the server; global skills are separate from workspace package assignments.

| Command | What it does |
|---------|--------------|
| `agh skill list` | Lists skills available from active collections. |
| `agh skill install acme/commenting@latest reviewer` | Resolves, downloads, and installs `reviewer` globally for the selected agent. |
| `agh skill remove reviewer` | Removes the global install for the selected or default agent. |
| `agh skill installed` | Lists locally installed global skills from the global-skill lock for the selected or default agent. |
| `agh skill agent show` | Shows the saved default agent for global skill commands. |
| `agh skill agent select opencode` | Saves OpenCode as the default for global skill commands. |
| `agh skill agent clear` | Clears the global skill default agent. |

If no default is saved and `--agent` is omitted, AGH prompts with:

```text
Select the agent for global skills:
```

Install paths:

- OpenCode global skills: `~/.config/opencode/skills`
- Claude global skills: `~/.claude/skills`

Global skill state is local user state under `XDG_STATE_HOME/agh` or `~/.local/state/agh`; it does not change workspace pull behavior or `.agh/lock.toml`. If a remove operation reports a filesystem recovery error, follow the command output: the lock and native skill file may need manual cleanup before retrying.

</details>

<details>
<summary><strong>Workspace pull and Git state</strong></summary>

| Command | What it does |
|---------|--------------|
| `agh sync` | Matches the git remote to an AGH project and writes `.agh/project.toml`. |
| `agh agent` / `agh agent show` | Shows Claude Code/OpenCode availability and the current local selection. |
| `agh agent select claude` | Selects Claude Code for this workspace. |
| `agh agent select opencode` | Selects OpenCode for this workspace. |
| `agh agent clear` | Removes the local workspace agent selection. |
| `agh pull --dry-run` | Fetches the server plan without writing repo files. |
| `agh pull` | Applies instructions and skills for the selected agent and writes `.agh/lock.toml`. |
| `agh pull --force` | Replaces conflicted AGH blocks or skill targets. |

There is no `both` option. If no agent is selected, interactive `agh pull` asks which agent to use. Skip exits with code `2` and writes nothing.

Instruction files use managed blocks:

```md
<!-- AGH-BEGIN package="<package-ref>" artifact="instructions/AGENTS.md" checksum="sha256:..." -->
Project instructions from AGH live here.
<!-- AGH-END package="<package-ref>" -->
```

If you edit inside the block, the next `agh pull` exits with conflict code `3`. Use `agh pull --force` when AGH should replace it.

Skills go where agents already look:

```text
.claude/skills/<skill>/SKILL.md
.opencode/skills/<skill>/SKILL.md
```

AGH tries a relative symlink to `.agh-cache/packages/...`. If the OS rejects symlinks, AGH copies the file. The lockfile records the mode:

```toml
[[packages]]
package_ref = "acme/onboarding@1.0.0"

[[artifacts]]
package_ref = "acme/onboarding@1.0.0"
path = "skills/reviewer/SKILL.md"
target_path = ".opencode/skills/reviewer/SKILL.md"
mode = "symlink" # or mode = "copy"
source = ".agh-cache/packages/acme/onboarding/1.0.0/skills/reviewer/SKILL.md"
```

Commit shared workspace state:

- `.agh/project.toml`
- `.agh/lock.toml`
- generated `AGENTS.md` / `CLAUDE.md` when your team wants those reviewed
- generated `.claude/skills/` or `.opencode/skills/` when your team wants skills reviewed

Do not commit local cache state:

```gitignore
.agh-cache/
```

AGH downloads packages to `.agh-cache/packages/` and stores each developer's agent choice in `.agh-cache/preferences.toml`. If skill targets are symlinks, a fresh clone needs `agh pull` to rebuild the cache before those links resolve.

Exit codes:

| Code | Meaning |
|------|---------|
| `0` | Success or no changes. |
| `1` | Runtime/API/download failure. |
| `2` | Local validation, malformed manifest, or missing/skipped agent selection. |
| `3` | Conflict. |
| `4` | Authentication/authorization failure. |
| `5` | Workspace is not linked; run `agh sync`. |

</details>

## Server operations

The first owner token is written once:

```text
/data/secrets/initial_owner_token
```

Store it. AGH will not show it again. The server stores token hashes, not plaintext tokens.

| Role | Use |
|------|-----|
| `owner` | Full admin access, including bootstrap ownership. |
| `admin` | Manage users, projects, packages, and assignments. |
| `member` | Day-to-day workspace access. |

Admin commands:

```bash
agh user list
agh user create user@example.com --role admin
agh user show user@example.com
agh user update usr_... --role member
agh user delete usr_...
agh token rotate
agh token reset usr_...
agh config show
```

User commands that take a user reference accept `usr_...` ids or exact emails.

`agh config show` masks the saved token as `token = ****`.

Runtime state lives under `/data`:

| Path | Purpose |
|------|---------|
| `/data/agh.sqlite3` | SQLite database. |
| `/data/packages/` | Published package payloads. |
| `/data/logs/agh.log` | Server log. |
| `/data/secrets/initial_owner_token` | First owner token, created once. |

The image owns `/data` as `agh:agh` (`10001:10001`) at build time.
Named Docker volumes are initialized from that image-owned `/data` tree.
Bind mounts must already be writable by UID/GID `10001:10001`; the container does not repair host ownership.

Direct Docker run:

```bash
docker run --rm -p 8912:8912 -v agh-data:/data \
  -e AGH_BOOTSTRAP_OWNER_EMAIL=owner@example.com \
  ghcr.io/giulianotesta7/agent-guidance-hub:0.2.0
```

Healthcheck:

```bash
curl http://127.0.0.1:8912/api/v1/health
```

Backup at least:

```text
/data/agh.sqlite3
/data/packages/
/data/secrets/
```

Upgrade by pinning the next image tag and restarting:

```bash
AGH_IMAGE_TAG=0.2.0 docker compose pull
AGH_IMAGE_TAG=0.2.0 docker compose up -d
```

## Development

```bash
uv sync
uv run pytest
uv run uvicorn agh.server.app:app --host 0.0.0.0 --port 8912
```

Local data uses `.agh-data/` by default.

Contributing and security:

- [Contributing](CONTRIBUTING.md)
- [Security](SECURITY.md)
