@felkot/think-mcp 1.1.0

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 (49) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +165 -0
  3. package/dist/constants/index.d.ts +5 -0
  4. package/dist/constants/index.js +5 -0
  5. package/dist/constants/patterns.d.ts +8 -0
  6. package/dist/constants/patterns.js +34 -0
  7. package/dist/constants/prompts.d.ts +1 -0
  8. package/dist/constants/prompts.js +44 -0
  9. package/dist/constants/thresholds.d.ts +39 -0
  10. package/dist/constants/thresholds.js +54 -0
  11. package/dist/index.d.ts +20 -0
  12. package/dist/index.js +711 -0
  13. package/dist/services/burst.service.d.ts +39 -0
  14. package/dist/services/burst.service.js +215 -0
  15. package/dist/services/coaching.service.d.ts +38 -0
  16. package/dist/services/coaching.service.js +258 -0
  17. package/dist/services/consolidate.service.d.ts +17 -0
  18. package/dist/services/consolidate.service.js +236 -0
  19. package/dist/services/context.service.d.ts +15 -0
  20. package/dist/services/context.service.js +113 -0
  21. package/dist/services/export.service.d.ts +33 -0
  22. package/dist/services/export.service.js +129 -0
  23. package/dist/services/insights.service.d.ts +103 -0
  24. package/dist/services/insights.service.js +216 -0
  25. package/dist/services/logic.service.d.ts +28 -0
  26. package/dist/services/logic.service.js +467 -0
  27. package/dist/services/nudge.service.d.ts +20 -0
  28. package/dist/services/nudge.service.js +114 -0
  29. package/dist/services/recall.service.d.ts +38 -0
  30. package/dist/services/recall.service.js +188 -0
  31. package/dist/services/stagnation.service.d.ts +14 -0
  32. package/dist/services/stagnation.service.js +48 -0
  33. package/dist/services/thinking.service.d.ts +263 -0
  34. package/dist/services/thinking.service.js +1048 -0
  35. package/dist/services/validation.service.d.ts +45 -0
  36. package/dist/services/validation.service.js +260 -0
  37. package/dist/services/visualization.service.d.ts +23 -0
  38. package/dist/services/visualization.service.js +211 -0
  39. package/dist/types/thought.types.d.ts +486 -0
  40. package/dist/types/thought.types.js +5 -0
  41. package/dist/utils/index.d.ts +6 -0
  42. package/dist/utils/index.js +6 -0
  43. package/dist/utils/sensitive-data.d.ts +2 -0
  44. package/dist/utils/sensitive-data.js +28 -0
  45. package/dist/utils/storage-paths.d.ts +9 -0
  46. package/dist/utils/storage-paths.js +28 -0
  47. package/dist/utils/text-analysis.d.ts +30 -0
  48. package/dist/utils/text-analysis.js +92 -0
  49. package/package.json +66 -0
