@bobschlowinskii/clicraft 0.4.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/CHANGELOG.md +191 -0
- package/CONTRIBUTING.md +261 -0
- package/LICENSE.md +7 -0
- package/README.md +55 -0
- package/commands/auth.js +427 -0
- package/commands/config.js +356 -0
- package/commands/create.js +534 -0
- package/commands/info.js +113 -0
- package/commands/install.js +130 -0
- package/commands/launch.js +296 -0
- package/commands/search.js +50 -0
- package/commands/uninstall.js +94 -0
- package/commands/upgrade.js +343 -0
- package/commands/version.js +21 -0
- package/docs/README.md +119 -0
- package/docs/_config.yml +63 -0
- package/docs/commands/auth.md +376 -0
- package/docs/commands/config.md +294 -0
- package/docs/commands/create.md +276 -0
- package/docs/commands/info.md +460 -0
- package/docs/commands/install.md +320 -0
- package/docs/commands/launch.md +427 -0
- package/docs/commands/search.md +276 -0
- package/docs/commands/uninstall.md +128 -0
- package/docs/commands/upgrade.md +515 -0
- package/docs/commands.md +266 -0
- package/docs/configuration.md +410 -0
- package/docs/contributing.md +114 -0
- package/docs/index.md +82 -0
- package/docs/installation.md +162 -0
- package/helpers/auth/index.js +20 -0
- package/helpers/auth/microsoft.js +329 -0
- package/helpers/auth/storage.js +260 -0
- package/helpers/config.js +258 -0
- package/helpers/constants.js +38 -0
- package/helpers/getv.js +5 -0
- package/helpers/minecraft.js +308 -0
- package/helpers/modrinth.js +141 -0
- package/helpers/utils.js +334 -0
- package/helpers/versions.js +21 -0
- package/index.js +89 -0
- package/package.json +30 -0
package/helpers/utils.js
ADDED
|
@@ -0,0 +1,334 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import chalk from 'chalk';
|
|
4
|
+
import inquirer from 'inquirer';
|
|
5
|
+
import { pipeline } from 'stream/promises';
|
|
6
|
+
import { Readable } from 'stream';
|
|
7
|
+
import { USER_AGENT, PAGE_SIZE, NEXT_PAGE, PREV_PAGE } from './constants.js';
|
|
8
|
+
|
|
9
|
+
// ============================================
|
|
10
|
+
// HTTP Utilities
|
|
11
|
+
// ============================================
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Fetch JSON from URL with proper user agent
|
|
15
|
+
*/
|
|
16
|
+
export async function fetchJson(url) {
|
|
17
|
+
const response = await fetch(url, {
|
|
18
|
+
headers: { 'User-Agent': USER_AGENT }
|
|
19
|
+
});
|
|
20
|
+
if (!response.ok) {
|
|
21
|
+
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
22
|
+
}
|
|
23
|
+
return await response.json();
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Download a file with optional progress indication
|
|
28
|
+
* @param {string} url - URL to download from
|
|
29
|
+
* @param {string} destPath - Destination file path
|
|
30
|
+
* @param {string} [description] - Optional description for progress display
|
|
31
|
+
* @param {boolean} [showProgress=true] - Whether to show progress
|
|
32
|
+
*/
|
|
33
|
+
export async function downloadFile(url, destPath, description = null, showProgress = true) {
|
|
34
|
+
const response = await fetch(url, {
|
|
35
|
+
headers: { 'User-Agent': USER_AGENT }
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
if (!response.ok) {
|
|
39
|
+
throw new Error(`Download failed: ${response.status} ${response.statusText}`);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const totalSize = parseInt(response.headers.get('content-length') || '0');
|
|
43
|
+
const fileStream = fs.createWriteStream(destPath);
|
|
44
|
+
|
|
45
|
+
if (showProgress && description && totalSize > 0) {
|
|
46
|
+
let downloadedSize = 0;
|
|
47
|
+
const reader = response.body.getReader();
|
|
48
|
+
|
|
49
|
+
while (true) {
|
|
50
|
+
const { done, value } = await reader.read();
|
|
51
|
+
if (done) break;
|
|
52
|
+
|
|
53
|
+
downloadedSize += value.length;
|
|
54
|
+
const percent = Math.round((downloadedSize / totalSize) * 100);
|
|
55
|
+
process.stdout.write(`\r${chalk.gray(` ${description}: ${percent}%`)}`);
|
|
56
|
+
|
|
57
|
+
fileStream.write(Buffer.from(value));
|
|
58
|
+
}
|
|
59
|
+
process.stdout.write('\n');
|
|
60
|
+
fileStream.end();
|
|
61
|
+
} else {
|
|
62
|
+
await pipeline(Readable.fromWeb(response.body), fileStream);
|
|
63
|
+
if (showProgress && description) {
|
|
64
|
+
console.log(chalk.gray(` ${description}: Done`));
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// ============================================
|
|
70
|
+
// Instance Config Utilities
|
|
71
|
+
// ============================================
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Load mcconfig.json from an instance directory
|
|
75
|
+
* @param {string} instancePath - Path to instance directory
|
|
76
|
+
* @returns {object|null} - Config object or null if not found
|
|
77
|
+
*/
|
|
78
|
+
export function loadConfig(instancePath) {
|
|
79
|
+
const configPath = path.join(instancePath, 'mcconfig.json');
|
|
80
|
+
if (!fs.existsSync(configPath)) {
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
83
|
+
try {
|
|
84
|
+
const content = fs.readFileSync(configPath, 'utf-8');
|
|
85
|
+
return JSON.parse(content);
|
|
86
|
+
} catch {
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Save mcconfig.json to an instance directory
|
|
93
|
+
* @param {string} instancePath - Path to instance directory
|
|
94
|
+
* @param {object} config - Config object to save
|
|
95
|
+
*/
|
|
96
|
+
export function saveConfig(instancePath, config) {
|
|
97
|
+
const configPath = path.join(instancePath, 'mcconfig.json');
|
|
98
|
+
fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Get instance path from options or current directory
|
|
103
|
+
* @param {object} options - Command options
|
|
104
|
+
* @returns {string} - Resolved instance path
|
|
105
|
+
*/
|
|
106
|
+
export function getInstancePath(options) {
|
|
107
|
+
return options?.instance ? path.resolve(options.instance) : process.cwd();
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Load config with error handling and user feedback
|
|
112
|
+
* @param {string} instancePath - Path to instance directory
|
|
113
|
+
* @returns {object|null} - Config object or null with error printed
|
|
114
|
+
*/
|
|
115
|
+
export function requireConfig(instancePath) {
|
|
116
|
+
const config = loadConfig(instancePath);
|
|
117
|
+
if (!config) {
|
|
118
|
+
console.log(chalk.red('Error: No mcconfig.json found.'));
|
|
119
|
+
console.log(chalk.gray('Make sure you are in a Minecraft instance directory or use --instance <path>'));
|
|
120
|
+
return null;
|
|
121
|
+
}
|
|
122
|
+
return config;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// ============================================
|
|
126
|
+
// Maven/Library Utilities
|
|
127
|
+
// ============================================
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Convert Maven coordinate to file path
|
|
131
|
+
* e.g., "org.ow2.asm:asm:9.9" -> "org/ow2/asm/asm/9.9/asm-9.9.jar"
|
|
132
|
+
* @param {string} name - Maven coordinate
|
|
133
|
+
* @returns {string|null} - File path or null if invalid
|
|
134
|
+
*/
|
|
135
|
+
export function mavenToPath(name) {
|
|
136
|
+
const parts = name.split(':');
|
|
137
|
+
if (parts.length < 3) return null;
|
|
138
|
+
|
|
139
|
+
const [group, artifact, version] = parts;
|
|
140
|
+
const classifier = parts.length > 3 ? `-${parts[3]}` : '';
|
|
141
|
+
const groupPath = group.replace(/\./g, '/');
|
|
142
|
+
|
|
143
|
+
return `${groupPath}/${artifact}/${version}/${artifact}-${version}${classifier}.jar`;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// ============================================
|
|
147
|
+
// UI Utilities
|
|
148
|
+
// ============================================
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Paginated selection prompt for large lists
|
|
152
|
+
* @param {string} message - Prompt message
|
|
153
|
+
* @param {Array} allChoices - All choices to paginate
|
|
154
|
+
* @param {Function} [getChoiceDisplay] - Function to get display text for a choice
|
|
155
|
+
* @returns {Promise<any>} - Selected value
|
|
156
|
+
*/
|
|
157
|
+
export async function paginatedSelect(message, allChoices, getChoiceDisplay = (c) => c) {
|
|
158
|
+
let currentPage = 0;
|
|
159
|
+
const totalPages = Math.ceil(allChoices.length / PAGE_SIZE);
|
|
160
|
+
|
|
161
|
+
while (true) {
|
|
162
|
+
const startIdx = currentPage * PAGE_SIZE;
|
|
163
|
+
const endIdx = Math.min(startIdx + PAGE_SIZE, allChoices.length);
|
|
164
|
+
const pageChoices = allChoices.slice(startIdx, endIdx);
|
|
165
|
+
|
|
166
|
+
// Build choices for this page
|
|
167
|
+
const choices = pageChoices.map((choice) => ({
|
|
168
|
+
name: typeof choice === 'object' && choice.name ? choice.name : getChoiceDisplay(choice),
|
|
169
|
+
value: typeof choice === 'object' && choice.value !== undefined ? choice.value : choice
|
|
170
|
+
}));
|
|
171
|
+
|
|
172
|
+
// Add navigation options
|
|
173
|
+
if (currentPage > 0) {
|
|
174
|
+
choices.push({ name: chalk.cyan('← Previous page'), value: PREV_PAGE });
|
|
175
|
+
}
|
|
176
|
+
if (currentPage < totalPages - 1) {
|
|
177
|
+
choices.push({ name: chalk.cyan('→ Next page'), value: NEXT_PAGE });
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
const pageInfo = totalPages > 1 ? ` (page ${currentPage + 1}/${totalPages})` : '';
|
|
181
|
+
|
|
182
|
+
const { selection } = await inquirer.prompt([
|
|
183
|
+
{
|
|
184
|
+
type: 'rawlist',
|
|
185
|
+
name: 'selection',
|
|
186
|
+
message: `${message}${pageInfo}`,
|
|
187
|
+
choices: choices
|
|
188
|
+
}
|
|
189
|
+
]);
|
|
190
|
+
|
|
191
|
+
if (selection === NEXT_PAGE) {
|
|
192
|
+
currentPage++;
|
|
193
|
+
continue;
|
|
194
|
+
} else if (selection === PREV_PAGE) {
|
|
195
|
+
currentPage--;
|
|
196
|
+
continue;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
return selection;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// ============================================
|
|
204
|
+
// Formatting Utilities
|
|
205
|
+
// ============================================
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Format bytes to human readable string
|
|
209
|
+
* @param {number} bytes - Byte count
|
|
210
|
+
* @returns {string} - Formatted string (e.g., "1.5 MB")
|
|
211
|
+
*/
|
|
212
|
+
export function formatBytes(bytes) {
|
|
213
|
+
if (bytes === 0) return '0 B';
|
|
214
|
+
const k = 1024;
|
|
215
|
+
const sizes = ['B', 'KB', 'MB', 'GB'];
|
|
216
|
+
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
217
|
+
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Format download count for display
|
|
222
|
+
* @param {number} count - Download count
|
|
223
|
+
* @returns {string} - Formatted string (e.g., "1.5M")
|
|
224
|
+
*/
|
|
225
|
+
export function formatDownloads(count) {
|
|
226
|
+
if (count >= 1000000) {
|
|
227
|
+
return (count / 1000000).toFixed(1) + 'M';
|
|
228
|
+
} else if (count >= 1000) {
|
|
229
|
+
return (count / 1000).toFixed(1) + 'K';
|
|
230
|
+
}
|
|
231
|
+
return count.toString();
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Format ISO date string to readable format
|
|
236
|
+
* @param {string} isoString - ISO date string
|
|
237
|
+
* @returns {string} - Formatted date string
|
|
238
|
+
*/
|
|
239
|
+
export function formatDate(isoString) {
|
|
240
|
+
if (!isoString) return 'Unknown';
|
|
241
|
+
const date = new Date(isoString);
|
|
242
|
+
return date.toLocaleDateString('en-US', {
|
|
243
|
+
year: 'numeric',
|
|
244
|
+
month: 'long',
|
|
245
|
+
day: 'numeric',
|
|
246
|
+
hour: '2-digit',
|
|
247
|
+
minute: '2-digit'
|
|
248
|
+
});
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// ============================================
|
|
252
|
+
// File System Utilities
|
|
253
|
+
// ============================================
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* Get directory size recursively
|
|
257
|
+
* @param {string} dirPath - Directory path
|
|
258
|
+
* @returns {number} - Size in bytes
|
|
259
|
+
*/
|
|
260
|
+
export function getDirSize(dirPath) {
|
|
261
|
+
if (!fs.existsSync(dirPath)) return 0;
|
|
262
|
+
|
|
263
|
+
let size = 0;
|
|
264
|
+
const files = fs.readdirSync(dirPath, { withFileTypes: true });
|
|
265
|
+
|
|
266
|
+
for (const file of files) {
|
|
267
|
+
const filePath = path.join(dirPath, file.name);
|
|
268
|
+
if (file.isDirectory()) {
|
|
269
|
+
size += getDirSize(filePath);
|
|
270
|
+
} else {
|
|
271
|
+
try {
|
|
272
|
+
size += fs.statSync(filePath).size;
|
|
273
|
+
} catch {
|
|
274
|
+
// Skip files we can't read
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
return size;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* Count files in directory recursively
|
|
284
|
+
* @param {string} dirPath - Directory path
|
|
285
|
+
* @param {string} [extension] - Optional extension filter
|
|
286
|
+
* @returns {number} - File count
|
|
287
|
+
*/
|
|
288
|
+
export function countFiles(dirPath, extension = null) {
|
|
289
|
+
if (!fs.existsSync(dirPath)) return 0;
|
|
290
|
+
|
|
291
|
+
let count = 0;
|
|
292
|
+
const files = fs.readdirSync(dirPath, { withFileTypes: true });
|
|
293
|
+
|
|
294
|
+
for (const file of files) {
|
|
295
|
+
const filePath = path.join(dirPath, file.name);
|
|
296
|
+
if (file.isDirectory()) {
|
|
297
|
+
count += countFiles(filePath, extension);
|
|
298
|
+
} else if (!extension || file.name.endsWith(extension)) {
|
|
299
|
+
count++;
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
return count;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
/**
|
|
307
|
+
* Parse a value string into appropriate type (boolean, number, or string)
|
|
308
|
+
* @param {string} value - Value to parse
|
|
309
|
+
* @returns {boolean|number|string|null} - Parsed value
|
|
310
|
+
*/
|
|
311
|
+
export function parseValue(value) {
|
|
312
|
+
if (value === 'null' || value === 'auto') return null;
|
|
313
|
+
if (value === 'true') return true;
|
|
314
|
+
if (value === 'false') return false;
|
|
315
|
+
if (!isNaN(value) && value !== '') return parseFloat(value);
|
|
316
|
+
return value;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
export default {
|
|
320
|
+
fetchJson,
|
|
321
|
+
downloadFile,
|
|
322
|
+
loadConfig,
|
|
323
|
+
saveConfig,
|
|
324
|
+
getInstancePath,
|
|
325
|
+
requireConfig,
|
|
326
|
+
mavenToPath,
|
|
327
|
+
paginatedSelect,
|
|
328
|
+
formatBytes,
|
|
329
|
+
formatDownloads,
|
|
330
|
+
formatDate,
|
|
331
|
+
getDirSize,
|
|
332
|
+
countFiles,
|
|
333
|
+
parseValue
|
|
334
|
+
};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
const versions = [
|
|
2
|
+
'0.1.0',
|
|
3
|
+
'0.2.0',
|
|
4
|
+
'0.2.1',
|
|
5
|
+
'0.2.2',
|
|
6
|
+
'0.3.0',
|
|
7
|
+
'0.3.1',
|
|
8
|
+
'0.3.2',
|
|
9
|
+
'0.4.0',
|
|
10
|
+
];
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
function enumerate(version) {
|
|
14
|
+
return versions.indexOf(version) ?? -2;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function denumerate(index) {
|
|
18
|
+
return versions[index] ?? null;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export { enumerate, denumerate };
|
package/index.js
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
#! /usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { createInstance } from './commands/create.js';
|
|
4
|
+
import { searchMods } from './commands/search.js';
|
|
5
|
+
import { installMod } from './commands/install.js';
|
|
6
|
+
import { uninstallMod } from './commands/uninstall.js';
|
|
7
|
+
import { authCommand } from './commands/auth.js';
|
|
8
|
+
import { launchInstance } from './commands/launch.js';
|
|
9
|
+
import { instanceInfo } from './commands/info.js';
|
|
10
|
+
import { upgrade } from './commands/upgrade.js';
|
|
11
|
+
import { showVersion } from './commands/version.js';
|
|
12
|
+
import { configCommand } from './commands/config.js';
|
|
13
|
+
|
|
14
|
+
import {program} from 'commander';
|
|
15
|
+
|
|
16
|
+
program
|
|
17
|
+
.option('-v, --version', 'Show the curent version')
|
|
18
|
+
.action(showVersion)
|
|
19
|
+
|
|
20
|
+
program
|
|
21
|
+
.command('search <query>')
|
|
22
|
+
.description('Search for Minecraft mods on Modrinth')
|
|
23
|
+
.option('-l, --limit <number>', 'Number of results to show', '10')
|
|
24
|
+
.option('-v, --version <version>', 'Filter by Minecraft version')
|
|
25
|
+
.option('--loader <loader>', 'Filter by mod loader (fabric, forge, quilt, neoforge)')
|
|
26
|
+
.option('--verbose', 'Enable verbose output')
|
|
27
|
+
.action(searchMods);
|
|
28
|
+
|
|
29
|
+
program
|
|
30
|
+
.command('create')
|
|
31
|
+
.description('Create a new Minecraft instance')
|
|
32
|
+
.option('--verbose', 'Enable verbose output')
|
|
33
|
+
.action(createInstance);
|
|
34
|
+
|
|
35
|
+
program
|
|
36
|
+
.command('install <mod>')
|
|
37
|
+
.description('Install a mod to the current Minecraft instance')
|
|
38
|
+
.option('-i, --instance <path>', 'Path to the instance directory')
|
|
39
|
+
.option('-f, --force', 'Force reinstall/update if already installed')
|
|
40
|
+
.option('--verbose', 'Enable verbose output')
|
|
41
|
+
.action(installMod);
|
|
42
|
+
|
|
43
|
+
program
|
|
44
|
+
.command('uninstall [mod]')
|
|
45
|
+
.description('Uninstall a mod from the current Minecraft instance')
|
|
46
|
+
.option('-i, --instance <path>', 'Path to the instance directory')
|
|
47
|
+
.option('-f, --force', 'Skip confirmation prompt')
|
|
48
|
+
.option('--verbose', 'Enable verbose output')
|
|
49
|
+
.action(uninstallMod);
|
|
50
|
+
|
|
51
|
+
program
|
|
52
|
+
.command('auth [action] [args...]')
|
|
53
|
+
.description('Manage Minecraft accounts (login, logout, switch, status)')
|
|
54
|
+
.option('-f, --force', 'Skip confirmation prompts')
|
|
55
|
+
.option('--verbose', 'Enable verbose output')
|
|
56
|
+
.action(authCommand);
|
|
57
|
+
|
|
58
|
+
program
|
|
59
|
+
.command('launch')
|
|
60
|
+
.description('Launch the Minecraft instance')
|
|
61
|
+
.option('-i, --instance <path>', 'Path to the instance directory')
|
|
62
|
+
.option('--offline', 'Launch in offline mode')
|
|
63
|
+
.option('--verbose', 'Enable verbose output')
|
|
64
|
+
.action(launchInstance);
|
|
65
|
+
|
|
66
|
+
program
|
|
67
|
+
.command('info')
|
|
68
|
+
.description('Show information about the current Minecraft instance')
|
|
69
|
+
.option('-i, --instance <path>', 'Path to the instance directory')
|
|
70
|
+
.option('--verbose', 'Show detailed information')
|
|
71
|
+
.option('--mods', 'List installed mods')
|
|
72
|
+
.action(instanceInfo);
|
|
73
|
+
|
|
74
|
+
program
|
|
75
|
+
.command('upgrade [mod]')
|
|
76
|
+
.description('Upgrade mods, Minecraft version, or mod loader')
|
|
77
|
+
.option('-i, --instance <path>', 'Path to the instance directory')
|
|
78
|
+
.option('-f, --force', 'Force upgrade even if already up to date')
|
|
79
|
+
.option('--verbose', 'Enable verbose output')
|
|
80
|
+
.action(upgrade);
|
|
81
|
+
|
|
82
|
+
program
|
|
83
|
+
.command('config [action] [args...]')
|
|
84
|
+
.description('Manage CLI settings and game settings')
|
|
85
|
+
.option('-i, --instance <path>', 'Path to the instance directory')
|
|
86
|
+
.option('--verbose', 'Show detailed output')
|
|
87
|
+
.action(configCommand);
|
|
88
|
+
|
|
89
|
+
program.parse();
|
package/package.json
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@bobschlowinskii/clicraft",
|
|
3
|
+
"version": "0.4.0",
|
|
4
|
+
"description": "A simple Minecraft Mod Package Manager written in Node.JS",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
|
9
|
+
},
|
|
10
|
+
"bin": {
|
|
11
|
+
"clicraft": "./index.js"
|
|
12
|
+
},
|
|
13
|
+
"repository": {
|
|
14
|
+
"type": "git",
|
|
15
|
+
"url": "git+https://github.com/theinfamousben/clicraft.git"
|
|
16
|
+
},
|
|
17
|
+
"author": "theinfamousben",
|
|
18
|
+
"license": "ISC",
|
|
19
|
+
"bugs": {
|
|
20
|
+
"url": "https://github.com/theinfamousben/clicraft/issues"
|
|
21
|
+
},
|
|
22
|
+
"homepage": "https://github.com/theinfamousben/clicraft#readme",
|
|
23
|
+
"dependencies": {
|
|
24
|
+
"chalk": "^5.6.2",
|
|
25
|
+
"commander": "^14.0.2",
|
|
26
|
+
"conf": "^15.0.2",
|
|
27
|
+
"inquirer": "^13.2.0",
|
|
28
|
+
"open": "^11.0.0"
|
|
29
|
+
}
|
|
30
|
+
}
|