Skip to content

Add Java as a supported output language#101

Merged
kalil0321 merged 5 commits into
kalil0321:mainfrom
jarsh921:add-java-output-language
Jul 22, 2026
Merged

Add Java as a supported output language#101
kalil0321 merged 5 commits into
kalil0321:mainfrom
jarsh921:add-java-output-language

Conversation

@jarsh921

@jarsh921 jarsh921 commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

Follows the exact same pattern as python/javascript/typescript(/go):

  • New prompt partial (prompts/partials/_language_java.md): uses java.net.http.HttpClient (built into the JDK since 11, no HTTP library dependency) and Gson for JSON (the JDK has no built-in JSON support), packaged as a minimal Maven project with exec-maven-plugin so it runs with a single command. Same auth-hardcoding/refresh guidance as the other languages. Also calls out a Java-specific naming detail: the generated file is named api_client.java (this generator's lowercase convention), so the top-level class is named ApiClient but declared package-private (no public modifier) - a public class's filename must exactly match its class name, and a package-private one doesn't have that restriction while compiling and running identically.
  • base_engineer.py: added "java" to _OUTPUT_LANGUAGE_EXTENSIONS (.java), _get_language_name() ("Java"), _get_run_command() ("mvn -q compile exec:java"), and _get_auto_output_files() (pom.xml, always included for Java - unlike Go/JS's conditional dependency files, Gson is a hard requirement here since there's no stdlib JSON alternative, matching typescript's "always" precedent instead).
  • cli.py: added Java to the interactive output_language config picker.
  • Updated the three docstrings/comments that listed the language set (config.py, engineer.py, prompts/init.py) and README.md's output language line.
  • Tests: mirrored the existing per-language test pattern in test_base_engineer.py (extension, run command, prompt content) and test_prompts.py (partial loading) for Java.

Caught and fixed one real bug before this counts as done: my first draft of the partial included a literal { ... } in prose describing the class declaration, which crashed load()'s str.format_map() with a KeyError (it treats any {...} in the template as a placeholder to substitute, not just the documented {run_command}-style ones) - confirmed against the other partials that none of them contain a literal brace anywhere, for the same reason. Rephrased to avoid it.

Full test suite passes (49/49 in the touched files, run 3x to confirm) except one pre-existing flaky test unrelated to this change (test_existing_client_language_falls_back_to_newest_file - a mtime-resolution race condition; confirmed it fails intermittently on a clean main checkout with none of these changes too, ~40% of runs in this environment). ruff check is clean on every file this touches (8 pre-existing lint errors elsewhere on main, none in the touched lines).


Summary by cubic

Adds Java as a supported output language. Generates a minimal Maven project using java.net.http.HttpClient and Gson, runnable with mvn -q -f path/to/pom.xml compile exec:exec.

  • New Features

    • Add output_language: "java" to settings and CLI.
    • New Java partial; generates api_client.java, README.md, and pom.xml with Gson and exec-maven-plugin configured to run via exec:exec.
    • Language mapping for .java and name "Java". Docs and tests updated.
  • Bug Fixes

    • Make Maven runs work from any cwd by passing -f the run’s pom.xml (absolute and shlex.quote-escaped; resolves relative scripts_dir).
    • Compile root-level source by setting <sourceDirectory> to . in the POM.
    • Use exec:exec instead of exec:java to run the package-private ApiClient reliably across platforms.
    • Remove a {...} literal from the Java partial to avoid str.format_map KeyError.

Written for commit 1a641bb. Summary will update on new commits.

Review in cubic

Greptile Summary

This PR adds Java as a supported output language. The main changes are:

  • Java client generation using JDK HttpClient and Gson.
  • A minimal Maven project with root-level source compilation.
  • Java language selection, file naming, and run-command support.
  • Updated documentation and Java-specific tests.

Confidence Score: 5/5

This looks safe to merge.

  • The Maven command now targets the generated POM from any working directory.
  • Relative paths and shell-sensitive output paths are handled safely.
  • The generated Maven project is instructed to compile the root-level Java source.
  • No blocking issues were found in the updated code.

Important Files Changed

Filename Overview
src/reverse_api/base_engineer.py Adds Java metadata, Maven output files, and an absolute shell-quoted Maven command.
src/reverse_api/prompts/partials/_language_java.md Adds Java generation instructions with Gson, HttpClient, and root-level Maven source compilation.
src/reverse_api/cli.py Adds Java to the interactive output-language setting.
tests/test_base_engineer.py Adds coverage for Java extensions, prompts, and Maven command path handling.
tests/test_prompts.py Adds coverage for loading and formatting the Java prompt partial.

Reviews (2): Last reviewed commit: "Resolve scripts_dir to an absolute path ..." | Re-trigger Greptile

Comment thread src/reverse_api/prompts/partials/_language_java.md
Comment thread src/reverse_api/prompts/partials/_language_java.md Outdated
Comment thread src/reverse_api/cli.py
Choice(title="python", value="python"),
Choice(title="javascript", value="javascript"),
Choice(title="typescript", value="typescript"),
Choice(title="java", value="java"),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Java Runs Remain Python-Only

Selecting Java creates api_client.java, but the existing run workflow discovers only .py files and launches selected files with the virtualenv Python interpreter. A Java run is therefore omitted from run --ls, reports that no Python scripts exist, or produces a Python syntax error when passed explicitly.

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/reverse_api/cli.py
Line: 1086

Comment:
**Java Runs Remain Python-Only**

Selecting Java creates `api_client.java`, but the existing `run` workflow discovers only `.py` files and launches selected files with the virtualenv Python interpreter. A Java run is therefore omitted from `run --ls`, reports that no Python scripts exist, or produces a Python syntax error when passed explicitly.

How can I resolve this? If you propose a fix, please make it concise.

@kind-agent

kind-agent Bot commented Jul 21, 2026

Copy link
Copy Markdown

⚠️ Error — The test run failed unexpectedly.

Snapshot kind-sandbox is inactive

This is likely a transient issue. You can re-trigger a run from the dashboard.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed across 10 files

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread src/reverse_api/prompts/partials/_language_java.md Outdated
Comment thread src/reverse_api/base_engineer.py Outdated
@kalil0321

Copy link
Copy Markdown
Owner

@greptile review

Joshua Dunbar added 4 commits July 22, 2026 12:26
Follows the exact same pattern as python/javascript/typescript(/go):

- New prompt partial (prompts/partials/_language_java.md): uses
  java.net.http.HttpClient (built into the JDK since 11, no HTTP
  library dependency) and Gson for JSON (the JDK has no built-in JSON
  support), packaged as a minimal Maven project with exec-maven-plugin
  so it runs with a single command. Same auth-hardcoding/refresh
  guidance as the other languages. Also calls out a Java-specific
  naming detail: the generated file is named api_client.java (this
  generator's lowercase convention), so the top-level class is named
  ApiClient but declared package-private (no `public` modifier) -
  a public class's filename must exactly match its class name, and
  a package-private one doesn't have that restriction while compiling
  and running identically.
- base_engineer.py: added "java" to _OUTPUT_LANGUAGE_EXTENSIONS
  (.java), _get_language_name() ("Java"), _get_run_command() ("mvn -q
  compile exec:java"), and _get_auto_output_files() (pom.xml, always
  included for Java - unlike Go/JS's conditional dependency files,
  Gson is a hard requirement here since there's no stdlib JSON
  alternative, matching typescript's "always" precedent instead).
- cli.py: added Java to the interactive `output_language` config
  picker.
- Updated the three docstrings/comments that listed the language set
  (config.py, engineer.py, prompts/__init__.py) and README.md's output
  language line.
- Tests: mirrored the existing per-language test pattern in
  test_base_engineer.py (extension, run command, prompt content) and
  test_prompts.py (partial loading) for Java.

Caught and fixed one real bug before this counts as done: my first
draft of the partial included a literal `{ ... }` in prose describing
the class declaration, which crashed load()'s str.format_map() with
a KeyError (it treats any `{...}` in the template as a placeholder to
substitute, not just the documented `{run_command}`-style ones) -
confirmed against the other partials that none of them contain a
literal brace anywhere, for the same reason. Rephrased to avoid it.

Full test suite passes (49/49 in the touched files, run 3x to
confirm) except one pre-existing flaky test unrelated to this change
(test_existing_client_language_falls_back_to_newest_file - a
mtime-resolution race condition; confirmed it fails intermittently
on a clean main checkout with none of these changes too, ~40% of
runs in this environment). ruff check is clean on every file this
touches (8 pre-existing lint errors elsewhere on main, none in the
touched lines).
Two real bugs caught by review bots (greptile-apps) on the PR, both
verified against the actual code before fixing:

1. Maven only compiles src/main/java by default, but the partial saved
   api_client.java at the project root alongside pom.xml. mvn compile
   would silently skip it, and exec:java would fail to find ApiClient.
   Fixed by telling the generated POM to override <sourceDirectory> to
   the project root.

2. The agent's cwd for the whole session is scripts_dir.parent.parent
   (confirmed in engineer.py's analyze_and_generate -> ClaudeAgentOptions),
   not scripts_dir itself, while pom.xml is saved under scripts_dir.
   python/node/npx's run commands share this same cwd gap but take a
   bare relative filename either way, so it doesn't matter for them in
   practice (that's why this went unnoticed for the existing three
   languages) - but Maven hard-fails immediately with no upward search
   if invoked from a directory with no pom.xml, a sharper failure mode.
   _get_run_command() now points -f explicitly at this run's own
   pom.xml path for Java specifically, removing the ambiguity rather
   than relying on the agent to cd there itself first.

Updated test_get_run_command_java to match the new (scripts_dir-
dependent, no longer a static string) return value.

A third bot finding (cli.py's `run <id> --file` subcommand only
discovers *.py scripts and always launches them with the shared venv's
python interpreter) is real but pre-existing and out of scope here -
confirmed discover_scripts() in utils.py has been Python-only since
before javascript/typescript were added, so a Java run is no worse off
than an existing JS/TS one already is. Multi-language support for that
separate subcommand looks like its own follow-up, not something to
fold into a single-language-addition PR.

Full test suite passes (49/49, run 2x) except the same pre-existing
flaky test noted in the other language PRs. ruff check clean on the
touched lines (2 pre-existing unused-import warnings elsewhere in
test_base_engineer.py, unrelated).
Same class of bug caught by review bots on the PHP PR, applied here
proactively: _get_run_command()'s "-f \"{path}/pom.xml\"" only looked
safe - manual double-quoting doesn't stop $()/backtick command
substitution inside a double-quoted shell string. Replaced with
shlex.quote(), which correctly single-quotes the whole path when
metacharacters are present. Added a test with a deliberately hostile
path (embedded $(rm -rf ~)) asserting the command round-trips through
shlex.split() back to the literal path.

Full test suite passes (50/50, run 2x) except the same pre-existing
flaky test noted on the other language PRs.
Same root cause cubic flagged on the C# and Ruby PRs: a relative
--output-dir leaves scripts_dir relative to the original cwd, but once
the agent's cwd moves to scripts_dir.parent.parent (see
analyze_and_generate), embedding that same relative string in -f
re-interprets it from the new location and points Maven at a doubly-
nested, nonexistent pom.xml. Resolve scripts_dir before interpolating
it, proactively applying the same fix here before it gets flagged.
@kalil0321
kalil0321 force-pushed the add-java-output-language branch from c87b55b to 8e0765c Compare July 22, 2026 10:34
exec:java invokes main() reflectively in-process, which fails on the
package-private ApiClient class this generator requires (a public class
would have to match the api_client.java filename). exec:exec spawns a
real java process instead, and its <classpath/> element expands with the
platform-correct separator on Windows/macOS/Linux. The partial now pins
the exact known-good plugin configuration so the agent doesn't have to
rediscover this incompatibility during its test attempts.
@kalil0321
kalil0321 merged commit d074bab into kalil0321:main Jul 22, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants