amkernel 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.
- amkernel/__init__.py +7 -0
- amkernel/__main__.py +5 -0
- amkernel/artifact_store.py +1363 -0
- amkernel/cli.py +172 -0
- amkernel/distribution.py +597 -0
- amkernel/output.py +444 -0
- amkernel/protocol.py +1464 -0
- amkernel/py.typed +0 -0
- amkernel/runner.py +1222 -0
- amkernel/runtime.py +581 -0
- amkernel/server.py +774 -0
- amkernel/skill/SKILL.md +124 -0
- amkernel/skill/references/amkernel-appium.md +46 -0
- amkernel/skill/references/official-skills.json +232 -0
- amkernel/skill/references/official-sources.json +21 -0
- amkernel/skill/scripts/verify_official_skills.py +252 -0
- amkernel/structured_output.py +328 -0
- amkernel/supervisor.py +1453 -0
- amkernel/workspace.py +305 -0
- amkernel-0.1.0.dist-info/METADATA +105 -0
- amkernel-0.1.0.dist-info/RECORD +23 -0
- amkernel-0.1.0.dist-info/WHEEL +4 -0
- amkernel-0.1.0.dist-info/entry_points.txt +3 -0
amkernel/cli.py
ADDED
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
"""AMKernel server and distribution command-line interface."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import argparse
|
|
6
|
+
import asyncio
|
|
7
|
+
import json
|
|
8
|
+
import sys
|
|
9
|
+
from collections.abc import Sequence
|
|
10
|
+
from dataclasses import replace
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
|
|
13
|
+
from amkernel import __version__
|
|
14
|
+
from amkernel.distribution import Client, DistributionError, DistributionManager, ProjectStatus
|
|
15
|
+
from amkernel.server import serve_stdio
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def build_parser() -> argparse.ArgumentParser:
|
|
19
|
+
"""Build the command-line parser."""
|
|
20
|
+
parser = argparse.ArgumentParser(
|
|
21
|
+
prog="amkernel",
|
|
22
|
+
description="Install, verify, and run AMKernel for Coding Agents.",
|
|
23
|
+
)
|
|
24
|
+
parser.add_argument("--version", action="version", version=f"%(prog)s {__version__}")
|
|
25
|
+
subparsers = parser.add_subparsers(dest="command")
|
|
26
|
+
for name in ("install", "status", "doctor", "update"):
|
|
27
|
+
command = subparsers.add_parser(name)
|
|
28
|
+
command.add_argument("--client", choices=("auto", "omp", "pi", "codex"), default="auto")
|
|
29
|
+
command.add_argument("--project", type=Path, default=Path.cwd())
|
|
30
|
+
command.add_argument("--json", action="store_true")
|
|
31
|
+
if name in {"install", "update"}:
|
|
32
|
+
command.add_argument("--scope", choices=("project",))
|
|
33
|
+
command.add_argument("--yes", action="store_true")
|
|
34
|
+
uninstall = subparsers.add_parser("uninstall")
|
|
35
|
+
uninstall.add_argument("--client", choices=("auto", "omp", "pi", "codex"), default="auto")
|
|
36
|
+
uninstall.add_argument("--project", type=Path, default=Path.cwd())
|
|
37
|
+
uninstall.add_argument("--scope", choices=("project",))
|
|
38
|
+
uninstall.add_argument("--global", dest="global_scope", action="store_true")
|
|
39
|
+
uninstall.add_argument("--yes", action="store_true")
|
|
40
|
+
uninstall.add_argument("--json", action="store_true")
|
|
41
|
+
return parser
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def _confirm(message: str, assumed: bool) -> None:
|
|
45
|
+
if assumed:
|
|
46
|
+
return
|
|
47
|
+
if not sys.stdin.isatty():
|
|
48
|
+
raise DistributionError("confirmation required; rerun with --yes")
|
|
49
|
+
answer = input(f"{message} [y/N] ").strip().lower()
|
|
50
|
+
if answer not in {"y", "yes"}:
|
|
51
|
+
raise DistributionError("cancelled")
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def _print_status(status: ProjectStatus, as_json: bool) -> None:
|
|
55
|
+
if as_json:
|
|
56
|
+
print(json.dumps(status.to_dict(), indent=2, sort_keys=True))
|
|
57
|
+
return
|
|
58
|
+
print(f"Runtime installed: {'yes' if status.runtime_installed else 'no'}")
|
|
59
|
+
print(f"Skill installed: {'yes' if status.skill_installed else 'no'}")
|
|
60
|
+
print(f"MCP configured: {'yes' if status.mcp_configured else 'no'}")
|
|
61
|
+
print(f"Project: {status.project}")
|
|
62
|
+
if status.client:
|
|
63
|
+
print(f"Client: {status.client}")
|
|
64
|
+
if status.runtime_version:
|
|
65
|
+
print(f"Runtime version: {status.runtime_version}")
|
|
66
|
+
if status.skill_version:
|
|
67
|
+
print(f"Skill version: {status.skill_version}")
|
|
68
|
+
if status.known_project_references:
|
|
69
|
+
print("Known project references:")
|
|
70
|
+
for project in status.known_project_references:
|
|
71
|
+
print(f"- {project}")
|
|
72
|
+
if status.references_truncated:
|
|
73
|
+
print("- additional references omitted")
|
|
74
|
+
if status.detail:
|
|
75
|
+
print(f"Next action: {status.detail}")
|
|
76
|
+
elif status.restart_required:
|
|
77
|
+
print("Next action: restart the Coding Agent to load AMKernel")
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def _client(manager: DistributionManager, requested: str) -> Client:
|
|
81
|
+
return manager.select_client(requested)
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def _require_automation_contract(args: argparse.Namespace) -> None:
|
|
85
|
+
if not getattr(args, "yes", False):
|
|
86
|
+
return
|
|
87
|
+
if not args.json:
|
|
88
|
+
raise DistributionError("automated mutations require --json")
|
|
89
|
+
if args.command == "uninstall" and args.global_scope:
|
|
90
|
+
return
|
|
91
|
+
if args.client == "auto":
|
|
92
|
+
raise DistributionError("automated project mutations require an explicit --client")
|
|
93
|
+
if args.scope != "project":
|
|
94
|
+
raise DistributionError("automated project mutations require --scope project")
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def _print_plan(manager: DistributionManager, client: Client) -> None:
|
|
98
|
+
print("Planned changes:", file=sys.stderr)
|
|
99
|
+
for change in manager.planned_changes(client):
|
|
100
|
+
print(f"- {change}", file=sys.stderr)
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def _run_distribution(args: argparse.Namespace) -> int:
|
|
104
|
+
manager = DistributionManager(args.project)
|
|
105
|
+
_require_automation_contract(args)
|
|
106
|
+
if args.command == "install":
|
|
107
|
+
client = _client(manager, args.client)
|
|
108
|
+
_print_plan(manager, client)
|
|
109
|
+
_confirm(
|
|
110
|
+
f"Install AMKernel globally and enable it for {manager.project}?",
|
|
111
|
+
args.yes,
|
|
112
|
+
)
|
|
113
|
+
status = manager.install(client)
|
|
114
|
+
elif args.command == "status":
|
|
115
|
+
status_client = None if args.client == "auto" else _client(manager, args.client)
|
|
116
|
+
status = manager.status(status_client)
|
|
117
|
+
elif args.command == "doctor":
|
|
118
|
+
status = manager.doctor(_client(manager, args.client))
|
|
119
|
+
elif args.command == "update":
|
|
120
|
+
client = _client(manager, args.client)
|
|
121
|
+
_print_plan(manager, client)
|
|
122
|
+
_confirm("Update the global AMKernel Runtime used by every enabled project?", args.yes)
|
|
123
|
+
status = manager.update(client)
|
|
124
|
+
elif args.command == "uninstall":
|
|
125
|
+
if args.global_scope:
|
|
126
|
+
references = sorted(manager.known_projects())
|
|
127
|
+
retained_references = tuple(references[:100])
|
|
128
|
+
reference_note = (
|
|
129
|
+
" No enabled project references are registered."
|
|
130
|
+
if not references
|
|
131
|
+
else " Registered project references will stop working: "
|
|
132
|
+
+ ", ".join(references[:20])
|
|
133
|
+
)
|
|
134
|
+
print(reference_note.strip(), file=sys.stderr)
|
|
135
|
+
_confirm("Remove the global AMKernel Runtime?" + reference_note, args.yes)
|
|
136
|
+
manager.uninstall_global()
|
|
137
|
+
status = replace(
|
|
138
|
+
manager.status(None),
|
|
139
|
+
known_project_references=retained_references,
|
|
140
|
+
references_truncated=len(references) > len(retained_references),
|
|
141
|
+
)
|
|
142
|
+
else:
|
|
143
|
+
_confirm(f"Disable AMKernel in {manager.project}?", args.yes)
|
|
144
|
+
status = manager.uninstall_project(_client(manager, args.client))
|
|
145
|
+
else:
|
|
146
|
+
raise AssertionError(f"unexpected command: {args.command}")
|
|
147
|
+
_print_status(status, args.json)
|
|
148
|
+
return 0 if not status.detail or args.command == "uninstall" else 1
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
def main(argv: Sequence[str] | None = None) -> int:
|
|
152
|
+
"""Run the MCP server by default, or an explicit distribution command."""
|
|
153
|
+
parser = build_parser()
|
|
154
|
+
args = parser.parse_args(argv)
|
|
155
|
+
if args.command:
|
|
156
|
+
try:
|
|
157
|
+
return _run_distribution(args)
|
|
158
|
+
except DistributionError as error:
|
|
159
|
+
if getattr(args, "json", False):
|
|
160
|
+
message = str(error)[:4096]
|
|
161
|
+
print(
|
|
162
|
+
json.dumps(
|
|
163
|
+
{"ok": False, "error": {"type": "DistributionError", "message": message}}
|
|
164
|
+
)
|
|
165
|
+
)
|
|
166
|
+
else:
|
|
167
|
+
print(f"amkernel: {error}", file=sys.stderr)
|
|
168
|
+
return 2
|
|
169
|
+
try:
|
|
170
|
+
return asyncio.run(serve_stdio())
|
|
171
|
+
except KeyboardInterrupt:
|
|
172
|
+
return 130
|