UNPKG

aiwg

Version:

Deployment tool and support utility for AI context. Copies agents, skills, commands, rules, and behaviors into the paths each AI platform reads (Claude Code, Codex, Copilot, Cursor, Warp, OpenClaw, and 6 more) so one source of truth works across 10 platfo

160 lines 5.76 kB
/** * Knowledge Base — load and search domain knowledge for grounding agents * * @module agents/grounding/knowledge-base * @issue #184 */ import { promises as fs } from 'fs'; import path from 'path'; // ============================================================================ // KnowledgeStore // ============================================================================ export class KnowledgeStore { bases; projectPath; constructor(projectPath) { this.projectPath = projectPath; this.bases = new Map(); } /** * Load knowledge bases from .aiwg/knowledge/ */ async load() { const knowledgeDir = path.join(this.projectPath, '.aiwg/knowledge'); try { const domains = await fs.readdir(knowledgeDir); for (const domain of domains) { const domainPath = path.join(knowledgeDir, domain); const stat = await fs.stat(domainPath); if (!stat.isDirectory()) continue; const files = await fs.readdir(domainPath); const entries = []; for (const file of files) { if (!file.endsWith('.json')) continue; try { const content = await fs.readFile(path.join(domainPath, file), 'utf-8'); const parsed = JSON.parse(content); if (Array.isArray(parsed.entries)) { entries.push(...parsed.entries); } else if (parsed.id) { entries.push(parsed); } } catch { // Skip malformed files } } if (entries.length > 0) { this.bases.set(domain, { domain, version: '1.0.0', entries, }); } } } catch { // Knowledge directory doesn't exist yet } } /** * Add a knowledge base from a JSON file */ async addFromFile(domain, filePath) { const content = await fs.readFile(filePath, 'utf-8'); const parsed = JSON.parse(content); const entries = Array.isArray(parsed.entries) ? parsed.entries : Array.isArray(parsed) ? parsed : [parsed]; const existing = this.bases.get(domain) || { domain, version: '1.0.0', entries: [] }; existing.entries.push(...entries); this.bases.set(domain, existing); // Persist const domainDir = path.join(this.projectPath, '.aiwg/knowledge', domain); await fs.mkdir(domainDir, { recursive: true }); const outFile = path.join(domainDir, path.basename(filePath)); await fs.copyFile(filePath, outFile); return entries.length; } /** * Search knowledge across all domains or a specific domain */ search(query, options) { const limit = options?.limit ?? 5; const queryTerms = query.toLowerCase().split(/\s+/).filter((t) => t.length > 2); const results = []; const domains = options?.domain ? [this.bases.get(options.domain)].filter(Boolean) : Array.from(this.bases.values()); for (const base of domains) { for (const entry of base.entries) { const score = calculateRelevance(entry, queryTerms); if (score > 0) { results.push({ content: entry.content, source: `${base.domain}/${entry.source || entry.id}`, relevance_score: score, metadata: { topic: entry.topic, id: entry.id }, }); } } } return results .sort((a, b) => b.relevance_score - a.relevance_score) .slice(0, limit); } /** * Verify a claim against domain knowledge */ verifyClaim(claim, domain) { const fragments = this.search(claim, { domain, limit: 3 }); if (fragments.length === 0) { return { claim, verified: false, confidence: 0, sources: [], correction: 'No relevant knowledge found in knowledge base', }; } // Simple verification: check if top fragments support or contradict const topFragment = fragments[0]; const confidence = Math.min(topFragment.relevance_score, 1.0); return { claim, verified: confidence > 0.3, confidence, sources: fragments.map((f) => f.source), }; } listDomains() { return Array.from(this.bases.keys()); } getDomainStats() { return Array.from(this.bases.entries()).map(([domain, base]) => ({ domain, entries: base.entries.length, })); } } // ============================================================================ // Helpers // ============================================================================ function calculateRelevance(entry, queryTerms) { const searchable = [ entry.topic, entry.content, ...(entry.keywords || []), ].join(' ').toLowerCase(); let matches = 0; for (const term of queryTerms) { if (searchable.includes(term)) { matches++; } } return queryTerms.length > 0 ? matches / queryTerms.length : 0; } //# sourceMappingURL=knowledge-base.js.map