@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.
@@ -0,0 +1,343 @@
1
+ import chalk from 'chalk';
2
+ import fs from 'fs';
3
+ import path from 'path';
4
+ import inquirer from 'inquirer';
5
+ import version from '../helpers/getv.js';
6
+ import * as v from '../helpers/versions.js';
7
+ import {
8
+ fetchJson,
9
+ downloadFile,
10
+ loadConfig,
11
+ saveConfig,
12
+ getInstancePath,
13
+ requireConfig,
14
+ paginatedSelect,
15
+ mavenToPath
16
+ } from '../helpers/utils.js';
17
+ import { getProjectVersions } from '../helpers/modrinth.js';
18
+ import { MOJANG_VERSION_MANIFEST, FABRIC_META, FABRIC_MAVEN } from '../helpers/constants.js';
19
+
20
+ const CONFIG_VERSION = version;
21
+
22
+ // Update a single mod
23
+ async function updateMod(instancePath, config, mod, options) {
24
+ console.log(chalk.gray(`\nChecking ${mod.name}...`));
25
+
26
+ try {
27
+ const versions = await getProjectVersions(mod.slug, config.minecraftVersion, config.modLoader);
28
+
29
+ if (versions.length === 0) {
30
+ console.log(chalk.yellow(` ⚠️ No compatible version found for ${config.minecraftVersion}`));
31
+ return false;
32
+ }
33
+
34
+ const latestVersion = versions[0];
35
+
36
+ if (latestVersion.id === mod.versionId && !options.force) {
37
+ console.log(chalk.gray(` ✓ Already up to date (${mod.versionNumber})`));
38
+ return false;
39
+ }
40
+
41
+ const file = latestVersion.files.find(f => f.primary) || latestVersion.files[0];
42
+ if (!file) {
43
+ console.log(chalk.red(` ✗ No downloadable file found`));
44
+ return false;
45
+ }
46
+
47
+ // Remove old file
48
+ const modsPath = path.join(instancePath, 'mods');
49
+ const oldFilePath = path.join(modsPath, mod.fileName);
50
+ if (fs.existsSync(oldFilePath)) {
51
+ fs.unlinkSync(oldFilePath);
52
+ }
53
+
54
+ // Download new file
55
+ const destPath = path.join(modsPath, file.filename);
56
+ console.log(chalk.gray(` Downloading ${file.filename}...`));
57
+ await downloadFile(file.url, destPath, null, false);
58
+
59
+ // Update mod entry
60
+ mod.versionId = latestVersion.id;
61
+ mod.versionNumber = latestVersion.version_number;
62
+ mod.fileName = file.filename;
63
+ mod.updatedAt = new Date().toISOString();
64
+
65
+ console.log(chalk.green(` ✓ Updated to ${latestVersion.version_number}`));
66
+ return true;
67
+ } catch (error) {
68
+ console.log(chalk.red(` ✗ Error: ${error.message}`));
69
+ return false;
70
+ }
71
+ }
72
+
73
+ // Upgrade all mods
74
+ async function upgradeMods(instancePath, config, options) {
75
+ if (config.mods.length === 0) {
76
+ console.log(chalk.yellow('No mods installed.'));
77
+ return;
78
+ }
79
+
80
+ console.log(chalk.cyan(`\n📦 Checking ${config.mods.length} mods for updates...\n`));
81
+
82
+ let updated = 0;
83
+ for (const mod of config.mods) {
84
+ if (await updateMod(instancePath, config, mod, options)) {
85
+ updated++;
86
+ }
87
+ }
88
+
89
+ saveConfig(instancePath, config);
90
+
91
+ console.log();
92
+ if (updated > 0) {
93
+ console.log(chalk.green(`✅ Updated ${updated} mod(s)`));
94
+ } else {
95
+ console.log(chalk.gray('All mods are up to date.'));
96
+ }
97
+ }
98
+
99
+ // Upgrade a single mod by name
100
+ async function upgradeSingleMod(instancePath, config, modName, options) {
101
+ const mod = config.mods.find(m =>
102
+ m.slug.toLowerCase() === modName.toLowerCase() ||
103
+ m.name.toLowerCase() === modName.toLowerCase()
104
+ );
105
+
106
+ if (!mod) {
107
+ console.log(chalk.red(`Error: Mod "${modName}" is not installed.`));
108
+ console.log(chalk.gray('Installed mods:'));
109
+ for (const m of config.mods) {
110
+ console.log(chalk.gray(` - ${m.name} (${m.slug})`));
111
+ }
112
+ return;
113
+ }
114
+
115
+ console.log(chalk.cyan(`\n📦 Upgrading ${mod.name}...`));
116
+
117
+ if (await updateMod(instancePath, config, mod, { ...options, force: true })) {
118
+ saveConfig(instancePath, config);
119
+ console.log(chalk.green(`\n✅ Successfully upgraded ${mod.name}`));
120
+ } else {
121
+ console.log(chalk.yellow(`\n⚠️ Could not upgrade ${mod.name}`));
122
+ }
123
+ }
124
+
125
+ // Upgrade Minecraft version
126
+ async function upgradeMinecraft(instancePath, config, options) {
127
+ console.log(chalk.cyan('\n🎮 Upgrade Minecraft Version\n'));
128
+ console.log(chalk.gray(`Current version: ${config.minecraftVersion}`));
129
+
130
+ console.log(chalk.gray('Fetching available versions...'));
131
+ const manifest = await fetchJson(MOJANG_VERSION_MANIFEST);
132
+
133
+ const releaseVersions = manifest.versions
134
+ .filter(v => v.type === 'release')
135
+ .map(v => v.id);
136
+
137
+ const currentIndex = releaseVersions.indexOf(config.minecraftVersion);
138
+ const newerVersions = currentIndex > 0 ? releaseVersions.slice(0, currentIndex) : [];
139
+
140
+ if (newerVersions.length === 0) {
141
+ console.log(chalk.green('\n✓ You are already on the latest Minecraft version!'));
142
+ return;
143
+ }
144
+
145
+ console.log(chalk.yellow(`\n${newerVersions.length} newer version(s) available`));
146
+
147
+ const newVersion = await paginatedSelect('Select new Minecraft version:', newerVersions);
148
+
149
+ const { confirm } = await inquirer.prompt([{
150
+ type: 'confirm',
151
+ name: 'confirm',
152
+ message: `Upgrade from ${config.minecraftVersion} to ${newVersion}?`,
153
+ default: false
154
+ }]);
155
+
156
+ if (!confirm) {
157
+ console.log(chalk.yellow('Upgrade cancelled.'));
158
+ return;
159
+ }
160
+
161
+ console.log(chalk.yellow('\n⚠️ Warning: Changing Minecraft version requires:'));
162
+ console.log(chalk.gray(' 1. Re-downloading game files (libraries, assets)'));
163
+ console.log(chalk.gray(' 2. Updating mod loader'));
164
+ console.log(chalk.gray(' 3. Checking mod compatibility'));
165
+ console.log();
166
+ console.log(chalk.red('This feature is not yet fully implemented.'));
167
+ console.log(chalk.gray('For now, create a new instance with the desired version.'));
168
+ }
169
+
170
+ // Upgrade mod loader version
171
+ async function upgradeLoader(instancePath, config, options) {
172
+ console.log(chalk.cyan('\n🔧 Upgrade Mod Loader\n'));
173
+ console.log(chalk.gray(`Current: ${config.modLoader} ${config.loaderVersion}`));
174
+
175
+ if (config.modLoader === 'fabric') {
176
+ console.log(chalk.gray('Fetching Fabric loader versions...'));
177
+ const loaderVersions = await fetchJson(`${FABRIC_META}/versions/loader`);
178
+ const stableVersions = loaderVersions.filter(v => v.stable).map(v => v.version);
179
+
180
+ const currentIndex = stableVersions.indexOf(config.loaderVersion);
181
+ const newerVersions = currentIndex > 0 ? stableVersions.slice(0, currentIndex) :
182
+ currentIndex === -1 ? stableVersions.slice(0, 5) : [];
183
+
184
+ if (newerVersions.length === 0) {
185
+ console.log(chalk.green('\n✓ You are already on the latest Fabric loader version!'));
186
+ return;
187
+ }
188
+
189
+ console.log(chalk.yellow(`\n${newerVersions.length} newer version(s) available`));
190
+
191
+ const newVersion = await paginatedSelect('Select Fabric loader version:', stableVersions);
192
+
193
+ if (newVersion === config.loaderVersion) {
194
+ console.log(chalk.gray('No change selected.'));
195
+ return;
196
+ }
197
+
198
+ const oldVersionId = config.versionId;
199
+ config.loaderVersion = newVersion;
200
+ config.versionId = `fabric-loader-${newVersion}-${config.minecraftVersion}`;
201
+
202
+ console.log(chalk.gray('\nFetching new Fabric profile...'));
203
+ const fabricProfile = await fetchJson(
204
+ `${FABRIC_META}/versions/loader/${config.minecraftVersion}/${newVersion}/profile/json`
205
+ );
206
+
207
+ const versionsPath = path.join(instancePath, 'versions');
208
+ const newVersionPath = path.join(versionsPath, config.versionId);
209
+ fs.mkdirSync(newVersionPath, { recursive: true });
210
+ fs.writeFileSync(
211
+ path.join(newVersionPath, `${config.versionId}.json`),
212
+ JSON.stringify(fabricProfile, null, 2)
213
+ );
214
+
215
+ console.log(chalk.gray('Checking for new libraries...'));
216
+ const librariesPath = path.join(instancePath, 'libraries');
217
+
218
+ for (const lib of fabricProfile.libraries) {
219
+ const relativePath = mavenToPath(lib.name);
220
+ if (!relativePath) continue;
221
+
222
+ const libPath = path.join(librariesPath, relativePath);
223
+
224
+ if (!fs.existsSync(libPath)) {
225
+ const url = (lib.url || FABRIC_MAVEN + '/') + relativePath;
226
+ console.log(chalk.gray(` Downloading ${lib.name.split(':')[1]}...`));
227
+ fs.mkdirSync(path.dirname(libPath), { recursive: true });
228
+ try {
229
+ await downloadFile(url, libPath, null, false);
230
+ } catch (err) {
231
+ console.log(chalk.yellow(` Warning: Failed to download`));
232
+ }
233
+ }
234
+ }
235
+
236
+ if (oldVersionId !== config.versionId) {
237
+ const oldVersionPath = path.join(versionsPath, oldVersionId);
238
+ if (fs.existsSync(oldVersionPath)) {
239
+ fs.rmSync(oldVersionPath, { recursive: true });
240
+ }
241
+ }
242
+
243
+ saveConfig(instancePath, config);
244
+ console.log(chalk.green(`\n✅ Upgraded Fabric loader to ${newVersion}`));
245
+
246
+ } else if (config.modLoader === 'forge') {
247
+ console.log(chalk.yellow('\n⚠️ Forge loader upgrade is not yet implemented.'));
248
+ console.log(chalk.gray('For now, create a new instance with the desired Forge version.'));
249
+ } else {
250
+ console.log(chalk.red(`Unknown mod loader: ${config.modLoader}`));
251
+ }
252
+ }
253
+
254
+ // Upgrade config version
255
+ async function upgradeConfig(instancePath, config, options) {
256
+ const configVersionInt = v.enumerate(config.configVersion) ?? -1;
257
+ const currentVersionInt = v.enumerate(CONFIG_VERSION);
258
+
259
+ if (configVersionInt === currentVersionInt) {
260
+ console.log(chalk.green('✓ Config is already up to date.'));
261
+ return;
262
+ }
263
+
264
+ if (configVersionInt > currentVersionInt) {
265
+ console.log(chalk.yellow('Config version is newer than CLIcraft. Please update CLIcraft.'));
266
+ return;
267
+ }
268
+
269
+ console.log(chalk.cyan('\n🔄 Upgrading config format...\n'));
270
+
271
+ if (configVersionInt === -1) {
272
+ config.configVersion = CONFIG_VERSION;
273
+ console.log(chalk.gray(' + Added configVersion field'));
274
+ }
275
+
276
+ if (configVersionInt === -2) {
277
+ config.configVersion = CONFIG_VERSION;
278
+ console.log(chalk.gray(' + Set configVersion to current version'));
279
+ }
280
+
281
+ saveConfig(instancePath, config);
282
+ console.log(chalk.green(`\n✅ Config upgraded to v${CONFIG_VERSION}`));
283
+ }
284
+
285
+ // Main upgrade command
286
+ export async function upgrade(modName, options) {
287
+ const instancePath = getInstancePath(options);
288
+
289
+ const config = requireConfig(instancePath);
290
+ if (!config) return;
291
+
292
+ try {
293
+ if (modName) {
294
+ await upgradeSingleMod(instancePath, config, modName, options);
295
+ return;
296
+ }
297
+
298
+ console.log(chalk.cyan(`\n🔄 Upgrade ${config.name}\n`));
299
+
300
+ const { upgradeType } = await inquirer.prompt([{
301
+ type: 'rawlist',
302
+ name: 'upgradeType',
303
+ message: 'What would you like to upgrade?',
304
+ choices: [
305
+ { name: '📦 All mods', value: 'mods' },
306
+ { name: '🎮 Minecraft version', value: 'minecraft' },
307
+ { name: '🔧 Mod loader version', value: 'loader' },
308
+ { name: '⚙️ Config format', value: 'config' },
309
+ { name: '❌ Cancel', value: 'cancel' }
310
+ ]
311
+ }]);
312
+
313
+ switch (upgradeType) {
314
+ case 'mods':
315
+ await upgradeMods(instancePath, config, options);
316
+ break;
317
+ case 'minecraft':
318
+ await upgradeMinecraft(instancePath, config, options);
319
+ break;
320
+ case 'loader':
321
+ await upgradeLoader(instancePath, config, options);
322
+ break;
323
+ case 'config':
324
+ await upgradeConfig(instancePath, config, options);
325
+ break;
326
+ case 'cancel':
327
+ console.log(chalk.gray('Upgrade cancelled.'));
328
+ break;
329
+ }
330
+
331
+ } catch (error) {
332
+ if (error.name === 'ExitPromptError') {
333
+ console.log(chalk.yellow('\nUpgrade cancelled.'));
334
+ return;
335
+ }
336
+ console.error(chalk.red('Error during upgrade:'), error.message);
337
+ if (options.verbose) {
338
+ console.error(error);
339
+ }
340
+ }
341
+ }
342
+
343
+ export default { upgrade };
@@ -0,0 +1,21 @@
1
+ import chalk from 'chalk';
2
+ import version from '../helpers/getv.js';
3
+
4
+ const art = "\n" +
5
+ " _____ _ _____ __ _ \n" +
6
+ " / ____| | |_ _| / _| | \n" +
7
+ " | | | | | | ___ _ __ __ _| |_| |_ \n" +
8
+ " | | | | | | / __| '__/ _` | _| __|\n" +
9
+ " | |____| |____ _| || (__| | | (_| | | | |_ \n" +
10
+ " \\_____|______|_____\\___|_| \\__,_|_| \\__|\n" +
11
+ " "
12
+ ;
13
+
14
+ const output = (`
15
+ ${chalk.white(art)}\n
16
+ ${chalk.white('Version:')} ${chalk.gray(version)}
17
+ `);
18
+
19
+ export function showVersion() {
20
+ console.log(output);
21
+ }
package/docs/README.md ADDED
@@ -0,0 +1,119 @@
1
+ # CLIcraft Documentation
2
+
3
+ This directory contains the complete documentation for CLIcraft - A Minecraft Mod Package Manager.
4
+
5
+ ## 📚 Documentation Structure
6
+
7
+ ### Getting Started
8
+ - **[index.md](index.md)** - Homepage with project overview and quick start guide
9
+ - **[installation.md](installation.md)** - Installation instructions for all platforms
10
+
11
+ ### Commands
12
+ - **[commands.md](commands.md)** - Overview of all available commands
13
+
14
+ #### Detailed Command Documentation
15
+ - **[commands/create.md](commands/create.md)** - Create new Minecraft instances
16
+ - **[commands/search.md](commands/search.md)** - Search for mods on Modrinth
17
+ - **[commands/install.md](commands/install.md)** - Install mods to instances
18
+ - **[commands/login.md](commands/login.md)** - Authentication commands (login, logout, status)
19
+ - **[commands/launch.md](commands/launch.md)** - Launch Minecraft instances
20
+ - **[commands/info.md](commands/info.md)** - View instance information
21
+ - **[commands/upgrade.md](commands/upgrade.md)** - Update mods and versions
22
+
23
+ ### Configuration
24
+ - **[configuration.md](configuration.md)** - Configuration guide including mcconfig.json, JVM arguments, and templates
25
+
26
+ ## 🎯 Documentation Features
27
+
28
+ Each documentation page includes:
29
+ - ✨ **Clear syntax and examples** - Easy to understand command usage
30
+ - 🎮 **Real-world examples** - Practical usage scenarios
31
+ - 💡 **Pro tips** - Best practices and optimization advice
32
+ - ⚠️ **Troubleshooting** - Common issues and solutions
33
+ - 🔗 **Navigation links** - Easy navigation between related topics
34
+ - 📋 **Detailed explanations** - Comprehensive coverage of features
35
+ - 🎨 **Emoji indicators** - Visual organization and readability
36
+
37
+ ## 📖 Reading the Documentation
38
+
39
+ ### For New Users
40
+ 1. Start with [index.md](index.md) for an overview
41
+ 2. Follow [installation.md](installation.md) to install CLIcraft
42
+ 3. Read [commands/create.md](commands/create.md) to create your first instance
43
+ 4. Explore other command documentation as needed
44
+
45
+ ### For Existing Users
46
+ - Use [commands.md](commands.md) as a quick reference
47
+ - Check specific command pages for detailed options
48
+ - Review [configuration.md](configuration.md) for optimization tips
49
+
50
+ ### For Troubleshooting
51
+ - Each command page has a "Common Issues" section
52
+ - Check "Troubleshooting" sections for solutions
53
+ - Review [configuration.md](configuration.md) for performance tuning
54
+
55
+ ## 🌐 Viewing the Documentation
56
+
57
+ ### On GitHub
58
+ The documentation is written in Markdown and can be viewed directly on GitHub:
59
+ https://github.com/theinfamousben/clicraft/tree/main/docs
60
+
61
+ ### Locally
62
+ View the documentation files with any Markdown viewer or text editor:
63
+ ```bash
64
+ cd docs
65
+ cat index.md
66
+ ```
67
+
68
+ ### Future: GitHub Pages
69
+ In the future, this documentation may be hosted on GitHub Pages for a better reading experience.
70
+
71
+ ## 📝 Documentation Style Guide
72
+
73
+ The documentation follows these conventions:
74
+ - **Commands** - Shown in code blocks with `clicraft command`
75
+ - **Options** - Listed in tables with descriptions
76
+ - **Examples** - Practical, real-world usage scenarios
77
+ - **File paths** - Shown with proper directory structure
78
+ - **Code snippets** - Syntax-highlighted JSON and bash examples
79
+
80
+ ## 🤝 Contributing to Documentation
81
+
82
+ To improve the documentation:
83
+ 1. Fork the repository
84
+ 2. Edit the relevant `.md` files
85
+ 3. Submit a pull request
86
+ 4. Include a clear description of changes
87
+
88
+ ### Documentation Guidelines
89
+ - Keep examples practical and tested
90
+ - Use clear, concise language
91
+ - Include troubleshooting for common issues
92
+ - Add links to related documentation
93
+ - Maintain consistent formatting
94
+
95
+ ## 📊 Documentation Statistics
96
+
97
+ Total documentation: **~3,500 lines**
98
+ - Homepage: ~70 lines
99
+ - Installation: ~150 lines
100
+ - Commands overview: ~190 lines
101
+ - Command pages: ~2,500 lines (7 commands)
102
+ - Configuration: ~500 lines
103
+
104
+ ## 🔗 External Resources
105
+
106
+ - [GitHub Repository](https://github.com/theinfamousben/clicraft)
107
+ - [Modrinth](https://modrinth.com) - Mod hosting platform
108
+ - [Minecraft](https://www.minecraft.net) - Official Minecraft website
109
+
110
+ ## 🆘 Need Help?
111
+
112
+ If you can't find what you're looking for in the documentation:
113
+ 1. Check the [GitHub Issues](https://github.com/theinfamousben/clicraft/issues)
114
+ 2. Open a new issue with your question
115
+ 3. Visit the [Discussions](https://github.com/theinfamousben/clicraft/discussions)
116
+
117
+ ## 📄 License
118
+
119
+ This documentation is part of the CLIcraft project and is licensed under the ISC license.
@@ -0,0 +1,63 @@
1
+ title: CLIcraft Documentation
2
+ description: A simple Minecraft Mod Package Manager written in Node.js
3
+ remote_theme: just-the-docs/just-the-docs
4
+ color_scheme: dark
5
+
6
+ # GitHub repository
7
+ repository: theinfamousben/clicraft
8
+ url: https://theinfamousben.github.io/clicraft
9
+
10
+ # Enable search
11
+ search_enabled: true
12
+ search:
13
+ heading_level: 2
14
+ previews: 3
15
+ preview_words_before: 5
16
+ preview_words_after: 10
17
+ tokenizer_separator: /[\s/]+/
18
+ rel_url: true
19
+ button: false
20
+
21
+ # Callouts (for notes, warnings, etc.)
22
+ callouts_level: quiet
23
+ callouts:
24
+ note:
25
+ title: Note
26
+ color: blue
27
+ warning:
28
+ title: Warning
29
+ color: red
30
+ tip:
31
+ title: Pro Tip
32
+ color: green
33
+
34
+ # Navigation structure
35
+ nav_sort: case_insensitive
36
+
37
+ # Aux links
38
+ aux_links:
39
+ "GitHub Repository":
40
+ - "https://github.com/theinfamousben/clicraft"
41
+
42
+ # Footer content
43
+ footer_content: "Copyright © 2024 theinfamousben. Distributed under the ISC license."
44
+
45
+ # Back to top link
46
+ back_to_top: true
47
+ back_to_top_text: "Back to top"
48
+
49
+ # Markdown settings
50
+ markdown: kramdown
51
+ kramdown:
52
+ input: GFM
53
+ syntax_highlighter: rouge
54
+ syntax_highlighter_opts:
55
+ block:
56
+ line_numbers: false
57
+
58
+ # Exclude from processing
59
+ exclude:
60
+ - README.md
61
+ - node_modules
62
+ - package.json
63
+ - package-lock.json