enve-doctor 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Abdul-Qudus Rufai
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.
package/README.md ADDED
@@ -0,0 +1,228 @@
1
+ # enve
2
+
3
+ > Environment Variable Doctor — scan, validate, and protect your `.env` files.
4
+
5
+ [![npm version](https://img.shields.io/npm/v/enve-doctor.svg)](https://www.npmjs.com/package/enve-doctor)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
7
+
8
+ `enve` is a developer-friendly CLI that continuously monitors your projects for environment-variable mistakes: missing variables, unused secrets, values that don't match expected formats, `.env` files not in `.gitignore`, and more. It also ships an interactive terminal dashboard so you can see the health of every tracked project at a glance.
9
+
10
+ ---
11
+
12
+ ## Table of Contents
13
+
14
+ - [Why enve?](#why-enve)
15
+ - [Installation](#installation)
16
+ - [Quick Start](#quick-start)
17
+ - [Commands](#commands)
18
+ - [`enve scan`](#enve-scan)
19
+ - [`enve doctor`](#enve-doctor)
20
+ - [`enve unused`](#enve-unused)
21
+ - [`enve missing`](#enve-missing)
22
+ - [`enve validate`](#enve-validate)
23
+ - [`enve generate-example`](#enve-generate-example)
24
+ - [`enve hook`](#enve-hook)
25
+ - [`enve dashboard`](#enve-dashboard)
26
+ - [How the Score Works](#how-the-score-works)
27
+ - [Tracking Behavior](#tracking-behavior)
28
+ - [Contributing](#contributing)
29
+
30
+ ---
31
+
32
+ ## Why enve?
33
+
34
+ Environment variables are easy to get wrong:
35
+
36
+ - You reference `process.env.API_KEY` but forget to add it to `.env`.
37
+ - You leave an old secret in `.env` after rotating it.
38
+ - You commit a `.env` file because `.gitignore` was missing.
39
+ - Your `.env.example` drifts out of sync with `.env`.
40
+
41
+ `enve` catches these issues before they become production incidents.
42
+
43
+ ---
44
+
45
+ ## Installation
46
+
47
+ ```bash
48
+ npm install -g enve-doctor
49
+ ```
50
+
51
+ Requires Node.js 18 or later.
52
+
53
+ ---
54
+
55
+ ## Quick Start
56
+
57
+ ```bash
58
+ # Navigate to any project with a package.json
59
+ cd my-project
60
+
61
+ # Run a full health check
62
+ enve doctor
63
+
64
+ # Or scan for a quick overview
65
+ enve scan
66
+ ```
67
+
68
+ `enve` automatically detects the project root by walking up from the current directory until it finds a `package.json`.
69
+
70
+ ---
71
+
72
+ ## Commands
73
+
74
+ ### `enve scan`
75
+
76
+ Fast overview of env-file health.
77
+
78
+ ```bash
79
+ enve scan
80
+ ```
81
+
82
+ Output includes `.env` files found, code references, issues, and a score.
83
+
84
+ ### `enve doctor`
85
+
86
+ Comprehensive health check with detailed recommendations.
87
+
88
+ ```bash
89
+ enve doctor
90
+ ```
91
+
92
+ Sections: File Structure, Variables, Security, Validation, and prioritized Recommendations.
93
+
94
+ ### `enve unused`
95
+
96
+ Find variables defined in `.env` but never referenced in code.
97
+
98
+ ```bash
99
+ enve unused
100
+ enve unused --fix # Remove unused variables after confirmation
101
+ ```
102
+
103
+ Standard variables like `PORT`, `HOST`, `NODE_ENV`, `CI`, and `PWD` are ignored.
104
+
105
+ ### `enve missing`
106
+
107
+ Find variables referenced in code but missing from `.env`.
108
+
109
+ ```bash
110
+ enve missing
111
+ enve missing --add # Add placeholders after confirmation
112
+ ```
113
+
114
+ Variables that have a fallback default in code are reported as warnings; those without are errors.
115
+
116
+ ### `enve validate`
117
+
118
+ Validate values against expected formats.
119
+
120
+ ```bash
121
+ enve validate
122
+ enve validate --fix # Auto-fix simple issues after confirmation
123
+ ```
124
+
125
+ Checks include:
126
+
127
+ - `PORT` and `*_PORT` — must be a valid port number.
128
+ - `NODE_ENV` — must be `development`, `production`, or `test`.
129
+ - `*_URL`, `DATABASE_URL`, `REDIS_URL` — must be a valid URL.
130
+ - `SECRET`, `KEY`, `TOKEN`, `PRIVATE` — warns if too short or weak.
131
+ - `DEBUG`, `VERBOSE`, `*_ENABLED` — must be a boolean-like value.
132
+
133
+ ### `enve generate-example`
134
+
135
+ Create or update `.env.example` from `.env`.
136
+
137
+ ```bash
138
+ enve generate-example
139
+ enve generate-example --overwrite
140
+ enve generate-example --dry-run
141
+ enve generate-example --all-secrets
142
+ ```
143
+
144
+ Secrets are redacted to `your_variable_name` placeholders. Public values are copied as-is.
145
+
146
+ ### `enve hook`
147
+
148
+ Install a Git pre-commit hook that runs `enve doctor` before each commit.
149
+
150
+ ```bash
151
+ enve hook install
152
+ enve hook uninstall
153
+ ```
154
+
155
+ ### `enve dashboard`
156
+
157
+ Launch the interactive terminal dashboard.
158
+
159
+ ```bash
160
+ enve dashboard
161
+ ```
162
+
163
+ Keyboard shortcuts:
164
+
165
+ | Key | Action |
166
+ | --- | --- |
167
+ | `↑` / `↓` | Navigate projects |
168
+ | `Enter` | Run `enve doctor` for selected project |
169
+ | `r` | Refresh all projects |
170
+ | `q` / `Ctrl+C` | Quit |
171
+
172
+ ---
173
+
174
+ ## How the Score Works
175
+
176
+ Every project gets a score from 0 to 100 and a grade:
177
+
178
+ | Score | Grade | Meaning |
179
+ | --- | --- | --- |
180
+ | 90–100 | Excellent | Clean env setup |
181
+ | 70–89 | Good | Minor issues |
182
+ | 50–69 | Needs attention | Several issues to fix |
183
+ | 0–49 | Critical | Major problems |
184
+
185
+ Score deductions include:
186
+
187
+ - Missing variable without fallback: `-10`
188
+ - Missing variable with fallback: `-3`
189
+ - Secret in non-local `.env`: `-8`
190
+ - `.env` not in `.gitignore`: `-20`
191
+ - `.env.production` not in `.gitignore`: `-25`
192
+ - Unused variable: up to `-15` total
193
+ - Missing `.env.example`: `-10`
194
+ - Variable in `.env` but not `.env.example`: up to `-10` total
195
+
196
+ ---
197
+
198
+ ## Tracking Behavior
199
+
200
+ `enve` keeps a lightweight local config (using [`conf`](https://github.com/sindresorhus/conf)) so the dashboard can show all your projects. A project is added to the tracked list the first time you run `enve scan`, `enve doctor`, or any command that calls `getProjectInfo`. No data leaves your machine.
201
+
202
+ ---
203
+
204
+ ## Contributing
205
+
206
+ ```bash
207
+ # Clone and install
208
+ git clone https://github.com/your-username/enve.git
209
+ cd enve
210
+ npm install
211
+
212
+ # Run tests
213
+ npm test
214
+
215
+ # Run in development
216
+ npm run dev
217
+
218
+ # Build the CLI
219
+ npm run build
220
+ ```
221
+
222
+ Pull requests are welcome. Please add tests for new behavior and run `npm test` before submitting.
223
+
224
+ ---
225
+
226
+ ## License
227
+
228
+ MIT © Abdul-Qudus Rufai
package/dist/cli.mjs ADDED
@@ -0,0 +1,59 @@
1
+ #!/usr/bin/env node
2
+ import{Command as Ft}from"commander";import{Command as ot}from"commander";import V from"chalk";import{join as Ae,basename as et}from"path";import{readFile as go,writeFile as po,access as vo,mkdir as yo}from"fs/promises";import{dirname as ho}from"path";async function k(e){try{return await go(e,"utf-8")}catch{return}}async function C(e,o){await xo(ho(e)),await po(e,o,"utf-8")}async function L(e){try{return await vo(e),!0}catch{return!1}}async function xo(e){if(e)try{await yo(e,{recursive:!0})}catch{}}import{join as ce}from"path";var q="# === Enve Pre-Commit Hook ===",le="# === End Enve Hook ===";async function M(e){return L(ce(e,".git"))}async function bo(e){let o=ce(e,".gitignore");return k(o)}function ko(e,o){return e.split(`
3
+ `).some(n=>{let i=n.trim();return i===o||i===`/${o}`})}async function xe(e,o){let t=await bo(e);return t?ko(t,o):!1}function _(e){return ce(e,".git","hooks","pre-commit")}function wo(){return`#!/bin/bash
4
+ ${q}
5
+ # Blocks commits of .env files that may contain secrets
6
+ # Installed by: enve hook install
7
+ # Remove with: enve hook uninstall
8
+
9
+ BLOCKED_PATTERNS='^\\.env(\\.[^/]*)?$'
10
+ STAGED_ENV_FILES=$(git diff --cached --name-only | grep -E "$BLOCKED_PATTERNS" || true)
11
+
12
+ if [ -n "$STAGED_ENV_FILES" ]; then
13
+ echo ""
14
+ echo " \u2717 COMMIT BLOCKED by Enve"
15
+ echo ""
16
+ echo " You are attempting to commit environment files:"
17
+ echo "$STAGED_ENV_FILES" | sed 's/^/ /'
18
+ echo ""
19
+ echo " These files may contain secrets (API keys, passwords, tokens)."
20
+ echo ""
21
+ echo " What to do instead:"
22
+ echo " 1. Add them to .gitignore: echo '.env.local' >> .gitignore"
23
+ echo " 2. Use .env.example for templates: enve generate-example"
24
+ echo " 3. Share secrets through a secure channel (1Password, etc.)"
25
+ echo ""
26
+ echo " If you are CERTAIN these files are safe to commit:"
27
+ echo " git commit --no-verify"
28
+ echo ""
29
+ exit 1
30
+ fi
31
+ ${le}
32
+ `}async function J(e){let o=_(e);return await L(o)?(await k(o))?.includes(q)??!1:!1}async function be(e){let o=_(e),t=await k(o)??"";if(t.includes(q))return;let n=`${wo()}
33
+ ${t}`;await C(o,n)}async function ke(e){let o=_(e);if(!await L(o))return!1;let t=await k(o);if(!t||!t.includes(q))return!1;let n=t.indexOf(q),i=t.indexOf(le,n);if(n===-1||i===-1)return!1;let r=t.slice(0,n),s=t.slice(i+le.length),l=`${r.trimEnd()}
34
+ ${s.trimStart()}`.trim();return l?await C(o,`${l}
35
+ `):await C(o,""),!0}import{join as Eo}from"path";import{readFileSync as $o}from"fs";function ue(e){let o;try{o=$o(e,"utf-8")}catch{return[]}if(!o)return[];let t=[],n=o.split(`
36
+ `);for(let i=0;i<n.length;i++){let s=n[i].trim();if(!s||s.startsWith("#"))continue;let l=s.indexOf("=");if(l===-1)continue;let f=s.slice(0,l).trim(),m=s.slice(l+1);if(f.startsWith("export ")&&(f=f.slice(7).trim()),!f)continue;let{value:p,comment:v}=Po(m),d=Co(f,p);t.push({key:f,value:p,line:i+1,source:e,isSecret:d,comment:v})}return t}function Po(e){let o=e.trimStart();if(o.startsWith('"')){let{value:i,endIndex:r}=we(o,'"'),s=o.slice(r+1).trim(),l=Ee(s);return{value:i,comment:l}}if(o.startsWith("'")){let{value:i,endIndex:r}=we(o,"'"),s=o.slice(r+1).trim(),l=Ee(s);return{value:i,comment:l}}let t=e.trimEnd(),n=To(t);return n!==-1?{value:t.slice(0,n).trimEnd(),comment:t.slice(n+1).trim()}:{value:e.trimEnd()}}function we(e,o){let t="",n=!1;for(let i=1;i<e.length;i++){let r=e[i];if(n){r==="n"?t+=`
37
+ `:r==="t"?t+=" ":r===o?t+=o:t+=`\\${r}`,n=!1;continue}if(r==="\\"){n=!0;continue}if(r===o)return{value:t,endIndex:i};t+=r}return{value:t,endIndex:e.length-1}}function Ee(e){let o=e.indexOf("#");if(o!==-1)return e.slice(o+1).trim()}function To(e){for(let o=0;o<e.length;o++)if(e[o]==="#"&&(o===0||e[o-1]===" "))return o;return-1}function Co(e,o){return!!(/SECRET|KEY|TOKEN|PASSWORD|PRIVATE|ACCESS|CREDENTIAL/i.test(e)||o.length>20&&/[A-Z]/.test(o)&&/[a-z]/.test(o)&&/\d/.test(o))}async function Io(e){let o=[".env",".env.local",".env.development",".env.production",".env.test",".env.example"],t=[];for(let n of o){let i=Eo(e,n);await k(i)!==void 0&&t.push({path:i,filename:n,variables:ue(i)})}return t}async function D(e){return Io(e)}import So from"fast-glob";import{readFile as jo}from"fs/promises";import{Parser as Fo}from"acorn";import{fullAncestor as Do}from"acorn-walk";var Ao=["src/**/*.{js,ts,jsx,tsx}","lib/**/*.{js,ts}","pages/**/*.{js,ts,jsx,tsx}","app/**/*.{js,ts,jsx,tsx}"],$e=["**/node_modules/**","**/dist/**","**/build/**","**/.next/**","**/*.test.{js,ts}","**/*.spec.{js,ts}"];async function U(e,o={}){let t=o.include??Ao,n=o.exclude??(o.checkTests?$e.slice(0,-2):$e),i=await So(t,{cwd:e,ignore:n,absolute:!0}),r=[];for(let s of i){let l=await Bo(s);r.push(...l)}return r}async function Bo(e){let o=[],t=await jo(e,"utf-8"),n;try{n=Fo.parse(t,{ecmaVersion:"latest",sourceType:"module",allowReturnOutsideFunction:!0,allowImportExportEverywhere:!0,locations:!0})}catch{return o}return Do(n,(i,r,s)=>{if(i.type==="MemberExpression"){let l=No(i,s,e,t);l&&o.push(l)}if(i.type==="ObjectPattern"){let l=Ce(i,s,e,t);o.push(...l)}}),o}function No(e,o,t,n){let i=Vo(e);if(!i)return;let r=o.length>1?o[o.length-2]:void 0,s=Oo(e,r);return Z(i,t,e,n,s)}function Vo(e){if(e.object?.type==="MemberExpression"&&e.object.object?.name==="process"&&e.object.property?.name==="env"){if(e.property?.type==="Identifier"&&!e.computed)return e.property.name;if(e.property?.type==="Literal"&&typeof e.property.value=="string")return e.property.value;if(e.property?.type==="TemplateLiteral"&&e.property.quasis?.length===1)return e.property.quasis[0].value.cooked??e.property.quasis[0].value.raw}if(e.object?.type==="MemberExpression"&&e.object.object?.type==="MetaProperty"&&e.object.object.meta?.name==="import"&&e.object.object.property?.name==="meta"&&e.object.property?.name==="env"&&!e.computed){if(e.property?.type==="Identifier")return e.property.name;if(e.property?.type==="Literal"&&typeof e.property.value=="string")return e.property.value}}function Ce(e,o,t,n){if(!Ie(e,o))return[];let i=[];for(let r of e.properties){if(r.type==="RestElement"&&r.argument?.type==="Identifier"){i.push(Z(r.argument.name,t,e,n,{hasFallback:!1}));continue}if(r.type==="Property"){if(r.value?.type==="Identifier"&&i.push(Z(r.value.name,t,r.value,n,{hasFallback:!1})),r.value?.type==="AssignmentPattern"&&r.value.left?.type==="Identifier"){let s=r.value.left.name,l=G(r.value.right);i.push(Z(s,t,r.value.left,n,{hasFallback:!0,fallbackValue:l,fallbackType:"literal"}))}if(r.value?.type==="ObjectPattern"&&r.key?.name==="env"){let s=Ce(r.value,[...o,r,r.value],t,n);i.push(...s)}}}return i}function Ie(e,o){let t=o.length>1?o[o.length-2]:void 0;if(!t)return!1;if(t.type==="VariableDeclarator"&&t.id===e)return Te(t.init)||Pe(t.init);if(t.type==="AssignmentPattern"&&t.left===e)return Te(t.right)||Pe(t.right);if(t.type==="Property"&&t.value===e&&t.key?.name==="env"){let n=o.length>2?o[o.length-3]:void 0;if(n?.type==="ObjectPattern")return Ie(n,o.slice(0,-2))}return!1}function Pe(e){return e?.type==="Identifier"&&e.name==="process"}function Te(e){return e?e.type==="MemberExpression"&&e.object?.type==="Identifier"&&e.object.name==="process"&&e.property?.name==="env":!1}function Oo(e,o){if(!o)return{hasFallback:!1};if(o.type==="LogicalExpression"){let t=o.left===e,n=o.right===e;if(o.operator==="||"||o.operator==="??"){let i=t?o.right:n?o.left:void 0;if(i)return{hasFallback:!0,fallbackValue:G(i),fallbackType:Ro(i)}}if(o.operator==="&&"&&t)return{hasFallback:!0,fallbackValue:"guarded",fallbackType:"expression"}}return o.type==="ConditionalExpression"&&o.test===e?{hasFallback:!0,fallbackValue:G(o.alternate),fallbackType:"ternary"}:{hasFallback:!1}}function Ro(e){return e.type==="Literal"||e.type==="TemplateLiteral"?"literal":e.type==="CallExpression"||e.type==="NewExpression"?"function":"expression"}function G(e){return e?e.type==="Literal"?typeof e.value=="string"?`'${e.value}'`:String(e.value):e.type==="Identifier"?e.name:e.type==="TemplateLiteral"?e.quasis.length===1?`'${e.quasis[0].value.cooked??e.quasis[0].value.raw}'`:"`template`":e.type==="CallExpression"||e.type==="NewExpression"?`<${e.callee?.name??e.callee?.property?.name??"function"}()>`:e.type==="UnaryExpression"?`${e.operator}${G(e.argument)}`:e.type==="BinaryExpression"?`${G(e.left)} ${e.operator} ${G(e.right)}`:e.type==="MemberExpression"?e.property?.name??"member":"<expression>":"unknown"}function Z(e,o,t,n,i){let r=t.loc?.start?.line??0,s=t.loc?.start?.column??0,l=Lo(n,r);return{key:e,file:o,line:r,column:s,context:l,hasFallback:i.hasFallback,fallbackValue:i.fallbackValue,fallbackType:i.fallbackType}}function Lo(e,o){let t=e.split(`
38
+ `),n=Math.max(0,o-2),i=Math.min(t.length,o+1);return t.slice(n,i).join(`
39
+ `)}var Mo=["PORT","HOST","NODE_ENV","CI","PWD"];async function Se(e,o,t){let n=[];return n.push(...await _o(e,o)),n.push(...Go(e,o)),n.push(...Uo(e)),n.push(...await Wo(e,t)),n.push(...Ho(e)),n}async function _o(e,o){let t=new Set(e.flatMap(i=>i.variables.map(r=>r.key))),n=[];for(let i of o)t.has(i.key)||(i.hasFallback?n.push({type:"missing",severity:"warning",key:i.key,message:`${i.key} is not defined in .env but has a fallback default: ${i.fallbackValue??"unknown"}`,file:i.file,line:i.line,suggestion:`Consider adding ${i.key} to .env for explicit configuration, or keep the fallback`,hasFallback:!0,fallbackValue:i.fallbackValue}):n.push({type:"missing",severity:"error",key:i.key,message:`${i.key} referenced in ${i.file}:${i.line} but not defined in any .env file (no fallback)`,file:i.file,line:i.line,suggestion:`Add ${i.key} to .env and .env.example`}));return n}function Go(e,o){let t=new Set(o.map(i=>i.key)),n=[];for(let i of e)if(!(i.filename===".env.example"||i.filename===".env.test"))for(let r of i.variables)Mo.includes(r.key)||t.has(r.key)||n.push({type:"unused",severity:"warning",key:r.key,message:`${r.key} is defined in ${i.filename} but never used in code`,file:i.filename,line:r.line,suggestion:"Remove if no longer needed, or use it in your code"});return n}function Uo(e){let o=[];for(let t of e)if(t.filename===".env")for(let n of t.variables)n.isSecret&&o.push({type:"secret-risk",severity:"error",key:n.key,message:`${n.key} looks like a secret but is in .env instead of .env.local`,file:t.filename,line:n.line,suggestion:`Move ${n.key} to .env.local (which should be in .gitignore)`});return o}async function Wo(e,o){let t=[],n=e.map(l=>l.filename),i=[".env",".env.local",".env.production"],r={".env":".env is not in .gitignore \u2014 risk of committing secrets",".env.local":".env.local is not in .gitignore",".env.production":".env.production is not in .gitignore \u2014 production secrets at risk"},s={".env":"echo '.env' >> .gitignore",".env.local":"echo '.env.local' >> .gitignore",".env.production":"echo '.env.production' >> .gitignore"};for(let l of i){if(!n.includes(l))continue;await xe(o,l)||t.push({type:"gitignore",severity:"error",key:l,message:r[l],file:".gitignore",suggestion:s[l]})}return t}function Ho(e){let o=[],t=e.find(r=>r.filename===".env.example"),n=e.find(r=>r.filename===".env");if(!t)return o.push({type:"suspicious",severity:"warning",key:".env.example",message:".env.example is missing \u2014 new developers won't know what env vars are needed",file:".",suggestion:"Run `enve generate-example` to create one"}),o;if(!n)return o;let i=new Set(t.variables.map(r=>r.key));for(let r of n.variables)i.has(r.key)||o.push({type:"suspicious",severity:"warning",key:r.key,message:`${r.key} is in .env but missing from .env.example`,file:".env.example",suggestion:`Add ${r.key}=your_${r.key} to .env.example`});return o}function je(e){let o=100,t=0,n=0,i=0,r=0,s=0;for(let l of e){switch(l.severity){case"error":t++;break;case"warning":n++;break;case"info":i++;break}switch(l.type){case"missing":o-=l.hasFallback?3:10;break;case"secret-risk":o-=8;break;case"gitignore":l.key===".env.production"?o-=25:o-=20;break;case"syntax-error":o-=15;break;case"invalid":o-=5;break;case"unused":r=Math.min(r+3,15);break;case"suspicious":l.key===".env.example"?o-=10:s=Math.min(s+2,10);break}}return o-=r+s,o=Math.max(0,Math.min(100,o)),{score:o,grade:Ko(o),totalIssues:e.length,errorCount:t,warningCount:n,infoCount:i}}function Ko(e){return e>=90?"Excellent":e>=70?"Good":e>=50?"Needs attention":"Critical"}import Yo from"conf";import{readFile as qo}from"fs/promises";import{join as Xo}from"path";var zo={version:1,projects:[]};function Fe(){return new Yo({projectName:"enve",defaults:zo})}function ee(){return Fe().get("projects",[])}function Qo(e){return ee().some(o=>o.path===e)}function Jo(e){return ee().find(o=>o.path===e)}async function De(e){let o=Fe();if(Qo(e))return Jo(e);let n={name:await Zo(e),path:e,addedAt:new Date().toISOString()},i=o.get("projects",[]);return o.set("projects",[...i,n]),n}async function Zo(e){try{let o=await qo(Xo(e,"package.json"),"utf-8"),t=JSON.parse(o);if(t.name)return t.name}catch{}return e.split("/").pop()??"unknown"}async function w(e){let o=e;for(;;){if(await L(Ae(o,"package.json")))return o;let t=Ae(o,"..");if(t===o)break;o=t}}async function W(e){let[o,t,n,i]=await Promise.all([D(e),U(e),M(e),J(e)]),r=await Se(o,t,e),s=je(r),l=et(e);return await De(e),{name:l,path:e,envFiles:o,envCount:o.reduce((f,m)=>f+m.variables.length,0),referenceCount:t.length,issues:r,score:s,hasGit:n,hookInstalled:i,hasEnvInGitignore:!r.some(f=>f.type==="gitignore"),lastModified:new Date}}import oe from"chalk";var te={error:"\u2717",warning:"\u26A0",success:"\u2713",info:"\u2139",bullet:"\u2022",arrow:"\u2192",pointer:"\u25B8"};function y(e){return`${oe.red(te.error)} ${e}`}function I(e){return`${oe.yellow(te.warning)} ${e}`}function x(e){return`${oe.green(te.success)} ${e}`}function X(e){return` ${te.bullet} ${e}`}function a(e,o=1){return" ".repeat(o)+e}function E(e){return oe.bold(e)}var Be=new ot("scan").description("Quick overview of the current project's environment variable health").action(async()=>{let e=process.cwd(),o=await w(e);o||(console.log(y("No package.json found. Are you in a project directory?")),process.exit(1));let t=await W(o);console.log(),console.log(a(`${V.bold("Enve Scan Report")} \u2014 ${t.name}`)),console.log(a(V.dim(t.path))),console.log(),console.log(a(`.env files found: ${t.envFiles.length}`));for(let n of t.envFiles)console.log(a(`${X(`${n.filename.padEnd(18)} ${n.variables.length} variables`)}`,2));if(console.log(),console.log(a(`Code references: ${t.referenceCount} variables used`)),console.log(),t.issues.length===0)console.log(a(x("No issues found. Great job!")));else{console.log(a(`Issues found: ${t.issues.length}`));let n=t.issues.filter(s=>s.severity==="error"),i=t.issues.filter(s=>s.severity==="warning"),r=t.issues.filter(s=>s.severity==="info");for(let s of n)console.log(a(y(s.message),2));for(let s of i)console.log(a(I(s.message),2));for(let s of r)console.log(a(x(s.message),2))}console.log(),console.log(a(`Score: ${tt(t.score.score)} [ ${t.score.grade} ]`)),console.log(a(V.dim(`${t.score.errorCount} errors, ${t.score.warningCount} warnings`))),console.log(),console.log(a("Run `enve doctor` for detailed recommendations."))});function tt(e){let o=`${e}/100`;return e>=90?V.bgGreen.black(` ${o} `):e>=70?V.bgYellow.black(` ${o} `):e>=50?V.red(o):V.bgRed.white(` ${o} `)}import{Command as nt}from"commander";import S from"chalk";var Ne=new nt("doctor").description("Comprehensive health check with detailed recommendations").action(async()=>{let e=process.cwd(),o=await w(e);o||(console.log(y("No package.json found. Are you in a project directory?")),process.exit(1)),await me(o)});async function me(e){let o=await W(e),t=await D(e);console.log(),console.log(a(E(`Enve Health Check \u2014 ${o.name}`))),console.log(a(S.dim(o.path))),console.log();let n=o.issues.filter(l=>l.type==="gitignore"||l.type==="suspicious"&&l.key===".env.example"),i=o.issues.filter(l=>l.type==="missing"||l.type==="unused"),r=o.issues.filter(l=>l.type==="secret-risk"),s=o.issues.filter(l=>l.type==="invalid"||l.type==="syntax-error");fe("[File Structure]",n),rt(i,t),fe("[Security]",r),fe("[Validation]",s),it(o.issues),console.log(),console.log(a(`Score: ${st(o.score.score)} [ ${o.score.grade} ]`)),console.log(a(S.dim(`${o.score.errorCount} errors, ${o.score.warningCount} warnings, ${o.score.infoCount} info`))),console.log()}function fe(e,o){if(console.log(a(E(e))),o.length===0)console.log(a(x("No issues")));else for(let t of o){let n=t.severity==="error"?y:t.severity==="warning"?I:x;console.log(a(n(t.message),2)),t.suggestion&&console.log(a(S.dim(`\u2192 ${t.suggestion}`),3))}console.log()}function rt(e,o){let t=o.reduce((n,i)=>n+i.variables.length,0);if(console.log(a(E(`[Variables \u2014 ${t} defined]`))),e.length===0)console.log(a(x("All variables are used and defined")));else for(let n of e){let i=n.severity==="error"?y:I,r=n.message;n.type==="missing"&&n.hasFallback&&n.fallbackValue&&(r+=S.dim(` (fallback: ${n.fallbackValue})`)),console.log(a(i(r),2)),n.suggestion&&console.log(a(S.dim(`\u2192 ${n.suggestion}`),3))}console.log()}function it(e){let o=e.filter(r=>r.suggestion);if(o.length===0)return;console.log(a(E(`[Recommendations \u2014 ${o.length} actions needed]`)));let t=o.filter(r=>r.severity==="error"),n=o.filter(r=>r.severity==="warning"),i=o.filter(r=>r.severity==="info");t.length>0&&(console.log(a(S.red.bold("HIGH PRIORITY:"))),t.forEach((r,s)=>{console.log(a(`${s+1}. ${r.suggestion}`,2))}),console.log()),n.length>0&&(console.log(a(S.yellow.bold("MEDIUM PRIORITY:"))),n.forEach((r,s)=>{console.log(a(`${s+1}. ${r.suggestion}`,2))}),console.log()),i.length>0&&(console.log(a(S.blue.bold("LOW PRIORITY:"))),i.forEach((r,s)=>{console.log(a(`${s+1}. ${r.suggestion}`,2))}),console.log())}function st(e){let o=`${e}/100`;return e>=90?S.bgGreen.black(` ${o} `):e>=70?S.bgYellow.black(` ${o} `):e>=50?S.red(o):S.bgRed.white(` ${o} `)}import{Command as lt}from"commander";import ge from"chalk";import{copyFile as ct}from"fs/promises";import{createInterface as at}from"readline/promises";async function de(e){let o=at({input:process.stdin,output:process.stdout});try{return(await o.question(`${e} `)).trim()}finally{o.close()}}async function N(e,o=!1){let n=await de(`${e}${o?" (Y/n)":" (y/N)"}`);return n?/^y(es)?$/i.test(n):o}var Ve=new lt("unused").description("Find environment variables defined in .env files but never referenced in code").option("--fix","Remove unused variables from .env files").option("--dry-run","Show what would be removed without making changes").option("--include-commons","Also flag PORT, HOST, NODE_ENV if truly unused").action(async e=>{let o=process.cwd(),t=await w(o);t||(console.log(y("No package.json found. Are you in a project directory?")),process.exit(1));let n=await D(t),i=await U(t),r=new Set(i.map(d=>d.key)),s=e.includeCommons?[]:["PORT","HOST","NODE_ENV","CI","PWD"],l=[];for(let d of n)if(!(d.filename===".env.example"||d.filename===".env.test"))for(let b of d.variables)s.includes(b.key)||r.has(b.key)||l.push({filename:d.filename,path:d.path,key:b.key,line:b.line});if(console.log(),console.log(a(E(`Unused variables in ${t}`))),console.log(),l.length===0){console.log(a(x("All variables are used")));return}let f=new Map;for(let d of l)f.set(d.filename,[...f.get(d.filename)??[],d]);for(let[d,b]of f){console.log(a(d));for(let F of b)console.log(a(I(`${F.key.padEnd(20)} (line ${F.line})`),2))}if(console.log(),console.log(a(`${l.length} unused variables found. They may be safe to remove.`)),e.dryRun){console.log(a(ge.dim("Dry run: no changes made.")));return}if(!e.fix){console.log(a("Run `enve unused --fix` to remove them, or `--dry-run` to preview."));return}if(!await N(`Remove ${l.length} unused variables from .env files?`)){console.log(a(ge.dim("Aborted. No changes made.")));return}let p=new Map;for(let d of l)p.set(d.path,[...p.get(d.path)??[],d]);let v=0;for(let[d,b]of p){let F=`${d}.backup.${ut(new Date)}`;await ct(d,F);let T=(await k(d)??"").split(`
40
+ `),B=new Set(b.map(he=>he.line-1)),fo=T.filter((he,mo)=>!B.has(mo));await C(d,fo.join(`
41
+ `)),v+=b.length,console.log(a(ge.dim(`Backup saved: ${F}`),2))}console.log(),console.log(a(x(`Removed ${v} unused variables.`)))});function ut(e){return e.toISOString().replace(/[:.]/g,"-").slice(0,19)}import{Command as ft}from"commander";import z from"chalk";import{join as Oe}from"path";var Re=new Set(["PATH","HOME","USER","SHELL","PWD","OLDPWD","NODE","NODE_PATH","TMPDIR","LANG","LOGNAME","TERM","EDITOR","SHLVL","_","__CF_USER_TEXT_ENCODING"]),Le=new ft("missing").description("Find process.env references in code that are not defined in any .env file").option("--include-fallbacks","Also show variables that have fallback defaults").option("--no-fallbacks","Hide variables with fallback defaults").option("--add","Interactively add missing variables to .env").action(async e=>{let o=process.cwd(),t=await w(o);t||(console.log(y("No package.json found. Are you in a project directory?")),process.exit(1));let n=await D(t),i=await U(t),r=new Set(n.flatMap(f=>f.variables.map(m=>m.key))),s=i.filter(f=>!r.has(f.key)&&!f.hasFallback&&!Re.has(f.key)),l=e.fallbacks?i.filter(f=>!r.has(f.key)&&f.hasFallback&&!Re.has(f.key)):[];if(console.log(),console.log(a(E(`Missing environment variables in ${t}`))),console.log(),s.length===0&&l.length===0){console.log(a(x("All referenced variables are defined")));return}if(s.length>0){console.log(a(E("Missing (no fallback \u2014 WILL cause runtime errors)")));for(let f of s)console.log(a(y(`${f.key}`),2)),console.log(a(z.dim(`\u2192 ${f.file}:${f.line}`),3)),f.context&&console.log(a(z.dim(f.context.split(`
42
+ `).map(m=>` ${m}`).join(`
43
+ `)),3));console.log()}if(l.length>0){console.log(a(E("Missing (has fallback \u2014 will use default value)")));for(let f of l)console.log(a(I(`${f.key}`),2)),console.log(a(z.dim(`\u2192 fallback: ${f.fallbackValue??"unknown"}`),3)),console.log(a(z.dim(`\u2192 ${f.file}:${f.line}`),3));console.log()}console.log(a(`${y(`${s.length} truly missing`)} \xB7 ${I(`${l.length} with fallback defaults`)}`)),e.add?await mt(t,n,s):(console.log(),console.log(a("Run with `--add` to interactively add missing variables to .env")))});async function mt(e,o,t){let n=Oe(e,".env");if(!o.find(m=>m.filename===".env")){console.log(y("No .env file found. Cannot add variables."));return}let r=[],s=[];for(let m of t){let p=await de(`Value for ${m.key}:`);if(!p)continue;r.push(`${m.key}=${p}`),await N(`Add ${m.key} to .env.example?`,!1)&&s.push(`${m.key}=your_${m.key.toLowerCase()}`)}if(r.length===0){console.log(a(z.dim("No variables added.")));return}let l=await k(n)??"",f=l.endsWith(`
44
+ `)||l===""?"":`
45
+ `;if(await C(n,`${l}${f}${r.join(`
46
+ `)}
47
+ `),s.length>0){let m=Oe(e,".env.example"),p=await k(m)??"",v=p.endsWith(`
48
+ `)||p===""?"":`
49
+ `;await C(m,`${p}${v}${s.join(`
50
+ `)}
51
+ `)}console.log(),console.log(a(x(`Added ${r.length} variables to .env`))),s.length>0&&console.log(a(x(`Added ${s.length} placeholders to .env.example`)))}import{Command as dt}from"commander";import H from"chalk";function Me(e){let o=Number(e);return!Number.isInteger(o)||o<1||o>65535?{valid:!1,message:"PORT must be a number between 1 and 65535",suggestion:"PORT=3000"}:{valid:!0}}function _e(e){if(!e.includes("://"))return{valid:!1,message:"Invalid URL \u2014 missing protocol",suggestion:`Did you mean https://${e} or a protocol-specific URL?`};try{let o=new URL(e);if(!o.protocol||o.protocol===":")throw new Error("Missing protocol");return{valid:!0}}catch{return{valid:!1,message:"Invalid URL",suggestion:"Check the URL format"}}}function Ge(e){return["development","production","test"].includes(e)?{valid:!0}:{valid:!1,message:"NODE_ENV should be 'development', 'production', or 'test'",suggestion:`NODE_ENV=${e==="dev"?"development":e==="prod"?"production":"development"}`}}function Ue(e,o){let t=["default","secret","test","password","123456"];return e.length<16?{valid:!1,message:`${o} is too short (${e.length} characters)`,suggestion:`Use at least 16 random characters. Generate one: node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"`}:t.includes(e.toLowerCase())?{valid:!1,message:`${o} uses a weak placeholder value`,suggestion:"Generate a strong random secret"}:{valid:!0}}function We(e,o){let t=e.toLowerCase();if(t==="true"||t==="false")return{valid:!0};let n={yes:"true",no:"false",1:"true",0:"false",on:"true",off:"false"};return t in n?{valid:!1,message:`${o} should be 'true' or 'false' for boolean values`,suggestion:`${o}=${n[t]}`}:{valid:!1,message:`${o} is not a valid boolean value`,suggestion:`${o}=true`}}function He(e){return/SECRET|KEY|TOKEN|PASSWORD|PRIVATE|ACCESS|CREDENTIAL|AUTH/i.test(e)}import{copyFile as gt}from"fs/promises";var Ye=new dt("validate").description("Validate the values of environment variables against expected formats").option("--fix","Automatically fix simple issues").action(async e=>{let o=process.cwd(),t=await w(o);t||(console.log(y("No package.json found. Are you in a project directory?")),process.exit(1));let i=(await D(t)).find(m=>m.filename===".env");i||(console.log(y("No .env file found. Cannot validate.")),process.exit(1));let r=i.variables;console.log(),console.log(a(E(`Validating environment variables \u2014 ${i.filename}`))),console.log();let s=0,l=0,f=[];for(let m of r){let p=pt(m);if(p.valid)console.log(a(x(`${m.key}=${Ke(m.value)}`)));else{s++,console.log(a(y(`${m.key}=${Ke(m.value)}`))),p.message&&console.log(a(H.dim(` ${p.message}`),2)),p.suggestion&&console.log(a(H.dim(` Suggestion: ${p.suggestion}`),2));let v=vt(m);v!==void 0&&(console.log(a(H.yellow(` Auto-fix available: ${m.key}=${v}`),2)),f.push({key:m.key,oldValue:m.value,newValue:v}))}}if(console.log(),console.log(a(`${r.length} variables checked, ${s} errors, ${l} warnings`)),e.fix){let m=f.filter(v=>!qe(v.key));if(m.length===0){console.log(a(H.dim("No auto-fixable issues (secrets are never auto-fixed).")));return}if(!await N(`Apply ${m.length} auto-fixes to .env?`)){console.log(a(H.dim("Aborted. No changes made.")));return}await yt(i.path,m),console.log(),console.log(a(x(`Applied ${m.length} fixes to .env`)))}else f.length>0&&console.log(a("Run with `--fix` to apply available auto-fixes."))});function pt(e){let o=e.key,t=e.value;return o==="PORT"||o.endsWith("_PORT")?Me(t):o==="NODE_ENV"?Ge(t):o.endsWith("_URL")||o==="DATABASE_URL"||o==="REDIS_URL"?_e(t):qe(o)?Ue(t,o):o==="DEBUG"||o==="VERBOSE"||o.endsWith("_ENABLED")?We(t,o):{valid:!0}}function qe(e){return/SECRET|KEY|TOKEN|PRIVATE/i.test(e)&&e!=="API_KEY"}function vt(e){let o=e.key,t=e.value;if((o.endsWith("_URL")||o==="DATABASE_URL"||o==="REDIS_URL")&&!t.includes("://")&&!t.startsWith("http"))return`https://${t}`;if(o==="NODE_ENV"){if(t==="dev")return"development";if(t==="prod")return"production"}if(o==="DEBUG"||o==="VERBOSE"||o.endsWith("_ENABLED")){let n=t.toLowerCase();if(n==="yes"||n==="1"||n==="on")return"true";if(n==="no"||n==="0"||n==="off")return"false"}}async function yt(e,o){let t=`${e}.backup.validate.${ht(new Date)}`;await gt(e,t);let n=await k(e)??"",i=new Map(o.map(s=>[s.key,s.newValue])),r=n.split(`
52
+ `).map(s=>{let l=s.trim();if(!l||l.startsWith("#"))return s;let f=l.indexOf("=");if(f===-1)return s;let m=l.slice(0,f).trim();if(m.startsWith("export ")&&(m=m.slice(7).trim()),i.has(m)){let p=s.match(/^\s*/)?.[0]??"",v=s.slice(s.indexOf("=")+1),d=v.indexOf(" #"),b=d!==-1?v.slice(d):"",$=(d!==-1?v.slice(0,d):v).match(/^\s*/)?.[0]??"";return`${p}${m}=${$}${i.get(m)}${b}`}return s});await C(e,r.join(`
53
+ `)),console.log(a(H.dim(`Backup saved: ${t}`),2))}function ht(e){return e.toISOString().replace(/[:.]/g,"-").slice(0,19)}function Ke(e,o=40){return e.length<=o?e:e.slice(0,o)+"..."}import{Command as xt}from"commander";import K from"chalk";import{join as Xe}from"path";var ze=new xt("generate-example").description("Automatically create or update .env.example from the current .env file").option("--overwrite","Update existing .env.example without prompting").option("--output <path>","Write to a different file",".env.example").option("--all-secrets","Redact ALL values").option("--dry-run","Show what would be generated without writing files").action(async e=>{let o=process.cwd(),t=await w(o);t||(console.log(y("No package.json found. Are you in a project directory?")),process.exit(1));let n=Xe(t,".env");await k(n)||(console.log(y("No .env file found. Cannot generate .env.example.")),process.exit(1));let r=Xe(t,e.output),s=await k(r),l=ue(n),f=[],m=0,p=0;for(let d of l)e.allSecrets||d.isSecret||He(d.key)?(f.push(`${d.key}=your_${d.key.toLowerCase()}`),m++):(f.push(`${d.key}=${d.value}`),p++);let v=`${f.join(`
54
+ `)}
55
+ `;if(console.log(),console.log(a(E(`Generating ${e.output} from .env`))),console.log(),console.log(a(`Variables: ${l.length} total`)),console.log(a(X(`${p} public variables \u2192 copied as-is`),2)),console.log(a(X(`${m} secrets redacted`),2)),s){let d=bt(s,v);if(d.length===0){console.log(),console.log(a(x(`${e.output} is already up to date.`)));return}console.log(),console.log(a(E("Changes since last generation:")));for(let b of d){let F=b.type==="added"?K.green("+"):b.type==="removed"?K.red("-"):K.yellow("~");console.log(a(`${F} ${b.type}: ${b.key}`,2))}if(e.dryRun){console.log(),console.log(a(K.dim("Dry run \u2014 no changes made.")));return}if(!e.overwrite&&!await N(`Update ${e.output}?`)){console.log(a(K.dim("Aborted. No changes made.")));return}}else e.dryRun&&(console.log(),console.log(a(K.dim("Dry run \u2014 no changes made."))));e.dryRun||(await C(r,v),console.log(),console.log(a(x(`Created ${e.output}`))))});function bt(e,o){let t=new Map,n=new Map;for(let r of e.split(`
56
+ `)){let s=r.trim();if(!s||s.startsWith("#"))continue;let l=s.indexOf("=");l!==-1&&t.set(s.slice(0,l).trim(),s.slice(l+1).trim())}for(let r of o.split(`
57
+ `)){let s=r.trim();if(!s||s.startsWith("#"))continue;let l=s.indexOf("=");l!==-1&&n.set(s.slice(0,l).trim(),s.slice(l+1).trim())}let i=[];for(let[r,s]of n)t.has(r)?t.get(r)!==s&&i.push({type:"updated",key:r}):i.push({type:"added",key:r});for(let r of t.keys())n.has(r)||i.push({type:"removed",key:r});return i}import{Command as ne}from"commander";var Qe=new ne("hook").description("Install, manage, and check a pre-commit git hook that prevents .env commits").addCommand(new ne("install").description("Install the enve pre-commit hook").action(async()=>{let e=process.cwd(),o=await w(e);(!o||!await M(o))&&(console.log(y("Not a git repository. Run `git init` first.")),process.exit(1)),await be(o),console.log(),console.log(a(x("Pre-commit hook installed"))),console.log(a(`Location: ${_(o)}`)),console.log(),console.log(a("What it does:")),console.log(a(pe("Blocks commits of .env, .env.local, .env.production files"),2)),console.log(a(pe("Shows a helpful message with next steps"),2)),console.log(a(pe("Can be bypassed with --no-verify (emergency only)"),2)),console.log(),console.log(a("To remove: enve hook uninstall"))})).addCommand(new ne("status").description("Check the status of the enve pre-commit hook").action(async()=>{let e=process.cwd(),o=await w(e);(!o||!await M(o))&&(console.log(y("Not a git repository.")),process.exit(1));let t=await J(o);console.log(),t?(console.log(a(x("Pre-commit hook: Installed (enve protection active)"))),console.log(a(`Location: ${_(o)}`))):(console.log(a(I("Pre-commit hook: Not installed"))),console.log(a("Run `enve hook install` to enable .env commit protection.")))})).addCommand(new ne("uninstall").description("Remove the enve pre-commit hook").action(async()=>{let e=process.cwd(),o=await w(e);(!o||!await M(o))&&(console.log(y("Not a git repository.")),process.exit(1));let t=await ke(o);console.log(),t?(console.log(a(x("Pre-commit hook removed"))),console.log(a("Run `enve hook install` to re-enable."))):console.log(a(I("No enve hook found to remove.")))}));function pe(e){return`\u2022 ${e}`}import{Command as Ct}from"commander";import{render as It}from"ink";import lo,{useState as St,useCallback as ao}from"react";import c,{useState as io,useMemo as kt}from"react";import{Box as h,Text as g,useInput as wt,useApp as Et,useStdout as $t}from"ink";import j from"react";import{Box as Q,Text as Y}from"ink";import ie from"react";import{Box as oo,Text as to}from"ink";var u={canvas:"#FAFAF8",ink:"#1A1A1A",muted:"#6B6B6B",subtle:"#A3A3A3",border:"#E5E5E0",borderStrong:"#D4D4CF",excellent:"#10B981",excellentBg:"#ECFDF5",good:"#F59E0B",goodBg:"#FFFBEB",attention:"#F97316",attentionBg:"#FFF7ED",critical:"#EF4444",criticalBg:"#FEF2F2",error:"#DC2626",warning:"#D97706",info:"#2563EB",success:"#059669"},Je={Excellent:{text:u.excellent,bg:u.excellentBg,label:"Excellent"},Good:{text:u.good,bg:u.goodBg,label:"Good"},"Needs attention":{text:u.attention,bg:u.attentionBg,label:"Needs attention"},Critical:{text:u.critical,bg:u.criticalBg,label:"Critical"}},Ze={error:{color:u.error,symbol:"\u25CF"},warning:{color:u.warning,symbol:"\u25CF"},info:{color:u.info,symbol:"\u25CF"}},eo={missing:"\u2298",unused:"\u25CB",invalid:"\u2715",suspicious:"\u25C6","secret-risk":"\u25B2","syntax-error":"\u2715",gitignore:"\u25C9"},re={sidebarWidth:34,maxContentWidth:86,paddingX:2,paddingY:1};function se({score:e,compact:o=!1}){let t=Je[e.grade];return o?ie.createElement(oo,null,ie.createElement(to,{backgroundColor:t.bg,color:t.text,bold:!0}," ",e.score," ")):ie.createElement(oo,null,ie.createElement(to,{backgroundColor:t.bg,color:t.text,bold:!0}," ",e.score," ",e.grade," "))}function no({project:e,isSelected:o,isFocused:t}){let{score:n}=e,i=o?u.ink:u.border,r=t?"\u203A":" ";return j.createElement(Q,{borderStyle:"single",borderColor:i,paddingX:1,paddingY:1,flexDirection:"column"},j.createElement(Q,{justifyContent:"space-between"},j.createElement(Q,null,j.createElement(Y,{color:o?u.ink:u.muted},r," "),j.createElement(Y,{bold:!0,color:u.ink},e.name)),j.createElement(se,{score:n,compact:!0})),j.createElement(Q,{marginTop:1},j.createElement(Y,{color:u.muted},e.path)),j.createElement(Q,{marginTop:1,columnGap:2},n.errorCount>0&&j.createElement(Y,{color:u.error},n.errorCount," err"),n.warningCount>0&&j.createElement(Y,{color:u.warning},n.warningCount," warn"),n.totalIssues===0&&j.createElement(Y,{color:u.success},"Clean")))}import P from"react";import{Box as O,Text as R}from"ink";function ro({issues:e,maxRows:o=12}){if(e.length===0)return P.createElement(O,{paddingY:1},P.createElement(R,{color:u.success},"No issues found. Great job."));let t=[...e].sort((r,s)=>{let l={error:0,warning:1,info:2};return l[r.severity]-l[s.severity]}),n=t.slice(0,o),i=t.length-n.length;return P.createElement(O,{flexDirection:"column",width:"100%"},n.map((r,s)=>{let l=Ze[r.severity],f=eo[r.type]??"\u2022";return P.createElement(O,{key:`${r.type}-${r.key}-${s}`,flexDirection:"column",marginBottom:1},P.createElement(O,null,P.createElement(R,{color:l.color},f," "),P.createElement(R,{bold:!0,color:u.ink},r.key),P.createElement(R,{color:u.subtle}," \u2014 ",r.type)),P.createElement(O,{marginLeft:2},P.createElement(R,{color:u.muted},r.message)),r.suggestion&&P.createElement(O,{marginLeft:2},P.createElement(R,{color:u.subtle},"\u2192 ",r.suggestion)))}),i>0&&P.createElement(O,{marginTop:1},P.createElement(R,{color:u.subtle},"+ ",i," more issue",i===1?"":"s")))}function so({projects:e,version:o,onRunDoctor:t,onRefresh:n}){let{exit:i}=Et(),{stdout:r}=$t(),[s,l]=io(0),[f,m]=io(!1),p=r.rows??24,v=e[s],d=Math.max(4,Math.floor((p-10)/5)),b=kt(()=>{if(e.length<=d)return e;let $=Math.max(0,s-Math.floor(d/2)),T=$+d;return T>e.length&&(T=e.length,$=Math.max(0,T-d)),e.slice($,T)},[e,s,d]),F=e.length<=d?0:Math.max(0,s-Math.floor(d/2));return wt(($,T)=>{f||(T.upArrow||$==="k"?l(B=>Math.max(0,B-1)):T.downArrow||$==="j"?l(B=>Math.min(e.length-1,B+1)):T.return?v&&(m(!0),Promise.resolve(t(v)).then(()=>i())):$==="r"?Promise.resolve(n()):($==="q"||T.escape)&&(m(!0),i()))},{isActive:!f}),e.length===0?c.createElement(h,{flexDirection:"column",padding:2},c.createElement(g,{bold:!0,color:u.ink},"enve dashboard"),c.createElement(h,{marginTop:1},c.createElement(g,{color:u.muted},"No projects tracked yet.")),c.createElement(h,{marginTop:1},c.createElement(g,{color:u.subtle},"Run `enve scan` or `enve doctor` in a project to add it."))):c.createElement(h,{flexDirection:"column",width:"100%",paddingX:re.paddingX,paddingY:re.paddingY},c.createElement(h,{justifyContent:"space-between",width:"100%",marginBottom:1},c.createElement(h,null,c.createElement(g,{bold:!0,color:u.ink},"enve"),c.createElement(g,{color:u.muted}," dashboard")),c.createElement(g,{color:u.subtle},"v",o)),c.createElement(h,{flexGrow:1,width:"100%"},c.createElement(h,{width:re.sidebarWidth,flexDirection:"column",borderStyle:"single",borderColor:u.border,paddingX:1,paddingY:1,marginRight:2},c.createElement(h,{marginBottom:1},c.createElement(g,{bold:!0,color:u.ink},"Projects"),c.createElement(g,{color:u.subtle}," (",e.length,")")),c.createElement(h,{flexDirection:"column",rowGap:1,flexGrow:1},b.map(($,T)=>{let B=F+T;return c.createElement(no,{key:$.path,project:$,isSelected:B===s,isFocused:B===s})})),e.length>d&&c.createElement(h,{marginTop:1},c.createElement(g,{color:u.subtle},"Use \u2191/\u2193 to scroll"))),c.createElement(h,{flexGrow:1,flexDirection:"column",borderStyle:"single",borderColor:u.border,paddingX:2,paddingY:1},v?c.createElement(Pt,{project:v}):c.createElement(h,null,c.createElement(g,{color:u.muted},"Select a project to view details.")))),c.createElement(h,{justifyContent:"space-between",width:"100%",borderStyle:"single",borderColor:u.border,paddingX:1,marginTop:1},c.createElement(h,{columnGap:3},c.createElement(g,{color:u.muted},c.createElement(g,{bold:!0},"\u2191/\u2193")," navigate"),c.createElement(g,{color:u.muted},c.createElement(g,{bold:!0},"Enter")," doctor"),c.createElement(g,{color:u.muted},c.createElement(g,{bold:!0},"r")," refresh"),c.createElement(g,{color:u.muted},c.createElement(g,{bold:!0},"q")," quit")),c.createElement(g,{color:u.subtle},"enve-doctor v",o)))}function Pt({project:e}){let o=e.envFiles.map(t=>t.filename).join(", ")||"none";return c.createElement(h,{flexDirection:"column",width:"100%"},c.createElement(h,{justifyContent:"space-between",alignItems:"center",marginBottom:1},c.createElement(g,{bold:!0,color:u.ink},e.name),c.createElement(se,{score:e.score})),c.createElement(h,{marginBottom:1},c.createElement(g,{color:u.muted},e.path)),c.createElement(h,{flexDirection:"row",columnGap:4,marginBottom:1},c.createElement(ve,{label:"Env vars",value:String(e.envCount)}),c.createElement(ve,{label:"References",value:String(e.referenceCount)}),c.createElement(ve,{label:"Issues",value:String(e.score.totalIssues)})),c.createElement(h,{flexDirection:"row",columnGap:4,marginBottom:1},c.createElement(ye,{label:"Git repo",ok:e.hasGit}),c.createElement(ye,{label:"Git hook",ok:e.hookInstalled}),c.createElement(ye,{label:".gitignore",ok:e.hasEnvInGitignore})),c.createElement(h,{marginTop:1,marginBottom:1},c.createElement(g,{color:u.subtle},"Env files: "),c.createElement(g,{color:u.ink},o)),c.createElement(h,{marginTop:1,marginBottom:1},c.createElement(g,{color:u.borderStrong},"\u2500".repeat(60))),c.createElement(h,{marginTop:1,marginBottom:1},c.createElement(g,{bold:!0,color:u.ink},"Top issues")),c.createElement(ro,{issues:e.issues,maxRows:8}))}function ve({label:e,value:o}){return c.createElement(h,{flexDirection:"column"},c.createElement(g,{color:u.subtle},e),c.createElement(g,{bold:!0,color:u.ink},o))}function ye({label:e,ok:o}){return c.createElement(h,null,c.createElement(g,{color:o?u.success:u.error},o?"\u2713":"\u2717"," "),c.createElement(g,{color:u.muted},e))}var ae={name:"enve-doctor",version:"1.0.0",description:"Environment Variable Doctor \u2014 scan, validate, and protect your env files",main:"dist/cli.mjs",bin:{enve:"./dist/cli.mjs"},repository:{type:"git",url:"git+https://github.com/RufixDuke/enve.git"},bugs:{url:"https://github.com/RufixDuke/enve/issues"},homepage:"https://enve-lac.vercel.app",files:["dist"],scripts:{build:"tsup src/cli/cli.ts --minify --clean",dev:"tsx src/cli/index.ts",test:"vitest run","test:watch":"vitest",prepublishOnly:"npm run build"},dependencies:{acorn:"^8.14.0","acorn-walk":"^8.3.4",chalk:"^5.4.1",commander:"^13.1.0",conf:"^13.1.0",dotenv:"^16.4.7","fast-glob":"^3.3.3",ink:"^5.1.0",ora:"^8.2.0",react:"^18.3.1"},devDependencies:{"@types/node":"^22.13.0","@types/react":"^18.3.18","@typescript-eslint/eslint-plugin":"^8.22.0","@typescript-eslint/parser":"^8.22.0",eslint:"^9.19.0","ink-testing-library":"^4.0.0",tsup:"^8.3.6",tsx:"^4.19.2",typescript:"^5.7.3",vitest:"^3.0.5"},keywords:["env","environment-variables","secrets","security","cli","developer-tools"],author:"Abdul-Qudus Rufai",license:"MIT",engines:{node:">=18.0.0"}};var co=new Ct("dashboard").description("Interactive terminal dashboard showing all tracked projects").action(async()=>{let e=ee();if(e.length===0){console.log(),console.log(" Enve Dashboard"),console.log(),console.log(" No projects tracked yet."),console.log(" Run `enve scan` or `enve doctor` in a project to add it."),console.log();return}if(!process.stdin.isTTY){console.log(),console.log(" The interactive dashboard requires a TTY terminal."),console.log(" Run `enve dashboard` in your terminal to use it."),console.log();return}let o=await uo(e.map(t=>t.path));It(lo.createElement(jt,{initialProjects:o,trackedPaths:e.map(t=>t.path),version:ae.version}))});function jt({initialProjects:e,trackedPaths:o,version:t}){let[n,i]=St(e),r=ao(async l=>{await me(l.path)},[]),s=ao(async()=>{let l=await uo(o);i(l)},[o]);return lo.createElement(so,{projects:n,version:t,onRunDoctor:r,onRefresh:s})}async function uo(e){return await Promise.all(e.map(t=>W(t)))}var A=new Ft;A.name("enve").description("Environment Variable Doctor \u2014 scan, validate, and protect your env files").version(ae.version);A.addCommand(Be);A.addCommand(Ne);A.addCommand(Ve);A.addCommand(Le);A.addCommand(Ye);A.addCommand(ze);A.addCommand(Qe);A.addCommand(co);process.on("unhandledRejection",e=>{console.error(`
58
+ Unexpected error:`,e instanceof Error?e.message:e),console.error(" If this keeps happening, please open an issue with the stack trace above."),process.exit(1)});A.parse();
59
+ //# sourceMappingURL=cli.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/cli/cli.ts","../src/cli/commands/scan.ts","../src/core/project.ts","../src/utils/fs.ts","../src/utils/git.ts","../src/core/parser.ts","../src/core/scanner.ts","../src/core/analyzer.ts","../src/core/config.ts","../src/utils/logger.ts","../src/cli/commands/doctor.ts","../src/cli/commands/unused.ts","../src/utils/prompt.ts","../src/cli/commands/missing.ts","../src/cli/commands/validate.ts","../src/utils/validators.ts","../src/cli/commands/generate-example.ts","../src/cli/commands/hook.ts","../src/cli/commands/dashboard.tsx","../src/ui/Dashboard.tsx","../src/ui/ProjectCard.tsx","../src/ui/ScoreBadge.tsx","../src/ui/theme.ts","../src/ui/IssueList.tsx","../package.json"],"sourcesContent":["import { Command } from 'commander';\nimport { scanCommand } from './commands/scan.js';\nimport { doctorCommand } from './commands/doctor.js';\nimport { unusedCommand } from './commands/unused.js';\nimport { missingCommand } from './commands/missing.js';\nimport { validateCommand } from './commands/validate.js';\nimport { generateExampleCommand } from './commands/generate-example.js';\nimport { hookCommand } from './commands/hook.js';\nimport { dashboardCommand } from './commands/dashboard.js';\nimport pkg from '../../package.json' with { type: 'json' };\n\nconst program = new Command();\n\nprogram\n .name('enve')\n .description('Environment Variable Doctor — scan, validate, and protect your env files')\n .version(pkg.version);\n\nprogram.addCommand(scanCommand);\nprogram.addCommand(doctorCommand);\nprogram.addCommand(unusedCommand);\nprogram.addCommand(missingCommand);\nprogram.addCommand(validateCommand);\nprogram.addCommand(generateExampleCommand);\nprogram.addCommand(hookCommand);\nprogram.addCommand(dashboardCommand);\n\nprocess.on('unhandledRejection', (reason) => {\n console.error('\\n Unexpected error:', reason instanceof Error ? reason.message : reason);\n console.error(' If this keeps happening, please open an issue with the stack trace above.');\n process.exit(1);\n});\n\nprogram.parse();\n","import { Command } from 'commander';\nimport chalk from 'chalk';\nimport { detectProject, getProjectInfo } from '../../core/project.js';\nimport { success, warning, error, bullet, indent } from '../../utils/logger.js';\n\nexport const scanCommand = new Command('scan')\n .description('Quick overview of the current project\\'s environment variable health')\n .action(async () => {\n const cwd = process.cwd();\n const projectPath = await detectProject(cwd);\n\n if (!projectPath) {\n console.log(error('No package.json found. Are you in a project directory?'));\n process.exit(1);\n }\n\n const info = await getProjectInfo(projectPath);\n\n console.log();\n console.log(indent(`${chalk.bold('Enve Scan Report')} — ${info.name}`));\n console.log(indent(chalk.dim(info.path)));\n console.log();\n\n console.log(indent(`.env files found: ${info.envFiles.length}`));\n for (const file of info.envFiles) {\n console.log(indent(`${bullet(`${file.filename.padEnd(18)} ${file.variables.length} variables`)}`, 2));\n }\n console.log();\n\n console.log(indent(`Code references: ${info.referenceCount} variables used`));\n console.log();\n\n if (info.issues.length === 0) {\n console.log(indent(success('No issues found. Great job!')));\n } else {\n console.log(indent(`Issues found: ${info.issues.length}`));\n\n const errors = info.issues.filter((i) => i.severity === 'error');\n const warnings = info.issues.filter((i) => i.severity === 'warning');\n const infos = info.issues.filter((i) => i.severity === 'info');\n\n for (const issue of errors) {\n console.log(indent(error(issue.message), 2));\n }\n for (const issue of warnings) {\n console.log(indent(warning(issue.message), 2));\n }\n for (const issue of infos) {\n console.log(indent(success(issue.message), 2));\n }\n }\n\n console.log();\n console.log(indent(`Score: ${formatScore(info.score.score)} [ ${info.score.grade} ]`));\n console.log(indent(chalk.dim(`${info.score.errorCount} errors, ${info.score.warningCount} warnings`)));\n console.log();\n console.log(indent('Run `enve doctor` for detailed recommendations.'));\n });\n\nfunction formatScore(score: number): string {\n const text = `${score}/100`;\n if (score >= 90) return chalk.bgGreen.black(` ${text} `);\n if (score >= 70) return chalk.bgYellow.black(` ${text} `);\n if (score >= 50) return chalk.red(text);\n return chalk.bgRed.white(` ${text} `);\n}\n","import { join, basename } from 'node:path';\nimport { fileExists } from '../utils/fs.js';\nimport { isGitRepo } from '../utils/git.js';\nimport { parseAllEnvFiles } from './parser.js';\nimport { scanProject } from './scanner.js';\nimport { analyze, calculateScore } from './analyzer.js';\nimport { addProject } from './config.js';\nimport type { ProjectInfo } from '../types/index.js';\nimport { isHookInstalled } from '../utils/git.js';\n\nexport async function detectProject(startPath: string): Promise<string | undefined> {\n let current = startPath;\n while (true) {\n if (await fileExists(join(current, 'package.json'))) {\n return current;\n }\n const parent = join(current, '..');\n if (parent === current) break;\n current = parent;\n }\n return undefined;\n}\n\nexport async function getProjectInfo(projectPath: string): Promise<ProjectInfo> {\n const [envFiles, references, hasGit, hookInstalled] = await Promise.all([\n parseAllEnvFiles(projectPath),\n scanProject(projectPath),\n isGitRepo(projectPath),\n isHookInstalled(projectPath),\n ]);\n\n const issues = await analyze(envFiles, references, projectPath);\n const score = calculateScore(issues);\n const name = basename(projectPath);\n\n await addProject(projectPath);\n\n return {\n name,\n path: projectPath,\n envFiles,\n envCount: envFiles.reduce((sum, f) => sum + f.variables.length, 0),\n referenceCount: references.length,\n issues,\n score,\n hasGit,\n hookInstalled,\n hasEnvInGitignore: !issues.some((i) => i.type === 'gitignore'),\n lastModified: new Date(),\n };\n}\n","import { readFile, writeFile, access, mkdir } from 'node:fs/promises';\nimport { dirname } from 'node:path';\n\nexport async function readFileSafe(filePath: string): Promise<string | undefined> {\n try {\n return await readFile(filePath, 'utf-8');\n } catch {\n return undefined;\n }\n}\n\nexport async function writeFileSafe(filePath: string, content: string): Promise<void> {\n await ensureDir(dirname(filePath));\n await writeFile(filePath, content, 'utf-8');\n}\n\nexport async function fileExists(filePath: string): Promise<boolean> {\n try {\n await access(filePath);\n return true;\n } catch {\n return false;\n }\n}\n\nexport async function ensureDir(dirPath: string): Promise<void> {\n if (!dirPath) return;\n try {\n await mkdir(dirPath, { recursive: true });\n } catch {\n // Directory may already exist\n }\n}\n","import { join } from 'node:path';\nimport { readFileSafe, fileExists, writeFileSafe } from './fs.js';\n\nconst PRE_COMMIT_HOOK_MARKER = '# === Enve Pre-Commit Hook ===';\nconst PRE_COMMIT_HOOK_END = '# === End Enve Hook ===';\n\nexport async function isGitRepo(projectPath: string): Promise<boolean> {\n return fileExists(join(projectPath, '.git'));\n}\n\nexport async function readGitignore(projectPath: string): Promise<string | undefined> {\n const gitignorePath = join(projectPath, '.gitignore');\n return readFileSafe(gitignorePath);\n}\n\nexport function isPatternInGitignore(gitignoreContent: string, pattern: string): boolean {\n const lines = gitignoreContent.split('\\n');\n return lines.some((line) => {\n const trimmed = line.trim();\n return trimmed === pattern || trimmed === `/${pattern}`;\n });\n}\n\nexport async function isEnvFileGitignored(\n projectPath: string,\n filename: string\n): Promise<boolean> {\n const content = await readGitignore(projectPath);\n if (!content) return false;\n return isPatternInGitignore(content, filename);\n}\n\nexport function getPreCommitHookPath(projectPath: string): string {\n return join(projectPath, '.git', 'hooks', 'pre-commit');\n}\n\nexport function getHookScript(): string {\n return `#!/bin/bash\n${PRE_COMMIT_HOOK_MARKER}\n# Blocks commits of .env files that may contain secrets\n# Installed by: enve hook install\n# Remove with: enve hook uninstall\n\nBLOCKED_PATTERNS='^\\\\.env(\\\\.[^/]*)?$'\nSTAGED_ENV_FILES=$(git diff --cached --name-only | grep -E \"$BLOCKED_PATTERNS\" || true)\n\nif [ -n \"$STAGED_ENV_FILES\" ]; then\n echo \"\"\n echo \" ✗ COMMIT BLOCKED by Enve\"\n echo \"\"\n echo \" You are attempting to commit environment files:\"\n echo \"$STAGED_ENV_FILES\" | sed 's/^/ /'\n echo \"\"\n echo \" These files may contain secrets (API keys, passwords, tokens).\"\n echo \"\"\n echo \" What to do instead:\"\n echo \" 1. Add them to .gitignore: echo '.env.local' >> .gitignore\"\n echo \" 2. Use .env.example for templates: enve generate-example\"\n echo \" 3. Share secrets through a secure channel (1Password, etc.)\"\n echo \"\"\n echo \" If you are CERTAIN these files are safe to commit:\"\n echo \" git commit --no-verify\"\n echo \"\"\n exit 1\nfi\n${PRE_COMMIT_HOOK_END}\n`;\n}\n\nexport async function isHookInstalled(projectPath: string): Promise<boolean> {\n const hookPath = getPreCommitHookPath(projectPath);\n if (!(await fileExists(hookPath))) return false;\n const content = await readFileSafe(hookPath);\n return content?.includes(PRE_COMMIT_HOOK_MARKER) ?? false;\n}\n\nexport async function installHook(projectPath: string): Promise<void> {\n const hookPath = getPreCommitHookPath(projectPath);\n const existing = (await readFileSafe(hookPath)) ?? '';\n\n if (existing.includes(PRE_COMMIT_HOOK_MARKER)) {\n return; // Already installed\n }\n\n const newContent = `${getHookScript()}\\n${existing}`;\n await writeFileSafe(hookPath, newContent);\n}\n\nexport async function uninstallHook(projectPath: string): Promise<boolean> {\n const hookPath = getPreCommitHookPath(projectPath);\n if (!(await fileExists(hookPath))) return false;\n\n const content = await readFileSafe(hookPath);\n if (!content || !content.includes(PRE_COMMIT_HOOK_MARKER)) return false;\n\n const markerStart = content.indexOf(PRE_COMMIT_HOOK_MARKER);\n const markerEnd = content.indexOf(PRE_COMMIT_HOOK_END, markerStart);\n if (markerStart === -1 || markerEnd === -1) return false;\n\n const before = content.slice(0, markerStart);\n const after = content.slice(markerEnd + PRE_COMMIT_HOOK_END.length);\n const newContent = `${before.trimEnd()}\\n${after.trimStart()}`.trim();\n\n if (newContent) {\n await writeFileSafe(hookPath, `${newContent}\\n`);\n } else {\n // No remaining content; leave file removal to caller if desired\n await writeFileSafe(hookPath, '');\n }\n return true;\n}\n","import { join } from 'node:path';\nimport { readFileSync } from 'node:fs';\nimport type { EnvFile, EnvVariable } from '../types/index.js';\nimport { readFileSafe } from '../utils/fs.js';\n\nexport function parseEnvFile(filePath: string): EnvVariable[] {\n let content: string | undefined;\n try {\n content = readFileSync(filePath, 'utf-8');\n } catch {\n return [];\n }\n if (!content) return [];\n\n const variables: EnvVariable[] = [];\n const lines = content.split('\\n');\n\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i];\n const trimmed = line.trim();\n\n if (!trimmed || trimmed.startsWith('#')) continue;\n\n const equalIndex = trimmed.indexOf('=');\n if (equalIndex === -1) continue;\n\n let key = trimmed.slice(0, equalIndex).trim();\n let rawValue = trimmed.slice(equalIndex + 1);\n\n // Strip export keyword\n if (key.startsWith('export ')) {\n key = key.slice(7).trim();\n }\n\n if (!key) continue;\n\n const { value, comment } = parseValue(rawValue);\n const isSecret = detectSecret(key, value);\n\n variables.push({\n key,\n value,\n line: i + 1,\n source: filePath,\n isSecret,\n comment,\n });\n }\n\n return variables;\n}\n\nfunction parseValue(raw: string): { value: string; comment?: string } {\n const trimmed = raw.trimStart();\n\n if (trimmed.startsWith('\"')) {\n const { value, endIndex } = parseQuotedValue(trimmed, '\"');\n const rest = trimmed.slice(endIndex + 1).trim();\n const comment = extractInlineComment(rest);\n return { value: unescape(value), comment };\n }\n\n if (trimmed.startsWith(\"'\")) {\n const { value, endIndex } = parseQuotedValue(trimmed, \"'\");\n const rest = trimmed.slice(endIndex + 1).trim();\n const comment = extractInlineComment(rest);\n return { value, comment };\n }\n\n const trailing = raw.trimEnd();\n const commentIndex = findUnquotedCommentIndex(trailing);\n if (commentIndex !== -1) {\n return {\n value: trailing.slice(0, commentIndex).trimEnd(),\n comment: trailing.slice(commentIndex + 1).trim(),\n };\n }\n\n return { value: raw.trimEnd() };\n}\n\nfunction parseQuotedValue(raw: string, quote: string): { value: string; endIndex: number } {\n let value = '';\n let escaped = false;\n for (let i = 1; i < raw.length; i++) {\n const char = raw[i];\n if (escaped) {\n if (char === 'n') value += '\\n';\n else if (char === 't') value += '\\t';\n else if (char === quote) value += quote;\n else value += `\\\\${char}`;\n escaped = false;\n continue;\n }\n if (char === '\\\\') {\n escaped = true;\n continue;\n }\n if (char === quote) {\n return { value, endIndex: i };\n }\n value += char;\n }\n // Unterminated quote — return rest as value\n return { value, endIndex: raw.length - 1 };\n}\n\nfunction unescape(value: string): string {\n // Escape sequences are already handled during quote parsing.\n // This function remains as a no-op fallback for any raw unquoted escapes.\n return value;\n}\n\nfunction extractInlineComment(rest: string): string | undefined {\n const idx = rest.indexOf('#');\n if (idx === -1) return undefined;\n return rest.slice(idx + 1).trim();\n}\n\nfunction findUnquotedCommentIndex(value: string): number {\n for (let i = 0; i < value.length; i++) {\n if (value[i] === '#' && (i === 0 || value[i - 1] === ' ')) {\n return i;\n }\n }\n return -1;\n}\n\nfunction detectSecret(key: string, value: string): boolean {\n const secretPatterns = /SECRET|KEY|TOKEN|PASSWORD|PRIVATE|ACCESS|CREDENTIAL/i;\n if (secretPatterns.test(key)) return true;\n\n // Simple entropy heuristic: high ratio of non-word characters or length > 20 with mixed case\n if (value.length > 20 && /[A-Z]/.test(value) && /[a-z]/.test(value) && /\\d/.test(value)) {\n return true;\n }\n\n return false;\n}\n\nexport async function findEnvFiles(projectPath: string): Promise<EnvFile[]> {\n const envFileNames = ['.env', '.env.local', '.env.development', '.env.production', '.env.test', '.env.example'];\n const files: EnvFile[] = [];\n\n for (const name of envFileNames) {\n const filePath = join(projectPath, name);\n const content = await readFileSafe(filePath);\n if (content !== undefined) {\n files.push({\n path: filePath,\n filename: name,\n variables: parseEnvFile(filePath),\n });\n }\n }\n\n return files;\n}\n\nexport async function parseAllEnvFiles(projectPath: string): Promise<EnvFile[]> {\n return findEnvFiles(projectPath);\n}\n","import fg from 'fast-glob';\nimport { readFile } from 'node:fs/promises';\nimport { Parser } from 'acorn';\nimport { fullAncestor } from 'acorn-walk';\nimport type { EnvReference, ScannerOptions } from '../types/index.js';\n\nconst DEFAULT_INCLUDE = [\n 'src/**/*.{js,ts,jsx,tsx}',\n 'lib/**/*.{js,ts}',\n 'pages/**/*.{js,ts,jsx,tsx}',\n 'app/**/*.{js,ts,jsx,tsx}',\n];\n\nconst DEFAULT_EXCLUDE = [\n '**/node_modules/**',\n '**/dist/**',\n '**/build/**',\n '**/.next/**',\n '**/*.test.{js,ts}',\n '**/*.spec.{js,ts}',\n];\n\nexport async function scanProject(\n projectPath: string,\n options: ScannerOptions = {}\n): Promise<EnvReference[]> {\n const include = options.include ?? DEFAULT_INCLUDE;\n const exclude = options.exclude ?? (options.checkTests ? DEFAULT_EXCLUDE.slice(0, -2) : DEFAULT_EXCLUDE);\n\n const files = await fg(include, {\n cwd: projectPath,\n ignore: exclude,\n absolute: true,\n });\n\n const references: EnvReference[] = [];\n for (const file of files) {\n const fileRefs = await scanFile(file);\n references.push(...fileRefs);\n }\n\n return references;\n}\n\nexport async function scanFile(filePath: string): Promise<EnvReference[]> {\n const references: EnvReference[] = [];\n const content = await readFile(filePath, 'utf-8');\n\n let ast: unknown;\n try {\n ast = Parser.parse(content, {\n ecmaVersion: 'latest',\n sourceType: 'module',\n allowReturnOutsideFunction: true,\n allowImportExportEverywhere: true,\n locations: true,\n });\n } catch {\n return references;\n }\n\n fullAncestor(ast as any, (node: any, _state: any, ancestors: any[]) => {\n if (node.type === 'MemberExpression') {\n const ref = extractEnvReference(node, ancestors, filePath, content);\n if (ref) references.push(ref);\n }\n\n if (node.type === 'ObjectPattern') {\n const refs = extractDestructuredEnvReferences(node, ancestors, filePath, content);\n references.push(...refs);\n }\n });\n\n return references;\n}\n\nfunction extractEnvReference(\n node: any,\n ancestors: any[],\n filePath: string,\n content: string\n): EnvReference | undefined {\n const key = getMemberExpressionKey(node);\n if (!key) return undefined;\n\n // fullAncestor includes the current node as the last ancestor\n const parent = ancestors.length > 1 ? ancestors[ancestors.length - 2] : undefined;\n const fallback = detectFallback(node, parent);\n\n return createReference(key, filePath, node, content, fallback);\n}\n\nfunction getMemberExpressionKey(node: any): string | undefined {\n // process.env.X or process.env['X'] (object is process.env)\n if (\n node.object?.type === 'MemberExpression' &&\n node.object.object?.name === 'process' &&\n node.object.property?.name === 'env'\n ) {\n if (node.property?.type === 'Identifier' && !node.computed) return node.property.name;\n if (node.property?.type === 'Literal' && typeof node.property.value === 'string') {\n return node.property.value;\n }\n if (node.property?.type === 'TemplateLiteral' && node.property.quasis?.length === 1) {\n return node.property.quasis[0].value.cooked ?? node.property.quasis[0].value.raw;\n }\n }\n\n // import.meta.env.X (static property access, not computed)\n if (\n node.object?.type === 'MemberExpression' &&\n node.object.object?.type === 'MetaProperty' &&\n node.object.object.meta?.name === 'import' &&\n node.object.object.property?.name === 'meta' &&\n node.object.property?.name === 'env' &&\n !node.computed\n ) {\n if (node.property?.type === 'Identifier') return node.property.name;\n if (node.property?.type === 'Literal' && typeof node.property.value === 'string') {\n return node.property.value;\n }\n }\n\n return undefined;\n}\n\nfunction extractDestructuredEnvReferences(\n node: any,\n ancestors: any[],\n filePath: string,\n content: string\n): EnvReference[] {\n if (!isEnvDestructuringPattern(node, ancestors)) return [];\n\n const references: EnvReference[] = [];\n for (const prop of node.properties) {\n if (prop.type === 'RestElement' && prop.argument?.type === 'Identifier') {\n references.push(\n createReference(prop.argument.name, filePath, node, content, { hasFallback: false })\n );\n continue;\n }\n\n if (prop.type !== 'Property') continue;\n\n if (prop.value?.type === 'Identifier') {\n references.push(\n createReference(prop.value.name, filePath, prop.value, content, { hasFallback: false })\n );\n }\n\n if (prop.value?.type === 'AssignmentPattern' && prop.value.left?.type === 'Identifier') {\n const key = prop.value.left.name;\n const fallbackValue = expressionToString(prop.value.right);\n references.push(\n createReference(key, filePath, prop.value.left, content, {\n hasFallback: true,\n fallbackValue,\n fallbackType: 'literal',\n })\n );\n }\n\n // Nested destructuring: const { env: { X } } = process\n if (prop.value?.type === 'ObjectPattern' && prop.key?.name === 'env') {\n const nested = extractDestructuredEnvReferences(\n prop.value,\n [...ancestors, prop, prop.value],\n filePath,\n content\n );\n references.push(...nested);\n }\n }\n\n return references;\n}\n\nfunction isEnvDestructuringPattern(node: any, ancestors: any[]): boolean {\n // fullAncestor includes the current node as the last ancestor\n const parent = ancestors.length > 1 ? ancestors[ancestors.length - 2] : undefined;\n if (!parent) return false;\n\n // const { X } = process.env\n if (parent.type === 'VariableDeclarator' && parent.id === node) {\n return isProcessEnvNode(parent.init) || isProcessNode(parent.init);\n }\n\n // const { X = 'd' } = process.env (AssignmentPattern wrapping ObjectPattern)\n if (parent.type === 'AssignmentPattern' && parent.left === node) {\n return isProcessEnvNode(parent.right) || isProcessNode(parent.right);\n }\n\n // const { env: { X } } = process\n if (parent.type === 'Property' && parent.value === node && parent.key?.name === 'env') {\n const grandParent = ancestors.length > 2 ? ancestors[ancestors.length - 3] : undefined;\n if (grandParent?.type === 'ObjectPattern') {\n return isEnvDestructuringPattern(grandParent, ancestors.slice(0, -2));\n }\n }\n\n return false;\n}\n\nfunction isProcessNode(node: any): boolean {\n return node?.type === 'Identifier' && node.name === 'process';\n}\n\nfunction isProcessEnvNode(node: any): boolean {\n if (!node) return false;\n\n // process.env\n if (\n node.type === 'MemberExpression' &&\n node.object?.type === 'Identifier' &&\n node.object.name === 'process' &&\n node.property?.name === 'env'\n ) {\n return true;\n }\n\n return false;\n}\n\nfunction detectFallback(node: any, parent: any): FallbackInfo {\n if (!parent) return { hasFallback: false };\n\n if (parent.type === 'LogicalExpression') {\n const isLeft = parent.left === node;\n const isRight = parent.right === node;\n\n if (parent.operator === '||' || parent.operator === '??') {\n const fallbackNode = isLeft ? parent.right : isRight ? parent.left : undefined;\n if (fallbackNode) {\n return {\n hasFallback: true,\n fallbackValue: expressionToString(fallbackNode),\n fallbackType: getFallbackType(fallbackNode),\n };\n }\n }\n\n if (parent.operator === '&&' && isLeft) {\n return { hasFallback: true, fallbackValue: 'guarded', fallbackType: 'expression' };\n }\n }\n\n if (parent.type === 'ConditionalExpression' && parent.test === node) {\n return {\n hasFallback: true,\n fallbackValue: expressionToString(parent.alternate),\n fallbackType: 'ternary',\n };\n }\n\n return { hasFallback: false };\n}\n\nfunction getFallbackType(node: any): 'literal' | 'function' | 'expression' {\n if (node.type === 'Literal' || node.type === 'TemplateLiteral') return 'literal';\n if (node.type === 'CallExpression' || node.type === 'NewExpression') return 'function';\n return 'expression';\n}\n\nfunction expressionToString(node: any): string {\n if (!node) return 'unknown';\n\n if (node.type === 'Literal') {\n return typeof node.value === 'string' ? `'${node.value}'` : String(node.value);\n }\n\n if (node.type === 'Identifier') return node.name;\n\n if (node.type === 'TemplateLiteral') {\n if (node.quasis.length === 1) return `'${node.quasis[0].value.cooked ?? node.quasis[0].value.raw}'`;\n return '`template`';\n }\n\n if (node.type === 'CallExpression' || node.type === 'NewExpression') {\n const callee = node.callee?.name ?? node.callee?.property?.name ?? 'function';\n return `<${callee}()>`;\n }\n\n if (node.type === 'UnaryExpression') {\n return `${node.operator}${expressionToString(node.argument)}`;\n }\n\n if (node.type === 'BinaryExpression') {\n return `${expressionToString(node.left)} ${node.operator} ${expressionToString(node.right)}`;\n }\n\n if (node.type === 'MemberExpression') {\n return node.property?.name ?? 'member';\n }\n\n return '<expression>';\n}\n\ninterface FallbackInfo {\n hasFallback: boolean;\n fallbackValue?: string;\n fallbackType?: 'literal' | 'function' | 'expression' | 'ternary';\n}\n\nfunction createReference(\n key: string,\n filePath: string,\n node: any,\n content: string,\n fallback: FallbackInfo\n): EnvReference {\n const line = node.loc?.start?.line ?? 0;\n const column = node.loc?.start?.column ?? 0;\n const context = extractContext(content, line);\n\n return {\n key,\n file: filePath,\n line,\n column,\n context,\n hasFallback: fallback.hasFallback,\n fallbackValue: fallback.fallbackValue,\n fallbackType: fallback.fallbackType,\n };\n}\n\nfunction extractContext(content: string, line: number): string {\n const lines = content.split('\\n');\n const start = Math.max(0, line - 2);\n const end = Math.min(lines.length, line + 1);\n return lines.slice(start, end).join('\\n');\n}\n","import type { EnvFile, EnvReference, Issue, ScoreResult, Grade } from '../types/index.js';\nimport { isEnvFileGitignored } from '../utils/git.js';\n\nconst ALWAYS_ALLOWED = ['PORT', 'HOST', 'NODE_ENV', 'CI', 'PWD'];\n\nexport async function analyze(\n envFiles: EnvFile[],\n references: EnvReference[],\n projectPath: string\n): Promise<Issue[]> {\n const issues: Issue[] = [];\n\n issues.push(...(await checkMissingVariables(envFiles, references)));\n issues.push(...checkUnusedVariables(envFiles, references));\n issues.push(...checkSecretRisks(envFiles));\n issues.push(...(await checkGitignore(envFiles, projectPath)));\n issues.push(...checkExampleFile(envFiles));\n\n return issues;\n}\n\nasync function checkMissingVariables(envFiles: EnvFile[], references: EnvReference[]): Promise<Issue[]> {\n const definedKeys = new Set(envFiles.flatMap((file) => file.variables.map((v) => v.key)));\n const issues: Issue[] = [];\n\n for (const ref of references) {\n if (definedKeys.has(ref.key)) continue;\n\n if (ref.hasFallback) {\n issues.push({\n type: 'missing',\n severity: 'warning',\n key: ref.key,\n message: `${ref.key} is not defined in .env but has a fallback default: ${ref.fallbackValue ?? 'unknown'}`,\n file: ref.file,\n line: ref.line,\n suggestion: `Consider adding ${ref.key} to .env for explicit configuration, or keep the fallback`,\n hasFallback: true,\n fallbackValue: ref.fallbackValue,\n });\n } else {\n issues.push({\n type: 'missing',\n severity: 'error',\n key: ref.key,\n message: `${ref.key} referenced in ${ref.file}:${ref.line} but not defined in any .env file (no fallback)`,\n file: ref.file,\n line: ref.line,\n suggestion: `Add ${ref.key} to .env and .env.example`,\n });\n }\n }\n\n return issues;\n}\n\nfunction checkUnusedVariables(envFiles: EnvFile[], references: EnvReference[]): Issue[] {\n const referencedKeys = new Set(references.map((r) => r.key));\n const issues: Issue[] = [];\n\n for (const file of envFiles) {\n if (file.filename === '.env.example' || file.filename === '.env.test') continue;\n\n for (const variable of file.variables) {\n if (ALWAYS_ALLOWED.includes(variable.key)) continue;\n if (referencedKeys.has(variable.key)) continue;\n\n issues.push({\n type: 'unused',\n severity: 'warning',\n key: variable.key,\n message: `${variable.key} is defined in ${file.filename} but never used in code`,\n file: file.filename,\n line: variable.line,\n suggestion: 'Remove if no longer needed, or use it in your code',\n });\n }\n }\n\n return issues;\n}\n\nfunction checkSecretRisks(envFiles: EnvFile[]): Issue[] {\n const issues: Issue[] = [];\n\n for (const file of envFiles) {\n if (file.filename !== '.env') continue;\n\n for (const variable of file.variables) {\n if (!variable.isSecret) continue;\n\n issues.push({\n type: 'secret-risk',\n severity: 'error',\n key: variable.key,\n message: `${variable.key} looks like a secret but is in .env instead of .env.local`,\n file: file.filename,\n line: variable.line,\n suggestion: `Move ${variable.key} to .env.local (which should be in .gitignore)`,\n });\n }\n }\n\n return issues;\n}\n\nasync function checkGitignore(envFiles: EnvFile[], projectPath: string): Promise<Issue[]> {\n const issues: Issue[] = [];\n const filenames = envFiles.map((f) => f.filename);\n\n const filesToCheck = ['.env', '.env.local', '.env.production'];\n const messages: Record<string, string> = {\n '.env': '.env is not in .gitignore — risk of committing secrets',\n '.env.local': '.env.local is not in .gitignore',\n '.env.production': '.env.production is not in .gitignore — production secrets at risk',\n };\n const suggestions: Record<string, string> = {\n '.env': \"echo '.env' >> .gitignore\",\n '.env.local': \"echo '.env.local' >> .gitignore\",\n '.env.production': \"echo '.env.production' >> .gitignore\",\n };\n\n for (const filename of filesToCheck) {\n if (!filenames.includes(filename)) continue;\n const ignored = await isEnvFileGitignored(projectPath, filename);\n if (!ignored) {\n issues.push({\n type: 'gitignore',\n severity: 'error',\n key: filename,\n message: messages[filename],\n file: '.gitignore',\n suggestion: suggestions[filename],\n });\n }\n }\n\n return issues;\n}\n\nfunction checkExampleFile(envFiles: EnvFile[]): Issue[] {\n const issues: Issue[] = [];\n const exampleFile = envFiles.find((f) => f.filename === '.env.example');\n const envFile = envFiles.find((f) => f.filename === '.env');\n\n if (!exampleFile) {\n issues.push({\n type: 'suspicious',\n severity: 'warning',\n key: '.env.example',\n message: \".env.example is missing — new developers won't know what env vars are needed\",\n file: '.',\n suggestion: 'Run `enve generate-example` to create one',\n });\n return issues;\n }\n\n if (!envFile) return issues;\n\n const exampleKeys = new Set(exampleFile.variables.map((v) => v.key));\n for (const variable of envFile.variables) {\n if (exampleKeys.has(variable.key)) continue;\n\n issues.push({\n type: 'suspicious',\n severity: 'warning',\n key: variable.key,\n message: `${variable.key} is in .env but missing from .env.example`,\n file: '.env.example',\n suggestion: `Add ${variable.key}=your_${variable.key} to .env.example`,\n });\n }\n\n return issues;\n}\n\nexport function calculateScore(issues: Issue[]): ScoreResult {\n let score = 100;\n let errorCount = 0;\n let warningCount = 0;\n let infoCount = 0;\n let unusedDeduction = 0;\n let exampleDeduction = 0;\n\n for (const issue of issues) {\n switch (issue.severity) {\n case 'error':\n errorCount++;\n break;\n case 'warning':\n warningCount++;\n break;\n case 'info':\n infoCount++;\n break;\n }\n\n switch (issue.type) {\n case 'missing':\n score -= issue.hasFallback ? 3 : 10;\n break;\n case 'secret-risk':\n score -= 8;\n break;\n case 'gitignore':\n if (issue.key === '.env.production') score -= 25;\n else score -= 20;\n break;\n case 'syntax-error':\n score -= 15;\n break;\n case 'invalid':\n score -= 5;\n break;\n case 'unused':\n unusedDeduction = Math.min(unusedDeduction + 3, 15);\n break;\n case 'suspicious':\n if (issue.key === '.env.example') score -= 10;\n else exampleDeduction = Math.min(exampleDeduction + 2, 10);\n break;\n }\n }\n\n score -= unusedDeduction + exampleDeduction;\n score = Math.max(0, Math.min(100, score));\n\n return {\n score,\n grade: getGrade(score),\n totalIssues: issues.length,\n errorCount,\n warningCount,\n infoCount,\n };\n}\n\nfunction getGrade(score: number): Grade {\n if (score >= 90) return 'Excellent';\n if (score >= 70) return 'Good';\n if (score >= 50) return 'Needs attention';\n return 'Critical';\n}\n","import Conf from 'conf';\nimport { readFile } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport type { EnveConfig, TrackedProject } from '../types/index.js';\n\nconst DEFAULT_CONFIG: EnveConfig = {\n version: 1,\n projects: [],\n};\n\nfunction createConfig(): Conf<EnveConfig> {\n return new Conf<EnveConfig>({\n projectName: 'enve',\n defaults: DEFAULT_CONFIG,\n });\n}\n\nexport function getProjects(): TrackedProject[] {\n const config = createConfig();\n return config.get('projects', []);\n}\n\nexport function isTracked(projectPath: string): boolean {\n return getProjects().some((p) => p.path === projectPath);\n}\n\nexport function getProject(projectPath: string): TrackedProject | undefined {\n return getProjects().find((p) => p.path === projectPath);\n}\n\nexport async function addProject(projectPath: string): Promise<TrackedProject> {\n const config = createConfig();\n\n if (isTracked(projectPath)) {\n return getProject(projectPath)!;\n }\n\n const name = await detectProjectName(projectPath);\n const project: TrackedProject = {\n name,\n path: projectPath,\n addedAt: new Date().toISOString(),\n };\n\n const projects = config.get('projects', []);\n config.set('projects', [...projects, project]);\n\n return project;\n}\n\nexport function removeProject(projectPath: string): boolean {\n const config = createConfig();\n const projects = config.get('projects', []);\n const before = projects.length;\n const filtered = projects.filter((p) => p.path !== projectPath);\n\n if (filtered.length === before) return false;\n\n config.set('projects', filtered);\n return true;\n}\n\nasync function detectProjectName(projectPath: string): Promise<string> {\n try {\n const pkgRaw = await readFile(join(projectPath, 'package.json'), 'utf-8');\n const pkg = JSON.parse(pkgRaw) as { name?: string };\n if (pkg.name) return pkg.name;\n } catch {\n // fall through to directory name\n }\n return projectPath.split('/').pop() ?? 'unknown';\n}\n","import chalk from 'chalk';\n\nexport const symbols = {\n error: '✗',\n warning: '⚠',\n success: '✓',\n info: 'ℹ',\n bullet: '•',\n arrow: '→',\n pointer: '▸',\n};\n\nexport function error(message: string): string {\n return `${chalk.red(symbols.error)} ${message}`;\n}\n\nexport function warning(message: string): string {\n return `${chalk.yellow(symbols.warning)} ${message}`;\n}\n\nexport function success(message: string): string {\n return `${chalk.green(symbols.success)} ${message}`;\n}\n\nexport function info(message: string): string {\n return `${chalk.blue(symbols.info)} ${message}`;\n}\n\nexport function bullet(message: string): string {\n return ` ${symbols.bullet} ${message}`;\n}\n\nexport function indent(message: string, level = 1): string {\n return ' '.repeat(level) + message;\n}\n\nexport function header(title: string): string {\n return chalk.bold(title);\n}\n\nexport function dim(message: string): string {\n return chalk.dim(message);\n}\n","import { Command } from 'commander';\nimport chalk from 'chalk';\nimport { detectProject, getProjectInfo } from '../../core/project.js';\nimport { parseAllEnvFiles } from '../../core/parser.js';\nimport { error, warning, success, indent, header } from '../../utils/logger.js';\nimport type { Issue } from '../../types/index.js';\n\nexport const doctorCommand = new Command('doctor')\n .description('Comprehensive health check with detailed recommendations')\n .action(async () => {\n const cwd = process.cwd();\n const projectPath = await detectProject(cwd);\n\n if (!projectPath) {\n console.log(error('No package.json found. Are you in a project directory?'));\n process.exit(1);\n }\n\n await runDoctor(projectPath);\n });\n\nexport async function runDoctor(projectPath: string): Promise<void> {\n const info = await getProjectInfo(projectPath);\n const envFiles = await parseAllEnvFiles(projectPath);\n\n console.log();\n console.log(indent(header(`Enve Health Check — ${info.name}`)));\n console.log(indent(chalk.dim(info.path)));\n console.log();\n\n const fileStructureIssues = info.issues.filter(\n (i) => i.type === 'gitignore' || (i.type === 'suspicious' && i.key === '.env.example')\n );\n const variableIssues = info.issues.filter((i) => i.type === 'missing' || i.type === 'unused');\n const securityIssues = info.issues.filter((i) => i.type === 'secret-risk');\n const validationIssues = info.issues.filter((i) => i.type === 'invalid' || i.type === 'syntax-error');\n\n printSection('[File Structure]', fileStructureIssues);\n printVariablesSection(variableIssues, envFiles);\n printSection('[Security]', securityIssues);\n printSection('[Validation]', validationIssues);\n printRecommendations(info.issues);\n\n console.log();\n console.log(indent(`Score: ${formatScore(info.score.score)} [ ${info.score.grade} ]`));\n console.log(indent(chalk.dim(`${info.score.errorCount} errors, ${info.score.warningCount} warnings, ${info.score.infoCount} info`)));\n console.log();\n}\n\nfunction printSection(title: string, issues: Issue[]): void {\n console.log(indent(header(title)));\n if (issues.length === 0) {\n console.log(indent(success('No issues')));\n } else {\n for (const issue of issues) {\n const formatter = issue.severity === 'error' ? error : issue.severity === 'warning' ? warning : success;\n console.log(indent(formatter(issue.message), 2));\n if (issue.suggestion) {\n console.log(indent(chalk.dim(`→ ${issue.suggestion}`), 3));\n }\n }\n }\n console.log();\n}\n\nfunction printVariablesSection(issues: Issue[], envFiles: Awaited<ReturnType<typeof import('../../core/parser.js').parseAllEnvFiles>>): void {\n const totalDefined = envFiles.reduce((sum, f) => sum + f.variables.length, 0);\n console.log(indent(header(`[Variables — ${totalDefined} defined]`)));\n\n if (issues.length === 0) {\n console.log(indent(success('All variables are used and defined')));\n } else {\n for (const issue of issues) {\n const formatter = issue.severity === 'error' ? error : warning;\n let message = issue.message;\n if (issue.type === 'missing' && issue.hasFallback && issue.fallbackValue) {\n message += chalk.dim(` (fallback: ${issue.fallbackValue})`);\n }\n console.log(indent(formatter(message), 2));\n if (issue.suggestion) {\n console.log(indent(chalk.dim(`→ ${issue.suggestion}`), 3));\n }\n }\n }\n console.log();\n}\n\nfunction printRecommendations(issues: Issue[]): void {\n const actionable = issues.filter((i) => i.suggestion);\n if (actionable.length === 0) return;\n\n console.log(indent(header(`[Recommendations — ${actionable.length} actions needed]`)));\n\n const high = actionable.filter((i) => i.severity === 'error');\n const medium = actionable.filter((i) => i.severity === 'warning');\n const low = actionable.filter((i) => i.severity === 'info');\n\n if (high.length > 0) {\n console.log(indent(chalk.red.bold('HIGH PRIORITY:')));\n high.forEach((issue, idx) => {\n console.log(indent(`${idx + 1}. ${issue.suggestion}`, 2));\n });\n console.log();\n }\n\n if (medium.length > 0) {\n console.log(indent(chalk.yellow.bold('MEDIUM PRIORITY:')));\n medium.forEach((issue, idx) => {\n console.log(indent(`${idx + 1}. ${issue.suggestion}`, 2));\n });\n console.log();\n }\n\n if (low.length > 0) {\n console.log(indent(chalk.blue.bold('LOW PRIORITY:')));\n low.forEach((issue, idx) => {\n console.log(indent(`${idx + 1}. ${issue.suggestion}`, 2));\n });\n console.log();\n }\n}\n\nfunction formatScore(score: number): string {\n const text = `${score}/100`;\n if (score >= 90) return chalk.bgGreen.black(` ${text} `);\n if (score >= 70) return chalk.bgYellow.black(` ${text} `);\n if (score >= 50) return chalk.red(text);\n return chalk.bgRed.white(` ${text} `);\n}\n","import { Command } from 'commander';\nimport chalk from 'chalk';\nimport { detectProject } from '../../core/project.js';\nimport { parseAllEnvFiles } from '../../core/parser.js';\nimport { scanProject } from '../../core/scanner.js';\nimport { readFileSafe, writeFileSafe } from '../../utils/fs.js';\nimport { copyFile } from 'node:fs/promises';\nimport { error, warning, success, indent, header } from '../../utils/logger.js';\nimport { confirm } from '../../utils/prompt.js';\n\nexport const unusedCommand = new Command('unused')\n .description('Find environment variables defined in .env files but never referenced in code')\n .option('--fix', 'Remove unused variables from .env files')\n .option('--dry-run', 'Show what would be removed without making changes')\n .option('--include-commons', 'Also flag PORT, HOST, NODE_ENV if truly unused')\n .action(async (options) => {\n const cwd = process.cwd();\n const projectPath = await detectProject(cwd);\n\n if (!projectPath) {\n console.log(error('No package.json found. Are you in a project directory?'));\n process.exit(1);\n }\n\n const envFiles = await parseAllEnvFiles(projectPath);\n const references = await scanProject(projectPath);\n const referencedKeys = new Set(references.map((r) => r.key));\n\n const alwaysAllowed = options.includeCommons ? [] : ['PORT', 'HOST', 'NODE_ENV', 'CI', 'PWD'];\n\n type UnusedItem = { filename: string; path: string; key: string; line: number };\n const unused: UnusedItem[] = [];\n\n for (const file of envFiles) {\n if (file.filename === '.env.example' || file.filename === '.env.test') continue;\n for (const variable of file.variables) {\n if (alwaysAllowed.includes(variable.key)) continue;\n if (!referencedKeys.has(variable.key)) {\n unused.push({\n filename: file.filename,\n path: file.path,\n key: variable.key,\n line: variable.line,\n });\n }\n }\n }\n\n console.log();\n console.log(indent(header(`Unused variables in ${projectPath}`)));\n console.log();\n\n if (unused.length === 0) {\n console.log(indent(success('All variables are used')));\n return;\n }\n\n // Group by file\n const byFile = new Map<string, UnusedItem[]>();\n for (const item of unused) {\n byFile.set(item.filename, [...(byFile.get(item.filename) ?? []), item]);\n }\n\n for (const [filename, items] of byFile) {\n console.log(indent(filename));\n for (const item of items) {\n console.log(indent(warning(`${item.key.padEnd(20)} (line ${item.line})`), 2));\n }\n }\n\n console.log();\n console.log(indent(`${unused.length} unused variables found. They may be safe to remove.`));\n\n if (options.dryRun) {\n console.log(indent(chalk.dim('Dry run: no changes made.')));\n return;\n }\n\n if (!options.fix) {\n console.log(indent('Run `enve unused --fix` to remove them, or `--dry-run` to preview.'));\n return;\n }\n\n const confirmed = await confirm(`Remove ${unused.length} unused variables from .env files?`);\n if (!confirmed) {\n console.log(indent(chalk.dim('Aborted. No changes made.')));\n return;\n }\n\n // Group by file path for removal\n const byPath = new Map<string, UnusedItem[]>();\n for (const item of unused) {\n byPath.set(item.path, [...(byPath.get(item.path) ?? []), item]);\n }\n\n let removedCount = 0;\n for (const [filePath, items] of byPath) {\n const backupPath = `${filePath}.backup.${formatTimestamp(new Date())}`;\n await copyFile(filePath, backupPath);\n\n const content = (await readFileSafe(filePath)) ?? '';\n const lines = content.split('\\n');\n const linesToRemove = new Set(items.map((i) => i.line - 1));\n const newLines = lines.filter((_, idx) => !linesToRemove.has(idx));\n await writeFileSafe(filePath, newLines.join('\\n'));\n\n removedCount += items.length;\n console.log(indent(chalk.dim(`Backup saved: ${backupPath}`), 2));\n }\n\n console.log();\n console.log(indent(success(`Removed ${removedCount} unused variables.`)));\n });\n\nfunction formatTimestamp(date: Date): string {\n return date.toISOString().replace(/[:.]/g, '-').slice(0, 19);\n}\n","import { createInterface } from 'node:readline/promises';\n\nexport async function prompt(message: string): Promise<string> {\n const rl = createInterface({\n input: process.stdin,\n output: process.stdout,\n });\n\n try {\n const answer = await rl.question(`${message} `);\n return answer.trim();\n } finally {\n rl.close();\n }\n}\n\nexport async function confirm(message: string, defaultValue = false): Promise<boolean> {\n const suffix = defaultValue ? ' (Y/n)' : ' (y/N)';\n const answer = await prompt(`${message}${suffix}`);\n if (!answer) return defaultValue;\n return /^y(es)?$/i.test(answer);\n}\n","import { Command } from 'commander';\nimport chalk from 'chalk';\nimport { join } from 'node:path';\nimport { detectProject } from '../../core/project.js';\nimport { parseAllEnvFiles } from '../../core/parser.js';\nimport { scanProject } from '../../core/scanner.js';\nimport { readFileSafe, writeFileSafe } from '../../utils/fs.js';\nimport { error, warning, success, indent, header } from '../../utils/logger.js';\nimport { prompt, confirm } from '../../utils/prompt.js';\n\nconst STANDARD_NODE_VARS = new Set([\n 'PATH',\n 'HOME',\n 'USER',\n 'SHELL',\n 'PWD',\n 'OLDPWD',\n 'NODE',\n 'NODE_PATH',\n 'TMPDIR',\n 'LANG',\n 'LOGNAME',\n 'TERM',\n 'EDITOR',\n 'SHLVL',\n '_',\n '__CF_USER_TEXT_ENCODING',\n]);\n\nexport const missingCommand = new Command('missing')\n .description('Find process.env references in code that are not defined in any .env file')\n .option('--include-fallbacks', 'Also show variables that have fallback defaults')\n .option('--no-fallbacks', 'Hide variables with fallback defaults')\n .option('--add', 'Interactively add missing variables to .env')\n .action(async (options) => {\n const cwd = process.cwd();\n const projectPath = await detectProject(cwd);\n\n if (!projectPath) {\n console.log(error('No package.json found. Are you in a project directory?'));\n process.exit(1);\n }\n\n const envFiles = await parseAllEnvFiles(projectPath);\n const references = await scanProject(projectPath);\n const definedKeys = new Set(envFiles.flatMap((f) => f.variables.map((v) => v.key)));\n\n const missingNoFallback = references.filter(\n (r) => !definedKeys.has(r.key) && !r.hasFallback && !STANDARD_NODE_VARS.has(r.key)\n );\n const missingWithFallback = options.fallbacks\n ? references.filter((r) => !definedKeys.has(r.key) && r.hasFallback && !STANDARD_NODE_VARS.has(r.key))\n : [];\n\n console.log();\n console.log(indent(header(`Missing environment variables in ${projectPath}`)));\n console.log();\n\n if (missingNoFallback.length === 0 && missingWithFallback.length === 0) {\n console.log(indent(success('All referenced variables are defined')));\n return;\n }\n\n if (missingNoFallback.length > 0) {\n console.log(indent(header('Missing (no fallback — WILL cause runtime errors)')));\n for (const ref of missingNoFallback) {\n console.log(indent(error(`${ref.key}`), 2));\n console.log(indent(chalk.dim(`→ ${ref.file}:${ref.line}`), 3));\n if (ref.context) {\n console.log(indent(chalk.dim(ref.context.split('\\n').map((l) => ` ${l}`).join('\\n')), 3));\n }\n }\n console.log();\n }\n\n if (missingWithFallback.length > 0) {\n console.log(indent(header('Missing (has fallback — will use default value)')));\n for (const ref of missingWithFallback) {\n console.log(indent(warning(`${ref.key}`), 2));\n console.log(indent(chalk.dim(`→ fallback: ${ref.fallbackValue ?? 'unknown'}`), 3));\n console.log(indent(chalk.dim(`→ ${ref.file}:${ref.line}`), 3));\n }\n console.log();\n }\n\n console.log(\n indent(\n `${error(`${missingNoFallback.length} truly missing`)} · ${warning(\n `${missingWithFallback.length} with fallback defaults`\n )}`\n )\n );\n\n if (options.add) {\n await addMissingVariables(projectPath, envFiles, missingNoFallback);\n } else {\n console.log();\n console.log(indent('Run with `--add` to interactively add missing variables to .env'));\n }\n });\n\nasync function addMissingVariables(\n projectPath: string,\n envFiles: Awaited<ReturnType<typeof parseAllEnvFiles>>,\n missing: Awaited<ReturnType<typeof scanProject>>\n): Promise<void> {\n const envPath = join(projectPath, '.env');\n const envFile = envFiles.find((f) => f.filename === '.env');\n\n if (!envFile) {\n console.log(error('No .env file found. Cannot add variables.'));\n return;\n }\n\n const updates: string[] = [];\n const exampleUpdates: string[] = [];\n\n for (const ref of missing) {\n const value = await prompt(`Value for ${ref.key}:`);\n if (!value) continue;\n\n updates.push(`${ref.key}=${value}`);\n\n const addToExample = await confirm(`Add ${ref.key} to .env.example?`, false);\n if (addToExample) {\n exampleUpdates.push(`${ref.key}=your_${ref.key.toLowerCase()}`);\n }\n }\n\n if (updates.length === 0) {\n console.log(indent(chalk.dim('No variables added.')));\n return;\n }\n\n const existing = (await readFileSafe(envPath)) ?? '';\n const separator = existing.endsWith('\\n') || existing === '' ? '' : '\\n';\n await writeFileSafe(envPath, `${existing}${separator}${updates.join('\\n')}\\n`);\n\n if (exampleUpdates.length > 0) {\n const examplePath = join(projectPath, '.env.example');\n const exampleExisting = (await readFileSafe(examplePath)) ?? '';\n const exampleSeparator = exampleExisting.endsWith('\\n') || exampleExisting === '' ? '' : '\\n';\n await writeFileSafe(examplePath, `${exampleExisting}${exampleSeparator}${exampleUpdates.join('\\n')}\\n`);\n }\n\n console.log();\n console.log(indent(success(`Added ${updates.length} variables to .env`)));\n if (exampleUpdates.length > 0) {\n console.log(indent(success(`Added ${exampleUpdates.length} placeholders to .env.example`)));\n }\n}\n","import { Command } from 'commander';\nimport chalk from 'chalk';\nimport { detectProject } from '../../core/project.js';\nimport { parseAllEnvFiles } from '../../core/parser.js';\nimport {\n validatePort,\n validateUrl,\n validateNodeEnv,\n validateSecret,\n validateBoolean,\n} from '../../utils/validators.js';\nimport { readFileSafe, writeFileSafe } from '../../utils/fs.js';\nimport { copyFile } from 'node:fs/promises';\nimport { error, success, indent, header } from '../../utils/logger.js';\nimport { confirm } from '../../utils/prompt.js';\nimport type { EnvVariable } from '../../types/index.js';\n\nexport const validateCommand = new Command('validate')\n .description('Validate the values of environment variables against expected formats')\n .option('--fix', 'Automatically fix simple issues')\n .action(async (options) => {\n const cwd = process.cwd();\n const projectPath = await detectProject(cwd);\n\n if (!projectPath) {\n console.log(error('No package.json found. Are you in a project directory?'));\n process.exit(1);\n }\n\n const envFiles = await parseAllEnvFiles(projectPath);\n const envFile = envFiles.find((f) => f.filename === '.env');\n\n if (!envFile) {\n console.log(error('No .env file found. Cannot validate.'));\n process.exit(1);\n }\n\n const variables = envFile.variables;\n\n console.log();\n console.log(indent(header(`Validating environment variables — ${envFile.filename}`)));\n console.log();\n\n let errorCount = 0;\n let warningCount = 0;\n const fixes: Array<{ key: string; oldValue: string; newValue: string }> = [];\n\n for (const variable of variables) {\n const result = validateVariable(variable);\n if (result.valid) {\n console.log(indent(success(`${variable.key}=${truncate(variable.value)}`)));\n } else {\n errorCount++;\n console.log(indent(error(`${variable.key}=${truncate(variable.value)}`)));\n if (result.message) console.log(indent(chalk.dim(` ${result.message}`), 2));\n if (result.suggestion) console.log(indent(chalk.dim(` Suggestion: ${result.suggestion}`), 2));\n\n const fixedValue = getFixedValue(variable);\n if (fixedValue !== undefined) {\n console.log(indent(chalk.yellow(` Auto-fix available: ${variable.key}=${fixedValue}`), 2));\n fixes.push({ key: variable.key, oldValue: variable.value, newValue: fixedValue });\n }\n }\n }\n\n console.log();\n console.log(indent(`${variables.length} variables checked, ${errorCount} errors, ${warningCount} warnings`));\n\n if (options.fix) {\n const safeFixes = fixes.filter((f) => !isSecretKey(f.key));\n if (safeFixes.length === 0) {\n console.log(indent(chalk.dim('No auto-fixable issues (secrets are never auto-fixed).')));\n return;\n }\n\n const confirmed = await confirm(`Apply ${safeFixes.length} auto-fixes to .env?`);\n if (!confirmed) {\n console.log(indent(chalk.dim('Aborted. No changes made.')));\n return;\n }\n\n await applyFixes(envFile.path, safeFixes);\n console.log();\n console.log(indent(success(`Applied ${safeFixes.length} fixes to .env`)));\n } else if (fixes.length > 0) {\n console.log(indent('Run with `--fix` to apply available auto-fixes.'));\n }\n });\n\nexport function validateVariable(variable: EnvVariable): { valid: boolean; message?: string; suggestion?: string } {\n const key = variable.key;\n const value = variable.value;\n\n if (key === 'PORT' || key.endsWith('_PORT')) {\n return validatePort(value);\n }\n\n if (key === 'NODE_ENV') {\n return validateNodeEnv(value);\n }\n\n if (key.endsWith('_URL') || key === 'DATABASE_URL' || key === 'REDIS_URL') {\n return validateUrl(value);\n }\n\n if (isSecretKey(key)) {\n return validateSecret(value, key);\n }\n\n if (key === 'DEBUG' || key === 'VERBOSE' || key.endsWith('_ENABLED')) {\n return validateBoolean(value, key);\n }\n\n return { valid: true };\n}\n\nfunction isSecretKey(key: string): boolean {\n return /SECRET|KEY|TOKEN|PRIVATE/i.test(key) && key !== 'API_KEY';\n}\n\nfunction getFixedValue(variable: EnvVariable): string | undefined {\n const key = variable.key;\n const value = variable.value;\n\n // URL missing protocol\n if (key.endsWith('_URL') || key === 'DATABASE_URL' || key === 'REDIS_URL') {\n if (!value.includes('://') && !value.startsWith('http')) {\n return `https://${value}`;\n }\n }\n\n // NODE_ENV shorthand\n if (key === 'NODE_ENV') {\n if (value === 'dev') return 'development';\n if (value === 'prod') return 'production';\n }\n\n // Boolean normalization\n if (key === 'DEBUG' || key === 'VERBOSE' || key.endsWith('_ENABLED')) {\n const normalized = value.toLowerCase();\n if (normalized === 'yes' || normalized === '1' || normalized === 'on') return 'true';\n if (normalized === 'no' || normalized === '0' || normalized === 'off') return 'false';\n }\n\n return undefined;\n}\n\nasync function applyFixes(filePath: string, fixes: Array<{ key: string; newValue: string }>): Promise<void> {\n const backupPath = `${filePath}.backup.validate.${formatTimestamp(new Date())}`;\n await copyFile(filePath, backupPath);\n\n const content = (await readFileSafe(filePath)) ?? '';\n const fixMap = new Map(fixes.map((f) => [f.key, f.newValue]));\n\n const lines = content.split('\\n').map((line) => {\n const trimmed = line.trim();\n if (!trimmed || trimmed.startsWith('#')) return line;\n\n const equalIndex = trimmed.indexOf('=');\n if (equalIndex === -1) return line;\n\n let key = trimmed.slice(0, equalIndex).trim();\n if (key.startsWith('export ')) key = key.slice(7).trim();\n\n if (fixMap.has(key)) {\n const prefix = line.match(/^\\s*/)?.[0] ?? '';\n const suffix = line.slice(line.indexOf('=') + 1);\n const commentIndex = suffix.indexOf(' #');\n const comment = commentIndex !== -1 ? suffix.slice(commentIndex) : '';\n const rawValue = commentIndex !== -1 ? suffix.slice(0, commentIndex) : suffix;\n const leadingSpaces = rawValue.match(/^\\s*/)?.[0] ?? '';\n return `${prefix}${key}=${leadingSpaces}${fixMap.get(key)}${comment}`;\n }\n\n return line;\n });\n\n await writeFileSafe(filePath, lines.join('\\n'));\n console.log(indent(chalk.dim(`Backup saved: ${backupPath}`), 2));\n}\n\nfunction formatTimestamp(date: Date): string {\n return date.toISOString().replace(/[:.]/g, '-').slice(0, 19);\n}\n\nfunction truncate(value: string, max = 40): string {\n if (value.length <= max) return value;\n return value.slice(0, max) + '...';\n}\n","export interface ValidationResult {\n valid: boolean;\n message?: string;\n suggestion?: string;\n}\n\nexport function validatePort(value: string): ValidationResult {\n const port = Number(value);\n if (!Number.isInteger(port) || port < 1 || port > 65535) {\n return {\n valid: false,\n message: `PORT must be a number between 1 and 65535`,\n suggestion: `PORT=3000`,\n };\n }\n return { valid: true };\n}\n\nexport function validateUrl(value: string): ValidationResult {\n if (!value.includes('://')) {\n return {\n valid: false,\n message: 'Invalid URL — missing protocol',\n suggestion: `Did you mean https://${value} or a protocol-specific URL?`,\n };\n }\n\n try {\n const url = new URL(value);\n if (!url.protocol || url.protocol === ':') {\n throw new Error('Missing protocol');\n }\n return { valid: true };\n } catch {\n return {\n valid: false,\n message: 'Invalid URL',\n suggestion: `Check the URL format`,\n };\n }\n}\n\nexport function validateNodeEnv(value: string): ValidationResult {\n const allowed = ['development', 'production', 'test'];\n if (!allowed.includes(value)) {\n return {\n valid: false,\n message: `NODE_ENV should be 'development', 'production', or 'test'`,\n suggestion: `NODE_ENV=${value === 'dev' ? 'development' : value === 'prod' ? 'production' : 'development'}`,\n };\n }\n return { valid: true };\n}\n\nexport function validateSecret(value: string, key: string): ValidationResult {\n const forbidden = ['default', 'secret', 'test', 'password', '123456'];\n if (value.length < 16) {\n return {\n valid: false,\n message: `${key} is too short (${value.length} characters)`,\n suggestion: `Use at least 16 random characters. Generate one: node -e \"console.log(require('crypto').randomBytes(32).toString('hex'))\"`,\n };\n }\n if (forbidden.includes(value.toLowerCase())) {\n return {\n valid: false,\n message: `${key} uses a weak placeholder value`,\n suggestion: `Generate a strong random secret`,\n };\n }\n return { valid: true };\n}\n\nexport function validateBoolean(value: string, key: string): ValidationResult {\n const normalized = value.toLowerCase();\n if (normalized === 'true' || normalized === 'false') {\n return { valid: true };\n }\n const boolMap: Record<string, string> = {\n yes: 'true',\n no: 'false',\n '1': 'true',\n '0': 'false',\n on: 'true',\n off: 'false',\n };\n if (normalized in boolMap) {\n return {\n valid: false,\n message: `${key} should be 'true' or 'false' for boolean values`,\n suggestion: `${key}=${boolMap[normalized]}`,\n };\n }\n return {\n valid: false,\n message: `${key} is not a valid boolean value`,\n suggestion: `${key}=true`,\n };\n}\n\nexport function isSecretKey(key: string): boolean {\n const secretPatterns = /SECRET|KEY|TOKEN|PASSWORD|PRIVATE|ACCESS|CREDENTIAL|AUTH/i;\n return secretPatterns.test(key);\n}\n","import { Command } from 'commander';\nimport chalk from 'chalk';\nimport { join } from 'node:path';\nimport { detectProject } from '../../core/project.js';\nimport { parseEnvFile } from '../../core/parser.js';\nimport { readFileSafe, writeFileSafe } from '../../utils/fs.js';\nimport { isSecretKey } from '../../utils/validators.js';\nimport { error, success, indent, header, bullet } from '../../utils/logger.js';\nimport { confirm } from '../../utils/prompt.js';\n\nexport const generateExampleCommand = new Command('generate-example')\n .description('Automatically create or update .env.example from the current .env file')\n .option('--overwrite', 'Update existing .env.example without prompting')\n .option('--output <path>', 'Write to a different file', '.env.example')\n .option('--all-secrets', 'Redact ALL values')\n .option('--dry-run', 'Show what would be generated without writing files')\n .action(async (options) => {\n const cwd = process.cwd();\n const projectPath = await detectProject(cwd);\n\n if (!projectPath) {\n console.log(error('No package.json found. Are you in a project directory?'));\n process.exit(1);\n }\n\n const envPath = join(projectPath, '.env');\n const envContent = await readFileSafe(envPath);\n\n if (!envContent) {\n console.log(error('No .env file found. Cannot generate .env.example.'));\n process.exit(1);\n }\n\n const outputPath = join(projectPath, options.output);\n const existing = await readFileSafe(outputPath);\n const variables = parseEnvFile(envPath);\n\n const newLines: string[] = [];\n let redactedCount = 0;\n let copiedCount = 0;\n\n for (const variable of variables) {\n const shouldRedact = options.allSecrets || variable.isSecret || isSecretKey(variable.key);\n if (shouldRedact) {\n newLines.push(`${variable.key}=your_${variable.key.toLowerCase()}`);\n redactedCount++;\n } else {\n newLines.push(`${variable.key}=${variable.value}`);\n copiedCount++;\n }\n }\n\n const newContent = `${newLines.join('\\n')}\\n`;\n\n console.log();\n console.log(indent(header(`Generating ${options.output} from .env`)));\n console.log();\n console.log(indent(`Variables: ${variables.length} total`));\n console.log(indent(bullet(`${copiedCount} public variables → copied as-is`), 2));\n console.log(indent(bullet(`${redactedCount} secrets redacted`), 2));\n\n if (existing) {\n const diff = computeDiff(existing, newContent);\n\n if (diff.length === 0) {\n console.log();\n console.log(indent(success(`${options.output} is already up to date.`)));\n return;\n }\n\n console.log();\n console.log(indent(header('Changes since last generation:')));\n for (const change of diff) {\n const symbol = change.type === 'added' ? chalk.green('+') : change.type === 'removed' ? chalk.red('-') : chalk.yellow('~');\n console.log(indent(`${symbol} ${change.type}: ${change.key}`, 2));\n }\n\n if (options.dryRun) {\n console.log();\n console.log(indent(chalk.dim('Dry run — no changes made.')));\n return;\n }\n\n if (!options.overwrite) {\n const confirmed = await confirm(`Update ${options.output}?`);\n if (!confirmed) {\n console.log(indent(chalk.dim('Aborted. No changes made.')));\n return;\n }\n }\n } else if (options.dryRun) {\n console.log();\n console.log(indent(chalk.dim('Dry run — no changes made.')));\n }\n\n if (!options.dryRun) {\n await writeFileSafe(outputPath, newContent);\n\n console.log();\n console.log(indent(success(`Created ${options.output}`)));\n }\n });\n\ninterface DiffEntry {\n type: 'added' | 'removed' | 'updated';\n key: string;\n}\n\nfunction computeDiff(existingContent: string, newContent: string): DiffEntry[] {\n const existingVars = new Map<string, string>();\n const newVars = new Map<string, string>();\n\n for (const line of existingContent.split('\\n')) {\n const trimmed = line.trim();\n if (!trimmed || trimmed.startsWith('#')) continue;\n const idx = trimmed.indexOf('=');\n if (idx === -1) continue;\n existingVars.set(trimmed.slice(0, idx).trim(), trimmed.slice(idx + 1).trim());\n }\n\n for (const line of newContent.split('\\n')) {\n const trimmed = line.trim();\n if (!trimmed || trimmed.startsWith('#')) continue;\n const idx = trimmed.indexOf('=');\n if (idx === -1) continue;\n newVars.set(trimmed.slice(0, idx).trim(), trimmed.slice(idx + 1).trim());\n }\n\n const diff: DiffEntry[] = [];\n\n for (const [key, value] of newVars) {\n if (!existingVars.has(key)) {\n diff.push({ type: 'added', key });\n } else if (existingVars.get(key) !== value) {\n diff.push({ type: 'updated', key });\n }\n }\n\n for (const key of existingVars.keys()) {\n if (!newVars.has(key)) {\n diff.push({ type: 'removed', key });\n }\n }\n\n return diff;\n}\n","import { Command } from 'commander';\nimport { detectProject } from '../../core/project.js';\nimport {\n isGitRepo,\n isHookInstalled,\n installHook,\n uninstallHook,\n getPreCommitHookPath,\n} from '../../utils/git.js';\nimport { error, warning, success, indent } from '../../utils/logger.js';\n\nexport const hookCommand = new Command('hook')\n .description('Install, manage, and check a pre-commit git hook that prevents .env commits')\n .addCommand(\n new Command('install')\n .description('Install the enve pre-commit hook')\n .action(async () => {\n const cwd = process.cwd();\n const projectPath = await detectProject(cwd);\n\n if (!projectPath || !(await isGitRepo(projectPath))) {\n console.log(error('Not a git repository. Run `git init` first.'));\n process.exit(1);\n }\n\n await installHook(projectPath);\n console.log();\n console.log(indent(success('Pre-commit hook installed')));\n console.log(indent(`Location: ${getPreCommitHookPath(projectPath)}`));\n console.log();\n console.log(indent('What it does:'));\n console.log(indent(bullet('Blocks commits of .env, .env.local, .env.production files'), 2));\n console.log(indent(bullet('Shows a helpful message with next steps'), 2));\n console.log(indent(bullet('Can be bypassed with --no-verify (emergency only)'), 2));\n console.log();\n console.log(indent('To remove: enve hook uninstall'));\n })\n )\n .addCommand(\n new Command('status')\n .description('Check the status of the enve pre-commit hook')\n .action(async () => {\n const cwd = process.cwd();\n const projectPath = await detectProject(cwd);\n\n if (!projectPath || !(await isGitRepo(projectPath))) {\n console.log(error('Not a git repository.'));\n process.exit(1);\n }\n\n const installed = await isHookInstalled(projectPath);\n console.log();\n if (installed) {\n console.log(indent(success('Pre-commit hook: Installed (enve protection active)')));\n console.log(indent(`Location: ${getPreCommitHookPath(projectPath)}`));\n } else {\n console.log(indent(warning('Pre-commit hook: Not installed')));\n console.log(indent('Run `enve hook install` to enable .env commit protection.'));\n }\n })\n )\n .addCommand(\n new Command('uninstall')\n .description('Remove the enve pre-commit hook')\n .action(async () => {\n const cwd = process.cwd();\n const projectPath = await detectProject(cwd);\n\n if (!projectPath || !(await isGitRepo(projectPath))) {\n console.log(error('Not a git repository.'));\n process.exit(1);\n }\n\n const removed = await uninstallHook(projectPath);\n console.log();\n if (removed) {\n console.log(indent(success('Pre-commit hook removed')));\n console.log(indent('Run `enve hook install` to re-enable.'));\n } else {\n console.log(indent(warning('No enve hook found to remove.')));\n }\n })\n );\n\nfunction bullet(text: string): string {\n return `• ${text}`;\n}\n","import { Command } from 'commander';\nimport { render } from 'ink';\nimport React, { useState, useCallback } from 'react';\nimport { getProjects } from '../../core/config.js';\nimport { getProjectInfo } from '../../core/project.js';\nimport { runDoctor } from './doctor.js';\nimport { Dashboard } from '../../ui/Dashboard.js';\nimport type { ProjectInfo } from '../../types/index.js';\nimport pkg from '../../../package.json' with { type: 'json' };\n\nexport const dashboardCommand = new Command('dashboard')\n .description('Interactive terminal dashboard showing all tracked projects')\n .action(async () => {\n const tracked = getProjects();\n\n if (tracked.length === 0) {\n console.log();\n console.log(' Enve Dashboard');\n console.log();\n console.log(' No projects tracked yet.');\n console.log(' Run `enve scan` or `enve doctor` in a project to add it.');\n console.log();\n return;\n }\n\n if (!process.stdin.isTTY) {\n console.log();\n console.log(' The interactive dashboard requires a TTY terminal.');\n console.log(' Run `enve dashboard` in your terminal to use it.');\n console.log();\n return;\n }\n\n const initialProjects = await analyzeProjects(tracked.map((p) => p.path));\n\n render(\n <DashboardApp\n initialProjects={initialProjects}\n trackedPaths={tracked.map((p) => p.path)}\n version={pkg.version}\n />\n );\n });\n\ninterface DashboardAppProps {\n initialProjects: ProjectInfo[];\n trackedPaths: string[];\n version: string;\n}\n\nfunction DashboardApp({ initialProjects, trackedPaths, version }: DashboardAppProps): React.ReactElement {\n const [projects, setProjects] = useState(initialProjects);\n\n const handleRunDoctor = useCallback(\n async (project: ProjectInfo) => {\n await runDoctor(project.path);\n },\n []\n );\n\n const handleRefresh = useCallback(async () => {\n const fresh = await analyzeProjects(trackedPaths);\n setProjects(fresh);\n }, [trackedPaths]);\n\n return (\n <Dashboard\n projects={projects}\n version={version}\n onRunDoctor={handleRunDoctor}\n onRefresh={handleRefresh}\n />\n );\n}\n\nasync function analyzeProjects(paths: string[]): Promise<ProjectInfo[]> {\n const results = await Promise.all(paths.map((p) => getProjectInfo(p)));\n return results;\n}\n","import React, { useState, useMemo } from 'react';\nimport { Box, Text, useInput, useApp, useStdout } from 'ink';\nimport type { ProjectInfo } from '../types/index.js';\nimport { ProjectCard } from './ProjectCard.js';\nimport { IssueList } from './IssueList.js';\nimport { ScoreBadge } from './ScoreBadge.js';\nimport { colors, layout } from './theme.js';\n\ninterface DashboardProps {\n projects: ProjectInfo[];\n version: string;\n onRunDoctor: (project: ProjectInfo) => void | Promise<void>;\n onRefresh: () => void | Promise<void>;\n}\n\nexport function Dashboard({\n projects,\n version,\n onRunDoctor,\n onRefresh,\n}: DashboardProps): React.ReactElement {\n const { exit } = useApp();\n const { stdout } = useStdout();\n const [selectedIndex, setSelectedIndex] = useState(0);\n const [isExiting, setIsExiting] = useState(false);\n\n const terminalRows = stdout.rows ?? 24;\n const selectedProject = projects[selectedIndex];\n const maxVisible = Math.max(4, Math.floor((terminalRows - 10) / 5));\n\n const visibleProjects = useMemo(() => {\n if (projects.length <= maxVisible) return projects;\n\n let start = Math.max(0, selectedIndex - Math.floor(maxVisible / 2));\n let end = start + maxVisible;\n if (end > projects.length) {\n end = projects.length;\n start = Math.max(0, end - maxVisible);\n }\n return projects.slice(start, end);\n }, [projects, selectedIndex, maxVisible]);\n\n const visibleStart = projects.length <= maxVisible ? 0 : Math.max(0, selectedIndex - Math.floor(maxVisible / 2));\n\n useInput(\n (input, key) => {\n if (isExiting) return;\n\n if (key.upArrow || input === 'k') {\n setSelectedIndex((prev) => Math.max(0, prev - 1));\n } else if (key.downArrow || input === 'j') {\n setSelectedIndex((prev) => Math.min(projects.length - 1, prev + 1));\n } else if (key.return) {\n if (selectedProject) {\n setIsExiting(true);\n void Promise.resolve(onRunDoctor(selectedProject)).then(() => exit());\n }\n } else if (input === 'r') {\n void Promise.resolve(onRefresh());\n } else if (input === 'q' || key.escape) {\n setIsExiting(true);\n exit();\n }\n },\n { isActive: !isExiting }\n );\n\n if (projects.length === 0) {\n return (\n <Box flexDirection=\"column\" padding={2}>\n <Text bold color={colors.ink}>enve dashboard</Text>\n <Box marginTop={1}>\n <Text color={colors.muted}>No projects tracked yet.</Text>\n </Box>\n <Box marginTop={1}>\n <Text color={colors.subtle}>Run `enve scan` or `enve doctor` in a project to add it.</Text>\n </Box>\n </Box>\n );\n }\n\n return (\n <Box flexDirection=\"column\" width=\"100%\" paddingX={layout.paddingX} paddingY={layout.paddingY}>\n {/* Top bar */}\n <Box justifyContent=\"space-between\" width=\"100%\" marginBottom={1}>\n <Box>\n <Text bold color={colors.ink}>enve</Text>\n <Text color={colors.muted}> dashboard</Text>\n </Box>\n <Text color={colors.subtle}>v{version}</Text>\n </Box>\n\n {/* Main content */}\n <Box flexGrow={1} width=\"100%\">\n {/* Sidebar */}\n <Box\n width={layout.sidebarWidth}\n flexDirection=\"column\"\n borderStyle=\"single\"\n borderColor={colors.border}\n paddingX={1}\n paddingY={1}\n marginRight={2}\n >\n <Box marginBottom={1}>\n <Text bold color={colors.ink}>Projects</Text>\n <Text color={colors.subtle}> ({projects.length})</Text>\n </Box>\n\n <Box flexDirection=\"column\" rowGap={1} flexGrow={1}>\n {visibleProjects.map((project, index) => {\n const actualIndex = visibleStart + index;\n return (\n <ProjectCard\n key={project.path}\n project={project}\n isSelected={actualIndex === selectedIndex}\n isFocused={actualIndex === selectedIndex}\n />\n );\n })}\n </Box>\n\n {projects.length > maxVisible && (\n <Box marginTop={1}>\n <Text color={colors.subtle}>Use ↑/↓ to scroll</Text>\n </Box>\n )}\n </Box>\n\n {/* Details panel */}\n <Box\n flexGrow={1}\n flexDirection=\"column\"\n borderStyle=\"single\"\n borderColor={colors.border}\n paddingX={2}\n paddingY={1}\n >\n {selectedProject ? (\n <ProjectDetail project={selectedProject} />\n ) : (\n <Box>\n <Text color={colors.muted}>Select a project to view details.</Text>\n </Box>\n )}\n </Box>\n </Box>\n\n {/* Bottom bar */}\n <Box\n justifyContent=\"space-between\"\n width=\"100%\"\n borderStyle=\"single\"\n borderColor={colors.border}\n paddingX={1}\n marginTop={1}\n >\n <Box columnGap={3}>\n <Text color={colors.muted}><Text bold>↑/↓</Text> navigate</Text>\n <Text color={colors.muted}><Text bold>Enter</Text> doctor</Text>\n <Text color={colors.muted}><Text bold>r</Text> refresh</Text>\n <Text color={colors.muted}><Text bold>q</Text> quit</Text>\n </Box>\n <Text color={colors.subtle}>enve-doctor v{version}</Text>\n </Box>\n </Box>\n );\n}\n\nfunction ProjectDetail({ project }: { project: ProjectInfo }): React.ReactElement {\n const envFileNames = project.envFiles.map((f) => f.filename).join(', ') || 'none';\n\n return (\n <Box flexDirection=\"column\" width=\"100%\">\n <Box justifyContent=\"space-between\" alignItems=\"center\" marginBottom={1}>\n <Text bold color={colors.ink}>{project.name}</Text>\n <ScoreBadge score={project.score} />\n </Box>\n\n <Box marginBottom={1}>\n <Text color={colors.muted}>{project.path}</Text>\n </Box>\n\n <Box flexDirection=\"row\" columnGap={4} marginBottom={1}>\n <Metric label=\"Env vars\" value={String(project.envCount)} />\n <Metric label=\"References\" value={String(project.referenceCount)} />\n <Metric label=\"Issues\" value={String(project.score.totalIssues)} />\n </Box>\n\n <Box flexDirection=\"row\" columnGap={4} marginBottom={1}>\n <Status label=\"Git repo\" ok={project.hasGit} />\n <Status label=\"Git hook\" ok={project.hookInstalled} />\n <Status label=\".gitignore\" ok={project.hasEnvInGitignore} />\n </Box>\n\n <Box marginTop={1} marginBottom={1}>\n <Text color={colors.subtle}>Env files: </Text>\n <Text color={colors.ink}>{envFileNames}</Text>\n </Box>\n\n <Box marginTop={1} marginBottom={1}>\n <Text color={colors.borderStrong}>{'─'.repeat(60)}</Text>\n </Box>\n\n <Box marginTop={1} marginBottom={1}>\n <Text bold color={colors.ink}>Top issues</Text>\n </Box>\n\n <IssueList issues={project.issues} maxRows={8} />\n </Box>\n );\n}\n\nfunction Metric({ label, value }: { label: string; value: string }): React.ReactElement {\n return (\n <Box flexDirection=\"column\">\n <Text color={colors.subtle}>{label}</Text>\n <Text bold color={colors.ink}>{value}</Text>\n </Box>\n );\n}\n\nfunction Status({ label, ok }: { label: string; ok: boolean }): React.ReactElement {\n return (\n <Box>\n <Text color={ok ? colors.success : colors.error}>{ok ? '✓' : '✗'} </Text>\n <Text color={colors.muted}>{label}</Text>\n </Box>\n );\n}\n","import React from 'react';\nimport { Box, Text } from 'ink';\nimport type { ProjectInfo } from '../types/index.js';\nimport { ScoreBadge } from './ScoreBadge.js';\nimport { colors } from './theme.js';\n\ninterface ProjectCardProps {\n project: ProjectInfo;\n isSelected: boolean;\n isFocused: boolean;\n}\n\nexport function ProjectCard({\n project,\n isSelected,\n isFocused,\n}: ProjectCardProps): React.ReactElement {\n const { score } = project;\n const borderColor = isSelected ? colors.ink : colors.border;\n const indicator = isFocused ? '›' : ' ';\n\n return (\n <Box\n borderStyle=\"single\"\n borderColor={borderColor}\n paddingX={1}\n paddingY={1}\n flexDirection=\"column\"\n >\n <Box justifyContent=\"space-between\">\n <Box>\n <Text color={isSelected ? colors.ink : colors.muted}>{indicator} </Text>\n <Text bold color={colors.ink}>{project.name}</Text>\n </Box>\n <ScoreBadge score={score} compact />\n </Box>\n\n <Box marginTop={1}>\n <Text color={colors.muted}>{project.path}</Text>\n </Box>\n\n <Box marginTop={1} columnGap={2}>\n {score.errorCount > 0 && (\n <Text color={colors.error}>{score.errorCount} err</Text>\n )}\n {score.warningCount > 0 && (\n <Text color={colors.warning}>{score.warningCount} warn</Text>\n )}\n {score.totalIssues === 0 && (\n <Text color={colors.success}>Clean</Text>\n )}\n </Box>\n </Box>\n );\n}\n","import React from 'react';\nimport { Box, Text } from 'ink';\nimport type { ScoreResult } from '../types/index.js';\nimport { gradeStyles } from './theme.js';\n\ninterface ScoreBadgeProps {\n score: ScoreResult;\n compact?: boolean;\n}\n\nexport function ScoreBadge({ score, compact = false }: ScoreBadgeProps): React.ReactElement {\n const style = gradeStyles[score.grade];\n\n if (compact) {\n return (\n <Box>\n <Text backgroundColor={style.bg} color={style.text} bold>\n {' '}\n {score.score}\n {' '}\n </Text>\n </Box>\n );\n }\n\n return (\n <Box>\n <Text backgroundColor={style.bg} color={style.text} bold>\n {' '}\n {score.score} {score.grade}\n {' '}\n </Text>\n </Box>\n );\n}\n","import type { Grade, Severity } from '../types/index.js';\n\nexport const colors = {\n // Warm monochrome base\n canvas: '#FAFAF8',\n ink: '#1A1A1A',\n muted: '#6B6B6B',\n subtle: '#A3A3A3',\n\n // Borders & dividers\n border: '#E5E5E0',\n borderStrong: '#D4D4CF',\n\n // Semantic accents — desaturated pastels\n excellent: '#10B981', // emerald 500\n excellentBg: '#ECFDF5', // emerald 50\n good: '#F59E0B', // amber 500\n goodBg: '#FFFBEB', // amber 50\n attention: '#F97316', // orange 500\n attentionBg: '#FFF7ED', // orange 50\n critical: '#EF4444', // red 500\n criticalBg: '#FEF2F2', // red 50\n\n // Severity\n error: '#DC2626',\n warning: '#D97706',\n info: '#2563EB',\n success: '#059669',\n} as const;\n\nexport const gradeStyles: Record<\n Grade,\n { text: string; bg: string; label: string }\n> = {\n Excellent: { text: colors.excellent, bg: colors.excellentBg, label: 'Excellent' },\n Good: { text: colors.good, bg: colors.goodBg, label: 'Good' },\n 'Needs attention': { text: colors.attention, bg: colors.attentionBg, label: 'Needs attention' },\n Critical: { text: colors.critical, bg: colors.criticalBg, label: 'Critical' },\n};\n\nexport const severityStyles: Record<Severity, { color: string; symbol: string }> = {\n error: { color: colors.error, symbol: '●' },\n warning: { color: colors.warning, symbol: '●' },\n info: { color: colors.info, symbol: '●' },\n};\n\nexport const issueTypeSymbol: Record<string, string> = {\n missing: '⊘',\n unused: '○',\n invalid: '✕',\n suspicious: '◆',\n 'secret-risk': '▲',\n 'syntax-error': '✕',\n gitignore: '◉',\n};\n\nexport const layout = {\n sidebarWidth: 34,\n maxContentWidth: 86,\n paddingX: 2,\n paddingY: 1,\n} as const;\n","import React from 'react';\nimport { Box, Text } from 'ink';\nimport type { Issue } from '../types/index.js';\nimport { severityStyles, issueTypeSymbol, colors } from './theme.js';\n\ninterface IssueListProps {\n issues: Issue[];\n maxRows?: number;\n}\n\nexport function IssueList({ issues, maxRows = 12 }: IssueListProps): React.ReactElement {\n if (issues.length === 0) {\n return (\n <Box paddingY={1}>\n <Text color={colors.success}>No issues found. Great job.</Text>\n </Box>\n );\n }\n\n const sorted = [...issues].sort((a, b) => {\n const rank = { error: 0, warning: 1, info: 2 };\n return rank[a.severity] - rank[b.severity];\n });\n\n const visible = sorted.slice(0, maxRows);\n const remaining = sorted.length - visible.length;\n\n return (\n <Box flexDirection=\"column\" width=\"100%\">\n {visible.map((issue, index) => {\n const style = severityStyles[issue.severity];\n const symbol = issueTypeSymbol[issue.type] ?? '•';\n\n return (\n <Box key={`${issue.type}-${issue.key}-${index}`} flexDirection=\"column\" marginBottom={1}>\n <Box>\n <Text color={style.color}>{symbol} </Text>\n <Text bold color={colors.ink}>{issue.key}</Text>\n <Text color={colors.subtle}> — {issue.type}</Text>\n </Box>\n <Box marginLeft={2}>\n <Text color={colors.muted}>{issue.message}</Text>\n </Box>\n {issue.suggestion && (\n <Box marginLeft={2}>\n <Text color={colors.subtle}>→ {issue.suggestion}</Text>\n </Box>\n )}\n </Box>\n );\n })}\n {remaining > 0 && (\n <Box marginTop={1}>\n <Text color={colors.subtle}>+ {remaining} more issue{remaining === 1 ? '' : 's'}</Text>\n </Box>\n )}\n </Box>\n );\n}\n","{\n \"name\": \"enve-doctor\",\n \"version\": \"1.0.0\",\n \"description\": \"Environment Variable Doctor — scan, validate, and protect your env files\",\n \"main\": \"dist/cli.mjs\",\n \"bin\": {\n \"enve\": \"./dist/cli.mjs\"\n },\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://github.com/RufixDuke/enve.git\"\n },\n \"bugs\": {\n \"url\": \"https://github.com/RufixDuke/enve/issues\"\n },\n \"homepage\": \"https://enve-lac.vercel.app\",\n \"files\": [\n \"dist\"\n ],\n \"scripts\": {\n \"build\": \"tsup src/cli/cli.ts --minify --clean\",\n \"dev\": \"tsx src/cli/index.ts\",\n \"test\": \"vitest run\",\n \"test:watch\": \"vitest\",\n \"prepublishOnly\": \"npm run build\"\n },\n \"dependencies\": {\n \"acorn\": \"^8.14.0\",\n \"acorn-walk\": \"^8.3.4\",\n \"chalk\": \"^5.4.1\",\n \"commander\": \"^13.1.0\",\n \"conf\": \"^13.1.0\",\n \"dotenv\": \"^16.4.7\",\n \"fast-glob\": \"^3.3.3\",\n \"ink\": \"^5.1.0\",\n \"ora\": \"^8.2.0\",\n \"react\": \"^18.3.1\"\n },\n \"devDependencies\": {\n \"@types/node\": \"^22.13.0\",\n \"@types/react\": \"^18.3.18\",\n \"@typescript-eslint/eslint-plugin\": \"^8.22.0\",\n \"@typescript-eslint/parser\": \"^8.22.0\",\n \"eslint\": \"^9.19.0\",\n \"ink-testing-library\": \"^4.0.0\",\n \"tsup\": \"^8.3.6\",\n \"tsx\": \"^4.19.2\",\n \"typescript\": \"^5.7.3\",\n \"vitest\": \"^3.0.5\"\n },\n \"keywords\": [\n \"env\",\n \"environment-variables\",\n \"secrets\",\n \"security\",\n \"cli\",\n \"developer-tools\"\n ],\n \"author\": \"Abdul-Qudus Rufai\",\n \"license\": \"MIT\",\n \"engines\": {\n \"node\": \">=18.0.0\"\n }\n}\n"],"mappings":"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","names":["Command","Command","chalk","join","basename","readFile","writeFile","access","mkdir","dirname","readFileSafe","filePath","writeFileSafe","content","ensureDir","fileExists","dirPath","join","PRE_COMMIT_HOOK_MARKER","PRE_COMMIT_HOOK_END","isGitRepo","projectPath","fileExists","join","readGitignore","gitignorePath","readFileSafe","isPatternInGitignore","gitignoreContent","pattern","line","trimmed","isEnvFileGitignored","filename","content","getPreCommitHookPath","getHookScript","isHookInstalled","hookPath","installHook","existing","newContent","writeFileSafe","uninstallHook","markerStart","markerEnd","before","after","join","readFileSync","parseEnvFile","filePath","content","readFileSync","variables","lines","trimmed","equalIndex","key","rawValue","value","comment","parseValue","isSecret","detectSecret","raw","endIndex","parseQuotedValue","rest","extractInlineComment","trailing","commentIndex","findUnquotedCommentIndex","quote","escaped","char","extractInlineComment","rest","idx","findUnquotedCommentIndex","value","i","detectSecret","key","findEnvFiles","projectPath","envFileNames","files","name","filePath","join","readFileSafe","parseEnvFile","parseAllEnvFiles","fg","readFile","Parser","fullAncestor","DEFAULT_INCLUDE","DEFAULT_EXCLUDE","scanProject","projectPath","options","include","exclude","files","references","file","fileRefs","scanFile","filePath","content","ast","node","_state","ancestors","ref","extractEnvReference","refs","extractDestructuredEnvReferences","key","getMemberExpressionKey","parent","fallback","detectFallback","createReference","isEnvDestructuringPattern","prop","fallbackValue","expressionToString","nested","isProcessEnvNode","isProcessNode","grandParent","isLeft","isRight","fallbackNode","getFallbackType","line","column","context","extractContext","lines","start","end","ALWAYS_ALLOWED","analyze","envFiles","references","projectPath","issues","checkMissingVariables","checkUnusedVariables","checkSecretRisks","checkGitignore","checkExampleFile","definedKeys","file","v","ref","referencedKeys","r","variable","filenames","f","filesToCheck","messages","suggestions","filename","isEnvFileGitignored","exampleFile","envFile","exampleKeys","calculateScore","score","errorCount","warningCount","infoCount","unusedDeduction","exampleDeduction","issue","getGrade","Conf","readFile","join","DEFAULT_CONFIG","createConfig","getProjects","isTracked","projectPath","p","getProject","addProject","config","project","detectProjectName","projects","detectProjectName","projectPath","pkgRaw","readFile","join","pkg","detectProject","startPath","current","fileExists","join","parent","getProjectInfo","projectPath","envFiles","references","hasGit","hookInstalled","parseAllEnvFiles","scanProject","isGitRepo","isHookInstalled","issues","analyze","score","calculateScore","name","basename","addProject","sum","f","i","chalk","symbols","error","message","warning","success","bullet","message","symbols","indent","level","header","title","chalk","scanCommand","Command","cwd","projectPath","detectProject","error","info","getProjectInfo","indent","chalk","file","bullet","success","errors","i","warnings","infos","issue","warning","formatScore","score","text","Command","chalk","doctorCommand","Command","cwd","projectPath","detectProject","error","runDoctor","info","getProjectInfo","envFiles","parseAllEnvFiles","indent","header","chalk","fileStructureIssues","i","variableIssues","securityIssues","validationIssues","printSection","printVariablesSection","printRecommendations","formatScore","title","issues","success","issue","formatter","warning","totalDefined","sum","f","message","actionable","high","medium","low","idx","score","text","Command","chalk","copyFile","createInterface","prompt","message","rl","confirm","defaultValue","answer","unusedCommand","Command","options","cwd","projectPath","detectProject","error","envFiles","parseAllEnvFiles","references","scanProject","referencedKeys","r","alwaysAllowed","unused","file","variable","indent","header","success","byFile","item","filename","items","warning","chalk","confirm","byPath","removedCount","filePath","backupPath","formatTimestamp","copyFile","lines","readFileSafe","linesToRemove","i","newLines","_","idx","writeFileSafe","date","Command","chalk","join","STANDARD_NODE_VARS","missingCommand","Command","options","cwd","projectPath","detectProject","error","envFiles","parseAllEnvFiles","references","scanProject","definedKeys","v","missingNoFallback","r","missingWithFallback","indent","header","success","ref","chalk","l","warning","addMissingVariables","missing","envPath","join","f","updates","exampleUpdates","value","prompt","confirm","existing","readFileSafe","separator","writeFileSafe","examplePath","exampleExisting","exampleSeparator","Command","chalk","validatePort","value","port","validateUrl","url","validateNodeEnv","validateSecret","key","forbidden","validateBoolean","normalized","boolMap","isSecretKey","copyFile","validateCommand","Command","options","cwd","projectPath","detectProject","error","envFile","parseAllEnvFiles","f","variables","indent","header","errorCount","warningCount","fixes","variable","result","validateVariable","success","truncate","chalk","fixedValue","getFixedValue","safeFixes","isSecretKey","confirm","applyFixes","key","value","validatePort","validateNodeEnv","validateUrl","validateSecret","validateBoolean","normalized","filePath","backupPath","formatTimestamp","copyFile","content","readFileSafe","fixMap","lines","line","trimmed","equalIndex","prefix","suffix","commentIndex","comment","leadingSpaces","writeFileSafe","date","max","Command","chalk","join","generateExampleCommand","Command","options","cwd","projectPath","detectProject","error","envPath","join","readFileSafe","outputPath","existing","variables","parseEnvFile","newLines","redactedCount","copiedCount","variable","isSecretKey","newContent","indent","header","bullet","diff","computeDiff","success","change","symbol","chalk","confirm","writeFileSafe","existingContent","existingVars","newVars","line","trimmed","idx","key","value","Command","hookCommand","Command","cwd","projectPath","detectProject","isGitRepo","error","installHook","indent","success","getPreCommitHookPath","bullet","installed","isHookInstalled","warning","removed","uninstallHook","text","Command","render","React","useState","useCallback","React","useState","useMemo","Box","Text","useInput","useApp","useStdout","React","Box","Text","React","Box","Text","colors","gradeStyles","severityStyles","issueTypeSymbol","layout","ScoreBadge","score","compact","style","gradeStyles","React","Box","Text","ProjectCard","project","isSelected","isFocused","score","borderColor","colors","indicator","React","Box","Text","ScoreBadge","React","Box","Text","IssueList","issues","maxRows","React","Box","Text","colors","sorted","a","b","rank","visible","remaining","issue","index","style","severityStyles","symbol","issueTypeSymbol","Dashboard","projects","version","onRunDoctor","onRefresh","exit","useApp","stdout","useStdout","selectedIndex","setSelectedIndex","useState","isExiting","setIsExiting","terminalRows","selectedProject","maxVisible","visibleProjects","useMemo","start","end","visibleStart","useInput","input","key","prev","React","Box","Text","colors","layout","project","index","actualIndex","ProjectCard","ProjectDetail","envFileNames","f","ScoreBadge","Metric","Status","IssueList","label","value","ok","package_default","dashboardCommand","Command","tracked","getProjects","initialProjects","analyzeProjects","p","render","React","DashboardApp","package_default","trackedPaths","version","projects","setProjects","useState","handleRunDoctor","useCallback","project","runDoctor","handleRefresh","fresh","Dashboard","paths","getProjectInfo","program","Command","package_default","scanCommand","doctorCommand","unusedCommand","missingCommand","validateCommand","generateExampleCommand","hookCommand","dashboardCommand","reason"]}
package/package.json ADDED
@@ -0,0 +1,64 @@
1
+ {
2
+ "name": "enve-doctor",
3
+ "version": "1.0.0",
4
+ "description": "Environment Variable Doctor — scan, validate, and protect your env files",
5
+ "main": "dist/cli.mjs",
6
+ "bin": {
7
+ "enve": "./dist/cli.mjs"
8
+ },
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "git+https://github.com/RufixDuke/enve.git"
12
+ },
13
+ "bugs": {
14
+ "url": "https://github.com/RufixDuke/enve/issues"
15
+ },
16
+ "homepage": "https://enve-lac.vercel.app",
17
+ "files": [
18
+ "dist"
19
+ ],
20
+ "scripts": {
21
+ "build": "tsup src/cli/cli.ts --minify --clean",
22
+ "dev": "tsx src/cli/index.ts",
23
+ "test": "vitest run",
24
+ "test:watch": "vitest",
25
+ "prepublishOnly": "npm run build"
26
+ },
27
+ "dependencies": {
28
+ "acorn": "^8.14.0",
29
+ "acorn-walk": "^8.3.4",
30
+ "chalk": "^5.4.1",
31
+ "commander": "^13.1.0",
32
+ "conf": "^13.1.0",
33
+ "dotenv": "^16.4.7",
34
+ "fast-glob": "^3.3.3",
35
+ "ink": "^5.1.0",
36
+ "ora": "^8.2.0",
37
+ "react": "^18.3.1"
38
+ },
39
+ "devDependencies": {
40
+ "@types/node": "^22.13.0",
41
+ "@types/react": "^18.3.18",
42
+ "@typescript-eslint/eslint-plugin": "^8.22.0",
43
+ "@typescript-eslint/parser": "^8.22.0",
44
+ "eslint": "^9.19.0",
45
+ "ink-testing-library": "^4.0.0",
46
+ "tsup": "^8.3.6",
47
+ "tsx": "^4.19.2",
48
+ "typescript": "^5.7.3",
49
+ "vitest": "^3.0.5"
50
+ },
51
+ "keywords": [
52
+ "env",
53
+ "environment-variables",
54
+ "secrets",
55
+ "security",
56
+ "cli",
57
+ "developer-tools"
58
+ ],
59
+ "author": "Abdul-Qudus Rufai",
60
+ "license": "MIT",
61
+ "engines": {
62
+ "node": ">=18.0.0"
63
+ }
64
+ }