enve-doctor 1.0.2 → 1.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.mjs +21 -20
- package/dist/cli.mjs.map +1 -1
- package/package.json +1 -1
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
|
|
2
|
+
import{Command as Dt}from"commander";import{Command as nt}from"commander";import R from"chalk";import{join as Fe,basename as tt}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 L(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 z="# === Enve Pre-Commit Hook ===",ce="# === End Enve Hook ===";async function M(e){return L(ue(e,".git"))}async function ho(e){let o=ue(e,".gitignore");return k(o)}function xo(e,o){return e.split(`
|
|
3
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
|
-
${
|
|
4
|
+
${z}
|
|
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
|
|
@@ -29,31 +29,32 @@ if [ -n "$STAGED_ENV_FILES" ]; then
|
|
|
29
29
|
exit 1
|
|
30
30
|
fi
|
|
31
31
|
${ce}
|
|
32
|
-
`}async function
|
|
33
|
-
${t}`;await C(o,n)}async function we(e){let o=_(e);if(!await
|
|
32
|
+
`}async function ee(e){let o=_(e);return await L(o)?(await k(o))?.includes(z)??!1:!1}async function ke(e){let o=_(e),t=await k(o)??"";if(t.includes(z))return;let n=`${bo()}
|
|
33
|
+
${t}`;await C(o,n)}async function we(e){let o=_(e);if(!await L(o))return!1;let t=await k(o);if(!t||!t.includes(z))return!1;let n=t.indexOf(z),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
34
|
${s.trimStart()}`.trim();return l?await C(o,`${l}
|
|
35
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
|
|
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
|
|
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 m=s.slice(0,l).trim(),u=s.slice(l+1);if(m.startsWith("export ")&&(m=m.slice(7).trim()),!m)continue;let{value:p,comment:g}=Eo(u),d=Po(m,p);t.push({key:m,value:p,line:i+1,source:e,isSecret:d,comment:g})}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 A(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}"],Ao=["**/node_modules/**","**/dist/**","**/build/**","**/.next/**"];async function W(e,o={}){let t=o.include??Fo,n=o.exclude??Ao,i=await Co(t,{cwd:e,ignore:n,absolute:!0}),r=[];for(let s of i){let l=await Do(s);r.push(...l)}return r}async function Do(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 Ro(t,e)}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 X(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(X(r.argument.name,t,e,n,{hasFallback:!1}));continue}if(r.type==="Property"){if(r.value?.type==="Identifier"&&r.key?.type==="Identifier"&&i.push(X(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(X(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 Ro(e,o){let t=[],n=new Set,i=(u,p)=>{if(n.has(u))return;n.add(u);let d={loc:{start:{line:e.slice(0,p).split(`
|
|
38
|
+
`).length,column:0}}};t.push(X(u,o,d,e,{hasFallback:!1}))},r=/(?:process|import\.meta)\.env\.([A-Za-z_$][A-Za-z0-9_$]*)/g,s;for(;(s=r.exec(e))!==null;)i(s[1],s.index);let l=/(?:process|import\.meta)\.env\[(?:'([^']+)'|"([^"]+)"|`([^`]+)`)\]/g;for(;(s=l.exec(e))!==null;){let u=s[1]??s[2]??s[3];u&&i(u,s.index)}let m=/(?:const|let|var)\s*\{\s*([^}]+)\s*\}\s*=\s*(?:process\.env|import\.meta\.env)(?:\s*(?:\|\||\?\?)\s*\{[^}]*\})?/g;for(;(s=m.exec(e))!==null;){let u=s[1].split(",");for(let p of u){let g=p.trim();if(!g)continue;let d=g.match(/^([A-Za-z_$][A-Za-z0-9_$]*)/);d&&i(d[1],s.index)}}return t}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:Mo(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 Mo(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 X(e,o,t,n,i){let r=t.loc?.start?.line??0,s=t.loc?.start?.column??0,l=_o(n,r);return{key:e,file:o,line:r,column:s,context:l,hasFallback:i.hasFallback,fallbackValue:i.fallbackValue,fallbackType:i.fallbackType}}function _o(e,o){let t=e.split(`
|
|
38
39
|
`),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
|
+
`)}var Go=["PORT","HOST","NODE_ENV","CI","PWD"];async function Ce(e,o,t){let n=[];return n.push(...await Uo(e,o)),n.push(...Wo(e,o)),n.push(...Ho(e)),n.push(...await Ko(e,t)),n.push(...Yo(e)),n}async function Uo(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 Wo(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)Go.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 Ho(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 Ko(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 Yo(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:qo(o),totalIssues:e.length,errorCount:t,warningCount:n,infoCount:i}}function qo(e){return e>=90?"Excellent":e>=70?"Good":e>=50?"Needs attention":"Critical"}import zo from"conf";import{readFile as Xo}from"fs/promises";import{join as Zo}from"path";var Qo={version:1,projects:[]};function Se(){return new zo({projectName:"enve",defaults:Qo})}function oe(){return Se().get("projects",[])}function Jo(e){return oe().some(o=>o.path===e)}function et(e){return oe().find(o=>o.path===e)}async function je(e){let o=Se();if(Jo(e))return et(e);let n={name:await ot(e),path:e,addedAt:new Date().toISOString()},i=o.get("projects",[]);return o.set("projects",[...i,n]),n}async function ot(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 L(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([A(e),W(e),M(e),ee(e)]),r=await Ce(o,t,e),s=Ie(r),l=tt(e);return await je(e),{name:l,path:e,envFiles:o,envCount:o.reduce((m,u)=>m+u.variables.length,0),referenceCount:t.length,issues:r,score:s,hasGit:n,hookInstalled:i,hasEnvInGitignore:!r.some(m=>m.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 Ae=new nt("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(`${R.bold("Enve Scan Report")} \u2014 ${t.name}`)),console.log(a(R.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: ${rt(t.score.score)} [ ${t.score.grade} ]`)),console.log(a(R.dim(`${t.score.errorCount} errors, ${t.score.warningCount} warnings`))),console.log(),console.log(a("Run `enve doctor` for detailed recommendations."))});function rt(e){let o=`${e}/100`;return e>=90?R.bgGreen.black(` ${o} `):e>=70?R.bgYellow.black(` ${o} `):e>=50?R.red(o):R.bgRed.white(` ${o} `)}import{Command as it}from"commander";import S from"chalk";var De=new it("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 A(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),st(i,t),me("[Security]",r),me("[Validation]",s),at(o.issues),console.log(),console.log(a(`Score: ${lt(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 st(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 at(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 lt(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 ut}from"commander";import ge from"chalk";import{copyFile as ft}from"fs/promises";import{createInterface as ct}from"readline/promises";async function pe(e){let o=ct({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 ut("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 A(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 m=new Map;for(let d of l)m.set(d.filename,[...m.get(d.filename)??[],d]);for(let[d,b]of m){console.log(a(d));for(let F of b)console.log(a(I(`${F.key.padEnd(20)} (line ${F.line})`),2))}if(console.log(),console.log(a(`${l.length} unused variables found. They may be safe to remove.`)),e.dryRun){console.log(a(ge.dim("Dry run: no changes made.")));return}if(!e.fix){console.log(a("Run `enve unused --fix` to remove them, or `--dry-run` to preview."));return}if(!await N(`Remove ${l.length} unused variables from .env files?`)){console.log(a(ge.dim("Aborted. No changes made.")));return}let p=new Map;for(let d of l)p.set(d.path,[...p.get(d.path)??[],d]);let g=0;for(let[d,b]of p){let F=`${d}.backup.${mt(new Date)}`;await ft(d,F);let T=(await k(d)??"").split(`
|
|
40
41
|
`),B=new Set(b.map(xe=>xe.line-1)),co=T.filter((xe,uo)=>!B.has(uo));await C(d,co.join(`
|
|
41
|
-
`)),
|
|
42
|
-
`).map(
|
|
43
|
-
`)),3));console.log()}if(l.length>0){console.log(a(E("Missing (has fallback \u2014 will use default value)")));for(let
|
|
42
|
+
`)),g+=b.length,console.log(a(ge.dim(`Backup saved: ${F}`),2))}console.log(),console.log(a(x(`Removed ${g} unused variables.`)))});function mt(e){return e.toISOString().replace(/[:.]/g,"-").slice(0,19)}import{Command as dt}from"commander";import Q from"chalk";import{join as Ne}from"path";var Re=new Set(["PATH","HOME","USER","SHELL","PWD","OLDPWD","NODE","NODE_PATH","TMPDIR","LANG","LOGNAME","TERM","EDITOR","SHLVL","_","__CF_USER_TEXT_ENCODING"]),Ve=new dt("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 A(t),i=await W(t),r=new Set(n.flatMap(m=>m.variables.map(u=>u.key))),s=i.filter(m=>!r.has(m.key)&&!m.hasFallback&&!Re.has(m.key)),l=e.fallbacks?i.filter(m=>!r.has(m.key)&&m.hasFallback&&!Re.has(m.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 m of s)console.log(a(y(`${m.key}`),2)),console.log(a(Q.dim(`\u2192 ${m.file}:${m.line}`),3)),m.context&&console.log(a(Q.dim(m.context.split(`
|
|
43
|
+
`).map(u=>` ${u}`).join(`
|
|
44
|
+
`)),3));console.log()}if(l.length>0){console.log(a(E("Missing (has fallback \u2014 will use default value)")));for(let m of l)console.log(a(I(`${m.key}`),2)),console.log(a(Q.dim(`\u2192 fallback: ${m.fallbackValue??"unknown"}`),3)),console.log(a(Q.dim(`\u2192 ${m.file}:${m.line}`),3));console.log()}console.log(a(`${y(`${s.length} truly missing`)} \xB7 ${I(`${l.length} with fallback defaults`)}`)),e.add?await pt(t,n,s):(console.log(),console.log(a("Run with `--add` to interactively add missing variables to .env")))});async function pt(e,o,t){let n=Ne(e,".env");if(!o.find(u=>u.filename===".env")){console.log(y("No .env file found. Cannot add variables."));return}let r=[],s=[];for(let u of t){let p=await pe(`Value for ${u.key}:`);if(!p)continue;r.push(`${u.key}=${p}`),await N(`Add ${u.key} to .env.example?`,!1)&&s.push(`${u.key}=your_${u.key.toLowerCase()}`)}if(r.length===0){console.log(a(Q.dim("No variables added.")));return}let l=await k(n)??"",m=l.endsWith(`
|
|
44
45
|
`)||l===""?"":`
|
|
45
|
-
`;if(await C(n,`${l}${
|
|
46
|
+
`;if(await C(n,`${l}${m}${r.join(`
|
|
46
47
|
`)}
|
|
47
|
-
`),s.length>0){let
|
|
48
|
-
`)||
|
|
49
|
-
`;await C(
|
|
48
|
+
`),s.length>0){let u=Ne(e,".env.example"),p=await k(u)??"",g=p.endsWith(`
|
|
49
|
+
`)||p===""?"":`
|
|
50
|
+
`;await C(u,`${p}${g}${s.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
|
|
52
|
-
`).map(s=>{let l=s.trim();if(!l||l.startsWith("#"))return s;let
|
|
53
|
-
`)),console.log(a(K.dim(`Backup saved: ${t}`),2))}function
|
|
52
|
+
`)}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 gt}from"commander";import K from"chalk";function Oe(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 Le(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 vt}from"fs/promises";var He=new gt("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 A(t)).find(u=>u.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,m=[];for(let u of r){let p=yt(u);if(p.valid)console.log(a(x(`${u.key}=${We(u.value)}`)));else{s++,console.log(a(y(`${u.key}=${We(u.value)}`))),p.message&&console.log(a(K.dim(` ${p.message}`),2)),p.suggestion&&console.log(a(K.dim(` Suggestion: ${p.suggestion}`),2));let g=ht(u);g!==void 0&&(console.log(a(K.yellow(` Auto-fix available: ${u.key}=${g}`),2)),m.push({key:u.key,oldValue:u.value,newValue:g}))}}if(console.log(),console.log(a(`${r.length} variables checked, ${s} errors, ${l} warnings`)),e.fix){let u=m.filter(g=>!Ke(g.key));if(u.length===0){console.log(a(K.dim("No auto-fixable issues (secrets are never auto-fixed).")));return}if(!await N(`Apply ${u.length} auto-fixes to .env?`)){console.log(a(K.dim("Aborted. No changes made.")));return}await xt(i.path,u),console.log(),console.log(a(x(`Applied ${u.length} fixes to .env`)))}else m.length>0&&console.log(a("Run with `--fix` to apply available auto-fixes."))});function yt(e){let o=e.key,t=e.value;return o==="PORT"||o.endsWith("_PORT")?Oe(t):o==="NODE_ENV"?Me(t):o.endsWith("_URL")||o==="DATABASE_URL"||o==="REDIS_URL"?Le(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 ht(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 xt(e,o){let t=`${e}.backup.validate.${bt(new Date)}`;await vt(e,t);let n=await k(e)??"",i=new Map(o.map(s=>[s.key,s.newValue])),r=n.split(`
|
|
53
|
+
`).map(s=>{let l=s.trim();if(!l||l.startsWith("#"))return s;let m=l.indexOf("=");if(m===-1)return s;let u=l.slice(0,m).trim();if(u.startsWith("export ")&&(u=u.slice(7).trim()),i.has(u)){let p=s.match(/^\s*/)?.[0]??"",g=s.slice(s.indexOf("=")+1),d=g.indexOf(" #"),b=d!==-1?g.slice(d):"",$=(d!==-1?g.slice(0,d):g).match(/^\s*/)?.[0]??"";return`${p}${u}=${$}${i.get(u)}${b}`}return s});await C(e,r.join(`
|
|
54
|
+
`)),console.log(a(K.dim(`Backup saved: ${t}`),2))}function bt(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 kt}from"commander";import Y from"chalk";import{join as Ye}from"path";var qe=new kt("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),m=[],u=0,p=0;for(let d of l)e.allSecrets||d.isSecret||Ue(d.key)?(m.push(`${d.key}=your_${d.key.toLowerCase()}`),u++):(m.push(`${d.key}=${d.value}`),p++);let g=`${m.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(
|
|
56
|
+
`;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(`${p} public variables \u2192 copied as-is`),2)),console.log(a(Z(`${u} secrets redacted`),2)),s){let d=wt(s,g);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,g),console.log(),console.log(a(x(`Created ${e.output}`))))});function wt(e,o){let t=new Map,n=new Map;for(let r of e.split(`
|
|
56
57
|
`)){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)});
|
|
58
|
+
`)){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 ze=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 ee(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 St}from"commander";import{render as jt}from"ink";import so,{useState as Ft,useCallback as io}from"react";import c,{useState as no,useMemo as Et}from"react";import{Box as h,Text as v,useInput as $t,useApp as Pt,useStdout as Tt}from"ink";import j from"react";import{Box as J,Text as q}from"ink";import se from"react";import{Box as Je,Text as eo}from"ink";var f={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"},Xe={Excellent:{text:f.excellent,bg:f.excellentBg,label:"Excellent"},Good:{text:f.good,bg:f.goodBg,label:"Good"},"Needs attention":{text:f.attention,bg:f.attentionBg,label:"Needs attention"},Critical:{text:f.critical,bg:f.criticalBg,label:"Critical"}},Ze={error:{color:f.error,symbol:"\u25CF"},warning:{color:f.warning,symbol:"\u25CF"},info:{color:f.info,symbol:"\u25CF"}},Qe={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=Xe[e.grade];return o?se.createElement(Je,null,se.createElement(eo,{backgroundColor:t.bg,color:t.text,bold:!0}," ",e.score," ")):se.createElement(Je,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?f.ink:f.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?f.ink:f.muted},r," "),j.createElement(q,{bold:!0,color:f.ink},e.name)),j.createElement(ae,{score:n,compact:!0})),j.createElement(J,{marginTop:1},j.createElement(q,{color:f.muted},e.path)),j.createElement(J,{marginTop:1,columnGap:2},n.errorCount>0&&j.createElement(q,{color:f.error},n.errorCount," err"),n.warningCount>0&&j.createElement(q,{color:f.warning},n.warningCount," warn"),n.totalIssues===0&&j.createElement(q,{color:f.success},"Clean")))}import P from"react";import{Box as V,Text as O}from"ink";function to({issues:e,maxRows:o=12}){if(e.length===0)return P.createElement(V,{paddingY:1},P.createElement(O,{color:f.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(V,{flexDirection:"column",width:"100%"},n.map((r,s)=>{let l=Ze[r.severity],m=Qe[r.type]??"\u2022";return P.createElement(V,{key:`${r.type}-${r.key}-${s}`,flexDirection:"column",marginBottom:1},P.createElement(V,null,P.createElement(O,{color:l.color},m," "),P.createElement(O,{bold:!0,color:f.ink},r.key),P.createElement(O,{color:f.subtle}," \u2014 ",r.type)),P.createElement(V,{marginLeft:2},P.createElement(O,{color:f.muted},r.message)),r.suggestion&&P.createElement(V,{marginLeft:2},P.createElement(O,{color:f.subtle},"\u2192 ",r.suggestion)))}),i>0&&P.createElement(V,{marginTop:1},P.createElement(O,{color:f.subtle},"+ ",i," more issue",i===1?"":"s")))}function ro({projects:e,version:o,onRunDoctor:t,onRefresh:n}){let{exit:i}=Pt(),{stdout:r}=Tt(),[s,l]=no(0),[m,u]=no(!1),p=r.rows??24,g=e[s],d=Math.max(4,Math.floor((p-10)/5)),b=Et(()=>{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 $t(($,T)=>{m||(T.upArrow||$==="k"?l(B=>Math.max(0,B-1)):T.downArrow||$==="j"?l(B=>Math.min(e.length-1,B+1)):T.return?g&&(u(!0),Promise.resolve(t(g)).then(()=>i())):$==="r"?Promise.resolve(n()):($==="q"||T.escape)&&(u(!0),i()))},{isActive:!m}),e.length===0?c.createElement(h,{flexDirection:"column",padding:2},c.createElement(v,{bold:!0,color:f.ink},"enve dashboard"),c.createElement(h,{marginTop:1},c.createElement(v,{color:f.muted},"No projects tracked yet.")),c.createElement(h,{marginTop:1},c.createElement(v,{color:f.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(v,{bold:!0,color:f.ink},"enve"),c.createElement(v,{color:f.muted}," dashboard")),c.createElement(v,{color:f.subtle},"v",o)),c.createElement(h,{flexGrow:1,width:"100%"},c.createElement(h,{width:ie.sidebarWidth,flexDirection:"column",borderStyle:"single",borderColor:f.border,paddingX:1,paddingY:1,marginRight:2},c.createElement(h,{marginBottom:1},c.createElement(v,{bold:!0,color:f.ink},"Projects"),c.createElement(v,{color:f.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(v,{color:f.subtle},"Use \u2191/\u2193 to scroll"))),c.createElement(h,{flexGrow:1,flexDirection:"column",borderStyle:"single",borderColor:f.border,paddingX:2,paddingY:1},g?c.createElement(Ct,{project:g}):c.createElement(h,null,c.createElement(v,{color:f.muted},"Select a project to view details.")))),c.createElement(h,{justifyContent:"space-between",width:"100%",borderStyle:"single",borderColor:f.border,paddingX:1,marginTop:1},c.createElement(h,{columnGap:3},c.createElement(v,{color:f.muted},c.createElement(v,{bold:!0},"\u2191/\u2193")," navigate"),c.createElement(v,{color:f.muted},c.createElement(v,{bold:!0},"Enter")," doctor"),c.createElement(v,{color:f.muted},c.createElement(v,{bold:!0},"r")," refresh"),c.createElement(v,{color:f.muted},c.createElement(v,{bold:!0},"q")," quit")),c.createElement(v,{color:f.subtle},"enve-doctor v",o)))}function Ct({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(v,{bold:!0,color:f.ink},e.name),c.createElement(ae,{score:e.score})),c.createElement(h,{marginBottom:1},c.createElement(v,{color:f.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(v,{color:f.subtle},"Env files: "),c.createElement(v,{color:f.ink},o)),c.createElement(h,{marginTop:1,marginBottom:1},c.createElement(v,{color:f.borderStrong},"\u2500".repeat(60))),c.createElement(h,{marginTop:1,marginBottom:1},c.createElement(v,{bold:!0,color:f.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(v,{color:f.subtle},e),c.createElement(v,{bold:!0,color:f.ink},o))}function he({label:e,ok:o}){return c.createElement(h,null,c.createElement(v,{color:o?f.success:f.error},o?"\u2713":"\u2717"," "),c.createElement(v,{color:f.muted},e))}var le={name:"enve-doctor",version:"1.0.3",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 St("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));jt(so.createElement(At,{initialProjects:o,trackedPaths:e.map(t=>t.path),version:le.version}))});function At({initialProjects:e,trackedPaths:o,version:t}){let[n,i]=Ft(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 D=new Dt;D.name("enve").description("Environment Variable Doctor \u2014 scan, validate, and protect your env files").version(le.version);D.addCommand(Ae);D.addCommand(De);D.addCommand(Be);D.addCommand(Ve);D.addCommand(He);D.addCommand(qe);D.addCommand(ze);D.addCommand(ao);process.on("unhandledRejection",e=>{console.error(`
|
|
59
|
+
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)});D.parse();
|
|
59
60
|
//# sourceMappingURL=cli.mjs.map
|
package/dist/cli.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/cli/cli.ts","../src/cli/commands/scan.ts","../src/core/project.ts","../src/utils/fs.ts","../src/utils/git.ts","../src/core/parser.ts","../src/core/scanner.ts","../src/core/analyzer.ts","../src/core/config.ts","../src/utils/logger.ts","../src/cli/commands/doctor.ts","../src/cli/commands/unused.ts","../src/utils/prompt.ts","../src/cli/commands/missing.ts","../src/cli/commands/validate.ts","../src/utils/validators.ts","../src/cli/commands/generate-example.ts","../src/cli/commands/hook.ts","../src/cli/commands/dashboard.tsx","../src/ui/Dashboard.tsx","../src/ui/ProjectCard.tsx","../src/ui/ScoreBadge.tsx","../src/ui/theme.ts","../src/ui/IssueList.tsx","../package.json"],"sourcesContent":["import { Command } from 'commander';\nimport { scanCommand } from './commands/scan.js';\nimport { doctorCommand } from './commands/doctor.js';\nimport { unusedCommand } from './commands/unused.js';\nimport { missingCommand } from './commands/missing.js';\nimport { validateCommand } from './commands/validate.js';\nimport { generateExampleCommand } from './commands/generate-example.js';\nimport { hookCommand } from './commands/hook.js';\nimport { dashboardCommand } from './commands/dashboard.js';\nimport pkg from '../../package.json' with { type: 'json' };\n\nconst program = new Command();\n\nprogram\n .name('enve')\n .description('Environment Variable Doctor — scan, validate, and protect your env files')\n .version(pkg.version);\n\nprogram.addCommand(scanCommand);\nprogram.addCommand(doctorCommand);\nprogram.addCommand(unusedCommand);\nprogram.addCommand(missingCommand);\nprogram.addCommand(validateCommand);\nprogram.addCommand(generateExampleCommand);\nprogram.addCommand(hookCommand);\nprogram.addCommand(dashboardCommand);\n\nprocess.on('unhandledRejection', (reason) => {\n console.error('\\n Unexpected error:', reason instanceof Error ? reason.message : reason);\n console.error(' If this keeps happening, please open an issue with the stack trace above.');\n process.exit(1);\n});\n\nprogram.parse();\n","import { Command } from 'commander';\nimport chalk from 'chalk';\nimport { detectProject, getProjectInfo } from '../../core/project.js';\nimport { success, warning, error, bullet, indent } from '../../utils/logger.js';\n\nexport const scanCommand = new Command('scan')\n .description('Quick overview of the current project\\'s environment variable health')\n .action(async () => {\n const cwd = process.cwd();\n const projectPath = await detectProject(cwd);\n\n if (!projectPath) {\n console.log(error('No package.json found. Are you in a project directory?'));\n process.exit(1);\n }\n\n const info = await getProjectInfo(projectPath);\n\n console.log();\n console.log(indent(`${chalk.bold('Enve Scan Report')} — ${info.name}`));\n console.log(indent(chalk.dim(info.path)));\n console.log();\n\n console.log(indent(`.env files found: ${info.envFiles.length}`));\n for (const file of info.envFiles) {\n console.log(indent(`${bullet(`${file.filename.padEnd(18)} ${file.variables.length} variables`)}`, 2));\n }\n console.log();\n\n console.log(indent(`Code references: ${info.referenceCount} variables used`));\n console.log();\n\n if (info.issues.length === 0) {\n console.log(indent(success('No issues found. Great job!')));\n } else {\n console.log(indent(`Issues found: ${info.issues.length}`));\n\n const errors = info.issues.filter((i) => i.severity === 'error');\n const warnings = info.issues.filter((i) => i.severity === 'warning');\n const infos = info.issues.filter((i) => i.severity === 'info');\n\n for (const issue of errors) {\n console.log(indent(error(issue.message), 2));\n }\n for (const issue of warnings) {\n console.log(indent(warning(issue.message), 2));\n }\n for (const issue of infos) {\n console.log(indent(success(issue.message), 2));\n }\n }\n\n console.log();\n console.log(indent(`Score: ${formatScore(info.score.score)} [ ${info.score.grade} ]`));\n console.log(indent(chalk.dim(`${info.score.errorCount} errors, ${info.score.warningCount} warnings`)));\n console.log();\n console.log(indent('Run `enve doctor` for detailed recommendations.'));\n });\n\nfunction formatScore(score: number): string {\n const text = `${score}/100`;\n if (score >= 90) return chalk.bgGreen.black(` ${text} `);\n if (score >= 70) return chalk.bgYellow.black(` ${text} `);\n if (score >= 50) return chalk.red(text);\n return chalk.bgRed.white(` ${text} `);\n}\n","import { join, basename } from 'node:path';\nimport { fileExists } from '../utils/fs.js';\nimport { isGitRepo } from '../utils/git.js';\nimport { parseAllEnvFiles } from './parser.js';\nimport { scanProject } from './scanner.js';\nimport { analyze, calculateScore } from './analyzer.js';\nimport { addProject } from './config.js';\nimport type { ProjectInfo } from '../types/index.js';\nimport { isHookInstalled } from '../utils/git.js';\n\nexport async function detectProject(startPath: string): Promise<string | undefined> {\n let current = startPath;\n while (true) {\n if (await fileExists(join(current, 'package.json'))) {\n return current;\n }\n const parent = join(current, '..');\n if (parent === current) break;\n current = parent;\n }\n return undefined;\n}\n\nexport async function getProjectInfo(projectPath: string): Promise<ProjectInfo> {\n const [envFiles, references, hasGit, hookInstalled] = await Promise.all([\n parseAllEnvFiles(projectPath),\n scanProject(projectPath),\n isGitRepo(projectPath),\n isHookInstalled(projectPath),\n ]);\n\n const issues = await analyze(envFiles, references, projectPath);\n const score = calculateScore(issues);\n const name = basename(projectPath);\n\n await addProject(projectPath);\n\n return {\n name,\n path: projectPath,\n envFiles,\n envCount: envFiles.reduce((sum, f) => sum + f.variables.length, 0),\n referenceCount: references.length,\n issues,\n score,\n hasGit,\n hookInstalled,\n hasEnvInGitignore: !issues.some((i) => i.type === 'gitignore'),\n lastModified: new Date(),\n };\n}\n","import { readFile, writeFile, access, mkdir } from 'node:fs/promises';\nimport { dirname } from 'node:path';\n\nexport async function readFileSafe(filePath: string): Promise<string | undefined> {\n try {\n return await readFile(filePath, 'utf-8');\n } catch {\n return undefined;\n }\n}\n\nexport async function writeFileSafe(filePath: string, content: string): Promise<void> {\n await ensureDir(dirname(filePath));\n await writeFile(filePath, content, 'utf-8');\n}\n\nexport async function fileExists(filePath: string): Promise<boolean> {\n try {\n await access(filePath);\n return true;\n } catch {\n return false;\n }\n}\n\nexport async function ensureDir(dirPath: string): Promise<void> {\n if (!dirPath) return;\n try {\n await mkdir(dirPath, { recursive: true });\n } catch {\n // Directory may already exist\n }\n}\n","import { join } from 'node:path';\nimport { readFileSafe, fileExists, writeFileSafe } from './fs.js';\n\nconst PRE_COMMIT_HOOK_MARKER = '# === Enve Pre-Commit Hook ===';\nconst PRE_COMMIT_HOOK_END = '# === End Enve Hook ===';\n\nexport async function isGitRepo(projectPath: string): Promise<boolean> {\n return fileExists(join(projectPath, '.git'));\n}\n\nexport async function readGitignore(projectPath: string): Promise<string | undefined> {\n const gitignorePath = join(projectPath, '.gitignore');\n return readFileSafe(gitignorePath);\n}\n\nexport function isPatternInGitignore(gitignoreContent: string, pattern: string): boolean {\n const lines = gitignoreContent.split('\\n');\n return lines.some((line) => {\n const trimmed = line.trim();\n return trimmed === pattern || trimmed === `/${pattern}`;\n });\n}\n\nexport async function isEnvFileGitignored(\n projectPath: string,\n filename: string\n): Promise<boolean> {\n const content = await readGitignore(projectPath);\n if (!content) return false;\n return isPatternInGitignore(content, filename);\n}\n\nexport function getPreCommitHookPath(projectPath: string): string {\n return join(projectPath, '.git', 'hooks', 'pre-commit');\n}\n\nexport function getHookScript(): string {\n return `#!/bin/bash\n${PRE_COMMIT_HOOK_MARKER}\n# Blocks commits of .env files that may contain secrets\n# Installed by: enve hook install\n# Remove with: enve hook uninstall\n\nBLOCKED_PATTERNS='^\\\\.env(\\\\.[^/]*)?$'\nSTAGED_ENV_FILES=$(git diff --cached --name-only | grep -E \"$BLOCKED_PATTERNS\" || true)\n\nif [ -n \"$STAGED_ENV_FILES\" ]; then\n echo \"\"\n echo \" ✗ COMMIT BLOCKED by Enve\"\n echo \"\"\n echo \" You are attempting to commit environment files:\"\n echo \"$STAGED_ENV_FILES\" | sed 's/^/ /'\n echo \"\"\n echo \" These files may contain secrets (API keys, passwords, tokens).\"\n echo \"\"\n echo \" What to do instead:\"\n echo \" 1. Add them to .gitignore: echo '.env.local' >> .gitignore\"\n echo \" 2. Use .env.example for templates: enve generate-example\"\n echo \" 3. Share secrets through a secure channel (1Password, etc.)\"\n echo \"\"\n echo \" If you are CERTAIN these files are safe to commit:\"\n echo \" git commit --no-verify\"\n echo \"\"\n exit 1\nfi\n${PRE_COMMIT_HOOK_END}\n`;\n}\n\nexport async function isHookInstalled(projectPath: string): Promise<boolean> {\n const hookPath = getPreCommitHookPath(projectPath);\n if (!(await fileExists(hookPath))) return false;\n const content = await readFileSafe(hookPath);\n return content?.includes(PRE_COMMIT_HOOK_MARKER) ?? false;\n}\n\nexport async function installHook(projectPath: string): Promise<void> {\n const hookPath = getPreCommitHookPath(projectPath);\n const existing = (await readFileSafe(hookPath)) ?? '';\n\n if (existing.includes(PRE_COMMIT_HOOK_MARKER)) {\n return; // Already installed\n }\n\n const newContent = `${getHookScript()}\\n${existing}`;\n await writeFileSafe(hookPath, newContent);\n}\n\nexport async function uninstallHook(projectPath: string): Promise<boolean> {\n const hookPath = getPreCommitHookPath(projectPath);\n if (!(await fileExists(hookPath))) return false;\n\n const content = await readFileSafe(hookPath);\n if (!content || !content.includes(PRE_COMMIT_HOOK_MARKER)) return false;\n\n const markerStart = content.indexOf(PRE_COMMIT_HOOK_MARKER);\n const markerEnd = content.indexOf(PRE_COMMIT_HOOK_END, markerStart);\n if (markerStart === -1 || markerEnd === -1) return false;\n\n const before = content.slice(0, markerStart);\n const after = content.slice(markerEnd + PRE_COMMIT_HOOK_END.length);\n const newContent = `${before.trimEnd()}\\n${after.trimStart()}`.trim();\n\n if (newContent) {\n await writeFileSafe(hookPath, `${newContent}\\n`);\n } else {\n // No remaining content; leave file removal to caller if desired\n await writeFileSafe(hookPath, '');\n }\n return true;\n}\n","import { join } from 'node:path';\nimport { readFileSync } from 'node:fs';\nimport type { EnvFile, EnvVariable } from '../types/index.js';\nimport { readFileSafe } from '../utils/fs.js';\n\nexport function parseEnvFile(filePath: string): EnvVariable[] {\n let content: string | undefined;\n try {\n content = readFileSync(filePath, 'utf-8');\n } catch {\n return [];\n }\n if (!content) return [];\n\n const variables: EnvVariable[] = [];\n const lines = content.split('\\n');\n\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i];\n const trimmed = line.trim();\n\n if (!trimmed || trimmed.startsWith('#')) continue;\n\n const equalIndex = trimmed.indexOf('=');\n if (equalIndex === -1) continue;\n\n let key = trimmed.slice(0, equalIndex).trim();\n let rawValue = trimmed.slice(equalIndex + 1);\n\n // Strip export keyword\n if (key.startsWith('export ')) {\n key = key.slice(7).trim();\n }\n\n if (!key) continue;\n\n const { value, comment } = parseValue(rawValue);\n const isSecret = detectSecret(key, value);\n\n variables.push({\n key,\n value,\n line: i + 1,\n source: filePath,\n isSecret,\n comment,\n });\n }\n\n return variables;\n}\n\nfunction parseValue(raw: string): { value: string; comment?: string } {\n const trimmed = raw.trimStart();\n\n if (trimmed.startsWith('\"')) {\n const { value, endIndex } = parseQuotedValue(trimmed, '\"');\n const rest = trimmed.slice(endIndex + 1).trim();\n const comment = extractInlineComment(rest);\n return { value: unescape(value), comment };\n }\n\n if (trimmed.startsWith(\"'\")) {\n const { value, endIndex } = parseQuotedValue(trimmed, \"'\");\n const rest = trimmed.slice(endIndex + 1).trim();\n const comment = extractInlineComment(rest);\n return { value, comment };\n }\n\n const trailing = raw.trimEnd();\n const commentIndex = findUnquotedCommentIndex(trailing);\n if (commentIndex !== -1) {\n return {\n value: trailing.slice(0, commentIndex).trimEnd(),\n comment: trailing.slice(commentIndex + 1).trim(),\n };\n }\n\n return { value: raw.trimEnd() };\n}\n\nfunction parseQuotedValue(raw: string, quote: string): { value: string; endIndex: number } {\n let value = '';\n let escaped = false;\n for (let i = 1; i < raw.length; i++) {\n const char = raw[i];\n if (escaped) {\n if (char === 'n') value += '\\n';\n else if (char === 't') value += '\\t';\n else if (char === quote) value += quote;\n else value += `\\\\${char}`;\n escaped = false;\n continue;\n }\n if (char === '\\\\') {\n escaped = true;\n continue;\n }\n if (char === quote) {\n return { value, endIndex: i };\n }\n value += char;\n }\n // Unterminated quote — return rest as value\n return { value, endIndex: raw.length - 1 };\n}\n\nfunction unescape(value: string): string {\n // Escape sequences are already handled during quote parsing.\n // This function remains as a no-op fallback for any raw unquoted escapes.\n return value;\n}\n\nfunction extractInlineComment(rest: string): string | undefined {\n const idx = rest.indexOf('#');\n if (idx === -1) return undefined;\n return rest.slice(idx + 1).trim();\n}\n\nfunction findUnquotedCommentIndex(value: string): number {\n for (let i = 0; i < value.length; i++) {\n if (value[i] === '#' && (i === 0 || value[i - 1] === ' ')) {\n return i;\n }\n }\n return -1;\n}\n\nfunction detectSecret(key: string, value: string): boolean {\n const secretPatterns = /SECRET|KEY|TOKEN|PASSWORD|PRIVATE|ACCESS|CREDENTIAL/i;\n if (secretPatterns.test(key)) return true;\n\n // Simple entropy heuristic: high ratio of non-word characters or length > 20 with mixed case\n if (value.length > 20 && /[A-Z]/.test(value) && /[a-z]/.test(value) && /\\d/.test(value)) {\n return true;\n }\n\n return false;\n}\n\nexport async function findEnvFiles(projectPath: string): Promise<EnvFile[]> {\n const envFileNames = ['.env', '.env.local', '.env.development', '.env.production', '.env.test', '.env.example'];\n const files: EnvFile[] = [];\n\n for (const name of envFileNames) {\n const filePath = join(projectPath, name);\n const content = await readFileSafe(filePath);\n if (content !== undefined) {\n files.push({\n path: filePath,\n filename: name,\n variables: parseEnvFile(filePath),\n });\n }\n }\n\n return files;\n}\n\nexport async function parseAllEnvFiles(projectPath: string): Promise<EnvFile[]> {\n return findEnvFiles(projectPath);\n}\n","import fg from 'fast-glob';\nimport { readFile } from 'node:fs/promises';\nimport { Parser } from 'acorn';\nimport { fullAncestor } from 'acorn-walk';\nimport type { EnvReference, ScannerOptions } from '../types/index.js';\n\nconst DEFAULT_INCLUDE = [\n '**/*.{js,ts,jsx,tsx,mjs,cjs}',\n];\n\nconst DEFAULT_EXCLUDE = [\n '**/node_modules/**',\n '**/dist/**',\n '**/build/**',\n '**/.next/**',\n];\n\nexport async function scanProject(\n projectPath: string,\n options: ScannerOptions = {}\n): Promise<EnvReference[]> {\n const include = options.include ?? DEFAULT_INCLUDE;\n const exclude = options.exclude ?? DEFAULT_EXCLUDE;\n\n const files = await fg(include, {\n cwd: projectPath,\n ignore: exclude,\n absolute: true,\n });\n\n const references: EnvReference[] = [];\n for (const file of files) {\n const fileRefs = await scanFile(file);\n references.push(...fileRefs);\n }\n\n return references;\n}\n\nexport async function scanFile(filePath: string): Promise<EnvReference[]> {\n const references: EnvReference[] = [];\n const content = await readFile(filePath, 'utf-8');\n\n let ast: unknown;\n try {\n ast = Parser.parse(content, {\n ecmaVersion: 'latest',\n sourceType: 'module',\n allowReturnOutsideFunction: true,\n allowImportExportEverywhere: true,\n locations: true,\n });\n } catch {\n return references;\n }\n\n fullAncestor(ast as any, (node: any, _state: any, ancestors: any[]) => {\n if (node.type === 'MemberExpression') {\n const ref = extractEnvReference(node, ancestors, filePath, content);\n if (ref) references.push(ref);\n }\n\n if (node.type === 'ObjectPattern') {\n const refs = extractDestructuredEnvReferences(node, ancestors, filePath, content);\n references.push(...refs);\n }\n });\n\n return references;\n}\n\nfunction extractEnvReference(\n node: any,\n ancestors: any[],\n filePath: string,\n content: string\n): EnvReference | undefined {\n const key = getMemberExpressionKey(node);\n if (!key) return undefined;\n\n // fullAncestor includes the current node as the last ancestor\n const parent = ancestors.length > 1 ? ancestors[ancestors.length - 2] : undefined;\n const fallback = detectFallback(node, parent);\n\n return createReference(key, filePath, node, content, fallback);\n}\n\nfunction getMemberExpressionKey(node: any): string | undefined {\n // process.env.X or process.env['X'] (object is process.env)\n if (\n node.object?.type === 'MemberExpression' &&\n node.object.object?.name === 'process' &&\n node.object.property?.name === 'env'\n ) {\n if (node.property?.type === 'Identifier' && !node.computed) return node.property.name;\n if (node.property?.type === 'Literal' && typeof node.property.value === 'string') {\n return node.property.value;\n }\n if (node.property?.type === 'TemplateLiteral' && node.property.quasis?.length === 1) {\n return node.property.quasis[0].value.cooked ?? node.property.quasis[0].value.raw;\n }\n }\n\n // import.meta.env.X (static property access, not computed)\n if (\n node.object?.type === 'MemberExpression' &&\n node.object.object?.type === 'MetaProperty' &&\n node.object.object.meta?.name === 'import' &&\n node.object.object.property?.name === 'meta' &&\n node.object.property?.name === 'env' &&\n !node.computed\n ) {\n if (node.property?.type === 'Identifier') return node.property.name;\n if (node.property?.type === 'Literal' && typeof node.property.value === 'string') {\n return node.property.value;\n }\n }\n\n return undefined;\n}\n\nfunction extractDestructuredEnvReferences(\n node: any,\n ancestors: any[],\n filePath: string,\n content: string\n): EnvReference[] {\n if (!isEnvDestructuringPattern(node, ancestors)) return [];\n\n const references: EnvReference[] = [];\n for (const prop of node.properties) {\n if (prop.type === 'RestElement' && prop.argument?.type === 'Identifier') {\n references.push(\n createReference(prop.argument.name, filePath, node, content, { hasFallback: false })\n );\n continue;\n }\n\n if (prop.type !== 'Property') continue;\n\n if (prop.value?.type === 'Identifier' && prop.key?.type === 'Identifier') {\n // Shorthand: const { MY_VAR } = process.env\n // Renamed: const { MY_VAR: myVar } = process.env\n references.push(\n createReference(prop.key.name, filePath, prop.value, content, { hasFallback: false })\n );\n }\n\n if (prop.value?.type === 'AssignmentPattern' && prop.value.left?.type === 'Identifier' && prop.key?.type === 'Identifier') {\n // Shorthand with default: const { MY_VAR = 'x' } = process.env\n // Renamed with default: const { MY_VAR: myVar = 'x' } = process.env\n const key = prop.key.name;\n const fallbackValue = expressionToString(prop.value.right);\n references.push(\n createReference(key, filePath, prop.value.left, content, {\n hasFallback: true,\n fallbackValue,\n fallbackType: 'literal',\n })\n );\n }\n\n // Nested destructuring: const { env: { X } } = process\n if (prop.value?.type === 'ObjectPattern' && prop.key?.name === 'env') {\n const nested = extractDestructuredEnvReferences(\n prop.value,\n [...ancestors, prop, prop.value],\n filePath,\n content\n );\n references.push(...nested);\n }\n }\n\n return references;\n}\n\nfunction isEnvDestructuringPattern(node: any, ancestors: any[]): boolean {\n // fullAncestor includes the current node as the last ancestor\n const parent = ancestors.length > 1 ? ancestors[ancestors.length - 2] : undefined;\n if (!parent) return false;\n\n // const { X } = process.env\n if (parent.type === 'VariableDeclarator' && parent.id === node) {\n return isEnvSource(parent.init);\n }\n\n // const { X = 'd' } = process.env (AssignmentPattern wrapping ObjectPattern)\n if (parent.type === 'AssignmentPattern' && parent.left === node) {\n return isEnvSource(parent.right);\n }\n\n // const { env: { X } } = process\n if (parent.type === 'Property' && parent.value === node && parent.key?.name === 'env') {\n const grandParent = ancestors.length > 2 ? ancestors[ancestors.length - 3] : undefined;\n if (grandParent?.type === 'ObjectPattern') {\n return isEnvDestructuringPattern(grandParent, ancestors.slice(0, -2));\n }\n }\n\n return false;\n}\n\nfunction isProcessNode(node: any): boolean {\n return node?.type === 'Identifier' && node.name === 'process';\n}\n\nfunction isProcessEnvNode(node: any): boolean {\n if (!node) return false;\n\n // process.env\n if (\n node.type === 'MemberExpression' &&\n node.object?.type === 'Identifier' &&\n node.object.name === 'process' &&\n node.property?.name === 'env'\n ) {\n return true;\n }\n\n return false;\n}\n\nfunction isEnvSource(node: any): boolean {\n if (isProcessEnvNode(node) || isProcessNode(node)) return true;\n\n // const { X } = process.env || {}\n // const { X } = process.env ?? {}\n if (\n node?.type === 'LogicalExpression' &&\n (node.operator === '||' || node.operator === '??')\n ) {\n return isEnvSource(node.left) || isEnvSource(node.right);\n }\n\n // const { X } = condition ? process.env : {}\n if (node?.type === 'ConditionalExpression') {\n return isEnvSource(node.consequent) || isEnvSource(node.alternate);\n }\n\n return false;\n}\n\nfunction detectFallback(node: any, parent: any): FallbackInfo {\n if (!parent) return { hasFallback: false };\n\n if (parent.type === 'LogicalExpression') {\n const isLeft = parent.left === node;\n const isRight = parent.right === node;\n\n if (parent.operator === '||' || parent.operator === '??') {\n const fallbackNode = isLeft ? parent.right : isRight ? parent.left : undefined;\n if (fallbackNode) {\n return {\n hasFallback: true,\n fallbackValue: expressionToString(fallbackNode),\n fallbackType: getFallbackType(fallbackNode),\n };\n }\n }\n\n if (parent.operator === '&&' && isLeft) {\n return { hasFallback: true, fallbackValue: 'guarded', fallbackType: 'expression' };\n }\n }\n\n if (parent.type === 'ConditionalExpression' && parent.test === node) {\n return {\n hasFallback: true,\n fallbackValue: expressionToString(parent.alternate),\n fallbackType: 'ternary',\n };\n }\n\n return { hasFallback: false };\n}\n\nfunction getFallbackType(node: any): 'literal' | 'function' | 'expression' {\n if (node.type === 'Literal' || node.type === 'TemplateLiteral') return 'literal';\n if (node.type === 'CallExpression' || node.type === 'NewExpression') return 'function';\n return 'expression';\n}\n\nfunction expressionToString(node: any): string {\n if (!node) return 'unknown';\n\n if (node.type === 'Literal') {\n return typeof node.value === 'string' ? `'${node.value}'` : String(node.value);\n }\n\n if (node.type === 'Identifier') return node.name;\n\n if (node.type === 'TemplateLiteral') {\n if (node.quasis.length === 1) return `'${node.quasis[0].value.cooked ?? node.quasis[0].value.raw}'`;\n return '`template`';\n }\n\n if (node.type === 'CallExpression' || node.type === 'NewExpression') {\n const callee = node.callee?.name ?? node.callee?.property?.name ?? 'function';\n return `<${callee}()>`;\n }\n\n if (node.type === 'UnaryExpression') {\n return `${node.operator}${expressionToString(node.argument)}`;\n }\n\n if (node.type === 'BinaryExpression') {\n return `${expressionToString(node.left)} ${node.operator} ${expressionToString(node.right)}`;\n }\n\n if (node.type === 'MemberExpression') {\n return node.property?.name ?? 'member';\n }\n\n return '<expression>';\n}\n\ninterface FallbackInfo {\n hasFallback: boolean;\n fallbackValue?: string;\n fallbackType?: 'literal' | 'function' | 'expression' | 'ternary';\n}\n\nfunction createReference(\n key: string,\n filePath: string,\n node: any,\n content: string,\n fallback: FallbackInfo\n): EnvReference {\n const line = node.loc?.start?.line ?? 0;\n const column = node.loc?.start?.column ?? 0;\n const context = extractContext(content, line);\n\n return {\n key,\n file: filePath,\n line,\n column,\n context,\n hasFallback: fallback.hasFallback,\n fallbackValue: fallback.fallbackValue,\n fallbackType: fallback.fallbackType,\n };\n}\n\nfunction extractContext(content: string, line: number): string {\n const lines = content.split('\\n');\n const start = Math.max(0, line - 2);\n const end = Math.min(lines.length, line + 1);\n return lines.slice(start, end).join('\\n');\n}\n","import type { EnvFile, EnvReference, Issue, ScoreResult, Grade } from '../types/index.js';\nimport { isEnvFileGitignored } from '../utils/git.js';\n\nconst ALWAYS_ALLOWED = ['PORT', 'HOST', 'NODE_ENV', 'CI', 'PWD'];\n\nexport async function analyze(\n envFiles: EnvFile[],\n references: EnvReference[],\n projectPath: string\n): Promise<Issue[]> {\n const issues: Issue[] = [];\n\n issues.push(...(await checkMissingVariables(envFiles, references)));\n issues.push(...checkUnusedVariables(envFiles, references));\n issues.push(...checkSecretRisks(envFiles));\n issues.push(...(await checkGitignore(envFiles, projectPath)));\n issues.push(...checkExampleFile(envFiles));\n\n return issues;\n}\n\nasync function checkMissingVariables(envFiles: EnvFile[], references: EnvReference[]): Promise<Issue[]> {\n const definedKeys = new Set(envFiles.flatMap((file) => file.variables.map((v) => v.key)));\n const issues: Issue[] = [];\n\n for (const ref of references) {\n if (definedKeys.has(ref.key)) continue;\n\n if (ref.hasFallback) {\n issues.push({\n type: 'missing',\n severity: 'warning',\n key: ref.key,\n message: `${ref.key} is not defined in .env but has a fallback default: ${ref.fallbackValue ?? 'unknown'}`,\n file: ref.file,\n line: ref.line,\n suggestion: `Consider adding ${ref.key} to .env for explicit configuration, or keep the fallback`,\n hasFallback: true,\n fallbackValue: ref.fallbackValue,\n });\n } else {\n issues.push({\n type: 'missing',\n severity: 'error',\n key: ref.key,\n message: `${ref.key} referenced in ${ref.file}:${ref.line} but not defined in any .env file (no fallback)`,\n file: ref.file,\n line: ref.line,\n suggestion: `Add ${ref.key} to .env and .env.example`,\n });\n }\n }\n\n return issues;\n}\n\nfunction checkUnusedVariables(envFiles: EnvFile[], references: EnvReference[]): Issue[] {\n const referencedKeys = new Set(references.map((r) => r.key));\n const issues: Issue[] = [];\n\n for (const file of envFiles) {\n if (file.filename === '.env.example' || file.filename === '.env.test') continue;\n\n for (const variable of file.variables) {\n if (ALWAYS_ALLOWED.includes(variable.key)) continue;\n if (referencedKeys.has(variable.key)) continue;\n\n issues.push({\n type: 'unused',\n severity: 'warning',\n key: variable.key,\n message: `${variable.key} is defined in ${file.filename} but never used in code`,\n file: file.filename,\n line: variable.line,\n suggestion: 'Remove if no longer needed, or use it in your code',\n });\n }\n }\n\n return issues;\n}\n\nfunction checkSecretRisks(envFiles: EnvFile[]): Issue[] {\n const issues: Issue[] = [];\n\n for (const file of envFiles) {\n if (file.filename !== '.env') continue;\n\n for (const variable of file.variables) {\n if (!variable.isSecret) continue;\n\n issues.push({\n type: 'secret-risk',\n severity: 'error',\n key: variable.key,\n message: `${variable.key} looks like a secret but is in .env instead of .env.local`,\n file: file.filename,\n line: variable.line,\n suggestion: `Move ${variable.key} to .env.local (which should be in .gitignore)`,\n });\n }\n }\n\n return issues;\n}\n\nasync function checkGitignore(envFiles: EnvFile[], projectPath: string): Promise<Issue[]> {\n const issues: Issue[] = [];\n const filenames = envFiles.map((f) => f.filename);\n\n const filesToCheck = ['.env', '.env.local', '.env.production'];\n const messages: Record<string, string> = {\n '.env': '.env is not in .gitignore — risk of committing secrets',\n '.env.local': '.env.local is not in .gitignore',\n '.env.production': '.env.production is not in .gitignore — production secrets at risk',\n };\n const suggestions: Record<string, string> = {\n '.env': \"echo '.env' >> .gitignore\",\n '.env.local': \"echo '.env.local' >> .gitignore\",\n '.env.production': \"echo '.env.production' >> .gitignore\",\n };\n\n for (const filename of filesToCheck) {\n if (!filenames.includes(filename)) continue;\n const ignored = await isEnvFileGitignored(projectPath, filename);\n if (!ignored) {\n issues.push({\n type: 'gitignore',\n severity: 'error',\n key: filename,\n message: messages[filename],\n file: '.gitignore',\n suggestion: suggestions[filename],\n });\n }\n }\n\n return issues;\n}\n\nfunction checkExampleFile(envFiles: EnvFile[]): Issue[] {\n const issues: Issue[] = [];\n const exampleFile = envFiles.find((f) => f.filename === '.env.example');\n const envFile = envFiles.find((f) => f.filename === '.env');\n\n if (!exampleFile) {\n issues.push({\n type: 'suspicious',\n severity: 'warning',\n key: '.env.example',\n message: \".env.example is missing — new developers won't know what env vars are needed\",\n file: '.',\n suggestion: 'Run `enve generate-example` to create one',\n });\n return issues;\n }\n\n if (!envFile) return issues;\n\n const exampleKeys = new Set(exampleFile.variables.map((v) => v.key));\n for (const variable of envFile.variables) {\n if (exampleKeys.has(variable.key)) continue;\n\n issues.push({\n type: 'suspicious',\n severity: 'warning',\n key: variable.key,\n message: `${variable.key} is in .env but missing from .env.example`,\n file: '.env.example',\n suggestion: `Add ${variable.key}=your_${variable.key} to .env.example`,\n });\n }\n\n return issues;\n}\n\nexport function calculateScore(issues: Issue[]): ScoreResult {\n let score = 100;\n let errorCount = 0;\n let warningCount = 0;\n let infoCount = 0;\n let unusedDeduction = 0;\n let exampleDeduction = 0;\n\n for (const issue of issues) {\n switch (issue.severity) {\n case 'error':\n errorCount++;\n break;\n case 'warning':\n warningCount++;\n break;\n case 'info':\n infoCount++;\n break;\n }\n\n switch (issue.type) {\n case 'missing':\n score -= issue.hasFallback ? 3 : 10;\n break;\n case 'secret-risk':\n score -= 8;\n break;\n case 'gitignore':\n if (issue.key === '.env.production') score -= 25;\n else score -= 20;\n break;\n case 'syntax-error':\n score -= 15;\n break;\n case 'invalid':\n score -= 5;\n break;\n case 'unused':\n unusedDeduction = Math.min(unusedDeduction + 3, 15);\n break;\n case 'suspicious':\n if (issue.key === '.env.example') score -= 10;\n else exampleDeduction = Math.min(exampleDeduction + 2, 10);\n break;\n }\n }\n\n score -= unusedDeduction + exampleDeduction;\n score = Math.max(0, Math.min(100, score));\n\n return {\n score,\n grade: getGrade(score),\n totalIssues: issues.length,\n errorCount,\n warningCount,\n infoCount,\n };\n}\n\nfunction getGrade(score: number): Grade {\n if (score >= 90) return 'Excellent';\n if (score >= 70) return 'Good';\n if (score >= 50) return 'Needs attention';\n return 'Critical';\n}\n","import Conf from 'conf';\nimport { readFile } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport type { EnveConfig, TrackedProject } from '../types/index.js';\n\nconst DEFAULT_CONFIG: EnveConfig = {\n version: 1,\n projects: [],\n};\n\nfunction createConfig(): Conf<EnveConfig> {\n return new Conf<EnveConfig>({\n projectName: 'enve',\n defaults: DEFAULT_CONFIG,\n });\n}\n\nexport function getProjects(): TrackedProject[] {\n const config = createConfig();\n return config.get('projects', []);\n}\n\nexport function isTracked(projectPath: string): boolean {\n return getProjects().some((p) => p.path === projectPath);\n}\n\nexport function getProject(projectPath: string): TrackedProject | undefined {\n return getProjects().find((p) => p.path === projectPath);\n}\n\nexport async function addProject(projectPath: string): Promise<TrackedProject> {\n const config = createConfig();\n\n if (isTracked(projectPath)) {\n return getProject(projectPath)!;\n }\n\n const name = await detectProjectName(projectPath);\n const project: TrackedProject = {\n name,\n path: projectPath,\n addedAt: new Date().toISOString(),\n };\n\n const projects = config.get('projects', []);\n config.set('projects', [...projects, project]);\n\n return project;\n}\n\nexport function removeProject(projectPath: string): boolean {\n const config = createConfig();\n const projects = config.get('projects', []);\n const before = projects.length;\n const filtered = projects.filter((p) => p.path !== projectPath);\n\n if (filtered.length === before) return false;\n\n config.set('projects', filtered);\n return true;\n}\n\nasync function detectProjectName(projectPath: string): Promise<string> {\n try {\n const pkgRaw = await readFile(join(projectPath, 'package.json'), 'utf-8');\n const pkg = JSON.parse(pkgRaw) as { name?: string };\n if (pkg.name) return pkg.name;\n } catch {\n // fall through to directory name\n }\n return projectPath.split('/').pop() ?? 'unknown';\n}\n","import chalk from 'chalk';\n\nexport const symbols = {\n error: '✗',\n warning: '⚠',\n success: '✓',\n info: 'ℹ',\n bullet: '•',\n arrow: '→',\n pointer: '▸',\n};\n\nexport function error(message: string): string {\n return `${chalk.red(symbols.error)} ${message}`;\n}\n\nexport function warning(message: string): string {\n return `${chalk.yellow(symbols.warning)} ${message}`;\n}\n\nexport function success(message: string): string {\n return `${chalk.green(symbols.success)} ${message}`;\n}\n\nexport function info(message: string): string {\n return `${chalk.blue(symbols.info)} ${message}`;\n}\n\nexport function bullet(message: string): string {\n return ` ${symbols.bullet} ${message}`;\n}\n\nexport function indent(message: string, level = 1): string {\n return ' '.repeat(level) + message;\n}\n\nexport function header(title: string): string {\n return chalk.bold(title);\n}\n\nexport function dim(message: string): string {\n return chalk.dim(message);\n}\n","import { Command } from 'commander';\nimport chalk from 'chalk';\nimport { detectProject, getProjectInfo } from '../../core/project.js';\nimport { parseAllEnvFiles } from '../../core/parser.js';\nimport { error, warning, success, indent, header } from '../../utils/logger.js';\nimport type { Issue } from '../../types/index.js';\n\nexport const doctorCommand = new Command('doctor')\n .description('Comprehensive health check with detailed recommendations')\n .action(async () => {\n const cwd = process.cwd();\n const projectPath = await detectProject(cwd);\n\n if (!projectPath) {\n console.log(error('No package.json found. Are you in a project directory?'));\n process.exit(1);\n }\n\n await runDoctor(projectPath);\n });\n\nexport async function runDoctor(projectPath: string): Promise<void> {\n const info = await getProjectInfo(projectPath);\n const envFiles = await parseAllEnvFiles(projectPath);\n\n console.log();\n console.log(indent(header(`Enve Health Check — ${info.name}`)));\n console.log(indent(chalk.dim(info.path)));\n console.log();\n\n const fileStructureIssues = info.issues.filter(\n (i) => i.type === 'gitignore' || (i.type === 'suspicious' && i.key === '.env.example')\n );\n const variableIssues = info.issues.filter((i) => i.type === 'missing' || i.type === 'unused');\n const securityIssues = info.issues.filter((i) => i.type === 'secret-risk');\n const validationIssues = info.issues.filter((i) => i.type === 'invalid' || i.type === 'syntax-error');\n\n printSection('[File Structure]', fileStructureIssues);\n printVariablesSection(variableIssues, envFiles);\n printSection('[Security]', securityIssues);\n printSection('[Validation]', validationIssues);\n printRecommendations(info.issues);\n\n console.log();\n console.log(indent(`Score: ${formatScore(info.score.score)} [ ${info.score.grade} ]`));\n console.log(indent(chalk.dim(`${info.score.errorCount} errors, ${info.score.warningCount} warnings, ${info.score.infoCount} info`)));\n console.log();\n}\n\nfunction printSection(title: string, issues: Issue[]): void {\n console.log(indent(header(title)));\n if (issues.length === 0) {\n console.log(indent(success('No issues')));\n } else {\n for (const issue of issues) {\n const formatter = issue.severity === 'error' ? error : issue.severity === 'warning' ? warning : success;\n console.log(indent(formatter(issue.message), 2));\n if (issue.suggestion) {\n console.log(indent(chalk.dim(`→ ${issue.suggestion}`), 3));\n }\n }\n }\n console.log();\n}\n\nfunction printVariablesSection(issues: Issue[], envFiles: Awaited<ReturnType<typeof import('../../core/parser.js').parseAllEnvFiles>>): void {\n const totalDefined = envFiles.reduce((sum, f) => sum + f.variables.length, 0);\n console.log(indent(header(`[Variables — ${totalDefined} defined]`)));\n\n if (issues.length === 0) {\n console.log(indent(success('All variables are used and defined')));\n } else {\n for (const issue of issues) {\n const formatter = issue.severity === 'error' ? error : warning;\n let message = issue.message;\n if (issue.type === 'missing' && issue.hasFallback && issue.fallbackValue) {\n message += chalk.dim(` (fallback: ${issue.fallbackValue})`);\n }\n console.log(indent(formatter(message), 2));\n if (issue.suggestion) {\n console.log(indent(chalk.dim(`→ ${issue.suggestion}`), 3));\n }\n }\n }\n console.log();\n}\n\nfunction printRecommendations(issues: Issue[]): void {\n const actionable = issues.filter((i) => i.suggestion);\n if (actionable.length === 0) return;\n\n console.log(indent(header(`[Recommendations — ${actionable.length} actions needed]`)));\n\n const high = actionable.filter((i) => i.severity === 'error');\n const medium = actionable.filter((i) => i.severity === 'warning');\n const low = actionable.filter((i) => i.severity === 'info');\n\n if (high.length > 0) {\n console.log(indent(chalk.red.bold('HIGH PRIORITY:')));\n high.forEach((issue, idx) => {\n console.log(indent(`${idx + 1}. ${issue.suggestion}`, 2));\n });\n console.log();\n }\n\n if (medium.length > 0) {\n console.log(indent(chalk.yellow.bold('MEDIUM PRIORITY:')));\n medium.forEach((issue, idx) => {\n console.log(indent(`${idx + 1}. ${issue.suggestion}`, 2));\n });\n console.log();\n }\n\n if (low.length > 0) {\n console.log(indent(chalk.blue.bold('LOW PRIORITY:')));\n low.forEach((issue, idx) => {\n console.log(indent(`${idx + 1}. ${issue.suggestion}`, 2));\n });\n console.log();\n }\n}\n\nfunction formatScore(score: number): string {\n const text = `${score}/100`;\n if (score >= 90) return chalk.bgGreen.black(` ${text} `);\n if (score >= 70) return chalk.bgYellow.black(` ${text} `);\n if (score >= 50) return chalk.red(text);\n return chalk.bgRed.white(` ${text} `);\n}\n","import { Command } from 'commander';\nimport chalk from 'chalk';\nimport { detectProject } from '../../core/project.js';\nimport { parseAllEnvFiles } from '../../core/parser.js';\nimport { scanProject } from '../../core/scanner.js';\nimport { readFileSafe, writeFileSafe } from '../../utils/fs.js';\nimport { copyFile } from 'node:fs/promises';\nimport { error, warning, success, indent, header } from '../../utils/logger.js';\nimport { confirm } from '../../utils/prompt.js';\n\nexport const unusedCommand = new Command('unused')\n .description('Find environment variables defined in .env files but never referenced in code')\n .option('--fix', 'Remove unused variables from .env files')\n .option('--dry-run', 'Show what would be removed without making changes')\n .option('--include-commons', 'Also flag PORT, HOST, NODE_ENV if truly unused')\n .action(async (options) => {\n const cwd = process.cwd();\n const projectPath = await detectProject(cwd);\n\n if (!projectPath) {\n console.log(error('No package.json found. Are you in a project directory?'));\n process.exit(1);\n }\n\n const envFiles = await parseAllEnvFiles(projectPath);\n const references = await scanProject(projectPath);\n const referencedKeys = new Set(references.map((r) => r.key));\n\n const alwaysAllowed = options.includeCommons ? [] : ['PORT', 'HOST', 'NODE_ENV', 'CI', 'PWD'];\n\n type UnusedItem = { filename: string; path: string; key: string; line: number };\n const unused: UnusedItem[] = [];\n\n for (const file of envFiles) {\n if (file.filename === '.env.example' || file.filename === '.env.test') continue;\n for (const variable of file.variables) {\n if (alwaysAllowed.includes(variable.key)) continue;\n if (!referencedKeys.has(variable.key)) {\n unused.push({\n filename: file.filename,\n path: file.path,\n key: variable.key,\n line: variable.line,\n });\n }\n }\n }\n\n console.log();\n console.log(indent(header(`Unused variables in ${projectPath}`)));\n console.log();\n\n if (unused.length === 0) {\n console.log(indent(success('All variables are used')));\n return;\n }\n\n // Group by file\n const byFile = new Map<string, UnusedItem[]>();\n for (const item of unused) {\n byFile.set(item.filename, [...(byFile.get(item.filename) ?? []), item]);\n }\n\n for (const [filename, items] of byFile) {\n console.log(indent(filename));\n for (const item of items) {\n console.log(indent(warning(`${item.key.padEnd(20)} (line ${item.line})`), 2));\n }\n }\n\n console.log();\n console.log(indent(`${unused.length} unused variables found. They may be safe to remove.`));\n\n if (options.dryRun) {\n console.log(indent(chalk.dim('Dry run: no changes made.')));\n return;\n }\n\n if (!options.fix) {\n console.log(indent('Run `enve unused --fix` to remove them, or `--dry-run` to preview.'));\n return;\n }\n\n const confirmed = await confirm(`Remove ${unused.length} unused variables from .env files?`);\n if (!confirmed) {\n console.log(indent(chalk.dim('Aborted. No changes made.')));\n return;\n }\n\n // Group by file path for removal\n const byPath = new Map<string, UnusedItem[]>();\n for (const item of unused) {\n byPath.set(item.path, [...(byPath.get(item.path) ?? []), item]);\n }\n\n let removedCount = 0;\n for (const [filePath, items] of byPath) {\n const backupPath = `${filePath}.backup.${formatTimestamp(new Date())}`;\n await copyFile(filePath, backupPath);\n\n const content = (await readFileSafe(filePath)) ?? '';\n const lines = content.split('\\n');\n const linesToRemove = new Set(items.map((i) => i.line - 1));\n const newLines = lines.filter((_, idx) => !linesToRemove.has(idx));\n await writeFileSafe(filePath, newLines.join('\\n'));\n\n removedCount += items.length;\n console.log(indent(chalk.dim(`Backup saved: ${backupPath}`), 2));\n }\n\n console.log();\n console.log(indent(success(`Removed ${removedCount} unused variables.`)));\n });\n\nfunction formatTimestamp(date: Date): string {\n return date.toISOString().replace(/[:.]/g, '-').slice(0, 19);\n}\n","import { createInterface } from 'node:readline/promises';\n\nexport async function prompt(message: string): Promise<string> {\n const rl = createInterface({\n input: process.stdin,\n output: process.stdout,\n });\n\n try {\n const answer = await rl.question(`${message} `);\n return answer.trim();\n } finally {\n rl.close();\n }\n}\n\nexport async function confirm(message: string, defaultValue = false): Promise<boolean> {\n const suffix = defaultValue ? ' (Y/n)' : ' (y/N)';\n const answer = await prompt(`${message}${suffix}`);\n if (!answer) return defaultValue;\n return /^y(es)?$/i.test(answer);\n}\n","import { Command } from 'commander';\nimport chalk from 'chalk';\nimport { join } from 'node:path';\nimport { detectProject } from '../../core/project.js';\nimport { parseAllEnvFiles } from '../../core/parser.js';\nimport { scanProject } from '../../core/scanner.js';\nimport { readFileSafe, writeFileSafe } from '../../utils/fs.js';\nimport { error, warning, success, indent, header } from '../../utils/logger.js';\nimport { prompt, confirm } from '../../utils/prompt.js';\n\nconst STANDARD_NODE_VARS = new Set([\n 'PATH',\n 'HOME',\n 'USER',\n 'SHELL',\n 'PWD',\n 'OLDPWD',\n 'NODE',\n 'NODE_PATH',\n 'TMPDIR',\n 'LANG',\n 'LOGNAME',\n 'TERM',\n 'EDITOR',\n 'SHLVL',\n '_',\n '__CF_USER_TEXT_ENCODING',\n]);\n\nexport const missingCommand = new Command('missing')\n .description('Find process.env references in code that are not defined in any .env file')\n .option('--include-fallbacks', 'Also show variables that have fallback defaults')\n .option('--no-fallbacks', 'Hide variables with fallback defaults')\n .option('--add', 'Interactively add missing variables to .env')\n .action(async (options) => {\n const cwd = process.cwd();\n const projectPath = await detectProject(cwd);\n\n if (!projectPath) {\n console.log(error('No package.json found. Are you in a project directory?'));\n process.exit(1);\n }\n\n const envFiles = await parseAllEnvFiles(projectPath);\n const references = await scanProject(projectPath);\n const definedKeys = new Set(envFiles.flatMap((f) => f.variables.map((v) => v.key)));\n\n const missingNoFallback = references.filter(\n (r) => !definedKeys.has(r.key) && !r.hasFallback && !STANDARD_NODE_VARS.has(r.key)\n );\n const missingWithFallback = options.fallbacks\n ? references.filter((r) => !definedKeys.has(r.key) && r.hasFallback && !STANDARD_NODE_VARS.has(r.key))\n : [];\n\n console.log();\n console.log(indent(header(`Missing environment variables in ${projectPath}`)));\n console.log();\n\n if (missingNoFallback.length === 0 && missingWithFallback.length === 0) {\n console.log(indent(success('All referenced variables are defined')));\n return;\n }\n\n if (missingNoFallback.length > 0) {\n console.log(indent(header('Missing (no fallback — WILL cause runtime errors)')));\n for (const ref of missingNoFallback) {\n console.log(indent(error(`${ref.key}`), 2));\n console.log(indent(chalk.dim(`→ ${ref.file}:${ref.line}`), 3));\n if (ref.context) {\n console.log(indent(chalk.dim(ref.context.split('\\n').map((l) => ` ${l}`).join('\\n')), 3));\n }\n }\n console.log();\n }\n\n if (missingWithFallback.length > 0) {\n console.log(indent(header('Missing (has fallback — will use default value)')));\n for (const ref of missingWithFallback) {\n console.log(indent(warning(`${ref.key}`), 2));\n console.log(indent(chalk.dim(`→ fallback: ${ref.fallbackValue ?? 'unknown'}`), 3));\n console.log(indent(chalk.dim(`→ ${ref.file}:${ref.line}`), 3));\n }\n console.log();\n }\n\n console.log(\n indent(\n `${error(`${missingNoFallback.length} truly missing`)} · ${warning(\n `${missingWithFallback.length} with fallback defaults`\n )}`\n )\n );\n\n if (options.add) {\n await addMissingVariables(projectPath, envFiles, missingNoFallback);\n } else {\n console.log();\n console.log(indent('Run with `--add` to interactively add missing variables to .env'));\n }\n });\n\nasync function addMissingVariables(\n projectPath: string,\n envFiles: Awaited<ReturnType<typeof parseAllEnvFiles>>,\n missing: Awaited<ReturnType<typeof scanProject>>\n): Promise<void> {\n const envPath = join(projectPath, '.env');\n const envFile = envFiles.find((f) => f.filename === '.env');\n\n if (!envFile) {\n console.log(error('No .env file found. Cannot add variables.'));\n return;\n }\n\n const updates: string[] = [];\n const exampleUpdates: string[] = [];\n\n for (const ref of missing) {\n const value = await prompt(`Value for ${ref.key}:`);\n if (!value) continue;\n\n updates.push(`${ref.key}=${value}`);\n\n const addToExample = await confirm(`Add ${ref.key} to .env.example?`, false);\n if (addToExample) {\n exampleUpdates.push(`${ref.key}=your_${ref.key.toLowerCase()}`);\n }\n }\n\n if (updates.length === 0) {\n console.log(indent(chalk.dim('No variables added.')));\n return;\n }\n\n const existing = (await readFileSafe(envPath)) ?? '';\n const separator = existing.endsWith('\\n') || existing === '' ? '' : '\\n';\n await writeFileSafe(envPath, `${existing}${separator}${updates.join('\\n')}\\n`);\n\n if (exampleUpdates.length > 0) {\n const examplePath = join(projectPath, '.env.example');\n const exampleExisting = (await readFileSafe(examplePath)) ?? '';\n const exampleSeparator = exampleExisting.endsWith('\\n') || exampleExisting === '' ? '' : '\\n';\n await writeFileSafe(examplePath, `${exampleExisting}${exampleSeparator}${exampleUpdates.join('\\n')}\\n`);\n }\n\n console.log();\n console.log(indent(success(`Added ${updates.length} variables to .env`)));\n if (exampleUpdates.length > 0) {\n console.log(indent(success(`Added ${exampleUpdates.length} placeholders to .env.example`)));\n }\n}\n","import { Command } from 'commander';\nimport chalk from 'chalk';\nimport { detectProject } from '../../core/project.js';\nimport { parseAllEnvFiles } from '../../core/parser.js';\nimport {\n validatePort,\n validateUrl,\n validateNodeEnv,\n validateSecret,\n validateBoolean,\n} from '../../utils/validators.js';\nimport { readFileSafe, writeFileSafe } from '../../utils/fs.js';\nimport { copyFile } from 'node:fs/promises';\nimport { error, success, indent, header } from '../../utils/logger.js';\nimport { confirm } from '../../utils/prompt.js';\nimport type { EnvVariable } from '../../types/index.js';\n\nexport const validateCommand = new Command('validate')\n .description('Validate the values of environment variables against expected formats')\n .option('--fix', 'Automatically fix simple issues')\n .action(async (options) => {\n const cwd = process.cwd();\n const projectPath = await detectProject(cwd);\n\n if (!projectPath) {\n console.log(error('No package.json found. Are you in a project directory?'));\n process.exit(1);\n }\n\n const envFiles = await parseAllEnvFiles(projectPath);\n const envFile = envFiles.find((f) => f.filename === '.env');\n\n if (!envFile) {\n console.log(error('No .env file found. Cannot validate.'));\n process.exit(1);\n }\n\n const variables = envFile.variables;\n\n console.log();\n console.log(indent(header(`Validating environment variables — ${envFile.filename}`)));\n console.log();\n\n let errorCount = 0;\n let warningCount = 0;\n const fixes: Array<{ key: string; oldValue: string; newValue: string }> = [];\n\n for (const variable of variables) {\n const result = validateVariable(variable);\n if (result.valid) {\n console.log(indent(success(`${variable.key}=${truncate(variable.value)}`)));\n } else {\n errorCount++;\n console.log(indent(error(`${variable.key}=${truncate(variable.value)}`)));\n if (result.message) console.log(indent(chalk.dim(` ${result.message}`), 2));\n if (result.suggestion) console.log(indent(chalk.dim(` Suggestion: ${result.suggestion}`), 2));\n\n const fixedValue = getFixedValue(variable);\n if (fixedValue !== undefined) {\n console.log(indent(chalk.yellow(` Auto-fix available: ${variable.key}=${fixedValue}`), 2));\n fixes.push({ key: variable.key, oldValue: variable.value, newValue: fixedValue });\n }\n }\n }\n\n console.log();\n console.log(indent(`${variables.length} variables checked, ${errorCount} errors, ${warningCount} warnings`));\n\n if (options.fix) {\n const safeFixes = fixes.filter((f) => !isSecretKey(f.key));\n if (safeFixes.length === 0) {\n console.log(indent(chalk.dim('No auto-fixable issues (secrets are never auto-fixed).')));\n return;\n }\n\n const confirmed = await confirm(`Apply ${safeFixes.length} auto-fixes to .env?`);\n if (!confirmed) {\n console.log(indent(chalk.dim('Aborted. No changes made.')));\n return;\n }\n\n await applyFixes(envFile.path, safeFixes);\n console.log();\n console.log(indent(success(`Applied ${safeFixes.length} fixes to .env`)));\n } else if (fixes.length > 0) {\n console.log(indent('Run with `--fix` to apply available auto-fixes.'));\n }\n });\n\nexport function validateVariable(variable: EnvVariable): { valid: boolean; message?: string; suggestion?: string } {\n const key = variable.key;\n const value = variable.value;\n\n if (key === 'PORT' || key.endsWith('_PORT')) {\n return validatePort(value);\n }\n\n if (key === 'NODE_ENV') {\n return validateNodeEnv(value);\n }\n\n if (key.endsWith('_URL') || key === 'DATABASE_URL' || key === 'REDIS_URL') {\n return validateUrl(value);\n }\n\n if (isSecretKey(key)) {\n return validateSecret(value, key);\n }\n\n if (key === 'DEBUG' || key === 'VERBOSE' || key.endsWith('_ENABLED')) {\n return validateBoolean(value, key);\n }\n\n return { valid: true };\n}\n\nfunction isSecretKey(key: string): boolean {\n return /SECRET|KEY|TOKEN|PRIVATE/i.test(key) && key !== 'API_KEY';\n}\n\nfunction getFixedValue(variable: EnvVariable): string | undefined {\n const key = variable.key;\n const value = variable.value;\n\n // URL missing protocol\n if (key.endsWith('_URL') || key === 'DATABASE_URL' || key === 'REDIS_URL') {\n if (!value.includes('://') && !value.startsWith('http')) {\n return `https://${value}`;\n }\n }\n\n // NODE_ENV shorthand\n if (key === 'NODE_ENV') {\n if (value === 'dev') return 'development';\n if (value === 'prod') return 'production';\n }\n\n // Boolean normalization\n if (key === 'DEBUG' || key === 'VERBOSE' || key.endsWith('_ENABLED')) {\n const normalized = value.toLowerCase();\n if (normalized === 'yes' || normalized === '1' || normalized === 'on') return 'true';\n if (normalized === 'no' || normalized === '0' || normalized === 'off') return 'false';\n }\n\n return undefined;\n}\n\nasync function applyFixes(filePath: string, fixes: Array<{ key: string; newValue: string }>): Promise<void> {\n const backupPath = `${filePath}.backup.validate.${formatTimestamp(new Date())}`;\n await copyFile(filePath, backupPath);\n\n const content = (await readFileSafe(filePath)) ?? '';\n const fixMap = new Map(fixes.map((f) => [f.key, f.newValue]));\n\n const lines = content.split('\\n').map((line) => {\n const trimmed = line.trim();\n if (!trimmed || trimmed.startsWith('#')) return line;\n\n const equalIndex = trimmed.indexOf('=');\n if (equalIndex === -1) return line;\n\n let key = trimmed.slice(0, equalIndex).trim();\n if (key.startsWith('export ')) key = key.slice(7).trim();\n\n if (fixMap.has(key)) {\n const prefix = line.match(/^\\s*/)?.[0] ?? '';\n const suffix = line.slice(line.indexOf('=') + 1);\n const commentIndex = suffix.indexOf(' #');\n const comment = commentIndex !== -1 ? suffix.slice(commentIndex) : '';\n const rawValue = commentIndex !== -1 ? suffix.slice(0, commentIndex) : suffix;\n const leadingSpaces = rawValue.match(/^\\s*/)?.[0] ?? '';\n return `${prefix}${key}=${leadingSpaces}${fixMap.get(key)}${comment}`;\n }\n\n return line;\n });\n\n await writeFileSafe(filePath, lines.join('\\n'));\n console.log(indent(chalk.dim(`Backup saved: ${backupPath}`), 2));\n}\n\nfunction formatTimestamp(date: Date): string {\n return date.toISOString().replace(/[:.]/g, '-').slice(0, 19);\n}\n\nfunction truncate(value: string, max = 40): string {\n if (value.length <= max) return value;\n return value.slice(0, max) + '...';\n}\n","export interface ValidationResult {\n valid: boolean;\n message?: string;\n suggestion?: string;\n}\n\nexport function validatePort(value: string): ValidationResult {\n const port = Number(value);\n if (!Number.isInteger(port) || port < 1 || port > 65535) {\n return {\n valid: false,\n message: `PORT must be a number between 1 and 65535`,\n suggestion: `PORT=3000`,\n };\n }\n return { valid: true };\n}\n\nexport function validateUrl(value: string): ValidationResult {\n if (!value.includes('://')) {\n return {\n valid: false,\n message: 'Invalid URL — missing protocol',\n suggestion: `Did you mean https://${value} or a protocol-specific URL?`,\n };\n }\n\n try {\n const url = new URL(value);\n if (!url.protocol || url.protocol === ':') {\n throw new Error('Missing protocol');\n }\n return { valid: true };\n } catch {\n return {\n valid: false,\n message: 'Invalid URL',\n suggestion: `Check the URL format`,\n };\n }\n}\n\nexport function validateNodeEnv(value: string): ValidationResult {\n const allowed = ['development', 'production', 'test'];\n if (!allowed.includes(value)) {\n return {\n valid: false,\n message: `NODE_ENV should be 'development', 'production', or 'test'`,\n suggestion: `NODE_ENV=${value === 'dev' ? 'development' : value === 'prod' ? 'production' : 'development'}`,\n };\n }\n return { valid: true };\n}\n\nexport function validateSecret(value: string, key: string): ValidationResult {\n const forbidden = ['default', 'secret', 'test', 'password', '123456'];\n if (value.length < 16) {\n return {\n valid: false,\n message: `${key} is too short (${value.length} characters)`,\n suggestion: `Use at least 16 random characters. Generate one: node -e \"console.log(require('crypto').randomBytes(32).toString('hex'))\"`,\n };\n }\n if (forbidden.includes(value.toLowerCase())) {\n return {\n valid: false,\n message: `${key} uses a weak placeholder value`,\n suggestion: `Generate a strong random secret`,\n };\n }\n return { valid: true };\n}\n\nexport function validateBoolean(value: string, key: string): ValidationResult {\n const normalized = value.toLowerCase();\n if (normalized === 'true' || normalized === 'false') {\n return { valid: true };\n }\n const boolMap: Record<string, string> = {\n yes: 'true',\n no: 'false',\n '1': 'true',\n '0': 'false',\n on: 'true',\n off: 'false',\n };\n if (normalized in boolMap) {\n return {\n valid: false,\n message: `${key} should be 'true' or 'false' for boolean values`,\n suggestion: `${key}=${boolMap[normalized]}`,\n };\n }\n return {\n valid: false,\n message: `${key} is not a valid boolean value`,\n suggestion: `${key}=true`,\n };\n}\n\nexport function isSecretKey(key: string): boolean {\n const secretPatterns = /SECRET|KEY|TOKEN|PASSWORD|PRIVATE|ACCESS|CREDENTIAL|AUTH/i;\n return secretPatterns.test(key);\n}\n","import { Command } from 'commander';\nimport chalk from 'chalk';\nimport { join } from 'node:path';\nimport { detectProject } from '../../core/project.js';\nimport { parseEnvFile } from '../../core/parser.js';\nimport { readFileSafe, writeFileSafe } from '../../utils/fs.js';\nimport { isSecretKey } from '../../utils/validators.js';\nimport { error, success, indent, header, bullet } from '../../utils/logger.js';\nimport { confirm } from '../../utils/prompt.js';\n\nexport const generateExampleCommand = new Command('generate-example')\n .description('Automatically create or update .env.example from the current .env file')\n .option('--overwrite', 'Update existing .env.example without prompting')\n .option('--output <path>', 'Write to a different file', '.env.example')\n .option('--all-secrets', 'Redact ALL values')\n .option('--dry-run', 'Show what would be generated without writing files')\n .action(async (options) => {\n const cwd = process.cwd();\n const projectPath = await detectProject(cwd);\n\n if (!projectPath) {\n console.log(error('No package.json found. Are you in a project directory?'));\n process.exit(1);\n }\n\n const envPath = join(projectPath, '.env');\n const envContent = await readFileSafe(envPath);\n\n if (!envContent) {\n console.log(error('No .env file found. Cannot generate .env.example.'));\n process.exit(1);\n }\n\n const outputPath = join(projectPath, options.output);\n const existing = await readFileSafe(outputPath);\n const variables = parseEnvFile(envPath);\n\n const newLines: string[] = [];\n let redactedCount = 0;\n let copiedCount = 0;\n\n for (const variable of variables) {\n const shouldRedact = options.allSecrets || variable.isSecret || isSecretKey(variable.key);\n if (shouldRedact) {\n newLines.push(`${variable.key}=your_${variable.key.toLowerCase()}`);\n redactedCount++;\n } else {\n newLines.push(`${variable.key}=${variable.value}`);\n copiedCount++;\n }\n }\n\n const newContent = `${newLines.join('\\n')}\\n`;\n\n console.log();\n console.log(indent(header(`Generating ${options.output} from .env`)));\n console.log();\n console.log(indent(`Variables: ${variables.length} total`));\n console.log(indent(bullet(`${copiedCount} public variables → copied as-is`), 2));\n console.log(indent(bullet(`${redactedCount} secrets redacted`), 2));\n\n if (existing) {\n const diff = computeDiff(existing, newContent);\n\n if (diff.length === 0) {\n console.log();\n console.log(indent(success(`${options.output} is already up to date.`)));\n return;\n }\n\n console.log();\n console.log(indent(header('Changes since last generation:')));\n for (const change of diff) {\n const symbol = change.type === 'added' ? chalk.green('+') : change.type === 'removed' ? chalk.red('-') : chalk.yellow('~');\n console.log(indent(`${symbol} ${change.type}: ${change.key}`, 2));\n }\n\n if (options.dryRun) {\n console.log();\n console.log(indent(chalk.dim('Dry run — no changes made.')));\n return;\n }\n\n if (!options.overwrite) {\n const confirmed = await confirm(`Update ${options.output}?`);\n if (!confirmed) {\n console.log(indent(chalk.dim('Aborted. No changes made.')));\n return;\n }\n }\n } else if (options.dryRun) {\n console.log();\n console.log(indent(chalk.dim('Dry run — no changes made.')));\n }\n\n if (!options.dryRun) {\n await writeFileSafe(outputPath, newContent);\n\n console.log();\n console.log(indent(success(`Created ${options.output}`)));\n }\n });\n\ninterface DiffEntry {\n type: 'added' | 'removed' | 'updated';\n key: string;\n}\n\nfunction computeDiff(existingContent: string, newContent: string): DiffEntry[] {\n const existingVars = new Map<string, string>();\n const newVars = new Map<string, string>();\n\n for (const line of existingContent.split('\\n')) {\n const trimmed = line.trim();\n if (!trimmed || trimmed.startsWith('#')) continue;\n const idx = trimmed.indexOf('=');\n if (idx === -1) continue;\n existingVars.set(trimmed.slice(0, idx).trim(), trimmed.slice(idx + 1).trim());\n }\n\n for (const line of newContent.split('\\n')) {\n const trimmed = line.trim();\n if (!trimmed || trimmed.startsWith('#')) continue;\n const idx = trimmed.indexOf('=');\n if (idx === -1) continue;\n newVars.set(trimmed.slice(0, idx).trim(), trimmed.slice(idx + 1).trim());\n }\n\n const diff: DiffEntry[] = [];\n\n for (const [key, value] of newVars) {\n if (!existingVars.has(key)) {\n diff.push({ type: 'added', key });\n } else if (existingVars.get(key) !== value) {\n diff.push({ type: 'updated', key });\n }\n }\n\n for (const key of existingVars.keys()) {\n if (!newVars.has(key)) {\n diff.push({ type: 'removed', key });\n }\n }\n\n return diff;\n}\n","import { Command } from 'commander';\nimport { detectProject } from '../../core/project.js';\nimport {\n isGitRepo,\n isHookInstalled,\n installHook,\n uninstallHook,\n getPreCommitHookPath,\n} from '../../utils/git.js';\nimport { error, warning, success, indent } from '../../utils/logger.js';\n\nexport const hookCommand = new Command('hook')\n .description('Install, manage, and check a pre-commit git hook that prevents .env commits')\n .addCommand(\n new Command('install')\n .description('Install the enve pre-commit hook')\n .action(async () => {\n const cwd = process.cwd();\n const projectPath = await detectProject(cwd);\n\n if (!projectPath || !(await isGitRepo(projectPath))) {\n console.log(error('Not a git repository. Run `git init` first.'));\n process.exit(1);\n }\n\n await installHook(projectPath);\n console.log();\n console.log(indent(success('Pre-commit hook installed')));\n console.log(indent(`Location: ${getPreCommitHookPath(projectPath)}`));\n console.log();\n console.log(indent('What it does:'));\n console.log(indent(bullet('Blocks commits of .env, .env.local, .env.production files'), 2));\n console.log(indent(bullet('Shows a helpful message with next steps'), 2));\n console.log(indent(bullet('Can be bypassed with --no-verify (emergency only)'), 2));\n console.log();\n console.log(indent('To remove: enve hook uninstall'));\n })\n )\n .addCommand(\n new Command('status')\n .description('Check the status of the enve pre-commit hook')\n .action(async () => {\n const cwd = process.cwd();\n const projectPath = await detectProject(cwd);\n\n if (!projectPath || !(await isGitRepo(projectPath))) {\n console.log(error('Not a git repository.'));\n process.exit(1);\n }\n\n const installed = await isHookInstalled(projectPath);\n console.log();\n if (installed) {\n console.log(indent(success('Pre-commit hook: Installed (enve protection active)')));\n console.log(indent(`Location: ${getPreCommitHookPath(projectPath)}`));\n } else {\n console.log(indent(warning('Pre-commit hook: Not installed')));\n console.log(indent('Run `enve hook install` to enable .env commit protection.'));\n }\n })\n )\n .addCommand(\n new Command('uninstall')\n .description('Remove the enve pre-commit hook')\n .action(async () => {\n const cwd = process.cwd();\n const projectPath = await detectProject(cwd);\n\n if (!projectPath || !(await isGitRepo(projectPath))) {\n console.log(error('Not a git repository.'));\n process.exit(1);\n }\n\n const removed = await uninstallHook(projectPath);\n console.log();\n if (removed) {\n console.log(indent(success('Pre-commit hook removed')));\n console.log(indent('Run `enve hook install` to re-enable.'));\n } else {\n console.log(indent(warning('No enve hook found to remove.')));\n }\n })\n );\n\nfunction bullet(text: string): string {\n return `• ${text}`;\n}\n","import { Command } from 'commander';\nimport { render } from 'ink';\nimport React, { useState, useCallback } from 'react';\nimport { getProjects } from '../../core/config.js';\nimport { getProjectInfo } from '../../core/project.js';\nimport { runDoctor } from './doctor.js';\nimport { Dashboard } from '../../ui/Dashboard.js';\nimport type { ProjectInfo } from '../../types/index.js';\nimport pkg from '../../../package.json' with { type: 'json' };\n\nexport const dashboardCommand = new Command('dashboard')\n .description('Interactive terminal dashboard showing all tracked projects')\n .action(async () => {\n const tracked = getProjects();\n\n if (tracked.length === 0) {\n console.log();\n console.log(' Enve Dashboard');\n console.log();\n console.log(' No projects tracked yet.');\n console.log(' Run `enve scan` or `enve doctor` in a project to add it.');\n console.log();\n return;\n }\n\n if (!process.stdin.isTTY) {\n console.log();\n console.log(' The interactive dashboard requires a TTY terminal.');\n console.log(' Run `enve dashboard` in your terminal to use it.');\n console.log();\n return;\n }\n\n const initialProjects = await analyzeProjects(tracked.map((p) => p.path));\n\n render(\n <DashboardApp\n initialProjects={initialProjects}\n trackedPaths={tracked.map((p) => p.path)}\n version={pkg.version}\n />\n );\n });\n\ninterface DashboardAppProps {\n initialProjects: ProjectInfo[];\n trackedPaths: string[];\n version: string;\n}\n\nfunction DashboardApp({ initialProjects, trackedPaths, version }: DashboardAppProps): React.ReactElement {\n const [projects, setProjects] = useState(initialProjects);\n\n const handleRunDoctor = useCallback(\n async (project: ProjectInfo) => {\n await runDoctor(project.path);\n },\n []\n );\n\n const handleRefresh = useCallback(async () => {\n const fresh = await analyzeProjects(trackedPaths);\n setProjects(fresh);\n }, [trackedPaths]);\n\n return (\n <Dashboard\n projects={projects}\n version={version}\n onRunDoctor={handleRunDoctor}\n onRefresh={handleRefresh}\n />\n );\n}\n\nasync function analyzeProjects(paths: string[]): Promise<ProjectInfo[]> {\n const results = await Promise.all(paths.map((p) => getProjectInfo(p)));\n return results;\n}\n","import React, { useState, useMemo } from 'react';\nimport { Box, Text, useInput, useApp, useStdout } from 'ink';\nimport type { ProjectInfo } from '../types/index.js';\nimport { ProjectCard } from './ProjectCard.js';\nimport { IssueList } from './IssueList.js';\nimport { ScoreBadge } from './ScoreBadge.js';\nimport { colors, layout } from './theme.js';\n\ninterface DashboardProps {\n projects: ProjectInfo[];\n version: string;\n onRunDoctor: (project: ProjectInfo) => void | Promise<void>;\n onRefresh: () => void | Promise<void>;\n}\n\nexport function Dashboard({\n projects,\n version,\n onRunDoctor,\n onRefresh,\n}: DashboardProps): React.ReactElement {\n const { exit } = useApp();\n const { stdout } = useStdout();\n const [selectedIndex, setSelectedIndex] = useState(0);\n const [isExiting, setIsExiting] = useState(false);\n\n const terminalRows = stdout.rows ?? 24;\n const selectedProject = projects[selectedIndex];\n const maxVisible = Math.max(4, Math.floor((terminalRows - 10) / 5));\n\n const visibleProjects = useMemo(() => {\n if (projects.length <= maxVisible) return projects;\n\n let start = Math.max(0, selectedIndex - Math.floor(maxVisible / 2));\n let end = start + maxVisible;\n if (end > projects.length) {\n end = projects.length;\n start = Math.max(0, end - maxVisible);\n }\n return projects.slice(start, end);\n }, [projects, selectedIndex, maxVisible]);\n\n const visibleStart = projects.length <= maxVisible ? 0 : Math.max(0, selectedIndex - Math.floor(maxVisible / 2));\n\n useInput(\n (input, key) => {\n if (isExiting) return;\n\n if (key.upArrow || input === 'k') {\n setSelectedIndex((prev) => Math.max(0, prev - 1));\n } else if (key.downArrow || input === 'j') {\n setSelectedIndex((prev) => Math.min(projects.length - 1, prev + 1));\n } else if (key.return) {\n if (selectedProject) {\n setIsExiting(true);\n void Promise.resolve(onRunDoctor(selectedProject)).then(() => exit());\n }\n } else if (input === 'r') {\n void Promise.resolve(onRefresh());\n } else if (input === 'q' || key.escape) {\n setIsExiting(true);\n exit();\n }\n },\n { isActive: !isExiting }\n );\n\n if (projects.length === 0) {\n return (\n <Box flexDirection=\"column\" padding={2}>\n <Text bold color={colors.ink}>enve dashboard</Text>\n <Box marginTop={1}>\n <Text color={colors.muted}>No projects tracked yet.</Text>\n </Box>\n <Box marginTop={1}>\n <Text color={colors.subtle}>Run `enve scan` or `enve doctor` in a project to add it.</Text>\n </Box>\n </Box>\n );\n }\n\n return (\n <Box flexDirection=\"column\" width=\"100%\" paddingX={layout.paddingX} paddingY={layout.paddingY}>\n {/* Top bar */}\n <Box justifyContent=\"space-between\" width=\"100%\" marginBottom={1}>\n <Box>\n <Text bold color={colors.ink}>enve</Text>\n <Text color={colors.muted}> dashboard</Text>\n </Box>\n <Text color={colors.subtle}>v{version}</Text>\n </Box>\n\n {/* Main content */}\n <Box flexGrow={1} width=\"100%\">\n {/* Sidebar */}\n <Box\n width={layout.sidebarWidth}\n flexDirection=\"column\"\n borderStyle=\"single\"\n borderColor={colors.border}\n paddingX={1}\n paddingY={1}\n marginRight={2}\n >\n <Box marginBottom={1}>\n <Text bold color={colors.ink}>Projects</Text>\n <Text color={colors.subtle}> ({projects.length})</Text>\n </Box>\n\n <Box flexDirection=\"column\" rowGap={1} flexGrow={1}>\n {visibleProjects.map((project, index) => {\n const actualIndex = visibleStart + index;\n return (\n <ProjectCard\n key={project.path}\n project={project}\n isSelected={actualIndex === selectedIndex}\n isFocused={actualIndex === selectedIndex}\n />\n );\n })}\n </Box>\n\n {projects.length > maxVisible && (\n <Box marginTop={1}>\n <Text color={colors.subtle}>Use ↑/↓ to scroll</Text>\n </Box>\n )}\n </Box>\n\n {/* Details panel */}\n <Box\n flexGrow={1}\n flexDirection=\"column\"\n borderStyle=\"single\"\n borderColor={colors.border}\n paddingX={2}\n paddingY={1}\n >\n {selectedProject ? (\n <ProjectDetail project={selectedProject} />\n ) : (\n <Box>\n <Text color={colors.muted}>Select a project to view details.</Text>\n </Box>\n )}\n </Box>\n </Box>\n\n {/* Bottom bar */}\n <Box\n justifyContent=\"space-between\"\n width=\"100%\"\n borderStyle=\"single\"\n borderColor={colors.border}\n paddingX={1}\n marginTop={1}\n >\n <Box columnGap={3}>\n <Text color={colors.muted}><Text bold>↑/↓</Text> navigate</Text>\n <Text color={colors.muted}><Text bold>Enter</Text> doctor</Text>\n <Text color={colors.muted}><Text bold>r</Text> refresh</Text>\n <Text color={colors.muted}><Text bold>q</Text> quit</Text>\n </Box>\n <Text color={colors.subtle}>enve-doctor v{version}</Text>\n </Box>\n </Box>\n );\n}\n\nfunction ProjectDetail({ project }: { project: ProjectInfo }): React.ReactElement {\n const envFileNames = project.envFiles.map((f) => f.filename).join(', ') || 'none';\n\n return (\n <Box flexDirection=\"column\" width=\"100%\">\n <Box justifyContent=\"space-between\" alignItems=\"center\" marginBottom={1}>\n <Text bold color={colors.ink}>{project.name}</Text>\n <ScoreBadge score={project.score} />\n </Box>\n\n <Box marginBottom={1}>\n <Text color={colors.muted}>{project.path}</Text>\n </Box>\n\n <Box flexDirection=\"row\" columnGap={4} marginBottom={1}>\n <Metric label=\"Env vars\" value={String(project.envCount)} />\n <Metric label=\"References\" value={String(project.referenceCount)} />\n <Metric label=\"Issues\" value={String(project.score.totalIssues)} />\n </Box>\n\n <Box flexDirection=\"row\" columnGap={4} marginBottom={1}>\n <Status label=\"Git repo\" ok={project.hasGit} />\n <Status label=\"Git hook\" ok={project.hookInstalled} />\n <Status label=\".gitignore\" ok={project.hasEnvInGitignore} />\n </Box>\n\n <Box marginTop={1} marginBottom={1}>\n <Text color={colors.subtle}>Env files: </Text>\n <Text color={colors.ink}>{envFileNames}</Text>\n </Box>\n\n <Box marginTop={1} marginBottom={1}>\n <Text color={colors.borderStrong}>{'─'.repeat(60)}</Text>\n </Box>\n\n <Box marginTop={1} marginBottom={1}>\n <Text bold color={colors.ink}>Top issues</Text>\n </Box>\n\n <IssueList issues={project.issues} maxRows={8} />\n </Box>\n );\n}\n\nfunction Metric({ label, value }: { label: string; value: string }): React.ReactElement {\n return (\n <Box flexDirection=\"column\">\n <Text color={colors.subtle}>{label}</Text>\n <Text bold color={colors.ink}>{value}</Text>\n </Box>\n );\n}\n\nfunction Status({ label, ok }: { label: string; ok: boolean }): React.ReactElement {\n return (\n <Box>\n <Text color={ok ? colors.success : colors.error}>{ok ? '✓' : '✗'} </Text>\n <Text color={colors.muted}>{label}</Text>\n </Box>\n );\n}\n","import React from 'react';\nimport { Box, Text } from 'ink';\nimport type { ProjectInfo } from '../types/index.js';\nimport { ScoreBadge } from './ScoreBadge.js';\nimport { colors } from './theme.js';\n\ninterface ProjectCardProps {\n project: ProjectInfo;\n isSelected: boolean;\n isFocused: boolean;\n}\n\nexport function ProjectCard({\n project,\n isSelected,\n isFocused,\n}: ProjectCardProps): React.ReactElement {\n const { score } = project;\n const borderColor = isSelected ? colors.ink : colors.border;\n const indicator = isFocused ? '›' : ' ';\n\n return (\n <Box\n borderStyle=\"single\"\n borderColor={borderColor}\n paddingX={1}\n paddingY={1}\n flexDirection=\"column\"\n >\n <Box justifyContent=\"space-between\">\n <Box>\n <Text color={isSelected ? colors.ink : colors.muted}>{indicator} </Text>\n <Text bold color={colors.ink}>{project.name}</Text>\n </Box>\n <ScoreBadge score={score} compact />\n </Box>\n\n <Box marginTop={1}>\n <Text color={colors.muted}>{project.path}</Text>\n </Box>\n\n <Box marginTop={1} columnGap={2}>\n {score.errorCount > 0 && (\n <Text color={colors.error}>{score.errorCount} err</Text>\n )}\n {score.warningCount > 0 && (\n <Text color={colors.warning}>{score.warningCount} warn</Text>\n )}\n {score.totalIssues === 0 && (\n <Text color={colors.success}>Clean</Text>\n )}\n </Box>\n </Box>\n );\n}\n","import React from 'react';\nimport { Box, Text } from 'ink';\nimport type { ScoreResult } from '../types/index.js';\nimport { gradeStyles } from './theme.js';\n\ninterface ScoreBadgeProps {\n score: ScoreResult;\n compact?: boolean;\n}\n\nexport function ScoreBadge({ score, compact = false }: ScoreBadgeProps): React.ReactElement {\n const style = gradeStyles[score.grade];\n\n if (compact) {\n return (\n <Box>\n <Text backgroundColor={style.bg} color={style.text} bold>\n {' '}\n {score.score}\n {' '}\n </Text>\n </Box>\n );\n }\n\n return (\n <Box>\n <Text backgroundColor={style.bg} color={style.text} bold>\n {' '}\n {score.score} {score.grade}\n {' '}\n </Text>\n </Box>\n );\n}\n","import type { Grade, Severity } from '../types/index.js';\n\nexport const colors = {\n // Warm monochrome base\n canvas: '#FAFAF8',\n ink: '#1A1A1A',\n muted: '#6B6B6B',\n subtle: '#A3A3A3',\n\n // Borders & dividers\n border: '#E5E5E0',\n borderStrong: '#D4D4CF',\n\n // Semantic accents — desaturated pastels\n excellent: '#10B981', // emerald 500\n excellentBg: '#ECFDF5', // emerald 50\n good: '#F59E0B', // amber 500\n goodBg: '#FFFBEB', // amber 50\n attention: '#F97316', // orange 500\n attentionBg: '#FFF7ED', // orange 50\n critical: '#EF4444', // red 500\n criticalBg: '#FEF2F2', // red 50\n\n // Severity\n error: '#DC2626',\n warning: '#D97706',\n info: '#2563EB',\n success: '#059669',\n} as const;\n\nexport const gradeStyles: Record<\n Grade,\n { text: string; bg: string; label: string }\n> = {\n Excellent: { text: colors.excellent, bg: colors.excellentBg, label: 'Excellent' },\n Good: { text: colors.good, bg: colors.goodBg, label: 'Good' },\n 'Needs attention': { text: colors.attention, bg: colors.attentionBg, label: 'Needs attention' },\n Critical: { text: colors.critical, bg: colors.criticalBg, label: 'Critical' },\n};\n\nexport const severityStyles: Record<Severity, { color: string; symbol: string }> = {\n error: { color: colors.error, symbol: '●' },\n warning: { color: colors.warning, symbol: '●' },\n info: { color: colors.info, symbol: '●' },\n};\n\nexport const issueTypeSymbol: Record<string, string> = {\n missing: '⊘',\n unused: '○',\n invalid: '✕',\n suspicious: '◆',\n 'secret-risk': '▲',\n 'syntax-error': '✕',\n gitignore: '◉',\n};\n\nexport const layout = {\n sidebarWidth: 34,\n maxContentWidth: 86,\n paddingX: 2,\n paddingY: 1,\n} as const;\n","import React from 'react';\nimport { Box, Text } from 'ink';\nimport type { Issue } from '../types/index.js';\nimport { severityStyles, issueTypeSymbol, colors } from './theme.js';\n\ninterface IssueListProps {\n issues: Issue[];\n maxRows?: number;\n}\n\nexport function IssueList({ issues, maxRows = 12 }: IssueListProps): React.ReactElement {\n if (issues.length === 0) {\n return (\n <Box paddingY={1}>\n <Text color={colors.success}>No issues found. Great job.</Text>\n </Box>\n );\n }\n\n const sorted = [...issues].sort((a, b) => {\n const rank = { error: 0, warning: 1, info: 2 };\n return rank[a.severity] - rank[b.severity];\n });\n\n const visible = sorted.slice(0, maxRows);\n const remaining = sorted.length - visible.length;\n\n return (\n <Box flexDirection=\"column\" width=\"100%\">\n {visible.map((issue, index) => {\n const style = severityStyles[issue.severity];\n const symbol = issueTypeSymbol[issue.type] ?? '•';\n\n return (\n <Box key={`${issue.type}-${issue.key}-${index}`} flexDirection=\"column\" marginBottom={1}>\n <Box>\n <Text color={style.color}>{symbol} </Text>\n <Text bold color={colors.ink}>{issue.key}</Text>\n <Text color={colors.subtle}> — {issue.type}</Text>\n </Box>\n <Box marginLeft={2}>\n <Text color={colors.muted}>{issue.message}</Text>\n </Box>\n {issue.suggestion && (\n <Box marginLeft={2}>\n <Text color={colors.subtle}>→ {issue.suggestion}</Text>\n </Box>\n )}\n </Box>\n );\n })}\n {remaining > 0 && (\n <Box marginTop={1}>\n <Text color={colors.subtle}>+ {remaining} more issue{remaining === 1 ? '' : 's'}</Text>\n </Box>\n )}\n </Box>\n );\n}\n","{\n \"name\": \"enve-doctor\",\n \"version\": \"1.0.2\",\n \"description\": \"Environment Variable Doctor — scan, validate, and protect your env files\",\n \"main\": \"dist/cli.mjs\",\n \"bin\": {\n \"enve\": \"dist/cli.mjs\"\n },\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://github.com/RufixDuke/enve.git\"\n },\n \"bugs\": {\n \"url\": \"https://github.com/RufixDuke/enve/issues\"\n },\n \"homepage\": \"https://enve-lac.vercel.app\",\n \"files\": [\n \"dist\"\n ],\n \"scripts\": {\n \"build\": \"tsup src/cli/cli.ts --minify --clean\",\n \"dev\": \"tsx src/cli/index.ts\",\n \"test\": \"vitest run\",\n \"test:watch\": \"vitest\",\n \"prepublishOnly\": \"npm run build\"\n },\n \"dependencies\": {\n \"acorn\": \"^8.14.0\",\n \"acorn-walk\": \"^8.3.4\",\n \"chalk\": \"^5.4.1\",\n \"commander\": \"^13.1.0\",\n \"conf\": \"^13.1.0\",\n \"dotenv\": \"^16.4.7\",\n \"fast-glob\": \"^3.3.3\",\n \"ink\": \"^5.1.0\",\n \"ora\": \"^8.2.0\",\n \"react\": \"^18.3.1\"\n },\n \"devDependencies\": {\n \"@types/node\": \"^22.13.0\",\n \"@types/react\": \"^18.3.18\",\n \"@typescript-eslint/eslint-plugin\": \"^8.22.0\",\n \"@typescript-eslint/parser\": \"^8.22.0\",\n \"eslint\": \"^9.19.0\",\n \"ink-testing-library\": \"^4.0.0\",\n \"tsup\": \"^8.3.6\",\n \"tsx\": \"^4.19.2\",\n \"typescript\": \"^5.7.3\",\n \"vitest\": \"^3.0.5\"\n },\n \"keywords\": [\n \"env\",\n \"environment-variables\",\n \"secrets\",\n \"security\",\n \"cli\",\n \"developer-tools\"\n ],\n \"author\": \"Abdul-Qudus Rufai\",\n \"license\": \"MIT\",\n \"engines\": {\n \"node\": \">=18.0.0\"\n }\n}\n"],"mappings":"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","names":["Command","Command","chalk","join","basename","readFile","writeFile","access","mkdir","dirname","readFileSafe","filePath","writeFileSafe","content","ensureDir","fileExists","dirPath","join","PRE_COMMIT_HOOK_MARKER","PRE_COMMIT_HOOK_END","isGitRepo","projectPath","fileExists","join","readGitignore","gitignorePath","readFileSafe","isPatternInGitignore","gitignoreContent","pattern","line","trimmed","isEnvFileGitignored","filename","content","getPreCommitHookPath","getHookScript","isHookInstalled","hookPath","installHook","existing","newContent","writeFileSafe","uninstallHook","markerStart","markerEnd","before","after","join","readFileSync","parseEnvFile","filePath","content","readFileSync","variables","lines","trimmed","equalIndex","key","rawValue","value","comment","parseValue","isSecret","detectSecret","raw","endIndex","parseQuotedValue","rest","extractInlineComment","trailing","commentIndex","findUnquotedCommentIndex","quote","escaped","char","extractInlineComment","rest","idx","findUnquotedCommentIndex","value","i","detectSecret","key","findEnvFiles","projectPath","envFileNames","files","name","filePath","join","readFileSafe","parseEnvFile","parseAllEnvFiles","fg","readFile","Parser","fullAncestor","DEFAULT_INCLUDE","DEFAULT_EXCLUDE","scanProject","projectPath","options","include","exclude","files","references","file","fileRefs","scanFile","filePath","content","ast","node","_state","ancestors","ref","extractEnvReference","refs","extractDestructuredEnvReferences","key","getMemberExpressionKey","parent","fallback","detectFallback","createReference","isEnvDestructuringPattern","prop","fallbackValue","expressionToString","nested","isEnvSource","grandParent","isProcessNode","isProcessEnvNode","isLeft","isRight","fallbackNode","getFallbackType","line","column","context","extractContext","lines","start","end","ALWAYS_ALLOWED","analyze","envFiles","references","projectPath","issues","checkMissingVariables","checkUnusedVariables","checkSecretRisks","checkGitignore","checkExampleFile","definedKeys","file","v","ref","referencedKeys","r","variable","filenames","f","filesToCheck","messages","suggestions","filename","isEnvFileGitignored","exampleFile","envFile","exampleKeys","calculateScore","score","errorCount","warningCount","infoCount","unusedDeduction","exampleDeduction","issue","getGrade","Conf","readFile","join","DEFAULT_CONFIG","createConfig","getProjects","isTracked","projectPath","p","getProject","addProject","config","project","detectProjectName","projects","detectProjectName","projectPath","pkgRaw","readFile","join","pkg","detectProject","startPath","current","fileExists","join","parent","getProjectInfo","projectPath","envFiles","references","hasGit","hookInstalled","parseAllEnvFiles","scanProject","isGitRepo","isHookInstalled","issues","analyze","score","calculateScore","name","basename","addProject","sum","f","i","chalk","symbols","error","message","warning","success","bullet","message","symbols","indent","level","header","title","chalk","scanCommand","Command","cwd","projectPath","detectProject","error","info","getProjectInfo","indent","chalk","file","bullet","success","errors","i","warnings","infos","issue","warning","formatScore","score","text","Command","chalk","doctorCommand","Command","cwd","projectPath","detectProject","error","runDoctor","info","getProjectInfo","envFiles","parseAllEnvFiles","indent","header","chalk","fileStructureIssues","i","variableIssues","securityIssues","validationIssues","printSection","printVariablesSection","printRecommendations","formatScore","title","issues","success","issue","formatter","warning","totalDefined","sum","f","message","actionable","high","medium","low","idx","score","text","Command","chalk","copyFile","createInterface","prompt","message","rl","confirm","defaultValue","answer","unusedCommand","Command","options","cwd","projectPath","detectProject","error","envFiles","parseAllEnvFiles","references","scanProject","referencedKeys","r","alwaysAllowed","unused","file","variable","indent","header","success","byFile","item","filename","items","warning","chalk","confirm","byPath","removedCount","filePath","backupPath","formatTimestamp","copyFile","lines","readFileSafe","linesToRemove","i","newLines","_","idx","writeFileSafe","date","Command","chalk","join","STANDARD_NODE_VARS","missingCommand","Command","options","cwd","projectPath","detectProject","error","envFiles","parseAllEnvFiles","references","scanProject","definedKeys","v","missingNoFallback","r","missingWithFallback","indent","header","success","ref","chalk","l","warning","addMissingVariables","missing","envPath","join","f","updates","exampleUpdates","value","prompt","confirm","existing","readFileSafe","separator","writeFileSafe","examplePath","exampleExisting","exampleSeparator","Command","chalk","validatePort","value","port","validateUrl","url","validateNodeEnv","validateSecret","key","forbidden","validateBoolean","normalized","boolMap","isSecretKey","copyFile","validateCommand","Command","options","cwd","projectPath","detectProject","error","envFile","parseAllEnvFiles","f","variables","indent","header","errorCount","warningCount","fixes","variable","result","validateVariable","success","truncate","chalk","fixedValue","getFixedValue","safeFixes","isSecretKey","confirm","applyFixes","key","value","validatePort","validateNodeEnv","validateUrl","validateSecret","validateBoolean","normalized","filePath","backupPath","formatTimestamp","copyFile","content","readFileSafe","fixMap","lines","line","trimmed","equalIndex","prefix","suffix","commentIndex","comment","leadingSpaces","writeFileSafe","date","max","Command","chalk","join","generateExampleCommand","Command","options","cwd","projectPath","detectProject","error","envPath","join","readFileSafe","outputPath","existing","variables","parseEnvFile","newLines","redactedCount","copiedCount","variable","isSecretKey","newContent","indent","header","bullet","diff","computeDiff","success","change","symbol","chalk","confirm","writeFileSafe","existingContent","existingVars","newVars","line","trimmed","idx","key","value","Command","hookCommand","Command","cwd","projectPath","detectProject","isGitRepo","error","installHook","indent","success","getPreCommitHookPath","bullet","installed","isHookInstalled","warning","removed","uninstallHook","text","Command","render","React","useState","useCallback","React","useState","useMemo","Box","Text","useInput","useApp","useStdout","React","Box","Text","React","Box","Text","colors","gradeStyles","severityStyles","issueTypeSymbol","layout","ScoreBadge","score","compact","style","gradeStyles","React","Box","Text","ProjectCard","project","isSelected","isFocused","score","borderColor","colors","indicator","React","Box","Text","ScoreBadge","React","Box","Text","IssueList","issues","maxRows","React","Box","Text","colors","sorted","a","b","rank","visible","remaining","issue","index","style","severityStyles","symbol","issueTypeSymbol","Dashboard","projects","version","onRunDoctor","onRefresh","exit","useApp","stdout","useStdout","selectedIndex","setSelectedIndex","useState","isExiting","setIsExiting","terminalRows","selectedProject","maxVisible","visibleProjects","useMemo","start","end","visibleStart","useInput","input","key","prev","React","Box","Text","colors","layout","project","index","actualIndex","ProjectCard","ProjectDetail","envFileNames","f","ScoreBadge","Metric","Status","IssueList","label","value","ok","package_default","dashboardCommand","Command","tracked","getProjects","initialProjects","analyzeProjects","p","render","React","DashboardApp","package_default","trackedPaths","version","projects","setProjects","useState","handleRunDoctor","useCallback","project","runDoctor","handleRefresh","fresh","Dashboard","paths","getProjectInfo","program","Command","package_default","scanCommand","doctorCommand","unusedCommand","missingCommand","validateCommand","generateExampleCommand","hookCommand","dashboardCommand","reason"]}
|
|
1
|
+
{"version":3,"sources":["../src/cli/cli.ts","../src/cli/commands/scan.ts","../src/core/project.ts","../src/utils/fs.ts","../src/utils/git.ts","../src/core/parser.ts","../src/core/scanner.ts","../src/core/analyzer.ts","../src/core/config.ts","../src/utils/logger.ts","../src/cli/commands/doctor.ts","../src/cli/commands/unused.ts","../src/utils/prompt.ts","../src/cli/commands/missing.ts","../src/cli/commands/validate.ts","../src/utils/validators.ts","../src/cli/commands/generate-example.ts","../src/cli/commands/hook.ts","../src/cli/commands/dashboard.tsx","../src/ui/Dashboard.tsx","../src/ui/ProjectCard.tsx","../src/ui/ScoreBadge.tsx","../src/ui/theme.ts","../src/ui/IssueList.tsx","../package.json"],"sourcesContent":["import { Command } from 'commander';\nimport { scanCommand } from './commands/scan.js';\nimport { doctorCommand } from './commands/doctor.js';\nimport { unusedCommand } from './commands/unused.js';\nimport { missingCommand } from './commands/missing.js';\nimport { validateCommand } from './commands/validate.js';\nimport { generateExampleCommand } from './commands/generate-example.js';\nimport { hookCommand } from './commands/hook.js';\nimport { dashboardCommand } from './commands/dashboard.js';\nimport pkg from '../../package.json' with { type: 'json' };\n\nconst program = new Command();\n\nprogram\n .name('enve')\n .description('Environment Variable Doctor — scan, validate, and protect your env files')\n .version(pkg.version);\n\nprogram.addCommand(scanCommand);\nprogram.addCommand(doctorCommand);\nprogram.addCommand(unusedCommand);\nprogram.addCommand(missingCommand);\nprogram.addCommand(validateCommand);\nprogram.addCommand(generateExampleCommand);\nprogram.addCommand(hookCommand);\nprogram.addCommand(dashboardCommand);\n\nprocess.on('unhandledRejection', (reason) => {\n console.error('\\n Unexpected error:', reason instanceof Error ? reason.message : reason);\n console.error(' If this keeps happening, please open an issue with the stack trace above.');\n process.exit(1);\n});\n\nprogram.parse();\n","import { Command } from 'commander';\nimport chalk from 'chalk';\nimport { detectProject, getProjectInfo } from '../../core/project.js';\nimport { success, warning, error, bullet, indent } from '../../utils/logger.js';\n\nexport const scanCommand = new Command('scan')\n .description('Quick overview of the current project\\'s environment variable health')\n .action(async () => {\n const cwd = process.cwd();\n const projectPath = await detectProject(cwd);\n\n if (!projectPath) {\n console.log(error('No package.json found. Are you in a project directory?'));\n process.exit(1);\n }\n\n const info = await getProjectInfo(projectPath);\n\n console.log();\n console.log(indent(`${chalk.bold('Enve Scan Report')} — ${info.name}`));\n console.log(indent(chalk.dim(info.path)));\n console.log();\n\n console.log(indent(`.env files found: ${info.envFiles.length}`));\n for (const file of info.envFiles) {\n console.log(indent(`${bullet(`${file.filename.padEnd(18)} ${file.variables.length} variables`)}`, 2));\n }\n console.log();\n\n console.log(indent(`Code references: ${info.referenceCount} variables used`));\n console.log();\n\n if (info.issues.length === 0) {\n console.log(indent(success('No issues found. Great job!')));\n } else {\n console.log(indent(`Issues found: ${info.issues.length}`));\n\n const errors = info.issues.filter((i) => i.severity === 'error');\n const warnings = info.issues.filter((i) => i.severity === 'warning');\n const infos = info.issues.filter((i) => i.severity === 'info');\n\n for (const issue of errors) {\n console.log(indent(error(issue.message), 2));\n }\n for (const issue of warnings) {\n console.log(indent(warning(issue.message), 2));\n }\n for (const issue of infos) {\n console.log(indent(success(issue.message), 2));\n }\n }\n\n console.log();\n console.log(indent(`Score: ${formatScore(info.score.score)} [ ${info.score.grade} ]`));\n console.log(indent(chalk.dim(`${info.score.errorCount} errors, ${info.score.warningCount} warnings`)));\n console.log();\n console.log(indent('Run `enve doctor` for detailed recommendations.'));\n });\n\nfunction formatScore(score: number): string {\n const text = `${score}/100`;\n if (score >= 90) return chalk.bgGreen.black(` ${text} `);\n if (score >= 70) return chalk.bgYellow.black(` ${text} `);\n if (score >= 50) return chalk.red(text);\n return chalk.bgRed.white(` ${text} `);\n}\n","import { join, basename } from 'node:path';\nimport { fileExists } from '../utils/fs.js';\nimport { isGitRepo } from '../utils/git.js';\nimport { parseAllEnvFiles } from './parser.js';\nimport { scanProject } from './scanner.js';\nimport { analyze, calculateScore } from './analyzer.js';\nimport { addProject } from './config.js';\nimport type { ProjectInfo } from '../types/index.js';\nimport { isHookInstalled } from '../utils/git.js';\n\nexport async function detectProject(startPath: string): Promise<string | undefined> {\n let current = startPath;\n while (true) {\n if (await fileExists(join(current, 'package.json'))) {\n return current;\n }\n const parent = join(current, '..');\n if (parent === current) break;\n current = parent;\n }\n return undefined;\n}\n\nexport async function getProjectInfo(projectPath: string): Promise<ProjectInfo> {\n const [envFiles, references, hasGit, hookInstalled] = await Promise.all([\n parseAllEnvFiles(projectPath),\n scanProject(projectPath),\n isGitRepo(projectPath),\n isHookInstalled(projectPath),\n ]);\n\n const issues = await analyze(envFiles, references, projectPath);\n const score = calculateScore(issues);\n const name = basename(projectPath);\n\n await addProject(projectPath);\n\n return {\n name,\n path: projectPath,\n envFiles,\n envCount: envFiles.reduce((sum, f) => sum + f.variables.length, 0),\n referenceCount: references.length,\n issues,\n score,\n hasGit,\n hookInstalled,\n hasEnvInGitignore: !issues.some((i) => i.type === 'gitignore'),\n lastModified: new Date(),\n };\n}\n","import { readFile, writeFile, access, mkdir } from 'node:fs/promises';\nimport { dirname } from 'node:path';\n\nexport async function readFileSafe(filePath: string): Promise<string | undefined> {\n try {\n return await readFile(filePath, 'utf-8');\n } catch {\n return undefined;\n }\n}\n\nexport async function writeFileSafe(filePath: string, content: string): Promise<void> {\n await ensureDir(dirname(filePath));\n await writeFile(filePath, content, 'utf-8');\n}\n\nexport async function fileExists(filePath: string): Promise<boolean> {\n try {\n await access(filePath);\n return true;\n } catch {\n return false;\n }\n}\n\nexport async function ensureDir(dirPath: string): Promise<void> {\n if (!dirPath) return;\n try {\n await mkdir(dirPath, { recursive: true });\n } catch {\n // Directory may already exist\n }\n}\n","import { join } from 'node:path';\nimport { readFileSafe, fileExists, writeFileSafe } from './fs.js';\n\nconst PRE_COMMIT_HOOK_MARKER = '# === Enve Pre-Commit Hook ===';\nconst PRE_COMMIT_HOOK_END = '# === End Enve Hook ===';\n\nexport async function isGitRepo(projectPath: string): Promise<boolean> {\n return fileExists(join(projectPath, '.git'));\n}\n\nexport async function readGitignore(projectPath: string): Promise<string | undefined> {\n const gitignorePath = join(projectPath, '.gitignore');\n return readFileSafe(gitignorePath);\n}\n\nexport function isPatternInGitignore(gitignoreContent: string, pattern: string): boolean {\n const lines = gitignoreContent.split('\\n');\n return lines.some((line) => {\n const trimmed = line.trim();\n return trimmed === pattern || trimmed === `/${pattern}`;\n });\n}\n\nexport async function isEnvFileGitignored(\n projectPath: string,\n filename: string\n): Promise<boolean> {\n const content = await readGitignore(projectPath);\n if (!content) return false;\n return isPatternInGitignore(content, filename);\n}\n\nexport function getPreCommitHookPath(projectPath: string): string {\n return join(projectPath, '.git', 'hooks', 'pre-commit');\n}\n\nexport function getHookScript(): string {\n return `#!/bin/bash\n${PRE_COMMIT_HOOK_MARKER}\n# Blocks commits of .env files that may contain secrets\n# Installed by: enve hook install\n# Remove with: enve hook uninstall\n\nBLOCKED_PATTERNS='^\\\\.env(\\\\.[^/]*)?$'\nSTAGED_ENV_FILES=$(git diff --cached --name-only | grep -E \"$BLOCKED_PATTERNS\" || true)\n\nif [ -n \"$STAGED_ENV_FILES\" ]; then\n echo \"\"\n echo \" ✗ COMMIT BLOCKED by Enve\"\n echo \"\"\n echo \" You are attempting to commit environment files:\"\n echo \"$STAGED_ENV_FILES\" | sed 's/^/ /'\n echo \"\"\n echo \" These files may contain secrets (API keys, passwords, tokens).\"\n echo \"\"\n echo \" What to do instead:\"\n echo \" 1. Add them to .gitignore: echo '.env.local' >> .gitignore\"\n echo \" 2. Use .env.example for templates: enve generate-example\"\n echo \" 3. Share secrets through a secure channel (1Password, etc.)\"\n echo \"\"\n echo \" If you are CERTAIN these files are safe to commit:\"\n echo \" git commit --no-verify\"\n echo \"\"\n exit 1\nfi\n${PRE_COMMIT_HOOK_END}\n`;\n}\n\nexport async function isHookInstalled(projectPath: string): Promise<boolean> {\n const hookPath = getPreCommitHookPath(projectPath);\n if (!(await fileExists(hookPath))) return false;\n const content = await readFileSafe(hookPath);\n return content?.includes(PRE_COMMIT_HOOK_MARKER) ?? false;\n}\n\nexport async function installHook(projectPath: string): Promise<void> {\n const hookPath = getPreCommitHookPath(projectPath);\n const existing = (await readFileSafe(hookPath)) ?? '';\n\n if (existing.includes(PRE_COMMIT_HOOK_MARKER)) {\n return; // Already installed\n }\n\n const newContent = `${getHookScript()}\\n${existing}`;\n await writeFileSafe(hookPath, newContent);\n}\n\nexport async function uninstallHook(projectPath: string): Promise<boolean> {\n const hookPath = getPreCommitHookPath(projectPath);\n if (!(await fileExists(hookPath))) return false;\n\n const content = await readFileSafe(hookPath);\n if (!content || !content.includes(PRE_COMMIT_HOOK_MARKER)) return false;\n\n const markerStart = content.indexOf(PRE_COMMIT_HOOK_MARKER);\n const markerEnd = content.indexOf(PRE_COMMIT_HOOK_END, markerStart);\n if (markerStart === -1 || markerEnd === -1) return false;\n\n const before = content.slice(0, markerStart);\n const after = content.slice(markerEnd + PRE_COMMIT_HOOK_END.length);\n const newContent = `${before.trimEnd()}\\n${after.trimStart()}`.trim();\n\n if (newContent) {\n await writeFileSafe(hookPath, `${newContent}\\n`);\n } else {\n // No remaining content; leave file removal to caller if desired\n await writeFileSafe(hookPath, '');\n }\n return true;\n}\n","import { join } from 'node:path';\nimport { readFileSync } from 'node:fs';\nimport type { EnvFile, EnvVariable } from '../types/index.js';\nimport { readFileSafe } from '../utils/fs.js';\n\nexport function parseEnvFile(filePath: string): EnvVariable[] {\n let content: string | undefined;\n try {\n content = readFileSync(filePath, 'utf-8');\n } catch {\n return [];\n }\n if (!content) return [];\n\n const variables: EnvVariable[] = [];\n const lines = content.split('\\n');\n\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i];\n const trimmed = line.trim();\n\n if (!trimmed || trimmed.startsWith('#')) continue;\n\n const equalIndex = trimmed.indexOf('=');\n if (equalIndex === -1) continue;\n\n let key = trimmed.slice(0, equalIndex).trim();\n let rawValue = trimmed.slice(equalIndex + 1);\n\n // Strip export keyword\n if (key.startsWith('export ')) {\n key = key.slice(7).trim();\n }\n\n if (!key) continue;\n\n const { value, comment } = parseValue(rawValue);\n const isSecret = detectSecret(key, value);\n\n variables.push({\n key,\n value,\n line: i + 1,\n source: filePath,\n isSecret,\n comment,\n });\n }\n\n return variables;\n}\n\nfunction parseValue(raw: string): { value: string; comment?: string } {\n const trimmed = raw.trimStart();\n\n if (trimmed.startsWith('\"')) {\n const { value, endIndex } = parseQuotedValue(trimmed, '\"');\n const rest = trimmed.slice(endIndex + 1).trim();\n const comment = extractInlineComment(rest);\n return { value: unescape(value), comment };\n }\n\n if (trimmed.startsWith(\"'\")) {\n const { value, endIndex } = parseQuotedValue(trimmed, \"'\");\n const rest = trimmed.slice(endIndex + 1).trim();\n const comment = extractInlineComment(rest);\n return { value, comment };\n }\n\n const trailing = raw.trimEnd();\n const commentIndex = findUnquotedCommentIndex(trailing);\n if (commentIndex !== -1) {\n return {\n value: trailing.slice(0, commentIndex).trimEnd(),\n comment: trailing.slice(commentIndex + 1).trim(),\n };\n }\n\n return { value: raw.trimEnd() };\n}\n\nfunction parseQuotedValue(raw: string, quote: string): { value: string; endIndex: number } {\n let value = '';\n let escaped = false;\n for (let i = 1; i < raw.length; i++) {\n const char = raw[i];\n if (escaped) {\n if (char === 'n') value += '\\n';\n else if (char === 't') value += '\\t';\n else if (char === quote) value += quote;\n else value += `\\\\${char}`;\n escaped = false;\n continue;\n }\n if (char === '\\\\') {\n escaped = true;\n continue;\n }\n if (char === quote) {\n return { value, endIndex: i };\n }\n value += char;\n }\n // Unterminated quote — return rest as value\n return { value, endIndex: raw.length - 1 };\n}\n\nfunction unescape(value: string): string {\n // Escape sequences are already handled during quote parsing.\n // This function remains as a no-op fallback for any raw unquoted escapes.\n return value;\n}\n\nfunction extractInlineComment(rest: string): string | undefined {\n const idx = rest.indexOf('#');\n if (idx === -1) return undefined;\n return rest.slice(idx + 1).trim();\n}\n\nfunction findUnquotedCommentIndex(value: string): number {\n for (let i = 0; i < value.length; i++) {\n if (value[i] === '#' && (i === 0 || value[i - 1] === ' ')) {\n return i;\n }\n }\n return -1;\n}\n\nfunction detectSecret(key: string, value: string): boolean {\n const secretPatterns = /SECRET|KEY|TOKEN|PASSWORD|PRIVATE|ACCESS|CREDENTIAL/i;\n if (secretPatterns.test(key)) return true;\n\n // Simple entropy heuristic: high ratio of non-word characters or length > 20 with mixed case\n if (value.length > 20 && /[A-Z]/.test(value) && /[a-z]/.test(value) && /\\d/.test(value)) {\n return true;\n }\n\n return false;\n}\n\nexport async function findEnvFiles(projectPath: string): Promise<EnvFile[]> {\n const envFileNames = ['.env', '.env.local', '.env.development', '.env.production', '.env.test', '.env.example'];\n const files: EnvFile[] = [];\n\n for (const name of envFileNames) {\n const filePath = join(projectPath, name);\n const content = await readFileSafe(filePath);\n if (content !== undefined) {\n files.push({\n path: filePath,\n filename: name,\n variables: parseEnvFile(filePath),\n });\n }\n }\n\n return files;\n}\n\nexport async function parseAllEnvFiles(projectPath: string): Promise<EnvFile[]> {\n return findEnvFiles(projectPath);\n}\n","import fg from 'fast-glob';\nimport { readFile } from 'node:fs/promises';\nimport { Parser } from 'acorn';\nimport { fullAncestor } from 'acorn-walk';\nimport type { EnvReference, ScannerOptions } from '../types/index.js';\n\nconst DEFAULT_INCLUDE = [\n '**/*.{js,ts,jsx,tsx,mjs,cjs}',\n];\n\nconst DEFAULT_EXCLUDE = [\n '**/node_modules/**',\n '**/dist/**',\n '**/build/**',\n '**/.next/**',\n];\n\nexport async function scanProject(\n projectPath: string,\n options: ScannerOptions = {}\n): Promise<EnvReference[]> {\n const include = options.include ?? DEFAULT_INCLUDE;\n const exclude = options.exclude ?? DEFAULT_EXCLUDE;\n\n const files = await fg(include, {\n cwd: projectPath,\n ignore: exclude,\n absolute: true,\n });\n\n const references: EnvReference[] = [];\n for (const file of files) {\n const fileRefs = await scanFile(file);\n references.push(...fileRefs);\n }\n\n return references;\n}\n\nexport async function scanFile(filePath: string): Promise<EnvReference[]> {\n const references: EnvReference[] = [];\n const content = await readFile(filePath, 'utf-8');\n\n let ast: unknown;\n try {\n ast = Parser.parse(content, {\n ecmaVersion: 'latest',\n sourceType: 'module',\n allowReturnOutsideFunction: true,\n allowImportExportEverywhere: true,\n locations: true,\n });\n } catch {\n // If acorn can't parse the file (e.g. TSX/JSX or TypeScript-specific syntax),\n // fall back to a regex-based scan so we don't miss obvious process.env uses.\n return extractReferencesWithRegex(content, filePath);\n }\n\n fullAncestor(ast as any, (node: any, _state: any, ancestors: any[]) => {\n if (node.type === 'MemberExpression') {\n const ref = extractEnvReference(node, ancestors, filePath, content);\n if (ref) references.push(ref);\n }\n\n if (node.type === 'ObjectPattern') {\n const refs = extractDestructuredEnvReferences(node, ancestors, filePath, content);\n references.push(...refs);\n }\n });\n\n return references;\n}\n\nfunction extractEnvReference(\n node: any,\n ancestors: any[],\n filePath: string,\n content: string\n): EnvReference | undefined {\n const key = getMemberExpressionKey(node);\n if (!key) return undefined;\n\n // fullAncestor includes the current node as the last ancestor\n const parent = ancestors.length > 1 ? ancestors[ancestors.length - 2] : undefined;\n const fallback = detectFallback(node, parent);\n\n return createReference(key, filePath, node, content, fallback);\n}\n\nfunction getMemberExpressionKey(node: any): string | undefined {\n // process.env.X or process.env['X'] (object is process.env)\n if (\n node.object?.type === 'MemberExpression' &&\n node.object.object?.name === 'process' &&\n node.object.property?.name === 'env'\n ) {\n if (node.property?.type === 'Identifier' && !node.computed) return node.property.name;\n if (node.property?.type === 'Literal' && typeof node.property.value === 'string') {\n return node.property.value;\n }\n if (node.property?.type === 'TemplateLiteral' && node.property.quasis?.length === 1) {\n return node.property.quasis[0].value.cooked ?? node.property.quasis[0].value.raw;\n }\n }\n\n // import.meta.env.X (static property access, not computed)\n if (\n node.object?.type === 'MemberExpression' &&\n node.object.object?.type === 'MetaProperty' &&\n node.object.object.meta?.name === 'import' &&\n node.object.object.property?.name === 'meta' &&\n node.object.property?.name === 'env' &&\n !node.computed\n ) {\n if (node.property?.type === 'Identifier') return node.property.name;\n if (node.property?.type === 'Literal' && typeof node.property.value === 'string') {\n return node.property.value;\n }\n }\n\n return undefined;\n}\n\nfunction extractDestructuredEnvReferences(\n node: any,\n ancestors: any[],\n filePath: string,\n content: string\n): EnvReference[] {\n if (!isEnvDestructuringPattern(node, ancestors)) return [];\n\n const references: EnvReference[] = [];\n for (const prop of node.properties) {\n if (prop.type === 'RestElement' && prop.argument?.type === 'Identifier') {\n references.push(\n createReference(prop.argument.name, filePath, node, content, { hasFallback: false })\n );\n continue;\n }\n\n if (prop.type !== 'Property') continue;\n\n if (prop.value?.type === 'Identifier' && prop.key?.type === 'Identifier') {\n // Shorthand: const { MY_VAR } = process.env\n // Renamed: const { MY_VAR: myVar } = process.env\n references.push(\n createReference(prop.key.name, filePath, prop.value, content, { hasFallback: false })\n );\n }\n\n if (prop.value?.type === 'AssignmentPattern' && prop.value.left?.type === 'Identifier' && prop.key?.type === 'Identifier') {\n // Shorthand with default: const { MY_VAR = 'x' } = process.env\n // Renamed with default: const { MY_VAR: myVar = 'x' } = process.env\n const key = prop.key.name;\n const fallbackValue = expressionToString(prop.value.right);\n references.push(\n createReference(key, filePath, prop.value.left, content, {\n hasFallback: true,\n fallbackValue,\n fallbackType: 'literal',\n })\n );\n }\n\n // Nested destructuring: const { env: { X } } = process\n if (prop.value?.type === 'ObjectPattern' && prop.key?.name === 'env') {\n const nested = extractDestructuredEnvReferences(\n prop.value,\n [...ancestors, prop, prop.value],\n filePath,\n content\n );\n references.push(...nested);\n }\n }\n\n return references;\n}\n\nfunction extractReferencesWithRegex(content: string, filePath: string): EnvReference[] {\n const references: EnvReference[] = [];\n const seen = new Set<string>();\n\n const addRef = (key: string, index: number) => {\n if (seen.has(key)) return;\n seen.add(key);\n\n const line = content.slice(0, index).split('\\n').length;\n const dummyNode = { loc: { start: { line, column: 0 } } };\n references.push(\n createReference(key, filePath, dummyNode as any, content, { hasFallback: false })\n );\n };\n\n // process.env.X or import.meta.env.X\n const memberRe = /(?:process|import\\.meta)\\.env\\.([A-Za-z_$][A-Za-z0-9_$]*)/g;\n let match: RegExpExecArray | null;\n while ((match = memberRe.exec(content)) !== null) {\n addRef(match[1], match.index);\n }\n\n // process.env['X'] / process.env[\"X\"] / process.env[`X`]\n const bracketRe = /(?:process|import\\.meta)\\.env\\[(?:'([^']+)'|\"([^\"]+)\"|`([^`]+)`)\\]/g;\n while ((match = bracketRe.exec(content)) !== null) {\n const key = match[1] ?? match[2] ?? match[3];\n if (key) addRef(key, match.index);\n }\n\n // const { X, Y: y, Z = 'd' } = process.env[ || {}]\n const destructRe =\n /(?:const|let|var)\\s*\\{\\s*([^}]+)\\s*\\}\\s*=\\s*(?:process\\.env|import\\.meta\\.env)(?:\\s*(?:\\|\\||\\?\\?)\\s*\\{[^}]*\\})?/g;\n while ((match = destructRe.exec(content)) !== null) {\n const props = match[1].split(',');\n for (const prop of props) {\n const trimmed = prop.trim();\n if (!trimmed) continue;\n const keyMatch = trimmed.match(/^([A-Za-z_$][A-Za-z0-9_$]*)/);\n if (keyMatch) {\n addRef(keyMatch[1], match.index);\n }\n }\n }\n\n return references;\n}\n\nfunction isEnvDestructuringPattern(node: any, ancestors: any[]): boolean {\n // fullAncestor includes the current node as the last ancestor\n const parent = ancestors.length > 1 ? ancestors[ancestors.length - 2] : undefined;\n if (!parent) return false;\n\n // const { X } = process.env\n if (parent.type === 'VariableDeclarator' && parent.id === node) {\n return isEnvSource(parent.init);\n }\n\n // const { X = 'd' } = process.env (AssignmentPattern wrapping ObjectPattern)\n if (parent.type === 'AssignmentPattern' && parent.left === node) {\n return isEnvSource(parent.right);\n }\n\n // const { env: { X } } = process\n if (parent.type === 'Property' && parent.value === node && parent.key?.name === 'env') {\n const grandParent = ancestors.length > 2 ? ancestors[ancestors.length - 3] : undefined;\n if (grandParent?.type === 'ObjectPattern') {\n return isEnvDestructuringPattern(grandParent, ancestors.slice(0, -2));\n }\n }\n\n return false;\n}\n\nfunction isProcessNode(node: any): boolean {\n return node?.type === 'Identifier' && node.name === 'process';\n}\n\nfunction isProcessEnvNode(node: any): boolean {\n if (!node) return false;\n\n // process.env\n if (\n node.type === 'MemberExpression' &&\n node.object?.type === 'Identifier' &&\n node.object.name === 'process' &&\n node.property?.name === 'env'\n ) {\n return true;\n }\n\n return false;\n}\n\nfunction isEnvSource(node: any): boolean {\n if (isProcessEnvNode(node) || isProcessNode(node)) return true;\n\n // const { X } = process.env || {}\n // const { X } = process.env ?? {}\n if (\n node?.type === 'LogicalExpression' &&\n (node.operator === '||' || node.operator === '??')\n ) {\n return isEnvSource(node.left) || isEnvSource(node.right);\n }\n\n // const { X } = condition ? process.env : {}\n if (node?.type === 'ConditionalExpression') {\n return isEnvSource(node.consequent) || isEnvSource(node.alternate);\n }\n\n return false;\n}\n\nfunction detectFallback(node: any, parent: any): FallbackInfo {\n if (!parent) return { hasFallback: false };\n\n if (parent.type === 'LogicalExpression') {\n const isLeft = parent.left === node;\n const isRight = parent.right === node;\n\n if (parent.operator === '||' || parent.operator === '??') {\n const fallbackNode = isLeft ? parent.right : isRight ? parent.left : undefined;\n if (fallbackNode) {\n return {\n hasFallback: true,\n fallbackValue: expressionToString(fallbackNode),\n fallbackType: getFallbackType(fallbackNode),\n };\n }\n }\n\n if (parent.operator === '&&' && isLeft) {\n return { hasFallback: true, fallbackValue: 'guarded', fallbackType: 'expression' };\n }\n }\n\n if (parent.type === 'ConditionalExpression' && parent.test === node) {\n return {\n hasFallback: true,\n fallbackValue: expressionToString(parent.alternate),\n fallbackType: 'ternary',\n };\n }\n\n return { hasFallback: false };\n}\n\nfunction getFallbackType(node: any): 'literal' | 'function' | 'expression' {\n if (node.type === 'Literal' || node.type === 'TemplateLiteral') return 'literal';\n if (node.type === 'CallExpression' || node.type === 'NewExpression') return 'function';\n return 'expression';\n}\n\nfunction expressionToString(node: any): string {\n if (!node) return 'unknown';\n\n if (node.type === 'Literal') {\n return typeof node.value === 'string' ? `'${node.value}'` : String(node.value);\n }\n\n if (node.type === 'Identifier') return node.name;\n\n if (node.type === 'TemplateLiteral') {\n if (node.quasis.length === 1) return `'${node.quasis[0].value.cooked ?? node.quasis[0].value.raw}'`;\n return '`template`';\n }\n\n if (node.type === 'CallExpression' || node.type === 'NewExpression') {\n const callee = node.callee?.name ?? node.callee?.property?.name ?? 'function';\n return `<${callee}()>`;\n }\n\n if (node.type === 'UnaryExpression') {\n return `${node.operator}${expressionToString(node.argument)}`;\n }\n\n if (node.type === 'BinaryExpression') {\n return `${expressionToString(node.left)} ${node.operator} ${expressionToString(node.right)}`;\n }\n\n if (node.type === 'MemberExpression') {\n return node.property?.name ?? 'member';\n }\n\n return '<expression>';\n}\n\ninterface FallbackInfo {\n hasFallback: boolean;\n fallbackValue?: string;\n fallbackType?: 'literal' | 'function' | 'expression' | 'ternary';\n}\n\nfunction createReference(\n key: string,\n filePath: string,\n node: any,\n content: string,\n fallback: FallbackInfo\n): EnvReference {\n const line = node.loc?.start?.line ?? 0;\n const column = node.loc?.start?.column ?? 0;\n const context = extractContext(content, line);\n\n return {\n key,\n file: filePath,\n line,\n column,\n context,\n hasFallback: fallback.hasFallback,\n fallbackValue: fallback.fallbackValue,\n fallbackType: fallback.fallbackType,\n };\n}\n\nfunction extractContext(content: string, line: number): string {\n const lines = content.split('\\n');\n const start = Math.max(0, line - 2);\n const end = Math.min(lines.length, line + 1);\n return lines.slice(start, end).join('\\n');\n}\n","import type { EnvFile, EnvReference, Issue, ScoreResult, Grade } from '../types/index.js';\nimport { isEnvFileGitignored } from '../utils/git.js';\n\nconst ALWAYS_ALLOWED = ['PORT', 'HOST', 'NODE_ENV', 'CI', 'PWD'];\n\nexport async function analyze(\n envFiles: EnvFile[],\n references: EnvReference[],\n projectPath: string\n): Promise<Issue[]> {\n const issues: Issue[] = [];\n\n issues.push(...(await checkMissingVariables(envFiles, references)));\n issues.push(...checkUnusedVariables(envFiles, references));\n issues.push(...checkSecretRisks(envFiles));\n issues.push(...(await checkGitignore(envFiles, projectPath)));\n issues.push(...checkExampleFile(envFiles));\n\n return issues;\n}\n\nasync function checkMissingVariables(envFiles: EnvFile[], references: EnvReference[]): Promise<Issue[]> {\n const definedKeys = new Set(envFiles.flatMap((file) => file.variables.map((v) => v.key)));\n const issues: Issue[] = [];\n\n for (const ref of references) {\n if (definedKeys.has(ref.key)) continue;\n\n if (ref.hasFallback) {\n issues.push({\n type: 'missing',\n severity: 'warning',\n key: ref.key,\n message: `${ref.key} is not defined in .env but has a fallback default: ${ref.fallbackValue ?? 'unknown'}`,\n file: ref.file,\n line: ref.line,\n suggestion: `Consider adding ${ref.key} to .env for explicit configuration, or keep the fallback`,\n hasFallback: true,\n fallbackValue: ref.fallbackValue,\n });\n } else {\n issues.push({\n type: 'missing',\n severity: 'error',\n key: ref.key,\n message: `${ref.key} referenced in ${ref.file}:${ref.line} but not defined in any .env file (no fallback)`,\n file: ref.file,\n line: ref.line,\n suggestion: `Add ${ref.key} to .env and .env.example`,\n });\n }\n }\n\n return issues;\n}\n\nfunction checkUnusedVariables(envFiles: EnvFile[], references: EnvReference[]): Issue[] {\n const referencedKeys = new Set(references.map((r) => r.key));\n const issues: Issue[] = [];\n\n for (const file of envFiles) {\n if (file.filename === '.env.example' || file.filename === '.env.test') continue;\n\n for (const variable of file.variables) {\n if (ALWAYS_ALLOWED.includes(variable.key)) continue;\n if (referencedKeys.has(variable.key)) continue;\n\n issues.push({\n type: 'unused',\n severity: 'warning',\n key: variable.key,\n message: `${variable.key} is defined in ${file.filename} but never used in code`,\n file: file.filename,\n line: variable.line,\n suggestion: 'Remove if no longer needed, or use it in your code',\n });\n }\n }\n\n return issues;\n}\n\nfunction checkSecretRisks(envFiles: EnvFile[]): Issue[] {\n const issues: Issue[] = [];\n\n for (const file of envFiles) {\n if (file.filename !== '.env') continue;\n\n for (const variable of file.variables) {\n if (!variable.isSecret) continue;\n\n issues.push({\n type: 'secret-risk',\n severity: 'error',\n key: variable.key,\n message: `${variable.key} looks like a secret but is in .env instead of .env.local`,\n file: file.filename,\n line: variable.line,\n suggestion: `Move ${variable.key} to .env.local (which should be in .gitignore)`,\n });\n }\n }\n\n return issues;\n}\n\nasync function checkGitignore(envFiles: EnvFile[], projectPath: string): Promise<Issue[]> {\n const issues: Issue[] = [];\n const filenames = envFiles.map((f) => f.filename);\n\n const filesToCheck = ['.env', '.env.local', '.env.production'];\n const messages: Record<string, string> = {\n '.env': '.env is not in .gitignore — risk of committing secrets',\n '.env.local': '.env.local is not in .gitignore',\n '.env.production': '.env.production is not in .gitignore — production secrets at risk',\n };\n const suggestions: Record<string, string> = {\n '.env': \"echo '.env' >> .gitignore\",\n '.env.local': \"echo '.env.local' >> .gitignore\",\n '.env.production': \"echo '.env.production' >> .gitignore\",\n };\n\n for (const filename of filesToCheck) {\n if (!filenames.includes(filename)) continue;\n const ignored = await isEnvFileGitignored(projectPath, filename);\n if (!ignored) {\n issues.push({\n type: 'gitignore',\n severity: 'error',\n key: filename,\n message: messages[filename],\n file: '.gitignore',\n suggestion: suggestions[filename],\n });\n }\n }\n\n return issues;\n}\n\nfunction checkExampleFile(envFiles: EnvFile[]): Issue[] {\n const issues: Issue[] = [];\n const exampleFile = envFiles.find((f) => f.filename === '.env.example');\n const envFile = envFiles.find((f) => f.filename === '.env');\n\n if (!exampleFile) {\n issues.push({\n type: 'suspicious',\n severity: 'warning',\n key: '.env.example',\n message: \".env.example is missing — new developers won't know what env vars are needed\",\n file: '.',\n suggestion: 'Run `enve generate-example` to create one',\n });\n return issues;\n }\n\n if (!envFile) return issues;\n\n const exampleKeys = new Set(exampleFile.variables.map((v) => v.key));\n for (const variable of envFile.variables) {\n if (exampleKeys.has(variable.key)) continue;\n\n issues.push({\n type: 'suspicious',\n severity: 'warning',\n key: variable.key,\n message: `${variable.key} is in .env but missing from .env.example`,\n file: '.env.example',\n suggestion: `Add ${variable.key}=your_${variable.key} to .env.example`,\n });\n }\n\n return issues;\n}\n\nexport function calculateScore(issues: Issue[]): ScoreResult {\n let score = 100;\n let errorCount = 0;\n let warningCount = 0;\n let infoCount = 0;\n let unusedDeduction = 0;\n let exampleDeduction = 0;\n\n for (const issue of issues) {\n switch (issue.severity) {\n case 'error':\n errorCount++;\n break;\n case 'warning':\n warningCount++;\n break;\n case 'info':\n infoCount++;\n break;\n }\n\n switch (issue.type) {\n case 'missing':\n score -= issue.hasFallback ? 3 : 10;\n break;\n case 'secret-risk':\n score -= 8;\n break;\n case 'gitignore':\n if (issue.key === '.env.production') score -= 25;\n else score -= 20;\n break;\n case 'syntax-error':\n score -= 15;\n break;\n case 'invalid':\n score -= 5;\n break;\n case 'unused':\n unusedDeduction = Math.min(unusedDeduction + 3, 15);\n break;\n case 'suspicious':\n if (issue.key === '.env.example') score -= 10;\n else exampleDeduction = Math.min(exampleDeduction + 2, 10);\n break;\n }\n }\n\n score -= unusedDeduction + exampleDeduction;\n score = Math.max(0, Math.min(100, score));\n\n return {\n score,\n grade: getGrade(score),\n totalIssues: issues.length,\n errorCount,\n warningCount,\n infoCount,\n };\n}\n\nfunction getGrade(score: number): Grade {\n if (score >= 90) return 'Excellent';\n if (score >= 70) return 'Good';\n if (score >= 50) return 'Needs attention';\n return 'Critical';\n}\n","import Conf from 'conf';\nimport { readFile } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport type { EnveConfig, TrackedProject } from '../types/index.js';\n\nconst DEFAULT_CONFIG: EnveConfig = {\n version: 1,\n projects: [],\n};\n\nfunction createConfig(): Conf<EnveConfig> {\n return new Conf<EnveConfig>({\n projectName: 'enve',\n defaults: DEFAULT_CONFIG,\n });\n}\n\nexport function getProjects(): TrackedProject[] {\n const config = createConfig();\n return config.get('projects', []);\n}\n\nexport function isTracked(projectPath: string): boolean {\n return getProjects().some((p) => p.path === projectPath);\n}\n\nexport function getProject(projectPath: string): TrackedProject | undefined {\n return getProjects().find((p) => p.path === projectPath);\n}\n\nexport async function addProject(projectPath: string): Promise<TrackedProject> {\n const config = createConfig();\n\n if (isTracked(projectPath)) {\n return getProject(projectPath)!;\n }\n\n const name = await detectProjectName(projectPath);\n const project: TrackedProject = {\n name,\n path: projectPath,\n addedAt: new Date().toISOString(),\n };\n\n const projects = config.get('projects', []);\n config.set('projects', [...projects, project]);\n\n return project;\n}\n\nexport function removeProject(projectPath: string): boolean {\n const config = createConfig();\n const projects = config.get('projects', []);\n const before = projects.length;\n const filtered = projects.filter((p) => p.path !== projectPath);\n\n if (filtered.length === before) return false;\n\n config.set('projects', filtered);\n return true;\n}\n\nasync function detectProjectName(projectPath: string): Promise<string> {\n try {\n const pkgRaw = await readFile(join(projectPath, 'package.json'), 'utf-8');\n const pkg = JSON.parse(pkgRaw) as { name?: string };\n if (pkg.name) return pkg.name;\n } catch {\n // fall through to directory name\n }\n return projectPath.split('/').pop() ?? 'unknown';\n}\n","import chalk from 'chalk';\n\nexport const symbols = {\n error: '✗',\n warning: '⚠',\n success: '✓',\n info: 'ℹ',\n bullet: '•',\n arrow: '→',\n pointer: '▸',\n};\n\nexport function error(message: string): string {\n return `${chalk.red(symbols.error)} ${message}`;\n}\n\nexport function warning(message: string): string {\n return `${chalk.yellow(symbols.warning)} ${message}`;\n}\n\nexport function success(message: string): string {\n return `${chalk.green(symbols.success)} ${message}`;\n}\n\nexport function info(message: string): string {\n return `${chalk.blue(symbols.info)} ${message}`;\n}\n\nexport function bullet(message: string): string {\n return ` ${symbols.bullet} ${message}`;\n}\n\nexport function indent(message: string, level = 1): string {\n return ' '.repeat(level) + message;\n}\n\nexport function header(title: string): string {\n return chalk.bold(title);\n}\n\nexport function dim(message: string): string {\n return chalk.dim(message);\n}\n","import { Command } from 'commander';\nimport chalk from 'chalk';\nimport { detectProject, getProjectInfo } from '../../core/project.js';\nimport { parseAllEnvFiles } from '../../core/parser.js';\nimport { error, warning, success, indent, header } from '../../utils/logger.js';\nimport type { Issue } from '../../types/index.js';\n\nexport const doctorCommand = new Command('doctor')\n .description('Comprehensive health check with detailed recommendations')\n .action(async () => {\n const cwd = process.cwd();\n const projectPath = await detectProject(cwd);\n\n if (!projectPath) {\n console.log(error('No package.json found. Are you in a project directory?'));\n process.exit(1);\n }\n\n await runDoctor(projectPath);\n });\n\nexport async function runDoctor(projectPath: string): Promise<void> {\n const info = await getProjectInfo(projectPath);\n const envFiles = await parseAllEnvFiles(projectPath);\n\n console.log();\n console.log(indent(header(`Enve Health Check — ${info.name}`)));\n console.log(indent(chalk.dim(info.path)));\n console.log();\n\n const fileStructureIssues = info.issues.filter(\n (i) => i.type === 'gitignore' || (i.type === 'suspicious' && i.key === '.env.example')\n );\n const variableIssues = info.issues.filter((i) => i.type === 'missing' || i.type === 'unused');\n const securityIssues = info.issues.filter((i) => i.type === 'secret-risk');\n const validationIssues = info.issues.filter((i) => i.type === 'invalid' || i.type === 'syntax-error');\n\n printSection('[File Structure]', fileStructureIssues);\n printVariablesSection(variableIssues, envFiles);\n printSection('[Security]', securityIssues);\n printSection('[Validation]', validationIssues);\n printRecommendations(info.issues);\n\n console.log();\n console.log(indent(`Score: ${formatScore(info.score.score)} [ ${info.score.grade} ]`));\n console.log(indent(chalk.dim(`${info.score.errorCount} errors, ${info.score.warningCount} warnings, ${info.score.infoCount} info`)));\n console.log();\n}\n\nfunction printSection(title: string, issues: Issue[]): void {\n console.log(indent(header(title)));\n if (issues.length === 0) {\n console.log(indent(success('No issues')));\n } else {\n for (const issue of issues) {\n const formatter = issue.severity === 'error' ? error : issue.severity === 'warning' ? warning : success;\n console.log(indent(formatter(issue.message), 2));\n if (issue.suggestion) {\n console.log(indent(chalk.dim(`→ ${issue.suggestion}`), 3));\n }\n }\n }\n console.log();\n}\n\nfunction printVariablesSection(issues: Issue[], envFiles: Awaited<ReturnType<typeof import('../../core/parser.js').parseAllEnvFiles>>): void {\n const totalDefined = envFiles.reduce((sum, f) => sum + f.variables.length, 0);\n console.log(indent(header(`[Variables — ${totalDefined} defined]`)));\n\n if (issues.length === 0) {\n console.log(indent(success('All variables are used and defined')));\n } else {\n for (const issue of issues) {\n const formatter = issue.severity === 'error' ? error : warning;\n let message = issue.message;\n if (issue.type === 'missing' && issue.hasFallback && issue.fallbackValue) {\n message += chalk.dim(` (fallback: ${issue.fallbackValue})`);\n }\n console.log(indent(formatter(message), 2));\n if (issue.suggestion) {\n console.log(indent(chalk.dim(`→ ${issue.suggestion}`), 3));\n }\n }\n }\n console.log();\n}\n\nfunction printRecommendations(issues: Issue[]): void {\n const actionable = issues.filter((i) => i.suggestion);\n if (actionable.length === 0) return;\n\n console.log(indent(header(`[Recommendations — ${actionable.length} actions needed]`)));\n\n const high = actionable.filter((i) => i.severity === 'error');\n const medium = actionable.filter((i) => i.severity === 'warning');\n const low = actionable.filter((i) => i.severity === 'info');\n\n if (high.length > 0) {\n console.log(indent(chalk.red.bold('HIGH PRIORITY:')));\n high.forEach((issue, idx) => {\n console.log(indent(`${idx + 1}. ${issue.suggestion}`, 2));\n });\n console.log();\n }\n\n if (medium.length > 0) {\n console.log(indent(chalk.yellow.bold('MEDIUM PRIORITY:')));\n medium.forEach((issue, idx) => {\n console.log(indent(`${idx + 1}. ${issue.suggestion}`, 2));\n });\n console.log();\n }\n\n if (low.length > 0) {\n console.log(indent(chalk.blue.bold('LOW PRIORITY:')));\n low.forEach((issue, idx) => {\n console.log(indent(`${idx + 1}. ${issue.suggestion}`, 2));\n });\n console.log();\n }\n}\n\nfunction formatScore(score: number): string {\n const text = `${score}/100`;\n if (score >= 90) return chalk.bgGreen.black(` ${text} `);\n if (score >= 70) return chalk.bgYellow.black(` ${text} `);\n if (score >= 50) return chalk.red(text);\n return chalk.bgRed.white(` ${text} `);\n}\n","import { Command } from 'commander';\nimport chalk from 'chalk';\nimport { detectProject } from '../../core/project.js';\nimport { parseAllEnvFiles } from '../../core/parser.js';\nimport { scanProject } from '../../core/scanner.js';\nimport { readFileSafe, writeFileSafe } from '../../utils/fs.js';\nimport { copyFile } from 'node:fs/promises';\nimport { error, warning, success, indent, header } from '../../utils/logger.js';\nimport { confirm } from '../../utils/prompt.js';\n\nexport const unusedCommand = new Command('unused')\n .description('Find environment variables defined in .env files but never referenced in code')\n .option('--fix', 'Remove unused variables from .env files')\n .option('--dry-run', 'Show what would be removed without making changes')\n .option('--include-commons', 'Also flag PORT, HOST, NODE_ENV if truly unused')\n .action(async (options) => {\n const cwd = process.cwd();\n const projectPath = await detectProject(cwd);\n\n if (!projectPath) {\n console.log(error('No package.json found. Are you in a project directory?'));\n process.exit(1);\n }\n\n const envFiles = await parseAllEnvFiles(projectPath);\n const references = await scanProject(projectPath);\n const referencedKeys = new Set(references.map((r) => r.key));\n\n const alwaysAllowed = options.includeCommons ? [] : ['PORT', 'HOST', 'NODE_ENV', 'CI', 'PWD'];\n\n type UnusedItem = { filename: string; path: string; key: string; line: number };\n const unused: UnusedItem[] = [];\n\n for (const file of envFiles) {\n if (file.filename === '.env.example' || file.filename === '.env.test') continue;\n for (const variable of file.variables) {\n if (alwaysAllowed.includes(variable.key)) continue;\n if (!referencedKeys.has(variable.key)) {\n unused.push({\n filename: file.filename,\n path: file.path,\n key: variable.key,\n line: variable.line,\n });\n }\n }\n }\n\n console.log();\n console.log(indent(header(`Unused variables in ${projectPath}`)));\n console.log();\n\n if (unused.length === 0) {\n console.log(indent(success('All variables are used')));\n return;\n }\n\n // Group by file\n const byFile = new Map<string, UnusedItem[]>();\n for (const item of unused) {\n byFile.set(item.filename, [...(byFile.get(item.filename) ?? []), item]);\n }\n\n for (const [filename, items] of byFile) {\n console.log(indent(filename));\n for (const item of items) {\n console.log(indent(warning(`${item.key.padEnd(20)} (line ${item.line})`), 2));\n }\n }\n\n console.log();\n console.log(indent(`${unused.length} unused variables found. They may be safe to remove.`));\n\n if (options.dryRun) {\n console.log(indent(chalk.dim('Dry run: no changes made.')));\n return;\n }\n\n if (!options.fix) {\n console.log(indent('Run `enve unused --fix` to remove them, or `--dry-run` to preview.'));\n return;\n }\n\n const confirmed = await confirm(`Remove ${unused.length} unused variables from .env files?`);\n if (!confirmed) {\n console.log(indent(chalk.dim('Aborted. No changes made.')));\n return;\n }\n\n // Group by file path for removal\n const byPath = new Map<string, UnusedItem[]>();\n for (const item of unused) {\n byPath.set(item.path, [...(byPath.get(item.path) ?? []), item]);\n }\n\n let removedCount = 0;\n for (const [filePath, items] of byPath) {\n const backupPath = `${filePath}.backup.${formatTimestamp(new Date())}`;\n await copyFile(filePath, backupPath);\n\n const content = (await readFileSafe(filePath)) ?? '';\n const lines = content.split('\\n');\n const linesToRemove = new Set(items.map((i) => i.line - 1));\n const newLines = lines.filter((_, idx) => !linesToRemove.has(idx));\n await writeFileSafe(filePath, newLines.join('\\n'));\n\n removedCount += items.length;\n console.log(indent(chalk.dim(`Backup saved: ${backupPath}`), 2));\n }\n\n console.log();\n console.log(indent(success(`Removed ${removedCount} unused variables.`)));\n });\n\nfunction formatTimestamp(date: Date): string {\n return date.toISOString().replace(/[:.]/g, '-').slice(0, 19);\n}\n","import { createInterface } from 'node:readline/promises';\n\nexport async function prompt(message: string): Promise<string> {\n const rl = createInterface({\n input: process.stdin,\n output: process.stdout,\n });\n\n try {\n const answer = await rl.question(`${message} `);\n return answer.trim();\n } finally {\n rl.close();\n }\n}\n\nexport async function confirm(message: string, defaultValue = false): Promise<boolean> {\n const suffix = defaultValue ? ' (Y/n)' : ' (y/N)';\n const answer = await prompt(`${message}${suffix}`);\n if (!answer) return defaultValue;\n return /^y(es)?$/i.test(answer);\n}\n","import { Command } from 'commander';\nimport chalk from 'chalk';\nimport { join } from 'node:path';\nimport { detectProject } from '../../core/project.js';\nimport { parseAllEnvFiles } from '../../core/parser.js';\nimport { scanProject } from '../../core/scanner.js';\nimport { readFileSafe, writeFileSafe } from '../../utils/fs.js';\nimport { error, warning, success, indent, header } from '../../utils/logger.js';\nimport { prompt, confirm } from '../../utils/prompt.js';\n\nconst STANDARD_NODE_VARS = new Set([\n 'PATH',\n 'HOME',\n 'USER',\n 'SHELL',\n 'PWD',\n 'OLDPWD',\n 'NODE',\n 'NODE_PATH',\n 'TMPDIR',\n 'LANG',\n 'LOGNAME',\n 'TERM',\n 'EDITOR',\n 'SHLVL',\n '_',\n '__CF_USER_TEXT_ENCODING',\n]);\n\nexport const missingCommand = new Command('missing')\n .description('Find process.env references in code that are not defined in any .env file')\n .option('--include-fallbacks', 'Also show variables that have fallback defaults')\n .option('--no-fallbacks', 'Hide variables with fallback defaults')\n .option('--add', 'Interactively add missing variables to .env')\n .action(async (options) => {\n const cwd = process.cwd();\n const projectPath = await detectProject(cwd);\n\n if (!projectPath) {\n console.log(error('No package.json found. Are you in a project directory?'));\n process.exit(1);\n }\n\n const envFiles = await parseAllEnvFiles(projectPath);\n const references = await scanProject(projectPath);\n const definedKeys = new Set(envFiles.flatMap((f) => f.variables.map((v) => v.key)));\n\n const missingNoFallback = references.filter(\n (r) => !definedKeys.has(r.key) && !r.hasFallback && !STANDARD_NODE_VARS.has(r.key)\n );\n const missingWithFallback = options.fallbacks\n ? references.filter((r) => !definedKeys.has(r.key) && r.hasFallback && !STANDARD_NODE_VARS.has(r.key))\n : [];\n\n console.log();\n console.log(indent(header(`Missing environment variables in ${projectPath}`)));\n console.log();\n\n if (missingNoFallback.length === 0 && missingWithFallback.length === 0) {\n console.log(indent(success('All referenced variables are defined')));\n return;\n }\n\n if (missingNoFallback.length > 0) {\n console.log(indent(header('Missing (no fallback — WILL cause runtime errors)')));\n for (const ref of missingNoFallback) {\n console.log(indent(error(`${ref.key}`), 2));\n console.log(indent(chalk.dim(`→ ${ref.file}:${ref.line}`), 3));\n if (ref.context) {\n console.log(indent(chalk.dim(ref.context.split('\\n').map((l) => ` ${l}`).join('\\n')), 3));\n }\n }\n console.log();\n }\n\n if (missingWithFallback.length > 0) {\n console.log(indent(header('Missing (has fallback — will use default value)')));\n for (const ref of missingWithFallback) {\n console.log(indent(warning(`${ref.key}`), 2));\n console.log(indent(chalk.dim(`→ fallback: ${ref.fallbackValue ?? 'unknown'}`), 3));\n console.log(indent(chalk.dim(`→ ${ref.file}:${ref.line}`), 3));\n }\n console.log();\n }\n\n console.log(\n indent(\n `${error(`${missingNoFallback.length} truly missing`)} · ${warning(\n `${missingWithFallback.length} with fallback defaults`\n )}`\n )\n );\n\n if (options.add) {\n await addMissingVariables(projectPath, envFiles, missingNoFallback);\n } else {\n console.log();\n console.log(indent('Run with `--add` to interactively add missing variables to .env'));\n }\n });\n\nasync function addMissingVariables(\n projectPath: string,\n envFiles: Awaited<ReturnType<typeof parseAllEnvFiles>>,\n missing: Awaited<ReturnType<typeof scanProject>>\n): Promise<void> {\n const envPath = join(projectPath, '.env');\n const envFile = envFiles.find((f) => f.filename === '.env');\n\n if (!envFile) {\n console.log(error('No .env file found. Cannot add variables.'));\n return;\n }\n\n const updates: string[] = [];\n const exampleUpdates: string[] = [];\n\n for (const ref of missing) {\n const value = await prompt(`Value for ${ref.key}:`);\n if (!value) continue;\n\n updates.push(`${ref.key}=${value}`);\n\n const addToExample = await confirm(`Add ${ref.key} to .env.example?`, false);\n if (addToExample) {\n exampleUpdates.push(`${ref.key}=your_${ref.key.toLowerCase()}`);\n }\n }\n\n if (updates.length === 0) {\n console.log(indent(chalk.dim('No variables added.')));\n return;\n }\n\n const existing = (await readFileSafe(envPath)) ?? '';\n const separator = existing.endsWith('\\n') || existing === '' ? '' : '\\n';\n await writeFileSafe(envPath, `${existing}${separator}${updates.join('\\n')}\\n`);\n\n if (exampleUpdates.length > 0) {\n const examplePath = join(projectPath, '.env.example');\n const exampleExisting = (await readFileSafe(examplePath)) ?? '';\n const exampleSeparator = exampleExisting.endsWith('\\n') || exampleExisting === '' ? '' : '\\n';\n await writeFileSafe(examplePath, `${exampleExisting}${exampleSeparator}${exampleUpdates.join('\\n')}\\n`);\n }\n\n console.log();\n console.log(indent(success(`Added ${updates.length} variables to .env`)));\n if (exampleUpdates.length > 0) {\n console.log(indent(success(`Added ${exampleUpdates.length} placeholders to .env.example`)));\n }\n}\n","import { Command } from 'commander';\nimport chalk from 'chalk';\nimport { detectProject } from '../../core/project.js';\nimport { parseAllEnvFiles } from '../../core/parser.js';\nimport {\n validatePort,\n validateUrl,\n validateNodeEnv,\n validateSecret,\n validateBoolean,\n} from '../../utils/validators.js';\nimport { readFileSafe, writeFileSafe } from '../../utils/fs.js';\nimport { copyFile } from 'node:fs/promises';\nimport { error, success, indent, header } from '../../utils/logger.js';\nimport { confirm } from '../../utils/prompt.js';\nimport type { EnvVariable } from '../../types/index.js';\n\nexport const validateCommand = new Command('validate')\n .description('Validate the values of environment variables against expected formats')\n .option('--fix', 'Automatically fix simple issues')\n .action(async (options) => {\n const cwd = process.cwd();\n const projectPath = await detectProject(cwd);\n\n if (!projectPath) {\n console.log(error('No package.json found. Are you in a project directory?'));\n process.exit(1);\n }\n\n const envFiles = await parseAllEnvFiles(projectPath);\n const envFile = envFiles.find((f) => f.filename === '.env');\n\n if (!envFile) {\n console.log(error('No .env file found. Cannot validate.'));\n process.exit(1);\n }\n\n const variables = envFile.variables;\n\n console.log();\n console.log(indent(header(`Validating environment variables — ${envFile.filename}`)));\n console.log();\n\n let errorCount = 0;\n let warningCount = 0;\n const fixes: Array<{ key: string; oldValue: string; newValue: string }> = [];\n\n for (const variable of variables) {\n const result = validateVariable(variable);\n if (result.valid) {\n console.log(indent(success(`${variable.key}=${truncate(variable.value)}`)));\n } else {\n errorCount++;\n console.log(indent(error(`${variable.key}=${truncate(variable.value)}`)));\n if (result.message) console.log(indent(chalk.dim(` ${result.message}`), 2));\n if (result.suggestion) console.log(indent(chalk.dim(` Suggestion: ${result.suggestion}`), 2));\n\n const fixedValue = getFixedValue(variable);\n if (fixedValue !== undefined) {\n console.log(indent(chalk.yellow(` Auto-fix available: ${variable.key}=${fixedValue}`), 2));\n fixes.push({ key: variable.key, oldValue: variable.value, newValue: fixedValue });\n }\n }\n }\n\n console.log();\n console.log(indent(`${variables.length} variables checked, ${errorCount} errors, ${warningCount} warnings`));\n\n if (options.fix) {\n const safeFixes = fixes.filter((f) => !isSecretKey(f.key));\n if (safeFixes.length === 0) {\n console.log(indent(chalk.dim('No auto-fixable issues (secrets are never auto-fixed).')));\n return;\n }\n\n const confirmed = await confirm(`Apply ${safeFixes.length} auto-fixes to .env?`);\n if (!confirmed) {\n console.log(indent(chalk.dim('Aborted. No changes made.')));\n return;\n }\n\n await applyFixes(envFile.path, safeFixes);\n console.log();\n console.log(indent(success(`Applied ${safeFixes.length} fixes to .env`)));\n } else if (fixes.length > 0) {\n console.log(indent('Run with `--fix` to apply available auto-fixes.'));\n }\n });\n\nexport function validateVariable(variable: EnvVariable): { valid: boolean; message?: string; suggestion?: string } {\n const key = variable.key;\n const value = variable.value;\n\n if (key === 'PORT' || key.endsWith('_PORT')) {\n return validatePort(value);\n }\n\n if (key === 'NODE_ENV') {\n return validateNodeEnv(value);\n }\n\n if (key.endsWith('_URL') || key === 'DATABASE_URL' || key === 'REDIS_URL') {\n return validateUrl(value);\n }\n\n if (isSecretKey(key)) {\n return validateSecret(value, key);\n }\n\n if (key === 'DEBUG' || key === 'VERBOSE' || key.endsWith('_ENABLED')) {\n return validateBoolean(value, key);\n }\n\n return { valid: true };\n}\n\nfunction isSecretKey(key: string): boolean {\n return /SECRET|KEY|TOKEN|PRIVATE/i.test(key) && key !== 'API_KEY';\n}\n\nfunction getFixedValue(variable: EnvVariable): string | undefined {\n const key = variable.key;\n const value = variable.value;\n\n // URL missing protocol\n if (key.endsWith('_URL') || key === 'DATABASE_URL' || key === 'REDIS_URL') {\n if (!value.includes('://') && !value.startsWith('http')) {\n return `https://${value}`;\n }\n }\n\n // NODE_ENV shorthand\n if (key === 'NODE_ENV') {\n if (value === 'dev') return 'development';\n if (value === 'prod') return 'production';\n }\n\n // Boolean normalization\n if (key === 'DEBUG' || key === 'VERBOSE' || key.endsWith('_ENABLED')) {\n const normalized = value.toLowerCase();\n if (normalized === 'yes' || normalized === '1' || normalized === 'on') return 'true';\n if (normalized === 'no' || normalized === '0' || normalized === 'off') return 'false';\n }\n\n return undefined;\n}\n\nasync function applyFixes(filePath: string, fixes: Array<{ key: string; newValue: string }>): Promise<void> {\n const backupPath = `${filePath}.backup.validate.${formatTimestamp(new Date())}`;\n await copyFile(filePath, backupPath);\n\n const content = (await readFileSafe(filePath)) ?? '';\n const fixMap = new Map(fixes.map((f) => [f.key, f.newValue]));\n\n const lines = content.split('\\n').map((line) => {\n const trimmed = line.trim();\n if (!trimmed || trimmed.startsWith('#')) return line;\n\n const equalIndex = trimmed.indexOf('=');\n if (equalIndex === -1) return line;\n\n let key = trimmed.slice(0, equalIndex).trim();\n if (key.startsWith('export ')) key = key.slice(7).trim();\n\n if (fixMap.has(key)) {\n const prefix = line.match(/^\\s*/)?.[0] ?? '';\n const suffix = line.slice(line.indexOf('=') + 1);\n const commentIndex = suffix.indexOf(' #');\n const comment = commentIndex !== -1 ? suffix.slice(commentIndex) : '';\n const rawValue = commentIndex !== -1 ? suffix.slice(0, commentIndex) : suffix;\n const leadingSpaces = rawValue.match(/^\\s*/)?.[0] ?? '';\n return `${prefix}${key}=${leadingSpaces}${fixMap.get(key)}${comment}`;\n }\n\n return line;\n });\n\n await writeFileSafe(filePath, lines.join('\\n'));\n console.log(indent(chalk.dim(`Backup saved: ${backupPath}`), 2));\n}\n\nfunction formatTimestamp(date: Date): string {\n return date.toISOString().replace(/[:.]/g, '-').slice(0, 19);\n}\n\nfunction truncate(value: string, max = 40): string {\n if (value.length <= max) return value;\n return value.slice(0, max) + '...';\n}\n","export interface ValidationResult {\n valid: boolean;\n message?: string;\n suggestion?: string;\n}\n\nexport function validatePort(value: string): ValidationResult {\n const port = Number(value);\n if (!Number.isInteger(port) || port < 1 || port > 65535) {\n return {\n valid: false,\n message: `PORT must be a number between 1 and 65535`,\n suggestion: `PORT=3000`,\n };\n }\n return { valid: true };\n}\n\nexport function validateUrl(value: string): ValidationResult {\n if (!value.includes('://')) {\n return {\n valid: false,\n message: 'Invalid URL — missing protocol',\n suggestion: `Did you mean https://${value} or a protocol-specific URL?`,\n };\n }\n\n try {\n const url = new URL(value);\n if (!url.protocol || url.protocol === ':') {\n throw new Error('Missing protocol');\n }\n return { valid: true };\n } catch {\n return {\n valid: false,\n message: 'Invalid URL',\n suggestion: `Check the URL format`,\n };\n }\n}\n\nexport function validateNodeEnv(value: string): ValidationResult {\n const allowed = ['development', 'production', 'test'];\n if (!allowed.includes(value)) {\n return {\n valid: false,\n message: `NODE_ENV should be 'development', 'production', or 'test'`,\n suggestion: `NODE_ENV=${value === 'dev' ? 'development' : value === 'prod' ? 'production' : 'development'}`,\n };\n }\n return { valid: true };\n}\n\nexport function validateSecret(value: string, key: string): ValidationResult {\n const forbidden = ['default', 'secret', 'test', 'password', '123456'];\n if (value.length < 16) {\n return {\n valid: false,\n message: `${key} is too short (${value.length} characters)`,\n suggestion: `Use at least 16 random characters. Generate one: node -e \"console.log(require('crypto').randomBytes(32).toString('hex'))\"`,\n };\n }\n if (forbidden.includes(value.toLowerCase())) {\n return {\n valid: false,\n message: `${key} uses a weak placeholder value`,\n suggestion: `Generate a strong random secret`,\n };\n }\n return { valid: true };\n}\n\nexport function validateBoolean(value: string, key: string): ValidationResult {\n const normalized = value.toLowerCase();\n if (normalized === 'true' || normalized === 'false') {\n return { valid: true };\n }\n const boolMap: Record<string, string> = {\n yes: 'true',\n no: 'false',\n '1': 'true',\n '0': 'false',\n on: 'true',\n off: 'false',\n };\n if (normalized in boolMap) {\n return {\n valid: false,\n message: `${key} should be 'true' or 'false' for boolean values`,\n suggestion: `${key}=${boolMap[normalized]}`,\n };\n }\n return {\n valid: false,\n message: `${key} is not a valid boolean value`,\n suggestion: `${key}=true`,\n };\n}\n\nexport function isSecretKey(key: string): boolean {\n const secretPatterns = /SECRET|KEY|TOKEN|PASSWORD|PRIVATE|ACCESS|CREDENTIAL|AUTH/i;\n return secretPatterns.test(key);\n}\n","import { Command } from 'commander';\nimport chalk from 'chalk';\nimport { join } from 'node:path';\nimport { detectProject } from '../../core/project.js';\nimport { parseEnvFile } from '../../core/parser.js';\nimport { readFileSafe, writeFileSafe } from '../../utils/fs.js';\nimport { isSecretKey } from '../../utils/validators.js';\nimport { error, success, indent, header, bullet } from '../../utils/logger.js';\nimport { confirm } from '../../utils/prompt.js';\n\nexport const generateExampleCommand = new Command('generate-example')\n .description('Automatically create or update .env.example from the current .env file')\n .option('--overwrite', 'Update existing .env.example without prompting')\n .option('--output <path>', 'Write to a different file', '.env.example')\n .option('--all-secrets', 'Redact ALL values')\n .option('--dry-run', 'Show what would be generated without writing files')\n .action(async (options) => {\n const cwd = process.cwd();\n const projectPath = await detectProject(cwd);\n\n if (!projectPath) {\n console.log(error('No package.json found. Are you in a project directory?'));\n process.exit(1);\n }\n\n const envPath = join(projectPath, '.env');\n const envContent = await readFileSafe(envPath);\n\n if (!envContent) {\n console.log(error('No .env file found. Cannot generate .env.example.'));\n process.exit(1);\n }\n\n const outputPath = join(projectPath, options.output);\n const existing = await readFileSafe(outputPath);\n const variables = parseEnvFile(envPath);\n\n const newLines: string[] = [];\n let redactedCount = 0;\n let copiedCount = 0;\n\n for (const variable of variables) {\n const shouldRedact = options.allSecrets || variable.isSecret || isSecretKey(variable.key);\n if (shouldRedact) {\n newLines.push(`${variable.key}=your_${variable.key.toLowerCase()}`);\n redactedCount++;\n } else {\n newLines.push(`${variable.key}=${variable.value}`);\n copiedCount++;\n }\n }\n\n const newContent = `${newLines.join('\\n')}\\n`;\n\n console.log();\n console.log(indent(header(`Generating ${options.output} from .env`)));\n console.log();\n console.log(indent(`Variables: ${variables.length} total`));\n console.log(indent(bullet(`${copiedCount} public variables → copied as-is`), 2));\n console.log(indent(bullet(`${redactedCount} secrets redacted`), 2));\n\n if (existing) {\n const diff = computeDiff(existing, newContent);\n\n if (diff.length === 0) {\n console.log();\n console.log(indent(success(`${options.output} is already up to date.`)));\n return;\n }\n\n console.log();\n console.log(indent(header('Changes since last generation:')));\n for (const change of diff) {\n const symbol = change.type === 'added' ? chalk.green('+') : change.type === 'removed' ? chalk.red('-') : chalk.yellow('~');\n console.log(indent(`${symbol} ${change.type}: ${change.key}`, 2));\n }\n\n if (options.dryRun) {\n console.log();\n console.log(indent(chalk.dim('Dry run — no changes made.')));\n return;\n }\n\n if (!options.overwrite) {\n const confirmed = await confirm(`Update ${options.output}?`);\n if (!confirmed) {\n console.log(indent(chalk.dim('Aborted. No changes made.')));\n return;\n }\n }\n } else if (options.dryRun) {\n console.log();\n console.log(indent(chalk.dim('Dry run — no changes made.')));\n }\n\n if (!options.dryRun) {\n await writeFileSafe(outputPath, newContent);\n\n console.log();\n console.log(indent(success(`Created ${options.output}`)));\n }\n });\n\ninterface DiffEntry {\n type: 'added' | 'removed' | 'updated';\n key: string;\n}\n\nfunction computeDiff(existingContent: string, newContent: string): DiffEntry[] {\n const existingVars = new Map<string, string>();\n const newVars = new Map<string, string>();\n\n for (const line of existingContent.split('\\n')) {\n const trimmed = line.trim();\n if (!trimmed || trimmed.startsWith('#')) continue;\n const idx = trimmed.indexOf('=');\n if (idx === -1) continue;\n existingVars.set(trimmed.slice(0, idx).trim(), trimmed.slice(idx + 1).trim());\n }\n\n for (const line of newContent.split('\\n')) {\n const trimmed = line.trim();\n if (!trimmed || trimmed.startsWith('#')) continue;\n const idx = trimmed.indexOf('=');\n if (idx === -1) continue;\n newVars.set(trimmed.slice(0, idx).trim(), trimmed.slice(idx + 1).trim());\n }\n\n const diff: DiffEntry[] = [];\n\n for (const [key, value] of newVars) {\n if (!existingVars.has(key)) {\n diff.push({ type: 'added', key });\n } else if (existingVars.get(key) !== value) {\n diff.push({ type: 'updated', key });\n }\n }\n\n for (const key of existingVars.keys()) {\n if (!newVars.has(key)) {\n diff.push({ type: 'removed', key });\n }\n }\n\n return diff;\n}\n","import { Command } from 'commander';\nimport { detectProject } from '../../core/project.js';\nimport {\n isGitRepo,\n isHookInstalled,\n installHook,\n uninstallHook,\n getPreCommitHookPath,\n} from '../../utils/git.js';\nimport { error, warning, success, indent } from '../../utils/logger.js';\n\nexport const hookCommand = new Command('hook')\n .description('Install, manage, and check a pre-commit git hook that prevents .env commits')\n .addCommand(\n new Command('install')\n .description('Install the enve pre-commit hook')\n .action(async () => {\n const cwd = process.cwd();\n const projectPath = await detectProject(cwd);\n\n if (!projectPath || !(await isGitRepo(projectPath))) {\n console.log(error('Not a git repository. Run `git init` first.'));\n process.exit(1);\n }\n\n await installHook(projectPath);\n console.log();\n console.log(indent(success('Pre-commit hook installed')));\n console.log(indent(`Location: ${getPreCommitHookPath(projectPath)}`));\n console.log();\n console.log(indent('What it does:'));\n console.log(indent(bullet('Blocks commits of .env, .env.local, .env.production files'), 2));\n console.log(indent(bullet('Shows a helpful message with next steps'), 2));\n console.log(indent(bullet('Can be bypassed with --no-verify (emergency only)'), 2));\n console.log();\n console.log(indent('To remove: enve hook uninstall'));\n })\n )\n .addCommand(\n new Command('status')\n .description('Check the status of the enve pre-commit hook')\n .action(async () => {\n const cwd = process.cwd();\n const projectPath = await detectProject(cwd);\n\n if (!projectPath || !(await isGitRepo(projectPath))) {\n console.log(error('Not a git repository.'));\n process.exit(1);\n }\n\n const installed = await isHookInstalled(projectPath);\n console.log();\n if (installed) {\n console.log(indent(success('Pre-commit hook: Installed (enve protection active)')));\n console.log(indent(`Location: ${getPreCommitHookPath(projectPath)}`));\n } else {\n console.log(indent(warning('Pre-commit hook: Not installed')));\n console.log(indent('Run `enve hook install` to enable .env commit protection.'));\n }\n })\n )\n .addCommand(\n new Command('uninstall')\n .description('Remove the enve pre-commit hook')\n .action(async () => {\n const cwd = process.cwd();\n const projectPath = await detectProject(cwd);\n\n if (!projectPath || !(await isGitRepo(projectPath))) {\n console.log(error('Not a git repository.'));\n process.exit(1);\n }\n\n const removed = await uninstallHook(projectPath);\n console.log();\n if (removed) {\n console.log(indent(success('Pre-commit hook removed')));\n console.log(indent('Run `enve hook install` to re-enable.'));\n } else {\n console.log(indent(warning('No enve hook found to remove.')));\n }\n })\n );\n\nfunction bullet(text: string): string {\n return `• ${text}`;\n}\n","import { Command } from 'commander';\nimport { render } from 'ink';\nimport React, { useState, useCallback } from 'react';\nimport { getProjects } from '../../core/config.js';\nimport { getProjectInfo } from '../../core/project.js';\nimport { runDoctor } from './doctor.js';\nimport { Dashboard } from '../../ui/Dashboard.js';\nimport type { ProjectInfo } from '../../types/index.js';\nimport pkg from '../../../package.json' with { type: 'json' };\n\nexport const dashboardCommand = new Command('dashboard')\n .description('Interactive terminal dashboard showing all tracked projects')\n .action(async () => {\n const tracked = getProjects();\n\n if (tracked.length === 0) {\n console.log();\n console.log(' Enve Dashboard');\n console.log();\n console.log(' No projects tracked yet.');\n console.log(' Run `enve scan` or `enve doctor` in a project to add it.');\n console.log();\n return;\n }\n\n if (!process.stdin.isTTY) {\n console.log();\n console.log(' The interactive dashboard requires a TTY terminal.');\n console.log(' Run `enve dashboard` in your terminal to use it.');\n console.log();\n return;\n }\n\n const initialProjects = await analyzeProjects(tracked.map((p) => p.path));\n\n render(\n <DashboardApp\n initialProjects={initialProjects}\n trackedPaths={tracked.map((p) => p.path)}\n version={pkg.version}\n />\n );\n });\n\ninterface DashboardAppProps {\n initialProjects: ProjectInfo[];\n trackedPaths: string[];\n version: string;\n}\n\nfunction DashboardApp({ initialProjects, trackedPaths, version }: DashboardAppProps): React.ReactElement {\n const [projects, setProjects] = useState(initialProjects);\n\n const handleRunDoctor = useCallback(\n async (project: ProjectInfo) => {\n await runDoctor(project.path);\n },\n []\n );\n\n const handleRefresh = useCallback(async () => {\n const fresh = await analyzeProjects(trackedPaths);\n setProjects(fresh);\n }, [trackedPaths]);\n\n return (\n <Dashboard\n projects={projects}\n version={version}\n onRunDoctor={handleRunDoctor}\n onRefresh={handleRefresh}\n />\n );\n}\n\nasync function analyzeProjects(paths: string[]): Promise<ProjectInfo[]> {\n const results = await Promise.all(paths.map((p) => getProjectInfo(p)));\n return results;\n}\n","import React, { useState, useMemo } from 'react';\nimport { Box, Text, useInput, useApp, useStdout } from 'ink';\nimport type { ProjectInfo } from '../types/index.js';\nimport { ProjectCard } from './ProjectCard.js';\nimport { IssueList } from './IssueList.js';\nimport { ScoreBadge } from './ScoreBadge.js';\nimport { colors, layout } from './theme.js';\n\ninterface DashboardProps {\n projects: ProjectInfo[];\n version: string;\n onRunDoctor: (project: ProjectInfo) => void | Promise<void>;\n onRefresh: () => void | Promise<void>;\n}\n\nexport function Dashboard({\n projects,\n version,\n onRunDoctor,\n onRefresh,\n}: DashboardProps): React.ReactElement {\n const { exit } = useApp();\n const { stdout } = useStdout();\n const [selectedIndex, setSelectedIndex] = useState(0);\n const [isExiting, setIsExiting] = useState(false);\n\n const terminalRows = stdout.rows ?? 24;\n const selectedProject = projects[selectedIndex];\n const maxVisible = Math.max(4, Math.floor((terminalRows - 10) / 5));\n\n const visibleProjects = useMemo(() => {\n if (projects.length <= maxVisible) return projects;\n\n let start = Math.max(0, selectedIndex - Math.floor(maxVisible / 2));\n let end = start + maxVisible;\n if (end > projects.length) {\n end = projects.length;\n start = Math.max(0, end - maxVisible);\n }\n return projects.slice(start, end);\n }, [projects, selectedIndex, maxVisible]);\n\n const visibleStart = projects.length <= maxVisible ? 0 : Math.max(0, selectedIndex - Math.floor(maxVisible / 2));\n\n useInput(\n (input, key) => {\n if (isExiting) return;\n\n if (key.upArrow || input === 'k') {\n setSelectedIndex((prev) => Math.max(0, prev - 1));\n } else if (key.downArrow || input === 'j') {\n setSelectedIndex((prev) => Math.min(projects.length - 1, prev + 1));\n } else if (key.return) {\n if (selectedProject) {\n setIsExiting(true);\n void Promise.resolve(onRunDoctor(selectedProject)).then(() => exit());\n }\n } else if (input === 'r') {\n void Promise.resolve(onRefresh());\n } else if (input === 'q' || key.escape) {\n setIsExiting(true);\n exit();\n }\n },\n { isActive: !isExiting }\n );\n\n if (projects.length === 0) {\n return (\n <Box flexDirection=\"column\" padding={2}>\n <Text bold color={colors.ink}>enve dashboard</Text>\n <Box marginTop={1}>\n <Text color={colors.muted}>No projects tracked yet.</Text>\n </Box>\n <Box marginTop={1}>\n <Text color={colors.subtle}>Run `enve scan` or `enve doctor` in a project to add it.</Text>\n </Box>\n </Box>\n );\n }\n\n return (\n <Box flexDirection=\"column\" width=\"100%\" paddingX={layout.paddingX} paddingY={layout.paddingY}>\n {/* Top bar */}\n <Box justifyContent=\"space-between\" width=\"100%\" marginBottom={1}>\n <Box>\n <Text bold color={colors.ink}>enve</Text>\n <Text color={colors.muted}> dashboard</Text>\n </Box>\n <Text color={colors.subtle}>v{version}</Text>\n </Box>\n\n {/* Main content */}\n <Box flexGrow={1} width=\"100%\">\n {/* Sidebar */}\n <Box\n width={layout.sidebarWidth}\n flexDirection=\"column\"\n borderStyle=\"single\"\n borderColor={colors.border}\n paddingX={1}\n paddingY={1}\n marginRight={2}\n >\n <Box marginBottom={1}>\n <Text bold color={colors.ink}>Projects</Text>\n <Text color={colors.subtle}> ({projects.length})</Text>\n </Box>\n\n <Box flexDirection=\"column\" rowGap={1} flexGrow={1}>\n {visibleProjects.map((project, index) => {\n const actualIndex = visibleStart + index;\n return (\n <ProjectCard\n key={project.path}\n project={project}\n isSelected={actualIndex === selectedIndex}\n isFocused={actualIndex === selectedIndex}\n />\n );\n })}\n </Box>\n\n {projects.length > maxVisible && (\n <Box marginTop={1}>\n <Text color={colors.subtle}>Use ↑/↓ to scroll</Text>\n </Box>\n )}\n </Box>\n\n {/* Details panel */}\n <Box\n flexGrow={1}\n flexDirection=\"column\"\n borderStyle=\"single\"\n borderColor={colors.border}\n paddingX={2}\n paddingY={1}\n >\n {selectedProject ? (\n <ProjectDetail project={selectedProject} />\n ) : (\n <Box>\n <Text color={colors.muted}>Select a project to view details.</Text>\n </Box>\n )}\n </Box>\n </Box>\n\n {/* Bottom bar */}\n <Box\n justifyContent=\"space-between\"\n width=\"100%\"\n borderStyle=\"single\"\n borderColor={colors.border}\n paddingX={1}\n marginTop={1}\n >\n <Box columnGap={3}>\n <Text color={colors.muted}><Text bold>↑/↓</Text> navigate</Text>\n <Text color={colors.muted}><Text bold>Enter</Text> doctor</Text>\n <Text color={colors.muted}><Text bold>r</Text> refresh</Text>\n <Text color={colors.muted}><Text bold>q</Text> quit</Text>\n </Box>\n <Text color={colors.subtle}>enve-doctor v{version}</Text>\n </Box>\n </Box>\n );\n}\n\nfunction ProjectDetail({ project }: { project: ProjectInfo }): React.ReactElement {\n const envFileNames = project.envFiles.map((f) => f.filename).join(', ') || 'none';\n\n return (\n <Box flexDirection=\"column\" width=\"100%\">\n <Box justifyContent=\"space-between\" alignItems=\"center\" marginBottom={1}>\n <Text bold color={colors.ink}>{project.name}</Text>\n <ScoreBadge score={project.score} />\n </Box>\n\n <Box marginBottom={1}>\n <Text color={colors.muted}>{project.path}</Text>\n </Box>\n\n <Box flexDirection=\"row\" columnGap={4} marginBottom={1}>\n <Metric label=\"Env vars\" value={String(project.envCount)} />\n <Metric label=\"References\" value={String(project.referenceCount)} />\n <Metric label=\"Issues\" value={String(project.score.totalIssues)} />\n </Box>\n\n <Box flexDirection=\"row\" columnGap={4} marginBottom={1}>\n <Status label=\"Git repo\" ok={project.hasGit} />\n <Status label=\"Git hook\" ok={project.hookInstalled} />\n <Status label=\".gitignore\" ok={project.hasEnvInGitignore} />\n </Box>\n\n <Box marginTop={1} marginBottom={1}>\n <Text color={colors.subtle}>Env files: </Text>\n <Text color={colors.ink}>{envFileNames}</Text>\n </Box>\n\n <Box marginTop={1} marginBottom={1}>\n <Text color={colors.borderStrong}>{'─'.repeat(60)}</Text>\n </Box>\n\n <Box marginTop={1} marginBottom={1}>\n <Text bold color={colors.ink}>Top issues</Text>\n </Box>\n\n <IssueList issues={project.issues} maxRows={8} />\n </Box>\n );\n}\n\nfunction Metric({ label, value }: { label: string; value: string }): React.ReactElement {\n return (\n <Box flexDirection=\"column\">\n <Text color={colors.subtle}>{label}</Text>\n <Text bold color={colors.ink}>{value}</Text>\n </Box>\n );\n}\n\nfunction Status({ label, ok }: { label: string; ok: boolean }): React.ReactElement {\n return (\n <Box>\n <Text color={ok ? colors.success : colors.error}>{ok ? '✓' : '✗'} </Text>\n <Text color={colors.muted}>{label}</Text>\n </Box>\n );\n}\n","import React from 'react';\nimport { Box, Text } from 'ink';\nimport type { ProjectInfo } from '../types/index.js';\nimport { ScoreBadge } from './ScoreBadge.js';\nimport { colors } from './theme.js';\n\ninterface ProjectCardProps {\n project: ProjectInfo;\n isSelected: boolean;\n isFocused: boolean;\n}\n\nexport function ProjectCard({\n project,\n isSelected,\n isFocused,\n}: ProjectCardProps): React.ReactElement {\n const { score } = project;\n const borderColor = isSelected ? colors.ink : colors.border;\n const indicator = isFocused ? '›' : ' ';\n\n return (\n <Box\n borderStyle=\"single\"\n borderColor={borderColor}\n paddingX={1}\n paddingY={1}\n flexDirection=\"column\"\n >\n <Box justifyContent=\"space-between\">\n <Box>\n <Text color={isSelected ? colors.ink : colors.muted}>{indicator} </Text>\n <Text bold color={colors.ink}>{project.name}</Text>\n </Box>\n <ScoreBadge score={score} compact />\n </Box>\n\n <Box marginTop={1}>\n <Text color={colors.muted}>{project.path}</Text>\n </Box>\n\n <Box marginTop={1} columnGap={2}>\n {score.errorCount > 0 && (\n <Text color={colors.error}>{score.errorCount} err</Text>\n )}\n {score.warningCount > 0 && (\n <Text color={colors.warning}>{score.warningCount} warn</Text>\n )}\n {score.totalIssues === 0 && (\n <Text color={colors.success}>Clean</Text>\n )}\n </Box>\n </Box>\n );\n}\n","import React from 'react';\nimport { Box, Text } from 'ink';\nimport type { ScoreResult } from '../types/index.js';\nimport { gradeStyles } from './theme.js';\n\ninterface ScoreBadgeProps {\n score: ScoreResult;\n compact?: boolean;\n}\n\nexport function ScoreBadge({ score, compact = false }: ScoreBadgeProps): React.ReactElement {\n const style = gradeStyles[score.grade];\n\n if (compact) {\n return (\n <Box>\n <Text backgroundColor={style.bg} color={style.text} bold>\n {' '}\n {score.score}\n {' '}\n </Text>\n </Box>\n );\n }\n\n return (\n <Box>\n <Text backgroundColor={style.bg} color={style.text} bold>\n {' '}\n {score.score} {score.grade}\n {' '}\n </Text>\n </Box>\n );\n}\n","import type { Grade, Severity } from '../types/index.js';\n\nexport const colors = {\n // Warm monochrome base\n canvas: '#FAFAF8',\n ink: '#1A1A1A',\n muted: '#6B6B6B',\n subtle: '#A3A3A3',\n\n // Borders & dividers\n border: '#E5E5E0',\n borderStrong: '#D4D4CF',\n\n // Semantic accents — desaturated pastels\n excellent: '#10B981', // emerald 500\n excellentBg: '#ECFDF5', // emerald 50\n good: '#F59E0B', // amber 500\n goodBg: '#FFFBEB', // amber 50\n attention: '#F97316', // orange 500\n attentionBg: '#FFF7ED', // orange 50\n critical: '#EF4444', // red 500\n criticalBg: '#FEF2F2', // red 50\n\n // Severity\n error: '#DC2626',\n warning: '#D97706',\n info: '#2563EB',\n success: '#059669',\n} as const;\n\nexport const gradeStyles: Record<\n Grade,\n { text: string; bg: string; label: string }\n> = {\n Excellent: { text: colors.excellent, bg: colors.excellentBg, label: 'Excellent' },\n Good: { text: colors.good, bg: colors.goodBg, label: 'Good' },\n 'Needs attention': { text: colors.attention, bg: colors.attentionBg, label: 'Needs attention' },\n Critical: { text: colors.critical, bg: colors.criticalBg, label: 'Critical' },\n};\n\nexport const severityStyles: Record<Severity, { color: string; symbol: string }> = {\n error: { color: colors.error, symbol: '●' },\n warning: { color: colors.warning, symbol: '●' },\n info: { color: colors.info, symbol: '●' },\n};\n\nexport const issueTypeSymbol: Record<string, string> = {\n missing: '⊘',\n unused: '○',\n invalid: '✕',\n suspicious: '◆',\n 'secret-risk': '▲',\n 'syntax-error': '✕',\n gitignore: '◉',\n};\n\nexport const layout = {\n sidebarWidth: 34,\n maxContentWidth: 86,\n paddingX: 2,\n paddingY: 1,\n} as const;\n","import React from 'react';\nimport { Box, Text } from 'ink';\nimport type { Issue } from '../types/index.js';\nimport { severityStyles, issueTypeSymbol, colors } from './theme.js';\n\ninterface IssueListProps {\n issues: Issue[];\n maxRows?: number;\n}\n\nexport function IssueList({ issues, maxRows = 12 }: IssueListProps): React.ReactElement {\n if (issues.length === 0) {\n return (\n <Box paddingY={1}>\n <Text color={colors.success}>No issues found. Great job.</Text>\n </Box>\n );\n }\n\n const sorted = [...issues].sort((a, b) => {\n const rank = { error: 0, warning: 1, info: 2 };\n return rank[a.severity] - rank[b.severity];\n });\n\n const visible = sorted.slice(0, maxRows);\n const remaining = sorted.length - visible.length;\n\n return (\n <Box flexDirection=\"column\" width=\"100%\">\n {visible.map((issue, index) => {\n const style = severityStyles[issue.severity];\n const symbol = issueTypeSymbol[issue.type] ?? '•';\n\n return (\n <Box key={`${issue.type}-${issue.key}-${index}`} flexDirection=\"column\" marginBottom={1}>\n <Box>\n <Text color={style.color}>{symbol} </Text>\n <Text bold color={colors.ink}>{issue.key}</Text>\n <Text color={colors.subtle}> — {issue.type}</Text>\n </Box>\n <Box marginLeft={2}>\n <Text color={colors.muted}>{issue.message}</Text>\n </Box>\n {issue.suggestion && (\n <Box marginLeft={2}>\n <Text color={colors.subtle}>→ {issue.suggestion}</Text>\n </Box>\n )}\n </Box>\n );\n })}\n {remaining > 0 && (\n <Box marginTop={1}>\n <Text color={colors.subtle}>+ {remaining} more issue{remaining === 1 ? '' : 's'}</Text>\n </Box>\n )}\n </Box>\n );\n}\n","{\n \"name\": \"enve-doctor\",\n \"version\": \"1.0.3\",\n \"description\": \"Environment Variable Doctor — scan, validate, and protect your env files\",\n \"main\": \"dist/cli.mjs\",\n \"bin\": {\n \"enve\": \"dist/cli.mjs\"\n },\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://github.com/RufixDuke/enve.git\"\n },\n \"bugs\": {\n \"url\": \"https://github.com/RufixDuke/enve/issues\"\n },\n \"homepage\": \"https://enve-lac.vercel.app\",\n \"files\": [\n \"dist\"\n ],\n \"scripts\": {\n \"build\": \"tsup src/cli/cli.ts --minify --clean\",\n \"dev\": \"tsx src/cli/index.ts\",\n \"test\": \"vitest run\",\n \"test:watch\": \"vitest\",\n \"prepublishOnly\": \"npm run build\"\n },\n \"dependencies\": {\n \"acorn\": \"^8.14.0\",\n \"acorn-walk\": \"^8.3.4\",\n \"chalk\": \"^5.4.1\",\n \"commander\": \"^13.1.0\",\n \"conf\": \"^13.1.0\",\n \"dotenv\": \"^16.4.7\",\n \"fast-glob\": \"^3.3.3\",\n \"ink\": \"^5.1.0\",\n \"ora\": \"^8.2.0\",\n \"react\": \"^18.3.1\"\n },\n \"devDependencies\": {\n \"@types/node\": \"^22.13.0\",\n \"@types/react\": \"^18.3.18\",\n \"@typescript-eslint/eslint-plugin\": \"^8.22.0\",\n \"@typescript-eslint/parser\": \"^8.22.0\",\n \"eslint\": \"^9.19.0\",\n \"ink-testing-library\": \"^4.0.0\",\n \"tsup\": \"^8.3.6\",\n \"tsx\": \"^4.19.2\",\n \"typescript\": \"^5.7.3\",\n \"vitest\": \"^3.0.5\"\n },\n \"keywords\": [\n \"env\",\n \"environment-variables\",\n \"secrets\",\n \"security\",\n \"cli\",\n \"developer-tools\"\n ],\n \"author\": \"Abdul-Qudus Rufai\",\n \"license\": \"MIT\",\n \"engines\": {\n \"node\": \">=18.0.0\"\n }\n}\n"],"mappings":"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","names":["Command","Command","chalk","join","basename","readFile","writeFile","access","mkdir","dirname","readFileSafe","filePath","writeFileSafe","content","ensureDir","fileExists","dirPath","join","PRE_COMMIT_HOOK_MARKER","PRE_COMMIT_HOOK_END","isGitRepo","projectPath","fileExists","join","readGitignore","gitignorePath","readFileSafe","isPatternInGitignore","gitignoreContent","pattern","line","trimmed","isEnvFileGitignored","filename","content","getPreCommitHookPath","getHookScript","isHookInstalled","hookPath","installHook","existing","newContent","writeFileSafe","uninstallHook","markerStart","markerEnd","before","after","join","readFileSync","parseEnvFile","filePath","content","readFileSync","variables","lines","trimmed","equalIndex","key","rawValue","value","comment","parseValue","isSecret","detectSecret","raw","endIndex","parseQuotedValue","rest","extractInlineComment","trailing","commentIndex","findUnquotedCommentIndex","quote","escaped","char","extractInlineComment","rest","idx","findUnquotedCommentIndex","value","i","detectSecret","key","findEnvFiles","projectPath","envFileNames","files","name","filePath","join","readFileSafe","parseEnvFile","parseAllEnvFiles","fg","readFile","Parser","fullAncestor","DEFAULT_INCLUDE","DEFAULT_EXCLUDE","scanProject","projectPath","options","include","exclude","files","references","file","fileRefs","scanFile","filePath","content","ast","extractReferencesWithRegex","node","_state","ancestors","ref","extractEnvReference","refs","extractDestructuredEnvReferences","key","getMemberExpressionKey","parent","fallback","detectFallback","createReference","isEnvDestructuringPattern","prop","fallbackValue","expressionToString","nested","seen","addRef","index","dummyNode","memberRe","match","bracketRe","destructRe","props","trimmed","keyMatch","isEnvSource","grandParent","isProcessNode","isProcessEnvNode","isLeft","isRight","fallbackNode","getFallbackType","line","column","context","extractContext","lines","start","end","ALWAYS_ALLOWED","analyze","envFiles","references","projectPath","issues","checkMissingVariables","checkUnusedVariables","checkSecretRisks","checkGitignore","checkExampleFile","definedKeys","file","v","ref","referencedKeys","r","variable","filenames","f","filesToCheck","messages","suggestions","filename","isEnvFileGitignored","exampleFile","envFile","exampleKeys","calculateScore","score","errorCount","warningCount","infoCount","unusedDeduction","exampleDeduction","issue","getGrade","Conf","readFile","join","DEFAULT_CONFIG","createConfig","getProjects","isTracked","projectPath","p","getProject","addProject","config","project","detectProjectName","projects","detectProjectName","projectPath","pkgRaw","readFile","join","pkg","detectProject","startPath","current","fileExists","join","parent","getProjectInfo","projectPath","envFiles","references","hasGit","hookInstalled","parseAllEnvFiles","scanProject","isGitRepo","isHookInstalled","issues","analyze","score","calculateScore","name","basename","addProject","sum","f","i","chalk","symbols","error","message","warning","success","bullet","message","symbols","indent","level","header","title","chalk","scanCommand","Command","cwd","projectPath","detectProject","error","info","getProjectInfo","indent","chalk","file","bullet","success","errors","i","warnings","infos","issue","warning","formatScore","score","text","Command","chalk","doctorCommand","Command","cwd","projectPath","detectProject","error","runDoctor","info","getProjectInfo","envFiles","parseAllEnvFiles","indent","header","chalk","fileStructureIssues","i","variableIssues","securityIssues","validationIssues","printSection","printVariablesSection","printRecommendations","formatScore","title","issues","success","issue","formatter","warning","totalDefined","sum","f","message","actionable","high","medium","low","idx","score","text","Command","chalk","copyFile","createInterface","prompt","message","rl","confirm","defaultValue","answer","unusedCommand","Command","options","cwd","projectPath","detectProject","error","envFiles","parseAllEnvFiles","references","scanProject","referencedKeys","r","alwaysAllowed","unused","file","variable","indent","header","success","byFile","item","filename","items","warning","chalk","confirm","byPath","removedCount","filePath","backupPath","formatTimestamp","copyFile","lines","readFileSafe","linesToRemove","i","newLines","_","idx","writeFileSafe","date","Command","chalk","join","STANDARD_NODE_VARS","missingCommand","Command","options","cwd","projectPath","detectProject","error","envFiles","parseAllEnvFiles","references","scanProject","definedKeys","f","v","missingNoFallback","r","missingWithFallback","indent","header","success","ref","chalk","l","warning","addMissingVariables","missing","envPath","join","updates","exampleUpdates","value","prompt","confirm","existing","readFileSafe","separator","writeFileSafe","examplePath","exampleExisting","exampleSeparator","Command","chalk","validatePort","value","port","validateUrl","url","validateNodeEnv","validateSecret","key","forbidden","validateBoolean","normalized","boolMap","isSecretKey","copyFile","validateCommand","Command","options","cwd","projectPath","detectProject","error","envFile","parseAllEnvFiles","f","variables","indent","header","errorCount","warningCount","fixes","variable","result","validateVariable","success","truncate","chalk","fixedValue","getFixedValue","safeFixes","isSecretKey","confirm","applyFixes","key","value","validatePort","validateNodeEnv","validateUrl","validateSecret","validateBoolean","normalized","filePath","backupPath","formatTimestamp","copyFile","content","readFileSafe","fixMap","lines","line","trimmed","equalIndex","prefix","suffix","commentIndex","comment","leadingSpaces","writeFileSafe","date","max","Command","chalk","join","generateExampleCommand","Command","options","cwd","projectPath","detectProject","error","envPath","join","readFileSafe","outputPath","existing","variables","parseEnvFile","newLines","redactedCount","copiedCount","variable","isSecretKey","newContent","indent","header","bullet","diff","computeDiff","success","change","symbol","chalk","confirm","writeFileSafe","existingContent","existingVars","newVars","line","trimmed","idx","key","value","Command","hookCommand","Command","cwd","projectPath","detectProject","isGitRepo","error","installHook","indent","success","getPreCommitHookPath","bullet","installed","isHookInstalled","warning","removed","uninstallHook","text","Command","render","React","useState","useCallback","React","useState","useMemo","Box","Text","useInput","useApp","useStdout","React","Box","Text","React","Box","Text","colors","gradeStyles","severityStyles","issueTypeSymbol","layout","ScoreBadge","score","compact","style","gradeStyles","React","Box","Text","ProjectCard","project","isSelected","isFocused","score","borderColor","colors","indicator","React","Box","Text","ScoreBadge","React","Box","Text","IssueList","issues","maxRows","React","Box","Text","colors","sorted","a","b","rank","visible","remaining","issue","index","style","severityStyles","symbol","issueTypeSymbol","Dashboard","projects","version","onRunDoctor","onRefresh","exit","useApp","stdout","useStdout","selectedIndex","setSelectedIndex","useState","isExiting","setIsExiting","terminalRows","selectedProject","maxVisible","visibleProjects","useMemo","start","end","visibleStart","useInput","input","key","prev","React","Box","Text","colors","layout","project","index","actualIndex","ProjectCard","ProjectDetail","envFileNames","f","ScoreBadge","Metric","Status","IssueList","label","value","ok","package_default","dashboardCommand","Command","tracked","getProjects","initialProjects","analyzeProjects","p","render","React","DashboardApp","package_default","trackedPaths","version","projects","setProjects","useState","handleRunDoctor","useCallback","project","runDoctor","handleRefresh","fresh","Dashboard","paths","getProjectInfo","program","Command","package_default","scanCommand","doctorCommand","unusedCommand","missingCommand","validateCommand","generateExampleCommand","hookCommand","dashboardCommand","reason"]}
|