Initial commit: RA-H Open Source Edition

Local-first knowledge management system with BYO API keys.

Features:
- 3-panel UI (Nodes | Focus | Helpers)
- SQLite + sqlite-vec for vector search
- Agent system (Easy/Hard mode orchestrators)
- Content extraction (YouTube, PDF, web)
- Integrate workflow for connection discovery
- Dimension system with auto-assignment

Tech stack:
- Next.js 15 + TypeScript + Tailwind CSS
- Anthropic (Claude) + OpenAI (GPT) via Vercel AI SDK

Setup:
  npm install && npm rebuild better-sqlite3
  scripts/dev/bootstrap-local.sh
  npm run dev

MIT License
This commit is contained in:
“BeeRad”
2025-12-15 16:14:28 +11:00
commit 733d1c3407
226 changed files with 46231 additions and 0 deletions
+71
View File
@@ -0,0 +1,71 @@
import { tool } from 'ai';
import { z } from 'zod';
import { formatNodeForChat } from '../infrastructure/nodeFormatter';
export const createNodeTool = tool({
description: 'Create node with title/content/link and optional dimensions (locked dimensions auto-assigned)',
inputSchema: z.object({
title: z.string().describe('The title of the node'),
content: z.string().optional().describe('The main content, description, or notes for this node'),
link: z.string().optional().describe('A URL link to the source'),
dimensions: z
.array(z.string())
.max(5)
.optional()
.describe('Optional dimension tags to apply to this node (0-5 items). Locked dimensions will be auto-assigned.'),
chunk: z.string().optional().describe('Raw content for later processing - CRITICAL for extracted content from URLs'),
metadata: z.record(z.any()).optional().describe('Additional metadata like source info, extraction details, etc.')
}),
execute: async (params) => {
console.log('🎯 CreateNode tool called with params:', JSON.stringify(params, null, 2));
try {
const rawDimensions = params.dimensions || [];
const trimmedDimensions = rawDimensions
.map(d => typeof d === 'string' ? d.trim() : '')
.filter(Boolean)
.slice(0, 5); // Limit to 5 dimensions max
// Call the nodes API endpoint
const response = await fetch(`${process.env.NEXT_PUBLIC_BASE_URL || 'http://localhost:3000'}/api/nodes`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ ...params, dimensions: trimmedDimensions })
});
const result = await response.json();
if (!response.ok) {
return {
success: false,
error: result.error || 'Failed to create node',
data: null
};
}
// Format the created node for chat display
const formattedDisplay = formatNodeForChat({
id: result.data.id,
title: result.data.title,
dimensions: result.data.dimensions || trimmedDimensions
});
return {
success: true,
data: {
...result.data,
formatted_display: formattedDisplay
},
message: `Created node ${formattedDisplay} with dimensions: ${result.data.dimensions ? result.data.dimensions.join(', ') : 'auto-assigned'}`
};
} catch (error) {
return {
success: false,
error: error instanceof Error ? error.message : 'Failed to create node',
data: null
};
}
}
});
// Legacy export for backwards compatibility
export const createItemTool = createNodeTool;