@bobschlowinskii/clicraft 0.4.4 → 0.4.5

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 CHANGED
@@ -2,7 +2,21 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file.
4
4
 
5
- ## [0.4.4] - [TBA]
5
+ ## [0.4.5] - 2026-01-31
6
+
7
+ ### Added
8
+ - **Added `force` option to `create` command**
9
+ - Overwrites existing instance if folder already exists in path
10
+ - call with `--force` or `-f`
11
+ - **Added `path` option to `create` command**
12
+ - Specifies a path other than the working directory to install the instance
13
+ - call with `--path` or `-p`
14
+ - **Multi-mod installation support**
15
+ - Install multiple mods in a single command: `clicraft install mod1 mod2 mod3`
16
+ - Shows installation summary when installing multiple mods
17
+ - Tracks success/failure count for batch installations
18
+
19
+ ## [0.4.4] - 2026-01-29
6
20
 
7
21
  ### Added
8
22
 
@@ -482,7 +482,7 @@ export async function createInstance(options) {
482
482
  message: 'Instance Name:',
483
483
  validate: (input) => {
484
484
  if (!input.trim()) return 'Instance name is required';
485
- if (fs.existsSync(input.trim())) return 'A folder with this name already exists';
485
+ if (fs.existsSync(input.trim()) && !options.force) return 'A folder with this name already exists';
486
486
  return true;
487
487
  }
488
488
  }]);
@@ -526,7 +526,16 @@ export async function createInstance(options) {
526
526
  loaderVersion = await paginatedSelect('Forge Version:', forgeChoices);
527
527
  }
528
528
 
529
- const instancePath = path.resolve(instanceName.trim());
529
+ const instancePath = options.path
530
+ ? path.resolve(options.path) + path.resolve(instanceName.trim())
531
+ : path.resolve(instanceName.trim())
532
+ ;
533
+ if (fs.existsSync(instancePath) && !options.force) {
534
+ console.log(chalk.red(`A folder already exists at ${instancePath}. Use --force to overwrite.`));
535
+ return;
536
+ } else if (fs.existsSync(instancePath) && options.force) {
537
+ fs.rmSync(instancePath, { recursive: true, force: true });
538
+ }
530
539
  fs.mkdirSync(instancePath, { recursive: true });
531
540
  fs.mkdirSync(path.join(instancePath, 'mods'), { recursive: true });
532
541
 
@@ -5,12 +5,41 @@ import { downloadFile, loadConfig, saveConfig, getInstancePath, requireConfig }
5
5
  import { getProject, getProjectVersions } from '../helpers/modrinth.js';
6
6
  import { callPostCommandActions } from '../helpers/post-command.js';
7
7
 
