* feat(cli): agnes marketplace search/detail/add/remove + retire stale subcommands
Unified CLI surface for the v28+ marketplace: search across Curated and
Flea Market (RBAC-filtered server-side), drill into a single item's
detail, add/remove from your stack. Replaces opt-out era commands that
no longer reflect how users compose their stack.
CLI changes:
- Added: agnes marketplace {search,detail,add,remove}
- Removed: agnes my-stack toggle (opt-out semantics, curated-only)
- Removed: agnes store {list,show,install,uninstall} (consumer-side ops
moved under marketplace; store now covers only creator-side upload,
update, delete, mine)
ID format unifies curated and flea: marketplace_id/plugin_name (slash)
routes to /api/marketplace/curated/..., bare UUID routes to
/api/store/entities/... (flea bundles skills/agents into a synthetic
plugin server-side, so the analyst sees a single add/remove surface).
Templates:
- claude_md_template.txt: rewritten marketplace section as operational
guidance for Claude Code (discovery, stack management, behaviour
notes). Dropped the static {% if marketplaces %} listing — the CLI is
the source of truth for what's in the stack at any moment, so a
snapshot rendered at init time would lie the moment the user runs
agnes marketplace add/remove. Same discipline already applied to
tables and metrics.
- agnes_workspace_template.txt: cheat sheet adds 5 marketplace
one-liners; keeps the file's reference-doc tone (the original
commit's intent: 'what is this thing, how does it work, how do I
uninstall it').
Docs: HOWTO/05-customizing-skills.md rewritten around the new CLI flow;
the opt-out section is replaced by 'Removing items from your stack'.
Tests: new test_cli_marketplace.py covers all four subcommands incl.
RBAC/409 paths (system plugin guard, not-approved flea entity);
test_cli_store.py trimmed to the retained creator-side commands.
* release: 0.54.1 — agnes marketplace CLI redesign + retire stale subcommands
Last commit on the PR per CLAUDE.md hard rule. Patch bump (0.54.0 →
0.54.1) bundling the BREAKING removals of `agnes my-stack toggle` and
`agnes store {list,show,install,uninstall}` plus the new unified
`agnes marketplace {search,detail,add,remove}` surface.
No DB migration; no operator-facing config change. Operators on
floating tags (`:stable`) auto-upgrade transparently. Analyst CLI
upgrade prompt fires on next `agnes pull`; users invoking the
retired commands get "No such command" with the new `agnes
marketplace` substitution called out in the BREAKING bullets.
---------
Co-authored-by: Minas Arustamyan <arustamyan.minas@gmail.com>
Co-authored-by: ZdenekSrotyr <zdenek.srotyr@keboola.com>
48 lines
1.5 KiB
Python
48 lines
1.5 KiB
Python
"""`agnes my-stack show` — read-only view of the user's current marketplace stack.
|
|
|
|
Reads ``GET /api/my-stack``. To add or remove items use
|
|
``agnes marketplace add/remove``.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
from typing import Optional
|
|
|
|
import typer
|
|
|
|
from cli.v2_client import V2ClientError, api_get_json
|
|
|
|
my_stack_app = typer.Typer(help="Show your current marketplace stack (use 'agnes marketplace' to add/remove)")
|
|
|
|
|
|
@my_stack_app.command("show")
|
|
def show_stack(
|
|
json_out: bool = typer.Option(False, "--json"),
|
|
):
|
|
"""Show curated plugins available to subscribe to and your Flea Market installs."""
|
|
try:
|
|
body = api_get_json("/api/my-stack")
|
|
except V2ClientError as e:
|
|
typer.echo(str(e), err=True)
|
|
raise typer.Exit(1)
|
|
if json_out:
|
|
typer.echo(json.dumps(body, indent=2))
|
|
return
|
|
curated = body.get("curated", [])
|
|
store = body.get("store", [])
|
|
typer.echo(f"Curated plugins: {len(curated)}")
|
|
for p in curated:
|
|
flag = "✓" if p["enabled"] else "✗"
|
|
typer.echo(
|
|
f" [{flag}] {p['marketplace_id']}/{p['plugin_name']:24s} "
|
|
f"manifest={p['manifest_name']} v{p.get('version') or '?'}"
|
|
)
|
|
typer.echo(f"\nFrom Flea Market: {len(store)}")
|
|
for it in store:
|
|
typer.echo(
|
|
f" [{it['type']:6s}] {it['name']:24s} by {it['owner_username']:20s} "
|
|
f"invocation={it['invocation_name']} id={it['entity_id']}"
|
|
)
|
|
|
|
|