@grainulation/mill 1.0.0 → 1.0.1

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 (41) 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.js +41 -34
  10. package/lib/formats/changelog.js +27 -26
  11. package/lib/formats/confluence-adf.js +312 -0
  12. package/lib/formats/csv.js +41 -37
  13. package/lib/formats/dot.js +45 -34
  14. package/lib/formats/evidence-matrix.js +17 -16
  15. package/lib/formats/executive-summary.js +89 -41
  16. package/lib/formats/github-issues.js +40 -33
  17. package/lib/formats/graphml.js +45 -32
  18. package/lib/formats/html-report.js +110 -63
  19. package/lib/formats/jira-csv.js +30 -29
  20. package/lib/formats/json-ld.js +6 -6
  21. package/lib/formats/markdown.js +53 -36
  22. package/lib/formats/ndjson.js +6 -6
  23. package/lib/formats/obsidian.js +43 -35
  24. package/lib/formats/opml.js +38 -28
  25. package/lib/formats/ris.js +29 -23
  26. package/lib/formats/rss.js +31 -28
  27. package/lib/formats/sankey.js +16 -15
  28. package/lib/formats/slide-deck.js +145 -57
  29. package/lib/formats/sql.js +57 -53
  30. package/lib/formats/static-site.js +64 -52
  31. package/lib/formats/treemap.js +16 -15
  32. package/lib/formats/typescript-defs.js +79 -76
  33. package/lib/formats/yaml.js +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 +7 -3
@@ -7,10 +7,11 @@
7
7
  * Zero dependencies — node built-in only.
8
8
  */
9
9
 
10
- export const name = 'static-site';
11
- export const extension = '.json';
12
- export const mimeType = 'application/json; charset=utf-8';
13
- export const description = 'Hugo-compatible static site manifest (file tree as JSON)';
10
+ export const name = "static-site";
11
+ export const extension = ".json";
12
+ export const mimeType = "application/json; charset=utf-8";
13
+ export const description =
14
+ "Hugo-compatible static site manifest (file tree as JSON)";
14
15
 
