tachibot-mcp 2.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (214) hide show
  1. package/.env.example +260 -0
  2. package/CHANGELOG.md +54 -0
  3. package/CODE_OF_CONDUCT.md +56 -0
  4. package/CONTRIBUTING.md +54 -0
  5. package/Dockerfile +36 -0
  6. package/LICENSE +644 -0
  7. package/README.md +201 -0
  8. package/SECURITY.md +95 -0
  9. package/dist/personality/komaai-expressions.js +12 -0
  10. package/dist/profiles/balanced.json +33 -0
  11. package/dist/profiles/code_focus.json +33 -0
  12. package/dist/profiles/full.json +33 -0
  13. package/dist/profiles/minimal.json +33 -0
  14. package/dist/profiles/research_power.json +33 -0
  15. package/dist/scripts/build-profiles.js +46 -0
  16. package/dist/src/application/services/focus/FocusModeRegistry.js +46 -0
  17. package/dist/src/application/services/focus/FocusTool.service.js +109 -0
  18. package/dist/src/application/services/focus/ModeRegistry.js +46 -0
  19. package/dist/src/application/services/focus/modes/focus-deep.mode.js +27 -0
  20. package/dist/src/application/services/focus/modes/status.mode.js +50 -0
  21. package/dist/src/application/services/focus/modes/tachibot-status.mode.js +50 -0
  22. package/dist/src/collaborative-orchestrator.js +391 -0
  23. package/dist/src/config/model-constants.js +188 -0
  24. package/dist/src/config/model-defaults.js +57 -0
  25. package/dist/src/config/model-preferences.js +382 -0
  26. package/dist/src/config/timeout-config.js +130 -0
  27. package/dist/src/config.js +173 -0
  28. package/dist/src/domain/interfaces/IFocusMode.js +5 -0
  29. package/dist/src/domain/interfaces/IProvider.js +6 -0
  30. package/dist/src/domain/interfaces/ITool.js +5 -0
  31. package/dist/src/focus-deep.js +245 -0
  32. package/dist/src/infrastructure/ascii/art/robots.ascii.js +16 -0
  33. package/dist/src/mcp-client.js +90 -0
  34. package/dist/src/memory/index.js +17 -0
  35. package/dist/src/memory/memory-config.js +135 -0
  36. package/dist/src/memory/memory-interface.js +174 -0
  37. package/dist/src/memory/memory-manager.js +383 -0
  38. package/dist/src/memory/providers/devlog-provider.js +385 -0
  39. package/dist/src/memory/providers/hybrid-provider.js +399 -0
  40. package/dist/src/memory/providers/local-provider.js +388 -0
  41. package/dist/src/memory/providers/mem0-provider.js +337 -0
  42. package/dist/src/modes/architect.js +477 -0
  43. package/dist/src/modes/auditor.js +362 -0
  44. package/dist/src/modes/challenger.js +841 -0
  45. package/dist/src/modes/code-reviewer.js +382 -0
  46. package/dist/src/modes/commit-guardian.js +424 -0
  47. package/dist/src/modes/documentation-writer.js +572 -0
  48. package/dist/src/modes/scout.js +587 -0
  49. package/dist/src/modes/shared/helpers/challenger-helpers.js +454 -0
  50. package/dist/src/modes/shared/helpers/index.js +17 -0
  51. package/dist/src/modes/shared/helpers/scout-helpers.js +270 -0
  52. package/dist/src/modes/shared/helpers/verifier-helpers.js +332 -0
  53. package/dist/src/modes/test-architect.js +767 -0
  54. package/dist/src/modes/verifier.js +378 -0
  55. package/dist/src/monitoring/performance-monitor.js +435 -0
  56. package/dist/src/optimization/batch-executor.js +121 -0
  57. package/dist/src/optimization/context-pruner.js +196 -0
  58. package/dist/src/optimization/cost-monitor.js +338 -0
  59. package/dist/src/optimization/index.js +65 -0
  60. package/dist/src/optimization/model-router.js +264 -0
  61. package/dist/src/optimization/result-cache.js +114 -0
  62. package/dist/src/optimization/token-optimizer.js +257 -0
  63. package/dist/src/optimization/token-tracker.js +118 -0
  64. package/dist/src/orchestrator-instructions.js +128 -0
  65. package/dist/src/orchestrator-lite.js +139 -0
  66. package/dist/src/orchestrator.js +191 -0
  67. package/dist/src/orchestrators/collaborative/interfaces/IToolExecutionEngine.js +1 -0
  68. package/dist/src/orchestrators/collaborative/interfaces/IToolExecutionStrategy.js +5 -0
  69. package/dist/src/orchestrators/collaborative/interfaces/IVisualizationRenderer.js +1 -0
  70. package/dist/src/orchestrators/collaborative/registries/ModelProviderRegistry.js +95 -0
  71. package/dist/src/orchestrators/collaborative/registries/ToolAdapterRegistry.js +64 -0
  72. package/dist/src/orchestrators/collaborative/services/tool-execution/ToolExecutionService.js +502 -0
  73. package/dist/src/orchestrators/collaborative/services/visualization/VisualizationService.js +206 -0
  74. package/dist/src/orchestrators/collaborative/types/session-types.js +5 -0
  75. package/dist/src/profiles/balanced.js +37 -0
  76. package/dist/src/profiles/code_focus.js +37 -0
  77. package/dist/src/profiles/debug_intensive.js +59 -0
  78. package/dist/src/profiles/full.js +37 -0
  79. package/dist/src/profiles/minimal.js +37 -0
  80. package/dist/src/profiles/research_code.js +59 -0
  81. package/dist/src/profiles/research_power.js +37 -0
  82. package/dist/src/profiles/types.js +5 -0
  83. package/dist/src/profiles/workflow_builder.js +53 -0
  84. package/dist/src/prompt-engineer-lite.js +78 -0
  85. package/dist/src/prompt-engineer.js +399 -0
  86. package/dist/src/reasoning-chain.js +508 -0
  87. package/dist/src/sequential-thinking.js +291 -0
  88. package/dist/src/server-diagnostic.js +74 -0
  89. package/dist/src/server-raw.js +158 -0
  90. package/dist/src/server-simple.js +58 -0
  91. package/dist/src/server.js +514 -0
  92. package/dist/src/session/session-logger.js +617 -0
  93. package/dist/src/session/session-manager.js +571 -0
  94. package/dist/src/session/session-tools.js +400 -0
  95. package/dist/src/tools/advanced-modes.js +200 -0
  96. package/dist/src/tools/claude-integration.js +356 -0
  97. package/dist/src/tools/consolidated/ai-router.js +174 -0
  98. package/dist/src/tools/consolidated/ai-tool.js +48 -0
  99. package/dist/src/tools/consolidated/brainstorm-tool.js +87 -0
  100. package/dist/src/tools/consolidated/environment-detector.js +80 -0
  101. package/dist/src/tools/consolidated/index.js +50 -0
  102. package/dist/src/tools/consolidated/search-tool.js +110 -0
  103. package/dist/src/tools/consolidated/workflow-tool.js +238 -0
  104. package/dist/src/tools/gemini-tools.js +329 -0
  105. package/dist/src/tools/grok-enhanced.js +376 -0
  106. package/dist/src/tools/grok-tools.js +299 -0
  107. package/dist/src/tools/lmstudio-tools.js +223 -0
  108. package/dist/src/tools/openai-tools.js +498 -0
  109. package/dist/src/tools/openrouter-tools.js +317 -0
  110. package/dist/src/tools/optimized-wrapper.js +204 -0
  111. package/dist/src/tools/perplexity-tools.js +294 -0
  112. package/dist/src/tools/pingpong-tool.js +343 -0
  113. package/dist/src/tools/qwen-wrapper.js +74 -0
  114. package/dist/src/tools/tool-router.js +444 -0
  115. package/dist/src/tools/unified-ai-provider.js +260 -0
  116. package/dist/src/tools/workflow-runner.js +425 -0
  117. package/dist/src/tools/workflow-validator-tool.js +107 -0
  118. package/dist/src/types.js +23 -0
  119. package/dist/src/utils/input-validator.js +130 -0
  120. package/dist/src/utils/model-router.js +91 -0
  121. package/dist/src/utils/progress-stream.js +255 -0
  122. package/dist/src/utils/provider-router.js +88 -0
  123. package/dist/src/utils/smart-api-client.js +146 -0
  124. package/dist/src/utils/table-builder.js +218 -0
  125. package/dist/src/utils/timestamp-formatter.js +134 -0
  126. package/dist/src/utils/tool-compressor.js +257 -0
  127. package/dist/src/utils/tool-config.js +201 -0
  128. package/dist/src/validators/dependency-graph-validator.js +147 -0
  129. package/dist/src/validators/interpolation-validator.js +222 -0
  130. package/dist/src/validators/output-usage-validator.js +151 -0
  131. package/dist/src/validators/syntax-validator.js +102 -0
  132. package/dist/src/validators/tool-registry-validator.js +123 -0
  133. package/dist/src/validators/tool-types.js +97 -0
  134. package/dist/src/validators/types.js +8 -0
  135. package/dist/src/validators/workflow-validator.js +134 -0
  136. package/dist/src/visualizer-lite.js +42 -0
  137. package/dist/src/visualizer.js +179 -0
  138. package/dist/src/workflows/circuit-breaker.js +199 -0
  139. package/dist/src/workflows/custom-workflows.js +451 -0
  140. package/dist/src/workflows/engine/AutoSynthesizer.js +97 -0
  141. package/dist/src/workflows/engine/StepParameterResolver.js +74 -0
  142. package/dist/src/workflows/engine/VariableInterpolator.js +123 -0
  143. package/dist/src/workflows/engine/WorkflowDiscovery.js +125 -0
  144. package/dist/src/workflows/engine/WorkflowExecutionEngine.js +485 -0
  145. package/dist/src/workflows/engine/WorkflowExecutor.js +113 -0
  146. package/dist/src/workflows/engine/WorkflowFileManager.js +244 -0
  147. package/dist/src/workflows/engine/WorkflowHelpers.js +114 -0
  148. package/dist/src/workflows/engine/WorkflowOutputFormatter.js +83 -0
  149. package/dist/src/workflows/engine/events/WorkflowEventBus.js +132 -0
  150. package/dist/src/workflows/engine/events/interfaces/IEventBus.js +5 -0
  151. package/dist/src/workflows/engine/handlers/ErrorRecoveryHandler.js +162 -0
  152. package/dist/src/workflows/engine/handlers/PromptEnhancementHandler.js +115 -0
  153. package/dist/src/workflows/engine/handlers/SessionPersistenceHandler.js +167 -0
  154. package/dist/src/workflows/engine/handlers/StepExecutionHandler.js +231 -0
  155. package/dist/src/workflows/engine/handlers/ToolInvocationHandler.js +46 -0
  156. package/dist/src/workflows/engine/interfaces/IAutoSynthesizer.js +5 -0
  157. package/dist/src/workflows/engine/interfaces/IStepParameterResolver.js +5 -0
  158. package/dist/src/workflows/engine/interfaces/IVariableInterpolator.js +5 -0
  159. package/dist/src/workflows/engine/interfaces/IWorkflowDiscovery.js +4 -0
  160. package/dist/src/workflows/engine/interfaces/IWorkflowFileManager.js +5 -0
  161. package/dist/src/workflows/engine/interfaces/IWorkflowOutputFormatter.js +5 -0
  162. package/dist/src/workflows/engine/state/WorkflowStateMachine.js +194 -0
  163. package/dist/src/workflows/engine/state/interfaces/IStateMachine.js +17 -0
  164. package/dist/src/workflows/fallback-strategies.js +373 -0
  165. package/dist/src/workflows/message-queue.js +455 -0
  166. package/dist/src/workflows/model-router.js +189 -0
  167. package/dist/src/workflows/orchestrator-examples.js +174 -0
  168. package/dist/src/workflows/orchestrator-integration.js +200 -0
  169. package/dist/src/workflows/self-healing.js +524 -0
  170. package/dist/src/workflows/tool-mapper.js +407 -0
  171. package/dist/src/workflows/tool-orchestrator.js +796 -0
  172. package/dist/src/workflows/workflow-engine.js +573 -0
  173. package/dist/src/workflows/workflow-parser.js +283 -0
  174. package/dist/src/workflows/workflow-types.js +95 -0
  175. package/dist/src/workflows.js +568 -0
  176. package/dist/test-workflow-file-output.js +93 -0
  177. package/docs/API_KEYS.md +570 -0
  178. package/docs/CLAUDE_CODE_SETUP.md +181 -0
  179. package/docs/CLAUDE_DESKTOP_MANUAL.md +127 -0
  180. package/docs/CONFIGURATION.md +745 -0
  181. package/docs/FOCUS_MODES.md +240 -0
  182. package/docs/INSTALLATION_BOTH.md +145 -0
  183. package/docs/TERMS.md +352 -0
  184. package/docs/TOOLS_REFERENCE.md +1622 -0
  185. package/docs/TOOL_PARAMETERS.md +496 -0
  186. package/docs/TOOL_PROFILES.md +236 -0
  187. package/docs/WORKFLOWS.md +987 -0
  188. package/docs/WORKFLOW_OUTPUT.md +198 -0
  189. package/docs/WORKFLOW_PROGRESS_TRACKING.md +305 -0
  190. package/docs/workflows/design-brainstorm.md +335 -0
  191. package/package.json +97 -0
  192. package/profiles/balanced.json +37 -0
  193. package/profiles/code_focus.json +37 -0
  194. package/profiles/debug_intensive.json +34 -0
  195. package/profiles/full.json +37 -0
  196. package/profiles/minimal.json +37 -0
  197. package/profiles/research_power.json +37 -0
  198. package/profiles/workflow_builder.json +37 -0
  199. package/smithery.yaml +66 -0
  200. package/start.sh +8 -0
  201. package/tools.config.json +81 -0
  202. package/tsconfig.json +18 -0
  203. package/workflows/accessibility-code-audit.yaml +92 -0
  204. package/workflows/code-architecture-review.yaml +202 -0
  205. package/workflows/code-review.yaml +142 -0
  206. package/workflows/core/iterative-problem-solver.yaml +283 -0
  207. package/workflows/creative-brainstorm-yaml.yaml +215 -0
  208. package/workflows/pingpong.yaml +141 -0
  209. package/workflows/system/README.md +412 -0
  210. package/workflows/system/challenger.yaml +175 -0
  211. package/workflows/system/scout.yaml +164 -0
  212. package/workflows/system/verifier.yaml +133 -0
  213. package/workflows/ultra-creative-brainstorm.yaml +318 -0
  214. package/workflows/ux-research-flow.yaml +92 -0
@@ -0,0 +1,571 @@
1
+ /**
2
+ * Session Manager for Focus MCP Server
3
+ * Manages session lifecycle, analytics, and cross-session intelligence
4
+ */
5
+ import * as fs from "fs/promises";
6
+ import * as fsSync from "fs";
7
+ import * as path from "path";
8
+ import { SessionLogger } from "./session-logger.js";
9
+ export class SessionManager {
10
+ constructor(sessionDir = "./.focus-sessions") {
11
+ this.sessionCache = new Map();
12
+ this.analytics = null;
13
+ this.sessionLogger = new SessionLogger({ sessionDir });
14
+ this.sessionDir = path.resolve(sessionDir);
15
+ this.ensureSessionDirectorySync();
16
+ }
17
+ /**
18
+ * Ensure session directory exists (synchronous version for constructor)
19
+ */
20
+ ensureSessionDirectorySync() {
21
+ try {
22
+ fsSync.mkdirSync(this.sessionDir, { recursive: true });
23
+ }
24
+ catch (error) {
25
+ // Ignore EEXIST errors (directory already exists)
26
+ if (error?.code !== 'EEXIST') {
27
+ console.error(`Failed to create session directory: ${error}`);
28
+ }
29
+ }
30
+ }
31
+ /**
32
+ * Ensure session directory exists (async version for other operations)
33
+ */
34
+ async ensureSessionDirectory() {
35
+ try {
36
+ await fs.mkdir(this.sessionDir, { recursive: true });
37
+ }
38
+ catch (error) {
39
+ console.error(`Failed to create session directory: ${error}`);
40
+ }
41
+ }
42
+ /**
43
+ * Start a new session
44
+ */
45
+ async startSession(mode, query, config) {
46
+ return this.sessionLogger.startSession(mode, query);
47
+ }
48
+ /**
49
+ * Get current active session
50
+ */
51
+ getCurrentSession() {
52
+ return this.sessionLogger.getCurrentSession();
53
+ }
54
+ /**
55
+ * End current session
56
+ */
57
+ async endSession(save = true) {
58
+ await this.sessionLogger.endSession(save);
59
+ }
60
+ /**
61
+ * Load all sessions for analysis
62
+ */
63
+ async loadAllSessions() {
64
+ const sessions = [];
65
+ try {
66
+ const files = await fs.readdir(this.sessionDir);
67
+ for (const file of files) {
68
+ if (file.endsWith(".json")) {
69
+ const filepath = path.join(this.sessionDir, file);
70
+ const content = await fs.readFile(filepath, "utf-8");
71
+ const session = JSON.parse(content);
72
+ sessions.push(session);
73
+ this.sessionCache.set(session.id, session);
74
+ }
75
+ }
76
+ }
77
+ catch (error) {
78
+ console.error(`Error loading sessions: ${error}`);
79
+ }
80
+ return sessions;
81
+ }
82
+ /**
83
+ * Analyze sessions for patterns and insights
84
+ */
85
+ async analyzeSessions(dateRange, mode, metrics = ["model-usage", "response-time", "consensus-rate"]) {
86
+ const sessions = await this.loadAllSessions();
87
+ // Filter sessions by date range and mode
88
+ const filteredSessions = sessions.filter(session => {
89
+ const sessionDate = new Date(session.timestamp);
90
+ if (dateRange) {
91
+ if (sessionDate < dateRange.start || sessionDate > dateRange.end) {
92
+ return false;
93
+ }
94
+ }
95
+ if (mode && session.mode !== mode) {
96
+ return false;
97
+ }
98
+ return true;
99
+ });
100
+ // Calculate analytics
101
+ const analytics = {
102
+ totalSessions: filteredSessions.length,
103
+ averageDuration: 0,
104
+ averageSteps: 0,
105
+ modelUsageStats: new Map(),
106
+ modeDistribution: new Map(),
107
+ domainDistribution: new Map(),
108
+ consensusRate: 0,
109
+ topicClusters: []
110
+ };
111
+ if (filteredSessions.length === 0) {
112
+ return analytics;
113
+ }
114
+ // Calculate basic metrics
115
+ let totalDuration = 0;
116
+ let totalSteps = 0;
117
+ for (const session of filteredSessions) {
118
+ // Duration
119
+ if (session.totalDuration) {
120
+ totalDuration += session.totalDuration;
121
+ }
122
+ // Steps
123
+ totalSteps += session.steps.length;
124
+ // Mode distribution
125
+ const modeCount = analytics.modeDistribution.get(session.mode) || 0;
126
+ analytics.modeDistribution.set(session.mode, modeCount + 1);
127
+ // Domain distribution
128
+ if (session.domain) {
129
+ const domainCount = analytics.domainDistribution.get(session.domain) || 0;
130
+ analytics.domainDistribution.set(session.domain, domainCount + 1);
131
+ }
132
+ // Model usage stats
133
+ for (const step of session.steps) {
134
+ const modelKey = `${step.model}:${step.provider}`;
135
+ const stats = analytics.modelUsageStats.get(modelKey) || {
136
+ count: 0,
137
+ averageDuration: 0,
138
+ totalTokens: 0
139
+ };
140
+ stats.count++;
141
+ stats.averageDuration =
142
+ (stats.averageDuration * (stats.count - 1) + step.duration) / stats.count;
143
+ stats.totalTokens += step.tokens?.total || 0;
144
+ analytics.modelUsageStats.set(modelKey, stats);
145
+ }
146
+ }
147
+ analytics.averageDuration = totalDuration / filteredSessions.length;
148
+ analytics.averageSteps = totalSteps / filteredSessions.length;
149
+ // Calculate consensus rate (simplified - looks for similar responses)
150
+ if (metrics.includes("consensus-rate")) {
151
+ analytics.consensusRate = this.calculateConsensusRate(filteredSessions);
152
+ }
153
+ // Topic clustering (simplified)
154
+ if (metrics.includes("topic-clustering")) {
155
+ analytics.topicClusters = this.clusterTopics(filteredSessions);
156
+ }
157
+ this.analytics = analytics;
158
+ return analytics;
159
+ }
160
+ /**
161
+ * Calculate consensus rate across sessions
162
+ */
163
+ calculateConsensusRate(sessions) {
164
+ if (sessions.length < 2)
165
+ return 1.0;
166
+ let totalComparisons = 0;
167
+ let agreements = 0;
168
+ // Compare synthesis or final responses
169
+ for (let i = 0; i < sessions.length - 1; i++) {
170
+ for (let j = i + 1; j < sessions.length; j++) {
171
+ const session1 = sessions[i];
172
+ const session2 = sessions[j];
173
+ // Skip if different modes
174
+ if (session1.mode !== session2.mode)
175
+ continue;
176
+ totalComparisons++;
177
+ // Simple similarity check (could be enhanced with NLP)
178
+ const text1 = session1.synthesis || session1.steps[session1.steps.length - 1]?.response || "";
179
+ const text2 = session2.synthesis || session2.steps[session2.steps.length - 1]?.response || "";
180
+ const similarity = this.calculateTextSimilarity(text1, text2);
181
+ if (similarity > 0.7) {
182
+ agreements++;
183
+ }
184
+ }
185
+ }
186
+ return totalComparisons > 0 ? agreements / totalComparisons : 0;
187
+ }
188
+ /**
189
+ * Simple text similarity calculation
190
+ */
191
+ calculateTextSimilarity(text1, text2) {
192
+ const words1 = new Set(text1.toLowerCase().split(/\s+/));
193
+ const words2 = new Set(text2.toLowerCase().split(/\s+/));
194
+ const intersection = new Set([...words1].filter(x => words2.has(x)));
195
+ const union = new Set([...words1, ...words2]);
196
+ return union.size > 0 ? intersection.size / union.size : 0;
197
+ }
198
+ /**
199
+ * Cluster sessions by topic
200
+ */
201
+ clusterTopics(sessions) {
202
+ const clusters = new Map();
203
+ // Simple keyword-based clustering
204
+ const topicKeywords = {
205
+ "architecture": ["architecture", "design", "system", "pattern", "structure"],
206
+ "debugging": ["debug", "error", "bug", "fix", "issue"],
207
+ "optimization": ["optimize", "performance", "speed", "efficiency", "improve"],
208
+ "security": ["security", "vulnerability", "authentication", "authorization", "encryption"],
209
+ "testing": ["test", "testing", "unit", "integration", "coverage"],
210
+ "database": ["database", "sql", "query", "schema", "migration"],
211
+ "api": ["api", "endpoint", "rest", "graphql", "webhook"],
212
+ "frontend": ["frontend", "ui", "ux", "react", "vue", "css"],
213
+ "backend": ["backend", "server", "api", "database", "microservice"],
214
+ "devops": ["devops", "deployment", "ci", "cd", "docker", "kubernetes"]
215
+ };
216
+ for (const session of sessions) {
217
+ const queryLower = session.query.toLowerCase();
218
+ for (const [topic, keywords] of Object.entries(topicKeywords)) {
219
+ if (keywords.some(keyword => queryLower.includes(keyword))) {
220
+ const sessionIds = clusters.get(topic) || [];
221
+ sessionIds.push(session.id);
222
+ clusters.set(topic, sessionIds);
223
+ break; // Only assign to first matching topic
224
+ }
225
+ }
226
+ }
227
+ // Convert to result format
228
+ const result = [];
229
+ for (const [topic, sessionIds] of clusters.entries()) {
230
+ result.push({
231
+ topic,
232
+ sessions: sessionIds,
233
+ commonPatterns: this.findCommonPatterns(sessionIds, sessions)
234
+ });
235
+ }
236
+ return result;
237
+ }
238
+ /**
239
+ * Find common patterns in a group of sessions
240
+ */
241
+ findCommonPatterns(sessionIds, allSessions) {
242
+ const patterns = [];
243
+ const sessions = allSessions.filter(s => sessionIds.includes(s.id));
244
+ if (sessions.length < 2)
245
+ return patterns;
246
+ // Find common model sequences
247
+ const modelSequences = sessions.map(s => s.steps.map(step => step.model).join(" -> "));
248
+ // Find most common sequence
249
+ const sequenceCount = new Map();
250
+ for (const seq of modelSequences) {
251
+ sequenceCount.set(seq, (sequenceCount.get(seq) || 0) + 1);
252
+ }
253
+ const mostCommonSequence = [...sequenceCount.entries()]
254
+ .sort((a, b) => b[1] - a[1])[0];
255
+ if (mostCommonSequence && mostCommonSequence[1] > 1) {
256
+ patterns.push(`Model sequence: ${mostCommonSequence[0]}`);
257
+ }
258
+ // Find common response patterns (simplified)
259
+ const commonWords = this.findCommonWords(sessions);
260
+ if (commonWords.length > 0) {
261
+ patterns.push(`Common terms: ${commonWords.slice(0, 5).join(", ")}`);
262
+ }
263
+ return patterns;
264
+ }
265
+ /**
266
+ * Find common words across sessions
267
+ */
268
+ findCommonWords(sessions) {
269
+ const wordFrequency = new Map();
270
+ for (const session of sessions) {
271
+ const text = session.synthesis ||
272
+ session.steps.map(s => s.response).join(" ");
273
+ const words = text.toLowerCase()
274
+ .split(/\s+/)
275
+ .filter(word => word.length > 4); // Filter short words
276
+ for (const word of words) {
277
+ wordFrequency.set(word, (wordFrequency.get(word) || 0) + 1);
278
+ }
279
+ }
280
+ // Sort by frequency and return top words
281
+ return [...wordFrequency.entries()]
282
+ .sort((a, b) => b[1] - a[1])
283
+ .map(entry => entry[0])
284
+ .slice(0, 10);
285
+ }
286
+ /**
287
+ * Find sessions similar to a query
288
+ */
289
+ async findSimilarSessions(query, limit = 5) {
290
+ const sessions = await this.loadAllSessions();
291
+ const results = [];
292
+ for (const session of sessions) {
293
+ const similarity = this.calculateTextSimilarity(query, session.query);
294
+ results.push({ session, similarity });
295
+ }
296
+ // Sort by similarity and return top results
297
+ return results
298
+ .sort((a, b) => b.similarity - a.similarity)
299
+ .slice(0, limit);
300
+ }
301
+ /**
302
+ * Get session recommendations based on history
303
+ */
304
+ async getRecommendations(currentQuery) {
305
+ const similar = await this.findSimilarSessions(currentQuery, 3);
306
+ // Extract suggested models from similar sessions
307
+ const modelCounts = new Map();
308
+ const modeCounts = new Map();
309
+ for (const { session } of similar) {
310
+ // Count models
311
+ for (const step of session.steps) {
312
+ const modelKey = `${step.model}:${step.provider}`;
313
+ modelCounts.set(modelKey, (modelCounts.get(modelKey) || 0) + 1);
314
+ }
315
+ // Count modes
316
+ modeCounts.set(session.mode, (modeCounts.get(session.mode) || 0) + 1);
317
+ }
318
+ // Get top models
319
+ const suggestedModels = [...modelCounts.entries()]
320
+ .sort((a, b) => b[1] - a[1])
321
+ .slice(0, 3)
322
+ .map(entry => entry[0]);
323
+ // Get most common mode
324
+ const suggestedMode = [...modeCounts.entries()]
325
+ .sort((a, b) => b[1] - a[1])[0]?.[0] || "brainstorm";
326
+ return {
327
+ similarSessions: similar.map(({ session, similarity }) => ({
328
+ id: session.id,
329
+ query: session.query,
330
+ similarity
331
+ })),
332
+ suggestedModels,
333
+ suggestedMode
334
+ };
335
+ }
336
+ /**
337
+ * Export analytics report
338
+ */
339
+ async exportAnalyticsReport(format = "markdown") {
340
+ if (!this.analytics) {
341
+ await this.analyzeSessions();
342
+ }
343
+ const filename = `analytics-${new Date().toISOString().split('T')[0]}.${format === "html" ? "html" : format === "json" ? "json" : "md"}`;
344
+ const filepath = path.join(this.sessionDir, filename);
345
+ let content;
346
+ switch (format) {
347
+ case "json":
348
+ content = this.formatAnalyticsAsJSON();
349
+ break;
350
+ case "html":
351
+ content = this.formatAnalyticsAsHTML();
352
+ break;
353
+ case "markdown":
354
+ default:
355
+ content = this.formatAnalyticsAsMarkdown();
356
+ break;
357
+ }
358
+ await fs.writeFile(filepath, content, "utf-8");
359
+ return filepath;
360
+ }
361
+ /**
362
+ * Format analytics as Markdown
363
+ */
364
+ formatAnalyticsAsMarkdown() {
365
+ if (!this.analytics)
366
+ return "No analytics data available";
367
+ const lines = [];
368
+ const a = this.analytics;
369
+ lines.push("# Focus MCP Session Analytics Report\n");
370
+ lines.push(`Generated: ${new Date().toISOString()}\n`);
371
+ lines.push("## Overview");
372
+ lines.push(`- **Total Sessions**: ${a.totalSessions}`);
373
+ lines.push(`- **Average Duration**: ${(a.averageDuration / 1000).toFixed(1)}s`);
374
+ lines.push(`- **Average Steps**: ${a.averageSteps.toFixed(1)}`);
375
+ lines.push(`- **Consensus Rate**: ${(a.consensusRate * 100).toFixed(1)}%\n`);
376
+ lines.push("## Mode Distribution");
377
+ for (const [mode, count] of a.modeDistribution.entries()) {
378
+ lines.push(`- ${mode}: ${count} sessions`);
379
+ }
380
+ lines.push("");
381
+ if (a.domainDistribution.size > 0) {
382
+ lines.push("## Domain Distribution");
383
+ for (const [domain, count] of a.domainDistribution.entries()) {
384
+ lines.push(`- ${domain}: ${count} sessions`);
385
+ }
386
+ lines.push("");
387
+ }
388
+ lines.push("## Model Usage Statistics");
389
+ lines.push("| Model | Provider | Count | Avg Duration | Total Tokens |");
390
+ lines.push("|-------|----------|-------|--------------|--------------|");
391
+ for (const [modelKey, stats] of a.modelUsageStats.entries()) {
392
+ const [model, provider] = modelKey.split(":");
393
+ lines.push(`| ${model} | ${provider} | ${stats.count} | ${(stats.averageDuration / 1000).toFixed(1)}s | ${stats.totalTokens} |`);
394
+ }
395
+ lines.push("");
396
+ if (a.topicClusters.length > 0) {
397
+ lines.push("## Topic Clusters");
398
+ for (const cluster of a.topicClusters) {
399
+ lines.push(`\n### ${cluster.topic}`);
400
+ lines.push(`- Sessions: ${cluster.sessions.length}`);
401
+ if (cluster.commonPatterns.length > 0) {
402
+ lines.push("- Common Patterns:");
403
+ for (const pattern of cluster.commonPatterns) {
404
+ lines.push(` - ${pattern}`);
405
+ }
406
+ }
407
+ }
408
+ }
409
+ return lines.join("\n");
410
+ }
411
+ /**
412
+ * Format analytics as JSON
413
+ */
414
+ formatAnalyticsAsJSON() {
415
+ if (!this.analytics)
416
+ return "{}";
417
+ return JSON.stringify({
418
+ ...this.analytics,
419
+ modelUsageStats: Object.fromEntries(this.analytics.modelUsageStats),
420
+ modeDistribution: Object.fromEntries(this.analytics.modeDistribution),
421
+ domainDistribution: Object.fromEntries(this.analytics.domainDistribution)
422
+ }, null, 2);
423
+ }
424
+ /**
425
+ * Format analytics as HTML
426
+ */
427
+ formatAnalyticsAsHTML() {
428
+ if (!this.analytics)
429
+ return "<p>No analytics data available</p>";
430
+ const a = this.analytics;
431
+ return `<!DOCTYPE html>
432
+ <html lang="en">
433
+ <head>
434
+ <meta charset="UTF-8">
435
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
436
+ <title>Focus MCP Analytics Report</title>
437
+ <style>
438
+ body {
439
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
440
+ line-height: 1.6;
441
+ max-width: 1200px;
442
+ margin: 0 auto;
443
+ padding: 20px;
444
+ background: #f5f5f5;
445
+ }
446
+ .header {
447
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
448
+ color: white;
449
+ padding: 30px;
450
+ border-radius: 10px;
451
+ margin-bottom: 30px;
452
+ }
453
+ .card {
454
+ background: white;
455
+ padding: 20px;
456
+ border-radius: 10px;
457
+ margin-bottom: 20px;
458
+ box-shadow: 0 2px 4px rgba(0,0,0,0.1);
459
+ }
460
+ .stats-grid {
461
+ display: grid;
462
+ grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
463
+ gap: 20px;
464
+ margin: 20px 0;
465
+ }
466
+ .stat-card {
467
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
468
+ color: white;
469
+ padding: 20px;
470
+ border-radius: 10px;
471
+ text-align: center;
472
+ }
473
+ .stat-value {
474
+ font-size: 32px;
475
+ font-weight: bold;
476
+ }
477
+ .stat-label {
478
+ font-size: 14px;
479
+ opacity: 0.9;
480
+ margin-top: 5px;
481
+ }
482
+ table {
483
+ width: 100%;
484
+ border-collapse: collapse;
485
+ }
486
+ th, td {
487
+ padding: 12px;
488
+ text-align: left;
489
+ border-bottom: 1px solid #ddd;
490
+ }
491
+ th {
492
+ background: #f0f0f0;
493
+ font-weight: 600;
494
+ }
495
+ </style>
496
+ </head>
497
+ <body>
498
+ <div class="header">
499
+ <h1>Focus MCP Analytics Report</h1>
500
+ <p>Generated: ${new Date().toISOString()}</p>
501
+ </div>
502
+
503
+ <div class="stats-grid">
504
+ <div class="stat-card">
505
+ <div class="stat-value">${a.totalSessions}</div>
506
+ <div class="stat-label">Total Sessions</div>
507
+ </div>
508
+ <div class="stat-card">
509
+ <div class="stat-value">${(a.averageDuration / 1000).toFixed(1)}s</div>
510
+ <div class="stat-label">Avg Duration</div>
511
+ </div>
512
+ <div class="stat-card">
513
+ <div class="stat-value">${a.averageSteps.toFixed(1)}</div>
514
+ <div class="stat-label">Avg Steps</div>
515
+ </div>
516
+ <div class="stat-card">
517
+ <div class="stat-value">${(a.consensusRate * 100).toFixed(1)}%</div>
518
+ <div class="stat-label">Consensus Rate</div>
519
+ </div>
520
+ </div>
521
+
522
+ <div class="card">
523
+ <h2>Model Usage Statistics</h2>
524
+ <table>
525
+ <thead>
526
+ <tr>
527
+ <th>Model</th>
528
+ <th>Provider</th>
529
+ <th>Count</th>
530
+ <th>Avg Duration</th>
531
+ <th>Total Tokens</th>
532
+ </tr>
533
+ </thead>
534
+ <tbody>
535
+ ${[...a.modelUsageStats.entries()].map(([modelKey, stats]) => {
536
+ const [model, provider] = modelKey.split(":");
537
+ return `
538
+ <tr>
539
+ <td>${model}</td>
540
+ <td>${provider}</td>
541
+ <td>${stats.count}</td>
542
+ <td>${(stats.averageDuration / 1000).toFixed(1)}s</td>
543
+ <td>${stats.totalTokens}</td>
544
+ </tr>
545
+ `;
546
+ }).join('')}
547
+ </tbody>
548
+ </table>
549
+ </div>
550
+
551
+ ${a.topicClusters.length > 0 ? `
552
+ <div class="card">
553
+ <h2>Topic Clusters</h2>
554
+ ${a.topicClusters.map(cluster => `
555
+ <h3>${cluster.topic}</h3>
556
+ <p>Sessions: ${cluster.sessions.length}</p>
557
+ ${cluster.commonPatterns.length > 0 ? `
558
+ <p>Common Patterns:</p>
559
+ <ul>
560
+ ${cluster.commonPatterns.map(p => `<li>${p}</li>`).join('')}
561
+ </ul>
562
+ ` : ''}
563
+ `).join('')}
564
+ </div>
565
+ ` : ''}
566
+ </body>
567
+ </html>`;
568
+ }
569
+ }
570
+ // Export singleton instance
571
+ export const sessionManager = new SessionManager();