.agents/skills/micronaut-sourcegen/SKILL.md
Use this skill when a Micronaut module wants to generate types with Micronaut Sourcegen instead of handwritten source strings, reflection, JavaPoet/KotlinPoet calls in visitors, or ad hoc bytecode. The target module may emit Java source, Kotlin source, Groovy-compatible source, or bytecode, but the generation logic should model the type once with io.micronaut.sourcegen.model.
Help other modules adopt Micronaut Sourcegen safely: choose the right backend, wire the right processor dependencies, build language-neutral model definitions, and verify generated Java/Kotlin/bytecode behavior with targeted tests.
Should trigger:
ClassDef and MethodDef."Should not trigger:
SourceGenerator.write(...) or bytecode writer APIs as appropriate.META-INF/services/io.micronaut.inject.visitor.TypeElementVisitor.annotationProcessor.ksp.sourcegen-generator-java when the module should emit .java source for VisitorContext.Language.JAVA; it also provides GroovyPoetSourceGenerator for GROOVY.sourcegen-generator-kotlin when Kotlin/KSP processing should emit .kt source for VisitorContext.Language.KOTLIN.sourcegen-generator-bytecode when Java-language processing should emit .class files directly through ByteCodeGenerator.VisitorContext.Language.JAVA, and SourceGenerators.findByLanguage(JAVA) expects a single effective backend.sourcegen-bytecode-writer directly only when implementing or testing bytecode writer behavior, not as the normal integration point for consuming annotation processors.sourcegen-generator for the model and service lookup APIs. Add sourcegen-annotations only when the module consumes Sourcegen annotations such as builders/wither support.annotationProcessor.ksp.sourcegen-generator-bytecode to annotationProcessor.Example repository-local shapes:
dependencies {
implementation(projects.sourcegenGenerator)
annotationProcessor(projects.sourcegenGeneratorJava)
annotationProcessor(projects.myCustomGenerators)
}
dependencies {
ksp(projects.sourcegenGeneratorKotlin)
ksp(projects.myCustomGeneratorsKotlin)
}
dependencies {
annotationProcessor(projects.sourcegenGeneratorBytecode)
annotationProcessor(projects.myCustomGenerators)
}
ClassDef.builder, InterfaceDef.builder, RecordDef.builder, EnumDef.builder, AnnotationObjectDef.builder, MethodDef.builder, MethodDef.constructor, FieldDef.builder, PropertyDef.builder, ParameterDef.builder, TypeDef.of, ClassTypeDef.of, TypeDef.parameterized, ExpressionDef.constant, StatementDef.multi, StatementDef.doTry, expr.returning, expr.invoke, type.invokeStatic, type.getStaticField, and switch/if helper methods.new ClassTypeDef.Parameterized(...), new TypeDef.Array(...), new ExpressionDef.Constant(...), new StatementDef.Switch(...), or other new ...record(...) forms.TypeElementVisitor with VisitorKind.ISOLATING when generated output depends only on the visited element.SourceGenerators.findByLanguage(context.getLanguage()).orElse(null) and return when none is available.ObjectDef instances from Micronaut AST metadata (ClassElement, MethodElement, FieldElement, PropertyElement) when possible.sourceGenerator.write(objectDef, context, originatingElement).SourceGenerators.handleFatalException(...) when generation may postpone to a later round.Minimal visitor shape:
SourceGenerator sourceGenerator = SourceGenerators.findByLanguage(context.getLanguage()).orElse(null);
if (sourceGenerator == null) {
return;
}
ClassDef generated = ClassDef.builder(element.getPackageName() + ".Generated")
.addModifiers(Modifier.PUBLIC, Modifier.FINAL)
.addMethod(MethodDef.builder("name")
.addModifiers(Modifier.PUBLIC)
.returns(TypeDef.STRING)
.build((aThis, params) -> ExpressionDef.constant("generated").returning()))
.build();
sourceGenerator.write(generated, context, element);
ClassDef, InterfaceDef, RecordDef, EnumDef, and AnnotationObjectDef for generated type kind.TypeDef and ClassTypeDef for primitives, arrays, generics, generated class names, nullability, and static field owners.MethodDef body builders for executable logic. Prefer explicit returns(...) for public or non-trivial generated methods.FieldDef instance and reference it with aThis.field(fieldDef) or generatedType.getStaticField(fieldDef) instead of repeating the field name and type.MethodDef instance and invoke it with receiver.invoke(methodDef, ...) or generatedType.invokeStatic(methodDef, ...) instead of repeating the method name, parameter types, and return type.ReflectionUtils.getRequiredMethod(...) or ReflectionUtils.getRequiredField(...). Pass method handles to receiver.invoke(method, ...) or type.invokeStatic(method, ...); pass static field handles to type.getStaticField(field); for instance fields, centralize the field-name/type bridge in a helper fed by the reflective Field. This uses reflection only at generation time to describe a known member; it must not generate runtime reflection.StatementDef.multi(...), expr.returning(), expr.newLocal(...), condition.doIf(...), expr.asExpressionSwitch(...), expr.asStatementSwitch(...), StatementDef.doTry(...), and invocation helpers to compose bodies.ClassTypeDef.getStaticField("NAME", valueType) for enum constants, static constants, class literals, and static-field arguments passed to invocations.TypeDef.STRING switch expression and ExpressionDef.constant("case") keys with asExpressionSwitch(...) or asStatementSwitch(...)..java behavior through Java test-suite tests.AGENTS.md: affected compile tasks, targeted tests, full affected module tests, ./gradlew -q cM, and ./gradlew -q spotlessCheck if new files were added.JavaPoetSourceGenerator writes Java source for VisitorContext.Language.JAVA.GroovyPoetSourceGenerator reuses the Java source generator for GROOVY.KotlinPoetSourceGenerator writes Kotlin source for VisitorContext.Language.KOTLIN.ByteCodeGenerator advertises JAVA and writes class files through context.visitClass(...); it does not support write(ObjectDef, Writer).Use references/sourcegen-cookbook.md for adoption patterns covering:
FieldDef or MethodDef instance when reading/writing generated fields or invoking generated methods.ReflectionUtils.getRequiredMethod(...) or ReflectionUtils.getRequiredField(...) handle can describe the member safely.JAVA Sourcegen backends on the same annotation-processor path unless selection is explicit and tested.name: frontmatter are micronaut-sourcegen.JAVA Sourcegen backend is present for a Java compilation path.FieldDef/MethodDef instances where available.SourceGenerator.write(...) or backend writer APIs where appropriate.references/sourcegen-cookbook.md