atris 3.47.0 → 3.48.0

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.
@@ -0,0 +1,91 @@
1
+ #!/usr/bin/env node
2
+ // det/date.js — deterministic date/time conversion. Epoch<->ISO and weekday are
3
+ // the asks LLMs fumble most (seconds vs ms, and timezone guesses). Everything is
4
+ // UTC so the answer is the same on every machine. Reads stdin, writes stdout.
5
+ //
6
+ // Usage:
7
+ // echo 1700000000 | node date.js iso # epoch (s or ms) -> ISO 8601 UTC
8
+ // echo 2026-07-07 | node date.js epoch # date/ISO -> epoch seconds
9
+ // echo 2026-07-07 | node date.js epochms # -> epoch milliseconds
10
+ // echo 2026-07-07 | node date.js weekday # -> Monday..Sunday (UTC)
11
+ //
12
+ // Modes: iso | epoch | epochms | weekday
13
+ // Bare date strings (no timezone) are read as UTC. Exit 2 on bad mode/input.
14
+
15
+ 'use strict';
16
+
17
+ const DAYS = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];
18
+
19
+ // Parse stdin into a Date, deterministically. A pure-digit string is an epoch
20
+ // (>=13 digits = ms, else seconds). Otherwise a date string; if it carries no
21
+ // timezone we pin it to UTC by appending 'Z' so machines don't disagree.
22
+ function toDate(s) {
23
+ const t = s.trim();
24
+ if (t === '') return { error: 'empty input' };
25
+ if (/^-?\d+$/.test(t)) {
26
+ const n = Number(t);
27
+ const ms = t.replace('-', '').length >= 13 ? n : n * 1000;
28
+ return { date: new Date(ms) };
29
+ }
30
+ // ISO-ish without an explicit zone/offset -> treat as UTC.
31
+ let str = t;
32
+ const hasZone = /[zZ]$|[+-]\d{2}:?\d{2}$/.test(t);
33
+ if (!hasZone) {
34
+ str = /\d{4}-\d{2}-\d{2}$/.test(t) ? t + 'T00:00:00Z' : t + 'Z';
35
+ }
36
+ const d = new Date(str);
37
+ if (Number.isNaN(d.getTime())) return { error: `cannot parse date: ${t}` };
38
+ return { date: d };
39
+ }
40
+
41
+ // Pure core: returns { text } or { error }.
42
+ function run(mode, input) {
43
+ const p = toDate(input);
44
+ if (p.error) return { error: p.error };
45
+ const d = p.date;
46
+ switch (mode) {
47
+ case 'iso':
48
+ return { text: d.toISOString() };
49
+ case 'epoch':
50
+ return { text: String(Math.floor(d.getTime() / 1000)) };
51
+ case 'epochms':
52
+ return { text: String(d.getTime()) };
53
+ case 'weekday':
54
+ return { text: DAYS[d.getUTCDay()] };
55
+ default:
56
+ return { error: `unknown mode: ${mode}` };
57
+ }
58
+ }
59
+
60
+ function readStdin() {
61
+ return new Promise((resolve) => {
62
+ let data = '';
63
+ process.stdin.setEncoding('utf8');
64
+ process.stdin.on('data', (c) => (data += c));
65
+ process.stdin.on('end', () => resolve(data));
66
+ if (process.stdin.isTTY) resolve('');
67
+ });
68
+ }
69
+
70
+ const MODES = ['iso', 'epoch', 'epochms', 'weekday'];
71
+
72
+ async function main() {
73
+ const mode = process.argv.slice(2).find((a) => !a.startsWith('-'));
74
+ if (!mode || !MODES.includes(mode)) {
75
+ process.stderr.write(`unknown mode: ${mode || '(none)'}\nmodes: ${MODES.join(' | ')}\n`);
76
+ process.exit(2);
77
+ }
78
+ const input = await readStdin();
79
+ const res = run(mode, input);
80
+ if (res.error) {
81
+ process.stderr.write(res.error + '\n');
82
+ process.exit(2);
83
+ }
84
+ if (res.text.length) process.stdout.write(res.text + '\n');
85
+ }
86
+
87
+ if (require.main === module) {
88
+ main();
89
+ }
90
+
91
+ module.exports = { run, toDate, MODES };
@@ -0,0 +1,149 @@
1
+ #!/usr/bin/env node
2
+ // det/det.js — one entrypoint for the deterministic task scripts. A cheap model
3
+ // runs `node det.js` to see the whole catalog, then `node det.js <script> <mode>`
4
+ // to route its stdin through the right one. No need to know the file layout.
5
+ //
6
+ // node det.js # print the catalog (task -> script -> modes)
7
+ // node det.js --json # same, machine-readable
8
+ // node det.js extract urls < f # route stdin through extract.js in urls mode
9
+ // node det.js json csv < arr.json # route stdin through json.js in csv mode
10
+ //
11
+ // The catalog is derived from the scripts themselves (their exported MODES /
12
+ // EXTRACTORS), so it can never drift from what actually runs.
13
+
14
+ 'use strict';
15
+
16
+ const extract = require('./extract');
17
+ const json = require('./json');
18
+ const text = require('./text');
19
+ const hash = require('./hash');
20
+ const date = require('./date');
21
+ const voice = require('./voice');
22
+
23
+ // git-facing scripts: they read the repo, not stdin, so they aren't routable
24
+ // through det.js. Listed here only so the front door surfaces all the tools —
25
+ // a cheap model running `det.js` sees these too and knows to run them directly.
26
+ const GIT_SCRIPTS = [
27
+ {
28
+ name: 'commit-msg',
29
+ ask: 'draft a Conventional-Commits message from the staged diff',
30
+ usage: 'git add -A && node scripts/det/commit-msg.js',
31
+ },
32
+ {
33
+ name: 'changelog',
34
+ ask: 'group commits since a ref/tag into a changelog',
35
+ usage: 'node scripts/det/changelog.js [ref]',
36
+ },
37
+ {
38
+ name: 'pr-description',
39
+ ask: 'draft a PR title + area summary + test-plan from the branch diff',
40
+ usage: 'node scripts/det/pr-description.js [base]',
41
+ },
42
+ ];
43
+
44
+ // script -> { ask, modes, run(mode, input) -> {text}|{error} }
45
+ const CATALOG = {
46
+ extract: {
47
+ ask: 'pull links / emails / code / numbers out of text',
48
+ modes: Object.keys(extract.EXTRACTORS),
49
+ run: (mode, input) => {
50
+ const items = extract.extract(mode, input);
51
+ return items === null ? { error: `unknown mode: ${mode}` } : { text: items.join('\n') };
52
+ },
53
+ },
54
+ json: {
55
+ ask: 'reformat / validate / flatten JSON (incl. JSON->CSV)',
56
+ modes: json.MODES,
57
+ run: json.run,
58
+ },
59
+ text: {
60
+ ask: 'dedupe / sort / count / slugify / trim lines',
61
+ modes: text.MODES,
62
+ run: text.run,
63
+ },
64
+ hash: {
65
+ ask: 'base64 / hex encode-decode, sha256 / sha1 / md5',
66
+ modes: hash.MODES,
67
+ run: hash.run,
68
+ },
69
+ date: {
70
+ ask: 'epoch <-> ISO, weekday (all UTC)',
71
+ modes: date.MODES,
72
+ run: date.run,
73
+ },
74
+ voice: {
75
+ ask: 'score a chat reply against the way of talking (PASS or findings)',
76
+ modes: voice.MODES,
77
+ run: voice.run,
78
+ },
79
+ };
80
+
81
+ function catalogText() {
82
+ const rows = Object.entries(CATALOG).map(
83
+ ([name, s]) => ` ${name.padEnd(9)} ${s.modes.join(' ')}\n ${s.ask}`
84
+ );
85
+ const gitRows = GIT_SCRIPTS.map((g) => ` ${g.name.padEnd(15)} ${g.usage}\n ${g.ask}`);
86
+ return (
87
+ 'deterministic task scripts — run: node det.js <script> <mode> < input\n\n' +
88
+ rows.join('\n\n') +
89
+ '\n\ngit-facing (read the repo, run the script directly; not via det.js):\n\n' +
90
+ gitRows.join('\n\n') +
91
+ '\n'
92
+ );
93
+ }
94
+
95
+ function catalogJson() {
96
+ const out = {};
97
+ for (const [name, s] of Object.entries(CATALOG)) out[name] = { ask: s.ask, modes: s.modes };
98
+ const git = {};
99
+ for (const g of GIT_SCRIPTS) git[g.name] = { ask: g.ask, usage: g.usage };
100
+ return JSON.stringify({ ...out, git }, null, 2);
101
+ }
102
+
103
+ function readStdin() {
104
+ return new Promise((resolve) => {
105
+ let data = '';
106
+ process.stdin.setEncoding('utf8');
107
+ process.stdin.on('data', (c) => (data += c));
108
+ process.stdin.on('end', () => resolve(data));
109
+ if (process.stdin.isTTY) resolve('');
110
+ });
111
+ }
112
+
113
+ async function main() {
114
+ const args = process.argv.slice(2).filter((a) => a !== '--json');
115
+ const wantJson = process.argv.includes('--json');
116
+ const [script, mode] = args;
117
+
118
+ if (!script) {
119
+ process.stdout.write((wantJson ? catalogJson() : catalogText()) + '\n');
120
+ return;
121
+ }
122
+ const git = GIT_SCRIPTS.find((g) => g.name === script);
123
+ if (git) {
124
+ process.stdout.write(`${git.name} reads the repo, not stdin — run it directly:\n ${git.usage}\n`);
125
+ return;
126
+ }
127
+ const entry = CATALOG[script];
128
+ if (!entry) {
129
+ process.stderr.write(`unknown script: ${script}\nscripts: ${Object.keys(CATALOG).join(' | ')}\n`);
130
+ process.exit(2);
131
+ }
132
+ if (!mode || !entry.modes.includes(mode)) {
133
+ process.stderr.write(`unknown mode: ${mode || '(none)'}\nmodes: ${entry.modes.join(' | ')}\n`);
134
+ process.exit(2);
135
+ }
136
+ const input = await readStdin();
137
+ const res = entry.run(mode, input);
138
+ if (res.error) {
139
+ process.stderr.write(res.error + '\n');
140
+ process.exit(2);
141
+ }
142
+ if (res.text.length) process.stdout.write(res.text + '\n');
143
+ }
144
+
145
+ if (require.main === module) {
146
+ main();
147
+ }
148
+
149
+ module.exports = { CATALOG, catalogJson, catalogText, GIT_SCRIPTS };
@@ -0,0 +1,93 @@
1
+ #!/usr/bin/env node
2
+ // det/extract.js — deterministically pull structured items out of text.
3
+ // Replaces the "extract all the X from this" ask that a cheap LLM does slowly
4
+ // and sometimes wrong. Reads stdin, writes one item per line to stdout.
5
+ //
6
+ // Usage:
7
+ // cat file.txt | node extract.js urls
8
+ // node extract.js emails < file.txt
9
+ // node extract.js code < README.md # fenced ```code blocks
10
+ // node extract.js numbers < report.txt
11
+ // node extract.js --json urls < file.txt # JSON array instead of lines
12
+ //
13
+ // Kinds: urls | emails | code | numbers | ipv4 | hashtags
14
+ // Exit 0 with output, exit 2 on bad/unknown kind. Duplicates removed, order preserved.
15
+
16
+ 'use strict';
17
+
18
+ const EXTRACTORS = {
19
+ urls: (t) => match(t, /\bhttps?:\/\/[^\s<>"')\]]+/g).map(stripTrailingPunct),
20
+ emails: (t) => match(t, /\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}\b/g),
21
+ ipv4: (t) => match(t, /\b(?:(?:25[0-5]|2[0-4]\d|1?\d?\d)\.){3}(?:25[0-5]|2[0-4]\d|1?\d?\d)\b/g),
22
+ hashtags: (t) => match(t, /(?:^|\s)(#[A-Za-z0-9_]+)/g).map((m) => m.trim()),
23
+ numbers: (t) => match(t, /-?\b\d[\d,]*(?:\.\d+)?\b/g),
24
+ code: (t) => {
25
+ // Fenced blocks: ```lang\n...\n``` — return block contents (not fences).
26
+ const blocks = [];
27
+ const re = /```[^\n]*\n([\s\S]*?)```/g;
28
+ let m;
29
+ while ((m = re.exec(t)) !== null) blocks.push(m[1].replace(/\n$/, ''));
30
+ return blocks;
31
+ },
32
+ };
33
+
34
+ function match(text, re) {
35
+ return text.match(re) || [];
36
+ }
37
+
38
+ function stripTrailingPunct(s) {
39
+ return s.replace(/[.,;:!?]+$/, '');
40
+ }
41
+
42
+ function dedupePreserveOrder(items) {
43
+ const seen = new Set();
44
+ const out = [];
45
+ for (const it of items) {
46
+ if (!seen.has(it)) {
47
+ seen.add(it);
48
+ out.push(it);
49
+ }
50
+ }
51
+ return out;
52
+ }
53
+
54
+ function extract(kind, text) {
55
+ const fn = EXTRACTORS[kind];
56
+ if (!fn) return null;
57
+ return dedupePreserveOrder(fn(text));
58
+ }
59
+
60
+ function readStdin() {
61
+ return new Promise((resolve) => {
62
+ let data = '';
63
+ process.stdin.setEncoding('utf8');
64
+ process.stdin.on('data', (c) => (data += c));
65
+ process.stdin.on('end', () => resolve(data));
66
+ if (process.stdin.isTTY) resolve('');
67
+ });
68
+ }
69
+
70
+ async function main() {
71
+ const args = process.argv.slice(2);
72
+ const json = args.includes('--json');
73
+ const kind = args.find((a) => !a.startsWith('-'));
74
+ if (!kind || !EXTRACTORS[kind]) {
75
+ process.stderr.write(
76
+ `unknown kind: ${kind || '(none)'}\nkinds: ${Object.keys(EXTRACTORS).join(' | ')}\n`
77
+ );
78
+ process.exit(2);
79
+ }
80
+ const text = await readStdin();
81
+ const items = extract(kind, text);
82
+ if (json) {
83
+ process.stdout.write(JSON.stringify(items) + '\n');
84
+ } else if (items.length) {
85
+ process.stdout.write(items.join('\n') + '\n');
86
+ }
87
+ }
88
+
89
+ if (require.main === module) {
90
+ main();
91
+ }
92
+
93
+ module.exports = { extract, EXTRACTORS };
@@ -0,0 +1,79 @@
1
+ #!/usr/bin/env node
2
+ // det/hash.js — deterministic encode / hash. The "base64 this" and "give me the
3
+ // sha256" asks an LLM fakes or does wrong. Reads stdin, writes stdout.
4
+ //
5
+ // Usage:
6
+ // printf 'hi' | node hash.js b64 # base64 encode
7
+ // node hash.js b64d < encoded.txt # base64 decode
8
+ // node hash.js sha256 < file.txt # hex sha256 of the bytes
9
+ // node hash.js sha1 < file.txt
10
+ // node hash.js md5 < file.txt
11
+ // node hash.js hexenc < file.txt # raw -> hex
12
+ // node hash.js hexdec < hex.txt # hex -> raw
13
+ //
14
+ // Modes: b64 | b64d | sha256 | sha1 | md5 | hexenc | hexdec
15
+ // Input's trailing newline is stripped before encoding/hashing so
16
+ // `printf 'hi'` and `echo hi` give the same result. Exit 2 on bad mode/input.
17
+
18
+ 'use strict';
19
+
20
+ const crypto = require('crypto');
21
+
22
+ const DIGESTS = { sha256: 'sha256', sha1: 'sha1', md5: 'md5' };
23
+
24
+ // Pure core: returns { text } or { error }. Unit-testable.
25
+ function run(mode, input) {
26
+ // Strip a single trailing newline so shell echo vs printf agree.
27
+ const raw = input.replace(/\n$/, '');
28
+ if (DIGESTS[mode]) {
29
+ return { text: crypto.createHash(DIGESTS[mode]).update(raw, 'utf8').digest('hex') };
30
+ }
31
+ switch (mode) {
32
+ case 'b64':
33
+ return { text: Buffer.from(raw, 'utf8').toString('base64') };
34
+ case 'b64d':
35
+ return { text: Buffer.from(raw, 'base64').toString('utf8') };
36
+ case 'hexenc':
37
+ return { text: Buffer.from(raw, 'utf8').toString('hex') };
38
+ case 'hexdec':
39
+ if (!/^[0-9a-fA-F]*$/.test(raw) || raw.length % 2 !== 0) {
40
+ return { error: 'hexdec needs an even-length hex string' };
41
+ }
42
+ return { text: Buffer.from(raw, 'hex').toString('utf8') };
43
+ default:
44
+ return { error: `unknown mode: ${mode}` };
45
+ }
46
+ }
47
+
48
+ function readStdin() {
49
+ return new Promise((resolve) => {
50
+ let data = '';
51
+ process.stdin.setEncoding('utf8');
52
+ process.stdin.on('data', (c) => (data += c));
53
+ process.stdin.on('end', () => resolve(data));
54
+ if (process.stdin.isTTY) resolve('');
55
+ });
56
+ }
57
+
58
+ const MODES = ['b64', 'b64d', 'sha256', 'sha1', 'md5', 'hexenc', 'hexdec'];
59
+
60
+ async function main() {
61
+ const mode = process.argv.slice(2).find((a) => !a.startsWith('-'));
62
+ if (!mode || !MODES.includes(mode)) {
63
+ process.stderr.write(`unknown mode: ${mode || '(none)'}\nmodes: ${MODES.join(' | ')}\n`);
64
+ process.exit(2);
65
+ }
66
+ const input = await readStdin();
67
+ const res = run(mode, input);
68
+ if (res.error) {
69
+ process.stderr.write(res.error + '\n');
70
+ process.exit(2);
71
+ }
72
+ if (res.text.length) process.stdout.write(res.text + '\n');
73
+ }
74
+
75
+ if (require.main === module) {
76
+ main();
77
+ }
78
+
79
+ module.exports = { run, MODES };
@@ -0,0 +1,73 @@
1
+ #!/usr/bin/env node
2
+ // det/hunk-filter.js - keep only unified-diff hunks whose lines match a regex.
3
+ // For concurrent-editor workspaces: stage YOUR hunk of a shared file without
4
+ // sweeping in other agents' churn (git apply --cached the filtered patch).
5
+ //
6
+ // Usage:
7
+ // git diff -U1 -- path/file | node scripts/det/hunk-filter.js "<regex>"
8
+ // git diff -U1 -- path/file | node scripts/det/hunk-filter.js "Horizon" | git apply --cached --unidiff-zero -
9
+ //
10
+ // A file section is printed only when at least one of its hunks matches.
11
+ // Exit 0 on success (even if nothing matched), 2 on missing pattern.
12
+ 'use strict';
13
+
14
+ function filterHunks(diffText, pattern) {
15
+ const re = new RegExp(pattern);
16
+ const lines = String(diffText).split('\n');
17
+ const out = [];
18
+ let header = [];
19
+ let hunk = null;
20
+ let fileHasMatch = false;
21
+ let fileHunks = [];
22
+
23
+ const flushFile = () => {
24
+ if (fileHasMatch && fileHunks.length) {
25
+ out.push(...header, ...fileHunks);
26
+ }
27
+ header = [];
28
+ fileHunks = [];
29
+ fileHasMatch = false;
30
+ };
31
+ const flushHunk = () => {
32
+ if (!hunk) return;
33
+ if (hunk.some((l) => re.test(l))) {
34
+ fileHunks.push(...hunk);
35
+ fileHasMatch = true;
36
+ }
37
+ hunk = null;
38
+ };
39
+
40
+ for (const line of lines) {
41
+ if (line.startsWith('diff --git ')) {
42
+ flushHunk();
43
+ flushFile();
44
+ header = [line];
45
+ } else if (line.startsWith('@@')) {
46
+ flushHunk();
47
+ hunk = [line];
48
+ } else if (hunk) {
49
+ hunk.push(line);
50
+ } else {
51
+ header.push(line);
52
+ }
53
+ }
54
+ flushHunk();
55
+ flushFile();
56
+ const text = out.join('\n');
57
+ return text && !text.endsWith('\n') ? `${text}\n` : text;
58
+ }
59
+
60
+ module.exports = { filterHunks };
61
+
62
+ if (require.main === module) {
63
+ const pattern = process.argv[2];
64
+ if (!pattern) {
65
+ console.error('usage: git diff -U1 -- file | hunk-filter.js "<regex>"');
66
+ process.exit(2);
67
+ }
68
+ let input = '';
69
+ process.stdin.on('data', (c) => { input += c; });
70
+ process.stdin.on('end', () => {
71
+ process.stdout.write(filterHunks(input, pattern));
72
+ });
73
+ }
@@ -0,0 +1,120 @@
1
+ #!/usr/bin/env node
2
+ // det/json.js — deterministic JSON reshaping. The reformat/validate/flatten asks
3
+ // an LLM does by hand (and mis-escapes). Reads JSON on stdin, writes stdout.
4
+ //
5
+ // Usage:
6
+ // cat data.json | node json.js pretty # 2-space indent
7
+ // node json.js min < data.json # minified, one line
8
+ // node json.js validate < data.json # prints "valid" or errors (exit 2)
9
+ // node json.js keys < data.json # top-level keys, one per line
10
+ // node json.js csv < array.json # array of objects -> RFC-4180 CSV
11
+ //
12
+ // Modes: pretty | min | validate | keys | csv
13
+ // Exit 0 on success, 2 on invalid JSON or bad mode/shape.
14
+
15
+ 'use strict';
16
+
17
+ function parse(text) {
18
+ try {
19
+ return { ok: true, value: JSON.parse(text) };
20
+ } catch (e) {
21
+ return { ok: false, error: e.message };
22
+ }
23
+ }
24
+
25
+ // RFC-4180: quote a field if it holds comma, quote, CR or LF; double inner quotes.
26
+ function csvField(v) {
27
+ let s;
28
+ if (v === null || v === undefined) s = '';
29
+ else if (typeof v === 'object') s = JSON.stringify(v);
30
+ else s = String(v);
31
+ if (/[",\r\n]/.test(s)) s = '"' + s.replace(/"/g, '""') + '"';
32
+ return s;
33
+ }
34
+
35
+ function toCsv(arr) {
36
+ if (!Array.isArray(arr)) throw new Error('csv mode needs a JSON array of objects');
37
+ if (arr.length === 0) return '';
38
+ // Column order = first-seen key order across all rows (stable, deterministic).
39
+ const cols = [];
40
+ const seen = new Set();
41
+ for (const row of arr) {
42
+ if (row === null || typeof row !== 'object' || Array.isArray(row)) {
43
+ throw new Error('csv mode needs each item to be an object');
44
+ }
45
+ for (const k of Object.keys(row)) {
46
+ if (!seen.has(k)) {
47
+ seen.add(k);
48
+ cols.push(k);
49
+ }
50
+ }
51
+ }
52
+ const lines = [cols.map(csvField).join(',')];
53
+ for (const row of arr) {
54
+ lines.push(cols.map((c) => csvField(row[c])).join(','));
55
+ }
56
+ return lines.join('\n');
57
+ }
58
+
59
+ // Returns { text } on success or { error } on failure. Pure — unit-testable.
60
+ function run(mode, input) {
61
+ if (mode === 'validate') {
62
+ const p = parse(input);
63
+ return p.ok ? { text: 'valid' } : { error: p.error };
64
+ }
65
+ const p = parse(input);
66
+ if (!p.ok) return { error: p.error };
67
+ const v = p.value;
68
+ switch (mode) {
69
+ case 'pretty':
70
+ return { text: JSON.stringify(v, null, 2) };
71
+ case 'min':
72
+ return { text: JSON.stringify(v) };
73
+ case 'keys':
74
+ if (v === null || typeof v !== 'object' || Array.isArray(v)) {
75
+ return { error: 'keys mode needs a JSON object' };
76
+ }
77
+ return { text: Object.keys(v).join('\n') };
78
+ case 'csv':
79
+ try {
80
+ return { text: toCsv(v) };
81
+ } catch (e) {
82
+ return { error: e.message };
83
+ }
84
+ default:
85
+ return { error: `unknown mode: ${mode}` };
86
+ }
87
+ }
88
+
89
+ function readStdin() {
90
+ return new Promise((resolve) => {
91
+ let data = '';
92
+ process.stdin.setEncoding('utf8');
93
+ process.stdin.on('data', (c) => (data += c));
94
+ process.stdin.on('end', () => resolve(data));
95
+ if (process.stdin.isTTY) resolve('');
96
+ });
97
+ }
98
+
99
+ const MODES = ['pretty', 'min', 'validate', 'keys', 'csv'];
100
+
101
+ async function main() {
102
+ const mode = process.argv.slice(2).find((a) => !a.startsWith('-'));
103
+ if (!mode || !MODES.includes(mode)) {
104
+ process.stderr.write(`unknown mode: ${mode || '(none)'}\nmodes: ${MODES.join(' | ')}\n`);
105
+ process.exit(2);
106
+ }
107
+ const input = await readStdin();
108
+ const res = run(mode, input);
109
+ if (res.error) {
110
+ process.stderr.write(res.error + '\n');
111
+ process.exit(2);
112
+ }
113
+ if (res.text.length) process.stdout.write(res.text + '\n');
114
+ }
115
+
116
+ if (require.main === module) {
117
+ main();
118
+ }
119
+
120
+ module.exports = { run, toCsv, MODES };