Skip to content

Fix plugin install: invalid hooks schema, stale pinned marketplace source - #31

Merged
developer-ainative merged 1 commit into
mainfrom
fix/plugin-install-broken
Sep 25, 2026
Merged

developer-ainative merged 1 commit into
mainfrom
fix/plugin-install-broken

Conversation

@developer-ainative

Copy link
Copy Markdown
Contributor

The plugin has never actually installed successfully via /plugin install or claude plugin install — reproduced the exact failure locally and root-caused it against the real Claude Code plugin docs (docs.claude.com → code.claude.com redirect).

Two real install blockers, both fixed

1. Invalid hooks schema. plugin.json mapped event names directly to bare script paths ("Stop": "hooks/session-end.sh"). The real schema requires:

{ "EventName": [{ "hooks": [{ "type": "command", "command": "..." }] }] }

claude plugin install failed on every attempt with hooks: Invalid input.

2. Stale pinned marketplace source. marketplace.json's plugin entry used { "source": "url", "url": "...", "sha": "9debdb2..." } — fetching a pinned commit over the network — instead of a relative path. Since the plugin lives in this same repo as the marketplace file, the docs say the correct source is ".". The url+sha form meant even a fix pushed to main would never actually be picked up by new installs.

Also fixed

  • Wrong hook events: session-start.sh ran on PreToolUse (fires on every tool call) with a manual sentinel-file workaround for "once per session" — switched to the real SessionStart event, which already fires exactly once. session-end.sh moved from Stop (end of every turn) to SessionEnd (end of session).
  • Added the missing author field.

Flagged, not fixed

The top-level "statusline" field in plugin.json isn't a real manifest field — Claude Code silently ignores it (claude plugin validate confirms: "Unknown field 'statusline'"). The only real status-line hook is settings.subagentStatusLine, scoped to subagents, not the main session. statusline/zerodb-status.sh (live memory-sync indicator) has never actually been wired up and can't be fixed with a manifest change — needs a product decision. Left in place rather than silently deleting a documented feature intent.

Verified locally, end to end

claude plugin marketplace add ./this-repo
claude plugin install zerodb-memory@ainative-plugins   # ✔ succeeds (previously failed)
claude plugin details zerodb-memory                     # Hooks (2): SessionStart, SessionEnd
claude plugin uninstall / marketplace remove             # clean

…urce

The plugin has never actually installed via /plugin install or
claude plugin install — confirmed by reproducing the exact failure
locally and root-causing it against the real Claude Code plugin docs.

Two independent bugs, both real install blockers:

1. hooks schema was wrong. plugin.json mapped event names directly to
   bare script-path strings ("Stop": "hooks/session-end.sh"). The
   real schema requires a structured object per event:
   { "EventName": [{ "hooks": [{ "type": "command", "command": "..." }] }] }
   claude plugin install failed with "hooks: Invalid input" on every
   attempt.

2. marketplace.json pinned the plugin's source to a fetched git URL +
   sha (source: { source: "url", url: "...", sha: "9debdb2..." })
   instead of a relative path. Since the plugin lives in this same
   repo as the marketplace file, the correct source per the docs is a
   relative path ("."). The url+sha form meant every install
   re-fetched a pinned stale commit, so even a correct hooks fix
   pushed to main would never actually be picked up.

Also fixed while in here:

- Wrong hook events: the plugin used PreToolUse (fires on every tool
  call) with a manual sentinel-file workaround to approximate
  "once per session", when SessionStart is the real event that
  already fires exactly once. Switched session-start.sh to
  SessionStart and session-end.sh to SessionEnd (was Stop, which
  fires at the end of every turn, not the session).
- Added the missing author field (plugin.json → author).

NOT fixed, flagged instead: the top-level "statusline" field in
plugin.json is not a real manifest field at all -- Claude Code silently
ignores it (confirmed via claude plugin validate). The only supported
status-line hook is settings.subagentStatusLine, which is scoped to
subagents, not the main session. This plugin's status-bar feature
(statusline/zerodb-status.sh, showing live memory-sync state) has
never actually been wired up and cannot be fixed with a manifest change
-- it needs either a real product decision (drop the feature, or wait
for/request a main-session statusline hook) or a different delivery
mechanism entirely. Left the file and the field in place rather than
silently deleting a documented feature intent.

Verified end-to-end locally: claude plugin marketplace add, claude
plugin install, and claude plugin details all succeed; hooks show as
registered (SessionStart, SessionEnd); clean uninstall/remove after.
@developer-ainative
developer-ainative merged commit 3f675b9 into main Sep 25, 2026
2 of 3 checks passed
developer-ainative pushed a commit that referenced this pull request Sep 25, 2026
The 'Validate hooks reference matches plugin.json' CI step still
assumed the old flat hooks schema (event -> path string) from before
PR #31 fixed plugin.json to use the correct nested
{event: [{hooks: [{type, command}]}]} structure. It was never updated
to match, so every PR touching hooks has been failing CI with
'TypeError: stat: path should be string, bytes, os.PathLike or
integer, not list' since #31 merged.

Refs #33
developer-ainative added a commit that referenced this pull request Sep 25, 2026
* Auto-provision ZeroDB credentials for zero-config MCP startup

Fixes the MCP server failing with 'Missing environment variables:
ZERODB_API_KEY, ZERODB_PROJECT_ID' even when a user has credentials
configured elsewhere. Claude Code's plugin mcpServers[].env ${VAR}
substitution only reads the launching process's own environment — it
never sees credentials set in a sibling tool's config, in Claude
Code's own settings, or anywhere else.

Replaces the direct npx command with bin/start-mcp-server.sh, which
resolves credentials via lib/ensure-credentials.sh before the MCP
server starts:
  1. ZERODB_API_KEY / ZERODB_PROJECT_ID already exported
  2. Previously persisted by this plugin (~/.claude/zerodb-memory)
  3. A sibling ZeroDB tool's ~/.zerodb/.env, if present and valid
  4. Auto-provision a free trial project via the public instant-db API

Each non-environment candidate is verified live against the API
before use, so a stale or invalid key falls through instead of being
handed to the MCP server. A flock-based lock prevents the SessionStart
hook and the MCP server from racing to provision separate trials.

session-start.sh now resolves credentials the same way instead of
silently exiting when ZERODB_API_KEY isn't exported, and announces
the claim link when a trial was just auto-provisioned.

Refs #33

* Fix CI hooks validator for the nested hooks schema

The 'Validate hooks reference matches plugin.json' CI step still
assumed the old flat hooks schema (event -> path string) from before
PR #31 fixed plugin.json to use the correct nested
{event: [{hooks: [{type, command}]}]} structure. It was never updated
to match, so every PR touching hooks has been failing CI with
'TypeError: stat: path should be string, bytes, os.PathLike or
integer, not list' since #31 merged.

Refs #33

---------

Co-authored-by: AINative Admin <admin@ainative.studio>
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.

1 participant