Floop

Floop Documentation


Floop Banner 2

Feedback in. Fixes out. That's the floop.

Floop is a Laravel package that turns testers into co-pilots. A floating widget captures what needs changing along with every piece of context an AI agent needs to find and fix the code. No tickets. No databases. No waiting.

View on Github

Quick Start

Install the package

composer require igclabs/floop:@dev

That's it. No migrations, no config files, no build step. The widget appears automatically on every page in your local environment.

Install the agent skill

php artisan floop:install-skill

This gives your AI agent (Claude Code, Codex, OpenCode, etc.) a skill file that teaches it how to read and process Floop work orders.

Use it

  1. Browse your app
  2. Hit the floop button (or press Ctrl+Shift+F)
  3. Type what you want changed
  4. Tell your agent: "work through feedback"

The agent reads the work order, finds the code, makes the change, and closes the loop.


Examples

Example 1: Fix a button that's too small on mobile

You're testing on your phone and the submit button is barely tappable.

You type into the widget:

The submit button is too small on mobile. I can barely tap it.

Floop generates this work order in storage/app/feedback/pending/:

# 💬 Feedback: The submit button is too small on mobile

**Status:** 🟡 Pending
**Created:** 2026-02-26 14:30:00
**Type:** Feedback
**Priority:** 🔴 High

---

## Message

The submit button is too small on mobile. I can barely tap it.

---

## Page Context

| Property | Value |
|----------|-------|
| **URL** | `https://myapp.test/orders` |
| **Route** | `orders.index` |
| **Controller** | `App\Http\Controllers\OrderController@index` |
| **Method** | `GET` |
| **View** | `orders.index` |
| **User** | Joe (joe@example.com) |
| **Viewport** | 375x812 |

### Blade Views

- `layouts.app`
- `orders.index`
- `partials.header`
- `components.order-table`

Your agent reads this and knows exactly where to go: resources/views/orders/index.blade.php, the .btn-submit class, on a 375px viewport. It adds a responsive size bump and closes the loop.


Example 2: Target a specific element

You see a price showing the wrong format. Instead of describing it, you click the element directly.

You click the price, then type:

This should show two decimal places

The work order includes a Targeted Element section:

## Targeted Element

| Property | Value |
|----------|-------|
| **Selector** | `#product-card .price-display > span.amount` |
| **Tag** | `SPAN` |
| **Text** | $19.9 |
| **Position** | 220, 340 (80×24) |

The agent now has the exact CSS selector. No guesswork.


Example 3: Report a bug with a screenshot

The page layout breaks when there's no data. You click the camera icon to capture a screenshot, then describe the issue.

You type:

The empty state is broken — the table headers still show with no rows and there's no "no results" message

The work order includes:

## Screenshot

![Screenshot](2026-02-26_150500_the-empty-state-is-broken.png)

The screenshot PNG is saved alongside the .md file. Agents that support vision can see exactly what you see.


Example 4: Console errors auto-attach

You open a page and it looks broken. The widget detects JavaScript errors automatically.

You type:

This page isn't loading properly

The work order includes captured console errors:

## Console Errors

| # | Error |
|---|-------|
| 1 | `TypeError: Cannot read properties of undefined (reading 'map') at ProductList.vue:42` |
| 2 | `Failed to load resource: the server responded with a status of 404 (Not Found) — /api/products` |

The agent sees the exact error, file, and line number — plus the failing API endpoint.


Example 5: Batch processing

You spend 10 minutes browsing the app and queue up several work orders while your agent is busy on something else. Then:

php artisan floop:list
+----+---------------------------------------------------+----------+----------+---------------------+
| #  | Summary                                           | Type     | Priority | Created             |
+----+---------------------------------------------------+----------+----------+---------------------+
| 1  | the-submit-button-is-too-small-on-mobile.md       | Feedback | High     | 2026-02-26 14:30:00 |
| 2  | this-should-show-two-decimal-places.md             | Bug      | Medium   | 2026-02-26 14:32:00 |
| 3  | add-a-search-box-to-the-users-table.md             | Task     | Medium   | 2026-02-26 14:35:00 |
| 4  | the-empty-state-is-broken.md                       | Bug      | High     | 2026-02-26 14:38:00 |
| 5  | change-the-header-color-to-match-the-brand.md      | Feedback | Low      | 2026-02-26 14:40:00 |
+----+---------------------------------------------------+----------+----------+---------------------+

