@ipul-labs/forge 1.35.0 → 1.35.1

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.
@@ -13,7 +13,7 @@ Please change the parent <Route path="${e}"> to <Route path="${e===`/`?`*`:`${e}
13
13
  `),a=r.length>150?r.slice(-150).join(`
14
14
  `):t.trace;return`--- Job ${e[n]} (${i}) ---\n${a}`}).join(`
15
15
 
16
- `),f=n?.trim()?`${d}\n\n--- User instructions ---\n${n.trim()}`:d;Re(`ci-fix`);let m=P||`default`,v=u.iid;if(g){Me(!0),w(null),p([]);try{let{jobId:e}=await R(`/issues/${u.iid}/refine`,{method:`POST`,body:JSON.stringify({ciContext:f,previousChanges:g.changes,repo:P,secondaryRepos:pt,agent:wt})});Wr(m,v,`ci-fix`,{jobId:e,meta:{isRefine:!0,ordinal:A}}),I.current.add(e),An(e,{repoKey:m,issueIid:v,isRefine:!0})}catch(e){w(e.message),Me(!1)}return}let{jobId:y}=await R(`/issues/${u.iid}/ci-fix`,{method:`POST`,body:JSON.stringify({ciContext:f,model:bt,repo:P,secondaryRepos:pt,agent:wt})});p([]),h([]),_(null),w(null),t(`analyzing`),Wr(m,v,`ci-fix`,{jobId:y,meta:{isRefine:!1}}),I.current.add(y),An(y,{repoKey:m,issueIid:v,isRefine:!1})},Mn=async()=>{if(u){Oe(!0),Ae(null);try{let e=await R(`/issues/${u.iid}/resolution-check`,{method:`POST`,body:JSON.stringify({repo:P,secondaryRepos:pt,agent:wt})});Ee(e),e.cost??e.inputTokens}catch(e){Ae(e.message)}finally{Oe(!1)}}},Nn=()=>{ce&&(j({applications:[{repo:P,branchName:ce.branchName,mr:{iid:ce.iid,web_url:ce.web_url,title:ce.title},pipelineUrl:null}]}),t(`monitoring`))},Pn=async()=>{u&&(await R(`/issues/${u.iid}/close`,{method:`POST`,body:JSON.stringify({repo:P})}),r(e=>e.filter(e=>e.iid!==u.iid)),Ln())},Fn=async()=>{u&&(await R(`/issues/${u.iid}?repo=${encodeURIComponent(P)}`,{method:`DELETE`}),r(e=>e.filter(e=>e.iid!==u.iid)),Ln())},In=()=>{F.current?.close(),F.current=null,et.current?.close(),et.current=null,tt.current=null,I.current.clear(),Ye(!1),Me(!1),Pe([]),te(null),$e(Zr),Ze(null),t(`selected`)},Ln=()=>{At.current+=1,Ot.current?.abort(),F.current?.close(),F.current=null,I.current.clear(),d(null),le(null),ue(!1),fe([]),me([]),ge(!1),Ee(null),Ae(null),_(null),k([]),te(null),j(null),p([]),h([]),w(null),ae(null),Me(!1),Pe([]),$e(Zr),Ze(null),t(`idle`)};return{phase:e,setPhase:t,issues:n,issuesLoading:i,issuesError:c,selectedIssue:u,progressSteps:f,analysisLogs:m,analysisResult:g,analysisError:C,applying:ne,applyResult:ie,applyError:M,pipelineRefreshKey:oe,refining:je,analysisMode:Le,analysisPrompt:ze,setAnalysisPrompt:Be,existingMr:ce,existingMrLoading:N,mergedMrs:de,issueEnvironments:pe,issueEnvironmentsLoading:he,openMRs:_e,openMRsLoading:ye,openMRsError:Ce,storedAnalyses:O,currentOrdinal:A,handleRestoreAnalysis:vn,restoreAnalysisByOrdinal:yn,handleDiscardStoredAnalysis:bn,handleClearIssueCache:xn,handleDestroyAllAnalyses:Sn,handleUndoRefine:Cn,formatAnalysisDate:kr,reloadStatus:It,loadIssues:zt,loadOpenMRs:Bt,handleSelectMR:Jt,handleCreateIssue:Yt,handleUpdateIssue:Xt,handleSelectIssue:Qt,handleBeginAnalysis:en,handleFreshReanalyze:tn,handleCancelAnalysis:nn,handleApprove:fn,handleDeployBranch:pn,handlePromoteBranch:mn,handleCommitToBranch:hn,handleReject:_n,handleRefine:On,handleRefinePipelineFailure:jn,handleCheckResolution:Mn,resolutionCheck:Te,resolutionChecking:De,resolutionError:ke,handleViewExistingMr:Nn,handleCloseIssue:Pn,handleDeleteIssue:Fn,handleBackToIssue:In,handleReset:Ln,availableRepos:st,selectedRepo:P,setSelectedRepo:ut,selectedSecondaryRepos:pt,setSelectedSecondaryRepos:ht,testDeployUrlTemplate:dt,availableModels:gt,selectedModel:bt,setSelectedModel:Et,availableAgents:St,selectedAgent:wt,setSelectedAgent:Dt,validating:Je,validationError:Xe,validation:Qe,handleValidate:ln,handleFixValidation:kn,autoPushOnValidatePass:rt,setAutoPushOnValidatePass:ot,refineThread:v,refineQueue:Ne,thinkingLogs:Fe,backendError:T,statusLoaded:vt,issuesLoaded:o,openMRsLoaded:xe,backendRetrying:ee,retryBackend:Lt,remoteActiveIids:Vt,remoteJobId:Ut,ciFixAttempts:Ve,ciFixLoopWarning:Ge,ciFixMaxAttempts:qe.current,dismissCiFixLoopWarning:()=>{Ke(null),He(0),We(null)}}}var si=`setPhase.setAnalysisPrompt.setAutoPushOnValidatePass.setSelectedModel.setSelectedAgent.setSelectedRepo.setSelectedSecondaryRepos.reloadStatus.handleSelectMR.handleCreateIssue.handleUpdateIssue.handleSelectIssue.handleBeginAnalysis.handleFreshReanalyze.handleCancelAnalysis.handleApprove.handleDeployBranch.handlePromoteBranch.handleCommitToBranch.handleReject.handleRefine.handleUndoRefine.handleRefinePipelineFailure.handleCheckResolution.handleRestoreAnalysis.restoreAnalysisByOrdinal.handleDiscardStoredAnalysis.handleClearIssueCache.handleDestroyAllAnalyses.handleViewExistingMr.handleCloseIssue.handleDeleteIssue.handleBackToIssue.handleReset.handleValidate.handleFixValidation.loadIssues.loadOpenMRs.formatAnalysisDate.retryBackend.dismissCiFixLoopWarning`.split(`.`),ci=(0,x.createContext)(null),li=(0,x.createContext)(null),ui=(0,x.createContext)(null),di=new Set(si);function fi(e){let t={},n={};for(let[r,i]of Object.entries(e))di.has(r)?n[r]=i:t[r]=i;return{state:t,actions:n}}function pi({children:e}){let{state:t,actions:n}=fi(oi()),[r,i]=(0,x.useState)(`issue`),[a,o]=(0,x.useState)(!1),[s,c]=(0,x.useState)(!1);return(0,L.jsx)(ci.Provider,{value:t,children:(0,L.jsx)(li.Provider,{value:n,children:(0,L.jsx)(ui.Provider,{value:{activeDetailTab:r,setActiveDetailTab:i,mrMerged:a,setMrMerged:o,taskModalOpen:s,setTaskModalOpen:c},children:e})})})}function mi(e,t){let n=(0,x.useContext)(e);if(!n)throw Error(`${t} must be used inside <ForgeProvider>`);return n}var V=()=>mi(ci,`useForgeState`),H=()=>mi(li,`useForgeActions`),hi=()=>mi(ui,`useForgeUI`);function gi(){let{availableRepos:e,selectedRepo:t,statusLoaded:n}=V(),{setSelectedRepo:r}=H(),i=e.find(e=>e.id===t)?.label??(n?`No repository`:``);return e.length<2?(0,L.jsx)(`div`,{className:`h-4 truncate text-[12px] leading-4 text-fg-subtle ${n?``:`w-16 animate-pulse rounded bg-surface-hover`}`,children:i},`single`):(0,L.jsxs)(`div`,{className:`relative flex h-4 min-w-0 items-center gap-1 text-[12px] leading-4 text-fg-muted hover:text-fg`,children:[(0,L.jsx)(`span`,{className:`truncate`,children:i}),(0,L.jsx)(B,{name:`chevronDown`,size:12,className:`shrink-0`}),(0,L.jsx)(`select`,{"aria-label":`Repository`,value:t,onChange:e=>r(e.target.value),className:`absolute inset-0 cursor-pointer opacity-0`,children:e.map(e=>(0,L.jsx)(`option`,{value:e.id,children:e.label},e.id))})]},`multi`)}var _i=[`viewer`,`operator`,`approver`,`admin`],vi={viewer:`Viewer`,operator:`Operator`,approver:`Approver`,admin:`Admin`},yi={viewer:`Read issues, merge requests, pipelines and runs.`,operator:`Analyze issues, refine fixes, validate and open merge requests.`,approver:`Everything an operator can do, plus merge and close merge requests.`,admin:`Full access, including connections, repositories, members and the audit log.`},bi=(e,t)=>_i.indexOf(e)>=_i.indexOf(t);function xi(){let[e,t]=(0,x.useState)(!1),{user:n,signOut:r}=Ui(),i=(0,x.useRef)(null);return(0,x.useEffect)(()=>{if(!e)return;let n=e=>{i.current&&!i.current.contains(e.target)&&t(!1)},r=e=>e.key===`Escape`&&t(!1);return document.addEventListener(`mousedown`,n),document.addEventListener(`keydown`,r),()=>{document.removeEventListener(`mousedown`,n),document.removeEventListener(`keydown`,r)}},[e]),(0,L.jsxs)(`div`,{ref:i,className:`relative`,children:[e&&(0,L.jsxs)(`div`,{role:`menu`,className:`absolute bottom-full left-0 right-0 mb-1.5 overflow-hidden rounded-lg bg-surface-raised p-1 shadow-overlay animate-pop-in`,children:[(0,L.jsxs)(`div`,{className:`px-2.5 py-2`,children:[(0,L.jsx)(`div`,{className:`truncate text-sm text-fg`,children:n.email}),(0,L.jsx)(`div`,{className:`text-[11px] text-fg-subtle`,children:vi[n.role]})]}),(0,L.jsx)(`div`,{className:`my-1 h-px bg-line`}),(0,L.jsxs)(`button`,{role:`menuitem`,onClick:()=>{t(!1),r()},className:`flex w-full items-center gap-2 rounded-md px-2.5 py-1.5 text-left text-sm text-fg-muted hover:bg-surface-hover hover:text-fg`,children:[(0,L.jsx)(B,{name:`logout`,size:14}),` Sign out`]})]}),(0,L.jsxs)(`button`,{onClick:()=>t(e=>!e),className:`flex h-10 w-full items-center gap-2.5 rounded-lg px-2 text-sm text-fg-muted transition-colors hover:bg-surface-hover hover:text-fg`,"aria-expanded":e,"aria-haspopup":`menu`,children:[(0,L.jsx)(`span`,{className:`flex size-7 shrink-0 items-center justify-center rounded-full bg-gradient-to-br from-violet-500 to-indigo-600 text-[11px] font-semibold text-on-accent`,children:n.name.slice(0,1).toUpperCase()}),(0,L.jsxs)(`span`,{className:`min-w-0 flex-1 text-left leading-tight`,children:[(0,L.jsx)(`span`,{className:`block truncate text-fg`,children:n.name}),(0,L.jsx)(`span`,{className:`block text-[11px] text-fg-subtle`,children:vi[n.role]})]}),(0,L.jsx)(B,{name:`more`,size:14,className:`text-fg-subtle`})]}),(0,L.jsxs)(`p`,{className:`mt-1 text-center text-[10px] tabular-nums text-fg-subtle opacity-60`,children:[`v`,`1.35.0`]})]})}function Si({count:e,active:t}){let n=!e;return(0,L.jsx)(`span`,{"data-testid":`count-badge`,"aria-hidden":n||void 0,className:Gn(`min-w-6 rounded-md px-1.5 text-center text-[11px] font-medium tabular-nums leading-5`,t?`bg-surface text-fg`:`bg-surface-hover text-fg-subtle`,n&&`invisible`),children:e??0})}function Ci({label:e,icon:t,active:n,onClick:r,badgeCount:i,showBadge:a=!0}){return(0,L.jsxs)(`button`,{type:`button`,onClick:r,"aria-current":n?`page`:void 0,className:Gn(`flex h-8 w-full items-center gap-2.5 rounded-lg px-2.5 text-sm transition-colors`,n?`bg-surface-hover font-medium text-fg`:`text-fg-muted hover:bg-surface-hover/60 hover:text-fg`),children:[(0,L.jsx)(B,{name:t,className:n?`text-accent-fg`:`text-fg-subtle`}),(0,L.jsx)(`span`,{className:`flex-1 text-left`,children:e}),a&&(0,L.jsx)(Si,{count:i,active:n})]})}var wi=(0,x.createContext)(null);function Ti({basePath:e=``,children:t}){let n=ct(),r=at(),i=e.replace(/\/+$/,``),a=(0,x.useCallback)((e,t)=>{n(`${i}${e}`,t)},[n,i]),o=(0,x.useMemo)(()=>{let e=r.pathname;return i&&e.startsWith(i)?e.slice(i.length)||`/`:e},[r.pathname,i]),s=(0,x.useMemo)(()=>({navigate:a,pathname:o,search:r.search,basePath:i}),[a,o,r.search,i]);return(0,L.jsx)(wi.Provider,{value:s,children:t})}function Ei(){let e=(0,x.useContext)(wi);if(!e)throw Error(`useForgeRouter must be used inside <ForgeRouterProvider>`);return e}var Di=[{path:`/issues`,label:`Issues`,icon:`issues`,section:`primary`,adminOnly:!1,preloadMRs:!1,keywords:[]},{path:`/mrs`,label:`Merge requests`,icon:`mergeRequest`,section:`primary`,adminOnly:!1,preloadMRs:!0,keywords:[`mr`,`pull`]},{path:`/pipelines`,label:`Pipelines`,icon:`pipeline`,section:`primary`,adminOnly:!1,preloadMRs:!0,keywords:[`ci`]},{path:`/settings`,label:`Settings`,icon:`settings`,section:`workspace`,adminOnly:!1,preloadMRs:!1,keywords:[`config`,`tokens`,`repos`]},{path:`/members`,label:`Members`,icon:`users`,section:`workspace`,adminOnly:!0,preloadMRs:!1,keywords:[`users`,`roles`,`team`]},{path:`/audit`,label:`Audit log`,icon:`shield`,section:`workspace`,adminOnly:!0,preloadMRs:!1,keywords:[`activity`,`security`]}];function Oi(e,t){return e===t||e.startsWith(`${t}/`)}function ki(e){return Di.find(t=>Oi(e,t.path))}function Ai(){let{can:e}=Ui(),t=e(`admin`);return Di.filter(e=>t||!e.adminOnly)}function U(){let{issues:e,issuesLoaded:t,openMRs:n,openMRsLoading:r}=V(),{loadOpenMRs:i}=H(),{navigate:a,pathname:o}=Ei(),s=Ai(),c={"/issues":t?e.length:void 0,"/mrs":n.length,"/pipelines":n.filter(e=>e.pipeline).length};return s.map(e=>({...e,active:Oi(o,e.path),badgeCount:c[e.path],go:()=>{a(e.path,{replace:!0}),e.preloadMRs&&n.length===0&&!r&&i()}}))}function ji(){let e=U();return(0,L.jsx)(`nav`,{"aria-label":`Primary`,className:`flex flex-col gap-0.5`,children:e.map((t,n)=>(0,L.jsxs)(x.Fragment,{children:[t.section===`workspace`&&e[n-1]?.section!==`workspace`&&(0,L.jsx)(`div`,{className:`px-2.5 pb-1 pt-5 text-xs font-medium text-fg-subtle`,children:`Workspace`}),(0,L.jsx)(Ci,{label:t.label,icon:t.icon,active:t.active,onClick:t.go,badgeCount:t.badgeCount,showBadge:t.section===`primary`})]},t.path))})}function Mi({size:e=28}){return(0,L.jsx)(`span`,{className:`flex shrink-0 items-center justify-center rounded-lg bg-gradient-to-br from-violet-500 to-indigo-600 text-on-accent shadow-sm`,style:{width:e,height:e},"aria-hidden":!0,children:(0,L.jsx)(`svg`,{width:e*.55,height:e*.55,viewBox:`0 0 24 24`,fill:`none`,stroke:`currentColor`,strokeWidth:2.2,strokeLinecap:`round`,strokeLinejoin:`round`,children:(0,L.jsx)(`path`,{d:`M4 20h16M7 16h10l-1.5-5h-7L7 16Zm1.5-5L10 4h4l1.5 7`})})})}function Ni({open:e,onClose:t}){let{setTaskModalOpen:n}=hi(),{preference:r,setPreference:i}=ir(),{can:a}=Ui();return(0,x.useEffect)(()=>{if(!e)return;let n=e=>e.key===`Escape`&&t();return document.addEventListener(`keydown`,n),()=>document.removeEventListener(`keydown`,n)},[e,t]),(0,L.jsxs)(L.Fragment,{children:[e&&(0,L.jsx)(`div`,{"aria-hidden":!0,className:`fixed inset-0 z-40 bg-black/40 lg:hidden`,onClick:t}),(0,L.jsxs)(`aside`,{className:Gn(`w-60 shrink-0 flex-col border-r border-line bg-surface lg:static lg:flex`,e?`fixed inset-y-0 left-0 z-50 flex shadow-overlay animate-[forge-slide-in_280ms_cubic-bezier(0.32,0.72,0,1)] pt-[env(safe-area-inset-top)]`:`hidden`),children:[(0,L.jsxs)(`div`,{className:`flex h-14 items-center gap-2.5 px-4`,children:[(0,L.jsx)(Mi,{}),(0,L.jsxs)(`div`,{className:`min-w-0 leading-tight`,children:[(0,L.jsx)(`div`,{className:`text-sm font-semibold tracking-tight text-fg`,children:`Forge`}),(0,L.jsx)(gi,{})]})]}),(0,L.jsxs)(`div`,{className:`flex flex-1 flex-col gap-4 overflow-y-auto px-3 pb-3 pt-2`,children:[(0,L.jsxs)(z,{variant:`primary`,className:`w-full`,disabled:!a(`operator`),title:a(`operator`)?void 0:`Requires the operator role`,onClick:()=>n(!0),children:[(0,L.jsx)(B,{name:`plus`,size:15}),` New task`]}),(0,L.jsx)(ji,{})]}),(0,L.jsxs)(`div`,{className:`flex flex-col gap-2 border-t border-line p-3 pb-[max(0.75rem,env(safe-area-inset-bottom))]`,children:[(0,L.jsx)(or,{value:r,onChange:i}),(0,L.jsx)(xi,{})]})]})]})}function Pi({title:e,subtitle:t,children:n}){return(0,L.jsx)(`main`,{className:`flex min-h-screen items-center justify-center bg-canvas px-4 py-12`,children:(0,L.jsxs)(`div`,{className:`w-full max-w-sm`,children:[(0,L.jsxs)(`div`,{className:`mb-8 flex flex-col items-center gap-4 text-center`,children:[(0,L.jsx)(Mi,{size:40}),(0,L.jsxs)(`div`,{children:[(0,L.jsx)(`h1`,{className:`text-xl font-semibold tracking-tight text-fg`,children:e}),(0,L.jsx)(`p`,{className:`mt-1 text-sm text-fg-muted`,children:t})]})]}),(0,L.jsx)(`div`,{className:`rounded-2xl border border-line bg-surface-raised p-6 shadow-sm`,children:n})]})})}var Fi=`h-10 w-full rounded-lg border border-line bg-canvas px-3 text-sm text-fg placeholder:text-fg-subtle focus:border-accent-hover focus:outline-none`;function Ii({message:e}){return(0,L.jsx)(`p`,{role:e?`alert`:void 0,className:e?`rounded-lg bg-red-950/50 px-3 py-2 text-sm text-red-300 ring-1 ring-inset ring-red-800/60`:`hidden`,children:e})}function Li({sso:e,onSignedIn:t}){let[n,r]=(0,x.useState)(``),[i,a]=(0,x.useState)(``),[o,s]=(0,x.useState)(null),[c,l]=(0,x.useState)(!1);return(0,L.jsx)(Pi,{title:`Sign in to Forge`,subtitle:`Use your work account to continue.`,children:(0,L.jsxs)(`div`,{className:`flex flex-col gap-5`,children:[e&&(0,L.jsxs)(L.Fragment,{children:[(0,L.jsxs)(z,{variant:`secondary`,className:`h-10 w-full`,onClick:()=>{window.location.assign(Wn.ssoStartUrl(window.location.pathname+window.location.search))},children:[`Continue with `,e.name]}),(0,L.jsxs)(`div`,{className:`flex items-center gap-3 text-xs text-fg-subtle`,children:[(0,L.jsx)(`span`,{className:`h-px flex-1 bg-line`}),`or use a password`,(0,L.jsx)(`span`,{className:`h-px flex-1 bg-line`})]})]}),(0,L.jsxs)(`form`,{onSubmit:async e=>{e.preventDefault(),l(!0),s(null);try{await Wn.login(n,i),t()}catch(e){s(e.message)}finally{l(!1)}},className:`flex flex-col gap-4`,children:[(0,L.jsxs)(`label`,{className:`flex flex-col gap-1.5 text-sm font-medium text-fg`,children:[`Email`,(0,L.jsx)(`input`,{type:`email`,autoComplete:`username`,required:!0,value:n,onChange:e=>r(e.target.value),className:Fi})]}),(0,L.jsxs)(`label`,{className:`flex flex-col gap-1.5 text-sm font-medium text-fg`,children:[`Password`,(0,L.jsx)(`input`,{type:`password`,autoComplete:`current-password`,required:!0,value:i,onChange:e=>a(e.target.value),className:Fi})]}),(0,L.jsx)(Ii,{message:o}),(0,L.jsx)(z,{type:`submit`,variant:`primary`,className:`h-10 w-full`,disabled:c,children:c?`Signing in…`:`Sign in`})]})]})})}function Ri({requiresToken:e,onDone:t}){let[n,r]=(0,x.useState)({name:``,email:``,password:``,setupToken:``}),[i,a]=(0,x.useState)(null),[o,s]=(0,x.useState)(!1),c=e=>t=>r(n=>({...n,[e]:t.target.value}));return(0,L.jsx)(Pi,{title:`Set up Forge`,subtitle:`Create the first admin account for this workspace.`,children:(0,L.jsxs)(`form`,{onSubmit:async r=>{r.preventDefault(),s(!0),a(null);try{await Wn.setup(e?n:{...n,setupToken:void 0}),t()}catch(e){a(e.message)}finally{s(!1)}},className:`flex flex-col gap-4`,children:[(0,L.jsxs)(`label`,{className:`flex flex-col gap-1.5 text-sm font-medium text-fg`,children:[`Your name`,(0,L.jsx)(`input`,{autoComplete:`name`,required:!0,value:n.name,onChange:c(`name`),className:Fi})]}),(0,L.jsxs)(`label`,{className:`flex flex-col gap-1.5 text-sm font-medium text-fg`,children:[`Work email`,(0,L.jsx)(`input`,{type:`email`,autoComplete:`username`,required:!0,value:n.email,onChange:c(`email`),className:Fi})]}),(0,L.jsxs)(`label`,{className:`flex flex-col gap-1.5 text-sm font-medium text-fg`,children:[`Password`,(0,L.jsx)(`input`,{type:`password`,autoComplete:`new-password`,required:!0,minLength:12,value:n.password,onChange:c(`password`),className:Fi}),(0,L.jsx)(`span`,{className:`text-xs font-normal text-fg-subtle`,children:`At least 12 characters.`})]}),e&&(0,L.jsxs)(`div`,{className:`flex flex-col gap-1.5`,children:[(0,L.jsxs)(`label`,{className:`flex flex-col gap-1.5 text-sm font-medium text-fg`,children:[`Setup code`,(0,L.jsx)(`input`,{autoComplete:`off`,required:!0,"aria-describedby":`setup-code-hint`,value:n.setupToken,onChange:c(`setupToken`),className:`h-10 w-full rounded-lg border border-line bg-canvas px-3 text-sm text-fg placeholder:text-fg-subtle focus:border-accent-hover focus:outline-none`})]}),(0,L.jsx)(`span`,{id:`setup-code-hint`,className:`text-xs text-fg-subtle`,children:`Printed in the server log on first start (or your FORGE_TOKEN, if set). It proves you operate this install.`})]}),(0,L.jsx)(Ii,{message:i}),(0,L.jsx)(z,{type:`submit`,variant:`primary`,className:`h-10 w-full`,disabled:o,children:o?`Creating…`:`Create admin account`})]})})}var zi=(0,x.createContext)(null),Bi=700;function Vi(){let e=window.__forgeBootT??0;return Math.round((performance.now()-e)%Bi)}function Hi({children:e}){let[t,n]=(0,x.useState)(null),[r,i]=(0,x.useState)(null),a=(0,x.useRef)(null),o=()=>a.current??=Wn.me().then(e=>{n(e),i(null)}).catch(e=>i(e.message)).finally(()=>{a.current=null});if((0,x.useEffect)(()=>{o();let e=()=>o();return window.addEventListener(Hn,e),()=>window.removeEventListener(Hn,e)},[]),r&&!t)return(0,L.jsxs)(`div`,{role:`alert`,className:`flex min-h-screen flex-col items-center justify-center gap-3 bg-canvas px-4 text-center`,children:[(0,L.jsx)(`p`,{className:`text-sm font-medium text-fg`,children:`Forge couldn't load your session.`}),(0,L.jsx)(`p`,{className:`max-w-sm text-sm text-fg-muted`,children:r}),(0,L.jsx)(`button`,{onClick:o,className:`text-sm text-accent-fg hover:underline`,children:`Try again`})]});if(!t)return(0,L.jsx)(`div`,{className:`flex min-h-screen items-center justify-center bg-canvas`,"aria-busy":`true`,children:(0,L.jsx)(`div`,{className:`size-5 animate-spin rounded-full border-2 border-line-strong border-t-violet-500`,style:{animationDelay:`-${Vi()}ms`}})});if(t.needsSetup)return(0,L.jsx)(Ri,{requiresToken:t.setupRequiresToken??!0,onDone:o});if(!t.user)return(0,L.jsx)(Li,{sso:t.sso,onSignedIn:o});let s=t.user,c={user:s,can:e=>bi(s.role,e),signOut:async()=>{await Wn.logout().catch(()=>void 0),await o()}};return(0,L.jsx)(zi.Provider,{value:c,children:e})}function Ui(){let e=(0,x.useContext)(zi);if(!e)throw Error(`useSession must be used inside <AuthGate>`);return e}var Wi={neutral:`bg-surface-hover/70 text-fg ring-line-strong/60`,accent:`bg-violet-950/60 text-violet-300 ring-violet-800/60`,success:`bg-emerald-950/60 text-emerald-300 ring-emerald-800/60`,warning:`bg-amber-950/60 text-amber-300 ring-amber-800/60`,danger:`bg-red-950/60 text-red-300 ring-red-800/60`,info:`bg-sky-950/60 text-sky-300 ring-sky-800/60`};function Gi({tone:e=`neutral`,dot:t,className:n,children:r}){return(0,L.jsxs)(`span`,{className:Gn(`inline-flex items-center gap-1.5 rounded-md px-1.5 py-0.5 text-[11px] font-medium ring-1 ring-inset whitespace-nowrap`,Wi[e],n),children:[t&&(0,L.jsx)(`span`,{className:`size-1.5 rounded-full bg-current`,"aria-hidden":!0}),r]})}function Ki({icon:e,title:t,description:n,action:r}){return(0,L.jsxs)(`div`,{className:`flex min-h-[40vh] flex-col items-center justify-center gap-3 rounded-xl border border-dashed border-line px-6 py-12 text-center`,children:[(0,L.jsx)(`div`,{className:`flex size-11 items-center justify-center rounded-2xl bg-surface-hover text-fg-muted ring-1 ring-line`,children:(0,L.jsx)(B,{name:e,size:20})}),(0,L.jsxs)(`div`,{children:[(0,L.jsx)(`p`,{className:`text-sm font-medium text-fg`,children:t}),n&&(0,L.jsx)(`p`,{className:`mt-1 max-w-sm text-sm text-fg-muted`,children:n})]}),r]})}function qi({title:e,description:t,actions:n}){return(0,L.jsxs)(`header`,{className:`flex flex-wrap items-end justify-between gap-4`,children:[(0,L.jsxs)(`div`,{className:`min-w-0`,children:[(0,L.jsx)(`h1`,{className:`text-xl font-semibold tracking-tight text-fg`,children:e}),t&&(0,L.jsx)(`p`,{className:`mt-1 text-sm text-fg-muted`,children:t})]}),n&&(0,L.jsx)(`div`,{className:`flex items-center gap-2`,children:n})]})}var Ji=(e,t,n)=>R(t,{method:e,body:n===void 0?void 0:JSON.stringify(n)}),Yi={listMembers:()=>R(`/admin/users`),addMember:e=>Ji(`POST`,`/admin/users`,e),updateMember:(e,t)=>Ji(`PATCH`,`/admin/users/${e}`,t),listAudit:e=>{let t=new URLSearchParams;return e.action&&t.set(`action`,e.action),e.before&&t.set(`before`,String(e.before)),R(`/admin/audit?${t}`)},listTokens:()=>R(`/auth/tokens`),createToken:e=>Ji(`POST`,`/auth/tokens`,{name:e}),revokeToken:e=>Ji(`DELETE`,`/auth/tokens/${e}`)},Xi=new Intl.DateTimeFormat(void 0,{month:`short`,day:`numeric`,hour:`numeric`,minute:`2-digit`}),Zi=e=>e?Xi.format(new Date(e)):`—`,Qi={"auth.setup":`Set up Forge`,"auth.login":`Signed in`,"auth.sso_login":`Signed in with SSO`,"auth.logout":`Signed out`,"user.create":`Added member`,"user.update":`Updated member`,"token.create":`Created API token`,"token.revoke":`Revoked API token`,"PUT /config":`Changed workspace settings`,"DELETE /workspaces":`Deleted local workspace`,"POST /workspaces/purge":`Purged local workspaces`,"POST /issues":`Created issue`,"PUT /issues/:iid":`Edited issue`,"POST /issues/:iid/close":`Closed issue`,"DELETE /issues/:iid":`Deleted issue`,"POST /issues/:iid/analyze":`Started analysis`,"POST /issues/:iid/refine":`Refined fix`,"POST /issues/:iid/ci-fix":`Asked AI to fix CI`,"POST /issues/:iid/resolution-check":`Checked resolution`,"POST /validate":`Ran validation`,"POST /apply":`Opened merge request`,"POST /commit-to-branch":`Committed to branch`,"POST /promote-branch":`Promoted branch`,"POST /deploy-branch":`Deployed branch`,"POST /jobs/:jobId/cancel":`Cancelled job`,"POST /mr/:mrIid/merge":`Merged merge request`,"POST /mr/:mrIid/auto-merge":`Enabled auto-merge`,"POST /mr/:mrIid/cancel-auto-merge":`Cancelled auto-merge`,"POST /mr/:mrIid/close":`Closed merge request`,"POST /mr/:mrIid/reopen":`Reopened merge request`,"POST /mr/:mrIid/rebase":`Rebased merge request`,"POST /mr/:mrIid/draft":`Changed draft status`};function $i(e){return Qi[e]??e}var ea={success:{label:`Success`,tone:`success`},failure:{label:`Failed`,tone:`danger`},denied:{label:`Denied`,tone:`warning`}},ta=[{value:``,label:`All activity`},{value:`auth.login`,label:`Sign-ins`},{value:`auth.sso_login`,label:`SSO sign-ins`},{value:`POST /mr/:mrIid/merge`,label:`Merges`},{value:`POST /apply`,label:`Merge requests opened`},{value:`PUT /config`,label:`Settings changes`},{value:`user.update`,label:`Member changes`}];function na(){let[e,t]=(0,x.useState)(``),[n,r]=(0,x.useState)(null),[i,a]=(0,x.useState)(null),[o,s]=(0,x.useState)(null);return(0,x.useEffect)(()=>{r(null),Yi.listAudit({action:e}).then(e=>{r(e.events),a(e.nextCursor)}).catch(e=>s(e.message))},[e]),(0,L.jsxs)(`div`,{className:`mx-auto flex w-full max-w-6xl flex-col gap-6`,children:[(0,L.jsx)(qi,{title:`Audit log`,description:`Every sign-in and every change made through Forge. Entries can't be edited or deleted.`,actions:(0,L.jsx)(`select`,{"aria-label":`Filter activity`,value:e,onChange:e=>t(e.target.value),className:`h-8 rounded-lg border border-line bg-surface-raised px-2.5 text-xs text-fg focus:border-accent-hover focus:outline-none`,children:ta.map(e=>(0,L.jsx)(`option`,{value:e.value,children:e.label},e.value))})}),o&&(0,L.jsx)(`p`,{role:`alert`,className:`text-sm text-red-400`,children:o}),n&&n.length===0?(0,L.jsx)(Ki,{icon:`inbox`,title:`No activity yet`,description:`Sign-ins and changes will appear here as they happen.`}):(0,L.jsx)(`div`,{className:`overflow-x-auto rounded-xl border border-line bg-surface-raised`,children:(0,L.jsxs)(`table`,{"aria-label":`Audit log`,className:`w-full min-w-[860px] table-fixed text-left text-sm`,children:[(0,L.jsxs)(`colgroup`,{children:[(0,L.jsx)(`col`,{className:`w-36`}),(0,L.jsx)(`col`,{className:`w-52`}),(0,L.jsx)(`col`,{}),(0,L.jsx)(`col`,{className:`w-44`}),(0,L.jsx)(`col`,{className:`w-28`})]}),(0,L.jsx)(`thead`,{className:`border-b border-line text-xs font-medium text-fg-subtle`,children:(0,L.jsxs)(`tr`,{children:[(0,L.jsx)(`th`,{scope:`col`,className:`py-2.5 pl-4 pr-3 font-medium`,children:`When`}),(0,L.jsx)(`th`,{scope:`col`,className:`px-3 py-2.5 font-medium`,children:`Who`}),(0,L.jsx)(`th`,{scope:`col`,className:`px-3 py-2.5 font-medium`,children:`What`}),(0,L.jsx)(`th`,{scope:`col`,className:`px-3 py-2.5 font-medium`,children:`Where`}),(0,L.jsx)(`th`,{scope:`col`,className:`py-2.5 pl-3 pr-4 text-right font-medium`,children:`Result`})]})}),(0,L.jsxs)(`tbody`,{className:`divide-y divide-line`,children:[(n??[]).map(e=>(0,L.jsxs)(`tr`,{children:[(0,L.jsx)(`td`,{className:`py-3 pl-4 pr-3 tabular-nums text-fg-muted`,children:Zi(e.at)}),(0,L.jsx)(`td`,{className:`truncate px-3 py-3 text-fg`,title:e.ip??void 0,children:e.actorLabel}),(0,L.jsxs)(`td`,{className:`px-3 py-3`,children:[(0,L.jsx)(`span`,{className:`text-fg`,children:$i(e.action)}),e.target&&(0,L.jsx)(`span`,{className:`ml-2 font-mono text-xs text-fg-subtle`,children:e.target})]}),(0,L.jsx)(`td`,{className:`truncate px-3 py-3 font-mono text-xs text-fg-subtle`,children:e.repo?.replace(/^[a-z]+:/,``)??`—`}),(0,L.jsx)(`td`,{className:`py-3 pl-3 pr-4 text-right`,children:(0,L.jsx)(Gi,{tone:ea[e.outcome].tone,children:ea[e.outcome].label})})]},e.id)),!n&&Array.from({length:5}).map((e,t)=>(0,L.jsx)(`tr`,{children:(0,L.jsx)(`td`,{colSpan:5,className:`h-[45px] px-4`,children:(0,L.jsx)(`div`,{className:`h-3 w-1/2 animate-pulse rounded bg-surface-hover`})})},t))]})]})}),i&&(0,L.jsx)(`div`,{className:`flex justify-center`,children:(0,L.jsx)(z,{size:`sm`,onClick:async()=>{if(!i)return;let t=await Yi.listAudit({action:e,before:i});r(e=>[...e??[],...t.events]),a(t.nextCursor)},children:`Load older activity`})})]})}var ra=`h-9 w-full rounded-lg border border-line bg-canvas px-3 text-sm text-fg placeholder:text-fg-subtle focus:border-accent-hover focus:outline-none`,ia=`h-8 rounded-lg border border-line bg-surface-raised px-2 text-xs text-fg focus:border-accent-hover focus:outline-none disabled:opacity-50`;function aa({onAdded:e,onCancel:t}){let[n,r]=(0,x.useState)({name:``,email:``,role:`operator`,password:``}),[i,a]=(0,x.useState)(null),[o,s]=(0,x.useState)(!1);return(0,L.jsxs)(`form`,{onSubmit:async t=>{t.preventDefault(),s(!0),a(null);try{e((await Yi.addMember(n)).user)}catch(e){a(e.message)}finally{s(!1)}},"aria-label":`Add member`,className:`grid gap-4 rounded-xl border border-line bg-surface-raised p-5 shadow-sm sm:grid-cols-2`,children:[(0,L.jsxs)(`label`,{className:`flex flex-col gap-1.5 text-sm font-medium text-fg`,children:[`Name`,(0,L.jsx)(`input`,{required:!0,value:n.name,onChange:e=>r({...n,name:e.target.value}),className:ra})]}),(0,L.jsxs)(`label`,{className:`flex flex-col gap-1.5 text-sm font-medium text-fg`,children:[`Email`,(0,L.jsx)(`input`,{type:`email`,required:!0,value:n.email,onChange:e=>r({...n,email:e.target.value}),className:ra})]}),(0,L.jsxs)(`label`,{className:`flex flex-col gap-1.5 text-sm font-medium text-fg`,children:[`Role`,(0,L.jsx)(`select`,{value:n.role,onChange:e=>r({...n,role:e.target.value}),className:ra,children:_i.map(e=>(0,L.jsx)(`option`,{value:e,children:vi[e]},e))}),(0,L.jsx)(`span`,{className:`text-xs font-normal text-fg-subtle`,children:yi[n.role]})]}),(0,L.jsxs)(`label`,{className:`flex flex-col gap-1.5 text-sm font-medium text-fg`,children:[`Temporary password`,(0,L.jsx)(`input`,{type:`password`,required:!0,minLength:12,autoComplete:`new-password`,value:n.password,onChange:e=>r({...n,password:e.target.value}),className:ra}),(0,L.jsx)(`span`,{className:`text-xs font-normal text-fg-subtle`,children:`Share it privately. SSO users can leave Forge's password unused.`})]}),i&&(0,L.jsx)(`p`,{role:`alert`,className:`text-sm text-red-400 sm:col-span-2`,children:i}),(0,L.jsxs)(`div`,{className:`flex justify-end gap-2 sm:col-span-2`,children:[(0,L.jsx)(z,{variant:`ghost`,size:`sm`,onClick:t,children:`Cancel`}),(0,L.jsx)(z,{type:`submit`,variant:`primary`,size:`sm`,disabled:o,children:`Add`})]})]})}function oa(){let{user:e}=Ui(),[t,n]=(0,x.useState)(null),[r,i]=(0,x.useState)(null),[a,o]=(0,x.useState)(!1);(0,x.useEffect)(()=>{Yi.listMembers().then(e=>n(e.users)).catch(e=>i(e.message))},[]);let s=async(e,t)=>{i(null);try{let{user:r}=await Yi.updateMember(e.id,t);n(e=>e?.map(e=>e.id===r.id?r:e)??null)}catch(e){i(e.message)}};return(0,L.jsxs)(`div`,{className:`mx-auto flex w-full max-w-6xl flex-col gap-6`,children:[(0,L.jsx)(qi,{title:`Members`,description:`Who can use this Forge workspace, and what they're allowed to do.`,actions:(0,L.jsxs)(z,{size:`sm`,variant:`primary`,onClick:()=>o(!0),children:[(0,L.jsx)(B,{name:`plus`,size:14}),` Add member`]})}),a&&(0,L.jsx)(aa,{onCancel:()=>o(!1),onAdded:e=>{n(t=>[...t??[],e]),o(!1)}}),r&&(0,L.jsx)(`p`,{role:`alert`,className:`rounded-lg bg-red-950/50 px-3 py-2 text-sm text-red-300 ring-1 ring-inset ring-red-800/60`,children:r}),(0,L.jsx)(`div`,{className:`overflow-x-auto rounded-xl border border-line bg-surface-raised`,children:(0,L.jsxs)(`table`,{"aria-label":`Members`,className:`w-full min-w-[760px] table-fixed text-left text-sm`,children:[(0,L.jsxs)(`colgroup`,{children:[(0,L.jsx)(`col`,{}),(0,L.jsx)(`col`,{className:`w-40`}),(0,L.jsx)(`col`,{className:`w-28`}),(0,L.jsx)(`col`,{className:`w-40`}),(0,L.jsx)(`col`,{className:`w-32`})]}),(0,L.jsx)(`thead`,{className:`border-b border-line text-xs font-medium text-fg-subtle`,children:(0,L.jsxs)(`tr`,{children:[(0,L.jsx)(`th`,{scope:`col`,className:`py-2.5 pl-4 pr-3 font-medium`,children:`Member`}),(0,L.jsx)(`th`,{scope:`col`,className:`px-3 py-2.5 font-medium`,children:`Role`}),(0,L.jsx)(`th`,{scope:`col`,className:`px-3 py-2.5 font-medium`,children:`Sign-in`}),(0,L.jsx)(`th`,{scope:`col`,className:`px-3 py-2.5 font-medium`,children:`Last active`}),(0,L.jsx)(`th`,{scope:`col`,className:`py-2.5 pl-3 pr-4 text-right font-medium`,children:`Access`})]})}),(0,L.jsxs)(`tbody`,{className:`divide-y divide-line`,children:[(t??[]).map(t=>(0,L.jsxs)(`tr`,{className:t.disabled?`opacity-60`:void 0,children:[(0,L.jsx)(`td`,{className:`py-3 pl-4 pr-3`,children:(0,L.jsxs)(`div`,{className:`flex items-center gap-3`,children:[(0,L.jsx)(`span`,{className:`flex size-8 shrink-0 items-center justify-center rounded-full bg-surface-hover text-xs font-semibold text-fg-muted ring-1 ring-line`,children:t.name.slice(0,1).toUpperCase()}),(0,L.jsxs)(`div`,{className:`min-w-0`,children:[(0,L.jsxs)(`div`,{className:`truncate font-medium text-fg`,children:[t.name,` `,t.id===e.id&&(0,L.jsx)(`span`,{className:`font-normal text-fg-subtle`,children:`(you)`})]}),(0,L.jsx)(`div`,{className:`truncate text-xs text-fg-subtle`,children:t.email})]})]})}),(0,L.jsx)(`td`,{className:`px-3 py-3`,children:(0,L.jsx)(`select`,{"aria-label":`Role for ${t.name}`,value:t.role,onChange:e=>s(t,{role:e.target.value}),title:yi[t.role],className:ia,children:_i.map(e=>(0,L.jsx)(`option`,{value:e,children:vi[e]},e))})}),(0,L.jsx)(`td`,{className:`px-3 py-3`,children:(0,L.jsx)(Gi,{tone:t.authProvider===`oidc`?`info`:`neutral`,children:t.authProvider===`oidc`?`SSO`:`Password`})}),(0,L.jsx)(`td`,{className:`px-3 py-3 tabular-nums text-fg-muted`,children:Zi(t.lastLoginAt)}),(0,L.jsx)(`td`,{className:`py-3 pl-3 pr-4 text-right`,children:(0,L.jsx)(z,{size:`sm`,variant:`ghost`,disabled:t.id===e.id,onClick:()=>s(t,{disabled:!t.disabled}),children:t.disabled?`Enable`:`Disable`})})]},t.id)),!t&&Array.from({length:3}).map((e,t)=>(0,L.jsx)(`tr`,{children:(0,L.jsx)(`td`,{colSpan:5,className:`h-[57px] px-4`,children:(0,L.jsx)(`div`,{className:`h-3 w-1/3 animate-pulse rounded bg-surface-hover`})})},t))]})]})})]})}var sa=/\b(error|failed|failure|exception|panic|traceback|cannot|could not|refused|denied|timed? out|abort|unexpected)\b/i,ca=/\b(warn|warning|skipped|skipping|deprecated|retry|retrying|fallback|truncated|missing)\b/i,la=/\b(ok|success|passed|completed|done|ready|merged|applied|resolved)\b/i,ua=/^(reasoning|scanning|analysis started|ci fix started|identified|loaded)\b/i;function da(e){return sa.test(e)?`error`:ca.test(e)?`warn`:la.test(e)?`success`:ua.test(e)?`debug`:`info`}function fa(e){try{let t=new Date(e);return Number.isNaN(t.getTime())?``:`${String(t.getHours()).padStart(2,`0`)}:${String(t.getMinutes()).padStart(2,`0`)}:${String(t.getSeconds()).padStart(2,`0`)}.${String(t.getMilliseconds()).padStart(3,`0`)}`}catch{return`--:--:--.---`}}var pa={info:{icon:`ℹ`,text:`text-fg`,iconColor:`text-sky-400`},warn:{icon:`⚠`,text:`text-amber-300`,iconColor:`text-amber-400`},error:{icon:`✖`,text:`text-red-300`,iconColor:`text-red-400`},debug:{icon:`›`,text:`text-fg-subtle`,iconColor:`text-fg-subtle`},success:{icon:`✓`,text:`text-emerald-300`,iconColor:`text-emerald-400`}};function ma({entries:e,autoScroll:t=!0,variant:n=`panel`,heightClass:r=`min-h-[12rem] max-h-[60vh]`,label:i,badge:a,headerAccessory:o,empty:s=`No output yet.`}){let c=(0,x.useRef)(null);(0,x.useEffect)(()=>{t&&c.current&&(c.current.scrollTop=c.current.scrollHeight)},[e.length,t]);let l=e.map(e=>typeof e==`string`?{ts:new Date().toISOString(),message:e}:e),u=(0,L.jsx)(`div`,{ref:c,className:`font-mono text-[11px] leading-5 ${r} overflow-y-auto bg-canvas`,children:l.length===0?(0,L.jsx)(`div`,{className:`px-4 py-3 text-fg-subtle italic`,children:s}):(0,L.jsx)(`div`,{children:l.map((e,t)=>{let n=e.level??da(e.message),r=pa[n];return(0,L.jsxs)(`div`,{className:`flex items-start gap-2 px-3 py-[2px] hover:bg-fg/[0.02] border-l-2 border-transparent`,style:{borderLeftColor:n===`error`?`rgba(248, 113, 113, 0.5)`:n===`warn`?`rgba(251, 191, 36, 0.4)`:n===`success`?`rgba(52, 211, 153, 0.4)`:`transparent`},children:[(0,L.jsx)(`span`,{className:`text-fg-subtle select-none shrink-0 tabular-nums`,children:fa(e.ts)}),(0,L.jsx)(`span`,{className:`${r.iconColor} shrink-0 select-none`,children:r.icon}),(0,L.jsx)(`span`,{className:`${r.text} whitespace-pre-wrap break-words`,children:e.message})]},t)})})});return n===`embedded`?u:(0,L.jsxs)(`div`,{className:`border border-line rounded-lg overflow-hidden bg-canvas shadow-inner`,children:[(0,L.jsxs)(`div`,{className:`flex items-center justify-between bg-surface-raised/80 border-b border-line px-3 py-1.5`,children:[(0,L.jsxs)(`div`,{className:`flex items-center gap-2 text-[11px] text-fg-subtle font-mono`,children:[(0,L.jsxs)(`span`,{className:`flex gap-1.5`,children:[(0,L.jsx)(`span`,{className:`w-2.5 h-2.5 rounded-full bg-red-500/60`}),(0,L.jsx)(`span`,{className:`w-2.5 h-2.5 rounded-full bg-amber-500/60`}),(0,L.jsx)(`span`,{className:`w-2.5 h-2.5 rounded-full bg-emerald-500/60`})]}),i&&(0,L.jsx)(`span`,{className:`pl-1`,children:i}),a&&(0,L.jsx)(`span`,{className:`ml-1 px-1.5 py-0.5 rounded bg-surface-raised text-[10px] text-fg-muted tracking-normal normal-case`,children:a})]}),o&&(0,L.jsx)(`div`,{className:`flex items-center`,children:o})]}),u]})}function ha(e,t=Date.now()){return e.replace(/\x1b\[[0-9;]*[a-zA-Z]/g,``).split(`
16
+ `),f=n?.trim()?`${d}\n\n--- User instructions ---\n${n.trim()}`:d;Re(`ci-fix`);let m=P||`default`,v=u.iid;if(g){Me(!0),w(null),p([]);try{let{jobId:e}=await R(`/issues/${u.iid}/refine`,{method:`POST`,body:JSON.stringify({ciContext:f,previousChanges:g.changes,repo:P,secondaryRepos:pt,agent:wt})});Wr(m,v,`ci-fix`,{jobId:e,meta:{isRefine:!0,ordinal:A}}),I.current.add(e),An(e,{repoKey:m,issueIid:v,isRefine:!0})}catch(e){w(e.message),Me(!1)}return}let{jobId:y}=await R(`/issues/${u.iid}/ci-fix`,{method:`POST`,body:JSON.stringify({ciContext:f,model:bt,repo:P,secondaryRepos:pt,agent:wt})});p([]),h([]),_(null),w(null),t(`analyzing`),Wr(m,v,`ci-fix`,{jobId:y,meta:{isRefine:!1}}),I.current.add(y),An(y,{repoKey:m,issueIid:v,isRefine:!1})},Mn=async()=>{if(u){Oe(!0),Ae(null);try{let e=await R(`/issues/${u.iid}/resolution-check`,{method:`POST`,body:JSON.stringify({repo:P,secondaryRepos:pt,agent:wt})});Ee(e),e.cost??e.inputTokens}catch(e){Ae(e.message)}finally{Oe(!1)}}},Nn=()=>{ce&&(j({applications:[{repo:P,branchName:ce.branchName,mr:{iid:ce.iid,web_url:ce.web_url,title:ce.title},pipelineUrl:null}]}),t(`monitoring`))},Pn=async()=>{u&&(await R(`/issues/${u.iid}/close`,{method:`POST`,body:JSON.stringify({repo:P})}),r(e=>e.filter(e=>e.iid!==u.iid)),Ln())},Fn=async()=>{u&&(await R(`/issues/${u.iid}?repo=${encodeURIComponent(P)}`,{method:`DELETE`}),r(e=>e.filter(e=>e.iid!==u.iid)),Ln())},In=()=>{F.current?.close(),F.current=null,et.current?.close(),et.current=null,tt.current=null,I.current.clear(),Ye(!1),Me(!1),Pe([]),te(null),$e(Zr),Ze(null),t(`selected`)},Ln=()=>{At.current+=1,Ot.current?.abort(),F.current?.close(),F.current=null,I.current.clear(),d(null),le(null),ue(!1),fe([]),me([]),ge(!1),Ee(null),Ae(null),_(null),k([]),te(null),j(null),p([]),h([]),w(null),ae(null),Me(!1),Pe([]),$e(Zr),Ze(null),t(`idle`)};return{phase:e,setPhase:t,issues:n,issuesLoading:i,issuesError:c,selectedIssue:u,progressSteps:f,analysisLogs:m,analysisResult:g,analysisError:C,applying:ne,applyResult:ie,applyError:M,pipelineRefreshKey:oe,refining:je,analysisMode:Le,analysisPrompt:ze,setAnalysisPrompt:Be,existingMr:ce,existingMrLoading:N,mergedMrs:de,issueEnvironments:pe,issueEnvironmentsLoading:he,openMRs:_e,openMRsLoading:ye,openMRsError:Ce,storedAnalyses:O,currentOrdinal:A,handleRestoreAnalysis:vn,restoreAnalysisByOrdinal:yn,handleDiscardStoredAnalysis:bn,handleClearIssueCache:xn,handleDestroyAllAnalyses:Sn,handleUndoRefine:Cn,formatAnalysisDate:kr,reloadStatus:It,loadIssues:zt,loadOpenMRs:Bt,handleSelectMR:Jt,handleCreateIssue:Yt,handleUpdateIssue:Xt,handleSelectIssue:Qt,handleBeginAnalysis:en,handleFreshReanalyze:tn,handleCancelAnalysis:nn,handleApprove:fn,handleDeployBranch:pn,handlePromoteBranch:mn,handleCommitToBranch:hn,handleReject:_n,handleRefine:On,handleRefinePipelineFailure:jn,handleCheckResolution:Mn,resolutionCheck:Te,resolutionChecking:De,resolutionError:ke,handleViewExistingMr:Nn,handleCloseIssue:Pn,handleDeleteIssue:Fn,handleBackToIssue:In,handleReset:Ln,availableRepos:st,selectedRepo:P,setSelectedRepo:ut,selectedSecondaryRepos:pt,setSelectedSecondaryRepos:ht,testDeployUrlTemplate:dt,availableModels:gt,selectedModel:bt,setSelectedModel:Et,availableAgents:St,selectedAgent:wt,setSelectedAgent:Dt,validating:Je,validationError:Xe,validation:Qe,handleValidate:ln,handleFixValidation:kn,autoPushOnValidatePass:rt,setAutoPushOnValidatePass:ot,refineThread:v,refineQueue:Ne,thinkingLogs:Fe,backendError:T,statusLoaded:vt,issuesLoaded:o,openMRsLoaded:xe,backendRetrying:ee,retryBackend:Lt,remoteActiveIids:Vt,remoteJobId:Ut,ciFixAttempts:Ve,ciFixLoopWarning:Ge,ciFixMaxAttempts:qe.current,dismissCiFixLoopWarning:()=>{Ke(null),He(0),We(null)}}}var si=`setPhase.setAnalysisPrompt.setAutoPushOnValidatePass.setSelectedModel.setSelectedAgent.setSelectedRepo.setSelectedSecondaryRepos.reloadStatus.handleSelectMR.handleCreateIssue.handleUpdateIssue.handleSelectIssue.handleBeginAnalysis.handleFreshReanalyze.handleCancelAnalysis.handleApprove.handleDeployBranch.handlePromoteBranch.handleCommitToBranch.handleReject.handleRefine.handleUndoRefine.handleRefinePipelineFailure.handleCheckResolution.handleRestoreAnalysis.restoreAnalysisByOrdinal.handleDiscardStoredAnalysis.handleClearIssueCache.handleDestroyAllAnalyses.handleViewExistingMr.handleCloseIssue.handleDeleteIssue.handleBackToIssue.handleReset.handleValidate.handleFixValidation.loadIssues.loadOpenMRs.formatAnalysisDate.retryBackend.dismissCiFixLoopWarning`.split(`.`),ci=(0,x.createContext)(null),li=(0,x.createContext)(null),ui=(0,x.createContext)(null),di=new Set(si);function fi(e){let t={},n={};for(let[r,i]of Object.entries(e))di.has(r)?n[r]=i:t[r]=i;return{state:t,actions:n}}function pi({children:e}){let{state:t,actions:n}=fi(oi()),[r,i]=(0,x.useState)(`issue`),[a,o]=(0,x.useState)(!1),[s,c]=(0,x.useState)(!1);return(0,L.jsx)(ci.Provider,{value:t,children:(0,L.jsx)(li.Provider,{value:n,children:(0,L.jsx)(ui.Provider,{value:{activeDetailTab:r,setActiveDetailTab:i,mrMerged:a,setMrMerged:o,taskModalOpen:s,setTaskModalOpen:c},children:e})})})}function mi(e,t){let n=(0,x.useContext)(e);if(!n)throw Error(`${t} must be used inside <ForgeProvider>`);return n}var V=()=>mi(ci,`useForgeState`),H=()=>mi(li,`useForgeActions`),hi=()=>mi(ui,`useForgeUI`);function gi(){let{availableRepos:e,selectedRepo:t,statusLoaded:n}=V(),{setSelectedRepo:r}=H(),i=e.find(e=>e.id===t)?.label??(n?`No repository`:``);return e.length<2?(0,L.jsx)(`div`,{className:`h-4 truncate text-[12px] leading-4 text-fg-subtle ${n?``:`w-16 animate-pulse rounded bg-surface-hover`}`,children:i},`single`):(0,L.jsxs)(`div`,{className:`relative flex h-4 min-w-0 items-center gap-1 text-[12px] leading-4 text-fg-muted hover:text-fg`,children:[(0,L.jsx)(`span`,{className:`truncate`,children:i}),(0,L.jsx)(B,{name:`chevronDown`,size:12,className:`shrink-0`}),(0,L.jsx)(`select`,{"aria-label":`Repository`,value:t,onChange:e=>r(e.target.value),className:`absolute inset-0 cursor-pointer opacity-0`,children:e.map(e=>(0,L.jsx)(`option`,{value:e.id,children:e.label},e.id))})]},`multi`)}var _i=[`viewer`,`operator`,`approver`,`admin`],vi={viewer:`Viewer`,operator:`Operator`,approver:`Approver`,admin:`Admin`},yi={viewer:`Read issues, merge requests, pipelines and runs.`,operator:`Analyze issues, refine fixes, validate and open merge requests.`,approver:`Everything an operator can do, plus merge and close merge requests.`,admin:`Full access, including connections, repositories, members and the audit log.`},bi=(e,t)=>_i.indexOf(e)>=_i.indexOf(t);function xi(){let[e,t]=(0,x.useState)(!1),{user:n,signOut:r}=Ui(),i=(0,x.useRef)(null);return(0,x.useEffect)(()=>{if(!e)return;let n=e=>{i.current&&!i.current.contains(e.target)&&t(!1)},r=e=>e.key===`Escape`&&t(!1);return document.addEventListener(`mousedown`,n),document.addEventListener(`keydown`,r),()=>{document.removeEventListener(`mousedown`,n),document.removeEventListener(`keydown`,r)}},[e]),(0,L.jsxs)(`div`,{ref:i,className:`relative`,children:[e&&(0,L.jsxs)(`div`,{role:`menu`,className:`absolute bottom-full left-0 right-0 mb-1.5 overflow-hidden rounded-lg bg-surface-raised p-1 shadow-overlay animate-pop-in`,children:[(0,L.jsxs)(`div`,{className:`px-2.5 py-2`,children:[(0,L.jsx)(`div`,{className:`truncate text-sm text-fg`,children:n.email}),(0,L.jsx)(`div`,{className:`text-[11px] text-fg-subtle`,children:vi[n.role]})]}),(0,L.jsx)(`div`,{className:`my-1 h-px bg-line`}),(0,L.jsxs)(`button`,{role:`menuitem`,onClick:()=>{t(!1),r()},className:`flex w-full items-center gap-2 rounded-md px-2.5 py-1.5 text-left text-sm text-fg-muted hover:bg-surface-hover hover:text-fg`,children:[(0,L.jsx)(B,{name:`logout`,size:14}),` Sign out`]})]}),(0,L.jsxs)(`button`,{onClick:()=>t(e=>!e),className:`flex h-10 w-full items-center gap-2.5 rounded-lg px-2 text-sm text-fg-muted transition-colors hover:bg-surface-hover hover:text-fg`,"aria-expanded":e,"aria-haspopup":`menu`,children:[(0,L.jsx)(`span`,{className:`flex size-7 shrink-0 items-center justify-center rounded-full bg-gradient-to-br from-violet-500 to-indigo-600 text-[11px] font-semibold text-on-accent`,children:n.name.slice(0,1).toUpperCase()}),(0,L.jsxs)(`span`,{className:`min-w-0 flex-1 text-left leading-tight`,children:[(0,L.jsx)(`span`,{className:`block truncate text-fg`,children:n.name}),(0,L.jsx)(`span`,{className:`block text-[11px] text-fg-subtle`,children:vi[n.role]})]}),(0,L.jsx)(B,{name:`more`,size:14,className:`text-fg-subtle`})]}),(0,L.jsxs)(`p`,{className:`mt-1 text-center text-[10px] tabular-nums text-fg-subtle opacity-60`,children:[`v`,`1.35.1`]})]})}function Si({count:e,active:t}){let n=!e;return(0,L.jsx)(`span`,{"data-testid":`count-badge`,"aria-hidden":n||void 0,className:Gn(`min-w-6 rounded-md px-1.5 text-center text-[11px] font-medium tabular-nums leading-5`,t?`bg-surface text-fg`:`bg-surface-hover text-fg-subtle`,n&&`invisible`),children:e??0})}function Ci({label:e,icon:t,active:n,onClick:r,badgeCount:i,showBadge:a=!0}){return(0,L.jsxs)(`button`,{type:`button`,onClick:r,"aria-current":n?`page`:void 0,className:Gn(`flex h-8 w-full items-center gap-2.5 rounded-lg px-2.5 text-sm transition-colors`,n?`bg-surface-hover font-medium text-fg`:`text-fg-muted hover:bg-surface-hover/60 hover:text-fg`),children:[(0,L.jsx)(B,{name:t,className:n?`text-accent-fg`:`text-fg-subtle`}),(0,L.jsx)(`span`,{className:`flex-1 text-left`,children:e}),a&&(0,L.jsx)(Si,{count:i,active:n})]})}var wi=(0,x.createContext)(null);function Ti({basePath:e=``,children:t}){let n=ct(),r=at(),i=e.replace(/\/+$/,``),a=(0,x.useCallback)((e,t)=>{n(`${i}${e}`,t)},[n,i]),o=(0,x.useMemo)(()=>{let e=r.pathname;return i&&e.startsWith(i)?e.slice(i.length)||`/`:e},[r.pathname,i]),s=(0,x.useMemo)(()=>({navigate:a,pathname:o,search:r.search,basePath:i}),[a,o,r.search,i]);return(0,L.jsx)(wi.Provider,{value:s,children:t})}function Ei(){let e=(0,x.useContext)(wi);if(!e)throw Error(`useForgeRouter must be used inside <ForgeRouterProvider>`);return e}var Di=[{path:`/issues`,label:`Issues`,icon:`issues`,section:`primary`,adminOnly:!1,preloadMRs:!1,keywords:[]},{path:`/mrs`,label:`Merge requests`,icon:`mergeRequest`,section:`primary`,adminOnly:!1,preloadMRs:!0,keywords:[`mr`,`pull`]},{path:`/pipelines`,label:`Pipelines`,icon:`pipeline`,section:`primary`,adminOnly:!1,preloadMRs:!0,keywords:[`ci`]},{path:`/settings`,label:`Settings`,icon:`settings`,section:`workspace`,adminOnly:!1,preloadMRs:!1,keywords:[`config`,`tokens`,`repos`]},{path:`/members`,label:`Members`,icon:`users`,section:`workspace`,adminOnly:!0,preloadMRs:!1,keywords:[`users`,`roles`,`team`]},{path:`/audit`,label:`Audit log`,icon:`shield`,section:`workspace`,adminOnly:!0,preloadMRs:!1,keywords:[`activity`,`security`]}];function Oi(e,t){return e===t||e.startsWith(`${t}/`)}function ki(e){return Di.find(t=>Oi(e,t.path))}function Ai(){let{can:e}=Ui(),t=e(`admin`);return Di.filter(e=>t||!e.adminOnly)}function U(){let{issues:e,issuesLoaded:t,openMRs:n,openMRsLoading:r}=V(),{loadOpenMRs:i}=H(),{navigate:a,pathname:o}=Ei(),s=Ai(),c={"/issues":t?e.length:void 0,"/mrs":n.length,"/pipelines":n.filter(e=>e.pipeline).length};return s.map(e=>({...e,active:Oi(o,e.path),badgeCount:c[e.path],go:()=>{a(e.path,{replace:!0}),e.preloadMRs&&n.length===0&&!r&&i()}}))}function ji(){let e=U();return(0,L.jsx)(`nav`,{"aria-label":`Primary`,className:`flex flex-col gap-0.5`,children:e.map((t,n)=>(0,L.jsxs)(x.Fragment,{children:[t.section===`workspace`&&e[n-1]?.section!==`workspace`&&(0,L.jsx)(`div`,{className:`px-2.5 pb-1 pt-5 text-xs font-medium text-fg-subtle`,children:`Workspace`}),(0,L.jsx)(Ci,{label:t.label,icon:t.icon,active:t.active,onClick:t.go,badgeCount:t.badgeCount,showBadge:t.section===`primary`})]},t.path))})}function Mi({size:e=28}){return(0,L.jsx)(`span`,{className:`flex shrink-0 items-center justify-center rounded-lg bg-gradient-to-br from-violet-500 to-indigo-600 text-on-accent shadow-sm`,style:{width:e,height:e},"aria-hidden":!0,children:(0,L.jsx)(`svg`,{width:e*.55,height:e*.55,viewBox:`0 0 24 24`,fill:`none`,stroke:`currentColor`,strokeWidth:2.2,strokeLinecap:`round`,strokeLinejoin:`round`,children:(0,L.jsx)(`path`,{d:`M4 20h16M7 16h10l-1.5-5h-7L7 16Zm1.5-5L10 4h4l1.5 7`})})})}function Ni({open:e,onClose:t}){let{setTaskModalOpen:n}=hi(),{preference:r,setPreference:i}=ir(),{can:a}=Ui();return(0,x.useEffect)(()=>{if(!e)return;let n=e=>e.key===`Escape`&&t();return document.addEventListener(`keydown`,n),()=>document.removeEventListener(`keydown`,n)},[e,t]),(0,L.jsxs)(L.Fragment,{children:[e&&(0,L.jsx)(`div`,{"aria-hidden":!0,className:`fixed inset-0 z-40 bg-black/40 lg:hidden`,onClick:t}),(0,L.jsxs)(`aside`,{className:Gn(`w-60 shrink-0 flex-col border-r border-line bg-surface lg:static lg:flex`,e?`fixed inset-y-0 left-0 z-50 flex shadow-overlay animate-[forge-slide-in_280ms_cubic-bezier(0.32,0.72,0,1)] pt-[env(safe-area-inset-top)]`:`hidden`),children:[(0,L.jsxs)(`div`,{className:`flex h-14 items-center gap-2.5 px-4`,children:[(0,L.jsx)(Mi,{}),(0,L.jsxs)(`div`,{className:`min-w-0 leading-tight`,children:[(0,L.jsx)(`div`,{className:`text-sm font-semibold tracking-tight text-fg`,children:`Forge`}),(0,L.jsx)(gi,{})]})]}),(0,L.jsxs)(`div`,{className:`flex flex-1 flex-col gap-4 overflow-y-auto px-3 pb-3 pt-2`,children:[(0,L.jsxs)(z,{variant:`primary`,className:`w-full`,disabled:!a(`operator`),title:a(`operator`)?void 0:`Requires the operator role`,onClick:()=>n(!0),children:[(0,L.jsx)(B,{name:`plus`,size:15}),` New task`]}),(0,L.jsx)(ji,{})]}),(0,L.jsxs)(`div`,{className:`flex flex-col gap-2 border-t border-line p-3 pb-[max(0.75rem,env(safe-area-inset-bottom))]`,children:[(0,L.jsx)(or,{value:r,onChange:i}),(0,L.jsx)(xi,{})]})]})]})}function Pi({title:e,subtitle:t,children:n}){return(0,L.jsx)(`main`,{className:`flex min-h-screen items-center justify-center bg-canvas px-4 py-12`,children:(0,L.jsxs)(`div`,{className:`w-full max-w-sm`,children:[(0,L.jsxs)(`div`,{className:`mb-8 flex flex-col items-center gap-4 text-center`,children:[(0,L.jsx)(Mi,{size:40}),(0,L.jsxs)(`div`,{children:[(0,L.jsx)(`h1`,{className:`text-xl font-semibold tracking-tight text-fg`,children:e}),(0,L.jsx)(`p`,{className:`mt-1 text-sm text-fg-muted`,children:t})]})]}),(0,L.jsx)(`div`,{className:`rounded-2xl border border-line bg-surface-raised p-6 shadow-sm`,children:n})]})})}var Fi=`h-10 w-full rounded-lg border border-line bg-canvas px-3 text-sm text-fg placeholder:text-fg-subtle focus:border-accent-hover focus:outline-none`;function Ii({message:e}){return(0,L.jsx)(`p`,{role:e?`alert`:void 0,className:e?`rounded-lg bg-red-950/50 px-3 py-2 text-sm text-red-300 ring-1 ring-inset ring-red-800/60`:`hidden`,children:e})}function Li({sso:e,onSignedIn:t}){let[n,r]=(0,x.useState)(``),[i,a]=(0,x.useState)(``),[o,s]=(0,x.useState)(null),[c,l]=(0,x.useState)(!1);return(0,L.jsx)(Pi,{title:`Sign in to Forge`,subtitle:`Use your work account to continue.`,children:(0,L.jsxs)(`div`,{className:`flex flex-col gap-5`,children:[e&&(0,L.jsxs)(L.Fragment,{children:[(0,L.jsxs)(z,{variant:`secondary`,className:`h-10 w-full`,onClick:()=>{window.location.assign(Wn.ssoStartUrl(window.location.pathname+window.location.search))},children:[`Continue with `,e.name]}),(0,L.jsxs)(`div`,{className:`flex items-center gap-3 text-xs text-fg-subtle`,children:[(0,L.jsx)(`span`,{className:`h-px flex-1 bg-line`}),`or use a password`,(0,L.jsx)(`span`,{className:`h-px flex-1 bg-line`})]})]}),(0,L.jsxs)(`form`,{onSubmit:async e=>{e.preventDefault(),l(!0),s(null);try{await Wn.login(n,i),t()}catch(e){s(e.message)}finally{l(!1)}},className:`flex flex-col gap-4`,children:[(0,L.jsxs)(`label`,{className:`flex flex-col gap-1.5 text-sm font-medium text-fg`,children:[`Email`,(0,L.jsx)(`input`,{type:`email`,autoComplete:`username`,required:!0,value:n,onChange:e=>r(e.target.value),className:Fi})]}),(0,L.jsxs)(`label`,{className:`flex flex-col gap-1.5 text-sm font-medium text-fg`,children:[`Password`,(0,L.jsx)(`input`,{type:`password`,autoComplete:`current-password`,required:!0,value:i,onChange:e=>a(e.target.value),className:Fi})]}),(0,L.jsx)(Ii,{message:o}),(0,L.jsx)(z,{type:`submit`,variant:`primary`,className:`h-10 w-full`,disabled:c,children:c?`Signing in…`:`Sign in`})]})]})})}function Ri({requiresToken:e,onDone:t}){let[n,r]=(0,x.useState)({name:``,email:``,password:``,setupToken:``}),[i,a]=(0,x.useState)(null),[o,s]=(0,x.useState)(!1),c=e=>t=>r(n=>({...n,[e]:t.target.value}));return(0,L.jsx)(Pi,{title:`Set up Forge`,subtitle:`Create the first admin account for this workspace.`,children:(0,L.jsxs)(`form`,{onSubmit:async r=>{r.preventDefault(),s(!0),a(null);try{await Wn.setup(e?n:{...n,setupToken:void 0}),t()}catch(e){a(e.message)}finally{s(!1)}},className:`flex flex-col gap-4`,children:[(0,L.jsxs)(`label`,{className:`flex flex-col gap-1.5 text-sm font-medium text-fg`,children:[`Your name`,(0,L.jsx)(`input`,{autoComplete:`name`,required:!0,value:n.name,onChange:c(`name`),className:Fi})]}),(0,L.jsxs)(`label`,{className:`flex flex-col gap-1.5 text-sm font-medium text-fg`,children:[`Work email`,(0,L.jsx)(`input`,{type:`email`,autoComplete:`username`,required:!0,value:n.email,onChange:c(`email`),className:Fi})]}),(0,L.jsxs)(`label`,{className:`flex flex-col gap-1.5 text-sm font-medium text-fg`,children:[`Password`,(0,L.jsx)(`input`,{type:`password`,autoComplete:`new-password`,required:!0,minLength:12,value:n.password,onChange:c(`password`),className:Fi}),(0,L.jsx)(`span`,{className:`text-xs font-normal text-fg-subtle`,children:`At least 12 characters.`})]}),e&&(0,L.jsxs)(`div`,{className:`flex flex-col gap-1.5`,children:[(0,L.jsxs)(`label`,{className:`flex flex-col gap-1.5 text-sm font-medium text-fg`,children:[`Setup code`,(0,L.jsx)(`input`,{autoComplete:`off`,required:!0,"aria-describedby":`setup-code-hint`,value:n.setupToken,onChange:c(`setupToken`),className:`h-10 w-full rounded-lg border border-line bg-canvas px-3 text-sm text-fg placeholder:text-fg-subtle focus:border-accent-hover focus:outline-none`})]}),(0,L.jsx)(`span`,{id:`setup-code-hint`,className:`text-xs text-fg-subtle`,children:`Printed in the server log on first start (or your FORGE_TOKEN, if set). It proves you operate this install.`})]}),(0,L.jsx)(Ii,{message:i}),(0,L.jsx)(z,{type:`submit`,variant:`primary`,className:`h-10 w-full`,disabled:o,children:o?`Creating…`:`Create admin account`})]})})}var zi=(0,x.createContext)(null),Bi=700;function Vi(){let e=window.__forgeBootT??0;return Math.round((performance.now()-e)%Bi)}function Hi({children:e}){let[t,n]=(0,x.useState)(null),[r,i]=(0,x.useState)(null),a=(0,x.useRef)(null),o=()=>a.current??=Wn.me().then(e=>{n(e),i(null)}).catch(e=>i(e.message)).finally(()=>{a.current=null});if((0,x.useEffect)(()=>{o();let e=()=>o();return window.addEventListener(Hn,e),()=>window.removeEventListener(Hn,e)},[]),r&&!t)return(0,L.jsxs)(`div`,{role:`alert`,className:`flex min-h-screen flex-col items-center justify-center gap-3 bg-canvas px-4 text-center`,children:[(0,L.jsx)(`p`,{className:`text-sm font-medium text-fg`,children:`Forge couldn't load your session.`}),(0,L.jsx)(`p`,{className:`max-w-sm text-sm text-fg-muted`,children:r}),(0,L.jsx)(`button`,{onClick:o,className:`text-sm text-accent-fg hover:underline`,children:`Try again`})]});if(!t)return(0,L.jsx)(`div`,{className:`flex min-h-screen items-center justify-center bg-canvas`,"aria-busy":`true`,children:(0,L.jsx)(`div`,{className:`size-5 animate-spin rounded-full border-2 border-line-strong border-t-violet-500`,style:{animationDelay:`-${Vi()}ms`}})});if(t.needsSetup)return(0,L.jsx)(Ri,{requiresToken:t.setupRequiresToken??!0,onDone:o});if(!t.user)return(0,L.jsx)(Li,{sso:t.sso,onSignedIn:o});let s=t.user,c={user:s,can:e=>bi(s.role,e),signOut:async()=>{await Wn.logout().catch(()=>void 0),await o()}};return(0,L.jsx)(zi.Provider,{value:c,children:e})}function Ui(){let e=(0,x.useContext)(zi);if(!e)throw Error(`useSession must be used inside <AuthGate>`);return e}var Wi={neutral:`bg-surface-hover/70 text-fg ring-line-strong/60`,accent:`bg-violet-950/60 text-violet-300 ring-violet-800/60`,success:`bg-emerald-950/60 text-emerald-300 ring-emerald-800/60`,warning:`bg-amber-950/60 text-amber-300 ring-amber-800/60`,danger:`bg-red-950/60 text-red-300 ring-red-800/60`,info:`bg-sky-950/60 text-sky-300 ring-sky-800/60`};function Gi({tone:e=`neutral`,dot:t,className:n,children:r}){return(0,L.jsxs)(`span`,{className:Gn(`inline-flex items-center gap-1.5 rounded-md px-1.5 py-0.5 text-[11px] font-medium ring-1 ring-inset whitespace-nowrap`,Wi[e],n),children:[t&&(0,L.jsx)(`span`,{className:`size-1.5 rounded-full bg-current`,"aria-hidden":!0}),r]})}function Ki({icon:e,title:t,description:n,action:r}){return(0,L.jsxs)(`div`,{className:`flex min-h-[40vh] flex-col items-center justify-center gap-3 rounded-xl border border-dashed border-line px-6 py-12 text-center`,children:[(0,L.jsx)(`div`,{className:`flex size-11 items-center justify-center rounded-2xl bg-surface-hover text-fg-muted ring-1 ring-line`,children:(0,L.jsx)(B,{name:e,size:20})}),(0,L.jsxs)(`div`,{children:[(0,L.jsx)(`p`,{className:`text-sm font-medium text-fg`,children:t}),n&&(0,L.jsx)(`p`,{className:`mt-1 max-w-sm text-sm text-fg-muted`,children:n})]}),r]})}function qi({title:e,description:t,actions:n}){return(0,L.jsxs)(`header`,{className:`flex flex-wrap items-end justify-between gap-4`,children:[(0,L.jsxs)(`div`,{className:`min-w-0`,children:[(0,L.jsx)(`h1`,{className:`text-xl font-semibold tracking-tight text-fg`,children:e}),t&&(0,L.jsx)(`p`,{className:`mt-1 text-sm text-fg-muted`,children:t})]}),n&&(0,L.jsx)(`div`,{className:`flex items-center gap-2`,children:n})]})}var Ji=(e,t,n)=>R(t,{method:e,body:n===void 0?void 0:JSON.stringify(n)}),Yi={listMembers:()=>R(`/admin/users`),addMember:e=>Ji(`POST`,`/admin/users`,e),updateMember:(e,t)=>Ji(`PATCH`,`/admin/users/${e}`,t),listAudit:e=>{let t=new URLSearchParams;return e.action&&t.set(`action`,e.action),e.before&&t.set(`before`,String(e.before)),R(`/admin/audit?${t}`)},listTokens:()=>R(`/auth/tokens`),createToken:e=>Ji(`POST`,`/auth/tokens`,{name:e}),revokeToken:e=>Ji(`DELETE`,`/auth/tokens/${e}`)},Xi=new Intl.DateTimeFormat(void 0,{month:`short`,day:`numeric`,hour:`numeric`,minute:`2-digit`}),Zi=e=>e?Xi.format(new Date(e)):`—`,Qi={"auth.setup":`Set up Forge`,"auth.login":`Signed in`,"auth.sso_login":`Signed in with SSO`,"auth.logout":`Signed out`,"user.create":`Added member`,"user.update":`Updated member`,"token.create":`Created API token`,"token.revoke":`Revoked API token`,"PUT /config":`Changed workspace settings`,"DELETE /workspaces":`Deleted local workspace`,"POST /workspaces/purge":`Purged local workspaces`,"POST /issues":`Created issue`,"PUT /issues/:iid":`Edited issue`,"POST /issues/:iid/close":`Closed issue`,"DELETE /issues/:iid":`Deleted issue`,"POST /issues/:iid/analyze":`Started analysis`,"POST /issues/:iid/refine":`Refined fix`,"POST /issues/:iid/ci-fix":`Asked AI to fix CI`,"POST /issues/:iid/resolution-check":`Checked resolution`,"POST /validate":`Ran validation`,"POST /apply":`Opened merge request`,"POST /commit-to-branch":`Committed to branch`,"POST /promote-branch":`Promoted branch`,"POST /deploy-branch":`Deployed branch`,"POST /jobs/:jobId/cancel":`Cancelled job`,"POST /mr/:mrIid/merge":`Merged merge request`,"POST /mr/:mrIid/auto-merge":`Enabled auto-merge`,"POST /mr/:mrIid/cancel-auto-merge":`Cancelled auto-merge`,"POST /mr/:mrIid/close":`Closed merge request`,"POST /mr/:mrIid/reopen":`Reopened merge request`,"POST /mr/:mrIid/rebase":`Rebased merge request`,"POST /mr/:mrIid/draft":`Changed draft status`};function $i(e){return Qi[e]??e}var ea={success:{label:`Success`,tone:`success`},failure:{label:`Failed`,tone:`danger`},denied:{label:`Denied`,tone:`warning`}},ta=[{value:``,label:`All activity`},{value:`auth.login`,label:`Sign-ins`},{value:`auth.sso_login`,label:`SSO sign-ins`},{value:`POST /mr/:mrIid/merge`,label:`Merges`},{value:`POST /apply`,label:`Merge requests opened`},{value:`PUT /config`,label:`Settings changes`},{value:`user.update`,label:`Member changes`}];function na(){let[e,t]=(0,x.useState)(``),[n,r]=(0,x.useState)(null),[i,a]=(0,x.useState)(null),[o,s]=(0,x.useState)(null);return(0,x.useEffect)(()=>{r(null),Yi.listAudit({action:e}).then(e=>{r(e.events),a(e.nextCursor)}).catch(e=>s(e.message))},[e]),(0,L.jsxs)(`div`,{className:`mx-auto flex w-full max-w-6xl flex-col gap-6`,children:[(0,L.jsx)(qi,{title:`Audit log`,description:`Every sign-in and every change made through Forge. Entries can't be edited or deleted.`,actions:(0,L.jsx)(`select`,{"aria-label":`Filter activity`,value:e,onChange:e=>t(e.target.value),className:`h-8 rounded-lg border border-line bg-surface-raised px-2.5 text-xs text-fg focus:border-accent-hover focus:outline-none`,children:ta.map(e=>(0,L.jsx)(`option`,{value:e.value,children:e.label},e.value))})}),o&&(0,L.jsx)(`p`,{role:`alert`,className:`text-sm text-red-400`,children:o}),n&&n.length===0?(0,L.jsx)(Ki,{icon:`inbox`,title:`No activity yet`,description:`Sign-ins and changes will appear here as they happen.`}):(0,L.jsx)(`div`,{className:`overflow-x-auto rounded-xl border border-line bg-surface-raised`,children:(0,L.jsxs)(`table`,{"aria-label":`Audit log`,className:`w-full min-w-[860px] table-fixed text-left text-sm`,children:[(0,L.jsxs)(`colgroup`,{children:[(0,L.jsx)(`col`,{className:`w-36`}),(0,L.jsx)(`col`,{className:`w-52`}),(0,L.jsx)(`col`,{}),(0,L.jsx)(`col`,{className:`w-44`}),(0,L.jsx)(`col`,{className:`w-28`})]}),(0,L.jsx)(`thead`,{className:`border-b border-line text-xs font-medium text-fg-subtle`,children:(0,L.jsxs)(`tr`,{children:[(0,L.jsx)(`th`,{scope:`col`,className:`py-2.5 pl-4 pr-3 font-medium`,children:`When`}),(0,L.jsx)(`th`,{scope:`col`,className:`px-3 py-2.5 font-medium`,children:`Who`}),(0,L.jsx)(`th`,{scope:`col`,className:`px-3 py-2.5 font-medium`,children:`What`}),(0,L.jsx)(`th`,{scope:`col`,className:`px-3 py-2.5 font-medium`,children:`Where`}),(0,L.jsx)(`th`,{scope:`col`,className:`py-2.5 pl-3 pr-4 text-right font-medium`,children:`Result`})]})}),(0,L.jsxs)(`tbody`,{className:`divide-y divide-line`,children:[(n??[]).map(e=>(0,L.jsxs)(`tr`,{children:[(0,L.jsx)(`td`,{className:`py-3 pl-4 pr-3 tabular-nums text-fg-muted`,children:Zi(e.at)}),(0,L.jsx)(`td`,{className:`truncate px-3 py-3 text-fg`,title:e.ip??void 0,children:e.actorLabel}),(0,L.jsxs)(`td`,{className:`px-3 py-3`,children:[(0,L.jsx)(`span`,{className:`text-fg`,children:$i(e.action)}),e.target&&(0,L.jsx)(`span`,{className:`ml-2 font-mono text-xs text-fg-subtle`,children:e.target})]}),(0,L.jsx)(`td`,{className:`truncate px-3 py-3 font-mono text-xs text-fg-subtle`,children:e.repo?.replace(/^[a-z]+:/,``)??`—`}),(0,L.jsx)(`td`,{className:`py-3 pl-3 pr-4 text-right`,children:(0,L.jsx)(Gi,{tone:ea[e.outcome].tone,children:ea[e.outcome].label})})]},e.id)),!n&&Array.from({length:5}).map((e,t)=>(0,L.jsx)(`tr`,{children:(0,L.jsx)(`td`,{colSpan:5,className:`h-[45px] px-4`,children:(0,L.jsx)(`div`,{className:`h-3 w-1/2 animate-pulse rounded bg-surface-hover`})})},t))]})]})}),i&&(0,L.jsx)(`div`,{className:`flex justify-center`,children:(0,L.jsx)(z,{size:`sm`,onClick:async()=>{if(!i)return;let t=await Yi.listAudit({action:e,before:i});r(e=>[...e??[],...t.events]),a(t.nextCursor)},children:`Load older activity`})})]})}var ra=`h-9 w-full rounded-lg border border-line bg-canvas px-3 text-sm text-fg placeholder:text-fg-subtle focus:border-accent-hover focus:outline-none`,ia=`h-8 rounded-lg border border-line bg-surface-raised px-2 text-xs text-fg focus:border-accent-hover focus:outline-none disabled:opacity-50`;function aa({onAdded:e,onCancel:t}){let[n,r]=(0,x.useState)({name:``,email:``,role:`operator`,password:``}),[i,a]=(0,x.useState)(null),[o,s]=(0,x.useState)(!1);return(0,L.jsxs)(`form`,{onSubmit:async t=>{t.preventDefault(),s(!0),a(null);try{e((await Yi.addMember(n)).user)}catch(e){a(e.message)}finally{s(!1)}},"aria-label":`Add member`,className:`grid gap-4 rounded-xl border border-line bg-surface-raised p-5 shadow-sm sm:grid-cols-2`,children:[(0,L.jsxs)(`label`,{className:`flex flex-col gap-1.5 text-sm font-medium text-fg`,children:[`Name`,(0,L.jsx)(`input`,{required:!0,value:n.name,onChange:e=>r({...n,name:e.target.value}),className:ra})]}),(0,L.jsxs)(`label`,{className:`flex flex-col gap-1.5 text-sm font-medium text-fg`,children:[`Email`,(0,L.jsx)(`input`,{type:`email`,required:!0,value:n.email,onChange:e=>r({...n,email:e.target.value}),className:ra})]}),(0,L.jsxs)(`label`,{className:`flex flex-col gap-1.5 text-sm font-medium text-fg`,children:[`Role`,(0,L.jsx)(`select`,{value:n.role,onChange:e=>r({...n,role:e.target.value}),className:ra,children:_i.map(e=>(0,L.jsx)(`option`,{value:e,children:vi[e]},e))}),(0,L.jsx)(`span`,{className:`text-xs font-normal text-fg-subtle`,children:yi[n.role]})]}),(0,L.jsxs)(`label`,{className:`flex flex-col gap-1.5 text-sm font-medium text-fg`,children:[`Temporary password`,(0,L.jsx)(`input`,{type:`password`,required:!0,minLength:12,autoComplete:`new-password`,value:n.password,onChange:e=>r({...n,password:e.target.value}),className:ra}),(0,L.jsx)(`span`,{className:`text-xs font-normal text-fg-subtle`,children:`Share it privately. SSO users can leave Forge's password unused.`})]}),i&&(0,L.jsx)(`p`,{role:`alert`,className:`text-sm text-red-400 sm:col-span-2`,children:i}),(0,L.jsxs)(`div`,{className:`flex justify-end gap-2 sm:col-span-2`,children:[(0,L.jsx)(z,{variant:`ghost`,size:`sm`,onClick:t,children:`Cancel`}),(0,L.jsx)(z,{type:`submit`,variant:`primary`,size:`sm`,disabled:o,children:`Add`})]})]})}function oa(){let{user:e}=Ui(),[t,n]=(0,x.useState)(null),[r,i]=(0,x.useState)(null),[a,o]=(0,x.useState)(!1);(0,x.useEffect)(()=>{Yi.listMembers().then(e=>n(e.users)).catch(e=>i(e.message))},[]);let s=async(e,t)=>{i(null);try{let{user:r}=await Yi.updateMember(e.id,t);n(e=>e?.map(e=>e.id===r.id?r:e)??null)}catch(e){i(e.message)}};return(0,L.jsxs)(`div`,{className:`mx-auto flex w-full max-w-6xl flex-col gap-6`,children:[(0,L.jsx)(qi,{title:`Members`,description:`Who can use this Forge workspace, and what they're allowed to do.`,actions:(0,L.jsxs)(z,{size:`sm`,variant:`primary`,onClick:()=>o(!0),children:[(0,L.jsx)(B,{name:`plus`,size:14}),` Add member`]})}),a&&(0,L.jsx)(aa,{onCancel:()=>o(!1),onAdded:e=>{n(t=>[...t??[],e]),o(!1)}}),r&&(0,L.jsx)(`p`,{role:`alert`,className:`rounded-lg bg-red-950/50 px-3 py-2 text-sm text-red-300 ring-1 ring-inset ring-red-800/60`,children:r}),(0,L.jsx)(`div`,{className:`overflow-x-auto rounded-xl border border-line bg-surface-raised`,children:(0,L.jsxs)(`table`,{"aria-label":`Members`,className:`w-full min-w-[760px] table-fixed text-left text-sm`,children:[(0,L.jsxs)(`colgroup`,{children:[(0,L.jsx)(`col`,{}),(0,L.jsx)(`col`,{className:`w-40`}),(0,L.jsx)(`col`,{className:`w-28`}),(0,L.jsx)(`col`,{className:`w-40`}),(0,L.jsx)(`col`,{className:`w-32`})]}),(0,L.jsx)(`thead`,{className:`border-b border-line text-xs font-medium text-fg-subtle`,children:(0,L.jsxs)(`tr`,{children:[(0,L.jsx)(`th`,{scope:`col`,className:`py-2.5 pl-4 pr-3 font-medium`,children:`Member`}),(0,L.jsx)(`th`,{scope:`col`,className:`px-3 py-2.5 font-medium`,children:`Role`}),(0,L.jsx)(`th`,{scope:`col`,className:`px-3 py-2.5 font-medium`,children:`Sign-in`}),(0,L.jsx)(`th`,{scope:`col`,className:`px-3 py-2.5 font-medium`,children:`Last active`}),(0,L.jsx)(`th`,{scope:`col`,className:`py-2.5 pl-3 pr-4 text-right font-medium`,children:`Access`})]})}),(0,L.jsxs)(`tbody`,{className:`divide-y divide-line`,children:[(t??[]).map(t=>(0,L.jsxs)(`tr`,{className:t.disabled?`opacity-60`:void 0,children:[(0,L.jsx)(`td`,{className:`py-3 pl-4 pr-3`,children:(0,L.jsxs)(`div`,{className:`flex items-center gap-3`,children:[(0,L.jsx)(`span`,{className:`flex size-8 shrink-0 items-center justify-center rounded-full bg-surface-hover text-xs font-semibold text-fg-muted ring-1 ring-line`,children:t.name.slice(0,1).toUpperCase()}),(0,L.jsxs)(`div`,{className:`min-w-0`,children:[(0,L.jsxs)(`div`,{className:`truncate font-medium text-fg`,children:[t.name,` `,t.id===e.id&&(0,L.jsx)(`span`,{className:`font-normal text-fg-subtle`,children:`(you)`})]}),(0,L.jsx)(`div`,{className:`truncate text-xs text-fg-subtle`,children:t.email})]})]})}),(0,L.jsx)(`td`,{className:`px-3 py-3`,children:(0,L.jsx)(`select`,{"aria-label":`Role for ${t.name}`,value:t.role,onChange:e=>s(t,{role:e.target.value}),title:yi[t.role],className:ia,children:_i.map(e=>(0,L.jsx)(`option`,{value:e,children:vi[e]},e))})}),(0,L.jsx)(`td`,{className:`px-3 py-3`,children:(0,L.jsx)(Gi,{tone:t.authProvider===`oidc`?`info`:`neutral`,children:t.authProvider===`oidc`?`SSO`:`Password`})}),(0,L.jsx)(`td`,{className:`px-3 py-3 tabular-nums text-fg-muted`,children:Zi(t.lastLoginAt)}),(0,L.jsx)(`td`,{className:`py-3 pl-3 pr-4 text-right`,children:(0,L.jsx)(z,{size:`sm`,variant:`ghost`,disabled:t.id===e.id,onClick:()=>s(t,{disabled:!t.disabled}),children:t.disabled?`Enable`:`Disable`})})]},t.id)),!t&&Array.from({length:3}).map((e,t)=>(0,L.jsx)(`tr`,{children:(0,L.jsx)(`td`,{colSpan:5,className:`h-[57px] px-4`,children:(0,L.jsx)(`div`,{className:`h-3 w-1/3 animate-pulse rounded bg-surface-hover`})})},t))]})]})})]})}var sa=/\b(error|failed|failure|exception|panic|traceback|cannot|could not|refused|denied|timed? out|abort|unexpected)\b/i,ca=/\b(warn|warning|skipped|skipping|deprecated|retry|retrying|fallback|truncated|missing)\b/i,la=/\b(ok|success|passed|completed|done|ready|merged|applied|resolved)\b/i,ua=/^(reasoning|scanning|analysis started|ci fix started|identified|loaded)\b/i;function da(e){return sa.test(e)?`error`:ca.test(e)?`warn`:la.test(e)?`success`:ua.test(e)?`debug`:`info`}function fa(e){try{let t=new Date(e);return Number.isNaN(t.getTime())?``:`${String(t.getHours()).padStart(2,`0`)}:${String(t.getMinutes()).padStart(2,`0`)}:${String(t.getSeconds()).padStart(2,`0`)}.${String(t.getMilliseconds()).padStart(3,`0`)}`}catch{return`--:--:--.---`}}var pa={info:{icon:`ℹ`,text:`text-fg`,iconColor:`text-sky-400`},warn:{icon:`⚠`,text:`text-amber-300`,iconColor:`text-amber-400`},error:{icon:`✖`,text:`text-red-300`,iconColor:`text-red-400`},debug:{icon:`›`,text:`text-fg-subtle`,iconColor:`text-fg-subtle`},success:{icon:`✓`,text:`text-emerald-300`,iconColor:`text-emerald-400`}};function ma({entries:e,autoScroll:t=!0,variant:n=`panel`,heightClass:r=`min-h-[12rem] max-h-[60vh]`,label:i,badge:a,headerAccessory:o,empty:s=`No output yet.`}){let c=(0,x.useRef)(null);(0,x.useEffect)(()=>{t&&c.current&&(c.current.scrollTop=c.current.scrollHeight)},[e.length,t]);let l=e.map(e=>typeof e==`string`?{ts:new Date().toISOString(),message:e}:e),u=(0,L.jsx)(`div`,{ref:c,className:`font-mono text-[11px] leading-5 ${r} overflow-y-auto bg-canvas`,children:l.length===0?(0,L.jsx)(`div`,{className:`px-4 py-3 text-fg-subtle italic`,children:s}):(0,L.jsx)(`div`,{children:l.map((e,t)=>{let n=e.level??da(e.message),r=pa[n];return(0,L.jsxs)(`div`,{className:`flex items-start gap-2 px-3 py-[2px] hover:bg-fg/[0.02] border-l-2 border-transparent`,style:{borderLeftColor:n===`error`?`rgba(248, 113, 113, 0.5)`:n===`warn`?`rgba(251, 191, 36, 0.4)`:n===`success`?`rgba(52, 211, 153, 0.4)`:`transparent`},children:[(0,L.jsx)(`span`,{className:`text-fg-subtle select-none shrink-0 tabular-nums`,children:fa(e.ts)}),(0,L.jsx)(`span`,{className:`${r.iconColor} shrink-0 select-none`,children:r.icon}),(0,L.jsx)(`span`,{className:`${r.text} whitespace-pre-wrap break-words`,children:e.message})]},t)})})});return n===`embedded`?u:(0,L.jsxs)(`div`,{className:`border border-line rounded-lg overflow-hidden bg-canvas shadow-inner`,children:[(0,L.jsxs)(`div`,{className:`flex items-center justify-between bg-surface-raised/80 border-b border-line px-3 py-1.5`,children:[(0,L.jsxs)(`div`,{className:`flex items-center gap-2 text-[11px] text-fg-subtle font-mono`,children:[(0,L.jsxs)(`span`,{className:`flex gap-1.5`,children:[(0,L.jsx)(`span`,{className:`w-2.5 h-2.5 rounded-full bg-red-500/60`}),(0,L.jsx)(`span`,{className:`w-2.5 h-2.5 rounded-full bg-amber-500/60`}),(0,L.jsx)(`span`,{className:`w-2.5 h-2.5 rounded-full bg-emerald-500/60`})]}),i&&(0,L.jsx)(`span`,{className:`pl-1`,children:i}),a&&(0,L.jsx)(`span`,{className:`ml-1 px-1.5 py-0.5 rounded bg-surface-raised text-[10px] text-fg-muted tracking-normal normal-case`,children:a})]}),o&&(0,L.jsx)(`div`,{className:`flex items-center`,children:o})]}),u]})}function ha(e,t=Date.now()){return e.replace(/\x1b\[[0-9;]*[a-zA-Z]/g,``).split(`
17
17
  `).map((e,n)=>({ts:new Date(t+n).toISOString(),message:e.length?e:` `}))}var ga=[`fetch_issue`,`scan_repo`,`identify_files`,`fetch_files`,`generate_fix`],_a={fetch_issue:`fetch_issue`,scan_repo:`scan_repos`,identify_files:`identify_files`,fetch_files:`fetch_files`,generate_fix:`generate_fix`};function va({steps:e,logs:t,issueTitle:n,mode:r=`analyze`}){let i=new Set(e.map(e=>e.step)),a=e[e.length-1],o=r===`ci-fix`?`ci-fix`:`analyze`;return(0,L.jsxs)(`div`,{className:`flex flex-col gap-6`,children:[(0,L.jsxs)(`div`,{className:`border border-line rounded-lg overflow-hidden bg-canvas`,children:[(0,L.jsxs)(`div`,{className:`flex items-center justify-between bg-surface-raised/80 border-b border-line px-4 py-2`,children:[(0,L.jsxs)(`div`,{className:`flex items-center gap-3 text-xs font-mono text-fg-muted`,children:[(0,L.jsx)(`span`,{className:`text-emerald-400`,children:`●`}),(0,L.jsxs)(`span`,{className:`text-fg-subtle`,children:[`forge / `,o]}),(0,L.jsx)(`span`,{className:`text-fg-subtle`,children:`›`}),(0,L.jsx)(`span`,{className:`text-fg truncate max-w-md`,children:n})]}),r===`ci-fix`&&(0,L.jsx)(`span`,{className:`text-[11px] font-mono px-2 py-0.5 rounded bg-amber-950/60 text-amber-300 border border-amber-900`,children:`CI FAILURE`})]}),(0,L.jsx)(`div`,{className:`px-4 py-4`,children:(0,L.jsx)(`div`,{className:`flex flex-col gap-1`,children:ga.map((t,n)=>{let r=i.has(t),o=a?.step===t,s=e.find(e=>e.step===t)?.message??``,c=_a[t]??t,l,u=`text-fg-subtle`;return r&&!o?(l=(0,L.jsx)(`span`,{className:`text-emerald-400 text-sm`,children:`✓`}),u=`text-fg-muted`):o?(l=(0,L.jsx)(`span`,{className:`w-3.5 h-3.5 rounded-full border-2 border-violet-400 border-t-transparent animate-spin inline-block`}),u=`text-fg`):l=(0,L.jsx)(`span`,{className:`text-fg-subtle text-sm`,children:`○`}),(0,L.jsxs)(`div`,{className:`flex items-center gap-3 font-mono text-xs`,children:[(0,L.jsx)(`span`,{className:`text-fg-subtle tabular-nums select-none w-6 text-right`,children:String(n+1).padStart(2,`0`)}),(0,L.jsx)(`span`,{className:`w-5 flex items-center justify-center shrink-0`,children:l}),(0,L.jsx)(`span`,{className:`${u} w-36 shrink-0`,children:c}),(0,L.jsx)(`span`,{className:`text-fg-subtle truncate`,children:s||`—`})]},t)})})})]}),t.length>0&&(0,L.jsx)(ma,{entries:t,label:`forge ${o}`,badge:`${t.length} entr${t.length===1?`y`:`ies`}`,heightClass:`min-h-[40vh] max-h-[65vh]`})]})}function ya(){let{selectedIssue:e,progressSteps:t,analysisLogs:n,analysisResult:r,analysisMode:i}=V(),{handleCancelAnalysis:a}=H();return e?(0,L.jsxs)(`div`,{className:`flex flex-col gap-6`,children:[(0,L.jsx)(va,{steps:t,logs:n,issueTitle:e.title,mode:i}),(0,L.jsx)(`div`,{className:`flex items-center justify-end`,children:(0,L.jsx)(`button`,{onClick:a,className:`w-full md:w-auto min-h-[50px] md:min-h-0 text-[17px] md:text-xs text-red-400 md:text-fg-muted hover:text-red-300 border border-line-strong hover:border-red-800/60 px-3 py-1.5 rounded-xl md:rounded-lg transition-colors`,title:`Stop the running analysis and return to the issue view. The Claude CLI is killed on the server.`,children:`Cancel analysis`})}),r&&r.changes.length>0&&(0,L.jsxs)(`div`,{className:`flex flex-col gap-3`,children:[(0,L.jsxs)(`p`,{className:`text-xs font-medium text-fg-subtle`,children:[`Proposed changes (`,r.changes.length,` file`,r.changes.length===1?``:`s`,`)`]}),r.changes.map(e=>(0,L.jsxs)(`div`,{className:`flex items-start gap-3 bg-surface-raised border border-line-strong rounded-lg px-4 py-3`,children:[(0,L.jsx)(`span`,{className:`text-xs font-medium mt-0.5 shrink-0 ${e.action===`create`?`text-green-400`:e.action===`delete`?`text-red-400`:`text-yellow-400`}`,children:e.action}),(0,L.jsxs)(`div`,{className:`flex flex-col gap-0.5 min-w-0`,children:[(0,L.jsx)(`span`,{className:`text-sm font-mono text-fg truncate`,children:e.file}),e.summary&&(0,L.jsx)(`span`,{className:`text-xs text-fg-muted`,children:e.summary})]})]},e.file))]})]}):null}var ba={success:`bg-green-400`,failed:`bg-red-400`,running:`bg-yellow-400 animate-pulse`,pending:`bg-yellow-600`,canceled:`bg-fg-subtle`,skipped:`bg-fg-subtle`,created:`bg-fg-subtle`},xa={success:`passed`,failed:`failed`,running:`running`,pending:`pending`,canceled:`canceled`},Sa={open:`No open Forge merge requests.`,merged:`No merged Forge merge requests.`,closed:`No closed Forge merge requests.`};function Ca({mrs:e,loading:t,refreshing:n=!1,error:r,state:i=`open`,onSelect:a,onRefresh:o}){return t?(0,L.jsx)(`div`,{className:`flex flex-col gap-3`,children:Array.from({length:3}).map((e,t)=>(0,L.jsx)(`div`,{className:`h-16 bg-surface-raised rounded-lg animate-pulse`},t))}):r?(0,L.jsxs)(`div`,{className:`flex items-center justify-between bg-red-950/40 border border-red-800 rounded-lg px-4 py-3`,children:[(0,L.jsx)(`p`,{className:`text-sm text-red-300`,children:r}),(0,L.jsx)(`button`,{onClick:o,className:`text-xs text-red-400 hover:text-red-200 transition-colors ml-4 shrink-0`,children:`Retry`})]}):e.length===0?(0,L.jsx)(`div`,{className:`text-center py-12 text-fg-subtle text-sm`,children:Sa[i]}):(0,L.jsx)(`div`,{"aria-busy":n||void 0,className:`flex flex-col gap-2 transition-opacity ${n?`opacity-60`:``}`,children:e.map(e=>{let t=e.pipeline?ba[e.pipeline.status]??`bg-fg-subtle`:`bg-fg-subtle`,n=e.pipeline?xa[e.pipeline.status]??e.pipeline.status:`no pipeline`,r=e.source_branch.match(/^forge\/issue-(\d+)-/),i=r?r[1]:null;return(0,L.jsx)(`button`,{onClick:()=>a(e),className:`w-full text-left bg-surface-raised ring-1 ring-inset ring-line-strong hover:bg-surface-hover active:bg-surface-hover border border-line-strong hover:border-violet-500 rounded-xl md:rounded-lg px-4 py-3 transition-colors group`,children:(0,L.jsxs)(`div`,{className:`flex items-start gap-3`,children:[(0,L.jsx)(`div`,{className:`flex flex-col items-center gap-1 pt-0.5 shrink-0`,children:(0,L.jsx)(`span`,{className:`w-2 h-2 rounded-full ${t}`,title:n})}),(0,L.jsxs)(`div`,{className:`flex-1 min-w-0`,children:[(0,L.jsxs)(`div`,{className:`flex items-center gap-2 mb-0.5`,children:[(0,L.jsxs)(`span`,{className:`text-xs text-fg-subtle font-mono`,children:[`!`,e.iid]}),i&&(0,L.jsxs)(`span`,{className:`text-xs text-fg-subtle font-mono`,children:[`#`,i]}),e.has_conflicts&&(0,L.jsx)(`span`,{className:`text-xs bg-amber-900/50 text-amber-400 border border-amber-700/50 px-1.5 py-0.5 rounded`,children:`conflicts`}),e.pipeline&&(0,L.jsx)(`span`,{className:`text-xs ${e.pipeline.status===`failed`?`text-red-400`:e.pipeline.status===`success`?`text-green-400`:e.pipeline.status===`running`?`text-yellow-400`:`text-fg-subtle`}`,children:n})]}),(0,L.jsx)(`p`,{className:`text-[16px] md:text-sm leading-snug text-fg font-semibold md:font-medium line-clamp-2 md:line-clamp-none md:truncate`,children:e.title}),(0,L.jsx)(`p`,{className:`text-xs font-mono text-fg-subtle mt-0.5 truncate`,children:e.source_branch})]}),(0,L.jsxs)(`div`,{className:`shrink-0 flex flex-col items-end gap-1`,children:[(0,L.jsx)(`span`,{className:`text-xs text-fg-subtle`,children:new Date(e.created_at).toLocaleDateString()}),(0,L.jsx)(`span`,{className:`hidden md:inline text-violet-400 opacity-0 group-hover:opacity-100 transition-opacity text-xs`,children:`View →`}),(0,L.jsx)(B,{name:`chevronRight`,className:`md:hidden mt-2 text-fg-subtle`})]})]})},e.iid)})})}var wa={success:`success`,failed:`danger`,running:`info`,pending:`warning`,created:`neutral`,manual:`accent`,canceled:`neutral`,skipped:`neutral`},Ta=/^refs\/merge-requests\/(\d+)\/head$/,Ea=`h-14`;function Da(e){let t=Ta.exec(e);return t?`!${t[1]}`:e}function Oa(e,t){let n=new URLSearchParams;return e&&n.set(`repo`,e),n.set(`page`,String(t)),`/pipelines?${n}`}function ka({repo:e,reloadKey:t}){let[n,r]=(0,x.useState)(null),[i,a]=(0,x.useState)(null),[o,s]=(0,x.useState)(!1),[c,l]=(0,x.useState)(null);return(0,x.useEffect)(()=>{let t=!1;return l(null),R(Oa(e,1)).then(e=>{t||(r(e.pipelines),a(e.nextPage))}).catch(e=>{t||l(e.message)}),()=>{t=!0}},[e,t]),c&&n===null?(0,L.jsx)(`div`,{className:`rounded-lg border border-red-800 bg-red-950/40 px-4 py-3 text-sm text-red-300`,children:c}):n===null?(0,L.jsx)(`div`,{className:`flex flex-col gap-2`,children:Array.from({length:6}).map((e,t)=>(0,L.jsx)(`div`,{"data-testid":`pipeline-skeleton`,className:`${Ea} animate-pulse rounded-lg bg-surface-raised`},t))}):n.length===0?(0,L.jsx)(Ki,{icon:`pipeline`,title:`No pipelines yet`,description:`CI runs for this repo will show up here.`}):(0,L.jsxs)(`div`,{className:`flex flex-col gap-3`,children:[(0,L.jsx)(`ul`,{className:`flex flex-col gap-2`,children:n.map(e=>{let t=Da(e.ref);return(0,L.jsxs)(`li`,{className:`${Ea} flex items-center gap-4 rounded-lg border border-line-strong/50 bg-surface-raised/50 px-4`,children:[(0,L.jsx)(Gi,{tone:wa[e.status]??`neutral`,dot:!0,className:`w-20 justify-center`,children:e.status}),(0,L.jsxs)(`div`,{className:`flex min-w-0 flex-1 flex-col`,children:[(0,L.jsxs)(`div`,{className:`flex min-w-0 items-center gap-2`,children:[(0,L.jsx)(`span`,{className:`truncate font-mono text-sm text-fg`,title:e.ref,children:t}),e.ref.startsWith(`forge/`)&&(0,L.jsx)(Gi,{tone:`accent`,children:`Forge`})]}),(0,L.jsxs)(`span`,{className:`font-mono text-[11px] text-fg-subtle`,children:[e.sha.slice(0,8),` · `,e.source.replace(/_/g,` `)]})]}),(0,L.jsx)(`span`,{className:`shrink-0 text-xs tabular-nums text-fg-subtle`,children:kr(e.created_at)}),(0,L.jsxs)(`a`,{href:e.web_url,target:`_blank`,rel:`noreferrer`,className:`shrink-0 text-xs tabular-nums text-fg-subtle transition-colors hover:text-fg`,children:[`#`,e.id,` ↗`]})]},e.id)})}),c&&(0,L.jsx)(`p`,{className:`text-xs text-red-400`,children:c}),i!==null&&(0,L.jsx)(z,{size:`sm`,className:`self-center`,onClick:()=>{i!==null&&(s(!0),R(Oa(e,i)).then(e=>{r(t=>{let n=new Set(t?.map(e=>e.id));return[...t??[],...e.pipelines.filter(e=>!n.has(e.id))]}),a(e.nextPage)}).catch(e=>l(e.message)).finally(()=>s(!1)))},disabled:o,children:o?`Loading…`:`Load more`})]})}var Aa=new Map;function ja(e){let t=Aa.get(e);return t||(t=R(`/labels?repo=${encodeURIComponent(e)}`).then(({labels:e})=>e.map(e=>e.name)).catch(()=>(Aa.delete(e),[])),Aa.set(e,t)),t}function Ma(e){let[t,n]=(0,x.useState)([]);return(0,x.useEffect)(()=>{if(!e)return;let t=!0;return ja(e).then(e=>t&&n(e)),()=>{t=!1}},[e]),t}var Na=[`critical`,`high`,`medium`,`low`],Pa={priority:Na,status:[`triage`,`ready`,`in-progress`,`in-review`,`blocked`]},Fa=/^(type|priority|status|area)::(.+)$/;function Ia(e){let t={type:null,priority:null,status:null,areas:[],other:[]};for(let n of e){let e=n.match(Fa);if(!e){t.other.push(n);continue}let[,r,i]=e;r===`area`?t.areas.push(i):t[r]=i}return t}function La({issues:e,query:t,type:n,priority:r}){let i=t.trim().toLowerCase().split(/\s+/).filter(Boolean);return e.filter(e=>{let t=Ia(e.labels);if(n!==`all`&&t.type!==n||r!==`all`&&t.priority!==r)return!1;if(!i.length)return!0;let a=`#${e.iid} ${e.title} ${e.labels.join(` `)}`.toLowerCase();return i.every(t=>t.startsWith(`#`)?`#${e.iid}`===t:a.includes(t))})}function Ra(e,t,n){let r=e.filter(e=>!e.startsWith(`${t}::`));return n?[...r,`${t}::${n}`]:r}function za(e,t){return e.includes(t)?e.filter(e=>e!==t):[...e,t]}function Ba({available:e,scope:t,current:n}){let r=`${t}::`,i=new Set(e.filter(e=>e.startsWith(r)).map(e=>e.slice(r.length)));n&&i.add(n);let a=t===`area`?void 0:Pa[t],o=e=>a?.includes(e)?a.indexOf(e):2**53-1;return[...i].sort((e,t)=>o(e)-o(t)||e.localeCompare(t))}var Va=[{scope:`type`,label:`Type`},{scope:`priority`,label:`Priority`},{scope:`status`,label:`Status`}],Ha=`h-8 w-full rounded-lg border border-line bg-canvas px-2.5 text-xs text-fg focus:border-accent-hover focus:outline-none`,Ua=`inline-flex h-6 items-center gap-1 rounded-md px-2 text-xs transition-colors`;function Wa({labels:e,available:t,onChange:n}){let r=Ia(e),i=Ba({available:t,scope:`area`,current:null});for(let e of r.areas)i.includes(e)||i.push(e);let a=t.filter(t=>!t.includes(`::`)&&!e.includes(t)),[o,s]=(0,x.useState)(``),c=(0,x.useId)(),l=()=>{let t=o.trim();!t||t.includes(`,`)||(e.includes(t)||n([...e,t]),s(``))};return(0,L.jsxs)(`fieldset`,{className:`flex flex-col gap-3`,children:[(0,L.jsx)(`legend`,{className:`sr-only`,children:`Labels`}),(0,L.jsx)(`div`,{className:`grid grid-cols-1 gap-2 sm:grid-cols-3`,children:Va.map(({scope:i,label:a})=>(0,L.jsxs)(`label`,{className:`flex flex-col gap-1 text-xs font-medium text-fg-subtle`,children:[a,(0,L.jsxs)(`select`,{"aria-label":a,value:r[i]??``,onChange:t=>n(Ra(e,i,t.target.value||null)),className:Gn(Ha,`normal-case tracking-normal`),children:[(0,L.jsx)(`option`,{value:``,children:`None`}),Ba({available:t,scope:i,current:r[i]}).map(e=>(0,L.jsx)(`option`,{value:e,children:e},e))]})]},i))}),i.length>0&&(0,L.jsxs)(`div`,{className:`flex flex-col gap-1`,children:[(0,L.jsx)(`span`,{className:`text-xs font-medium text-fg-subtle`,children:`Areas`}),(0,L.jsx)(`div`,{className:`flex flex-wrap gap-1.5`,children:i.map(t=>{let i=r.areas.includes(t);return(0,L.jsx)(`button`,{type:`button`,"aria-pressed":i,onClick:()=>n(za(e,`area::${t}`)),className:Gn(Ua,i?`bg-accent text-on-accent`:`bg-surface-raised text-fg-muted ring-1 ring-inset ring-line hover:text-fg`),children:t},t)})})]}),(0,L.jsxs)(`div`,{className:`flex flex-col gap-1`,children:[(0,L.jsx)(`span`,{className:`text-xs font-medium text-fg-subtle`,children:`Other labels`}),(0,L.jsxs)(`div`,{className:`flex flex-wrap items-center gap-1.5`,children:[r.other.map(t=>(0,L.jsxs)(`span`,{className:Gn(Ua,`bg-surface-raised text-fg-muted ring-1 ring-inset ring-line`),children:[t,(0,L.jsx)(`button`,{type:`button`,"aria-label":`Remove ${t}`,onClick:()=>n(e.filter(e=>e!==t)),className:`text-fg-subtle hover:text-fg`,children:`×`})]},t)),(0,L.jsx)(`input`,{type:`text`,role:`combobox`,"aria-label":`Add label`,"aria-expanded":!1,list:c,placeholder:`Add label…`,value:o,onChange:e=>s(e.target.value),onKeyDown:e=>{e.key===`Enter`&&(e.preventDefault(),l())},onBlur:l,className:Gn(Ha,`h-6 w-36`)}),(0,L.jsx)(`datalist`,{id:c,children:a.map(e=>(0,L.jsx)(`option`,{value:e},e))})]})]})]})}var Ga=`w-full rounded-lg border border-line bg-canvas px-3 py-2 text-sm text-fg placeholder:text-fg-subtle focus:border-accent-hover focus:outline-none`;function Ka({onCreate:e,availableLabels:t,onCancel:n}){let[r,i]=(0,x.useState)(``),[a,o]=(0,x.useState)(``),[s,c]=(0,x.useState)([]),[l,u]=(0,x.useState)(!1),[d,f]=(0,x.useState)(null);return(0,L.jsxs)(`form`,{onSubmit:async t=>{if(t.preventDefault(),r.trim()){u(!0),f(null);try{await e(r.trim(),a.trim(),s),n()}catch(e){f(e.message)}finally{u(!1)}}},"aria-label":`New issue`,className:`flex flex-col gap-3 rounded-xl border border-line bg-surface-raised p-4 shadow-sm`,children:[(0,L.jsx)(`input`,{type:`text`,"aria-label":`Issue title`,placeholder:`Issue title`,value:r,onChange:e=>i(e.target.value),className:Ga}),(0,L.jsx)(`textarea`,{"aria-label":`Description`,placeholder:`Description (optional)`,value:a,onChange:e=>o(e.target.value),rows:3,className:`${Ga} resize-none`}),(0,L.jsx)(Wa,{labels:s,available:t,onChange:c}),d&&(0,L.jsx)(`p`,{className:`text-xs text-red-400`,children:d}),(0,L.jsxs)(`div`,{className:`flex justify-end gap-2`,children:[(0,L.jsx)(z,{variant:`ghost`,size:`sm`,onClick:n,children:`Cancel`}),(0,L.jsx)(z,{type:`submit`,variant:`primary`,size:`sm`,disabled:l||!r.trim(),children:l?`Creating…`:`Create issue`})]})]})}var qa={analyze:`analyzing`,refine:`refining`,"ci-fix":`fixing CI`,validate:`validating`};function Ja({types:e}){if(!e.length)return null;let t=e.find(e=>e===`analyze`)??e.find(e=>e===`ci-fix`)??e.find(e=>e===`refine`)??e[0];return(0,L.jsxs)(`span`,{className:`flex items-center gap-1.5 text-[11px] text-violet-300 bg-violet-950/50 border border-violet-800/60 px-2 py-0.5 rounded-full shrink-0`,children:[(0,L.jsx)(`span`,{className:`w-2 h-2 rounded-full border border-violet-300 border-t-transparent animate-spin inline-block shrink-0`}),qa[t],e.length>1&&(0,L.jsxs)(`span`,{className:`text-violet-400/70`,children:[`+`,e.length-1]})]})}var Ya=new Set([`running`,`pending`,`created`,`preparing`,`waiting_for_resource`,`scheduled`]);function Xa({pipeline:e}){if(!e)return null;let t=Ya.has(e.status),n=e.status===`failed`;if(!t&&!n)return null;let r=t?`text-amber-300 bg-amber-950/40 border-amber-800/60 hover:bg-amber-900/50`:`text-red-300 bg-red-950/40 border-red-800/60 hover:bg-red-900/50`,i=t?`pipeline running`:`pipeline failed`;return(0,L.jsxs)(`a`,{href:e.web_url,target:`_blank`,rel:`noreferrer`,onClick:e=>e.stopPropagation(),className:`flex items-center gap-1.5 text-[11px] px-2 py-0.5 rounded-full shrink-0 border transition-colors ${r}`,title:`CI pipeline is ${e.status}`,children:[t?(0,L.jsx)(`span`,{className:`w-2 h-2 rounded-full border border-amber-300 border-t-transparent animate-spin inline-block shrink-0`}):(0,L.jsx)(`span`,{"aria-hidden":!0,children:`✗`}),i]})}function Za({issue:e,activeJobs:t=[],pipeline:n,openMr:r}){return(0,L.jsxs)(L.Fragment,{children:[(0,L.jsx)(Ja,{types:t}),(0,L.jsx)(Xa,{pipeline:n}),!e.likelyFixedByMr&&r&&(0,L.jsxs)(`a`,{href:r.web_url,target:`_blank`,rel:`noreferrer`,onClick:e=>e.stopPropagation(),className:`text-[11px] text-sky-300 bg-sky-950/50 border border-sky-800/60 px-2 py-0.5 rounded-full shrink-0 hover:bg-sky-900/60 transition-colors`,title:`MR !${r.iid} is open — not yet merged.`,children:[`MR open !`,r.iid]}),e.likelyFixedByMr&&(0,L.jsxs)(`a`,{href:e.likelyFixedByMr.webUrl,target:`_blank`,rel:`noreferrer`,onClick:e=>e.stopPropagation(),className:`text-[11px] text-emerald-300 bg-emerald-950/50 border border-emerald-800/60 px-2 py-0.5 rounded-full shrink-0 hover:bg-emerald-900/60 transition-colors`,title:`Merged via !${e.likelyFixedByMr.iid}. Verify the fix and close the issue on GitLab if resolved.`,children:[`✓ merged !`,e.likelyFixedByMr.iid]}),e.forgeBranch&&e.branchAhead===0&&(0,L.jsx)(`span`,{className:`text-[11px] text-amber-300 bg-amber-950/40 border border-amber-800/60 px-2 py-0.5 rounded-full shrink-0`,title:`Branch has no commits ahead of main — nothing to promote.`,children:`stale branch`})]})}var Qa={bug:`danger`,security:`danger`,feature:`accent`,enhancement:`info`},$a={critical:`danger`,high:`warning`,medium:`info`,low:`neutral`},eo=e=>e.charAt(0).toUpperCase()+e.slice(1),to=new Intl.DateTimeFormat(void 0,{month:`short`,day:`numeric`});function no({issues:e,onSelect:t,renderStatus:n}){return(0,L.jsxs)(L.Fragment,{children:[(0,L.jsx)(`ul`,{"aria-label":`Issues`,className:`divide-y divide-line overflow-hidden rounded-xl border border-line bg-surface-raised md:hidden`,children:e.map(e=>{let r=Ia(e.labels);return(0,L.jsx)(`li`,{children:(0,L.jsxs)(`button`,{type:`button`,onClick:()=>t(e),className:`flex w-full items-center gap-3 px-4 py-3.5 text-left active:bg-surface-hover`,children:[(0,L.jsxs)(`div`,{className:`min-w-0 flex-1`,children:[(0,L.jsx)(`div`,{className:`line-clamp-2 font-medium text-fg`,children:e.title}),(0,L.jsxs)(`div`,{className:`mt-1 flex min-w-0 flex-wrap items-center gap-x-2 gap-y-1 text-xs text-fg-subtle`,children:[(0,L.jsxs)(`span`,{children:[`#`,e.iid]}),r.type&&(0,L.jsx)(Gi,{tone:Qa[r.type]??`neutral`,children:eo(r.type)}),r.priority&&(0,L.jsx)(Gi,{tone:$a[r.priority]??`neutral`,dot:!0,children:eo(r.priority)}),n?.(e)]})]}),(0,L.jsx)(B,{name:`chevronRight`,size:16,className:`shrink-0 text-fg-subtle`})]})},e.iid)})}),(0,L.jsx)(`div`,{className:`hidden overflow-x-auto rounded-xl border border-line bg-surface-raised md:block`,children:(0,L.jsxs)(`table`,{"aria-label":`Issues`,className:`w-full min-w-[880px] table-fixed text-left text-sm`,children:[(0,L.jsxs)(`colgroup`,{children:[(0,L.jsx)(`col`,{}),(0,L.jsx)(`col`,{className:`w-28`}),(0,L.jsx)(`col`,{className:`w-28`}),(0,L.jsx)(`col`,{className:`w-48`}),(0,L.jsx)(`col`,{className:`w-36`}),(0,L.jsx)(`col`,{className:`w-24`})]}),(0,L.jsx)(`thead`,{className:`border-b border-line text-xs font-medium text-fg-subtle`,children:(0,L.jsxs)(`tr`,{children:[(0,L.jsx)(`th`,{scope:`col`,className:`py-2.5 pl-4 pr-3 font-medium`,children:`Issue`}),(0,L.jsx)(`th`,{scope:`col`,className:`px-3 py-2.5 font-medium`,children:`Type`}),(0,L.jsx)(`th`,{scope:`col`,className:`px-3 py-2.5 font-medium`,children:`Priority`}),(0,L.jsx)(`th`,{scope:`col`,className:`px-3 py-2.5 font-medium`,children:`Status`}),(0,L.jsx)(`th`,{scope:`col`,className:`px-3 py-2.5 font-medium`,children:`Author`}),(0,L.jsx)(`th`,{scope:`col`,className:`py-2.5 pl-3 pr-4 text-right font-medium`,children:`Opened`})]})}),(0,L.jsx)(`tbody`,{className:`divide-y divide-line`,children:e.map(e=>{let r=Ia(e.labels);return(0,L.jsxs)(`tr`,{onClick:()=>t(e),className:`group cursor-pointer transition-colors hover:bg-surface-hover`,children:[(0,L.jsxs)(`td`,{className:`py-3 pl-4 pr-3 align-top`,children:[(0,L.jsxs)(`div`,{className:`flex min-w-0 items-baseline gap-2`,children:[(0,L.jsxs)(`span`,{className:`shrink-0 font-mono text-xs text-fg-subtle`,children:[`#`,e.iid]}),(0,L.jsx)(`button`,{type:`button`,onClick:n=>{n.stopPropagation(),t(e)},className:`truncate text-left font-medium text-fg group-hover:text-accent-fg`,children:e.title})]}),(0,L.jsxs)(`div`,{className:`mt-1 flex min-w-0 items-center gap-2 text-xs text-fg-subtle`,children:[e.forgeBranch&&(0,L.jsxs)(`span`,{className:`flex min-w-0 items-center gap-1 font-mono`,title:e.forgeBranch,children:[(0,L.jsx)(B,{name:`branch`,size:12,className:`shrink-0`}),(0,L.jsx)(`span`,{className:`truncate`,children:e.forgeBranch})]}),r.areas.map(e=>(0,L.jsx)(`span`,{className:`shrink-0 rounded bg-surface-hover px-1.5 py-px`,children:e},e))]})]}),(0,L.jsx)(`td`,{className:`px-3 py-3 align-top`,children:r.type?(0,L.jsx)(Gi,{tone:Qa[r.type]??`neutral`,children:eo(r.type)}):(0,L.jsx)(`span`,{className:`text-fg-subtle`,children:`—`})}),(0,L.jsx)(`td`,{className:`px-3 py-3 align-top`,children:r.priority?(0,L.jsx)(Gi,{tone:$a[r.priority]??`neutral`,dot:!0,children:eo(r.priority)}):(0,L.jsx)(`span`,{className:`text-fg-subtle`,children:`—`})}),(0,L.jsx)(`td`,{className:`px-3 py-3 align-top`,children:(0,L.jsx)(`div`,{className:`flex flex-wrap gap-1.5`,children:n?.(e)})}),(0,L.jsx)(`td`,{className:`truncate px-3 py-3 align-top text-fg-muted`,children:e.author.name}),(0,L.jsx)(`td`,{className:`whitespace-nowrap py-3 pl-3 pr-4 text-right align-top tabular-nums text-fg-subtle`,children:to.format(new Date(e.created_at))})]},e.iid)})})]})})]})}var ro=`h-8 rounded-lg border border-line bg-surface-raised px-2.5 text-xs text-fg focus:border-accent-hover focus:outline-none`;function io(e,t){let n=new Set;for(let r of e){let e=Ia(r.labels)[t];e&&n.add(e)}let r=[...n];return t===`priority`?r.sort((e,t)=>Na.indexOf(e)-Na.indexOf(t)):r.sort()}function ao({issues:e,loading:t,refreshing:n=!1,error:r,onSelect:i,onCreate:a,availableLabels:o,activeJobsByIid:s,pipelinesByIid:c,openMrByIid:l,creating:u,onCreatingChange:d}){let[f,p]=(0,x.useState)(``),[m,h]=(0,x.useState)(`all`),[g,_]=(0,x.useState)(`all`),v=La({issues:e,query:f,type:m,priority:g}),y=v.length===e.length?`${e.length} ${e.length===1?`issue`:`issues`}`:`${v.length} of ${e.length} issues`;return(0,L.jsxs)(`div`,{className:`flex flex-col gap-4`,children:[u&&(0,L.jsx)(Ka,{onCreate:a,availableLabels:o,onCancel:()=>d(!1)}),(0,L.jsxs)(`div`,{className:`grid grid-cols-2 items-center gap-2 sm:flex sm:flex-wrap`,children:[(0,L.jsxs)(`div`,{className:`relative col-span-2 min-w-[220px] flex-1 sm:max-w-xs`,children:[(0,L.jsx)(B,{name:`search`,size:14,className:`pointer-events-none absolute left-2.5 top-1/2 -translate-y-1/2 text-fg-subtle`}),(0,L.jsx)(`input`,{type:`search`,"aria-label":`Filter issues`,placeholder:`Filter by title, #number or label`,value:f,onChange:e=>p(e.target.value),className:`h-8 w-full rounded-lg border border-line bg-surface-raised pl-8 pr-2.5 text-xs text-fg placeholder:text-fg-subtle focus:border-accent-hover focus:outline-none`})]}),(0,L.jsxs)(`select`,{"aria-label":`Type`,value:m,onChange:e=>h(e.target.value),className:ro,children:[(0,L.jsx)(`option`,{value:`all`,children:`All types`}),io(e,`type`).map(e=>(0,L.jsx)(`option`,{value:e,children:e},e))]}),(0,L.jsxs)(`select`,{"aria-label":`Priority`,value:g,onChange:e=>_(e.target.value),className:ro,children:[(0,L.jsx)(`option`,{value:`all`,children:`All priorities`}),io(e,`priority`).map(e=>(0,L.jsx)(`option`,{value:e,children:e},e))]}),(0,L.jsx)(`span`,{className:`col-span-2 min-w-28 text-xs tabular-nums text-fg-subtle sm:ml-auto`,"aria-live":`polite`,children:t?`Loading…`:y})]}),r&&(0,L.jsx)(`div`,{className:`max-h-48 overflow-y-auto whitespace-pre-wrap break-words rounded-xl border border-red-800/60 bg-red-950/40 px-4 py-3 text-sm leading-snug text-red-300 [overflow-wrap:anywhere]`,children:r}),t?(0,L.jsx)(`div`,{className:`overflow-hidden rounded-xl border border-line bg-surface-raised`,"aria-busy":`true`,children:Array.from({length:6}).map((e,t)=>(0,L.jsxs)(`div`,{className:`flex h-[61px] items-center gap-4 border-b border-line px-4 last:border-0`,children:[(0,L.jsx)(`div`,{className:`h-3 w-8 animate-pulse rounded bg-surface-hover`}),(0,L.jsx)(`div`,{className:`h-3 flex-1 animate-pulse rounded bg-surface-hover`}),(0,L.jsx)(`div`,{className:`h-3 w-16 animate-pulse rounded bg-surface-hover`}),(0,L.jsx)(`div`,{className:`h-3 w-16 animate-pulse rounded bg-surface-hover`})]},t))}):e.length===0?(0,L.jsx)(Ki,{icon:`inbox`,title:`No open issues`,description:`Create an issue and Forge can analyze it, propose a fix and open the merge request.`,action:(0,L.jsxs)(z,{size:`sm`,variant:`primary`,onClick:()=>d(!0),children:[(0,L.jsx)(B,{name:`plus`,size:14}),` New issue`]})}):v.length===0?(0,L.jsx)(Ki,{icon:`search`,title:`No matching issues`,description:`Try a different search or clear the filters.`}):(0,L.jsx)(`div`,{"aria-busy":n||void 0,className:Gn(`transition-opacity`,n&&`opacity-60`),children:(0,L.jsx)(no,{issues:v,onSelect:i,renderStatus:e=>(0,L.jsx)(Za,{issue:e,activeJobs:s[e.iid],pipeline:c[e.iid],openMr:l[e.iid]})})})]})}var oo=new Set([`running`,`pending`,`created`,`preparing`,`waiting_for_resource`,`scheduled`]),so=15e3;function co(e){if(!e)return null;let t=e.match(/^forge\/issue-(\d+)(?:-|$)/);return t?Number(t[1]):null}function W(e){return e===`/mrs`||e.startsWith(`/mrs/`)?`mrs`:e===`/pipelines`||e.startsWith(`/pipelines/`)?`pipelines`:`issues`}var G=[`open`,`merged`,`closed`];function lo(){let{pathname:e}=Ei(),{issues:t,issuesLoading:n,issuesLoaded:r,issuesError:i,openMRs:a,openMRsLoading:o,openMRsLoaded:s,openMRsError:c,selectedRepo:l,availableRepos:u,remoteActiveIids:d}=V(),[f,p]=(0,x.useState)(!1),[m,h]=(0,x.useState)(0),g=qr(),_=(0,x.useMemo)(()=>{let e=l||`default`,n={};for(let r of t){let t=Xr(g,e,r.iid);t.length?n[r.iid]=t:d.has(r.iid)&&!n[r.iid]?.length&&(n[r.iid]=[`analyze`])}return n},[g,t,l,d]),v=(0,x.useMemo)(()=>{let e={};for(let t of a){if(!t.pipeline)continue;let n=co(t.source_branch);n!=null&&(e[n]={status:t.pipeline.status,web_url:t.pipeline.web_url})}return e},[a]),y=(0,x.useMemo)(()=>{let e={};for(let t of a){let n=co(t.source_branch);n!=null&&(e[n]={iid:t.iid,web_url:t.web_url})}return e},[a]),{handleSelectIssue:b,handleCreateIssue:S,handleSelectMR:C,loadIssues:w,loadOpenMRs:T}=H(),E=W(e),ee=Ma(l),D=(0,x.useMemo)(()=>Object.values(v).some(e=>oo.has(e.status)),[v]);(0,x.useEffect)(()=>{if(E!==`issues`||!D)return;let e=setInterval(()=>T(),so);return()=>clearInterval(e)},[E,D,T]);let[O,k]=(0,x.useState)(`open`);(0,x.useEffect)(()=>{E!==`mrs`&&O!==`open`&&(k(`open`),T(`open`))},[E]);let A=e=>{e!==O&&(k(e),T(e))},te=()=>{E===`issues`?w():T(E===`mrs`?O:`open`)},ne=u.find(e=>e.id===l)?.label??l,re=(0,L.jsxs)(z,{size:`sm`,onClick:te,children:[(0,L.jsx)(B,{name:`refresh`,size:14}),` Refresh`]});return(0,L.jsxs)(`div`,{className:`mx-auto flex w-full max-w-6xl flex-col gap-6`,children:[E===`issues`&&(0,L.jsxs)(L.Fragment,{children:[(0,L.jsx)(qi,{title:`Issues`,description:(0,L.jsxs)(`span`,{"aria-hidden":!ne||void 0,className:ne?void 0:`animate-pulse rounded bg-surface-hover text-transparent [box-decoration-break:clone]`,children:[`Open issues in `,ne||`repository`,`. Pick one to have Forge analyze and fix it.`]},ne?`ready`:`loading`),actions:(0,L.jsxs)(L.Fragment,{children:[re,(0,L.jsxs)(z,{size:`sm`,variant:`primary`,onClick:()=>p(!0),children:[(0,L.jsx)(B,{name:`plus`,size:14}),` New issue`]})]})}),(0,L.jsx)(ao,{issues:t,loading:!r||!s,refreshing:n&&r,error:i,onSelect:b,onCreate:S,availableLabels:ee,activeJobsByIid:_,pipelinesByIid:v,openMrByIid:y,creating:f,onCreatingChange:p})]}),E===`mrs`&&(0,L.jsxs)(L.Fragment,{children:[(0,L.jsx)(qi,{title:`Merge requests`,description:`Forge-authored and team merge requests, with their pipelines.`,actions:(0,L.jsxs)(L.Fragment,{children:[(0,L.jsx)(`div`,{role:`tablist`,"aria-label":`Merge request state`,className:`flex rounded-lg border border-line bg-surface p-0.5`,children:G.map(e=>(0,L.jsx)(`button`,{role:`tab`,"aria-selected":O===e,onClick:()=>A(e),className:`h-6 rounded-md px-3 text-xs capitalize transition-colors ${O===e?`bg-surface-raised text-fg shadow-sm ring-1 ring-line`:`text-fg-subtle hover:text-fg`}`,children:e},e))}),re]})}),(0,L.jsx)(Ca,{mrs:a,loading:!s&&!c,refreshing:o&&s,error:c,state:O,onSelect:C,onRefresh:()=>T(O)})]}),E===`pipelines`&&(0,L.jsxs)(L.Fragment,{children:[(0,L.jsx)(qi,{title:`Pipelines`,description:`CI history: main, merge requests and branches.`,actions:(0,L.jsxs)(z,{size:`sm`,onClick:()=>h(e=>e+1),children:[(0,L.jsx)(B,{name:`refresh`,size:14}),` Refresh`]})}),(0,L.jsx)(ka,{repo:l||void 0,reloadKey:m})]})]})}function uo(){let{issueEnvironments:e}=V();return e.length===0?null:(0,L.jsxs)(`div`,{className:`flex flex-col gap-3`,children:[(0,L.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,L.jsx)(`h3`,{className:`text-[15px] font-semibold text-fg`,children:`Environments`}),(0,L.jsx)(`span`,{className:`text-[10px] text-fg-subtle`,children:`Ephemeral review apps`})]}),(0,L.jsx)(`div`,{className:`flex flex-col gap-2`,children:e.map(e=>(0,L.jsxs)(`div`,{className:`flex items-center justify-between gap-3 bg-surface-raised/50 border border-line-strong rounded-lg px-4 py-3`,children:[(0,L.jsxs)(`div`,{className:`flex flex-col gap-0.5 min-w-0`,children:[(0,L.jsxs)(`div`,{className:`flex items-center gap-2 text-xs`,children:[(0,L.jsx)(`span`,{className:`font-medium ${e.state===`available`?`text-emerald-400`:e.state===`stopping`?`text-amber-400`:e.state===`stopped`?`text-fg-subtle`:`text-fg-muted`}`,children:e.state}),(0,L.jsx)(`span`,{className:`text-fg-subtle`,children:`·`}),(0,L.jsx)(`span`,{className:`text-fg-muted font-mono truncate`,title:e.name,children:e.name})]}),(0,L.jsxs)(`span`,{className:`text-[11px] font-mono text-fg-subtle truncate`,title:e.branchName,children:[`⎇ `,e.branchName]})]}),(0,L.jsxs)(`div`,{className:`flex items-center gap-3 shrink-0`,children:[e.external_url?(0,L.jsx)(`a`,{href:e.external_url,target:`_blank`,rel:`noreferrer`,className:`text-sm text-violet-400 hover:underline`,children:`Open preview ↗`}):(0,L.jsx)(`span`,{className:`text-xs text-fg-subtle italic`,children:`no URL yet`}),e.web_url&&(0,L.jsx)(`a`,{href:e.web_url,target:`_blank`,rel:`noreferrer`,className:`text-xs text-fg-subtle hover:text-fg transition-colors`,title:`View environment in GitLab`,children:`details ↗`})]})]},e.id))})]})}function fo({label:e,trigger:t,items:n,triggerClassName:r}){let[i,a]=(0,x.useState)(!1),o=(0,x.useId)(),s=(0,x.useRef)(null),c=(0,x.useRef)(null),l=(0,x.useRef)([]);l.current.length=n.length,(0,x.useEffect)(()=>{if(!i)return;l.current[0]?.focus();let e=e=>{s.current?.contains(e.target)||a(!1)};return document.addEventListener(`pointerdown`,e),()=>document.removeEventListener(`pointerdown`,e)},[i]);let u=()=>{a(!1),c.current?.focus()};return(0,L.jsxs)(`div`,{ref:s,className:`relative`,children:[(0,L.jsx)(`button`,{ref:c,type:`button`,"aria-label":e,"aria-haspopup":`menu`,"aria-expanded":i,"aria-controls":i?o:void 0,onClick:()=>a(e=>!e),className:r,children:t}),i&&(0,L.jsx)(`div`,{id:o,role:`menu`,"aria-label":e,onKeyDown:e=>{let t=l.current.filter(e=>e!==null),n=t.indexOf(document.activeElement),r=n=>{e.preventDefault(),t[(n+t.length)%t.length]?.focus()};e.key===`ArrowDown`?r(n+1):e.key===`ArrowUp`?r(n-1):e.key===`Home`?r(0):e.key===`End`?r(t.length-1):e.key===`Escape`?(e.preventDefault(),u()):e.key===`Tab`&&a(!1)},className:`absolute right-0 top-full mt-2 z-40 w-60 p-1.5 flex flex-col rounded-xl border border-line-strong bg-surface-raised shadow-2xl animate-[forge-fade_120ms_ease-out]`,children:n.map((e,t)=>(0,L.jsxs)(`div`,{className:`contents`,children:[e.separated&&(0,L.jsx)(`div`,{role:`separator`,className:`my-1.5 mx-1 h-px bg-line`}),(0,L.jsxs)(`button`,{ref:e=>{l.current[t]=e},type:`button`,role:`menuitem`,tabIndex:-1,onClick:()=>{a(!1),e.onSelect()},className:Gn(`flex items-center gap-2.5 min-h-11 md:min-h-9 px-2.5 rounded-lg text-[15px] md:text-sm text-left outline-none transition-colors`,`hover:bg-surface-hover focus-visible:bg-surface-hover`,e.tone===`danger`?`text-red-400`:`text-fg`),children:[e.icon,e.label]})]},e.id))})]})}function po(e,t){let n=(0,x.useRef)(t);n.current=t,(0,x.useEffect)(()=>{if(!e)return;let t=document.activeElement instanceof HTMLElement?document.activeElement:null,r=e=>{e.key===`Escape`&&n.current()};window.addEventListener(`keydown`,r);let i=document.body.style.overflow;return document.body.style.overflow=`hidden`,()=>{window.removeEventListener(`keydown`,r),document.body.style.overflow=i,t?.isConnected&&t.focus()}},[e])}function mo({open:e,onClose:t,title:n,description:r,icon:i,footer:a,children:o}){let s=(0,x.useId)(),c=(0,x.useId)();return po(e,t),e?(0,L.jsx)(`div`,{className:`fixed inset-0 z-50 flex items-end md:items-center justify-center bg-black/60 md:p-4 animate-[forge-fade_160ms_ease-out]`,onClick:t,children:(0,L.jsxs)(`section`,{role:`dialog`,"aria-modal":`true`,"aria-labelledby":s,"aria-describedby":r?c:void 0,onClick:e=>e.stopPropagation(),className:`w-full md:max-w-[480px] max-h-[92dvh] flex flex-col bg-surface-raised border-t md:border border-line-strong rounded-t-[20px] md:rounded-2xl shadow-2xl animate-[forge-sheet-up_320ms_cubic-bezier(0.32,0.72,0,1)] md:animate-none`,children:[(0,L.jsx)(`div`,{className:`md:hidden flex justify-center pt-2`,children:(0,L.jsx)(`span`,{"aria-hidden":`true`,className:`w-9 h-[5px] rounded-full bg-line-strong`})}),(0,L.jsxs)(`header`,{className:`flex items-start gap-3.5 px-4 md:px-6 pt-3 md:pt-6`,children:[i&&(0,L.jsx)(`span`,{className:`hidden md:inline-flex shrink-0`,children:i}),(0,L.jsxs)(`div`,{className:`flex flex-col gap-1 min-w-0`,children:[(0,L.jsx)(`h2`,{id:s,className:`text-xl md:text-[17px] font-bold md:font-semibold text-fg`,children:n}),r&&(0,L.jsx)(`p`,{id:c,className:`text-sm md:text-[13px] text-fg-muted truncate`,children:r})]})]}),o&&(0,L.jsx)(`div`,{className:`overflow-y-auto overscroll-contain px-4 md:px-6 py-4 md:py-5 flex flex-col gap-4`,children:o}),(0,L.jsx)(`footer`,{className:Gn(`flex flex-col-reverse md:flex-row md:justify-end gap-2 px-4 md:px-6 pt-3 md:py-4 pb-[max(1.25rem,env(safe-area-inset-bottom))] md:border-t md:border-line`,!o&&`mt-4`),children:a})]})}):null}var ho={close:{title:`Close issue`,confirm:`Close issue`,pending:`Closing…`,issueRow:`The issue`,issueEffect:`closed, can be reopened`},delete:{title:`Delete issue`,confirm:`Delete issue`,pending:`Deleting…`,issueRow:`The issue and its comments`,issueEffect:`deleted`}};function go({action:e,onClose:t,onSwitch:n}){let{selectedIssue:r,selectedRepo:i}=V(),{handleCloseIssue:a,handleDeleteIssue:o}=H(),[s,c]=(0,x.useState)({status:`loading`}),[l,u]=(0,x.useState)(``),[d,f]=(0,x.useState)(!1),[p,m]=(0,x.useState)(null),h=r?.iid,g=e!==null&&h!==void 0;if((0,x.useEffect)(()=>{if(!g)return;let e=!1;return c({status:`loading`}),R(`/issues/${h}/footprint?repo=${encodeURIComponent(i)}`).then(t=>!e&&c({status:`ready`,footprint:t})).catch(()=>!e&&c({status:`unknown`})),()=>{e=!0}},[g,h,i]),(0,x.useEffect)(()=>{u(``),m(null),f(!1)},[e,h]),!g||!r||!e)return null;let _=ho[e],v=e===`delete`,y=s.status===`ready`?s.footprint.openMr:null,b=l.trim()===String(r.iid),S=s.status!==`loading`&&!y&&(!v||b)&&!d,C=async()=>{f(!0),m(null);try{await(v?o():a()),t()}catch(e){m(e.message),f(!1)}},w=e=>s.status===`ready`?String(e(s.footprint)):s.status===`unknown`?`—`:null;return(0,L.jsxs)(mo,{open:!0,onClose:d?()=>{}:t,title:`${_.title} #${r.iid}?`,description:r.title,icon:(0,L.jsx)(`span`,{"aria-hidden":`true`,className:Gn(`inline-flex items-center justify-center size-10 rounded-[10px]`,v?`bg-red-950 text-red-400`:`bg-surface-hover text-fg-muted`),children:(0,L.jsx)(B,{name:v?`trash`:`archive`,size:18})}),footer:(0,L.jsxs)(L.Fragment,{children:[(0,L.jsx)(z,{variant:`secondary`,onClick:t,disabled:d,className:`h-12 md:h-10 px-4 text-[15px] md:text-sm`,children:`Cancel`}),(0,L.jsx)(z,{variant:v?`danger`:`primary`,onClick:C,disabled:!S,className:`h-12 md:h-10 px-4 text-[15px] md:text-sm font-semibold`,children:(0,L.jsxs)(`span`,{className:`grid`,children:[(0,L.jsx)(`span`,{className:Gn(`[grid-area:1/1]`,d&&`invisible`),children:_.confirm}),(0,L.jsx)(`span`,{"aria-hidden":!d,className:Gn(`[grid-area:1/1]`,!d&&`invisible`),children:_.pending})]})})]}),children:[(0,L.jsxs)(`div`,{className:`flex flex-col gap-2`,children:[(0,L.jsx)(`span`,{className:`text-sm md:text-[13px] text-fg`,children:v?`This permanently removes:`:`This will:`}),(0,L.jsxs)(`ul`,{className:`rounded-xl md:rounded-[10px] border border-line bg-surface`,children:[(0,L.jsx)(_o,{label:_.issueRow,value:_.issueEffect}),(0,L.jsx)(_o,{label:`Forge branches`,value:w(e=>e.branches),effect:`deleted`}),(0,L.jsx)(_o,{label:`Review environments`,value:w(e=>e.environments),effect:`stopped`})]})]}),(0,L.jsx)(`div`,{className:`min-h-[76px] flex flex-col justify-center`,children:y?(0,L.jsxs)(`p`,{role:`alert`,className:`rounded-[10px] border border-amber-800/60 bg-amber-950/40 px-3.5 py-3 text-sm md:text-[13px] text-amber-200`,children:[`MR`,` `,(0,L.jsxs)(`a`,{href:y.web_url,target:`_blank`,rel:`noreferrer`,className:`font-mono underline`,children:[`!`,y.iid]}),` `,`is still open. Merge or close it first.`]}):v?(0,L.jsxs)(`label`,{className:`flex flex-col gap-1.5`,children:[(0,L.jsxs)(`span`,{className:`text-sm md:text-[13px] text-fg`,children:[`Type `,(0,L.jsx)(`strong`,{className:`font-mono`,children:r.iid}),` to confirm`]}),(0,L.jsx)(`input`,{autoFocus:!0,value:l,onChange:e=>u(e.target.value),onKeyDown:e=>{e.key===`Enter`&&S&&C()},inputMode:`numeric`,autoComplete:`off`,"aria-label":`Type ${r.iid} to confirm`,className:Gn(`h-12 md:h-10 px-3 rounded-xl md:rounded-lg border bg-canvas font-mono text-fg outline-none`,b?`border-red-500`:`border-line-strong focus:border-accent`)})]}):(0,L.jsx)(`p`,{className:`text-sm md:text-[13px] text-fg-muted`,children:`You can reopen it later.`})}),v&&(0,L.jsxs)(`div`,{className:`flex items-center gap-3 rounded-xl md:rounded-[10px] bg-surface-hover px-3.5 py-3`,children:[(0,L.jsx)(`span`,{className:`flex-1 text-sm md:text-[13px] text-fg`,children:`Just want it off the list? Closing is reversible.`}),(0,L.jsx)(z,{variant:`secondary`,size:`sm`,onClick:()=>n(`close`),disabled:d||!!y,className:`h-9`,children:`Close instead`})]}),p&&(0,L.jsx)(`p`,{role:`alert`,className:`rounded-[10px] border border-red-800 bg-red-950/40 px-3.5 py-3 text-sm md:text-[13px] text-red-300`,children:p})]})}function _o({label:e,value:t,effect:n}){return(0,L.jsxs)(`li`,{className:`flex items-center justify-between gap-3 min-h-11 md:min-h-10 px-3.5 text-sm md:text-[13px] border-t border-line first:border-t-0`,children:[(0,L.jsx)(`span`,{className:`text-fg`,children:e}),(0,L.jsx)(`span`,{className:`text-fg-muted tabular-nums`,children:t===null?(0,L.jsx)(`span`,{"aria-label":`Loading`,className:`inline-block align-middle h-3 w-16 rounded bg-surface-hover animate-pulse`}):n?`${t} ${n}`:t})]})}function vo({onEdit:e}){let{selectedIssue:t}=V(),{can:n}=Ui(),[r,i]=(0,x.useState)(null);if(!t)return null;let a=[...e?[{id:`edit`,label:`Edit title & labels`,icon:(0,L.jsx)(B,{name:`pencil`}),onSelect:e}]:[],{id:`open`,label:`Open in GitLab`,icon:(0,L.jsx)(B,{name:`external`}),onSelect:()=>window.open(t.web_url,`_blank`,`noopener,noreferrer`)},...n(`approver`)?[{id:`delete`,label:`Delete issue…`,icon:(0,L.jsx)(B,{name:`trash`}),tone:`danger`,separated:!0,onSelect:()=>i(`delete`)}]:[]];return(0,L.jsxs)(`div`,{className:`flex items-center gap-2 shrink-0`,children:[n(`operator`)&&(0,L.jsx)(z,{variant:`secondary`,onClick:()=>i(`close`),className:`h-11 md:h-10 px-3.5`,children:`Close issue`}),(0,L.jsx)(fo,{label:`More actions`,trigger:(0,L.jsx)(B,{name:`more`,size:18}),items:a,triggerClassName:`inline-flex items-center justify-center size-11 md:size-10 rounded-lg border border-line-strong text-fg hover:bg-surface-hover aria-expanded:bg-surface-hover transition-colors`}),(0,L.jsx)(go,{action:r,onClose:()=>i(null),onSwitch:i})]})}var yo={success:`bg-green-400`,failed:`bg-red-400`,running:`bg-yellow-400 animate-pulse`,pending:`bg-yellow-600`,canceled:`bg-fg-subtle`,skipped:`bg-fg-subtle`,created:`bg-fg-subtle`},bo={success:`passed`,failed:`failed`,running:`running`,pending:`pending`,canceled:`canceled`,skipped:`skipped`,created:`created`},xo={success:`text-green-400`,failed:`text-red-400`,running:`text-yellow-400`,pending:`text-yellow-500`,canceled:`text-fg-subtle`,skipped:`text-fg-subtle`,created:`text-fg-subtle`};function So({mrs:e}){return e.length===0?null:(0,L.jsxs)(`div`,{className:`flex flex-col gap-2`,children:[(0,L.jsx)(`h3`,{className:`text-[15px] font-semibold text-fg`,children:`Merged changes`}),(0,L.jsx)(`div`,{className:`flex flex-col gap-1`,children:e.map(e=>{let t=e.pipeline?.status,n=t?yo[t]??`bg-fg-subtle`:`bg-fg-subtle`,r=t?bo[t]??t:`no pipeline`,i=t?xo[t]??`text-fg-subtle`:`text-fg-subtle`;return(0,L.jsxs)(`div`,{className:`flex items-center justify-between bg-surface-raised border border-line rounded px-3 py-2`,children:[(0,L.jsxs)(`div`,{className:`flex items-center gap-3 min-w-0`,children:[(0,L.jsx)(`span`,{className:`w-2 h-2 rounded-full shrink-0 ${n}`,title:`post-merge pipeline: ${r}`}),(0,L.jsxs)(`div`,{className:`flex flex-col gap-0.5 min-w-0`,children:[(0,L.jsxs)(`a`,{href:e.web_url,target:`_blank`,rel:`noreferrer`,className:`text-sm text-fg hover:text-fg transition-colors truncate`,children:[`!`,e.iid,` `,e.title]}),(0,L.jsx)(`span`,{className:`text-xs font-mono text-fg-subtle truncate`,children:e.branchName})]})]}),(0,L.jsxs)(`div`,{className:`flex items-center gap-3 shrink-0 ml-3`,children:[e.pipeline?(0,L.jsx)(`a`,{href:e.pipeline.web_url,target:`_blank`,rel:`noreferrer`,className:`text-xs hover:underline ${i}`,title:`View post-merge pipeline in GitLab`,children:r}):(0,L.jsx)(`span`,{className:`text-xs text-fg-subtle italic`,children:`no pipeline`}),(0,L.jsx)(`span`,{className:`text-xs text-fg-subtle`,children:new Date(e.merged_at).toLocaleDateString()})]})]},e.iid)})})]})}function Co(){let{selectedIssue:e,analysisPrompt:t,refining:n,mergedMrs:r}=V(),{setAnalysisPrompt:i,handleBeginAnalysis:a}=H();return e?(0,L.jsxs)(`div`,{className:`flex flex-col gap-6 w-full`,children:[(0,L.jsxs)(`div`,{className:`flex flex-col gap-1`,children:[(0,L.jsxs)(`div`,{className:`flex items-center gap-2 text-xs text-fg-subtle`,children:[(0,L.jsxs)(`span`,{className:`font-mono`,children:[`#`,e.iid]}),e.labels.map(e=>(0,L.jsx)(`span`,{className:`bg-surface-raised text-fg-muted ring-1 ring-inset ring-line px-1.5 py-0.5 rounded`,children:e},e))]}),(0,L.jsxs)(`div`,{className:`flex items-start justify-between gap-4`,children:[(0,L.jsx)(`h2`,{className:`text-lg font-semibold text-fg`,children:e.title}),(0,L.jsx)(vo,{})]}),(0,L.jsxs)(`p`,{className:`text-xs text-fg-subtle`,children:[`by `,e.author.name,` ·`,` `,(0,L.jsx)(`a`,{href:e.web_url,target:`_blank`,rel:`noreferrer`,className:`hover:text-fg transition-colors`,children:`view on GitLab ↗`})]})]}),e.description&&(0,L.jsx)(`div`,{className:`bg-surface-raised border border-line-strong rounded-lg px-4 py-4 text-sm text-fg whitespace-pre-wrap leading-relaxed w-full max-h-[50vh] overflow-y-auto`,children:e.description}),(0,L.jsx)(uo,{}),!n&&(0,L.jsxs)(`div`,{className:`flex flex-col gap-3`,children:[(0,L.jsx)(`textarea`,{value:t,onChange:e=>i(e.target.value),placeholder:`Optional instructions for the analysis…`,rows:2,className:`w-full bg-surface-raised/60 border border-line-strong rounded-lg px-3 py-2 text-sm text-fg placeholder-fg-subtle focus:outline-none focus:border-violet-500 resize-none`}),(0,L.jsx)(`div`,{className:`flex items-center justify-between gap-6 border-t border-line/60 pt-3`,children:(0,L.jsx)(`div`,{className:`flex items-center gap-3`,children:(0,L.jsx)(`button`,{onClick:a,className:`bg-surface-raised ring-1 ring-inset ring-line-strong hover:bg-surface-hover text-fg text-sm font-medium px-4 py-2 rounded-lg transition-colors`,children:`Analyze & Update MR`})})})]}),(0,L.jsx)(So,{mrs:r})]}):null}var wo=(0,x.createContext)(null);function To({children:e,placeholder:t=null,className:n}){let[r,i]=(0,x.useState)({}),[a,o]=(0,x.useState)(!1),[s,c]=(0,x.useState)(!1),l=(0,x.useCallback)((e,t)=>{i(n=>n[e]===!t?n:{...n,[e]:!t})},[]);(0,x.useEffect)(()=>o(!0),[]);let u=a&&Object.values(r).every(e=>!e);return(0,x.useEffect)(()=>{u&&c(!0)},[u]),(0,L.jsxs)(wo.Provider,{value:l,children:[!s&&t,(0,L.jsx)(`div`,{hidden:!s,className:s?n:void 0,children:e})]})}function Eo(e){let t=(0,x.useContext)(wo),n=(0,x.useId)();(0,x.useEffect)(()=>{t?.(n,e)},[t,n,e]),(0,x.useEffect)(()=>()=>t?.(n,!0),[t,n])}function Do({ready:e}){return Eo(e),null}async function Oo(e,t){let n=await e.text().catch(()=>``);try{let e=JSON.parse(n);if(e.error)return e.error}catch{}return n||t}function ko({state:e,label:t,detail:n}){return(0,L.jsxs)(`div`,{className:`flex items-center gap-2 text-xs`,children:[(0,L.jsx)(`span`,{className:`w-4 text-center font-mono ${e===`ok`?`text-emerald-400`:e===`fail`?`text-red-400`:e===`pending`?`text-yellow-400`:`text-fg-subtle`}`,children:e===`ok`?`✓`:e===`fail`?`✗`:e===`pending`?`○`:`–`}),(0,L.jsx)(`span`,{className:`text-fg`,children:t}),n&&(0,L.jsxs)(`span`,{className:`text-fg-subtle`,children:[`— `,n]})]})}function Ao(e){let t=[],n=null;for(let r of e.split(`
