@wovin/tranz-cli 0.0.26

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,95 @@
1
+ import { Command, Flags } from '@oclif/core';
2
+ // import { Logger } from '@ztax/logger/src'
3
+ import { getName } from "../../utils/file-utils.js";
4
+ const splitOnSil = (silenceInfo, output, sourceFileName) => {
5
+ const splitSeeks = [];
6
+ let report = `
7
+ Exclusions and inclusions:
8
+ `;
9
+ let segmentDuration = 0;
10
+ for (const [inx, eachSil] of silenceInfo.entries()) {
11
+ const { stSec, endSec, duration } = eachSil;
12
+ segmentDuration = duration;
13
+ const nextSil = silenceInfo[inx + 1];
14
+ if (!nextSil)
15
+ break;
16
+ const seekExclude = (duration - (Split.DEFAULTS.SILBUF * 2)).toFixed(3);
17
+ const segmentSt = +(endSec - Split.DEFAULTS.SILBUF).toFixed(3);
18
+ const segmentDur = +((nextSil?.stSec ?? 0) + Split.DEFAULTS.SILBUF - segmentSt).toFixed(3);
19
+ report += ` ex:${seekExclude} - in:${segmentDur}`;
20
+ const ts = 'todo';
21
+ const outputPath = `${output}/${inx}-${ts}-from-${sourceFileName}.wav`;
22
+ splitSeeks.push([segmentSt, segmentDur, ts, outputPath]);
23
+ }
24
+ report += ` ex:${segmentDuration.toFixed(3)} `;
25
+ };
26
+ const doSplit = () => {
27
+ const { WARN, LOG, DEBUG, VERBOSE, ERROR } = { WARN: console.warn, LOG: console.log, DEBUG: console.log, VERBOSE: console.debug, ERROR: (...Any) => new Error(JSON.stringify(Any)) }; // Logger.setup(Logger.INFO); // eslint-disable-line no-unused-vars
28
+ LOG('//TODO');
29
+ // return new Promise<SplitResult>(async (resolveFx, rejectFx) => {
30
+ // const transResults = await getWhisperTranscript(phase1OutputPath, output)
31
+ // if (!transResults?.trans?.transcription) return rejectFx(ERROR('bum transcription', transResults))
32
+ // const { code, trace, trans } = transResults
33
+ // const transTrace = ['Transcription results: ', { code, trace, trans }]
34
+ // info.traces.push(transTrace); DEBUG(transTrace)
35
+ // const transcription = trans.transcription as TranscriptionArray
36
+ // const isCreateInterimArtifacts = true // TODO implement chaining if this is false and add param
37
+ // const splitSeeks = []
38
+ // for (const [inx, eachTrans] of transcription.entries()) {
39
+ // const { offsets: { from: segSt, to: segEnd },
40
+ // timestamps: { from: tsFrom, to: tsTo },
41
+ // text, speaker_turn_next: isNewSpeaker } = eachTrans
42
+ // const {
43
+ // offsets: { from: nxSegSt, to: nxSegEnd },
44
+ // // timestamps: { from: tsFromNx, to: tsToNx },
45
+ // text: textNx, speaker_turn_next: isNewSpeakerNx } = transcription[inx + 1] ?? { offsets: {} }
46
+ // const gap = (nxSegSt ?? segEnd) - segEnd
47
+ // const duration = (segEnd + gap * 0.5) - segSt
48
+ // const ts = tsFrom // TODO absolute uts ts if startTimestamp is given or
49
+ // const outputPath = `${output}/${sourceFileName}-${inx}-${ts}-${text.substring(0, 42)}.wav` as string
50
+ // splitSeeks.push([segSt, duration, ts, outputPath])
51
+ // }
52
+ // console.log('starting segment splitting' /*, { splitSeeks, leadingSil, leadingSilEnd, inputParams }*/);
53
+ // for (const [inx, [sSt, sDur, ts, outputPath]] of splitSeeks.entries()) {
54
+ // console.log({ inx, sSt, sDur, ts })
55
+ // ffmpeg(phase2OutputPath, { logger: console }) //.inputOptions(inputParams)
56
+ // .seekInput(sSt)
57
+ // .duration(sDur)
58
+ // .save(outputPath as string)
59
+ // .on('end', function (stdout, stderr) {
60
+ // })
61
+ // }
62
+ // console.log('wrote', splitSeeks.length, 'files to', output)
63
+ // })
64
+ };
65
+ class Split extends Command {
66
+ async run() {
67
+ const { args: { input }, flags, flags: { output, silthr, sildur } } = await this.parse(Split);
68
+ const sourceFileName = getName(input);
69
+ this.log(`\n Preparing ${flags.input} into ${output}, maybe \n`);
70
+ const report = await Split.split();
71
+ console.log(report);
72
+ }
73
+ }
74
+ Split.description = 'Split audio file - on silence, or split info from transcription';
75
+ Split.DEFAULTS = {
76
+ OUTDIR: './out',
77
+ SILDUR: '1.3',
78
+ SILBUF: 0.2,
79
+ SILTHR: '-35dB',
80
+ };
81
+ Split.flags = {
82
+ output: Flags.string({ char: 'o', description: 'output directory', required: false, default: Split.DEFAULTS.OUTDIR }),
83
+ silthr: Flags.string({ char: 's', description: 'silence threshold', required: false, default: Split.DEFAULTS.SILTHR }),
84
+ sildur: Flags.string({ char: 'd', description: 'silence duration', required: false, default: Split.DEFAULTS.SILDUR }),
85
+ };
86
+ Split.args = [
87
+ { name: 'input', description: 'input file', required: true },
88
+ ];
89
+ Split.split = doSplit;
90
+ Split.examples = [
91
+ `$ tranz prep split /pathTo/audio.wav
92
+ ...TODO
93
+ `,
94
+ ];
95
+ export default Split;
@@ -0,0 +1,2 @@
1
+ export { run } from '@oclif/core';
2
+ export * from '@wovin/tranz';
package/dist/index.js ADDED
@@ -0,0 +1,3 @@
1
+ export { run } from '@oclif/core';
2
+ // Re-export library for convenience
3
+ export * from '@wovin/tranz';
@@ -0,0 +1,5 @@
1
+ import path from 'node:path';
2
+ export declare const getExt: (filePath: string) => string;
3
+ export declare const getName: (filePath: string) => string;
4
+ export declare const getNameWithExt: (filePath: string) => string;
5
+ export declare const getFileInfo: (filePath: string) => path.ParsedPath;
@@ -0,0 +1,15 @@
1
+ import path from 'node:path';
2
+ export const getExt = (filePath) => {
3
+ return getFileInfo(filePath).ext;
4
+ };
5
+ export const getName = (filePath) => {
6
+ return getFileInfo(filePath).name;
7
+ };
8
+ export const getNameWithExt = (filePath) => {
9
+ const { name, ext } = getFileInfo(filePath);
10
+ return `${name}.${ext}`;
11
+ };
12
+ export const getFileInfo = (filePath) => {
13
+ const normed = path.normalize(filePath);
14
+ return path.parse(normed);
15
+ };
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Format bytes into human-readable size string
3
+ *
4
+ * @param bytes - Number of bytes
5
+ * @param decimals - Number of decimal places (default: 1)
6
+ * @param binary - Use binary (1024) vs decimal (1000) units (default: true)
7
+ * @returns Formatted string like "36.9 MiB" or "38.7 MB"
8
+ *
9
+ * @example
10
+ * formatBytes(38746650) // "36.9 MiB"
11
+ * formatBytes(38746650, 2) // "36.95 MiB"
12
+ * formatBytes(38746650, 1, false) // "38.7 MB"
13
+ */
14
+ export declare function formatBytes(bytes: number, decimals?: number, binary?: boolean): string;
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Format bytes into human-readable size string
3
+ *
4
+ * @param bytes - Number of bytes
5
+ * @param decimals - Number of decimal places (default: 1)
6
+ * @param binary - Use binary (1024) vs decimal (1000) units (default: true)
7
+ * @returns Formatted string like "36.9 MiB" or "38.7 MB"
8
+ *
9
+ * @example
10
+ * formatBytes(38746650) // "36.9 MiB"
11
+ * formatBytes(38746650, 2) // "36.95 MiB"
12
+ * formatBytes(38746650, 1, false) // "38.7 MB"
13
+ */
14
+ export function formatBytes(bytes, decimals = 1, binary = true) {
15
+ if (bytes === 0)
16
+ return '0 B';
17
+ const k = binary ? 1024 : 1000;
18
+ const sizes = binary ? ['B', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB'] : ['B', 'KB', 'MB', 'GB', 'TB', 'PB'];
19
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
20
+ const value = bytes / Math.pow(k, i);
21
+ return `${value.toFixed(decimals)} ${sizes[i]}`;
22
+ }
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Load an API key from environment variable or secret file
3
+ * @param envVar - Environment variable name (e.g., 'MISTRAL_API_KEY')
4
+ * @param secretFile - Path to secret file (e.g., 'secret/mistral')
5
+ * @returns The API key, or undefined if not found
6
+ */
7
+ export declare function loadSecret(envVar: string, secretFile: string): Promise<string | undefined>;
8
+ /**
9
+ * Load an API key from environment variable or secret file, with error handling
10
+ * @param envVar - Environment variable name
11
+ * @param secretFile - Path to secret file
12
+ * @param displayName - Human-readable name for error messages (e.g., 'Mistral API key')
13
+ * @returns The API key
14
+ * @throws If the key is not found
15
+ */
16
+ export declare function loadSecretOrThrow(envVar: string, secretFile: string, displayName: string): Promise<string>;
17
+ export declare const loadApiKey: typeof loadSecret;
18
+ export declare const loadApiKeyOrThrow: typeof loadSecretOrThrow;
@@ -0,0 +1,40 @@
1
+ import * as fs from 'node:fs';
2
+ /**
3
+ * Load an API key from environment variable or secret file
4
+ * @param envVar - Environment variable name (e.g., 'MISTRAL_API_KEY')
5
+ * @param secretFile - Path to secret file (e.g., 'secret/mistral')
6
+ * @returns The API key, or undefined if not found
7
+ */
8
+ export async function loadSecret(envVar, secretFile) {
9
+ // Try environment variable first
10
+ const envValue = process.env[envVar];
11
+ if (envValue)
12
+ return envValue;
13
+ // Fall back to secret file
14
+ try {
15
+ const fileContent = fs.readFileSync(secretFile, 'utf-8');
16
+ return fileContent.trim() || undefined;
17
+ }
18
+ catch {
19
+ // File doesn't exist or can't be read
20
+ return undefined;
21
+ }
22
+ }
23
+ /**
24
+ * Load an API key from environment variable or secret file, with error handling
25
+ * @param envVar - Environment variable name
26
+ * @param secretFile - Path to secret file
27
+ * @param displayName - Human-readable name for error messages (e.g., 'Mistral API key')
28
+ * @returns The API key
29
+ * @throws If the key is not found
30
+ */
31
+ export async function loadSecretOrThrow(envVar, secretFile, displayName) {
32
+ const secret = await loadSecret(envVar, secretFile);
33
+ if (!secret) {
34
+ throw new Error(`${displayName} not found. Set ${envVar} environment variable or create ${secretFile} file`);
35
+ }
36
+ return secret;
37
+ }
38
+ // Keep backwards compatibility aliases
39
+ export const loadApiKey = loadSecret;
40
+ export const loadApiKeyOrThrow = loadSecretOrThrow;
@@ -0,0 +1,284 @@
1
+ {
2
+ "version": "0.0.26",
3
+ "commands": {
4
+ "prep": {
5
+ "id": "prep",
6
+ "description": "Prepare audio file - normalize, noise reduce, split on silence",
7
+ "strict": true,
8
+ "pluginName": "@wovin/tranz-cli",
9
+ "pluginAlias": "@wovin/tranz-cli",
10
+ "pluginType": "core",
11
+ "aliases": [],
12
+ "hiddenAliases": [],
13
+ "examples": [
14
+ "$ tranz prep AI_Could_Be_The_End_Of_Democracy.m4a' -s -35dB -d 1.5\n--> sets silence threshold to -35dB and min silence duration to 1.5s\n--> runs normalization and noise removal\n"
15
+ ],
16
+ "flags": {
17
+ "norm": {
18
+ "name": "norm",
19
+ "type": "boolean",
20
+ "char": "n",
21
+ "description": "do normalization?",
22
+ "required": false,
23
+ "allowNo": false
24
+ },
25
+ "verbose": {
26
+ "name": "verbose",
27
+ "type": "boolean",
28
+ "char": "v",
29
+ "description": "trace more?",
30
+ "required": false,
31
+ "allowNo": false
32
+ },
33
+ "outdir": {
34
+ "name": "outdir",
35
+ "type": "option",
36
+ "char": "o",
37
+ "description": "output directory",
38
+ "required": false,
39
+ "multiple": false,
40
+ "default": "./out"
41
+ },
42
+ "silthr": {
43
+ "name": "silthr",
44
+ "type": "option",
45
+ "char": "s",
46
+ "description": "silence threshold",
47
+ "required": false,
48
+ "multiple": false,
49
+ "default": "-35dB"
50
+ },
51
+ "sildur": {
52
+ "name": "sildur",
53
+ "type": "option",
54
+ "char": "d",
55
+ "description": "silence duration",
56
+ "required": false,
57
+ "multiple": false,
58
+ "default": "1.3"
59
+ }
60
+ },
61
+ "args": {
62
+ "input": {
63
+ "name": "input",
64
+ "description": "input file",
65
+ "required": true
66
+ }
67
+ },
68
+ "DEFAULTS": {
69
+ "SILDUR": "1.3",
70
+ "SILBUF": 0.2,
71
+ "SILTHR": "-35dB",
72
+ "RNNN": "https://raw.githubusercontent.com/GregorR/rnnoise-models/master/somnolent-hogwash-2018-09-01/sh.rnnn"
73
+ }
74
+ },
75
+ "scribe": {
76
+ "id": "scribe",
77
+ "description": "Transcribe audio file - optionally prepare first",
78
+ "strict": true,
79
+ "pluginName": "@wovin/tranz-cli",
80
+ "pluginAlias": "@wovin/tranz-cli",
81
+ "pluginType": "core",
82
+ "aliases": [],
83
+ "hiddenAliases": [],
84
+ "examples": [
85
+ "$ tranz scribe 'path/to/16khz-audiofile.wav'\nruns whisper and outputs and saves a transcription json\n\n$ tranz scribe 'path/to/whatever-audiofile.mp3' -p\nfirst prepares and then runs whisper and outputs and saves a transcription json\n\n$ tranz scribe 'path/to/audiofile.mp3' -r mistral -m voxtral-mini-latest\ntranscribe using Mistral provider with specified model\n\n$ tranz scribe 'path/to/audiofile.mp3' -r mistral --timestamps segment\ntranscribe using Mistral with segment-level timestamps\n\n$ tranz scribe 'path/to/audiofile.mp3' -r mistral --diarization --timestamps word\ntranscribe using Mistral with speaker diarization and word-level timestamps\n\n$ tranz scribe 'path/to/audiofile.mp3' -r greenpt -l en\ntranscribe using GreenPT provider with language specification\n\n$ tranz scribe 'path/to/long-audio.mp3' --auto-split --max-segment 300\nautomatically split long audio at silence boundaries (max 5 min segments)\n"
86
+ ],
87
+ "flags": {
88
+ "prep": {
89
+ "name": "prep",
90
+ "type": "boolean",
91
+ "char": "p",
92
+ "description": "do prep?",
93
+ "required": false,
94
+ "allowNo": false
95
+ },
96
+ "withGPU": {
97
+ "name": "withGPU",
98
+ "type": "boolean",
99
+ "char": "g",
100
+ "description": "use gpu?",
101
+ "required": false,
102
+ "allowNo": false
103
+ },
104
+ "norm": {
105
+ "name": "norm",
106
+ "type": "boolean",
107
+ "char": "n",
108
+ "description": "do normalization?",
109
+ "required": false,
110
+ "allowNo": false
111
+ },
112
+ "output": {
113
+ "name": "output",
114
+ "type": "option",
115
+ "char": "o",
116
+ "description": "output directory",
117
+ "required": false,
118
+ "multiple": false,
119
+ "default": "./out"
120
+ },
121
+ "diarization": {
122
+ "name": "diarization",
123
+ "type": "boolean",
124
+ "char": "d",
125
+ "description": "enable speaker diarization",
126
+ "required": false,
127
+ "allowNo": false
128
+ },
129
+ "separate_speakers": {
130
+ "name": "separate_speakers",
131
+ "type": "boolean",
132
+ "description": "separate via diarization (deprecated: use --diarization)",
133
+ "required": false,
134
+ "allowNo": false,
135
+ "deprecated": {
136
+ "message": "use --diarization instead"
137
+ }
138
+ },
139
+ "provider": {
140
+ "name": "provider",
141
+ "type": "option",
142
+ "char": "r",
143
+ "description": "transcription provider (whisper, mistral, greenpt)",
144
+ "multiple": false,
145
+ "options": [
146
+ "whisper",
147
+ "mistral",
148
+ "voxtral",
149
+ "greenpt"
150
+ ],
151
+ "default": "whisper"
152
+ },
153
+ "model": {
154
+ "name": "model",
155
+ "type": "option",
156
+ "char": "m",
157
+ "description": "model name (provider-specific)",
158
+ "multiple": false
159
+ },
160
+ "language": {
161
+ "name": "language",
162
+ "type": "option",
163
+ "char": "l",
164
+ "description": "language code (e.g., en, de, fr)",
165
+ "multiple": false
166
+ },
167
+ "timestamps": {
168
+ "name": "timestamps",
169
+ "type": "option",
170
+ "description": "enable timestamp granularity (segment, word) - Mistral only",
171
+ "multiple": false,
172
+ "options": [
173
+ "segment",
174
+ "word"
175
+ ]
176
+ },
177
+ "no-pauses": {
178
+ "name": "no-pauses",
179
+ "type": "boolean",
180
+ "description": "disable pause-based line break formatting in transcript preview",
181
+ "allowNo": false
182
+ },
183
+ "auto-split": {
184
+ "name": "auto-split",
185
+ "type": "boolean",
186
+ "description": "automatically split long audio files at silence boundaries",
187
+ "allowNo": false
188
+ },
189
+ "max-segment": {
190
+ "name": "max-segment",
191
+ "type": "option",
192
+ "description": "maximum segment duration in seconds for auto-split (default: 600)",
193
+ "multiple": false,
194
+ "default": 600
195
+ },
196
+ "split-silence-dur": {
197
+ "name": "split-silence-dur",
198
+ "type": "option",
199
+ "description": "minimum silence duration for split points (default: 1.0)",
200
+ "multiple": false,
201
+ "default": "1.0"
202
+ }
203
+ },
204
+ "args": {
205
+ "input": {
206
+ "name": "input",
207
+ "description": "input file",
208
+ "required": true
209
+ }
210
+ },
211
+ "DEFAULTS": {
212
+ "DIARIZE": false,
213
+ "SILDUR": "1.3",
214
+ "SILBUF": 0.2,
215
+ "SILTHR": "-35dB",
216
+ "MODEL_KEYS": {
217
+ "tinyd": "ggml-small.en-tdrz.bin",
218
+ "small": "ggml-small.bin",
219
+ "medium": "ggml-medium.bin"
220
+ },
221
+ "MODELS": {
222
+ "tinyd": "https://huggingface.co/akashmjn/tinydiarize-whisper.cpp/resolve/main/ggml-small.en-tdrz.bin",
223
+ "small": "https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-small.bin",
224
+ "medium": "https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-medium.bin"
225
+ }
226
+ }
227
+ },
228
+ "split": {
229
+ "id": "split",
230
+ "description": "Split audio file - on silence, or split info from transcription",
231
+ "strict": true,
232
+ "pluginName": "@wovin/tranz-cli",
233
+ "pluginAlias": "@wovin/tranz-cli",
234
+ "pluginType": "core",
235
+ "aliases": [],
236
+ "hiddenAliases": [],
237
+ "examples": [
238
+ "$ tranz prep split /pathTo/audio.wav\n...TODO\n"
239
+ ],
240
+ "flags": {
241
+ "output": {
242
+ "name": "output",
243
+ "type": "option",
244
+ "char": "o",
245
+ "description": "output directory",
246
+ "required": false,
247
+ "multiple": false,
248
+ "default": "./out"
249
+ },
250
+ "silthr": {
251
+ "name": "silthr",
252
+ "type": "option",
253
+ "char": "s",
254
+ "description": "silence threshold",
255
+ "required": false,
256
+ "multiple": false,
257
+ "default": "-35dB"
258
+ },
259
+ "sildur": {
260
+ "name": "sildur",
261
+ "type": "option",
262
+ "char": "d",
263
+ "description": "silence duration",
264
+ "required": false,
265
+ "multiple": false,
266
+ "default": "1.3"
267
+ }
268
+ },
269
+ "args": {
270
+ "input": {
271
+ "name": "input",
272
+ "description": "input file",
273
+ "required": true
274
+ }
275
+ },
276
+ "DEFAULTS": {
277
+ "OUTDIR": "./out",
278
+ "SILDUR": "1.3",
279
+ "SILBUF": 0.2,
280
+ "SILTHR": "-35dB"
281
+ }
282
+ }
283
+ }
284
+ }
package/package.json ADDED
@@ -0,0 +1,90 @@
1
+ {
2
+ "type": "module",
3
+ "name": "@wovin/tranz-cli",
4
+ "version": "0.0.26",
5
+ "description": "CLI for audio and video transcription",
6
+ "author": "gotjoshua @gotjoshua",
7
+ "bin": {
8
+ "tranz": "./bin/run.js",
9
+ "tranzz": "./bin/dev.js"
10
+ },
11
+ "homepage": "https://gitlab.com/onezoomin/ztax/tranz",
12
+ "license": "ISC",
13
+ "main": "",
14
+ "repository": {
15
+ "type": "git",
16
+ "url": "git+https://gitlab.com/wovin/wovin.git",
17
+ "directory": "packages/@wovin/tranz-cli"
18
+ },
19
+ "files": [
20
+ "/bin",
21
+ "/dist",
22
+ "/npm-shrinkwrap.json",
23
+ "/oclif.manifest.json"
24
+ ],
25
+ "publishConfig": {
26
+ "access": "public"
27
+ },
28
+ "dependencies": {
29
+ "@dharmendrasha/ffmpeg-normalize": "^1.8.2",
30
+ "@oclif/core": "^2",
31
+ "@oclif/plugin-help": "^5",
32
+ "@oclif/plugin-plugins": "^3.1.8",
33
+ "@ztax/logger": "^0.2.1",
34
+ "fluent-ffmpeg": "^2.1.2",
35
+ "@wovin/tranz": "^0.0.26"
36
+ },
37
+ "devDependencies": {
38
+ "@oclif/test": "^2.4.2",
39
+ "@types/chai": "^5.2.3",
40
+ "@types/fluent-ffmpeg": "^2.1.21",
41
+ "@types/mocha": "^10.0.10",
42
+ "@types/node": "^24.10.1",
43
+ "chai": "^4",
44
+ "eslint": "^7.32.0",
45
+ "eslint-config-oclif": "^4",
46
+ "eslint-config-oclif-typescript": "^1.0.3",
47
+ "mocha": "^9",
48
+ "oclif": "^3.11.3",
49
+ "shx": "^0.4.0",
50
+ "ts-node": "^10.9.1",
51
+ "tslib": "^2.6.1",
52
+ "typescript": "^5.9.3",
53
+ "whisper-node": "^1.1.1"
54
+ },
55
+ "oclif": {
56
+ "bin": "tranz",
57
+ "dirname": "tranz",
58
+ "commands": "./dist/commands",
59
+ "plugins": [
60
+ "@oclif/plugin-help",
61
+ "@oclif/plugin-plugins"
62
+ ],
63
+ "topicSeparator": " ",
64
+ "topics": {
65
+ "prep": {
66
+ "description": "Prepare audio for transcription"
67
+ }
68
+ }
69
+ },
70
+ "engines": {
71
+ "node": ">=12.0.0"
72
+ },
73
+ "bugs": "https://gitlab.com/onezoomin/ztax/tranz/-/issues",
74
+ "keywords": [
75
+ "oclif",
76
+ "cli",
77
+ "transcription"
78
+ ],
79
+ "types": "dist/index.d.ts",
80
+ "scripts": {
81
+ "tranz": "./bin/dev.js",
82
+ "oclif": "oclif",
83
+ "manifest": "ts-node oclif manifest",
84
+ "build": "shx rm -rf dist && tsc -b",
85
+ "lint": "eslint . --ext .ts --config .eslintrc",
86
+ "posttest": "npm run lint",
87
+ "test": "mocha --forbid-only \"test/**/*.test.ts\"",
88
+ "version": "oclif readme && git add README.md"
89
+ }
90
+ }