#
tokens: 34331/50000 2/128 files (page 4/13)
lines: on (toggle) GitHub
raw markdown copy reset
This is page 4 of 13. Use http://codebase.md/tejpalvirk/contextmanager?lines=true&page={x} to view the full context.

# Directory Structure

```
├── .gitattributes
├── .gitignore
├── build-all-domains.sh
├── developer
│   ├── .gitattributes
│   ├── developer_advancedcontext.txt
│   ├── developer_buildcontext.txt
│   ├── developer_deletecontext.txt
│   ├── developer_endsession_examples.txt
│   ├── developer_endsession.txt
│   ├── developer_loadcontext.txt
│   ├── developer_startsession.txt
│   ├── Dockerfile
│   ├── index.d.ts
│   ├── index.js
│   ├── index.ts
│   ├── package.json
│   ├── README.md
│   └── tsconfig.json
├── dist
│   ├── developer
│   │   ├── index.d.ts
│   │   └── index.js
│   ├── main
│   │   ├── descriptions
│   │   │   ├── common_advancedcontext.txt
│   │   │   ├── common_buildcontext.txt
│   │   │   ├── common_deletecontext.txt
│   │   │   ├── common_endsession.txt
│   │   │   ├── common_loadcontext.txt
│   │   │   ├── common_startsession.txt
│   │   │   ├── developer_advancedcontext.txt
│   │   │   ├── developer_buildcontext.txt
│   │   │   ├── developer_deletecontext.txt
│   │   │   ├── developer_endsession_examples.txt
│   │   │   ├── developer_endsession.txt
│   │   │   ├── developer_loadcontext.txt
│   │   │   ├── developer_startsession.txt
│   │   │   ├── project_advancedcontext.txt
│   │   │   ├── project_buildcontext.txt
│   │   │   ├── project_deletecontext.txt
│   │   │   ├── project_endsession_examples.txt
│   │   │   ├── project_endsession.txt
│   │   │   ├── project_loadcontext.txt
│   │   │   ├── project_startsession.txt
│   │   │   ├── qualitativeresearch_advancedcontext.txt
│   │   │   ├── qualitativeresearch_buildcontext.txt
│   │   │   ├── qualitativeresearch_deletecontext.txt
│   │   │   ├── qualitativeresearch_endsession_examples.txt
│   │   │   ├── qualitativeresearch_endsession.txt
│   │   │   ├── qualitativeresearch_loadcontext.txt
│   │   │   ├── qualitativeresearch_startsession.txt
│   │   │   ├── quantitativeresearch_advancedcontext.txt
│   │   │   ├── quantitativeresearch_buildcontext.txt
│   │   │   ├── quantitativeresearch_deletecontext.txt
│   │   │   ├── quantitativeresearch_endsession_examples.txt
│   │   │   ├── quantitativeresearch_endsession.txt
│   │   │   ├── quantitativeresearch_loadcontext.txt
│   │   │   ├── quantitativeresearch_startsession.txt
│   │   │   ├── student_advancedcontext.txt
│   │   │   ├── student_buildcontext.txt
│   │   │   ├── student_deletecontext.txt
│   │   │   ├── student_endsession_examples.txt
│   │   │   ├── student_endsession.txt
│   │   │   ├── student_loadcontext.txt
│   │   │   └── student_startsession.txt
│   │   ├── index.d.ts
│   │   ├── index.js
│   │   ├── mcp.d.ts
│   │   └── mcp.js
│   ├── project
│   │   ├── index.d.ts
│   │   └── index.js
│   ├── qualitativeresearch
│   │   ├── index.d.ts
│   │   └── index.js
│   ├── quantitativeresearch
│   │   ├── index.d.ts
│   │   └── index.js
│   └── student
│       ├── index.d.ts
│       └── index.js
├── main
│   ├── descriptions
│   │   ├── common_advancedcontext.txt
│   │   ├── common_buildcontext.txt
│   │   ├── common_deletecontext.txt
│   │   ├── common_endsession.txt
│   │   ├── common_loadcontext.txt
│   │   ├── common_startsession.txt
│   │   ├── developer_advancedcontext.txt
│   │   ├── developer_buildcontext.txt
│   │   ├── developer_deletecontext.txt
│   │   ├── developer_endsession_examples.txt
│   │   ├── developer_endsession.txt
│   │   ├── developer_loadcontext.txt
│   │   ├── developer_startsession.txt
│   │   ├── project_advancedcontext.txt
│   │   ├── project_buildcontext.txt
│   │   ├── project_deletecontext.txt
│   │   ├── project_endsession_examples.txt
│   │   ├── project_endsession.txt
│   │   ├── project_loadcontext.txt
│   │   ├── project_startsession.txt
│   │   ├── qualitativeresearch_advancedcontext.txt
│   │   ├── qualitativeresearch_buildcontext.txt
│   │   ├── qualitativeresearch_deletecontext.txt
│   │   ├── qualitativeresearch_endsession_examples.txt
│   │   ├── qualitativeresearch_endsession.txt
│   │   ├── qualitativeresearch_loadcontext.txt
│   │   ├── qualitativeresearch_startsession.txt
│   │   ├── quantitativeresearch_advancedcontext.txt
│   │   ├── quantitativeresearch_buildcontext.txt
│   │   ├── quantitativeresearch_deletecontext.txt
│   │   ├── quantitativeresearch_endsession_examples.txt
│   │   ├── quantitativeresearch_endsession.txt
│   │   ├── quantitativeresearch_loadcontext.txt
│   │   ├── quantitativeresearch_startsession.txt
│   │   ├── student_advancedcontext.txt
│   │   ├── student_buildcontext.txt
│   │   ├── student_deletecontext.txt
│   │   ├── student_endsession_examples.txt
│   │   ├── student_endsession.txt
│   │   ├── student_loadcontext.txt
│   │   └── student_startsession.txt
│   ├── index.js
│   ├── index.ts
│   ├── mcp.ts
│   ├── package.json
│   ├── README.md
│   └── tsconfig.json
├── package-lock.json
├── package.json
├── project
│   ├── .gitattributes
│   ├── Dockerfile
│   ├── index.d.ts
│   ├── index.js
│   ├── index.ts
│   ├── package.json
│   ├── project_advancedcontext.txt
│   ├── project_buildcontext.txt
│   ├── project_deletecontext.txt
│   ├── project_endsession_examples.txt
│   ├── project_endsession.txt
│   ├── project_loadcontext.txt
│   ├── project_startsession.txt
│   ├── README.md
│   └── tsconfig.json
├── qualitativeresearch
│   ├── .gitattributes
│   ├── Dockerfile
│   ├── index.d.ts
│   ├── index.js
│   ├── index.ts
│   ├── package.json
│   ├── qualitativeresearch_advancedcontext.txt
│   ├── qualitativeresearch_buildcontext.txt
│   ├── qualitativeresearch_deletecontext.txt
│   ├── qualitativeresearch_endsession_examples.txt
│   ├── qualitativeresearch_endsession.txt
│   ├── qualitativeresearch_loadcontext.txt
│   ├── qualitativeresearch_startsession.txt
│   ├── README.md
│   └── tsconfig.json
├── quantitativeresearch
│   ├── .gitattributes
│   ├── Dockerfile
│   ├── index.d.ts
│   ├── index.js
│   ├── index.ts
│   ├── package.json
│   ├── quantitativeresearch_advancedcontext.txt
│   ├── quantitativeresearch_buildcontext.txt
│   ├── quantitativeresearch_deletecontext.txt
│   ├── quantitativeresearch_endsession_examples.txt
│   ├── quantitativeresearch_endsession.txt
│   ├── quantitativeresearch_loadcontext.txt
│   ├── quantitativeresearch_startsession.txt
│   ├── README.md
│   └── tsconfig.json
├── README.md
├── student
│   ├── .gitattributes
│   ├── Dockerfile
│   ├── index.d.ts
│   ├── index.js
│   ├── index.ts
│   ├── package.json
│   ├── README.md
│   ├── student_advancedcontext.txt
│   ├── student_buildcontext.txt
│   ├── student_deletecontext.txt
│   ├── student_endsession_examples.txt
│   ├── student_endsession.txt
│   ├── student_loadcontext.txt
│   ├── student_startsession.txt
│   └── tsconfig.json
└── tsconfig.json
```

# Files

--------------------------------------------------------------------------------
/main/index.ts:
--------------------------------------------------------------------------------

