@chriscode/hush 4.2.0 → 5.0.1

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.
Files changed (78) hide show
  1. package/dist/cli.js +58 -29
  2. package/dist/commands/check.d.ts +3 -3
  3. package/dist/commands/check.d.ts.map +1 -1
  4. package/dist/commands/check.js +30 -33
  5. package/dist/commands/decrypt.d.ts +2 -2
  6. package/dist/commands/decrypt.d.ts.map +1 -1
  7. package/dist/commands/decrypt.js +52 -55
  8. package/dist/commands/edit.d.ts +2 -2
  9. package/dist/commands/edit.d.ts.map +1 -1
  10. package/dist/commands/edit.js +10 -12
  11. package/dist/commands/encrypt.d.ts +2 -2
  12. package/dist/commands/encrypt.d.ts.map +1 -1
  13. package/dist/commands/encrypt.js +27 -29
  14. package/dist/commands/expansions.d.ts +2 -2
  15. package/dist/commands/expansions.d.ts.map +1 -1
  16. package/dist/commands/expansions.js +46 -44
  17. package/dist/commands/has.d.ts +2 -2
  18. package/dist/commands/has.d.ts.map +1 -1
  19. package/dist/commands/has.js +12 -15
  20. package/dist/commands/init.d.ts +2 -2
  21. package/dist/commands/init.d.ts.map +1 -1
  22. package/dist/commands/init.js +107 -87
  23. package/dist/commands/inspect.d.ts +2 -2
  24. package/dist/commands/inspect.d.ts.map +1 -1
  25. package/dist/commands/inspect.js +14 -16
  26. package/dist/commands/keys.d.ts +2 -1
  27. package/dist/commands/keys.d.ts.map +1 -1
  28. package/dist/commands/keys.js +47 -49
  29. package/dist/commands/list.d.ts +2 -2
  30. package/dist/commands/list.d.ts.map +1 -1
  31. package/dist/commands/list.js +11 -14
  32. package/dist/commands/migrate.d.ts +7 -0
  33. package/dist/commands/migrate.d.ts.map +1 -0
  34. package/dist/commands/migrate.js +117 -0
  35. package/dist/commands/push.d.ts +2 -2
  36. package/dist/commands/push.d.ts.map +1 -1
  37. package/dist/commands/push.js +41 -45
  38. package/dist/commands/resolve.d.ts +2 -2
  39. package/dist/commands/resolve.d.ts.map +1 -1
  40. package/dist/commands/resolve.js +25 -28
  41. package/dist/commands/run.d.ts +2 -2
  42. package/dist/commands/run.d.ts.map +1 -1
  43. package/dist/commands/run.js +35 -39
  44. package/dist/commands/set.d.ts +2 -2
  45. package/dist/commands/set.d.ts.map +1 -1
  46. package/dist/commands/set.js +61 -70
  47. package/dist/commands/skill.d.ts +2 -2
  48. package/dist/commands/skill.d.ts.map +1 -1
  49. package/dist/commands/skill.js +186 -487
  50. package/dist/commands/status.d.ts +2 -2
  51. package/dist/commands/status.d.ts.map +1 -1
  52. package/dist/commands/status.js +52 -55
  53. package/dist/commands/template.d.ts +2 -2
  54. package/dist/commands/template.d.ts.map +1 -1
  55. package/dist/commands/template.js +36 -39
  56. package/dist/commands/trace.d.ts +2 -2
  57. package/dist/commands/trace.d.ts.map +1 -1
  58. package/dist/commands/trace.js +16 -19
  59. package/dist/config/loader.js +3 -3
  60. package/dist/context.d.ts +3 -0
  61. package/dist/context.d.ts.map +1 -0
  62. package/dist/context.js +59 -0
  63. package/dist/core/parse.js +3 -3
  64. package/dist/core/sops.js +9 -9
  65. package/dist/core/template.d.ts +2 -2
  66. package/dist/core/template.d.ts.map +1 -1
  67. package/dist/core/template.js +11 -12
  68. package/dist/lib/age.js +9 -9
  69. package/dist/lib/fs.d.ts +25 -0
  70. package/dist/lib/fs.d.ts.map +1 -0
  71. package/dist/lib/fs.js +36 -0
  72. package/dist/lib/onepassword.d.ts.map +1 -1
  73. package/dist/lib/onepassword.js +41 -4
  74. package/dist/types.d.ts +91 -0
  75. package/dist/types.d.ts.map +1 -1
  76. package/dist/types.js +4 -4
  77. package/dist/utils/version-check.js +5 -5
  78. package/package.json +3 -2
@@ -1,20 +1,18 @@
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': `---
8
6
  name: hush-secrets
