escapeboy/claude-code-kit/module-assistant

Claude Code and agent skill by escapeboy/claude-code-kit

Trust tier 3
  • community
Source check pending

Checking that the SKILL.md file is still in the repository.

Add an AI assistant chat panel to any Laravel project — with tool calling, streaming, and MCP support

Install

npx vskill@latest install escapeboy/claude-code-kit/module-assistant

SKILL.md

Module: AI Assistant

Implement an AI-powered assistant chat panel embedded in your Laravel application. The assistant understands your domain, can execute tools to read/write data, and supports multiple LLM providers including local agents.

Reference implementation: Agent Fleet — Livewire panel with streaming, PrismPHP tool calling, local agent support (Claude Code text-based <tool_call> loop, Codex via MCP), conversation history, context awareness.


Usage

/module:assistant [action]
Quick Examples
bash
/module:assistant # Full implementation
/module:assistant analyze # Only analyze and produce a component plan
/module:assistant tools # Only generate the tool registry (add to existing assistant)

Actions

1. Full Implementation (Default)

Implements the complete assistant: database, backend, tools, frontend.

Process:

Phase 1: Analyze the Project
  1. Read CLAUDE.md to understand the domain, models, and architecture.
  1. Check prerequisites:
  • Livewire 3+ installed? (composer show livewire/livewire)
  • PrismPHP installed? (composer show prism-php/prism)
  • Alpine.js available? (comes with Livewire)
  • Tailwind CSS configured?
  1. If MCP is already set up (from /module:mcp): the assistant can reference MCP tools for local agents that support MCP natively (e.g. Codex).
  1. Identify domain tools — the assistant needs tools to interact with your data. Scan models and actions to plan what tools to create (same analysis as /module:mcp analyze).
  1. Decide the tool strategy:
  • Cloud LLMs (Anthropic, OpenAI): Use PrismPHP Tool objects — native function calling.
  • Claude Code (local): Use text-based <tool_call> format — system prompt describes tool schemas, responses parsed for <tool_call> tags.
  • Codex (local): Use MCP natively — connect to your MCP server (requires /module:mcp first).
Phase 2: Database

Create the migration for assistant conversations and messages.

bash
php artisan make:migration create_assistant_tables

Migration:

php
Schema::create('assistant_conversations', function (Blueprint $table) {
$table->uuid('id')->primary();
$table->foreignUuid('user_id')->constrained()->cascadeOnDelete();
$table->string('title')->nullable();
$table->string('context_type')->nullable(); // e.g. 'order', 'product'
$table->uuid('context_id')->nullable(); // ID of the entity being discussed
$table->timestamps();
});
Schema::create('assistant_messages', function (Blueprint $table) {
$table->uuid('id')->primary();
$table->foreignUuid('conversation_id')->constrained('assistant_conversations')->cascadeOnDelete();
$table->string('role'); // 'user', 'assistant', 'system'
$table->text('content');
$table->jsonb('tool_calls')->nullable(); // Tool execution results
$table->jsonb('token_usage')->nullable(); // {prompt_tokens, completion_tokens, cost}
$table->timestamps();
$table->index(['conversation_id', 'created_at']);
});

Models:

php
// app/Domain/Assistant/Models/AssistantConversation.php
class AssistantConversation extends Model
{
use HasUuids;
protected $fillable = ['user_id', 'title', 'context_type', 'context_id'];
public function user(): BelongsTo { return $this->belongsTo(User::class); }
public function messages(): HasMany { return $this->hasMany(AssistantMessage::class, 'conversation_id'); }
}
// app/Domain/Assistant/Models/AssistantMessage.php
class AssistantMessage extends Model
{
use HasUuids;
protected $fillable = ['conversation_id', 'role', 'content', 'tool_calls', 'token_usage'];
protected function casts(): array {
return ['tool_calls' => 'array', 'token_usage' => 'array'];
}
public function conversation(): BelongsTo { return $this->belongsTo(AssistantConversation::class, 'conversation_id'); }
}
Phase 3: Assistant Tools (PrismPHP)

