Skip to content

Refine JabKit CLI: positional input argument and check command group#15759

Merged
koppor merged 32 commits into
mainfrom
refine-jabkit
May 19, 2026
Merged

Refine JabKit CLI: positional input argument and check command group#15759
koppor merged 32 commits into
mainfrom
refine-jabkit

Conversation

@koppor

@koppor koppor commented May 18, 2026

Copy link
Copy Markdown
Member

Related issues and pull requests

No related issue — CLI ergonomics refinement.

PR Description

This PR refines the jabkit CLI: subcommands now accept the input file as a positional argument (with --input kept as an alias), the consistency and integrity checks are grouped under a new check command, and jabkit check consistency gains an errorformat output format that is now the default for both check subcommands. The intent is a more intuitive command-line experience while keeping backward compatibility via the --input alias.

All in all, this moves towards lint, but this can be a next step to do this cleanly. For now, I think, check is a good thing?!

before

$ mise exec java@temurin-25 -- jbang /c/git-repositories/jabref-all/jabref-worktree-3/.jbang/JabKitLauncher.java check-integrity --input=x.bib
Importing x.bib
Checking integrity of 'x.bib'.
x:1:7: Citation key deviates from generated key Lauwers2025
x:15:3: capital letters are not masked using curly brackets {}

after

$ jbang /c/git-repositories/jabref-all/jabref-worktree-2/.jbang/JabKitLauncher.java check integrity x.bib
Importing x.bib
Checking integrity of 'x.bib'.
x.bib:1:7:TOSCA20: Citation key deviates from generated key Lauwers2025
x.bib:15:3:Aryan2025:title: capital letters are not masked using curly brackets {}

Steps to test

  1. Build jabkit and run jabkit check integrity references.bib — the input file is accepted positionally.
  2. Run jabkit check consistency references.bib — output is emitted in errorformat (file:line:column: message).
  3. Verify --input references.bib still works as an alias for the positional argument.

AI usage

Claude Code (model claude-opus-4-7).

Checklist

  • I own the copyright of the code submitted and I license it under the MIT license
  • If AI tools were used, I disclosed them in the "AI usage" section and reviewed, understood, and take full ownership of all AI-generated code
  • I manually tested my changes in running JabRef (always required)
  • I added JUnit tests for changes (if applicable)
  • [/] I added screenshots in the PR description (if change is visible to the user)
  • [/] I added a screenshot in the PR description showing a library with a single entry with me as author and as title the issue number
  • I described the change in CHANGELOG.md in a way that can be understood by the average user (if change is visible to the user)
  • [/] I checked the user documentation for up to dateness and submitted a pull request to our user documentation repository

koppor and others added 4 commits May 18, 2026 15:53
Group the consistency and integrity checks under a new `check` parent
command (`jabkit check consistency`, `jabkit check integrity`) and default
`check integrity` to the txt output format.

Accept the input file as a positional argument on every input-taking
subcommand via a shared InputOption mixin; `--input` is kept as an alias.
This supersedes ADR-0045 (ADR-0057) for increased convenience while
preserving cross-command consistency.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Add a BibliographyConsistencyCheckResultErrorFormatWriter producing
line-oriented `file:line:column: message` output, mirroring the integrity
check's errorformat writer. One line is emitted per deviating field of a
reported entry.

Make `errorformat` the default output format for both `jabkit check`
subcommands.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
koppor added a commit that referenced this pull request May 18, 2026
Move JabKit CLI changelog entries to "Added" and link PR #15759

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
koppor and others added 6 commits May 18, 2026 22:57
jabkit check <file> now runs the consistency and integrity checks together.
The shared check logic is extracted into static execute() methods so the
parent check command and the consistency/integrity subcommands reuse it.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Replace the "errorformat"/"txt"/"csv" string literals in the `check`
commands with `FORMAT_*` constants on `Check`.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Align the `?`/`:` operators under the first operand to match the JabRef
code style.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@koppor

koppor commented May 18, 2026

Copy link
Copy Markdown
Member Author

@InAnYan I thought too complicated regarding the JBang tests - with some more effort on the CI, the test should be straight forward - see 525bdaa (this PR)

@koppor koppor marked this pull request as ready for review May 18, 2026 23:47
@qodo-free-for-open-source-projects

qodo-free-for-open-source-projects Bot commented May 18, 2026

Copy link
Copy Markdown
Contributor

Review Summary by Qodo

(Agentic_describe updated until commit e415751)

Refine JabKit CLI: positional input argument, check command grouping, and errorformat output

✨ Enhancement

Grey Divider

Walkthroughs

Description
• Restructure jabkit CLI with new check parent command grouping consistency and integrity checks
• Accept input files as positional arguments with --input as backward-compatible alias
• Add errorformat output format to consistency check, making it default for both checks
• Support running both checks together via jabkit check <file> without subcommand
• Refactor commands to use shared InputOption mixin and return exit codes consistently
Diagram
flowchart LR
  A["CLI Input"] -->|"positional or --input"| B["InputOption Mixin"]
  B --> C["Check Parent Command"]
  C -->|"consistency"| D["CheckConsistency"]
  C -->|"integrity"| E["CheckIntegrity"]
  C -->|"no subcommand"| F["Run Both Checks"]
  D --> G["errorformat/txt/csv Output"]
  E --> G
  F --> G
Loading

Grey Divider

File Changes

1. jabkit/src/main/java/org/jabref/toolkit/commands/Check.java ✨ Enhancement +56/-0

New parent command grouping consistency and integrity checks

jabkit/src/main/java/org/jabref/toolkit/commands/Check.java


2. jabkit/src/main/java/org/jabref/toolkit/commands/InputOption.java ✨ Enhancement +46/-0

Reusable mixin for positional input file argument

jabkit/src/main/java/org/jabref/toolkit/commands/InputOption.java


3. jabkit/src/main/java/org/jabref/toolkit/commands/CheckConsistency.java ✨ Enhancement +42/-29

Refactor to use InputOption mixin and errorformat output

jabkit/src/main/java/org/jabref/toolkit/commands/CheckConsistency.java


