Skip to main content

Let a Python package carry its own agent integrations and install them into whatever agent harness is present.

Project description

AgentSquire

A reusable Python library + CLI that lets a Python package carry its own agent integrations (Agent Skills) and install them into whatever agent harness is present - the executable is the framework.

Your CLI ships its skills as package data inside its own wheel, adds agentsquire as a plain pip dependency, and mounts a ready-made subcommand group. Your users only ever see your tool:

$ awiki skills install
installed awiki-search -> /home/you/.claude/skills/awiki-search

Supported harnesses at launch: Claude Code, pi, Hermes, and opencode. Detection is by marker directory; every operation is local (no network in any verb). Each harness's directories, scopes, and behaviour are recorded in docs/harnesses.md.

Installation

pip install agentsquire

This installs the library and its own CLI, squire (aliased agentsquire). As a consumer you normally add agentsquire to your package's dependencies rather than having users install it directly - it rides in your wheel, and your users only ever see your tool.

agentsquire dogfoods its own contract: it carries the developing-with-agentsquire skill and its reference docs as package data. When you build an app on agentsquire, install that skill into your own agent harness so the harness has the integration know-how on hand while it works:

squire skills install

That copies developing-with-agentsquire (how to ship skills, mount the group, and wire the staleness hook) into every detected harness. The same reference docs are served at the terminal from the installed wheel - no checkout needed:

squire guide              # topics: api, harnesses, integration
squire guide api          # the Python API reference
squire guide integration  # this consumer integration guide

From source

Clone the repository and install it into your environment:

git clone https://github.com/TacoTakumi/AgentSquire.git
cd AgentSquire
pip install .

Add -e (pip install -e .) for an editable install if you plan to work on agentsquire itself. The repo uses uv: uv sync provisions a dev environment and uv run pytest runs the test suite.

Consumer integration guide

1. Ship skills as package data

Lay each skill out as a directory containing a SKILL.md (the agentskills.io format) under a skills/ resource inside your importable package:

your_pkg/
    __init__.py
    skills/
        my-skill/
            SKILL.md
            reference.md

The skills ride inside your wheel; make sure your build backend includes package data (hatchling includes it by default, setuptools needs include-package-data). No source checkout is needed at run time - skills are enumerated straight from the installed wheel.

2. Mount the subcommand group

One call returns a click group with install, status, update, and uninstall subcommands, parameterized by your package name, the resource path (default "skills"), and the default scope.

For a click CLI:

import click

from agentsquire.cli import skills_command_group


@click.group()
def cli():
    """Your CLI."""


cli.add_command(skills_command_group("your_pkg", default_scope="user"))

For a typer CLI, mount onto the underlying click command:

import typer
import typer.main

from agentsquire.cli import skills_command_group

app = typer.Typer()


@app.callback()
def main():
    """Your CLI."""


cli = typer.main.get_command(app)
cli.add_command(skills_command_group("your_pkg", default_scope="user"))

Your users now run your-cli skills install and friends. Every subcommand takes --scope user|project (overriding your declared default) and --harness NAME (default: all detected harnesses).

Choosing the default scope: user installs into the harness's per-user skills directory and follows the user everywhere - right for general-purpose tools. project installs into the current project's directory - right for skills that only make sense inside a repository that uses your tool.

3. Surface updates proactively (optional)

Place the one-call staleness hook at your CLI entry point. When installed skills have updates available it prints a single advisory line on stderr, for example:

your-cli: a skills update is available for 1 skill (alpha); run `your-cli skills update`

The hook is notice-only: it never prompts, never reads stdin, and never updates anything itself - the explicit skills update verb stays the sole updater. It writes nothing to stdout, never changes your exit code, and swallows its own errors, so it can never break the command it runs inside:

from agentsquire import BundledPackageDataSource, check_stale


def main():
    check_stale(
        BundledPackageDataSource("your_pkg"),
        prog_name="your-cli",
        update_command="your-cli skills update",
    )
    # ... the rest of your entry point

The notice shows unless a suppression gate holds: CI set to a non-empty value, or AGENTSQUIRE_NO_UPDATE_CHECK set to a non-empty value. It is not gated on an interactive terminal - it fires on non-TTY stderr too, so an agent harness that runs your CLI with captured stderr still sees that an update is available. Suppression is presence-disables, the NO_COLOR convention: any non-empty value disables the notice (CI=false and AGENTSQUIRE_NO_UPDATE_CHECK=0 both suppress), while an empty string is treated as unset.

4. Mark your package as skill-carrying (optional)

One pyproject line registers your package under the agentsquire.skills entry-point group. Nothing reads it today - it is reserved for a future environment-wide listing of skill-carrying packages and changes no behaviour:

[project.entry-points."agentsquire.skills"]
your_pkg = "your_pkg"

5. Ship repo-level skills from your repo root (optional)

