UNPKG

@davenportsociety/clear-thought-patterns

Version:

A Model Context Protocol (MCP) server providing systematic thinking tools, mental models, and debugging approaches for enhanced problem-solving capabilities

1 lines 388 kB
{"version":3,"sources":["../src/index.ts","../src/state/session-registry.ts","../src/state/stores/base-store.ts","../src/state/stores/thought-store.ts","../src/state/stores/mental-model-store.ts","../src/state/stores/debugging-store.ts","../src/state/stores/collaborative-store.ts","../src/state/stores/decision-store.ts","../src/state/stores/metacognitive-store.ts","../src/state/stores/scientific-store.ts","../src/state/stores/creative-store.ts","../src/state/stores/systems-store.ts","../src/state/stores/visual-store.ts","../src/state/stores/stochastic-store.ts","../src/state/stores/planning-verification-store.ts","../src/state/session-state.ts","../src/config.ts","../src/tools/sequential-thinking.ts","../src/tools/mental-model.ts","../src/tools/debugging-approach.ts","../src/tools/collaborative-reasoning.ts","../src/tools/decision-framework.ts","../src/tools/metacognitive.ts","../src/tools/socratic-method.ts","../src/tools/creative-thinking.ts","../src/tools/systems-thinking.ts","../src/tools/scientific-method.ts","../src/tools/assumption-check.ts","../src/tools/structured-argumentation.ts","../src/tools/visual-reasoning.ts","../src/tools/design-pattern.ts","../src/tools/programming-paradigm.ts","../src/tools/stochastic-algorithm.ts","../src/tools/planning-verification.ts","../src/tools/index.ts"],"sourcesContent":["import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'\nimport { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'\nimport { SessionRegistry } from './state/session-registry.js'\nimport { safeParseConfig } from './config.js'\nimport { registerTools } from './tools/index.js'\n\nlet mcpServer: McpServer\n\n/**\n * Main entry point for the MCP Thinking Tools server\n * This server provides 17 consolidated thinking and reasoning tools\n * with session state management capabilities.\n */\n\nasync function main() {\n // Parse configuration from environment variables or use defaults\n const config = safeParseConfig({\n debug: process.env.DEBUG === 'true',\n maxThoughtsPerSession: process.env.MAX_THOUGHTS_PER_SESSION\n ? parseInt(process.env.MAX_THOUGHTS_PER_SESSION, 10)\n : undefined,\n sessionTimeout: process.env.SESSION_TIMEOUT\n ? parseInt(process.env.SESSION_TIMEOUT, 10)\n : undefined,\n enableMetrics: process.env.ENABLE_METRICS === 'true',\n })\n\n // Create MCP server instance with comprehensive options\n mcpServer = new McpServer(\n {\n name: 'clear-thought-patterns',\n version: '1.2.0',\n },\n {\n instructions: `\n## Clear Thought Patterns - MCP Thinking Tools\n\nThis server provides 16 systematic thinking tools, mental models, and analytical frameworks for enhanced problem-solving, decision-making, and cognitive analysis.\n\n### Core Principles\n- Use these tools when facing complex, multi-faceted problems\n- Each tool is designed for specific types of challenges\n- Tools can be combined for comprehensive analysis\n- All tools maintain session state for iterative refinement\n\n### Tool Categories & Usage Guidelines\n\n#### 🧠 CORE THINKING TOOLS\n\n**Sequential Thinking** - Use for complex multi-step problems requiring careful progression\n- Complex feature planning or architectural decisions\n- System-wide changes with multiple considerations\n- Breaking down overwhelming problems into manageable steps\n- When you need to track and refine your reasoning process\n\n**Mental Models** - Use for initial problem understanding and systematic analysis\n- First principles: Break down to fundamental truths\n- Opportunity cost: Analyze trade-offs between options\n- Error propagation: Understand how failures cascade\n- Rubber duck: Clarify thinking through explanation\n- Pareto principle: Identify high-impact factors\n- Occams razor: Choose simplest viable explanation\n\n**Creative Thinking** - Use for innovation and breaking conventional patterns\n- Generating novel solutions to challenging problems\n- Breaking through mental blocks or design constraints\n- Product development and feature brainstorming\n- Exploring possibilities beyond obvious solutions\n\n**Systems Thinking** - Use for understanding complex interconnected systems\n- Analyzing organizational or technical architectures\n- Identifying root causes in multi-component systems\n- Understanding feedback loops and emergent behaviors\n- Finding leverage points for maximum impact\n\n#### 🔍 ANALYSIS TOOLS\n\n**Debugging Approach** - Use for systematic problem investigation\n- Binary search: Narrow down large problem spaces\n- Reverse engineering: Work backwards from symptoms\n- Divide & conquer: Break complex issues into parts\n- Cause elimination: Rule out potential causes systematically\n\n**Scientific Method** - Use for hypothesis-driven investigation\n- Performance optimization requiring measurement\n- Testing causal relationships and dependencies\n- Validating architectural decisions with data\n- Building evidence-based understanding\n\n**Assumption Test** - Use for quickly validating implementation assumptions\n- Validate inferred codebase patterns or conventions before implementing\n- Run a focused disproof search (code/docs) to find contradicting examples\n- Record evidence and make an explicit go/no-go decision to reduce rework and hidden duplication\n\n**Metacognitive Monitoring** - Use to assess your own thinking quality\n- Evaluating confidence levels in predictions/decisions\n- Detecting potential reasoning biases or blind spots\n- Determining when to seek additional expertise\n- Calibrating confidence in unfamiliar domains\n\n#### 🤝 COLLABORATIVE TOOLS\n\n**Collaborative Reasoning** - Use for multi-perspective analysis\n- Complex decisions requiring diverse expertise\n- Controversial or high-stakes decisions\n- Innovation requiring creative perspectives\n- Risk assessment needing comprehensive analysis\n\n**Socratic Method** - Use for challenging assumptions and deepening understanding\n- Examining beliefs or claims critically\n- Educational exploration of complex topics\n- Challenging conventional wisdom\n- Developing understanding through guided questioning\n\n#### 📊 DECISION TOOLS\n\n**Decision Framework** - Use for structured choice between alternatives\n- Technology selection and architectural decisions\n- Resource allocation and strategic planning\n- Risk-aware decision making with multiple criteria\n- Choices requiring stakeholder justification\n\n**Stochastic Algorithm** - Use for decisions involving uncertainty\n- A/B testing and experimentation strategies\n- Resource allocation with uncertain demands\n- Performance optimization under uncertainty\n- Risk assessment with probabilistic modeling\n\n#### 🏗️ DESIGN TOOLS\n\n**Design Pattern** - Use for implementing proven software solutions\n- Solving recurring architectural challenges\n- Refactoring for better maintainability\n- Managing technical debt and code complexity\n- Ensuring scalable code structure\n\n**Programming Paradigm** - Use for selecting optimal coding approaches\n- Choosing between OOP, functional, reactive approaches\n- Leveraging language-specific strengths\n- Optimizing for specific characteristics (performance, maintainability)\n- Designing systems that leverage paradigm strengths\n\n#### 📈 REASONING TOOLS\n\n**Structured Argumentation** - Use for building logical cases\n- Constructing evidence-based justifications\n- Analyzing logical structure of arguments\n- Evaluating competing positions or viewpoints\n- Building persuasive cases for decisions\n\n**Visual Reasoning** - Use for spatial and diagrammatic analysis\n- System architecture visualization\n- Process flows and workflow mapping\n- Pattern recognition in complex data\n- Spatial problem-solving and geometric reasoning\n\n### Best Practices\n1. **Start Simple**: Begin with Mental Models or Sequential Thinking for problem understanding\n2. **Combine Strategically**: Use multiple tools for comprehensive analysis (e.g., Systems Thinking + Decision Framework)\n3. **Iterate**: Use tools iteratively to refine understanding and solutions\n4. **Match Tool to Problem**: Choose tools based on problem characteristics and desired outcomes\n5. **Document Process**: Tools maintain state to track reasoning development over time\n\n### Tool Integration Patterns\n- **Analysis Phase**: Mental Models → Systems Thinking → Scientific Method\n- **Decision Phase**: Decision Framework → Collaborative Reasoning → Structured Argumentation\n- **Innovation Phase**: Creative Thinking → Design Pattern → Programming Paradigm\n- **Quality Assurance**: Debugging Approach → Metacognitive Monitoring → Socratic Method\n`,\n }\n )\n\n // Create session registry (sessions are created on-demand per-call)\n const sessionRegistry = new SessionRegistry(config)\n\n // Register all tools with the registry (tools will resolve/create per-call sessions)\n registerTools(mcpServer, sessionRegistry)\n\n if (config.debug) {\n console.error(`MCP Thinking Tools server starting with session registry (on-demand sessions)`)\n }\n\n // Create transport and connect\n const transport = new StdioServerTransport()\n await mcpServer.server.connect(transport)\n\n if (config.debug) {\n console.error('MCP Thinking Tools server started successfully')\n console.error(`Session handling: on-demand LLM-provided sessionId (will create uuid fallback)`)\n console.error(`Configuration:`, config)\n }\n}\n\n// Start the server\nmain().catch((error) => {\n console.error('Fatal error starting MCP Thinking Tools server:', error)\n process.exit(1)\n})\n\nprocess.stdin.on('close', async () => {\n console.error('MCP Thinking Tools server closed')\n mcpServer?.close()\n process.exit(0)\n})\n\nprocess.on('SIGINT', async () => {\n console.error('MCP Thinking Tools server shutting down gracefully [SIGINT]')\n mcpServer?.close()\n process.exit(0)\n})\n\nprocess.on('SIGTERM', async () => {\n console.error('MCP Thinking Tools server shutting down gracefully [SIGTERM]')\n mcpServer?.close()\n process.exit(0)\n})\n","import crypto from 'crypto'\nimport type { ServerConfig } from '../config.js'\nimport { SessionState } from './session-state.js'\n\n/**\n * Simple in-memory session registry that maps sessionId -> SessionState\n * If a sessionId is not provided, getOrCreate will generate a UUID and\n * create a new SessionState using the provided server config.\n */\nexport class SessionRegistry {\n private readonly sessions: Map<string, SessionState>\n private readonly config: ServerConfig\n\n constructor(config: ServerConfig) {\n this.sessions = new Map()\n this.config = config\n }\n\n /**\n * Return an existing SessionState for sessionId or create a new one.\n * If sessionId is undefined, a new uuid is generated and returned.\n */\n getOrCreate(sessionId?: string): { session: SessionState; created: boolean } {\n const id =\n sessionId && typeof sessionId === 'string' && sessionId.length > 0\n ? sessionId\n : crypto.randomUUID()\n\n if (this.sessions.has(id)) {\n return { session: this.sessions.get(id) as SessionState, created: false }\n }\n\n const s = new SessionState(id, this.config)\n this.sessions.set(id, s)\n return { session: s, created: true }\n }\n\n /**\n * Optional: get without creating\n */\n get(sessionId: string): SessionState | undefined {\n return this.sessions.get(sessionId)\n }\n\n /**\n * Forcibly remove a session\n */\n remove(sessionId: string): boolean {\n const s = this.sessions.get(sessionId)\n if (s) {\n s.cleanup()\n }\n return this.sessions.delete(sessionId)\n }\n}\n","/**\n * Base abstract class for all data stores in the Clear Thought Patterns MCP server\n *\n * This class provides common functionality for storing and managing\n * different types of thinking session data.\n */\n\n/**\n * Generic base store for managing collections of typed data\n * @template T - The type of data this store manages\n */\nexport abstract class BaseStore<T> {\n /** Internal storage map */\n protected data: Map<string, T>\n\n /** Store name for logging and debugging */\n protected readonly storeName: string\n\n constructor(storeName: string) {\n this.storeName = storeName\n this.data = new Map()\n }\n\n /**\n * Add a new item to the store\n * @param id - Unique identifier for the item\n * @param item - The item to store\n */\n abstract add(id: string, item: T): void\n\n /**\n * Get all items from the store\n * @returns Array of all stored items\n */\n abstract getAll(): T[]\n\n /**\n * Clear all items from the store\n */\n abstract clear(): void\n\n /**\n * Get a specific item by ID\n * @param id - The item's unique identifier\n * @returns The item if found, undefined otherwise\n */\n get(id: string): T | undefined {\n return this.data.get(id)\n }\n\n /**\n * Check if an item exists\n * @param id - The item's unique identifier\n * @returns True if the item exists\n */\n has(id: string): boolean {\n return this.data.has(id)\n }\n\n /**\n * Delete a specific item\n * @param id - The item's unique identifier\n * @returns True if the item was deleted\n */\n delete(id: string): boolean {\n return this.data.delete(id)\n }\n\n /**\n * Get the number of items in the store\n * @returns The count of items\n */\n size(): number {\n return this.data.size\n }\n\n /**\n * Export all data for persistence\n * @returns Serializable representation of the store\n */\n export(): Record<string, T> {\n const result: Record<string, T> = {}\n this.data.forEach((value, key) => {\n result[key] = value\n })\n return result\n }\n\n /**\n * Import data from a serialized representation\n * @param data - The data to import\n */\n import(data: Record<string, T>): void {\n this.clear()\n Object.entries(data).forEach(([key, value]) => {\n this.add(key, value)\n })\n }\n\n /**\n * Get all keys in the store\n * @returns Array of all keys\n */\n keys(): string[] {\n return Array.from(this.data.keys())\n }\n\n /**\n * Get all values in the store\n * @returns Array of all values\n */\n values(): T[] {\n return Array.from(this.data.values())\n }\n\n /**\n * Iterate over all entries\n * @param callback - Function to call for each entry\n */\n forEach(callback: (value: T, key: string) => void): void {\n this.data.forEach(callback)\n }\n\n /**\n * Filter items based on a predicate\n * @param predicate - Function to test each item\n * @returns Array of items that match the predicate\n */\n filter(predicate: (item: T) => boolean): T[] {\n return this.values().filter(predicate)\n }\n\n /**\n * Find the first item matching a predicate\n * @param predicate - Function to test each item\n * @returns The first matching item or undefined\n */\n find(predicate: (item: T) => boolean): T | undefined {\n return this.values().find(predicate)\n }\n\n /**\n * Update an existing item\n * @param id - The item's unique identifier\n * @param updater - Function to update the item\n * @returns True if the item was updated\n */\n update(id: string, updater: (item: T) => T): boolean {\n const item = this.get(id)\n if (item) {\n this.add(id, updater(item))\n return true\n }\n return false\n }\n}\n","/**\n * Store for managing sequential thinking data with branching support\n */\n\nimport { BaseStore } from './base-store.js'\nimport { ThoughtData } from '../../types/index.js'\n\n/**\n * Specialized store for managing thoughts with branching and revision support\n */\nexport class ThoughtStore extends BaseStore<ThoughtData> {\n /** Map of branch IDs to their thoughts */\n private branches: Map<string, ThoughtData[]>\n\n /** Map of thought numbers to their revision history */\n private revisions: Map<number, ThoughtData[]>\n\n constructor() {\n super('ThoughtStore')\n this.branches = new Map()\n this.revisions = new Map()\n }\n\n /**\n * Add a new thought to the store\n * @param id - Unique identifier for the thought\n * @param thought - The thought data to store\n */\n add(id: string, thought: ThoughtData): void {\n this.data.set(id, thought)\n\n // Track branches\n if (thought.branchId) {\n const branchThoughts = this.branches.get(thought.branchId) || []\n branchThoughts.push(thought)\n this.branches.set(thought.branchId, branchThoughts)\n }\n\n // Track revisions\n if (thought.isRevision && thought.revisesThought !== undefined) {\n const revisionHistory = this.revisions.get(thought.revisesThought) || []\n revisionHistory.push(thought)\n this.revisions.set(thought.revisesThought, revisionHistory)\n }\n }\n\n /**\n * Get all thoughts in chronological order\n * @returns Array of all thoughts\n */\n getAll(): ThoughtData[] {\n return Array.from(this.data.values()).sort((a, b) => a.thoughtNumber - b.thoughtNumber)\n }\n\n /**\n * Clear all thoughts and associated data\n */\n clear(): void {\n this.data.clear()\n this.branches.clear()\n this.revisions.clear()\n }\n\n /**\n * Get thoughts for a specific branch\n * @param branchId - The branch identifier\n * @returns Array of thoughts in the branch\n */\n getBranch(branchId: string): ThoughtData[] {\n return this.branches.get(branchId) || []\n }\n\n /**\n * Get all branches\n * @returns Map of branch IDs to their thoughts\n */\n getAllBranches(): Map<string, ThoughtData[]> {\n return new Map(this.branches)\n }\n\n /**\n * Get revision history for a thought\n * @param thoughtNumber - The original thought number\n * @returns Array of revisions for the thought\n */\n getRevisions(thoughtNumber: number): ThoughtData[] {\n return this.revisions.get(thoughtNumber) || []\n }\n\n /**\n * Get the latest thought (highest thought number)\n * @returns The most recent thought or undefined\n */\n getLatest(): ThoughtData | undefined {\n const thoughts = this.getAll()\n return thoughts[thoughts.length - 1]\n }\n\n /**\n * Get thoughts in a specific range\n * @param start - Starting thought number (inclusive)\n * @param end - Ending thought number (inclusive)\n * @returns Array of thoughts in the range\n */\n getRange(start: number, end: number): ThoughtData[] {\n return this.getAll().filter(\n (thought) => thought.thoughtNumber >= start && thought.thoughtNumber <= end\n )\n }\n\n /**\n * Get thoughts that need continuation\n * @returns Array of thoughts where nextThoughtNeeded is true\n */\n getPendingThoughts(): ThoughtData[] {\n return this.filter((thought) => thought.nextThoughtNeeded)\n }\n\n /**\n * Count thoughts by type\n * @returns Object with counts for regular, revision, and branched thoughts\n */\n getStatistics(): {\n total: number\n regular: number\n revisions: number\n branched: number\n branches: number\n } {\n const thoughts = this.getAll()\n return {\n total: thoughts.length,\n regular: thoughts.filter((t) => !t.isRevision && !t.branchId).length,\n revisions: thoughts.filter((t) => t.isRevision).length,\n branched: thoughts.filter((t) => t.branchId).length,\n branches: this.branches.size,\n }\n }\n\n /**\n * Export store data including branch and revision metadata\n */\n export(): Record<string, any> {\n return {\n thoughts: super.export(),\n branches: Object.fromEntries(this.branches),\n revisions: Object.fromEntries(Array.from(this.revisions).map(([k, v]) => [k.toString(), v])),\n }\n }\n\n /**\n * Import store data including branch and revision metadata\n */\n import(data: Record<string, any>): void {\n if (data.thoughts) {\n super.import(data.thoughts)\n }\n\n // Rebuild branch and revision maps\n this.branches.clear()\n this.revisions.clear()\n\n this.data.forEach((thought) => {\n if (thought.branchId) {\n const branchThoughts = this.branches.get(thought.branchId) || []\n branchThoughts.push(thought)\n this.branches.set(thought.branchId, branchThoughts)\n }\n\n if (thought.isRevision && thought.revisesThought !== undefined) {\n const revisionHistory = this.revisions.get(thought.revisesThought) || []\n revisionHistory.push(thought)\n this.revisions.set(thought.revisesThought, revisionHistory)\n }\n })\n }\n}\n","/**\n * Store for managing mental model application data\n */\n\nimport { BaseStore } from './base-store.js'\nimport { MentalModelData } from '../../types/index.js'\n\n/**\n * Specialized store for managing mental model applications\n */\nexport class MentalModelStore extends BaseStore<MentalModelData> {\n /** Map of model names to their applications */\n private modelApplications: Map<string, MentalModelData[]>\n\n constructor() {\n super('MentalModelStore')\n this.modelApplications = new Map()\n }\n\n /**\n * Add a new mental model application\n * @param id - Unique identifier\n * @param model - The mental model data\n */\n add(id: string, model: MentalModelData): void {\n this.data.set(id, model)\n\n // Track by model name\n const applications = this.modelApplications.get(model.modelName) || []\n applications.push(model)\n this.modelApplications.set(model.modelName, applications)\n }\n\n /**\n * Get all mental model applications\n * @returns Array of all applications\n */\n getAll(): MentalModelData[] {\n return Array.from(this.data.values())\n }\n\n /**\n * Clear all data\n */\n clear(): void {\n this.data.clear()\n this.modelApplications.clear()\n }\n\n /**\n * Get applications of a specific model\n * @param modelName - The name of the mental model\n * @returns Array of applications for that model\n */\n getByModel(modelName: MentalModelData['modelName']): MentalModelData[] {\n return this.modelApplications.get(modelName) || []\n }\n\n /**\n * Get all unique problems analyzed\n * @returns Array of unique problem statements\n */\n getUniqueProblems(): string[] {\n const problems = new Set<string>()\n this.data.forEach((model) => problems.add(model.problem))\n return Array.from(problems)\n }\n\n /**\n * Get statistics about model usage\n * @returns Object with comprehensive statistics\n */\n getStatistics(): Record<string, any> {\n const models = this.getAll()\n const modelUsage: Record<string, number> = {}\n\n this.modelApplications.forEach((applications, modelName) => {\n modelUsage[modelName] = applications.length\n })\n\n return {\n count: models.length,\n totalApplications: models.length,\n uniqueModels: this.modelApplications.size,\n uniqueProblems: this.getUniqueProblems().length,\n modelUsage,\n mostUsed: this.getMostUsedModel(),\n }\n }\n\n /**\n * Find models applied to similar problems\n * @param problem - The problem to search for\n * @returns Array of models applied to similar problems\n */\n findSimilarApplications(problem: string): MentalModelData[] {\n const problemLower = problem.toLowerCase()\n return this.filter(\n (model) =>\n model.problem.toLowerCase().includes(problemLower) ||\n problemLower.includes(model.problem.toLowerCase())\n )\n }\n\n /**\n * Get the most frequently used model\n * @returns The model name and count, or undefined\n */\n getMostUsedModel(): { modelName: string; count: number } | undefined {\n let maxCount = 0\n let mostUsed: string | undefined\n\n this.modelApplications.forEach((applications, modelName) => {\n if (applications.length > maxCount) {\n maxCount = applications.length\n mostUsed = modelName\n }\n })\n\n return mostUsed ? { modelName: mostUsed, count: maxCount } : undefined\n }\n}\n","/**\n * Store for managing debugging session data\n */\n\nimport { BaseStore } from './base-store.js'\nimport { DebuggingSession } from '../../types/index.js'\n\n/**\n * Specialized store for managing debugging sessions\n */\nexport class DebuggingStore extends BaseStore<DebuggingSession> {\n /** Map of approach names to their sessions */\n private approachSessions: Map<string, DebuggingSession[]>\n\n /** Map of issue keywords to related sessions */\n private issueIndex: Map<string, Set<string>>\n\n constructor() {\n super('DebuggingStore')\n this.approachSessions = new Map()\n this.issueIndex = new Map()\n }\n\n /**\n * Add a new debugging session\n * @param id - Unique identifier\n * @param session - The debugging session data\n */\n add(id: string, session: DebuggingSession): void {\n this.data.set(id, session)\n\n // Track by approach\n const sessions = this.approachSessions.get(session.approachName) || []\n sessions.push(session)\n this.approachSessions.set(session.approachName, sessions)\n\n // Index by issue keywords\n this.indexIssue(id, session.issue)\n }\n\n /**\n * Index issue keywords for search\n * @param sessionId - Session identifier\n * @param issue - Issue description\n */\n private indexIssue(sessionId: string, issue: string): void {\n // Extract keywords (simple tokenization)\n const keywords = issue\n .toLowerCase()\n .split(/\\s+/)\n .filter((word) => word.length > 3) // Only index words > 3 chars\n\n keywords.forEach((keyword) => {\n const sessions = this.issueIndex.get(keyword) || new Set()\n sessions.add(sessionId)\n this.issueIndex.set(keyword, sessions)\n })\n }\n\n /**\n * Get all debugging sessions\n * @returns Array of all sessions\n */\n getAll(): DebuggingSession[] {\n return Array.from(this.data.values())\n }\n\n /**\n * Clear all data\n */\n clear(): void {\n this.data.clear()\n this.approachSessions.clear()\n this.issueIndex.clear()\n }\n\n /**\n * Get sessions by approach\n * @param approachName - The debugging approach name\n * @returns Array of sessions using that approach\n */\n getByApproach(approachName: DebuggingSession['approachName']): DebuggingSession[] {\n return this.approachSessions.get(approachName) || []\n }\n\n /**\n * Search sessions by issue keywords\n * @param keywords - Keywords to search for\n * @returns Array of matching sessions\n */\n searchByIssue(keywords: string): DebuggingSession[] {\n const searchTerms = keywords.toLowerCase().split(/\\s+/)\n const matchingIds = new Set<string>()\n\n searchTerms.forEach((term) => {\n const sessions = this.issueIndex.get(term)\n if (sessions) {\n sessions.forEach((id) => matchingIds.add(id))\n }\n })\n\n return Array.from(matchingIds)\n .map((id) => this.get(id))\n .filter((session): session is DebuggingSession => session !== undefined)\n }\n\n /**\n * Get successfully resolved sessions\n * @returns Array of sessions with resolutions\n */\n getResolvedSessions(): DebuggingSession[] {\n return this.filter((session) => session.resolution.trim().length > 0)\n }\n\n /**\n * Get statistics about debugging approaches\n * @returns Statistics object\n */\n getStatistics(): {\n totalSessions: number\n resolvedSessions: number\n approachUsage: Record<string, number>\n successRate: number\n } {\n const resolved = this.getResolvedSessions()\n const stats: Record<string, number> = {}\n\n this.approachSessions.forEach((sessions, approach) => {\n stats[approach] = sessions.length\n })\n\n return {\n totalSessions: this.size(),\n resolvedSessions: resolved.length,\n approachUsage: stats,\n successRate: this.size() > 0 ? resolved.length / this.size() : 0,\n }\n }\n\n /**\n * Get the most effective approach based on resolution rate\n * @returns Approach with highest success rate\n */\n getMostEffectiveApproach(): { approach: string; successRate: number } | undefined {\n let bestApproach: string | undefined\n let bestRate = 0\n\n this.approachSessions.forEach((sessions, approach) => {\n const resolved = sessions.filter((s) => s.resolution && s.resolution.trim().length > 0)\n const rate = sessions.length > 0 ? resolved.length / sessions.length : 0\n\n if (rate > bestRate) {\n bestRate = rate\n bestApproach = approach\n }\n })\n\n return bestApproach ? { approach: bestApproach, successRate: bestRate } : undefined\n }\n}\n","/**\n * Store for managing collaborative reasoning sessions\n */\n\nimport { BaseStore } from './base-store.js'\nimport { CollaborativeSession } from '../../types/index.js'\n\n/**\n * Specialized store for managing collaborative reasoning sessions\n */\nexport class CollaborativeStore extends BaseStore<CollaborativeSession> {\n /** Map of topics to their sessions */\n private topicSessions: Map<string, CollaborativeSession[]>\n\n /** Map of persona IDs to their participation sessions */\n private personaParticipation: Map<string, Set<string>>\n\n constructor() {\n super('CollaborativeStore')\n this.topicSessions = new Map()\n this.personaParticipation = new Map()\n }\n\n /**\n * Add a new collaborative session\n * @param id - Unique identifier\n * @param session - The collaborative session data\n */\n add(id: string, session: CollaborativeSession): void {\n this.data.set(id, session)\n\n // Track by topic\n const sessions = this.topicSessions.get(session.topic) || []\n sessions.push(session)\n this.topicSessions.set(session.topic, sessions)\n\n // Track persona participation\n session.personas.forEach((persona) => {\n const participation = this.personaParticipation.get(persona.id) || new Set()\n participation.add(id)\n this.personaParticipation.set(persona.id, participation)\n })\n }\n\n /**\n * Get all collaborative sessions\n * @returns Array of all sessions\n */\n getAll(): CollaborativeSession[] {\n return Array.from(this.data.values())\n }\n\n /**\n * Clear all data\n */\n clear(): void {\n this.data.clear()\n this.topicSessions.clear()\n this.personaParticipation.clear()\n }\n\n /**\n * Get sessions by topic\n * @param topic - The topic to search for\n * @returns Array of sessions on that topic\n */\n getByTopic(topic: string): CollaborativeSession[] {\n // Exact match\n const exact = this.topicSessions.get(topic) || []\n\n // Also find partial matches\n const partial = this.filter(\n (session) =>\n session.topic.toLowerCase().includes(topic.toLowerCase()) && !exact.includes(session)\n )\n\n return [...exact, ...partial]\n }\n\n /**\n * Get sessions where a specific persona participated\n * @param personaId - The persona identifier\n * @returns Array of sessions with that persona\n */\n getByPersona(personaId: string): CollaborativeSession[] {\n const sessionIds = this.personaParticipation.get(personaId)\n if (!sessionIds) return []\n\n return Array.from(sessionIds)\n .map((id) => this.get(id))\n .filter((session): session is CollaborativeSession => session !== undefined)\n }\n\n /**\n * Get active sessions (those needing next contribution)\n * @returns Array of active sessions\n */\n getActiveSessions(): CollaborativeSession[] {\n return this.filter((session) => session.nextContributionNeeded)\n }\n\n /**\n * Get sessions with consensus\n * @returns Array of sessions that reached consensus\n */\n getConsensusSession(): CollaborativeSession[] {\n return this.filter(\n (session) => !!(session.consensusPoints && session.consensusPoints.length > 0)\n )\n }\n\n /**\n * Get sessions with unresolved disagreements\n * @returns Array of sessions with active disagreements\n */\n getDisagreementSessions(): CollaborativeSession[] {\n return this.filter(\n (session) => !!(session.disagreements && session.disagreements.some((d) => !d.resolution))\n )\n }\n\n /**\n * Get contribution statistics for a session\n * @param sessionId - The session identifier\n * @returns Statistics about contributions\n */\n getContributionStats(sessionId: string): Record<string, any> | undefined {\n const session = this.get(sessionId)\n if (!session) return undefined\n\n const stats: Record<string, any> = {\n totalContributions: session.contributions.length,\n byType: {} as Record<string, number>,\n byPersona: {} as Record<string, number>,\n averageConfidence: 0,\n }\n\n let totalConfidence = 0\n\n session.contributions.forEach((contrib) => {\n // Count by type\n stats.byType[contrib.type] = (stats.byType[contrib.type] || 0) + 1\n\n // Count by persona\n const personaName =\n session.personas.find((p) => p.id === contrib.personaId)?.name || 'Unknown'\n stats.byPersona[personaName] = (stats.byPersona[personaName] || 0) + 1\n\n totalConfidence += contrib.confidence\n })\n\n stats.averageConfidence =\n session.contributions.length > 0 ? totalConfidence / session.contributions.length : 0\n\n return stats\n }\n\n /**\n * Get overall statistics\n * @returns Comprehensive statistics\n */\n getStatistics(): Record<string, any> {\n const sessions = this.getAll()\n\n return {\n totalSessions: sessions.length,\n activeSessions: this.getActiveSessions().length,\n sessionsWithConsensus: this.getConsensusSession().length,\n sessionsWithDisagreements: this.getDisagreementSessions().length,\n averageContributions:\n sessions.length > 0\n ? sessions.reduce((sum, s) => sum + s.contributions.length, 0) / sessions.length\n : 0,\n averagePersonas:\n sessions.length > 0\n ? sessions.reduce((sum, s) => sum + s.personas.length, 0) / sessions.length\n : 0,\n stageDistribution: this.getStageDistribution(),\n }\n }\n\n /**\n * Get distribution of sessions by stage\n * @returns Count of sessions in each stage\n */\n private getStageDistribution(): Record<string, number> {\n const distribution: Record<string, number> = {}\n\n this.forEach((session) => {\n distribution[session.stage] = (distribution[session.stage] || 0) + 1\n })\n\n return distribution\n }\n}\n","/**\n * Store for managing decision framework data\n */\n\nimport { BaseStore } from './base-store.js'\nimport { DecisionData, DecisionOption } from '../../types/index.js'\n\n/**\n * Specialized store for managing decision-making sessions\n */\nexport class DecisionStore extends BaseStore<DecisionData> {\n /** Map of decision statements to their sessions */\n private decisionIndex: Map<string, Set<string>>\n\n /** Map of analysis types to their sessions */\n private analysisSessions: Map<string, DecisionData[]>\n\n constructor() {\n super('DecisionStore')\n this.decisionIndex = new Map()\n this.analysisSessions = new Map()\n }\n\n /**\n * Add a new decision session\n * @param id - Unique identifier\n * @param decision - The decision data\n */\n add(id: string, decision: DecisionData): void {\n this.data.set(id, decision)\n\n // Index by decision keywords\n this.indexDecision(id, decision.decisionStatement)\n\n // Track by analysis type\n const sessions = this.analysisSessions.get(decision.analysisType) || []\n sessions.push(decision)\n this.analysisSessions.set(decision.analysisType, sessions)\n }\n\n /**\n * Index decision keywords for search\n * @param decisionId - Decision identifier\n * @param statement - Decision statement\n */\n private indexDecision(decisionId: string, statement: string): void {\n const keywords = statement\n .toLowerCase()\n .split(/\\s+/)\n .filter((word) => word.length > 3)\n\n keywords.forEach((keyword) => {\n const decisions = this.decisionIndex.get(keyword) || new Set()\n decisions.add(decisionId)\n this.decisionIndex.set(keyword, decisions)\n })\n }\n\n /**\n * Get all decision sessions\n * @returns Array of all sessions\n */\n getAll(): DecisionData[] {\n return Array.from(this.data.values())\n }\n\n /**\n * Clear all data\n */\n clear(): void {\n this.data.clear()\n this.decisionIndex.clear()\n this.analysisSessions.clear()\n }\n\n /**\n * Search decisions by keywords\n * @param keywords - Keywords to search for\n * @returns Array of matching decisions\n */\n searchDecisions(keywords: string): DecisionData[] {\n const searchTerms = keywords.toLowerCase().split(/\\s+/)\n const matchingIds = new Set<string>()\n\n searchTerms.forEach((term) => {\n const decisions = this.decisionIndex.get(term)\n if (decisions) {\n decisions.forEach((id) => matchingIds.add(id))\n }\n })\n\n return Array.from(matchingIds)\n .map((id) => this.get(id))\n .filter((decision): decision is DecisionData => decision !== undefined)\n }\n\n /**\n * Get decisions by analysis type\n * @param analysisType - The type of analysis\n * @returns Array of decisions using that analysis\n */\n getByAnalysisType(analysisType: DecisionData['analysisType']): DecisionData[] {\n return this.analysisSessions.get(analysisType) || []\n }\n\n /**\n * Get decisions by stage\n * @param stage - The decision stage\n * @returns Array of decisions in that stage\n */\n getByStage(stage: DecisionData['stage']): DecisionData[] {\n return this.filter((decision) => decision.stage === stage)\n }\n\n /**\n * Get completed decisions (with recommendations)\n * @returns Array of completed decisions\n */\n getCompletedDecisions(): DecisionData[] {\n return this.filter(\n (decision) => !!(decision.recommendation && decision.recommendation.trim().length > 0)\n )\n }\n\n /**\n * Get active decisions (needing next stage)\n * @returns Array of active decisions\n */\n getActiveDecisions(): DecisionData[] {\n return this.filter((decision) => decision.nextStageNeeded)\n }\n\n /**\n * Calculate decision quality score\n * @param decisionId - The decision identifier\n * @returns Quality score and breakdown\n */\n getDecisionQuality(decisionId: string): Record<string, any> | undefined {\n const decision = this.get(decisionId)\n if (!decision) return undefined\n\n const quality = {\n hasMultipleOptions: decision.options.length > 1,\n hasCriteria: (decision.criteria?.length || 0) > 0,\n hasEvaluations: (decision.criteriaEvaluations?.length || 0) > 0,\n hasStakeholders: (decision.stakeholders?.length || 0) > 0,\n hasConstraints: (decision.constraints?.length || 0) > 0,\n hasOutcomes: (decision.possibleOutcomes?.length || 0) > 0,\n hasInformationGaps: (decision.informationGaps?.length || 0) > 0,\n hasSensitivityAnalysis: (decision.sensitivityInsights?.length || 0) > 0,\n hasRecommendation: !!decision.recommendation,\n }\n\n const score = Object.values(quality).filter((v) => v).length / Object.keys(quality).length\n\n return {\n score,\n breakdown: quality,\n completeness: `${Math.round(score * 100)}%`,\n }\n }\n\n /**\n * Get the best option based on scores\n * @param decisionId - The decision identifier\n * @returns The best option or undefined\n */\n getBestOption(decisionId: string): DecisionOption | undefined {\n const decision = this.get(decisionId)\n if (!decision) return undefined\n\n // Check different scoring methods\n if (decision.expectedValues) {\n const expectedValues = decision.expectedValues\n const bestId = Object.entries(expectedValues).reduce(\n (best, [id, value]) => (value > (expectedValues[best] || -Infinity) ? id : best),\n ''\n )\n\n return decision.options.find((opt) => opt.id === bestId)\n }\n\n if (decision.multiCriteriaScores) {\n const multiCriteriaScores = decision.multiCriteriaScores\n const bestId = Object.entries(multiCriteriaScores).reduce(\n (best, [id, score]) => (score > (multiCriteriaScores[best] || -Infinity) ? id : best),\n ''\n )\n\n return decision.options.find((opt) => opt.id === bestId)\n }\n\n return undefined\n }\n\n /**\n * Get overall statistics\n * @returns Comprehensive statistics\n */\n getStatistics(): Record<string, any> {\n const decisions = this.getAll()\n const completed = this.getCompletedDecisions()\n\n return {\n totalDecisions: decisions.length,\n completedDecisions: completed.length,\n activeDecisions: this.getActiveDecisions().length,\n completionRate: decisions.length > 0 ? completed.length / decisions.length : 0,\n averageOptions:\n decisions.length > 0\n ? decisions.reduce((sum, d) => sum + d.options.length, 0) / decisions.length\n : 0,\n averageCriteria:\n decisions.length > 0\n ? decisions.reduce((sum, d) => sum + (d.criteria?.length || 0), 0) / decisions.length\n : 0,\n analysisTypeDistribution: this.getAnalysisTypeDistribution(),\n stageDistribution: this.getStageDistribution(),\n }\n }\n\n /**\n * Get distribution by analysis type\n */\n private getAnalysisTypeDistribution(): Record<string, number> {\n const distribution: Record<string, number> = {}\n\n this.analysisSessions.forEach((sessions, type) => {\n distribution[type] = sessions.length\n })\n\n return distribution\n }\n\n /**\n * Get distribution by stage\n */\n private getStageDistribution(): Record<string, number> {\n const distribution: Record<string, number> = {}\n\n this.forEach((decision) => {\n distribution[decision.stage] = (distribution[decision.stage] || 0) + 1\n })\n\n return distribution\n }\n}\n","/**\n * Store for managing metacognitive monitoring data\n */\n\nimport { BaseStore } from './base-store.js'\nimport { MetacognitiveData, KnowledgeAssessment, ClaimAssessment } from '../../types/index.js'\n\n/**\n * Specialized store for managing metacognitive monitoring sessions\n */\nexport class MetacognitiveStore extends BaseStore<MetacognitiveData> {\n /** Map of tasks to their monitoring sessions */\n private taskSessions: Map<string, MetacognitiveData[]>\n\n /** Map of domains to knowledge assessments */\n private domainKnowledge: Map<string, KnowledgeAssessment[]>\n\n constructor() {\n super('MetacognitiveStore')\n this.taskSessions = new Map()\n this.domainKnowledge = new Map()\n }\n\n /**\n * Add a new metacognitive monitoring session\n * @param id - Unique identifier\n * @param session - The metacognitive data\n */\n add(id: string, session: MetacognitiveData): void {\n this.data.set(id, session)\n\n // Track by task\n const sessions = this.taskSessions.get(session.task) || []\n sessions.push(session)\n this.taskSessions.set(session.task, sessions)\n\n // Track knowledge assessments by domain\n if (session.knowledgeAssessment) {\n const assessments = this.domainKnowledge.get(session.knowledgeAssessment.domain) || []\n assessments.push(session.knowledgeAssessment)\n this.domainKnowledge.set(session.knowledgeAssessment.domain, assessments)\n }\n }\n\n /**\n * Get all metacognitive sessions\n * @returns Array of all sessions\n */\n getAll(): MetacognitiveData[] {\n return Array.from(this.data.values())\n }\n\n /**\n * Clear all data\n */\n clear(): void {\n this.data.clear()\n this.taskSessions.clear()\n this.domainKnowledge.clear()\n }\n\n /**\n * Get sessions by task\n * @param task - The task being monitored\n * @returns Array of monitoring sessions for that task\n */\n getByTask(task: string): MetacognitiveData[] {\n const taskLower = task.toLowerCase()\n return this.filter((session) => session.task.toLowerCase().includes(taskLower))\n }\n\n /**\n * Get sessions by stage\n * @param stage - The monitoring stage\n * @returns Array of sessions in that stage\n */\n getByStage(stage: MetacognitiveData['stage']): MetacognitiveData[] {\n return this.filter((session) => session.stage === stage)\n }\n\n /**\n * Get knowledge assessments for a domain\n * @param domain - The knowledge domain\n * @returns Array of assessments for that domain\n */\n getKnowledgeByDomain(domain: string): KnowledgeAssessment[] {\n return this.domainKnowledge.get(domain) || []\n }\n\n /**\n * Get all unique domains assessed\n * @returns Array of domain names\n */\n getAssessedDomains(): string[] {\n return Array.from(this.domainKnowledge.keys())\n }\n\n /**\n * Get sessions with low confidence\n * @param threshold - Confidence threshold (default: 0.5)\n * @returns Array of low-confidence sessions\n */\n getLowConfidenceSessions(threshold: number = 0.5): MetacognitiveData[] {\n return this.filter((session) => session.overallConfidence < threshold)\n }\n\n /**\n * Get sessions with high uncertainty\n * @param minAreas - Minimum number of uncertainty areas (default: 3)\n * @returns Array of high-uncertainty sessions\n */\n getHighUncertaintySessions(minAreas: number = 3): MetacognitiveData[] {\n return this.filter((session) => session.uncertaintyAreas.length >= minAreas)\n }\n\n /**\n * Get active sessions needing assessment\n * @returns Array of active sessions\n */\n getActiveSessions(): MetacognitiveData[] {\n return this.filter((session) => session.nextAssessmentNeeded)\n }\n\n /**\n * Analyze confidence trends across iterations\n * @param monitoringId - The monitoring session ID\n * @returns Confidence trend data\n */\n getConfidenceTrend(\n monitoringId: string\n ): Array<{ iteration: number; confidence: number }> | undefined {\n const sessions = this.filter((session) => session.monitoringId === monitoringId)\n if (sessions.length === 0) return undefined\n\n return sessions\n .sort((a, b) => a.iteration - b.iteration)\n .map((session) => ({\n iteration: session.iteration,\n confidence: session.overallConfidence,\n }))\n }\n\n /**\n * Get claim assessment statistics\n * @returns Statistics about claims\n */\n getClaimStatistics(): Record<string, any> {\n const allClaims: ClaimAssessment[] = []\n\n this.forEach((session) => {\n if (session.claims) {\n allClaims.push(...session.claims)\n }\n })\n\n const stats = {\n totalClaims: allClaims.length,\n byStatus: {} as Record<string, number>,\n averageConfidence: 0,\n withAlternatives: 0,\n }\n\n let totalConfidence = 0\n\n allClaims.forEach((claim) => {\n stats.byStatus[claim.status] = (stats.byStatus[claim.status] || 0) + 1\n totalConfidence += claim.confidenceScore\n if (claim.alternativeInterpretations && claim.alternativeInterpretations.length > 0) {\n stats.withAlternatives++\n }\n })\n\n stats.averageConfidence = allClaims.length > 0 ? totalConfidence / allClaims.length : 0\n\n return stats\n }\n\n /**\n * Get overall statistics\n * @returns Comprehensive statistics\n */\n getStatistics(): Record<string, any> {\n const sessions = this.getAll()\n\n return {\n totalSessions: sessions.length,\n activeSessions: this.getActiveSessions().length,\n averageConfidence:\n sessions.length > 0\n ? sessions.reduce((sum, s) => sum + s.overallConfidence, 0) / sessions.length\n : 0,\n averageUncertaintyAreas:\n sessions.length > 0\n ? sessions.reduce((sum, s) => sum + s.uncertaintyAreas.length, 0) / sessions.length\n : 0,\n assessedDomains: this.getAssessedDomains().length,\n stageDistribution: this.getStageDistribution(),\n claimStats: this.getClaimStatistics(),\n }\n }\n\n /**\n * Get distribution by stage\n */\n private getStageDistribution(): Record<string, number> {\n const distribution: Record<string, number> = {}\n\n this.forEach((session) => {\n distribution[session.stage] = (distribution[session.stage] || 0) + 1\n })\n\n return distribution\n }\n}\n","/**\n * Store for managing scientific inquiry data\n */\n\nimport { BaseStore } from './base-store.js'\nimport { ScientificInquiryData, HypothesisData, ExperimentData } from '../../types/index.js'\n\n/**\n * Specialized store for managing scientific inquiry sessions\n */\nexport class ScientificStore extends BaseStore<ScientificInquiryData> {\n /** Map of inquiry IDs to their sessions */\n private inquirySessions: Map<string, ScientificInquiryData[]>\n\n /** Map of hypothesis IDs to their data */\n private hypotheses: Map<string, HypothesisData>\n\n /** Map of experiment IDs to their data */\n private experiments: Map<string, ExperimentData>\n\n constructor() {\n super('ScientificStore')\n this.inquirySessions = new Map()\n this.hypotheses = new Map()\n this.experiments = new Map()\n }\n\n /**\n * Add a new scientific inquiry session\n * @param id - Unique identifier\n * @param inquiry - The scientific inquiry data\n */\n add(id: string, inquiry: ScientificInquiryData): void {\n this.data.set(id, inquiry)\n\n // Track by inquiry ID\n const sessions = this.inquirySessions.get(inquiry.inquiryId) || []\n sessions.push(inquiry)\n this.inquirySessions.set(inquiry.inquiryId, sessions)\n\n // Track hypotheses\n if (inquiry.hypothesis) {\n this.hypotheses.set(inquiry.hypothesis.hypothesisId, inquiry.hypothesis)\n }\n\n // Track experiments\n if (inquiry.experiment) {\n this.experiments.set(inquiry.experiment.experimentId, inquiry.experiment)\n }\n }\n\n /**\n * Get all scientific inquiry sessions\n * @returns Array of all sessions\n */\n getAll(): ScientificInquiryData[] {