agentharnesses-cli 0.1.3__tar.gz → 0.1.4__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.
- {agentharnesses_cli-0.1.3 → agentharnesses_cli-0.1.4}/PKG-INFO +31 -1
- {agentharnesses_cli-0.1.3 → agentharnesses_cli-0.1.4}/README.md +29 -0
- {agentharnesses_cli-0.1.3 → agentharnesses_cli-0.1.4}/pyproject.toml +6 -0
- agentharnesses_cli-0.1.4/tests/__init__.py +0 -0
- agentharnesses_cli-0.1.4/tests/test_init.py +137 -0
- {agentharnesses_cli-0.1.3 → agentharnesses_cli-0.1.4}/.github/workflows/pypi.yaml +0 -0
- {agentharnesses_cli-0.1.3 → agentharnesses_cli-0.1.4}/.gitignore +0 -0
- {agentharnesses_cli-0.1.3 → agentharnesses_cli-0.1.4}/src/ahar/__init__.py +0 -0
- {agentharnesses_cli-0.1.3 → agentharnesses_cli-0.1.4}/src/ahar/commands/__init__.py +0 -0
- {agentharnesses_cli-0.1.3 → agentharnesses_cli-0.1.4}/src/ahar/commands/init.py +0 -0
- {agentharnesses_cli-0.1.3 → agentharnesses_cli-0.1.4}/src/ahar/main.py +0 -0
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: agentharnesses-cli
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.4
|
|
4
4
|
Summary: CLI tools for agentharnesses.io
|
|
5
5
|
Project-URL: Homepage, https://agentharnesses.io
|
|
6
6
|
Project-URL: Repository, https://github.com/agentharnesses/cli
|
|
7
7
|
License-Expression: Apache-2.0
|
|
8
8
|
Requires-Python: >=3.10
|
|
9
9
|
Requires-Dist: click>=8.1
|
|
10
|
+
Requires-Dist: harnesses-ref>=0.1.0
|
|
10
11
|
Description-Content-Type: text/markdown
|
|
11
12
|
|
|
12
13
|
# agentharnesses-cli
|
|
@@ -64,6 +65,35 @@ When using the `claude` preset (default), also installs:
|
|
|
64
65
|
└── SKILL.md
|
|
65
66
|
```
|
|
66
67
|
|
|
68
|
+
The metaskill is cloned fresh from [agentharnesses/metaskill](https://github.com/agentharnesses/metaskill) at init time, so you always get the latest version.
|
|
69
|
+
|
|
70
|
+
### `ahar validate`
|
|
71
|
+
|
|
72
|
+
Validate a harness directory structure:
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
ahar validate ./my-harness
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### `ahar read`
|
|
79
|
+
|
|
80
|
+
Read a property from a harness's `HARNESS.md` frontmatter:
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
ahar read ./my-harness name
|
|
84
|
+
ahar read ./my-harness description
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### `ahar prompt`
|
|
88
|
+
|
|
89
|
+
Render a harness as prompt XML for agent injection:
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
ahar prompt ./my-harness
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
These commands are backed by [harnesses-ref](https://pypi.org/project/harnesses-ref/), the reference implementation for the Agent Harnesses standard.
|
|
96
|
+
|
|
67
97
|
## Publishing
|
|
68
98
|
|
|
69
99
|
Releases are published to PyPI automatically when a version tag is pushed:
|
|
@@ -53,6 +53,35 @@ When using the `claude` preset (default), also installs:
|
|
|
53
53
|
└── SKILL.md
|
|
54
54
|
```
|
|
55
55
|
|
|
56
|
+
The metaskill is cloned fresh from [agentharnesses/metaskill](https://github.com/agentharnesses/metaskill) at init time, so you always get the latest version.
|
|
57
|
+
|
|
58
|
+
### `ahar validate`
|
|
59
|
+
|
|
60
|
+
Validate a harness directory structure:
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
ahar validate ./my-harness
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### `ahar read`
|
|
67
|
+
|
|
68
|
+
Read a property from a harness's `HARNESS.md` frontmatter:
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
ahar read ./my-harness name
|
|
72
|
+
ahar read ./my-harness description
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### `ahar prompt`
|
|
76
|
+
|
|
77
|
+
Render a harness as prompt XML for agent injection:
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
ahar prompt ./my-harness
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
These commands are backed by [harnesses-ref](https://pypi.org/project/harnesses-ref/), the reference implementation for the Agent Harnesses standard.
|
|
84
|
+
|
|
56
85
|
## Publishing
|
|
57
86
|
|
|
58
87
|
Releases are published to PyPI automatically when a version tag is pushed:
|
|
@@ -7,6 +7,7 @@ readme = "README.md"
|
|
|
7
7
|
requires-python = ">=3.10"
|
|
8
8
|
dependencies = [
|
|
9
9
|
"click>=8.1",
|
|
10
|
+
"harnesses-ref>=0.1.0",
|
|
10
11
|
]
|
|
11
12
|
|
|
12
13
|
[project.urls]
|
|
@@ -25,3 +26,8 @@ source = "vcs"
|
|
|
25
26
|
|
|
26
27
|
[tool.hatch.build.targets.wheel]
|
|
27
28
|
packages = ["src/ahar"]
|
|
29
|
+
|
|
30
|
+
[dependency-groups]
|
|
31
|
+
dev = [
|
|
32
|
+
"pytest>=7.0",
|
|
33
|
+
]
|
|
File without changes
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import json
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
|
|
4
|
+
import pytest
|
|
5
|
+
from click.testing import CliRunner
|
|
6
|
+
|
|
7
|
+
from ahar.commands.init import scaffold
|
|
8
|
+
from ahar.main import cli
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
# --- scaffold unit tests ---
|
|
12
|
+
|
|
13
|
+
def test_scaffold_creates_required_files(tmp_path):
|
|
14
|
+
scaffold(str(tmp_path), "my-harness")
|
|
15
|
+
assert (tmp_path / "HARNESS.md").exists()
|
|
16
|
+
assert (tmp_path / "README.md").exists()
|
|
17
|
+
assert (tmp_path / ".gitignore").exists()
|
|
18
|
+
assert (tmp_path / ".claude" / "settings.json").exists()
|
|
19
|
+
assert (tmp_path / "skills" / "SKILLS.md").exists()
|
|
20
|
+
assert (tmp_path / "references" / "REFERENCES.md").exists()
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def test_scaffold_harness_md_contains_name(tmp_path):
|
|
24
|
+
scaffold(str(tmp_path), "my-harness")
|
|
25
|
+
content = (tmp_path / "HARNESS.md").read_text()
|
|
26
|
+
assert "name: my-harness" in content
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def test_scaffold_settings_registers_marketplace(tmp_path):
|
|
30
|
+
scaffold(str(tmp_path), "my-harness")
|
|
31
|
+
settings = json.loads((tmp_path / ".claude" / "settings.json").read_text())
|
|
32
|
+
assert "my-harness" in settings["extraKnownMarketplaces"]
|
|
33
|
+
assert settings["extraKnownMarketplaces"]["my-harness"]["source"]["source"] == "directory"
|
|
34
|
+
assert settings["extraKnownMarketplaces"]["my-harness"]["source"]["path"] == str(tmp_path)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def test_scaffold_settings_enables_plugin(tmp_path):
|
|
38
|
+
scaffold(str(tmp_path), "my-harness")
|
|
39
|
+
settings = json.loads((tmp_path / ".claude" / "settings.json").read_text())
|
|
40
|
+
assert "my-harness@my-harness" in settings["enabledPlugins"]
|
|
41
|
+
assert settings["enabledPlugins"]["my-harness@my-harness"] is True
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def test_scaffold_gitignore_excludes_settings(tmp_path):
|
|
45
|
+
scaffold(str(tmp_path), "my-harness")
|
|
46
|
+
content = (tmp_path / ".gitignore").read_text()
|
|
47
|
+
assert ".claude/settings.json" in content
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
# --- init CLI tests ---
|
|
51
|
+
|
|
52
|
+
def test_init_empty_preset_creates_scaffold(tmp_path):
|
|
53
|
+
runner = CliRunner()
|
|
54
|
+
with runner.isolated_filesystem(temp_dir=tmp_path):
|
|
55
|
+
result = runner.invoke(cli, ["init", "test-harness"], input="empty\n")
|
|
56
|
+
assert result.exit_code == 0
|
|
57
|
+
assert Path("HARNESS.md").exists()
|
|
58
|
+
assert Path("skills/SKILLS.md").exists()
|
|
59
|
+
assert Path("references/REFERENCES.md").exists()
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def test_init_uses_directory_name_as_default(tmp_path):
|
|
63
|
+
runner = CliRunner()
|
|
64
|
+
with runner.isolated_filesystem(temp_dir=tmp_path):
|
|
65
|
+
result = runner.invoke(cli, ["init"], input="empty\n")
|
|
66
|
+
assert result.exit_code == 0
|
|
67
|
+
content = Path("HARNESS.md").read_text()
|
|
68
|
+
assert f"name: {Path.cwd().name}" in content
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def test_init_fails_if_already_initialized(tmp_path):
|
|
72
|
+
runner = CliRunner()
|
|
73
|
+
with runner.isolated_filesystem(temp_dir=tmp_path):
|
|
74
|
+
Path("HARNESS.md").write_text("---\nname: existing\ndescription: x\n---\n")
|
|
75
|
+
result = runner.invoke(cli, ["init"], input="empty\n")
|
|
76
|
+
assert result.exit_code == 1
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def test_init_empty_preset_skips_metaskill(tmp_path):
|
|
80
|
+
runner = CliRunner()
|
|
81
|
+
with runner.isolated_filesystem(temp_dir=tmp_path):
|
|
82
|
+
result = runner.invoke(cli, ["init", "test-harness"], input="empty\n")
|
|
83
|
+
assert result.exit_code == 0
|
|
84
|
+
assert not Path(".claude/skills/agent-harnesses").exists()
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
# --- validate CLI tests ---
|
|
88
|
+
|
|
89
|
+
def _make_valid_harness(path: Path) -> Path:
|
|
90
|
+
(path / "HARNESS.md").write_text(
|
|
91
|
+
"---\nname: Test Harness\ndescription: A test harness.\n---\nBody.\n"
|
|
92
|
+
)
|
|
93
|
+
return path
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def test_validate_valid_harness(tmp_path):
|
|
97
|
+
_make_valid_harness(tmp_path)
|
|
98
|
+
runner = CliRunner()
|
|
99
|
+
result = runner.invoke(cli, ["validate", str(tmp_path)])
|
|
100
|
+
assert result.exit_code == 0
|
|
101
|
+
assert "valid" in result.output
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def test_validate_invalid_harness(tmp_path):
|
|
105
|
+
(tmp_path / "HARNESS.md").write_text("no frontmatter")
|
|
106
|
+
runner = CliRunner()
|
|
107
|
+
result = runner.invoke(cli, ["validate", str(tmp_path)])
|
|
108
|
+
assert result.exit_code == 1
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
# --- read CLI tests ---
|
|
112
|
+
|
|
113
|
+
def test_read_name(tmp_path):
|
|
114
|
+
_make_valid_harness(tmp_path)
|
|
115
|
+
runner = CliRunner()
|
|
116
|
+
result = runner.invoke(cli, ["read", str(tmp_path), "name"])
|
|
117
|
+
assert result.exit_code == 0
|
|
118
|
+
assert result.output.strip() == "Test Harness"
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def test_read_description(tmp_path):
|
|
122
|
+
_make_valid_harness(tmp_path)
|
|
123
|
+
runner = CliRunner()
|
|
124
|
+
result = runner.invoke(cli, ["read", str(tmp_path), "description"])
|
|
125
|
+
assert result.exit_code == 0
|
|
126
|
+
assert result.output.strip() == "A test harness."
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
# --- prompt CLI tests ---
|
|
130
|
+
|
|
131
|
+
def test_prompt_renders_xml(tmp_path):
|
|
132
|
+
_make_valid_harness(tmp_path)
|
|
133
|
+
runner = CliRunner()
|
|
134
|
+
result = runner.invoke(cli, ["prompt", str(tmp_path)])
|
|
135
|
+
assert result.exit_code == 0
|
|
136
|
+
assert "<harness" in result.output
|
|
137
|
+
assert "Test Harness" in result.output
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|