@grainulation/mill 1.0.0 → 1.0.2

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.
Files changed (49) hide show
  1. package/CODE_OF_CONDUCT.md +25 -0
  2. package/CONTRIBUTING.md +101 -0
  3. package/README.md +90 -42
  4. package/bin/mill.js +233 -67
  5. package/lib/exporters/csv.js +35 -30
  6. package/lib/exporters/json-ld.js +19 -13
  7. package/lib/exporters/markdown.js +83 -44
  8. package/lib/exporters/pdf.js +15 -15
  9. package/lib/formats/bibtex.mjs +83 -0
  10. package/lib/formats/{changelog.js → changelog.mjs} +27 -26
  11. package/lib/formats/confluence-adf.mjs +312 -0
  12. package/lib/formats/{csv.js → csv.mjs} +41 -37
  13. package/lib/formats/{dot.js → dot.mjs} +45 -34
  14. package/lib/formats/{evidence-matrix.js → evidence-matrix.mjs} +17 -16
  15. package/lib/formats/executive-summary.mjs +178 -0
  16. package/lib/formats/github-issues.mjs +96 -0
  17. package/lib/formats/{graphml.js → graphml.mjs} +45 -32
  18. package/lib/formats/html-report.mjs +228 -0
  19. package/lib/formats/{jira-csv.js → jira-csv.mjs} +30 -29
  20. package/lib/formats/{json-ld.js → json-ld.mjs} +6 -6
  21. package/lib/formats/{markdown.js → markdown.mjs} +53 -36
  22. package/lib/formats/{ndjson.js → ndjson.mjs} +6 -6
  23. package/lib/formats/{obsidian.js → obsidian.mjs} +43 -35
  24. package/lib/formats/{opml.js → opml.mjs} +38 -28
  25. package/lib/formats/ris.mjs +76 -0
  26. package/lib/formats/{rss.js → rss.mjs} +31 -28
  27. package/lib/formats/{sankey.js → sankey.mjs} +16 -15
  28. package/lib/formats/slide-deck.mjs +288 -0
  29. package/lib/formats/sql.mjs +120 -0
  30. package/lib/formats/{static-site.js → static-site.mjs} +64 -52
  31. package/lib/formats/{treemap.js → treemap.mjs} +16 -15
  32. package/lib/formats/typescript-defs.mjs +150 -0
  33. package/lib/formats/{yaml.js → yaml.mjs} +58 -40
  34. package/lib/formats.js +16 -16
  35. package/lib/index.js +5 -5
  36. package/lib/json-ld-common.js +37 -31
  37. package/lib/publishers/clipboard.js +21 -19
  38. package/lib/publishers/static.js +27 -12
  39. package/lib/serve-mcp.js +158 -83
  40. package/lib/server.js +252 -142
  41. package/package.json +6 -3
  42. package/lib/formats/bibtex.js +0 -76
  43. package/lib/formats/executive-summary.js +0 -130
  44. package/lib/formats/github-issues.js +0 -89
  45. package/lib/formats/html-report.js +0 -181
  46. package/lib/formats/ris.js +0 -70
  47. package/lib/formats/slide-deck.js +0 -200
  48. package/lib/formats/sql.js +0 -116
  49. package/lib/formats/typescript-defs.js +0 -147
