#
tokens: 24504/50000 1/128 files (page 5/13)
lines: on (toggle) GitHub
raw markdown copy reset
This is page 5 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

--------------------------------------------------------------------------------
/developer/index.js:
--------------------------------------------------------------------------------

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