@@ -0,0 +1,45 @@
1
+ /**
2
+ * ValidationService - Thought sequence and path validation
3
+ * Stateless service - receives data as parameters
4
+ */
5
+ import type { ThoughtInput, ThoughtRecord, ValidationResult, PathConnectivityResult } from '../types/thought.types.js';
6
+ export declare class ValidationService {
7
+ /**
8
+ * Validate thought sequence - prevent skipping steps and invalid revisions
9
+ * Also validates revision content is meaningfully different
10
+ * @param input - The thought input to validate
11
+ * @param sessionThoughts - Current session thoughts
12
+ * @param lastThoughtNumber - Last recorded thought number
13
+ */
14
+ validateSequence(input: ThoughtInput, sessionThoughts: ThoughtRecord[], lastThoughtNumber: number): ValidationResult;
15
+ /**
16
+ * Validate thought quality - enforce substance and depth
17
+ * (v5.3.0) - Rejects lazy thoughts
18
+ */
19
+ validateThoughtQuality(input: ThoughtInput, isComplexTask: boolean): ValidationResult;
20
+ /**
21
+ * Check semantic quality using entropy and pattern matching
22
+ * Acts as a lightweight "LLM-Validator"
23
+ */
24
+ private checkSemanticQuality;
25
+ /**
26
+ * HARD duplicate check - returns error message if duplicate found
27
+ * Used for strict rejection before adding to history
28
+ * @param input - The thought input to check
29
+ * @param sessionThoughts - Current session thoughts
30
+ */
31
+ checkDuplicateStrict(input: ThoughtInput, sessionThoughts: ThoughtRecord[]): string | undefined;
32
+ /**
33
+ * Validate branch source - reject if branchFromThought references non-existent thought
34
+ * @param input - The thought input to validate
35
+ * @param sessionThoughts - Current session thoughts
36
+ */
37
+ validateBranchSource(input: ThoughtInput, sessionThoughts: ThoughtRecord[]): string | undefined;
38
+ /**
39
+ * Validate path connectivity - ensure thoughts in winningPath are logically connected
40
+ * Each thought must be reachable from its predecessor via sequence, branch, or revision
41
+ * @param winningPath - Array of thought numbers in the winning path
42
+ * @param sessionThoughts - Current session thoughts
43
+ */
44
+ validatePathConnectivity(winningPath: number[], sessionThoughts: ThoughtRecord[]): PathConnectivityResult;
45
+ }
@@ -0,0 +1,260 @@
1
+ /**
2
+ * ValidationService - Thought sequence and path validation
3
+ * Stateless service - receives data as parameters
4
+ */
5
+ import { calculateJaccardSimilarity, calculateWordEntropy } from '../utils/index.js';
6
+ import { LAZY_THINKING_PATTERNS } from '../constants/patterns.js';
7
+ export class ValidationService {
8
+ /**
9
+ * Validate thought sequence - prevent skipping steps and invalid revisions
10
+ * Also validates revision content is meaningfully different
11
+ * @param input - The thought input to validate
12
+ * @param sessionThoughts - Current session thoughts
13
+ * @param lastThoughtNumber - Last recorded thought number
14
+ */
15
+ validateSequence(input, sessionThoughts, lastThoughtNumber) {
16
+ // Validate revision target - can't revise future or non-existent thoughts
17
+ if (input.isRevision && input.revisesThought !== undefined) {
18
+ const targetThought = sessionThoughts.find((t) => t.thoughtNumber === input.revisesThought);
19
+ if (!targetThought) {
20
+ return {
21
+ valid: false,
22
+ warning: `🚫 INVALID REVISION: Cannot revise #${input.revisesThought}. Available: ${sessionThoughts.map((t) => t.thoughtNumber).join(', ')}`
23
+ };
24
+ }
25
+ // Check revision is meaningfully different from original
26
+ const similarity = calculateJaccardSimilarity(input.thought, targetThought.thought);
27
+ if (similarity > 0.85) {
28
+ return {
29
+ valid: false,
30
+ warning: `⚠️ SHALLOW: ${Math.round(similarity * 100)}% similar. Rewrite substantially.`
31
+ };
32
+ }
33
+ // Check for circular revision (revision text similar to even earlier thought)
34
+ const earlierThoughts = sessionThoughts.filter((t) => t.thoughtNumber < input.revisesThought && !t.isRevision);
35
+ for (const earlier of earlierThoughts) {
36
+ const circularSimilarity = calculateJaccardSimilarity(input.thought, earlier.thought);
37
+ if (circularSimilarity > 0.8) {
38
+ return {
39
+ valid: false,
40
+ warning: `🔄 CIRCULAR: ${Math.round(circularSimilarity * 100)}% similar to #${earlier.thoughtNumber}. New approach needed.`
41
+ };
42
+ }
43
+ }
44
+ }
45
+ // Allow revisions and branches to jump in sequence
46
+ if (input.isRevision || input.branchFromThought) {
47
+ return { valid: true };
48
+ }
49
+ // First thought is always valid
50
+ if (lastThoughtNumber === 0) {
51
+ return { valid: true };
52
+ }
53
+ const expectedNext = lastThoughtNumber + 1;
54
+ if (input.thoughtNumber > expectedNext) {
55
+ return {
56
+ valid: false,
57
+ warning: `⚠️ Sequence break: expected #${expectedNext}, got #${input.thoughtNumber}.`
58
+ };
59
+ }
60
+ return { valid: true };
61
+ }
62
+ /**
63
+ * Validate thought quality - enforce substance and depth
64
+ * (v5.3.0) - Rejects lazy thoughts
65
+ */
66
+ validateThoughtQuality(input, isComplexTask) {
67
+ const isHeavyTask = input.totalThoughts >= 8;
68
+ // 1. Check Phase Logic
69
+ if (input.phase) {
70
+ if (input.phase === 'execution' && input.thoughtNumber === 1) {
71
+ return {
72
+ valid: false,
73
+ warning: `⚠️ ILLEGAL PHASE JUMP: You cannot start with 'execution'. Begin with 'initialization' or 'analysis'.`
74
+ };
75
+ }
76
+ }
77
+ if (isHeavyTask &&
78
+ input.nextThoughtNeeded &&
79
+ input.thoughtNumber > 1 &&
80
+ (!input.subSteps || input.subSteps.length === 0) &&
81
+ (!input.alternatives || input.alternatives.length === 0)) {
82
+ return {
83
+ valid: false,
84
+ warning: '⚠️ HEAVY TASK: Add subSteps or alternatives for this step to avoid shallow linear reasoning.'
85
+ };
86
+ }
87
+ if (isHeavyTask &&
88
+ !input.nextThoughtNeeded &&
89
+ input.thoughtNumber < Math.ceil(input.totalThoughts * 0.7)) {
90
+ return {
91
+ valid: false,
92
+ warning: '⚠️ PREMATURE FINISH: You are ending too early for a heavy task. Continue reasoning or lower totalThoughts with justification.'
93
+ };
94
+ }
95
+ // 1. Minimum Length Check
96
+ if (input.thought.length < 20) {
97
+ return {
98
+ valid: false,
99
+ warning: `⚠️ TOO SHORT: Thought is too brief (${input.thought.length} chars). Expand your reasoning.`
100
+ };
101
+ }
102
+ // 2. Forbidden Weak Phrases
103
+ const weakPhrases = ['i hope', 'maybe', 'probably', 'might work', 'guess'];
104
+ const foundWeakness = weakPhrases.find((phrase) => input.thought.toLowerCase().includes(phrase));
105
+ if (foundWeakness) {
106
+ return {
107
+ valid: false,
108
+ warning: `⚠️ WEAK LANGUAGE: Avoid "${foundWeakness}". Be precise or use 'think_diverge' to explore uncertainty.`
109
+ };
110
+ }
111
+ // 3. Substeps for Complex Tasks (Middle of chain)
112
+ // If it's a complex task, and we are not in the first or last thought, require substeps for detailed execution
113
+ if (isComplexTask &&
114
+ input.thoughtNumber > 1 &&
115
+ !input.nextThoughtNeeded &&
116
+ (!input.subSteps || input.subSteps.length === 0)) {
117
+ // This is a heuristic: if we are finishing a complex task, we should have had substeps in previous thoughts.
118
+ // But if this SPECIFIC thought is a summary, it might be fine.
119
+ // Let's be less strict here to avoid false positives, but still warn.
120
+ // Actually, let's strictly require substeps if confidence is high (>8) to prove the confidence.
121
+ if (input.confidence && input.confidence > 8) {
122
+ return {
123
+ valid: false,
124
+ warning: `⚠️ UNJUSTIFIED CONFIDENCE: High confidence (${input.confidence}) requires subSteps to prove the logic. Add subSteps.`
125
+ };
126
+ }
127
+ }
128
+ // 4. Semantic Heuristics (v5.5.0) - Rejects "lazy" thinking and gibberish
129
+ const semanticCheck = this.checkSemanticQuality(input);
130
+ if (!semanticCheck.valid) {
131
+ return semanticCheck;
132
+ }
133
+ // 5. Complexity Budget Check (Game Theory)
134
+ // If thought proposes new files/libs without justification in "planning" phase
135
+ if (input.phase === 'strategy') {
136
+ const lowerT = input.thought.toLowerCase();
137
+ if ((lowerT.includes('create new file') || lowerT.includes('add library')) &&
138
+ input.thought.length < 100) {
139
+ return {
140
+ valid: false,
141
+ warning: `⚠️ COMPLEXITY BUDGET: Creating files/libs costs tokens. Justify WHY this is needed in detail (>100 chars).`
142
+ };
143
+ }
144
+ }
145
+ return { valid: true };
146
+ }
147
+ /**
148
+ * Check semantic quality using entropy and pattern matching
149
+ * Acts as a lightweight "LLM-Validator"
150
+ */
151
+ checkSemanticQuality(input) {
152
+ // A. Entropy Check (detects gibberish or extreme repetition)
153
+ const entropy = calculateWordEntropy(input.thought);
154
+ // Threshold 0.2: Relaxed to allow simple commands
155
+ if (entropy < 0.2 && input.thought.length > 50) {
156
+ return {
157
+ valid: false,
158
+ warning: `⚠️ LOW ENTROPY (${entropy.toFixed(2)}): Thought is too repetitive. Elaborate with diverse vocabulary.`
159
+ };
160
+ }
161
+ // B. Lazy Thinking Detection (e.g. "I will just simply...")
162
+ const lowerThought = input.thought.toLowerCase();
163
+ const foundLazyPattern = LAZY_THINKING_PATTERNS.find((pattern) => lowerThought.includes(pattern));
164
+ if (foundLazyPattern) {
165
+ // Allow if confidence is low (honest uncertainty)
166
+ if (!input.confidence || input.confidence > 5) {
167
+ return {
168
+ valid: false,
169
+ warning: `⚠️ LAZY THINKING DETECTED: Avoid terms like "${foundLazyPattern}". Be specific about HOW and WHY.`
170
+ };
171
+ }
172
+ }
173
+ // C. Context Awareness Check
174
+ // If files are provided but not mentioned, warn?
175
+ // (Disabled for now as it might be too strict, but good for future)
176
+ return { valid: true };
177
+ }
178
+ /**
179
+ * HARD duplicate check - returns error message if duplicate found
180
+ * Used for strict rejection before adding to history
181
+ * @param input - The thought input to check
182
+ * @param sessionThoughts - Current session thoughts
183
+ */
184
+ checkDuplicateStrict(input, sessionThoughts) {
185
+ if (input.isRevision)
186
+ return undefined; // Revisions are allowed to reuse numbers
187
+ const exists = sessionThoughts.some((t) => t.thoughtNumber === input.thoughtNumber);
188
+ if (exists) {
189
+ return `🚫 REJECTED: #${input.thoughtNumber} exists. Use isRevision:true or quickExtension.`;
190
+ }
191
+ return undefined;
192
+ }
193
+ /**
194
+ * Validate branch source - reject if branchFromThought references non-existent thought
195
+ * @param input - The thought input to validate
196
+ * @param sessionThoughts - Current session thoughts
197
+ */
198
+ validateBranchSource(input, sessionThoughts) {
199
+ if (!input.branchFromThought)
200
+ return undefined;
201
+ const sourceExists = sessionThoughts.some((t) => t.thoughtNumber === input.branchFromThought);
202
+ if (!sourceExists) {
203
+ return `🚫 INVALID BRANCH: Cannot branch from #${input.branchFromThought}. Available: ${sessionThoughts.map((t) => t.thoughtNumber).join(', ') || 'none'}`;
204
+ }
205
+ return undefined;
206
+ }
207
+ /**
208
+ * Validate path connectivity - ensure thoughts in winningPath are logically connected
209
+ * Each thought must be reachable from its predecessor via sequence, branch, or revision
210
+ * @param winningPath - Array of thought numbers in the winning path
211
+ * @param sessionThoughts - Current session thoughts
212
+ */
213
+ validatePathConnectivity(winningPath, sessionThoughts) {
214
+ if (winningPath.length <= 1)
215
+ return { valid: true };
216
+ const thoughtMap = new Map(sessionThoughts.map((t) => [t.thoughtNumber, t]));
217
+ for (let i = 1; i < winningPath.length; i++) {
218
+ const current = winningPath[i];
219
+ const previous = winningPath[i - 1];
220
+ const currentThought = thoughtMap.get(current);
221
+ if (!currentThought) {
222
+ return {
223
+ valid: false,
224
+ error: `Thought #${current} not found`,
225
+ disconnectedAt: current
226
+ };
227
+ }
228
+ // Build set of valid predecessors for current thought
229
+ const validPredecessors = new Set();
230
+ // Sequential predecessor (N can follow N-1)
231
+ validPredecessors.add(current - 1);
232
+ // Branch source (if this thought branches from another)
233
+ if (currentThought.branchFromThought) {
234
+ validPredecessors.add(currentThought.branchFromThought);
235
+ }
236
+ // Revision target (revision can follow the thought it revises)
237
+ if (currentThought.isRevision && currentThought.revisesThought) {
238
+ validPredecessors.add(currentThought.revisesThought);
239
+ // Also allow revision to follow the thought BEFORE the one it revises
240
+ validPredecessors.add(currentThought.revisesThought - 1);
241
+ }
242
+ // Special case: if previous thought was revised, current can follow the revision
243
+ const previousThought = thoughtMap.get(previous);
244
+ if (previousThought?.isRevision && previousThought.revisesThought) {
245
+ // Allow next sequential after revision target
246
+ validPredecessors.add(previousThought.revisesThought + 1);
247
+ }
248
+ if (!validPredecessors.has(previous)) {
249
+ return {
250
+ valid: false,
251
+ error: `Path discontinuity: #${current} cannot logically follow #${previous}. Valid predecessors for #${current}: [${Array.from(validPredecessors)
252
+ .sort((a, b) => a - b)
253
+ .join(', ')}]`,
254
+ disconnectedAt: current
255
+ };
256
+ }
257
+ }
258
+ return { valid: true };
259
+ }
260
+ }
@@ -0,0 +1,23 @@
1
+ /**
2
+ * VisualizationService - ASCII tree and Mermaid diagram generation
3
+ * Stateless service - receives data as parameters
4
+ * v4.3.0 - Improved visualization with confidence icons and truncation
5
+ */
6
+ import type { ThoughtRecord } from '../types/thought.types.js';
7
+ export declare class VisualizationService {
8
+ /**
9
+ * Generate ASCII tree visualization of thought structure
10
+ * v4.3.0: Added confidence icons, truncation for long sessions
11
+ * @param sessionThoughts - thoughts from current session
12
+ * @param branches - Map of branch ID to branch thoughts
13
+ */
14
+ generateAsciiTree(sessionThoughts: ThoughtRecord[], branches: Map<string, ThoughtRecord[]>): string;
15
+ /**
16
+ * Generate Mermaid.js graph visualization
17
+ * @param sessionThoughts - thoughts from current session
18
+ * @param branches - Map of branch ID to branch thoughts
19
+ * @param thoughtHistory - full thought history for branch filtering
20
+ * @param sessionStartIndex - index where current session starts
21
+ */
22
+ generateMermaid(sessionThoughts: ThoughtRecord[], branches: Map<string, ThoughtRecord[]>, thoughtHistory: ThoughtRecord[], sessionStartIndex: number): string;
23
+ }
@@ -0,0 +1,211 @@
1
+ /**
2
+ * VisualizationService - ASCII tree and Mermaid diagram generation
3
+ * Stateless service - receives data as parameters
4
+ * v4.3.0 - Improved visualization with confidence icons and truncation
5
+ */
6
+ import { sanitizeForMermaid } from '../utils/index.js';
7
+ // v4.3.0: Max thoughts to show in tree (older ones collapsed)
8
+ const MAX_TREE_THOUGHTS = 5;
9
+ /**
10
+ * Get confidence icon based on score
11
+ */
12
+ function getConfidenceIcon(confidence) {
13
+ if (!confidence)
14
+ return '○';
15
+ if (confidence >= 8)
16
+ return '✅';
17
+ if (confidence >= 5)
18
+ return '⚠️';
19
+ return '❌';
20
+ }
21
+ export class VisualizationService {
22
+ /**
23
+ * Generate ASCII tree visualization of thought structure
24
+ * v4.3.0: Added confidence icons, truncation for long sessions
25
+ * @param sessionThoughts - thoughts from current session
26
+ * @param branches - Map of branch ID to branch thoughts
27
+ */
28
+ generateAsciiTree(sessionThoughts, branches) {
29
+ if (sessionThoughts.length === 0)
30
+ return '(empty)';
31
+ const mainThoughts = sessionThoughts.filter((t) => !t.branchFromThought && !t.isRevision);
32
+ // v4.3.0: Truncate long sessions
33
+ const shouldTruncate = mainThoughts.length > MAX_TREE_THOUGHTS;
34
+ const hiddenCount = shouldTruncate ? mainThoughts.length - MAX_TREE_THOUGHTS : 0;
35
+ const visibleThoughts = shouldTruncate
36
+ ? mainThoughts.slice(-MAX_TREE_THOUGHTS)
37
+ : mainThoughts;
38
+ const lines = ['📊 Thought Tree:'];
39
+ // Show truncation notice
40
+ if (shouldTruncate) {
41
+ lines.push(`│ ... ${hiddenCount} earlier thought(s) hidden`);
42
+ }
43
+ for (let i = 0; i < visibleThoughts.length; i++) {
44
+ const thought = visibleThoughts[i];
45
+ const isLast = i === visibleThoughts.length - 1;
46
+ const prefix = isLast ? '└──' : '├──';
47
+ const childPrefix = isLast ? ' ' : '│ ';
48
+ // v4.3.0: Confidence icon instead of [N]
49
+ const confIcon = getConfidenceIcon(thought.confidence);
50
+ const preview = thought.thought.substring(0, 35);
51
+ lines.push(`${prefix} ${confIcon} #${thought.thoughtNumber}: ${preview}...`);
52
+ // Show subSteps (compact)
53
+ if (thought.subSteps && thought.subSteps.length > 0) {
54
+ lines.push(`${childPrefix}📋 [${thought.subSteps.length} steps]`);
55
+ }
56
+ // Show alternatives (compact)
57
+ if (thought.alternatives && thought.alternatives.length > 0) {
58
+ lines.push(`${childPrefix}⚖️ [${thought.alternatives.length} alts]`);
59
+ }
60
+ // Show extensions (compact - only count)
61
+ if (thought.extensions && thought.extensions.length > 0) {
62
+ const blockers = thought.extensions.filter(e => e.impact === 'blocker').length;
63
+ const extInfo = blockers > 0 ? `${thought.extensions.length} ext, ${blockers}🚫` : `${thought.extensions.length} ext`;
64
+ lines.push(`${childPrefix}🔍 [${extInfo}]`);
65
+ }
66
+ // Show revisions (compact)
67
+ const revisions = sessionThoughts.filter((t) => t.isRevision && t.revisesThought === thought.thoughtNumber);
68
+ if (revisions.length > 0) {
69
+ lines.push(`${childPrefix}🔄 [${revisions.length} revision(s)]`);
70
+ }
71
+ // Show branches (compact)
72
+ for (const [branchId, branchThoughts] of branches) {
73
+ const fromThis = branchThoughts.filter((t) => t.branchFromThought === thought.thoughtNumber);
74
+ if (fromThis.length > 0) {
75
+ lines.push(`${childPrefix}🌿 [${branchId}]: ${fromThis.length} thought(s)`);
76
+ }
77
+ }
78
+ }
79
+ return lines.join('\n');
80
+ }
81
+ /**
82
+ * Generate Mermaid.js graph visualization
83
+ * @param sessionThoughts - thoughts from current session
84
+ * @param branches - Map of branch ID to branch thoughts
85
+ * @param thoughtHistory - full thought history for branch filtering
86
+ * @param sessionStartIndex - index where current session starts
87
+ */
88
+ generateMermaid(sessionThoughts, branches, thoughtHistory, sessionStartIndex) {
89
+ if (sessionThoughts.length === 0)
90
+ return '';
91
+ const lines = ['graph TD;'];
92
+ const mainThoughts = sessionThoughts.filter((t) => !t.branchFromThought && !t.isRevision);
93
+ // Build set of revised thoughts (thoughts that have been superseded)
94
+ const revisedThoughts = new Set(sessionThoughts
95
+ .filter((t) => t.isRevision && t.revisesThought)
96
+ .map((t) => t.revisesThought));
97
+ // Build set of thoughts with blocker extensions
98
+ const blockerThoughts = new Set(sessionThoughts
99
+ .filter((t) => t.extensions?.some((e) => e.impact === 'blocker'))
100
+ .map((t) => t.thoughtNumber));
101
+ // Main flow subgraph
102
+ lines.push(' subgraph MainFlow["🧠 Main Reasoning"]');
103
+ // Add start node
104
+ if (mainThoughts.length > 0) {
105
+ lines.push(` start((Start)) --> ${mainThoughts[0].thoughtNumber};`);
106
+ }
107
+ // Process each main thought
108
+ for (let i = 0; i < mainThoughts.length; i++) {
109
+ const t = mainThoughts[i];
110
+ const label = sanitizeForMermaid(t.thought.substring(0, 25));
111
+ const confLabel = t.confidence ? `<br/>conf:${t.confidence}` : '';
112
+ const subStepsLabel = t.subSteps && t.subSteps.length > 0 ? `<br/>📋${t.subSteps.length} steps` : '';
113
+ const altsLabel = t.alternatives && t.alternatives.length > 0 ? `<br/>⚖️${t.alternatives.length} alts` : '';
114
+ // Determine style class with priority: blocker > revised > lowConf > highConf > normal
115
+ let styleClass = 'normal';
116
+ if (blockerThoughts.has(t.thoughtNumber)) {
117
+ styleClass = 'blocker';
118
+ }
119
+ else if (revisedThoughts.has(t.thoughtNumber)) {
120
+ styleClass = 'revised';
121
+ }
122
+ else if (t.confidence && t.confidence < 5) {
123
+ styleClass = 'lowConf';
124
+ }
125
+ else if (t.confidence && t.confidence >= 8) {
126
+ styleClass = 'highConf';
127
+ }
128
+ lines.push(` ${t.thoughtNumber}["#${t.thoughtNumber}: ${label}...${confLabel}${subStepsLabel}${altsLabel}"]:::${styleClass};`);
129
+ // Edge to next thought
130
+ if (i < mainThoughts.length - 1) {
131
+ lines.push(` ${t.thoughtNumber} --> ${mainThoughts[i + 1].thoughtNumber};`);
132
+ }
133
+ }
134
+ lines.push(' end');
135
+ // Extensions subgraph (if any)
136
+ const hasExtensions = mainThoughts.some((t) => t.extensions && t.extensions.length > 0);
137
+ if (hasExtensions) {
138
+ lines.push(' subgraph Extensions["🔍 Deep Analysis"]');
139
+ for (const t of mainThoughts) {
140
+ if (t.extensions && t.extensions.length > 0) {
141
+ t.extensions.forEach((ext, idx) => {
142
+ const extId = `ext_${t.thoughtNumber}_${idx}`;
143
+ const extLabel = sanitizeForMermaid(ext.content.substring(0, 20));
144
+ const extClass = ext.impact === 'blocker' ? 'blocker' : ext.impact === 'high' ? 'highImpact' : 'ext';
145
+ const icon = ext.impact === 'blocker' ? '🚫' : ext.impact === 'high' ? '⚠️' : '📝';
146
+ lines.push(` ${extId}[/"${icon} ${ext.type}: ${extLabel}..."/]:::${extClass};`);
147
+ });
148
+ }
149
+ }
150
+ lines.push(' end');
151
+ // Connect extensions to main thoughts
152
+ for (const t of mainThoughts) {
153
+ if (t.extensions && t.extensions.length > 0) {
154
+ t.extensions.forEach((_, idx) => {
155
+ const extId = `ext_${t.thoughtNumber}_${idx}`;
156
+ lines.push(` ${t.thoughtNumber} -.-> ${extId};`);
157
+ });
158
+ }
159
+ }
160
+ }
161
+ // Revisions subgraph (if any)
162
+ const revisions = sessionThoughts.filter((t) => t.isRevision);
163
+ if (revisions.length > 0) {
164
+ lines.push(' subgraph Revisions["🔄 Revisions"]');
165
+ revisions.forEach((rev, idx) => {
166
+ const revId = `rev_${rev.revisesThought}_${idx}`;
167
+ const revLabel = sanitizeForMermaid(rev.thought.substring(0, 20));
168
+ lines.push(` ${revId}["🔄 ${revLabel}..."]:::revision;`);
169
+ });
170
+ lines.push(' end');
171
+ // Connect revisions to targets
172
+ revisions.forEach((rev, idx) => {
173
+ const revId = `rev_${rev.revisesThought}_${idx}`;
174
+ lines.push(` ${revId} ==> ${rev.revisesThought};`);
175
+ });
176
+ }
177
+ // Branch subgraphs
178
+ for (const [branchId, branchThoughts] of branches) {
179
+ const sessionBranchThoughts = branchThoughts.filter((bt) => {
180
+ return thoughtHistory.indexOf(bt) >= sessionStartIndex;
181
+ });
182
+ if (sessionBranchThoughts.length > 0) {
183
+ lines.push(` subgraph Branch_${branchId}["🌿 Branch: ${branchId}"]`);
184
+ sessionBranchThoughts.forEach((bt, idx) => {
185
+ const branchNodeId = `branch_${branchId}_${idx}`;
186
+ const btLabel = sanitizeForMermaid(bt.thought.substring(0, 20));
187
+ lines.push(` ${branchNodeId}["${btLabel}..."]:::branch;`);
188
+ });
189
+ lines.push(' end');
190
+ // Connect branches to source thoughts
191
+ sessionBranchThoughts.forEach((bt, idx) => {
192
+ if (bt.branchFromThought) {
193
+ const branchNodeId = `branch_${branchId}_${idx}`;
194
+ lines.push(` ${bt.branchFromThought} -.->|${branchId}| ${branchNodeId};`);
195
+ }
196
+ });
197
+ }
198
+ }
199
+ // Style definitions with visual intelligence
200
+ lines.push(' classDef normal fill:#e1f5fe,stroke:#01579b;');
201
+ lines.push(' classDef highConf fill:#e1f5fe,stroke:#ffd700,stroke-width:3px;');
202
+ lines.push(' classDef lowConf fill:#ffecb3,stroke:#ff6f00;');
203
+ lines.push(' classDef blocker fill:#ffcdd2,stroke:#b71c1c,stroke-width:3px;');
204
+ lines.push(' classDef revised fill:#e0e0e0,stroke:#9e9e9e,stroke-dasharray:5 5;');
205
+ lines.push(' classDef highImpact fill:#fff3e0,stroke:#e65100;');
206
+ lines.push(' classDef ext fill:#f3e5f5,stroke:#7b1fa2;');
207
+ lines.push(' classDef revision fill:#e8f5e9,stroke:#2e7d32;');
208
+ lines.push(' classDef branch fill:#e0f2f1,stroke:#00695c;');
209
+ return lines.join('\n');
210
+ }
211
+ }