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 Helper Laravel Package

beyondcode/tinkerwell-helper

Beyond Code's companion package for Tinkerwell, the desktop code runner for PHP and Laravel. It adds convenient Laravel helpers and integrations so you can execute and debug code in your application's context with less setup.

View on GitHub
Deep Wiki
Context7

Getting Started

What you actually need before anything works

This package is glue, not a REPL. It does nothing on its own. You need:

  1. A Tinkerwell license and the desktop app installed on your machine (macOS or Windows). If nobody on the team owns a seat, stop here — the rest of this document is irrelevant.
  2. A Laravel project on that same machine, with a host OS that can honor a custom URL scheme.
  3. The package itself, as a dev-only dependency.

Install

composer require --dev beyondcode/tinkerwell-helper

There is no config to publish, no migration, no env var, no service provider to register manually. Laravel's package auto-discovery picks it up from composer.json. Commit both composer.json and composer.lock.

Find and run the command

The package registers a single Artisan command that builds a tinkerwell:// deep link for the current project root and hands it to your OS. Confirm the exact signature yourself rather than trusting a README:

php artisan list | grep -i tinker

Then run it from your project root. Success looks like: Tinkerwell comes to the foreground, already bound to this project, with Eloquent models, facades, the container, config(), and your services all live — because the package bootstraps your actual Laravel application rather than running bare PHP.

Your first use case

Don't start with anything clever. Open the project in Tinkerwell and run something that would be painful in php artisan tinker:

// Is this relationship actually loading the way I think it is?
$order = Order::with('items.product')->find(1);
$order->items->map(fn ($i) => [$i->id, $i->product->name]);

// What does this query compile to before I commit it to a controller?
User::whereHas('subscriptions', fn ($q) => $q->where('status', 'active'))->toSql();

// Does this event listener actually fire in the real container?
event(new OrderShipped($order));

That loop — edit, re-run, inspect formatted output, keep the result in tabs — is the entire day-to-day value. It collapses "cd into project, remember the tinker invocation, boot the app, lose your history" into one command from the project root.

First-day checklist for a new engineer

  • Install Tinkerwell, activate a seat.
  • composer install (picks up the --dev dependency).
  • Run the launch command once and verify the app lands on the project.
  • Fall back to php artisan tinker if it doesn't — see Gotchas.

Implementation Patterns

The daily inner loop

The pattern that makes this stick is always launch from the project root, never by opening Tinkerwell and browsing to a folder. The helper resolves the current working directory and passes it to the app, so:

cd ~/code/acme-api
php artisan tinkerwell   # (or whatever `artisan list` shows)

Tinkerwell then keeps separate connection contexts per project, so multi-tab work across three repos stays unambiguous. This is the reason to prefer the command over the app's file-picker: the picker is manual and error-prone when you have similarly named projects (acme-api vs acme-api-v2).

Codify it as a Composer script (locally only)

If your team likes short verbs, add a convenience alias to composer.json:

"scripts": {
    "repl": "@php artisan <the-tinkerwell-command>"
}

Then composer repl. Do not wire this into composer test, composer ci, or any script that a pipeline invokes — see Gotchas.

Snippet patterns worth standardizing in a team gist or tinker notes file

Tinkerwell snippets are the thing you'll actually share. The ones that pay for themselves:

  • Query inspection before writing the controller. Build the query, call toSql() and ->getBindings(), then paste the finished version into the controller. No more dd()-and-refresh cycles.
  • Container and binding checks. app(SomeInterface::class) after a provider change, without booting a request cycle.
  • Synchronous job/command execution. Bus::dispatchSync(new RebuildIndexes) or Artisan::call('...') with formatted output — useful for verifying behavior, not performance.
  • Event and listener verification. Event::fake() isn't available in a normal snippet, but event(new Thing()) is, and observing real listeners fire against real config catches wiring bugs fast.
  • Data shape checks. Model::first()->toArray() or json_encode(...) to confirm casts, accessors, and $hidden behave as your API consumer will see them.

Integration with the rest of the tooling layer

