Skip to content

Latest commit

 

History

History
238 lines (178 loc) · 7.44 KB

File metadata and controls

238 lines (178 loc) · 7.44 KB

Development

How to run, test, and iterate on this plugin locally without going through the public marketplace flow.

Prerequisites

  • Node 22+ (uses the built-in test runner; no npm install step, zero dependencies).
  • uvx on PATH (for the MCP server).
  • curl on PATH (for the bin/ shim and update check).
  • A ZeroPath API token (only for end-to-end runs that hit the API).

Local install (Claude Code session)

One-shot install from your checkout

From any Claude Code session, point the marketplace at the absolute path of your local checkout:

/plugin marketplace add /absolute/path/to/zeropath-agent-plugin
/plugin install zeropath@ZeroPathAI

Claude Code:

  1. Copies the plugin into ~/.claude/plugins/cache/zeropath-ZeroPathAI/.
  2. Shows a userConfig form asking for Token ID, Token Secret (masked), and optional Org ID + Custom base URL.
  3. Stores the secret in your system keychain; non-sensitive values go to ~/.claude/settings.json.

Source edits made AFTER this don't propagate until you run:

/plugin marketplace update ZeroPathAI

...followed by a session restart. To change credentials later: open /plugin -> zeropath -> Configure.

Faster iteration loop: symlink the cache

After the first install, replace the cache copy with a symlink:

# Run from inside your local checkout so $(pwd) is the source dir.
PLUGIN_SRC="$(pwd)"
PLUGIN_CACHE=~/.claude/plugins/cache/zeropath-ZeroPathAI
rm -rf "$PLUGIN_CACHE"
ln -s "$PLUGIN_SRC" "$PLUGIN_CACHE"

Now every source edit takes effect on the next session restart, no marketplace update needed. Reverse it (and restore a real copy) before running /plugin uninstall.

Uninstall

/plugin uninstall zeropath@ZeroPathAI
/plugin marketplace remove ZeroPathAI

Run the test suite

node --test tests/smoke.test.mjs

90 tests across 12 suites. No network, no real CLI download. Finishes in a few seconds on any modern machine.

To run a single suite:

node --test --test-name-pattern "session-start" tests/smoke.test.mjs

Smoke-test each surface by hand

Hooks

Every hook reads JSON from stdin, writes JSON (or silence) to stdout.

DATA=/tmp/zp-dev && mkdir -p "$DATA/sessions"

# SessionStart: vanilla
node hooks/session-start.mjs < /dev/null

# SessionStart with simulated update-available
cat > "$DATA/update-check.json" <<EOF
{"ok":true,"current":"0.3.0","latest":"0.4.0","updateAvailable":true,"checkedAt":1}
EOF
CLAUDE_PLUGIN_DATA="$DATA" node hooks/session-start.mjs < /dev/null

# PostToolUse: track an edit
echo '{"session_id":"dev","tool_input":{"file_path":"src/foo.js"}}' \
  | CLAUDE_PLUGIN_DATA="$DATA" node hooks/post-tool-use.mjs
cat "$DATA/sessions/dev.touched.log"

# PreToolUse: push warning (will fire - dev.touched.log has content)
echo '{"session_id":"dev","tool_input":{"command":"git push origin main"}}' \
  | CLAUDE_PLUGIN_DATA="$DATA" node hooks/pre-tool-use.mjs

# PreToolUse: gh pr create variant
echo '{"session_id":"dev","tool_input":{"command":"gh pr create --title foo"}}' \
  | CLAUDE_PLUGIN_DATA="$DATA" node hooks/pre-tool-use.mjs

# PreCompact: preserve summary across compaction
echo '{"session_id":"dev"}' \
  | CLAUDE_PLUGIN_DATA="$DATA" node hooks/pre-compact.mjs

Scripts

# Force a fresh GitHub release check (ignores cache TTL)
node scripts/check-latest-version.mjs --json --no-cache

# Inject the CLAUDE.md block into a sandbox file
node scripts/install-claude-md-block.mjs --target /tmp/test-CLAUDE.md
cat /tmp/test-CLAUDE.md

# Idempotent re-run
node scripts/install-claude-md-block.mjs --target /tmp/test-CLAUDE.md

# Remove the block
node scripts/install-claude-md-block.mjs --target /tmp/test-CLAUDE.md --remove

