regen-koi-mcp 1.0.4 → 1.0.5

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/README.md CHANGED
@@ -114,12 +114,26 @@ This MCP server gives AI assistants access to Regen Network's comprehensive know
114
114
 
115
115
  ## 📦 Available Tools
116
116
 
117
+ ### Knowledge Base Search
117
118
  | Tool | Description | Key Inputs |
118
119
  |------|-------------|-----------|
119
120
  | `search_knowledge` | Hybrid search (vectors + graph with RRF) | `query` (string), `limit` (1–20, default 5), `published_from` (YYYY‑MM‑DD), `published_to` (YYYY‑MM‑DD), `include_undated` (bool, default false) |
121
+ | `hybrid_search` | Intelligent search routing (auto-detects entity vs conceptual queries) | `query` (string), `limit` (1–50, default 10) |
120
122
  | `get_stats` | Knowledge base statistics | `detailed` (boolean) |
121
123
  | `generate_weekly_digest` | Generate weekly digest of Regen Network activity | `start_date` (YYYY-MM-DD, default: 7 days ago), `end_date` (YYYY-MM-DD, default: today), `save_to_file` (bool, default false), `output_path` (string), `format` ('markdown' or 'json', default: 'markdown') |
122
124
 
125
+ ### Code Knowledge Graph
126
+ | Tool | Description | Key Inputs |
127
+ |------|-------------|-----------|
128
+ | `query_code_graph` | Query relationships between Keepers, Messages, Events, and Documentation | `query_type` (enum: keeper_for_msg, msgs_for_keeper, docs_mentioning, entities_in_doc, related_entities), `entity_name` (string), `doc_path` (string) |
129
+
130
+ ### GitHub Documentation
131
+ | Tool | Description | Key Inputs |
132
+ |------|-------------|-----------|
133
+ | `search_github_docs` | Search Regen GitHub repos for documentation and technical content | `query` (string), `repository` (optional: regen-ledger, regen-web, regen-data-standards, regenie-corpus), `limit` (1-20, default 10) |
134
+ | `get_repo_overview` | Get structured overview of a Regen repository | `repository` (enum: regen-ledger, regen-web, regen-data-standards, regenie-corpus) |
135
+ | `get_tech_stack` | Get technical stack information for Regen repositories | `repository` (optional, omit to show all repos) |
136
+
123
137
  ## 🏗️ Architecture
124
138
 
125
139
  This repo contains everything you need to run a complete KOI MCP setup:
@@ -588,6 +602,43 @@ Built by the Regen Network community to make ecological knowledge accessible to
588
602
 
589
603
  ---
590
604
 
605
+ ## 🛠️ Developer & Deployment Documentation
606
+
607
+ For developers and operators who want to run their own instance or contribute:
608
+
609
+ | Document | Description |
610
+ |----------|-------------|
611
+ | [docs/INFRASTRUCTURE.md](docs/INFRASTRUCTURE.md) | Server requirements, PostgreSQL setup, Apache AGE installation |
612
+ | [docs/DEPLOYMENT.md](docs/DEPLOYMENT.md) | Production deployment guide, systemd setup, monitoring |
613
+ | [docs/TECHNICAL_ASSISTANT_PROJECT.md](docs/TECHNICAL_ASSISTANT_PROJECT.md) | Full project tracking and implementation details |
614
+ | [ARCHITECTURE.md](ARCHITECTURE.md) | System architecture and component overview |
615
+
616
+ ### Quick Local Development with Docker
617
+
618
+ Spin up a complete development stack with PostgreSQL + pgvector + Apache AGE:
619
+
620
+ ```bash
621
+ cd docker/
622
+ docker-compose up -d
623
+ ```
624
+
625
+ This starts everything needed for full functionality including graph queries.
626
+
627
+ ### Deploy Scripts
628
+
629
+ ```bash
630
+ # Install Apache AGE on existing PostgreSQL
631
+ ./scripts/deploy/install-apache-age.sh
632
+
633
+ # Setup database schema and extensions
634
+ ./scripts/deploy/setup-database.sh
635
+
636
+ # Load graph data (entities, summaries, relationships)
637
+ ./scripts/deploy/load-graph-data.sh
638
+ ```
639
+
640
+ ---
641
+
591
642
  ## 🏗️ Related Repositories
