@chriscode/hush 2.0.0 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -10,7 +10,8 @@ import { listCommand } from './commands/list.js';
10
10
  import { inspectCommand } from './commands/inspect.js';
11
11
  import { hasCommand } from './commands/has.js';
12
12
  import { checkCommand } from './commands/check.js';
13
- const VERSION = '2.0.0';
13
+ import { skillCommand } from './commands/skill.js';
14
+ const VERSION = '2.1.0';
14
15
  function printHelp() {
15
16
  console.log(`
16
17
  ${pc.bold('hush')} - SOPS-based secrets management for monorepos
@@ -22,13 +23,14 @@ ${pc.bold('Commands:')}
22
23
  init Initialize hush.yaml config
23
24
  encrypt Encrypt source .env files
24
25
  decrypt Decrypt and distribute to targets
25
- edit [file] Edit encrypted file in $EDITOR
26
+ set [file] Set/edit secrets in $EDITOR (alias: edit)
26
27
  list List all variables (shows values)
27
28
  inspect List all variables (masked values, AI-safe)
28
29
  has <key> Check if a secret exists (exit 0 if set, 1 if not)
29
30
  check Verify secrets are encrypted (for pre-commit hooks)
30
31
  push Push secrets to Cloudflare Workers
31
32
  status Show configuration and status
33
+ skill Install Claude Code / OpenCode skill
32
34
 
33
35
  ${pc.bold('Options:')}
34
36
  -e, --env <env> Environment: development or production (default: development)
@@ -39,6 +41,8 @@ ${pc.bold('Options:')}
39
41
  --json Output machine-readable JSON (check only)
40
42
  --only-changed Only check git-modified files (check only)
41
43
  --require-source Fail if source file is missing (check only)
44
+ --global Install skill to ~/.claude/skills/ (skill only)
45
+ --local Install skill to ./.claude/skills/ (skill only)
42
46
  -h, --help Show this help message
43
47
  -v, --version Show version number
44
48
 
@@ -47,8 +51,8 @@ ${pc.bold('Examples:')}
47
51
  hush encrypt Encrypt .env files
48
52
  hush decrypt Decrypt for development
49
53
  hush decrypt -e production Decrypt for production
50
- hush edit Edit shared secrets
51
- hush edit development Edit development secrets
54
+ hush set Set/edit shared secrets
55
+ hush set development Set/edit development secrets
52
56
  hush list List all variables (shows values)
53
57
  hush inspect List all variables (masked, AI-safe)
54
58
  hush has DATABASE_URL Check if DATABASE_URL is set
@@ -58,6 +62,9 @@ ${pc.bold('Examples:')}
58
62
  hush check --json Output JSON for CI
59
63
  hush push --dry-run Preview push to Cloudflare
60
64
  hush status Show current status
65
+ hush skill Install Claude skill (interactive)
66
+ hush skill --global Install skill for all projects
67
+ hush skill --local Install skill for this project only
61
68
  `);
62
69
  }
