enve-doctor 1.0.2 → 1.3.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/README.md CHANGED
@@ -23,6 +23,11 @@
23
23
  - [`enve generate-example`](#enve-generate-example)
24
24
  - [`enve hook`](#enve-hook)
25
25
  - [`enve dashboard`](#enve-dashboard)
26
+ - [`enve ci`](#enve-ci)
27
+ - [`enve fix`](#enve-fix)
28
+ - [`enve docs`](#enve-docs)
29
+ - [`enve sync`](#enve-sync)
30
+ - [`enve history`](#enve-history)
26
31
  - [How the Score Works](#how-the-score-works)
27
32
  - [Tracking Behavior](#tracking-behavior)
28
33
  - [Contributing](#contributing)
@@ -169,6 +174,72 @@ Keyboard shortcuts:
169
174
  | `r` | Refresh all projects |
170
175
  | `q` / `Ctrl+C` | Quit |
171
176
 
177
+ ### `enve ci`
178
+
179
+ Non-interactive check designed for CI/CD pipelines.
180
+
181
+ ```bash
182
+ enve ci
183
+ enve ci --fail-on error # Exit with error code if any errors exist (default)
184
+ enve ci --fail-on warning # Exit with error code if any warnings or errors exist
185
+ enve ci --fail-on none # Never fail the pipeline
186
+ enve ci --format json # Output a JSON report
187
+ enve ci --format junit # Output a JUnit XML report
188
+ ```
189
+
190
+ Example GitHub Actions step:
191
+
192
+ ```yaml
193
+ - name: Check env health
194
+ run: npx enve-doctor ci --fail-on warning
195
+ ```
196
+
197
+ ### `enve fix`
198
+
199
+ Apply safe, auto-fixable corrections.
200
+
201
+ ```bash
202
+ enve fix
203
+ enve fix --yes # Apply all fixes without prompting
204
+ ```
205
+
206
+ Fixes include:
207
+
208
+ - Adding `.env`, `.env.local`, and `.env.production` to `.gitignore`
209
+ - Moving secrets from `.env` to `.env.local`
210
+ - Removing unused variables from `.env`
211
+ - Adding missing variables to `.env.example`
212
+
213
+ ### `enve docs`
214
+
215
+ Generate `ENV.md` documentation from your `.env` files.
216
+
217
+ ```bash
218
+ enve docs
219
+ enve docs --output docs/env.md
220
+ enve docs --overwrite
221
+ ```
222
+
223
+ ### `enve sync`
224
+
225
+ Share `.env.example` with your team through a shared directory.
226
+
227
+ ```bash
228
+ enve sync set-path /path/to/team/shared
229
+ enve sync push
230
+ enve sync pull
231
+ ```
232
+
233
+ ### `enve history`
234
+
235
+ Track env health score over time.
236
+
237
+ ```bash
238
+ enve history
239
+ enve history --graph
240
+ enve history --limit 20
241
+ ```
242
+
172
243
  ---
173
244
 
174
245
  ## How the Score Works
package/dist/cli.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
- import{Command as Dt}from"commander";import{Command as tt}from"commander";import V from"chalk";import{join as Fe,basename as ot}from"path";import{readFile as fo,writeFile as mo,access as po,mkdir as go}from"fs/promises";import{dirname as vo}from"path";async function k(e){try{return await fo(e,"utf-8")}catch{return}}async function C(e,o){await yo(vo(e)),await mo(e,o,"utf-8")}async function R(e){try{return await po(e),!0}catch{return!1}}async function yo(e){if(e)try{await go(e,{recursive:!0})}catch{}}import{join as ue}from"path";var X="# === Enve Pre-Commit Hook ===",ce="# === End Enve Hook ===";async function M(e){return R(ue(e,".git"))}async function ho(e){let o=ue(e,".gitignore");return k(o)}function xo(e,o){return e.split(`
3
- `).some(n=>{let i=n.trim();return i===o||i===`/${o}`})}async function be(e,o){let t=await ho(e);return t?xo(t,o):!1}function _(e){return ue(e,".git","hooks","pre-commit")}function bo(){return`#!/bin/bash
4
- ${X}
2
+ import{Command as bn}from"commander";import{Command as wt}from"commander";import _ from"chalk";import{join as Ke,basename as bt}from"path";import{readFile as To,writeFile as Ao,access as Fo,mkdir as Do}from"fs/promises";import{dirname as No}from"path";async function w(e){try{return await To(e,"utf-8")}catch{return}}async function E(e,o){await ve(No(e)),await Ao(e,o,"utf-8")}async function B(e){try{return await Fo(e),!0}catch{return!1}}async function ve(e){if(e)try{await Do(e,{recursive:!0})}catch{}}import{join as be}from"path";var oe="# === Enve Pre-Commit Hook ===",xe="# === End Enve Hook ===";async function Y(e){return B(be(e,".git"))}async function Ro(e){let o=be(e,".gitignore");return w(o)}function Bo(e,o){return e.split(`
3
+ `).some(n=>{let r=n.trim();return r===o||r===`/${o}`})}async function Fe(e,o){let t=await Ro(e);return t?Bo(t,o):!1}function q(e){return be(e,".git","hooks","pre-commit")}function Oo(){return`#!/bin/bash
4
+ ${oe}
5
5
  # Blocks commits of .env files that may contain secrets
6
6
  # Installed by: enve hook install
7
7
  # Remove with: enve hook uninstall
@@ -28,32 +28,57 @@ if [ -n "$STAGED_ENV_FILES" ]; then
28
28
  echo ""
29
29
  exit 1
30
30
  fi
31
- ${ce}
32
- `}async function Z(e){let o=_(e);return await R(o)?(await k(o))?.includes(X)??!1:!1}async function ke(e){let o=_(e),t=await k(o)??"";if(t.includes(X))return;let n=`${bo()}
33
- ${t}`;await C(o,n)}async function we(e){let o=_(e);if(!await R(o))return!1;let t=await k(o);if(!t||!t.includes(X))return!1;let n=t.indexOf(X),i=t.indexOf(ce,n);if(n===-1||i===-1)return!1;let r=t.slice(0,n),s=t.slice(i+ce.length),l=`${r.trimEnd()}
34
- ${s.trimStart()}`.trim();return l?await C(o,`${l}
35
- `):await C(o,""),!0}import{join as ko}from"path";import{readFileSync as wo}from"fs";function fe(e){let o;try{o=wo(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:g,comment:v}=Eo(m),d=Po(f,g);t.push({key:f,value:g,line:i+1,source:e,isSecret:d,comment:v})}return t}function Eo(e){let o=e.trimStart();if(o.startsWith('"')){let{value:i,endIndex:r}=Ee(o,'"'),s=o.slice(r+1).trim(),l=$e(s);return{value:i,comment:l}}if(o.startsWith("'")){let{value:i,endIndex:r}=Ee(o,"'"),s=o.slice(r+1).trim(),l=$e(s);return{value:i,comment:l}}let t=e.trimEnd(),n=$o(t);return n!==-1?{value:t.slice(0,n).trimEnd(),comment:t.slice(n+1).trim()}:{value:e.trimEnd()}}function Ee(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 $e(e){let o=e.indexOf("#");if(o!==-1)return e.slice(o+1).trim()}function $o(e){for(let o=0;o<e.length;o++)if(e[o]==="#"&&(o===0||e[o-1]===" "))return o;return-1}function Po(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 To(e){let o=[".env",".env.local",".env.development",".env.production",".env.test",".env.example"],t=[];for(let n of o){let i=ko(e,n);await k(i)!==void 0&&t.push({path:i,filename:n,variables:fe(i)})}return t}async function D(e){return To(e)}import Co from"fast-glob";import{readFile as Io}from"fs/promises";import{Parser as So}from"acorn";import{fullAncestor as jo}from"acorn-walk";var Fo=["**/*.{js,ts,jsx,tsx,mjs,cjs}"],Do=["**/node_modules/**","**/dist/**","**/build/**","**/.next/**"];async function W(e,o={}){let t=o.include??Fo,n=o.exclude??Do,i=await Co(t,{cwd:e,ignore:n,absolute:!0}),r=[];for(let s of i){let l=await Ao(s);r.push(...l)}return r}async function Ao(e){let o=[],t=await Io(e,"utf-8"),n;try{n=So.parse(t,{ecmaVersion:"latest",sourceType:"module",allowReturnOutsideFunction:!0,allowImportExportEverywhere:!0,locations:!0})}catch{return o}return jo(n,(i,r,s)=>{if(i.type==="MemberExpression"){let l=Bo(i,s,e,t);l&&o.push(l)}if(i.type==="ObjectPattern"){let l=Pe(i,s,e,t);o.push(...l)}}),o}function Bo(e,o,t,n){let i=No(e);if(!i)return;let r=o.length>1?o[o.length-2]:void 0,s=Lo(e,r);return ee(i,t,e,n,s)}function No(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 Pe(e,o,t,n){if(!Te(e,o))return[];let i=[];for(let r of e.properties){if(r.type==="RestElement"&&r.argument?.type==="Identifier"){i.push(ee(r.argument.name,t,e,n,{hasFallback:!1}));continue}if(r.type==="Property"){if(r.value?.type==="Identifier"&&r.key?.type==="Identifier"&&i.push(ee(r.key.name,t,r.value,n,{hasFallback:!1})),r.value?.type==="AssignmentPattern"&&r.value.left?.type==="Identifier"&&r.key?.type==="Identifier"){let s=r.key.name,l=U(r.value.right);i.push(ee(s,t,r.value.left,n,{hasFallback:!0,fallbackValue:l,fallbackType:"literal"}))}if(r.value?.type==="ObjectPattern"&&r.key?.name==="env"){let s=Pe(r.value,[...o,r,r.value],t,n);i.push(...s)}}}return i}function Te(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 G(t.init);if(t.type==="AssignmentPattern"&&t.left===e)return G(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 Te(n,o.slice(0,-2))}return!1}function Vo(e){return e?.type==="Identifier"&&e.name==="process"}function Oo(e){return e?e.type==="MemberExpression"&&e.object?.type==="Identifier"&&e.object.name==="process"&&e.property?.name==="env":!1}function G(e){return Oo(e)||Vo(e)?!0:e?.type==="LogicalExpression"&&(e.operator==="||"||e.operator==="??")?G(e.left)||G(e.right):e?.type==="ConditionalExpression"?G(e.consequent)||G(e.alternate):!1}function Lo(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:U(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:U(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 U(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}${U(e.argument)}`:e.type==="BinaryExpression"?`${U(e.left)} ${e.operator} ${U(e.right)}`:e.type==="MemberExpression"?e.property?.name??"member":"<expression>":"unknown"}function ee(e,o,t,n,i){let r=t.loc?.start?.line??0,s=t.loc?.start?.column??0,l=Mo(n,r);return{key:e,file:o,line:r,column:s,context:l,hasFallback:i.hasFallback,fallbackValue:i.fallbackValue,fallbackType:i.fallbackType}}function Mo(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 _o=["PORT","HOST","NODE_ENV","CI","PWD"];async function Ce(e,o,t){let n=[];return n.push(...await Go(e,o)),n.push(...Uo(e,o)),n.push(...Wo(e)),n.push(...await Ho(e,t)),n.push(...Ko(e)),n}async function Go(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 Uo(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)_o.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 Wo(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 Ho(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 be(o,l)||t.push({type:"gitignore",severity:"error",key:l,message:r[l],file:".gitignore",suggestion:s[l]})}return t}function Ko(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 Ie(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:Yo(o),totalIssues:e.length,errorCount:t,warningCount:n,infoCount:i}}function Yo(e){return e>=90?"Excellent":e>=70?"Good":e>=50?"Needs attention":"Critical"}import qo from"conf";import{readFile as Xo}from"fs/promises";import{join as zo}from"path";var Qo={version:1,projects:[]};function Se(){return new qo({projectName:"enve",defaults:Qo})}function oe(){return Se().get("projects",[])}function Jo(e){return oe().some(o=>o.path===e)}function Zo(e){return oe().find(o=>o.path===e)}async function je(e){let o=Se();if(Jo(e))return Zo(e);let n={name:await et(e),path:e,addedAt:new Date().toISOString()},i=o.get("projects",[]);return o.set("projects",[...i,n]),n}async function et(e){try{let o=await Xo(zo(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 R(Fe(o,"package.json")))return o;let t=Fe(o,"..");if(t===o)break;o=t}}async function H(e){let[o,t,n,i]=await Promise.all([D(e),W(e),M(e),Z(e)]),r=await Ce(o,t,e),s=Ie(r),l=ot(e);return await je(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 te from"chalk";var ne={error:"\u2717",warning:"\u26A0",success:"\u2713",info:"\u2139",bullet:"\u2022",arrow:"\u2192",pointer:"\u25B8"};function y(e){return`${te.red(ne.error)} ${e}`}function I(e){return`${te.yellow(ne.warning)} ${e}`}function x(e){return`${te.green(ne.success)} ${e}`}function z(e){return` ${ne.bullet} ${e}`}function a(e,o=1){return" ".repeat(o)+e}function E(e){return te.bold(e)}var De=new tt("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 H(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(`${z(`${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: ${nt(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 nt(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 rt}from"commander";import S from"chalk";var Ae=new rt("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 de(o)});async function de(e){let o=await H(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");me("[File Structure]",n),it(i,t),me("[Security]",r),me("[Validation]",s),st(o.issues),console.log(),console.log(a(`Score: ${at(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 me(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 it(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 st(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 at(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 ct}from"commander";import ge from"chalk";import{copyFile as ut}from"fs/promises";import{createInterface as lt}from"readline/promises";async function pe(e){let o=lt({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 pe(`${e}${o?" (Y/n)":" (y/N)"}`);return n?/^y(es)?$/i.test(n):o}var Be=new ct("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 W(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 g=new Map;for(let d of l)g.set(d.path,[...g.get(d.path)??[],d]);let v=0;for(let[d,b]of g){let F=`${d}.backup.${ft(new Date)}`;await ut(d,F);let T=(await k(d)??"").split(`
40
- `),B=new Set(b.map(xe=>xe.line-1)),co=T.filter((xe,uo)=>!B.has(uo));await C(d,co.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 ft(e){return e.toISOString().replace(/[:.]/g,"-").slice(0,19)}import{Command as mt}from"commander";import Q from"chalk";import{join as Ne}from"path";var Ve=new Set(["PATH","HOME","USER","SHELL","PWD","OLDPWD","NODE","NODE_PATH","TMPDIR","LANG","LOGNAME","TERM","EDITOR","SHLVL","_","__CF_USER_TEXT_ENCODING"]),Oe=new mt("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 W(t),r=new Set(n.flatMap(f=>f.variables.map(m=>m.key))),s=i.filter(f=>!r.has(f.key)&&!f.hasFallback&&!Ve.has(f.key)),l=e.fallbacks?i.filter(f=>!r.has(f.key)&&f.hasFallback&&!Ve.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(Q.dim(`\u2192 ${f.file}:${f.line}`),3)),f.context&&console.log(a(Q.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(Q.dim(`\u2192 fallback: ${f.fallbackValue??"unknown"}`),3)),console.log(a(Q.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 dt(t,n,s):(console.log(),console.log(a("Run with `--add` to interactively add missing variables to .env")))});async function dt(e,o,t){let n=Ne(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 g=await pe(`Value for ${m.key}:`);if(!g)continue;r.push(`${m.key}=${g}`),await N(`Add ${m.key} to .env.example?`,!1)&&s.push(`${m.key}=your_${m.key.toLowerCase()}`)}if(r.length===0){console.log(a(Q.dim("No variables added.")));return}let l=await k(n)??"",f=l.endsWith(`
44
- `)||l===""?"":`
45
- `;if(await C(n,`${l}${f}${r.join(`
31
+ ${xe}
32
+ `}async function ae(e){let o=q(e);return await B(o)?(await w(o))?.includes(oe)??!1:!1}async function De(e){let o=q(e),t=await w(o)??"";if(t.includes(oe))return;let n=`${Oo()}
33
+ ${t}`;await E(o,n)}async function Ne(e){let o=q(e);if(!await B(o))return!1;let t=await w(o);if(!t||!t.includes(oe))return!1;let n=t.indexOf(oe),r=t.indexOf(xe,n);if(n===-1||r===-1)return!1;let s=t.slice(0,n),a=t.slice(r+xe.length),i=`${s.trimEnd()}
34
+ ${a.trimStart()}`.trim();return i?await E(o,`${i}
35
+ `):await E(o,""),!0}import{join as Vo}from"path";import{readFileSync as Lo}from"fs";function we(e){let o;try{o=Lo(e,"utf-8")}catch{return[]}if(!o)return[];let t=[],n=o.split(`
36
+ `);for(let r=0;r<n.length;r++){let a=n[r].trim();if(!a||a.startsWith("#"))continue;let i=a.indexOf("=");if(i===-1)continue;let f=a.slice(0,i).trim(),u=a.slice(i+1);if(f.startsWith("export ")&&(f=f.slice(7).trim()),!f)continue;let{value:c,comment:p}=Mo(u),m=Go(f,c);t.push({key:f,value:c,line:r+1,source:e,isSecret:m,comment:p})}return t}function Mo(e){let o=e.trimStart();if(o.startsWith('"')){let{value:r,endIndex:s}=Re(o,'"'),a=o.slice(s+1).trim(),i=Be(a);return{value:r,comment:i}}if(o.startsWith("'")){let{value:r,endIndex:s}=Re(o,"'"),a=o.slice(s+1).trim(),i=Be(a);return{value:r,comment:i}}let t=e.trimEnd(),n=_o(t);return n!==-1?{value:t.slice(0,n).trimEnd(),comment:t.slice(n+1).trim()}:{value:e.trimEnd()}}function Re(e,o){let t="",n=!1;for(let r=1;r<e.length;r++){let s=e[r];if(n){s==="n"?t+=`
37
+ `:s==="t"?t+=" ":s===o?t+=o:t+=`\\${s}`,n=!1;continue}if(s==="\\"){n=!0;continue}if(s===o)return{value:t,endIndex:r};t+=s}return{value:t,endIndex:e.length-1}}function Be(e){let o=e.indexOf("#");if(o!==-1)return e.slice(o+1).trim()}function _o(e){for(let o=0;o<e.length;o++)if(e[o]==="#"&&(o===0||e[o-1]===" "))return o;return-1}function Go(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 Wo(e){let o=[".env",".env.local",".env.development",".env.production",".env.test",".env.example"],t=[];for(let n of o){let r=Vo(e,n);await w(r)!==void 0&&t.push({path:r,filename:n,variables:we(r)})}return t}async function O(e){return Wo(e)}import Uo from"fast-glob";import{readFile as Ho}from"fs/promises";import{extname as Ko}from"path";import{Parser as Yo}from"acorn";import{fullAncestor as qo}from"acorn-walk";var zo=["**/*.{js,ts,jsx,tsx,mjs,cjs,py,go,rb,rs}"],Xo=["**/node_modules/**","**/dist/**","**/build/**","**/.next/**","**/target/**","**/__pycache__/**","**/vendor/**"];async function M(e,o={}){let t=o.include??zo,n=o.exclude??Xo,r=await Uo(t,{cwd:e,ignore:n,absolute:!0}),s=[];for(let a of r){let i=await Zo(a);s.push(...i)}return s}async function Zo(e){let o=[],t=await Ho(e,"utf-8"),n;try{n=Yo.parse(t,{ecmaVersion:"latest",sourceType:"module",allowReturnOutsideFunction:!0,allowImportExportEverywhere:!0,locations:!0})}catch{return et(t,e)}return qo(n,(r,s,a)=>{if(r.type==="MemberExpression"){let i=Jo(r,a,e,t);i&&o.push(i)}if(r.type==="ObjectPattern"){let i=Oe(r,a,e,t);o.push(...i)}}),o}function Jo(e,o,t,n){let r=Qo(e);if(!r)return;let s=o.length>1?o[o.length-2]:void 0,a=nt(e,s);return te(r,t,e,n,a)}function Qo(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 Oe(e,o,t,n){if(!Ve(e,o))return[];let r=[];for(let s of e.properties){if(s.type==="RestElement"&&s.argument?.type==="Identifier"){r.push(te(s.argument.name,t,e,n,{hasFallback:!1}));continue}if(s.type==="Property"){if(s.value?.type==="Identifier"&&s.key?.type==="Identifier"&&r.push(te(s.key.name,t,s.value,n,{hasFallback:!1})),s.value?.type==="AssignmentPattern"&&s.value.left?.type==="Identifier"&&s.key?.type==="Identifier"){let a=s.key.name,i=X(s.value.right);r.push(te(a,t,s.value.left,n,{hasFallback:!0,fallbackValue:i,fallbackType:"literal"}))}if(s.value?.type==="ObjectPattern"&&s.key?.name==="env"){let a=Oe(s.value,[...o,s,s.value],t,n);r.push(...a)}}}return r}function et(e,o){let t=[],n=new Set,r=(a,i)=>{if(n.has(a))return;n.add(a);let u={loc:{start:{line:e.slice(0,i).split(`
38
+ `).length,column:0}}};t.push(te(a,o,u,e,{hasFallback:!1}))},s=Ko(o);if([".js",".ts",".jsx",".tsx",".mjs",".cjs"].includes(s)){let a=/(?:process|import\.meta)\.env\.([A-Za-z_$][A-Za-z0-9_$]*)/g,i;for(;(i=a.exec(e))!==null;)r(i[1],i.index);let f=/(?:process|import\.meta)\.env\[(?:'([^']+)'|"([^"]+)"|`([^`]+)`)\]/g;for(;(i=f.exec(e))!==null;){let c=i[1]??i[2]??i[3];c&&r(c,i.index)}let u=/(?:const|let|var)\s*\{\s*([^}]+)\s*\}\s*=\s*(?:process\.env|import\.meta\.env)(?:\s*(?:\|\||\?\?)\s*\{[^}]*\})?/g;for(;(i=u.exec(e))!==null;){let c=i[1].split(",");for(let p of c){let m=p.trim();if(!m)continue;let h=m.match(/^([A-Za-z_$][A-Za-z0-9_$]*)/);h&&r(h[1],i.index)}}}if(s===".py"){let a=/(?:os\.environ\.get|os\.environ\[|os\.getenv)\s*\(\s*['"]([^'"]+)['"]\s*\)|os\.environ\[\s*['"]([^'"]+)['"]\s*\]/g,i;for(;(i=a.exec(e))!==null;){let f=i[1]??i[2];f&&r(f,i.index)}}if(s===".go"){let a=/os\.Getenv\s*\(\s*["']([^"']+)["']\s*\)/g,i;for(;(i=a.exec(e))!==null;)r(i[1],i.index)}if(s===".rb"){let a=/ENV\s*\[\s*["']([^"']+)["']\s*\]/g,i;for(;(i=a.exec(e))!==null;)r(i[1],i.index)}if(s===".rs"){let a=/(?:std::)?env::var(?:_os)?\s*\(\s*["']([^"']+)["']\s*\)/g,i;for(;(i=a.exec(e))!==null;)r(i[1],i.index)}return t}function Ve(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 z(t.init);if(t.type==="AssignmentPattern"&&t.left===e)return z(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 Ve(n,o.slice(0,-2))}return!1}function ot(e){return e?.type==="Identifier"&&e.name==="process"}function tt(e){return e?e.type==="MemberExpression"&&e.object?.type==="Identifier"&&e.object.name==="process"&&e.property?.name==="env":!1}function z(e){return tt(e)||ot(e)?!0:e?.type==="LogicalExpression"&&(e.operator==="||"||e.operator==="??")?z(e.left)||z(e.right):e?.type==="ConditionalExpression"?z(e.consequent)||z(e.alternate):!1}function nt(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 r=t?o.right:n?o.left:void 0;if(r)return{hasFallback:!0,fallbackValue:X(r),fallbackType:rt(r)}}if(o.operator==="&&"&&t)return{hasFallback:!0,fallbackValue:"guarded",fallbackType:"expression"}}return o.type==="ConditionalExpression"&&o.test===e?{hasFallback:!0,fallbackValue:X(o.alternate),fallbackType:"ternary"}:{hasFallback:!1}}function rt(e){return e.type==="Literal"||e.type==="TemplateLiteral"?"literal":e.type==="CallExpression"||e.type==="NewExpression"?"function":"expression"}function X(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}${X(e.argument)}`:e.type==="BinaryExpression"?`${X(e.left)} ${e.operator} ${X(e.right)}`:e.type==="MemberExpression"?e.property?.name??"member":"<expression>":"unknown"}function te(e,o,t,n,r){let s=t.loc?.start?.line??0,a=t.loc?.start?.column??0,i=st(n,s);return{key:e,file:o,line:s,column:a,context:i,hasFallback:r.hasFallback,fallbackValue:r.fallbackValue,fallbackType:r.fallbackType}}function st(e,o){let t=e.split(`
39
+ `),n=Math.max(0,o-2),r=Math.min(t.length,o+1);return t.slice(n,r).join(`
40
+ `)}var it=["PORT","HOST","NODE_ENV","CI","PWD"];async function Le(e,o,t){let n=[];return n.push(...await at(e,o)),n.push(...lt(e,o)),n.push(...ct(e)),n.push(...await ut(e,t)),n.push(...ft(e)),n}async function at(e,o){let t=new Set(e.flatMap(r=>r.variables.map(s=>s.key))),n=[];for(let r of o)t.has(r.key)||(r.hasFallback?n.push({type:"missing",severity:"warning",key:r.key,message:`${r.key} is not defined in .env but has a fallback default: ${r.fallbackValue??"unknown"}`,file:r.file,line:r.line,suggestion:`Consider adding ${r.key} to .env for explicit configuration, or keep the fallback`,hasFallback:!0,fallbackValue:r.fallbackValue}):n.push({type:"missing",severity:"error",key:r.key,message:`${r.key} referenced in ${r.file}:${r.line} but not defined in any .env file (no fallback)`,file:r.file,line:r.line,suggestion:`Add ${r.key} to .env and .env.example`}));return n}function lt(e,o){let t=new Set(o.map(r=>r.key)),n=[];for(let r of e)if(!(r.filename===".env.example"||r.filename===".env.test"))for(let s of r.variables)it.includes(s.key)||t.has(s.key)||n.push({type:"unused",severity:"warning",key:s.key,message:`${s.key} is defined in ${r.filename} but never used in code`,file:r.filename,line:s.line,suggestion:"Remove if no longer needed, or use it in your code"});return n}function ct(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 ut(e,o){let t=[],n=e.map(i=>i.filename),r=[".env",".env.local",".env.production"],s={".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"},a={".env":"echo '.env' >> .gitignore",".env.local":"echo '.env.local' >> .gitignore",".env.production":"echo '.env.production' >> .gitignore"};for(let i of r){if(!n.includes(i))continue;await Fe(o,i)||t.push({type:"gitignore",severity:"error",key:i,message:s[i],file:".gitignore",suggestion:a[i]})}return t}function ft(e){let o=[],t=e.find(s=>s.filename===".env.example"),n=e.find(s=>s.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 r=new Set(t.variables.map(s=>s.key));for(let s of n.variables)r.has(s.key)||o.push({type:"suspicious",severity:"warning",key:s.key,message:`${s.key} is in .env but missing from .env.example`,file:".env.example",suggestion:`Add ${s.key}=your_${s.key} to .env.example`});return o}function Me(e){let o=100,t=0,n=0,r=0,s=0,a=0;for(let i of e){switch(i.severity){case"error":t++;break;case"warning":n++;break;case"info":r++;break}switch(i.type){case"missing":o-=i.hasFallback?3:10;break;case"secret-risk":o-=8;break;case"gitignore":i.key===".env.production"?o-=25:o-=20;break;case"syntax-error":o-=15;break;case"invalid":o-=5;break;case"unused":s=Math.min(s+3,15);break;case"suspicious":i.key===".env.example"?o-=10:a=Math.min(a+2,10);break}}return o-=s+a,o=Math.max(0,Math.min(100,o)),{score:o,grade:mt(o),totalIssues:e.length,errorCount:t,warningCount:n,infoCount:r}}function mt(e){return e>=90?"Excellent":e>=70?"Good":e>=50?"Needs attention":"Critical"}import dt from"conf";import{readFile as pt}from"fs/promises";import{join as gt}from"path";var yt={version:1,projects:[],sync:{},history:[]};function Z(){return new dt({projectName:"enve",defaults:yt})}function le(){return Z().get("projects",[])}function ht(e){return le().some(o=>o.path===e)}function vt(e){return le().find(o=>o.path===e)}async function _e(e){let o=Z();if(ht(e))return vt(e);let n={name:await xt(e),path:e,addedAt:new Date().toISOString()},r=o.get("projects",[]);return o.set("projects",[...r,n]),n}function Ge(e){let o=Z(),t=o.get("sync",{});t.path=e,o.set("sync",t)}function We(){return Z().get("sync",{}).path}function Ue(e){let t=Z().get("history",[]);return e?t.filter(n=>n.projectPath===e):t}function He(e){let o=Z(),t=o.get("history",[]);t.push(e);let n=new Map;for(let s of t)n.has(s.projectPath)||n.set(s.projectPath,[]),n.get(s.projectPath).push(s);let r=[];for(let s of n.values())r.push(...s.slice(-50));o.set("history",r)}async function xt(e){try{let o=await pt(gt(e,"package.json"),"utf-8"),t=JSON.parse(o);if(t.name)return t.name}catch{}return e.split("/").pop()??"unknown"}async function $(e){let o=e;for(;;){if(await B(Ke(o,"package.json")))return o;let t=Ke(o,"..");if(t===o)break;o=t}}async function A(e){let[o,t,n,r]=await Promise.all([O(e),M(e),Y(e),ae(e)]),s=await Le(o,t,e),a=Me(s),i=bt(e);return await _e(e),{name:i,path:e,envFiles:o,envCount:o.reduce((f,u)=>f+u.variables.length,0),referenceCount:t.length,issues:s,score:a,hasGit:n,hookInstalled:r,hasEnvInGitignore:!s.some(f=>f.type==="gitignore"),lastModified:new Date}}import ce from"chalk";var ue={error:"\u2717",warning:"\u26A0",success:"\u2713",info:"\u2139",bullet:"\u2022",arrow:"\u2192",pointer:"\u25B8"};function v(e){return`${ce.red(ue.error)} ${e}`}function j(e){return`${ce.yellow(ue.warning)} ${e}`}function b(e){return`${ce.green(ue.success)} ${e}`}function L(e){return` ${ue.bullet} ${e}`}function l(e,o=1){return" ".repeat(o)+e}function S(e){return ce.bold(e)}var Ye=new wt("scan").description("Quick overview of the current project's environment variable health").action(async()=>{let e=process.cwd(),o=await $(e);o||(console.log(v("No package.json found. Are you in a project directory?")),process.exit(1));let t=await A(o);console.log(),console.log(l(`${_.bold("Enve Scan Report")} \u2014 ${t.name}`)),console.log(l(_.dim(t.path))),console.log(),console.log(l(`.env files found: ${t.envFiles.length}`));for(let n of t.envFiles)console.log(l(`${L(`${n.filename.padEnd(18)} ${n.variables.length} variables`)}`,2));if(console.log(),console.log(l(`Code references: ${t.referenceCount} variables used`)),console.log(),t.issues.length===0)console.log(l(b("No issues found. Great job!")));else{console.log(l(`Issues found: ${t.issues.length}`));let n=t.issues.filter(a=>a.severity==="error"),r=t.issues.filter(a=>a.severity==="warning"),s=t.issues.filter(a=>a.severity==="info");for(let a of n)console.log(l(v(a.message),2));for(let a of r)console.log(l(j(a.message),2));for(let a of s)console.log(l(b(a.message),2))}console.log(),console.log(l(`Score: ${kt(t.score.score)} [ ${t.score.grade} ]`)),console.log(l(_.dim(`${t.score.errorCount} errors, ${t.score.warningCount} warnings`))),console.log(),console.log(l("Run `enve doctor` for detailed recommendations."))});function kt(e){let o=`${e}/100`;return e>=90?_.bgGreen.black(` ${o} `):e>=70?_.bgYellow.black(` ${o} `):e>=50?_.red(o):_.bgRed.white(` ${o} `)}import{Command as Et}from"commander";import N from"chalk";var qe=new Et("doctor").description("Comprehensive health check with detailed recommendations").action(async()=>{let e=process.cwd(),o=await $(e);o||(console.log(v("No package.json found. Are you in a project directory?")),process.exit(1)),await Ee(o)});async function Ee(e){let o=await A(e),t=await O(e);console.log(),console.log(l(S(`Enve Health Check \u2014 ${o.name}`))),console.log(l(N.dim(o.path))),console.log();let n=o.issues.filter(i=>i.type==="gitignore"||i.type==="suspicious"&&i.key===".env.example"),r=o.issues.filter(i=>i.type==="missing"||i.type==="unused"),s=o.issues.filter(i=>i.type==="secret-risk"),a=o.issues.filter(i=>i.type==="invalid"||i.type==="syntax-error");ke("[File Structure]",n),$t(r,t),ke("[Security]",s),ke("[Validation]",a),Ct(o.issues),console.log(),console.log(l(`Score: ${St(o.score.score)} [ ${o.score.grade} ]`)),console.log(l(N.dim(`${o.score.errorCount} errors, ${o.score.warningCount} warnings, ${o.score.infoCount} info`))),console.log()}function ke(e,o){if(console.log(l(S(e))),o.length===0)console.log(l(b("No issues")));else for(let t of o){let n=t.severity==="error"?v:t.severity==="warning"?j:b;console.log(l(n(t.message),2)),t.suggestion&&console.log(l(N.dim(`\u2192 ${t.suggestion}`),3))}console.log()}function $t(e,o){let t=o.reduce((n,r)=>n+r.variables.length,0);if(console.log(l(S(`[Variables \u2014 ${t} defined]`))),e.length===0)console.log(l(b("All variables are used and defined")));else for(let n of e){let r=n.severity==="error"?v:j,s=n.message;n.type==="missing"&&n.hasFallback&&n.fallbackValue&&(s+=N.dim(` (fallback: ${n.fallbackValue})`)),console.log(l(r(s),2)),n.suggestion&&console.log(l(N.dim(`\u2192 ${n.suggestion}`),3))}console.log()}function Ct(e){let o=e.filter(s=>s.suggestion);if(o.length===0)return;console.log(l(S(`[Recommendations \u2014 ${o.length} actions needed]`)));let t=o.filter(s=>s.severity==="error"),n=o.filter(s=>s.severity==="warning"),r=o.filter(s=>s.severity==="info");t.length>0&&(console.log(l(N.red.bold("HIGH PRIORITY:"))),t.forEach((s,a)=>{console.log(l(`${a+1}. ${s.suggestion}`,2))}),console.log()),n.length>0&&(console.log(l(N.yellow.bold("MEDIUM PRIORITY:"))),n.forEach((s,a)=>{console.log(l(`${a+1}. ${s.suggestion}`,2))}),console.log()),r.length>0&&(console.log(l(N.blue.bold("LOW PRIORITY:"))),r.forEach((s,a)=>{console.log(l(`${a+1}. ${s.suggestion}`,2))}),console.log())}function St(e){let o=`${e}/100`;return e>=90?N.bgGreen.black(` ${o} `):e>=70?N.bgYellow.black(` ${o} `):e>=50?N.red(o):N.bgRed.white(` ${o} `)}import{Command as jt}from"commander";import Ce from"chalk";import{copyFile as It}from"fs/promises";import{createInterface as Pt}from"readline/promises";async function $e(e){let o=Pt({input:process.stdin,output:process.stdout});try{return(await o.question(`${e} `)).trim()}finally{o.close()}}async function I(e,o=!1){let n=await $e(`${e}${o?" (Y/n)":" (y/N)"}`);return n?/^y(es)?$/i.test(n):o}var ze=new jt("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 $(o);t||(console.log(v("No package.json found. Are you in a project directory?")),process.exit(1));let n=await O(t),r=await M(t),s=new Set(r.map(m=>m.key)),a=e.includeCommons?[]:["PORT","HOST","NODE_ENV","CI","PWD"],i=[];for(let m of n)if(!(m.filename===".env.example"||m.filename===".env.test"))for(let h of m.variables)a.includes(h.key)||s.has(h.key)||i.push({filename:m.filename,path:m.path,key:h.key,line:h.line});if(console.log(),console.log(l(S(`Unused variables in ${t}`))),console.log(),i.length===0){console.log(l(b("All variables are used")));return}let f=new Map;for(let m of i)f.set(m.filename,[...f.get(m.filename)??[],m]);for(let[m,h]of f){console.log(l(m));for(let x of h)console.log(l(j(`${x.key.padEnd(20)} (line ${x.line})`),2))}if(console.log(),console.log(l(`${i.length} unused variables found. They may be safe to remove.`)),e.dryRun){console.log(l(Ce.dim("Dry run: no changes made.")));return}if(!e.fix){console.log(l("Run `enve unused --fix` to remove them, or `--dry-run` to preview."));return}if(!await I(`Remove ${i.length} unused variables from .env files?`)){console.log(l(Ce.dim("Aborted. No changes made.")));return}let c=new Map;for(let m of i)c.set(m.path,[...c.get(m.path)??[],m]);let p=0;for(let[m,h]of c){let x=`${m}.backup.${Tt(new Date)}`;await It(m,x);let C=(await w(m)??"").split(`
41
+ `),D=new Set(h.map(ie=>ie.line-1)),K=C.filter((ie,Io)=>!D.has(Io));await E(m,K.join(`
42
+ `)),p+=h.length,console.log(l(Ce.dim(`Backup saved: ${x}`),2))}console.log(),console.log(l(b(`Removed ${p} unused variables.`)))});function Tt(e){return e.toISOString().replace(/[:.]/g,"-").slice(0,19)}import{Command as At}from"commander";import ne from"chalk";import{join as Xe}from"path";var Ze=new Set(["PATH","HOME","USER","SHELL","PWD","OLDPWD","NODE","NODE_PATH","TMPDIR","LANG","LOGNAME","TERM","EDITOR","SHLVL","_","__CF_USER_TEXT_ENCODING"]),Je=new At("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 $(o);t||(console.log(v("No package.json found. Are you in a project directory?")),process.exit(1));let n=await O(t),r=await M(t),s=new Set(n.flatMap(f=>f.variables.map(u=>u.key))),a=r.filter(f=>!s.has(f.key)&&!f.hasFallback&&!Ze.has(f.key)),i=e.fallbacks?r.filter(f=>!s.has(f.key)&&f.hasFallback&&!Ze.has(f.key)):[];if(console.log(),console.log(l(S(`Missing environment variables in ${t}`))),console.log(),a.length===0&&i.length===0){console.log(l(b("All referenced variables are defined")));return}if(a.length>0){console.log(l(S("Missing (no fallback \u2014 WILL cause runtime errors)")));for(let f of a)console.log(l(v(`${f.key}`),2)),console.log(l(ne.dim(`\u2192 ${f.file}:${f.line}`),3)),f.context&&console.log(l(ne.dim(f.context.split(`
43
+ `).map(u=>` ${u}`).join(`
44
+ `)),3));console.log()}if(i.length>0){console.log(l(S("Missing (has fallback \u2014 will use default value)")));for(let f of i)console.log(l(j(`${f.key}`),2)),console.log(l(ne.dim(`\u2192 fallback: ${f.fallbackValue??"unknown"}`),3)),console.log(l(ne.dim(`\u2192 ${f.file}:${f.line}`),3));console.log()}console.log(l(`${v(`${a.length} truly missing`)} \xB7 ${j(`${i.length} with fallback defaults`)}`)),e.add?await Ft(t,n,a):(console.log(),console.log(l("Run with `--add` to interactively add missing variables to .env")))});async function Ft(e,o,t){let n=Xe(e,".env");if(!o.find(u=>u.filename===".env")){console.log(v("No .env file found. Cannot add variables."));return}let s=[],a=[];for(let u of t){let c=await $e(`Value for ${u.key}:`);if(!c)continue;s.push(`${u.key}=${c}`),await I(`Add ${u.key} to .env.example?`,!1)&&a.push(`${u.key}=your_${u.key.toLowerCase()}`)}if(s.length===0){console.log(l(ne.dim("No variables added.")));return}let i=await w(n)??"",f=i.endsWith(`
45
+ `)||i===""?"":`
46
+ `;if(await E(n,`${i}${f}${s.join(`
46
47
  `)}
47
- `),s.length>0){let m=Ne(e,".env.example"),g=await k(m)??"",v=g.endsWith(`
48
- `)||g===""?"":`
49
- `;await C(m,`${g}${v}${s.join(`
48
+ `),a.length>0){let u=Xe(e,".env.example"),c=await w(u)??"",p=c.endsWith(`
49
+ `)||c===""?"":`
50
+ `;await E(u,`${c}${p}${a.join(`
50
51
  `)}
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 pt}from"commander";import K from"chalk";function Le(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 Re(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 Me(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 _e(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 Ge(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 Ue(e){return/SECRET|KEY|TOKEN|PASSWORD|PRIVATE|ACCESS|CREDENTIAL|AUTH/i.test(e)}import{copyFile as gt}from"fs/promises";var He=new pt("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 g=vt(m);if(g.valid)console.log(a(x(`${m.key}=${We(m.value)}`)));else{s++,console.log(a(y(`${m.key}=${We(m.value)}`))),g.message&&console.log(a(K.dim(` ${g.message}`),2)),g.suggestion&&console.log(a(K.dim(` Suggestion: ${g.suggestion}`),2));let v=yt(m);v!==void 0&&(console.log(a(K.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=>!Ke(v.key));if(m.length===0){console.log(a(K.dim("No auto-fixable issues (secrets are never auto-fixed).")));return}if(!await N(`Apply ${m.length} auto-fixes to .env?`)){console.log(a(K.dim("Aborted. No changes made.")));return}await ht(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 vt(e){let o=e.key,t=e.value;return o==="PORT"||o.endsWith("_PORT")?Le(t):o==="NODE_ENV"?Me(t):o.endsWith("_URL")||o==="DATABASE_URL"||o==="REDIS_URL"?Re(t):Ke(o)?_e(t,o):o==="DEBUG"||o==="VERBOSE"||o.endsWith("_ENABLED")?Ge(t,o):{valid:!0}}function Ke(e){return/SECRET|KEY|TOKEN|PRIVATE/i.test(e)&&e!=="API_KEY"}function yt(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 ht(e,o){let t=`${e}.backup.validate.${xt(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 g=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`${g}${m}=${$}${i.get(m)}${b}`}return s});await C(e,r.join(`
53
- `)),console.log(a(K.dim(`Backup saved: ${t}`),2))}function xt(e){return e.toISOString().replace(/[:.]/g,"-").slice(0,19)}function We(e,o=40){return e.length<=o?e:e.slice(0,o)+"..."}import{Command as bt}from"commander";import Y from"chalk";import{join as Ye}from"path";var qe=new bt("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=Ye(t,".env");await k(n)||(console.log(y("No .env file found. Cannot generate .env.example.")),process.exit(1));let r=Ye(t,e.output),s=await k(r),l=fe(n),f=[],m=0,g=0;for(let d of l)e.allSecrets||d.isSecret||Ue(d.key)?(f.push(`${d.key}=your_${d.key.toLowerCase()}`),m++):(f.push(`${d.key}=${d.value}`),g++);let v=`${f.join(`
52
+ `)}console.log(),console.log(l(b(`Added ${s.length} variables to .env`))),a.length>0&&console.log(l(b(`Added ${a.length} placeholders to .env.example`)))}import{Command as Dt}from"commander";import J from"chalk";function Qe(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 eo(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 oo(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 to(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 no(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 re(e){return/SECRET|KEY|TOKEN|PASSWORD|PRIVATE|ACCESS|CREDENTIAL|AUTH/i.test(e)}import{copyFile as Nt}from"fs/promises";var so=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 $(o);t||(console.log(v("No package.json found. Are you in a project directory?")),process.exit(1));let r=(await O(t)).find(u=>u.filename===".env");r||(console.log(v("No .env file found. Cannot validate.")),process.exit(1));let s=r.variables;console.log(),console.log(l(S(`Validating environment variables \u2014 ${r.filename}`))),console.log();let a=0,i=0,f=[];for(let u of s){let c=Rt(u);if(c.valid)console.log(l(b(`${u.key}=${ro(u.value)}`)));else{a++,console.log(l(v(`${u.key}=${ro(u.value)}`))),c.message&&console.log(l(J.dim(` ${c.message}`),2)),c.suggestion&&console.log(l(J.dim(` Suggestion: ${c.suggestion}`),2));let p=Bt(u);p!==void 0&&(console.log(l(J.yellow(` Auto-fix available: ${u.key}=${p}`),2)),f.push({key:u.key,oldValue:u.value,newValue:p}))}}if(console.log(),console.log(l(`${s.length} variables checked, ${a} errors, ${i} warnings`)),e.fix){let u=f.filter(p=>!io(p.key));if(u.length===0){console.log(l(J.dim("No auto-fixable issues (secrets are never auto-fixed).")));return}if(!await I(`Apply ${u.length} auto-fixes to .env?`)){console.log(l(J.dim("Aborted. No changes made.")));return}await Ot(r.path,u),console.log(),console.log(l(b(`Applied ${u.length} fixes to .env`)))}else f.length>0&&console.log(l("Run with `--fix` to apply available auto-fixes."))});function Rt(e){let o=e.key,t=e.value;return o==="PORT"||o.endsWith("_PORT")?Qe(t):o==="NODE_ENV"?oo(t):o.endsWith("_URL")||o==="DATABASE_URL"||o==="REDIS_URL"?eo(t):io(o)?to(t,o):o==="DEBUG"||o==="VERBOSE"||o.endsWith("_ENABLED")?no(t,o):{valid:!0}}function io(e){return/SECRET|KEY|TOKEN|PRIVATE/i.test(e)&&e!=="API_KEY"}function Bt(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 Ot(e,o){let t=`${e}.backup.validate.${Vt(new Date)}`;await Nt(e,t);let n=await w(e)??"",r=new Map(o.map(a=>[a.key,a.newValue])),s=n.split(`
53
+ `).map(a=>{let i=a.trim();if(!i||i.startsWith("#"))return a;let f=i.indexOf("=");if(f===-1)return a;let u=i.slice(0,f).trim();if(u.startsWith("export ")&&(u=u.slice(7).trim()),r.has(u)){let c=a.match(/^\s*/)?.[0]??"",p=a.slice(a.indexOf("=")+1),m=p.indexOf(" #"),h=m!==-1?p.slice(m):"",y=(m!==-1?p.slice(0,m):p).match(/^\s*/)?.[0]??"";return`${c}${u}=${y}${r.get(u)}${h}`}return a});await E(e,s.join(`
54
+ `)),console.log(l(J.dim(`Backup saved: ${t}`),2))}function Vt(e){return e.toISOString().replace(/[:.]/g,"-").slice(0,19)}function ro(e,o=40){return e.length<=o?e:e.slice(0,o)+"..."}import{Command as Lt}from"commander";import Q from"chalk";import{join as ao}from"path";var lo=new Lt("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 $(o);t||(console.log(v("No package.json found. Are you in a project directory?")),process.exit(1));let n=ao(t,".env");await w(n)||(console.log(v("No .env file found. Cannot generate .env.example.")),process.exit(1));let s=ao(t,e.output),a=await w(s),i=we(n),f=[],u=0,c=0;for(let m of i)e.allSecrets||m.isSecret||re(m.key)?(f.push(`${m.key}=your_${m.key.toLowerCase()}`),u++):(f.push(`${m.key}=${m.value}`),c++);let p=`${f.join(`
54
55
  `)}
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(z(`${g} public variables \u2192 copied as-is`),2)),console.log(a(z(`${m} secrets redacted`),2)),s){let d=kt(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"?Y.green("+"):b.type==="removed"?Y.red("-"):Y.yellow("~");console.log(a(`${F} ${b.type}: ${b.key}`,2))}if(e.dryRun){console.log(),console.log(a(Y.dim("Dry run \u2014 no changes made.")));return}if(!e.overwrite&&!await N(`Update ${e.output}?`)){console.log(a(Y.dim("Aborted. No changes made.")));return}}else e.dryRun&&(console.log(),console.log(a(Y.dim("Dry run \u2014 no changes made."))));e.dryRun||(await C(r,v),console.log(),console.log(a(x(`Created ${e.output}`))))});function kt(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 re}from"commander";var Xe=new re("hook").description("Install, manage, and check a pre-commit git hook that prevents .env commits").addCommand(new re("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 ke(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(ve("Blocks commits of .env, .env.local, .env.production files"),2)),console.log(a(ve("Shows a helpful message with next steps"),2)),console.log(a(ve("Can be bypassed with --no-verify (emergency only)"),2)),console.log(),console.log(a("To remove: enve hook uninstall"))})).addCommand(new re("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 Z(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 re("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 we(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 ve(e){return`\u2022 ${e}`}import{Command as It}from"commander";import{render as St}from"ink";import so,{useState as jt,useCallback as io}from"react";import c,{useState as no,useMemo as wt}from"react";import{Box as h,Text as p,useInput as Et,useApp as $t,useStdout as Pt}from"ink";import j from"react";import{Box as J,Text as q}from"ink";import se from"react";import{Box as Ze,Text as eo}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"},ze={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"}},Qe={error:{color:u.error,symbol:"\u25CF"},warning:{color:u.warning,symbol:"\u25CF"},info:{color:u.info,symbol:"\u25CF"}},Je={missing:"\u2298",unused:"\u25CB",invalid:"\u2715",suspicious:"\u25C6","secret-risk":"\u25B2","syntax-error":"\u2715",gitignore:"\u25C9"},ie={sidebarWidth:34,maxContentWidth:86,paddingX:2,paddingY:1};function ae({score:e,compact:o=!1}){let t=ze[e.grade];return o?se.createElement(Ze,null,se.createElement(eo,{backgroundColor:t.bg,color:t.text,bold:!0}," ",e.score," ")):se.createElement(Ze,null,se.createElement(eo,{backgroundColor:t.bg,color:t.text,bold:!0}," ",e.score," ",e.grade," "))}function oo({project:e,isSelected:o,isFocused:t}){let{score:n}=e,i=o?u.ink:u.border,r=t?"\u203A":" ";return j.createElement(J,{borderStyle:"single",borderColor:i,paddingX:1,paddingY:1,flexDirection:"column"},j.createElement(J,{justifyContent:"space-between"},j.createElement(J,null,j.createElement(q,{color:o?u.ink:u.muted},r," "),j.createElement(q,{bold:!0,color:u.ink},e.name)),j.createElement(ae,{score:n,compact:!0})),j.createElement(J,{marginTop:1},j.createElement(q,{color:u.muted},e.path)),j.createElement(J,{marginTop:1,columnGap:2},n.errorCount>0&&j.createElement(q,{color:u.error},n.errorCount," err"),n.warningCount>0&&j.createElement(q,{color:u.warning},n.warningCount," warn"),n.totalIssues===0&&j.createElement(q,{color:u.success},"Clean")))}import P from"react";import{Box as O,Text as L}from"ink";function to({issues:e,maxRows:o=12}){if(e.length===0)return P.createElement(O,{paddingY:1},P.createElement(L,{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=Qe[r.severity],f=Je[r.type]??"\u2022";return P.createElement(O,{key:`${r.type}-${r.key}-${s}`,flexDirection:"column",marginBottom:1},P.createElement(O,null,P.createElement(L,{color:l.color},f," "),P.createElement(L,{bold:!0,color:u.ink},r.key),P.createElement(L,{color:u.subtle}," \u2014 ",r.type)),P.createElement(O,{marginLeft:2},P.createElement(L,{color:u.muted},r.message)),r.suggestion&&P.createElement(O,{marginLeft:2},P.createElement(L,{color:u.subtle},"\u2192 ",r.suggestion)))}),i>0&&P.createElement(O,{marginTop:1},P.createElement(L,{color:u.subtle},"+ ",i," more issue",i===1?"":"s")))}function ro({projects:e,version:o,onRunDoctor:t,onRefresh:n}){let{exit:i}=$t(),{stdout:r}=Pt(),[s,l]=no(0),[f,m]=no(!1),g=r.rows??24,v=e[s],d=Math.max(4,Math.floor((g-10)/5)),b=wt(()=>{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 Et(($,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(p,{bold:!0,color:u.ink},"enve dashboard"),c.createElement(h,{marginTop:1},c.createElement(p,{color:u.muted},"No projects tracked yet.")),c.createElement(h,{marginTop:1},c.createElement(p,{color:u.subtle},"Run `enve scan` or `enve doctor` in a project to add it."))):c.createElement(h,{flexDirection:"column",width:"100%",paddingX:ie.paddingX,paddingY:ie.paddingY},c.createElement(h,{justifyContent:"space-between",width:"100%",marginBottom:1},c.createElement(h,null,c.createElement(p,{bold:!0,color:u.ink},"enve"),c.createElement(p,{color:u.muted}," dashboard")),c.createElement(p,{color:u.subtle},"v",o)),c.createElement(h,{flexGrow:1,width:"100%"},c.createElement(h,{width:ie.sidebarWidth,flexDirection:"column",borderStyle:"single",borderColor:u.border,paddingX:1,paddingY:1,marginRight:2},c.createElement(h,{marginBottom:1},c.createElement(p,{bold:!0,color:u.ink},"Projects"),c.createElement(p,{color:u.subtle}," (",e.length,")")),c.createElement(h,{flexDirection:"column",rowGap:1,flexGrow:1},b.map(($,T)=>{let B=F+T;return c.createElement(oo,{key:$.path,project:$,isSelected:B===s,isFocused:B===s})})),e.length>d&&c.createElement(h,{marginTop:1},c.createElement(p,{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(Tt,{project:v}):c.createElement(h,null,c.createElement(p,{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(p,{color:u.muted},c.createElement(p,{bold:!0},"\u2191/\u2193")," navigate"),c.createElement(p,{color:u.muted},c.createElement(p,{bold:!0},"Enter")," doctor"),c.createElement(p,{color:u.muted},c.createElement(p,{bold:!0},"r")," refresh"),c.createElement(p,{color:u.muted},c.createElement(p,{bold:!0},"q")," quit")),c.createElement(p,{color:u.subtle},"enve-doctor v",o)))}function Tt({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(p,{bold:!0,color:u.ink},e.name),c.createElement(ae,{score:e.score})),c.createElement(h,{marginBottom:1},c.createElement(p,{color:u.muted},e.path)),c.createElement(h,{flexDirection:"row",columnGap:4,marginBottom:1},c.createElement(ye,{label:"Env vars",value:String(e.envCount)}),c.createElement(ye,{label:"References",value:String(e.referenceCount)}),c.createElement(ye,{label:"Issues",value:String(e.score.totalIssues)})),c.createElement(h,{flexDirection:"row",columnGap:4,marginBottom:1},c.createElement(he,{label:"Git repo",ok:e.hasGit}),c.createElement(he,{label:"Git hook",ok:e.hookInstalled}),c.createElement(he,{label:".gitignore",ok:e.hasEnvInGitignore})),c.createElement(h,{marginTop:1,marginBottom:1},c.createElement(p,{color:u.subtle},"Env files: "),c.createElement(p,{color:u.ink},o)),c.createElement(h,{marginTop:1,marginBottom:1},c.createElement(p,{color:u.borderStrong},"\u2500".repeat(60))),c.createElement(h,{marginTop:1,marginBottom:1},c.createElement(p,{bold:!0,color:u.ink},"Top issues")),c.createElement(to,{issues:e.issues,maxRows:8}))}function ye({label:e,value:o}){return c.createElement(h,{flexDirection:"column"},c.createElement(p,{color:u.subtle},e),c.createElement(p,{bold:!0,color:u.ink},o))}function he({label:e,ok:o}){return c.createElement(h,null,c.createElement(p,{color:o?u.success:u.error},o?"\u2713":"\u2717"," "),c.createElement(p,{color:u.muted},e))}var le={name:"enve-doctor",version:"1.0.2",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 ao=new It("dashboard").description("Interactive terminal dashboard showing all tracked projects").action(async()=>{let e=oe();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 lo(e.map(t=>t.path));St(so.createElement(Ft,{initialProjects:o,trackedPaths:e.map(t=>t.path),version:le.version}))});function Ft({initialProjects:e,trackedPaths:o,version:t}){let[n,i]=jt(e),r=io(async l=>{await de(l.path)},[]),s=io(async()=>{let l=await lo(o);i(l)},[o]);return so.createElement(ro,{projects:n,version:t,onRunDoctor:r,onRefresh:s})}async function lo(e){return await Promise.all(e.map(t=>H(t)))}var A=new Dt;A.name("enve").description("Environment Variable Doctor \u2014 scan, validate, and protect your env files").version(le.version);A.addCommand(De);A.addCommand(Ae);A.addCommand(Be);A.addCommand(Oe);A.addCommand(He);A.addCommand(qe);A.addCommand(Xe);A.addCommand(ao);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();
56
+ `;if(console.log(),console.log(l(S(`Generating ${e.output} from .env`))),console.log(),console.log(l(`Variables: ${i.length} total`)),console.log(l(L(`${c} public variables \u2192 copied as-is`),2)),console.log(l(L(`${u} secrets redacted`),2)),a){let m=Mt(a,p);if(m.length===0){console.log(),console.log(l(b(`${e.output} is already up to date.`)));return}console.log(),console.log(l(S("Changes since last generation:")));for(let h of m){let x=h.type==="added"?Q.green("+"):h.type==="removed"?Q.red("-"):Q.yellow("~");console.log(l(`${x} ${h.type}: ${h.key}`,2))}if(e.dryRun){console.log(),console.log(l(Q.dim("Dry run \u2014 no changes made.")));return}if(!e.overwrite&&!await I(`Update ${e.output}?`)){console.log(l(Q.dim("Aborted. No changes made.")));return}}else e.dryRun&&(console.log(),console.log(l(Q.dim("Dry run \u2014 no changes made."))));e.dryRun||(await E(s,p),console.log(),console.log(l(b(`Created ${e.output}`))))});function Mt(e,o){let t=new Map,n=new Map;for(let s of e.split(`
57
+ `)){let a=s.trim();if(!a||a.startsWith("#"))continue;let i=a.indexOf("=");i!==-1&&t.set(a.slice(0,i).trim(),a.slice(i+1).trim())}for(let s of o.split(`
58
+ `)){let a=s.trim();if(!a||a.startsWith("#"))continue;let i=a.indexOf("=");i!==-1&&n.set(a.slice(0,i).trim(),a.slice(i+1).trim())}let r=[];for(let[s,a]of n)t.has(s)?t.get(s)!==a&&r.push({type:"updated",key:s}):r.push({type:"added",key:s});for(let s of t.keys())n.has(s)||r.push({type:"removed",key:s});return r}import{Command as fe}from"commander";var co=new fe("hook").description("Install, manage, and check a pre-commit git hook that prevents .env commits").addCommand(new fe("install").description("Install the enve pre-commit hook").action(async()=>{let e=process.cwd(),o=await $(e);(!o||!await Y(o))&&(console.log(v("Not a git repository. Run `git init` first.")),process.exit(1)),await De(o),console.log(),console.log(l(b("Pre-commit hook installed"))),console.log(l(`Location: ${q(o)}`)),console.log(),console.log(l("What it does:")),console.log(l(Se("Blocks commits of .env, .env.local, .env.production files"),2)),console.log(l(Se("Shows a helpful message with next steps"),2)),console.log(l(Se("Can be bypassed with --no-verify (emergency only)"),2)),console.log(),console.log(l("To remove: enve hook uninstall"))})).addCommand(new fe("status").description("Check the status of the enve pre-commit hook").action(async()=>{let e=process.cwd(),o=await $(e);(!o||!await Y(o))&&(console.log(v("Not a git repository.")),process.exit(1));let t=await ae(o);console.log(),t?(console.log(l(b("Pre-commit hook: Installed (enve protection active)"))),console.log(l(`Location: ${q(o)}`))):(console.log(l(j("Pre-commit hook: Not installed"))),console.log(l("Run `enve hook install` to enable .env commit protection.")))})).addCommand(new fe("uninstall").description("Remove the enve pre-commit hook").action(async()=>{let e=process.cwd(),o=await $(e);(!o||!await Y(o))&&(console.log(v("Not a git repository.")),process.exit(1));let t=await Ne(o);console.log(),t?(console.log(l(b("Pre-commit hook removed"))),console.log(l("Run `enve hook install` to re-enable."))):console.log(l(j("No enve hook found to remove.")))}));function Se(e){return`\u2022 ${e}`}import{Command as Yt}from"commander";import{render as qt}from"ink";import wo,{useState as zt,useCallback as bo}from"react";import d,{useState as vo,useMemo as _t}from"react";import{Box as P,Text as k,useInput as Gt,useApp as Wt,useStdout as Ut}from"ink";import R from"react";import{Box as se,Text as ee}from"ink";import de from"react";import{Box as po,Text as go}from"ink";var g={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"},uo={Excellent:{text:g.excellent,bg:g.excellentBg,label:"Excellent"},Good:{text:g.good,bg:g.goodBg,label:"Good"},"Needs attention":{text:g.attention,bg:g.attentionBg,label:"Needs attention"},Critical:{text:g.critical,bg:g.criticalBg,label:"Critical"}},fo={error:{color:g.error,symbol:"\u25CF"},warning:{color:g.warning,symbol:"\u25CF"},info:{color:g.info,symbol:"\u25CF"}},mo={missing:"\u2298",unused:"\u25CB",invalid:"\u2715",suspicious:"\u25C6","secret-risk":"\u25B2","syntax-error":"\u2715",gitignore:"\u25C9"},me={sidebarWidth:34,maxContentWidth:86,paddingX:2,paddingY:1};function pe({score:e,compact:o=!1}){let t=uo[e.grade];return o?de.createElement(po,null,de.createElement(go,{backgroundColor:t.bg,color:t.text,bold:!0}," ",e.score," ")):de.createElement(po,null,de.createElement(go,{backgroundColor:t.bg,color:t.text,bold:!0}," ",e.score," ",e.grade," "))}function yo({project:e,isSelected:o,isFocused:t}){let{score:n}=e,r=o?g.ink:g.border,s=t?"\u203A":" ";return R.createElement(se,{borderStyle:"single",borderColor:r,paddingX:1,paddingY:1,flexDirection:"column"},R.createElement(se,{justifyContent:"space-between"},R.createElement(se,null,R.createElement(ee,{color:o?g.ink:g.muted},s," "),R.createElement(ee,{bold:!0,color:g.ink},e.name)),R.createElement(pe,{score:n,compact:!0})),R.createElement(se,{marginTop:1},R.createElement(ee,{color:g.muted},e.path)),R.createElement(se,{marginTop:1,columnGap:2},n.errorCount>0&&R.createElement(ee,{color:g.error},n.errorCount," err"),n.warningCount>0&&R.createElement(ee,{color:g.warning},n.warningCount," warn"),n.totalIssues===0&&R.createElement(ee,{color:g.success},"Clean")))}import F from"react";import{Box as G,Text as W}from"ink";function ho({issues:e,maxRows:o=12}){if(e.length===0)return F.createElement(G,{paddingY:1},F.createElement(W,{color:g.success},"No issues found. Great job."));let t=[...e].sort((s,a)=>{let i={error:0,warning:1,info:2};return i[s.severity]-i[a.severity]}),n=t.slice(0,o),r=t.length-n.length;return F.createElement(G,{flexDirection:"column",width:"100%"},n.map((s,a)=>{let i=fo[s.severity],f=mo[s.type]??"\u2022";return F.createElement(G,{key:`${s.type}-${s.key}-${a}`,flexDirection:"column",marginBottom:1},F.createElement(G,null,F.createElement(W,{color:i.color},f," "),F.createElement(W,{bold:!0,color:g.ink},s.key),F.createElement(W,{color:g.subtle}," \u2014 ",s.type)),F.createElement(G,{marginLeft:2},F.createElement(W,{color:g.muted},s.message)),s.suggestion&&F.createElement(G,{marginLeft:2},F.createElement(W,{color:g.subtle},"\u2192 ",s.suggestion)))}),r>0&&F.createElement(G,{marginTop:1},F.createElement(W,{color:g.subtle},"+ ",r," more issue",r===1?"":"s")))}function xo({projects:e,version:o,onRunDoctor:t,onRefresh:n}){let{exit:r}=Wt(),{stdout:s}=Ut(),[a,i]=vo(0),[f,u]=vo(!1),c=s.rows??24,p=e[a],m=Math.max(4,Math.floor((c-10)/5)),h=_t(()=>{if(e.length<=m)return e;let y=Math.max(0,a-Math.floor(m/2)),C=y+m;return C>e.length&&(C=e.length,y=Math.max(0,C-m)),e.slice(y,C)},[e,a,m]),x=e.length<=m?0:Math.max(0,a-Math.floor(m/2));return Gt((y,C)=>{f||(C.upArrow||y==="k"?i(D=>Math.max(0,D-1)):C.downArrow||y==="j"?i(D=>Math.min(e.length-1,D+1)):C.return?p&&(u(!0),Promise.resolve(t(p)).then(()=>r())):y==="r"?Promise.resolve(n()):(y==="q"||C.escape)&&(u(!0),r()))},{isActive:!f}),e.length===0?d.createElement(P,{flexDirection:"column",padding:2},d.createElement(k,{bold:!0,color:g.ink},"enve dashboard"),d.createElement(P,{marginTop:1},d.createElement(k,{color:g.muted},"No projects tracked yet.")),d.createElement(P,{marginTop:1},d.createElement(k,{color:g.subtle},"Run `enve scan` or `enve doctor` in a project to add it."))):d.createElement(P,{flexDirection:"column",width:"100%",paddingX:me.paddingX,paddingY:me.paddingY},d.createElement(P,{justifyContent:"space-between",width:"100%",marginBottom:1},d.createElement(P,null,d.createElement(k,{bold:!0,color:g.ink},"enve"),d.createElement(k,{color:g.muted}," dashboard")),d.createElement(k,{color:g.subtle},"v",o)),d.createElement(P,{flexGrow:1,width:"100%"},d.createElement(P,{width:me.sidebarWidth,flexDirection:"column",borderStyle:"single",borderColor:g.border,paddingX:1,paddingY:1,marginRight:2},d.createElement(P,{marginBottom:1},d.createElement(k,{bold:!0,color:g.ink},"Projects"),d.createElement(k,{color:g.subtle}," (",e.length,")")),d.createElement(P,{flexDirection:"column",rowGap:1,flexGrow:1},h.map((y,C)=>{let D=x+C;return d.createElement(yo,{key:y.path,project:y,isSelected:D===a,isFocused:D===a})})),e.length>m&&d.createElement(P,{marginTop:1},d.createElement(k,{color:g.subtle},"Use \u2191/\u2193 to scroll"))),d.createElement(P,{flexGrow:1,flexDirection:"column",borderStyle:"single",borderColor:g.border,paddingX:2,paddingY:1},p?d.createElement(Ht,{project:p}):d.createElement(P,null,d.createElement(k,{color:g.muted},"Select a project to view details.")))),d.createElement(P,{justifyContent:"space-between",width:"100%",borderStyle:"single",borderColor:g.border,paddingX:1,marginTop:1},d.createElement(P,{columnGap:3},d.createElement(k,{color:g.muted},d.createElement(k,{bold:!0},"\u2191/\u2193")," navigate"),d.createElement(k,{color:g.muted},d.createElement(k,{bold:!0},"Enter")," doctor"),d.createElement(k,{color:g.muted},d.createElement(k,{bold:!0},"r")," refresh"),d.createElement(k,{color:g.muted},d.createElement(k,{bold:!0},"q")," quit")),d.createElement(k,{color:g.subtle},"enve-doctor v",o)))}function Ht({project:e}){let o=e.envFiles.map(t=>t.filename).join(", ")||"none";return d.createElement(P,{flexDirection:"column",width:"100%"},d.createElement(P,{justifyContent:"space-between",alignItems:"center",marginBottom:1},d.createElement(k,{bold:!0,color:g.ink},e.name),d.createElement(pe,{score:e.score})),d.createElement(P,{marginBottom:1},d.createElement(k,{color:g.muted},e.path)),d.createElement(P,{flexDirection:"row",columnGap:4,marginBottom:1},d.createElement(Pe,{label:"Env vars",value:String(e.envCount)}),d.createElement(Pe,{label:"References",value:String(e.referenceCount)}),d.createElement(Pe,{label:"Issues",value:String(e.score.totalIssues)})),d.createElement(P,{flexDirection:"row",columnGap:4,marginBottom:1},d.createElement(je,{label:"Git repo",ok:e.hasGit}),d.createElement(je,{label:"Git hook",ok:e.hookInstalled}),d.createElement(je,{label:".gitignore",ok:e.hasEnvInGitignore})),d.createElement(P,{marginTop:1,marginBottom:1},d.createElement(k,{color:g.subtle},"Env files: "),d.createElement(k,{color:g.ink},o)),d.createElement(P,{marginTop:1,marginBottom:1},d.createElement(k,{color:g.borderStrong},"\u2500".repeat(60))),d.createElement(P,{marginTop:1,marginBottom:1},d.createElement(k,{bold:!0,color:g.ink},"Top issues")),d.createElement(ho,{issues:e.issues,maxRows:8}))}function Pe({label:e,value:o}){return d.createElement(P,{flexDirection:"column"},d.createElement(k,{color:g.subtle},e),d.createElement(k,{bold:!0,color:g.ink},o))}function je({label:e,ok:o}){return d.createElement(P,null,d.createElement(k,{color:o?g.success:g.error},o?"\u2713":"\u2717"," "),d.createElement(k,{color:g.muted},e))}var ge={name:"enve-doctor",version:"1.3.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 ko=new Yt("dashboard").description("Interactive terminal dashboard showing all tracked projects").action(async()=>{let e=le();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 Eo(e.map(t=>t.path));qt(wo.createElement(Xt,{initialProjects:o,trackedPaths:e.map(t=>t.path),version:ge.version}))});function Xt({initialProjects:e,trackedPaths:o,version:t}){let[n,r]=zt(e),s=bo(async i=>{await Ee(i.path)},[]),a=bo(async()=>{let i=await Eo(o);r(i)},[o]);return wo.createElement(xo,{projects:n,version:t,onRunDoctor:s,onRefresh:a})}async function Eo(e){return await Promise.all(e.map(t=>A(t)))}import{Command as Zt}from"commander";import U from"chalk";var $o=new Zt("history").description("Track and display env health score history").option("--graph","Show ASCII graph of score over time").option("--limit <n>","Number of entries to show","10").action(async e=>{let o=process.cwd(),t=await $(o);t||(console.log(v("No package.json found. Are you in a project directory?")),process.exit(1)),await Jt(t,e.graph??!1,parseInt(e.limit,10))});async function Jt(e,o,t){let n=await A(e);He({projectPath:e,name:n.name,score:n.score.score,grade:n.score.grade,timestamp:new Date().toISOString()});let r=Ue(e).slice(-Math.max(1,t));if(console.log(),console.log(l(S(`Enve History \u2014 ${n.name}`))),console.log(),r.length===0){console.log(l("No history recorded yet."));return}o?en(r):Qt(r)}function Qt(e){console.log(l(`${U.bold("Date".padEnd(12))} ${U.bold("Score".padEnd(8))} Grade`)),console.log(l("-".repeat(32)));for(let o of e){let t=new Date(o.timestamp).toLocaleDateString(),n=`${o.score}/100`.padEnd(8);console.log(l(`${U.dim(t.padEnd(12))} ${n} ${o.grade}`))}}function en(e){let o=e.map(i=>i.score),t=Math.min(...o,0),n=Math.max(...o,100),r=Math.max(n-t,1),s=10;console.log(l(U.dim(`Score range: ${t}\u2013${n}`))),console.log();for(let i=s;i>=0;i--){let f=t+r*i/s,u=e.map(c=>c.score>=f?U.green("\u2588"):U.dim("\xB7")).join(" ");console.log(l(`${String(Math.round(f)).padStart(3)} \u2502 ${u}`))}let a=e.map(i=>new Date(i.timestamp).toLocaleDateString(void 0,{month:"short",day:"numeric"})).join(" ");console.log(l(` \u2514 ${U.dim(a)}`))}import{Command as ye}from"commander";import{join as Ie,basename as on}from"path";var So=new ye("sync").description("Share .env.example with a team via a shared directory").addCommand(new ye("set-path").description("Set the shared team directory").argument("<path>","Path to the shared directory").action(async e=>{Ge(e),console.log(l(b(`Team sync path set to ${e}`)))})).addCommand(new ye("push").description("Push .env.example to the shared team directory").action(async()=>{await Co("push")})).addCommand(new ye("pull").description("Pull .env.example from the shared team directory").action(async()=>{await Co("pull")}));async function Co(e){let o=process.cwd(),t=await $(o);t||(console.log(v("No package.json found. Are you in a project directory?")),process.exit(1));let n=We();n||(console.log(v("No sync path configured. Run `enve sync set-path <path>` first.")),process.exit(1));let r=on(t),s=Ie(t,".env.example"),a=Ie(n,r,".env.example");if(console.log(),console.log(l(S(`Enve Sync \u2014 ${e}`))),console.log(),console.log(l(`Project: ${r}`)),console.log(l(`Shared path: ${a}`)),console.log(),e==="push"){let i=await w(s);i||(console.log(v("No .env.example found to push.")),process.exit(1)),await ve(Ie(n,r)),await E(a,i),console.log(l(b("Pushed .env.example to team directory")))}else{await B(a)||(console.log(v("No shared .env.example found for this project.")),process.exit(1));let i=await w(a)??"";await E(s,i),console.log(l(b("Pulled .env.example from team directory")))}}import{Command as tn}from"commander";import{join as nn}from"path";var Po=new tn("docs").description("Generate documentation for environment variables").option("--output <path>","Output file","ENV.md").option("--overwrite","Overwrite existing file without prompting").action(async e=>{let o=process.cwd(),t=await $(o);t||(console.log(v("No package.json found. Are you in a project directory?")),process.exit(1)),await rn(t,e.output,e.overwrite??!1)});async function rn(e,o,t){let n=await A(e),r=await M(e),s=new Set(r.map(p=>p.key)),a=new Set(n.envFiles.flatMap(p=>p.variables.map(m=>m.key))),i=nn(e,o);if(await w(i)&&!t&&!await I(`${o} already exists. Overwrite?`)){console.log(l("Aborted. No changes made."));return}let u=new Set(n.issues.filter(p=>p.type==="missing").map(p=>p.key)),c=[];if(c.push(`# Environment Variables \u2014 ${n.name}`),c.push(""),c.push(`Generated by [enve](https://enve-lac.vercel.app) on ${new Date().toISOString().slice(0,10)}.`),c.push(""),c.push(`**Health score:** ${n.score.score}/100 (${n.score.grade})`),c.push(""),n.issues.length>0){c.push("## Issues"),c.push("");for(let p of n.issues){let m=p.severity==="error"?"\u{1F534}":p.severity==="warning"?"\u{1F7E1}":"\u{1F535}";c.push(`- ${m} **${p.key}** \u2014 ${p.message}`),p.suggestion&&c.push(` \u2192 ${p.suggestion}`)}c.push("")}c.push("## Variables"),c.push(""),c.push("| Variable | Source | Used in code | Secret | Notes |"),c.push("| --- | --- | --- | --- | --- |");for(let p of n.envFiles)for(let m of p.variables){let h=s.has(m.key),x=[];m.comment&&x.push(m.comment),m.isSecret&&x.push("treat as secret"),c.push(`| \`${m.key}\` | ${p.filename} | ${h?"Yes":"No"} | ${m.isSecret?"Yes":"No"} | ${x.join(", ")||"-"} |`)}if(u.size>0)for(let p of u)a.has(p)||c.push(`| \`${p}\` | \u2014 | No (missing) | No | Referenced in code but not defined |`);c.push(""),c.push("---"),c.push(""),c.push("Keep this file up to date by running `enve docs`."),c.push(""),await E(i,c.join(`
59
+ `)),console.log(),console.log(l(S(`Generated ${o}`))),console.log(),console.log(l(b(`Wrote ${c.length} lines to ${i}`)))}import{Command as sn}from"commander";import{join as H}from"path";var jo=new sn("fix").description("Apply safe, auto-fixable env-file corrections").option("--yes","Apply all fixes without prompting").action(async e=>{let o=process.cwd(),t=await $(o);t||(console.log(v("No package.json found. Are you in a project directory?")),process.exit(1)),await an(t,e.yes??!1)});async function an(e,o){let n=(await A(e)).issues;if(console.log(),console.log(l(S("Enve Auto-Fix"))),console.log(),n.length===0){console.log(l(b("No issues to fix")));return}let r=[];if(r.push(...await ln(e,n,o)),r.push(...await cn(e,n,o)),r.push(...await un(e,n,o)),r.push(...await fn(e,n,o)),r.push(...await mn(e,n,o)),console.log(),r.length===0)console.log(l(j("No fixes applied")));else{console.log(l(S("Applied fixes:")));for(let s of r)console.log(l(L(s),2));console.log(),console.log(l(b("Run `enve doctor` to see the updated score.")))}}async function ln(e,o,t){let n=o.filter(u=>u.type==="gitignore");if(n.length===0)return[];let r=n.map(u=>u.key),s=`Add ${r.join(", ")} to .gitignore?`;if(!t&&!await I(s))return[];let a=H(e,".gitignore"),f=(await w(a)??"").split(`
60
+ `);for(let u of r)f.some(c=>c.trim()===u)||f.push(u);return await E(a,f.join(`
61
+ `).trimEnd()+`
62
+ `),[`Added ${r.length} ${r.length===1?"entry":"entries"} to .gitignore`]}async function cn(e,o,t){let n=o.filter(x=>x.type==="secret-risk");if(n.length===0)return[];let r=n.map(x=>x.key);if(!t&&!await I(`Move ${r.join(", ")} from .env to .env.local?`))return[];let s=H(e,".env"),a=H(e,".env.local"),i=await w(s);if(!i)return[];let f=i.split(`
63
+ `),u=await w(a)??"",c=u?u.split(`
64
+ `):[],p=new Set(c.map(x=>x.trim()).filter(x=>x&&!x.startsWith("#")).map(x=>x.split("=")[0].trim())),m=new Set;for(let x of n){let y=(x.line??1)-1,C=f[y];if(!C)continue;let D=C.indexOf("=");if(D===-1)continue;let K=C.slice(0,D).trim();if(K!==x.key)continue;let ie=C.slice(D+1).trim();m.add(y),p.has(K)||(c.push(`${K}=${ie}`),p.add(K))}let h=f.filter((x,y)=>!m.has(y));return await E(s,h.join(`
65
+ `).trimEnd()+`
66
+ `),await E(a,c.join(`
67
+ `).trimEnd()+`
68
+ `),[`Moved ${m.size} secret ${m.size===1?"variable":"variables"} to .env.local`]}async function un(e,o,t){let n=/SECRET|KEY|TOKEN|PASSWORD|PRIVATE|ACCESS|CREDENTIAL/i,r=o.filter(m=>m.type==="unused"&&m.file===".env"&&!n.test(m.key));if(r.length===0)return[];let s=r.map(m=>m.key);if(!t&&!await I(`Remove unused ${s.join(", ")} from .env?`))return[];let a=H(e,".env"),i=await w(a);if(!i)return[];let f=i.split(`
69
+ `),u=new Set(s),c=f.filter(m=>{let h=m.trim();if(!h||h.startsWith("#"))return!0;let x=h.indexOf("=");if(x===-1)return!0;let y=h.slice(0,x).trim();return y.startsWith("export ")&&(y=y.slice(7).trim()),u.has(y)?(u.delete(y),!1):!0}),p=s.length-u.size;return p===0?[]:(await E(a,c.join(`
70
+ `).trimEnd()+`
71
+ `),[`Removed ${p} unused ${p===1?"variable":"variables"}`])}async function fn(e,o,t){let n=o.filter(c=>c.type==="missing"&&c.severity==="error");if(n.length===0)return[];let r=n.map(c=>c.key);if(!t&&!await I(`Add ${r.join(", ")} placeholders to .env.example?`))return[];let s=H(e,".env.example"),a=await w(s)??"",i=a?a.split(`
72
+ `):[],f=new Set(i.map(c=>c.trim()).filter(c=>c&&!c.startsWith("#")).map(c=>c.split("=")[0].trim())),u=0;for(let c of r)f.has(c)||(i.push(`${c}=your_${c.toLowerCase()}`),f.add(c),u++);return u===0?[]:(await E(s,i.join(`
73
+ `).trimEnd()+`
74
+ `),[`Added ${u} missing ${u===1?"placeholder":"placeholders"} to .env.example`])}async function mn(e,o,t){let n=o.filter(y=>y.type==="suspicious"&&y.key!==".env.example"),r=o.find(y=>y.type==="suspicious"&&y.key===".env.example");if(n.length===0&&!r)return[];let s=H(e,".env"),a=await w(s);if(!a)return[];let i=H(e,".env.example"),f=await B(i),u=await w(i)??"",c=u?u.split(`
75
+ `):[],p=new Set(c.map(y=>y.trim()).filter(y=>y&&!y.startsWith("#")).map(y=>y.split("=")[0].trim())),m=dn(a),h=0;if(r){if(t||await I("Create .env.example from .env?")){let y=[];for(let C of m)C.isSecret||re(C.key)?y.push(`${C.key}=your_${C.key.toLowerCase()}`):y.push(`${C.key}=${C.value}`),h++;return await E(i,y.join(`
76
+ `).trimEnd()+`
77
+ `),[`Created .env.example with ${h} variables`]}return[]}let x=n.map(y=>y.key);if(!t&&!await I(`Add ${x.join(", ")} to .env.example?`))return[];for(let y of m)x.includes(y.key)&&(p.has(y.key)||(y.isSecret||re(y.key)?c.push(`${y.key}=your_${y.key.toLowerCase()}`):c.push(`${y.key}=${y.value}`),p.add(y.key),h++));return h===0?[]:(await E(i,c.join(`
78
+ `).trimEnd()+`
79
+ `),[`Added ${h} missing ${h===1?"variable":"variables"} to .env.example`])}function dn(e){let o=[],t=/SECRET|KEY|TOKEN|PASSWORD|PRIVATE|ACCESS|CREDENTIAL/i;for(let n of e.split(`
80
+ `)){let r=n.trim();if(!r||r.startsWith("#"))continue;let s=r.indexOf("=");if(s===-1)continue;let a=r.slice(0,s).trim(),i=r.slice(s+1).trim();a.startsWith("export ")&&(a=a.slice(7).trim()),a&&o.push({key:a,value:i,isSecret:t.test(a)})}return o}import{Command as pn}from"commander";import V from"chalk";var Ae=new pn("ci").description("Non-interactive CI check with structured output").option("--fail-on <level>",'Fail on "error", "warning", or "none"',"error").option("--format <format>","Output format: text, json, or junit","text").action(async()=>{let e=Ae.opts(),o=await gn(e.failOn,e.format);process.exit(o)});async function gn(e,o){let t=process.cwd(),n=await $(t);if(!n)return console.log(v("No package.json found. Are you in a project directory?")),1;let r=await A(n);return o==="json"?console.log(JSON.stringify(vn(r),null,2)):o==="junit"?console.log(xn(r)):yn(r),e==="none"?0:e==="warning"?r.score.errorCount>0||r.score.warningCount>0?1:0:r.score.errorCount>0?1:0}function yn(e){if(console.log(),console.log(V.bold(`Enve CI Report \u2014 ${e.name}`)),console.log(V.dim(e.path)),console.log(),console.log(`Score: ${hn(e.score.score)} [ ${e.score.grade} ]`),console.log(V.dim(`${e.score.errorCount} errors, ${e.score.warningCount} warnings, ${e.score.infoCount} info`)),console.log(),e.issues.length===0){console.log(V.green("No issues found"));return}let o=e.issues.filter(r=>r.severity==="error"),t=e.issues.filter(r=>r.severity==="warning"),n=e.issues.filter(r=>r.severity==="info");Te("Errors",o),Te("Warnings",t),Te("Info",n)}function Te(e,o){if(o.length!==0){console.log(V.bold(`${e} (${o.length})`));for(let t of o){let n=t.severity==="error"?v:j;console.log(l(n(`${L(t.message)}`),2)),t.suggestion&&console.log(l(V.dim(`\u2192 ${t.suggestion}`),4))}console.log()}}function hn(e){return e>=90?V.green(`${e}/100`):e>=70?V.yellow(`${e}/100`):e>=50?V.hex("#f97316")(`${e}/100`):V.red(`${e}/100`)}function vn(e){return{name:e.name,path:e.path,score:e.score,envFiles:e.envFiles.map(o=>({filename:o.filename,variableCount:o.variables.length})),referenceCount:e.referenceCount,issues:e.issues.map(o=>({type:o.type,severity:o.severity,key:o.key,message:o.message,file:o.file,line:o.line,suggestion:o.suggestion})),generatedAt:new Date().toISOString()}}function xn(e){let o=e.issues,t=o.length,n=o.length||1,r=[];if(r.push('<?xml version="1.0" encoding="UTF-8"?>'),r.push(`<testsuites name="enve" tests="${n}" failures="${t}" errors="0" time="0">`),r.push(` <testsuite name="Environment Variables \u2014 ${he(e.name)}" tests="${n}" failures="${t}" errors="0" time="0">`),o.length===0)r.push(' <testcase name="No issues found" />');else for(let s of o){let a=he(`${s.type}: ${s.key}`),i=he(s.message),f=[s.message,s.suggestion,`${s.file}${s.line?`:${s.line}`:""}`].filter(Boolean).join(`
81
+ `);r.push(` <testcase name="${a}">`),r.push(` <failure message="${i}" type="${s.severity}">${he(f)}</failure>`),r.push(" </testcase>")}return r.push(" </testsuite>"),r.push("</testsuites>"),r.join(`
82
+ `)}function he(e){return e.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&apos;")}var T=new bn;T.name("enve").description("Environment Variable Doctor \u2014 scan, validate, and protect your env files").version(ge.version);T.addCommand(Ye);T.addCommand(qe);T.addCommand(ze);T.addCommand(Je);T.addCommand(so);T.addCommand(lo);T.addCommand(co);T.addCommand(ko);T.addCommand(Ae);T.addCommand(jo);T.addCommand(Po);T.addCommand(So);T.addCommand($o);process.on("unhandledRejection",e=>{console.error(`
83
+ 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)});T.parse();
59
84
  //# sourceMappingURL=cli.mjs.map