@magic5644/graph-it-live 1.14.4 → 1.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -3338,7 +3338,7 @@ ${" ".repeat(t)}}`;default:return"any"}}function $O(e){let t=["namespace functio
3338
3338
  ${JSON.stringify(i,null,2)}
3339
3339
  \`\`\``:o=JSON.stringify(i,null,2);return{content:[{type:"text",text:o}],structuredContent:i}}function tz(e,t){let n=e.data,a=[];e.success&&mv(n)?(a.push(Yt([{indexRevision:n.indexRevision,fresh:n.fresh,mode:n.mode,tokenEstimate:n.tokenEstimate,truncated:n.truncated,nextCursor:n.nextCursor??""}],{objectName:"graph_context"})),a.push(Yt(n.nodes,{objectName:"nodes"})),a.push(Yt(n.edges,{objectName:"edges"})),a.push(Yt(n.paths,{objectName:"paths"})),a.push(Yt(n.ambiguous,{objectName:"ambiguous"})),a.push(Yt([n.omitted],{objectName:"omitted"})),a.push(Yt(n.nextQueries.map(s=>({query:s})),{objectName:"nextQueries"})),a.push(Yt([],{objectName:"errors"}))):a.push(Yt([{message:e.error??"Unknown graph-context error"}],{objectName:"errors"}));let r=a.map(s=>s.trimEnd()).join(`
3340
3340
  `),i=JSON.stringify(e.success?n:e,null,2),o=uv(i,r);return en.record({toolName:t,jsonTokens:o.jsonTokens,toonTokens:o.toonTokens,savings:o.savings,truncated:mv(n)&&n.truncated,timestamp:Date.now()}),{content:r,format:"toon",tokenSavings:o}}function mv(e){if(typeof e!="object"||e===null)return!1;let t=e;return typeof t.indexRevision=="string"&&typeof t.mode=="string"&&Array.isArray(t.seeds)&&Array.isArray(t.nodes)&&Array.isArray(t.edges)&&Array.isArray(t.paths)&&Array.isArray(t.ambiguous)&&typeof t.omitted=="object"&&t.omitted!==null&&Array.isArray(t.nextQueries)}function rz(e,t){let n=_s(t),a=r=>{if(typeof r=="string"){let i=_s(r);return n&&i===n?".":n&&i.startsWith(`${n}/`)?i.slice(n.length+1):zI.isAbsolute(r)?`[external:${zI.basename(r)}]`:r}return Array.isArray(r)?r.map(a):typeof r!="object"||r===null?r:Object.fromEntries(Object.entries(r).map(([i,o])=>[i,a(o)]))};return a(e)}function nz(e){if(Array.isArray(e))return e;if(typeof e=="object"&&e!==null){let t=e,n=["items","results","data","nodes","edges","dependencies","symbols","callers"];for(let a of n)if(Array.isArray(t[a]))return t[a];return[e]}return[{value:e}]}function iz(e,t="data",n){if(e==null)return{content:`${t}()
3341
- `,format:"toon"};let a=nz(e);try{let r=Yt(a,{objectName:t}),i=JSON.stringify(e,null,2),o=uv(i,r);return en.record({toolName:n??"unknown",jsonTokens:o.jsonTokens,toonTokens:o.toonTokens,savings:o.savings,truncated:!1,timestamp:Date.now()}),{content:r,format:"toon",tokenSavings:o}}catch(r){return ez.error("[formatDataAsToon] TOON conversion failed:",r),{content:JSON.stringify(e,null,2),format:"json"}}}function az(e){if(!Array.isArray(e)||e.length===0)return"data";let t=e[0];if(typeof t!="object"||t===null)return"data";let n=Object.keys(t);return n.includes("file")||n.includes("filePath")?"files":n.includes("symbolName")||n.includes("symbol")?"symbols":n.includes("source")&&n.includes("target")?"edges":n.includes("node")||n.includes("id")?"nodes":n.includes("dependency")||n.includes("dependencies")?"dependencies":n.includes("caller")||n.includes("callers")?"callers":"data"}function oz(e){return az(e.data)}import*as fv from"node:path";ia();ia();import*as aa from"node:fs";import*as nr from"node:path";function sz(e){let t=e;for(;!aa.existsSync(t);){let n=nr.dirname(t);if(n===t)return e;t=n}return aa.realpathSync.native(t)}function gv(e,t){let n=na(nr.resolve(t,e)),a=na(nr.resolve(t));return n===a||n.startsWith(`${a}/`)}function DI(e,t){if(!gv(e,t))return!1;let n=nr.resolve(t);if(!aa.existsSync(n))return!0;let a=nr.resolve(t,e),r=na(aa.realpathSync.native(n)),i=na(sz(a));return i===r||i.startsWith(`${r}/`)}function MI(e,t){if(e.includes("\0"))throw new Error("Path contains null bytes");if(!gv(e,t))throw new Error(`File path is outside workspace: ${e}`);if(!DI(e,t))throw new Error(`File path escapes workspace through a symbolic link: ${e}`)}ia();var sa="1.1.0",fF=G.enum(["json","toon"]).default("json").describe("Output format: 'json' (default) or 'toon' (Token-Oriented Object Notation for reduced token usage)"),tn={FILE_PATH:1024,SYMBOL_NAME:500,FILE_CONTENT:1024*1024,GENERIC_STRING:10*1024},je=G.string().max(tn.FILE_PATH,`File path exceeds maximum length of ${tn.FILE_PATH} bytes`).refine(e=>!e.includes("\0"),"File path contains null bytes"),Mn=G.string().max(tn.SYMBOL_NAME,`Symbol name exceeds maximum length of ${tn.SYMBOL_NAME} bytes`).refine(e=>!e.includes("\0"),"Symbol name contains null bytes"),LI=G.string().max(tn.FILE_CONTENT,`File content exceeds maximum size of ${tn.FILE_CONTENT} bytes (~1 MB)`).refine(e=>!e.includes("\0"),"File content contains null bytes"),oa=G.string().max(tn.GENERIC_STRING,`String exceeds maximum length of ${tn.GENERIC_STRING} bytes`).refine(e=>!e.includes("\0"),"String contains null bytes"),dp={MAX_LIMIT:1e4,MAX_OFFSET:1e5,DEFAULT_LIMIT:1e3},lz=G.object({hubScore:G.number().min(0,"hubScore must be >= 0").max(1,"hubScore must be <= 1"),loc:G.number().int().positive().optional(),fileExtension:G.string().optional(),communityId:G.number().int().min(0).optional()}),cz=G.object({source:G.string(),target:G.string(),relationType:G.enum(["dependency","call","reference"]).optional()}),yF=G.object({nodes:G.array(G.string()),edges:G.array(cz),nodeLabels:G.record(G.string(),G.string()).optional(),parentCounts:G.record(G.string(),G.number()).optional(),unusedEdges:G.array(G.string()).optional(),nodeMetadata:G.record(G.string(),lz).optional()}),jI=G.object({workspacePath:je.describe("Absolute path to the project/workspace directory to analyze"),tsConfigPath:je.optional().describe("Optional path to tsconfig.json for path alias resolution"),excludeNodeModules:G.boolean().optional().describe("Whether to exclude node_modules (default: true)"),maxDepth:G.number().min(1).max(100).optional().describe("Maximum crawl depth (default: 50, min: 1, max: 100)")}),UI=G.object({filePath:je.describe("Absolute path to the file to analyze")}),FI=G.object({entryFile:je.describe("Absolute path to the entry file"),maxDepth:G.number().min(1).max(100).optional().describe("Maximum depth to crawl (default: from config, min: 1, max: 100)"),limit:G.number().min(1).max(dp.MAX_LIMIT).optional().describe(`Maximum number of nodes to return (for pagination, max: ${dp.MAX_LIMIT})`),offset:G.number().min(0).max(dp.MAX_OFFSET).optional().describe(`Number of nodes to skip (for pagination, max: ${dp.MAX_OFFSET})`),onlyUsed:G.boolean().optional().describe("If true, only returns edges where symbols are actually used (requires AST analysis, slower)")}),BI=G.object({targetPath:je.describe("Absolute path to the file to find references for")}),qI=G.object({filePath:je.describe("Absolute path to the node to expand"),knownPaths:G.array(je).max(5e4).describe("Array of already known file paths to exclude (max: 50000)"),extraDepth:G.number().min(1).max(100).optional().describe("Additional depth to scan from this node (default: 10, min: 1, max: 100)")}),HI=G.object({filePath:je.describe("Absolute path to the file to parse")}),GI=G.object({sourceFile:je.describe("Absolute path to the source file (the one importing using symbols)"),targetFile:je.describe("Absolute path to the target file (the one providing symbols)")}),VI=G.object({fromFile:je.describe("Absolute path of the file containing the import"),moduleSpecifier:oa.describe('The module specifier to resolve (e.g., "./utils", "@/components/Button")')}),bF=G.object({}),WI=G.object({filePaths:G.array(je).max(1e4).describe("Array of absolute file paths to invalidate from the cache (max: 10000). Use this after modifying files to ensure fresh analysis.")}),vF=G.object({}),KI=G.object({filePath:je.describe("Absolute path to the file to analyze for symbols")}),JI=G.object({filePath:je.describe("Absolute path to the file to check for unused exported symbols")}),ZI=G.object({filePath:je.describe("Absolute path to the file containing the symbol"),symbolName:Mn.describe("Name of the symbol to find dependents for")}),YI=G.object({filePath:je.describe("Absolute path to the file containing the root symbol"),symbolName:Mn.describe("Name of the root symbol to trace from"),maxDepth:G.number().min(1).max(100).optional().describe("Maximum depth to trace the call chain (default: 10, min: 1, max: 100)")}),XI=G.object({filePath:je.describe("The absolute path to the file containing the target symbol."),symbolName:Mn.describe("The name of the symbol (function, class, method, variable) to find callers for."),includeTypeOnly:G.boolean().optional().describe("Include type-only usages (interfaces, type aliases). Default is true.")}),QI=G.object({filePath:je.describe("Absolute path to the file to analyze"),symbolName:Mn.optional().describe("Optional: Only analyze changes to this specific symbol"),oldContent:LI.describe("The old version of the file content"),newContent:LI.optional().describe("The new version of the file content (if not provided, reads current file)")}),eT=G.object({baseRef:oa.describe("Git base ref to compare against, for example origin/main"),headRef:oa.optional().describe("Optional Git head ref; defaults to the checked-out worktree"),maxFiles:G.number().int().min(1).max(1e3).optional(),maxDepth:G.number().int().min(1).max(10).optional()}),tT=G.object({filePath:je.describe("Absolute path to the file being modified"),symbolName:Mn.describe("Name of the symbol being modified"),includeTransitive:G.boolean().optional().describe("Include transitive dependents (default: false)"),maxDepth:G.number().min(1).max(20).optional().describe("Maximum depth for transitive analysis (default: 3, min: 1, max: 20)")}),_F=G.object({filePath:je.describe("Absolute path to the file to analyze"),includeExternal:G.boolean().optional().describe("Include calls to external modules (default: false)")}),SF=G.object({filePath:je.describe("Absolute path to the file to generate a codemap for")}),rT=G.object({filePath:je.describe("Absolute path to the file containing the target symbol."),symbolName:Mn.describe("Name of the symbol to query (function, class, method)."),direction:G.enum(["callers","callees","both"]).optional().describe("Direction of traversal: 'callers', 'callees', or 'both' (default: 'both')"),depth:G.number().min(1).max(10).optional().describe("Maximum BFS traversal depth (default: 2, min: 1, max: 10)"),relationTypes:G.array(G.enum(["CALLS","INHERITS","IMPLEMENTS","USES"])).optional().describe("Filter by relation types (default: all)")}),nT=G.object({question:G.string().max(1024).describe("Natural language question about the codebase"),depth:G.number().int().min(1).max(5).default(2).optional().describe("BFS traversal depth from seed nodes (default: 2, min: 1, max: 5)"),tokenBudget:G.number().int().min(500).max(16e3).default(4e3).optional().describe("Maximum token budget for the output subgraph (default: 4000)"),fileFilter:G.string().max(256).optional().describe("Glob pattern to restrict search scope"),outputFormat:G.enum(["toon","json"]).default("toon").optional().describe("Output format. 'toon' (default): compact TOON encoding (header+rows), typically 30-50% fewer tokens than JSON for node/edge lists \u2014 prefer this unless you need to re-parse structured JSON programmatically. 'json': short-key JSON object per node/edge \u2014 use when the calling client needs to JSON.parse() the result directly (e.g. building its own graph structure).")}),hv=G.object({id:oa.refine(e=>e.trim().length>0,"Seed ID must not be empty").optional(),filePath:je.refine(e=>e.trim().length>0,"Seed file path must not be empty").optional(),symbolName:Mn.refine(e=>e.trim().length>0,"Seed symbol name must not be empty").optional(),label:oa.refine(e=>e.trim().length>0,"Seed label must not be empty").optional()}).refine(e=>e.id!==void 0||e.filePath!==void 0||e.symbolName!==void 0||e.label!==void 0,"A seed must contain an id, filePath, symbolName, or label"),uz=G.enum(["CONTAINS","IMPORTS","CALLS","INHERITS","IMPLEMENTS","USES","TESTED_BY","IMPACTED_BY","BELONGS_TO","REFERENCES","EXPLAINS","DOCUMENTS"]),iT=G.object({question:G.string().max(1024).optional(),seeds:G.array(hv).max(500).optional(),mode:G.enum(["search","neighbors","path","impact","refactor","overview"]).optional(),from:hv.optional(),to:hv.optional(),relations:G.array(uz).max(12).refine(e=>new Set(e).size===e.length,"Relations must be unique").optional(),scope:G.string().max(256).optional(),depth:G.number().int().min(1).max(5).optional(),maxNodes:G.number().int().min(1).max(500).optional(),tokenBudget:G.number().int().min(500).max(16e3).optional(),directed:G.boolean().optional(),cursor:G.string().min(1).max(4096).optional(),detail:G.enum(["compact","standard","full"]).optional(),response_format:G.enum(["json","markdown","toon"]).optional()}).superRefine((e,t)=>{let n=e.question!==void 0&&e.question.trim().length>0,a=(e.seeds?.length??0)>0,r=e.from!==void 0,i=e.to!==void 0;r!==i&&t.addIssue({code:"custom",message:"Both from and to are required for a path request"}),e.mode==="path"&&!(r&&i)&&t.addIssue({code:"custom",message:"Path mode requires both from and to"}),e.mode!==void 0&&e.mode!=="path"&&(r||i)&&t.addIssue({code:"custom",message:"from and to are only valid in path mode"}),e.mode===void 0&&r&&i&&(n||a)&&t.addIssue({code:"custom",message:"Set mode to path when combining endpoints with a question or seeds"}),!n&&!a&&!(r&&i)&&t.addIssue({code:"custom",message:"Provide a question, at least one seed, or both from and to"})}).transform(e=>{let t=e.question!==void 0&&e.question.trim().length>0,n=(e.seeds?.length??0)>0,a=e.from!==void 0&&e.to!==void 0;return e.mode===void 0&&!t&&!n&&a?{...e,mode:"path"}:e}),kF=G.object({outputDir:je.optional().refine(e=>e===void 0||!fv.isAbsolute(e),"Output directory must be relative to the workspace").describe("Workspace-relative output directory for wiki files (default: wiki)"),topHubsLimit:G.number().int().min(1).max(50).optional().describe("Number of top hub files to list (max: 50, default: 10)"),scope:je.optional().refine(e=>e===void 0||!fv.isAbsolute(e),"Scope must be relative to the workspace").describe("Relative path within workspace to restrict wiki to (e.g. 'src/')"),exclude:G.array(oa).max(100).optional().describe("Relative glob-like patterns to exclude")}).strict(),aT=G.object({scopePath:je.optional().describe("Absolute path to a directory to scope the scan (default: workspace root). Must be inside the workspace."),maxFiles:G.number().int().min(1).max(1e4).default(500).optional().describe("Maximum number of files to analyse (default: 500). Prevents OOM on very large monorepos.")});function oT(e,t){return MI(e,t),!0}function pp(e,t,n,a,r){return{success:!0,data:e,metadata:{executionTimeMs:t,toolVersion:sa,timestamp:new Date().toISOString(),workspaceRoot:n,...r&&{indexedAt:r.indexedAt,stale:r.stale}},...a&&{pagination:a}}}function Ss(e,t,n){return{success:!1,data:null,error:e,metadata:{executionTimeMs:t,toolVersion:sa,timestamp:new Date().toISOString(),workspaceRoot:n}}}sT();ia();var lT=G.object({workspaceRoot:G.string().optional().describe("Absolute path to workspace root. Defaults to the configured workspace."),outputDir:G.string().optional().describe("Absolute path to wiki output directory. Defaults to <workspaceRoot>/wiki."),topHubsLimit:G.number().int().min(1).max(50).optional().describe("Number of top hub files to include (default: 10, max: 50)."),scope:G.string().optional().describe("Relative path within workspace to restrict wiki to (e.g. 'src/'). Defaults to entire workspace."),exclude:G.array(G.string()).optional().describe("Relative glob-like patterns to exclude (e.g. ['tests/**', '**/*.test.ts']). When omitted, default exclusions apply: tests/, dist/, *.test.ts, etc.")});import*as Tt from"fs";import*as uT from"os";import*as la from"path";var cT=50;function bv(){return la.join(uT.homedir(),".graph-it","stats")}function pz(){return process.env.GRAPH_IT_NO_STATS==="1"}function mz(e){Tt.mkdirSync(e,{recursive:!0,mode:448})}function gz(e){return e.totals.calls>0||e.llmUsage.calls>0}function dT(e,t){if(pz()||!gz(e))return;let n=t??bv();mz(n);let a=`${e.source}-${e.sessionId}.json`,r=la.join(n,a),i=la.join(n,`.${a}.${process.pid}.tmp`);Tt.writeFileSync(i,JSON.stringify(e,null,2),"utf8"),Tt.renameSync(i,r),hz(n)}function pT(e){let t=e??bv(),n;try{n=Tt.readdirSync(t)}catch{return[]}let a=[];for(let r of n){if(!r.endsWith(".json"))continue;let i=la.join(t,r);try{let o=Tt.readFileSync(i,"utf8"),s=JSON.parse(o);s&&typeof s=="object"&&s.schemaVersion===1?a.push(s):console.warn(`graph-it stats: skipping invalid stats file: ${r}`)}catch{console.warn(`graph-it stats: skipping corrupted stats file: ${r}`)}}return a}function hz(e){let t=e??bv(),n;try{n=Tt.readdirSync(t)}catch{return}let a=new Map;for(let r of n){if(!r.endsWith(".json"))continue;let i=r.indexOf("-");if(i<=0)continue;let o=r.slice(0,i),s=la.join(t,r);try{let l=Tt.statSync(s),c=a.get(o);c||(c=[],a.set(o,c)),c.push({filePath:s,mtimeMs:l.mtimeMs})}catch{}}for(let r of a.values()){if(r.length<=cT)continue;r.sort((o,s)=>o.mtimeMs-s.mtimeMs);let i=r.length-cT;for(let o=0;o<i;o++)try{Tt.unlinkSync(r[o].filePath)}catch{}}}var mT=G.object({});function fz(){return{sessions:0,calls:0,jsonTokens:0,toonTokens:0,savings:0,truncations:0,llmTokensUsed:0,llmCalls:0}}function yz(e){let t={};for(let n of e){let a=t[n.source]??=fz();a.sessions+=1,a.calls+=n.totals.calls,a.jsonTokens+=n.totals.jsonTokens,a.toonTokens+=n.totals.toonTokens,a.savings+=n.totals.savings,a.truncations+=n.totals.truncations,a.llmTokensUsed+=n.llmUsage.tokensUsed,a.llmCalls+=n.llmUsage.calls}return{persistedSessions:e.length,bySource:t}}function gT(e){let t=en.snapshot(),n={persistedSessions:0,bySource:{}};try{n=yz(pT(e))}catch{}return{description:"TOON encoding size vs JSON equivalent",estimationNote:"estimated (chars/4 heuristic)",llmUsageNote:t.source==="mcp"&&t.llmUsage.calls===0?"llmUsage is tracked for CLI `graph-it query` only \u2014 the MCP server delegates LLM synthesis to the calling client and makes no LLM API calls itself":"llmUsage is real provider-reported token consumption; it is never summed into the estimated encoding totals",currentSession:t,history:n}}Pv({createLogger(e,t){return new Bs(e,t)}});var wv=process.env.DEBUG_MCP==="true",gp=`${fT.homedir()}/mcp-debug.log`,bz=5*1024*1024,vv=`${gp}.1`;function vz(){if(wv)try{Rt.statSync(gp).size>=bz&&(Rt.existsSync(vv)&&Rt.unlinkSync(vv),Rt.renameSync(gp,vv))}catch{}}function Se(...e){let t=e.map(n=>typeof n=="string"?n:JSON.stringify(n)).join(" ");if(process.stderr.write(t.endsWith(`
3341
+ `,format:"toon"};let a=nz(e);try{let r=Yt(a,{objectName:t}),i=JSON.stringify(e,null,2),o=uv(i,r);return en.record({toolName:n??"unknown",jsonTokens:o.jsonTokens,toonTokens:o.toonTokens,savings:o.savings,truncated:!1,timestamp:Date.now()}),{content:r,format:"toon",tokenSavings:o}}catch(r){return ez.error("[formatDataAsToon] TOON conversion failed:",r),{content:JSON.stringify(e,null,2),format:"json"}}}function az(e){if(!Array.isArray(e)||e.length===0)return"data";let t=e[0];if(typeof t!="object"||t===null)return"data";let n=Object.keys(t);return n.includes("file")||n.includes("filePath")?"files":n.includes("symbolName")||n.includes("symbol")?"symbols":n.includes("source")&&n.includes("target")?"edges":n.includes("node")||n.includes("id")?"nodes":n.includes("dependency")||n.includes("dependencies")?"dependencies":n.includes("caller")||n.includes("callers")?"callers":"data"}function oz(e){return az(e.data)}import*as fv from"node:path";ia();ia();import*as aa from"node:fs";import*as nr from"node:path";function sz(e){let t=e;for(;!aa.existsSync(t);){let n=nr.dirname(t);if(n===t)return e;t=n}return aa.realpathSync.native(t)}function gv(e,t){let n=na(nr.resolve(t,e)),a=na(nr.resolve(t));return n===a||n.startsWith(`${a}/`)}function DI(e,t){if(!gv(e,t))return!1;let n=nr.resolve(t);if(!aa.existsSync(n))return!0;let a=nr.resolve(t,e),r=na(aa.realpathSync.native(n)),i=na(sz(a));return i===r||i.startsWith(`${r}/`)}function MI(e,t){if(e.includes("\0"))throw new Error("Path contains null bytes");if(!gv(e,t))throw new Error(`File path is outside workspace: ${e}`);if(!DI(e,t))throw new Error(`File path escapes workspace through a symbolic link: ${e}`)}ia();var sa="1.1.0",fF=G.enum(["json","toon"]).default("json").describe("Output format: 'json' (default) or 'toon' (Token-Oriented Object Notation for reduced token usage)"),tn={FILE_PATH:1024,SYMBOL_NAME:500,FILE_CONTENT:1024*1024,GENERIC_STRING:10*1024},je=G.string().max(tn.FILE_PATH,`File path exceeds maximum length of ${tn.FILE_PATH} bytes`).refine(e=>!e.includes("\0"),"File path contains null bytes"),Mn=G.string().max(tn.SYMBOL_NAME,`Symbol name exceeds maximum length of ${tn.SYMBOL_NAME} bytes`).refine(e=>!e.includes("\0"),"Symbol name contains null bytes"),LI=G.string().max(tn.FILE_CONTENT,`File content exceeds maximum size of ${tn.FILE_CONTENT} bytes (~1 MB)`).refine(e=>!e.includes("\0"),"File content contains null bytes"),oa=G.string().max(tn.GENERIC_STRING,`String exceeds maximum length of ${tn.GENERIC_STRING} bytes`).refine(e=>!e.includes("\0"),"String contains null bytes"),dp={MAX_LIMIT:1e4,MAX_OFFSET:1e5,DEFAULT_LIMIT:1e3},lz=G.object({hubScore:G.number().min(0,"hubScore must be >= 0").max(1,"hubScore must be <= 1"),loc:G.number().int().positive().optional(),fileExtension:G.string().optional(),communityId:G.number().int().min(0).optional(),communityKey:G.string().optional()}),cz=G.object({source:G.string(),target:G.string(),relationType:G.enum(["dependency","call","reference"]).optional()}),yF=G.object({nodes:G.array(G.string()),edges:G.array(cz),nodeLabels:G.record(G.string(),G.string()).optional(),parentCounts:G.record(G.string(),G.number()).optional(),unusedEdges:G.array(G.string()).optional(),nodeMetadata:G.record(G.string(),lz).optional()}),jI=G.object({workspacePath:je.describe("Absolute path to the project/workspace directory to analyze"),tsConfigPath:je.optional().describe("Optional path to tsconfig.json for path alias resolution"),excludeNodeModules:G.boolean().optional().describe("Whether to exclude node_modules (default: true)"),maxDepth:G.number().min(1).max(100).optional().describe("Maximum crawl depth (default: 50, min: 1, max: 100)")}),UI=G.object({filePath:je.describe("Absolute path to the file to analyze")}),FI=G.object({entryFile:je.describe("Absolute path to the entry file"),maxDepth:G.number().min(1).max(100).optional().describe("Maximum depth to crawl (default: from config, min: 1, max: 100)"),limit:G.number().min(1).max(dp.MAX_LIMIT).optional().describe(`Maximum number of nodes to return (for pagination, max: ${dp.MAX_LIMIT})`),offset:G.number().min(0).max(dp.MAX_OFFSET).optional().describe(`Number of nodes to skip (for pagination, max: ${dp.MAX_OFFSET})`),onlyUsed:G.boolean().optional().describe("If true, only returns edges where symbols are actually used (requires AST analysis, slower)")}),BI=G.object({targetPath:je.describe("Absolute path to the file to find references for")}),qI=G.object({filePath:je.describe("Absolute path to the node to expand"),knownPaths:G.array(je).max(5e4).describe("Array of already known file paths to exclude (max: 50000)"),extraDepth:G.number().min(1).max(100).optional().describe("Additional depth to scan from this node (default: 10, min: 1, max: 100)")}),HI=G.object({filePath:je.describe("Absolute path to the file to parse")}),GI=G.object({sourceFile:je.describe("Absolute path to the source file (the one importing using symbols)"),targetFile:je.describe("Absolute path to the target file (the one providing symbols)")}),VI=G.object({fromFile:je.describe("Absolute path of the file containing the import"),moduleSpecifier:oa.describe('The module specifier to resolve (e.g., "./utils", "@/components/Button")')}),bF=G.object({}),WI=G.object({filePaths:G.array(je).max(1e4).describe("Array of absolute file paths to invalidate from the cache (max: 10000). Use this after modifying files to ensure fresh analysis.")}),vF=G.object({}),KI=G.object({filePath:je.describe("Absolute path to the file to analyze for symbols")}),JI=G.object({filePath:je.describe("Absolute path to the file to check for unused exported symbols")}),ZI=G.object({filePath:je.describe("Absolute path to the file containing the symbol"),symbolName:Mn.describe("Name of the symbol to find dependents for")}),YI=G.object({filePath:je.describe("Absolute path to the file containing the root symbol"),symbolName:Mn.describe("Name of the root symbol to trace from"),maxDepth:G.number().min(1).max(100).optional().describe("Maximum depth to trace the call chain (default: 10, min: 1, max: 100)")}),XI=G.object({filePath:je.describe("The absolute path to the file containing the target symbol."),symbolName:Mn.describe("The name of the symbol (function, class, method, variable) to find callers for."),includeTypeOnly:G.boolean().optional().describe("Include type-only usages (interfaces, type aliases). Default is true.")}),QI=G.object({filePath:je.describe("Absolute path to the file to analyze"),symbolName:Mn.optional().describe("Optional: Only analyze changes to this specific symbol"),oldContent:LI.describe("The old version of the file content"),newContent:LI.optional().describe("The new version of the file content (if not provided, reads current file)")}),eT=G.object({baseRef:oa.describe("Git base ref to compare against, for example origin/main"),headRef:oa.optional().describe("Optional Git head ref; defaults to the checked-out worktree"),maxFiles:G.number().int().min(1).max(1e3).optional(),maxDepth:G.number().int().min(1).max(10).optional()}),tT=G.object({filePath:je.describe("Absolute path to the file being modified"),symbolName:Mn.describe("Name of the symbol being modified"),includeTransitive:G.boolean().optional().describe("Include transitive dependents (default: false)"),maxDepth:G.number().min(1).max(20).optional().describe("Maximum depth for transitive analysis (default: 3, min: 1, max: 20)")}),_F=G.object({filePath:je.describe("Absolute path to the file to analyze"),includeExternal:G.boolean().optional().describe("Include calls to external modules (default: false)")}),SF=G.object({filePath:je.describe("Absolute path to the file to generate a codemap for")}),rT=G.object({filePath:je.describe("Absolute path to the file containing the target symbol."),symbolName:Mn.describe("Name of the symbol to query (function, class, method)."),direction:G.enum(["callers","callees","both"]).optional().describe("Direction of traversal: 'callers', 'callees', or 'both' (default: 'both')"),depth:G.number().min(1).max(10).optional().describe("Maximum BFS traversal depth (default: 2, min: 1, max: 10)"),relationTypes:G.array(G.enum(["CALLS","INHERITS","IMPLEMENTS","USES"])).optional().describe("Filter by relation types (default: all)")}),nT=G.object({question:G.string().max(1024).describe("Natural language question about the codebase"),depth:G.number().int().min(1).max(5).default(2).optional().describe("BFS traversal depth from seed nodes (default: 2, min: 1, max: 5)"),tokenBudget:G.number().int().min(500).max(16e3).default(4e3).optional().describe("Maximum token budget for the output subgraph (default: 4000)"),fileFilter:G.string().max(256).optional().describe("Glob pattern to restrict search scope"),outputFormat:G.enum(["toon","json"]).default("toon").optional().describe("Output format. 'toon' (default): compact TOON encoding (header+rows), typically 30-50% fewer tokens than JSON for node/edge lists \u2014 prefer this unless you need to re-parse structured JSON programmatically. 'json': short-key JSON object per node/edge \u2014 use when the calling client needs to JSON.parse() the result directly (e.g. building its own graph structure).")}),hv=G.object({id:oa.refine(e=>e.trim().length>0,"Seed ID must not be empty").optional(),filePath:je.refine(e=>e.trim().length>0,"Seed file path must not be empty").optional(),symbolName:Mn.refine(e=>e.trim().length>0,"Seed symbol name must not be empty").optional(),label:oa.refine(e=>e.trim().length>0,"Seed label must not be empty").optional()}).refine(e=>e.id!==void 0||e.filePath!==void 0||e.symbolName!==void 0||e.label!==void 0,"A seed must contain an id, filePath, symbolName, or label"),uz=G.enum(["CONTAINS","IMPORTS","CALLS","INHERITS","IMPLEMENTS","USES","TESTED_BY","IMPACTED_BY","BELONGS_TO","REFERENCES","EXPLAINS","DOCUMENTS"]),iT=G.object({question:G.string().max(1024).optional(),seeds:G.array(hv).max(500).optional(),mode:G.enum(["search","neighbors","path","impact","refactor","overview"]).optional(),from:hv.optional(),to:hv.optional(),relations:G.array(uz).max(12).refine(e=>new Set(e).size===e.length,"Relations must be unique").optional(),scope:G.string().max(256).optional(),depth:G.number().int().min(1).max(5).optional(),maxNodes:G.number().int().min(1).max(500).optional(),tokenBudget:G.number().int().min(500).max(16e3).optional(),directed:G.boolean().optional(),cursor:G.string().min(1).max(4096).optional(),detail:G.enum(["compact","standard","full"]).optional(),response_format:G.enum(["json","markdown","toon"]).optional()}).superRefine((e,t)=>{let n=e.question!==void 0&&e.question.trim().length>0,a=(e.seeds?.length??0)>0,r=e.from!==void 0,i=e.to!==void 0;r!==i&&t.addIssue({code:"custom",message:"Both from and to are required for a path request"}),e.mode==="path"&&!(r&&i)&&t.addIssue({code:"custom",message:"Path mode requires both from and to"}),e.mode!==void 0&&e.mode!=="path"&&(r||i)&&t.addIssue({code:"custom",message:"from and to are only valid in path mode"}),e.mode===void 0&&r&&i&&(n||a)&&t.addIssue({code:"custom",message:"Set mode to path when combining endpoints with a question or seeds"}),!n&&!a&&!(r&&i)&&t.addIssue({code:"custom",message:"Provide a question, at least one seed, or both from and to"})}).transform(e=>{let t=e.question!==void 0&&e.question.trim().length>0,n=(e.seeds?.length??0)>0,a=e.from!==void 0&&e.to!==void 0;return e.mode===void 0&&!t&&!n&&a?{...e,mode:"path"}:e}),kF=G.object({outputDir:je.optional().refine(e=>e===void 0||!fv.isAbsolute(e),"Output directory must be relative to the workspace").describe("Workspace-relative output directory for wiki files (default: wiki)"),topHubsLimit:G.number().int().min(1).max(50).optional().describe("Number of top hub files to list (max: 50, default: 10)"),scope:je.optional().refine(e=>e===void 0||!fv.isAbsolute(e),"Scope must be relative to the workspace").describe("Relative path within workspace to restrict wiki to (e.g. 'src/')"),exclude:G.array(oa).max(100).optional().describe("Relative glob-like patterns to exclude")}).strict(),aT=G.object({scopePath:je.optional().describe("Absolute path to a directory to scope the scan (default: workspace root). Must be inside the workspace."),maxFiles:G.number().int().min(1).max(1e4).default(500).optional().describe("Maximum number of files to analyse (default: 500). Prevents OOM on very large monorepos.")});function oT(e,t){return MI(e,t),!0}function pp(e,t,n,a,r){return{success:!0,data:e,metadata:{executionTimeMs:t,toolVersion:sa,timestamp:new Date().toISOString(),workspaceRoot:n,...r&&{indexedAt:r.indexedAt,stale:r.stale}},...a&&{pagination:a}}}function Ss(e,t,n){return{success:!1,data:null,error:e,metadata:{executionTimeMs:t,toolVersion:sa,timestamp:new Date().toISOString(),workspaceRoot:n}}}sT();ia();var lT=G.object({workspaceRoot:G.string().optional().describe("Absolute path to workspace root. Defaults to the configured workspace."),outputDir:G.string().optional().describe("Absolute path to wiki output directory. Defaults to <workspaceRoot>/wiki."),topHubsLimit:G.number().int().min(1).max(50).optional().describe("Number of top hub files to include (default: 10, max: 50)."),scope:G.string().optional().describe("Relative path within workspace to restrict wiki to (e.g. 'src/'). Defaults to entire workspace."),exclude:G.array(G.string()).optional().describe("Relative glob-like patterns to exclude (e.g. ['tests/**', '**/*.test.ts']). When omitted, default exclusions apply: tests/, dist/, *.test.ts, etc.")});import*as Tt from"fs";import*as uT from"os";import*as la from"path";var cT=50;function bv(){return la.join(uT.homedir(),".graph-it","stats")}function pz(){return process.env.GRAPH_IT_NO_STATS==="1"}function mz(e){Tt.mkdirSync(e,{recursive:!0,mode:448})}function gz(e){return e.totals.calls>0||e.llmUsage.calls>0}function dT(e,t){if(pz()||!gz(e))return;let n=t??bv();mz(n);let a=`${e.source}-${e.sessionId}.json`,r=la.join(n,a),i=la.join(n,`.${a}.${process.pid}.tmp`);Tt.writeFileSync(i,JSON.stringify(e,null,2),"utf8"),Tt.renameSync(i,r),hz(n)}function pT(e){let t=e??bv(),n;try{n=Tt.readdirSync(t)}catch{return[]}let a=[];for(let r of n){if(!r.endsWith(".json"))continue;let i=la.join(t,r);try{let o=Tt.readFileSync(i,"utf8"),s=JSON.parse(o);s&&typeof s=="object"&&s.schemaVersion===1?a.push(s):console.warn(`graph-it stats: skipping invalid stats file: ${r}`)}catch{console.warn(`graph-it stats: skipping corrupted stats file: ${r}`)}}return a}function hz(e){let t=e??bv(),n;try{n=Tt.readdirSync(t)}catch{return}let a=new Map;for(let r of n){if(!r.endsWith(".json"))continue;let i=r.indexOf("-");if(i<=0)continue;let o=r.slice(0,i),s=la.join(t,r);try{let l=Tt.statSync(s),c=a.get(o);c||(c=[],a.set(o,c)),c.push({filePath:s,mtimeMs:l.mtimeMs})}catch{}}for(let r of a.values()){if(r.length<=cT)continue;r.sort((o,s)=>o.mtimeMs-s.mtimeMs);let i=r.length-cT;for(let o=0;o<i;o++)try{Tt.unlinkSync(r[o].filePath)}catch{}}}var mT=G.object({});function fz(){return{sessions:0,calls:0,jsonTokens:0,toonTokens:0,savings:0,truncations:0,llmTokensUsed:0,llmCalls:0}}function yz(e){let t={};for(let n of e){let a=t[n.source]??=fz();a.sessions+=1,a.calls+=n.totals.calls,a.jsonTokens+=n.totals.jsonTokens,a.toonTokens+=n.totals.toonTokens,a.savings+=n.totals.savings,a.truncations+=n.totals.truncations,a.llmTokensUsed+=n.llmUsage.tokensUsed,a.llmCalls+=n.llmUsage.calls}return{persistedSessions:e.length,bySource:t}}function gT(e){let t=en.snapshot(),n={persistedSessions:0,bySource:{}};try{n=yz(pT(e))}catch{}return{description:"TOON encoding size vs JSON equivalent",estimationNote:"estimated (chars/4 heuristic)",llmUsageNote:t.source==="mcp"&&t.llmUsage.calls===0?"llmUsage is tracked for CLI `graph-it query` only \u2014 the MCP server delegates LLM synthesis to the calling client and makes no LLM API calls itself":"llmUsage is real provider-reported token consumption; it is never summed into the estimated encoding totals",currentSession:t,history:n}}Pv({createLogger(e,t){return new Bs(e,t)}});var wv=process.env.DEBUG_MCP==="true",gp=`${fT.homedir()}/mcp-debug.log`,bz=5*1024*1024,vv=`${gp}.1`;function vz(){if(wv)try{Rt.statSync(gp).size>=bz&&(Rt.existsSync(vv)&&Rt.unlinkSync(vv),Rt.renameSync(gp,vv))}catch{}}function Se(...e){let t=e.map(n=>typeof n=="string"?n:JSON.stringify(n)).join(" ");if(process.stderr.write(t.endsWith(`
3342
3342
  `)?t:`${t}
3343
3343
  `),!!wv)try{vz();let a=`[${new Date().toISOString()}] ${t}
3344
3344
  `;Rt.appendFileSync(gp,a)}catch{}}wv?(Se("[McpServer] ===== PROCESS STARTING (DEBUG MODE) ====="),Se("[McpServer] Node version:",process.version),Se("[McpServer] PID:",process.pid),Se("[McpServer] cwd:",process.cwd()),Se("[McpServer] argv:",JSON.stringify(process.argv)),Se("[McpServer] Environment vars:"),Se(" WORKSPACE_ROOT:",process.env.WORKSPACE_ROOT??"(not set)"),Se(" TSCONFIG_PATH:",process.env.TSCONFIG_PATH??"(not set)"),Se(" EXCLUDE_NODE_MODULES:",process.env.EXCLUDE_NODE_MODULES??"(not set)"),Se(" MAX_DEPTH:",process.env.MAX_DEPTH??"(not set)")):process.stderr.write(`[McpServer] Starting (debug logging disabled - set DEBUG_MCP=true to enable)