# Install the zeropath CLI to ~/.local/bin/ (writes outside the repo)
node scripts/install.mjs --force

bin/ shim

# First call downloads the CLI to the cache; subsequent calls reuse it
DATA=/tmp/zp-dev CLAUDE_PLUGIN_DATA="$DATA" ./bin/zeropath --help

Opt-in statusline

# Silent when no update-check cache exists
CLAUDE_PLUGIN_DATA=/tmp/empty ./bin/statusline-zeropath.sh

# Up-arrow indicator when cache says update available
cat > /tmp/zp-dev/update-check.json <<EOF
{"ok":true,"latest":"0.4.0","updateAvailable":true}
EOF
CLAUDE_PLUGIN_DATA=/tmp/zp-dev ./bin/statusline-zeropath.sh

Running the MCP server outside Claude Code

The MCP server is launched by Claude Code via the .mcp.json config. For debugging, you can spawn it directly:

ZEROPATH_TOKEN_ID=... \
ZEROPATH_TOKEN_SECRET=... \
ZEROPATH_ORG_ID=... \
uvx --from git+https://github.com/ZeroPathAI/zeropath-mcp-server zeropath-mcp-server

The server speaks JSON-RPC on stdin/stdout. For an interactive tool browser, use the MCP inspector:

npx @modelcontextprotocol/inspector \
  uvx --from git+https://github.com/ZeroPathAI/zeropath-mcp-server zeropath-mcp-server

State + cache locations

Path What lives there
~/.claude/plugins/cache/zeropath-ZeroPathAI/ Installed plugin copy
${CLAUDE_PLUGIN_DATA}/cli/ Cached zeropath binary
${CLAUDE_PLUGIN_DATA}/sessions/<id>.touched.log Files edited this session
${CLAUDE_PLUGIN_DATA}/sessions/<id>.lastscan Unix-ms timestamp of last clean scan
${CLAUDE_PLUGIN_DATA}/update-check.json 24h-cached GitHub release check
${CLAUDE_PLUGIN_DATA}/config.json Per-user config override
<project>/.zeropath/plugin.json Per-repo config
~/.config/zeropath/credentials.json CLI credentials

CLAUDE_PLUGIN_DATA is set by Claude Code when the plugin is active. Outside Claude Code (i.e. running hooks manually), our code falls back to ${tmpdir()}/zeropath-agent-plugin/.

Reset all local state

rm -rf ~/.claude/plugins/cache/zeropath-ZeroPathAI
rm -rf ~/.claude/plugins/data/zeropath-ZeroPathAI
rm -rf ~/.config/zeropath

Pre-commit checklist

# 1. Tests pass
node --test tests/smoke.test.mjs

# 2. All .mjs parse
find . -name "*.mjs" -not -path "./node_modules/*" -print0 \
  | xargs -0 -n1 node --check

# 3. JSON manifests valid
for f in .claude-plugin/plugin.json .claude-plugin/marketplace.json \
         .claude-plugin/settings.json .mcp.json hooks/hooks.json; do
  node -e "JSON.parse(require('fs').readFileSync('$f','utf8'))" || echo "INVALID: $f"
done

# 4. Version sync between plugin.json and marketplace.json
grep -h '"version":' .claude-plugin/plugin.json .claude-plugin/marketplace.json

Gotchas

  • First zeropath call inside Claude Code takes ~5-10s while the bin/ shim downloads the CLI. Pre-warm with CLAUDE_PLUGIN_DATA=... ./bin/zeropath --version.
  • zeropath whoami doesn't exist upstream yet. SessionStart handles the absence gracefully (just shows "ready" without identity).
  • MCP env vars must be set in the shell that launched Claude Code. Editing ~/.zshrc after launch has no effect until you restart.
  • Symlinked plugin cache breaks /plugin uninstall. Restore a real directory (or delete the symlink manually) before uninstalling.
  • The PreToolUse regex is intentionally narrow to avoid false positives on commands like echo "git push". It matches the common invocation forms only; CI scripts that do creative things with git may bypass the warning.
  • Update check writes to ${CLAUDE_PLUGIN_DATA} even when running manually. Use a sandbox CLAUDE_PLUGIN_DATA=/tmp/... when poking at scripts to avoid polluting your real plugin state.