@craftpipe/contextpack 1.0.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.
- package/.contextpackrc.example.json +167 -0
- package/.env.example +5 -0
- package/.github/ISSUE_TEMPLATE/bug_report.md +26 -0
- package/.github/ISSUE_TEMPLATE/feature_request.md +15 -0
- package/.github/pull_request_template.md +9 -0
- package/CODE_OF_CONDUCT.md +40 -0
- package/CONTRIBUTING.md +59 -0
- package/LICENSE +21 -0
- package/README.md +100 -0
- package/SECURITY.md +21 -0
- package/index.js +428 -0
- package/lib/analyzer.js +547 -0
- package/lib/bundler.js +477 -0
- package/lib/config.js +269 -0
- package/lib/license.js +180 -0
- package/lib/premium/config-file.js +917 -0
- package/lib/premium/gate.js +13 -0
- package/lib/premium/html-report.js +1094 -0
- package/lib/premium/index.js +57 -0
- package/lib/premium/watch-mode.js +627 -0
- package/lib/scanner.js +480 -0
- package/lib/tokenizer.js +291 -0
- package/lib/validator.js +561 -0
- package/package.json +12 -0
- package/tests/analyzer.test.mjs +128 -0
- package/tests/bundler.test.mjs +126 -0
- package/tests/config.test.mjs +103 -0
- package/tests/gate.test.mjs +118 -0
- package/tests/index.test.mjs +103 -0
- package/tests/license.test.mjs +97 -0
- package/tests/scanner.test.mjs +110 -0
- package/tests/tokenizer.test.mjs +103 -0
- package/tests/validator.test.mjs +111 -0
- package/vitest.config.mjs +13 -0
package/index.js
ADDED
|
@@ -0,0 +1,428 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
'use strict';
|
|
4
|
+
|
|
5
|
+
const path = require('path');
|
|
6
|
+
const fs = require('fs');
|
|
7
|
+
|
|
8
|
+
const { loadConfig, mergeWithFlags } = require('./lib/config.js');
|
|
9
|
+
const { scanProject, buildFileTree, filterFiles } = require('./lib/scanner.js');
|
|
10
|
+
const { analyzeFile, analyzeProject, extractSymbols, buildDependencyGraph } = require('./lib/analyzer.js');
|
|
11
|
+
const { createBundle, formatAsJSON, formatAsMarkdown } = require('./lib/bundler.js');
|
|
12
|
+
const { validateBundle, checkCircularDependencies, checkSymbolConflicts } = require('./lib/validator.js');
|
|
13
|
+
const { estimateTokens, calculateBundleSize, estimateSavings } = require('./lib/tokenizer.js');
|
|
14
|
+
const { getLicenseStatus, assertProLicense, showUpgradePrompt } = require('./lib/license.js');
|
|
15
|
+
|
|
16
|
+
const TOOL_NAME = 'contextpack';
|
|
17
|
+
const VERSION = (() => {
|
|
18
|
+
try {
|
|
19
|
+
return require('./package.json').version;
|
|
20
|
+
} catch {
|
|
21
|
+
return '0.0.0';
|
|
22
|
+
}
|
|
23
|
+
})();
|
|
24
|
+
|
|
25
|
+
function printHelp() {
|
|
26
|
+
console.log(`
|
|
27
|
+
${TOOL_NAME} v${VERSION}
|
|
28
|
+
Analyze your codebase and generate compact, token-optimized context bundles for AI coding sessions.
|
|
29
|
+
|
|
30
|
+
USAGE:
|
|
31
|
+
${TOOL_NAME} [command] [options]
|
|
32
|
+
|
|
33
|
+
COMMANDS:
|
|
34
|
+
scan Scan project structure and build file tree
|
|
35
|
+
analyze Analyze files, extract symbols and dependencies
|
|
36
|
+
bundle Generate a context bundle from analysis
|
|
37
|
+
validate Validate an existing context bundle
|
|
38
|
+
run Run the full pipeline: scan → summarize → bundle → validate
|
|
39
|
+
info Show token estimates and bundle size info for a bundle file
|
|
40
|
+
|
|
41
|
+
OPTIONS:
|
|
42
|
+
-d, --dir <path> Root directory to scan (default: current working directory)
|
|
43
|
+
-o, --output <file> Output file path (default: contextpack.json)
|
|
44
|
+
-f, --format <fmt> Output format: json | markdown (default: json)
|
|
45
|
+
-i, --include <globs> Comma-separated glob patterns to include
|
|
46
|
+
-e, --exclude <globs> Comma-separated glob patterns to exclude
|
|
47
|
+
-t, --types <exts> Comma-separated file extensions to include (e.g. js,ts,py)
|
|
48
|
+
--config <file> Path to config file (default: .contextpackrc or contextpack.config.js)
|
|
49
|
+
--no-validate Skip validation step in full pipeline
|
|
50
|
+
--no-symbols Skip symbol extraction
|
|
51
|
+
--no-deps Skip dependency graph generation
|
|
52
|
+
--pro Assert Pro license features
|
|
53
|
+
-v, --version Print version number
|
|
54
|
+
-h, --help Show this help message
|
|
55
|
+
|
|
56
|
+
EXAMPLES:
|
|
57
|
+
# Run full pipeline on current directory
|
|
58
|
+
${TOOL_NAME} run
|
|
59
|
+
|
|
60
|
+
# Scan a specific directory and output as markdown
|
|
61
|
+
${TOOL_NAME} run --dir ./src --format markdown --output context.md
|
|
62
|
+
|
|
63
|
+
# Only scan and analyze, skip bundling
|
|
64
|
+
${TOOL_NAME} analyze --dir ./src --output analysis.json
|
|
65
|
+
|
|
66
|
+
# Validate an existing bundle
|
|
67
|
+
${TOOL_NAME} validate --dir . contextpack.json
|
|
68
|
+
|
|
69
|
+
# Get token estimates for a bundle
|
|
70
|
+
${TOOL_NAME} info contextpack.json
|
|
71
|
+
|
|
72
|
+
# Include only JS and TS files, exclude tests
|
|
73
|
+
${TOOL_NAME} run --types js,ts --exclude "**/*.test.*,**/__tests__/**"
|
|
74
|
+
|
|
75
|
+
# Use a custom config file
|
|
76
|
+
${TOOL_NAME} run --config ./my-contextpack.config.js
|
|
77
|
+
`);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function printVersion() {
|
|
81
|
+
console.log(`${TOOL_NAME} v${VERSION}`);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function parseArgs(argv) {
|
|
85
|
+
const args = argv.slice(2);
|
|
86
|
+
const flags = {
|
|
87
|
+
command: null,
|
|
88
|
+
dir: process.cwd(),
|
|
89
|
+
output: 'contextpack.json',
|
|
90
|
+
format: 'json',
|
|
91
|
+
include: null,
|
|
92
|
+
exclude: null,
|
|
93
|
+
types: null,
|
|
94
|
+
config: null,
|
|
95
|
+
validate: true,
|
|
96
|
+
symbols: true,
|
|
97
|
+
deps: true,
|
|
98
|
+
pro: false,
|
|
99
|
+
help: false,
|
|
100
|
+
version: false,
|
|
101
|
+
positional: [],
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
const commands = new Set(['scan', 'analyze', 'bundle', 'validate', 'run', 'info']);
|
|
105
|
+
|
|
106
|
+
let i = 0;
|
|
107
|
+
while (i < args.length) {
|
|
108
|
+
const arg = args[i];
|
|
109
|
+
|
|
110
|
+
if (arg === '-h' || arg === '--help') {
|
|
111
|
+
flags.help = true;
|
|
112
|
+
} else if (arg === '-v' || arg === '--version') {
|
|
113
|
+
flags.version = true;
|
|
114
|
+
} else if (arg === '--no-validate') {
|
|
115
|
+
flags.validate = false;
|
|
116
|
+
} else if (arg === '--no-symbols') {
|
|
117
|
+
flags.symbols = false;
|
|
118
|
+
} else if (arg === '--no-deps') {
|
|
119
|
+
flags.deps = false;
|
|
120
|
+
} else if (arg === '--pro') {
|
|
121
|
+
flags.pro = true;
|
|
122
|
+
} else if ((arg === '-d' || arg === '--dir') && args[i + 1]) {
|
|
123
|
+
flags.dir = path.resolve(args[++i]);
|
|
124
|
+
} else if ((arg === '-o' || arg === '--output') && args[i + 1]) {
|
|
125
|
+
flags.output = args[++i];
|
|
126
|
+
} else if ((arg === '-f' || arg === '--format') && args[i + 1]) {
|
|
127
|
+
flags.format = args[++i];
|
|
128
|
+
} else if ((arg === '-i' || arg === '--include') && args[i + 1]) {
|
|
129
|
+
flags.include = args[++i].split(',').map(s => s.trim());
|
|
130
|
+
} else if ((arg === '-e' || arg === '--exclude') && args[i + 1]) {
|
|
131
|
+
flags.exclude = args[++i].split(',').map(s => s.trim());
|
|
132
|
+
} else if ((arg === '-t' || arg === '--types') && args[i + 1]) {
|
|
133
|
+
flags.types = args[++i].split(',').map(s => s.trim().replace(/^\./, ''));
|
|
134
|
+
} else if (arg === '--config' && args[i + 1]) {
|
|
135
|
+
flags.config = path.resolve(args[++i]);
|
|
136
|
+
} else if (!arg.startsWith('-')) {
|
|
137
|
+
if (commands.has(arg) && flags.command === null) {
|
|
138
|
+
flags.command = arg;
|
|
139
|
+
} else {
|
|
140
|
+
flags.positional.push(arg);
|
|
141
|
+
}
|
|
142
|
+
} else {
|
|
143
|
+
console.warn(`Warning: Unknown flag "${arg}" — ignoring.`);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
i++;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
return flags;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function resolveOutputPath(flags) {
|
|
153
|
+
return path.resolve(flags.dir, flags.output);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function writeOutput(content, outputPath) {
|
|
157
|
+
const dir = path.dirname(outputPath);
|
|
158
|
+
if (!fs.existsSync(dir)) {
|
|
159
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
160
|
+
}
|
|
161
|
+
fs.writeFileSync(outputPath, content, 'utf8');
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
async function cmdScan(flags, config) {
|
|
165
|
+
console.log(`[scan] Scanning project at: ${flags.dir}`);
|
|
166
|
+
|
|
167
|
+
const rawFiles = await scanProject(flags.dir, config);
|
|
168
|
+
const fileTree = buildFileTree(rawFiles, flags.dir);
|
|
169
|
+
|
|
170
|
+
const filterOptions = {
|
|
171
|
+
include: flags.include || config.include,
|
|
172
|
+
exclude: flags.exclude || config.exclude,
|
|
173
|
+
types: flags.types || config.types,
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
const filteredFiles = filterFiles(rawFiles, filterOptions);
|
|
177
|
+
|
|
178
|
+
console.log(`[scan] Found ${rawFiles.length} files, ${filteredFiles.length} after filtering.`);
|
|
179
|
+
|
|
180
|
+
return { rawFiles, fileTree, filteredFiles };
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
async function cmdAnalyze(flags, config, filteredFiles) {
|
|
184
|
+
console.log(`[analyze] Analyzing ${filteredFiles.length} files...`);
|
|
185
|
+
|
|
186
|
+
const projectAnalysis = await analyzeProject(filteredFiles, config);
|
|
187
|
+
|
|
188
|
+
let symbols = null;
|
|
189
|
+
if (flags.symbols) {
|
|
190
|
+
console.log('[analyze] Extracting symbols...');
|
|
191
|
+
symbols = await extractSymbols(filteredFiles, config);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
let depGraph = null;
|
|
195
|
+
if (flags.deps) {
|
|
196
|
+
console.log('[analyze] Building dependency graph...');
|
|
197
|
+
depGraph = await buildDependencyGraph(filteredFiles, config);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
return { projectAnalysis, symbols, depGraph };
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
async function cmdBundle(flags, config, scanResult, analyzeResult) {
|
|
204
|
+
console.log(`[bundle] Creating bundle (format: ${flags.format})...`);
|
|
205
|
+
|
|
206
|
+
const bundleInput = {
|
|
207
|
+
fileTree: scanResult.fileTree,
|
|
208
|
+
filteredFiles: scanResult.filteredFiles,
|
|
209
|
+
projectAnalysis: analyzeResult.projectAnalysis,
|
|
210
|
+
symbols: analyzeResult.symbols,
|
|
211
|
+
depGraph: analyzeResult.depGraph,
|
|
212
|
+
meta: {
|
|
213
|
+
generatedAt: new Date().toISOString(),
|
|
214
|
+
rootDir: flags.dir,
|
|
215
|
+
version: VERSION,
|
|
216
|
+
format: flags.format,
|
|
217
|
+
},
|
|
218
|
+
};
|
|
219
|
+
|
|
220
|
+
const bundle = await createBundle(bundleInput, config);
|
|
221
|
+
|
|
222
|
+
let formatted;
|
|
223
|
+
if (flags.format === 'markdown') {
|
|
224
|
+
formatted = formatAsMarkdown(bundle, config);
|
|
225
|
+
} else {
|
|
226
|
+
formatted = formatAsJSON(bundle, config);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
const outputPath = resolveOutputPath(flags);
|
|
230
|
+
writeOutput(formatted, outputPath);
|
|
231
|
+
console.log(`[bundle] Bundle written to: ${outputPath}`);
|
|
232
|
+
|
|
233
|
+
return { bundle, formatted, outputPath };
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
async function cmdValidate(flags, config, bundle) {
|
|
237
|
+
console.log('[validate] Validating bundle...');
|
|
238
|
+
|
|
239
|
+
const validationResult = await validateBundle(bundle, config);
|
|
240
|
+
|
|
241
|
+
let circularResult = null;
|
|
242
|
+
if (bundle.depGraph) {
|
|
243
|
+
circularResult = await checkCircularDependencies(bundle.depGraph, config);
|
|
244
|
+
if (circularResult.hasCircular) {
|
|
245
|
+
console.warn(`[validate] Warning: ${circularResult.cycles.length} circular dependency cycle(s) detected.`);
|
|
246
|
+
} else {
|
|
247
|
+
console.log('[validate] No circular dependencies found.');
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
let conflictResult = null;
|
|
252
|
+
if (bundle.symbols) {
|
|
253
|
+
conflictResult = await checkSymbolConflicts(bundle.symbols, config);
|
|
254
|
+
if (conflictResult.hasConflicts) {
|
|
255
|
+
console.warn(`[validate] Warning: ${conflictResult.conflicts.length} symbol conflict(s) detected.`);
|
|
256
|
+
} else {
|
|
257
|
+
console.log('[validate] No symbol conflicts found.');
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
if (validationResult.valid) {
|
|
262
|
+
console.log('[validate] Bundle is valid.');
|
|
263
|
+
} else {
|
|
264
|
+
console.warn(`[validate] Bundle validation issues: ${validationResult.errors.length} error(s).`);
|
|
265
|
+
validationResult.errors.forEach(err => console.warn(` - ${err}`));
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
return { validationResult, circularResult, conflictResult };
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
async function cmdInfo(flags, config) {
|
|
272
|
+
const targetFile = flags.positional[0] || resolveOutputPath(flags);
|
|
273
|
+
const resolvedTarget = path.resolve(targetFile);
|
|
274
|
+
|
|
275
|
+
if (!fs.existsSync(resolvedTarget)) {
|
|
276
|
+
throw new Error(`Bundle file not found: ${resolvedTarget}`);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
console.log(`[info] Reading bundle: ${resolvedTarget}`);
|
|
280
|
+
const content = fs.readFileSync(resolvedTarget, 'utf8');
|
|
281
|
+
|
|
282
|
+
const tokenCount = estimateTokens(content);
|
|
283
|
+
const bundleSize = calculateBundleSize(content);
|
|
284
|
+
const savings = estimateSavings(content, config);
|
|
285
|
+
|
|
286
|
+
console.log(`\nBundle Info:`);
|
|
287
|
+
console.log(` File: ${resolvedTarget}`);
|
|
288
|
+
console.log(` Size: ${(bundleSize / 1024).toFixed(2)} KB`);
|
|
289
|
+
console.log(` Estimated tokens: ${tokenCount.toLocaleString()}`);
|
|
290
|
+
console.log(` Estimated savings: ${savings.percentage}% vs raw source (${savings.rawTokens.toLocaleString()} raw tokens)`);
|
|
291
|
+
console.log('');
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
async function cmdRun(flags, config) {
|
|
295
|
+
console.log(`[run] Starting full pipeline for: ${flags.dir}`);
|
|
296
|
+
|
|
297
|
+
const scanResult = await cmdScan(flags, config);
|
|
298
|
+
const analyzeResult = await cmdAnalyze(flags, config, scanResult.filteredFiles);
|
|
299
|
+
const bundleResult = await cmdBundle(flags, config, scanResult, analyzeResult);
|
|
300
|
+
|
|
301
|
+
if (flags.validate) {
|
|
302
|
+
await cmdValidate(flags, config, bundleResult.bundle);
|
|
303
|
+
} else {
|
|
304
|
+
console.log('[run] Skipping validation (--no-validate).');
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
const tokenCount = estimateTokens(bundleResult.formatted);
|
|
308
|
+
const savings = estimateSavings(bundleResult.formatted, config);
|
|
309
|
+
|
|
310
|
+
console.log(`\n✓ ContextPack complete!`);
|
|
311
|
+
console.log(` Output: ${bundleResult.outputPath}`);
|
|
312
|
+
console.log(` Files bundled: ${scanResult.filteredFiles.length}`);
|
|
313
|
+
console.log(` Estimated tokens: ${tokenCount.toLocaleString()}`);
|
|
314
|
+
console.log(` Token savings: ~${savings.percentage}% vs raw source`);
|
|
315
|
+
console.log('');
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
async function main() {
|
|
319
|
+
const flags = parseArgs(process.argv);
|
|
320
|
+
|
|
321
|
+
if (flags.help || (!flags.command && flags.positional.length === 0)) {
|
|
322
|
+
printHelp();
|
|
323
|
+
process.exit(0);
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
if (flags.version) {
|
|
327
|
+
printVersion();
|
|
328
|
+
process.exit(0);
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
let config;
|
|
332
|
+
try {
|
|
333
|
+
const rawConfig = loadConfig(flags.config || flags.dir);
|
|
334
|
+
config = mergeWithFlags(rawConfig, flags);
|
|
335
|
+
} catch (err) {
|
|
336
|
+
console.warn(`[config] Could not load config file: ${err.message}. Using defaults.`);
|
|
337
|
+
config = mergeWithFlags({}, flags);
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
if (flags.pro) {
|
|
341
|
+
try {
|
|
342
|
+
assertProLicense(config);
|
|
343
|
+
} catch (err) {
|
|
344
|
+
console.error(`[license] Pro feature required: ${err.message}`);
|
|
345
|
+
showUpgradePrompt();
|
|
346
|
+
process.exit(1);
|
|
347
|
+
}
|
|
348
|
+
} else {
|
|
349
|
+
const licenseStatus = getLicenseStatus(config);
|
|
350
|
+
if (licenseStatus && licenseStatus.type === 'pro') {
|
|
351
|
+
console.log(`[license] Pro license active.`);
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
const command = flags.command || 'run';
|
|
356
|
+
|
|
357
|
+
try {
|
|
358
|
+
switch (command) {
|
|
359
|
+
case 'scan': {
|
|
360
|
+
const result = await cmdScan(flags, config);
|
|
361
|
+
const outputPath = resolveOutputPath(flags);
|
|
362
|
+
const out = JSON.stringify({ fileTree: result.fileTree, filteredFiles: result.filteredFiles }, null, 2);
|
|
363
|
+
writeOutput(out, outputPath);
|
|
364
|
+
console.log(`[scan] Scan result written to: ${outputPath}`);
|
|
365
|
+
break;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
case 'analyze': {
|
|
369
|
+
const scanResult = await cmdScan(flags, config);
|
|
370
|
+
const analyzeResult = await cmdAnalyze(flags, config, scanResult.filteredFiles);
|
|
371
|
+
const outputPath = resolveOutputPath(flags);
|
|
372
|
+
const out = JSON.stringify(analyzeResult, null, 2);
|
|
373
|
+
writeOutput(out, outputPath);
|
|
374
|
+
console.log(`[analyze] Analysis written to: ${outputPath}`);
|
|
375
|
+
break;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
case 'bundle': {
|
|
379
|
+
const scanResult = await cmdScan(flags, config);
|
|
380
|
+
const analyzeResult = await cmdAnalyze(flags, config, scanResult.filteredFiles);
|
|
381
|
+
await cmdBundle(flags, config, scanResult, analyzeResult);
|
|
382
|
+
break;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
case 'validate': {
|
|
386
|
+
const targetFile = flags.positional[0] || resolveOutputPath(flags);
|
|
387
|
+
const resolvedTarget = path.resolve(targetFile);
|
|
388
|
+
|
|
389
|
+
if (!fs.existsSync(resolvedTarget)) {
|
|
390
|
+
throw new Error(`Bundle file not found: ${resolvedTarget}`);
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
const content = fs.readFileSync(resolvedTarget, 'utf8');
|
|
394
|
+
let bundle;
|
|
395
|
+
try {
|
|
396
|
+
bundle = JSON.parse(content);
|
|
397
|
+
} catch {
|
|
398
|
+
throw new Error(`Could not parse bundle as JSON: ${resolvedTarget}`);
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
await cmdValidate(flags, config, bundle);
|
|
402
|
+
break;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
case 'info': {
|
|
406
|
+
await cmdInfo(flags, config);
|
|
407
|
+
break;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
case 'run': {
|
|
411
|
+
await cmdRun(flags, config);
|
|
412
|
+
break;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
default:
|
|
416
|
+
console.error(`Unknown command: "${command}". Run "${TOOL_NAME} --help" for usage.`);
|
|
417
|
+
process.exit(1);
|
|
418
|
+
}
|
|
419
|
+
} catch (err) {
|
|
420
|
+
console.error(`\n[error] ${err.message}`);
|
|
421
|
+
if (process.env.DEBUG) {
|
|
422
|
+
console.error(err.stack);
|
|
423
|
+
}
|
|
424
|
+
process.exit(1);
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
main();
|