Skip to content

feat(cli): Dynamic OpenAPI-driven CLI with interactive shell mode#117

Merged
thestumonkey merged 19 commits into
mainfrom
207f-ush-tool
Jan 20, 2026
Merged

feat(cli): Dynamic OpenAPI-driven CLI with interactive shell mode#117
thestumonkey merged 19 commits into
mainfrom
207f-ush-tool

Conversation

@thestumonkey

@thestumonkey thestumonkey commented Jan 20, 2026

Copy link
Copy Markdown
Member

Summary

Rewrites the ush CLI 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

  • Dynamic command discovery: CLI auto-discovers all API endpoints from openapi.json - no more manual command definitions
  • Interactive shell mode (./ush shell): REPL with nested tab completion, fish-style history suggestions, and command help
  • Resource-first completion: Type services <Tab> to see both commands AND available resources (mem0, chronicle, etc.)
  • Bidirectional command syntax: Both services start mem0 and services mem0 start work
  • Auto-detection of list endpoints: Automatically discovers resource list endpoints from OpenAPI spec for tab completion
  • Pretty formatted output: Rich tables for services list, providers list, etc.

New Files

  • ushadow/client/auth.py - Simple authenticated HTTP client wrapper
  • ushadow/client/__init__.py - Client module exports
  • scripts/generate_openapi_spec.py - Script to download OpenAPI spec from running backend
  • scripts/regenerate_client.sh - Simplified script to refresh openapi.json
  • openapi.json - Cached OpenAPI spec (source of truth for CLI commands)
  • docs/CLI-TOOL-GUIDE.md - Documentation for the new CLI

How It Works

  1. Commands auto-discover from OpenAPI spec - add a new endpoint, it appears in CLI automatically
  2. Resource completion auto-detects list endpoints (GET /api/{group}/) from the spec
  3. Name field guessing tries common patterns (service_name, name, id) with manual overrides for edge cases

Usage

# One-off commands
./ush services list
./ush services start mem0
./ush health

# Interactive shell with tab completion
./ush shell
ushadow> services <Tab>     # Shows commands AND resources
ushadow> services mem0 <Tab> # Shows available actions

Dependencies Added

  • prompt_toolkit>=3.0.48 - Interactive shell with completion
  • rich>=13.0.0 - Pretty terminal output

🤖 Generated with Claude Code

# 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 thestumonkey changed the title ush tool (vibe-kanban) ush tool Jan 20, 2026
@thestumonkey thestumonkey changed the title ush tool feat(cli): Dynamic OpenAPI-driven CLI with interactive shell mode Jan 20, 2026
@thestumonkey thestumonkey merged commit 958bb92 into main Jan 20, 2026
2 of 4 checks passed
@thestumonkey thestumonkey deleted the 207f-ush-tool branch January 20, 2026 15:24
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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant