CLI

phpnomad/cli gives you two things: a way to understand your project and a way to generate code for it. It scans PHPNomad projects using AST parsing and produces structured JSONL index files that answer structural questions in a fraction of the tokens it would take to read raw source. It also scaffolds new PHP files from JSON recipe specifications and auto-registers them in your initializers.

The package has two major subsystems:

Five commands expose these subsystems:

Command What it does
phpnomad index Build the full JSONL index
phpnomad inspect:di Display boot sequence and DI bindings
phpnomad inspect:routes List REST routes with methods and capabilities
phpnomad context Generate a compact project context summary
phpnomad make Scaffold code from a recipe spec

Key ideas at a glance


High-level data flow

                         phpnomad/cli
                    +---------------------+
                    |                     |
  Project Files     |   AST Parsing       |     .phpnomad/
  (*.php)      ---->|   (nikic/php-parser) |---->  classes.jsonl
                    |         |           |        initializers.jsonl
                    |         v           |        applications.jsonl
                    |   Boot Sequence     |        controllers.jsonl
                    |   Walking           |        dependencies.jsonl
                    |         |           |        dependency-map.jsonl
                    |         v           |        dependents-map.jsonl
                    |   Analyzer          |        tables.jsonl
                    |   Pipeline          |        events.jsonl
                    |         |           |        commands.jsonl
                    |         v           |        facades.jsonl
                    |   Dependency        |        orphans.jsonl
                    |   Graph Builder     |        meta.json
                    +---------------------+

  Recipe Spec       +---------------------+
  (JSON)       ---->|   Scaffolder        |---->  Generated PHP files
                    |     |               |       + initializer mutations
                    |     v               |
                    |   Template Renderer |
                    |     |               |
                    |     v               |
                    |   Initializer       |
                    |   Mutator (AST)     |
                    +---------------------+

The indexer pipeline reads your project files, parses them into ASTs, walks the boot sequence, runs a series of analyzers, and writes everything to JSONL. The scaffolder reads a recipe spec, renders PHP templates, writes the output files, and patches your initializer to register the new class.


Installation

composer require phpnomad/cli --dev

This installs the phpnomad binary into vendor/bin/. You can run it directly:

vendor/bin/phpnomad index --path=.

For system-wide access, symlink the binary:

ln -s /path/to/phpnomad/cli/bin/phpnomad ~/.local/bin/phpnomad

Then from any project directory:

phpnomad index --path=.

Requirements


Why use this package

Token efficiency for AI agents

The primary motivation behind the CLI is token savings. Instead of reading PHP source files and grepping across a codebase, an AI agent can query a single JSONL file and get a precise, structured answer.

Here are benchmarks from a real project (Siren, 1,019 classes, 69 events, 27 tables):

Query Index size Raw source Savings
What depends on AllocateDistribution? 1.1 KB 75 KB 67x
What injects EventStrategy? 7 KB 394 KB 54x
All task handlers with task mappings 0.3 KB 37 KB 109x
Boot sequence + initializer contributions 10 KB 315 KB 32x
All DI bindings with resolution chains 7 KB 93 KB 13x
What implements DataModel? (36 classes) 3 KB 47 KB 14x
All events with IDs (69 events) 23 KB 73 KB 3x
All table schemas (27 tables) 19 KB 42 KB 2x
Unreferenced classes (50 orphans) 6 KB N/A impossible without index

At roughly 4 bytes per token, a reverse lookup on EventStrategy drops from ~98,000 tokens to ~1,750 tokens. The savings are largest for reverse lookups (what depends on X?) because without the index, the agent must grep and read every file in the project.

Structural understanding without reading source

The JSONL index gives you a complete structural map of your application. You can answer questions like "what initializers register controllers?" or "what classes have no relationships?" without opening a single PHP file.

Automated scaffolding with auto-registration

The make command generates new PHP files and automatically registers them in the correct initializer. If the initializer doesn't have the required method yet (for example, getListeners() for a new event listener), the scaffolder creates the method and adds the corresponding Has* interface. This saves you from the boilerplate of manually wiring new classes into the boot sequence.


When to use this package

Use phpnomad/cli when:

You don't need this package if your project doesn't use the PHPNomad Bootstrapper pattern. The indexer relies on tracing the Application -> Bootstrapper -> Initializer chain, so non-PHPNomad projects won't benefit from the index pipeline.


Design philosophy

Read-side (introspection) is prioritized over write-side (scaffolding). Introspection commands save tokens on every AI session by providing structured data about the project. Scaffolding saves tokens only when creating new code. Both matter, but introspection is the higher-leverage starting point.

JSONL over nested JSON. Each record type gets its own file with one JSON object per line. This lets agents grep for specific classes without parsing the entire index, and keeps token costs proportional to what's being queried. A single grep "PayoutDatastore" .phpnomad/dependents-map.jsonl answers "what depends on this interface?" without touching any other data.

Static analysis, not runtime reflection. The CLI parses PHP files via AST without executing them. No database connection, no bootstrap, no platform dependencies. It can index a WordPress plugin, a standalone app, or a test harness identically.


Quick examples

Build an index

phpnomad index --path=/path/to/project

This creates a .phpnomad/ directory with JSONL files covering all indexed data.

Inspect the boot sequence

phpnomad inspect:di --path=.
Application: Siren\SaaS\Application (saas/Application.php)

Boot sequence (74 initializers):
  #1   Core\Bootstrap\CoreInitializer (vendor)    1 binding
  #2   SaaS\SaaSInitializer                       10 bindings
  ...

Summary
  4 application(s)
  74 initializers
  244 bindings
  110 controllers

Scaffold a new event listener

phpnomad make --from=listener '{"name":"SendWelcomeEmail","event":"App\\Events\\UserCreated","initializer":"App\\AppInit"}'

This generates the listener PHP file, adds the listener registration to AppInit, and (if needed) adds the HasListeners interface and getListeners() method to the initializer class.


The index output

When you run phpnomad index, the CLI writes these files to .phpnomad/:

.phpnomad/
  meta.json              # Summary counts
  classes.jsonl          # One class per line (FQCN, interfaces, traits, constructor params)
  initializers.jsonl     # One initializer per line (bindings, controllers, listeners, commands)
  applications.jsonl     # One application per line (boot sequence, pre/post bindings)
  controllers.jsonl      # One controller per line (endpoint, method, capabilities)
  commands.jsonl         # One command per line (signature, description)
  dependencies.jsonl     # One dependency tree per line (recursive resolution chain)
  tables.jsonl           # One table per line (name, columns, types, foreign keys)
  events.jsonl           # One event per line (event ID, payload properties)
  graphql-types.jsonl    # One GraphQL type per line (SDL, resolvers)
  facades.jsonl          # One facade per line (proxied interface)
  task-handlers.jsonl    # One handler per line (task class, task ID)
  mutations.jsonl        # One mutation handler per line (actions, adapter info)
  dependency-map.jsonl   # What each class depends on (all relationship types)
  dependents-map.jsonl   # What depends on each class (reverse lookup)
  orphans.jsonl          # Classes with no relationships in either direction

Each line is a self-contained JSON object. The dependency-map and dependents-map cover all edge types: constructor injection, interface implementation, inheritance, trait usage, event listeners, task handlers, facade proxies, DI bindings, and mutation adapters.

See Indexer Output Format for the full field reference.


Package components

Indexer

The indexer pipeline is the core of the CLI. It walks your project's boot sequence, runs a chain of analyzers (ClassIndex, BootSequenceWalker, InitializerAnalyzer, ControllerAnalyzer, DependencyResolver, DependencyGraphBuilder, and others), and writes structured JSONL output.

See Indexer for the full pipeline documentation.

Scaffolder

The scaffolder takes JSON recipe specs, resolves variables against your project, renders PHP templates, and mutates initializers to register new classes. It supports built-in recipes for common patterns (listener, event, command, controller) and custom recipe files for more complex scaffolding.

See Scaffolder for recipe-driven code generation.

Commands

Five commands expose the indexer and scaffolder: index, inspect:di, inspect:routes, context, and make. Each accepts a --path flag pointing to the target project.

See Commands for the full command reference.


Relationship to other packages


Next steps