@visulima/vis 1.0.0-alpha.10 → 1.0.0-alpha.11

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.
Files changed (120) hide show
  1. package/CHANGELOG.md +95 -42
  2. package/LICENSE.md +213 -0
  3. package/README.md +8 -4
  4. package/dist/bin.js +9 -1
  5. package/dist/config/index.d.ts +1818 -0
  6. package/dist/config/index.js +2 -0
  7. package/dist/generate/index.d.ts +1 -1
  8. package/dist/generate/index.js +3 -1
  9. package/dist/packem_chunks/applyDefaults.js +336 -0
  10. package/dist/packem_chunks/bin.js +9554 -64
  11. package/dist/packem_chunks/doctor-probe.js +112 -0
  12. package/dist/packem_chunks/fix.js +229 -48
  13. package/dist/packem_chunks/handler.js +99 -1
  14. package/dist/packem_chunks/handler10.js +53 -1
  15. package/dist/packem_chunks/handler11.js +32 -1
  16. package/dist/packem_chunks/handler12.js +100 -2
  17. package/dist/packem_chunks/handler13.js +25 -1
  18. package/dist/packem_chunks/handler14.js +916 -5
  19. package/dist/packem_chunks/handler15.js +206 -1
  20. package/dist/packem_chunks/handler16.js +122 -18
  21. package/dist/packem_chunks/handler17.js +13 -1
  22. package/dist/packem_chunks/handler18.js +106 -1
  23. package/dist/packem_chunks/handler19.js +19 -1
  24. package/dist/packem_chunks/handler2.js +75 -1
  25. package/dist/packem_chunks/handler20.js +29 -1
  26. package/dist/packem_chunks/handler21.js +222 -1
  27. package/dist/packem_chunks/handler22.js +237 -5
  28. package/dist/packem_chunks/handler23.js +101 -1
  29. package/dist/packem_chunks/handler24.js +110 -1
  30. package/dist/packem_chunks/handler25.js +402 -5
  31. package/dist/packem_chunks/handler26.js +13 -1
  32. package/dist/packem_chunks/handler27.js +63 -3
  33. package/dist/packem_chunks/handler28.js +34 -1
  34. package/dist/packem_chunks/handler29.js +458 -7
  35. package/dist/packem_chunks/handler3.js +95 -2
  36. package/dist/packem_chunks/handler30.js +168 -21
  37. package/dist/packem_chunks/handler31.js +530 -3
  38. package/dist/packem_chunks/handler32.js +214 -2
  39. package/dist/packem_chunks/handler33.js +119 -24
  40. package/dist/packem_chunks/handler34.js +630 -2
  41. package/dist/packem_chunks/handler35.js +283 -19
  42. package/dist/packem_chunks/handler36.js +521 -407
  43. package/dist/packem_chunks/handler37.js +762 -22
  44. package/dist/packem_chunks/handler38.js +989 -22
  45. package/dist/packem_chunks/handler39.js +574 -22
  46. package/dist/packem_chunks/handler4.js +90 -4
  47. package/dist/packem_chunks/handler40.js +1685 -3
  48. package/dist/packem_chunks/handler41.js +1088 -10
  49. package/dist/packem_chunks/handler42.js +785 -141
  50. package/dist/packem_chunks/handler43.js +2658 -42
  51. package/dist/packem_chunks/handler44.js +3886 -3
  52. package/dist/packem_chunks/handler45.js +2568 -21
  53. package/dist/packem_chunks/handler46.js +3769 -0
  54. package/dist/packem_chunks/handler47.js +1491 -0
  55. package/dist/packem_chunks/handler5.js +174 -2
  56. package/dist/packem_chunks/handler6.js +95 -13
  57. package/dist/packem_chunks/handler7.js +115 -8
  58. package/dist/packem_chunks/handler8.js +12 -1
  59. package/dist/packem_chunks/handler9.js +29 -1
  60. package/dist/packem_chunks/heal-accept.js +522 -0
  61. package/dist/packem_chunks/heal.js +673 -0
  62. package/dist/packem_chunks/index.js +873 -7
  63. package/dist/packem_chunks/loader.js +23 -1
  64. package/dist/packem_shared/VisUpdateApp-D-Yz_wvg.js +1316 -0
  65. package/dist/packem_shared/_commonjsHelpers-BqLXS_qQ.js +5 -0
  66. package/dist/packem_shared/ai-analysis-CHeB1joD.js +367 -0
  67. package/dist/packem_shared/ai-cache-Be_jexe4.js +142 -0
  68. package/dist/packem_shared/ai-fix-B9iQVcD2.js +379 -0
  69. package/dist/packem_shared/cache-directory-2qvs4goY.js +98 -0
  70. package/dist/packem_shared/catalog-BJTtyi-O.js +1371 -0
  71. package/dist/packem_shared/dependency-scan-A0KSklpG.js +188 -0
  72. package/dist/packem_shared/docker-2iZzc280.js +181 -0
  73. package/dist/packem_shared/failure-log-Cz3Z4SKL.js +100 -0
  74. package/dist/packem_shared/flakiness-goTxXuCX.js +180 -0
  75. package/dist/packem_shared/otel-DCvqCTz_.js +158 -0
  76. package/dist/packem_shared/otelPlugin-DFaLDvJf.js +3 -0
  77. package/dist/packem_shared/registry-CbqXI0rc.js +272 -0
  78. package/dist/packem_shared/run-summary-utils-PVMl4aIh.js +130 -0
  79. package/dist/packem_shared/runtime-check-Cobi3p6l.js +127 -0
  80. package/dist/packem_shared/selectors-SM69TfqC.js +194 -0
  81. package/dist/packem_shared/symbols-Ta7g2nU-.js +14 -0
  82. package/dist/packem_shared/toolchain-BdZd9eBi.js +975 -0
  83. package/dist/packem_shared/typosquats-C-bCh3PX.js +1210 -0
  84. package/dist/packem_shared/use-measured-height-CNP0vT4M.js +20 -0
  85. package/dist/packem_shared/utils-CthVdBPS.js +40 -0
  86. package/dist/packem_shared/xxh3-Ck8mXNg1.js +239 -0
  87. package/index.js +727 -555
  88. package/package.json +35 -17
  89. package/schemas/project.schema.json +8 -10
  90. package/schemas/vis-config.schema.json +132 -8
  91. package/skills/vis/SKILL.md +96 -0
  92. package/templates/buildkite-ci/.buildkite/pipeline.yml.tera +85 -0
  93. package/templates/buildkite-ci/template.yml +20 -0
  94. package/dist/errors/index.d.ts +0 -26
  95. package/dist/errors/index.js +0 -1
  96. package/dist/packem_chunks/config.js +0 -2
  97. package/dist/packem_shared/VisConfigCycleError-CAYNC7d-.js +0 -1
  98. package/dist/packem_shared/VisConfigError-B5LP1zRf.js +0 -1
  99. package/dist/packem_shared/VisConfigLoadError-CeqBSd2Z.js +0 -2
  100. package/dist/packem_shared/VisConfigNotFoundError-DZ9KC527.js +0 -5
  101. package/dist/packem_shared/VisUpdateApp-D-L4_-Iu.js +0 -1
  102. package/dist/packem_shared/_commonjsHelpers-D6W6KoPK.js +0 -1
  103. package/dist/packem_shared/ai-analysis-CGuy7dfE.js +0 -67
  104. package/dist/packem_shared/ai-cache-Bynt6Y9x.js +0 -1
  105. package/dist/packem_shared/cache-directory-D72ZEag2.js +0 -1
  106. package/dist/packem_shared/catalog-BVPerCwG.js +0 -12
  107. package/dist/packem_shared/dependency-scan-Du0tBu64.js +0 -2
  108. package/dist/packem_shared/docker-BcfqH4Av.js +0 -2
  109. package/dist/packem_shared/failure-log-DqYen0LC.js +0 -2
  110. package/dist/packem_shared/flakiness-DSIHZGBT.js +0 -1
  111. package/dist/packem_shared/run-summary-utils-C24Aaf9E.js +0 -1
  112. package/dist/packem_shared/runtime-check-CGHal8SO.js +0 -1
  113. package/dist/packem_shared/selectors-CfH9ZY08.js +0 -3
  114. package/dist/packem_shared/symbols-CQmER5MT.js +0 -1
  115. package/dist/packem_shared/target-merge-DNa-6eWu.js +0 -1
  116. package/dist/packem_shared/toolchain-DQfTQY8E.js +0 -5
  117. package/dist/packem_shared/typosquats-DOR8izpX.js +0 -1
  118. package/dist/packem_shared/use-measured-height-DjYgUOKk.js +0 -1
  119. package/dist/packem_shared/utils-DrNg0XTR.js +0 -1
  120. package/dist/packem_shared/xxh3-DrAUNq4n.js +0 -1
