agentsquire 0.2.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- agentsquire-0.2.2/.gitignore +12 -0
- agentsquire-0.2.2/CHANGELOG.md +102 -0
- agentsquire-0.2.2/LICENSE +21 -0
- agentsquire-0.2.2/PKG-INFO +190 -0
- agentsquire-0.2.2/README.md +166 -0
- agentsquire-0.2.2/docs/api.md +229 -0
- agentsquire-0.2.2/docs/harnesses.md +120 -0
- agentsquire-0.2.2/pyproject.toml +58 -0
- agentsquire-0.2.2/src/agentsquire/__init__.py +100 -0
- agentsquire-0.2.2/src/agentsquire/cli.py +198 -0
- agentsquire-0.2.2/src/agentsquire/harnesses.py +141 -0
- agentsquire-0.2.2/src/agentsquire/hashing.py +59 -0
- agentsquire-0.2.2/src/agentsquire/py.typed +0 -0
- agentsquire-0.2.2/src/agentsquire/roots.py +50 -0
- agentsquire-0.2.2/src/agentsquire/skills.py +95 -0
- agentsquire-0.2.2/src/agentsquire/sources.py +120 -0
- agentsquire-0.2.2/src/agentsquire/staleness.py +76 -0
- agentsquire-0.2.2/src/agentsquire/stamping.py +74 -0
- agentsquire-0.2.2/src/agentsquire/verbs.py +335 -0
- agentsquire-0.2.2/tests/test_api_lifecycle.py +79 -0
- agentsquire-0.2.2/tests/test_backends.py +110 -0
- agentsquire-0.2.2/tests/test_bundled_source.py +173 -0
- agentsquire-0.2.2/tests/test_cli_group.py +268 -0
- agentsquire-0.2.2/tests/test_docs_snippets.py +101 -0
- agentsquire-0.2.2/tests/test_entry_point.py +119 -0
- agentsquire-0.2.2/tests/test_harness_registry.py +207 -0
- agentsquire-0.2.2/tests/test_hash.py +116 -0
- agentsquire-0.2.2/tests/test_install.py +299 -0
- agentsquire-0.2.2/tests/test_package.py +5 -0
- agentsquire-0.2.2/tests/test_reference_doc.py +78 -0
- agentsquire-0.2.2/tests/test_root_overrides.py +151 -0
- agentsquire-0.2.2/tests/test_skill_model.py +139 -0
- agentsquire-0.2.2/tests/test_source_seam.py +185 -0
- agentsquire-0.2.2/tests/test_staleness_check.py +340 -0
- agentsquire-0.2.2/tests/test_status.py +186 -0
- agentsquire-0.2.2/tests/test_uninstall.py +164 -0
- agentsquire-0.2.2/tests/test_update.py +219 -0
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to AgentSquire are documented here. The format is based on
|
|
4
|
+
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and the project aims
|
|
5
|
+
to follow [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
|
+
|
|
7
|
+
The version is declared in two places that are kept in sync by the release
|
|
8
|
+
tooling: `__version__` in `src/agentsquire/__init__.py` and `version` in
|
|
9
|
+
`pyproject.toml`.
|
|
10
|
+
|
|
11
|
+
## [0.2.2]
|
|
12
|
+
|
|
13
|
+
### Changed
|
|
14
|
+
- **Source distributions now ship an explicit, minimal file set.** The sdist is
|
|
15
|
+
scoped with `[tool.hatch.build.targets.sdist]` to `src/`, `tests/`,
|
|
16
|
+
`CHANGELOG.md`, and the two public docs (`docs/api.md`, `docs/harnesses.md`),
|
|
17
|
+
plus the always-included `pyproject.toml`/`README.md`/`LICENSE`. Previously the
|
|
18
|
+
sdist inherited hatchling's default (every VCS-tracked file), so
|
|
19
|
+
repository-only files could ride along; the wheel was already limited to the
|
|
20
|
+
package. This is a default-deny allowlist - files not listed never ship.
|
|
21
|
+
|
|
22
|
+
## [0.2.1]
|
|
23
|
+
|
|
24
|
+
### Fixed
|
|
25
|
+
- **A pre-existing symlink at a target no longer crashes install/update or
|
|
26
|
+
gets clobbered (BUG-02).** The classifier and copy guard tested the target
|
|
27
|
+
with `Path.exists()`, which follows a symlink: a dangling link read as
|
|
28
|
+
not-installed and then `install` hit `FileExistsError`, while a live link
|
|
29
|
+
reached `shutil.rmtree` on `update --force` and raised "Cannot call rmtree
|
|
30
|
+
on a symbolic link". Detection now uses `is_symlink`, so a symlink (dangling
|
|
31
|
+
or live) classifies as `LOCALLY_MODIFIED` - present but not ours. `install`
|
|
32
|
+
and `update` (without `--force`) skip it with a reason naming the symlink;
|
|
33
|
+
`update --force` unlinks the link (never `rmtree`s through it) before
|
|
34
|
+
writing a real install; `uninstall` leaves it in place with a reason.
|
|
35
|
+
Symlinking skills into `~/.claude/skills` is a mainstream hand-wired setup,
|
|
36
|
+
so anyone migrating to `skills install` from it hit this on the first run.
|
|
37
|
+
|
|
38
|
+
## [0.2.0] - 2026-07-10
|
|
39
|
+
|
|
40
|
+
### Added
|
|
41
|
+
- **`AGENTSQUIRE_HOME` / `AGENTSQUIRE_PROJECT` root overrides.** When
|
|
42
|
+
`check_stale` and the mounted `skills` subcommands are wired the production
|
|
43
|
+
way (no `home=`/`project=` arguments, so each invocation resolves the real
|
|
44
|
+
roots), these env vars redirect the home and project roots. A consumer's
|
|
45
|
+
CLI-level test can point the wired hook and the subcommands at fixture
|
|
46
|
+
directories by setting two variables instead of monkeypatching `Path.home`
|
|
47
|
+
and chdir. Explicit `home=`/`project=` arguments still take precedence; an
|
|
48
|
+
empty value is treated as unset (the `NO_COLOR` convention). Documented in
|
|
49
|
+
the `docs/api.md` "Testing your integration" section.
|
|
50
|
+
|
|
51
|
+
### Fixed
|
|
52
|
+
- **`check_stale` now surfaces the update notice on non-TTY stderr.** The
|
|
53
|
+
hook previously required stderr to be an interactive TTY, which silenced
|
|
54
|
+
it in exactly the case that matters most for an agent-skills tool: an
|
|
55
|
+
agent harness runs the consumer CLI with captured (non-TTY) stderr and so
|
|
56
|
+
never saw that an update was available. The interactive-TTY gate is
|
|
57
|
+
removed; `CI` and `AGENTSQUIRE_NO_UPDATE_CHECK` (presence-disables) remain
|
|
58
|
+
the escape hatches for pipelines and opt-out. README and `docs/api.md`
|
|
59
|
+
updated accordingly.
|
|
60
|
+
|
|
61
|
+
## [0.1.0] - 2026-07-10
|
|
62
|
+
|
|
63
|
+
Initial release.
|
|
64
|
+
|
|
65
|
+
### Added
|
|
66
|
+
- **Mountable `skills` command group.** `skills_command_group(package, ...)`
|
|
67
|
+
returns a ready-made click group with `install`, `status`, `update`, and
|
|
68
|
+
`uninstall` subcommands that a consumer CLI mounts under its own name, so
|
|
69
|
+
its users only ever see one tool (e.g. `awiki skills install`). Works with
|
|
70
|
+
click directly and with typer via `typer.main.get_command`. Every
|
|
71
|
+
subcommand takes `--scope user|project` and `--harness NAME`.
|
|
72
|
+
- **Skills as package data.** Consumers ship SKILL.md directories (the
|
|
73
|
+
agentskills.io format) as package data inside their own wheel; skills are
|
|
74
|
+
enumerated straight from the installed wheel, no source checkout needed.
|
|
75
|
+
`BundledPackageDataSource` reads them, with structural validation against
|
|
76
|
+
the agentskills.io spec.
|
|
77
|
+
- **Four harness backends.** Claude Code, pi, Hermes, and opencode, detected
|
|
78
|
+
by marker directory, each with user and project scopes. Directories and
|
|
79
|
+
behaviour are recorded in `docs/harnesses.md`; backends resolve exactly
|
|
80
|
+
the paths documented there.
|
|
81
|
+
- **Copy-with-provenance install model.** Installs are plain copies - no
|
|
82
|
+
symlinks, no lockfile - stamped with an `agentsquire` provenance block
|
|
83
|
+
(installer, installer_version, source_package, source_version,
|
|
84
|
+
content_hash) in the installed SKILL.md frontmatter. The skill body stays
|
|
85
|
+
byte-identical to what was shipped.
|
|
86
|
+
- **Hash-based status and safe updates.** `status` classifies each skill as
|
|
87
|
+
not-installed, up-to-date, update-available, or locally-modified using
|
|
88
|
+
local hash compares only (no network in any verb). `update` refreshes
|
|
89
|
+
update-available skills and skips locally modified ones unless `--force`;
|
|
90
|
+
`uninstall` removes only directories whose stamp names the consumer's
|
|
91
|
+
package. User content is never silently overwritten or deleted.
|
|
92
|
+
- **Notice-only staleness hook.** `check_stale(source, prog_name=...,
|
|
93
|
+
update_command=...)` prints a single stderr advisory when installed skills
|
|
94
|
+
are stale. It never prompts, never updates, never touches stdout or the
|
|
95
|
+
exit code, and swallows its own errors. Gated on stderr being a TTY, `CI`
|
|
96
|
+
unset or empty, and `AGENTSQUIRE_NO_UPDATE_CHECK` unset or empty.
|
|
97
|
+
- **Python API.** Everything the CLI group does - enumerate, detect, install,
|
|
98
|
+
status, update, uninstall, staleness check - is available as plain Python;
|
|
99
|
+
see `docs/api.md`.
|
|
100
|
+
- **`agentsquire.skills` entry-point group.** Consumers may register their
|
|
101
|
+
package under it; reserved for a future environment-wide listing, changes
|
|
102
|
+
no behaviour today.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 TacoTakumi
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: agentsquire
|
|
3
|
+
Version: 0.2.2
|
|
4
|
+
Summary: Let a Python package carry its own agent integrations and install them into whatever agent harness is present.
|
|
5
|
+
Author: TacoTakumi
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
License-File: LICENSE
|
|
8
|
+
Keywords: agent,agent-skills,claude-code,cli,harness,skills
|
|
9
|
+
Classifier: Development Status :: 4 - Beta
|
|
10
|
+
Classifier: Environment :: Console
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: Operating System :: OS Independent
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
18
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
19
|
+
Classifier: Typing :: Typed
|
|
20
|
+
Requires-Python: >=3.10
|
|
21
|
+
Requires-Dist: click
|
|
22
|
+
Requires-Dist: pyyaml
|
|
23
|
+
Description-Content-Type: text/markdown
|
|
24
|
+
|
|
25
|
+
# AgentSquire
|
|
26
|
+
|
|
27
|
+
A reusable Python library + CLI that lets a Python package carry its own agent
|
|
28
|
+
integrations (Agent Skills) and install them into whatever agent harness is
|
|
29
|
+
present - the executable is the framework.
|
|
30
|
+
|
|
31
|
+
Your CLI ships its skills as package data inside its own wheel, adds
|
|
32
|
+
`agentsquire` as a plain pip dependency, and mounts a ready-made subcommand
|
|
33
|
+
group. Your users only ever see your tool:
|
|
34
|
+
|
|
35
|
+
```
|
|
36
|
+
$ awiki skills install
|
|
37
|
+
installed awiki-search -> /home/you/.claude/skills/awiki-search
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Supported harnesses at launch: Claude Code, pi, Hermes, and opencode. Detection
|
|
41
|
+
is by marker directory; every operation is local (no network in any verb). Each
|
|
42
|
+
harness's directories, scopes, and behaviour are recorded in
|
|
43
|
+
[docs/harnesses.md](docs/harnesses.md).
|
|
44
|
+
|
|
45
|
+
## Consumer integration guide
|
|
46
|
+
|
|
47
|
+
### 1. Ship skills as package data
|
|
48
|
+
|
|
49
|
+
Lay each skill out as a directory containing a `SKILL.md` (the agentskills.io
|
|
50
|
+
format) under a `skills/` resource inside your importable package:
|
|
51
|
+
|
|
52
|
+
```
|
|
53
|
+
your_pkg/
|
|
54
|
+
__init__.py
|
|
55
|
+
skills/
|
|
56
|
+
my-skill/
|
|
57
|
+
SKILL.md
|
|
58
|
+
reference.md
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
The skills ride inside your wheel; make sure your build backend includes
|
|
62
|
+
package data (hatchling includes it by default, setuptools needs
|
|
63
|
+
`include-package-data`). No source checkout is needed at run time - skills are
|
|
64
|
+
enumerated straight from the installed wheel.
|
|
65
|
+
|
|
66
|
+
### 2. Mount the subcommand group
|
|
67
|
+
|
|
68
|
+
One call returns a click group with `install`, `status`, `update`, and
|
|
69
|
+
`uninstall` subcommands, parameterized by your package name, the resource path
|
|
70
|
+
(default `"skills"`), and the default scope.
|
|
71
|
+
|
|
72
|
+
For a click CLI:
|
|
73
|
+
|
|
74
|
+
```python
|
|
75
|
+
import click
|
|
76
|
+
|
|
77
|
+
from agentsquire.cli import skills_command_group
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
@click.group()
|
|
81
|
+
def cli():
|
|
82
|
+
"""Your CLI."""
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
cli.add_command(skills_command_group("your_pkg", default_scope="user"))
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
For a typer CLI, mount onto the underlying click command:
|
|
89
|
+
|
|
90
|
+
```python
|
|
91
|
+
import typer
|
|
92
|
+
import typer.main
|
|
93
|
+
|
|
94
|
+
from agentsquire.cli import skills_command_group
|
|
95
|
+
|
|
96
|
+
app = typer.Typer()
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
@app.callback()
|
|
100
|
+
def main():
|
|
101
|
+
"""Your CLI."""
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
cli = typer.main.get_command(app)
|
|
105
|
+
cli.add_command(skills_command_group("your_pkg", default_scope="user"))
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
Your users now run `your-cli skills install` and friends. Every subcommand
|
|
109
|
+
takes `--scope user|project` (overriding your declared default) and
|
|
110
|
+
`--harness NAME` (default: all detected harnesses).
|
|
111
|
+
|
|
112
|
+
Choosing the default scope: `user` installs into the harness's per-user
|
|
113
|
+
skills directory and follows the user everywhere - right for general-purpose
|
|
114
|
+
tools. `project` installs into the current project's directory - right for
|
|
115
|
+
skills that only make sense inside a repository that uses your tool.
|
|
116
|
+
|
|
117
|
+
### 3. Surface updates proactively (optional)
|
|
118
|
+
|
|
119
|
+
Place the one-call staleness hook at your CLI entry point. When installed
|
|
120
|
+
skills have updates available it prints a single advisory line on stderr,
|
|
121
|
+
for example:
|
|
122
|
+
|
|
123
|
+
your-cli: a skills update is available for 1 skill (alpha); run `your-cli skills update`
|
|
124
|
+
|
|
125
|
+
The hook is notice-only: it never prompts, never reads stdin, and never
|
|
126
|
+
updates anything itself - the explicit `skills update` verb stays the sole
|
|
127
|
+
updater. It writes nothing to stdout, never changes your exit code, and
|
|
128
|
+
swallows its own errors, so it can never break the command it runs inside:
|
|
129
|
+
|
|
130
|
+
```python
|
|
131
|
+
from agentsquire import BundledPackageDataSource, check_stale
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def main():
|
|
135
|
+
check_stale(
|
|
136
|
+
BundledPackageDataSource("your_pkg"),
|
|
137
|
+
prog_name="your-cli",
|
|
138
|
+
update_command="your-cli skills update",
|
|
139
|
+
)
|
|
140
|
+
# ... the rest of your entry point
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
The notice shows unless a suppression gate holds: `CI` set to a non-empty
|
|
144
|
+
value, or `AGENTSQUIRE_NO_UPDATE_CHECK` set to a non-empty value. It is not
|
|
145
|
+
gated on an interactive terminal - it fires on non-TTY stderr too, so an
|
|
146
|
+
agent harness that runs your CLI with captured stderr still sees that an
|
|
147
|
+
update is available. Suppression is presence-disables, the `NO_COLOR`
|
|
148
|
+
convention: any non-empty value disables the notice (`CI=false` and
|
|
149
|
+
`AGENTSQUIRE_NO_UPDATE_CHECK=0` both suppress), while an empty string is
|
|
150
|
+
treated as unset.
|
|
151
|
+
|
|
152
|
+
### 4. Mark your package as skill-carrying (optional)
|
|
153
|
+
|
|
154
|
+
One pyproject line registers your package under the `agentsquire.skills`
|
|
155
|
+
entry-point group. Nothing reads it today - it is reserved for a future
|
|
156
|
+
environment-wide listing of skill-carrying packages and changes no behaviour:
|
|
157
|
+
|
|
158
|
+
```toml
|
|
159
|
+
[project.entry-points."agentsquire.skills"]
|
|
160
|
+
your_pkg = "your_pkg"
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
## The provenance and update model
|
|
164
|
+
|
|
165
|
+
Installs are plain copies - no symlinks, no lockfile, no references back into
|
|
166
|
+
site-packages - so an installed skill survives upgrade or removal of your
|
|
167
|
+
package. Each installed `SKILL.md` carries a provenance stamp in its
|
|
168
|
+
frontmatter `metadata.agentsquire` map: `installer`, `installer_version`,
|
|
169
|
+
`source_package`, `source_version`, and `content_hash`. The skill body is
|
|
170
|
+
byte-identical to what you shipped.
|
|
171
|
+
|
|
172
|
+
`status` classifies every skill by local hash compares only (no network,
|
|
173
|
+
ever): not-installed, up-to-date, update-available (your shipped copy moved
|
|
174
|
+
on), or locally-modified (the user edited the install, the directory carries
|
|
175
|
+
no stamp, or a symlink sits at the target - none of them ours to touch).
|
|
176
|
+
`update` refreshes update-available skills and skips locally modified ones
|
|
177
|
+
unless `--force` is given; `uninstall` removes only directories whose stamp
|
|
178
|
+
names your package. User content is never silently overwritten or deleted -
|
|
179
|
+
a pre-existing symlink at a target (a common hand-wired setup) is reported
|
|
180
|
+
and skipped, never followed or clobbered.
|
|
181
|
+
|
|
182
|
+
## Python API
|
|
183
|
+
|
|
184
|
+
Everything the CLI group does is available as plain Python - enumerate,
|
|
185
|
+
detect, install, status, update, uninstall, and the staleness check - with no
|
|
186
|
+
CLI involved. See [docs/api.md](docs/api.md) for the reference.
|
|
187
|
+
|
|
188
|
+
## License
|
|
189
|
+
|
|
190
|
+
MIT.
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
# AgentSquire
|
|
2
|
+
|
|
3
|
+
A reusable Python library + CLI that lets a Python package carry its own agent
|
|
4
|
+
integrations (Agent Skills) and install them into whatever agent harness is
|
|
5
|
+
present - the executable is the framework.
|
|
6
|
+
|
|
7
|
+
Your CLI ships its skills as package data inside its own wheel, adds
|
|
8
|
+
`agentsquire` as a plain pip dependency, and mounts a ready-made subcommand
|
|
9
|
+
group. Your users only ever see your tool:
|
|
10
|
+
|
|
11
|
+
```
|
|
12
|
+
$ awiki skills install
|
|
13
|
+
installed awiki-search -> /home/you/.claude/skills/awiki-search
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
Supported harnesses at launch: Claude Code, pi, Hermes, and opencode. Detection
|
|
17
|
+
is by marker directory; every operation is local (no network in any verb). Each
|
|
18
|
+
harness's directories, scopes, and behaviour are recorded in
|
|
19
|
+
[docs/harnesses.md](docs/harnesses.md).
|
|
20
|
+
|
|
21
|
+
## Consumer integration guide
|
|
22
|
+
|
|
23
|
+
### 1. Ship skills as package data
|
|
24
|
+
|
|
25
|
+
Lay each skill out as a directory containing a `SKILL.md` (the agentskills.io
|
|
26
|
+
format) under a `skills/` resource inside your importable package:
|
|
27
|
+
|
|
28
|
+
```
|
|
29
|
+
your_pkg/
|
|
30
|
+
__init__.py
|
|
31
|
+
skills/
|
|
32
|
+
my-skill/
|
|
33
|
+
SKILL.md
|
|
34
|
+
reference.md
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
The skills ride inside your wheel; make sure your build backend includes
|
|
38
|
+
package data (hatchling includes it by default, setuptools needs
|
|
39
|
+
`include-package-data`). No source checkout is needed at run time - skills are
|
|
40
|
+
enumerated straight from the installed wheel.
|
|
41
|
+
|
|
42
|
+
### 2. Mount the subcommand group
|
|
43
|
+
|
|
44
|
+
One call returns a click group with `install`, `status`, `update`, and
|
|
45
|
+
`uninstall` subcommands, parameterized by your package name, the resource path
|
|
46
|
+
(default `"skills"`), and the default scope.
|
|
47
|
+
|
|
48
|
+
For a click CLI:
|
|
49
|
+
|
|
50
|
+
```python
|
|
51
|
+
import click
|
|
52
|
+
|
|
53
|
+
from agentsquire.cli import skills_command_group
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
@click.group()
|
|
57
|
+
def cli():
|
|
58
|
+
"""Your CLI."""
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
cli.add_command(skills_command_group("your_pkg", default_scope="user"))
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
For a typer CLI, mount onto the underlying click command:
|
|
65
|
+
|
|
66
|
+
```python
|
|
67
|
+
import typer
|
|
68
|
+
import typer.main
|
|
69
|
+
|
|
70
|
+
from agentsquire.cli import skills_command_group
|
|
71
|
+
|
|
72
|
+
app = typer.Typer()
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
@app.callback()
|
|
76
|
+
def main():
|
|
77
|
+
"""Your CLI."""
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
cli = typer.main.get_command(app)
|
|
81
|
+
cli.add_command(skills_command_group("your_pkg", default_scope="user"))
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Your users now run `your-cli skills install` and friends. Every subcommand
|
|
85
|
+
takes `--scope user|project` (overriding your declared default) and
|
|
86
|
+
`--harness NAME` (default: all detected harnesses).
|
|
87
|
+
|
|
88
|
+
Choosing the default scope: `user` installs into the harness's per-user
|
|
89
|
+
skills directory and follows the user everywhere - right for general-purpose
|
|
90
|
+
tools. `project` installs into the current project's directory - right for
|
|
91
|
+
skills that only make sense inside a repository that uses your tool.
|
|
92
|
+
|
|
93
|
+
### 3. Surface updates proactively (optional)
|
|
94
|
+
|
|
95
|
+
Place the one-call staleness hook at your CLI entry point. When installed
|
|
96
|
+
skills have updates available it prints a single advisory line on stderr,
|
|
97
|
+
for example:
|
|
98
|
+
|
|
99
|
+
your-cli: a skills update is available for 1 skill (alpha); run `your-cli skills update`
|
|
100
|
+
|
|
101
|
+
The hook is notice-only: it never prompts, never reads stdin, and never
|
|
102
|
+
updates anything itself - the explicit `skills update` verb stays the sole
|
|
103
|
+
updater. It writes nothing to stdout, never changes your exit code, and
|
|
104
|
+
swallows its own errors, so it can never break the command it runs inside:
|
|
105
|
+
|
|
106
|
+
```python
|
|
107
|
+
from agentsquire import BundledPackageDataSource, check_stale
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def main():
|
|
111
|
+
check_stale(
|
|
112
|
+
BundledPackageDataSource("your_pkg"),
|
|
113
|
+
prog_name="your-cli",
|
|
114
|
+
update_command="your-cli skills update",
|
|
115
|
+
)
|
|
116
|
+
# ... the rest of your entry point
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
The notice shows unless a suppression gate holds: `CI` set to a non-empty
|
|
120
|
+
value, or `AGENTSQUIRE_NO_UPDATE_CHECK` set to a non-empty value. It is not
|
|
121
|
+
gated on an interactive terminal - it fires on non-TTY stderr too, so an
|
|
122
|
+
agent harness that runs your CLI with captured stderr still sees that an
|
|
123
|
+
update is available. Suppression is presence-disables, the `NO_COLOR`
|
|
124
|
+
convention: any non-empty value disables the notice (`CI=false` and
|
|
125
|
+
`AGENTSQUIRE_NO_UPDATE_CHECK=0` both suppress), while an empty string is
|
|
126
|
+
treated as unset.
|
|
127
|
+
|
|
128
|
+
### 4. Mark your package as skill-carrying (optional)
|
|
129
|
+
|
|
130
|
+
One pyproject line registers your package under the `agentsquire.skills`
|
|
131
|
+
entry-point group. Nothing reads it today - it is reserved for a future
|
|
132
|
+
environment-wide listing of skill-carrying packages and changes no behaviour:
|
|
133
|
+
|
|
134
|
+
```toml
|
|
135
|
+
[project.entry-points."agentsquire.skills"]
|
|
136
|
+
your_pkg = "your_pkg"
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
## The provenance and update model
|
|
140
|
+
|
|
141
|
+
Installs are plain copies - no symlinks, no lockfile, no references back into
|
|
142
|
+
site-packages - so an installed skill survives upgrade or removal of your
|
|
143
|
+
package. Each installed `SKILL.md` carries a provenance stamp in its
|
|
144
|
+
frontmatter `metadata.agentsquire` map: `installer`, `installer_version`,
|
|
145
|
+
`source_package`, `source_version`, and `content_hash`. The skill body is
|
|
146
|
+
byte-identical to what you shipped.
|
|
147
|
+
|
|
148
|
+
`status` classifies every skill by local hash compares only (no network,
|
|
149
|
+
ever): not-installed, up-to-date, update-available (your shipped copy moved
|
|
150
|
+
on), or locally-modified (the user edited the install, the directory carries
|
|
151
|
+
no stamp, or a symlink sits at the target - none of them ours to touch).
|
|
152
|
+
`update` refreshes update-available skills and skips locally modified ones
|
|
153
|
+
unless `--force` is given; `uninstall` removes only directories whose stamp
|
|
154
|
+
names your package. User content is never silently overwritten or deleted -
|
|
155
|
+
a pre-existing symlink at a target (a common hand-wired setup) is reported
|
|
156
|
+
and skipped, never followed or clobbered.
|
|
157
|
+
|
|
158
|
+
## Python API
|
|
159
|
+
|
|
160
|
+
Everything the CLI group does is available as plain Python - enumerate,
|
|
161
|
+
detect, install, status, update, uninstall, and the staleness check - with no
|
|
162
|
+
CLI involved. See [docs/api.md](docs/api.md) for the reference.
|
|
163
|
+
|
|
164
|
+
## License
|
|
165
|
+
|
|
166
|
+
MIT.
|