View more (34)
4. jabkit/src/main/java/org/jabref/toolkit/commands/CheckIntegrity.java ✨ Enhancement +25/-29

Refactor to use InputOption mixin and shared execute method

jabkit/src/main/java/org/jabref/toolkit/commands/CheckIntegrity.java


5. jabkit/src/main/java/org/jabref/toolkit/commands/JabKit.java ✨ Enhancement +22/-2

Add Check parent command and ImportOutcome helper record

jabkit/src/main/java/org/jabref/toolkit/commands/JabKit.java


6. jablib/src/main/java/org/jabref/logic/quality/consistency/BibliographyConsistencyCheckResultErrorFormatWriter.java ✨ Enhancement +74/-0

New writer for errorformat output of consistency check

jablib/src/main/java/org/jabref/logic/quality/consistency/BibliographyConsistencyCheckResultErrorFormatWriter.java


7. jablib/src/main/java/org/jabref/logic/integrity/IntegrityCheckResultErrorFormatWriter.java ✨ Enhancement +12/-1

Enhance errorformat writer with citation key and field info

jablib/src/main/java/org/jabref/logic/integrity/IntegrityCheckResultErrorFormatWriter.java


8. jablib/src/main/java/org/jabref/logic/integrity/IntegrityCheckResultTxtWriter.java ✨ Enhancement +0/-20

Remove txt writer, consolidate to errorformat

jablib/src/main/java/org/jabref/logic/integrity/IntegrityCheckResultTxtWriter.java


9. jabkit/src/main/java/org/jabref/toolkit/commands/Convert.java ✨ Enhancement +18/-15

Implement Callable interface and use InputOption mixin

jabkit/src/main/java/org/jabref/toolkit/commands/Convert.java


10. jabkit/src/main/java/org/jabref/toolkit/commands/Fetch.java ✨ Enhancement +8/-5

Implement Callable interface and improve error handling

jabkit/src/main/java/org/jabref/toolkit/commands/Fetch.java


11. jabkit/src/main/java/org/jabref/toolkit/commands/GenerateBibFromAux.java ✨ Enhancement +13/-16

Implement Callable interface and use InputOption mixin

jabkit/src/main/java/org/jabref/toolkit/commands/GenerateBibFromAux.java


12. jabkit/src/main/java/org/jabref/toolkit/commands/GenerateCitationKeys.java ✨ Enhancement +13/-15

Implement Callable interface and use InputOption mixin

jabkit/src/main/java/org/jabref/toolkit/commands/GenerateCitationKeys.java


13. jabkit/src/main/java/org/jabref/toolkit/commands/PdfUpdate.java ✨ Enhancement +12/-10

Implement Callable interface and use InputOption mixin

jabkit/src/main/java/org/jabref/toolkit/commands/PdfUpdate.java


14. jabkit/src/main/java/org/jabref/toolkit/commands/Pseudonymize.java ✨ Enhancement +15/-13

Implement Callable interface and use InputOption mixin

jabkit/src/main/java/org/jabref/toolkit/commands/Pseudonymize.java


15. jabkit/src/main/java/org/jabref/toolkit/commands/Search.java ✨ Enhancement +15/-12

Implement Callable interface and use InputOption mixin

jabkit/src/main/java/org/jabref/toolkit/commands/Search.java


16. jabkit/src/main/java/org/jabref/toolkit/commands/Pdf.java ✨ Enhancement +8/-3

Implement Callable interface with proper error handling

jabkit/src/main/java/org/jabref/toolkit/commands/Pdf.java


17. jabkit/src/main/java/org/jabref/toolkit/commands/Preferences.java ✨ Enhancement +6/-3

Implement Callable interface with proper error handling

jabkit/src/main/java/org/jabref/toolkit/commands/Preferences.java


18. jabkit/src/main/java/org/jabref/toolkit/commands/DoiToBibtex.java ✨ Enhancement +1/-1

Improve error message output to stderr

jabkit/src/main/java/org/jabref/toolkit/commands/DoiToBibtex.java


19. jabkit/src/main/java/org/jabref/toolkit/JabKitLauncher.java ✨ Enhancement +3/-6

Remove special case handling for check-consistency command

jabkit/src/main/java/org/jabref/toolkit/JabKitLauncher.java


20. jabkit/src/test/java/org/jabref/toolkit/commands/JabKitTest.java 🧪 Tests +44/-3

Update tests for new check command structure and positional args

jabkit/src/test/java/org/jabref/toolkit/commands/JabKitTest.java


21. jablib/src/test/java/org/jabref/logic/quality/consistency/BibliographyConsistencyCheckResultErrorFormatWriterTest.java 🧪 Tests +69/-0

Add tests for consistency check errorformat output

jablib/src/test/java/org/jabref/logic/quality/consistency/BibliographyConsistencyCheckResultErrorFormatWriterTest.java


22. jabkit/src/test/java/org/jabref/toolkit/commands/AbstractJabKitTest.java 🧪 Tests +17/-0

Add citation key pattern preferences to test setup

jabkit/src/test/java/org/jabref/toolkit/commands/AbstractJabKitTest.java


23. .jbang/JabKitLauncher.java ⚙️ Configuration changes +6/-2

Update Java version and add mavenlocal repository

.jbang/JabKitLauncher.java


24. .jbang/JabLsLauncher.java ⚙️ Configuration changes +4/-2

Update Java version and add mavenlocal repository

.jbang/JabLsLauncher.java


25. .jbang/JabSrvLauncher.java ⚙️ Configuration changes +4/-2

Update Java version and add mavenlocal repository

.jbang/JabSrvLauncher.java


26. .jbang/README.md 📝 Documentation +13/-3

Document new check command structure and local testing

.jbang/README.md


27. docs/decisions/0045-use-input-flag-always-for-input-files.md 📝 Documentation +12/-3

Mark ADR-0045 as superseded by ADR-0057

docs/decisions/0045-use-input-flag-always-for-input-files.md


28. docs/decisions/0057-allow-positional-input-file-argument.md 📝 Documentation +93/-0

