Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Tinkerwell Laravel Package

beyondcode/tinkerwell

Tinkerwell is a desktop REPL and code editor for PHP and Laravel that lets you run code instantly against local or remote projects, inspect output, and query databases without touching the command line.

View on GitHub
Deep Wiki
Context7

Getting Started

Confirm the prerequisite before writing a line of code. This package is the Composer-side SDK for the Tinkerwell desktop client — drivers (how the client boots and runs code against your app) and panels (custom sidebar widgets). Without a licensed client installed, there is nothing to execute. If you don't have a seat, stop here; php artisan tinker, Ray, and your IDE debugger already cover the same inner loop.

Assuming seats exist:

  1. Install as dev-only in the Laravel app the client will connect to:
    composer require beyondcode/tinkerwell --dev
    composer dump-autoload
    
  2. Open the project root in the Tinkerwell client. Discovery is autoloader-driven — there is no service provider to register. If you go looking for one, you'll waste an afternoon.
  3. Read src/Drivers and src/Panels first. That's the entire contract. Pay particular attention to how the built-in drivers shell out to ssh and docker exec; that's the pattern every custom driver copies.
  4. Ship one small thing on day one. The highest-return first move is a single config-driven driver for your Docker or staging environment, so nobody on the team has to remember container names, SSH users, and env flags. The second is a panel answering a question you ask every week ("which tenant is that account on?").

A minimal driver:

namespace App\Tinkerwell\Drivers;

use BeyondCode\Tinkerwell\Drivers\Driver;

class DockerDriver extends Driver
{
    public function connect(): void
    {
        $this->container = config('tinkerwell.docker.container');
    }

    public function execute(string $code): string
    {
        return $this->runProcess([
            'docker', 'exec', '-i', $this->container,
            'php', 'artisan', 'tinker', '--execute=' . $code,
        ]);
    }
}

Run composer dump-autoload, reopen the project, and the driver appears in the connection picker. That's the whole loop.

Implementation Patterns

One driver per environment, named for the environment. LocalDriver, DockerDriver, StagingDriver — not MyDriver and MyDriver2. The picker then maps 1:1 to environments, and any behavioral difference (read-only credentials, different container, different PHP version) lives in code review instead of tribal memory.

Make every driver config-driven. Publish a config/tinkerwell.php (or reuse your existing app config) and read hosts, container names, SSH users, and flags from it. This is the single difference between a driver the whole team can use and a driver only its author can run on their laptop.

// config/tinkerwell.php
return [
    'docker' => [
        'container' => env('TINKERWELL_DOCKER_CONTAINER', 'app'),
    ],
    'staging' => [
        'host' => env('TINKERWELL_STAGING_HOST'),
        'user' => env('TINKERWELL_STAGING_USER', 'deploy'),
        'path' => env('TINKERWELL_STAGING_PATH', '/var/www/app'),
    ],
];

Panel per domain concept, not per query. Panels are where the compounding value sits, and each one should encode a diagnostic workflow rather than a single count. Tenant state, feature-flag resolution, queue backlog, billing lifecycle position. Build them with the same query primitives you'd type into artisan tinker, then commit them so a support engineer can answer a routine question without opening the codebase.

namespace App\Tinkerwell\Panels;

use BeyondCode\Tinkerwell\Panels\Panel;
use Illuminate\Support\Facades\DB;

class QueueHealthPanel extends Panel
{
    public function render(): array
    {
        return [
            'pending'   => DB::table('jobs')->count(),
            'oldest'    => DB::table('jobs')->min('created_at'),
            'failed_24h'=> DB::table('failed_jobs')
                ->where('failed_at', '>=', now()->subDay())->count(),
            'by_queue'  => DB::table('jobs')
                ->select('queue', DB::raw('count(*) as total'))
                ->groupBy('queue')->pluck('total', 'queue')->all(),
        ];
    }
}

Pair panels with the editor, don't duplicate it. The panel is the summary; the code tab is where you drill in. A good panel surfaces an identifier (tenant ID, job ID, flag key) that you immediately paste into an ad-hoc snippet. If a panel tries to be a full admin UI, it belongs in Telescope/Horizon instead.

Share helpers, not snippets. When two panels need the same "resolve tenant from session" logic, extract it into a small App\Tinkerwell\Support class and call it from both. Panel code is application code; treat duplication the way you'd treat duplication in a controller.

Document the workflow next to the code. A short docs/tinkerwell.md (or a section in your onboarding doc) listing which driver to pick for which environment and which panel answers which question converts the whole thing from a personal shortcut into a team asset. A new hire runs composer install (dev deps included), opens the project, and inherits every standard workflow.

Keep it adjacent to your existing tools. Tinkerwell is not a replacement for breakpoint debugging or for one-liner artisan tinker calls in CI scripts. Use the driver for multi-environment connection management and the panels for app-specific state; keep Xdebug in the IDE and keep throwaway scripts in artisan tinker.

Gotchas and Tips

Dev-only, verified. --dev is not optional. After installing, run composer install --no-dev once and confirm the package and your App\Tinkerwell classes are absent from the resulting artifact. A driver class accidentally autoloaded in production is dead weight at best and an information leak at worst.

Discovery is autoloader-driven. If your driver or panel doesn't show up, the cause is almost always one of: no composer dump-autoload after creating the class, the class not being under an autoloaded namespace, or the client pointing at a different project root than the one whose autoloader knows about it. There is no provider to debug.

Shell-out drivers fail in the shell, not in your PHP. When execute() returns empty or times out, copy the exact command array into a terminal and run it by hand. Every time I've seen this, the bug was a wrong container name, an SSH key not loaded in the agent, or a TTY flag the shell needed. -i on docker exec and -T on ssh are the usual suspects.

Never point a driver at production. A REPL wired to a live database can run unbounded writes, and there's no undo. Restrict drivers to local, ephemeral, or staging targets with scoped read-only credentials, and write that rule down. A committed connection string to a shared environment is a security incident, so add secret scanning on config/tinkerwell.php.

Panels run on every refresh — keep them cheap. Whatever render() does happens repeatedly while the client is open. Avoid unbounded table scans, N+1 loops over Model::all(), and anything that hits a remote API on each tick. Cache expensive aggregates for 30–60 seconds and cap pluck/take sizes so you never dump 50k rows into the sidebar.

Pin the version and check the client. With a small star count, zero dependents, and a single maintainer, you are effectively self-supporting. Pin an exact version in composer.json, and record which Tinkerwell client version your drivers were written against. The driver/panel API is defined by a closed-source client, so an auto-updated desktop app is as much an upgrade risk as a Composer bump.

Re-verify at every Laravel major. The package's PHP/Laravel constraints are the likeliest thing to fall behind. When they do, it surfaces as a broken composer install for developers — annoying, but not a production incident. Budget an hour at each Laravel upgrade to re-check, and be ready to fork the MIT SDK or strip the dependency if it lags.

Extension points are just base classes. Where the built-in driver or panel almost does what you need, subclass it and override the one method rather than rewriting from scratch — connect() for connection setup, render() for the data shape. Read the built-in implementations, copy the structure, and don't invent new lifecycle hooks; there aren't any.

License lapse leaves harmless dead code — but plan for it. If the seats go away, the drivers and panels simply stop being used. Know that MIT lets you fork the SDK but not the closed-source client, and assign an owner: this is now first-party code you review, version, and maintain like anything else in app/, or it dies with the engineer who wrote it.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
skyraptor/chatter
edlugz/sasapay
abbasudo/artisan-route
mirvan/ippanel
ignibyte/aic-framework
glavweb/data-schema-bundle
glavweb/datagrid-bundle
tacticmedia/scim-server
uni-method/frame
neika-ai/sdk
adachsoft/php-code-style
pharaonic/laravel-notification-dependencies
beautinow/translug
dev-brainiac/bot-cloaked
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
adachsoft/api-version-bundle
cgv/localized-resource