Treat this exactly like Pint, Larastan, and PHPUnit: require-dev, pinned, documented in the local-setup guide, zero production surface.

  • Verify your build image runs composer install --no-dev so this never lands in a deploy artifact. Don't assume — grep the pipeline.
  • Keep php artisan tinker as the documented baseline. Tinkerwell is the GUI upgrade for people who want history, tabs, and formatted output; it is not a replacement, and container/remote-only developers must not be made to feel they're on a second-class path.
  • Opt-in, not mandated. Do not add it to a new-project template unless the team has separately decided to buy seats.
  • One line in the onboarding checklist: "Install Tinkerwell, activate your seat, run the launch command." That line is most of the return on this package — it converts a discretionary purchase into a used one.

The container/remote fallback (document it, don't improvise it)

If a developer works in Docker, a Devcontainer, WSL2, Codespaces, or over SSH, the deep link is structurally meaningless — there's no host OS handler on the other end. Prescribe the workaround in the setup doc:

  • Inside the container: php artisan tinker.
  • On the host: open Tinkerwell and point its own "open project" flow at the mounted path of the repository.
  • Or, from a host shell: tinkerwell . in the project directory, bypassing the Artisan command entirely.

Naming the fallback up front is the difference between "the command is broken" and "the command isn't supported here."

Gotchas and Tips

The silent no-op is the #1 failure mode. The command's only job is to ask the OS to open a URL scheme. In a container, WSL2, remote workspace, or CI runner, no handler is registered — so nothing happens, and typically no instructive error is raised. Debugging procedure: copy the tinkerwell:// URL the command generates and paste it into a browser address bar or open / start on the host. If that does nothing, the problem is the environment, not the package. Don't spend an afternoon on the package's source for this.

It opened the app but the right project isn't there. Usually a path-resolution issue: a volume mount the host sees differently, a symlinked working directory, or a path with spaces. Run from the real project root. If your dev environment maps /var/www/html on the container to ~/code/acme on the host, the app can't read the container path — use the app's open-project flow against the host path instead.

Never a production dependency, and never an automation primitive. Three separate rules, all easy to violate by accident:

  1. Confirm --no-dev in the deploy build. A stray composer install in a build stage silently ships a dev tool.
  2. Keep it out of CI, scheduled tasks, and any script that runs unattended.
  3. Treat it as manual-invocation only. It opens a custom URL scheme via the OS — don't feed it untrusted arguments or wrap it in something that consumes user input. If the command accepts a path argument, that's an injection surface; check the signature before building on it.

Don't assume framework-version support. Last release was 2024-05-12. Verify it against your PHP/Laravel matrix — explicitly Laravel 11+ and PHP 8.3 — before rolling it out. This is not a criticism of the wrapper; it's a two-minute test that saves a "why doesn't discovery work on the new app" thread.

Pin it and assume no upstream responsiveness. 59 stars, zero dependents, single maintainer, single-vendor ecosystem. The code risk is near-zero, but plan for the package going quiet. composer.lock is your friend; a future Laravel major stranding provider discovery is the realistic breakage, and the fix is trivial.

Understand the vendor coupling you're accepting. Beyond Code controls both the wrapper and the closed-source, commercially licensed desktop app. URL-scheme stability, OS support, pricing, and roadmap are all outside your control. That's fine — but make it a deliberate decision against artisan tinker, Telescope, Ray, and PhpStorm + Xdebug, not an accident of a Composer one-liner. If the team moves to containerized or remote-only dev environments, drop it without ceremony.

Removal is genuinely one line, no residue. No schema, no config, no published files, no cache to clear:

composer remove --dev beyondcode/tinkerwell-helper

Then delete the Composer script alias and the onboarding line. An hour of work, at most, if you'd rather own it.

Extension points: fork, don't lobby. This is a thin wrapper around one Artisan command. If you want it to also open a specific file, pass a snippet, or integrate with a different launcher, write your own command rather than waiting on upstream — the whole behavior is roughly:

// Illustrative — mirror the actual URL format the package emits
Artisan::command('repl', function () {
    $url = 'tinkerwell://'.base_path();
    // hand $url to the OS opener for the current platform
});

Five lines in app/Console/Commands and you've replaced the dependency. That escape hatch is precisely why the package is safe to adopt: the blast radius is "the shortcut stops working," never "the app stops working."

Set a review trigger. If the command isn't being invoked unprompted after a quarter, the thing to re-evaluate is the license line item, not the package. Remove it, reclaim the seats, and move on.

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.
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox
pairus/product-data
fayne/laravel-filament-cms-core