New ADR documenting positional input file argument decision

docs/decisions/0057-allow-positional-input-file-argument.md


29. docs/requirements/cli.md 📝 Documentation +17/-4

Update requirements for positional input and errorformat output

docs/requirements/cli.md


30. CHANGELOG.md 📝 Documentation +4/-0

Document CLI enhancements and new check command structure

CHANGELOG.md


31. jabkit/build.gradle.kts ⚙️ Configuration changes +1/-1

Update smoke test to use new check command syntax

jabkit/build.gradle.kts


32. .github/actions/jbang-check/action.yml ⚙️ Configuration changes +28/-0

New GitHub action for building and testing JBang scripts

.github/actions/jbang-check/action.yml


33. .github/workflows/tests-code.yml ⚙️ Configuration changes +12/-100

Refactor JBang workflow to use new action and publish jablib locally

.github/workflows/tests-code.yml


34. jablib-examples/jbang/doi_to_bibtex.java ⚙️ Configuration changes +1/-1

Update Java version requirement

jablib-examples/jbang/doi_to_bibtex.java


35. jablib-examples/jbang/ieee_pdf_references_to_bibtex.java ⚙️ Configuration changes +1/-1

Update Java version requirement

jablib-examples/jbang/ieee_pdf_references_to_bibtex.java


36. jablib/src/main/resources/l10n/JabRef_en.properties 📝 Documentation +5/-0

Add localization strings for new error messages

jablib/src/main/resources/l10n/JabRef_en.properties


37. jablib/src/main/java/org/jabref/logic/quality/consistency/BibliographyConsistencyCheckResultWriter.java Additional files +1/-1

...

jablib/src/main/java/org/jabref/logic/quality/consistency/BibliographyConsistencyCheckResultWriter.java


Grey Divider

Qodo Logo

@qodo-free-for-open-source-projects

qodo-free-for-open-source-projects Bot commented May 18, 2026

Copy link
Copy Markdown
Contributor

Code Review by Qodo

🐞 Bugs (3) 📘 Rule violations (2) 📎 Requirement gaps (0)

Grey Divider


Action required

1. Null passed to getFieldRange 📘 Rule violation ≡ Correctness
Description
IntegrityCheckResultErrorFormatWriter.writeFindings() calls
parserResult.getFieldRange(message.entry(), message.field()) before checking whether
message.field() is null, which can trigger a NullPointerException for entry-level findings. This
violates the requirement to not pass null to methods unless explicitly required.
Code

