sync: Idea Genealogy + Quick Capture Overhaul

From private repo:

Idea Genealogy:
- Required edge explanations across UI/tools/API/MCP
- Inferred edge schema (category/type/confidence)
- Added part_of relationship type
- Connection UI chips (Made by / Part of / Came from / Related)

Quick Capture Overhaul:
- Jina.ai fallback for JS-rendered sites (Twitter, SPAs)
- Simplified UI: removed mode buttons, auto-detect input type
- Renamed to "Add Stuff", moved to top-right
- Auto-edge creation from description entities
- Increased content summary length (500→1500 chars)

Description Generation Fix:
- Simplified prompt to "what is this"
- Added link/dimensions context
- Better creator attribution

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
“BeeRad”
2026-01-14 17:08:26 +11:00
co-authored by Claude Opus 4.5
parent f7b8b2058c
commit 2cbc950fd4
21 changed files with 1296 additions and 389 deletions
+18 -21
View File
@@ -159,8 +159,7 @@ const getNodesOutputSchema = {
const createEdgeInputSchema = {
sourceId: z.number().int().positive().describe('Source node ID'),
targetId: z.number().int().positive().describe('Target node ID'),
type: z.string().optional().describe('Edge type/label'),
weight: z.number().min(0).max(1).optional().describe('Edge weight 0-1')
explanation: z.string().min(1).describe('REQUIRED: Why does this connection exist? Be specific.')
};
const createEdgeOutputSchema = {
@@ -191,8 +190,7 @@ const queryEdgesOutputSchema = {
// rah_update_edge schemas
const updateEdgeInputSchema = {
id: z.number().int().positive().describe('Edge ID to update'),
type: z.string().optional().describe('New edge type/label'),
weight: z.number().min(0).max(1).optional().describe('New edge weight 0-1')
explanation: z.string().min(1).optional().describe('New explanation text (will re-infer relationship type)')
};
const updateEdgeOutputSchema = {
@@ -553,12 +551,13 @@ mcpServer.registerTool(
inputSchema: createEdgeInputSchema,
outputSchema: createEdgeOutputSchema
},
async ({ sourceId, targetId, type, weight }) => {
async ({ sourceId, targetId, explanation }) => {
const payload = {
source_id: sourceId,
target_id: targetId,
type: type || 'related',
weight: weight ?? 0.5
from_node_id: sourceId,
to_node_id: targetId,
explanation: explanation.trim(),
source: 'helper_name',
created_via: 'mcp'
};
const result = await callRaHApi('/api/edges', {
@@ -602,10 +601,10 @@ mcpServer.registerTool(
count: edges.length,
edges: edges.map(e => ({
id: e.id,
source_id: e.source_id,
target_id: e.target_id,
type: e.type ?? null,
weight: e.weight ?? null
source_id: e.from_node_id,
target_id: e.to_node_id,
type: e.context?.type ?? null,
weight: typeof e.context?.confidence === 'number' ? e.context.confidence : null
}))
}
};
@@ -620,18 +619,16 @@ mcpServer.registerTool(
inputSchema: updateEdgeInputSchema,
outputSchema: updateEdgeOutputSchema
},
async ({ id, type, weight }) => {
const payload = {};
if (type !== undefined) payload.type = type;
if (weight !== undefined) payload.weight = weight;
if (Object.keys(payload).length === 0) {
throw new McpError(ErrorCode.InvalidParams, 'At least one field (type or weight) must be provided.');
async ({ id, explanation }) => {
if (typeof explanation !== 'string' || explanation.trim().length === 0) {
throw new McpError(ErrorCode.InvalidParams, 'explanation is required.');
}
const result = await callRaHApi(`/api/edges/${id}`, {
method: 'PUT',
body: JSON.stringify(payload)
body: JSON.stringify({
context: { explanation: explanation.trim(), created_via: 'mcp' }
})
});
return {
+12 -15
View File
@@ -108,8 +108,7 @@ const getNodesOutputSchema = {
const createEdgeInputSchema = {
sourceId: z.number().int().positive().describe('Source node ID'),
targetId: z.number().int().positive().describe('Target node ID'),
type: z.string().optional().describe('Edge type/label'),
weight: z.number().min(0).max(1).optional().describe('Edge weight 0-1')
explanation: z.string().min(1).describe('REQUIRED: Why does this connection exist? Be specific.')
};
const createEdgeOutputSchema = {
@@ -140,8 +139,7 @@ const queryEdgesOutputSchema = {
// rah_update_edge schemas
const updateEdgeInputSchema = {
id: z.number().int().positive().describe('Edge ID to update'),
type: z.string().optional().describe('New edge type/label'),
weight: z.number().min(0).max(1).optional().describe('New edge weight 0-1')
explanation: z.string().min(1).optional().describe('New explanation text (will re-infer relationship type)')
};
const updateEdgeOutputSchema = {
@@ -525,12 +523,13 @@ server.registerTool(
inputSchema: createEdgeInputSchema,
outputSchema: createEdgeOutputSchema
},
async ({ sourceId, targetId, type, weight }) => {
async ({ sourceId, targetId, explanation }) => {
const payload = {
from_node_id: sourceId,
to_node_id: targetId,
type: type || 'related',
weight: weight ?? 0.5
explanation: explanation.trim(),
source: 'helper_name',
created_via: 'mcp'
};
const result = await callRaHApi('/api/edges', {
@@ -592,18 +591,16 @@ server.registerTool(
inputSchema: updateEdgeInputSchema,
outputSchema: updateEdgeOutputSchema
},
async ({ id, type, weight }) => {
const payload = {};
if (type !== undefined) payload.type = type;
if (weight !== undefined) payload.weight = weight;
if (Object.keys(payload).length === 0) {
throw new Error('At least one field (type or weight) must be provided.');
async ({ id, explanation }) => {
if (typeof explanation !== 'string' || explanation.trim().length === 0) {
throw new Error('explanation is required');
}
const result = await callRaHApi(`/api/edges/${id}`, {
method: 'PUT',
body: JSON.stringify(payload)
body: JSON.stringify({
context: { explanation: explanation.trim(), created_via: 'mcp' }
})
});
return {