9
- description: Manage secrets safely using Hush CLI. Use when working with .env files, environment variables, secrets, API keys, database URLs, credentials, or configuration. NEVER read .env files directly - always use hush commands instead to prevent exposing secrets to the LLM.
7
+ description: Manage secrets safely using Hush CLI. Use when working with .hush files, environment variables, secrets, API keys, database URLs, credentials, or configuration. NEVER read .hush files directly - always use hush commands instead to prevent exposing secrets to the LLM.
10
8
  allowed-tools: Bash(hush:*), Bash(npx hush:*), Bash(brew:*), Bash(npm:*), Bash(pnpm:*), Bash(age-keygen:*), Read, Grep, Glob, Write, Bash(cat:*), Bash(grep:*)
11
9
  ---
12
10
 
13
11
  # Hush - AI-Native Secrets Management
14
12
 
15
- **CRITICAL: NEVER read root .env files directly.** Always use \`npx hush status\`, \`npx hush inspect\`, or \`npx hush has\` to check secrets.
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. Subdirectory \`.env\` files are **templates** (safe to commit and read) that reference root secrets via \`\${VAR}\` syntax.
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
 
@@ -35,13 +33,13 @@ This tells you:
35
33
 
36
34
  | You See | What It Means | Action |
37
35
  |---------|---------------|--------|
38
- | \`SECURITY WARNING: Unencrypted .env files\` | Plaintext secrets at project root! | Run \`npx hush encrypt\` immediately |
36
+ | \`SECURITY WARNING: Unencrypted .env files\` | Plaintext .env files found (legacy or output) | Run \`npx hush migrate\` or delete them |
39
37
  | \`No hush.yaml found\` | Hush not initialized | Run \`npx hush init\` |
40
38
  | \`SOPS not installed\` | Missing prerequisite | \`brew install sops\` |
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:** Security warnings only apply to root-level \`.env\` files. Subdirectory \`.env\` files are templates (safe to commit).
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
 
@@ -53,12 +51,12 @@ npx hush encrypt # Encrypts any existing .env files, deletes plaintext
53
51
  npx hush inspect # Verify setup
54
52
  \`\`\`
55
53
 
56
- ### Scenario 2: Existing .env Files Found
54
+ ### Scenario 2: Existing .env Files Found (Migration from v4)
57
55
 
58
56
  \`\`\`bash
59
57
  npx hush status # Check what's there
60
- npx hush encrypt # Encrypt them (auto-deletes plaintext after verification)
61
- npx hush inspect # Confirm everything is encrypted
58
+ npx hush migrate # Migrate .env.encrypted to .hush.encrypted
59
+ npx hush inspect # Confirm everything is migrated
62
60
  \`\`\`
63
61
 
64
62
  ### Scenario 3: Hush Already Set Up (Team Member Joining)
@@ -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> # Add interactively (you invoke, user types value)
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 .env templates)
112
+ ### Pull (subdirectory .hush templates)
114
113
 
115
114
  Best for transformation, renaming, or explicit dependencies:
116
115
 
117
116
  \`\`\`bash
118
- # apps/mobile/.env (committed - it's just a template)
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 \`.env\` template file in that subdirectory.
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/.env
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 \`.env\` template
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/.env
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/.env
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/.env
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
- - **Subdirectory .env files ARE committed to git** - they're templates, not secrets
199
- - **Can contain expansions AND constants** - \`APP_NAME=MyApp\` alongside \`API_URL=\${API_URL}\`
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 interactively | User needs to enter a value |
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 interactively
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 # You invoke this, user types value
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
- The user will be prompted to enter the value (hidden input).
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
- When stdin has piped data, Hush reads from it instead of prompting.
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
 
@@ -423,9 +441,9 @@ The generated config looks like:
423
441
 
424
442
  \`\`\`yaml
425
443
  sources:
426
- shared: .env
427
- development: .env.development
428
- production: .env.production
444
+ shared: .hush
445
+ development: .hush.development
446
+ production: .hush.production
429
447
 
430
448
  targets:
431
449
  - name: root
@@ -461,33 +479,79 @@ Customize targets for your monorepo. Common patterns:
461
479
  format: yaml
462
480
  \`\`\`
463
481
 
464
- ### Step 5: Create initial \`.env\` files
482
+ ### Step 5: Create initial \`.hush\` files
465
483
 
466
- Create \`.env\` with shared secrets:
484
+ Create \`.hush\` with shared secrets at the **repository root**:
467
485
 
468
486
  \`\`\`bash
469
- # .env
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
- Create \`.env.development\` for dev-specific values:
495
+ Create \`.hush.development\` for dev-specific values:
476
496
 
477
497
  \`\`\`bash
478
- # .env.development
498
+ # .hush.development
479
499
  DEBUG=true
480
500
  LOG_LEVEL=debug
481
501
  \`\`\`
482
502
 
483
- Create \`.env.production\` for production values:
503
+ Create \`.hush.production\` for production values:
484
504
 
485
505
  \`\`\`bash
486
- # .env.production
506
+ # .hush.production
487
507
  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
@@ -495,9 +559,9 @@ npx hush encrypt
495
559
  \`\`\`
496
560
 
497
561
  This creates:
498
- - \`.env.encrypted\`
499
- - \`.env.development.encrypted\`
500
- - \`.env.production.encrypted\`
562
+ - \`.hush.encrypted\`
563
+ - \`.hush.development.encrypted\`
564
+ - \`.hush.production.encrypted\`
501
565
 
502
566
  ### Step 7: Verify setup
503
567
 
@@ -511,22 +575,26 @@ npx hush inspect
511
575
  Add these lines to \`.gitignore\`:
512
576
 
513
577
  \`\`\`gitignore
514
- # Hush - plaintext env files (generated, not committed)
578
+ # Hush - plaintext source files (encrypted versions are committed)
579
+ .hush
580
+ .hush.local
581
+ .hush.development
582
+ .hush.production
583
+
584
+ # Output files (generated by hush decrypt, not committed)
515
585
  .env
516
- .env.local
517
- .env.development
518
- .env.production
586
+ .env.*
519
587
  .dev.vars
520
588
 
521
589
  # Keep encrypted files (these ARE committed)
522
- !.env.encrypted
523
- !.env.*.encrypted
590
+ !.hush.encrypted
591
+ !.hush.*.encrypted
524
592
  \`\`\`
525
593
 
526
594
  ### Step 9: Commit encrypted files
527
595
 
528
596
  \`\`\`bash
529
- git add .sops.yaml hush.yaml .env*.encrypted .gitignore
597
+ git add .sops.yaml hush.yaml .hush*.encrypted .gitignore
530
598
  git commit -m "chore: add Hush secrets management"
531
599
  \`\`\`
532
600
 
@@ -552,8 +620,8 @@ After setup, verify everything works:
552
620
  - [ ] \`npx hush status\` shows configuration
553
621
  - [ ] \`npx hush inspect\` shows masked variables
554
622
  - [ ] \`npx hush run -- env\` can decrypt and run (secrets stay in memory!)
555
- - [ ] \`.env.encrypted\` files are committed to git
556
- - [ ] Plaintext \`.env\` files are in \`.gitignore\`
623
+ - [ ] \`.hush.encrypted\` files are committed to git
624
+ - [ ] Plaintext \`.hush\` and \`.env\` files are in \`.gitignore\`
557
625
 
558
626
  ---
559
627
 
@@ -594,7 +662,7 @@ Complete reference for all Hush CLI commands with flags, options, and examples.
594
662
 
595
663
  ## Security Model: Encrypted at Rest
596
664
 
597
- All secrets are stored encrypted on disk. You can safely read any \`.env\` file—they contain only encrypted data. No special precautions needed for file reading.
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.
598
666
 
599
667
  ## Global Options
600
668
 
@@ -632,23 +700,27 @@ hush run -t api -- wrangler dev # Run filtered for 'api' target
632
700
 
633
701
  ---
634
702
 
635
- ### hush set <KEY> ⭐
703
+ ### hush set [VALUE] <KEY> ⭐
636
704
 
637
- Add or update a single secret interactively. You invoke this, user enters the value.
705
+ Add or update a single secret. Prompts for value if not provided inline.
638
706
 
639
707
  \`\`\`bash
640
- hush set DATABASE_URL # Set in shared secrets
708
+ hush set DATABASE_URL # Prompts for value interactively
709
+ hush set "postgres://..." DATABASE_URL # Inline value (no prompt)
641
710
  hush set API_KEY -e production # Set in production secrets
642
711
  hush set DEBUG --local # Set personal local override
643
712
  \`\`\`
644
713
 
645
- User will be prompted with hidden input - the value is never visible.
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
646
718
 
647
- **Pipe support:** You can also pipe values directly:
719
+ **GUI dialog (--gui flag):**
648
720
  \`\`\`bash
649
- echo "my-secret" | hush set MY_KEY
650
- cat cert.pem | hush set CERTIFICATE
721
+ hush set API_KEY --gui # Opens visible dialog (for AI agents)
651
722
  \`\`\`
723
+ The GUI dialog shows the value as you type/paste for easier verification.
652
724
 
653
725
  ---
654
726
 
@@ -701,7 +773,7 @@ hush init
701
773
 
702
774
  ### hush encrypt
703
775
 
704
- Encrypt source \`.env\` files to \`.env.encrypted\` files.
776
+ Encrypt source \`.hush\` files to \`.hush.encrypted\` files.
705
777
 
706
778
  \`\`\`bash
707
779
  hush encrypt
@@ -782,7 +854,7 @@ hush trace STRIPE_SECRET_KEY # Trace another variable
782
854
 
783
855
  ### hush template
784
856
 
785
- Show the resolved template for the current directory's \`.env\` file.
857
+ Show the resolved template for the current directory's \`.hush\` file.
786
858
 
787
859
  \`\`\`bash
788
860
  cd apps/mobile
@@ -801,7 +873,7 @@ hush template -e production # Show for production
801
873
 
802
874
  ### hush expansions
803
875
 
804
- Show the expansion graph across all subdirectories that have \`.env\` templates.
876
+ Show the expansion graph across all subdirectories that have \`.hush\` templates.
805
877
 
806
878
  \`\`\`bash
807
879
  hush expansions # Scan all subdirectories
@@ -809,7 +881,7 @@ hush expansions -e production # Show for production
809
881
  \`\`\`
810
882
 
811
883
  **Output shows:**
812
- - Which subdirectories have \`.env\` templates
884
+ - Which subdirectories have \`.hush\` templates
813
885
  - What variables each template references from root
814
886
  - Resolution status for each reference
815
887
 
@@ -989,9 +1061,9 @@ hush skill --local # Install to ./.claude/skills/
989
1061
 
990
1062
  \`\`\`yaml
991
1063
  sources:
992
- shared: .env
993
- development: .env.development
994
- production: .env.production
1064
+ shared: .hush
1065
+ development: .hush.development
1066
+ production: .hush.production
995
1067
 
996
1068
  targets:
997
1069
  - name: root
@@ -1046,7 +1118,7 @@ targets:
1046
1118
  | Expo | \`EXPO_PUBLIC_*\` | \`include: [EXPO_PUBLIC_*]\` |
1047
1119
  | Gatsby | \`GATSBY_*\` | \`include: [GATSBY_*]\` |
1048
1120
 
1049
- ### Variable Interpolation (v4+)
1121
+ ### Variable Interpolation (v5+)
1050
1122
 
1051
1123
  Reference other variables using \`\${VAR}\` syntax:
1052
1124
 
@@ -1061,8 +1133,8 @@ PORT=\${PORT:-3000}
1061
1133
  # System environment (explicit opt-in)
1062
1134
  CI=\${env:CI}
1063
1135
 
1064
- # Pull from root (subdirectory .env can reference root secrets)
1065
- # apps/mobile/.env:
1136
+ # Pull from root (subdirectory .hush can reference root secrets)
1137
+ # apps/mobile/.hush:
1066
1138
  EXPO_PUBLIC_API_URL=\${API_URL} # Renamed from root
1067
1139
  \`\`\`
1068
1140
 
@@ -1079,380 +1151,7 @@ targets:
1079
1151
 
1080
1152
  **Pull (subdirectory templates):** Transformation, renaming, defaults
1081
1153
  \`\`\`bash
1082
- # apps/mobile/.env
1083
- EXPO_PUBLIC_API_URL=\${API_URL} # Rename required
1084
- PORT=\${PORT:-3000} # Default value
1085
- \`\`\`
1086
-
1087
- **Decision:** Use push for "all X → Y". Use pull for rename/transform/defaults.
1088
- `,
1089
- 'examples/workflows.md': `# Hush Workflow Examples
1090
-
1091
- Step-by-step examples for common workflows when working with secrets.
1092
-
1093
- **CRITICAL: NEVER read .env files directly. Use hush commands instead.**
1094
-
1095
- ---
1096
-
1097
- ## First-Time Setup (Most Important!)
1098
-
1099
- ### "Help me set up Hush for this project"
1100
-
1101
- **Step 1: Check current state**
1102
- \`\`\`bash
1103
- npx hush status
1104
- \`\`\`
1105
-
1106
- This will show:
1107
- - If Hush is already configured
1108
- - If there are unencrypted .env files (security risk!)
1109
- - What prerequisites are missing
1110
-
1111
- **Step 2: Based on the output, follow the appropriate path:**
1112
-
1113
- #### Path A: "SECURITY WARNING: Unencrypted .env files detected"
1114
- \`\`\`bash
1115
- npx hush init # If no hush.yaml exists
1116
- npx hush encrypt # Encrypts files and DELETES plaintext automatically
1117
- npx hush status # Verify the warning is gone
1118
- \`\`\`
1119
-
1120
- #### Path B: "No hush.yaml found"
1121
- \`\`\`bash
1122
- npx hush init # Creates config and sets up keys
1123
- npx hush set <KEY> # Add secrets (if none exist yet)
1124
- \`\`\`
1125
-
1126
- #### Path C: "age key not found"
1127
- \`\`\`bash
1128
- npx hush keys setup # Pull from 1Password or generate new key
1129
- \`\`\`
1130
-
1131
- #### Path D: Everything looks good
1132
- \`\`\`bash
1133
- npx hush inspect # See what secrets are configured
1134
- \`\`\`
1135
-
1136
- ---
1137
-
1138
- ## Running Programs (Most Common)
1139
-
1140
- ### "Start the development server"
1141
- \`\`\`bash
1142
- npx hush run -- npm run dev
1143
- \`\`\`
1144
-
1145
- ### "Build for production"
1146
- \`\`\`bash
1147
- npx hush run -e production -- npm run build
1148
- \`\`\`
1149
-
1150
- ### "Run tests with secrets"
1151
- \`\`\`bash
1152
- npx hush run -- npm test
1153
- \`\`\`
1154
-
1155
- ### "Run Wrangler for Cloudflare Worker"
1156
- \`\`\`bash
1157
- npx hush run -t api -- wrangler dev
1158
- \`\`\`
1159
-
1160
- ---
1161
-
1162
- ## Checking Secrets
1163
-
1164
- ### "What environment variables does this project use?"
1165
- \`\`\`bash
1166
- npx hush inspect # Shows all variables with masked values
1167
- \`\`\`
1168
-
1169
- ### "Is the database configured?"
1170
- \`\`\`bash
1171
- npx hush has DATABASE_URL
1172
- \`\`\`
1173
-
1174
- If "not found", help user add it:
1175
- \`\`\`bash
1176
- npx hush set DATABASE_URL
1177
- \`\`\`
1178
- Tell user: "Enter your database URL when prompted"
1179
-
1180
- ### "Check all required secrets"
1181
- \`\`\`bash
1182
- npx hush has DATABASE_URL -q && \\
1183
- npx hush has API_KEY -q && \\
1184
- echo "All configured" || \\
1185
- echo "Some missing"
1186
- \`\`\`
1187
-
1188
- ---
1189
-
1190
- ## Adding Secrets
1191
-
1192
- ### "Help me add DATABASE_URL"
1193
- \`\`\`bash
1194
- npx hush set DATABASE_URL
1195
- \`\`\`
1196
- Tell user: "Enter your database URL when prompted (input will be hidden)"
1197
-
1198
- ### "Add a production-only secret"
1199
- \`\`\`bash
1200
- npx hush set STRIPE_SECRET_KEY -e production
1201
- \`\`\`
1202
-
1203
- ### "Add a personal local override"
1204
- \`\`\`bash
1205
- npx hush set DEBUG --local
1206
- \`\`\`
1207
-
1208
- ### "Edit multiple secrets at once"
1209
- \`\`\`bash
1210
- npx hush edit
1211
- \`\`\`
1212
- Tell user: "Your editor will open. Add or modify secrets, then save and close."
1213
-
1214
- ---
1215
-
1216
- ## Debugging
1217
-
1218
- ### "My app can't find DATABASE_URL"
1219
-
1220
- 1. **Trace the variable** to see where it exists and where it goes:
1221
- \`\`\`bash
1222
- npx hush trace DATABASE_URL
1223
- \`\`\`
1224
- This shows which source files have it and which targets include/exclude it.
1225
-
1226
- 2. **Check if it exists** in your current environment:
1227
- \`\`\`bash
1228
- npx hush has DATABASE_URL
1229
- \`\`\`
1230
-
1231
- 3. **Resolve the target** to see what variables it receives:
1232
- \`\`\`bash
1233
- npx hush resolve api-workers
1234
- \`\`\`
1235
-
1236
- ### "Target is missing expected variables"
1237
-
1238
- \`\`\`bash
1239
- npx hush resolve <target-name> # See included/excluded variables
1240
- npx hush resolve <target-name> -e prod # Check production
1241
- \`\`\`
1242
-
1243
- Look at the 🚫 EXCLUDED section to see which pattern is filtering out your variable.
1244
-
1245
- ### "Wrangler dev not seeing secrets"
1246
-
1247
- If you are using \`hush run -- wrangler dev\` and secrets are missing:
1248
-
1249
- **Step 1: Check for blocking files**
1250
- \`\`\`bash
1251
- ls -la .dev.vars # If this exists, it blocks Hush secrets
1252
- \`\`\`
1253
-
1254
- **Step 2: Delete the blocking file**
1255
- \`\`\`bash
1256
- rm .dev.vars
1257
- \`\`\`
1258
-
1259
- **Step 3: Run normally**
1260
- \`\`\`bash
1261
- npx hush run -t api -- wrangler dev
1262
- \`\`\`
1263
-
1264
- **Step 4: If still not working, update Wrangler**
1265
- \`\`\`bash
1266
- npm update wrangler
1267
- \`\`\`
1268
-
1269
- **Why this happens:**
1270
- - Wrangler has a strict rule: if \`.dev.vars\` exists (even empty!), it ignores ALL environment variables
1271
- - Hush automatically sets \`CLOUDFLARE_INCLUDE_PROCESS_ENV=true\` for you
1272
- - But Wrangler only respects this when no \`.dev.vars\` file exists
1273
- - Older Wrangler versions may not support \`CLOUDFLARE_INCLUDE_PROCESS_ENV\` at all
1274
-
1275
- **Prevention tip:** Never use \`hush decrypt\` for Wrangler targets—always use \`hush run\`.
1276
-
1277
- ### "Variable appears in wrong places"
1278
-
1279
- \`\`\`bash
1280
- npx hush trace <VARIABLE_NAME>
1281
- \`\`\`
1282
-
1283
- This shows the full disposition across all targets - which include it and which exclude it.
1284
-
1285
- ### "Push is missing some secrets"
1286
-
1287
- \`\`\`bash
1288
- npx hush push --dry-run --verbose
1289
- \`\`\`
1290
-
1291
- This shows exactly what would be pushed to each target.
1292
-
1293
- ---
1294
-
1295
- ## Team Workflows
1296
-
1297
- ### "New team member setup"
1298
-
1299
- Guide them through these steps:
1300
- \`\`\`bash
1301
- # 1. Pull key from 1Password (or get from team member)
1302
- npx hush keys setup
1303
-
1304
- # 2. Verify setup
1305
- npx hush status
1306
-
1307
- # 3. Check secrets are accessible
1308
- npx hush inspect
1309
-
1310
- # 4. Start developing
1311
- npx hush run -- npm run dev
1312
- \`\`\`
1313
-
1314
- ### "Someone added new secrets"
1315
- \`\`\`bash
1316
- git pull
1317
- npx hush inspect # See what's new
1318
- \`\`\`
1319
-
1320
- ---
1321
-
1322
- ## Deployment
1323
-
1324
- ### "Push to Cloudflare Workers"
1325
- \`\`\`bash
1326
- npx hush push --dry-run # Preview first
1327
- npx hush push # Actually push
1328
- npx hush push -t api # Push specific target
1329
- \`\`\`
1330
-
1331
- ### "Push to Cloudflare Pages"
1332
-
1333
- First, add \`push_to\` to your target in \`hush.yaml\`:
1334
- \`\`\`yaml
1335
- targets:
1336
- - name: app
1337
- path: ./app
1338
- format: dotenv
1339
- push_to:
1340
- type: cloudflare-pages
1341
- project: my-pages-project
1342
- \`\`\`
1343
-
1344
- Then push:
1345
- \`\`\`bash
1346
- npx hush push -t app --dry-run # Preview first
1347
- npx hush push -t app # Actually push
1348
- \`\`\`
1349
-
1350
- ### "Build and deploy"
1351
- \`\`\`bash
1352
- npx hush run -e production -- npm run build
1353
- npx hush push
1354
- \`\`\`
1355
-
1356
- ---
1357
-
1358
- ## Setting Up Subdirectory Templates (Pull-Based Secrets)
1359
-
1360
- ### "Set up secrets for a subdirectory app (Expo, Next.js, etc.)"
1361
-
1362
- **Use this when:** You need to rename, transform, or add defaults to root secrets for a specific package.
1363
-
1364
- **Step 1: Verify root secrets exist**
1365
- \`\`\`bash
1366
- cd /path/to/repo/root
1367
- npx hush inspect
1368
- \`\`\`
1369
-
1370
- **Step 2: Create the subdirectory template file**
1371
-
1372
- Create a \`.env\` file in the subdirectory. This file is committed to git - it's just a template, not actual secrets.
1373
-
1374
- \`\`\`bash
1375
- # Example: apps/mobile/.env
1376
- EXPO_PUBLIC_API_URL=\${API_URL} # Pulls API_URL from root, renames it
1377
- EXPO_PUBLIC_STRIPE_KEY=\${STRIPE_KEY} # Pulls and renames
1378
- PORT=\${PORT:-8081} # Uses root PORT if set, otherwise 8081
1379
- DEBUG=\${DEBUG:-false} # Uses root DEBUG if set, otherwise false
1380
- \`\`\`
1381
-
1382
- **Step 3: Run from the subdirectory**
1383
- \`\`\`bash
1384
- cd apps/mobile
1385
- npx hush run -- npm start
1386
- \`\`\`
1387
-
1388
- ### Variable Expansion Syntax Reference
1389
-
1390
- | Syntax | What It Does | Example |
1391
- |--------|--------------|---------|
1392
- | \`\${VAR}\` | Pull VAR from root secrets | \`API_URL=\${API_URL}\` |
1393
- | \`\${VAR:-default}\` | Pull VAR, use default if not set | \`PORT=\${PORT:-3000}\` |
1394
- | \`\${env:VAR}\` | Read from system environment | \`CI=\${env:CI}\` |
1395
-
1396
- ### Framework Examples
1397
-
1398
- **Expo/React Native:**
1399
- \`\`\`bash
1400
- # apps/mobile/.env
1401
- EXPO_PUBLIC_API_URL=\${API_URL}
1402
- EXPO_PUBLIC_STRIPE_KEY=\${STRIPE_PUBLISHABLE_KEY}
1403
- EXPO_PUBLIC_ENV=\${ENV:-development}
1404
- \`\`\`
1405
-
1406
- **Next.js:**
1407
- \`\`\`bash
1408
- # apps/web/.env
1409
- NEXT_PUBLIC_API_URL=\${API_URL}
1410
- NEXT_PUBLIC_STRIPE_KEY=\${STRIPE_PUBLISHABLE_KEY}
1411
- DATABASE_URL=\${DATABASE_URL}
1412
- \`\`\`
1413
-
1414
- **Cloudflare Worker:**
1415
- \`\`\`bash
1416
- # apps/api/.env
1417
- DATABASE_URL=\${DATABASE_URL}
1418
- STRIPE_SECRET_KEY=\${STRIPE_SECRET_KEY}
1419
- PORT=\${PORT:-8787}
1420
- \`\`\`
1421
-
1422
- ### Important Notes
1423
-
1424
- - **Template files ARE committed** to git (they contain no secrets)
1425
- - **Root secrets stay encrypted** - templates just reference them
1426
- - **Run from subdirectory** - \`hush run\` finds the project root automatically
1427
- - **Self-reference works** - \`PORT=\${PORT:-3000}\` uses root PORT if set
1428
-
1429
- ---
1430
-
1431
- ## Choosing Push vs Pull (Monorepos)
1432
-
1433
- ### "How should I set up secrets for a new package?"
1434
-
1435
- **Ask yourself:** Does this package need to rename variables or add defaults?
1436
-
1437
- #### If NO (simple filtering) → Use Push
1438
-
1439
- Edit \`hush.yaml\` to add a target:
1440
- \`\`\`yaml
1441
- targets:
1442
- - name: new-package
1443
- path: ./packages/new-package
1444
- format: dotenv
1445
- include:
1446
- - NEXT_PUBLIC_* # Or whatever pattern fits
1447
- \`\`\`
1448
-
1449
- **Benefits:** New \`NEXT_PUBLIC_*\` vars at root auto-flow. Zero maintenance.
1450
-
1451
- #### If YES (transformation needed) → Use Pull
1452
-
1453
- Create a template \`.env\` in the package:
1454
- \`\`\`bash
1455
- # packages/mobile/.env (committed to git)
1154
+ # packages/mobile/.hush
1456
1155
  EXPO_PUBLIC_API_URL=\${API_URL} # Rename from root
1457
1156
  EXPO_PUBLIC_DEBUG=\${DEBUG:-false} # With default
1458
1157
  PORT=\${PORT:-8081} # Local default
@@ -1465,7 +1164,7 @@ PORT=\${PORT:-8081} # Local default
1465
1164
  | Scenario | Update |
1466
1165
  |----------|--------|
1467
1166
  | New \`NEXT_PUBLIC_*\` var, web uses push | Nothing! Auto-flows |
1468
- | New var mobile needs, mobile uses pull | \`packages/mobile/.env\` template |
1167
+ | New var mobile needs, mobile uses pull | \`packages/mobile/.hush\` template |
1469
1168
  | New package needs secrets | \`hush.yaml\` (push) or new template (pull) |
1470
1169
  | Change var routing | \`hush.yaml\` include/exclude patterns |
1471
1170
 
@@ -1495,11 +1194,11 @@ Key Status:
1495
1194
  \`\`\`
1496
1195
 
1497
1196
  **Reading this:**
1498
- - There's a security issue - plaintext files exist
1197
+ - There are .env files present (legacy or output from decrypt)
1499
1198
  - The project is configured with key management
1500
1199
  - Keys are properly set up and backed up
1501
1200
 
1502
- **To fix:** Run \`npx hush encrypt\`
1201
+ **To fix:** Run \`npx hush migrate\` (if v4) or delete/gitignore these .env files
1503
1202
 
1504
1203
  ### npx hush inspect output explained
1505
1204
 
@@ -1519,23 +1218,23 @@ Total: 3 variables
1519
1218
  - \`API_KEY\` is not set - user needs to add it
1520
1219
  `,