63
70
  function parseEnvironment(value) {
@@ -86,6 +93,8 @@ function parseArgs(args) {
86
93
  let json = false;
87
94
  let onlyChanged = false;
88
95
  let requireSource = false;
96
+ let global = false;
97
+ let local = false;
89
98
  let file;
90
99
  let key;
91
100
  for (let i = 0; i < args.length; i++) {
@@ -139,11 +148,19 @@ function parseArgs(args) {
139
148
  requireSource = true;
140
149
  continue;
141
150
  }
151
+ if (arg === '--global') {
152
+ global = true;
153
+ continue;
154
+ }
155
+ if (arg === '--local') {
156
+ local = true;
157
+ continue;
158
+ }
142
159
  if (!command && !arg.startsWith('-')) {
143
160
  command = arg;
144
161
  continue;
145
162
  }
146
- if (command === 'edit' && !arg.startsWith('-')) {
163
+ if ((command === 'set' || command === 'edit') && !arg.startsWith('-')) {
147
164
  const parsed = parseFileKey(arg);
148
165
  if (parsed) {
149
166
  file = parsed;
@@ -160,7 +177,7 @@ function parseArgs(args) {
160
177
  continue;
161
178
  }
162
179
  }
163
- return { command, env, root, dryRun, quiet, warn, json, onlyChanged, requireSource, file, key };
180
+ return { command, env, root, dryRun, quiet, warn, json, onlyChanged, requireSource, global, local, file, key };
164
181
  }
165
182
  async function main() {
166
183
  const args = process.argv.slice(2);
@@ -168,7 +185,7 @@ async function main() {
168
185
  printHelp();
169
186
  process.exit(0);
170
187
  }
171
- const { command, env, root, dryRun, quiet, warn, json, onlyChanged, requireSource, file, key } = parseArgs(args);
188
+ const { command, env, root, dryRun, quiet, warn, json, onlyChanged, requireSource, global, local, file, key } = parseArgs(args);
172
189
  try {
173
190
  switch (command) {
174
191
  case 'init':
@@ -180,6 +197,7 @@ async function main() {
180
197
  case 'decrypt':
181
198
  await decryptCommand({ root, env });
182
199
  break;
200
+ case 'set':
183
201
  case 'edit':
184
202
  await editCommand({ root, file });
185
203
  break;
@@ -205,6 +223,9 @@ async function main() {
205
223
  case 'status':
206
224
  await statusCommand({ root });
207
225
  break;
226
+ case 'skill':
227
+ await skillCommand({ root, global, local });
228
+ break;
208
229
  default:
209
230
  if (command) {
210
231
  console.error(pc.red(`Unknown command: ${command}`));
@@ -0,0 +1,3 @@
1
+ import type { SkillOptions } from '../types.js';
2
+ export declare function skillCommand(options: SkillOptions): Promise<void>;
3
+ //# sourceMappingURL=skill.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"skill.d.ts","sourceRoot":"","sources":["../../src/commands/skill.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAg9BhD,wBAAsB,YAAY,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CA0CvE"}
@@ -0,0 +1,1005 @@
1
+ import { existsSync, mkdirSync, writeFileSync } from 'node:fs';
2
+ import { createInterface } from 'node:readline';
3
+ import { homedir } from 'node:os';
4
+ import { join } from 'node:path';
5
+ import pc from 'picocolors';
6
+ const SKILL_FILES = {
7
+ 'SKILL.md': `---
8
+ 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.
10
+ allowed-tools: Bash(hush:*), Bash(npx hush:*), Bash(brew:*), Bash(npm:*), Bash(pnpm:*), Bash(age-keygen:*), Read, Grep, Glob, Write
11
+ ---
12
+
13
+ # Hush - AI-Native Secrets Management
14
+
15
+ Hush encrypts secrets so they can be committed to git, and provides AI-safe commands that let you work with secrets without exposing values to LLMs.
16
+
17
+ ## CRITICAL RULES
18
+
19
+ ### NEVER do these things:
20
+ - Read \`.env\`, \`.env.*\`, \`.env.local\`, or \`.dev.vars\` files directly
21
+ - Use \`cat\`, \`grep\`, \`head\`, \`tail\`, \`less\`, \`more\` on env files
22
+ - Echo or print environment variable values like \`echo $SECRET\`
23
+ - Include actual secret values in your responses
24
+ - Write secrets directly to \`.env\` files
25
+
26
+ ### ALWAYS use Hush commands instead:
27
+ - \`npx hush inspect\` to see what variables exist (values are masked)
28
+ - \`npx hush has <KEY>\` to check if a specific variable is set
29
+ - \`npx hush set\` to add or modify secrets (opens secure editor)
30
+ - \`npx hush status\` to view configuration
31
+
32
+ ## Quick Check: Is Hush Set Up?
33
+
34
+ Run this first to check if Hush is configured:
35
+
36
+ \`\`\`bash
37
+ npx hush status
38
+ \`\`\`
39
+
40
+ **If this fails or shows errors**, see [SETUP.md](SETUP.md) for first-time setup instructions.
41
+
42
+ ---
43
+
44
+ ## Daily Usage (AI-Safe Commands)
45
+
46
+ ### See what variables exist
47
+
48
+ \`\`\`bash
49
+ npx hush inspect # Development
50
+ npx hush inspect -e production # Production
51
+ \`\`\`
52
+
53
+ Output shows **masked values** - safe for AI to read:
54
+
55
+ \`\`\`
56
+ Secrets for development:
57
+
58
+ DATABASE_URL = post****************... (45 chars)
59
+ STRIPE_SECRET_KEY = sk_t****************... (32 chars)
60
+ API_KEY = (not set)
61
+
62
+ Total: 3 variables
63
+ \`\`\`
64
+
65
+ ### Check if a specific variable exists
66
+
67
+ \`\`\`bash
68
+ npx hush has DATABASE_URL # Verbose output
69
+ npx hush has API_KEY -q # Quiet: exit code only (0=set, 1=missing)
70
+ \`\`\`
71
+
72
+ ### View configuration
73
+
74
+ \`\`\`bash
75
+ npx hush status
76
+ \`\`\`
77
+
78
+ ### Set/modify secrets (requires user interaction)
79
+
80
+ \`\`\`bash
81
+ npx hush set # Set shared secrets
82
+ npx hush set development # Set dev secrets
83
+ npx hush set production # Set prod secrets
84
+ \`\`\`
85
+
86
+ After setting, encrypt:
87
+
88
+ \`\`\`bash
89
+ npx hush encrypt
90
+ \`\`\`
91
+
92
+ ### Decrypt to targets
93
+
94
+ \`\`\`bash
95
+ npx hush decrypt # Development
96
+ npx hush decrypt -e production # Production
97
+ \`\`\`
98
+
99
+ ---
100
+
101
+ ## Common Workflows
102
+
103
+ ### "What secrets are configured?"
104
+ \`\`\`bash
105
+ npx hush inspect
106
+ \`\`\`
107
+
108
+ ### "Is DATABASE_URL set?"
109
+ \`\`\`bash
110
+ npx hush has DATABASE_URL
111
+ \`\`\`
112
+
113
+ ### "Help user add a new secret"
114
+ 1. Tell user to run: \`npx hush set\`
115
+ 2. They add the variable in their editor
116
+ 3. They save and close
117
+ 4. Tell them to run: \`npx hush encrypt\`
118
+ 5. Verify: \`npx hush inspect\`
119
+
120
+ ### "Check all required secrets"
121
+ \`\`\`bash
122
+ npx hush has DATABASE_URL -q && npx hush has API_KEY -q && echo "All configured" || echo "Some missing"
123
+ \`\`\`
124
+
125
+ ---
126
+
127
+ ## Files You Must NOT Read
128
+
129
+ These contain plaintext secrets - NEVER read them:
130
+ - \`.env\`, \`.env.local\`, \`.env.development\`, \`.env.production\`
131
+ - \`.dev.vars\`
132
+ - Any \`*/.env\` or \`*/.env.*\` files
133
+
134
+ ## Files That Are Safe to Read
135
+
136
+ - \`hush.yaml\` - Configuration (no secrets)
137
+ - \`.sops.yaml\` - SOPS config (public key only)
138
+ - \`.env.encrypted\`, \`.env.*.encrypted\` - Encrypted files
139
+
140
+ ---
141
+
142
+ ## Additional Resources
143
+
144
+ - **First-time setup**: [SETUP.md](SETUP.md)
145
+ - **Command reference**: [REFERENCE.md](REFERENCE.md)
146
+ - **Workflow examples**: [examples/workflows.md](examples/workflows.md)
147
+ `,
148
+ 'SETUP.md': `# Hush First-Time Setup
149
+
150
+ This guide walks through setting up Hush from scratch. Follow these steps in order.
151
+
152
+ ## Prerequisites Check
153
+
154
+ ### 1. Check if SOPS and age are installed
155
+
156
+ \`\`\`bash
157
+ which sops && which age-keygen && echo "Prerequisites installed" || echo "Need to install prerequisites"
158
+ \`\`\`
159
+
160
+ If not installed:
161
+
162
+ **macOS:**
163
+ \`\`\`bash
164
+ brew install sops age
165
+ \`\`\`
166
+
167
+ **Linux (Debian/Ubuntu):**
168
+ \`\`\`bash
169
+ sudo apt install age
170
+ # SOPS: Download from https://github.com/getsops/sops/releases
171
+ wget https://github.com/getsops/sops/releases/download/v3.8.1/sops-v3.8.1.linux.amd64 -O /usr/local/bin/sops
172
+ chmod +x /usr/local/bin/sops
173
+ \`\`\`
174
+
175
+ **Windows (Chocolatey):**
176
+ \`\`\`powershell
177
+ choco install sops age
178
+ \`\`\`
179
+
180
+ ### 2. Check for age encryption key
181
+
182
+ \`\`\`bash
183
+ test -f ~/.config/sops/age/key.txt && echo "Key exists" || echo "Need to create key"
184
+ \`\`\`
185
+
186
+ If no key exists, create one:
187
+
188
+ \`\`\`bash
189
+ mkdir -p ~/.config/sops/age
190
+ age-keygen -o ~/.config/sops/age/key.txt
191
+ \`\`\`
192
+
193
+ ### 3. Get your public key
194
+
195
+ \`\`\`bash
196
+ grep "public key:" ~/.config/sops/age/key.txt
197
+ \`\`\`
198
+
199
+ Save this \`age1...\` value - you'll need it for the next step.
200
+
201
+ ---
202
+
203
+ ## Project Setup
204
+
205
+ ### Step 1: Install Hush
206
+
207
+ \`\`\`bash
208
+ npm install -D @chriscode/hush
209
+ # or
210
+ pnpm add -D @chriscode/hush
211
+ \`\`\`
212
+
213
+ ### Step 2: Create \`.sops.yaml\`
214
+
215
+ Create \`.sops.yaml\` in your repo root with your public key:
216
+
217
+ \`\`\`yaml
218
+ creation_rules:
219
+ - encrypted_regex: '.*'
220
+ age: age1xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
221
+ \`\`\`
222
+
223
+ Replace \`age1xxx...\` with your actual public key from the prerequisites step.
224
+
225
+ ### Step 3: Initialize Hush
226
+
227
+ \`\`\`bash
228
+ npx hush init
229
+ \`\`\`
230
+
231
+ This creates \`hush.yaml\` with auto-detected targets based on your project structure.
232
+
233
+ ### Step 4: Review \`hush.yaml\`
234
+
235
+ The generated config looks like:
236
+
237
+ \`\`\`yaml
238
+ sources:
239
+ shared: .env
240
+ development: .env.development
241
+ production: .env.production
242
+
243
+ targets:
244
+ - name: root
245
+ path: .
246
+ format: dotenv
247
+ \`\`\`
248
+
249
+ Customize targets for your monorepo. Common patterns:
250
+
251
+ **Next.js app (client vars only):**
252
+ \`\`\`yaml
253
+ - name: web
254
+ path: ./apps/web
255
+ format: dotenv
256
+ include:
257
+ - NEXT_PUBLIC_*
258
+ \`\`\`
259
+
260
+ **API server (exclude client vars):**
261
+ \`\`\`yaml
262
+ - name: api
263
+ path: ./apps/api
264
+ format: wrangler # or dotenv
265
+ exclude:
266
+ - NEXT_PUBLIC_*
267
+ - VITE_*
268
+ \`\`\`
269
+
270
+ **Kubernetes:**
271
+ \`\`\`yaml
272
+ - name: k8s
273
+ path: ./k8s
274
+ format: yaml
275
+ \`\`\`
276
+
277
+ ### Step 5: Create initial \`.env\` files
278
+
279
+ Create \`.env\` with shared secrets:
280
+
281
+ \`\`\`bash
282
+ # .env
283
+ DATABASE_URL=postgres://localhost/mydb
284
+ API_KEY=your_api_key_here
285
+ NEXT_PUBLIC_API_URL=http://localhost:3000
286
+ \`\`\`
287
+
288
+ Create \`.env.development\` for dev-specific values:
289
+
290
+ \`\`\`bash
291
+ # .env.development
292
+ DEBUG=true
293
+ LOG_LEVEL=debug
294
+ \`\`\`
295
+
296
+ Create \`.env.production\` for production values:
297
+
298
+ \`\`\`bash
299
+ # .env.production
300
+ DEBUG=false
301
+ LOG_LEVEL=error
302
+ \`\`\`
303
+
304
+ ### Step 6: Encrypt secrets
305
+
306
+ \`\`\`bash
307
+ npx hush encrypt
308
+ \`\`\`
309
+
310
+ This creates:
311
+ - \`.env.encrypted\`
312
+ - \`.env.development.encrypted\`
313
+ - \`.env.production.encrypted\`
314
+
315
+ ### Step 7: Verify setup
316
+
317
+ \`\`\`bash
318
+ npx hush status
319
+ npx hush inspect
320
+ \`\`\`
321
+
322
+ ### Step 8: Update \`.gitignore\`
323
+
324
+ Add these lines to \`.gitignore\`:
325
+
326
+ \`\`\`gitignore
327
+ # Hush - plaintext env files (generated, not committed)
328
+ .env
329
+ .env.local
330
+ .env.development
331
+ .env.production
332
+ .dev.vars
333
+
334
+ # Keep encrypted files (these ARE committed)
335
+ !.env.encrypted
336
+ !.env.*.encrypted
337
+ \`\`\`
338
+
339
+ ### Step 9: Commit encrypted files
340
+
341
+ \`\`\`bash
342
+ git add .sops.yaml hush.yaml .env*.encrypted .gitignore
343
+ git commit -m "chore: add Hush secrets management"
344
+ \`\`\`
345
+
346
+ ---
347
+
348
+ ## Team Member Setup
349
+
350
+ When a new team member joins:
351
+
352
+ 1. **Get the age private key** from an existing team member
353
+ 2. **Save it** to \`~/.config/sops/age/key.txt\`
354
+ 3. **Run** \`npx hush decrypt\` to generate local env files
355
+ 4. **Start developing**
356
+
357
+ The private key should be shared securely (password manager, encrypted channel, etc.)
358
+
359
+ ---
360
+
361
+ ## Verification Checklist
362
+
363
+ After setup, verify everything works:
364
+
365
+ - [ ] \`npx hush status\` shows configuration
366
+ - [ ] \`npx hush inspect\` shows masked variables
367
+ - [ ] \`npx hush decrypt\` creates local env files
368
+ - [ ] \`.env.encrypted\` files are committed to git
369
+ - [ ] Plaintext \`.env\` files are in \`.gitignore\`
370
+
371
+ ---
372
+
373
+ ## Troubleshooting Setup
374
+
375
+ ### "age: command not found"
376
+ \`\`\`bash
377
+ brew install age # macOS
378
+ \`\`\`
379
+
380
+ ### "sops: command not found"
381
+ \`\`\`bash
382
+ brew install sops # macOS
383
+ \`\`\`
384
+
385
+ ### "Error: no matching keys found"
386
+ Your age key doesn't match. Get the correct private key from a team member.
387
+
388
+ ### "hush.yaml not found"
389
+ Run \`npx hush init\` to generate configuration.
390
+
391
+ ### "No sources defined in hush.yaml"
392
+ Edit \`hush.yaml\` and add your source files under \`sources:\`.
393
+ `,
394
+ 'REFERENCE.md': `# Hush Command Reference
395
+
396
+ Complete reference for all Hush CLI commands with flags, options, and examples.
397
+
398
+ ## Global Options
399
+
400
+ These options work with most commands:
401
+
402
+ | Option | Description |
403
+ |--------|-------------|
404
+ | \`-e, --env <env>\` | Environment: \`development\` (or \`dev\`) / \`production\` (or \`prod\`). Default: \`development\` |
405
+ | \`-r, --root <dir>\` | Root directory containing \`hush.yaml\`. Default: current directory |
406
+ | \`-h, --help\` | Show help message |
407
+ | \`-v, --version\` | Show version number |
408
+
409
+ ## Commands
410
+
411
+ ### hush init
412
+
413
+ Generate a \`hush.yaml\` configuration file with auto-detected targets.
414
+
415
+ \`\`\`bash
416
+ hush init
417
+ \`\`\`
418
+
419
+ Scans for \`package.json\` and \`wrangler.toml\` files to auto-detect targets.
420
+
421
+ ---
422
+
423
+ ### hush encrypt
424
+
425
+ Encrypt source \`.env\` files to \`.env.encrypted\` files.
426
+
427
+ \`\`\`bash
428
+ hush encrypt
429
+ \`\`\`
430
+
431
+ **What gets encrypted** (based on \`hush.yaml\` sources):
432
+ - \`.env\` -> \`.env.encrypted\`
433
+ - \`.env.development\` -> \`.env.development.encrypted\`
434
+ - \`.env.production\` -> \`.env.production.encrypted\`
435
+
436
+ ---
437
+
438
+ ### hush decrypt
439
+
440
+ Decrypt and distribute secrets to all configured targets.
441
+
442
+ \`\`\`bash
443
+ hush decrypt # Development (default)
444
+ hush decrypt -e production # Production
445
+ hush decrypt -e prod # Short form
446
+ \`\`\`
447
+
448
+ **Process:**
449
+ 1. Decrypts encrypted source files
450
+ 2. Merges: shared -> environment -> local overrides
451
+ 3. Interpolates variable references (\`\${VAR}\`)
452
+ 4. Filters per target using \`include\`/\`exclude\` patterns
453
+ 5. Writes to each target in configured format
454
+
455
+ ---
456
+
457
+ ### hush set (alias: edit)
458
+
459
+ Set or modify secrets. Opens encrypted file in your \`$EDITOR\`.
460
+
461
+ \`\`\`bash
462
+ hush set # Set shared secrets
463
+ hush set development # Set development secrets
464
+ hush set production # Set production secrets
465
+ \`\`\`
466
+
467
+ Opens a temporary decrypted file, re-encrypts on save.
468
+
469
+ **Tip:** Set your editor with \`export EDITOR=vim\` or use \`code --wait\` for VS Code.
470
+
471
+ ---
472
+
473
+ ### hush list
474
+
475
+ List all variables with their **actual values**.
476
+
477
+ \`\`\`bash
478
+ hush list # Development
479
+ hush list -e production # Production
480
+ \`\`\`
481
+
482
+ **WARNING:** This shows real secret values. Use \`hush inspect\` for AI-safe output.
483
+
484
+ ---
485
+
486
+ ### hush inspect (AI-Safe)
487
+
488
+ List all variables with **masked values**. Safe for AI agents.
489
+
490
+ \`\`\`bash
491
+ hush inspect # Development
492
+ hush inspect -e production # Production
493
+ \`\`\`
494
+
495
+ **Output format:**
496
+ \`\`\`
497
+ Secrets for development:
498
+
499
+ DATABASE_URL = post****************... (45 chars)
500
+ STRIPE_SECRET_KEY = sk_t****************... (32 chars)
501
+ API_KEY = (not set)
502
+
503
+ Total: 3 variables
504
+
505
+ Target distribution:
506
+
507
+ root (.) - 3 vars
508
+ app (./app/) - 1 vars
509
+ include: EXPO_PUBLIC_*
510
+ api (./api/) - 2 vars
511
+ exclude: EXPO_PUBLIC_*
512
+ \`\`\`
513
+
514
+ **What's visible:**
515
+ - Variable names
516
+ - First 4 characters (helps identify type: \`sk_\` = Stripe, \`ghp_\` = GitHub)
517
+ - Value length
518
+ - Which targets receive which variables
519
+
520
+ ---
521
+
522
+ ### hush has (AI-Safe)
523
+
524
+ Check if a specific secret exists.
525
+
526
+ \`\`\`bash
527
+ hush has <KEY> # Verbose output
528
+ hush has <KEY> -q # Quiet mode (exit code only)
529
+ hush has <KEY> --quiet # Same as -q
530
+ \`\`\`
531
+
532
+ **Exit codes:**
533
+ - \`0\` - Variable is set
534
+ - \`1\` - Variable not found
535
+
536
+ **Examples:**
537
+ \`\`\`bash
538
+ # Check with output
539
+ hush has DATABASE_URL
540
+ # Output: DATABASE_URL is set (45 chars)
541
+
542
+ # Check missing variable
543
+ hush has MISSING_KEY
544
+ # Output: MISSING_KEY not found
545
+ # Exit code: 1
546
+
547
+ # Use in scripts
548
+ hush has DATABASE_URL -q && echo "DB ready"
549
+
550
+ # Check multiple
551
+ hush has DB_URL -q && hush has API_KEY -q && echo "All set"
552
+ \`\`\`
553
+
554
+ ---
555
+
556
+ ### hush push
557
+
558
+ Push production secrets to Cloudflare Workers.
559
+
560
+ \`\`\`bash
561
+ hush push # Push secrets
562
+ hush push --dry-run # Preview without pushing
563
+ \`\`\`
564
+
565
+ **Options:**
566
+
567
+ | Option | Description |
568
+ |--------|-------------|
569
+ | \`--dry-run\` | Preview what would be pushed, don't actually push |
570
+
571
+ **Requirements:**
572
+ - Target must have \`format: wrangler\`
573
+ - \`wrangler.toml\` must exist in target path
574
+ - \`wrangler\` CLI must be installed and authenticated
575
+
576
+ ---
577
+
578
+ ### hush status
579
+
580
+ Show configuration and file status.
581
+
582
+ \`\`\`bash
583
+ hush status
584
+ \`\`\`
585
+
586
+ **Output includes:**
587
+ - Configuration file location
588
+ - Source files and their encryption status
589
+ - Target configuration (paths, formats, filters)
590
+ - Whether files are in sync
591
+
592
+ ---
593
+
594
+ ### hush check
595
+
596
+ Verify secrets are encrypted (useful for pre-commit hooks).
597
+
598
+ \`\`\`bash
599
+ hush check # Basic check
600
+ hush check --warn # Warn but don't fail
601
+ hush check --json # JSON output for CI
602
+ hush check --only-changed # Only check git-modified files
603
+ hush check --require-source # Fail if source file missing
604
+ \`\`\`
605
+
606
+ **Exit codes:**
607
+ - \`0\` - All in sync
608
+ - \`1\` - Drift detected (run \`hush encrypt\`)
609
+ - \`2\` - Config error
610
+ - \`3\` - Runtime error (sops missing, decrypt failed)
611
+
612
+ **Pre-commit hook (Husky):**
613
+ \`\`\`bash
614
+ # .husky/pre-commit
615
+ npx hush check || exit 1
616
+ \`\`\`
617
+
618
+ Bypass with: \`HUSH_SKIP_CHECK=1 git commit -m "message"\`
619
+
620
+ ---
621
+
622
+ ### hush skill
623
+
624
+ Install the Claude Code / OpenCode skill for AI-safe secrets management.
625
+
626
+ \`\`\`bash
627
+ hush skill # Interactive: choose global or local
628
+ hush skill --global # Install to ~/.claude/skills/
629
+ hush skill --local # Install to ./.claude/skills/
630
+ \`\`\`
631
+
632
+ **Global install:** Works across all your projects. Recommended for personal use.
633
+
634
+ **Local install:** Bundled with the project. Recommended for teams (skill travels with the repo).
635
+
636
+ ## Configuration File (hush.yaml)
637
+
638
+ \`\`\`yaml
639
+ sources:
640
+ shared: .env
641
+ development: .env.development
642
+ production: .env.production
643
+
644
+ targets:
645
+ - name: root
646
+ path: .
647
+ format: dotenv
648
+
649
+ - name: app
650
+ path: ./packages/app
651
+ format: dotenv
652
+ include:
653
+ - EXPO_PUBLIC_*
654
+ - NEXT_PUBLIC_*
655
+
656
+ - name: api
657
+ path: ./packages/api
658
+ format: wrangler
659
+ exclude:
660
+ - EXPO_PUBLIC_*
661
+ \`\`\`
662
+
663
+ ### Target Options
664
+
665
+ | Option | Description |
666
+ |--------|-------------|
667
+ | \`name\` | Identifier for the target |
668
+ | \`path\` | Directory to write output file |
669
+ | \`format\` | Output format: \`dotenv\`, \`wrangler\`, \`json\`, \`shell\`, \`yaml\` |
670
+ | \`include\` | Glob patterns to include (e.g., \`NEXT_PUBLIC_*\`) |
671
+ | \`exclude\` | Glob patterns to exclude |
672
+
673
+ ### Output Formats
674
+
675
+ | Format | Output File | Use Case |
676
+ |--------|-------------|----------|
677
+ | \`dotenv\` | \`.env.development\` / \`.env.production\` | Next.js, Vite, Expo, Remix, Node.js |
678
+ | \`wrangler\` | \`.dev.vars\` | Cloudflare Workers & Pages |
679
+ | \`json\` | \`.env.development.json\` | AWS Lambda, serverless, JSON configs |
680
+ | \`shell\` | \`.env.development.sh\` | CI/CD pipelines, Docker builds |
681
+ | \`yaml\` | \`.env.development.yaml\` | Kubernetes ConfigMaps, Docker Compose |
682
+
683
+ ### Framework Client Prefixes
684
+
685
+ | Framework | Client Prefix | Example |
686
+ |-----------|--------------|---------|
687
+ | Next.js | \`NEXT_PUBLIC_*\` | \`include: [NEXT_PUBLIC_*]\` |
688
+ | Vite | \`VITE_*\` | \`include: [VITE_*]\` |
689
+ | Create React App | \`REACT_APP_*\` | \`include: [REACT_APP_*]\` |
690
+ | Vue CLI | \`VUE_APP_*\` | \`include: [VUE_APP_*]\` |
691
+ | Nuxt | \`NUXT_PUBLIC_*\` | \`include: [NUXT_PUBLIC_*]\` |
692
+ | Astro | \`PUBLIC_*\` | \`include: [PUBLIC_*]\` |
693
+ | SvelteKit | \`PUBLIC_*\` | \`include: [PUBLIC_*]\` |
694
+ | Expo | \`EXPO_PUBLIC_*\` | \`include: [EXPO_PUBLIC_*]\` |
695
+ | Gatsby | \`GATSBY_*\` | \`include: [GATSBY_*]\` |
696
+ `,
697
+ 'examples/workflows.md': `# Hush Workflow Examples
698
+
699
+ Step-by-step examples for common AI assistant workflows when working with secrets.
700
+
701
+ ## Checking Configuration
702
+
703
+ ### "What environment variables does this project use?"
704
+
705
+ \`\`\`bash
706
+ hush inspect
707
+ \`\`\`
708
+
709
+ Read the output to see all configured variables, their approximate lengths, and which targets receive them.
710
+
711
+ ### "Is the database configured?"
712
+
713
+ \`\`\`bash
714
+ hush has DATABASE_URL
715
+ \`\`\`
716
+
717
+ If the output says "not found", guide the user to add it.
718
+
719
+ ### "Are all required secrets set?"
720
+
721
+ \`\`\`bash
722
+ # Check each required secret
723
+ hush has DATABASE_URL -q || echo "Missing: DATABASE_URL"
724
+ hush has API_KEY -q || echo "Missing: API_KEY"
725
+ hush has STRIPE_SECRET_KEY -q || echo "Missing: STRIPE_SECRET_KEY"
726
+ \`\`\`
727
+
728
+ Or check all at once:
729
+ \`\`\`bash
730
+ hush has DATABASE_URL -q && \\
731
+ hush has API_KEY -q && \\
732
+ hush has STRIPE_SECRET_KEY -q && \\
733
+ echo "All secrets configured" || \\
734
+ echo "Some secrets missing"
735
+ \`\`\`
736
+
737
+ ---
738
+
739
+ ## Helping Users Add Secrets
740
+
741
+ ### "Help me add a new API key"
742
+
743
+ 1. **Check if it already exists:**
744
+ \`\`\`bash
745
+ hush has NEW_API_KEY
746
+ \`\`\`
747
+
748
+ 2. **If not set, guide the user:**
749
+ > To add \`NEW_API_KEY\`, run:
750
+ > \`\`\`bash
751
+ > hush set
752
+ > \`\`\`
753
+ > Add a line like: \`NEW_API_KEY=your_actual_key_here\`
754
+ > Save and close the editor, then run:
755
+ > \`\`\`bash
756
+ > hush encrypt
757
+ > \`\`\`
758
+
759
+ 3. **Verify it was added:**
760
+ \`\`\`bash
761
+ hush has NEW_API_KEY
762
+ \`\`\`
763
+
764
+ ### "I need to add secrets for production"
765
+
766
+ Guide the user:
767
+ > Run \`hush set production\` to set production secrets.
768
+ > After saving, run \`hush encrypt\` to encrypt the changes.
769
+ > To deploy, run \`hush decrypt -e production\`.
770
+
771
+ ---
772
+
773
+ ## Debugging Issues
774
+
775
+ ### "My app can't find DATABASE_URL"
776
+
777
+ 1. **Check if the variable exists:**
778
+ \`\`\`bash
779
+ hush has DATABASE_URL
780
+ \`\`\`
781
+
782
+ 2. **If it exists, check target distribution:**
783
+ \`\`\`bash
784
+ hush inspect
785
+ \`\`\`
786
+ Look at the "Target distribution" section to see which targets receive it.
787
+
788
+ 3. **Check if it's filtered out:**
789
+ \`\`\`bash
790
+ cat hush.yaml
791
+ \`\`\`
792
+ Look for \`include\`/\`exclude\` patterns that might filter the variable.
793
+
794
+ 4. **Regenerate env files:**
795
+ \`\`\`bash
796
+ hush decrypt
797
+ \`\`\`
798
+
799
+ ### "Secrets aren't reaching my API folder"
800
+
801
+ 1. **Check target configuration:**
802
+ \`\`\`bash
803
+ hush status
804
+ \`\`\`
805
+ Verify the API target path and format are correct.
806
+
807
+ 2. **Check filters:**
808
+ \`\`\`bash
809
+ cat hush.yaml
810
+ \`\`\`
811
+ If there's an \`exclude: EXPO_PUBLIC_*\` pattern, that's intentional.
812
+ If there's an \`include\` pattern, only matching variables are sent.
813
+
814
+ 3. **Run inspect to see distribution:**
815
+ \`\`\`bash
816
+ hush inspect
817
+ \`\`\`
818
+
819
+ ---
820
+
821
+ ## Deployment Workflows
822
+
823
+ ### "Deploy to production"
824
+
825
+ \`\`\`bash
826
+ # Decrypt production secrets to all targets
827
+ hush decrypt -e production
828
+ \`\`\`
829
+
830
+ ### "Push secrets to Cloudflare Workers"
831
+
832
+ \`\`\`bash
833
+ # Preview what would be pushed
834
+ hush push --dry-run
835
+
836
+ # Actually push (requires wrangler auth)
837
+ hush push
838
+ \`\`\`
839
+
840
+ ### "Verify before deploying"
841
+
842
+ \`\`\`bash
843
+ # Check all encrypted files are up to date
844
+ hush check
845
+
846
+ # If drift detected, encrypt first
847
+ hush encrypt
848
+
849
+ # Then decrypt for production
850
+ hush decrypt -e production
851
+ \`\`\`
852
+
853
+ ---
854
+
855
+ ## Team Workflows
856
+
857
+ ### "New team member setup"
858
+
859
+ Guide them:
860
+ > 1. Get the age private key from a team member
861
+ > 2. Save it to \`~/.config/sops/age/key.txt\`
862
+ > 3. Run \`hush decrypt\` to generate local env files
863
+ > 4. Start developing!
864
+
865
+ ### "Someone added new secrets, my app is broken"
866
+
867
+ \`\`\`bash
868
+ # Pull latest changes
869
+ git pull
870
+
871
+ # Regenerate env files
872
+ hush decrypt
873
+ \`\`\`
874
+
875
+ ### "Check if I forgot to encrypt changes"
876
+
877
+ \`\`\`bash
878
+ hush check
879
+ \`\`\`
880
+
881
+ If drift detected:
882
+ \`\`\`bash
883
+ hush encrypt
884
+ git add .env*.encrypted
885
+ git commit -m "chore: encrypt new secrets"
886
+ \`\`\`
887
+
888
+ ---
889
+
890
+ ## Understanding the Output
891
+
892
+ ### hush inspect output explained
893
+
894
+ \`\`\`
895
+ Secrets for development:
896
+
897
+ DATABASE_URL = post****************... (45 chars)
898
+ STRIPE_SECRET_KEY = sk_t****************... (32 chars)
899
+ API_KEY = (not set)
900
+
901
+ Total: 3 variables
902
+
903
+ Target distribution:
904
+
905
+ root (.) - 3 vars
906
+ app (./app/) - 1 vars
907
+ include: EXPO_PUBLIC_*
908
+ api (./api/) - 2 vars
909
+ exclude: EXPO_PUBLIC_*
910
+ \`\`\`
911
+
912
+ **Reading this:**
913
+ - \`DATABASE_URL\` is set, starts with "post", is 45 characters (likely a postgres:// URL)
914
+ - \`STRIPE_SECRET_KEY\` starts with "sk_t" (Stripe test key format)
915
+ - \`API_KEY\` is not set - user needs to add it
916
+ - The \`app\` folder only gets \`EXPO_PUBLIC_*\` variables
917
+ - The \`api\` folder gets everything except \`EXPO_PUBLIC_*\`
918
+
919
+ ### hush has output explained
920
+
921
+ \`\`\`bash
922
+ $ hush has DATABASE_URL
923
+ DATABASE_URL is set (45 chars)
924
+
925
+ $ hush has MISSING_VAR
926
+ MISSING_VAR not found
927
+ \`\`\`
928
+
929
+ The character count helps identify if the value looks reasonable (e.g., a 45-char DATABASE_URL is plausible, a 3-char one might be wrong).
930
+ `,
931
+ };
932
+ function getSkillPath(location, root) {
933
+ if (location === 'global') {
934
+ return join(homedir(), '.claude', 'skills', 'hush-secrets');
935
+ }
936
+ return join(root, '.claude', 'skills', 'hush-secrets');
937
+ }
938
+ async function promptForLocation() {
939
+ const rl = createInterface({
940
+ input: process.stdin,
941
+ output: process.stdout,
942
+ });
943
+ return new Promise((resolve) => {
944
+ console.log(pc.bold('\nWhere would you like to install the Claude skill?\n'));
945
+ console.log(` ${pc.cyan('1)')} ${pc.bold('Global')} ${pc.dim('(~/.claude/skills/)')}`);
946
+ console.log(` Works across all your projects. Recommended for personal use.\n`);
947
+ console.log(` ${pc.cyan('2)')} ${pc.bold('Local')} ${pc.dim('(.claude/skills/)')}`);
948
+ console.log(` Bundled with this project. Recommended for teams.\n`);
949
+ rl.question(`${pc.bold('Choice')} ${pc.dim('[1/2]')}: `, (answer) => {
950
+ rl.close();
951
+ const choice = answer.trim();
952
+ if (choice === '2' || choice.toLowerCase() === 'local') {
953
+ resolve('local');
954
+ }
955
+ else {
956
+ resolve('global');
957
+ }
958
+ });
959
+ });
960
+ }
961
+ function writeSkillFiles(skillPath) {
962
+ mkdirSync(skillPath, { recursive: true });
963
+ mkdirSync(join(skillPath, 'examples'), { recursive: true });
964
+ for (const [filename, content] of Object.entries(SKILL_FILES)) {
965
+ const filePath = join(skillPath, filename);
966
+ writeFileSync(filePath, content, 'utf-8');
967
+ }
968
+ }
969
+ export async function skillCommand(options) {
970
+ const { root, global: isGlobal, local: isLocal } = options;
971
+ let location;
972
+ if (isGlobal) {
973
+ location = 'global';
974
+ }
975
+ else if (isLocal) {
976
+ location = 'local';
977
+ }
978
+ else {
979
+ location = await promptForLocation();
980
+ }
981
+ const skillPath = getSkillPath(location, root);
982
+ const alreadyInstalled = existsSync(join(skillPath, 'SKILL.md'));
983
+ if (alreadyInstalled) {
984
+ console.log(pc.yellow(`\nSkill already installed at: ${skillPath}`));
985
+ console.log(pc.dim('To reinstall, delete the directory first.\n'));
986
+ return;
987
+ }
988
+ console.log(pc.blue(`\nInstalling Claude skill to: ${skillPath}`));
989
+ writeSkillFiles(skillPath);
990
+ console.log(pc.green('\n✓ Skill installed successfully!\n'));
991
+ if (location === 'global') {
992
+ console.log(pc.dim('The skill is now active for all projects using Claude Code.\n'));
993
+ }
994
+ else {
995
+ console.log(pc.dim('The skill is now bundled with this project.'));
996
+ console.log(pc.dim('Commit the .claude/ directory to share with your team.\n'));
997
+ console.log(pc.bold('Suggested:'));
998
+ console.log(` git add .claude/`);
999
+ console.log(` git commit -m "chore: add Hush Claude skill"\n`);
1000
+ }
1001
+ console.log(pc.bold('What the skill does:'));
1002
+ console.log(` • Teaches AI to use ${pc.cyan('hush inspect')} instead of reading .env files`);
1003
+ console.log(` • Prevents accidental exposure of secrets to LLMs`);
1004
+ console.log(` • Guides AI through adding/modifying secrets safely\n`);
1005
+ }
@@ -3,6 +3,7 @@ import { formatDotenv } from './dotenv.js';
3
3
  import { formatJson } from './json.js';
4
4
  import { formatShell } from './shell.js';
5
5
  import { formatWrangler } from './wrangler.js';
6
+ import { formatYaml } from './yaml.js';
6
7
  export declare function formatVars(vars: EnvVar[], format: OutputFormat): string;
7
- export { formatDotenv, formatJson, formatShell, formatWrangler };
8
+ export { formatDotenv, formatJson, formatShell, formatWrangler, formatYaml };
8
9
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/formats/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AACxD,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AACvC,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACzC,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAE/C,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,MAAM,EAAE,YAAY,GAAG,MAAM,CAWvE;AAED,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,WAAW,EAAE,cAAc,EAAE,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/formats/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AACxD,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AACvC,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACzC,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAC/C,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAEvC,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,MAAM,EAAE,YAAY,GAAG,MAAM,CAavE;AAED,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,WAAW,EAAE,cAAc,EAAE,UAAU,EAAE,CAAC"}
@@ -2,6 +2,7 @@ import { formatDotenv } from './dotenv.js';
2
2
  import { formatJson } from './json.js';
3
3
  import { formatShell } from './shell.js';
4
4
  import { formatWrangler } from './wrangler.js';
5
+ import { formatYaml } from './yaml.js';
5
6
  export function formatVars(vars, format) {
6
7
  switch (format) {
7
8
  case 'dotenv':
@@ -12,6 +13,8 @@ export function formatVars(vars, format) {
12
13
  return formatJson(vars);
13
14
  case 'shell':
14
15
  return formatShell(vars);
16
+ case 'yaml':
17
+ return formatYaml(vars);
15
18
  }
16
19
  }
17
- export { formatDotenv, formatJson, formatShell, formatWrangler };
20
+ export { formatDotenv, formatJson, formatShell, formatWrangler, formatYaml };
@@ -0,0 +1,13 @@
1
+ import type { EnvVar } from '../types.js';
2
+ /**
3
+ * Format environment variables as YAML.
4
+ * Useful for Kubernetes ConfigMaps, Docker Compose, and other YAML-based configs.
5
+ *
6
+ * Output format:
7
+ * ```yaml
8
+ * DATABASE_URL: "postgres://localhost/db"
9
+ * API_KEY: "sk_test_xxx"
10
+ * ```
11
+ */
12
+ export declare function formatYaml(vars: EnvVar[]): string;
13
+ //# sourceMappingURL=yaml.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"yaml.d.ts","sourceRoot":"","sources":["../../src/formats/yaml.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAE1C;;;;;;;;;GASG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,MAAM,CA6CjD"}
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Format environment variables as YAML.
3
+ * Useful for Kubernetes ConfigMaps, Docker Compose, and other YAML-based configs.
4
+ *
5
+ * Output format:
6
+ * ```yaml
7
+ * DATABASE_URL: "postgres://localhost/db"
8
+ * API_KEY: "sk_test_xxx"
9
+ * ```
10
+ */
11
+ export function formatYaml(vars) {
12
+ if (vars.length === 0) {
13
+ return '{}\n';
14
+ }
15
+ return (vars
16
+ .map(({ key, value }) => {
17
+ // YAML string escaping rules:
18
+ // - Simple alphanumeric values don't need quotes
19
+ // - Values with special chars need double quotes
20
+ // - Double quotes inside values need escaping
21
+ const needsQuotes = value === '' ||
22
+ value.includes(':') ||
23
+ value.includes('#') ||
24
+ value.includes("'") ||
25
+ value.includes('"') ||
26
+ value.includes('\n') ||
27
+ value.includes('\\') ||
28
+ value.startsWith(' ') ||
29
+ value.endsWith(' ') ||
30
+ value.startsWith('!') ||
31
+ value.startsWith('&') ||
32
+ value.startsWith('*') ||
33
+ value.startsWith('|') ||
34
+ value.startsWith('>') ||
35
+ value.startsWith('%') ||
36
+ value.startsWith('@') ||
37
+ value.startsWith('`') ||
38
+ /^(true|false|yes|no|on|off|null|~)$/i.test(value) ||
39
+ /^-?\d+(\.\d+)?$/.test(value) ||
40
+ /^0x[0-9a-fA-F]+$/.test(value) ||
41
+ /^0o[0-7]+$/.test(value);
42
+ if (needsQuotes) {
43
+ // Escape backslashes and double quotes for YAML double-quoted strings
44
+ const escaped = value.replace(/\\/g, '\\\\').replace(/"/g, '\\"').replace(/\n/g, '\\n');
45
+ return `${key}: "${escaped}"`;
46
+ }
47
+ return `${key}: ${value}`;
48
+ })
49
+ .join('\n') + '\n');
50
+ }
package/dist/types.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export type OutputFormat = 'dotenv' | 'wrangler' | 'json' | 'shell';
1
+ export type OutputFormat = 'dotenv' | 'wrangler' | 'json' | 'shell' | 'yaml';
2
2
  export type Environment = 'development' | 'production';
3
3
  export interface Target {
4
4
  name: string;
@@ -67,6 +67,11 @@ export interface CheckResult {
67
67
  status: 'ok' | 'drift' | 'error';
68
68
  files: CheckFileResult[];
69
69
  }
70
+ export interface SkillOptions {
71
+ root: string;
72
+ global?: boolean;
73
+ local?: boolean;
74
+ }
70
75
  export declare const DEFAULT_SOURCES: SourceFiles;
71
76
  export declare const FORMAT_OUTPUT_FILES: Record<OutputFormat, Record<Environment, string>>;
72
77
  //# sourceMappingURL=types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,YAAY,GAAG,QAAQ,GAAG,UAAU,GAAG,MAAM,GAAG,OAAO,CAAC;AACpE,MAAM,MAAM,WAAW,GAAG,aAAa,GAAG,YAAY,CAAC;AAEvD,MAAM,WAAW,MAAM;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,YAAY,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;CACpB;AAED,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,WAAW,CAAC;IACrB,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AAED,MAAM,WAAW,MAAM;IACrB,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,cAAc;IAC7B,GAAG,EAAE,WAAW,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,QAAQ,GAAG,aAAa,GAAG,YAAY,CAAC;CAChD;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,OAAO,CAAC;CACjB;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,WAAW,CAAC;CAClB;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,OAAO,CAAC;IACd,IAAI,EAAE,OAAO,CAAC;IACd,KAAK,EAAE,OAAO,CAAC;IACf,WAAW,EAAE,OAAO,CAAC;IACrB,aAAa,EAAE,OAAO,CAAC;CACxB;AAED,MAAM,MAAM,cAAc,GAAG,gBAAgB,GAAG,mBAAmB,GAAG,gBAAgB,GAAG,oBAAoB,CAAC;AAE9G,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,OAAO,CAAC;IAChB,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,KAAK,CAAC,EAAE,cAAc,CAAC;CACxB;AAED,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,IAAI,GAAG,OAAO,GAAG,OAAO,CAAC;IACjC,KAAK,EAAE,eAAe,EAAE,CAAC;CAC1B;AAED,eAAO,MAAM,eAAe,EAAE,WAI7B,CAAC;AAEF,eAAO,MAAM,mBAAmB,EAAE,MAAM,CAAC,YAAY,EAAE,MAAM,CAAC,WAAW,EAAE,MAAM,CAAC,CAiBjF,CAAC"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,YAAY,GAAG,QAAQ,GAAG,UAAU,GAAG,MAAM,GAAG,OAAO,GAAG,MAAM,CAAC;AAC7E,MAAM,MAAM,WAAW,GAAG,aAAa,GAAG,YAAY,CAAC;AAEvD,MAAM,WAAW,MAAM;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,YAAY,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;CACpB;AAED,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,WAAW,CAAC;IACrB,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AAED,MAAM,WAAW,MAAM;IACrB,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,cAAc;IAC7B,GAAG,EAAE,WAAW,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,QAAQ,GAAG,aAAa,GAAG,YAAY,CAAC;CAChD;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,OAAO,CAAC;CACjB;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,WAAW,CAAC;CAClB;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,OAAO,CAAC;IACd,IAAI,EAAE,OAAO,CAAC;IACd,KAAK,EAAE,OAAO,CAAC;IACf,WAAW,EAAE,OAAO,CAAC;IACrB,aAAa,EAAE,OAAO,CAAC;CACxB;AAED,MAAM,MAAM,cAAc,GAAG,gBAAgB,GAAG,mBAAmB,GAAG,gBAAgB,GAAG,oBAAoB,CAAC;AAE9G,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,OAAO,CAAC;IAChB,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,KAAK,CAAC,EAAE,cAAc,CAAC;CACxB;AAED,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,IAAI,GAAG,OAAO,GAAG,OAAO,CAAC;IACjC,KAAK,EAAE,eAAe,EAAE,CAAC;CAC1B;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED,eAAO,MAAM,eAAe,EAAE,WAI7B,CAAC;AAEF,eAAO,MAAM,mBAAmB,EAAE,MAAM,CAAC,YAAY,EAAE,MAAM,CAAC,WAAW,EAAE,MAAM,CAAC,CAqBjF,CAAC"}
package/dist/types.js CHANGED
@@ -20,4 +20,8 @@ export const FORMAT_OUTPUT_FILES = {
20
20
  development: '.env.development.sh',
21
21
  production: '.env.production.sh',
22
22
  },
23
+ yaml: {
24
+ development: '.env.development.yaml',
25
+ production: '.env.production.yaml',
26
+ },
23
27
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@chriscode/hush",
3
- "version": "2.0.0",
3
+ "version": "2.1.0",
4
4
  "description": "SOPS-based secrets management for monorepos. Encrypt once, decrypt everywhere.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -12,6 +12,14 @@
12
12
  "types": "./dist/index.d.ts"
13
13
  }
14
14
  },
15
+ "scripts": {
16
+ "build": "tsc",
17
+ "dev": "tsc --watch",
18
+ "test": "vitest run",
19
+ "test:watch": "vitest",
20
+ "prepublishOnly": "pnpm build && pnpm test",
21
+ "type-check": "tsc --noEmit"
22
+ },
15
23
  "keywords": [
16
24
  "secrets",
17
25
  "sops",
@@ -53,12 +61,5 @@
53
61
  ],
54
62
  "publishConfig": {
55
63
  "access": "public"
56
- },
57
- "scripts": {
58
- "build": "tsc",
59
- "dev": "tsc --watch",
60
- "test": "vitest run",
61
- "test:watch": "vitest",
62
- "type-check": "tsc --noEmit"
63
64
  }
64
- }
65
+ }
package/LICENSE DELETED
@@ -1,21 +0,0 @@
1
- MIT License
2
-
3
- Copyright (c) 2025 Chris Hasson
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.