Create tool classes that the assistant can call. These are PrismPHP Tool objects, NOT MCP tools (MCP tools are for external agents; PrismPHP tools are for in-process tool calling).

Tool registry at app/Domain/Assistant/Services/AssistantToolRegistry.php:

php
<?php
namespace App\Domain\Assistant\Services;
use App\Domain\Assistant\Tools\ListTools;
use App\Domain\Assistant\Tools\GetTools;
use App\Domain\Assistant\Tools\MutationTools;
use App\Models\User;
class AssistantToolRegistry
{
/**
* Get all available tools for the given user.
*
* @return array<\Prism\Prism\Tool>
*/
public function getTools(User $user): array
{
$tools = [];
// Read tools — always available
$tools = array_merge($tools, ListTools::tools());
$tools = array_merge($tools, GetTools::tools());
// Write tools — check user permissions
if ($this->canWrite($user)) {
$tools = array_merge($tools, MutationTools::writeTools());
}
// Destructive tools — admin only
if ($this->canDestroy($user)) {
$tools = array_merge($tools, MutationTools::destructiveTools());
}
return $tools;
}
private function canWrite(User $user): bool
{
// Adapt to your auth model
return true; // or $user->hasRole('admin', 'editor')
}
private function canDestroy(User $user): bool
{
return $user->is_admin ?? false;
}
}

Tool implementation pattern (PrismPHP tools):

php
<?php
namespace App\Domain\Assistant\Tools;
use App\Models\Order;
use Prism\Prism\Tool;
class ListTools
{
/**
* @return array<Tool>
*/
public static function tools(): array
{
return [
self::listOrders(),
self::listProducts(),
// ... add more list tools
];
}
private static function listOrders(): Tool
{
return Tool::as('list_orders')
->for('List orders with optional status filter. Returns id, number, status, total, date.')
->withStringParameter('status', 'Filter: pending, processing, completed, cancelled')
->withNumberParameter('limit', 'Max results (default 10)')
->using(function (?string $status = null, ?int $limit = 10): string {
$query = Order::query()->orderByDesc('created_at');
if ($status) {
$query->where('status', $status);
}
$items = $query->limit(min($limit ?? 10, 100))
->get(['id', 'order_number', 'status', 'total', 'created_at']);
return json_encode(['count' => $items->count(), 'orders' => $items->toArray()]);
});
}
}

Group tools by concern: ListTools, GetTools, MutationTools, StatusTools. Each returns an array of Prism\Prism\Tool objects.

Phase 4: Core Services

ConversationManager — handles conversation CRUD and message history:

php
<?php
namespace App\Domain\Assistant\Services;
use App\Domain\Assistant\Models\AssistantConversation;
class ConversationManager
{
public function addMessage(AssistantConversation $conversation, string $role, string $content, ?array $toolCalls = null, ?array $tokenUsage = null): void
{
$conversation->messages()->create([
'role' => $role,
'content' => $content,
'tool_calls' => $toolCalls,
'token_usage' => $tokenUsage,
]);
}
public function buildMessageHistory(AssistantConversation $conversation, int $limit = 20): array
{
return $conversation->messages()
->orderBy('created_at')
->limit($limit)
->get(['role', 'content'])
->map(fn ($m) => ['role' => $m->role, 'content' => $m->content])
->toArray();
}
public function generateTitle(AssistantConversation $conversation): void
{
if ($conversation->title) return;
$firstMessage = $conversation->messages()->where('role', 'user')->first();
if ($firstMessage) {
$conversation->update(['title' => Str::limit($firstMessage->content, 80)]);
}
}
}

ContextResolver — injects page-level context into the system prompt:

php
<?php
namespace App\Domain\Assistant\Services;
class ContextResolver
{
/**
* Resolve context for the current page the user is viewing.
*/
public function resolve(?string $contextType, ?string $contextId): string
{
if (! $contextType || ! $contextId) {
return 'The user is on the dashboard.';
}
return match ($contextType) {
'order' => $this->resolveOrder($contextId),
'product' => $this->resolveProduct($contextId),
// Add more entity types as needed
default => "Viewing {$contextType} #{$contextId}",
};
}
private function resolveOrder(string $id): string
{
$order = \App\Models\Order::with('items')->find($id);
if (! $order) return 'Order not found.';
return "Viewing Order #{$order->order_number} — Status: {$order->status}, Total: {$order->total}, Items: {$order->items->count()}";
}
}