18
18
  `))r.startsWith(`@@`)?(n={header:r,lines:[]},t.push(n)):n&&(r.startsWith(`+`)?n.lines.push({type:`add`,content:r.slice(1)}):r.startsWith(`-`)?n.lines.push({type:`remove`,content:r.slice(1)}):r.startsWith(`\\`)||n.lines.push({type:`context`,content:r.slice(1)}));return t}function jo({d:e}){let[t,n]=(0,x.useState)(!0),r=Ao(e.diff),i=e.new_file?`A`:e.deleted_file?`D`:e.renamed_file?`R`:`M`;return(0,L.jsxs)(`div`,{className:`border border-line-strong rounded-lg overflow-hidden`,children:[(0,L.jsxs)(`button`,{onClick:()=>n(e=>!e),className:`w-full flex items-center gap-2 bg-surface-raised px-3 py-2 text-left hover:bg-surface-hover transition-colors`,children:[(0,L.jsx)(`span`,{className:`text-xs font-mono font-bold ${i===`A`?`text-green-400`:i===`D`?`text-red-400`:i===`R`?`text-blue-400`:`text-fg-muted`}`,children:i}),(0,L.jsx)(`span`,{className:`text-xs font-mono text-fg flex-1 truncate`,children:e.new_path!==e.old_path&&e.old_path!==`/dev/null`?`${e.old_path} → ${e.new_path}`:e.new_path}),(0,L.jsx)(`span`,{className:`text-fg-subtle text-xs`,children:t?`▲`:`▼`})]}),t&&(0,L.jsx)(`div`,{className:`overflow-x-auto`,children:r.length===0?(0,L.jsx)(`p`,{className:`text-xs text-fg-subtle px-3 py-2`,children:`Binary or empty diff`}):r.map((e,t)=>(0,L.jsxs)(`div`,{children:[(0,L.jsx)(`div`,{className:`bg-surface-raised px-3 py-0.5 text-xs font-mono text-fg-subtle`,children:e.header}),e.lines.map((e,t)=>(0,L.jsxs)(`div`,{className:`flex font-mono text-xs leading-5 ${e.type===`add`?`bg-green-950/40 text-green-300`:e.type===`remove`?`bg-red-950/40 text-red-300`:`text-fg-muted`}`,children:[(0,L.jsx)(`span`,{className:`select-none w-4 shrink-0 text-center ${e.type===`add`?`text-green-500`:e.type===`remove`?`text-red-500`:`text-fg-subtle`}`,children:e.type===`add`?`+`:e.type===`remove`?`−`:` `}),(0,L.jsx)(`span`,{className:`pl-2 whitespace-pre`,children:e.content})]},t))]},t))})]})}function Mo({mrIid:e,repo:t,pipelineStatus:n,onMerged:r}){let[i,a]=(0,x.useState)(null),[o,s]=(0,x.useState)(!0),[c,l]=(0,x.useState)(null),[u,d]=(0,x.useState)(!1),[f,p]=(0,x.useState)(null),[m,h]=(0,x.useState)(!1),[g,_]=(0,x.useState)(null),[v,y]=(0,x.useState)(null),{can:b}=Ui(),[S,C]=(0,x.useState)(null),[w,T]=(0,x.useState)(!1),[E,ee]=(0,x.useState)(!1),[D,O]=(0,x.useState)(null),[k,A]=(0,x.useState)(!1),[te,ne]=(0,x.useState)(null),[re,ie]=(0,x.useState)(!1),j=(0,x.useRef)(null),M=(0,x.useRef)(!1),ae=(0,x.useRef)(!1);Eo(!o);let oe=t?`?repo=${encodeURIComponent(t)}`:``,se=(0,x.useCallback)(async()=>{try{let t=await window.fetch(`/api/forge/mr/${e}/detail${oe}`,{headers:Vn});if(!t.ok)throw t.status===404?(M.current=!0,j.current&&=(clearInterval(j.current),null),Error(`MR not found — it may have been deleted or belongs to a different project`)):Error(`MR API ${t.status}`);let n=await t.json();if(M.current)return;if(a(n),l(null),s(!1),n.state===`merged`&&!ae.current){ae.current=!0,j.current&&clearInterval(j.current),j.current=null,r?.();return}!n.rebase_in_progress&&j.current&&(clearInterval(j.current),j.current=null)}catch(e){if(M.current)return;l(e.message),s(!1)}},[e,oe]);(0,x.useEffect)(()=>(M.current=!1,se(),()=>{M.current=!0,j.current&&clearInterval(j.current)}),[se]);let ce=async()=>{d(!0),p(null);try{let t=await window.fetch(`/api/forge/mr/${e}/rebase${oe}`,{method:`POST`,headers:Vn});if(!t.ok)throw Error(await Oo(t,`Rebase API ${t.status}`));a(e=>e&&{...e,rebase_in_progress:!0}),j.current&&clearInterval(j.current),j.current=setInterval(se,3e3)}catch(e){p(e.message)}finally{d(!1)}},le=async()=>{h(!0),_(null);let t=i?.merge_when_pipeline_succeeds;try{let n=t?`/api/forge/mr/${e}/cancel-auto-merge${oe}`:`/api/forge/mr/${e}/auto-merge${oe}`,r=await window.fetch(n,{method:`POST`,headers:Vn});if(!r.ok)throw Error(await Oo(r,`Auto-merge API ${r.status}`));a(e=>e&&{...e,merge_when_pipeline_succeeds:!t})}catch(e){_(e.message)}finally{h(!1)}},N=async(t,n,r)=>{y(t),C(null);try{let t=await window.fetch(`/api/forge/mr/${e}/${n}${oe}`,{method:`POST`,headers:{...Vn,...r?{"Content-Type":`application/json`}:{}},body:r?JSON.stringify(r):void 0});if(!t.ok)throw Error(await Oo(t,`${n} API ${t.status}`));await se()}catch(e){C(e.message)}finally{y(null)}},ue=async()=>{A(!0),ne(null);try{let t=await window.fetch(`/api/forge/mr/${e}/diffs${oe}`,{headers:Vn});if(!t.ok)throw Error(`Diffs API ${t.status}`);O((await t.json()).diffs??[])}catch(e){ne(e.message)}finally{A(!1)}},de=()=>{!re&&D===null&&ue(),ie(e=>!e)};if(o)return(0,L.jsxs)(`div`,{className:`flex items-center gap-2 text-sm text-fg-subtle py-1`,children:[(0,L.jsx)(`span`,{className:`w-3 h-3 rounded-full border-2 border-fg-subtle border-t-transparent animate-spin inline-block shrink-0`}),`Loading MR status…`]});if(c||!i)return(0,L.jsxs)(`div`,{className:`text-sm text-fg-subtle py-1`,children:[`Could not load MR status`,c?` — ${c}`:``,`.`]});let fe=i.has_conflicts||i.detailed_merge_status===`need_rebase`||i.merge_status===`cannot_be_merged`;return(0,L.jsxs)(`div`,{className:`flex flex-col gap-3`,children:[i.rebase_in_progress&&(0,L.jsxs)(`div`,{className:`flex items-center gap-2 bg-surface-raised border border-line-strong rounded-lg px-4 py-3`,children:[(0,L.jsx)(`span`,{className:`w-3 h-3 rounded-full border-2 border-yellow-400 border-t-transparent animate-spin inline-block shrink-0`}),(0,L.jsx)(`span`,{className:`text-sm text-yellow-300`,children:`Rebase in progress…`})]}),!i.rebase_in_progress&&fe&&(0,L.jsxs)(`div`,{className:`bg-amber-950/40 border border-amber-700 rounded-lg px-4 py-3 flex items-start justify-between gap-4`,children:[(0,L.jsxs)(`div`,{children:[(0,L.jsx)(`p`,{className:`text-sm font-medium text-amber-300`,children:i.has_conflicts?`Merge conflicts detected`:`Rebase required`}),(0,L.jsx)(`p`,{className:`text-xs text-amber-400/70 mt-0.5`,children:i.has_conflicts?`This branch has conflicts with the target branch.`:`The target branch has moved ahead of this MR branch.`}),f&&(0,L.jsx)(`p`,{className:`text-xs text-red-400 mt-1`,children:f})]}),(0,L.jsxs)(`div`,{className:`flex items-center gap-2 shrink-0`,children:[b(`operator`)&&(0,L.jsx)(`button`,{onClick:ce,disabled:u,className:`bg-amber-600 hover:bg-amber-500 disabled:opacity-50 text-on-accent text-xs font-medium px-3 py-1.5 rounded-lg transition-colors`,children:u?`Rebasing…`:`Rebase`}),i.has_conflicts&&(0,L.jsx)(`a`,{href:`${i.web_url}/conflicts`,target:`_blank`,rel:`noreferrer`,className:`text-xs text-amber-400 hover:text-amber-200 transition-colors`,children:`Resolve in GitLab ↗`})]})]}),i.state===`opened`&&!i.rebase_in_progress&&(()=>{let e=n===`failed`||n===`canceled`,t=n===`success`?`ok`:e?`fail`:n===`running`||n===`pending`||n===`created`?`pending`:`neutral`,r=i.draft?`Marked as draft`:i.has_conflicts?`Has merge conflicts`:fe?`Needs rebase`:e?`CI pipeline failed`:null,a=!r;return(0,L.jsxs)(`div`,{className:`bg-surface-raised/50 border border-line-strong rounded-lg overflow-hidden`,children:[(0,L.jsxs)(`div`,{className:`px-4 py-2.5 border-b border-line-strong flex items-center justify-between`,children:[(0,L.jsx)(`div`,{className:`flex items-center gap-2`,children:i.draft?(0,L.jsxs)(`span`,{className:`inline-flex items-center gap-1.5 text-xs font-medium bg-surface-raised text-fg-muted ring-1 ring-inset ring-line px-2 py-0.5 rounded`,children:[(0,L.jsx)(`span`,{className:`w-1.5 h-1.5 rounded-full bg-fg-muted`}),`Draft`]}):(0,L.jsxs)(`span`,{className:`inline-flex items-center gap-1.5 text-xs font-medium bg-emerald-900/40 text-emerald-300 px-2 py-0.5 rounded`,children:[(0,L.jsx)(`span`,{className:`w-1.5 h-1.5 rounded-full bg-emerald-400`}),`Open`]})}),b(`approver`)&&(0,L.jsx)(`button`,{onClick:le,disabled:m||!a,className:i.merge_when_pipeline_succeeds?`text-xs text-fg-subtle hover:text-fg disabled:opacity-40 transition-colors`:`text-xs text-violet-400 hover:text-violet-200 disabled:opacity-40 transition-colors`,title:i.merge_when_pipeline_succeeds?`Cancel the pending auto-merge`:`Queue this MR to merge automatically when the pipeline passes`,children:m?`…`:i.merge_when_pipeline_succeeds?`Cancel auto-merge`:`Set auto-merge`})]}),(0,L.jsxs)(`div`,{className:`px-4 py-3 border-b border-line-strong flex flex-col gap-1`,children:[(0,L.jsx)(ko,{state:t,label:`CI pipeline`,detail:n??`no pipeline`}),(0,L.jsx)(ko,{state:i.has_conflicts?`fail`:`ok`,label:i.has_conflicts?`Merge conflicts`:`No merge conflicts`}),(0,L.jsx)(ko,{state:fe?`fail`:`ok`,label:fe?`Rebase required`:`Branch up to date`}),i.draft&&(0,L.jsx)(ko,{state:`fail`,label:`Marked as draft`,detail:`mark ready to enable merge`}),i.merge_when_pipeline_succeeds&&(0,L.jsx)(ko,{state:`pending`,label:`Auto-merge queued`,detail:`will merge when pipeline passes`}),g&&(0,L.jsx)(`p`,{className:`text-xs text-red-400 pt-1`,children:g})]}),(0,L.jsxs)(`div`,{className:`px-4 py-3 bg-surface-raised/40 flex items-center justify-between gap-3`,children:[(0,L.jsxs)(`div`,{className:`flex items-center gap-3 text-xs`,children:[b(`operator`)&&(0,L.jsx)(`button`,{onClick:()=>N(`draft`,`draft`,{draft:!i.draft}),disabled:v!==null,className:`text-fg-subtle hover:text-fg disabled:opacity-50 transition-colors`,title:i.draft?`Mark this MR as ready for review`:`Mark this MR as a draft`,children:v===`draft`?`…`:i.draft?`Mark ready`:`Mark as draft`}),b(`operator`)&&b(`approver`)&&(0,L.jsx)(`span`,{className:`text-fg-subtle`,children:`•`}),b(`approver`)&&(E?(0,L.jsxs)(`span`,{className:`inline-flex items-center gap-2`,children:[(0,L.jsx)(`span`,{className:`text-red-300`,children:`Close MR?`}),(0,L.jsx)(`button`,{onClick:()=>{ee(!1),N(`close`,`close`)},disabled:v!==null,className:`font-medium text-red-300 hover:text-red-200 disabled:opacity-50 transition-colors`,children:v===`close`?`Closing…`:`Confirm`}),(0,L.jsx)(`button`,{onClick:()=>ee(!1),className:`text-fg-subtle hover:text-fg transition-colors`,children:`Cancel`})]}):(0,L.jsx)(`button`,{onClick:()=>{C(null),T(!1),ee(!0)},disabled:v!==null,className:`text-fg-subtle hover:text-red-300 disabled:opacity-50 transition-colors`,children:`Close MR`}))]}),(0,L.jsx)(`div`,{className:`flex items-center gap-3`,children:a?b(`approver`)?w?(0,L.jsxs)(`span`,{className:`inline-flex items-center gap-2`,children:[(0,L.jsx)(`button`,{onClick:()=>T(!1),className:`text-xs text-fg-muted hover:text-fg transition-colors`,children:`Cancel`}),(0,L.jsx)(`button`,{onClick:()=>{T(!1),N(`merge`,`merge`)},disabled:v!==null,className:`text-sm font-medium text-on-accent bg-emerald-600 hover:bg-emerald-500 disabled:opacity-50 rounded-lg px-4 py-1.5 transition-colors`,children:v===`merge`?`Merging…`:`Confirm merge`})]}):(0,L.jsx)(`button`,{onClick:()=>{C(null),ee(!1),T(!0)},disabled:v!==null,className:`text-sm font-medium text-on-accent bg-emerald-600 hover:bg-emerald-500 disabled:opacity-50 rounded-lg px-4 py-1.5 transition-colors`,children:`Merge now`}):(0,L.jsx)(`span`,{className:`text-xs text-fg-subtle`,children:`Ready to merge — an approver merges it`}):(0,L.jsxs)(`span`,{className:`text-xs text-fg-subtle`,children:[`Can't merge — `,r]})})]})]})})(),i.state!==`opened`&&(0,L.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,L.jsxs)(`span`,{className:`inline-flex items-center gap-1.5 text-xs font-medium px-2 py-0.5 rounded ${i.state===`merged`?`bg-violet-900/40 text-violet-300`:`bg-red-900/30 text-red-300`}`,children:[(0,L.jsx)(`span`,{className:`w-1.5 h-1.5 rounded-full ${i.state===`merged`?`bg-violet-400`:`bg-red-400`}`}),i.state===`merged`?`Merged`:`Closed`]}),i.state===`closed`&&b(`approver`)&&(0,L.jsx)(`button`,{onClick:()=>N(`reopen`,`reopen`),disabled:v!==null,className:`text-xs text-violet-400 hover:text-violet-200 disabled:opacity-50 transition-colors`,children:v===`reopen`?`Reopening…`:`Reopen`})]}),S&&(0,L.jsx)(`p`,{className:`text-xs text-red-400`,children:S}),(0,L.jsxs)(`div`,{children:[(0,L.jsxs)(`button`,{onClick:de,className:`text-xs text-fg-subtle hover:text-fg transition-colors flex items-center gap-1`,children:[(0,L.jsx)(`span`,{className:`transition-transform ${re?`rotate-90`:``} inline-block`,children:`›`}),re?`Hide diff`:`Show current diff`,D!==null&&` (${D.length} file${D.length===1?``:`s`})`]}),re&&(0,L.jsxs)(`div`,{className:`mt-3 flex flex-col gap-2`,children:[k&&(0,L.jsxs)(`div`,{className:`flex items-center gap-2 text-sm text-fg-subtle`,children:[(0,L.jsx)(`span`,{className:`w-3 h-3 rounded-full border-2 border-fg-subtle border-t-transparent animate-spin inline-block shrink-0`}),`Loading diff…`]}),te&&(0,L.jsx)(`p`,{className:`text-xs text-red-400`,children:te}),D&&D.length===0&&(0,L.jsx)(`p`,{className:`text-xs text-fg-subtle`,children:`No changes in this MR.`}),D&&D.map((e,t)=>(0,L.jsx)(jo,{d:e},t))]})]})]})}var No={success:`text-green-400`,failed:`text-red-400`,running:`text-yellow-400`,pending:`text-yellow-400`,canceled:`text-fg-subtle`,skipped:`text-fg-subtle`,created:`text-fg-muted`,waiting_for_resource:`text-fg-muted`,preparing:`text-fg-muted`,scheduled:`text-fg-muted`,manual:`text-blue-400`},Po={success:`✓`,failed:`✗`,running:`◎`,pending:`○`,canceled:`⊘`,skipped:`→`,created:`○`,manual:`⊡`},Fo=new Set([`success`,`failed`,`canceled`]);function Io(e){return No[e]??`text-fg-muted`}function Lo(e){return Po[e]??`○`}function Ro(e){let t=new Map;return e.forEach(e=>{t.has(e.stage)||t.set(e.stage,[]),t.get(e.stage).push(e)}),t}function zo({endpoint:e,repo:t,refreshKey:n,onRefinePipelineFailure:r,onStatusChange:i,ciFixAttempts:a,ciFixMaxAttempts:o,ciFixLoopWarning:s,onDismissCiFixWarning:c}){let[l,u]=(0,x.useState)(null),[d,f]=(0,x.useState)([]),[p,m]=(0,x.useState)(!0),[h,g]=(0,x.useState)(null),[_,v]=(0,x.useState)(``),[y,b]=(0,x.useState)({}),[S,C]=(0,x.useState)({}),[w,T]=(0,x.useState)(0);Eo(!p&&w===0);let E=(0,x.useRef)(!1),ee=(0,x.useRef)(null),D=(0,x.useRef)(!1),O=(0,x.useRef)(null),k=(0,x.useRef)(i);(0,x.useEffect)(()=>{k.current=i},[i]);let A=async()=>{let n=new AbortController,r=setTimeout(()=>n.abort(),12e3);try{let i=e.includes(`?`)?`&`:`?`,a=t?`${i}repo=${encodeURIComponent(t)}`:``,o=await window.fetch(`/api/forge/${e}${a}`,{headers:Vn,signal:n.signal});if(clearTimeout(r),!o.ok)throw Error(`Pipeline API ${o.status}`);let s=await o.json();if(E.current)return;u(s.pipeline??null),f(s.jobs??[]),g(null),m(!1);let c=s.pipeline?.status??null;if(c!==O.current&&(O.current=c,k.current?.(c)),s.pipeline&&Fo.has(s.pipeline.status)&&(E.current=!0,ee.current&&clearInterval(ee.current),s.pipeline.status===`failed`&&!D.current)){D.current=!0;let e=(s.jobs??[]).filter(e=>e.status===`failed`);T(e.length),e.forEach(e=>{window.fetch(`/api/forge/jobs/${e.id}/trace`,{headers:Vn}).then(e=>e.json()).then(t=>b(n=>({...n,[e.id]:t.trace??`(empty)`}))).catch(()=>b(t=>({...t,[e.id]:`(failed to load trace)`}))).finally(()=>T(e=>Math.max(0,e-1)))})}}catch(e){if(clearTimeout(r),E.current)return;g(e.name===`AbortError`?`Request timed out`:e.message),m(!1)}};(0,x.useEffect)(()=>(E.current=!1,D.current=!1,O.current=null,u(null),f([]),m(!0),g(null),b({}),T(0),A(),ee.current=setInterval(A,1e4),()=>{E.current=!0,ee.current&&clearInterval(ee.current)}),[e,n]);let te=async e=>{if(y[e]!==void 0){b(t=>{let n={...t};return delete n[e],n});return}C(t=>({...t,[e]:!0}));try{let t=await(await window.fetch(`/api/forge/jobs/${e}/trace`,{headers:Vn})).json();b(n=>({...n,[e]:t.trace??`(empty)`}))}catch{b(t=>({...t,[e]:`(failed to load trace)`}))}finally{C(t=>({...t,[e]:!1}))}},ne=d.filter(e=>e.status===`failed`);if(p)return(0,L.jsxs)(`div`,{className:`flex items-center gap-2 py-4 text-sm text-fg-subtle`,children:[(0,L.jsx)(`span`,{className:`w-3 h-3 rounded-full border-2 border-fg-subtle border-t-transparent animate-spin inline-block shrink-0`}),`Waiting for pipeline...`]});if(h)return(0,L.jsxs)(`div`,{className:`flex items-center justify-between py-3 px-4 bg-surface-raised border border-line-strong rounded-lg`,children:[(0,L.jsxs)(`p`,{className:`text-sm text-fg-muted`,children:[`Could not load pipeline — `,h]}),(0,L.jsx)(`button`,{onClick:()=>{m(!0),g(null),A()},className:`text-xs text-fg-muted hover:text-fg transition-colors ml-4 shrink-0`,children:`Retry`})]});if(!l)return(0,L.jsxs)(`div`,{className:`flex items-center gap-2 py-4 text-sm text-fg-subtle`,children:[(0,L.jsx)(`span`,{className:`w-3 h-3 rounded-full border-2 border-line-strong border-t-transparent animate-spin inline-block shrink-0`}),`Waiting for pipeline to start...`]});let re=Ro(d),ie=l.status===`failed`;return(0,L.jsxs)(`div`,{className:`flex flex-col gap-4`,children:[(0,L.jsxs)(`div`,{className:`flex items-center justify-between`,children:[(0,L.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,L.jsx)(`span`,{className:`font-mono text-sm ${Io(l.status)}`,children:l.status===`running`?(0,L.jsx)(`span`,{className:`w-3 h-3 rounded-full border-2 border-yellow-400 border-t-transparent animate-spin inline-block`}):Lo(l.status)}),(0,L.jsx)(`span`,{className:`text-sm font-medium capitalize ${Io(l.status)}`,children:l.status}),(0,L.jsxs)(`span`,{className:`text-xs text-fg-subtle`,children:[`#`,l.id]})]}),(0,L.jsxs)(`div`,{className:`flex items-center gap-3`,children:[(0,L.jsx)(`button`,{onClick:()=>{m(!0),A()},className:`text-xs text-fg-subtle hover:text-fg-muted transition-colors`,children:`↻`}),(0,L.jsx)(`a`,{href:l.web_url,target:`_blank`,rel:`noreferrer`,className:`text-xs text-fg-muted hover:text-fg transition-colors`,children:`View in GitLab →`})]})]}),ie&&r&&ne.length>0&&(0,L.jsxs)(`div`,{className:`bg-red-950/40 border border-red-800 rounded-lg px-4 py-3 flex flex-col gap-3`,children:[(0,L.jsxs)(`div`,{className:`flex items-start justify-between gap-4`,children:[(0,L.jsxs)(`div`,{children:[(0,L.jsx)(`p`,{className:`text-sm font-medium text-red-300`,children:`Pipeline failed`}),(0,L.jsxs)(`p`,{className:`text-xs text-red-400/70 mt-0.5`,children:[ne.length,` job`,ne.length===1?``:`s`,` `,`failed: `,ne.map(e=>e.name).join(`, `)]})]}),a!=null&&a>0&&(0,L.jsxs)(`span`,{className:`text-xs font-mono shrink-0 ${o&&a>=o?`text-amber-400`:`text-fg-subtle`}`,children:[`attempt `,a,`/`,o??3]})]}),s?(0,L.jsxs)(`div`,{className:`bg-amber-950/40 border border-amber-800/50 rounded-lg px-3 py-2 flex flex-col gap-2`,children:[(0,L.jsx)(`p`,{className:`text-xs text-amber-300 font-medium`,children:`CI fix loop detected`}),(0,L.jsx)(`p`,{className:`text-xs text-amber-400/80 whitespace-pre-wrap`,children:s}),(0,L.jsx)(`div`,{className:`flex items-center gap-2`,children:(0,L.jsx)(`button`,{onClick:c,className:`text-xs text-amber-400 hover:text-amber-300 underline transition-colors`,children:`Dismiss and retry`})})]}):(0,L.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,L.jsx)(`input`,{type:`text`,value:_,onChange:e=>v(e.target.value),onKeyDown:e=>{e.key===`Enter`&&!e.shiftKey&&(e.preventDefault(),r(ne.map(e=>e.id),_,t))},placeholder:`Add context or instructions for the AI fix (optional)`,className:`flex-1 bg-surface-raised border border-red-800/50 rounded-lg px-3 py-1.5 text-xs text-fg placeholder-fg-subtle focus:outline-none focus:border-red-600`}),(0,L.jsx)(`button`,{onClick:()=>r(ne.map(e=>e.id),_,t),className:`shrink-0 bg-violet-600 hover:bg-violet-500 text-on-accent text-xs font-medium px-4 py-2 rounded-lg transition-colors`,children:`Fix with AI`})]})]}),Array.from(re.entries()).map(([e,t])=>(0,L.jsxs)(`div`,{children:[(0,L.jsx)(`p`,{className:`text-xs font-medium text-fg-subtle mb-2`,children:e}),(0,L.jsx)(`div`,{className:`flex flex-col gap-1`,children:t.map(e=>(0,L.jsxs)(`div`,{className:`flex flex-col`,children:[(0,L.jsxs)(`div`,{className:`flex items-center gap-3 bg-surface-raised rounded px-3 py-2`,children:[(0,L.jsx)(`span`,{className:`font-mono text-sm w-4 ${Io(e.status)}`,children:e.status===`running`?(0,L.jsx)(`span`,{className:`w-3 h-3 rounded-full border-2 border-yellow-400 border-t-transparent animate-spin inline-block`}):Lo(e.status)}),(0,L.jsx)(`a`,{href:e.web_url,target:`_blank`,rel:`noreferrer`,className:`text-sm text-fg hover:text-fg transition-colors flex-1`,children:e.name}),e.duration!=null&&(0,L.jsxs)(`span`,{className:`text-xs text-fg-subtle`,children:[Math.round(e.duration),`s`]}),Fo.has(e.status)&&(0,L.jsx)(`button`,{onClick:()=>te(e.id),className:`text-xs text-fg-subtle hover:text-fg transition-colors shrink-0`,children:S[e.id]?(0,L.jsx)(`span`,{className:`w-3 h-3 rounded-full border border-fg-subtle border-t-transparent animate-spin inline-block`}):y[e.id]===void 0?`View logs`:`Hide logs`})]}),y[e.id]!==void 0&&(0,L.jsx)(`div`,{className:`border border-line border-t-0 rounded-b overflow-hidden`,children:(0,L.jsx)(ma,{variant:`embedded`,autoScroll:!1,heightClass:`max-h-64`,entries:ha(y[e.id].split(`
