diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index a9d6142..60b5979 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -13,6 +13,12 @@ "source": "./plugins/openbao-session", "description": "Provisions a short-lived OpenBao session token for interactive agents.", "version": "0.1.0" + }, + { + "name": "shared-vault-sync", + "source": "./plugins/shared-vault-sync", + "description": "Safely pulls the shared vault at session start.", + "version": "0.1.0" } ] } diff --git a/marketplace.json b/marketplace.json index f905105..14bbde8 100644 --- a/marketplace.json +++ b/marketplace.json @@ -1,18 +1,44 @@ { "name": "infra-plugins", - "interface": { "displayName": "Infra Plugins" }, + "interface": { + "displayName": "Infra Plugins" + }, "plugins": [ { "name": "taiga-auto-sync", - "source": { "source": "local", "path": "./plugins/taiga-auto-sync" }, - "policy": { "installation": "AVAILABLE", "authentication": "ON_INSTALL" }, + "source": { + "source": "local", + "path": "./plugins/taiga-auto-sync" + }, + "policy": { + "installation": "AVAILABLE", + "authentication": "ON_INSTALL" + }, "category": "Productivity" }, { "name": "openbao-session", - "source": { "source": "local", "path": "./plugins/openbao-session" }, - "policy": { "installation": "AVAILABLE", "authentication": "ON_INSTALL" }, + "source": { + "source": "local", + "path": "./plugins/openbao-session" + }, + "policy": { + "installation": "AVAILABLE", + "authentication": "ON_INSTALL" + }, "category": "Security" + }, + { + "name": "shared-vault-sync", + "source": { + "source": "local", + "path": "./plugins/shared-vault-sync" + }, + "policy": { + "installation": "AVAILABLE", + "authentication": "ON_INSTALL" + }, + "category": "Productivity" } ] } diff --git a/plugins/shared-vault-sync/.claude-plugin/plugin.json b/plugins/shared-vault-sync/.claude-plugin/plugin.json new file mode 100644 index 0000000..d681ea3 --- /dev/null +++ b/plugins/shared-vault-sync/.claude-plugin/plugin.json @@ -0,0 +1,7 @@ +{ + "name": "shared-vault-sync", + "version": "0.1.0", + "description": "Safely pulls the shared vault at interactive session start.", + "author": { "name": "Senkensha" }, + "hooks": "./hooks/claude-codex-hooks.json" +} diff --git a/plugins/shared-vault-sync/.codex-plugin/plugin.json b/plugins/shared-vault-sync/.codex-plugin/plugin.json new file mode 100644 index 0000000..d9a5055 --- /dev/null +++ b/plugins/shared-vault-sync/.codex-plugin/plugin.json @@ -0,0 +1,20 @@ +{ + "name": "shared-vault-sync", + "version": "0.1.0", + "description": "Safely pulls the shared vault at interactive session start.", + "author": { + "name": "Local developer" + }, + "hooks": "./hooks/claude-codex-hooks.json", + "interface": { + "displayName": "Shared Vault Sync", + "shortDescription": "Safely pull the shared vault at session start.", + "longDescription": "Fast-forwards a clean shared vault at session start without blocking the agent when the network is unavailable.", + "developerName": "Local developer", + "category": "Productivity", + "capabilities": [ + "Lifecycle hooks" + ], + "defaultPrompt": "Synchronize the shared vault." + } +} diff --git a/plugins/shared-vault-sync/README.md b/plugins/shared-vault-sync/README.md new file mode 100644 index 0000000..5a8f50a --- /dev/null +++ b/plugins/shared-vault-sync/README.md @@ -0,0 +1,15 @@ +# shared-vault-sync + +Pulls a clean shared vault at `SessionStart` without blocking Claude Code or Codex when the network is unavailable. + +Set `SHARED_VAULT_DIR` to the vault directory to sync it from any working directory. Without it, the hook acts only when the session starts inside a repository whose `AGENTS.md` identifies it as the shared persistent vault. + +The hook skips a dirty worktree and uses `git pull --ff-only`; it never rebases, merges, or pushes. + +## Install + +Install `shared-vault-sync` from the `infra-plugins` marketplace, then open a new session to activate its `SessionStart` hook. + +## Recommended push automation + +Keep pushes commit-driven. A scheduled local job may run `git fetch` and `git push` only when the vault is clean and the local branch is ahead; it must never rebase, force-push, or resolve conflicts. Let this hook handle the next safe pull after a rejected push. diff --git a/plugins/shared-vault-sync/hooks/claude-codex-hooks.json b/plugins/shared-vault-sync/hooks/claude-codex-hooks.json new file mode 100644 index 0000000..bb2766d --- /dev/null +++ b/plugins/shared-vault-sync/hooks/claude-codex-hooks.json @@ -0,0 +1,16 @@ +{ + "hooks": { + "SessionStart": [ + { + "hooks": [ + { + "type": "command", + "command": "bash \"${CLAUDE_PLUGIN_ROOT}/hooks/pull-shared-vault.sh\"", + "timeout": 15, + "statusMessage": "Synchronizing shared vault..." + } + ] + } + ] + } +} diff --git a/plugins/shared-vault-sync/hooks/pull-shared-vault.sh b/plugins/shared-vault-sync/hooks/pull-shared-vault.sh new file mode 100755 index 0000000..9a1cfb9 --- /dev/null +++ b/plugins/shared-vault-sync/hooks/pull-shared-vault.sh @@ -0,0 +1,25 @@ +#!/usr/bin/env bash +# Pull only the designated shared vault; never block a new agent session. +set -u + +vault_dir="${SHARED_VAULT_DIR:-${PWD:-}}" + +emit() { + printf '{"continue":true,"suppressOutput":true,"hookSpecificOutput":{"additionalContext":"%s"}}\n' "$1" +} + +if [ ! -f "$vault_dir/AGENTS.md" ] || ! grep -Fq 'This is the shared persistent vault' "$vault_dir/AGENTS.md" || ! git -C "$vault_dir" rev-parse --is-inside-work-tree >/dev/null 2>&1; then + emit 'Shared vault sync skipped: set SHARED_VAULT_DIR or start the session from the shared vault.' + exit 0 +fi + +if [ -n "$(git -C "$vault_dir" status --porcelain)" ]; then + emit 'Shared vault sync skipped: the worktree has local changes.' + exit 0 +fi + +if GIT_TERMINAL_PROMPT=0 git -C "$vault_dir" pull --ff-only --quiet; then + emit 'Shared vault sync completed.' +else + emit 'Shared vault sync failed; run git pull --ff-only from the vault when it is safe to do so.' +fi