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,617 @@
1
+ /**
2
+ * Session Logger for Focus MCP Server
3
+ * Provides full visibility into multi-model brainstorming sessions
4
+ */
5
+ import { z } from "zod";
6
+ import * as fs from "fs/promises";
7
+ import * as fsSync from "fs";
8
+ import * as path from "path";
9
+ import { fileURLToPath } from "url";
10
+ import { dirname } from "path";
11
+ import { generateSessionId as generateSessionIdUtil } from "../utils/timestamp-formatter.js";
12
+ // Session configuration schema
13
+ export const SessionConfigSchema = z.object({
14
+ saveSession: z.boolean().default(false),
15
+ outputFormat: z.enum(["markdown", "json", "html"]).default("markdown"),
16
+ includeTimestamps: z.boolean().default(true),
17
+ includeModelMetadata: z.boolean().default(true),
18
+ sessionDir: z.string().default("./workflow-output/sessions"),
19
+ verbose: z.boolean().default(false),
20
+ autoSave: z.boolean().default(false),
21
+ maxHistorySize: z.number().default(100)
22
+ });
23
+ /**
24
+ * Session Logger Class
25
+ */
26
+ export class SessionLogger {
27
+ constructor(config = {}) {
28
+ this.currentSession = null;
29
+ this.sessions = new Map();
30
+ this.config = SessionConfigSchema.parse(config);
31
+ // Get the project root directory (where this file is located)
32
+ const __filename = fileURLToPath(import.meta.url);
33
+ const __dirname = dirname(__filename);
34
+ const projectRoot = path.resolve(__dirname, '..', '..');
35
+ // If sessionDir is relative (starts with .), make it relative to project root
36
+ if (this.config.sessionDir.startsWith('.')) {
37
+ this.sessionDir = path.join(projectRoot, this.config.sessionDir);
38
+ }
39
+ else {
40
+ // Otherwise use as-is (absolute path)
41
+ this.sessionDir = path.resolve(this.config.sessionDir);
42
+ }
43
+ this.ensureSessionDirectorySync();
44
+ }
45
+ /**
46
+ * Update configuration at runtime
47
+ */
48
+ updateConfig(config) {
49
+ this.config = SessionConfigSchema.parse({ ...this.config, ...config });
50
+ // Update sessionDir if it changed
51
+ if (config.sessionDir) {
52
+ const __filename = fileURLToPath(import.meta.url);
53
+ const __dirname = dirname(__filename);
54
+ const projectRoot = path.resolve(__dirname, '..', '..');
55
+ if (config.sessionDir.startsWith('.')) {
56
+ this.sessionDir = path.join(projectRoot, config.sessionDir);
57
+ }
58
+ else {
59
+ this.sessionDir = path.resolve(config.sessionDir);
60
+ }
61
+ this.ensureSessionDirectorySync();
62
+ }
63
+ }
64
+ /**
65
+ * Ensure session directory exists (synchronous version for constructor)
66
+ */
67
+ ensureSessionDirectorySync() {
68
+ try {
69
+ fsSync.mkdirSync(this.sessionDir, { recursive: true });
70
+ }
71
+ catch (error) {
72
+ // Ignore EEXIST errors (directory already exists)
73
+ if (error?.code !== 'EEXIST') {
74
+ console.error(`Failed to create session directory: ${error}`);
75
+ // Try async as fallback
76
+ this.ensureSessionDirectory();
77
+ }
78
+ }
79
+ }
80
+ /**
81
+ * Ensure session directory exists (async version for other operations)
82
+ */
83
+ async ensureSessionDirectory() {
84
+ try {
85
+ await fs.mkdir(this.sessionDir, { recursive: true });
86
+ }
87
+ catch (error) {
88
+ console.error(`Failed to create session directory: ${error}`);
89
+ }
90
+ }
91
+ /**
92
+ * Start a new session
93
+ */
94
+ async startSession(mode, query, domain) {
95
+ const sessionId = this.generateSessionId(mode);
96
+ this.currentSession = {
97
+ id: sessionId,
98
+ timestamp: new Date(),
99
+ mode,
100
+ query,
101
+ domain,
102
+ steps: [],
103
+ status: "active"
104
+ };
105
+ this.sessions.set(sessionId, this.currentSession);
106
+ if (this.config.verbose) {
107
+ console.error(`\n🎬 Session Started: ${sessionId}`);
108
+ console.error(`Mode: ${mode}`);
109
+ console.error(`Query: ${query}`);
110
+ if (domain)
111
+ console.error(`Domain: ${domain}`);
112
+ console.error("─".repeat(50));
113
+ }
114
+ return sessionId;
115
+ }
116
+ /**
117
+ * Log a step in the current session
118
+ */
119
+ async logStep(model, provider, mode, prompt, response, metadata) {
120
+ if (!this.currentSession) {
121
+ console.error("No active session to log step");
122
+ return;
123
+ }
124
+ const startTime = new Date();
125
+ const step = {
126
+ stepNumber: this.currentSession.steps.length + 1,
127
+ model,
128
+ provider,
129
+ mode,
130
+ prompt,
131
+ response,
132
+ startTime,
133
+ endTime: new Date(),
134
+ duration: 0, // Will be updated
135
+ metadata
136
+ };
137
+ // Simulate processing time (in real usage, this would be actual API call time)
138
+ step.endTime = new Date();
139
+ step.duration = step.endTime.getTime() - step.startTime.getTime();
140
+ this.currentSession.steps.push(step);
141
+ if (this.config.verbose) {
142
+ this.printStepVerbose(step);
143
+ }
144
+ // Auto-save if configured
145
+ if (this.config.autoSave) {
146
+ await this.saveSession(this.currentSession.id);
147
+ }
148
+ }
149
+ /**
150
+ * Print step in verbose mode
151
+ */
152
+ printStepVerbose(step) {
153
+ console.error(`\n📍 Step ${step.stepNumber}: ${step.mode.toUpperCase()}`);
154
+ console.error(`Model: ${step.model} (${step.provider})`);
155
+ console.error(`Duration: ${step.duration}ms`);
156
+ if (step.tokens) {
157
+ console.error(`Tokens: ${step.tokens.total || 'N/A'}`);
158
+ }
159
+ console.error("\n🤖 Response:");
160
+ console.error("─".repeat(50));
161
+ // Truncate very long responses in verbose mode
162
+ const maxLength = 1000;
163
+ if (step.response.length > maxLength) {
164
+ console.error(step.response.substring(0, maxLength) + "...\n[Truncated]");
165
+ }
166
+ else {
167
+ console.error(step.response);
168
+ }
169
+ console.error("─".repeat(50));
170
+ }
171
+ /**
172
+ * Add synthesis to session
173
+ */
174
+ async addSynthesis(synthesis) {
175
+ if (!this.currentSession) {
176
+ console.error("No active session for synthesis");
177
+ return;
178
+ }
179
+ this.currentSession.synthesis = synthesis;
180
+ this.currentSession.status = "completed";
181
+ this.currentSession.totalDuration = this.currentSession.steps.reduce((sum, step) => sum + step.duration, 0);
182
+ if (this.config.verbose) {
183
+ console.error("\n🎯 Final Synthesis:");
184
+ console.error("═".repeat(50));
185
+ console.error(synthesis);
186
+ console.error("═".repeat(50));
187
+ console.error(`\nTotal Duration: ${this.currentSession.totalDuration}ms`);
188
+ console.error(`Total Steps: ${this.currentSession.steps.length}`);
189
+ }
190
+ }
191
+ /**
192
+ * End the current session
193
+ */
194
+ async endSession(save = true) {
195
+ if (!this.currentSession) {
196
+ console.error("No active session to end");
197
+ return;
198
+ }
199
+ if (this.currentSession.status === "active") {
200
+ this.currentSession.status = "completed";
201
+ }
202
+ let filepath;
203
+ if (save || this.config.saveSession) {
204
+ try {
205
+ filepath = await this.saveSession(this.currentSession.id);
206
+ if (this.config.verbose) {
207
+ console.error(`\n✅ Session saved: ${filepath}`);
208
+ }
209
+ }
210
+ catch (error) {
211
+ console.error(`\n❌ Failed to save session: ${error}`);
212
+ // Don't throw, just log the error
213
+ }
214
+ }
215
+ if (this.config.verbose) {
216
+ console.error(`\n🏁 Session Ended: ${this.currentSession.id}`);
217
+ }
218
+ const sessionId = this.currentSession.id;
219
+ this.currentSession = null;
220
+ // Return filepath if saved, otherwise return sessionId for reference
221
+ return filepath || (save ? sessionId : undefined);
222
+ }
223
+ /**
224
+ * Save session to file
225
+ */
226
+ async saveSession(sessionId, format) {
227
+ // Try to get session from Map first, then check currentSession
228
+ let session = this.sessions.get(sessionId);
229
+ if (!session && this.currentSession?.id === sessionId) {
230
+ session = this.currentSession;
231
+ // Ensure it's also in the Map for consistency
232
+ this.sessions.set(sessionId, this.currentSession);
233
+ }
234
+ if (!session) {
235
+ throw new Error(`Session ${sessionId} not found`);
236
+ }
237
+ const outputFormat = format || this.config.outputFormat;
238
+ const filename = `${sessionId}.${outputFormat === "html" ? "html" : outputFormat === "json" ? "json" : "md"}`;
239
+ const filepath = path.join(this.sessionDir, filename);
240
+ let content;
241
+ switch (outputFormat) {
242
+ case "json":
243
+ content = this.formatAsJSON(session);
244
+ break;
245
+ case "html":
246
+ content = this.formatAsHTML(session);
247
+ break;
248
+ case "markdown":
249
+ default:
250
+ content = this.formatAsMarkdown(session);
251
+ break;
252
+ }
253
+ await fs.writeFile(filepath, content, "utf-8");
254
+ if (this.config.verbose) {
255
+ console.error(`\n💾 Session saved: ${filepath}`);
256
+ }
257
+ return filepath;
258
+ }
259
+ /**
260
+ * Format session as Markdown
261
+ */
262
+ formatAsMarkdown(session) {
263
+ const lines = [];
264
+ lines.push(`# Focus MCP ${session.mode} Session\n`);
265
+ lines.push("## Metadata");
266
+ lines.push(`- **Session ID**: ${session.id}`);
267
+ lines.push(`- **Date**: ${session.timestamp.toISOString()}`);
268
+ lines.push(`- **Mode**: ${session.mode}`);
269
+ if (session.domain) {
270
+ lines.push(`- **Domain**: ${session.domain}`);
271
+ }
272
+ const models = [...new Set(session.steps.map(s => `${s.model} (${s.provider})`))];
273
+ lines.push(`- **Models Used**: ${models.join(", ")}`);
274
+ if (session.totalDuration) {
275
+ lines.push(`- **Total Duration**: ${(session.totalDuration / 1000).toFixed(1)}s`);
276
+ }
277
+ lines.push(`- **Query**: "${session.query}"\n`);
278
+ lines.push("---\n");
279
+ // Add each step
280
+ session.steps.forEach(step => {
281
+ lines.push(`## Step ${step.stepNumber}: ${step.mode}`);
282
+ lines.push(`**Model**: ${step.model}`);
283
+ lines.push(`**Provider**: ${step.provider}`);
284
+ lines.push(`**Duration**: ${(step.duration / 1000).toFixed(1)}s`);
285
+ if (step.tokens && this.config.includeModelMetadata) {
286
+ lines.push(`**Tokens**: ${step.tokens.total || 'N/A'}`);
287
+ }
288
+ lines.push("\n### Prompt:");
289
+ lines.push("```");
290
+ lines.push(step.prompt);
291
+ lines.push("```\n");
292
+ lines.push("### Response:");
293
+ lines.push(step.response);
294
+ lines.push("\n---\n");
295
+ });
296
+ // Add synthesis if available
297
+ if (session.synthesis) {
298
+ lines.push("## Final Synthesis");
299
+ lines.push(session.synthesis);
300
+ lines.push("\n---\n");
301
+ }
302
+ // Add summary statistics
303
+ lines.push("## Summary Statistics");
304
+ lines.push(`- Total Models Used: ${models.length}`);
305
+ lines.push(`- Total Steps: ${session.steps.length}`);
306
+ if (session.totalDuration) {
307
+ lines.push(`- Total Response Time: ${(session.totalDuration / 1000).toFixed(1)}s`);
308
+ }
309
+ const totalTokens = session.steps.reduce((sum, step) => sum + (step.tokens?.total || 0), 0);
310
+ if (totalTokens > 0) {
311
+ lines.push(`- Total Tokens: ${totalTokens}`);
312
+ }
313
+ return lines.join("\n");
314
+ }
315
+ /**
316
+ * Format session as JSON
317
+ */
318
+ formatAsJSON(session) {
319
+ return JSON.stringify(session, null, 2);
320
+ }
321
+ /**
322
+ * Format session as HTML
323
+ */
324
+ formatAsHTML(session) {
325
+ const html = `<!DOCTYPE html>
326
+ <html lang="en">
327
+ <head>
328
+ <meta charset="UTF-8">
329
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
330
+ <title>Focus MCP Session - ${session.id}</title>
331
+ <style>
332
+ body {
333
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
334
+ line-height: 1.6;
335
+ max-width: 1200px;
336
+ margin: 0 auto;
337
+ padding: 20px;
338
+ background: #f5f5f5;
339
+ }
340
+ .header {
341
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
342
+ color: white;
343
+ padding: 30px;
344
+ border-radius: 10px;
345
+ margin-bottom: 30px;
346
+ }
347
+ .metadata {
348
+ background: white;
349
+ padding: 20px;
350
+ border-radius: 10px;
351
+ margin-bottom: 20px;
352
+ box-shadow: 0 2px 4px rgba(0,0,0,0.1);
353
+ }
354
+ .step {
355
+ background: white;
356
+ padding: 20px;
357
+ border-radius: 10px;
358
+ margin-bottom: 20px;
359
+ box-shadow: 0 2px 4px rgba(0,0,0,0.1);
360
+ }
361
+ .step-header {
362
+ display: flex;
363
+ justify-content: space-between;
364
+ align-items: center;
365
+ margin-bottom: 15px;
366
+ padding-bottom: 10px;
367
+ border-bottom: 2px solid #f0f0f0;
368
+ }
369
+ .model-badge {
370
+ background: #667eea;
371
+ color: white;
372
+ padding: 5px 10px;
373
+ border-radius: 5px;
374
+ font-size: 14px;
375
+ }
376
+ .prompt, .response {
377
+ background: #f8f9fa;
378
+ padding: 15px;
379
+ border-radius: 5px;
380
+ margin: 10px 0;
381
+ border-left: 3px solid #667eea;
382
+ }
383
+ .synthesis {
384
+ background: #e8f5e9;
385
+ padding: 20px;
386
+ border-radius: 10px;
387
+ border-left: 4px solid #4caf50;
388
+ }
389
+ .stats {
390
+ display: grid;
391
+ grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
392
+ gap: 15px;
393
+ margin-top: 20px;
394
+ }
395
+ .stat-card {
396
+ background: white;
397
+ padding: 15px;
398
+ border-radius: 8px;
399
+ text-align: center;
400
+ box-shadow: 0 2px 4px rgba(0,0,0,0.1);
401
+ }
402
+ .stat-value {
403
+ font-size: 24px;
404
+ font-weight: bold;
405
+ color: #667eea;
406
+ }
407
+ .stat-label {
408
+ font-size: 14px;
409
+ color: #666;
410
+ margin-top: 5px;
411
+ }
412
+ </style>
413
+ </head>
414
+ <body>
415
+ <div class="header">
416
+ <h1>Focus MCP ${session.mode} Session</h1>
417
+ <p>${session.query}</p>
418
+ </div>
419
+
420
+ <div class="metadata">
421
+ <h2>Session Information</h2>
422
+ <p><strong>ID:</strong> ${session.id}</p>
423
+ <p><strong>Date:</strong> ${session.timestamp.toISOString()}</p>
424
+ <p><strong>Status:</strong> ${session.status}</p>
425
+ ${session.domain ? `<p><strong>Domain:</strong> ${session.domain}</p>` : ''}
426
+ </div>
427
+
428
+ ${session.steps.map(step => `
429
+ <div class="step">
430
+ <div class="step-header">
431
+ <h3>Step ${step.stepNumber}: ${step.mode}</h3>
432
+ <span class="model-badge">${step.model}</span>
433
+ </div>
434
+ <div class="prompt">
435
+ <strong>Prompt:</strong><br>
436
+ ${step.prompt.replace(/\n/g, '<br>')}
437
+ </div>
438
+ <div class="response">
439
+ <strong>Response:</strong><br>
440
+ ${step.response.replace(/\n/g, '<br>')}
441
+ </div>
442
+ <p><small>Duration: ${(step.duration / 1000).toFixed(1)}s</small></p>
443
+ </div>
444
+ `).join('')}
445
+
446
+ ${session.synthesis ? `
447
+ <div class="synthesis">
448
+ <h2>Final Synthesis</h2>
449
+ <p>${session.synthesis.replace(/\n/g, '<br>')}</p>
450
+ </div>
451
+ ` : ''}
452
+
453
+ <div class="stats">
454
+ <div class="stat-card">
455
+ <div class="stat-value">${session.steps.length}</div>
456
+ <div class="stat-label">Total Steps</div>
457
+ </div>
458
+ <div class="stat-card">
459
+ <div class="stat-value">${[...new Set(session.steps.map(s => s.model))].length}</div>
460
+ <div class="stat-label">Models Used</div>
461
+ </div>
462
+ ${session.totalDuration ? `
463
+ <div class="stat-card">
464
+ <div class="stat-value">${(session.totalDuration / 1000).toFixed(1)}s</div>
465
+ <div class="stat-label">Total Duration</div>
466
+ </div>
467
+ ` : ''}
468
+ </div>
469
+ </body>
470
+ </html>`;
471
+ return html;
472
+ }
473
+ /**
474
+ * List all saved sessions
475
+ */
476
+ async listSessions(filter, limit = 10) {
477
+ try {
478
+ const files = await fs.readdir(this.sessionDir);
479
+ const sessions = [];
480
+ for (const file of files) {
481
+ if (file.endsWith(".json")) {
482
+ const filepath = path.join(this.sessionDir, file);
483
+ const content = await fs.readFile(filepath, "utf-8");
484
+ const session = JSON.parse(content);
485
+ if (!filter ||
486
+ session.mode.includes(filter) ||
487
+ session.query.includes(filter)) {
488
+ sessions.push({
489
+ id: session.id,
490
+ date: new Date(session.timestamp),
491
+ mode: session.mode,
492
+ query: session.query
493
+ });
494
+ }
495
+ }
496
+ }
497
+ // Sort by date descending and limit
498
+ return sessions
499
+ .sort((a, b) => b.date.getTime() - a.date.getTime())
500
+ .slice(0, limit);
501
+ }
502
+ catch (error) {
503
+ console.error(`Error listing sessions: ${error}`);
504
+ return [];
505
+ }
506
+ }
507
+ /**
508
+ * Load and replay a session
509
+ */
510
+ async replaySession(sessionId, format = "full") {
511
+ // Try different file extensions
512
+ const extensions = [".json", ".md", ".html"];
513
+ let session = null;
514
+ for (const ext of extensions) {
515
+ try {
516
+ const filepath = path.join(this.sessionDir, sessionId + ext);
517
+ const content = await fs.readFile(filepath, "utf-8");
518
+ if (ext === ".json") {
519
+ session = JSON.parse(content);
520
+ break;
521
+ }
522
+ }
523
+ catch {
524
+ // Try next extension
525
+ }
526
+ }
527
+ if (!session) {
528
+ throw new Error(`Session ${sessionId} not found`);
529
+ }
530
+ if (format === "summary") {
531
+ return this.formatSessionSummary(session);
532
+ }
533
+ else {
534
+ return this.formatAsMarkdown(session);
535
+ }
536
+ }
537
+ /**
538
+ * Format session summary
539
+ */
540
+ formatSessionSummary(session) {
541
+ const lines = [];
542
+ lines.push(`# Session Summary: ${session.id}\n`);
543
+ lines.push(`**Query**: ${session.query}`);
544
+ lines.push(`**Mode**: ${session.mode}`);
545
+ lines.push(`**Date**: ${session.timestamp}`);
546
+ lines.push(`**Steps**: ${session.steps.length}`);
547
+ if (session.synthesis) {
548
+ lines.push("\n## Final Synthesis");
549
+ lines.push(session.synthesis);
550
+ }
551
+ return lines.join("\n");
552
+ }
553
+ /**
554
+ * Generate session ID with human-readable format
555
+ * Format: session-YYYY-MM-DD-DayName-HH-MM-mode
556
+ * Example: session-2025-11-23-Sunday-22-44-advanced-pingpong
557
+ */
558
+ generateSessionId(mode) {
559
+ return generateSessionIdUtil(mode);
560
+ }
561
+ /**
562
+ * Export session in different format
563
+ */
564
+ async exportSession(sessionId, format, outputPath) {
565
+ const session = this.sessions.get(sessionId);
566
+ if (!session) {
567
+ // Try to load from file
568
+ const loaded = await this.replaySession(sessionId);
569
+ if (!loaded) {
570
+ throw new Error(`Session ${sessionId} not found`);
571
+ }
572
+ }
573
+ if (format === "pdf") {
574
+ // PDF export would require additional dependencies
575
+ throw new Error("PDF export not yet implemented");
576
+ }
577
+ const filepath = outputPath ||
578
+ path.join(this.sessionDir, `${sessionId}-export.${format}`);
579
+ await this.saveSession(sessionId, format);
580
+ return filepath;
581
+ }
582
+ /**
583
+ * Get current session
584
+ */
585
+ getCurrentSession() {
586
+ return this.currentSession;
587
+ }
588
+ /**
589
+ * Clear old sessions
590
+ */
591
+ async clearOldSessions(daysToKeep = 30) {
592
+ const cutoffDate = new Date();
593
+ cutoffDate.setDate(cutoffDate.getDate() - daysToKeep);
594
+ let deletedCount = 0;
595
+ try {
596
+ const files = await fs.readdir(this.sessionDir);
597
+ for (const file of files) {
598
+ const filepath = path.join(this.sessionDir, file);
599
+ const stats = await fs.stat(filepath);
600
+ if (stats.mtime < cutoffDate) {
601
+ await fs.unlink(filepath);
602
+ deletedCount++;
603
+ }
604
+ }
605
+ }
606
+ catch (error) {
607
+ console.error(`Error clearing old sessions: ${error}`);
608
+ }
609
+ return deletedCount;
610
+ }
611
+ }
612
+ // Export singleton instance with default config that can be updated
613
+ export const sessionLogger = new SessionLogger({
614
+ saveSession: true, // Default to true for saving sessions
615
+ verbose: false,
616
+ outputFormat: "markdown"
617
+ });