@atoms-tech/atoms-mcp 0.25.2 → 0.25.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +1 -1
- package/dist/index.js +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import e,{readFile as t,mkdir as r,writeFile as i,unlink as n,stat as a,open as s}from"fs/promises";import{homedir as o}from"os";import d,{join as c}from"path";import{createClient as l}from"@supabase/supabase-js";import{createServer as m}from"http";import p,{randomUUID as u,randomBytes as _,createHash as f}from"crypto";import{fileURLToPath as h,URL as g}from"url";import{z as y}from"zod";import{registerAppTool as w,registerAppResource as b,RESOURCE_MIME_TYPE as v}from"@modelcontextprotocol/ext-apps/server";import{McpServer as j}from"@modelcontextprotocol/sdk/server/mcp.js";import{createRequire as S}from"module";import{StdioServerTransport as x}from"@modelcontextprotocol/sdk/server/stdio.js";var k,E,I,A,O,R,D,H=Object.defineProperty,C=Object.getOwnPropertyNames,U=(e,t,r)=>function(){if(r)throw r[0];try{return e&&(t=(0,e[C(e)[0]])(e=0)),t}catch(e){throw r=[e],e}},q=(e,t)=>{for(var r in t)H(e,r,{get:t[r],enumerable:!0})};function T(e){return D.get(e)}var M=U({"src/core/tool-catalog.ts"(){k=new Set(["atoms_status","atoms_list_projects"]),E=new Set(["atoms_create_item","atoms_update_item","atoms_delete_item","atoms_restore_item","atoms_link_items","atoms_bulk_import","atoms_record_test_result","atoms_record_test_results","atoms_create_variable","atoms_update_variable","atoms_delete_variable","atoms_create_domain","atoms_update_domain","atoms_delete_domain","atoms_create_level","atoms_create_field","atoms_delete_field","atoms_reorder_fields","atoms_create_baseline","atoms_lock_baseline","atoms_restore_baseline","atoms_create_analysis","atoms_fill_row","atoms_link_mitigation"]),I=new Set(["atoms_delete_item","atoms_delete_variable","atoms_delete_domain","atoms_delete_field","atoms_restore_baseline"]),A=new Set(["atoms_bulk_import"]),O={items:["atoms_list_items","atoms_get_item","atoms_search","atoms_semantic_search","atoms_browse","atoms_get_history","atoms_create_item","atoms_update_item","atoms_delete_item","atoms_restore_item","atoms_bulk_import"],traceability:["atoms_trace","atoms_link_items","atoms_export_mermaid","atoms_impact_analysis"],coverage:["atoms_get_coverage","atoms_verification_status","atoms_record_test_result","atoms_record_test_results","atoms_project_summary","atoms_list_assets"],variables:["atoms_list_variables","atoms_get_variable","atoms_create_variable","atoms_update_variable","atoms_delete_variable"],domains:["atoms_list_domains","atoms_create_domain","atoms_update_domain","atoms_delete_domain","atoms_list_levels","atoms_create_level","atoms_list_fields","atoms_create_field","atoms_delete_field","atoms_reorder_fields"],baselines:["atoms_list_baselines","atoms_create_baseline","atoms_lock_baseline","atoms_diff_baselines","atoms_restore_baseline"],analysis:["atoms_create_analysis","atoms_fill_row","atoms_get_analysis","atoms_link_mitigation","atoms_get_gaps"]},R=Object.keys(O),D=new Map;for(const[e,t]of Object.entries(O))for(const r of t)D.set(r,e)}});function P(e,t){if(k.has(t))return{allowed:!0};if(e.lockdown&&!new Set(["atoms_list_items","atoms_get_item","atoms_search","atoms_browse"]).has(t))return{allowed:!1,reason:"lockdown"};if(e.forbidden_tools.includes(t))return{allowed:!1,reason:"forbidden_tool"};if(e.read_only&&E.has(t))return{allowed:!1,reason:"read_only"};if(e.block_destructive&&I.has(t))return{allowed:!1,reason:"block_destructive"};if(e.block_bulk_import&&A.has(t))return{allowed:!1,reason:"block_bulk_import"};const r=T(t);return void 0===r||e.allowed_toolsets.includes(r)?{allowed:!0}:{allowed:!1,reason:"toolset_disabled"}}function $(e,t){return P(e,t).allowed}function N(e){switch(e){case"lockdown":return"MCP is in lockdown mode for your role — only read-only browse is available.";case"read_only":return"Your org policy puts MCP in read-only mode. Mutating tools are not exposed.";case"block_destructive":return"Your org policy blocks destructive operations (delete tools).";case"block_bulk_import":return"Your org policy blocks bulk import.";case"forbidden_tool":return"This tool is on your org's forbidden list.";case"toolset_disabled":return"This tool's toolset is not enabled in your org policy."}}var L,F,W,V,B=U({"src/core/policy.ts"(){M()}}),z=U({"src/core/types.ts"(){L={read_only:!1,lockdown:!1,block_destructive:!1,block_bulk_import:!1,cross_project_browse:!0,allowed_toolsets:["items","traceability","coverage","variables","domains","baselines"],forbidden_tools:[],project_scope:null,require_confirmation_for:[]},F={read_only:!0,lockdown:!0,block_destructive:!0,block_bulk_import:!0,cross_project_browse:!1,allowed_toolsets:[],forbidden_tools:[],project_scope:[],require_confirmation_for:[]}}});function J(){return W}var Q=U({"src/middleware/session-policy.ts"(){z(),W=L,V=null}});function G(){const e=process.env.ATOMS_MCP_TOOLSETS?.trim();if(!e)return null;const t=e.split(",").map(e=>e.trim()).filter(Boolean);if(0===t.length)return null;const r=new Set,i=[];for(const e of t)R.includes(e)?r.add(e):i.push(e);return{allowed:r,unknown:i}}function Y(){const e=process.env.ATOMS_MCP_READ_ONLY;return"1"===e||"true"===e?.toLowerCase()}function K(e){if(Y()&&E.has(e))return!1;if(k.has(e))return!0;const t=G();if(null===t)return $(J(),e);const r=T(e);return!!r&&!!t.allowed.has(r)&&$(J(),e)}var Z,X,ee=U({"src/middleware/tool-registry.ts"(){M(),B(),Q()}}),te={};async function re(){try{const e=await t(X,"utf-8"),r=JSON.parse(e);return r.access_token&&r.refresh_token?r:null}catch{return null}}async function ie(e){await r(Z,{recursive:!0,mode:448}),await i(X,JSON.stringify(e,null,2),{mode:384})}async function ne(){const e=await re();return!!e&&e.expires_at>Date.now()+6e4}function ae(e){return e.expires_at>Date.now()+6e4}async function se(){try{await n(X)}catch{}}function oe(){return X}q(te,{clearCredentials:()=>se,getCredentialsPath:()=>oe,hasValidCredentials:()=>ne,isTokenValid:()=>ae,readCredentials:()=>re,writeCredentials:()=>ie});var de,ce,le,me,pe,ue,_e,fe=U({"src/auth/token-store.ts"(){Z=c(o(),".atoms"),X=c(Z,"credentials.json")}}),he={};async function ge(){try{await r(de,{recursive:!0,mode:448});const e=await s(ce,"wx");try{await e.writeFile(`${process.pid} ${Date.now()}`)}finally{await e.close()}return!0}catch(e){if("EEXIST"===e.code)return!1;throw e}}async function ye(){try{await n(ce)}catch{}}async function we(){try{const e=await a(ce);return Date.now()-e.mtimeMs>le}catch{return!1}}async function be(e,t,r=_e){const i=r.now()+me;for(;;){if(await r.tryAcquire())try{try{return await e()}catch{}return await t()}finally{await r.release()}try{return await e()}catch{}if(await r.isStale())await r.release();else{if(r.now()>i)return await t();await r.wait(pe)}}}q(he,{runExclusiveAuth:()=>be});var ve,je,Se,xe=U({"src/auth/lock.ts"(){de=c(o(),".atoms"),ce=c(de,".auth.lock"),le=7e4,me=12e4,pe=300,ue=e=>new Promise(t=>setTimeout(t,e)),_e={now:Date.now,wait:ue,tryAcquire:ge,release:ye,isStale:we}}}),ke={};function Ee(e){return e.trim().replace(/\/+$/,"")}q(ke,{ATOMS_APP_URL:()=>Se,ATOMS_SUPABASE_ANON_KEY:()=>je,ATOMS_SUPABASE_URL:()=>ve,resolveUrl:()=>Ee});var Ie=U({"src/config.ts"(){ve=Ee(process.env.ATOMS_SUPABASE_URL??"https://gmebjyhomsbvhrxffzre.supabase.co"),je=process.env.ATOMS_SUPABASE_ANON_KEY??"sb_publishable_b49MUAB8XCrQiF7x5b9lUA_w1aplSBH",Se=process.env.ATOMS_APP_URL??"https://atoms.tech"}}),Ae={};async function Oe(){const e=process.env.ATOMS_ACCESS_TOKEN;if(e){const t=De(e);return{access_token:e,refresh_token:"",user_id:t.sub,email:t.email??""}}const t=await re();if(t&&t.expires_at>Date.now()+6e4){const e=De(t.access_token);return{access_token:t.access_token,refresh_token:t.refresh_token,user_id:e.sub,email:e.email??t.user_email??""}}return null}async function Re(){const e=await Oe();if(e)return e;const t=await re();if(!t)throw new Error("Not authenticated. Run 'npx @atoms-tech/atoms-mcp login' to connect your ATOMS account.");return be(async()=>{const e=await Oe();if(e)return e;throw new Error("no fresh token yet")},()=>async function(e){process.stderr.write("[atoms-mcp] Refreshing access token...\n");const t=l(ve,je);let r=await t.auth.refreshSession({refresh_token:e.refresh_token});if(r.error||!r.data.session){const i=await re();if(i&&i.expires_at>Date.now()+6e4){const e=De(i.access_token);return{access_token:i.access_token,refresh_token:i.refresh_token,user_id:e.sub,email:e.email??i.user_email??""}}i&&i.refresh_token&&i.refresh_token!==e.refresh_token&&(process.stderr.write("[atoms-mcp] Refresh token was rotated by another session; retrying with the latest...\n"),r=await t.auth.refreshSession({refresh_token:i.refresh_token}))}if(r.error||!r.data.session){const e=r.error?[r.error.status,r.error.message].filter(Boolean).join(" ").trim():"no session returned";throw new Error(`Token refresh failed (${e}). Your saved ATOMS session is no longer valid — re-run 'npx @atoms-tech/atoms-mcp login'.`)}const i=r.data.session;return await ie({access_token:i.access_token,refresh_token:i.refresh_token,expires_at:Date.now()+1e3*(i.expires_in??3600),user_email:i.user?.email}),{access_token:i.access_token,refresh_token:i.refresh_token,user_id:i.user?.id??"",email:i.user?.email??""}}(t))}function De(e){const t=e.split(".");if(3!==t.length)throw new Error("Invalid JWT format");const r=t[1].replace(/-/g,"+").replace(/_/g,"/").padEnd(t[1].length+(4-t[1].length%4)%4,"="),i=JSON.parse(Buffer.from(r,"base64").toString("utf-8"));if(!i.sub)throw new Error("JWT missing 'sub' claim");return i}q(Ae,{getValidToken:()=>Re,readValidToken:()=>Oe});var He,Ce,Ue,qe,Te,Me=U({"src/auth/refresh.ts"(){fe(),xe(),Ie()}}),Pe={};async function $e(){if(He)try{const e=await re();e&&e.expires_at>Te&&(process.stderr.write("[atoms-mcp] Credentials file refreshed elsewhere — rebuilding client.\n"),Ne())}catch{}if(He&&qe>Date.now()+6e4)return He;He&&process.stderr.write("[atoms-mcp] Token expiring, refreshing client...\n");const{access_token:e,user_id:t,email:r}=await Re(),i=e.split(".");if(3===i.length)try{const e=JSON.parse(Buffer.from(i[1].replace(/-/g,"+").replace(/_/g,"/"),"base64").toString());qe=1e3*(e.exp??0)}catch{qe=Date.now()+36e5}try{const e=await re();Te=e?.expires_at??0}catch{Te=0}const n=l(ve,je,{global:{headers:{Authorization:`Bearer ${e}`}}});return Ce=t,Ue=r,He=n}function Ne(){He=null,Ce=null,Ue=null,qe=0,Te=0}function Le(e){let t="";if(e instanceof Error)t=e.message;else if(e&&"object"==typeof e){const r=e;t="string"==typeof r.message?r.message:"string"==typeof r.code?r.code:String(e)}else null!=e&&(t=String(e));return/jwt\s*expired|invalid.?jwt|token.*(?:has\s+)?expired|jwt.*invalid|pgrst301/i.test(t)}async function Fe(e){try{return await e()}catch(t){if(!Le(t))throw t;return process.stderr.write("[atoms-mcp] Data tier rejected JWT — invalidating client and retrying once.\n"),Ne(),await e()}}function We(){if(!Ce)throw new Error("Not authenticated. Call getClient() first.");return Ce}function Ve(){return Ue??""}async function Be(e,t){const r=We(),{data:i,error:n}=await e.from("projects").select("org_id").eq("id",t).maybeSingle();if(n||!i)throw new Error(`Project '${t}' not found`);const{data:a,error:s}=await e.from("org_members").select("role").eq("user_id",r).eq("org_id",i.org_id).maybeSingle();if(s||!a){const{data:t}=await e.rpc("is_platform_admin");if(!0===t)return"admin";throw new Error("Not a member of this project's organization")}if("viewer"===a.role)throw new Error("VIEWER_ROLE");return a.role}async function ze(e,t){const r=We(),{data:i,error:n}=await e.from("projects").select("org_id, created_by").eq("id",t).maybeSingle();if(n||!i)throw new Error(`Project '${t}' not found`);const{data:a,error:s}=await e.from("org_members").select("role").eq("user_id",r).eq("org_id",i.org_id).maybeSingle();if(s||!a)throw new Error("Not a member of this project's organization");if("admin"===a.role)return"admin";if(i.created_by===r)return"owner";throw new Error("PROJECT_FIELD_ADMIN_OR_OWNER_REQUIRED")}q(Pe,{getClient:()=>$e,getUserEmail:()=>Ve,getUserId:()=>We,invalidateClient:()=>Ne,isJwtExpiredError:()=>Le,requireProjectFieldDefinitionAccess:()=>ze,requireWriteAccess:()=>Be,withJwtRetry:()=>Fe});var Je,Qe,Ge=U({"src/db/client.ts"(){Me(),fe(),Ie(),He=null,Ce=null,Ue=null,qe=0,Te=0}}),Ye={};async function Ke(){const e=_(32).toString("base64url"),t=f("sha256").update(e).digest("base64url"),r=`http://localhost:${Je}${Qe}`,i=new g(`${Se}/auth/mcp-consent`);return i.searchParams.set("redirect_uri",r),i.searchParams.set("code_challenge",t),i.searchParams.set("code_challenge_method","S256"),new Promise((t,r)=>{let n;function a(){clearTimeout(n)}const s=m(async(i,n)=>{const o=new g(i.url??"/",`http://localhost:${Je}`);if(o.pathname!==Qe)return n.writeHead(404),void n.end("Not found");try{if("access_denied"===o.searchParams.get("error"))return n.writeHead(200,{"Content-Type":"text/html"}),n.end(Xe("Authorization was cancelled.")),a(),s.close(),void r(new Error("Authorization cancelled by user."));const i=o.searchParams.get("code");if(i){const o=await fetch(`${ve}/auth/v1/token?grant_type=pkce`,{method:"POST",headers:{"Content-Type":"application/json",apikey:je},body:JSON.stringify({auth_code:i,code_verifier:e})}),d=await o.json();if(!o.ok||!d.access_token){const e=d.error_description??d.msg??"Unknown error";return n.writeHead(200,{"Content-Type":"text/html"}),n.end(Xe(`Code exchange failed: ${e}`)),a(),s.close(),void r(new Error(`Code exchange failed: ${e}`))}let c="authenticated";try{c=JSON.parse(Buffer.from(d.access_token.split(".")[1],"base64").toString()).email??c}catch{}const{createClient:l}=await import("@supabase/supabase-js"),m=l(ve,je,{global:{headers:{Authorization:`Bearer ${d.access_token}`}}}),{data:p,error:u}=await m.from("org_members").select("org_id").limit(1);return u||!p||0===p.length?(n.writeHead(200,{"Content-Type":"text/html"}),n.end(Xe('No ATOMS account found for this Google account.<br><br>Please sign up at <a href="https://atoms.tech">atoms.tech</a> first, then run this login command again.')),a(),s.close(),void r(new Error("No ATOMS account found. Sign up at atoms.tech first."))):d.refresh_token?(await ie({access_token:d.access_token,refresh_token:d.refresh_token,expires_at:Date.now()+1e3*(d.expires_in??3600),user_email:c}),n.writeHead(200,{"Content-Type":"text/html"}),n.end(Ze(c)),a(),s.close(),void t({email:c})):(n.writeHead(200,{"Content-Type":"text/html"}),n.end(Xe("Sign-in succeeded but no refresh token was issued, so the session would expire within the hour. This usually points to a Supabase Auth setting (refresh-token rotation or session policy). Please contact ATOMS support.")),a(),s.close(),void r(new Error("No refresh token issued by Supabase — cannot persist a durable session.")))}const d=o.searchParams.get("access_token"),c=o.searchParams.get("refresh_token"),l=parseInt(o.searchParams.get("expires_in")??"3600",10);d&&c?(await ie({access_token:d,refresh_token:c,expires_at:Date.now()+1e3*l}),n.writeHead(200,{"Content-Type":"text/html"}),n.end(Ze("authenticated")),a(),s.close(),t({email:"authenticated"})):(n.writeHead(200,{"Content-Type":"text/html"}),n.end("<!DOCTYPE html>\n<html>\n<head><title>ATOMS MCP Login</title></head>\n<body>\n <h1>Completing login...</h1>\n <script>\n const hash = window.location.hash.substring(1);\n const params = new URLSearchParams(hash);\n const data = {\n access_token: params.get('access_token'),\n refresh_token: params.get('refresh_token'),\n expires_in: params.get('expires_in'),\n user_email: ''\n };\n if (data.access_token) {\n // Decode JWT to get email\n try {\n const payload = JSON.parse(atob(data.access_token.split('.')[1]));\n data.user_email = payload.email || '';\n } catch(e) {}\n fetch('/token', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(data)\n }).then(() => {\n document.body.innerHTML = '<h1>Login successful! You can close this tab.</h1>';\n }).catch(() => {\n document.body.innerHTML = '<h1>Login failed. Please try again.</h1>';\n });\n } else {\n document.body.innerHTML = '<h1>Login failed — no tokens received.</h1>';\n }\n <\/script>\n</body>\n</html>"))}catch(e){n.writeHead(500),n.end("Authentication failed"),a(),s.close(),r(e)}});s.on("request",async(e,i)=>{if("POST"===e.method&&"/token"===e.url){let n="";e.on("data",e=>{n+=e.toString()}),e.on("end",async()=>{try{const e=JSON.parse(n);await ie({access_token:e.access_token,refresh_token:e.refresh_token,expires_at:Date.now()+1e3*(e.expires_in??3600),user_email:e.user_email}),i.writeHead(200,{"Content-Type":"application/json"}),i.end(JSON.stringify({ok:!0})),a(),s.close(),t({email:e.user_email??"authenticated"})}catch(e){i.writeHead(500),i.end("Failed to store credentials"),a(),s.close(),r(e)}})}}),s.listen(Je,"127.0.0.1",async()=>{process.stderr.write("\nOpening browser for ATOMS login...\n"),process.stderr.write(`If the browser doesn't open, visit:\n${i.toString()}\n\n`);try{const{default:e}=await import("open");await e(i.toString())}catch{process.stderr.write("Could not open browser automatically. Please open the URL above.\n")}}),n=setTimeout(()=>{s.close(),r(new Error("Login timed out after 60 seconds. Run 'npx @atoms-tech/atoms-mcp login' to try again."))},6e4)})}function Ze(e){return`<!DOCTYPE html>\n<html>\n<head>\n <title>ATOMS MCP — Authorized</title>\n <style>\n * { margin: 0; padding: 0; box-sizing: border-box; }\n body {\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif;\n background: #0a0a0a;\n color: #fafafa;\n display: flex;\n align-items: center;\n justify-content: center;\n min-height: 100vh;\n }\n .card {\n background: #171717;\n border: 1px solid #262626;\n border-radius: 16px;\n padding: 48px;\n max-width: 480px;\n width: 100%;\n text-align: center;\n }\n .icon {\n width: 56px;\n height: 56px;\n background: #052e16;\n border-radius: 50%;\n display: flex;\n align-items: center;\n justify-content: center;\n margin: 0 auto 24px;\n }\n .icon svg { width: 28px; height: 28px; }\n h1 {\n font-size: 22px;\n font-weight: 600;\n margin-bottom: 8px;\n color: #22c55e;\n }\n .email {\n font-size: 14px;\n color: #a3a3a3;\n margin-bottom: 32px;\n }\n .permissions {\n text-align: left;\n background: #0a0a0a;\n border: 1px solid #262626;\n border-radius: 12px;\n padding: 20px;\n margin-bottom: 24px;\n }\n .permissions h3 {\n font-size: 12px;\n font-weight: 600;\n color: #737373;\n text-transform: uppercase;\n letter-spacing: 0.05em;\n margin-bottom: 12px;\n }\n .perm-item {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 6px 0;\n font-size: 14px;\n color: #d4d4d4;\n }\n .perm-item .dot {\n width: 6px;\n height: 6px;\n background: #22c55e;\n border-radius: 50%;\n flex-shrink: 0;\n }\n .closing {\n font-size: 13px;\n color: #525252;\n }\n .closing span { color: #a3a3a3; font-variant-numeric: tabular-nums; }\n </style>\n</head>\n<body>\n <div class="card">\n <div class="icon">\n <svg fill="none" viewBox="0 0 24 24" stroke="#22c55e" stroke-width="2.5">\n <path stroke-linecap="round" stroke-linejoin="round" d="M5 13l4 4L19 7"/>\n </svg>\n </div>\n <h1>Authorization Granted</h1>\n <p class="email">${e}</p>\n\n <div class="permissions">\n <h3>ATOMS MCP can now</h3>\n <div class="perm-item"><span class="dot"></span> Read your projects and requirements</div>\n <div class="perm-item"><span class="dot"></span> Create and edit requirements and test cases</div>\n <div class="perm-item"><span class="dot"></span> Record test results (pass/fail)</div>\n <div class="perm-item"><span class="dot"></span> Manage traceability relationships</div>\n <div class="perm-item"><span class="dot"></span> Export coverage reports and diagrams</div>\n </div>\n\n <p class="closing">This tab will close in <span id="countdown">5</span>s</p>\n </div>\n\n <script>\n let seconds = 5;\n const el = document.getElementById('countdown');\n const timer = setInterval(() => {\n seconds--;\n el.textContent = seconds;\n if (seconds <= 0) {\n clearInterval(timer);\n window.close();\n // If window.close() is blocked (not opened by script), update message\n setTimeout(() => {\n document.querySelector('.closing').textContent = 'You can close this tab now.';\n }, 500);\n }\n }, 1000);\n <\/script>\n</body>\n</html>`}function Xe(e){return`<!DOCTYPE html>\n<html>\n<head>\n <title>ATOMS MCP — Failed</title>\n <style>\n * { margin: 0; padding: 0; box-sizing: border-box; }\n body {\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif;\n background: #0a0a0a;\n color: #fafafa;\n display: flex;\n align-items: center;\n justify-content: center;\n min-height: 100vh;\n }\n .card {\n background: #171717;\n border: 1px solid #262626;\n border-radius: 16px;\n padding: 48px;\n max-width: 480px;\n width: 100%;\n text-align: center;\n }\n .icon {\n width: 56px;\n height: 56px;\n background: #450a0a;\n border-radius: 50%;\n display: flex;\n align-items: center;\n justify-content: center;\n margin: 0 auto 24px;\n }\n .icon svg { width: 28px; height: 28px; }\n h1 { font-size: 22px; font-weight: 600; margin-bottom: 16px; color: #ef4444; }\n .reason { font-size: 14px; color: #a3a3a3; line-height: 1.6; }\n .reason a { color: #7c3aed; text-decoration: underline; }\n </style>\n</head>\n<body>\n <div class="card">\n <div class="icon">\n <svg fill="none" viewBox="0 0 24 24" stroke="#ef4444" stroke-width="2.5">\n <path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12"/>\n </svg>\n </div>\n <h1>Authorization Failed</h1>\n <p class="reason">${e}</p>\n </div>\n</body>\n</html>`}q(Ye,{login:()=>Ke});var et,tt,rt,it,nt=U({"src/auth/login.ts"(){fe(),Ie(),Je=19275,Qe="/callback"}});function at(e,t=0){if(t>5)return"[depth-limit]";if("string"==typeof e){for(const t of rt)if(t.test(e.trim()))return"[redacted]";return e.length>it?e.slice(0,it)+"…":e}if(Array.isArray(e))return e.slice(0,20).map(e=>at(e,t+1));if(null!==e&&"object"==typeof e){const r={};for(const[i,n]of Object.entries(e)){const e=i.toLowerCase();et.has(e)?r[i]="[redacted]":tt.has(e)?r[i]="string"==typeof n?"[content]":at(n,t+1):r[i]=at(n,t+1)}return r}return e}function st(e){return at(e)}var ot,dt,ct,lt,mt,pt=U({"src/middleware/audit.ts"(){et=new Set(["access_token","refresh_token","password","api_key","secret","token","authorization","credential","credentials","private_key"]),tt=new Set(["body","description","content","notes","summary","title","system_prompt","prompt"]),rt=[/^[^\s@]+@[^\s@]+\.[^\s@]+$/,/^bearer\s+\S+$/i,/^(sk|pk|npm|gh|glpat|xoxb|xoxp|rk|SG\.)[-_][A-Za-z0-9_-]{16,}/,/^eyJ[A-Za-z0-9_-]+\.eyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+$/],it=200}}),ut=U({"src/middleware/rate-limiter.ts"(){ot=60,dt=6e4}});function _t(){const e=process.env.ATOMS_MCP_PROJECT_ID?.trim();return e?ct.test(e)?e:(lt||(process.stderr.write(`[atoms-mcp] WARNING: ATOMS_MCP_PROJECT_ID="${e.slice(0,50)}" is not a valid UUID — scope ignored.\n`),lt=!0),null):null}var ft,ht=U({"src/middleware/project-scope.ts"(){ct=/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i,lt=!1,mt=class extends Error{expectedProjectId;receivedProjectId;constructor(e,t){super("Tool call rejected: project_id is outside the configured ATOMS_MCP_PROJECT_ID scope."),this.name="ProjectScopeError",this.expectedProjectId=e,this.receivedProjectId=t}}}});function gt(e){const t=function(e){return e.trim().toLowerCase().replace(/[^a-z0-9]+/g,"_").replace(/^_+|_+$/g,"")}(e??"");return t?t.includes("cursor")?"cursor":t.includes("copilot")?"copilot":t.includes("claude")?ft:t:ft}var yt,wt,bt,vt,jt,St,xt,kt,Et=U({"src/core/client-identity.ts"(){ft="claude_desktop"}}),It=U({"src/core/timestamps.ts"(){yt=y.string().datetime({offset:!0})}}),At=U({"src/core/errors.ts"(){}}),Ot={};function Rt(e,t=bt){if("string"!=typeof e)return"";const r=e.replace(jt,"").replace(St,"");return r.length<=t?r:r.slice(0,t)+"..."}function Dt(e){return"string"!=typeof e?"":e.trim().toLowerCase().replace(/\s+/g,"-").replace(/[^a-z0-9-]/g,"").replace(/-+/g,"-").replace(/^-|-$/g,"")}function Ht(e,t){return{status:"success",data:e,...t?{meta:t}:{}}}function Ct(e,t,r){return{total_count:e,limit:t,offset:r,has_more:r+t<e}}function Ut(e,t){return{status:"error",message:e,next_steps:t}}function qt(e,t){return Ut(`${e} '${Rt(t)}' not found`,[`Verify the ${e.toLowerCase()} ID is correct`,"Use atoms_list_items or atoms_search to find valid IDs"])}function Tt(e){return Ut(`${e} role cannot modify project data`,["Contact your org admin to upgrade your role to editor","Use read-only tools (atoms_list_items, atoms_get_item, atoms_search) instead"])}function Mt(e){return Ut(`Validation error: ${Rt(e,vt)}`,["Check the parameter types and constraints in the tool description","Ensure all required parameters are provided"])}function Pt(){return Ut("Not authenticated. Run 'npx @atoms-tech/atoms-mcp login' first.",["Run: npx @atoms-tech/atoms-mcp login","Or set ATOMS_ACCESS_TOKEN environment variable"])}function $t(){return Ut("Your ATOMS session token expired and could not be refreshed automatically.",["Call atoms_status() — it will refresh the token if possible","If that still reports 'expired', restart Claude Desktop (quit and reopen)","If the problem persists, run: npx @atoms-tech/atoms-mcp logout && npx @atoms-tech/atoms-mcp login"])}function Nt(e){return Ut("Tool call rejected: project_id is outside the configured ATOMS_MCP_PROJECT_ID scope.",[`This MCP session is bound to project ${e}`,"Pass that project_id, or restart without ATOMS_MCP_PROJECT_ID to access other projects"])}function Lt(e){return Ut("Rate limited: too many requests to ATOMS API.",[e?`Wait ${e} seconds before retrying.`:"Wait a few seconds before retrying.","Reduce the frequency of back-to-back tool calls"])}function Ft(e){return Ut(`Parse error: ${Rt(e,vt)}`,["Check that body/title/summary fields contain valid text (no unexpected control characters)","If the content was generated programmatically, validate the string before passing it","Simplify the content and retry — if it succeeds, the original content has a malformed character"])}function Wt(e){return function(e){return/stale_item_write|sqlstate\s*40001|could not serialize access/i.test(e)}(e)}function Vt(){return Ut("Stale write rejected: this item changed after it was read.",["Call atoms_get_item again and use the returned metadata.updated_at value","Review the current item before retrying so you do not overwrite newer work","Retry atoms_update_item or atoms_link_items with the fresh expected timestamp"])}function Bt(){return Ut("Save paused because this item is already being written.",["Wait a few seconds, read the item again, and retry once","Do not repeatedly retry the same stale payload"])}function zt(){return Ut("Item saves are temporarily paused by an ATOMS operator.",["Keep your local draft and wait for the operator to re-enable saves","Do not repeatedly retry while the safety brake is active"])}function Jt(){return Ut("Item save rejected because this item is in Trash.",["Restore the item before changing it","Do not retry the same save while the item remains in Trash"])}function Qt(e){const t=e instanceof Error?e.message:String(e&&"object"==typeof e&&"message"in e?e.message??"":e),r=e&&"object"==typeof e&&"code"in e?String(e.code??""):"";return/not authenticated/i.test(t)?Pt():function(e){let t="";if(e instanceof Error)t=e.message;else if(e&&"object"==typeof e){const r=e;t="string"==typeof r.message?r.message:"string"==typeof r.code?r.code:String(e)}else null!=e&&(t=String(e));return/jwt\s*expired|invalid.?jwt|token.*(?:has\s+)?expired|jwt.*invalid|pgrst301/i.test(t)}(e)?$t():/rate.?limit|too many requests|429/i.test(t)?Lt():Wt(t)?Vt():/soft_deleted_item_write/i.test(t)?Jt():/save_temporarily_unavailable/i.test(t)?zt():"55P03"===r||"57014"===r||/item_save_busy|statement timeout|request timeout|upstream request timeout/i.test(t)?Bt():"42501"===r&&/org_project_field_admin_required/i.test(t)?Tt("Org admin"):"42501"===r&&/project_field/i.test(t)?Tt("Project admin or owner"):"P0002"===r&&/project_field/i.test(t)?Ut("Project field not found.",["Refresh the field list and retry with an existing field name"]):["22023","23505","23514"].includes(r)&&/project_field/i.test(t)?Mt(t.replace(/^project_field_/i,"").replace(/_/g," ")):/invalid input syntax|invalid json|unexpected token|malformed|json parse/i.test(t)?Ft(t):Yt(t)}function Gt(e){const t=e instanceof Error?e.message:String(e&&"object"==typeof e&&"message"in e?e.message??"":e??""),r=e&&"object"==typeof e&&"code"in e?String(e.code??""):"";return["40001","PT409","55P03","57014"].includes(r)||/stale_item_write|soft_deleted_item_write|save_temporarily_unavailable|item_save_busy|statement timeout|request timeout|upstream request timeout|fetch failed|network error|connection (?:closed|reset|refused)/i.test(t)}function Yt(e){return/row.level security|permission denied for/i.test(e)?Ut("Permission denied: your account does not have write access to this project.",["Verify you have editor or admin role in this project","Contact your org admin to upgrade your permissions","Use atoms_list_projects to confirm the project_id is one you have access to"]):Ut(`Database error: ${Rt(e,vt)}`,["This may be a temporary issue — try again","If the error persists, check that the project_id is valid"])}function Kt(e){return null==e?e:`<user_content>${e.replace(/<\/\s*user_content\s*>/gi,"</user_content>")}</user_content>`}function Zt(e){return xt.has(e)}function Xt(e,t,r=!1){return"string"==typeof e?r||null!=t&&xt.has(t)?Kt(e):e:Array.isArray(e)?e.map(e=>Xt(e,t,r)):e&&"object"==typeof e?Object.fromEntries(Object.entries(e).map(([e,t])=>[e,Xt(t,e,r||kt.has(e))])):e}function er(e){if(JSON.stringify(e,null,2).length>wt&&"success"===e.status&&Array.isArray(e.data)){const t=Math.max(1,Math.floor(e.data.length/2)),r={...e,data:e.data.slice(0,t),meta:{...e.meta,truncated:!0,truncation_message:`Response truncated from ${e.data.length} to ${t} items. Use 'offset' parameter or add filters to see more results.`}};return{content:[{type:"text",text:JSON.stringify(Xt(r),null,2)}],structuredContent:r}}return{content:[{type:"text",text:JSON.stringify(Xt(e),null,2)}],structuredContent:e}}function tr(e){return{content:[{type:"text",text:JSON.stringify(Xt(e),null,2)}],structuredContent:e,isError:!0}}q(Ot,{CHARACTER_LIMIT:()=>wt,USER_CONTENT_KEYS:()=>xt,accessDeniedError:()=>Tt,authError:()=>Pt,classifyError:()=>Qt,dbError:()=>Yt,errorResponse:()=>Ut,formatErrorResult:()=>tr,formatToolResult:()=>er,isAutoWrappedKey:()=>Zt,isItemSaveStopError:()=>Gt,isStaleWriteErrorMessage:()=>Wt,jwtExpiredError:()=>$t,notFoundError:()=>qt,paginationMeta:()=>Ct,parseError:()=>Ft,rateLimitError:()=>Lt,sanitizeForEcho:()=>Rt,sanitizeTagOrLevel:()=>Dt,saveBusyError:()=>Bt,saveUnavailableError:()=>zt,scopeError:()=>Nt,softDeletedItemError:()=>Jt,staleWriteError:()=>Vt,success:()=>Ht,validationError:()=>Mt,wrapMcpUserContentFields:()=>Xt,wrapUserContent:()=>Kt});var rr,ir=U({"src/tools/_base.ts"(){At(),wt=25e3,bt=50,vt=200,jt=new RegExp("[\\u0000-\\u001F\\u007F]","g"),St=new RegExp("[\\u200B-\\u200F\\u202A-\\u202E\\u2060-\\u206F\\uFEFF]","g"),xt=new Set(["body","description","domain","domains","error","level","levels","message","name","note","summary","title","value","effect","failure_mode","cause","operating_scenario","safe_state","ftti","rationale","hazard","assumptions","source_function"]),kt=new Set(["fields"])}});var nr=U({"src/apps/register.ts"(){rr={resourceDomains:["fonts.googleapis.com","fonts.gstatic.com"]}}});function ar(e){return function(e){return`"${e.replace(/\\/g,"\\\\").replace(/"/g,'\\"')}"`}(`*${t=e,t.replace(/[\\%_*]/g,e=>`\\${e}`)}*`);var t}var sr=U({"src/core/postgrest.ts"(){}});async function or(e,t,r){let i=e.from("items").select("*",{count:"exact"}).eq("project_id",t).is("deleted_at",null).order("created_at",{ascending:!0});r.type&&(i=i.eq("type",r.type)),r.domain&&(i=i.filter("data->tags->domains","cs",JSON.stringify([r.domain]))),r.level&&(i=i.filter("data->tags->>level","eq",r.level)),i=i.range(r.offset,r.offset+r.limit-1);const{data:n,error:a,count:s}=await i;if(a)throw new Error(a.message);return{items:n??[],totalCount:s??0}}async function dr(e,t,r){const{data:i,error:n}=await e.from("items").select("*").eq("id",r).eq("project_id",t).is("deleted_at",null).maybeSingle();if(n)throw new Error(n.message);return i}async function cr(e,t){if(t.baseline_id&&t.as_of)throw new Error("baseline_id and as_of are mutually exclusive");if(t.as_of)return t.as_of;if(t.baseline_id){const{data:r,error:i}=await e.from("baselines").select("project_id, snapshot_at, deleted_at").eq("id",t.baseline_id).maybeSingle();if(i)throw new Error(`resolveSnapshotAt: ${i.message}`);if(!r||null!==r.deleted_at)throw new Error(`Baseline not found: ${t.baseline_id}`);if(r.project_id!==t.project_id)throw new Error("Baseline does not belong to this project");return r.snapshot_at}return null}async function lr(e,t){const{data:r,error:i}=await e.from("baselines").select("id, project_id, name, description, field_definitions_snapshot, snapshot_at, created_at, created_by, locked_at, locked_by, deleted_at, release_version, release_status").eq("id",t).maybeSingle();if(i)throw new Error(`getBaselineById: ${i.message}`);return r&&null===r.deleted_at?r:null}function mr(e){return{id:e.id,project_id:e.project_id,type:e.type,title:e.title,data:e.data,created_by:e.changed_by,updated_by:e.changed_by,created_at:e.valid_from,updated_at:e.valid_from,deleted_at:null}}async function pr(e,t,r){const{data:i,error:n}=await e.rpc("project_state_at",{p_project_id:t,p_snapshot_at:r});if(n)throw new Error(`projectStateAt: ${n.message}`);return i??[]}async function ur(e,t,r,i){const n=r.trim();if(!n)return{items:[],totalCount:0};let a=null;if(i.asset&&i.asset.trim()){const{data:r,error:n}=await e.from("test_results").select("item_id").eq("project_id",t).eq("asset",i.asset.trim());if(n)throw new Error(n.message);const s=new Set;for(const e of r??[])s.add(e.item_id);if(a=Array.from(s),0===a.length)return{items:[],totalCount:0}}let s=e.from("items").select("*",{count:"exact"}).eq("project_id",t).is("deleted_at",null).or(`title.ilike.${ar(n)},data->>body.ilike.${ar(n)},data->>summary.ilike.${ar(n)}`).limit(i.limit);i.type&&(s=s.eq("type",i.type)),a&&(s=s.in("id",a));const{data:o,error:d,count:c}=await s;if(d)throw new Error(d.message);return{items:o??[],totalCount:c??0}}function _r(e,t){const r=new Set(e.map(e=>e.id)),i=new Set,n=[],a=(e,t,a)=>{if(e===t)return;if(!r.has(e)||!r.has(t))return;const s=`${e}|${t}|${a}`;i.has(s)||(i.add(s),n.push({from_id:e,to_id:t,type:a}))};for(const e of t)a(e.from_id,e.to_id,e.type);for(const t of e){const e=t.data?.relationships??{},r=t=>Array.isArray(e[t])?e[t].filter(e=>"string"==typeof e):[];for(const e of r("parents"))a(t.id,e,"parent");for(const e of r("children"))a(t.id,e,"child");for(const e of r("related"))a(t.id,e,"related");for(const e of r("verifies"))a(t.id,e,"verifies");for(const e of r("verified_by"))a(t.id,e,"verified_by");for(const e of r("mitigated_by"))a(t.id,e,"mitigated_by");for(const e of r("mitigates"))a(t.id,e,"mitigates")}return n}async function fr(e,t,r){const i="requirement"===r?"REQ":"test-case"===r?"TC":"note"===r?"NOTE":"TABLE",{data:n,error:a}=await e.rpc("next_item_id",{p_project_id:t,p_prefix:i});if(a)throw new Error(`ID generation failed: ${a.message}`);return n}var hr=U({"src/db/queries.ts"(){sr()}}),gr={};async function yr(){try{const e=await Fe(async()=>{const e=await $e();return await async function(e){const{data:t,error:r}=await e.from("projects").select("id, name, description, org_id, created_at, organizations(name)").is("deleted_at",null).order("created_at",{ascending:!1});if(r)throw new Error(r.message);return(t??[]).map(e=>({id:e.id,name:e.name,description:e.description,org_id:e.org_id,org_name:e.organizations?.name??"Unknown",created_at:e.created_at}))}(e)}),t=_t(),r=V,i=J().project_scope,n=i?new Set(i):null;return er(Ht(e.filter(e=>!r||e.org_id===r).filter(e=>!t||e.id===t).filter(e=>!n||n.has(e.id)).map(e=>({id:e.id,name:e.name,description:e.description,org_id:e.org_id,org_name:e.org_name,created_at:e.created_at}))))}catch(e){return tr(Qt(e))}}q(gr,{listProjectsHandler:()=>yr});var wr=U({"src/tools/list-projects.ts"(){Ge(),hr(),ht(),Q(),ir()}}),br={};async function vr(e){try{const t=await $e();let r,i,n=null;try{n=await cr(t,{baseline_id:e.baseline_id,as_of:e.as_of,project_id:e.project_id})}catch(e){return tr(Yt(e.message))}if(n){const a=(await pr(t,e.project_id,n)).filter(t=>{if(e.type&&t.type!==e.type)return!1;const r=t.data??{};if(e.domain){const t=r?.tags?.domains??[];if(!Array.isArray(t)||!t.includes(e.domain))return!1}return!e.level||r?.tags?.level===e.level});i=a.length,r=a.slice(e.offset,e.offset+e.limit).map(e=>({id:e.id,title:e.title,type:e.type,data:e.data}))}else{const n=await or(t,e.project_id,{type:e.type,domain:e.domain,level:e.level,limit:e.limit,offset:e.offset});r=n.items,i=n.totalCount}return er(Ht(r.map(e=>({id:e.id,title:e.title,type:e.type,status:e.data?.status,domains:e.data?.tags?.domains??[],level:e.data?.tags?.level})),Ct(i,e.limit,e.offset)))}catch(e){return e instanceof Error&&e.message.includes("Not authenticated")?tr(Pt()):tr(Yt(e instanceof Error?e.message:String(e)))}}q(br,{listItemsHandler:()=>vr});var jr,Sr=U({"src/tools/list-items.ts"(){Ge(),hr(),ir()}}),xr={};function kr(e,t){if(!t)return e;const r={...e};for(const e of jr){const i=t[e];null!=i&&(r[e]=i)}return r}async function Er(e){try{const r=await $e();let i=null;try{i=await cr(r,{baseline_id:e.baseline_id,as_of:e.as_of,project_id:e.project_id})}catch(e){return tr(Ut(e.message,["Pass baseline_id OR as_of, not both"]))}const n=i?await async function(e,t,r,i){const{data:n,error:a}=await e.rpc("item_state_at",{p_item_id:r,p_project_id:t,p_snapshot_at:i});if(a)throw new Error(`itemStateAt: ${a.message}`);return(n??[])[0]??null}(r,e.project_id,e.item_id,i).then(e=>e?mr(e):null):await dr(r,e.project_id,e.item_id);if(!n)return tr(qt("Item",e.item_id));const a={parents:[],children:[],related:[],verified_by:[],verifies:[],mitigated_by:[],mitigates:[]};if(i){const e=n.data?.relationships??{};for(const t of Object.keys(a)){const r=e[t];Array.isArray(r)&&(a[t]=r.slice())}}else{const t=await async function(e,t,r){const{data:i,error:n}=await e.from("item_relationships").select("from_id, to_id, type").eq("project_id",t).or(`from_id.eq.${r},to_id.eq.${r}`);if(n)throw new Error(n.message);return i??[]}(r,e.project_id,e.item_id),i={parent:"parents",child:"children",related:"related",verifies:"verifies",verified_by:"verified_by",mitigated_by:"mitigated_by",mitigates:"mitigates"},s={parent:"children",child:"parents",verified_by:"verifies",verifies:"verified_by",mitigated_by:"mitigates",mitigates:"mitigated_by",related:"related"},o=(e,t)=>{if(!(e in a))return;const r=a[e];r.includes(t)||r.push(t)},d=new Set([e.item_id]);for(const e of t)d.add(e.from_id),d.add(e.to_id);const c=n.data?.relationships??{};for(const e of Object.keys(a)){const t=c[e];if(Array.isArray(t))for(const e of t)"string"==typeof e&&d.add(e)}let l=new Set([e.item_id]);const m=Array.from(d);if(m.length>0){const{data:t,error:i}=await r.from("items").select("id").eq("project_id",e.project_id).is("deleted_at",null).in("id",m);if(i)throw new Error(i.message);l=new Set((t??[]).map(e=>e.id)),l.add(e.item_id)}const p=e=>l.has(e);for(const r of t)if(r.from_id===e.item_id){if(!p(r.to_id))continue;o(i[r.type]??r.type,r.to_id)}else{if(!p(r.from_id))continue;o(s[r.type]??r.type,r.from_id)}for(const t of Object.keys(a)){const r=c[t];if(Array.isArray(r))for(const i of r)"string"==typeof i&&i!==e.item_id&&p(i)&&o(t,i)}}let s=[];if("test-case"===n.type)if(i)s=(n.data?.runs??[]).map(e=>({base:{result:e.result??"not-run",run_by:e.by??e.run_by??null,run_at:e.date??e.run_at??"",note:e.note??null,..."string"==typeof e.asset?{asset:e.asset}:{}},canonical:e})).filter(e=>"string"==typeof e.base.run_at&&e.base.run_at.length>0).sort((e,t)=>String(t.base.run_at).localeCompare(String(e.base.run_at))).slice(0,20).map(e=>kr(e.base,e.canonical));else{const{data:t,error:i}=await r.from("test_results").select("result, run_by, run_at, note, asset").eq("item_id",e.item_id).eq("project_id",e.project_id).order("run_at",{ascending:!1}).limit(20);if(!i&&t){const e=new Map;for(const t of n.data?.runs??[])"string"==typeof t?.date&&e.set(t.date,t);s=t.map(t=>kr(t,e.get(String(t.run_at))))}}return er(Ht({id:n.id,type:n.type,title:n.title,summary:n.data?.summary,body:n.data?.body,tags:n.data?.tags??{domains:[]},ownership:n.data?.ownership??{primary:null,additional:[]},relationships:a,links:n.data?.links??[],fields:(t=n.data?.fields,!t||"object"!=typeof t||Array.isArray(t)?{}:Object.fromEntries(Object.entries(t).filter(e=>"string"==typeof e[1]))),status:n.data?.status,..."test-case"===n.type?{latest_result:s[0]?.result??"not-run",test_results:s}:{},metadata:{created_at:n.created_at,created_by:n.created_by,updated_at:n.updated_at,updated_by:n.updated_by,source_id:n.data?.metadata?.source_id}}))}catch(e){return e instanceof Error&&e.message.includes("Not authenticated")?tr(Pt()):tr(Yt(e instanceof Error?e.message:String(e)))}var t}q(xr,{getItemHandler:()=>Er});var Ir=U({"src/tools/get-item.ts"(){Ge(),hr(),ir(),jr=["point","release_id","build","environment","tooling","step_results"]}}),Ar={};async function Or(e){try{const t=await $e();let r,i,n=null;try{n=await cr(t,{baseline_id:e.baseline_id,project_id:e.project_id})}catch(e){return tr(Yt(e.message))}if(n){const a=await pr(t,e.project_id,n),s=e.query.trim().toLowerCase(),o=0===s.length?[]:a.filter(t=>{if(e.type&&t.type!==e.type)return!1;const r=t.data??{};return[t.title,r?.body,r?.summary].filter(e=>"string"==typeof e).map(e=>e.toLowerCase()).some(e=>e.includes(s))});i=o.length,r=o.slice(0,e.limit).map(e=>({id:e.id,title:e.title,type:e.type,data:e.data}))}else{const n=await ur(t,e.project_id,e.query,{type:e.type,limit:e.limit,asset:e.asset});r=n.items,i=n.totalCount}return er(Ht(r.map(e=>({id:e.id,title:e.title,type:e.type,status:e.data?.status,domains:e.data?.tags?.domains??[],level:e.data?.tags?.level})),Ct(i,e.limit,0)))}catch(e){return e instanceof Error&&e.message.includes("Not authenticated")?tr(Pt()):tr(Yt(e instanceof Error?e.message:String(e)))}}q(Ar,{searchHandler:()=>Or});var Rr=U({"src/tools/search.ts"(){Ge(),hr(),ir()}}),Dr={};async function Hr(e){try{const t=await $e(),{access_token:r}=await Re(),{items:i,totalCount:n}=await async function(e,t,r,i){const{ATOMS_SUPABASE_URL:n,ATOMS_SUPABASE_ANON_KEY:a}=await Promise.resolve().then(()=>(Ie(),ke)),s=await fetch(`${n}/functions/v1/embed`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${i.accessToken}`,apikey:a},body:JSON.stringify({input:r})});if(!s.ok)throw new Error(`Embed function failed: ${s.status}`);const{embedding:o}=await s.json(),{data:d,error:c}=await e.rpc("semantic_search",{query_embedding:o,p_project_id:t,match_threshold:.3,match_count:i.limit,p_type:i.type||null});if(c)throw new Error(c.message);return{items:d??[],totalCount:(d??[]).length,method:"semantic"}}(t,e.project_id,e.query,{type:e.type,limit:e.limit,accessToken:r});return er(Ht(i.map(e=>({id:e.id,title:e.title,type:e.type,status:e.data?.status,domains:e.data?.tags?.domains??[],level:e.data?.tags?.level})),Ct(n,e.limit,0)))}catch(e){return e instanceof Error&&e.message.includes("Not authenticated")?tr(Pt()):tr(Yt(e instanceof Error?e.message:String(e)))}}q(Dr,{semanticSearchHandler:()=>Hr});var Cr=U({"src/tools/semantic-search.ts"(){Ge(),Me(),hr(),ir()}}),Ur={};async function qr(e){try{if(e.baseline_id&&e.as_of)return tr(Mt("baseline_id and as_of are mutually exclusive"));const t=await $e();let r;if(e.baseline_id){const i=await lr(t,e.baseline_id);if(!i)return tr(Mt(`Baseline ${e.baseline_id} not found`));if(i.project_id!==e.project_id)return tr(Mt("Baseline does not belong to this project"));r=i.snapshot_at}else e.as_of&&(r=e.as_of);const{covered:i,uncovered:n,total:a}=await async function(e,t,r){let i,n,a;if(r.snapshotAt){const s=(await pr(e,t,r.snapshotAt)).map(mr);i=s.filter(e=>"requirement"===e.type),n=s.map(e=>({id:e.id,data:e.data})),a=[]}else{const{data:r,error:s}=await e.from("items").select("*").eq("project_id",t).eq("type","requirement").is("deleted_at",null);if(s)throw new Error(s.message);i=r??[];const{data:o,error:d}=await e.from("items").select("id, data").eq("project_id",t).is("deleted_at",null);if(d)throw new Error(d.message);n=o??[];const{data:c,error:l}=await e.from("item_relationships").select("from_id, to_id, type").eq("project_id",t);if(l)throw new Error(l.message);a=c??[]}const s=i.filter(e=>!(r.domain&&!(e.data?.tags?.domains??[]).includes(r.domain)||r.level&&e.data?.tags?.level!==r.level)),o=_r(n,a),d=new Set;for(const e of o)"verifies"===e.type?d.add(e.to_id):"verified_by"===e.type&&d.add(e.from_id);return{covered:s.filter(e=>d.has(e.id)),uncovered:s.filter(e=>!d.has(e.id)),total:s.length}}(t,e.project_id,{domain:e.domain,level:e.level,snapshotAt:r}),s=n.map(e=>({id:e.id,title:e.title,type:e.type,status:e.data?.status,domains:e.data?.tags?.domains??[],level:e.data?.tags?.level}));return er(Ht({covered:i.length,uncovered:n.length,total:a,coverage_percent:a>0?Math.round(i.length/a*1e3)/10:100,uncovered_items:s}))}catch(e){return e instanceof Error&&e.message.includes("Not authenticated")?tr(Pt()):tr(Yt(e instanceof Error?e.message:String(e)))}}q(Ur,{getCoverageHandler:()=>qr});var Tr,Mr=U({"src/tools/get-coverage.ts"(){Ge(),hr(),ir()}}),Pr={};async function $r(e){try{const t=await $e();let r;if(e.release_id){const i=await lr(t,e.release_id);if(!i)return tr(Mt(`Release ${e.release_id} not found`));if(i.project_id!==e.project_id)return tr(Mt("Release does not belong to this project"));if(null===i.release_version)return tr(Ut(`Baseline "${i.name}" is not a formal Release.`,["Promote the baseline to a Release first (Snapshots view in the app)","Use atoms_list_baselines to see which baselines carry a release_version"]));r=i}else{const{data:i,error:n}=await t.from("baselines").select("id, release_version, release_status").eq("project_id",e.project_id).eq("release_status","released").is("deleted_at",null).maybeSingle();if(n)return tr(Yt(n.message));if(!i)return tr(Tr);r=i}const{data:i,error:n}=await t.rpc("verification_readiness",{p_project_id:e.project_id,p_release_id:r.id});if(n)return tr(Qt(n));const{data:a,error:s}=await t.from("verification_status").select("requirement_id, state, stale, points_total, points_run").eq("project_id",e.project_id).eq("release_id",r.id);if(s)return tr(Yt(s.message));const o=(a??[]).slice().sort((e,t)=>e.requirement_id<t.requirement_id?-1:e.requirement_id>t.requirement_id?1:0),d={release:{id:r.id,version:r.release_version,status:r.release_status},readiness:i??{},requirements:o,...0===o.length?{hint:"projection empty; an admin may need to run rebuild_verification_from_runs for this project"}:{}};if(e.include_gaps){const{data:i,error:n}=await t.rpc("verification_notrun_points",{p_project_id:e.project_id,p_release_id:r.id});if(n)return tr(Qt(n));d.gaps=i??[]}return er(Ht(d))}catch(e){return tr(Qt(e))}}q(Pr,{verificationStatusHandler:()=>$r});var Nr=U({"src/tools/verification-status.ts"(){Ge(),hr(),ir(),Tr=Ut("This project has no current formal Release.",["Promote a locked baseline to a Release first (Snapshots view in the app)","Or pass release_id explicitly to query a specific (e.g. superseded) Release","Use atoms_list_baselines to see which baselines carry a release_version"])}}),Lr={};async function Fr(e){try{const t=await $e(),r=await async function(e,t,r,i){const{data:n,error:a}=await e.from("change_history").select("*").eq("item_id",r).eq("project_id",t).order("changed_at",{ascending:!1}).limit(i);if(a)throw new Error(a.message);return n??[]}(t,e.project_id,e.item_id,e.limit),i=r.map(e=>{const t=[],r=e.old_data,i=e.new_data;if(r&&i){const e=new Set([...Object.keys(r),...Object.keys(i)]);for(const n of e)JSON.stringify(r[n])!==JSON.stringify(i[n])&&t.push(n)}else i?t.push("(created)"):r&&t.push("(deleted)");return{id:e.id,event_type:e.event_type,changed_by:e.changed_by??null,changed_at:e.changed_at,actor:e.actor??"user",session_id:e.session_id??null,fields_changed:t}});return er(Ht(i,Ct(i.length,e.limit,0)))}catch(e){return e instanceof Error&&e.message.includes("Not authenticated")?tr(Pt()):tr(Yt(e instanceof Error?e.message:String(e)))}}q(Lr,{getHistoryHandler:()=>Fr});var Wr=U({"src/tools/get-history.ts"(){Ge(),hr(),ir()}});function Vr(e){return`item_${e.replace(/[^A-Za-z0-9_]/g,"_")||"node"}`}function Br(e){const t=Jr(`${e.id}: ${e.title}`);return"test-case"===e.type?`(["${t}"])`:"note"===e.type?`("${t}")`:`["${t}"]`}function zr(e){const t=Vr(e.from_id),r=Vr(e.to_id);return"child"===e.type?`${t} --\x3e ${r}`:"verified_by"===e.type?`${t} -.-> ${r}`:"related"===e.type?`${t} -.- ${r}`:null}function Jr(e){return e.replace(/"/g,"'").replace(/[[\]{}()]/g,"").substring(0,70)}var Qr=U({"src/core/mermaid.ts"(){}}),Gr={};async function Yr(e){try{if(e.baseline_id&&e.as_of)return tr(Mt("baseline_id and as_of are mutually exclusive"));const t=await $e();let r,i,n;if(e.baseline_id){const i=await lr(t,e.baseline_id);if(!i)return tr(Mt(`Baseline ${e.baseline_id} not found`));if(i.project_id!==e.project_id)return tr(Mt("Baseline does not belong to this project"));r=i.snapshot_at}else e.as_of&&(r=e.as_of);if(r){i=(await pr(t,e.project_id,r)).map(mr).map(e=>({id:e.id,title:e.title,type:e.type,data:e.data})),n=_r(i,[])}else{const{data:r,error:a}=await t.from("items").select("id, title, type, data").eq("project_id",e.project_id).is("deleted_at",null);if(a)throw new Error(a.message);i=r??[];const{data:s,error:o}=await t.from("item_relationships").select("from_id, to_id, type").eq("project_id",e.project_id);if(o)throw new Error(o.message);n=_r(i,s??[])}return er(Ht(function(e){const t=e.items,r=e.relationships??[],i=Math.max(0,e.depth);if(0===t.length)return{mermaid:`graph TD\n empty[${Jr(e.emptyLabel??"No items in project")}]`,node_count:0,edge_count:0};const n=new Map(t.map(e=>[e.id,e])),a=function(e,t,r,i){if(r)return[r];const n=new Set(t.filter(e=>"child"===e.type).map(e=>e.to_id));return e.filter(e=>!n.has(e.id)).filter(e=>i||"test-case"!==e.type).map(e=>e.id)}(t,r,e.rootItemId,e.includeTests),s=a.map(e=>({id:e,depth:0})),o=new Set,d=new Set,c=[],l=[];for(;s.length>0;){const{id:t,depth:a}=s.shift();if(o.has(t)||a>i)continue;const m=n.get(t);if(m&&(e.includeTests||"test-case"!==m.type)){o.add(t),c.push(` ${Vr(t)}${Br(m)}`);for(const c of r){if(c.from_id!==t)continue;const r=n.get(c.to_id);if(!r)continue;if(!e.includeTests&&"test-case"===r.type)continue;if(a+1>i)continue;const m=zr(c);if(!m)continue;const p=`${c.from_id}:${c.type}:${c.to_id}`;d.has(p)||(d.add(p),l.push(` ${m}`),o.has(c.to_id)||s.push({id:c.to_id,depth:a+1}))}}}return{mermaid:["graph TD",...c,...l].join("\n"),node_count:o.size,edge_count:d.size}}({items:i,relationships:n,rootItemId:e.root_item_id,depth:e.depth,includeTests:e.include_tests,emptyLabel:"No items in project"})))}catch(e){return e instanceof Error&&e.message.includes("Not authenticated")?tr(Pt()):tr(Yt(e instanceof Error?e.message:String(e)))}}q(Gr,{exportMermaidHandler:()=>Yr});var Kr=U({"src/tools/export-mermaid.ts"(){Ge(),hr(),ir(),Qr()}}),Zr={};function Xr(e){const t=Dt(e??"");return"sub-system"===t?"subsystem":t}async function ei(e,t){const{data:r}=await e.from("metadata").select("domains, levels").eq("project_id",t).maybeSingle();return{domains:r?.domains??[],levels:r?.levels??[]}}async function ti(e,t,r,i){const{error:n}=await e.from("metadata").upsert({project_id:t,domains:r,levels:i},{onConflict:"project_id"});if(n)throw new Error(n.message)}async function ri(e,t,r){const i=Dt(r??""),n=Xr(i);if(!i)return!1;try{const{domains:r,levels:a}=await ei(e,t);return!a.some(e=>Xr(e)===n)&&(await ti(e,t,r,[...a,i]),!0)}catch{return!1}}async function ii(e){try{const t=await $e(),{levels:r}=await ei(t,e.project_id),{data:i}=await t.from("items").select("data").eq("project_id",e.project_id).is("deleted_at",null),n={};for(const e of i??[]){const t=e.data?.tags,r=Xr(t?.level);r&&(n[r]=(n[r]??0)+1)}return er(Ht({levels:r.map((e,t)=>({name:e,prefix:`L${t}`,item_count:n[Xr(e)]??0})),total:r.length,note:"L# is the level's position in this list (0-based). Names are user-defined."}))}catch(e){return e instanceof Error&&e.message.includes("Not authenticated")?tr(Pt()):tr(Yt(e instanceof Error?e.message:String(e)))}}async function ni(e){try{const t=await $e();try{await Be(t,e.project_id)}catch(e){if(e instanceof Error&&"VIEWER_ROLE"===e.message)return tr(Tt("Viewer"));throw e}const{domains:r,levels:i}=await ei(t,e.project_id),n=Dt(e.name),a=Xr(n);if(!n)return tr(Mt("Level name cannot be empty."));if(i.some(e=>Xr(e)===a))return tr(Mt(`Level "${n}" already exists in this project.`));const s=i.length;return await ti(t,e.project_id,r,[...i,n]),er(Ht({level:{name:n,prefix:`L${s}`},message:`Level "${n}" registered as L${s}. Tag items with level="${n}".`}))}catch(e){return e instanceof Error&&e.message.includes("Not authenticated")?tr(Pt()):tr(Yt(e instanceof Error?e.message:String(e)))}}q(Zr,{createLevelHandler:()=>ni,ensureLevelRegistered:()=>ri,listLevelsHandler:()=>ii});var ai,si,oi,di,ci,li,mi,pi,ui,_i,fi,hi=U({"src/tools/levels.ts"(){Ge(),ir()}});function gi(e){return(e??"").trim().toLowerCase()}function yi(e,t){const r=gi(e);if(!r)return null;for(const e of t)if(gi(e)===r)return e;return null}function wi(e){const t=e?.severity||"",r=e?.exposure||"",i=e?.controllability||"";return t&&r&&i?"S0"===t||"E0"===r||"C0"===i?"QM":oi[`${t}-${r}-${i}`]||ai:ai}function bi(e){const t=parseInt(e?.severity||"",10),r=parseInt(e?.occurrence||"",10),i=parseInt(e?.detection||"",10);return Number.isNaN(t)||Number.isNaN(r)||Number.isNaN(i)?ai:String(t*r*i)}function vi(e,t){return"hara"===e?wi(t):bi(t)}function ji(e,t,r){if(r)return!1;if("hara"===e){const e=wi(t);return"C"===e||"D"===e}const i=parseInt(bi(t),10);return!Number.isNaN(i)&&i>=200}function Si(e,t,r){if(!e?.approved||!e.approved_at)return!1;const i=Date.parse(e.approved_at);return!Number.isNaN(i)&&[...e.source_req?[e.source_req]:[],...t].some(e=>{const t=r(e),n=t?Date.parse(t):NaN;return!Number.isNaN(n)&&n>i})}function xi(e){return Array.isArray(e)?e.filter(e=>"string"==typeof e):[]}function ki(e,t,r=gi){const i=new Set(t.map(r).filter(e=>e.length>0));return e.filter(e=>i.has(r(e)))}function Ei(e){const t=e.length>1;return`Function-flagged tag${t?"s":""} used as domain${t?"s":""}: ${e.join(", ")}. A function is not a domain tag: seed a HARA row with it instead (atoms_create_analysis source_functions, or atoms_fill_row source_function), or unflag it in Taxonomy Management.`}var Ii,Ai,Oi=U({"src/core/analysis.ts"(){ai="—",si=["prevention","detection","mitigation"],oi={"S1-E1-C1":"QM","S1-E1-C2":"QM","S1-E1-C3":"QM","S1-E2-C1":"QM","S1-E2-C2":"QM","S1-E2-C3":"QM","S1-E3-C1":"QM","S1-E3-C2":"QM","S1-E3-C3":"A","S1-E4-C1":"QM","S1-E4-C2":"A","S1-E4-C3":"B","S2-E1-C1":"QM","S2-E1-C2":"QM","S2-E1-C3":"QM","S2-E2-C1":"QM","S2-E2-C2":"QM","S2-E2-C3":"A","S2-E3-C1":"QM","S2-E3-C2":"A","S2-E3-C3":"B","S2-E4-C1":"A","S2-E4-C2":"B","S2-E4-C3":"C","S3-E1-C1":"QM","S3-E1-C2":"QM","S3-E1-C3":"A","S3-E2-C1":"QM","S3-E2-C2":"A","S3-E2-C3":"B","S3-E3-C1":"A","S3-E3-C2":"B","S3-E3-C3":"C","S3-E4-C1":"B","S3-E4-C2":"C","S3-E4-C3":"D"},ci={hara:{severity:["S0","S1","S2","S3"],exposure:["E0","E1","E2","E3","E4"],controllability:["C0","C1","C2","C3"]},dfmea:{severity:di=["1","2","3","4","5","6","7","8","9","10"],occurrence:di,detection:di}},li={hara:["failure_mode","operating_scenario","hazard","assumptions","severity","exposure","controllability","safe_state","ftti","rationale","source_function"],dfmea:["failure_mode","cause","severity","occurrence","detection","rationale"]},mi={hara:"safety-goal",dfmea:"failure-mode"},pi={hara:"SG",dfmea:"FM"},ui={hara:"HARA",dfmea:"DFMEA"},_i={hara:"hara_iso26262",dfmea:"dfmea_rpn"},fi="ISO 26262"}}),Ri=U({"src/core/project-field-counts.ts"(){}}),Di={};async function Hi(e,t){const{data:r,error:i}=await e.from("metadata").select("fields").eq("project_id",t).maybeSingle();if(i)throw i;return Ci(r?.fields)}function Ci(e){if(!Array.isArray(e))return[];const t=[],r=new Set;for(const i of e){if(!i||"object"!=typeof i)continue;const e=i,n="string"==typeof e.name?e.name.trim():"";if(!n)continue;const a=n.toLowerCase();if(r.has(a))continue;r.add(a);const s=void 0===e.type?"text":"string"==typeof e.type&&Ai.has(e.type)?e.type:"text",o=Array.isArray(e.options)?Fi(e.options):[];t.push({name:n,type:s,..."select"===s?{options:o}:{}})}return t}function Ui(e,t){const r=e.name.trim();if(!r)return"Field name cannot be empty.";if(r.length>60)return"Field name must be 60 characters or fewer.";if(!Ai.has(e.type))return"Field type is not supported.";const i=t.find(e=>e.name.toLowerCase()===r.toLowerCase());if(i)return`Field "${i.name}" already exists.`;if("select"===e.type){const t=Fi(e.options??[]);if(0===t.length)return"Select fields need at least one option.";if(t.length>100)return"Select fields support at most 100 options.";if(t.some(e=>e.length>120))return"Select options must be 120 characters or fewer."}return null}function qi(e,t){const r=new Set(e.map(e=>e.name.toLowerCase()));return Object.keys(t).filter(e=>!r.has(e.toLowerCase()))}function Ti(e,t){const r=new Map(e.map(e=>[e.name.toLowerCase(),e.name])),i={};for(const[e,n]of Object.entries(t))i[r.get(e.toLowerCase())??e]=n;return i}function Mi(e,t,r=new Set){const i=qi(e,t);if(i.length>0)return{error:`Unknown field${i.length>1?"s":""}: ${i.join(", ")}. Defined: ${e.length>0?e.map(e=>e.name).join(", "):"(none)"}. Define fields first with atoms_create_field.`};const n={},a=new Map(e.map(e=>[e.name.toLowerCase(),e]));for(const[e,i]of Object.entries(t)){const t=a.get(e.toLowerCase());if(!t)continue;const s=Pi(t,i,r);if(s.error)return{error:s.error};s.value&&(n[t.name]=s.value)}return{fields:n}}function Pi(e,t,r){const i=t.trim();if(!i)return{value:""};if(i.length>1e4)return{error:`${e.name} is too long.`};switch(e.type){case"text":case"long":return{value:i};case"bool":{const t=i.toLowerCase();return"yes"===t?{value:"yes"}:"no"===t?{value:"no"}:{error:`${e.name} must be yes or no.`}}case"date":return function(e){if(!/^\d{4}-\d{2}-\d{2}$/.test(e))return!1;const t=new Date(`${e}T00:00:00.000Z`);return!Number.isNaN(t.getTime())&&t.toISOString().slice(0,10)===e}(i)?{value:i}:{error:`${e.name} must be an ISO date.`};case"person":return Li(i)&&r.has(i)?{value:i}:{error:`${e.name} must be an active project member ID.`};case"select":return(e.options??[]).includes(i)?{value:i}:{error:`${e.name} must be one of: ${(e.options??[]).join(", ")}.`};case"number":return Number.isFinite(Number(i))?{value:i}:{error:`${e.name} must be numeric.`};case"link":{const t=function(e){if(!e)return{label:"",url:""};try{const t=JSON.parse(e);if(t&&"object"==typeof t&&"string"==typeof t.url){const e=t.label;return{label:"string"==typeof e?e:"",url:t.url}}}catch{}return{label:"",url:e}}(i);return function(e){try{const t=new URL(e);return"http:"===t.protocol||"https:"===t.protocol}catch{return!1}}(t.url)?{value:$i(t.label,t.url)}:{error:`${e.name} must be an http(s) URL.`}}}}function $i(e,t){const r=t.trim();return r?JSON.stringify({label:e.trim(),url:r}):""}async function Ni(e,t,r,i){const n=new Map(r.map(e=>[e.name.toLowerCase(),e])),a=new Map;for(const[e,t]of Object.entries(i)){const r=n.get(e.toLowerCase());"person"===r?.type&&t.trim()&&a.set(t.trim(),r)}const s=new Set;for(const[r,i]of a){if(!Li(r))return{error:`${i.name} must be an active project member ID.`};const{data:n,error:a}=await e.rpc("is_active_project_member_for_field",{p_project_id:t,p_person_user_id:r,p_actor_user_id:We()});if(a)return{error:a.message};!0===n&&s.add(r)}return Mi(r,i,s)}function Li(e){return/^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(e)}function Fi(e){const t=[],r=new Set;for(const i of e){if("string"!=typeof i)continue;const e=i.trim();if(!e)continue;const n=e.toLowerCase();r.has(n)||(r.add(n),t.push(e))}return t}async function Wi(e){try{const t=await $e(),r=await Hi(t,e.project_id),i=await async function(e,t,r,i=500){const n=Object.fromEntries(r.map(e=>[e.name,0])),a=new Map(r.map(e=>[e.name.toLowerCase(),e.name]));let s=0;for(;;){const{data:r,error:o}=await e.from("items").select("data").eq("project_id",t).is("deleted_at",null).order("id",{ascending:!0}).range(s,s+i-1);if(o)throw o;const d=r??[];for(const e of d){const t=e.data?.fields;if(t&&"object"==typeof t&&!Array.isArray(t))for(const[e,r]of Object.entries(t)){const t=a.get(e.toLowerCase());t&&"string"==typeof r&&r.length>0&&(n[t]+=1)}}if(d.length<i)return n;s+=d.length}}(t,e.project_id,r);return er(Ht({fields:r.map(e=>({name:e.name,type:e.type,options:e.options??[],item_count:i[e.name]??0})),total:r.length}))}catch(e){return e instanceof Error&&e.message.includes("Not authenticated")?tr(Pt()):tr(Qt(e))}}async function Vi(e){try{const t=await $e();try{await ze(t,e.project_id)}catch(e){if(e instanceof Error&&"PROJECT_FIELD_ADMIN_OR_OWNER_REQUIRED"===e.message)return tr(Tt("Project admin or owner"));throw e}const{data:r}=await t.from("metadata").select("fields").eq("project_id",e.project_id).maybeSingle(),i=Ci(r?.fields),n={name:e.name.trim(),type:e.type??"text",..."select"===e.type?{options:Fi(e.options??[])}:{}},a=Ui(n,i);if(a)return tr(Mt(a));const{data:s,error:o}="organization"===e.scope?await t.rpc("create_org_project_field_definition",{p_project_id:e.project_id,p_field:n,p_actor_user_id:We()}):await t.rpc("mutate_project_field_definition",{p_project_id:e.project_id,p_operation:"create",p_field:n,p_field_name:null,p_actor_user_id:We()});if(o)throw o;return er(Ht({field:s?.field??n,..."organization"===e.scope?{created_projects:s?.created_projects??[],skipped_projects:s?.skipped_projects??[]}:{},message:`Field "${n.name}" defined. Set values with atoms_update_item fields={"${n.name}": "<value>"}.`}))}catch(e){return e instanceof Error&&e.message.includes("Not authenticated")?tr(Pt()):tr(Qt(e))}}async function Bi(e){try{const t=await $e();try{await ze(t,e.project_id)}catch(e){if(e instanceof Error&&"PROJECT_FIELD_ADMIN_OR_OWNER_REQUIRED"===e.message)return tr(Tt("Project admin or owner"));throw e}const r=e.name.trim(),{data:i,error:n}=await t.rpc("mutate_project_field_definition",{p_project_id:e.project_id,p_operation:"delete",p_field:null,p_field_name:r,p_actor_user_id:We()});if(n)throw n;return er(Ht({deleted:i?.deleted??r}))}catch(e){return e instanceof Error&&e.message.includes("Not authenticated")?tr(Pt()):tr(Qt(e))}}async function zi(e){try{const t=await $e();try{await ze(t,e.project_id)}catch(e){if(e instanceof Error&&"PROJECT_FIELD_ADMIN_OR_OWNER_REQUIRED"===e.message)return tr(Tt("Project admin or owner"));throw e}const r=Array.isArray(e.names)?e.names.map(e=>String(e??"").trim()).filter(e=>e.length>0):[];if(0===r.length)return tr(Mt("names must be a non-empty array of existing field names, in the order you want them"));const i=await Hi(t,e.project_id),n=i.map(e=>e.name.toLowerCase()).sort(),a=r.map(e=>e.toLowerCase()).sort();if(n.length!==a.length||n.some((e,t)=>e!==a[t]))return tr(Mt(`names must list every existing field exactly once. Defined: ${i.length>0?i.map(e=>e.name).join(", "):"(none)"}`));const{data:s,error:o}=await t.rpc("mutate_project_field_definition",{p_project_id:e.project_id,p_operation:"reorder",p_field:r,p_field_name:null,p_actor_user_id:We()});if(o)throw o;return er(Ht({fields:Ci(s?.fields).map(e=>e.name)}))}catch(e){return e instanceof Error&&e.message.includes("Not authenticated")?tr(Pt()):tr(Qt(e))}}q(Di,{PROJECT_FIELD_TYPES:()=>Ii,canonicalizeFieldKeys:()=>Ti,createFieldHandler:()=>Vi,deleteFieldHandler:()=>Bi,findUnknownFieldNames:()=>qi,getProjectFields:()=>Hi,listFieldsHandler:()=>Wi,normalizeProjectFields:()=>Ci,reorderFieldsHandler:()=>zi,validateAndCanonicalizeFieldValues:()=>Mi,validateProjectFieldDefinition:()=>Ui,validateProjectFieldValuesForWrite:()=>Ni});var Ji=U({"src/tools/fields.ts"(){Ge(),ir(),Ri(),Ii=["text","long","bool","date","person","select","number","link"],Ai=new Set(Ii)}}),Qi={};async function Gi(e){try{const t=await $e(),r=We();try{await Be(t,e.project_id)}catch(e){if(e instanceof Error&&"VIEWER_ROLE"===e.message)return tr(Tt("Viewer"));throw e}const i=(e.domains??[]).map(Dt).filter(e=>e.length>0),n=void 0!==e.level?Dt(e.level):void 0;if(i.length>0){const{data:r,error:n}=await t.from("metadata").select("domains, function_tags").eq("project_id",e.project_id).maybeSingle();if(n)throw new Error(`metadata read failed: ${n.message}`);const a=r?.domains??[],s=ki(i,xi(r?.function_tags),Dt);if(s.length>0)return tr(Mt(Ei(s)));if(a.length>0){const e=new Set(a.map(Dt).filter(e=>e.length>0)),t=i.filter(t=>!e.has(t));if(t.length>0)return tr(Mt(`Unknown domain${t.length>1?"s":""}: ${t.join(", ")}. Registered: ${a.join(", ")}. Add new domains via Taxonomy Management in the ATOMS web app first.`))}}let a;if(e.fields&&Object.keys(e.fields).length>0){const{getProjectFields:r,validateProjectFieldValuesForWrite:i}=await Promise.resolve().then(()=>(Ji(),Di)),n=await r(t,e.project_id),s=await i(t,e.project_id,n,e.fields);if(s.error)return tr(Mt(s.error));a=s.fields}const s=await fr(t,e.project_id,e.type),o=(new Date).toISOString(),d={id:s,type:e.type,title:e.title,summary:e.summary,body:e.body,tags:{domains:i,level:n??""},ownership:{primary:null,additional:[]},relationships:{parents:e.parent_ids??[],children:[],related:[],verifies:[],verified_by:[]},links:[],...a?{fields:a}:{},metadata:{created_at:o,created_by:r,updated_at:o,updated_by:r}},{error:c}=await t.rpc("save_item_with_revision",{p_item_id:s,p_project_id:e.project_id,p_type:e.type,p_title:e.title,p_data:d,p_user_id:r,p_event_source:"mcp",p_event_session_id:ds()??null,p_diff_summary:null});if(c)throw new Error(c.message);if(n&&await ri(t,e.project_id,n),e.parent_ids&&e.parent_ids.length>0)for(const i of e.parent_ids){const{error:n}=await t.rpc("apply_link_change",{p_project_id:e.project_id,p_from_id:s,p_to_id:i,p_type:"parent",p_action:"add",p_user_id:r,p_event_source:"mcp"});n&&process.stderr.write(`[atoms-mcp] Warning: parent link sync failed for ${i}: ${n.message}\n`)}return await t.from("change_history").insert({item_id:s,project_id:e.project_id,changed_by:r,event_type:"created",old_data:null,new_data:d,actor:cs(),session_id:ds()}).then(({error:e})=>{e&&process.stderr.write(`[atoms-mcp] Warning: change_history log failed: ${e.message}\n`)}),!1===e.echo?er(Ht({id:s,type:e.type})):er(Ht({id:s,type:e.type,title:e.title,summary:e.summary??"",body:e.body??"",domains:i,level:n??"",project_id:e.project_id}))}catch(e){return tr(Qt(e))}}q(Qi,{createItemHandler:()=>Gi});var Yi=U({"src/tools/create-item.ts"(){Ge(),hr(),ms(),ir(),hi(),Oi()}});function Ki(e,t){const r=function(e){return`SG-${e}`}(t);return{id:e,outcome:"captured_as_change",change:{id:r,sg_number:t,status:"open"},message:`Captured as change ${r}, awaiting approval. This requirement stays at its approved revision until a human approver approves the change; no MCP tool can approve, dismiss, or apply it.`}}function Zi(e){return{id:e,outcome:"under_review",message:"This requirement is under change control. Your edit was submitted; if it changed the requirement's meaning it is now a change awaiting a human approver, otherwise it wrote through live. Reload the item to confirm. No MCP tool can approve, dismiss, or apply a change."}}var Xi=U({"src/core/change-control.ts"(){}}),en={};async function tn(e){try{const t=await $e(),r=We();try{await Be(t,e.project_id)}catch(e){if(e instanceof Error&&"VIEWER_ROLE"===e.message)return tr(Tt("Viewer"));throw e}const i=async(i,n,a,s)=>{const o=await dr(t,e.project_id,i);if(!o)return{updated_fields:[],error:`Item "${i}" not found`};const d="approved"===o.approval_state,c=void 0!==n.domains?n.domains.map(Dt).filter(e=>e.length>0):void 0,l=void 0!==n.level?Dt(n.level):void 0;if(void 0!==c&&c.length>0){const{data:r,error:i}=await t.from("metadata").select("domains, function_tags").eq("project_id",e.project_id).maybeSingle();if(i)throw new Error(`metadata read failed: ${i.message}`);const n=r?.domains??[],a=ki(c,xi(r?.function_tags),Dt);if(a.length>0)return{updated_fields:[],error:Ei(a)};if(n.length>0){const e=new Set(n.map(Dt).filter(e=>e.length>0)),t=c.filter(t=>!e.has(t));if(t.length>0)return{updated_fields:[],error:`Unknown domains: ${t.join(", ")}`}}}const m={...o.data},p={...o.data},u=[],_=(e,t)=>JSON.stringify(e)!==JSON.stringify(t);if(void 0!==n.title&&_(o.data.title,n.title)&&(p.title=n.title,u.push("title")),void 0!==n.body&&_(o.data.body,n.body)&&(p.body=n.body,u.push("body")),void 0!==n.summary&&_(o.data.summary,n.summary)&&(p.summary=n.summary,u.push("summary")),void 0!==c&&_(o.data.tags?.domains??[],c)&&(p.tags={...p.tags,domains:c},u.push("domains")),void 0!==l&&_(o.data.tags?.level,l)&&(p.tags={...p.tags,level:l},u.push("level")),void 0!==n.status&&_(o.data.status,n.status)&&(p.status=n.status,u.push("status")),void 0!==n.steps&&_(o.data.steps,n.steps)&&(p.steps=n.steps,u.push("steps")),void 0!==n.params&&_(o.data.params,n.params)&&(p.params=n.params,u.push("params")),void 0!==n.fields&&Object.keys(n.fields).length>0){const{getProjectFields:r,validateProjectFieldValuesForWrite:i,canonicalizeFieldKeys:a}=await Promise.resolve().then(()=>(Ji(),Di)),s=await r(t,e.project_id),d=await i(t,e.project_id,s,n.fields);if(d.error)return{updated_fields:[],error:d.error};const c=a(s,o.data.fields??{});for(const[e,t]of Object.entries({...a(s,n.fields),...d.fields??{}}))""===t?delete c[e]:c[e]=t;_(o.data.fields??{},c)&&(p.fields=c,u.push("fields"))}if(0===u.length)return{updated_fields:[],updatedData:o.data,itemType:o.type};p.metadata={...p.metadata,updated_at:(new Date).toISOString(),updated_by:r};const f=n.title??o.title,{data:h,error:g}=await t.rpc("save_item_with_revision",{p_item_id:i,p_project_id:e.project_id,p_type:o.type,p_title:f,p_data:p,p_user_id:r,p_event_source:"mcp",p_event_session_id:ds()??null,p_diff_summary:null,p_expected_updated_at:n.expected_updated_at??o.updated_at});if(g)return{updated_fields:[],error:g};if(!h)return{updated_fields:[],error:`Update silently failed for "${i}" — RPC returned no row. Likely RLS policy denied the write; verify your role and project access.`};u.includes("level")&&l&&await ri(t,e.project_id,l);const y={item_id:i,project_id:e.project_id,changed_by:r,event_type:"updated",old_data:m,new_data:p,actor:cs(),session_id:ds()};if(a&&(y.batch_id=a),s&&(y.reason=s),await t.from("change_history").insert(y).then(({error:e})=>{e&&process.stderr.write(`[atoms-mcp] Warning: change_history log failed: ${e.message}\n`)}),d&&function(e){return e.some(e=>"title"===e||"body"===e||"type"===e)}(u)){const r=await async function(e,t,r){const{data:i,error:n}=await e.from("item_suggestions").select("sg_number").eq("project_id",t).eq("item_id",r).eq("status","open").maybeSingle();return n||!i?null:"number"==typeof i.sg_number?i.sg_number:null}(t,e.project_id,i);return null!==r?{updated_fields:u,updatedData:p,itemType:o.type,capturedSg:r}:{updated_fields:u,updatedData:p,itemType:o.type,capturedUnknown:!0}}return{updated_fields:u,updatedData:p,itemType:o.type}};if(e.updates&&Array.isArray(e.updates)){const t=crypto.randomUUID(),r=!0===e.continue_on_error,n=[],a=[];for(const s of e.updates){const{updated_fields:o,capturedSg:d,capturedUnknown:c,error:l}=await i(s.item_id,s,t,e.reason);if(l){if(Gt(l))return tr(Qt(l));if(!r){const e=l instanceof Error?l.message:String("object"==typeof l&&l&&"message"in l?l.message??"":l);return tr(Mt(`Batch stopped at "${s.item_id}": ${e}. ${n.length} item(s) already updated.`))}a.push({item_id:s.item_id,error:String(l)})}else void 0!==d?n.push(Ki(s.item_id,d)):c?n.push(Zi(s.item_id)):n.push({id:s.item_id,updated_fields:o})}return er(Ht({batch_id:t,updated:n.length,results:!1===e.echo?void 0:n,errors:a}))}if(!e.item_id)return tr(Mt("Provide either item_id (single) or updates[] (array form)."));const{updated_fields:n,updatedData:a,itemType:s,capturedSg:o,capturedUnknown:d,error:c}=await i(e.item_id,{title:e.title,body:e.body,summary:e.summary,domains:e.domains,level:e.level,status:e.status,fields:e.fields,steps:e.steps,params:e.params,expected_updated_at:e.expected_updated_at},void 0,e.reason);if(c){const t=c instanceof Error?c.message:String("object"==typeof c&&c&&"message"in c?c.message??"":c);return t.includes("not found")?tr(qt("Item",e.item_id)):t.includes("Unknown domain")||t.includes("Unknown field")||t.includes("Function-flagged tag")?tr(Mt(t)):Wt(t)||Gt(c)?tr(Qt(c)):tr(Yt(t))}if(void 0!==o)return er(Ht(Ki(e.item_id,o)));if(d)return er(Ht(Zi(e.item_id)));if(!1===e.echo)return er(Ht({id:e.item_id,updated_fields:n}));const l=a??{};return er(Ht({id:e.item_id,type:s,title:l.title??"",summary:l.summary??"",body:l.body??"",domains:l.tags?.domains??[],level:l.tags?.level??"",updated_fields:n}))}catch(e){return tr(Qt(e))}}q(en,{updateItemHandler:()=>tn});var rn,nn,an=U({"src/tools/update-item.ts"(){Ge(),hr(),ms(),ir(),hi(),Xi(),Oi()}});function sn(e){const t=process.env.ATOMS_MCP_REQUIRE_CONFIRMATION?.trim();if(t){const r=t.toLowerCase();if("1"===r||"true"===r||"all"===r)return!0;if(t.split(",").map(e=>e.trim()).filter(Boolean).includes(e))return!0}return!!J().require_confirmation_for.includes(e)}function on(e,t=rn){const r=Date.now()+t,i={...e,exp:r},n=Buffer.from(JSON.stringify(i)).toString("base64url");return{token:`${n}.${p.createHmac("sha256",nn).update(n).digest("base64url")}`,expires_at:new Date(r).toISOString(),expires_in_seconds:Math.round(t/1e3)}}function dn(e,t){if("string"!=typeof e||!e.includes("."))return{valid:!1,reason:"Token format invalid"};const[r,i]=e.split(".");if(!r||!i)return{valid:!1,reason:"Token format invalid"};const n=p.createHmac("sha256",nn).update(r).digest("base64url"),a=Buffer.from(i,"utf8"),s=Buffer.from(n,"utf8");if(a.length!==s.length||!p.timingSafeEqual(a,s))return{valid:!1,reason:"Token signature invalid"};let o;try{o=JSON.parse(Buffer.from(r,"base64url").toString("utf8"))}catch{return{valid:!1,reason:"Token payload malformed"}}return"number"!=typeof o.exp||o.exp<Date.now()?{valid:!1,reason:"Token expired"}:o.tool!==t.tool?{valid:!1,reason:"Token issued for a different tool"}:o.project_id!==t.project_id?{valid:!1,reason:"Token issued for a different project"}:o.target!==t.target?{valid:!1,reason:"Token issued for a different target"}:{valid:!0}}function cn(e){if(Array.isArray(e))return e.map(cn);if(null!==e&&"object"==typeof e){const t={};for(const r of Object.keys(e).sort())t[r]=cn(e[r]);return t}return e}function ln(e){const t=JSON.stringify(cn(e));return p.createHash("sha256").update(t).digest("base64url").slice(0,16)}var mn,pn=U({"src/middleware/confirmation.ts"(){Q(),rn=6e4,nn=(()=>{const e=process.env.ATOMS_MCP_CONFIRMATION_SECRET;return e&&e.length>=16?e:p.randomBytes(32).toString("base64url")})()}}),un={};async function _n(e){try{const t=await $e(),r=We();try{await Be(t,e.project_id)}catch(e){if(e instanceof Error&&"VIEWER_ROLE"===e.message)return tr(Tt("Viewer"));throw e}const i=await async function(e,t,r){const{data:i,error:n}=await e.from("items").select("*").eq("id",r).eq("project_id",t).maybeSingle();if(n)throw new Error(n.message);return i}(t,e.project_id,e.item_id);if(!i)return tr(qt("Item",e.item_id));if(i.deleted_at)return tr(Mt(`Item ${e.item_id} is already deleted`));if(sn(mn)){const t={tool:mn,project_id:e.project_id,target:e.item_id};if(!e.confirmation_token){const r=on(t);return er(Ht({status:"confirmation_required",preview:{id:e.item_id,title:i.title,type:i.type,would_soft_delete:!0},confirmation_token:r.token,expires_at:r.expires_at,expires_in_seconds:r.expires_in_seconds,message:`Re-call atoms_delete_item with the same project_id, item_id, and confirmation_token within ${r.expires_in_seconds}s to execute.`}))}const r=dn(e.confirmation_token,t);if(!r.valid)return tr(Mt(`confirmation_token rejected: ${r.reason}`))}const{error:n}=await t.rpc("soft_delete_item_with_revision",{p_item_id:e.item_id,p_project_id:e.project_id,p_user_id:r,p_expected_updated_at:i.updated_at,p_event_source:"mcp",p_actor:cs(),p_session_id:ds()});if(n)throw new Error(n.message);return er(Ht({id:e.item_id,title:i.title,type:i.type,deleted:!0,message:`Item ${e.item_id} soft-deleted. It can still be found in audit logs.`}))}catch(e){return tr(Qt(e))}}q(un,{deleteItemHandler:()=>_n});var fn=U({"src/tools/delete-item.ts"(){Ge(),hr(),ms(),pn(),ir(),mn="atoms_delete_item"}}),hn={};async function gn(e){try{const t=await $e(),r=We();try{await Be(t,e.project_id)}catch(e){if(e instanceof Error&&"VIEWER_ROLE"===e.message)return tr(Tt("Viewer"));throw e}const{data:i,error:n}=await t.from("items").select("*").eq("id",e.item_id).eq("project_id",e.project_id).maybeSingle();if(n)throw new Error(n.message);if(!i)return tr(qt("Item",e.item_id));if(!i.deleted_at)return tr(Ut(`Item ${e.item_id} is not deleted and cannot be restored.`,["Use atoms_get_item to view the current item state","Use atoms_delete_item first if you intended to delete it"]));const{error:a}=await t.rpc("save_item_with_revision",{p_item_id:e.item_id,p_project_id:e.project_id,p_type:i.type,p_title:i.title,p_data:i.data,p_user_id:r,p_event_source:"restore",p_event_session_id:ds()??null,p_diff_summary:{changed_fields:["deleted_at"],restored:!0},p_expected_updated_at:i.updated_at});if(a)throw new Error(a.message);const{error:s}=await t.rpc("repair_live_item_relationships_from_jsonb",{p_project_id:e.project_id,p_item_id:e.item_id});if(s)throw new Error(`Relationship repair failed: ${s.message}`);return await t.from("change_history").insert({item_id:e.item_id,project_id:e.project_id,changed_by:r,event_type:"restored",old_data:null,new_data:i.data,actor:cs(),session_id:ds()}).then(({error:e})=>{e&&process.stderr.write(`[atoms-mcp] Warning: change_history log failed: ${e.message}\n`)}),er(Ht({id:e.item_id,title:i.title,type:i.type,restored:!0,message:`Item ${e.item_id} restored successfully.`}))}catch(e){return tr(Qt(e))}}q(hn,{restoreItemHandler:()=>gn});var yn=U({"src/tools/restore-item.ts"(){Ge(),ms(),ir()}}),wn={};async function bn(e){try{const t=await $e(),r=We();try{await Be(t,e.project_id)}catch(e){if(e instanceof Error&&"VIEWER_ROLE"===e.message)return tr(Tt("Viewer"));throw e}const i=async(i,n,a)=>{const s=await dr(t,e.project_id,i.from_id);if(!s)throw new Error(`Item "${i.from_id}" not found`);const o=await dr(t,e.project_id,i.to_id);if(!o)throw new Error(`Item "${i.to_id}" not found`);const{error:d}=await t.rpc("apply_link_change",{p_project_id:e.project_id,p_from_id:i.from_id,p_to_id:i.to_id,p_type:i.type,p_action:i.action,p_user_id:r,p_event_source:"mcp",p_expected_from_updated_at:i.expected_from_updated_at??s.updated_at,p_expected_to_updated_at:i.expected_to_updated_at??o.updated_at});if(d)throw new Error(d.message);const c={item_id:i.from_id,project_id:e.project_id,changed_by:r,event_type:"updated",old_data:null,new_data:{relationship_change:{action:i.action,type:i.type,from:i.from_id,to:i.to_id}},actor:cs(),session_id:ds()};n&&(c.batch_id=n),a&&(c.reason=a),await t.from("change_history").insert(c).then(({error:e})=>{e&&process.stderr.write(`[atoms-mcp] Warning: change_history log failed: ${e.message}\n`)})};if(e.operations&&Array.isArray(e.operations)){for(const t of e.operations)if(t.from_id===t.to_id)return tr(Mt(`Self-reference in batch: "${t.from_id}"`));const r=[...new Set(e.operations.flatMap(e=>[e.from_id,e.to_id]))],{data:n}=await t.from("items").select("id").eq("project_id",e.project_id).is("deleted_at",null).in("id",r),a=new Set((n??[]).map(e=>e.id)),s=r.filter(e=>!a.has(e));if(s.length>0)return tr(qt("Items",s.join(", ")));const{data:o}=await t.from("item_relationships").select("from_id, to_id, type").eq("project_id",e.project_id),d=new Map;for(const e of o??[])"parent"===e.type&&(d.has(e.from_id)||d.set(e.from_id,new Set),d.get(e.from_id).add(e.to_id));for(const t of e.operations){if("parent"!==t.type&&"child"!==t.type)continue;const[e,r]="parent"===t.type?[t.from_id,t.to_id]:[t.to_id,t.from_id];"remove"===t.action?d.get(e)?.delete(r):(d.has(e)||d.set(e,new Set),d.get(e).add(r))}const c=0,l=1,m=2,p=new Map,u=new Set;for(const[e,t]of d){u.add(e);for(const e of t)u.add(e)}const _=e=>{if((p.get(e)??c)!==c)return null;const t=[],r=[],i=e=>{p.set(e,l),r.push(e),t.push({node:e,parents:[...d.get(e)??[]],idx:0})};for(i(e);t.length>0;){const e=t[t.length-1];if(e.idx>=e.parents.length){p.set(e.node,m),r.pop(),t.pop();continue}const n=e.parents[e.idx];e.idx++;const a=p.get(n)??c;if(a===l){const e=r.indexOf(n);return[...r.slice(e),n]}a!==m&&i(n)}return null};for(const e of u){const t=_(e);if(t)return tr(Mt(`Batch would create a cycle: ${t.join(" -> ")}. No changes applied.`))}const f=crypto.randomUUID(),h=e.operations.filter(e=>"remove"===e.action),g=e.operations.filter(e=>"add"===e.action);for(const t of[...h,...g])await i(t,f,e.reason);return er(Ht({batch_id:f,processed:e.operations.length,operations:!1===e.echo?void 0:e.operations}))}return e.from_id&&e.to_id&&e.type&&e.action?e.from_id===e.to_id?tr(Mt("Cannot create a relationship between an item and itself")):(await i({action:e.action,from_id:e.from_id,to_id:e.to_id,type:e.type,expected_from_updated_at:e.expected_from_updated_at,expected_to_updated_at:e.expected_to_updated_at},void 0,e.reason),!1===e.echo?er(Ht({action:e.action,from_id:e.from_id,to_id:e.to_id})):er(Ht({action:e.action,type:e.type,from_id:e.from_id,to_id:e.to_id,message:`Relationship ${"add"===e.action?"added":"removed"}: ${e.from_id} --[${e.type}]--\x3e ${e.to_id}`}))):tr(Mt("Provide (from_id, to_id, type, action) for single-op or operations[] for batch."))}catch(e){return tr(Qt(e))}}q(wn,{linkItemsHandler:()=>bn});var vn=U({"src/tools/link-items.ts"(){Ge(),hr(),ms(),ir()}}),jn=U({"src/core/bulk-parent-links.ts"(){}}),Sn={};function xn(e){return new Set(["22023","23503","23505","23514"]).has(e.code??"")}async function kn(e){try{if(!e.items||0===e.items.length)return tr(Mt("items array must contain at least 1 item"));if(e.items.length>100)return tr(Mt(`Maximum 100 items per call (received ${e.items.length}). Split into multiple calls.`));const t=await $e(),r=We();try{await Be(t,e.project_id)}catch(e){if(e instanceof Error&&"VIEWER_ROLE"===e.message)return tr(Tt("Viewer"));throw e}const i=await async function(e,t,r){const{data:i,error:n}=await e.from("metadata").select("domains, function_tags").eq("project_id",t).maybeSingle();if(n)throw new Error(`metadata read failed: ${n.message}`);const a=i?.domains??[],s=xi(i?.function_tags),o=a.length>0?new Set(a.map(Dt).filter(e=>e.length>0)):null,d=ln(a.map(Dt).filter(e=>e.length>0).sort()),c=[],l=new Set,m={};for(let e=0;e<r.length;e++){const t=r[e];m[t.type]=(m[t.type]??0)+1;const i=(t.domains??[]).map(Dt).filter(e=>e.length>0);if(0===i.length)continue;const n=ki(i,s,Dt);if(n.length>0){l.add(e),c.push({index:e,title:t.title,error:Ei(n)});continue}if(!o)continue;const d=i.filter(e=>!o.has(e));d.length>0&&(l.add(e),c.push({index:e,title:t.title,error:`Unknown domain${d.length>1?"s":""}: ${d.join(", ")}. Registered: ${a.join(", ")}.`}))}return{registeredDomains:a,registeredDomainHash:d,invalidDomainIndexes:l,errors:c,typeBreakdown:m}}(t,e.project_id,e.items);if(sn("atoms_bulk_import")){const t={tool:"atoms_bulk_import",project_id:e.project_id,target:ln({items:e.items,registered_domain_hash:i.registeredDomainHash})};if(!e.confirmation_token){const r=on(t);return er(Ht({status:"confirmation_required",preview:{total_items:e.items.length,by_type:i.typeBreakdown,sample_titles:e.items.slice(0,5).map(e=>e.title),would_fail:i.errors.length>0,errors:i.errors,registered_domain_hash:i.registeredDomainHash},confirmation_token:r.token,expires_at:r.expires_at,expires_in_seconds:r.expires_in_seconds,message:`Re-call atoms_bulk_import with the same project_id, items array, and confirmation_token within ${r.expires_in_seconds}s to execute. Changing items or project domains will invalidate the token.`}))}const r=dn(e.confirmation_token,t);if(!r.valid)return tr(Mt(`confirmation_token rejected: ${r.reason}`))}const n=(new Date).toISOString(),a=ds(),s=[],o=[...i.errors],d=[],c=new Map,l=async r=>{let i=c.get(r);if(!i){const n=await fr(t,e.project_id,r),a=/^([A-Z]+)-(\d+)$/.exec(n);if(!a)throw new Error(`Unexpected item id format from next_item_id: ${n}`);i={prefix:a[1],next:Number(a[2])},c.set(r,i)}const n=`${i.prefix}-${String(i.next).padStart(3,"0")}`;return i.next+=1,n};for(let t=0;t<e.items.length;t++){const r=e.items[t];if(i.invalidDomainIndexes.has(t))continue;const n=(r.domains??[]).map(Dt).filter(e=>e.length>0),a=void 0!==r.level?Dt(r.level):void 0;try{const e=await l(r.type);d.push({itemId:e,input:r,sanitizedDomains:n,sanitizedLevel:a,index:t})}catch(e){o.push({index:t,title:r.title,error:`ID generation failed: ${e instanceof Error?e.message:String(e)}`})}}if(0===d.length)return er(Ht({created:0,items:[],errors:o}));const m=[],p=new Map;for(const{itemId:t,input:i,sanitizedDomains:a,sanitizedLevel:s}of d){const o={id:t,type:i.type,title:i.title,summary:i.summary,body:i.body,tags:{domains:a,level:s??""},ownership:{primary:null,additional:[]},relationships:{parents:i.parent_id?[i.parent_id]:[],children:[],related:[]},links:[],metadata:{created_at:n,created_by:r,updated_at:n,updated_by:r}};p.set(t,o),m.push({id:t,project_id:e.project_id,type:i.type,title:i.title,data:o,created_by:r,updated_by:r})}const u=m.map(e=>({id:e.id,type:e.type,title:e.title,data:e.data})),{error:_}=await t.rpc("bulk_save_items_with_revisions",{p_project_id:e.project_id,p_items:u,p_user_id:r,p_event_source:"mcp"});if(_&&xn(_))for(const{itemId:i,input:n,index:c}of d){const d=m.find(e=>e.id===i);if(!d)continue;const{error:l}=await t.rpc("save_item_with_revision",{p_item_id:d.id,p_project_id:e.project_id,p_type:d.type,p_title:d.title,p_data:d.data,p_user_id:r,p_event_source:"mcp",p_event_session_id:a,p_diff_summary:null});l?o.push({index:c,title:n.title,error:l.message}):s.push({id:i,title:n.title,type:n.type})}else{if(_)throw _;for(const{itemId:e,input:t}of d)s.push({id:e,title:t.title,type:t.type})}const f=d.filter(({input:e})=>e.parent_id).filter(({itemId:e})=>s.some(t=>t.id===e)).map(({itemId:t,input:r})=>({from_id:t,to_id:r.parent_id,type:"parent",project_id:e.project_id}));await async function(e){for(const t of e.links){const{error:r}=await e.client.rpc("apply_link_change",{p_project_id:e.projectId,p_from_id:t.from,p_to_id:t.to,p_type:"parent",p_action:"add",p_user_id:e.userId,p_event_source:"mcp"});r&&e.warn(`bulk parent link sync failed for ${t.to}: ${r.message??"unknown error"}`)}}({client:t,projectId:e.project_id,links:f.map(e=>({from:e.from_id,to:e.to_id})),userId:r,warn:e=>process.stderr.write(`[atoms-mcp] Warning: ${e}\n`)});const h=s.map(t=>({item_id:t.id,project_id:e.project_id,changed_by:r,event_type:"created",old_data:null,new_data:p.get(t.id)??null,actor:cs(),session_id:a}));h.length>0&&await t.from("change_history").insert(h).then(({error:e})=>{e&&process.stderr.write(`[atoms-mcp] Warning: bulk change_history log failed: ${e.message}\n`)});const g=new Set(s.map(e=>e.id)),y=new Set(d.filter(({itemId:e,sanitizedLevel:t})=>t&&g.has(e)).map(({sanitizedLevel:e})=>e));for(const r of y)await ri(t,e.project_id,r);return er(Ht({created:s.length,items:s,errors:o.length>0?o:[],project_id:e.project_id}))}catch(e){return tr(Qt(e))}}q(Sn,{bulkImportHandler:()=>kn,canRetryBulkImportPerItem:()=>xn});var En=U({"src/tools/bulk-import.ts"(){Ge(),hr(),ms(),pn(),ir(),jn(),hi(),Oi()}}),In={};async function An(e){try{const t=await $e(),r=We(),i=Ve();try{await Be(t,e.project_id)}catch(e){if(e instanceof Error&&"VIEWER_ROLE"===e.message)return tr(Tt("Viewer"));throw e}const n=await dr(t,e.project_id,e.item_id);if(!n)return tr(qt("Item",e.item_id));if("test-case"!==n.type)return tr(Mt(`Item '${e.item_id}' is a ${n.type}, not a test-case`));const a=(e.asset??"").trim(),s=(new Date).toISOString(),{data:o,error:d}=await t.rpc("record_test_results_bulk",{p_project_id:e.project_id,p_item_id:e.item_id,p_run_by:i||r,p_actor_user_id:r,p_results:[{result:e.result,note:e.note,asset:a||void 0,date:s,point:e.point,release_id:e.release_id,build:e.build,environment:e.environment,tooling:e.tooling,steps_rev:e.steps_rev,step_results:e.step_results}]});if(d)throw new Error(d.message);const c=Array.isArray(o)?o[0]:o?.[0];await t.from("change_history").insert({item_id:e.item_id,project_id:e.project_id,changed_by:r,event_type:"test_result_recorded",old_data:null,new_data:{result:e.result,run_at:s,note:e.note,asset:a||null},actor:cs(),session_id:ds()}).then(({error:e})=>{e&&process.stderr.write(`[atoms-mcp] Warning: change_history log failed: ${e.message}\n`)});const l=a?` (asset: ${a})`:"";return er(Ht({item_id:e.item_id,result:e.result,run_at:s,run_by:i||r,note:e.note??null,asset:a||null,asset_id:c?.asset_id??null,asset_created:c?.asset_created??!1,message:`Test result '${e.result}' recorded for ${e.item_id}${l}`}))}catch(e){return tr(Qt(e))}}q(In,{recordTestResultHandler:()=>An});var On=U({"src/tools/record-test-result.ts"(){Ge(),hr(),ms(),ir()}}),Rn={};async function Dn(e){try{const t=await $e(),r=We(),i=Ve();if(!Array.isArray(e.results)||0===e.results.length)return tr(Mt("results must be a non-empty array"));if(e.results.length>100)return tr(Mt(`results must have at most 100 entries (got ${e.results.length})`));try{await Be(t,e.project_id)}catch(e){if(e instanceof Error&&"VIEWER_ROLE"===e.message)return tr(Tt("Viewer"));throw e}const n=await dr(t,e.project_id,e.item_id);if(!n)return tr(qt("Item",e.item_id));if("test-case"!==n.type)return tr(Mt(`Item '${e.item_id}' is a ${n.type}, not a test-case`));const{data:a,error:s}=await t.rpc("record_test_results_bulk",{p_project_id:e.project_id,p_item_id:e.item_id,p_run_by:i||r,p_actor_user_id:r,p_results:e.results});if(s)throw new Error(s.message);return t.from("change_history").insert({item_id:e.item_id,project_id:e.project_id,changed_by:r,event_type:"test_result_recorded",old_data:null,new_data:{bulk:!0,count:e.results.length,results:a},actor:cs(),session_id:ds()}).then(({error:e})=>{e&&process.stderr.write(`[atoms-mcp] Warning: change_history log failed: ${e.message}\n`)}),er(Ht({item_id:e.item_id,count:e.results.length,results:a,message:`Recorded ${e.results.length} test results for ${e.item_id}`}))}catch(e){return tr(Qt(e))}}q(Rn,{recordTestResultsBulkHandler:()=>Dn});var Hn,Cn,Un,qn,Tn=U({"src/tools/record-test-results-bulk.ts"(){Ge(),hr(),ms(),ir()}});function Mn(e){return Math.min(Math.max(e??5,1),10)}var Pn=U({"src/core/trace-graph.ts"(){Hn=200,Cn=new Set(["parent","verifies","mitigated_by"]),Un=new Set(["child","verified_by","mitigates"]),qn={parent:"child",child:"parent",verifies:"verified_by",verified_by:"verifies",mitigated_by:"mitigates",mitigates:"mitigated_by",related:"related"}}}),$n={};async function Nn(e){try{const t=Mn(e.depth),r=await $e(),i=await dr(r,e.project_id,e.item_id);if(!i)return tr(qt("Item",e.item_id));const{data:n,error:a}=await r.from("items").select("id, title, type, data").eq("project_id",e.project_id).is("deleted_at",null);if(a)throw new Error(a.message);const{data:s,error:o}=await r.from("item_relationships").select("from_id, to_id, type").eq("project_id",e.project_id);if(o)throw new Error(o.message);const d=_r(n??[],s??[]);return er(Ht(function(e){const t=Mn(e.depth),r=e.relationshipTypes?.length?new Set(e.relationshipTypes):null,i=new Map(e.items.map(e=>[e.id,e]));i.has(e.rootItemId)||i.set(e.rootItemId,e.rootItem);const n=new Map,a=(e,t,r)=>{n.has(e)||n.set(e,[]),n.get(e).push({neighbor:t,relType:r})};for(const t of e.relationships){const i=t.type;if(("upstream"===e.direction||"both"===e.direction)&&(!Cn.has(i)||r&&!r.has(i)||a(t.from_id,t.to_id,i),Un.has(i))){const e=qn[i]??i;r&&!r.has(e)||a(t.to_id,t.from_id,e)}if(("downstream"===e.direction||"both"===e.direction)&&(!Un.has(i)||r&&!r.has(i)||a(t.from_id,t.to_id,i),Cn.has(i))){const e=qn[i]??i;r&&!r.has(e)||a(t.to_id,t.from_id,e)}"related"!==i||r&&!r.has("related")||(a(t.from_id,t.to_id,"related"),a(t.to_id,t.from_id,"related"))}const s=new Set([e.rootItemId]),o=[],d=[],c=[];for(const t of n.get(e.rootItemId)??[])c.push({id:t.neighbor,depth:1,relType:t.relType,from:e.rootItemId});for(;c.length>0&&o.length<Hn;){const{id:e,depth:r,relType:a,from:l}=c.shift();if(s.has(e)||r>t)continue;s.add(e);const m=i.get(e);if(m&&(o.push({id:e,title:m.title,type:m.type,relationship:a,depth:r,from:l}),d.push({from:l,to:e,type:a}),r<t))for(const t of n.get(e)??[])s.has(t.neighbor)||c.push({id:t.neighbor,depth:r+1,relType:t.relType,from:e})}const l={id:e.rootItemId,title:e.rootItem.title,type:e.rootItem.type,relationship:"root",depth:0,from:e.rootItemId};return{root:e.rootItemId,project_id:e.projectId,direction:e.direction,depth_limit:t,items:[l,...o],edges:d,total_count:o.length+1,...o.length>=Hn?{truncated:!0,truncation_message:`Results capped at ${Hn} items. Use a smaller depth or filter by relationship_types.`}:{}}}({projectId:e.project_id,rootItemId:e.item_id,rootItem:{id:e.item_id,title:i.title??e.item_id,type:i.type??"unknown"},items:n??[],relationships:d??[],direction:e.direction,depth:t,relationshipTypes:e.relationship_types})))}catch(e){return e instanceof Error&&e.message.includes("Not authenticated")?tr(Pt()):tr(Yt(e instanceof Error?e.message:String(e)))}}q($n,{traceHandler:()=>Nn});var Ln=U({"src/tools/trace.ts"(){Ge(),hr(),Pn(),ir()}}),Fn={};async function Wn(e){try{let t=(await $e()).from("assets").select("id, name, archived_at, created_at").eq("project_id",e.project_id).order("name");e.include_archived||(t=t.is("archived_at",null));const{data:r,error:i}=await t;if(i)throw new Error(i.message);return er(Ht({assets:r||[],total:(r||[]).length}))}catch(e){return e instanceof Error&&e.message.includes("Not authenticated")?tr(Pt()):tr(Yt(e instanceof Error?e.message:String(e)))}}q(Fn,{listAssetsHandler:()=>Wn});var Vn=U({"src/tools/assets.ts"(){Ge(),ir()}}),Bn={};async function zn(e,t,r,i){if(r&&i)throw new Error("baseline_id and as_of are mutually exclusive");if(i)return i;if(!r)return null;const{data:n,error:a}=await e.from("baselines").select("project_id, snapshot_at, deleted_at").eq("id",r).maybeSingle();if(a)throw new Error(a.message);if(!n||n.project_id!==t||n.deleted_at)throw new Error("Baseline not found for this project");return n.snapshot_at}async function Jn(e,t,r){if(r){const{data:i,error:n}=await e.rpc("variables_at",{p_project_id:t,p_snapshot_at:r});if(n)throw new Error(n.message);return i??[]}const{data:i,error:n}=await e.from("project_variables").select("*").eq("project_id",t).order("name");if(n)throw new Error(n.message);return i??[]}async function Qn(e,t,r,i){const{data:n,error:a}=await e.rpc("save_variable_with_revision",{p_project_id:t.project_id,p_name:t.name,p_value:t.value,p_unit:t.unit??null,p_description:t.description??null,p_is_signal:t.is_signal??!1,p_range_min:t.range_min??null,p_range_max:t.range_max??null,p_rate_ms:t.rate_ms??null,p_user_id:r,p_event_source:i,p_variable_id:t.id??null});if(a)throw new Error(a.message);return n}async function Gn(e){try{const t=await $e(),r=await zn(t,e.project_id,e.baseline_id,e.as_of),i=await Jn(t,e.project_id,r),n=e.limit??50,a=e.offset??0;return er(Ht({variables:i.slice(a,a+n),total:i.length},Ct(i.length,n,a)))}catch(e){return e instanceof Error&&e.message.includes("Not authenticated")?tr(Pt()):tr(Yt(e instanceof Error?e.message:String(e)))}}async function Yn(e){try{const t=await $e(),r=await zn(t,e.project_id,e.baseline_id,e.as_of),i=(await Jn(t,e.project_id,r)).find(t=>t.name===e.variable_name)??null;if(!i)return tr(qt("Variable",e.variable_name));const{data:n}=await t.from("items").select("id, title, type, data").eq("project_id",e.project_id).is("deleted_at",null),a=`{${e.variable_name}}`,s=(n||[]).filter(e=>(e.data?.body||"").includes(a));return er(Ht({variable:i,referenced_by:s.map(e=>({id:e.id,title:e.title,type:e.type})),reference_count:s.length}))}catch(e){return e instanceof Error&&e.message.includes("Not authenticated")?tr(Pt()):tr(Yt(e instanceof Error?e.message:String(e)))}}async function Kn(e){try{const t=await $e(),r=We();try{await Be(t,e.project_id)}catch(e){if(e instanceof Error&&"VIEWER_ROLE"===e.message)return tr(Tt("Viewer"));throw e}const i=await t.from("project_variables").select("*").eq("project_id",e.project_id).eq("name",e.variable_name).maybeSingle();if(!i.data)return tr(qt("Variable",e.variable_name));const n={updated_by:r,updated_at:(new Date).toISOString()};return void 0!==e.value&&(n.value=e.value),void 0!==e.unit&&(n.unit=e.unit||null),void 0!==e.description&&(n.description=e.description||null),er(Ht({variable:await Qn(t,{...i.data,...n,project_id:e.project_id,name:e.variable_name,value:n.value??i.data.value,unit:n.unit??i.data.unit,description:n.description??i.data.description},r,"mcp")}))}catch(e){return e instanceof Error&&e.message.includes("Not authenticated")?tr(Pt()):tr(Yt(e instanceof Error?e.message:String(e)))}}async function Zn(e){try{const t=await $e(),r=We();try{await Be(t,e.project_id)}catch(e){if(e instanceof Error&&"VIEWER_ROLE"===e.message)return tr(Tt("Viewer"));throw e}const i=/^[a-zA-Z_][a-zA-Z0-9_]*$/;if(e.variables&&Array.isArray(e.variables)){const n=e.variables.filter(e=>!i.test(e.name));if(n.length>0)return tr(Mt(`Invalid variable name(s): ${n.map(e=>e.name).join(", ")}. Use letters, digits, underscores; must start with letter or underscore.`));const a=e.variables.map(e=>e.name),s=a.filter((e,t)=>a.indexOf(e)!==t);if(s.length>0)return tr(Mt(`Duplicate names in batch: ${[...new Set(s)].join(", ")}`));const{data:o}=await t.from("project_variables").select("name").eq("project_id",e.project_id).in("name",a),d=(o??[]).map(e=>e.name);if(d.length>0)return tr(Mt(`Variable(s) already exist: ${d.join(", ")}. Remove them from the batch or use atoms_update_variable to change values.`));const c=e.variables.map(t=>({project_id:e.project_id,name:t.name,value:t.value,unit:t.unit??null,description:t.description??null,created_by:r,updated_by:r})),l=await async function(e,t,r,i){const{data:n,error:a}=await e.rpc("bulk_save_variables_with_revisions",{p_project_id:t,p_variables:r,p_user_id:i,p_event_source:"mcp"});if(a)throw new Error(a.message);return n??[]}(t,e.project_id,c,r);return er(Ht({batch_id:crypto.randomUUID(),created:(l??[]).length,variables:!1===e.echo?(l??[]).map(e=>({name:e.name})):l??[],message:`${(l??[]).length} variable(s) created. Reference them as {name} in requirement bodies.`}))}if(!e.name||!e.value)return tr(Mt("Provide either (name, value) for single variable or variables[] for batch."));if(!i.test(e.name))return tr(Mt(`Variable name "${e.name}" is invalid. Use letters, digits, and underscores only; must start with a letter or underscore.`));const{data:n}=await t.from("project_variables").select("name").eq("project_id",e.project_id).eq("name",e.name).maybeSingle();if(n)return tr(Mt(`Variable "${e.name}" already exists in this project.`));const a=await Qn(t,{project_id:e.project_id,name:e.name,value:e.value,unit:e.unit??null,description:e.description??null},r,"mcp");return!1===e.echo?er(Ht({name:e.name})):er(Ht({variable:a,message:`Variable "${e.name}" created. Reference it in requirement bodies as {${e.name}}.`}))}catch(e){return e instanceof Error&&e.message.includes("Not authenticated")?tr(Pt()):tr(Yt(e instanceof Error?e.message:String(e)))}}async function Xn(e){try{const t=await $e(),r=We();try{await Be(t,e.project_id)}catch(e){if(e instanceof Error&&"VIEWER_ROLE"===e.message)return tr(Tt("Viewer"));throw e}const{data:i}=await t.from("project_variables").select("id").eq("project_id",e.project_id).eq("name",e.name).maybeSingle();if(!i)return tr(qt("Variable",e.name));const{data:n}=await t.from("items").select("id").eq("project_id",e.project_id).is("deleted_at",null).filter("data->>body","ilike",`%{${e.name}}%`),a=(n??[]).length;if(a>0&&!e.force)return tr(Mt(`Variable "${e.name}" is referenced in ${a} item${a>1?"s":""}. Use force=true to delete anyway, or remove all {${e.name}} references first.`));if(sn("atoms_delete_variable")){const t={tool:"atoms_delete_variable",project_id:e.project_id,target:e.name};if(!e.confirmation_token){const r=on(t);return er(Ht({status:"confirmation_required",preview:{variable_name:e.name,referenced_by_items:a,would_orphan_references:a>0},confirmation_token:r.token,expires_at:r.expires_at,expires_in_seconds:r.expires_in_seconds,message:`Re-call atoms_delete_variable with the same project_id, name, and confirmation_token within ${r.expires_in_seconds}s to execute.`}))}const r=dn(e.confirmation_token,t);if(!r.valid)return tr(Mt(`confirmation_token rejected: ${r.reason}`))}const s=await async function(e,t,r,i){const{data:n,error:a}=await e.rpc("delete_variable_with_revision",{p_project_id:t,p_variable_id:r,p_user_id:i,p_event_source:"mcp"});if(a)throw new Error(a.message);return Boolean(n)}(t,e.project_id,i.id,r);return s?er(Ht({deleted:!0,name:e.name,referenced_by_items:a,warning:a>0?`${a} item${a>1?"s":""} still contain {${e.name}} — those references are now unresolved.`:void 0})):tr(qt("Variable",e.name))}catch(e){return e instanceof Error&&e.message.includes("Not authenticated")?tr(Pt()):tr(Yt(e instanceof Error?e.message:String(e)))}}q(Bn,{createVariableHandler:()=>Zn,deleteVariableHandler:()=>Xn,getVariableHandler:()=>Yn,listVariablesHandler:()=>Gn,updateVariableHandler:()=>Kn});var ea=U({"src/tools/variables.ts"(){Ge(),pn(),ir()}}),ta={};async function ra(e,t){const{data:r}=await e.from("metadata").select("domains, levels, function_tags").eq("project_id",t).maybeSingle();return{domains:r?.domains??[],levels:r?.levels??[],functionTags:Array.isArray(r?.function_tags)?r.function_tags.filter(e=>"string"==typeof e):[]}}async function ia(e,t,r,i,n){const{error:a}=await e.from("metadata").upsert({project_id:t,domains:r,levels:i,...void 0!==n?{function_tags:n}:{}},{onConflict:"project_id"});if(a)throw new Error(a.message)}async function na(e){try{const t=await $e(),{domains:r}=await ra(t,e.project_id),{counts:i,orphans:n}=await async function(e,t,r){const{data:i}=await e.from("items").select("data").eq("project_id",t).is("deleted_at",null),n={};for(const e of r)n[e]=0;const a={};for(const e of i??[]){const t=e.data?.tags?.domains??[];for(const e of t)a[e]=(a[e]??0)+1,e in n&&n[e]++}const s=new Set(r),o={};for(const[e,t]of Object.entries(a))s.has(e)||(o[e]=t);return{counts:n,orphans:o}}(t,e.project_id,r),a=r.map(e=>({name:e,registered:!0,item_count:i[e]??0})),s=Object.entries(n).map(([e,t])=>({name:e,registered:!1,item_count:t})).sort((e,t)=>e.name.localeCompare(t.name)),o=[...a,...s];return er(Ht({domains:o,total:o.length,registered_count:a.length,unregistered_count:s.length,note:s.length>0?`${s.length} unregistered tag(s) found on items. Use atoms_create_domain to register them, or atoms_update_item to remove them.`:void 0}))}catch(e){return e instanceof Error&&e.message.includes("Not authenticated")?tr(Pt()):tr(Yt(e instanceof Error?e.message:String(e)))}}async function aa(e){try{const t=await $e();try{await Be(t,e.project_id)}catch(e){if(e instanceof Error&&"VIEWER_ROLE"===e.message)return tr(Tt("Viewer"));throw e}const{domains:r,levels:i}=await ra(t,e.project_id),n=e.name.trim().toLowerCase();return n?r.includes(n)?tr(Mt(`Domain "${n}" already exists in this project.`)):(await ia(t,e.project_id,[...r,n],i),er(Ht({domain:{name:n},message:`Domain "${n}" registered. You can now tag items with domains=["${n}"].`}))):tr(Mt("Domain name cannot be empty."))}catch(e){return e instanceof Error&&e.message.includes("Not authenticated")?tr(Pt()):tr(Yt(e instanceof Error?e.message:String(e)))}}async function sa(e){try{const t=await $e(),r=We();try{await Be(t,e.project_id)}catch(e){if(e instanceof Error&&"VIEWER_ROLE"===e.message)return tr(Tt("Viewer"));throw e}const{domains:i,levels:n,functionTags:a}=await ra(t,e.project_id),s=e.name.trim().toLowerCase(),o=e.new_name.trim().toLowerCase();if(!o)return tr(Mt("new_name cannot be empty."));const d=i.includes(s);if(!d){const{data:r}=await t.from("items").select("id").eq("project_id",e.project_id).is("deleted_at",null).filter("data->tags->domains","cs",JSON.stringify([s])).limit(1);if(!r||0===r.length)return tr(qt("Domain",s))}if(o!==s&&i.includes(o))return tr(Mt(`Domain "${o}" already exists. Choose a different name.`));const{data:c,error:l}=await t.from("items").select("id, data").eq("project_id",e.project_id).is("deleted_at",null).filter("data->tags->domains","cs",JSON.stringify([s]));if(l)throw new Error(l.message);const m=(c??[]).length;if(e.dry_run)return er(Ht({dry_run:!0,rename:{from:s,to:o},items_affected:m,message:`Dry run: renaming "${s}" → "${o}" would update ${m} item${1!==m?"s":""}. Call again with dry_run=false to apply.`}));let p;p=d?i.map(e=>e===s?o:e):i.includes(o)?i:[...i,o];const u=a.map(e=>gi(e)===gi(s)?o:e);await ia(t,e.project_id,p,n,u);const{error:_}=await t.rpc("apply_domain_change",{p_project_id:e.project_id,p_old_domain:s,p_new_domain:o,p_user_id:r,p_event_source:"mcp"});if(_)throw new Error(_.message);return er(Ht({domain:{name:o},renamed_from:s,items_updated:m,message:`Domain renamed "${s}" → "${o}". ${m} item${1!==m?"s":""} updated.`}))}catch(e){return e instanceof Error&&e.message.includes("Not authenticated")?tr(Pt()):tr(Yt(e instanceof Error?e.message:String(e)))}}async function oa(e){try{const t=await $e(),r=We();try{await Be(t,e.project_id)}catch(e){if(e instanceof Error&&"VIEWER_ROLE"===e.message)return tr(Tt("Viewer"));throw e}const{domains:i,levels:n,functionTags:a}=await ra(t,e.project_id),s=e.name.trim().toLowerCase(),o=i.includes(s),{data:d,error:c}=await t.from("items").select("id, data").eq("project_id",e.project_id).is("deleted_at",null).filter("data->tags->domains","cs",JSON.stringify([s]));if(c)throw new Error(c.message);const l=(d??[]).length;if(!o&&0===l)return tr(qt("Domain",s));if(l>0&&!e.force)return tr(Mt(`Domain "${s}" is used by ${l} item${l>1?"s":""}. Use force=true to delete and strip the domain from all items, or re-tag them first.`));if(sn("atoms_delete_domain")){const t={tool:"atoms_delete_domain",project_id:e.project_id,target:s};if(!e.confirmation_token){const e=on(t);return er(Ht({status:"confirmation_required",preview:{domain_name:s,items_using_count:l,would_strip_from_items:l>0},confirmation_token:e.token,expires_at:e.expires_at,expires_in_seconds:e.expires_in_seconds,message:`Re-call atoms_delete_domain with the same project_id, name, and confirmation_token within ${e.expires_in_seconds}s to execute.`}))}const r=dn(e.confirmation_token,t);if(!r.valid)return tr(Mt(`confirmation_token rejected: ${r.reason}`))}const m=o?i.filter(e=>e!==s):i,p=a.filter(e=>gi(e)!==gi(s));if(await ia(t,e.project_id,m,n,p),l>0){const{error:i}=await t.rpc("apply_domain_change",{p_project_id:e.project_id,p_old_domain:s,p_new_domain:null,p_user_id:r,p_event_source:"mcp"});if(i)throw new Error(i.message)}return er(Ht({deleted:!0,name:s,items_updated:l,message:l>0?`Domain "${s}" deleted and stripped from ${l} item${l>1?"s":""}.`:`Domain "${s}" deleted.`}))}catch(e){return e instanceof Error&&e.message.includes("Not authenticated")?tr(Pt()):tr(Yt(e instanceof Error?e.message:String(e)))}}q(ta,{createDomainHandler:()=>aa,deleteDomainHandler:()=>oa,listDomainsHandler:()=>na,updateDomainHandler:()=>sa});var da=U({"src/tools/domains.ts"(){Ge(),pn(),ir(),Oi()}}),ca={};async function la(e){try{const t=await $e(),{data:r,error:i}=await t.from("projects").select("id, name").eq("id",e.project_id).is("deleted_at",null).maybeSingle(),n=r?.name??null,a=i?.message??null,{data:s,error:o}=await t.from("items").select("id, type, data").eq("project_id",e.project_id).is("deleted_at",null);if(o)throw new Error(o.message);const d=s??[];if(!n&&0===d.length){if(a)throw new Error(a);return tr(qt("Project",e.project_id))}const c={requirements:0,test_cases:0,notes:0},l=[],m=[];for(const e of d)if("requirement"===e.type){c.requirements++;const t=e.data?.tags?.domains??[];m.push({id:e.id,domains:t})}else"test-case"===e.type?(c.test_cases++,l.push(e.id)):"note"===e.type&&c.notes++;const p={passed:0,failed:0,blocked:0,not_run:0};if(l.length>0){const{data:r,error:i}=await t.from("test_results").select("item_id, result, run_at").eq("project_id",e.project_id).order("run_at",{ascending:!1});if(i)throw new Error(i.message);const n=new Map;for(const e of r??[])n.has(e.item_id)||n.set(e.item_id,e.result);for(const e of l){const t=n.get(e);t&&"not-run"!==t?"passed"===t?p.passed++:"failed"===t?p.failed++:"blocked"===t?p.blocked++:p.not_run++:p.not_run++}}const{data:u,error:_}=await t.from("item_relationships").select("from_id, to_id, type").eq("project_id",e.project_id);if(_)throw new Error(_.message);const f=_r(s??[],u??[]),h=new Set;for(const e of f)"verifies"===e.type?h.add(e.to_id):"verified_by"===e.type&&h.add(e.from_id);const g=m.filter(e=>h.has(e.id)).length,y=m.length,w={covered:g,uncovered:y-g,total:y,percent:y>0?Math.round(g/y*1e3)/10:100},b=new Map;for(const e of m){const t=e.domains.length>0?e.domains:["(untagged)"],r=h.has(e.id);for(const e of t){b.has(e)||b.set(e,{covered:0,total:0});const t=b.get(e);t.total++,r&&t.covered++}}const v=Array.from(b.entries()).sort((e,t)=>e[0].localeCompare(t[0])).map(([e,t])=>({domain:e,covered:t.covered,total:t.total,percent:t.total>0?Math.round(t.covered/t.total*1e3)/10:100})),j=new Date;j.setDate(j.getDate()-7);const{count:S,error:x}=await t.from("change_history").select("id",{count:"exact",head:!0}).eq("project_id",e.project_id).gte("changed_at",j.toISOString());if(x)throw new Error(x.message);return er(Ht({project_id:e.project_id,project_name:n??`Project ${e.project_id.slice(0,8)}`,counts:c,test_status:p,coverage:w,coverage_by_domain:v,recent_changes:S??0,last_updated:(new Date).toISOString()}))}catch(e){return e instanceof Error&&e.message.includes("Not authenticated")?tr(Pt()):tr(Yt(e instanceof Error?e.message:String(e)))}}q(ca,{projectSummaryHandler:()=>la});var ma,pa=U({"src/tools/project-summary.ts"(){Ge(),hr(),ir()}}),ua={};async function _a(e){try{const t=await $e();let r,i;if(e.query&&e.query.trim().length>0){const n=await ur(t,e.project_id,e.query,{type:e.type,limit:e.limit});r=n.items.map(e=>({id:e.id,title:e.title,type:e.type,status:e.data?.status,domains:e.data?.tags?.domains??[],level:e.data?.tags?.level})),i=n.totalCount}else{const n=await or(t,e.project_id,{type:e.type,domain:e.domain,level:e.level,limit:e.limit,offset:e.offset});r=n.items.map(e=>({id:e.id,title:e.title,type:e.type,status:e.data?.status,domains:e.data?.tags?.domains??[],level:e.data?.tags?.level})),i=n.totalCount}const n=await async function(e,t){const{data:r,error:i}=await e.from("items").select("data").eq("project_id",t).is("deleted_at",null);if(i)throw new Error(i.message);const n=new Set,a=new Set;for(const e of r??[]){const t=e.data?.tags;if(t){const e=t.domains;if(e)for(const t of e)n.add(t);const r=t.level;r&&a.add(r)}}return{domains:[...n].sort(),levels:[...a].sort()}}(t,e.project_id);return er(Ht({items:r,filters:{domains:n.domains,levels:n.levels,types:ma},project_id:e.project_id},Ct(i,e.limit,e.offset)))}catch(e){return e instanceof Error&&e.message.includes("Not authenticated")?tr(Pt()):tr(Yt(e instanceof Error?e.message:String(e)))}}q(ua,{browseHandler:()=>_a});var fa,ha,ga,ya,wa=U({"src/tools/browse.ts"(){Ge(),hr(),ir(),ma=["requirement","test-case","note","table"]}}),ba={};async function va(e){try{const t=e.direction??"downstream",r=null!=e.depth?Math.min(Math.max(e.depth,1),20):null,i=await $e(),n=await dr(i,e.project_id,e.item_id);if(!n)return tr(qt("Item",e.item_id));const{data:a,error:s}=await i.from("item_relationships").select("from_id, to_id, type").eq("project_id",e.project_id);if(s)throw new Error(s.message);const{data:o,error:d}=await i.from("items").select("id, title, type").eq("project_id",e.project_id).is("deleted_at",null);if(d)throw new Error(d.message);const c=new Map;for(const e of o??[])c.set(e.id,{title:e.title,type:e.type});const l=new Map,m=(e,t,r)=>{l.has(e)||l.set(e,[]),l.get(e).push({neighbor:t,relType:r})};for(const e of a??[]){const r=e.type;"upstream"!==t&&"both"!==t||(ha.includes(r)&&m(e.from_id,e.to_id,r),ga.includes(r)&&m(e.to_id,e.from_id,ya[r]??r)),"downstream"!==t&&"both"!==t||(ga.includes(r)&&m(e.from_id,e.to_id,r),ha.includes(r)&&m(e.to_id,e.from_id,ya[r]??r)),"related"===r&&(m(e.from_id,e.to_id,"related"),m(e.to_id,e.from_id,"related"))}const p=new Set;p.add(e.item_id);const u=[];let _=0;const f=[];for(const t of l.get(e.item_id)??[])f.push({id:t.neighbor,depth:1,path:[{from_id:e.item_id,to_id:t.neighbor,relationship_type:t.relType}]});for(;f.length>0&&u.length<fa;){const{id:e,depth:t,path:i}=f.shift();if(p.has(e))continue;if(null!==r&&t>r)continue;p.add(e);const n=c.get(e);if(n&&(u.push({item_id:e,title:n.title,type:n.type,depth:t,relationship_path:i}),t>_&&(_=t),null===r||t<r))for(const r of l.get(e)??[])p.has(r.neighbor)||f.push({id:r.neighbor,depth:t+1,path:[...i,{from_id:e,to_id:r.neighbor,relationship_type:r.relType}]})}let h=0;if(e.include_variable_refs){const{data:t}=await i.from("variable_references").select("variable_id").eq("item_id",e.item_id).eq("project_id",e.project_id),r=[...new Set((t??[]).map(e=>e.variable_id))];if(r.length>0){const{data:t}=await i.from("project_variables").select("id, name").in("id",r),n=new Map((t??[]).map(e=>[e.id,e.name])),{data:a}=await i.from("variable_references").select("variable_id, item_id").in("variable_id",r).eq("project_id",e.project_id).neq("item_id",e.item_id);for(const t of a??[]){const r=t.item_id;if(p.has(r))continue;if(u.length>=fa)break;p.add(r);const i=c.get(r);if(!i)continue;const a=n.get(t.variable_id)??t.variable_id;u.push({item_id:r,title:i.title,type:i.type,depth:1,relationship_path:[{from_id:e.item_id,to_id:r,relationship_type:`variable_ref:${a}`}]}),h++}}}const g={};for(const e of u)g[e.type]=(g[e.type]??0)+1;const y=c.get(e.item_id);return er(Ht({root:{item_id:e.item_id,title:y?.title??n.title,type:y?.type??n.type},impacted_items:u,summary:{total_impacted:u.length,by_type:g,max_depth_reached:_,...h>0?{variable_impact_count:h}:{},...u.length>=fa?{truncated:!0}:{}}}))}catch(e){return e instanceof Error&&e.message.includes("Not authenticated")?tr(Pt()):tr(Yt(e instanceof Error?e.message:String(e)))}}q(ba,{impactAnalysisHandler:()=>va});var ja=U({"src/tools/impact-analysis.ts"(){Ge(),hr(),ir(),fa=200,ha=["parent","verifies","traces_to","allocated_from","decomposed_from"],ga=["child","verified_by","allocated_to","decomposes_to","traced_by"],ya={parent:"child",child:"parent",verifies:"verified_by",verified_by:"verifies",related:"related",traces_to:"traced_by",traced_by:"traces_to",allocated_to:"allocated_from",allocated_from:"allocated_to",decomposes_to:"decomposed_from",decomposed_from:"decomposes_to"}}}),Sa=U({"src/core/relationship-sync.ts"(){}}),xa={};function ka(e){return e instanceof Error&&e.message.includes("Not authenticated")?tr(Pt()):null}async function Ea(e,t){const{data:r,error:i}=await e.rpc("restore_baseline_state",{p_baseline_id:t.baselineId,p_project_id:t.projectId,p_items:[],p_variables:[],p_user_id:t.userId});if(i)throw new Error(i.message);return r}async function Ia(e,t,r){const{data:i,error:n}=await e.from("items").select("id, deleted_at").eq("project_id",t).eq("id",r).maybeSingle();if(n)throw new Error(n.message);if(a=i,!Boolean(a?.id&&null===a.deleted_at))return;var a;const{error:s}=await e.rpc("sync_item_relationships_from_jsonb",{p_project_id:t,p_item_id:r});if(s)throw new Error(s.message)}async function Aa(e,t,r){const{data:i,error:n}=await e.from("projects").select("org_id").eq("id",t).maybeSingle();if(n)return tr(Yt(n.message));if(!i)return tr(qt("Project",t));const{data:a,error:s}=await e.from("organizations").select("feature_flags").eq("id",i.org_id).maybeSingle();return s?tr(Yt(s.message)):!0!==(a?.feature_flags??{})[r]?tr(Ut(`Feature '${r}' is not enabled for this organization`,["Ask an org admin to enable the flag in Org Settings."])):null}async function Oa(e){try{const t=await $e(),r=await async function(e,t){const{data:r,error:i}=await e.from("baselines").select("id, project_id, name, description, field_definitions_snapshot, snapshot_at, created_at, created_by, locked_at, locked_by, release_version, release_status").eq("project_id",t).is("deleted_at",null).order("snapshot_at",{ascending:!1});if(i)throw new Error(`listBaselines: ${i.message}`);return r??[]}(t,e.project_id),i=Math.max(e.offset??0,0),n=Math.min(Math.max(e.limit??50,1),100);return er(Ht({baselines:r.slice(i,i+n).map(e=>({id:e.id,name:e.name,description:e.description,field_definitions_snapshot:e.field_definitions_snapshot,snapshot_at:e.snapshot_at,created_at:e.created_at,created_by:e.created_by,locked:null!==e.locked_at,locked_at:e.locked_at,release_version:e.release_version,release_status:e.release_status})),total:r.length,offset:i,limit:n}))}catch(e){return ka(e)??tr(Yt(e instanceof Error?e.message:String(e)))}}async function Ra(e){try{const t=await $e(),r=await Aa(t,e.project_id,"baselines_enabled");if(r)return r;const{data:i,error:n}=await t.from("baselines").insert({project_id:e.project_id,name:e.name.trim(),description:e.description??null,snapshot_at:(new Date).toISOString()}).select().single();return n?"23505"===n.code?tr(Ut(`Baseline name "${e.name}" already exists in this project`,["Pick a different name"])):tr(Yt(n.message)):er(Ht({id:i.id,name:i.name,description:i.description,field_definitions_snapshot:i.field_definitions_snapshot,snapshot_at:i.snapshot_at,created_at:i.created_at,locked:!1}))}catch(e){return ka(e)??tr(Yt(e instanceof Error?e.message:String(e)))}}async function Da(e){try{const t=await $e(),r=await lr(t,e.baseline_id);if(!r)return tr(qt("Baseline",e.baseline_id));const i=await Aa(t,r.project_id,"baselines_enabled");if(i)return i;if(null!==r.locked_at)return tr(Ut("Baseline is already locked",["Locks are one-way; nothing to do"]));const{data:n}=await t.from("projects").select("org_id").eq("id",r.project_id).maybeSingle();if(!n)return tr(qt("Project",r.project_id));const{data:a}=await t.auth.getUser(),s=a?.user?.id;if(!s)return tr(Pt());const{data:o}=await t.from("org_members").select("role").eq("user_id",s).eq("org_id",n.org_id).maybeSingle();if(!o||"admin"!==o.role)return tr(Ut("Only admins can lock baselines",["Ask an org admin to lock this baseline"]));const{data:d,error:c}=await t.from("baselines").update({locked_at:(new Date).toISOString(),locked_by:s}).eq("id",e.baseline_id).select().single();return c?tr(Yt(c.message)):er(Ht({id:d.id,locked_at:d.locked_at,locked_by:d.locked_by}))}catch(e){return ka(e)??tr(Yt(e instanceof Error?e.message:String(e)))}}async function Ha(e){try{const t=await $e(),[r,i]=await Promise.all([lr(t,e.baseline_a_id),lr(t,e.baseline_b_id)]);if(!r||!i)return tr(qt("Baseline","one of the baseline_ids"));if(r.project_id!==i.project_id)return tr(Ut("Both baselines must belong to the same project",["Verify both baseline_ids are from the same project_id"]));const[n,a]=await Promise.all([pr(t,r.project_id,r.snapshot_at),pr(t,i.project_id,i.snapshot_at)]),s=new Map(n.map(e=>[e.id,e])),o=new Map(a.map(e=>[e.id,e])),d=Array.from(new Set([...s.keys(),...o.keys()])).sort(),c=[];let l=0,m=0,p=0;for(const e of d){const t=s.get(e),r=o.get(e);if(!t&&r)l++,c.push({item_id:e,change_type:"added"});else if(t&&!r)m++,c.push({item_id:e,change_type:"removed"});else if(t&&r){const i=[];t.title!==r.title&&i.push("title"),t.type!==r.type&&i.push("type");JSON.stringify(t.data??{})!==JSON.stringify(r.data??{})&&i.push("data"),i.length>0&&(p++,c.push({item_id:e,change_type:"modified",fields_changed:i}))}}const u=Math.max(e.offset??0,0),_=Math.min(Math.max(e.limit??50,1),200),f=e.detail?c.slice(u,u+_):c.slice(u,u+_).map(({item_id:e,change_type:t,fields_changed:r})=>({item_id:e,change_type:t,fields_changed:r}));return er(Ht({summary:{added:l,removed:m,modified:p,total_compared:d.length},items:f,page_info:{offset:u,limit:_,total:c.length},baselines:{a:{id:r.id,name:r.name,snapshot_at:r.snapshot_at},b:{id:i.id,name:i.name,snapshot_at:i.snapshot_at}}}))}catch(e){return ka(e)??tr(Yt(e instanceof Error?e.message:String(e)))}}async function Ca(e){try{const t=await $e(),r=await lr(t,e.baseline_id);if(!r)return tr(qt("Baseline",e.baseline_id));const i=await Aa(t,r.project_id,"baselines_enabled");if(i)return i;const{data:n}=await t.from("projects").select("org_id").eq("id",r.project_id).maybeSingle();if(!n)return tr(qt("Project",r.project_id));const{data:a}=await t.auth.getUser(),s=a?.user?.id;if(!s)return tr(Pt());const{data:o}=await t.from("org_members").select("role").eq("user_id",s).eq("org_id",n.org_id).maybeSingle();if(!o||"admin"!==o.role)return tr(Ut("Only admins can restore from a baseline",["Ask an org admin to run the restore"]));const{data:d}=await t.rpc("cc_project_is_controlled",{p_project_id:r.project_id});if(!0===d)return tr(Ut("Change control is on for this project. Release review before restoring a baseline.",["Release review on the project, then run the restore"]));const c=await pr(t,r.project_id,r.snapshot_at),l=c.map(e=>e.id),{data:m,error:p}=l.length>0?await t.from("items").select("*").eq("project_id",r.project_id).in("id",l):{data:[],error:null};if(p)return tr(Yt(p.message));const u=function(e,t){const r=new Map(t.map(e=>[e.id,e]));return e.flatMap(e=>{const t=r.get(e.id),i=[];var n,a;return t?(null!==t.deleted_at&&i.push("deleted_at"),t.type!==e.type&&i.push("type"),t.title!==e.title&&i.push("title"),n=t.data,a=e.data,JSON.stringify(n??{})!==JSON.stringify(a??{})&&i.push("data")):i.push("missing"),0===i.length?[]:[{id:e.id,type:e.type,title:e.title,data:e.data??{},diff_summary:{changed_fields:i,restored:!0},oldData:t?.data??null}]})}(c,m??[]),{data:_,error:f}=await t.rpc("variables_at",{p_project_id:r.project_id,p_snapshot_at:r.snapshot_at});if(f)return tr(Yt(f.message));const h=_??[];if(!0!==e.confirm)return er(Ht({preview:!0,items_to_restore:u.length,variables_to_restore:h.length,field_definitions_to_restore:r.field_definitions_snapshot?.length??null}));const g=await Ea(t,{baselineId:r.id,projectId:r.project_id,userId:s});return er(Ht({ok:!0,items_restored:g.items_restored,variables_restored:g.variables_restored,field_definitions_restored:g.field_definitions.length}))}catch(e){return ka(e)??tr(Yt(e instanceof Error?e.message:String(e)))}}q(xa,{createBaselineHandler:()=>Ra,diffBaselinesHandler:()=>Ha,listBaselinesHandler:()=>Oa,lockBaselineHandler:()=>Da,restoreBaselineHandler:()=>Ca,restoreBaselineStateAtomically:()=>Ea,syncRestoredItemRelationships:()=>Ia});var Ua,qa=U({"src/tools/baselines.ts"(){Ge(),ms(),ir(),hr(),Sa()}}),Ta={};function Ma(e){return"safety-goal"===e?"hara":"failure-mode"===e?"dfmea":null}async function Pa(e,t,r){const{data:i,error:n}=await e.rpc("next_item_id",{p_project_id:t,p_prefix:r});if(n)throw new Error(`ID generation failed: ${n.message}`);return i}function $a(e){const t=e?.relationships?.mitigated_by;return Array.isArray(t)?t.filter(e=>"string"==typeof e):[]}function Na(e){return JSON.parse(JSON.stringify(e??{}))}async function La(e,t,r){const i=[];for(const e of r){const t=(e??"").trim();t&&null===yi(t,i)&&i.push(t)}if(0===i.length)return i;try{const{data:r}=await e.from("metadata").select("domains, function_tags").eq("project_id",t).maybeSingle(),n=e=>Array.isArray(e)?e.filter(e=>"string"==typeof e):[],a=n(r?.domains),s=n(r?.function_tags),o=i.map(e=>yi(e,a)??e),d=[...a],c=[...s];let l=!1;for(const e of o)null===yi(e,d)&&(d.push(e),l=!0),null===yi(e,c)&&(c.push(e),l=!0);if(l){const{error:r}=await e.from("metadata").upsert({project_id:t,domains:d,function_tags:c},{onConflict:"project_id"});r&&process.stderr.write(`[atoms-mcp] Warning: function tag registration failed: ${r.message}\n`)}return o}catch(e){return process.stderr.write(`[atoms-mcp] Warning: function tag registration failed: ${e instanceof Error?e.message:String(e)}\n`),i}}async function Fa(e){try{const t=await $e(),r=We();try{await Be(t,e.project_id)}catch(e){if(e instanceof Error&&"VIEWER_ROLE"===e.message)return tr(Tt("Viewer"));throw e}const i=e.source_req_ids??[],n=e.source_functions??[];if("hara"===e.subtype&&i.length>0)return tr(Mt("A HARA is seeded from functions, never requirements (a requirement is an output of the analysis): pass source_functions and omit source_req_ids"));if("dfmea"===e.subtype&&n.length>0)return tr(Mt("source_functions applies to hara only; seed a DFMEA with source_req_ids"));if(i.length>50)return tr(Mt("source_req_ids supports at most 50 entries per call"));if(n.length>50)return tr(Mt("source_functions supports at most 50 entries per call"));if(i.length>0){const{data:r,error:n}=await t.from("items").select("id, type, data").eq("project_id",e.project_id).is("deleted_at",null).in("id",i);if(n)throw new Error(n.message);const a=new Map((r??[]).map(e=>[e.id,e])),s=i.filter(e=>!a.has(e));if(s.length>0)return tr(qt("Source requirement(s)",s.join(", ")));const o=i.filter(e=>{const t=a.get(e);return"requirement"!==t.type||Ua.has(t.data?.req_type)});if(o.length>0)return tr(Mt(`source_req_ids must reference plain requirements (type='requirement', not analysis rows): ${o.join(", ")}`))}const a=e.subtype,s="hara"===a?await La(t,e.project_id,n):i,o=await Pa(t,e.project_id,ui[a]),d=e.title??("hara"===a?"New HARA":"New DFMEA"),c=e.revision??"1.0",l=mi[a],m=(new Date).toISOString(),p={id:o,type:"analysis",title:d,body:"",tags:{domains:[],level:""},ownership:{primary:null,additional:[]},relationships:{parents:[],children:[],related:[],verifies:[],verified_by:[]},links:[],analysis:{subtype:a,scheme:_i[a],revision:c,..."hara"===a?{standard:fi}:{}},metadata:{created_at:m,created_by:r,updated_at:m,updated_by:r}},{error:u}=await t.rpc("save_item_with_revision",{p_item_id:o,p_project_id:e.project_id,p_type:"analysis",p_title:d,p_data:p,p_user_id:r,p_event_source:"mcp",p_event_session_id:ds()??null,p_diff_summary:null});if(u)throw new Error(u.message);const _=(e,t)=>"hara"===a?{id:e,source_function:t}:{id:e,source_req:t},f=[],h=[];for(const i of s){const n=await Pa(t,e.project_id,pi[a]),s={id:n,type:"requirement",title:"",body:"",req_type:l,tags:{domains:[],level:""},ownership:{primary:null,additional:[]},relationships:{parents:[o],children:[],related:[],verifies:[],verified_by:[]},links:[],analysis_props:"hara"===a?{source_req:null,source_function:i,approved:!1,approved_at:null,field_source:{}}:{source_req:i,approved:!1,approved_at:null,field_source:{}},metadata:{created_at:m,created_by:r,updated_at:m,updated_by:r}},{error:d}=await t.rpc("save_item_with_revision",{p_item_id:n,p_project_id:e.project_id,p_type:"requirement",p_title:"",p_data:s,p_user_id:r,p_event_source:"mcp",p_event_session_id:ds()??null,p_diff_summary:null});if(d)throw new Error(d.message);const{error:c}=await t.rpc("apply_link_change",{p_project_id:e.project_id,p_from_id:n,p_to_id:o,p_type:"parent",p_action:"add",p_user_id:r,p_event_source:"mcp"});c&&(process.stderr.write(`[atoms-mcp] Warning: analysis row parent link sync failed for ${n}: ${c.message}\n`),h.push({row_id:n,..."hara"===a?{source_function:i}:{source_req:i},message:c.message})),await t.from("change_history").insert({item_id:n,project_id:e.project_id,changed_by:r,event_type:"created",old_data:null,new_data:s,actor:cs(),session_id:ds()}).then(({error:e})=>{e&&process.stderr.write(`[atoms-mcp] Warning: change_history log failed: ${e.message}\n`)}),f.push(_(n,i))}return await t.from("change_history").insert({item_id:o,project_id:e.project_id,changed_by:r,event_type:"created",old_data:null,new_data:p,actor:cs(),session_id:ds()}).then(({error:e})=>{e&&process.stderr.write(`[atoms-mcp] Warning: change_history log failed: ${e.message}\n`)}),er(Ht({id:o,subtype:a,title:d,revision:c,..."hara"===a?{standard:fi}:{},rows:f,...h.length>0?{link_warnings:h,warning:`${h.length} row(s) were created but could not be attached to the analysis and will not appear in atoms_get_analysis. Repair: for each row in link_warnings, call atoms_link_items with type 'parent', from_id=<row id>, to_id=${o}.`}:{}}))}catch(e){return tr(Qt(e))}}async function Wa(e){try{const t=await $e(),r=We();try{await Be(t,e.project_id)}catch(e){if(e instanceof Error&&"VIEWER_ROLE"===e.message)return tr(Tt("Viewer"));throw e}const i=await dr(t,e.project_id,e.row_id);if(!i)return tr(qt("Analysis row",e.row_id));const n=i.data,a=Ma(n?.req_type);if(!a)return tr(Mt(`Item ${e.row_id} is not an analysis row (req_type must be 'safety-goal' or 'failure-mode')`));const s=new Set([...li[a],"effect"]);"hara"===a&&s.add("title");const o=Object.keys(e.fields??{});if(0===o.length)return tr(Mt("fields must contain at least one key"));const d=o.filter(e=>!s.has(e));if(d.length>0)return tr(Mt(`Unknown field(s) for ${a}: ${d.join(", ")}. Allowed: ${[...s].sort().join(", ")}`));const c=ci[a],l={};for(const t of o){const r=e.fields[t];if(c[t]){const e="number"==typeof r&&"dfmea"===a?String(r):r;if("string"!=typeof e||!c[t].includes(e))return tr(Mt(`Invalid ${t} value ${JSON.stringify(r)} for ${a}. Allowed: ${c[t].join(", ")}`));l[t]=e}else{if("string"!=typeof r)return tr(Mt(`Field '${t}' must be a string`));l[t]=r}}if("hara"===a&&"string"==typeof l.source_function&&l.source_function.trim()){const[r]=await La(t,e.project_id,[l.source_function]);r&&(l.source_function=r)}const m=n?.analysis_props??null,p=Na(n);p.analysis_props&&"object"==typeof p.analysis_props||(p.analysis_props={source_req:null,approved:!1,approved_at:null,field_source:{}});const u=p.analysis_props;u.field_source&&"object"==typeof u.field_source||(u.field_source={});let _=i.title;for(const[e,t]of Object.entries(l))"effect"===e?p.body=t:"title"===e?(p.title=t,_=t):(u[e]=t,"failure_mode"===e&&"dfmea"===a&&(p.title=t,_=t)),u.field_source[e]="mcp";const f=(new Date).toISOString();p.metadata={...p.metadata??{},updated_at:f,updated_by:r};const{data:h,error:g}=await t.rpc("save_item_with_revision",{p_item_id:i.id,p_project_id:e.project_id,p_type:i.type,p_title:_,p_data:p,p_user_id:r,p_event_source:"mcp",p_event_session_id:ds()??null,p_diff_summary:null,p_expected_updated_at:i.updated_at});if(g)throw new Error(g.message);if(!h)return tr(Ut(`Fill silently failed for '${e.row_id}': save RPC returned no row. Likely RLS denied the write.`,["Verify you have editor or admin role in this project","Retry after checking project access"]));await t.from("change_history").insert({item_id:i.id,project_id:e.project_id,changed_by:r,event_type:"updated",old_data:n,new_data:p,actor:cs(),session_id:ds()}).then(({error:e})=>{e&&process.stderr.write(`[atoms-mcp] Warning: change_history log failed: ${e.message}\n`)});const y=vi(a,u),w=[];if("hara"===a){const n=vi("hara",m),a=$a(p);if(a.length>0&&y!==ai&&y!==n)for(const n of a){const a=await dr(t,e.project_id,n);if(!a)continue;const s=a.data;if(s?.asil===y&&s?.asil_source===i.id)continue;const o=s?.asil??null,d=s?.asil_source??null,c=Na(s);c.asil=y,c.asil_source=i.id,c.metadata={...c.metadata??{},updated_at:f,updated_by:r};const{error:l}=await t.rpc("save_item_with_revision",{p_item_id:a.id,p_project_id:e.project_id,p_type:a.type,p_title:a.title,p_data:c,p_user_id:r,p_event_source:"mcp",p_event_session_id:ds()??null,p_diff_summary:null,p_expected_updated_at:a.updated_at});if(l)throw new Error(l.message);await t.from("change_history").insert({item_id:a.id,project_id:e.project_id,changed_by:r,event_type:"updated",old_data:{asil:o,asil_source:d},new_data:{asil:y,asil_source:i.id},actor:cs(),session_id:ds(),reason:`ASIL propagated from ${i.id} (rating change via atoms_fill_row)`}).then(({error:e})=>{e&&process.stderr.write(`[atoms-mcp] Warning: change_history log failed: ${e.message}\n`)}),w.push(n)}}return er(Ht({row_id:i.id,subtype:a,written_fields:Object.keys(l),computed_level:y,...w.length>0?{asil_propagated_to:w}:{}}))}catch(e){return tr(Qt(e))}}async function Va(e,t,r){const i=await dr(e,t,r);if(!i)return{ok:!1,result:tr(qt("Analysis",r))};if("analysis"!==i.type)return{ok:!1,result:tr(Mt(`Item ${r} is not an analysis (type='${i.type}')`))};const n=i.data,a=Ma("hara"===n?.analysis?.subtype?"safety-goal":"dfmea"===n?.analysis?.subtype?"failure-mode":null);if(!a)return{ok:!1,result:tr(Mt(`Analysis ${r} is missing a valid analysis.subtype`))};const s=mi[a],o=Array.isArray(n?.relationships?.children)?n.relationships.children.filter(e=>"string"==typeof e):[];let d=[];if(o.length>0){const{data:r,error:i}=await e.from("items").select("*").eq("project_id",t).is("deleted_at",null).in("id",o);if(i)throw new Error(i.message);const n=new Map((r??[]).filter(e=>!e.deleted_at&&o.includes(e.id)&&e.data?.req_type===s).map(e=>[e.id,e]));d=o.map(e=>n.get(e)).filter(e=>Boolean(e))}const c=new Set;for(const e of d){const t=e.data?.analysis_props??{};"string"==typeof t.source_req&&t.source_req&&c.add(t.source_req);for(const t of $a(e.data))c.add(t)}const l=new Map;if(c.size>0){const{data:r,error:i}=await e.from("items").select("id, updated_at").eq("project_id",t).is("deleted_at",null).in("id",[...c]);if(i)throw new Error(i.message);for(const e of r??[])l.set(e.id,e.updated_at)}const m=d.map(e=>{const t=e.data?.analysis_props??{},r=$a(e.data).filter(e=>l.has(e)),i=r.length>0,n=vi(a,t),s=function(e,t,r){if(r)return null;if("hara"===e){const e=wi(t);return"QM"!==e&&e!==ai?`asil ${e} unmitigated`:null}const i=parseInt(bi(t),10),n=parseInt(t?.severity||"",10);return!Number.isNaN(i)&&i>=100?"rpn >= 100":!Number.isNaN(n)&&n>=9?"severity >= 9":null}(a,t,i),o={id:e.id,title:"string"==typeof e.title?e.title:"",source_req:"string"==typeof t.source_req?t.source_req:null,effect:"string"==typeof e.data?.body?e.data.body:"",computed_level:n,is_gap:null!==s,gap_rule:s,is_critical:ji(a,t,i),is_stale:Si(t,r,e=>l.get(e)??null),mitigated_by:r,link_roles:t.link_roles??{},approved:!0===t.approved,approved_at:t.approved_at??null,mcp_fields:Object.entries(t.field_source??{}).filter(([,e])=>"mcp"===e).map(([e])=>e).sort()};for(const e of li[a])o[e]=t[e]??null;return o});return{ok:!0,analysis:{id:i.id,title:"string"==typeof i.title?i.title:"",subtype:a,scheme:"string"==typeof n?.analysis?.scheme?n.analysis.scheme:_i[a],revision:"string"==typeof n?.analysis?.revision?n.analysis.revision:"1.0",standard:"string"==typeof n?.analysis?.standard?n.analysis.standard:"hara"===a?fi:null},rows:m,counts:{rows:m.length,gaps:m.filter(e=>e.is_gap).length,critical:m.filter(e=>e.is_critical).length,stale:m.filter(e=>e.is_stale).length,approved:m.filter(e=>e.approved).length,linked:m.filter(e=>e.mitigated_by.length>0).length}}}async function Ba(e){try{const t=await $e(),r=await Va(t,e.project_id,e.analysis_id);return r.ok?er(Ht({analysis:r.analysis,rows:r.rows,counts:r.counts})):r.result}catch(e){return tr(Qt(e))}}async function za(e){try{const n=await $e(),a=We();try{await Be(n,e.project_id)}catch(e){if(e instanceof Error&&"VIEWER_ROLE"===e.message)return tr(Tt("Viewer"));throw e}if(e.row_id===e.req_id)return tr(Mt("row_id and req_id must be different items"));const s=await dr(n,e.project_id,e.row_id);if(!s)return tr(qt("Analysis row",e.row_id));const o=s.data,d=Ma(o?.req_type);if(!d)return tr(Mt(`Item ${e.row_id} is not an analysis row (req_type must be 'safety-goal' or 'failure-mode')`));const c=await dr(n,e.project_id,e.req_id);if(!c)return tr(qt("Requirement",e.req_id));const l=c.data;if("requirement"!==c.type||Ua.has(l?.req_type))return tr(Mt(`req_id must reference a plain requirement (type='requirement', not an analysis row): ${e.req_id}`));if("dfmea"===d&&!e.role)return tr(Mt(`role is required for dfmea mitigation links. Choose one of: ${si.join(", ")}`));if("hara"===d&&e.role)return tr(Mt("role is not applicable to hara mitigation links; omit it (roles are a dfmea concept)"));let m=ai;if("hara"===d){m=vi("hara",o?.analysis_props);const n=l?.asil;if(m!==ai&&"string"==typeof n&&n.length>0&&n!==m&&!0!==e.override)return tr((r=m,{...Ut(`ASIL conflict: the target requirement already carries asil '${t=n}' but ${i=s.id} computes '${r}'.`,["Pass override=true to apply the HARA value; the override is history-logged like a dialog accept","Or align the row's S/E/C ratings with the existing ASIL before linking"]),details:{code:"asil_conflict",current_value:t,incoming_value:r,source_row:i,hint:"pass override=true to apply the HARA value; the override is history-logged like a dialog accept"}}))}const p="dfmea"===d?e.role:null,{error:u}=await n.rpc("apply_link_change",{p_project_id:e.project_id,p_from_id:e.row_id,p_to_id:e.req_id,p_type:"mitigated_by",p_action:"add",p_user_id:a,p_event_source:"mcp",p_expected_from_updated_at:s.updated_at,p_expected_to_updated_at:c.updated_at,p_role:p});if(u)throw new Error(u.message);let _;if(await n.from("change_history").insert({item_id:e.row_id,project_id:e.project_id,changed_by:a,event_type:"updated",old_data:null,new_data:{relationship_change:{action:"add",type:"mitigated_by",from:e.row_id,to:e.req_id,...p?{role:p}:{}}},actor:cs(),session_id:ds()}).then(({error:e})=>{e&&process.stderr.write(`[atoms-mcp] Warning: change_history log failed: ${e.message}\n`)}),"hara"===d&&m!==ai){const t=await dr(n,e.project_id,e.req_id);if(t){const r=t.data,i=r?.asil??null,o=r?.asil_source??null,d=Na(r);d.asil=m,d.asil_source=s.id;const c=(new Date).toISOString();d.metadata={...d.metadata??{},updated_at:c,updated_by:a};const{error:l}=await n.rpc("save_item_with_revision",{p_item_id:t.id,p_project_id:e.project_id,p_type:t.type,p_title:t.title,p_data:d,p_user_id:a,p_event_source:"mcp",p_event_session_id:ds()??null,p_diff_summary:null,p_expected_updated_at:t.updated_at});if(l)throw new Error(l.message);await n.from("change_history").insert({item_id:t.id,project_id:e.project_id,changed_by:a,event_type:"updated",old_data:{asil:i,asil_source:o},new_data:{asil:m,asil_source:s.id},actor:cs(),session_id:ds(),reason:!0===e.override?`ASIL override accepted via atoms_link_mitigation (was '${i??"unset"}', now '${m}' from ${s.id})`:`ASIL set via atoms_link_mitigation from ${s.id}`}).then(({error:e})=>{e&&process.stderr.write(`[atoms-mcp] Warning: change_history log failed: ${e.message}\n`)}),_=m}}return er(Ht({row_id:e.row_id,req_id:e.req_id,type:"mitigated_by",role:p,..._?{asil_written:_}:{}}))}catch(e){return tr(Qt(e))}var t,r,i}async function Ja(e){try{const t=await $e(),r=await Va(t,e.project_id,e.analysis_id);if(!r.ok)return r.result;const i=r.rows.filter(e=>e.is_gap).map(e=>({id:e.id,title:e.title,source_req:e.source_req,source_function:"string"==typeof e.source_function?e.source_function:null,computed_level:e.computed_level,rule:e.gap_rule}));return er(Ht({analysis_id:r.analysis.id,subtype:r.analysis.subtype,gaps:i,count:i.length}))}catch(e){return tr(Qt(e))}}q(Ta,{createAnalysisHandler:()=>Fa,fillRowHandler:()=>Wa,getAnalysisHandler:()=>Ba,getGapsHandler:()=>Ja,linkMitigationHandler:()=>za});var Qa,Ga,Ya,Ka,Za,Xa,es,ts,rs,is,ns=U({"src/tools/analysis.ts"(){Ge(),hr(),ms(),ir(),Oi(),Ua=new Set(["safety-goal","failure-mode"])}}),as={};function ss(t){K(t.toolName)&&function(t,r){const i=`ui://${r.toolName}/${r.htmlFile}`,n=function(e,t){const r=h(import.meta.url),i=d.dirname(r);return d.resolve(i,"apps","src","apps",e,t)}(r.appName,r.htmlFile),a=[...rr.resourceDomains,...r.extraResourceDomains??[]];w(t,r.toolName,{title:r.title,description:r.description,inputSchema:r.inputSchema,annotations:r.annotations,_meta:{ui:{resourceUri:i}}},r.handler),b(t,`${r.title} UI`,i,{mimeType:v},async()=>{const t=await e.readFile(n,"utf-8");return{contents:[{uri:i,mimeType:v,text:t,_meta:{ui:{csp:{resourceDomains:a}}}}]}})}(Ka,t)}function os(e,t){return async r=>{const i=P(J(),e);if(!i.allowed)return tr(Ut(`Tool ${e} is blocked by org policy.`,[N(i.reason),"Contact your org admin to adjust the MCP access policy."]));try{!function(e){const t=_t();if(!t)return;if("object"!=typeof e||null===e)return;const r=e.project_id;if("string"==typeof r&&r!==t)throw new mt(t,r)}(r)}catch(e){if(e instanceof mt)return tr(Nt(e.expectedProjectId));throw e}{const e=J(),t=r?.project_id;if(null!==e.project_scope&&"string"==typeof t&&!e.project_scope.includes(t))return tr(Ut("Tool call rejected: project_id is outside your org's MCP project scope.",[`Allowed projects: ${e.project_scope.length} project${1===e.project_scope.length?"":"s"} configured by your admin`,"Use atoms_list_projects to see which projects you can access in this session"]))}let n,a;try{a=await $e(),n=We()}catch{return t(r)}const s=await async function(e,t){const r=parseInt(process.env.ATOMS_RATE_LIMIT_RPM??"",10)||ot,i=new Date,n=new Date(i.getTime()-dt);try{const{count:a,error:s}=await t.from("mcp_rate_limits").select("id",{count:"exact",head:!0}).eq("user_id",e).gte("requested_at",n.toISOString());if(s)throw new Error(s.message);if((a??0)>=r){const{data:r,error:a}=await t.from("mcp_rate_limits").select("requested_at").eq("user_id",e).gte("requested_at",n.toISOString()).order("requested_at",{ascending:!0}).limit(1).maybeSingle();if(a)throw new Error(a.message);const s=(r?new Date(r.requested_at).getTime():i.getTime())+dt-i.getTime();return{allowed:!1,retryAfterSeconds:Math.max(1,Math.ceil(s/1e3))}}t.from("mcp_rate_limits").insert({user_id:e,requested_at:i.toISOString()}).then(({error:e})=>{e&&process.stderr.write(`[rate-limiter] insert failed: ${e.message}\n`)});const o=new Date(i.getTime()-2*dt);return t.from("mcp_rate_limits").delete().eq("user_id",e).lt("requested_at",o.toISOString()).then(()=>{}),{allowed:!0}}catch(e){return process.stderr.write(`[rate-limiter] DB check failed (${e instanceof Error?e.message:String(e)}), falling back to allow.\n`),{allowed:!0}}}(n,a);if(!s.allowed)return tr(Lt(s.retryAfterSeconds));if("1"===process.env.ATOMS_MCP_LOCKDOWN||"true"===process.env.ATOMS_MCP_LOCKDOWN){const e=r?.project_id;if("string"==typeof e)try{const{requireWriteAccess:t}=await Promise.resolve().then(()=>(Ge(),Pe));await t(a,e)}catch(e){if(e instanceof Error&&"VIEWER_ROLE"===e.message){const{accessDeniedError:e}=await Promise.resolve().then(()=>(ir(),Ot));return tr(e("Viewer (lockdown mode: read access requires editor or admin)"))}}}const o=function(){const e=performance.now();return()=>Math.round(performance.now()-e)}();let d,c="success";try{const e=await t(r),i=e;if(!0===i?.isError){c="error";const e=i?.content;if(e?.[0]?.text)try{d=JSON.parse(e[0].text).message}catch{}}return e}catch(e){throw c="error",d=e instanceof Error?e.message:String(e),e}finally{const t={tool_name:e,params:r,status:c,duration_ms:o(),error_msg:d,project_id:r.project_id,session_id:Xa,client_name:es};try{!function(e,t,r){const i=st(r.params);e.from("mcp_audit_log").insert({user_id:t,tool_name:r.tool_name,params:i,status:r.status,duration_ms:r.duration_ms,error_msg:r.error_msg??null,project_id:r.project_id??null,session_id:r.session_id??null,client_name:r.client_name??null}).then(({error:e})=>{e&&process.stderr.write(`[audit] Failed to log: ${e.message}\n`)})}(await $e(),n,t)}catch{}}}}function ds(){return Xa}function cs(){return ts}function ls(){return es}q(as,{ATOMS_MCP_SERVER_VERSION:()=>Ya,getMcpActor:()=>cs,getMcpClientName:()=>ls,getMcpSessionId:()=>ds,server:()=>Ka});var ms=U({"src/server.ts"(){pt(),ut(),ee(),ht(),Q(),B(),Et(),It(),Ge(),ir(),nr(),Qa=S(import.meta.url),Ga=Qa("../package.json"),Ya=Ga.version,Ka=new j({name:"atoms-mcp-server",version:Ya}),Za=(e,t,r)=>{if(K(e))return Ka.registerTool(e,t,r)},Xa=u(),es=gt(process.env.ATOMS_CLIENT_NAME),ts=function(){const e=gt(es);return"cursor"===e?"mcp_cursor":"copilot"===e?"mcp_copilot":"mcp_claude"}(),Za("atoms_status",{title:"ATOMS MCP Health Check",description:'Check the health and authentication status of the ATOMS MCP server.\n\nCall this FIRST if other atoms tools fail or return auth errors.\nThis tool works WITHOUT authentication — it checks whether credentials\nexist and are valid, and tells the user exactly what to do if not.\n\nArgs: None\n\nReturns:\n { status: "authenticated"|"not_authenticated"|"expired", email, message, next_steps }\n\nExamples:\n - "Is ATOMS MCP working?" → atoms_status()\n - "Why are atoms tools failing?" → atoms_status()',inputSchema:{},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1}},async()=>{try{const{readCredentials:e,isTokenValid:t}=await Promise.resolve().then(()=>(fe(),te)),r=await e();if(!r)return{content:[{type:"text",text:JSON.stringify({status:"not_authenticated",message:"No ATOMS credentials found. You need to login first.",next_steps:["1. Open a terminal on your machine","2. Run: npx @atoms-tech/atoms-mcp login","3. Complete the login in your browser","4. Restart Claude Desktop (quit and reopen)","5. Try your query again"]},null,2)}]};if(!t(r))try{const{getValidToken:e}=await Promise.resolve().then(()=>(Me(),Ae)),{email:t}=await e(),{invalidateClient:r}=await Promise.resolve().then(()=>(Ge(),Pe));return r(),{content:[{type:"text",text:JSON.stringify({status:"authenticated",email:t,message:"Token was expired but has been refreshed. You're good to go!"},null,2)}]}}catch{return{content:[{type:"text",text:JSON.stringify({status:"expired",email:r.user_email??"unknown",message:"Your session has expired and could not be refreshed.",next_steps:["1. Open a terminal on your machine","2. Run: npx @atoms-tech/atoms-mcp logout","3. Run: npx @atoms-tech/atoms-mcp login","4. Complete the login in your browser","5. Restart Claude Desktop (quit and reopen)"]},null,2)}]}}return{content:[{type:"text",text:JSON.stringify({status:"authenticated",email:r.user_email??"unknown",message:"ATOMS MCP is connected and authenticated. All tools are ready."},null,2)}]}}catch(e){return{content:[{type:"text",text:JSON.stringify({status:"error",message:`Health check failed: ${e instanceof Error?e.message:String(e)}`,next_steps:["1. Open a terminal","2. Run: npx @atoms-tech/atoms-mcp login","3. Restart Claude Desktop"]},null,2)}]}}}),Za("atoms_list_projects",{title:"List ATOMS Projects",description:'List all projects the authenticated user has access to.\n\nThis is the ENTRY POINT tool. Call this first to discover project IDs,\nthen use those IDs with all other tools.\n\nArgs: None\n\nReturns:\n { status: "success", data: [{ id, name, description, org_id, created_at }] }\n\nExamples:\n - "What projects do I have?" → atoms_list_projects()\n - "Show my projects" → atoms_list_projects()\n\nWorkflow:\n atoms_list_projects → atoms_list_items(project_id) → atoms_get_item(project_id, item_id)',inputSchema:{},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1}},os("atoms_list_projects",async()=>{const{listProjectsHandler:e}=await Promise.resolve().then(()=>(wr(),gr));return e()})),Za("atoms_list_items",{title:"List ATOMS Items",description:'List items in an ATOMS project with optional type/domain/level filters.\n\nThis is the primary ENTRY POINT for discovering items. Use it to browse\nproject contents before drilling into specific items with atoms_get_item.\n\nArgs:\n - project_id (string, UUID): The project to list items from\n - type (string, optional): Filter: requirement|test-case|note|table\n - domain (string, optional): Filter by domain tag\n - level (string, optional): Filter by level (System, Subsystem, Component)\n - limit (number, optional): Max results, 1-200 (default 50)\n - offset (number, optional): Pagination offset (default 0)\n\nReturns:\n { status: "success", data: [{ id, title, type, status, domains, level }], meta: { total_count, limit, offset, has_more } }\n\nExamples:\n - "Show me all requirements" → atoms_list_items(project_id, type="requirement")\n - "What test cases exist?" → atoms_list_items(project_id, type="test-case")\n\nErrors:\n - "Project not found" → verify project_id\n - "Access denied" → check org membership',inputSchema:{project_id:y.string().uuid("Must be a valid project UUID").describe("UUID of the project"),type:y.enum(["requirement","test-case","note","table"]).optional().describe("Filter by item type"),domain:y.string().max(64).optional().describe("Filter by domain tag (e.g., 'Safety', 'Performance')"),level:y.string().max(32).optional().describe("Filter by level (System, Subsystem, Component)"),limit:y.number().int().min(1).max(200).default(50).describe("Max results (default 50, max 200)"),offset:y.number().int().min(0).default(0).describe("Pagination offset (default 0)"),baseline_id:y.string().uuid().optional().describe("Optional baseline UUID. Returns items as they were at that baseline's moment. Mutually exclusive with as_of."),as_of:y.string().datetime().optional().describe("Optional ISO 8601 timestamp. Returns items as they were at that moment. Mutually exclusive with baseline_id.")},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1}},os("atoms_list_items",async e=>{const{listItemsHandler:t}=await Promise.resolve().then(()=>(Sr(),br));return t(e)})),Za("atoms_get_item",{title:"Get ATOMS Item Details",description:'Get full details of a single item including relationships, test history, and ownership.\n\nUse after atoms_list_items or atoms_search to drill into a specific item.\n\nArgs:\n - project_id (string, UUID): The project containing the item\n - item_id (string): Item ID (e.g., "REQ-001", "TC-050")\n\nReturns:\n Full WorkItem with relationships, test runs, ownership, metadata.\n\nExamples:\n - "Show me requirement REQ-001" → atoms_get_item(project_id, "REQ-001")\n - "Get test case details" → atoms_get_item(project_id, "TC-050")',inputSchema:{project_id:y.string().uuid().describe("UUID of the project"),item_id:y.string().min(1).max(20).describe("Item ID (e.g., REQ-001)"),baseline_id:y.string().uuid().optional().describe("Optional baseline UUID. Returns the item as it was at that baseline's moment. Mutually exclusive with as_of."),as_of:y.string().datetime().optional().describe("Optional ISO 8601 timestamp. Returns the item as it was at that moment. Mutually exclusive with baseline_id.")},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1}},os("atoms_get_item",async e=>{const{getItemHandler:t}=await Promise.resolve().then(()=>(Ir(),xr));return t(e)})),Za("atoms_search",{title:"Search ATOMS Items",description:'Full-text search across items in a project. Searches title, body, and summary.\n\nAlternative entry point to atoms_list_items when you know what you\'re looking for.\n\nArgs:\n - project_id (string, UUID): The project to search in\n - query (string): Search text (max 1000 chars)\n - type (string, optional): Filter by type\n - asset (string, optional): Filter to items with at least one run on the given asset\n (VIN, module, lot, ...). Case-sensitive exact match.\n - limit (number, optional): Max results, 1-100 (default 25)\n\nReturns:\n Matching items with @basic fields, ranked by relevance.\n\nExamples:\n - "Find braking requirements" → atoms_search(project_id, "braking system")\n - "Search for safety tests" → atoms_search(project_id, "safety", type="test-case")\n - "Tests that ran on this VIN" → atoms_search(project_id, "", asset="WEV...0005")',inputSchema:{project_id:y.string().uuid().describe("UUID of the project"),query:y.string().min(1).max(1e3).describe("Search text"),type:y.enum(["requirement","test-case","note","table"]).optional().describe("Filter by item type"),asset:y.string().max(200).optional().describe("Filter to items with at least one run on this asset (case-sensitive exact)"),limit:y.number().int().min(1).max(100).default(25).describe("Max results (default 25, max 100)"),baseline_id:y.string().uuid().optional().describe("Optional baseline UUID. Searches within the project state at that baseline's moment.")},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1}},os("atoms_search",async e=>{const{searchHandler:t}=await Promise.resolve().then(()=>(Rr(),Ar));return t(e)})),Za("atoms_semantic_search",{title:"Semantic Search ATOMS Items",description:'Meaning-based search across items using pgvector embeddings (GTE-small, 384d).\n\nUse when atoms_search (exact/substring) misses conceptually related items.\nBest for natural-language concept queries; atoms_search is better for exact phrases or IDs.\n\nArgs:\n - project_id (string, UUID): The project to search in\n - query (string): Natural-language concept query (max 1000 chars)\n - type (string, optional): Filter by type\n - limit (number, optional): Max results, 1-50 (default 10)\n\nReturns:\n Semantically similar items ranked by cosine similarity.\n\nExamples:\n - "Find braking-related requirements" → atoms_semantic_search(project_id, "braking deceleration stopping distance")\n - "Safety-related test cases" → atoms_semantic_search(project_id, "hazard risk mitigation safety", type="test-case")',inputSchema:{project_id:y.string().uuid().describe("UUID of the project"),query:y.string().min(1).max(1e3).describe("Natural-language concept query"),type:y.enum(["requirement","test-case","note","table"]).optional().describe("Filter by item type"),limit:y.number().int().min(1).max(50).default(10).describe("Max results (default 10, max 50)")},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1}},os("atoms_semantic_search",async e=>{const{semanticSearchHandler:t}=await Promise.resolve().then(()=>(Cr(),Dr));return t(e)})),ss({appName:"coverage",htmlFile:"coverage-app.html",toolName:"atoms_get_coverage",title:"Get Test Coverage Report",description:'Find requirements without linked test cases (coverage gaps).\n\nEssential for compliance reporting in safety-critical industries.\n\nIn MCP App hosts (Claude, ChatGPT), renders a visual coverage heatmap.\nFalls back to JSON for non-UI clients.\n\nArgs:\n - project_id (string, UUID): The project to analyze\n - domain (string, optional): Filter by domain\n - level (string, optional): Filter by level\n - baseline_id (string, UUID, optional): coverage at that baseline\'s moment\n - as_of (string ISO 8601, optional): coverage at that moment\n baseline_id and as_of are mutually exclusive.\n\nReturns:\n { covered, uncovered, total, coverage_percent, uncovered_items: [...] }\n\nExamples:\n - "What\'s our test coverage?" → atoms_get_coverage(project_id)\n - "Safety coverage gaps?" → atoms_get_coverage(project_id, domain="Safety")\n - "Coverage at PDR submission" → atoms_get_coverage(project_id, baseline_id="...")',inputSchema:{project_id:y.string().uuid().describe("UUID of the project"),domain:y.string().max(64).optional().describe("Filter by domain tag"),level:y.string().max(32).optional().describe("Filter by level"),baseline_id:y.string().uuid().optional().describe("Compute coverage as of this baseline's snapshot moment"),as_of:y.string().datetime().optional().describe("Compute coverage as of this ISO 8601 moment")},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1},handler:os("atoms_get_coverage",async e=>{const{getCoverageHandler:t}=await Promise.resolve().then(()=>(Mr(),Ur));return t(e)})}),Za("atoms_verification_status",{title:"Get Release Verification Status",description:'Release-bound verification state per requirement, matching the in-app projection.\n\nEach requirement\'s state is derived from the runs bound to a formal Release:\nverified, failing, incomplete, or not-run, plus a stale flag and the\n(points_run / points_total) counts for its parameterized test points.\n\nArgs:\n - project_id (string, UUID): The project to report on\n - release_id (string, UUID, optional): A formal Release (a baselines row with\n a release_version). Defaults to the project\'s CURRENT Release\n (release_status = \'released\'). Errors if the project has none.\n - include_gaps (boolean, optional, default false): Also return the not-run\n (test x unit x value) points for the Release.\n\nReturns:\n { release: {id, version, status}, readiness: {...roll-up counts...},\n requirements: [{requirement_id, state, stale, points_total, points_run}],\n gaps?: [{tc_id, asset, point_key}] }\n An empty requirements array with a "hint" field means the projection has not\n been rebuilt for this project yet, NOT that everything is unverified.\n\nExamples:\n - "Are we ready to ship 2.0?" → atoms_verification_status(project_id)\n - "What\'s still not run for release R?" → atoms_verification_status(project_id, release_id="...", include_gaps=true)',inputSchema:{project_id:y.string().uuid().describe("UUID of the project"),release_id:y.string().uuid().optional().describe("Formal Release (baselines row) UUID; defaults to the project's current Release"),include_gaps:y.boolean().default(!1).describe("Include the not-run (test x unit x value) points for the Release")},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1}},os("atoms_verification_status",async e=>{const{verificationStatusHandler:t}=await Promise.resolve().then(()=>(Nr(),Pr));return t(e)})),Za("atoms_get_history",{title:"Get Item Change History",description:"Audit trail for an item — who changed what, when, and whether it was human or AI.\n\nShows actor attribution (user vs mcp_<client>) and session grouping.\n\nArgs:\n - project_id (string, UUID): The project containing the item\n - item_id (string): Item ID\n - limit (number, optional): Max entries (default 20, max 100)\n\nReturns:\n Change entries with actor, session_id, event_type, changed_at, fields_changed.",inputSchema:{project_id:y.string().uuid().describe("UUID of the project"),item_id:y.string().min(1).max(20).describe("Item ID"),limit:y.number().int().min(1).max(100).default(20).describe("Max entries (default 20)")},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1}},os("atoms_get_history",async e=>{const{getHistoryHandler:t}=await Promise.resolve().then(()=>(Wr(),Lr));return t(e)})),ss({appName:"mermaid",htmlFile:"mermaid-app.html",toolName:"atoms_export_mermaid",title:"Export Mermaid Diagram",description:"Generate a Mermaid diagram of the requirement/test hierarchy.\n\nOutput is paste-ready for markdown docs. Shows parent-child relationships\nand requirement-to-test-case verification links.\n\nIn MCP App hosts (Claude, ChatGPT), renders an interactive pan/zoom diagram.\nFalls back to Mermaid text for non-UI clients.\n\nArgs:\n - project_id (string, UUID): The project to visualize\n - root_item_id (string, optional): Start node (default: all roots)\n - depth (number, optional): Max depth (default 3, max 10)\n - include_tests (boolean, optional): Show test case links (default true)\n - baseline_id (string, UUID, optional): render the graph at that baseline's moment\n - as_of (string ISO 8601, optional): render the graph at that moment\n baseline_id and as_of are mutually exclusive.\n\nReturns:\n Mermaid graph string.",inputSchema:{project_id:y.string().uuid().describe("UUID of the project"),root_item_id:y.string().max(20).optional().describe("Root item ID to start from"),depth:y.number().int().min(1).max(10).default(3).describe("Max depth"),include_tests:y.boolean().default(!0).describe("Include test case links"),baseline_id:y.string().uuid().optional().describe("Render the graph as of this baseline's snapshot moment"),as_of:y.string().datetime().optional().describe("Render the graph as of this ISO 8601 moment")},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1},handler:os("atoms_export_mermaid",async e=>{const{exportMermaidHandler:t}=await Promise.resolve().then(()=>(Kr(),Gr));return t(e)})}),Za("atoms_create_item",{title:"Create ATOMS Item",description:'Create a new requirement, test case, or note in a project.\n\nRequires editor or admin role. Changes are logged with AI actor attribution.\n\nArgs:\n - project_id (string, UUID): Target project\n - type (string): requirement|test-case|note\n - title (string): Item title (max 500 chars)\n - body (string, optional): Rich text body\n - summary (string, optional): Brief summary\n - domains (string[], optional): Domain tags (a tag flagged as a function is not a domain and is rejected)\n - level (string, optional): System|Subsystem|Component\n - parent_ids (string[], optional): Parent item IDs to link\n - fields (object, optional): Project-field values, e.g. {"Verification Method": "Design Review"}. Keys must be defined via atoms_create_field first and values must match each field type.\n\nReturns:\n Created item with generated ID (e.g., "REQ-058")\n\nSide effects:\n - Logs to change_history with actor=\'mcp_<client>\'\n - Creates relationships if parent_ids provided',inputSchema:{project_id:y.string().uuid().describe("UUID of the target project"),type:y.enum(["requirement","test-case","note"]).describe("Item type"),title:y.string().min(1).max(500).describe("Item title"),body:y.string().max(5e4).optional().describe("Rich text body"),summary:y.string().max(2e3).optional().describe("Brief summary"),domains:y.array(y.string().max(64)).max(20).optional().describe("Domain tags"),level:y.string().max(32).optional().describe("System|Subsystem|Component"),parent_ids:y.array(y.string().max(20)).max(50).optional().describe("Parent item IDs to link"),fields:y.record(y.string().max(60),y.string().max(1e4)).optional().describe("Project-field values keyed by defined field name"),echo:y.boolean().optional().describe("false → lean {id,type} response, saves ~580 tokens")},annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!1,openWorldHint:!1}},os("atoms_create_item",async e=>{const{createItemHandler:t}=await Promise.resolve().then(()=>(Yi(),Qi));return t(e)})),rs={steps:y.array(y.object({id:y.string().describe("Stable step id within the item"),instruction:y.string().describe("What the tester does"),test_data:y.string().optional().describe("Optional test data for the step"),expected:y.string().describe("Expected result")}).passthrough()).optional().describe("Test-case script steps: array of {id, instruction, test_data?, expected}. Max 50 steps, enforced by the DB trigger."),params:y.array(y.object({name:y.string().describe("Parameter name; steps reference it as {name}"),unit:y.string().optional().describe("Optional unit (e.g. km/h, V, m)"),values:y.array(y.union([y.string(),y.number()])).describe("Value set the test sweeps")}).passthrough()).optional().describe("Matrix parameters: array of {name, unit, values[]}. Matrix points (product of value-set sizes) capped at 64 by the DB trigger.")},Za("atoms_update_item",{title:"Update ATOMS Item",description:'Update an existing item\'s fields. Only provided fields are changed.\n\nRequires editor or admin role. Logs old/new data diff to change history.\n\nArgs:\n - project_id (string, UUID): Project containing the item\n - item_id (string): Item to update\n - title, body, summary, domains, level, status: Fields to update (all optional) (a tag flagged as a function is not a domain and is rejected)\n - fields (object, optional): Project-field values, e.g. {"Verification Method": "Design Review"}. Keys must be defined via atoms_create_field; merged per key, "" clears a key; non-empty values must match each field type\n - steps (array, optional): Test-case script steps [{id, instruction, test_data?, expected}]. Max 50 steps (DB-enforced)\n - params (array, optional): Matrix parameters [{name, unit, values[]}]. Matrix points capped at 64 (DB-enforced)\n - expected_updated_at (string, optional): Pass metadata.updated_at from atoms_get_item to reject stale writes\n\nReturns:\n Updated item.',inputSchema:{project_id:y.string().uuid().describe("UUID of the project"),item_id:y.string().min(1).max(20).optional().describe("Item ID (single form)"),title:y.string().min(1).max(500).optional().describe("New title"),body:y.string().max(5e4).optional().describe("New body"),summary:y.string().max(2e3).optional().describe("New summary"),domains:y.array(y.string().max(64)).max(20).optional().describe("New domain tags (a tag flagged as a function is not a domain and is rejected)"),level:y.string().max(32).optional().describe("New level"),status:y.enum(["passed","failed","blocked","not-run"]).optional().describe("Test case status"),fields:y.record(y.string().max(60),y.string().max(1e4)).optional().describe('Project-field values keyed by defined field name; "" clears a key'),...rs,expected_updated_at:yt.optional().describe("metadata.updated_at value from atoms_get_item; rejects stale writes"),echo:y.boolean().optional().describe("false → lean {id,updated_fields} response, saves ~580 tokens"),reason:y.string().max(500).optional().describe("Change rationale, persisted in audit history"),updates:y.array(y.object({item_id:y.string().min(1).max(20),title:y.string().min(1).max(500).optional(),body:y.string().max(5e4).optional(),summary:y.string().max(2e3).optional(),domains:y.array(y.string().max(64)).max(20).optional(),level:y.string().max(32).optional(),status:y.enum(["passed","failed","blocked","not-run"]).optional(),fields:y.record(y.string().max(60),y.string().max(1e4)).optional(),...rs,expected_updated_at:yt.optional()})).optional().describe("Array form: update many items in one call"),idempotency_key:y.string().max(128).optional().describe("Safe retry key (24h TTL)"),continue_on_error:y.boolean().optional().describe("Allow partial success (default false = atomic)")},annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1}},os("atoms_update_item",async e=>{const{updateItemHandler:t}=await Promise.resolve().then(()=>(an(),en));return t(e)})),Za("atoms_delete_item",{title:"Delete ATOMS Item",description:'Soft-delete an item (sets deleted_at, preserves for audit trail).\n\nRequires editor or admin role. The item is never truly removed.\n\nWhen ATOMS_MCP_REQUIRE_CONFIRMATION is set on the server, this tool uses a\ntwo-step flow: the first call returns a preview + confirmation_token; the\nsecond call (with the token) executes. Tokens expire in 60s and are bound\nto (tool, project_id, item_id).\n\nArgs:\n - project_id (string, UUID): Project containing the item\n - item_id (string): Item to delete\n - confirmation_token (string, optional): Token from a prior preview call\n\nReturns:\n Confirmation with deleted item summary, or { status: "confirmation_required",\n preview, confirmation_token } when the confirmation gate is active.',inputSchema:{project_id:y.string().uuid().describe("UUID of the project"),item_id:y.string().min(1).max(20).describe("Item ID to soft-delete"),confirmation_token:y.string().max(500).optional().describe("Token from a prior preview call (only used when ATOMS_MCP_REQUIRE_CONFIRMATION is set)")},annotations:{readOnlyHint:!1,destructiveHint:!0,idempotentHint:!0,openWorldHint:!1}},os("atoms_delete_item",async e=>{const{deleteItemHandler:t}=await Promise.resolve().then(()=>(fn(),un));return t(e)})),Za("atoms_restore_item",{title:"Restore ATOMS Item",description:'Undo a soft-delete — restores an item that was previously deleted with atoms_delete_item.\n\nOnly works on items that have been soft-deleted (still in the database with deleted_at set).\nHas no effect if the item is not deleted. Logs the restoration to change_history.\n\nArgs:\n - project_id (string, UUID): Project containing the item\n - item_id (string): ID of the deleted item to restore\n\nReturns:\n Confirmation with the restored item\'s title and type.\n\nExamples:\n - "Restore the deleted requirement REQ-042" → atoms_restore_item(project_id, "REQ-042")',inputSchema:{project_id:y.string().uuid().describe("UUID of the project"),item_id:y.string().max(256).describe("ID of the soft-deleted item to restore")},annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1}},os("atoms_restore_item",async e=>{const{restoreItemHandler:t}=await Promise.resolve().then(()=>(yn(),hn));return t(e)})),Za("atoms_link_items",{title:"Link ATOMS Items",description:"Add or remove relationships between items.\n\nSupports: parent, child, related, verifies, verified_by.\nUpdates both items' JSONB data and the shadow table.\n\nArgs:\n - project_id (string, UUID): Project containing both items\n - from_id (string): Source item\n - to_id (string): Target item\n - type (string): parent|child|related|verifies|verified_by\n - action (string): add|remove\n - expected_from_updated_at / expected_to_updated_at (string, optional): pass metadata.updated_at values from atoms_get_item to reject stale link commands\n\nReturns:\n Updated relationship state for both items.",inputSchema:{project_id:y.string().uuid().describe("UUID of the project"),from_id:y.string().min(1).max(20).optional().describe("Source item ID (single form)"),to_id:y.string().min(1).max(20).optional().describe("Target item ID (single form)"),type:y.enum(["parent","child","related","verifies","verified_by"]).optional().describe("Relationship type"),action:y.enum(["add","remove"]).optional().describe("Add or remove"),expected_from_updated_at:yt.optional().describe("metadata.updated_at for from_id from atoms_get_item"),expected_to_updated_at:yt.optional().describe("metadata.updated_at for to_id from atoms_get_item"),echo:y.boolean().optional().describe("false → lean response"),reason:y.string().max(500).optional().describe("Change rationale, persisted in audit history"),operations:y.array(y.object({action:y.enum(["add","remove"]),from_id:y.string().min(1).max(20),to_id:y.string().min(1).max(20),type:y.enum(["parent","child","related","verifies","verified_by"]),expected_from_updated_at:yt.optional(),expected_to_updated_at:yt.optional()})).optional().describe("Array form: batch relationship ops. Always atomic with DAG cycle detection."),idempotency_key:y.string().max(128).optional().describe("Safe retry key (24h TTL)")},annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1}},os("atoms_link_items",async e=>{const{linkItemsHandler:t}=await Promise.resolve().then(()=>(vn(),wn));return t(e)})),ss({appName:"import",htmlFile:"import-app.html",toolName:"atoms_bulk_import",title:"Bulk Import ATOMS Items",description:"Bulk create multiple items in a single tool call. Essential for AI agents\nthat generate 20+ requirements at once.\n\nChecks write access once, generates sequential IDs, batch inserts all items,\nand reports per-item errors without aborting the entire batch.\n\nIn MCP App hosts (Claude, ChatGPT), renders an interactive results table.\nFalls back to JSON for non-UI clients.\n\nArgs:\n - project_id (string, UUID): Target project\n - items (array): Up to 100 items to create, each with:\n - type (string): requirement|test-case|note\n - title (string): Item title (max 500 chars)\n - body (string, optional): Rich text body\n - summary (string, optional): Brief summary\n - domains (string[], optional): Domain tags (a tag flagged as a function is not a domain and is rejected)\n - level (string, optional): System|Subsystem|Component\n - parent_id (string, optional): Parent item ID to link\n\nReturns:\n { created: number, items: [{ id, title, type }], errors: [] }\n\nLimits:\n - Maximum 100 items per call\n - If any item fails, others still succeed — errors reported separately\n\nSide effects:\n - Logs to change_history with actor='mcp_<client>' for each created item\n - Creates parent relationships for items with parent_id",inputSchema:{project_id:y.string().uuid().describe("UUID of the target project"),items:y.array(y.object({type:y.enum(["requirement","test-case","note"]).describe("Item type"),title:y.string().min(1).max(500).describe("Item title"),body:y.string().max(5e4).optional().describe("Rich text body"),summary:y.string().max(2e3).optional().describe("Brief summary"),domains:y.array(y.string().max(64)).max(20).optional().describe("Domain tags"),level:y.string().max(32).optional().describe("System|Subsystem|Component"),parent_id:y.string().max(20).optional().describe("Parent item ID to link")})).min(1).max(100).describe("Items to create (1-100)"),confirmation_token:y.string().max(500).optional().describe("Token from a prior preview call (only used when ATOMS_MCP_REQUIRE_CONFIRMATION is set)")},annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!1,openWorldHint:!1},handler:os("atoms_bulk_import",async e=>{const{bulkImportHandler:t}=await Promise.resolve().then(()=>(En(),Sn));return t(e)})}),is={point:y.record(y.string(),y.union([y.string(),y.number()])).optional().describe('Matrix point this run executed, e.g. {"speed": 40}'),release_id:y.string().uuid().optional().describe("Formal Release to bind this run to (a baseline with a release_version)"),build:y.string().optional().describe("Free-text build label"),environment:y.string().optional().describe("Structured environment field, distinct from build"),tooling:y.string().optional().describe("Structured tooling field, distinct from build"),steps_rev:y.string().optional().describe("Item revision id the run's steps were pinned to"),step_results:y.array(y.object({step_id:y.string().describe("Step id from data.steps"),status:y.enum(["passed","failed","blocked","skipped"]).describe("Per-step status"),actual:y.string().optional().describe("Actual-result note"),evidence_url:y.string().optional().describe("Evidence URL (validated server-side)")}).passthrough()).optional().describe("Per-step results [{step_id, status, actual?, evidence_url?}]. When present the run verdict is derived server-side and the top-level result is ignored.")},Za("atoms_record_test_result",{title:"Record Test Result",description:"Record a pass/fail/blocked result for a test case.\n\nAppends to canonical items.data.runs and projects test_results from that\nhistory. Use atoms_get_item to see all results.\n\nArgs:\n - project_id (string, UUID): Project containing the test case\n - item_id (string): Test case ID (e.g., \"TC-001\")\n - result (string): passed|failed|blocked|not-run\n - note (string, optional): Reason or comment for this result\n - asset (string, optional): Asset identifier this run was executed against\n (VIN, module ID, lot number, serial number, etc). Max 200 chars.\n Auto-trimmed; empty/whitespace is treated as omitted. If the asset\n name does not yet exist in the project, it is auto-registered.\n - point, release_id, build, environment, tooling, steps_rev (optional):\n run metadata. release_id must be a formal Release (a baseline with a\n release_version). All validated server-side.\n - step_results (array, optional): per-step outcomes for a stepped run.\n When provided, the verdict is derived server-side from the steps.\n\nReturns:\n Recorded result with timestamp. When asset is provided, also returns\n asset_id and asset_created.\n\nSide effects:\n - Appends to items.data.runs through save_item_with_revision\n - Projects public.test_results from items.data.runs\n - Inserts into assets table if asset name is new in this project\n - Logs to change_history with actor='mcp_<client>'",inputSchema:{project_id:y.string().uuid().describe("UUID of the project"),item_id:y.string().min(1).max(20).describe("Test case ID (e.g., TC-001)"),result:y.enum(["passed","failed","blocked","not-run"]).describe("Test result"),note:y.string().max(500).optional().describe("Reason or comment for this result"),asset:y.string().max(200).optional().describe("Asset identifier this run was executed against (VIN, module ID, lot, etc). Auto-registered if new."),...is},annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!1,openWorldHint:!1}},os("atoms_record_test_result",async e=>{const{recordTestResultHandler:t}=await Promise.resolve().then(()=>(On(),In));return t(e)})),Za("atoms_record_test_results",{title:"Record Test Results (bulk)",description:'Record many runs on one test case in a single atomic call.\n\nOptimized for multi-asset campaigns. Each entry can have its own asset and\nresult. Asset lookup-or-create happens per entry; new asset names are\nauto-registered. Runs are appended to canonical items.data.runs, then\ntest_results is projected from that history. The whole call is one DB\ntransaction; any entry failure rolls back the rest.\n\nUse the singular atoms_record_test_result for one-off runs.\n\nArgs:\n - project_id (string, UUID): Project containing the test case\n - item_id (string): Test case ID (e.g., "TC-001")\n - results (array, 1-100 entries): Each entry has:\n - asset (string, optional): Asset identifier (VIN, module ID, lot)\n - result (string, required): passed|failed|blocked|not-run\n - note (string, optional): Run note\n - date (string, optional): ISO 8601 datetime, default now()\n - point, release_id, build, environment, tooling, steps_rev (optional):\n run metadata; release_id must be a formal Release. Server-validated.\n - step_results (array, optional): per-step outcomes; when present the\n verdict is derived server-side from the steps.\n\nReturns:\n Per-entry outcomes with asset_id, asset_created, run_at, ok flags.\n\nSide effects:\n - Appends to items.data.runs through save_item_with_revision\n - Projects public.test_results from items.data.runs\n - Inserts new rows into assets for any asset name not already registered\n - Logs a single change_history entry summarizing the batch',inputSchema:{project_id:y.string().uuid().describe("UUID of the project"),item_id:y.string().min(1).max(20).describe("Test case ID (e.g., TC-001)"),results:y.array(y.object({asset:y.string().max(200).optional().describe("Asset identifier this run was executed against. Auto-registered if new."),result:y.enum(["passed","failed","blocked","not-run"]).describe("Test result"),note:y.string().max(500).optional().describe("Run note"),date:y.string().optional().describe("ISO 8601 datetime (default: now())"),...is})).min(1).max(100).describe("1 to 100 result entries on this test case")},annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!1,openWorldHint:!1}},os("atoms_record_test_results",async e=>{const{recordTestResultsBulkHandler:t}=await Promise.resolve().then(()=>(Tn(),Rn));return t(e)})),ss({appName:"trace",htmlFile:"trace-app.html",toolName:"atoms_trace",title:"Trace Item Relationships",description:'Walk the traceability graph from a starting item.\n\nAnswers questions like "which requirements does TC-003 verify?" or\n"if I change REQ-001, what\'s affected downstream?"\n\nIn MCP App hosts (Claude, ChatGPT), renders an interactive graph visualization.\nFalls back to flat list for non-UI clients.\n\nArgs:\n - project_id (string, UUID): Project containing the item\n - item_id (string): Starting item ID\n - direction (string): upstream|downstream|both\n - upstream: follow parent/verifies links (dependencies)\n - downstream: follow child/verified_by links (dependents)\n - both: trace in both directions\n - depth (number, optional): Max traversal depth (default 5, max 10)\n - relationship_types (string[], optional): Filter to specific types (parent, child, related, verifies, verified_by, mitigated_by, mitigates)\n\nReturns:\n { root, direction, items: [{ id, title, type, relationship, depth }], total_count }\n\nExamples:\n - "What does TC-003 verify?" → atoms_trace(project_id, "TC-003", "upstream")\n - "What\'s affected by REQ-001?" → atoms_trace(project_id, "REQ-001", "downstream")',inputSchema:{project_id:y.string().uuid().describe("UUID of the project"),item_id:y.string().min(1).max(20).describe("Starting item ID"),direction:y.enum(["upstream","downstream","both"]).describe("Traversal direction"),depth:y.number().int().min(1).max(10).default(5).describe("Max traversal depth (default 5)"),relationship_types:y.array(y.enum(["parent","child","related","verifies","verified_by","mitigated_by","mitigates"])).optional().describe("Filter to specific relationship types")},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1},handler:os("atoms_trace",async e=>{const{traceHandler:t}=await Promise.resolve().then(()=>(Ln(),$n));return t(e)})}),Za("atoms_list_assets",{title:"List Project Assets",description:"List asset identifiers registered in a project.\n\nAssets are free-form per-instance IDs (VINs, module names, lot numbers,\nserial numbers) that test runs are tagged against. They are auto-registered\nthe first time they appear on a run via atoms_record_test_result.\n\nArgs:\n - project_id (string, UUID): Project to list assets for\n - include_archived (boolean, optional): Include archived assets (default false)\n\nReturns:\n { assets: [{ id, name, archived_at, created_at }], total }",inputSchema:{project_id:y.string().uuid().describe("UUID of the project"),include_archived:y.boolean().optional().describe("Include archived assets (default false)")},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1}},os("atoms_list_assets",async e=>{const{listAssetsHandler:t}=await Promise.resolve().then(()=>(Vn(),Fn));return t(e)})),Za("atoms_list_variables",{title:"List Project Variables",description:"List all parameterized variables defined in a project.\n\nVariables are shared values (e.g., backup_camera_latency = 2s) that can be\nreferenced in requirement bodies as {variable_name}.\n\nArgs:\n - project_id (string, UUID): Project to list variables for\n - baseline_id (string, UUID, optional): list variables as they were at that baseline\n - as_of (string ISO 8601, optional): list variables as they were at that moment\n - limit (number, optional): max results (default 50, max 200)\n - offset (number, optional): pagination offset (default 0)\n\nReturns:\n { variables: [{ id, name, value, unit, description }], total }, meta: { total_count, limit, offset, has_more }",inputSchema:{project_id:y.string().uuid().describe("UUID of the project"),baseline_id:y.string().uuid().optional().describe("Optional baseline UUID for time-travel reads"),as_of:y.string().datetime().optional().describe("Optional ISO timestamp for time-travel reads"),limit:y.number().int().min(1).max(200).default(50).describe("Max results (default 50, max 200)"),offset:y.number().int().min(0).default(0).describe("Pagination offset (default 0)")},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1}},os("atoms_list_variables",async e=>{const{listVariablesHandler:t}=await Promise.resolve().then(()=>(ea(),Bn));return t(e)})),Za("atoms_get_variable",{title:"Get Project Variable",description:'Get a specific variable by name, including which items reference it.\n\nArgs:\n - project_id (string, UUID): Project containing the variable\n - variable_name (string): Variable name (e.g., "backup_camera_latency")\n - baseline_id (string, UUID, optional): get the variable as it was at that baseline\n - as_of (string ISO 8601, optional): get the variable as it was at that moment\n\nReturns:\n { variable, referenced_by: [{ id, title, type }], reference_count }',inputSchema:{project_id:y.string().uuid().describe("UUID of the project"),variable_name:y.string().min(1).max(64).describe("Variable name"),baseline_id:y.string().uuid().optional().describe("Optional baseline UUID for time-travel reads"),as_of:y.string().datetime().optional().describe("Optional ISO timestamp for time-travel reads")},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1}},os("atoms_get_variable",async e=>{const{getVariableHandler:t}=await Promise.resolve().then(()=>(ea(),Bn));return t(e)})),Za("atoms_update_variable",{title:"Update Project Variable",description:"Update a variable's value, unit, or description.\n\nArgs:\n - project_id (string, UUID): Project containing the variable\n - variable_name (string): Variable name\n - value (string, optional): New value\n - unit (string, optional): Unit abbreviation (e.g., 's', 'Hz')\n - description (string, optional): Human-readable description\n\nReturns:\n { variable }",inputSchema:{project_id:y.string().uuid().describe("UUID of the project"),variable_name:y.string().min(1).max(64).describe("Variable name"),value:y.string().max(500).optional().describe("New value"),unit:y.string().max(32).optional().describe("Unit abbreviation"),description:y.string().max(2e3).optional().describe("Description")},annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1}},os("atoms_update_variable",async e=>{const{updateVariableHandler:t}=await Promise.resolve().then(()=>(ea(),Bn));return t(e)})),Za("atoms_create_variable",{title:"Create Project Variable",description:'Create one or many parameterized variables for a project.\n\nSingle form: provide name + value.\nArray form: provide variables[] for bulk creation (one atomic INSERT).\n\nVariables can then be embedded in requirement bodies as {variable_name}.\n\nArgs (single form):\n - project_id (string, UUID): Project to add the variable to\n - name (string): Variable name, letters, digits, underscores; must start with letter/underscore\n - value (string): Initial value (e.g., "100", "true", "nominal")\n - unit (string, optional): Unit abbreviation (e.g., "Hz", "ms", "m/s²")\n - description (string, optional): Human-readable description\n - echo (bool, optional): false → lean {name} response (default true)\n\nArgs (array form):\n - project_id (string, UUID): Project UUID\n - variables[]: Array of { name, value, unit?, description? }\n - echo (bool, optional): false → lean {name} list (default true)\n - idempotency_key (string, optional): Dedup key\n\nReturns (single): { variable, message }\nReturns (array): { batch_id, created, variables, message }',inputSchema:{project_id:y.string().uuid().describe("UUID of the project"),name:y.string().min(1).max(64).optional().describe("Variable name (single form)"),value:y.string().min(1).max(500).optional().describe("Initial value (single form)"),unit:y.string().max(32).optional().describe("Unit abbreviation"),description:y.string().max(2e3).optional().describe("Description"),echo:y.boolean().optional().describe("false → lean response (default true)"),variables:y.array(y.object({name:y.string().min(1).max(64).describe("Variable name"),value:y.string().min(1).max(500).describe("Initial value"),unit:y.string().max(32).optional().describe("Unit abbreviation"),description:y.string().max(2e3).optional().describe("Description")})).optional().describe("Array form: batch variable creation. Atomic — all or none."),idempotency_key:y.string().max(128).optional().describe("Dedup key for retries")},annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!1,openWorldHint:!1}},os("atoms_create_variable",async e=>{const{createVariableHandler:t}=await Promise.resolve().then(()=>(ea(),Bn));return t(e)})),Za("atoms_delete_variable",{title:"Delete Project Variable",description:"Delete a project variable. Refuses by default if any items reference it.\n\nArgs:\n - project_id (string, UUID): Project containing the variable\n - name (string): Variable name to delete\n - force (bool, optional): Delete even if items still reference it (default false)\n\nReturns:\n { deleted, name, referenced_by_items, warning? }",inputSchema:{project_id:y.string().uuid().describe("UUID of the project"),name:y.string().min(1).max(64).describe("Variable name to delete"),force:y.boolean().optional().describe("Delete even if items reference it (default false)"),confirmation_token:y.string().max(500).optional().describe("Token from a prior preview call (only used when ATOMS_MCP_REQUIRE_CONFIRMATION is set)")},annotations:{readOnlyHint:!1,destructiveHint:!0,idempotentHint:!1,openWorldHint:!1}},os("atoms_delete_variable",async e=>{const{deleteVariableHandler:t}=await Promise.resolve().then(()=>(ea(),Bn));return t(e)})),Za("atoms_list_domains",{title:"List Project Domains",description:"List all domain tags registered in a project's taxonomy, with item counts.\n\nArgs:\n - project_id (string, UUID): Project to inspect\n\nReturns:\n { domains: [{ name, item_count }], total }",inputSchema:{project_id:y.string().uuid().describe("UUID of the project")},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1}},os("atoms_list_domains",async e=>{const{listDomainsHandler:t}=await Promise.resolve().then(()=>(da(),ta));return t(e)})),Za("atoms_create_domain",{title:"Create Project Domain",description:'Register a new domain tag in the project taxonomy.\n\nOnce created, items can be tagged with this domain via atoms_create_item or atoms_update_item.\n\nArgs:\n - project_id (string, UUID): Project to add the domain to\n - name (string): Domain name (lowercased automatically, e.g., "safety", "can-bus")\n\nReturns:\n { domain: { name }, message }',inputSchema:{project_id:y.string().uuid().describe("UUID of the project"),name:y.string().min(1).max(64).describe("Domain name")},annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!1,openWorldHint:!1}},os("atoms_create_domain",async e=>{const{createDomainHandler:t}=await Promise.resolve().then(()=>(da(),ta));return t(e)})),Za("atoms_list_levels",{title:"List Project Levels",description:"List the project's level taxonomy in order, with each level's L# (positional index) and item count.\n\nArgs:\n - project_id (string, UUID): Project to list levels for\n\nReturns:\n { levels: [{ name, prefix, item_count }], total }",inputSchema:{project_id:y.string().uuid().describe("UUID of the project")},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1}},os("atoms_list_levels",async e=>{const{listLevelsHandler:t}=await Promise.resolve().then(()=>(hi(),Zr));return t(e)})),Za("atoms_create_level",{title:"Create Project Level",description:'Register a new level in the project taxonomy so items can be tagged with it and it shows in the Strata view. Appended to the ordered level list; its L# is the next index. Names are user-defined labels (lowercased automatically); matching is case-insensitive.\n\nArgs:\n - project_id (string, UUID): Project to add the level to\n - name (string): Level name, e.g. "feature", "system", "subsystem"\n\nReturns:\n { level: { name, prefix }, message }',inputSchema:{project_id:y.string().uuid().describe("UUID of the project"),name:y.string().min(1).max(64).describe("Level name (user-defined label)")},annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!1,openWorldHint:!1}},os("atoms_create_level",async e=>{const{createLevelHandler:t}=await Promise.resolve().then(()=>(hi(),Zr));return t(e)})),Za("atoms_list_fields",{title:"List Project Fields",description:'List the project\'s custom field definitions (project-scoped key-value slots on items, e.g. "Verification Method"), with counts of items carrying a value.\n\nField values are set per item via atoms_create_item / atoms_update_item fields.\n\nArgs:\n - project_id (string, UUID): Project to inspect\n\nReturns:\n { fields: [{ name, type, options, item_count }], total }',inputSchema:{project_id:y.string().uuid().describe("UUID of the project")},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1}},os("atoms_list_fields",async e=>{const{listFieldsHandler:t}=await Promise.resolve().then(()=>(Ji(),Di));return t(e)})),Za("atoms_create_field",{title:"Create Project Field",description:'Define a project custom field so items can carry a value for it (shown as a Matrix column, e.g. "Verification Method").\n\nThe name keeps its casing; uniqueness is case-insensitive. Type is one of text, long, bool, date, person, select, number, link. Select fields require options. Field definitions never carry visibility rules, permissions, or workflow logic.\n\nArgs:\n - project_id (string, UUID): Project to define the field in\n - name (string): Field name, 1-60 characters (e.g. "Verification Method", "Essential Output?")\n - type (string, optional): Defaults to text\n - options (string[], optional): Required when type=select\n - scope (string, optional): project (default) or organization (org admin only)\n\nReturns:\n { field: { name, type, options }, message }',inputSchema:{project_id:y.string().uuid().describe("UUID of the project"),name:y.string().min(1).max(60).describe("Field name (display name, 1-60 chars)"),type:y.enum(["text","long","bool","date","person","select","number","link"]).optional().describe("Field type, defaults to text"),options:y.array(y.string().min(1).max(120)).max(100).optional().describe("Allowed values for select fields"),scope:y.enum(["project","organization"]).optional().describe("Create in this project or copy to every project in the organization")},annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!1,openWorldHint:!1}},os("atoms_create_field",async e=>{const{createFieldHandler:t}=await Promise.resolve().then(()=>(Ji(),Di));return t(e)})),Za("atoms_delete_field",{title:"Delete Project Field",description:"Delete a project field definition. Existing item values are left in item JSON but no longer render as Matrix columns until a matching field is created again.\n\nArgs:\n - project_id (string, UUID): Project containing the field\n - name (string): Field name to delete",inputSchema:{project_id:y.string().uuid().describe("UUID of the project"),name:y.string().min(1).max(60).describe("Field name")},annotations:{readOnlyHint:!1,destructiveHint:!0,idempotentHint:!1,openWorldHint:!1}},os("atoms_delete_field",async e=>{const{deleteFieldHandler:t}=await Promise.resolve().then(()=>(Ji(),Di));return t(e)})),Za("atoms_reorder_fields",{title:"Reorder Project Fields",description:"Reorder project field definitions. Definition order drives the Column Picker listing and a fresh user's Matrix column order.\n\nOrdering only: no definition is added, dropped, renamed, or retyped, and no item value moves. Use this instead of deleting and recreating definitions to change their order.\n\nArgs:\n - project_id (string, UUID): Project containing the fields\n - names (string[]): Every existing field name exactly once, in the order you want them. A list that is not a permutation of the current names is refused, not partially applied.",inputSchema:{project_id:y.string().uuid().describe("UUID of the project"),names:y.array(y.string().min(1).max(60)).describe("Every existing field name exactly once, in the desired order")},annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1}},os("atoms_reorder_fields",async e=>{const{reorderFieldsHandler:t}=await Promise.resolve().then(()=>(Ji(),Di));return t(e)})),Za("atoms_update_domain",{title:"Rename Project Domain",description:"Rename a domain tag. Updates the taxonomy and propagates to all tagged items.\n\nUse dry_run=true first to see how many items will be affected before committing.\n\nArgs:\n - project_id (string, UUID): Project containing the domain\n - name (string): Current domain name\n - new_name (string): New domain name\n - dry_run (bool, optional): If true, return impact count without making changes (default false)\n\nReturns (dry_run=false):\n { domain: { name }, renamed_from, items_updated, message }\n\nReturns (dry_run=true):\n { dry_run: true, rename: { from, to }, items_affected, message }",inputSchema:{project_id:y.string().uuid().describe("UUID of the project"),name:y.string().min(1).max(64).describe("Current domain name"),new_name:y.string().min(1).max(64).describe("New domain name"),dry_run:y.boolean().optional().describe("Preview impact without applying (default false)")},annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!1,openWorldHint:!1}},os("atoms_update_domain",async e=>{const{updateDomainHandler:t}=await Promise.resolve().then(()=>(da(),ta));return t(e)})),Za("atoms_delete_domain",{title:"Delete Project Domain",description:"Delete a domain tag from the project taxonomy.\n\nRefuses by default if any items are tagged with it. With force=true, deletes the\ndomain and strips the tag from all items.\n\nArgs:\n - project_id (string, UUID): Project containing the domain\n - name (string): Domain name to delete\n - force (bool, optional): Strip from items and delete anyway (default false)\n\nReturns:\n { deleted, name, items_updated, message }",inputSchema:{project_id:y.string().uuid().describe("UUID of the project"),name:y.string().min(1).max(64).describe("Domain name to delete"),force:y.boolean().optional().describe("Strip from all items and delete (default false)"),confirmation_token:y.string().max(500).optional().describe("Token from a prior preview call (only used when ATOMS_MCP_REQUIRE_CONFIRMATION is set)")},annotations:{readOnlyHint:!1,destructiveHint:!0,idempotentHint:!1,openWorldHint:!1}},os("atoms_delete_domain",async e=>{const{deleteDomainHandler:t}=await Promise.resolve().then(()=>(da(),ta));return t(e)})),ss({appName:"summary",htmlFile:"summary-app.html",toolName:"atoms_project_summary",title:"Project Compliance Summary",description:'One-call project health and compliance dashboard.\n\nReturns item counts by type, test execution status, requirement coverage\n(overall and by domain), and recent change activity.\n\nIn MCP App hosts (Claude, ChatGPT), renders an interactive dashboard with charts.\nFalls back to JSON for non-UI clients.\n\nArgs:\n - project_id (string, UUID): Project to summarize\n\nReturns:\n { project_name, counts, test_status, coverage, coverage_by_domain, recent_changes, last_updated }\n\nExamples:\n - "Give me a compliance snapshot" → atoms_project_summary(project_id)\n - "How\'s our test coverage?" → atoms_project_summary(project_id)',inputSchema:{project_id:y.string().uuid().describe("UUID of the project")},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1},handler:os("atoms_project_summary",async e=>{const{projectSummaryHandler:t}=await Promise.resolve().then(()=>(pa(),ca));return t(e)})}),ss({appName:"browse",htmlFile:"browse-app.html",toolName:"atoms_browse",title:"Browse ATOMS Items",description:'View and filter items in an ATOMS project with a visual browser.\n\nIn MCP App hosts (Claude, ChatGPT), renders an interactive filterable list\nwith expandable item details. Falls back to JSON for non-UI clients.\n\nArgs:\n - project_id (string, UUID): The project to browse\n - type (string, optional): Filter by type (requirement, test-case, note, table)\n - domain (string, optional): Filter by domain tag\n - level (string, optional): Filter by level (System, Subsystem, Component)\n - query (string, optional): Full-text search query\n - limit (number, optional): Max results (default 25, max 100)\n - offset (number, optional): Pagination offset (default 0)\n\nReturns:\n { items: [...], filters: { domains, levels, types }, meta: { total_count, limit, offset, has_more } }\n\nExamples:\n - "Show me the requirements" → atoms_browse(project_id, type="requirement")\n - "Browse safety items" → atoms_browse(project_id, domain="Safety")\n - "Find braking requirements" → atoms_browse(project_id, query="braking")',inputSchema:{project_id:y.string().uuid().describe("UUID of the project"),type:y.enum(["requirement","test-case","note","table"]).optional().describe("Filter by item type"),domain:y.string().max(64).optional().describe("Filter by domain tag"),level:y.string().max(32).optional().describe("Filter by level (System, Subsystem, Component)"),query:y.string().max(1e3).optional().describe("Full-text search query"),limit:y.number().int().min(1).max(100).default(25).describe("Max results (default 25, max 100)"),offset:y.number().int().min(0).default(0).describe("Pagination offset (default 0)")},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1},handler:os("atoms_browse",async e=>{const{browseHandler:t}=await Promise.resolve().then(()=>(wa(),ua));return t(e)})}),Za("atoms_impact_analysis",{title:"Analyze Impact of Changing an Item",description:'Analyze the blast radius of changing a specific item.\n\nTraverses the relationship graph from the given item and returns every item that\nwould be affected by a change — with their types, depths, and full relationship paths.\n\nArgs:\n - project_id (string, UUID): Project containing the item\n - item_id (string): Item to analyze (e.g., "REQ-001")\n - direction (string, optional): "downstream" (default) | "upstream" | "both"\n - downstream: what this item affects (children, verified_by)\n - upstream: what affects this item (parents, verifies)\n - both: full bidirectional traversal\n - depth (integer, optional): Max traversal depth. Omit for full depth (all reachable nodes).\n - include_variable_refs (boolean, optional): Reserved for future variable-aware impact. Default true.\n\nReturns:\n { root: { item_id, title, type }, impacted_items: [{ item_id, title, type, depth, relationship_path }], summary: { total_impacted, by_type, max_depth_reached } }\n\nExamples:\n - "What breaks if I change REQ-001?" → atoms_impact_analysis(project_id, "REQ-001")\n - "What\'s upstream of TC-005?" → atoms_impact_analysis(project_id, "TC-005", direction="upstream")\n - "Show direct dependencies only" → atoms_impact_analysis(project_id, "REQ-001", depth=1)\n\nErrors:\n - "Item not found" → verify item_id exists in the project',inputSchema:{project_id:y.string().uuid().describe("UUID of the project"),item_id:y.string().min(1).max(20).describe("Item ID to analyze (e.g., REQ-001)"),direction:y.enum(["downstream","upstream","both"]).default("downstream").describe("Traversal direction (default: downstream)"),depth:y.number().int().min(1).optional().describe("Max traversal depth. Omit for full depth."),include_variable_refs:y.boolean().default(!0).describe("Reserved for variable-aware impact (future). Default true.")},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1}},os("atoms_impact_analysis",async e=>{const{impactAnalysisHandler:t}=await Promise.resolve().then(()=>(ja(),ba));return t(e)})),Za("atoms_list_baselines",{title:"List ATOMS Baselines",description:"List named baselines (named project snapshots) for a project, newest first.\n\nA baseline is a labeled moment in time. Use atoms_create_baseline to make one,\natoms_diff_baselines to compare two, and atoms_restore_baseline to roll back.",inputSchema:{project_id:y.string().uuid().describe("UUID of the project"),limit:y.number().int().min(1).max(100).default(50).describe("Max results (default 50, max 100)"),offset:y.number().int().min(0).default(0).describe("Pagination offset")},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1}},os("atoms_list_baselines",async e=>{const{listBaselinesHandler:t}=await Promise.resolve().then(()=>(qa(),xa));return t(e)})),Za("atoms_create_baseline",{title:"Create ATOMS Baseline",description:"Create a named baseline (snapshot label) at the current moment for a project.\n\nThe baseline records a name + timestamp. It does NOT copy item data; the project\nstate at that moment is reconstructed via temporal queries against item_revisions.\nNames must be unique per project (case-insensitive).",inputSchema:{project_id:y.string().uuid().describe("UUID of the project"),name:y.string().min(1).max(200).describe("Baseline name (1-200 chars, unique per project)"),description:y.string().max(2e3).optional().describe("Optional description (max 2000 chars)")},annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!1,openWorldHint:!1}},os("atoms_create_baseline",async e=>{const{createBaselineHandler:t}=await Promise.resolve().then(()=>(qa(),xa));return t(e)})),Za("atoms_lock_baseline",{title:"Lock ATOMS Baseline",description:"Lock a baseline so it becomes audit-grade immutable. Admin only. One-way: there is no unlock.\n\nAfter locking, the baseline's name, description, and snapshot_at cannot be changed,\nand it cannot be deleted. Use for regulatory submissions and DRBs.",inputSchema:{baseline_id:y.string().uuid().describe("UUID of the baseline to lock")},annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!1,openWorldHint:!1}},os("atoms_lock_baseline",async e=>{const{lockBaselineHandler:t}=await Promise.resolve().then(()=>(qa(),xa));return t(e)})),Za("atoms_diff_baselines",{title:"Diff ATOMS Baselines",description:"Compute the diff between two baselines: which items were added, removed, or modified.\n\nReturns a summary plus a paginated items array. Pass detail=true to include\nper-item field-level diffs (slower for large projects).",inputSchema:{baseline_a_id:y.string().uuid().describe("UUID of the first (older) baseline"),baseline_b_id:y.string().uuid().describe("UUID of the second (newer) baseline"),detail:y.boolean().default(!1).describe("Include per-item field-level diffs (default false)"),limit:y.number().int().min(1).max(200).default(50).describe("Max items per page (default 50)"),offset:y.number().int().min(0).default(0).describe("Pagination offset")},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1}},os("atoms_diff_baselines",async e=>{const{diffBaselinesHandler:t}=await Promise.resolve().then(()=>(qa(),xa));return t(e)})),Za("atoms_restore_baseline",{title:"Restore from ATOMS Baseline",description:"Restore project state to a baseline. Admin only. Preview-by-default.\n\nCall with confirm=false (default) for a dry run that returns items_to_restore count.\nCall with confirm=true to execute: each item present at the baseline gets a new\nrevision matching its historical state, event_source='restore'. Items added AFTER\nthe baseline are NOT deleted. Always recoverable via further history.",inputSchema:{baseline_id:y.string().uuid().describe("UUID of the baseline to restore from"),confirm:y.boolean().default(!1).describe("Set true to execute; false (default) returns a preview")},annotations:{readOnlyHint:!1,destructiveHint:!0,idempotentHint:!1,openWorldHint:!1}},os("atoms_restore_baseline",async e=>{const{restoreBaselineHandler:t}=await Promise.resolve().then(()=>(qa(),xa));return t(e)})),Za("atoms_create_analysis",{title:"Create HARA / DFMEA Analysis",description:'Create a new safety analysis (HARA or DFMEA) with one empty row per seed.\n\nRequires editor or admin role. Structure matches analyses created in the ATOMS web app.\nA HARA is seeded from FUNCTIONS (tags flagged as functions), never from requirements: a\nrequirement is an output of the analysis. A DFMEA is seeded from requirements / design elements.\n\nArgs:\n - project_id (string, UUID): Target project\n - subtype (string): hara | dfmea\n - title (string, optional): Analysis title (default "New HARA" / "New DFMEA")\n - source_functions (string[], hara only, max 50): Function names to seed rows from. A name\n absent from the project\'s tags is registered as a function-flagged tag; matching is\n case-insensitive and trimmed. Rejected for dfmea.\n - source_req_ids (string[], dfmea only, max 50): Requirement IDs to seed rows from.\n Each must exist, be a plain requirement, and not itself be an analysis row. Rejected for hara.\n - revision (string, optional): Revision label (default "1.0")\n\nReturns:\n { id (e.g. "HARA-001" / "DFMEA-001"), subtype, title, revision, standard? ("ISO 26262" for hara),\n rows: [{ id, source_function }] (hara) | [{ id, source_req }] (dfmea),\n link_warnings?: [{ row_id, source_function | source_req, message }], warning? }\n rows lists every created row. If any row could not be attached to the analysis, it is\n named in link_warnings and warning explains the repair (atoms_link_items with type\n \'parent\', from_id=<row id>, to_id=<analysis id>); such rows will not appear in\n atoms_get_analysis until relinked.\n\nSide effects:\n - Creates the analysis parent item plus one row item (SG-nnn / FM-nnn) per seed\n - hara: registers new function names in the project\'s tags and function flags (metadata)\n - Links each row to the parent (dual-written to the relationship shadow table)\n - Logs each created item to change_history with actor=\'mcp_<client>\'',inputSchema:{project_id:y.string().uuid().describe("UUID of the target project"),subtype:y.enum(["hara","dfmea"]).describe("Analysis subtype"),title:y.string().min(1).max(500).optional().describe("Analysis title"),source_functions:y.array(y.string().min(1).max(100)).max(50).optional().describe("hara only: function names to seed rows from (0-50); new names are registered as function-flagged tags"),source_req_ids:y.array(y.string().min(1).max(20)).max(50).optional().describe("dfmea only: requirement IDs to seed rows from (0-50)"),revision:y.string().min(1).max(32).optional().describe("Revision label (default 1.0)")},annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!1,openWorldHint:!1}},os("atoms_create_analysis",async e=>{const{createAnalysisHandler:t}=await Promise.resolve().then(()=>(ns(),Ta));return t(e)})),Za("atoms_fill_row",{title:"Fill Analysis Row Fields",description:"Fill fields on one HARA / DFMEA analysis row (SG-nnn / FM-nnn).\n\nRequires editor or admin role. Written fields are marked as MCP-sourced so the\nweb app shows their provenance. The computed level (ASIL / RPN) is derived and\nnever stored: asil is never a writable key.\n\nArgs:\n - project_id (string, UUID): Project containing the row\n - row_id (string): Analysis row ID (e.g. \"SG-001\", \"FM-003\")\n - fields (object): Field name to value map. Allowed keys:\n hara: failure_mode, operating_scenario, hazard, assumptions, severity (S0-S3), exposure (E0-E4),\n controllability (C0-C3), safe_state, ftti, rationale, source_function, effect, title\n dfmea: failure_mode, cause, severity (1-10), occurrence (1-10), detection (1-10),\n rationale, effect\n 'effect' writes the row's long text body (the hazardous event for hara). dfmea failure_mode also\n becomes the row title. dfmea ratings accept numbers and are stored as strings.\n hara failure_mode: the standardized suggestions are No function, Loss of function, Partial function,\n Degradation of function, Misinterpretation of function (a suggestion; any text is accepted).\n hara source_function: the function the row analyzes, registered as a function-flagged tag if new.\n\nReturns:\n { row_id, subtype, written_fields, computed_level, asil_propagated_to? }\n\nSide effects:\n - Saves the row with stale-write protection and logs to change_history\n - hara only: if the row is linked and its computed ASIL changed, updates each\n linked requirement's asil / asil_source (each update is history-logged)",inputSchema:{project_id:y.string().uuid().describe("UUID of the project"),row_id:y.string().min(1).max(20).describe("Analysis row ID (e.g. SG-001, FM-003)"),fields:y.record(y.string().max(64),y.union([y.string().max(5e4),y.number()])).describe("Field name to value map (see description for allowed keys per subtype)")},annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1}},os("atoms_fill_row",async e=>{const{fillRowHandler:t}=await Promise.resolve().then(()=>(ns(),Ta));return t(e)})),Za("atoms_get_analysis",{title:"Get HARA / DFMEA Analysis",description:'Get a full analysis view: every row with its computed level (ASIL / RPN) and\ngap / critical / stale classifications, plus summary counts.\n\nClassifications match the ATOMS web app exactly (same rules, same thresholds).\n\nArgs:\n - project_id (string, UUID): Project containing the analysis\n - analysis_id (string): Analysis ID (e.g. "HARA-001", "DFMEA-002")\n\nReturns:\n { analysis: { id, title, subtype, scheme, revision, standard },\n rows: [{ id, title, source_req, effect, <subtype fields incl. source_function / hazard / assumptions for hara>,\n computed_level, is_gap, gap_rule, is_critical, is_stale, mitigated_by, link_roles,\n approved, approved_at, mcp_fields }],\n counts: { rows, gaps, critical, stale, approved, linked } }',inputSchema:{project_id:y.string().uuid().describe("UUID of the project"),analysis_id:y.string().min(1).max(20).describe("Analysis ID (e.g. HARA-001, DFMEA-002)")},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1}},os("atoms_get_analysis",async e=>{const{getAnalysisHandler:t}=await Promise.resolve().then(()=>(ns(),Ta));return t(e)})),Za("atoms_link_mitigation",{title:"Link Analysis Row to Mitigating Requirement",description:'Link a HARA / DFMEA row to the technical requirement that mitigates it.\n\nRequires editor or admin role.\n\nArgs:\n - project_id (string, UUID): Project containing both items\n - row_id (string): Analysis row ID (e.g. "SG-001", "FM-003")\n - req_id (string): Target requirement ID (must be a plain requirement, not an analysis row)\n - role (string, dfmea only, REQUIRED for dfmea): prevention | detection | mitigation.\n Must be omitted for hara rows.\n - override (boolean, optional, default false): hara only. When the target already\n carries a different asil, the call fails with a structured error\n (details.code=\'asil_conflict\') unless override=true; the override is\n history-logged like a dialog accept.\n\nReturns:\n { row_id, req_id, type: "mitigated_by", role, asil_written? }\n\nSide effects:\n - Adds the mitigated_by/mitigates relationship (JSONB + shadow table, one transaction)\n - hara only: writes the row\'s computed ASIL onto the target requirement\n (data.asil + data.asil_source) with a history entry recording previous/new values\n - dfmea: never modifies any field on the target requirement',inputSchema:{project_id:y.string().uuid().describe("UUID of the project"),row_id:y.string().min(1).max(20).describe("Analysis row ID (e.g. SG-001, FM-003)"),req_id:y.string().min(1).max(20).describe("Target requirement ID (e.g. REQ-042)"),role:y.enum(["prevention","detection","mitigation"]).optional().describe("Mitigation role. Required for dfmea rows; must be omitted for hara rows."),override:y.boolean().optional().describe("hara only: apply the row's ASIL over a conflicting existing value (history-logged)")},annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1}},os("atoms_link_mitigation",async e=>{const{linkMitigationHandler:t}=await Promise.resolve().then(()=>(ns(),Ta));return t(e)})),Za("atoms_get_gaps",{title:"Get Analysis Gap Rows",description:'List only the gap rows of a HARA / DFMEA analysis, each naming the rule that matched.\n\nGap rules (identical to the web app):\n hara: unmitigated row with ASIL A-D ("asil <X> unmitigated")\n dfmea: unmitigated row with RPN >= 100 ("rpn >= 100") or severity >= 9 ("severity >= 9")\n\nArgs:\n - project_id (string, UUID): Project containing the analysis\n - analysis_id (string): Analysis ID (e.g. "HARA-001", "DFMEA-002")\n\nReturns:\n { analysis_id, subtype, gaps: [{ id, title, source_req, source_function, computed_level, rule }], count }',inputSchema:{project_id:y.string().uuid().describe("UUID of the project"),analysis_id:y.string().min(1).max(20).describe("Analysis ID (e.g. HARA-001, DFMEA-002)")},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1}},os("atoms_get_gaps",async e=>{const{getGapsHandler:t}=await Promise.resolve().then(()=>(ns(),Ta));return t(e)}))}});ee(),Q(),Ge(),z();var ps=S(import.meta.url)("../package.json"),us=process.argv.slice(2)[0];(async function(){switch(us){case"login":{const{login:e}=await Promise.resolve().then(()=>(nt(),Ye));try{const{email:t}=await e();process.stderr.write(`\n [32m✓[0m Authenticated as ${t}\n`),process.stderr.write(" [32m✓[0m Credentials saved to ~/.atoms/credentials.json\n\n"),process.stderr.write(" [1mNext steps:[0m\n"),process.stderr.write(" [36m$[0m claude mcp add atoms -- npx -y @atoms-tech/atoms-mcp\n"),process.stderr.write("\n Or add to Claude Desktop (Settings → MCP → Edit Config):\n"),process.stderr.write(' [90m{\n "mcpServers": {\n "atoms": {\n "command": "npx",\n "args": ["-y", "@atoms-tech/atoms-mcp"]\n }\n }\n }[0m\n\n')}catch(e){process.stderr.write(`\n [31m✗[0m ${e instanceof Error?e.message:String(e)}\n\n`),process.exit(1)}break}case"whoami":{const{getValidToken:e}=await Promise.resolve().then(()=>(Me(),Ae));try{const{email:t,user_id:r}=await e();process.stderr.write(`\n [32m✓[0m Logged in as ${t}\n [90mUser ID: ${r}[0m\n\n`)}catch(e){process.stderr.write(`\n [31m✗[0m Not authenticated: ${e instanceof Error?e.message:String(e)}\n Run: npx @atoms-tech/atoms-mcp login\n\n`),process.exit(1)}break}case"logout":{const{clearCredentials:e}=await Promise.resolve().then(()=>(fe(),te));await e(),process.stderr.write("Logged out. Credentials removed.\n"),process.stderr.write("Run 'npx @atoms-tech/atoms-mcp login' to authenticate again.\n");break}case"--version":case"-v":process.stderr.write(`@atoms-tech/atoms-mcp v${ps.version}\n`);break;case"--help":case"-h":process.stderr.write("ATOMS MCP Server — AI agent integration for requirements management\n\nUsage:\n npx @atoms-tech/atoms-mcp Start MCP server (stdio)\n npx @atoms-tech/atoms-mcp login Authenticate with ATOMS.tech\n npx @atoms-tech/atoms-mcp logout Switch account / clear credentials\n npx @atoms-tech/atoms-mcp whoami Show current user\n npx @atoms-tech/atoms-mcp --version Show version\n");break;default:{process.stderr.write("[atoms-mcp] Starting MCP server via stdio...\n");{const{getValidToken:e,readValidToken:t}=await Promise.resolve().then(()=>(Me(),Ae)),{runExclusiveAuth:r}=await Promise.resolve().then(()=>(xe(),he)),{login:i}=await Promise.resolve().then(()=>(nt(),Ye));try{let n;try{n=(await e()).email}catch(e){process.stderr.write(`[atoms-mcp] Could not use the saved session: ${e instanceof Error?e.message:String(e)}\n`),process.stderr.write("[atoms-mcp] Starting login flow...\n");const a=async()=>{const e=await t();if(!e)throw new Error("no valid token yet");return{email:e.email}};n=(await r(a,async()=>({email:(await i()).email}))).email}process.stderr.write(`[atoms-mcp] Authenticated as ${n}\n`)}catch(e){process.stderr.write(`[atoms-mcp] Login failed: ${e instanceof Error?e.message:String(e)}\n[atoms-mcp] Starting server without auth. Tools will fail until you run: npx @atoms-tech/atoms-mcp login\n`)}}try{const t=await $e(),r=We(),i=await async function(e,t){const r=process.env.ATOMS_MCP_ORG_ID?.trim();if(r)return r;const{data:i,error:n}=await e.from("org_members").select("org_id, joined_at").eq("user_id",t).order("joined_at",{ascending:!0}).limit(1).maybeSingle();return n?(process.stderr.write(`[atoms-mcp] Could not resolve session org: ${n.message}\n`),null):i?.org_id??null}(t,r),n=await async function(e,t,r){if(!r)return{role:null,is_member:!1,effective:L};const{data:i,error:n}=await e.rpc("mcp_resolve_policy",{p_user_id:t,p_org_id:r});return n?(process.stderr.write(`[atoms-mcp] mcp_resolve_policy failed (${n.code}): ${n.message}. Falling back to fail-closed policy.\n`),{role:null,is_member:!1,effective:F}):function(e){if(!e||"object"!=typeof e)return{role:null,is_member:!1,effective:L};const t=e,r="string"==typeof t.role?t.role:null,i=!0===t.is_member,n=t.effective??{},a=(e,t)=>Array.isArray(e)?e.filter(e=>"string"==typeof e):t,s=(e,t)=>"boolean"==typeof e?e:t,o=n.project_scope,d=Array.isArray(o)&&o.length>0?o.filter(e=>"string"==typeof e):null;return{role:r,is_member:i,effective:{read_only:s(n.read_only,L.read_only),lockdown:s(n.lockdown,L.lockdown),block_destructive:s(n.block_destructive,L.block_destructive),block_bulk_import:s(n.block_bulk_import,L.block_bulk_import),cross_project_browse:s(n.cross_project_browse,L.cross_project_browse),allowed_toolsets:a(n.allowed_toolsets,L.allowed_toolsets),forbidden_tools:a(n.forbidden_tools,L.forbidden_tools),project_scope:d,require_confirmation_for:a(n.require_confirmation_for,L.require_confirmation_for)}}}(i)}(t,r,i);e={effective:n.effective,role:n.role,org_id:i},W=e.effective,V=e.org_id,n.is_member&&i?process.stderr.write(`[atoms-mcp] Org policy loaded: org=${i.slice(0,8)}… role=${n.role??"?"} read_only=${n.effective.read_only} lockdown=${n.effective.lockdown}\n`):process.stderr.write("[atoms-mcp] No org membership detected — running with permissive default policy.\n")}catch(e){process.stderr.write(`[atoms-mcp] Could not resolve session policy (${e instanceof Error?e.message:String(e)}). Running with permissive default — RLS still enforces row-level access.\n`)}process.stderr.write(`[atoms-mcp] Tool registry: ${function(){const e=Y(),t=G(),r=[];r.push(e?"read-only":"read+write"),null===t?r.push("all toolsets"):(r.push(`toolsets=${[...t.allowed].sort().join(",")||"(none — only always-on tools)"}`),t.unknown.length>0&&r.push(`unknown=${t.unknown.join(",")}`));const i=process.env.ATOMS_MCP_PROJECT_ID?.trim();return i&&r.push(`project_scope=${i.slice(0,8)}…`),("1"===process.env.ATOMS_MCP_LOCKDOWN||"true"===process.env.ATOMS_MCP_LOCKDOWN)&&r.push("lockdown=on"),r.join(" | ")}()}\n`);const{server:t}=await Promise.resolve().then(()=>(ms(),as)),r=new x;await t.connect(r),process.stderr.write("[atoms-mcp] MCP server running. Waiting for tool calls...\n")}}var e})().catch(e=>{process.stderr.write(`[atoms-mcp] Fatal: ${e instanceof Error?e.message:String(e)}\n`),process.exit(1)});
|
|
2
|
+
import e,{readFile as t,mkdir as r,writeFile as i,unlink as n,stat as a,open as s}from"fs/promises";import{homedir as o}from"os";import d,{join as c}from"path";import{createClient as l}from"@supabase/supabase-js";import{createServer as m}from"http";import p,{randomUUID as u,randomBytes as _,createHash as f}from"crypto";import{fileURLToPath as h,URL as g}from"url";import{z as y}from"zod";import{registerAppTool as w,registerAppResource as b,RESOURCE_MIME_TYPE as v}from"@modelcontextprotocol/ext-apps/server";import{McpServer as j}from"@modelcontextprotocol/sdk/server/mcp.js";import{createRequire as S}from"module";import{StdioServerTransport as x}from"@modelcontextprotocol/sdk/server/stdio.js";var k,E,I,A,O,R,D,H=Object.defineProperty,C=Object.getOwnPropertyNames,U=(e,t,r)=>function(){if(r)throw r[0];try{return e&&(t=(0,e[C(e)[0]])(e=0)),t}catch(e){throw r=[e],e}},q=(e,t)=>{for(var r in t)H(e,r,{get:t[r],enumerable:!0})};function T(e){return D.get(e)}var M=U({"src/core/tool-catalog.ts"(){k=new Set(["atoms_status","atoms_list_projects"]),E=new Set(["atoms_create_item","atoms_update_item","atoms_delete_item","atoms_restore_item","atoms_link_items","atoms_bulk_import","atoms_record_test_result","atoms_record_test_results","atoms_create_variable","atoms_update_variable","atoms_delete_variable","atoms_create_domain","atoms_update_domain","atoms_delete_domain","atoms_create_level","atoms_create_field","atoms_delete_field","atoms_reorder_fields","atoms_create_baseline","atoms_lock_baseline","atoms_restore_baseline","atoms_create_analysis","atoms_fill_row","atoms_link_mitigation"]),I=new Set(["atoms_delete_item","atoms_delete_variable","atoms_delete_domain","atoms_delete_field","atoms_restore_baseline"]),A=new Set(["atoms_bulk_import"]),O={items:["atoms_list_items","atoms_get_item","atoms_search","atoms_semantic_search","atoms_browse","atoms_get_history","atoms_create_item","atoms_update_item","atoms_delete_item","atoms_restore_item","atoms_bulk_import"],traceability:["atoms_trace","atoms_link_items","atoms_export_mermaid","atoms_impact_analysis"],coverage:["atoms_get_coverage","atoms_verification_status","atoms_record_test_result","atoms_record_test_results","atoms_project_summary","atoms_list_assets"],variables:["atoms_list_variables","atoms_get_variable","atoms_create_variable","atoms_update_variable","atoms_delete_variable"],domains:["atoms_list_domains","atoms_create_domain","atoms_update_domain","atoms_delete_domain","atoms_list_levels","atoms_create_level","atoms_list_fields","atoms_create_field","atoms_delete_field","atoms_reorder_fields"],baselines:["atoms_list_baselines","atoms_create_baseline","atoms_lock_baseline","atoms_diff_baselines","atoms_restore_baseline"],analysis:["atoms_create_analysis","atoms_fill_row","atoms_get_analysis","atoms_link_mitigation","atoms_get_gaps"]},R=Object.keys(O),D=new Map;for(const[e,t]of Object.entries(O))for(const r of t)D.set(r,e)}});function P(e,t){if(k.has(t))return{allowed:!0};if(e.lockdown&&!new Set(["atoms_list_items","atoms_get_item","atoms_search","atoms_browse"]).has(t))return{allowed:!1,reason:"lockdown"};if(e.forbidden_tools.includes(t))return{allowed:!1,reason:"forbidden_tool"};if(e.read_only&&E.has(t))return{allowed:!1,reason:"read_only"};if(e.block_destructive&&I.has(t))return{allowed:!1,reason:"block_destructive"};if(e.block_bulk_import&&A.has(t))return{allowed:!1,reason:"block_bulk_import"};const r=T(t);return void 0===r||e.allowed_toolsets.includes(r)?{allowed:!0}:{allowed:!1,reason:"toolset_disabled"}}function $(e,t){return P(e,t).allowed}function N(e){switch(e){case"lockdown":return"MCP is in lockdown mode for your role — only read-only browse is available.";case"read_only":return"Your org policy puts MCP in read-only mode. Mutating tools are not exposed.";case"block_destructive":return"Your org policy blocks destructive operations (delete tools).";case"block_bulk_import":return"Your org policy blocks bulk import.";case"forbidden_tool":return"This tool is on your org's forbidden list.";case"toolset_disabled":return"This tool's toolset is not enabled in your org policy."}}var L,F,W,V,B=U({"src/core/policy.ts"(){M()}}),z=U({"src/core/types.ts"(){L={read_only:!1,lockdown:!1,block_destructive:!1,block_bulk_import:!1,cross_project_browse:!0,allowed_toolsets:["items","traceability","coverage","variables","domains","baselines"],forbidden_tools:[],project_scope:null,require_confirmation_for:[]},F={read_only:!0,lockdown:!0,block_destructive:!0,block_bulk_import:!0,cross_project_browse:!1,allowed_toolsets:[],forbidden_tools:[],project_scope:[],require_confirmation_for:[]}}});function J(){return W}var Q=U({"src/middleware/session-policy.ts"(){z(),W=L,V=null}});function G(){const e=process.env.ATOMS_MCP_TOOLSETS?.trim();if(!e)return null;const t=e.split(",").map(e=>e.trim()).filter(Boolean);if(0===t.length)return null;const r=new Set,i=[];for(const e of t)R.includes(e)?r.add(e):i.push(e);return{allowed:r,unknown:i}}function Y(){const e=process.env.ATOMS_MCP_READ_ONLY;return"1"===e||"true"===e?.toLowerCase()}function K(e){if(Y()&&E.has(e))return!1;if(k.has(e))return!0;const t=G();if(null===t)return $(J(),e);const r=T(e);return!!r&&!!t.allowed.has(r)&&$(J(),e)}var Z,X,ee=U({"src/middleware/tool-registry.ts"(){M(),B(),Q()}}),te={};async function re(){try{const e=await t(X,"utf-8"),r=JSON.parse(e);return r.access_token&&r.refresh_token?r:null}catch{return null}}async function ie(e){await r(Z,{recursive:!0,mode:448}),await i(X,JSON.stringify(e,null,2),{mode:384})}async function ne(){const e=await re();return!!e&&e.expires_at>Date.now()+6e4}function ae(e){return e.expires_at>Date.now()+6e4}async function se(){try{await n(X)}catch{}}function oe(){return X}q(te,{clearCredentials:()=>se,getCredentialsPath:()=>oe,hasValidCredentials:()=>ne,isTokenValid:()=>ae,readCredentials:()=>re,writeCredentials:()=>ie});var de,ce,le,me,pe,ue,_e,fe=U({"src/auth/token-store.ts"(){Z=c(o(),".atoms"),X=c(Z,"credentials.json")}}),he={};async function ge(){try{await r(de,{recursive:!0,mode:448});const e=await s(ce,"wx");try{await e.writeFile(`${process.pid} ${Date.now()}`)}finally{await e.close()}return!0}catch(e){if("EEXIST"===e.code)return!1;throw e}}async function ye(){try{await n(ce)}catch{}}async function we(){try{const e=await a(ce);return Date.now()-e.mtimeMs>le}catch{return!1}}async function be(e,t,r=_e){const i=r.now()+me;for(;;){if(await r.tryAcquire())try{try{return await e()}catch{}return await t()}finally{await r.release()}try{return await e()}catch{}if(await r.isStale())await r.release();else{if(r.now()>i)return await t();await r.wait(pe)}}}q(he,{runExclusiveAuth:()=>be});var ve,je,Se,xe=U({"src/auth/lock.ts"(){de=c(o(),".atoms"),ce=c(de,".auth.lock"),le=7e4,me=12e4,pe=300,ue=e=>new Promise(t=>setTimeout(t,e)),_e={now:Date.now,wait:ue,tryAcquire:ge,release:ye,isStale:we}}}),ke={};function Ee(e){return e.trim().replace(/\/+$/,"")}q(ke,{ATOMS_APP_URL:()=>Se,ATOMS_SUPABASE_ANON_KEY:()=>je,ATOMS_SUPABASE_URL:()=>ve,resolveUrl:()=>Ee});var Ie=U({"src/config.ts"(){ve=Ee(process.env.ATOMS_SUPABASE_URL??"https://gmebjyhomsbvhrxffzre.supabase.co"),je=process.env.ATOMS_SUPABASE_ANON_KEY??"sb_publishable_b49MUAB8XCrQiF7x5b9lUA_w1aplSBH",Se=process.env.ATOMS_APP_URL??"https://atoms.tech"}}),Ae={};async function Oe(){const e=process.env.ATOMS_ACCESS_TOKEN;if(e){const t=De(e);return{access_token:e,refresh_token:"",user_id:t.sub,email:t.email??""}}const t=await re();if(t&&t.expires_at>Date.now()+6e4){const e=De(t.access_token);return{access_token:t.access_token,refresh_token:t.refresh_token,user_id:e.sub,email:e.email??t.user_email??""}}return null}async function Re(){const e=await Oe();if(e)return e;const t=await re();if(!t)throw new Error("Not authenticated. Run 'npx @atoms-tech/atoms-mcp login' to connect your ATOMS account.");return be(async()=>{const e=await Oe();if(e)return e;throw new Error("no fresh token yet")},()=>async function(e){process.stderr.write("[atoms-mcp] Refreshing access token...\n");const t=l(ve,je);let r=await t.auth.refreshSession({refresh_token:e.refresh_token});if(r.error||!r.data.session){const i=await re();if(i&&i.expires_at>Date.now()+6e4){const e=De(i.access_token);return{access_token:i.access_token,refresh_token:i.refresh_token,user_id:e.sub,email:e.email??i.user_email??""}}i&&i.refresh_token&&i.refresh_token!==e.refresh_token&&(process.stderr.write("[atoms-mcp] Refresh token was rotated by another session; retrying with the latest...\n"),r=await t.auth.refreshSession({refresh_token:i.refresh_token}))}if(r.error||!r.data.session){const e=r.error?[r.error.status,r.error.message].filter(Boolean).join(" ").trim():"no session returned";throw new Error(`Token refresh failed (${e}). Your saved ATOMS session is no longer valid — re-run 'npx @atoms-tech/atoms-mcp login'.`)}const i=r.data.session;return await ie({access_token:i.access_token,refresh_token:i.refresh_token,expires_at:Date.now()+1e3*(i.expires_in??3600),user_email:i.user?.email}),{access_token:i.access_token,refresh_token:i.refresh_token,user_id:i.user?.id??"",email:i.user?.email??""}}(t))}function De(e){const t=e.split(".");if(3!==t.length)throw new Error("Invalid JWT format");const r=t[1].replace(/-/g,"+").replace(/_/g,"/").padEnd(t[1].length+(4-t[1].length%4)%4,"="),i=JSON.parse(Buffer.from(r,"base64").toString("utf-8"));if(!i.sub)throw new Error("JWT missing 'sub' claim");return i}q(Ae,{getValidToken:()=>Re,readValidToken:()=>Oe});var He,Ce,Ue,qe,Te,Me=U({"src/auth/refresh.ts"(){fe(),xe(),Ie()}}),Pe={};async function $e(){if(He)try{const e=await re();e&&e.expires_at>Te&&(process.stderr.write("[atoms-mcp] Credentials file refreshed elsewhere — rebuilding client.\n"),Ne())}catch{}if(He&&qe>Date.now()+6e4)return He;He&&process.stderr.write("[atoms-mcp] Token expiring, refreshing client...\n");const{access_token:e,user_id:t,email:r}=await Re(),i=e.split(".");if(3===i.length)try{const e=JSON.parse(Buffer.from(i[1].replace(/-/g,"+").replace(/_/g,"/"),"base64").toString());qe=1e3*(e.exp??0)}catch{qe=Date.now()+36e5}try{const e=await re();Te=e?.expires_at??0}catch{Te=0}const n=l(ve,je,{global:{headers:{Authorization:`Bearer ${e}`}}});return Ce=t,Ue=r,He=n}function Ne(){He=null,Ce=null,Ue=null,qe=0,Te=0}function Le(e){let t="";if(e instanceof Error)t=e.message;else if(e&&"object"==typeof e){const r=e;t="string"==typeof r.message?r.message:"string"==typeof r.code?r.code:String(e)}else null!=e&&(t=String(e));return/jwt\s*expired|invalid.?jwt|token.*(?:has\s+)?expired|jwt.*invalid|pgrst301/i.test(t)}async function Fe(e){try{return await e()}catch(t){if(!Le(t))throw t;return process.stderr.write("[atoms-mcp] Data tier rejected JWT — invalidating client and retrying once.\n"),Ne(),await e()}}function We(){if(!Ce)throw new Error("Not authenticated. Call getClient() first.");return Ce}function Ve(){return Ue??""}async function Be(e,t){const r=We(),{data:i,error:n}=await e.from("projects").select("org_id").eq("id",t).maybeSingle();if(n||!i)throw new Error(`Project '${t}' not found`);const{data:a,error:s}=await e.from("org_members").select("role").eq("user_id",r).eq("org_id",i.org_id).maybeSingle();if(s||!a){const{data:t}=await e.rpc("is_platform_admin");if(!0===t)return"admin";throw new Error("Not a member of this project's organization")}if("viewer"===a.role)throw new Error("VIEWER_ROLE");return a.role}async function ze(e,t){const r=We(),{data:i,error:n}=await e.from("projects").select("org_id, created_by").eq("id",t).maybeSingle();if(n||!i)throw new Error(`Project '${t}' not found`);const{data:a,error:s}=await e.from("org_members").select("role").eq("user_id",r).eq("org_id",i.org_id).maybeSingle();if(s||!a)throw new Error("Not a member of this project's organization");if("admin"===a.role)return"admin";if(i.created_by===r)return"owner";throw new Error("PROJECT_FIELD_ADMIN_OR_OWNER_REQUIRED")}q(Pe,{getClient:()=>$e,getUserEmail:()=>Ve,getUserId:()=>We,invalidateClient:()=>Ne,isJwtExpiredError:()=>Le,requireProjectFieldDefinitionAccess:()=>ze,requireWriteAccess:()=>Be,withJwtRetry:()=>Fe});var Je,Qe,Ge=U({"src/db/client.ts"(){Me(),fe(),Ie(),He=null,Ce=null,Ue=null,qe=0,Te=0}}),Ye={};async function Ke(){const e=_(32).toString("base64url"),t=f("sha256").update(e).digest("base64url"),r=`http://localhost:${Je}${Qe}`,i=new g(`${Se}/auth/mcp-consent`);return i.searchParams.set("redirect_uri",r),i.searchParams.set("code_challenge",t),i.searchParams.set("code_challenge_method","S256"),new Promise((t,r)=>{let n;function a(){clearTimeout(n)}const s=m(async(i,n)=>{const o=new g(i.url??"/",`http://localhost:${Je}`);if(o.pathname!==Qe)return n.writeHead(404),void n.end("Not found");try{if("access_denied"===o.searchParams.get("error"))return n.writeHead(200,{"Content-Type":"text/html"}),n.end(Xe("Authorization was cancelled.")),a(),s.close(),void r(new Error("Authorization cancelled by user."));const i=o.searchParams.get("code");if(i){const o=await fetch(`${ve}/auth/v1/token?grant_type=pkce`,{method:"POST",headers:{"Content-Type":"application/json",apikey:je},body:JSON.stringify({auth_code:i,code_verifier:e})}),d=await o.json();if(!o.ok||!d.access_token){const e=d.error_description??d.msg??"Unknown error";return n.writeHead(200,{"Content-Type":"text/html"}),n.end(Xe(`Code exchange failed: ${e}`)),a(),s.close(),void r(new Error(`Code exchange failed: ${e}`))}let c="authenticated";try{c=JSON.parse(Buffer.from(d.access_token.split(".")[1],"base64").toString()).email??c}catch{}const{createClient:l}=await import("@supabase/supabase-js"),m=l(ve,je,{global:{headers:{Authorization:`Bearer ${d.access_token}`}}}),{data:p,error:u}=await m.from("org_members").select("org_id").limit(1);return u||!p||0===p.length?(n.writeHead(200,{"Content-Type":"text/html"}),n.end(Xe('No ATOMS account found for this Google account.<br><br>Please sign up at <a href="https://atoms.tech">atoms.tech</a> first, then run this login command again.')),a(),s.close(),void r(new Error("No ATOMS account found. Sign up at atoms.tech first."))):d.refresh_token?(await ie({access_token:d.access_token,refresh_token:d.refresh_token,expires_at:Date.now()+1e3*(d.expires_in??3600),user_email:c}),n.writeHead(200,{"Content-Type":"text/html"}),n.end(Ze(c)),a(),s.close(),void t({email:c})):(n.writeHead(200,{"Content-Type":"text/html"}),n.end(Xe("Sign-in succeeded but no refresh token was issued, so the session would expire within the hour. This usually points to a Supabase Auth setting (refresh-token rotation or session policy). Please contact ATOMS support.")),a(),s.close(),void r(new Error("No refresh token issued by Supabase — cannot persist a durable session.")))}const d=o.searchParams.get("access_token"),c=o.searchParams.get("refresh_token"),l=parseInt(o.searchParams.get("expires_in")??"3600",10);d&&c?(await ie({access_token:d,refresh_token:c,expires_at:Date.now()+1e3*l}),n.writeHead(200,{"Content-Type":"text/html"}),n.end(Ze("authenticated")),a(),s.close(),t({email:"authenticated"})):(n.writeHead(200,{"Content-Type":"text/html"}),n.end("<!DOCTYPE html>\n<html>\n<head><title>ATOMS MCP Login</title></head>\n<body>\n <h1>Completing login...</h1>\n <script>\n const hash = window.location.hash.substring(1);\n const params = new URLSearchParams(hash);\n const data = {\n access_token: params.get('access_token'),\n refresh_token: params.get('refresh_token'),\n expires_in: params.get('expires_in'),\n user_email: ''\n };\n if (data.access_token) {\n // Decode JWT to get email\n try {\n const payload = JSON.parse(atob(data.access_token.split('.')[1]));\n data.user_email = payload.email || '';\n } catch(e) {}\n fetch('/token', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(data)\n }).then(() => {\n document.body.innerHTML = '<h1>Login successful! You can close this tab.</h1>';\n }).catch(() => {\n document.body.innerHTML = '<h1>Login failed. Please try again.</h1>';\n });\n } else {\n document.body.innerHTML = '<h1>Login failed — no tokens received.</h1>';\n }\n <\/script>\n</body>\n</html>"))}catch(e){n.writeHead(500),n.end("Authentication failed"),a(),s.close(),r(e)}});s.on("request",async(e,i)=>{if("POST"===e.method&&"/token"===e.url){let n="";e.on("data",e=>{n+=e.toString()}),e.on("end",async()=>{try{const e=JSON.parse(n);await ie({access_token:e.access_token,refresh_token:e.refresh_token,expires_at:Date.now()+1e3*(e.expires_in??3600),user_email:e.user_email}),i.writeHead(200,{"Content-Type":"application/json"}),i.end(JSON.stringify({ok:!0})),a(),s.close(),t({email:e.user_email??"authenticated"})}catch(e){i.writeHead(500),i.end("Failed to store credentials"),a(),s.close(),r(e)}})}}),s.listen(Je,"127.0.0.1",async()=>{process.stderr.write("\nOpening browser for ATOMS login...\n"),process.stderr.write(`If the browser doesn't open, visit:\n${i.toString()}\n\n`);try{const{default:e}=await import("open");await e(i.toString())}catch{process.stderr.write("Could not open browser automatically. Please open the URL above.\n")}}),n=setTimeout(()=>{s.close(),r(new Error("Login timed out after 60 seconds. Run 'npx @atoms-tech/atoms-mcp login' to try again."))},6e4)})}function Ze(e){return`<!DOCTYPE html>\n<html>\n<head>\n <title>ATOMS MCP — Authorized</title>\n <style>\n * { margin: 0; padding: 0; box-sizing: border-box; }\n body {\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif;\n background: #0a0a0a;\n color: #fafafa;\n display: flex;\n align-items: center;\n justify-content: center;\n min-height: 100vh;\n }\n .card {\n background: #171717;\n border: 1px solid #262626;\n border-radius: 16px;\n padding: 48px;\n max-width: 480px;\n width: 100%;\n text-align: center;\n }\n .icon {\n width: 56px;\n height: 56px;\n background: #052e16;\n border-radius: 50%;\n display: flex;\n align-items: center;\n justify-content: center;\n margin: 0 auto 24px;\n }\n .icon svg { width: 28px; height: 28px; }\n h1 {\n font-size: 22px;\n font-weight: 600;\n margin-bottom: 8px;\n color: #22c55e;\n }\n .email {\n font-size: 14px;\n color: #a3a3a3;\n margin-bottom: 32px;\n }\n .permissions {\n text-align: left;\n background: #0a0a0a;\n border: 1px solid #262626;\n border-radius: 12px;\n padding: 20px;\n margin-bottom: 24px;\n }\n .permissions h3 {\n font-size: 12px;\n font-weight: 600;\n color: #737373;\n text-transform: uppercase;\n letter-spacing: 0.05em;\n margin-bottom: 12px;\n }\n .perm-item {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 6px 0;\n font-size: 14px;\n color: #d4d4d4;\n }\n .perm-item .dot {\n width: 6px;\n height: 6px;\n background: #22c55e;\n border-radius: 50%;\n flex-shrink: 0;\n }\n .closing {\n font-size: 13px;\n color: #525252;\n }\n .closing span { color: #a3a3a3; font-variant-numeric: tabular-nums; }\n </style>\n</head>\n<body>\n <div class="card">\n <div class="icon">\n <svg fill="none" viewBox="0 0 24 24" stroke="#22c55e" stroke-width="2.5">\n <path stroke-linecap="round" stroke-linejoin="round" d="M5 13l4 4L19 7"/>\n </svg>\n </div>\n <h1>Authorization Granted</h1>\n <p class="email">${e}</p>\n\n <div class="permissions">\n <h3>ATOMS MCP can now</h3>\n <div class="perm-item"><span class="dot"></span> Read your projects and requirements</div>\n <div class="perm-item"><span class="dot"></span> Create and edit requirements and test cases</div>\n <div class="perm-item"><span class="dot"></span> Record test results (pass/fail)</div>\n <div class="perm-item"><span class="dot"></span> Manage traceability relationships</div>\n <div class="perm-item"><span class="dot"></span> Export coverage reports and diagrams</div>\n </div>\n\n <p class="closing">This tab will close in <span id="countdown">5</span>s</p>\n </div>\n\n <script>\n let seconds = 5;\n const el = document.getElementById('countdown');\n const timer = setInterval(() => {\n seconds--;\n el.textContent = seconds;\n if (seconds <= 0) {\n clearInterval(timer);\n window.close();\n // If window.close() is blocked (not opened by script), update message\n setTimeout(() => {\n document.querySelector('.closing').textContent = 'You can close this tab now.';\n }, 500);\n }\n }, 1000);\n <\/script>\n</body>\n</html>`}function Xe(e){return`<!DOCTYPE html>\n<html>\n<head>\n <title>ATOMS MCP — Failed</title>\n <style>\n * { margin: 0; padding: 0; box-sizing: border-box; }\n body {\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif;\n background: #0a0a0a;\n color: #fafafa;\n display: flex;\n align-items: center;\n justify-content: center;\n min-height: 100vh;\n }\n .card {\n background: #171717;\n border: 1px solid #262626;\n border-radius: 16px;\n padding: 48px;\n max-width: 480px;\n width: 100%;\n text-align: center;\n }\n .icon {\n width: 56px;\n height: 56px;\n background: #450a0a;\n border-radius: 50%;\n display: flex;\n align-items: center;\n justify-content: center;\n margin: 0 auto 24px;\n }\n .icon svg { width: 28px; height: 28px; }\n h1 { font-size: 22px; font-weight: 600; margin-bottom: 16px; color: #ef4444; }\n .reason { font-size: 14px; color: #a3a3a3; line-height: 1.6; }\n .reason a { color: #7c3aed; text-decoration: underline; }\n </style>\n</head>\n<body>\n <div class="card">\n <div class="icon">\n <svg fill="none" viewBox="0 0 24 24" stroke="#ef4444" stroke-width="2.5">\n <path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12"/>\n </svg>\n </div>\n <h1>Authorization Failed</h1>\n <p class="reason">${e}</p>\n </div>\n</body>\n</html>`}q(Ye,{login:()=>Ke});var et,tt,rt,it,nt=U({"src/auth/login.ts"(){fe(),Ie(),Je=19275,Qe="/callback"}});function at(e,t=0){if(t>5)return"[depth-limit]";if("string"==typeof e){for(const t of rt)if(t.test(e.trim()))return"[redacted]";return e.length>it?e.slice(0,it)+"…":e}if(Array.isArray(e))return e.slice(0,20).map(e=>at(e,t+1));if(null!==e&&"object"==typeof e){const r={};for(const[i,n]of Object.entries(e)){const e=i.toLowerCase();et.has(e)?r[i]="[redacted]":tt.has(e)?r[i]="string"==typeof n?"[content]":at(n,t+1):r[i]=at(n,t+1)}return r}return e}function st(e){return at(e)}var ot,dt,ct,lt,mt,pt=U({"src/middleware/audit.ts"(){et=new Set(["access_token","refresh_token","password","api_key","secret","token","authorization","credential","credentials","private_key"]),tt=new Set(["body","description","content","notes","summary","title","system_prompt","prompt"]),rt=[/^[^\s@]+@[^\s@]+\.[^\s@]+$/,/^bearer\s+\S+$/i,/^(sk|pk|npm|gh|glpat|xoxb|xoxp|rk|SG\.)[-_][A-Za-z0-9_-]{16,}/,/^eyJ[A-Za-z0-9_-]+\.eyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+$/],it=200}}),ut=U({"src/middleware/rate-limiter.ts"(){ot=60,dt=6e4}});function _t(){const e=process.env.ATOMS_MCP_PROJECT_ID?.trim();return e?ct.test(e)?e:(lt||(process.stderr.write(`[atoms-mcp] WARNING: ATOMS_MCP_PROJECT_ID="${e.slice(0,50)}" is not a valid UUID — scope ignored.\n`),lt=!0),null):null}var ft,ht=U({"src/middleware/project-scope.ts"(){ct=/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i,lt=!1,mt=class extends Error{expectedProjectId;receivedProjectId;constructor(e,t){super("Tool call rejected: project_id is outside the configured ATOMS_MCP_PROJECT_ID scope."),this.name="ProjectScopeError",this.expectedProjectId=e,this.receivedProjectId=t}}}});function gt(e){const t=function(e){return e.trim().toLowerCase().replace(/[^a-z0-9]+/g,"_").replace(/^_+|_+$/g,"")}(e??"");return t?t.includes("cursor")?"cursor":t.includes("copilot")?"copilot":t.includes("claude")?ft:t:ft}var yt,wt,bt,vt,jt,St,xt,kt,Et=U({"src/core/client-identity.ts"(){ft="claude_desktop"}}),It=U({"src/core/timestamps.ts"(){yt=y.string().datetime({offset:!0})}}),At=U({"src/core/errors.ts"(){}}),Ot={};function Rt(e,t=bt){if("string"!=typeof e)return"";const r=e.replace(jt,"").replace(St,"");return r.length<=t?r:r.slice(0,t)+"..."}function Dt(e){return"string"!=typeof e?"":e.trim().toLowerCase().replace(/\s+/g,"-").replace(/[^a-z0-9-]/g,"").replace(/-+/g,"-").replace(/^-|-$/g,"")}function Ht(e,t){return{status:"success",data:e,...t?{meta:t}:{}}}function Ct(e,t,r){return{total_count:e,limit:t,offset:r,has_more:r+t<e}}function Ut(e,t){return{status:"error",message:e,next_steps:t}}function qt(e,t){return Ut(`${e} '${Rt(t)}' not found`,[`Verify the ${e.toLowerCase()} ID is correct`,"Use atoms_list_items or atoms_search to find valid IDs"])}function Tt(e){return Ut(`${e} role cannot modify project data`,["Contact your org admin to upgrade your role to editor","Use read-only tools (atoms_list_items, atoms_get_item, atoms_search) instead"])}function Mt(e){return Ut(`Validation error: ${Rt(e,vt)}`,["Check the parameter types and constraints in the tool description","Ensure all required parameters are provided"])}function Pt(){return Ut("Not authenticated. Run 'npx @atoms-tech/atoms-mcp login' first.",["Run: npx @atoms-tech/atoms-mcp login","Or set ATOMS_ACCESS_TOKEN environment variable"])}function $t(){return Ut("Your ATOMS session token expired and could not be refreshed automatically.",["Call atoms_status() — it will refresh the token if possible","If that still reports 'expired', restart Claude Desktop (quit and reopen)","If the problem persists, run: npx @atoms-tech/atoms-mcp logout && npx @atoms-tech/atoms-mcp login"])}function Nt(e){return Ut("Tool call rejected: project_id is outside the configured ATOMS_MCP_PROJECT_ID scope.",[`This MCP session is bound to project ${e}`,"Pass that project_id, or restart without ATOMS_MCP_PROJECT_ID to access other projects"])}function Lt(e){return Ut("Rate limited: too many requests to ATOMS API.",[e?`Wait ${e} seconds before retrying.`:"Wait a few seconds before retrying.","Reduce the frequency of back-to-back tool calls"])}function Ft(e){return Ut(`Parse error: ${Rt(e,vt)}`,["Check that body/title/summary fields contain valid text (no unexpected control characters)","If the content was generated programmatically, validate the string before passing it","Simplify the content and retry — if it succeeds, the original content has a malformed character"])}function Wt(e){return function(e){return/stale_item_write|sqlstate\s*40001|could not serialize access/i.test(e)}(e)}function Vt(){return Ut("Stale write rejected: this item changed after it was read.",["Call atoms_get_item again and use the returned metadata.updated_at value","Review the current item before retrying so you do not overwrite newer work","Retry atoms_update_item or atoms_link_items with the fresh expected timestamp"])}function Bt(){return Ut("Save paused because this item is already being written.",["Wait a few seconds, read the item again, and retry once","Do not repeatedly retry the same stale payload"])}function zt(){return Ut("Item saves are temporarily paused by an ATOMS operator.",["Keep your local draft and wait for the operator to re-enable saves","Do not repeatedly retry while the safety brake is active"])}function Jt(){return Ut("Item save rejected because this item is in Trash.",["Restore the item before changing it","Do not retry the same save while the item remains in Trash"])}function Qt(e){const t=e instanceof Error?e.message:String(e&&"object"==typeof e&&"message"in e?e.message??"":e),r=e&&"object"==typeof e&&"code"in e?String(e.code??""):"";return/not authenticated/i.test(t)?Pt():function(e){let t="";if(e instanceof Error)t=e.message;else if(e&&"object"==typeof e){const r=e;t="string"==typeof r.message?r.message:"string"==typeof r.code?r.code:String(e)}else null!=e&&(t=String(e));return/jwt\s*expired|invalid.?jwt|token.*(?:has\s+)?expired|jwt.*invalid|pgrst301/i.test(t)}(e)?$t():/rate.?limit|too many requests|429/i.test(t)?Lt():Wt(t)?Vt():/soft_deleted_item_write/i.test(t)?Jt():/save_temporarily_unavailable/i.test(t)?zt():"55P03"===r||"57014"===r||/item_save_busy|statement timeout|request timeout|upstream request timeout/i.test(t)?Bt():"42501"===r&&/org_project_field_admin_required/i.test(t)?Tt("Org admin"):"42501"===r&&/project_field/i.test(t)?Tt("Project admin or owner"):"P0002"===r&&/project_field/i.test(t)?Ut("Project field not found.",["Refresh the field list and retry with an existing field name"]):["22023","23505","23514"].includes(r)&&/project_field/i.test(t)?Mt(t.replace(/^project_field_/i,"").replace(/_/g," ")):/invalid input syntax|invalid json|unexpected token|malformed|json parse/i.test(t)?Ft(t):Yt(t)}function Gt(e){const t=e instanceof Error?e.message:String(e&&"object"==typeof e&&"message"in e?e.message??"":e??""),r=e&&"object"==typeof e&&"code"in e?String(e.code??""):"";return["40001","PT409","55P03","57014"].includes(r)||/stale_item_write|soft_deleted_item_write|save_temporarily_unavailable|item_save_busy|statement timeout|request timeout|upstream request timeout|fetch failed|network error|connection (?:closed|reset|refused)/i.test(t)}function Yt(e){return/row.level security|permission denied for/i.test(e)?Ut("Permission denied: your account does not have write access to this project.",["Verify you have editor or admin role in this project","Contact your org admin to upgrade your permissions","Use atoms_list_projects to confirm the project_id is one you have access to"]):Ut(`Database error: ${Rt(e,vt)}`,["This may be a temporary issue — try again","If the error persists, check that the project_id is valid"])}function Kt(e){return null==e?e:`<user_content>${e.replace(/<\/\s*user_content\s*>/gi,"</user_content>")}</user_content>`}function Zt(e){return"string"!=typeof e?e:e.replace(/<\s*\/?\s*user_content\s*>/gi,"")}function Xt(e){return xt.has(e)}function er(e,t,r=!1){return"string"==typeof e?r||null!=t&&xt.has(t)?Kt(e):e:Array.isArray(e)?e.map(e=>er(e,t,r)):e&&"object"==typeof e?Object.fromEntries(Object.entries(e).map(([e,t])=>[e,er(t,e,r||kt.has(e))])):e}function tr(e){if(JSON.stringify(e,null,2).length>wt&&"success"===e.status&&Array.isArray(e.data)){const t=Math.max(1,Math.floor(e.data.length/2)),r={...e,data:e.data.slice(0,t),meta:{...e.meta,truncated:!0,truncation_message:`Response truncated from ${e.data.length} to ${t} items. Use 'offset' parameter or add filters to see more results.`}};return{content:[{type:"text",text:JSON.stringify(er(r),null,2)}],structuredContent:r}}return{content:[{type:"text",text:JSON.stringify(er(e),null,2)}],structuredContent:e}}function rr(e){return{content:[{type:"text",text:JSON.stringify(er(e),null,2)}],structuredContent:e,isError:!0}}q(Ot,{CHARACTER_LIMIT:()=>wt,USER_CONTENT_KEYS:()=>xt,accessDeniedError:()=>Tt,authError:()=>Pt,classifyError:()=>Qt,dbError:()=>Yt,errorResponse:()=>Ut,formatErrorResult:()=>rr,formatToolResult:()=>tr,isAutoWrappedKey:()=>Xt,isItemSaveStopError:()=>Gt,isStaleWriteErrorMessage:()=>Wt,jwtExpiredError:()=>$t,notFoundError:()=>qt,paginationMeta:()=>Ct,parseError:()=>Ft,rateLimitError:()=>Lt,sanitizeForEcho:()=>Rt,sanitizeTagOrLevel:()=>Dt,saveBusyError:()=>Bt,saveUnavailableError:()=>zt,scopeError:()=>Nt,softDeletedItemError:()=>Jt,staleWriteError:()=>Vt,stripUserContentMarkers:()=>Zt,success:()=>Ht,validationError:()=>Mt,wrapMcpUserContentFields:()=>er,wrapUserContent:()=>Kt});var ir,nr=U({"src/tools/_base.ts"(){At(),wt=25e3,bt=50,vt=200,jt=new RegExp("[\\u0000-\\u001F\\u007F]","g"),St=new RegExp("[\\u200B-\\u200F\\u202A-\\u202E\\u2060-\\u206F\\uFEFF]","g"),xt=new Set(["body","description","domain","domains","error","level","levels","message","name","note","summary","title","value","effect","failure_mode","cause","operating_scenario","safe_state","ftti","rationale","hazard","assumptions","source_function"]),kt=new Set(["fields"])}});var ar=U({"src/apps/register.ts"(){ir={resourceDomains:["fonts.googleapis.com","fonts.gstatic.com"]}}});function sr(e){return function(e){return`"${e.replace(/\\/g,"\\\\").replace(/"/g,'\\"')}"`}(`*${t=e,t.replace(/[\\%_*]/g,e=>`\\${e}`)}*`);var t}var or=U({"src/core/postgrest.ts"(){}});async function dr(e,t,r){let i=e.from("items").select("*",{count:"exact"}).eq("project_id",t).is("deleted_at",null).order("created_at",{ascending:!0});r.type&&(i=i.eq("type",r.type)),r.domain&&(i=i.filter("data->tags->domains","cs",JSON.stringify([r.domain]))),r.level&&(i=i.filter("data->tags->>level","eq",r.level)),i=i.range(r.offset,r.offset+r.limit-1);const{data:n,error:a,count:s}=await i;if(a)throw new Error(a.message);return{items:n??[],totalCount:s??0}}async function cr(e,t,r){const{data:i,error:n}=await e.from("items").select("*").eq("id",r).eq("project_id",t).is("deleted_at",null).maybeSingle();if(n)throw new Error(n.message);return i}async function lr(e,t){if(t.baseline_id&&t.as_of)throw new Error("baseline_id and as_of are mutually exclusive");if(t.as_of)return t.as_of;if(t.baseline_id){const{data:r,error:i}=await e.from("baselines").select("project_id, snapshot_at, deleted_at").eq("id",t.baseline_id).maybeSingle();if(i)throw new Error(`resolveSnapshotAt: ${i.message}`);if(!r||null!==r.deleted_at)throw new Error(`Baseline not found: ${t.baseline_id}`);if(r.project_id!==t.project_id)throw new Error("Baseline does not belong to this project");return r.snapshot_at}return null}async function mr(e,t){const{data:r,error:i}=await e.from("baselines").select("id, project_id, name, description, field_definitions_snapshot, snapshot_at, created_at, created_by, locked_at, locked_by, deleted_at, release_version, release_status").eq("id",t).maybeSingle();if(i)throw new Error(`getBaselineById: ${i.message}`);return r&&null===r.deleted_at?r:null}function pr(e){return{id:e.id,project_id:e.project_id,type:e.type,title:e.title,data:e.data,created_by:e.changed_by,updated_by:e.changed_by,created_at:e.valid_from,updated_at:e.valid_from,deleted_at:null}}async function ur(e,t,r){const{data:i,error:n}=await e.rpc("project_state_at",{p_project_id:t,p_snapshot_at:r});if(n)throw new Error(`projectStateAt: ${n.message}`);return i??[]}async function _r(e,t,r,i){const n=r.trim();if(!n)return{items:[],totalCount:0};let a=null;if(i.asset&&i.asset.trim()){const{data:r,error:n}=await e.from("test_results").select("item_id").eq("project_id",t).eq("asset",i.asset.trim());if(n)throw new Error(n.message);const s=new Set;for(const e of r??[])s.add(e.item_id);if(a=Array.from(s),0===a.length)return{items:[],totalCount:0}}let s=e.from("items").select("*",{count:"exact"}).eq("project_id",t).is("deleted_at",null).or(`title.ilike.${sr(n)},data->>body.ilike.${sr(n)},data->>summary.ilike.${sr(n)}`).limit(i.limit);i.type&&(s=s.eq("type",i.type)),a&&(s=s.in("id",a));const{data:o,error:d,count:c}=await s;if(d)throw new Error(d.message);return{items:o??[],totalCount:c??0}}function fr(e,t){const r=new Set(e.map(e=>e.id)),i=new Set,n=[],a=(e,t,a)=>{if(e===t)return;if(!r.has(e)||!r.has(t))return;const s=`${e}|${t}|${a}`;i.has(s)||(i.add(s),n.push({from_id:e,to_id:t,type:a}))};for(const e of t)a(e.from_id,e.to_id,e.type);for(const t of e){const e=t.data?.relationships??{},r=t=>Array.isArray(e[t])?e[t].filter(e=>"string"==typeof e):[];for(const e of r("parents"))a(t.id,e,"parent");for(const e of r("children"))a(t.id,e,"child");for(const e of r("related"))a(t.id,e,"related");for(const e of r("verifies"))a(t.id,e,"verifies");for(const e of r("verified_by"))a(t.id,e,"verified_by");for(const e of r("mitigated_by"))a(t.id,e,"mitigated_by");for(const e of r("mitigates"))a(t.id,e,"mitigates")}return n}async function hr(e,t,r){const i="requirement"===r?"REQ":"test-case"===r?"TC":"note"===r?"NOTE":"TABLE",{data:n,error:a}=await e.rpc("next_item_id",{p_project_id:t,p_prefix:i});if(a)throw new Error(`ID generation failed: ${a.message}`);return n}var gr=U({"src/db/queries.ts"(){or()}}),yr={};async function wr(){try{const e=await Fe(async()=>{const e=await $e();return await async function(e){const{data:t,error:r}=await e.from("projects").select("id, name, description, org_id, created_at, organizations(name)").is("deleted_at",null).order("created_at",{ascending:!1});if(r)throw new Error(r.message);return(t??[]).map(e=>({id:e.id,name:e.name,description:e.description,org_id:e.org_id,org_name:e.organizations?.name??"Unknown",created_at:e.created_at}))}(e)}),t=_t(),r=V,i=J().project_scope,n=i?new Set(i):null;return tr(Ht(e.filter(e=>!r||e.org_id===r).filter(e=>!t||e.id===t).filter(e=>!n||n.has(e.id)).map(e=>({id:e.id,name:e.name,description:e.description,org_id:e.org_id,org_name:e.org_name,created_at:e.created_at}))))}catch(e){return rr(Qt(e))}}q(yr,{listProjectsHandler:()=>wr});var br=U({"src/tools/list-projects.ts"(){Ge(),gr(),ht(),Q(),nr()}}),vr={};async function jr(e){try{const t=await $e();let r,i,n=null;try{n=await lr(t,{baseline_id:e.baseline_id,as_of:e.as_of,project_id:e.project_id})}catch(e){return rr(Yt(e.message))}if(n){const a=(await ur(t,e.project_id,n)).filter(t=>{if(e.type&&t.type!==e.type)return!1;const r=t.data??{};if(e.domain){const t=r?.tags?.domains??[];if(!Array.isArray(t)||!t.includes(e.domain))return!1}return!e.level||r?.tags?.level===e.level});i=a.length,r=a.slice(e.offset,e.offset+e.limit).map(e=>({id:e.id,title:e.title,type:e.type,data:e.data}))}else{const n=await dr(t,e.project_id,{type:e.type,domain:e.domain,level:e.level,limit:e.limit,offset:e.offset});r=n.items,i=n.totalCount}return tr(Ht(r.map(e=>({id:e.id,title:e.title,type:e.type,status:e.data?.status,domains:e.data?.tags?.domains??[],level:e.data?.tags?.level})),Ct(i,e.limit,e.offset)))}catch(e){return e instanceof Error&&e.message.includes("Not authenticated")?rr(Pt()):rr(Yt(e instanceof Error?e.message:String(e)))}}q(vr,{listItemsHandler:()=>jr});var Sr,xr=U({"src/tools/list-items.ts"(){Ge(),gr(),nr()}}),kr={};function Er(e,t){if(!t)return e;const r={...e};for(const e of Sr){const i=t[e];null!=i&&(r[e]=i)}return r}async function Ir(e){try{const r=await $e();let i=null;try{i=await lr(r,{baseline_id:e.baseline_id,as_of:e.as_of,project_id:e.project_id})}catch(e){return rr(Ut(e.message,["Pass baseline_id OR as_of, not both"]))}const n=i?await async function(e,t,r,i){const{data:n,error:a}=await e.rpc("item_state_at",{p_item_id:r,p_project_id:t,p_snapshot_at:i});if(a)throw new Error(`itemStateAt: ${a.message}`);return(n??[])[0]??null}(r,e.project_id,e.item_id,i).then(e=>e?pr(e):null):await cr(r,e.project_id,e.item_id);if(!n)return rr(qt("Item",e.item_id));const a={parents:[],children:[],related:[],verified_by:[],verifies:[],mitigated_by:[],mitigates:[]};if(i){const e=n.data?.relationships??{};for(const t of Object.keys(a)){const r=e[t];Array.isArray(r)&&(a[t]=r.slice())}}else{const t=await async function(e,t,r){const{data:i,error:n}=await e.from("item_relationships").select("from_id, to_id, type").eq("project_id",t).or(`from_id.eq.${r},to_id.eq.${r}`);if(n)throw new Error(n.message);return i??[]}(r,e.project_id,e.item_id),i={parent:"parents",child:"children",related:"related",verifies:"verifies",verified_by:"verified_by",mitigated_by:"mitigated_by",mitigates:"mitigates"},s={parent:"children",child:"parents",verified_by:"verifies",verifies:"verified_by",mitigated_by:"mitigates",mitigates:"mitigated_by",related:"related"},o=(e,t)=>{if(!(e in a))return;const r=a[e];r.includes(t)||r.push(t)},d=new Set([e.item_id]);for(const e of t)d.add(e.from_id),d.add(e.to_id);const c=n.data?.relationships??{};for(const e of Object.keys(a)){const t=c[e];if(Array.isArray(t))for(const e of t)"string"==typeof e&&d.add(e)}let l=new Set([e.item_id]);const m=Array.from(d);if(m.length>0){const{data:t,error:i}=await r.from("items").select("id").eq("project_id",e.project_id).is("deleted_at",null).in("id",m);if(i)throw new Error(i.message);l=new Set((t??[]).map(e=>e.id)),l.add(e.item_id)}const p=e=>l.has(e);for(const r of t)if(r.from_id===e.item_id){if(!p(r.to_id))continue;o(i[r.type]??r.type,r.to_id)}else{if(!p(r.from_id))continue;o(s[r.type]??r.type,r.from_id)}for(const t of Object.keys(a)){const r=c[t];if(Array.isArray(r))for(const i of r)"string"==typeof i&&i!==e.item_id&&p(i)&&o(t,i)}}let s=[];if("test-case"===n.type)if(i)s=(n.data?.runs??[]).map(e=>({base:{result:e.result??"not-run",run_by:e.by??e.run_by??null,run_at:e.date??e.run_at??"",note:e.note??null,..."string"==typeof e.asset?{asset:e.asset}:{}},canonical:e})).filter(e=>"string"==typeof e.base.run_at&&e.base.run_at.length>0).sort((e,t)=>String(t.base.run_at).localeCompare(String(e.base.run_at))).slice(0,20).map(e=>Er(e.base,e.canonical));else{const{data:t,error:i}=await r.from("test_results").select("result, run_by, run_at, note, asset").eq("item_id",e.item_id).eq("project_id",e.project_id).order("run_at",{ascending:!1}).limit(20);if(!i&&t){const e=new Map;for(const t of n.data?.runs??[])"string"==typeof t?.date&&e.set(t.date,t);s=t.map(t=>Er(t,e.get(String(t.run_at))))}}return tr(Ht({id:n.id,type:n.type,title:n.title,summary:n.data?.summary,body:n.data?.body,tags:n.data?.tags??{domains:[]},ownership:n.data?.ownership??{primary:null,additional:[]},relationships:a,links:n.data?.links??[],fields:(t=n.data?.fields,!t||"object"!=typeof t||Array.isArray(t)?{}:Object.fromEntries(Object.entries(t).filter(e=>"string"==typeof e[1]))),status:n.data?.status,..."test-case"===n.type?{latest_result:s[0]?.result??"not-run",test_results:s}:{},metadata:{created_at:n.created_at,created_by:n.created_by,updated_at:n.updated_at,updated_by:n.updated_by,source_id:n.data?.metadata?.source_id}}))}catch(e){return e instanceof Error&&e.message.includes("Not authenticated")?rr(Pt()):rr(Yt(e instanceof Error?e.message:String(e)))}var t}q(kr,{getItemHandler:()=>Ir});var Ar=U({"src/tools/get-item.ts"(){Ge(),gr(),nr(),Sr=["point","release_id","build","environment","tooling","step_results"]}}),Or={};async function Rr(e){try{const t=await $e();let r,i,n=null;try{n=await lr(t,{baseline_id:e.baseline_id,project_id:e.project_id})}catch(e){return rr(Yt(e.message))}if(n){const a=await ur(t,e.project_id,n),s=e.query.trim().toLowerCase(),o=0===s.length?[]:a.filter(t=>{if(e.type&&t.type!==e.type)return!1;const r=t.data??{};return[t.title,r?.body,r?.summary].filter(e=>"string"==typeof e).map(e=>e.toLowerCase()).some(e=>e.includes(s))});i=o.length,r=o.slice(0,e.limit).map(e=>({id:e.id,title:e.title,type:e.type,data:e.data}))}else{const n=await _r(t,e.project_id,e.query,{type:e.type,limit:e.limit,asset:e.asset});r=n.items,i=n.totalCount}return tr(Ht(r.map(e=>({id:e.id,title:e.title,type:e.type,status:e.data?.status,domains:e.data?.tags?.domains??[],level:e.data?.tags?.level})),Ct(i,e.limit,0)))}catch(e){return e instanceof Error&&e.message.includes("Not authenticated")?rr(Pt()):rr(Yt(e instanceof Error?e.message:String(e)))}}q(Or,{searchHandler:()=>Rr});var Dr=U({"src/tools/search.ts"(){Ge(),gr(),nr()}}),Hr={};async function Cr(e){try{const t=await $e(),{access_token:r}=await Re(),{items:i,totalCount:n}=await async function(e,t,r,i){const{ATOMS_SUPABASE_URL:n,ATOMS_SUPABASE_ANON_KEY:a}=await Promise.resolve().then(()=>(Ie(),ke)),s=await fetch(`${n}/functions/v1/embed`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${i.accessToken}`,apikey:a},body:JSON.stringify({input:r})});if(!s.ok)throw new Error(`Embed function failed: ${s.status}`);const{embedding:o}=await s.json(),{data:d,error:c}=await e.rpc("semantic_search",{query_embedding:o,p_project_id:t,match_threshold:.3,match_count:i.limit,p_type:i.type||null});if(c)throw new Error(c.message);return{items:d??[],totalCount:(d??[]).length,method:"semantic"}}(t,e.project_id,e.query,{type:e.type,limit:e.limit,accessToken:r});return tr(Ht(i.map(e=>({id:e.id,title:e.title,type:e.type,status:e.data?.status,domains:e.data?.tags?.domains??[],level:e.data?.tags?.level})),Ct(n,e.limit,0)))}catch(e){return e instanceof Error&&e.message.includes("Not authenticated")?rr(Pt()):rr(Yt(e instanceof Error?e.message:String(e)))}}q(Hr,{semanticSearchHandler:()=>Cr});var Ur=U({"src/tools/semantic-search.ts"(){Ge(),Me(),gr(),nr()}}),qr={};async function Tr(e){try{if(e.baseline_id&&e.as_of)return rr(Mt("baseline_id and as_of are mutually exclusive"));const t=await $e();let r;if(e.baseline_id){const i=await mr(t,e.baseline_id);if(!i)return rr(Mt(`Baseline ${e.baseline_id} not found`));if(i.project_id!==e.project_id)return rr(Mt("Baseline does not belong to this project"));r=i.snapshot_at}else e.as_of&&(r=e.as_of);const{covered:i,uncovered:n,total:a}=await async function(e,t,r){let i,n,a;if(r.snapshotAt){const s=(await ur(e,t,r.snapshotAt)).map(pr);i=s.filter(e=>"requirement"===e.type),n=s.map(e=>({id:e.id,data:e.data})),a=[]}else{const{data:r,error:s}=await e.from("items").select("*").eq("project_id",t).eq("type","requirement").is("deleted_at",null);if(s)throw new Error(s.message);i=r??[];const{data:o,error:d}=await e.from("items").select("id, data").eq("project_id",t).is("deleted_at",null);if(d)throw new Error(d.message);n=o??[];const{data:c,error:l}=await e.from("item_relationships").select("from_id, to_id, type").eq("project_id",t);if(l)throw new Error(l.message);a=c??[]}const s=i.filter(e=>!(r.domain&&!(e.data?.tags?.domains??[]).includes(r.domain)||r.level&&e.data?.tags?.level!==r.level)),o=fr(n,a),d=new Set;for(const e of o)"verifies"===e.type?d.add(e.to_id):"verified_by"===e.type&&d.add(e.from_id);return{covered:s.filter(e=>d.has(e.id)),uncovered:s.filter(e=>!d.has(e.id)),total:s.length}}(t,e.project_id,{domain:e.domain,level:e.level,snapshotAt:r}),s=n.map(e=>({id:e.id,title:e.title,type:e.type,status:e.data?.status,domains:e.data?.tags?.domains??[],level:e.data?.tags?.level}));return tr(Ht({covered:i.length,uncovered:n.length,total:a,coverage_percent:a>0?Math.round(i.length/a*1e3)/10:100,uncovered_items:s}))}catch(e){return e instanceof Error&&e.message.includes("Not authenticated")?rr(Pt()):rr(Yt(e instanceof Error?e.message:String(e)))}}q(qr,{getCoverageHandler:()=>Tr});var Mr,Pr=U({"src/tools/get-coverage.ts"(){Ge(),gr(),nr()}}),$r={};async function Nr(e){try{const t=await $e();let r;if(e.release_id){const i=await mr(t,e.release_id);if(!i)return rr(Mt(`Release ${e.release_id} not found`));if(i.project_id!==e.project_id)return rr(Mt("Release does not belong to this project"));if(null===i.release_version)return rr(Ut(`Baseline "${i.name}" is not a formal Release.`,["Promote the baseline to a Release first (Snapshots view in the app)","Use atoms_list_baselines to see which baselines carry a release_version"]));r=i}else{const{data:i,error:n}=await t.from("baselines").select("id, release_version, release_status").eq("project_id",e.project_id).eq("release_status","released").is("deleted_at",null).maybeSingle();if(n)return rr(Yt(n.message));if(!i)return rr(Mr);r=i}const{data:i,error:n}=await t.rpc("verification_readiness",{p_project_id:e.project_id,p_release_id:r.id});if(n)return rr(Qt(n));const{data:a,error:s}=await t.from("verification_status").select("requirement_id, state, stale, points_total, points_run").eq("project_id",e.project_id).eq("release_id",r.id);if(s)return rr(Yt(s.message));const o=(a??[]).slice().sort((e,t)=>e.requirement_id<t.requirement_id?-1:e.requirement_id>t.requirement_id?1:0),d={release:{id:r.id,version:r.release_version,status:r.release_status},readiness:i??{},requirements:o,...0===o.length?{hint:"projection empty; an admin may need to run rebuild_verification_from_runs for this project"}:{}};if(e.include_gaps){const{data:i,error:n}=await t.rpc("verification_notrun_points",{p_project_id:e.project_id,p_release_id:r.id});if(n)return rr(Qt(n));d.gaps=i??[]}return tr(Ht(d))}catch(e){return rr(Qt(e))}}q($r,{verificationStatusHandler:()=>Nr});var Lr=U({"src/tools/verification-status.ts"(){Ge(),gr(),nr(),Mr=Ut("This project has no current formal Release.",["Promote a locked baseline to a Release first (Snapshots view in the app)","Or pass release_id explicitly to query a specific (e.g. superseded) Release","Use atoms_list_baselines to see which baselines carry a release_version"])}}),Fr={};async function Wr(e){try{const t=await $e(),r=await async function(e,t,r,i){const{data:n,error:a}=await e.from("change_history").select("*").eq("item_id",r).eq("project_id",t).order("changed_at",{ascending:!1}).limit(i);if(a)throw new Error(a.message);return n??[]}(t,e.project_id,e.item_id,e.limit),i=r.map(e=>{const t=[],r=e.old_data,i=e.new_data;if(r&&i){const e=new Set([...Object.keys(r),...Object.keys(i)]);for(const n of e)JSON.stringify(r[n])!==JSON.stringify(i[n])&&t.push(n)}else i?t.push("(created)"):r&&t.push("(deleted)");return{id:e.id,event_type:e.event_type,changed_by:e.changed_by??null,changed_at:e.changed_at,actor:e.actor??"user",session_id:e.session_id??null,fields_changed:t}});return tr(Ht(i,Ct(i.length,e.limit,0)))}catch(e){return e instanceof Error&&e.message.includes("Not authenticated")?rr(Pt()):rr(Yt(e instanceof Error?e.message:String(e)))}}q(Fr,{getHistoryHandler:()=>Wr});var Vr=U({"src/tools/get-history.ts"(){Ge(),gr(),nr()}});function Br(e){return`item_${e.replace(/[^A-Za-z0-9_]/g,"_")||"node"}`}function zr(e){const t=Qr(`${e.id}: ${e.title}`);return"test-case"===e.type?`(["${t}"])`:"note"===e.type?`("${t}")`:`["${t}"]`}function Jr(e){const t=Br(e.from_id),r=Br(e.to_id);return"child"===e.type?`${t} --\x3e ${r}`:"verified_by"===e.type?`${t} -.-> ${r}`:"related"===e.type?`${t} -.- ${r}`:null}function Qr(e){return e.replace(/"/g,"'").replace(/[[\]{}()]/g,"").substring(0,70)}var Gr=U({"src/core/mermaid.ts"(){}}),Yr={};async function Kr(e){try{if(e.baseline_id&&e.as_of)return rr(Mt("baseline_id and as_of are mutually exclusive"));const t=await $e();let r,i,n;if(e.baseline_id){const i=await mr(t,e.baseline_id);if(!i)return rr(Mt(`Baseline ${e.baseline_id} not found`));if(i.project_id!==e.project_id)return rr(Mt("Baseline does not belong to this project"));r=i.snapshot_at}else e.as_of&&(r=e.as_of);if(r){i=(await ur(t,e.project_id,r)).map(pr).map(e=>({id:e.id,title:e.title,type:e.type,data:e.data})),n=fr(i,[])}else{const{data:r,error:a}=await t.from("items").select("id, title, type, data").eq("project_id",e.project_id).is("deleted_at",null);if(a)throw new Error(a.message);i=r??[];const{data:s,error:o}=await t.from("item_relationships").select("from_id, to_id, type").eq("project_id",e.project_id);if(o)throw new Error(o.message);n=fr(i,s??[])}return tr(Ht(function(e){const t=e.items,r=e.relationships??[],i=Math.max(0,e.depth);if(0===t.length)return{mermaid:`graph TD\n empty[${Qr(e.emptyLabel??"No items in project")}]`,node_count:0,edge_count:0};const n=new Map(t.map(e=>[e.id,e])),a=function(e,t,r,i){if(r)return[r];const n=new Set(t.filter(e=>"child"===e.type).map(e=>e.to_id));return e.filter(e=>!n.has(e.id)).filter(e=>i||"test-case"!==e.type).map(e=>e.id)}(t,r,e.rootItemId,e.includeTests),s=a.map(e=>({id:e,depth:0})),o=new Set,d=new Set,c=[],l=[];for(;s.length>0;){const{id:t,depth:a}=s.shift();if(o.has(t)||a>i)continue;const m=n.get(t);if(m&&(e.includeTests||"test-case"!==m.type)){o.add(t),c.push(` ${Br(t)}${zr(m)}`);for(const c of r){if(c.from_id!==t)continue;const r=n.get(c.to_id);if(!r)continue;if(!e.includeTests&&"test-case"===r.type)continue;if(a+1>i)continue;const m=Jr(c);if(!m)continue;const p=`${c.from_id}:${c.type}:${c.to_id}`;d.has(p)||(d.add(p),l.push(` ${m}`),o.has(c.to_id)||s.push({id:c.to_id,depth:a+1}))}}}return{mermaid:["graph TD",...c,...l].join("\n"),node_count:o.size,edge_count:d.size}}({items:i,relationships:n,rootItemId:e.root_item_id,depth:e.depth,includeTests:e.include_tests,emptyLabel:"No items in project"})))}catch(e){return e instanceof Error&&e.message.includes("Not authenticated")?rr(Pt()):rr(Yt(e instanceof Error?e.message:String(e)))}}q(Yr,{exportMermaidHandler:()=>Kr});var Zr=U({"src/tools/export-mermaid.ts"(){Ge(),gr(),nr(),Gr()}}),Xr={};function ei(e){const t=Dt(e??"");return"sub-system"===t?"subsystem":t}async function ti(e,t){const{data:r}=await e.from("metadata").select("domains, levels").eq("project_id",t).maybeSingle();return{domains:r?.domains??[],levels:r?.levels??[]}}async function ri(e,t,r,i){const{error:n}=await e.from("metadata").upsert({project_id:t,domains:r,levels:i},{onConflict:"project_id"});if(n)throw new Error(n.message)}async function ii(e,t,r){const i=Dt(r??""),n=ei(i);if(!i)return!1;try{const{domains:r,levels:a}=await ti(e,t);return!a.some(e=>ei(e)===n)&&(await ri(e,t,r,[...a,i]),!0)}catch{return!1}}async function ni(e){try{const t=await $e(),{levels:r}=await ti(t,e.project_id),{data:i}=await t.from("items").select("data").eq("project_id",e.project_id).is("deleted_at",null),n={};for(const e of i??[]){const t=e.data?.tags,r=ei(t?.level);r&&(n[r]=(n[r]??0)+1)}return tr(Ht({levels:r.map((e,t)=>({name:e,prefix:`L${t}`,item_count:n[ei(e)]??0})),total:r.length,note:"L# is the level's position in this list (0-based). Names are user-defined."}))}catch(e){return e instanceof Error&&e.message.includes("Not authenticated")?rr(Pt()):rr(Yt(e instanceof Error?e.message:String(e)))}}async function ai(e){try{const t=await $e();try{await Be(t,e.project_id)}catch(e){if(e instanceof Error&&"VIEWER_ROLE"===e.message)return rr(Tt("Viewer"));throw e}const{domains:r,levels:i}=await ti(t,e.project_id),n=Dt(e.name),a=ei(n);if(!n)return rr(Mt("Level name cannot be empty."));if(i.some(e=>ei(e)===a))return rr(Mt(`Level "${n}" already exists in this project.`));const s=i.length;return await ri(t,e.project_id,r,[...i,n]),tr(Ht({level:{name:n,prefix:`L${s}`},message:`Level "${n}" registered as L${s}. Tag items with level="${n}".`}))}catch(e){return e instanceof Error&&e.message.includes("Not authenticated")?rr(Pt()):rr(Yt(e instanceof Error?e.message:String(e)))}}q(Xr,{createLevelHandler:()=>ai,ensureLevelRegistered:()=>ii,listLevelsHandler:()=>ni});var si,oi,di,ci,li,mi,pi,ui,_i,fi,hi,gi=U({"src/tools/levels.ts"(){Ge(),nr()}});function yi(e){return(e??"").trim().toLowerCase()}function wi(e,t){const r=yi(e);if(!r)return null;for(const e of t)if(yi(e)===r)return e;return null}function bi(e){const t=e?.severity||"",r=e?.exposure||"",i=e?.controllability||"";return t&&r&&i?"S0"===t||"E0"===r||"C0"===i?"QM":di[`${t}-${r}-${i}`]||si:si}function vi(e){const t=parseInt(e?.severity||"",10),r=parseInt(e?.occurrence||"",10),i=parseInt(e?.detection||"",10);return Number.isNaN(t)||Number.isNaN(r)||Number.isNaN(i)?si:String(t*r*i)}function ji(e,t){return"hara"===e?bi(t):vi(t)}function Si(e,t,r){if(r)return!1;if("hara"===e){const e=bi(t);return"C"===e||"D"===e}const i=parseInt(vi(t),10);return!Number.isNaN(i)&&i>=200}function xi(e,t,r){if(!e?.approved||!e.approved_at)return!1;const i=Date.parse(e.approved_at);return!Number.isNaN(i)&&[...e.source_req?[e.source_req]:[],...t].some(e=>{const t=r(e),n=t?Date.parse(t):NaN;return!Number.isNaN(n)&&n>i})}function ki(e){return Array.isArray(e)?e.filter(e=>"string"==typeof e):[]}function Ei(e,t,r=yi){const i=new Set(t.map(r).filter(e=>e.length>0));return e.filter(e=>i.has(r(e)))}function Ii(e){const t=e.length>1;return`Function-flagged tag${t?"s":""} used as domain${t?"s":""}: ${e.join(", ")}. A function is not a domain tag: seed a HARA row with it instead (atoms_create_analysis source_functions, or atoms_fill_row source_function), or unflag it in Taxonomy Management.`}var Ai,Oi,Ri=U({"src/core/analysis.ts"(){si="—",oi=["prevention","detection","mitigation"],di={"S1-E1-C1":"QM","S1-E1-C2":"QM","S1-E1-C3":"QM","S1-E2-C1":"QM","S1-E2-C2":"QM","S1-E2-C3":"QM","S1-E3-C1":"QM","S1-E3-C2":"QM","S1-E3-C3":"A","S1-E4-C1":"QM","S1-E4-C2":"A","S1-E4-C3":"B","S2-E1-C1":"QM","S2-E1-C2":"QM","S2-E1-C3":"QM","S2-E2-C1":"QM","S2-E2-C2":"QM","S2-E2-C3":"A","S2-E3-C1":"QM","S2-E3-C2":"A","S2-E3-C3":"B","S2-E4-C1":"A","S2-E4-C2":"B","S2-E4-C3":"C","S3-E1-C1":"QM","S3-E1-C2":"QM","S3-E1-C3":"A","S3-E2-C1":"QM","S3-E2-C2":"A","S3-E2-C3":"B","S3-E3-C1":"A","S3-E3-C2":"B","S3-E3-C3":"C","S3-E4-C1":"B","S3-E4-C2":"C","S3-E4-C3":"D"},li={hara:{severity:["S0","S1","S2","S3"],exposure:["E0","E1","E2","E3","E4"],controllability:["C0","C1","C2","C3"]},dfmea:{severity:ci=["1","2","3","4","5","6","7","8","9","10"],occurrence:ci,detection:ci}},mi={hara:["failure_mode","operating_scenario","hazard","assumptions","severity","exposure","controllability","safe_state","ftti","rationale","source_function"],dfmea:["failure_mode","cause","severity","occurrence","detection","rationale"]},pi={hara:"safety-goal",dfmea:"failure-mode"},ui={hara:"SG",dfmea:"FM"},_i={hara:"HARA",dfmea:"DFMEA"},fi={hara:"hara_iso26262",dfmea:"dfmea_rpn"},hi="ISO 26262"}}),Di=U({"src/core/project-field-counts.ts"(){}}),Hi={};async function Ci(e,t){const{data:r,error:i}=await e.from("metadata").select("fields").eq("project_id",t).maybeSingle();if(i)throw i;return Ui(r?.fields)}function Ui(e){if(!Array.isArray(e))return[];const t=[],r=new Set;for(const i of e){if(!i||"object"!=typeof i)continue;const e=i,n="string"==typeof e.name?e.name.trim():"";if(!n)continue;const a=n.toLowerCase();if(r.has(a))continue;r.add(a);const s=void 0===e.type?"text":"string"==typeof e.type&&Oi.has(e.type)?e.type:"text",o=Array.isArray(e.options)?Wi(e.options):[];t.push({name:n,type:s,..."select"===s?{options:o}:{}})}return t}function qi(e,t){const r=e.name.trim();if(!r)return"Field name cannot be empty.";if(r.length>60)return"Field name must be 60 characters or fewer.";if(!Oi.has(e.type))return"Field type is not supported.";const i=t.find(e=>e.name.toLowerCase()===r.toLowerCase());if(i)return`Field "${i.name}" already exists.`;if("select"===e.type){const t=Wi(e.options??[]);if(0===t.length)return"Select fields need at least one option.";if(t.length>100)return"Select fields support at most 100 options.";if(t.some(e=>e.length>120))return"Select options must be 120 characters or fewer."}return null}function Ti(e,t){const r=new Set(e.map(e=>e.name.toLowerCase()));return Object.keys(t).filter(e=>!r.has(e.toLowerCase()))}function Mi(e,t){const r=new Map(e.map(e=>[e.name.toLowerCase(),e.name])),i={};for(const[e,n]of Object.entries(t))i[r.get(e.toLowerCase())??e]=n;return i}function Pi(e,t,r=new Set){const i=Ti(e,t);if(i.length>0)return{error:`Unknown field${i.length>1?"s":""}: ${i.join(", ")}. Defined: ${e.length>0?e.map(e=>e.name).join(", "):"(none)"}. Define fields first with atoms_create_field.`};const n={},a=new Map(e.map(e=>[e.name.toLowerCase(),e]));for(const[e,i]of Object.entries(t)){const t=a.get(e.toLowerCase());if(!t)continue;const s=$i(t,i,r);if(s.error)return{error:s.error};s.value&&(n[t.name]=s.value)}return{fields:n}}function $i(e,t,r){const i=t.trim();if(!i)return{value:""};if(i.length>1e4)return{error:`${e.name} is too long.`};switch(e.type){case"text":case"long":return{value:i};case"bool":{const t=i.toLowerCase();return"yes"===t?{value:"yes"}:"no"===t?{value:"no"}:{error:`${e.name} must be yes or no.`}}case"date":return function(e){if(!/^\d{4}-\d{2}-\d{2}$/.test(e))return!1;const t=new Date(`${e}T00:00:00.000Z`);return!Number.isNaN(t.getTime())&&t.toISOString().slice(0,10)===e}(i)?{value:i}:{error:`${e.name} must be an ISO date.`};case"person":return Fi(i)&&r.has(i)?{value:i}:{error:`${e.name} must be an active project member ID.`};case"select":return(e.options??[]).includes(i)?{value:i}:{error:`${e.name} must be one of: ${(e.options??[]).join(", ")}.`};case"number":return Number.isFinite(Number(i))?{value:i}:{error:`${e.name} must be numeric.`};case"link":{const t=function(e){if(!e)return{label:"",url:""};try{const t=JSON.parse(e);if(t&&"object"==typeof t&&"string"==typeof t.url){const e=t.label;return{label:"string"==typeof e?e:"",url:t.url}}}catch{}return{label:"",url:e}}(i);return function(e){try{const t=new URL(e);return"http:"===t.protocol||"https:"===t.protocol}catch{return!1}}(t.url)?{value:Ni(t.label,t.url)}:{error:`${e.name} must be an http(s) URL.`}}}}function Ni(e,t){const r=t.trim();return r?JSON.stringify({label:e.trim(),url:r}):""}async function Li(e,t,r,i){const n=new Map(r.map(e=>[e.name.toLowerCase(),e])),a=new Map;for(const[e,t]of Object.entries(i)){const r=n.get(e.toLowerCase());"person"===r?.type&&t.trim()&&a.set(t.trim(),r)}const s=new Set;for(const[r,i]of a){if(!Fi(r))return{error:`${i.name} must be an active project member ID.`};const{data:n,error:a}=await e.rpc("is_active_project_member_for_field",{p_project_id:t,p_person_user_id:r,p_actor_user_id:We()});if(a)return{error:a.message};!0===n&&s.add(r)}return Pi(r,i,s)}function Fi(e){return/^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(e)}function Wi(e){const t=[],r=new Set;for(const i of e){if("string"!=typeof i)continue;const e=i.trim();if(!e)continue;const n=e.toLowerCase();r.has(n)||(r.add(n),t.push(e))}return t}async function Vi(e){try{const t=await $e(),r=await Ci(t,e.project_id),i=await async function(e,t,r,i=500){const n=Object.fromEntries(r.map(e=>[e.name,0])),a=new Map(r.map(e=>[e.name.toLowerCase(),e.name]));let s=0;for(;;){const{data:r,error:o}=await e.from("items").select("data").eq("project_id",t).is("deleted_at",null).order("id",{ascending:!0}).range(s,s+i-1);if(o)throw o;const d=r??[];for(const e of d){const t=e.data?.fields;if(t&&"object"==typeof t&&!Array.isArray(t))for(const[e,r]of Object.entries(t)){const t=a.get(e.toLowerCase());t&&"string"==typeof r&&r.length>0&&(n[t]+=1)}}if(d.length<i)return n;s+=d.length}}(t,e.project_id,r);return tr(Ht({fields:r.map(e=>({name:e.name,type:e.type,options:e.options??[],item_count:i[e.name]??0})),total:r.length}))}catch(e){return e instanceof Error&&e.message.includes("Not authenticated")?rr(Pt()):rr(Qt(e))}}async function Bi(e){try{const t=await $e();try{await ze(t,e.project_id)}catch(e){if(e instanceof Error&&"PROJECT_FIELD_ADMIN_OR_OWNER_REQUIRED"===e.message)return rr(Tt("Project admin or owner"));throw e}const{data:r}=await t.from("metadata").select("fields").eq("project_id",e.project_id).maybeSingle(),i=Ui(r?.fields),n={name:e.name.trim(),type:e.type??"text",..."select"===e.type?{options:Wi(e.options??[])}:{}},a=qi(n,i);if(a)return rr(Mt(a));const{data:s,error:o}="organization"===e.scope?await t.rpc("create_org_project_field_definition",{p_project_id:e.project_id,p_field:n,p_actor_user_id:We()}):await t.rpc("mutate_project_field_definition",{p_project_id:e.project_id,p_operation:"create",p_field:n,p_field_name:null,p_actor_user_id:We()});if(o)throw o;return tr(Ht({field:s?.field??n,..."organization"===e.scope?{created_projects:s?.created_projects??[],skipped_projects:s?.skipped_projects??[]}:{},message:`Field "${n.name}" defined. Set values with atoms_update_item fields={"${n.name}": "<value>"}.`}))}catch(e){return e instanceof Error&&e.message.includes("Not authenticated")?rr(Pt()):rr(Qt(e))}}async function zi(e){try{const t=await $e();try{await ze(t,e.project_id)}catch(e){if(e instanceof Error&&"PROJECT_FIELD_ADMIN_OR_OWNER_REQUIRED"===e.message)return rr(Tt("Project admin or owner"));throw e}const r=e.name.trim(),{data:i,error:n}=await t.rpc("mutate_project_field_definition",{p_project_id:e.project_id,p_operation:"delete",p_field:null,p_field_name:r,p_actor_user_id:We()});if(n)throw n;return tr(Ht({deleted:i?.deleted??r}))}catch(e){return e instanceof Error&&e.message.includes("Not authenticated")?rr(Pt()):rr(Qt(e))}}async function Ji(e){try{const t=await $e();try{await ze(t,e.project_id)}catch(e){if(e instanceof Error&&"PROJECT_FIELD_ADMIN_OR_OWNER_REQUIRED"===e.message)return rr(Tt("Project admin or owner"));throw e}const r=Array.isArray(e.names)?e.names.map(e=>String(e??"").trim()).filter(e=>e.length>0):[];if(0===r.length)return rr(Mt("names must be a non-empty array of existing field names, in the order you want them"));const i=await Ci(t,e.project_id),n=i.map(e=>e.name.toLowerCase()).sort(),a=r.map(e=>e.toLowerCase()).sort();if(n.length!==a.length||n.some((e,t)=>e!==a[t]))return rr(Mt(`names must list every existing field exactly once. Defined: ${i.length>0?i.map(e=>e.name).join(", "):"(none)"}`));const{data:s,error:o}=await t.rpc("mutate_project_field_definition",{p_project_id:e.project_id,p_operation:"reorder",p_field:r,p_field_name:null,p_actor_user_id:We()});if(o)throw o;return tr(Ht({fields:Ui(s?.fields).map(e=>e.name)}))}catch(e){return e instanceof Error&&e.message.includes("Not authenticated")?rr(Pt()):rr(Qt(e))}}q(Hi,{PROJECT_FIELD_TYPES:()=>Ai,canonicalizeFieldKeys:()=>Mi,createFieldHandler:()=>Bi,deleteFieldHandler:()=>zi,findUnknownFieldNames:()=>Ti,getProjectFields:()=>Ci,listFieldsHandler:()=>Vi,normalizeProjectFields:()=>Ui,reorderFieldsHandler:()=>Ji,validateAndCanonicalizeFieldValues:()=>Pi,validateProjectFieldDefinition:()=>qi,validateProjectFieldValuesForWrite:()=>Li});var Qi=U({"src/tools/fields.ts"(){Ge(),nr(),Di(),Ai=["text","long","bool","date","person","select","number","link"],Oi=new Set(Ai)}}),Gi={};async function Yi(e){try{const t=await $e(),r=We();try{await Be(t,e.project_id)}catch(e){if(e instanceof Error&&"VIEWER_ROLE"===e.message)return rr(Tt("Viewer"));throw e}e.title=Zt(e.title),void 0!==e.body&&(e.body=Zt(e.body)),void 0!==e.summary&&(e.summary=Zt(e.summary)),void 0!==e.level&&(e.level=Zt(e.level)),e.domains&&(e.domains=e.domains.map(e=>Zt(e)));const i=(e.domains??[]).map(Dt).filter(e=>e.length>0),n=void 0!==e.level?Dt(e.level):void 0;if(i.length>0){const{data:r,error:n}=await t.from("metadata").select("domains, function_tags").eq("project_id",e.project_id).maybeSingle();if(n)throw new Error(`metadata read failed: ${n.message}`);const a=r?.domains??[],s=Ei(i,ki(r?.function_tags),Dt);if(s.length>0)return rr(Mt(Ii(s)));if(a.length>0){const e=new Set(a.map(Dt).filter(e=>e.length>0)),t=i.filter(t=>!e.has(t));if(t.length>0)return rr(Mt(`Unknown domain${t.length>1?"s":""}: ${t.join(", ")}. Registered: ${a.join(", ")}. Add new domains via Taxonomy Management in the ATOMS web app first.`))}}let a;if(e.fields&&Object.keys(e.fields).length>0){const{getProjectFields:r,validateProjectFieldValuesForWrite:i}=await Promise.resolve().then(()=>(Qi(),Hi)),n=await r(t,e.project_id),s=await i(t,e.project_id,n,e.fields);if(s.error)return rr(Mt(s.error));a=s.fields}const s=await hr(t,e.project_id,e.type),o=(new Date).toISOString(),d={id:s,type:e.type,title:e.title,summary:e.summary,body:e.body,tags:{domains:i,level:n??""},ownership:{primary:null,additional:[]},relationships:{parents:e.parent_ids??[],children:[],related:[],verifies:[],verified_by:[]},links:[],...a?{fields:a}:{},metadata:{created_at:o,created_by:r,updated_at:o,updated_by:r}},{error:c}=await t.rpc("save_item_with_revision",{p_item_id:s,p_project_id:e.project_id,p_type:e.type,p_title:e.title,p_data:d,p_user_id:r,p_event_source:"mcp",p_event_session_id:cs()??null,p_diff_summary:null});if(c)throw new Error(c.message);if(n&&await ii(t,e.project_id,n),e.parent_ids&&e.parent_ids.length>0)for(const i of e.parent_ids){const{error:n}=await t.rpc("apply_link_change",{p_project_id:e.project_id,p_from_id:s,p_to_id:i,p_type:"parent",p_action:"add",p_user_id:r,p_event_source:"mcp"});n&&process.stderr.write(`[atoms-mcp] Warning: parent link sync failed for ${i}: ${n.message}\n`)}return await t.from("change_history").insert({item_id:s,project_id:e.project_id,changed_by:r,event_type:"created",old_data:null,new_data:d,actor:ls(),session_id:cs()}).then(({error:e})=>{e&&process.stderr.write(`[atoms-mcp] Warning: change_history log failed: ${e.message}\n`)}),!1===e.echo?tr(Ht({id:s,type:e.type})):tr(Ht({id:s,type:e.type,title:e.title,summary:e.summary??"",body:e.body??"",domains:i,level:n??"",project_id:e.project_id}))}catch(e){return rr(Qt(e))}}q(Gi,{createItemHandler:()=>Yi});var Ki=U({"src/tools/create-item.ts"(){Ge(),gr(),ps(),nr(),gi(),Ri()}});function Zi(e,t){const r=function(e){return`SG-${e}`}(t);return{id:e,outcome:"captured_as_change",change:{id:r,sg_number:t,status:"open"},message:`Captured as change ${r}, awaiting approval. This requirement stays at its approved revision until a human approver approves the change; no MCP tool can approve, dismiss, or apply it.`}}function Xi(e){return{id:e,outcome:"under_review",message:"This requirement is under change control. Your edit was submitted; if it changed the requirement's meaning it is now a change awaiting a human approver, otherwise it wrote through live. Reload the item to confirm. No MCP tool can approve, dismiss, or apply a change."}}var en=U({"src/core/change-control.ts"(){}}),tn={};async function rn(e){try{const t=await $e(),r=We();try{await Be(t,e.project_id)}catch(e){if(e instanceof Error&&"VIEWER_ROLE"===e.message)return rr(Tt("Viewer"));throw e}const i=async(i,n,a,s)=>{const o=await cr(t,e.project_id,i);if(!o)return{updated_fields:[],error:`Item "${i}" not found`};const d="approved"===o.approval_state;void 0!==n.title&&(n.title=Zt(n.title)),void 0!==n.body&&(n.body=Zt(n.body)),void 0!==n.summary&&(n.summary=Zt(n.summary)),void 0!==n.level&&(n.level=Zt(n.level)),void 0!==n.domains&&(n.domains=n.domains.map(e=>Zt(e)));const c=void 0!==n.domains?n.domains.map(Dt).filter(e=>e.length>0):void 0,l=void 0!==n.level?Dt(n.level):void 0;if(void 0!==c&&c.length>0){const{data:r,error:i}=await t.from("metadata").select("domains, function_tags").eq("project_id",e.project_id).maybeSingle();if(i)throw new Error(`metadata read failed: ${i.message}`);const n=r?.domains??[],a=Ei(c,ki(r?.function_tags),Dt);if(a.length>0)return{updated_fields:[],error:Ii(a)};if(n.length>0){const e=new Set(n.map(Dt).filter(e=>e.length>0)),t=c.filter(t=>!e.has(t));if(t.length>0)return{updated_fields:[],error:`Unknown domains: ${t.join(", ")}`}}}const m={...o.data},p={...o.data},u=[],_=(e,t)=>JSON.stringify(e)!==JSON.stringify(t);if(void 0!==n.title&&_(o.data.title,n.title)&&(p.title=n.title,u.push("title")),void 0!==n.body&&_(o.data.body,n.body)&&(p.body=n.body,u.push("body")),void 0!==n.summary&&_(o.data.summary,n.summary)&&(p.summary=n.summary,u.push("summary")),void 0!==c&&_(o.data.tags?.domains??[],c)&&(p.tags={...p.tags,domains:c},u.push("domains")),void 0!==l&&_(o.data.tags?.level,l)&&(p.tags={...p.tags,level:l},u.push("level")),void 0!==n.status&&_(o.data.status,n.status)&&(p.status=n.status,u.push("status")),void 0!==n.steps&&_(o.data.steps,n.steps)&&(p.steps=n.steps,u.push("steps")),void 0!==n.params&&_(o.data.params,n.params)&&(p.params=n.params,u.push("params")),void 0!==n.fields&&Object.keys(n.fields).length>0){const{getProjectFields:r,validateProjectFieldValuesForWrite:i,canonicalizeFieldKeys:a}=await Promise.resolve().then(()=>(Qi(),Hi)),s=await r(t,e.project_id),d=await i(t,e.project_id,s,n.fields);if(d.error)return{updated_fields:[],error:d.error};const c=a(s,o.data.fields??{});for(const[e,t]of Object.entries({...a(s,n.fields),...d.fields??{}}))""===t?delete c[e]:c[e]=t;_(o.data.fields??{},c)&&(p.fields=c,u.push("fields"))}if(0===u.length)return{updated_fields:[],updatedData:o.data,itemType:o.type};p.metadata={...p.metadata,updated_at:(new Date).toISOString(),updated_by:r};const f=n.title??o.title,{data:h,error:g}=await t.rpc("save_item_with_revision",{p_item_id:i,p_project_id:e.project_id,p_type:o.type,p_title:f,p_data:p,p_user_id:r,p_event_source:"mcp",p_event_session_id:cs()??null,p_diff_summary:null,p_expected_updated_at:n.expected_updated_at??o.updated_at});if(g)return{updated_fields:[],error:g};if(!h)return{updated_fields:[],error:`Update silently failed for "${i}" — RPC returned no row. Likely RLS policy denied the write; verify your role and project access.`};u.includes("level")&&l&&await ii(t,e.project_id,l);const y={item_id:i,project_id:e.project_id,changed_by:r,event_type:"updated",old_data:m,new_data:p,actor:ls(),session_id:cs()};if(a&&(y.batch_id=a),s&&(y.reason=s),await t.from("change_history").insert(y).then(({error:e})=>{e&&process.stderr.write(`[atoms-mcp] Warning: change_history log failed: ${e.message}\n`)}),d&&function(e){return e.some(e=>"title"===e||"body"===e||"type"===e)}(u)){const r=await async function(e,t,r){const{data:i,error:n}=await e.from("item_suggestions").select("sg_number").eq("project_id",t).eq("item_id",r).eq("status","open").maybeSingle();return n||!i?null:"number"==typeof i.sg_number?i.sg_number:null}(t,e.project_id,i);return null!==r?{updated_fields:u,updatedData:p,itemType:o.type,capturedSg:r}:{updated_fields:u,updatedData:p,itemType:o.type,capturedUnknown:!0}}return{updated_fields:u,updatedData:p,itemType:o.type}};if(e.updates&&Array.isArray(e.updates)){const t=crypto.randomUUID(),r=!0===e.continue_on_error,n=[],a=[];for(const s of e.updates){const{updated_fields:o,capturedSg:d,capturedUnknown:c,error:l}=await i(s.item_id,s,t,e.reason);if(l){if(Gt(l))return rr(Qt(l));if(!r){const e=l instanceof Error?l.message:String("object"==typeof l&&l&&"message"in l?l.message??"":l);return rr(Mt(`Batch stopped at "${s.item_id}": ${e}. ${n.length} item(s) already updated.`))}a.push({item_id:s.item_id,error:String(l)})}else void 0!==d?n.push(Zi(s.item_id,d)):c?n.push(Xi(s.item_id)):n.push({id:s.item_id,updated_fields:o})}return tr(Ht({batch_id:t,updated:n.length,results:!1===e.echo?void 0:n,errors:a}))}if(!e.item_id)return rr(Mt("Provide either item_id (single) or updates[] (array form)."));const{updated_fields:n,updatedData:a,itemType:s,capturedSg:o,capturedUnknown:d,error:c}=await i(e.item_id,{title:e.title,body:e.body,summary:e.summary,domains:e.domains,level:e.level,status:e.status,fields:e.fields,steps:e.steps,params:e.params,expected_updated_at:e.expected_updated_at},void 0,e.reason);if(c){const t=c instanceof Error?c.message:String("object"==typeof c&&c&&"message"in c?c.message??"":c);return t.includes("not found")?rr(qt("Item",e.item_id)):t.includes("Unknown domain")||t.includes("Unknown field")||t.includes("Function-flagged tag")?rr(Mt(t)):Wt(t)||Gt(c)?rr(Qt(c)):rr(Yt(t))}if(void 0!==o)return tr(Ht(Zi(e.item_id,o)));if(d)return tr(Ht(Xi(e.item_id)));if(!1===e.echo)return tr(Ht({id:e.item_id,updated_fields:n}));const l=a??{};return tr(Ht({id:e.item_id,type:s,title:l.title??"",summary:l.summary??"",body:l.body??"",domains:l.tags?.domains??[],level:l.tags?.level??"",updated_fields:n}))}catch(e){return rr(Qt(e))}}q(tn,{updateItemHandler:()=>rn});var nn,an,sn=U({"src/tools/update-item.ts"(){Ge(),gr(),ps(),nr(),gi(),en(),Ri()}});function on(e){const t=process.env.ATOMS_MCP_REQUIRE_CONFIRMATION?.trim();if(t){const r=t.toLowerCase();if("1"===r||"true"===r||"all"===r)return!0;if(t.split(",").map(e=>e.trim()).filter(Boolean).includes(e))return!0}return!!J().require_confirmation_for.includes(e)}function dn(e,t=nn){const r=Date.now()+t,i={...e,exp:r},n=Buffer.from(JSON.stringify(i)).toString("base64url");return{token:`${n}.${p.createHmac("sha256",an).update(n).digest("base64url")}`,expires_at:new Date(r).toISOString(),expires_in_seconds:Math.round(t/1e3)}}function cn(e,t){if("string"!=typeof e||!e.includes("."))return{valid:!1,reason:"Token format invalid"};const[r,i]=e.split(".");if(!r||!i)return{valid:!1,reason:"Token format invalid"};const n=p.createHmac("sha256",an).update(r).digest("base64url"),a=Buffer.from(i,"utf8"),s=Buffer.from(n,"utf8");if(a.length!==s.length||!p.timingSafeEqual(a,s))return{valid:!1,reason:"Token signature invalid"};let o;try{o=JSON.parse(Buffer.from(r,"base64url").toString("utf8"))}catch{return{valid:!1,reason:"Token payload malformed"}}return"number"!=typeof o.exp||o.exp<Date.now()?{valid:!1,reason:"Token expired"}:o.tool!==t.tool?{valid:!1,reason:"Token issued for a different tool"}:o.project_id!==t.project_id?{valid:!1,reason:"Token issued for a different project"}:o.target!==t.target?{valid:!1,reason:"Token issued for a different target"}:{valid:!0}}function ln(e){if(Array.isArray(e))return e.map(ln);if(null!==e&&"object"==typeof e){const t={};for(const r of Object.keys(e).sort())t[r]=ln(e[r]);return t}return e}function mn(e){const t=JSON.stringify(ln(e));return p.createHash("sha256").update(t).digest("base64url").slice(0,16)}var pn,un=U({"src/middleware/confirmation.ts"(){Q(),nn=6e4,an=(()=>{const e=process.env.ATOMS_MCP_CONFIRMATION_SECRET;return e&&e.length>=16?e:p.randomBytes(32).toString("base64url")})()}}),_n={};async function fn(e){try{const t=await $e(),r=We();try{await Be(t,e.project_id)}catch(e){if(e instanceof Error&&"VIEWER_ROLE"===e.message)return rr(Tt("Viewer"));throw e}const i=await async function(e,t,r){const{data:i,error:n}=await e.from("items").select("*").eq("id",r).eq("project_id",t).maybeSingle();if(n)throw new Error(n.message);return i}(t,e.project_id,e.item_id);if(!i)return rr(qt("Item",e.item_id));if(i.deleted_at)return rr(Mt(`Item ${e.item_id} is already deleted`));if(on(pn)){const t={tool:pn,project_id:e.project_id,target:e.item_id};if(!e.confirmation_token){const r=dn(t);return tr(Ht({status:"confirmation_required",preview:{id:e.item_id,title:i.title,type:i.type,would_soft_delete:!0},confirmation_token:r.token,expires_at:r.expires_at,expires_in_seconds:r.expires_in_seconds,message:`Re-call atoms_delete_item with the same project_id, item_id, and confirmation_token within ${r.expires_in_seconds}s to execute.`}))}const r=cn(e.confirmation_token,t);if(!r.valid)return rr(Mt(`confirmation_token rejected: ${r.reason}`))}const{error:n}=await t.rpc("soft_delete_item_with_revision",{p_item_id:e.item_id,p_project_id:e.project_id,p_user_id:r,p_expected_updated_at:i.updated_at,p_event_source:"mcp",p_actor:ls(),p_session_id:cs()});if(n)throw new Error(n.message);return tr(Ht({id:e.item_id,title:i.title,type:i.type,deleted:!0,message:`Item ${e.item_id} soft-deleted. It can still be found in audit logs.`}))}catch(e){return rr(Qt(e))}}q(_n,{deleteItemHandler:()=>fn});var hn=U({"src/tools/delete-item.ts"(){Ge(),gr(),ps(),un(),nr(),pn="atoms_delete_item"}}),gn={};async function yn(e){try{const t=await $e(),r=We();try{await Be(t,e.project_id)}catch(e){if(e instanceof Error&&"VIEWER_ROLE"===e.message)return rr(Tt("Viewer"));throw e}const{data:i,error:n}=await t.from("items").select("*").eq("id",e.item_id).eq("project_id",e.project_id).maybeSingle();if(n)throw new Error(n.message);if(!i)return rr(qt("Item",e.item_id));if(!i.deleted_at)return rr(Ut(`Item ${e.item_id} is not deleted and cannot be restored.`,["Use atoms_get_item to view the current item state","Use atoms_delete_item first if you intended to delete it"]));const{error:a}=await t.rpc("save_item_with_revision",{p_item_id:e.item_id,p_project_id:e.project_id,p_type:i.type,p_title:i.title,p_data:i.data,p_user_id:r,p_event_source:"restore",p_event_session_id:cs()??null,p_diff_summary:{changed_fields:["deleted_at"],restored:!0},p_expected_updated_at:i.updated_at});if(a)throw new Error(a.message);const{error:s}=await t.rpc("repair_live_item_relationships_from_jsonb",{p_project_id:e.project_id,p_item_id:e.item_id});if(s)throw new Error(`Relationship repair failed: ${s.message}`);return await t.from("change_history").insert({item_id:e.item_id,project_id:e.project_id,changed_by:r,event_type:"restored",old_data:null,new_data:i.data,actor:ls(),session_id:cs()}).then(({error:e})=>{e&&process.stderr.write(`[atoms-mcp] Warning: change_history log failed: ${e.message}\n`)}),tr(Ht({id:e.item_id,title:i.title,type:i.type,restored:!0,message:`Item ${e.item_id} restored successfully.`}))}catch(e){return rr(Qt(e))}}q(gn,{restoreItemHandler:()=>yn});var wn=U({"src/tools/restore-item.ts"(){Ge(),ps(),nr()}}),bn={};async function vn(e){try{const t=await $e(),r=We();try{await Be(t,e.project_id)}catch(e){if(e instanceof Error&&"VIEWER_ROLE"===e.message)return rr(Tt("Viewer"));throw e}const i=async(i,n,a)=>{const s=await cr(t,e.project_id,i.from_id);if(!s)throw new Error(`Item "${i.from_id}" not found`);const o=await cr(t,e.project_id,i.to_id);if(!o)throw new Error(`Item "${i.to_id}" not found`);const{error:d}=await t.rpc("apply_link_change",{p_project_id:e.project_id,p_from_id:i.from_id,p_to_id:i.to_id,p_type:i.type,p_action:i.action,p_user_id:r,p_event_source:"mcp",p_expected_from_updated_at:i.expected_from_updated_at??s.updated_at,p_expected_to_updated_at:i.expected_to_updated_at??o.updated_at});if(d)throw new Error(d.message);const c={item_id:i.from_id,project_id:e.project_id,changed_by:r,event_type:"updated",old_data:null,new_data:{relationship_change:{action:i.action,type:i.type,from:i.from_id,to:i.to_id}},actor:ls(),session_id:cs()};n&&(c.batch_id=n),a&&(c.reason=a),await t.from("change_history").insert(c).then(({error:e})=>{e&&process.stderr.write(`[atoms-mcp] Warning: change_history log failed: ${e.message}\n`)})};if(e.operations&&Array.isArray(e.operations)){for(const t of e.operations)if(t.from_id===t.to_id)return rr(Mt(`Self-reference in batch: "${t.from_id}"`));const r=[...new Set(e.operations.flatMap(e=>[e.from_id,e.to_id]))],{data:n}=await t.from("items").select("id").eq("project_id",e.project_id).is("deleted_at",null).in("id",r),a=new Set((n??[]).map(e=>e.id)),s=r.filter(e=>!a.has(e));if(s.length>0)return rr(qt("Items",s.join(", ")));const{data:o}=await t.from("item_relationships").select("from_id, to_id, type").eq("project_id",e.project_id),d=new Map;for(const e of o??[])"parent"===e.type&&(d.has(e.from_id)||d.set(e.from_id,new Set),d.get(e.from_id).add(e.to_id));for(const t of e.operations){if("parent"!==t.type&&"child"!==t.type)continue;const[e,r]="parent"===t.type?[t.from_id,t.to_id]:[t.to_id,t.from_id];"remove"===t.action?d.get(e)?.delete(r):(d.has(e)||d.set(e,new Set),d.get(e).add(r))}const c=0,l=1,m=2,p=new Map,u=new Set;for(const[e,t]of d){u.add(e);for(const e of t)u.add(e)}const _=e=>{if((p.get(e)??c)!==c)return null;const t=[],r=[],i=e=>{p.set(e,l),r.push(e),t.push({node:e,parents:[...d.get(e)??[]],idx:0})};for(i(e);t.length>0;){const e=t[t.length-1];if(e.idx>=e.parents.length){p.set(e.node,m),r.pop(),t.pop();continue}const n=e.parents[e.idx];e.idx++;const a=p.get(n)??c;if(a===l){const e=r.indexOf(n);return[...r.slice(e),n]}a!==m&&i(n)}return null};for(const e of u){const t=_(e);if(t)return rr(Mt(`Batch would create a cycle: ${t.join(" -> ")}. No changes applied.`))}const f=crypto.randomUUID(),h=e.operations.filter(e=>"remove"===e.action),g=e.operations.filter(e=>"add"===e.action);for(const t of[...h,...g])await i(t,f,e.reason);return tr(Ht({batch_id:f,processed:e.operations.length,operations:!1===e.echo?void 0:e.operations}))}return e.from_id&&e.to_id&&e.type&&e.action?e.from_id===e.to_id?rr(Mt("Cannot create a relationship between an item and itself")):(await i({action:e.action,from_id:e.from_id,to_id:e.to_id,type:e.type,expected_from_updated_at:e.expected_from_updated_at,expected_to_updated_at:e.expected_to_updated_at},void 0,e.reason),!1===e.echo?tr(Ht({action:e.action,from_id:e.from_id,to_id:e.to_id})):tr(Ht({action:e.action,type:e.type,from_id:e.from_id,to_id:e.to_id,message:`Relationship ${"add"===e.action?"added":"removed"}: ${e.from_id} --[${e.type}]--\x3e ${e.to_id}`}))):rr(Mt("Provide (from_id, to_id, type, action) for single-op or operations[] for batch."))}catch(e){return rr(Qt(e))}}q(bn,{linkItemsHandler:()=>vn});var jn=U({"src/tools/link-items.ts"(){Ge(),gr(),ps(),nr()}}),Sn=U({"src/core/bulk-parent-links.ts"(){}}),xn={};function kn(e){return new Set(["22023","23503","23505","23514"]).has(e.code??"")}async function En(e){try{if(!e.items||0===e.items.length)return rr(Mt("items array must contain at least 1 item"));if(e.items.length>100)return rr(Mt(`Maximum 100 items per call (received ${e.items.length}). Split into multiple calls.`));const t=await $e(),r=We();try{await Be(t,e.project_id)}catch(e){if(e instanceof Error&&"VIEWER_ROLE"===e.message)return rr(Tt("Viewer"));throw e}const i=await async function(e,t,r){const{data:i,error:n}=await e.from("metadata").select("domains, function_tags").eq("project_id",t).maybeSingle();if(n)throw new Error(`metadata read failed: ${n.message}`);const a=i?.domains??[],s=ki(i?.function_tags),o=a.length>0?new Set(a.map(Dt).filter(e=>e.length>0)):null,d=mn(a.map(Dt).filter(e=>e.length>0).sort()),c=[],l=new Set,m={};for(let e=0;e<r.length;e++){const t=r[e];m[t.type]=(m[t.type]??0)+1;const i=(t.domains??[]).map(Dt).filter(e=>e.length>0);if(0===i.length)continue;const n=Ei(i,s,Dt);if(n.length>0){l.add(e),c.push({index:e,title:t.title,error:Ii(n)});continue}if(!o)continue;const d=i.filter(e=>!o.has(e));d.length>0&&(l.add(e),c.push({index:e,title:t.title,error:`Unknown domain${d.length>1?"s":""}: ${d.join(", ")}. Registered: ${a.join(", ")}.`}))}return{registeredDomains:a,registeredDomainHash:d,invalidDomainIndexes:l,errors:c,typeBreakdown:m}}(t,e.project_id,e.items);if(on("atoms_bulk_import")){const t={tool:"atoms_bulk_import",project_id:e.project_id,target:mn({items:e.items,registered_domain_hash:i.registeredDomainHash})};if(!e.confirmation_token){const r=dn(t);return tr(Ht({status:"confirmation_required",preview:{total_items:e.items.length,by_type:i.typeBreakdown,sample_titles:e.items.slice(0,5).map(e=>e.title),would_fail:i.errors.length>0,errors:i.errors,registered_domain_hash:i.registeredDomainHash},confirmation_token:r.token,expires_at:r.expires_at,expires_in_seconds:r.expires_in_seconds,message:`Re-call atoms_bulk_import with the same project_id, items array, and confirmation_token within ${r.expires_in_seconds}s to execute. Changing items or project domains will invalidate the token.`}))}const r=cn(e.confirmation_token,t);if(!r.valid)return rr(Mt(`confirmation_token rejected: ${r.reason}`))}const n=(new Date).toISOString(),a=cs(),s=[],o=[...i.errors],d=[],c=new Map,l=async r=>{let i=c.get(r);if(!i){const n=await hr(t,e.project_id,r),a=/^([A-Z]+)-(\d+)$/.exec(n);if(!a)throw new Error(`Unexpected item id format from next_item_id: ${n}`);i={prefix:a[1],next:Number(a[2])},c.set(r,i)}const n=`${i.prefix}-${String(i.next).padStart(3,"0")}`;return i.next+=1,n};for(let t=0;t<e.items.length;t++){const r=e.items[t];if(i.invalidDomainIndexes.has(t))continue;const n=(r.domains??[]).map(Dt).filter(e=>e.length>0),a=void 0!==r.level?Dt(r.level):void 0;try{const e=await l(r.type);d.push({itemId:e,input:r,sanitizedDomains:n,sanitizedLevel:a,index:t})}catch(e){o.push({index:t,title:r.title,error:`ID generation failed: ${e instanceof Error?e.message:String(e)}`})}}if(0===d.length)return tr(Ht({created:0,items:[],errors:o}));const m=[],p=new Map;for(const{itemId:t,input:i,sanitizedDomains:a,sanitizedLevel:s}of d){const o={id:t,type:i.type,title:i.title,summary:i.summary,body:i.body,tags:{domains:a,level:s??""},ownership:{primary:null,additional:[]},relationships:{parents:i.parent_id?[i.parent_id]:[],children:[],related:[]},links:[],metadata:{created_at:n,created_by:r,updated_at:n,updated_by:r}};p.set(t,o),m.push({id:t,project_id:e.project_id,type:i.type,title:i.title,data:o,created_by:r,updated_by:r})}const u=m.map(e=>({id:e.id,type:e.type,title:e.title,data:e.data})),{error:_}=await t.rpc("bulk_save_items_with_revisions",{p_project_id:e.project_id,p_items:u,p_user_id:r,p_event_source:"mcp"});if(_&&kn(_))for(const{itemId:i,input:n,index:c}of d){const d=m.find(e=>e.id===i);if(!d)continue;const{error:l}=await t.rpc("save_item_with_revision",{p_item_id:d.id,p_project_id:e.project_id,p_type:d.type,p_title:d.title,p_data:d.data,p_user_id:r,p_event_source:"mcp",p_event_session_id:a,p_diff_summary:null});l?o.push({index:c,title:n.title,error:l.message}):s.push({id:i,title:n.title,type:n.type})}else{if(_)throw _;for(const{itemId:e,input:t}of d)s.push({id:e,title:t.title,type:t.type})}const f=d.filter(({input:e})=>e.parent_id).filter(({itemId:e})=>s.some(t=>t.id===e)).map(({itemId:t,input:r})=>({from_id:t,to_id:r.parent_id,type:"parent",project_id:e.project_id}));await async function(e){for(const t of e.links){const{error:r}=await e.client.rpc("apply_link_change",{p_project_id:e.projectId,p_from_id:t.from,p_to_id:t.to,p_type:"parent",p_action:"add",p_user_id:e.userId,p_event_source:"mcp"});r&&e.warn(`bulk parent link sync failed for ${t.to}: ${r.message??"unknown error"}`)}}({client:t,projectId:e.project_id,links:f.map(e=>({from:e.from_id,to:e.to_id})),userId:r,warn:e=>process.stderr.write(`[atoms-mcp] Warning: ${e}\n`)});const h=s.map(t=>({item_id:t.id,project_id:e.project_id,changed_by:r,event_type:"created",old_data:null,new_data:p.get(t.id)??null,actor:ls(),session_id:a}));h.length>0&&await t.from("change_history").insert(h).then(({error:e})=>{e&&process.stderr.write(`[atoms-mcp] Warning: bulk change_history log failed: ${e.message}\n`)});const g=new Set(s.map(e=>e.id)),y=new Set(d.filter(({itemId:e,sanitizedLevel:t})=>t&&g.has(e)).map(({sanitizedLevel:e})=>e));for(const r of y)await ii(t,e.project_id,r);return tr(Ht({created:s.length,items:s,errors:o.length>0?o:[],project_id:e.project_id}))}catch(e){return rr(Qt(e))}}q(xn,{bulkImportHandler:()=>En,canRetryBulkImportPerItem:()=>kn});var In=U({"src/tools/bulk-import.ts"(){Ge(),gr(),ps(),un(),nr(),Sn(),gi(),Ri()}}),An={};async function On(e){try{const t=await $e(),r=We(),i=Ve();try{await Be(t,e.project_id)}catch(e){if(e instanceof Error&&"VIEWER_ROLE"===e.message)return rr(Tt("Viewer"));throw e}const n=await cr(t,e.project_id,e.item_id);if(!n)return rr(qt("Item",e.item_id));if("test-case"!==n.type)return rr(Mt(`Item '${e.item_id}' is a ${n.type}, not a test-case`));const a=(e.asset??"").trim(),s=(new Date).toISOString(),{data:o,error:d}=await t.rpc("record_test_results_bulk",{p_project_id:e.project_id,p_item_id:e.item_id,p_run_by:i||r,p_actor_user_id:r,p_results:[{result:e.result,note:e.note,asset:a||void 0,date:s,point:e.point,release_id:e.release_id,build:e.build,environment:e.environment,tooling:e.tooling,steps_rev:e.steps_rev,step_results:e.step_results}]});if(d)throw new Error(d.message);const c=Array.isArray(o)?o[0]:o?.[0];await t.from("change_history").insert({item_id:e.item_id,project_id:e.project_id,changed_by:r,event_type:"test_result_recorded",old_data:null,new_data:{result:e.result,run_at:s,note:e.note,asset:a||null},actor:ls(),session_id:cs()}).then(({error:e})=>{e&&process.stderr.write(`[atoms-mcp] Warning: change_history log failed: ${e.message}\n`)});const l=a?` (asset: ${a})`:"";return tr(Ht({item_id:e.item_id,result:e.result,run_at:s,run_by:i||r,note:e.note??null,asset:a||null,asset_id:c?.asset_id??null,asset_created:c?.asset_created??!1,message:`Test result '${e.result}' recorded for ${e.item_id}${l}`}))}catch(e){return rr(Qt(e))}}q(An,{recordTestResultHandler:()=>On});var Rn=U({"src/tools/record-test-result.ts"(){Ge(),gr(),ps(),nr()}}),Dn={};async function Hn(e){try{const t=await $e(),r=We(),i=Ve();if(!Array.isArray(e.results)||0===e.results.length)return rr(Mt("results must be a non-empty array"));if(e.results.length>100)return rr(Mt(`results must have at most 100 entries (got ${e.results.length})`));try{await Be(t,e.project_id)}catch(e){if(e instanceof Error&&"VIEWER_ROLE"===e.message)return rr(Tt("Viewer"));throw e}const n=await cr(t,e.project_id,e.item_id);if(!n)return rr(qt("Item",e.item_id));if("test-case"!==n.type)return rr(Mt(`Item '${e.item_id}' is a ${n.type}, not a test-case`));const{data:a,error:s}=await t.rpc("record_test_results_bulk",{p_project_id:e.project_id,p_item_id:e.item_id,p_run_by:i||r,p_actor_user_id:r,p_results:e.results});if(s)throw new Error(s.message);return t.from("change_history").insert({item_id:e.item_id,project_id:e.project_id,changed_by:r,event_type:"test_result_recorded",old_data:null,new_data:{bulk:!0,count:e.results.length,results:a},actor:ls(),session_id:cs()}).then(({error:e})=>{e&&process.stderr.write(`[atoms-mcp] Warning: change_history log failed: ${e.message}\n`)}),tr(Ht({item_id:e.item_id,count:e.results.length,results:a,message:`Recorded ${e.results.length} test results for ${e.item_id}`}))}catch(e){return rr(Qt(e))}}q(Dn,{recordTestResultsBulkHandler:()=>Hn});var Cn,Un,qn,Tn,Mn=U({"src/tools/record-test-results-bulk.ts"(){Ge(),gr(),ps(),nr()}});function Pn(e){return Math.min(Math.max(e??5,1),10)}var $n=U({"src/core/trace-graph.ts"(){Cn=200,Un=new Set(["parent","verifies","mitigated_by"]),qn=new Set(["child","verified_by","mitigates"]),Tn={parent:"child",child:"parent",verifies:"verified_by",verified_by:"verifies",mitigated_by:"mitigates",mitigates:"mitigated_by",related:"related"}}}),Nn={};async function Ln(e){try{const t=Pn(e.depth),r=await $e(),i=await cr(r,e.project_id,e.item_id);if(!i)return rr(qt("Item",e.item_id));const{data:n,error:a}=await r.from("items").select("id, title, type, data").eq("project_id",e.project_id).is("deleted_at",null);if(a)throw new Error(a.message);const{data:s,error:o}=await r.from("item_relationships").select("from_id, to_id, type").eq("project_id",e.project_id);if(o)throw new Error(o.message);const d=fr(n??[],s??[]);return tr(Ht(function(e){const t=Pn(e.depth),r=e.relationshipTypes?.length?new Set(e.relationshipTypes):null,i=new Map(e.items.map(e=>[e.id,e]));i.has(e.rootItemId)||i.set(e.rootItemId,e.rootItem);const n=new Map,a=(e,t,r)=>{n.has(e)||n.set(e,[]),n.get(e).push({neighbor:t,relType:r})};for(const t of e.relationships){const i=t.type;if(("upstream"===e.direction||"both"===e.direction)&&(!Un.has(i)||r&&!r.has(i)||a(t.from_id,t.to_id,i),qn.has(i))){const e=Tn[i]??i;r&&!r.has(e)||a(t.to_id,t.from_id,e)}if(("downstream"===e.direction||"both"===e.direction)&&(!qn.has(i)||r&&!r.has(i)||a(t.from_id,t.to_id,i),Un.has(i))){const e=Tn[i]??i;r&&!r.has(e)||a(t.to_id,t.from_id,e)}"related"!==i||r&&!r.has("related")||(a(t.from_id,t.to_id,"related"),a(t.to_id,t.from_id,"related"))}const s=new Set([e.rootItemId]),o=[],d=[],c=[];for(const t of n.get(e.rootItemId)??[])c.push({id:t.neighbor,depth:1,relType:t.relType,from:e.rootItemId});for(;c.length>0&&o.length<Cn;){const{id:e,depth:r,relType:a,from:l}=c.shift();if(s.has(e)||r>t)continue;s.add(e);const m=i.get(e);if(m&&(o.push({id:e,title:m.title,type:m.type,relationship:a,depth:r,from:l}),d.push({from:l,to:e,type:a}),r<t))for(const t of n.get(e)??[])s.has(t.neighbor)||c.push({id:t.neighbor,depth:r+1,relType:t.relType,from:e})}const l={id:e.rootItemId,title:e.rootItem.title,type:e.rootItem.type,relationship:"root",depth:0,from:e.rootItemId};return{root:e.rootItemId,project_id:e.projectId,direction:e.direction,depth_limit:t,items:[l,...o],edges:d,total_count:o.length+1,...o.length>=Cn?{truncated:!0,truncation_message:`Results capped at ${Cn} items. Use a smaller depth or filter by relationship_types.`}:{}}}({projectId:e.project_id,rootItemId:e.item_id,rootItem:{id:e.item_id,title:i.title??e.item_id,type:i.type??"unknown"},items:n??[],relationships:d??[],direction:e.direction,depth:t,relationshipTypes:e.relationship_types})))}catch(e){return e instanceof Error&&e.message.includes("Not authenticated")?rr(Pt()):rr(Yt(e instanceof Error?e.message:String(e)))}}q(Nn,{traceHandler:()=>Ln});var Fn=U({"src/tools/trace.ts"(){Ge(),gr(),$n(),nr()}}),Wn={};async function Vn(e){try{let t=(await $e()).from("assets").select("id, name, archived_at, created_at").eq("project_id",e.project_id).order("name");e.include_archived||(t=t.is("archived_at",null));const{data:r,error:i}=await t;if(i)throw new Error(i.message);return tr(Ht({assets:r||[],total:(r||[]).length}))}catch(e){return e instanceof Error&&e.message.includes("Not authenticated")?rr(Pt()):rr(Yt(e instanceof Error?e.message:String(e)))}}q(Wn,{listAssetsHandler:()=>Vn});var Bn=U({"src/tools/assets.ts"(){Ge(),nr()}}),zn={};async function Jn(e,t,r,i){if(r&&i)throw new Error("baseline_id and as_of are mutually exclusive");if(i)return i;if(!r)return null;const{data:n,error:a}=await e.from("baselines").select("project_id, snapshot_at, deleted_at").eq("id",r).maybeSingle();if(a)throw new Error(a.message);if(!n||n.project_id!==t||n.deleted_at)throw new Error("Baseline not found for this project");return n.snapshot_at}async function Qn(e,t,r){if(r){const{data:i,error:n}=await e.rpc("variables_at",{p_project_id:t,p_snapshot_at:r});if(n)throw new Error(n.message);return i??[]}const{data:i,error:n}=await e.from("project_variables").select("*").eq("project_id",t).order("name");if(n)throw new Error(n.message);return i??[]}async function Gn(e,t,r,i){const{data:n,error:a}=await e.rpc("save_variable_with_revision",{p_project_id:t.project_id,p_name:t.name,p_value:t.value,p_unit:t.unit??null,p_description:t.description??null,p_is_signal:t.is_signal??!1,p_range_min:t.range_min??null,p_range_max:t.range_max??null,p_rate_ms:t.rate_ms??null,p_user_id:r,p_event_source:i,p_variable_id:t.id??null});if(a)throw new Error(a.message);return n}async function Yn(e){try{const t=await $e(),r=await Jn(t,e.project_id,e.baseline_id,e.as_of),i=await Qn(t,e.project_id,r),n=e.limit??50,a=e.offset??0;return tr(Ht({variables:i.slice(a,a+n),total:i.length},Ct(i.length,n,a)))}catch(e){return e instanceof Error&&e.message.includes("Not authenticated")?rr(Pt()):rr(Yt(e instanceof Error?e.message:String(e)))}}async function Kn(e){try{const t=await $e(),r=await Jn(t,e.project_id,e.baseline_id,e.as_of),i=(await Qn(t,e.project_id,r)).find(t=>t.name===e.variable_name)??null;if(!i)return rr(qt("Variable",e.variable_name));const{data:n}=await t.from("items").select("id, title, type, data").eq("project_id",e.project_id).is("deleted_at",null),a=`{${e.variable_name}}`,s=(n||[]).filter(e=>(e.data?.body||"").includes(a));return tr(Ht({variable:i,referenced_by:s.map(e=>({id:e.id,title:e.title,type:e.type})),reference_count:s.length}))}catch(e){return e instanceof Error&&e.message.includes("Not authenticated")?rr(Pt()):rr(Yt(e instanceof Error?e.message:String(e)))}}async function Zn(e){try{const t=await $e(),r=We();try{await Be(t,e.project_id)}catch(e){if(e instanceof Error&&"VIEWER_ROLE"===e.message)return rr(Tt("Viewer"));throw e}const i=await t.from("project_variables").select("*").eq("project_id",e.project_id).eq("name",e.variable_name).maybeSingle();if(!i.data)return rr(qt("Variable",e.variable_name));const n={updated_by:r,updated_at:(new Date).toISOString()};return void 0!==e.value&&(n.value=e.value),void 0!==e.unit&&(n.unit=e.unit||null),void 0!==e.description&&(n.description=e.description||null),tr(Ht({variable:await Gn(t,{...i.data,...n,project_id:e.project_id,name:e.variable_name,value:n.value??i.data.value,unit:n.unit??i.data.unit,description:n.description??i.data.description},r,"mcp")}))}catch(e){return e instanceof Error&&e.message.includes("Not authenticated")?rr(Pt()):rr(Yt(e instanceof Error?e.message:String(e)))}}async function Xn(e){try{const t=await $e(),r=We();try{await Be(t,e.project_id)}catch(e){if(e instanceof Error&&"VIEWER_ROLE"===e.message)return rr(Tt("Viewer"));throw e}const i=/^[a-zA-Z_][a-zA-Z0-9_]*$/;if(e.variables&&Array.isArray(e.variables)){const n=e.variables.filter(e=>!i.test(e.name));if(n.length>0)return rr(Mt(`Invalid variable name(s): ${n.map(e=>e.name).join(", ")}. Use letters, digits, underscores; must start with letter or underscore.`));const a=e.variables.map(e=>e.name),s=a.filter((e,t)=>a.indexOf(e)!==t);if(s.length>0)return rr(Mt(`Duplicate names in batch: ${[...new Set(s)].join(", ")}`));const{data:o}=await t.from("project_variables").select("name").eq("project_id",e.project_id).in("name",a),d=(o??[]).map(e=>e.name);if(d.length>0)return rr(Mt(`Variable(s) already exist: ${d.join(", ")}. Remove them from the batch or use atoms_update_variable to change values.`));const c=e.variables.map(t=>({project_id:e.project_id,name:t.name,value:t.value,unit:t.unit??null,description:t.description??null,created_by:r,updated_by:r})),l=await async function(e,t,r,i){const{data:n,error:a}=await e.rpc("bulk_save_variables_with_revisions",{p_project_id:t,p_variables:r,p_user_id:i,p_event_source:"mcp"});if(a)throw new Error(a.message);return n??[]}(t,e.project_id,c,r);return tr(Ht({batch_id:crypto.randomUUID(),created:(l??[]).length,variables:!1===e.echo?(l??[]).map(e=>({name:e.name})):l??[],message:`${(l??[]).length} variable(s) created. Reference them as {name} in requirement bodies.`}))}if(!e.name||!e.value)return rr(Mt("Provide either (name, value) for single variable or variables[] for batch."));if(!i.test(e.name))return rr(Mt(`Variable name "${e.name}" is invalid. Use letters, digits, and underscores only; must start with a letter or underscore.`));const{data:n}=await t.from("project_variables").select("name").eq("project_id",e.project_id).eq("name",e.name).maybeSingle();if(n)return rr(Mt(`Variable "${e.name}" already exists in this project.`));const a=await Gn(t,{project_id:e.project_id,name:e.name,value:e.value,unit:e.unit??null,description:e.description??null},r,"mcp");return!1===e.echo?tr(Ht({name:e.name})):tr(Ht({variable:a,message:`Variable "${e.name}" created. Reference it in requirement bodies as {${e.name}}.`}))}catch(e){return e instanceof Error&&e.message.includes("Not authenticated")?rr(Pt()):rr(Yt(e instanceof Error?e.message:String(e)))}}async function ea(e){try{const t=await $e(),r=We();try{await Be(t,e.project_id)}catch(e){if(e instanceof Error&&"VIEWER_ROLE"===e.message)return rr(Tt("Viewer"));throw e}const{data:i}=await t.from("project_variables").select("id").eq("project_id",e.project_id).eq("name",e.name).maybeSingle();if(!i)return rr(qt("Variable",e.name));const{data:n}=await t.from("items").select("id").eq("project_id",e.project_id).is("deleted_at",null).filter("data->>body","ilike",`%{${e.name}}%`),a=(n??[]).length;if(a>0&&!e.force)return rr(Mt(`Variable "${e.name}" is referenced in ${a} item${a>1?"s":""}. Use force=true to delete anyway, or remove all {${e.name}} references first.`));if(on("atoms_delete_variable")){const t={tool:"atoms_delete_variable",project_id:e.project_id,target:e.name};if(!e.confirmation_token){const r=dn(t);return tr(Ht({status:"confirmation_required",preview:{variable_name:e.name,referenced_by_items:a,would_orphan_references:a>0},confirmation_token:r.token,expires_at:r.expires_at,expires_in_seconds:r.expires_in_seconds,message:`Re-call atoms_delete_variable with the same project_id, name, and confirmation_token within ${r.expires_in_seconds}s to execute.`}))}const r=cn(e.confirmation_token,t);if(!r.valid)return rr(Mt(`confirmation_token rejected: ${r.reason}`))}const s=await async function(e,t,r,i){const{data:n,error:a}=await e.rpc("delete_variable_with_revision",{p_project_id:t,p_variable_id:r,p_user_id:i,p_event_source:"mcp"});if(a)throw new Error(a.message);return Boolean(n)}(t,e.project_id,i.id,r);return s?tr(Ht({deleted:!0,name:e.name,referenced_by_items:a,warning:a>0?`${a} item${a>1?"s":""} still contain {${e.name}} — those references are now unresolved.`:void 0})):rr(qt("Variable",e.name))}catch(e){return e instanceof Error&&e.message.includes("Not authenticated")?rr(Pt()):rr(Yt(e instanceof Error?e.message:String(e)))}}q(zn,{createVariableHandler:()=>Xn,deleteVariableHandler:()=>ea,getVariableHandler:()=>Kn,listVariablesHandler:()=>Yn,updateVariableHandler:()=>Zn});var ta=U({"src/tools/variables.ts"(){Ge(),un(),nr()}}),ra={};async function ia(e,t){const{data:r}=await e.from("metadata").select("domains, levels, function_tags").eq("project_id",t).maybeSingle();return{domains:r?.domains??[],levels:r?.levels??[],functionTags:Array.isArray(r?.function_tags)?r.function_tags.filter(e=>"string"==typeof e):[]}}async function na(e,t,r,i,n){const{error:a}=await e.from("metadata").upsert({project_id:t,domains:r,levels:i,...void 0!==n?{function_tags:n}:{}},{onConflict:"project_id"});if(a)throw new Error(a.message)}async function aa(e){try{const t=await $e(),{domains:r}=await ia(t,e.project_id),{counts:i,orphans:n}=await async function(e,t,r){const{data:i}=await e.from("items").select("data").eq("project_id",t).is("deleted_at",null),n={};for(const e of r)n[e]=0;const a={};for(const e of i??[]){const t=e.data?.tags?.domains??[];for(const e of t)a[e]=(a[e]??0)+1,e in n&&n[e]++}const s=new Set(r),o={};for(const[e,t]of Object.entries(a))s.has(e)||(o[e]=t);return{counts:n,orphans:o}}(t,e.project_id,r),a=r.map(e=>({name:e,registered:!0,item_count:i[e]??0})),s=Object.entries(n).map(([e,t])=>({name:e,registered:!1,item_count:t})).sort((e,t)=>e.name.localeCompare(t.name)),o=[...a,...s];return tr(Ht({domains:o,total:o.length,registered_count:a.length,unregistered_count:s.length,note:s.length>0?`${s.length} unregistered tag(s) found on items. Use atoms_create_domain to register them, or atoms_update_item to remove them.`:void 0}))}catch(e){return e instanceof Error&&e.message.includes("Not authenticated")?rr(Pt()):rr(Yt(e instanceof Error?e.message:String(e)))}}async function sa(e){try{const t=await $e();try{await Be(t,e.project_id)}catch(e){if(e instanceof Error&&"VIEWER_ROLE"===e.message)return rr(Tt("Viewer"));throw e}const{domains:r,levels:i}=await ia(t,e.project_id),n=Zt(e.name).trim().toLowerCase();return n?r.includes(n)?rr(Mt(`Domain "${n}" already exists in this project.`)):(await na(t,e.project_id,[...r,n],i),tr(Ht({domain:{name:n},message:`Domain "${n}" registered. You can now tag items with domains=["${n}"].`}))):rr(Mt("Domain name cannot be empty."))}catch(e){return e instanceof Error&&e.message.includes("Not authenticated")?rr(Pt()):rr(Yt(e instanceof Error?e.message:String(e)))}}async function oa(e){try{const t=await $e(),r=We();try{await Be(t,e.project_id)}catch(e){if(e instanceof Error&&"VIEWER_ROLE"===e.message)return rr(Tt("Viewer"));throw e}const{domains:i,levels:n,functionTags:a}=await ia(t,e.project_id),s=Zt(e.name).trim().toLowerCase(),o=Zt(e.new_name).trim().toLowerCase();if(!o)return rr(Mt("new_name cannot be empty."));const d=i.includes(s);if(!d){const{data:r}=await t.from("items").select("id").eq("project_id",e.project_id).is("deleted_at",null).filter("data->tags->domains","cs",JSON.stringify([s])).limit(1);if(!r||0===r.length)return rr(qt("Domain",s))}if(o!==s&&i.includes(o))return rr(Mt(`Domain "${o}" already exists. Choose a different name.`));const{data:c,error:l}=await t.from("items").select("id, data").eq("project_id",e.project_id).is("deleted_at",null).filter("data->tags->domains","cs",JSON.stringify([s]));if(l)throw new Error(l.message);const m=(c??[]).length;if(e.dry_run)return tr(Ht({dry_run:!0,rename:{from:s,to:o},items_affected:m,message:`Dry run: renaming "${s}" → "${o}" would update ${m} item${1!==m?"s":""}. Call again with dry_run=false to apply.`}));let p;p=d?i.map(e=>e===s?o:e):i.includes(o)?i:[...i,o];const u=a.map(e=>yi(e)===yi(s)?o:e);await na(t,e.project_id,p,n,u);const{error:_}=await t.rpc("apply_domain_change",{p_project_id:e.project_id,p_old_domain:s,p_new_domain:o,p_user_id:r,p_event_source:"mcp"});if(_)throw new Error(_.message);return tr(Ht({domain:{name:o},renamed_from:s,items_updated:m,message:`Domain renamed "${s}" → "${o}". ${m} item${1!==m?"s":""} updated.`}))}catch(e){return e instanceof Error&&e.message.includes("Not authenticated")?rr(Pt()):rr(Yt(e instanceof Error?e.message:String(e)))}}async function da(e){try{const t=await $e(),r=We();try{await Be(t,e.project_id)}catch(e){if(e instanceof Error&&"VIEWER_ROLE"===e.message)return rr(Tt("Viewer"));throw e}const{domains:i,levels:n,functionTags:a}=await ia(t,e.project_id),s=Zt(e.name).trim().toLowerCase(),o=i.includes(s),{data:d,error:c}=await t.from("items").select("id, data").eq("project_id",e.project_id).is("deleted_at",null).filter("data->tags->domains","cs",JSON.stringify([s]));if(c)throw new Error(c.message);const l=(d??[]).length;if(!o&&0===l)return rr(qt("Domain",s));if(l>0&&!e.force)return rr(Mt(`Domain "${s}" is used by ${l} item${l>1?"s":""}. Use force=true to delete and strip the domain from all items, or re-tag them first.`));if(on("atoms_delete_domain")){const t={tool:"atoms_delete_domain",project_id:e.project_id,target:s};if(!e.confirmation_token){const e=dn(t);return tr(Ht({status:"confirmation_required",preview:{domain_name:s,items_using_count:l,would_strip_from_items:l>0},confirmation_token:e.token,expires_at:e.expires_at,expires_in_seconds:e.expires_in_seconds,message:`Re-call atoms_delete_domain with the same project_id, name, and confirmation_token within ${e.expires_in_seconds}s to execute.`}))}const r=cn(e.confirmation_token,t);if(!r.valid)return rr(Mt(`confirmation_token rejected: ${r.reason}`))}const m=o?i.filter(e=>e!==s):i,p=a.filter(e=>yi(e)!==yi(s));if(await na(t,e.project_id,m,n,p),l>0){const{error:i}=await t.rpc("apply_domain_change",{p_project_id:e.project_id,p_old_domain:s,p_new_domain:null,p_user_id:r,p_event_source:"mcp"});if(i)throw new Error(i.message)}return tr(Ht({deleted:!0,name:s,items_updated:l,message:l>0?`Domain "${s}" deleted and stripped from ${l} item${l>1?"s":""}.`:`Domain "${s}" deleted.`}))}catch(e){return e instanceof Error&&e.message.includes("Not authenticated")?rr(Pt()):rr(Yt(e instanceof Error?e.message:String(e)))}}q(ra,{createDomainHandler:()=>sa,deleteDomainHandler:()=>da,listDomainsHandler:()=>aa,updateDomainHandler:()=>oa});var ca=U({"src/tools/domains.ts"(){Ge(),un(),nr(),Ri()}}),la={};async function ma(e){try{const t=await $e(),{data:r,error:i}=await t.from("projects").select("id, name").eq("id",e.project_id).is("deleted_at",null).maybeSingle(),n=r?.name??null,a=i?.message??null,{data:s,error:o}=await t.from("items").select("id, type, data").eq("project_id",e.project_id).is("deleted_at",null);if(o)throw new Error(o.message);const d=s??[];if(!n&&0===d.length){if(a)throw new Error(a);return rr(qt("Project",e.project_id))}const c={requirements:0,test_cases:0,notes:0},l=[],m=[];for(const e of d)if("requirement"===e.type){c.requirements++;const t=e.data?.tags?.domains??[];m.push({id:e.id,domains:t})}else"test-case"===e.type?(c.test_cases++,l.push(e.id)):"note"===e.type&&c.notes++;const p={passed:0,failed:0,blocked:0,not_run:0};if(l.length>0){const{data:r,error:i}=await t.from("test_results").select("item_id, result, run_at").eq("project_id",e.project_id).order("run_at",{ascending:!1});if(i)throw new Error(i.message);const n=new Map;for(const e of r??[])n.has(e.item_id)||n.set(e.item_id,e.result);for(const e of l){const t=n.get(e);t&&"not-run"!==t?"passed"===t?p.passed++:"failed"===t?p.failed++:"blocked"===t?p.blocked++:p.not_run++:p.not_run++}}const{data:u,error:_}=await t.from("item_relationships").select("from_id, to_id, type").eq("project_id",e.project_id);if(_)throw new Error(_.message);const f=fr(s??[],u??[]),h=new Set;for(const e of f)"verifies"===e.type?h.add(e.to_id):"verified_by"===e.type&&h.add(e.from_id);const g=m.filter(e=>h.has(e.id)).length,y=m.length,w={covered:g,uncovered:y-g,total:y,percent:y>0?Math.round(g/y*1e3)/10:100},b=new Map;for(const e of m){const t=e.domains.length>0?e.domains:["(untagged)"],r=h.has(e.id);for(const e of t){b.has(e)||b.set(e,{covered:0,total:0});const t=b.get(e);t.total++,r&&t.covered++}}const v=Array.from(b.entries()).sort((e,t)=>e[0].localeCompare(t[0])).map(([e,t])=>({domain:e,covered:t.covered,total:t.total,percent:t.total>0?Math.round(t.covered/t.total*1e3)/10:100})),j=new Date;j.setDate(j.getDate()-7);const{count:S,error:x}=await t.from("change_history").select("id",{count:"exact",head:!0}).eq("project_id",e.project_id).gte("changed_at",j.toISOString());if(x)throw new Error(x.message);return tr(Ht({project_id:e.project_id,project_name:n??`Project ${e.project_id.slice(0,8)}`,counts:c,test_status:p,coverage:w,coverage_by_domain:v,recent_changes:S??0,last_updated:(new Date).toISOString()}))}catch(e){return e instanceof Error&&e.message.includes("Not authenticated")?rr(Pt()):rr(Yt(e instanceof Error?e.message:String(e)))}}q(la,{projectSummaryHandler:()=>ma});var pa,ua=U({"src/tools/project-summary.ts"(){Ge(),gr(),nr()}}),_a={};async function fa(e){try{const t=await $e();let r,i;if(e.query&&e.query.trim().length>0){const n=await _r(t,e.project_id,e.query,{type:e.type,limit:e.limit});r=n.items.map(e=>({id:e.id,title:e.title,type:e.type,status:e.data?.status,domains:e.data?.tags?.domains??[],level:e.data?.tags?.level})),i=n.totalCount}else{const n=await dr(t,e.project_id,{type:e.type,domain:e.domain,level:e.level,limit:e.limit,offset:e.offset});r=n.items.map(e=>({id:e.id,title:e.title,type:e.type,status:e.data?.status,domains:e.data?.tags?.domains??[],level:e.data?.tags?.level})),i=n.totalCount}const n=await async function(e,t){const{data:r,error:i}=await e.from("items").select("data").eq("project_id",t).is("deleted_at",null);if(i)throw new Error(i.message);const n=new Set,a=new Set;for(const e of r??[]){const t=e.data?.tags;if(t){const e=t.domains;if(e)for(const t of e)n.add(t);const r=t.level;r&&a.add(r)}}return{domains:[...n].sort(),levels:[...a].sort()}}(t,e.project_id);return tr(Ht({items:r,filters:{domains:n.domains,levels:n.levels,types:pa},project_id:e.project_id},Ct(i,e.limit,e.offset)))}catch(e){return e instanceof Error&&e.message.includes("Not authenticated")?rr(Pt()):rr(Yt(e instanceof Error?e.message:String(e)))}}q(_a,{browseHandler:()=>fa});var ha,ga,ya,wa,ba=U({"src/tools/browse.ts"(){Ge(),gr(),nr(),pa=["requirement","test-case","note","table"]}}),va={};async function ja(e){try{const t=e.direction??"downstream",r=null!=e.depth?Math.min(Math.max(e.depth,1),20):null,i=await $e(),n=await cr(i,e.project_id,e.item_id);if(!n)return rr(qt("Item",e.item_id));const{data:a,error:s}=await i.from("item_relationships").select("from_id, to_id, type").eq("project_id",e.project_id);if(s)throw new Error(s.message);const{data:o,error:d}=await i.from("items").select("id, title, type").eq("project_id",e.project_id).is("deleted_at",null);if(d)throw new Error(d.message);const c=new Map;for(const e of o??[])c.set(e.id,{title:e.title,type:e.type});const l=new Map,m=(e,t,r)=>{l.has(e)||l.set(e,[]),l.get(e).push({neighbor:t,relType:r})};for(const e of a??[]){const r=e.type;"upstream"!==t&&"both"!==t||(ga.includes(r)&&m(e.from_id,e.to_id,r),ya.includes(r)&&m(e.to_id,e.from_id,wa[r]??r)),"downstream"!==t&&"both"!==t||(ya.includes(r)&&m(e.from_id,e.to_id,r),ga.includes(r)&&m(e.to_id,e.from_id,wa[r]??r)),"related"===r&&(m(e.from_id,e.to_id,"related"),m(e.to_id,e.from_id,"related"))}const p=new Set;p.add(e.item_id);const u=[];let _=0;const f=[];for(const t of l.get(e.item_id)??[])f.push({id:t.neighbor,depth:1,path:[{from_id:e.item_id,to_id:t.neighbor,relationship_type:t.relType}]});for(;f.length>0&&u.length<ha;){const{id:e,depth:t,path:i}=f.shift();if(p.has(e))continue;if(null!==r&&t>r)continue;p.add(e);const n=c.get(e);if(n&&(u.push({item_id:e,title:n.title,type:n.type,depth:t,relationship_path:i}),t>_&&(_=t),null===r||t<r))for(const r of l.get(e)??[])p.has(r.neighbor)||f.push({id:r.neighbor,depth:t+1,path:[...i,{from_id:e,to_id:r.neighbor,relationship_type:r.relType}]})}let h=0;if(e.include_variable_refs){const{data:t}=await i.from("variable_references").select("variable_id").eq("item_id",e.item_id).eq("project_id",e.project_id),r=[...new Set((t??[]).map(e=>e.variable_id))];if(r.length>0){const{data:t}=await i.from("project_variables").select("id, name").in("id",r),n=new Map((t??[]).map(e=>[e.id,e.name])),{data:a}=await i.from("variable_references").select("variable_id, item_id").in("variable_id",r).eq("project_id",e.project_id).neq("item_id",e.item_id);for(const t of a??[]){const r=t.item_id;if(p.has(r))continue;if(u.length>=ha)break;p.add(r);const i=c.get(r);if(!i)continue;const a=n.get(t.variable_id)??t.variable_id;u.push({item_id:r,title:i.title,type:i.type,depth:1,relationship_path:[{from_id:e.item_id,to_id:r,relationship_type:`variable_ref:${a}`}]}),h++}}}const g={};for(const e of u)g[e.type]=(g[e.type]??0)+1;const y=c.get(e.item_id);return tr(Ht({root:{item_id:e.item_id,title:y?.title??n.title,type:y?.type??n.type},impacted_items:u,summary:{total_impacted:u.length,by_type:g,max_depth_reached:_,...h>0?{variable_impact_count:h}:{},...u.length>=ha?{truncated:!0}:{}}}))}catch(e){return e instanceof Error&&e.message.includes("Not authenticated")?rr(Pt()):rr(Yt(e instanceof Error?e.message:String(e)))}}q(va,{impactAnalysisHandler:()=>ja});var Sa=U({"src/tools/impact-analysis.ts"(){Ge(),gr(),nr(),ha=200,ga=["parent","verifies","traces_to","allocated_from","decomposed_from"],ya=["child","verified_by","allocated_to","decomposes_to","traced_by"],wa={parent:"child",child:"parent",verifies:"verified_by",verified_by:"verifies",related:"related",traces_to:"traced_by",traced_by:"traces_to",allocated_to:"allocated_from",allocated_from:"allocated_to",decomposes_to:"decomposed_from",decomposed_from:"decomposes_to"}}}),xa=U({"src/core/relationship-sync.ts"(){}}),ka={};function Ea(e){return e instanceof Error&&e.message.includes("Not authenticated")?rr(Pt()):null}async function Ia(e,t){const{data:r,error:i}=await e.rpc("restore_baseline_state",{p_baseline_id:t.baselineId,p_project_id:t.projectId,p_items:[],p_variables:[],p_user_id:t.userId});if(i)throw new Error(i.message);return r}async function Aa(e,t,r){const{data:i,error:n}=await e.from("items").select("id, deleted_at").eq("project_id",t).eq("id",r).maybeSingle();if(n)throw new Error(n.message);if(a=i,!Boolean(a?.id&&null===a.deleted_at))return;var a;const{error:s}=await e.rpc("sync_item_relationships_from_jsonb",{p_project_id:t,p_item_id:r});if(s)throw new Error(s.message)}async function Oa(e,t,r){const{data:i,error:n}=await e.from("projects").select("org_id").eq("id",t).maybeSingle();if(n)return rr(Yt(n.message));if(!i)return rr(qt("Project",t));const{data:a,error:s}=await e.from("organizations").select("feature_flags").eq("id",i.org_id).maybeSingle();return s?rr(Yt(s.message)):!0!==(a?.feature_flags??{})[r]?rr(Ut(`Feature '${r}' is not enabled for this organization`,["Ask an org admin to enable the flag in Org Settings."])):null}async function Ra(e){try{const t=await $e(),r=await async function(e,t){const{data:r,error:i}=await e.from("baselines").select("id, project_id, name, description, field_definitions_snapshot, snapshot_at, created_at, created_by, locked_at, locked_by, release_version, release_status").eq("project_id",t).is("deleted_at",null).order("snapshot_at",{ascending:!1});if(i)throw new Error(`listBaselines: ${i.message}`);return r??[]}(t,e.project_id),i=Math.max(e.offset??0,0),n=Math.min(Math.max(e.limit??50,1),100);return tr(Ht({baselines:r.slice(i,i+n).map(e=>({id:e.id,name:e.name,description:e.description,field_definitions_snapshot:e.field_definitions_snapshot,snapshot_at:e.snapshot_at,created_at:e.created_at,created_by:e.created_by,locked:null!==e.locked_at,locked_at:e.locked_at,release_version:e.release_version,release_status:e.release_status})),total:r.length,offset:i,limit:n}))}catch(e){return Ea(e)??rr(Yt(e instanceof Error?e.message:String(e)))}}async function Da(e){try{const t=await $e(),r=await Oa(t,e.project_id,"baselines_enabled");if(r)return r;const{data:i,error:n}=await t.from("baselines").insert({project_id:e.project_id,name:e.name.trim(),description:e.description??null,snapshot_at:(new Date).toISOString()}).select().single();return n?"23505"===n.code?rr(Ut(`Baseline name "${e.name}" already exists in this project`,["Pick a different name"])):rr(Yt(n.message)):tr(Ht({id:i.id,name:i.name,description:i.description,field_definitions_snapshot:i.field_definitions_snapshot,snapshot_at:i.snapshot_at,created_at:i.created_at,locked:!1}))}catch(e){return Ea(e)??rr(Yt(e instanceof Error?e.message:String(e)))}}async function Ha(e){try{const t=await $e(),r=await mr(t,e.baseline_id);if(!r)return rr(qt("Baseline",e.baseline_id));const i=await Oa(t,r.project_id,"baselines_enabled");if(i)return i;if(null!==r.locked_at)return rr(Ut("Baseline is already locked",["Locks are one-way; nothing to do"]));const{data:n}=await t.from("projects").select("org_id").eq("id",r.project_id).maybeSingle();if(!n)return rr(qt("Project",r.project_id));const{data:a}=await t.auth.getUser(),s=a?.user?.id;if(!s)return rr(Pt());const{data:o}=await t.from("org_members").select("role").eq("user_id",s).eq("org_id",n.org_id).maybeSingle();if(!o||"admin"!==o.role)return rr(Ut("Only admins can lock baselines",["Ask an org admin to lock this baseline"]));const{data:d,error:c}=await t.from("baselines").update({locked_at:(new Date).toISOString(),locked_by:s}).eq("id",e.baseline_id).select().single();return c?rr(Yt(c.message)):tr(Ht({id:d.id,locked_at:d.locked_at,locked_by:d.locked_by}))}catch(e){return Ea(e)??rr(Yt(e instanceof Error?e.message:String(e)))}}async function Ca(e){try{const t=await $e(),[r,i]=await Promise.all([mr(t,e.baseline_a_id),mr(t,e.baseline_b_id)]);if(!r||!i)return rr(qt("Baseline","one of the baseline_ids"));if(r.project_id!==i.project_id)return rr(Ut("Both baselines must belong to the same project",["Verify both baseline_ids are from the same project_id"]));const[n,a]=await Promise.all([ur(t,r.project_id,r.snapshot_at),ur(t,i.project_id,i.snapshot_at)]),s=new Map(n.map(e=>[e.id,e])),o=new Map(a.map(e=>[e.id,e])),d=Array.from(new Set([...s.keys(),...o.keys()])).sort(),c=[];let l=0,m=0,p=0;for(const e of d){const t=s.get(e),r=o.get(e);if(!t&&r)l++,c.push({item_id:e,change_type:"added"});else if(t&&!r)m++,c.push({item_id:e,change_type:"removed"});else if(t&&r){const i=[];t.title!==r.title&&i.push("title"),t.type!==r.type&&i.push("type");JSON.stringify(t.data??{})!==JSON.stringify(r.data??{})&&i.push("data"),i.length>0&&(p++,c.push({item_id:e,change_type:"modified",fields_changed:i}))}}const u=Math.max(e.offset??0,0),_=Math.min(Math.max(e.limit??50,1),200),f=e.detail?c.slice(u,u+_):c.slice(u,u+_).map(({item_id:e,change_type:t,fields_changed:r})=>({item_id:e,change_type:t,fields_changed:r}));return tr(Ht({summary:{added:l,removed:m,modified:p,total_compared:d.length},items:f,page_info:{offset:u,limit:_,total:c.length},baselines:{a:{id:r.id,name:r.name,snapshot_at:r.snapshot_at},b:{id:i.id,name:i.name,snapshot_at:i.snapshot_at}}}))}catch(e){return Ea(e)??rr(Yt(e instanceof Error?e.message:String(e)))}}async function Ua(e){try{const t=await $e(),r=await mr(t,e.baseline_id);if(!r)return rr(qt("Baseline",e.baseline_id));const i=await Oa(t,r.project_id,"baselines_enabled");if(i)return i;const{data:n}=await t.from("projects").select("org_id").eq("id",r.project_id).maybeSingle();if(!n)return rr(qt("Project",r.project_id));const{data:a}=await t.auth.getUser(),s=a?.user?.id;if(!s)return rr(Pt());const{data:o}=await t.from("org_members").select("role").eq("user_id",s).eq("org_id",n.org_id).maybeSingle();if(!o||"admin"!==o.role)return rr(Ut("Only admins can restore from a baseline",["Ask an org admin to run the restore"]));const{data:d}=await t.rpc("cc_project_is_controlled",{p_project_id:r.project_id});if(!0===d)return rr(Ut("Change control is on for this project. Release review before restoring a baseline.",["Release review on the project, then run the restore"]));const c=await ur(t,r.project_id,r.snapshot_at),l=c.map(e=>e.id),{data:m,error:p}=l.length>0?await t.from("items").select("*").eq("project_id",r.project_id).in("id",l):{data:[],error:null};if(p)return rr(Yt(p.message));const u=function(e,t){const r=new Map(t.map(e=>[e.id,e]));return e.flatMap(e=>{const t=r.get(e.id),i=[];var n,a;return t?(null!==t.deleted_at&&i.push("deleted_at"),t.type!==e.type&&i.push("type"),t.title!==e.title&&i.push("title"),n=t.data,a=e.data,JSON.stringify(n??{})!==JSON.stringify(a??{})&&i.push("data")):i.push("missing"),0===i.length?[]:[{id:e.id,type:e.type,title:e.title,data:e.data??{},diff_summary:{changed_fields:i,restored:!0},oldData:t?.data??null}]})}(c,m??[]),{data:_,error:f}=await t.rpc("variables_at",{p_project_id:r.project_id,p_snapshot_at:r.snapshot_at});if(f)return rr(Yt(f.message));const h=_??[];if(!0!==e.confirm)return tr(Ht({preview:!0,items_to_restore:u.length,variables_to_restore:h.length,field_definitions_to_restore:r.field_definitions_snapshot?.length??null}));const g=await Ia(t,{baselineId:r.id,projectId:r.project_id,userId:s});return tr(Ht({ok:!0,items_restored:g.items_restored,variables_restored:g.variables_restored,field_definitions_restored:g.field_definitions.length}))}catch(e){return Ea(e)??rr(Yt(e instanceof Error?e.message:String(e)))}}q(ka,{createBaselineHandler:()=>Da,diffBaselinesHandler:()=>Ca,listBaselinesHandler:()=>Ra,lockBaselineHandler:()=>Ha,restoreBaselineHandler:()=>Ua,restoreBaselineStateAtomically:()=>Ia,syncRestoredItemRelationships:()=>Aa});var qa,Ta=U({"src/tools/baselines.ts"(){Ge(),ps(),nr(),gr(),xa()}}),Ma={};function Pa(e){return"safety-goal"===e?"hara":"failure-mode"===e?"dfmea":null}async function $a(e,t,r){const{data:i,error:n}=await e.rpc("next_item_id",{p_project_id:t,p_prefix:r});if(n)throw new Error(`ID generation failed: ${n.message}`);return i}function Na(e){const t=e?.relationships?.mitigated_by;return Array.isArray(t)?t.filter(e=>"string"==typeof e):[]}function La(e){return JSON.parse(JSON.stringify(e??{}))}async function Fa(e,t,r){const i=[];for(const e of r){const t=(e??"").trim();t&&null===wi(t,i)&&i.push(t)}if(0===i.length)return i;try{const{data:r}=await e.from("metadata").select("domains, function_tags").eq("project_id",t).maybeSingle(),n=e=>Array.isArray(e)?e.filter(e=>"string"==typeof e):[],a=n(r?.domains),s=n(r?.function_tags),o=i.map(e=>wi(e,a)??e),d=[...a],c=[...s];let l=!1;for(const e of o)null===wi(e,d)&&(d.push(e),l=!0),null===wi(e,c)&&(c.push(e),l=!0);if(l){const{error:r}=await e.from("metadata").upsert({project_id:t,domains:d,function_tags:c},{onConflict:"project_id"});r&&process.stderr.write(`[atoms-mcp] Warning: function tag registration failed: ${r.message}\n`)}return o}catch(e){return process.stderr.write(`[atoms-mcp] Warning: function tag registration failed: ${e instanceof Error?e.message:String(e)}\n`),i}}async function Wa(e){try{const t=await $e(),r=We();try{await Be(t,e.project_id)}catch(e){if(e instanceof Error&&"VIEWER_ROLE"===e.message)return rr(Tt("Viewer"));throw e}const i=e.source_req_ids??[],n=e.source_functions??[];if("hara"===e.subtype&&i.length>0)return rr(Mt("A HARA is seeded from functions, never requirements (a requirement is an output of the analysis): pass source_functions and omit source_req_ids"));if("dfmea"===e.subtype&&n.length>0)return rr(Mt("source_functions applies to hara only; seed a DFMEA with source_req_ids"));if(i.length>50)return rr(Mt("source_req_ids supports at most 50 entries per call"));if(n.length>50)return rr(Mt("source_functions supports at most 50 entries per call"));if(i.length>0){const{data:r,error:n}=await t.from("items").select("id, type, data").eq("project_id",e.project_id).is("deleted_at",null).in("id",i);if(n)throw new Error(n.message);const a=new Map((r??[]).map(e=>[e.id,e])),s=i.filter(e=>!a.has(e));if(s.length>0)return rr(qt("Source requirement(s)",s.join(", ")));const o=i.filter(e=>{const t=a.get(e);return"requirement"!==t.type||qa.has(t.data?.req_type)});if(o.length>0)return rr(Mt(`source_req_ids must reference plain requirements (type='requirement', not analysis rows): ${o.join(", ")}`))}const a=e.subtype,s="hara"===a?await Fa(t,e.project_id,n):i,o=await $a(t,e.project_id,_i[a]),d=e.title??("hara"===a?"New HARA":"New DFMEA"),c=e.revision??"1.0",l=pi[a],m=(new Date).toISOString(),p={id:o,type:"analysis",title:d,body:"",tags:{domains:[],level:""},ownership:{primary:null,additional:[]},relationships:{parents:[],children:[],related:[],verifies:[],verified_by:[]},links:[],analysis:{subtype:a,scheme:fi[a],revision:c,..."hara"===a?{standard:hi}:{}},metadata:{created_at:m,created_by:r,updated_at:m,updated_by:r}},{error:u}=await t.rpc("save_item_with_revision",{p_item_id:o,p_project_id:e.project_id,p_type:"analysis",p_title:d,p_data:p,p_user_id:r,p_event_source:"mcp",p_event_session_id:cs()??null,p_diff_summary:null});if(u)throw new Error(u.message);const _=(e,t)=>"hara"===a?{id:e,source_function:t}:{id:e,source_req:t},f=[],h=[];for(const i of s){const n=await $a(t,e.project_id,ui[a]),s={id:n,type:"requirement",title:"",body:"",req_type:l,tags:{domains:[],level:""},ownership:{primary:null,additional:[]},relationships:{parents:[o],children:[],related:[],verifies:[],verified_by:[]},links:[],analysis_props:"hara"===a?{source_req:null,source_function:i,approved:!1,approved_at:null,field_source:{}}:{source_req:i,approved:!1,approved_at:null,field_source:{}},metadata:{created_at:m,created_by:r,updated_at:m,updated_by:r}},{error:d}=await t.rpc("save_item_with_revision",{p_item_id:n,p_project_id:e.project_id,p_type:"requirement",p_title:"",p_data:s,p_user_id:r,p_event_source:"mcp",p_event_session_id:cs()??null,p_diff_summary:null});if(d)throw new Error(d.message);const{error:c}=await t.rpc("apply_link_change",{p_project_id:e.project_id,p_from_id:n,p_to_id:o,p_type:"parent",p_action:"add",p_user_id:r,p_event_source:"mcp"});c&&(process.stderr.write(`[atoms-mcp] Warning: analysis row parent link sync failed for ${n}: ${c.message}\n`),h.push({row_id:n,..."hara"===a?{source_function:i}:{source_req:i},message:c.message})),await t.from("change_history").insert({item_id:n,project_id:e.project_id,changed_by:r,event_type:"created",old_data:null,new_data:s,actor:ls(),session_id:cs()}).then(({error:e})=>{e&&process.stderr.write(`[atoms-mcp] Warning: change_history log failed: ${e.message}\n`)}),f.push(_(n,i))}return await t.from("change_history").insert({item_id:o,project_id:e.project_id,changed_by:r,event_type:"created",old_data:null,new_data:p,actor:ls(),session_id:cs()}).then(({error:e})=>{e&&process.stderr.write(`[atoms-mcp] Warning: change_history log failed: ${e.message}\n`)}),tr(Ht({id:o,subtype:a,title:d,revision:c,..."hara"===a?{standard:hi}:{},rows:f,...h.length>0?{link_warnings:h,warning:`${h.length} row(s) were created but could not be attached to the analysis and will not appear in atoms_get_analysis. Repair: for each row in link_warnings, call atoms_link_items with type 'parent', from_id=<row id>, to_id=${o}.`}:{}}))}catch(e){return rr(Qt(e))}}async function Va(e){try{const t=await $e(),r=We();try{await Be(t,e.project_id)}catch(e){if(e instanceof Error&&"VIEWER_ROLE"===e.message)return rr(Tt("Viewer"));throw e}const i=await cr(t,e.project_id,e.row_id);if(!i)return rr(qt("Analysis row",e.row_id));const n=i.data,a=Pa(n?.req_type);if(!a)return rr(Mt(`Item ${e.row_id} is not an analysis row (req_type must be 'safety-goal' or 'failure-mode')`));const s=new Set([...mi[a],"effect"]);"hara"===a&&s.add("title");const o=Object.keys(e.fields??{});if(0===o.length)return rr(Mt("fields must contain at least one key"));const d=o.filter(e=>!s.has(e));if(d.length>0)return rr(Mt(`Unknown field(s) for ${a}: ${d.join(", ")}. Allowed: ${[...s].sort().join(", ")}`));const c=li[a],l={};for(const t of o){const r=e.fields[t];if(c[t]){const e="number"==typeof r&&"dfmea"===a?String(r):r;if("string"!=typeof e||!c[t].includes(e))return rr(Mt(`Invalid ${t} value ${JSON.stringify(r)} for ${a}. Allowed: ${c[t].join(", ")}`));l[t]=e}else{if("string"!=typeof r)return rr(Mt(`Field '${t}' must be a string`));l[t]=r}}if("hara"===a&&"string"==typeof l.source_function&&l.source_function.trim()){const[r]=await Fa(t,e.project_id,[l.source_function]);r&&(l.source_function=r)}const m=n?.analysis_props??null,p=La(n);p.analysis_props&&"object"==typeof p.analysis_props||(p.analysis_props={source_req:null,approved:!1,approved_at:null,field_source:{}});const u=p.analysis_props;u.field_source&&"object"==typeof u.field_source||(u.field_source={});let _=i.title;for(const[e,t]of Object.entries(l))"effect"===e?p.body=t:"title"===e?(p.title=t,_=t):(u[e]=t,"failure_mode"===e&&"dfmea"===a&&(p.title=t,_=t)),u.field_source[e]="mcp";const f=(new Date).toISOString();p.metadata={...p.metadata??{},updated_at:f,updated_by:r};const{data:h,error:g}=await t.rpc("save_item_with_revision",{p_item_id:i.id,p_project_id:e.project_id,p_type:i.type,p_title:_,p_data:p,p_user_id:r,p_event_source:"mcp",p_event_session_id:cs()??null,p_diff_summary:null,p_expected_updated_at:i.updated_at});if(g)throw new Error(g.message);if(!h)return rr(Ut(`Fill silently failed for '${e.row_id}': save RPC returned no row. Likely RLS denied the write.`,["Verify you have editor or admin role in this project","Retry after checking project access"]));await t.from("change_history").insert({item_id:i.id,project_id:e.project_id,changed_by:r,event_type:"updated",old_data:n,new_data:p,actor:ls(),session_id:cs()}).then(({error:e})=>{e&&process.stderr.write(`[atoms-mcp] Warning: change_history log failed: ${e.message}\n`)});const y=ji(a,u),w=[];if("hara"===a){const n=ji("hara",m),a=Na(p);if(a.length>0&&y!==si&&y!==n)for(const n of a){const a=await cr(t,e.project_id,n);if(!a)continue;const s=a.data;if(s?.asil===y&&s?.asil_source===i.id)continue;const o=s?.asil??null,d=s?.asil_source??null,c=La(s);c.asil=y,c.asil_source=i.id,c.metadata={...c.metadata??{},updated_at:f,updated_by:r};const{error:l}=await t.rpc("save_item_with_revision",{p_item_id:a.id,p_project_id:e.project_id,p_type:a.type,p_title:a.title,p_data:c,p_user_id:r,p_event_source:"mcp",p_event_session_id:cs()??null,p_diff_summary:null,p_expected_updated_at:a.updated_at});if(l)throw new Error(l.message);await t.from("change_history").insert({item_id:a.id,project_id:e.project_id,changed_by:r,event_type:"updated",old_data:{asil:o,asil_source:d},new_data:{asil:y,asil_source:i.id},actor:ls(),session_id:cs(),reason:`ASIL propagated from ${i.id} (rating change via atoms_fill_row)`}).then(({error:e})=>{e&&process.stderr.write(`[atoms-mcp] Warning: change_history log failed: ${e.message}\n`)}),w.push(n)}}return tr(Ht({row_id:i.id,subtype:a,written_fields:Object.keys(l),computed_level:y,...w.length>0?{asil_propagated_to:w}:{}}))}catch(e){return rr(Qt(e))}}async function Ba(e,t,r){const i=await cr(e,t,r);if(!i)return{ok:!1,result:rr(qt("Analysis",r))};if("analysis"!==i.type)return{ok:!1,result:rr(Mt(`Item ${r} is not an analysis (type='${i.type}')`))};const n=i.data,a=Pa("hara"===n?.analysis?.subtype?"safety-goal":"dfmea"===n?.analysis?.subtype?"failure-mode":null);if(!a)return{ok:!1,result:rr(Mt(`Analysis ${r} is missing a valid analysis.subtype`))};const s=pi[a],o=Array.isArray(n?.relationships?.children)?n.relationships.children.filter(e=>"string"==typeof e):[];let d=[];if(o.length>0){const{data:r,error:i}=await e.from("items").select("*").eq("project_id",t).is("deleted_at",null).in("id",o);if(i)throw new Error(i.message);const n=new Map((r??[]).filter(e=>!e.deleted_at&&o.includes(e.id)&&e.data?.req_type===s).map(e=>[e.id,e]));d=o.map(e=>n.get(e)).filter(e=>Boolean(e))}const c=new Set;for(const e of d){const t=e.data?.analysis_props??{};"string"==typeof t.source_req&&t.source_req&&c.add(t.source_req);for(const t of Na(e.data))c.add(t)}const l=new Map;if(c.size>0){const{data:r,error:i}=await e.from("items").select("id, updated_at").eq("project_id",t).is("deleted_at",null).in("id",[...c]);if(i)throw new Error(i.message);for(const e of r??[])l.set(e.id,e.updated_at)}const m=d.map(e=>{const t=e.data?.analysis_props??{},r=Na(e.data).filter(e=>l.has(e)),i=r.length>0,n=ji(a,t),s=function(e,t,r){if(r)return null;if("hara"===e){const e=bi(t);return"QM"!==e&&e!==si?`asil ${e} unmitigated`:null}const i=parseInt(vi(t),10),n=parseInt(t?.severity||"",10);return!Number.isNaN(i)&&i>=100?"rpn >= 100":!Number.isNaN(n)&&n>=9?"severity >= 9":null}(a,t,i),o={id:e.id,title:"string"==typeof e.title?e.title:"",source_req:"string"==typeof t.source_req?t.source_req:null,effect:"string"==typeof e.data?.body?e.data.body:"",computed_level:n,is_gap:null!==s,gap_rule:s,is_critical:Si(a,t,i),is_stale:xi(t,r,e=>l.get(e)??null),mitigated_by:r,link_roles:t.link_roles??{},approved:!0===t.approved,approved_at:t.approved_at??null,mcp_fields:Object.entries(t.field_source??{}).filter(([,e])=>"mcp"===e).map(([e])=>e).sort()};for(const e of mi[a])o[e]=t[e]??null;return o});return{ok:!0,analysis:{id:i.id,title:"string"==typeof i.title?i.title:"",subtype:a,scheme:"string"==typeof n?.analysis?.scheme?n.analysis.scheme:fi[a],revision:"string"==typeof n?.analysis?.revision?n.analysis.revision:"1.0",standard:"string"==typeof n?.analysis?.standard?n.analysis.standard:"hara"===a?hi:null},rows:m,counts:{rows:m.length,gaps:m.filter(e=>e.is_gap).length,critical:m.filter(e=>e.is_critical).length,stale:m.filter(e=>e.is_stale).length,approved:m.filter(e=>e.approved).length,linked:m.filter(e=>e.mitigated_by.length>0).length}}}async function za(e){try{const t=await $e(),r=await Ba(t,e.project_id,e.analysis_id);return r.ok?tr(Ht({analysis:r.analysis,rows:r.rows,counts:r.counts})):r.result}catch(e){return rr(Qt(e))}}async function Ja(e){try{const n=await $e(),a=We();try{await Be(n,e.project_id)}catch(e){if(e instanceof Error&&"VIEWER_ROLE"===e.message)return rr(Tt("Viewer"));throw e}if(e.row_id===e.req_id)return rr(Mt("row_id and req_id must be different items"));const s=await cr(n,e.project_id,e.row_id);if(!s)return rr(qt("Analysis row",e.row_id));const o=s.data,d=Pa(o?.req_type);if(!d)return rr(Mt(`Item ${e.row_id} is not an analysis row (req_type must be 'safety-goal' or 'failure-mode')`));const c=await cr(n,e.project_id,e.req_id);if(!c)return rr(qt("Requirement",e.req_id));const l=c.data;if("requirement"!==c.type||qa.has(l?.req_type))return rr(Mt(`req_id must reference a plain requirement (type='requirement', not an analysis row): ${e.req_id}`));if("dfmea"===d&&!e.role)return rr(Mt(`role is required for dfmea mitigation links. Choose one of: ${oi.join(", ")}`));if("hara"===d&&e.role)return rr(Mt("role is not applicable to hara mitigation links; omit it (roles are a dfmea concept)"));let m=si;if("hara"===d){m=ji("hara",o?.analysis_props);const n=l?.asil;if(m!==si&&"string"==typeof n&&n.length>0&&n!==m&&!0!==e.override)return rr((r=m,{...Ut(`ASIL conflict: the target requirement already carries asil '${t=n}' but ${i=s.id} computes '${r}'.`,["Pass override=true to apply the HARA value; the override is history-logged like a dialog accept","Or align the row's S/E/C ratings with the existing ASIL before linking"]),details:{code:"asil_conflict",current_value:t,incoming_value:r,source_row:i,hint:"pass override=true to apply the HARA value; the override is history-logged like a dialog accept"}}))}const p="dfmea"===d?e.role:null,{error:u}=await n.rpc("apply_link_change",{p_project_id:e.project_id,p_from_id:e.row_id,p_to_id:e.req_id,p_type:"mitigated_by",p_action:"add",p_user_id:a,p_event_source:"mcp",p_expected_from_updated_at:s.updated_at,p_expected_to_updated_at:c.updated_at,p_role:p});if(u)throw new Error(u.message);let _;if(await n.from("change_history").insert({item_id:e.row_id,project_id:e.project_id,changed_by:a,event_type:"updated",old_data:null,new_data:{relationship_change:{action:"add",type:"mitigated_by",from:e.row_id,to:e.req_id,...p?{role:p}:{}}},actor:ls(),session_id:cs()}).then(({error:e})=>{e&&process.stderr.write(`[atoms-mcp] Warning: change_history log failed: ${e.message}\n`)}),"hara"===d&&m!==si){const t=await cr(n,e.project_id,e.req_id);if(t){const r=t.data,i=r?.asil??null,o=r?.asil_source??null,d=La(r);d.asil=m,d.asil_source=s.id;const c=(new Date).toISOString();d.metadata={...d.metadata??{},updated_at:c,updated_by:a};const{error:l}=await n.rpc("save_item_with_revision",{p_item_id:t.id,p_project_id:e.project_id,p_type:t.type,p_title:t.title,p_data:d,p_user_id:a,p_event_source:"mcp",p_event_session_id:cs()??null,p_diff_summary:null,p_expected_updated_at:t.updated_at});if(l)throw new Error(l.message);await n.from("change_history").insert({item_id:t.id,project_id:e.project_id,changed_by:a,event_type:"updated",old_data:{asil:i,asil_source:o},new_data:{asil:m,asil_source:s.id},actor:ls(),session_id:cs(),reason:!0===e.override?`ASIL override accepted via atoms_link_mitigation (was '${i??"unset"}', now '${m}' from ${s.id})`:`ASIL set via atoms_link_mitigation from ${s.id}`}).then(({error:e})=>{e&&process.stderr.write(`[atoms-mcp] Warning: change_history log failed: ${e.message}\n`)}),_=m}}return tr(Ht({row_id:e.row_id,req_id:e.req_id,type:"mitigated_by",role:p,..._?{asil_written:_}:{}}))}catch(e){return rr(Qt(e))}var t,r,i}async function Qa(e){try{const t=await $e(),r=await Ba(t,e.project_id,e.analysis_id);if(!r.ok)return r.result;const i=r.rows.filter(e=>e.is_gap).map(e=>({id:e.id,title:e.title,source_req:e.source_req,source_function:"string"==typeof e.source_function?e.source_function:null,computed_level:e.computed_level,rule:e.gap_rule}));return tr(Ht({analysis_id:r.analysis.id,subtype:r.analysis.subtype,gaps:i,count:i.length}))}catch(e){return rr(Qt(e))}}q(Ma,{createAnalysisHandler:()=>Wa,fillRowHandler:()=>Va,getAnalysisHandler:()=>za,getGapsHandler:()=>Qa,linkMitigationHandler:()=>Ja});var Ga,Ya,Ka,Za,Xa,es,ts,rs,is,ns,as=U({"src/tools/analysis.ts"(){Ge(),gr(),ps(),nr(),Ri(),qa=new Set(["safety-goal","failure-mode"])}}),ss={};function os(t){K(t.toolName)&&function(t,r){const i=`ui://${r.toolName}/${r.htmlFile}`,n=function(e,t){const r=h(import.meta.url),i=d.dirname(r);return d.resolve(i,"apps","src","apps",e,t)}(r.appName,r.htmlFile),a=[...ir.resourceDomains,...r.extraResourceDomains??[]];w(t,r.toolName,{title:r.title,description:r.description,inputSchema:r.inputSchema,annotations:r.annotations,_meta:{ui:{resourceUri:i}}},r.handler),b(t,`${r.title} UI`,i,{mimeType:v},async()=>{const t=await e.readFile(n,"utf-8");return{contents:[{uri:i,mimeType:v,text:t,_meta:{ui:{csp:{resourceDomains:a}}}}]}})}(Za,t)}function ds(e,t){return async r=>{const i=P(J(),e);if(!i.allowed)return rr(Ut(`Tool ${e} is blocked by org policy.`,[N(i.reason),"Contact your org admin to adjust the MCP access policy."]));try{!function(e){const t=_t();if(!t)return;if("object"!=typeof e||null===e)return;const r=e.project_id;if("string"==typeof r&&r!==t)throw new mt(t,r)}(r)}catch(e){if(e instanceof mt)return rr(Nt(e.expectedProjectId));throw e}{const e=J(),t=r?.project_id;if(null!==e.project_scope&&"string"==typeof t&&!e.project_scope.includes(t))return rr(Ut("Tool call rejected: project_id is outside your org's MCP project scope.",[`Allowed projects: ${e.project_scope.length} project${1===e.project_scope.length?"":"s"} configured by your admin`,"Use atoms_list_projects to see which projects you can access in this session"]))}let n,a;try{a=await $e(),n=We()}catch{return t(r)}const s=await async function(e,t){const r=parseInt(process.env.ATOMS_RATE_LIMIT_RPM??"",10)||ot,i=new Date,n=new Date(i.getTime()-dt);try{const{count:a,error:s}=await t.from("mcp_rate_limits").select("id",{count:"exact",head:!0}).eq("user_id",e).gte("requested_at",n.toISOString());if(s)throw new Error(s.message);if((a??0)>=r){const{data:r,error:a}=await t.from("mcp_rate_limits").select("requested_at").eq("user_id",e).gte("requested_at",n.toISOString()).order("requested_at",{ascending:!0}).limit(1).maybeSingle();if(a)throw new Error(a.message);const s=(r?new Date(r.requested_at).getTime():i.getTime())+dt-i.getTime();return{allowed:!1,retryAfterSeconds:Math.max(1,Math.ceil(s/1e3))}}t.from("mcp_rate_limits").insert({user_id:e,requested_at:i.toISOString()}).then(({error:e})=>{e&&process.stderr.write(`[rate-limiter] insert failed: ${e.message}\n`)});const o=new Date(i.getTime()-2*dt);return t.from("mcp_rate_limits").delete().eq("user_id",e).lt("requested_at",o.toISOString()).then(()=>{}),{allowed:!0}}catch(e){return process.stderr.write(`[rate-limiter] DB check failed (${e instanceof Error?e.message:String(e)}), falling back to allow.\n`),{allowed:!0}}}(n,a);if(!s.allowed)return rr(Lt(s.retryAfterSeconds));if("1"===process.env.ATOMS_MCP_LOCKDOWN||"true"===process.env.ATOMS_MCP_LOCKDOWN){const e=r?.project_id;if("string"==typeof e)try{const{requireWriteAccess:t}=await Promise.resolve().then(()=>(Ge(),Pe));await t(a,e)}catch(e){if(e instanceof Error&&"VIEWER_ROLE"===e.message){const{accessDeniedError:e}=await Promise.resolve().then(()=>(nr(),Ot));return rr(e("Viewer (lockdown mode: read access requires editor or admin)"))}}}const o=function(){const e=performance.now();return()=>Math.round(performance.now()-e)}();let d,c="success";try{const e=await t(r),i=e;if(!0===i?.isError){c="error";const e=i?.content;if(e?.[0]?.text)try{d=JSON.parse(e[0].text).message}catch{}}return e}catch(e){throw c="error",d=e instanceof Error?e.message:String(e),e}finally{const t={tool_name:e,params:r,status:c,duration_ms:o(),error_msg:d,project_id:r.project_id,session_id:es,client_name:ts};try{!function(e,t,r){const i=st(r.params);e.from("mcp_audit_log").insert({user_id:t,tool_name:r.tool_name,params:i,status:r.status,duration_ms:r.duration_ms,error_msg:r.error_msg??null,project_id:r.project_id??null,session_id:r.session_id??null,client_name:r.client_name??null}).then(({error:e})=>{e&&process.stderr.write(`[audit] Failed to log: ${e.message}\n`)})}(await $e(),n,t)}catch{}}}}function cs(){return es}function ls(){return rs}function ms(){return ts}q(ss,{ATOMS_MCP_SERVER_VERSION:()=>Ka,getMcpActor:()=>ls,getMcpClientName:()=>ms,getMcpSessionId:()=>cs,server:()=>Za});var ps=U({"src/server.ts"(){pt(),ut(),ee(),ht(),Q(),B(),Et(),It(),Ge(),nr(),ar(),Ga=S(import.meta.url),Ya=Ga("../package.json"),Ka=Ya.version,Za=new j({name:"atoms-mcp-server",version:Ka}),Xa=(e,t,r)=>{if(K(e))return Za.registerTool(e,t,r)},es=u(),ts=gt(process.env.ATOMS_CLIENT_NAME),rs=function(){const e=gt(ts);return"cursor"===e?"mcp_cursor":"copilot"===e?"mcp_copilot":"mcp_claude"}(),Xa("atoms_status",{title:"ATOMS MCP Health Check",description:'Check the health and authentication status of the ATOMS MCP server.\n\nCall this FIRST if other atoms tools fail or return auth errors.\nThis tool works WITHOUT authentication — it checks whether credentials\nexist and are valid, and tells the user exactly what to do if not.\n\nArgs: None\n\nReturns:\n { status: "authenticated"|"not_authenticated"|"expired", email, message, next_steps }\n\nExamples:\n - "Is ATOMS MCP working?" → atoms_status()\n - "Why are atoms tools failing?" → atoms_status()',inputSchema:{},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1}},async()=>{try{const{readCredentials:e,isTokenValid:t}=await Promise.resolve().then(()=>(fe(),te)),r=await e();if(!r)return{content:[{type:"text",text:JSON.stringify({status:"not_authenticated",message:"No ATOMS credentials found. You need to login first.",next_steps:["1. Open a terminal on your machine","2. Run: npx @atoms-tech/atoms-mcp login","3. Complete the login in your browser","4. Restart Claude Desktop (quit and reopen)","5. Try your query again"]},null,2)}]};if(!t(r))try{const{getValidToken:e}=await Promise.resolve().then(()=>(Me(),Ae)),{email:t}=await e(),{invalidateClient:r}=await Promise.resolve().then(()=>(Ge(),Pe));return r(),{content:[{type:"text",text:JSON.stringify({status:"authenticated",email:t,message:"Token was expired but has been refreshed. You're good to go!"},null,2)}]}}catch{return{content:[{type:"text",text:JSON.stringify({status:"expired",email:r.user_email??"unknown",message:"Your session has expired and could not be refreshed.",next_steps:["1. Open a terminal on your machine","2. Run: npx @atoms-tech/atoms-mcp logout","3. Run: npx @atoms-tech/atoms-mcp login","4. Complete the login in your browser","5. Restart Claude Desktop (quit and reopen)"]},null,2)}]}}return{content:[{type:"text",text:JSON.stringify({status:"authenticated",email:r.user_email??"unknown",message:"ATOMS MCP is connected and authenticated. All tools are ready."},null,2)}]}}catch(e){return{content:[{type:"text",text:JSON.stringify({status:"error",message:`Health check failed: ${e instanceof Error?e.message:String(e)}`,next_steps:["1. Open a terminal","2. Run: npx @atoms-tech/atoms-mcp login","3. Restart Claude Desktop"]},null,2)}]}}}),Xa("atoms_list_projects",{title:"List ATOMS Projects",description:'List all projects the authenticated user has access to.\n\nThis is the ENTRY POINT tool. Call this first to discover project IDs,\nthen use those IDs with all other tools.\n\nArgs: None\n\nReturns:\n { status: "success", data: [{ id, name, description, org_id, created_at }] }\n\nExamples:\n - "What projects do I have?" → atoms_list_projects()\n - "Show my projects" → atoms_list_projects()\n\nWorkflow:\n atoms_list_projects → atoms_list_items(project_id) → atoms_get_item(project_id, item_id)',inputSchema:{},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1}},ds("atoms_list_projects",async()=>{const{listProjectsHandler:e}=await Promise.resolve().then(()=>(br(),yr));return e()})),Xa("atoms_list_items",{title:"List ATOMS Items",description:'List items in an ATOMS project with optional type/domain/level filters.\n\nThis is the primary ENTRY POINT for discovering items. Use it to browse\nproject contents before drilling into specific items with atoms_get_item.\n\nArgs:\n - project_id (string, UUID): The project to list items from\n - type (string, optional): Filter: requirement|test-case|note|table\n - domain (string, optional): Filter by domain tag\n - level (string, optional): Filter by level (System, Subsystem, Component)\n - limit (number, optional): Max results, 1-200 (default 50)\n - offset (number, optional): Pagination offset (default 0)\n\nReturns:\n { status: "success", data: [{ id, title, type, status, domains, level }], meta: { total_count, limit, offset, has_more } }\n\nExamples:\n - "Show me all requirements" → atoms_list_items(project_id, type="requirement")\n - "What test cases exist?" → atoms_list_items(project_id, type="test-case")\n\nErrors:\n - "Project not found" → verify project_id\n - "Access denied" → check org membership',inputSchema:{project_id:y.string().uuid("Must be a valid project UUID").describe("UUID of the project"),type:y.enum(["requirement","test-case","note","table"]).optional().describe("Filter by item type"),domain:y.string().max(64).optional().describe("Filter by domain tag (e.g., 'Safety', 'Performance')"),level:y.string().max(32).optional().describe("Filter by level (System, Subsystem, Component)"),limit:y.number().int().min(1).max(200).default(50).describe("Max results (default 50, max 200)"),offset:y.number().int().min(0).default(0).describe("Pagination offset (default 0)"),baseline_id:y.string().uuid().optional().describe("Optional baseline UUID. Returns items as they were at that baseline's moment. Mutually exclusive with as_of."),as_of:y.string().datetime().optional().describe("Optional ISO 8601 timestamp. Returns items as they were at that moment. Mutually exclusive with baseline_id.")},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1}},ds("atoms_list_items",async e=>{const{listItemsHandler:t}=await Promise.resolve().then(()=>(xr(),vr));return t(e)})),Xa("atoms_get_item",{title:"Get ATOMS Item Details",description:'Get full details of a single item including relationships, test history, and ownership.\n\nUse after atoms_list_items or atoms_search to drill into a specific item.\n\nArgs:\n - project_id (string, UUID): The project containing the item\n - item_id (string): Item ID (e.g., "REQ-001", "TC-050")\n\nReturns:\n Full WorkItem with relationships, test runs, ownership, metadata.\n\nExamples:\n - "Show me requirement REQ-001" → atoms_get_item(project_id, "REQ-001")\n - "Get test case details" → atoms_get_item(project_id, "TC-050")',inputSchema:{project_id:y.string().uuid().describe("UUID of the project"),item_id:y.string().min(1).max(20).describe("Item ID (e.g., REQ-001)"),baseline_id:y.string().uuid().optional().describe("Optional baseline UUID. Returns the item as it was at that baseline's moment. Mutually exclusive with as_of."),as_of:y.string().datetime().optional().describe("Optional ISO 8601 timestamp. Returns the item as it was at that moment. Mutually exclusive with baseline_id.")},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1}},ds("atoms_get_item",async e=>{const{getItemHandler:t}=await Promise.resolve().then(()=>(Ar(),kr));return t(e)})),Xa("atoms_search",{title:"Search ATOMS Items",description:'Full-text search across items in a project. Searches title, body, and summary.\n\nAlternative entry point to atoms_list_items when you know what you\'re looking for.\n\nArgs:\n - project_id (string, UUID): The project to search in\n - query (string): Search text (max 1000 chars)\n - type (string, optional): Filter by type\n - asset (string, optional): Filter to items with at least one run on the given asset\n (VIN, module, lot, ...). Case-sensitive exact match.\n - limit (number, optional): Max results, 1-100 (default 25)\n\nReturns:\n Matching items with @basic fields, ranked by relevance.\n\nExamples:\n - "Find braking requirements" → atoms_search(project_id, "braking system")\n - "Search for safety tests" → atoms_search(project_id, "safety", type="test-case")\n - "Tests that ran on this VIN" → atoms_search(project_id, "", asset="WEV...0005")',inputSchema:{project_id:y.string().uuid().describe("UUID of the project"),query:y.string().min(1).max(1e3).describe("Search text"),type:y.enum(["requirement","test-case","note","table"]).optional().describe("Filter by item type"),asset:y.string().max(200).optional().describe("Filter to items with at least one run on this asset (case-sensitive exact)"),limit:y.number().int().min(1).max(100).default(25).describe("Max results (default 25, max 100)"),baseline_id:y.string().uuid().optional().describe("Optional baseline UUID. Searches within the project state at that baseline's moment.")},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1}},ds("atoms_search",async e=>{const{searchHandler:t}=await Promise.resolve().then(()=>(Dr(),Or));return t(e)})),Xa("atoms_semantic_search",{title:"Semantic Search ATOMS Items",description:'Meaning-based search across items using pgvector embeddings (GTE-small, 384d).\n\nUse when atoms_search (exact/substring) misses conceptually related items.\nBest for natural-language concept queries; atoms_search is better for exact phrases or IDs.\n\nArgs:\n - project_id (string, UUID): The project to search in\n - query (string): Natural-language concept query (max 1000 chars)\n - type (string, optional): Filter by type\n - limit (number, optional): Max results, 1-50 (default 10)\n\nReturns:\n Semantically similar items ranked by cosine similarity.\n\nExamples:\n - "Find braking-related requirements" → atoms_semantic_search(project_id, "braking deceleration stopping distance")\n - "Safety-related test cases" → atoms_semantic_search(project_id, "hazard risk mitigation safety", type="test-case")',inputSchema:{project_id:y.string().uuid().describe("UUID of the project"),query:y.string().min(1).max(1e3).describe("Natural-language concept query"),type:y.enum(["requirement","test-case","note","table"]).optional().describe("Filter by item type"),limit:y.number().int().min(1).max(50).default(10).describe("Max results (default 10, max 50)")},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1}},ds("atoms_semantic_search",async e=>{const{semanticSearchHandler:t}=await Promise.resolve().then(()=>(Ur(),Hr));return t(e)})),os({appName:"coverage",htmlFile:"coverage-app.html",toolName:"atoms_get_coverage",title:"Get Test Coverage Report",description:'Find requirements without linked test cases (coverage gaps).\n\nEssential for compliance reporting in safety-critical industries.\n\nIn MCP App hosts (Claude, ChatGPT), renders a visual coverage heatmap.\nFalls back to JSON for non-UI clients.\n\nArgs:\n - project_id (string, UUID): The project to analyze\n - domain (string, optional): Filter by domain\n - level (string, optional): Filter by level\n - baseline_id (string, UUID, optional): coverage at that baseline\'s moment\n - as_of (string ISO 8601, optional): coverage at that moment\n baseline_id and as_of are mutually exclusive.\n\nReturns:\n { covered, uncovered, total, coverage_percent, uncovered_items: [...] }\n\nExamples:\n - "What\'s our test coverage?" → atoms_get_coverage(project_id)\n - "Safety coverage gaps?" → atoms_get_coverage(project_id, domain="Safety")\n - "Coverage at PDR submission" → atoms_get_coverage(project_id, baseline_id="...")',inputSchema:{project_id:y.string().uuid().describe("UUID of the project"),domain:y.string().max(64).optional().describe("Filter by domain tag"),level:y.string().max(32).optional().describe("Filter by level"),baseline_id:y.string().uuid().optional().describe("Compute coverage as of this baseline's snapshot moment"),as_of:y.string().datetime().optional().describe("Compute coverage as of this ISO 8601 moment")},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1},handler:ds("atoms_get_coverage",async e=>{const{getCoverageHandler:t}=await Promise.resolve().then(()=>(Pr(),qr));return t(e)})}),Xa("atoms_verification_status",{title:"Get Release Verification Status",description:'Release-bound verification state per requirement, matching the in-app projection.\n\nEach requirement\'s state is derived from the runs bound to a formal Release:\nverified, failing, incomplete, or not-run, plus a stale flag and the\n(points_run / points_total) counts for its parameterized test points.\n\nArgs:\n - project_id (string, UUID): The project to report on\n - release_id (string, UUID, optional): A formal Release (a baselines row with\n a release_version). Defaults to the project\'s CURRENT Release\n (release_status = \'released\'). Errors if the project has none.\n - include_gaps (boolean, optional, default false): Also return the not-run\n (test x unit x value) points for the Release.\n\nReturns:\n { release: {id, version, status}, readiness: {...roll-up counts...},\n requirements: [{requirement_id, state, stale, points_total, points_run}],\n gaps?: [{tc_id, asset, point_key}] }\n An empty requirements array with a "hint" field means the projection has not\n been rebuilt for this project yet, NOT that everything is unverified.\n\nExamples:\n - "Are we ready to ship 2.0?" → atoms_verification_status(project_id)\n - "What\'s still not run for release R?" → atoms_verification_status(project_id, release_id="...", include_gaps=true)',inputSchema:{project_id:y.string().uuid().describe("UUID of the project"),release_id:y.string().uuid().optional().describe("Formal Release (baselines row) UUID; defaults to the project's current Release"),include_gaps:y.boolean().default(!1).describe("Include the not-run (test x unit x value) points for the Release")},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1}},ds("atoms_verification_status",async e=>{const{verificationStatusHandler:t}=await Promise.resolve().then(()=>(Lr(),$r));return t(e)})),Xa("atoms_get_history",{title:"Get Item Change History",description:"Audit trail for an item — who changed what, when, and whether it was human or AI.\n\nShows actor attribution (user vs mcp_<client>) and session grouping.\n\nArgs:\n - project_id (string, UUID): The project containing the item\n - item_id (string): Item ID\n - limit (number, optional): Max entries (default 20, max 100)\n\nReturns:\n Change entries with actor, session_id, event_type, changed_at, fields_changed.",inputSchema:{project_id:y.string().uuid().describe("UUID of the project"),item_id:y.string().min(1).max(20).describe("Item ID"),limit:y.number().int().min(1).max(100).default(20).describe("Max entries (default 20)")},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1}},ds("atoms_get_history",async e=>{const{getHistoryHandler:t}=await Promise.resolve().then(()=>(Vr(),Fr));return t(e)})),os({appName:"mermaid",htmlFile:"mermaid-app.html",toolName:"atoms_export_mermaid",title:"Export Mermaid Diagram",description:"Generate a Mermaid diagram of the requirement/test hierarchy.\n\nOutput is paste-ready for markdown docs. Shows parent-child relationships\nand requirement-to-test-case verification links.\n\nIn MCP App hosts (Claude, ChatGPT), renders an interactive pan/zoom diagram.\nFalls back to Mermaid text for non-UI clients.\n\nArgs:\n - project_id (string, UUID): The project to visualize\n - root_item_id (string, optional): Start node (default: all roots)\n - depth (number, optional): Max depth (default 3, max 10)\n - include_tests (boolean, optional): Show test case links (default true)\n - baseline_id (string, UUID, optional): render the graph at that baseline's moment\n - as_of (string ISO 8601, optional): render the graph at that moment\n baseline_id and as_of are mutually exclusive.\n\nReturns:\n Mermaid graph string.",inputSchema:{project_id:y.string().uuid().describe("UUID of the project"),root_item_id:y.string().max(20).optional().describe("Root item ID to start from"),depth:y.number().int().min(1).max(10).default(3).describe("Max depth"),include_tests:y.boolean().default(!0).describe("Include test case links"),baseline_id:y.string().uuid().optional().describe("Render the graph as of this baseline's snapshot moment"),as_of:y.string().datetime().optional().describe("Render the graph as of this ISO 8601 moment")},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1},handler:ds("atoms_export_mermaid",async e=>{const{exportMermaidHandler:t}=await Promise.resolve().then(()=>(Zr(),Yr));return t(e)})}),Xa("atoms_create_item",{title:"Create ATOMS Item",description:'Create a new requirement, test case, or note in a project.\n\nRequires editor or admin role. Changes are logged with AI actor attribution.\n\nArgs:\n - project_id (string, UUID): Target project\n - type (string): requirement|test-case|note\n - title (string): Item title (max 500 chars)\n - body (string, optional): Rich text body\n - summary (string, optional): Brief summary\n - domains (string[], optional): Domain tags (a tag flagged as a function is not a domain and is rejected)\n - level (string, optional): System|Subsystem|Component\n - parent_ids (string[], optional): Parent item IDs to link\n - fields (object, optional): Project-field values, e.g. {"Verification Method": "Design Review"}. Keys must be defined via atoms_create_field first and values must match each field type.\n\nReturns:\n Created item with generated ID (e.g., "REQ-058")\n\nSide effects:\n - Logs to change_history with actor=\'mcp_<client>\'\n - Creates relationships if parent_ids provided',inputSchema:{project_id:y.string().uuid().describe("UUID of the target project"),type:y.enum(["requirement","test-case","note"]).describe("Item type"),title:y.string().min(1).max(500).describe("Item title"),body:y.string().max(5e4).optional().describe("Rich text body"),summary:y.string().max(2e3).optional().describe("Brief summary"),domains:y.array(y.string().max(64)).max(20).optional().describe("Domain tags"),level:y.string().max(32).optional().describe("System|Subsystem|Component"),parent_ids:y.array(y.string().max(20)).max(50).optional().describe("Parent item IDs to link"),fields:y.record(y.string().max(60),y.string().max(1e4)).optional().describe("Project-field values keyed by defined field name"),echo:y.boolean().optional().describe("false → lean {id,type} response, saves ~580 tokens")},annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!1,openWorldHint:!1}},ds("atoms_create_item",async e=>{const{createItemHandler:t}=await Promise.resolve().then(()=>(Ki(),Gi));return t(e)})),is={steps:y.array(y.object({id:y.string().describe("Stable step id within the item"),instruction:y.string().describe("What the tester does"),test_data:y.string().optional().describe("Optional test data for the step"),expected:y.string().describe("Expected result")}).passthrough()).optional().describe("Test-case script steps: array of {id, instruction, test_data?, expected}. Max 50 steps, enforced by the DB trigger."),params:y.array(y.object({name:y.string().describe("Parameter name; steps reference it as {name}"),unit:y.string().optional().describe("Optional unit (e.g. km/h, V, m)"),values:y.array(y.union([y.string(),y.number()])).describe("Value set the test sweeps")}).passthrough()).optional().describe("Matrix parameters: array of {name, unit, values[]}. Matrix points (product of value-set sizes) capped at 64 by the DB trigger.")},Xa("atoms_update_item",{title:"Update ATOMS Item",description:'Update an existing item\'s fields. Only provided fields are changed.\n\nRequires editor or admin role. Logs old/new data diff to change history.\n\nArgs:\n - project_id (string, UUID): Project containing the item\n - item_id (string): Item to update\n - title, body, summary, domains, level, status: Fields to update (all optional) (a tag flagged as a function is not a domain and is rejected)\n - fields (object, optional): Project-field values, e.g. {"Verification Method": "Design Review"}. Keys must be defined via atoms_create_field; merged per key, "" clears a key; non-empty values must match each field type\n - steps (array, optional): Test-case script steps [{id, instruction, test_data?, expected}]. Max 50 steps (DB-enforced)\n - params (array, optional): Matrix parameters [{name, unit, values[]}]. Matrix points capped at 64 (DB-enforced)\n - expected_updated_at (string, optional): Pass metadata.updated_at from atoms_get_item to reject stale writes\n\nReturns:\n Updated item.',inputSchema:{project_id:y.string().uuid().describe("UUID of the project"),item_id:y.string().min(1).max(20).optional().describe("Item ID (single form)"),title:y.string().min(1).max(500).optional().describe("New title"),body:y.string().max(5e4).optional().describe("New body"),summary:y.string().max(2e3).optional().describe("New summary"),domains:y.array(y.string().max(64)).max(20).optional().describe("New domain tags (a tag flagged as a function is not a domain and is rejected)"),level:y.string().max(32).optional().describe("New level"),status:y.enum(["passed","failed","blocked","not-run"]).optional().describe("Test case status"),fields:y.record(y.string().max(60),y.string().max(1e4)).optional().describe('Project-field values keyed by defined field name; "" clears a key'),...is,expected_updated_at:yt.optional().describe("metadata.updated_at value from atoms_get_item; rejects stale writes"),echo:y.boolean().optional().describe("false → lean {id,updated_fields} response, saves ~580 tokens"),reason:y.string().max(500).optional().describe("Change rationale, persisted in audit history"),updates:y.array(y.object({item_id:y.string().min(1).max(20),title:y.string().min(1).max(500).optional(),body:y.string().max(5e4).optional(),summary:y.string().max(2e3).optional(),domains:y.array(y.string().max(64)).max(20).optional(),level:y.string().max(32).optional(),status:y.enum(["passed","failed","blocked","not-run"]).optional(),fields:y.record(y.string().max(60),y.string().max(1e4)).optional(),...is,expected_updated_at:yt.optional()})).optional().describe("Array form: update many items in one call"),idempotency_key:y.string().max(128).optional().describe("Safe retry key (24h TTL)"),continue_on_error:y.boolean().optional().describe("Allow partial success (default false = atomic)")},annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1}},ds("atoms_update_item",async e=>{const{updateItemHandler:t}=await Promise.resolve().then(()=>(sn(),tn));return t(e)})),Xa("atoms_delete_item",{title:"Delete ATOMS Item",description:'Soft-delete an item (sets deleted_at, preserves for audit trail).\n\nRequires editor or admin role. The item is never truly removed.\n\nWhen ATOMS_MCP_REQUIRE_CONFIRMATION is set on the server, this tool uses a\ntwo-step flow: the first call returns a preview + confirmation_token; the\nsecond call (with the token) executes. Tokens expire in 60s and are bound\nto (tool, project_id, item_id).\n\nArgs:\n - project_id (string, UUID): Project containing the item\n - item_id (string): Item to delete\n - confirmation_token (string, optional): Token from a prior preview call\n\nReturns:\n Confirmation with deleted item summary, or { status: "confirmation_required",\n preview, confirmation_token } when the confirmation gate is active.',inputSchema:{project_id:y.string().uuid().describe("UUID of the project"),item_id:y.string().min(1).max(20).describe("Item ID to soft-delete"),confirmation_token:y.string().max(500).optional().describe("Token from a prior preview call (only used when ATOMS_MCP_REQUIRE_CONFIRMATION is set)")},annotations:{readOnlyHint:!1,destructiveHint:!0,idempotentHint:!0,openWorldHint:!1}},ds("atoms_delete_item",async e=>{const{deleteItemHandler:t}=await Promise.resolve().then(()=>(hn(),_n));return t(e)})),Xa("atoms_restore_item",{title:"Restore ATOMS Item",description:'Undo a soft-delete — restores an item that was previously deleted with atoms_delete_item.\n\nOnly works on items that have been soft-deleted (still in the database with deleted_at set).\nHas no effect if the item is not deleted. Logs the restoration to change_history.\n\nArgs:\n - project_id (string, UUID): Project containing the item\n - item_id (string): ID of the deleted item to restore\n\nReturns:\n Confirmation with the restored item\'s title and type.\n\nExamples:\n - "Restore the deleted requirement REQ-042" → atoms_restore_item(project_id, "REQ-042")',inputSchema:{project_id:y.string().uuid().describe("UUID of the project"),item_id:y.string().max(256).describe("ID of the soft-deleted item to restore")},annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1}},ds("atoms_restore_item",async e=>{const{restoreItemHandler:t}=await Promise.resolve().then(()=>(wn(),gn));return t(e)})),Xa("atoms_link_items",{title:"Link ATOMS Items",description:"Add or remove relationships between items.\n\nSupports: parent, child, related, verifies, verified_by.\nUpdates both items' JSONB data and the shadow table.\n\nArgs:\n - project_id (string, UUID): Project containing both items\n - from_id (string): Source item\n - to_id (string): Target item\n - type (string): parent|child|related|verifies|verified_by\n - action (string): add|remove\n - expected_from_updated_at / expected_to_updated_at (string, optional): pass metadata.updated_at values from atoms_get_item to reject stale link commands\n\nReturns:\n Updated relationship state for both items.",inputSchema:{project_id:y.string().uuid().describe("UUID of the project"),from_id:y.string().min(1).max(20).optional().describe("Source item ID (single form)"),to_id:y.string().min(1).max(20).optional().describe("Target item ID (single form)"),type:y.enum(["parent","child","related","verifies","verified_by"]).optional().describe("Relationship type"),action:y.enum(["add","remove"]).optional().describe("Add or remove"),expected_from_updated_at:yt.optional().describe("metadata.updated_at for from_id from atoms_get_item"),expected_to_updated_at:yt.optional().describe("metadata.updated_at for to_id from atoms_get_item"),echo:y.boolean().optional().describe("false → lean response"),reason:y.string().max(500).optional().describe("Change rationale, persisted in audit history"),operations:y.array(y.object({action:y.enum(["add","remove"]),from_id:y.string().min(1).max(20),to_id:y.string().min(1).max(20),type:y.enum(["parent","child","related","verifies","verified_by"]),expected_from_updated_at:yt.optional(),expected_to_updated_at:yt.optional()})).optional().describe("Array form: batch relationship ops. Always atomic with DAG cycle detection."),idempotency_key:y.string().max(128).optional().describe("Safe retry key (24h TTL)")},annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1}},ds("atoms_link_items",async e=>{const{linkItemsHandler:t}=await Promise.resolve().then(()=>(jn(),bn));return t(e)})),os({appName:"import",htmlFile:"import-app.html",toolName:"atoms_bulk_import",title:"Bulk Import ATOMS Items",description:"Bulk create multiple items in a single tool call. Essential for AI agents\nthat generate 20+ requirements at once.\n\nChecks write access once, generates sequential IDs, batch inserts all items,\nand reports per-item errors without aborting the entire batch.\n\nIn MCP App hosts (Claude, ChatGPT), renders an interactive results table.\nFalls back to JSON for non-UI clients.\n\nArgs:\n - project_id (string, UUID): Target project\n - items (array): Up to 100 items to create, each with:\n - type (string): requirement|test-case|note\n - title (string): Item title (max 500 chars)\n - body (string, optional): Rich text body\n - summary (string, optional): Brief summary\n - domains (string[], optional): Domain tags (a tag flagged as a function is not a domain and is rejected)\n - level (string, optional): System|Subsystem|Component\n - parent_id (string, optional): Parent item ID to link\n\nReturns:\n { created: number, items: [{ id, title, type }], errors: [] }\n\nLimits:\n - Maximum 100 items per call\n - If any item fails, others still succeed — errors reported separately\n\nSide effects:\n - Logs to change_history with actor='mcp_<client>' for each created item\n - Creates parent relationships for items with parent_id",inputSchema:{project_id:y.string().uuid().describe("UUID of the target project"),items:y.array(y.object({type:y.enum(["requirement","test-case","note"]).describe("Item type"),title:y.string().min(1).max(500).describe("Item title"),body:y.string().max(5e4).optional().describe("Rich text body"),summary:y.string().max(2e3).optional().describe("Brief summary"),domains:y.array(y.string().max(64)).max(20).optional().describe("Domain tags"),level:y.string().max(32).optional().describe("System|Subsystem|Component"),parent_id:y.string().max(20).optional().describe("Parent item ID to link")})).min(1).max(100).describe("Items to create (1-100)"),confirmation_token:y.string().max(500).optional().describe("Token from a prior preview call (only used when ATOMS_MCP_REQUIRE_CONFIRMATION is set)")},annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!1,openWorldHint:!1},handler:ds("atoms_bulk_import",async e=>{const{bulkImportHandler:t}=await Promise.resolve().then(()=>(In(),xn));return t(e)})}),ns={point:y.record(y.string(),y.union([y.string(),y.number()])).optional().describe('Matrix point this run executed, e.g. {"speed": 40}'),release_id:y.string().uuid().optional().describe("Formal Release to bind this run to (a baseline with a release_version)"),build:y.string().optional().describe("Free-text build label"),environment:y.string().optional().describe("Structured environment field, distinct from build"),tooling:y.string().optional().describe("Structured tooling field, distinct from build"),steps_rev:y.string().optional().describe("Item revision id the run's steps were pinned to"),step_results:y.array(y.object({step_id:y.string().describe("Step id from data.steps"),status:y.enum(["passed","failed","blocked","skipped"]).describe("Per-step status"),actual:y.string().optional().describe("Actual-result note"),evidence_url:y.string().optional().describe("Evidence URL (validated server-side)")}).passthrough()).optional().describe("Per-step results [{step_id, status, actual?, evidence_url?}]. When present the run verdict is derived server-side and the top-level result is ignored.")},Xa("atoms_record_test_result",{title:"Record Test Result",description:"Record a pass/fail/blocked result for a test case.\n\nAppends to canonical items.data.runs and projects test_results from that\nhistory. Use atoms_get_item to see all results.\n\nArgs:\n - project_id (string, UUID): Project containing the test case\n - item_id (string): Test case ID (e.g., \"TC-001\")\n - result (string): passed|failed|blocked|not-run\n - note (string, optional): Reason or comment for this result\n - asset (string, optional): Asset identifier this run was executed against\n (VIN, module ID, lot number, serial number, etc). Max 200 chars.\n Auto-trimmed; empty/whitespace is treated as omitted. If the asset\n name does not yet exist in the project, it is auto-registered.\n - point, release_id, build, environment, tooling, steps_rev (optional):\n run metadata. release_id must be a formal Release (a baseline with a\n release_version). All validated server-side.\n - step_results (array, optional): per-step outcomes for a stepped run.\n When provided, the verdict is derived server-side from the steps.\n\nReturns:\n Recorded result with timestamp. When asset is provided, also returns\n asset_id and asset_created.\n\nSide effects:\n - Appends to items.data.runs through save_item_with_revision\n - Projects public.test_results from items.data.runs\n - Inserts into assets table if asset name is new in this project\n - Logs to change_history with actor='mcp_<client>'",inputSchema:{project_id:y.string().uuid().describe("UUID of the project"),item_id:y.string().min(1).max(20).describe("Test case ID (e.g., TC-001)"),result:y.enum(["passed","failed","blocked","not-run"]).describe("Test result"),note:y.string().max(500).optional().describe("Reason or comment for this result"),asset:y.string().max(200).optional().describe("Asset identifier this run was executed against (VIN, module ID, lot, etc). Auto-registered if new."),...ns},annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!1,openWorldHint:!1}},ds("atoms_record_test_result",async e=>{const{recordTestResultHandler:t}=await Promise.resolve().then(()=>(Rn(),An));return t(e)})),Xa("atoms_record_test_results",{title:"Record Test Results (bulk)",description:'Record many runs on one test case in a single atomic call.\n\nOptimized for multi-asset campaigns. Each entry can have its own asset and\nresult. Asset lookup-or-create happens per entry; new asset names are\nauto-registered. Runs are appended to canonical items.data.runs, then\ntest_results is projected from that history. The whole call is one DB\ntransaction; any entry failure rolls back the rest.\n\nUse the singular atoms_record_test_result for one-off runs.\n\nArgs:\n - project_id (string, UUID): Project containing the test case\n - item_id (string): Test case ID (e.g., "TC-001")\n - results (array, 1-100 entries): Each entry has:\n - asset (string, optional): Asset identifier (VIN, module ID, lot)\n - result (string, required): passed|failed|blocked|not-run\n - note (string, optional): Run note\n - date (string, optional): ISO 8601 datetime, default now()\n - point, release_id, build, environment, tooling, steps_rev (optional):\n run metadata; release_id must be a formal Release. Server-validated.\n - step_results (array, optional): per-step outcomes; when present the\n verdict is derived server-side from the steps.\n\nReturns:\n Per-entry outcomes with asset_id, asset_created, run_at, ok flags.\n\nSide effects:\n - Appends to items.data.runs through save_item_with_revision\n - Projects public.test_results from items.data.runs\n - Inserts new rows into assets for any asset name not already registered\n - Logs a single change_history entry summarizing the batch',inputSchema:{project_id:y.string().uuid().describe("UUID of the project"),item_id:y.string().min(1).max(20).describe("Test case ID (e.g., TC-001)"),results:y.array(y.object({asset:y.string().max(200).optional().describe("Asset identifier this run was executed against. Auto-registered if new."),result:y.enum(["passed","failed","blocked","not-run"]).describe("Test result"),note:y.string().max(500).optional().describe("Run note"),date:y.string().optional().describe("ISO 8601 datetime (default: now())"),...ns})).min(1).max(100).describe("1 to 100 result entries on this test case")},annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!1,openWorldHint:!1}},ds("atoms_record_test_results",async e=>{const{recordTestResultsBulkHandler:t}=await Promise.resolve().then(()=>(Mn(),Dn));return t(e)})),os({appName:"trace",htmlFile:"trace-app.html",toolName:"atoms_trace",title:"Trace Item Relationships",description:'Walk the traceability graph from a starting item.\n\nAnswers questions like "which requirements does TC-003 verify?" or\n"if I change REQ-001, what\'s affected downstream?"\n\nIn MCP App hosts (Claude, ChatGPT), renders an interactive graph visualization.\nFalls back to flat list for non-UI clients.\n\nArgs:\n - project_id (string, UUID): Project containing the item\n - item_id (string): Starting item ID\n - direction (string): upstream|downstream|both\n - upstream: follow parent/verifies links (dependencies)\n - downstream: follow child/verified_by links (dependents)\n - both: trace in both directions\n - depth (number, optional): Max traversal depth (default 5, max 10)\n - relationship_types (string[], optional): Filter to specific types (parent, child, related, verifies, verified_by, mitigated_by, mitigates)\n\nReturns:\n { root, direction, items: [{ id, title, type, relationship, depth }], total_count }\n\nExamples:\n - "What does TC-003 verify?" → atoms_trace(project_id, "TC-003", "upstream")\n - "What\'s affected by REQ-001?" → atoms_trace(project_id, "REQ-001", "downstream")',inputSchema:{project_id:y.string().uuid().describe("UUID of the project"),item_id:y.string().min(1).max(20).describe("Starting item ID"),direction:y.enum(["upstream","downstream","both"]).describe("Traversal direction"),depth:y.number().int().min(1).max(10).default(5).describe("Max traversal depth (default 5)"),relationship_types:y.array(y.enum(["parent","child","related","verifies","verified_by","mitigated_by","mitigates"])).optional().describe("Filter to specific relationship types")},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1},handler:ds("atoms_trace",async e=>{const{traceHandler:t}=await Promise.resolve().then(()=>(Fn(),Nn));return t(e)})}),Xa("atoms_list_assets",{title:"List Project Assets",description:"List asset identifiers registered in a project.\n\nAssets are free-form per-instance IDs (VINs, module names, lot numbers,\nserial numbers) that test runs are tagged against. They are auto-registered\nthe first time they appear on a run via atoms_record_test_result.\n\nArgs:\n - project_id (string, UUID): Project to list assets for\n - include_archived (boolean, optional): Include archived assets (default false)\n\nReturns:\n { assets: [{ id, name, archived_at, created_at }], total }",inputSchema:{project_id:y.string().uuid().describe("UUID of the project"),include_archived:y.boolean().optional().describe("Include archived assets (default false)")},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1}},ds("atoms_list_assets",async e=>{const{listAssetsHandler:t}=await Promise.resolve().then(()=>(Bn(),Wn));return t(e)})),Xa("atoms_list_variables",{title:"List Project Variables",description:"List all parameterized variables defined in a project.\n\nVariables are shared values (e.g., backup_camera_latency = 2s) that can be\nreferenced in requirement bodies as {variable_name}.\n\nArgs:\n - project_id (string, UUID): Project to list variables for\n - baseline_id (string, UUID, optional): list variables as they were at that baseline\n - as_of (string ISO 8601, optional): list variables as they were at that moment\n - limit (number, optional): max results (default 50, max 200)\n - offset (number, optional): pagination offset (default 0)\n\nReturns:\n { variables: [{ id, name, value, unit, description }], total }, meta: { total_count, limit, offset, has_more }",inputSchema:{project_id:y.string().uuid().describe("UUID of the project"),baseline_id:y.string().uuid().optional().describe("Optional baseline UUID for time-travel reads"),as_of:y.string().datetime().optional().describe("Optional ISO timestamp for time-travel reads"),limit:y.number().int().min(1).max(200).default(50).describe("Max results (default 50, max 200)"),offset:y.number().int().min(0).default(0).describe("Pagination offset (default 0)")},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1}},ds("atoms_list_variables",async e=>{const{listVariablesHandler:t}=await Promise.resolve().then(()=>(ta(),zn));return t(e)})),Xa("atoms_get_variable",{title:"Get Project Variable",description:'Get a specific variable by name, including which items reference it.\n\nArgs:\n - project_id (string, UUID): Project containing the variable\n - variable_name (string): Variable name (e.g., "backup_camera_latency")\n - baseline_id (string, UUID, optional): get the variable as it was at that baseline\n - as_of (string ISO 8601, optional): get the variable as it was at that moment\n\nReturns:\n { variable, referenced_by: [{ id, title, type }], reference_count }',inputSchema:{project_id:y.string().uuid().describe("UUID of the project"),variable_name:y.string().min(1).max(64).describe("Variable name"),baseline_id:y.string().uuid().optional().describe("Optional baseline UUID for time-travel reads"),as_of:y.string().datetime().optional().describe("Optional ISO timestamp for time-travel reads")},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1}},ds("atoms_get_variable",async e=>{const{getVariableHandler:t}=await Promise.resolve().then(()=>(ta(),zn));return t(e)})),Xa("atoms_update_variable",{title:"Update Project Variable",description:"Update a variable's value, unit, or description.\n\nArgs:\n - project_id (string, UUID): Project containing the variable\n - variable_name (string): Variable name\n - value (string, optional): New value\n - unit (string, optional): Unit abbreviation (e.g., 's', 'Hz')\n - description (string, optional): Human-readable description\n\nReturns:\n { variable }",inputSchema:{project_id:y.string().uuid().describe("UUID of the project"),variable_name:y.string().min(1).max(64).describe("Variable name"),value:y.string().max(500).optional().describe("New value"),unit:y.string().max(32).optional().describe("Unit abbreviation"),description:y.string().max(2e3).optional().describe("Description")},annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1}},ds("atoms_update_variable",async e=>{const{updateVariableHandler:t}=await Promise.resolve().then(()=>(ta(),zn));return t(e)})),Xa("atoms_create_variable",{title:"Create Project Variable",description:'Create one or many parameterized variables for a project.\n\nSingle form: provide name + value.\nArray form: provide variables[] for bulk creation (one atomic INSERT).\n\nVariables can then be embedded in requirement bodies as {variable_name}.\n\nArgs (single form):\n - project_id (string, UUID): Project to add the variable to\n - name (string): Variable name, letters, digits, underscores; must start with letter/underscore\n - value (string): Initial value (e.g., "100", "true", "nominal")\n - unit (string, optional): Unit abbreviation (e.g., "Hz", "ms", "m/s²")\n - description (string, optional): Human-readable description\n - echo (bool, optional): false → lean {name} response (default true)\n\nArgs (array form):\n - project_id (string, UUID): Project UUID\n - variables[]: Array of { name, value, unit?, description? }\n - echo (bool, optional): false → lean {name} list (default true)\n - idempotency_key (string, optional): Dedup key\n\nReturns (single): { variable, message }\nReturns (array): { batch_id, created, variables, message }',inputSchema:{project_id:y.string().uuid().describe("UUID of the project"),name:y.string().min(1).max(64).optional().describe("Variable name (single form)"),value:y.string().min(1).max(500).optional().describe("Initial value (single form)"),unit:y.string().max(32).optional().describe("Unit abbreviation"),description:y.string().max(2e3).optional().describe("Description"),echo:y.boolean().optional().describe("false → lean response (default true)"),variables:y.array(y.object({name:y.string().min(1).max(64).describe("Variable name"),value:y.string().min(1).max(500).describe("Initial value"),unit:y.string().max(32).optional().describe("Unit abbreviation"),description:y.string().max(2e3).optional().describe("Description")})).optional().describe("Array form: batch variable creation. Atomic — all or none."),idempotency_key:y.string().max(128).optional().describe("Dedup key for retries")},annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!1,openWorldHint:!1}},ds("atoms_create_variable",async e=>{const{createVariableHandler:t}=await Promise.resolve().then(()=>(ta(),zn));return t(e)})),Xa("atoms_delete_variable",{title:"Delete Project Variable",description:"Delete a project variable. Refuses by default if any items reference it.\n\nArgs:\n - project_id (string, UUID): Project containing the variable\n - name (string): Variable name to delete\n - force (bool, optional): Delete even if items still reference it (default false)\n\nReturns:\n { deleted, name, referenced_by_items, warning? }",inputSchema:{project_id:y.string().uuid().describe("UUID of the project"),name:y.string().min(1).max(64).describe("Variable name to delete"),force:y.boolean().optional().describe("Delete even if items reference it (default false)"),confirmation_token:y.string().max(500).optional().describe("Token from a prior preview call (only used when ATOMS_MCP_REQUIRE_CONFIRMATION is set)")},annotations:{readOnlyHint:!1,destructiveHint:!0,idempotentHint:!1,openWorldHint:!1}},ds("atoms_delete_variable",async e=>{const{deleteVariableHandler:t}=await Promise.resolve().then(()=>(ta(),zn));return t(e)})),Xa("atoms_list_domains",{title:"List Project Domains",description:"List all domain tags registered in a project's taxonomy, with item counts.\n\nArgs:\n - project_id (string, UUID): Project to inspect\n\nReturns:\n { domains: [{ name, item_count }], total }",inputSchema:{project_id:y.string().uuid().describe("UUID of the project")},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1}},ds("atoms_list_domains",async e=>{const{listDomainsHandler:t}=await Promise.resolve().then(()=>(ca(),ra));return t(e)})),Xa("atoms_create_domain",{title:"Create Project Domain",description:'Register a new domain tag in the project taxonomy.\n\nOnce created, items can be tagged with this domain via atoms_create_item or atoms_update_item.\n\nArgs:\n - project_id (string, UUID): Project to add the domain to\n - name (string): Domain name (lowercased automatically, e.g., "safety", "can-bus")\n\nReturns:\n { domain: { name }, message }',inputSchema:{project_id:y.string().uuid().describe("UUID of the project"),name:y.string().min(1).max(64).describe("Domain name")},annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!1,openWorldHint:!1}},ds("atoms_create_domain",async e=>{const{createDomainHandler:t}=await Promise.resolve().then(()=>(ca(),ra));return t(e)})),Xa("atoms_list_levels",{title:"List Project Levels",description:"List the project's level taxonomy in order, with each level's L# (positional index) and item count.\n\nArgs:\n - project_id (string, UUID): Project to list levels for\n\nReturns:\n { levels: [{ name, prefix, item_count }], total }",inputSchema:{project_id:y.string().uuid().describe("UUID of the project")},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1}},ds("atoms_list_levels",async e=>{const{listLevelsHandler:t}=await Promise.resolve().then(()=>(gi(),Xr));return t(e)})),Xa("atoms_create_level",{title:"Create Project Level",description:'Register a new level in the project taxonomy so items can be tagged with it and it shows in the Strata view. Appended to the ordered level list; its L# is the next index. Names are user-defined labels (lowercased automatically); matching is case-insensitive.\n\nArgs:\n - project_id (string, UUID): Project to add the level to\n - name (string): Level name, e.g. "feature", "system", "subsystem"\n\nReturns:\n { level: { name, prefix }, message }',inputSchema:{project_id:y.string().uuid().describe("UUID of the project"),name:y.string().min(1).max(64).describe("Level name (user-defined label)")},annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!1,openWorldHint:!1}},ds("atoms_create_level",async e=>{const{createLevelHandler:t}=await Promise.resolve().then(()=>(gi(),Xr));return t(e)})),Xa("atoms_list_fields",{title:"List Project Fields",description:'List the project\'s custom field definitions (project-scoped key-value slots on items, e.g. "Verification Method"), with counts of items carrying a value.\n\nField values are set per item via atoms_create_item / atoms_update_item fields.\n\nArgs:\n - project_id (string, UUID): Project to inspect\n\nReturns:\n { fields: [{ name, type, options, item_count }], total }',inputSchema:{project_id:y.string().uuid().describe("UUID of the project")},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1}},ds("atoms_list_fields",async e=>{const{listFieldsHandler:t}=await Promise.resolve().then(()=>(Qi(),Hi));return t(e)})),Xa("atoms_create_field",{title:"Create Project Field",description:'Define a project custom field so items can carry a value for it (shown as a Matrix column, e.g. "Verification Method").\n\nThe name keeps its casing; uniqueness is case-insensitive. Type is one of text, long, bool, date, person, select, number, link. Select fields require options. Field definitions never carry visibility rules, permissions, or workflow logic.\n\nArgs:\n - project_id (string, UUID): Project to define the field in\n - name (string): Field name, 1-60 characters (e.g. "Verification Method", "Essential Output?")\n - type (string, optional): Defaults to text\n - options (string[], optional): Required when type=select\n - scope (string, optional): project (default) or organization (org admin only)\n\nReturns:\n { field: { name, type, options }, message }',inputSchema:{project_id:y.string().uuid().describe("UUID of the project"),name:y.string().min(1).max(60).describe("Field name (display name, 1-60 chars)"),type:y.enum(["text","long","bool","date","person","select","number","link"]).optional().describe("Field type, defaults to text"),options:y.array(y.string().min(1).max(120)).max(100).optional().describe("Allowed values for select fields"),scope:y.enum(["project","organization"]).optional().describe("Create in this project or copy to every project in the organization")},annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!1,openWorldHint:!1}},ds("atoms_create_field",async e=>{const{createFieldHandler:t}=await Promise.resolve().then(()=>(Qi(),Hi));return t(e)})),Xa("atoms_delete_field",{title:"Delete Project Field",description:"Delete a project field definition. Existing item values are left in item JSON but no longer render as Matrix columns until a matching field is created again.\n\nArgs:\n - project_id (string, UUID): Project containing the field\n - name (string): Field name to delete",inputSchema:{project_id:y.string().uuid().describe("UUID of the project"),name:y.string().min(1).max(60).describe("Field name")},annotations:{readOnlyHint:!1,destructiveHint:!0,idempotentHint:!1,openWorldHint:!1}},ds("atoms_delete_field",async e=>{const{deleteFieldHandler:t}=await Promise.resolve().then(()=>(Qi(),Hi));return t(e)})),Xa("atoms_reorder_fields",{title:"Reorder Project Fields",description:"Reorder project field definitions. Definition order drives the Column Picker listing and a fresh user's Matrix column order.\n\nOrdering only: no definition is added, dropped, renamed, or retyped, and no item value moves. Use this instead of deleting and recreating definitions to change their order.\n\nArgs:\n - project_id (string, UUID): Project containing the fields\n - names (string[]): Every existing field name exactly once, in the order you want them. A list that is not a permutation of the current names is refused, not partially applied.",inputSchema:{project_id:y.string().uuid().describe("UUID of the project"),names:y.array(y.string().min(1).max(60)).describe("Every existing field name exactly once, in the desired order")},annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1}},ds("atoms_reorder_fields",async e=>{const{reorderFieldsHandler:t}=await Promise.resolve().then(()=>(Qi(),Hi));return t(e)})),Xa("atoms_update_domain",{title:"Rename Project Domain",description:"Rename a domain tag. Updates the taxonomy and propagates to all tagged items.\n\nUse dry_run=true first to see how many items will be affected before committing.\n\nArgs:\n - project_id (string, UUID): Project containing the domain\n - name (string): Current domain name\n - new_name (string): New domain name\n - dry_run (bool, optional): If true, return impact count without making changes (default false)\n\nReturns (dry_run=false):\n { domain: { name }, renamed_from, items_updated, message }\n\nReturns (dry_run=true):\n { dry_run: true, rename: { from, to }, items_affected, message }",inputSchema:{project_id:y.string().uuid().describe("UUID of the project"),name:y.string().min(1).max(64).describe("Current domain name"),new_name:y.string().min(1).max(64).describe("New domain name"),dry_run:y.boolean().optional().describe("Preview impact without applying (default false)")},annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!1,openWorldHint:!1}},ds("atoms_update_domain",async e=>{const{updateDomainHandler:t}=await Promise.resolve().then(()=>(ca(),ra));return t(e)})),Xa("atoms_delete_domain",{title:"Delete Project Domain",description:"Delete a domain tag from the project taxonomy.\n\nRefuses by default if any items are tagged with it. With force=true, deletes the\ndomain and strips the tag from all items.\n\nArgs:\n - project_id (string, UUID): Project containing the domain\n - name (string): Domain name to delete\n - force (bool, optional): Strip from items and delete anyway (default false)\n\nReturns:\n { deleted, name, items_updated, message }",inputSchema:{project_id:y.string().uuid().describe("UUID of the project"),name:y.string().min(1).max(64).describe("Domain name to delete"),force:y.boolean().optional().describe("Strip from all items and delete (default false)"),confirmation_token:y.string().max(500).optional().describe("Token from a prior preview call (only used when ATOMS_MCP_REQUIRE_CONFIRMATION is set)")},annotations:{readOnlyHint:!1,destructiveHint:!0,idempotentHint:!1,openWorldHint:!1}},ds("atoms_delete_domain",async e=>{const{deleteDomainHandler:t}=await Promise.resolve().then(()=>(ca(),ra));return t(e)})),os({appName:"summary",htmlFile:"summary-app.html",toolName:"atoms_project_summary",title:"Project Compliance Summary",description:'One-call project health and compliance dashboard.\n\nReturns item counts by type, test execution status, requirement coverage\n(overall and by domain), and recent change activity.\n\nIn MCP App hosts (Claude, ChatGPT), renders an interactive dashboard with charts.\nFalls back to JSON for non-UI clients.\n\nArgs:\n - project_id (string, UUID): Project to summarize\n\nReturns:\n { project_name, counts, test_status, coverage, coverage_by_domain, recent_changes, last_updated }\n\nExamples:\n - "Give me a compliance snapshot" → atoms_project_summary(project_id)\n - "How\'s our test coverage?" → atoms_project_summary(project_id)',inputSchema:{project_id:y.string().uuid().describe("UUID of the project")},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1},handler:ds("atoms_project_summary",async e=>{const{projectSummaryHandler:t}=await Promise.resolve().then(()=>(ua(),la));return t(e)})}),os({appName:"browse",htmlFile:"browse-app.html",toolName:"atoms_browse",title:"Browse ATOMS Items",description:'View and filter items in an ATOMS project with a visual browser.\n\nIn MCP App hosts (Claude, ChatGPT), renders an interactive filterable list\nwith expandable item details. Falls back to JSON for non-UI clients.\n\nArgs:\n - project_id (string, UUID): The project to browse\n - type (string, optional): Filter by type (requirement, test-case, note, table)\n - domain (string, optional): Filter by domain tag\n - level (string, optional): Filter by level (System, Subsystem, Component)\n - query (string, optional): Full-text search query\n - limit (number, optional): Max results (default 25, max 100)\n - offset (number, optional): Pagination offset (default 0)\n\nReturns:\n { items: [...], filters: { domains, levels, types }, meta: { total_count, limit, offset, has_more } }\n\nExamples:\n - "Show me the requirements" → atoms_browse(project_id, type="requirement")\n - "Browse safety items" → atoms_browse(project_id, domain="Safety")\n - "Find braking requirements" → atoms_browse(project_id, query="braking")',inputSchema:{project_id:y.string().uuid().describe("UUID of the project"),type:y.enum(["requirement","test-case","note","table"]).optional().describe("Filter by item type"),domain:y.string().max(64).optional().describe("Filter by domain tag"),level:y.string().max(32).optional().describe("Filter by level (System, Subsystem, Component)"),query:y.string().max(1e3).optional().describe("Full-text search query"),limit:y.number().int().min(1).max(100).default(25).describe("Max results (default 25, max 100)"),offset:y.number().int().min(0).default(0).describe("Pagination offset (default 0)")},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1},handler:ds("atoms_browse",async e=>{const{browseHandler:t}=await Promise.resolve().then(()=>(ba(),_a));return t(e)})}),Xa("atoms_impact_analysis",{title:"Analyze Impact of Changing an Item",description:'Analyze the blast radius of changing a specific item.\n\nTraverses the relationship graph from the given item and returns every item that\nwould be affected by a change — with their types, depths, and full relationship paths.\n\nArgs:\n - project_id (string, UUID): Project containing the item\n - item_id (string): Item to analyze (e.g., "REQ-001")\n - direction (string, optional): "downstream" (default) | "upstream" | "both"\n - downstream: what this item affects (children, verified_by)\n - upstream: what affects this item (parents, verifies)\n - both: full bidirectional traversal\n - depth (integer, optional): Max traversal depth. Omit for full depth (all reachable nodes).\n - include_variable_refs (boolean, optional): Reserved for future variable-aware impact. Default true.\n\nReturns:\n { root: { item_id, title, type }, impacted_items: [{ item_id, title, type, depth, relationship_path }], summary: { total_impacted, by_type, max_depth_reached } }\n\nExamples:\n - "What breaks if I change REQ-001?" → atoms_impact_analysis(project_id, "REQ-001")\n - "What\'s upstream of TC-005?" → atoms_impact_analysis(project_id, "TC-005", direction="upstream")\n - "Show direct dependencies only" → atoms_impact_analysis(project_id, "REQ-001", depth=1)\n\nErrors:\n - "Item not found" → verify item_id exists in the project',inputSchema:{project_id:y.string().uuid().describe("UUID of the project"),item_id:y.string().min(1).max(20).describe("Item ID to analyze (e.g., REQ-001)"),direction:y.enum(["downstream","upstream","both"]).default("downstream").describe("Traversal direction (default: downstream)"),depth:y.number().int().min(1).optional().describe("Max traversal depth. Omit for full depth."),include_variable_refs:y.boolean().default(!0).describe("Reserved for variable-aware impact (future). Default true.")},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1}},ds("atoms_impact_analysis",async e=>{const{impactAnalysisHandler:t}=await Promise.resolve().then(()=>(Sa(),va));return t(e)})),Xa("atoms_list_baselines",{title:"List ATOMS Baselines",description:"List named baselines (named project snapshots) for a project, newest first.\n\nA baseline is a labeled moment in time. Use atoms_create_baseline to make one,\natoms_diff_baselines to compare two, and atoms_restore_baseline to roll back.",inputSchema:{project_id:y.string().uuid().describe("UUID of the project"),limit:y.number().int().min(1).max(100).default(50).describe("Max results (default 50, max 100)"),offset:y.number().int().min(0).default(0).describe("Pagination offset")},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1}},ds("atoms_list_baselines",async e=>{const{listBaselinesHandler:t}=await Promise.resolve().then(()=>(Ta(),ka));return t(e)})),Xa("atoms_create_baseline",{title:"Create ATOMS Baseline",description:"Create a named baseline (snapshot label) at the current moment for a project.\n\nThe baseline records a name + timestamp. It does NOT copy item data; the project\nstate at that moment is reconstructed via temporal queries against item_revisions.\nNames must be unique per project (case-insensitive).",inputSchema:{project_id:y.string().uuid().describe("UUID of the project"),name:y.string().min(1).max(200).describe("Baseline name (1-200 chars, unique per project)"),description:y.string().max(2e3).optional().describe("Optional description (max 2000 chars)")},annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!1,openWorldHint:!1}},ds("atoms_create_baseline",async e=>{const{createBaselineHandler:t}=await Promise.resolve().then(()=>(Ta(),ka));return t(e)})),Xa("atoms_lock_baseline",{title:"Lock ATOMS Baseline",description:"Lock a baseline so it becomes audit-grade immutable. Admin only. One-way: there is no unlock.\n\nAfter locking, the baseline's name, description, and snapshot_at cannot be changed,\nand it cannot be deleted. Use for regulatory submissions and DRBs.",inputSchema:{baseline_id:y.string().uuid().describe("UUID of the baseline to lock")},annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!1,openWorldHint:!1}},ds("atoms_lock_baseline",async e=>{const{lockBaselineHandler:t}=await Promise.resolve().then(()=>(Ta(),ka));return t(e)})),Xa("atoms_diff_baselines",{title:"Diff ATOMS Baselines",description:"Compute the diff between two baselines: which items were added, removed, or modified.\n\nReturns a summary plus a paginated items array. Pass detail=true to include\nper-item field-level diffs (slower for large projects).",inputSchema:{baseline_a_id:y.string().uuid().describe("UUID of the first (older) baseline"),baseline_b_id:y.string().uuid().describe("UUID of the second (newer) baseline"),detail:y.boolean().default(!1).describe("Include per-item field-level diffs (default false)"),limit:y.number().int().min(1).max(200).default(50).describe("Max items per page (default 50)"),offset:y.number().int().min(0).default(0).describe("Pagination offset")},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1}},ds("atoms_diff_baselines",async e=>{const{diffBaselinesHandler:t}=await Promise.resolve().then(()=>(Ta(),ka));return t(e)})),Xa("atoms_restore_baseline",{title:"Restore from ATOMS Baseline",description:"Restore project state to a baseline. Admin only. Preview-by-default.\n\nCall with confirm=false (default) for a dry run that returns items_to_restore count.\nCall with confirm=true to execute: each item present at the baseline gets a new\nrevision matching its historical state, event_source='restore'. Items added AFTER\nthe baseline are NOT deleted. Always recoverable via further history.",inputSchema:{baseline_id:y.string().uuid().describe("UUID of the baseline to restore from"),confirm:y.boolean().default(!1).describe("Set true to execute; false (default) returns a preview")},annotations:{readOnlyHint:!1,destructiveHint:!0,idempotentHint:!1,openWorldHint:!1}},ds("atoms_restore_baseline",async e=>{const{restoreBaselineHandler:t}=await Promise.resolve().then(()=>(Ta(),ka));return t(e)})),Xa("atoms_create_analysis",{title:"Create HARA / DFMEA Analysis",description:'Create a new safety analysis (HARA or DFMEA) with one empty row per seed.\n\nRequires editor or admin role. Structure matches analyses created in the ATOMS web app.\nA HARA is seeded from FUNCTIONS (tags flagged as functions), never from requirements: a\nrequirement is an output of the analysis. A DFMEA is seeded from requirements / design elements.\n\nArgs:\n - project_id (string, UUID): Target project\n - subtype (string): hara | dfmea\n - title (string, optional): Analysis title (default "New HARA" / "New DFMEA")\n - source_functions (string[], hara only, max 50): Function names to seed rows from. A name\n absent from the project\'s tags is registered as a function-flagged tag; matching is\n case-insensitive and trimmed. Rejected for dfmea.\n - source_req_ids (string[], dfmea only, max 50): Requirement IDs to seed rows from.\n Each must exist, be a plain requirement, and not itself be an analysis row. Rejected for hara.\n - revision (string, optional): Revision label (default "1.0")\n\nReturns:\n { id (e.g. "HARA-001" / "DFMEA-001"), subtype, title, revision, standard? ("ISO 26262" for hara),\n rows: [{ id, source_function }] (hara) | [{ id, source_req }] (dfmea),\n link_warnings?: [{ row_id, source_function | source_req, message }], warning? }\n rows lists every created row. If any row could not be attached to the analysis, it is\n named in link_warnings and warning explains the repair (atoms_link_items with type\n \'parent\', from_id=<row id>, to_id=<analysis id>); such rows will not appear in\n atoms_get_analysis until relinked.\n\nSide effects:\n - Creates the analysis parent item plus one row item (SG-nnn / FM-nnn) per seed\n - hara: registers new function names in the project\'s tags and function flags (metadata)\n - Links each row to the parent (dual-written to the relationship shadow table)\n - Logs each created item to change_history with actor=\'mcp_<client>\'',inputSchema:{project_id:y.string().uuid().describe("UUID of the target project"),subtype:y.enum(["hara","dfmea"]).describe("Analysis subtype"),title:y.string().min(1).max(500).optional().describe("Analysis title"),source_functions:y.array(y.string().min(1).max(100)).max(50).optional().describe("hara only: function names to seed rows from (0-50); new names are registered as function-flagged tags"),source_req_ids:y.array(y.string().min(1).max(20)).max(50).optional().describe("dfmea only: requirement IDs to seed rows from (0-50)"),revision:y.string().min(1).max(32).optional().describe("Revision label (default 1.0)")},annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!1,openWorldHint:!1}},ds("atoms_create_analysis",async e=>{const{createAnalysisHandler:t}=await Promise.resolve().then(()=>(as(),Ma));return t(e)})),Xa("atoms_fill_row",{title:"Fill Analysis Row Fields",description:"Fill fields on one HARA / DFMEA analysis row (SG-nnn / FM-nnn).\n\nRequires editor or admin role. Written fields are marked as MCP-sourced so the\nweb app shows their provenance. The computed level (ASIL / RPN) is derived and\nnever stored: asil is never a writable key.\n\nArgs:\n - project_id (string, UUID): Project containing the row\n - row_id (string): Analysis row ID (e.g. \"SG-001\", \"FM-003\")\n - fields (object): Field name to value map. Allowed keys:\n hara: failure_mode, operating_scenario, hazard, assumptions, severity (S0-S3), exposure (E0-E4),\n controllability (C0-C3), safe_state, ftti, rationale, source_function, effect, title\n dfmea: failure_mode, cause, severity (1-10), occurrence (1-10), detection (1-10),\n rationale, effect\n 'effect' writes the row's long text body (the hazardous event for hara). dfmea failure_mode also\n becomes the row title. dfmea ratings accept numbers and are stored as strings.\n hara failure_mode: the standardized suggestions are No function, Loss of function, Partial function,\n Degradation of function, Misinterpretation of function (a suggestion; any text is accepted).\n hara source_function: the function the row analyzes, registered as a function-flagged tag if new.\n\nReturns:\n { row_id, subtype, written_fields, computed_level, asil_propagated_to? }\n\nSide effects:\n - Saves the row with stale-write protection and logs to change_history\n - hara only: if the row is linked and its computed ASIL changed, updates each\n linked requirement's asil / asil_source (each update is history-logged)",inputSchema:{project_id:y.string().uuid().describe("UUID of the project"),row_id:y.string().min(1).max(20).describe("Analysis row ID (e.g. SG-001, FM-003)"),fields:y.record(y.string().max(64),y.union([y.string().max(5e4),y.number()])).describe("Field name to value map (see description for allowed keys per subtype)")},annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1}},ds("atoms_fill_row",async e=>{const{fillRowHandler:t}=await Promise.resolve().then(()=>(as(),Ma));return t(e)})),Xa("atoms_get_analysis",{title:"Get HARA / DFMEA Analysis",description:'Get a full analysis view: every row with its computed level (ASIL / RPN) and\ngap / critical / stale classifications, plus summary counts.\n\nClassifications match the ATOMS web app exactly (same rules, same thresholds).\n\nArgs:\n - project_id (string, UUID): Project containing the analysis\n - analysis_id (string): Analysis ID (e.g. "HARA-001", "DFMEA-002")\n\nReturns:\n { analysis: { id, title, subtype, scheme, revision, standard },\n rows: [{ id, title, source_req, effect, <subtype fields incl. source_function / hazard / assumptions for hara>,\n computed_level, is_gap, gap_rule, is_critical, is_stale, mitigated_by, link_roles,\n approved, approved_at, mcp_fields }],\n counts: { rows, gaps, critical, stale, approved, linked } }',inputSchema:{project_id:y.string().uuid().describe("UUID of the project"),analysis_id:y.string().min(1).max(20).describe("Analysis ID (e.g. HARA-001, DFMEA-002)")},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1}},ds("atoms_get_analysis",async e=>{const{getAnalysisHandler:t}=await Promise.resolve().then(()=>(as(),Ma));return t(e)})),Xa("atoms_link_mitigation",{title:"Link Analysis Row to Mitigating Requirement",description:'Link a HARA / DFMEA row to the technical requirement that mitigates it.\n\nRequires editor or admin role.\n\nArgs:\n - project_id (string, UUID): Project containing both items\n - row_id (string): Analysis row ID (e.g. "SG-001", "FM-003")\n - req_id (string): Target requirement ID (must be a plain requirement, not an analysis row)\n - role (string, dfmea only, REQUIRED for dfmea): prevention | detection | mitigation.\n Must be omitted for hara rows.\n - override (boolean, optional, default false): hara only. When the target already\n carries a different asil, the call fails with a structured error\n (details.code=\'asil_conflict\') unless override=true; the override is\n history-logged like a dialog accept.\n\nReturns:\n { row_id, req_id, type: "mitigated_by", role, asil_written? }\n\nSide effects:\n - Adds the mitigated_by/mitigates relationship (JSONB + shadow table, one transaction)\n - hara only: writes the row\'s computed ASIL onto the target requirement\n (data.asil + data.asil_source) with a history entry recording previous/new values\n - dfmea: never modifies any field on the target requirement',inputSchema:{project_id:y.string().uuid().describe("UUID of the project"),row_id:y.string().min(1).max(20).describe("Analysis row ID (e.g. SG-001, FM-003)"),req_id:y.string().min(1).max(20).describe("Target requirement ID (e.g. REQ-042)"),role:y.enum(["prevention","detection","mitigation"]).optional().describe("Mitigation role. Required for dfmea rows; must be omitted for hara rows."),override:y.boolean().optional().describe("hara only: apply the row's ASIL over a conflicting existing value (history-logged)")},annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1}},ds("atoms_link_mitigation",async e=>{const{linkMitigationHandler:t}=await Promise.resolve().then(()=>(as(),Ma));return t(e)})),Xa("atoms_get_gaps",{title:"Get Analysis Gap Rows",description:'List only the gap rows of a HARA / DFMEA analysis, each naming the rule that matched.\n\nGap rules (identical to the web app):\n hara: unmitigated row with ASIL A-D ("asil <X> unmitigated")\n dfmea: unmitigated row with RPN >= 100 ("rpn >= 100") or severity >= 9 ("severity >= 9")\n\nArgs:\n - project_id (string, UUID): Project containing the analysis\n - analysis_id (string): Analysis ID (e.g. "HARA-001", "DFMEA-002")\n\nReturns:\n { analysis_id, subtype, gaps: [{ id, title, source_req, source_function, computed_level, rule }], count }',inputSchema:{project_id:y.string().uuid().describe("UUID of the project"),analysis_id:y.string().min(1).max(20).describe("Analysis ID (e.g. HARA-001, DFMEA-002)")},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1}},ds("atoms_get_gaps",async e=>{const{getGapsHandler:t}=await Promise.resolve().then(()=>(as(),Ma));return t(e)}))}});ee(),Q(),Ge(),z();var us=S(import.meta.url)("../package.json"),_s=process.argv.slice(2)[0];(async function(){switch(_s){case"login":{const{login:e}=await Promise.resolve().then(()=>(nt(),Ye));try{const{email:t}=await e();process.stderr.write(`\n [32m✓[0m Authenticated as ${t}\n`),process.stderr.write(" [32m✓[0m Credentials saved to ~/.atoms/credentials.json\n\n"),process.stderr.write(" [1mNext steps:[0m\n"),process.stderr.write(" [36m$[0m claude mcp add atoms -- npx -y @atoms-tech/atoms-mcp\n"),process.stderr.write("\n Or add to Claude Desktop (Settings → MCP → Edit Config):\n"),process.stderr.write(' [90m{\n "mcpServers": {\n "atoms": {\n "command": "npx",\n "args": ["-y", "@atoms-tech/atoms-mcp"]\n }\n }\n }[0m\n\n')}catch(e){process.stderr.write(`\n [31m✗[0m ${e instanceof Error?e.message:String(e)}\n\n`),process.exit(1)}break}case"whoami":{const{getValidToken:e}=await Promise.resolve().then(()=>(Me(),Ae));try{const{email:t,user_id:r}=await e();process.stderr.write(`\n [32m✓[0m Logged in as ${t}\n [90mUser ID: ${r}[0m\n\n`)}catch(e){process.stderr.write(`\n [31m✗[0m Not authenticated: ${e instanceof Error?e.message:String(e)}\n Run: npx @atoms-tech/atoms-mcp login\n\n`),process.exit(1)}break}case"logout":{const{clearCredentials:e}=await Promise.resolve().then(()=>(fe(),te));await e(),process.stderr.write("Logged out. Credentials removed.\n"),process.stderr.write("Run 'npx @atoms-tech/atoms-mcp login' to authenticate again.\n");break}case"--version":case"-v":process.stderr.write(`@atoms-tech/atoms-mcp v${us.version}\n`);break;case"--help":case"-h":process.stderr.write("ATOMS MCP Server — AI agent integration for requirements management\n\nUsage:\n npx @atoms-tech/atoms-mcp Start MCP server (stdio)\n npx @atoms-tech/atoms-mcp login Authenticate with ATOMS.tech\n npx @atoms-tech/atoms-mcp logout Switch account / clear credentials\n npx @atoms-tech/atoms-mcp whoami Show current user\n npx @atoms-tech/atoms-mcp --version Show version\n");break;default:{process.stderr.write("[atoms-mcp] Starting MCP server via stdio...\n");{const{getValidToken:e,readValidToken:t}=await Promise.resolve().then(()=>(Me(),Ae)),{runExclusiveAuth:r}=await Promise.resolve().then(()=>(xe(),he)),{login:i}=await Promise.resolve().then(()=>(nt(),Ye));try{let n;try{n=(await e()).email}catch(e){process.stderr.write(`[atoms-mcp] Could not use the saved session: ${e instanceof Error?e.message:String(e)}\n`),process.stderr.write("[atoms-mcp] Starting login flow...\n");const a=async()=>{const e=await t();if(!e)throw new Error("no valid token yet");return{email:e.email}};n=(await r(a,async()=>({email:(await i()).email}))).email}process.stderr.write(`[atoms-mcp] Authenticated as ${n}\n`)}catch(e){process.stderr.write(`[atoms-mcp] Login failed: ${e instanceof Error?e.message:String(e)}\n[atoms-mcp] Starting server without auth. Tools will fail until you run: npx @atoms-tech/atoms-mcp login\n`)}}try{const t=await $e(),r=We(),i=await async function(e,t){const r=process.env.ATOMS_MCP_ORG_ID?.trim();if(r)return r;const{data:i,error:n}=await e.from("org_members").select("org_id, joined_at").eq("user_id",t).order("joined_at",{ascending:!0}).limit(1).maybeSingle();return n?(process.stderr.write(`[atoms-mcp] Could not resolve session org: ${n.message}\n`),null):i?.org_id??null}(t,r),n=await async function(e,t,r){if(!r)return{role:null,is_member:!1,effective:L};const{data:i,error:n}=await e.rpc("mcp_resolve_policy",{p_user_id:t,p_org_id:r});return n?(process.stderr.write(`[atoms-mcp] mcp_resolve_policy failed (${n.code}): ${n.message}. Falling back to fail-closed policy.\n`),{role:null,is_member:!1,effective:F}):function(e){if(!e||"object"!=typeof e)return{role:null,is_member:!1,effective:L};const t=e,r="string"==typeof t.role?t.role:null,i=!0===t.is_member,n=t.effective??{},a=(e,t)=>Array.isArray(e)?e.filter(e=>"string"==typeof e):t,s=(e,t)=>"boolean"==typeof e?e:t,o=n.project_scope,d=Array.isArray(o)&&o.length>0?o.filter(e=>"string"==typeof e):null;return{role:r,is_member:i,effective:{read_only:s(n.read_only,L.read_only),lockdown:s(n.lockdown,L.lockdown),block_destructive:s(n.block_destructive,L.block_destructive),block_bulk_import:s(n.block_bulk_import,L.block_bulk_import),cross_project_browse:s(n.cross_project_browse,L.cross_project_browse),allowed_toolsets:a(n.allowed_toolsets,L.allowed_toolsets),forbidden_tools:a(n.forbidden_tools,L.forbidden_tools),project_scope:d,require_confirmation_for:a(n.require_confirmation_for,L.require_confirmation_for)}}}(i)}(t,r,i);e={effective:n.effective,role:n.role,org_id:i},W=e.effective,V=e.org_id,n.is_member&&i?process.stderr.write(`[atoms-mcp] Org policy loaded: org=${i.slice(0,8)}… role=${n.role??"?"} read_only=${n.effective.read_only} lockdown=${n.effective.lockdown}\n`):process.stderr.write("[atoms-mcp] No org membership detected — running with permissive default policy.\n")}catch(e){process.stderr.write(`[atoms-mcp] Could not resolve session policy (${e instanceof Error?e.message:String(e)}). Running with permissive default — RLS still enforces row-level access.\n`)}process.stderr.write(`[atoms-mcp] Tool registry: ${function(){const e=Y(),t=G(),r=[];r.push(e?"read-only":"read+write"),null===t?r.push("all toolsets"):(r.push(`toolsets=${[...t.allowed].sort().join(",")||"(none — only always-on tools)"}`),t.unknown.length>0&&r.push(`unknown=${t.unknown.join(",")}`));const i=process.env.ATOMS_MCP_PROJECT_ID?.trim();return i&&r.push(`project_scope=${i.slice(0,8)}…`),("1"===process.env.ATOMS_MCP_LOCKDOWN||"true"===process.env.ATOMS_MCP_LOCKDOWN)&&r.push("lockdown=on"),r.join(" | ")}()}\n`);const{server:t}=await Promise.resolve().then(()=>(ps(),ss)),r=new x;await t.connect(r),process.stderr.write("[atoms-mcp] MCP server running. Waiting for tool calls...\n")}}var e})().catch(e=>{process.stderr.write(`[atoms-mcp] Fatal: ${e instanceof Error?e.message:String(e)}\n`),process.exit(1)});
|