@chriscode/hush 5.0.0 → 5.0.2
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/dist/cli.js +39 -26
- package/dist/commands/check.d.ts +3 -3
- package/dist/commands/check.d.ts.map +1 -1
- package/dist/commands/check.js +27 -31
- package/dist/commands/decrypt.d.ts +2 -2
- package/dist/commands/decrypt.d.ts.map +1 -1
- package/dist/commands/decrypt.js +52 -55
- package/dist/commands/edit.d.ts +2 -2
- package/dist/commands/edit.d.ts.map +1 -1
- package/dist/commands/edit.js +10 -12
- package/dist/commands/encrypt.d.ts +2 -2
- package/dist/commands/encrypt.d.ts.map +1 -1
- package/dist/commands/encrypt.js +27 -29
- package/dist/commands/expansions.d.ts +2 -2
- package/dist/commands/expansions.d.ts.map +1 -1
- package/dist/commands/expansions.js +46 -44
- package/dist/commands/has.d.ts +2 -2
- package/dist/commands/has.d.ts.map +1 -1
- package/dist/commands/has.js +12 -15
- package/dist/commands/init.d.ts +2 -2
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +92 -100
- package/dist/commands/inspect.d.ts +2 -2
- package/dist/commands/inspect.d.ts.map +1 -1
- package/dist/commands/inspect.js +14 -16
- package/dist/commands/keys.d.ts +2 -1
- package/dist/commands/keys.d.ts.map +1 -1
- package/dist/commands/keys.js +47 -49
- package/dist/commands/list.d.ts +2 -2
- package/dist/commands/list.d.ts.map +1 -1
- package/dist/commands/list.js +11 -14
- package/dist/commands/migrate.d.ts +2 -1
- package/dist/commands/migrate.d.ts.map +1 -1
- package/dist/commands/migrate.js +38 -37
- package/dist/commands/push.d.ts +2 -2
- package/dist/commands/push.d.ts.map +1 -1
- package/dist/commands/push.js +41 -45
- package/dist/commands/resolve.d.ts +2 -2
- package/dist/commands/resolve.d.ts.map +1 -1
- package/dist/commands/resolve.js +25 -28
- package/dist/commands/run.d.ts +2 -2
- package/dist/commands/run.d.ts.map +1 -1
- package/dist/commands/run.js +35 -39
- package/dist/commands/set.d.ts +2 -2
- package/dist/commands/set.d.ts.map +1 -1
- package/dist/commands/set.js +61 -70
- package/dist/commands/skill.d.ts +2 -2
- package/dist/commands/skill.d.ts.map +1 -1
- package/dist/commands/skill.js +149 -459
- package/dist/commands/status.d.ts +2 -2
- package/dist/commands/status.d.ts.map +1 -1
- package/dist/commands/status.js +48 -52
- package/dist/commands/template.d.ts +2 -2
- package/dist/commands/template.d.ts.map +1 -1
- package/dist/commands/template.js +36 -39
- package/dist/commands/trace.d.ts +2 -2
- package/dist/commands/trace.d.ts.map +1 -1
- package/dist/commands/trace.js +16 -19
- package/dist/config/loader.js +3 -3
- package/dist/context.d.ts +3 -0
- package/dist/context.d.ts.map +1 -0
- package/dist/context.js +60 -0
- package/dist/core/parse.js +3 -3
- package/dist/core/sops.js +9 -9
- package/dist/core/template.d.ts +2 -2
- package/dist/core/template.d.ts.map +1 -1
- package/dist/core/template.js +11 -12
- package/dist/lib/age.js +9 -9
- package/dist/lib/fs.d.ts +25 -0
- package/dist/lib/fs.d.ts.map +1 -0
- package/dist/lib/fs.js +36 -0
- package/dist/lib/onepassword.d.ts.map +1 -1
- package/dist/lib/onepassword.js +41 -4
- package/dist/types.d.ts +92 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/version-check.js +5 -5
- package/package.json +3 -2
package/dist/commands/skill.js
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
|
-
import { existsSync, mkdirSync, writeFileSync } from 'node:fs';
|
|
2
1
|
import { createInterface } from 'node:readline';
|
|
3
2
|
import { homedir } from 'node:os';
|
|
4
|
-
import { join } from 'node:path';
|
|
5
3
|
import pc from 'picocolors';
|
|
6
4
|
const SKILL_FILES = {
|
|
7
5
|
'SKILL.md': `---
|
|
@@ -14,7 +12,7 @@ allowed-tools: Bash(hush:*), Bash(npx hush:*), Bash(brew:*), Bash(npm:*), Bash(p
|
|
|
14
12
|
|
|
15
13
|
**CRITICAL: NEVER read .hush files directly.** Always use \`npx hush status\`, \`npx hush inspect\`, or \`npx hush has\` to check secrets.
|
|
16
14
|
|
|
17
|
-
Hush keeps secrets **encrypted at rest** at the project root using \`.hush.encrypted\` files. Subdirectory \`.
|
|
15
|
+
Hush keeps secrets **encrypted at rest** at the project root using \`.hush.encrypted\` files. Subdirectory \`.hush\` files are **templates** (safe to commit) that reference root secrets via \`\${VAR}\` syntax.
|
|
18
16
|
|
|
19
17
|
## First Step: Investigate Current State
|
|
20
18
|
|
|
@@ -41,7 +39,7 @@ This tells you:
|
|
|
41
39
|
| \`age key not found\` | Missing encryption key | \`npx hush keys setup\` |
|
|
42
40
|
| \`Project: not set\` | Key management limited | Add \`project:\` to hush.yaml |
|
|
43
41
|
|
|
44
|
-
**Note:**
|
|
42
|
+
**Note:** Any \`.env\` file is suspect - Hush uses \`.hush\` files everywhere. Subdirectory \`.hush\` files are templates (safe to commit).
|
|
45
43
|
|
|
46
44
|
## Decision Tree: What Do I Do?
|
|
47
45
|
|
|
@@ -72,7 +70,8 @@ npx hush inspect # See what secrets exist
|
|
|
72
70
|
### Scenario 4: Need to Add/Modify Secrets
|
|
73
71
|
|
|
74
72
|
\`\`\`bash
|
|
75
|
-
npx hush set <KEY>
|
|
73
|
+
npx hush set <VALUE> <KEY> # Add inline (value and key provided)
|
|
74
|
+
npx hush set <KEY> # Add interactively (prompts for value)
|
|
76
75
|
npx hush edit # Edit all secrets in $EDITOR
|
|
77
76
|
npx hush inspect # Verify changes
|
|
78
77
|
\`\`\`
|
|
@@ -110,12 +109,12 @@ targets:
|
|
|
110
109
|
include: [NEXT_PUBLIC_*] # All matching vars auto-flow
|
|
111
110
|
\`\`\`
|
|
112
111
|
|
|
113
|
-
### Pull (subdirectory .
|
|
112
|
+
### Pull (subdirectory .hush templates)
|
|
114
113
|
|
|
115
114
|
Best for transformation, renaming, or explicit dependencies:
|
|
116
115
|
|
|
117
116
|
\`\`\`bash
|
|
118
|
-
# apps/mobile/.
|
|
117
|
+
# apps/mobile/.hush (committed - it's just a template)
|
|
119
118
|
EXPO_PUBLIC_API_URL=\${API_URL} # Rename from root
|
|
120
119
|
PORT=\${PORT:-8081} # Default value
|
|
121
120
|
\`\`\`
|
|
@@ -126,7 +125,7 @@ PORT=\${PORT:-8081} # Default value
|
|
|
126
125
|
|
|
127
126
|
## Subdirectory Templates (Pull-Based)
|
|
128
127
|
|
|
129
|
-
When a subdirectory needs to rename, transform, or add defaults to root secrets, create a \`.
|
|
128
|
+
When a subdirectory needs to rename, transform, or add defaults to root secrets, create a \`.hush\` template file in that subdirectory.
|
|
130
129
|
|
|
131
130
|
### Step-by-Step Setup
|
|
132
131
|
|
|
@@ -137,7 +136,7 @@ npx hush inspect # From repo root - verify secrets are configured
|
|
|
137
136
|
|
|
138
137
|
**2. Create subdirectory template (this file is committed to git):**
|
|
139
138
|
\`\`\`bash
|
|
140
|
-
# apps/mobile/.
|
|
139
|
+
# apps/mobile/.hush
|
|
141
140
|
EXPO_PUBLIC_API_URL=\${API_URL} # Pull API_URL from root, rename it
|
|
142
141
|
EXPO_PUBLIC_STRIPE_KEY=\${STRIPE_KEY} # Pull and rename
|
|
143
142
|
PORT=\${PORT:-8081} # Use root PORT, or default to 8081
|
|
@@ -153,7 +152,7 @@ npx hush run -- npm start
|
|
|
153
152
|
Hush automatically:
|
|
154
153
|
1. Finds the project root (where \`hush.yaml\` is)
|
|
155
154
|
2. Decrypts root secrets
|
|
156
|
-
3. Loads the local \`.
|
|
155
|
+
3. Loads the local \`.hush\` template
|
|
157
156
|
4. Resolves \`\${VAR}\` references against root secrets
|
|
158
157
|
5. **Filters root secrets based on target config (include/exclude)**
|
|
159
158
|
6. **Merges them (Template overrides Target)**
|
|
@@ -171,7 +170,7 @@ Hush automatically:
|
|
|
171
170
|
|
|
172
171
|
**Expo/React Native app:**
|
|
173
172
|
\`\`\`bash
|
|
174
|
-
# apps/mobile/.
|
|
173
|
+
# apps/mobile/.hush
|
|
175
174
|
EXPO_PUBLIC_API_URL=\${API_URL}
|
|
176
175
|
EXPO_PUBLIC_STRIPE_KEY=\${STRIPE_PUBLISHABLE_KEY}
|
|
177
176
|
EXPO_PUBLIC_ENV=\${ENV:-development}
|
|
@@ -179,7 +178,7 @@ EXPO_PUBLIC_ENV=\${ENV:-development}
|
|
|
179
178
|
|
|
180
179
|
**Next.js app:**
|
|
181
180
|
\`\`\`bash
|
|
182
|
-
# apps/web/.
|
|
181
|
+
# apps/web/.hush
|
|
183
182
|
NEXT_PUBLIC_API_URL=\${API_URL}
|
|
184
183
|
NEXT_PUBLIC_STRIPE_KEY=\${STRIPE_PUBLISHABLE_KEY}
|
|
185
184
|
DATABASE_URL=\${DATABASE_URL}
|
|
@@ -187,20 +186,30 @@ DATABASE_URL=\${DATABASE_URL}
|
|
|
187
186
|
|
|
188
187
|
**API server with defaults:**
|
|
189
188
|
\`\`\`bash
|
|
190
|
-
# apps/api/.
|
|
189
|
+
# apps/api/.hush
|
|
191
190
|
DATABASE_URL=\${DATABASE_URL}
|
|
192
191
|
PORT=\${PORT:-8787}
|
|
193
192
|
LOG_LEVEL=\${LOG_LEVEL:-info}
|
|
194
193
|
\`\`\`
|
|
195
194
|
|
|
196
|
-
### Important Notes
|
|
195
|
+
### Important Notes: File Conventions
|
|
197
196
|
|
|
198
|
-
|
|
199
|
-
|
|
197
|
+
**All Hush files use \`.hush\` extension - never \`.env\`:**
|
|
198
|
+
|
|
199
|
+
| Location | File Type | Contains | Committed? |
|
|
200
|
+
|----------|-----------|----------|------------|
|
|
201
|
+
| **Root** | \`.hush\` | Actual secrets | NO (gitignored) |
|
|
202
|
+
| **Root** | \`.hush.encrypted\` | Encrypted secrets | YES |
|
|
203
|
+
| **Subdirectory** | \`.hush\` | Templates with \`\${VAR}\` | YES (safe) |
|
|
204
|
+
| **Subdirectory** | \`.hush.development\` | Dev-specific templates | YES (safe) |
|
|
205
|
+
| **Anywhere** | \`.env\` | ⚠️ LEGACY - delete! | NO |
|
|
206
|
+
|
|
207
|
+
**Key rules:**
|
|
208
|
+
- **Everything is \`.hush\`** - consistent naming throughout
|
|
209
|
+
- **Any \`.env\` file is wrong** - legacy file that should be deleted
|
|
210
|
+
- Subdirectory \`.hush\` templates are safe to commit (no actual secrets)
|
|
200
211
|
- **Run from the subdirectory** - \`hush run\` auto-detects the project root
|
|
201
|
-
- **Root secrets stay encrypted** - subdirectory templates just reference them
|
|
202
212
|
- **Self-reference works** - \`PORT=\${PORT:-3000}\` uses root PORT if set, else 3000
|
|
203
|
-
- **Security warnings only apply to root** - subdirectory .env files are always safe
|
|
204
213
|
|
|
205
214
|
---
|
|
206
215
|
|
|
@@ -211,7 +220,7 @@ LOG_LEVEL=\${LOG_LEVEL:-info}
|
|
|
211
220
|
| \`npx hush status\` | **Full diagnostic** | First step, always |
|
|
212
221
|
| \`npx hush inspect\` | See variables (masked) | Check what's configured |
|
|
213
222
|
| \`npx hush has <KEY>\` | Check specific variable | Verify a secret exists |
|
|
214
|
-
| \`npx hush set <KEY>\` | Add secret
|
|
223
|
+
| \`npx hush set [VALUE] <KEY>\` | Add secret (prompts if no value) | User needs to set a value |
|
|
215
224
|
| \`npx hush edit\` | Edit all secrets | Bulk editing |
|
|
216
225
|
| \`npx hush run -- <cmd>\` | Run with secrets in memory | Actually use the secrets |
|
|
217
226
|
| \`npx hush init\` | Initialize Hush | First-time setup |
|
|
@@ -304,25 +313,34 @@ npx hush has API_KEY -q # Quiet: exit code only (0=set, 1=missing)
|
|
|
304
313
|
|
|
305
314
|
## Adding/Modifying Secrets
|
|
306
315
|
|
|
307
|
-
### Add a single secret
|
|
316
|
+
### Add a single secret (three methods)
|
|
317
|
+
|
|
318
|
+
**Method 1: Inline value (recommended for AI agents)**
|
|
319
|
+
\`\`\`bash
|
|
320
|
+
npx hush set "postgres://user:pass@host/db" DATABASE_URL
|
|
321
|
+
npx hush set "sk_live_xxx" STRIPE_KEY -e production
|
|
322
|
+
\`\`\`
|
|
308
323
|
|
|
324
|
+
**Method 2: Interactive prompt (for users)**
|
|
309
325
|
\`\`\`bash
|
|
310
|
-
npx hush set DATABASE_URL #
|
|
326
|
+
npx hush set DATABASE_URL # Prompts user for value
|
|
311
327
|
npx hush set API_KEY -e production # Set in production secrets
|
|
312
328
|
npx hush set DEBUG --local # Set personal local override
|
|
313
329
|
\`\`\`
|
|
314
330
|
|
|
315
|
-
|
|
316
|
-
**You never see the actual secret - just invoke the command!**
|
|
317
|
-
|
|
318
|
-
### Add a secret via pipe (for scripts/automation)
|
|
319
|
-
|
|
331
|
+
**Method 3: Pipe (for scripts/automation)**
|
|
320
332
|
\`\`\`bash
|
|
321
333
|
echo "my-secret-value" | npx hush set MY_KEY
|
|
322
334
|
cat secret.txt | npx hush set CERT_CONTENT
|
|
323
335
|
\`\`\`
|
|
324
336
|
|
|
325
|
-
|
|
337
|
+
### GUI dialog for AI agents
|
|
338
|
+
|
|
339
|
+
When running in a non-TTY environment (like AI agents), use \`--gui\`:
|
|
340
|
+
\`\`\`bash
|
|
341
|
+
npx hush set API_KEY --gui # Opens visible dialog
|
|
342
|
+
\`\`\`
|
|
343
|
+
The dialog shows the pasted value for easy verification.
|
|
326
344
|
|
|
327
345
|
---
|
|
328
346
|
|
|
@@ -463,13 +481,15 @@ Customize targets for your monorepo. Common patterns:
|
|
|
463
481
|
|
|
464
482
|
### Step 5: Create initial \`.hush\` files
|
|
465
483
|
|
|
466
|
-
Create \`.hush\` with shared secrets
|
|
484
|
+
Create \`.hush\` with shared secrets at the **repository root**:
|
|
467
485
|
|
|
468
486
|
\`\`\`bash
|
|
469
|
-
# .hush
|
|
487
|
+
# .hush (root level - contains actual secrets)
|
|
470
488
|
DATABASE_URL=postgres://localhost/mydb
|
|
489
|
+
SUPABASE_URL=https://xxx.supabase.co
|
|
490
|
+
SUPABASE_ANON_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
|
|
491
|
+
STRIPE_SECRET_KEY=sk_test_xxx
|
|
471
492
|
API_KEY=your_api_key_here
|
|
472
|
-
NEXT_PUBLIC_API_URL=http://localhost:3000
|
|
473
493
|
\`\`\`
|
|
474
494
|
|
|
475
495
|
Create \`.hush.development\` for dev-specific values:
|
|
@@ -488,6 +508,50 @@ DEBUG=false
|
|
|
488
508
|
LOG_LEVEL=error
|
|
489
509
|
\`\`\`
|
|
490
510
|
|
|
511
|
+
### Step 5b: Set up subdirectory templates (for monorepos)
|
|
512
|
+
|
|
513
|
+
For packages that need secrets with different prefixes, create a **template** \`.hush\` file in the subdirectory.
|
|
514
|
+
|
|
515
|
+
**Example: Expo app needs Supabase with EXPO_PUBLIC_ prefix**
|
|
516
|
+
|
|
517
|
+
\`\`\`bash
|
|
518
|
+
# apps/mobile/.hush (committed to git - template with variable references)
|
|
519
|
+
EXPO_PUBLIC_SUPABASE_URL=\${SUPABASE_URL}
|
|
520
|
+
EXPO_PUBLIC_SUPABASE_ANON_KEY=\${SUPABASE_ANON_KEY}
|
|
521
|
+
EXPO_PUBLIC_API_URL=\${API_URL:-http://localhost:3000}
|
|
522
|
+
\`\`\`
|
|
523
|
+
|
|
524
|
+
**Example: Next.js app needs different prefixes**
|
|
525
|
+
|
|
526
|
+
\`\`\`bash
|
|
527
|
+
# apps/web/.hush (committed to git - template)
|
|
528
|
+
NEXT_PUBLIC_SUPABASE_URL=\${SUPABASE_URL}
|
|
529
|
+
NEXT_PUBLIC_SUPABASE_ANON_KEY=\${SUPABASE_ANON_KEY}
|
|
530
|
+
DATABASE_URL=\${DATABASE_URL}
|
|
531
|
+
\`\`\`
|
|
532
|
+
|
|
533
|
+
**File structure:**
|
|
534
|
+
\`\`\`
|
|
535
|
+
repo-root/
|
|
536
|
+
├── .hush # Actual secrets (gitignored)
|
|
537
|
+
├── .hush.encrypted # Encrypted secrets (committed)
|
|
538
|
+
├── apps/
|
|
539
|
+
│ ├── mobile/
|
|
540
|
+
│ │ └── .hush # Template with \${VAR} refs (committed)
|
|
541
|
+
│ └── web/
|
|
542
|
+
│ └── .hush # Template with \${VAR} refs (committed)
|
|
543
|
+
\`\`\`
|
|
544
|
+
|
|
545
|
+
**How it works:**
|
|
546
|
+
1. Root \`.hush\` contains actual secrets → encrypted to \`.hush.encrypted\`
|
|
547
|
+
2. Subdirectory \`.hush\` templates reference root secrets via \`\${VAR}\`
|
|
548
|
+
3. Run \`hush run\` from subdirectory - it resolves templates automatically
|
|
549
|
+
|
|
550
|
+
\`\`\`bash
|
|
551
|
+
cd apps/mobile
|
|
552
|
+
npx hush run -- expo start # Template vars resolved from root secrets
|
|
553
|
+
\`\`\`
|
|
554
|
+
|
|
491
555
|
### Step 6: Encrypt secrets
|
|
492
556
|
|
|
493
557
|
\`\`\`bash
|
|
@@ -598,7 +662,7 @@ Complete reference for all Hush CLI commands with flags, options, and examples.
|
|
|
598
662
|
|
|
599
663
|
## Security Model: Encrypted at Rest
|
|
600
664
|
|
|
601
|
-
All secrets are stored encrypted
|
|
665
|
+
All secrets are stored encrypted in \`.hush.encrypted\` files. Never read \`.env\` files directly - they indicate legacy/misconfigured setup. Use \`hush inspect\` or \`hush has\` to check secrets safely.
|
|
602
666
|
|
|
603
667
|
## Global Options
|
|
604
668
|
|
|
@@ -636,23 +700,27 @@ hush run -t api -- wrangler dev # Run filtered for 'api' target
|
|
|
636
700
|
|
|
637
701
|
---
|
|
638
702
|
|
|
639
|
-
### hush set <KEY> ⭐
|
|
703
|
+
### hush set [VALUE] <KEY> ⭐
|
|
640
704
|
|
|
641
|
-
Add or update a single secret
|
|
705
|
+
Add or update a single secret. Prompts for value if not provided inline.
|
|
642
706
|
|
|
643
707
|
\`\`\`bash
|
|
644
|
-
hush set DATABASE_URL #
|
|
708
|
+
hush set DATABASE_URL # Prompts for value interactively
|
|
709
|
+
hush set "postgres://..." DATABASE_URL # Inline value (no prompt)
|
|
645
710
|
hush set API_KEY -e production # Set in production secrets
|
|
646
711
|
hush set DEBUG --local # Set personal local override
|
|
647
712
|
\`\`\`
|
|
648
713
|
|
|
649
|
-
|
|
714
|
+
**Input methods (in priority order):**
|
|
715
|
+
1. **Inline value**: \`hush set "myvalue" KEY\` - value provided directly
|
|
716
|
+
2. **Piped input**: \`echo "myvalue" | hush set KEY\` - reads from stdin
|
|
717
|
+
3. **Interactive prompt**: Opens dialog/prompt for user input
|
|
650
718
|
|
|
651
|
-
**
|
|
719
|
+
**GUI dialog (--gui flag):**
|
|
652
720
|
\`\`\`bash
|
|
653
|
-
|
|
654
|
-
cat cert.pem | hush set CERTIFICATE
|
|
721
|
+
hush set API_KEY --gui # Opens visible dialog (for AI agents)
|
|
655
722
|
\`\`\`
|
|
723
|
+
The GUI dialog shows the value as you type/paste for easier verification.
|
|
656
724
|
|
|
657
725
|
---
|
|
658
726
|
|
|
@@ -786,7 +854,7 @@ hush trace STRIPE_SECRET_KEY # Trace another variable
|
|
|
786
854
|
|
|
787
855
|
### hush template
|
|
788
856
|
|
|
789
|
-
Show the resolved template for the current directory's \`.
|
|
857
|
+
Show the resolved template for the current directory's \`.hush\` file.
|
|
790
858
|
|
|
791
859
|
\`\`\`bash
|
|
792
860
|
cd apps/mobile
|
|
@@ -805,7 +873,7 @@ hush template -e production # Show for production
|
|
|
805
873
|
|
|
806
874
|
### hush expansions
|
|
807
875
|
|
|
808
|
-
Show the expansion graph across all subdirectories that have \`.
|
|
876
|
+
Show the expansion graph across all subdirectories that have \`.hush\` templates.
|
|
809
877
|
|
|
810
878
|
\`\`\`bash
|
|
811
879
|
hush expansions # Scan all subdirectories
|
|
@@ -813,7 +881,7 @@ hush expansions -e production # Show for production
|
|
|
813
881
|
\`\`\`
|
|
814
882
|
|
|
815
883
|
**Output shows:**
|
|
816
|
-
- Which subdirectories have \`.
|
|
884
|
+
- Which subdirectories have \`.hush\` templates
|
|
817
885
|
- What variables each template references from root
|
|
818
886
|
- Resolution status for each reference
|
|
819
887
|
|
|
@@ -1065,8 +1133,8 @@ PORT=\${PORT:-3000}
|
|
|
1065
1133
|
# System environment (explicit opt-in)
|
|
1066
1134
|
CI=\${env:CI}
|
|
1067
1135
|
|
|
1068
|
-
# Pull from root (subdirectory .
|
|
1069
|
-
# apps/mobile/.
|
|
1136
|
+
# Pull from root (subdirectory .hush can reference root secrets)
|
|
1137
|
+
# apps/mobile/.hush:
|
|
1070
1138
|
EXPO_PUBLIC_API_URL=\${API_URL} # Renamed from root
|
|
1071
1139
|
\`\`\`
|
|
1072
1140
|
|
|
@@ -1083,385 +1151,7 @@ targets:
|
|
|
1083
1151
|
|
|
1084
1152
|
**Pull (subdirectory templates):** Transformation, renaming, defaults
|
|
1085
1153
|
\`\`\`bash
|
|
1086
|
-
#
|
|
1087
|
-
EXPO_PUBLIC_API_URL=\${API_URL} # Rename required
|
|
1088
|
-
PORT=\${PORT:-3000} # Default value
|
|
1089
|
-
\`\`\`
|
|
1090
|
-
|
|
1091
|
-
**Decision:** Use push for "all X → Y". Use pull for rename/transform/defaults.
|
|
1092
|
-
`,
|
|
1093
|
-
'examples/workflows.md': `# Hush Workflow Examples
|
|
1094
|
-
|
|
1095
|
-
Step-by-step examples for common workflows when working with secrets.
|
|
1096
|
-
|
|
1097
|
-
**CRITICAL: NEVER read .env files directly. Use hush commands instead.**
|
|
1098
|
-
|
|
1099
|
-
---
|
|
1100
|
-
|
|
1101
|
-
## First-Time Setup (Most Important!)
|
|
1102
|
-
|
|
1103
|
-
### "Help me set up Hush for this project"
|
|
1104
|
-
|
|
1105
|
-
**Step 1: Check current state**
|
|
1106
|
-
\`\`\`bash
|
|
1107
|
-
npx hush status
|
|
1108
|
-
\`\`\`
|
|
1109
|
-
|
|
1110
|
-
This will show:
|
|
1111
|
-
- If Hush is already configured
|
|
1112
|
-
- If there are unencrypted .env files (security risk!)
|
|
1113
|
-
- What prerequisites are missing
|
|
1114
|
-
|
|
1115
|
-
**Step 2: Based on the output, follow the appropriate path:**
|
|
1116
|
-
|
|
1117
|
-
#### Path A: "SECURITY WARNING: Unencrypted .env files detected"
|
|
1118
|
-
\`\`\`bash
|
|
1119
|
-
# If migrating from v4 (has .env.encrypted files):
|
|
1120
|
-
npx hush migrate # Converts to .hush.encrypted format
|
|
1121
|
-
|
|
1122
|
-
# If new setup with plaintext .env files:
|
|
1123
|
-
mv .env .hush # Rename to .hush
|
|
1124
|
-
npx hush init # If no hush.yaml exists
|
|
1125
|
-
npx hush encrypt # Encrypts .hush files
|
|
1126
|
-
npx hush status # Verify the warning is gone
|
|
1127
|
-
\`\`\`
|
|
1128
|
-
|
|
1129
|
-
#### Path B: "No hush.yaml found"
|
|
1130
|
-
\`\`\`bash
|
|
1131
|
-
npx hush init # Creates config and sets up keys
|
|
1132
|
-
npx hush set <KEY> # Add secrets (if none exist yet)
|
|
1133
|
-
\`\`\`
|
|
1134
|
-
|
|
1135
|
-
#### Path C: "age key not found"
|
|
1136
|
-
\`\`\`bash
|
|
1137
|
-
npx hush keys setup # Pull from 1Password or generate new key
|
|
1138
|
-
\`\`\`
|
|
1139
|
-
|
|
1140
|
-
#### Path D: Everything looks good
|
|
1141
|
-
\`\`\`bash
|
|
1142
|
-
npx hush inspect # See what secrets are configured
|
|
1143
|
-
\`\`\`
|
|
1144
|
-
|
|
1145
|
-
---
|
|
1146
|
-
|
|
1147
|
-
## Running Programs (Most Common)
|
|
1148
|
-
|
|
1149
|
-
### "Start the development server"
|
|
1150
|
-
\`\`\`bash
|
|
1151
|
-
npx hush run -- npm run dev
|
|
1152
|
-
\`\`\`
|
|
1153
|
-
|
|
1154
|
-
### "Build for production"
|
|
1155
|
-
\`\`\`bash
|
|
1156
|
-
npx hush run -e production -- npm run build
|
|
1157
|
-
\`\`\`
|
|
1158
|
-
|
|
1159
|
-
### "Run tests with secrets"
|
|
1160
|
-
\`\`\`bash
|
|
1161
|
-
npx hush run -- npm test
|
|
1162
|
-
\`\`\`
|
|
1163
|
-
|
|
1164
|
-
### "Run Wrangler for Cloudflare Worker"
|
|
1165
|
-
\`\`\`bash
|
|
1166
|
-
npx hush run -t api -- wrangler dev
|
|
1167
|
-
\`\`\`
|
|
1168
|
-
|
|
1169
|
-
---
|
|
1170
|
-
|
|
1171
|
-
## Checking Secrets
|
|
1172
|
-
|
|
1173
|
-
### "What environment variables does this project use?"
|
|
1174
|
-
\`\`\`bash
|
|
1175
|
-
npx hush inspect # Shows all variables with masked values
|
|
1176
|
-
\`\`\`
|
|
1177
|
-
|
|
1178
|
-
### "Is the database configured?"
|
|
1179
|
-
\`\`\`bash
|
|
1180
|
-
npx hush has DATABASE_URL
|
|
1181
|
-
\`\`\`
|
|
1182
|
-
|
|
1183
|
-
If "not found", help user add it:
|
|
1184
|
-
\`\`\`bash
|
|
1185
|
-
npx hush set DATABASE_URL
|
|
1186
|
-
\`\`\`
|
|
1187
|
-
Tell user: "Enter your database URL when prompted"
|
|
1188
|
-
|
|
1189
|
-
### "Check all required secrets"
|
|
1190
|
-
\`\`\`bash
|
|
1191
|
-
npx hush has DATABASE_URL -q && \\
|
|
1192
|
-
npx hush has API_KEY -q && \\
|
|
1193
|
-
echo "All configured" || \\
|
|
1194
|
-
echo "Some missing"
|
|
1195
|
-
\`\`\`
|
|
1196
|
-
|
|
1197
|
-
---
|
|
1198
|
-
|
|
1199
|
-
## Adding Secrets
|
|
1200
|
-
|
|
1201
|
-
### "Help me add DATABASE_URL"
|
|
1202
|
-
\`\`\`bash
|
|
1203
|
-
npx hush set DATABASE_URL
|
|
1204
|
-
\`\`\`
|
|
1205
|
-
Tell user: "Enter your database URL when prompted (input will be hidden)"
|
|
1206
|
-
|
|
1207
|
-
### "Add a production-only secret"
|
|
1208
|
-
\`\`\`bash
|
|
1209
|
-
npx hush set STRIPE_SECRET_KEY -e production
|
|
1210
|
-
\`\`\`
|
|
1211
|
-
|
|
1212
|
-
### "Add a personal local override"
|
|
1213
|
-
\`\`\`bash
|
|
1214
|
-
npx hush set DEBUG --local
|
|
1215
|
-
\`\`\`
|
|
1216
|
-
|
|
1217
|
-
### "Edit multiple secrets at once"
|
|
1218
|
-
\`\`\`bash
|
|
1219
|
-
npx hush edit
|
|
1220
|
-
\`\`\`
|
|
1221
|
-
Tell user: "Your editor will open. Add or modify secrets, then save and close."
|
|
1222
|
-
|
|
1223
|
-
---
|
|
1224
|
-
|
|
1225
|
-
## Debugging
|
|
1226
|
-
|
|
1227
|
-
### "My app can't find DATABASE_URL"
|
|
1228
|
-
|
|
1229
|
-
1. **Trace the variable** to see where it exists and where it goes:
|
|
1230
|
-
\`\`\`bash
|
|
1231
|
-
npx hush trace DATABASE_URL
|
|
1232
|
-
\`\`\`
|
|
1233
|
-
This shows which source files have it and which targets include/exclude it.
|
|
1234
|
-
|
|
1235
|
-
2. **Check if it exists** in your current environment:
|
|
1236
|
-
\`\`\`bash
|
|
1237
|
-
npx hush has DATABASE_URL
|
|
1238
|
-
\`\`\`
|
|
1239
|
-
|
|
1240
|
-
3. **Resolve the target** to see what variables it receives:
|
|
1241
|
-
\`\`\`bash
|
|
1242
|
-
npx hush resolve api-workers
|
|
1243
|
-
\`\`\`
|
|
1244
|
-
|
|
1245
|
-
### "Target is missing expected variables"
|
|
1246
|
-
|
|
1247
|
-
\`\`\`bash
|
|
1248
|
-
npx hush resolve <target-name> # See included/excluded variables
|
|
1249
|
-
npx hush resolve <target-name> -e prod # Check production
|
|
1250
|
-
\`\`\`
|
|
1251
|
-
|
|
1252
|
-
Look at the 🚫 EXCLUDED section to see which pattern is filtering out your variable.
|
|
1253
|
-
|
|
1254
|
-
### "Wrangler dev not seeing secrets"
|
|
1255
|
-
|
|
1256
|
-
If you are using \`hush run -- wrangler dev\` and secrets are missing:
|
|
1257
|
-
|
|
1258
|
-
**Step 1: Check for blocking files**
|
|
1259
|
-
\`\`\`bash
|
|
1260
|
-
ls -la .dev.vars # If this exists, it blocks Hush secrets
|
|
1261
|
-
\`\`\`
|
|
1262
|
-
|
|
1263
|
-
**Step 2: Delete the blocking file**
|
|
1264
|
-
\`\`\`bash
|
|
1265
|
-
rm .dev.vars
|
|
1266
|
-
\`\`\`
|
|
1267
|
-
|
|
1268
|
-
**Step 3: Run normally**
|
|
1269
|
-
\`\`\`bash
|
|
1270
|
-
npx hush run -t api -- wrangler dev
|
|
1271
|
-
\`\`\`
|
|
1272
|
-
|
|
1273
|
-
**Step 4: If still not working, update Wrangler**
|
|
1274
|
-
\`\`\`bash
|
|
1275
|
-
npm update wrangler
|
|
1276
|
-
\`\`\`
|
|
1277
|
-
|
|
1278
|
-
**Why this happens:**
|
|
1279
|
-
- Wrangler has a strict rule: if \`.dev.vars\` exists (even empty!), it ignores ALL environment variables
|
|
1280
|
-
- Hush automatically sets \`CLOUDFLARE_INCLUDE_PROCESS_ENV=true\` for you
|
|
1281
|
-
- But Wrangler only respects this when no \`.dev.vars\` file exists
|
|
1282
|
-
- Older Wrangler versions may not support \`CLOUDFLARE_INCLUDE_PROCESS_ENV\` at all
|
|
1283
|
-
|
|
1284
|
-
**Prevention tip:** Never use \`hush decrypt\` for Wrangler targets—always use \`hush run\`.
|
|
1285
|
-
|
|
1286
|
-
### "Variable appears in wrong places"
|
|
1287
|
-
|
|
1288
|
-
\`\`\`bash
|
|
1289
|
-
npx hush trace <VARIABLE_NAME>
|
|
1290
|
-
\`\`\`
|
|
1291
|
-
|
|
1292
|
-
This shows the full disposition across all targets - which include it and which exclude it.
|
|
1293
|
-
|
|
1294
|
-
### "Push is missing some secrets"
|
|
1295
|
-
|
|
1296
|
-
\`\`\`bash
|
|
1297
|
-
npx hush push --dry-run --verbose
|
|
1298
|
-
\`\`\`
|
|
1299
|
-
|
|
1300
|
-
This shows exactly what would be pushed to each target.
|
|
1301
|
-
|
|
1302
|
-
---
|
|
1303
|
-
|
|
1304
|
-
## Team Workflows
|
|
1305
|
-
|
|
1306
|
-
### "New team member setup"
|
|
1307
|
-
|
|
1308
|
-
Guide them through these steps:
|
|
1309
|
-
\`\`\`bash
|
|
1310
|
-
# 1. Pull key from 1Password (or get from team member)
|
|
1311
|
-
npx hush keys setup
|
|
1312
|
-
|
|
1313
|
-
# 2. Verify setup
|
|
1314
|
-
npx hush status
|
|
1315
|
-
|
|
1316
|
-
# 3. Check secrets are accessible
|
|
1317
|
-
npx hush inspect
|
|
1318
|
-
|
|
1319
|
-
# 4. Start developing
|
|
1320
|
-
npx hush run -- npm run dev
|
|
1321
|
-
\`\`\`
|
|
1322
|
-
|
|
1323
|
-
### "Someone added new secrets"
|
|
1324
|
-
\`\`\`bash
|
|
1325
|
-
git pull
|
|
1326
|
-
npx hush inspect # See what's new
|
|
1327
|
-
\`\`\`
|
|
1328
|
-
|
|
1329
|
-
---
|
|
1330
|
-
|
|
1331
|
-
## Deployment
|
|
1332
|
-
|
|
1333
|
-
### "Push to Cloudflare Workers"
|
|
1334
|
-
\`\`\`bash
|
|
1335
|
-
npx hush push --dry-run # Preview first
|
|
1336
|
-
npx hush push # Actually push
|
|
1337
|
-
npx hush push -t api # Push specific target
|
|
1338
|
-
\`\`\`
|
|
1339
|
-
|
|
1340
|
-
### "Push to Cloudflare Pages"
|
|
1341
|
-
|
|
1342
|
-
First, add \`push_to\` to your target in \`hush.yaml\`:
|
|
1343
|
-
\`\`\`yaml
|
|
1344
|
-
targets:
|
|
1345
|
-
- name: app
|
|
1346
|
-
path: ./app
|
|
1347
|
-
format: dotenv
|
|
1348
|
-
push_to:
|
|
1349
|
-
type: cloudflare-pages
|
|
1350
|
-
project: my-pages-project
|
|
1351
|
-
\`\`\`
|
|
1352
|
-
|
|
1353
|
-
Then push:
|
|
1354
|
-
\`\`\`bash
|
|
1355
|
-
npx hush push -t app --dry-run # Preview first
|
|
1356
|
-
npx hush push -t app # Actually push
|
|
1357
|
-
\`\`\`
|
|
1358
|
-
|
|
1359
|
-
### "Build and deploy"
|
|
1360
|
-
\`\`\`bash
|
|
1361
|
-
npx hush run -e production -- npm run build
|
|
1362
|
-
npx hush push
|
|
1363
|
-
\`\`\`
|
|
1364
|
-
|
|
1365
|
-
---
|
|
1366
|
-
|
|
1367
|
-
## Setting Up Subdirectory Templates (Pull-Based Secrets)
|
|
1368
|
-
|
|
1369
|
-
### "Set up secrets for a subdirectory app (Expo, Next.js, etc.)"
|
|
1370
|
-
|
|
1371
|
-
**Use this when:** You need to rename, transform, or add defaults to root secrets for a specific package.
|
|
1372
|
-
|
|
1373
|
-
**Step 1: Verify root secrets exist**
|
|
1374
|
-
\`\`\`bash
|
|
1375
|
-
cd /path/to/repo/root
|
|
1376
|
-
npx hush inspect
|
|
1377
|
-
\`\`\`
|
|
1378
|
-
|
|
1379
|
-
**Step 2: Create the subdirectory template file**
|
|
1380
|
-
|
|
1381
|
-
Create a \`.env\` file in the subdirectory. This file is committed to git - it's just a template, not actual secrets.
|
|
1382
|
-
|
|
1383
|
-
\`\`\`bash
|
|
1384
|
-
# Example: apps/mobile/.env
|
|
1385
|
-
EXPO_PUBLIC_API_URL=\${API_URL} # Pulls API_URL from root, renames it
|
|
1386
|
-
EXPO_PUBLIC_STRIPE_KEY=\${STRIPE_KEY} # Pulls and renames
|
|
1387
|
-
PORT=\${PORT:-8081} # Uses root PORT if set, otherwise 8081
|
|
1388
|
-
DEBUG=\${DEBUG:-false} # Uses root DEBUG if set, otherwise false
|
|
1389
|
-
\`\`\`
|
|
1390
|
-
|
|
1391
|
-
**Step 3: Run from the subdirectory**
|
|
1392
|
-
\`\`\`bash
|
|
1393
|
-
cd apps/mobile
|
|
1394
|
-
npx hush run -- npm start
|
|
1395
|
-
\`\`\`
|
|
1396
|
-
|
|
1397
|
-
### Variable Expansion Syntax Reference
|
|
1398
|
-
|
|
1399
|
-
| Syntax | What It Does | Example |
|
|
1400
|
-
|--------|--------------|---------|
|
|
1401
|
-
| \`\${VAR}\` | Pull VAR from root secrets | \`API_URL=\${API_URL}\` |
|
|
1402
|
-
| \`\${VAR:-default}\` | Pull VAR, use default if not set | \`PORT=\${PORT:-3000}\` |
|
|
1403
|
-
| \`\${env:VAR}\` | Read from system environment | \`CI=\${env:CI}\` |
|
|
1404
|
-
|
|
1405
|
-
### Framework Examples
|
|
1406
|
-
|
|
1407
|
-
**Expo/React Native:**
|
|
1408
|
-
\`\`\`bash
|
|
1409
|
-
# apps/mobile/.env
|
|
1410
|
-
EXPO_PUBLIC_API_URL=\${API_URL}
|
|
1411
|
-
EXPO_PUBLIC_STRIPE_KEY=\${STRIPE_PUBLISHABLE_KEY}
|
|
1412
|
-
EXPO_PUBLIC_ENV=\${ENV:-development}
|
|
1413
|
-
\`\`\`
|
|
1414
|
-
|
|
1415
|
-
**Next.js:**
|
|
1416
|
-
\`\`\`bash
|
|
1417
|
-
# apps/web/.env
|
|
1418
|
-
NEXT_PUBLIC_API_URL=\${API_URL}
|
|
1419
|
-
NEXT_PUBLIC_STRIPE_KEY=\${STRIPE_PUBLISHABLE_KEY}
|
|
1420
|
-
DATABASE_URL=\${DATABASE_URL}
|
|
1421
|
-
\`\`\`
|
|
1422
|
-
|
|
1423
|
-
**Cloudflare Worker:**
|
|
1424
|
-
\`\`\`bash
|
|
1425
|
-
# apps/api/.env
|
|
1426
|
-
DATABASE_URL=\${DATABASE_URL}
|
|
1427
|
-
STRIPE_SECRET_KEY=\${STRIPE_SECRET_KEY}
|
|
1428
|
-
PORT=\${PORT:-8787}
|
|
1429
|
-
\`\`\`
|
|
1430
|
-
|
|
1431
|
-
### Important Notes
|
|
1432
|
-
|
|
1433
|
-
- **Template files ARE committed** to git (they contain no secrets)
|
|
1434
|
-
- **Root secrets stay encrypted** - templates just reference them
|
|
1435
|
-
- **Run from subdirectory** - \`hush run\` finds the project root automatically
|
|
1436
|
-
- **Self-reference works** - \`PORT=\${PORT:-3000}\` uses root PORT if set
|
|
1437
|
-
|
|
1438
|
-
---
|
|
1439
|
-
|
|
1440
|
-
## Choosing Push vs Pull (Monorepos)
|
|
1441
|
-
|
|
1442
|
-
### "How should I set up secrets for a new package?"
|
|
1443
|
-
|
|
1444
|
-
**Ask yourself:** Does this package need to rename variables or add defaults?
|
|
1445
|
-
|
|
1446
|
-
#### If NO (simple filtering) → Use Push
|
|
1447
|
-
|
|
1448
|
-
Edit \`hush.yaml\` to add a target:
|
|
1449
|
-
\`\`\`yaml
|
|
1450
|
-
targets:
|
|
1451
|
-
- name: new-package
|
|
1452
|
-
path: ./packages/new-package
|
|
1453
|
-
format: dotenv
|
|
1454
|
-
include:
|
|
1455
|
-
- NEXT_PUBLIC_* # Or whatever pattern fits
|
|
1456
|
-
\`\`\`
|
|
1457
|
-
|
|
1458
|
-
**Benefits:** New \`NEXT_PUBLIC_*\` vars at root auto-flow. Zero maintenance.
|
|
1459
|
-
|
|
1460
|
-
#### If YES (transformation needed) → Use Pull
|
|
1461
|
-
|
|
1462
|
-
Create a template \`.env\` in the package:
|
|
1463
|
-
\`\`\`bash
|
|
1464
|
-
# packages/mobile/.env (committed to git)
|
|
1154
|
+
# packages/mobile/.hush
|
|
1465
1155
|
EXPO_PUBLIC_API_URL=\${API_URL} # Rename from root
|
|
1466
1156
|
EXPO_PUBLIC_DEBUG=\${DEBUG:-false} # With default
|
|
1467
1157
|
PORT=\${PORT:-8081} # Local default
|
|
@@ -1474,7 +1164,7 @@ PORT=\${PORT:-8081} # Local default
|
|
|
1474
1164
|
| Scenario | Update |
|
|
1475
1165
|
|----------|--------|
|
|
1476
1166
|
| New \`NEXT_PUBLIC_*\` var, web uses push | Nothing! Auto-flows |
|
|
1477
|
-
| New var mobile needs, mobile uses pull | \`packages/mobile/.
|
|
1167
|
+
| New var mobile needs, mobile uses pull | \`packages/mobile/.hush\` template |
|
|
1478
1168
|
| New package needs secrets | \`hush.yaml\` (push) or new template (pull) |
|
|
1479
1169
|
| Change var routing | \`hush.yaml\` include/exclude patterns |
|
|
1480
1170
|
|
|
@@ -1528,23 +1218,23 @@ Total: 3 variables
|
|
|
1528
1218
|
- \`API_KEY\` is not set - user needs to add it
|
|
1529
1219
|
`,
|
|
1530
1220
|
};
|
|
1531
|
-
function getSkillPath(location, root) {
|
|
1221
|
+
function getSkillPath(ctx, location, root) {
|
|
1532
1222
|
if (location === 'global') {
|
|
1533
|
-
return join(homedir(), '.claude', 'skills', 'hush-secrets');
|
|
1223
|
+
return ctx.path.join(homedir(), '.claude', 'skills', 'hush-secrets');
|
|
1534
1224
|
}
|
|
1535
|
-
return join(root, '.claude', 'skills', 'hush-secrets');
|
|
1225
|
+
return ctx.path.join(root, '.claude', 'skills', 'hush-secrets');
|
|
1536
1226
|
}
|
|
1537
|
-
async function promptForLocation() {
|
|
1227
|
+
async function promptForLocation(ctx) {
|
|
1538
1228
|
const rl = createInterface({
|
|
1539
|
-
input: process.stdin,
|
|
1540
|
-
output: process.stdout,
|
|
1229
|
+
input: ctx.process.stdin,
|
|
1230
|
+
output: ctx.process.stdout,
|
|
1541
1231
|
});
|
|
1542
1232
|
return new Promise((resolve) => {
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1233
|
+
ctx.logger.log(pc.bold('\nWhere would you like to install the Claude skill?\n'));
|
|
1234
|
+
ctx.logger.log(` ${pc.cyan('1)')} ${pc.bold('Global')} ${pc.dim('(~/.claude/skills/)')}`);
|
|
1235
|
+
ctx.logger.log(` Works across all your projects. Recommended for personal use.\n`);
|
|
1236
|
+
ctx.logger.log(` ${pc.cyan('2)')} ${pc.bold('Local')} ${pc.dim('(.claude/skills/)')}`);
|
|
1237
|
+
ctx.logger.log(` Bundled with this project. Recommended for teams.\n`);
|
|
1548
1238
|
rl.question(`${pc.bold('Choice')} ${pc.dim('[1/2]')}: `, (answer) => {
|
|
1549
1239
|
rl.close();
|
|
1550
1240
|
const choice = answer.trim();
|
|
@@ -1557,16 +1247,16 @@ async function promptForLocation() {
|
|
|
1557
1247
|
});
|
|
1558
1248
|
});
|
|
1559
1249
|
}
|
|
1560
|
-
function writeSkillFiles(skillPath) {
|
|
1561
|
-
mkdirSync(skillPath, { recursive: true });
|
|
1562
|
-
mkdirSync(join(skillPath, 'examples'), { recursive: true });
|
|
1250
|
+
function writeSkillFiles(ctx, skillPath) {
|
|
1251
|
+
ctx.fs.mkdirSync(skillPath, { recursive: true });
|
|
1252
|
+
ctx.fs.mkdirSync(ctx.path.join(skillPath, 'examples'), { recursive: true });
|
|
1563
1253
|
for (const [filename, content] of Object.entries(SKILL_FILES)) {
|
|
1564
|
-
const filePath = join(skillPath, filename);
|
|
1565
|
-
writeFileSync(filePath, content, 'utf-8');
|
|
1254
|
+
const filePath = ctx.path.join(skillPath, filename);
|
|
1255
|
+
ctx.fs.writeFileSync(filePath, content, 'utf-8');
|
|
1566
1256
|
}
|
|
1567
1257
|
}
|
|
1568
|
-
export async function skillCommand(options) {
|
|
1569
|
-
const {
|
|
1258
|
+
export async function skillCommand(ctx, options) {
|
|
1259
|
+
const { global: isGlobal, local: isLocal } = options;
|
|
1570
1260
|
let location;
|
|
1571
1261
|
if (isGlobal) {
|
|
1572
1262
|
location = 'global';
|
|
@@ -1575,30 +1265,30 @@ export async function skillCommand(options) {
|
|
|
1575
1265
|
location = 'local';
|
|
1576
1266
|
}
|
|
1577
1267
|
else {
|
|
1578
|
-
location = await promptForLocation();
|
|
1268
|
+
location = await promptForLocation(ctx);
|
|
1579
1269
|
}
|
|
1580
|
-
const skillPath = getSkillPath(location,
|
|
1581
|
-
const alreadyInstalled = existsSync(join(skillPath, 'SKILL.md'));
|
|
1270
|
+
const skillPath = getSkillPath(ctx, location, ctx.process.cwd());
|
|
1271
|
+
const alreadyInstalled = ctx.fs.existsSync(ctx.path.join(skillPath, 'SKILL.md'));
|
|
1582
1272
|
if (alreadyInstalled) {
|
|
1583
|
-
|
|
1584
|
-
|
|
1273
|
+
ctx.logger.log(pc.yellow(`\nSkill already installed at: ${skillPath}`));
|
|
1274
|
+
ctx.logger.log(pc.dim('To reinstall, delete the directory first.\n'));
|
|
1585
1275
|
return;
|
|
1586
1276
|
}
|
|
1587
|
-
|
|
1588
|
-
writeSkillFiles(skillPath);
|
|
1589
|
-
|
|
1277
|
+
ctx.logger.log(pc.blue(`\nInstalling Claude skill to: ${skillPath}`));
|
|
1278
|
+
writeSkillFiles(ctx, skillPath);
|
|
1279
|
+
ctx.logger.log(pc.green('\n✓ Skill installed successfully!\n'));
|
|
1590
1280
|
if (location === 'global') {
|
|
1591
|
-
|
|
1281
|
+
ctx.logger.log(pc.dim('The skill is now active for all projects using Claude Code.\n'));
|
|
1592
1282
|
}
|
|
1593
1283
|
else {
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1284
|
+
ctx.logger.log(pc.dim('The skill is now bundled with this project.'));
|
|
1285
|
+
ctx.logger.log(pc.dim('Commit the .claude/ directory to share with your team.\n'));
|
|
1286
|
+
ctx.logger.log(pc.bold('Suggested:'));
|
|
1287
|
+
ctx.logger.log(` git add .claude/`);
|
|
1288
|
+
ctx.logger.log(` git commit -m "chore: add Hush Claude skill"\n`);
|
|
1599
1289
|
}
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1290
|
+
ctx.logger.log(pc.bold('What the skill does:'));
|
|
1291
|
+
ctx.logger.log(` • Teaches AI to use ${pc.cyan('hush inspect')} instead of reading .env files`);
|
|
1292
|
+
ctx.logger.log(` • Prevents accidental exposure of secrets to LLMs`);
|
|
1293
|
+
ctx.logger.log(` • Guides AI through adding/modifying secrets safely\n`);
|
|
1604
1294
|
}
|