project-graph-mcp 2.3.1 → 2.4.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.
Files changed (226) hide show
  1. package/package.json +3 -2
  2. package/src/analysis/analysis-cache.ctx +9 -0
  3. package/src/analysis/analysis-cache.js +1 -1
  4. package/src/analysis/complexity.ctx +6 -0
  5. package/src/analysis/complexity.js +1 -1
  6. package/src/analysis/custom-rules.ctx +14 -0
  7. package/src/analysis/custom-rules.js +1 -1
  8. package/src/analysis/db-analysis.ctx +7 -0
  9. package/src/analysis/db-analysis.js +1 -1
  10. package/src/analysis/dead-code.ctx +6 -0
  11. package/src/analysis/dead-code.js +1 -1
  12. package/src/analysis/full-analysis.ctx +9 -0
  13. package/src/analysis/full-analysis.js +1 -1
  14. package/src/analysis/jsdoc-checker.ctx +10 -0
  15. package/src/analysis/jsdoc-checker.js +1 -1
  16. package/src/analysis/jsdoc-generator.ctx +9 -0
  17. package/src/analysis/jsdoc-generator.js +1 -1
  18. package/src/analysis/large-files.ctx +6 -0
  19. package/src/analysis/large-files.js +1 -1
  20. package/src/analysis/outdated-patterns.ctx +7 -0
  21. package/src/analysis/outdated-patterns.js +1 -1
  22. package/src/analysis/similar-functions.ctx +6 -0
  23. package/src/analysis/similar-functions.js +1 -1
  24. package/src/analysis/test-annotations.ctx +11 -0
  25. package/src/analysis/test-annotations.js +1 -1
  26. package/src/analysis/type-checker.ctx +6 -0
  27. package/src/analysis/type-checker.js +1 -1
  28. package/src/analysis/undocumented.ctx +8 -0
  29. package/src/analysis/undocumented.js +1 -1
  30. package/src/cli/cli-handlers.ctx +7 -0
  31. package/src/cli/cli-handlers.js +1 -1
  32. package/src/cli/cli.ctx +6 -0
  33. package/src/cli/cli.js +1 -1
  34. package/src/compact/ai-context.ctx +6 -0
  35. package/src/compact/ai-context.js +1 -1
  36. package/src/compact/compact-migrate.ctx +8 -0
  37. package/src/compact/compact-migrate.js +1 -1
  38. package/src/compact/compact.ctx +11 -0
  39. package/src/compact/compact.js +1 -1
  40. package/src/compact/compress.ctx +7 -0
  41. package/src/compact/compress.js +1 -1
  42. package/src/compact/ctx-resolver.ctx +2 -0
  43. package/src/compact/ctx-resolver.js +1 -1
  44. package/src/compact/ctx-to-jsdoc.ctx +11 -0
  45. package/src/compact/ctx-to-jsdoc.js +1 -1
  46. package/src/compact/doc-dialect.ctx +11 -0
  47. package/src/compact/doc-dialect.js +2 -2
  48. package/src/compact/expand.ctx +14 -0
  49. package/src/compact/expand.js +1 -1
  50. package/src/compact/framework-references.ctx +7 -0
  51. package/src/compact/framework-references.js +1 -1
  52. package/src/compact/instructions.ctx +6 -0
  53. package/src/compact/instructions.js +1 -1
  54. package/src/compact/jsdoc-builder.ctx +4 -0
  55. package/src/compact/jsdoc-builder.js +1 -1
  56. package/src/compact/mode-config.ctx +8 -0
  57. package/src/compact/mode-config.js +1 -1
  58. package/src/compact/split-declarations.ctx +6 -0
  59. package/src/compact/split-declarations.js +1 -1
  60. package/src/compact/validate-pipeline.ctx +12 -0
  61. package/src/compact/validate-pipeline.js +1 -1
  62. package/src/core/event-bus.ctx +9 -0
  63. package/src/core/event-bus.js +1 -1
  64. package/src/core/file-walker.ctx +1 -0
  65. package/src/core/file-walker.js +1 -1
  66. package/src/core/filters.ctx +12 -0
  67. package/src/core/filters.js +1 -1
  68. package/src/core/graph-builder.ctx +7 -0
  69. package/src/core/graph-builder.js +1 -1
  70. package/src/core/parser.ctx +12 -0
  71. package/src/core/parser.js +1 -1
  72. package/src/core/utils.ctx +1 -0
  73. package/src/core/utils.js +1 -1
  74. package/src/core/workspace.ctx +7 -0
  75. package/src/core/workspace.js +1 -1
  76. package/src/lang/lang-go.ctx +8 -0
  77. package/src/lang/lang-go.js +1 -1
  78. package/src/lang/lang-python.ctx +5 -0
  79. package/src/lang/lang-python.js +1 -1
  80. package/src/lang/lang-sql.ctx +10 -0
  81. package/src/lang/lang-sql.js +1 -1
  82. package/src/lang/lang-typescript.ctx +6 -0
  83. package/src/lang/lang-typescript.js +1 -1
  84. package/src/lang/lang-utils.ctx +5 -0
  85. package/src/lang/lang-utils.js +1 -1
  86. package/src/mcp/mcp-server.ctx +6 -0
  87. package/src/mcp/mcp-server.js +1 -1
  88. package/src/mcp/tool-defs.ctx +2 -0
  89. package/src/mcp/tool-defs.js +1 -1
  90. package/src/mcp/tools.ctx +13 -0
  91. package/src/mcp/tools.js +1 -1
  92. package/src/network/backend-lifecycle.ctx +10 -0
  93. package/src/network/backend-lifecycle.js +1 -1
  94. package/src/network/backend.ctx +5 -0
  95. package/src/network/backend.js +1 -1
  96. package/src/network/local-gateway.ctx +9 -0
  97. package/src/network/local-gateway.js +1 -1
  98. package/src/network/mdns.ctx +6 -0
  99. package/src/network/mdns.js +1 -1
  100. package/src/network/server.ctx +2 -0
  101. package/src/network/server.js +2 -2
  102. package/src/network/web-server.ctx +17 -0
  103. package/src/network/web-server.js +2 -2
  104. package/web/follow-controller.js +94 -25
  105. package/web/panels/dep-graph.js +207 -21
  106. package/project-graph-mcp-2.3.0.tgz +0 -0
  107. package/vendor/symbiote-node/CHANGELOG.md +0 -31
  108. package/vendor/symbiote-node/LICENSE +0 -21
  109. package/vendor/symbiote-node/README.md +0 -206
  110. package/vendor/symbiote-node/canvas/AutoLayout.js +0 -725
  111. package/vendor/symbiote-node/canvas/Breadcrumb/Breadcrumb.css.js +0 -73
  112. package/vendor/symbiote-node/canvas/Breadcrumb/Breadcrumb.js +0 -93
  113. package/vendor/symbiote-node/canvas/Breadcrumb/Breadcrumb.tpl.js +0 -9
  114. package/vendor/symbiote-node/canvas/CanvasConnectionRenderer.js +0 -962
  115. package/vendor/symbiote-node/canvas/ConnectionRenderer.js +0 -1468
  116. package/vendor/symbiote-node/canvas/FlowSimulator.js +0 -323
  117. package/vendor/symbiote-node/canvas/ForceLayout.js +0 -189
  118. package/vendor/symbiote-node/canvas/ForceWorker.js +0 -1325
  119. package/vendor/symbiote-node/canvas/GraphTabs/GraphTabs.css.js +0 -97
  120. package/vendor/symbiote-node/canvas/GraphTabs/GraphTabs.js +0 -176
  121. package/vendor/symbiote-node/canvas/GraphTabs/GraphTabs.tpl.js +0 -12
  122. package/vendor/symbiote-node/canvas/LODManager.js +0 -88
  123. package/vendor/symbiote-node/canvas/Minimap/Minimap.css.js +0 -71
  124. package/vendor/symbiote-node/canvas/Minimap/Minimap.js +0 -207
  125. package/vendor/symbiote-node/canvas/Minimap/Minimap.tpl.js +0 -9
  126. package/vendor/symbiote-node/canvas/NodeCanvas/NodeCanvas.css.js +0 -261
  127. package/vendor/symbiote-node/canvas/NodeCanvas/NodeCanvas.js +0 -1840
  128. package/vendor/symbiote-node/canvas/NodeCanvas/NodeCanvas.tpl.js +0 -22
  129. package/vendor/symbiote-node/canvas/NodeSearch/NodeSearch.css.js +0 -97
  130. package/vendor/symbiote-node/canvas/NodeSearch/NodeSearch.js +0 -132
  131. package/vendor/symbiote-node/canvas/NodeSearch/NodeSearch.tpl.js +0 -21
  132. package/vendor/symbiote-node/canvas/NodeViewManager.js +0 -584
  133. package/vendor/symbiote-node/canvas/PinExpansion.js +0 -131
  134. package/vendor/symbiote-node/canvas/PseudoConnection.js +0 -80
  135. package/vendor/symbiote-node/canvas/SubgraphManager.js +0 -201
  136. package/vendor/symbiote-node/canvas/SubgraphRouter.js +0 -443
  137. package/vendor/symbiote-node/canvas/ViewportActions.js +0 -446
  138. package/vendor/symbiote-node/core/Connection.js +0 -45
  139. package/vendor/symbiote-node/core/Editor.js +0 -451
  140. package/vendor/symbiote-node/core/Frame.js +0 -31
  141. package/vendor/symbiote-node/core/GraphMermaid.js +0 -348
  142. package/vendor/symbiote-node/core/GraphText.js +0 -210
  143. package/vendor/symbiote-node/core/Node.js +0 -143
  144. package/vendor/symbiote-node/core/Portal.js +0 -104
  145. package/vendor/symbiote-node/core/Socket.js +0 -185
  146. package/vendor/symbiote-node/core/SubgraphNode.js +0 -125
  147. package/vendor/symbiote-node/index.js +0 -103
  148. package/vendor/symbiote-node/inspector/InspectorPanel/InspectorPanel.css.js +0 -361
  149. package/vendor/symbiote-node/inspector/InspectorPanel/InspectorPanel.js +0 -332
  150. package/vendor/symbiote-node/inspector/InspectorPanel/InspectorPanel.tpl.js +0 -96
  151. package/vendor/symbiote-node/inspector/TemplatePreview/TemplatePreview.css.js +0 -104
  152. package/vendor/symbiote-node/inspector/TemplatePreview/TemplatePreview.js +0 -133
  153. package/vendor/symbiote-node/inspector/TemplatePreview/TemplatePreview.tpl.js +0 -33
  154. package/vendor/symbiote-node/interactions/ConnectFlow.js +0 -307
  155. package/vendor/symbiote-node/interactions/Drag.js +0 -102
  156. package/vendor/symbiote-node/interactions/Selector.js +0 -132
  157. package/vendor/symbiote-node/interactions/SnapGrid.js +0 -65
  158. package/vendor/symbiote-node/interactions/Zoom.js +0 -140
  159. package/vendor/symbiote-node/layout/ActionZone/ActionZone.css.js +0 -88
  160. package/vendor/symbiote-node/layout/ActionZone/ActionZone.js +0 -254
  161. package/vendor/symbiote-node/layout/ActionZone/ActionZone.tpl.js +0 -11
  162. package/vendor/symbiote-node/layout/Layout/Layout.css.js +0 -88
  163. package/vendor/symbiote-node/layout/Layout/Layout.js +0 -622
  164. package/vendor/symbiote-node/layout/Layout/Layout.tpl.js +0 -25
  165. package/vendor/symbiote-node/layout/LayoutNode/LayoutNode.css.js +0 -293
  166. package/vendor/symbiote-node/layout/LayoutNode/LayoutNode.js +0 -467
  167. package/vendor/symbiote-node/layout/LayoutNode/LayoutNode.tpl.js +0 -33
  168. package/vendor/symbiote-node/layout/LayoutPreview/LayoutPreview.css.js +0 -46
  169. package/vendor/symbiote-node/layout/LayoutPreview/LayoutPreview.js +0 -102
  170. package/vendor/symbiote-node/layout/LayoutPreview/LayoutPreview.tpl.js +0 -6
  171. package/vendor/symbiote-node/layout/LayoutRouter/LayoutRouter.js +0 -156
  172. package/vendor/symbiote-node/layout/LayoutRouter/routerSync.js +0 -250
  173. package/vendor/symbiote-node/layout/LayoutSidebar/LayoutSidebar.css.js +0 -379
  174. package/vendor/symbiote-node/layout/LayoutSidebar/LayoutSidebar.js +0 -263
  175. package/vendor/symbiote-node/layout/LayoutSidebar/LayoutSidebar.tpl.js +0 -20
  176. package/vendor/symbiote-node/layout/LayoutSidebar/SidebarSection.js +0 -183
  177. package/vendor/symbiote-node/layout/LayoutTree.js +0 -246
  178. package/vendor/symbiote-node/layout/PanelMenu/PanelMenu.css.js +0 -43
  179. package/vendor/symbiote-node/layout/PanelMenu/PanelMenu.js +0 -89
  180. package/vendor/symbiote-node/layout/PanelMenu/PanelMenu.tpl.js +0 -14
  181. package/vendor/symbiote-node/layout/index.js +0 -16
  182. package/vendor/symbiote-node/menu/ContextMenu/ContextMenu.css.js +0 -61
  183. package/vendor/symbiote-node/menu/ContextMenu/ContextMenu.js +0 -79
  184. package/vendor/symbiote-node/menu/ContextMenu/ContextMenu.tpl.js +0 -19
  185. package/vendor/symbiote-node/node/CtrlItem/CtrlItem.css.js +0 -41
  186. package/vendor/symbiote-node/node/CtrlItem/CtrlItem.js +0 -24
  187. package/vendor/symbiote-node/node/CtrlItem/CtrlItem.tpl.js +0 -16
  188. package/vendor/symbiote-node/node/GraphFrame/GraphFrame.css.js +0 -65
  189. package/vendor/symbiote-node/node/GraphFrame/GraphFrame.js +0 -29
  190. package/vendor/symbiote-node/node/GraphFrame/GraphFrame.tpl.js +0 -13
  191. package/vendor/symbiote-node/node/GraphNode/GraphNode.css.js +0 -683
  192. package/vendor/symbiote-node/node/GraphNode/GraphNode.js +0 -92
  193. package/vendor/symbiote-node/node/GraphNode/GraphNode.tpl.js +0 -17
  194. package/vendor/symbiote-node/node/NodeSocket/NodeSocket.js +0 -25
  195. package/vendor/symbiote-node/node/NodeSocket/NodeSocket.tpl.js +0 -7
  196. package/vendor/symbiote-node/node/PortItem/PortItem.css.js +0 -90
  197. package/vendor/symbiote-node/node/PortItem/PortItem.js +0 -87
  198. package/vendor/symbiote-node/node/PortItem/PortItem.tpl.js +0 -10
  199. package/vendor/symbiote-node/package.json +0 -59
  200. package/vendor/symbiote-node/palette/PaletteBrowser/PaletteBrowser.css.js +0 -143
  201. package/vendor/symbiote-node/palette/PaletteBrowser/PaletteBrowser.js +0 -131
  202. package/vendor/symbiote-node/palette/PaletteBrowser/PaletteBrowser.tpl.js +0 -16
  203. package/vendor/symbiote-node/plugins/History.js +0 -384
  204. package/vendor/symbiote-node/plugins/Readonly.js +0 -59
  205. package/vendor/symbiote-node/shapes/CircleShape.js +0 -80
  206. package/vendor/symbiote-node/shapes/CommentShape.js +0 -35
  207. package/vendor/symbiote-node/shapes/DiamondShape.js +0 -115
  208. package/vendor/symbiote-node/shapes/NodeShape.js +0 -80
  209. package/vendor/symbiote-node/shapes/PillShape.js +0 -91
  210. package/vendor/symbiote-node/shapes/RectShape.js +0 -72
  211. package/vendor/symbiote-node/shapes/SVGShape.js +0 -494
  212. package/vendor/symbiote-node/shapes/index.js +0 -53
  213. package/vendor/symbiote-node/themes/Palette.js +0 -32
  214. package/vendor/symbiote-node/themes/Skin.js +0 -113
  215. package/vendor/symbiote-node/themes/Theme.js +0 -84
  216. package/vendor/symbiote-node/themes/carbon.js +0 -137
  217. package/vendor/symbiote-node/themes/dark.js +0 -137
  218. package/vendor/symbiote-node/themes/ebook.js +0 -138
  219. package/vendor/symbiote-node/themes/grey.js +0 -137
  220. package/vendor/symbiote-node/themes/light.js +0 -137
  221. package/vendor/symbiote-node/themes/neon.js +0 -138
  222. package/vendor/symbiote-node/themes/pcb.js +0 -273
  223. package/vendor/symbiote-node/themes/synthwave.js +0 -137
  224. package/vendor/symbiote-node/toolbar/QuickToolbar/QuickToolbar.css.js +0 -86
  225. package/vendor/symbiote-node/toolbar/QuickToolbar/QuickToolbar.js +0 -128
  226. package/vendor/symbiote-node/toolbar/QuickToolbar/QuickToolbar.tpl.js +0 -29
