@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
package/lib/formats.js CHANGED
@@ -1,17 +1,17 @@
1
- 'use strict';
1
+ "use strict";
2
2
 
3
- const pdf = require('./exporters/pdf.js');
4
- const csv = require('./exporters/csv.js');
5
- const markdown = require('./exporters/markdown.js');
6
- const jsonLd = require('./exporters/json-ld.js');
7
- const static_ = require('./publishers/static.js');
8
- const clipboard = require('./publishers/clipboard.js');
3
+ const pdf = require("./exporters/pdf.js");
4
+ const csv = require("./exporters/csv.js");
5
+ const markdown = require("./exporters/markdown.js");
6
+ const jsonLd = require("./exporters/json-ld.js");
7
+ const static_ = require("./publishers/static.js");
8
+ const clipboard = require("./publishers/clipboard.js");
9
9
 
10
10
  const EXPORTERS = {
11
11
  pdf,
12
12
  csv,
13
13
  markdown,
14
- 'json-ld': jsonLd,
14
+ "json-ld": jsonLd,
15
15
  };
16
16
 
17
17
  const PUBLISHERS = {
@@ -23,16 +23,16 @@ const PUBLISHERS = {
23
23
  * Detect the likely format of an input file by extension.
24
24
  */
25
25
  function detectFormat(filePath) {
26
- const ext = filePath.split('.').pop().toLowerCase();
26
+ const ext = filePath.split(".").pop().toLowerCase();
27
27
  const map = {
28
- html: 'html',
29
- htm: 'html',
30
- md: 'markdown',
31
- json: 'json',
32
- csv: 'csv',
33
- jsonld: 'json-ld',
28
+ html: "html",
29
+ htm: "html",
30
+ md: "markdown",
31
+ json: "json",
32
+ csv: "csv",
33
+ jsonld: "json-ld",
34
34
  };
35
- return map[ext] || 'unknown';
35
+ return map[ext] || "unknown";
36
36
  }
37
37
 
38
38
  function getExporter(name) {
package/lib/index.js CHANGED
@@ -1,10 +1,10 @@
1
- 'use strict';
1
+ "use strict";
2
2
 
3
- const formats = require('./formats.js');
3
+ const formats = require("./formats.js");
4
4
 
5
- const name = 'mill';
6
- const version = require('../package.json').version;
7
- const description = 'Turn wheat sprint artifacts into shareable formats';
5
+ const name = "mill";
6
+ const version = require("../package.json").version;
7
+ const description = "Turn wheat sprint artifacts into shareable formats";
8
8
 
9
9
  module.exports = {
10
10
  name,
@@ -1,4 +1,4 @@
1
- 'use strict';
1
+ "use strict";
2
2
 
3
3
  /**
4
4
  * json-ld-common.js — Shared JSON-LD vocabulary for mill
@@ -11,24 +11,25 @@
11
11
  */
12
12
 
13
13
  const CONTEXT = {
14
- '@vocab': 'https://schema.org/',
15
- wheat: 'https://grainulation.com/ns/wheat#',
16
- claim: 'wheat:Claim',
17
- confidence: 'wheat:confidence',
18
- evidenceTier: 'wheat:evidenceTier',
19
- claimType: 'wheat:claimType',
20
- sprintId: 'wheat:sprintId',
14
+ "@vocab": "https://schema.org/",
15
+ wheat: "https://grainulation.com/ns/wheat#",
16
+ claim: "wheat:Claim",
17
+ confidence: "wheat:confidence",
18
+ evidenceTier: "wheat:evidenceTier",
19
+ claimType: "wheat:claimType",
20
+ sprintId: "wheat:sprintId",
21
21
  };
22
22
 
23
23
  function claimToJsonLd(claim) {
24
- const body = claim.content || claim.text || '';
25
- const evidenceTier = typeof claim.evidence === 'string'
26
- ? claim.evidence
27
- : (claim.evidence?.tier ?? claim.evidence_tier ?? null);
24
+ const body = claim.content || claim.text || "";
25
+ const evidenceTier =
26
+ typeof claim.evidence === "string"
27
+ ? claim.evidence
28
+ : (claim.evidence?.tier ?? claim.evidence_tier ?? null);
28
29
 
29
30
  return {
30
- '@type': 'claim',
31
- '@id': `wheat:claim/${claim.id}`,
31
+ "@type": "claim",
32
+ "@id": `wheat:claim/${claim.id}`,
32
33
  identifier: claim.id,
33
34
  claimType: claim.type,
34
35
  text: body,
@@ -36,36 +37,41 @@ function claimToJsonLd(claim) {
36
37
  confidence: claim.confidence ?? null,
37
38
  evidenceTier,
38
39
  dateCreated: claim.created || claim.timestamp || null,
39
- ...(claim.tags?.length ? { keywords: claim.tags.join(', ') } : {}),
40
+ ...(claim.tags?.length ? { keywords: claim.tags.join(", ") } : {}),
40
41
  ...(claim.status ? { status: claim.status } : {}),
41
42
  };
42
43
  }
43
44
 
44
45
  function buildReport(meta, claims, certificate) {
45
46
  return {
46
- '@context': CONTEXT,
47
- '@type': 'Report',
48
- '@id': `wheat:sprint/${meta.sprint || 'unknown'}`,
49
- name: meta.sprint || meta.question || 'Wheat Sprint Report',
50
- description: meta.question || '',
51
- dateCreated: (certificate && certificate.compiled_at) || new Date().toISOString(),
52
- ...(meta.audience ? { audience: { '@type': 'Audience', name: meta.audience } } : {}),
47
+ "@context": CONTEXT,
48
+ "@type": "Report",
49
+ "@id": `wheat:sprint/${meta.sprint || "unknown"}`,
50
+ name: meta.sprint || meta.question || "Wheat Sprint Report",
51
+ description: meta.question || "",
52
+ dateCreated:
53
+ (certificate && certificate.compiled_at) || new Date().toISOString(),
54
+ ...(meta.audience
55
+ ? { audience: { "@type": "Audience", name: meta.audience } }
56
+ : {}),
53
57
  hasPart: {
54
- '@type': 'ItemList',
58
+ "@type": "ItemList",
55
59
  numberOfItems: claims.length,
56
60
  itemListElement: claims.map((claim, i) => ({
57
- '@type': 'ListItem',
61
+ "@type": "ListItem",
58
62
  position: i + 1,
59
63
  item: claimToJsonLd(claim),
60
64
  })),
61
65
  },
62
- ...((certificate && certificate.sha256) ? {
63
- identifier: {
64
- '@type': 'PropertyValue',
65
- name: 'certificate-sha256',
66
- value: certificate.sha256,
67
- },
68
- } : {}),
66
+ ...(certificate && certificate.sha256
67
+ ? {
68
+ identifier: {
69
+ "@type": "PropertyValue",
70
+ name: "certificate-sha256",
71
+ value: certificate.sha256,
72
+ },
73
+ }
74
+ : {}),
69
75
  };
70
76
  }
71
77
 
@@ -1,8 +1,8 @@
1
- 'use strict';
1
+ "use strict";
2
2
 
3
- const fs = require('node:fs');
4
- const path = require('node:path');
5
- const { execFile } = require('node:child_process');
3
+ const fs = require("node:fs");
4
+ const path = require("node:path");
5
+ const { execFile } = require("node:child_process");
6
6
 
7
7
  /**
8
8
  * Copy formatted output to system clipboard.
@@ -11,12 +11,12 @@ const { execFile } = require('node:child_process');
11
11
 
12
12
  function getClipboardCommand() {
13
13
  switch (process.platform) {
14
- case 'darwin':
15
- return { cmd: 'pbcopy', args: [] };
16
- case 'linux':
17
- return { cmd: 'xclip', args: ['-selection', 'clipboard'] };
18
- case 'win32':
19
- return { cmd: 'clip', args: [] };
14
+ case "darwin":
15
+ return { cmd: "pbcopy", args: [] };
16
+ case "linux":
17
+ return { cmd: "xclip", args: ["-selection", "clipboard"] };
18
+ case "win32":
19
+ return { cmd: "clip", args: [] };
20
20
  default:
21
21
  return null;
22
22
  }
@@ -46,25 +46,27 @@ async function publishClipboard(inputPath) {
46
46
  let content;
47
47
  if (stat.isDirectory()) {
48
48
  // If directory, list the files
49
- const files = fs.readdirSync(inputPath).filter((f) => !f.startsWith('.'));
50
- content = files.map((f) => {
51
- const full = path.join(inputPath, f);
52
- return fs.readFileSync(full, 'utf-8');
53
- }).join('\n\n---\n\n');
49
+ const files = fs.readdirSync(inputPath).filter((f) => !f.startsWith("."));
50
+ content = files
51
+ .map((f) => {
52
+ const full = path.join(inputPath, f);
53
+ return fs.readFileSync(full, "utf-8");
54
+ })
55
+ .join("\n\n---\n\n");
54
56
  } else {
55
- content = fs.readFileSync(inputPath, 'utf-8');
57
+ content = fs.readFileSync(inputPath, "utf-8");
56
58
  }
57
59
 
58
60
  await copyToClipboard(content);
59
61
 
60
- const size = Buffer.byteLength(content, 'utf-8');
62
+ const size = Buffer.byteLength(content, "utf-8");
61
63
  return {
62
64
  message: `Copied to clipboard (${size} bytes)`,
63
65
  };
64
66
  }
65
67
 
66
68
  module.exports = {
67
- name: 'clipboard',
68
- description: 'Copy formatted output to system clipboard',
69
+ name: "clipboard",
70
+ description: "Copy formatted output to system clipboard",
69
71
  publish: publishClipboard,
70
72
  };
@@ -1,9 +1,16 @@
1
- 'use strict';
1
+ "use strict";
2
2
 
3
- const fs = require('node:fs');
4
- const path = require('node:path');
3
+ const fs = require("node:fs");
4
+ const path = require("node:path");
5
5
 
6
- function esc(s) { return String(s || '').replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/"/g,'&quot;').replace(/'/g,'&#39;'); }
6
+ function esc(s) {
7
+ return String(s || "")
8
+ .replace(/&/g, "&amp;")
9
+ .replace(/</g, "&lt;")
10
+ .replace(/>/g, "&gt;")
11
+ .replace(/"/g, "&quot;")
12
+ .replace(/'/g, "&#39;");
13
+ }
7
14
 
8
15
  /**
9
16
  * Generate a static site from sprint output directory.
@@ -93,7 +100,7 @@ function scanDir(dir, base) {
93
100
  const stat = fs.statSync(full);
94
101
  const real = fs.realpathSync(full);
95
102
  if (!real.startsWith(fs.realpathSync(base))) continue;
96
- if (stat.isFile() && !name.startsWith('.')) {
103
+ if (stat.isFile() && !name.startsWith(".")) {
97
104
  const rel = path.relative(base, full);
98
105
  entries.push({
99
106
  name,
@@ -102,7 +109,11 @@ function scanDir(dir, base) {
102
109
  ext: path.extname(name).slice(1).toLowerCase(),
103
110
  modified: stat.mtime,
104
111
  });
105
- } else if (stat.isDirectory() && !name.startsWith('.') && name !== '_site') {
112
+ } else if (
113
+ stat.isDirectory() &&
114
+ !name.startsWith(".") &&
115
+ name !== "_site"
116
+ ) {
106
117
  entries.push(...scanDir(full, base));
107
118
  }
108
119
  }
@@ -110,7 +121,7 @@ function scanDir(dir, base) {
110
121
  }
111
122
 
112
123
  async function publishStatic(inputDir, outputDir) {
113
- const outDir = outputDir || path.join(inputDir, '_site');
124
+ const outDir = outputDir || path.join(inputDir, "_site");
114
125
  if (!fs.existsSync(outDir)) {
115
126
  fs.mkdirSync(outDir, { recursive: true });
116
127
  }
@@ -130,14 +141,18 @@ async function publishStatic(inputDir, outputDir) {
130
141
 
131
142
  // Build index
132
143
  const sprintName = path.basename(path.resolve(inputDir));
133
- const cards = entries.map((e) => `
144
+ const cards = entries
145
+ .map(
146
+ (e) => `
134
147
  <div class="card">
135
148
  <a href="${esc(e.path)}">${esc(e.name)}</a>
136
149
  <div class="meta">${e.ext.toUpperCase()} &middot; ${formatBytes(e.size)}</div>
137
- </div>`).join('\n');
150
+ </div>`,
151
+ )
152
+ .join("\n");
138
153
 
139
154
  const html = TEMPLATE(`Sprint: ${sprintName}`, cards);
140
- fs.writeFileSync(path.join(outDir, 'index.html'), html, 'utf-8');
155
+ fs.writeFileSync(path.join(outDir, "index.html"), html, "utf-8");
141
156
 
142
157
  return {
143
158
  outputPath: outDir,
@@ -146,7 +161,7 @@ async function publishStatic(inputDir, outputDir) {
146
161
  }
147
162
 
148
163
  module.exports = {
149
- name: 'static',
150
- description: 'Generate a static site from sprint outputs',
164
+ name: "static",
165
+ description: "Generate a static site from sprint outputs",
151
166
  publish: publishStatic,
152
167
  };