Skip to content

ModPorter-AI entity detection (vibe-kanban)#1027

Merged
anchapin merged 4 commits intomainfrom
vk/28d5-modporter-ai-ent
Apr 10, 2026
Merged

ModPorter-AI entity detection (vibe-kanban)#1027
anchapin merged 4 commits intomainfrom
vk/28d5-modporter-ai-ent

Conversation

@anchapin
Copy link
Copy Markdown
Owner

@anchapin anchapin commented Apr 10, 2026

Work on this GitHub issue #1023

Summary by Sourcery

Adopt an AST-first Java analysis pipeline for mod conversion, adding entity detection and conversion into the Bedrock add-on flow and exposing detection counts in the CLI results.

New Features:

  • Detect and convert Java mod entities via AST analysis and include them in generated Bedrock behavior and resource packs.
  • Expose counts of detected blocks and entities in the conversion result and CLI logging for both ai-engine and modporter CLIs.

Enhancements:

  • Fallback to the previous MVP-based analysis path when AST analysis fails to keep conversions resilient.
  • Improve logging around primary entities, textures, and entity conversion output during the conversion process.

Build:

  • Add Husky Git hooks scaffolding files for local Git workflow integration.

…convert_mod()

- Changed convert_mod() to use AST-first analysis which detects ALL entities
- Falls back to MVP analysis only if AST fails
- Detected entities are now converted via EntityConverter and written to addon
- Added entities_detected and blocks_detected to result for audit visibility
- Fixes #1023: entity detection regression (was detecting 1/entity, now detects all)
…t() as primary path in convert_mod()

The remaining modified files are unrelated to this fix (frontend assets, binary files, etc.).
Copilot AI review requested due to automatic review settings April 10, 2026 02:30
@sourcery-ai
Copy link
Copy Markdown

sourcery-ai bot commented Apr 10, 2026

Reviewer's Guide

Switches the mod conversion CLI to an AST-first Java analysis pipeline that can detect entities and other features, then converts detected entities into Bedrock behavior/resource pack content and surfaces detection metrics in the CLI output, with mirrored changes in both ai-engine and modporter entrypoints and some repo tooling/assets updates.

Sequence diagram for AST-first mod conversion with entity support

sequenceDiagram
    actor User
    participant CLI as convert_mod
    participant FS as FileSystem
    participant JavaAnalyzerAgent
    participant BedrockBuilder as BedrockBuilderAgent
    participant EntityConverter
    participant PackagingAgent

    User->>CLI: invoke convert_mod(jar_path, output_dir)
    CLI->>FS: resolve jar_file path

    Note over CLI,JavaAnalyzerAgent: Step 1 - AST-first analysis
    CLI->>JavaAnalyzerAgent: analyze_jar_with_ast(jar_file)
    alt AST analysis succeeds
        JavaAnalyzerAgent-->>CLI: ast_analysis_result {success, features, assets}
        CLI->>CLI: features = ast_analysis_result.features
        CLI->>CLI: entities = features.entities
        CLI->>CLI: blocks = features.blocks
        CLI->>CLI: registry_name from first block or default
        CLI->>CLI: texture_path from assets.block_textures or None
    else AST analysis fails
        JavaAnalyzerAgent-->>CLI: ast_analysis_result {success:false, error}
        CLI->>CLI: log warning AST analysis failed
        CLI->>JavaAnalyzerAgent: analyze_jar_for_mvp(jar_file)
        JavaAnalyzerAgent-->>CLI: analysis_result {success, registry_name, texture_path}
        alt MVP analysis succeeds
            CLI->>CLI: registry_name = analysis_result.registry_name
            CLI->>CLI: texture_path = analysis_result.texture_path
            CLI->>CLI: entities = []
            CLI->>CLI: features = {}
        else MVP analysis fails
            CLI->>CLI: raise RuntimeError(Analysis failed)
        end
    end

    Note over CLI,BedrockBuilder: Step 2 - Build Bedrock add-on
    CLI->>FS: create TemporaryDirectory()
    CLI->>BedrockBuilder: build_behavior_and_resource_packs(temp_dir, registry_name, texture_path)
    BedrockBuilder-->>CLI: build_result {behavior_pack_dir, resource_pack_dir, success}
    alt build failed
        CLI->>CLI: raise RuntimeError(Bedrock build failed)
    end

    Note over CLI,EntityConverter: Step 2b - Entity conversion (if any)
    alt entities list is not empty
        CLI->>EntityConverter: convert_entities(entities)
        EntityConverter-->>CLI: bedrock_entities
        alt bedrock_entities not empty
            CLI->>FS: resolve bp_path from build_result.behavior_pack_dir
            CLI->>FS: resolve rp_path from build_result.resource_pack_dir
            CLI->>EntityConverter: write_entities_to_disk(bedrock_entities, bp_path, rp_path)
            EntityConverter-->>CLI: written {entities, behaviors, animations}
        else no bedrock_entities
            CLI->>CLI: skip writing entities
        end
    else no entities
        CLI->>CLI: skip entity conversion
    end

    Note over CLI,PackagingAgent: Step 3 - Package as .mcaddon
    CLI->>PackagingAgent: package_addon(temp_dir, output_dir)
    PackagingAgent-->>CLI: package_result {output_path, file_size, validation}

    CLI->>CLI: result = {output_file, file_size, registry_name,
    CLI->>CLI:             entities_detected = len(entities),
    CLI->>CLI:             blocks_detected = len(features.blocks),
    CLI->>CLI:             validation}

    CLI-->>User: return result and log counts of blocks and entities
Loading

File-Level Changes

Change Details Files
Adopt AST-first Java analysis for mod conversion with fallback to legacy MVP analysis.
  • Instantiate JavaAnalyzerAgent and call analyze_jar_with_ast before the previous analyze_jar_for_mvp path.
  • On AST analysis failure, log a warning and fall back to analyze_jar_for_mvp, preserving previous behavior.
  • On AST success, extract features, entities, blocks, assets, and derive registry_name and texture_path from the AST results instead of the legacy analysis output.
  • Ensure safe defaults when AST succeeds (unknown:block, optional texture) or when both analyses return minimal data.
ai-engine/cli/main.py
modporter/cli/main.py
Add entity conversion and file emission into built Bedrock behavior/resource packs when entities are detected.
  • After building the Bedrock add-on, check for detected entities and, if present, instantiate EntityConverter and call convert_entities.
  • Resolve behavior_pack and resource_pack paths from build_result (falling back to the temp_dir) and pass them to write_entities_to_disk.
  • Log counts of written entities, behaviors, and animations for visibility in the CLI output.
ai-engine/cli/main.py
modporter/cli/main.py
Expose feature detection metrics in the conversion result and logs.
  • Augment the result dict with entities_detected and blocks_detected derived from the AST features and entities list.
  • Add a final log line summarizing detected blocks and entities alongside existing output_file and file_size logs.
ai-engine/cli/main.py
modporter/cli/main.py
Introduce Husky Git hooks scaffolding and adjust frontend story assets.
  • Add placeholder Husky hook scripts for post-checkout, post-commit, post-merge, and pre-push to enable repo-level automation.
  • Update or normalize Storybook asset SVGs used in the frontend (github.svg, tutorials.svg, youtube.svg).
.husky/_/post-checkout
.husky/_/post-commit
.husky/_/post-merge
.husky/_/pre-push
frontend/src/stories/assets/github.svg
frontend/src/stories/assets/tutorials.svg
frontend/src/stories/assets/youtube.svg

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link
Copy Markdown

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

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

Hey - I've found 1 issue, and left some high level feedback:

  • The new AST-first analysis and entity-conversion flow is duplicated almost verbatim between ai-engine/cli/main.py and modporter/cli/main.py; consider extracting this into a shared helper function to keep the logic DRY and reduce the chance of these paths diverging over time.
  • The log line logger.info(f"Primary entity: {registry_name}") is using a registry_name that is derived from the first block rather than an entity, which can be misleading; consider renaming the message or using an entity-derived identifier when available.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The new AST-first analysis and entity-conversion flow is duplicated almost verbatim between `ai-engine/cli/main.py` and `modporter/cli/main.py`; consider extracting this into a shared helper function to keep the logic DRY and reduce the chance of these paths diverging over time.
- The log line `logger.info(f"Primary entity: {registry_name}")` is using a `registry_name` that is derived from the first block rather than an entity, which can be misleading; consider renaming the message or using an entity-derived identifier when available.

## Individual Comments

### Comment 1
<location path="modporter/cli/main.py" line_range="76-85" />
<code_context>
+        ast_analysis_result = java_analyzer.analyze_jar_with_ast(str(jar_file))
</code_context>
<issue_to_address>
**suggestion:** There is substantial duplicated AST-analysis and extraction logic that could be shared between CLIs

The AST-first analysis flow (invocation, feature extraction, block/entity handling, texture selection) is almost identical between `ai-engine/cli/main.py` and `modporter/cli/main.py`. Consider extracting this into a shared helper (e.g., returning `registry_name`, `texture_path`, `entities`, `features`) so changes to analysis behavior stay consistent and avoid drift between the CLIs.

Suggested implementation:

```python
        # Step 1: Analyze the JAR file using shared AST-first analysis flow (detects ALL entities)
        logger.info("Step 1: Analyzing Java mod (AST-first)...")
        java_analyzer = JavaAnalyzerAgent()

        # Use shared AST-first analysis helper so behavior is consistent across CLIs.
        # The helper is responsible for:
        # - Running AST-first analysis
        # - Falling back to MVP analysis if AST fails
        # - Returning a normalized analysis_result dict (including entities/features/texture/registry info)
        analysis_result = run_ast_first_analysis(java_analyzer, jar_file, logger)

        if not analysis_result.get("success", False):

```

To fully implement the refactor and actually share the AST-first analysis flow between CLIs, you should also:

1. **Introduce a shared helper** (for example in a new module like `shared/cli/analysis_flow.py` or similar, depending on your package layout):

   ```python
   # shared/cli/analysis_flow.py
   from pathlib import Path
   from typing import Any, Dict

   def run_ast_first_analysis(java_analyzer, jar_file: Path, logger) -> Dict[str, Any]:
       """
       Shared AST-first analysis flow used by both ai-engine and modporter CLIs.

       Responsibilities:
       - Run AST-based JAR analysis first
       - Fall back to MVP analysis if AST fails
       - Return a normalized analysis_result dict containing:
         - success: bool
         - entities / blocks / items / etc.
         - features
         - texture_path / registry_name (if your analyzers populate these)
       """
       # Try AST analysis first - this detects all entities, blocks, items, etc.
       ast_analysis_result = java_analyzer.analyze_jar_with_ast(str(jar_file))

       if ast_analysis_result.get("success", False):
           return ast_analysis_result

       # Fall back to MVP analysis if AST fails
       logger.warning("AST analysis failed, falling back to MVP analysis...")
       mvp_result = java_analyzer.analyze_jar_for_mvp(str(jar_file))

       # Ensure we always return a dict with a success flag and normalized keys
       if not mvp_result.get("success", False):
           return mvp_result

       return mvp_result
   ```

   Adjust the normalization logic to ensure `registry_name`, `texture_path`, `entities`, and `features` are consistently present on the returned dict (or adapt it to return those values directly if the rest of your pipeline expects a tuple instead).

2. **Import the helper into `modporter/cli/main.py`** at the top of the file:

   ```python
   from shared.cli.analysis_flow import run_ast_first_analysis
   ```

   Update the import path to match your actual shared package/module structure.

3. **Update `ai-engine/cli/main.py`** to use the same helper instead of its own inlined AST-first analysis flow. Replace its AST + MVP logic with:

   ```python
   analysis_result = run_ast_first_analysis(java_analyzer, jar_file, logger)
   if not analysis_result.get("success", False):
       # existing error handling
   ```

4. If your current CLIs unpack `registry_name`, `texture_path`, `entities`, and `features` separately instead of using the raw `analysis_result` dict, you can adjust `run_ast_first_analysis` to return a tuple:

   ```python
   registry_name, texture_path, entities, features = run_ast_first_analysis(java_analyzer, jar_file, logger)
   ```

   and then update both CLIs accordingly so they all rely on this shared helper for AST-first analysis behavior.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment on lines +76 to +85
ast_analysis_result = java_analyzer.analyze_jar_with_ast(str(jar_file))

registry_name = analysis_result.get("registry_name", "unknown_block")
texture_path = analysis_result.get("texture_path")

logger.info(f"Found block: {registry_name}")
if not ast_analysis_result.get("success", False):
# Fall back to MVP analysis if AST fails
logger.warning("AST analysis failed, falling back to MVP analysis...")
analysis_result = java_analyzer.analyze_jar_for_mvp(str(jar_file))
if not analysis_result.get("success", False):
raise RuntimeError(
f"Analysis failed: {analysis_result.get('error', 'Unknown error')}"
)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

suggestion: There is substantial duplicated AST-analysis and extraction logic that could be shared between CLIs

The AST-first analysis flow (invocation, feature extraction, block/entity handling, texture selection) is almost identical between ai-engine/cli/main.py and modporter/cli/main.py. Consider extracting this into a shared helper (e.g., returning registry_name, texture_path, entities, features) so changes to analysis behavior stay consistent and avoid drift between the CLIs.

Suggested implementation:

        # Step 1: Analyze the JAR file using shared AST-first analysis flow (detects ALL entities)
        logger.info("Step 1: Analyzing Java mod (AST-first)...")
        java_analyzer = JavaAnalyzerAgent()

        # Use shared AST-first analysis helper so behavior is consistent across CLIs.
        # The helper is responsible for:
        # - Running AST-first analysis
        # - Falling back to MVP analysis if AST fails
        # - Returning a normalized analysis_result dict (including entities/features/texture/registry info)
        analysis_result = run_ast_first_analysis(java_analyzer, jar_file, logger)

        if not analysis_result.get("success", False):

To fully implement the refactor and actually share the AST-first analysis flow between CLIs, you should also:

  1. Introduce a shared helper (for example in a new module like shared/cli/analysis_flow.py or similar, depending on your package layout):

    # shared/cli/analysis_flow.py
    from pathlib import Path
    from typing import Any, Dict
    
    def run_ast_first_analysis(java_analyzer, jar_file: Path, logger) -> Dict[str, Any]:
        """
        Shared AST-first analysis flow used by both ai-engine and modporter CLIs.
    
        Responsibilities:
        - Run AST-based JAR analysis first
        - Fall back to MVP analysis if AST fails
        - Return a normalized analysis_result dict containing:
          - success: bool
          - entities / blocks / items / etc.
          - features
          - texture_path / registry_name (if your analyzers populate these)
        """
        # Try AST analysis first - this detects all entities, blocks, items, etc.
        ast_analysis_result = java_analyzer.analyze_jar_with_ast(str(jar_file))
    
        if ast_analysis_result.get("success", False):
            return ast_analysis_result
    
        # Fall back to MVP analysis if AST fails
        logger.warning("AST analysis failed, falling back to MVP analysis...")
        mvp_result = java_analyzer.analyze_jar_for_mvp(str(jar_file))
    
        # Ensure we always return a dict with a success flag and normalized keys
        if not mvp_result.get("success", False):
            return mvp_result
    
        return mvp_result

    Adjust the normalization logic to ensure registry_name, texture_path, entities, and features are consistently present on the returned dict (or adapt it to return those values directly if the rest of your pipeline expects a tuple instead).

  2. Import the helper into modporter/cli/main.py at the top of the file:

    from shared.cli.analysis_flow import run_ast_first_analysis

    Update the import path to match your actual shared package/module structure.

  3. Update ai-engine/cli/main.py to use the same helper instead of its own inlined AST-first analysis flow. Replace its AST + MVP logic with:

    analysis_result = run_ast_first_analysis(java_analyzer, jar_file, logger)
    if not analysis_result.get("success", False):
        # existing error handling
  4. If your current CLIs unpack registry_name, texture_path, entities, and features separately instead of using the raw analysis_result dict, you can adjust run_ast_first_analysis to return a tuple:

    registry_name, texture_path, entities, features = run_ast_first_analysis(java_analyzer, jar_file, logger)

    and then update both CLIs accordingly so they all rely on this shared helper for AST-first analysis behavior.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Updates the CLI conversion pipeline to address the entity-detection regression in v2 (Issue #1023) by routing analysis through AST-first detection and attempting to convert detected entities into the generated Bedrock add-on.

Changes:

  • Switch convert_mod() in both CLIs to try analyze_jar_with_ast() first, with MVP fallback.
  • Add an entity conversion step using EntityConverter and surface detected entity/block counts in CLI output.
  • Add/convert multiple binary assets to Git LFS pointers (plus Git LFS hook scripts and miscellaneous binaries).

Reviewed changes

Copilot reviewed 6 out of 18 changed files in this pull request and generated 10 comments.

Show a summary per file
File Description
modporter/cli/main.py AST-first analysis + entity conversion integration in the main ModPorter CLI pipeline.
ai-engine/cli/main.py Same AST-first/entity conversion pipeline updates for the AI engine CLI entry point.
verification_spinner.png Adds a Git LFS–tracked screenshot artifact.
node-installer.msi Adds a Git LFS–tracked MSI binary.
tests/fixtures/expected_output/.../copper_block.png Converts fixture image to Git LFS pointer.
`frontend/src/stories/assets/*.(svg png)`
`.husky/_/(pre-push post-merge

Comment on lines +138 to +142
bp_path = (
Path(build_result.get("behavior_pack_dir", temp_dir)) / "behavior_pack"
)
rp_path = (
Path(build_result.get("resource_pack_dir", temp_dir)) / "resource_pack"
Copy link

Copilot AI Apr 10, 2026

Choose a reason for hiding this comment

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

BedrockBuilderAgent.build_block_addon_mvp() already returns absolute paths to the pack directories (e.g., .../behavior_pack and .../resource_pack). Appending another /"behavior_pack" and /"resource_pack" here will create nested directories (behavior_pack/behavior_pack) so the written entity files won’t be included by the packager (which zips temp_dir/behavior_pack). Use Path(build_result["behavior_pack_dir"]) / Path(build_result["resource_pack_dir"]) directly (and only fall back to Path(temp_dir)/... if those keys are missing).

Suggested change
bp_path = (
Path(build_result.get("behavior_pack_dir", temp_dir)) / "behavior_pack"
)
rp_path = (
Path(build_result.get("resource_pack_dir", temp_dir)) / "resource_pack"
behavior_pack_dir = build_result.get("behavior_pack_dir")
resource_pack_dir = build_result.get("resource_pack_dir")
bp_path = (
Path(behavior_pack_dir)
if behavior_pack_dir
else Path(temp_dir) / "behavior_pack"
)
rp_path = (
Path(resource_pack_dir)
if resource_pack_dir
else Path(temp_dir) / "resource_pack"

Copilot uses AI. Check for mistakes.
Comment on lines +131 to +136
# If we detected entities, convert them and add to the addon
if entities:
logger.info(f"Step 2b: Converting {len(entities)} entities...")
entity_converter = EntityConverter()
bedrock_entities = entity_converter.convert_entities(entities)

Copy link

Copilot AI Apr 10, 2026

Choose a reason for hiding this comment

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

The entities list coming from JavaAnalyzerAgent.analyze_jar_with_ast() contains items like {name, registry_name, methods, ...} (see JavaAnalyzerAgent._extract_features_from_ast / bytecode extraction), but EntityConverter expects each entry to have at least id (and ideally namespace). As written, _convert_java_entity() will default every entity to modporter:unknown_entity, causing dictionary key collisions and effectively leaving you with only one entity converted/written. Map analyzer entities into the schema EntityConverter expects (e.g., derive id from registry_name or name, and namespace from ast_analysis_result["mod_info"]["name"]) before calling convert_entities().

Copilot uses AI. Check for mistakes.
Comment on lines +107 to +108
if "block_textures" in assets and assets["block_textures"]:
texture_path = assets["block_textures"][0]
Copy link

Copilot AI Apr 10, 2026

Choose a reason for hiding this comment

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

JavaAnalyzerAgent.analyze_jar_with_ast() populates assets with keys {textures, models, sounds, other} (see _analyze_assets_from_jar), but this code looks for assets["block_textures"], which will never be present. As a result texture_path will always stay None on the AST-success path. Filter assets["textures"] for a likely block texture (e.g., containing /textures/block/), or reuse the MVP _find_block_texture logic.

Suggested change
if "block_textures" in assets and assets["block_textures"]:
texture_path = assets["block_textures"][0]
textures = assets.get("textures", [])
if textures:
block_textures = [
texture
for texture in textures
if "/textures/block/" in texture or "textures/block/" in texture
]
texture_path = block_textures[0] if block_textures else textures[0]

Copilot uses AI. Check for mistakes.
Comment on lines +83 to +85
raise RuntimeError(
f"Analysis failed: {analysis_result.get('error', 'Unknown error')}"
)
Copy link

Copilot AI Apr 10, 2026

Choose a reason for hiding this comment

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

On AST failure fallback, JavaAnalyzerAgent.analyze_jar_for_mvp() reports details in an errors list, not an error field. Using analysis_result.get('error', ...) will typically surface Unknown error even when real errors exist. Prefer analysis_result.get('error') OR join analysis_result.get('errors', []) for a useful message.

Suggested change
raise RuntimeError(
f"Analysis failed: {analysis_result.get('error', 'Unknown error')}"
)
error_message = analysis_result.get("error")
if not error_message:
errors = analysis_result.get("errors", [])
if isinstance(errors, list) and errors:
error_message = "; ".join(str(error) for error in errors)
else:
error_message = "Unknown error"
raise RuntimeError(f"Analysis failed: {error_message}")

Copilot uses AI. Check for mistakes.
Comment on lines 126 to 129
@@ -99,6 +128,29 @@ def convert_mod(jar_path: str, output_dir: str = None) -> Dict[str, Any]:
f"Bedrock build failed: {build_result.get('error', 'Unknown error')}"
)
Copy link

Copilot AI Apr 10, 2026

Choose a reason for hiding this comment

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

BedrockBuilderAgent.build_block_addon_mvp() returns failures via an errors list (it does not set an error field). This branch will usually raise Bedrock build failed: Unknown error and hide the real cause. Use build_result.get('error') OR join build_result.get('errors', []) when constructing the exception message.

Copilot uses AI. Check for mistakes.
Comment on lines 71 to 77
# Step 1: Analyze the JAR file using AST-first approach (detects ALL entities)
logger.info("Step 1: Analyzing Java mod (AST-first)...")
java_analyzer = JavaAnalyzerAgent()
analysis_result = java_analyzer.analyze_jar_for_mvp(str(jar_file))

if not analysis_result.get("success", False):
raise RuntimeError(f"Analysis failed: {analysis_result.get('error', 'Unknown error')}")
# Try AST analysis first - this detects all entities, blocks, items, etc.
ast_analysis_result = java_analyzer.analyze_jar_with_ast(str(jar_file))

Copy link

Copilot AI Apr 10, 2026

Choose a reason for hiding this comment

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

This change introduces a new primary execution path (AST-success) plus entity conversion/writing, but the existing CLI unit tests primarily mock analyze_jar_for_mvp() and don’t cover the AST-success branch or entity conversion output. Add/adjust tests to mock analyze_jar_with_ast() returning multiple entities and assert that EntityConverter.write_entities_to_disk() is called with the correct pack directories and that the result reports entities_detected accordingly.

Copilot uses AI. Check for mistakes.
Comment on lines +124 to +128
bp_path = (
Path(build_result.get("behavior_pack_dir", temp_dir)) / "behavior_pack"
)
rp_path = (
Path(build_result.get("resource_pack_dir", temp_dir)) / "resource_pack"
Copy link

Copilot AI Apr 10, 2026

Choose a reason for hiding this comment

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

BedrockBuilderAgent.build_block_addon_mvp() already returns absolute paths to the pack directories. Appending another /"behavior_pack" and /"resource_pack" here creates nested directories (behavior_pack/behavior_pack), so written entity files won’t be packaged (the packager zips temp_dir/behavior_pack). Use the returned behavior_pack_dir / resource_pack_dir paths directly.

Suggested change
bp_path = (
Path(build_result.get("behavior_pack_dir", temp_dir)) / "behavior_pack"
)
rp_path = (
Path(build_result.get("resource_pack_dir", temp_dir)) / "resource_pack"
bp_path = Path(
build_result.get("behavior_pack_dir", Path(temp_dir) / "behavior_pack")
)
rp_path = Path(
build_result.get("resource_pack_dir", Path(temp_dir) / "resource_pack")

Copilot uses AI. Check for mistakes.
Comment on lines +77 to +82
# AST analysis succeeded - extract comprehensive features
features = ast_analysis_result.get("features", {})
entities = features.get("entities", [])
blocks = features.get("blocks", [])

logger.info(f"AST analysis found: {len(blocks)} blocks, {len(entities)} entities")
Copy link

Copilot AI Apr 10, 2026

Choose a reason for hiding this comment

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

The entities extracted from analyze_jar_with_ast() are analyzer feature dicts (e.g., {name, registry_name, methods}) but EntityConverter expects at least id (and typically namespace). With the current input, all entities default to modporter:unknown_entity, causing key collisions and effectively converting only one entity. Transform analyzer entities into the EntityConverter schema (derive id/namespace) before calling convert_entities().

Copilot uses AI. Check for mistakes.
Comment on lines +93 to +94
if "block_textures" in assets and assets["block_textures"]:
texture_path = assets["block_textures"][0]
Copy link

Copilot AI Apr 10, 2026

Choose a reason for hiding this comment

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

analyze_jar_with_ast() asset analysis returns assets["textures"] (see JavaAnalyzerAgent._analyze_assets_from_jar), not assets["block_textures"]. This means texture_path will remain None on the AST-success path. Update this to select an appropriate entry from assets["textures"] (e.g., one under /textures/block/).

Suggested change
if "block_textures" in assets and assets["block_textures"]:
texture_path = assets["block_textures"][0]
textures = assets.get("textures", [])
if textures:
texture_path = next(
(
texture
for texture in textures
if "/textures/block/" in texture or "textures/block/" in texture
),
textures[0],
)

Copilot uses AI. Check for mistakes.
Comment on lines +1 to +3
#!/bin/sh
command -v git-lfs >/dev/null 2>&1 || { echo >&2 "\nThis repository is configured for Git LFS but 'git-lfs' was not found on your path. If you no longer wish to use Git LFS, remove this hook by deleting the 'pre-push' file in the hooks directory (set by 'core.hookspath'; usually '.git/hooks').\n"; exit 2; }
git lfs pre-push "$@"
Copy link

Copilot AI Apr 10, 2026

Choose a reason for hiding this comment

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

These Git LFS hook scripts under .husky/_/ duplicate the existing hooks in .husky/ and will not run unless core.hooksPath is configured to point at .husky/_. This looks like an accidental commit from running git lfs install with a different local hooksPath; either remove these duplicates or update the repo’s Husky/Git hook configuration so there is a single, consistent hook location.

Copilot uses AI. Check for mistakes.
…n main.py

Resolved merge conflicts in convert_mod function:
- Unified AST-first analysis approach from PR with entity texture/model extraction
- Preserved entity conversion logic with texture enrichment
- Kept cleaner logging from PR while maintaining entity-only mod handling
When AST analysis succeeds, the block registry_name was derived only from
the class name (e.g., 'emerald_block') without the namespace prefix.
This caused test_cli_integration_e2e to fail because it expected
'cli_test:' to be in the registry name.

The fix extracts mod_id from mod_info (which comes from fabric.mod.json)
and prefixes the block registry name when it's not already namespaced.

Note: Fixed both ai-engine/cli/main.py and modporter/cli/main.py as they
both contain the convert_mod function used by different test imports.
@anchapin anchapin merged commit 775e339 into main Apr 10, 2026
25 checks passed
@anchapin anchapin deleted the vk/28d5-modporter-ai-ent branch April 10, 2026 03:55
anchapin added a commit that referenced this pull request Apr 10, 2026
- Add audit v3 report: 8/8 mods pass, 54.7% textures, 0% models/recipes
- Update ROADMAP.md: replace Feb 2025 version with current 11-week launch plan
- Update .factory/tasks.md: current sprint status and audit summary
- Entity fix #1027 partially working (Create: 1→9 entities)
- P0 gaps identified: models (4,806), recipes (3,852), BlockEntity classification
- B2B readiness: ~25-30% weighted, targeting 60% after Week 3-4 sprint
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.

3 participants