@@ -1,2 +1,2 @@
1
- // @ctx .context/src/compact/split-declarations.ctx
1
+ // @ctx split-declarations.ctx
2
2
  import{parse as t}from"../../vendor/acorn.mjs";export function splitDeclarations(r){try{const e=t(r,{ecmaVersion:"latest",sourceType:"module"});if(e.body.length<=3)return r;const n=[];for(const t of e.body)n.push(t.start);n.sort((t,r)=>t-r);const o=[];let s=0;for(const t of n)t>0&&(o.push(r.slice(s,t).trimEnd()),s=t);return o.push(r.slice(s).trimEnd()),o.filter(t=>t).join("\n")}catch{return r}}export function isSingleLineBlob(r){try{if(r.split("\n").filter(t=>!t.startsWith("// @ctx ")&&!t.startsWith("#!")).length>2)return!1;return t(r,{ecmaVersion:"latest",sourceType:"module"}).body.length>3}catch{return!1}}
@@ -0,0 +1,12 @@
1
+ --- src/compact/validate-pipeline.js ---
2
+ @sig aa5dc47b
3
+ @enrich: Replace each (internal utility) below. Read src/compact/validate-pipeline.js for context.
4
+ Rules: max 80ch, pipe|separated, abbrev (fn/ret/cfg/init/auth/db/msg).
5
+ Save this file after filling all markers. Remove @enrich lines when done.
6
+ D(t,s)→c,e,f.split,u.startsWith,i.push,l|(internal utility)
7
+ W(e)→Math.ceil|(internal utility)
8
+ export validatePipeline(rootPath:string,options:Object=)→Date.now,p,u,r,n,S|run full Mode 4 validation pipeline|returns PASS/FAIL with contract, AST, token reports
9
+ PATTERNS: 4-step pipeline (contracts→decompile→AST verify→tokens)|single-call validation
10
+ EDGE_CASES: missing .full/→skip AST verify|missing src/→skip tokens|strict mode for extra contract checks
11
+ E(a,b)|checkCompactStyle — internal helper
12
+ export fixCompactStyle(rootPath:string,files:string[])|auto-fix compact style issues via terser re-minification
@@ -1,4 +1,4 @@
1
- // @ctx .context/src/compact/validate-pipeline.ctx
1
+ // @ctx validate-pipeline.ctx
2
2
  import{walkJSFiles as S}from"../core/file-walker.js";import{estimateTokens as W}from"../core/utils.js";import{readFileSync as e,readdirSync as t,statSync as s,existsSync as n,writeFileSync as i,mkdirSync as o}from"fs";import{join as r,extname as a,relative as c,dirname as l,basename as d}from"path";import{execSync as f}from"node:child_process";import{parse as m}from"../../vendor/acorn.mjs";import{validateCtxContracts as p}from"./ctx-to-jsdoc.js";import{expandProject as u}from"./expand.js";import{parseProject as h}from"../core/parser.js";import{getGraph as g}from"../mcp/tools.js";import{generateContextFiles as x}from"./doc-dialect.js";import{simple as y}from"../../vendor/walk.mjs";import{splitDeclarations as j,isSingleLineBlob as w}from"./split-declarations.js";