15
16
  /**
16
17
  * Convert a compilation object to a static site manifest.
@@ -22,148 +23,159 @@ export function convert(compilation) {
22
23
  const meta = compilation.meta || {};
23
24
  const certificate = compilation.certificate || {};
24
25
 
25
- const sprintName = meta.sprint || 'sprint';
26
- const question = meta.question || '';
27
- const audience = meta.audience || '';
26
+ const sprintName = meta.sprint || "sprint";
27
+ const question = meta.question || "";
28
+ const audience = meta.audience || "";
28
29
 
29
30
  const files = {};
30
31
 
31
32
  // Hugo config
32
- files['config.yaml'] = buildConfig(sprintName, question);
33
+ files["config.yaml"] = buildConfig(sprintName, question);
33
34
 
34
35
  // Index page
35
- files['content/_index.md'] = buildIndex(sprintName, question, audience, claims);
36
+ files["content/_index.md"] = buildIndex(
37
+ sprintName,
38
+ question,
39
+ audience,
40
+ claims,
41
+ );
36
42
 
37
43
  // Per-type section pages
38
44
  const groups = {};
39
45
  for (const claim of claims) {
40
- const type = claim.type || 'other';
46
+ const type = claim.type || "other";
41
47
  if (!groups[type]) groups[type] = [];
42
48
  groups[type].push(claim);
43
49
  }
44
50
 
45
51
  for (const [type, group] of Object.entries(groups)) {
46
- files[`content/claims/${type}/_index.md`] = buildSectionIndex(type, group.length);
52
+ files[`content/claims/${type}/_index.md`] = buildSectionIndex(
53
+ type,
54
+ group.length,
55
+ );
47
56
 
48
57
  for (const claim of group) {
49
- const id = claim.id || 'unknown';
58
+ const id = claim.id || "unknown";
50
59
  files[`content/claims/${type}/${id}.md`] = buildClaimPage(claim);
51
60
  }
52
61
  }
53
62
 
54
63
  // Data file: full claims array
55
- files['data/claims.json'] = JSON.stringify(claims, null, 2);
64
+ files["data/claims.json"] = JSON.stringify(claims, null, 2);
56
65
 
57
66
  // Data file: certificate
58
67
  if (Object.keys(certificate).length > 0) {
59
- files['data/certificate.json'] = JSON.stringify(certificate, null, 2);
68
+ files["data/certificate.json"] = JSON.stringify(certificate, null, 2);
60
69
  }
61
70
 
62
71
  // Data file: meta
63
- files['data/meta.json'] = JSON.stringify(meta, null, 2);
72
+ files["data/meta.json"] = JSON.stringify(meta, null, 2);
64
73
 
65
74
  const manifest = {
66
- generator: 'mill',
75
+ generator: "mill",
67
76
  sprint: sprintName,
68
77
  claimCount: claims.length,
69
78
  files,
70
79
  };
71
80
 
72
- return JSON.stringify(manifest, null, 2) + '\n';
81
+ return JSON.stringify(manifest, null, 2) + "\n";
73
82
  }
74
83
 
75
84
  function buildConfig(sprintName, question) {
76
85
  const lines = [];
77
- lines.push(`baseURL: "https://your-site.example.com/${sanitizeSlug(sprintName)}/" # Update with your domain`);
86
+ lines.push(
87
+ `baseURL: "https://your-site.example.com/${sanitizeSlug(sprintName)}/" # Update with your domain`,
88
+ );
78
89
  lines.push(`title: "${escYaml(sprintName)}"`);
79
90
  lines.push(`theme: "mill-default"`);
80
91
  lines.push('languageCode: "en-us"');
81
- lines.push('');
82
- lines.push('params:');
92
+ lines.push("");
93
+ lines.push("params:");
83
94
  lines.push(` question: "${escYaml(question)}"`);
84
95
  lines.push(` generator: "mill"`);
85
- return lines.join('\n') + '\n';
96
+ return lines.join("\n") + "\n";
86
97
  }
87
98
 
88
99
  function buildIndex(sprintName, question, audience, claims) {
89
100
  const lines = [];
90
- lines.push('---');
101
+ lines.push("---");
91
102
  lines.push(`title: "${escYaml(sprintName)}"`);
92
103
  lines.push(`description: "${escYaml(question)}"`);
93
104
  lines.push('type: "index"');
94
- lines.push('---');
95
- lines.push('');
105
+ lines.push("---");
106
+ lines.push("");
96
107
  lines.push(`# ${sprintName}`);
97
- lines.push('');
108
+ lines.push("");
98
109
  if (question) lines.push(`**Question:** ${question}`);
99
110
  if (audience) lines.push(`**Audience:** ${audience}`);
100
- lines.push('');
111
+ lines.push("");
101
112
  lines.push(`**Total claims:** ${claims.length}`);
102
113
 
103
114
  // Type summary
104
115
  const typeCounts = {};
105
116
  for (const c of claims) {
106
- const t = c.type || 'other';
117
+ const t = c.type || "other";
107
118
  typeCounts[t] = (typeCounts[t] || 0) + 1;
108
119
  }
109
- lines.push('');
120
+ lines.push("");
110
121
  for (const [type, count] of Object.entries(typeCounts)) {
111
122
  lines.push(`- ${type}: ${count}`);
112
123
  }
113
124
 
114
- return lines.join('\n') + '\n';
125
+ return lines.join("\n") + "\n";
115
126
  }
116
127
 
117
128
  function buildSectionIndex(type, count) {
118
129
  const lines = [];
119
- lines.push('---');
130
+ lines.push("---");
120
131
  lines.push(`title: "${escYaml(type)}"`);
121
132
  lines.push(`type: "section"`);
122
- lines.push('---');
123
- lines.push('');
124
- lines.push(`${count} ${type} claim${count !== 1 ? 's' : ''}.`);
125
- return lines.join('\n') + '\n';
133
+ lines.push("---");
134
+ lines.push("");
135
+ lines.push(`${count} ${type} claim${count !== 1 ? "s" : ""}.`);
136
+ return lines.join("\n") + "\n";
126
137
  }
127
138
 
128
139
  function buildClaimPage(claim) {
129
- const id = claim.id || 'unknown';
130
- const type = claim.type || '';
131
- const content = claim.content || claim.text || '';
140
+ const id = claim.id || "unknown";
141
+ const type = claim.type || "";
142
+ const content = claim.content || claim.text || "";
132
143
  const evidence = getEvidence(claim);
133
- const status = claim.status || '';
144
+ const status = claim.status || "";
134
145
  const tags = Array.isArray(claim.tags) ? claim.tags : [];
135
- const confidence = claim.confidence != null ? claim.confidence : '';
146
+ const confidence = claim.confidence != null ? claim.confidence : "";
136
147
 
137
148
  const lines = [];
138
- lines.push('---');
149
+ lines.push("---");
139
150
  lines.push(`title: "${escYaml(id)}"`);
140
151
  lines.push(`type: "${escYaml(type)}"`);
141
152
  lines.push(`evidence: "${escYaml(evidence)}"`);
142
153
  lines.push(`status: "${escYaml(status)}"`);
143
- if (confidence !== '') lines.push(`confidence: ${confidence}`);
144
- if (tags.length > 0) lines.push(`tags: [${tags.map(t => `"${escYaml(t)}"`).join(', ')}]`);
145
- lines.push('---');
146
- lines.push('');
154
+ if (confidence !== "") lines.push(`confidence: ${confidence}`);
155
+ if (tags.length > 0)
156
+ lines.push(`tags: [${tags.map((t) => `"${escYaml(t)}"`).join(", ")}]`);
157
+ lines.push("---");
158
+ lines.push("");
147
159
  lines.push(content);
148
- return lines.join('\n') + '\n';
160
+ return lines.join("\n") + "\n";
149
161
  }
150
162
 
151
163
  function getEvidence(claim) {
152
- if (typeof claim.evidence === 'string') return claim.evidence;
153
- if (typeof claim.evidence === 'object' && claim.evidence !== null) {
154
- return claim.evidence.tier || claim.evidence_tier || '';
164
+ if (typeof claim.evidence === "string") return claim.evidence;
165
+ if (typeof claim.evidence === "object" && claim.evidence !== null) {
166
+ return claim.evidence.tier || claim.evidence_tier || "";
155
167
  }
156
- return claim.evidence_tier || '';
168
+ return claim.evidence_tier || "";
157
169
  }
158
170
 
159
171
  function escYaml(str) {
160
- if (str == null) return '';
172
+ if (str == null) return "";
161
173
  return String(str).replace(/"/g, '\\"');
162
174
  }
163
175
 
164
176
  function sanitizeSlug(str) {
165
177
  return String(str)
166
178
  .toLowerCase()
167
- .replace(/[^a-z0-9]+/g, '-')
168
- .replace(/^-+|-+$/g, '');
179
+ .replace(/[^a-z0-9]+/g, "-")
180
+ .replace(/^-+|-+$/g, "");
169
181
  }
@@ -6,10 +6,11 @@
6
6
  * Zero dependencies — node built-in only.
7
7
  */
