net.ladenthin:srcmorph-cli is a standalone command-line front end for
srcmorph: a single java -jar invocation driven by one JSON or YAML configuration
file, with no Maven project of your own required. It wraps the
srcmorph core library's engines exactly the way the
Maven plugin does, following the
BitcoinAddressFinder cli.Main pattern.
srcmorph publishes no runnable jar. The srcmorph-cli jar on Maven Central is the CLI without its
dependencies; the llama natives it needs differ per OS, CPU and GPU, and no set of pre-built jars
covers every combination. You build the runnable jar for your machine with Maven, from
../examples/build-your-own-jar/:
cd examples/build-your-own-jar
./build.sh # build.bat on Windows
java -jar target/srcmorph.jar <path-to-config.json-or-.yaml>Its pom.xml takes srcmorph-cli from Maven Central, swaps the CPU natives of every desktop
platform (llama-platform) for the one natives jar of your OS/arch (picked by a Maven profile), and
packs everything into target/srcmorph.jar together with slf4j-simple defaults (INFO, stdout,
timestamps). A GPU module of the same OS/arch is one more property, for example
./build.sh -Dgpu=cuda13-linux-x86-64 or build.bat -Dgpu=vulkan-windows-x86-64; ggml loads it when
the vendor runtime is installed and runs on the CPU otherwise. The classifiers are listed in the
java-llama.cpp README, Choosing the natives
jars. A walkthrough for a
Windows laptop is in ../docs/windows-intel-core-ultra.md.
JBang runs the CLI without a build (srcmorph-cli's jar names its main
class): jbang net.ladenthin:srcmorph-cli:<version> <config>, with a GPU module as
--deps net.ladenthin:llama:<llama-version>:<classifier>. Maven is the supported path.
Releases up to 1.3.0 attached pre-built fat jars to the GitHub Release; from 1.4.0 there are none.
Both .json/.js (parsed via a
Jackson ObjectMapper) and .yaml/.yml (parsed via YAMLMapper) are supported — pick whichever you
prefer; both mappers are configured strictly (FAIL_ON_UNKNOWN_PROPERTIES), so a typo'd key fails the
run immediately rather than being silently ignored. On startup the CLI logs the parsed configuration
back to you (re-serialized as both JSON and YAML) so you can confirm exactly what will run before any
model loads or file is written.
See ../examples/ for a complete, ready-to-run set of config_*.json/.yaml files
(all using the mock provider, so they run with no GGUF model on disk) plus paired
run_*.sh/run_*.bat launcher scripts and an example simplelogger.properties.
The root object is net.ladenthin.srcmorph.cli.configuration.CConfiguration — a plain, public-field
JavaBean (Jackson binds it directly, no getters/setters needed):
public class CConfiguration {
public CCommand command = CCommand.Plan;
public SrcMorphConfiguration srcMorph = new SrcMorphConfiguration();
}command— one of the sixCCommandvalues below. Defaults toPlan— the safe default: no command means no model load and nothing written.srcMorph— the samenet.ladenthin.srcmorph.config.SrcMorphConfigurationobject the Maven plugin's mojos build from their own@Parameterfields. Every key undersrcMorphin your JSON/YAML file reads identically to the matching Maven<configuration>XML element —baseDirectory,outputDirectory,subtrees,excludes,fileExtensions,generationProvider,promptDefinitions,aiDefinitions,modelDirectory,fieldGenerations,factDefinitions, thellama*fallback parameters,force,planOnly,projectName. A relativemodelDirectory(like every relative path here) is relative to the working directory the CLI is started in. If you already know the plugin's XML shape, you already know this shape — see../srcmorph-maven-plugin/README.md's "Configuration" section for the full field-by-field reference (routing-rule conditions, oversize strategies, per-model sampling parameters, etc.), which applies verbatim here.
| Command | What it does |
|---|---|
Plan |
Runs GenerateEngine with planOnly forced to true, regardless of what the file says — builds and logs the routing plan (which model + prompt each file would get) and stops. No model is loaded, nothing is written. The default; always safe to run. |
GenerateFileIndex |
Phase 1, exactly as configured: indexes source files and fills in their AI-generated summaries (GenerateEngine). |
AggregatePackages |
Phase 2: aggregates per-package .ai.md index files (AggregatePackagesEngine). |
AggregateProject |
Phase 3: aggregates the single project-level .ai.md index (AggregateProjectEngine). |
All |
Runs GenerateFileIndex, then AggregatePackages, then AggregateProject in order, stopping at the first phase that fails. |
Calibrate |
Loads each distinct model your fieldGenerations would route to, measures its prefill/decode throughput, and prints a paste-ready <calibration> XML block per model to standard output (CalibrateEngine). |
Unlike the Maven plugin, where each goal execution gets its own <fieldGenerations> block in the
POM, the CLI's All and Calibrate commands reuse the one srcMorph.fieldGenerations list across
every engine they run. That list means different things to different engines:
GenerateFileIndexroutes: each file is matched to exactly one rule bycondition+priority(the explicitfallbackcatches anything unmatched).AggregatePackagesdoes not route by condition — it applies every entry infieldGenerationsto every package, in list order, and the last entry's output is kept.AggregateProject's optional AI overview paragraph uses onlyfieldGenerations[0].
If your fieldGenerations list has more than one entry (e.g. a conditioned Java-file rule plus a
fallback, as in ../examples/config_All.json), running All will
still aggregate every package using the entire list's rules layered on top of each other (last one
wins) — which is usually fine (the rules typically write similar structured Markdown), but is worth
knowing if you see a package's body reflecting your fallback rule's prompt rather than the
file-specific one. A single fallback-only rule sidesteps the question entirely and behaves identically
across all three phases. See ../srcmorph/README.md for the engine-level
detail.
MainTest— unit tests for the extension-dispatched loader and the JSON/YAML mappers.configuration.ConfigBindingTest— round-trips a private ad-hocsrc/test/resources/test-fixtures/minimal-generate.{json,yaml}fixture pair.ExamplesConfigBindingTest— sweeps every shipped../examples/config_*.{json,yaml}file (the public, documented examples) through the same strict mappers, so an example can never silently drift out of sync withSrcMorphConfiguration's field set.CliEndToEndTest— drivesMain#run()directly (no forked process) against themockprovider for theAllandPlancommands, asserting the expected.ai.mdtree lands on disk (or, forPlan, that nothing is written and the original configuration object is never mutated).CliArchitectureTest— ArchUnit rules: this module is the reactor's leaf-most consumer, no public mutable fields outsideconfiguration, noSystem.exit, no dependency on the Maven Plugin API.
mvn -pl srcmorph-cli -am packageproduces srcmorph-cli/target/srcmorph-cli-<version>.jar, the plain jar that goes to Maven Central.
To run a local build, install the reactor (mvn install) and build
examples/build-your-own-jar with -Dsrcmorph.version=<the reactor version>; CI's smoke-jar job
does exactly that.