3
3
 
4
4
 
@@ -0,0 +1,9 @@
1
+ --- src/core/event-bus.js ---
2
+ @sig f4e3c62d
3
+ export emitToolCall(toolName:string,args:Object)→Date.now,t.emit|emit tool:call event with timestamp and arguments
4
+ export emitToolResult(toolName:string,args:Object,result:Object,duration:number,success:boolean)→Object.keys,Date.now,t.emit|emit tool:result event with duration, success status, and result keys
5
+ export onToolCall(callback:Function)→t.on|register listener for tool:call events
6
+ export onToolResult(callback:Function)→t.on|register listener for tool:result events
7
+ export removeToolListener(event:string,callback:Function)→t.off|remove specific event listener from bus
8
+ PATTERNS: Singleton EventEmitter|event name namespacing tool:*
9
+ EDGE_CASES: max listeners increased to 50 to prevent warnings
@@ -1,4 +1,4 @@
1
- // @ctx .context/src/core/event-bus.ctx
1
+ // @ctx event-bus.ctx
2
2
  import{EventEmitter as o}from"node:events";
3
3
  const t=new o;
4
4
  t.setMaxListeners(50);
@@ -0,0 +1 @@
1
+ export walkJSFiles(dir:string,root:string=)|recursively find all .js/.mjs files|excludes node_modules, .git, vendor, etc
@@ -1,4 +1,4 @@
1
- // @ctx .context/src/core/file-walker.ctx
1
+ // @ctx file-walker.ctx
2
2
  import{readdirSync as n,statSync as o}from"fs";import{join as r,extname as c}from"path";
3
3
  const f=new Set(["node_modules",".git","vendor",".context","dev-docs",".agent",".agents",".expanded","web"]),d=new Set([".js",".mjs"]);
4
4
  export function walkJSFiles(e,t=e){const s=[];try{for(const a of n(e)){if(a.startsWith(".")&&"."!==a)continue;const n=r(e,a);o(n).isDirectory()?f.has(a)||s.push(...walkJSFiles(n,t)):d.has(c(a).toLowerCase())&&s.push(n)}}catch{}return s}
@@ -0,0 +1,12 @@
1
+ --- src/core/filters.js ---
2
+ @sig 321a1e95
3
+ export getFilters()|return current filter configuration
4
+ export setFilters(updates:Object)→getFilters|update filter configuration state
5
+ export addExcludes(dirs:string[])→getFilters|add directories to exclude list
6
+ export removeExcludes(dirs:string[])→e.includes,excludeDirs.filter,getFilters|remove directories from exclude list
7
+ export resetFilters()→getFilters|restore default filter configuration
8
+ export parseGitignore(rootDir:string)→r,t,e,e.trim,e.startsWith,e.replace|parse and load patterns from .gitignore
9
+ export shouldExcludeDir(dirName:string,relativePath:string=)→e.startsWith,excludeDirs.includes,o|check if directory should be excluded by rules
10
+ export shouldExcludeFile(fileName:string,relativePath:string=)→u,o|check if file should be excluded by rules
11
+ PATTERNS: mutable module state|regex wildcard matching
12
+ EDGE_CASES: ignores gitignore comments|handles hidden files option
@@ -1,4 +1,4 @@
1
- // @ctx .context/src/core/filters.ctx
1
+ // @ctx filters.ctx
2
2
  import{readFileSync as e,existsSync as t}from"fs";
3
3
  import{join as r}from"path";
4
4
  const i=["node_modules","dist","build","coverage",".next",".nuxt",".output","__pycache__",".cache",".turbo","out"],n=["*.test.js","*.spec.js","*.min.js","*.bundle.js","*.d.ts",".project-graph-cache.json"];
@@ -0,0 +1,7 @@
1
+ --- src/core/graph-builder.js ---
2
+ @sig 15e88a1b
3
+ export minifyLegend(names:string[])→e,o.has,o.add|create minified legend from names|handles collisions with numeric suffix
4
+ export buildGraph(parsed:Object)→s.map,t.map,s.flatMap,minifyLegend,Object.entries,Object.fromEntries|build minified graph from parsed data|tracks nodes, edges, orphans, duplicates
5
+ export createSkeleton(graph:Object)→Object.entries,e.push,Object.values,c.add,Object.keys,c.has|create compact project skeleton|minimizes tokens for AI context
6
+ PATTERNS: adjacency list for edges|minified legend for token savings
7
+ EDGE_CASES: skips empty classes|handles unreferenced functions as orphans
@@ -1,4 +1,4 @@
1
- // @ctx .context/src/core/graph-builder.ctx
1
+ // @ctx graph-builder.ctx
2
2
  export function minifyLegend(s){const t={},o=new Set;for(const n of s){let s=e(n),c=1;for(;o.has(s);)s=e(n)+c,c++;o.add(s),t[n]=s}return t}
3
3
  function e(e){const s=e.replace(/[a-z]/g,"");if(s.length>=2)return s.slice(0,3);const t=e.match(/[A-Z]/g);return t&&t.length>0?e[0].toLowerCase()+t[0]:e.slice(0,2)}
4
4
  export function buildGraph(e){const s=e.classes||[],t=e.functions||[],o=[...s.map(e=>e.name),...t.map(e=>e.name),...s.flatMap(e=>e.methods||[])],n=minifyLegend([...new Set(o)]),c=Object.fromEntries(Object.entries(n).map(([e,s])=>[s,e])),f={v:1,legend:n,reverseLegend:c,stats:{files:(e.files||[]).length,classes:s.length,functions:t.length,tables:(e.tables||[]).length},nodes:{},edges:[],orphans:[],duplicates:{},files:e.files||[],fileImports:e.fileImports||{}};for(const e of s){const s=n[e.name];f.nodes[s]={t:"C",x:e.extends||void 0,m:(e.methods||[]).map(e=>n[e]||e),$:(e.properties||[]).length?e.properties:void 0,i:e.imports?.length?e.imports:void 0,f:e.file||void 0,l:e.line||void 0};for(const t of e.calls||[])if(t.includes(".")){const[e,o]=t.split(".");if(n[e]){const t=[s,"→",`${n[e]}.${n[o]||o}`];f.edges.push(t)}}else if(n[t]){const e=[s,"→",n[t]];f.edges.push(e)}}for(const e of t){const s=n[e.name];f.nodes[s]={t:"F",e:e.exported,f:e.file||void 0,l:e.line||void 0};for(const t of e.dbReads||[])f.edges.push([s,"R→",t]);for(const t of e.dbWrites||[])f.edges.push([s,"W→",t])}for(const e of s){const s=n[e.name];for(const t of e.dbReads||[])f.edges.push([s,"R→",t]);for(const t of e.dbWrites||[])f.edges.push([s,"W→",t])}for(const s of e.tables||[])f.nodes[s.name]={t:"T",cols:s.columns.map(e=>e.name),f:s.file||void 0};const l=new Set;for(const e of f.edges){const s=e[2].split(".")[0];l.add(s)}for(const e of Object.keys(f.nodes))l.has(e)||"F"!==f.nodes[e].t||f.nodes[e].e||f.orphans.push(c[e]);const i=Object.create(null);for(const e of s)for(const s of e.methods||[])i[s]||(i[s]=[]),i[s].push(`${e.name}:${e.line}`);for(const[e,s]of Object.entries(i))s.length>1&&(f.duplicates[e]=s);return f}
@@ -0,0 +1,12 @@
1
+ --- src/core/parser.js ---
2
+ @sig c64e9ff4
3
+ v(e,s)→s.endsWith,h,d,m,f,parseFile|(internal utility)
4
+ j(e,s,t,n)→s.includes,s.push,b.has,S,g,y|(internal utility)
5
+ export findJSFiles(dir:string,rootDir:string=)→u,s,r,t,o,c.isDirectory|recursively find .js files in dir, respecting ignores and extensions
6
+ E(e)→e.endsWith,x.some|(internal utility)
7
+ export parseFile(code:string,filename:string)→a,console.warn,value.startsWith,s.slice,i.exec,e.slice|parse single JS file to AST|extracts classes,functions,imports,exports
8
+ export discoverSubProjects(rootPath:string)→i,r,n,s,t,e|find sub-projects in known monorepo directories
9
+ export parseProject(dir:string)→i,findJSFiles,e,o,v,files.push|parse all JS files in dir|returns aggregated ParseResult
10
+ export findAllProjectFiles(rootDir:string,options:Object=)→u,i,s,r,t,o|recursively find all files in dir, respecting gitignore and graphignore
11
+ PATTERNS: AST visitor via walk.simple|extension-based parser routing
12
+ EDGE_CASES: ignores parse errors (returns empty)|excludes specific presentation files
@@ -1,4 +1,4 @@
1
- // @ctx .context/src/core/parser.ctx
1
+ // @ctx parser.ctx
2
2
  import{readFileSync as e,readdirSync as s,statSync as t,existsSync as n}from"fs";