19
19
  `).slice(-200).join(`
package/dist/index.html CHANGED
@@ -22,7 +22,7 @@
22
22
  .boot-spinner { width: 20px; height: 20px; border-radius: 50%; border: 2px solid rgb(127 127 140 / 0.35); border-top-color: #8b5cf6; animation: forge-spin 0.7s linear infinite; }
23
23
  @keyframes forge-spin { to { transform: rotate(360deg); } }
24
24
  </style>
25
- <script type="module" crossorigin src="/assets/index-BmEuPeOG.js"></script>
25
+ <script type="module" crossorigin src="/assets/index-CNvSOdpC.js"></script>
26
26
  <link rel="stylesheet" crossorigin href="/assets/index-CSH1OQYQ.css">
27
27
  </head>
28
28
  <body style="margin:0;">
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ipul-labs/forge",
3
- "version": "1.35.0",
3
+ "version": "1.35.1",
4
4
  "description": "AI-powered code change & deploy control panel — connects Claude, GitLab, and Kubernetes",
5
5
  "readmeFilename": "README.md",
6
6
  "type": "module",
@@ -31,8 +31,7 @@
31
31
  "test:e2e": "playwright test",
32
32
  "build:server": "tsc -p tsconfig.build.json && node scripts/fix-shebang.cjs",
33
33
  "test:package": "playwright test -c playwright.package.config.ts",
34
- "prepack": "npm run build && npm run build:server && node scripts/inject-readme.cjs",
35
- "postpack": "node scripts/inject-readme.cjs --restore"
34
+ "prepack": "npm run build && npm run build:server"
36
35
  },
