no-more-leaked-keys 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +63 -0
- package/README.md +395 -0
- package/bin/install.js +216 -0
- package/commands/add-mcp.md +17 -0
- package/commands/secrets.md +12 -0
- package/hooks/block-unsafe-mcp-add.sh +17 -0
- package/keychain-secrets/SKILL.md +129 -0
- package/keychain-secrets/references/env-file-patterns.md +220 -0
- package/keychain-secrets/references/keychain-commands.md +159 -0
- package/keychain-secrets/workflows/add-key.md +99 -0
- package/keychain-secrets/workflows/add-mcp.md +171 -0
- package/keychain-secrets/workflows/list-keys.md +83 -0
- package/keychain-secrets/workflows/populate-env.md +128 -0
- package/keychain-secrets/workflows/remove-key.md +71 -0
- package/package.json +49 -0
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
# Workflow: Add MCP Server Securely
|
|
2
|
+
|
|
3
|
+
<purpose>
|
|
4
|
+
Securely add an MCP server with authentication to Claude Code or OpenCode WITHOUT exposing the API key. This replaces the unsafe `claude mcp add` command which echoes secrets to the terminal.
|
|
5
|
+
|
|
6
|
+
**NEVER use `claude mcp add` with `--header "Authorization: Bearer ..."` - it will expose the key!**
|
|
7
|
+
</purpose>
|
|
8
|
+
|
|
9
|
+
<required_reading>
|
|
10
|
+
**Read these reference files NOW:**
|
|
11
|
+
1. references/keychain-commands.md
|
|
12
|
+
</required_reading>
|
|
13
|
+
|
|
14
|
+
<process>
|
|
15
|
+
## Step 1: Gather MCP Information
|
|
16
|
+
|
|
17
|
+
Ask the user for:
|
|
18
|
+
1. **MCP Name** - e.g., `tally`, `github`, `sentry`
|
|
19
|
+
2. **MCP URL** - e.g., `https://api.tally.so/mcp`
|
|
20
|
+
3. **Key Name in Keychain** - e.g., `TALLY_API_KEY` (must already be stored via add-key workflow)
|
|
21
|
+
4. **Target** - `claude` (Claude Code) or `opencode` or `both`
|
|
22
|
+
|
|
23
|
+
## Step 2: Verify Key Exists in Keychain
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
security find-generic-password -a "$USER" -s "KEY_NAME" -w >/dev/null 2>&1 && \
|
|
27
|
+
echo "[OK] KEY_NAME found in Keychain" || \
|
|
28
|
+
echo "[MISSING] KEY_NAME not in Keychain - add it first with the add-key workflow"
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
If missing, switch to `add-key.md` workflow first.
|
|
32
|
+
|
|
33
|
+
## Step 3: Add to Claude Code (~/.claude.json)
|
|
34
|
+
|
|
35
|
+
**CRITICAL: We directly edit the JSON file. We NEVER echo or print the key.**
|
|
36
|
+
|
|
37
|
+
Use this Python script to safely add the MCP without exposing the key:
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
python3 << 'PYTHON_SCRIPT'
|
|
41
|
+
import json
|
|
42
|
+
import subprocess
|
|
43
|
+
import os
|
|
44
|
+
|
|
45
|
+
# Configuration - REPLACE THESE
|
|
46
|
+
MCP_NAME = "MCP_NAME_HERE"
|
|
47
|
+
MCP_URL = "MCP_URL_HERE"
|
|
48
|
+
KEY_NAME = "KEY_NAME_HERE"
|
|
49
|
+
|
|
50
|
+
# Get key from Keychain (never printed)
|
|
51
|
+
result = subprocess.run(
|
|
52
|
+
["security", "find-generic-password", "-a", os.environ["USER"], "-s", KEY_NAME, "-w"],
|
|
53
|
+
capture_output=True, text=True
|
|
54
|
+
)
|
|
55
|
+
if result.returncode != 0:
|
|
56
|
+
print(f"[ERROR] Could not retrieve {KEY_NAME} from Keychain")
|
|
57
|
+
exit(1)
|
|
58
|
+
|
|
59
|
+
api_key = result.stdout.strip()
|
|
60
|
+
|
|
61
|
+
# Read existing config
|
|
62
|
+
config_path = os.path.expanduser("~/.claude.json")
|
|
63
|
+
with open(config_path, "r") as f:
|
|
64
|
+
config = json.load(f)
|
|
65
|
+
|
|
66
|
+
# Add MCP server
|
|
67
|
+
if "mcpServers" not in config:
|
|
68
|
+
config["mcpServers"] = {}
|
|
69
|
+
|
|
70
|
+
config["mcpServers"][MCP_NAME] = {
|
|
71
|
+
"type": "http",
|
|
72
|
+
"url": MCP_URL,
|
|
73
|
+
"headers": {
|
|
74
|
+
"Authorization": f"Bearer {api_key}"
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
# Write back
|
|
79
|
+
with open(config_path, "w") as f:
|
|
80
|
+
json.dump(config, f, indent=2)
|
|
81
|
+
|
|
82
|
+
print(f"[SUCCESS] Added {MCP_NAME} MCP to Claude Code")
|
|
83
|
+
print(f"[SUCCESS] Key retrieved from Keychain (never displayed)")
|
|
84
|
+
PYTHON_SCRIPT
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## Step 4: Add to OpenCode (~/.config/opencode/opencode.json)
|
|
88
|
+
|
|
89
|
+
For OpenCode, we can use environment variable syntax which is safer:
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
python3 << 'PYTHON_SCRIPT'
|
|
93
|
+
import json
|
|
94
|
+
import os
|
|
95
|
+
|
|
96
|
+
# Configuration - REPLACE THESE
|
|
97
|
+
MCP_NAME = "MCP_NAME_HERE"
|
|
98
|
+
MCP_URL = "MCP_URL_HERE"
|
|
99
|
+
KEY_NAME = "KEY_NAME_HERE"
|
|
100
|
+
|
|
101
|
+
config_path = os.path.expanduser("~/.config/opencode/opencode.json")
|
|
102
|
+
|
|
103
|
+
# Read existing config
|
|
104
|
+
with open(config_path, "r") as f:
|
|
105
|
+
config = json.load(f)
|
|
106
|
+
|
|
107
|
+
# Add MCP server with env var reference (OpenCode expands this at runtime)
|
|
108
|
+
if "mcp" not in config:
|
|
109
|
+
config["mcp"] = {}
|
|
110
|
+
|
|
111
|
+
config["mcp"][MCP_NAME] = {
|
|
112
|
+
"type": "remote",
|
|
113
|
+
"url": MCP_URL,
|
|
114
|
+
"enabled": True,
|
|
115
|
+
"headers": {
|
|
116
|
+
"Authorization": "Bearer {env:" + KEY_NAME + "}"
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
# Write back
|
|
121
|
+
with open(config_path, "w") as f:
|
|
122
|
+
json.dump(config, f, indent=2)
|
|
123
|
+
|
|
124
|
+
print(f"[SUCCESS] Added {MCP_NAME} MCP to OpenCode")
|
|
125
|
+
print(f"[SUCCESS] Configured to use {{env:{KEY_NAME}}} (loaded from environment at runtime)")
|
|
126
|
+
PYTHON_SCRIPT
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
**Note:** OpenCode requires the key to be exported in ~/.zshrc:
|
|
130
|
+
```bash
|
|
131
|
+
grep -q "KEY_NAME" ~/.zshrc || echo 'export KEY_NAME=$(security find-generic-password -a "$USER" -s "KEY_NAME" -w 2>/dev/null)' >> ~/.zshrc
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
## Step 5: Verify Installation
|
|
135
|
+
|
|
136
|
+
```bash
|
|
137
|
+
# For Claude Code
|
|
138
|
+
claude mcp list 2>/dev/null | grep -i "MCP_NAME" && echo "[OK] MCP_NAME visible in Claude Code" || echo "[CHECK] Restart Claude Code to see MCP_NAME"
|
|
139
|
+
|
|
140
|
+
# For OpenCode - just confirm the config exists
|
|
141
|
+
grep -q "MCP_NAME" ~/.config/opencode/opencode.json && echo "[OK] MCP_NAME configured in OpenCode"
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
## Step 6: Remind User to Restart
|
|
145
|
+
|
|
146
|
+
Tell the user:
|
|
147
|
+
- "Restart Claude Code / OpenCode for the MCP to be available"
|
|
148
|
+
- "Use `/mcp` to see connected servers"
|
|
149
|
+
- "The API key was pulled from Keychain and written directly to config - it was never displayed"
|
|
150
|
+
</process>
|
|
151
|
+
|
|
152
|
+
<warning>
|
|
153
|
+
## NEVER DO THIS
|
|
154
|
+
|
|
155
|
+
```bash
|
|
156
|
+
# UNSAFE - exposes key in terminal output!
|
|
157
|
+
claude mcp add --transport http tally https://api.tally.so/mcp \
|
|
158
|
+
--header "Authorization: Bearer $(security find-generic-password -a "$USER" -s "TALLY_API_KEY" -w)"
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
The `claude mcp add` command echoes the full config including headers back to the terminal. Always use this workflow instead.
|
|
162
|
+
</warning>
|
|
163
|
+
|
|
164
|
+
<success_criteria>
|
|
165
|
+
This workflow is complete when:
|
|
166
|
+
- [ ] Key verified in Keychain
|
|
167
|
+
- [ ] MCP config added to target (Claude Code and/or OpenCode)
|
|
168
|
+
- [ ] Key was NEVER echoed to terminal
|
|
169
|
+
- [ ] User reminded to restart their tool
|
|
170
|
+
- [ ] User can verify MCP is connected
|
|
171
|
+
</success_criteria>
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# Workflow: List Stored Keys
|
|
2
|
+
|
|
3
|
+
<required_reading>
|
|
4
|
+
**Read these reference files NOW:**
|
|
5
|
+
1. references/keychain-commands.md
|
|
6
|
+
</required_reading>
|
|
7
|
+
|
|
8
|
+
<process>
|
|
9
|
+
## Step 1: List All Generic Passwords
|
|
10
|
+
|
|
11
|
+
The `security` command can dump keychain info, but we need to filter for our API keys:
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
# List all generic password entries (shows service names, not values)
|
|
15
|
+
security dump-keychain | grep -A 4 'class: "genp"' | grep '"svce"' | cut -d'"' -f4 | sort -u
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
This shows the service names (which are our key names like `OPENAI_API_KEY`).
|
|
19
|
+
|
|
20
|
+
## Step 2: Filter for Common API Keys
|
|
21
|
+
|
|
22
|
+
Check for commonly used API key names:
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
COMMON_KEYS=(
|
|
26
|
+
"OPENAI_API_KEY"
|
|
27
|
+
"ANTHROPIC_API_KEY"
|
|
28
|
+
"STRIPE_SECRET_KEY"
|
|
29
|
+
"STRIPE_PUBLISHABLE_KEY"
|
|
30
|
+
"DATABASE_URL"
|
|
31
|
+
"SUPABASE_URL"
|
|
32
|
+
"SUPABASE_ANON_KEY"
|
|
33
|
+
"SUPABASE_SERVICE_ROLE_KEY"
|
|
34
|
+
"GITHUB_TOKEN"
|
|
35
|
+
"VERCEL_TOKEN"
|
|
36
|
+
"AWS_ACCESS_KEY_ID"
|
|
37
|
+
"AWS_SECRET_ACCESS_KEY"
|
|
38
|
+
"GOOGLE_API_KEY"
|
|
39
|
+
"SENDGRID_API_KEY"
|
|
40
|
+
"TWILIO_ACCOUNT_SID"
|
|
41
|
+
"TWILIO_AUTH_TOKEN"
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
echo "Checking for common API keys in Keychain..."
|
|
45
|
+
echo "==========================================="
|
|
46
|
+
for key in "${COMMON_KEYS[@]}"; do
|
|
47
|
+
if security find-generic-password -a "$USER" -s "$key" -w >/dev/null 2>&1; then
|
|
48
|
+
echo "[FOUND] $key"
|
|
49
|
+
fi
|
|
50
|
+
done
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Step 3: Present Results
|
|
54
|
+
|
|
55
|
+
Display found keys to the user in a clear format:
|
|
56
|
+
|
|
57
|
+
```
|
|
58
|
+
Available API Keys in Keychain:
|
|
59
|
+
================================
|
|
60
|
+
[FOUND] OPENAI_API_KEY
|
|
61
|
+
[FOUND] ANTHROPIC_API_KEY
|
|
62
|
+
[FOUND] DATABASE_URL
|
|
63
|
+
|
|
64
|
+
Not in Keychain (if you need them):
|
|
65
|
+
====================================
|
|
66
|
+
[ ] STRIPE_SECRET_KEY
|
|
67
|
+
[ ] GITHUB_TOKEN
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## Step 4: Offer Next Steps
|
|
71
|
+
|
|
72
|
+
Ask the user:
|
|
73
|
+
1. **Add a missing key** - Switch to add-key.md workflow
|
|
74
|
+
2. **Populate .env** - Switch to populate-env.md workflow
|
|
75
|
+
3. **Done** - Exit skill
|
|
76
|
+
</process>
|
|
77
|
+
|
|
78
|
+
<success_criteria>
|
|
79
|
+
This workflow is complete when:
|
|
80
|
+
- [ ] Available keys listed
|
|
81
|
+
- [ ] User can see which keys are stored
|
|
82
|
+
- [ ] User offered next steps
|
|
83
|
+
</success_criteria>
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
# Workflow: Populate .env from Keychain
|
|
2
|
+
|
|
3
|
+
<required_reading>
|
|
4
|
+
**Read these reference files NOW:**
|
|
5
|
+
1. references/keychain-commands.md
|
|
6
|
+
2. references/env-file-patterns.md
|
|
7
|
+
</required_reading>
|
|
8
|
+
|
|
9
|
+
<process>
|
|
10
|
+
## Step 1: Identify Required Keys
|
|
11
|
+
|
|
12
|
+
Ask the user or detect from the project:
|
|
13
|
+
- Check for `.env.example` or `.env.template` in the project
|
|
14
|
+
- Check for environment variable references in code (e.g., `process.env.X`, `os.environ["X"]`)
|
|
15
|
+
- Ask the user what keys the project needs
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
# Check for .env.example
|
|
19
|
+
cat .env.example 2>/dev/null || echo "No .env.example found"
|
|
20
|
+
|
|
21
|
+
# Search for env var usage
|
|
22
|
+
grep -r "process\.env\." --include="*.js" --include="*.ts" . 2>/dev/null | head -20
|
|
23
|
+
grep -r "os\.environ" --include="*.py" . 2>/dev/null | head -20
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Step 2: Check Keychain for Available Keys
|
|
27
|
+
|
|
28
|
+
For each required key, check if it exists in Keychain:
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
# Check if a key exists (returns 0 if found, non-zero if not)
|
|
32
|
+
security find-generic-password -a "$USER" -s "KEY_NAME" -w >/dev/null 2>&1 && echo "Found" || echo "Not found"
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Report which keys are available and which are missing.
|
|
36
|
+
|
|
37
|
+
## Step 3: Handle Missing Keys
|
|
38
|
+
|
|
39
|
+
If keys are missing from Keychain:
|
|
40
|
+
|
|
41
|
+
1. Ask the user if they want to add the missing keys now
|
|
42
|
+
2. If yes, switch to the `add-key.md` workflow for each missing key
|
|
43
|
+
3. If no, proceed with available keys only (warn about missing ones)
|
|
44
|
+
|
|
45
|
+
## Step 4: Ensure .gitignore Protection
|
|
46
|
+
|
|
47
|
+
Before writing .env, verify it's protected:
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
# Check if .gitignore exists and contains .env
|
|
51
|
+
if [ -f .gitignore ]; then
|
|
52
|
+
grep -q "^\.env$" .gitignore || echo ".env" >> .gitignore
|
|
53
|
+
else
|
|
54
|
+
echo ".env" > .gitignore
|
|
55
|
+
fi
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## Step 5: Generate .env File
|
|
59
|
+
|
|
60
|
+
Write each key to .env by retrieving from Keychain, then secure the file:
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
# Create or overwrite .env
|
|
64
|
+
> .env
|
|
65
|
+
|
|
66
|
+
# Add each key
|
|
67
|
+
echo "OPENAI_API_KEY=$(security find-generic-password -a "$USER" -s "OPENAI_API_KEY" -w 2>/dev/null)" >> .env
|
|
68
|
+
echo "ANTHROPIC_API_KEY=$(security find-generic-password -a "$USER" -s "ANTHROPIC_API_KEY" -w 2>/dev/null)" >> .env
|
|
69
|
+
# ... repeat for each key
|
|
70
|
+
|
|
71
|
+
# IMPORTANT: Secure file permissions (only owner can read/write)
|
|
72
|
+
chmod 600 .env
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
**Alternative: Create a reusable script**
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
cat > populate-env.sh << 'EOF'
|
|
79
|
+
#!/bin/bash
|
|
80
|
+
# Populate .env from macOS Keychain
|
|
81
|
+
# Add key names to the KEYS array
|
|
82
|
+
|
|
83
|
+
KEYS=(
|
|
84
|
+
"OPENAI_API_KEY"
|
|
85
|
+
"ANTHROPIC_API_KEY"
|
|
86
|
+
# Add more keys here
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
# Ensure .gitignore protection
|
|
90
|
+
[ -f .gitignore ] && grep -q "^\.env$" .gitignore || echo ".env" >> .gitignore
|
|
91
|
+
|
|
92
|
+
> .env
|
|
93
|
+
for key in "${KEYS[@]}"; do
|
|
94
|
+
value=$(security find-generic-password -a "$USER" -s "$key" -w 2>/dev/null)
|
|
95
|
+
if [ -n "$value" ]; then
|
|
96
|
+
echo "$key=$value" >> .env
|
|
97
|
+
echo "[OK] $key"
|
|
98
|
+
else
|
|
99
|
+
echo "[MISSING] $key"
|
|
100
|
+
fi
|
|
101
|
+
done
|
|
102
|
+
|
|
103
|
+
# Secure file permissions
|
|
104
|
+
chmod 600 .env
|
|
105
|
+
echo "Done. .env created with secure permissions (600)."
|
|
106
|
+
EOF
|
|
107
|
+
chmod +x populate-env.sh
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
## Step 6: Verify Success
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
# Count lines in .env (should match expected key count)
|
|
114
|
+
wc -l .env
|
|
115
|
+
|
|
116
|
+
# Show keys present (values hidden)
|
|
117
|
+
cut -d= -f1 .env
|
|
118
|
+
```
|
|
119
|
+
</process>
|
|
120
|
+
|
|
121
|
+
<success_criteria>
|
|
122
|
+
This workflow is complete when:
|
|
123
|
+
- [ ] All required keys identified
|
|
124
|
+
- [ ] Missing keys reported to user (and optionally added)
|
|
125
|
+
- [ ] .env is in .gitignore
|
|
126
|
+
- [ ] .env file created with keys from Keychain
|
|
127
|
+
- [ ] User can verify which keys were populated
|
|
128
|
+
</success_criteria>
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# Workflow: Remove Key from Keychain
|
|
2
|
+
|
|
3
|
+
<required_reading>
|
|
4
|
+
**Read these reference files NOW:**
|
|
5
|
+
1. references/keychain-commands.md
|
|
6
|
+
</required_reading>
|
|
7
|
+
|
|
8
|
+
<process>
|
|
9
|
+
## Step 1: Confirm Key Name
|
|
10
|
+
|
|
11
|
+
Ask the user which key they want to remove. Verify it exists first:
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
security find-generic-password -a "$USER" -s "KEY_NAME" -w >/dev/null 2>&1 && \
|
|
15
|
+
echo "Key found: KEY_NAME" || \
|
|
16
|
+
echo "Key not found: KEY_NAME"
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Step 2: Confirm Deletion
|
|
20
|
+
|
|
21
|
+
**IMPORTANT**: Deletion is permanent. Confirm with the user:
|
|
22
|
+
|
|
23
|
+
"Are you sure you want to delete KEY_NAME from Keychain? This cannot be undone. You will need to re-add the key if you need it again."
|
|
24
|
+
|
|
25
|
+
## Step 3: Delete the Key
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
security delete-generic-password -a "$USER" -s "KEY_NAME"
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Expected output on success:
|
|
32
|
+
```
|
|
33
|
+
password has been deleted.
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Step 4: Verify Deletion
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
security find-generic-password -a "$USER" -s "KEY_NAME" -w >/dev/null 2>&1 && \
|
|
40
|
+
echo "Error: Key still exists" || \
|
|
41
|
+
echo "Confirmed: KEY_NAME has been removed"
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Step 5: Update .env if Needed
|
|
45
|
+
|
|
46
|
+
If a .env file exists and contains this key, warn the user:
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
if [ -f .env ] && grep -q "KEY_NAME=" .env; then
|
|
50
|
+
echo "Warning: .env still contains KEY_NAME"
|
|
51
|
+
echo "You may want to regenerate .env or remove this line"
|
|
52
|
+
fi
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Step 6: Offer Next Steps
|
|
56
|
+
|
|
57
|
+
Ask the user:
|
|
58
|
+
1. **Remove another key** - Repeat this workflow
|
|
59
|
+
2. **Re-add this key** - Switch to add-key.md workflow
|
|
60
|
+
3. **Update .env** - Switch to populate-env.md workflow
|
|
61
|
+
4. **Done** - Exit skill
|
|
62
|
+
</process>
|
|
63
|
+
|
|
64
|
+
<success_criteria>
|
|
65
|
+
This workflow is complete when:
|
|
66
|
+
- [ ] Key existence confirmed
|
|
67
|
+
- [ ] User confirmed deletion
|
|
68
|
+
- [ ] Key deleted from Keychain
|
|
69
|
+
- [ ] Deletion verified
|
|
70
|
+
- [ ] User warned about .env if applicable
|
|
71
|
+
</success_criteria>
|
package/package.json
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "no-more-leaked-keys",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Stop accidentally exposing API keys. Secure key management for Claude Code using macOS Keychain.",
|
|
5
|
+
"bin": {
|
|
6
|
+
"no-more-leaked-keys": "./bin/install.js"
|
|
7
|
+
},
|
|
8
|
+
"scripts": {
|
|
9
|
+
"postinstall": "node bin/install.js"
|
|
10
|
+
},
|
|
11
|
+
"keywords": [
|
|
12
|
+
"api-keys",
|
|
13
|
+
"security",
|
|
14
|
+
"secrets",
|
|
15
|
+
"keychain",
|
|
16
|
+
"macos",
|
|
17
|
+
"claude",
|
|
18
|
+
"claude-code",
|
|
19
|
+
"opencode",
|
|
20
|
+
"dotenv",
|
|
21
|
+
"env",
|
|
22
|
+
"environment-variables",
|
|
23
|
+
"mcp",
|
|
24
|
+
"credentials",
|
|
25
|
+
"secret-management"
|
|
26
|
+
],
|
|
27
|
+
"author": "Andrew Naegele",
|
|
28
|
+
"license": "SEE LICENSE IN LICENSE",
|
|
29
|
+
"repository": {
|
|
30
|
+
"type": "git",
|
|
31
|
+
"url": "https://github.com/Vibe-Marketer/no-more-leaked-keys.git"
|
|
32
|
+
},
|
|
33
|
+
"homepage": "https://github.com/Vibe-Marketer/no-more-leaked-keys#readme",
|
|
34
|
+
"bugs": {
|
|
35
|
+
"url": "https://github.com/Vibe-Marketer/no-more-leaked-keys/issues"
|
|
36
|
+
},
|
|
37
|
+
"os": ["darwin"],
|
|
38
|
+
"engines": {
|
|
39
|
+
"node": ">=14.0.0"
|
|
40
|
+
},
|
|
41
|
+
"files": [
|
|
42
|
+
"bin/",
|
|
43
|
+
"keychain-secrets/",
|
|
44
|
+
"commands/",
|
|
45
|
+
"hooks/",
|
|
46
|
+
"LICENSE",
|
|
47
|
+
"README.md"
|
|
48
|
+
]
|
|
49
|
+
}
|