SendAssistantMessageAction — the core action that orchestrates everything:

php
<?php
namespace App\Domain\Assistant\Actions;
use App\Domain\Assistant\Models\AssistantConversation;
use App\Domain\Assistant\Services\AssistantToolRegistry;
use App\Domain\Assistant\Services\ContextResolver;
use App\Domain\Assistant\Services\ConversationManager;
use App\Models\User;
class SendAssistantMessageAction
{
public function __construct(
private readonly ConversationManager $conversationManager,
private readonly ContextResolver $contextResolver,
private readonly AssistantToolRegistry $toolRegistry,
) {}
public function execute(
AssistantConversation $conversation,
string $userMessage,
User $user,
?string $contextType = null,
?string $contextId = null,
?string $provider = null,
?string $model = null,
): mixed {
// 1. Save user message
$this->conversationManager->addMessage($conversation, 'user', $userMessage);
// 2. Resolve provider (from settings or defaults)
$provider = $provider ?? config('assistant.default_provider', 'anthropic');
$model = $model ?? config('assistant.default_model', 'claude-sonnet-5');
// 3. Detect local vs cloud provider
$isLocal = in_array($provider, ['codex', 'claude-code']);
// 4. Get tools based on user permissions
$tools = $this->toolRegistry->getTools($user);
// 5. Build system prompt
$context = $this->contextResolver->resolve($contextType, $contextId);
$systemPrompt = $this->buildSystemPrompt($context, $user, $tools, $isLocal, $provider);
// 6. Build conversation history
$history = $this->conversationManager->buildMessageHistory($conversation);
$userPrompt = $this->buildUserPrompt($history, $userMessage);
// 7. Execute based on provider type
if ($isLocal && $provider === 'claude-code') {
// Claude Code: text-based <tool_call> loop
$response = $this->executeWithLocalToolLoop($provider, $model, $systemPrompt, $userPrompt, $tools, $user);
} elseif ($isLocal && $provider === 'codex') {
// Codex: MCP native — tools come from MCP server, no PrismPHP tools needed
$response = $this->executeLocal($provider, $model, $systemPrompt, $userPrompt, $user);
} else {
// Cloud: PrismPHP handles tool calling natively
$response = $this->executeCloud($provider, $model, $systemPrompt, $userPrompt, $tools, $user);
}

This is the start of the file. Read the full SKILL.md on GitHub.

Security

  • Security scanPassed, 90 of 100Pattern checks on the skill's files.
  • Model reviewPassed

Quality evaluation

  • Task evaluationNot runNo evaluation yet. Run one in Skill Studio.

Source

Popularity

7-day trend score
0

Not refreshed yet.

Works with

Any agent vskill installs to: 56 targets in all. These 39 have a page on this site.

  • Claude Code
  • Cursor
  • GitHub Copilot
  • Windsurf
  • Codex
  • Gemini CLI
  • Cline
  • Amp
  • Roo Code
  • Goose
  • Continue
  • Kilo Code
  • Junie
  • Trae
  • OpenHands
  • Qwen Code
  • AI Agent
  • Tabnine Chat
  • Sourcegraph Cody
  • Amazon Q
  • Firebase Genkit
  • Sweep
  • Devin
  • Phind
  • Mentat
  • Claude Desktop
  • Aider
  • GPT Pilot
  • Plandex
  • bolt.new
  • v0
  • Replit Agent
  • Anse
  • TypingMind
  • BetterChatGPT
  • LibreChat
  • Warp AI
  • Pieces
  • Fig AI
Embed badge
vskill VERIFIED badge
[![vskill VERIFIED](https://verified-skill.com/api/v1/skills/escapeboy/claude-code-kit/module-assistant/badge)](https://verified-skill.com/skills/escapeboy/claude-code-kit/module-assistant)

Something wrong with this skill or its page? Tell us.