Scan your app, then generate a Markdown context file with every route, model, and job cited to its file and line — so Claude Code, Cursor, or Copilot stop guessing and start citing your actual codebase.
$ php artisan necromancer:scan Necromancer manifest written to necromancer.json $ php artisan necromancer:generate Full context written to NECROMANCER.md Compact context written to CLAUDE.md
## Routes (14) | Name | Method | URI | Controller | Middleware | Source | |---|---|---|---|---|---| | orders.index | GET | /orders | OrderController@index | auth, verified | app/Http/Controllers/OrderController.php:12 | ## Models (1) | Name | Table | Fillable | Casts | Relationships | Source | |---|---|---|---|---|---| | Order | orders | customer_id, total, status | total → decimal:2 | belongsTo Customer, hasMany OrderLine | app/Models/Order.php:1 | ## Policies (1) | Class | Model | Methods | Source | |---|---|---|---| | OrderPolicy | Order | viewAny, view, create, update, delete | app/Policies/OrderPolicy.php:1 |
The manifest covers 18 artifact types across the full Laravel application structure. Every artifact carries a source field with file, line, and hash for precise citations and stale detection.
php artisan necromancer:graph
projects the manifest into an interactive, force-directed graph — colored by kind, connected by the same relationships your AI agent reads out of NECROMANCER.md. No build step, no server: graph.html opens straight from disk.
Necromancer follows a scan-first workflow — every other command reads the manifest produced by
necromancer:scan. Grouped below by what you're trying to do, not execution order.
necromancer:scan
Bootstraps the application and writes the necromancer.json manifest. Re-run whenever your application changes. Supports partial scans and CI drift detection.
php artisan necromancer:scan
php artisan necromancer:scan --only=routes,models
php artisan necromancer:scan --diff --fail-on-drift # CI gate
necromancer:map
Terminal-based pretty-printer of the manifest inventory. Read-only; never writes to disk. Shows a stale-manifest warning if source files are newer than the manifest.
php artisan necromancer:map php artisan necromancer:map --type=routes php artisan necromancer:map --type=models
necromancer:audit
Scored AI-readability report (0–100). Findings grouped by severity: error (−10), warning (−5), suggestion (−1). Checks unnamed routes, missing model casts, dead events, unconfigured jobs, high-risk artifacts without an ADR, external-service artifacts without tests, and more.
ERRORS ✗ Unnamed routes (3) GET /orders routes/web.php:12 WARNINGS ⚠ Missing return types (7) OrderController::store() app/Http/Controllers/OrderController.php:45
php artisan necromancer:audit php artisan necromancer:audit --format=markdown --output=audit.md php artisan necromancer:audit --fail-on=error # exit 1 on errors (CI) php artisan necromancer:audit --fail-on=warning # exit 1 on warnings or errors
necromancer:doctor
Eight-dimension AI readability percentage dashboard with progress bars. Dimensions: Route Clarity, Model Expressiveness, Authorization Coverage, Validation Coverage, Async Clarity, Codebase Vocabulary, Test Presence, Artifact Annotation Coverage.
Laravel Necromancer — AI Readability Score ────────────────────────────────────────── Score: 74% Route Clarity ████████░░ 82% (12/15 named · 14/15 controller-backed) Model Expressiveness ██████░░░░ 61% (3/5 casts · 4/5 fillable · 2/5 relationships) Authorization Coverage ███████░░░ 70% (2/3 policies · 8/12 write routes with auth) Validation Coverage ████████░░ 80% (8/10 write routes with FormRequest) Async Clarity ████████░░ 83% (4/5 jobs configured · 4/4 events with listeners) Codebase Vocabulary ██████░░░░ 63% (5/8 commands described · 1/1 backed enums) Test Presence ████████░░ 80% (4/5 models · 3/3 jobs) Artifact Annotation Cov. ████████░░ 83% (5/6 tagged with domain · 2/2 high-risk with ADR · 1/2 external-service artifacts tested · 4/4 flow-consistent) Tip: run necromancer:audit for a detailed findings list.
php artisan necromancer:doctor
php artisan necromancer:doctor --json
php artisan necromancer:doctor --min-score=80 # exit 1 when score < 80 (CI)
php artisan necromancer:doctor --only=route-clarity
necromancer:generate
Produces NECROMANCER.md — a full Markdown context file for AI coding agents. Renders Domain/Risk/External Services/ADR columns in the routes table when route metadata is declared. When Laravel Boost is installed, writes to .ai/guidelines/necromancer.md automatically. When a Knowledge Bundle exists (okf/ and/or okf-enriched/), a ## Knowledge Bundle section names its path, regenerate command, and live stats — unaffected by --only/--except/--paths, suppressible via okf.announce_in_context. Each line compares the bundle's own content_hash against the current manifest's and appends a "may be stale" caveat on mismatch — never a claim either way for a bundle exported before this field existed.
php artisan necromancer:generate php artisan necromancer:generate --only=routes,models php artisan necromancer:generate --except=listeners,validation_rules php artisan necromancer:generate --paths=app/Models,app/Http/Controllers/Admin php artisan necromancer:generate --output=.ai/context/app.md --force
necromancer:prompt
Builds a ready-to-paste AI prompt grounded in the most relevant manifest entries for your question. Keyword-searches the manifest, ranks artifacts by relevance, and outputs a prompt block with file:line citations.
php artisan necromancer:prompt "Where is tenant isolation enforced?" php artisan necromancer:prompt "billing" --top=5 php artisan necromancer:prompt "auth" --no-ai --output=prompt.txt
necromancer:ask
requires laravel/ai
Natural-language codebase Q&A. The manifest is injected verbatim into the AI's context — answers are grounded in your actual application, not the model's prior knowledge.
necromancer:infer
requires laravel/ai
Generates Architecture Decision Records (ADRs) from the manifest. Evaluates nine architectural dimensions (async-processing, authorization, event-driven, api-design, data-modeling, command-scheduling, form-validation, external-services, architecture-pattern), with a critic agent reviewing and filtering the initial ADRs.
necromancer:diff
Compares the current manifest against a branch or snapshot. Shows added, removed, and modified routes, models, jobs, events, listeners, policies, and other artifacts. Added or changed artifacts of any family tagged high/critical risk or declaring external services surface in a dedicated "Flagged Artifacts" section — no AI required. With --review, an AI agent summarises the architectural impact and surfaces risks, grounded in the same flagged-artifacts signal.
php artisan necromancer:diff main php artisan necromancer:diff main --review --format=markdown php artisan necromancer:diff --base-manifest=snapshots/before.json
necromancer:benchmark
Measures how much Necromancer's generated context improves AI accuracy, hallucination rate, latency, and token cost. Runs a task suite in three conditions by default — no context, manual AGENTS.md, and Necromancer-generated NECROMANCER.md — and reports results side by side, including per-condition average latency (generation and, separately, judge). An opt-in fourth condition, necromancer-mcp (bare instructions plus live, tool-based manifest queries instead of a pre-assembled document), adds a "Necromancer (MCP) vs Necromancer (static)" comparison line when requested via --condition=. Use --generate-suite to scaffold a grounded task suite from the current manifest before running the benchmark.
php artisan necromancer:benchmark php artisan necromancer:benchmark --generate-suite # scaffold tasks from manifest → config/benchmark-tasks.php php artisan necromancer:benchmark --generate-suite --suite-output=custom/tasks.php php artisan necromancer:benchmark --no-judge # automated checks only php artisan necromancer:benchmark --format=markdown --output=benchmark.md php artisan necromancer:benchmark --condition=necromancer,necromancer-mcp # static context vs. live tool-querying
necromancer:okf
Projects the manifest into a deterministic Open Knowledge Format (OKF) bundle — one Markdown file per artifact under okf/artifacts/, with authoritative YAML front matter and a concise Architectural Context prose mirror. Resolvable cross-artifact relationships (a route's controller, a model's relationships/policy/observers, event/listener pairings) render as Markdown links; every shared domain/flow value gets a synthesized, linked concept; and declared local ADRs are copied into the bundle with provenance — a missing one fails the export.
---
title: "SendInvoiceEmail"
type: "artifact"
kind: "jobs"
necromancer:
id: "jobs:App\\Jobs\\SendInvoiceEmail"
facts:
queue: "emails"
tries: 3
annotations:
domain: "billing"
risk: "high"
---
## Architectural Context
domain: billing · risk: high
php artisan necromancer:okf php artisan necromancer:okf --allow-stale --allow-partial php artisan necromancer:okf --output=dist/okf
necromancer:okf-enrich
requires laravel/ai
Writes an AI-enriched sibling bundle alongside the deterministic one — the okf/ bundle from necromancer:okf is never touched. Enrichment can only add a description field and an "AI-Enriched Summary" section — it structurally cannot change facts, annotations, Artifact IDs, or links, because the AI is never given access to those fields. Prompts exclude raw framework metadata, source paths/hashes, configuration, and ADR body content. Each concept caches independently, and a generated okf-enriched/README.md documents all of this, with bundle.json carrying its own content_hash.
necromancer:graph
Projects the manifest into a deterministic Artifact Graph — see it pictured above. Structural edges mirror the OKF bundle's relationship taxonomy (route→controller, model→relationships/policy/observers, event→listeners); grouping edges connect an artifact to its declared domain/flow; reference edges connect it to a declared local ADR. The sidebar doubles as a color-coded legend and kind filter. graph.html is self-contained (no CDN dependencies) and opens straight from disk — no server needed. Entirely independent of necromancer:okf; refuses a stale or partial manifest by default; writes atomically.
php artisan necromancer:graph php artisan necromancer:graph --allow-stale --allow-partial php artisan necromancer:graph --output=dist/graph open necromancer-graph/graph.html # opens directly, no server needed
Three steps from zero to AI-readable codebase.
composer require --dev robertogallea/laravel-necromancer
The service provider is auto-discovered — no manual registration needed. Optionally publish the configuration:
php artisan vendor:publish --tag=necromancer-config
Build the manifest. Re-run whenever your application changes.
php artisan necromancer:scan
Check AI-readability and generate the Markdown context file.
php artisan necromancer:audit php artisan necromancer:generate
Add these steps to your pipeline to enforce manifest freshness and AI-readability quality gates.
- name: Check manifest is up to date run: php artisan necromancer:scan --diff --fail-on-drift - name: Fail on AI-readability errors run: php artisan necromancer:audit --fail-on=error - name: Enforce minimum AI readability score run: php artisan necromancer:doctor --min-score=80
When laravel/mcp is installed, Necromancer exposes the manifest as read-only tools via a
laravel-necromancer MCP server handle — auto-configured in .mcp.json on first run.
query_routes
List routes, optionally filtered by method or name/URI pattern.
query_models
List Eloquent models, optionally filtered by class name.
query_artifacts
List artifacts of any type, optionally filtered by JSON substring. Use when you know the artifact type.
search_artifacts
Full-text search across all artifact types. Use when you need to search across types.
{
"mcpServers": {
"laravel-necromancer": {
"command": "php",
"args": ["artisan", "mcp:start", "necromancer"]
}
}
}
Necromancer reads PHP 8 attributes as primary data sources alongside class properties. Codebases using the attribute-based API introduced in Laravel 11+ are fully supported.
| Subsystem | Attributes | Manifest fields |
|---|---|---|
| Routing |
#[Authorize]
|
authorization |
| Eloquent |
#[ObservedBy]
#[UsePolicy]
#[ScopedBy]
#[UseFactory]
#[UseEloquentBuilder]
#[Scope]
|
observers,
policy,
global_scopes,
factory,
custom_builder,
scopes
|
| Queue |
#[Queue]
#[Connection]
#[Tries]
#[Timeout]
#[Backoff]
#[MaxExceptions]
|
all job queue config fields |
| Console |
#[Aliases]
|
aliases |
| FormRequest |
#[StopOnFirstFailure]
#[ErrorBag]
|
stop_on_first_failure,
error_bag
|
| Universal annotations |
#[Necromancer]
|
annotations on every class-backed artifact, controllers (class default + action refinement), and middleware classes
|
| Exact-ID mappings |
config('necromancer.annotations')
|
annotations on closures, test files, gates, scheduled tasks, and registration-specific middleware — keyed by exact canonical Artifact ID, no wildcards
|
On Laravel 13.17+, Necromancer reads route metadata set via the framework's native
Route::metadata()
API, under a reserved necromancer
namespace — a compact, declared-by-the-developer semantic signal that takes priority over anything
Necromancer infers from naming. Entirely opt-in: apps that don't set route metadata, or that run
an older Laravel version, are unaffected.
The withNecromancer()
route macro declares it with named arguments — every field optional, and dropped when left unset:
Route::post('/billing/cancel', [SubscriptionController::class, 'cancel'])
->withNecromancer(
domain: 'billing',
flow: 'subscription-cancellation',
capability: 'subscription.cancel',
summary: 'Cancels an active subscription.',
risk: 'high',
externalServices: ['stripe'],
adrs: ['docs/adr/004-subscription-cancellation.md'],
);
The macro is registered on every routing surface, so a group — or every route a resource registers — can be tagged in one place. Routes inherit their group's fields, and a field declared on the route itself wins for that field:
Route::withNecromancer(domain: 'billing')->prefix('billing')->group(/* ... */);
Route::prefix('billing')->withNecromancer(domain: 'billing')->group(/* ... */);
Route::resource('posts', PostController::class)->withNecromancer(domain: 'blog');
Route::singleton('profile', ProfileController::class)->withNecromancer(domain: 'account');
It is a shorthand, never a parallel metadata system — the fields go straight into native route metadata under the configured namespace. The raw array form stays fully supported, and is what to use on Laravel < 13.17, where the macro throws because the framework has no route metadata to write to:
Route::post('/billing/cancel', [SubscriptionController::class, 'cancel'])
->metadata([
'necromancer' => [
'domain' => 'billing',
'risk' => 'high',
'external_services' => ['stripe'],
],
]);
.env or raw configuration values// config/necromancer.php
'exclude' => [
'routes' => ['horizon.*', 'telescope.*', 'debugbar.*'],
'models' => [],
'tests' => [],
],
2.0 removes the 1.x-only compatibility surfaces that eased the transition to universal Artifact Annotations
and Knowledge Bundles. Declaring annotations is unchanged — #[Necromancer],
withNecromancer(), and exact-ID config mappings all work as before.
What changes is what 2.0 stops reading, emitting, and accepting. Full details and a diff example are in the
README migration guide.
necromancer.json now treats a pre-1.5 manifest exactly like a missing one. Run php artisan necromancer:scan once after upgrading.route_metadata.necromancer no longer appears in the manifestannotations key. route_metadata.raw — native Route::getMetadata() output — is unaffected.--only=route-metadata-coverage no longer matches--only=artifact-annotation-coverage dimension key on necromancer:doctor instead.AN_LEGACY_VALUE/AN_LEGACY_RISK are now AN_SCHEMA_INCOMPATIBLE_VALUE/AN_SCHEMA_INCOMPATIBLE_RISK. The check is unchanged, only the name.adr parameter was removedwithNecromancer() and RouteMetadataFactory::forMetadata() only accept the plural adrs array now — swap adr: '...' for adrs: ['...'].