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

279 lines 9.24 kB
/** * Citation service for formatting and validating citations * * @module research/services/citation */ import { promises as fs } from 'fs'; /** * Citation service for managing citations and networks */ export class CitationService { corpusPath; claimsIndexPath; constructor(config = {}) { this.corpusPath = config.corpusPath || '.aiwg/research/sources'; this.claimsIndexPath = config.claimsIndexPath || '.aiwg/research/claims-index.json'; } /** * Format citation in specified style */ formatCitation(paper, style = 'apa') { switch (style) { case 'apa': return this.formatAPA(paper); case 'bibtex': return this.formatBibTeX(paper); case 'chicago': return this.formatChicago(paper); case 'mla': return this.formatMLA(paper); case 'ieee': return this.formatIEEE(paper); default: return this.formatAPA(paper); } } /** * Validate that a citation exists in corpus */ async validateCitation(refId, corpusPath) { const path = corpusPath || this.corpusPath; try { const files = await fs.readdir(path); return files.some((file) => file.startsWith(refId)); } catch (error) { return false; } } /** * Build citation network from papers */ buildNetwork(papers) { const nodes = papers.map((paper) => ({ refId: paper.id, title: paper.title, year: paper.year, citationCount: paper.citationCount || 0, })); // For now, return empty edges (full implementation would extract references) const edges = []; // Calculate simple metrics const sortedByCitations = [...nodes].sort((a, b) => b.citationCount - a.citationCount); const mostCitedPapers = sortedByCitations.slice(0, 5).map((n) => n.refId); return { nodes, edges, metrics: { mostCitedPapers, mostInfluentialPapers: mostCitedPapers, // Simplified clusterCenters: [], // Would require clustering algorithm }, }; } /** * Update claims index with new claim */ async updateClaimsIndex(claim, sources) { let index = {}; // Load existing index try { const data = await fs.readFile(this.claimsIndexPath, 'utf-8'); index = JSON.parse(data); } catch (error) { // Index doesn't exist yet } // Update or create claim entry if (index[claim]) { // Merge sources, remove duplicates const merged = [...new Set([...index[claim], ...sources])]; index[claim] = merged; } else { index[claim] = sources; } // Write back await fs.writeFile(this.claimsIndexPath, JSON.stringify(index, null, 2), 'utf-8'); } /** * Format citation in APA style */ formatAPA(paper) { const authors = this.formatAuthorsAPA(paper.authors.map((a) => a.name)); const year = paper.year || 'n.d.'; const title = paper.title; const venue = paper.venue || ''; const doi = paper.doi ? `https://doi.org/${paper.doi}` : ''; let citation = `${authors} (${year}). ${title}.`; if (venue) { citation += ` ${venue}.`; } if (doi) { citation += ` ${doi}`; } return citation; } /** * Format citation in BibTeX style */ formatBibTeX(paper) { const id = this.generateBibTeXId(paper); const authors = paper.authors.map((a) => a.name).join(' and '); const year = paper.year || ''; const title = paper.title; const venue = paper.venue || ''; const doi = paper.doi || ''; const entryType = paper.type === 'journal' ? 'article' : 'inproceedings'; let bibtex = `@${entryType}{${id},\n`; bibtex += ` author = {${authors}},\n`; bibtex += ` title = {${title}},\n`; bibtex += ` year = {${year}},\n`; if (venue) { const field = entryType === 'article' ? 'journal' : 'booktitle'; bibtex += ` ${field} = {${venue}},\n`; } if (doi) { bibtex += ` doi = {${doi}},\n`; } bibtex += `}`; return bibtex; } /** * Format citation in Chicago style */ formatChicago(paper) { const authors = this.formatAuthorsChicago(paper.authors.map((a) => a.name)); const year = paper.year || 'n.d.'; const title = `"${paper.title}"`; const venue = paper.venue || ''; const doi = paper.doi ? `https://doi.org/${paper.doi}` : ''; let citation = `${authors}. ${year}. ${title}.`; if (venue) { citation += ` ${venue}.`; } if (doi) { citation += ` ${doi}`; } return citation; } /** * Format citation in MLA style */ formatMLA(paper) { const authors = this.formatAuthorsMLA(paper.authors.map((a) => a.name)); const title = `"${paper.title}"`; const venue = paper.venue || ''; const year = paper.year || 'n.d.'; let citation = `${authors}. ${title}.`; if (venue) { citation += ` ${venue},`; } citation += ` ${year}.`; return citation; } /** * Format citation in IEEE style */ formatIEEE(paper) { const authors = this.formatAuthorsIEEE(paper.authors.map((a) => a.name)); const title = `"${paper.title}"`; const venue = paper.venue ? `, ${paper.venue}` : ''; const year = paper.year || 'n.d.'; return `${authors}, ${title}${venue}, ${year}.`; } /** * Format authors in APA style */ formatAuthorsAPA(authors) { if (authors.length === 0) return 'Unknown'; if (authors.length === 1) return authors[0]; if (authors.length === 2) return `${authors[0]} & ${authors[1]}`; const lastAuthor = authors[authors.length - 1]; const otherAuthors = authors.slice(0, -1).join(', '); return `${otherAuthors}, & ${lastAuthor}`; } /** * Format authors in Chicago style */ formatAuthorsChicago(authors) { if (authors.length === 0) return 'Unknown'; if (authors.length === 1) return this.reverseAuthorName(authors[0]); if (authors.length === 2) { return `${this.reverseAuthorName(authors[0])} and ${authors[1]}`; } const firstAuthor = this.reverseAuthorName(authors[0]); const otherAuthors = authors.slice(1).join(', '); return `${firstAuthor}, ${otherAuthors}`; } /** * Format authors in MLA style */ formatAuthorsMLA(authors) { if (authors.length === 0) return 'Unknown'; if (authors.length === 1) return this.reverseAuthorName(authors[0]); if (authors.length === 2) { return `${this.reverseAuthorName(authors[0])}, and ${authors[1]}`; } const firstAuthor = this.reverseAuthorName(authors[0]); return `${firstAuthor}, et al.`; } /** * Format authors in IEEE style */ formatAuthorsIEEE(authors) { if (authors.length === 0) return 'Unknown'; const initials = authors.map((name) => { const parts = name.split(' '); if (parts.length === 1) return name; const lastName = parts[parts.length - 1]; const firstInitials = parts .slice(0, -1) .map((p) => p.charAt(0) + '.') .join(' '); return `${firstInitials} ${lastName}`; }); if (initials.length === 1) return initials[0]; if (initials.length === 2) return `${initials[0]} and ${initials[1]}`; const lastAuthor = initials[initials.length - 1]; const otherAuthors = initials.slice(0, -1).join(', '); return `${otherAuthors}, and ${lastAuthor}`; } /** * Reverse author name (First Last -> Last, First) */ reverseAuthorName(name) { const parts = name.split(' '); if (parts.length === 1) return name; const lastName = parts[parts.length - 1]; const firstName = parts.slice(0, -1).join(' '); return `${lastName}, ${firstName}`; } /** * Generate BibTeX citation ID */ generateBibTeXId(paper) { const author = paper.authors.length > 0 ? paper.authors[0].name.split(' ').pop()?.toLowerCase() : 'unknown'; const year = paper.year || 'unknown'; const titleWord = paper.title .toLowerCase() .replace(/[^\w\s]/g, '') .split(/\s+/)[0]; return `${author}${year}${titleWord}`; } } //# sourceMappingURL=citation.js.map