ToolGenerator
Generates the AI atom: a plain class carrying ONE `#[Milpa\ToolRuntime\Attributes\Tool]`-attributed method, targeting an EXISTING (or about-to-exist) plugin — the same `<Plugin> <Name>` shape {@see ControllerGenerator}/{@see EntityGenerator}/{@see ServiceGenerator} use. A freestanding tool class registers nothing on its own until a `Milpa\Interfaces\Tooling\ ToolProviderInterface::registerTools()` scans it into the tool registry, so — mirroring {@see ServiceGenerator::wireService()} exactly, one concern swapped for another (DI registration -> tool-registry scanning) — this generator ALSO wires the new tool into the target plugin: - No `PluginInterface` plugin exists yet at the target area's conventional path (`{appDir}/Plugins/{plugin}/{plugin}.php`) -> a minimal one is generated alongside the tool, already implementing `ToolProviderInterface` with a `registerTools()` that scans the new tool class via `Milpa\ToolRuntime\ToolScanner` — plus guidance to register the new plugin class in `config/plugins.php`. - One already exists AND carries the {@see \Milpa\DevTools\Make\Markers::TOOLS}/ {@see \Milpa\DevTools\Make\Markers::TOOL_PROMPTS} anchors (F1) -> the scan call and prompt-section entry are INSERTED at those markers via {@see \Milpa\DevTools\Make\MarkerInserter} — a deterministic splice at a known anchor, not a rewrite — see {@see self::wireToolProvider()}. - One already exists but carries NEITHER marker (a hand-written plugin, or one predating F1) -> it is NOT edited (parsing/rewriting arbitrary host PHP is exactly the fragile AST surgery this generator's deterministic `PlannedFile`/`WriteGuard` model exists to avoid). The exact `registerTools()`/`getPromptSections()` snippet to add by hand is returned via {@see GenerationResult::$guidance} instead. F4: `--needs=Fqcn[,Fqcn]` declares the tool's collaborators — the generated class gains a constructor promoting one `private readonly` property per FQCN (see {@see self::constructorBlock()}), and every wiring path that constructs the tool (the fresh `tool-plugin.runtime.php.stub`, a marker insertion, or the guidance snippet) resolves each one from the container instead of calling a zero-arg `new {Tool}()` — see {@see self::constructorCallArgs()}. Omitting `--needs` keeps the zero-arg form exactly as before. `milpa/tool-runtime` is a runtime-only dependency of the TARGET app being scaffolded, never of `milpa/devtools` itself (this package has no `#[Tool]`/`ToolScanner` coupling of its own, the same way the CONTROLLER path has zero Doctrine coupling — see {@see \Milpa\DevTools\Support\DoctrineAvailability}). When the target app does not appear to have it installed yet, {@see GenerationResult::$guidance} proactively says so instead of emitting a file the host cannot actually load — see {@see self::dependencyGuidance()}. Only a RUNTIME convention exists — see {@see generate()} for why LEGACY throws.
ToolGenerator::__construct()
public function __construct(Milpa\DevTools\Make\StubRenderer $renderer = new StubRenderer(), Milpa\DevTools\Make\ConventionDetector $detector = new ConventionDetector(), Milpa\DevTools\Make\MarkerInserter $markers = new MarkerInserter()):Parameters
| Name | Type | Description |
|---|---|---|
| $renderer | Milpa\DevTools\Make\StubRenderer | |
| $detector | Milpa\DevTools\Make\ConventionDetector | |
| $markers | Milpa\DevTools\Make\MarkerInserter |
ToolGenerator::name()
public function name(): stringThe `<what>` token this generator answers to: `'tool'`.
ToolGenerator::generate()
public function generate(Milpa\DevTools\Make\GenerationContext $context): Milpa\DevTools\Make\GenerationResultRenders the tool (+ provider wiring) per the detected/overridden {@see Flavor}.
Parameters
| Name | Type | Description |
|---|---|---|
| $context | Milpa\DevTools\Make\GenerationContext |
Throws
\RuntimeException When the detected/forced flavor is {@see Flavor::Legacy} — see
{@see generateLegacy()}.