Skills can also live at your repo root - in a top-level skills/ directory outside your importable package - and still ship to your users. This suits skills that are developed and used from the repository itself. agentsquire resolves the union of your package-data skills (Root A, section 1) and these repo-level skills (Root B), so skills install and friends see both.

Root B resolves wheel-first then checkout: in a built wheel it is read from a fixed in-package location your_pkg/_repo_skills; in an editable checkout it is read straight from <repo>/skills (found by walking up to the first directory with a pyproject.toml and a skills/ subdir). To copy the repo-root skills/ into your wheel at that location, add this single force-include line to your pyproject:

[tool.hatch.build.targets.wheel.force-include]
"skills" = "your_pkg/_repo_skills"

This is the only packaging change agentsquire requires: agentsquire ships no build-hook plugin and adds no build-time dependency to your project.

The two roots must be disjoint - a skill name present in both roots is a packaging mistake, not an override, and raises DuplicateSkillError. Enforce it in your test suite in one line (it runs wherever agentsquire is already a runtime dependency, so it protects editable installs too, where a build hook cannot run):

from agentsquire import verify_skill_roots


def test_skill_roots_are_disjoint():
    verify_skill_roots("your_pkg")

Optional completeness guard: force-include copies the directory, but a stray ignore rule could still drop a skill from the wheel. To fail the build loudly if that ever happens, drop this self-contained hatch_build.py beside your pyproject and point hatchling at it:

[tool.hatch.build.targets.wheel.hooks.custom]
path = "hatch_build.py"
# hatch_build.py - force-include every repo-root skill and fail the build if the
# skills/ directory has been dropped or emptied. Self-contained; not an
# agentsquire dependency.
from pathlib import Path

from hatchling.builders.hooks.plugin.interface import BuildHookInterface


class CustomBuildHook(BuildHookInterface):
    def initialize(self, version, build_data):
        skills = Path(self.root) / "skills"
        if not skills.is_dir():
            raise ValueError("hatch_build.py: repo-root skills/ is missing")
        found = [d for d in sorted(skills.iterdir()) if d.is_dir()]
        if not found:
            raise ValueError("hatch_build.py: repo-root skills/ has no skills")
        force_include = build_data.setdefault("force_include", {})
        for skill in found:
            force_include[str(skill)] = f"your_pkg/_repo_skills/{skill.name}"

agentsquire never wires itself into your build: the completeness hook is your own optional file, and the force-include line above is the only mandatory change.

The provenance and update model

Installs are plain copies - no symlinks, no lockfile, no references back into site-packages - so an installed skill survives upgrade or removal of your package. Each installed SKILL.md carries a provenance stamp in its frontmatter metadata.agentsquire map: installer, installer_version, source_package, source_version, and content_hash. The skill body is byte-identical to what you shipped.

status classifies every skill by local hash compares only (no network, ever): not-installed, up-to-date, update-available (your shipped copy moved on), or locally-modified (the user edited the install, the directory carries no stamp, or a symlink sits at the target - none of them ours to touch). update refreshes update-available skills and skips locally modified ones unless --force is given; uninstall removes only directories whose stamp names your package. User content is never silently overwritten or deleted - a pre-existing symlink at a target (a common hand-wired setup) is reported and skipped, never followed or clobbered.

Python API

Everything the CLI group does is available as plain Python - enumerate, detect, install, status, update, uninstall, and the staleness check - with no CLI involved. See docs/api.md for the reference.

License

MIT.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

agentsquire-0.5.0.tar.gz (79.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

agentsquire-0.5.0-py3-none-any.whl (45.6 kB view details)

Uploaded Python 3

File details

Details for the file agentsquire-0.5.0.tar.gz.

File metadata

  • Download URL: agentsquire-0.5.0.tar.gz
  • Upload date:
  • Size: 79.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.3

File hashes

Hashes for agentsquire-0.5.0.tar.gz
Algorithm Hash digest
SHA256 51c1454182523debde6eb565ca6e975a0259e2b52389b55a59dd0f77f7f0da85
MD5 ea5b08cde044a3057bfb6101169e02bb
BLAKE2b-256 5f4f0ff5a47a4d7f797329debe7821c0d3821551d194b05919871e4ce6cd3f27

See more details on using hashes here.

File details

Details for the file agentsquire-0.5.0-py3-none-any.whl.

File metadata

  • Download URL: agentsquire-0.5.0-py3-none-any.whl
  • Upload date:
  • Size: 45.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.3

File hashes

Hashes for agentsquire-0.5.0-py3-none-any.whl
Algorithm Hash digest
SHA256 8e15ef707ac4c049a5ea1c346ba3f8476f9fde1786443d39c59a120c3ce30993
MD5 b1b99be8f702245fa2af0f97663f0e84
BLAKE2b-256 f1679a5d4249b1565a8c9913a77f810d315b7d8564e87b2a8f26a79ad9b3b16b

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page