```typescript
  1 | #!/usr/bin/env node
  2 | 
  3 | import { McpServer } from "./mcp.js";
  4 | import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
  5 | import { Client } from "@modelcontextprotocol/sdk/client/index.js";
  6 | import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js";
  7 | import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
  8 | import { z } from "zod";
  9 | import path from "path";
 10 | import { fileURLToPath } from "url";
 11 | import { readFileSync, existsSync } from "fs";
 12 | 
 13 | // Get the directory where the contextmanager is located
 14 | const __filename = fileURLToPath(import.meta.url);
 15 | const __dirname = path.dirname(__filename);
 16 | 
 17 | // Domain interfaces
 18 | interface DomainInfo {
 19 |   name: string;
 20 |   description: string;
 21 |   entityTypes: string[];
 22 |   host?: string;
 23 |   port?: number;
 24 |   path?: string;
 25 |   command?: string;
 26 |   args?: string[];
 27 | }
 28 | 
 29 | interface FlowInfo {
 30 |   id: string;
 31 |   domain: string;
 32 |   entityName?: string;
 33 |   entityType?: string;
 34 |   active: boolean;
 35 |   createdAt: number;
 36 | }
 37 | 
 38 | // Domain client class
 39 | class DomainClient {
 40 |   public name: string;
 41 |   public client: Client | null = null;
 42 |   public transport: SSEClientTransport | StdioClientTransport | null = null;
 43 |   public connected: boolean = false;
 44 |   private process: any = null;
 45 | 
 46 |   constructor(public domain: DomainInfo) {
 47 |     this.name = domain.name;
 48 |   }
 49 | 
 50 |   async connect(): Promise<boolean> {
 51 |     if (this.connected) {
 52 |       return true;
 53 |     }
 54 | 
 55 |     try {
 56 |       // Create client
 57 |       this.client = new Client(
 58 |         {
 59 |           name: `contextmanager-${this.name}-client`,
 60 |           version: "1.0.0"
 61 |         },
 62 |         {
 63 |           capabilities: {
 64 |             resources: {},
 65 |             tools: {},
 66 |             prompts: {}
 67 |           }
 68 |         }
 69 |       );
 70 | 
 71 |       // Connect to domain server
 72 |       if (this.domain.host && this.domain.port) {
 73 |         // Connect via SSE
 74 |         const url = new URL(`http://${this.domain.host}:${this.domain.port}${this.domain.path || '/sse'}`);
 75 |         this.transport = new SSEClientTransport(url);
 76 |       } else if (this.domain.command) {
 77 |         // Connect via stdio
 78 |         this.transport = new StdioClientTransport({
 79 |           command: this.domain.command,
 80 |           args: this.domain.args || [],
 81 |         });
 82 |       } else {
 83 |         console.error(`Domain ${this.name} has no connection information`);
 84 |         return false;
 85 |       }
 86 | 
 87 |       await this.client.connect(this.transport);
 88 |       this.connected = true;
 89 |       return true;
 90 |     } catch (error) {
 91 |       console.error(`Failed to connect to domain ${this.name}:`, error);
 92 |       this.connected = false;
 93 |       return false;
 94 |     }
 95 |   }
 96 | 
 97 |   async disconnect(): Promise<void> {
 98 |     if (!this.connected) {
 99 |       return;
100 |     }
101 | 
102 |     try {
103 |       if (this.client) {
104 |         // Manually close the transport as there's no official disconnect method
105 |         if (this.transport) {
106 |           if ('close' in this.transport) {
107 |             await (this.transport as any).close();
108 |           }
109 |         }
110 |       }
111 |       this.connected = false;
112 |     } catch (error) {
113 |       console.error(`Error disconnecting from domain ${this.name}:`, error);
114 |     }
115 |   }
116 | 
117 |   async callTool(toolRequest: { name: string; arguments: any }): Promise<any> {
118 |     if (!this.connected || !this.client) {
119 |       const connected = await this.connect();
120 |       if (!connected) {
121 |         return {
122 |           content: [{ type: "text", text: `Error: Not connected to domain ${this.name}` }],
123 |           isError: true
124 |         };
125 |       }
126 |     }
127 | 
128 |     try {
129 |       if (!this.client) {
130 |         throw new Error(`Client for domain ${this.name} is not initialized`);
131 |       }
132 |       
133 |       const result = await this.client.callTool(toolRequest);
134 |       return result;
135 |     } catch (error) {
136 |       console.error(`Error calling tool ${toolRequest.name} on domain ${this.name}:`, error);
137 |       return {
138 |         content: [{ type: "text", text: `Error calling tool ${toolRequest.name} on domain ${this.name}: ${error}` }],
139 |         isError: true
140 |       };
141 |     }
142 |   }
143 | 
144 |   async readResource(resourceRequest: { uri: string }): Promise<any> {
145 |     if (!this.connected || !this.client) {
146 |       const connected = await this.connect();
147 |       if (!connected) {
148 |         return {
149 |           contents: [{
150 |             uri: resourceRequest.uri,
151 |             text: `Error: Not connected to domain ${this.name}`
152 |           }]
153 |         };
154 |       }
155 |     }
156 | 
157 |     try {
158 |       if (!this.client) {
159 |         throw new Error(`Client for domain ${this.name} is not initialized`);
160 |       }
161 |       
162 |       return await this.client.readResource(resourceRequest);
163 |     } catch (error) {
164 |       console.error(`Error reading resource ${resourceRequest.uri} from domain ${this.name}:`, error);
165 |       return {
166 |         contents: [{
167 |           uri: resourceRequest.uri,
168 |           text: `Error reading resource: ${error}`
169 |         }]
170 |       };
171 |     }
172 |   }
173 | }
174 | 
175 | // In-memory storage
176 | const domains: DomainInfo[] = [
177 |   {
178 |     name: "developer",
179 |     description: "Software development context with entities like projects, components, and tasks",
180 |     entityTypes: ["project", "component", "task", "issue", "commit"],
181 |     command: "node",
182 |     args: [path.resolve(__dirname, "../developer/index.js")]
183 |   },
184 |   {
185 |     name: "project",
186 |     description: "Project management context with entities like projects, tasks, and resources",
187 |     entityTypes: ["project", "task", "resource", "milestone", "risk"],
188 |     command: "node",
189 |     args: [path.resolve(__dirname, "../project/index.js")]
190 |   },
191 |   {
192 |     name: "student",
193 |     description: "Educational context with entities like courses, assignments, and exams",
194 |     entityTypes: ["course", "assignment", "exam", "note", "grade"],
195 |     command: "node",
196 |     args: [path.resolve(__dirname, "../student/index.js")]
197 |   },
198 |   {
199 |     name: "qualitativeresearch",
200 |     description: "Qualitative research context with entities like studies, participants, and interviews",
201 |     entityTypes: ["study", "participant", "interview", "code", "theme"],
202 |     command: "node",
203 |     args: [path.resolve(__dirname, "../qualitativeresearch/index.js")]
204 |   },
205 |   {
206 |     name: "quantitativeresearch",
207 |     description: "Quantitative research context with entities like datasets, variables, and analyses",
208 |     entityTypes: ["dataset", "variable", "analysis", "model", "result"],
209 |     command: "node",
210 |     args: [path.resolve(__dirname, "../quantitativeresearch/index.js")]
211 |   }
212 | ];
213 | 
214 | // Domain clients
215 | const domainClients: { [key: string]: DomainClient } = {};
216 | 
217 | // Initialize domain clients
218 | for (const domain of domains) {
219 |   domainClients[domain.name] = new DomainClient(domain);
220 | }
221 | 
222 | // In-memory flow management
223 | const flows: FlowInfo[] = [];
224 | let activeDomain: string | null = null;
225 | let flowCounter = 0;
226 | 
227 | // Function to get tool description based on active domain
228 | function getToolDescription(toolName: string, domain: string | null): string {
229 |   if (!domain) {
230 |     domain = "common";
231 |   }
232 |   
233 |   const descriptionFilePath = path.resolve(
234 |     __dirname,
235 |     "descriptions",
236 |     `${domain}_${toolName}.txt`
237 |   );
238 |   
239 |   try {
240 |     if (existsSync(descriptionFilePath)) {
241 |       return readFileSync(descriptionFilePath, "utf8");
242 |     } else {
243 |       console.warn(`Description file not found: ${descriptionFilePath}`);
244 |       return `${toolName} tool for ${domain} domain`;
245 |     }
246 |   } catch (error) {
247 |     console.error(`Error reading description file for ${domain}_${toolName}:`, error);
248 |     return `${toolName} tool for ${domain} domain`;
249 |   }
250 | }
251 | 
252 | // Create an MCP server
253 | const server = new McpServer({
254 |   name: "Context Manager",
255 |   version: "1.0.0"
256 | });
257 | 
258 | // Store tool schemas and callbacks for re-registration when domain changes
259 | const toolDefinitions: {
260 |   [key: string]: {
261 |     schema: any;
262 |     callback: (...args: any[]) => Promise<any>;
263 |   }
264 | } = {};
265 | 
266 | // Function to register or update a tool with domain-specific description
267 | function registerDomainTool(
268 |   name: string, 
269 |   schema: any, 
270 |   callback: (...args: any[]) => Promise<any>
271 | ): void {
272 |   // Store the tool definition for later re-registration
273 |   toolDefinitions[name] = {
274 |     schema,
275 |     callback
276 |   };
277 |   
278 |   // Register the tool with the current domain description
279 |   server.tool(
280 |     name,
281 |     getToolDescription(name, activeDomain),
282 |     schema,
283 |     callback
284 |   );
285 | }
286 | 
287 | // Function to update all tool descriptions when domain changes
288 | function updateDomainToolDescriptions(): void {
289 |   // Re-register all stored tools with new descriptions
290 |   for (const [name, definition] of Object.entries(toolDefinitions)) {
291 |     server.removeTool(name);
292 |     server.tool(
293 |       name,
294 |       getToolDescription(name, activeDomain),
295 |       definition.schema,
296 |       definition.callback
297 |     );
298 |   }
299 | }
300 | 
301 | // Domain management tools
302 | server.tool(
303 |   "setActiveDomain",
304 |   "Change the active domain for subsequent tool calls",
305 |   { domain: z.string() },
306 |   async ({ domain }) => {
307 |     const foundDomain = domains.find(d => d.name.toLowerCase() === domain.toLowerCase());
308 |     if (!foundDomain) {
309 |       return {
310 |         content: [{ type: "text", text: `Error: Domain '${domain}' not found. Available domains: ${domains.map(d => d.name).join(", ")}` }],
311 |         isError: true
312 |       };
313 |     }
314 |     
315 |     // Connect to the domain server
316 |     const domainClient = domainClients[foundDomain.name];
317 |     const connected = await domainClient.connect();
318 |     
319 |     if (!connected) {
320 |       return {
321 |         content: [{ type: "text", text: `Error: Could not connect to domain server for '${domain}'` }],
322 |         isError: true
323 |       };
324 |     }
325 |     
326 |     activeDomain = foundDomain.name;
327 |     
328 |     // Update tool descriptions for the new active domain
329 |     updateDomainToolDescriptions();
330 |     
331 |     return {
332 |       content: [{ type: "text", text: `Active domain set to: ${activeDomain}` }]
333 |     };
334 |   }
335 | );
336 | 
337 | // Flow management tools
338 | registerDomainTool(
339 |   "startsession",
340 |   { 
341 |     domain: z.string()
342 |   },
343 |   async ({ domain }) => {
344 |     const foundDomain = domains.find(d => d.name.toLowerCase() === domain.toLowerCase());
345 |     if (!foundDomain) {
346 |       return {
347 |         content: [{ type: "text", text: `Error: Domain '${domain}' not found. Available domains: ${domains.map(d => d.name).join(", ")}` }],
348 |         isError: true
349 |       };
350 |     }
351 |     
352 |     // Connect to the domain server
353 |     const domainClient = domainClients[foundDomain.name];
354 |     const connected = await domainClient.connect();
355 |     
356 |     if (!connected) {
357 |       return {
358 |         content: [{ type: "text", text: `Error: Could not connect to domain server for '${domain}'` }],
359 |         isError: true
360 |       };
361 |     }
362 |     
363 |     activeDomain = foundDomain.name;
364 |     flowCounter++;
365 | 
366 |     // Update tool descriptions for the new active domain since activeDomain changed
367 |     updateDomainToolDescriptions();
368 | 
369 |     // Forward the startsession call to the domain server with a domain-specific session identifier
370 |     try {
371 |       const result = await domainClient.callTool({
372 |         name: "startsession",
373 |         arguments: {}
374 |       });
375 | 
376 |       const lastWord = result.content[0].text.split(' ').pop();
377 |       // Create a contextmanager flow ID from domain session ID (last word)
378 |       const flowId = `flow_${lastWord}`;
379 | 
380 |       flows.push({
381 |         id: flowId,
382 |         domain: activeDomain,
383 |         active: true,
384 |         createdAt: Date.now()
385 |       });
386 |       
387 |       return {
388 |         content: [
389 |           { type: "text", text: `${result.content[0].text}`}
390 |         ],
391 |       };
392 |     } catch (error) {
393 |       console.error(`Error starting session for domain ${activeDomain}:`, error);
394 |       return {
395 |         content: [{ type: "text", text: `Error starting session for domain ${activeDomain}: ${error}` }],
396 |         isError: true
397 |       };
398 |     }
399 |   }
400 | );
401 | 
402 | registerDomainTool(
403 |   "endsession",
404 |   {
405 |     sessionId: z.string(),
406 |     stage: z.string(),
407 |     stageNumber: z.number(),
408 |     totalStages: z.number(),
409 |     nextStageNeeded: z.boolean(),
410 |     analysis: z.string().optional(),
411 |     isRevision: z.boolean().optional(),
412 |     revisesStage: z.number().optional(),
413 |     stageData: z.record(z.string(), z.any()).optional()
414 |   },
415 |   async ({ sessionId, stage, stageNumber, totalStages, nextStageNeeded, analysis, isRevision, revisesStage, stageData }) => {
416 |     const flowId = `flow_${sessionId}`;
417 |     const flowIndex = flows.findIndex(s => s.id === flowId);
418 |     if (flowIndex === -1) {
419 |       return {
420 |         content: [{ type: "text", text: `Error: Context Manager flow with ID '${flowId}' not found.` }],
421 |         isError: true
422 |       };
423 |     }
424 |     
425 |     const flow = flows[flowIndex];
426 |     const domainName = flow.domain;
427 |     const domainClient = domainClients[domainName];
428 |     
429 |     if (!domainClient || !domainClient.connected) {
430 |       const connected = await domainClient.connect();
431 |       if (!connected) {
432 |         return {
433 |           content: [{ type: "text", text: `Error: Could not connect to domain server for '${domainName}'` }],
434 |           isError: true
435 |         };
436 |       }
437 |     }
438 |     
439 |     // Forward the endsession call to the domain server
440 |     try {
441 |       const result = await domainClient.callTool({
442 |         name: "endsession",
443 |         arguments: {
444 |           sessionId: sessionId,
445 |           stage,
446 |           stageNumber,
447 |           totalStages,
448 |           nextStageNeeded,
449 |           analysis,
450 |           isRevision,
451 |           revisesStage,
452 |           stageData
453 |         }
454 |       });
455 |       
456 |       if (!nextStageNeeded) {
457 |         flows[flowIndex].active = false;
458 |         return {
459 |           content: [{ type: "text", text: `${result.content[0].text}`}]
460 |         }
461 |       }
462 |       
463 |       return {
464 |         content: [{ type: "text", text: `${result.content[0].text}` }]
465 |       };
466 |     } catch (error) {
467 |       console.error(`Error ending session for domain ${domainName}:`, error);
468 |       return {
469 |         content: [{ type: "text", text: `Error ending session for domain ${domainName}: ${error}` }],
470 |         isError: true
471 |       };
472 |     }
473 |   }
474 | );
475 | 
476 | // Context management tools
477 | registerDomainTool(
478 |   "buildcontext",
479 |   {
480 |     type: z.enum(["entities", "relations", "observations"]),
481 |     data: z.array(z.any())
482 |   },
483 |   async ({ type, data }) => {
484 |     if (!activeDomain) {
485 |       return {
486 |         content: [{ type: "text", text: "Error: No active domain set. Use setActiveDomain or startsession tool first." }],
487 |         isError: true
488 |       };
489 |     }
490 | 
491 |     const domainClient = domainClients[activeDomain];
492 |     
493 |     if (!domainClient || !domainClient.connected) {
494 |       const connected = await domainClient.connect();
495 |       if (!connected) {
496 |         return {
497 |           content: [{ type: "text", text: `Error: Could not connect to domain server for '${activeDomain}'` }],
498 |           isError: true
499 |         };
500 |       }
501 |     }
502 |     
503 |     // Forward the buildcontext call to the domain server
504 |     try {
505 |       const result = await domainClient.callTool({
506 |         name: "buildcontext",
507 |         arguments: {
508 |           type,
509 |           data
510 |         }
511 |       });
512 |       
513 |       return result;
514 |     } catch (error) {
515 |       console.error(`Error building context for domain ${activeDomain}:`, error);
516 |       return {
517 |         content: [{ type: "text", text: `Error building context for domain ${activeDomain}: ${error}` }],
518 |         isError: true
519 |       };
520 |     }
521 |   }
522 | );
523 | 
524 | registerDomainTool(
525 |   "deletecontext",
526 |   {
527 |     type: z.enum(["entities", "relations", "observations"]),
528 |     data: z.array(z.any())
529 |   },
530 |   async ({ type, data }) => {
531 |     if (!activeDomain) {
532 |       return {
533 |         content: [{ type: "text", text: "Error: No active domain set. Use setActiveDomain or startsession tool first." }],
534 |         isError: true
535 |       };
536 |     }
537 | 
538 |     const domainClient = domainClients[activeDomain];
539 |     
540 |     if (!domainClient || !domainClient.connected) {
541 |       const connected = await domainClient.connect();
542 |       if (!connected) {
543 |         return {
544 |           content: [{ type: "text", text: `Error: Could not connect to domain server for '${activeDomain}'` }],
545 |           isError: true
546 |         };
547 |       }
548 |     }
549 |     
550 |     // Forward the deletecontext call to the domain server
551 |     try {
552 |       const result = await domainClient.callTool({
553 |         name: "deletecontext",
554 |         arguments: {
555 |           type,
556 |           data
557 |         }
558 |       });
559 |       
560 |       return result;
561 |     } catch (error) {
562 |       console.error(`Error deleting context for domain ${activeDomain}:`, error);
563 |       return {
564 |         content: [{ type: "text", text: `Error deleting context for domain ${activeDomain}: ${error}` }],
565 |         isError: true
566 |       };
567 |     }
568 |   }
569 | );
570 | 
571 | registerDomainTool(
572 |   "loadcontext",
573 |   {
574 |     entityName: z.string(),
575 |     entityType: z.string().optional(),
576 |     sessionId: z.string().optional()
577 |   },
578 |   async ({ entityName, entityType, sessionId }) => {
579 |     if (!activeDomain) {
580 |       return {
581 |         content: [{ type: "text", text: "Error: No active domain set. Use setActiveDomain or startsession tool first." }],
582 |         isError: true
583 |       };
584 |     }
585 | 
586 |     const flowId = `flow_${sessionId}`;
587 | 
588 |     // Find active contextmanager flow or use provided sessionId
589 |     let targetFlow: FlowInfo | undefined;
590 |     if (sessionId) {
591 |       targetFlow = flows.find(s => s.id === flowId);
592 |     } else {
593 |       targetFlow = flows.find(s => s.domain === activeDomain && s.active);
594 |     }
595 | 
596 |     if (!targetFlow) {
597 |       return {
598 |         content: [{ type: "text", text: "Error: No active Context Manager flow found. Start a flow first." }],
599 |         isError: true
600 |       };
601 |     }
602 | 
603 |     const domainClient = domainClients[activeDomain];
604 |     
605 |     if (!domainClient || !domainClient.connected) {
606 |       const connected = await domainClient.connect();
607 |       if (!connected) {
608 |         return {
609 |           content: [{ type: "text", text: `Error: Could not connect to domain server for '${activeDomain}'` }],
610 |           isError: true
611 |         };
612 |       }
613 |     }
614 |     
615 |     // Update contextmanager flow with entity details
616 |     targetFlow.entityName = entityName;
617 |     targetFlow.entityType = entityType || "unknown";
618 | 
619 |     // Forward the loadcontext call to the domain server
620 |     try {
621 |       const result = await domainClient.callTool({
622 |         name: "loadcontext",
623 |         arguments: {
624 |           entityName,
625 |           entityType,
626 |           sessionId: sessionId
627 |         }
628 |       });
629 |       
630 |       return {
631 |         content: [{ 
632 |           type: "text", 
633 |           text: `${result.content[0].text}`
634 |         }]
635 |       };
636 |     } catch (error) {
637 |       console.error(`Error loading context for domain ${activeDomain}:`, error);
638 |       return {
639 |         content: [{ type: "text", text: `Error loading context for domain ${activeDomain}: ${error}` }],
640 |         isError: true
641 |       };
642 |     }
643 |   }
644 | );
645 | 
646 | registerDomainTool(
647 |   "advancedcontext",
648 |   {
649 |     type: z.string(),
650 |     params: z.record(z.string(), z.any())
651 |   },
652 |   async ({ type, params }) => {
653 |     if (!activeDomain) {
654 |       return {
655 |         content: [{ type: "text", text: "Error: No active domain set. Use setActiveDomain or startsession tool first." }],
656 |         isError: true
657 |       };
658 |     }
659 | 
660 |     const domainClient = domainClients[activeDomain];
661 |     
662 |     if (!domainClient || !domainClient.connected) {
663 |       const connected = await domainClient.connect();
664 |       if (!connected) {
665 |         return {
666 |           content: [{ type: "text", text: `Error: Could not connect to domain server for '${activeDomain}'` }],
667 |           isError: true
668 |         };
669 |       }
670 |     }
671 |     
672 |     // Forward the advancedcontext call to the domain server
673 |     try {
674 |       const result = await domainClient.callTool({
675 |         name: "advancedcontext",
676 |         arguments: {
677 |           type,
678 |           params
679 |         }
680 |       });
681 |       
682 |       return result;
683 |     } catch (error) {
684 |       console.error(`Error performing advanced context operation for domain ${activeDomain}:`, error);
685 |       return {
686 |         content: [{ type: "text", text: `Error performing advanced context operation for domain ${activeDomain}: ${error}` }],
687 |         isError: true
688 |       };
689 |     }
690 |   }
691 | );
692 | 
693 | // // List all entities
694 | // server.tool(
695 | //   "listAllEntities",
696 | //   {
697 | //     domain: z.string().optional()
698 | //   },
699 | //   async () => {
700 | //     if (!activeDomain) {
701 | //       return {
702 | //         content: [{ type: "text", text: "Error: No active domain set. Use setActiveDomain or startsession tool first." }],
703 | //         isError: true
704 | //       };
705 | //     }
706 | 
707 | //     const domainClient = domainClients[activeDomain];
708 |     
709 | //     if (!domainClient || !domainClient.connected) {
710 | //       const connected = await domainClient.connect();
711 | //       if (!connected) {
712 | //         return {
713 | //           content: [{ type: "text", text: `Error: Could not connect to domain server for '${activeDomain}'` }],
714 | //           isError: true
715 | //         };
716 | //       }
717 | //     }
718 |     
719 | //     // Forward the listAllEntities call to the domain server
720 | //     try {
721 | //       const result = await domainClient.callTool({
722 | //         name: "listAllEntities",
723 | //         arguments: {}
724 | //       });
725 |       
726 | //       return result;
727 | //     } catch (error) {
728 | //       // If the domain server doesn't support listAllEntities, fall back to our domain registry
729 | //       const domain = domains.find(d => d.name === activeDomain);
730 | //       if (!domain) {
731 | //         return {
732 | //           content: [{ type: "text", text: `Error: Domain '${activeDomain}' not found.` }],
733 | //           isError: true
734 | //         };
735 | //       }
736 | 
737 | //       return {
738 | //         content: [{ 
739 | //           type: "text", 
740 | //           text: `Available entity types in ${activeDomain} domain: ${domain.entityTypes.join(", ")}` 
741 | //         }]
742 | //       };
743 | //     }
744 | //   }
745 | // );
746 | 
747 | // Domain discovery resources
748 | server.resource(
749 |   "domains",
750 |   "domains://list",
751 |   async (uri) => ({
752 |     contents: [{
753 |       uri: uri.href,
754 |       text: JSON.stringify(domains, null, 2)
755 |     }]
756 |   })
757 | );
758 | 
759 | // Cross-domain functionality
760 | server.tool(
761 |   "relateCrossDomain",
762 |   "Create connections between entities across different domains",
763 |   {
764 |     fromDomain: z.string(),
765 |     fromEntity: z.string(),
766 |     toDomain: z.string(),
767 |     toEntity: z.string(),
768 |     relationType: z.string()
769 |   },
770 |   async ({ fromDomain, fromEntity, toDomain, toEntity, relationType }) => {
771 |     // Validate domains
772 |     const fromDomainInfo = domains.find(d => d.name.toLowerCase() === fromDomain.toLowerCase());
773 |     const toDomainInfo = domains.find(d => d.name.toLowerCase() === toDomain.toLowerCase());
774 |     
775 |     if (!fromDomainInfo) {
776 |       return {
777 |         content: [{ type: "text", text: `Error: Source domain '${fromDomain}' not found.` }],
778 |         isError: true
779 |       };
780 |     }
781 |     
782 |     if (!toDomainInfo) {
783 |       return {
784 |         content: [{ type: "text", text: `Error: Target domain '${toDomain}' not found.` }],
785 |         isError: true
786 |       };
787 |     }
788 |     
789 |     // Create cross-domain relation by adding observations to both entities
790 |     try {
791 |       // Connect to source domain
792 |       const fromDomainClient = domainClients[fromDomainInfo.name];
793 |       if (!fromDomainClient || !fromDomainClient.connected) {
794 |         const connected = await fromDomainClient.connect();
795 |         if (!connected) {
796 |           return {
797 |             content: [{ type: "text", text: `Error: Could not connect to domain server for '${fromDomain}'` }],
798 |             isError: true
799 |           };
800 |         }
801 |       }
802 |       
803 |       // Connect to target domain
804 |       const toDomainClient = domainClients[toDomainInfo.name];
805 |       if (!toDomainClient || !toDomainClient.connected) {
806 |         const connected = await toDomainClient.connect();
807 |         if (!connected) {
808 |           return {
809 |             content: [{ type: "text", text: `Error: Could not connect to domain server for '${toDomain}'` }],
810 |             isError: true
811 |           };
812 |         }
813 |       }
814 |       
815 |       // Add observation to source entity about relation to target entity
816 |       await fromDomainClient.callTool({
817 |         name: "buildcontext",
818 |         arguments: {
819 |           type: "observations",
820 |           data: {
821 |             observations: [
822 |               {
823 |                 entityName: fromEntity,
824 |                 contents: [`Related to ${toEntity} (${toDomainInfo.name} domain) via ${relationType}`]
825 |               }
826 |             ]
827 |           }
828 |         }
829 |       });
830 |       
831 |       // Add observation to target entity about relation from source entity
832 |       await toDomainClient.callTool({
833 |         name: "buildcontext",
834 |         arguments: {
835 |           type: "observations",
836 |           data: {
837 |             observations: [
838 |               {
839 |                 entityName: toEntity,
840 |                 contents: [`Related from ${fromEntity} (${fromDomainInfo.name} domain) via ${relationType}`]
841 |               }
842 |             ]
843 |           }
844 |         }
845 |       });
846 |       
847 |       return {
848 |         content: [{ 
849 |           type: "text", 
850 |           text: `Created cross-domain relation: ${fromEntity} (${fromDomain}) --[${relationType}]--> ${toEntity} (${toDomain})` 
851 |         }]
852 |       };
853 |     } catch (error) {
854 |       console.error(`Error creating cross-domain relation:`, error);
855 |       return {
856 |         content: [{ type: "text", text: `Error creating cross-domain relation: ${error}` }],
857 |         isError: true
858 |       };
859 |     }
860 |   }
861 | );
862 | 
863 | // Main function to start the server
864 | async function main() {
865 |   // Start receiving messages on stdin and sending messages on stdout
866 |   const transport = new StdioServerTransport();
867 |   await server.connect(transport);
868 | }
869 | 
870 | // Execute the main function
871 | main().catch(error => {
872 |   console.error("Error in contextmanager:", error);
873 |   process.exit(1);
874 | }); 
```

--------------------------------------------------------------------------------
/developer/index.ts:
--------------------------------------------------------------------------------

```typescript
   1 | #!/usr/bin/env node
   2 | 
   3 | import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
   4 | import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
   5 | import { promises as fs } from 'fs';
   6 | import * as path from 'path';
   7 | import { fileURLToPath } from 'url';
   8 | import { z } from "zod";
   9 | import { readFileSync, existsSync } from "fs";
  10 | 
  11 | // Define memory file path using environment variable with fallback
  12 | const parentPath = path.dirname(fileURLToPath(import.meta.url));
  13 | const defaultMemoryPath = path.join(parentPath, 'memory.json');
  14 | const defaultSessionsPath = path.join(parentPath, 'sessions.json');
  15 | 
  16 | // Properly handle absolute and relative paths for MEMORY_FILE_PATH
  17 | const MEMORY_FILE_PATH = process.env.MEMORY_FILE_PATH
  18 |   ? path.isAbsolute(process.env.MEMORY_FILE_PATH)
  19 |     ? process.env.MEMORY_FILE_PATH  // Use absolute path as is
  20 |     : path.join(process.cwd(), process.env.MEMORY_FILE_PATH)  // Relative to current working directory
  21 |   : defaultMemoryPath;  // Default fallback
  22 | 
  23 | // Properly handle absolute and relative paths for SESSIONS_FILE_PATH
  24 | const SESSIONS_FILE_PATH = process.env.SESSIONS_FILE_PATH
  25 |   ? path.isAbsolute(process.env.SESSIONS_FILE_PATH)
  26 |     ? process.env.SESSIONS_FILE_PATH  // Use absolute path as is
  27 |     : path.join(process.cwd(), process.env.SESSIONS_FILE_PATH)  // Relative to current working directory
  28 |   : defaultSessionsPath;  // Default fallback
  29 | 
  30 | // Software Development specific entity types
  31 | const VALID_ENTITY_TYPES = [
  32 |   'project',       // Overall software project
  33 |   'component',     // Module, service, or package within a project
  34 |   'feature',       // Specific functionality being developed
  35 |   'issue',         // Bug or problem to be fixed
  36 |   'task',          // Work item or activity needed for development
  37 |   'technology',    // Language, framework, or tool used
  38 |   'decision',      // Important technical or architectural decision
  39 |   'milestone',     // Key project deadline or phase
  40 |   'environment',   // Development, staging, production environments
  41 |   'documentation', // Project documentation
  42 |   'requirement',   // Project requirement or specification
  43 |   'status',        // Entity status (inactive, active, or complete)
  44 |   'priority'       // Entity priority (low or high)
  45 | ];
  46 | 
  47 | // Software Development specific relation types
  48 | const VALID_RELATION_TYPES = [
  49 |   'depends_on',     // Dependency relationship
  50 |   'implements',     // Component implements a feature
  51 |   'blocked_by',     // Task is blocked by an issue
  52 |   'uses',           // Component uses a technology
  53 |   'part_of',        // Component is part of a project
  54 |   'contains',       // Project contains a component
  55 |   'related_to',     // General relationship
  56 |   'affects',        // Issue affects a component
  57 |   'resolves',       // Task resolves an issue
  58 |   'documented_in',  // Component is documented in documentation
  59 |   'decided_in',     // Decision was made in a meeting
  60 |   'required_by',    // Feature is required by a requirement
  61 |   'has_status',     // Entity has a particular status
  62 |   'has_priority',   // Entity has a particular priority
  63 |   'depends_on_milestone', // Task depends on reaching a milestone
  64 |   'precedes',       // Task precedes another task (for sequencing)
  65 |   'tested_in'       // Component is tested in an environment
  66 | ];
  67 | 
  68 | const __filename = fileURLToPath(import.meta.url);
  69 | const __dirname = path.dirname(__filename);
  70 | 
  71 | // Collect tool descriptions from text files
  72 | const toolDescriptions: Record<string, string> = {
  73 |   'startsession': '',
  74 |   'loadcontext': '',
  75 |   'deletecontext': '',
  76 |   'buildcontext': '',
  77 |   'advancedcontext': '',
  78 |   'endsession': '',
  79 | };
  80 | for (const tool of Object.keys(toolDescriptions)) {
  81 |   try {
  82 |     const descriptionFilePath = path.resolve(
  83 |       __dirname,
  84 |       `developer_${tool}.txt`
  85 |     );
  86 |     if (existsSync(descriptionFilePath)) {
  87 |         toolDescriptions[tool] = readFileSync(descriptionFilePath, 'utf-8');
  88 |       }
  89 |   } catch (error) {
  90 |     console.error(`Error reading description file for tool '${tool}': ${error}`);
  91 |   }
  92 | }
  93 | 
  94 | // Session management functions
  95 | async function loadSessionStates(): Promise<Map<string, any[]>> {
  96 |   try {
  97 |     const fileContent = await fs.readFile(SESSIONS_FILE_PATH, 'utf-8');
  98 |     const sessions = JSON.parse(fileContent);
  99 |     // Convert from object to Map
 100 |     const sessionsMap = new Map<string, any[]>();
 101 |     for (const [key, value] of Object.entries(sessions)) {
 102 |       sessionsMap.set(key, value as any[]);
 103 |     }
 104 |     return sessionsMap;
 105 |   } catch (error) {
 106 |     if (error instanceof Error && 'code' in error && (error as any).code === "ENOENT") {
 107 |       return new Map<string, any[]>();
 108 |     }
 109 |     throw error;
 110 |   }
 111 | }
 112 | 
 113 | async function saveSessionStates(sessionsMap: Map<string, any[]>): Promise<void> {
 114 |   // Convert from Map to object
 115 |   const sessions: Record<string, any[]> = {};
 116 |   for (const [key, value] of sessionsMap.entries()) {
 117 |     sessions[key] = value;
 118 |   }
 119 |   await fs.writeFile(SESSIONS_FILE_PATH, JSON.stringify(sessions, null, 2), 'utf-8');
 120 | }
 121 | 
 122 | // Generate a unique session ID
 123 | function generateSessionId(): string {
 124 |   return `dev_${Date.now()}_${Math.random().toString(36).substring(2, 15)}`;
 125 | }
 126 | 
 127 | // Basic validation functions
 128 | function validateEntityType(entityType: string): boolean {
 129 |   return VALID_ENTITY_TYPES.includes(entityType);
 130 | }
 131 | 
 132 | function validateRelationType(relationType: string): boolean {
 133 |   return VALID_RELATION_TYPES.includes(relationType);
 134 | }
 135 | 
 136 | // We are storing our memory using entities, relations, and observations in a graph structure
 137 | interface Entity {
 138 |   name: string;
 139 |   entityType: string;
 140 |   observations: string[];
 141 | }
 142 | 
 143 | interface Relation {
 144 |   from: string;
 145 |   to: string;
 146 |   relationType: string;
 147 | }
 148 | 
 149 | interface KnowledgeGraph {
 150 |   entities: Entity[];
 151 |   relations: Relation[];
 152 | }
 153 | 
 154 | // Define the valid status and priority values
 155 | const VALID_STATUS_VALUES = ['inactive', 'active', 'complete'];
 156 | const VALID_PRIORITY_VALUES = ['low', 'high'];
 157 | 
 158 | // The KnowledgeGraphManager class contains all operations to interact with the knowledge graph
 159 | class KnowledgeGraphManager {
 160 |   private async loadGraph(): Promise<KnowledgeGraph> {
 161 |     try {
 162 |       const fileContent = await fs.readFile(MEMORY_FILE_PATH, 'utf-8');
 163 |       return JSON.parse(fileContent);
 164 |     } catch (error) {
 165 |       if (error instanceof Error && 'code' in error && (error as any).code === "ENOENT") {
 166 |         return { entities: [], relations: [] };
 167 |       }
 168 |       throw error;
 169 |     }
 170 |   }
 171 | 
 172 |   private async saveGraph(graph: KnowledgeGraph): Promise<void> {
 173 |     await fs.writeFile(MEMORY_FILE_PATH, JSON.stringify(graph, null, 2), 'utf-8');
 174 |   }
 175 | 
 176 |   // Initialize status and priority entities
 177 |   async initializeStatusAndPriority(): Promise<void> {
 178 |     const graph = await this.loadGraph();
 179 |     
 180 |     // Create status entities if they don't exist
 181 |     for (const statusValue of VALID_STATUS_VALUES) {
 182 |       const statusName = `status:${statusValue}`;
 183 |       if (!graph.entities.some(e => e.name === statusName && e.entityType === 'status')) {
 184 |         graph.entities.push({
 185 |           name: statusName,
 186 |           entityType: 'status',
 187 |           observations: [`A ${statusValue} status value`]
 188 |         });
 189 |       }
 190 |     }
 191 |     
 192 |     // Create priority entities if they don't exist
 193 |     for (const priorityValue of VALID_PRIORITY_VALUES) {
 194 |       const priorityName = `priority:${priorityValue}`;
 195 |       if (!graph.entities.some(e => e.name === priorityName && e.entityType === 'priority')) {
 196 |         graph.entities.push({
 197 |           name: priorityName,
 198 |           entityType: 'priority',
 199 |           observations: [`A ${priorityValue} priority value`]
 200 |         });
 201 |       }
 202 |     }
 203 |     
 204 |     await this.saveGraph(graph);
 205 |   }
 206 | 
 207 |   // Helper method to get status of an entity
 208 |   async getEntityStatus(entityName: string): Promise<string | null> {
 209 |     const graph = await this.loadGraph();
 210 |     
 211 |     // Find status relation for this entity
 212 |     const statusRelation = graph.relations.find(r => 
 213 |       r.from === entityName && 
 214 |       r.relationType === 'has_status'
 215 |     );
 216 |     
 217 |     if (statusRelation) {
 218 |       // Extract status value from the status entity name (status:value)
 219 |       return statusRelation.to.split(':')[1];
 220 |     }
 221 |     
 222 |     return null;
 223 |   }
 224 |   
 225 |   // Helper method to get priority of an entity
 226 |   async getEntityPriority(entityName: string): Promise<string | null> {
 227 |     const graph = await this.loadGraph();
 228 |     
 229 |     // Find priority relation for this entity
 230 |     const priorityRelation = graph.relations.find(r => 
 231 |       r.from === entityName && 
 232 |       r.relationType === 'has_priority'
 233 |     );
 234 |     
 235 |     if (priorityRelation) {
 236 |       // Extract priority value from the priority entity name (priority:value)
 237 |       return priorityRelation.to.split(':')[1];
 238 |     }
 239 |     
 240 |     return null;
 241 |   }
 242 |   
 243 |   // Helper method to set status of an entity
 244 |   async setEntityStatus(entityName: string, statusValue: string): Promise<void> {
 245 |     if (!VALID_STATUS_VALUES.includes(statusValue)) {
 246 |       throw new Error(`Invalid status value: ${statusValue}. Valid values are: ${VALID_STATUS_VALUES.join(', ')}`);
 247 |     }
 248 |     
 249 |     const graph = await this.loadGraph();
 250 |     
 251 |     // Remove any existing status relations for this entity
 252 |     graph.relations = graph.relations.filter(r => 
 253 |       !(r.from === entityName && r.relationType === 'has_status')
 254 |     );
 255 |     
 256 |     // Add new status relation
 257 |     graph.relations.push({
 258 |       from: entityName,
 259 |       to: `status:${statusValue}`,
 260 |       relationType: 'has_status'
 261 |     });
 262 |     
 263 |     await this.saveGraph(graph);
 264 |   }
 265 |   
 266 |   // Helper method to set priority of an entity
 267 |   async setEntityPriority(entityName: string, priorityValue: string): Promise<void> {
 268 |     if (!VALID_PRIORITY_VALUES.includes(priorityValue)) {
 269 |       throw new Error(`Invalid priority value: ${priorityValue}. Valid values are: ${VALID_PRIORITY_VALUES.join(', ')}`);
 270 |     }
 271 |     
 272 |     const graph = await this.loadGraph();
 273 |     
 274 |     // Remove any existing priority relations for this entity
 275 |     graph.relations = graph.relations.filter(r => 
 276 |       !(r.from === entityName && r.relationType === 'has_priority')
 277 |     );
 278 |     
 279 |     // Add new priority relation
 280 |     graph.relations.push({
 281 |       from: entityName,
 282 |       to: `priority:${priorityValue}`,
 283 |       relationType: 'has_priority'
 284 |     });
 285 |     
 286 |     await this.saveGraph(graph);
 287 |   }
 288 | 
 289 |   async createEntities(entities: Entity[]): Promise<Entity[]> {
 290 |     // Validate entity types
 291 |     for (const entity of entities) {
 292 |       if (!validateEntityType(entity.entityType)) {
 293 |         throw new Error(`Invalid entity type: ${entity.entityType}. Valid types are: ${VALID_ENTITY_TYPES.join(', ')}`);
 294 |       }
 295 |     }
 296 | 
 297 |     const graph = await this.loadGraph();
 298 |     const newEntities = entities.filter(e => !graph.entities.some(existingEntity => existingEntity.name === e.name));
 299 |     graph.entities.push(...newEntities);
 300 |     await this.saveGraph(graph);
 301 |     return newEntities;
 302 |   }
 303 | 
 304 |   async createRelations(relations: Relation[]): Promise<Relation[]> {
 305 |     // Validate relation types
 306 |     for (const relation of relations) {
 307 |       if (!validateRelationType(relation.relationType)) {
 308 |         throw new Error(`Invalid relation type: ${relation.relationType}. Valid types are: ${VALID_RELATION_TYPES.join(', ')}`);
 309 |       }
 310 |     }
 311 | 
 312 |     const graph = await this.loadGraph();
 313 |     
 314 |     // Check if entities exist
 315 |     for (const relation of relations) {
 316 |       const fromEntity = graph.entities.find(e => e.name === relation.from);
 317 |       const toEntity = graph.entities.find(e => e.name === relation.to);
 318 |       
 319 |       if (!fromEntity) {
 320 |         throw new Error(`Source entity '${relation.from}' does not exist. Please create it first.`);
 321 |       }
 322 |       if (!toEntity) {
 323 |         throw new Error(`Target entity '${relation.to}' does not exist. Please create it first.`);
 324 |       }
 325 |     }
 326 |     
 327 |     const newRelations = relations.filter(r => !graph.relations.some(existingRelation => 
 328 |       existingRelation.from === r.from && 
 329 |       existingRelation.to === r.to && 
 330 |       existingRelation.relationType === r.relationType
 331 |     ));
 332 |     graph.relations.push(...newRelations);
 333 |     await this.saveGraph(graph);
 334 |     return newRelations;
 335 |   }
 336 | 
 337 |   async addObservations(observations: { entityName: string; contents: string[] }[]): Promise<{ entityName: string; addedObservations: string[] }[]> {
 338 |     const graph = await this.loadGraph();
 339 |     const results = observations.map(o => {
 340 |       const entity = graph.entities.find(e => e.name === o.entityName);
 341 |       if (!entity) {
 342 |         throw new Error(`Entity with name ${o.entityName} not found`);
 343 |       }
 344 |       const newObservations = o.contents.filter(content => !entity.observations.includes(content));
 345 |       entity.observations.push(...newObservations);
 346 |       return { entityName: o.entityName, addedObservations: newObservations };
 347 |     });
 348 |     await this.saveGraph(graph);
 349 |     return results;
 350 |   }
 351 | 
 352 |   async deleteEntities(entityNames: string[]): Promise<void> {
 353 |     const graph = await this.loadGraph();
 354 |     graph.entities = graph.entities.filter(e => !entityNames.includes(e.name));
 355 |     graph.relations = graph.relations.filter(r => !entityNames.includes(r.from) && !entityNames.includes(r.to));
 356 |     await this.saveGraph(graph);
 357 |   }
 358 | 
 359 |   async deleteObservations(deletions: { entityName: string; observations: string[] }[]): Promise<void> {
 360 |     const graph = await this.loadGraph();
 361 |     deletions.forEach(d => {
 362 |       const entity = graph.entities.find(e => e.name === d.entityName);
 363 |       if (entity) {
 364 |         entity.observations = entity.observations.filter(o => !d.observations.includes(o));
 365 |       }
 366 |     });
 367 |     await this.saveGraph(graph);
 368 |   }
 369 | 
 370 |   async deleteRelations(relations: Relation[]): Promise<void> {
 371 |     const graph = await this.loadGraph();
 372 |     graph.relations = graph.relations.filter(r => !relations.some(delRelation => 
 373 |       r.from === delRelation.from && 
 374 |       r.to === delRelation.to && 
 375 |       r.relationType === delRelation.relationType
 376 |     ));
 377 |     await this.saveGraph(graph);
 378 |   }
 379 | 
 380 |   async readGraph(): Promise<KnowledgeGraph> {
 381 |     return this.loadGraph();
 382 |   }
 383 | 
 384 |   // Basic search function
 385 |   async searchNodes(query: string): Promise<KnowledgeGraph> {
 386 |     const graph = await this.loadGraph();
 387 |     
 388 |     // Filter entities
 389 |     const filteredEntities = graph.entities.filter(e => 
 390 |       e.name.toLowerCase().includes(query.toLowerCase()) ||
 391 |       e.entityType.toLowerCase().includes(query.toLowerCase()) ||
 392 |       e.observations.some(o => o.toLowerCase().includes(query.toLowerCase()))
 393 |     );
 394 |   
 395 |     // Create a Set of filtered entity names for quick lookup
 396 |     const filteredEntityNames = new Set(filteredEntities.map(e => e.name));
 397 |   
 398 |     // Filter relations to only include those between filtered entities
 399 |     const filteredRelations = graph.relations.filter(r => 
 400 |       filteredEntityNames.has(r.from) && filteredEntityNames.has(r.to)
 401 |     );
 402 |   
 403 |     const filteredGraph: KnowledgeGraph = {
 404 |       entities: filteredEntities,
 405 |       relations: filteredRelations,
 406 |     };
 407 |   
 408 |     return filteredGraph;
 409 |   }
 410 | 
 411 |   async openNodes(names: string[]): Promise<KnowledgeGraph> {
 412 |     const graph = await this.loadGraph();
 413 |     
 414 |     // Filter entities
 415 |     const filteredEntities = graph.entities.filter(e => names.includes(e.name));
 416 |   
 417 |     // Create a Set of filtered entity names for quick lookup
 418 |     const filteredEntityNames = new Set(filteredEntities.map(e => e.name));
 419 |   
 420 |     // Filter relations to only include those between filtered entities
 421 |     const filteredRelations = graph.relations.filter(r => 
 422 |       filteredEntityNames.has(r.from) && filteredEntityNames.has(r.to)
 423 |     );
 424 |   
 425 |     const filteredGraph: KnowledgeGraph = {
 426 |       entities: filteredEntities,
 427 |       relations: filteredRelations,
 428 |     };
 429 |   
 430 |     return filteredGraph;
 431 |   }
 432 | 
 433 |   // Software Development specific functions
 434 |   
 435 |   // Get project overview including components, features, issues, etc.
 436 |   async getProjectStatus(projectName: string): Promise<any> {
 437 |     const graph = await this.loadGraph();
 438 |     
 439 |     // Find the project entity
 440 |     const project = graph.entities.find(e => e.name === projectName && e.entityType === 'project');
 441 |     if (!project) {
 442 |       throw new Error(`Project '${projectName}' not found`);
 443 |     }
 444 |     
 445 |     // Find components that are part of this project
 446 |     const components: Entity[] = [];
 447 |     
 448 |     // Find features, issues, tasks, milestones related to this project
 449 |     const features: Entity[] = [];
 450 |     const issues: Entity[] = [];
 451 |     const tasks: Entity[] = [];
 452 |     const milestones: Entity[] = [];
 453 |     
 454 |     // Find entities directly related to the project
 455 |     for (const relation of graph.relations) {
 456 |       if (relation.from === projectName || relation.to === projectName) {
 457 |         const relatedEntity = graph.entities.find(e => 
 458 |           (relation.from === projectName && e.name === relation.to) || 
 459 |           (relation.to === projectName && e.name === relation.from)
 460 |         );
 461 |         
 462 |         if (relatedEntity) {
 463 |           if (relatedEntity.entityType === 'component') components.push(relatedEntity);
 464 |           if (relatedEntity.entityType === 'feature') features.push(relatedEntity);
 465 |           if (relatedEntity.entityType === 'issue') issues.push(relatedEntity);
 466 |           if (relatedEntity.entityType === 'task') tasks.push(relatedEntity);
 467 |           if (relatedEntity.entityType === 'milestone') milestones.push(relatedEntity);
 468 |         }
 469 |       }
 470 |     }
 471 |     
 472 |     // Find entities related to components of the project
 473 |     for (const component of components) {
 474 |       for (const relation of graph.relations) {
 475 |         if (relation.from === component.name || relation.to === component.name) {
 476 |           const relatedEntity = graph.entities.find(e => 
 477 |             (relation.from === component.name && e.name === relation.to) || 
 478 |             (relation.to === component.name && e.name === relation.from)
 479 |           );
 480 |           
 481 |           if (relatedEntity) {
 482 |             if (relatedEntity.entityType === 'feature' && !features.some(f => f.name === relatedEntity.name)) {
 483 |               features.push(relatedEntity);
 484 |             }
 485 |             if (relatedEntity.entityType === 'issue' && !issues.some(i => i.name === relatedEntity.name)) {
 486 |               issues.push(relatedEntity);
 487 |             }
 488 |             if (relatedEntity.entityType === 'task' && !tasks.some(t => t.name === relatedEntity.name)) {
 489 |               tasks.push(relatedEntity);
 490 |             }
 491 |           }
 492 |         }
 493 |       }
 494 |     }
 495 |     
 496 |     // Get active tasks and issues
 497 |     const statuses: Record<string, string> = {};
 498 |     const priorities: Record<string, string> = {};
 499 |     
 500 |     // Load status and priority for tasks and issues
 501 |     for (const entity of [...tasks, ...issues, ...features, ...milestones]) {
 502 |       const status = await this.getEntityStatus(entity.name);
 503 |       if (status) {
 504 |         statuses[entity.name] = status;
 505 |       }
 506 |       
 507 |       const priority = await this.getEntityPriority(entity.name);
 508 |       if (priority) {
 509 |         priorities[entity.name] = priority;
 510 |       }
 511 |     }
 512 |     
 513 |     // Filter active tasks and issues based on status
 514 |     const activeTasks = tasks.filter(task => {
 515 |       const status = statuses[task.name];
 516 |       return status ? status === 'active' : true;
 517 |     });
 518 |     
 519 |     const activeIssues = issues.filter(issue => {
 520 |       const status = statuses[issue.name];
 521 |       return status ? status === 'active' : true;
 522 |     });
 523 |     
 524 |     // Find upcoming milestones
 525 |     const upcomingMilestones = milestones.filter(milestone => {
 526 |       const status = statuses[milestone.name];
 527 |       return status ? status === 'active' : true;
 528 |     });
 529 |     
 530 |     // Get decision history
 531 |     const decisions = graph.entities.filter(e => 
 532 |       e.entityType === 'decision' && 
 533 |       graph.relations.some(r => 
 534 |         (r.from === e.name && r.to === projectName) || 
 535 |         (r.to === e.name && r.from === projectName)
 536 |       )
 537 |     );
 538 |     
 539 |     // Find task sequencing
 540 |     const taskSequencing: Record<string, string[]> = {};
 541 |     for (const task of tasks) {
 542 |       const precedingTasks: string[] = [];
 543 |       const followingTasks: string[] = [];
 544 |       
 545 |       // Find tasks that this task precedes
 546 |       for (const relation of graph.relations) {
 547 |         if (relation.from === task.name && relation.relationType === 'precedes') {
 548 |           followingTasks.push(relation.to);
 549 |         }
 550 |         if (relation.to === task.name && relation.relationType === 'precedes') {
 551 |           precedingTasks.push(relation.from);
 552 |         }
 553 |       }
 554 |       
 555 |       if (precedingTasks.length > 0 || followingTasks.length > 0) {
 556 |         taskSequencing[task.name] = {
 557 |           precedingTasks,
 558 |           followingTasks
 559 |         } as any;
 560 |       }
 561 |     }
 562 |     
 563 |     return {
 564 |       project,
 565 |       components,
 566 |       activeFeatures: features.filter(f => {
 567 |         const status = statuses[f.name];
 568 |         return status ? status === 'active' : true;
 569 |       }),
 570 |       activeTasks,
 571 |       activeIssues,
 572 |       upcomingMilestones,
 573 |       allFeatures: features,
 574 |       allIssues: issues,
 575 |       allTasks: tasks,
 576 |       allMilestones: milestones,
 577 |       recentDecisions: decisions.slice(0, 5),  // Limit to 5 most recent decisions
 578 |       statuses,  // Include status mapping for reference
 579 |       priorities,  // Include priority mapping for reference
 580 |       taskSequencing  // Include task sequencing information
 581 |     };
 582 |   }
 583 |   
 584 |   // Get detailed context for a specific component
 585 |   async getComponentContext(componentName: string): Promise<any> {
 586 |     const graph = await this.loadGraph();
 587 |     
 588 |     // Find the component entity
 589 |     const component = graph.entities.find(e => e.name === componentName && e.entityType === 'component');
 590 |     if (!component) {
 591 |       throw new Error(`Component '${componentName}' not found`);
 592 |     }
 593 |     
 594 |     // Find projects this component is part of
 595 |     const projects: Entity[] = [];
 596 |     
 597 |     for (const relation of graph.relations) {
 598 |       if (relation.relationType === 'contains' && relation.to === componentName) {
 599 |         const project = graph.entities.find(e => e.name === relation.from && e.entityType === 'project');
 600 |         if (project) {
 601 |           projects.push(project);
 602 |         }
 603 |       }
 604 |     }
 605 |     
 606 |     // Find features implemented by this component
 607 |     const features: Entity[] = [];
 608 |     
 609 |     for (const relation of graph.relations) {
 610 |       if (relation.relationType === 'implements' && relation.from === componentName) {
 611 |         const feature = graph.entities.find(e => e.name === relation.to && e.entityType === 'feature');
 612 |         if (feature) {
 613 |           features.push(feature);
 614 |         }
 615 |       }
 616 |     }
 617 |     
 618 |     // Find technologies used by this component
 619 |     const technologies: Entity[] = [];
 620 |     
 621 |     for (const relation of graph.relations) {
 622 |       if (relation.relationType === 'uses' && relation.from === componentName) {
 623 |         const technology = graph.entities.find(e => e.name === relation.to && e.entityType === 'technology');
 624 |         if (technology) {
 625 |           technologies.push(technology);
 626 |         }
 627 |       }
 628 |     }
 629 |     
 630 |     // Find issues affecting this component
 631 |     const issues: Entity[] = [];
 632 |     
 633 |     for (const relation of graph.relations) {
 634 |       if (relation.relationType === 'affects' && relation.to === componentName) {
 635 |         const issue = graph.entities.find(e => e.name === relation.from && e.entityType === 'issue');
 636 |         if (issue) {
 637 |           issues.push(issue);
 638 |         }
 639 |       }
 640 |     }
 641 |     
 642 |     // Find tasks related to this component
 643 |     const tasks = [];
 644 |     for (const relation of graph.relations) {
 645 |       if ((relation.from === componentName || relation.to === componentName) && 
 646 |           graph.entities.some(e => 
 647 |             (e.name === relation.from || e.name === relation.to) && 
 648 |             e.name !== componentName && 
 649 |             e.entityType === 'task'
 650 |           )) {
 651 |         const task = graph.entities.find(e => 
 652 |           (e.name === relation.from || e.name === relation.to) && 
 653 |           e.name !== componentName && 
 654 |           e.entityType === 'task'
 655 |         );
 656 |         if (task) {
 657 |           tasks.push(task);
 658 |         }
 659 |       }
 660 |     }
 661 |     
 662 |     // Find documentation for this component
 663 |     const documentation = [];
 664 |     for (const relation of graph.relations) {
 665 |       if (relation.relationType === 'documented_in' && relation.from === componentName) {
 666 |         const doc = graph.entities.find(e => e.name === relation.to && e.entityType === 'documentation');
 667 |         if (doc) {
 668 |           documentation.push(doc);
 669 |         }
 670 |       }
 671 |     }
 672 |     
 673 |     // Find dependencies
 674 |     const dependencies = [];
 675 |     for (const relation of graph.relations) {
 676 |       if (relation.relationType === 'depends_on' && relation.from === componentName) {
 677 |         const dependency = graph.entities.find(e => e.name === relation.to);
 678 |         if (dependency) {
 679 |           dependencies.push(dependency);
 680 |         }
 681 |       }
 682 |     }
 683 |     
 684 |     // Get statuses and priorities for tasks and issues
 685 |     const statuses: Record<string, string> = {};
 686 |     const priorities: Record<string, string> = {};
 687 |     
 688 |     // Load status and priority for tasks and issues
 689 |     for (const entity of [...tasks, ...issues, ...features]) {
 690 |       const status = await this.getEntityStatus(entity.name);
 691 |       if (status) {
 692 |         statuses[entity.name] = status;
 693 |       }
 694 |       
 695 |       const priority = await this.getEntityPriority(entity.name);
 696 |       if (priority) {
 697 |         priorities[entity.name] = priority;
 698 |       }
 699 |     }
 700 |     
 701 |     return {
 702 |       component,
 703 |       projects,
 704 |       features,
 705 |       technologies,
 706 |       activeIssues: issues.filter(issue => {
 707 |         const status = statuses[issue.name];
 708 |         return status ? status === 'active' : true;
 709 |       }),
 710 |       activeTasks: tasks.filter(task => {
 711 |         const status = statuses[task.name];
 712 |         return status ? status === 'active' : true;
 713 |       }),
 714 |       documentation,
 715 |       dependencies,
 716 |       allIssues: issues,
 717 |       allTasks: tasks,
 718 |       statuses,
 719 |       priorities
 720 |     };
 721 |   }
 722 |   
 723 |   // Get all entities related to a specific entity
 724 |   async getRelatedEntities(entityName: string, relationTypes?: string[]): Promise<any> {
 725 |     const graph = await this.loadGraph();
 726 |     
 727 |     // Find the entity
 728 |     const entity = graph.entities.find(e => e.name === entityName);
 729 |     if (!entity) {
 730 |       throw new Error(`Entity '${entityName}' not found`);
 731 |     }
 732 |     
 733 |     // Find all relations involving this entity
 734 |     let relevantRelations = graph.relations.filter(r => r.from === entityName || r.to === entityName);
 735 |     
 736 |     // Filter by relation types if specified
 737 |     if (relationTypes && relationTypes.length > 0) {
 738 |       relevantRelations = relevantRelations.filter(r => relationTypes.includes(r.relationType));
 739 |     }
 740 |     
 741 |     // Get all related entities
 742 |     const related = {
 743 |       entity,
 744 |       incomingRelations: [] as { relation: Relation; source: Entity }[],
 745 |       outgoingRelations: [] as { relation: Relation; target: Entity }[],
 746 |     };
 747 |     
 748 |     for (const relation of relevantRelations) {
 749 |       if (relation.from === entityName) {
 750 |         const target = graph.entities.find(e => e.name === relation.to);
 751 |         if (target) {
 752 |           related.outgoingRelations.push({
 753 |             relation,
 754 |             target
 755 |           });
 756 |         }
 757 |       } else {
 758 |         const source = graph.entities.find(e => e.name === relation.from);
 759 |         if (source) {
 760 |           related.incomingRelations.push({
 761 |             relation,
 762 |             source
 763 |           });
 764 |         }
 765 |       }
 766 |     }
 767 |     
 768 |     return related;
 769 |   }
 770 |   
 771 |   // Get the history of decisions related to a project
 772 |   async getDecisionHistory(projectName: string): Promise<any> {
 773 |     const graph = await this.loadGraph();
 774 |     
 775 |     // Find the project
 776 |     const project = graph.entities.find(e => e.name === projectName && e.entityType === "project");
 777 |     if (!project) {
 778 |       throw new Error(`Project '${projectName}' not found`);
 779 |     }
 780 |     
 781 |     // Find all decision entities related to this project
 782 |     const decisions: Entity[] = [];
 783 |     
 784 |     // Direct decision relations to the project
 785 |     for (const relation of graph.relations) {
 786 |       if (relation.relationType === "related_to" && relation.to === projectName) {
 787 |         const decision = graph.entities.find(e => e.name === relation.from && e.entityType === "decision");
 788 |         if (decision) {
 789 |           decisions.push(decision);
 790 |         }
 791 |       }
 792 |     }
 793 |     
 794 |     // Decisions related to components of the project
 795 |     const components: Entity[] = [];
 796 |     
 797 |     for (const relation of graph.relations) {
 798 |       if (relation.relationType === "contains" && relation.from === projectName) {
 799 |         const component = graph.entities.find(e => e.name === relation.to && e.entityType === "component");
 800 |         if (component) {
 801 |           components.push(component);
 802 |         }
 803 |       }
 804 |     }
 805 |     
 806 |     for (const component of components) {
 807 |       for (const relation of graph.relations) {
 808 |         if (relation.relationType === "related_to" && relation.to === component.name) {
 809 |           const decision = graph.entities.find(e => e.name === relation.from && e.entityType === "decision");
 810 |           if (decision && !decisions.some(d => d.name === decision.name)) {
 811 |             decisions.push(decision);
 812 |           }
 813 |         }
 814 |       }
 815 |     }
 816 |     
 817 |     // Sort decisions chronologically if they have date observations
 818 |     const decisionsWithDates = decisions.map(decision => {
 819 |       const dateObs = decision.observations.find(o => o.startsWith('Date:'));
 820 |       return {
 821 |         decision,
 822 |         date: dateObs ? new Date(dateObs.split(':')[1].trim()) : new Date(0)
 823 |       };
 824 |     });
 825 |     
 826 |     decisionsWithDates.sort((a, b) => b.date.getTime() - a.date.getTime());
 827 |     
 828 |     return {
 829 |       project,
 830 |       decisions: decisionsWithDates.map(d => d.decision),
 831 |     };
 832 |   }
 833 |   
 834 |   // Get progress toward a milestone
 835 |   async getMilestoneProgress(milestoneName: string): Promise<any> {
 836 |     const graph = await this.loadGraph();
 837 |     
 838 |     // Find the milestone
 839 |     const milestone = graph.entities.find(e => e.name === milestoneName && e.entityType === "milestone");
 840 |     if (!milestone) {
 841 |       throw new Error(`Milestone '${milestoneName}' not found`);
 842 |     }
 843 |     
 844 |     // Find all tasks related to this milestone
 845 |     const tasks: Entity[] = [];
 846 |     
 847 |     for (const relation of graph.relations) {
 848 |       if (relation.relationType === "related_to" && relation.to === milestoneName) {
 849 |         const task = graph.entities.find(e => e.name === relation.from && e.entityType === "task");
 850 |         if (task) {
 851 |           tasks.push(task);
 852 |         }
 853 |       }
 854 |     }
 855 |     
 856 |     // Get statuses for all tasks
 857 |     const statuses: Record<string, string> = {};
 858 |     
 859 |     // Load status for tasks
 860 |     for (const task of tasks) {
 861 |       const status = await this.getEntityStatus(task.name);
 862 |       if (status) {
 863 |         statuses[task.name] = status;
 864 |       }
 865 |     }
 866 |     
 867 |     // Group tasks by status
 868 |     const completedTasks: Entity[] = [];
 869 |     const inProgressTasks: Entity[] = [];
 870 |     const notStartedTasks: Entity[] = [];
 871 |     
 872 |     for (const task of tasks) {
 873 |       const status = statuses[task.name] || 'inactive';
 874 |       
 875 |       if (status === 'complete') {
 876 |         completedTasks.push(task);
 877 |       } else if (status === 'active') {
 878 |         inProgressTasks.push(task);
 879 |       } else {
 880 |         notStartedTasks.push(task);
 881 |       }
 882 |     }
 883 |     
 884 |     // Calculate progress percentage
 885 |     const totalTasks = tasks.length;
 886 |     const progressPercentage = totalTasks > 0 
 887 |       ? Math.round((completedTasks.length / totalTasks) * 100) 
 888 |       : 0;
 889 |     
 890 |     // Find task sequencing
 891 |     const taskSequencing: Record<string, any> = {};
 892 |     for (const task of tasks) {
 893 |       const precedingTasks: string[] = [];
 894 |       const followingTasks: string[] = [];
 895 |       
 896 |       // Find tasks that this task precedes
 897 |       for (const relation of graph.relations) {
 898 |         if (relation.from === task.name && relation.relationType === 'precedes') {
 899 |           followingTasks.push(relation.to);
 900 |         }
 901 |         if (relation.to === task.name && relation.relationType === 'precedes') {
 902 |           precedingTasks.push(relation.from);
 903 |         }
 904 |       }
 905 |       
 906 |       if (precedingTasks.length > 0 || followingTasks.length > 0) {
 907 |         taskSequencing[task.name] = {
 908 |           precedingTasks,
 909 |           followingTasks
 910 |         };
 911 |       }
 912 |     }
 913 |     
 914 |     // Determine if milestone can be considered complete
 915 |     const milestoneComplete = tasks.length > 0 && tasks.every(task => 
 916 |       statuses[task.name] === 'complete'
 917 |     );
 918 |     
 919 |     return {
 920 |       milestone,
 921 |       progress: {
 922 |         totalTasks,
 923 |         completedTasks: completedTasks.length,
 924 |         inProgressTasks: inProgressTasks.length,
 925 |         notStartedTasks: notStartedTasks.length,
 926 |         percentage: progressPercentage,
 927 |         complete: milestoneComplete
 928 |       },
 929 |       tasks: {
 930 |         completed: completedTasks,
 931 |         inProgress: inProgressTasks,
 932 |         notStarted: notStartedTasks
 933 |       },
 934 |       taskSequencing,
 935 |       statuses
 936 |     };
 937 |   }
 938 | }
 939 | 
 940 | // Main function to set up the MCP server
 941 | async function main() {
 942 |   try {
 943 |     const knowledgeGraphManager = new KnowledgeGraphManager();
 944 |     
 945 |     // Initialize status and priority entities
 946 |     await knowledgeGraphManager.initializeStatusAndPriority();
 947 |     
 948 |     // Initialize session states from persistent storage
 949 |     const sessionStates = await loadSessionStates();
 950 |     
 951 |     // Create the MCP server with a name and version
 952 |     const server = new McpServer({
 953 |       name: "Context Manager",
 954 |       version: "1.0.0"
 955 |     });
 956 |     
 957 |     // Define a resource that exposes the entire graph
 958 |     server.resource(
 959 |       "graph",
 960 |       "graph://developer",
 961 |       async (uri) => ({
 962 |         contents: [{
 963 |           uri: uri.href,
 964 |           text: JSON.stringify(await knowledgeGraphManager.readGraph(), null, 2)
 965 |         }]
 966 |       })
 967 |     );
 968 |     
 969 |     // Define tools using zod for parameter validation
 970 |     
 971 |     // CRUD operations - these are now consolidated into buildcontext, deletecontext, and advancedcontext tools
 972 | 
 973 |     /**
 974 |      * Create new entities, relations, and observations.
 975 |      */
 976 |     server.tool(
 977 |       "buildcontext",
 978 |       toolDescriptions["buildcontext"],
 979 |       {
 980 |         type: z.enum(["entities", "relations", "observations"]).describe("Type of creation operation: 'entities', 'relations', or 'observations'"),
 981 |         data: z.array(z.any()).describe("Data for the creation operation, structure varies by type but must be an array")
 982 |       },
 983 |       async ({ type, data }) => {
 984 |         try {
 985 |           let result;
 986 |           
 987 |           switch (type) {
 988 |             case "entities":
 989 |               // Ensure entities match the Entity interface
 990 |               const typedEntities: Entity[] = data.map((e: any) => ({
 991 |                 name: e.name,
 992 |                 entityType: e.entityType,
 993 |                 observations: e.observations
 994 |               }));
 995 |               result = await knowledgeGraphManager.createEntities(typedEntities);
 996 |               return {
 997 |                 content: [{
 998 |                   type: "text",
 999 |                   text: JSON.stringify({ success: true, created: result }, null, 2)
1000 |                 }]
1001 |               };
1002 |               
1003 |             case "relations":
1004 |               // Ensure relations match the Relation interface
1005 |               const typedRelations: Relation[] = data.map((r: any) => ({
1006 |                 from: r.from,
1007 |                 to: r.to,
1008 |                 relationType: r.relationType
1009 |               }));
1010 |               result = await knowledgeGraphManager.createRelations(typedRelations);
1011 |               return {
1012 |                 content: [{
1013 |                   type: "text",
1014 |                   text: JSON.stringify({ success: true, created: result }, null, 2)
1015 |                 }]
1016 |               };
1017 |               
1018 |             case "observations":
1019 |               // Ensure observations match the required interface
1020 |               const typedObservations: { entityName: string; contents: string[] }[] = data.map((o: any) => ({
1021 |                 entityName: o.entityName,
1022 |                 contents: o.contents
1023 |               }));
1024 |               result = await knowledgeGraphManager.addObservations(typedObservations);
1025 |               return {
1026 |                 content: [{
1027 |                   type: "text",
1028 |                   text: JSON.stringify({ success: true, added: result }, null, 2)
1029 |                 }]
1030 |               };
1031 |               
1032 |             default:
1033 |               throw new Error(`Invalid type: ${type}. Must be 'entities', 'relations', or 'observations'.`);
1034 |           }
1035 |         } catch (error) {
1036 |           return {
1037 |             content: [{
1038 |               type: "text",
1039 |               text: JSON.stringify({ 
1040 |                 success: false,
1041 |                 error: error instanceof Error ? error.message : String(error)
1042 |               }, null, 2)
1043 |             }]
1044 |           };
1045 |         }
1046 |       }
1047 |     );
1048 | 
1049 |     /**
1050 |      * Delete entities, relations, and observations.
1051 |      */
1052 |     server.tool(
1053 |       "deletecontext",
1054 |       toolDescriptions["deletecontext"],
1055 |       {
1056 |         type: z.enum(["entities", "relations", "observations"]).describe("Type of deletion operation: 'entities', 'relations', or 'observations'"),
1057 |         data: z.array(z.any()).describe("Data for the deletion operation, structure varies by type but must be an array")
1058 |       },
1059 |       async ({ type, data }) => {
1060 |         try {
1061 |           switch (type) {
1062 |             case "entities":
1063 |               await knowledgeGraphManager.deleteEntities(data);
1064 |               return {
1065 |                 content: [{
1066 |                   type: "text",
1067 |                   text: JSON.stringify({ success: true, message: `Deleted ${data.length} entities` }, null, 2)
1068 |                 }]
1069 |               };
1070 |               
1071 |             case "relations":
1072 |               // Ensure relations match the Relation interface
1073 |               const typedRelations: Relation[] = data.map((r: any) => ({
1074 |                 from: r.from,
1075 |                 to: r.to,
1076 |                 relationType: r.relationType
1077 |               }));
1078 |               await knowledgeGraphManager.deleteRelations(typedRelations);
1079 |               return {
1080 |                 content: [{
1081 |                   type: "text",
1082 |                   text: JSON.stringify({ success: true, message: `Deleted ${data.length} relations` }, null, 2)
1083 |                 }]
1084 |               };
1085 |               
1086 |             case "observations":
1087 |               // Ensure deletions match the required interface
1088 |               const typedDeletions: { entityName: string; observations: string[] }[] = data.map((d: any) => ({
1089 |                 entityName: d.entityName,
1090 |                 observations: d.observations
1091 |               }));
1092 |               await knowledgeGraphManager.deleteObservations(typedDeletions);
1093 |               return {
1094 |                 content: [{
1095 |                   type: "text",
1096 |                   text: JSON.stringify({ success: true, message: `Deleted observations from ${data.length} entities` }, null, 2)
1097 |                 }]
1098 |               };
1099 |               
1100 |             default:
1101 |               throw new Error(`Invalid type: ${type}. Must be 'entities', 'relations', or 'observations'.`);
1102 |           }
1103 |         } catch (error) {
1104 |           return {
1105 |             content: [{
1106 |               type: "text",
1107 |               text: JSON.stringify({ 
1108 |                 success: false,
1109 |                 error: error instanceof Error ? error.message : String(error)
1110 |               }, null, 2)
1111 |             }]
1112 |           };
1113 |         }
1114 |       }
1115 |     );
1116 | 
1117 |     /**
1118 |      * Get information about the graph, search for nodes, open nodes, get related entities, get decision history, and get milestone progress.
1119 |      */
1120 |     server.tool(
1121 |       "advancedcontext",
1122 |       toolDescriptions["advancedcontext"],
1123 |       {
1124 |         type: z.enum(["graph", "search", "nodes", "related", "decisions", "milestone"]).describe("Type of get operation: 'graph', 'search', 'nodes', 'related', 'decisions', or 'milestone'"),
1125 |         params: z.record(z.string(), z.any()).describe("Parameters for the operation, structure varies by type")
1126 |       },
1127 |       async ({ type, params }) => {
1128 |         try {
1129 |           let result;
1130 |           
1131 |           switch (type) {
1132 |             case "graph":
1133 |               result = await knowledgeGraphManager.readGraph();
1134 |               return {
1135 |                 content: [{
1136 |                   type: "text",
1137 |                   text: JSON.stringify({ success: true, graph: result }, null, 2)
1138 |                 }]
1139 |               };
1140 |               
1141 |             case "search":
1142 |               result = await knowledgeGraphManager.searchNodes(params.query);
1143 |               return {
1144 |                 content: [{
1145 |                   type: "text",
1146 |                   text: JSON.stringify({ success: true, results: result }, null, 2)
1147 |                 }]
1148 |               };
1149 |               
1150 |             case "nodes":
1151 |               result = await knowledgeGraphManager.openNodes(params.names);
1152 |               return {
1153 |                 content: [{
1154 |                   type: "text",
1155 |                   text: JSON.stringify({ success: true, nodes: result }, null, 2)
1156 |                 }]
1157 |               };
1158 |               
1159 |             case "related":
1160 |               result = await knowledgeGraphManager.getRelatedEntities(params.entityName, params.relationTypes);
1161 |               return {
1162 |                 content: [{
1163 |                   type: "text",
1164 |                   text: JSON.stringify({ success: true, entities: result }, null, 2)
1165 |                 }]
1166 |               };
1167 |               
1168 |             case "decisions":
1169 |               result = await knowledgeGraphManager.getDecisionHistory(params.projectName);
1170 |               return {
1171 |                 content: [{
1172 |                   type: "text",
1173 |                   text: JSON.stringify({ success: true, decisions: result }, null, 2)
1174 |                 }]
1175 |               };
1176 |               
1177 |             case "milestone":
1178 |               result = await knowledgeGraphManager.getMilestoneProgress(params.milestoneName);
1179 |               return {
1180 |                 content: [{
1181 |                   type: "text",
1182 |                   text: JSON.stringify({ success: true, progress: result }, null, 2)
1183 |                 }]
1184 |               };
1185 |               
1186 |             default:
1187 |               throw new Error(`Invalid type: ${type}. Must be 'graph', 'search', 'nodes', 'related', 'decisions', or 'milestone'.`);
1188 |           }
1189 |         } catch (error) {
1190 |           return {
1191 |             content: [{
1192 |               type: "text",
1193 |               text: JSON.stringify({ 
1194 |                 success: false,
1195 |                 error: error instanceof Error ? error.message : String(error)
1196 |               }, null, 2)
1197 |             }]
1198 |           };
1199 |         }
1200 |       }
1201 |     );
1202 | 
1203 |     /**
1204 |      * Start a new development session. Returns session ID, recent development sessions, active projects, high-priority tasks, and upcoming milestones.
1205 |      * The output allows the user to easily choose what to focus on and which specific context to load.
1206 |      */
1207 |     server.tool(
1208 |       "startsession",
1209 |       toolDescriptions["startsession"],
1210 |       {},
1211 |       async () => {
1212 |         try {
1213 |           // Generate a unique session ID
1214 |           const sessionId = generateSessionId();
1215 |           
1216 |           // Get recent sessions from persistent storage
1217 |           const sessionStates = await loadSessionStates();
1218 | 
1219 |           // Initialize the session state
1220 |           sessionStates.set(sessionId, []);
1221 |           await saveSessionStates(sessionStates);
1222 |           
1223 |           // Convert sessions map to array, sort by date, and take most recent ones
1224 |           const recentSessions = Array.from(sessionStates.entries())
1225 |             .map(([id, stages]) => {
1226 |               // Extract summary data from the first stage (if it exists)
1227 |               const summaryStage = stages.find(s => s.stage === "summary");
1228 |               return {
1229 |                 id,
1230 |                 project: summaryStage?.stageData?.project || "Unknown project",
1231 |                 focus: summaryStage?.stageData?.focus || "Unknown focus",
1232 |                 summary: summaryStage?.stageData?.summary || "No summary available"
1233 |               };
1234 |             })
1235 |             .slice(0, 3); // Default to showing 3 recent sessions
1236 |           
1237 |           // Get active development projects
1238 |           const graph = await knowledgeGraphManager.readGraph();
1239 |           const activeProjects = [];
1240 |           
1241 |           // Find projects with active status
1242 |           for (const entity of graph.entities) {
1243 |             if (entity.entityType === 'project') {
1244 |               const status = await knowledgeGraphManager.getEntityStatus(entity.name);
1245 |               if (status === 'active') {
1246 |                 activeProjects.push(entity);
1247 |               }
1248 |             }
1249 |           }
1250 |           
1251 |           // Get high-priority development tasks
1252 |           const highPriorityTasks = [];
1253 |           
1254 |           // Find tasks with high priority and active status
1255 |           for (const entity of graph.entities) {
1256 |             if (entity.entityType === 'task') {
1257 |               const status = await knowledgeGraphManager.getEntityStatus(entity.name);
1258 |               const priority = await knowledgeGraphManager.getEntityPriority(entity.name);
1259 |               
1260 |               if (status === 'active' && priority === 'high') {
1261 |                 highPriorityTasks.push(entity);
1262 |               }
1263 |             }
1264 |           }
1265 |           
1266 |           // Get upcoming milestones
1267 |           const upcomingMilestones = [];
1268 |           
1269 |           // Find milestones with active status
1270 |           for (const entity of graph.entities) {
1271 |             if (entity.entityType === 'milestone') {
1272 |               const status = await knowledgeGraphManager.getEntityStatus(entity.name);
1273 |               
1274 |               if (status === 'active') {
1275 |                 upcomingMilestones.push(entity);
1276 |               }
1277 |             }
1278 |           }
1279 |           
1280 |           let sessionsText = "No recent sessions found.";
1281 |           if (recentSessions.length > 0) {
1282 |             sessionsText = recentSessions.map(session => 
1283 |               `- ${session.project} - ${session.focus} - ${session.summary.substring(0, 100)}${session.summary.length > 100 ? '...' : ''}`
1284 |             ).join('\n');
1285 |           }
1286 |           
1287 |           let projectsText = "No active projects found.";
1288 |           if (activeProjects.length > 0) {
1289 |             projectsText = activeProjects.map(project => {
1290 |               const obsPreview = project.observations.length > 0 ? 
1291 |                 `: ${project.observations[0].substring(0, 60)}${project.observations[0].length > 60 ? '...' : ''}` : '';
1292 |               return `- ${project.name}${obsPreview}`;
1293 |             }).join('\n');
1294 |           }
1295 |           
1296 |           let tasksText = "No high-priority tasks found.";
1297 |           if (highPriorityTasks.length > 0) {
1298 |             tasksText = highPriorityTasks.map(task => {
1299 |               const obsPreview = task.observations.length > 0 ? 
1300 |                 `: ${task.observations[0].substring(0, 60)}${task.observations[0].length > 60 ? '...' : ''}` : '';
1301 |               return `- ${task.name}${obsPreview}`;
1302 |             }).join('\n');
1303 |           }
1304 |           
1305 |           let milestonesText = "No upcoming milestones found.";
1306 |           if (upcomingMilestones.length > 0) {
1307 |             milestonesText = upcomingMilestones.map(milestone => {
1308 |               const obsPreview = milestone.observations.length > 0 ? 
1309 |                 `: ${milestone.observations[0].substring(0, 60)}${milestone.observations[0].length > 60 ? '...' : ''}` : '';
1310 |               return `- ${milestone.name}${obsPreview}`;
1311 |             }).join('\n');
1312 |           }
1313 |           
1314 |           return {
1315 |             content: [{
1316 |               type: "text",
1317 |               text: `# Ask user to choose what to focus on in this session. Present the following options:
1318 | 
1319 | ## Recent Development Sessions
1320 | ${sessionsText}
1321 | 
1322 | ## Active Projects
1323 | ${projectsText}
1324 | 
1325 | ## High-Priority Tasks
1326 | ${tasksText}
1327 | 
1328 | ## Upcoming Milestones
1329 | ${milestonesText}
1330 | 
1331 | To load specific context based on the user's choice, use the \`loadcontext\` tool with the entity name and developer session ID - ${sessionId}.`
1332 |             }]
1333 |           };
1334 |         } catch (error) {
1335 |           return {
1336 |             content: [{
1337 |               type: "text",
1338 |               text: JSON.stringify({ 
1339 |                 success: false,
1340 |                 error: error instanceof Error ? error.message : String(error)
1341 |               }, null, 2)
1342 |             }]
1343 |           };
1344 |         }
1345 |       }
1346 |     );
1347 | 
1348 |     /**
1349 |      * Load the context for a specific entity.
1350 |      * Valid entity types are: project, component, task, issue, milestone, decision, feature, technology, documentation, dependency.
1351 |      */
1352 |     server.tool(
1353 |       "loadcontext",
1354 |       toolDescriptions["loadcontext"],
1355 |       {
1356 |         entityName: z.string(),
1357 |         entityType: z.string().optional(),
1358 |         sessionId: z.string().optional() 
1359 |       },
1360 |       async ({ entityName, entityType = "project", sessionId }) => {
1361 |         try {
1362 |           // Validate session if ID is provided
1363 |           if (sessionId) {
1364 |             const sessionStates = await loadSessionStates();
1365 |             if (!sessionStates.has(sessionId)) {
1366 |               console.warn(`Warning: Session ${sessionId} not found, but proceeding with context load`);
1367 |               // Initialize it anyway for more robustness
1368 |               sessionStates.set(sessionId, []);
1369 |               await saveSessionStates(sessionStates);
1370 |             }
1371 |             
1372 |             // Track that this entity was loaded in this session
1373 |             const sessionState = sessionStates.get(sessionId) || [];
1374 |             const loadEvent = {
1375 |               type: 'context_loaded',
1376 |               timestamp: new Date().toISOString(),
1377 |               entityName,
1378 |               entityType
1379 |             };
1380 |             sessionState.push(loadEvent);
1381 |             sessionStates.set(sessionId, sessionState);
1382 |             await saveSessionStates(sessionStates);
1383 |           }
1384 |           
1385 |           // Get entity
1386 |           const entityGraph = await knowledgeGraphManager.searchNodes(entityName);
1387 |           if (entityGraph.entities.length === 0) {
1388 |             throw new Error(`Entity ${entityName} not found`);
1389 |           }
1390 |           
1391 |           // Find the exact entity by name (case-sensitive match)
1392 |           const entity = entityGraph.entities.find(e => e.name === entityName);
1393 |           if (!entity) {
1394 |             throw new Error(`Entity ${entityName} not found`);
1395 |           }
1396 |           
1397 |           // Get status and priority
1398 |           const status = await knowledgeGraphManager.getEntityStatus(entityName) || "unknown";
1399 |           const priority = await knowledgeGraphManager.getEntityPriority(entityName);
1400 |           
1401 |           // Format observations for display (show all observations)
1402 |           const observationsList = entity.observations.length > 0 
1403 |             ? entity.observations.map(obs => `- ${obs}`).join("\n")
1404 |             : "No observations";
1405 |           
1406 |           // Different context loading based on entity type
1407 |           let contextMessage = "";
1408 |           
1409 |           if (entityType === "project") {
1410 |             // Get project status
1411 |             const projectStatus = await knowledgeGraphManager.getProjectStatus(entityName);
1412 |             
1413 |             // Format project context
1414 |             const componentsText = projectStatus.components?.map((component: Entity) => {
1415 |               return `- **${component.name}**${component.observations.length > 0 ? `: ${component.observations[0]}` : ''}`;
1416 |             }).join("\n") || "No components found";
1417 |             
1418 |             const featuresText = projectStatus.activeFeatures?.map((feature: Entity) => {
1419 |               const featureStatus = projectStatus.statuses[feature.name] || "unknown";
1420 |               return `- **${feature.name}** (${featureStatus})${feature.observations.length > 0 ? `: ${feature.observations.join(', ')}` : ''}`;
1421 |             }).join("\n") || "No active features found";
1422 |             
1423 |             const tasksText = projectStatus.activeTasks?.map((task: Entity) => {
1424 |               const taskStatus = projectStatus.statuses[task.name] || "unknown";
1425 |               const taskPriority = projectStatus.priorities[task.name] || "normal";
1426 |               return `- **${task.name}** (${taskStatus}, ${taskPriority} priority)${task.observations.length > 0 ? `: ${task.observations.join(', ')}` : ''}`;
1427 |             }).join("\n") || "No active tasks found";
1428 |             
1429 |             const issuesText = projectStatus.activeIssues?.map((issue: Entity) => {
1430 |               const issueStatus = projectStatus.statuses[issue.name] || "unknown";
1431 |               return `- **${issue.name}** (${issueStatus})${issue.observations.length > 0 ? `: ${issue.observations.join(', ')}` : ''}`;
1432 |             }).join("\n") || "No active issues found";
1433 |             
1434 |             const milestonesText = projectStatus.upcomingMilestones?.map((milestone: Entity) => {
1435 |               const milestoneStatus = projectStatus.statuses[milestone.name] || "unknown";
1436 |               return `- **${milestone.name}** (${milestoneStatus})${milestone.observations.length > 0 ? `: ${milestone.observations.join(', ')}` : ''}`;
1437 |             }).join("\n") || "No upcoming milestones found";
1438 |             
1439 |             const decisionsText = projectStatus.recentDecisions?.map((decision: Entity) => {
1440 |               return `- **${decision.name}**${decision.observations.length > 0 ? `: ${decision.observations.join(', ')}` : ''}`;
1441 |             }).join("\n") || "No recent decisions";
1442 |             
1443 |             // Task sequencing information
1444 |             const sequencingText = Object.keys(projectStatus.taskSequencing || {}).length > 0
1445 |               ? Object.entries(projectStatus.taskSequencing).map(([taskName, sequence]: [string, any]) => {
1446 |                   return `- **${taskName}**:\n  - Precedes: ${sequence.followingTasks.length > 0 ? sequence.followingTasks.join(', ') : 'None'}\n  - Follows: ${sequence.precedingTasks.length > 0 ? sequence.precedingTasks.join(', ') : 'None'}`;
1447 |                 }).join("\n")
1448 |               : "No task sequencing information available";
1449 |             
1450 |             contextMessage = `# Software Development Project Context: ${entityName}
1451 | 
1452 | ## Project Overview
1453 | - **Status**: ${status}
1454 | - **Priority**: ${priority || "N/A"}
1455 | 
1456 | ## Observations
1457 | ${observationsList}
1458 | 
1459 | ## Components
1460 | ${componentsText}
1461 | 
1462 | ## Active Features
1463 | ${featuresText}
1464 | 
1465 | ## Active Tasks
1466 | ${tasksText}
1467 | 
1468 | ## Active Issues
1469 | ${issuesText}
1470 | 
1471 | ## Upcoming Milestones
1472 | ${milestonesText}
1473 | 
1474 | ## Recent Decisions
1475 | ${decisionsText}
1476 | 
1477 | ## Task Sequencing
1478 | ${sequencingText}`;
1479 |           } 
1480 |           else if (entityType === "component") {
1481 |             // Get component context
1482 |             const componentContext = await knowledgeGraphManager.getComponentContext(entityName);
1483 |             
1484 |             const projectsText = componentContext.projects?.map((project: Entity) => {
1485 |               return `- **${project.name}**${project.observations.length > 0 ? `: ${project.observations.join(', ')}` : ''}`;
1486 |             }).join("\n") || "No parent projects found";
1487 |             
1488 |             const featuresText = componentContext.features?.map((feature: Entity) => {
1489 |               const featureStatus = componentContext.statuses[feature.name] || "unknown";
1490 |               return `- **${feature.name}** (${featureStatus})${feature.observations.length > 0 ? `: ${feature.observations.join(', ')}` : ''}`;
1491 |             }).join("\n") || "No implemented features found";
1492 |             
1493 |             const technologiesText = componentContext.technologies?.map((tech: Entity) => {
1494 |               return `- **${tech.name}**${tech.observations.length > 0 ? `: ${tech.observations.join(', ')}` : ''}`;
1495 |             }).join("\n") || "No technologies specified";
1496 |             
1497 |             const issuesText = componentContext.activeIssues?.map((issue: Entity) => {
1498 |               const issueStatus = componentContext.statuses[issue.name] || "unknown";
1499 |               return `- **${issue.name}** (${issueStatus})${issue.observations.length > 0 ? `: ${issue.observations.join(', ')}` : ''}`;
1500 |             }).join("\n") || "No active issues found";
1501 |             
1502 |             const dependenciesText = componentContext.dependencies?.map((dep: Entity) => {
1503 |               return `- **${dep.name}** (${dep.entityType})${dep.observations.length > 0 ? `: ${dep.observations.join(', ')}` : ''}`;
1504 |             }).join("\n") || "No dependencies found";
1505 |             
1506 |             const documentationText = componentContext.documentation?.map((doc: Entity) => {
1507 |               return `- **${doc.name}**${doc.observations.length > 0 ? `: ${doc.observations.join(', ')}` : ''}`;
1508 |             }).join("\n") || "No documentation found";
1509 |             
1510 |             contextMessage = `# Component Context: ${entityName}
1511 | 
1512 | ## Overview
1513 | - **Status**: ${status}
1514 | - **Priority**: ${priority || "N/A"}
1515 | 
1516 | ## Observations
1517 | ${observationsList}
1518 | 
1519 | ## Part of Projects
1520 | ${projectsText}
1521 | 
1522 | ## Technologies
1523 | ${technologiesText}
1524 | 
1525 | ## Implemented Features
1526 | ${featuresText}
1527 | 
1528 | ## Dependencies
1529 | ${dependenciesText}
1530 | 
1531 | ## Active Issues
1532 | ${issuesText}
1533 | 
1534 | ## Documentation
1535 | ${documentationText}`;
1536 |           }
1537 |           else if (entityType === "feature") {
1538 |             // Get related entities
1539 |             const relatedEntities = await knowledgeGraphManager.getRelatedEntities(entityName);
1540 |             
1541 |             // Find implementing components
1542 |             const implementingComponents = relatedEntities.incomingRelations
1543 |               .filter((rel: { relation: Relation; source: Entity }) => rel.relation.relationType === "implements")
1544 |               .map((rel: { relation: Relation; source: Entity }) => rel.source);
1545 |             
1546 |             const componentsText = implementingComponents.map((component: Entity) => {
1547 |               return `- **${component.name}**${component.observations.length > 0 ? `: ${component.observations.join(', ')}` : ''}`;
1548 |             }).join("\n") || "No implementing components found";
1549 |             
1550 |             // Find related tasks
1551 |             const relatedTasks = [...relatedEntities.incomingRelations, ...relatedEntities.outgoingRelations]
1552 |               .filter((rel: { relation: Relation; source?: Entity; target?: Entity }) => 
1553 |                 rel.relation.relationType === "related_to" && 
1554 |                 (rel.source?.entityType === "task" || rel.target?.entityType === "task")
1555 |               )
1556 |               .map((rel: { relation: Relation; source?: Entity; target?: Entity }) => 
1557 |                 rel.source?.entityType === "task" ? rel.source : rel.target
1558 |               )
1559 |               .filter((entity): entity is Entity => entity !== undefined);
1560 |             
1561 |             // Get status for each task
1562 |             const taskStatuses: Record<string, string> = {};
1563 |             for (const task of relatedTasks) {
1564 |               const taskStatus = await knowledgeGraphManager.getEntityStatus(task.name);
1565 |               if (taskStatus) {
1566 |                 taskStatuses[task.name] = taskStatus;
1567 |               }
1568 |             }
1569 |             
1570 |             const tasksText = relatedTasks.map((task: Entity) => {
1571 |               const taskStatus = taskStatuses[task.name] || "unknown";
1572 |               return `- **${task.name}** (${taskStatus})${task.observations.length > 0 ? `: ${task.observations.join(', ')}` : ''}`;
1573 |             }).join("\n") || "No related tasks found";
1574 |             
1575 |             // Find requirements
1576 |             const requirements = relatedEntities.incomingRelations
1577 |               .filter((rel: { relation: Relation; source: Entity }) => rel.relation.relationType === "required_by")
1578 |               .map((rel: { relation: Relation; source: Entity }) => rel.source);
1579 |             
1580 |             const requirementsText = requirements.map((req: Entity) => {
1581 |               return `- **${req.name}**${req.observations.length > 0 ? `: ${req.observations.join(', ')}` : ''}`;
1582 |             }).join("\n") || "No requirements specified";
1583 |             
1584 |             contextMessage = `# Feature Context: ${entityName}
1585 | 
1586 | ## Overview
1587 | - **Status**: ${status}
1588 | - **Priority**: ${priority || "normal"}
1589 | 
1590 | ## Observations
1591 | ${observationsList}
1592 | 
1593 | ## Requirements
1594 | ${requirementsText}
1595 | 
1596 | ## Implementing Components
1597 | ${componentsText}
1598 | 
1599 | ## Related Tasks
1600 | ${tasksText}`;
1601 |           }
1602 |           else if (entityType === "task") {
1603 |             // Get related entities
1604 |             const relatedEntities = await knowledgeGraphManager.getRelatedEntities(entityName);
1605 |             
1606 |             // Find related issues
1607 |             const relatedIssues = relatedEntities.outgoingRelations
1608 |               .filter((rel: { relation: Relation; target: Entity }) => rel.relation.relationType === "resolves")
1609 |               .map((rel: { relation: Relation; target: Entity }) => rel.target);
1610 |             
1611 |             // Get status for each issue
1612 |             const issueStatuses: Record<string, string> = {};
1613 |             for (const issue of relatedIssues) {
1614 |               const issueStatus = await knowledgeGraphManager.getEntityStatus(issue.name);
1615 |               if (issueStatus) {
1616 |                 issueStatuses[issue.name] = issueStatus;
1617 |               }
1618 |             }
1619 |             
1620 |             const issuesText = relatedIssues.map((issue: Entity) => {
1621 |               const issueStatus = issueStatuses[issue.name] || "unknown";
1622 |               return `- **${issue.name}** (${issueStatus})${issue.observations.length > 0 ? `: ${issue.observations.join(', ')}` : ''}`;
1623 |             }).join("\n") || "No related issues found";
1624 |             
1625 |             // Find parent project
1626 |             const parentProjects = relatedEntities.incomingRelations
1627 |               .filter((rel: { relation: Relation; source: Entity }) => rel.relation.relationType === "contains" && rel.source.entityType === "project")
1628 |               .map((rel: { relation: Relation; source: Entity }) => rel.source);
1629 |             
1630 |             const projectName = parentProjects.length > 0 ? parentProjects[0].name : "Unknown project";
1631 |             
1632 |             // Find blocking tasks or issues
1633 |             const blockingItems = relatedEntities.outgoingRelations
1634 |               .filter((rel: { relation: Relation; target: Entity }) => rel.relation.relationType === "blocked_by")
1635 |               .map((rel: { relation: Relation; target: Entity }) => rel.target);
1636 |             
1637 |             // Get status for each blocking item
1638 |             const blockingStatuses: Record<string, string> = {};
1639 |             for (const item of blockingItems) {
1640 |               const itemStatus = await knowledgeGraphManager.getEntityStatus(item.name);
1641 |               if (itemStatus) {
1642 |                 blockingStatuses[item.name] = itemStatus;
1643 |               }
1644 |             }
1645 |             
1646 |             const blockingText = blockingItems.map((item: Entity) => {
1647 |               const itemStatus = blockingStatuses[item.name] || "unknown";
1648 |               return `- **${item.name}** (${item.entityType}, ${itemStatus})${item.observations.length > 0 ? `: ${item.observations.join(', ')}` : ''}`;
1649 |             }).join("\n") || "No blocking items";
1650 |             
1651 |             // Find task sequencing
1652 |             const precedingTasks: string[] = [];
1653 |             const followingTasks: string[] = [];
1654 |             
1655 |             // Get the graph to find sequencing relations
1656 |             const graph = await knowledgeGraphManager.readGraph();
1657 |             
1658 |             for (const relation of graph.relations) {
1659 |               if (relation.from === entityName && relation.relationType === 'precedes') {
1660 |                 followingTasks.push(relation.to);
1661 |               }
1662 |               if (relation.to === entityName && relation.relationType === 'precedes') {
1663 |                 precedingTasks.push(relation.from);
1664 |               }
1665 |             }
1666 |             
1667 |             const sequencingText = `### Preceding Tasks\n${precedingTasks.length > 0 ? precedingTasks.map(t => `- ${t}`).join('\n') : 'None'}\n\n### Following Tasks\n${followingTasks.length > 0 ? followingTasks.map(t => `- ${t}`).join('\n') : 'None'}`;
1668 |             
1669 |             contextMessage = `# Task Context: ${entityName}
1670 | 
1671 | ## Overview
1672 | - **Project**: ${projectName}
1673 | - **Status**: ${status}
1674 | - **Priority**: ${priority || "normal"}
1675 | 
1676 | ## Observations
1677 | ${observationsList}
1678 | 
1679 | ## Related Issues
1680 | ${issuesText}
1681 | 
1682 | ## Blocked By
1683 | ${blockingText}
1684 | 
1685 | ## Task Sequencing
1686 | ${sequencingText}`;
1687 |           }
1688 |           else if (entityType === "milestone") {
1689 |             // Get milestone progress
1690 |             const milestoneProgress = await knowledgeGraphManager.getMilestoneProgress(entityName);
1691 |             
1692 |             contextMessage = `# Milestone Context: ${entityName}
1693 | 
1694 | ## Overview
1695 | - **Status**: ${status}
1696 | - **Progress**: ${milestoneProgress.progress?.percentage || 0}% complete
1697 | - **Complete**: ${milestoneProgress.progress?.complete ? "Yes" : "No"}
1698 | 
1699 | ## Observations
1700 | ${observationsList}
1701 | 
1702 | ## Tasks
1703 | ### Completed (${milestoneProgress.tasks?.completed?.length || 0})
1704 | ${milestoneProgress.tasks?.completed?.map((task: Entity) => {
1705 |   return `- **${task.name}**${task.observations.length > 0 ? `: ${task.observations.join(', ')}` : ''}`;
1706 | }).join("\n") || "No completed tasks"}
1707 | 
1708 | ### In Progress (${milestoneProgress.tasks?.inProgress?.length || 0})
1709 | ${milestoneProgress.tasks?.inProgress?.map((task: Entity) => {
1710 |   return `- **${task.name}**${task.observations.length > 0 ? `: ${task.observations.join(', ')}` : ''}`;
1711 | }).join("\n") || "No in-progress tasks"}
1712 | 
1713 | ### Not Started (${milestoneProgress.tasks?.notStarted?.length || 0})
1714 | ${milestoneProgress.tasks?.notStarted?.map((task: Entity) => {
1715 |   return `- **${task.name}**${task.observations.length > 0 ? `: ${task.observations.join(', ')}` : ''}`;
1716 | }).join("\n") || "No not-started tasks"}
1717 | 
1718 | ## Task Sequencing
1719 | ${Object.keys(milestoneProgress.taskSequencing || {}).length > 0
1720 |   ? Object.entries(milestoneProgress.taskSequencing).map(([taskName, sequence]: [string, any]) => {
1721 |       return `- **${taskName}**:\n  - Precedes: ${sequence.followingTasks.length > 0 ? sequence.followingTasks.join(', ') : 'None'}\n  - Follows: ${sequence.precedingTasks.length > 0 ? sequence.precedingTasks.join(', ') : 'None'}`;
1722 |     }).join("\n")
1723 |   : "No task sequencing information available"}`;
1724 |           }
1725 |           
1726 |           return {
1727 |             content: [{
1728 |               type: "text",
1729 |               text: contextMessage
1730 |             }]
1731 |           };
1732 |         } catch (error) {
1733 |           return {
1734 |             content: [{
1735 |               type: "text",
1736 |               text: JSON.stringify({ 
1737 |                 success: false,
1738 |                 error: error instanceof Error ? error.message : String(error)
1739 |               }, null, 2)
1740 |             }]
1741 |           };
1742 |         }
1743 |       }
1744 |     );
1745 | 
1746 |     // Helper function to process each stage of endsession
1747 |     async function processStage(params: {
1748 |       sessionId: string;
1749 |       stage: string;
1750 |       stageNumber: number;
1751 |       totalStages: number;
1752 |       analysis?: string;
1753 |       stageData?: any;
1754 |       nextStageNeeded: boolean;
1755 |       isRevision?: boolean;
1756 |       revisesStage?: number;
1757 |     }, previousStages: any[]): Promise<any> {
1758 |       // Process based on the stage
1759 |       switch (params.stage) {
1760 |         case "summary":
1761 |           // Process summary stage
1762 |           return {
1763 |             stage: "summary",
1764 |             stageNumber: params.stageNumber,
1765 |             analysis: params.analysis || "",
1766 |             stageData: params.stageData || { 
1767 |               summary: "",
1768 |               duration: "",
1769 |               focus: ""
1770 |             },
1771 |             completed: !params.nextStageNeeded
1772 |           };
1773 |           
1774 |         case "achievements":
1775 |           // Process achievements stage
1776 |           return {
1777 |             stage: "achievements",
1778 |             stageNumber: params.stageNumber,
1779 |             analysis: params.analysis || "",
1780 |             stageData: params.stageData || { achievements: [] },
1781 |             completed: !params.nextStageNeeded
1782 |           };
1783 |           
1784 |         case "taskUpdates":
1785 |           // Process task updates stage
1786 |           return {
1787 |             stage: "taskUpdates",
1788 |             stageNumber: params.stageNumber,
1789 |             analysis: params.analysis || "",
1790 |             stageData: params.stageData || { taskUpdates: [] },
1791 |             completed: !params.nextStageNeeded
1792 |           };
1793 |           
1794 |         case "newTasks":
1795 |           // Process new tasks stage
1796 |           return {
1797 |             stage: "newTasks",
1798 |             stageNumber: params.stageNumber,
1799 |             analysis: params.analysis || "",
1800 |             stageData: params.stageData || { newTasks: [] },
1801 |             completed: !params.nextStageNeeded
1802 |           };
1803 |           
1804 |         case "projectStatus":
1805 |           // Process project status stage
1806 |           return {
1807 |             stage: "projectStatus",
1808 |             stageNumber: params.stageNumber,
1809 |             analysis: params.analysis || "",
1810 |             stageData: params.stageData || { 
1811 |               projectName: "",
1812 |               projectStatus: "",
1813 |               projectObservation: ""
1814 |             },
1815 |             completed: !params.nextStageNeeded
1816 |           };
1817 |           
1818 |         case "assembly":
1819 |           // Final assembly stage - compile all arguments for end session
1820 |           return {
1821 |             stage: "assembly",
1822 |             stageNumber: params.stageNumber,
1823 |             analysis: "Final assembly of endsession arguments",
1824 |             stageData: assembleEndSessionArgs(previousStages),
1825 |             completed: true
1826 |           };
1827 |           
1828 |         default:
1829 |           throw new Error(`Unknown stage: ${params.stage}`);
1830 |       }
1831 |     }
1832 | 
1833 |     // Helper function to assemble the final end session arguments
1834 |     function assembleEndSessionArgs(stages: any[]): any {
1835 |       const summaryStage = stages.find(s => s.stage === "summary");
1836 |       const achievementsStage = stages.find(s => s.stage === "achievements");
1837 |       const taskUpdatesStage = stages.find(s => s.stage === "taskUpdates");
1838 |       const newTasksStage = stages.find(s => s.stage === "newTasks");
1839 |       const projectStatusStage = stages.find(s => s.stage === "projectStatus");
1840 |       
1841 |       return {
1842 |         summary: summaryStage?.stageData?.summary || "",
1843 |         duration: summaryStage?.stageData?.duration || "unknown",
1844 |         focus: summaryStage?.stageData?.focus || "",
1845 |         achievements: JSON.stringify(achievementsStage?.stageData?.achievements || []),
1846 |         taskUpdates: JSON.stringify(taskUpdatesStage?.stageData?.taskUpdates || []),
1847 |         projectName: projectStatusStage?.stageData?.projectName || "",
1848 |         projectStatus: projectStatusStage?.stageData?.projectStatus || "",
1849 |         projectObservation: projectStatusStage?.stageData?.projectObservation || "",
1850 |         newTasks: JSON.stringify(newTasksStage?.stageData?.newTasks || [])
1851 |       };
1852 |     }
1853 | 
1854 |     /**
1855 |      * End session by processing all stages and recording the final results.
1856 |      * Only use this tool if the user asks for it.
1857 |      * 
1858 |      * Usage examples:
1859 |      * 
1860 |      * 1. Starting the end session process with the summary stage:
1861 |      * {
1862 |      *   "sessionId": "dev_1234567890_abc123",  // From startsession
1863 |      *   "stage": "summary",
1864 |      *   "stageNumber": 1,
1865 |      *   "totalStages": 6,  // Total stages you plan to use
1866 |      *   "analysis": "Analyzed progress on the authentication system",
1867 |      *   "stageData": {
1868 |      *     "summary": "Completed the login functionality and fixed related bugs",
1869 |      *     "duration": "3 hours",
1870 |      *     "focus": "AuthSystem"  // Project/component name
1871 |      *   },
1872 |      *   "nextStageNeeded": true,  // More stages coming
1873 |      *   "isRevision": false
1874 |      * }
1875 |      * 
1876 |      * 2. Middle stage for achievements:
1877 |      * {
1878 |      *   "sessionId": "dev_1234567890_abc123",
1879 |      *   "stage": "achievements",
1880 |      *   "stageNumber": 2,
1881 |      *   "totalStages": 6,
1882 |      *   "analysis": "Listed key accomplishments",
1883 |      *   "stageData": {
1884 |      *     "achievements": [
1885 |      *       "Implemented password reset functionality",
1886 |      *       "Fixed login redirect bug",
1887 |      *       "Added error handling for authentication failures"
1888 |      *     ]
1889 |      *   },
1890 |      *   "nextStageNeeded": true,
1891 |      *   "isRevision": false
1892 |      * }
1893 |      * 
1894 |      * 3. Final assembly stage:
1895 |      * {
1896 |      *   "sessionId": "dev_1234567890_abc123",
1897 |      *   "stage": "assembly",
1898 |      *   "stageNumber": 6,
1899 |      *   "totalStages": 6,
1900 |      *   "nextStageNeeded": false,  // This completes the session
1901 |      *   "isRevision": false
1902 |      * }
1903 |      */
1904 |     server.tool(
1905 |       "endsession",
1906 |       toolDescriptions["endsession"],
1907 |       {
1908 |         sessionId: z.string().describe("The unique session identifier obtained from startsession"),
1909 |         stage: z.string().describe("Current stage of analysis: 'summary', 'achievements', 'taskUpdates', 'newTasks', 'projectStatus', or 'assembly'"),
1910 |         stageNumber: z.number().int().positive().describe("The sequence number of the current stage (starts at 1)"),
1911 |         totalStages: z.number().int().positive().describe("Total number of stages in the workflow (typically 6 for standard workflow)"),
1912 |         analysis: z.string().optional().describe("Text analysis or observations for the current stage"),
1913 |         stageData: z.record(z.string(), z.any()).optional().describe(`Stage-specific data structure - format depends on the stage type:
1914 |         - For 'summary' stage: { summary: "Session summary text", duration: "2 hours", focus: "ProjectName" }
1915 |         - For 'achievements' stage: { achievements: ["Implemented feature X", "Fixed bug Y", "Refactored component Z"] }
1916 |         - For 'taskUpdates' stage: { taskUpdates: [{ name: "Task1", status: "completed" }, { name: "Task2", status: "in_progress" }] }
1917 |         - For 'newTasks' stage: { newTasks: [{ name: "NewTask1", description: "Implement feature A", priority: "high" }] }
1918 |         - For 'projectStatus' stage: { projectName: "ProjectName", projectStatus: "in_progress", projectObservation: "Making good progress" }
1919 |         - For 'assembly' stage: no stageData needed - automatic assembly of previous stages`),
1920 |         nextStageNeeded: z.boolean().describe("Whether additional stages are needed after this one (false for final stage)"),
1921 |         isRevision: z.boolean().optional().describe("Whether this is revising a previous stage"),
1922 |         revisesStage: z.number().int().positive().optional().describe("If revising, which stage number is being revised")
1923 |       },
1924 |       async (params) => {
1925 |         try {
1926 |           
1927 |           // Load session states from persistent storage
1928 |           const sessionStates = await loadSessionStates();
1929 |           
1930 |           // Validate session ID
1931 |           if (!sessionStates.has(params.sessionId)) {
1932 |             return {
1933 |               content: [{
1934 |                 type: "text",
1935 |                 text: JSON.stringify({ 
1936 |                   success: false,
1937 |                   error: `Session with ID ${params.sessionId} not found. Please start a new session with startsession.`
1938 |                 }, null, 2)
1939 |               }]
1940 |             };
1941 |           }
1942 |           
1943 |           // Get or initialize session state
1944 |           let sessionState = sessionStates.get(params.sessionId) || [];
1945 |           
1946 |           // Process the current stage
1947 |           const stageResult = await processStage(params, sessionState);
1948 |           
1949 |           // Store updated state
1950 |           if (params.isRevision && params.revisesStage) {
1951 |             // Find the analysis stages in the session state
1952 |             const analysisStages = sessionState.filter(item => item.type === 'analysis_stage') || [];
1953 |             
1954 |             if (params.revisesStage <= analysisStages.length) {
1955 |               // Replace the revised stage
1956 |               analysisStages[params.revisesStage - 1] = {
1957 |                 type: 'analysis_stage',
1958 |                 ...stageResult
1959 |               };
1960 |             } else {
1961 |               // Add as a new stage
1962 |               analysisStages.push({
1963 |                 type: 'analysis_stage',
1964 |                 ...stageResult
1965 |               });
1966 |             }
1967 |             
1968 |             // Update the session state with the modified analysis stages
1969 |             sessionState = [
1970 |               ...sessionState.filter(item => item.type !== 'analysis_stage'),
1971 |               ...analysisStages
1972 |             ];
1973 |           } else {
1974 |             // Add new stage
1975 |             sessionState.push({
1976 |               type: 'analysis_stage',
1977 |               ...stageResult
1978 |             });
1979 |           }
1980 |           
1981 |           // Update in-memory and persistent storage
1982 |           sessionStates.set(params.sessionId, sessionState);
1983 |           await saveSessionStates(sessionStates);
1984 |           
1985 |           // Check if this is the final assembly stage and no more stages are needed
1986 |           if (params.stage === "assembly" && !params.nextStageNeeded) {
1987 |             // Get the assembled arguments
1988 |             const args = stageResult.stageData;
1989 |             
1990 |             try {
1991 |               // Parse arguments
1992 |               const summary = args.summary;
1993 |               const duration = args.duration;
1994 |               const focus = args.focus;
1995 |               const achievements = args.achievements ? JSON.parse(args.achievements) : [];
1996 |               const taskUpdates = args.taskUpdates ? JSON.parse(args.taskUpdates) : [];
1997 |               const projectUpdate = {
1998 |                 name: args.projectName,
1999 |                 status: args.projectStatus,
2000 |                 observation: args.projectObservation
2001 |               };
2002 |               const newTasks = args.newTasks ? JSON.parse(args.newTasks) : [];
2003 |               
2004 |               // 2. Create achievement entities and link to focus project
2005 |               const achievementEntities = achievements.map((achievement: string, i: number) => ({
2006 |                 name: `Achievement_${new Date().getTime()}_${i + 1}`,
2007 |                 entityType: "decision",
2008 |                 observations: [achievement]
2009 |               }));
2010 |               
2011 |               if (achievementEntities.length > 0) {
2012 |                 await knowledgeGraphManager.createEntities(achievementEntities);
2013 |                 
2014 |                 // Link achievements to focus project
2015 |                 const achievementRelations = achievementEntities.map((achievement: {name: string}) => ({
2016 |                   from: focus,
2017 |                   to: achievement.name,
2018 |                   relationType: "contains"
2019 |                 }));
2020 |                 
2021 |                 await knowledgeGraphManager.createRelations(achievementRelations);
2022 |               }
2023 |               
2024 |               // 3. Update task statuses
2025 |               for (const task of taskUpdates) {
2026 |                 // First find the task entity
2027 |                 const taskGraph = await knowledgeGraphManager.searchNodes(`name:${task.name}`);
2028 |                 if (taskGraph.entities.length > 0) {
2029 |                   // Update the status observation
2030 |                   const taskEntity = taskGraph.entities[0];
2031 |                   
2032 |                   // Set task status
2033 |                   try {
2034 |                     const statusValue = task.status === "completed" || task.status === "complete" ? "complete" :
2035 |                                        task.status === "in_progress" ? "active" : "inactive";
2036 |                     await knowledgeGraphManager.setEntityStatus(task.name, statusValue);
2037 |                   } catch (error) {
2038 |                     console.error(`Error updating status for task ${task.name}: ${error}`);
2039 |                   }
2040 |                   
2041 |                   // If completed, link to this session
2042 |                   if (task.status === "complete" || task.status === "completed") {
2043 |                     await knowledgeGraphManager.createRelations([{
2044 |                       from: focus,
2045 |                       to: task.name,
2046 |                       relationType: "resolves"
2047 |                     }]);
2048 |                   }
2049 |                 }
2050 |               }
2051 |               
2052 |               // 4. Update project status
2053 |               const projectGraph = await knowledgeGraphManager.searchNodes(`name:${projectUpdate.name}`);
2054 |               if (projectGraph.entities.length > 0) {
2055 |                 const projectEntity = projectGraph.entities[0];
2056 |                 
2057 |                 // Add project observation if specified
2058 |                 if (projectUpdate.observation) {
2059 |                   await knowledgeGraphManager.addObservations([{
2060 |                     entityName: projectUpdate.name,
2061 |                     contents: [projectUpdate.observation]
2062 |                   }]);
2063 |                 }
2064 |                 
2065 |                 // Set project status
2066 |                 try {
2067 |                   const statusValue = projectUpdate.status === "completed" || projectUpdate.status === "complete" ? "complete" :
2068 |                                      projectUpdate.status === "in_progress" || projectUpdate.status === "active" ? "active" : "inactive";
2069 |                   await knowledgeGraphManager.setEntityStatus(projectUpdate.name, statusValue);
2070 |                 } catch (error) {
2071 |                   console.error(`Error updating status for project ${projectUpdate.name}: ${error}`);
2072 |                 }
2073 |               }
2074 |               
2075 |               // 5. Create new tasks
2076 |               if (newTasks && newTasks.length > 0) {
2077 |                 const taskEntities = newTasks.map((task: {name: string, description: string, priority?: string, precedes?: string, follows?: string}, i: number) => ({
2078 |                   name: task.name,
2079 |                   entityType: "task",
2080 |                   observations: [
2081 |                     task.description
2082 |                   ]
2083 |                 }));
2084 |                 
2085 |                 await knowledgeGraphManager.createEntities(taskEntities);
2086 |                 
2087 |                 // Set status, priority, and sequencing for each task
2088 |                 for (const task of newTasks) {
2089 |                   // Set task status to active by default
2090 |                   try {
2091 |                     await knowledgeGraphManager.setEntityStatus(task.name, "active");
2092 |                   } catch (error) {
2093 |                     console.error(`Error setting status for new task ${task.name}: ${error}`);
2094 |                   }
2095 |                   
2096 |                   // Set task priority if specified
2097 |                   if (task.priority) {
2098 |                     try {
2099 |                       const priorityValue = task.priority.toLowerCase() === "high" ? "high" : "low";
2100 |                       await knowledgeGraphManager.setEntityPriority(task.name, priorityValue);
2101 |                     } catch (error) {
2102 |                       console.error(`Error setting priority for new task ${task.name}: ${error}`);
2103 |                     }
2104 |                   }
2105 |                   
2106 |                   // Create sequencing relationships if specified
2107 |                   try {
2108 |                     // This task precedes another task
2109 |                     if (task.precedes) {
2110 |                       await knowledgeGraphManager.createRelations([{
2111 |                         from: task.name,
2112 |                         to: task.precedes,
2113 |                         relationType: "precedes"
2114 |                       }]);
2115 |                     }
2116 |                     
2117 |                     // This task follows another task
2118 |                     if (task.follows) {
2119 |                       await knowledgeGraphManager.createRelations([{
2120 |                         from: task.follows,
2121 |                         to: task.name,
2122 |                         relationType: "precedes"
2123 |                       }]);
2124 |                     }
2125 |                   } catch (error) {
2126 |                     console.error(`Error setting sequencing for task ${task.name}: ${error}`);
2127 |                   }
2128 |                 }
2129 |                 
2130 |                 // Link tasks to project
2131 |                 const taskRelations = taskEntities.map((task: {name: string}) => ({
2132 |                   from: projectUpdate.name,
2133 |                   to: task.name,
2134 |                   relationType: "contains"
2135 |                 }));
2136 |                 
2137 |                 await knowledgeGraphManager.createRelations(taskRelations);
2138 |               }
2139 |               
2140 |               // Record session completion in persistent storage
2141 |               sessionState.push({
2142 |                 type: 'session_completed',
2143 |                 timestamp: new Date().toISOString(),
2144 |                 summary: summary,
2145 |                 project: focus
2146 |               });
2147 |               
2148 |               sessionStates.set(params.sessionId, sessionState);
2149 |               await saveSessionStates(sessionStates);
2150 |               
2151 |               // Prepare the summary message
2152 |               const summaryMessage = `# Development Session Recorded
2153 | 
2154 | I've recorded your development session focusing on ${focus}.
2155 | 
2156 | ## Achievements Documented
2157 | ${achievements.map((a: string) => `- ${a}`).join('\n') || "No achievements recorded."}
2158 | 
2159 | ## Task Updates
2160 | ${taskUpdates.map((t: {name: string, status: string}) => `- ${t.name}: ${t.status}`).join('\n') || "No task updates."}
2161 | 
2162 | ## Project Status
2163 | Project ${projectUpdate.name} has been updated to: ${projectUpdate.status}
2164 | 
2165 | ${newTasks && newTasks.length > 0 ? `## New Tasks Added
2166 | ${newTasks.map((t: {name: string, description: string, priority?: string}) => `- ${t.name}: ${t.description} (Priority: ${t.priority || "medium"})`).join('\n')}` : "No new tasks added."}
2167 | 
2168 | ## Session Summary
2169 | ${summary}
2170 | 
2171 | Would you like me to perform any additional updates to the development knowledge graph?`;
2172 |               
2173 |               // Return the final result with the session recorded message
2174 |               return {
2175 |                 content: [{
2176 |                   type: "text",
2177 |                   text: JSON.stringify({
2178 |                     success: true,
2179 |                     stageCompleted: params.stage,
2180 |                     nextStageNeeded: false,
2181 |                     stageResult: stageResult,
2182 |                     sessionRecorded: true,
2183 |                     summaryMessage: summaryMessage
2184 |                   }, null, 2)
2185 |                 }]
2186 |               };
2187 |             } catch (error) {
2188 |               return {
2189 |                 content: [{
2190 |                   type: "text",
2191 |                   text: JSON.stringify({
2192 |                     success: false,
2193 |                     error: `Error recording development session: ${error instanceof Error ? error.message : String(error)}`
2194 |                   }, null, 2)
2195 |                 }]
2196 |               };
2197 |             }
2198 |           } else {
2199 |             // This is not the final stage or more stages are needed
2200 |             // Return intermediate result
2201 |             return {
2202 |               content: [{
2203 |                 type: "text",
2204 |                 text: JSON.stringify({
2205 |                   success: true,
2206 |                   stageCompleted: params.stage,
2207 |                   nextStageNeeded: params.nextStageNeeded,
2208 |                   stageResult: stageResult,
2209 |                   endSessionArgs: params.stage === "assembly" ? stageResult.stageData : null
2210 |                 }, null, 2)
2211 |               }]
2212 |             };
2213 |           }
2214 |         } catch (error) {
2215 |           return {
2216 |             content: [{
2217 |               type: "text",
2218 |               text: JSON.stringify({ 
2219 |                 success: false,
2220 |                 error: error instanceof Error ? error.message : String(error)
2221 |               }, null, 2)
2222 |             }]
2223 |           };
2224 |         }
2225 |       }
2226 |     );
2227 | 
2228 |     // Connect the server to the transport
2229 |     const transport = new StdioServerTransport();
2230 |     await server.connect(transport);
2231 |   } catch (error) {
2232 |     console.error("Error starting server:", error);
2233 |     process.exit(1);
2234 |   }
2235 | }
2236 | 
2237 | // Run the main function
2238 | main().catch(error => {
2239 |   console.error("Unhandled error:", error);
2240 |   process.exit(1);
2241 | });
2242 | 
2243 | // Export the KnowledgeGraphManager for testing
2244 | export { KnowledgeGraphManager };
```
Page 4/13FirstPrevNextLast