gitnexus 1.4.0 → 1.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (131) hide show
  1. package/README.md +194 -214
  2. package/dist/cli/ai-context.d.ts +1 -2
  3. package/dist/cli/ai-context.js +90 -117
  4. package/dist/cli/analyze.d.ts +0 -2
  5. package/dist/cli/analyze.js +2 -20
  6. package/dist/cli/index.js +25 -17
  7. package/dist/cli/setup.js +19 -17
  8. package/dist/core/augmentation/engine.js +20 -20
  9. package/dist/core/embeddings/embedding-pipeline.js +26 -26
  10. package/dist/core/graph/types.d.ts +2 -5
  11. package/dist/core/ingestion/ast-cache.js +2 -3
  12. package/dist/core/ingestion/call-processor.d.ts +5 -5
  13. package/dist/core/ingestion/call-processor.js +258 -173
  14. package/dist/core/ingestion/cluster-enricher.js +16 -16
  15. package/dist/core/ingestion/entry-point-scoring.d.ts +1 -2
  16. package/dist/core/ingestion/entry-point-scoring.js +22 -81
  17. package/dist/core/ingestion/framework-detection.d.ts +1 -5
  18. package/dist/core/ingestion/framework-detection.js +8 -39
  19. package/dist/core/ingestion/heritage-processor.d.ts +4 -13
  20. package/dist/core/ingestion/heritage-processor.js +28 -92
  21. package/dist/core/ingestion/import-processor.d.ts +19 -17
  22. package/dist/core/ingestion/import-processor.js +695 -170
  23. package/dist/core/ingestion/parsing-processor.d.ts +10 -1
  24. package/dist/core/ingestion/parsing-processor.js +177 -41
  25. package/dist/core/ingestion/pipeline.js +26 -49
  26. package/dist/core/ingestion/process-processor.js +1 -2
  27. package/dist/core/ingestion/symbol-table.d.ts +1 -12
  28. package/dist/core/ingestion/symbol-table.js +12 -19
  29. package/dist/core/ingestion/tree-sitter-queries.d.ts +11 -11
  30. package/dist/core/ingestion/tree-sitter-queries.js +485 -590
  31. package/dist/core/ingestion/utils.d.ts +0 -67
  32. package/dist/core/ingestion/utils.js +9 -692
  33. package/dist/core/ingestion/workers/parse-worker.d.ts +3 -20
  34. package/dist/core/ingestion/workers/parse-worker.js +345 -84
  35. package/dist/core/ingestion/workers/worker-pool.js +0 -8
  36. package/dist/core/kuzu/csv-generator.js +3 -19
  37. package/dist/core/kuzu/kuzu-adapter.js +19 -14
  38. package/dist/core/kuzu/schema.d.ts +3 -3
  39. package/dist/core/kuzu/schema.js +288 -303
  40. package/dist/core/search/bm25-index.js +6 -7
  41. package/dist/core/search/hybrid-search.js +3 -3
  42. package/dist/core/wiki/diagrams.d.ts +27 -0
  43. package/dist/core/wiki/diagrams.js +163 -0
  44. package/dist/core/wiki/generator.d.ts +50 -2
  45. package/dist/core/wiki/generator.js +548 -49
  46. package/dist/core/wiki/graph-queries.d.ts +42 -0
  47. package/dist/core/wiki/graph-queries.js +276 -97
  48. package/dist/core/wiki/html-viewer.js +192 -192
  49. package/dist/core/wiki/llm-client.js +73 -11
  50. package/dist/core/wiki/prompts.d.ts +52 -8
  51. package/dist/core/wiki/prompts.js +200 -86
  52. package/dist/mcp/core/kuzu-adapter.d.ts +3 -1
  53. package/dist/mcp/core/kuzu-adapter.js +44 -13
  54. package/dist/mcp/local/local-backend.js +128 -128
  55. package/dist/mcp/resources.js +42 -42
  56. package/dist/mcp/server.js +19 -18
  57. package/dist/mcp/tools.js +104 -103
  58. package/hooks/claude/gitnexus-hook.cjs +155 -238
  59. package/hooks/claude/pre-tool-use.sh +79 -79
  60. package/hooks/claude/session-start.sh +42 -42
  61. package/package.json +96 -96
  62. package/scripts/patch-tree-sitter-swift.cjs +74 -74
  63. package/skills/gitnexus-cli.md +82 -82
  64. package/skills/gitnexus-debugging.md +89 -89
  65. package/skills/gitnexus-exploring.md +78 -78
  66. package/skills/gitnexus-guide.md +64 -64
  67. package/skills/gitnexus-impact-analysis.md +97 -97
  68. package/skills/gitnexus-pr-review.md +163 -163
  69. package/skills/gitnexus-refactoring.md +121 -121
  70. package/vendor/leiden/index.cjs +355 -355
  71. package/vendor/leiden/utils.cjs +392 -392
  72. package/dist/cli/lazy-action.d.ts +0 -6
  73. package/dist/cli/lazy-action.js +0 -18
  74. package/dist/cli/skill-gen.d.ts +0 -26
  75. package/dist/cli/skill-gen.js +0 -549
  76. package/dist/core/ingestion/constants.d.ts +0 -16
  77. package/dist/core/ingestion/constants.js +0 -16
  78. package/dist/core/ingestion/export-detection.d.ts +0 -18
  79. package/dist/core/ingestion/export-detection.js +0 -230
  80. package/dist/core/ingestion/language-config.d.ts +0 -46
  81. package/dist/core/ingestion/language-config.js +0 -167
  82. package/dist/core/ingestion/mro-processor.d.ts +0 -45
  83. package/dist/core/ingestion/mro-processor.js +0 -369
  84. package/dist/core/ingestion/named-binding-extraction.d.ts +0 -61
  85. package/dist/core/ingestion/named-binding-extraction.js +0 -363
  86. package/dist/core/ingestion/resolvers/csharp.d.ts +0 -22
  87. package/dist/core/ingestion/resolvers/csharp.js +0 -109
  88. package/dist/core/ingestion/resolvers/go.d.ts +0 -19
  89. package/dist/core/ingestion/resolvers/go.js +0 -42
  90. package/dist/core/ingestion/resolvers/index.d.ts +0 -16
  91. package/dist/core/ingestion/resolvers/index.js +0 -11
  92. package/dist/core/ingestion/resolvers/jvm.d.ts +0 -23
  93. package/dist/core/ingestion/resolvers/jvm.js +0 -87
  94. package/dist/core/ingestion/resolvers/php.d.ts +0 -15
  95. package/dist/core/ingestion/resolvers/php.js +0 -35
  96. package/dist/core/ingestion/resolvers/rust.d.ts +0 -15
  97. package/dist/core/ingestion/resolvers/rust.js +0 -73
  98. package/dist/core/ingestion/resolvers/standard.d.ts +0 -28
  99. package/dist/core/ingestion/resolvers/standard.js +0 -145
  100. package/dist/core/ingestion/resolvers/utils.d.ts +0 -33
  101. package/dist/core/ingestion/resolvers/utils.js +0 -120
  102. package/dist/core/ingestion/symbol-resolver.d.ts +0 -32
  103. package/dist/core/ingestion/symbol-resolver.js +0 -83
  104. package/dist/core/ingestion/type-env.d.ts +0 -27
  105. package/dist/core/ingestion/type-env.js +0 -86
  106. package/dist/core/ingestion/type-extractors/c-cpp.d.ts +0 -2
  107. package/dist/core/ingestion/type-extractors/c-cpp.js +0 -60
  108. package/dist/core/ingestion/type-extractors/csharp.d.ts +0 -2
  109. package/dist/core/ingestion/type-extractors/csharp.js +0 -89
  110. package/dist/core/ingestion/type-extractors/go.d.ts +0 -2
  111. package/dist/core/ingestion/type-extractors/go.js +0 -105
  112. package/dist/core/ingestion/type-extractors/index.d.ts +0 -21
  113. package/dist/core/ingestion/type-extractors/index.js +0 -29
  114. package/dist/core/ingestion/type-extractors/jvm.d.ts +0 -3
  115. package/dist/core/ingestion/type-extractors/jvm.js +0 -121
  116. package/dist/core/ingestion/type-extractors/php.d.ts +0 -2
  117. package/dist/core/ingestion/type-extractors/php.js +0 -31
  118. package/dist/core/ingestion/type-extractors/python.d.ts +0 -2
  119. package/dist/core/ingestion/type-extractors/python.js +0 -41
  120. package/dist/core/ingestion/type-extractors/rust.d.ts +0 -2
  121. package/dist/core/ingestion/type-extractors/rust.js +0 -39
  122. package/dist/core/ingestion/type-extractors/shared.d.ts +0 -17
  123. package/dist/core/ingestion/type-extractors/shared.js +0 -97
  124. package/dist/core/ingestion/type-extractors/swift.d.ts +0 -2
  125. package/dist/core/ingestion/type-extractors/swift.js +0 -43
  126. package/dist/core/ingestion/type-extractors/types.d.ts +0 -14
  127. package/dist/core/ingestion/type-extractors/types.js +0 -1
  128. package/dist/core/ingestion/type-extractors/typescript.d.ts +0 -2
  129. package/dist/core/ingestion/type-extractors/typescript.js +0 -46
  130. package/dist/mcp/compatible-stdio-transport.d.ts +0 -25
  131. package/dist/mcp/compatible-stdio-transport.js +0 -200
