project-graph-mcp 2.1.13 → 2.2.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.
- package/docs/AGENT_INSTRUCTIONS.md +91 -0
- package/package.json +1 -1
- package/src/.contextignore +22 -0
- package/src/.project-graph-cache.json +1 -1
- package/src/compact/ai-context.js +32 -5
- package/src/compact/instructions.js +6 -2
- package/src/mcp/tool-defs.js +1 -1
- package/src/network/backend-lifecycle.js +2 -1
- package/src/network/local-gateway.js +5 -3
- package/src/network/mdns.js +1 -1
- package/src/network/web-server.js +1 -1
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
|
|
2
|
+
# 🤖 Project Guidelines for AI Agents
|
|
3
|
+
|
|
4
|
+
## 1. Architecture Standards (Symbiote.js)
|
|
5
|
+
- **Component Structure**: Always use Triple-File Partitioning for components:
|
|
6
|
+
- `MyComponent.js`: Class logic (extends Symbiote)
|
|
7
|
+
- `MyComponent.tpl.js`: HTML template (export template)
|
|
8
|
+
- `MyComponent.css.js`: CSS styles (export rootStyles/shadowStyles)
|
|
9
|
+
- **State Management**: Use `this.init$` for local state and `this.sub()` for reactivity.
|
|
10
|
+
- **Directives**: Use `itemize` for lists, `js-d-kit` for static generation.
|
|
11
|
+
|
|
12
|
+
## 2. General Coding Rules
|
|
13
|
+
- **ESM Only**: Use `import` / `export`. No `require`.
|
|
14
|
+
- **No Dependencies**: Avoid adding new npm packages unless critical.
|
|
15
|
+
- **Comments**: Write clear JSDoc for all public methods.
|
|
16
|
+
- **Async/Await**: Prefer async/await over promises.
|
|
17
|
+
|
|
18
|
+
## 3. MCP Tools — Recommended Workflow
|
|
19
|
+
|
|
20
|
+
### Three Modes of `get_ai_context`
|
|
21
|
+
|
|
22
|
+
| Mode | Call | Returns | Tokens |
|
|
23
|
+
|------|------|---------|--------|
|
|
24
|
+
| **Code only** | `includeFiles: ["*"]` | All compressed source files | ~40-50k |
|
|
25
|
+
| **Code + context** | `includeFiles: ["*"], includeSkeleton: true, includeDocs: true` | Source + skeleton + docs | ~50-55k |
|
|
26
|
+
| **Overview** | _(default, no includeFiles)_ | Skeleton + docs only | ~2-3k |
|
|
27
|
+
|
|
28
|
+
### Quick Start: Full Codebase Context
|
|
29
|
+
For small/medium projects (under 100k tokens), load ALL code at once:
|
|
30
|
+
```
|
|
31
|
+
get_ai_context({ path: ".", includeFiles: ["*"] })
|
|
32
|
+
```
|
|
33
|
+
Returns compressed source of all JS files — pure code, no metadata noise.
|
|
34
|
+
|
|
35
|
+
To also get structural overview and documentation:
|
|
36
|
+
```
|
|
37
|
+
get_ai_context({ path: ".", includeFiles: ["*"], includeSkeleton: true, includeDocs: true })
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### Step-by-Step: Large Projects
|
|
41
|
+
1. **Overview**: `get_ai_context({ path: "." })` → skeleton + docs (~2-3k tokens)
|
|
42
|
+
2. **Navigate**: `expand("ClassName")` → read specific class code
|
|
43
|
+
3. **Dependencies**: `deps("symbol")` / `usages("symbol")` → trace connections
|
|
44
|
+
4. **Focus Zone**: `get_focus_zone({ useGitDiff: true })` → recently changed files
|
|
45
|
+
|
|
46
|
+
### After Code Changes
|
|
47
|
+
- Call `invalidate_cache()` to refresh the graph.
|
|
48
|
+
|
|
49
|
+
### .contextignore
|
|
50
|
+
Placed in project root, controls which files are excluded from `includeFiles: ["*"]`.
|
|
51
|
+
Auto-created with sensible defaults (vendor/, *.min.js, chart.js, etc.).
|
|
52
|
+
Users can edit to add project-specific exclusions.
|
|
53
|
+
|
|
54
|
+
## 4. Custom Rules System
|
|
55
|
+
Configurable code analysis with auto-detection.
|
|
56
|
+
|
|
57
|
+
### Available Tools
|
|
58
|
+
- `get_custom_rules`: List all rulesets and their rules
|
|
59
|
+
- `set_custom_rule`: Add or update a rule in a ruleset
|
|
60
|
+
- `check_custom_rules`: Run analysis (auto-detects applicable rulesets)
|
|
61
|
+
|
|
62
|
+
### Auto-Detection
|
|
63
|
+
Rulesets are applied automatically based on:
|
|
64
|
+
1. `package.json` dependencies
|
|
65
|
+
2. Import patterns in source code
|
|
66
|
+
3. Code patterns (e.g., `extends Symbiote`)
|
|
67
|
+
|
|
68
|
+
### Creating New Rules
|
|
69
|
+
Use `set_custom_rule` to add framework-specific rules:
|
|
70
|
+
```json
|
|
71
|
+
{
|
|
72
|
+
"ruleSet": "my-framework-2x",
|
|
73
|
+
"rule": {
|
|
74
|
+
"id": "my-rule-id",
|
|
75
|
+
"name": "Rule Name",
|
|
76
|
+
"description": "What this rule checks",
|
|
77
|
+
"pattern": "badPattern",
|
|
78
|
+
"patternType": "string",
|
|
79
|
+
"replacement": "Use goodPattern instead",
|
|
80
|
+
"severity": "warning",
|
|
81
|
+
"filePattern": "*.js",
|
|
82
|
+
"docs": "https://docs.example.com/rule"
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### Severity Levels
|
|
88
|
+
- `error`: Critical issues that must be fixed
|
|
89
|
+
- `warning`: Important but not blocking
|
|
90
|
+
- `info`: Suggestions and best practices
|
|
91
|
+
|
package/package.json
CHANGED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# Files and directories excluded from AI context (get_ai_context includeFiles: ["*"])
|
|
2
|
+
# Glob patterns — one per line
|
|
3
|
+
|
|
4
|
+
# Vendored / bundled libs
|
|
5
|
+
vendor/
|
|
6
|
+
*.min.js
|
|
7
|
+
*.bundle.js
|
|
8
|
+
chart.js
|
|
9
|
+
d3.js
|
|
10
|
+
three.js
|
|
11
|
+
lodash.js
|
|
12
|
+
jquery*.js
|
|
13
|
+
|
|
14
|
+
# Generated
|
|
15
|
+
dist/
|
|
16
|
+
build/
|
|
17
|
+
coverage/
|
|
18
|
+
*.d.ts
|
|
19
|
+
|
|
20
|
+
# Tests
|
|
21
|
+
*.test.js
|
|
22
|
+
*.spec.js
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":1,"path":"/Users/v.matiyasevich/Documents/GitHub/project-graph-mcp/src","mtimes":{"/Users/v.matiyasevich/Documents/GitHub/project-graph-mcp/src/analysis/analysis-cache.js":1776021880954.865,"/Users/v.matiyasevich/Documents/GitHub/project-graph-mcp/src/analysis/complexity.js":1776020743745.4976,"/Users/v.matiyasevich/Documents/GitHub/project-graph-mcp/src/analysis/custom-rules.js":1776020743746.4695,"/Users/v.matiyasevich/Documents/GitHub/project-graph-mcp/src/analysis/db-analysis.js":1776020743747.036,"/Users/v.matiyasevich/Documents/GitHub/project-graph-mcp/src/analysis/dead-code.js":1776020743747.205,"/Users/v.matiyasevich/Documents/GitHub/project-graph-mcp/src/analysis/full-analysis.js":1776020743747.434,"/Users/v.matiyasevich/Documents/GitHub/project-graph-mcp/src/analysis/jsdoc-checker.js":1776020743747.778,"/Users/v.matiyasevich/Documents/GitHub/project-graph-mcp/src/analysis/jsdoc-generator.js":1776020743748.1309,"/Users/v.matiyasevich/Documents/GitHub/project-graph-mcp/src/analysis/large-files.js":1775932068231.2554,"/Users/v.matiyasevich/Documents/GitHub/project-graph-mcp/src/analysis/outdated-patterns.js":1775932068241.3853,"/Users/v.matiyasevich/Documents/GitHub/project-graph-mcp/src/analysis/similar-functions.js":1776020743748.3684,"/Users/v.matiyasevich/Documents/GitHub/project-graph-mcp/src/analysis/test-annotations.js":1776021880973.3127,"/Users/v.matiyasevich/Documents/GitHub/project-graph-mcp/src/analysis/type-checker.js":1776020743790.671,"/Users/v.matiyasevich/Documents/GitHub/project-graph-mcp/src/analysis/undocumented.js":1776020743791.3872,"/Users/v.matiyasevich/Documents/GitHub/project-graph-mcp/src/cli/cli-handlers.js":1776020743791.534,"/Users/v.matiyasevich/Documents/GitHub/project-graph-mcp/src/cli/cli.js":1776009281031.448,"/Users/v.matiyasevich/Documents/GitHub/project-graph-mcp/src/compact/ai-context.js":1776024855845.1536,"/Users/v.matiyasevich/Documents/GitHub/project-graph-mcp/src/compact/compact-migrate.js":1776025662126.3894,"/Users/v.matiyasevich/Documents/GitHub/project-graph-mcp/src/compact/compact.js":1776025662121.6743,"/Users/v.matiyasevich/Documents/GitHub/project-graph-mcp/src/compact/compress.js":1776024835288.3477,"/Users/v.matiyasevich/Documents/GitHub/project-graph-mcp/src/compact/ctx-resolver.js":1776024988359.2908,"/Users/v.matiyasevich/Documents/GitHub/project-graph-mcp/src/compact/ctx-to-jsdoc.js":1776025604473.7043,"/Users/v.matiyasevich/Documents/GitHub/project-graph-mcp/src/compact/doc-dialect.js":1776021881027.7375,"/Users/v.matiyasevich/Documents/GitHub/project-graph-mcp/src/compact/expand.js":1776025662125.8457,"/Users/v.matiyasevich/Documents/GitHub/project-graph-mcp/src/compact/framework-references.js":1775932068436.373,"/Users/v.matiyasevich/Documents/GitHub/project-graph-mcp/src/compact/instructions.js":1775932068437.6816,"/Users/v.matiyasevich/Documents/GitHub/project-graph-mcp/src/compact/jsdoc-builder.js":1776024792784.1958,"/Users/v.matiyasevich/Documents/GitHub/project-graph-mcp/src/compact/mode-config.js":1776021881032.5818,"/Users/v.matiyasevich/Documents/GitHub/project-graph-mcp/src/compact/split-declarations.js":1776020731393.9968,"/Users/v.matiyasevich/Documents/GitHub/project-graph-mcp/src/compact/validate-pipeline.js":1776025626422.5903,"/Users/v.matiyasevich/Documents/GitHub/project-graph-mcp/src/core/event-bus.js":1776021881034.3083,"/Users/v.matiyasevich/Documents/GitHub/project-graph-mcp/src/core/file-walker.js":1776024739329.9265,"/Users/v.matiyasevich/Documents/GitHub/project-graph-mcp/src/core/filters.js":1776021881038.4585,"/Users/v.matiyasevich/Documents/GitHub/project-graph-mcp/src/core/graph-builder.js":1776020731524.8525,"/Users/v.matiyasevich/Documents/GitHub/project-graph-mcp/src/core/parser.js":1776021881058.1765,"/Users/v.matiyasevich/Documents/GitHub/project-graph-mcp/src/core/utils.js":1776024855849.509,"/Users/v.matiyasevich/Documents/GitHub/project-graph-mcp/src/core/workspace.js":1775932068530.74,"/Users/v.matiyasevich/Documents/GitHub/project-graph-mcp/src/lang/lang-go.js":1775932068546.679,"/Users/v.matiyasevich/Documents/GitHub/project-graph-mcp/src/lang/lang-python.js":1775932068554.9434,"/Users/v.matiyasevich/Documents/GitHub/project-graph-mcp/src/lang/lang-sql.js":1776021881068.4143,"/Users/v.matiyasevich/Documents/GitHub/project-graph-mcp/src/lang/lang-typescript.js":1775932068582.2983,"/Users/v.matiyasevich/Documents/GitHub/project-graph-mcp/src/lang/lang-utils.js":1775932068585.4934,"/Users/v.matiyasevich/Documents/GitHub/project-graph-mcp/src/mcp/mcp-server.js":1776017634423.9546,"/Users/v.matiyasevich/Documents/GitHub/project-graph-mcp/src/mcp/tool-defs.js":1776018403086.5388,"/Users/v.matiyasevich/Documents/GitHub/project-graph-mcp/src/mcp/tools.js":1776021881078.6213,"/Users/v.matiyasevich/Documents/GitHub/project-graph-mcp/src/network/backend-lifecycle.js":1776021881086.6484,"/Users/v.matiyasevich/Documents/GitHub/project-graph-mcp/src/network/backend.js":1775932249992.6167,"/Users/v.matiyasevich/Documents/GitHub/project-graph-mcp/src/network/local-gateway.js":1776020743913.1072,"/Users/v.matiyasevich/Documents/GitHub/project-graph-mcp/src/network/mdns.js":1776020743913.2654,"/Users/v.matiyasevich/Documents/GitHub/project-graph-mcp/src/network/server.js":1776016300633.3318,"/Users/v.matiyasevich/Documents/GitHub/project-graph-mcp/src/network/web-server.js":1776026310263.7048},"graph":{"v":1,"legend":{"computeContentHash":"CH","getCachePath":"CP","readCache":"rC","writeCache":"wC","isCacheValid":"CV","p":"p","u":"u","h":"h","analyzeComplexityFile":"CF","f":"f","getComplexity":"gC","y":"y","g":"g","x":"x","v":"v","$":"$","j":"j","S":"sS","b":"b","getCustomRules":"CR","setCustomRule":"CR1","deleteCustomRule":"CR2","detectProjectRuleSets":"PRS","checkCustomRules":"CR3","getDBSchema":"DBS","getTableUsage":"TU","getDBDeadTables":"DBD","m":"m","getDeadCode":"DC","w":"w","F":"fF","D":"dD","M":"mM","P":"pP","getFullAnalysis":"FA","getAnalysisSummaryOnly":"ASO","d":"d","checkJSDocFile":"JSD","checkJSDocConsistency":"JSD1","generateJSDoc":"JSD2","i":"i","o":"o","s":"s","generateJSDocFor":"JSD3","findJSFiles":"JSF","analyzeFile":"aF","getLargeFiles":"LF","analyzeFilePatterns":"FP","analyzePackageJson":"PJ","getOutdatedPatterns":"OP","getSimilarFunctions":"SF","a":"a","parseAnnotations":"pA","getAllFeatures":"AF","getPendingTests":"PT","markTestPassed":"TP","markTestFailed":"TF","getTestSummary":"TS","resetTestState":"TS1","l":"l","checkTypes":"cT","checkUndocumentedFile":"UF","getUndocumented":"gU","getUndocumentedSummary":"US","O":"oO","R":"rR","printHelp":"pH","runCLI":"CLI","getAiContext":"AC","compactMigrate":"cM","addTopLevelNewlines":"TLN","compactFile":"cF","beautifyFile":"bF","compactProject":"cP","expandProject":"eP","c":"c","compressFile":"cF1","editCompressed":"eC","resolveCtxPath":"CP1","resolveCtxRelPath":"CRP","readCtxFile":"CF1","parseCtxFile":"CF2","injectJSDoc":"JSD4","stripJSDoc":"JSD5","validateCtxContracts":"CC","generateDocDialect":"DD","readContextDocs":"CD","getProjectDocs":"PD","E":"eE","checkStaleness":"cS","generateContextFiles":"CF3","expandFile":"eF","fetchReference":"fR","listAvailable":"lA","getFrameworkReference":"FR","getInstructions":"gI","parseCtxParams":"CP2","sanitize":"sa","buildJSDocBlock":"JSD6","buildJSDocFromRaw":"JSD7","getConfig":"gC1","setConfig":"sC","getModeDescription":"MD","getModeWorkflow":"MW","splitDeclarations":"sD","isSingleLineBlob":"SLB","validatePipeline":"vP","emitToolCall":"TC","emitToolResult":"TR","onToolCall":"TC1","onToolResult":"TR1","removeToolListener":"TL","walkJSFiles":"JSF1","getFilters":"gF","setFilters":"sF","addExcludes":"aE","removeExcludes":"rE","resetFilters":"rF","parseGitignore":"pG","shouldExcludeDir":"ED","shouldExcludeFile":"EF","minifyLegend":"mL","e":"e","buildGraph":"bG","createSkeleton":"cS1","parseFile":"pF","discoverSubProjects":"SP","parseProject":"pP1","findAllProjectFiles":"APF","estimateTokens":"eT","setRoots":"sR","getWorkspaceRoot":"WR","resolvePath":"rP","parseGo":"pG1","extractImports":"eI","getBody":"gB","extractCalls":"eC1","parsePython":"pP2","isSQLString":"SQL","t":"t","extractSQLFromString":"SQL1","parseSQL":"SQL2","n":"n","extractSQLFromCode":"SQL3","extractORMFromCode":"ORM","parseTypeScript":"TS2","extractParams":"eP1","stripStringsAndComments":"SAC","createServer":"cS2","startStdioServer":"SS","getGraph":"gG","getSkeleton":"gS","getFocusZone":"FZ","expand":"ex","deps":"de","usages":"us","getCallChain":"CC1","invalidateCache":"iC","B":"bB","writePortFile":"PF","removePortFile":"PF1","listBackends":"lB","ensureBackend":"eB","startStdioProxy":"SP1","cleanup":"cl","registerService":"rS","getGatewayPort":"GP","registerLocal":"rL","k":"k","N":"nN","z":"z","A":"aA","startWebServer":"WS"},"reverseLegend":{"CH":"computeContentHash","CP":"getCachePath","rC":"readCache","wC":"writeCache","CV":"isCacheValid","p":"p","u":"u","h":"h","CF":"analyzeComplexityFile","f":"f","gC":"getComplexity","y":"y","g":"g","x":"x","v":"v","$":"$","j":"j","sS":"S","b":"b","CR":"getCustomRules","CR1":"setCustomRule","CR2":"deleteCustomRule","PRS":"detectProjectRuleSets","CR3":"checkCustomRules","DBS":"getDBSchema","TU":"getTableUsage","DBD":"getDBDeadTables","m":"m","DC":"getDeadCode","w":"w","fF":"F","dD":"D","mM":"M","pP":"P","FA":"getFullAnalysis","ASO":"getAnalysisSummaryOnly","d":"d","JSD":"checkJSDocFile","JSD1":"checkJSDocConsistency","JSD2":"generateJSDoc","i":"i","o":"o","s":"s","JSD3":"generateJSDocFor","JSF":"findJSFiles","aF":"analyzeFile","LF":"getLargeFiles","FP":"analyzeFilePatterns","PJ":"analyzePackageJson","OP":"getOutdatedPatterns","SF":"getSimilarFunctions","a":"a","pA":"parseAnnotations","AF":"getAllFeatures","PT":"getPendingTests","TP":"markTestPassed","TF":"markTestFailed","TS":"getTestSummary","TS1":"resetTestState","l":"l","cT":"checkTypes","UF":"checkUndocumentedFile","gU":"getUndocumented","US":"getUndocumentedSummary","oO":"O","rR":"R","pH":"printHelp","CLI":"runCLI","AC":"getAiContext","cM":"compactMigrate","TLN":"addTopLevelNewlines","cF":"compactFile","bF":"beautifyFile","cP":"compactProject","eP":"expandProject","c":"c","cF1":"compressFile","eC":"editCompressed","CP1":"resolveCtxPath","CRP":"resolveCtxRelPath","CF1":"readCtxFile","CF2":"parseCtxFile","JSD4":"injectJSDoc","JSD5":"stripJSDoc","CC":"validateCtxContracts","DD":"generateDocDialect","CD":"readContextDocs","PD":"getProjectDocs","eE":"E","cS":"checkStaleness","CF3":"generateContextFiles","eF":"expandFile","fR":"fetchReference","lA":"listAvailable","FR":"getFrameworkReference","gI":"getInstructions","CP2":"parseCtxParams","sa":"sanitize","JSD6":"buildJSDocBlock","JSD7":"buildJSDocFromRaw","gC1":"getConfig","sC":"setConfig","MD":"getModeDescription","MW":"getModeWorkflow","sD":"splitDeclarations","SLB":"isSingleLineBlob","vP":"validatePipeline","TC":"emitToolCall","TR":"emitToolResult","TC1":"onToolCall","TR1":"onToolResult","TL":"removeToolListener","JSF1":"walkJSFiles","gF":"getFilters","sF":"setFilters","aE":"addExcludes","rE":"removeExcludes","rF":"resetFilters","pG":"parseGitignore","ED":"shouldExcludeDir","EF":"shouldExcludeFile","mL":"minifyLegend","e":"e","bG":"buildGraph","cS1":"createSkeleton","pF":"parseFile","SP":"discoverSubProjects","pP1":"parseProject","APF":"findAllProjectFiles","eT":"estimateTokens","sR":"setRoots","WR":"getWorkspaceRoot","rP":"resolvePath","pG1":"parseGo","eI":"extractImports","gB":"getBody","eC1":"extractCalls","pP2":"parsePython","SQL":"isSQLString","t":"t","SQL1":"extractSQLFromString","SQL2":"parseSQL","n":"n","SQL3":"extractSQLFromCode","ORM":"extractORMFromCode","TS2":"parseTypeScript","eP1":"extractParams","SAC":"stripStringsAndComments","cS2":"createServer","SS":"startStdioServer","gG":"getGraph","gS":"getSkeleton","FZ":"getFocusZone","ex":"expand","de":"deps","us":"usages","CC1":"getCallChain","iC":"invalidateCache","bB":"B","PF":"writePortFile","PF1":"removePortFile","lB":"listBackends","eB":"ensureBackend","SP1":"startStdioProxy","cl":"cleanup","rS":"registerService","GP":"getGatewayPort","rL":"registerLocal","k":"k","nN":"N","z":"z","aA":"A","WS":"startWebServer"},"stats":{"files":51,"classes":0,"functions":240,"tables":0},"nodes":{"CH":{"t":"F","e":true,"f":"analysis/analysis-cache.js"},"CP":{"t":"F","e":true,"f":"analysis/analysis-cache.js"},"rC":{"t":"F","e":true,"f":"analysis/analysis-cache.js"},"wC":{"t":"F","e":true,"f":"analysis/analysis-cache.js"},"CV":{"t":"F","e":true,"f":"analysis/analysis-cache.js"},"p":{"t":"F","e":false,"f":"network/local-gateway.js"},"u":{"t":"F","e":false,"f":"network/web-server.js"},"h":{"t":"F","e":false,"f":"network/local-gateway.js"},"CF":{"t":"F","e":true,"f":"analysis/complexity.js"},"f":{"t":"F","e":false,"f":"network/local-gateway.js"},"gC":{"t":"F","e":true,"f":"analysis/complexity.js"},"y":{"t":"F","e":false,"f":"network/web-server.js"},"g":{"t":"F","e":false,"f":"network/web-server.js"},"x":{"t":"F","e":false,"f":"network/web-server.js"},"v":{"t":"F","e":false,"f":"core/parser.js"},"$":{"t":"F","e":false,"f":"analysis/custom-rules.js"},"j":{"t":"F","e":false,"f":"core/parser.js"},"sS":{"t":"F","e":false,"f":"network/web-server.js"},"b":{"t":"F","e":false,"f":"analysis/custom-rules.js"},"CR":{"t":"F","e":true,"f":"analysis/custom-rules.js"},"CR1":{"t":"F","e":true,"f":"analysis/custom-rules.js"},"CR2":{"t":"F","e":true,"f":"analysis/custom-rules.js"},"PRS":{"t":"F","e":true,"f":"analysis/custom-rules.js"},"CR3":{"t":"F","e":true,"f":"analysis/custom-rules.js"},"DBS":{"t":"F","e":true,"f":"analysis/db-analysis.js"},"TU":{"t":"F","e":true,"f":"analysis/db-analysis.js"},"DBD":{"t":"F","e":true,"f":"analysis/db-analysis.js"},"m":{"t":"F","e":false,"f":"analysis/similar-functions.js"},"DC":{"t":"F","e":true,"f":"analysis/dead-code.js"},"w":{"t":"F","e":false,"f":"network/web-server.js"},"fF":{"t":"F","e":false,"f":"compact/ctx-to-jsdoc.js"},"dD":{"t":"F","e":false,"f":"compact/validate-pipeline.js"},"mM":{"t":"F","e":false,"f":"analysis/full-analysis.js"},"pP":{"t":"F","e":false,"f":"network/web-server.js"},"FA":{"t":"F","e":true,"f":"analysis/full-analysis.js"},"ASO":{"t":"F","e":true,"f":"analysis/full-analysis.js"},"d":{"t":"F","e":false,"f":"network/local-gateway.js"},"JSD":{"t":"F","e":true,"f":"analysis/jsdoc-checker.js"},"JSD1":{"t":"F","e":true,"f":"analysis/jsdoc-checker.js"},"JSD2":{"t":"F","e":true,"f":"analysis/jsdoc-generator.js"},"i":{"t":"F","e":false,"f":"analysis/type-checker.js"},"o":{"t":"F","e":false,"f":"network/mdns.js"},"s":{"t":"F","e":false,"f":"analysis/jsdoc-generator.js"},"JSD3":{"t":"F","e":true,"f":"analysis/jsdoc-generator.js"},"JSF":{"t":"F","e":true,"f":"core/parser.js"},"aF":{"t":"F","e":false,"f":"analysis/large-files.js"},"LF":{"t":"F","e":true,"f":"analysis/large-files.js"},"FP":{"t":"F","e":false,"f":"analysis/outdated-patterns.js"},"PJ":{"t":"F","e":false,"f":"analysis/outdated-patterns.js"},"OP":{"t":"F","e":true,"f":"analysis/outdated-patterns.js"},"SF":{"t":"F","e":true,"f":"analysis/similar-functions.js"},"a":{"t":"F","e":false,"f":"analysis/test-annotations.js"},"pA":{"t":"F","e":true,"f":"analysis/test-annotations.js"},"AF":{"t":"F","e":true,"f":"analysis/test-annotations.js"},"PT":{"t":"F","e":true,"f":"analysis/test-annotations.js"},"TP":{"t":"F","e":true,"f":"analysis/test-annotations.js"},"TF":{"t":"F","e":true,"f":"analysis/test-annotations.js"},"TS":{"t":"F","e":true,"f":"analysis/test-annotations.js"},"TS1":{"t":"F","e":true,"f":"analysis/test-annotations.js"},"l":{"t":"F","e":false,"f":"network/local-gateway.js"},"cT":{"t":"F","e":true,"f":"analysis/type-checker.js"},"UF":{"t":"F","e":true,"f":"analysis/undocumented.js"},"gU":{"t":"F","e":true,"f":"analysis/undocumented.js"},"US":{"t":"F","e":true,"f":"analysis/undocumented.js"},"oO":{"t":"F","e":false,"f":"network/web-server.js"},"rR":{"t":"F","e":false,"f":"cli/cli-handlers.js"},"pH":{"t":"F","e":true,"f":"cli/cli.js"},"CLI":{"t":"F","e":true,"f":"cli/cli.js"},"AC":{"t":"F","e":true,"f":"compact/ai-context.js"},"cM":{"t":"F","e":true,"f":"compact/compact-migrate.js"},"TLN":{"t":"F","e":false,"f":"compact/compact.js"},"cF":{"t":"F","e":false,"f":"compact/compact.js"},"bF":{"t":"F","e":false,"f":"compact/compact.js"},"cP":{"t":"F","e":true,"f":"compact/compact.js"},"eP":{"t":"F","e":true,"f":"compact/expand.js"},"c":{"t":"F","e":false,"f":"network/mdns.js"},"cF1":{"t":"F","e":true,"f":"compact/compress.js"},"eC":{"t":"F","e":true,"f":"compact/compress.js"},"CP1":{"t":"F","e":true,"f":"compact/ctx-resolver.js"},"CRP":{"t":"F","e":true,"f":"compact/ctx-resolver.js"},"CF1":{"t":"F","e":true,"f":"compact/ctx-resolver.js"},"CF2":{"t":"F","e":true,"f":"compact/ctx-to-jsdoc.js"},"JSD4":{"t":"F","e":true,"f":"compact/ctx-to-jsdoc.js"},"JSD5":{"t":"F","e":true,"f":"compact/ctx-to-jsdoc.js"},"CC":{"t":"F","e":true,"f":"compact/ctx-to-jsdoc.js"},"DD":{"t":"F","e":true,"f":"compact/doc-dialect.js"},"CD":{"t":"F","e":true,"f":"compact/doc-dialect.js"},"PD":{"t":"F","e":true,"f":"compact/doc-dialect.js"},"eE":{"t":"F","e":false,"f":"core/parser.js"},"cS":{"t":"F","e":true,"f":"compact/doc-dialect.js"},"CF3":{"t":"F","e":true,"f":"compact/doc-dialect.js"},"eF":{"t":"F","e":true,"f":"compact/expand.js"},"fR":{"t":"F","e":false,"f":"compact/framework-references.js"},"lA":{"t":"F","e":false,"f":"compact/framework-references.js"},"FR":{"t":"F","e":true,"f":"compact/framework-references.js"},"gI":{"t":"F","e":true,"f":"compact/instructions.js"},"CP2":{"t":"F","e":true,"f":"compact/jsdoc-builder.js"},"sa":{"t":"F","e":false,"f":"compact/jsdoc-builder.js"},"JSD6":{"t":"F","e":true,"f":"compact/jsdoc-builder.js"},"JSD7":{"t":"F","e":true,"f":"compact/jsdoc-builder.js"},"gC1":{"t":"F","e":true,"f":"compact/mode-config.js"},"sC":{"t":"F","e":true,"f":"compact/mode-config.js"},"MD":{"t":"F","e":true,"f":"compact/mode-config.js"},"MW":{"t":"F","e":true,"f":"compact/mode-config.js"},"sD":{"t":"F","e":true,"f":"compact/split-declarations.js"},"SLB":{"t":"F","e":true,"f":"compact/split-declarations.js"},"vP":{"t":"F","e":true,"f":"compact/validate-pipeline.js"},"TC":{"t":"F","e":true,"f":"core/event-bus.js"},"TR":{"t":"F","e":true,"f":"core/event-bus.js"},"TC1":{"t":"F","e":true,"f":"core/event-bus.js"},"TR1":{"t":"F","e":true,"f":"core/event-bus.js"},"TL":{"t":"F","e":true,"f":"core/event-bus.js"},"JSF1":{"t":"F","e":true,"f":"core/file-walker.js"},"gF":{"t":"F","e":true,"f":"core/filters.js"},"sF":{"t":"F","e":true,"f":"core/filters.js"},"aE":{"t":"F","e":true,"f":"core/filters.js"},"rE":{"t":"F","e":true,"f":"core/filters.js"},"rF":{"t":"F","e":true,"f":"core/filters.js"},"pG":{"t":"F","e":true,"f":"core/filters.js"},"ED":{"t":"F","e":true,"f":"core/filters.js"},"EF":{"t":"F","e":true,"f":"core/filters.js"},"mL":{"t":"F","e":true,"f":"core/graph-builder.js"},"e":{"t":"F","e":false,"f":"core/graph-builder.js"},"bG":{"t":"F","e":true,"f":"core/graph-builder.js"},"cS1":{"t":"F","e":true,"f":"core/graph-builder.js"},"pF":{"t":"F","e":true,"f":"core/parser.js"},"SP":{"t":"F","e":true,"f":"core/parser.js"},"pP1":{"t":"F","e":true,"f":"core/parser.js"},"APF":{"t":"F","e":true,"f":"core/parser.js"},"eT":{"t":"F","e":true,"f":"core/utils.js"},"sR":{"t":"F","e":true,"f":"core/workspace.js"},"WR":{"t":"F","e":true,"f":"core/workspace.js"},"rP":{"t":"F","e":true,"f":"core/workspace.js"},"pG1":{"t":"F","e":true,"f":"lang/lang-go.js"},"eI":{"t":"F","e":false,"f":"lang/lang-go.js"},"gB":{"t":"F","e":false,"f":"lang/lang-go.js"},"eC1":{"t":"F","e":false,"f":"lang/lang-go.js"},"pP2":{"t":"F","e":true,"f":"lang/lang-python.js"},"SQL":{"t":"F","e":true,"f":"lang/lang-sql.js"},"t":{"t":"F","e":false,"f":"lang/lang-sql.js"},"SQL1":{"t":"F","e":true,"f":"lang/lang-sql.js"},"SQL2":{"t":"F","e":true,"f":"lang/lang-sql.js"},"n":{"t":"F","e":false,"f":"network/mdns.js"},"SQL3":{"t":"F","e":true,"f":"lang/lang-sql.js"},"ORM":{"t":"F","e":true,"f":"lang/lang-sql.js"},"TS2":{"t":"F","e":true,"f":"lang/lang-typescript.js"},"eP1":{"t":"F","e":false,"f":"lang/lang-typescript.js"},"SAC":{"t":"F","e":true,"f":"lang/lang-utils.js"},"cS2":{"t":"F","e":true,"f":"mcp/mcp-server.js"},"SS":{"t":"F","e":true,"f":"mcp/mcp-server.js"},"gG":{"t":"F","e":true,"f":"mcp/tools.js"},"gS":{"t":"F","e":true,"f":"mcp/tools.js"},"FZ":{"t":"F","e":true,"f":"mcp/tools.js"},"ex":{"t":"F","e":true,"f":"mcp/tools.js"},"de":{"t":"F","e":true,"f":"mcp/tools.js"},"us":{"t":"F","e":true,"f":"mcp/tools.js"},"CC1":{"t":"F","e":true,"f":"mcp/tools.js"},"iC":{"t":"F","e":true,"f":"mcp/tools.js"},"bB":{"t":"F","e":false,"f":"network/backend-lifecycle.js"},"PF":{"t":"F","e":true,"f":"network/backend-lifecycle.js"},"PF1":{"t":"F","e":true,"f":"network/backend-lifecycle.js"},"lB":{"t":"F","e":true,"f":"network/backend-lifecycle.js"},"eB":{"t":"F","e":true,"f":"network/backend-lifecycle.js"},"SP1":{"t":"F","e":true,"f":"network/backend-lifecycle.js"},"cl":{"t":"F","e":false,"f":"network/backend.js"},"rS":{"t":"F","e":true,"f":"network/local-gateway.js"},"GP":{"t":"F","e":true,"f":"network/local-gateway.js"},"rL":{"t":"F","e":true,"f":"network/mdns.js"},"k":{"t":"F","e":false,"f":"network/web-server.js"},"nN":{"t":"F","e":false,"f":"network/web-server.js"},"z":{"t":"F","e":false,"f":"network/web-server.js"},"aA":{"t":"F","e":false,"f":"network/web-server.js"},"WS":{"t":"F","e":true,"f":"network/web-server.js"}},"edges":[],"orphans":["p","u","h","f","y","g","x","v","$","j","S","b","m","w","F","D","M","P","d","i","o","s","analyzeFile","analyzeFilePatterns","analyzePackageJson","a","l","O","R","addTopLevelNewlines","compactFile","beautifyFile","c","E","fetchReference","listAvailable","sanitize","e","extractImports","getBody","extractCalls","t","n","extractParams","B","cleanup","k","N","z","A"],"duplicates":{},"files":["analysis/analysis-cache.js","analysis/complexity.js","analysis/custom-rules.js","analysis/db-analysis.js","analysis/dead-code.js","analysis/full-analysis.js","analysis/jsdoc-checker.js","analysis/jsdoc-generator.js","analysis/large-files.js","analysis/outdated-patterns.js","analysis/similar-functions.js","analysis/test-annotations.js","analysis/type-checker.js","analysis/undocumented.js","cli/cli-handlers.js","cli/cli.js","compact/ai-context.js","compact/compact-migrate.js","compact/compact.js","compact/compress.js","compact/ctx-resolver.js","compact/ctx-to-jsdoc.js","compact/doc-dialect.js","compact/expand.js","compact/framework-references.js","compact/instructions.js","compact/jsdoc-builder.js","compact/mode-config.js","compact/split-declarations.js","compact/validate-pipeline.js","core/event-bus.js","core/file-walker.js","core/filters.js","core/graph-builder.js","core/parser.js","core/utils.js","core/workspace.js","lang/lang-go.js","lang/lang-python.js","lang/lang-sql.js","lang/lang-typescript.js","lang/lang-utils.js","mcp/mcp-server.js","mcp/tool-defs.js","mcp/tools.js","network/backend-lifecycle.js","network/backend.js","network/local-gateway.js","network/mdns.js","network/server.js","network/web-server.js"]}}
|
|
1
|
+
{"version":1,"path":"/Users/v.matiyasevich/Documents/GitHub/project-graph-mcp/src","mtimes":{"/Users/v.matiyasevich/Documents/GitHub/project-graph-mcp/src/analysis/analysis-cache.js":1776021880954.865,"/Users/v.matiyasevich/Documents/GitHub/project-graph-mcp/src/analysis/complexity.js":1776020743745.4976,"/Users/v.matiyasevich/Documents/GitHub/project-graph-mcp/src/analysis/custom-rules.js":1776020743746.4695,"/Users/v.matiyasevich/Documents/GitHub/project-graph-mcp/src/analysis/db-analysis.js":1776020743747.036,"/Users/v.matiyasevich/Documents/GitHub/project-graph-mcp/src/analysis/dead-code.js":1776020743747.205,"/Users/v.matiyasevich/Documents/GitHub/project-graph-mcp/src/analysis/full-analysis.js":1776020743747.434,"/Users/v.matiyasevich/Documents/GitHub/project-graph-mcp/src/analysis/jsdoc-checker.js":1776020743747.778,"/Users/v.matiyasevich/Documents/GitHub/project-graph-mcp/src/analysis/jsdoc-generator.js":1776020743748.1309,"/Users/v.matiyasevich/Documents/GitHub/project-graph-mcp/src/analysis/large-files.js":1775932068231.2554,"/Users/v.matiyasevich/Documents/GitHub/project-graph-mcp/src/analysis/outdated-patterns.js":1775932068241.3853,"/Users/v.matiyasevich/Documents/GitHub/project-graph-mcp/src/analysis/similar-functions.js":1776020743748.3684,"/Users/v.matiyasevich/Documents/GitHub/project-graph-mcp/src/analysis/test-annotations.js":1776021880973.3127,"/Users/v.matiyasevich/Documents/GitHub/project-graph-mcp/src/analysis/type-checker.js":1776020743790.671,"/Users/v.matiyasevich/Documents/GitHub/project-graph-mcp/src/analysis/undocumented.js":1776020743791.3872,"/Users/v.matiyasevich/Documents/GitHub/project-graph-mcp/src/cli/cli-handlers.js":1776020743791.534,"/Users/v.matiyasevich/Documents/GitHub/project-graph-mcp/src/cli/cli.js":1776009281031.448,"/Users/v.matiyasevich/Documents/GitHub/project-graph-mcp/src/compact/ai-context.js":1776090090709.129,"/Users/v.matiyasevich/Documents/GitHub/project-graph-mcp/src/compact/compact-migrate.js":1776025662126.3894,"/Users/v.matiyasevich/Documents/GitHub/project-graph-mcp/src/compact/compact.js":1776025662121.6743,"/Users/v.matiyasevich/Documents/GitHub/project-graph-mcp/src/compact/compress.js":1776077544228.2026,"/Users/v.matiyasevich/Documents/GitHub/project-graph-mcp/src/compact/ctx-resolver.js":1776024988359.2908,"/Users/v.matiyasevich/Documents/GitHub/project-graph-mcp/src/compact/ctx-to-jsdoc.js":1776025604473.7043,"/Users/v.matiyasevich/Documents/GitHub/project-graph-mcp/src/compact/doc-dialect.js":1776021881027.7375,"/Users/v.matiyasevich/Documents/GitHub/project-graph-mcp/src/compact/expand.js":1776025662125.8457,"/Users/v.matiyasevich/Documents/GitHub/project-graph-mcp/src/compact/framework-references.js":1775932068436.373,"/Users/v.matiyasevich/Documents/GitHub/project-graph-mcp/src/compact/instructions.js":1776089868557.5547,"/Users/v.matiyasevich/Documents/GitHub/project-graph-mcp/src/compact/jsdoc-builder.js":1776024792784.1958,"/Users/v.matiyasevich/Documents/GitHub/project-graph-mcp/src/compact/mode-config.js":1776021881032.5818,"/Users/v.matiyasevich/Documents/GitHub/project-graph-mcp/src/compact/split-declarations.js":1776020731393.9968,"/Users/v.matiyasevich/Documents/GitHub/project-graph-mcp/src/compact/validate-pipeline.js":1776025626422.5903,"/Users/v.matiyasevich/Documents/GitHub/project-graph-mcp/src/core/event-bus.js":1776021881034.3083,"/Users/v.matiyasevich/Documents/GitHub/project-graph-mcp/src/core/file-walker.js":1776024739329.9265,"/Users/v.matiyasevich/Documents/GitHub/project-graph-mcp/src/core/filters.js":1776021881038.4585,"/Users/v.matiyasevich/Documents/GitHub/project-graph-mcp/src/core/graph-builder.js":1776020731524.8525,"/Users/v.matiyasevich/Documents/GitHub/project-graph-mcp/src/core/parser.js":1776021881058.1765,"/Users/v.matiyasevich/Documents/GitHub/project-graph-mcp/src/core/utils.js":1776024855849.509,"/Users/v.matiyasevich/Documents/GitHub/project-graph-mcp/src/core/workspace.js":1775932068530.74,"/Users/v.matiyasevich/Documents/GitHub/project-graph-mcp/src/lang/lang-go.js":1775932068546.679,"/Users/v.matiyasevich/Documents/GitHub/project-graph-mcp/src/lang/lang-python.js":1775932068554.9434,"/Users/v.matiyasevich/Documents/GitHub/project-graph-mcp/src/lang/lang-sql.js":1776021881068.4143,"/Users/v.matiyasevich/Documents/GitHub/project-graph-mcp/src/lang/lang-typescript.js":1775932068582.2983,"/Users/v.matiyasevich/Documents/GitHub/project-graph-mcp/src/lang/lang-utils.js":1775932068585.4934,"/Users/v.matiyasevich/Documents/GitHub/project-graph-mcp/src/mcp/mcp-server.js":1776087281350.1672,"/Users/v.matiyasevich/Documents/GitHub/project-graph-mcp/src/mcp/tool-defs.js":1776089667319.6912,"/Users/v.matiyasevich/Documents/GitHub/project-graph-mcp/src/mcp/tools.js":1776021881078.6213,"/Users/v.matiyasevich/Documents/GitHub/project-graph-mcp/src/network/backend-lifecycle.js":1776087982767.4238,"/Users/v.matiyasevich/Documents/GitHub/project-graph-mcp/src/network/backend.js":1775932249992.6167,"/Users/v.matiyasevich/Documents/GitHub/project-graph-mcp/src/network/local-gateway.js":1776081624832.668,"/Users/v.matiyasevich/Documents/GitHub/project-graph-mcp/src/network/mdns.js":1776020743913.2654,"/Users/v.matiyasevich/Documents/GitHub/project-graph-mcp/src/network/server.js":1776088001724.466,"/Users/v.matiyasevich/Documents/GitHub/project-graph-mcp/src/network/web-server.js":1776087374451.2217},"graph":{"v":1,"legend":{"computeContentHash":"CH","getCachePath":"CP","readCache":"rC","writeCache":"wC","isCacheValid":"CV","p":"p","u":"u","h":"h","analyzeComplexityFile":"CF","f":"f","getComplexity":"gC","y":"y","g":"g","x":"x","v":"v","$":"$","j":"j","S":"sS","b":"b","getCustomRules":"CR","setCustomRule":"CR1","deleteCustomRule":"CR2","detectProjectRuleSets":"PRS","checkCustomRules":"CR3","getDBSchema":"DBS","getTableUsage":"TU","getDBDeadTables":"DBD","m":"m","getDeadCode":"DC","w":"w","F":"fF","D":"dD","M":"mM","P":"pP","getFullAnalysis":"FA","getAnalysisSummaryOnly":"ASO","d":"d","checkJSDocFile":"JSD","checkJSDocConsistency":"JSD1","generateJSDoc":"JSD2","i":"i","o":"o","s":"s","generateJSDocFor":"JSD3","findJSFiles":"JSF","analyzeFile":"aF","getLargeFiles":"LF","analyzeFilePatterns":"FP","analyzePackageJson":"PJ","getOutdatedPatterns":"OP","getSimilarFunctions":"SF","a":"a","parseAnnotations":"pA","getAllFeatures":"AF","getPendingTests":"PT","markTestPassed":"TP","markTestFailed":"TF","getTestSummary":"TS","resetTestState":"TS1","l":"l","checkTypes":"cT","checkUndocumentedFile":"UF","getUndocumented":"gU","getUndocumentedSummary":"US","O":"oO","R":"rR","printHelp":"pH","runCLI":"CLI","_loadIgnore":"_I","_parseIgnore":"_I1","_shouldIgnore":"_I2","getAiContext":"AC","compactMigrate":"cM","addTopLevelNewlines":"TLN","compactFile":"cF","beautifyFile":"bF","compactProject":"cP","expandProject":"eP","c":"c","compressFile":"cF1","editCompressed":"eC","resolveCtxPath":"CP1","resolveCtxRelPath":"CRP","readCtxFile":"CF1","parseCtxFile":"CF2","injectJSDoc":"JSD4","stripJSDoc":"JSD5","validateCtxContracts":"CC","generateDocDialect":"DD","readContextDocs":"CD","getProjectDocs":"PD","E":"eE","checkStaleness":"cS","generateContextFiles":"CF3","expandFile":"eF","fetchReference":"fR","listAvailable":"lA","getFrameworkReference":"FR","getInstructions":"gI","parseCtxParams":"CP2","sanitize":"sa","buildJSDocBlock":"JSD6","buildJSDocFromRaw":"JSD7","getConfig":"gC1","setConfig":"sC","getModeDescription":"MD","getModeWorkflow":"MW","splitDeclarations":"sD","isSingleLineBlob":"SLB","validatePipeline":"vP","emitToolCall":"TC","emitToolResult":"TR","onToolCall":"TC1","onToolResult":"TR1","removeToolListener":"TL","walkJSFiles":"JSF1","getFilters":"gF","setFilters":"sF","addExcludes":"aE","removeExcludes":"rE","resetFilters":"rF","parseGitignore":"pG","shouldExcludeDir":"ED","shouldExcludeFile":"EF","minifyLegend":"mL","e":"e","buildGraph":"bG","createSkeleton":"cS1","parseFile":"pF","discoverSubProjects":"SP","parseProject":"pP1","findAllProjectFiles":"APF","estimateTokens":"eT","setRoots":"sR","getWorkspaceRoot":"WR","resolvePath":"rP","parseGo":"pG1","extractImports":"eI","getBody":"gB","extractCalls":"eC1","parsePython":"pP2","isSQLString":"SQL","t":"t","extractSQLFromString":"SQL1","parseSQL":"SQL2","n":"n","extractSQLFromCode":"SQL3","extractORMFromCode":"ORM","parseTypeScript":"TS2","extractParams":"eP1","stripStringsAndComments":"SAC","createServer":"cS2","startStdioServer":"SS","getGraph":"gG","getSkeleton":"gS","getFocusZone":"FZ","expand":"ex","deps":"de","usages":"us","getCallChain":"CC1","invalidateCache":"iC","_getVersion":"_V","B":"bB","writePortFile":"PF","removePortFile":"PF1","listBackends":"lB","ensureBackend":"eB","startStdioProxy":"SP1","cleanup":"cl","registerService":"rS","getGatewayPort":"GP","registerLocal":"rL","_rv":"_r","k":"k","_clearCache":"_C","N":"nN","z":"z","A":"aA","startWebServer":"WS"},"reverseLegend":{"CH":"computeContentHash","CP":"getCachePath","rC":"readCache","wC":"writeCache","CV":"isCacheValid","p":"p","u":"u","h":"h","CF":"analyzeComplexityFile","f":"f","gC":"getComplexity","y":"y","g":"g","x":"x","v":"v","$":"$","j":"j","sS":"S","b":"b","CR":"getCustomRules","CR1":"setCustomRule","CR2":"deleteCustomRule","PRS":"detectProjectRuleSets","CR3":"checkCustomRules","DBS":"getDBSchema","TU":"getTableUsage","DBD":"getDBDeadTables","m":"m","DC":"getDeadCode","w":"w","fF":"F","dD":"D","mM":"M","pP":"P","FA":"getFullAnalysis","ASO":"getAnalysisSummaryOnly","d":"d","JSD":"checkJSDocFile","JSD1":"checkJSDocConsistency","JSD2":"generateJSDoc","i":"i","o":"o","s":"s","JSD3":"generateJSDocFor","JSF":"findJSFiles","aF":"analyzeFile","LF":"getLargeFiles","FP":"analyzeFilePatterns","PJ":"analyzePackageJson","OP":"getOutdatedPatterns","SF":"getSimilarFunctions","a":"a","pA":"parseAnnotations","AF":"getAllFeatures","PT":"getPendingTests","TP":"markTestPassed","TF":"markTestFailed","TS":"getTestSummary","TS1":"resetTestState","l":"l","cT":"checkTypes","UF":"checkUndocumentedFile","gU":"getUndocumented","US":"getUndocumentedSummary","oO":"O","rR":"R","pH":"printHelp","CLI":"runCLI","_I":"_loadIgnore","_I1":"_parseIgnore","_I2":"_shouldIgnore","AC":"getAiContext","cM":"compactMigrate","TLN":"addTopLevelNewlines","cF":"compactFile","bF":"beautifyFile","cP":"compactProject","eP":"expandProject","c":"c","cF1":"compressFile","eC":"editCompressed","CP1":"resolveCtxPath","CRP":"resolveCtxRelPath","CF1":"readCtxFile","CF2":"parseCtxFile","JSD4":"injectJSDoc","JSD5":"stripJSDoc","CC":"validateCtxContracts","DD":"generateDocDialect","CD":"readContextDocs","PD":"getProjectDocs","eE":"E","cS":"checkStaleness","CF3":"generateContextFiles","eF":"expandFile","fR":"fetchReference","lA":"listAvailable","FR":"getFrameworkReference","gI":"getInstructions","CP2":"parseCtxParams","sa":"sanitize","JSD6":"buildJSDocBlock","JSD7":"buildJSDocFromRaw","gC1":"getConfig","sC":"setConfig","MD":"getModeDescription","MW":"getModeWorkflow","sD":"splitDeclarations","SLB":"isSingleLineBlob","vP":"validatePipeline","TC":"emitToolCall","TR":"emitToolResult","TC1":"onToolCall","TR1":"onToolResult","TL":"removeToolListener","JSF1":"walkJSFiles","gF":"getFilters","sF":"setFilters","aE":"addExcludes","rE":"removeExcludes","rF":"resetFilters","pG":"parseGitignore","ED":"shouldExcludeDir","EF":"shouldExcludeFile","mL":"minifyLegend","e":"e","bG":"buildGraph","cS1":"createSkeleton","pF":"parseFile","SP":"discoverSubProjects","pP1":"parseProject","APF":"findAllProjectFiles","eT":"estimateTokens","sR":"setRoots","WR":"getWorkspaceRoot","rP":"resolvePath","pG1":"parseGo","eI":"extractImports","gB":"getBody","eC1":"extractCalls","pP2":"parsePython","SQL":"isSQLString","t":"t","SQL1":"extractSQLFromString","SQL2":"parseSQL","n":"n","SQL3":"extractSQLFromCode","ORM":"extractORMFromCode","TS2":"parseTypeScript","eP1":"extractParams","SAC":"stripStringsAndComments","cS2":"createServer","SS":"startStdioServer","gG":"getGraph","gS":"getSkeleton","FZ":"getFocusZone","ex":"expand","de":"deps","us":"usages","CC1":"getCallChain","iC":"invalidateCache","_V":"_getVersion","bB":"B","PF":"writePortFile","PF1":"removePortFile","lB":"listBackends","eB":"ensureBackend","SP1":"startStdioProxy","cl":"cleanup","rS":"registerService","GP":"getGatewayPort","rL":"registerLocal","_r":"_rv","k":"k","_C":"_clearCache","nN":"N","z":"z","aA":"A","WS":"startWebServer"},"stats":{"files":51,"classes":0,"functions":246,"tables":0},"nodes":{"CH":{"t":"F","e":true,"f":"analysis/analysis-cache.js"},"CP":{"t":"F","e":true,"f":"analysis/analysis-cache.js"},"rC":{"t":"F","e":true,"f":"analysis/analysis-cache.js"},"wC":{"t":"F","e":true,"f":"analysis/analysis-cache.js"},"CV":{"t":"F","e":true,"f":"analysis/analysis-cache.js"},"p":{"t":"F","e":false,"f":"network/local-gateway.js"},"u":{"t":"F","e":false,"f":"network/web-server.js"},"h":{"t":"F","e":false,"f":"network/local-gateway.js"},"CF":{"t":"F","e":true,"f":"analysis/complexity.js"},"f":{"t":"F","e":false,"f":"network/local-gateway.js"},"gC":{"t":"F","e":true,"f":"analysis/complexity.js"},"y":{"t":"F","e":false,"f":"network/web-server.js"},"g":{"t":"F","e":false,"f":"network/web-server.js"},"x":{"t":"F","e":false,"f":"network/web-server.js"},"v":{"t":"F","e":false,"f":"core/parser.js"},"$":{"t":"F","e":false,"f":"analysis/custom-rules.js"},"j":{"t":"F","e":false,"f":"core/parser.js"},"sS":{"t":"F","e":false,"f":"network/web-server.js"},"b":{"t":"F","e":false,"f":"analysis/custom-rules.js"},"CR":{"t":"F","e":true,"f":"analysis/custom-rules.js"},"CR1":{"t":"F","e":true,"f":"analysis/custom-rules.js"},"CR2":{"t":"F","e":true,"f":"analysis/custom-rules.js"},"PRS":{"t":"F","e":true,"f":"analysis/custom-rules.js"},"CR3":{"t":"F","e":true,"f":"analysis/custom-rules.js"},"DBS":{"t":"F","e":true,"f":"analysis/db-analysis.js"},"TU":{"t":"F","e":true,"f":"analysis/db-analysis.js"},"DBD":{"t":"F","e":true,"f":"analysis/db-analysis.js"},"m":{"t":"F","e":false,"f":"analysis/similar-functions.js"},"DC":{"t":"F","e":true,"f":"analysis/dead-code.js"},"w":{"t":"F","e":false,"f":"network/web-server.js"},"fF":{"t":"F","e":false,"f":"compact/ctx-to-jsdoc.js"},"dD":{"t":"F","e":false,"f":"compact/validate-pipeline.js"},"mM":{"t":"F","e":false,"f":"analysis/full-analysis.js"},"pP":{"t":"F","e":false,"f":"network/web-server.js"},"FA":{"t":"F","e":true,"f":"analysis/full-analysis.js"},"ASO":{"t":"F","e":true,"f":"analysis/full-analysis.js"},"d":{"t":"F","e":false,"f":"network/local-gateway.js"},"JSD":{"t":"F","e":true,"f":"analysis/jsdoc-checker.js"},"JSD1":{"t":"F","e":true,"f":"analysis/jsdoc-checker.js"},"JSD2":{"t":"F","e":true,"f":"analysis/jsdoc-generator.js"},"i":{"t":"F","e":false,"f":"analysis/type-checker.js"},"o":{"t":"F","e":false,"f":"network/mdns.js"},"s":{"t":"F","e":false,"f":"analysis/jsdoc-generator.js"},"JSD3":{"t":"F","e":true,"f":"analysis/jsdoc-generator.js"},"JSF":{"t":"F","e":true,"f":"core/parser.js"},"aF":{"t":"F","e":false,"f":"analysis/large-files.js"},"LF":{"t":"F","e":true,"f":"analysis/large-files.js"},"FP":{"t":"F","e":false,"f":"analysis/outdated-patterns.js"},"PJ":{"t":"F","e":false,"f":"analysis/outdated-patterns.js"},"OP":{"t":"F","e":true,"f":"analysis/outdated-patterns.js"},"SF":{"t":"F","e":true,"f":"analysis/similar-functions.js"},"a":{"t":"F","e":false,"f":"analysis/test-annotations.js"},"pA":{"t":"F","e":true,"f":"analysis/test-annotations.js"},"AF":{"t":"F","e":true,"f":"analysis/test-annotations.js"},"PT":{"t":"F","e":true,"f":"analysis/test-annotations.js"},"TP":{"t":"F","e":true,"f":"analysis/test-annotations.js"},"TF":{"t":"F","e":true,"f":"analysis/test-annotations.js"},"TS":{"t":"F","e":true,"f":"analysis/test-annotations.js"},"TS1":{"t":"F","e":true,"f":"analysis/test-annotations.js"},"l":{"t":"F","e":false,"f":"network/local-gateway.js"},"cT":{"t":"F","e":true,"f":"analysis/type-checker.js"},"UF":{"t":"F","e":true,"f":"analysis/undocumented.js"},"gU":{"t":"F","e":true,"f":"analysis/undocumented.js"},"US":{"t":"F","e":true,"f":"analysis/undocumented.js"},"oO":{"t":"F","e":false,"f":"network/web-server.js"},"rR":{"t":"F","e":false,"f":"cli/cli-handlers.js"},"pH":{"t":"F","e":true,"f":"cli/cli.js"},"CLI":{"t":"F","e":true,"f":"cli/cli.js"},"_I":{"t":"F","e":false,"f":"compact/ai-context.js"},"_I1":{"t":"F","e":false,"f":"compact/ai-context.js"},"_I2":{"t":"F","e":false,"f":"compact/ai-context.js"},"AC":{"t":"F","e":true,"f":"compact/ai-context.js"},"cM":{"t":"F","e":true,"f":"compact/compact-migrate.js"},"TLN":{"t":"F","e":false,"f":"compact/compact.js"},"cF":{"t":"F","e":false,"f":"compact/compact.js"},"bF":{"t":"F","e":false,"f":"compact/compact.js"},"cP":{"t":"F","e":true,"f":"compact/compact.js"},"eP":{"t":"F","e":true,"f":"compact/expand.js"},"c":{"t":"F","e":false,"f":"network/mdns.js"},"cF1":{"t":"F","e":true,"f":"compact/compress.js"},"eC":{"t":"F","e":true,"f":"compact/compress.js"},"CP1":{"t":"F","e":true,"f":"compact/ctx-resolver.js"},"CRP":{"t":"F","e":true,"f":"compact/ctx-resolver.js"},"CF1":{"t":"F","e":true,"f":"compact/ctx-resolver.js"},"CF2":{"t":"F","e":true,"f":"compact/ctx-to-jsdoc.js"},"JSD4":{"t":"F","e":true,"f":"compact/ctx-to-jsdoc.js"},"JSD5":{"t":"F","e":true,"f":"compact/ctx-to-jsdoc.js"},"CC":{"t":"F","e":true,"f":"compact/ctx-to-jsdoc.js"},"DD":{"t":"F","e":true,"f":"compact/doc-dialect.js"},"CD":{"t":"F","e":true,"f":"compact/doc-dialect.js"},"PD":{"t":"F","e":true,"f":"compact/doc-dialect.js"},"eE":{"t":"F","e":false,"f":"core/parser.js"},"cS":{"t":"F","e":true,"f":"compact/doc-dialect.js"},"CF3":{"t":"F","e":true,"f":"compact/doc-dialect.js"},"eF":{"t":"F","e":true,"f":"compact/expand.js"},"fR":{"t":"F","e":false,"f":"compact/framework-references.js"},"lA":{"t":"F","e":false,"f":"compact/framework-references.js"},"FR":{"t":"F","e":true,"f":"compact/framework-references.js"},"gI":{"t":"F","e":true,"f":"compact/instructions.js"},"CP2":{"t":"F","e":true,"f":"compact/jsdoc-builder.js"},"sa":{"t":"F","e":false,"f":"compact/jsdoc-builder.js"},"JSD6":{"t":"F","e":true,"f":"compact/jsdoc-builder.js"},"JSD7":{"t":"F","e":true,"f":"compact/jsdoc-builder.js"},"gC1":{"t":"F","e":true,"f":"compact/mode-config.js"},"sC":{"t":"F","e":true,"f":"compact/mode-config.js"},"MD":{"t":"F","e":true,"f":"compact/mode-config.js"},"MW":{"t":"F","e":true,"f":"compact/mode-config.js"},"sD":{"t":"F","e":true,"f":"compact/split-declarations.js"},"SLB":{"t":"F","e":true,"f":"compact/split-declarations.js"},"vP":{"t":"F","e":true,"f":"compact/validate-pipeline.js"},"TC":{"t":"F","e":true,"f":"core/event-bus.js"},"TR":{"t":"F","e":true,"f":"core/event-bus.js"},"TC1":{"t":"F","e":true,"f":"core/event-bus.js"},"TR1":{"t":"F","e":true,"f":"core/event-bus.js"},"TL":{"t":"F","e":true,"f":"core/event-bus.js"},"JSF1":{"t":"F","e":true,"f":"core/file-walker.js"},"gF":{"t":"F","e":true,"f":"core/filters.js"},"sF":{"t":"F","e":true,"f":"core/filters.js"},"aE":{"t":"F","e":true,"f":"core/filters.js"},"rE":{"t":"F","e":true,"f":"core/filters.js"},"rF":{"t":"F","e":true,"f":"core/filters.js"},"pG":{"t":"F","e":true,"f":"core/filters.js"},"ED":{"t":"F","e":true,"f":"core/filters.js"},"EF":{"t":"F","e":true,"f":"core/filters.js"},"mL":{"t":"F","e":true,"f":"core/graph-builder.js"},"e":{"t":"F","e":false,"f":"core/graph-builder.js"},"bG":{"t":"F","e":true,"f":"core/graph-builder.js"},"cS1":{"t":"F","e":true,"f":"core/graph-builder.js"},"pF":{"t":"F","e":true,"f":"core/parser.js"},"SP":{"t":"F","e":true,"f":"core/parser.js"},"pP1":{"t":"F","e":true,"f":"core/parser.js"},"APF":{"t":"F","e":true,"f":"core/parser.js"},"eT":{"t":"F","e":true,"f":"core/utils.js"},"sR":{"t":"F","e":true,"f":"core/workspace.js"},"WR":{"t":"F","e":true,"f":"core/workspace.js"},"rP":{"t":"F","e":true,"f":"core/workspace.js"},"pG1":{"t":"F","e":true,"f":"lang/lang-go.js"},"eI":{"t":"F","e":false,"f":"lang/lang-go.js"},"gB":{"t":"F","e":false,"f":"lang/lang-go.js"},"eC1":{"t":"F","e":false,"f":"lang/lang-go.js"},"pP2":{"t":"F","e":true,"f":"lang/lang-python.js"},"SQL":{"t":"F","e":true,"f":"lang/lang-sql.js"},"t":{"t":"F","e":false,"f":"lang/lang-sql.js"},"SQL1":{"t":"F","e":true,"f":"lang/lang-sql.js"},"SQL2":{"t":"F","e":true,"f":"lang/lang-sql.js"},"n":{"t":"F","e":false,"f":"network/mdns.js"},"SQL3":{"t":"F","e":true,"f":"lang/lang-sql.js"},"ORM":{"t":"F","e":true,"f":"lang/lang-sql.js"},"TS2":{"t":"F","e":true,"f":"lang/lang-typescript.js"},"eP1":{"t":"F","e":false,"f":"lang/lang-typescript.js"},"SAC":{"t":"F","e":true,"f":"lang/lang-utils.js"},"cS2":{"t":"F","e":true,"f":"mcp/mcp-server.js"},"SS":{"t":"F","e":true,"f":"mcp/mcp-server.js"},"gG":{"t":"F","e":true,"f":"mcp/tools.js"},"gS":{"t":"F","e":true,"f":"mcp/tools.js"},"FZ":{"t":"F","e":true,"f":"mcp/tools.js"},"ex":{"t":"F","e":true,"f":"mcp/tools.js"},"de":{"t":"F","e":true,"f":"mcp/tools.js"},"us":{"t":"F","e":true,"f":"mcp/tools.js"},"CC1":{"t":"F","e":true,"f":"mcp/tools.js"},"iC":{"t":"F","e":true,"f":"mcp/tools.js"},"_V":{"t":"F","e":false,"f":"network/backend-lifecycle.js"},"bB":{"t":"F","e":false,"f":"network/backend-lifecycle.js"},"PF":{"t":"F","e":true,"f":"network/backend-lifecycle.js"},"PF1":{"t":"F","e":true,"f":"network/backend-lifecycle.js"},"lB":{"t":"F","e":true,"f":"network/backend-lifecycle.js"},"eB":{"t":"F","e":true,"f":"network/backend-lifecycle.js"},"SP1":{"t":"F","e":true,"f":"network/backend-lifecycle.js"},"cl":{"t":"F","e":false,"f":"network/backend.js"},"rS":{"t":"F","e":true,"f":"network/local-gateway.js"},"GP":{"t":"F","e":true,"f":"network/local-gateway.js"},"rL":{"t":"F","e":true,"f":"network/mdns.js"},"_r":{"t":"F","e":false,"f":"network/web-server.js"},"k":{"t":"F","e":false,"f":"network/web-server.js"},"_C":{"t":"F","e":false,"f":"network/web-server.js"},"nN":{"t":"F","e":false,"f":"network/web-server.js"},"z":{"t":"F","e":false,"f":"network/web-server.js"},"aA":{"t":"F","e":false,"f":"network/web-server.js"},"WS":{"t":"F","e":true,"f":"network/web-server.js"}},"edges":[],"orphans":["p","u","h","f","y","g","x","v","$","j","S","b","m","w","F","D","M","P","d","i","o","s","analyzeFile","analyzeFilePatterns","analyzePackageJson","a","l","O","R","_loadIgnore","_parseIgnore","_shouldIgnore","addTopLevelNewlines","compactFile","beautifyFile","c","E","fetchReference","listAvailable","sanitize","e","extractImports","getBody","extractCalls","t","n","extractParams","_getVersion","B","cleanup","_rv","k","_clearCache","N","z","A"],"duplicates":{},"files":["analysis/analysis-cache.js","analysis/complexity.js","analysis/custom-rules.js","analysis/db-analysis.js","analysis/dead-code.js","analysis/full-analysis.js","analysis/jsdoc-checker.js","analysis/jsdoc-generator.js","analysis/large-files.js","analysis/outdated-patterns.js","analysis/similar-functions.js","analysis/test-annotations.js","analysis/type-checker.js","analysis/undocumented.js","cli/cli-handlers.js","cli/cli.js","compact/ai-context.js","compact/compact-migrate.js","compact/compact.js","compact/compress.js","compact/ctx-resolver.js","compact/ctx-to-jsdoc.js","compact/doc-dialect.js","compact/expand.js","compact/framework-references.js","compact/instructions.js","compact/jsdoc-builder.js","compact/mode-config.js","compact/split-declarations.js","compact/validate-pipeline.js","core/event-bus.js","core/file-walker.js","core/filters.js","core/graph-builder.js","core/parser.js","core/utils.js","core/workspace.js","lang/lang-go.js","lang/lang-python.js","lang/lang-sql.js","lang/lang-typescript.js","lang/lang-utils.js","mcp/mcp-server.js","mcp/tool-defs.js","mcp/tools.js","network/backend-lifecycle.js","network/backend.js","network/local-gateway.js","network/mdns.js","network/server.js","network/web-server.js"]}}
|
|
@@ -1,7 +1,34 @@
|
|
|
1
1
|
// @ctx .context/src/compact/ai-context.ctx
|
|
2
|
-
import{estimateTokens}from"../core/utils.js";import{resolve as e,extname as t}from"path";import{getSkeleton as s,getGraph as o}from"../mcp/tools.js";import{getProjectDocs as n}from"./doc-dialect.js";import{compressFile as i}from"./compress.js";import{findJSFiles as r}from"../core/parser.js";
|
|
2
|
+
import{estimateTokens}from"../core/utils.js";import{resolve as e,extname as t,relative as _rel,join as _join,basename as _base}from"path";import{getSkeleton as s,getGraph as o}from"../mcp/tools.js";import{getProjectDocs as n}from"./doc-dialect.js";import{compressFile as i}from"./compress.js";import{findJSFiles as r}from"../core/parser.js";import{readFileSync as _rf,existsSync as _ex,writeFileSync as _wf}from"fs";
|
|
3
3
|
const c=new Set([".js",".mjs",".ts",".tsx"]);
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
4
|
+
const IGNORE_FILE=".contextignore";
|
|
5
|
+
const DEFAULT_IGNORE=`# Files and directories excluded from AI context (get_ai_context includeFiles: ["*"])
|
|
6
|
+
# Glob patterns — one per line
|
|
7
|
+
|
|
8
|
+
# Vendored / bundled libs
|
|
9
|
+
vendor/
|
|
10
|
+
*.min.js
|
|
11
|
+
*.bundle.js
|
|
12
|
+
chart.js
|
|
13
|
+
d3.js
|
|
14
|
+
three.js
|
|
15
|
+
lodash.js
|
|
16
|
+
jquery*.js
|
|
17
|
+
|
|
18
|
+
# Generated
|
|
19
|
+
dist/
|
|
20
|
+
build/
|
|
21
|
+
coverage/
|
|
22
|
+
*.d.ts
|
|
23
|
+
|
|
24
|
+
# Tests
|
|
25
|
+
*.test.js
|
|
26
|
+
*.spec.js
|
|
27
|
+
`;
|
|
28
|
+
function _loadIgnore(p){const f=_join(p,IGNORE_FILE);if(!_ex(f)){try{_wf(f,DEFAULT_IGNORE)}catch{}return _parseIgnore(DEFAULT_IGNORE)}return _parseIgnore(_rf(f,"utf-8"))}
|
|
29
|
+
function _parseIgnore(s){return s.split("\n").map(l=>l.trim()).filter(l=>l&&!l.startsWith("#"))}
|
|
30
|
+
function _shouldIgnore(relPath,patterns){for(const p of patterns){if(p.endsWith("/")&&relPath.startsWith(p.slice(0,-1)))return true;if(p.endsWith("/")&&relPath.includes("/"+p.slice(0,-1)))return true;const re=new RegExp("^"+p.replace(/\./g,"\\.").replace(/\*/g,".*").replace(/\?/g,".")+"$");if(re.test(_base(relPath))||re.test(relPath))return true}return false}
|
|
31
|
+
export async function getAiContext(a,l={}){if(!a)return{error:"path is required",hint:'Usage: get_ai_context({ path: ".", includeFiles: ["*"] })',modes:{overview:'get_ai_context({ path: "." }) → skeleton + docs (~2-3k tokens)',code:'get_ai_context({ path: ".", includeFiles: ["*"] }) → all source files',full:'get_ai_context({ path: ".", includeFiles: ["*"], includeSkeleton: true, includeDocs: true }) → everything'}};const p=e(a);if(!_ex(p))return{error:`Path not found: ${p}`,hint:"Provide an existing directory path"};const f=l.includeFiles||[];if(typeof f==="string")return{error:"includeFiles must be an array, got string",hint:`Use includeFiles: ["${f}"] or includeFiles: ["*"] for all files`};const u={};const allJS=r(p);const wantAll=f.includes("*");const useSkeleton="includeSkeleton"in l?l.includeSkeleton:!wantAll;const useDocs="includeDocs"in l?l.includeDocs:!wantAll;if(allJS.length===0){u.hint="No JS/TS files found. Check the path — it should point to your source directory.";}
|
|
32
|
+
let g=0;if(useSkeleton&&(u.skeleton=await s(p),g+=estimateTokens(u.skeleton)),useDocs){const e=await o(p);u.docs=n(e,p),g+=estimateTokens(u.docs)}if(wantAll||f.length>0){u.files={};const ignorePatterns=wantAll?_loadIgnore(p):[];
|
|
33
|
+
let targets;if(wantAll){targets=allJS.map(e=>_rel(p,e)).filter(f=>!_shouldIgnore(f,ignorePatterns));u.ignored=allJS.length-targets.length}else{targets=f}for(const s of targets){const jsMatch=allJS.find(e=>e.endsWith(s)||e.endsWith("/"+s));if(jsMatch){const n=t(jsMatch).toLowerCase();if(c.has(n))try{const e=await i(jsMatch,{beautify:!1,legend:!1});u.files[s]=e.code,g+=e.compressed}catch(e){u.files[s]={error:e.message}}else{const txt=_rf(jsMatch,"utf-8");u.files[s]=txt;g+=estimateTokens(txt)}}else{const absPath=_join(p,s);if(_ex(absPath)){try{const txt=_rf(absPath,"utf-8");u.files[s]=txt;g+=estimateTokens(txt)}catch(e){u.files[s]={error:e.message}}}else{u.files[s]={error:`File not found: ${s}`}}}}}
|
|
34
|
+
let k=0;for(const e of allJS)try{k+=estimateTokens(_rf(e,"utf-8"))}catch{}const y=k>0?Math.round(100*(1-g/k)):0;return u.totalTokens=g,u.vsOriginal=k,u.savings=`${y}%`,u}
|
|
@@ -1,3 +1,7 @@
|
|
|
1
1
|
// @ctx .context/src/compact/instructions.ctx
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
import{readFileSync}from"fs";import{join,dirname}from"path";import{fileURLToPath}from"url";
|
|
3
|
+
const __dir=dirname(fileURLToPath(import.meta.url));
|
|
4
|
+
const _mdPath=join(__dir,"..","..","docs","AGENT_INSTRUCTIONS.md");
|
|
5
|
+
let _cached=null;
|
|
6
|
+
export function getInstructions(){if(!_cached)try{_cached=readFileSync(_mdPath,"utf-8")}catch{_cached="# Agent Instructions\n\nFile not found: "+_mdPath}return _cached}
|
|
7
|
+
export const AGENT_INSTRUCTIONS=getInstructions();
|
package/src/mcp/tool-defs.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
// @ctx .context/src/mcp/tool-defs.ctx
|
|
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: skeleton +
|
|
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"]}}];
|
|
@@ -13,5 +13,6 @@ function B(e){const t=y(e);if(!r(t))return null;try{const e=JSON.parse(o(t,"utf8
|
|
|
13
13
|
export function writePortFile(e,t){n(g,{recursive:!0});const r=l(e),i={port:t,pid:process.pid,project:r,name:f(r)||"root",version:_getVersion(),startedAt:Date.now()};c(y(e),JSON.stringify(i,null,2))}
|
|
14
14
|
export function removePortFile(e){try{s(y(e))}catch{}}
|
|
15
15
|
export function listBackends(){if(!r(g))return[];const e=i(g).filter(e=>e.endsWith(".json")),t=[];for(const r of e)try{const e=JSON.parse(o(a(g,r),"utf8"));try{process.kill(e.pid,0),t.push(e)}catch{try{s(a(g,r))}catch{}}}catch{}return t}
|
|
16
|
-
|
|
16
|
+
function _srcChanged(startedAt){try{const dir=a(m,"..","..","src");const{statSync:st,readdirSync:rd}=require("fs");const files=rd(dir,{recursive:true});for(const f of files){try{const{mtimeMs}=st(a(dir,String(f)));if(mtimeMs>startedAt)return true}catch{}}return false}catch{return false}}
|
|
17
|
+
export async function ensureBackend(e,{force:f}={}){const t=l(e),n=B(t);if(n){const cv=_getVersion();const needRestart=f||(n.version&&n.version!==cv)||_srcChanged(n.startedAt);if(needRestart){if(n.version!==cv)console.error(`[project-graph] Version mismatch: running ${n.version}, installed ${cv}`);else if(_srcChanged(n.startedAt))console.error("[project-graph] Source files changed, restarting...");console.error("[project-graph] Restarting backend...");try{process.kill(n.pid,"SIGTERM")}catch{}try{s(y(t))}catch{}await new Promise(r=>setTimeout(r,500))}else{return n.port}}const o=a(m,"backend.js");u(process.execPath,[o,t],{detached:!0,stdio:"ignore",env:{...process.env,PROJECT_GRAPH_BACKEND:"1"}}).unref();const c=y(t),_t=Date.now();for(;Date.now()-_t<1e4;)if(await new Promise(e=>setTimeout(e,200)),r(c)){const e=B(t);if(e)return e.port}throw new Error("Backend failed to start within 10s")}
|
|
17
18
|
export function startStdioProxy(e,r=[]){const n=t(16).toString("base64"),o=d({host:"127.0.0.1",port:e},()=>{o.write(`GET /mcp-ws HTTP/1.1\r\nHost: 127.0.0.1:${e}\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Key: ${n}\r\nSec-WebSocket-Version: 13\r\n\r\n`)});let c=!1,s=Buffer.alloc(0),i=[...r];const a=p({input:process.stdin,terminal:!1});function l(e){const r=Buffer.from(e,"utf8"),n=t(4),o=Buffer.alloc(r.length);for(let e=0;e<r.length;e++)o[e]=r[e]^n[e%4];let c;return r.length<126?(c=Buffer.alloc(2),c[0]=129,c[1]=128|r.length):r.length<65536?(c=Buffer.alloc(4),c[0]=129,c[1]=254,c.writeUInt16BE(r.length,2)):(c=Buffer.alloc(10),c[0]=129,c[1]=255,c.writeBigUInt64BE(BigInt(r.length),2)),Buffer.concat([c,n,o])}function f(e){if(e.length<2)return null;const t=15&e[0];let r=127&e[1],n=2;if(126===r){if(e.length<4)return null;r=e.readUInt16BE(2),n=4}else if(127===r){if(e.length<10)return null;r=Number(e.readBigUInt64BE(2)),n=10}return e.length<n+r?null:{opcode:t,data:e.slice(n,n+r).toString("utf8"),totalLen:n+r}}a.on("line",e=>{if(c)try{o.write(l(e))}catch{}else i.push(e)}),a.on("close",()=>{o.end(),process.exit(0)}),o.on("data",e=>{if(c)s=Buffer.concat([s,e]);else{const t=Buffer.concat([s,e]),r=t.indexOf("\r\n\r\n");if(-1===r)return void(s=t);t.slice(0,r).toString().includes("101")||(console.error("[project-graph] WebSocket handshake failed"),process.exit(1)),c=!0,s=t.slice(r+4);for(const e of i)try{o.write(l(e))}catch{}i=[]}for(;s.length>=2;){const e=f(s);if(!e)break;if(s=s.slice(e.totalLen),1===e.opcode)process.stdout.write(e.data+"\n");else if(8===e.opcode)process.exit(0);else if(9===e.opcode){const e=Buffer.alloc(2);e[0]=138,e[1]=0,o.write(e)}}}),o.on("close",()=>process.exit(0)),o.on("error",e=>{console.error(`[project-graph] Proxy connection error: ${e.message}`),process.exit(1)})}
|
|
@@ -4,8 +4,10 @@ const s=o.join(process.env.HOME||process.env.USERPROFILE||"/tmp",".local-gateway
|
|
|
4
4
|
function p(){try{return JSON.parse(r.readFileSync(c,"utf8"))}catch{return{}}}
|
|
5
5
|
function l(t){r.mkdirSync(s,{recursive:!0}),r.writeFileSync(c,JSON.stringify(t,null,2))}
|
|
6
6
|
function d(){if(!r.existsSync(a))return;const t=r.readdirSync(a).filter(t=>t.endsWith(".json"));let e=!1;const n=p();for(const s of t)try{const t=JSON.parse(r.readFileSync(o.join(a,s),"utf8"));try{process.kill(t.pid,0)}catch{r.unlinkSync(o.join(a,s));continue}const c=t.name||"root",i=`/${c}`;n["project-graph.local"]=n["project-graph.local"]||{name:"project-graph",routes:{}},n["project-graph.local"].routes[i]||(n["project-graph.local"].routes[i]={port:t.port,pid:t.pid,projectPath:t.project,projectName:c},e=!0)}catch{}e&&l(n)}
|
|
7
|
-
export function registerService(t,e,r={}){const o=`${t}.local`,s=p();if(r.projectName){s[o]||(s[o]={name:t,routes:{}});const n=`/${r.projectName}`;s[o].routes=s[o].routes||{},s[o].routes[n]={port:e,pid:process.pid,projectPath:r.projectPath,projectName:r.projectName}}else s[o]={port:e,pid:process.pid,name:t};l(s);const c=n(o,
|
|
8
|
-
function u(t,e,r){const o=r[t];if(!o)return null;if(o.routes){const t=Object.keys(o.routes).sort((t,e)=>e.length-t.length);for(const r of t)if(e===r||e.startsWith(r+"/")){const t=o.routes[r];try{process.kill(t.pid,0)}catch{continue}const n=e.slice(r.length)||"/";return{port:t.port,rewritePath:n,prefix:r}}for(const r of t)try{const t=o.routes[r];process.kill(t.pid,0);const n="/"===e||""===e?"/dashboard.html":e;return{port:t.port,rewritePath:n}}catch{continue}}if(o.port){const t="/"===e||""===e?"/dashboard.html":e;return{port:o.port,rewritePath:t}}return null}
|
|
7
|
+
export function registerService(t,e,r={}){const o=`${t}.local`,s=p();if(r.projectName){s[o]||(s[o]={name:t,routes:{}});const n=`/${r.projectName}`;s[o].routes=s[o].routes||{},s[o].routes[n]={port:e,pid:process.pid,projectPath:r.projectPath,projectName:r.projectName}}else s[o]={port:e,pid:process.pid,name:t};l(s);f();const gwPort=getGatewayPort();const c=n(o,gwPort);const i=()=>{c.cleanup()};process.on("exit",i),process.on("SIGINT",()=>{i(),process.exit()}),process.on("SIGTERM",()=>{i(),process.exit()});const d=80===gwPort?"":`:${gwPort}`,u=r.projectName?`http://${o}${d}/${r.projectName}/`:`http://${o}${d}/`;return{cleanup:i,url:u,directUrl:`http://localhost:${e}/`}}
|
|
8
|
+
function u(t,e,r){const o=r[t];if(!o)return null;if(o.routes){const t=Object.keys(o.routes).sort((t,e)=>e.length-t.length);for(const r of t)if(e===r||e.startsWith(r+"/")){const t=o.routes[r];try{process.kill(t.pid,0)}catch{if(t.projectPath){_autoRestart(t.projectPath);return{port:t.port,rewritePath:"/restarting",restarting:true,prefix:r}}continue}const n=e.slice(r.length)||"/";return{port:t.port,rewritePath:n,prefix:r}}for(const r of t)try{const t=o.routes[r];process.kill(t.pid,0);const n="/"===e||""===e?"/dashboard.html":e;return{port:t.port,rewritePath:n}}catch{continue}}if(o.port){const t="/"===e||""===e?"/dashboard.html":e;return{port:o.port,rewritePath:t}}return null}
|
|
9
|
+
const _restarts=new Map();function _autoRestart(projectPath){const now=Date.now();if(_restarts.has(projectPath)&&now-_restarts.get(projectPath)<15e3)return;_restarts.set(projectPath,now);console.error(`[gateway] Auto-restarting backend for ${projectPath}`);try{const{spawn:s}=require("node:child_process");const b=o.join(o.dirname(new URL(import.meta.url).pathname),"backend.js");s(process.execPath,[b,projectPath],{detached:true,stdio:"ignore",env:{...process.env,PROJECT_GRAPH_BACKEND:"1"}}).unref()}catch(e){console.error(`[gateway] Auto-restart failed: ${e.message}`)}}
|
|
9
10
|
function h(){try{const t=r.readFileSync(i,"utf8");return t.trim().startsWith("{")?JSON.parse(t):{pid:parseInt(t,10),port:80}}catch{return null}}
|
|
10
11
|
export function getGatewayPort(){const t=h();return t?.port||80}
|
|
11
|
-
|
|
12
|
+
const RESTART_HTML='<!DOCTYPE html><html><head><meta charset="utf-8"><meta http-equiv="refresh" content="3"><title>Restarting...</title><style>body{background:#1a1a2e;color:#e0e0e0;font-family:system-ui;display:flex;justify-content:center;align-items:center;height:100vh;margin:0}.box{text-align:center}.spinner{width:40px;height:40px;border:3px solid #333;border-top:3px solid #7c3aed;border-radius:50%;animation:spin 1s linear infinite;margin:0 auto 16px}@keyframes spin{to{transform:rotate(360deg)}}</style></head><body><div class="box"><div class="spinner"></div><h2>⬡ Backend restarting...</h2><p>Auto-refreshing in 3 seconds</p></div></body></html>';
|
|
13
|
+
function f(){if(!function(){const t=h();if(!t)return!1;try{return process.kill(t.pid,0),!0}catch{return!1}}())try{process.on("uncaughtException",t=>{});process.on("unhandledRejection",t=>{});const o=t.createServer((e,r)=>{const o=(e.headers.host||"").split(":")[0];let n=p(),s=u(o,e.url,n);if(!s&&(d(),n=p(),s=u(o,e.url,n),!s))return r.writeHead(404,{"Content-Type":"text/plain"}),void r.end(`Unknown host: ${o}\nRegistered: ${Object.keys(n).join(", ")}`);if("/api/gateway-info"===e.url){const t=JSON.stringify(n["project-graph.local"]||{routes:{}});return r.writeHead(200,{"Content-Type":"application/json"}),void r.end(t)}if("POST"===e.method&&"/api/remove-project"===e.url){let t="";return e.on("data",e=>t+=e),void e.on("end",()=>{try{const e=JSON.parse(t).route,o=p();if(o["project-graph.local"]?.routes?.[e]){const t=o["project-graph.local"].routes[e];try{process.kill(t.pid,9)}catch{}delete o["project-graph.local"].routes[e],l(o)}r.writeHead(200,{"Content-Type":"application/json"}),r.end(JSON.stringify({ok:!0}))}catch(t){r.writeHead(400,{"Content-Type":"application/json"}),r.end(JSON.stringify({error:t.message}))}})}if("/api/instances"===e.url){const n2=p(),rt=n2["project-graph.local"]?.routes||{};const list=Object.entries(rt).map(([k,v])=>({name:v.projectName||k,project:v.projectPath||"",pid:v.pid,port:v.port,prefix:k,startedAt:v.startedAt||0}));r.writeHead(200,{"Content-Type":"application/json"});return void r.end(JSON.stringify(list))}if("/api/project-info"===e.url){const n2=p(),rt=n2["project-graph.local"]?.routes||{};r.writeHead(200,{"Content-Type":"application/json"});return void r.end(JSON.stringify({name:"Gateway",path:"project-graph.local",agents:Object.keys(rt).length,pid:process.pid}))}if("/api/server-status"===e.url){const n2=p(),rt=n2["project-graph.local"]?.routes||{};r.writeHead(200,{"Content-Type":"application/json"});return void r.end(JSON.stringify({uptime:Math.round(process.uptime()),agents:0,monitors:Object.keys(rt).length,shutdownAt:null}))}if(s.restarting){r.writeHead(200,{"Content-Type":"text/html"});return void r.end(RESTART_HTML)}const c=t.request({hostname:"127.0.0.1",port:s.port,path:s.rewritePath,method:e.method,headers:{...e.headers,host:`localhost:${s.port}`}},t=>{if((t.headers["content-type"]||"").includes("text/html")&&s.prefix){const e=[];t.on("data",t=>e.push(t)),t.on("end",()=>{let o=Buffer.concat(e).toString("utf8");const n=`<base href="${s.prefix}/">`;o=o.includes("<head>")?o.replace("<head>",`<head>\n ${n}`):n+"\n"+o;const c=Buffer.from(o,"utf8"),i={...t.headers};i["content-length"]=c.length,delete i["transfer-encoding"];try{r.writeHead(t.statusCode,i),r.end(c)}catch{}})}else{try{r.writeHead(t.statusCode,t.headers)}catch{}t.pipe(r).on("error",()=>{})}});c.on("error",()=>{try{r.writeHead(502,{"Content-Type":"text/plain"}),r.end(`Backend unavailable on port ${s.port}`)}catch{}}),e.pipe(c).on("error",()=>{})});function n(t){o.listen(t,"0.0.0.0",()=>{const t=o.address().port;r.mkdirSync(s,{recursive:!0}),r.writeFileSync(i,JSON.stringify({pid:process.pid,port:t})),d()})}o.on("upgrade",(t,r,o)=>{const n=(t.headers.host||"").split(":")[0],s=p(),c=u(n,t.url,s);if(!c||c.isDashboard)return void r.destroy();const i=e.createConnection({host:"127.0.0.1",port:c.port},()=>{const e=c.rewritePath,n=`${t.method} ${e} HTTP/1.1\r\n`+Object.entries(t.headers).map(([t,e])=>`${t}: ${e}`).join("\r\n")+"\r\n\r\n";i.write(n),o.length&&i.write(o);let s=Buffer.alloc(0);i.on("data",function t(e){s=Buffer.concat([s,e]),-1!==s.indexOf("\r\n\r\n")&&(r.write(s),i.removeListener("data",t),r.pipe(i).on("error",()=>{}),i.pipe(r).on("error",()=>{}))})});i.on("error",()=>{try{r.destroy()}catch{}}),r.on("error",()=>{try{i.destroy()}catch{}})}),o.on("error",t=>{"EACCES"===t.code&&!1===o.listening?n(8080):"EADDRINUSE"===t.code&&o.listening}),n(80)}catch{}}
|
package/src/network/mdns.js
CHANGED
|
@@ -2,6 +2,6 @@
|
|
|
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)}
|
|
5
|
-
function n(e,r){const n=t("dns-sd",["-P","Project Graph","_http._tcp","",String(r),e,"127.0.0.1"],{stdio:"ignore",detached:!1});return n.unref(),{method:"Bonjour (dns-sd)",cleanup:()=>{try{n.kill()}catch{}}}}
|
|
5
|
+
function n(e,r){try{const{execSync:x}=require("node:child_process");x('pkill -f "dns-sd.*Project Graph" 2>/dev/null',{stdio:"ignore"})}catch{}const n=t("dns-sd",["-P","Project Graph","_http._tcp","",String(r),e,"127.0.0.1"],{stdio:"ignore",detached:!1});return n.unref(),{method:"Bonjour (dns-sd)",cleanup:()=>{try{n.kill()}catch{}}}}
|
|
6
6
|
function c(e){try{const r=t("avahi-publish-address",["-R",e,"127.0.0.1"],{stdio:"ignore",detached:!1});let n=!1;return r.on("error",()=>{n=!0}),r.unref(),n?null:{method:"Avahi",cleanup:()=>{try{r.kill()}catch{}}}}catch{return null}}
|
|
7
7
|
function o(t){const n=t.split("."),c=Buffer.concat([...n.map(t=>{const e=Buffer.alloc(1+t.length);return e[0]=t.length,e.write(t,1,"ascii"),e}),Buffer.from([0])]);let o;try{o=e.createSocket({type:"udp4",reuseAddr:!0})}catch{return{method:"none",cleanup:()=>{}}}o.on("message",t=>{if(t.length<12)return;if(32768&t.readUInt16BE(2))return;if(0===t.readUInt16BE(4))return;if(12+c.length+4>t.length)return;if(0!==t.compare(c,0,c.length,12,12+c.length))return;const e=12+c.length,n=t.readUInt16BE(e),i=32767&t.readUInt16BE(e+2);if(1!==n||1!==i)return;const a=Buffer.alloc(12+c.length+10+4);let l=0;a.writeUInt16BE(0,l),l+=2,a.writeUInt16BE(33792,l),l+=2,a.writeUInt16BE(0,l),l+=2,a.writeUInt16BE(1,l),l+=2,a.writeUInt16BE(0,l),l+=2,a.writeUInt16BE(0,l),l+=2,c.copy(a,l),l+=c.length,a.writeUInt16BE(1,l),l+=2,a.writeUInt16BE(32769,l),l+=2,a.writeUInt32BE(120,l),l+=4,a.writeUInt16BE(4,l),l+=2,a[l++]=127,a[l++]=0,a[l++]=0,a[l++]=1,o.send(a,0,l,5353,r)}),o.on("error",()=>{try{o.close()}catch{}});try{o.bind({port:5353,exclusive:!1},()=>{try{o.addMembership(r),o.setMulticastTTL(255)}catch{try{o.close()}catch{}}})}catch{return{method:"none",cleanup:()=>{}}}return{method:"Node.js mDNS",cleanup:()=>{try{o.close()}catch{}}}}
|
|
@@ -5,4 +5,4 @@ function g(e,n){const a=o.normalize(e).replace(/^(\.\.[/\\])+/,""),s=a.match(/^[
|
|
|
5
5
|
function u(e){return n.createHash("sha1").update(e+"258EAFA5-E914-47DA-95CA-5AB5ADF35C70").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
|
-
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),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 c={code:"// No file specified",file:""};break}case"/api/compact-file":{const e=a.get("path");if(e){const{resolve:n}=await import("path"),s=n(t,e),i=await _cf(s,{beautify:!1,legend:!1});c={code:i.code,file:e,original:i.original,compressed:i.compressed,savings:i.savings}}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/compression-stats":if(_cache.cs&&Date.now()-_cache.cst<6e4){c=_cache.cs;break}{const{readdirSync:e,statSync:n,readFileSync:a,existsSync:s}=await import("fs"),{join:i,extname:r,basename:d,dirname:p,relative:m}=await import("path"),h=new Set([".js",".mjs"]),f=[],g=["node_modules",".git","vendor",".context",".expanded","web"];!function t(o){try{for(const a of e(o)){if(a.startsWith("."))continue;const e=i(o,a);n(e).isDirectory()?g.includes(a)||t(e):h.has(r(a))&&f.push(e)}}catch{}}(o.resolve(t,"src"));let u=0,y=0,w=0,v=0,_og=0;const j=o.resolve(t);for(const e of f){try{const t=m(j,e),i=d(t,".js"),c=o.resolve(j,".context",p(t),i+".ctx");let r=0;s(c)&&(r=n(c).size,w+=r);try{const t=s(c)?a(c,"utf-8"):null;{const _r=await _cf(e,{beautify:false,legend:false});v+=(_r.expanded||_r.original);u+=_r.compressed;_og+=_r.original}}catch{v+=Math.ceil(1.3*n(e).size/4);_og+=Math.ceil(n(e).size/4)}}catch{continue}y++}c={files:y,codeTok:u,ctxTok:Math.ceil(w/4),totalTok:u+Math.ceil(w/4),expanded:v,original:_og};_cache.cs=c;_cache.cst=Date.now();break}case"/api/docs":{const e=a.get("file");if(e){try{const{readFileSync:n,existsSync:a}=await import("fs"),{basename:s,extname:i,dirname:r}=await import("path"),d=s(e,i(e))+".ctx",p=o.resolve(t,".context",r(e),d);c=a(p)?{docs:n(p,"utf-8"),file:e}:{docs:"",file:e}}catch(t){c={docs:"",file:e}}}else{c=await d.executeTool("docs",{action:"get",path:r})}break}case"/api/analysis":if(_cache.fa&&Date.now()-_cache.fat<12e4){c=_cache.fa}else{c=await d.executeTool("analyze",{action:"full_analysis",path:r});_cache.fa=c;_cache.fat=Date.now()}break;case"/api/analysis-summary":if(_cache.as&&Date.now()-_cache.ast<12e4){c=_cache.as}else{c=await d.executeTool("analyze",{action:"analysis_summary",path:r});_cache.as=c;_cache.ast=Date.now()}break;case"/api/deps":c=await d.executeTool("navigate",{action:"deps",symbol:a.get("symbol")});break;case"/api/usages":c=await d.executeTool("navigate",{action:"usages",symbol:a.get("symbol")});break;case"/api/expand":c=await d.executeTool("navigate",{action:"expand",symbol:a.get("symbol")});break;case"/api/chain":c=await d.executeTool("navigate",{action:"call_chain",from:a.get("from"),to:a.get("to")});break;case"/api/server-status":{const _now=Date.now();c={version:_pkgVersion,uptime:Math.round((_now-_startedAt)/1e3),agents:b.size,monitors:T.size,shutdownAt:_shutdownAt?Math.max(0,Math.round((_shutdownAt-_now)/1e3)):null};break}case"/api/stop":i.writeHead(200,{"Content-Type":"application/json"});i.end(JSON.stringify({ok:true}));setTimeout(()=>process.exit(0),200);return;case"/api/project-info":{const e=o.resolve(t),a=n.createHash("md5").update(e).digest("hex"),s=parseInt(a.slice(0,4),16)%360;c={name:p,path:e,color:`hsl(${s}, 65%, 55%)`,version:_pkgVersion,agents:b.size,pid:process.pid};break}case"/api/instances":try{const{listBackends:e}=await import("./backend-lifecycle.js");c=e()}catch{c=[{name:p,path:o.resolve(t),agents:b.size}]}break;default:return"POST"===s&&"/api/restart"===e?(i.writeHead(200,{"Content-Type":"application/json","Cache-Control":"no-cache"}),i.end(JSON.stringify({ok:!0,message:"Server restarting..."})),void setTimeout(async()=>{const{spawn:e}=await import("child_process"),{removePortFile:n}=await import("./backend-lifecycle.js"),{fileURLToPath:a}=await import("url"),s=o.join(o.dirname(a(import.meta.url)),"backend.js");n(t),e(process.execPath,[s,o.resolve(t)],{detached:!0,stdio:"ignore",env:{...process.env,PROJECT_GRAPH_BACKEND:"1"}}).unref(),setTimeout(()=>process.exit(0),300)},200)):(i.writeHead(200,{"Content-Type":"application/json","Cache-Control":"no-cache, no-store, must-revalidate"}),void i.end(JSON.stringify({error:"Unknown API endpoint"})))}i.writeHead(200,{"Content-Type":"application/json","Access-Control-Allow-Origin":"*","Cache-Control":"no-cache"}),i.end(JSON.stringify(c))}catch(e){i.writeHead(500,{"Content-Type":"application/json","Cache-Control":"no-cache, no-store, must-revalidate"}),i.end(JSON.stringify({error:e.message}))}}P(),k(),c.on("tool:call",e=>{j.events.push(e),j.events.length>500&&j.events.shift(),x("event",e);const _n=e.tool||e.name||"";if("invalidate_cache"===_n||"set_custom_rule"===_n||"delete_custom_rule"===_n)_clearCache();else if("docs"===_n||"compact"===_n||"filters"===_n){const _a=e.args?.action||"";if("generate"===_a||"set_mode"===_a||"set"===_a||"reset"===_a)_clearCache()}}),c.on("tool:result",e=>{j.events.push(e),j.events.length>500&&j.events.shift(),x("event",e)});const B=e.createServer((e,t)=>{const o=new URL(e.url,`http://localhost:${a||0}`);return"OPTIONS"===e.method?(t.writeHead(204,{"Access-Control-Allow-Origin":"*","Access-Control-Allow-Methods":"GET, POST","Access-Control-Allow-Headers":"Content-Type"}),void t.end()):o.pathname.startsWith("/api/")?(z(),void A(o.pathname,o.searchParams,e.method,t)):("/ws/monitor"===o.pathname&&console.log("UNEXPECTED HTTP /ws/monitor",e.headers),void g(o.pathname,t))}),M=new s({noServer:!0});M.on("connection",async e=>{T.add(e),z();const t={project:j.project};try{e.send(JSON.stringify({jsonrpc:"2.0",method:"snapshot",params:{state:t}}))}catch{}e.on("message",async t=>{let n;z();try{n=JSON.parse(t.toString())}catch{return}if(n.jsonrpc&&n.id&&n.method)if("tool"===n.method){const{name:a,args:s}=n.params||{};try{let t;if("compact"===a&&"compact_file"===s?.action&&s?.path){const e=o.resolve(h,s.path),n=o.basename(s.path,o.extname(s.path))+".ctx",a=o.resolve(h,".context",o.dirname(s.path),n);let i=null;try{const{readFileSync:e,existsSync:t}=await import("fs");t(a)&&(i=e(a,"utf-8"))}catch{}const c=await _cf(e,{beautify:false,legend:false}),p=i?Math.ceil(i.length/4):0,m=c.compressed+p;t={code:c.code,file:s.path,codeTok:c.compressed,ctxTok:p,totalTok:m,expanded:c.expanded||c.original,savings:c.savings}}else t=await d.executeTool(a,s||{});e.send(JSON.stringify({jsonrpc:"2.0",id:n.id,result:t}))}catch(t){e.send(JSON.stringify({jsonrpc:"2.0",id:n.id,error:{code:-32e3,message:t.message}}))}}else e.send(JSON.stringify({jsonrpc:"2.0",id:n.id,error:{code:-32601,message:`Unknown method: ${n.method}`}}))}),e.on("close",()=>{T.delete(e),P()}),e.on("error",()=>{T.delete(e),P()})}),B.on("upgrade",(e,t,o)=>{const n=e.headers["sec-websocket-key"];if(n)if("/ws/monitor"!==e.url){if("/mcp-ws"===e.url){const e=u(n);t.write(`HTTP/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: ${e}\r\n\r\n`);const o="agent-"+m++,a=i(e=>{try{t.write(y(JSON.stringify(e)))}catch{}});b.set(t,{agentId:o,mcpServer:a,connectedAt:Date.now()}),O(),S("project.agents",b.size),x("event",{type:"agent_connect",agentId:o,agents:b.size,ts:Date.now()});let s=Buffer.alloc(0);return t.on("data",e=>{for(s=Buffer.concat([s,e]);s.length>=2;){const n=w(s);if(!n)break;if(s=s.slice(n.totalLen),8===n.opcode)return b.delete(t),S("project.agents",b.size),x("event",{type:"agent_disconnect",agentId:o,agents:b.size,ts:Date.now()}),t.end(),void(0===b.size&&P());if(9===n.opcode){const o=Buffer.from(e);o[0]=240&o[0]|10,t.write(o);continue}1===n.opcode&&(async()=>{try{const e=JSON.parse(n.data),o=await a.handleMessage(e);null!==o&&t.write(y(JSON.stringify(o)))}catch(e){t.write(y(JSON.stringify({jsonrpc:"2.0",error:{code:-32700,message:"Parse error"}})))}})()}}),t.on("close",()=>{b.delete(t),S("project.agents",b.size),x("event",{type:"agent_disconnect",agentId:o,agents:b.size,ts:Date.now()}),0===b.size&&P()}),void t.on("error",()=>{b.delete(t),0===b.size&&P()})}t.destroy()}else M.handleUpgrade(e,t,o,t=>{M.emit("connection",t,e)});else t.destroy()});const H=!a,J=a||0;return B.listen(J,"127.0.0.1",()=>{const e=B.address().port;if(H){const n=r("project-graph",e,{projectPath:o.resolve(t),projectName:p});setTimeout(()=>{const a=n.url;console.log(`\n ⬡ project-graph-mcp v${_pkgVersion}`),console.log(" ─────────────────────────────"),console.log(` → ${a}`),console.log(` → ${n.directUrl} (direct)`),console.log(` → Project: ${o.resolve(t)}`),console.log(` → MCP WebSocket: ws://127.0.0.1:${e}/mcp-ws\n`)},200)}else console.log(`\n ⬡ project-graph-mcp v${_pkgVersion}`),console.log(" ─────────────────────────────"),console.log(` → http://localhost:${e}/`),console.log(` → Project: ${o.resolve(t)}`),console.log(` → MCP WebSocket: ws://127.0.0.1:${e}/mcp-ws\n`)}),B}
|
|
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/compression-stats":if(_cache.cs&&Date.now()-_cache.cst<6e4){c=_cache.cs;break}{const{readdirSync:e,statSync:n,readFileSync:a,existsSync:s}=await import("fs"),{join:i,extname:r,basename:d,dirname:p,relative:m}=await import("path"),h=new Set([".js",".mjs"]),f=[],g=["node_modules",".git","vendor",".context",".expanded","web"];!function t(o){try{for(const a of e(o)){if(a.startsWith("."))continue;const e=i(o,a);n(e).isDirectory()?g.includes(a)||t(e):h.has(r(a))&&f.push(e)}}catch{}}(o.resolve(t,"src"));let u=0,y=0,w=0,v=0,_og=0;const j=o.resolve(t);for(const e of f){try{const t=m(j,e),i=d(t,".js"),c=o.resolve(j,".context",p(t),i+".ctx");let r=0;s(c)&&(r=n(c).size,w+=r);try{const t=s(c)?a(c,"utf-8"):null;{const _r=await _cf(e,{beautify:false,legend:false});v+=(_r.expanded||_r.original);u+=_r.compressed;_og+=_r.original}}catch{v+=Math.ceil(1.3*n(e).size/4);_og+=Math.ceil(n(e).size/4)}}catch{continue}y++}c={files:y,codeTok:u,ctxTok:Math.ceil(w/4),totalTok:u+Math.ceil(w/4),expanded:v,original:_og};_cache.cs=c;_cache.cst=Date.now();break}case"/api/docs":{const e=a.get("file");if(e){try{const{readFileSync:n,existsSync:a}=await import("fs"),{basename:s,extname:i,dirname:r}=await import("path"),d=s(e,i(e))+".ctx",p=o.resolve(t,".context",r(e),d);c=a(p)?{docs:n(p,"utf-8"),file:e}:{docs:"",file:e}}catch(t){c={docs:"",file:e}}}else{c=await d.executeTool("docs",{action:"get",path:r})}break}case"/api/analysis":if(_cache.fa&&Date.now()-_cache.fat<12e4){c=_cache.fa}else{c=await d.executeTool("analyze",{action:"full_analysis",path:r});_cache.fa=c;_cache.fat=Date.now()}break;case"/api/analysis-summary":if(_cache.as&&Date.now()-_cache.ast<12e4){c=_cache.as}else{c=await d.executeTool("analyze",{action:"analysis_summary",path:r});_cache.as=c;_cache.ast=Date.now()}break;case"/api/deps":c=await d.executeTool("navigate",{action:"deps",symbol:a.get("symbol")});break;case"/api/usages":c=await d.executeTool("navigate",{action:"usages",symbol:a.get("symbol")});break;case"/api/expand":c=await d.executeTool("navigate",{action:"expand",symbol:a.get("symbol")});break;case"/api/chain":c=await d.executeTool("navigate",{action:"call_chain",from:a.get("from"),to:a.get("to")});break;case"/api/server-status":{const _now=Date.now();c={version:_pkgVersion,uptime:Math.round((_now-_startedAt)/1e3),agents:b.size,monitors:T.size,shutdownAt:_shutdownAt?Math.max(0,Math.round((_shutdownAt-_now)/1e3)):null};break}case"/api/stop":i.writeHead(200,{"Content-Type":"application/json"});i.end(JSON.stringify({ok:true}));setTimeout(()=>process.exit(0),200);return;case"/api/project-info":{const e=o.resolve(t),a=n.createHash("md5").update(e).digest("hex"),s=parseInt(a.slice(0,4),16)%360;c={name:p,path:e,color:`hsl(${s}, 65%, 55%)`,version:_pkgVersion,agents:b.size,pid:process.pid};break}case"/api/instances":try{const{listBackends:e}=await import("./backend-lifecycle.js");c=e()}catch{c=[{name:p,path:o.resolve(t),agents:b.size}]}break;default:return"POST"===s&&"/api/restart"===e?(i.writeHead(200,{"Content-Type":"application/json","Cache-Control":"no-cache"}),i.end(JSON.stringify({ok:!0,message:"Server restarting..."})),void setTimeout(async()=>{const{spawn:e}=await import("child_process"),{removePortFile:n}=await import("./backend-lifecycle.js"),{fileURLToPath:a}=await import("url"),s=o.join(o.dirname(a(import.meta.url)),"backend.js");n(t),e(process.execPath,[s,o.resolve(t)],{detached:!0,stdio:"ignore",env:{...process.env,PROJECT_GRAPH_BACKEND:"1"}}).unref(),setTimeout(()=>process.exit(0),300)},200)):(i.writeHead(200,{"Content-Type":"application/json","Cache-Control":"no-cache, no-store, must-revalidate"}),void i.end(JSON.stringify({error:"Unknown API endpoint"})))}i.writeHead(200,{"Content-Type":"application/json","Access-Control-Allow-Origin":"*","Cache-Control":"no-cache"}),i.end(JSON.stringify(c))}catch(e){i.writeHead(500,{"Content-Type":"application/json","Cache-Control":"no-cache, no-store, must-revalidate"}),i.end(JSON.stringify({error:e.message}))}}P(),k(),c.on("tool:call",e=>{j.events.push(e),j.events.length>500&&j.events.shift(),x("event",e);const _n=e.tool||e.name||"";if("invalidate_cache"===_n||"set_custom_rule"===_n||"delete_custom_rule"===_n)_clearCache();else if("docs"===_n||"compact"===_n||"filters"===_n){const _a=e.args?.action||"";if("generate"===_a||"set_mode"===_a||"set"===_a||"reset"===_a)_clearCache()}}),c.on("tool:result",e=>{j.events.push(e),j.events.length>500&&j.events.shift(),x("event",e)});const B=e.createServer((e,t)=>{const o=new URL(e.url,`http://localhost:${a||0}`);return"OPTIONS"===e.method?(t.writeHead(204,{"Access-Control-Allow-Origin":"*","Access-Control-Allow-Methods":"GET, POST","Access-Control-Allow-Headers":"Content-Type"}),void t.end()):o.pathname.startsWith("/api/")?(z(),void A(o.pathname,o.searchParams,e.method,t)):("/ws/monitor"===o.pathname&&console.log("UNEXPECTED HTTP /ws/monitor",e.headers),void g(o.pathname,t))}),M=new s({noServer:!0});M.on("connection",async e=>{T.add(e),z();const t={project:j.project};try{e.send(JSON.stringify({jsonrpc:"2.0",method:"snapshot",params:{state:t}}))}catch{}e.on("message",async t=>{let n;z();try{n=JSON.parse(t.toString())}catch{return}if(n.jsonrpc&&n.id&&n.method)if("tool"===n.method){const{name:a,args:s}=n.params||{};try{let t;if("compact"===a&&"compact_file"===s?.action&&s?.path){const e=o.resolve(h,s.path),_wsExt=o.extname(s.path).toLowerCase(),_wsJsExts=new Set([".js",".mjs",".ts",".tsx"]);if(_wsJsExts.has(_wsExt)){const n=o.basename(s.path,o.extname(s.path))+".ctx",a=o.resolve(h,".context",o.dirname(s.path),n);let i=null;try{const{readFileSync:e,existsSync:t}=await import("fs");t(a)&&(i=e(a,"utf-8"))}catch{}const c=await _cf(e,{beautify:false,legend:false}),p=i?Math.ceil(i.length/4):0,m=c.compressed+p;t={code:c.code,file:s.path,codeTok:c.compressed,ctxTok:p,totalTok:m,expanded:c.expanded||c.original,savings:c.savings}}else{try{const{readFileSync:rf}=await import("fs");const raw=rf(e,"utf-8"),tok=Math.ceil(raw.length/4);t={code:raw,file:s.path,codeTok:tok,ctxTok:0,totalTok:tok,expanded:tok,savings:"0%",raw:true}}catch(err){t={code:`// Cannot read: ${err.message}`,file:s.path}}}}else t=await d.executeTool(a,s||{});e.send(JSON.stringify({jsonrpc:"2.0",id:n.id,result:t}))}catch(t){e.send(JSON.stringify({jsonrpc:"2.0",id:n.id,error:{code:-32e3,message:t.message}}))}}else e.send(JSON.stringify({jsonrpc:"2.0",id:n.id,error:{code:-32601,message:`Unknown method: ${n.method}`}}))}),e.on("close",()=>{T.delete(e),P()}),e.on("error",()=>{T.delete(e),P()})}),B.on("upgrade",(e,t,o)=>{const n=e.headers["sec-websocket-key"];if(n)if("/ws/monitor"!==e.url){if("/mcp-ws"===e.url){const e=u(n);t.write(`HTTP/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: ${e}\r\n\r\n`);const o="agent-"+m++,a=i(e=>{try{t.write(y(JSON.stringify(e)))}catch{}});b.set(t,{agentId:o,mcpServer:a,connectedAt:Date.now()}),O(),S("project.agents",b.size),x("event",{type:"agent_connect",agentId:o,agents:b.size,ts:Date.now()});let s=Buffer.alloc(0);return t.on("data",e=>{for(s=Buffer.concat([s,e]);s.length>=2;){const n=w(s);if(!n)break;if(s=s.slice(n.totalLen),8===n.opcode)return b.delete(t),S("project.agents",b.size),x("event",{type:"agent_disconnect",agentId:o,agents:b.size,ts:Date.now()}),t.end(),void(0===b.size&&P());if(9===n.opcode){const o=Buffer.from(e);o[0]=240&o[0]|10,t.write(o);continue}1===n.opcode&&(async()=>{try{const e=JSON.parse(n.data),o=await a.handleMessage(e);null!==o&&t.write(y(JSON.stringify(o)))}catch(e){t.write(y(JSON.stringify({jsonrpc:"2.0",error:{code:-32700,message:"Parse error"}})))}})()}}),t.on("close",()=>{b.delete(t),S("project.agents",b.size),x("event",{type:"agent_disconnect",agentId:o,agents:b.size,ts:Date.now()}),0===b.size&&P()}),void t.on("error",()=>{b.delete(t),0===b.size&&P()})}t.destroy()}else M.handleUpgrade(e,t,o,t=>{M.emit("connection",t,e)});else t.destroy()});const H=!a,J=a||0;return B.listen(J,"127.0.0.1",()=>{const e=B.address().port;if(H){const n=r("project-graph",e,{projectPath:o.resolve(t),projectName:p});setTimeout(()=>{const a=n.url;console.log(`\n ⬡ project-graph-mcp v${_pkgVersion}`),console.log(" ─────────────────────────────"),console.log(` → ${a}`),console.log(` → ${n.directUrl} (direct)`),console.log(` → Project: ${o.resolve(t)}`),console.log(` → MCP WebSocket: ws://127.0.0.1:${e}/mcp-ws\n`)},200)}else console.log(`\n ⬡ project-graph-mcp v${_pkgVersion}`),console.log(" ─────────────────────────────"),console.log(` → http://localhost:${e}/`),console.log(` → Project: ${o.resolve(t)}`),console.log(` → MCP WebSocket: ws://127.0.0.1:${e}/mcp-ws\n`)}),B}
|