# The old way:
-weight: 500;">docker ps
-weight: 500;">docker logs my-app -n 50
-weight: 500;">docker stats
-weight: 500;">docker inspect some_container
# ... back and forth, breaking your flow
# The old way:
-weight: 500;">docker ps
-weight: 500;">docker logs my-app -n 50
-weight: 500;">docker stats
-weight: 500;">docker inspect some_container
# ... back and forth, breaking your flow
# The old way:
-weight: 500;">docker ps
-weight: 500;">docker logs my-app -n 50
-weight: 500;">docker stats
-weight: 500;">docker inspect some_container
# ... back and forth, breaking your flow
🐳 Docker v24.0.7 | ▶ 3 running | ● 8 total
🐳 Docker v24.0.7 | ▶ 3 running | ● 8 total
🐳 Docker v24.0.7 | ▶ 3 running | ● 8 total
const dangerousPatterns = [ /(?:-weight: 500;">docker|podman|nerdctl)\s+(?:rm|container\s+rm)\s+-f/i, /(?:-weight: 500;">docker|podman|nerdctl)\s+system\s+prune\s+-a/i, /(?:-weight: 500;">docker|podman|nerdctl)\s+-weight: 500;">stop\s+\$\(-weight: 500;">docker\s+ps\s+-aq\)/i, // ...
];
const dangerousPatterns = [ /(?:-weight: 500;">docker|podman|nerdctl)\s+(?:rm|container\s+rm)\s+-f/i, /(?:-weight: 500;">docker|podman|nerdctl)\s+system\s+prune\s+-a/i, /(?:-weight: 500;">docker|podman|nerdctl)\s+-weight: 500;">stop\s+\$\(-weight: 500;">docker\s+ps\s+-aq\)/i, // ...
];
const dangerousPatterns = [ /(?:-weight: 500;">docker|podman|nerdctl)\s+(?:rm|container\s+rm)\s+-f/i, /(?:-weight: 500;">docker|podman|nerdctl)\s+system\s+prune\s+-a/i, /(?:-weight: 500;">docker|podman|nerdctl)\s+-weight: 500;">stop\s+\$\(-weight: 500;">docker\s+ps\s+-aq\)/i, // ...
];
container-dashboard/
├── index.ts # Entry point, permission gates, lifecycle hooks
├── runtime.ts # Runtime detection (-weight: 500;">docker → podman → nerdctl), CLI abstraction
├── commands.ts # /-weight: 500;">docker:* slash commands with formatted output
├── tools.ts # 13 LLM tools registered via TypeBox schemas
└── widget.ts # Live TUI sidebar widget
container-dashboard/
├── index.ts # Entry point, permission gates, lifecycle hooks
├── runtime.ts # Runtime detection (-weight: 500;">docker → podman → nerdctl), CLI abstraction
├── commands.ts # /-weight: 500;">docker:* slash commands with formatted output
├── tools.ts # 13 LLM tools registered via TypeBox schemas
└── widget.ts # Live TUI sidebar widget
container-dashboard/
├── index.ts # Entry point, permission gates, lifecycle hooks
├── runtime.ts # Runtime detection (-weight: 500;">docker → podman → nerdctl), CLI abstraction
├── commands.ts # /-weight: 500;">docker:* slash commands with formatted output
├── tools.ts # 13 LLM tools registered via TypeBox schemas
└── widget.ts # Live TUI sidebar widget
const RUNTIMES = ["-weight: 500;">docker", "podman", "nerdctl"] as const; export async function detectRuntime(pi: ExtensionAPI): Promise<RuntimeState> { for (const runtime of RUNTIMES) { try { const result = await pi.exec(runtime, ["--version"], { timeout: 3000 }); if (result.code === 0 && result.stdout) { return { runtime, version: result.stdout.trim(), available: true }; } } catch { continue; } } return { runtime: null, version: "", available: false };
}
const RUNTIMES = ["-weight: 500;">docker", "podman", "nerdctl"] as const; export async function detectRuntime(pi: ExtensionAPI): Promise<RuntimeState> { for (const runtime of RUNTIMES) { try { const result = await pi.exec(runtime, ["--version"], { timeout: 3000 }); if (result.code === 0 && result.stdout) { return { runtime, version: result.stdout.trim(), available: true }; } } catch { continue; } } return { runtime: null, version: "", available: false };
}
const RUNTIMES = ["-weight: 500;">docker", "podman", "nerdctl"] as const; export async function detectRuntime(pi: ExtensionAPI): Promise<RuntimeState> { for (const runtime of RUNTIMES) { try { const result = await pi.exec(runtime, ["--version"], { timeout: 3000 }); if (result.code === 0 && result.stdout) { return { runtime, version: result.stdout.trim(), available: true }; } } catch { continue; } } return { runtime: null, version: "", available: false };
}
Containers CONTAINER ID NAME IMAGE STATUS PORTS
a1b2c3d4e5f6 my-postgres postgres:16 ▶ running 5432→5432
b2c3d4e5f6a7 redis-cache redis:7-alpine ▶ running 6379→6379
c3d4e5f6a7b8 old-test-container node:18 ● exited —
Containers CONTAINER ID NAME IMAGE STATUS PORTS
a1b2c3d4e5f6 my-postgres postgres:16 ▶ running 5432→5432
b2c3d4e5f6a7 redis-cache redis:7-alpine ▶ running 6379→6379
c3d4e5f6a7b8 old-test-container node:18 ● exited —
Containers CONTAINER ID NAME IMAGE STATUS PORTS
a1b2c3d4e5f6 my-postgres postgres:16 ▶ running 5432→5432
b2c3d4e5f6a7 redis-cache redis:7-alpine ▶ running 6379→6379
c3d4e5f6a7b8 old-test-container node:18 ● exited —
# From -weight: 500;">npm (recommended)
pi -weight: 500;">install -weight: 500;">npm:container-dashboard # Or from GitHub
pi -weight: 500;">install -weight: 500;">git:github.com/k1lgor/pi-container-dashboard # Or load locally
pi -e ./path/to/index.ts
# From -weight: 500;">npm (recommended)
pi -weight: 500;">install -weight: 500;">npm:container-dashboard # Or from GitHub
pi -weight: 500;">install -weight: 500;">git:github.com/k1lgor/pi-container-dashboard # Or load locally
pi -e ./path/to/index.ts
# From -weight: 500;">npm (recommended)
pi -weight: 500;">install -weight: 500;">npm:container-dashboard # Or from GitHub
pi -weight: 500;">install -weight: 500;">git:github.com/k1lgor/pi-container-dashboard # Or load locally
pi -e ./path/to/index.ts
You: "What containers are running?" 🤖 AI: *calls container_ps*
▶ my-postgres (running)
▶ redis-cache (running)
▶ api-gateway (running) You: "Check the api-gateway logs, something's wrong" 🤖 AI: *calls container_logs("api-gateway", 100)*
📋 Logs for api-gateway:
Error: Connection refused to postgres:5432 at ... You: "Restart it" 🤖 AI: *calls container_restart("api-gateway")*
🔄 Restarted api-gateway You: "Clean up old containers, but save the images" 🤖 AI: *calls container_prune*
🗑️ Pruned 3 stopped containers. Freed: 1.2GB
You: "What containers are running?" 🤖 AI: *calls container_ps*
▶ my-postgres (running)
▶ redis-cache (running)
▶ api-gateway (running) You: "Check the api-gateway logs, something's wrong" 🤖 AI: *calls container_logs("api-gateway", 100)*
📋 Logs for api-gateway:
Error: Connection refused to postgres:5432 at ... You: "Restart it" 🤖 AI: *calls container_restart("api-gateway")*
🔄 Restarted api-gateway You: "Clean up old containers, but save the images" 🤖 AI: *calls container_prune*
🗑️ Pruned 3 stopped containers. Freed: 1.2GB
You: "What containers are running?" 🤖 AI: *calls container_ps*
▶ my-postgres (running)
▶ redis-cache (running)
▶ api-gateway (running) You: "Check the api-gateway logs, something's wrong" 🤖 AI: *calls container_logs("api-gateway", 100)*
📋 Logs for api-gateway:
Error: Connection refused to postgres:5432 at ... You: "Restart it" 🤖 AI: *calls container_restart("api-gateway")*
🔄 Restarted api-gateway You: "Clean up old containers, but save the images" 🤖 AI: *calls container_prune*
🗑️ Pruned 3 stopped containers. Freed: 1.2GB - Break your flow by typing out commands manually
- Trust the AI to blindly run -weight: 500;">docker rm -f without confirmation (yikes!)
- Squint at raw JSON output from -weight: 500;">docker inspect - Force--weight: 500;">remove a running container
- System prune everything
- Stop ALL containers at once - The AI gets agency — it can check logs, -weight: 500;">restart services, clean up disk space
- You get safety — every destructive action requires confirmation
- Everyone gets pretty output — formatted tables instead of JSON vomit - TypeScript 5 — Fully typed, strict mode
- pi coding agent SDK — Extension API hooks
- TypeBox — Runtime type validation for LLM tool parameters
- Zero external dependencies for the runtime logic — pure pi.exec() calls - GitHub: https://github.com/k1lgor/pi-container-dashboard
- -weight: 500;">npm: pi -weight: 500;">install -weight: 500;">npm:container-dashboard
- License: MIT — go build something cool with it