@envsave/cli 1.0.44 → 1.0.46
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/README.md +16 -0
- package/dist/SKILL.md +171 -0
- package/dist/cli.js +106 -3
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -161,6 +161,17 @@ envsave list
|
|
|
161
161
|
# Delete a secret (password required)
|
|
162
162
|
envsave delete OPENAI_API_KEY
|
|
163
163
|
|
|
164
|
+
# Rename a secret (password required)
|
|
165
|
+
# Creates a new opaque key derived from the new name, copies the value over,
|
|
166
|
+
# and deletes the old opaque key. The output prints OLD → NEW for easy
|
|
167
|
+
# search-and-replace in your code.
|
|
168
|
+
envsave rename CLERK_SECRET_KEY_THEHOTELAGENTS CLERK_SECRET_KEY_DIRECTBOOKAI
|
|
169
|
+
# Output:
|
|
170
|
+
# Renamed: CLERK_SECRET_KEY_THEHOTELAGENTS → CLERK_SECRET_KEY_DIRECTBOOKAI
|
|
171
|
+
# Replace in your code:
|
|
172
|
+
# OLD (deleted): es_7cXxcbsVaJQv...
|
|
173
|
+
# NEW (created): es_enLaxEjzTO5r...
|
|
174
|
+
|
|
164
175
|
# Bulk import from .env file (password required)
|
|
165
176
|
envsave import .env
|
|
166
177
|
|
|
@@ -174,6 +185,11 @@ envsave passwd
|
|
|
174
185
|
envsave config # show current
|
|
175
186
|
envsave config --max-gets 120 # set max gets/minute
|
|
176
187
|
|
|
188
|
+
# Install the EnvSave Claude skill (prompts: local in project / global for user)
|
|
189
|
+
envsave install-skill # interactive
|
|
190
|
+
envsave install-skill --local # → ./.claude/skills/envsave/SKILL.md
|
|
191
|
+
envsave install-skill --global # → ~/.claude/skills/envsave/SKILL.md
|
|
192
|
+
|
|
177
193
|
# Show version
|
|
178
194
|
envsave --version
|
|
179
195
|
```
|
package/dist/SKILL.md
ADDED
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: envsave
|
|
3
|
+
description: Use EnvSave to securely retrieve secrets via opaque keys instead of hardcoding them. Trigger whenever code needs API keys, tokens, passwords, database URLs, connection strings, webhook secrets, credentials, environment variables, or any sensitive value. Also trigger when you see hardcoded secrets in existing code that should be migrated to EnvSave, when .env files are being read directly, or when the user mentions secrets, credentials, or vault.
|
|
4
|
+
user_invocable: true
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# EnvSave Migration & Usage Skill
|
|
8
|
+
|
|
9
|
+
When this skill is triggered, follow the instructions below. Everything you need to know is in this file. Do not research, explore, or investigate envsave — just execute the steps.
|
|
10
|
+
|
|
11
|
+
## Installation
|
|
12
|
+
|
|
13
|
+
EnvSave is installed from **npm** (not from a local path on the user's machine). The npm package is scoped: `@envsave/cli`.
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
# Global CLI (the `envsave` binary)
|
|
17
|
+
npm install -g @envsave/cli
|
|
18
|
+
|
|
19
|
+
# As a library in a project
|
|
20
|
+
npm install @envsave/cli
|
|
21
|
+
# or: bun add @envsave/cli
|
|
22
|
+
|
|
23
|
+
# Python
|
|
24
|
+
pip install envsave
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
If the user's project doesn't yet depend on envsave, add `@envsave/cli` to their dependencies before emitting `import` statements. Never tell the user to link from a local checkout — always use the npm package.
|
|
28
|
+
|
|
29
|
+
**Installing this skill in Claude Code:**
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
envsave install-skill # interactive — asks local or global
|
|
33
|
+
envsave install-skill --local # → ./.claude/skills/envsave/SKILL.md (this project only)
|
|
34
|
+
envsave install-skill --global # → ~/.claude/skills/envsave/SKILL.md (all your projects)
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
**Upgrading from the old unscoped package:** if `npm install -g @envsave/cli` fails with `EEXIST` on `bin/envsave`, the user has the old unscoped `envsave` package installed globally (pre-rename). Fix it with:
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
npm uninstall -g envsave
|
|
41
|
+
npm install -g @envsave/cli
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## If user asks to migrate / invokes `/envsave`:
|
|
45
|
+
|
|
46
|
+
Execute these steps in order. Start with Step 1 immediately.
|
|
47
|
+
|
|
48
|
+
### Step 1 — Grep the project NOW
|
|
49
|
+
|
|
50
|
+
Use the Grep tool (not Bash, not Agent, not Explore) to search the current project directory. Run ALL of these searches in parallel in a single response:
|
|
51
|
+
|
|
52
|
+
- `process\.env` in `*.{ts,js,tsx,jsx,mjs,cjs}`
|
|
53
|
+
- `os\.environ|os\.getenv|environ\.get` in `*.py`
|
|
54
|
+
- `dotenv|load_dotenv|from dotenv|python-dotenv` in all source files
|
|
55
|
+
- `sk-[a-zA-Z0-9]{20,}|pk_[a-zA-Z0-9]{20,}|ghp_|gho_|xoxb-|xoxp-|postgres://|mongodb://|redis://|mysql://|amqp://` in all files
|
|
56
|
+
- `readFileSync.*\.env|Bun\.file.*\.env|open.*\.env` in all files
|
|
57
|
+
- Glob for `.env*` files in project root
|
|
58
|
+
|
|
59
|
+
Skip `node_modules/`, `.git/`, `dist/`, `build/`, `__pycache__/`, `.venv/`, `bun.lockb`.
|
|
60
|
+
|
|
61
|
+
### Step 2 — Show findings table
|
|
62
|
+
|
|
63
|
+
| File:Line | Env Var | Current Code | Replacement |
|
|
64
|
+
|-----------|---------|-------------|-------------|
|
|
65
|
+
| src/api.ts:12 | OPENAI_API_KEY | `process.env.OPENAI_API_KEY` | `envsave.get("es_???")` |
|
|
66
|
+
| lib/db.py:5 | DATABASE_URL | `os.getenv("DATABASE_URL")` | `envsave.get("es_???")` |
|
|
67
|
+
|
|
68
|
+
Also list `dotenv`/`load_dotenv` imports to remove and `.env` files found.
|
|
69
|
+
|
|
70
|
+
### Step 3 — Summary + copy-paste command
|
|
71
|
+
|
|
72
|
+
List unique env var names, then output ONE single `envsave reveal` command with all keys (supports multiple args, one password prompt).
|
|
73
|
+
|
|
74
|
+
**IMPORTANT: The command MUST be on a single line with no line breaks.** If the list of keys is long, still keep it on one line. Never wrap or split the command across multiple lines — line breaks will break copy-paste in the terminal.
|
|
75
|
+
|
|
76
|
+
```
|
|
77
|
+
## Env Vars to Migrate
|
|
78
|
+
|
|
79
|
+
| # | Env Var Name | Files |
|
|
80
|
+
|---|-------------|-------|
|
|
81
|
+
| 1 | OPENAI_API_KEY | 3 |
|
|
82
|
+
| 2 | DATABASE_URL | 1 |
|
|
83
|
+
|
|
84
|
+
## Run this to get all opaque keys (single password prompt):
|
|
85
|
+
|
|
86
|
+
envsave reveal OPENAI_API_KEY DATABASE_URL
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Tell user: run the command, paste back the output, and I'll do all code replacements.
|
|
90
|
+
|
|
91
|
+
### Step 4 — Replace code (only after user provides keys)
|
|
92
|
+
|
|
93
|
+
Wait for opaque keys. Then:
|
|
94
|
+
1. Replace `process.env.XXX` / `os.getenv("XXX")` with `envsave.get("es_actual_key")`
|
|
95
|
+
2. Add `import envsave from "@envsave/cli"` (TS/JS) or `import envsave` (Python) if missing
|
|
96
|
+
3. Remove `dotenv`/`load_dotenv` imports and `.config()` calls
|
|
97
|
+
4. List all `.env*` files found (`.env`, `.env.local`, `.env.production`, etc.) and **tell the user they must delete them** — these files contain real secrets in plain text and are no longer needed after migration to envsave. Add them to `.gitignore` if not already there.
|
|
98
|
+
|
|
99
|
+
**IMPORTANT: Always use `envsave.get("es_...")` directly — the full module call. Never create wrapper functions like `envsave_get()`, `get_secret()`, `_get_env()`, or similar. The call should always be `envsave.get("es_...")` exactly.**
|
|
100
|
+
|
|
101
|
+
---
|
|
102
|
+
|
|
103
|
+
## If user needs a secret in new code:
|
|
104
|
+
|
|
105
|
+
Use `envsave.get("es_???")` as placeholder and ask user to provide the opaque key.
|
|
106
|
+
|
|
107
|
+
**TypeScript/JavaScript:**
|
|
108
|
+
```typescript
|
|
109
|
+
import envsave from "@envsave/cli";
|
|
110
|
+
const apiKey = envsave.get("es_7a3f2b1e9c8d4f6b2a1c3d5f");
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
**Python:**
|
|
114
|
+
```python
|
|
115
|
+
import envsave
|
|
116
|
+
api_key = envsave.get("es_7a3f2b1e9c8d4f6b2a1c3d5f")
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
**Shell:**
|
|
120
|
+
```bash
|
|
121
|
+
envsave get es_7a3f2b1e9c8d4f6b2a1c3d5f
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
## Vercel / Cloud Deployment
|
|
125
|
+
|
|
126
|
+
On Vercel there is no envsave vault. When `envsave.get("es_xxx")` can't find the vault, it falls back to `process.env["es_xxx"]`. So you need to set Vercel env vars where:
|
|
127
|
+
|
|
128
|
+
- **Name** = the opaque key (e.g. `es_7a3f2b1e9c8d4f6b`)
|
|
129
|
+
- **Value** = the real secret (e.g. `sk-proj-abc123...`)
|
|
130
|
+
|
|
131
|
+
This way the code `envsave.get("es_7a3f2b1e9c8d4f6b")` resolves via `process.env.es_7a3f2b1e9c8d4f6b` on Vercel, returning the real API key — without any vault needed.
|
|
132
|
+
|
|
133
|
+
**To push secrets to Vercel**, use the built-in `envsave vercel` command. The project must be linked to Vercel first (`vercel link`).
|
|
134
|
+
|
|
135
|
+
```bash
|
|
136
|
+
# Push specific secrets
|
|
137
|
+
envsave vercel OPENAI_API_KEY STRIPE_KEY
|
|
138
|
+
|
|
139
|
+
# Target specific environments (default: production,preview,development)
|
|
140
|
+
envsave vercel OPENAI_API_KEY --env production,preview
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
The command automatically:
|
|
144
|
+
- Checks the project is linked to Vercel (`.vercel/project.json`)
|
|
145
|
+
- Resolves each clean key name to its opaque key
|
|
146
|
+
- Sets the opaque key as the Vercel env var name, real secret as the value
|
|
147
|
+
- Removes existing vars before adding (handles updates)
|
|
148
|
+
- Pipes values via stdin — no shell escaping issues, no trailing newlines
|
|
149
|
+
|
|
150
|
+
**CRITICAL: Never write real secret values to any local file.** No `.env.local`, no `.env.production`, no temp files.
|
|
151
|
+
|
|
152
|
+
---
|
|
153
|
+
|
|
154
|
+
## Rules
|
|
155
|
+
|
|
156
|
+
1. Never hardcode raw secrets. Use `envsave.get()` with opaque key.
|
|
157
|
+
2. Never read .env files directly. Use EnvSave instead.
|
|
158
|
+
3. You cannot list or discover secrets. Ask the user for opaque keys.
|
|
159
|
+
4. Don't guess opaque keys. Ask user to run `envsave reveal <NAME>`.
|
|
160
|
+
5. Handle undefined returns — `envsave.get()` returns `undefined`/`None` if key doesn't exist.
|
|
161
|
+
6. **Never write real secret values to any local file** (no `.env.local`, `.env.production`, etc.). For cloud deployments (Vercel, etc.), pipe from `envsave get` directly to the platform CLI.
|
|
162
|
+
|
|
163
|
+
## Quick Reference
|
|
164
|
+
|
|
165
|
+
| Task | User Command |
|
|
166
|
+
|------|-------------|
|
|
167
|
+
| Add a secret | `envsave set <NAME> <VALUE>` |
|
|
168
|
+
| Get opaque key | `envsave reveal <NAME>` |
|
|
169
|
+
| Rename a secret | `envsave rename <OLD_NAME> <NEW_NAME>` (creates new opaque key, deletes old — output prints OLD → NEW for code search-and-replace) |
|
|
170
|
+
| Unlock vault | `envsave list` |
|
|
171
|
+
| Push to Vercel | `envsave vercel KEY1 KEY2` |
|
package/dist/cli.js
CHANGED
|
@@ -100,6 +100,7 @@ __export(exports_vault, {
|
|
|
100
100
|
searchKeys: () => searchKeys,
|
|
101
101
|
revealKeysByPattern: () => revealKeysByPattern,
|
|
102
102
|
revealKey: () => revealKey,
|
|
103
|
+
renameSecret: () => renameSecret,
|
|
103
104
|
listKeys: () => listKeys,
|
|
104
105
|
initVault: () => initVault,
|
|
105
106
|
importEnv: () => importEnv,
|
|
@@ -446,6 +447,28 @@ function deleteSecret(key, password) {
|
|
|
446
447
|
writeSessionCache(vault.secrets, vault.registry);
|
|
447
448
|
return true;
|
|
448
449
|
}
|
|
450
|
+
function renameSecret(oldName, newName, password) {
|
|
451
|
+
if (oldName === newName) {
|
|
452
|
+
throw new Error("Old and new names are the same.");
|
|
453
|
+
}
|
|
454
|
+
const vault = readMainVault(password);
|
|
455
|
+
const sshKeyContent = loadSshKey(vault);
|
|
456
|
+
const oldOpaque = computeOpaqueName(vault.hmacSeed, oldName, sshKeyContent);
|
|
457
|
+
if (!vault.registry.includes(oldName) || !(oldOpaque in vault.secrets)) {
|
|
458
|
+
throw new Error(`Secret '${oldName}' not found.`);
|
|
459
|
+
}
|
|
460
|
+
if (vault.registry.includes(newName)) {
|
|
461
|
+
throw new Error(`Secret '${newName}' already exists.`);
|
|
462
|
+
}
|
|
463
|
+
const newOpaque = computeOpaqueName(vault.hmacSeed, newName, sshKeyContent);
|
|
464
|
+
const value = vault.secrets[oldOpaque];
|
|
465
|
+
vault.secrets[newOpaque] = value;
|
|
466
|
+
delete vault.secrets[oldOpaque];
|
|
467
|
+
vault.registry = vault.registry.map((k) => k === oldName ? newName : k);
|
|
468
|
+
writeMainVault(vault, password);
|
|
469
|
+
writeSessionCache(vault.secrets, vault.registry);
|
|
470
|
+
return { oldOpaque, newOpaque };
|
|
471
|
+
}
|
|
449
472
|
function listKeys(password) {
|
|
450
473
|
const vault = readMainVault(password);
|
|
451
474
|
writeSessionCache(vault.secrets, vault.registry);
|
|
@@ -688,7 +711,8 @@ import { createInterface } from "node:readline";
|
|
|
688
711
|
import { existsSync as existsSync3, readFileSync as readFileSync5, writeFileSync as writeFileSync3, mkdirSync as mkdirSync3, readdirSync as readdirSync2, statSync as statSync2 } from "node:fs";
|
|
689
712
|
import { spawnSync } from "node:child_process";
|
|
690
713
|
import { createHash } from "node:crypto";
|
|
691
|
-
import { join as join4 } from "node:path";
|
|
714
|
+
import { dirname, join as join4 } from "node:path";
|
|
715
|
+
import { fileURLToPath } from "node:url";
|
|
692
716
|
import { homedir as homedir3 } from "node:os";
|
|
693
717
|
|
|
694
718
|
// src/license.ts
|
|
@@ -836,7 +860,7 @@ async function checkLicense() {
|
|
|
836
860
|
// package.json
|
|
837
861
|
var package_default = {
|
|
838
862
|
name: "@envsave/cli",
|
|
839
|
-
version: "1.0.
|
|
863
|
+
version: "1.0.46",
|
|
840
864
|
description: "Local secret vault for LLM-safe environments — encrypted secrets that agents can't bulk-discover",
|
|
841
865
|
type: "module",
|
|
842
866
|
main: "dist/index.js",
|
|
@@ -850,7 +874,7 @@ var package_default = {
|
|
|
850
874
|
],
|
|
851
875
|
scripts: {
|
|
852
876
|
prebuild: `bun -e "const p=require('./package.json');const v=p.version.split('.');v[2]=+v[2]+1;p.version=v.join('.');require('fs').writeFileSync('package.json',JSON.stringify(p,null,2)+'\\n')"`,
|
|
853
|
-
build: "bun build src/cli.ts --outdir dist --target node --format esm && bun build src/index.ts --outdir dist --target node --format esm --outfile dist/index.js",
|
|
877
|
+
build: "bun build src/cli.ts --outdir dist --target node --format esm && bun build src/index.ts --outdir dist --target node --format esm --outfile dist/index.js && cp .claude/skills/envsave/SKILL.md dist/SKILL.md",
|
|
854
878
|
dev: "bun run src/cli.ts",
|
|
855
879
|
webapp: "bun run webapp/server.ts",
|
|
856
880
|
"lint:claude": "claude -p 'you are a linter. please look at the changes vs. main and report any issues related to typos. report the filename and line number on one line, and a description of the issue on the second line. do not return any other text.'"
|
|
@@ -922,6 +946,7 @@ Usage:
|
|
|
922
946
|
envsave show <key> Show real value by key name (password required)
|
|
923
947
|
envsave reveal <keyword> [keyword2]... Show opaque key(s) for any name matching keyword (password required)
|
|
924
948
|
envsave delete <key> Remove a secret (password required)
|
|
949
|
+
envsave rename <old> <new> Rename a secret (creates new opaque key, deletes old) (password required)
|
|
925
950
|
envsave search <keyword> Search keys matching keyword (no password)
|
|
926
951
|
envsave scan [dir] Scan project for env variable usage
|
|
927
952
|
envsave passwd Change master password
|
|
@@ -934,6 +959,7 @@ Usage:
|
|
|
934
959
|
envsave restore Download vault backup from server
|
|
935
960
|
envsave sync Sync vault with server (pull if cloud newer)
|
|
936
961
|
envsave vercel <KEY|es_key>... Push secrets to Vercel under both clean name + opaque id
|
|
962
|
+
envsave install-skill [--local|--global] Install the EnvSave Claude skill (prompts if no flag)
|
|
937
963
|
envsave help Show this help
|
|
938
964
|
|
|
939
965
|
Security model:
|
|
@@ -1168,6 +1194,28 @@ Use 'envsave reveal <KEY>' to get the opaque key for each secret.`);
|
|
|
1168
1194
|
}
|
|
1169
1195
|
break;
|
|
1170
1196
|
}
|
|
1197
|
+
case "rename": {
|
|
1198
|
+
const oldName = args[1];
|
|
1199
|
+
const newName = args[2];
|
|
1200
|
+
if (!oldName || !newName) {
|
|
1201
|
+
console.error("Usage: envsave rename <old> <new>");
|
|
1202
|
+
process.exit(1);
|
|
1203
|
+
}
|
|
1204
|
+
const password = await requirePassword();
|
|
1205
|
+
const { oldOpaque, newOpaque } = renameSecret(oldName, newName, password);
|
|
1206
|
+
console.log(`
|
|
1207
|
+
Renamed: ${oldName} → ${newName}`);
|
|
1208
|
+
console.log(`
|
|
1209
|
+
Replace in your code:`);
|
|
1210
|
+
console.log(` OLD (deleted): ${oldOpaque}`);
|
|
1211
|
+
console.log(` NEW (created): ${newOpaque}`);
|
|
1212
|
+
console.log(`
|
|
1213
|
+
Or replace the full call:`);
|
|
1214
|
+
console.log(` envsave.get("${oldOpaque}")`);
|
|
1215
|
+
console.log(` envsave.get("${newOpaque}")
|
|
1216
|
+
`);
|
|
1217
|
+
break;
|
|
1218
|
+
}
|
|
1171
1219
|
case "config": {
|
|
1172
1220
|
const maxGets = getFlag("--max-gets");
|
|
1173
1221
|
if (!maxGets) {
|
|
@@ -1601,6 +1649,61 @@ Done: ${success} pushed, ${failed} failed.`);
|
|
|
1601
1649
|
process.exit(1);
|
|
1602
1650
|
break;
|
|
1603
1651
|
}
|
|
1652
|
+
case "install-skill": {
|
|
1653
|
+
const cliDir = dirname(fileURLToPath(import.meta.url));
|
|
1654
|
+
const candidates = [
|
|
1655
|
+
join4(cliDir, "SKILL.md"),
|
|
1656
|
+
join4(cliDir, "..", ".claude", "skills", "envsave", "SKILL.md")
|
|
1657
|
+
];
|
|
1658
|
+
const skillSource = candidates.find((p) => existsSync3(p));
|
|
1659
|
+
if (!skillSource) {
|
|
1660
|
+
console.error("Skill source SKILL.md not found in the package.");
|
|
1661
|
+
process.exit(1);
|
|
1662
|
+
}
|
|
1663
|
+
let scope;
|
|
1664
|
+
if (args.includes("--local"))
|
|
1665
|
+
scope = "local";
|
|
1666
|
+
else if (args.includes("--global"))
|
|
1667
|
+
scope = "global";
|
|
1668
|
+
if (!scope) {
|
|
1669
|
+
const rl = createInterface({ input: process.stdin, output: process.stderr });
|
|
1670
|
+
const answer = await new Promise((resolve) => {
|
|
1671
|
+
rl.question("Install EnvSave skill (l)ocally in this project or (g)lobally for your user? [l/g] ", resolve);
|
|
1672
|
+
});
|
|
1673
|
+
rl.close();
|
|
1674
|
+
const a = answer.trim().toLowerCase();
|
|
1675
|
+
if (a === "l" || a === "local")
|
|
1676
|
+
scope = "local";
|
|
1677
|
+
else if (a === "g" || a === "global")
|
|
1678
|
+
scope = "global";
|
|
1679
|
+
else {
|
|
1680
|
+
console.error("Cancelled — answer must be 'l' or 'g'.");
|
|
1681
|
+
process.exit(1);
|
|
1682
|
+
}
|
|
1683
|
+
}
|
|
1684
|
+
const targetDir = scope === "local" ? join4(process.cwd(), ".claude", "skills", "envsave") : join4(homedir3(), ".claude", "skills", "envsave");
|
|
1685
|
+
const targetFile = join4(targetDir, "SKILL.md");
|
|
1686
|
+
if (existsSync3(targetFile)) {
|
|
1687
|
+
const rl = createInterface({ input: process.stdin, output: process.stderr });
|
|
1688
|
+
const answer = await new Promise((resolve) => {
|
|
1689
|
+
rl.question(`Skill already exists at ${targetFile}. Overwrite? (y/N) `, resolve);
|
|
1690
|
+
});
|
|
1691
|
+
rl.close();
|
|
1692
|
+
if (answer.trim().toLowerCase() !== "y") {
|
|
1693
|
+
console.log("Install cancelled.");
|
|
1694
|
+
break;
|
|
1695
|
+
}
|
|
1696
|
+
}
|
|
1697
|
+
mkdirSync3(targetDir, { recursive: true });
|
|
1698
|
+
writeFileSync3(targetFile, readFileSync5(skillSource));
|
|
1699
|
+
console.log(`
|
|
1700
|
+
✓ Installed EnvSave skill ${scope === "local" ? "locally" : "globally"}`);
|
|
1701
|
+
console.log(` ${targetFile}
|
|
1702
|
+
`);
|
|
1703
|
+
console.log(` Restart Claude Code (or open the project) and invoke /envsave to use it.
|
|
1704
|
+
`);
|
|
1705
|
+
break;
|
|
1706
|
+
}
|
|
1604
1707
|
default:
|
|
1605
1708
|
console.error(`Unknown command: ${command}`);
|
|
1606
1709
|
usage();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@envsave/cli",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.46",
|
|
4
4
|
"description": "Local secret vault for LLM-safe environments — encrypted secrets that agents can't bulk-discover",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
],
|
|
15
15
|
"scripts": {
|
|
16
16
|
"prebuild": "bun -e \"const p=require('./package.json');const v=p.version.split('.');v[2]=+v[2]+1;p.version=v.join('.');require('fs').writeFileSync('package.json',JSON.stringify(p,null,2)+'\\n')\"",
|
|
17
|
-
"build": "bun build src/cli.ts --outdir dist --target node --format esm && bun build src/index.ts --outdir dist --target node --format esm --outfile dist/index.js",
|
|
17
|
+
"build": "bun build src/cli.ts --outdir dist --target node --format esm && bun build src/index.ts --outdir dist --target node --format esm --outfile dist/index.js && cp .claude/skills/envsave/SKILL.md dist/SKILL.md",
|
|
18
18
|
"dev": "bun run src/cli.ts",
|
|
19
19
|
"webapp": "bun run webapp/server.ts",
|
|
20
20
|
"lint:claude": "claude -p 'you are a linter. please look at the changes vs. main and report any issues related to typos. report the filename and line number on one line, and a description of the issue on the second line. do not return any other text.'"
|