@@ -1,42 +1,2658 @@
1
- var Xt=Object.defineProperty;var M=(r,e)=>Xt(r,"name",{value:e,configurable:!0});import{createRequire as Jt}from"node:module";import{runConcurrently as Ge,TerminalBuffer as Mt,readLastRunSummary as er,enforceProjectConstraints as tr,parsePartition as rr,TaskScheduler as or,createTaskGraph as ir,generateRunSummary as ct,writeChromeTrace as nr,CompositeLifeCycle as dt,defaultTaskRunner as ut,createLogReporter as sr,writeRunSummary as lr}from"@visulima/task-runner";import{M as ar,a7 as jt,a8 as cr,a9 as dr,r as ur,s as hr,aa as fr,ab as pr,d as ht}from"./bin.js";import{E as mr,a as gr}from"../packem_shared/cache-directory-D72ZEag2.js";import{F as wr}from"../packem_shared/failure-log-DqYen0LC.js";import{f as kr,l as yr,c as vr,a as br,b as Tr}from"../packem_shared/flakiness-DSIHZGBT.js";import{r as Cr}from"../packem_shared/toolchain-DQfTQY8E.js";import{r as Sr,f as $r}from"../packem_shared/selectors-CfH9ZY08.js";import{Box as u,Text as o,StaticRender as ft,renderToString as X,ScrollView as Et,Spinner as Rr,useApp as Mr,useWindowSize as jr,useInput as pt,Dialog as Er,render as Or}from"@visulima/tui";import A,{useSyncExternalStore as Fr,useState as mt,useRef as $e,useCallback as Le,useEffect as Ae,useMemo as gt}from"react";import{jsxs as a,jsx as i}from"react/jsx-runtime";import{T as Ot,C as He,D as je,E as Ft}from"../packem_shared/symbols-CQmER5MT.js";import{duration as Pt}from"@visulima/humanizer";import{createHooks as Pr}from"hookable";import{basename as Ar,join as We,dirname as Dr,resolve as Lt,relative as Br}from"@visulima/path";const Zt=Jt(import.meta.url),Se=typeof globalThis<"u"&&typeof globalThis.process<"u"?globalThis.process:process,Me=M(r=>{if(typeof Se<"u"&&Se.versions&&Se.versions.node){const[e,t]=Se.versions.node.split(".").map(Number);if(e>22||e===22&&t>=3||e===20&&t>=16)return Se.getBuiltinModule(r)}return Zt(r)},"__cjs_getBuiltinModule"),{createInterface:xr,emitKeypressEvents:Ir}=Me("node:readline"),{appendFile:Ue}=Me("node:fs/promises"),{platform:Lr,homedir:_e}=Me("node:os"),{watch:Nr}=Me("node:fs");var qr=Object.defineProperty,me=M((r,e)=>qr(r,"name",{value:e,configurable:!0}),"i$2");const wt=me(r=>{const e=new Set;for(const t of Object.values(r.projects))for(const s of Object.keys(t.targets??{}))e.add(s);return[...e].sort()},"collectAvailableTargets"),Gr=me(r=>{const e=new Map;for(const t of r.values())for(const[s,n]of Object.entries(t))for(const d of n.aliases??[])e.has(d)||e.set(d,s);return e},"buildAliasMap"),Hr=me((r,e)=>e.get(r)??r,"resolveTargetAlias"),Ur=me((r,e)=>{if(r.length===0)return e.length;if(e.length===0)return r.length;const t=[];for(let s=0;s<=e.length;s++)t[s]=[s];for(let s=0;s<=r.length;s++)t[0][s]=s;for(let s=1;s<=e.length;s++)for(let n=1;n<=r.length;n++){const d=e[s-1]===r[n-1]?0:1;t[s][n]=Math.min(t[s-1][n]+1,t[s][n-1]+1,t[s-1][n-1]+d)}return t[e.length][r.length]},"levenshtein"),_r=me((r,e,t=3)=>At(r,e,1,t)[0],"suggestTarget"),At=me((r,e,t=3,s=3)=>{const n=[];for(const d of e){const c=Ur(r.toLowerCase(),d.toLowerCase());c<=s&&n.push({distance:c,name:d})}return n.sort((d,c)=>d.distance-c.distance||d.name.localeCompare(c.name)),n.slice(0,t).map(d=>d.name)},"suggestTargets"),Wr=me(r=>r.length===0?" (no targets found)":r.map(e=>` - ${e}`).join(`
2
- `),"formatTargetList"),Vr=me(async r=>{if(r.length===0||!process.stdin.isTTY||!process.stdout.isTTY)return;const e=xr({input:process.stdin,output:process.stdout});try{process.stdout.write(`Available targets:
3
- `);for(const[n,d]of r.entries())process.stdout.write(` ${String(n+1).padStart(2," ")}. ${d}
4
- `);process.stdout.write(`
5
- `);const t=(await new Promise(n=>{e.question("Select a target (number or name, blank to cancel): ",n)})).trim();if(t.length===0)return;const s=Number.parseInt(t,10);return Number.isFinite(s)&&s>=1&&s<=r.length?r[s-1]:r.includes(t)?t:_r(t,r)}finally{e.close()}},"promptTargetInteractively");var zr=Object.defineProperty,Ve=M((r,e)=>zr(r,"name",{value:e,configurable:!0}),"i$1");const Yr=Ve(r=>Array.isArray(r)?`[${r.join(",")}]`:typeof r=="object"&&r!==null?JSON.stringify(r):String(r),"formatValue"),Kr=Ve((r,e,t)=>e==="_"?`${r}${Array.isArray(t)?t.join(" "):String(t)}`:`${r}--${e}=${Yr(t)}`,"formatFlags"),ze=Ve((r,e,t)=>{const s=new Set(new Set(t.map(y=>y.target.target))),n=new Set(new Set(t.map(y=>y.target.project))),d=e.filter(y=>s.has(y)),c=r.filter(y=>n.has(y)),h=t.length-c.length*d.length,m=d.length===1?"target":"targets",p=d.join(", ");let l;l=c.length===1?`project ${c[0]}`:`${c.length} projects`;let P=`${m} ${p} for ${l}`;return h>0&&(P+=` and ${h} ${h===1?"task":"tasks"} ${h===1?"it depends":"they depend"} on`),P},"formatTargetsAndProjects");var Qr=Object.defineProperty,Xr=M((r,e)=>Qr(r,"name",{value:e,configurable:!0}),"c$3");const Jr={error:"red",info:"white",success:"green"},Ne=Xr(({children:r,title:e,variant:t})=>{const s=Jr[t];return a(u,{flexDirection:"column",children:[a(u,{gap:1,children:[i(o,{bold:!0,inverse:!0,children:" VIS "}),i(o,{bold:!0,color:s,children:"•"}),i(o,{children:e})]}),r]})},"Header");var Zr=Object.defineProperty,eo=M((r,e)=>Zr(r,"name",{value:e,configurable:!0}),"s$2");const Dt=eo(({cached:r,failed:e,failedIds:t,projectNames:s,skippedIds:n,succeeded:d,targets:c,tasks:h,took:m})=>{const p=ze(s,c,h);if(e===0&&(!n||n.length===0)){const l=r>0?` (${r} read from cache)`:"";return i(ft,{children:M(()=>i(Ne,{title:`Successfully ran ${p}`,variant:"success",children:a(u,{flexDirection:"column",paddingLeft:2,children:[a(o,{children:[i(o,{color:"green",children:Ot})," ",d+r," ","tasks completed",l?i(o,{dimColor:!0,children:l}):null]}),a(o,{dimColor:!0,children:[" ","Took",m]})]})}),"children")})}return i(ft,{children:M(()=>i(Ne,{title:`Ran ${p}`,variant:"error",children:a(u,{flexDirection:"column",paddingLeft:2,children:[n&&n.length>0&&a(u,{flexDirection:"column",children:[a(o,{dimColor:!0,children:[n.length," ","task",n.length===1?"":"s"," ","skipped (dependency failed or --bail)"]}),n.map(l=>a(o,{dimColor:!0,children:[" - ",l]},l)),i(o,{})]}),e>0&&a(u,{flexDirection:"column",children:[a(o,{children:[i(o,{color:"red",children:String(e)})," ","task",e===1?"":"s"," ","failed:"]}),t.map(l=>a(o,{children:[" ",i(o,{color:"red",children:He})," ",l]},l)),i(o,{})]}),a(o,{dimColor:!0,children:[" ","Took",m]})]})}),"children")})},"CommandSummary");var to=Object.defineProperty,De=M((r,e)=>to(r,"name",{value:e,configurable:!0}),"u$2");class Re{static{M(this,"TaskStore")}static{De(this,"TaskStore")}#e;#r=new Set;#o=new Map;constructor(e){this.#e={autoExitCountdown:null,cached:0,completed:0,done:!1,endTime:null,failed:0,filterActive:!1,filterText:"",focusedPanel:"tasks",interactiveMode:!1,outputs:new Map,pinnedTaskIds:[null,null],rerunRequested:!1,retryTaskId:null,rows:e.map(t=>({status:"pending",taskId:t.id})),selectedIndex:0,startTime:Date.now(),statusFilter:"all",succeeded:0,viewMode:"list"}}getSnapshot=De(()=>this.#e,"getSnapshot");subscribe=De(e=>(this.#r.add(e),()=>{this.#r.delete(e)}),"subscribe");startTasks(e){const t=[...this.#e.rows];for(const s of e){const n=t.findIndex(d=>d.taskId===s.id);n!==-1&&(t[n]={...t[n],elapsed:0,status:"running"},this.#o.set(s.id,process.hrtime()))}this.#t({...this.#e,rows:t})}endTasks(e){const t=[...this.#e.rows];let{cached:s,completed:n,failed:d,succeeded:c}=this.#e;const h=new Map(this.#e.outputs);for(const p of e){const l=t.findIndex(P=>P.taskId===p.task.id);switch(l!==-1&&(t[l]={...t[l],duration:p.startTime&&p.endTime?p.endTime-p.startTime:void 0,status:p.status}),n++,p.status){case"failure":{d++;break}case"local-cache":case"local-cache-kept-existing":case"remote-cache":{s++;break}case"success":{c++;break}}p.terminalOutput&&!h.has(p.task.id)&&h.set(p.task.id,p.terminalOutput),this.#o.delete(p.task.id)}let{selectedIndex:m}=this.#e;if(d>this.#e.failed){const p=t.findIndex(l=>l.status==="failure");p!==-1&&(m=p)}this.#t({...this.#e,cached:s,completed:n,failed:d,outputs:h,rows:t,selectedIndex:m,succeeded:c})}static#i=256*1024;addOutput(e,t){if(!t.trim())return;let s=(this.#e.outputs.get(e)??"")+t;s.length>Re.#i&&(s=s.slice(-Re.#i)),this.#e.outputs.set(e,s),this.#t({...this.#e})}setOutput(e,t){this.#e.outputs.set(e,t),this.#t({...this.#e})}markDone(){this.#t({...this.#e,done:!0,endTime:Date.now()})}tick(){if(this.#o.size===0)return;let e=!1;const t=[...this.#e.rows];for(let s=0;s<t.length;s++){const n=t[s];if(n.status==="running"){const d=this.#o.get(n.taskId);if(d){const c=process.hrtime(d),h=c[0]*1e3+c[1]/1e6;t[s]={...n,elapsed:h},e=!0}}}e&&this.#t({...this.#e,rows:t})}setSelectedIndex(e){e!==this.#e.selectedIndex&&this.#t({...this.#e,selectedIndex:e})}setFocusedPanel(e){e!==this.#e.focusedPanel&&this.#t({...this.#e,focusedPanel:e})}setFilter(e){this.#t({...this.#e,filterText:e,selectedIndex:0})}setFilterActive(e){e!==this.#e.filterActive&&this.#t({...this.#e,filterActive:e,filterText:e?this.#e.filterText:"",selectedIndex:0})}pinTask(e,t){const s=[...this.#e.pinnedTaskIds];s[e]=t,this.#t({...this.#e,pinnedTaskIds:s})}clearPins(){this.#t({...this.#e,pinnedTaskIds:[null,null]})}requestRetry(e){const t=[...this.#e.rows],s=t.findIndex(h=>h.taskId===e);let{completed:n,failed:d,succeeded:c}=this.#e;if(s!==-1){const h=t[s].status;h==="failure"?(d=Math.max(0,d-1),n=Math.max(0,n-1)):h==="success"&&(c=Math.max(0,c-1),n=Math.max(0,n-1)),t[s]={...t[s],elapsed:0,status:"running"},this.#o.set(e,process.hrtime())}this.#t({...this.#e,completed:n,done:!1,endTime:null,failed:d,interactiveMode:!1,retryTaskId:e,rows:t,succeeded:c})}acknowledgeRetry(){const e=this.#e.retryTaskId;return e&&this.#t({...this.#e,retryTaskId:null}),e}setInteractiveMode(e){e!==this.#e.interactiveMode&&this.#t({...this.#e,interactiveMode:e})}setViewMode(e){e!==this.#e.viewMode&&this.#t({...this.#e,viewMode:e})}setStatusFilter(e){this.#t({...this.#e,selectedIndex:0,statusFilter:e})}requestRerun(){this.#o.clear(),this.#t({...this.#e,autoExitCountdown:null,cached:0,completed:0,done:!1,endTime:null,failed:0,interactiveMode:!1,outputs:new Map,rerunRequested:!0,rows:this.#e.rows.map(e=>({status:"pending",taskId:e.taskId})),startTime:Date.now(),succeeded:0,viewMode:"list"})}acknowledgeRerun(){this.#e.rerunRequested&&this.#t({...this.#e,rerunRequested:!1})}#t(e){this.#e=e;for(const t of this.#r)try{t()}catch{}}}var ro=Object.defineProperty,ce=M((r,e)=>ro(r,"name",{value:e,configurable:!0}),"t$1");const oo={d:ce(()=>" d","d"),future:"in %s",h:ce(()=>" h","h"),m:ce(()=>" m","m"),mo:ce(()=>" mo","mo"),ms:ce(()=>" ms","ms"),past:"%s ago",s:ce(()=>" s","s"),w:ce(()=>" w","w"),y:ce(()=>" y","y")},Bt={delimiter:" ",language:oo,largest:2,round:!0,spacer:"",units:["h","m","s","ms"]};ce(r=>{const e=r[0]*1e3+r[1]/1e6;return Pt(e,Bt)},"formatHrtime");const we=ce(r=>Pt(r,Bt),"formatMs");var io=Object.defineProperty,xe=M((r,e)=>io(r,"name",{value:e,configurable:!0}),"n$4");const ve=xe(r=>r==="local-cache"||r==="local-cache-kept-existing"||r==="remote-cache","isCacheStatus"),Ie=xe(r=>{switch(r){case"failure":return{color:"red",icon:He};case"local-cache":case"local-cache-kept-existing":case"remote-cache":case"success":return{color:"green",icon:Ot};case"skipped":return{color:"gray",icon:je};default:return{color:"gray",icon:"?"}}},"getStatusInfo"),Nt=xe(r=>{const{color:e,icon:t}=Ie(r);return X(A.createElement(o,{color:e},t),{columns:10}).trim()},"getStatusIcon"),no=xe(r=>{const{color:e,icon:t}=Ie(r);switch(r){case"local-cache":case"local-cache-kept-existing":case"remote-cache":return X(A.createElement(o,null,A.createElement(o,{color:e},t)," ",A.createElement(o,{color:"cyan"},"[cache]")),{columns:30}).trim();case"skipped":return X(A.createElement(o,null,A.createElement(o,{dimColor:!0},t)," ",A.createElement(o,{dimColor:!0},"[skipped]")),{columns:30}).trim();default:return X(A.createElement(o,{color:e},t),{columns:10}).trim()}},"getStatusPrefix"),so=xe((r,e,t)=>{const s=t.trim();if(!s)return;const n=`
6
- `;if(process.env.GITHUB_ACTIONS==="true")process.stdout.write(`::group::${Nt(e)} ${r}${n}`),process.stdout.write(s+n),process.stdout.write(`::endgroup::${n}`);else{const d=process.stdout.columns||80,c=X(A.createElement(o,{dimColor:!0},je.repeat(d)),{columns:d}).trim(),h=no(e),m=X(A.createElement(o,{bold:!0},r),{columns:d}).trim();process.stdout.write(`${c}${n}`),process.stdout.write(`${h} ${m}${n}`),process.stdout.write(s+n),process.stdout.write(`${c}${n}`)}},"logCommandOutputCI");var lo=Object.defineProperty,qt=M((r,e)=>lo(r,"name",{value:e,configurable:!0}),"c$2");const ao=qt(r=>r==="running"?{color:"white",icon:"•"}:r==="pending"?{color:"gray",icon:"·"}:Ie(r),"getDisplayInfo"),kt=qt(({duration:r,focused:e,interactiveMode:t,output:s,scrollRef:n,showFullscreenHint:d,status:c,taskId:h})=>{const m=c??"pending",{icon:p}=ao(m),l=e?"bold":"single",P=m==="failure"?"red":m==="success"||ve(m)?e?"green":"gray":m==="running"?e?"white":"cyan":e?"white":"gray",y=h?`${p} ${h}`:void 0,O=r===void 0?void 0:we(r),v=h?t?"Esc cancel | Enter send":e&&m==="running"&&d?"⏎ FULLSCREEN i INPUT":e&&m==="running"?"i INPUT":e&&d?"<enter> full screen":e?void 0:"<tab> or <enter> to focus":void 0;if(!h)return a(u,{alignItems:"center",borderColor:"gray",borderStyle:"single",flexDirection:"column",flexGrow:1,justifyContent:"center",paddingX:2,paddingY:1,children:[i(o,{dimColor:!0,children:"Select a task to view output"}),i(o,{dimColor:!0,children:"Press Enter or 1/2 to pin"})]});const S=s?s.split(`
7
- `).map(x=>x.replace(/\r$/,"")):[];return!s&&(m==="running"||m==="pending")?i(u,{borderBottomTitle:v,borderColor:P,borderStyle:l,borderTopRightTitle:O,borderTopTitle:y,flexDirection:"column",flexGrow:1,paddingX:2,paddingY:1,children:i(u,{alignItems:"center",flexGrow:1,justifyContent:"center",children:i(o,{dimColor:!0,children:"Waiting for task output..."})})}):a(u,{borderBottomTitle:v,borderColor:t?"yellow":P,borderStyle:l,borderTopRightTitle:O,borderTopTitle:y,flexDirection:"column",flexGrow:1,children:[i(u,{flexGrow:1,flexShrink:1,paddingY:1,children:i(Et,{flexGrow:1,followOutput:!0,paddingX:2,ref:n,scrollbar:!0,scrollbarColor:"gray",scrollbarStyle:"block",children:S.map((x,R)=>i(o,{children:x},String(R)))})}),t&&i(u,{flexShrink:0,justifyContent:"center",paddingX:1,children:i(o,{bold:!0,color:"yellow",children:"INTERACTIVE — keystrokes forwarded to task — Esc to exit"})})]})},"OutputPanel");var co=Object.defineProperty,Ee=M((r,e)=>co(r,"name",{value:e,configurable:!0}),"u$1");const Gt=6,Ht=8,Ut=10,uo=Ee(r=>r==="running"||r==="pending"?Ft:r==="local-cache"||r==="local-cache-kept-existing"?"Local":r==="remote-cache"?"Remote":je,"getCacheLabel"),yt=Ee((r,e)=>e[0]===r?"[1]":e[1]===r?"[2]":"","getPinLabel"),vt=Ee(({compact:r,isSelected:e,pinLabel:t,row:s})=>{const{status:n,taskId:d}=s,c=e?">":" ";let h;if(n==="running")h=a(o,{bold:!0,color:"white",children:[" ",i(Rr,{type:"dots"})," "]});else if(n==="pending")h=i(o,{bold:!0,color:"gray",children:" · "});else{const{color:p,icon:l}=Ie(n);h=a(o,{bold:!0,color:p,children:[" ",l," "]})}let m=Ft;return n!=="running"&&n!=="pending"?m=s.duration===void 0?je:we(s.duration):n==="running"&&s.elapsed!==void 0&&(m=we(s.elapsed)),a(u,{children:[i(o,{children:c}),i(u,{width:Gt,children:h}),a(u,{flexGrow:1,children:[i(o,{bold:e,inverse:e,children:d}),t?i(o,{dimColor:!0,children:` ${t}`}):null]}),!r&&i(u,{justifyContent:"flex-end",width:Ht,children:i(o,{dimColor:!ve(n),children:uo(n)})}),!r&&i(u,{justifyContent:"flex-end",width:Ut,children:i(o,{dimColor:n==="pending",children:m})})]})},"TaskListRow"),bt=Ee(({compact:r,filterActive:e,filterText:t,focused:s,headerStatus:n,parallelSlots:d=3,pinnedTaskIds:c,rows:h,scrollRef:m,selectedIndex:p,title:l})=>{const P=n==="error"?"red":n==="success"?"green":s?"white":"gray",y=h[p]?.taskId,O=h.filter(x=>x.status==="running"),v=O.length>0||h.some(x=>x.status==="pending"),S=[];if(v)for(let x=0;x<d;x++){const R=O[x];R?S.push(i(vt,{compact:r,isSelected:R.taskId===y,pinLabel:yt(R.taskId,c),row:R},`par-${R.taskId}`)):S.push(a(u,{children:[i(o,{children:" "}),i(u,{width:Gt,children:i(o,{bold:!0,color:"gray",children:" · "})}),i(o,{dimColor:!0,children:"Waiting for task..."})]},`par-empty-${String(x)}`))}return a(u,{borderColor:P,borderStyle:"single",flexDirection:"column",flexGrow:1,children:[a(u,{flexShrink:0,gap:1,paddingX:1,children:[i(o,{bold:!0,inverse:!0,children:" VIS "}),i(o,{children:l}),!r&&a(u,{flexGrow:1,gap:0,justifyContent:"flex-end",children:[i(u,{justifyContent:"flex-end",width:Ht,children:i(o,{dimColor:!0,children:"Cache"})}),i(u,{justifyContent:"flex-end",width:Ut,children:i(o,{dimColor:!0,children:"Duration"})})]})]}),i(Et,{flexGrow:1,flexShrink:1,paddingLeft:1,paddingY:1,ref:m,scrollbar:!0,scrollbarColor:"gray",scrollbarStyle:"block",children:h.map(x=>i(vt,{compact:r,isSelected:x.taskId===y,pinLabel:yt(x.taskId,c),row:x},x.taskId))}),v&&i(u,{borderBottom:!1,borderColor:"gray",borderLeft:!1,borderRight:!1,borderStyle:"single",borderTop:!0,flexDirection:"column",flexShrink:0,paddingLeft:1,paddingY:1,children:S}),e&&a(u,{borderBottom:!1,borderColor:"gray",borderLeft:!1,borderRight:!1,borderStyle:"single",borderTop:!0,flexShrink:0,paddingX:1,children:[i(o,{bold:!0,color:"white",children:"/ "}),i(o,{children:t}),i(o,{inverse:!0,children:" "})]})]})},"TaskListPanel");var ho=Object.defineProperty,fo=M((r,e)=>ho(r,"name",{value:e,configurable:!0}),"y");const Tt=40,Ct=10,St=100,po=fo(({autoExitSeconds:r,parallelSlots:e,projectNames:t,stdinRegistry:s,store:n,targets:d,tasks:c})=>{const{exit:h}=Mr(),{columns:m,rows:p}=jr(),l=Fr(n.subscribe,n.getSnapshot),[P,y]=mt(!1),O=$e(null),v=$e(null),S=$e(null),[x,R]=mt(!1),j=$e({list:0,splitList:0,splitOutput:0}),J=Le(()=>{l.viewMode==="list"?j.current.list=v.current?.getScrollOffset()??0:l.viewMode==="split"&&(j.current.splitList=v.current?.getScrollOffset()??0,j.current.splitOutput=S.current?.getScrollOffset()??0)},[l.viewMode]),E=Le(g=>{setTimeout(()=>{if(g==="list"){const f=j.current.list;v.current?.scrollTo(f)}else if(g==="split"){const f=j.current.splitList;f>0?v.current?.scrollTo(f):v.current?.scrollTo(Math.max(0,n.getSnapshot().selectedIndex-2)),S.current?.scrollTo(j.current.splitOutput)}},0)},[n]),Z=$e(!1);Ae(()=>{l.done&&!Z.current&&(Z.current=!0,r>0&&R(!0)),!l.done&&Z.current&&(Z.current=!1,R(!1))},[l.done,r]);const D=gt(()=>{let g=l.rows;if(l.statusFilter!=="all"&&(g=g.filter(f=>l.statusFilter==="failed"?f.status==="failure":l.statusFilter==="running"?f.status==="running"||f.status==="pending":l.statusFilter==="passed"?f.status==="success"||ve(f.status):!0)),l.filterText){const f=l.filterText.toLowerCase();g=g.filter(k=>k.taskId.toLowerCase().includes(f))}return g},[l.rows,l.filterText,l.statusFilter]),z=gt(()=>l.rows.filter(g=>g.status==="running").length,[l.rows]),ee=(D[l.selectedIndex]??null)?.taskId??null,L=l.pinnedTaskIds[0]??ee,b=L?l.rows.find(g=>g.taskId===L):null,H=L?l.outputs.get(L)??"":"",_=ze(t,d,c),N=l.done?`Completed ${_} (${we((l.endTime??Date.now())-l.startTime)})`:`Running ${_}...`,W=l.done?l.failed>0?"error":"success":"running",fe=Le(g=>{v.current?.scrollTo(Math.max(0,g-2))},[]);if(Ae(()=>{l.interactiveMode&&b?.status!=="running"&&n.setInteractiveMode(!1)},[l.interactiveMode,b?.status,n]),Ae(()=>{if(!L)return;let g=m;l.viewMode==="split"&&m>=St?g=m-Math.floor(m*.4)-2:(l.viewMode==="split"||l.viewMode==="fullscreen")&&(g=m-2);const f=Math.max(1,p-4);s.get(L)?.resize?.(g,f)},[m,p,l.viewMode,L]),pt((g,f)=>{if(f.escape){n.setInteractiveMode(!1);return}if(!L)return;const k=s.get(L);if(!k){n.setInteractiveMode(!1);return}if(f.return)k.write("\r");else if(f.upArrow)k.write("\x1B[A");else if(f.downArrow)k.write("\x1B[B");else if(f.rightArrow)k.write("\x1B[C");else if(f.leftArrow)k.write("\x1B[D");else if(f.backspace)k.write("");else if(f.delete)k.write("\x1B[3~");else if(f.tab)k.write(" ");else if(f.home)k.write("\x1B[H");else if(f.end)k.write("\x1B[F");else if(f.pageUp)k.write("\x1B[5~");else if(f.pageDown)k.write("\x1B[6~");else if(f.ctrl&&g){const re=g.toUpperCase().codePointAt(0);re!==void 0&&re>=65&&re<=90&&k.write(String.fromCodePoint(re-64))}else g&&k.write(g)},{isActive:l.interactiveMode}),pt((g,f)=>{if(g==="c"&&f.ctrl){h();return}if(!x){if(P){f.escape||g==="?"?y(!1):g==="q"?(y(!1),R(!0)):f.downArrow||g==="j"?O.current?.scrollBy(1):f.upArrow||g==="k"?O.current?.scrollBy(-1):f.pageDown?O.current?.scrollBy(5):f.pageUp?O.current?.scrollBy(-5):f.home?O.current?.scrollToTop():f.end&&O.current?.scrollToBottom();return}if(g==="?"){y(!0);return}if(g==="q"){R(!0);return}if(g==="r"&&l.done){n.requestRerun();return}if(g==="R"&&l.done){const k=D[l.selectedIndex];k?.status==="failure"&&n.requestRetry(k.taskId);return}if(g==="F"&&!l.filterActive){const k=["all","failed","running","passed"],re=k.indexOf(l.statusFilter);n.setStatusFilter(k[(re+1)%k.length]);return}if(l.filterActive){if(f.escape){n.setFilterActive(!1);return}if(f.return){n.setFilterActive(!1);return}if(f.backspace){n.setFilter(l.filterText.slice(0,-1));return}if(g&&!f.ctrl&&!f.meta){n.setFilter(l.filterText+g);return}return}if(g==="i"&&b?.status==="running"&&(l.viewMode==="fullscreen"||l.viewMode==="split"&&l.focusedPanel==="output")){n.setInteractiveMode(!0);return}if(l.viewMode==="fullscreen"){if(f.escape){n.setViewMode("split"),E("split");return}if(f.downArrow||g==="j"){S.current?.scrollBy(1);return}if(f.upArrow||g==="k"){S.current?.scrollBy(-1);return}if(f.pageDown||f.ctrl&&g==="d"){S.current?.scrollBy(12);return}if(f.pageUp||f.ctrl&&g==="u"){S.current?.scrollBy(-12);return}if(f.home){S.current?.scrollToTop();return}if(f.end){S.current?.scrollToBottom();return}return}if(l.viewMode==="split"){if(f.tab){const k=l.focusedPanel==="tasks"?"output":"tasks";n.setFocusedPanel(k);return}if(l.focusedPanel==="output"){if(f.escape){n.setFocusedPanel("tasks");return}if(f.return){J(),n.setViewMode("fullscreen");return}if(f.downArrow||g==="j"){S.current?.scrollBy(1);return}if(f.upArrow||g==="k"){S.current?.scrollBy(-1);return}if(f.pageDown||f.ctrl&&g==="d"){S.current?.scrollBy(12);return}if(f.pageUp||f.ctrl&&g==="u"){S.current?.scrollBy(-12);return}if(f.home){S.current?.scrollToTop();return}if(f.end){S.current?.scrollToBottom();return}return}if(f.escape){n.setViewMode("list"),E("list");return}if(f.return){n.setFocusedPanel("output");return}}if(l.viewMode==="list"||l.viewMode==="split"&&l.focusedPanel==="tasks"){if(f.downArrow||g==="j"){const k=Math.min(l.selectedIndex+1,Math.max(0,D.length-1));n.setSelectedIndex(k),fe(k);return}if(f.upArrow||g==="k"){const k=Math.max(l.selectedIndex-1,0);n.setSelectedIndex(k),fe(k);return}if(f.return&&l.viewMode==="list"){J(),j.current.splitList=0,j.current.splitOutput=0,n.setViewMode("split"),n.setFocusedPanel("output"),E("split");return}if(g==="/"){n.setFilterActive(!0);return}if(g==="1"&&ee){n.pinTask(0,ee);return}if(g==="2"&&ee){n.pinTask(1,ee);return}if(g==="0"){n.clearPins();return}f.escape&&l.filterText&&n.setFilter("")}}},{isActive:!l.interactiveMode}),m<Tt||p<Ct)return i(u,{alignItems:"center",height:p,justifyContent:"center",width:m,children:a(o,{color:"yellow",children:["Terminal too small (",m,"x",p,"). Minimum:"," ",Tt,"x",Ct]})});const F=a(u,{gap:1,children:[l.succeeded>0&&a(o,{bold:!0,color:"green",children:["✓"," ",l.succeeded]}),l.failed>0&&a(o,{bold:!0,color:"red",children:["✗"," ",l.failed]}),z>0&&a(o,{color:"cyan",children:["◷"," ",z]}),a(o,{dimColor:!0,children:[l.rows.length," ","total"]}),l.statusFilter!=="all"&&a(o,{color:"yellow",children:["[",l.statusFilter,"]"]})]});let te;if(l.viewMode==="fullscreen")te=[a(u,{gap:1,children:[i(o,{bold:!0,color:"white",children:"Esc"}),i(o,{dimColor:!0,children:"BACK"})]},"esc"),a(u,{gap:1,children:[i(o,{bold:!0,color:"white",children:"↑↓"}),i(o,{dimColor:!0,children:"SCROLL"})]},"scroll"),a(u,{gap:1,children:[i(o,{bold:!0,color:"white",children:"^u ^d"}),i(o,{dimColor:!0,children:"PAGE"})]},"page"),...b?.status==="running"?[a(u,{gap:1,children:[i(o,{bold:!0,color:"white",children:"i"}),i(o,{dimColor:!0,children:"INPUT"})]},"i")]:[],a(u,{gap:1,children:[i(o,{bold:!0,color:"white",children:"q"}),i(o,{dimColor:!0,children:"QUIT"})]},"q")];else if(l.done){const g=D[l.selectedIndex]?.status==="failure";te=[a(u,{gap:1,children:[i(o,{bold:!0,color:"white",children:"q"}),i(o,{dimColor:!0,children:"QUIT"})]},"q"),a(u,{gap:1,children:[i(o,{bold:!0,color:"white",children:"r"}),i(o,{dimColor:!0,children:"RERUN"})]},"r"),...g?[a(u,{gap:1,children:[i(o,{bold:!0,color:"white",children:"R"}),i(o,{dimColor:!0,children:"RETRY"})]},"R")]:[],a(u,{gap:1,children:[i(o,{bold:!0,color:"white",children:"?"}),i(o,{dimColor:!0,children:"HELP"})]},"?"),a(u,{gap:1,children:[i(o,{bold:!0,color:"white",children:"↑↓"}),i(o,{dimColor:!0,children:"NAV"})]},"nav"),a(u,{gap:1,children:[i(o,{bold:!0,color:"white",children:"F"}),i(o,{dimColor:!0,children:"FILTER"})]},"F"),a(u,{gap:1,children:[i(o,{bold:!0,color:"white",children:"⏎"}),i(o,{dimColor:!0,children:l.viewMode==="list"?"OUTPUT":"FULLSCREEN"})]},"enter")]}else l.viewMode==="split"&&l.focusedPanel==="output"?te=[a(u,{gap:1,children:[i(o,{bold:!0,color:"white",children:"q"}),i(o,{dimColor:!0,children:"QUIT"})]},"q"),a(u,{gap:1,children:[i(o,{bold:!0,color:"white",children:"Esc"}),i(o,{dimColor:!0,children:"BACK"})]},"esc"),a(u,{gap:1,children:[i(o,{bold:!0,color:"white",children:"↑↓"}),i(o,{dimColor:!0,children:"SCROLL"})]},"scroll"),...b?.status==="running"?[a(u,{gap:1,children:[i(o,{bold:!0,color:"white",children:"i"}),i(o,{dimColor:!0,children:"INPUT"})]},"i")]:[],a(u,{gap:1,children:[i(o,{bold:!0,color:"white",children:"⏎"}),i(o,{dimColor:!0,children:"FULLSCREEN"})]},"enter"),a(u,{gap:1,children:[i(o,{bold:!0,color:"white",children:"Tab"}),i(o,{dimColor:!0,children:"PANEL"})]},"tab"),a(u,{gap:1,children:[i(o,{bold:!0,color:"white",children:"?"}),i(o,{dimColor:!0,children:"HELP"})]},"?")]:te=[a(u,{gap:1,children:[i(o,{bold:!0,color:"white",children:"q"}),i(o,{dimColor:!0,children:"QUIT"})]},"q"),a(u,{gap:1,children:[i(o,{bold:!0,color:"white",children:"?"}),i(o,{dimColor:!0,children:"HELP"})]},"?"),a(u,{gap:1,children:[i(o,{bold:!0,color:"white",children:"↑↓"}),i(o,{dimColor:!0,children:"NAV"})]},"nav"),a(u,{gap:1,children:[i(o,{bold:!0,color:"white",children:"/"}),i(o,{dimColor:!0,children:"FILTER"})]},"/"),a(u,{gap:1,children:[i(o,{bold:!0,color:"white",children:"F"}),i(o,{dimColor:!0,children:"STATUS"})]},"F"),a(u,{gap:1,children:[i(o,{bold:!0,color:"white",children:"⏎"}),i(o,{dimColor:!0,children:l.viewMode==="list"?"OUTPUT":"FULLSCREEN"})]},"enter"),...l.viewMode==="split"?[a(u,{gap:1,children:[i(o,{bold:!0,color:"white",children:"Tab"}),i(o,{dimColor:!0,children:"PANEL"})]},"tab")]:[]];const K=a(u,{borderBottom:!1,borderColor:"gray",borderLeft:!1,borderRight:!1,borderStyle:"single",flexShrink:0,justifyContent:"space-between",children:[i(u,{flexGrow:1,flexWrap:"wrap",gap:2,paddingX:1,children:te}),i(u,{flexShrink:0,paddingX:1,children:F})]}),ne=a(Er,{backgroundColor:"#1e1e1e",footer:a(o,{dimColor:!0,children:[i(o,{bold:!0,color:"white",children:"↑↓"})," ","scroll"," ",i(o,{bold:!0,color:"white",children:"?"}),"/",i(o,{bold:!0,color:"white",children:"Esc"})," ","close"]}),scrollRef:O,title:"KEYBOARD SHORTCUTS",visible:P,width:52,children:[a(u,{flexDirection:"column",marginBottom:1,children:[a(u,{marginBottom:1,children:[i(o,{dimColor:!0,children:"── "}),i(o,{bold:!0,color:"white",children:"NAVIGATION"})]}),a(u,{children:[i(u,{width:24,children:a(o,{children:[a(o,{bold:!0,color:"white",children:[" ","↑","/k"]}),i(o,{dimColor:!0,children:" Move up"})]})}),a(o,{children:[a(o,{bold:!0,color:"white",children:[" ","↓","/j"]}),i(o,{dimColor:!0,children:" Move down"})]})]}),a(o,{children:[a(o,{bold:!0,color:"white",children:[" ","Tab"]}),i(o,{dimColor:!0,children:" Switch panel (split)"})]}),a(o,{children:[a(o,{bold:!0,color:"white",children:[" ","Esc"]}),i(o,{dimColor:!0,children:" Back / close"})]}),a(o,{children:[a(o,{bold:!0,color:"white",children:[" ","Enter"]}),i(o,{dimColor:!0,children:" Show output / fullscreen"})]})]}),a(u,{flexDirection:"column",marginBottom:1,children:[a(u,{marginBottom:1,children:[i(o,{dimColor:!0,children:"── "}),i(o,{bold:!0,color:"white",children:"VIEWS"})]}),a(o,{children:[a(o,{bold:!0,color:"white",children:[" ","Enter"]}),a(o,{dimColor:!0,children:[" ","List"," ","→"," ","Split"," ","→"," ","Fullscreen"]})]}),a(o,{children:[a(o,{bold:!0,color:"white",children:[" ","Esc"]}),a(o,{dimColor:!0,children:[" ","Fullscreen"," ","→"," ","Split"," ","→"," ","List"]})]})]}),a(u,{flexDirection:"column",marginBottom:1,children:[a(u,{marginBottom:1,children:[i(o,{dimColor:!0,children:"── "}),i(o,{bold:!0,color:"white",children:"ACTIONS"})]}),a(u,{children:[i(u,{width:24,children:a(o,{children:[a(o,{bold:!0,color:"white",children:[" ","/"]}),i(o,{dimColor:!0,children:" Filter by text"})]})}),a(o,{children:[a(o,{bold:!0,color:"white",children:[" ","F"]}),i(o,{dimColor:!0,children:" Filter by status"})]})]}),a(u,{children:[i(u,{width:24,children:a(o,{children:[a(o,{bold:!0,color:"white",children:[" ","1"]}),i(o,{dimColor:!0,children:"/"}),i(o,{bold:!0,color:"white",children:"2"}),i(o,{dimColor:!0,children:" Pin to output pane"})]})}),a(o,{children:[a(o,{bold:!0,color:"white",children:[" ","0"]}),i(o,{dimColor:!0,children:" Clear pins"})]})]}),a(u,{children:[i(u,{width:24,children:a(o,{children:[a(o,{bold:!0,color:"white",children:[" ","r"]}),i(o,{dimColor:!0,children:" Rerun all (done)"})]})}),a(o,{children:[a(o,{bold:!0,color:"white",children:[" ","R"]}),i(o,{dimColor:!0,children:" Retry failed task"})]})]}),a(o,{children:[a(o,{bold:!0,color:"white",children:[" ","i"]}),i(o,{dimColor:!0,children:" Interactive input (running task)"})]})]}),a(u,{flexDirection:"column",marginBottom:1,children:[a(u,{marginBottom:1,children:[i(o,{dimColor:!0,children:"── "}),i(o,{bold:!0,color:"white",children:"SCROLLING"}),i(o,{dimColor:!0,children:" (output panel)"})]}),a(u,{children:[i(u,{width:24,children:a(o,{children:[a(o,{bold:!0,color:"white",children:[" ","↑","/k"]}),i(o,{dimColor:!0,children:" Scroll up"})]})}),a(o,{children:[a(o,{bold:!0,color:"white",children:[" ","↓","/j"]}),i(o,{dimColor:!0,children:" Scroll down"})]})]}),a(u,{children:[i(u,{width:24,children:a(o,{children:[a(o,{bold:!0,color:"white",children:[" ","^u"]}),i(o,{dimColor:!0,children:" Page up"})]})}),a(o,{children:[a(o,{bold:!0,color:"white",children:[" ","^d"]}),i(o,{dimColor:!0,children:" Page down"})]})]}),a(u,{children:[i(u,{width:24,children:a(o,{children:[a(o,{bold:!0,color:"white",children:[" ","Home"]}),i(o,{dimColor:!0,children:" Top"})]})}),a(o,{children:[a(o,{bold:!0,color:"white",children:[" ","End"]}),i(o,{dimColor:!0,children:" Bottom"})]})]})]}),a(u,{flexDirection:"column",children:[a(u,{marginBottom:1,children:[i(o,{dimColor:!0,children:"── "}),i(o,{bold:!0,color:"white",children:"GENERAL"})]}),a(u,{children:[i(u,{width:24,children:a(o,{children:[a(o,{bold:!0,color:"white",children:[" ","q"]}),i(o,{dimColor:!0,children:" Quit"})]})}),a(o,{children:[a(o,{bold:!0,color:"white",children:[" ","?"]}),i(o,{dimColor:!0,children:" Toggle help"})]})]})]})]}),de=i(ar,{autoExitSeconds:r>0?r:3,onCancel:M(()=>{R(!1)},"onCancel"),visible:x});if(l.viewMode==="fullscreen")return a(u,{flexDirection:"column",height:p,width:m,children:[i(u,{flexGrow:1,children:i(kt,{duration:b?.duration??b?.elapsed,focused:!0,interactiveMode:l.interactiveMode,output:H,scrollRef:S,status:b?.status,taskId:L})}),K,de,ne]});if(l.viewMode==="split"){const g=m>=St,f=i(bt,{compact:!0,filterActive:l.filterActive,filterText:l.filterText,focused:l.focusedPanel==="tasks",headerStatus:W,parallelSlots:e,pinnedTaskIds:l.pinnedTaskIds,rows:D,scrollRef:v,selectedIndex:l.selectedIndex,title:N}),k=i(kt,{duration:b?.duration??b?.elapsed,focused:l.focusedPanel==="output",interactiveMode:l.interactiveMode,output:H,scrollRef:S,showFullscreenHint:!0,status:b?.status,taskId:L});if(g){const ke=Math.floor(m*.4);return a(u,{flexDirection:"column",height:p,width:m,children:[a(u,{flexDirection:"row",flexGrow:1,children:[i(u,{width:ke,children:f}),i(u,{flexGrow:1,children:k})]}),K,de,ne]})}const re=Math.floor(p*.45);return a(u,{flexDirection:"column",height:p,width:m,children:[i(u,{height:re,children:f}),i(u,{flexGrow:1,children:k}),K,de,ne]})}return a(u,{flexDirection:"column",height:p,width:m,children:[i(u,{flexGrow:1,children:i(bt,{filterActive:l.filterActive,filterText:l.filterText,focused:!0,headerStatus:W,parallelSlots:e,pinnedTaskIds:l.pinnedTaskIds,rows:D,scrollRef:v,selectedIndex:l.selectedIndex,title:N})}),K,de,ne]})},"VisTaskRunnerApp");var mo=Object.defineProperty,ye=M((r,e)=>mo(r,"name",{value:e,configurable:!0}),"n$3");const go=ye(r=>{const{args:e,autoExit:t=!1,outputStyle:s="normal",projectNames:n,stdinRegistry:d,tasks:c}=r,h=new Re(c),m=typeof e.parallel=="number"?e.parallel:3,p=t===!0?3:typeof t=="number"?t:0;let l,P;const y=new Promise(E=>{P=E});let O;const v=ye(()=>{O&&(clearInterval(O),O=void 0)},"cleanup"),S=ye(()=>{if(d){for(const E of d.values())E.kill?.();d.clear()}},"killAllPtyProcesses"),x=ye(()=>{v(),J(),S(),process.stdout.write("\x1B[?1049l\x1B[?25h"),l?.cleanup(),process.exit(1)},"onSignal"),R=ye(()=>{const E=h.getSnapshot(),Z=we(Date.now()-E.startTime),D=E.rows.filter(L=>L.status==="failure").map(L=>L.taskId),z=process.stdout.columns||80;process.stdout.write(`
8
- `);for(const L of E.rows){const{status:b,taskId:H}=L,_=Ie(b);let N="";switch(b){case"local-cache":case"local-cache-kept-existing":{N=" [local cache]";break}case"remote-cache":{N=" [remote cache]";break}case"skipped":{N=" [skipped]";break}}const W=X(A.createElement(o,null," ",A.createElement(o,{color:_.color},_.icon),` vis run ${H}`,N?A.createElement(o,{dimColor:!0},` ${N}`):null),{columns:z});process.stdout.write(`${W}
9
- `)}process.stdout.write(`
10
- `);const ee=X(A.createElement(Dt,{cached:E.cached,failed:E.failed,failedIds:D,projectNames:n,succeeded:E.succeeded,targets:e.targets,tasks:c,took:Z}),{columns:z});if(process.stdout.write(`${ee}
11
- `),D.length>0&&s!=="quiet")for(const L of D){const b=E.outputs.get(L);if(b?.trim()){const H=X(A.createElement(o,null,`
12
- `,A.createElement(o,{bold:!0,color:"red"},` ${He} vis run ${L}`)),{columns:z});process.stdout.write(`${H}
13
-
14
- `);const _=b.trim().split(`
15
- `).map(N=>` ${N}`).join(`
16
- `);process.stdout.write(`${_}
17
- `)}}},"printExitSummary");let j;const J=ye(()=>{j&&(clearInterval(j),j=void 0)},"clearKeepAlive");return{lifeCycle:{endCommand(){v(),h.markDone(),j||(j=setInterval(()=>{},1e3)),process.stdin.isTTY&&typeof process.stdin.setRawMode=="function"&&(process.stdin.setRawMode(!0),process.stdin.ref(),process.stdin.resume())},endTasks(E){h.endTasks(E)},printTaskTerminalOutput(E,Z,D){h.getSnapshot().outputs.has(E.id)||h.addOutput(E.id,D)},startCommand(){process.on("SIGINT",x),process.on("SIGTERM",x),j||(j=setInterval(()=>{},1e3)),process.stdin.isTTY&&typeof process.stdin.setRawMode=="function"&&(process.stdin.setRawMode(!0),process.stdin.ref(),process.stdin.resume()),l=Or(A.createElement(po,{autoExitSeconds:p,parallelSlots:m,projectNames:n,stdinRegistry:d??new Map,store:h,targets:e.targets,tasks:c}),{alternateScreen:!0,exitOnCtrlC:!1,interactive:!0,patchConsole:!0}),l.waitUntilExit().then(()=>{J(),S(),process.removeListener("SIGINT",x),process.removeListener("SIGTERM",x),R(),P()},()=>{J(),S(),process.removeListener("SIGINT",x),process.removeListener("SIGTERM",x),P()})},startTasks(E){h.startTasks(E),O||(O=setInterval(()=>{h.tick()},100))}},renderIsDone:y,store:h}},"createDynamicOutputRenderer");var wo=Object.defineProperty,Ye=M((r,e)=>wo(r,"name",{value:e,configurable:!0}),"l$1");const ko=Ye(r=>r==="quiet"?"quiet":"normal","parseOutputStyle"),yo=Ye((r,e)=>{const t=r.overrides.visOptions;return t?.outputStyle==="normal"||t?.outputStyle==="quiet"?t.outputStyle:e},"resolveTaskOutputStyle");class vo{static{M(this,"StaticOutputLifeCycle")}static{Ye(this,"StaticOutputLifeCycle")}#e;#r;#o;#i=[];#t=[];#n=new Map;#s;#l;#a=0;constructor(e){this.#e=e.projectNames,this.#r=e.args.targets,this.#o=e.tasks,this.#s=e.logReporter,this.#l=e.outputStyle??"normal"}startCommand(){this.#a=Date.now();const e=process.stdout.columns||80,t=`Running ${ze(this.#e,this.#r,this.#o)}`,s=X(A.createElement(Ne,{title:t,variant:"info"}),{columns:e});process.stdout.write(s);const n=this.#o[0],d=n?.overrides?Object.entries(n.overrides).filter(([c])=>c!=="command"):[];if(d.length>0){process.stdout.write(`
18
- With additional flags:
19
- `);for(const[c,h]of d)process.stdout.write(`${Kr(" ",c,h)}
20
- `)}process.stdout.write(`
21
- `)}startTasks(e){const t=process.stdout.columns||80;for(const s of e){const n=X(A.createElement(o,null,A.createElement(o,{dimColor:!0},">"),` ${s.id}`),{columns:t});process.stdout.write(`${n}
22
- `)}}endTasks(e){const t=process.stdout.columns||80;for(const s of e){this.#n.set(s.task.id,s),s.status==="failure"?this.#i.push(s):ve(s.status)&&this.#t.push(s);const n=Nt(s.status),d=s.startTime&&s.endTime?` (${we(s.endTime-s.startTime)})`:"",c=ve(s.status)?" [cache]":"",h=X(A.createElement(o,null,n,` ${s.task.id}`,c?A.createElement(o,{color:"cyan"},c):null,d?A.createElement(o,{dimColor:!0},d):null),{columns:t});process.stdout.write(`${h}
23
- `)}}printTaskTerminalOutput(e,t,s){if(!(yo(e,this.#l)==="quiet"&&(t==="success"||ve(t)))){if(this.#s){this.#s.printTaskTerminalOutput(e,t,s);return}so(e.id,t,s)}}endCommand(){const e=we(Date.now()-this.#a),t=this.#o.filter(d=>!this.#n.has(d.id)).map(d=>d.id);process.stdout.write(`
24
- `);const s=process.stdout.columns||80,n=X(A.createElement(Dt,{cached:this.#t.length,failed:this.#i.length,failedIds:this.#i.map(d=>d.task.id),projectNames:this.#e,skippedIds:t.length>0?t:void 0,succeeded:this.#n.size-this.#i.length-this.#t.length,targets:this.#r,tasks:this.#o,took:e}),{columns:s});process.stdout.write(`${n}
25
- `)}}var bo=Object.defineProperty,Oe=M((r,e)=>bo(r,"name",{value:e,configurable:!0}),"t");const To=Oe(()=>Pr(),"createVisHooks"),Co=Oe(async(r,e)=>{if(!(!e||e.length===0))for(const t of e){if(t.hooks)for(const[s,n]of Object.entries(t.hooks)){const d=Array.isArray(n)?n:[n];for(const c of d)r.hook(s,c)}t.setup&&await t.setup(r)}},"registerPlugins");class So{static{M(this,"HookableLifeCycle")}static{Oe(this,"HookableLifeCycle")}#e;#r;#o=new Map;constructor(e,t){this.#e=e,this.#r=t}startTasks(e){for(const t of e)this.#o.set(t.id,t),this.#i("task:before",t)}endTasks(e){for(const t of e)this.#o.delete(t.task.id),this.#i("task:after",t.task,t),t.status==="failure"?this.#i("task:failure",t.task,t):$o(t.status)&&this.#i("task:cacheHit",t.task,t)}printCacheMiss(e,t){this.#i("task:cacheMiss",e,t)}onTaskStdout(e,t){this.#i("task:stdout",e,t)}onTaskStderr(e,t){this.#i("task:stderr",e,t)}#i(e,...t){Promise.resolve(this.#e.callHook(e,...t)).catch(s=>{if(this.#r)try{this.#r(e,s)}catch{}})}}const $o=Oe(r=>r==="local-cache"||r==="local-cache-kept-existing"||r==="remote-cache","isCacheStatus");var xo=Object.defineProperty,Fe=M((r,e)=>xo(r,"name",{value:e,configurable:!0}),"o");const Io=Fe(async r=>{if(process.env.VIS_NO_SHELL_HISTORY||Lr()==="win32")return;const e=process.env.SHELL;if(!e)return;const t=Ar(e);try{if(t==="zsh"){await Ro(r);return}if(t==="bash"){await Mo(r);return}t==="fish"&&await jo(r)}catch{}},"appendToShellHistory"),Ro=Fe(async r=>{const e=process.env.HISTFILE??We(process.env.ZDOTDIR??_e(),".zsh_history"),t=`: ${Math.floor(Date.now()/1e3)}:0;${r}
26
- `;await Ue(e,t)},"writeZshHistory"),Mo=Fe(async r=>{const e=process.env.HISTFILE??We(_e(),".bash_history");await Ue(e,`${r}
27
- `)},"writeBashHistory"),jo=Fe(async r=>{const e=We(_e(),".local","share","fish","fish_history"),t=`- cmd: ${r.replaceAll("\\","\\\\").replaceAll(`
28
- `,String.raw`\n`)}
29
- when: ${Math.floor(Date.now()/1e3)}
30
- `;await Ue(e,t)},"writeFishHistory");var Eo=Object.defineProperty,Be=M((r,e)=>Eo(r,"name",{value:e,configurable:!0}),"e");const Oo=Be(r=>{const{killGracePeriodMs:e=5e3,onTimeout:t,sendSignal:s,timeoutMs:n}=r;if(n<=0)return{cancel:Be(()=>{},"cancel")};const d=Math.max(e,0);let c;const h=setTimeout(()=>{t?.(),s("SIGTERM"),d>0&&(c=setTimeout(()=>{s("SIGKILL")},d))},n);return{cancel:Be(()=>{clearTimeout(h),c&&clearTimeout(c)},"cancel")}},"scheduleTimeoutKill");var Fo=Object.defineProperty,be=M((r,e)=>Fo(r,"name",{value:e,configurable:!0}),"l");const Po=[/node_modules(?:\/|$)/,/\.git(?:\/|$)/,/\.vis(?:\/|$)/,/\.task-runner(?:\/|$)/],$t=be((r,e)=>{const t=new Set,s=new Set;for(const[,c]of r){const h=c.task.hashDetails?.nodes;if(h)for(const m of Object.keys(h))t.add(m),s.add(Dr(Lt(e,m)))}const n=[...s].sort(),d=[];for(const c of n)d.some(h=>c===h||c.startsWith(`${h}/`))||d.push(c);return{directories:d,files:t}},"collectTrackedWatchTargets"),Lo=be((r,e,t)=>{const s=new Set;for(const n of r){const d=Lt(e,n);for(const c of t)if(d===c||d.startsWith(`${c}/`)){const h=Br(c,d);h.length>0&&s.add(h)}}return n=>{const d=n.replaceAll("\\","/");return s.has(d)}},"createTrackedFileFilter"),Ao=be(r=>{const e=r.replaceAll("\\","/");return Po.some(t=>t.test(e))},"shouldIgnore"),xt=be(r=>{const{debounceMs:e=150,filter:t,onChange:s,paths:n}=r,d=[];let c=new Set,h;const m=be(()=>{h=void 0;const p=[...c];c=new Set,p.length!==0&&Promise.resolve(s(p)).catch(l=>{console.error("[vis watch] onChange handler failed:",l)})},"flush");for(const p of n)try{const l=Nr(p,{recursive:!0},(P,y)=>{y&&(Ao(y)||t&&!t(y)||(c.add(y),h&&clearTimeout(h),h=setTimeout(m,e)))});d.push(l)}catch(l){console.warn(`[vis watch] unable to watch ${p}: ${l.message}`)}return{close:be(()=>{h&&clearTimeout(h);for(const p of d)try{p.close()}catch{}},"close")}},"startWatcher");var Do=Object.defineProperty,Bo=M((r,e)=>Do(r,"name",{value:e,configurable:!0}),"n$1");const No=Bo((r,e)=>{const t=e?.trim();if(!t)return{filter:void 0,tasks:[...r]};const s=t.toLowerCase();return{filter:t,tasks:r.filter(n=>n.target.project.toLowerCase().includes(s))}},"applyProjectFilter");var qo=Object.defineProperty,he=M((r,e)=>qo(r,"name",{value:e,configurable:!0}),"n");const Go=[""," Watch keybinds:"," r, Enter rerun"," a rerun all (clear filter)"," p filter by project name"," q, Ctrl+C quit"," h, ? show this help",""].join(`
31
- `),Ho=he(r=>{r.write(`${Go}
32
- `)},"writeHelp"),Uo=he(async(r,e)=>{e.write("filter projects (empty to cancel) > ");const t=r,s=t.isRaw===!0;if(s)try{t.setRawMode?.(!1)}catch{}return await new Promise(n=>{let d="";const c=he(h=>{d+=typeof h=="string"?h:h.toString("utf8");const m=d.indexOf(`
33
- `);if(m===-1)return;const p=d.slice(0,m).replace(/\r$/,"").trim();if(r.off("data",c),s)try{t.setRawMode?.(!0)}catch{}n(p.length>0?p:void 0)},"onData");r.on("data",c)})},"defaultPromptFilter"),_o=he(r=>{const{handlers:e}=r,t=r.input??process.stdin,s=r.output??process.stdout,n=r.promptFilter??Uo;if(!t||t.isTTY===!1)return{close:he(()=>{},"close")};Ir(t);const d=t.isRaw===!0;try{t.setRawMode?.(!0)}catch{return{close:he(()=>{},"close")}}typeof t.resume=="function"&&t.resume();let c=!1;const h=he(async p=>{if(p.ctrl===!0&&p.name==="c"){await e.onQuit();return}if(!c)switch(p.name){case"?":case"h":{await e.onHelp();break}case"a":{await e.onClearFilter();break}case"p":{c=!0;try{const l=await n(t,s);l===void 0?s.write(`filter cancelled.
34
- `):await e.onFilter(l)}finally{c=!1}break}case"q":{await e.onQuit();break}case"r":{await e.onRerun();break}case"return":{await e.onRerun();break}}},"dispatch"),m=he((p,l)=>{h(l).catch(P=>{s.write(`[vis watch] keybind handler failed: ${P.message}
35
- `)})},"onKeypress");return t.on("keypress",m),{close:he(()=>{if(t.off("keypress",m),!d)try{t.setRawMode?.(!1)}catch{}const p=t;if(typeof p.pause=="function")try{p.pause()}catch{}},"close")}},"installKeybinds");var Wo=Object.defineProperty,C=M((r,e)=>Wo(r,"name",{value:e,configurable:!0}),"i");const Ke="VIS_AFFECTED_FILES",_t=C((r,e,t)=>t||!e?r:e.startsWith("/")?e:`${r}/${e}`,"resolveCwd"),It=C(async(r,e,t,s)=>{const n=r.map(d=>{const c=d.overrides.command;if(!c)return;const h=d.overrides.visOptions,m=_t(e,d.projectRoot,!!h?.runFromWorkspaceRoot),p=h?.envFile?jt(m,h.envFile):{},l=t&&(h?.affectedFiles==="env"||h?.affectedFiles==="both")?{[Ke]:t.join(`
36
- `)}:{};return{command:c,cwd:m,env:{INIT_CWD:s,...p,...l},name:d.id}}).filter(d=>d!==void 0);n.length!==0&&await Ge(n,{killOthers:["failure"]})},"runPersistentTasks"),qe=256*1024;class Vo{static{M(this,"Me")}static{C(this,"OutputRingBuffer")}#e;#r="";#o=!1;constructor(e){this.#e=e}append(e){this.#r+=e,this.#r.length>this.#e&&(this.#r=this.#r.slice(-this.#e),this.#o=!0)}toString(){return this.#o?`[...output truncated, showing last ${Math.round(this.#e/1024)}KB...]
37
- ${this.#r}`:this.#r}}const Wt=C(r=>{const e=r.overrides.visOptions;if(e&&typeof e=="object")return e},"getTaskOptions"),Qe=C(r=>`'${r.replaceAll("'",String.raw`'\''`)}'`,"singleQuoteEscape"),zo=C((r,e,t)=>{if(!e||e.length===0||t===!1||t===void 0)return r;if(t==="args"||t==="both"){const s=e.map(Qe).join(" ");return`${r} ${s}`}return r},"buildAffectedFilesArgs"),Vt="visForwardedArgs",Yo=C((r,e)=>{const t=e.overrides[Vt];if(!Array.isArray(t)||t.length===0)return r;const s=t.map(Qe).join(" ");return`${r} ${s}`},"appendForwardedArgs"),Ko=C(async(r,e,t)=>{if(!e)return t();const s=r.get(e)??Promise.resolve();let n;const d=new Promise(h=>{n=h}),c=s.then(()=>d);r.set(e,c),await s;try{return await t()}finally{n(),r.get(e)===c&&r.delete(e)}},"withMutex"),Qo=C(r=>{let e=Math.max(0,Math.floor(r));return{claim(t){const s=Math.max(0,Math.min(t,e));return e-=s,s},get remaining(){return e}}},"createRetryBudget"),Xo=C((r,e,t)=>{const s=`${e}\0${typeof t=="string"?t:String(t)}`,n=r.get(s);if(n)return n;const d=jt(e,t);return r.set(s,d),d},"loadEnvFileCached"),Rt=C(r=>{const e=new Map;return async(t,s)=>{const{affectedFiles:n,currentOs:d,initCwd:c,lifeCycle:h,mutexPool:m,onOutput:p,onOutputReplace:l,retryBudget:P,stdinRegistry:y,workspaceRoot:O}=r,v=Wt(t),S=_t(O,s.cwd??t.projectRoot,v?.runFromWorkspaceRoot===!0),x=t.overrides.command;if(!x)return{code:0,terminalOutput:`No command configured for ${t.target.project}:${t.target.target}`};const R=Yo(x,t),j=zo(R,n,v?.affectedFiles),J=cr(v,d),E=J?`${J} -c ${Qe(j)}`:j,Z=v?.envFile?Xo(e,S,v.envFile):void 0,D={};n&&n.length>0&&(v?.affectedFiles==="env"||v?.affectedFiles==="both")&&(D[Ke]=n.join(`
38
- `));const z={INIT_CWD:c,...Z,...s.env,...D},ee=v?.pty===!0,L=!!y,b=L||ee;b&&(t.cache=!1);const H=b?void 0:new Vo(qe),_=b?new Mt(qe):void 0;let N;const W=C(F=>{if(F.kind==="started"&&(N=F.kill,F.write&&y&&y.set(t.id,{kill:F.kill,resize:F.resize,write:F.write})),(F.kind==="stdout"||F.kind==="stderr")&&F.text!==void 0)if(F.kind==="stdout"?h?.onTaskStdout?.(t,F.text):h?.onTaskStderr?.(t,F.text),_)_.write(F.text),L&&l?.(t.id,_.toString());else{const te=`${F.text}
39
- `;H.append(te),p?.(t.id,te)}F.kind==="close"&&y&&y.delete(t.id)},"onEvent"),fe=C(async()=>{const F=v?.retryCount??0,te=v?.retryDelay,K=P?P.claim(F):F,ne=typeof v?.timeout=="number"&&v.timeout>0?v.timeout:0,de=typeof v?.killGracePeriodMs=="number"&&v.killGracePeriodMs>=0?v.killGracePeriodMs:5e3;let g=!1;const f=Oo({killGracePeriodMs:de,onTimeout:C(()=>{g=!0},"onTimeout"),sendSignal:C(Te=>{N?.(Te)},"sendSignal"),timeoutMs:ne});let k;try{k=await Ge([{command:E,cwd:S,env:z,name:t.id,...b?{ptySize:{cols:process.stdout.columns??80,rows:process.stdout.rows??24},stdin:"pty"}:{}}],{killOthers:["failure"],onEvent:W,...K>0?{restart:{delay:te??"exponential",tries:K}}:{}})}finally{f.cancel()}const re=k.closeEvents[0],ke=_?_.toString():H.toString();return g?{code:124,terminalOutput:`${ke}
40
- [timeout] Task "${t.id}" exceeded ${ne}ms budget.
41
- `}:{code:re?.exitCode??1,terminalOutput:ke}},"runOnce");return m?Ko(m,v?.mutex,fe):fe()}},"createConcurrentExecutor"),Jo=C(r=>{if(!r||r.trim().length===0)return;const e=Number.parseFloat(r);return!Number.isFinite(e)||e<=0?{invalid:r}:{value:Math.floor(e)}},"parseEnvConcurrency"),Zo=C(async(r,e)=>{const t=await er(r);if(!t){e.warn("No previous run recorded yet. Run a task at least once to populate .task-runner/last-summary.json.");return}const s=(t.duration/1e3).toFixed(2);if(e.info(""),e.info(`Last run — ${t.startTime} (${s}s)`),e.info(""),e.info(` Total: ${String(t.stats.total)}`),e.info(` Succeeded: ${String(t.stats.succeeded)}`),e.info(` Cached: ${String(t.stats.cached)}`),e.info(` Failed: ${String(t.stats.failed)}`),e.info(` Skipped: ${String(t.stats.skipped)}`),e.info(""),t.stats.failed>0){const d=t.tasks.filter(c=>c.exitCode!==void 0&&c.exitCode!==0);e.info("Failed tasks:");for(const c of d){const h=c.duration??0;e.info(` × ${c.taskId} (exit ${String(c.exitCode??-1)}, ${h}ms)`)}e.info("")}const n=[...t.tasks].filter(d=>typeof d.duration=="number").sort((d,c)=>(c.duration??0)-(d.duration??0)).slice(0,5);if(n.length>0){e.info("Slowest tasks:");for(const d of n)e.info(` ${d.taskId.padEnd(40)} ${String(d.duration??0).padStart(6)}ms [${d.cacheStatus}]`);e.info("")}},"renderLastRunSummary"),gi=C(async({argument:r,logger:e,options:t,runtime:s,visConfig:n,workspaceRoot:d})=>{if(!d)throw new Error("Could not determine workspace root. Run this command inside a monorepo.");const c=d;if(t.lastDetails===!0){await Zo(c,e);return}const h=process.cwd(),m=await dr(c),{config:p,packageJsons:l,projectOptions:P,workspace:y}=ur(c,n,m),O=hr(c,y,l);let v=r[0];if(!v){const w=wt(y);if(process.stdout.isTTY&&process.stdin.isTTY){const T=await Vr(w);if(!T){e.info("No target selected.");return}v=T,await Io(`vis run ${T}`)}else{e.info("Available targets:"),e.info(""),e.info(Wr(w)),e.info(""),e.info("Usage: vis run <target>");return}}if(p.constraints&&!t.skipConstraints){const w=tr(O,p.constraints);if(w.length>0){for(const T of w)e.error(`[${T.rule}] ${T.message}`);throw new Error(`${w.length} project constraint violation(s) found. Use --skip-constraints to bypass.`)}}if(t.affected){const w=[v];t.parallel!==void 0&&w.push(`--parallel=${String(t.parallel)}`),t.cache||w.push("--no-cache"),t.query&&w.push(`--query=${String(t.query)}`),await s.runCommand("affected",{argv:w});return}const S=await Sr(v,y,process.cwd(),c),x=Gr(P),R=Hr(S.target,x);R!==S.target&&e.debug?.(`Resolved alias "${S.target}" → "${R}"`);let j=S.projects;const J=r.slice(1).map(String);if(t.projects){const w=new Set(t.projects.split(",").map(T=>T.trim()));if(j=j.filter(T=>w.has(T)),j.length===0)throw new Error(`No matching projects found for: ${String(t.projects)}`)}if(t.query&&(j=$r(j,y,t.query),j.length===0)){e.info(`Query "${String(t.query)}" matched no projects.`);return}const E=fr(),Z=process.env[Ke],D=Z?Z.split(`
42
- `).filter(Boolean):void 0,z=[],ee=new Map;for(const w of j){const T=P.get(w)?.[R];if(!T)continue;const $=T.options;if(!$?.internal){if(!pr($,!!ht)){e.debug?.(`Skipping ${w}:${R} — runInCI filter`);continue}z.push(w),ee.set(w,T)}}if(z.length===0){const w=wt(y),T=Object.entries(y.projects).filter(([,$])=>$.targets?.[R]!==void 0).map(([$])=>$);if(e.error(`No projects have the "${R}" target.`),T.length>0){e.info(""),e.info(`Target "${R}" exists in these projects but was filtered out:`);for(const $ of T.slice(0,5))e.info(` - ${$}`);T.length>5&&e.info(` …and ${T.length-5} more`)}else{const $=At(R,w,3);$.length>0&&(e.info(""),e.info($.length===1?`Did you mean "${$[0]}"?`:`Did you mean one of: ${$.map(q=>`"${q}"`).join(", ")}?`)),e.info(""),e.info("Run `vis run` without arguments to see all available targets.")}return}const L=t.pty===!0;let b=z.map(w=>{const T=y.projects[w],$=ee.get(w),q={project:w,target:R},G=`${w}:${R}`,ue=L?{...$.options,pty:$.options?.pty??!0}:$.options;return{cache:$.cache,id:G,outputs:$.outputs??[],overrides:{command:$.command,...J.length>0?{[Vt]:J}:{},...ue?{visOptions:ue}:{}},parallelism:$.parallelism,projectRoot:T?.root,target:q}});const H=[],_=[];for(const w of b)Wt(w)?.persistent?(w.cache=!1,H.push(w)):_.push(w);b=_;const N=rr(t.partition);if(N&&(b=or.partitionTasks(b,N),e.info(`Partition ${N.index}/${N.total}: running ${b.length} task(s)`),b.length===0)){e.info("No tasks assigned to this partition.");return}const W=ir(b,{projectGraph:O,targetDefaults:p.targetDefaults,workspace:y});if(t.dryRun){const w=Object.keys(W.tasks).length,T=W.roots.length;e.info(`Execution plan (${String(w)} task(s), ${String(T)} root(s)):`),e.info("");const $=new Set,q=C((G,ue)=>{if($.has(G))return;$.add(G);for(const ge of W.dependencies[G]??[])q(ge,ue+1);const se=W.tasks[G],oe=" ".repeat(ue+1);e.info(`${oe}${G}${se?.cache===!1?" (no-cache)":""}`)},"walkPlan");for(const G of W.roots)q(G,0);H.length>0&&(e.info(""),e.info(` + ${String(H.length)} persistent task(s) (run after graph completes)`)),e.info("");return}await Cr(c,p.toolchain,{error:C(w=>{e.error(w)},"error"),info:C(w=>{e.info(w)},"info"),warn:C(w=>{e.warn(w)},"warn")},!!t.skipToolchain);const fe=Date.now(),F=To(),te=C((w,T)=>{const $=T instanceof Error?T.message:String(T);e.warn(`Plugin error in ${w}: ${$}`)},"onHookError");await Co(F,p.plugins);const K=typeof t.profile=="string"?t.profile:void 0,ne=C(async w=>{if(!K)return;const T=ct(w,W,fe),$=K.startsWith("/")?K:`${c}/${K}`;await nr(T,$),e.info(`Profile written to ${K}`)},"maybeWriteProfile"),de=p.taskRunnerOptions??{},g=mr(c,t.cacheDir,de.cacheDirectory,p.sharedWorktreeCache),f=gr(g,c,p.branchScopedCache),k=Jo(process.env.VIS_RUN_CONCURRENCY_LIMIT);k&&"invalid"in k&&e.warn(`VIS_RUN_CONCURRENCY_LIMIT=${k.invalid} is not a positive number; falling back to default concurrency.`);const re=k&&"value"in k?k.value:void 0,ke=t.parallel??re??3,Te={dryRun:t.dryRun??!1,parallel:ke,skipNxCache:!t.cache,summarize:t.summarize??!1,...de,cacheDirectory:f},zt=process.stdout.isTTY&&!ht,Yt=p.tui?.autoExit??!1,Xe={args:{parallel:Te.parallel,targets:[R]},autoExit:Yt,projectNames:z,tasks:b},Je=typeof t.retryBudget=="number"?t.retryBudget:void 0,Ze=Je===void 0?void 0:Qo(Je),et=new So(F,te),tt=new wr(c),rt=ko(typeof t.outputStyle=="string"?t.outputStyle.toLowerCase():void 0);if(await F.callHook("run:before",{tasks:b,workspaceRoot:c}),zt){const w=new Map,T=go({...Xe,outputStyle:rt,stdinRegistry:w}),{lifeCycle:$,store:q}=T,G=new dt([$,et,tt]),ue=Rt({affectedFiles:D,currentOs:E,initCwd:h,lifeCycle:G,mutexPool:new Map,onOutput:C((I,B)=>{q.addOutput(I,B)},"onOutput"),onOutputReplace:C((I,B)=>{q.setOutput(I,B)},"onOutputReplace"),retryBudget:Ze,stdinRegistry:w,workspaceRoot:c});let se="rerun",oe=null,ge=new Map;for(;se!=="quit";){if(se==="rerun")ge=await ut(b,Te,{lifeCycle:G,projectGraph:O,taskExecutor:ue,taskGraph:W,workspaceRoot:c});else if(se==="retry"&&oe){const I=b.find(Y=>Y.id===oe),B=I?.overrides.command;if(I&&B){const Y=I.projectRoot??c,pe=Y.startsWith("/")?Y:`${c}/${Y}`;G.startTasks?.([I]);const ie=new Mt(qe),le=(await Ge([{command:B,cwd:pe,name:I.id,ptySize:{cols:process.stdout.columns??80,rows:process.stdout.rows??24},stdin:"pty"}],{onEvent:C(U=>{U.kind==="started"&&U.write&&w.set(I.id,{kill:U.kill,resize:U.resize,write:U.write}),(U.kind==="stdout"||U.kind==="stderr")&&U.text&&(ie.write(U.text),q.setOutput(I.id,ie.toString())),U.kind==="close"&&w.delete(I.id)},"onEvent")})).closeEvents[0];G.endTasks?.([{code:le?.exitCode??1,status:le?.exitCode===0?"success":"failure",task:I,terminalOutput:q.getSnapshot().outputs.get(I.id)}])}else I&&G.endTasks?.([{code:1,status:"failure",task:I,terminalOutput:`No command configured for ${I.id}`}]);oe=null,q.markDone()}se=await new Promise(I=>{const B=q.subscribe(()=>{const Y=q.getSnapshot();Y.rerunRequested&&(q.acknowledgeRerun(),B(),I("rerun")),Y.retryTaskId&&(oe=q.acknowledgeRetry(),B(),I("retry"))});T.renderIsDone.then(()=>{B(),I("quit")},()=>{B(),I("quit")})})}await T.renderIsDone,await F.callHook("run:after",ge),await ne(ge),H.length>0&&!t.failFast&&await It(H,c,D,h)}else{const w=new Map,T=typeof t.log=="string"?t.log.toLowerCase():"",$=T==="labeled"||T==="grouped"||T==="interleaved"?T:void 0,q=$?sr($):void 0,G=new dt([new vo({...Xe,logReporter:q,outputStyle:rt}),et,tt]),ue=Rt({affectedFiles:D,currentOs:E,initCwd:h,lifeCycle:G,mutexPool:w,retryBudget:Ze,workspaceRoot:c}),se=C(async()=>{const I=Date.now(),B=await ut(b,Te,{lifeCycle:G,projectGraph:O,taskExecutor:ue,taskGraph:W,workspaceRoot:c}),Y=Date.now()-I;if(t.summarize){const ae=ct(B,W,fe);await lr(ae,c)}let pe=!1;for(const[,ae]of B)ae.status==="failure"&&(pe=!0);const ie=kr(B,Y),le=yr(c),U=vr(c,Y,le);return e.info(""),e.info(` ${ie}${U?` ${U}`:""}`),{hasFailure:pe,results:B,runHistory:le}},"runOnce"),oe=await se();await F.callHook("run:after",oe.results),await ne(oe.results);const{hasFailure:ge}=oe;if(t.watch){const I=z.map(V=>{const Q=y.projects[V]?.root;if(Q)return Q.startsWith("/")?Q:`${c}/${Q}`}).filter(V=>V!==void 0),B=b;let Y;const pe=C(V=>{const Q=No(B,V);return Y=Q.filter,b=Q.tasks,Q.tasks.length},"applyFilter");let ie=!1,le=oe.results;const U=C(()=>{const V=$t(le,c);if(V.directories.length>0&&V.files.size>0){const Q=Lo(V.files,c,V.directories);return{handle:xt({filter:Q,onChange:ot,paths:V.directories}),mode:"tracked"}}return{handle:xt({onChange:ot,paths:I}),mode:"roots"}},"buildHandle");let ae;const ot=C(async V=>{if(!ie){ie=!0;try{e.info(`Change detected in ${V.length} file(s), rerunning…`),le=(await se()).results,ae?.close(),ae=U().handle}finally{ie=!1}}},"onChangeHandler"),it=U();ae=it.handle;const Kt=it.mode==="tracked"?`Watching ${String($t(le,c).files.size)} tracked file(s)`:`Watching ${String(I.length)} project root(s)`;e.info(`${Kt} — edit a file to rerun, press h for keybinds, q to quit.`);const Pe=C(async()=>{if(!ie){ie=!0;try{if(b.length===0){e.info("No tasks match the active filter — press a to clear it.");return}le=(await se()).results,ae?.close(),ae=U().handle}finally{ie=!1}}},"triggerRerun");await new Promise(V=>{let Q=!1,nt;const st=C(()=>{lt()},"onSigint"),lt=C(()=>{Q||(Q=!0,nt?.close(),process.off("SIGINT",st),ae?.close(),V())},"exit");process.on("SIGINT",st),nt=_o({handlers:{onClearFilter:C(async()=>{const Ce=pe(void 0);e.info(`Filter cleared — running ${String(Ce)} task(s).`),await Pe()},"onClearFilter"),onFilter:C(async Ce=>{const Qt=Y,at=pe(Ce);if(at===0){e.info(`Filter "${Ce}" matched no projects — keeping previous filter.`),pe(Qt);return}e.info(`Filter "${Ce}" matched ${String(at)} task(s).`),await Pe()},"onFilter"),onHelp:C(()=>{Ho(process.stdout)},"onHelp"),onQuit:C(()=>{lt()},"onQuit"),onRerun:Pe}})});return}if(ge){if(t.flaky!==!1){const I=br(c,{minRuns:2},oe.runHistory);if(I.length>0){e.info(""),e.info("Flaky tasks (based on historical runs):"),e.info("");for(const B of Tr(I))e.info(` ${B}`);e.info("")}}throw new Error("Some tasks failed.")}H.length>0&&!t.failFast&&await It(H,c,D,h)}},"execute");export{Qo as createRetryBudget,gi as default};
1
+ import { createRequire as __cjs_createRequire } from "node:module";
2
+
3
+ const __cjs_require = __cjs_createRequire(import.meta.url);
4
+
5
+ const __cjs_getProcess = typeof globalThis !== "undefined" && typeof globalThis.process !== "undefined" ? globalThis.process : process;
6
+
7
+ const __cjs_getBuiltinModule = (module) => {
8
+ // Check if we're in Node.js and version supports getBuiltinModule
9
+ if (typeof __cjs_getProcess !== "undefined" && __cjs_getProcess.versions && __cjs_getProcess.versions.node) {
10
+ const [major, minor] = __cjs_getProcess.versions.node.split(".").map(Number);
11
+ // Node.js 20.16.0+ and 22.3.0+
12
+ if (major > 22 || (major === 22 && minor >= 3) || (major === 20 && minor >= 16)) {
13
+ return __cjs_getProcess.getBuiltinModule(module);
14
+ }
15
+ }
16
+ // Fallback to createRequire
17
+ return __cjs_require(module);
18
+ };
19
+
20
+ import { dim, bold, cyan, green, red, yellow } from '@visulima/colorize';
21
+ import { findPackageManagerSync } from '@visulima/package';
22
+ import { join, resolve } from '@visulima/path';
23
+ import { Box, Text, ScrollView, Tabs, Tab, Spinner, ScrollBar, useApp, useWindowSize, useInput, Dialog, render } from '@visulima/tui';
24
+ import { j as scoreColor, _ as QuitDialog, D as DEFAULT_LOW_SCORE_THRESHOLD, p as pail, c as detectPm, i as isInCi, S as SYMBOLS, x as runInstall, a as buildSocketOptions, f as fetchSocketReports } from './bin.js';
25
+ import React, { useSyncExternalStore, useState, useEffect, useRef, useMemo, useCallback } from 'react';
26
+ const {
27
+ rmSync,
28
+ writeFileSync,
29
+ statSync,
30
+ readFileSync
31
+ } = __cjs_getBuiltinModule("node:fs");
32
+ const {
33
+ homedir
34
+ } = __cjs_getBuiltinModule("node:os");
35
+ import { x as xxh3Hash } from '../packem_shared/xxh3-Ck8mXNg1.js';
36
+ import { isAccessibleSync, readJsonSync, ensureDirSync } from '@visulima/fs';
37
+ import { findVisConfigFile } from './applyDefaults.js';
38
+ import { d as discoverWorkspacePackages, c as collectDepsFromPkgJson, r as readLockfileText, b as buildE18eEntries, a as buildSocketEntries, e as applyOverrides, f as runCodemod, m as markCodemodAvailability } from './handler41.js';
39
+ import { c as checkRuntimeVersions } from '../packem_shared/runtime-check-Cobi3p6l.js';
40
+ const {
41
+ spawnSync
42
+ } = __cjs_getBuiltinModule("node:child_process");
43
+ import { l as lockedPackages, s as startScanProgress, f as findDuplicateDependencies } from '../packem_shared/dependency-scan-A0KSklpG.js';
44
+ import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
45
+ import { u as useMeasuredHeight } from '../packem_shared/use-measured-height-CNP0vT4M.js';
46
+ import { r as readCatalogs, l as loadNpmrc, c as checkOutdated, a as fetchVulnerabilities } from '../packem_shared/catalog-BJTtyi-O.js';
47
+
48
+ const getCacheDirectory = () => join(homedir(), ".vis", "cache", "doctor");
49
+ const DEFAULT_TTL_MS = 30 * 60 * 1e3;
50
+ const safeMtime = (path) => {
51
+ if (!path) {
52
+ return "";
53
+ }
54
+ try {
55
+ return String(statSync(path).mtimeMs);
56
+ } catch {
57
+ return "";
58
+ }
59
+ };
60
+ const CACHE_SCHEMA_VERSION = 2;
61
+ const buildDoctorCacheKey = (input) => {
62
+ const payload = JSON.stringify({
63
+ configMtime: safeMtime(input.configPath),
64
+ lockfileMtime: safeMtime(input.lockfilePath),
65
+ schema: CACHE_SCHEMA_VERSION,
66
+ sections: [...input.sections].toSorted(),
67
+ socketEnabled: input.socketEnabled,
68
+ workspaceRoot: input.workspaceRoot
69
+ });
70
+ return xxh3Hash(Buffer.from(payload));
71
+ };
72
+ const readDoctorCache = (cacheKey) => {
73
+ const filePath = join(getCacheDirectory(), `${cacheKey}.json`);
74
+ if (!isAccessibleSync(filePath)) {
75
+ return void 0;
76
+ }
77
+ try {
78
+ const entry = readJsonSync(filePath);
79
+ if (Date.now() - entry.createdAt > entry.ttlMs) {
80
+ rmSync(filePath, { force: true });
81
+ return void 0;
82
+ }
83
+ return { ...entry.results, sections: new Set(entry.results.sections) };
84
+ } catch {
85
+ rmSync(filePath, { force: true });
86
+ return void 0;
87
+ }
88
+ };
89
+ const writeDoctorCache = (cacheKey, results, ttlMs = DEFAULT_TTL_MS) => {
90
+ ensureDirSync(getCacheDirectory());
91
+ const entry = {
92
+ createdAt: Date.now(),
93
+ results: { ...results, sections: [...results.sections] },
94
+ ttlMs
95
+ };
96
+ writeFileSync(join(getCacheDirectory(), `${cacheKey}.json`), JSON.stringify(entry, void 0, 2), "utf8");
97
+ };
98
+
99
+ const ORPHANS_DIAGNOSTIC_ID = "orphans";
100
+ const checkInotifyCapacity = () => {
101
+ if (process.platform !== "linux") {
102
+ return {
103
+ id: "inotify",
104
+ message: "inotify capacity check skipped (not Linux).",
105
+ status: "skip"
106
+ };
107
+ }
108
+ let maxWatches;
109
+ try {
110
+ const raw = readFileSync("/proc/sys/fs/inotify/max_user_watches", "utf8").trim();
111
+ const parsed = Number.parseInt(raw, 10);
112
+ if (Number.isFinite(parsed) && parsed > 0) {
113
+ maxWatches = parsed;
114
+ }
115
+ } catch {
116
+ return {
117
+ id: "inotify",
118
+ message: "Could not read /proc/sys/fs/inotify/max_user_watches.",
119
+ status: "warn"
120
+ };
121
+ }
122
+ if (maxWatches === void 0) {
123
+ return {
124
+ id: "inotify",
125
+ message: "inotify max_user_watches reported a non-numeric value.",
126
+ status: "warn"
127
+ };
128
+ }
129
+ if (maxWatches < 65536) {
130
+ return {
131
+ detail: { maxWatches },
132
+ id: "inotify",
133
+ message: `inotify watcher limit is ${String(maxWatches)} — large monorepos can exhaust this. Bump now with \`sudo sysctl fs.inotify.max_user_watches=524288\` and persist via \`/etc/sysctl.d/99-vis.conf\` so it survives reboot.`,
134
+ status: "warn"
135
+ };
136
+ }
137
+ return {
138
+ detail: { maxWatches },
139
+ id: "inotify",
140
+ message: `inotify capacity OK (${String(maxWatches)} watches).`,
141
+ status: "ok"
142
+ };
143
+ };
144
+ const checkTtyAvailability = () => {
145
+ const stdinIsTty = Boolean(process.stdin.isTTY);
146
+ const stdoutIsTty = Boolean(process.stdout.isTTY);
147
+ if (stdinIsTty && stdoutIsTty) {
148
+ return {
149
+ id: "tty",
150
+ message: "Interactive TTY available — watch keybinds enabled.",
151
+ status: "ok"
152
+ };
153
+ }
154
+ if (!stdinIsTty && !stdoutIsTty) {
155
+ return {
156
+ id: "tty",
157
+ message: "No TTY on stdin/stdout — running in CI / piped mode (keybinds disabled).",
158
+ status: "skip"
159
+ };
160
+ }
161
+ return {
162
+ detail: { stdin: String(stdinIsTty), stdout: String(stdoutIsTty) },
163
+ id: "tty",
164
+ message: stdinIsTty ? "stdin is a TTY but stdout is not — output is being captured; keybinds still work." : "stdout is a TTY but stdin is not — keybinds disabled (input is piped).",
165
+ status: "skip"
166
+ };
167
+ };
168
+ const listOrphanPids = () => {
169
+ const selfPid = process.pid;
170
+ try {
171
+ return process.platform === "win32" ? listOrphansWindows(selfPid) : listOrphansUnix(selfPid);
172
+ } catch {
173
+ return [];
174
+ }
175
+ };
176
+ const checkOrphanedRunners = () => {
177
+ const selfPid = process.pid;
178
+ let pids;
179
+ try {
180
+ pids = process.platform === "win32" ? listOrphansWindows(selfPid) : listOrphansUnix(selfPid);
181
+ } catch {
182
+ return {
183
+ id: ORPHANS_DIAGNOSTIC_ID,
184
+ message: "Could not enumerate processes (ps/tasklist failed).",
185
+ status: "warn"
186
+ };
187
+ }
188
+ if (pids.length === 0) {
189
+ return {
190
+ id: ORPHANS_DIAGNOSTIC_ID,
191
+ message: "No orphaned vis/task-runner processes detected.",
192
+ status: "ok"
193
+ };
194
+ }
195
+ if (pids.length <= 2) {
196
+ return {
197
+ detail: { count: pids.length, pids: pids.join(",") },
198
+ id: ORPHANS_DIAGNOSTIC_ID,
199
+ message: `${String(pids.length)} possibly orphaned process(es) detected (PIDs: ${pids.join(", ")}). Likely benign.`,
200
+ status: "skip"
201
+ };
202
+ }
203
+ const killSnippet = process.platform === "win32" ? pids.map((p) => `taskkill /F /PID ${String(p)}`).join(" & ") : `kill ${pids.join(" ")}`;
204
+ return {
205
+ detail: { count: pids.length, pids: pids.join(",") },
206
+ id: "orphans",
207
+ message: `${String(pids.length)} possibly orphaned vis/task-runner processes — run \`vis doctor --fix\` to clean them up, or kill them manually: ${killSnippet}`,
208
+ status: "warn"
209
+ };
210
+ };
211
+ const killOrphanedRunners = (options = {}) => {
212
+ const enumerate = options.enumerate ?? listOrphanPids;
213
+ const force = options.force === true;
214
+ const signal = force ? "SIGKILL" : "SIGTERM";
215
+ const killFn = options.kill ?? defaultKill;
216
+ const pids = enumerate();
217
+ const killed = [];
218
+ const failed = [];
219
+ for (const pid of pids) {
220
+ try {
221
+ killFn(pid, signal);
222
+ killed.push(pid);
223
+ } catch (error) {
224
+ const reason = error.code ?? error.message;
225
+ if (reason === "ESRCH") {
226
+ killed.push(pid);
227
+ continue;
228
+ }
229
+ failed.push({ pid, reason });
230
+ }
231
+ }
232
+ return { failed, killed };
233
+ };
234
+ const defaultTaskkillRunner = (args) => spawnSync("taskkill", args, { encoding: "utf8" });
235
+ const defaultProcessKill = (pid, signal) => {
236
+ process.kill(pid, signal);
237
+ };
238
+ const killViaTaskkill = (pid, signal, runner = defaultTaskkillRunner) => {
239
+ const args = signal === "SIGKILL" ? ["/F", "/PID", String(pid)] : ["/PID", String(pid)];
240
+ const result = runner(args);
241
+ if (result.error) {
242
+ throw result.error;
243
+ }
244
+ if (typeof result.status === "number" && result.status !== 0) {
245
+ const code = result.status === 128 ? "ESRCH" : `taskkill exited with code ${String(result.status)}`;
246
+ const error = new Error(code);
247
+ error.code = code;
248
+ throw error;
249
+ }
250
+ };
251
+ const killViaSignal = (pid, signal, kill = defaultProcessKill) => {
252
+ kill(pid, signal);
253
+ };
254
+ const defaultKill = (pid, signal) => {
255
+ if (process.platform === "win32") {
256
+ killViaTaskkill(pid, signal);
257
+ return;
258
+ }
259
+ killViaSignal(pid, signal);
260
+ };
261
+ const runProcessListing = (command, args) => {
262
+ const result = spawnSync(command, args, { encoding: "utf8" });
263
+ if (result.error) {
264
+ throw result.error;
265
+ }
266
+ if (typeof result.status === "number" && result.status !== 0) {
267
+ throw new Error(`${command} exited with code ${String(result.status)}`);
268
+ }
269
+ return typeof result.stdout === "string" ? result.stdout : "";
270
+ };
271
+ const listOrphansUnix = (selfPid) => {
272
+ const stdout = runProcessListing("ps", ["-Ao", "pid=,command="]);
273
+ const pids = [];
274
+ for (const line of stdout.split("\n")) {
275
+ if (line.length === 0) {
276
+ continue;
277
+ }
278
+ const match = /^\s*(\d+)\s+(.+)$/.exec(line);
279
+ if (!match) {
280
+ continue;
281
+ }
282
+ const pid = Number.parseInt(match[1] ?? "", 10);
283
+ const command = (match[2] ?? "").toLowerCase();
284
+ if (!Number.isFinite(pid) || pid === selfPid) {
285
+ continue;
286
+ }
287
+ if (/(?:^|[ /])vis-native(?:\s|$|[-.])/.test(command) || /(?:^|[ /])vis\s+run\b/.test(command) || /(?:^|[ /])task-runner(?:\s|$|[-.])/.test(command)) {
288
+ pids.push(pid);
289
+ }
290
+ }
291
+ return pids;
292
+ };
293
+ const listOrphansWindows = (selfPid) => {
294
+ const stdout = runProcessListing("tasklist", ["/FO", "CSV", "/NH"]);
295
+ const pids = [];
296
+ for (const line of stdout.split(/\r?\n/)) {
297
+ if (line.length === 0) {
298
+ continue;
299
+ }
300
+ const cells = line.split(/","/).map((cell) => cell.replaceAll(/^"|"$/g, ""));
301
+ const image = (cells[0] ?? "").toLowerCase();
302
+ const pid = Number.parseInt(cells[1] ?? "", 10);
303
+ if (!Number.isFinite(pid) || pid === selfPid) {
304
+ continue;
305
+ }
306
+ if (image === "vis.exe" || image.startsWith("vis-native") || image.includes("task-runner")) {
307
+ pids.push(pid);
308
+ }
309
+ }
310
+ return pids;
311
+ };
312
+ const runRuntimeDiagnostics = () => [checkInotifyCapacity(), checkTtyAvailability(), checkOrphanedRunners()];
313
+
314
+ const FILTER_TABS = [
315
+ { id: "dependencies", label: "Deps" },
316
+ { id: "security", label: "Security" },
317
+ { id: "optimization", label: "Optimize" },
318
+ { id: "runtime", label: "Runtime" }
319
+ ];
320
+ const SECTION_ORDER = ["dependencies", "security", "optimization", "runtime"];
321
+ const groupBySection = (findings) => {
322
+ const map = /* @__PURE__ */ new Map();
323
+ for (const section of SECTION_ORDER) {
324
+ map.set(section, []);
325
+ }
326
+ for (const finding of findings) {
327
+ map.get(finding.section).push(finding);
328
+ }
329
+ for (const [section, items] of map) {
330
+ if (items.length === 0) {
331
+ map.delete(section);
332
+ }
333
+ }
334
+ return map;
335
+ };
336
+ const filterFindings = (findings, filterType, filterText, severityFilter) => {
337
+ let filtered = findings.filter((f) => f.section === filterType);
338
+ if (severityFilter) {
339
+ filtered = filtered.filter((f) => f.severity === severityFilter);
340
+ }
341
+ if (filterText) {
342
+ const lower = filterText.toLowerCase();
343
+ filtered = filtered.filter((f) => f.title.toLowerCase().includes(lower));
344
+ }
345
+ return [...filtered];
346
+ };
347
+ const initialStatus = (activeSections) => {
348
+ const status = {
349
+ dependencies: "idle",
350
+ optimization: "idle",
351
+ runtime: "idle",
352
+ security: "idle"
353
+ };
354
+ for (const id of SECTION_ORDER) {
355
+ if (activeSections.has(id)) {
356
+ status[id] = "idle";
357
+ }
358
+ }
359
+ return status;
360
+ };
361
+ class DoctorStore {
362
+ #state;
363
+ #listeners = /* @__PURE__ */ new Set();
364
+ constructor(input = []) {
365
+ const options = Array.isArray(input) ? { findings: input } : input;
366
+ const findings = options.findings ?? [];
367
+ const activeSections = options.activeSections ?? new Set(SECTION_ORDER);
368
+ const initialFilter = SECTION_ORDER.find((id) => activeSections.has(id)) ?? "dependencies";
369
+ const seed = filterFindings(findings, initialFilter, "", void 0);
370
+ const status = initialStatus(activeSections);
371
+ if (findings.length > 0) {
372
+ for (const finding of findings) {
373
+ status[finding.section] = "done";
374
+ }
375
+ }
376
+ this.#state = {
377
+ all: findings,
378
+ entries: seed,
379
+ filterActive: false,
380
+ filterText: "",
381
+ filterType: initialFilter,
382
+ focusedPanel: "list",
383
+ grouped: groupBySection(seed),
384
+ pendingAction: void 0,
385
+ sectionError: {},
386
+ sectionMessage: {},
387
+ sectionStatus: status,
388
+ selectedIndex: 0,
389
+ severityFilter: void 0
390
+ };
391
+ }
392
+ getSnapshot = () => this.#state;
393
+ subscribe = (listener) => {
394
+ this.#listeners.add(listener);
395
+ return () => {
396
+ this.#listeners.delete(listener);
397
+ };
398
+ };
399
+ setSelectedIndex(index) {
400
+ const clamped = Math.max(0, Math.min(index, this.#state.entries.length - 1));
401
+ if (clamped !== this.#state.selectedIndex) {
402
+ this.#emit({ ...this.#state, selectedIndex: clamped });
403
+ }
404
+ }
405
+ setFocusedPanel(panel) {
406
+ if (panel !== this.#state.focusedPanel) {
407
+ this.#emit({ ...this.#state, focusedPanel: panel });
408
+ }
409
+ }
410
+ setFilterType(type) {
411
+ if (type === this.#state.filterType) {
412
+ return;
413
+ }
414
+ const newEntries = filterFindings(this.#state.all, type, this.#state.filterText, this.#state.severityFilter);
415
+ this.#emit({
416
+ ...this.#state,
417
+ entries: newEntries,
418
+ filterType: type,
419
+ grouped: groupBySection(newEntries),
420
+ selectedIndex: 0
421
+ });
422
+ }
423
+ setFilter(text) {
424
+ const newEntries = filterFindings(this.#state.all, this.#state.filterType, text, this.#state.severityFilter);
425
+ this.#emit({
426
+ ...this.#state,
427
+ entries: newEntries,
428
+ filterText: text,
429
+ grouped: groupBySection(newEntries),
430
+ selectedIndex: 0
431
+ });
432
+ }
433
+ setFilterActive(active) {
434
+ if (active === this.#state.filterActive) {
435
+ return;
436
+ }
437
+ if (active) {
438
+ this.#emit({ ...this.#state, filterActive: true });
439
+ return;
440
+ }
441
+ const newEntries = filterFindings(this.#state.all, this.#state.filterType, "", this.#state.severityFilter);
442
+ this.#emit({
443
+ ...this.#state,
444
+ entries: newEntries,
445
+ filterActive: false,
446
+ filterText: "",
447
+ grouped: groupBySection(newEntries),
448
+ selectedIndex: 0
449
+ });
450
+ }
451
+ setPendingAction(action) {
452
+ this.#emit({ ...this.#state, pendingAction: action });
453
+ }
454
+ setSeverityFilter(severity) {
455
+ if (severity === this.#state.severityFilter) {
456
+ return;
457
+ }
458
+ const newEntries = filterFindings(this.#state.all, this.#state.filterType, this.#state.filterText, severity);
459
+ this.#emit({
460
+ ...this.#state,
461
+ entries: newEntries,
462
+ grouped: groupBySection(newEntries),
463
+ selectedIndex: 0,
464
+ severityFilter: severity
465
+ });
466
+ }
467
+ startSection(section, message) {
468
+ this.#emit({
469
+ ...this.#state,
470
+ sectionMessage: { ...this.#state.sectionMessage, [section]: message },
471
+ sectionStatus: { ...this.#state.sectionStatus, [section]: "running" }
472
+ });
473
+ }
474
+ completeSection(section, findings) {
475
+ const newAll = [...this.#state.all, ...findings];
476
+ const newEntries = filterFindings(newAll, this.#state.filterType, this.#state.filterText, this.#state.severityFilter);
477
+ const nextMessage = { ...this.#state.sectionMessage };
478
+ delete nextMessage[section];
479
+ this.#emit({
480
+ ...this.#state,
481
+ all: newAll,
482
+ entries: newEntries,
483
+ grouped: groupBySection(newEntries),
484
+ sectionMessage: nextMessage,
485
+ sectionStatus: { ...this.#state.sectionStatus, [section]: "done" }
486
+ });
487
+ }
488
+ failSection(section, error) {
489
+ this.#emit({
490
+ ...this.#state,
491
+ sectionError: { ...this.#state.sectionError, [section]: error },
492
+ sectionStatus: { ...this.#state.sectionStatus, [section]: "error" }
493
+ });
494
+ }
495
+ #emit(newState) {
496
+ this.#state = newState;
497
+ for (const listener of this.#listeners) {
498
+ try {
499
+ listener();
500
+ } catch {
501
+ }
502
+ }
503
+ }
504
+ }
505
+
506
+ const SEVERITY_RANK = { error: 0, warn: 1 };
507
+ const isAcknowledged = (entry) => Boolean(entry.acceptedRisk);
508
+ const flattenFindings = (results) => {
509
+ const findings = [];
510
+ if (results.sections.has("dependencies")) {
511
+ for (const entry of results.outdated) {
512
+ findings.push({
513
+ entry,
514
+ id: `outdated:${entry.packageName}`,
515
+ kind: "outdated",
516
+ section: "dependencies",
517
+ severity: "warn",
518
+ subtitle: `${entry.currentRange} → ${entry.newRange} (${entry.updateType})`,
519
+ title: entry.packageName
520
+ });
521
+ }
522
+ for (const pkg of results.duplicates) {
523
+ findings.push({
524
+ id: `duplicate:${pkg.name}`,
525
+ kind: "duplicate",
526
+ pkg,
527
+ section: "dependencies",
528
+ severity: "warn",
529
+ subtitle: `${String(pkg.versions.length)} versions installed`,
530
+ title: pkg.name
531
+ });
532
+ }
533
+ }
534
+ if (results.sections.has("security")) {
535
+ for (const entry of results.outdated) {
536
+ if (entry.vulnerabilities && entry.vulnerabilities.length > 0) {
537
+ const top = entry.vulnerabilities[0];
538
+ const sev = isAcknowledged(entry) ? "warn" : "error";
539
+ const count = entry.vulnerabilities.length;
540
+ findings.push({
541
+ entry,
542
+ id: `vuln:${entry.packageName}`,
543
+ kind: "vulnerability",
544
+ packageName: entry.packageName,
545
+ section: "security",
546
+ severity: sev,
547
+ subtitle: count === 1 ? `${top.severity} · ${top.id}` : `${String(count)} advisories · top: ${top.severity} ${top.id}`,
548
+ title: entry.packageName
549
+ });
550
+ }
551
+ if (entry.socketReport && entry.socketReport.alerts.length > 0) {
552
+ const score = Math.round(entry.socketReport.score.overall * 100);
553
+ findings.push({
554
+ entry,
555
+ id: `socket:${entry.packageName}`,
556
+ kind: "socket",
557
+ packageName: entry.packageName,
558
+ section: "security",
559
+ severity: "warn",
560
+ subtitle: `${String(entry.socketReport.alerts.length)} alert${entry.socketReport.alerts.length === 1 ? "" : "s"} · score ${String(score)}%`,
561
+ title: entry.packageName
562
+ });
563
+ }
564
+ }
565
+ }
566
+ if (results.sections.has("optimization")) {
567
+ for (const entry of results.optimizations) {
568
+ findings.push({
569
+ entry,
570
+ id: `opt:${entry.packageName}`,
571
+ kind: "optimization",
572
+ section: "optimization",
573
+ severity: "warn",
574
+ subtitle: `${entry.category} → ${entry.replacement}`,
575
+ title: entry.packageName
576
+ });
577
+ }
578
+ }
579
+ if (results.sections.has("runtime")) {
580
+ for (const diagnostic of results.runtime) {
581
+ if (diagnostic.status !== "warn") {
582
+ continue;
583
+ }
584
+ findings.push({
585
+ diagnostic,
586
+ id: `runtime:${diagnostic.id}`,
587
+ kind: "runtime",
588
+ section: "runtime",
589
+ severity: "warn",
590
+ title: diagnostic.message
591
+ });
592
+ }
593
+ }
594
+ findings.sort((a, b) => {
595
+ if (a.section !== b.section) {
596
+ const order = ["dependencies", "security", "optimization", "runtime"];
597
+ return order.indexOf(a.section) - order.indexOf(b.section);
598
+ }
599
+ return SEVERITY_RANK[a.severity] - SEVERITY_RANK[b.severity];
600
+ });
601
+ return findings;
602
+ };
603
+ const SECTION_LABELS = {
604
+ dependencies: "Dependencies",
605
+ optimization: "Optimization",
606
+ runtime: "Runtime",
607
+ security: "Security"
608
+ };
609
+
610
+ const SEVERITY_COLORS$2 = {
611
+ error: "red",
612
+ warn: "yellow"
613
+ };
614
+ const SEVERITY_GLYPHS = {
615
+ error: "✖",
616
+ warn: "⚠"
617
+ };
618
+ const SEVERITY_LABELS = {
619
+ error: " ERROR ",
620
+ warn: " WARN "
621
+ };
622
+ const ConfigBanner = ({ children, hint, message, severity, title }) => {
623
+ const color = SEVERITY_COLORS$2[severity];
624
+ return jsxs(Box, { borderColor: color, borderStyle: "single", flexDirection: "column", flexShrink: 0, paddingX: 1, children: [
625
+ jsxs(Box, { gap: 1, children: [
626
+ jsx(Text, { backgroundColor: color, bold: true, color: "black", children: SEVERITY_LABELS[severity] }),
627
+ jsx(Text, { bold: true, color, children: SEVERITY_GLYPHS[severity] }),
628
+ jsx(Text, { bold: true, wrap: "truncate-end", children: title })
629
+ ] }),
630
+ jsx(Text, { wrap: "truncate-end", children: message }),
631
+ hint ? jsx(Text, { dimColor: true, wrap: "truncate-end", children: hint }) : null,
632
+ children
633
+ ] });
634
+ };
635
+
636
+ const SEVERITY_COLORS$1 = {
637
+ CRITICAL: "red",
638
+ HIGH: "red",
639
+ LOW: "gray",
640
+ MODERATE: "yellow",
641
+ UNKNOWN: "gray"
642
+ };
643
+ const SOCKET_SEVERITY_COLORS = {
644
+ critical: "red",
645
+ high: "red",
646
+ low: "gray",
647
+ medium: "yellow"
648
+ };
649
+ const UPDATE_TYPE_COLORS = {
650
+ major: "red",
651
+ minor: "yellow",
652
+ patch: "green"
653
+ };
654
+ const FieldRow = ({ children, label, width = 14 }) => jsxs(Box, { children: [
655
+ jsx(Box, { width, children: jsxs(Text, { dimColor: true, children: [
656
+ label,
657
+ ":"
658
+ ] }) }),
659
+ typeof children === "string" ? jsx(Text, { children }) : children
660
+ ] });
661
+ const SectionTitle = ({ children }) => jsx(Box, { marginTop: 1, children: jsx(Text, { bold: true, color: "white", children }) });
662
+ const OutdatedDetail = ({ finding }) => {
663
+ const { entry } = finding;
664
+ const typeColor = UPDATE_TYPE_COLORS[entry.updateType] ?? "white";
665
+ return jsxs(Box, { flexDirection: "column", children: [
666
+ jsx(FieldRow, { label: "Current", children: entry.currentRange }),
667
+ jsxs(FieldRow, { label: "Target", children: [
668
+ jsx(Text, { children: entry.newRange }),
669
+ jsxs(Text, { bold: true, color: typeColor, children: [
670
+ " (",
671
+ entry.updateType,
672
+ ")"
673
+ ] })
674
+ ] }),
675
+ jsx(FieldRow, { label: "Catalog", children: entry.catalogName }),
676
+ entry.acceptedRisk ? jsx(FieldRow, { label: "Risk ack", children: jsx(Text, { dimColor: true, children: entry.acceptedRisk.reason ?? "(no reason recorded)" }) }) : null,
677
+ jsx(SectionTitle, { children: "Action" }),
678
+ jsxs(Text, { dimColor: true, children: [
679
+ "Run",
680
+ " ",
681
+ jsx(Text, { bold: true, color: "white", children: "vis update" }),
682
+ " ",
683
+ "to apply this change."
684
+ ] })
685
+ ] });
686
+ };
687
+ const DuplicateDetail = ({ finding }) => jsxs(Box, { flexDirection: "column", children: [
688
+ jsx(FieldRow, { label: "Versions", children: jsx(Text, { children: String(finding.pkg.versions.length) }) }),
689
+ jsx(SectionTitle, { children: "Installed versions" }),
690
+ finding.pkg.versions.map((v) => jsxs(Text, { children: [
691
+ " · ",
692
+ v
693
+ ] }, v)),
694
+ jsx(SectionTitle, { children: "Action" }),
695
+ jsxs(Text, { dimColor: true, children: [
696
+ "Run",
697
+ " ",
698
+ jsx(Text, { bold: true, color: "white", children: "vis dedupe" }),
699
+ " ",
700
+ "to consolidate to a single resolution."
701
+ ] })
702
+ ] });
703
+ const VulnerabilityDetail = ({ finding }) => {
704
+ const vulns = finding.entry.vulnerabilities ?? [];
705
+ return jsxs(Box, { flexDirection: "column", children: [
706
+ jsx(FieldRow, { label: "Package", children: finding.packageName }),
707
+ jsx(FieldRow, { label: "Current", children: finding.entry.currentRange }),
708
+ jsx(FieldRow, { label: "Advisories", children: String(vulns.length) }),
709
+ finding.entry.acceptedRisk ? jsx(FieldRow, { label: "Risk ack", children: jsx(Text, { dimColor: true, children: finding.entry.acceptedRisk.reason ?? "(no reason recorded)" }) }) : null,
710
+ vulns.map((v) => {
711
+ const sevColor = SEVERITY_COLORS$1[v.severity] ?? "gray";
712
+ return jsxs(Box, { flexDirection: "column", marginTop: 1, children: [
713
+ jsxs(Box, { children: [
714
+ jsx(Text, { bold: true, color: sevColor, children: v.severity }),
715
+ jsx(Text, { children: " " }),
716
+ jsx(Text, { children: v.id }),
717
+ typeof v.cvssScore === "number" ? jsxs(Text, { dimColor: true, children: [
718
+ " · CVSS ",
719
+ v.cvssScore.toFixed(1)
720
+ ] }) : null
721
+ ] }),
722
+ jsx(Text, { wrap: "wrap", children: v.summary }),
723
+ v.fixedVersions.length > 0 ? jsxs(Text, { dimColor: true, children: [
724
+ "Fixed in: ",
725
+ v.fixedVersions.join(", ")
726
+ ] }) : null,
727
+ v.aliases && v.aliases.length > 0 ? jsxs(Text, { dimColor: true, children: [
728
+ "Aliases: ",
729
+ v.aliases.join(", ")
730
+ ] }) : null
731
+ ] }, v.id);
732
+ })
733
+ ] });
734
+ };
735
+ const SocketDetail = ({ finding }) => {
736
+ const report = finding.entry.socketReport;
737
+ if (!report) {
738
+ return jsx(Text, { dimColor: true, children: "No Socket report attached." });
739
+ }
740
+ const overall = Math.round(report.score.overall * 100);
741
+ const overallColor = scoreColor(report.score.overall);
742
+ return jsxs(Box, { flexDirection: "column", children: [
743
+ jsx(FieldRow, { label: "Package", children: finding.packageName }),
744
+ jsx(FieldRow, { label: "Overall", children: jsxs(Text, { color: overallColor, children: [
745
+ String(overall),
746
+ "%"
747
+ ] }) }),
748
+ jsx(FieldRow, { label: "Alerts", children: String(report.alerts.length) }),
749
+ finding.entry.acceptedRisk ? jsx(FieldRow, { label: "Risk ack", children: jsx(Text, { dimColor: true, children: finding.entry.acceptedRisk.reason ?? "(no reason recorded)" }) }) : null,
750
+ jsx(SectionTitle, { children: "Score breakdown" }),
751
+ Object.entries(report.score).map(([key, value]) => {
752
+ if (key === "overall") {
753
+ return null;
754
+ }
755
+ const numericValue = typeof value === "number" ? value : 0;
756
+ const pct = Math.round(numericValue * 100);
757
+ const color = scoreColor(numericValue);
758
+ return jsxs(Box, { children: [
759
+ jsx(Box, { width: 14, children: jsxs(Text, { dimColor: true, children: [
760
+ key,
761
+ ":"
762
+ ] }) }),
763
+ jsxs(Text, { color, children: [
764
+ String(pct),
765
+ "%"
766
+ ] })
767
+ ] }, key);
768
+ }),
769
+ jsx(SectionTitle, { children: "Alerts" }),
770
+ report.alerts.map((alert, index) => {
771
+ const sevColor = SOCKET_SEVERITY_COLORS[alert.severity] ?? "gray";
772
+ return jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [
773
+ jsxs(Box, { children: [
774
+ jsx(Text, { bold: true, color: sevColor, children: alert.severity }),
775
+ jsx(Text, { children: " " }),
776
+ jsx(Text, { children: alert.type })
777
+ ] }),
778
+ alert.props ? jsx(Text, { dimColor: true, wrap: "wrap", children: JSON.stringify(alert.props) }) : null
779
+ ] }, `${alert.type}-${String(index)}`);
780
+ })
781
+ ] });
782
+ };
783
+ const OptimizationDetail = ({ finding }) => {
784
+ const { entry } = finding;
785
+ return jsxs(Box, { flexDirection: "column", children: [
786
+ jsx(FieldRow, { label: "Package", children: entry.packageName }),
787
+ jsx(FieldRow, { label: "Category", children: entry.category }),
788
+ jsx(FieldRow, { label: "Replacement", children: entry.replacement }),
789
+ entry.overrideSpec ? jsx(FieldRow, { label: "Override", children: entry.overrideSpec }) : null,
790
+ jsx(FieldRow, { label: "Codemod", children: jsx(Text, { color: entry.hasCodemod ? "green" : "gray", children: entry.hasCodemod ? "available" : "not available" }) }),
791
+ entry.docUrl ? jsx(FieldRow, { label: "Guide", children: jsx(Text, { color: "cyan", underline: true, children: entry.docUrl }) }) : null,
792
+ jsx(SectionTitle, { children: "Action" }),
793
+ entry.hasCodemod ? jsxs(Text, { dimColor: true, children: [
794
+ "Run",
795
+ " ",
796
+ jsx(Text, { bold: true, color: "white", children: "vis optimize" }),
797
+ " ",
798
+ "to apply the codemod interactively."
799
+ ] }) : entry.overrideSpec ? jsxs(Text, { dimColor: true, children: [
800
+ "Run",
801
+ " ",
802
+ jsx(Text, { bold: true, color: "white", children: "vis optimize" }),
803
+ " ",
804
+ "to install the package override."
805
+ ] }) : entry.docUrl ? jsx(Text, { dimColor: true, children: "No automated codemod. Open the migration guide above for the recommended alternative and steps." }) : jsx(Text, { dimColor: true, children: "No automated codemod. Consult the package's docs or the e18e module-replacements guide for an alternative." })
806
+ ] });
807
+ };
808
+ const RuntimeDetail = ({ finding }) => {
809
+ const { diagnostic } = finding;
810
+ const statusColor = diagnostic.status === "warn" ? "yellow" : diagnostic.status === "ok" ? "green" : "gray";
811
+ return jsxs(Box, { flexDirection: "column", children: [
812
+ jsx(FieldRow, { label: "Check", children: diagnostic.id }),
813
+ jsx(FieldRow, { label: "Status", children: jsx(Text, { color: statusColor, children: diagnostic.status }) }),
814
+ jsx(SectionTitle, { children: "Message" }),
815
+ jsx(Text, { wrap: "wrap", children: diagnostic.message }),
816
+ diagnostic.detail && Object.keys(diagnostic.detail).length > 0 ? jsxs(Fragment, { children: [
817
+ jsx(SectionTitle, { children: "Details" }),
818
+ Object.entries(diagnostic.detail).map(([key, value]) => jsxs(Box, { children: [
819
+ jsx(Box, { width: 20, children: jsxs(Text, { dimColor: true, children: [
820
+ key,
821
+ ":"
822
+ ] }) }),
823
+ jsx(Text, { children: String(value) })
824
+ ] }, key))
825
+ ] }) : null
826
+ ] });
827
+ };
828
+ const DoctorDetailPanel = ({ finding, focused, scrollRef }) => {
829
+ const borderColor = focused ? "white" : "gray";
830
+ if (!finding) {
831
+ return jsx(Box, { alignItems: "center", borderColor: "gray", borderStyle: "single", flexDirection: "column", flexGrow: 1, justifyContent: "center", children: jsx(Text, { dimColor: true, children: "No finding selected" }) });
832
+ }
833
+ let body;
834
+ switch (finding.kind) {
835
+ case "duplicate": {
836
+ body = jsx(DuplicateDetail, { finding });
837
+ break;
838
+ }
839
+ case "optimization": {
840
+ body = jsx(OptimizationDetail, { finding });
841
+ break;
842
+ }
843
+ case "outdated": {
844
+ body = jsx(OutdatedDetail, { finding });
845
+ break;
846
+ }
847
+ case "runtime": {
848
+ body = jsx(RuntimeDetail, { finding });
849
+ break;
850
+ }
851
+ case "socket": {
852
+ body = jsx(SocketDetail, { finding });
853
+ break;
854
+ }
855
+ case "vulnerability": {
856
+ body = jsx(VulnerabilityDetail, { finding });
857
+ break;
858
+ }
859
+ default: {
860
+ body = jsx(Text, { dimColor: true, children: "Unknown finding kind." });
861
+ break;
862
+ }
863
+ }
864
+ return jsxs(Box, { borderColor, borderStyle: "single", flexDirection: "column", flexGrow: 1, children: [
865
+ jsxs(Box, { flexShrink: 0, paddingTop: 1, paddingX: 2, children: [
866
+ jsx(Text, { bold: true, color: "white", children: finding.title }),
867
+ jsxs(Text, { dimColor: true, children: [
868
+ " ",
869
+ SECTION_LABELS[finding.section]
870
+ ] })
871
+ ] }),
872
+ jsxs(ScrollView, { flexGrow: 1, flexShrink: 1, paddingX: 2, ref: scrollRef, scrollbar: true, scrollbarColor: "gray", scrollbarStyle: "block", children: [
873
+ jsx(Text, {}),
874
+ body
875
+ ] })
876
+ ] });
877
+ };
878
+
879
+ const SEVERITY_COLORS = {
880
+ error: "red",
881
+ warn: "yellow"
882
+ };
883
+ const SEVERITY_GLYPH = {
884
+ error: "✖",
885
+ warn: "⚠"
886
+ };
887
+ const hasAcceptedRisk = (finding) => {
888
+ if (finding.kind === "outdated" || finding.kind === "vulnerability" || finding.kind === "socket") {
889
+ return Boolean(finding.entry.acceptedRisk);
890
+ }
891
+ return false;
892
+ };
893
+ const FindingRow = ({ finding, isSelected }) => {
894
+ const sevColor = SEVERITY_COLORS[finding.severity];
895
+ const acked = hasAcceptedRisk(finding);
896
+ return jsxs(Box, { flexShrink: 0, height: 1, children: [
897
+ jsx(Text, { children: isSelected ? ">" : " " }),
898
+ jsxs(Text, { color: sevColor, children: [
899
+ " ",
900
+ SEVERITY_GLYPH[finding.severity],
901
+ " "
902
+ ] }),
903
+ jsx(Box, { flexGrow: 1, children: jsx(Text, { bold: isSelected, inverse: isSelected, wrap: "truncate", children: finding.title }) }),
904
+ acked ? jsx(Text, { color: "cyan", children: " ack" }) : null,
905
+ finding.subtitle ? jsxs(Text, { dimColor: true, wrap: "truncate", children: [
906
+ " ",
907
+ finding.subtitle
908
+ ] }) : null
909
+ ] });
910
+ };
911
+ const SectionHeader = ({ count, section }) => jsxs(Box, { flexShrink: 0, height: 1, marginTop: 1, children: [
912
+ jsx(Text, { dimColor: true, children: "▼ " }),
913
+ jsx(Text, { bold: true, color: "white", children: SECTION_LABELS[section].toUpperCase() }),
914
+ jsxs(Text, { dimColor: true, children: [
915
+ " (",
916
+ count,
917
+ ")"
918
+ ] })
919
+ ] });
920
+ const TabLabel = ({ count, label, status }) => {
921
+ const showSpinner = status === "running";
922
+ return jsxs(Text, { children: [
923
+ label,
924
+ showSpinner ? jsxs(Text, { children: [
925
+ " ",
926
+ jsx(Spinner, { type: "dots" })
927
+ ] }) : null,
928
+ status === "error" ? jsx(Text, { bold: true, color: "red", children: " ✖" }) : jsxs(Text, { dimColor: true, children: [
929
+ " (",
930
+ String(count),
931
+ ")"
932
+ ] })
933
+ ] });
934
+ };
935
+ const DoctorListPanel = ({
936
+ elapsedMs,
937
+ entries,
938
+ filterActive,
939
+ filterText,
940
+ filterType,
941
+ focused,
942
+ fromCache = false,
943
+ grouped,
944
+ onViewportHeightChange,
945
+ scrollOffset,
946
+ sectionCounts,
947
+ sectionMessage,
948
+ sectionStatus,
949
+ selectedIndex,
950
+ severityFilter,
951
+ totalAll,
952
+ viewportHeight
953
+ }) => {
954
+ const borderColor = focused ? "white" : "gray";
955
+ const { measuredHeight: measuredViewportHeight, ref: contentRowRef } = useMeasuredHeight(viewportHeight, onViewportHeightChange);
956
+ let errors = 0;
957
+ let warns = 0;
958
+ for (const finding of entries) {
959
+ if (finding.severity === "error") {
960
+ errors += 1;
961
+ } else if (finding.severity === "warn") {
962
+ warns += 1;
963
+ }
964
+ }
965
+ const summaryParts = [];
966
+ if (errors > 0) {
967
+ summaryParts.push(`${String(errors)} error${errors === 1 ? "" : "s"}`);
968
+ }
969
+ if (warns > 0) {
970
+ summaryParts.push(`${String(warns)} warn${warns === 1 ? "" : "s"}`);
971
+ }
972
+ const summary = summaryParts.length > 0 ? ` (${summaryParts.join(", ")})` : "";
973
+ const elapsedSeconds = (elapsedMs / 1e3).toFixed(1);
974
+ const rows = [];
975
+ for (const [section, items] of grouped) {
976
+ rows.push(jsx(SectionHeader, { count: items.length, section }, `hdr-${section}`));
977
+ for (const item of items) {
978
+ const flatIndex = entries.indexOf(item);
979
+ rows.push(jsx(FindingRow, { finding: item, isSelected: flatIndex === selectedIndex }, item.id));
980
+ }
981
+ }
982
+ let contentHeight = 0;
983
+ for (const [, items] of grouped) {
984
+ contentHeight += 2 + items.length;
985
+ }
986
+ const showScrollbar = contentHeight > measuredViewportHeight && measuredViewportHeight > 0;
987
+ return jsxs(Box, { borderColor, borderStyle: "single", flexDirection: "column", flexGrow: 1, children: [
988
+ jsxs(Box, { flexShrink: 0, gap: 1, paddingX: 1, children: [
989
+ jsx(Text, { bold: true, inverse: true, children: " DOCTOR " }),
990
+ jsxs(Text, { wrap: "truncate", children: [
991
+ entries.length,
992
+ entries.length === totalAll ? "" : `/${String(totalAll)}`,
993
+ " finding",
994
+ entries.length === 1 ? "" : "s",
995
+ summary
996
+ ] }),
997
+ severityFilter ? jsx(Text, { bold: true, color: SEVERITY_COLORS[severityFilter], inverse: true, children: ` ${severityFilter.toUpperCase()} ONLY ` }) : null,
998
+ fromCache ? jsx(Text, { bold: true, color: "cyan", inverse: true, children: " CACHED " }) : null,
999
+ jsxs(Text, { dimColor: true, children: [
1000
+ " · ",
1001
+ elapsedSeconds,
1002
+ "s"
1003
+ ] })
1004
+ ] }),
1005
+ jsx(Box, { flexShrink: 0, paddingX: 1, paddingY: 1, children: jsx(
1006
+ Tabs,
1007
+ {
1008
+ isFocused: focused,
1009
+ keyMap: { next: [], previous: [], useNumbers: false, useTab: false },
1010
+ onChange: () => {
1011
+ },
1012
+ showIndex: false,
1013
+ value: filterType,
1014
+ children: FILTER_TABS.map(({ id, label }) => jsx(Tab, { name: id, children: jsx(TabLabel, { count: sectionCounts[id], label, status: sectionStatus[id] }) }, id))
1015
+ }
1016
+ ) }),
1017
+ (() => {
1018
+ const running = Object.keys(sectionStatus).filter((id) => sectionStatus[id] === "running" && sectionMessage[id]).map((id) => sectionMessage[id]);
1019
+ if (running.length === 0) {
1020
+ return null;
1021
+ }
1022
+ return jsx(Box, { flexShrink: 0, paddingX: 1, children: jsxs(Text, { dimColor: true, wrap: "truncate", children: [
1023
+ jsx(Spinner, { type: "dots" }),
1024
+ " ",
1025
+ running.join(" · ")
1026
+ ] }) });
1027
+ })(),
1028
+ filterActive && jsxs(Box, { flexShrink: 0, paddingX: 1, children: [
1029
+ jsx(Text, { bold: true, color: "white", children: "/ " }),
1030
+ jsx(Text, { children: filterText }),
1031
+ jsx(Text, { inverse: true, children: " " })
1032
+ ] }),
1033
+ jsxs(Box, { flexDirection: "row", flexGrow: 1, overflow: "hidden", ref: contentRowRef, children: [
1034
+ jsx(Box, { flexDirection: "column", flexGrow: 1, overflow: "hidden", paddingLeft: 1, children: jsx(Box, { flexDirection: "column", marginTop: -scrollOffset, children: rows.length > 0 ? rows : jsx(Box, { marginTop: 1, children: jsx(Text, { dimColor: true, children: "No findings match the current filter." }) }) }) }),
1035
+ showScrollbar && jsx(Box, { flexShrink: 0, marginLeft: 1, marginRight: 1, children: jsx(
1036
+ ScrollBar,
1037
+ {
1038
+ contentHeight,
1039
+ placement: "inset",
1040
+ scrollOffset,
1041
+ style: "block",
1042
+ viewportHeight: measuredViewportHeight
1043
+ }
1044
+ ) })
1045
+ ] }, `list-${filterType}-${filterText}`)
1046
+ ] });
1047
+ };
1048
+
1049
+ const buildUpdateAction = (finding) => {
1050
+ if (finding.kind === "outdated") {
1051
+ return {
1052
+ command: `vis update ${finding.entry.packageName}`,
1053
+ description: `Update ${finding.entry.packageName} to ${finding.entry.newRange}`
1054
+ };
1055
+ }
1056
+ if (finding.kind === "duplicate") {
1057
+ return {
1058
+ command: `vis dedupe ${finding.pkg.name}`,
1059
+ description: `Dedupe ${finding.pkg.name} (${String(finding.pkg.versions.length)} versions)`
1060
+ };
1061
+ }
1062
+ return void 0;
1063
+ };
1064
+ const buildOptimizeAction = (finding) => {
1065
+ if (finding.kind !== "optimization") {
1066
+ return void 0;
1067
+ }
1068
+ return {
1069
+ command: `vis optimize ${finding.entry.packageName}`,
1070
+ description: `Replace ${finding.entry.packageName} with ${finding.entry.replacement}`
1071
+ };
1072
+ };
1073
+ const buildAckAction = (finding) => {
1074
+ if (finding.kind !== "outdated" && finding.kind !== "vulnerability" && finding.kind !== "socket") {
1075
+ return void 0;
1076
+ }
1077
+ const pkg = finding.kind === "outdated" ? finding.entry.packageName : finding.packageName;
1078
+ const snippet = [
1079
+ "// Add to vis.config.ts:",
1080
+ "security: {",
1081
+ " acceptedRisks: {",
1082
+ ` "${pkg}": {`,
1083
+ ' reason: "explain why this risk is acceptable",',
1084
+ ' expiresAt: "YYYY-MM-DD",',
1085
+ " },",
1086
+ " },",
1087
+ "},"
1088
+ ].join("\n");
1089
+ return {
1090
+ command: snippet,
1091
+ configSnippet: snippet,
1092
+ description: `Acknowledge risk for ${pkg}`
1093
+ };
1094
+ };
1095
+ const MIN_HORIZONTAL_WIDTH = 100;
1096
+ const MIN_VIEWPORT_WIDTH = 40;
1097
+ const MIN_VIEWPORT_HEIGHT = 10;
1098
+ const VisDoctorApp = ({ autoExitSeconds = 0, banner, fromCache = false, startedAt, store }) => {
1099
+ const { exit } = useApp();
1100
+ const { columns, rows } = useWindowSize();
1101
+ const state = useSyncExternalStore(store.subscribe, store.getSnapshot);
1102
+ const [helpVisible, setHelpVisible] = useState(false);
1103
+ const [quitDialogVisible, setQuitDialogVisible] = useState(false);
1104
+ const [listScrollOffset, setListScrollOffset] = useState(0);
1105
+ const [now, setNow] = useState(() => Date.now());
1106
+ useEffect(() => {
1107
+ const id = setInterval(() => {
1108
+ setNow(Date.now());
1109
+ }, 1e3);
1110
+ return () => {
1111
+ clearInterval(id);
1112
+ };
1113
+ }, []);
1114
+ const elapsedMs = now - startedAt;
1115
+ const helpScrollRef = useRef(null);
1116
+ const detailScrollRef = useRef(null);
1117
+ const selectedFinding = state.entries[state.selectedIndex] ?? null;
1118
+ const sectionCounts = useMemo(() => {
1119
+ const counts = {
1120
+ dependencies: 0,
1121
+ optimization: 0,
1122
+ runtime: 0,
1123
+ security: 0
1124
+ };
1125
+ for (const finding of state.all) {
1126
+ counts[finding.section] += 1;
1127
+ }
1128
+ return counts;
1129
+ }, [state.all]);
1130
+ const bannerHeight = banner ? banner.hint ? 5 : 4 : 0;
1131
+ const activityLineHeight = useMemo(() => {
1132
+ for (const id of Object.keys(state.sectionStatus)) {
1133
+ if (state.sectionStatus[id] === "running" && state.sectionMessage[id]) {
1134
+ return 1;
1135
+ }
1136
+ }
1137
+ return 0;
1138
+ }, [state.sectionStatus, state.sectionMessage]);
1139
+ const isHorizontal = columns >= MIN_HORIZONTAL_WIDTH;
1140
+ const listPanelHeight = isHorizontal ? Math.max(1, rows - bannerHeight - 2) : Math.floor(rows * 0.55);
1141
+ const estimatedViewportHeight = useMemo(
1142
+ () => Math.max(1, listPanelHeight - 6 - activityLineHeight - (state.filterActive ? 1 : 0)),
1143
+ [listPanelHeight, activityLineHeight, state.filterActive]
1144
+ );
1145
+ const [measuredViewportHeight, setMeasuredViewportHeight] = useState(estimatedViewportHeight);
1146
+ const listViewportHeight = measuredViewportHeight > 0 ? measuredViewportHeight : estimatedViewportHeight;
1147
+ const contentHeight = useMemo(() => {
1148
+ let height = 0;
1149
+ for (const [, items] of state.grouped) {
1150
+ height += 2 + items.length;
1151
+ }
1152
+ return height;
1153
+ }, [state.grouped]);
1154
+ const maxScrollOffset = Math.max(0, contentHeight - listViewportHeight);
1155
+ useEffect(() => {
1156
+ setListScrollOffset((current) => Math.min(current, maxScrollOffset));
1157
+ }, [maxScrollOffset]);
1158
+ const getRowForIndex = useCallback(
1159
+ (index) => {
1160
+ let row = 0;
1161
+ let count = 0;
1162
+ for (const [, items] of state.grouped) {
1163
+ row += 2;
1164
+ for (const _ of items) {
1165
+ if (count === index) {
1166
+ return row;
1167
+ }
1168
+ row += 1;
1169
+ count += 1;
1170
+ }
1171
+ }
1172
+ return row;
1173
+ },
1174
+ [state.grouped]
1175
+ );
1176
+ const scrollToIndex = useCallback(
1177
+ (index) => {
1178
+ const targetRow = getRowForIndex(index);
1179
+ setListScrollOffset((current) => {
1180
+ if (targetRow > current + listViewportHeight - 2) {
1181
+ return Math.min(maxScrollOffset, Math.max(0, targetRow - listViewportHeight + 2));
1182
+ }
1183
+ if (targetRow < current + 1) {
1184
+ return Math.max(0, targetRow - 1);
1185
+ }
1186
+ return current;
1187
+ });
1188
+ },
1189
+ [getRowForIndex, listViewportHeight, maxScrollOffset]
1190
+ );
1191
+ useEffect(() => {
1192
+ detailScrollRef.current?.scrollToTop();
1193
+ }, [selectedFinding?.id]);
1194
+ useInput(
1195
+ (input, key) => {
1196
+ if (input === "c" && key.ctrl) {
1197
+ exit();
1198
+ return;
1199
+ }
1200
+ if (quitDialogVisible) {
1201
+ return;
1202
+ }
1203
+ if (helpVisible) {
1204
+ if (key.escape || input === "?") {
1205
+ setHelpVisible(false);
1206
+ } else if (input === "q") {
1207
+ setHelpVisible(false);
1208
+ setQuitDialogVisible(true);
1209
+ } else if (key.downArrow || input === "j") {
1210
+ helpScrollRef.current?.scrollBy(1);
1211
+ } else if (key.upArrow || input === "k") {
1212
+ helpScrollRef.current?.scrollBy(-1);
1213
+ }
1214
+ return;
1215
+ }
1216
+ if (input === "?") {
1217
+ setHelpVisible(true);
1218
+ return;
1219
+ }
1220
+ if (input === "q") {
1221
+ setQuitDialogVisible(true);
1222
+ return;
1223
+ }
1224
+ if (key.tab) {
1225
+ store.setFocusedPanel(state.focusedPanel === "list" ? "detail" : "list");
1226
+ return;
1227
+ }
1228
+ if (state.filterActive) {
1229
+ if (key.escape || key.return) {
1230
+ store.setFilterActive(false);
1231
+ return;
1232
+ }
1233
+ if (key.backspace) {
1234
+ setListScrollOffset(0);
1235
+ store.setFilter(state.filterText.slice(0, -1));
1236
+ return;
1237
+ }
1238
+ if (input && !key.ctrl && !key.meta) {
1239
+ setListScrollOffset(0);
1240
+ store.setFilter(state.filterText + input);
1241
+ }
1242
+ return;
1243
+ }
1244
+ if (state.focusedPanel === "list" && (key.leftArrow || key.rightArrow)) {
1245
+ const currentIndex = FILTER_TABS.findIndex((tab) => tab.id === state.filterType);
1246
+ const nextIndex = key.rightArrow ? (currentIndex + 1) % FILTER_TABS.length : (currentIndex - 1 + FILTER_TABS.length) % FILTER_TABS.length;
1247
+ setListScrollOffset(0);
1248
+ detailScrollRef.current?.scrollToTop();
1249
+ store.setFilterType(FILTER_TABS[nextIndex].id);
1250
+ return;
1251
+ }
1252
+ if (state.focusedPanel === "list") {
1253
+ if (key.downArrow || input === "j") {
1254
+ const next = Math.min(state.selectedIndex + 1, state.entries.length - 1);
1255
+ store.setSelectedIndex(next);
1256
+ scrollToIndex(next);
1257
+ return;
1258
+ }
1259
+ if (key.upArrow || input === "k") {
1260
+ const next = Math.max(state.selectedIndex - 1, 0);
1261
+ store.setSelectedIndex(next);
1262
+ scrollToIndex(next);
1263
+ return;
1264
+ }
1265
+ if (key.pageDown) {
1266
+ const next = Math.min(state.selectedIndex + 10, state.entries.length - 1);
1267
+ store.setSelectedIndex(next);
1268
+ scrollToIndex(next);
1269
+ return;
1270
+ }
1271
+ if (key.pageUp) {
1272
+ const next = Math.max(state.selectedIndex - 10, 0);
1273
+ store.setSelectedIndex(next);
1274
+ scrollToIndex(next);
1275
+ return;
1276
+ }
1277
+ if (key.home) {
1278
+ store.setSelectedIndex(0);
1279
+ setListScrollOffset(0);
1280
+ return;
1281
+ }
1282
+ if (key.end) {
1283
+ const last = state.entries.length - 1;
1284
+ store.setSelectedIndex(last);
1285
+ scrollToIndex(last);
1286
+ return;
1287
+ }
1288
+ if (input === "/") {
1289
+ store.setFilterActive(true);
1290
+ return;
1291
+ }
1292
+ if (input === "e") {
1293
+ store.setSeverityFilter(state.severityFilter === "error" ? void 0 : "error");
1294
+ setListScrollOffset(0);
1295
+ return;
1296
+ }
1297
+ if (input === "w") {
1298
+ store.setSeverityFilter(state.severityFilter === "warn" ? void 0 : "warn");
1299
+ setListScrollOffset(0);
1300
+ return;
1301
+ }
1302
+ if (input === "u" && selectedFinding) {
1303
+ const action = buildUpdateAction(selectedFinding);
1304
+ if (action) {
1305
+ store.setPendingAction(action);
1306
+ exit();
1307
+ }
1308
+ return;
1309
+ }
1310
+ if (input === "o" && selectedFinding) {
1311
+ const action = buildOptimizeAction(selectedFinding);
1312
+ if (action) {
1313
+ store.setPendingAction(action);
1314
+ exit();
1315
+ }
1316
+ return;
1317
+ }
1318
+ if (input === "a" && selectedFinding) {
1319
+ const action = buildAckAction(selectedFinding);
1320
+ if (action) {
1321
+ store.setPendingAction(action);
1322
+ exit();
1323
+ }
1324
+ return;
1325
+ }
1326
+ if (input === "d") {
1327
+ store.setFocusedPanel("detail");
1328
+ return;
1329
+ }
1330
+ return;
1331
+ }
1332
+ if (key.escape || key.leftArrow) {
1333
+ store.setFocusedPanel("list");
1334
+ return;
1335
+ }
1336
+ if (key.downArrow || input === "j") {
1337
+ detailScrollRef.current?.scrollBy(1);
1338
+ return;
1339
+ }
1340
+ if (key.upArrow || input === "k") {
1341
+ detailScrollRef.current?.scrollBy(-1);
1342
+ return;
1343
+ }
1344
+ if (key.pageDown) {
1345
+ detailScrollRef.current?.scrollBy(10);
1346
+ return;
1347
+ }
1348
+ if (key.pageUp) {
1349
+ detailScrollRef.current?.scrollBy(-10);
1350
+ return;
1351
+ }
1352
+ if (key.home) {
1353
+ detailScrollRef.current?.scrollToTop();
1354
+ return;
1355
+ }
1356
+ if (key.end) {
1357
+ detailScrollRef.current?.scrollToBottom();
1358
+ }
1359
+ },
1360
+ { isActive: true }
1361
+ );
1362
+ if (columns < MIN_VIEWPORT_WIDTH || rows < MIN_VIEWPORT_HEIGHT) {
1363
+ return jsx(Box, { alignItems: "center", height: rows, justifyContent: "center", width: columns, children: jsxs(Text, { color: "yellow", children: [
1364
+ "Terminal too small (",
1365
+ columns,
1366
+ "x",
1367
+ rows,
1368
+ ")"
1369
+ ] }) });
1370
+ }
1371
+ const detailFocused = state.focusedPanel === "detail";
1372
+ const footerItems = [
1373
+ jsxs(Box, { gap: 1, children: [
1374
+ jsx(Text, { bold: true, color: "white", children: "q" }),
1375
+ jsx(Text, { dimColor: true, children: "QUIT" })
1376
+ ] }, "q"),
1377
+ jsxs(Box, { gap: 1, children: [
1378
+ jsx(Text, { bold: true, color: "white", children: "?" }),
1379
+ jsx(Text, { dimColor: true, children: "HELP" })
1380
+ ] }, "?"),
1381
+ jsxs(Box, { gap: 1, children: [
1382
+ jsx(Text, { bold: true, color: "white", children: "↑↓" }),
1383
+ jsx(Text, { dimColor: true, children: detailFocused ? "SCROLL" : "NAV" })
1384
+ ] }, "nav"),
1385
+ detailFocused ? jsxs(Box, { gap: 1, children: [
1386
+ jsx(Text, { bold: true, color: "white", children: "←/Esc" }),
1387
+ jsx(Text, { dimColor: true, children: "LIST" })
1388
+ ] }, "lr") : jsxs(Box, { gap: 1, children: [
1389
+ jsx(Text, { bold: true, color: "white", children: "←→" }),
1390
+ jsx(Text, { dimColor: true, children: "SECTION" })
1391
+ ] }, "lr"),
1392
+ jsxs(Box, { gap: 1, children: [
1393
+ jsx(Text, { bold: true, color: "white", children: "/" }),
1394
+ jsx(Text, { dimColor: true, children: "SEARCH" })
1395
+ ] }, "search"),
1396
+ jsxs(Box, { gap: 1, children: [
1397
+ jsx(Text, { bold: true, color: "white", children: "e/w" }),
1398
+ jsx(Text, { dimColor: true, children: "SEVERITY" })
1399
+ ] }, "sev"),
1400
+ jsxs(Box, { gap: 1, children: [
1401
+ jsx(Text, { bold: true, color: "white", children: "u/o/a" }),
1402
+ jsx(Text, { dimColor: true, children: "ACTION" })
1403
+ ] }, "actions"),
1404
+ jsxs(Box, { gap: 1, children: [
1405
+ jsx(Text, { bold: true, color: "white", children: "Tab" }),
1406
+ jsx(Text, { dimColor: true, children: "PANEL" })
1407
+ ] }, "tab")
1408
+ ];
1409
+ const footer = jsx(Box, { borderBottom: false, borderColor: "gray", borderLeft: false, borderRight: false, borderStyle: "single", flexShrink: 0, children: jsx(Box, { gap: 2, overflow: "hidden", paddingX: 1, children: footerItems }) });
1410
+ const helpPopup = jsxs(
1411
+ Dialog,
1412
+ {
1413
+ footer: jsxs(Text, { dimColor: true, children: [
1414
+ jsx(Text, { bold: true, color: "white", children: "↑↓" }),
1415
+ " scroll ",
1416
+ jsx(Text, { bold: true, color: "white", children: "?" }),
1417
+ "/",
1418
+ jsx(Text, { bold: true, color: "white", children: "Esc" }),
1419
+ " close"
1420
+ ] }),
1421
+ scrollRef: helpScrollRef,
1422
+ title: "DOCTOR — KEYBOARD SHORTCUTS",
1423
+ visible: helpVisible,
1424
+ width: 56,
1425
+ children: [
1426
+ jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [
1427
+ jsxs(Box, { marginBottom: 1, children: [
1428
+ jsx(Text, { dimColor: true, children: "── " }),
1429
+ jsx(Text, { bold: true, color: "white", children: "NAVIGATION" })
1430
+ ] }),
1431
+ jsxs(Box, { children: [
1432
+ jsx(Box, { width: 26, children: jsxs(Text, { children: [
1433
+ jsx(Text, { bold: true, color: "white", children: " ↑/k " }),
1434
+ jsx(Text, { dimColor: true, children: "Move up" })
1435
+ ] }) }),
1436
+ jsxs(Text, { children: [
1437
+ jsx(Text, { bold: true, color: "white", children: " ↓/j " }),
1438
+ jsx(Text, { dimColor: true, children: "Move down" })
1439
+ ] })
1440
+ ] }),
1441
+ jsxs(Box, { children: [
1442
+ jsx(Box, { width: 26, children: jsxs(Text, { children: [
1443
+ jsx(Text, { bold: true, color: "white", children: " PgUp" }),
1444
+ jsx(Text, { dimColor: true, children: " Jump up 10" })
1445
+ ] }) }),
1446
+ jsxs(Text, { children: [
1447
+ jsx(Text, { bold: true, color: "white", children: " PgDn" }),
1448
+ jsx(Text, { dimColor: true, children: " Jump down 10" })
1449
+ ] })
1450
+ ] }),
1451
+ jsxs(Box, { children: [
1452
+ jsx(Box, { width: 26, children: jsxs(Text, { children: [
1453
+ jsx(Text, { bold: true, color: "white", children: " Home" }),
1454
+ jsx(Text, { dimColor: true, children: " Jump to top" })
1455
+ ] }) }),
1456
+ jsxs(Text, { children: [
1457
+ jsx(Text, { bold: true, color: "white", children: " End" }),
1458
+ jsx(Text, { dimColor: true, children: " Jump to bottom" })
1459
+ ] })
1460
+ ] }),
1461
+ jsxs(Text, { children: [
1462
+ jsx(Text, { bold: true, color: "white", children: " Tab" }),
1463
+ jsx(Text, { dimColor: true, children: " Switch panel" })
1464
+ ] }),
1465
+ jsxs(Text, { children: [
1466
+ jsx(Text, { bold: true, color: "white", children: " →/←" }),
1467
+ jsx(Text, { dimColor: true, children: " Section tabs (list) / Focus list (detail)" })
1468
+ ] })
1469
+ ] }),
1470
+ jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [
1471
+ jsxs(Box, { marginBottom: 1, children: [
1472
+ jsx(Text, { dimColor: true, children: "── " }),
1473
+ jsx(Text, { bold: true, color: "white", children: "FILTER" })
1474
+ ] }),
1475
+ jsxs(Text, { children: [
1476
+ jsx(Text, { bold: true, color: "white", children: " /" }),
1477
+ jsx(Text, { dimColor: true, children: " Open text filter (Esc/Enter to close)" })
1478
+ ] }),
1479
+ jsxs(Text, { children: [
1480
+ jsx(Text, { bold: true, color: "white", children: " e" }),
1481
+ jsx(Text, { dimColor: true, children: " Toggle errors-only filter" })
1482
+ ] }),
1483
+ jsxs(Text, { children: [
1484
+ jsx(Text, { bold: true, color: "white", children: " w" }),
1485
+ jsx(Text, { dimColor: true, children: " Toggle warns-only filter" })
1486
+ ] })
1487
+ ] }),
1488
+ jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [
1489
+ jsxs(Box, { marginBottom: 1, children: [
1490
+ jsx(Text, { dimColor: true, children: "── " }),
1491
+ jsx(Text, { bold: true, color: "white", children: "ACTIONS" })
1492
+ ] }),
1493
+ jsxs(Text, { children: [
1494
+ jsx(Text, { bold: true, color: "white", children: " u" }),
1495
+ jsx(Text, { dimColor: true, children: " Exit + suggest update / dedupe command" })
1496
+ ] }),
1497
+ jsxs(Text, { children: [
1498
+ jsx(Text, { bold: true, color: "white", children: " o" }),
1499
+ jsx(Text, { dimColor: true, children: " Exit + suggest optimize command" })
1500
+ ] }),
1501
+ jsxs(Text, { children: [
1502
+ jsx(Text, { bold: true, color: "white", children: " a" }),
1503
+ jsx(Text, { dimColor: true, children: " Exit + print risk-ack snippet" })
1504
+ ] }),
1505
+ jsxs(Text, { children: [
1506
+ jsx(Text, { bold: true, color: "white", children: " d" }),
1507
+ jsx(Text, { dimColor: true, children: " Focus detail panel" })
1508
+ ] })
1509
+ ] }),
1510
+ jsxs(Box, { flexDirection: "column", children: [
1511
+ jsxs(Box, { marginBottom: 1, children: [
1512
+ jsx(Text, { dimColor: true, children: "── " }),
1513
+ jsx(Text, { bold: true, color: "white", children: "EXIT" })
1514
+ ] }),
1515
+ jsxs(Text, { children: [
1516
+ jsx(Text, { bold: true, color: "white", children: " q" }),
1517
+ jsx(Text, { dimColor: true, children: " Quit (with countdown)" })
1518
+ ] }),
1519
+ jsxs(Text, { children: [
1520
+ jsx(Text, { bold: true, color: "white", children: " Ctrl+C" }),
1521
+ jsx(Text, { dimColor: true, children: " Quit immediately" })
1522
+ ] })
1523
+ ] })
1524
+ ]
1525
+ }
1526
+ );
1527
+ const listPanel = jsx(
1528
+ DoctorListPanel,
1529
+ {
1530
+ elapsedMs,
1531
+ entries: state.entries,
1532
+ filterActive: state.filterActive,
1533
+ filterText: state.filterText,
1534
+ filterType: state.filterType,
1535
+ focused: state.focusedPanel === "list",
1536
+ fromCache,
1537
+ grouped: state.grouped,
1538
+ onViewportHeightChange: setMeasuredViewportHeight,
1539
+ scrollOffset: listScrollOffset,
1540
+ sectionCounts,
1541
+ sectionMessage: state.sectionMessage,
1542
+ sectionStatus: state.sectionStatus,
1543
+ selectedIndex: state.selectedIndex,
1544
+ severityFilter: state.severityFilter,
1545
+ totalAll: state.all.length,
1546
+ viewportHeight: listViewportHeight
1547
+ }
1548
+ );
1549
+ const bannerNode = banner ? jsx(ConfigBanner, { hint: banner.hint, message: banner.message, severity: banner.severity, title: banner.title }) : null;
1550
+ const detailPanel = jsx(DoctorDetailPanel, { finding: selectedFinding, focused: state.focusedPanel === "detail", scrollRef: detailScrollRef });
1551
+ if (isHorizontal) {
1552
+ const detailWidth = Math.floor(columns * 0.4);
1553
+ return jsxs(Box, { flexDirection: "column", height: rows, width: columns, children: [
1554
+ bannerNode,
1555
+ jsxs(Box, { flexDirection: "row", flexGrow: 1, children: [
1556
+ jsx(Box, { flexGrow: 1, children: listPanel }),
1557
+ jsx(Box, { width: detailWidth, children: detailPanel })
1558
+ ] }),
1559
+ footer,
1560
+ jsx(
1561
+ QuitDialog,
1562
+ {
1563
+ autoExitSeconds: autoExitSeconds || 3,
1564
+ onCancel: () => {
1565
+ setQuitDialogVisible(false);
1566
+ },
1567
+ visible: quitDialogVisible
1568
+ }
1569
+ ),
1570
+ helpPopup
1571
+ ] });
1572
+ }
1573
+ return jsxs(Box, { flexDirection: "column", height: rows, width: columns, children: [
1574
+ bannerNode,
1575
+ jsx(Box, { height: listPanelHeight, children: listPanel }),
1576
+ jsx(Box, { flexGrow: 1, children: detailPanel }),
1577
+ footer,
1578
+ jsx(
1579
+ QuitDialog,
1580
+ {
1581
+ autoExitSeconds: autoExitSeconds || 3,
1582
+ onCancel: () => {
1583
+ setQuitDialogVisible(false);
1584
+ },
1585
+ visible: quitDialogVisible
1586
+ }
1587
+ ),
1588
+ helpPopup
1589
+ ] });
1590
+ };
1591
+
1592
+ const escapeRegex = (literal) => literal.replaceAll(/[$()+.?[\\\]^{|}]/g, String.raw`\$&`);
1593
+ const compilePattern = (pattern) => {
1594
+ const segments = pattern.split("*").map(escapeRegex);
1595
+ return new RegExp(`^${segments.join(".*")}$`, "i");
1596
+ };
1597
+ const parseFilterPatterns = (raw) => {
1598
+ if (!raw) {
1599
+ return [];
1600
+ }
1601
+ return raw.split(",").map((token) => token.trim()).filter((token) => token.length > 0).map(compilePattern);
1602
+ };
1603
+ const matchesAny = (name, patterns) => {
1604
+ for (const pattern of patterns) {
1605
+ if (pattern.test(name)) {
1606
+ return true;
1607
+ }
1608
+ }
1609
+ return false;
1610
+ };
1611
+ const applyFilter = (results, patterns) => {
1612
+ if (patterns.length === 0) {
1613
+ return results;
1614
+ }
1615
+ const outdated = results.outdated.filter((entry) => matchesAny(entry.packageName, patterns));
1616
+ const duplicates = results.duplicates.filter((dup) => matchesAny(dup.name, patterns));
1617
+ const optimizations = results.optimizations.filter((entry) => matchesAny(entry.packageName, patterns));
1618
+ let vulnCount = 0;
1619
+ let socketAlerts = 0;
1620
+ let socketLowScore = 0;
1621
+ for (const entry of outdated) {
1622
+ if (entry.vulnerabilities) {
1623
+ vulnCount += entry.vulnerabilities.length;
1624
+ }
1625
+ if (entry.socketReport) {
1626
+ socketAlerts += entry.socketReport.alerts.length;
1627
+ if (entry.socketReport.score.overall < DEFAULT_LOW_SCORE_THRESHOLD) {
1628
+ socketLowScore += 1;
1629
+ }
1630
+ }
1631
+ }
1632
+ return {
1633
+ ...results,
1634
+ duplicates,
1635
+ optimizations,
1636
+ outdated,
1637
+ socketIssues: { alerts: socketAlerts, lowScore: socketLowScore },
1638
+ vulnCount
1639
+ };
1640
+ };
1641
+ const filterFindingsByPattern = (findings, patterns) => {
1642
+ if (patterns.length === 0) {
1643
+ return [...findings];
1644
+ }
1645
+ return findings.filter((finding) => {
1646
+ if (finding.kind === "runtime") {
1647
+ return true;
1648
+ }
1649
+ const name = finding.kind === "duplicate" ? finding.pkg.name : finding.kind === "outdated" || finding.kind === "optimization" ? finding.entry.packageName : finding.packageName;
1650
+ return matchesAny(name, patterns);
1651
+ });
1652
+ };
1653
+
1654
+ const SECTION_IDS = ["dependencies", "security", "optimization", "runtime"];
1655
+ const parseSectionList = (raw) => {
1656
+ const parsed = /* @__PURE__ */ new Set();
1657
+ if (!raw) {
1658
+ return parsed;
1659
+ }
1660
+ for (const token of raw.split(",")) {
1661
+ const trimmed = token.trim().toLowerCase();
1662
+ if (SECTION_IDS.includes(trimmed)) {
1663
+ parsed.add(trimmed);
1664
+ }
1665
+ }
1666
+ return parsed;
1667
+ };
1668
+ const resolveSections = (only, skip) => {
1669
+ if (only !== void 0 && only !== "") {
1670
+ return parseSectionList(only);
1671
+ }
1672
+ const skipped = parseSectionList(skip);
1673
+ return new Set(SECTION_IDS.filter((s) => !skipped.has(s)));
1674
+ };
1675
+ const summarizeOptimizations = (entries) => {
1676
+ const counts = { micro: 0, native: 0, preferred: 0, socket: 0, total: entries.length };
1677
+ for (const entry of entries) {
1678
+ switch (entry.category) {
1679
+ case "micro-utility": {
1680
+ counts.micro += 1;
1681
+ break;
1682
+ }
1683
+ case "native": {
1684
+ counts.native += 1;
1685
+ break;
1686
+ }
1687
+ case "preferred": {
1688
+ counts.preferred += 1;
1689
+ break;
1690
+ }
1691
+ case "socket": {
1692
+ counts.socket += 1;
1693
+ break;
1694
+ }
1695
+ }
1696
+ }
1697
+ return counts;
1698
+ };
1699
+ const sectionStatus = (results, section) => {
1700
+ if (!results.sections.has(section)) {
1701
+ return "skip";
1702
+ }
1703
+ switch (section) {
1704
+ case "dependencies": {
1705
+ if (results.outdated.length > 0 || results.duplicates.length > 0) {
1706
+ return "warn";
1707
+ }
1708
+ return "ok";
1709
+ }
1710
+ case "optimization": {
1711
+ return results.optimizations.length > 0 ? "warn" : "ok";
1712
+ }
1713
+ case "runtime": {
1714
+ return results.runtime.some((d) => d.status === "warn") ? "warn" : "ok";
1715
+ }
1716
+ case "security": {
1717
+ if (results.vulnCount > 0 || results.socketIssues.alerts > 0) {
1718
+ return "error";
1719
+ }
1720
+ if (results.socketIssues.lowScore > 0) {
1721
+ return "warn";
1722
+ }
1723
+ return "ok";
1724
+ }
1725
+ default: {
1726
+ return "ok";
1727
+ }
1728
+ }
1729
+ };
1730
+ const buildJsonPayload = (results, packageManagerName) => {
1731
+ const counts = summarizeOptimizations(results.optimizations);
1732
+ const sectionsObj = {
1733
+ dependencies: sectionStatus(results, "dependencies"),
1734
+ optimization: sectionStatus(results, "optimization"),
1735
+ runtime: sectionStatus(results, "runtime"),
1736
+ security: sectionStatus(results, "security")
1737
+ };
1738
+ const statuses = /* @__PURE__ */ new Set([...Object.values(sectionsObj), results.supplyChain.status]);
1739
+ const overall = statuses.has("error") ? "error" : statuses.has("warn") ? "warn" : "ok";
1740
+ return {
1741
+ dependencies: {
1742
+ duplicates: results.duplicates.length,
1743
+ installed: results.installedCount,
1744
+ outdated: results.outdated.length,
1745
+ status: sectionsObj.dependencies
1746
+ },
1747
+ elapsedMs: results.elapsedMs,
1748
+ optimizations: {
1749
+ microUtilities: counts.micro,
1750
+ native: counts.native,
1751
+ preferred: counts.preferred,
1752
+ socket: counts.socket,
1753
+ status: sectionsObj.optimization,
1754
+ total: counts.total
1755
+ },
1756
+ packageManager: packageManagerName,
1757
+ runtime: results.runtime.map((d) => {
1758
+ return {
1759
+ detail: d.detail,
1760
+ id: d.id,
1761
+ message: d.message,
1762
+ status: d.status
1763
+ };
1764
+ }),
1765
+ runtimeStatus: sectionsObj.runtime,
1766
+ security: {
1767
+ alerts: results.socketIssues.alerts,
1768
+ lowScorePackages: results.socketIssues.lowScore,
1769
+ status: sectionsObj.security,
1770
+ vulnerabilities: results.vulnCount
1771
+ },
1772
+ status: overall,
1773
+ supplyChain: {
1774
+ findings: results.supplyChain.findings.map((f) => {
1775
+ return { detail: f.detail, label: f.label, severity: f.severity };
1776
+ }),
1777
+ status: results.supplyChain.status
1778
+ },
1779
+ workspaces: results.workspaceCount
1780
+ };
1781
+ };
1782
+ const shouldFail = (results, strict) => {
1783
+ const hasRuntimeWarn = results.runtime.some((d) => d.status === "warn");
1784
+ const baseFail = results.vulnCount > 0 || results.socketIssues.alerts > 0;
1785
+ if (!strict) {
1786
+ return baseFail;
1787
+ }
1788
+ return baseFail || results.outdated.length > 0 || results.duplicates.length > 0 || hasRuntimeWarn;
1789
+ };
1790
+
1791
+ const rollUpStatus = (findings) => {
1792
+ if (findings.some((f) => f.severity === "error")) {
1793
+ return "error";
1794
+ }
1795
+ if (findings.some((f) => f.severity === "warn")) {
1796
+ return "warn";
1797
+ }
1798
+ return "ok";
1799
+ };
1800
+ const buildSupplyChainPosture = (config) => {
1801
+ const findings = [];
1802
+ const security = config?.security;
1803
+ if (!security) {
1804
+ findings.push({
1805
+ detail: "Use defineConfig() from '@visulima/vis/config' to apply secure defaults.",
1806
+ label: "No security config — running with the PM's native defaults",
1807
+ severity: "warn"
1808
+ });
1809
+ return { findings, status: rollUpStatus(findings) };
1810
+ }
1811
+ if (security.minimumReleaseAge === void 0) {
1812
+ findings.push({
1813
+ detail: "Set security.minimumReleaseAge to block packages published in the last N minutes (mitigates supply-chain attacks).",
1814
+ label: "minimumReleaseAge is not set",
1815
+ severity: "warn"
1816
+ });
1817
+ } else if (security.minimumReleaseAge === 0) {
1818
+ findings.push({
1819
+ detail: "New packages can be installed immediately after publishing. Consider setting a non-zero cooldown.",
1820
+ label: "minimumReleaseAge is explicitly 0",
1821
+ severity: "warn"
1822
+ });
1823
+ } else {
1824
+ findings.push({
1825
+ label: `minimumReleaseAge: ${String(security.minimumReleaseAge)} minutes`,
1826
+ severity: "ok"
1827
+ });
1828
+ }
1829
+ if (security.trustPolicy === void 0 || security.trustPolicy === "off") {
1830
+ findings.push({
1831
+ detail: "Packages whose trust level has decreased will not be blocked. Consider 'no-downgrade'.",
1832
+ label: `trustPolicy: ${security.trustPolicy ?? "not set"}`,
1833
+ severity: "warn"
1834
+ });
1835
+ } else {
1836
+ findings.push({
1837
+ label: `trustPolicy: ${security.trustPolicy}`,
1838
+ severity: "ok"
1839
+ });
1840
+ }
1841
+ if (security.blockExoticSubdeps === void 0 || !security.blockExoticSubdeps) {
1842
+ findings.push({
1843
+ detail: "Transitive dependencies can pull code from git repos or tarball URLs. Set to true to block.",
1844
+ label: `blockExoticSubdeps: ${String(security.blockExoticSubdeps ?? false)}`,
1845
+ severity: "warn"
1846
+ });
1847
+ } else {
1848
+ findings.push({
1849
+ label: "blockExoticSubdeps: true",
1850
+ severity: "ok"
1851
+ });
1852
+ }
1853
+ const allowBuildsCount = security.allowBuilds ? Object.keys(security.allowBuilds).length : 0;
1854
+ if (allowBuildsCount === 0) {
1855
+ findings.push({
1856
+ detail: "Lifecycle scripts are blocked by default. List trusted packages here to opt them back in (e.g. esbuild, @prisma/client).",
1857
+ label: "allowBuilds: not configured",
1858
+ severity: "warn"
1859
+ });
1860
+ } else {
1861
+ findings.push({
1862
+ label: `allowBuilds: ${String(allowBuildsCount)} ${allowBuildsCount === 1 ? "entry" : "entries"}`,
1863
+ severity: "ok"
1864
+ });
1865
+ }
1866
+ if (security.strictDepBuilds && allowBuildsCount === 0) {
1867
+ findings.push({
1868
+ detail: "All dependencies with build scripts will be blocked. Run 'vis approve-builds' to populate allowBuilds.",
1869
+ label: "strictDepBuilds is on but allowBuilds is empty",
1870
+ severity: "error"
1871
+ });
1872
+ }
1873
+ return { findings, status: rollUpStatus(findings) };
1874
+ };
1875
+
1876
+ const fmtDuration = (ms) => {
1877
+ if (ms >= 1e3) {
1878
+ return `${(ms / 1e3).toFixed(1)}s`;
1879
+ }
1880
+ return `${String(Math.round(ms))}ms`;
1881
+ };
1882
+ const tracked = async (progress, id, factory, summarize) => {
1883
+ if (!progress) {
1884
+ return factory();
1885
+ }
1886
+ progress.start(id);
1887
+ const startedAt = Date.now();
1888
+ try {
1889
+ const value = await factory();
1890
+ const elapsed = Date.now() - startedAt;
1891
+ const { status, summary } = summarize(value, elapsed);
1892
+ progress.finish(id, status, summary);
1893
+ return value;
1894
+ } catch (error) {
1895
+ const elapsed = Date.now() - startedAt;
1896
+ const message = error instanceof Error ? error.message : String(error);
1897
+ progress.finish(id, "error", `${message} (${fmtDuration(elapsed)})`);
1898
+ throw error;
1899
+ }
1900
+ };
1901
+ const buildSectionFindings = (section, payload) => {
1902
+ const partial = {
1903
+ duplicates: payload.duplicates,
1904
+ optimizations: payload.optimizations,
1905
+ outdated: payload.outdated,
1906
+ runtime: payload.runtime,
1907
+ sections: /* @__PURE__ */ new Set([section])};
1908
+ return flattenFindings(partial);
1909
+ };
1910
+ const streamScans = async (context) => {
1911
+ const { filterPatterns, installed, progress, resolveCodemods, sections, store, visConfig, workspaceRoot } = context;
1912
+ const wantsDeps = sections.has("dependencies");
1913
+ const wantsSec = sections.has("security");
1914
+ const wantsOpt = sections.has("optimization");
1915
+ const wantsRuntime = sections.has("runtime");
1916
+ const sectionFindings = (section, payload) => filterFindingsByPattern(buildSectionFindings(section, payload), filterPatterns);
1917
+ const pm = detectPm(workspaceRoot);
1918
+ const { packageManager } = findPackageManagerSync(workspaceRoot);
1919
+ const rootDeps = collectDepsFromPkgJson(join(workspaceRoot, "package.json"), false);
1920
+ const workspaceDirectories = discoverWorkspacePackages(workspaceRoot);
1921
+ const allDeps = new Set(rootDeps);
1922
+ for (const wsDir of workspaceDirectories) {
1923
+ const wsDeps = collectDepsFromPkgJson(join(resolve(workspaceRoot, wsDir), "package.json"), false);
1924
+ for (const dep of wsDeps) {
1925
+ allDeps.add(dep);
1926
+ }
1927
+ }
1928
+ const npmrcConfig = loadNpmrc(workspaceRoot);
1929
+ const catalogs = readCatalogs(workspaceRoot, packageManager);
1930
+ const socketOptions = buildSocketOptions(visConfig?.security?.socket);
1931
+ const acceptedRisks = visConfig?.security?.socket?.acceptedRisks;
1932
+ const lockText = readLockfileText(workspaceRoot, pm.name);
1933
+ const checkOptions = {
1934
+ exclude: [],
1935
+ ignore: [],
1936
+ include: [],
1937
+ includeLocked: false,
1938
+ includePrerelease: false,
1939
+ security: true,
1940
+ target: "latest"
1941
+ };
1942
+ const duplicates = wantsDeps ? findDuplicateDependencies(workspaceRoot, pm.name) : [];
1943
+ const e18eEntries = wantsOpt ? buildE18eEntries(allDeps) : [];
1944
+ const socketOverrideEntries = wantsOpt ? buildSocketEntries(allDeps, lockText, pm, false) : [];
1945
+ const e18eNames = new Set(e18eEntries.map((e) => e.packageName));
1946
+ const dedupedSocket = socketOverrideEntries.filter((e) => !e18eNames.has(e.packageName));
1947
+ const allOptimizations = [...e18eEntries, ...dedupedSocket];
1948
+ const runtime = wantsRuntime ? runRuntimeDiagnostics() : [];
1949
+ if (store) {
1950
+ if (wantsDeps) {
1951
+ store.startSection("dependencies", catalogs.size > 0 ? "checking outdated catalog dependencies" : "scanning duplicates");
1952
+ }
1953
+ if (wantsSec) {
1954
+ store.startSection(
1955
+ "security",
1956
+ installed.length > 0 ? `scanning ${String(installed.length)} packages for advisories` : "no installed packages to scan"
1957
+ );
1958
+ }
1959
+ if (wantsOpt) {
1960
+ store.startSection("optimization", "matching e18e + socket overrides");
1961
+ }
1962
+ if (wantsRuntime) {
1963
+ store.startSection("runtime", "running runtime diagnostics");
1964
+ }
1965
+ }
1966
+ if (store && wantsRuntime) {
1967
+ store.completeSection(
1968
+ "runtime",
1969
+ sectionFindings("runtime", {
1970
+ duplicates: [],
1971
+ optimizations: [],
1972
+ outdated: [],
1973
+ runtime
1974
+ })
1975
+ );
1976
+ }
1977
+ const needsOutdated = (wantsDeps || wantsSec) && catalogs.size > 0;
1978
+ const outdatedPromise = needsOutdated ? tracked(
1979
+ progress,
1980
+ "outdated",
1981
+ () => checkOutdated(catalogs, checkOptions, npmrcConfig, void 0, workspaceRoot, socketOptions, acceptedRisks),
1982
+ (result, ms) => {
1983
+ const count = result.outdated.length;
1984
+ return {
1985
+ status: count > 0 ? "warn" : "ok",
1986
+ summary: count > 0 ? `${String(count)} outdated · ${fmtDuration(ms)}` : `up to date · ${fmtDuration(ms)}`
1987
+ };
1988
+ }
1989
+ ) : Promise.resolve({ failed: [], ignored: [], outdated: [] });
1990
+ const vulnPromise = wantsSec && installed.length > 0 ? tracked(
1991
+ progress,
1992
+ "vulnerabilities",
1993
+ () => fetchVulnerabilities(
1994
+ installed.map((p) => {
1995
+ return { name: p.name, version: p.version };
1996
+ })
1997
+ ),
1998
+ (vulnMap2, ms) => {
1999
+ let count = 0;
2000
+ for (const list of vulnMap2.values()) {
2001
+ count += list.length;
2002
+ }
2003
+ return {
2004
+ status: count > 0 ? "error" : "ok",
2005
+ summary: count > 0 ? `${String(count)} found · ${fmtDuration(ms)}` : `none found · ${fmtDuration(ms)}`
2006
+ };
2007
+ }
2008
+ ) : Promise.resolve(/* @__PURE__ */ new Map());
2009
+ const socketReportsPromise = wantsSec && socketOptions && installed.length > 0 ? tracked(
2010
+ progress,
2011
+ "socket",
2012
+ () => fetchSocketReports(
2013
+ installed.map((p) => {
2014
+ return { name: p.name, version: p.version };
2015
+ }),
2016
+ socketOptions
2017
+ ),
2018
+ (reports, ms) => {
2019
+ let alerts = 0;
2020
+ let low = 0;
2021
+ for (const report of reports.values()) {
2022
+ alerts += report.alerts.length;
2023
+ if (report.score.overall < DEFAULT_LOW_SCORE_THRESHOLD) {
2024
+ low += 1;
2025
+ }
2026
+ }
2027
+ const issues = alerts + low;
2028
+ return {
2029
+ status: issues > 0 ? "warn" : "ok",
2030
+ summary: issues > 0 ? `${String(alerts)} alert${alerts === 1 ? "" : "s"}, ${String(low)} low-score · ${fmtDuration(ms)}` : `clean · ${fmtDuration(ms)}`
2031
+ };
2032
+ }
2033
+ ) : Promise.resolve(/* @__PURE__ */ new Map());
2034
+ let outdatedError;
2035
+ let vulnError;
2036
+ let socketError;
2037
+ let optError;
2038
+ const outdatedSafe = outdatedPromise.catch((error) => {
2039
+ outdatedError = error instanceof Error ? error.message : String(error);
2040
+ if (!store) {
2041
+ pail.warn(`Outdated scan failed: ${outdatedError}`);
2042
+ }
2043
+ return { failed: [], ignored: [], outdated: [] };
2044
+ });
2045
+ const vulnSafe = vulnPromise.catch((error) => {
2046
+ vulnError = error instanceof Error ? error.message : String(error);
2047
+ if (!store) {
2048
+ pail.warn(`Vulnerability scan failed: ${vulnError}`);
2049
+ }
2050
+ return /* @__PURE__ */ new Map();
2051
+ });
2052
+ const socketSafe = socketReportsPromise.catch((error) => {
2053
+ socketError = error instanceof Error ? error.message : String(error);
2054
+ if (!store) {
2055
+ pail.warn(`Socket scan failed: ${socketError}`);
2056
+ }
2057
+ return /* @__PURE__ */ new Map();
2058
+ });
2059
+ const depsStream = store && wantsDeps ? outdatedSafe.then((res) => {
2060
+ if (outdatedError) {
2061
+ store.failSection("dependencies", outdatedError);
2062
+ return;
2063
+ }
2064
+ store.completeSection(
2065
+ "dependencies",
2066
+ sectionFindings("dependencies", {
2067
+ duplicates,
2068
+ optimizations: [],
2069
+ outdated: res.outdated,
2070
+ runtime: []
2071
+ })
2072
+ );
2073
+ }) : void 0;
2074
+ const secStream = store && wantsSec ? Promise.all([outdatedSafe, vulnSafe, socketSafe]).then(([res]) => {
2075
+ const firstError = outdatedError ?? vulnError ?? socketError;
2076
+ if (firstError) {
2077
+ store.failSection("security", firstError);
2078
+ return;
2079
+ }
2080
+ store.completeSection(
2081
+ "security",
2082
+ sectionFindings("security", {
2083
+ duplicates: [],
2084
+ optimizations: [],
2085
+ outdated: res.outdated,
2086
+ runtime: []
2087
+ })
2088
+ );
2089
+ }) : void 0;
2090
+ const optStream = (async () => {
2091
+ if (resolveCodemods && wantsOpt && allOptimizations.length > 0) {
2092
+ await tracked(
2093
+ progress,
2094
+ "codemods",
2095
+ async () => {
2096
+ await markCodemodAvailability(allOptimizations);
2097
+ return allOptimizations;
2098
+ },
2099
+ (entries, ms) => {
2100
+ const fixable = entries.filter((entry) => entry.hasCodemod || entry.category === "socket").length;
2101
+ return {
2102
+ status: "ok",
2103
+ summary: `${String(fixable)} auto-fixable · ${fmtDuration(ms)}`
2104
+ };
2105
+ }
2106
+ ).catch((error) => {
2107
+ optError = error instanceof Error ? error.message : String(error);
2108
+ });
2109
+ }
2110
+ if (store && wantsOpt) {
2111
+ if (optError) {
2112
+ store.failSection("optimization", optError);
2113
+ return;
2114
+ }
2115
+ store.completeSection(
2116
+ "optimization",
2117
+ sectionFindings("optimization", {
2118
+ duplicates: [],
2119
+ optimizations: allOptimizations,
2120
+ outdated: [],
2121
+ runtime: []
2122
+ })
2123
+ );
2124
+ }
2125
+ })();
2126
+ const [outdatedResult, vulnMap, socketReports] = await Promise.all([outdatedSafe, vulnSafe, socketSafe]);
2127
+ await Promise.all([depsStream, secStream, optStream]);
2128
+ let socketAlerts = 0;
2129
+ let socketLowScore = 0;
2130
+ if (wantsSec && socketOptions) {
2131
+ for (const report of socketReports.values()) {
2132
+ socketAlerts += report.alerts.length;
2133
+ if (report.score.overall < DEFAULT_LOW_SCORE_THRESHOLD) {
2134
+ socketLowScore += 1;
2135
+ }
2136
+ }
2137
+ }
2138
+ let vulnCount = 0;
2139
+ if (wantsSec) {
2140
+ for (const entry of outdatedResult.outdated) {
2141
+ if (entry.vulnerabilities && entry.vulnerabilities.length > 0) {
2142
+ vulnCount += entry.vulnerabilities.length;
2143
+ }
2144
+ }
2145
+ for (const list of vulnMap.values()) {
2146
+ vulnCount += list.length;
2147
+ }
2148
+ }
2149
+ return {
2150
+ duplicates,
2151
+ installedCount: installed.length,
2152
+ optimizations: wantsOpt ? allOptimizations : [],
2153
+ outdated: wantsDeps ? outdatedResult.outdated : [],
2154
+ runtime,
2155
+ sections,
2156
+ socketIssues: { alerts: socketAlerts, lowScore: socketLowScore },
2157
+ // Supply-chain posture is config-derived and cheap; always
2158
+ // compute so the doctor section can render even when --only is
2159
+ // limited to other sections (the section filters its own
2160
+ // visibility in displayResults).
2161
+ supplyChain: buildSupplyChainPosture(visConfig),
2162
+ vulnCount,
2163
+ workspaceCount: workspaceDirectories.length
2164
+ };
2165
+ };
2166
+ const sectionIcon = (status) => {
2167
+ switch (status) {
2168
+ case "error": {
2169
+ return red(SYMBOLS.failure);
2170
+ }
2171
+ case "skip": {
2172
+ return dim(SYMBOLS.dash);
2173
+ }
2174
+ case "warn": {
2175
+ return yellow(SYMBOLS.warning);
2176
+ }
2177
+ default: {
2178
+ return green(SYMBOLS.success);
2179
+ }
2180
+ }
2181
+ };
2182
+ const heading = (title, status) => {
2183
+ const cols = process.stderr.columns ?? 80;
2184
+ const cap = Math.max(20, Math.min(cols - 2, 60));
2185
+ const decoration = SYMBOLS.dash.repeat(2);
2186
+ const label = `${sectionIcon(status)} ${bold(title)}`;
2187
+ const labelWidth = label.replaceAll(/\[[0-9;]*m/g, "").length;
2188
+ const remaining = Math.max(0, cap - labelWidth - decoration.length - 2);
2189
+ return `${decoration} ${label} ${dim(SYMBOLS.dash.repeat(remaining))}`;
2190
+ };
2191
+ const itemOk = (text) => ` ${green(SYMBOLS.success)} ${text}`;
2192
+ const itemWarn = (text) => ` ${yellow(SYMBOLS.warning)} ${text}`;
2193
+ const itemError = (text) => ` ${red(SYMBOLS.failure)} ${text}`;
2194
+ const itemSkip = (text) => ` ${dim(SYMBOLS.dash)} ${dim(text)}`;
2195
+ const countLine = (count, label, parenthetical) => {
2196
+ const head = `${bold(String(count))} ${dim(label)}`;
2197
+ return parenthetical ? `${head} ${dim(`(${parenthetical})`)}` : head;
2198
+ };
2199
+ const displayDependencies = (results) => {
2200
+ if (!results.sections.has("dependencies")) {
2201
+ return;
2202
+ }
2203
+ pail.log("");
2204
+ pail.log(heading("Dependencies", sectionStatus(results, "dependencies")));
2205
+ pail.log(itemOk(countLine(results.installedCount, "packages installed")));
2206
+ if (results.outdated.length > 0) {
2207
+ const majors = results.outdated.filter((e) => e.updateType === "major").length;
2208
+ const minors = results.outdated.filter((e) => e.updateType === "minor").length;
2209
+ const patches = results.outdated.filter((e) => e.updateType === "patch").length;
2210
+ const parts = [];
2211
+ if (majors > 0) {
2212
+ parts.push(`${String(majors)} major`);
2213
+ }
2214
+ if (minors > 0) {
2215
+ parts.push(`${String(minors)} minor`);
2216
+ }
2217
+ if (patches > 0) {
2218
+ parts.push(`${String(patches)} patch`);
2219
+ }
2220
+ pail.log(itemWarn(countLine(results.outdated.length, "outdated", parts.join(", "))));
2221
+ } else {
2222
+ pail.log(itemOk("All dependencies up to date"));
2223
+ }
2224
+ if (results.duplicates.length > 0) {
2225
+ pail.log(itemWarn(countLine(results.duplicates.length, "packages with duplicate versions")));
2226
+ } else {
2227
+ pail.log(itemOk("No duplicate dependencies"));
2228
+ }
2229
+ };
2230
+ const displaySecurity = (results) => {
2231
+ if (!results.sections.has("security")) {
2232
+ return;
2233
+ }
2234
+ pail.log("");
2235
+ pail.log(heading("Security", sectionStatus(results, "security")));
2236
+ if (results.vulnCount > 0) {
2237
+ pail.log(itemError(countLine(results.vulnCount, `vulnerabilit${results.vulnCount === 1 ? "y" : "ies"} found`)));
2238
+ } else {
2239
+ pail.log(itemOk("No known vulnerabilities"));
2240
+ }
2241
+ if (results.socketIssues.alerts > 0) {
2242
+ pail.log(itemWarn(countLine(results.socketIssues.alerts, `Socket.dev security alert${results.socketIssues.alerts === 1 ? "" : "s"}`)));
2243
+ }
2244
+ if (results.socketIssues.lowScore > 0) {
2245
+ pail.log(itemWarn(countLine(results.socketIssues.lowScore, `package${results.socketIssues.lowScore === 1 ? "" : "s"} with low security score`)));
2246
+ }
2247
+ if (results.socketIssues.alerts === 0 && results.socketIssues.lowScore === 0 && results.vulnCount === 0) {
2248
+ pail.log(itemOk("No security issues detected"));
2249
+ }
2250
+ };
2251
+ const displayOptimization = (results) => {
2252
+ if (!results.sections.has("optimization")) {
2253
+ return;
2254
+ }
2255
+ pail.log("");
2256
+ pail.log(heading("Optimization", sectionStatus(results, "optimization")));
2257
+ const counts = summarizeOptimizations(results.optimizations);
2258
+ if (counts.total === 0) {
2259
+ pail.log(itemOk("No optimizations available"));
2260
+ return;
2261
+ }
2262
+ if (counts.native > 0) {
2263
+ pail.log(itemWarn(countLine(counts.native, "replaceable with native APIs")));
2264
+ }
2265
+ if (counts.preferred > 0) {
2266
+ pail.log(itemWarn(countLine(counts.preferred, "with lighter alternatives")));
2267
+ }
2268
+ if (counts.micro > 0) {
2269
+ pail.log(itemWarn(countLine(counts.micro, "trivial micro-utilities")));
2270
+ }
2271
+ if (counts.socket > 0) {
2272
+ pail.log(itemWarn(countLine(counts.socket, "@socketregistry overrides available")));
2273
+ }
2274
+ };
2275
+ const displaySupplyChain = (results) => {
2276
+ pail.log("");
2277
+ pail.log(heading("Supply Chain", results.supplyChain.status));
2278
+ for (const finding of results.supplyChain.findings) {
2279
+ const line = finding.severity === "ok" ? itemOk(finding.label) : finding.severity === "error" ? itemError(finding.label) : itemWarn(finding.label);
2280
+ pail.log(line);
2281
+ if (finding.detail) {
2282
+ pail.log(` ${dim(SYMBOLS.arrow)} ${dim(finding.detail)}`);
2283
+ }
2284
+ }
2285
+ if (results.supplyChain.status !== "ok") {
2286
+ pail.log(` ${dim(SYMBOLS.arrow)} ${dim("Configure with security.* in vis.config.ts. See `vis check --security-config` for details.")}`);
2287
+ }
2288
+ };
2289
+ const displayRuntime = (results) => {
2290
+ if (!results.sections.has("runtime")) {
2291
+ return;
2292
+ }
2293
+ pail.log("");
2294
+ pail.log(heading("Runtime", sectionStatus(results, "runtime")));
2295
+ for (const diagnostic of results.runtime) {
2296
+ if (diagnostic.status === "ok") {
2297
+ pail.log(itemOk(diagnostic.message));
2298
+ } else if (diagnostic.status === "skip") {
2299
+ pail.log(itemSkip(diagnostic.message));
2300
+ } else {
2301
+ pail.log(itemWarn(diagnostic.message));
2302
+ }
2303
+ }
2304
+ };
2305
+ const displaySummary = (results, quiet) => {
2306
+ const criticalCount = results.vulnCount;
2307
+ const runtimeWarnings = results.runtime.filter((d) => d.status === "warn").length;
2308
+ const improvementCount = results.outdated.length + results.duplicates.length + results.optimizations.length + runtimeWarnings;
2309
+ if (quiet) {
2310
+ if (criticalCount === 0 && improvementCount === 0) {
2311
+ pail.success(`Everything looks good! ${dim(`(${fmtDuration(results.elapsedMs)})`)}`);
2312
+ } else {
2313
+ const parts = [];
2314
+ if (criticalCount > 0) {
2315
+ parts.push(red(`${String(criticalCount)} security`));
2316
+ }
2317
+ if (improvementCount > 0) {
2318
+ parts.push(yellow(`${String(improvementCount)} improvement${improvementCount === 1 ? "" : "s"}`));
2319
+ }
2320
+ pail.log(`${red(SYMBOLS.failure)} ${parts.join(", ")} ${dim(`(${fmtDuration(results.elapsedMs)})`)}`);
2321
+ }
2322
+ return;
2323
+ }
2324
+ pail.log("");
2325
+ pail.log(heading("Summary", "ok"));
2326
+ if (criticalCount === 0 && improvementCount === 0) {
2327
+ pail.success(`Everything looks good! ${dim(`(${fmtDuration(results.elapsedMs)})`)}`);
2328
+ } else {
2329
+ if (criticalCount > 0) {
2330
+ pail.error(`${String(criticalCount)} security issue${criticalCount === 1 ? "" : "s"}`);
2331
+ }
2332
+ if (improvementCount > 0) {
2333
+ pail.log(
2334
+ ` ${cyan(SYMBOLS.arrow)} ${bold(String(improvementCount))} ${dim(`improvement${improvementCount === 1 ? "" : "s"} available`)} ${dim(`(${fmtDuration(results.elapsedMs)})`)}`
2335
+ );
2336
+ }
2337
+ }
2338
+ };
2339
+ const displayActions = (results) => {
2340
+ const actions = [];
2341
+ if (results.outdated.length > 0) {
2342
+ actions.push("vis update — update outdated dependencies");
2343
+ }
2344
+ if (results.vulnCount > 0 || results.socketIssues.alerts > 0) {
2345
+ actions.push("vis audit — detailed security analysis");
2346
+ }
2347
+ if (results.optimizations.length > 0) {
2348
+ actions.push("vis optimize — apply optimizations interactively");
2349
+ }
2350
+ if (results.duplicates.length > 0) {
2351
+ actions.push("vis dedupe — reduce duplicate versions");
2352
+ }
2353
+ if (actions.length > 0) {
2354
+ pail.log("");
2355
+ pail.log(bold("Next steps:"));
2356
+ for (const action of actions) {
2357
+ pail.log(` ${dim(SYMBOLS.arrow)} ${action}`);
2358
+ }
2359
+ }
2360
+ pail.log("");
2361
+ };
2362
+ const displayResults = (results, quiet) => {
2363
+ if (!quiet) {
2364
+ displayDependencies(results);
2365
+ displaySecurity(results);
2366
+ displayOptimization(results);
2367
+ displayRuntime(results);
2368
+ displaySupplyChain(results);
2369
+ }
2370
+ displaySummary(results, quiet);
2371
+ };
2372
+ const planScanTasks = (sections, catalogsCount, socketEnabled, installedCount, fix) => {
2373
+ const tasks = [];
2374
+ const wantsDeps = sections.has("dependencies");
2375
+ const wantsSec = sections.has("security");
2376
+ const wantsOpt = sections.has("optimization");
2377
+ if ((wantsDeps || wantsSec) && catalogsCount > 0) {
2378
+ tasks.push({ id: "outdated", label: "Outdated catalog dependencies" });
2379
+ }
2380
+ if (wantsSec && installedCount > 0) {
2381
+ tasks.push({ id: "vulnerabilities", label: "Known vulnerabilities (OSV)" });
2382
+ }
2383
+ if (wantsSec && socketEnabled && installedCount > 0) {
2384
+ tasks.push({ id: "socket", label: "Socket.dev supply-chain reports" });
2385
+ }
2386
+ if (wantsOpt && fix) {
2387
+ tasks.push({ id: "codemods", label: "Codemod availability" });
2388
+ }
2389
+ return tasks;
2390
+ };
2391
+ const printBanner = (input) => {
2392
+ pail.log("");
2393
+ pail.log(`${bold(cyan("vis doctor"))} ${dim("— project health check")}`);
2394
+ pail.log(itemOk(`Detected ${input.packageManagerName} v${input.packageManagerVersion}`));
2395
+ if (input.runtimeFindings.length === 0) {
2396
+ pail.log(itemOk(`Node.js ${input.nodeVersion}`));
2397
+ } else {
2398
+ for (const finding of input.runtimeFindings) {
2399
+ const colour = finding.severity === "error" ? red : yellow;
2400
+ pail.log(itemError(`Runtime: ${colour(finding.message)}`));
2401
+ }
2402
+ pail.log(
2403
+ ` ${dim(SYMBOLS.arrow)} Run ${green("vis toolchain install")} to install pinned versions, or ${green("vis toolchain status")} for the per-tool breakdown.`
2404
+ );
2405
+ }
2406
+ pail.log("");
2407
+ };
2408
+ const execute = async ({ logger, options, visConfig, visConfigError, workspaceRoot: wsRoot }) => {
2409
+ if (!wsRoot) {
2410
+ throw new Error("Could not determine workspace root.");
2411
+ }
2412
+ const isJson = options.format === "json" || options.json === true;
2413
+ const sections = resolveSections(options.only, options.skip);
2414
+ const quiet = Boolean(options.quiet);
2415
+ const noProgress = Boolean(options.noProgress);
2416
+ const filterPatterns = parseFilterPatterns(options.filter);
2417
+ if (sections.size === 0) {
2418
+ pail.error("No sections selected. Check your --only / --skip values.");
2419
+ process.exitCode = 2;
2420
+ return;
2421
+ }
2422
+ const startedAt = Date.now();
2423
+ const pm = detectPm(wsRoot);
2424
+ const runtimeFindings = checkRuntimeVersions(wsRoot);
2425
+ const stdoutTty = Boolean(process.stdout.isTTY);
2426
+ const wantsInteractive = !isJson && stdoutTty && !isInCi && !quiet && !noProgress;
2427
+ if (!isJson && !wantsInteractive) {
2428
+ printBanner({
2429
+ nodeVersion: process.versions.node,
2430
+ packageManagerName: pm.name,
2431
+ packageManagerVersion: pm.version,
2432
+ runtimeFindings});
2433
+ }
2434
+ const catalogs = readCatalogs(wsRoot, findPackageManagerSync(wsRoot).packageManager);
2435
+ const installed = lockedPackages(wsRoot, pm.name);
2436
+ const installedCount = installed.length;
2437
+ const socketEnabled = Boolean(buildSocketOptions(visConfig?.security?.socket));
2438
+ const workspaceDirectories = discoverWorkspacePackages(wsRoot);
2439
+ if (!isJson && !quiet && !wantsInteractive) {
2440
+ const wsLine = workspaceDirectories.length > 0 ? dim(` · ${String(workspaceDirectories.length)} workspace package${workspaceDirectories.length === 1 ? "" : "s"}`) : "";
2441
+ pail.log(`${dim("·")} ${dim("Found")} ${bold(String(installedCount))} ${dim(`installed package${installedCount === 1 ? "" : "s"}`)}${wsLine}`);
2442
+ }
2443
+ const banner = visConfigError ? {
2444
+ hint: visConfigError.file ? `Continuing with default settings — fix or regenerate ${visConfigError.file} (vis init --force).` : "Continuing with default settings.",
2445
+ message: visConfigError.message,
2446
+ severity: "error",
2447
+ title: visConfigError.file ? `Failed to load ${visConfigError.file}` : "Failed to load vis.config"
2448
+ } : void 0;
2449
+ const lockfileNameByPm = {
2450
+ bun: "bun.lock",
2451
+ npm: "package-lock.json",
2452
+ pnpm: "pnpm-lock.yaml",
2453
+ yarn: "yarn.lock"
2454
+ };
2455
+ const lockfileFile = lockfileNameByPm[pm.name];
2456
+ const lockfilePath = lockfileFile ? join(wsRoot, lockfileFile) : void 0;
2457
+ const configFilePath = findVisConfigFile(wsRoot);
2458
+ const cacheEnabled = !options.noCache && !options.fix;
2459
+ const cacheKey = cacheEnabled ? buildDoctorCacheKey({
2460
+ configPath: configFilePath,
2461
+ lockfilePath,
2462
+ sections,
2463
+ socketEnabled,
2464
+ workspaceRoot: wsRoot
2465
+ }) : void 0;
2466
+ const cachedResults = cacheKey ? readDoctorCache(cacheKey) : void 0;
2467
+ const cacheHit = cachedResults !== void 0;
2468
+ let scanResults;
2469
+ let pendingAction;
2470
+ if (wantsInteractive) {
2471
+ const store = cachedResults ? new DoctorStore({ activeSections: sections, findings: filterFindingsByPattern(flattenFindings(cachedResults), filterPatterns) }) : new DoctorStore({ activeSections: sections });
2472
+ const instance = render(React.createElement(VisDoctorApp, { banner, fromCache: cacheHit, startedAt, store }), {
2473
+ alternateScreen: true,
2474
+ exitOnCtrlC: false,
2475
+ interactive: true,
2476
+ patchConsole: true
2477
+ });
2478
+ try {
2479
+ scanResults = cachedResults ?? await streamScans({
2480
+ filterPatterns,
2481
+ installed,
2482
+ resolveCodemods: Boolean(options.fix),
2483
+ sections,
2484
+ store,
2485
+ visConfig,
2486
+ workspaceRoot: wsRoot
2487
+ });
2488
+ } catch (error) {
2489
+ instance.unmount();
2490
+ throw error;
2491
+ }
2492
+ await instance.waitUntilExit();
2493
+ pendingAction = store.getSnapshot().pendingAction;
2494
+ } else if (cachedResults) {
2495
+ scanResults = cachedResults;
2496
+ } else {
2497
+ const tasks = planScanTasks(sections, catalogs.size, socketEnabled, installedCount, Boolean(options.fix));
2498
+ const live = !isJson && !quiet && !noProgress;
2499
+ const progress = startScanProgress(tasks, { live });
2500
+ try {
2501
+ scanResults = await streamScans({
2502
+ filterPatterns,
2503
+ installed,
2504
+ progress,
2505
+ resolveCodemods: Boolean(options.fix),
2506
+ sections,
2507
+ visConfig,
2508
+ workspaceRoot: wsRoot
2509
+ });
2510
+ } finally {
2511
+ progress.stop();
2512
+ }
2513
+ }
2514
+ const fullResults = { ...scanResults, elapsedMs: Date.now() - startedAt };
2515
+ if (cacheKey && !cacheHit) {
2516
+ try {
2517
+ writeDoctorCache(cacheKey, fullResults);
2518
+ } catch {
2519
+ }
2520
+ }
2521
+ const results = applyFilter(fullResults, filterPatterns);
2522
+ if (isJson) {
2523
+ process.stdout.write(`${JSON.stringify(buildJsonPayload(results, pm.name), void 0, 2)}
2524
+ `);
2525
+ if (options.exitCode && shouldFail(results, Boolean(options.strict))) {
2526
+ process.exitCode = 1;
2527
+ }
2528
+ return;
2529
+ }
2530
+ if (cacheHit && !quiet) {
2531
+ pail.log(`${dim("·")} Cached results (use --no-cache to refresh)`);
2532
+ }
2533
+ if (filterPatterns.length > 0 && !quiet) {
2534
+ pail.log(`${dim("·")} Filter active: ${cyan(options.filter ?? "")}`);
2535
+ }
2536
+ displayResults(results, quiet);
2537
+ const hasOrphanWarning = results.runtime.some((d) => d.id === ORPHANS_DIAGNOSTIC_ID && d.status === "warn");
2538
+ const hasOptimizationFixes = results.sections.has("optimization") && results.optimizations.length > 0;
2539
+ if (options.fix && (hasOptimizationFixes || hasOrphanWarning)) {
2540
+ await runFixes({
2541
+ force: Boolean(options.fixForce),
2542
+ logger,
2543
+ pm,
2544
+ recoverOrphans: hasOrphanWarning,
2545
+ results,
2546
+ workspaceRoot: wsRoot
2547
+ });
2548
+ } else if (!quiet) {
2549
+ displayActions(results);
2550
+ }
2551
+ if (pendingAction) {
2552
+ process.stdout.write("\n");
2553
+ process.stdout.write(`${bold("→ ")}${pendingAction.description}
2554
+ `);
2555
+ if (pendingAction.configSnippet) {
2556
+ process.stdout.write("\n");
2557
+ process.stdout.write(`${dim(pendingAction.configSnippet)}
2558
+ `);
2559
+ } else {
2560
+ process.stdout.write(` ${cyan(pendingAction.command)}
2561
+ `);
2562
+ }
2563
+ process.stdout.write("\n");
2564
+ }
2565
+ if (options.exitCode && shouldFail(results, Boolean(options.strict))) {
2566
+ process.exitCode = 1;
2567
+ }
2568
+ };
2569
+ const runFixes = async (opts) => {
2570
+ const { force, logger, pm, recoverOrphans, results, workspaceRoot } = opts;
2571
+ pail.log("");
2572
+ pail.log(heading("Applying fixes", "ok"));
2573
+ const socketEntries = results.optimizations.filter((o) => o.category === "socket" && o.overrideSpec).map((o) => {
2574
+ return { original: o.packageName, spec: o.overrideSpec };
2575
+ });
2576
+ const codemodEntries = results.optimizations.filter((o) => o.category !== "socket" && o.hasCodemod);
2577
+ const manualEntries = results.optimizations.filter((o) => o.category !== "socket" && !o.hasCodemod);
2578
+ let didSomething = false;
2579
+ let codemodsApplied = 0;
2580
+ const codemodFailures = [];
2581
+ if (recoverOrphans) {
2582
+ const recovery = killOrphanedRunners({ force });
2583
+ if (recovery.killed.length > 0) {
2584
+ pail.success(
2585
+ `Cleaned up ${String(recovery.killed.length)} orphaned process${recovery.killed.length === 1 ? "" : "es"} (PIDs: ${recovery.killed.join(", ")}).`
2586
+ );
2587
+ didSomething = true;
2588
+ }
2589
+ if (recovery.failed.length > 0) {
2590
+ const escalation = force ? "" : " Re-run with `--fix --fix-force` to escalate to SIGKILL.";
2591
+ pail.warn(
2592
+ `Could not signal ${String(recovery.failed.length)} orphan${recovery.failed.length === 1 ? "" : "s"}: ${recovery.failed.map((f) => `${String(f.pid)} (${f.reason})`).join(", ")}.${escalation}`
2593
+ );
2594
+ }
2595
+ }
2596
+ if (socketEntries.length > 0) {
2597
+ const overrideResult = applyOverrides(workspaceRoot, join(workspaceRoot, "package.json"), socketEntries, pm);
2598
+ if (overrideResult.added.length > 0) {
2599
+ pail.success(`Added ${String(overrideResult.added.length)} security override${overrideResult.added.length === 1 ? "" : "s"}.`);
2600
+ didSomething = true;
2601
+ }
2602
+ if (overrideResult.updated.length > 0) {
2603
+ pail.success(`Updated ${String(overrideResult.updated.length)} override${overrideResult.updated.length === 1 ? "" : "s"}.`);
2604
+ didSomething = true;
2605
+ }
2606
+ }
2607
+ for (const entry of codemodEntries) {
2608
+ try {
2609
+ const codemodResult = await runCodemod(workspaceRoot, entry.packageName);
2610
+ if (codemodResult.filesChanged > 0) {
2611
+ pail.success(`${entry.packageName}: ${String(codemodResult.filesChanged)} file${codemodResult.filesChanged === 1 ? "" : "s"} updated`);
2612
+ codemodsApplied += 1;
2613
+ didSomething = true;
2614
+ }
2615
+ } catch (error) {
2616
+ const message = error instanceof Error ? error.message : String(error);
2617
+ codemodFailures.push({ error: message, package: entry.packageName });
2618
+ pail.warn(`${entry.packageName}: codemod failed — ${message}`);
2619
+ }
2620
+ }
2621
+ if (socketEntries.length > 0) {
2622
+ pail.log(`${cyan(SYMBOLS.arrow)} Running ${pm.name} install to update lockfile…`);
2623
+ const installOptions = {
2624
+ dev: false,
2625
+ filter: [],
2626
+ force: false,
2627
+ frozenLockfile: false,
2628
+ ignoreScripts: false,
2629
+ lockfileOnly: false,
2630
+ noOptional: false,
2631
+ offline: false,
2632
+ prod: false,
2633
+ recursive: false,
2634
+ silent: false,
2635
+ workspaceRoot: false
2636
+ };
2637
+ runInstall(pm, installOptions, workspaceRoot, logger);
2638
+ didSomething = true;
2639
+ }
2640
+ pail.log("");
2641
+ if (didSomething) {
2642
+ pail.success(`Fixes applied. ${codemodsApplied > 0 ? `${String(codemodsApplied)} codemod${codemodsApplied === 1 ? "" : "s"} applied.` : ""}`.trim());
2643
+ } else {
2644
+ pail.log(itemSkip("No auto-fixable items in the current run."));
2645
+ }
2646
+ if (codemodFailures.length > 0) {
2647
+ pail.warn(
2648
+ `${String(codemodFailures.length)} codemod${codemodFailures.length === 1 ? "" : "s"} failed (run ${green("vis optimize")} for the interactive picker).`
2649
+ );
2650
+ }
2651
+ if (manualEntries.length > 0) {
2652
+ pail.notice(
2653
+ `${String(manualEntries.length)} optimization${manualEntries.length === 1 ? "" : "s"} need manual review (no codemod). Run ${green("vis optimize")} to inspect them.`
2654
+ );
2655
+ }
2656
+ };
2657
+
2658
+ export { execute as default };