3
3
  import{join as r,relative as o,resolve as i}from"path";
4
4
  import{parse as a}from"../../vendor/acorn.mjs";
@@ -0,0 +1 @@
1
+ export estimateTokens(str:string)|estimate LLM token count for string|~4 chars per token
package/src/core/utils.js CHANGED
@@ -1,2 +1,2 @@
1
- // @ctx .context/src/core/utils.ctx
1
+ // @ctx utils.ctx
2
2
  export function estimateTokens(e){const t="string"==typeof e?e:JSON.stringify(e);return Math.ceil(t.length/4)}
@@ -0,0 +1,7 @@
1
+ --- src/core/workspace.js ---
2
+ @sig 895f5c6b
3
+ export setRoots(roots:string[])→o.startsWith,o.slice,console.error|set workspace root dirs from MCP client (file:// URI or path)
4
+ export getWorkspaceRoot()|return first workspace root or cwd fallback
5
+ export resolvePath(filePath:string)→getWorkspaceRoot,o,r,s.startsWith|resolve relative path to absolute, enforce workspace boundary
6
+ PATTERNS: workspace roots set once via MCP initialize|resolvePath prevents directory traversal attacks
7
+ EDGE_CASES: file:// URIs auto-stripped to path|absolute paths outside workspace rejected|empty roots fall back to process.cwd()
@@ -1,4 +1,4 @@
1
- // @ctx .context/src/core/workspace.ctx
1
+ // @ctx workspace.ctx
2
2
  import{resolve as r,isAbsolute as o,dirname as t}from"path";import{fileURLToPath as e}from"url";
3
3
  let s=null;
4
4
  const a=t(e(import.meta.url)),p=r(a,"..",".."),c=process.argv.find(r=>r.startsWith("--workspace="));c&&(s=c.split("=")[1],console.error(`[project-graph] Workspace from arg: ${s}`));
@@ -0,0 +1,8 @@
1
+ --- src/lang/lang-go.js ---
2
+ @sig b212f0be
3
+ export parseGo(code:string,filename:string)→extractImports,s,r.exec,getBody,t.substring,n.split|parse Go structs, interfaces, methods, and functions
4
+ extractImports(s)→s.replace,l.exec,n.match,t.includes,t.push,e.add|extract Go imports and package names
5
+ getBody(s,t)→s.substring|extract block body using brace counting
6
+ extractCalls(s,t)→n.exec,s.includes,s.split,t.has,e.includes,e.push|extract function calls ignoring Go keywords
7
+ PATTERNS: regex string extraction|brace counting for block scope
8
+ EDGE_CASES: ignores Go keywords|strips package prefix if alias matches
@@ -1,4 +1,4 @@
1
- // @ctx .context/src/lang/lang-go.ctx
1
+ // @ctx lang-go.ctx
2
2
  import{stripStringsAndComments as s}from"./lang-utils.js";
3
3
  export function parseGo(t,e){const n={file:e,classes:[],functions:[],imports:[],exports:[]},{imports:l,packageNames:o}=extractImports(t);n.imports=l;
4
4
  const i=s(t,{singleQuote:!1,backtick:!0,templateInterpolation:!1}),c=new Map,r=/^\s*type\s+([a-zA-Z_]\w*)\s+struct\s*\{/gm;
@@ -0,0 +1,5 @@
1
+ --- src/lang/lang-python.js ---
2
+ @sig 2e3acbc0
3
+ export parsePython(code:string=,filename:string=)→s,t.trim,t.match,classes.push,s.split,functions.push|parse Python classes, methods, and top-level functions
4
+ PATTERNS: indentation level tracking for scope|regex matching
5
+ EDGE_CASES: handles async def|handles __all__ for explicit exports
@@ -1,4 +1,4 @@
1
- // @ctx .context/src/lang/lang-python.ctx
1
+ // @ctx lang-python.ctx
2
2
  import{stripStringsAndComments as s}from"./lang-utils.js";
3
3
  export function parsePython(t="",n=""){const e={file:n,classes:[],functions:[],imports:[],exports:[]},o=s(t,{singleQuote:!0,hashComment:!0,tripleQuote:!0}).split("\n");
4
4
  let l=null,i=null,c=-1;for(let s=0;s<o.length;s++){const t=o[s];if(!t.trim())continue;
@@ -0,0 +1,10 @@
1
+ --- src/lang/lang-sql.js ---
2
+ @sig 676f0c3a
3
+ export isSQLString(str:string)|test if string looks like a SQL query
4
+ t(t)→t.toLowerCase,e.has|(internal utility)
5
+ export extractSQLFromString(str:string)→e.replace,a.exec,r.slice,t,n.add,i.exec|extract table READs and WRITEs from SQL string
6
+ export parseSQL(code:string=,filename:string=)→r.exec,e.substring,n,tables.push|parse CREATE TABLE statements into table objects
7
+ export extractSQLFromCode(code:string)→r.exec,isSQLString,extractSQLFromString,t.add,reads.forEach,n.add|extract SQL queries and ORM calls from source code
8
+ export extractORMFromCode(code:string)→o.exec,e.startsWith,s.has,n.add,r.has,a.add|extract DB reads/writes from prisma, sequelize, knex ORM calls
9
+ PATTERNS: ORM pattern matching (prisma, sequelize, knex)|regex SQL parsing
10
+ EDGE_CASES: ignores PostgreSQL keywords|ignores DELETE target in reads
@@ -1,4 +1,4 @@
1
- // @ctx .context/src/lang/lang-sql.ctx
1
+ // @ctx lang-sql.ctx
2
2
  const e=new Set(["select","from","where","and","or","not","in","on","as","join","left","right","inner","outer","cross","full","group","order","by","having","limit","offset","union","all","distinct","case","when","then","else","end","null","true","false","is","between","like","ilike","exists","any","some","set","values","into","table","create","alter","drop","index","primary","key","foreign","references","constraint","default","check","unique","if","begin","commit","rollback","transaction","returning","conflict","nothing","do","update","cascade","restrict","lateral","each","row","with","recursive","only","integer","int","bigint","smallint","serial","bigserial","text","varchar","char","character","boolean","bool","timestamp","timestamptz","date","time","timetz","interval","numeric","decimal","real","float","double","json","jsonb","uuid","bytea","inet","cidr","macaddr","array","point","line","box","circle","polygon","path","count","sum","avg","min","max","coalesce","cast","extract","now","current_timestamp","current_date","generate_series","unnest","string_agg","array_agg","row_number","rank","dense_rank","over","partition","asc","desc","nulls","first","last","filter","columns","rows","tables","schema","schemas","information_schema","pg_catalog","pg_tables","pg_class"]);
3
3
  export function isSQLString(e){return!(!e||"string"!=typeof e)&&/^\s*(SELECT|INSERT|UPDATE|DELETE|WITH|CREATE\s+TABLE)\b/i.test(e)}
4
4
  function t(t){return!(!t||t.length<2||e.has(t.toLowerCase())||!/^[a-zA-Z_]\w*$/.test(t)||/^[A-Z][A-Z_]*$/.test(t)||/^[A-Z][a-z]/.test(t)||/^(pg_|jsonb_|array_|string_|regexp_)/.test(t))}
@@ -0,0 +1,6 @@
1
+ --- src/lang/lang-typescript.js ---
2
+ @sig 9b26807c
3
+ export parseTypeScript(code:string,filename:string)→s,t.match,s.trim,imports.push,exports.push,classes.push|regex parsing of TypeScript classes, methods, and functions
4
+ extractParams(s)→s.match,s.trim,s.startsWith|extract and clean parameters from signature line
5
+ PATTERNS: regex line-by-line parsing|string/comment stripping preprocessing
6
+ EDGE_CASES: ignores type-only declarations|cleans generics and optional marks
@@ -1,4 +1,4 @@
1
- // @ctx .context/src/lang/lang-typescript.ctx
1
+ // @ctx lang-typescript.ctx
2
2
  import{stripStringsAndComments as s}from"./lang-utils.js";
3
3
  export function parseTypeScript(t,e){const r={file:e,classes:[],functions:[],imports:[],exports:[]},c=s(t).split("\n");
4
4
  let n=null,a=null;for(let s=0;s<c.length;s++){const t=c[s],o=s+1,i=t.match(/^\s*import\s+(?:type\s+)?(?:\{([^}]+)\}|(\w+))\s+from\s/);if(i){i[1]?i[1].split(",").forEach(s=>{const t=s.trim().replace(/\s+as\s+\w+/,"").replace(/^type\s+/,"");t&&r.imports.push(t)}):i[2]&&r.imports.push(i[2]);continue}const l=t.match(/^\s*import\s+\*\s+as\s+(\w+)\s+from\s/);if(l){r.imports.push(l[1]);continue}const p=t.match(/^\s*export\s+(?:default\s+)?(?:class|function|const|let|var|type|interface|enum|abstract)\s+(\w+)/);p&&r.exports.push(p[1]);
@@ -0,0 +1,5 @@
1
+ --- src/lang/lang-utils.js ---
2
+ @sig 9df6d2e2
3
+ export stripStringsAndComments(code:string,options:Object=)|strip comments and strings from code using state machine
4
+ PATTERNS: character-by-character state machine string parsing
5
+ EDGE_CASES: preserves template interpolations|handles triple quotes
@@ -1,4 +1,4 @@
1
- // @ctx .context/src/lang/lang-utils.ctx
1
+ // @ctx lang-utils.ctx
2
2
  export function stripStringsAndComments(n,e={}){const{singleQuote:t=!0,backtick:o=!0,hashComment:i=!1,tripleQuote:l=!1,templateInterpolation:f=!0}=e;
3
3
  let r="",s=0;for(;s<n.length;)if(i&&"#"===n[s])for(;s<n.length&&"\n"!==n[s];)r+=" ",s++;else{if(l&&("'"===n[s]&&"'"===n[s+1]&&"'"===n[s+2]||'"'===n[s]&&'"'===n[s+1]&&'"'===n[s+2])){const e=n[s];for(r+=" ",s+=3;s<n.length;)if("\\"!==n[s]){if(n[s]===e&&n[s+1]===e&&n[s+2]===e){r+=" ",s+=3;break}r+="\n"===n[s]?"\n":" ",s++}else r+=" ",s+=2;continue}if(i||"/"!==n[s]||"/"!==n[s+1])if(i||"/"!==n[s]||"*"!==n[s+1]){if('"'===n[s]||t&&"'"===n[s]||o&&"`"===n[s]){const e=n[s];for(r+=" ",s++;s<n.length;)if("\\"!==n[s]){if(n[s]===e){r+=" ",s++;break}if(f&&"`"===e&&"$"===n[s]&&"{"===n[s+1]){r+="${",s+=2;
4
4
  let e=1;for(;s<n.length&&e>0;)"{"===n[s]&&e++,"}"===n[s]&&e--,r+=e>0?"\n"===n[s]?"\n":n[s]:"}",s++;continue}r+="\n"===n[s]?"\n":" ",s++}else r+=" ",s+=2;continue}r+=n[s],s++}else{for(s+=2,r+=" ";s<n.length&&("*"!==n[s]||"/"!==n[s+1]);)r+="\n"===n[s]?"\n":" ",s++;s<n.length&&(r+=" ",s+=2)}else for(;s<n.length&&"\n"!==n[s];)r+=" ",s++}return r}
@@ -0,0 +1,6 @@
1
+ --- src/mcp/mcp-server.js ---
2
+ @sig 112b580b
3
+ export createServer()→i.get,i.delete,e.reject,e.resolve,handleNotification,q|create MCP server instance|handles JSON-RPC 2.0 messages
4
+ export startStdioServer()→JSON.stringify,console.log,createServer,JSON.parse,t.handleMessage,sendToClient|start MCP server with stdio transport
5
+ PATTERNS: JSON-RPC 2.0|bidirectional stdio communication
6
+ EDGE_CASES: handles timeout for roots/list|graceful parse error handling
@@ -1,4 +1,4 @@
1
- // @ctx .context/src/mcp/mcp-server.ctx
1
+ // @ctx mcp-server.ctx
2
2
  import e from"fs";import t from"path";import{fileURLToPath as s}from"url";import{TOOLS as o}from"./tool-defs.js";import{emitToolCall as a,emitToolResult as r}from"../core/event-bus.js";import{getSkeleton as n,getFocusZone as i,expand as c,deps as d,usages as l,invalidateCache as p,getCallChain as u}from"./tools.js";import{getPendingTests as m,markTestPassed as _,markTestFailed as g,getTestSummary as h,resetTestState as f}from"../analysis/test-annotations.js";import{getFilters as y,setFilters as x,addExcludes as j,removeExcludes as w,resetFilters as v}from"../core/filters.js";import{getInstructions as b}from"../compact/instructions.js";import{getUndocumentedSummary as k}from"../analysis/undocumented.js";import{getDeadCode as S}from"../analysis/dead-code.js";import{generateJSDoc as $,generateJSDocFor as R}from"../analysis/jsdoc-generator.js";import{getSimilarFunctions as F}from"../analysis/similar-functions.js";import{getComplexity as E}from"../analysis/complexity.js";import{getLargeFiles as C}from"../analysis/large-files.js";import{getOutdatedPatterns as T}from"../analysis/outdated-patterns.js";import{getFullAnalysis as U,getAnalysisSummaryOnly as D}from"../analysis/full-analysis.js";import{getCustomRules as P,setCustomRule as O,checkCustomRules as I}from"../analysis/custom-rules.js";import{getFrameworkReference as J}from"../compact/framework-references.js";import{setRoots as q,resolvePath as A}from"../core/workspace.js";import{getDBSchema as N,getTableUsage as L,getDBDeadTables as M}from"../analysis/db-analysis.js";import{compressFile as B,editCompressed as z}from"../compact/compress.js";import{getProjectDocs as G,generateContextFiles as V,checkStaleness as W}from"../compact/doc-dialect.js";import{getGraph as Q}from"./tools.js";import{parseProject as Y,discoverSubProjects as H}from"../core/parser.js";import{getAiContext as Z}from"../compact/ai-context.js";import{checkJSDocConsistency as K}from"../analysis/jsdoc-checker.js";import{checkTypes as X}from"../analysis/type-checker.js";import{compactProject as ee,expandProject as te}from"../compact/compact.js";import{expandFile as se,expandProject as oe}from"../compact/expand.js";import{validatePipeline as ae}from"../compact/validate-pipeline.js";import{validateCtxContracts as re}from"../compact/ctx-to-jsdoc.js";import{getConfig as ne,setConfig as ie,getModeDescription as ce,getModeWorkflow as de}from"../compact/mode-config.js";import{readFileSync as le,existsSync as pe}from"fs";
3
3
  const ue=t.dirname(s(import.meta.url));let _pkgVer="0.0.0";try{_pkgVer=JSON.parse(le(t.join(ue,"..","..","package.json"),"utf8")).version}catch{}const me={get_skeleton:e=>n(A(e.path)),get_focus_zone:e=>i({...e,path:A(e.path)}),expand:e=>c(e.symbol),deps:e=>d(e.symbol),usages:e=>l(e.symbol),get_call_chain:e=>u({from:e.from,to:e.to,path:e.path?A(e.path):void 0}),invalidate_cache:()=>(p(),{success:!0}),get_pending_tests:e=>m(A(e.path)),mark_test_passed:e=>_(e.testId),mark_test_failed:e=>g(e.testId,e.reason),get_test_summary:e=>h(A(e.path)),reset_test_state:()=>f(),get_filters:()=>y(),set_filters:e=>x(e),add_excludes:e=>j(e.dirs),remove_excludes:e=>w(e.dirs),reset_filters:()=>v(),get_usage_guide:s=>{try{const o=t.join(ue,"..","..","GUIDE.md"),a=e.readFileSync(o,"utf8");if(!s.topic)return a;
4
4
  const r=new RegExp(`## ${s.topic}`,"i"),n=a.match(r);if(!n)return`Topic '${s.topic}' not found in guide.`;
@@ -0,0 +1,2 @@
1
+
2
+ @names __top__:e=TOOLS
@@ -1,3 +1,3 @@
1
- // @ctx .context/src/mcp/tool-defs.ctx
1
+ // @ctx tool-defs.ctx
2
2
  const e={get_skeleton:{name:"get_skeleton",description:"Get compact minified project overview (10-50x smaller than source). Returns legend, stats, and node summaries.",inputSchema:{type:"object",properties:{path:{type:"string",description:'Path to scan (e.g., "src/components")'}},required:["path"]}},get_focus_zone:{name:"get_focus_zone",description:"Get enriched context for recently modified files. Auto-detects from git or accepts explicit file list.",inputSchema:{type:"object",properties:{path:{type:"string"},useGitDiff:{type:"boolean",description:"Auto-detect from git diff"},recentFiles:{type:"array",items:{type:"string"},description:"Explicit list of files to expand"}}}},get_ai_context:{name:"get_ai_context",description:"Boot AI agent context. Two modes:\n1. Default: returns skeleton + docs (2-3k tokens) for structure overview.\n2. includeFiles: ['*']: returns ALL compressed source code without skeleton/docs (pure code dump). Filters vendored files via .contextignore (auto-created on first call).\nCall FIRST when starting work. Use ['*'] for small/medium projects that fit in context.",inputSchema:{type:"object",properties:{path:{type:"string",description:"Project root path"},includeFiles:{type:"array",items:{type:"string"},description:'Files to include. Use ["*"] for ALL source files (filtered by .contextignore). Or specific files: ["parser.js", "tools.js"]'},includeDocs:{type:"boolean",description:"Include doc-dialect documentation (default: true, auto-disabled with '*')"},includeSkeleton:{type:"boolean",description:"Include project skeleton (default: true, auto-disabled with '*')"}},required:["path"]}},invalidate_cache:{name:"invalidate_cache",description:"Invalidate the cached graph. Use after making code changes.",inputSchema:{type:"object",properties:{}}},get_usage_guide:{name:"get_usage_guide",description:"Get the comprehensive usage guide for project-graph with examples and best practices.\nCall this FIRST when planning how to analyze, navigate, or audit a codebase.\nReturns practical examples and recommended workflow for each feature area.\n\nAvailable topics: navigation, analysis, testing, documentation, rules, workflow.\nOmit topic to get the full guide.",inputSchema:{type:"object",properties:{topic:{type:"string",description:"Optional topic filter: navigation, analysis, testing, documentation, rules, workflow"}}}},get_agent_instructions:{name:"get_agent_instructions",description:"Get coding guidelines, architectural standards, and JSDoc rules for this project.",inputSchema:{type:"object",properties:{}}},get_custom_rules:{name:"get_custom_rules",description:"List all custom code analysis rules. Rules are stored in JSON files in rules/ directory.",inputSchema:{type:"object",properties:{}}},set_custom_rule:{name:"set_custom_rule",description:"Add or update a custom code analysis rule. Creates ruleset if it does not exist.",inputSchema:{type:"object",properties:{ruleSet:{type:"string",description:'Name of ruleset (e.g., "symbiote", "react", "custom")'},rule:{type:"object",description:"Rule definition with id, name, description, pattern, patternType, replacement, severity, filePattern"}},required:["ruleSet","rule"]}},check_custom_rules:{name:"check_custom_rules",description:"Run custom rules analysis on a directory. Returns violations found.",inputSchema:{type:"object",properties:{path:{type:"string",description:"Path to scan"},ruleSet:{type:"string",description:"Optional: specific ruleset to use"},severity:{type:"string",description:"Optional: filter by severity (error/warning/info)"}},required:["path"]}},get_framework_reference:{name:"get_framework_reference",description:"Get framework-specific AI reference documentation. Auto-detects framework from project or accepts explicit name. Returns full API reference, patterns, and common mistakes as agent context.",inputSchema:{type:"object",properties:{framework:{type:"string",description:'Framework reference name (e.g., "symbiote-3x"). If omitted, auto-detects from path.'},path:{type:"string",description:'Project path for auto-detection (e.g., "src/")'}}}}};
3
3
  export const TOOLS=[e.get_skeleton,e.get_focus_zone,e.get_ai_context,e.invalidate_cache,e.get_usage_guide,e.get_agent_instructions,e.get_custom_rules,e.set_custom_rule,e.check_custom_rules,e.get_framework_reference,{name:"navigate",description:"Navigate the project graph. Actions: expand|deps|usages|call_chain|sub_projects",inputSchema:{type:"object",properties:{action:{type:"string",enum:["expand","deps","usages","call_chain","sub_projects"],description:"Navigation action to perform"},symbol:{type:"string",description:"Symbol name (for expand, deps, usages)"},from:{type:"string",description:"Starting symbol (for call_chain)"},to:{type:"string",description:"Target symbol (for call_chain)"},path:{type:"string",description:"Path to scan (for call_chain, sub_projects)"}},required:["action"]}},{name:"analyze",description:"Code quality analysis. Actions: dead_code|similar_functions|complexity|large_files|outdated_patterns|full_analysis|analysis_summary|undocumented",inputSchema:{type:"object",properties:{action:{type:"string",enum:["dead_code","similar_functions","complexity","large_files","outdated_patterns","full_analysis","analysis_summary","undocumented"],description:"Analysis type to run"},path:{type:"string",description:"Path to scan"},minComplexity:{type:"number",description:"For complexity: minimum threshold (default: 1)"},onlyProblematic:{type:"boolean",description:"For complexity/large_files: only show issues"},threshold:{type:"number",description:"For similar_functions: min similarity % (default: 60)"},includeItems:{type:"boolean",description:"For full_analysis: include individual items"},level:{type:"string",enum:["tests","params","all"],description:"For undocumented: strictness level"},codeOnly:{type:"boolean",description:"For outdated_patterns: only check code"},depsOnly:{type:"boolean",description:"For outdated_patterns: only check deps"}},required:["action","path"]}},{name:"testing",description:"Test checklist management. Actions: pending|pass|fail|summary|reset",inputSchema:{type:"object",properties:{action:{type:"string",enum:["pending","pass","fail","summary","reset"],description:"Test action to perform"},path:{type:"string",description:"Path to scan (for pending, summary)"},testId:{type:"string",description:"Test ID (for pass, fail)"},reason:{type:"string",description:"Failure reason (for fail)"}},required:["action"]}},{name:"filters",description:"Filter configuration. Actions: get|set|add_excludes|remove_excludes|reset",inputSchema:{type:"object",properties:{action:{type:"string",enum:["get","set","add_excludes","remove_excludes","reset"],description:"Filter action to perform"},excludeDirs:{type:"array",items:{type:"string"},description:"For set: directories to exclude"},excludePatterns:{type:"array",items:{type:"string"},description:"For set: file patterns to exclude"},useGitignore:{type:"boolean",description:"For set: use .gitignore patterns"},includeHidden:{type:"boolean",description:"For set: include hidden directories"},dirs:{type:"array",items:{type:"string"},description:"For add_excludes/remove_excludes"}},required:["action"]}},{name:"jsdoc",description:"JSDoc operations. Actions: check_consistency|check_types|generate",inputSchema:{type:"object",properties:{action:{type:"string",enum:["check_consistency","check_types","generate"],description:"JSDoc action to perform"},path:{type:"string",description:"Path to scan"},name:{type:"string",description:"For generate: specific function name"},files:{type:"array",items:{type:"string"},description:"For check_types: specific files"},maxDiagnostics:{type:"number",description:"For check_types: max diagnostics (default: 50)"}},required:["action","path"]}},{name:"docs",description:"Documentation (.ctx) management. Actions: get|generate|check_stale|validate_contracts",inputSchema:{type:"object",properties:{action:{type:"string",enum:["get","generate","check_stale","validate_contracts"],description:"Documentation action to perform"},path:{type:"string",description:"Project root path"},file:{type:"string",description:"For get: specific file docs"},overwrite:{type:"boolean",description:"For generate: overwrite existing (merge preserves descriptions)"},scope:{description:'For generate: "all", "focus" (git diff), or array of file paths'},strict:{type:"boolean",description:"For validate_contracts: report functions missing from .ctx"}},required:["action","path"]}},{name:"compact",description:"Compact code operations. Actions: compact_file|edit|compact_all|beautify|expand_file|expand_project|validate_pipeline|get_mode|set_mode",inputSchema:{type:"object",properties:{action:{type:"string",enum:["compact_file","edit","compact_all","beautify","expand_file","expand_project","validate_pipeline","get_mode","set_mode"],description:"Compact action to perform"},path:{type:"string",description:"Path to file or directory"},symbol:{type:"string",description:"For edit: function/class name to replace"},code:{type:"string",description:"For edit: new code for the symbol"},beautify:{type:"boolean",description:"Beautify output (default: true)"},legend:{type:"boolean",description:"For compact_file: include export legend"},dryRun:{type:"boolean",description:"Preview without modifying"},mode:{type:"number",description:"For set_mode: 1 (compact, recommended) or 2 (full)"},autoValidate:{type:"boolean",description:"For set_mode: auto-validate after edits"},stripJSDoc:{type:"boolean",description:"For set_mode: strip JSDoc when compacting"},strict:{type:"boolean",description:"For validate_pipeline: report fns missing from .ctx"},fix:{type:"boolean",description:"For validate_pipeline: auto-fix all style issues — generates .ctx documentation from readable code, then minifies (headers, imports, indentation, long names). Bidirectional: expand restores from .ctx."}},required:["action"]}},{name:"db",description:"Database analysis. Actions: schema|table_usage|dead_tables",inputSchema:{type:"object",properties:{action:{type:"string",enum:["schema","table_usage","dead_tables"],description:"Database analysis action"},path:{type:"string",description:"Path to scan"},table:{type:"string",description:"For table_usage: filter to specific table"}},required:["action","path"]}}];
@@ -0,0 +1,13 @@
1
+ --- src/mcp/tools.js ---
2
+ @sig 1671e433
3
+ export getGraph(path:string)→g,p,l,c,JSON.parse,m.clear|get or build project graph|uses smart mtime-based caching
4
+ export getSkeleton(path:string)→getGraph,r,o|get compact project skeleton
5
+ export getFocusZone(path:string,options:Object=)→getGraph,u,e.endsWith,c,t,Object.keys|get enriched focus zone|auto-detects from git diff or recent files
6
+ export expand(symbol:string)→getGraph,t.split,e,classes.find,functions.find,e.slice|expand minified symbol to full details|extracts method code if specified
7
+ export deps(symbol:string)→getGraph,edges.filter|get dependency tree for a symbol|shows imports, usedBy, calls
8
+ export usages(symbol:string)→getGraph,e,calls.includes,e.includes,calls.some,c.push|find all usages of a symbol across project
9
+ export getCallChain(options:Object=)→getGraph,e.push,f.add,l.shift,e.split,t.map|Object>→getGraph,caller.push,visitedNodes.add,queue.shift,current.split,sym.split|Object>→getGraph,caller.push,visitedNodes.add,queue.shift,current.split,sym.split|Object>→getGraph,caller.push,visitedNodes.add,queue.shift,current.split,sym.split|find shortest call path between two symbols|uses BFS
10
+ export invalidateCache()→p,l,f,m.clear|delete cache file and clear memory cache
11
+ PATTERNS: smart mtime caching|BFS for call chain|regex for method extraction
12
+ EDGE_CASES: gracefully handles unreadable files/mtimes|handles missing symbols
13
+ y(a)|getGraph — internal helper
package/src/mcp/tools.js CHANGED
@@ -1,4 +1,4 @@
1
- // @ctx .context/src/mcp/tools.ctx
1
+ // @ctx tools.ctx
2
2
  import{parseProject as e,parseFile as t,findJSFiles as n,findAllProjectFiles as r}from"../core/parser.js";
3
3
  import{buildGraph as s,createSkeleton as o}from"../core/graph-builder.js";
4
4
  import{readFileSync as c,statSync as i,writeFileSync as a,existsSync as l,unlinkSync as f}from"fs";
@@ -0,0 +1,10 @@
1
+ --- src/network/backend-lifecycle.js ---
2
+ @sig 3961b656
3
+ B(e)→y,r,o,JSON.parse,process.kill,s|(internal utility)
4
+ export writePortFile(rootPath:string,port:number)→n,l,f,Date.now,y,JSON.stringify|write active backend port and PID to project port file
5
+ export removePortFile(rootPath:string)→y,s|delete project port file on exit
6
+ export listBackends()→r,i,e.endsWith,a,o,JSON.parse|read all valid backend port files in registry directory
7
+ export ensureBackend(rootPath:string,options:Object=)→l,B,a,u,y,Date.now|check if backend running or spawn detached backend.js process
8
+ export startStdioProxy(rootPath:string,options:Object=)→t,o.write,d,Buffer.alloc,p,Buffer.from|proxy stdin/stdout to WebSocket connection via TCP
9
+ PATTERNS: detached process spawning|manual WebSocket client protocol via stdio
10
+ EDGE_CASES: stale port file cleanup|timeout waiting for backend spawn
@@ -1,4 +1,4 @@
1
- // @ctx .context/src/network/backend-lifecycle.ctx
1
+ // @ctx backend-lifecycle.ctx
2
2
  import{createHash as e,randomBytes as t}from"node:crypto";
3
3
  import{existsSync as r,mkdirSync as n,readFileSync as o,writeFileSync as c,unlinkSync as s,readdirSync as i}from"node:fs";
4
4
  import{join as a,resolve as l,basename as f}from"node:path";
@@ -0,0 +1,5 @@
1
+ --- src/network/backend.js ---
2
+ @sig d1e9720c
3
+ cleanup()→o|remove port file on exit
4
+ PATTERNS: executable CLI entrypoint|interval port file polling
5
+ EDGE_CASES: graceful exit on SIGINT/SIGTERM
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- // @ctx .context/src/network/backend.ctx
2
+ // @ctx backend.ctx
3
3
  import{resolve as e}from"node:path";import{startWebServer as r}from"./web-server.js";import{writePortFile as s,removePortFile as o}from"./backend-lifecycle.js";
4
4
  const t=e(process.argv[2]||".");function cleanup(){o(t)}process.on("exit",cleanup),process.on("SIGINT",()=>{cleanup(),process.exit()}),process.on("SIGTERM",()=>{cleanup(),process.exit()});
5
5
  const c=r(t,0),a=setInterval(()=>{const e=c.address();e&&(clearInterval(a),s(t,e.port))},50);
@@ -0,0 +1,9 @@
1
+ --- src/network/local-gateway.js ---
2
+ @sig b22646c7
3
+ h()→r.readFileSync,t.trim,JSON.parse,parseInt|(internal utility)
4
+ d()→r.existsSync,r.readdirSync,t.endsWith,p,o.join,r.readFileSync|(internal utility)
5
+ export registerService(name:string,port:number,options:Object=)→p,l,n,f,c.cleanup,process.on|register local domain/project route and trigger mDNS broadcast
6
+ export getGatewayPort()→h|get active gateway port or fallback to 80
7
+ PATTERNS: singleton global proxy across projects|transparent HTTP/WS routing
8
+ EDGE_CASES: port 80 fallback to 8080 on EACCES|HTML injection for base href
9
+ u(a,b,c)|resolveBackend — internal helper
@@ -1,4 +1,4 @@
1
- // @ctx .context/src/network/local-gateway.ctx
1
+ // @ctx local-gateway.ctx
2
2
  import t from"node:http";import e from"node:net";import r from"node:fs";import o from"node:path";import{registerLocal as n}from"./mdns.js";
3
3
  const s=o.join(process.env.HOME||process.env.USERPROFILE||"/tmp",".local-gateway"),c=o.join(s,"services.json"),i=o.join(s,"gateway.pid"),a=o.join(s,"backends");
4
4
  function p(){try{return JSON.parse(r.readFileSync(c,"utf8"))}catch{return{}}}
@@ -0,0 +1,6 @@
1
+ --- src/network/mdns.js ---
2
+ @sig bc73bb43
3
+ n(e,r)→String,t,n.unref,n.kill|(internal utility)
4
+ export registerLocal(port:number,name:string)→n,c,o|route mDNS registration to dns-sd(mac)|Avahi(linux)|Node.js mcast
5
+ PATTERNS: platform-specific mDNS implementation fallback
6
+ EDGE_CASES: silently ignores UDP errors|kills spawned processes on cleanup
@@ -1,4 +1,4 @@
1
- // @ctx .context/src/network/mdns.ctx
1
+ // @ctx mdns.ctx
2
2
  import{spawn as t}from"node:child_process";import e from"node:dgram";
3
3
  const r="224.0.0.251";
4
4
  export function registerLocal(t,e){if("darwin"===process.platform)return n(t,e);if("linux"===process.platform){const e=c(t);if(e)return e}return o(t)}
@@ -0,0 +1,2 @@
1
+ @names __top__:c=rl,a=pendingMessages,l=resolved,p=rootsRequestId,d=initializeId,n=debugLog
2
+ @names startProxy:e=projectRoot
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
- // @ctx .context/src/network/server.ctx
3
- import e from"node:path";import t from"node:fs";let _v="0.0.0";try{const _d=e.dirname(new URL(import.meta.url).pathname);_v=JSON.parse(t.readFileSync(e.join(_d,"..","..","package.json"),"utf8")).version}catch{}if(process.argv[1]&&(process.argv[1].endsWith("server.js")||process.argv[1].endsWith("project-graph-mcp"))){const[,,o,...r]=process.argv;if("serve"===o){const t=r[0]||".",o=r.indexOf("--port"),s=-1!==o?parseInt(r[o+1],10):0;if(s){const{startWebServer:e}=await import("./web-server.js");e(t,s)}else{const{ensureBackend:o}=await import("./backend-lifecycle.js");try{const r=await o(t,{force:true}),s=e.resolve(t);console.log(`\n ⬡ project-graph-mcp v${_v}`),console.log(" ─────────────────────────────"),console.log(` → http://localhost:${r}/`),console.log(` → Project: ${s}`),console.log(` → MCP WebSocket: ws://127.0.0.1:${r}/mcp-ws\n`)}catch(e){console.error(`Failed to start backend: ${e.message}`),process.exit(1)}}}else if(o){const{runCLI:e}=await import("../cli/cli.js");e(o,r)}else if(process.env.PROJECT_GRAPH_BACKEND){const{startStdioServer:e}=await import("../mcp/mcp-server.js");console.error("Starting Project Graph MCP (stdio, direct)..."),e()}else{const{setRoots:e,getWorkspaceRoot:o}=await import("../core/workspace.js"),{ensureBackend:r,startStdioProxy:s}=await import("./backend-lifecycle.js"),{createInterface:i}=await import("node:readline"),n=t.createWriteStream("/tmp/pg-init-debug.log",{flags:"a"});n.write(`\n=== NEW SESSION ${(new Date).toISOString()} ===\n`);
2
+ // @ctx server.ctx
3
+ import e from"node:path";import t from"node:fs";let _v="0.0.0";try{const _d=e.dirname(new URL(import.meta.url).pathname);_v=JSON.parse(t.readFileSync(e.join(_d,"..","..","package.json"),"utf8")).version}catch{}if(process.argv[1]&&(process.argv[1].endsWith("server.js")||process.argv[1].endsWith("project-graph-mcp"))){const[,,o,...r]=process.argv;if("serve"===o){const t=r[0]||".";console.log("\n [redirect] UI has moved to 'agent-portal'.");console.log(" Running: npx agent-portal\n");const{spawn:s}=await import("child_process");s("npx",["agent-portal"],{stdio:"inherit",cwd:e.resolve(t)})}else if(o){const{runCLI:e}=await import("../cli/cli.js");e(o,r)}else if(process.env.PROJECT_GRAPH_BACKEND){const{startStdioServer:e}=await import("../mcp/mcp-server.js");console.error("Starting Project Graph MCP (stdio, direct)..."),e()}else{const{setRoots:e,getWorkspaceRoot:o}=await import("../core/workspace.js"),{ensureBackend:r,startStdioProxy:s}=await import("./backend-lifecycle.js"),{createInterface:i}=await import("node:readline"),n=t.createWriteStream("/tmp/pg-init-debug.log",{flags:"a"});n.write(`\n=== NEW SESSION ${(new Date).toISOString()} ===\n`);
4
4
  const c=i({input:process.stdin,terminal:!1}),a=[];
5
5
  let l=!1,p=null,d=null;
6
6
  const startProxy=async e=>{if(!l){l=!0,c.removeAllListeners("line"),c.close(),n.write(`RESOLVED: ${e}\n`),n.end();try{const t=await r(e);console.error(`[project-graph] Connected to backend on port ${t} (project: ${e})`),s(t,a)}catch(e){console.error(`[project-graph] Singleton failed (${e.message}), falling back to direct stdio`);const{startStdioServer:t}=await import("../mcp/mcp-server.js");t(a)}}};c.on("line",t=>{try{const r=JSON.parse(t);if(n.write(`IN: ${r.method||`response:${r.id}`}\n`),"initialize"===r.method){d=r.id,r.params?.roots?.length>0&&(e(r.params.roots),n.write("ROOTS from initialize.params\n"));
@@ -0,0 +1,17 @@
1
+ --- src/network/web-server.js ---
2
+ @sig f4957190
3
+ y(e)→Buffer.from,Buffer.alloc,n.writeUInt16BE,BigInt,n.writeBigUInt64BE,Buffer.concat|(internal utility)
4
+ x(e,t)→JSON.stringify,e.send,T.delete|(internal utility)
5
+ S(e,t)→e.split,x|(internal utility)
6
+ w(e)→e.readUInt16BE,e.readBigUInt64BE,Number,e.slice,s.toString|(internal utility)
7
+ P()→N,O,console.log,process.exit,setTimeout|(internal utility)
8
+ O()→clearTimeout|(internal utility)
9
+ k()→d.executeTool|(internal utility)
10
+ N()|(internal utility)
11
+ z()→O,P|(internal utility)
12
+ A(e,a,s,i)→a.get,d.executeTool,n,s,a,o.dirname|(internal utility)
13
+ export startWebServer(rootPath:string,options:Object)→i,o.resolve,o.basename,n.createHash,f.slice,parseInt|init HTTP/WS server, handle routing and WebSocket upgrades
14
+ PATTERNS: custom HTTP routing|manual WebSocket frame parsing without deps
15
+ EDGE_CASES: 15min auto-shutdown|unmasked server frames|ping/pong opcodes
16
+ g(a,b)|serveStatic — internal helper
17
+ u(a)|computeWSAccept — internal helper
@@ -1,8 +1,8 @@
1
- // @ctx .context/src/network/web-server.ctx
1
+ // @ctx web-server.ctx
2
2
  import e from"node:http";import t from"node:fs";import o from"node:path";import n from"node:crypto";import{fileURLToPath as a}from"node:url";import{createRequire as _createRequire}from"node:module";import{WebSocketServer as s}from"ws";import{createServer as i}from"../mcp/mcp-server.js";import c from"../core/event-bus.js";import{registerService as r}from"./local-gateway.js";import{compressFile as _cf}from"../compact/compress.js";import{expandFile as _ef}from"../compact/expand.js";import{setRoots as _setRoots}from"../core/workspace.js";
3
3
  const d=o.dirname(a(import.meta.url)),p=o.join(d,"..","..");const _rq=_createRequire(import.meta.url);let _pkgVersion="0.0.0";try{_pkgVersion=JSON.parse(t.readFileSync(o.join(p,"package.json"),"utf8")).version}catch{}function _rv(k){try{const r=_rq.resolve(k);const marker=o.sep+"node_modules"+o.sep+k.replace(/\//g,o.sep);const idx=r.lastIndexOf(marker);if(idx>=0)return r.substring(0,idx+marker.length);return o.dirname(r)}catch{return o.join(p,"node_modules",...k.split("/"))}}const m=o.join(p,"web"),_symNodeVendor=o.join(p,"vendor","symbiote-node"),h={"symbiote-node":t.existsSync(_symNodeVendor)?_symNodeVendor:_rv("symbiote-node"),symbiote:_rv("@symbiotejs/symbiote")},f={".html":"text/html",".js":"text/javascript",".mjs":"text/javascript",".css":"text/css",".json":"application/json",".svg":"image/svg+xml",".png":"image/png",".ico":"image/x-icon",".woff2":"font/woff2"};
4
4
  function g(e,n){const a=o.normalize(e).replace(/^(\.\.[/\\])+/,""),s=a.match(/^[/\\]?vendor[/\\]([^/\\]+)[/\\]?(.*)/);let i,c;if(s&&h[s[1]]?(c=h[s[1]],i=o.join(c,s[2]||"index.js")):(c=m,i=o.join(m,"/"===a?"index.html":a)),!i.startsWith(c))return n.writeHead(403),void n.end("Forbidden");if(t.existsSync(i)&&t.statSync(i).isDirectory()&&(i=o.join(i,"index.html")),!t.existsSync(i))return n.writeHead(404),void n.end("Not Found");const r=o.extname(i),l=f[r]||"application/octet-stream",d=t.readFileSync(i);n.writeHead(200,{"Content-Type":l,"Cache-Control":"no-cache, no-store, must-revalidate"}),n.end(d)}
5
- function u(e){return n.createHash("sha1").update(e+"258EAFA5-E914-47DA-95CA-5AB5ADF35C70").digest("base64")}
5
+ function u(e){return n.createHash("sha1").update(e+"258EAFA5-E914-47DA-95CA-C5AB0DC85B11").digest("base64")}
6
6
  function y(e){const t=Buffer.from(e,"utf8"),o=t.length;let n;return o<126?(n=Buffer.alloc(2),n[0]=129,n[1]=o):o<65536?(n=Buffer.alloc(4),n[0]=129,n[1]=126,n.writeUInt16BE(o,2)):(n=Buffer.alloc(10),n[0]=129,n[1]=127,n.writeBigUInt64BE(BigInt(o),2)),Buffer.concat([n,t])}
7
7
  function w(e){if(e.length<2)return null;const t=15&e[0],o=!!(128&e[1]);let n=127&e[1],a=2;if(126===n){if(e.length<4)return null;n=e.readUInt16BE(2),a=4}else if(127===n){if(e.length<10)return null;n=Number(e.readBigUInt64BE(2)),a=10}if(o){if(e.length<a+4+n)return null;const o=e.slice(a,a+4);a+=4;const s=e.slice(a,a+n);for(let e=0;e<s.length;e++)s[e]^=o[e%4];return{opcode:t,data:s.toString("utf8"),totalLen:a+n}}return e.length<a+n?null:{opcode:t,data:e.slice(a,a+n).toString("utf8"),totalLen:a+n}}
8
8
  export function startWebServer(t,a){_setRoots([{uri:"file://"+o.resolve(t)}]);const d=i(()=>{}),p=o.basename(o.resolve(t))||"root";let m=1;const _startedAt=Date.now();const h=o.resolve(t),f=n.createHash("md5").update(h).digest("hex"),v=parseInt(f.slice(0,4),16)%360,j={project:{name:p,path:h,color:`hsl(${v}, 65%, 55%)`,agents:0,pid:process.pid},skeleton:null,events:[]};function x(e,t){const o=JSON.stringify({jsonrpc:"2.0",method:e,params:t});for(const e of T)try{e.send(o)}catch{T.delete(e)}}function S(e,t){const o=e.split(".");let n=j;for(let e=0;e<o.length-1;e++)n=n[o[e]];n[o[o.length-1]]=t,x("patch",{path:e,value:t})}async function k(){if(!j.skeleton)try{j.skeleton=await d.executeTool("get_skeleton",{path:t})}catch(e){console.error("[project-graph] Failed to load skeleton:",e.message)}return j.skeleton}const b=new Map,T=new Set;let C=null;const _cache={cs:null,cst:0,as:null,ast:0,fa:null,fat:0};function _clearCache(){_cache.cs=null;_cache.cst=0;_cache.as=null;_cache.ast=0;_cache.fa=null;_cache.fat=0;j.skeleton=null}function N(){return b.size>0||T.size>0}function O(){C&&(clearTimeout(C),C=null);_shutdownAt=0}let _shutdownAt=0;function P(){N()||(O(),_shutdownAt=Date.now()+9e5,C=setTimeout(()=>{N()||(console.log("[project-graph] No clients for 15 min — shutting down."),process.exit(0))},9e5))}function z(){O(),P()}async function A(e,a,s,i){try{let c;const r=a.get("path")||t;switch(e){case"/api/skeleton":c=await d.executeTool("get_skeleton",{path:r});break;case"/api/file":{const e=a.get("path");if(e){const{resolve:n,basename:a,extname:s}=await import("path"),{readFileSync:i,existsSync:r}=await import("fs"),d=n(t,e),_ext=s(e).toLowerCase(),_jsExts=new Set([".js",".mjs",".ts",".tsx"]);if(_jsExts.has(_ext)){const p=a(e,s(e))+".ctx",m=o.resolve(t,".context",o.dirname(e),p),f=await _cf(d,{beautify:false,legend:false}),y=r(m)?Math.ceil(i(m,"utf-8").length/4):0,w=f.compressed+y;c={code:f.code,file:e,codeTok:f.compressed,ctxTok:y,totalTok:w,expanded:f.expanded||f.original,savings:f.savings}}else{try{const raw=i(d,"utf-8"),tok=Math.ceil(raw.length/4);c={code:raw,file:e,codeTok:tok,ctxTok:0,totalTok:tok,expanded:tok,savings:"0%",raw:true}}catch(err){c={code:`// Cannot read: ${err.message}`,file:e}}}}else c={code:"// No file specified",file:""};break}case"/api/compact-file":{const e=a.get("path");if(e){const{resolve:n,extname:_ex2}=await import("path"),{readFileSync:_rf2}=await import("fs"),s=n(t,e),_ext2=_ex2(e).toLowerCase(),_jsExts2=new Set([".js",".mjs",".ts",".tsx"]);if(_jsExts2.has(_ext2)){const i=await _cf(s,{beautify:!1,legend:!1});c={code:i.code,file:e,original:i.original,compressed:i.compressed,savings:i.savings}}else{try{const raw=_rf2(s,"utf-8"),tok=Math.ceil(raw.length/4);c={code:raw,file:e,original:tok,compressed:tok,savings:"0%",raw:true}}catch(err){c={code:`// Cannot read: ${err.message}`,file:e}}}}else c={code:"// No file specified",file:""};break}case"/api/raw-file":{const e=a.get("path");try{const{readFileSync:o}=await import("fs"),{resolve:n,relative:a}=await import("path");c={content:o(n(t,e),"utf-8"),file:e}}catch(t){c={content:`// Cannot read: ${t.message}`,file:e}}break}case"/api/expand-file":{const e=a.get("path");if(e){try{const _pt2=await import("path"),_fs2=await import("fs"),_abs=_pt2.resolve(t,e),_ext3=_pt2.extname(e).toLowerCase(),_jsExts3=new Set([".js",".mjs",".ts",".tsx"]);if(!_jsExts3.has(_ext3)){const raw=_fs2.readFileSync(_abs,"utf-8");c={code:raw,file:e,injected:0};break}let _ctxContent=null;const _ctxName=_pt2.basename(e,_pt2.extname(e))+".ctx";const _ctxPath1=_pt2.join(t,_pt2.dirname(e),_ctxName);const _ctxPath2=_pt2.join(t,".context",_pt2.dirname(e),_ctxName);if(_fs2.existsSync(_ctxPath1))_ctxContent=_fs2.readFileSync(_ctxPath1,"utf-8");else if(_fs2.existsSync(_ctxPath2))_ctxContent=_fs2.readFileSync(_ctxPath2,"utf-8");const _result=await _ef(_abs,_ctxContent);