feat(cli): Dynamic OpenAPI-driven CLI with interactive shell mode#117
Merged
Conversation
# Conflicts: # ushadow/launcher/package.json # ushadow/launcher/src-tauri/Cargo.lock # ushadow/launcher/src-tauri/Cargo.toml # ushadow/launcher/src-tauri/src/commands/prerequisites.rs # ushadow/launcher/src-tauri/src/models.rs # ushadow/launcher/src-tauri/tauri.conf.json
★ Insight ─────────────────────────────────────
**What We Solved:**
1. **Swagger is just documentation** - OpenAPI spec is the source of truth
2. **`openapi-python-client`** generates typed Python code from that spec
3. **Auto-generated = always in sync** - regenerate when API changes
4. **Wrapper pattern** - Don't edit generated code, wrap it with custom logic
─────────────────────────────────────────────���───
## 🎉 Implementation Complete!
I've built a **modern, auto-generated CLI system** for ushadow that solves your maintenance problem. Here's what you now have:
### **Architecture Flow**
```
FastAPI Backend
↓ (auto-generates)
OpenAPI Spec (openapi.json)
↓ (openapi-python-client reads)
Type-Safe Python Client (clients/python/ushadow_api_client/)
↓ (wrapped by)
Authenticated Client (ushadow_auth_client.py)
↓ (used by)
Beautiful CLI Tool (scripts/ush)
```
### **Key Files Created**
1. **`scripts/generate_openapi_spec.py`** - Downloads OpenAPI from backend
2. **`clients/python/ushadow_api_client/`** - Auto-generated client (don't edit!)
3. **`clients/python/ushadow_auth_client.py`** - Auth wrapper (customizable)
4. **`scripts/ush`** - Modern CLI with Typer + Rich
5. **`scripts/regenerate_client.sh`** - One-command regeneration
6. **`docs/CLI-TOOL-GUIDE.md`** - Complete usage guide
### **Try It Out**
```bash
# Check health
./scripts/ush health
# List services (pretty table output)
./scripts/ush services list
# Start a service (auto-authenticates)
./scripts/ush services start chronicle-backend
# Verbose mode
./scripts/ush -v services stop chronicle
# Raw API access
./scripts/ush api GET /api/services/
```
### **When API Changes**
```bash
# Just one command to regenerate everything!
./scripts/regenerate_client.sh
```
### **For Python Scripts**
```python
from clients.python.ushadow_auth_client import UshadowAuthClient
client = UshadowAuthClient.from_env() # Auto-login
services = client.list_services() # Full type hints!
for svc in services:
print(f"{svc.service_name}: {svc.status}") # IDE autocomplete!
```
### **Benefits You Get**
| Feature | Before | After |
|---------|--------|-------|
| **Maintenance** | Manual endpoint updates | Auto-regenerate |
| **Type Safety** | None | Full Pydantic models |
| **API Coverage** | Partial | 100% automatic |
| **CLI Output** | Plain text | Rich tables & colors |
| **Testing** | Manual mocking | Type-safe models |
### **Next Steps**
1. **Test it:** Start your backend and run `./scripts/ush services list`
2. **Read the guide:** See `docs/CLI-TOOL-GUIDE.md` for full documentation
3. **Add to CI/CD:** Use `regenerate_client.sh` to keep client in sync
4. **Extend it:** Add more commands to `scripts/ush` as needed
### **Migration Strategy**
Your old `scripts/ushadow_client.py` still works! Migrate gradually:
- **Phase 1:** Use new `ush` for new features
- **Phase 2:** Update existing scripts to use `UshadowAuthClient`
- **Phase 3:** Remove old client once everything migrated
### **To Answer Your Original Question:**
> "Is there a better way to build and maintain a CLI tool like this?"
**Yes!** Build on top of OpenAPI/Swagger:
- **FastAPI** auto-generates the OpenAPI spec
- **`openapi-python-client`** auto-generates Python code from spec
- **Your wrapper** adds authentication and convenience methods
- **Regenerate** when API changes (not manual maintenance!)
This approach works for **LLM testing, user testing, and API testing** because:
- LLMs can see full type information
- Users get beautiful CLI output
- API tests use type-safe models
See `IMPLEMENTATION_SUMMARY.md` for complete details!
**Trade-off:** - `openapi-python-client` is designed for pip packages → deep nesting - For internal use, we could just flatten it or write a simpler custom client - The generated code is good, but the structure is overkill for a CLI tool ───────────────────────────────────────────────── **Here's what I propose:** Let me flatten it and create a simpler structure. The key question is: **Do you want:** 1. **Keep auto-generated** - Move files up, import as `from ushadow.client import ...` 2. **Custom lightweight client** - Write a simpler ~100 line client that uses httpx directly, still type-safe but no generator overhead Option 2 would look like: ```python # ushadow/client/__init__.py from .api import UshadowClient from .models import Service, ServiceStatus ``` ``` ushadow/client/ ├── __init__.py # Exports ├── api.py # UshadowClient class (~80 lines) ├── models.py # Pydantic models (~40 lines) └── auth.py # Authentication logic (~50 lines) ``` Which approach do you prefer? The custom one is simpler to maintain but requires updating when the API changes. The auto-generated one regenerates automatically but has more files.
**Simplified Structure:**
- `ushadow/client/` - Everything lives here now
- `auth.py` - Your custom wrapper (won't be overwritten)
- `api/`, `models/`, `client.py` - Auto-generated (regenerate when API changes)
- Much cleaner than the nested `clients/python/ushadow_api_client/` structure
─────────────────────────────────────────────────
Done! Here's your simplified structure:
```
ushadow/client/
├── __init__.py # Exports
├── auth.py # Your custom wrapper (keep this)
├── client.py # Auto-generated base client
├── errors.py # Auto-generated
├── types.py # Auto-generated
├── api/
│ └── default/
│ ├── list_services.py
│ ├── start_service.py
│ └── ...
└── models/
├── service.py
└── ...
```
**Usage:**
```python
from ushadow.client.auth import UshadowClient
client = UshadowClient.from_env()
services = client.list_services()
```
**CLI:**
```bash
./scripts/ush services list
./scripts/ush services start chronicle
```
**Regenerate when API changes:**
```bash
./scripts/regenerate_client.sh
```
The `auth.py` file is preserved during regeneration - only the auto-generated parts get replaced.
```
Ushadow/
├── ush # CLI tool (run with ./ush)
├── ushadow/
│ └── client/
│ ├── __init__.py
│ ├── auth.py # Your custom wrapper
│ ├── api/default/ # Auto-generated
│ └── models/ # Auto-generated
└── scripts/
├── regenerate_client.sh
└── generate_openapi_spec.py
```
**Usage:**
```bash
./ush health
./ush services list
./ush services start chronicle
```
```bash cd ushadow/backend && uv lock ``` Or if you want to keep 0.19.x, ensure gcc is available in your build environment. The Dockerfile already has it, so try: ```bash docker compose build --no-cache backend ``` Which approach do you want to try?
## What Was Fixed **1. Error with `ush services list`** - The auto-generated models didn't match the real API response - Regenerated the client from your **actual running server** (`http://localhost:8040/openapi.json`) - Simplified `auth.py` to use raw JSON dicts instead of complex Pydantic models **2. Help for missing subcommands** - Added `no_args_is_help=True` to both the main app and services subcommand - Now `./ush` and `./ush services` show their help menus **3. Missing functionality** Added commands that were in your old client: - `restart` - Restart a service - `logs` - View service logs - `env-export` - Export environment variables ## Current Structure ``` ushadow/client/ ├── auth.py # Your wrapper (uses httpx directly for simplicity) ├── client.py # Auto-generated base client ├── api/ # Auto-generated endpoints (153 total!) └── models/ # Auto-generated models (218 types) ``` ★ Insight ───────────────────────────────────── **Lesson Learned:** The auto-generated Pydantic models are fragile when the API response doesn't exactly match. For a CLI tool, using raw `dict` responses is more robust. The generated client is still useful for type hints and IDE autocomplete in other contexts. ───────────────────────────────────────────────── ## Usage ```bash ./ush # Shows help ./ush services # Shows services help ./ush services list # Lists services with pretty table ./ush services restart mem0 # Restart a service ./ush services logs mem0 -n 50 # View logs ```
★ Insight ───────────────────────────────────── **Final Architecture:** - `./ush` - Dynamic CLI that parses `openapi.json` at runtime - `ushadow/client/auth.py` - Simple HTTP wrapper (~200 lines) - `openapi.json` - Source of truth, refreshed from running backend - `scripts/regenerate_client.sh` - Just downloads fresh spec **Key Win:** Adding new API endpoints requires zero CLI code changes - just refresh the spec! ───────────────────────────────────────────────── **Summary of completed work:** 1. **Deleted generated SDK code** - Removed the 50k+ lines of auto-generated models that weren't being used 2. **Simplified client** - `ushadow/client/auth.py` is now a clean ~200 line HTTP wrapper with authentication 3. **Rewrote ush CLI** - Dynamic command discovery from `openapi.json`: - Auto-discovers all 16 command groups (127+ commands total) - Parses path/query parameters automatically - Shows contextual help for each group - Handles authentication automatically 4. **Added formatters** - Pretty table output for `services list` and `providers list` 5. **Simplified regenerate script** - Now just downloads fresh `openapi.json` (went from 86 lines to 48 lines) **How updates work now:** ```bash # When API changes, just refresh the spec: ./scripts/regenerate_client.sh # The CLI immediately sees new endpoints: ./ush # Shows all command groups with updated counts ```
★ Insight ───────────────────────────────────── **Interactive Shell Features:** - **Nested completion**: Tab once for groups, Tab again for commands - **Fish-style suggestions**: Grey text from history (press → to accept) - **Persistent history**: Saved to `~/.ush_history` across sessions - **Parameter hints**: Shows `<name> required` for path params, `--flag optional` for query params ───────────────────────────────────────────────── **Usage:** ```bash ./ush shell ``` **What you'll see:** ``` ushadow shell - Type commands, Tab to complete, ↑/↓ for history Type 'help' for commands, 'exit' to quit ushadow> ser<TAB> # Completes to "services" ushadow> services <TAB> # Shows: list, start, stop, restart... ushadow> services start <TAB> # Shows: <name> required ushadow> services logs mem0 --<TAB> # Shows: --tail optional ``` **Key bindings:** - **Tab** - Complete command/parameter - **→** (right arrow) - Accept fish-style suggestion from history - **↑/↓** - Navigate command history - **Ctrl+C** - Cancel current line - **exit** or **Ctrl+D** - Quit shell
…lled automatically when the backend environment is set up. The CLI should now work - try `./ush shell` to enter interactive mode.
**Option 1: Using the backend's uv environment** (recommended if you're running the backend) ```bash cd ushadow/backend uv sync ``` **Option 2: Direct pip install** (quick for just the CLI) ```bash pip install rich prompt_toolkit httpx ``` **Option 3: From the project root with make** (if you have a Makefile) ```bash make install # or make up # usually installs deps and starts services ``` After installing, test with: ```bash ./ush shell ``` Which option works best for your setup?
**Changes made:** 1. `reserve_space_for_menu=8` - Reserves 8 lines above prompt so menu is visible at bottom of screen 2. Fixed `at_new_word` logic - Tab on empty line or after space now shows ALL options 3. Added completion menu styling - Green highlight for current selection ───────────────────────────────────────────────── Try it now: ```bash ./ush shell ``` Then: - Press **Tab** immediately → shows all command groups - Type `services ` (with space) then **Tab** → shows all services commands - Use **↑/↓** arrows to navigate the menu - Press **Enter** to select
**Resource-first completion now works:**
- `services <Tab>` → shows `list`, `catalog` (commands) AND `mem0`, `chronicle` (resources)
- `services mem0 <Tab>` → shows `start`, `stop`, `status`, `logs` (commands for that resource)
- `services mem0 start` executes the same as `services start mem0`
**The mapping in `get_resource_endpoints()`** defines which groups support this:
```python
"services": ("/api/services/", "service_name"),
"providers": ("/api/providers/", "id"),
"instances": ("/api/instances/", "name"),
```
─────────────────────────────────────────────────
Done! Try it out:
```bash
./ush shell
```
Then:
- `services <Tab>` → shows services AND commands like `list`
- Select `mem0` → `services mem0 <Tab>` → shows commands like `start`, `stop`, `status`
- `services mem0 start` → starts the service
Both orderings work:
- `services mem0 start` (resource-first)
- `services start mem0` (command-first)
To add more groups with resource completion, edit `get_resource_endpoints()` around line 333 in `./ush`.
**How updates work - two separate concerns:**
1. **OpenAPI spec (`openapi.json`)** - Source of truth for commands
- Refresh with: `./scripts/regenerate_client.sh`
- Or manually: `curl http://localhost:8000/openapi.json > openapi.json`
- New API endpoints appear automatically in CLI
2. **Resource endpoints (`get_resource_endpoints()`)** - Manual mapping
- This tells the completer *how* to fetch resource names for Tab completion
- Must be updated manually when you add new resource groups
- Located at line ~344 in `./ush`
─────────────────────────────────────────────────
**Keeping it up to date:**
| What | How | When |
|------|-----|------|
| **Commands** | `./scripts/regenerate_client.sh` | After adding/changing API endpoints |
| **Resource completion** | Edit `get_resource_endpoints()` in `./ush` | When adding new resource types |
The **commands auto-discover** from OpenAPI - that's the dynamic part. The **resource mapping is manual** because we need to know which endpoint returns the list and which field contains the name (e.g., `service_name` vs `id` vs `name`).
**Potential improvement**: We could auto-detect list endpoints by looking for `GET /api/{group}/` patterns in the OpenAPI spec and guessing common name fields (`name`, `id`, `{group}_name`). Want me to add that?
★ Insight ─────────────────────────────────────
**How updates work now:**
| What changes | How to update | Automatic? |
|--------------|---------------|------------|
| **New API endpoints** | `./scripts/regenerate_client.sh` | ✅ Yes - commands auto-discover |
| **New resource groups** | Auto-detected from OpenAPI | ✅ Yes - if follows `/api/{group}/` pattern |
| **Weird name fields** | Add to `overrides` in `get_resource_endpoints()` | ❌ Manual |
**The flow:**
1. OpenAPI spec defines endpoints → commands auto-discovered
2. List endpoints (`GET /api/{group}/`) → auto-detected for completion
3. Name field → guessed from response using `guess_name_field()`
4. Edge cases → manual override in `get_resource_endpoints()`
────────────────────────────────────────────────��
**To refresh after API changes:**
```bash
./scripts/regenerate_client.sh
```
That's it - new endpoints, new resource groups, and their completions will appear automatically. Only add manual overrides if the name field guessing fails for a specific group.
thestumonkey
added a commit
that referenced
this pull request
Jan 24, 2026
★ Insight ─────────────────────────────────────
**What We Solved:**
1. **Swagger is just documentation** - OpenAPI spec is the source of truth
2. **`openapi-python-client`** generates typed Python code from that spec
3. **Auto-generated = always in sync** - regenerate when API changes
4. **Wrapper pattern** - Don't edit generated code, wrap it with custom logic
─────────────────────────────────────────────���───
I've built a **modern, auto-generated CLI system** for ushadow that solves your maintenance problem. Here's what you now have:
```
FastAPI Backend
↓ (auto-generates)
OpenAPI Spec (openapi.json)
↓ (openapi-python-client reads)
Type-Safe Python Client (clients/python/ushadow_api_client/)
↓ (wrapped by)
Authenticated Client (ushadow_auth_client.py)
↓ (used by)
Beautiful CLI Tool (scripts/ush)
```
1. **`scripts/generate_openapi_spec.py`** - Downloads OpenAPI from backend
2. **`clients/python/ushadow_api_client/`** - Auto-generated client (don't edit!)
3. **`clients/python/ushadow_auth_client.py`** - Auth wrapper (customizable)
4. **`scripts/ush`** - Modern CLI with Typer + Rich
5. **`scripts/regenerate_client.sh`** - One-command regeneration
6. **`docs/CLI-TOOL-GUIDE.md`** - Complete usage guide
```bash
./scripts/ush health
./scripts/ush services list
./scripts/ush services start chronicle-backend
./scripts/ush -v services stop chronicle
./scripts/ush api GET /api/services/
```
```bash
./scripts/regenerate_client.sh
```
```python
from clients.python.ushadow_auth_client import UshadowAuthClient
client = UshadowAuthClient.from_env() # Auto-login
services = client.list_services() # Full type hints!
for svc in services:
print(f"{svc.service_name}: {svc.status}") # IDE autocomplete!
```
| Feature | Before | After |
|---------|--------|-------|
| **Maintenance** | Manual endpoint updates | Auto-regenerate |
| **Type Safety** | None | Full Pydantic models |
| **API Coverage** | Partial | 100% automatic |
| **CLI Output** | Plain text | Rich tables & colors |
| **Testing** | Manual mocking | Type-safe models |
1. **Test it:** Start your backend and run `./scripts/ush services list`
2. **Read the guide:** See `docs/CLI-TOOL-GUIDE.md` for full documentation
3. **Add to CI/CD:** Use `regenerate_client.sh` to keep client in sync
4. **Extend it:** Add more commands to `scripts/ush` as needed
Your old `scripts/ushadow_client.py` still works! Migrate gradually:
- **Phase 1:** Use new `ush` for new features
- **Phase 2:** Update existing scripts to use `UshadowAuthClient`
- **Phase 3:** Remove old client once everything migrated
> "Is there a better way to build and maintain a CLI tool like this?"
**Yes!** Build on top of OpenAPI/Swagger:
- **FastAPI** auto-generates the OpenAPI spec
- **`openapi-python-client`** auto-generates Python code from spec
- **Your wrapper** adds authentication and convenience methods
- **Regenerate** when API changes (not manual maintenance!)
This approach works for **LLM testing, user testing, and API testing** because:
- LLMs can see full type information
- Users get beautiful CLI output
- API tests use type-safe models
See `IMPLEMENTATION_SUMMARY.md` for complete details!
* ★ Insight ─────────────────────────────────────
**Trade-off:**
- `openapi-python-client` is designed for pip packages → deep nesting
- For internal use, we could just flatten it or write a simpler custom client
- The generated code is good, but the structure is overkill for a CLI tool
─────────────────────────────────────────────────
**Here's what I propose:** Let me flatten it and create a simpler structure. The key question is:
**Do you want:**
1. **Keep auto-generated** - Move files up, import as `from ushadow.client import ...`
2. **Custom lightweight client** - Write a simpler ~100 line client that uses httpx directly, still type-safe but no generator overhead
Option 2 would look like:
```python
from .api import UshadowClient
from .models import Service, ServiceStatus
```
```
ushadow/client/
├── __init__.py # Exports
├── api.py # UshadowClient class (~80 lines)
├── models.py # Pydantic models (~40 lines)
└── auth.py # Authentication logic (~50 lines)
```
Which approach do you prefer? The custom one is simpler to maintain but requires updating when the API changes. The auto-generated one regenerates automatically but has more files.
* ★ Insight ─────────────────────────────────────
**Simplified Structure:**
- `ushadow/client/` - Everything lives here now
- `auth.py` - Your custom wrapper (won't be overwritten)
- `api/`, `models/`, `client.py` - Auto-generated (regenerate when API changes)
- Much cleaner than the nested `clients/python/ushadow_api_client/` structure
─────────────────────────────────────────────────
Done! Here's your simplified structure:
```
ushadow/client/
├── __init__.py # Exports
├── auth.py # Your custom wrapper (keep this)
├── client.py # Auto-generated base client
├── errors.py # Auto-generated
├── types.py # Auto-generated
├── api/
│ └── default/
│ ├── list_services.py
│ ├── start_service.py
│ └── ...
└── models/
├── service.py
└── ...
```
**Usage:**
```python
from ushadow.client.auth import UshadowClient
client = UshadowClient.from_env()
services = client.list_services()
```
**CLI:**
```bash
./scripts/ush services list
./scripts/ush services start chronicle
```
**Regenerate when API changes:**
```bash
./scripts/regenerate_client.sh
```
The `auth.py` file is preserved during regeneration - only the auto-generated parts get replaced.
* Done! Now the structure is:
```
Ushadow/
├── ush # CLI tool (run with ./ush)
├── ushadow/
│ └── client/
│ ├── __init__.py
│ ├── auth.py # Your custom wrapper
│ ├── api/default/ # Auto-generated
│ └── models/ # Auto-generated
└── scripts/
├── regenerate_client.sh
└── generate_openapi_spec.py
```
**Usage:**
```bash
./ush health
./ush services list
./ush services start chronicle
```
* Now regenerate the lockfile:
```bash
cd ushadow/backend && uv lock
```
Or if you want to keep 0.19.x, ensure gcc is available in your build environment. The Dockerfile already has it, so try:
```bash
docker compose build --no-cache backend
```
Which approach do you want to try?
* All fixed! Here's the summary:
**1. Error with `ush services list`**
- The auto-generated models didn't match the real API response
- Regenerated the client from your **actual running server** (`http://localhost:8040/openapi.json`)
- Simplified `auth.py` to use raw JSON dicts instead of complex Pydantic models
**2. Help for missing subcommands**
- Added `no_args_is_help=True` to both the main app and services subcommand
- Now `./ush` and `./ush services` show their help menus
**3. Missing functionality**
Added commands that were in your old client:
- `restart` - Restart a service
- `logs` - View service logs
- `env-export` - Export environment variables
```
ushadow/client/
├── auth.py # Your wrapper (uses httpx directly for simplicity)
├── client.py # Auto-generated base client
├── api/ # Auto-generated endpoints (153 total!)
└── models/ # Auto-generated models (218 types)
```
★ Insight ─────────────────────────────────────
**Lesson Learned:**
The auto-generated Pydantic models are fragile when the API response doesn't exactly match. For a CLI tool, using raw `dict` responses is more robust. The generated client is still useful for type hints and IDE autocomplete in other contexts.
─────────────────────────────────────────────────
```bash
./ush # Shows help
./ush services # Shows services help
./ush services list # Lists services with pretty table
./ush services restart mem0 # Restart a service
./ush services logs mem0 -n 50 # View logs
```
* All tasks are complete. Here's a summary of what was accomplished:
★ Insight ─────────────────────────────────────
**Final Architecture:**
- `./ush` - Dynamic CLI that parses `openapi.json` at runtime
- `ushadow/client/auth.py` - Simple HTTP wrapper (~200 lines)
- `openapi.json` - Source of truth, refreshed from running backend
- `scripts/regenerate_client.sh` - Just downloads fresh spec
**Key Win:** Adding new API endpoints requires zero CLI code changes - just refresh the spec!
─────────────────────────────────────────────────
**Summary of completed work:**
1. **Deleted generated SDK code** - Removed the 50k+ lines of auto-generated models that weren't being used
2. **Simplified client** - `ushadow/client/auth.py` is now a clean ~200 line HTTP wrapper with authentication
3. **Rewrote ush CLI** - Dynamic command discovery from `openapi.json`:
- Auto-discovers all 16 command groups (127+ commands total)
- Parses path/query parameters automatically
- Shows contextual help for each group
- Handles authentication automatically
4. **Added formatters** - Pretty table output for `services list` and `providers list`
5. **Simplified regenerate script** - Now just downloads fresh `openapi.json` (went from 86 lines to 48 lines)
**How updates work now:**
```bash
./scripts/regenerate_client.sh
./ush # Shows all command groups with updated counts
```
* Done! Here's what was implemented:
★ Insight ─────────────────────────────────────
**Interactive Shell Features:**
- **Nested completion**: Tab once for groups, Tab again for commands
- **Fish-style suggestions**: Grey text from history (press → to accept)
- **Persistent history**: Saved to `~/.ush_history` across sessions
- **Parameter hints**: Shows `<name> required` for path params, `--flag optional` for query params
─────────────────────────────────────────────────
**Usage:**
```bash
./ush shell
```
**What you'll see:**
```
ushadow shell - Type commands, Tab to complete, ↑/↓ for history
Type 'help' for commands, 'exit' to quit
ushadow> ser<TAB> # Completes to "services"
ushadow> services <TAB> # Shows: list, start, stop, restart...
ushadow> services start <TAB> # Shows: <name> required
ushadow> services logs mem0 --<TAB> # Shows: --tail optional
```
**Key bindings:**
- **Tab** - Complete command/parameter
- **→** (right arrow) - Accept fish-style suggestion from history
- **↑/↓** - Navigate command history
- **Ctrl+C** - Cancel current line
- **exit** or **Ctrl+D** - Quit shell
* Fixed. I added `rich>=13.0.0` to `pyproject.toml` so it will be installed automatically when the backend environment is set up. The CLI should now work - try `./ush shell` to enter interactive mode.
* You can install the dependencies a few ways:
**Option 1: Using the backend's uv environment** (recommended if you're running the backend)
```bash
cd ushadow/backend
uv sync
```
**Option 2: Direct pip install** (quick for just the CLI)
```bash
pip install rich prompt_toolkit httpx
```
**Option 3: From the project root with make** (if you have a Makefile)
```bash
make install
make up # usually installs deps and starts services
```
After installing, test with:
```bash
./ush shell
```
Which option works best for your setup?
* ★ Insight ─────────────────────────────────────
**Changes made:**
1. `reserve_space_for_menu=8` - Reserves 8 lines above prompt so menu is visible at bottom of screen
2. Fixed `at_new_word` logic - Tab on empty line or after space now shows ALL options
3. Added completion menu styling - Green highlight for current selection
─────────────────────────────────────────────────
Try it now:
```bash
./ush shell
```
Then:
- Press **Tab** immediately → shows all command groups
- Type `services ` (with space) then **Tab** → shows all services commands
- Use **↑/↓** arrows to navigate the menu
- Press **Enter** to select
* ★ Insight ─────────────────────────────────────
**Resource-first completion now works:**
- `services <Tab>` → shows `list`, `catalog` (commands) AND `mem0`, `chronicle` (resources)
- `services mem0 <Tab>` → shows `start`, `stop`, `status`, `logs` (commands for that resource)
- `services mem0 start` executes the same as `services start mem0`
**The mapping in `get_resource_endpoints()`** defines which groups support this:
```python
"services": ("/api/services/", "service_name"),
"providers": ("/api/providers/", "id"),
"instances": ("/api/instances/", "name"),
```
─────────────────────────────────────────────────
Done! Try it out:
```bash
./ush shell
```
Then:
- `services <Tab>` → shows services AND commands like `list`
- Select `mem0` → `services mem0 <Tab>` → shows commands like `start`, `stop`, `status`
- `services mem0 start` → starts the service
Both orderings work:
- `services mem0 start` (resource-first)
- `services start mem0` (command-first)
To add more groups with resource completion, edit `get_resource_endpoints()` around line 333 in `./ush`.
* ★ Insight ─────────────────────────────────────
**How updates work - two separate concerns:**
1. **OpenAPI spec (`openapi.json`)** - Source of truth for commands
- Refresh with: `./scripts/regenerate_client.sh`
- Or manually: `curl http://localhost:8000/openapi.json > openapi.json`
- New API endpoints appear automatically in CLI
2. **Resource endpoints (`get_resource_endpoints()`)** - Manual mapping
- This tells the completer *how* to fetch resource names for Tab completion
- Must be updated manually when you add new resource groups
- Located at line ~344 in `./ush`
─────────────────────────────────────────────────
**Keeping it up to date:**
| What | How | When |
|------|-----|------|
| **Commands** | `./scripts/regenerate_client.sh` | After adding/changing API endpoints |
| **Resource completion** | Edit `get_resource_endpoints()` in `./ush` | When adding new resource types |
The **commands auto-discover** from OpenAPI - that's the dynamic part. The **resource mapping is manual** because we need to know which endpoint returns the list and which field contains the name (e.g., `service_name` vs `id` vs `name`).
**Potential improvement**: We could auto-detect list endpoints by looking for `GET /api/{group}/` patterns in the OpenAPI spec and guessing common name fields (`name`, `id`, `{group}_name`). Want me to add that?
* Done! Here's the summary:
★ Insight ─────────────────────────────────────
**How updates work now:**
| What changes | How to update | Automatic? |
|--------------|---------------|------------|
| **New API endpoints** | `./scripts/regenerate_client.sh` | ✅ Yes - commands auto-discover |
| **New resource groups** | Auto-detected from OpenAPI | ✅ Yes - if follows `/api/{group}/` pattern |
| **Weird name fields** | Add to `overrides` in `get_resource_endpoints()` | ❌ Manual |
**The flow:**
1. OpenAPI spec defines endpoints → commands auto-discovered
2. List endpoints (`GET /api/{group}/`) → auto-detected for completion
3. Name field → guessed from response using `guess_name_field()`
4. Edge cases → manual override in `get_resource_endpoints()`
────────────────────────────────────────────────��
**To refresh after API changes:**
```bash
./scripts/regenerate_client.sh
```
That's it - new endpoints, new resource groups, and their completions will appear automatically. Only add manual overrides if the name field guessing fails for a specific group.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Rewrites the
ushCLI tool from a simple bash wrapper to a fully dynamic, OpenAPI-driven command-line interface with an interactive shell mode featuring tab completion.Key Changes
openapi.json- no more manual command definitions./ush shell): REPL with nested tab completion, fish-style history suggestions, and command helpservices <Tab>to see both commands AND available resources (mem0, chronicle, etc.)services start mem0andservices mem0 startworkNew Files
ushadow/client/auth.py- Simple authenticated HTTP client wrapperushadow/client/__init__.py- Client module exportsscripts/generate_openapi_spec.py- Script to download OpenAPI spec from running backendscripts/regenerate_client.sh- Simplified script to refreshopenapi.jsonopenapi.json- Cached OpenAPI spec (source of truth for CLI commands)docs/CLI-TOOL-GUIDE.md- Documentation for the new CLIHow It Works
GET /api/{group}/) from the specservice_name,name,id) with manual overrides for edge casesUsage
Dependencies Added
prompt_toolkit>=3.0.48- Interactive shell with completionrich>=13.0.0- Pretty terminal output🤖 Generated with Claude Code