agentkit-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.
- agentkit_cli/__init__.py +3 -0
- agentkit_cli/commands/__init__.py +1 -0
- agentkit_cli/commands/init_cmd.py +63 -0
- agentkit_cli/commands/run_cmd.py +173 -0
- agentkit_cli/commands/status_cmd.py +97 -0
- agentkit_cli/config.py +91 -0
- agentkit_cli/main.py +62 -0
- agentkit_cli/tools.py +73 -0
- agentkit_cli-0.1.0.dist-info/METADATA +148 -0
- agentkit_cli-0.1.0.dist-info/RECORD +13 -0
- agentkit_cli-0.1.0.dist-info/WHEEL +4 -0
- agentkit_cli-0.1.0.dist-info/entry_points.txt +2 -0
- agentkit_cli-0.1.0.dist-info/licenses/LICENSE +21 -0
agentkit_cli/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# commands package
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"""agentkit init command."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from typing import Optional
|
|
6
|
+
|
|
7
|
+
import typer
|
|
8
|
+
from rich.console import Console
|
|
9
|
+
from rich.panel import Panel
|
|
10
|
+
from rich.table import Table
|
|
11
|
+
from rich import print as rprint
|
|
12
|
+
|
|
13
|
+
from agentkit_cli.tools import QUARTET_TOOLS, is_installed, INSTALL_HINTS
|
|
14
|
+
from agentkit_cli.config import find_project_root, config_path, config_exists, write_default_config
|
|
15
|
+
|
|
16
|
+
console = Console()
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def init_command(
|
|
20
|
+
path: Optional[Path] = typer.Option(None, "--path", "-p", help="Project root (default: git root or cwd)"),
|
|
21
|
+
) -> None:
|
|
22
|
+
"""Initialize agentkit in a project directory."""
|
|
23
|
+
root = path or find_project_root()
|
|
24
|
+
console.print(f"\n[bold]Initializing agentkit in:[/bold] {root}\n")
|
|
25
|
+
|
|
26
|
+
# Check quartet tools
|
|
27
|
+
table = Table(title="Agent Quality Toolkit — Tool Status", show_header=True)
|
|
28
|
+
table.add_column("Tool", style="bold")
|
|
29
|
+
table.add_column("Status")
|
|
30
|
+
table.add_column("Install Command")
|
|
31
|
+
|
|
32
|
+
missing = []
|
|
33
|
+
for tool in QUARTET_TOOLS:
|
|
34
|
+
if is_installed(tool):
|
|
35
|
+
table.add_row(tool, "[green]✓ installed[/green]", "")
|
|
36
|
+
else:
|
|
37
|
+
table.add_row(tool, "[red]✗ missing[/red]", INSTALL_HINTS.get(tool, f"pip install {tool}"))
|
|
38
|
+
missing.append(tool)
|
|
39
|
+
|
|
40
|
+
console.print(table)
|
|
41
|
+
|
|
42
|
+
# Write config
|
|
43
|
+
cfg_path = config_path(root)
|
|
44
|
+
if config_exists(root):
|
|
45
|
+
console.print(f"\n[yellow]Config already exists:[/yellow] {cfg_path}")
|
|
46
|
+
else:
|
|
47
|
+
written = write_default_config(root)
|
|
48
|
+
console.print(f"\n[green]Created config:[/green] {written}")
|
|
49
|
+
|
|
50
|
+
# Print install instructions for missing tools
|
|
51
|
+
if missing:
|
|
52
|
+
console.print("\n[yellow]Install missing tools:[/yellow]")
|
|
53
|
+
for tool in missing:
|
|
54
|
+
console.print(f" pip install {tool}")
|
|
55
|
+
|
|
56
|
+
# Next steps
|
|
57
|
+
next_steps = "\n".join([
|
|
58
|
+
"1. Install any missing tools (see above)",
|
|
59
|
+
"2. Run [bold]agentkit run[/bold] to execute the full quality pipeline",
|
|
60
|
+
"3. Run [bold]agentkit status[/bold] to check your setup at any time",
|
|
61
|
+
])
|
|
62
|
+
console.print(Panel(next_steps, title="Next Steps", border_style="blue"))
|
|
63
|
+
console.print()
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
"""agentkit run command — sequential pipeline runner."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
import json
|
|
5
|
+
import time
|
|
6
|
+
from datetime import datetime, timezone
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from typing import List, Optional
|
|
9
|
+
|
|
10
|
+
import typer
|
|
11
|
+
from rich.console import Console
|
|
12
|
+
from rich.table import Table
|
|
13
|
+
|
|
14
|
+
from agentkit_cli.tools import is_installed, run_tool, INSTALL_HINTS
|
|
15
|
+
from agentkit_cli.config import find_project_root, save_last_run
|
|
16
|
+
|
|
17
|
+
console = Console()
|
|
18
|
+
|
|
19
|
+
STEP_TOOL_MAP = {
|
|
20
|
+
"generate": "agentmd",
|
|
21
|
+
"lint": "agentlint",
|
|
22
|
+
"benchmark": "coderace",
|
|
23
|
+
"reflect": "agentreflect",
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def _run_step(name: str, tool: str, args: list[str], cwd: str) -> dict:
|
|
28
|
+
"""Execute one pipeline step; return result dict."""
|
|
29
|
+
start = time.monotonic()
|
|
30
|
+
if not is_installed(tool):
|
|
31
|
+
return {
|
|
32
|
+
"step": name,
|
|
33
|
+
"tool": tool,
|
|
34
|
+
"status": "skipped",
|
|
35
|
+
"reason": f"not installed — {INSTALL_HINTS.get(tool, f'pip install {tool}')}",
|
|
36
|
+
"duration": 0.0,
|
|
37
|
+
"output": "",
|
|
38
|
+
}
|
|
39
|
+
try:
|
|
40
|
+
result = run_tool(tool, args, cwd=cwd)
|
|
41
|
+
duration = time.monotonic() - start
|
|
42
|
+
success = result.returncode == 0
|
|
43
|
+
return {
|
|
44
|
+
"step": name,
|
|
45
|
+
"tool": tool,
|
|
46
|
+
"status": "pass" if success else "fail",
|
|
47
|
+
"returncode": result.returncode,
|
|
48
|
+
"duration": round(duration, 2),
|
|
49
|
+
"output": (result.stdout + result.stderr).strip(),
|
|
50
|
+
}
|
|
51
|
+
except Exception as e:
|
|
52
|
+
return {
|
|
53
|
+
"step": name,
|
|
54
|
+
"tool": tool,
|
|
55
|
+
"status": "error",
|
|
56
|
+
"reason": str(e),
|
|
57
|
+
"duration": round(time.monotonic() - start, 2),
|
|
58
|
+
"output": "",
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def run_command(
|
|
63
|
+
path: Optional[Path] = typer.Option(None, "--path", "-p", help="Project directory to run against"),
|
|
64
|
+
skip: Optional[List[str]] = typer.Option(None, "--skip", help="Steps to skip: generate, lint, benchmark, reflect"),
|
|
65
|
+
benchmark: bool = typer.Option(False, "--benchmark", help="Include benchmark step (skipped by default)"),
|
|
66
|
+
json_output: bool = typer.Option(False, "--json", help="Output summary JSON"),
|
|
67
|
+
notes: Optional[str] = typer.Option(None, "--notes", help="Notes passed to agentreflect"),
|
|
68
|
+
) -> None:
|
|
69
|
+
"""Run the full Agent Quality pipeline."""
|
|
70
|
+
root = path or find_project_root()
|
|
71
|
+
cwd_str = str(root)
|
|
72
|
+
skip_set = set(s.lower() for s in (skip or []))
|
|
73
|
+
|
|
74
|
+
# Benchmark is opt-in
|
|
75
|
+
if not benchmark:
|
|
76
|
+
skip_set.add("benchmark")
|
|
77
|
+
|
|
78
|
+
console.print(f"\n[bold]agentkit run[/bold] — project: {root}\n")
|
|
79
|
+
|
|
80
|
+
context_file = root / "CLAUDE.md"
|
|
81
|
+
results = []
|
|
82
|
+
|
|
83
|
+
# Step 1: generate
|
|
84
|
+
if "generate" not in skip_set:
|
|
85
|
+
console.print("[dim]→ agentmd generate ...[/dim]")
|
|
86
|
+
r = _run_step("generate", "agentmd", ["generate", cwd_str], cwd_str)
|
|
87
|
+
results.append(r)
|
|
88
|
+
else:
|
|
89
|
+
results.append({"step": "generate", "tool": "agentmd", "status": "skipped", "reason": "user skipped", "duration": 0.0})
|
|
90
|
+
|
|
91
|
+
# Step 2: lint context file
|
|
92
|
+
if "lint" not in skip_set:
|
|
93
|
+
lint_args = ["check-context", str(context_file)] if context_file.exists() else ["check-context", cwd_str]
|
|
94
|
+
console.print("[dim]→ agentlint check-context ...[/dim]")
|
|
95
|
+
r = _run_step("lint-context", "agentlint", lint_args, cwd_str)
|
|
96
|
+
results.append(r)
|
|
97
|
+
|
|
98
|
+
# Step 3: lint diffs
|
|
99
|
+
console.print("[dim]→ agentlint (diff) ...[/dim]")
|
|
100
|
+
r2 = _run_step("lint-diff", "agentlint", [cwd_str], cwd_str)
|
|
101
|
+
results.append(r2)
|
|
102
|
+
else:
|
|
103
|
+
results.append({"step": "lint-context", "tool": "agentlint", "status": "skipped", "reason": "user skipped", "duration": 0.0})
|
|
104
|
+
results.append({"step": "lint-diff", "tool": "agentlint", "status": "skipped", "reason": "user skipped", "duration": 0.0})
|
|
105
|
+
|
|
106
|
+
# Step 4: benchmark (opt-in)
|
|
107
|
+
if "benchmark" not in skip_set:
|
|
108
|
+
console.print("[dim]→ coderace benchmark ...[/dim]")
|
|
109
|
+
r = _run_step("benchmark", "coderace", ["benchmark", cwd_str], cwd_str)
|
|
110
|
+
results.append(r)
|
|
111
|
+
else:
|
|
112
|
+
results.append({"step": "benchmark", "tool": "coderace", "status": "skipped", "reason": "opt-in only (use --benchmark)", "duration": 0.0})
|
|
113
|
+
|
|
114
|
+
# Step 5: reflect
|
|
115
|
+
if "reflect" not in skip_set:
|
|
116
|
+
reflect_args = ["generate", "--notes", notes or "agentkit run completed", cwd_str]
|
|
117
|
+
console.print("[dim]→ agentreflect generate ...[/dim]")
|
|
118
|
+
r = _run_step("reflect", "agentreflect", reflect_args, cwd_str)
|
|
119
|
+
results.append(r)
|
|
120
|
+
else:
|
|
121
|
+
results.append({"step": "reflect", "tool": "agentreflect", "status": "skipped", "reason": "user skipped", "duration": 0.0})
|
|
122
|
+
|
|
123
|
+
# Display table
|
|
124
|
+
table = Table(title="Pipeline Results", show_header=True)
|
|
125
|
+
table.add_column("Step", style="bold")
|
|
126
|
+
table.add_column("Tool")
|
|
127
|
+
table.add_column("Status")
|
|
128
|
+
table.add_column("Duration")
|
|
129
|
+
table.add_column("Notes", max_width=50)
|
|
130
|
+
|
|
131
|
+
status_colors = {"pass": "green", "fail": "red", "skipped": "yellow", "error": "red"}
|
|
132
|
+
|
|
133
|
+
for r in results:
|
|
134
|
+
status = r.get("status", "unknown")
|
|
135
|
+
color = status_colors.get(status, "white")
|
|
136
|
+
duration = f"{r.get('duration', 0):.2f}s" if r.get("duration") else ""
|
|
137
|
+
note = r.get("reason", "") or (r.get("output", "")[:60] if r.get("output") else "")
|
|
138
|
+
table.add_row(
|
|
139
|
+
r["step"],
|
|
140
|
+
r.get("tool", ""),
|
|
141
|
+
f"[{color}]{status}[/{color}]",
|
|
142
|
+
duration,
|
|
143
|
+
note,
|
|
144
|
+
)
|
|
145
|
+
|
|
146
|
+
console.print()
|
|
147
|
+
console.print(table)
|
|
148
|
+
|
|
149
|
+
# Save last run
|
|
150
|
+
summary = {
|
|
151
|
+
"timestamp": datetime.now(timezone.utc).isoformat(),
|
|
152
|
+
"project": cwd_str,
|
|
153
|
+
"steps": results,
|
|
154
|
+
"total": len(results),
|
|
155
|
+
"passed": sum(1 for r in results if r.get("status") == "pass"),
|
|
156
|
+
"failed": sum(1 for r in results if r.get("status") == "fail"),
|
|
157
|
+
"skipped": sum(1 for r in results if r.get("status") == "skipped"),
|
|
158
|
+
}
|
|
159
|
+
try:
|
|
160
|
+
save_last_run(summary, root)
|
|
161
|
+
except Exception:
|
|
162
|
+
pass
|
|
163
|
+
|
|
164
|
+
if json_output:
|
|
165
|
+
console.print("\n[bold]JSON Output:[/bold]")
|
|
166
|
+
print(json.dumps(summary, indent=2))
|
|
167
|
+
|
|
168
|
+
# Final status
|
|
169
|
+
if summary["failed"] > 0:
|
|
170
|
+
console.print(f"\n[red]Pipeline completed with {summary['failed']} failure(s).[/red]")
|
|
171
|
+
raise typer.Exit(code=1)
|
|
172
|
+
else:
|
|
173
|
+
console.print(f"\n[green]Pipeline complete.[/green] {summary['passed']} passed, {summary['skipped']} skipped.\n")
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
"""agentkit status command."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
import json
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from typing import Optional
|
|
7
|
+
|
|
8
|
+
import typer
|
|
9
|
+
from rich.console import Console
|
|
10
|
+
from rich.table import Table
|
|
11
|
+
|
|
12
|
+
from agentkit_cli.tools import tool_status
|
|
13
|
+
from agentkit_cli.config import find_project_root, config_exists, load_last_run
|
|
14
|
+
|
|
15
|
+
console = Console()
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def status_command(
|
|
19
|
+
path: Optional[Path] = typer.Option(None, "--path", "-p", help="Project directory to check"),
|
|
20
|
+
json_output: bool = typer.Option(False, "--json", help="Output as JSON"),
|
|
21
|
+
) -> None:
|
|
22
|
+
"""Show health status of the Agent Quality Toolkit."""
|
|
23
|
+
root = path or find_project_root()
|
|
24
|
+
console.print(f"\n[bold]agentkit status[/bold] — project: {root}\n")
|
|
25
|
+
|
|
26
|
+
# Tool table
|
|
27
|
+
tool_table = Table(title="Quartet Tools", show_header=True)
|
|
28
|
+
tool_table.add_column("Tool", style="bold")
|
|
29
|
+
tool_table.add_column("Status")
|
|
30
|
+
tool_table.add_column("Version")
|
|
31
|
+
tool_table.add_column("Path")
|
|
32
|
+
|
|
33
|
+
ts = tool_status()
|
|
34
|
+
for tool, info in ts.items():
|
|
35
|
+
if info["installed"]:
|
|
36
|
+
status_str = "[green]✓ installed[/green]"
|
|
37
|
+
version_str = info.get("version") or "—"
|
|
38
|
+
path_str = info.get("path") or "—"
|
|
39
|
+
else:
|
|
40
|
+
status_str = "[red]✗ missing[/red]"
|
|
41
|
+
version_str = "—"
|
|
42
|
+
path_str = "—"
|
|
43
|
+
tool_table.add_row(tool, status_str, version_str, path_str)
|
|
44
|
+
|
|
45
|
+
console.print(tool_table)
|
|
46
|
+
|
|
47
|
+
# Project files table
|
|
48
|
+
files_table = Table(title="Project Files", show_header=True)
|
|
49
|
+
files_table.add_column("File", style="bold")
|
|
50
|
+
files_table.add_column("Status")
|
|
51
|
+
|
|
52
|
+
config_ok = config_exists(root)
|
|
53
|
+
claude_ok = (root / "CLAUDE.md").exists()
|
|
54
|
+
last_run_ok = (root / ".agentkit-last-run.json").exists()
|
|
55
|
+
|
|
56
|
+
files_table.add_row(
|
|
57
|
+
".agentkit.yaml",
|
|
58
|
+
"[green]✓ exists[/green]" if config_ok else "[yellow]✗ missing — run agentkit init[/yellow]",
|
|
59
|
+
)
|
|
60
|
+
files_table.add_row(
|
|
61
|
+
"CLAUDE.md",
|
|
62
|
+
"[green]✓ exists[/green]" if claude_ok else "[yellow]✗ missing[/yellow]",
|
|
63
|
+
)
|
|
64
|
+
files_table.add_row(
|
|
65
|
+
".agentkit-last-run.json",
|
|
66
|
+
"[green]✓ exists[/green]" if last_run_ok else "[dim]not found[/dim]",
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
console.print(files_table)
|
|
70
|
+
|
|
71
|
+
# Last run summary
|
|
72
|
+
last_run = load_last_run(root)
|
|
73
|
+
if last_run:
|
|
74
|
+
lr_table = Table(title="Last Run Summary", show_header=True)
|
|
75
|
+
lr_table.add_column("Key", style="bold")
|
|
76
|
+
lr_table.add_column("Value")
|
|
77
|
+
lr_table.add_row("Timestamp", last_run.get("timestamp", "—"))
|
|
78
|
+
lr_table.add_row("Passed", str(last_run.get("passed", "—")))
|
|
79
|
+
lr_table.add_row("Failed", str(last_run.get("failed", "—")))
|
|
80
|
+
lr_table.add_row("Skipped", str(last_run.get("skipped", "—")))
|
|
81
|
+
console.print(lr_table)
|
|
82
|
+
|
|
83
|
+
if json_output:
|
|
84
|
+
output = {
|
|
85
|
+
"project": str(root),
|
|
86
|
+
"tools": ts,
|
|
87
|
+
"files": {
|
|
88
|
+
".agentkit.yaml": config_ok,
|
|
89
|
+
"CLAUDE.md": claude_ok,
|
|
90
|
+
".agentkit-last-run.json": last_run_ok,
|
|
91
|
+
},
|
|
92
|
+
"last_run": last_run,
|
|
93
|
+
}
|
|
94
|
+
console.print("\n[bold]JSON Output:[/bold]")
|
|
95
|
+
print(json.dumps(output, indent=2))
|
|
96
|
+
|
|
97
|
+
console.print()
|
agentkit_cli/config.py
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
"""Config file management for .agentkit.yaml."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
import os
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from typing import Optional
|
|
7
|
+
|
|
8
|
+
try:
|
|
9
|
+
import yaml # type: ignore
|
|
10
|
+
HAS_YAML = True
|
|
11
|
+
except ImportError:
|
|
12
|
+
HAS_YAML = False
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
CONFIG_FILENAME = ".agentkit.yaml"
|
|
16
|
+
LAST_RUN_FILENAME = ".agentkit-last-run.json"
|
|
17
|
+
|
|
18
|
+
DEFAULT_CONFIG = {
|
|
19
|
+
"tools": {
|
|
20
|
+
"coderace": True,
|
|
21
|
+
"agentmd": True,
|
|
22
|
+
"agentlint": True,
|
|
23
|
+
"agentreflect": True,
|
|
24
|
+
},
|
|
25
|
+
"defaults": {
|
|
26
|
+
"min_score": 80,
|
|
27
|
+
"context_file": "CLAUDE.md",
|
|
28
|
+
},
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
DEFAULT_CONFIG_YAML = """\
|
|
32
|
+
tools:
|
|
33
|
+
coderace: true
|
|
34
|
+
agentmd: true
|
|
35
|
+
agentlint: true
|
|
36
|
+
agentreflect: true
|
|
37
|
+
defaults:
|
|
38
|
+
min_score: 80
|
|
39
|
+
context_file: CLAUDE.md
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def find_project_root(start: Optional[Path] = None) -> Path:
|
|
44
|
+
"""Walk up to find git root; fall back to cwd."""
|
|
45
|
+
cwd = start or Path.cwd()
|
|
46
|
+
current = cwd
|
|
47
|
+
while True:
|
|
48
|
+
if (current / ".git").exists():
|
|
49
|
+
return current
|
|
50
|
+
parent = current.parent
|
|
51
|
+
if parent == current:
|
|
52
|
+
break
|
|
53
|
+
current = parent
|
|
54
|
+
return cwd
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def config_path(root: Optional[Path] = None) -> Path:
|
|
58
|
+
root = root or find_project_root()
|
|
59
|
+
return root / CONFIG_FILENAME
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def last_run_path(root: Optional[Path] = None) -> Path:
|
|
63
|
+
root = root or find_project_root()
|
|
64
|
+
return root / LAST_RUN_FILENAME
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def config_exists(root: Optional[Path] = None) -> bool:
|
|
68
|
+
return config_path(root).exists()
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def write_default_config(root: Optional[Path] = None) -> Path:
|
|
72
|
+
path = config_path(root)
|
|
73
|
+
path.write_text(DEFAULT_CONFIG_YAML)
|
|
74
|
+
return path
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def load_last_run(root: Optional[Path] = None) -> Optional[dict]:
|
|
78
|
+
import json
|
|
79
|
+
p = last_run_path(root)
|
|
80
|
+
if not p.exists():
|
|
81
|
+
return None
|
|
82
|
+
try:
|
|
83
|
+
return json.loads(p.read_text())
|
|
84
|
+
except Exception:
|
|
85
|
+
return None
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def save_last_run(data: dict, root: Optional[Path] = None) -> None:
|
|
89
|
+
import json
|
|
90
|
+
p = last_run_path(root)
|
|
91
|
+
p.write_text(json.dumps(data, indent=2))
|
agentkit_cli/main.py
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"""agentkit CLI entry point."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
import typer
|
|
5
|
+
from typing import Optional
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
from agentkit_cli.commands.init_cmd import init_command
|
|
9
|
+
from agentkit_cli.commands.run_cmd import run_command
|
|
10
|
+
from agentkit_cli.commands.status_cmd import status_command
|
|
11
|
+
|
|
12
|
+
app = typer.Typer(
|
|
13
|
+
name="agentkit",
|
|
14
|
+
help="Unified CLI for the Agent Quality Toolkit (agentmd, coderace, agentlint, agentreflect).",
|
|
15
|
+
add_completion=False,
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@app.command("init")
|
|
20
|
+
def init(
|
|
21
|
+
path: Optional[Path] = typer.Option(None, "--path", "-p", help="Project root"),
|
|
22
|
+
) -> None:
|
|
23
|
+
"""Initialize agentkit in a project. Creates .agentkit.yaml and checks for quartet tools."""
|
|
24
|
+
init_command(path=path)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
@app.command("run")
|
|
28
|
+
def run(
|
|
29
|
+
path: Optional[Path] = typer.Option(None, "--path", "-p", help="Project directory"),
|
|
30
|
+
skip: Optional[list[str]] = typer.Option(None, "--skip", help="Steps to skip: generate, lint, benchmark, reflect"),
|
|
31
|
+
benchmark: bool = typer.Option(False, "--benchmark", help="Include benchmark step (off by default)"),
|
|
32
|
+
json_output: bool = typer.Option(False, "--json", help="Emit summary as JSON"),
|
|
33
|
+
notes: Optional[str] = typer.Option(None, "--notes", help="Notes for agentreflect"),
|
|
34
|
+
) -> None:
|
|
35
|
+
"""Run the full Agent Quality pipeline sequentially."""
|
|
36
|
+
run_command(path=path, skip=skip, benchmark=benchmark, json_output=json_output, notes=notes)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
@app.command("status")
|
|
40
|
+
def status(
|
|
41
|
+
path: Optional[Path] = typer.Option(None, "--path", "-p", help="Project directory"),
|
|
42
|
+
json_output: bool = typer.Option(False, "--json", help="Emit status as JSON"),
|
|
43
|
+
) -> None:
|
|
44
|
+
"""Show health status of toolkit and current project."""
|
|
45
|
+
status_command(path=path, json_output=json_output)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
@app.callback(invoke_without_command=True)
|
|
49
|
+
def main(
|
|
50
|
+
ctx: typer.Context,
|
|
51
|
+
version: bool = typer.Option(False, "--version", "-V", help="Show version and exit"),
|
|
52
|
+
) -> None:
|
|
53
|
+
if version:
|
|
54
|
+
from agentkit_cli import __version__
|
|
55
|
+
typer.echo(f"agentkit-cli v{__version__}")
|
|
56
|
+
raise typer.Exit()
|
|
57
|
+
if ctx.invoked_subcommand is None:
|
|
58
|
+
typer.echo(ctx.get_help())
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
if __name__ == "__main__":
|
|
62
|
+
app()
|
agentkit_cli/tools.py
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
"""Tool detection and subprocess execution utilities."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
import shutil
|
|
5
|
+
import subprocess
|
|
6
|
+
from typing import Optional
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
QUARTET_TOOLS = ["agentmd", "coderace", "agentlint", "agentreflect"]
|
|
10
|
+
|
|
11
|
+
INSTALL_HINTS = {
|
|
12
|
+
"agentmd": "pip install agentmd",
|
|
13
|
+
"coderace": "pip install coderace",
|
|
14
|
+
"agentlint": "pip install agentlint",
|
|
15
|
+
"agentreflect": "pip install agentreflect",
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def which(tool: str) -> Optional[str]:
|
|
20
|
+
"""Return path to tool if installed, else None."""
|
|
21
|
+
return shutil.which(tool)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def is_installed(tool: str) -> bool:
|
|
25
|
+
return which(tool) is not None
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def get_version(tool: str) -> Optional[str]:
|
|
29
|
+
"""Try to get the version string for a tool."""
|
|
30
|
+
path = which(tool)
|
|
31
|
+
if not path:
|
|
32
|
+
return None
|
|
33
|
+
for flag in ["--version", "version"]:
|
|
34
|
+
try:
|
|
35
|
+
result = subprocess.run(
|
|
36
|
+
[path, flag],
|
|
37
|
+
capture_output=True,
|
|
38
|
+
text=True,
|
|
39
|
+
timeout=5,
|
|
40
|
+
)
|
|
41
|
+
output = (result.stdout + result.stderr).strip()
|
|
42
|
+
if output:
|
|
43
|
+
# Return first line
|
|
44
|
+
return output.splitlines()[0]
|
|
45
|
+
except (subprocess.TimeoutExpired, FileNotFoundError, OSError):
|
|
46
|
+
continue
|
|
47
|
+
return "installed"
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def run_tool(tool: str, args: list[str], cwd: Optional[str] = None) -> subprocess.CompletedProcess:
|
|
51
|
+
"""Run a quartet tool via subprocess."""
|
|
52
|
+
path = which(tool)
|
|
53
|
+
if not path:
|
|
54
|
+
raise FileNotFoundError(f"Tool not found: {tool}. Install with: {INSTALL_HINTS.get(tool, f'pip install {tool}')}")
|
|
55
|
+
return subprocess.run(
|
|
56
|
+
[path] + args,
|
|
57
|
+
capture_output=True,
|
|
58
|
+
text=True,
|
|
59
|
+
cwd=cwd,
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def tool_status() -> dict[str, dict]:
|
|
64
|
+
"""Return status dict for all quartet tools."""
|
|
65
|
+
result = {}
|
|
66
|
+
for tool in QUARTET_TOOLS:
|
|
67
|
+
installed = is_installed(tool)
|
|
68
|
+
result[tool] = {
|
|
69
|
+
"installed": installed,
|
|
70
|
+
"path": which(tool) if installed else None,
|
|
71
|
+
"version": get_version(tool) if installed else None,
|
|
72
|
+
}
|
|
73
|
+
return result
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: agentkit-cli
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Unified CLI for the Agent Quality Toolkit (agentmd, coderace, agentlint, agentreflect)
|
|
5
|
+
Project-URL: Homepage, https://github.com/mikiships/agentkit-cli
|
|
6
|
+
Project-URL: Repository, https://github.com/mikiships/agentkit-cli
|
|
7
|
+
Project-URL: Changelog, https://github.com/mikiships/agentkit-cli/blob/main/CHANGELOG.md
|
|
8
|
+
Author-email: mikiships <frank.michaels2123@gmail.com>
|
|
9
|
+
License: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: agents,ai,benchmark,cli,llm,quality,toolkit
|
|
12
|
+
Classifier: Development Status :: 3 - Alpha
|
|
13
|
+
Classifier: Environment :: Console
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Topic :: Software Development :: Quality Assurance
|
|
20
|
+
Classifier: Topic :: Software Development :: Testing
|
|
21
|
+
Requires-Python: >=3.11
|
|
22
|
+
Requires-Dist: rich>=13.0.0
|
|
23
|
+
Requires-Dist: typer>=0.9.0
|
|
24
|
+
Description-Content-Type: text/markdown
|
|
25
|
+
|
|
26
|
+
# agentkit-cli
|
|
27
|
+
|
|
28
|
+
**One install to rule them all.**
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
pip install agentkit-cli
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Get the full Agent Quality Toolkit pipeline in a single command — no more juggling four separate `pip install` steps.
|
|
35
|
+
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
## What is it?
|
|
39
|
+
|
|
40
|
+
`agentkit-cli` is a unified meta-CLI that wraps the **Agent Quality Toolkit quartet**:
|
|
41
|
+
|
|
42
|
+
| Tool | Purpose |
|
|
43
|
+
|------|---------|
|
|
44
|
+
| [agentmd](https://pypi.org/project/agentmd/) | Generate CLAUDE.md context files |
|
|
45
|
+
| [agentlint](https://pypi.org/project/agentlint/) | Lint AI context files and git diffs |
|
|
46
|
+
| [coderace](https://pypi.org/project/coderace/) | Benchmark AI coding performance |
|
|
47
|
+
| [agentreflect](https://pypi.org/project/agentreflect/) | Generate reflection reports from failures |
|
|
48
|
+
|
|
49
|
+
## Pipeline Overview
|
|
50
|
+
|
|
51
|
+
```
|
|
52
|
+
agentkit run
|
|
53
|
+
│
|
|
54
|
+
├── 1. agentmd generate → produces CLAUDE.md
|
|
55
|
+
├── 2. agentlint check-context → lints CLAUDE.md
|
|
56
|
+
├── 3. agentlint (diff) → lints recent changes
|
|
57
|
+
├── 4. coderace benchmark → (opt-in via --benchmark)
|
|
58
|
+
└── 5. agentreflect generate → reflection on failures
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Zero to full pipeline in 30 seconds.
|
|
62
|
+
|
|
63
|
+
---
|
|
64
|
+
|
|
65
|
+
## Installation
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
pip install agentkit-cli
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Install the quartet tools too:
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
pip install agentmd agentlint coderace agentreflect
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
---
|
|
78
|
+
|
|
79
|
+
## Usage
|
|
80
|
+
|
|
81
|
+
### `agentkit init`
|
|
82
|
+
|
|
83
|
+
Initialize agentkit in a project. Checks which tools are installed and creates `.agentkit.yaml`.
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
agentkit init
|
|
87
|
+
agentkit init --path /my/project
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Creates `.agentkit.yaml`:
|
|
91
|
+
|
|
92
|
+
```yaml
|
|
93
|
+
tools:
|
|
94
|
+
coderace: true
|
|
95
|
+
agentmd: true
|
|
96
|
+
agentlint: true
|
|
97
|
+
agentreflect: true
|
|
98
|
+
defaults:
|
|
99
|
+
min_score: 80
|
|
100
|
+
context_file: CLAUDE.md
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
---
|
|
104
|
+
|
|
105
|
+
### `agentkit run`
|
|
106
|
+
|
|
107
|
+
Run the full quality pipeline sequentially.
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
agentkit run
|
|
111
|
+
agentkit run --path /my/project
|
|
112
|
+
agentkit run --skip generate
|
|
113
|
+
agentkit run --skip lint
|
|
114
|
+
agentkit run --skip benchmark
|
|
115
|
+
agentkit run --skip reflect
|
|
116
|
+
agentkit run --benchmark # include benchmark step
|
|
117
|
+
agentkit run --json # emit summary as JSON
|
|
118
|
+
agentkit run --notes "regression after refactor"
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
Missing tools are skipped automatically with a warning — you don't need all four installed.
|
|
122
|
+
|
|
123
|
+
---
|
|
124
|
+
|
|
125
|
+
### `agentkit status`
|
|
126
|
+
|
|
127
|
+
Quick health check: tool versions, config presence, last run summary.
|
|
128
|
+
|
|
129
|
+
```bash
|
|
130
|
+
agentkit status
|
|
131
|
+
agentkit status --path /my/project
|
|
132
|
+
agentkit status --json
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
---
|
|
136
|
+
|
|
137
|
+
## Links
|
|
138
|
+
|
|
139
|
+
- [agentmd](https://pypi.org/project/agentmd/)
|
|
140
|
+
- [agentlint](https://pypi.org/project/agentlint/)
|
|
141
|
+
- [coderace](https://pypi.org/project/coderace/)
|
|
142
|
+
- [agentreflect](https://pypi.org/project/agentreflect/)
|
|
143
|
+
|
|
144
|
+
---
|
|
145
|
+
|
|
146
|
+
## License
|
|
147
|
+
|
|
148
|
+
MIT
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
agentkit_cli/__init__.py,sha256=NIxS8CSOajv7vlg679bwFnr5MF-JZOr6Tset4YdbO38,86
|
|
2
|
+
agentkit_cli/config.py,sha256=7TaODp9nTJjsPsFi59sCwumjbM9V258dZAynDE2-eHA,2015
|
|
3
|
+
agentkit_cli/main.py,sha256=HmcBYJNDPFPHwAPJa012QLk4SymMCVm1_7ayO06_KK0,2182
|
|
4
|
+
agentkit_cli/tools.py,sha256=__K3HXsYr62w09awmsENQmWriX231RNr8lAeQu114Go,2100
|
|
5
|
+
agentkit_cli/commands/__init__.py,sha256=wl_DixJDOCreBHQKEeSeFo8eeHB8ua1IOw2ePGV4x-I,19
|
|
6
|
+
agentkit_cli/commands/init_cmd.py,sha256=dFr4PPP7GXa4Xlb92RC3EVt95MVH_nvyCuXeEZf8rWw,2173
|
|
7
|
+
agentkit_cli/commands/run_cmd.py,sha256=3btPldzn0GFBbgwgv63V1pribzESHKvk5v_G43mp11w,6423
|
|
8
|
+
agentkit_cli/commands/status_cmd.py,sha256=k4ifWVglXQkje-bG5gKoc7CeOmYjY0uOSG7EnD9djPs,3239
|
|
9
|
+
agentkit_cli-0.1.0.dist-info/METADATA,sha256=mZofFnJF8iHRPlO3Cq1Ds1D4hQZFn3xJvSJBMSPnSEk,3663
|
|
10
|
+
agentkit_cli-0.1.0.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
|
|
11
|
+
agentkit_cli-0.1.0.dist-info/entry_points.txt,sha256=wTUbOJx7ZMH6KtzmS5zWfjXVUduScmdtris750nFKDs,51
|
|
12
|
+
agentkit_cli-0.1.0.dist-info/licenses/LICENSE,sha256=_xg5HZ1WlLJEQpwpHZeQrhtyhgKC7ke4BL_qH6U3Jzo,1066
|
|
13
|
+
agentkit_cli-0.1.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 mikiships
|
|
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.
|