Then tell your agent:

Work through the feedback

It processes each one in order — reading, fixing, and closing the loop. You get a summary at the end.


Example 6: The agent closes the loop

After making a change, the agent runs:

php artisan floop:action the-submit-button-is-too-small-on-mobile.md \
  --note="Increased button min-height to 48px and added touch-friendly padding on viewports under 640px in orders/index.blade.php"

The file moves from pending/ to actioned/. An Agent Notes section is appended to the work order:

## Agent Notes

Increased button min-height to 48px and added touch-friendly padding
on viewports under 640px in orders/index.blade.php

Next time you browse that page, verify the fix. If it's not right, submit another work order. The loop continues.


Where Work Orders Live

storage/app/feedback/
├── pending/           ← open work orders (what your agent reads)
│   ├── fix-the-login-button.md
│   ├── fix-the-login-button.png     ← screenshot companion
│   └── add-search-to-users.md
└── actioned/          ← closed loops (history)
    └── update-the-header-color.md

No database tables. No third-party services. Just markdown files in your storage directory, ignored by git.


CLI Reference

List work orders

# Show all pending work orders
php artisan floop:list

# Show actioned (completed) work orders
php artisan floop:list --status=actioned

# Filter by type
php artisan floop:list --type=bug

Close / reopen work orders

# Mark as actioned
php artisan floop:action fix-the-login-button.md

# Mark as actioned with a note
php artisan floop:action fix-the-login-button.md --note="Swapped href to the correct route"

# Reopen a previously actioned work order
php artisan floop:action fix-the-login-button.md --reopen

Clear work orders

# Clear all pending work orders
php artisan floop:clear

# Clear only actioned work orders
php artisan floop:clear --actioned

# Clear everything
php artisan floop:clear --all

Enable / disable the widget

# Disable the widget (creates a .disabled flag file)
php artisan floop:disable

# Re-enable it
php artisan floop:enable

Install the agent skill

# Auto-detect agent directories (.claude, .codex, .agents, .opencode)
php artisan floop:install-skill

# Manually choose which targets to install to
php artisan floop:install-skill --choose

# Overwrite existing skill files
php artisan floop:install-skill --force

Configuration

Publish the config file (optional — sensible defaults work out of the box):

php artisan vendor:publish --tag=floop-config

config/floop.php

return [

    // Auto-inject the widget into every HTML response.
    // Set to false if you want to place @floop manually.
    'auto_inject' => true,

    // Where work order files are stored.
    'storage_path' => storage_path('app/feedback'),

    // URL prefix for the widget's API endpoints.
    'route_prefix' => '_feedback',

    // Middleware applied to the feedback routes.
    'middleware' => ['web'],

    // Environments where the widget renders.
    // Use ['*'] to show on all environments (staging, production, etc.)
    'environments' => ['local'],

    // Widget position on the page.
    // Options: 'bottom-right', 'bottom-left', 'top-right', 'top-left'
    'position' => 'bottom-right',

    // Default feedback type when submitting.
    'default_type' => 'feedback',

    // Show the priority selector in the widget.
    'show_priority' => true,

    // Keyboard shortcut to toggle the feedback panel.
    'shortcut' => 'ctrl+shift+f',

    // Keyboard shortcut to hide/show the entire widget.
    'hide_shortcut' => 'ctrl+shift+h',

    // Maximum screenshot file size in bytes (default: 5MB).
    'screenshot_max_size' => 5242880,
];

Configuration Examples

Show the widget on staging too

'environments' => ['local', 'staging'],

Show on all environments

'environments' => ['*'],

Move the widget to the bottom-left

'position' => 'bottom-left',

Change the keyboard shortcut

'shortcut' => 'ctrl+shift+k',

Place the widget manually

Disable auto-injection:

'auto_inject' => false,

Then add the Blade directive wherever you want:

<body>
    {{-- your content --}}

    @floop
</body>

Events

Floop dispatches events you can hook into for notifications, webhooks, or custom integrations.

FeedbackStored

Fired when a new work order is submitted through the widget.

// app/Listeners/NotifyOnFeedback.php

