From db329d7cbef81767e649f7a34e7d783bebdca40e Mon Sep 17 00:00:00 2001 From: "zhengxiao.wu" Date: Wed, 13 May 2026 21:43:58 +0800 Subject: [PATCH 1/2] refactor(plugin/claude-code): move shell wrapper to standalone rc file MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The installer-emitted claude() wrapper had been inlined as a marker-delimited block in the user's ~/.zshrc / ~/.bashrc. Every upgrade required the awk-strip-and-append dance, the inline noise was hostile to anyone reading their own rc, and there was a known footgun: if the END marker got hand-deleted from the rc, the next install's awk-strip would drop everything from the BEGIN marker to EOF. Switch to the standard pyenv / nvm / fnm pattern, mirroring what the codex-memory-plugin installer now does (see #2023): - Wrapper body lives in its own file at ~/.openviking/claude-plugin.rc.sh (path overridable via OPENVIKING_CLAUDE_WRAPPER_RC). Full overwrite on every install — no marker logic inside the wrapper file itself. - The user's shell rc gets a single one-line source hook, still marker-wrapped for clean uninstall: # >>> openviking claude-code memory plugin >>> [ -f "$HOME/.openviking/claude-plugin.rc.sh" ] && . "..." # <<< openviking claude-code memory plugin <<< Content is constant across installs, so the marker-replacement logic only triggers the legacy-cleanup path once (when upgrading from a pre-rc-split install that inlined the full claude() function body). User-visible improvements: - ~/.zshrc OV-plugin block: ~16 lines of wrapper body → 3 lines. - Wrapper body is a real file you can `cat` / `diff` / restore from source control; no need to re-run the installer to inspect it. - Uninstall: `rm ~/.openviking/claude-plugin.rc.sh` + delete the 3-line marker block. - The END-marker corruption footgun is gone, since the marker block content is bytestring-stable and the awk-strip only runs when both markers are present anyway. No behavior change to the wrapper itself (still pulls url/api_key from ovcli.conf via jq). --- .../setup-helper/install.sh | 68 +++++++++++++------ 1 file changed, 46 insertions(+), 22 deletions(-) diff --git a/examples/claude-code-memory-plugin/setup-helper/install.sh b/examples/claude-code-memory-plugin/setup-helper/install.sh index 39f5b5af60..b1490378d0 100755 --- a/examples/claude-code-memory-plugin/setup-helper/install.sh +++ b/examples/claude-code-memory-plugin/setup-helper/install.sh @@ -159,9 +159,18 @@ else fi # ----- 4. Shell rc wrapper ----- +# +# Wrapper body lives in its own file at $WRAPPER_RC_FILE (full overwrite on +# every install — no marker parsing needed inside the wrapper file itself). +# The user's shell rc gets a single one-line source hook (marker-delimited, +# but content never changes, so the marker-replacement logic only runs the +# legacy-cleanup path once when upgrading from a pre-rc-split install). +# Same pattern pyenv / nvm / fnm use. heading '4. Shell rc — claude function wrapper' +WRAPPER_RC_FILE="${OPENVIKING_CLAUDE_WRAPPER_RC:-$OV_HOME/claude-plugin.rc.sh}" + case "${SHELL:-}" in */zsh) RC="$HOME/.zshrc" ;; */bash) RC="$HOME/.bashrc" ;; @@ -172,39 +181,54 @@ case "${SHELL:-}" in ;; esac +# Write the wrapper body to its standalone rc file (full overwrite). +mkdir -p "$(dirname "$WRAPPER_RC_FILE")" +cat > "$WRAPPER_RC_FILE" <<'WRAPPER' +# OpenViking claude-code memory plugin shell wrapper. +# Generated by examples/claude-code-memory-plugin/setup-helper/install.sh. +# Re-run the installer to update this file; do not hand-edit. + +claude() { + local _ov_conf="${OPENVIKING_CLI_CONFIG_FILE:-$HOME/.openviking/ovcli.conf}" + if [ -f "$_ov_conf" ] && command -v jq >/dev/null 2>&1; then + local _ov_url _ov_key + _ov_url=$(jq -r '.url // empty' "$_ov_conf" 2>/dev/null) + _ov_key=$(jq -r '.api_key // empty' "$_ov_conf" 2>/dev/null) + OPENVIKING_URL="${OPENVIKING_URL:-$_ov_url}" \ + OPENVIKING_API_KEY="${OPENVIKING_API_KEY:-$_ov_key}" \ + command claude "$@" + else + command claude "$@" + fi +} +WRAPPER +info "Wrote claude() wrapper to $WRAPPER_RC_FILE" + +# The user's shell rc gets a single one-line source hook. +SOURCE_HOOK="[ -f \"$WRAPPER_RC_FILE\" ] && . \"$WRAPPER_RC_FILE\"" +SOURCE_BLOCK="$MARKER_BEGIN +$SOURCE_HOOK +$MARKER_END" + if [ -z "$RC" ]; then - warn 'Could not detect shell rc. Add the function wrapper manually — see:' - warn ' https://github.com/volcengine/OpenViking/blob/main/examples/claude-code-memory-plugin/README.md' + warn 'Could not detect shell rc. Add this snippet to your rc manually:' + warn '' + while IFS= read -r line; do warn " $line"; done <<< "$SOURCE_BLOCK" else touch "$RC" if grep -qF "$MARKER_BEGIN" "$RC"; then - info "Existing wrapper detected in $RC — replacing in place" + info "Replacing openviking source hook in $RC" + # Strip existing block (whether it's the new one-liner or an old + # inline-wrapper block from a previous version). awk -v b="$MARKER_BEGIN" -v e="$MARKER_END" ' $0 == b {skip=1; next} $0 == e {skip=0; next} !skip ' "$RC" > "$RC.tmp" && mv "$RC.tmp" "$RC" else - info "Appending wrapper to $RC" - fi - cat >> "$RC" </dev/null 2>&1; then - local _ov_url _ov_key - _ov_url=\$(jq -r '.url // empty' "\$_ov_conf" 2>/dev/null) - _ov_key=\$(jq -r '.api_key // empty' "\$_ov_conf" 2>/dev/null) - OPENVIKING_URL="\${OPENVIKING_URL:-\$_ov_url}" \\ - OPENVIKING_API_KEY="\${OPENVIKING_API_KEY:-\$_ov_key}" \\ - command claude "\$@" - else - command claude "\$@" + info "Appending openviking source hook to $RC" fi -} -$MARKER_END -EOF + printf '\n%s\n' "$SOURCE_BLOCK" >> "$RC" fi # ----- 5. Plugin install ----- From f70baf177222a06f6ef69dffaf0b5bbc7aa144a5 Mon Sep 17 00:00:00 2001 From: "zhengxiao.wu" Date: Wed, 13 May 2026 21:48:04 +0800 Subject: [PATCH 2/2] refactor(plugin/claude-code): extract wrapper to checked-in setup-helper/wrapper.sh MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Squash-style follow-up to the previous rc-split commit on this branch: now that the wrapper lives in its own file conceptually, just check it in at examples/claude-code-memory-plugin/setup-helper/wrapper.sh and have the user's shell rc source it directly from the cloned plugin checkout. No copy step, no heredoc dance in install.sh. Why this is better than the previous approach (wrapper body embedded as a heredoc in install.sh, written to a copy in ~/.openviking): - Wrapper is a real reviewable file. Diffs show `+ claude() { ... }`, not "+ heredoc lines that produce the wrapper". - Updates ride on the installer's existing `git fetch + reset --hard` step — no separate "re-run installer to refresh the copy" path. - One less source of truth (no $HOME copy that can drift from the installer's intent). - Uninstall: `rm ~/.openviking/openviking-repo`; the source hook in the rc silently no-ops via `[ -f ... ] && .`. The previous commit on this branch already shrunk the rc block from ~16 inline lines to 3 (marker + source hook + marker). This commit just moves the wrapper body from "embedded in installer" to "checked into the repo at a stable path", with no behavior change to the wrapper itself. --- .../setup-helper/install.sh | 48 +++++++------------ .../setup-helper/wrapper.sh | 27 +++++++++++ 2 files changed, 43 insertions(+), 32 deletions(-) create mode 100644 examples/claude-code-memory-plugin/setup-helper/wrapper.sh diff --git a/examples/claude-code-memory-plugin/setup-helper/install.sh b/examples/claude-code-memory-plugin/setup-helper/install.sh index b1490378d0..38717f9abe 100755 --- a/examples/claude-code-memory-plugin/setup-helper/install.sh +++ b/examples/claude-code-memory-plugin/setup-helper/install.sh @@ -160,16 +160,19 @@ fi # ----- 4. Shell rc wrapper ----- # -# Wrapper body lives in its own file at $WRAPPER_RC_FILE (full overwrite on -# every install — no marker parsing needed inside the wrapper file itself). -# The user's shell rc gets a single one-line source hook (marker-delimited, -# but content never changes, so the marker-replacement logic only runs the -# legacy-cleanup path once when upgrading from a pre-rc-split install). -# Same pattern pyenv / nvm / fnm use. +# Source of truth: setup-helper/wrapper.sh in the plugin checkout. The +# user's shell rc just sources that file directly — no copy step, so any +# updates land via the next `git fetch + reset --hard` the installer +# already runs above. Same pattern pyenv / nvm / fnm use, except we don't +# even need an intermediate copy in $HOME. heading '4. Shell rc — claude function wrapper' -WRAPPER_RC_FILE="${OPENVIKING_CLAUDE_WRAPPER_RC:-$OV_HOME/claude-plugin.rc.sh}" +WRAPPER_SRC="$REPO_DIR/examples/claude-code-memory-plugin/setup-helper/wrapper.sh" +if [ ! -f "$WRAPPER_SRC" ]; then + err "Wrapper source not found at $WRAPPER_SRC" + exit 1 +fi case "${SHELL:-}" in */zsh) RC="$HOME/.zshrc" ;; @@ -181,31 +184,12 @@ case "${SHELL:-}" in ;; esac -# Write the wrapper body to its standalone rc file (full overwrite). -mkdir -p "$(dirname "$WRAPPER_RC_FILE")" -cat > "$WRAPPER_RC_FILE" <<'WRAPPER' -# OpenViking claude-code memory plugin shell wrapper. -# Generated by examples/claude-code-memory-plugin/setup-helper/install.sh. -# Re-run the installer to update this file; do not hand-edit. - -claude() { - local _ov_conf="${OPENVIKING_CLI_CONFIG_FILE:-$HOME/.openviking/ovcli.conf}" - if [ -f "$_ov_conf" ] && command -v jq >/dev/null 2>&1; then - local _ov_url _ov_key - _ov_url=$(jq -r '.url // empty' "$_ov_conf" 2>/dev/null) - _ov_key=$(jq -r '.api_key // empty' "$_ov_conf" 2>/dev/null) - OPENVIKING_URL="${OPENVIKING_URL:-$_ov_url}" \ - OPENVIKING_API_KEY="${OPENVIKING_API_KEY:-$_ov_key}" \ - command claude "$@" - else - command claude "$@" - fi -} -WRAPPER -info "Wrote claude() wrapper to $WRAPPER_RC_FILE" - -# The user's shell rc gets a single one-line source hook. -SOURCE_HOOK="[ -f \"$WRAPPER_RC_FILE\" ] && . \"$WRAPPER_RC_FILE\"" +# The user's shell rc gets a single one-line source hook pointing at the +# wrapper source in the cloned plugin checkout. Hook content stays stable +# across installs (only the absolute path matters), so the marker +# replacement only triggers a legacy-cleanup pass once when upgrading from +# a pre-split install that inlined the full wrapper into the rc. +SOURCE_HOOK="[ -f \"$WRAPPER_SRC\" ] && . \"$WRAPPER_SRC\"" SOURCE_BLOCK="$MARKER_BEGIN $SOURCE_HOOK $MARKER_END" diff --git a/examples/claude-code-memory-plugin/setup-helper/wrapper.sh b/examples/claude-code-memory-plugin/setup-helper/wrapper.sh new file mode 100644 index 0000000000..f45ce4face --- /dev/null +++ b/examples/claude-code-memory-plugin/setup-helper/wrapper.sh @@ -0,0 +1,27 @@ +# OpenViking claude-code memory plugin shell wrapper. +# +# Sourced from the user's shell rc via a `[ -f ... ] && . ...` hook that +# the installer writes once. Updates land for free via the installer's +# `git fetch + reset --hard` of the plugin checkout — no need to re-run +# the installer just to refresh this wrapper. +# +# The MCP server URL and bearer token end up in `.mcp.json` rather than +# in the model's per-process env, so Claude Code needs the OpenViking +# credentials in the env at `claude` launch. The wrapper pulls them from +# ovcli.conf and injects them as a prefix, so the user doesn't need to +# `export OPENVIKING_API_KEY` globally and risk leaking it into other +# subprocesses. + +claude() { + local _ov_conf="${OPENVIKING_CLI_CONFIG_FILE:-$HOME/.openviking/ovcli.conf}" + if [ -f "$_ov_conf" ] && command -v jq >/dev/null 2>&1; then + local _ov_url _ov_key + _ov_url=$(jq -r '.url // empty' "$_ov_conf" 2>/dev/null) + _ov_key=$(jq -r '.api_key // empty' "$_ov_conf" 2>/dev/null) + OPENVIKING_URL="${OPENVIKING_URL:-$_ov_url}" \ + OPENVIKING_API_KEY="${OPENVIKING_API_KEY:-$_ov_key}" \ + command claude "$@" + else + command claude "$@" + fi +}