UseScraper MCP Server
- src
import chalk from 'chalk';
import { ThoughtBranch, ThoughtData, Insight, CrossReference, InsightType, CrossRefType, BranchingThoughtInput } from './types.js';
export class BranchManager {
private branches: Map<string, ThoughtBranch> = new Map();
private insightCounter = 0;
private thoughtCounter = 0;
private crossRefCounter = 0;
private activeBranchId: string | null = null;
generateId(prefix: string): string {
const timestamp = Date.now();
const random = Math.floor(Math.random() * 1000);
return `${prefix}-${timestamp}-${random}`;
}
createBranch(branchId: string, parentBranchId?: string): ThoughtBranch {
const branch: ThoughtBranch = {
id: branchId,
parentBranchId,
state: 'active',
priority: 1.0,
confidence: 1.0,
thoughts: [],
insights: [],
crossRefs: []
};
this.branches.set(branchId, branch);
// Set as active if it's the first branch
if (!this.activeBranchId) {
this.activeBranchId = branchId;
}
return branch;
}
private createInsight(
type: InsightType,
content: string,
context: string[],
parentInsights?: string[]
): Insight {
return {
id: `insight-${++this.insightCounter}`,
type,
content,
context,
parentInsights,
applicabilityScore: 1.0,
supportingEvidence: {}
};
}
private createCrossReference(
fromBranch: string,
toBranch: string,
type: CrossRefType,
reason: string,
strength: number
): CrossReference {
return {
id: `xref-${++this.crossRefCounter}`,
fromBranch,
toBranch,
type,
reason,
strength,
touchpoints: []
};
}
addThought(input: BranchingThoughtInput): ThoughtData {
// Use active branch if no branchId provided
const branchId = input.branchId || this.activeBranchId || this.generateId('branch');
let branch = this.branches.get(branchId);
if (!branch) {
branch = this.createBranch(branchId, input.parentBranchId);
}
const thought: ThoughtData = {
id: `thought-${++this.thoughtCounter}`,
content: input.content,
branchId: branch.id,
timestamp: new Date(),
metadata: {
type: input.type,
confidence: input.confidence || 1.0,
keyPoints: input.keyPoints || []
}
};
branch.thoughts.push(thought);
// Create insights if key points are provided
if (input.keyPoints) {
const insight = this.createInsight(
'observation',
`Identified key points: ${input.keyPoints.join(', ')}`,
[input.type],
input.relatedInsights
);
branch.insights.push(insight);
}
// Create cross references if specified
if (input.crossRefs) {
input.crossRefs.forEach(ref => {
const crossRef = this.createCrossReference(
branch!.id,
ref.toBranch,
ref.type,
ref.reason,
ref.strength
);
branch!.crossRefs.push(crossRef);
});
}
this.updateBranchMetrics(branch);
return thought;
}
private updateBranchMetrics(branch: ThoughtBranch): void {
const avgConfidence = branch.thoughts.reduce((sum, t) => sum + t.metadata.confidence, 0) / branch.thoughts.length;
const insightScore = branch.insights.length * 0.1;
const crossRefScore = branch.crossRefs.reduce((sum, ref) => sum + ref.strength, 0) * 0.1;
branch.priority = avgConfidence + insightScore + crossRefScore;
branch.confidence = avgConfidence;
}
getBranch(branchId: string): ThoughtBranch | undefined {
return this.branches.get(branchId);
}
getAllBranches(): ThoughtBranch[] {
return Array.from(this.branches.values());
}
getActiveBranch(): ThoughtBranch | undefined {
return this.activeBranchId ? this.branches.get(this.activeBranchId) : undefined;
}
setActiveBranch(branchId: string): void {
if (!this.branches.has(branchId)) {
throw new Error(`Branch ${branchId} not found`);
}
this.activeBranchId = branchId;
}
getBranchHistory(branchId: string): string {
const branch = this.branches.get(branchId);
if (!branch) {
throw new Error(`Branch ${branchId} not found`);
}
const header = chalk.blue(`History for branch: ${branchId} (${branch.state})`);
const timeline = branch.thoughts.map((t, i) => {
const timestamp = t.timestamp.toLocaleTimeString();
const number = chalk.gray(`${i + 1}.`);
const content = t.content;
const type = chalk.yellow(`[${t.metadata.type}]`);
const points = t.metadata.keyPoints.length > 0
? chalk.green(`\n Key Points: ${t.metadata.keyPoints.join(', ')}`)
: '';
return `${number} ${timestamp} ${type}\n ${content}${points}`;
}).join('\n\n');
const insights = branch.insights.map(i =>
chalk.yellow(`→ ${i.content}`)
).join('\n');
return `
┌─────────────────────────────────────────────
│ ${header}
├─────────────────────────────────────────────
${timeline}
${insights ? `
├─────────────────────────────────────────────
│ Insights:
${insights}` : ''}
└─────────────────────────────────────────────`;
}
formatBranchStatus(branch: ThoughtBranch): string {
const isActive = branch.id === this.activeBranchId;
const header = `${chalk.blue('Branch:')} ${branch.id} (${branch.state})${isActive ? chalk.green(' [ACTIVE]') : ''}`;
const stats = `Priority: ${branch.priority.toFixed(2)} | Confidence: ${branch.confidence.toFixed(2)}`;
const thoughts = branch.thoughts.map(t =>
` ${chalk.green('•')} ${t.content} (${t.metadata.type})`
).join('\n');
const insights = branch.insights.map(i =>
` ${chalk.yellow('→')} ${i.content}`
).join('\n');
const crossRefs = branch.crossRefs.map(r =>
` ${chalk.magenta('↔')} ${r.toBranch}: ${r.reason} (${r.strength.toFixed(2)})`
).join('\n');
return `
┌─────────────────────────────────────────────
│ ${header}
│ ${stats}
├─────────────────────────────────────────────
│ Thoughts:
${thoughts}
│ Insights:
${insights}
│ Cross References:
${crossRefs}
└─────────────────────────────────────────────`;
}
}