microsoft / microsoft/apm

[BUG] Claude hook bundles drop nested package.json module metadata

Open
#2,950 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

status/accepted triage/recommended type/bug
Dominant language
Python
Stars
3.9k
Forks
365
Avg merge
1d 17h
Merged PRs (30d)
132

Description

Describe the bug

When a Claude hook package places a JavaScript hook and its module metadata in a nested directory, APM deploys the JavaScript file but drops the nested package.json. APM then generates a CommonJS sidecar at the higher hook root, so Node executes the nested .js file with the wrong module semantics.

To Reproduce

Steps to reproduce the behavior:

  1. Create a Claude hook package with this layout:

    hooks/
      hooks.json
      esm/
        entry.js
        package.json
    
  2. Set hooks/hooks.json to:

    {
      "hooks": {
        "SessionStart": [
          {
            "hooks": [
              {
                "type": "command",
                "command": "node ./esm/entry.js"
              }
            ]
          }
        ]
      }
    }
    
  3. Set hooks/esm/entry.js to an ES module, for example:

    import { writeFileSync } from "node:fs";
    writeFileSync(process.argv[2], "ok\\n");
    
  4. Set hooks/esm/package.json to:

    { "type": "module" }
    
  5. Install the package into a project with Claude hooks enabled by running apm install.

  6. Inspect the deployed hook under .claude/hooks/<package>/hooks/esm/.

  7. Observe that entry.js is present, but esm/package.json is missing. The generated .claude/hooks/<package>/hooks/package.json contains {"type": "commonjs"}.

  8. Run the deployed hook with Node and observe an import syntax error because the file is treated as CommonJS.

Expected behavior

APM should preserve the nested hooks/esm/package.json, or generate equivalent module metadata at the deployed esm boundary. Running the deployed entry.js should use ES module semantics and complete successfully.

Environment (please complete the following information):

  • OS: Windows
  • Python Version: 3.12.13
  • APM Version: 0.30.0

Logs

APM reports no installation error. Executing the deployed JavaScript file produces a Node module syntax error such as:

SyntaxError: Cannot use import statement outside a module

Additional context

src/apm_cli/integration/hook_bundle.py excludes every file named package.json in iter_deployable_hook_bundle_files, regardless of target. Claude passes exclude_json_files=False, but that option only controls the broader .json suffix filter and does not restore the skipped filename.

_hook_module_type searches the hook source root and its ancestors, not nested metadata directories. With no package-root or hooks/package.json, it therefore defaults to CommonJS and writes one sidecar at the deployed hook root.

This is distinct from #2824, which concerns missing .claude-plugin/plugin.json and other files outside the selected hook script bundle. This defect concerns module metadata inside an otherwise selected bundle.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Read src/apm_cli/integration/hook_bundle.py, starting with iter_deployable_hook_bundle_files and _hook_module_type, then reproduce the nested hooks/esm layout with apm install. Done means the deployed esm/package.json is preserved or equivalent metadata is placed at the esm boundary, and entry.js runs with ES module semantics.

Written by the indexing model from the issue text.

Assessment

Tech stack
javascript, python
Domain
cli, tooling
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
78/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.