Last week I was improving mops, my open-source CLI for working with MPS projects. The most important addition was ‘code mode’: the ability to execute Groovy scripts in the context of an MPS project.
Here is an example Groovy script that returns the names of all Java classes in a project:
def conceptName = 'jetbrains.mps.baseLanguage.ClassConcept'
return project.read {
def concept = project.concept(conceptName)
def names = []
mops.search.eachInstanceOf(concept, project.scope) { node ->
names << node.properties['name']
}
return names
}
Note project.read method for executing code in a read action, project.concept for looking up concepts by name, as
well as mops.search.eachInstanceOf for executing an instance search.
And here is how you execute it, say, against MPS-extensions:
mops --project-root=code --mps-home='.../mps/2025.1.3' --java-home='.../jbr' code run .../mops/examples/concept-names.groovy
Since it is tedious to figure out the right --mps-home and --java-home parameters, I have also implemented a
mops wrapper subcommand which can guess their values for common Gradle builds and write a wrapper script for launching
mops in the context of the project.
I think the Groovy scripting interface is going to become more important in the future. For agents, Groovy is a good fit
because they are already quite adept at writing it, and editing models from Groovy is likely to be easier than
alternatives. For humans, it could provide an easier-to-run alternative to MPS Console scripts and a convenient way to
export data. Groovy also makes it easy to add language-specific functionality and helps keep the script readable (as
illustrated by the direct access to the name property above).
I am still iterating on both the functionality and the best way to expose it. I have added Java parsing (under
mops.parsing.java) and build script reloading (mops.editing.build.reloadModulesFromDisk). I want it to be easy to
add further extensions, such as parsing other languages (KernelF, or even structure and editor languages), or executing
an intention.
These changes were motivated by trying to get mops to a state where it could autonomously reproduce an issue reported
against MPS-extensions and write an automated test for it. I am of course discovering bugs along the way and fixing
them. (Did you know that headless MPS will save your models only when explicitly asked, and will not save the
project’s list of modules even if explicitly asked? Well, I know now.) I am reaching the point where the tools are in
place and my focus will have to shift to helping agents use them, which means I am going to turn to skill writing at
some point in the near future.
I must admit, I am not entirely happy with the code quality of mops because I am letting agents (Codex/Astra these days)
write most of it. It does have tests, but some are of questionable utility. The code is not modularized the way I would
like, and despite having prompted most of the code into existence, I am not that familiar with it. I am however
deliberately trading off code quality for exploration at the moment (also known as taking on technical and cognitive
debt). After we reach AGI the state where mops can write editor tests on its own, I plan to pause and clean things
up.
I will be talking about mops at LangDev (confirmed) and at the MPS Community Meetup (pending acceptance). Let me know if you are going to attend one of these events and want to meet for a chat.