@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,39 @@
1
+ /**
2
+ * BurstService - Validation and processing for burst thinking sessions
3
+ * Stateless service - validates input and returns prepared data for commit
4
+ */
5
+ import type { BurstThought, BurstConsolidation, BurstMetrics, ThoughtRecord } from '../types/thought.types.js';
6
+ /** Burst validation limits */
7
+ export declare const BURST_LIMITS: {
8
+ maxThoughts: number;
9
+ minThoughts: number;
10
+ maxThoughtLength: number;
11
+ minThoughtLength: number;
12
+ maxStagnationScore: number;
13
+ minAvgEntropy: number;
14
+ minAvgConfidence: number;
15
+ };
16
+ /** Validation result from burst service */
17
+ export interface BurstValidationResult {
18
+ passed: boolean;
19
+ errors: string[];
20
+ warnings: string[];
21
+ metrics: BurstMetrics;
22
+ /** Sorted thoughts ready for commit (only if passed) */
23
+ sortedThoughts?: BurstThought[];
24
+ }
25
+ export declare class BurstService {
26
+ /**
27
+ * Validate burst thinking session
28
+ * Returns validation result with prepared data for commit
29
+ */
30
+ validate(goal: string, thoughts: BurstThought[], consolidation?: BurstConsolidation): BurstValidationResult;
31
+ /**
32
+ * Validate consolidation data
33
+ */
34
+ private validateConsolidation;
35
+ /**
36
+ * Convert BurstThought to ThoughtRecord
37
+ */
38
+ toThoughtRecord(t: BurstThought, totalThoughts: number, sessionId: string): ThoughtRecord;
39
+ }
@@ -0,0 +1,215 @@
1
+ /**
2
+ * BurstService - Validation and processing for burst thinking sessions
3
+ * Stateless service - validates input and returns prepared data for commit
4
+ */
5
+ import { calculateWordEntropy, calculateJaccardSimilarity } from '../utils/index.js';
6
+ /** Burst validation limits */
7
+ export const BURST_LIMITS = {
8
+ maxThoughts: 30,
9
+ minThoughts: 1,
10
+ maxThoughtLength: 2000, // Increased for deep architecture
11
+ minThoughtLength: 10, // Synced with thresholds.ts (was 50)
12
+ maxStagnationScore: 0.8, // Relaxed (was 0.6) - allow repetitive boilerplate
13
+ minAvgEntropy: 0.2, // Relaxed (was 0.25)
14
+ minAvgConfidence: 4,
15
+ };
16
+ export class BurstService {
17
+ /**
18
+ * Validate burst thinking session
19
+ * Returns validation result with prepared data for commit
20
+ */
21
+ validate(goal, thoughts, consolidation) {
22
+ const errors = [];
23
+ const warnings = [];
24
+ // === PHASE 1: Basic Validation ===
25
+ if (!goal || goal.trim().length < 10) {
26
+ errors.push('Goal is required and must be at least 10 characters');
27
+ }
28
+ if (!thoughts || thoughts.length === 0) {
29
+ errors.push('At least 1 thought is required');
30
+ }
31
+ else if (thoughts.length > BURST_LIMITS.maxThoughts) {
32
+ errors.push(`Too many thoughts: ${thoughts.length} > ${BURST_LIMITS.maxThoughts} max`);
33
+ }
34
+ // Early exit if basic validation fails
35
+ if (errors.length > 0) {
36
+ return {
37
+ passed: false,
38
+ errors,
39
+ warnings,
40
+ metrics: { avgConfidence: 0, avgEntropy: 0, avgLength: 0, stagnationScore: 0, thoughtCount: 0 },
41
+ };
42
+ }
43
+ // === PHASE 2: Sequence Validation ===
44
+ const sortedThoughts = [...thoughts].sort((a, b) => a.thoughtNumber - b.thoughtNumber);
45
+ for (let i = 0; i < sortedThoughts.length; i++) {
46
+ const expected = i + 1;
47
+ const actual = sortedThoughts[i].thoughtNumber;
48
+ if (actual !== expected && !sortedThoughts[i].isRevision) {
49
+ errors.push(`Sequence break: expected thought #${expected}, got #${actual}`);
50
+ break;
51
+ }
52
+ }
53
+ // Check for duplicate thought numbers (excluding revisions)
54
+ const nonRevisionNumbers = thoughts.filter(t => !t.isRevision).map(t => t.thoughtNumber);
55
+ const duplicates = nonRevisionNumbers.filter((n, i) => nonRevisionNumbers.indexOf(n) !== i);
56
+ if (duplicates.length > 0) {
57
+ errors.push(`Duplicate thought numbers: ${[...new Set(duplicates)].join(', ')}`);
58
+ }
59
+ // === PHASE 3: Content Quality Validation ===
60
+ let totalLength = 0;
61
+ let totalEntropy = 0;
62
+ let totalConfidence = 0;
63
+ let confidenceCount = 0;
64
+ for (const t of thoughts) {
65
+ if (t.thought.length < BURST_LIMITS.minThoughtLength) {
66
+ errors.push(`#${t.thoughtNumber} too short: ${t.thought.length} < ${BURST_LIMITS.minThoughtLength}`);
67
+ }
68
+ if (t.thought.length > BURST_LIMITS.maxThoughtLength) {
69
+ warnings.push(`#${t.thoughtNumber} truncated: ${t.thought.length} > ${BURST_LIMITS.maxThoughtLength}`);
70
+ }
71
+ totalLength += Math.min(t.thought.length, BURST_LIMITS.maxThoughtLength);
72
+ totalEntropy += calculateWordEntropy(t.thought);
73
+ if (t.confidence !== undefined) {
74
+ totalConfidence += t.confidence;
75
+ confidenceCount++;
76
+ }
77
+ // Validate revision targets
78
+ if (t.isRevision && t.revisesThought !== undefined) {
79
+ const targetExists = thoughts.some(other => other.thoughtNumber === t.revisesThought);
80
+ if (!targetExists) {
81
+ errors.push(`Revision #${t.thoughtNumber} targets non-existent #${t.revisesThought}`);
82
+ }
83
+ }
84
+ // Validate branch sources
85
+ if (t.branchFromThought !== undefined) {
86
+ const sourceExists = thoughts.some(other => other.thoughtNumber === t.branchFromThought);
87
+ if (!sourceExists) {
88
+ errors.push(`Branch #${t.thoughtNumber} from non-existent #${t.branchFromThought}`);
89
+ }
90
+ }
91
+ }
92
+ // === PHASE 4: Stagnation Detection (only if enough thoughts) ===
93
+ let stagnationScore = 0;
94
+ if (thoughts.length >= 3) { // v5.0.1: Skip for small batches
95
+ let totalSimilarity = 0;
96
+ let comparisons = 0;
97
+ for (let i = 1; i < thoughts.length; i++) {
98
+ const similarity = calculateJaccardSimilarity(thoughts[i].thought, thoughts[i - 1].thought);
99
+ totalSimilarity += similarity;
100
+ comparisons++;
101
+ }
102
+ stagnationScore = comparisons > 0 ? totalSimilarity / comparisons : 0;
103
+ if (stagnationScore > BURST_LIMITS.maxStagnationScore) {
104
+ errors.push(`Stagnation: ${(stagnationScore * 100).toFixed(0)}% similarity > ${BURST_LIMITS.maxStagnationScore * 100}%`);
105
+ }
106
+ }
107
+ // === PHASE 5: Calculate Metrics ===
108
+ const avgLength = thoughts.length > 0 ? totalLength / thoughts.length : 0;
109
+ const avgEntropy = thoughts.length > 0 ? totalEntropy / thoughts.length : 0;
110
+ const avgConfidence = confidenceCount > 0 ? totalConfidence / confidenceCount : 0;
111
+ // v5.0.1: Skip entropy warning for small batches (< 5 thoughts)
112
+ if (avgEntropy < BURST_LIMITS.minAvgEntropy && thoughts.length >= 5) {
113
+ warnings.push(`Low diversity: ${avgEntropy.toFixed(2)} < ${BURST_LIMITS.minAvgEntropy}`);
114
+ }
115
+ if (avgConfidence < BURST_LIMITS.minAvgConfidence && confidenceCount > 0) {
116
+ warnings.push(`Low confidence: ${avgConfidence.toFixed(1)} < ${BURST_LIMITS.minAvgConfidence}`);
117
+ }
118
+ // === PHASE 6: Consolidation Validation ===
119
+ if (consolidation) {
120
+ this.validateConsolidation(consolidation, thoughts, errors, warnings);
121
+ }
122
+ const metrics = {
123
+ avgConfidence: Math.round(avgConfidence * 10) / 10,
124
+ avgEntropy: Math.round(avgEntropy * 100) / 100,
125
+ avgLength: Math.round(avgLength),
126
+ stagnationScore: Math.round(stagnationScore * 100) / 100,
127
+ thoughtCount: thoughts.length,
128
+ };
129
+ return {
130
+ passed: errors.length === 0,
131
+ errors,
132
+ warnings,
133
+ metrics,
134
+ sortedThoughts: errors.length === 0 ? sortedThoughts : undefined,
135
+ };
136
+ }
137
+ /**
138
+ * Validate consolidation data
139
+ */
140
+ validateConsolidation(consolidation, thoughts, errors, warnings) {
141
+ const { winningPath, verdict } = consolidation;
142
+ // Validate path references
143
+ const thoughtNumbers = new Set(thoughts.map(t => t.thoughtNumber));
144
+ const invalidRefs = winningPath.filter(n => !thoughtNumbers.has(n));
145
+ if (invalidRefs.length > 0) {
146
+ errors.push(`Invalid winning path references: ${invalidRefs.join(', ')}`);
147
+ }
148
+ // Validate path connectivity (WARNING, not ERROR)
149
+ if (winningPath.length > 1 && invalidRefs.length === 0) {
150
+ const thoughtMap = new Map(thoughts.map(t => [t.thoughtNumber, t]));
151
+ const pathGaps = [];
152
+ for (let i = 1; i < winningPath.length; i++) {
153
+ const current = winningPath[i];
154
+ const previous = winningPath[i - 1];
155
+ const currentThought = thoughtMap.get(current);
156
+ if (!currentThought)
157
+ continue;
158
+ const validPredecessors = new Set([current - 1]);
159
+ if (currentThought.branchFromThought)
160
+ validPredecessors.add(currentThought.branchFromThought);
161
+ if (currentThought.isRevision && currentThought.revisesThought) {
162
+ validPredecessors.add(currentThought.revisesThought);
163
+ validPredecessors.add(currentThought.revisesThought - 1);
164
+ }
165
+ if (!validPredecessors.has(previous)) {
166
+ pathGaps.push(`#${previous}โ†’#${current}`);
167
+ }
168
+ }
169
+ if (pathGaps.length > 0) {
170
+ warnings.push(`Path gaps: ${pathGaps.join(', ')}. Use branches or include intermediate thoughts.`);
171
+ }
172
+ }
173
+ // Check for unaddressed blockers
174
+ if (verdict === 'ready') {
175
+ for (const num of winningPath) {
176
+ const thought = thoughts.find(t => t.thoughtNumber === num);
177
+ if (thought?.extensions) {
178
+ const hasBlocker = thought.extensions.some(e => e.impact === 'blocker');
179
+ if (hasBlocker) {
180
+ const hasRevision = thoughts.some(t => t.isRevision && t.revisesThought === num);
181
+ if (!hasRevision) {
182
+ errors.push(`Blocker in #${num} unresolved - cannot mark ready`);
183
+ }
184
+ }
185
+ }
186
+ }
187
+ }
188
+ }
189
+ /**
190
+ * Convert BurstThought to ThoughtRecord
191
+ */
192
+ toThoughtRecord(t, totalThoughts, sessionId) {
193
+ return {
194
+ thought: t.thought.substring(0, BURST_LIMITS.maxThoughtLength),
195
+ thoughtNumber: t.thoughtNumber,
196
+ totalThoughts,
197
+ nextThoughtNeeded: t.thoughtNumber < totalThoughts,
198
+ confidence: t.confidence,
199
+ subSteps: t.subSteps,
200
+ alternatives: t.alternatives,
201
+ isRevision: t.isRevision,
202
+ revisesThought: t.revisesThought,
203
+ branchFromThought: t.branchFromThought,
204
+ branchId: t.branchId,
205
+ timestamp: Date.now(),
206
+ sessionId,
207
+ extensions: t.extensions?.map(e => ({
208
+ type: e.type,
209
+ content: e.content,
210
+ impact: e.impact ?? 'medium',
211
+ timestamp: new Date().toISOString(),
212
+ })),
213
+ };
214
+ }
215
+ }
@@ -0,0 +1,38 @@
1
+ /**
2
+ * CoachingService - Proactive coaching and lateral thinking triggers
3
+ * Stateful service - owns recentAdvices for cooldown tracking
4
+ */
5
+ import type { ThoughtInput, ThoughtRecord } from '../types/thought.types.js';
6
+ export declare class CoachingService {
7
+ /** Coach cooldown - track recent advices to prevent spam */
8
+ private recentAdvices;
9
+ /**
10
+ * Reset coaching state (call on session reset)
11
+ */
12
+ reset(): void;
13
+ /**
14
+ * Add advice with cooldown - prevents spam of same advice
15
+ * Returns true if advice was added, false if on cooldown
16
+ */
17
+ addAdviceWithCooldown(advice: string, nudges: string[]): boolean;
18
+ /**
19
+ * LATERAL THINKING TRIGGER with escalating pressure
20
+ * @param sessionThoughts - Current session thoughts
21
+ * @param branches - Map of branch ID to branch thoughts
22
+ * @param isFinishing - True if nextThoughtNeeded=false (v5.0.1)
23
+ */
24
+ checkLateralThinking(sessionThoughts: ThoughtRecord[], branches: Map<string, ThoughtRecord[]>, isFinishing?: boolean): string | undefined;
25
+ /**
26
+ * PROACTIVE COACH - Analyzes thought content and recommends strategic lenses
27
+ * v5.0.1: Returns undefined if confidence >= 8 (no need for coaching)
28
+ */
29
+ generateProactiveCoachAdvice(sessionThoughts: ThoughtRecord[]): string | undefined;
30
+ /**
31
+ * PROACTIVE NUDGES - Enhanced coaching based on current thought
32
+ */
33
+ generateProactiveNudges(input: ThoughtInput, sessionThoughts: ThoughtRecord[]): string | undefined;
34
+ /**
35
+ * PRE-CONSOLIDATION AUDIT - Quality gate before finishing session
36
+ */
37
+ performPreConsolidationAudit(sessionThoughts: ThoughtRecord[]): string | undefined;
38
+ }
@@ -0,0 +1,258 @@
1
+ /**
2
+ * CoachingService - Proactive coaching and lateral thinking triggers
3
+ * Stateful service - owns recentAdvices for cooldown tracking
4
+ */
5
+ import { LINEAR_THINKING_THRESHOLD, ESCALATING_PRESSURE_INTERVAL, MAX_THOUGHTS_BUDGET, POLISH_THRESHOLD_CONFIDENCE, INNOVATION_THRESHOLD_THOUGHTS, DEPTH_METRIC_SIMPLE, DEPTH_METRIC_MEDIUM, DEPTH_METRIC_COMPLEX, MIN_THOUGHT_LENGTH, LOW_CONFIDENCE_THRESHOLD, NO_CRITIQUE_THRESHOLD, COACH_COOLDOWN_COUNT, SMART_PRUNING_THRESHOLD, NEAR_LIMIT_CONFIDENCE_THRESHOLD, MIN_ENTROPY_THRESHOLD, } from '../constants/index.js';
6
+ import { OPTIMIZATION_TRIGGERS, UNCERTAINTY_TRIGGERS } from '../constants/index.js';
7
+ import { calculateWordEntropy } from '../utils/index.js';
8
+ export class CoachingService {
9
+ /** Coach cooldown - track recent advices to prevent spam */
10
+ recentAdvices = [];
11
+ /**
12
+ * Reset coaching state (call on session reset)
13
+ */
14
+ reset() {
15
+ this.recentAdvices = [];
16
+ }
17
+ /**
18
+ * Add advice with cooldown - prevents spam of same advice
19
+ * Returns true if advice was added, false if on cooldown
20
+ */
21
+ addAdviceWithCooldown(advice, nudges) {
22
+ const adviceKey = advice.substring(0, 30);
23
+ if (this.recentAdvices.includes(adviceKey)) {
24
+ return false;
25
+ }
26
+ nudges.push(advice);
27
+ this.recentAdvices.push(adviceKey);
28
+ if (this.recentAdvices.length > COACH_COOLDOWN_COUNT) {
29
+ this.recentAdvices.shift();
30
+ }
31
+ return true;
32
+ }
33
+ /**
34
+ * LATERAL THINKING TRIGGER with escalating pressure
35
+ * @param sessionThoughts - Current session thoughts
36
+ * @param branches - Map of branch ID to branch thoughts
37
+ * @param isFinishing - True if nextThoughtNeeded=false (v5.0.1)
38
+ */
39
+ checkLateralThinking(sessionThoughts, branches, isFinishing = false) {
40
+ const thoughtCount = sessionThoughts.length;
41
+ const advices = [];
42
+ // Self-checklist: remind about subSteps ONLY when finishing session (v5.0.1)
43
+ if (isFinishing && thoughtCount >= 2) {
44
+ const prevThought = sessionThoughts[thoughtCount - 2];
45
+ if (prevThought.subSteps && prevThought.subSteps.length > 0) {
46
+ advices.push(`๐Ÿ“‹ SELF-CHECK: #${prevThought.thoughtNumber} had ${prevThought.subSteps.length} sub-steps. All completed?`);
47
+ }
48
+ }
49
+ // Check for forgotten branches
50
+ if (branches.size > 0 && thoughtCount > 3) {
51
+ const recentThoughts = sessionThoughts.slice(-3);
52
+ const recentBranchIds = new Set(recentThoughts.filter((t) => t.branchId).map((t) => t.branchId));
53
+ for (const branchId of branches.keys()) {
54
+ if (!recentBranchIds.has(branchId)) {
55
+ advices.push(`๐ŸŒฟ FORGOTTEN: Branch "${branchId}" untouched 3+ thoughts. Integrate or close.`);
56
+ break;
57
+ }
58
+ }
59
+ }
60
+ // Check for declining entropy
61
+ if (thoughtCount >= 3) {
62
+ const recentThoughts = sessionThoughts.slice(-3);
63
+ const entropies = recentThoughts.map((t) => calculateWordEntropy(t.thought));
64
+ const avgEntropy = entropies.reduce((a, b) => a + b, 0) / entropies.length;
65
+ const isDecreasing = entropies[2] < entropies[1] && entropies[1] < entropies[0];
66
+ if (avgEntropy < MIN_ENTROPY_THRESHOLD || (isDecreasing && entropies[2] < 0.3)) {
67
+ advices.push(`๐Ÿ“‰ ENTROPY: Vocabulary diversity low (${avgEntropy.toFixed(2)}). Rephrase with different concepts.`);
68
+ }
69
+ }
70
+ // Linear thinking check
71
+ if (thoughtCount >= LINEAR_THINKING_THRESHOLD) {
72
+ const hasExtensions = sessionThoughts.some((t) => t.extensions && t.extensions.length > 0);
73
+ const hasBranches = sessionThoughts.some((t) => t.branchFromThought !== undefined);
74
+ if (!hasExtensions && !hasBranches) {
75
+ const pressureLevel = Math.floor((thoughtCount - LINEAR_THINKING_THRESHOLD) / ESCALATING_PRESSURE_INTERVAL) + 1;
76
+ if (pressureLevel === 1) {
77
+ advices.push('๐Ÿ’ก LINEAR: Consider quickExtension:critique or think_diverge.');
78
+ }
79
+ else if (pressureLevel === 2) {
80
+ advices.push('โš ๏ธ LINEAR: STRONGLY use quickExtension:assumption_testing.');
81
+ }
82
+ else {
83
+ advices.push(`๐Ÿšจ CRITICAL: ${thoughtCount} thoughts, ZERO lateral. STOP and critique.`);
84
+ }
85
+ }
86
+ }
87
+ // Complexity Budget
88
+ if (thoughtCount >= MAX_THOUGHTS_BUDGET) {
89
+ const overBudget = thoughtCount - MAX_THOUGHTS_BUDGET;
90
+ if (overBudget === 0) {
91
+ advices.push(`๐Ÿ’ฐ BUDGET: ${MAX_THOUGHTS_BUDGET} thoughts. Consolidate.`);
92
+ }
93
+ else if (overBudget <= 3) {
94
+ advices.push(`โš ๏ธ OVER BUDGET: ${thoughtCount} thoughts. Consolidate NOW.`);
95
+ }
96
+ else {
97
+ advices.push(`๐Ÿšจ PARALYSIS: ${thoughtCount} thoughts. STOP. Consolidate immediately.`);
98
+ }
99
+ }
100
+ // Proactive coach advice
101
+ const coachAdvice = this.generateProactiveCoachAdvice(sessionThoughts);
102
+ if (coachAdvice) {
103
+ advices.push(coachAdvice);
104
+ }
105
+ return advices.length > 0 ? advices.join('\n') : undefined;
106
+ }
107
+ /**
108
+ * PROACTIVE COACH - Analyzes thought content and recommends strategic lenses
109
+ * v5.0.1: Returns undefined if confidence >= 8 (no need for coaching)
110
+ */
111
+ generateProactiveCoachAdvice(sessionThoughts) {
112
+ if (sessionThoughts.length === 0)
113
+ return undefined;
114
+ const lastThought = sessionThoughts[sessionThoughts.length - 1];
115
+ // v5.0.1: Skip coaching if high confidence - user knows what they're doing
116
+ if (lastThought.confidence && lastThought.confidence >= 8) {
117
+ return undefined;
118
+ }
119
+ const allContent = sessionThoughts.map((t) => t.thought.toLowerCase()).join(' ');
120
+ const lastContent = lastThought.thought.toLowerCase();
121
+ const existingExtensions = new Set();
122
+ sessionThoughts.forEach((t) => {
123
+ t.extensions?.forEach((e) => existingExtensions.add(e.type));
124
+ });
125
+ // OPTIMIZATION recommendation
126
+ if (!existingExtensions.has('optimization')) {
127
+ const hasOptimizationTrigger = OPTIMIZATION_TRIGGERS.some((trigger) => lastContent.includes(trigger) || allContent.includes(trigger));
128
+ if (hasOptimizationTrigger) {
129
+ return '๐ŸŽฏ COACH: TODO/perf detected. Use quickExtension:optimization';
130
+ }
131
+ }
132
+ // ASSUMPTION TESTING recommendation
133
+ if (!existingExtensions.has('assumption_testing')) {
134
+ const uncertaintyCount = UNCERTAINTY_TRIGGERS.filter((trigger) => lastContent.includes(trigger)).length;
135
+ if (uncertaintyCount >= 2) {
136
+ return '๐ŸŽฏ COACH: Uncertain language. Use quickExtension:assumption_testing';
137
+ }
138
+ }
139
+ // POLISH recommendation
140
+ if (!existingExtensions.has('polish')) {
141
+ const isNearEnd = lastThought.thoughtNumber >= lastThought.totalThoughts - 1;
142
+ const hasHighConfidence = lastThought.confidence && lastThought.confidence >= POLISH_THRESHOLD_CONFIDENCE;
143
+ if (isNearEnd && hasHighConfidence) {
144
+ return '๐ŸŽฏ COACH: High confidence. Use quickExtension:polish';
145
+ }
146
+ }
147
+ // INNOVATION recommendation
148
+ if (!existingExtensions.has('innovation') && sessionThoughts.length >= INNOVATION_THRESHOLD_THOUGHTS) {
149
+ const hasBranches = sessionThoughts.some((t) => t.branchFromThought !== undefined);
150
+ if (!hasBranches) {
151
+ return '๐ŸŽฏ COACH: Long session. Use quickExtension:innovation';
152
+ }
153
+ }
154
+ return undefined;
155
+ }
156
+ /**
157
+ * PROACTIVE NUDGES - Enhanced coaching based on current thought
158
+ */
159
+ generateProactiveNudges(input, sessionThoughts) {
160
+ const nudges = [];
161
+ // Short thought detection
162
+ if (input.thought.length < MIN_THOUGHT_LENGTH && input.nextThoughtNeeded) {
163
+ this.addAdviceWithCooldown(`โš ๏ธ SHORT: ${input.thought.length} chars. Expand.`, nudges);
164
+ }
165
+ // Low confidence nudge
166
+ if (input.confidence && input.confidence < LOW_CONFIDENCE_THRESHOLD) {
167
+ this.addAdviceWithCooldown(`๐Ÿ’ก LOW CONFIDENCE (${input.confidence}/10): STOP. Do not proceed linearly. Use think_diverge to explore 3 alternatives.`, nudges);
168
+ }
169
+ // Missing critique check
170
+ if (sessionThoughts.length >= NO_CRITIQUE_THRESHOLD) {
171
+ const hasCritique = sessionThoughts.some((t) => t.extensions?.some((e) => e.type === 'critique'));
172
+ if (!hasCritique) {
173
+ this.addAdviceWithCooldown(`๐Ÿง NO CRITIQUE: ${sessionThoughts.length} thoughts. Use quickExtension:critique`, nudges);
174
+ }
175
+ }
176
+ // Smart pruning reminder
177
+ if (sessionThoughts.length >= SMART_PRUNING_THRESHOLD) {
178
+ this.addAdviceWithCooldown(`๐Ÿงน LONG (${sessionThoughts.length} thoughts): Auto-pruning. Consolidate soon.`, nudges);
179
+ }
180
+ // Near-limit warning
181
+ if (input.thoughtNumber >= input.totalThoughts - 1 &&
182
+ input.confidence &&
183
+ input.confidence < NEAR_LIMIT_CONFIDENCE_THRESHOLD) {
184
+ this.addAdviceWithCooldown(`โš ๏ธ NEAR LIMIT: ${input.thoughtNumber}/${input.totalThoughts}, confidence ${input.confidence}/10.`, nudges);
185
+ }
186
+ // Context7 Integration (v6.0)
187
+ // If thought contains technical keywords but no Context7 context, prompt for it
188
+ const techKeywords = ['react', 'prisma', 'api', 'auth', 'database', 'deploy'];
189
+ const hasTechKeyword = techKeywords.some(k => input.thought.toLowerCase().includes(k));
190
+ // Simple heuristic: if talking tech but confidence < 8, suggest docs
191
+ if (hasTechKeyword && input.confidence && input.confidence < 8) {
192
+ // Check if we haven't already nudged about docs recently is handled by cooldown
193
+ this.addAdviceWithCooldown(`๐Ÿ“š DOCS: Technical topic detected with <8 confidence. Use 'context7' to check official docs.`, nudges);
194
+ }
195
+ return nudges.length > 0 ? nudges.join('\n') : undefined;
196
+ }
197
+ /**
198
+ * PRE-CONSOLIDATION AUDIT - Quality gate before finishing session
199
+ */
200
+ performPreConsolidationAudit(sessionThoughts) {
201
+ if (sessionThoughts.length === 0)
202
+ return undefined;
203
+ const auditWarnings = [];
204
+ // SUBSTEPS COMPLETION CHECK
205
+ const allSubSteps = [];
206
+ sessionThoughts.forEach((t) => {
207
+ if (t.subSteps && t.subSteps.length > 0) {
208
+ allSubSteps.push({ thoughtNum: t.thoughtNumber, steps: t.subSteps });
209
+ }
210
+ });
211
+ if (allSubSteps.length > 0) {
212
+ const totalSteps = allSubSteps.reduce((sum, s) => sum + s.steps.length, 0);
213
+ const thoughtsWithSteps = allSubSteps.map((s) => `#${s.thoughtNum}`).join(', ');
214
+ auditWarnings.push(`๐Ÿ“‹ SUBSTEPS: ${totalSteps} defined in ${thoughtsWithSteps}. Verify completion.`);
215
+ }
216
+ // DEPTH METRIC CHECK
217
+ const avgLength = sessionThoughts.reduce((sum, t) => sum + t.thought.length, 0) / sessionThoughts.length;
218
+ const thoughtCount = sessionThoughts.length;
219
+ let requiredDepth;
220
+ let complexityLevel;
221
+ if (thoughtCount <= 5) {
222
+ requiredDepth = DEPTH_METRIC_SIMPLE;
223
+ complexityLevel = 'simple';
224
+ }
225
+ else if (thoughtCount <= 10) {
226
+ requiredDepth = DEPTH_METRIC_MEDIUM;
227
+ complexityLevel = 'medium';
228
+ }
229
+ else {
230
+ requiredDepth = DEPTH_METRIC_COMPLEX;
231
+ complexityLevel = 'complex';
232
+ }
233
+ if (avgLength < requiredDepth) {
234
+ auditWarnings.push(`๐Ÿ”ฌ DEPTH: ${Math.round(avgLength)} chars < ${requiredDepth} for ${complexityLevel} task.`);
235
+ }
236
+ // BLOCKER GATE CHECK
237
+ const unresolvedBlockers = [];
238
+ sessionThoughts.forEach((t) => {
239
+ if (t.extensions) {
240
+ const hasBlocker = t.extensions.some((e) => e.impact === 'blocker' || (e.impact === 'high' && e.type === 'critique'));
241
+ if (hasBlocker) {
242
+ const hasRevision = sessionThoughts.some((rev) => rev.isRevision && rev.revisesThought === t.thoughtNumber);
243
+ if (!hasRevision) {
244
+ unresolvedBlockers.push(t.thoughtNumber);
245
+ }
246
+ }
247
+ }
248
+ });
249
+ if (unresolvedBlockers.length > 0) {
250
+ auditWarnings.push(`๐Ÿ›‘ BLOCKERS: #${unresolvedBlockers.join(', ')} unresolved.`);
251
+ }
252
+ if (auditWarnings.length > 0) {
253
+ auditWarnings.unshift('โšก PRE-CONSOLIDATION AUDIT:');
254
+ auditWarnings.push('๐Ÿ’ก Address items or call think_done.');
255
+ }
256
+ return auditWarnings.length > 0 ? auditWarnings.join('\n') : undefined;
257
+ }
258
+ }
@@ -0,0 +1,17 @@
1
+ /**
2
+ * ConsolidateService - Meta-cognitive audit for thinking sessions
3
+ * Stateless service - receives data as parameters
4
+ */
5
+ import type { ThoughtRecord, ConsolidateInput, ConsolidateResult } from '../types/thought.types.js';
6
+ export declare class ConsolidateService {
7
+ private validationService;
8
+ /**
9
+ * Consolidate and verify the thinking process (meta-cognitive audit)
10
+ * Forces model to synthesize, cross-check, and find contradictions
11
+ * @param input - Consolidation input with winningPath, summary, verdict
12
+ * @param sessionThoughts - Current session thoughts
13
+ * @param onDeadEnd - Callback to record dead end when verdict is needs_more_work
14
+ * @param onSuccess - Callback to save insight when verdict is ready and can proceed (v4.1.0)
15
+ */
16
+ consolidate(input: ConsolidateInput, sessionThoughts: ThoughtRecord[], onDeadEnd?: (path: number[], reason: string) => void, onSuccess?: (path: number[], summary: string) => void): ConsolidateResult;
17
+ }