namespace App\Listeners;

use IgcLabs\Floop\Events\FeedbackStored;

class NotifyOnFeedback
{
    public function handle(FeedbackStored $event): void
    {
        // $event->filename — the work order filename
        // $event->path     — full path to the file

        // Example: send a Slack notification
        Notification::route('slack', config('services.slack.webhook'))
            ->notify(new NewFeedbackNotification($event->filename));
    }
}

FeedbackActioned

Fired when a work order is marked as actioned.

// app/Listeners/LogActionedFeedback.php

namespace App\Listeners;

use IgcLabs\Floop\Events\FeedbackActioned;

class LogActionedFeedback
{
    public function handle(FeedbackActioned $event): void
    {
        logger()->info("Feedback actioned: {$event->filename}");
    }
}

Register them in your EventServiceProvider:

protected $listen = [
    \IgcLabs\Floop\Events\FeedbackStored::class => [
        \App\Listeners\NotifyOnFeedback::class,
    ],
    \IgcLabs\Floop\Events\FeedbackActioned::class => [
        \App\Listeners\LogActionedFeedback::class,
    ],
];

Keyboard Shortcuts

Shortcut Action
Ctrl+Shift+F Toggle the feedback panel open/closed
Ctrl+Shift+H Hide/show the entire widget
Enter Submit feedback (when typing)
Shift+Enter New line in the textarea
Escape Close the panel

What Gets Captured

Every work order automatically includes:

Data Description
URL The exact page the tester was on
Route name Laravel route name (e.g. orders.index)
Controller Full class and method (e.g. OrderController@index)
HTTP method GET, POST, etc.
Blade views Every view and partial rendered on the page
Viewport Screen dimensions — essential for responsive issues
User Authenticated user's name and email
Targeted element CSS selector, tag, text, and position of a clicked element
Screenshot PNG capture of the page state
Console errors Up to 5 deduplicated JS errors
Network failures Failed HTTP requests (400+) with method, URL, and status

All of this is written to the work order automatically. The tester just describes what they want — the context comes for free.


How It Works Under the Hood

  1. Service provider auto-discoveryFloopServiceProvider registers itself via Laravel's package discovery. No manual setup.
  2. Middleware injectionInjectFloopContext captures the current route, controller, and rendered Blade views on every request, then injects the widget HTML before </body>.
  3. Widget submission — The widget POSTs to /_feedback (configurable) with the message, priority, type, targeted element data, console errors, and optional screenshot.
  4. FloopManager writes the work order as a structured markdown file and dispatches the FeedbackStored event.
  5. AI agent processing — The agent reads the skill file, lists pending work orders, reads each one, uses the page context to locate code, makes changes, and runs floop:action to close the loop.

No database. No external services. No build toolchain. Just Laravel, markdown files, and your AI agent.


Frequently Asked Questions

Does Floop require a database?

No. All state is filesystem-based. Work orders are markdown files in storage/app/feedback/. There are no migrations and nothing to set up.

Will the widget show in production?

Not by default. The environments config defaults to ['local']. You can add 'staging' or use ['*'] to show it everywhere.

Does it work with Inertia / Livewire / SPAs?

The widget injects into any HTML response, so it works with Livewire and server-rendered Inertia pages out of the box. Full SPA support (React/Vue components) is on the roadmap.

Can I use it without an AI agent?

Yes. The work orders are human-readable markdown files. You can read them yourself and use the CLI to manage them. The AI agent integration is the superpower, but it's not required.

What AI agents are supported?

The skill file installs for Claude Code, Codex, OpenCode, and any agent that reads skill/instruction files from a .claude/, .codex/, .agents/, or .opencode/ directory. The floop:install-skill command auto-detects which ones you have.

How do I hide the widget temporarily?

Press Ctrl+Shift+H or run php artisan floop:disable. Re-enable with php artisan floop:enable or refresh after pressing the shortcut.

Are work orders committed to git?

Not by default. The storage/app/feedback/ directory is typically in .gitignore. Work orders are ephemeral — they exist to close the loop, not to be version-controlled.


Requirements

  • PHP 8.1+
  • Laravel 10, 11, or 12

License

MIT — IGC Enterprises Ltd

View on Github

Graphic Swish