1521
1220
  };
1522
- function getSkillPath(location, root) {
1221
+ function getSkillPath(ctx, location, root) {
1523
1222
  if (location === 'global') {
1524
- return join(homedir(), '.claude', 'skills', 'hush-secrets');
1223
+ return ctx.path.join(homedir(), '.claude', 'skills', 'hush-secrets');
1525
1224
  }
1526
- return join(root, '.claude', 'skills', 'hush-secrets');
1225
+ return ctx.path.join(root, '.claude', 'skills', 'hush-secrets');
1527
1226
  }
1528
- async function promptForLocation() {
1227
+ async function promptForLocation(ctx) {
1529
1228
  const rl = createInterface({
1530
- input: process.stdin,
1531
- output: process.stdout,
1229
+ input: ctx.process.stdin,
1230
+ output: ctx.process.stdout,
1532
1231
  });
1533
1232
  return new Promise((resolve) => {
1534
- console.log(pc.bold('\nWhere would you like to install the Claude skill?\n'));
1535
- console.log(` ${pc.cyan('1)')} ${pc.bold('Global')} ${pc.dim('(~/.claude/skills/)')}`);
1536
- console.log(` Works across all your projects. Recommended for personal use.\n`);
1537
- console.log(` ${pc.cyan('2)')} ${pc.bold('Local')} ${pc.dim('(.claude/skills/)')}`);
1538
- console.log(` Bundled with this project. Recommended for teams.\n`);
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`);
1539
1238
  rl.question(`${pc.bold('Choice')} ${pc.dim('[1/2]')}: `, (answer) => {
1540
1239
  rl.close();
1541
1240
  const choice = answer.trim();
@@ -1548,16 +1247,16 @@ async function promptForLocation() {
1548
1247
  });
1549
1248
  });
1550
1249
  }
1551
- function writeSkillFiles(skillPath) {
1552
- mkdirSync(skillPath, { recursive: true });
1553
- 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 });
1554
1253
  for (const [filename, content] of Object.entries(SKILL_FILES)) {
1555
- const filePath = join(skillPath, filename);
1556
- writeFileSync(filePath, content, 'utf-8');
1254
+ const filePath = ctx.path.join(skillPath, filename);
1255
+ ctx.fs.writeFileSync(filePath, content, 'utf-8');
1557
1256
  }
1558
1257
  }
1559
- export async function skillCommand(options) {
1560
- const { root, global: isGlobal, local: isLocal } = options;
1258
+ export async function skillCommand(ctx, options) {
1259
+ const { global: isGlobal, local: isLocal } = options;
1561
1260
  let location;
1562
1261
  if (isGlobal) {
1563
1262
  location = 'global';
@@ -1566,30 +1265,30 @@ export async function skillCommand(options) {
1566
1265
  location = 'local';
1567
1266
  }
1568
1267
  else {
1569
- location = await promptForLocation();
1268
+ location = await promptForLocation(ctx);
1570
1269
  }
1571
- const skillPath = getSkillPath(location, root);
1572
- 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'));
1573
1272
  if (alreadyInstalled) {
1574
- console.log(pc.yellow(`\nSkill already installed at: ${skillPath}`));
1575
- console.log(pc.dim('To reinstall, delete the directory first.\n'));
1273
+ ctx.logger.log(pc.yellow(`\nSkill already installed at: ${skillPath}`));
1274
+ ctx.logger.log(pc.dim('To reinstall, delete the directory first.\n'));
1576
1275
  return;
1577
1276
  }
1578
- console.log(pc.blue(`\nInstalling Claude skill to: ${skillPath}`));
1579
- writeSkillFiles(skillPath);
1580
- console.log(pc.green('\n✓ Skill installed successfully!\n'));
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'));
1581
1280
  if (location === 'global') {
1582
- console.log(pc.dim('The skill is now active for all projects using Claude Code.\n'));
1281
+ ctx.logger.log(pc.dim('The skill is now active for all projects using Claude Code.\n'));
1583
1282
  }
1584
1283
  else {
1585
- console.log(pc.dim('The skill is now bundled with this project.'));
1586
- console.log(pc.dim('Commit the .claude/ directory to share with your team.\n'));
1587
- console.log(pc.bold('Suggested:'));
1588
- console.log(` git add .claude/`);
1589
- console.log(` git commit -m "chore: add Hush Claude skill"\n`);
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`);
1590
1289
  }
1591
- console.log(pc.bold('What the skill does:'));
1592
- console.log(` • Teaches AI to use ${pc.cyan('hush inspect')} instead of reading .env files`);
1593
- console.log(` • Prevents accidental exposure of secrets to LLMs`);
1594
- console.log(` • Guides AI through adding/modifying secrets safely\n`);
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`);
1595
1294
  }