Releases: mad-core/mad-cli
Release list
v0.4.0
v0.4.0 (2026-07-04)
Documentation
- Document named environment profiles (
6bbf294)
Update the living-docs for mad profiles and mad install --profile: cli.md (new profiles command family + --profile row), components.md (the new core/profiles.py and commands/profiles.py modules), configuration.md (profiles storage + precedence), installation.md (--profile), cli-design.md (masking + restart hint), testing-strategy.md (new test modules); regenerate the deterministic source-tree/test-tree; re-acknowledge the affected docs.
Co-Authored-By: Claude Fable 5 noreply@anthropic.com
Claude-Session: https://claude.ai/code/session_01GWHBALtjHVd176YddWc9YP
Signed-off-by: Jose Salamanca jose.salamancacoy@gmail.com
- Document the HTTP API, service mode and the use-case layer (
a1fc77e)
Add docs/03-contracts/http-api.md (routes, bearer auth, always-masked secrets, error mapping, the synchronous-MVP limitation) and register it in the manifest. Extend cli.md with mad serve / mad service, README with the server extra and the auto-provisioned server venv, and refresh the architecture/convention pages for the new use-case layer, the server package, service-mode config (api-token, server-venv) and the optional fastapi/uvicorn deps. Regenerate the deterministic source/test trees and bump the manifest acknowledgements.
Co-Authored-By: Claude Fable 5 noreply@anthropic.com
Claude-Session: https://claude.ai/code/session_01GWHBALtjHVd176YddWc9YP
Signed-off-by: Jose Salamanca jose.salamancacoy@gmail.com
- Re-ack installation.md after server-extra coverage (
a2da5f9)
The page already documents the [server] extra and mad service flow; its acknowledgement lagged the commit that changed its trigger paths.
Co-Authored-By: Claude Fable 5 noreply@anthropic.com
Claude-Session: https://claude.ai/code/session_01GWHBALtjHVd176YddWc9YP
Signed-off-by: Jose Salamanca jose.salamancacoy@gmail.com
Features
- cli: Local HTTP API with CLI parity and mad service mode (
09f30e1)
Expose every CLI capability over a local HTTP API so a UI/dashboard can build on the same use cases. Shipped as an optional server extra (fastapi + uvicorn) so the base CLI stays a two-dependency package — import mad_cli.app never pulls in FastAPI. The FastAPI routes are thin adapters over core.usecases, giving OpenAPI for free and parity with the CLI by construction.
Security: binds 127.0.0.1 by default; a bearer token is auto-generated at config_root()/api-token (0600) and required on every route except /health; a non-loopback --host prints a loud warning. Secret values are always masked on reads — no reveal flag exists on the API.
Service mode: mad serve runs the API in the foreground; mad service install|uninstall|status|update manages a boot-persistent service (systemd user unit on Linux, launchd LaunchAgent on macOS) rendered from packaged templates. When the server extra is not importable, mad service install auto-provisions a dedicated venv under config_root()/server-venv (from PyPI, or a local wheel via --wheel) and points ExecStart at it. --render-to PATH writes the unit/plist without touching systemctl/launchctl.
MVP limitation: install-with-start, start and update run synchronously.
Closes #11
Co-Authored-By: Claude Fable 5 noreply@anthropic.com
Claude-Session: https://claude.ai/code/session_01GWHBALtjHVd176YddWc9YP
Signed-off-by: Jose Salamanca jose.salamancacoy@gmail.com
- cli: Named environment profiles for install and config (
bd1131b)
Add reusable, named sets of .env values ("profiles") so operators can stamp consistent credentials and tuning across instances and servers.
- core/profiles.py: framework-free storage at config_root()/profiles/.env via EnvFile (chmod 0600), with list/load/save/delete, name validation and the IDENTITY_KEYS set a profile never carries (MAD_INSTANCE, MAD_HOST_PORT, PUID, PGID, MAD_DATA_PATH, MAD_VERSION). - commands/profiles.py:
mad profiles create|list|show|delete|apply. create seeds from an instance (identity keys excluded) and/or --set KEY=VALUE; show masks secret-looking values; apply overlays a profile onto an instance's .env with a restart hint. - install.py:mad install --profile NAMEfeeds the profile's values as the wizard defaults (precedence: explicit flag > profile > builtin default), merged into the single existing default layer.
Closes #10
Co-Authored-By: Claude Fable 5 noreply@anthropic.com
Claude-Session: https://claude.ai/code/session_01GWHBALtjHVd176YddWc9YP
Signed-off-by: Jose Salamanca jose.salamancacoy@gmail.com
Refactoring
- Extract framework-free use-case layer (
5589ab8)
Move the orchestration that lived in commands/*.py into a framework-free use-case layer (mad_cli.core.usecases): instances, lifecycle, configvals, keys, versions, adopt and install, plus a shared error vocabulary. The Typer commands become thin adapters over these use cases (parse/prompt/present only), so the CLI and the forthcoming HTTP API cannot drift.
Secret detection (is_secret_key) and a display_value helper move into core.keyspec so both surfaces share one masking rule; commands/common.py re-exports it. The status-line helpers now escape their message text so literal brackets (e.g. an [A-Z][A-Z0-9]* pattern) are printed verbatim instead of being swallowed as rich markup.
The existing command tests stay the behavioural safety net: only the mock targets moved to the new module locations; no assertion was weakened. CONTRACTS.md gains the core.usecases + keyspec + (upcoming) server sections.
Co-Authored-By: Claude Fable 5 noreply@anthropic.com
Claude-Session: https://claude.ai/code/session_01GWHBALtjHVd176YddWc9YP
Signed-off-by: Jose Salamanca jose.salamancacoy@gmail.com
Detailed Changes: v0.3.2...v0.4.0
v0.3.2
v0.3.2 (2026-07-04)
Chores
- ci: Add living-docs docs-validate and docs-sync caller workflows (
bc1bff6)
Thin callers for the shared reusable workflows in mad-core/.github, parameterized with service_slug: mad-cli. docs-validate gates each PR to main on a clean docs structure lint; docs-sync mirrors docs/ into mad-core/mad-docs under raw/mad-cli/ after merge to main.
Co-Authored-By: Claude Fable 5 noreply@anthropic.com
Claude-Session: https://claude.ai/code/session_01GWHBALtjHVd176YddWc9YP
Signed-off-by: Jose Salamanca jose.salamancacoy@gmail.com
Documentation
- Bootstrap and author living-docs tree for mad-cli (
5adb6b3)
Add docs/.docs-manifest.yaml (service mad-cli, area backend) and the full /docs tree it declares, adapted from the org backend contract to what mad-cli is — an operator CLI with no HTTP/SSE/MCP surface and no database:
- 01-overview: passport, context, scope, glossary - 02-architecture: overview, components + the deterministic source/test trees - 03-contracts: the CLI command surface (cli.md) and external dependencies (the generic api-reference/data-model/events entries do not apply) - 04-conventions: cli-design, layering, quality, testing-strategy - 05-operations: installation, configuration, ci-cd, release - 06-history: changelog mirrored from the semantic-release CHANGELOG.md
Content is authored faithfully from src/mad_cli, CLAUDE.md, CONTRACTS.md, README.md and the workflows. The source/test trees are generated by gen_docs; every entry's acknowledged_at_commit is stamped at the CI-callers commit so the structure lint is clean.
Co-Authored-By: Claude Fable 5 noreply@anthropic.com
Claude-Session: https://claude.ai/code/session_01GWHBALtjHVd176YddWc9YP
Signed-off-by: Jose Salamanca jose.salamancacoy@gmail.com
- Document persistent session logs and new install options (
16263ae)
Reflect the code changes in the living-docs tree and re-acknowledge every page the linter flagged at the code commit (4499532):
- 03-contracts/cli.md: new install options (--anthropic-api-key, repeatable --set-key, --retention-days, --mcp-allowed-hosts) and the refreshed summary. - 05-operations/configuration.md: MAD_SESSIONS_DIR, MAD_SESSIONS_RETENTION_DAYS, MAD_MCP_ALLOWED_HOSTS, MAD_SSE_HEARTBEAT_S, and the new install paths for the anthropic/deepseek/linear/opencode credentials. - 05-operations/installation.md: the sessions bind mount (survives rebuilds; existing instances re-run
mad install) and the extra install knobs. - 03-contracts/external-dependencies.md: the sessions mount + MAD_SESSIONS_DIR. - 06-history/changelog.md: mirror the v0.3.0 and v0.3.1 releases.
The remaining flagged pages (components, cli-design, layering, quality, testing-strategy, release) needed no content change and are re-acknowledged.
Co-Authored-By: Claude Fable 5 noreply@anthropic.com
Claude-Session: https://claude.ai/code/session_01GWHBALtjHVd176YddWc9YP
Signed-off-by: Jose Salamanca jose.salamancacoy@gmail.com
Features
- cli: Ask for retention, extra API keys and MCP hosts during install (
4499532)
mad install now collects several useful mad-edge settings up front so the operator no longer has to follow up with mad config set:
- --anthropic-api-key (secret, optional): alternative billing to the Claude OAuth token; prompted right after the Claude token. Writes ANTHROPIC_API_KEY. - --set-key ID=VALUE (repeatable): a builtin registry id (fanned out to its env vars) or a custom [A-Z][A-Z0-9_]* name written verbatim. claude-oauth is rejected — it has its own --claude-token. Interactive runs offer the same via a mini-loop after the main credentials. - --retention-days (>= 1, optional): writes MAD_SESSIONS_RETENTION_DAYS, else a commented reference (unset = keep forever). - --mcp-allowed-hosts (optional): writes MAD_MCP_ALLOWED_HOSTS, else a commented reference. MAD_SSE_HEARTBEAT_S is always left as a commented reference.
install also creates the per-instance sessions/ data dir and the summary panel now shows the sessions path, retention, MCP hosts and the extra keys (masked).
EnvFile grows an additive add_comment() helper so the generated .env can carry documented, inert reference lines (get()/keys() ignore them).
Co-Authored-By: Claude Fable 5 noreply@anthropic.com
Claude-Session: https://claude.ai/code/session_01GWHBALtjHVd176YddWc9YP
Signed-off-by: Jose Salamanca jose.salamancacoy@gmail.com
- templates: Persist session logs via a dedicated bind mount (
799e44e)
mad-edge writes its JSONL session logs (its source of truth) under MAD_SESSIONS_DIR (default ./sessions inside the container). Because that path was never bind-mounted, a mad update rebuild destroyed every session.
Add a dedicated <data_path>/<instance>/sessions:/sessions bind mount, pin MAD_SESSIONS_DIR to /sessions in the compose environment: block (forced container-side like MAD_WORKSPACE_DIR, so the mount and the variable can never diverge), and create/own /sessions in the Dockerfile's non-root user block alongside /workspaces.
Co-Authored-By: Claude Fable 5 noreply@anthropic.com
Claude-Session: https://claude.ai/code/session_01GWHBALtjHVd176YddWc9YP
Signed-off-by: Jose Salamanca jose.salamancacoy@gmail.com
Detailed Changes: v0.3.1...v0.3.2
v0.3.1
v0.3.1 (2026-07-04)
Bug Fixes
- templates: Tolerate host UID/GID collisions when creating the container user (
cc4a022)
groupadd -g "${PGID}" fails with exit 4 when the host GID already exists in the base image — e.g. macOS operators have gid 20, which is Debian's dialout group. Reuse an existing group and allow a duplicate UID (useradd -o) instead of failing the build.
Co-Authored-By: Claude Fable 5 noreply@anthropic.com
Claude-Session: https://claude.ai/code/session_01GWHBALtjHVd176YddWc9YP
Signed-off-by: Jose Salamanca jose.salamancacoy@gmail.com
Detailed Changes: v0.3.0...v0.3.1
v0.3.0
v0.3.0 (2026-07-04)
Features
- cli: Add versions, update and adopt; polish list inventory (
534ed68)
Add mad versions [INSTANCE] (pinned / installed / latest-on-PyPI plus an update-available column), mad update INSTANCE [--version X] (re-pins MAD_VERSION in the .env and rebuilds from scratch), and mad adopt (migrates the legacy single-instance layout into instances//). Refresh mad list to Name / Port / State / Health / Version with best-effort state and health parsed from docker compose ps.
Co-Authored-By: Claude Fable 5 noreply@anthropic.com
Claude-Session: https://claude.ai/code/session_01GWHBALtjHVd176YddWc9YP
Signed-off-by: Jose Salamanca jose.salamancacoy@gmail.com
Detailed Changes: v0.2.0...v0.3.0
v0.2.0
v0.2.0 (2026-07-04)
Features
- cli: Add
mad keysandmad configcommands (a525213)
Add two Typer sub-apps for v0.2 credential and configuration management, wired into the root app with minimal footprint.
mad keys set|list|remove manages credentials in an instance's .env: builtin keys from the keyspec registry fan a single value out to all their env vars (e.g. github -> GITHUB_TOKEN + GH_TOKEN), claude-oauth additionally materialises the container's .credentials.json (chmod 600) under <data_path>//claude, and arbitrary [A-Z][A-Z0-9_]* names are written verbatim. Values are always masked on display; removing claude-oauth leaves the on-disk credentials file in place and reports where it is.
mad config get|set|unset is the general-purpose .env editor: get masks secret-looking values unless --reveal, set validates MAD_HOST_PORT and MAD_AGENT_TIMEOUT_S and warns that compose-baked keys need a regenerate to take effect, and every mutation ends with a restart hint.
Co-Authored-By: Claude Fable 5 noreply@anthropic.com
Claude-Session: https://claude.ai/code/session_01GWHBALtjHVd176YddWc9YP
Signed-off-by: Jose Salamanca jose.salamancacoy@gmail.com
Testing
- Cover keys/config against the real core (
082ea6a)
Add a make_real_instance fixture that writes instances//.env under a scratch MAD_CLI_CONFIG_DIR so the command tests exercise the unmocked engine end to end. Cover the builtin fan-out, the claude-oauth credentials file (0600) and its retention on remove, custom vars, masking, validation, instance resolution and the empty-config hint.
Also make the --version test version-agnostic: it pinned "0.1.0" and broke when release automation bumped version to 0.1.1; it now asserts the output equals the package version.
Co-Authored-By: Claude Fable 5 noreply@anthropic.com
Claude-Session: https://claude.ai/code/session_01GWHBALtjHVd176YddWc9YP
Signed-off-by: Jose Salamanca jose.salamancacoy@gmail.com
Detailed Changes: v0.1.1...v0.2.0
v0.1.1
v0.1.1 (2026-07-04)
Chores
-
Add CI, release and TestPyPI preview workflows (
b33c306) -
ci.yml: ruff check + format, mypy, pytest on 3.11/3.12 with core coverage,
pip-audit and a build + twine check gate (no import-linter, no pre-commit) -
release.yml: python-semantic-release with PyPI trusted publishing, path-gated
to src/mad_cli/**, pyproject.toml, README.md and LICENSE -
testpypi-preview.yml: build/publish a .dev preview to TestPyPI per PR, gated by
the TESTPYPI_ENABLED variable; verify installs the wheel and imports mad_cli
Co-Authored-By: Claude Fable 5 noreply@anthropic.com
Claude-Session: https://claude.ai/code/session_01GWHBALtjHVd176YddWc9YP
Signed-off-by: Jose Salamanca jose.salamancacoy@gmail.com
- Install build inside the semantic-release container (
2920d47)
The python-semantic-release action runs build_command in its own container, where the runner's dev deps are not installed; plain python -m build fails with 'No module named build'. Mirror the mad-edge build_command, which installs build first.
Co-Authored-By: Claude Fable 5 noreply@anthropic.com
Claude-Session: https://claude.ai/code/session_01GWHBALtjHVd176YddWc9YP
Signed-off-by: Jose Salamanca jose.salamancacoy@gmail.com
Code Style
- Apply ruff format to command modules and tests (
4e9beba)
Co-Authored-By: Claude Fable 5 noreply@anthropic.com
Claude-Session: https://claude.ai/code/session_01GWHBALtjHVd176YddWc9YP
Signed-off-by: Jose Salamanca jose.salamancacoy@gmail.com
Documentation
- Add operator README and contributor CLAUDE.md (
4514866)
README: operator quickstart (pip install mad-cli -> mad install -> mad start), what mad-cli is, the v0.1-v0.3 command table, its relationship to mad-edge, the alpha 0.x status and PyPI + CI badges.
CLAUDE.md: project summary and hard rules (DCO sign-off, Conventional Commits with the public scope set {cli, config, templates, deps}, the core/commands layering rule, CONTRACTS.md as source of truth, Docker-free tests, mad-edge versioning policy).
Co-Authored-By: Claude Fable 5 noreply@anthropic.com
Claude-Session: https://claude.ai/code/session_01GWHBALtjHVd176YddWc9YP
Signed-off-by: Jose Salamanca jose.salamancacoy@gmail.com
Features
- cli: Guided install wizard (
cfdc18f)
Port mad/scripts/configure.sh to mad install with an English UI: Docker preflight (offer install on Linux, actionable errors elsewhere), a CLI flag for every parameter that skips its prompt, and idempotent reconfiguration that pre-fills defaults from an existing instance's .env. Assemble the EnvFile (same keys as the bash script), render the instance files and write the Claude credentials through mad_cli.core, then print a masked summary panel and — unless --no-start — build, start and wait for health. --yes runs non-interactively and exits cleanly, naming the missing flag, when a required value such as --github-token is absent.
Co-Authored-By: Claude Fable 5 noreply@anthropic.com
Claude-Session: https://claude.ai/code/session_01GWHBALtjHVd176YddWc9YP
Signed-off-by: Jose Salamanca jose.salamancacoy@gmail.com
- cli: Implement framework-free core engine (
282720c)
Replace the core contract stubs with working implementations, honouring CONTRACTS.md and keeping mad_cli.core free of typer/rich (mypy --strict):
- paths: MAD_CLI_CONFIG_DIR-aware config root + instance name validation - envfile: comment/order-preserving .env parser with byte-stable round-trip - instance: modern + legacy instance discovery and typed accessors - keyspec: builtin key registry (github fan-out) and safe masking - claude_creds: claudeAiOauth credentials file, chmod 0600 - pypi: latest_version via the PyPI JSON API (stdlib urllib, fail-soft) - docker_check: docker/daemon/compose-v2 detection + opt-in Linux install - compose: instance-scoped docker compose runner with health polling
Co-Authored-By: Claude Fable 5 noreply@anthropic.com
Claude-Session: https://claude.ai/code/session_01GWHBALtjHVd176YddWc9YP
Signed-off-by: Jose Salamanca jose.salamancacoy@gmail.com
- cli: Lifecycle/inventory commands, app wiring and tests (
812fcbe)
Add mad start|stop|restart|status|logs|shell [INSTANCE] with optional INSTANCE resolution (the sole instance via default_instance, else an actionable error that hints mad install or lists candidates), and the mad list / mad info NAME inventory commands (best-effort state, secrets masked). Wire the Typer app (eager --version callback, no_args_is_help) plus the mad console-script and python -m mad_cli entry points. Cover the whole surface with CliRunner tests that mock mad_cli.core throughout, so any un-mocked core call fails loudly.
Co-Authored-By: Claude Fable 5 noreply@anthropic.com
Claude-Session: https://claude.ai/code/session_01GWHBALtjHVd176YddWc9YP
Signed-off-by: Jose Salamanca jose.salamancacoy@gmail.com
- cli: Rich console and prompt helpers (
80eec20)
Add the mad-cli presentation layer: a shared rich Console with info/ok/warn/error/header status helpers and a run_step spinner, plus ask/confirm prompts that honour the non-interactive contract — return the default or raise PromptRequiredError when stdin is not a TTY, and re-prompt while a validator raises ValueError. Seed the commands package with its secret-key masking helper.
Co-Authored-By: Claude Fable 5 noreply@anthropic.com
Claude-Session: https://claude.ai/code/session_01GWHBALtjHVd176YddWc9YP
Signed-off-by: Jose Salamanca jose.salamancacoy@gmail.com
- templates: Render instance files from packaged templates (
940a204)
Add the string.Template package data (Dockerfile.tmpl, compose.yml.tmpl, entrypoint.sh.tmpl) and the core renderer that fills them. Faithfully ports the write_dockerfile/write_compose/write_entrypoint heredocs from the tested configure.sh (node:20-slim, gh CLI, claude-code + opencode, /opt/venv, non-root mad user, EXPOSE 8000, /openapi.json healthcheck, gh auth login entrypoint).
Deliberate change vs the bash: the pip package is parameterised via RenderContext.edge_package (default mad-edge), and the container start binary is resolved in Python (mad-edge, or mad for the mad-bros package) and passed as the ${edge_entrypoint} placeholder. Literal $ is escaped as $$ so string.Template substitutes only the intended placeholders.
Co-Authored-By: Claude Fable 5 noreply@anthropic.com
Claude-Session: https://claude.ai/code/session_01GWHBALtjHVd176YddWc9YP
Signed-off-by: Jose Salamanca jose.salamancacoy@gmail.com
Detailed Changes: v0.1.0...v0.1.1