@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 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';
@@ -11,3 +11,5 @@ export { applyResearchOperation } from './operations.js';
11
11
  export { validateResearchModel } from './schema.js';
12
12
  // Tools
13
13
  export { createResearchModelTools } from './tools.js';
14
+ // Bibliography
15
+ export { generateBibliography } 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
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@compilr-dev/factory",
3
- "version": "0.1.15",
3
+ "version": "0.1.16",
4
4
  "description": "AI-driven application scaffolder for the compilr-dev ecosystem",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",