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.
@@ -0,0 +1,17 @@
1
+ #!/bin/bash
2
+ # Hook: Block unsafe 'claude mcp add' commands that would expose API keys
3
+ # Installed by: no-more-leaked-keys skill
4
+
5
+ # This hook is triggered by Claude Code's PreToolUse event
6
+ # It checks if the command contains 'claude mcp add' with auth headers
7
+
8
+ INPUT=$(cat)
9
+
10
+ # Check if this is a Bash command containing unsafe mcp add
11
+ if echo "$INPUT" | grep -q "claude mcp add" && echo "$INPUT" | grep -qi "header.*authorization\|header.*bearer"; then
12
+ echo '{"decision": "block", "message": "BLOCKED: claude mcp add with auth headers exposes your API key in terminal output!\n\nUse /secrets or /add-mcp instead - these pull keys from Keychain securely without exposing them.\n\nSee: https://github.com/Vibe-Marketer/no-more-leaked-keys"}'
13
+ exit 0
14
+ fi
15
+
16
+ # Allow all other commands
17
+ echo '{"decision": "allow"}'
@@ -0,0 +1,129 @@
1
+ ---
2
+ name: keychain-secrets
3
+ description: "Securely manage API keys using macOS Keychain. Use when working with .env files, adding new API keys, adding MCP servers, or populating environment variables from Keychain. Triggers on: api key, keychain, .env, environment variables, secrets, credentials, mcp, mcp server, claude mcp add."
4
+ ---
5
+
6
+ <essential_principles>
7
+ ## Security-First API Key Management
8
+
9
+ This skill uses macOS Keychain to securely store and retrieve API keys. Keys are never stored in plain text, never committed to git, and never exposed on screen during input.
10
+
11
+ ### Principle 1: Keychain is the Source of Truth
12
+
13
+ API keys live in Keychain, not in files. The `.env` file is populated at runtime from Keychain. This means:
14
+ - Keys are encrypted at rest by macOS
15
+ - Keys can be used across any project on your machine
16
+ - Accidental exposure via git or screen share is impossible
17
+
18
+ ### Principle 2: Safe Input for New Keys
19
+
20
+ When adding keys to Keychain, we use `read -s` to prevent the key from appearing on screen. The key is passed directly to the `security` command without echoing.
21
+
22
+ ### Principle 3: .env Files are Ephemeral
23
+
24
+ The `.env` file should be in `.gitignore` and can be regenerated anytime from Keychain. Never edit `.env` directly with secrets - always add to Keychain first.
25
+
26
+ ### Key Naming Convention
27
+
28
+ Use SCREAMING_SNAKE_CASE that matches the environment variable name:
29
+ - `OPENAI_API_KEY`
30
+ - `ANTHROPIC_API_KEY`
31
+ - `STRIPE_SECRET_KEY`
32
+ - `DATABASE_URL`
33
+ </essential_principles>
34
+
35
+ <intake>
36
+ What would you like to do?
37
+
38
+ 1. **Populate .env from Keychain** - Retrieve stored keys and write them to a .env file
39
+ 2. **Add a new key to Keychain** - Securely store a new API key (won't appear on screen)
40
+ 3. **Add MCP server securely** - Add an MCP to Claude Code or OpenCode WITHOUT exposing keys
41
+ 4. **List stored keys** - See what keys are available in your Keychain
42
+ 5. **Remove a key** - Delete a key from Keychain
43
+
44
+ **Wait for response before proceeding.**
45
+ </intake>
46
+
47
+ <routing>
48
+ | Response | Workflow |
49
+ |----------|----------|
50
+ | 1, "populate", "env", "retrieve", "get", "write env" | `workflows/populate-env.md` |
51
+ | 2, "add", "store", "new key", "save" | `workflows/add-key.md` |
52
+ | 3, "mcp", "add mcp", "claude mcp", "opencode mcp" | `workflows/add-mcp.md` |
53
+ | 4, "list", "show", "available", "what keys" | `workflows/list-keys.md` |
54
+ | 5, "remove", "delete", "revoke" | `workflows/remove-key.md` |
55
+
56
+ **After reading the workflow, follow it exactly.**
57
+ </routing>
58
+
59
+ <critical_warning>
60
+ ## NEVER USE `claude mcp add` WITH AUTH HEADERS
61
+
62
+ The `claude mcp add` command **exposes secrets in terminal output**:
63
+
64
+ ```bash
65
+ # DANGEROUS - This will print your API key to the terminal!
66
+ claude mcp add --transport http tally https://api.tally.so/mcp \
67
+ --header "Authorization: Bearer $TALLY_API_KEY"
68
+ ```
69
+
70
+ **Always use option 3 (Add MCP server securely) instead.** This workflow:
71
+ - Pulls the key from Keychain silently
72
+ - Writes directly to config files
73
+ - Never echoes the key anywhere
74
+ </critical_warning>
75
+
76
+ <reference_index>
77
+ All domain knowledge in `references/`:
78
+
79
+ **Commands:** keychain-commands.md - All macOS security CLI patterns
80
+ **Env Files:** env-file-patterns.md - Safe .env file handling
81
+ </reference_index>
82
+
83
+ <workflows_index>
84
+ | Workflow | Purpose |
85
+ |----------|---------|
86
+ | populate-env.md | Retrieve keys from Keychain and write to .env |
87
+ | add-key.md | Securely add a new API key to Keychain |
88
+ | add-mcp.md | Securely add MCP server to Claude Code / OpenCode |
89
+ | list-keys.md | Show available keys in Keychain |
90
+ | remove-key.md | Delete a key from Keychain |
91
+ </workflows_index>
92
+
93
+ <quick_commands>
94
+ ## Quick Reference
95
+
96
+ **Add key (Claude runs this - you just paste):**
97
+ ```bash
98
+ read -s -p "Paste API key: " K && security add-generic-password -a "$USER" -s "KEY_NAME" -w "$K" && unset K && pbcopy < /dev/null && echo -e "\n[OK] Stored"
99
+ ```
100
+
101
+ **Retrieve key:**
102
+ ```bash
103
+ security find-generic-password -a "$USER" -s "KEY_NAME" -w
104
+ ```
105
+
106
+ **Delete key:**
107
+ ```bash
108
+ security delete-generic-password -a "$USER" -s "KEY_NAME"
109
+ ```
110
+
111
+ **Populate .env from Keychain:**
112
+ ```bash
113
+ echo "OPENAI_API_KEY=$(security find-generic-password -a "$USER" -s "OPENAI_API_KEY" -w 2>/dev/null)" >> .env && chmod 600 .env
114
+ ```
115
+ </quick_commands>
116
+
117
+ <security_measures>
118
+ ## Security Measures
119
+
120
+ This skill implements multiple layers of protection:
121
+
122
+ 1. **Silent input** - Keys never appear on screen when you paste
123
+ 2. **Memory cleanup** - Variables cleared immediately after use
124
+ 3. **Clipboard clearing** - Auto-clears clipboard after storing key
125
+ 4. **Encrypted storage** - macOS Keychain uses AES-256 encryption
126
+ 5. **File permissions** - .env created with 600 permissions (owner only)
127
+ 6. **Git protection** - Automatically adds .env to .gitignore
128
+ 7. **No logging** - Keys never written to terminal history
129
+ </security_measures>
@@ -0,0 +1,220 @@
1
+ # .env File Patterns Reference
2
+
3
+ <overview>
4
+ Safe patterns for working with .env files when using Keychain as the source of truth.
5
+ </overview>
6
+
7
+ <gitignore_protection>
8
+ ## Always Protect .env
9
+
10
+ Before creating any .env file, ensure it's in .gitignore:
11
+
12
+ ```bash
13
+ # Check if .gitignore exists and has .env
14
+ if [ -f .gitignore ]; then
15
+ grep -q "^\.env$" .gitignore || echo ".env" >> .gitignore
16
+ else
17
+ echo ".env" > .gitignore
18
+ fi
19
+ ```
20
+
21
+ Also consider ignoring:
22
+ ```
23
+ .env
24
+ .env.local
25
+ .env.*.local
26
+ *.env
27
+ ```
28
+ </gitignore_protection>
29
+
30
+ <env_file_format>
31
+ ## .env File Format
32
+
33
+ Standard format:
34
+ ```bash
35
+ KEY_NAME=value
36
+ ANOTHER_KEY=another_value
37
+ ```
38
+
39
+ Rules:
40
+ - No spaces around `=`
41
+ - No quotes needed for simple values
42
+ - Use quotes for values with spaces: `KEY="value with spaces"`
43
+ - Comments start with `#`
44
+ - Blank lines are ignored
45
+ </env_file_format>
46
+
47
+ <generation_patterns>
48
+ ## Generating .env from Keychain
49
+
50
+ ### Simple Generation
51
+ ```bash
52
+ > .env # Clear/create file
53
+ echo "OPENAI_API_KEY=$(security find-generic-password -a "$USER" -s "OPENAI_API_KEY" -w)" >> .env
54
+ echo "DATABASE_URL=$(security find-generic-password -a "$USER" -s "DATABASE_URL" -w)" >> .env
55
+ ```
56
+
57
+ ### With Error Handling
58
+ ```bash
59
+ add_key_to_env() {
60
+ local key=$1
61
+ local value=$(security find-generic-password -a "$USER" -s "$key" -w 2>/dev/null)
62
+ if [ -n "$value" ]; then
63
+ echo "$key=$value" >> .env
64
+ echo "Added $key"
65
+ else
66
+ echo "# $key not found in Keychain" >> .env
67
+ echo "Warning: $key not found"
68
+ fi
69
+ }
70
+
71
+ > .env
72
+ add_key_to_env "OPENAI_API_KEY"
73
+ add_key_to_env "DATABASE_URL"
74
+ ```
75
+
76
+ ### Reusable Script (populate-env.sh)
77
+ ```bash
78
+ #!/bin/bash
79
+ # populate-env.sh - Generate .env from Keychain
80
+
81
+ KEYS=(
82
+ "OPENAI_API_KEY"
83
+ "ANTHROPIC_API_KEY"
84
+ "DATABASE_URL"
85
+ # Add your keys here
86
+ )
87
+
88
+ # Ensure .gitignore protection
89
+ if [ -f .gitignore ]; then
90
+ grep -q "^\.env$" .gitignore || echo ".env" >> .gitignore
91
+ else
92
+ echo ".env" > .gitignore
93
+ fi
94
+
95
+ # Generate .env
96
+ > .env
97
+ found=0
98
+ missing=0
99
+
100
+ for key in "${KEYS[@]}"; do
101
+ value=$(security find-generic-password -a "$USER" -s "$key" -w 2>/dev/null)
102
+ if [ -n "$value" ]; then
103
+ echo "$key=$value" >> .env
104
+ ((found++))
105
+ else
106
+ echo "# $key - NOT FOUND" >> .env
107
+ ((missing++))
108
+ fi
109
+ done
110
+
111
+ echo "Generated .env: $found keys found, $missing missing"
112
+ ```
113
+ </generation_patterns>
114
+
115
+ <project_templates>
116
+ ## .env.example Template
117
+
118
+ Create a .env.example that documents required keys without values:
119
+
120
+ ```bash
121
+ # .env.example - Required environment variables
122
+ # Copy to .env and populate from Keychain: ./populate-env.sh
123
+
124
+ # OpenAI API
125
+ OPENAI_API_KEY=
126
+
127
+ # Anthropic API
128
+ ANTHROPIC_API_KEY=
129
+
130
+ # Database
131
+ DATABASE_URL=
132
+
133
+ # Stripe (if using payments)
134
+ STRIPE_SECRET_KEY=
135
+ STRIPE_PUBLISHABLE_KEY=
136
+ ```
137
+
138
+ This file IS committed to git (no secrets) and documents what the project needs.
139
+ </project_templates>
140
+
141
+ <verification>
142
+ ## Verifying .env
143
+
144
+ ### Check Keys Present (values hidden)
145
+ ```bash
146
+ cut -d= -f1 .env | grep -v "^#" | grep -v "^$"
147
+ ```
148
+
149
+ ### Count Keys
150
+ ```bash
151
+ grep -c "=" .env
152
+ ```
153
+
154
+ ### Check for Empty Values
155
+ ```bash
156
+ grep "=$" .env # Lines ending with = have no value
157
+ ```
158
+
159
+ ### Validate Format
160
+ ```bash
161
+ # Lines should match: KEY=value or be comments/blank
162
+ grep -v "^#" .env | grep -v "^$" | grep -v "^[A-Z_]*=" && echo "Invalid lines found" || echo "Format OK"
163
+ ```
164
+ </verification>
165
+
166
+ <framework_loading>
167
+ ## Framework-Specific Loading
168
+
169
+ ### Node.js (dotenv)
170
+ ```javascript
171
+ require('dotenv').config();
172
+ // or in ES modules:
173
+ import 'dotenv/config';
174
+ ```
175
+
176
+ ### Python (python-dotenv)
177
+ ```python
178
+ from dotenv import load_dotenv
179
+ load_dotenv()
180
+ ```
181
+
182
+ ### Next.js
183
+ Automatic - .env.local is loaded by default.
184
+
185
+ ### Vite
186
+ Automatic - .env files are loaded. Use `VITE_` prefix for client-side vars.
187
+ </framework_loading>
188
+
189
+ <security_reminders>
190
+ ## Security Reminders
191
+
192
+ 1. **Never commit .env** - Always in .gitignore
193
+ 2. **Never log env vars** - Don't `console.log(process.env)`
194
+ 3. **Never expose in client code** - Only use server-side
195
+ 4. **Regenerate, don't edit** - If you need to change keys, update Keychain and regenerate .env
196
+ 5. **.env.example is safe** - Commit it to document required vars (no values)
197
+ 6. **Set file permissions** - Always `chmod 600 .env` (owner read/write only)
198
+ 7. **Clear clipboard** - After pasting keys, clipboard is auto-cleared to prevent accidents
199
+ </security_reminders>
200
+
201
+ <file_permissions>
202
+ ## Securing .env File Permissions
203
+
204
+ After creating .env, always set restrictive permissions:
205
+
206
+ ```bash
207
+ chmod 600 .env
208
+ ```
209
+
210
+ This means:
211
+ - Owner can read and write (you)
212
+ - Group has no access
213
+ - Others have no access
214
+
215
+ Verify with:
216
+ ```bash
217
+ ls -la .env
218
+ # Should show: -rw-------
219
+ ```
220
+ </file_permissions>
@@ -0,0 +1,159 @@
1
+ # macOS Keychain Security Commands Reference
2
+
3
+ <overview>
4
+ macOS provides the `security` CLI tool to interact with Keychain from the terminal. This reference covers all commands needed for API key management.
5
+ </overview>
6
+
7
+ <command_reference>
8
+ ## Core Commands
9
+
10
+ ### Add a Password
11
+ ```bash
12
+ security add-generic-password -a "$USER" -s "SERVICE_NAME" -w "PASSWORD"
13
+ ```
14
+
15
+ Parameters:
16
+ - `-a` : Account name (use `$USER` for current user)
17
+ - `-s` : Service name (the key identifier, e.g., `OPENAI_API_KEY`)
18
+ - `-w` : Password/secret value
19
+
20
+ **Will fail if key already exists.** Delete first to update.
21
+
22
+ ### Retrieve a Password
23
+ ```bash
24
+ security find-generic-password -a "$USER" -s "SERVICE_NAME" -w
25
+ ```
26
+
27
+ Parameters:
28
+ - `-a` : Account name
29
+ - `-s` : Service name
30
+ - `-w` : Output only the password (not metadata)
31
+
32
+ Returns just the password value. Exit code 0 on success, non-zero if not found.
33
+
34
+ ### Delete a Password
35
+ ```bash
36
+ security delete-generic-password -a "$USER" -s "SERVICE_NAME"
37
+ ```
38
+
39
+ Removes the entry from Keychain. Cannot be undone.
40
+
41
+ ### Check if Password Exists
42
+ ```bash
43
+ security find-generic-password -a "$USER" -s "SERVICE_NAME" -w >/dev/null 2>&1
44
+ echo $? # 0 = exists, non-zero = not found
45
+ ```
46
+
47
+ ### List All Entries
48
+ ```bash
49
+ # List all generic passwords (shows metadata, not values)
50
+ security dump-keychain | grep -A 4 'class: "genp"'
51
+
52
+ # Extract just service names
53
+ security dump-keychain | grep -A 4 'class: "genp"' | grep '"svce"' | cut -d'"' -f4 | sort -u
54
+ ```
55
+ </command_reference>
56
+
57
+ <secure_patterns>
58
+ ## Secure Input Patterns
59
+
60
+ ### Silent Input with Clipboard Clear (Recommended)
61
+ ```bash
62
+ read -s -p "Paste value: " VALUE && \
63
+ security add-generic-password -a "$USER" -s "KEY_NAME" -w "$VALUE" && \
64
+ unset VALUE && \
65
+ pbcopy < /dev/null && \
66
+ echo -e "\n[OK] Stored + clipboard cleared"
67
+ ```
68
+
69
+ - `read -s` : Silent mode (no echo to screen)
70
+ - `unset VALUE` : Clear from shell memory after use
71
+ - `pbcopy < /dev/null` : Clear clipboard (prevents accidental paste elsewhere)
72
+ - Key never appears in terminal, history, or remains in clipboard
73
+
74
+ ### Update Existing (Delete + Add)
75
+ ```bash
76
+ security delete-generic-password -a "$USER" -s "KEY_NAME" 2>/dev/null; \
77
+ read -s -p "Enter new value: " VALUE && \
78
+ security add-generic-password -a "$USER" -s "KEY_NAME" -w "$VALUE" && \
79
+ unset VALUE
80
+ ```
81
+
82
+ ### Batch Check Multiple Keys
83
+ ```bash
84
+ for key in OPENAI_API_KEY ANTHROPIC_API_KEY; do
85
+ if security find-generic-password -a "$USER" -s "$key" -w >/dev/null 2>&1; then
86
+ echo "[OK] $key"
87
+ else
88
+ echo "[MISSING] $key"
89
+ fi
90
+ done
91
+ ```
92
+ </secure_patterns>
93
+
94
+ <environment_integration>
95
+ ## Using with Environment Variables
96
+
97
+ ### Export to Shell
98
+ ```bash
99
+ export OPENAI_API_KEY=$(security find-generic-password -a "$USER" -s "OPENAI_API_KEY" -w)
100
+ ```
101
+
102
+ ### In .zshrc or .bashrc (loads on shell start)
103
+ ```bash
104
+ # ~/.zshrc
105
+ export OPENAI_API_KEY=$(security find-generic-password -a "$USER" -s "OPENAI_API_KEY" -w 2>/dev/null)
106
+ ```
107
+
108
+ ### Generate .env File
109
+ ```bash
110
+ echo "OPENAI_API_KEY=$(security find-generic-password -a "$USER" -s "OPENAI_API_KEY" -w)" >> .env
111
+ ```
112
+
113
+ ### Use in Scripts (Python)
114
+ ```python
115
+ import subprocess
116
+ import os
117
+
118
+ def get_keychain_secret(key_name):
119
+ result = subprocess.run(
120
+ ["security", "find-generic-password", "-a", os.environ["USER"], "-s", key_name, "-w"],
121
+ capture_output=True, text=True
122
+ )
123
+ return result.stdout.strip() if result.returncode == 0 else None
124
+ ```
125
+
126
+ ### Use in Scripts (Node.js)
127
+ ```javascript
128
+ const { execSync } = require('child_process');
129
+
130
+ function getKeychainSecret(keyName) {
131
+ try {
132
+ return execSync(`security find-generic-password -a "$USER" -s "${keyName}" -w`)
133
+ .toString().trim();
134
+ } catch {
135
+ return null;
136
+ }
137
+ }
138
+ ```
139
+ </environment_integration>
140
+
141
+ <troubleshooting>
142
+ ## Common Issues
143
+
144
+ ### "security: SecKeychainSearchCopyNext: The specified item could not be found"
145
+ Key doesn't exist. Check the exact name with `security dump-keychain`.
146
+
147
+ ### "already has an entry"
148
+ Key already exists. Delete first, then add:
149
+ ```bash
150
+ security delete-generic-password -a "$USER" -s "KEY_NAME"
151
+ security add-generic-password -a "$USER" -s "KEY_NAME" -w "value"
152
+ ```
153
+
154
+ ### "User interaction is not allowed"
155
+ Running in a context without Keychain access (e.g., some CI environments). Not applicable for local development.
156
+
157
+ ### Password Prompt on Access
158
+ macOS may prompt for your login password the first time a new application accesses a Keychain item. Click "Always Allow" to prevent future prompts.
159
+ </troubleshooting>
@@ -0,0 +1,99 @@
1
+ # Workflow: Add Key to Keychain Securely
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: Get Key Name
10
+
11
+ Ask the user what key they want to add. Suggest common options:
12
+ - `OPENAI_API_KEY`
13
+ - `ANTHROPIC_API_KEY`
14
+ - `STRIPE_SECRET_KEY`
15
+ - `DATABASE_URL`
16
+ - Or let them specify a custom name (SCREAMING_SNAKE_CASE)
17
+
18
+ ## Step 2: Check if Key Already Exists and Handle It
19
+
20
+ Run this check:
21
+ ```bash
22
+ security find-generic-password -a "$USER" -s "KEY_NAME" -w >/dev/null 2>&1 && echo "EXISTS" || echo "NEW"
23
+ ```
24
+
25
+ If it exists, ask: "This key already exists. Replace it with a new value?"
26
+ - If yes, delete the old one first: `security delete-generic-password -a "$USER" -s "KEY_NAME" 2>/dev/null`
27
+ - If no, cancel and offer to add a different key
28
+
29
+ ## Step 3: Open Terminal Window for Secure Input
30
+
31
+ **Claude opens a new Terminal window where the user pastes their key.**
32
+
33
+ Use this AppleScript command to open Terminal with the secure input prompt:
34
+
35
+ ```bash
36
+ osascript -e 'tell application "Terminal"
37
+ activate
38
+ do script "clear && echo \"\" && echo \"====================================\" && echo \" SECURE API KEY INPUT\" && echo \"====================================\" && echo \"\" && echo \"Paste your key below (it will be invisible)\" && echo \"Then press ENTER\" && echo \"\" && echo -n \"KEY_NAME: \" && read -s K && security add-generic-password -a \"$USER\" -s \"KEY_NAME\" -w \"$K\" && unset K && pbcopy < /dev/null && echo \"\" && echo \"\" && echo \"[SUCCESS] KEY_NAME stored in Keychain\" && echo \"[SUCCESS] Clipboard cleared\" && echo \"\" && echo \"You can close this window now.\" && echo \"\""
39
+ end tell'
40
+ ```
41
+
42
+ **What happens:**
43
+ 1. A new Terminal window opens and comes to front
44
+ 2. User sees clear instructions
45
+ 3. User pastes (Cmd+V) - nothing appears on screen
46
+ 4. User presses Enter
47
+ 5. Key is stored in Keychain (encrypted)
48
+ 6. Clipboard is cleared
49
+ 7. Success message shown
50
+ 8. User closes the window
51
+
52
+ **After running:** Wait for user to confirm they completed it, then verify storage.
53
+
54
+ ## Step 4: Verify Storage
55
+
56
+ Immediately verify the key was stored:
57
+ ```bash
58
+ security find-generic-password -a "$USER" -s "KEY_NAME" -w >/dev/null 2>&1 && \
59
+ echo "[VERIFIED] KEY_NAME is now stored in Keychain" || \
60
+ echo "[ERROR] KEY_NAME was not stored - please try again"
61
+ ```
62
+
63
+ ## Step 5: Offer Next Steps
64
+
65
+ Ask the user:
66
+ 1. **Add another key** - Repeat this workflow
67
+ 2. **Populate .env** - Switch to populate-env.md workflow
68
+ 3. **Done** - Exit skill
69
+ </process>
70
+
71
+ <terminal_commands>
72
+ ## Terminal Window Commands (Claude runs these via osascript)
73
+
74
+ **Template - replace KEY_NAME:**
75
+ ```bash
76
+ osascript -e 'tell application "Terminal"
77
+ activate
78
+ do script "clear && echo \"\" && echo \"====================================\" && echo \" SECURE API KEY INPUT\" && echo \"====================================\" && echo \"\" && echo \"Paste your key below (it will be invisible)\" && echo \"Then press ENTER\" && echo \"\" && echo -n \"KEY_NAME: \" && read -s K && security add-generic-password -a \"$USER\" -s \"KEY_NAME\" -w \"$K\" && unset K && pbcopy < /dev/null && echo \"\" && echo \"\" && echo \"[SUCCESS] KEY_NAME stored in Keychain\" && echo \"[SUCCESS] Clipboard cleared\" && echo \"\" && echo \"You can close this window now.\""
79
+ end tell'
80
+ ```
81
+
82
+ **For updating existing key (delete first):**
83
+ ```bash
84
+ osascript -e 'tell application "Terminal"
85
+ activate
86
+ do script "clear && echo \"\" && echo \"====================================\" && echo \" UPDATE API KEY\" && echo \"====================================\" && echo \"\" && security delete-generic-password -a \"$USER\" -s \"KEY_NAME\" 2>/dev/null && echo \"Paste your NEW key below (invisible)\" && echo \"Then press ENTER\" && echo \"\" && echo -n \"KEY_NAME: \" && read -s K && security add-generic-password -a \"$USER\" -s \"KEY_NAME\" -w \"$K\" && unset K && pbcopy < /dev/null && echo \"\" && echo \"\" && echo \"[SUCCESS] KEY_NAME updated in Keychain\" && echo \"[SUCCESS] Clipboard cleared\" && echo \"\" && echo \"You can close this window now.\""
87
+ end tell'
88
+ ```
89
+ </terminal_commands>
90
+
91
+ <success_criteria>
92
+ This workflow is complete when:
93
+ - [ ] Key name confirmed with user
94
+ - [ ] Existing key check performed
95
+ - [ ] User provided with secure input command
96
+ - [ ] User confirmed they ran the command
97
+ - [ ] Key storage verified
98
+ - [ ] User offered next steps
99
+ </success_criteria>