592
643
 
593
644
  This MCP client is part of the larger KOI ecosystem:
@@ -0,0 +1,204 @@
1
+ /**
2
+ * Graph Client - Apache AGE Query Abstraction Layer
3
+ *
4
+ * Provides a clean interface for querying the Regen code knowledge graph
5
+ * stored in PostgreSQL with Apache AGE extension.
6
+ */
7
+ interface GraphClientConfig {
8
+ host: string;
9
+ port: number;
10
+ database: string;
11
+ user?: string;
12
+ password?: string;
13
+ graphName: string;
14
+ }
15
+ export interface Keeper {
16
+ name: string;
17
+ file_path: string;
18
+ line_number: number;
19
+ docstring?: string;
20
+ fields?: string[];
21
+ }
22
+ export interface Msg {
23
+ name: string;
24
+ file_path: string;
25
+ line_number: number;
26
+ docstring?: string;
27
+ fields?: string[];
28
+ package?: string;
29
+ }
30
+ export interface Document {
31
+ title: string;
32
+ file_path: string;
33
+ rid?: string;
34
+ content?: string;
35
+ }
36
+ export interface Entity {
37
+ type: string;
38
+ name: string;
39
+ file_path?: string;
40
+ line_number?: number;
41
+ docstring?: string;
42
+ }
43
+ export interface RelatedEntity {
44
+ name: string;
45
+ type: string;
46
+ shared_docs?: number;
47
+ }
48
+ export interface Module {
49
+ name: string;
50
+ repo: string;
51
+ path: string;
52
+ summary?: string;
53
+ entity_count: number;
54
+ }
55
+ export interface ModuleSearchResult {
56
+ name: string;
57
+ repo: string;
58
+ path: string;
59
+ summary?: string;
60
+ entity_count: number;
61
+ score?: number;
62
+ }
63
+ export interface ModuleEntity {
64
+ type: string;
65
+ name: string;
66
+ file_path?: string;
67
+ line_number?: number;
68
+ }
69
+ export interface KeeperForMsgResult {
70
+ keeper_name: string;
71
+ keeper_file_path: string;
72
+ keeper_line_number: number;
73
+ }
74
+ export interface MsgForKeeperResult {
75
+ msg_name: string;
76
+ msg_package?: string;
77
+ }
78
+ export interface DocMentioningResult {
79
+ file_path: string;
80
+ title: string;
81
+ }
82
+ export interface EntityInDocResult {
83
+ type: string;
84
+ name: string;
85
+ }
86
+ /**
87
+ * GraphClient - Interface for querying the code knowledge graph
88
+ */
89
+ export declare class GraphClient {
90
+ private pool;
91
+ private graphName;
92
+ constructor(config: GraphClientConfig);
93
+ /**
94
+ * Close the database connection pool
95
+ */
96
+ close(): Promise<void>;
97
+ /**
98
+ * Execute a raw Cypher query
99
+ */
100
+ private executeCypher;
101
+ /**
102
+ * Query 1: Find which Keeper handles a given Msg
103
+ */
104
+ getKeeperForMsg(msgName: string): Promise<KeeperForMsgResult[]>;
105
+ /**
106
+ * Query 2: List all Msgs a Keeper handles
107
+ */
108
+ getMsgsForKeeper(keeperName: string): Promise<MsgForKeeperResult[]>;
109
+ /**
110
+ * Query 3: Find documents that mention an entity
111
+ */
112
+ getDocsMentioning(entityName: string): Promise<DocMentioningResult[]>;
113
+ /**
114
+ * Query 4: Find all entities mentioned in a document
115
+ */
116
+ getEntitiesInDoc(docPath: string): Promise<EntityInDocResult[]>;
117
+ /**
118
+ * Query 5: Find entities related to a given entity (via shared docs or direct edges)
119
+ */
120
+ getRelatedEntities(entityName: string): Promise<RelatedEntity[]>;
121
+ /**
122
+ * Get graph statistics
123
+ */
124
+ getStats(): Promise<{
125
+ node_counts: Record<string, number>;
126
+ edge_counts: Record<string, number>;
127
+ }>;
128
+ /**
129
+ * Search for entities by name pattern
130
+ */
131
+ searchEntities(pattern: string, entityType?: string): Promise<Entity[]>;
132
+ /**
133
+ * Get all Keepers
134
+ */
135
+ getAllKeepers(): Promise<Keeper[]>;
136
+ /**
137
+ * Get all Msgs
138
+ */
139
+ getAllMsgs(): Promise<Msg[]>;
140
+ /**
141
+ * Get entities by repository name
142
+ */
143
+ getEntitiesByRepo(repoName: string, limit?: number): Promise<Entity[]>;
144
+ /**
145
+ * Get entities by type (Sensor, Handler, Class, Interface, Function, etc.)
146
+ */
147
+ getEntitiesByType(typeName: string, limit?: number): Promise<Entity[]>;
148
+ /**
149
+ * Search entities by name (case-insensitive contains)
150
+ */
151
+ findEntitiesByName(searchTerm: string, limit?: number): Promise<Entity[]>;
152
+ /**
153
+ * Get all repositories with entity counts
154
+ */
155
+ getRepositories(): Promise<Array<{
156
+ name: string;
157
+ entity_count: number;
158
+ }>>;
159
+ /**
160
+ * Get entity type distribution for a repository
161
+ */
162
+ getRepoEntityTypes(repoName: string): Promise<Array<{
163
+ type: string;
164
+ count: number;
165
+ }>>;
166
+ /**
167
+ * Get all modules with their summaries
168
+ */
169
+ getAllModules(limit?: number): Promise<Module[]>;
170
+ /**
171
+ * Get a specific module by name
172
+ */
173
+ getModule(moduleName: string, repo?: string): Promise<Module | null>;
174
+ /**
175
+ * Search modules by name pattern (case-insensitive)
176
+ */
177
+ searchModules(pattern: string, limit?: number): Promise<ModuleSearchResult[]>;
178
+ /**
179
+ * Get all entities contained in a module
180
+ */
181
+ getModuleEntities(moduleName: string, repo?: string, limit?: number): Promise<ModuleEntity[]>;
182
+ /**
183
+ * Get modules by repository
184
+ */
185
+ getModulesByRepo(repoName: string, limit?: number): Promise<Module[]>;
186
+ /**
187
+ * Find which module contains a given entity
188
+ */
189
+ getModuleForEntity(entityName: string): Promise<Module | null>;
190
+ /**
191
+ * Get module statistics (count by repo)
192
+ */
193
+ getModuleStats(): Promise<Array<{
194
+ repo: string;
195
+ module_count: number;
196
+ total_entities: number;
197
+ }>>;
198
+ }
199
+ /**
200
+ * Create a default GraphClient instance using environment variables
201
+ */
202
+ export declare function createGraphClient(): GraphClient;
203
+ export {};
204
+ //# sourceMappingURL=graph_client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"graph_client.d.ts","sourceRoot":"","sources":["../src/graph_client.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAOH,UAAU,iBAAiB;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB;AAGD,MAAM,WAAW,MAAM;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;CACnB;AAED,MAAM,WAAW,GAAG;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,QAAQ;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,MAAM;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAGD,MAAM,WAAW,MAAM;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAGD,MAAM,WAAW,kBAAkB;IACjC,WAAW,EAAE,MAAM,CAAC;IACpB,gBAAgB,EAAE,MAAM,CAAC;IACzB,kBAAkB,EAAE,MAAM,CAAC;CAC5B;AAED,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,mBAAmB;IAClC,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;GAEG;AACH,qBAAa,WAAW;IACtB,OAAO,CAAC,IAAI,CAAW;IACvB,OAAO,CAAC,SAAS,CAAS;gBAEd,MAAM,EAAE,iBAAiB;IAWrC;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAI5B;;OAEG;YACW,aAAa;IAoD3B;;OAEG;IACG,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,EAAE,CAAC;IASrE;;OAEG;IACG,gBAAgB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,EAAE,CAAC;IASzE;;OAEG;IACG,iBAAiB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,mBAAmB,EAAE,CAAC;IAS3E;;OAEG;IACG,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,EAAE,CAAC;IASrE;;OAEG;IACG,kBAAkB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,EAAE,CAAC;IAYtE;;OAEG;IACG,QAAQ,IAAI,OAAO,CAAC;QAAE,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QAAC,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;KAAE,CAAC;IA8BvG;;OAEG;IACG,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAY7E;;OAEG;IACG,aAAa,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;IASxC;;OAEG;IACG,UAAU,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC;IAWlC;;OAEG;IACG,iBAAiB,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,GAAE,MAAW,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAShF;;OAEG;IACG,iBAAiB,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,GAAE,MAAW,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAShF;;OAEG;IACG,kBAAkB,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,GAAE,MAAW,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAYnF;;OAEG;IACG,eAAe,IAAI,OAAO,CAAC,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAS/E;;OAEG;IACG,kBAAkB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAY3F;;OAEG;IACG,aAAa,CAAC,KAAK,GAAE,MAAW,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAU1D;;OAEG;IACG,SAAS,CAAC,UAAU,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAW1E;;OAEG;IACG,aAAa,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,GAAE,MAAW,GAAG,OAAO,CAAC,kBAAkB,EAAE,CAAC;IAYvF;;OAEG;IACG,iBAAiB,CAAC,UAAU,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,EAAE,KAAK,GAAE,MAAW,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC;IAUvG;;OAEG;IACG,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,GAAE,MAAW,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAU/E;;OAEG;IACG,kBAAkB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAUpE;;OAEG;IACG,cAAc,IAAI,OAAO,CAAC,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,CAAC;QAAC,cAAc,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CASvG;AAED;;GAEG;AACH,wBAAgB,iBAAiB,IAAI,WAAW,CAS/C"}
@@ -0,0 +1,360 @@
1
+ /**
2
+ * Graph Client - Apache AGE Query Abstraction Layer
3
+ *
4
+ * Provides a clean interface for querying the Regen code knowledge graph
5
+ * stored in PostgreSQL with Apache AGE extension.
6
+ */
7
+ import pkg from 'pg';
8
+ const { Pool } = pkg;
9
+ /**
10
+ * GraphClient - Interface for querying the code knowledge graph
11
+ */
12
+ export class GraphClient {
13
+ pool;
14
+ graphName;
15
+ constructor(config) {
16
+ this.graphName = config.graphName;
17
+ this.pool = new Pool({
18
+ host: config.host,
19
+ port: config.port,
20
+ database: config.database,
21
+ user: config.user,
22
+ password: config.password,
23
+ });
24
+ }
25
+ /**
26
+ * Close the database connection pool
27
+ */
28
+ async close() {
29
+ await this.pool.end();
30
+ }
31
+ /**
32
+ * Execute a raw Cypher query
33
+ */
34
+ async executeCypher(query, params = []) {
35
+ const client = await this.pool.connect();
36
+ try {
37
+ // Load AGE extension for this session
38
+ await client.query("LOAD 'age';");
39
+ await client.query(`SET search_path = ag_catalog, "$user", public;`);
40
+ // Apache AGE requires wrapping the cypher() call with column definitions
41
+ // We use a generic 'result agtype' column and parse the results
42
+ const cypherQuery = `SELECT * FROM ag_catalog.cypher('${this.graphName}', $$ ${query} $$) as (result agtype)`;
43
+ const result = await client.query(cypherQuery, params);
44
+ // AGE returns results wrapped in agtype format
45
+ return result.rows.map((row) => {
46
+ const agtypeValue = row.result;
47
+ // Parse the agtype value - it can be a vertex, edge, or primitive
48
+ if (agtypeValue === null || agtypeValue === undefined) {
49
+ return null;
50
+ }
51
+ // AGE returns agtype as objects that need to be parsed
52
+ let parsed;
53
+ if (typeof agtypeValue === 'string') {
54
+ // String-encoded JSON
55
+ try {
56
+ parsed = JSON.parse(agtypeValue);
57
+ }
58
+ catch {
59
+ parsed = agtypeValue;
60
+ }
61
+ }
62
+ else if (typeof agtypeValue === 'object') {
63
+ // Already an object - might be a vertex/edge or a map
64
+ if (agtypeValue.properties) {
65
+ // This is a vertex or edge - extract properties
66
+ parsed = agtypeValue.properties;
67
+ }
68
+ else {
69
+ // This is a plain object/map
70
+ parsed = agtypeValue;
71
+ }
72
+ }
73
+ else {
74
+ // Primitive value
75
+ parsed = agtypeValue;
76
+ }
77
+ return parsed;
78
+ }).filter((item) => item !== null);
79
+ }
80
+ finally {
81
+ client.release();
82
+ }
83
+ }
84
+ /**
85
+ * Query 1: Find which Keeper handles a given Msg
86
+ */
87
+ async getKeeperForMsg(msgName) {
88
+ const query = `
89
+ MATCH (k:Keeper)-[:HANDLES]->(m:Msg {name: '${msgName}'})
90
+ RETURN {keeper_name: k.name, keeper_file_path: k.file_path, keeper_line_number: k.line_number}
91
+ `;
92
+ return this.executeCypher(query);
93
+ }
94
+ /**
95
+ * Query 2: List all Msgs a Keeper handles
96
+ */
97
+ async getMsgsForKeeper(keeperName) {
98
+ const query = `
99
+ MATCH (k:Keeper {name: '${keeperName}'})-[:HANDLES]->(m:Msg)
100
+ RETURN {msg_name: m.name, msg_package: m.package}
101
+ `;
102
+ return this.executeCypher(query);
103
+ }
104
+ /**
105
+ * Query 3: Find documents that mention an entity
106
+ */
107
+ async getDocsMentioning(entityName) {
108
+ const query = `
109
+ MATCH (d:Document)-[:MENTIONS]->(e {name: '${entityName}'})
110
+ RETURN {file_path: d.file_path, title: d.title}
111
+ `;
112
+ return this.executeCypher(query);
113
+ }
114
+ /**
115
+ * Query 4: Find all entities mentioned in a document
116
+ */
117
+ async getEntitiesInDoc(docPath) {
118
+ const query = `
119
+ MATCH (d:Document {file_path: '${docPath}'})-[:MENTIONS]->(e)
120
+ RETURN {type: labels(e)[0], name: e.name}
121
+ `;
122
+ return this.executeCypher(query);
123
+ }
124
+ /**
125
+ * Query 5: Find entities related to a given entity (via shared docs or direct edges)
126
+ */
127
+ async getRelatedEntities(entityName) {
128
+ const query = `
129
+ MATCH (e1 {name: '${entityName}'})<-[:MENTIONS]-(d:Document)-[:MENTIONS]->(e2)
130
+ WHERE e1 <> e2
131
+ WITH DISTINCT e2, count(d) as shared_docs
132
+ RETURN {name: e2.name, type: labels(e2)[0], shared_docs: shared_docs}
133
+ ORDER BY shared_docs DESC
134
+ `;
135
+ return this.executeCypher(query);
136
+ }
137
+ /**
138
+ * Get graph statistics
139
+ */
140
+ async getStats() {
141
+ // Count nodes by label
142
+ const nodeQuery = `
143
+ MATCH (n)
144
+ WITH labels(n)[0] as label, count(*) as count
145
+ RETURN {label: label, count: count}
146
+ `;
147
+ const nodeResults = await this.executeCypher(nodeQuery);
148
+ // Count edges by type
149
+ const edgeQuery = `
150
+ MATCH ()-[r]->()
151
+ WITH type(r) as type, count(*) as count
152
+ RETURN {type: type, count: count}
153
+ `;
154
+ const edgeResults = await this.executeCypher(edgeQuery);
155
+ const node_counts = {};
156
+ nodeResults.forEach(r => {
157
+ node_counts[r.label] = r.count;
158
+ });
159
+ const edge_counts = {};
160
+ edgeResults.forEach(r => {
161
+ edge_counts[r.type] = r.count;
162
+ });
163
+ return { node_counts, edge_counts };
164
+ }
165
+ /**
166
+ * Search for entities by name pattern
167
+ */
168
+ async searchEntities(pattern, entityType) {
169
+ const typeFilter = entityType ? `labels(e)[0] = '${entityType}' AND` : '';
170
+ const query = `
171
+ MATCH (e)
172
+ WHERE ${typeFilter} e.name =~ '.*${pattern}.*'
173
+ RETURN {type: labels(e)[0], name: e.name, file_path: e.file_path, line_number: e.line_number, docstring: e.docstring}
174
+ LIMIT 50
175
+ `;
176
+ return this.executeCypher(query);
177
+ }
178
+ /**
179
+ * Get all Keepers
180
+ */
181
+ async getAllKeepers() {
182
+ const query = `
183
+ MATCH (k:Keeper)
184
+ RETURN {name: k.name, file_path: k.file_path, line_number: k.line_number, docstring: k.docstring, fields: k.fields}
185
+ `;
186
+ return this.executeCypher(query);
187
+ }
188
+ /**
189
+ * Get all Msgs
190
+ */
191
+ async getAllMsgs() {
192
+ const query = `
193
+ MATCH (m:Msg)
194
+ RETURN {name: m.name, file_path: m.file_path, line_number: m.line_number, docstring: m.docstring, fields: m.fields, package: m.package}
195
+ `;
196
+ return this.executeCypher(query);
197
+ }
198
+ // ============= Multi-Language Entity Queries =============
199
+ /**
200
+ * Get entities by repository name
201
+ */
202
+ async getEntitiesByRepo(repoName, limit = 50) {
203
+ const query = `
204
+ MATCH (r:Repository {name: '${repoName}'})-[:CONTAINS]->(e)
205
+ RETURN {type: labels(e)[0], name: e.name, file_path: e.file_path, line_number: e.line_number, docstring: e.docstring}
206
+ LIMIT ${limit}
207
+ `;
208
+ return this.executeCypher(query);
209
+ }
210
+ /**
211
+ * Get entities by type (Sensor, Handler, Class, Interface, Function, etc.)
212
+ */
213
+ async getEntitiesByType(typeName, limit = 50) {
214
+ const query = `
215
+ MATCH (e:${typeName})
216
+ RETURN {type: labels(e)[0], name: e.name, file_path: e.file_path, line_number: e.line_number, docstring: e.docstring}
217
+ LIMIT ${limit}
218
+ `;
219
+ return this.executeCypher(query);
220
+ }
221
+ /**
222
+ * Search entities by name (case-insensitive contains)
223
+ */
224
+ async findEntitiesByName(searchTerm, limit = 20) {
225
+ // Use toLower for case-insensitive matching
226
+ const query = `
227
+ MATCH (e)
228
+ WHERE toLower(e.name) CONTAINS toLower('${searchTerm}')
229
+ RETURN {type: labels(e)[0], name: e.name, file_path: e.file_path, line_number: e.line_number, docstring: e.docstring, repo: e.repo, language: e.language}
230
+ ORDER BY e.name
231
+ LIMIT ${limit}
232
+ `;
233
+ return this.executeCypher(query);
234
+ }
235
+ /**
236
+ * Get all repositories with entity counts
237
+ */
238
+ async getRepositories() {
239
+ const query = `
240
+ MATCH (r:Repository)-[:CONTAINS]->(e)
241
+ WITH r.name as name, count(e) as entity_count
242
+ RETURN {name: name, entity_count: entity_count}
243
+ `;
244
+ return this.executeCypher(query);
245
+ }
246
+ /**
247
+ * Get entity type distribution for a repository
248
+ */
249
+ async getRepoEntityTypes(repoName) {
250
+ const query = `
251
+ MATCH (r:Repository {name: '${repoName}'})-[:CONTAINS]->(e)
252
+ WITH labels(e)[0] as type, count(*) as count
253
+ RETURN {type: type, count: count}
254
+ ORDER BY count DESC
255
+ `;
256
+ return this.executeCypher(query);
257
+ }
258
+ // ============= RAPTOR Module Queries =============
259
+ /**
260
+ * Get all modules with their summaries
261
+ */
262
+ async getAllModules(limit = 50) {
263
+ const query = `
264
+ MATCH (m:Module)
265
+ RETURN {name: m.name, repo: m.repo, path: m.path, summary: m.summary, entity_count: m.entity_count}
266
+ ORDER BY m.entity_count DESC
267
+ LIMIT ${limit}
268
+ `;
269
+ return this.executeCypher(query);
270
+ }
271
+ /**
272
+ * Get a specific module by name
273
+ */
274
+ async getModule(moduleName, repo) {
275
+ const repoFilter = repo ? `repo: '${repo}',` : '';
276
+ const query = `
277
+ MATCH (m:Module {${repoFilter} name: '${moduleName}'})
278
+ RETURN {name: m.name, repo: m.repo, path: m.path, summary: m.summary, entity_count: m.entity_count}
279
+ LIMIT 1
280
+ `;
281
+ const results = await this.executeCypher(query);
282
+ return results.length > 0 ? results[0] : null;
283
+ }
284
+ /**
285
+ * Search modules by name pattern (case-insensitive)
286
+ */
287
+ async searchModules(pattern, limit = 10) {
288
+ const query = `
289
+ MATCH (m:Module)
290
+ WHERE toLower(m.name) CONTAINS toLower('${pattern}')
291
+ OR toLower(m.summary) CONTAINS toLower('${pattern}')
292
+ RETURN {name: m.name, repo: m.repo, path: m.path, summary: m.summary, entity_count: m.entity_count}
293
+ ORDER BY m.entity_count DESC
294
+ LIMIT ${limit}
295
+ `;
296
+ return this.executeCypher(query);
297
+ }
298
+ /**
299
+ * Get all entities contained in a module
300
+ */
301
+ async getModuleEntities(moduleName, repo, limit = 50) {
302
+ const repoFilter = repo ? `repo: '${repo}',` : '';
303
+ const query = `
304
+ MATCH (m:Module {${repoFilter} name: '${moduleName}'})-[:CONTAINS]->(e)
305
+ RETURN {type: labels(e)[0], name: e.name, file_path: e.file_path, line_number: e.line_number}
306
+ LIMIT ${limit}
307
+ `;
308
+ return this.executeCypher(query);
309
+ }
310
+ /**
311
+ * Get modules by repository
312
+ */
313
+ async getModulesByRepo(repoName, limit = 20) {
314
+ const query = `
315
+ MATCH (m:Module {repo: '${repoName}'})
316
+ RETURN {name: m.name, repo: m.repo, path: m.path, summary: m.summary, entity_count: m.entity_count}
317
+ ORDER BY m.entity_count DESC
318
+ LIMIT ${limit}
319
+ `;
320
+ return this.executeCypher(query);
321
+ }
322
+ /**
323
+ * Find which module contains a given entity
324
+ */
325
+ async getModuleForEntity(entityName) {
326
+ const query = `
327
+ MATCH (m:Module)-[:CONTAINS]->(e {name: '${entityName}'})
328
+ RETURN {name: m.name, repo: m.repo, path: m.path, summary: m.summary, entity_count: m.entity_count}
329
+ LIMIT 1
330
+ `;
331
+ const results = await this.executeCypher(query);
332
+ return results.length > 0 ? results[0] : null;
333
+ }
334
+ /**
335
+ * Get module statistics (count by repo)
336
+ */
337
+ async getModuleStats() {
338
+ const query = `
339
+ MATCH (m:Module)
340
+ WITH m.repo as repo, count(m) as module_count, sum(m.entity_count) as total_entities
341
+ RETURN {repo: repo, module_count: module_count, total_entities: total_entities}
342
+ ORDER BY total_entities DESC
343
+ `;
344
+ return this.executeCypher(query);
345
+ }
346
+ }
347
+ /**
348
+ * Create a default GraphClient instance using environment variables
349
+ */
350
+ export function createGraphClient() {
351
+ return new GraphClient({
352
+ host: process.env.GRAPH_DB_HOST || 'localhost',
353
+ port: parseInt(process.env.GRAPH_DB_PORT || '5432'),
354
+ database: process.env.GRAPH_DB_NAME || 'eliza',
355
+ user: process.env.GRAPH_DB_USER,
356
+ password: process.env.GRAPH_DB_PASSWORD,
357
+ graphName: process.env.GRAPH_NAME || 'regen_graph',
358
+ });
359
+ }
360
+ //# sourceMappingURL=graph_client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"graph_client.js","sourceRoot":"","sources":["../src/graph_client.ts"],"names":[],"mappings":"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"}