@@ -1,31 +0,0 @@
1
- import { extractSimpleTypeName, extractVarName } from './shared.js';
2
- // PHP has no local variable type annotations; only params carry types
3
- const DECLARATION_NODE_TYPES = new Set();
4
- /** PHP: no typed local variable declarations */
5
- const extractDeclaration = (_node, _env) => {
6
- // PHP has no local variable type annotations
7
- };
8
- /** PHP: simple_parameter → type $name */
9
- const extractParameter = (node, env) => {
10
- let nameNode = null;
11
- let typeNode = null;
12
- if (node.type === 'simple_parameter') {
13
- typeNode = node.childForFieldName('type');
14
- nameNode = node.childForFieldName('name');
15
- }
16
- else {
17
- nameNode = node.childForFieldName('name') ?? node.childForFieldName('pattern');
18
- typeNode = node.childForFieldName('type');
19
- }
20
- if (!nameNode || !typeNode)
21
- return;
22
- const varName = extractVarName(nameNode);
23
- const typeName = extractSimpleTypeName(typeNode);
24
- if (varName && typeName)
25
- env.set(varName, typeName);
26
- };
27
- export const typeConfig = {
28
- declarationNodeTypes: DECLARATION_NODE_TYPES,
29
- extractDeclaration,
30
- extractParameter,
31
- };
@@ -1,2 +0,0 @@
1
- import type { LanguageTypeConfig } from './types.js';
2
- export declare const typeConfig: LanguageTypeConfig;
@@ -1,41 +0,0 @@
1
- import { extractSimpleTypeName, extractVarName } from './shared.js';
2
- const DECLARATION_NODE_TYPES = new Set([
3
- 'assignment',
4
- ]);
5
- /** Python: x: Foo = ... (PEP 484 annotations) */
6
- const extractDeclaration = (node, env) => {
7
- // Python annotated assignment: left : type = value
8
- // tree-sitter represents this differently based on grammar version
9
- const left = node.childForFieldName('left');
10
- const typeNode = node.childForFieldName('type');
11
- if (!left || !typeNode)
12
- return;
13
- const varName = extractVarName(left);
14
- const typeName = extractSimpleTypeName(typeNode);
15
- if (varName && typeName)
16
- env.set(varName, typeName);
17
- };
18
- /** Python: parameter with type annotation */
19
- const extractParameter = (node, env) => {
20
- let nameNode = null;
21
- let typeNode = null;
22
- if (node.type === 'parameter') {
23
- nameNode = node.childForFieldName('name');
24
- typeNode = node.childForFieldName('type');
25
- }
26
- else {
27
- nameNode = node.childForFieldName('name') ?? node.childForFieldName('pattern');
28
- typeNode = node.childForFieldName('type');
29
- }
30
- if (!nameNode || !typeNode)
31
- return;
32
- const varName = extractVarName(nameNode);
33
- const typeName = extractSimpleTypeName(typeNode);
34
- if (varName && typeName)
35
- env.set(varName, typeName);
36
- };
37
- export const typeConfig = {
38
- declarationNodeTypes: DECLARATION_NODE_TYPES,
39
- extractDeclaration,
40
- extractParameter,
41
- };
@@ -1,2 +0,0 @@
1
- import type { LanguageTypeConfig } from './types.js';
2
- export declare const typeConfig: LanguageTypeConfig;
@@ -1,39 +0,0 @@
1
- import { extractSimpleTypeName, extractVarName } from './shared.js';
2
- const DECLARATION_NODE_TYPES = new Set([
3
- 'let_declaration',
4
- ]);
5
- /** Rust: let x: Foo = ... */
6
- const extractDeclaration = (node, env) => {
7
- const pattern = node.childForFieldName('pattern');
8
- const typeNode = node.childForFieldName('type');
9
- if (!pattern || !typeNode)
10
- return;
11
- const varName = extractVarName(pattern);
12
- const typeName = extractSimpleTypeName(typeNode);
13
- if (varName && typeName)
14
- env.set(varName, typeName);
15
- };
16
- /** Rust: parameter → pattern: type */
17
- const extractParameter = (node, env) => {
18
- let nameNode = null;
19
- let typeNode = null;
20
- if (node.type === 'parameter') {
21
- nameNode = node.childForFieldName('pattern');
22
- typeNode = node.childForFieldName('type');
23
- }
24
- else {
25
- nameNode = node.childForFieldName('name') ?? node.childForFieldName('pattern');
26
- typeNode = node.childForFieldName('type');
27
- }
28
- if (!nameNode || !typeNode)
29
- return;
30
- const varName = extractVarName(nameNode);
31
- const typeName = extractSimpleTypeName(typeNode);
32
- if (varName && typeName)
33
- env.set(varName, typeName);
34
- };
35
- export const typeConfig = {
36
- declarationNodeTypes: DECLARATION_NODE_TYPES,
37
- extractDeclaration,
38
- extractParameter,
39
- };
@@ -1,17 +0,0 @@
1
- import type { SyntaxNode } from '../utils.js';
2
- /**
3
- * Extract the simple type name from a type AST node.
4
- * Handles generic types (e.g., List<User> → List), qualified names
5
- * (e.g., models.User → User), and nullable types (e.g., User? → User).
6
- * Returns undefined for complex types (unions, intersections, function types).
7
- */
8
- export declare const extractSimpleTypeName: (typeNode: SyntaxNode) => string | undefined;
9
- /**
10
- * Extract variable name from a declarator or pattern node.
11
- * Returns the simple identifier text, or undefined for destructuring/complex patterns.
12
- */
13
- export declare const extractVarName: (node: SyntaxNode) => string | undefined;
14
- /** Node types for function/method parameters with type annotations */
15
- export declare const TYPED_PARAMETER_TYPES: Set<string>;
16
- /** Find the first named child with the given node type */
17
- export declare const findChildByType: (node: SyntaxNode, type: string) => SyntaxNode | null;
@@ -1,97 +0,0 @@
1
- /**
2
- * Extract the simple type name from a type AST node.
3
- * Handles generic types (e.g., List<User> → List), qualified names
4
- * (e.g., models.User → User), and nullable types (e.g., User? → User).
5
- * Returns undefined for complex types (unions, intersections, function types).
6
- */
7
- export const extractSimpleTypeName = (typeNode) => {
8
- // Direct type identifier
9
- if (typeNode.type === 'type_identifier' || typeNode.type === 'identifier'
10
- || typeNode.type === 'simple_identifier') {
11
- return typeNode.text;
12
- }
13
- // Qualified/scoped names: take the last segment (e.g., models.User → User)
14
- if (typeNode.type === 'scoped_identifier' || typeNode.type === 'qualified_identifier'
15
- || typeNode.type === 'scoped_type_identifier' || typeNode.type === 'qualified_name'
16
- || typeNode.type === 'qualified_type'
17
- || typeNode.type === 'member_expression' || typeNode.type === 'attribute') {
18
- const last = typeNode.lastNamedChild;
19
- if (last && (last.type === 'type_identifier' || last.type === 'identifier'
20
- || last.type === 'simple_identifier' || last.type === 'name')) {
21
- return last.text;
22
- }
23
- }
24
- // Generic types: extract the base type (e.g., List<User> → List)
25
- if (typeNode.type === 'generic_type' || typeNode.type === 'parameterized_type') {
26
- const base = typeNode.childForFieldName('name')
27
- ?? typeNode.childForFieldName('type')
28
- ?? typeNode.firstNamedChild;
29
- if (base)
30
- return extractSimpleTypeName(base);
31
- }
32
- // Nullable types (Kotlin User?, C# User?)
33
- if (typeNode.type === 'nullable_type') {
34
- const inner = typeNode.firstNamedChild;
35
- if (inner)
36
- return extractSimpleTypeName(inner);
37
- }
38
- // Type annotations that wrap the actual type (TS/Python: `: Foo`, Kotlin: user_type)
39
- if (typeNode.type === 'type_annotation' || typeNode.type === 'type'
40
- || typeNode.type === 'user_type') {
41
- const inner = typeNode.firstNamedChild;
42
- if (inner)
43
- return extractSimpleTypeName(inner);
44
- }
45
- // Pointer/reference types (C++, Rust): User*, &User, &mut User
46
- if (typeNode.type === 'pointer_type' || typeNode.type === 'reference_type') {
47
- const inner = typeNode.firstNamedChild;
48
- if (inner)
49
- return extractSimpleTypeName(inner);
50
- }
51
- // PHP named_type / optional_type
52
- if (typeNode.type === 'named_type' || typeNode.type === 'optional_type') {
53
- const inner = typeNode.childForFieldName('name') ?? typeNode.firstNamedChild;
54
- if (inner)
55
- return extractSimpleTypeName(inner);
56
- }
57
- // Name node (PHP)
58
- if (typeNode.type === 'name') {
59
- return typeNode.text;
60
- }
61
- return undefined;
62
- };
63
- /**
64
- * Extract variable name from a declarator or pattern node.
65
- * Returns the simple identifier text, or undefined for destructuring/complex patterns.
66
- */
67
- export const extractVarName = (node) => {
68
- if (node.type === 'identifier' || node.type === 'simple_identifier'
69
- || node.type === 'variable_name' || node.type === 'name') {
70
- return node.text;
71
- }
72
- // variable_declarator (Java/C#): has a 'name' field
73
- if (node.type === 'variable_declarator') {
74
- const nameChild = node.childForFieldName('name');
75
- if (nameChild)
76
- return extractVarName(nameChild);
77
- }
78
- return undefined;
79
- };
80
- /** Node types for function/method parameters with type annotations */
81
- export const TYPED_PARAMETER_TYPES = new Set([
82
- 'required_parameter', // TS: (x: Foo)
83
- 'optional_parameter', // TS: (x?: Foo)
84
- 'formal_parameter', // Java/Kotlin
85
- 'parameter', // C#/Rust/Go/Python/Swift
86
- 'parameter_declaration', // C/C++ void f(Type name)
87
- 'simple_parameter', // PHP function(Foo $x)
88
- ]);
89
- /** Find the first named child with the given node type */
90
- export const findChildByType = (node, type) => {
91
- for (let i = 0; i < node.namedChildCount; i++) {
92
- const child = node.namedChild(i);
93
- if (child?.type === type)
94
- return child;
95
- }
96
- return null;
97
- };
@@ -1,2 +0,0 @@
1
- import type { LanguageTypeConfig } from './types.js';
2
- export declare const typeConfig: LanguageTypeConfig;
@@ -1,43 +0,0 @@
1
- import { extractSimpleTypeName, extractVarName, findChildByType } from './shared.js';
2
- const DECLARATION_NODE_TYPES = new Set([
3
- 'property_declaration',
4
- ]);
5
- /** Swift: let x: Foo = ... */
6
- const extractDeclaration = (node, env) => {
7
- // Swift property_declaration has pattern and type_annotation
8
- const pattern = node.childForFieldName('pattern')
9
- ?? findChildByType(node, 'pattern');
10
- const typeAnnotation = node.childForFieldName('type')
11
- ?? findChildByType(node, 'type_annotation');
12
- if (!pattern || !typeAnnotation)
13
- return;
14
- const varName = extractVarName(pattern) ?? pattern.text;
15
- const typeName = extractSimpleTypeName(typeAnnotation);
16
- if (varName && typeName)
17
- env.set(varName, typeName);
18
- };
19
- /** Swift: parameter → name: type */
20
- const extractParameter = (node, env) => {
21
- let nameNode = null;
22
- let typeNode = null;
23
- if (node.type === 'parameter') {
24
- nameNode = node.childForFieldName('name')
25
- ?? node.childForFieldName('internal_name');
26
- typeNode = node.childForFieldName('type');
27
- }
28
- else {
29
- nameNode = node.childForFieldName('name') ?? node.childForFieldName('pattern');
30
- typeNode = node.childForFieldName('type');
31
- }
32
- if (!nameNode || !typeNode)
33
- return;
34
- const varName = extractVarName(nameNode);
35
- const typeName = extractSimpleTypeName(typeNode);
36
- if (varName && typeName)
37
- env.set(varName, typeName);
38
- };
39
- export const typeConfig = {
40
- declarationNodeTypes: DECLARATION_NODE_TYPES,
41
- extractDeclaration,
42
- extractParameter,
43
- };
@@ -1,14 +0,0 @@
1
- import type { SyntaxNode } from '../utils.js';
2
- /** Extracts type bindings from a declaration node into the env map */
3
- export type TypeBindingExtractor = (node: SyntaxNode, env: Map<string, string>) => void;
4
- /** Extracts type bindings from a parameter node into the env map */
5
- export type ParameterExtractor = (node: SyntaxNode, env: Map<string, string>) => void;
6
- /** Per-language type extraction configuration */
7
- export interface LanguageTypeConfig {
8
- /** Node types that represent typed declarations for this language */
9
- declarationNodeTypes: ReadonlySet<string>;
10
- /** Extract a (varName → typeName) binding from a declaration node */
11
- extractDeclaration: TypeBindingExtractor;
12
- /** Extract a (varName → typeName) binding from a parameter node */
13
- extractParameter: ParameterExtractor;
14
- }
@@ -1 +0,0 @@
1
- export {};
@@ -1,2 +0,0 @@
1
- import type { LanguageTypeConfig } from './types.js';
2
- export declare const typeConfig: LanguageTypeConfig;
@@ -1,46 +0,0 @@
1
- import { extractSimpleTypeName, extractVarName } from './shared.js';
2
- const DECLARATION_NODE_TYPES = new Set([
3
- 'lexical_declaration',
4
- 'variable_declaration',
5
- ]);
6
- /** TypeScript: const x: Foo = ..., let x: Foo */
7
- const extractDeclaration = (node, env) => {
8
- for (let i = 0; i < node.namedChildCount; i++) {
9
- const declarator = node.namedChild(i);
10
- if (declarator?.type !== 'variable_declarator')
11
- continue;
12
- const nameNode = declarator.childForFieldName('name');
13
- const typeAnnotation = declarator.childForFieldName('type');
14
- if (!nameNode || !typeAnnotation)
15
- continue;
16
- const varName = extractVarName(nameNode);
17
- const typeName = extractSimpleTypeName(typeAnnotation);
18
- if (varName && typeName)
19
- env.set(varName, typeName);
20
- }
21
- };
22
- /** TypeScript: required_parameter / optional_parameter → name: type */
23
- const extractParameter = (node, env) => {
24
- let nameNode = null;
25
- let typeNode = null;
26
- if (node.type === 'required_parameter' || node.type === 'optional_parameter') {
27
- nameNode = node.childForFieldName('pattern') ?? node.childForFieldName('name');
28
- typeNode = node.childForFieldName('type');
29
- }
30
- else {
31
- // Generic fallback
32
- nameNode = node.childForFieldName('name') ?? node.childForFieldName('pattern');
33
- typeNode = node.childForFieldName('type');
34
- }
35
- if (!nameNode || !typeNode)
36
- return;
37
- const varName = extractVarName(nameNode);
38
- const typeName = extractSimpleTypeName(typeNode);
39
- if (varName && typeName)
40
- env.set(varName, typeName);
41
- };
42
- export const typeConfig = {
43
- declarationNodeTypes: DECLARATION_NODE_TYPES,
44
- extractDeclaration,
45
- extractParameter,
46
- };
@@ -1,25 +0,0 @@
1
- import type { Transport, TransportSendOptions } from '@modelcontextprotocol/sdk/shared/transport.js';
2
- import { type JSONRPCMessage } from '@modelcontextprotocol/sdk/types.js';
3
- export type StdioFraming = 'content-length' | 'newline';
4
- export declare class CompatibleStdioServerTransport implements Transport {
5
- private readonly _stdin;
6
- private readonly _stdout;
7
- private _readBuffer;
8
- private _started;
9
- private _framing;
10
- onmessage?: (message: JSONRPCMessage) => void;
11
- onerror?: (error: Error) => void;
12
- onclose?: () => void;
13
- constructor(_stdin?: NodeJS.ReadableStream, _stdout?: NodeJS.WritableStream);
14
- private readonly _ondata;
15
- private readonly _onerror;
16
- start(): Promise<void>;
17
- private detectFraming;
18
- private discardBufferedInput;
19
- private readContentLengthMessage;
20
- private readNewlineMessage;
21
- private readMessage;
22
- private processReadBuffer;
23
- close(): Promise<void>;
24
- send(message: JSONRPCMessage, _options?: TransportSendOptions): Promise<void>;
25
- }
@@ -1,200 +0,0 @@
1
- import process from 'node:process';
2
- import { JSONRPCMessageSchema } from '@modelcontextprotocol/sdk/types.js';
3
- function deserializeMessage(raw) {
4
- return JSONRPCMessageSchema.parse(JSON.parse(raw));
5
- }
6
- function serializeNewlineMessage(message) {
7
- return `${JSON.stringify(message)}\n`;
8
- }
9
- function serializeContentLengthMessage(message) {
10
- const body = JSON.stringify(message);
11
- return `Content-Length: ${Buffer.byteLength(body, 'utf8')}\r\n\r\n${body}`;
12
- }
13
- function findHeaderEnd(buffer) {
14
- const crlfEnd = buffer.indexOf('\r\n\r\n');
15
- if (crlfEnd !== -1) {
16
- return { index: crlfEnd, separatorLength: 4 };
17
- }
18
- const lfEnd = buffer.indexOf('\n\n');
19
- if (lfEnd !== -1) {
20
- return { index: lfEnd, separatorLength: 2 };
21
- }
22
- return null;
23
- }
24
- function looksLikeContentLength(buffer) {
25
- if (buffer.length < 14) {
26
- return false;
27
- }
28
- const probe = buffer.toString('utf8', 0, Math.min(buffer.length, 32));
29
- return /^content-length\s*:/i.test(probe);
30
- }
31
- const MAX_BUFFER_SIZE = 10 * 1024 * 1024; // 10 MB — generous for JSON-RPC
32
- export class CompatibleStdioServerTransport {
33
- _stdin;
34
- _stdout;
35
- _readBuffer;
36
- _started = false;
37
- _framing = null;
38
- onmessage;
39
- onerror;
40
- onclose;
41
- constructor(_stdin = process.stdin, _stdout = process.stdout) {
42
- this._stdin = _stdin;
43
- this._stdout = _stdout;
44
- }
45
- _ondata = (chunk) => {
46
- this._readBuffer = this._readBuffer ? Buffer.concat([this._readBuffer, chunk]) : chunk;
47
- if (this._readBuffer.length > MAX_BUFFER_SIZE) {
48
- this.onerror?.(new Error(`Read buffer exceeded maximum size (${MAX_BUFFER_SIZE} bytes)`));
49
- this.discardBufferedInput();
50
- return;
51
- }
52
- this.processReadBuffer();
53
- };
54
- _onerror = (error) => {
55
- this.onerror?.(error);
56
- };
57
- async start() {
58
- if (this._started) {
59
- throw new Error('CompatibleStdioServerTransport already started!');
60
- }
61
- this._started = true;
62
- this._stdin.on('data', this._ondata);
63
- this._stdin.on('error', this._onerror);
64
- }
65
- detectFraming() {
66
- if (!this._readBuffer || this._readBuffer.length === 0) {
67
- return null;
68
- }
69
- const firstByte = this._readBuffer[0];
70
- if (firstByte === 0x7b || firstByte === 0x5b) {
71
- return 'newline';
72
- }
73
- if (looksLikeContentLength(this._readBuffer)) {
74
- return 'content-length';
75
- }
76
- return null;
77
- }
78
- discardBufferedInput() {
79
- this._readBuffer = undefined;
80
- this._framing = null;
81
- }
82
- readContentLengthMessage() {
83
- if (!this._readBuffer) {
84
- return null;
85
- }
86
- const header = findHeaderEnd(this._readBuffer);
87
- if (header === null) {
88
- return null;
89
- }
90
- const headerText = this._readBuffer
91
- .toString('utf8', 0, header.index)
92
- .replace(/\r\n/g, '\n')
93
- .replace(/\r/g, '\n');
94
- const match = headerText.match(/(?:^|\n)content-length\s*:\s*(\d+)/i);
95
- if (!match) {
96
- this.discardBufferedInput();
97
- throw new Error('Missing Content-Length header from MCP client');
98
- }
99
- const contentLength = Number.parseInt(match[1], 10);
100
- if (!Number.isFinite(contentLength) || contentLength < 0) {
101
- this.discardBufferedInput();
102
- throw new Error('Invalid Content-Length header from MCP client');
103
- }
104
- if (contentLength > MAX_BUFFER_SIZE) {
105
- this.discardBufferedInput();
106
- throw new Error(`Content-Length ${contentLength} exceeds maximum allowed size (${MAX_BUFFER_SIZE} bytes)`);
107
- }
108
- const bodyStart = header.index + header.separatorLength;
109
- const bodyEnd = bodyStart + contentLength;
110
- if (this._readBuffer.length < bodyEnd) {
111
- return null;
112
- }
113
- const body = this._readBuffer.toString('utf8', bodyStart, bodyEnd);
114
- this._readBuffer = this._readBuffer.subarray(bodyEnd);
115
- return deserializeMessage(body);
116
- }
117
- readNewlineMessage() {
118
- if (!this._readBuffer) {
119
- return null;
120
- }
121
- while (true) {
122
- const newlineIndex = this._readBuffer.indexOf('\n');
123
- if (newlineIndex === -1) {
124
- return null;
125
- }
126
- const line = this._readBuffer.toString('utf8', 0, newlineIndex).replace(/\r$/, '');
127
- this._readBuffer = this._readBuffer.subarray(newlineIndex + 1);
128
- if (line.trim().length === 0) {
129
- continue;
130
- }
131
- return deserializeMessage(line);
132
- }
133
- }
134
- readMessage() {
135
- if (!this._readBuffer || this._readBuffer.length === 0) {
136
- return null;
137
- }
138
- if (this._framing === null) {
139
- this._framing = this.detectFraming();
140
- if (this._framing === null) {
141
- return null;
142
- }
143
- }
144
- return this._framing === 'content-length'
145
- ? this.readContentLengthMessage()
146
- : this.readNewlineMessage();
147
- }
148
- processReadBuffer() {
149
- while (true) {
150
- try {
151
- const message = this.readMessage();
152
- if (message === null) {
153
- break;
154
- }
155
- this.onmessage?.(message);
156
- }
157
- catch (error) {
158
- this.onerror?.(error);
159
- break;
160
- }
161
- }
162
- }
163
- async close() {
164
- this._stdin.off('data', this._ondata);
165
- this._stdin.off('error', this._onerror);
166
- const remainingDataListeners = this._stdin.listenerCount('data');
167
- if (remainingDataListeners === 0) {
168
- this._stdin.pause();
169
- }
170
- this._started = false;
171
- this._readBuffer = undefined;
172
- this.onclose?.();
173
- }
174
- send(message, _options) {
175
- return new Promise((resolve, reject) => {
176
- if (!this._started) {
177
- reject(new Error('Transport is closed'));
178
- return;
179
- }
180
- const payload = this._framing === 'newline'
181
- ? serializeNewlineMessage(message)
182
- : serializeContentLengthMessage(message);
183
- const onError = (error) => {
184
- this._stdout.removeListener('error', onError);
185
- reject(error);
186
- };
187
- this._stdout.on('error', onError);
188
- if (this._stdout.write(payload)) {
189
- this._stdout.removeListener('error', onError);
190
- resolve();
191
- }
192
- else {
193
- this._stdout.once('drain', () => {
194
- this._stdout.removeListener('error', onError);
195
- resolve();
196
- });
197
- }
198
- });
199
- }
200
- }