ferro-cli 0.2.20

CLI for scaffolding Ferro web applications
Documentation
---
name: ferro:tinker
description: Interactive database REPL
allowed-tools:
  - Bash
  - Read
---

<objective>
Start an interactive REPL for exploring the database and testing queries.

Uses ferro-mcp `tinker` tool for execution, with fallback to direct database access.
</objective>

<arguments>
Optional:
- `[query]` - Execute single query and exit
- `--sql` - Raw SQL mode (default is model-based)

Examples:
- `/ferro:tinker` - Start interactive session
- `/ferro:tinker "User::all()"` - Execute single query
- `/ferro:tinker --sql "SELECT * FROM users"` - Raw SQL
</arguments>

<process>

<step name="check_connection">

Verify database connection:

```bash
# Check if DATABASE_URL is set
if grep -q "DATABASE_URL" .env 2>/dev/null; then
    echo "Database configured"
else
    echo "ERROR: DATABASE_URL not configured in .env"
    exit 1
fi
```

</step>

<step name="single_query">

**If query argument provided:**

Execute the query using ferro-mcp `tinker` tool and display results:

```
> User::all()

[
  { "id": 1, "email": "admin@example.com", "name": "Admin" },
  { "id": 2, "email": "user@example.com", "name": "User" }
]

2 rows returned
```

</step>

<step name="interactive_mode">

**Start interactive session:**

```
🔧 Ferro Tinker - Interactive REPL

Database: sqlite:database.sqlite
Models: User, Post, Comment

Type expressions to evaluate. Use .help for commands.

ferro> _
```

**Commands:**
- `.help` - Show help
- `.models` - List available models
- `.schema [table]` - Show table schema
- `.sql` - Switch to raw SQL mode
- `.exit` - Exit tinker

**Example session:**

```
ferro> User::count()
=> 5

ferro> User::find(1)
=> Some(User { id: 1, email: "admin@example.com", name: "Admin", ... })

ferro> User::query().filter(Column::Email.contains("@example")).all()
=> [
     User { id: 1, email: "admin@example.com", ... },
     User { id: 2, email: "user@example.com", ... }
   ]

ferro> Post::query().with(Post::user()).first()
=> Some(Post { id: 1, title: "Hello", user: User { id: 1, name: "Admin" } })

ferro> .sql
Switched to SQL mode

sql> SELECT COUNT(*) FROM users
=> 5

sql> SELECT u.name, COUNT(p.id) as post_count
     FROM users u
     LEFT JOIN posts p ON p.user_id = u.id
     GROUP BY u.id
=> [
     { "name": "Admin", "post_count": 10 },
     { "name": "User", "post_count": 3 }
   ]

sql> .exit
Goodbye!
```

</step>

</process>

<available_operations>

## Model Queries
```
Model::all()                    # Get all records
Model::find(id)                 # Find by ID
Model::first()                  # Get first record
Model::count()                  # Count records
Model::query().filter(...).all() # Filtered query
```

## Mutations
```
Model::create({ ... })          # Create record
record.update({ ... })          # Update record
record.delete()                 # Delete record
```

## Relations
```
Model::query().with(Model::relation()).all()
record.relation().await
```

## Raw SQL (in .sql mode)
```
SELECT * FROM table
INSERT INTO table (col) VALUES (val)
UPDATE table SET col = val WHERE id = 1
DELETE FROM table WHERE id = 1
```

</available_operations>