8
8
 
9
- export const name = 'treemap';
10
- export const extension = '.json';
11
- export const mimeType = 'application/json; charset=utf-8';
12
- export const description = 'Claims as nested JSON for D3 treemap visualization (grouped by type)';
9
+ export const name = "treemap";
10
+ export const extension = ".json";
11
+ export const mimeType = "application/json; charset=utf-8";
12
+ export const description =
13
+ "Claims as nested JSON for D3 treemap visualization (grouped by type)";
13
14
 
14
15
  /**
15
16
  * Convert a compilation object to D3 treemap JSON.
@@ -18,12 +19,12 @@ export const description = 'Claims as nested JSON for D3 treemap visualization (
18
19
  */
19
20
  export function convert(compilation) {
20
21
  const claims = compilation.claims || [];
21
- const sprintName = compilation.meta?.sprint || 'sprint';
22
+ const sprintName = compilation.meta?.sprint || "sprint";
22
23
 
23
24
  // Group claims by type
24
25
  const groups = {};
25
26
  for (const claim of claims) {
26
- const type = claim.type || 'other';
27
+ const type = claim.type || "other";
27
28
  if (!groups[type]) groups[type] = [];
28
29
  groups[type].push(claim);
29
30
  }
@@ -32,17 +33,17 @@ export function convert(compilation) {
32
33
  name: sprintName,
33
34
  children: Object.entries(groups).map(([type, group]) => ({
34
35
  name: type,
35
- children: group.map(claim => {
36
+ children: group.map((claim) => {
36
37
  const node = {
37
- name: claim.id || '',
38
+ name: claim.id || "",
38
39
  value: claim.confidence != null ? claim.confidence : 1,
39
40
  evidence: getEvidence(claim),
40
41
  };
41
42
 
42
- const content = claim.content || claim.text || '';
43
+ const content = claim.content || claim.text || "";
43
44
  if (content) node.content = content;
44
45
 
45
- const status = claim.status || '';
46
+ const status = claim.status || "";
46
47
  if (status) node.status = status;
47
48
 
48
49
  const tags = Array.isArray(claim.tags) ? claim.tags : [];
@@ -53,13 +54,13 @@ export function convert(compilation) {
53
54
  })),
54
55
  };
55
56
 
56
- return JSON.stringify(tree, null, 2) + '\n';
57
+ return JSON.stringify(tree, null, 2) + "\n";
57
58
  }
58
59
 
59
60
  function getEvidence(claim) {
60
- if (typeof claim.evidence === 'string') return claim.evidence;
61
- if (typeof claim.evidence === 'object' && claim.evidence !== null) {
62
- return claim.evidence.tier || claim.evidence_tier || '';
61
+ if (typeof claim.evidence === "string") return claim.evidence;
62
+ if (typeof claim.evidence === "object" && claim.evidence !== null) {
63
+ return claim.evidence.tier || claim.evidence_tier || "";
63
64
  }
64
- return claim.evidence_tier || '';
65
+ return claim.evidence_tier || "";
65
66
  }
@@ -6,10 +6,11 @@
6
6
  * Zero dependencies — node built-in only.
7
7
  */
8
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';
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 =
13
+ "TypeScript type definitions derived from compilation data";
13
14
 
14
15
  /**
15
16
  * Convert a compilation object to TypeScript .d.ts definitions.
@@ -22,106 +23,108 @@ export function convert(compilation) {
22
23
  const conflicts = compilation.conflicts || [];
23
24
  const certificate = compilation.certificate || {};
24
25
 
25
- const claimTypes = uniqueSorted(claims.map(c => c.type).filter(Boolean));
26
+ const claimTypes = uniqueSorted(claims.map((c) => c.type).filter(Boolean));
26
27
  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)
28
+ claims
29
+ .map((c) => {
30
+ if (typeof c.evidence === "string") return c.evidence;
31
+ return c.evidence?.tier ?? c.evidence_tier ?? null;
32
+ })
33
+ .filter(Boolean),
31
34
  );
32
- const statuses = uniqueSorted(claims.map(c => c.status).filter(Boolean));
35
+ const statuses = uniqueSorted(claims.map((c) => c.status).filter(Boolean));
33
36
 
34
37
  const lines = [];
35
38
 
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
+ lines.push("// Auto-generated by mill typescript-defs format");
40
+ lines.push("// Derived from compilation data — do not edit manually");
41
+ lines.push("");
39
42
 
40
43
  // ClaimType union
41
44
  lines.push(`export type ClaimType = ${unionType(claimTypes)};`);
42
- lines.push('');
45
+ lines.push("");
43
46
 
44
47
  // EvidenceTier union
45
48
  lines.push(`export type EvidenceTier = ${unionType(evidenceTiers)};`);
46
- lines.push('');
49
+ lines.push("");
47
50
 
48
51
  // ClaimStatus union
49
52
  lines.push(`export type ClaimStatus = ${unionType(statuses)};`);
50
- lines.push('');
53
+ lines.push("");
51
54
 
52
55
  // Evidence interface
53
- lines.push('export interface Evidence {');
54
- lines.push(' tier: EvidenceTier;');
55
- lines.push(' source?: string;');
56
- lines.push('}');
57
- lines.push('');
56
+ lines.push("export interface Evidence {");
57
+ lines.push(" tier: EvidenceTier;");
58
+ lines.push(" source?: string;");
59
+ lines.push("}");
60
+ lines.push("");
58
61
 
59
62
  // Source interface
60
- lines.push('export interface ClaimSource {');
61
- lines.push(' origin?: string;');
62
- lines.push(' artifact?: string;');
63
- lines.push('}');
64
- lines.push('');
63
+ lines.push("export interface ClaimSource {");
64
+ lines.push(" origin?: string;");
65
+ lines.push(" artifact?: string;");
66
+ lines.push("}");
67
+ lines.push("");
65
68
 
66
69
  // 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('');
70
+ lines.push("export interface Claim {");
71
+ lines.push(" id: string;");
72
+ lines.push(" type: ClaimType;");
73
+ lines.push(" content: string;");
74
+ lines.push(" evidence: EvidenceTier | Evidence;");
75
+ lines.push(" status: ClaimStatus;");
76
+ lines.push(" confidence?: number;");
77
+ lines.push(" source?: string | ClaimSource;");
78
+ lines.push(" tags?: string[];");
79
+ lines.push(" created?: string;");
80
+ lines.push("}");
81
+ lines.push("");
79
82
 
80
83
  // Meta interface
81
- lines.push('export interface Meta {');
82
- lines.push(' sprint?: string;');
83
- lines.push(' question?: string;');
84
- lines.push(' audience?: string;');
84
+ lines.push("export interface Meta {");
85
+ lines.push(" sprint?: string;");
86
+ lines.push(" question?: string;");
87
+ lines.push(" audience?: string;");
85
88
  for (const key of Object.keys(meta)) {
86
- if (!['sprint', 'question', 'audience'].includes(key)) {
89
+ if (!["sprint", "question", "audience"].includes(key)) {
87
90
  lines.push(` ${sanitizeKey(key)}?: ${inferTsType(meta[key])};`);
88
91
  }
89
92
  }
90
- lines.push('}');
91
- lines.push('');
93
+ lines.push("}");
94
+ lines.push("");
92
95
 
93
96
  // 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('');
97
+ lines.push("export interface Conflict {");
98
+ lines.push(" ids?: string[];");
99
+ lines.push(" description?: string;");
100
+ lines.push(" reason?: string;");
101
+ lines.push(" resolution?: string;");
102
+ lines.push("}");
103
+ lines.push("");
101
104
 
102
105
  // 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;');
106
+ lines.push("export interface Certificate {");
107
+ lines.push(" compiled_at?: string;");
108
+ lines.push(" sha256?: string;");
109
+ lines.push(" claim_count?: number;");
107
110
  for (const key of Object.keys(certificate)) {
108
- if (!['compiled_at', 'sha256', 'claim_count'].includes(key)) {
111
+ if (!["compiled_at", "sha256", "claim_count"].includes(key)) {
109
112
  lines.push(` ${sanitizeKey(key)}?: ${inferTsType(certificate[key])};`);
110
113
  }
111
114
  }
112
- lines.push('}');
113
- lines.push('');
115
+ lines.push("}");
116
+ lines.push("");
114
117
 
115
118
  // 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');
119
+ lines.push("export interface Compilation {");
120
+ lines.push(" meta: Meta;");
121
+ lines.push(" claims: Claim[];");
122
+ lines.push(" conflicts: Conflict[];");
123
+ lines.push(" certificate: Certificate;");
124
+ lines.push("}");
125
+ lines.push("");
126
+
127
+ return lines.join("\n");
125
128
  }
126
129
 
127
130
  function uniqueSorted(arr) {
@@ -129,8 +132,8 @@ function uniqueSorted(arr) {
129
132
  }
130
133
 
131
134
  function unionType(values) {
132
- if (values.length === 0) return 'string';
133
- return values.map(v => `'${v}'`).join(' | ');
135
+ if (values.length === 0) return "string";
136
+ return values.map((v) => `'${v}'`).join(" | ");
134
137
  }
135
138
 
136
139
  function sanitizeKey(key) {
@@ -138,10 +141,10 @@ function sanitizeKey(key) {
138
141
  }
139
142
 
140
143
  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>';
144
+ if (value === null || value === undefined) return "unknown";
145
+ if (typeof value === "number") return "number";
146
+ if (typeof value === "boolean") return "boolean";
147
+ if (typeof value === "string") return "string";
148
+ if (Array.isArray(value)) return "unknown[]";
149
+ return "Record<string, unknown>";
147
150
  }