8
- export async function installMod(modSlug, options) {
8
+ export async function installMod(modSlugs, options) {
9
9
  const instancePath = getInstancePath(options);
10
10
 
11
11
  const config = requireConfig(instancePath);
12
12
  if (!config) return;
13
13
 
14
+ // Handle multiple mods
15
+ const slugs = Array.isArray(modSlugs) ? modSlugs : [modSlugs];
16
+
17
+ let successCount = 0;
18
+ let failCount = 0;
19
+
20
+ for (const modSlug of slugs) {
21
+ const success = await installSingleMod(modSlug, instancePath, config, options);
22
+ if (success) {
23
+ successCount++;
24
+ } else {
25
+ failCount++;
26
+ }
27
+ }
28
+
29
+ // Show summary if multiple mods were requested
30
+ if (slugs.length > 1) {
31
+ console.log(chalk.cyan('\nšŸ“Š Installation Summary:'));
32
+ console.log(chalk.green(` āœ… ${successCount} mod(s) installed successfully`));
33
+ if (failCount > 0) {
34
+ console.log(chalk.red(` āŒ ${failCount} mod(s) failed to install`));
35
+ }
36
+ }
37
+
38
+ callPostCommandActions();
39
+ }
40
+
41
+ async function installSingleMod(modSlug, instancePath, config, options) {
42
+
14
43
  console.log(chalk.cyan(`\nšŸ“¦ Installing "${modSlug}" to ${config.name}...\n`));
15
44
 
16
45
  try {
@@ -19,12 +48,12 @@ export async function installMod(modSlug, options) {
19
48
  if (!project) {
20
49
  console.log(chalk.red(`Error: Mod "${modSlug}" not found on Modrinth.`));
21
50
  console.log(chalk.gray('Use "clicraft search <query>" to find available mods.'));
22
- return;
51
+ return false;
23
52
  }
24
53
 
25
54
  if (project.project_type !== 'mod') {
26
55
  console.log(chalk.red(`Error: "${modSlug}" is a ${project.project_type}, not a mod.`));
27
- return;
56
+ return false;
28
57
  }
29
58
 
30
59
  console.log(chalk.gray(`Found: ${project.title}`));
@@ -44,7 +73,7 @@ export async function installMod(modSlug, options) {
44
73
  console.log(chalk.gray(`\nAvailable loaders: ${loaders.join(', ')}`));
45
74
  console.log(chalk.gray(`Recent game versions: ${gameVersions.join(', ')}`));
46
75
  }
47
- return;
76
+ return false;
48
77
  }
49
78
 
50
79
  // Use the latest compatible version
@@ -53,7 +82,7 @@ export async function installMod(modSlug, options) {
53
82
 
54
83
  if (!file) {
55
84
  console.log(chalk.red('Error: No downloadable file found for this version.'));
56
- return;
85
+ return false;
57
86
  }
58
87
 
59
88
  // Check if already installed
@@ -61,7 +90,7 @@ export async function installMod(modSlug, options) {
61
90
  if (existingMod && !options.force) {
62
91
  console.log(chalk.yellow(`\nāš ļø ${project.title} is already installed (version ${existingMod.versionNumber})`));
63
92
  console.log(chalk.gray('Use --force to reinstall or update.'));
64
- return;
93
+ return false;
65
94
  }
66
95
 
67
96
  // Create mods folder if needed
@@ -104,30 +133,52 @@ export async function installMod(modSlug, options) {
104
133
  // Show dependencies if any
105
134
  if (version.dependencies?.length > 0) {
106
135
  const requiredDeps = version.dependencies.filter(d => d.dependency_type === 'required');
107
- if (requiredDeps.length > 0) {
108
- console.log(chalk.yellow(`\nāš ļø This mod has ${requiredDeps.length} required dependencies:`));
109
- for (const dep of requiredDeps) {
110
- if (dep.project_id) {
111
- const depProject = await getProject(dep.project_id);
112
- if (depProject) {
113
- const isInstalled = config.mods.some(m => m.projectId === dep.project_id);
114
- const status = isInstalled ? chalk.green('āœ“') : chalk.red('āœ—');
115
- console.log(chalk.gray(` ${status} ${depProject.title} (${depProject.slug})`));
116
- }
136
+ let totalDeps = [];
137
+ let notInstalledDeps = [];
138
+
139
+ for (const dep of requiredDeps) {
140
+ if (dep.project_id) {
141
+ const depProject = await getProject(dep.project_id);
142
+ if (depProject) {
143
+ const isInstalled = config.mods.some(m => m.projectId === dep.project_id);
144
+ // const status = isInstalled ? chalk.green('āœ“') : chalk.red('āœ—');
145
+ totalDeps.push({ project: depProject, title: depProject.title, slug: depProject.slug, installed: isInstalled });
117
146
  }
118
147
  }
148
+ }
149
+
150
+ for (const dep of totalDeps) {
151
+ if (!dep.installed) {
152
+ notInstalledDeps.push(dep);
153
+ }
154
+ }
155
+
156
+ if (notInstalledDeps.length > 0) {
157
+ console.log(chalk.yellow(`\nāš ļø This mod has ${notInstalledDeps.length} dependencies which are not installed:`));
158
+ totalDeps.forEach(dep => {
159
+ if(dep.installed)
160
+ console.log(chalk.green(` - ${dep.title} (${dep.slug})`));
161
+ if(!dep.installed)
162
+ console.log(chalk.yellow(` - ${dep.title} (${dep.slug})`));
163
+ });
119
164
  console.log(chalk.gray('\nInstall dependencies with: clicraft install <slug>'));
120
165
  }
166
+
167
+ if (notInstalledDeps.length === 0) {
168
+ console.log(chalk.green('\nāœ… All dependencies are already installed.'));
169
+
170
+ }
121
171
  }
122
172
 
173
+ return true;
174
+
123
175
  } catch (error) {
124
176
  console.error(chalk.red('Error installing mod:'), error.message);
125
177
  if (options.verbose) {
126
178
  console.error(error);
127
179
  }
180
+ return false;
128
181
  }
129
-
130
- callPostCommandActions();
131
182
  }
132
183
 
133
184
  export default { installMod };
@@ -47,6 +47,8 @@ This is perfect for sharing modpack configurations or replicating setups across
47
47
  | Option | Description |
48
48
  |--------|-------------|
49
49
  | `--verbose` | Enable verbose output to see detailed progress |
50
+ | `--force, -f` | Overwrites existing instance folder if necessary |
51
+ | `--path, -p <path>` | Specifies path in which to install instance |
50
52
 
51
53
  ## šŸ’” Interactive Prompts
52
54
 
@@ -14,12 +14,12 @@ Install mods from Modrinth directly to your Minecraft instance.
14
14
  ## šŸ“ Synopsis
15
15
 
16
16
  ```bash
17
- clicraft install <mod> [options]
17
+ clicraft install <mods...> [options]
18
18
  ```
19
19
 
20
20
  ## šŸ“– Description
21
21
 
22
- The `install` command downloads and installs mods from Modrinth to your Minecraft instance. It automatically:
22
+ The `install` command downloads and installs one or more mods from Modrinth to your Minecraft instance. It automatically:
23
23
 
24
24
  1. Searches for the mod on Modrinth
25
25
  2. Finds the correct version for your Minecraft version and loader
@@ -31,7 +31,7 @@ The `install` command downloads and installs mods from Modrinth to your Minecraf
31
31
 
32
32
  | Argument | Description | Required |
33
33
  |----------|-------------|----------|
34
- | `<mod>` | Mod name or Modrinth project ID | Yes |
34
+ | `<mods...>` | One or more mod names or Modrinth project IDs | Yes (at least one) |
35
35
 
36
36
  ## šŸŽÆ Options
37
37
 
@@ -69,9 +69,13 @@ Use the project ID from `clicraft search` results.
69
69
  ### Install multiple mods
70
70
  ```bash
71
71
  cd my-instance
72
- clicraft install sodium
73
- clicraft install lithium
74
- clicraft install iris
72
+ clicraft install sodium lithium iris
73
+ ```
74
+
75
+ When installing multiple mods, you'll see a summary at the end:
76
+ ```
77
+ šŸ“Š Installation Summary:
78
+ āœ… 3 mod(s) installed successfully
75
79
  ```
76
80
 
77
81
  ### Verbose installation
@@ -130,33 +134,25 @@ instance-directory/
130
134
  ### Building a Performance Pack
131
135
  ```bash
132
136
  cd my-instance
133
- clicraft install sodium # Better FPS
134
- clicraft install lithium # Server optimization
135
- clicraft install starlight # Lighting engine
136
- clicraft install ferritecore # Memory optimization
137
+ clicraft install sodium lithium starlight ferritecore
137
138
  ```
138
139
 
139
140
  ### Adding Quality of Life Mods
140
141
  ```bash
141
142
  cd my-instance
142
- clicraft install "just enough items" # Recipe viewer
143
- clicraft install "journeymap" # Minimap
144
- clicraft install "appleskin" # Food info
143
+ clicraft install jei journeymap appleskin
145
144
  ```
146
145
 
147
146
  ### Installing Shader Support
148
147
  ```bash
149
148
  cd my-instance
150
- clicraft install sodium # Required for Iris
151
- clicraft install iris # Shader loader
149
+ clicraft install sodium iris
152
150
  ```
153
151
 
154
152
  ### Server Mods
155
153
  ```bash
156
154
  cd my-server
157
- clicraft install lithium
158
- clicraft install starlight
159
- clicraft install "fabric api"
155
+ clicraft install lithium starlight fabric-api
160
156
  ```
161
157
 
162
158
  ## āš ļø Important Notes
@@ -11,6 +11,7 @@ const versions = [
11
11
  '0.4.2',
12
12
  '0.4.3',
13
13
  '0.4.4',
14
+ '0.4.5',
14
15
  ];
15
16
 
16
17
 
package/index.js CHANGED
@@ -31,11 +31,13 @@ program
31
31
  .command('create')
32
32
  .description('Create a new Minecraft instance')
33
33
  .option('--verbose', 'Enable verbose output')
34
+ .option('-f, --force', 'Overwrite existing instance if it exists')
35
+ .option('-p, --path <path>', 'Path to create the instance in')
34
36
  .action(createInstance);
35
37
 
36
38
  program
37
- .command('install <mod>')
38
- .description('Install a mod to the current Minecraft instance')
39
+ .command('install <mods...>')
40
+ .description('Install one or more mods to the current Minecraft instance')
39
41
  .option('-i, --instance <path>', 'Path to the instance directory')
40
42
  .option('-f, --force', 'Force reinstall/update if already installed')
41
43
  .option('--verbose', 'Enable verbose output')
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bobschlowinskii/clicraft",
3
- "version": "0.4.4",
3
+ "version": "0.4.5",
4
4
  "description": "A simple Minecraft Mod Package Manager written in Node.JS",
5
5
  "main": "index.js",
6
6
  "type": "module",