37
36
  "dependencies": {
38
37
  "@kubernetes/client-node": "^1.0.0",
@@ -99,6 +98,5 @@
99
98
  "type": "git",
100
99
  "url": "git+https://gitlab.com/ipul-labs/forge.git"
101
100
  },
102
- "license": "MIT",
103
- "readme": "# Forge by iPuL\n\nAI-powered code change & deploy control panel. Connect an issue tracker, let Claude propose a fix, review the diff, approve — and Forge creates the branch, commits the changes, opens the MR/PR, and monitors the CI pipeline.\n\nBuilt on: Claude Code CLI · GitLab / GitHub / Azure DevOps · Kubernetes\n\n---\n\n## Why Forge\n\n**The CI refinement loop is the thing.** Every other AI coding tool hands off after the PR is open. Forge keeps going: when your pipeline fails, it grabs the last 150 lines of the failed job log, feeds them back to Claude with the original fix context, generates a targeted patch, and commits it to the branch — automatically. No human has to read the stack trace, translate it into a prompt, and paste it somewhere. The loop closes itself.\n\nEverything else fills in the gaps that matter for real teams:\n\n- **No per-token cost** — Claude runs via the `claude` CLI using your existing Claude subscription, not API credits. Heavy use doesn't trigger a bill. Hosted workspaces can instead save their own Anthropic API key (see [Claude billing](#claude-billing)).\n- **Multi-provider** — GitLab, GitHub, and Azure DevOps are all first-class. Switch providers with a single env var; all normalized data shapes stay the same.\n- **Self-hosted** — your code never leaves your infrastructure. The backend runs wherever you deploy it; AI calls go to Anthropic (or a local gateway via OpenClaw).\n- **Two-pass analysis keeps context focused** — Claude first identifies the 6 most relevant files, then reads only those when proposing the fix. This produces sharper diffs than dumping the whole repo into one prompt.\n\n---\n\n## How it works\n\n1. **Sign in** — with your Forge account or your company SSO\n2. **Pick an issue** — Forge loads open issues from your project. Create, edit, label (`type::` / `priority::` / `status::` / `area::`) and delete tickets right from Forge. Close and delete (in the issue's ⋯ menu) open a dialog that first shows what will be torn down (Forge branches deleted, review environments stopped) and names any open MR that blocks it; deleting also asks you to type the issue number. The preview comes from `GET /issues/:iid/footprint`\n3. **Analyze** — Claude reads the repo file tree, identifies relevant files, and proposes a targeted fix\n4. **Review** — inspect the diff file-by-file; refine with natural-language feedback if needed\n5. **Apply** — Forge creates a branch, commits all changes, and opens a merge/pull request\n6. **Monitor** — watch the CI pipeline run; if it fails, click \"Fix with AI\" to iterate\n\n---\n\n## Getting started\n\n### Prerequisites\n\n- Node.js 22.13+\n- `claude` CLI installed and authenticated (`claude auth login`)\n- Credentials for your SCM provider (see [Configuration](#configuration))\n\n### Install\n\nForge is published on npm as [`@ipul-labs/forge`](https://www.npmjs.com/package/@ipul-labs/forge). The package includes the server, the built dashboard and the `forge` CLI.\n\n```bash\nnpm install -g @ipul-labs/forge\n```\n\n### Configure\n\nPick a folder for this install's settings and run the setup there. It writes `.env` in that folder and your repos and code-host token to `~/.forge/config.json`:\n\n```bash\nmkdir forge-home && cd forge-home\nforge init\n```\n\n### Launch the dashboard\n\nFrom the same folder:\n\n```bash\nforge start # http://localhost:5001 (--port to change)\n```\n\nOpen it and create the first admin account (see [First run](#first-run)). `forge doctor` checks the Claude CLI, your config and the build if anything looks off, and `forge update` installs the latest release.\n\nPrefer not to install globally? `npx @ipul-labs/forge init` and `npx @ipul-labs/forge start` do the same.\n\n### Working on Forge itself\n\nFrom a checkout of the source:\n\n```bash\nnpm install\nnpm run dev # http://localhost:5000 (frontend) with the API on :5001\nnpm run build && npm start # production build, served on :5001\n```\n\n`npm run test:package` packs the repo exactly as npm would, installs the tarball into an empty folder, and runs the end-to-end checks against that install.\n\n### CLI usage\n\nThe `forge` CLI drives the same orchestrator from the terminal. After the global install above it is on your PATH; otherwise prefix commands with `npx @ipul-labs/forge`.\n\n#### Interactive mode\n\nRun `forge` with no arguments to enter an interactive terminal session:\n\n```bash\nforge\n```\n\n```\n forge v1.15.1\n\n──────────────────────────────────────────────────\n ❯ issues --limit 5\n──────────────────────────────────────────────────\n group/repo | agent:claude | v1.15.1\n```\n\nTab-completes command names. Type `use <repo>` to switch repos. Commands that error stay in the session — only `exit`, `quit`, or Ctrl+D leave.\n\n#### End-to-end: issue to production\n\n```bash\nforge ship 42\n```\n\nRuns: analyze → review → validate → apply → watch CI → merge → watch deploy → close, with a `y/N` gate at every state-changing step.\n\n#### Step-by-step commands\n\n```bash\nforge issues # List open issues (--label X, --mine, --limit N)\nforge issue 42 # Show full description\nforge create-issue --title \"fix: broken widget\" # Create a new issue\nforge analyze 42 # Run two-pass analysis, cache result\nforge show # Re-render cached analysis\nforge refine --feedback \"...\" # Iterate with feedback\nforge validate # Run lint/typecheck/test against proposed changes\nforge apply # Push branch + open MR\nforge watch # Poll the MR pipeline\nforge ci-fix # Re-analyze with failed-pipeline logs\nforge merge # Merge the MR\nforge resolution-check # Verify merged MR closes the issue\nforge close # Close the issue\nforge use group/other-repo # Switch active repo\nforge update # Install the latest release (a source checkout pulls instead)\nforge improve # Evaluate patterns and suggest improvements\nforge improve --create # Create improvement issues in the repo\n```\n\n#### Options\n\n```\n-p, --port <n> Port for start\n--repo <group/repo> Primary repo\n--secondary <repo> Additional repo (repeatable)\n--all-repos Include every configured repo\n--model <id> Claude model override\n--agent <name> Agent backend: claude\n--prompt <text> Extra user prompt\n--json JSON output\n--quiet Suppress progress\n--yes Skip confirmation gates\n--validate Run validate before apply\n```\n\n#### Diagnostics\n\n```bash\nforge doctor # Check prerequisites (node, claude CLI, env vars)\nforge session list # List cached sessions\nforge session clean # Remove stale session data\nforge reset-password <email> # Locked out? Set a new password on the Forge host\n```\n\n`reset-password` writes straight to the Forge database (`FORGE_DB_PATH`, default `~/.forge/forge.db`), so it works without signing in and only for someone with shell access to the host. It asks for the new password twice without echoing it (or reads one line from piped stdin), signs the account out everywhere, and records `user.password_reset` in the audit log. SSO accounts are refused; their passwords live with the identity provider.\n\n`forge help` lists every command and its options.\n\n---\n\n## Configuration\n\n### Core\n\n| Variable | Required | Description |\n|---|---|---|\n| `FORGE_TOKEN` | — | Admin credential for automation; also accepted as the remote setup code. Never sent to the browser |\n| `FORGE_REPOS` | Yes | Comma-separated repo list; first entry is the primary project |\n| `SCM_PROVIDER` | — | `gitlab` (default) · `github` · `azuredevops` |\n| `ANTHROPIC_API_KEY` | — | Enables live model list from Anthropic; falls back to hardcoded list. Never used to run Claude |\n| `FORGE_MODEL` | — | Override default model (default: `claude-sonnet-4-6`) |\n| `FORGE_AGENT_MODE` | — | `structured` (default) · `agentic` — how Claude proposes changes (see [Agent modes](#agent-modes)) |\n| `PORT` | — | HTTP server port (default: `5001`) |\n\n### Identity and storage\n\n| Variable | Required | Description |\n|---|---|---|\n| `FORGE_DB_PATH` | — | SQLite database for members, sessions, API tokens and the audit log (default `~/.forge/forge.db`) |\n| `FORGE_ENCRYPTION_KEY` | — | Base64 32-byte key used to encrypt SCM tokens at rest. Without it Forge generates `~/.forge/master.key` (`FORGE_KEY_PATH`) |\n| `FORGE_OIDC_ISSUER` | — | OIDC issuer URL (Okta, Entra ID, Google Workspace, GitLab, …). Enables \"Continue with SSO\" |\n| `FORGE_OIDC_CLIENT_ID` / `FORGE_OIDC_CLIENT_SECRET` | — | OIDC client credentials |\n| `FORGE_PUBLIC_URL` | — | External URL; the OIDC redirect is `${FORGE_PUBLIC_URL}/api/forge/auth/oidc/callback` unless `FORGE_OIDC_REDIRECT_URI` is set |\n| `FORGE_OIDC_NAME` | — | Label on the sign-in button (default `SSO`) |\n| `FORGE_OIDC_ALLOWED_DOMAINS` | — | Comma-separated email domains allowed to sign in with SSO |\n| `FORGE_OIDC_DEFAULT_ROLE` | — | Role given to new SSO members (default `viewer`) |\n| `FORGE_TRUST_PROXY` | — | Express `trust proxy` setting when behind a load balancer |\n| `FORGE_COOKIE_SECURE` | — | Override Secure cookies (on by default in production) |\n| `FORGE_DISABLE_SERVICE_TOKEN` | — | `1` stops `FORGE_TOKEN` working as an API credential once setup is done |\n\n### Claude billing\n\nBy default the `claude` CLI runs on the Claude Code subscription it is logged in with, and Forge strips `ANTHROPIC_API_KEY` from its environment so an env key can't quietly switch it to API credits.\n\nAn admin can save a workspace **Anthropic API key** in Settings → Connections. Claude then runs on that account's API credits instead. This is the mode for a hosted Forge serving other teams, since a personal subscription is only for its owner's own use. The key is encrypted at rest like SCM tokens, shown back only as a hint, and clearing it returns Claude to the subscription.\n\n### Provider credentials\n\nSCM tokens can be set here **or** in the Forge Settings UI (Settings → Connections). The UI value takes precedence over env vars.\n\n#### GitLab (`SCM_PROVIDER=gitlab`, default)\n\n| Variable | Required | Description |\n|---|---|---|\n| `GITLAB_TOKEN` | Yes | Personal access token with `api` + `write_repository` scopes |\n\n#### GitHub (`SCM_PROVIDER=github`)\n\n| Variable | Required | Description |\n|---|---|---|\n| `GITHUB_TOKEN` | Yes | Personal access token with `repo` + `workflow` scopes |\n| `GITHUB_OWNER` | Yes | Organization or user, e.g. `ipul-labs` |\n| `GITHUB_REPO` | Yes | Repository name, e.g. `web-app` |\n| `GITHUB_BASE_BRANCH` | — | Default target branch (default: `main`) |\n\n#### Azure DevOps (`SCM_PROVIDER=azuredevops`)\n\n| Variable | Required | Description |\n|---|---|---|\n| `AZURE_DEVOPS_TOKEN` | Yes | Personal access token (Full access or Code + Work Items) |\n| `AZURE_DEVOPS_ORG` | Yes | Organization name, e.g. `ipul-labs` |\n| `AZURE_DEVOPS_PROJECT` | Yes | Team project name, e.g. `web-app` |\n| `AZURE_DEVOPS_REPO` | — | Git repo name (defaults to project name) |\n| `AZURE_DEVOPS_BASE_BRANCH` | — | Default target branch (default: `main`) |\n\n### Deprecated (still work as fallbacks)\n\n| Variable | Replaced by |\n|---|---|\n| `FORGE_GITLAB_PROJECT` | First entry in `FORGE_REPOS` |\n| `FORGE_REFINE_REPO_ACCESS` | Settings UI toggle (Settings → Defaults) |\n\nSee `.env.example` for a full annotated list.\n\n---\n\n## Multi-provider support\n\nForge's SCM layer is a clean provider abstraction — all three providers implement the same `ScmProvider` interface and return identical normalized shapes (`NormalIssue`, `NormalMR`, `NormalPipeline`, etc.). The rest of the codebase never knows which provider is active.\n\n### Switching providers\n\n```bash\n# .env\nSCM_PROVIDER=github\nGITHUB_TOKEN=ghp_...\nGITHUB_OWNER=my-org\nGITHUB_REPO=my-repo\n```\n\n### Per-repo prefix (multi-repo mixed-provider setups)\n\n```bash\nFORGE_REPOS=gitlab:ipul/web-app,github:my-org/mobile\n```\n\n### Provider capabilities\n\n| Feature | GitLab | GitHub | Azure DevOps |\n|---|---|---|---|\n| Issues / Work Items | Yes | Yes | Yes (Work Items) |\n| Multi-file commits | Yes | Yes (Git Data API) | Yes (Pushes API) |\n| Merge / Pull Requests | Yes | Yes | Yes |\n| CI pipelines | Yes (GitLab CI) | Yes (GitHub Actions) | Yes (Azure Pipelines) |\n| Pipeline history page | Yes | — | — |\n| Job log traces | Yes | Yes | Yes |\n| Server-side rebase | Yes | — | — |\n| Ticket labels | Yes | Yes | Yes (Tags) |\n| Delete tickets | Yes (project Owner) | — (close instead) | Yes (recycle bin) |\n| Review env link on MRs | Yes (environment deployed by the MR pipeline) | — | — |\n\nOn GitLab, the MR tab shows a **Review env ↗** link when the MR's pipeline has deployed a live environment with an external URL (e.g. a `review/mr-<iid>` job with `environment:url`). No config needed; MRs without one show nothing.\n\n---\n\n## Security\n\nForge is designed for self-hosted deployment on trusted infrastructure. Security measures in place:\n\n- **Member accounts** — each person signs in with a password (scrypt) or SSO (OIDC with PKCE). Sessions are httpOnly, SameSite cookies stored server-side as hashes, with a 12-hour idle and 7-day absolute limit. No credential ever reaches browser JavaScript or the client bundle\n- **Roles** — viewer, operator, approver and admin, enforced on every API route. Merging needs approver; connections, repos and members need admin\n- **Audit log** — every sign-in and every change is recorded with who, what, where and the outcome. It is append-only and visible to admins under Audit log\n- **Encryption at rest** — SCM tokens in `~/.forge/config.json` are sealed with AES-256-GCM\n- **CSRF protection** — cookie-authenticated writes require a custom request header\n- **Brute-force protection** — accounts lock for 15 minutes after 5 failed sign-ins, and sign-in endpoints are rate-limited per IP. API calls are rate-limited per member\n- **API tokens** — personal tokens for the CLI and automation, shown once, stored hashed and revocable\n- **Security headers** — Helmet middleware sets X-Frame-Options, HSTS, X-Content-Type-Options, and CSP (production)\n- **Request size limits** — JSON body capped at 1MB to prevent memory exhaustion\n- **Secret redaction** — error logs and API responses scrub tokens (`glpat-*`, `ghp_*`, `oauth2:*@`) before output\n- **File permissions** — config files (`~/.forge/config.json`) written with `0600`, session directories with `0700`\n- **Git credential isolation** — SCM tokens passed via `GIT_ASKPASS` helper, never embedded in clone URLs\n- **Path traversal protection** — symlink-aware `realpath` checks prevent file writes outside clone directories\n- **No shell injection** — all subprocess calls use `spawn()` with argument arrays, never shell strings\n- **Atomic config writes** — config updates use temp file + rename to prevent corruption\n\n`FORGE_TOKEN`, when set, is an admin credential for automation. Use a long random value, never reuse an SCM token, and set `FORGE_DISABLE_SERVICE_TOKEN=1` once your team uses personal API tokens.\n\n### First run\n\nOpen Forge and create the first admin account. On the server machine itself (`localhost`) that is all it takes. From anywhere else the form asks for a setup code, which Forge prints to its log on first start (or your `FORGE_TOKEN`, if set), so nobody can claim a fresh install that is reachable on the network. Add the rest of your team under **Members**, or turn on SSO with the `FORGE_OIDC_*` variables.\n\nThe `forge` CLI authenticates with a personal API token: create one under **Settings → API tokens** and export it as `FORGE_API_TOKEN`.\n\n---\n\n## Production deployment\n\nOn a server, install from npm and run `forge start` as a service (systemd, pm2 or similar) from the folder holding your `.env`. Put it behind your HTTPS reverse proxy and set `FORGE_PUBLIC_URL` to the address people use.\n\nFrom a source checkout:\n\n```bash\nnpm run build # builds the SPA to dist/\nNODE_ENV=production tsx server.ts\n```\n\nOr with Docker, from a checkout:\n\n```bash\ndocker build -t forge .\ndocker run -p 5001:5001 --env-file .env -v forge-data:/data forge\n```\n\n### Kubernetes\n\n`manifests/forge/` holds an example Deployment, Service, ConfigMap and Secret template. Build and push the Docker image to your own registry, set `image` in `deployment.yaml`, create the secret, then:\n\n```bash\nkubectl create namespace forge\nkubectl apply -n forge -f manifests/forge/\n```\n\n---\n\n## Architecture\n\nFull-stack TypeScript. The backend runs via `tsx` (no build step); the frontend compiles through Vite.\n\n```\nbin/\n forge-agent.ts # CLI entry point (forge command)\n cli/\n ship.ts # End-to-end orchestrator (Ink UI)\n analyze.ts # Two-pass issue analysis\n repl.ts # Interactive terminal (Ink-based)\n serverClient.ts # CLI → Server API client (server/local mode)\n sessionStore.ts # ~/.forge/sessions/ persistence\n ...\n\nshared/\n events.ts # FORGE_EVENTS constants (server + client)\n\nserver/\n forge/\n routes.ts # Express router — all /api/forge/* endpoints\n core/\n ForgeAgent.ts # Orchestrates the full pipeline (analyze -> apply -> refine)\n claudeClient.ts # Shells out to the claude CLI for structured output\n agentClient.ts # Thin wrapper over claudeClient\n jobs.ts # Job store with SSE event buffering + replay\n providers/\n base.ts # Abstract ScmProvider — normalized shapes + interface\n gitlab.ts # GitLab REST API v4\n github.ts # GitHub REST API v3 + Git Data API\n azuredevops.ts # Azure DevOps REST API\n index.ts # createProvider() factory\n\nsrc/\n views/Forge/\n ForgeDashboard.tsx # Root UI component with tabbed issue/MR views\n api.ts # Session-aware fetch helper (cookie + CSRF header)\n types.ts # Shared TypeScript types\n utils.ts # SSE helper + state utilities\n ui.ts # Shared responsive class tokens (touch-sized actions)\n components/ # Analysis progress, pipeline status, diff viewer, Sheet, Switch\n shell/ # App chrome: sidebar (drawer on phones), top bar, command palette, phone tab bar\n useNav.ts # Single nav definition shared by sidebar, tab bar and command palette\n hooks/\n useForgeDashboard.ts # All state, phase transitions, and API calls\n useForgeEvents.ts # SSE event bus subscription\n```\n\n### Mobile layout\n\nBelow Tailwind's `md` breakpoint (768px) the dashboard adds iOS-style touches on top of the responsive shell: a bottom tab bar (Issues · Merge requests · Pipelines · Settings) on list pages, a New task button in the top bar, New Task as a bottom sheet, iOS switches, full-width 50pt primary actions, and 16px form fields so Safari doesn't zoom. The sidebar (repo picker, usage, theme, admin pages, sign-out) opens as a drawer from the top bar. It respects safe-area insets and uses `100dvh`, so it works as a home-screen web app on iPhone. Desktop layout is unchanged.\n\n### Install as an app (PWA)\n\nForge ships a web app manifest (`public/manifest.webmanifest`) and icons, so it can be installed. On iPhone open it in Safari → Share → **Add to Home Screen**; on Android/Chrome use **Install app**. It opens standalone at `/issues`. The icon source is `public/icon.svg`; regenerate the PNGs after changing it:\n\n```bash\nfor s in 180 192 512; do magick -background none -density 384 public/icon.svg -resize ${s}x${s} -strip PNG32:public/icon-$s.png; done\nmv public/icon-180.png public/apple-touch-icon.png\n```\n\nIf the app bundle fails to load or throws before React mounts, `public/boot-check.js` reloads once (a stale bundle after a deploy) and then shows the error with the browser's user agent and a Reload button instead of a blank screen. In production a missing file under `/assets/` returns 404 rather than `index.html`, so a stale bundle fails visibly.\n\n### Agent modes\n\nForge can drive Claude in two ways, set by `FORGE_AGENT_MODE` (or the Settings default, or a per-request `agentMode`):\n\n- **`structured`** (default) — the two-pass JSON pipeline below. Deterministic and schema-validated: Claude runs single-turn with tools disabled and returns complete file contents that Forge overlays onto a clone.\n- **`agentic`** — Claude runs as a full Claude Code agent inside a checkout of the repo: real tools (Read/Edit/Write/Bash…), the project's own `CLAUDE.md`, and a multi-turn loop. Forge captures the resulting `git diff` instead of parsing JSON, then feeds those changes into the same apply/validate/MR pipeline. The capture never includes symlinks or dependency folders (`node_modules`, `bower_components`, `jspm_packages`, `.venv`, `__pycache__`), whatever the repo's `.gitignore` says; anything left out is noted in the run log. Higher fidelity, less determinism. Requires the `claude` backend, a single repo, and a GitLab primary — otherwise Forge logs why and falls back to structured. Pick the mode under **Settings → Defaults → Agent mode**.\n\n Refine follows the mode too. In agentic mode a follow-up (\"open it in Playwright and check it\", \"rename the prop\") runs as the same full agent in the issue's worktree with the current proposal already applied, so it can run tests and browsers, gather evidence and edit. If it changes nothing and gathers no evidence, its reply comes back as an answer and the proposal is left as it was. In structured mode, refine only gets read-only repo access (Read/Glob/Grep), so it can't run anything. CI-fix refines always use the structured fix.\n\n### Rules and evidence\n\n**Settings → Defaults → Prompt rules** is free text added to every analysis and refine prompt as \"Custom rules — always follow these\", in both modes. In agentic mode the target repo's `CLAUDE.md` applies as well, so repo conventions belong there and Forge-wide instructions belong in the rules.\n\nIn agentic mode a rule can also ask for proof, e.g. *\"Run the e2e tests and screenshot every page you changed, at desktop and phone width.\"* Forge doesn't capture anything itself: the agent installs and runs what it needs (Playwright and its browsers included) and leaves the results in `.forge-evidence/` at the checkout root:\n\n- `.forge-evidence/EVIDENCE.md` — what it ran and what it saw; images are linked by file name (`![home](home.png)`)\n- PNG/JPEG/GIF/WebP screenshots next to it (nested folders are fine; up to 10 images, 10 MB each)\n\nAfter the run Forge uploads the images to the GitLab project, points the write-up's links at the uploads (appending any image it didn't mention), and puts the result in the MR's **🧪 Testing** section. The folder is cleared before each run and listed in the clone's `.git/info/exclude`, so it is never committed. A failed upload is logged in the activity feed and doesn't fail the analysis. The agent's run is bounded by `cliTimeout` (10 minutes by default), so a full browser install plus a long suite may need more.\n\n### Workspaces\n\nEvery local checkout Forge works in lives under `~/.forge/cache` (or `FORGE_VALIDATE_CACHE`):\n\n```\nrepos/<repo>/ shallow base clone, one per repo\nworktrees/<repo>/issue-<iid>/ git worktree per issue, on its forge/issue-… branch\n```\n\nAgentic runs, refine (with repo access) and validation each lease the issue's worktree, reset it to the remote HEAD, and release it when done. Different issues never share a tree, and git work on one repo (fetch, worktree add/remove) is serialised. Ignored files such as `node_modules` survive between runs of the same issue.\n\n**Settings → Workspaces** (admins) lists every base clone and worktree with its size, last use and whether a running job holds it, plus anything Forge doesn't own any more: clones from the old one-clone-per-repo layout (\"Old-layout clone\") and worktree folders git no longer knows about (\"Stray folder\"). You can delete one, or **Purge idle** to remove everything not in use; tick *Include base clones* to drop base clones that have no worktrees left too. A deleted workspace is simply re-created by the next job that needs it. The same actions are available as `GET /workspaces`, `DELETE /workspaces?id=<id>` and `POST /workspaces/purge` (`{\"includeBases\": true}`); a workspace in use answers 409.\n\n### Two-pass analysis (structured mode)\n\n**Pass 1** — `identifyRelevantFiles`: Claude reads the issue + file tree, returns the max-6 most relevant paths.\n\n**Pass 2** — `proposeFix`: Claude reads the actual file contents and proposes a targeted fix as a JSON diff.\n\nBoth passes use `claude --print --output-format json` so cost and service tier are tracked automatically.\n\n### CI refinement\n\nWhen a pipeline fails, Forge fetches the last 150 lines of each failed job's trace log and feeds them to pass 2 with an explicit \"pipeline failed\" framing — not generic reviewer feedback. This improves fix quality for common failures like lint errors and type errors.\n\n### Provider abstraction\n\nAll three SCM providers implement `ScmProvider` from `providers/base.ts`. The contract is:\n- **Normalized return types** — all methods return `NormalIssue`, `NormalMR`, `NormalPipeline`, etc., regardless of provider\n- **Instance-scoped** — each provider is instantiated with a repo string; no global state\n- **`supportsRebase`** — providers that don't support server-side rebase return `false`; the UI and API layer check this before showing the rebase button\n\n---\n\n## Programmatic usage\n\nThe npm package ships compiled JavaScript with TypeScript declarations — no `tsx` or TypeScript runtime needed by consumers.\n\n```ts\nimport { createApi } from \"@ipul-labs/forge/app\"; // Express app with auth + API\nimport { ForgeAgent } from \"@ipul-labs/forge/agent\"; // Core pipeline class\nimport { createProvider } from \"@ipul-labs/forge/providers\"; // SCM factory\n```\n\nThe bare router (`@ipul-labs/forge`) expects the auth stack in front of it and rejects requests without a signed-in principal. Mount `createApi({ db, env: process.env, isProd })` instead, with `db` from `openDatabase`.\n\n```ts\nconst agent = new ForgeAgent({ project: \"group/repo\" });\nconst result = await agent.analyzeIssue(42, (event) => console.log(event));\nconst { branchName, mr } = await agent.applyFix(result);\n```\n\n### Development\n\nSource is TypeScript (`.ts`). The dev server runs via `tsx` (no build step). For the npm package, `tsc` compiles to `lib/` with declarations during `prepack`.\n\n```bash\nnpm run dev # tsx watch + vite (dev)\nnpm run build:server # compile server/bin/shared → lib/\nnpm run typecheck # check both frontend + server configs\n```\n\n---\n\n## Contributing\n\nThe source repository is private for now. Send bug reports, ideas and security problems to support@ipullabs.com; please report security problems privately rather than in public.\n\n## Support\n\nQuestions or problems with Forge: support@ipullabs.com\n\n---\n\n## License\n\nMIT. Forge is free to use, change and share, for personal or commercial work. The full license text is in the `LICENSE` file that ships with the package.\n"
101
+ "license": "MIT"
104
102
  }