AgentStack
Browse Sign in
Browse Why AgentStack Sell Docs
Sign in
SKILL verified MIT Self-run

Sync Pass Bw

skill-pengguanya-claude-toolkit-sync-pass-bw · by pengguanya

Sync pass (password-store) with Bitwarden — incremental sync with LLM-powered fuzzy matching, BW as source of truth

No reviews yet
0 installs
35 views
0.0% view→install

Install

$ agentstack add skill-pengguanya-claude-toolkit-sync-pass-bw

✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

  • Prompt-injection patterns
  • Secret / credential exfiltration
  • Dangerous shell & filesystem operations
  • Untrusted network calls
  • Known-malicious package signatures

What it can access

  • Network access No
  • Filesystem access No
  • Shell / process execution No
  • Environment & secrets No
  • Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

View the full security report →

Verified badge

Passed review? Show it. Paste this badge into your README, it links to the public security report.

AgentStack Verified badge Links to your public security report.
[![AgentStack Verified](https://agentstack.voostack.com/badges/verified.svg)](https://agentstack.voostack.com/security/report/skill-pengguanya-claude-toolkit-sync-pass-bw)

Reliability & compatibility

Security review passed
0 installs to date
no reviews yet
5mo ago

Declared compatibility

Claude CodeClaude Desktop

Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.

Preview Execution monitoring

We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.

How agent discovery & health will work →
Are you the author of Sync Pass Bw? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

Sync pass Bitwarden

Incrementally synchronize pass (the standard Unix password manager) with Bitwarden using LLM-powered fuzzy matching.

Why this skill exists: pass-import does one-time bulk imports from Bitwarden CSV/JSON exports. It doesn't handle ongoing sync, detect password drift, or deal with the inevitable naming mismatches between the two stores. This skill does all three, using Claude's reasoning to match entries even when names differ (e.g., pass entry Github/github.com/user matches BW entry named "GitHub").

How it works: Bitwarden is the source of truth. The pass store is a deliberate subset — most users only put entries in pass that they need for CLI access, scripting, or browser autofill via browserpass. This skill compares the two stores, reports differences, and can fix mismatches.

Prerequisites

Before any operation, check that all tools are available:

# 1. Bitwarden CLI installed?
which bw || echo "ERROR: bw not found. Install: https://bitwarden.com/help/cli/"

# 2. Bitwarden auth status
bw status 2>&1

# 3. pass installed?
which pass || echo "ERROR: pass not found. Install: https://www.passwordstore.org/"

# 4. pass store exists?
ls ~/.password-store/*.gpg 2>/dev/null || ls ~/.password-store/**/*.gpg 2>/dev/null || echo "WARNING: password store appears empty"

# 5. jq installed? (required for JSON processing)
which jq || echo "ERROR: jq not found. Install: sudo apt install jq"

If Bitwarden is locked, tell the user: > Your Bitwarden vault is locked. Please run ! bw unlock in Claude Code, then provide the session key. You can also pass it directly: /sync-pass-bw sync --session

If Bitwarden is unauthenticated, tell the user: > Please run ! bw login first to authenticate with your Bitwarden account.

Session handling

The BW session key can come from (check in this order):

  1. $BW_SESSION environment variable already set in the shell
  2. --session argument passed to this skill
  3. Ask the user to provide it

Use --session "$BW_SESSION" or export BW_SESSION=... in all bw commands.

Interpreting Arguments

$ARGUMENTS

| Input pattern | Action | |---|---| | Empty or "status" or "check" | Run Compare workflow only (read-only report) | | "sync" | Run Compare + Fix Mismatches workflows | | "full" | Run Compare + Fix Mismatches + Structural Improvements workflows | | --session (in any position) | Extract the session key and use it for bw commands |

Workflow: Compare

Read-only comparison that produces a status report. This always runs first.

Step 1: Fetch all data

# Get all BW items as JSON (save to temp file for repeated queries)
bw list items --session "$BW_SESSION" 2>/dev/null > /tmp/bw_sync_items.json
echo "BW entries: $(jq length /tmp/bw_sync_items.json)"

# Get all pass entry paths
find ~/.password-store -name '*.gpg' | sed 's|.*/.password-store/||; s|\.gpg$||' | sort > /tmp/pass_entries.txt
echo "Pass entries: $(wc -l /dev/null | sed 's|.*/.password-store/||; s|/$||'

# Show depth distribution
find ~/.password-store -name '*.gpg' | sed 's|.*/.password-store/||; s|\.gpg$||' | awk -F/ '{print NF}' | sort | uniq -c

Common patterns to detect:

  • Flat: domain.com/username.gpg (2 levels)
  • Categorized: Category/domain.com/username.gpg (3 levels)
  • Nested: Category/Service/domain.com/username.gpg (4 levels)
  • Mixed: combination of the above

The last path segment is always the username (or a label). The segment before it is typically the domain or service name. Earlier segments are organizational categories.

Step 3: Match and compare each pass entry

For each pass entry, find the corresponding Bitwarden entry using fuzzy matching:

  1. Extract from pass path:
  • Username: the last path segment (filename without .gpg)
  • Domain: the parent directory name
  • Fallback domain: the grandparent directory name (for deeply nested structures)

Examples:

  • Social/twitter.com/myuser -> username=myuser, domain=twitter.com
  • Work/Roche/pengg3 -> username=pengg3, domain=Roche
  • Shopping/Amazon/amazon.com/user@email.com -> username=user@email.com, domain=amazon.com, fallback=Amazon
  1. Find BW match: Search BW items where:
  • login.username equals the extracted username, AND
  • Any URI contains the domain string, OR the BW entry name contains the domain string (case-insensitive)
  • If no match: retry with the fallback domain (grandparent directory)
  • If still no match: try matching username alone and present candidates for the user to confirm
  1. Compare passwords: If a BW match is found, compare passwords directly:
PASS_PW=$(pass show "$PASS_PATH" 2>/dev/null | head -1)
BW_PW=$(jq -r --arg u "$USERNAME" --arg d "$DOMAIN" '
  [.[] | select(
    .login.username == $u and
    ((.login.uris // [] | any(.uri | test($d; "i"))) or (.name | test($d; "i")))
  )] | .[0].login.password // ""
' /tmp/bw_sync_items.json)

if [ -z "$BW_PW" ]; then
  STATUS="PASS_ONLY"
elif [ "$PASS_PW" = "$BW_PW" ]; then
  STATUS="SYNCED"
else
  STATUS="PW_DIFFERS"
fi

Step 4: Report results

Present results grouped by status:

## Sync Status Report

### SYNCED (N entries)
| Pass Path | BW Entry |
|---|---|

### PW_DIFFERS (N entries) -- action needed
| Pass Path | BW Entry | Pass pw_len | BW pw_len | BW revised |
|---|---|---|---|---|

### PASS_ONLY (N entries) -- exists in pass but no BW match found
| Pass Path | Username | Domain | Notes |
|---|---|---|---|

### Summary
- Total pass entries: N
- Synced: N
- Password differs: N
- Pass-only (no BW match): N

If there are PASS_ONLY entries, use your judgment to determine if they might match a BW entry with a different naming convention. Present potential matches and ask the user to confirm.

Workflow: Fix Mismatches

Only run when user requests "sync" or "full". Requires the Compare workflow to have completed.

For each PW_DIFFERS entry:

  1. Determine which is newer:

```bash # BW revision date jq -r --arg u "$USERNAME" --arg d "$DOMAIN" ' [.[] | select(.login.username == $u and ((.login.uris // [] | any(.uri | test($d; "i"))) or (.name | test($d; "i"))) )] | .[0].revisionDate ' /tmp/bwsyncitems.json

# Pass last modification (from git log) cd ~/.password-store && git log -1 --format="%ai" -- "$PASS_PATH.gpg" ```

  1. Present the mismatch to the user:

> ``: BW password (revised YYYY-MM-DD) differs from pass (last changed YYYY-MM-DD). Password lengths: pass=N, BW=M. > Recommended: Update pass from BW (source of truth). > Update pass from BW?

  1. Apply fix (only after user confirms):

``bash BW_PW=$(jq -r --arg u "$USERNAME" --arg d "$DOMAIN" ' [.[] | select(.login.username == $u and ((.login.uris // [] | any(.uri | test($d; "i"))) or (.name | test($d; "i"))) )] | .[0].login.password ' /tmp/bw_sync_items.json) echo "$BW_PW" | pass insert -f -e "$PASS_PATH" ``

  1. After all fixes, re-run the comparison to confirm 0 mismatches remain.

Workflow: Structural Improvements

Only run when user requests "full". This workflow analyzes and suggests -- it never auto-applies changes.

Analysis 1: Naming mismatches

Look for pass entries where the path doesn't reflect the current BW entry name. This happens when BW entries are renamed but pass paths aren't updated.

  • Example: pass has ChatGPT/auth0.openai.com/user but BW entry is named "OpenAI"
  • Suggestion: pass mv "ChatGPT/auth0.openai.com/user" "OpenAI/auth0.openai.com/user"

Analysis 2: Redundant pass entries

Multiple pass entries that map to the same single BW entry (same password, same username, different paths).

  • This can be intentional -- e.g., browserpass users keep separate entries per subdomain so autofill works on each URL
  • Flag these but explain the trade-off: consolidating simplifies the store, but may break browser autofill for specific URLs

Analysis 3: BW duplicates

Multiple BW entries with the same username and overlapping URIs. These are likely accidental duplicates in BW itself.

  • Suggest: merge in the Bitwarden web vault (pass can't fix this)

Analysis 4: Browserpass compatibility

If browserpass is detected (which browserpass-linux64 || which browserpass-linux32 || ls ~/.mozilla/native-messaging-hosts/com.github.browserpass.native.json 2>/dev/null):

  • Check that pass entries contain the actual login domain in their path
  • Flag entries where the domain segment doesn't match any BW URI (autofill won't work)
  • Example: Work/CompanySSO/pengg3 won't match any URL -- suggest renaming to Work/sso.company.com/pengg3

Present all suggestions as a numbered list. Let the user approve, skip, or modify each one.

Cleanup

After all operations, remove temp files:

rm -f /tmp/bw_sync_items.json /tmp/pass_entries.txt

Important Rules

  • NEVER display passwords in plain text in your output. Only use them inside pipe commands (echo "$PW" | pass insert ...) or variable comparisons in bash. To show differences, display password lengths only.
  • NEVER create new pass entries without explicit user confirmation.
  • NEVER delete pass entries without explicit user confirmation.
  • NEVER modify Bitwarden entries -- this skill treats BW as read-only source of truth. Suggest BW changes for the user to make in the web vault.
  • ALWAYS check bw status before any query -- if locked or unauthenticated, stop and guide the user.
  • ALWAYS use pass insert -f -e (force + echo mode) when updating passwords programmatically. Never use interactive pass insert which requires typing the password twice.
  • ALWAYS confirm each password update individually with the user before executing.
  • ALWAYS clean up temp files (/tmp/bw_sync_items.json, /tmp/pass_entries.txt) when done.
  • The pass store is typically git-managed -- every pass insert, pass mv, and pass rm automatically creates a git commit. Do not run additional git commands in the pass store.
  • When matching fails, present candidates and ask -- do not guess silently.

Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet, be the first.

Versions

  • v0.1.0 Imported from the upstream source.