Skip to content

Commit 1bc6455

Browse files
committed
Enhance core layer functions for CLI/API parity
Refactor core business logic to support both CLI and REST API interfaces consistently. Changes: - src/core/nodes.ts: Enhanced node CRUD operations - src/core/edges.ts: Improved edge management functions - src/core/stats.ts: Updated statistics calculations - src/lib/scoring.ts: Refined hybrid scoring algorithm Ensures all features work identically across CLI and API interfaces following 3-layer architecture pattern. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
1 parent 6e4eeca commit 1bc6455

4 files changed

Lines changed: 19 additions & 12 deletions

File tree

src/core/edges.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -461,11 +461,11 @@ export async function sweepEdgesCore(
461461
let toSweep = suggestions;
462462

463463
if (options.minScore !== undefined) {
464-
toSweep = toSweep.filter((edge) => edge.score >= options.minScore);
464+
toSweep = toSweep.filter((edge) => edge.score >= options.minScore!);
465465
}
466466

467467
if (options.maxScore !== undefined) {
468-
toSweep = toSweep.filter((edge) => edge.score <= options.maxScore);
468+
toSweep = toSweep.filter((edge) => edge.score <= options.maxScore!);
469469
}
470470

471471
// Sort by score ascending (lowest first)

src/core/nodes.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -255,14 +255,14 @@ export async function createNodeCore(data: CreateNodeData): Promise<CreateNodeRe
255255
// Extract or use provided tags
256256
let tags = data.tags;
257257
if (!tags || tags.length === 0) {
258-
tags = extractTags(data.title ?? '', body);
258+
tags = extractTags(`${data.title ?? ''}\n${body}`);
259259
}
260260

261261
// Pick title if not provided
262262
const title = data.title ?? pickTitle(body);
263263

264264
// Tokenize
265-
const tokenCounts = tokenize(title, body);
265+
const tokenCounts = tokenize(`${title}\n${body}`);
266266

267267
// Create node record
268268
const now = new Date().toISOString();
@@ -334,7 +334,7 @@ export async function updateNodeCore(
334334
const tags = data.tags ?? existing.tags;
335335

336336
// Tokenize
337-
const tokenCounts = tokenize(title, body);
337+
const tokenCounts = tokenize(`${title}\n${body}`);
338338

339339
// Update node
340340
await dbUpdateNode(nodeId, {

src/core/stats.ts

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import { listEdges, listNodes, NodeRecord } from '../lib/db';
2-
import { buildGraph } from '../lib/graph';
32
import { describeSuggestion } from '../cli/shared/edges';
43

54
export type StatsResult = {
@@ -48,10 +47,16 @@ export async function getStats(options: StatsOptions = {}): Promise<StatsResult>
4847
const nodes = await listNodes();
4948
const edges = await listEdges('accepted');
5049
const allEdges = await listEdges('all');
51-
const graph = await buildGraph();
5250
const nodeMap = new Map(nodes.map((node) => [node.id, node]));
5351

54-
const degrees = nodes.map((node) => (graph.hasNode(node.id) ? graph.degree(node.id) : 0));
52+
// Compute degree stats from edge counts (faster than graph.degree())
53+
const nodeDegrees = new Map<string, number>();
54+
for (const edge of edges) {
55+
nodeDegrees.set(edge.sourceId, (nodeDegrees.get(edge.sourceId) ?? 0) + 1);
56+
nodeDegrees.set(edge.targetId, (nodeDegrees.get(edge.targetId) ?? 0) + 1);
57+
}
58+
59+
const degrees = nodes.map((node) => nodeDegrees.get(node.id) ?? 0);
5560
const sortedDegrees = [...degrees].sort((a, b) => a - b);
5661
const sumDegrees = degrees.reduce((acc, value) => acc + value, 0);
5762
const avg = degrees.length ? sumDegrees / degrees.length : 0;
@@ -100,7 +105,7 @@ export async function getStats(options: StatsOptions = {}): Promise<StatsResult>
100105
const highDegree = nodes
101106
.map((node) => ({
102107
node,
103-
degree: graph.hasNode(node.id) ? graph.degree(node.id) : 0,
108+
degree: nodeDegrees.get(node.id) ?? 0,
104109
}))
105110
.sort((a, b) => b.degree - a.degree)
106111
.slice(0, 5);

src/lib/scoring.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ export type ScoreComponents = {
66
tokenSimilarity: number;
77
titleSimilarity: number;
88
embeddingSimilarity: number;
9+
penalty: number;
910
};
1011

1112
const DEFAULT_AUTO_ACCEPT = 0.5;
@@ -36,8 +37,9 @@ export function computeScore(a: NodeRecord, b: NodeRecord): { score: number; com
3637
0.15 * tagOverlap +
3738
0.05 * titleSimilarity;
3839
// Penalize pairs with zero lexical/title overlap to avoid purely semantic weak links flooding suggestions
39-
if (tagOverlap === 0 && titleSimilarity === 0) score *= 0.9;
40-
return { score, components: { tagOverlap, tokenSimilarity, titleSimilarity, embeddingSimilarity } };
40+
const penalty = (tagOverlap === 0 && titleSimilarity === 0) ? 0.9 : 1.0;
41+
score *= penalty;
42+
return { score, components: { tagOverlap, tokenSimilarity, titleSimilarity, embeddingSimilarity, penalty } };
4143
}
4244

4345
export function classifyScore(score: number): EdgeStatus | 'discard' {
@@ -111,7 +113,7 @@ function titleCosine(a: string, b: string): number {
111113
return denom === 0 ? 0 : overlap / denom;
112114
}
113115

114-
function cosineEmbeddings(a?: number[], b?: number[]): number {
116+
export function cosineEmbeddings(a?: number[], b?: number[]): number {
115117
if (!a || !b || a.length === 0 || b.length === 0) return 0;
116118
const dim = Math.min(a.length, b.length);
117119
let dot = 0;

0 commit comments

Comments
 (0)