@@ -1,200 +0,0 @@
1
- /**
2
- * mill format: slide-deck
3
- *
4
- * Self-contained HTML with CSS scroll-snap. One slide per claim type group.
5
- * Dark theme matching grainulation design tokens.
6
- * Zero dependencies — node built-in only.
7
- */
8
-
9
- export const name = 'slide-deck';
10
- export const extension = '.html';
11
- export const mimeType = 'text/html; charset=utf-8';
12
- export const description = 'Scroll-snap slide deck: one slide per type group with keyboard navigation';
13
-
14
- /**
15
- * Convert a compilation object to a slide deck HTML page.
16
- * @param {object} compilation - The compilation.json content
17
- * @returns {string} HTML output
18
- */
19
- export function convert(compilation) {
20
- const meta = compilation.meta || {};
21
- const claims = compilation.claims || [];
22
- const conflicts = compilation.conflicts || [];
23
- const certificate = compilation.certificate || {};
24
-
25
- const title = meta.sprint || meta.question || 'Sprint Deck';
26
- const compiled = certificate.compiled_at || new Date().toISOString();
27
- const active = claims.filter(c => c.status === 'active').length;
28
-
29
- // Group by type (skip reverted)
30
- const byType = {};
31
- for (const c of claims) {
32
- if (c.status === 'reverted') continue;
33
- const t = c.type || 'unknown';
34
- if (!byType[t]) byType[t] = [];
35
- byType[t].push(c);
36
- }
37
-
38
- const typeOrder = ['constraint', 'factual', 'recommendation', 'risk', 'estimate', 'feedback'];
39
- const sortedTypes = typeOrder.filter(t => byType[t]);
40
- for (const t of Object.keys(byType)) {
41
- if (!sortedTypes.includes(t)) sortedTypes.push(t);
42
- }
43
-
44
- const typeColors = {
45
- constraint: '#e74c3c',
46
- factual: '#3498db',
47
- recommendation: '#2ecc71',
48
- risk: '#f39c12',
49
- estimate: '#9b59b6',
50
- feedback: '#1abc9c',
51
- unknown: '#95a5a6',
52
- };
53
-
54
- // Title slide
55
- const slides = [];
56
- slides.push(`
57
- <section class="slide">
58
- <div class="slide-content title-slide">
59
- <h1>${esc(title)}</h1>
60
- ${meta.question ? `<p class="question">${esc(meta.question)}</p>` : ''}
61
- <p class="meta">${esc(compiled)} | ${claims.length} claims</p>
62
- </div>
63
- </section>`);
64
-
65
- // Summary slide
66
- const typeStats = sortedTypes.map(t => {
67
- const color = typeColors[t] || typeColors.unknown;
68
- return `<div class="type-stat"><span class="dot" style="background:${color}"></span>${capitalize(t)}: ${byType[t].length}</div>`;
69
- }).join('\n ');
70
-
71
- slides.push(`
72
- <section class="slide">
73
- <div class="slide-content">
74
- <h2>Summary</h2>
75
- <div class="summary-grid">
76
- <div class="big-stat"><span class="num">${claims.length}</span><span class="label">Total</span></div>
77
- <div class="big-stat"><span class="num">${active}</span><span class="label">Active</span></div>
78
- ${conflicts.length ? `<div class="big-stat"><span class="num">${conflicts.length}</span><span class="label">Conflicts</span></div>` : ''}
79
- </div>
80
- <div class="type-stats">
81
- ${typeStats}
82
- </div>
83
- </div>
84
- </section>`);
85
-
86
- // One slide per type group
87
- for (const t of sortedTypes) {
88
- const group = byType[t];
89
- const color = typeColors[t] || typeColors.unknown;
90
- const items = group.map(c => {
91
- const body = esc(c.content || c.text || '');
92
- const conf = c.confidence != null ? ` (${Math.round(c.confidence * 100)}%)` : '';
93
- return `<li><strong>${esc(c.id)}</strong>${conf}: ${body}</li>`;
94
- }).join('\n ');
95
-
96
- slides.push(`
97
- <section class="slide">
98
- <div class="slide-content">
99
- <h2 style="border-left:4px solid ${color};padding-left:12px">${capitalize(t)}s (${group.length})</h2>
100
- <ul class="claim-list">
101
- ${items}
102
- </ul>
103
- </div>
104
- </section>`);
105
- }
106
-
107
- // Conflicts slide (if any)
108
- if (conflicts.length > 0) {
109
- const conflictItems = conflicts.map(c => {
110
- const ids = c.ids?.join(' vs ') || 'unknown';
111
- const resolved = c.resolution ? ' [resolved]' : '';
112
- return `<li><strong>${esc(ids)}</strong>${resolved}: ${esc(c.description || c.reason || '')}</li>`;
113
- }).join('\n ');
114
-
115
- slides.push(`
116
- <section class="slide">
117
- <div class="slide-content">
118
- <h2 style="border-left:4px solid #e74c3c;padding-left:12px">Conflicts (${conflicts.length})</h2>
119
- <ul class="claim-list">
120
- ${conflictItems}
121
- </ul>
122
- </div>
123
- </section>`);
124
- }
125
-
126
- // Certificate slide
127
- slides.push(`
128
- <section class="slide">
129
- <div class="slide-content title-slide">
130
- <h2>Certificate</h2>
131
- <p class="mono">${certificate.claim_count || claims.length} claims</p>
132
- <p class="mono">sha256:${esc((certificate.sha256 || 'unknown').slice(0, 24))}</p>
133
- <p class="meta">${esc(compiled)}</p>
134
- </div>
135
- </section>`);
136
-
137
- return `<!DOCTYPE html>
138
- <html lang="en">
139
- <head>
140
- <meta charset="utf-8">
141
- <meta name="viewport" content="width=device-width,initial-scale=1">
142
- <title>${esc(title)} — Slide Deck</title>
143
- <style>
144
- :root { --bg:#0a0e1a; --surface:#111827; --border:#1e293b; --text:#e2e8f0; --muted:#94a3b8; }
145
- * { margin:0; padding:0; box-sizing:border-box; }
146
- html { scroll-snap-type:y mandatory; overflow-y:scroll; }
147
- body { background:var(--bg); color:var(--text); font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif; line-height:1.6; }
148
- .slide { scroll-snap-align:start; height:100vh; display:flex; align-items:center; justify-content:center; padding:2rem; }
149
- .slide-content { max-width:800px; width:100%; }
150
- .title-slide { text-align:center; }
151
- .title-slide h1 { font-size:2.4rem; margin-bottom:0.75rem; }
152
- .title-slide h2 { font-size:1.8rem; margin-bottom:1rem; }
153
- .question { font-size:1.1rem; color:var(--muted); margin-bottom:1rem; }
154
- .meta { font-size:0.85rem; color:var(--muted); }
155
- .mono { font-family:monospace; font-size:1rem; color:var(--muted); margin-bottom:0.5rem; }
156
- h2 { font-size:1.5rem; margin-bottom:1.25rem; }
157
- .summary-grid { display:flex; gap:2rem; justify-content:center; margin-bottom:2rem; }
158
- .big-stat { display:flex; flex-direction:column; align-items:center; }
159
- .big-stat .num { font-size:2.5rem; font-weight:700; }
160
- .big-stat .label { font-size:0.8rem; color:var(--muted); text-transform:uppercase; }
161
- .type-stats { display:flex; flex-wrap:wrap; gap:0.75rem; justify-content:center; }
162
- .type-stat { display:flex; align-items:center; gap:0.4rem; font-size:0.9rem; }
163
- .dot { width:10px; height:10px; border-radius:50%; display:inline-block; }
164
- .claim-list { list-style:none; max-height:70vh; overflow-y:auto; }
165
- .claim-list li { padding:0.6rem 0; border-bottom:1px solid var(--border); font-size:0.9rem; }
166
- .claim-list li strong { font-family:monospace; font-size:0.8rem; }
167
- </style>
168
- </head>
169
- <body>
170
- ${slides.join('\n')}
171
- <script>
172
- document.addEventListener('keydown', function(e) {
173
- if (e.key === 'ArrowDown' || e.key === 'PageDown') {
174
- e.preventDefault();
175
- const slides = document.querySelectorAll('.slide');
176
- const vh = window.innerHeight;
177
- const current = Math.round(window.scrollY / vh);
178
- const next = Math.min(current + 1, slides.length - 1);
179
- slides[next].scrollIntoView({ behavior: 'smooth' });
180
- } else if (e.key === 'ArrowUp' || e.key === 'PageUp') {
181
- e.preventDefault();
182
- const vh = window.innerHeight;
183
- const current = Math.round(window.scrollY / vh);
184
- const prev = Math.max(current - 1, 0);
185
- document.querySelectorAll('.slide')[prev].scrollIntoView({ behavior: 'smooth' });
186
- }
187
- });
188
- </script>
189
- </body>
190
- </html>`;
191
- }
192
-
193
- function esc(str) {
194
- if (str == null) return '';
195
- return String(str).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;');
196
- }
197
-
198
- function capitalize(str) {
199
- return str.charAt(0).toUpperCase() + str.slice(1);
200
- }
@@ -1,116 +0,0 @@
1
- /**
2
- * mill format: sql
3
- *
4
- * Converts compilation.json to SQL CREATE TABLE + INSERT statements.
5
- * Compatible with SQLite and PostgreSQL.
6
- * Zero dependencies — node built-in only.
7
- */
8
-
9
- export const name = 'sql';
10
- export const extension = '.sql';
11
- export const mimeType = 'application/sql; charset=utf-8';
12
- export const description = 'SQL schema and INSERT statements for claims data';
13
-
14
- /**
15
- * Convert a compilation object to SQL statements.
16
- * @param {object} compilation - The compilation.json content
17
- * @returns {string} SQL output
18
- */
19
- export function convert(compilation) {
20
- const claims = compilation.claims || [];
21
- const meta = compilation.meta || {};
22
- const certificate = compilation.certificate || {};
23
-
24
- const lines = [];
25
-
26
- lines.push('-- Auto-generated by mill sql format');
27
- if (meta.sprint) lines.push(`-- Sprint: ${meta.sprint}`);
28
- if (certificate.compiled_at) lines.push(`-- Compiled: ${certificate.compiled_at}`);
29
- lines.push('');
30
-
31
- // Claims table
32
- lines.push('CREATE TABLE IF NOT EXISTS claims (');
33
- lines.push(' id TEXT PRIMARY KEY,');
34
- lines.push(' type TEXT,');
35
- lines.push(' content TEXT,');
36
- lines.push(' evidence_tier TEXT,');
37
- lines.push(' status TEXT,');
38
- lines.push(' confidence REAL,');
39
- lines.push(' source TEXT,');
40
- lines.push(' tags TEXT,');
41
- lines.push(' created TEXT');
42
- lines.push(');');
43
- lines.push('');
44
-
45
- // Conflicts table
46
- lines.push('CREATE TABLE IF NOT EXISTS conflicts (');
47
- lines.push(' id INTEGER PRIMARY KEY AUTOINCREMENT,');
48
- lines.push(' claim_ids TEXT,');
49
- lines.push(' description TEXT,');
50
- lines.push(' resolution TEXT');
51
- lines.push(');');
52
- lines.push('');
53
-
54
- // Insert claims
55
- if (claims.length > 0) {
56
- lines.push('-- Claims');
57
- for (const claim of claims) {
58
- const id = esc(claim.id || '');
59
- const type = esc(claim.type || '');
60
- const content = esc(claim.content || claim.text || '');
61
- const evidenceTier = esc(extractTier(claim));
62
- const status = esc(claim.status || '');
63
- const confidence = claim.confidence != null ? String(claim.confidence) : 'NULL';
64
- const source = esc(extractSource(claim));
65
- const tags = esc(Array.isArray(claim.tags) ? claim.tags.join(',') : '');
66
- const created = esc(claim.created || '');
67
-
68
- lines.push(
69
- `INSERT INTO claims VALUES ('${id}', '${type}', '${content}', '${evidenceTier}', '${status}', ${confidence}, '${source}', '${tags}', '${created}');`
70
- );
71
- }
72
- lines.push('');
73
- }
74
-
75
- // Insert conflicts
76
- const conflicts = compilation.conflicts || [];
77
- if (conflicts.length > 0) {
78
- lines.push('-- Conflicts');
79
- for (const conflict of conflicts) {
80
- const claimIds = esc(Array.isArray(conflict.ids) ? conflict.ids.join(',') : '');
81
- const description = esc(conflict.description || conflict.reason || '');
82
- const resolution = esc(conflict.resolution || '');
83
-
84
- lines.push(
85
- `INSERT INTO conflicts (claim_ids, description, resolution) VALUES ('${claimIds}', '${description}', '${resolution}');`
86
- );
87
- }
88
- lines.push('');
89
- }
90
-
91
- return lines.join('\n');
92
- }
93
-
94
- function esc(value) {
95
- if (value == null) return '';
96
- return String(value).replace(/'/g, "''");
97
- }
98
-
99
- function extractTier(claim) {
100
- if (typeof claim.evidence === 'string') return claim.evidence;
101
- if (typeof claim.evidence === 'object' && claim.evidence !== null) {
102
- return claim.evidence.tier || '';
103
- }
104
- return claim.evidence_tier || '';
105
- }
106
-
107
- function extractSource(claim) {
108
- if (typeof claim.source === 'string') return claim.source;
109
- if (typeof claim.source === 'object' && claim.source !== null) {
110
- return claim.source.origin || claim.source.artifact || '';
111
- }
112
- if (typeof claim.evidence === 'object' && claim.evidence?.source) {
113
- return claim.evidence.source;
114
- }
115
- return '';
116
- }
@@ -1,147 +0,0 @@
1
- /**
2
- * mill format: typescript-defs
3
- *
4
- * Converts compilation.json to TypeScript .d.ts definitions.
5
- * Derives union types from actual claim data values.
6
- * Zero dependencies — node built-in only.
7
- */
8
-
9
- export const name = 'typescript-defs';
10
- export const extension = '.d.ts';
11
- export const mimeType = 'text/plain; charset=utf-8';
12
- export const description = 'TypeScript type definitions derived from compilation data';
13
-
14
- /**
15
- * Convert a compilation object to TypeScript .d.ts definitions.
16
- * @param {object} compilation - The compilation.json content
17
- * @returns {string} TypeScript definition output
18
- */
19
- export function convert(compilation) {
20
- const claims = compilation.claims || [];
21
- const meta = compilation.meta || {};
22
- const conflicts = compilation.conflicts || [];
23
- const certificate = compilation.certificate || {};
24
-
25
- const claimTypes = uniqueSorted(claims.map(c => c.type).filter(Boolean));
26
- const evidenceTiers = uniqueSorted(
27
- claims.map(c => {
28
- if (typeof c.evidence === 'string') return c.evidence;
29
- return c.evidence?.tier ?? c.evidence_tier ?? null;
30
- }).filter(Boolean)
31
- );
32
- const statuses = uniqueSorted(claims.map(c => c.status).filter(Boolean));
33
-
34
- const lines = [];
35
-
36
- lines.push('// Auto-generated by mill typescript-defs format');
37
- lines.push('// Derived from compilation data — do not edit manually');
38
- lines.push('');
39
-
40
- // ClaimType union
41
- lines.push(`export type ClaimType = ${unionType(claimTypes)};`);
42
- lines.push('');
43
-
44
- // EvidenceTier union
45
- lines.push(`export type EvidenceTier = ${unionType(evidenceTiers)};`);
46
- lines.push('');
47
-
48
- // ClaimStatus union
49
- lines.push(`export type ClaimStatus = ${unionType(statuses)};`);
50
- lines.push('');
51
-
52
- // Evidence interface
53
- lines.push('export interface Evidence {');
54
- lines.push(' tier: EvidenceTier;');
55
- lines.push(' source?: string;');
56
- lines.push('}');
57
- lines.push('');
58
-
59
- // Source interface
60
- lines.push('export interface ClaimSource {');
61
- lines.push(' origin?: string;');
62
- lines.push(' artifact?: string;');
63
- lines.push('}');
64
- lines.push('');
65
-
66
- // Claim interface
67
- lines.push('export interface Claim {');
68
- lines.push(' id: string;');
69
- lines.push(' type: ClaimType;');
70
- lines.push(' content: string;');
71
- lines.push(' evidence: EvidenceTier | Evidence;');
72
- lines.push(' status: ClaimStatus;');
73
- lines.push(' confidence?: number;');
74
- lines.push(' source?: string | ClaimSource;');
75
- lines.push(' tags?: string[];');
76
- lines.push(' created?: string;');
77
- lines.push('}');
78
- lines.push('');
79
-
80
- // Meta interface
81
- lines.push('export interface Meta {');
82
- lines.push(' sprint?: string;');
83
- lines.push(' question?: string;');
84
- lines.push(' audience?: string;');
85
- for (const key of Object.keys(meta)) {
86
- if (!['sprint', 'question', 'audience'].includes(key)) {
87
- lines.push(` ${sanitizeKey(key)}?: ${inferTsType(meta[key])};`);
88
- }
89
- }
90
- lines.push('}');
91
- lines.push('');
92
-
93
- // Conflict interface
94
- lines.push('export interface Conflict {');
95
- lines.push(' ids?: string[];');
96
- lines.push(' description?: string;');
97
- lines.push(' reason?: string;');
98
- lines.push(' resolution?: string;');
99
- lines.push('}');
100
- lines.push('');
101
-
102
- // Certificate interface
103
- lines.push('export interface Certificate {');
104
- lines.push(' compiled_at?: string;');
105
- lines.push(' sha256?: string;');
106
- lines.push(' claim_count?: number;');
107
- for (const key of Object.keys(certificate)) {
108
- if (!['compiled_at', 'sha256', 'claim_count'].includes(key)) {
109
- lines.push(` ${sanitizeKey(key)}?: ${inferTsType(certificate[key])};`);
110
- }
111
- }
112
- lines.push('}');
113
- lines.push('');
114
-
115
- // Compilation interface
116
- lines.push('export interface Compilation {');
117
- lines.push(' meta: Meta;');
118
- lines.push(' claims: Claim[];');
119
- lines.push(' conflicts: Conflict[];');
120
- lines.push(' certificate: Certificate;');
121
- lines.push('}');
122
- lines.push('');
123
-
124
- return lines.join('\n');
125
- }
126
-
127
- function uniqueSorted(arr) {
128
- return [...new Set(arr)].sort();
129
- }
130
-
131
- function unionType(values) {
132
- if (values.length === 0) return 'string';
133
- return values.map(v => `'${v}'`).join(' | ');
134
- }
135
-
136
- function sanitizeKey(key) {
137
- return /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(key) ? key : `'${key}'`;
138
- }
139
-
140
- function inferTsType(value) {
141
- if (value === null || value === undefined) return 'unknown';
142
- if (typeof value === 'number') return 'number';
143
- if (typeof value === 'boolean') return 'boolean';
144
- if (typeof value === 'string') return 'string';
145
- if (Array.isArray(value)) return 'unknown[]';
146
- return 'Record<string, unknown>';
147
- }