agentharnesses-cli 0.1.0__py3-none-any.whl
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.
- agentharnesses_cli-0.1.0.dist-info/METADATA +62 -0
- agentharnesses_cli-0.1.0.dist-info/RECORD +8 -0
- agentharnesses_cli-0.1.0.dist-info/WHEEL +4 -0
- agentharnesses_cli-0.1.0.dist-info/entry_points.txt +2 -0
- ahar/__init__.py +0 -0
- ahar/commands/__init__.py +0 -0
- ahar/commands/init.py +200 -0
- ahar/main.py +10 -0
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: agentharnesses-cli
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: CLI tools for agentharnesses.io
|
|
5
|
+
Project-URL: Homepage, https://agentharnesses.io
|
|
6
|
+
Project-URL: Repository, https://github.com/agentharnesses/cli
|
|
7
|
+
License-Expression: Apache-2.0
|
|
8
|
+
Requires-Python: >=3.10
|
|
9
|
+
Requires-Dist: click>=8.1
|
|
10
|
+
Description-Content-Type: text/markdown
|
|
11
|
+
|
|
12
|
+
# agentharnesses-cli
|
|
13
|
+
|
|
14
|
+
Command line tools for [agentharnesses.io](http://agentharnesses.io).
|
|
15
|
+
|
|
16
|
+
## Installation
|
|
17
|
+
|
|
18
|
+
### From PyPI (once published)
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
pip install agentharnesses-cli
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
### From source
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
git clone https://github.com/your-org/cli.git
|
|
28
|
+
cd cli
|
|
29
|
+
pip install .
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
### Development install
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
git clone https://github.com/your-org/cli.git
|
|
36
|
+
cd cli
|
|
37
|
+
pip install -e .
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
The `-e` flag installs in editable mode so changes to the source are reflected immediately without reinstalling.
|
|
41
|
+
|
|
42
|
+
## Usage
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
ahar --help
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### `ahar init`
|
|
49
|
+
|
|
50
|
+
Initialize a new harness in the current directory:
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
ahar init
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Optionally specify a name (defaults to the directory name):
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
ahar init my-harness
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
This creates a `harness.yaml` file in the current directory.
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
ahar/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
+
ahar/main.py,sha256=tqloJWAyOIJ4WpuudghCiMazC3qI0VPk33262TZZIDU,160
|
|
3
|
+
ahar/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
4
|
+
ahar/commands/init.py,sha256=-2mkAzPDpLwRz2hj9pyk5MegSA7RL39jw94G74Vf_KI,5455
|
|
5
|
+
agentharnesses_cli-0.1.0.dist-info/METADATA,sha256=bvLjcwk2p-CB_mkvBA7t4H791b6P1MeUrLYVcc6SJwo,1125
|
|
6
|
+
agentharnesses_cli-0.1.0.dist-info/WHEEL,sha256=mffPy8wBnZQn2VnJUU5jE99KsxaSfiyMHV9Yt0aLVxs,87
|
|
7
|
+
agentharnesses_cli-0.1.0.dist-info/entry_points.txt,sha256=ILVBX997IkyOrGaIz5t2ICDdJsOwYHwu2cphSB0XtFc,39
|
|
8
|
+
agentharnesses_cli-0.1.0.dist-info/RECORD,,
|
ahar/__init__.py
ADDED
|
File without changes
|
|
File without changes
|
ahar/commands/init.py
ADDED
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import subprocess
|
|
3
|
+
import sys
|
|
4
|
+
import click
|
|
5
|
+
|
|
6
|
+
_METASKILL_REPO = "https://github.com/agentharnesses/metaskill" # TODO: replace with final URL
|
|
7
|
+
_METASKILL_DEST = ".claude/plugins/metaskill"
|
|
8
|
+
_MAINTAIN_SKILL_DEST = ".claude/skills/maintain-harness.md"
|
|
9
|
+
|
|
10
|
+
_MAINTAIN_SKILL = """\
|
|
11
|
+
---
|
|
12
|
+
name: maintain-harness
|
|
13
|
+
description: How to maintain and update this harness — updating HARNESS.md, adding skills and references, managing the skill index.
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## Maintaining the Harness
|
|
17
|
+
|
|
18
|
+
When asked to maintain, update, or extend this harness, follow these conventions:
|
|
19
|
+
|
|
20
|
+
### HARNESS.md
|
|
21
|
+
- Keep the `## Skills` section in sync with entries in `skills/SKILLS.md`
|
|
22
|
+
- Keep the `## References` section in sync with entries in `references/REFERENCES.md`
|
|
23
|
+
- Update the `description` frontmatter field when the harness scope changes
|
|
24
|
+
|
|
25
|
+
### Adding a skill bucket
|
|
26
|
+
1. Create `skills/<bucket-name>/<bucket-name>.md` with a frontmatter `name` and `description`
|
|
27
|
+
2. Add an entry to `skills/SKILLS.md` summarizing when to use the bucket
|
|
28
|
+
3. Add a bullet to the `## Skills` section in `HARNESS.md`
|
|
29
|
+
|
|
30
|
+
### Adding a reference document
|
|
31
|
+
1. Add the document to `references/`
|
|
32
|
+
2. Add an entry to `references/REFERENCES.md` describing the document's purpose
|
|
33
|
+
3. Add a bullet to the `## References` section in `HARNESS.md`
|
|
34
|
+
|
|
35
|
+
### General conventions
|
|
36
|
+
- Keep skill descriptions actionable: "Use when..." not "This skill..."
|
|
37
|
+
- Reference documents should be stable facts; skill buckets contain executable guidance
|
|
38
|
+
- Prefer updating existing skill buckets over creating new ones when scope overlaps
|
|
39
|
+
"""
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def _write(path, content):
|
|
43
|
+
os.makedirs(os.path.dirname(path), exist_ok=True)
|
|
44
|
+
with open(path, "w") as f:
|
|
45
|
+
f.write(content)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def scaffold(root, name):
|
|
49
|
+
_write(
|
|
50
|
+
f"{root}/HARNESS.md",
|
|
51
|
+
f"""\
|
|
52
|
+
---
|
|
53
|
+
name: {name}
|
|
54
|
+
description: TODO: describe what this harness does and the role it gives Claude.
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
## Upon loading the Harness
|
|
58
|
+
|
|
59
|
+
TODO: write the entry message Claude should internalize when this harness loads.
|
|
60
|
+
|
|
61
|
+
## Skills
|
|
62
|
+
|
|
63
|
+
TODO: list skill buckets here as they are created.
|
|
64
|
+
- See `skills/SKILLS.md` for the full index.
|
|
65
|
+
|
|
66
|
+
## References
|
|
67
|
+
|
|
68
|
+
TODO: list reference documents here as they are added.
|
|
69
|
+
- See `references/REFERENCES.md` for the full index.
|
|
70
|
+
""",
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
_write(
|
|
74
|
+
f"{root}/README.md",
|
|
75
|
+
f"""\
|
|
76
|
+
# {name}
|
|
77
|
+
|
|
78
|
+
TODO: brief description of this harness.
|
|
79
|
+
""",
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
_write(
|
|
83
|
+
f"{root}/.claude/settings.json",
|
|
84
|
+
f"""\
|
|
85
|
+
{{
|
|
86
|
+
"extraKnownMarketplaces": {{
|
|
87
|
+
"{name}": {{
|
|
88
|
+
"source": {{
|
|
89
|
+
"source": "directory",
|
|
90
|
+
"path": "{root}"
|
|
91
|
+
}}
|
|
92
|
+
}}
|
|
93
|
+
}},
|
|
94
|
+
"enabledPlugins": {{
|
|
95
|
+
"{name}@{name}": true
|
|
96
|
+
}}
|
|
97
|
+
}}
|
|
98
|
+
""",
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
_write(
|
|
102
|
+
f"{root}/skills/SKILLS.md",
|
|
103
|
+
"""\
|
|
104
|
+
---
|
|
105
|
+
description: TODO: describe the skill buckets in this harness and when to use each.
|
|
106
|
+
---
|
|
107
|
+
|
|
108
|
+
TODO: add skill buckets here as they are created.
|
|
109
|
+
""",
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
_write(
|
|
113
|
+
f"{root}/references/REFERENCES.md",
|
|
114
|
+
"""\
|
|
115
|
+
---
|
|
116
|
+
description: TODO: describe the reference documents in this harness and how to use them.
|
|
117
|
+
---
|
|
118
|
+
|
|
119
|
+
TODO: add reference documents here as they are added.
|
|
120
|
+
""",
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
_PRESETS = {
|
|
125
|
+
"claude": "Full Claude Code setup: metaskill plugin + maintain-harness skill",
|
|
126
|
+
"empty": "Bare minimum: no additional plugins or skills",
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
@click.command()
|
|
131
|
+
@click.argument("name", default=None, required=False)
|
|
132
|
+
def init(name):
|
|
133
|
+
"""Initialize a new harness in the current directory."""
|
|
134
|
+
cwd = os.getcwd()
|
|
135
|
+
|
|
136
|
+
if name is None:
|
|
137
|
+
name = os.path.basename(cwd)
|
|
138
|
+
|
|
139
|
+
if os.path.exists(os.path.join(cwd, "HARNESS.md")):
|
|
140
|
+
click.echo("HARNESS.md already exists here — already initialized.", err=True)
|
|
141
|
+
sys.exit(1)
|
|
142
|
+
|
|
143
|
+
scaffold(cwd, name)
|
|
144
|
+
|
|
145
|
+
click.echo(f"Initialized harness '{name}' in {cwd}")
|
|
146
|
+
click.echo(" HARNESS.md")
|
|
147
|
+
click.echo(" README.md")
|
|
148
|
+
click.echo(" .claude/settings.json")
|
|
149
|
+
click.echo(" skills/SKILLS.md")
|
|
150
|
+
click.echo(" references/REFERENCES.md")
|
|
151
|
+
|
|
152
|
+
click.echo("\nClaude Code preset:")
|
|
153
|
+
for key, desc in _PRESETS.items():
|
|
154
|
+
click.echo(f" {key:<8} {desc}")
|
|
155
|
+
|
|
156
|
+
preset = click.prompt(
|
|
157
|
+
"Preset",
|
|
158
|
+
type=click.Choice(list(_PRESETS.keys()), case_sensitive=False),
|
|
159
|
+
default="claude",
|
|
160
|
+
show_choices=False,
|
|
161
|
+
)
|
|
162
|
+
_configure_claude(cwd, preset)
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
def _configure_claude(root, preset):
|
|
166
|
+
if preset == "empty":
|
|
167
|
+
click.echo("Skipping Claude Code plugin configuration (empty preset).")
|
|
168
|
+
return
|
|
169
|
+
|
|
170
|
+
# claude preset: metaskill + maintain-harness skill
|
|
171
|
+
_install_metaskill(root)
|
|
172
|
+
_install_maintain_skill(root)
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
def _install_metaskill(root):
|
|
176
|
+
dest = os.path.join(root, _METASKILL_DEST)
|
|
177
|
+
if os.path.exists(dest):
|
|
178
|
+
click.echo(f"Metaskill already present at {_METASKILL_DEST} — skipping clone.")
|
|
179
|
+
return
|
|
180
|
+
|
|
181
|
+
os.makedirs(os.path.dirname(dest), exist_ok=True)
|
|
182
|
+
click.echo(f"Cloning metaskill into {_METASKILL_DEST}...")
|
|
183
|
+
result = subprocess.run(
|
|
184
|
+
["git", "clone", _METASKILL_REPO, dest],
|
|
185
|
+
capture_output=True,
|
|
186
|
+
text=True,
|
|
187
|
+
)
|
|
188
|
+
if result.returncode != 0:
|
|
189
|
+
click.echo(f"Clone failed:\n{result.stderr.strip()}", err=True)
|
|
190
|
+
sys.exit(1)
|
|
191
|
+
click.echo(f" {_METASKILL_DEST}")
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
def _install_maintain_skill(root):
|
|
195
|
+
dest = os.path.join(root, _MAINTAIN_SKILL_DEST)
|
|
196
|
+
if os.path.exists(dest):
|
|
197
|
+
click.echo(f"maintain-harness skill already present at {_MAINTAIN_SKILL_DEST} — skipping.")
|
|
198
|
+
return
|
|
199
|
+
_write(dest, _MAINTAIN_SKILL)
|
|
200
|
+
click.echo(f" {_MAINTAIN_SKILL_DEST}")
|