@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.
- package/LICENSE +21 -0
- package/README.md +165 -0
- package/dist/constants/index.d.ts +5 -0
- package/dist/constants/index.js +5 -0
- package/dist/constants/patterns.d.ts +8 -0
- package/dist/constants/patterns.js +34 -0
- package/dist/constants/prompts.d.ts +1 -0
- package/dist/constants/prompts.js +44 -0
- package/dist/constants/thresholds.d.ts +39 -0
- package/dist/constants/thresholds.js +54 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.js +711 -0
- package/dist/services/burst.service.d.ts +39 -0
- package/dist/services/burst.service.js +215 -0
- package/dist/services/coaching.service.d.ts +38 -0
- package/dist/services/coaching.service.js +258 -0
- package/dist/services/consolidate.service.d.ts +17 -0
- package/dist/services/consolidate.service.js +236 -0
- package/dist/services/context.service.d.ts +15 -0
- package/dist/services/context.service.js +113 -0
- package/dist/services/export.service.d.ts +33 -0
- package/dist/services/export.service.js +129 -0
- package/dist/services/insights.service.d.ts +103 -0
- package/dist/services/insights.service.js +216 -0
- package/dist/services/logic.service.d.ts +28 -0
- package/dist/services/logic.service.js +467 -0
- package/dist/services/nudge.service.d.ts +20 -0
- package/dist/services/nudge.service.js +114 -0
- package/dist/services/recall.service.d.ts +38 -0
- package/dist/services/recall.service.js +188 -0
- package/dist/services/stagnation.service.d.ts +14 -0
- package/dist/services/stagnation.service.js +48 -0
- package/dist/services/thinking.service.d.ts +263 -0
- package/dist/services/thinking.service.js +1048 -0
- package/dist/services/validation.service.d.ts +45 -0
- package/dist/services/validation.service.js +260 -0
- package/dist/services/visualization.service.d.ts +23 -0
- package/dist/services/visualization.service.js +211 -0
- package/dist/types/thought.types.d.ts +486 -0
- package/dist/types/thought.types.js +5 -0
- package/dist/utils/index.d.ts +6 -0
- package/dist/utils/index.js +6 -0
- package/dist/utils/sensitive-data.d.ts +2 -0
- package/dist/utils/sensitive-data.js +28 -0
- package/dist/utils/storage-paths.d.ts +9 -0
- package/dist/utils/storage-paths.js +28 -0
- package/dist/utils/text-analysis.d.ts +30 -0
- package/dist/utils/text-analysis.js +92 -0
- 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
|
+
}
|