@compilr-dev/factory 0.1.15 → 0.1.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/research-model/bibliography.d.ts +26 -0
- package/dist/research-model/bibliography.js +299 -0
- package/dist/research-model/index.d.ts +2 -0
- package/dist/research-model/index.js +2 -0
- package/dist/research-model/tools.js +77 -0
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -24,5 +24,5 @@ export { staticLandingToolkit } from './toolkits/static-landing/index.js';
|
|
|
24
24
|
export { reactFastapiToolkit } from './toolkits/react-fastapi/index.js';
|
|
25
25
|
export { reactGoToolkit } from './toolkits/react-go/index.js';
|
|
26
26
|
export { factoryScaffoldSkill, factorySkills } from './factory/skill.js';
|
|
27
|
-
export type { ResearchModel, Section, SectionStatus, Claim, ClaimType, EvidenceStrength, SourceRef, Source, SourceRelevance, CitationInfo, CitationType, CitationStyle, ResearchQuestion, QuestionStatus, ResearchMeta, ResearchValidationError, ResearchValidationResult, ResearchModelOperation, ResearchModelToolsConfig, } from './research-model/index.js';
|
|
28
|
-
export { createDefaultResearchModel, createDefaultSection, createDefaultClaim, createDefaultSource, createDefaultQuestion, ResearchModelStore, applyResearchOperation, validateResearchModel, createResearchModelTools, } from './research-model/index.js';
|
|
27
|
+
export type { ResearchModel, Section, SectionStatus, Claim, ClaimType, EvidenceStrength, SourceRef, Source, SourceRelevance, CitationInfo, CitationType, CitationStyle, ResearchQuestion, QuestionStatus, ResearchMeta, ResearchValidationError, ResearchValidationResult, ResearchModelOperation, ResearchModelToolsConfig, BibliographyOptions, } from './research-model/index.js';
|
|
28
|
+
export { createDefaultResearchModel, createDefaultSection, createDefaultClaim, createDefaultSource, createDefaultQuestion, ResearchModelStore, applyResearchOperation, validateResearchModel, createResearchModelTools, generateBibliography, } from './research-model/index.js';
|
package/dist/index.js
CHANGED
|
@@ -31,4 +31,4 @@ export { reactFastapiToolkit } from './toolkits/react-fastapi/index.js';
|
|
|
31
31
|
export { reactGoToolkit } from './toolkits/react-go/index.js';
|
|
32
32
|
// Factory skill (Phase 5)
|
|
33
33
|
export { factoryScaffoldSkill, factorySkills } from './factory/skill.js';
|
|
34
|
-
export { createDefaultResearchModel, createDefaultSection, createDefaultClaim, createDefaultSource, createDefaultQuestion, ResearchModelStore, applyResearchOperation, validateResearchModel, createResearchModelTools, } from './research-model/index.js';
|
|
34
|
+
export { createDefaultResearchModel, createDefaultSection, createDefaultClaim, createDefaultSource, createDefaultQuestion, ResearchModelStore, applyResearchOperation, validateResearchModel, createResearchModelTools, generateBibliography, } from './research-model/index.js';
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bibliography Generator
|
|
3
|
+
*
|
|
4
|
+
* Formats source citations from the Research Model into a complete
|
|
5
|
+
* bibliography in the project's citation style.
|
|
6
|
+
*
|
|
7
|
+
* Supported styles: APA, MLA, Chicago, IEEE, Harvard
|
|
8
|
+
* Output formats: Markdown, LaTeX/BibTeX
|
|
9
|
+
*/
|
|
10
|
+
import type { Source, CitationStyle } from './types.js';
|
|
11
|
+
export interface BibliographyOptions {
|
|
12
|
+
/** Citation style (from Research Model) */
|
|
13
|
+
style: CitationStyle;
|
|
14
|
+
/** Output format */
|
|
15
|
+
format: 'markdown' | 'latex' | 'bibtex';
|
|
16
|
+
/** Filter: only referenced sources or all */
|
|
17
|
+
filter: 'referenced' | 'all';
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Generate a formatted bibliography from a list of sources.
|
|
21
|
+
*
|
|
22
|
+
* @param sources - Sources to include
|
|
23
|
+
* @param referencedIds - Set of source IDs actually cited in the paper (for filter: 'referenced')
|
|
24
|
+
* @param options - Style, format, and filter options
|
|
25
|
+
*/
|
|
26
|
+
export declare function generateBibliography(sources: readonly Source[], referencedIds: Set<string>, options: BibliographyOptions): string;
|
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bibliography Generator
|
|
3
|
+
*
|
|
4
|
+
* Formats source citations from the Research Model into a complete
|
|
5
|
+
* bibliography in the project's citation style.
|
|
6
|
+
*
|
|
7
|
+
* Supported styles: APA, MLA, Chicago, IEEE, Harvard
|
|
8
|
+
* Output formats: Markdown, LaTeX/BibTeX
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* Generate a formatted bibliography from a list of sources.
|
|
12
|
+
*
|
|
13
|
+
* @param sources - Sources to include
|
|
14
|
+
* @param referencedIds - Set of source IDs actually cited in the paper (for filter: 'referenced')
|
|
15
|
+
* @param options - Style, format, and filter options
|
|
16
|
+
*/
|
|
17
|
+
export function generateBibliography(sources, referencedIds, options) {
|
|
18
|
+
let filtered = [...sources];
|
|
19
|
+
if (options.filter === 'referenced') {
|
|
20
|
+
filtered = filtered.filter((s) => referencedIds.has(s.id));
|
|
21
|
+
}
|
|
22
|
+
// Sort alphabetically by first author last name, then year
|
|
23
|
+
filtered.sort((a, b) => {
|
|
24
|
+
const authorA = getLastName(a.citation.authors[0] ?? '');
|
|
25
|
+
const authorB = getLastName(b.citation.authors[0] ?? '');
|
|
26
|
+
const cmp = authorA.localeCompare(authorB);
|
|
27
|
+
if (cmp !== 0)
|
|
28
|
+
return cmp;
|
|
29
|
+
return Number(a.citation.year) - Number(b.citation.year);
|
|
30
|
+
});
|
|
31
|
+
if (options.format === 'bibtex') {
|
|
32
|
+
return filtered.map((s) => formatBibTeX(s)).join('\n\n');
|
|
33
|
+
}
|
|
34
|
+
const formatter = getFormatter(options.style);
|
|
35
|
+
const entries = filtered.map((s) => formatter(s.citation, s.citeKey));
|
|
36
|
+
if (options.format === 'latex') {
|
|
37
|
+
return entries.join('\n\n');
|
|
38
|
+
}
|
|
39
|
+
// Markdown
|
|
40
|
+
return entries.join('\n\n');
|
|
41
|
+
}
|
|
42
|
+
function getFormatter(style) {
|
|
43
|
+
switch (style) {
|
|
44
|
+
case 'apa':
|
|
45
|
+
return formatAPA;
|
|
46
|
+
case 'mla':
|
|
47
|
+
return formatMLA;
|
|
48
|
+
case 'chicago':
|
|
49
|
+
return formatChicago;
|
|
50
|
+
case 'ieee':
|
|
51
|
+
return formatIEEE;
|
|
52
|
+
case 'harvard':
|
|
53
|
+
return formatHarvard;
|
|
54
|
+
default:
|
|
55
|
+
return formatAPA;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* APA 7th Edition
|
|
60
|
+
* Author, A. A., & Author, B. B. (Year). Title of work. *Venue*, Volume(Issue), Pages. DOI
|
|
61
|
+
*/
|
|
62
|
+
function formatAPA(info, _citeKey) {
|
|
63
|
+
const authors = formatAuthorsAPA(info.authors);
|
|
64
|
+
const year = `(${String(info.year)})`;
|
|
65
|
+
const title = info.type === 'article' || info.type === 'chapter' ? info.title : `*${info.title}*`;
|
|
66
|
+
const parts = [authors, year, `${title}.`];
|
|
67
|
+
if (info.venue) {
|
|
68
|
+
parts.push(info.type === 'article' ? `*${info.venue}*` : info.venue);
|
|
69
|
+
if (info.volume) {
|
|
70
|
+
const vol = info.issue ? `${info.volume}(${info.issue})` : info.volume;
|
|
71
|
+
parts[parts.length - 1] += `, ${vol}`;
|
|
72
|
+
}
|
|
73
|
+
if (info.pages) {
|
|
74
|
+
parts[parts.length - 1] += `, ${info.pages}`;
|
|
75
|
+
}
|
|
76
|
+
parts[parts.length - 1] += '.';
|
|
77
|
+
}
|
|
78
|
+
if (info.doi)
|
|
79
|
+
parts.push(`https://doi.org/${info.doi}`);
|
|
80
|
+
else if (info.url)
|
|
81
|
+
parts.push(info.url);
|
|
82
|
+
return parts.join(' ');
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* MLA 9th Edition
|
|
86
|
+
* Author. "Title." *Venue*, vol. Volume, no. Issue, Year, pp. Pages.
|
|
87
|
+
*/
|
|
88
|
+
function formatMLA(info, _citeKey) {
|
|
89
|
+
const authors = formatAuthorsMLA(info.authors);
|
|
90
|
+
const title = info.type === 'article' || info.type === 'chapter' ? `"${info.title}."` : `*${info.title}*.`;
|
|
91
|
+
const parts = [authors, title];
|
|
92
|
+
if (info.venue) {
|
|
93
|
+
let venue = `*${info.venue}*`;
|
|
94
|
+
if (info.volume)
|
|
95
|
+
venue += `, vol. ${info.volume}`;
|
|
96
|
+
if (info.issue)
|
|
97
|
+
venue += `, no. ${info.issue}`;
|
|
98
|
+
venue += `, ${String(info.year)}`;
|
|
99
|
+
if (info.pages)
|
|
100
|
+
venue += `, pp. ${info.pages}`;
|
|
101
|
+
venue += '.';
|
|
102
|
+
parts.push(venue);
|
|
103
|
+
}
|
|
104
|
+
else {
|
|
105
|
+
parts.push(`${String(info.year)}.`);
|
|
106
|
+
}
|
|
107
|
+
if (info.doi)
|
|
108
|
+
parts.push(`https://doi.org/${info.doi}`);
|
|
109
|
+
return parts.join(' ');
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Chicago (Author-Date)
|
|
113
|
+
* Author, First. Year. "Title." *Venue* Volume (Issue): Pages. DOI.
|
|
114
|
+
*/
|
|
115
|
+
function formatChicago(info, _citeKey) {
|
|
116
|
+
const authors = formatAuthorsChicago(info.authors);
|
|
117
|
+
const year = `${String(info.year)}.`;
|
|
118
|
+
const title = info.type === 'article' || info.type === 'chapter' ? `"${info.title}."` : `*${info.title}*.`;
|
|
119
|
+
const parts = [authors, year, title];
|
|
120
|
+
if (info.venue) {
|
|
121
|
+
let venue = `*${info.venue}*`;
|
|
122
|
+
if (info.volume) {
|
|
123
|
+
venue += ` ${info.volume}`;
|
|
124
|
+
if (info.issue)
|
|
125
|
+
venue += ` (${info.issue})`;
|
|
126
|
+
}
|
|
127
|
+
if (info.pages)
|
|
128
|
+
venue += `: ${info.pages}`;
|
|
129
|
+
venue += '.';
|
|
130
|
+
parts.push(venue);
|
|
131
|
+
}
|
|
132
|
+
if (info.doi)
|
|
133
|
+
parts.push(`https://doi.org/${info.doi}.`);
|
|
134
|
+
return parts.join(' ');
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* IEEE
|
|
138
|
+
* [N] A. Author and B. Author, "Title," *Venue*, vol. V, no. N, pp. P, Year.
|
|
139
|
+
*/
|
|
140
|
+
function formatIEEE(info, citeKey) {
|
|
141
|
+
const authors = formatAuthorsIEEE(info.authors);
|
|
142
|
+
const title = `"${info.title},"`;
|
|
143
|
+
const parts = [`[${citeKey}]`, `${authors},`, title];
|
|
144
|
+
if (info.venue) {
|
|
145
|
+
let venue = `*${info.venue}*`;
|
|
146
|
+
if (info.volume)
|
|
147
|
+
venue += `, vol. ${info.volume}`;
|
|
148
|
+
if (info.issue)
|
|
149
|
+
venue += `, no. ${info.issue}`;
|
|
150
|
+
if (info.pages)
|
|
151
|
+
venue += `, pp. ${info.pages}`;
|
|
152
|
+
venue += `, ${String(info.year)}.`;
|
|
153
|
+
parts.push(venue);
|
|
154
|
+
}
|
|
155
|
+
else {
|
|
156
|
+
parts.push(`${String(info.year)}.`);
|
|
157
|
+
}
|
|
158
|
+
if (info.doi)
|
|
159
|
+
parts.push(`doi: ${info.doi}.`);
|
|
160
|
+
return parts.join(' ');
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Harvard
|
|
164
|
+
* Author, A. (Year) Title. *Venue*, Volume(Issue), pp. Pages.
|
|
165
|
+
*/
|
|
166
|
+
function formatHarvard(info, _citeKey) {
|
|
167
|
+
const authors = formatAuthorsHarvard(info.authors);
|
|
168
|
+
const year = `(${String(info.year)})`;
|
|
169
|
+
const title = info.type === 'article' || info.type === 'chapter' ? `'${info.title}'.` : `*${info.title}*.`;
|
|
170
|
+
const parts = [authors, year, title];
|
|
171
|
+
if (info.venue) {
|
|
172
|
+
let venue = `*${info.venue}*`;
|
|
173
|
+
if (info.volume) {
|
|
174
|
+
venue += `, ${info.volume}`;
|
|
175
|
+
if (info.issue)
|
|
176
|
+
venue += `(${info.issue})`;
|
|
177
|
+
}
|
|
178
|
+
if (info.pages)
|
|
179
|
+
venue += `, pp. ${info.pages}`;
|
|
180
|
+
venue += '.';
|
|
181
|
+
parts.push(venue);
|
|
182
|
+
}
|
|
183
|
+
if (info.doi)
|
|
184
|
+
parts.push(`doi: ${info.doi}.`);
|
|
185
|
+
return parts.join(' ');
|
|
186
|
+
}
|
|
187
|
+
// =============================================================================
|
|
188
|
+
// BibTeX Format
|
|
189
|
+
// =============================================================================
|
|
190
|
+
function formatBibTeX(source) {
|
|
191
|
+
const info = source.citation;
|
|
192
|
+
const entryType = bibtexType(info.type);
|
|
193
|
+
const lines = [`@${entryType}{${source.citeKey},`];
|
|
194
|
+
lines.push(` author = {${info.authors.join(' and ')}},`);
|
|
195
|
+
lines.push(` title = {${info.title}},`);
|
|
196
|
+
lines.push(` year = {${String(info.year)}},`);
|
|
197
|
+
if (info.venue) {
|
|
198
|
+
const field = info.type === 'book' ? 'publisher' : 'journal';
|
|
199
|
+
lines.push(` ${field} = {${info.venue}},`);
|
|
200
|
+
}
|
|
201
|
+
if (info.volume)
|
|
202
|
+
lines.push(` volume = {${info.volume}},`);
|
|
203
|
+
if (info.issue)
|
|
204
|
+
lines.push(` number = {${info.issue}},`);
|
|
205
|
+
if (info.pages)
|
|
206
|
+
lines.push(` pages = {${info.pages}},`);
|
|
207
|
+
if (info.doi)
|
|
208
|
+
lines.push(` doi = {${info.doi}},`);
|
|
209
|
+
if (info.url)
|
|
210
|
+
lines.push(` url = {${info.url}},`);
|
|
211
|
+
lines.push('}');
|
|
212
|
+
return lines.join('\n');
|
|
213
|
+
}
|
|
214
|
+
function bibtexType(citationType) {
|
|
215
|
+
switch (citationType) {
|
|
216
|
+
case 'article':
|
|
217
|
+
return 'article';
|
|
218
|
+
case 'book':
|
|
219
|
+
return 'book';
|
|
220
|
+
case 'chapter':
|
|
221
|
+
return 'incollection';
|
|
222
|
+
case 'conference':
|
|
223
|
+
return 'inproceedings';
|
|
224
|
+
case 'thesis':
|
|
225
|
+
return 'phdthesis';
|
|
226
|
+
case 'report':
|
|
227
|
+
return 'techreport';
|
|
228
|
+
case 'web':
|
|
229
|
+
return 'misc';
|
|
230
|
+
default:
|
|
231
|
+
return 'misc';
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
// =============================================================================
|
|
235
|
+
// Author Formatting Helpers
|
|
236
|
+
// =============================================================================
|
|
237
|
+
function getLastName(author) {
|
|
238
|
+
// Handles "Last, First" and "First Last" formats
|
|
239
|
+
if (author.includes(','))
|
|
240
|
+
return author.split(',')[0].trim();
|
|
241
|
+
const parts = author.trim().split(/\s+/);
|
|
242
|
+
return parts[parts.length - 1];
|
|
243
|
+
}
|
|
244
|
+
function getInitials(author) {
|
|
245
|
+
if (author.includes(',')) {
|
|
246
|
+
const parts = author.split(',');
|
|
247
|
+
const firstNames = parts.slice(1).join(',').trim();
|
|
248
|
+
return firstNames
|
|
249
|
+
.split(/\s+/)
|
|
250
|
+
.map((n) => n.charAt(0).toUpperCase() + '.')
|
|
251
|
+
.join(' ');
|
|
252
|
+
}
|
|
253
|
+
const parts = author.trim().split(/\s+/);
|
|
254
|
+
return parts
|
|
255
|
+
.slice(0, -1)
|
|
256
|
+
.map((n) => n.charAt(0).toUpperCase() + '.')
|
|
257
|
+
.join(' ');
|
|
258
|
+
}
|
|
259
|
+
/** APA: Last, F. M., & Last, F. M. */
|
|
260
|
+
function formatAuthorsAPA(authors) {
|
|
261
|
+
if (authors.length === 0)
|
|
262
|
+
return '';
|
|
263
|
+
const formatted = authors.map((a) => `${getLastName(a)}, ${getInitials(a)}`);
|
|
264
|
+
if (formatted.length === 1)
|
|
265
|
+
return formatted[0];
|
|
266
|
+
if (formatted.length === 2)
|
|
267
|
+
return `${formatted[0]}, & ${formatted[1]}`;
|
|
268
|
+
return `${formatted.slice(0, -1).join(', ')}, & ${formatted[formatted.length - 1]}`;
|
|
269
|
+
}
|
|
270
|
+
/** MLA: Last, First, and First Last. */
|
|
271
|
+
function formatAuthorsMLA(authors) {
|
|
272
|
+
if (authors.length === 0)
|
|
273
|
+
return '';
|
|
274
|
+
if (authors.length === 1)
|
|
275
|
+
return `${authors[0]}.`;
|
|
276
|
+
if (authors.length === 2)
|
|
277
|
+
return `${authors[0]}, and ${authors[1]}.`;
|
|
278
|
+
return `${authors[0]}, et al.`;
|
|
279
|
+
}
|
|
280
|
+
/** Chicago: Last, First, and First Last. */
|
|
281
|
+
function formatAuthorsChicago(authors) {
|
|
282
|
+
return formatAuthorsMLA(authors);
|
|
283
|
+
}
|
|
284
|
+
/** IEEE: F. Last and F. Last */
|
|
285
|
+
function formatAuthorsIEEE(authors) {
|
|
286
|
+
const formatted = authors.map((a) => `${getInitials(a)} ${getLastName(a)}`);
|
|
287
|
+
if (formatted.length <= 2)
|
|
288
|
+
return formatted.join(' and ');
|
|
289
|
+
return `${formatted.slice(0, -1).join(', ')}, and ${formatted[formatted.length - 1]}`;
|
|
290
|
+
}
|
|
291
|
+
/** Harvard: Last, F. and Last, F. */
|
|
292
|
+
function formatAuthorsHarvard(authors) {
|
|
293
|
+
const formatted = authors.map((a) => `${getLastName(a)}, ${getInitials(a)}`);
|
|
294
|
+
if (formatted.length === 1)
|
|
295
|
+
return formatted[0];
|
|
296
|
+
if (formatted.length === 2)
|
|
297
|
+
return `${formatted[0]} and ${formatted[1]}`;
|
|
298
|
+
return `${formatted.slice(0, -1).join(', ')} and ${formatted[formatted.length - 1]}`;
|
|
299
|
+
}
|
|
@@ -10,3 +10,5 @@ export { validateResearchModel } from './schema.js';
|
|
|
10
10
|
export type { ResearchValidationError, ResearchValidationResult } from './schema.js';
|
|
11
11
|
export { createResearchModelTools } from './tools.js';
|
|
12
12
|
export type { ResearchModelToolsConfig } from './tools.js';
|
|
13
|
+
export { generateBibliography } from './bibliography.js';
|
|
14
|
+
export type { BibliographyOptions } from './bibliography.js';
|
|
@@ -10,6 +10,7 @@ import { ResearchModelStore } from './persistence.js';
|
|
|
10
10
|
import { applyResearchOperation } from './operations.js';
|
|
11
11
|
import { validateResearchModel } from './schema.js';
|
|
12
12
|
import { createDefaultResearchModel } from './defaults.js';
|
|
13
|
+
import { generateBibliography } from './bibliography.js';
|
|
13
14
|
// =============================================================================
|
|
14
15
|
// Helpers
|
|
15
16
|
// =============================================================================
|
|
@@ -572,6 +573,81 @@ function createResearchModelValidateTool(config) {
|
|
|
572
573
|
readonly: true,
|
|
573
574
|
});
|
|
574
575
|
}
|
|
576
|
+
function createBibliographyGenerateTool(config) {
|
|
577
|
+
return defineTool({
|
|
578
|
+
name: 'bibliography_generate',
|
|
579
|
+
description: 'Generate a formatted bibliography from the Research Model sources. Supports APA, MLA, Chicago, IEEE, Harvard styles and Markdown, LaTeX, BibTeX output.',
|
|
580
|
+
inputSchema: {
|
|
581
|
+
type: 'object',
|
|
582
|
+
properties: {
|
|
583
|
+
format: {
|
|
584
|
+
type: 'string',
|
|
585
|
+
enum: ['markdown', 'latex', 'bibtex'],
|
|
586
|
+
description: 'Output format. Defaults to "markdown".',
|
|
587
|
+
},
|
|
588
|
+
filter: {
|
|
589
|
+
type: 'string',
|
|
590
|
+
enum: ['referenced', 'all'],
|
|
591
|
+
description: 'Which sources to include. "referenced" = only sources cited in claims. "all" = entire registry. Defaults to "all".',
|
|
592
|
+
},
|
|
593
|
+
project_id: {
|
|
594
|
+
type: 'number',
|
|
595
|
+
description: 'Project ID. Uses active project if omitted.',
|
|
596
|
+
},
|
|
597
|
+
},
|
|
598
|
+
required: [],
|
|
599
|
+
},
|
|
600
|
+
execute: async (input) => {
|
|
601
|
+
try {
|
|
602
|
+
const projectId = getProjectId(config, input.project_id);
|
|
603
|
+
const store = createStore(config, projectId);
|
|
604
|
+
const model = await store.get();
|
|
605
|
+
if (!model) {
|
|
606
|
+
return createErrorResult('No Research Model found. Create one first.');
|
|
607
|
+
}
|
|
608
|
+
if (model.sources.length === 0) {
|
|
609
|
+
return createSuccessResult({
|
|
610
|
+
bibliography: '',
|
|
611
|
+
message: 'No sources in the model. Add sources first.',
|
|
612
|
+
count: 0,
|
|
613
|
+
});
|
|
614
|
+
}
|
|
615
|
+
// Collect referenced source IDs (from all claims)
|
|
616
|
+
const referencedIds = new Set();
|
|
617
|
+
for (const section of model.sections) {
|
|
618
|
+
for (const claim of section.claims) {
|
|
619
|
+
for (const ref of claim.supportingSources)
|
|
620
|
+
referencedIds.add(ref.sourceId);
|
|
621
|
+
for (const ref of claim.contradictingSources)
|
|
622
|
+
referencedIds.add(ref.sourceId);
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
const options = {
|
|
626
|
+
style: model.citationStyle,
|
|
627
|
+
format: input.format ?? 'markdown',
|
|
628
|
+
filter: input.filter ?? 'all',
|
|
629
|
+
};
|
|
630
|
+
const bibliography = generateBibliography(model.sources, referencedIds, options);
|
|
631
|
+
const count = options.filter === 'referenced'
|
|
632
|
+
? model.sources.filter((s) => referencedIds.has(s.id)).length
|
|
633
|
+
: model.sources.length;
|
|
634
|
+
return createSuccessResult({
|
|
635
|
+
bibliography,
|
|
636
|
+
style: model.citationStyle,
|
|
637
|
+
format: options.format,
|
|
638
|
+
filter: options.filter,
|
|
639
|
+
count,
|
|
640
|
+
totalSources: model.sources.length,
|
|
641
|
+
referencedSources: referencedIds.size,
|
|
642
|
+
});
|
|
643
|
+
}
|
|
644
|
+
catch (error) {
|
|
645
|
+
return createErrorResult(error instanceof Error ? error.message : String(error));
|
|
646
|
+
}
|
|
647
|
+
},
|
|
648
|
+
readonly: true,
|
|
649
|
+
});
|
|
650
|
+
}
|
|
575
651
|
// =============================================================================
|
|
576
652
|
// Public API
|
|
577
653
|
// =============================================================================
|
|
@@ -580,5 +656,6 @@ export function createResearchModelTools(config) {
|
|
|
580
656
|
createResearchModelGetTool(config),
|
|
581
657
|
createResearchModelUpdateTool(config),
|
|
582
658
|
createResearchModelValidateTool(config),
|
|
659
|
+
createBibliographyGenerateTool(config),
|
|
583
660
|
];
|
|
584
661
|
}
|