v3.1.1
Install
pip install codeanalyzer-java==3.1.1 # bundles a JVM; installs the canjv launcherOr the jar with a codeanalyzer launcher (requires Java 11+):
curl --proto '=https' --tlsv1.2 -LsSf https://github.com/codellm-devkit/codeanalyzer-java/releases/download/v3.1.1/codeanalyzer-installer.sh | shOr run the JAR directly (requires Java 11+):
# JSON output, at the analysis level you ask for
java -jar codeanalyzer.jar -i /path/to/project -a 4 -o ./out # writes out/analysis.json
# Neo4j projection - always full depth, so it takes no --analysis-level
java -jar codeanalyzer.jar -i /path/to/project --emit neo4j -o ./out # writes out/graph.cypherDownloads
| Asset | Description |
|---|---|
codeanalyzer.jar |
Self-contained analyzer (run with java -jar) |
codeanalyzer-installer.sh |
Installer that fetches the jar and adds a codeanalyzer launcher |
schema.neo4j.json |
Neo4j graph schema contract (node labels, relationships, DDL) |
📦 Other Changes
- docs: the PyPI install needs a real JDK from -a 2 onwards
- PR: #241
- feat(cli): --strict fails a run whose analysis degraded
- PR: #242
- chore(release): 3.1.1
- PR: #243
- feat(id): can:///java/… — the app becomes the outermost segment, and the graph root gets its id
- PR: #247
- docs(plan): can:///java/… identity migration
- PR: #245
- feat(ids): @external drops the language segment; document the polyglot rule
- PR: #248
⚠️ Breaking: the can:// id grammar changed
Every durable id moved. The application is now the outermost segment:
before can://java/<app>/<file>/<type>/<signature>
after can://<app>/java/<file>/<type>/<signature>
before can://artifact/<app>/<path> after can://<app>/artifact/<path>
before can://java/<app>/@external/<type>/<sig> after can://<app>/@external/<type>/<sig>
@external and artifact deliberately omit the language segment: they are language-neutral shared merge targets, so codeanalyzer-python and codeanalyzer-typescript mint byte-identical ids for the same file or library symbol. This release converges Java onto the shape those two already use.
schema_version remains 2.0.0, so this change is NOT detectable from the payload. A stored analysis.json or Neo4j graph written by 3.1.0 or earlier holds old-shape ids that will no longer join against 3.1.1 output. The analyzer version is the only signal. If you have persisted ids, re-analyze.
Neo4j: existing databases need one upgrade push, and it is handled. :JApplication now merges on its can://<app> id rather than the free-text --app-name, and the legacy j_application_name uniqueness constraint is dropped before any load — without that, the first push against a pre-3.1.1 database would fail with ConstraintValidationFailed. Verified end to end against Neo4j 5.
The Cypher wipe now covers the whole can://<app>/ prefix, which brings :Artifact and :ConfigKey inside it. They are rebuilt by every snapshot instead of accumulating forever. One deliberate consequence: a cross-language edge into a shared :Artifact is dropped by one analyzer's snapshot and restored on the other's next push. :Package (pkg: purls) sits under no application and is never wiped.
What this does not do: the app id is derived from --app-name, so two services analyzed under the same name still merge onto one root. Give each service its own --app-name.
New: --strict
A degraded run — the RTA overlay or the L4 semantic ddg unavailable — previously exited 0 with only a WARN on stderr. --strict turns any such degradation into a non-zero exit that names what was lost, and writes no analysis.json, so a pipeline cannot mistake a thin payload for a complete one. Opt-in: degrading remains a supported mode.
Relevant if you install from PyPI: the bundled jdk4py runtime is a JRE with no javac, so -a 2 and above need a real JDK on JAVA_HOME. Without one, RTA and the semantic ddg silently drop — which is exactly what --strict surfaces.