PHPackages                             dan/ai-loom-planner - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Utility &amp; Helpers](/categories/utility)
4. /
5. dan/ai-loom-planner

ActiveLibrary[Utility &amp; Helpers](/categories/utility)

dan/ai-loom-planner
===================

Generate context files and agent prompts from Loom video walkthroughs.

v3.1.0(2mo ago)0240↑37.5%MITPHPPHP ^8.2

Since Apr 29Pushed 2mo agoCompare

[ Source](https://github.com/DataAndNumbersOrganization/ai-loom-plan)[ Packagist](https://packagist.org/packages/dan/ai-loom-planner)[ Docs](https://github.com/DataAndNumbersOrganization/ai-loom-plan)[ RSS](/packages/dan-ai-loom-planner/feed)WikiDiscussions main Synced 1w ago

READMEChangelogDependencies (5)Versions (9)Used By (0)

AI Loom Planner
===============

[](#ai-loom-planner)

Generate ready-to-use AI agent prompts from Loom video walkthroughs — right from the command line.

This Laravel package extracts a Loom transcript and screenshots, builds a structured context file, and prints a copy-pastable prompt for your preferred AI agent (Warp, Cursor, Copilot, etc.) to turn into an implementation plan.

Features
--------

[](#features)

- **Transcript extraction** — pulls Loom transcripts via oEmbed, page scraping, API fallback, and Playwright (automatic cascade)
- **Screenshot capture** — grabs frames at configurable intervals with perceptual-hash deduplication
- **Prompt building** — assembles a structured context file with transcript, metadata, and screenshot references
- **Prompt templates** — ships with `feature`, `bug`, `epic`, and `documentation` templates; fully customisable via publish
- **Two commands** — `loom:plan` for context + prompt output; `loom:transcript` for quick transcript access

Requirements
------------

[](#requirements)

DependencyVersionPHP8.2+Laravel11.x, 12.x, or 13.xNode.js18+ (for Playwright scripts)Playwright`@playwright/test` installed in your projectNo AI provider or API key is required — the command builds the context and prints a prompt for you to paste into your preferred AI agent (Warp, Cursor, Copilot, etc.).

Playwright is only needed if you use screenshot capture (`--screenshots`) or if static transcript scraping fails (Playwright is the final fallback for transcript extraction). The package works without it — you'll just get fewer features.

Installation
------------

[](#installation)

```
composer require dan/ai-loom-planner
```

Publish the config file:

```
php artisan vendor:publish --tag=loom-planner-config
```

Optionally publish the prompt templates if you want to customise them:

```
php artisan vendor:publish --tag=loom-planner-templates
```

### Playwright setup (optional)

[](#playwright-setup-optional)

If you want screenshot capture or the Playwright transcript fallback:

```
npm install @playwright/test
npx playwright install chromium
```

Configuration
-------------

[](#configuration)

After publishing, edit `config/loom-planner.php`:

```
return [
    /*
    |--------------------------------------------------------------------------
    | Tech Stack Description
    |--------------------------------------------------------------------------
    |
    | A multi-line string describing your project's tech stack.
    | This is injected into the AI prompt so the plan references the
    | correct frameworks, languages, and tools.
    |
    | Set to null to use the built-in default (Laravel + Next.js + MySQL).
    |
    */
    'tech_stack' => env('LOOM_PLAN_TECH_STACK', null),

    /*
    |--------------------------------------------------------------------------
    | Application Name
    |--------------------------------------------------------------------------
    |
    | Used in AI prompts to give the model project context.
    | Defaults to your Laravel app name.
    |
    */
    'app_name' => env('LOOM_PLAN_APP_NAME', null), // falls back to config('app.name')

    /*
    |--------------------------------------------------------------------------
    | Output Directory
    |--------------------------------------------------------------------------
    |
    | Where generated plans and screenshots are saved.
    |
    */
    'output_dir' => env('LOOM_PLAN_OUTPUT_DIR', 'docs-and-plans/loom'),

    /*
    |--------------------------------------------------------------------------
    | Templates Directory
    |--------------------------------------------------------------------------
    |
    | Path to a directory containing prompt Blade templates.
    | Leave as null to use the package's bundled templates.
    | If you publish templates with `vendor:publish --tag=loom-planner-templates`,
    | set this to `resource_path('views/vendor/loom-planner')` to load them.
    |
    */
    'templates_dir' => null,
];
```

### Environment variables

[](#environment-variables)

```
# Optional overrides
LOOM_PLAN_OUTPUT_DIR=docs-and-plans/loom
LOOM_PLAN_TECH_STACK="- **Backend**: Laravel\n- **Frontend**: React"
LOOM_PLAN_APP_NAME=MyApp
```

Usage
-----

[](#usage)

### `loom:plan` — Generate an implementation plan

[](#loomplan--generate-an-implementation-plan)

```
# Fetch transcript, capture screenshots, and output a copy-pastable agent prompt
php artisan loom:plan https://www.loom.com/share/abc123def456...

# Capture screenshots every 5 seconds (default: 10)
php artisan loom:plan https://www.loom.com/share/abc123... --screenshots=5

# Disable screenshots entirely
php artisan loom:plan https://www.loom.com/share/abc123... --screenshots=0

# Use the "bug" plan template
php artisan loom:plan https://www.loom.com/share/abc123... --template=bug

# Custom output filename
php artisan loom:plan https://www.loom.com/share/abc123... --output=my-feature-plan
```

#### Options

[](#options)

OptionDescriptionDefault`url` (argument)Loom video URL— (required)`--screenshots`Seconds between screenshot captures (1–60, 0 to disable)`10``--template`Plan template: `feature`, `bug`, `epic`, or `documentation``feature``--output`Custom output filenameAuto-generated from video title#### Workflow

[](#workflow)

The command fetches the transcript, captures screenshots, writes a context file, and prints a copy-pastable agent prompt:

```
🎬 Fetching Loom video data...
  ✓ Video: Dashboard Redesign Walkthrough
  ✓ Duration: 4m 32s
  ✓ Transcript: 847 words
📸 Capturing screenshots at 10s intervals...
  ✓ Captured 27 frames, 14 unique after dedup

─── Copy below ───

I have a Loom video walkthrough for a new feature I need to implement...

docs-and-plans/loom/contexts/loom-plan-dashboard-redesign-walkthrough-context.md
docs-and-plans/loom/screenshots/loom-plan-dashboard-redesign-walkthrough-0010-reviewing-the-dashboard.jpg

─── End ───

```

### `loom:transcript` — Fetch a transcript

[](#loomtranscript--fetch-a-transcript)

```
# Plain text transcript
php artisan loom:transcript https://www.loom.com/share/abc123...

# With timestamps
php artisan loom:transcript https://www.loom.com/share/abc123... --timestamps

# Raw JSON output
php artisan loom:transcript https://www.loom.com/share/abc123... --json
```

#### Options

[](#options-1)

OptionDescriptionDefault`url` (argument)Loom video URL— (required)`--timestamps`Prefix each segment with `[M:SS]``false``--json`Output raw JSON (title, duration, transcript, segments)`false`How It Works
------------

[](#how-it-works)

### 1. Video data extraction (`LoomVideoService`)

[](#1-video-data-extraction-loomvideoservice)

The service tries multiple strategies in order:

1. **oEmbed API** — fetches title, thumbnail, and duration (no auth required)
2. **Page scraping** — loads the share page HTML and searches for transcript data in:
    - Apollo state (`__APOLLO_STATE__`)
    - Next.js data (`__NEXT_DATA__`)
    - Generic `` tags containing video JSON
3. **Transcription API** — tries Loom's direct `/v1/videos/{id}/transcriptions` endpoint
4. **Playwright** (final fallback) — launches a headless browser, loads the page with full JS execution, and extracts the transcript from the rendered DOM or intercepted API calls

### 2. Screenshot capture (`LoomScreenshotService`)

[](#2-screenshot-capture-loomscreenshotservice)

When screenshots are enabled, a Node.js/Playwright script:

- Opens the Loom embed in a headless Chromium browser
- Seeks the video to each target timestamp and takes a screenshot
- Computes a perceptual hash (aHash) for each frame *in the browser*
- Deduplicates consecutive identical/near-identical frames (Hamming distance ≤ 5)
- Returns only unique frames as JPEG files

Screenshots are saved with contextual filenames derived from the nearest transcript segment:

```
loom-plan-BBCM-1234-0012-reviewing-the-dashboard.jpg
loom-plan-BBCM-1234-0047-clicking-the-save-button.jpg

```

### 3. Context + prompt output (`LoomPlanService`)

[](#3-context--prompt-output-loomplanservice)

The service assembles a structured context prompt containing:

- Video metadata (title, duration, URL)
- Full timestamped transcript
- Screenshot labels and timestamps
- Your project's tech stack description

This is saved as a context markdown file and its path is printed alongside a ready-to-paste agent instruction. Paste both into your AI agent of choice to generate the plan.

Templates
---------

[](#templates)

The package ships with four prompt templates in `resources/templates/`:

TemplateUse case`feature`New feature implementation (default)`bug`Bug diagnosis and fix planning`epic`Breaking down a large epic into discrete tasks`documentation`Converting a walkthrough into admin-facing documentation### Customising templates

[](#customising-templates)

After publishing (`vendor:publish --tag=loom-planner-templates`), templates are copied to `resources/views/vendor/loom-planner/`. Each template receives two variables:

- `$planPath` — the file path where the plan should be saved
- `$screenshotLine` — a sentence about attached screenshots (empty string if none)

Example custom template:

```
Read the transcript below and create a migration plan for our Phoenix/Elixir app.{{ $screenshotLine }} Save the plan to `{{ $planPath }}`.
```

Output Structure
----------------

[](#output-structure)

All generated files are saved under the configured `output_dir` (default: `docs-and-plans/loom/`):

```
docs-and-plans/loom/
├── contexts/
│   └── loom-plan-my-feature-context.md      # Transcript + metadata (attach to AI agent)
└── screenshots/
    ├── loom-plan-my-feature-0000-intro-and-overview.jpg
    ├── loom-plan-my-feature-0010-reviewing-the-dashboard.jpg
    └── ...

```

Architecture
------------

[](#architecture)

```
src/
├── LoomPlannerServiceProvider.php   # Config, commands, views registration
├── Commands/
│   ├── LoomPlanCommand.php          # loom:plan artisan command
│   └── LoomTranscriptCommand.php    # loom:transcript artisan command
└── Services/
    ├── LoomVideoService.php         # URL parsing, oEmbed, transcript extraction
    ├── LoomPlanService.php          # Context/prompt building
    └── LoomScreenshotService.php    # Playwright screenshot capture & dedup

```

The Playwright helper scripts (`resources/scripts/loom-transcript.cjs` and `loom-screenshot.cjs`) intentionally use the `.cjs` extension so Node loads them as CommonJS even when the consumer project's `package.json` declares `"type": "module"`. See [Troubleshooting](#troubleshooting) for context.

Troubleshooting
---------------

[](#troubleshooting)

### `ReferenceError: require is not defined in ES module scope`

[](#referenceerror-require-is-not-defined-in-es-module-scope)

Fixed in **v1.0.1**. If you are pinned to `v1.0.0` and your project's `package.json` declares `"type": "module"`, the bundled `.js` Playwright scripts will fail with this error because Node treats every `.js` file as ESM. Bump to `^1.0.1` (`composer update dan/ai-loom-planner`) — the scripts now ship as `.cjs` so Node always parses them as CommonJS.

### `Cannot find module '@playwright/test'`

[](#cannot-find-module-playwrighttest)

Install Playwright in your consuming project (the package looks up `@playwright/test` via `NODE_PATH=/node_modules`):

```
npm install @playwright/test
npx playwright install chromium
```

This is only required if you use `--screenshots` or hit the Playwright transcript fallback.

### `node not found — cannot run Playwright transcript extraction`

[](#node-not-found--cannot-run-playwright-transcript-extraction)

The package looks for `node` via `which node`, then falls back to `/usr/local/bin/node`, `/opt/homebrew/bin/node`, and `/usr/bin/node`. If your Node.js binary is somewhere else (e.g. nvm-managed), make sure that path is on the PHP process's `PATH` (Laravel Herd / valet / Octane workers may have a different `PATH` from your interactive shell).

Testing
-------

[](#testing)

```
composer test
```

The test suite covers:

- **Unit tests** — service-level logic (URL parsing, prompt building, transcript normalisation, config)
- **Feature tests** — full command execution with mocked services

See [`tests/`](tests/) for the complete test suite.

Versioning
----------

[](#versioning)

This package follows [Semantic Versioning](https://semver.org/). The public API surface — artisan command signatures, service class signatures, config keys, and publish tags — is covered by semver from `v1.0.0` onwards. See [`CHANGELOG.md`](CHANGELOG.md) for release notes.

License
-------

[](#license)

MIT — see [`LICENSE`](LICENSE) for the full text.

###  Health Score

42

—

FairBetter than 88% of packages

Maintenance85

Actively maintained with recent releases

Popularity15

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity52

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~1 days

Total

8

Last Release

75d ago

Major Versions

v1.0.3 → v3.0.02026-05-01

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/219?v=4)[Dan Benjamin](/maintainers/dan)[@dan](https://github.com/dan)

---

Top Contributors

[![danrichards](https://avatars.githubusercontent.com/u/470255?v=4)](https://github.com/danrichards "danrichards (14 commits)")

---

Tags

laravelaiplanningtranscriptloom

###  Code Quality

TestsPHPUnit

### Embed Badge

![Health badge](/badges/dan-ai-loom-planner/health.svg)

```
[![Health](https://phpackages.com/badges/dan-ai-loom-planner/health.svg)](https://phpackages.com/packages/dan-ai-loom-planner)
```

###  Alternatives

[psalm/plugin-laravel

Psalm plugin for Laravel

3345.3M347](/packages/psalm-plugin-laravel)[laravel/mcp

Rapidly build MCP servers for your Laravel applications.

77922.3M186](/packages/laravel-mcp)[spatie/laravel-export

Create a static site bundle from a Laravel app

674146.0k6](/packages/spatie-laravel-export)[zidbih/laravel-deadlock

Make temporary Laravel workarounds expire and fail CI when ignored.

985.4k](/packages/zidbih-laravel-deadlock)[aedart/athenaeum

Athenaeum is a mono repository; a collection of various PHP packages

255.2k](/packages/aedart-athenaeum)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
