diff --git a/.ai/livewire/2/core.blade.php b/.ai/livewire/2/core.blade.php
new file mode 100644
index 0000000..ac380de
--- /dev/null
+++ b/.ai/livewire/2/core.blade.php
@@ -0,0 +1,16 @@
+- `wire:model` is live by default.
+- **Namespace**: Components typically exist in `App\Http\Livewire`.
+- **Events**: Use `emit()`, `emitTo()`, `emitSelf()` and `dispatchBrowserEvent()` for events.
+- Alpine is included separately to Livewire.
+- You can listen for `livewire:load` to hook into Livewire initialization, and `Livewire.onPageExpired` for when the page expires:
+@verbatim
+
+document.addEventListener('livewire:load', function () {
+ Livewire.onPageExpired(() => {
+ alert('Your session expired');
+ });
+
+ Livewire.onError(status => console.error(status));
+});
+
+@endverbatim
diff --git a/.ai/livewire/3/core.blade.php b/.ai/livewire/3/core.blade.php
index 2fde0f8..97a5269 100644
--- a/.ai/livewire/3/core.blade.php
+++ b/.ai/livewire/3/core.blade.php
@@ -1,36 +1,31 @@
-
-#### Key Changes from Livewire 2
-
-- **Namespace**: Components now use `App\Livewire` (not `App\Http\Livewire`)
-- **Events**: Use `$this->dispatch()` (not `emit` or `dispatchBrowserEvent`)
-- **Layout path**: `components.layouts.app` (not `layouts.app`)
-- **Deferred by default**: Use `wire:model.live` for real-time updates
-- **Alpine included**: Don't manually include Alpine.js
-
-#### Livewire Best Practices
-
-- **Single root element** in Blade components
-- **Add wire:key** in loops:
-
+## Key Changes from Livewire 2
+- These changed in Livewire 2, but may not have been updated in this project. Verify this project's setup to ensure you conform with project conventions.
+- **Wire:model**: Use `wire:model.live` for real-time updates, `wire:model` is now deferred by default.
+- **Namespace**: Components now use `App\Livewire` (not `App\Http\Livewire`).
+- **Events**: Use `$this->dispatch()` (not `emit` or `dispatchBrowserEvent`).
+- **Layout path**: `components.layouts.app` (not `layouts.app`).
+
+## New directives
+- `wire:show`, `wire:transition`, `wire:cloak, `wire:offline`, `wire:target` are available for use. Use the docs to find usages.
+
+## Alpine
+- Alpine is now included with Livewire, don't manually include Alpine.js.
+- Plugins built in to Alpine: persist, intersect, collapse, and focus.
+
+## Lifecycle hooks
+- You can listen for `livewire:init` to hook into Livewire initialization, and `fail.status === 419` for the page expiring:
@verbatim
-```blade
-@foreach ($items as $item)
-
- {{ $item->name }}
-
-@endforeach
-```
+
+document.addEventListener('livewire:init', function () {
+ Livewire.hook('request', ({ fail }) => {
+ if (fail && fail.status === 419) {
+ alert('Your session expired');
+ }
+ });
+
+ Livewire.hook('message.failed', (message, component) => {
+ console.error(message);
+ });
+});
+
@endverbatim
-
-- **Use attributes** for event listeners:
-
-```php
-#[On('todo-created')]
-public function refreshList()
-{
-// ...
-}
-```
-
-- **Loading states**: Use `wire:loading` and `wire:dirty`
-- **Confirmations**: Use `wire:confirm="Are you sure?"`
diff --git a/.ai/tailwindcss/3/.gitkeep b/.ai/livewire/4/.gitkeep
similarity index 100%
rename from .ai/tailwindcss/3/.gitkeep
rename to .ai/livewire/4/.gitkeep
diff --git a/.ai/livewire/core.blade.php b/.ai/livewire/core.blade.php
index a7eda0a..4c78e0a 100644
--- a/.ai/livewire/core.blade.php
+++ b/.ai/livewire/core.blade.php
@@ -1 +1,44 @@
## Livewire Core
+- Use the `search-docs` tool to find exact version specific documentation for how to write Livewire & Livewire tests.
+- Use the `php artisan make:livewire [Posts\\CreatePost]` artisan command to create new components
+- State should live on the server, with the UI reflecting it.
+- All Livewire requests hit the Laravel backend, they're like regular HTTP requests. Always validate form data, and run authorization checks in Livewire actions.
+
+## Livewire Best Practices
+- Livewire components require a single root element.
+- Use `wire:loading` and `wire:dirty` for delightful loading states.
+- Add `wire:key` in loops:
+@verbatim
+ ```blade
+ @foreach ($items as $item)
+
+ {{ $item->name }}
+
+ @endforeach
+ ```
+@endverbatim
+- Prefer lifecycle hooks like `mount()`, `updatedFoo()`) for initialization and reactive side effects:
+@verbatim
+
+ public function mount(User $user) { $this->user = $user; }
+ public function updatedSearch() { $this->resetPage(); }
+
+@endverbatim
+
+## Testing Livewire
+@verbatim
+
+ Livewire::test(Counter::class)
+ ->assertSet('count', 0)
+ ->call('increment')
+ ->assertSet('count', 1)
+ ->assertSee(1)
+ ->assertStatus(200);
+
+@endverbatim
+@verbatim
+
+ $this->get('/posts/create')
+ ->assertSeeLivewire(CreatePost::class);
+
+@endverbatim
diff --git a/.ai/tailwindcss/3/core.blade.php b/.ai/tailwindcss/3/core.blade.php
index fd61e7a..b825a89 100644
--- a/.ai/tailwindcss/3/core.blade.php
+++ b/.ai/tailwindcss/3/core.blade.php
@@ -1,2 +1 @@
- Always use Tailwind CSS v3, verify you're using only supported classes.
-- Use the `search-docs` tool to find exactly what's supported in this project's Tailwind setup.
diff --git a/.ai/tailwindcss/4/.gitkeep b/.ai/tailwindcss/4/.gitkeep
deleted file mode 100644
index e69de29..0000000
diff --git a/.ai/tailwindcss/4/core.blade.php b/.ai/tailwindcss/4/core.blade.php
index 5187aae..88645e0 100644
--- a/.ai/tailwindcss/4/core.blade.php
+++ b/.ai/tailwindcss/4/core.blade.php
@@ -1,5 +1,4 @@
- Always use Tailwind CSS v4, do not use the deprecated utilities.
-- Use the `search-docs` tool to find exactly what's supported in this project's Tailwind setup.
- In Tailwind v4 you import Tailwind using a regular CSS `@import` statement, not using the `@tailwind` directives used in v3:
@verbatim
$product->delete();
?>
-
+
@endvolt
@endverbatim
-
@verbatim
@endverbatim
-
-
@verbatim
diff --git a/src/Mcp/Tools/SearchDocs.php b/src/Mcp/Tools/SearchDocs.php
index 487f6fe..ef2c132 100644
--- a/src/Mcp/Tools/SearchDocs.php
+++ b/src/Mcp/Tools/SearchDocs.php
@@ -22,15 +22,20 @@ public function __construct(protected Roster $roster)
public function description(): string
{
- return 'Search for up-to-date version-specific documentation related to this project and its packages. This tool will search Laravel hosted documentation based on the packages installed and is perfect for all Laravel related packages. Laravel, inertia, pest, livewire, filament, nova, nightwatch, and more.'.PHP_EOL.'You must use this tool to search for Laravel-ecosystem docs before using other approaches.';
+ return 'Search for up-to-date version-specific documentation related to this project and its packages. This tool will search Laravel hosted documentation based on the packages installed and is perfect for all Laravel related packages. Laravel, inertia, pest, livewire, filament, nova, nightwatch, and more.'.PHP_EOL.'You must use this tool to search for Laravel-ecosystem docs before using other approaches. The results provided are for this project\'s package version and does not cover all versions of the package.';
}
public function schema(ToolInputSchema $schema): ToolInputSchema
{
return $schema
- ->string('queries')
- ->description('### separated list of queries to perform. Useful to pass multiple if you aren\'t sure if it is "toggle" or "switch", or "infinite scroll" or "infinite load", for example.')->required()
-
+ ->raw('queries', [
+ 'description' => 'List of queries to perform, pass multiple if you aren\'t sure if it is "toggle" or "switch", for example',
+ 'type' => 'array',
+ 'items' => [
+ 'type' => 'string',
+ 'description' => 'Search query',
+ ],
+ ])->required()
->raw('packages', [
'description' => 'Package names to limit searching to from application-info. Useful if you know the package(s) you need. i.e. laravel/framework, inertiajs/inertia-laravel, @inertiajs/react',
'type' => 'array',
@@ -54,7 +59,7 @@ public function handle(array $arguments): ToolResult|Generator
$packagesFilter = array_key_exists('packages', $arguments) ? $arguments['packages'] : null;
$queries = array_filter(
- array_map('trim', explode('###', $arguments['queries'])),
+ array_map('trim', $arguments['queries']),
fn ($query) => $query !== '' && $query !== '*'
);
@@ -90,6 +95,7 @@ public function handle(array $arguments): ToolResult|Generator
'token_limit' => $tokenLimit,
'format' => 'markdown',
];
+
try {
$response = $this->client()->asJson()->post($apiUrl, $payload);
diff --git a/tests/Feature/Mcp/Tools/SearchDocsTest.php b/tests/Feature/Mcp/Tools/SearchDocsTest.php
index 623ce98..2e0f7fd 100644
--- a/tests/Feature/Mcp/Tools/SearchDocsTest.php
+++ b/tests/Feature/Mcp/Tools/SearchDocsTest.php
@@ -24,7 +24,7 @@
]);
$tool = new SearchDocs($roster);
- $result = $tool->handle(['queries' => 'authentication###testing']);
+ $result = $tool->handle(['queries' => ['authentication', 'testing']]);
expect($result)->toBeInstanceOf(ToolResult::class);
@@ -57,7 +57,7 @@
]);
$tool = new SearchDocs($roster);
- $result = $tool->handle(['queries' => 'authentication']);
+ $result = $tool->handle(['queries' => ['authentication']]);
expect($result)->toBeInstanceOf(ToolResult::class);
@@ -77,7 +77,7 @@
]);
$tool = new SearchDocs($roster);
- $result = $tool->handle(['queries' => 'test### ###*### ']);
+ $result = $tool->handle(['queries' => ['test', ' ', '*', ' ']]);
expect($result)->toBeInstanceOf(ToolResult::class);
@@ -106,7 +106,7 @@
]);
$tool = new SearchDocs($roster);
- $result = $tool->handle(['queries' => 'test']);
+ $result = $tool->handle(['queries' => ['test']]);
expect($result)->toBeInstanceOf(ToolResult::class);
@@ -129,7 +129,7 @@
]);
$tool = new SearchDocs($roster);
- $result = $tool->handle(['queries' => 'nonexistent']);
+ $result = $tool->handle(['queries' => ['nonexistent']]);
expect($result)->toBeInstanceOf(ToolResult::class);
@@ -149,7 +149,7 @@
]);
$tool = new SearchDocs($roster);
- $result = $tool->handle(['queries' => 'test', 'token_limit' => 5000]);
+ $result = $tool->handle(['queries' => ['test'], 'token_limit' => 5000]);
expect($result)->toBeInstanceOf(ToolResult::class);
@@ -169,7 +169,7 @@
]);
$tool = new SearchDocs($roster);
- $result = $tool->handle(['queries' => 'test', 'token_limit' => 2000000]);
+ $result = $tool->handle(['queries' => ['test'], 'token_limit' => 2000000]);
expect($result)->toBeInstanceOf(ToolResult::class);