def build_parser() -> argparse.ArgumentParser:
"""Build the CLI argument parser."""
parser = argparse.ArgumentParser(
prog="akms",
description="AKMS — Adaptive Knowledge Management System CLI",
)
_add_repo_argument(parser, top_level=True)
# Host adapters probe `akms --version` before `--help` to decide whether
# they can enforce a supported-version range, so the output shape
# (`akms <version>` on stdout, exit 0) is part of the CLI contract.
parser.add_argument(
"--version",
action="version",
version=f"%(prog)s {__version__}",
)
subparsers = parser.add_subparsers(dest="command", help="Available commands")
# promote
promote_parser = subparsers.add_parser(
"promote",
help="Promote tentative node to established",
)
_add_repo_argument(promote_parser)
promote_parser.add_argument("node_id", help="Node ID to promote")
promote_parser.set_defaults(func=cmd_promote)
# suppress
suppress_parser = subparsers.add_parser(
"suppress",
help="Suppress node (set to draft)",
)
_add_repo_argument(suppress_parser)
suppress_parser.add_argument("node_id", help="Node ID to suppress")
suppress_parser.set_defaults(func=cmd_suppress)
# deprecate
deprecate_parser = subparsers.add_parser(
"deprecate",
help="Deprecate a node",
)
_add_repo_argument(deprecate_parser)
deprecate_parser.add_argument("node_id", help="Node ID to deprecate")
deprecate_parser.set_defaults(func=cmd_deprecate)
# status
status_parser = subparsers.add_parser(
"status",
help="Run graph health report",
)
_add_repo_argument(status_parser)
status_parser.set_defaults(func=cmd_status)
# query
query_parser = subparsers.add_parser(
"query",
help="Query the compiled graph by seed tags",
)
_add_repo_argument(query_parser)
query_parser.add_argument("tags", nargs="+", help="One or more seed tags")
query_parser.add_argument(
"--role",
choices=("implementer", "code_reviewer", "physics_reviewer"),
default="implementer",
help="Agent role query profile (default: implementer)",
)
query_parser.add_argument(
"--max-depth",
type=int,
default=2,
help="Maximum traversal depth from seed nodes (default: 2)",
)
query_parser.add_argument(
"--graph",
default=None,
help="Compiled graph path relative to repo (default: knowledge/graph/graph.json)",
)
query_parser.set_defaults(func=cmd_query)
# loadout
loadout_parser = subparsers.add_parser(
"loadout",
help="Query the graph and generate a task loadout",
)
_add_repo_argument(loadout_parser)
loadout_parser.add_argument("task_id", help="Task identifier for the loadout")
loadout_parser.add_argument(
"--phase", type=int, required=True, help="Task phase number"
)
loadout_parser.add_argument(
"--tags",
nargs="+",
required=True,
help="One or more query seed tags",
)
loadout_parser.add_argument(
"--role",
choices=("implementer", "code_reviewer", "physics_reviewer"),
default="implementer",
help="Agent role query profile (default: implementer)",
)
loadout_parser.add_argument(
"--mode",
choices=("routing", "full"),
default="routing",
help="Loadout content mode (default: routing)",
)
loadout_parser.add_argument(
"--max-depth",
type=int,
default=2,
help="Maximum traversal depth from seed nodes (default: 2)",
)
loadout_parser.add_argument(
"--available-context",
type=int,
default=0,
help="Estimated available context tokens recorded in the header",
)
loadout_parser.add_argument(
"--graph",
default=None,
help="Compiled graph path relative to repo (default: knowledge/graph/graph.json)",
)
loadout_parser.add_argument(
"--output",
default=None,
help="Exact output path relative to repo (default: canonical loadout path)",
)
loadout_parser.set_defaults(func=cmd_loadout)
# # ── resolve-task ─────────────────────────────────────────────────
resolve_parser = subparsers.add_parser(
"resolve-task",
help="Resolve exact task knowledge into a loadout and resolution manifest",
)
_add_repo_argument(resolve_parser)
resolve_parser.add_argument(
"--task-json",
required=True,
help="Path to the task JSON file",
)
resolve_parser.add_argument(
"--routes",
required=True,
help="Path to the task route index (JSON or YAML)",
)
resolve_parser.add_argument(
"--role",
choices=("implementer", "code_reviewer", "physics_reviewer"),
default="implementer",
help="Agent role query profile (default: implementer)",
)
resolve_parser.add_argument(
"--phase",
type=int,
default=None,
help="Phase number for output filenames (default: task.phase or 1)",
)
resolve_parser.add_argument(
"--output",
default=None,
help="Loadout output path relative to repo "
"(default: knowledge/loadouts/<phase>-<task>-<role>-loadout.md)",
)
resolve_parser.add_argument(
"--manifest",
default=None,
help="Resolution manifest output path relative to repo "
"(default: knowledge/resolution-manifests/<phase>-<task>-<role>-manifest.json)",
)
resolve_parser.add_argument(
"--changed-paths",
default=None,
help="JSON file with a list (or {changed_paths: [...]}) of changed paths. "
"A bare single path string is rejected — always pass a sequence.",
)
resolve_parser.add_argument(
"--base",
default=None,
help="Git base revision for changed-path discovery (mutually exclusive "
"with --changed-paths)",
)
resolve_parser.add_argument(
"--head",
default=None,
help="Git head revision for changed-path discovery (default: HEAD)",
)
resolve_parser.add_argument(
"--mode",
choices=("routing", "full"),
default="routing",
help="Loadout content mode (default: routing)",
)
resolve_parser.add_argument(
"--max-depth",
type=int,
default=2,
help="Advisory query ego-graph depth (default: 2)",
)
resolve_parser.add_argument(
"--available-context",
type=int,
default=0,
help="Estimated available context tokens recorded in the loadout header",
)
resolve_parser.add_argument(
"--graph",
default=None,
help="Compiled graph path relative to repo (default: knowledge/graph/graph.json)",
)
resolve_parser.set_defaults(func=cmd_resolve_task)
# orchestrate
orch_parser = subparsers.add_parser(
"orchestrate",
help="Run the orchestrator pipeline",
)
_add_repo_argument(orch_parser)
orch_parser.add_argument(
"--plan",
"-p",
default="",
help="Plan name (used for branch naming)",
)
orch_parser.add_argument(
"--config",
"-c",
default=None,
help="Path to propagation_config.yaml (default: knowledge/graph/propagation_config.yaml)",
)
orch_parser.add_argument(
"--backend",
"-B",
default=None,
choices=sorted(BACKENDS),
help=(
"Built-in agent runtime: claude-sdk (default, Claude Agent SDK), "
"claude-cli (drives the `claude` CLI headless — needs the claude "
"binary on PATH, not the SDK), codex-sdk (OpenAI Agents SDK), "
"codex-cli (drives `codex exec` — needs the codex binary on PATH), "
"or local (openai-agents against a local OpenAI-compatible endpoint "
"via AKMS_LLM_API_BASE). Overridden by --agent."
),
)
orch_parser.add_argument(
"--agent",
"-a",
default=None,
help=(
"Dotted import path to an AKMSAgent subclass "
"(e.g. myproject.akms_agent.MyProjectAgent or "
"akms.agents.base_codex.AKMSCodexAgent). Overrides --backend. "
"When both are omitted, uses the default AKMSAgent."
),
)
orch_parser.add_argument(
"--model",
"-m",
default=None,
help="Model override (e.g. claude-opus-4-6 for high-risk tasks)",
)
orch_parser.add_argument(
"--resume",
"-R",
action="store_true",
default=False,
help="Resume from saved pipeline state",
)
orch_parser.add_argument(
"--terminal",
"-t",
action="store_true",
default=False,
help="Use terminal checkpoint mode instead of file-based",
)
orch_parser.add_argument(
"--spec",
"-s",
default=None,
help="Path to specification file",
)
orch_parser.add_argument(
"--goal",
"-g",
default=None,
help="Goal description for the pipeline",
)
orch_parser.set_defaults(func=cmd_orchestrate)
# # Mirror provider status / refresh (separate module).
from akms.cli.provider_commands import register_provider_commands
register_provider_commands(subparsers)
# # Global vault status / install (separate module).
from akms.cli.vault_commands import register_vault_commands
register_vault_commands(subparsers)
return parser