jablib/src/main/java/org/jabref/logic/integrity/IntegrityCheckResultErrorFormatWriter.java[R26-33]

    for (IntegrityMessage message : messages) {
        ParserResult.Range fieldRange = parserResult.getFieldRange(message.entry(), message.field());
-            writer.append("%s:%d:%d: %s\n".formatted(
+            // Entry-level findings (e.g. on the citation key itself) carry only the citation key;
+            // field-level findings additionally carry the field name.
+            String location = message.entry().getCitationKey().orElse("");
+            Field field = message.field();
+            if (field != null && field != InternalField.KEY_FIELD) {
+                location += ":" + field.getName();
Evidence
PR Compliance ID 15 prohibits passing null as an argument unless explicitly required. The updated
writer checks field != null but still passes message.field() into
parserResult.getFieldRange(...) unconditionally, and ParserResult.getFieldRange(...)
dereferences field (via field.getAlias()), so null will crash at runtime.

AGENTS.md
jablib/src/main/java/org/jabref/logic/integrity/IntegrityCheckResultErrorFormatWriter.java[26-33]
jablib/src/main/java/org/jabref/logic/importer/ParserResult.java[189-205]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`IntegrityCheckResultErrorFormatWriter.writeFindings()` calls `ParserResult.getFieldRange(...)` with `message.field()` even when it can be `null`, which can cause a NullPointerException.
## Issue Context
The code already treats `message.field()` as potentially `null` (see the `field != null` check), but the null-sensitive call happens before that check.
## Fix Focus Areas
- jablib/src/main/java/org/jabref/logic/integrity/IntegrityCheckResultErrorFormatWriter.java[26-40]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Integrity exit code wrong ✓ Resolved 🐞 Bug ≡ Correctness
Description
CheckIntegrity.execute() always returns 0 even when integrity findings exist, so `jabkit check
integrity and jabkit check FILE` cannot signal findings via exit status. This contradicts the
parent check command’s documented exit-code semantics and can let CI pass with integrity problems.
Code

jabkit/src/main/java/org/jabref/toolkit/commands/CheckIntegrity.java[R89-102]

    Writer writer = new OutputStreamWriter(System.out);
    IntegrityCheckResultWriter checkResultWriter;
    switch (outputFormat.toLowerCase(Locale.ROOT)) {
-            case "errorformat" ->
-                    checkResultWriter = new IntegrityCheckResultErrorFormatWriter(writer, messages, parserResult.get(), inputFile);
-            case "txt" ->
-                    checkResultWriter = new IntegrityCheckResultTxtWriter(writer, messages);
-            case "csv" ->
+            // "txt" is kept as an alias for the errorformat output.
+            case Check.FORMAT_ERRORFORMAT,
+                 Check.FORMAT_TXT ->
+                    checkResultWriter = new IntegrityCheckResultErrorFormatWriter(writer, messages, parserResult, inputFile);
+            case Check.FORMAT_CSV ->
                checkResultWriter = new IntegrityCheckResultCsvWriter(writer, messages);
        default -> {
-                System.out.println(Localization.lang("Unknown output format '%0'.", outputFormat));
+                System.err.println(Localization.lang("Unknown output format '%0'.", outputFormat));
            return 3;
        }
    }
Evidence
The integrity check builds a messages list but returns 0 unconditionally after writing results,
while the parent check command expects 1 to represent findings and uses the max of both checks’
exit codes.

jabkit/src/main/java/org/jabref/toolkit/commands/CheckIntegrity.java[83-112]
jabkit/src/main/java/org/jabref/toolkit/commands/Check.java[50-54]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`CheckIntegrity.execute(...)` collects integrity messages, writes them, and then unconditionally returns `0`. This prevents callers (including `jabkit check FILE`) from using exit codes to detect integrity findings.
## Issue Context
The parent `check` command explicitly documents `1 = findings` and combines exit codes using `Math.max(...)`, but `CheckIntegrity.execute(...)` never returns `1`.
## Fix Focus Areas
- jabkit/src/main/java/org/jabref/toolkit/commands/CheckIntegrity.java[83-112]
- jabkit/src/main/java/org/jabref/toolkit/commands/Check.java[50-54]
### Implementation notes
- After writing findings, return `messages.isEmpty() ? 0 : 1`.
- Update the `execute` Javadoc to include `1 = findings` for consistency with `Check` and `CheckConsistency`.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. Key findings misformatted ✓ Resolved 🐞 Bug ≡ Correctness
Description
IntegrityCheckResultErrorFormatWriter appends a field segment for citation-key findings emitted with
StandardField.KEY, but requirements state citation-key (entry-level) findings must carry only the
citation key. This breaks the documented citationKey[:field] shape and can confuse parsers
consuming the errorformat stream.
Code

jablib/src/main/java/org/jabref/logic/integrity/IntegrityCheckResultErrorFormatWriter.java[R26-40]

    for (IntegrityMessage message : messages) {
        ParserResult.Range fieldRange = parserResult.getFieldRange(message.entry(), message.field());
-            writer.append("%s:%d:%d: %s\n".formatted(
+            // Entry-level findings (e.g. on the citation key itself) carry only the citation key;
+            // field-level findings additionally carry the field name.
+            String location = message.entry().getCitationKey().orElse("");
+            Field field = message.field();
+            if (field != null && field != InternalField.KEY_FIELD) {
+                location += ":" + field.getName();
+            }
+            writer.append("%s:%d:%d:%s: %s\n".formatted(
                inputFile,
                fieldRange.startLine(),
                fieldRange.startColumn(),
+                    location,
                message.message()));
Evidence
The requirements explicitly define that citation-key findings are entry-level and must carry only
the citation key; however, duplicate-key findings use StandardField.KEY and the writer appends the
field name for anything except InternalField.KEY_FIELD.

docs/requirements/cli.md[28-36]
jablib/src/main/java/org/jabref/logic/integrity/CitationKeyDuplicationChecker.java[21-32]
jablib/src/main/java/org/jabref/logic/integrity/IntegrityCheckResultErrorFormatWriter.java[23-41]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`IntegrityCheckResultErrorFormatWriter` only suppresses the field name when `field == InternalField.KEY_FIELD`. Some citation-key findings use `StandardField.KEY`, so the writer appends `:key` even though the requirement says entry-level findings on the citation key must not include a field segment.
## Issue Context
`CitationKeyDuplicationChecker` emits `IntegrityMessage(..., StandardField.KEY)` for duplicate citation keys.
## Fix Focus Areas
- jablib/src/main/java/org/jabref/logic/integrity/IntegrityCheckResultErrorFormatWriter.java[23-41]
- jablib/src/main/java/org/jabref/logic/integrity/CitationKeyDuplicationChecker.java[21-32]
- docs/requirements/cli.md[28-36]
### Implementation notes
- Treat `StandardField.KEY` the same as `InternalField.KEY_FIELD` for formatting (do not append `:field`).
- Optionally, for key-level findings use `parserResult.getCompleteEntryIndicator(entry)` for the range, to avoid depending on field-alias resolution for `StandardField.KEY`.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


View more (2)
4. Unlocalized Check console message ✓ Resolved 📘 Rule violation ⚙ Maintainability
Description
Check.call() prints a hardcoded user-facing message instead of using the project localization
mechanism. This prevents translation and violates the localization standard for user-visible
messages.
Code

jabkit/src/main/java/org/jabref/toolkit/commands/Check.java[R44-46]

+        if (inputFile == null) {
+            System.out.println("Specify a subcommand (consistency, integrity) or an input file.");
+            return 0;
Evidence
PR Compliance requires user-facing messages to be localized. The new check command prints
System.out.println("Specify a subcommand (consistency, integrity) or an input file."), which is a
hardcoded English string.

AGENTS.md
jabkit/src/main/java/org/jabref/toolkit/commands/Check.java[44-46]
Best Practice: Learned patterns

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
A user-facing CLI message is printed as a hardcoded English string, bypassing JabRef localization.
## Issue Context
Other `jabkit` commands print user-visible output via `Localization.lang(...)`. The new `check` command should follow the same convention.
## Fix Focus Areas
- jabkit/src/main/java/org/jabref/toolkit/commands/Check.java[44-46]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


5. JBang main job missing setup ✓ Resolved 🐞 Bug ☼ Reliability
Description
The jbang-main workflow job invokes a local composite action without checking out the repository
or installing JDK/JBang, so it will fail on main. The same workflow’s jbang-pr job still
performs checkout and setup, making this a main-branch-only regression.
Code

.github/workflows/tests-code.yml[R387-388]

+      - name: Build JBang scripts and run launchers
+        uses: ./.github/actions/jbang-check
Evidence
jbang-main has only a single step invoking the local composite action, while jbang-pr includes
checkout and the shared setup action; and setup-gradle is what installs JDK and JBang that
jbang-check depends on.

.github/workflows/tests-code.yml[381-388]
.github/workflows/tests-code.yml[390-407]
.github/actions/jbang-check/action.yml[1-25]
.github/actions/setup-gradle/action.yml[14-44]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The `jbang-main` job calls `./.github/actions/jbang-check` but does not run `actions/checkout` and does not install JDK/JBang (previously done via `./.github/actions/setup-gradle`). Local composite actions require the repo to be present in the workspace, and the `jbang-check` action assumes `jbang` is available.
### Issue Context
`jbang-pr` still performs checkout and uses `setup-gradle` (which installs JDK and JBang), but `jbang-main` no longer does.
### Fix Focus Areas
- .github/workflows/tests-code.yml[381-388]
- .github/actions/jbang-check/action.yml[1-25]
- .github/actions/setup-gradle/action.yml[14-44]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

6. JBang uses //JAVA 25 📘 Rule violation ☼ Reliability
Description
Multiple JBang launchers were changed from //JAVA 25+ to //JAVA 25, which may prevent running
with newer JDKs and conflicts with the repository requirement to use JDK 25+. This can reduce
forward-compatibility of the scripts in CI and developer environments.
Code

.jbang/JabKitLauncher.java[5]

+//JAVA 25
Evidence
PR Compliance ID 1 requires using JDK 25+. The diff shows the PR changed //JAVA 25+ to //JAVA 25
in launcher scripts, which weakens the '25 or later' contract expressed previously.

AGENTS.md
.jbang/JabKitLauncher.java[5-5]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The JBang scripts now specify `//JAVA 25` instead of `//JAVA 25+`, potentially restricting execution to an exact JDK version rather than allowing JDK 25 or later.
## Issue Context
Repository compliance requires using JDK 25+.
## Fix Focus Areas
- .jbang/JabKitLauncher.java[5-5]
- .jbang/JabLsLauncher.java[5-5]
- .jbang/JabSrvLauncher.java[5-5]
- jablib-examples/jbang/doi_to_bibtex.java[14-14]
- jablib-examples/jbang/ieee_pdf_references_to_bibtex.java[13-13]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


7. PdfUpdate errors on stdout ✓ Resolved 🐞 Bug ◔ Observability
Description
PdfUpdate prints fatal import/parse errors to stdout while returning non-zero exit codes, mixing
errors into the normal output stream. Other commands in this PR route equivalent failures to stderr,
so PdfUpdate remains inconsistent for scripting and tooling.
Code

jabkit/src/main/java/org/jabref/toolkit/commands/PdfUpdate.java[R75-82]

    if (parserResult.isEmpty()) {
        System.out.println(Localization.lang("Unable to open file '%0'.", inputFile));
-            return;
+            return 2;
    }
    if (parserResult.get().isInvalid()) {
        System.out.println(Localization.lang("Input file '%0' is invalid and could not be parsed.", inputFile));
-            return;
+            return 2;
Evidence
PdfUpdate uses stdout for import errors, while Convert (and other updated commands) uses stderr for
the same failure cases, indicating PdfUpdate was missed and remains inconsistent.

jabkit/src/main/java/org/jabref/toolkit/commands/PdfUpdate.java[69-83]
jabkit/src/main/java/org/jabref/toolkit/commands/Convert.java[59-68]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`PdfUpdate.call()` prints file-open / invalid-input failures using `System.out.println(...)` even though these are fatal errors (exit code 2). This makes it harder to separate normal output from errors in pipelines.
## Issue Context
Several other commands updated in this PR already use `System.err.println(...)` for the same error paths.
## Fix Focus Areas
- jabkit/src/main/java/org/jabref/toolkit/commands/PdfUpdate.java[69-83]
- jabkit/src/main/java/org/jabref/toolkit/commands/Convert.java[59-68]
### Implementation notes
- Replace the two `System.out.println(...)` calls in the error paths with `System.err.println(...)`.
- (Optional) Consider reusing the shared `importBibtexLibrary(...)` helper to standardize message + exit-code handling.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


8. CheckConsistency uses Optional.get() ✓ Resolved 📘 Rule violation ⚙ Maintainability
Description
The modified consistency- and integrity-check flows use Optional.isEmpty() followed by
Optional.get(), which is discouraged by the Optional-control-flow compliance rule. This pattern is
unnecessarily verbose and increases the risk of unsafe get() usage during future edits or
refactors.
Code

jabkit/src/main/java/org/jabref/toolkit/commands/CheckConsistency.java[R53-60]

   Optional<ParserResult> parserResult = JabKit.importFile(
           inputFile,
           "bibtex",
           jabKit.cliPreferences,
-                sharedOptions.porcelain);
+                porcelain);
   if (parserResult.isEmpty()) {
       System.out.println(Localization.lang("Unable to open file '%0'.", inputFile));
       return 2;
Evidence
PR Compliance ID 29 forbids using isPresent()/isEmpty() checks paired with get() in the same
control flow in new or modified code. In CheckConsistency.execute, the code checks
parserResult.isEmpty() and then calls parserResult.get() multiple times, and in
CheckIntegrity.execute, it similarly checks parserResult.isEmpty() and later calls
parserResult.get(), including when creating the writer, demonstrating the prohibited pattern in
both modified paths.

jabkit/src/main/java/org/jabref/toolkit/commands/CheckConsistency.java[53-84]
jabkit/src/main/java/org/jabref/toolkit/commands/CheckIntegrity.java[64-105]
Best Practice: Learned patterns

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
New/modified code uses `Optional.isEmpty()` and then `Optional.get()`, violating the Optional-control-flow compliance rule.
## Issue Context
PR Compliance ID 29 requires idiomatic Optional control flow (e.g., `map`, `ifPresentOrElse`, `orElseThrow`) instead of manual presence checks followed by `get()`, because the latter is verbose and can be accidentally made unsafe during later changes.
## Fix Focus Areas
- jabkit/src/main/java/org/jabref/toolkit/commands/CheckConsistency.java[53-83]
- jabkit/src/main/java/org/jabref/toolkit/commands/CheckIntegrity.java[64-105]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


View more (3)
9. check options ignored pre-subcommand 🐞 Bug ≡ Correctness
Description
--output-format and --porcelain are defined on both the check parent command and its
consistency/integrity subcommands, so the same flag can be parsed into a different object
depending on where it appears. This can make invocations like `jabkit check --output-format txt
consistency FILE or jabkit check --porcelain consistency FILE` silently ignore the user’s intent
because the subcommand reads its own fields, not the parent’s.
Code

jabkit/src/main/java/org/jabref/toolkit/commands/Check.java[R30-40]

+    @Mixin
+    private JabKit.SharedOptions sharedOptions = new JabKit.SharedOptions();
+
+    /// Optional positional input file. When supplied directly to `check` (without a
+    /// `consistency` or `integrity` subcommand), both checks run against it.
+    @Parameters(index = "0", arity = "0..1", paramLabel = "FILE", converter = CygWinPathConverter.class,
+            description = "Input file. When given without a subcommand, both the consistency and integrity checks run.")
+    private Path inputFile;
+
+    @Option(names = {"--output-format"}, description = "Output format: errorformat, txt or csv", defaultValue = FORMAT_ERRORFORMAT)
+    private String outputFormat;
Evidence
The parent Check declares SharedOptions and --output-format, while each subcommand also
declares its own SharedOptions/--output-format and passes only its own values into
execute(...), making parent-parsed values ineffective for subcommands.

jabkit/src/main/java/org/jabref/toolkit/commands/Check.java[30-50]
jabkit/src/main/java/org/jabref/toolkit/commands/CheckConsistency.java[33-45]
jabkit/src/main/java/org/jabref/toolkit/commands/CheckIntegrity.java[40-56]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The `check` command and its subcommands both declare `--output-format` and `SharedOptions` (`--porcelain`), but subcommands only consult their own fields. With subcommands, options placed before the subcommand name will bind to the parent, and the subcommand won’t see them.
### Issue Context
- `Check` defines `--output-format` and a `SharedOptions` mixin.
- `CheckConsistency` / `CheckIntegrity` define their own `--output-format` and `SharedOptions` and use those in `call()`.
### Fix Focus Areas
- jabkit/src/main/java/org/jabref/toolkit/commands/Check.java[30-54]
- jabkit/src/main/java/org/jabref/toolkit/commands/CheckConsistency.java[33-45]
- jabkit/src/main/java/org/jabref/toolkit/commands/CheckIntegrity.java[40-56]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


10. Unknown format falls back CSV 🐞 Bug ≡ Correctness
Description
check consistency treats any unrecognized --output-format value as CSV, so typos silently change
output instead of reporting an error. check integrity correctly rejects unknown formats with a
non-zero exit code, making the two checks inconsistent.
Code

jabkit/src/main/java/org/jabref/toolkit/commands/CheckConsistency.java[R95-118]

+        if (Check.FORMAT_ERRORFORMAT.equalsIgnoreCase(outputFormat)) {
+            checkResultWriter = new BibliographyConsistencyCheckResultErrorFormatWriter(
+                    result,
+                    writer,
+                    porcelain,
+                    jabKit.entryTypesManager,
+                    databaseContext.getMode(),
+                    parserResult,
+                    inputFile);
+        } else if (Check.FORMAT_TXT.equalsIgnoreCase(outputFormat)) {
       checkResultWriter = new BibliographyConsistencyCheckResultTxtWriter(
               result,
               writer,
-                    sharedOptions.porcelain,
+                    porcelain,
               jabKit.entryTypesManager,
               databaseContext.getMode());
   } else {
       checkResultWriter = new BibliographyConsistencyCheckResultCsvWriter(
               result,
               writer,
-                    sharedOptions.porcelain,
+                    porcelain,
               jabKit.entryTypesManager,
               databaseContext.getMode());
   }
Evidence
Consistency check routes all non-errorformat and non-txt values to the CSV writer, while
integrity check has an explicit default branch that prints an error and returns exit code 3.

jabkit/src/main/java/org/jabref/toolkit/commands/CheckConsistency.java[95-118]
jabkit/src/main/java/org/jabref/toolkit/commands/CheckIntegrity.java[100-113]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`CheckConsistency.writeCheckResult` defaults to CSV in the final `else`, even when `outputFormat` is an unknown string. This should return an error (and ideally the same exit code behavior as `CheckIntegrity`, which returns 3 for unknown formats).
### Issue Context
`--output-format` is documented as `errorformat, txt or csv`.
### Fix Focus Areas
- jabkit/src/main/java/org/jabref/toolkit/commands/CheckConsistency.java[95-118]
- jabkit/src/main/java/org/jabref/toolkit/commands/CheckIntegrity.java[100-113]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


11. Default errorformat includes progress lines 🐞 Bug ☼ Reliability
Description
Even when --output-format=errorformat (now the default), both checks print progress/completion
lines unless --porcelain is set, so output is not purely the advertised `file:line:column:
message` stream. This makes the default harder to consume in editor/CI integrations that expect
line-oriented diagnostics.
Code

jabkit/src/main/java/org/jabref/toolkit/commands/CheckConsistency.java[R39-60]

+    @Option(names = {"--output-format"}, description = "Output format: errorformat, txt or csv", defaultValue = Check.FORMAT_ERRORFORMAT)
private String outputFormat;
@Override
public Integer call() {
+        return execute(inputOption.getInputFile(), outputFormat, sharedOptions.porcelain, check.jabKit);
+    }
+
+    /// Runs the consistency check on `inputFile` and writes the findings to `System.out`.
+    ///
+    /// Shared with the parent `check` command, which runs both checks at once.
+    ///
+    /// @return the exit code (0 = consistent, 1 = inconsistencies found, 2/3 = error)
+    static int execute(Path inputFile, String outputFormat, boolean porcelain, JabKit jabKit) {
   Optional<ParserResult> parserResult = JabKit.importFile(
           inputFile,
           "bibtex",
           jabKit.cliPreferences,
-                sharedOptions.porcelain);
+                porcelain);
   if (parserResult.isEmpty()) {
       System.out.println(Localization.lang("Unable to open file '%0'.", inputFile));
       return 2;
Evidence
CheckConsistency and CheckIntegrity both print status output guarded only by !porcelain, while
the errorformat writer is documented as line-oriented output for editor/CI tooling.

jabkit/src/main/java/org/jabref/toolkit/commands/CheckConsistency.java[39-71]
jabkit/src/main/java/org/jabref/toolkit/commands/CheckConsistency.java[68-80]
jabkit/src/main/java/org/jabref/toolkit/commands/CheckIntegrity.java[46-82]
jablib/src/main/java/org/jabref/logic/quality/consistency/BibliographyConsistencyCheckResultErrorFormatWriter.java[16-18]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
With `errorformat` as the default, the commands still emit human-oriented status/progress lines unless `--porcelain` is specified. That pollutes the line-oriented diagnostic stream that `errorformat` is meant to provide.
### Issue Context
The new writer explicitly documents `errorformat` as “line-oriented … suitable for editors and CI tooling”, but the command implementation prints extra lines whenever `porcelain == false`.
### Fix Focus Areas
- jabkit/src/main/java/org/jabref/toolkit/commands/CheckConsistency.java[39-80]
- jabkit/src/main/java/org/jabref/toolkit/commands/CheckIntegrity.java[46-83]
- jablib/src/main/java/org/jabref/logic/quality/consistency/BibliographyConsistencyCheckResultErrorFormatWriter.java[16-18]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Qodo Logo

Comment on lines +44 to +46
if (inputFile == null) {
System.out.println("Specify a subcommand (consistency, integrity) or an input file.");
return 0;

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.

Action required

1. Unlocalized check console message 📘 Rule violation ⚙ Maintainability

Check.call() prints a hardcoded user-facing message instead of using the project localization
mechanism. This prevents translation and violates the localization standard for user-visible
messages.
Agent Prompt
## Issue description
A user-facing CLI message is printed as a hardcoded English string, bypassing JabRef localization.

## Issue Context
Other `jabkit` commands print user-visible output via `Localization.lang(...)`. The new `check` command should follow the same convention.

## Fix Focus Areas
- jabkit/src/main/java/org/jabref/toolkit/commands/Check.java[44-46]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I am not sure if we should translate JabKit in the first place - opinions?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I added translations

Comment on lines +387 to +388
- name: Build JBang scripts and run launchers
uses: ./.github/actions/jbang-check

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.

Action required

2. Jbang main job missing setup 🐞 Bug ☼ Reliability

The jbang-main workflow job invokes a local composite action without checking out the repository
or installing JDK/JBang, so it will fail on main. The same workflow’s jbang-pr job still
performs checkout and setup, making this a main-branch-only regression.
Agent Prompt
### Issue description
The `jbang-main` job calls `./.github/actions/jbang-check` but does not run `actions/checkout` and does not install JDK/JBang (previously done via `./.github/actions/setup-gradle`). Local composite actions require the repo to be present in the workspace, and the `jbang-check` action assumes `jbang` is available.

### Issue Context
`jbang-pr` still performs checkout and uses `setup-gradle` (which installs JDK and JBang), but `jbang-main` no longer does.

### Fix Focus Areas
- .github/workflows/tests-code.yml[381-388]
- .github/actions/jbang-check/action.yml[1-25]
- .github/actions/setup-gradle/action.yml[14-44]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

This installs JDK and Jbang -> false positive

@koppor koppor marked this pull request as draft May 19, 2026 06:39
koppor and others added 8 commits May 19, 2026 08:40
Emit findings as `file:line:column:citationKey[:field]: message` in both
the integrity and consistency error-format writers. Entry-level findings
carry only the citation key; field-level findings add the field name.

Document the format as req~jabkit.cli.check-errorformat-output~1 and link
it from the writers.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Extract the duplicated import-and-validate block from CheckConsistency
and CheckIntegrity into JabKit.importBibtexLibrary, which returns an
ImportOutcome record. Callers no longer unwrap an Optional.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Remove IntegrityCheckResultTxtWriter. The integrity check's `txt` output
format is now an alias for `errorformat`; `csv` is unchanged. The
consistency check keeps its tabular `txt` output.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
One step per launcher in the jbang-check action, so a failure points
precisely at the offending launcher.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Wrap the "Specify a subcommand ..." and PDF format-option messages in
Localization.lang and add the keys to JabRef_en.properties.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Print the --format validation error to System.err and convert PdfUpdate
to Callable<Integer> so usage and import errors exit with code 2.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
koppor and others added 5 commits May 19, 2026 12:17
Route user-facing error and usage messages in the jabkit commands to
System.err, and convert the remaining Runnable commands to
Callable<Integer> so failures exit with code 2 instead of 0.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Update the expected lines to the file:line:column:citationKey:field
format the writer now produces.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@koppor koppor marked this pull request as ready for review May 19, 2026 17:34
@qodo-free-for-open-source-projects

qodo-free-for-open-source-projects Bot commented May 19, 2026

Copy link
Copy Markdown
Contributor

Persistent review updated to latest commit e415751

@koppor koppor enabled auto-merge May 19, 2026 17:38
uses: actions/checkout@v6
with:
submodules: 'false'
submodules: 'true'

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Reallcy necessary now?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

yeah - context

      - name: Publish jablib to the local Maven repository
        run: ./gradlew -PprojVersion=6.0 :jablib:publishToMavenLocal

jablib needs submodules.

Comment on lines 26 to +33
for (IntegrityMessage message : messages) {
ParserResult.Range fieldRange = parserResult.getFieldRange(message.entry(), message.field());
writer.append("%s:%d:%d: %s\n".formatted(
// Entry-level findings (e.g. on the citation key itself) carry only the citation key;
// field-level findings additionally carry the field name.
String location = message.entry().getCitationKey().orElse("");
Field field = message.field();
if (field != null && field != InternalField.KEY_FIELD) {
location += ":" + field.getName();

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.

Action required

1. Null passed to getfieldrange 📘 Rule violation ≡ Correctness

IntegrityCheckResultErrorFormatWriter.writeFindings() calls
parserResult.getFieldRange(message.entry(), message.field()) before checking whether
message.field() is null, which can trigger a NullPointerException for entry-level findings. This
violates the requirement to not pass null to methods unless explicitly required.
Agent Prompt
## Issue description
`IntegrityCheckResultErrorFormatWriter.writeFindings()` calls `ParserResult.getFieldRange(...)` with `message.field()` even when it can be `null`, which can cause a NullPointerException.

## Issue Context
The code already treats `message.field()` as potentially `null` (see the `field != null` check), but the null-sensitive call happens before that check.

## Fix Focus Areas
- jablib/src/main/java/org/jabref/logic/integrity/IntegrityCheckResultErrorFormatWriter.java[26-40]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I added @Nullmarked and checked all callers --> false positive

koppor and others added 4 commits May 19, 2026 20:09
CheckIntegrity.execute() always returned 0, so `jabkit check integrity`
and `jabkit check FILE` could not report integrity findings via exit
status, letting CI pass with integrity problems.

Return 1 when findings exist (0 = none, 1 = findings, 2/3 = error),
matching CheckConsistency. The parent `check` command propagates the
worst code via Math.max.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
InAnYan
InAnYan previously approved these changes May 19, 2026
@Siedlerchr

Copy link
Copy Markdown
Member

54

Task :jabkit:test

JabKitTest > checkIntegrity() FAILED
org.opentest4j.AssertionFailedError at JabKitTest.java:76

JabKitTest > checkWithoutSubcommandRunsBothChecks() FAILED
org.opentest4j.AssertionFailedError at JabKitTest.java:100

@koppor koppor added this pull request to the merge queue May 19, 2026
@github-actions github-actions Bot added the status: to-be-merged PRs which are accepted and should go into the merge-queue. label May 19, 2026
Merged via the queue into main with commit b55efa8 May 19, 2026
64 checks passed
@koppor koppor deleted the refine-jabkit branch May 19, 2026 21:51
Siedlerchr added a commit that referenced this pull request May 20, 2026
* upstream/main:
  Update PULL_REQUEST_TEMPLATE.md (#15788)
  New Crowdin updates (#15787)
  Update heylogs to 0.18.0 and use github-actions format (#15786)
  Grand refactoring of the AI features (#15688)
  Chore(deps): Bump com.fasterxml:aalto-xml in /versions (#15782)
  Chore(deps): Bump org.junit:junit-bom from 6.0.3 to 6.1.0 in /versions (#15783)
  Fix default value for unwanted characters (#15743)
  Fix runner tag
  Fix runner for JBang (PR)
  Fix duplicate finder progress counter incrementing on empty queue polls (#15781)
  Refine JabKit CLI: positional input argument and check command group (#15759)
  Ignore exception in unregisterListener to prevent exception (#15761)
  Fix wrong usage of "key" (#15779)
  Fix Hayagriva export to nest identifiers under serial-number (#15750)
f0restron07 pushed a commit to f0restron07/jabref that referenced this pull request May 24, 2026
…abRef#15759)

* Fix description

* Fix typos

* Refine JabKit CLI: positional input + check command group

Group the consistency and integrity checks under a new `check` parent
command (`jabkit check consistency`, `jabkit check integrity`) and default
`check integrity` to the txt output format.

Accept the input file as a positional argument on every input-taking
subcommand via a shared InputOption mixin; `--input` is kept as an alias.
This supersedes ADR-0045 (ADR-0057) for increased convenience while
preserving cross-command consistency.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* Add errorformat output to the consistency check

Add a BibliographyConsistencyCheckResultErrorFormatWriter producing
line-oriented `file:line:column: message` output, mirroring the integrity
check's errorformat writer. One line is emitted per deviating field of a
reported entry.

Make `errorformat` the default output format for both `jabkit check`
subcommands.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* Move JabKit CLI changelog entries to "Added" and link PR JabRef#15759

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* Run both checks when jabkit check given a file without a subcommand

jabkit check <file> now runs the consistency and integrity checks together.
The shared check logic is extracted into static execute() methods so the
parent check command and the consistency/integrity subcommands reuse it.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* Extract jabkit check output-format constants

Replace the "errorformat"/"txt"/"csv" string literals in the `check`
commands with `FORMAT_*` constants on `Check`.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* Fix ternary indentation in InputOption

Align the `?`/`:` operators under the first operand to match the JabRef
code style.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* Add mavenlocal

* Refine JBang checks

* Pin JBang scripts to Java 25

* Refine check worfklow

* Use citationKey:field segments in check errorformat output

Emit findings as `file:line:column:citationKey[:field]: message` in both
the integrity and consistency error-format writers. Entry-level findings
carry only the citation key; field-level findings add the field name.

Document the format as req~jabkit.cli.check-errorformat-output~1 and link
it from the writers.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* Factor out file import/validation in the check commands

Extract the duplicated import-and-validate block from CheckConsistency
and CheckIntegrity into JabKit.importBibtexLibrary, which returns an
ImportOutcome record. Callers no longer unwrap an Optional.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* Drop the integrity check txt writer, alias txt to errorformat

Remove IntegrityCheckResultTxtWriter. The integrity check's `txt` output
format is now an alias for `errorformat`; `csv` is unchanged. The
consistency check keeps its tabular `txt` output.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* Format switch case labels in CheckIntegrity

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* Split launcher --help checks into separate steps

One step per launcher in the jbang-check action, so a failure points
precisely at the offending launcher.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* Localize the jabkit subcommand messages

Wrap the "Specify a subcommand ..." and PDF format-option messages in
Localization.lang and add the keys to JabRef_en.properties.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* Make pdf update fail with a non-zero exit on a bad --format

Print the --format validation error to System.err and convert PdfUpdate
to Callable<Integer> so usage and import errors exit with code 2.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* Send CLI errors to System.err with non-zero exit codes

Route user-facing error and usage messages in the jabkit commands to
System.err, and convert the remaining Runnable commands to
Callable<Integer> so failures exit with code 2 instead of 0.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* Fix consistency errorformat writer test assertions

Update the expected lines to the file:line:column:citationKey:field
format the writer now produces.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* Add JBang setup for jbang check

* More powerful runner for :jabkit:runJabKitPortableSmokeTest

* Fix NPE

* Fix handling of "null"

* Signal integrity findings with a non-zero exit code

CheckIntegrity.execute() always returned 0, so `jabkit check integrity`
and `jabkit check FILE` could not report integrity findings via exit
status, letting CI pass with integrity problems.

Return 1 when findings exist (0 = none, 1 = findings, 2/3 = error),
matching CheckConsistency. The parent `check` command propagates the
worst code via Math.max.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* Fix test

* Remove non-useful test

---------

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants