Skip to content

Ship a proper Claude Code plugin (marketplace) instead of the copy-paste bundle #34

Description

@ichoosetoaccept

Summary

The Claude Code install path (./install.sh --agent claude-code --target-dir <workspace>) vendors a static copy of the hook bundle into each project's .claude/ directory and writes a .claude/settings.json entry with a relative path to run-hook.sh. There is no update mechanism — if the bundle is improved or fixed, users never find out. The relative path also breaks depending on Claude Code's working directory at hook-execution time, producing a non-blocking "No such file or directory" error on every Bash call.

By contrast, the Cursor install path ships as a proper plugin (/add-plugin 1password) that's managed by Cursor's plugin system. Claude Code has had an equivalent plugin/marketplace system (.claude-plugin/marketplace.json + plugin.json) since early 2025, and many plugins (agentmemory, honcho, parallel, axiom, etc.) are already published and auto-update via ~/.claude/settings.json extraKnownMarketplaces + enabledPlugins.

Filing as a feature request: publish the 1Password Environments validator as a Claude Code plugin so it auto-updates and resolves its own paths correctly.

The current install is fragile in two ways

1. No update path (vendored static copy)

install.sh copies bin/, lib/, adapters/, and hooks/ into <workspace>/.claude/claude-code-1password-hooks-bundle/ and commits them. The VERSION file says 1.0.0. If you ship a fix (e.g., for #28 or the path bug below), users have to:

  1. Notice the fix exists (no notification mechanism).
  2. Re-run install.sh or manually copy files over.
  3. Commit the update per-project.

In practice, nobody does this. The vendored copy drifts forever.

2. Relative path breaks when Claude Code runs hooks from a non-root CWD

The generated .claude/settings.json:

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": ".claude/claude-code-1password-hooks-bundle/bin/run-hook.sh 1password-validate-mounted-env-files"
          }
        ]
      }
    ]
  }
}

Claude Code does not always execute hook commands from the project root — it uses the current working directory of the session, which may be a subdirectory. When the CWD isn't the project root, the relative path .claude/claude-code-1password-hooks-bundle/bin/run-hook.sh doesn't resolve:

PreToolUse:Bash hook error
Failed with non-blocking status code: /bin/sh: .claude/claude-code-1password-hooks-bundle/bin/run-hook.sh: No such file or directory

The hook is fail-open (non-blocking), so the Bash call proceeds anyway — but the validator is silently skipped and the user sees error noise on every single command.

The fix for this specific symptom is to use $CLAUDE_PROJECT_DIR (or an absolute path), but that variable has its own bug (anthropics/claude-code#33815 — empty/unset in some versions). A plugin would sidestep both issues: Claude Code resolves ${CLAUDE_PLUGIN_ROOT} reliably for plugin hooks.

Reproducer (path bug)

  1. Run ./install.sh --agent claude-code --target-dir <workspace>.
  2. Open a Claude Code session in a subdirectory of <workspace> (or just let the session change directories during work).
  3. Run any Bash command.
  4. Observe: PreToolUse:Bash hook error: .claude/claude-code-1password-hooks-bundle/bin/run-hook.sh: No such file or directory on every Bash call. Hook is silently skipped.

Proposed direction

Publish the validator as a Claude Code plugin via a marketplace, mirroring the Cursor plugin approach:

  1. Add a .claude-plugin/marketplace.json at the repo root (or a subdirectory) declaring the plugin.
  2. Add a .claude-plugin/plugin.json with the plugin metadata.
  3. Add a hooks/hooks.json using ${CLAUDE_PLUGIN_ROOT} for command paths — Claude Code resolves this reliably for plugin-scoped hooks, unlike the relative-path or $CLAUDE_PROJECT_DIR approaches.
  4. Users install once: /plugin marketplace add 1Password/agent-hooks then /plugin install 1password@agent-hooks (or equivalent). Auto-update keeps the hook current.

This eliminates both problems: no per-project vendoring, no path resolution failures, and fixes propagate automatically.

Related

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions