@brookmind/ai-toolkit 1.0.0 → 1.0.1

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.
Files changed (3) hide show
  1. package/bin/cli.js +8 -1
  2. package/package.json +1 -1
  3. package/src/index.js +252 -68
package/bin/cli.js CHANGED
@@ -2,4 +2,11 @@
2
2
 
3
3
  import { run } from '../src/index.js';
4
4
 
5
- run().catch(console.error);
5
+ run().catch((error) => {
6
+ if (error.name === 'ExitPromptError') {
7
+ console.log('\nInstallation cancelled.');
8
+ process.exit(0);
9
+ }
10
+ console.error(error);
11
+ process.exit(1);
12
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@brookmind/ai-toolkit",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "description": "AI Toolkit installer for Claude Code and OpenCode - agents, skills, and MCPs",
5
5
  "type": "module",
6
6
  "main": "src/index.js",
package/src/index.js CHANGED
@@ -1,8 +1,7 @@
1
- import { select, checkbox, confirm } from '@inquirer/prompts';
1
+ import { select, checkbox } from '@inquirer/prompts';
2
2
  import chalk from 'chalk';
3
3
  import ora from 'ora';
4
- import { readdir, cp, mkdir } from 'fs/promises';
5
- import { existsSync } from 'fs';
4
+ import { readdir, cp, mkdir, access } from 'fs/promises';
6
5
  import { join, dirname } from 'path';
7
6
  import { fileURLToPath } from 'url';
8
7
  import { homedir } from 'os';
@@ -30,16 +29,41 @@ async function getFiles(path, extension) {
30
29
  }
31
30
  }
32
31
 
32
+ async function exists(path) {
33
+ try {
34
+ await access(path);
35
+ return true;
36
+ } catch {
37
+ return false;
38
+ }
39
+ }
40
+
41
+ const BACK = '__BACK__';
42
+ const CONTINUE = '__CONTINUE__';
43
+
44
+ function createChoices(items, includeBack = false) {
45
+ const choices = items.map(item => ({
46
+ name: item,
47
+ value: item,
48
+ }));
49
+
50
+ choices.push({ name: chalk.green('─→ Continue'), value: CONTINUE });
51
+
52
+ if (includeBack) {
53
+ choices.push({ name: chalk.yellow('←─ Back'), value: BACK });
54
+ }
55
+
56
+ return choices;
57
+ }
58
+
33
59
  export async function run() {
34
60
  console.clear();
35
61
 
36
62
  // Header
37
63
  console.log('');
38
- console.log(chalk.magenta('╔═══════════════════════════════════════════════════╗'));
39
- console.log(chalk.magenta('║ ║'));
40
- console.log(chalk.magenta('') + chalk.white.bold(' CKW AI Toolkit Installer ') + chalk.magenta('║'));
41
- console.log(chalk.magenta('║ ║'));
42
- console.log(chalk.magenta('╚═══════════════════════════════════════════════════╝'));
64
+ console.log(chalk.magenta('╔══════════════════════════════════╗'));
65
+ console.log(chalk.magenta('║') + chalk.white.bold(' CKW AI Toolkit Installer ') + chalk.magenta('║'));
66
+ console.log(chalk.magenta('╚══════════════════════════════════╝'));
43
67
  console.log('');
44
68
 
45
69
  // Get available items
@@ -47,70 +71,230 @@ export async function run() {
47
71
  const skills = await getDirectories(join(ROOT_DIR, 'skills'));
48
72
  const mcps = await getDirectories(join(ROOT_DIR, 'mcps'));
49
73
 
50
- // Step 1: Platform
51
- console.log(chalk.cyan('Step 1/4: Platform'));
52
- const platform = await select({
53
- message: 'Where do you want to install?',
54
- choices: [
55
- { name: 'Claude Code', value: 'claude' },
56
- { name: 'OpenCode', value: 'opencode' },
57
- { name: 'Both', value: 'both' }
58
- ]
59
- });
60
-
61
- // Step 2: Agents
62
- console.log('');
63
- console.log(chalk.cyan('Step 2/4: Agents'));
64
- const selectedAgents = agents.length > 0
65
- ? await checkbox({
66
- message: 'Select agents to install',
67
- choices: agents.map(a => ({ name: a, value: a }))
68
- })
69
- : [];
70
-
71
- // Step 3: Skills
72
- console.log('');
73
- console.log(chalk.cyan('Step 3/4: Skills'));
74
- const selectedSkills = skills.length > 0
75
- ? await checkbox({
76
- message: 'Select skills to install',
77
- choices: skills.map(s => ({ name: s, value: s }))
78
- })
79
- : [];
80
-
81
- // Step 4: MCPs
82
- console.log('');
83
- console.log(chalk.cyan('Step 4/4: MCPs'));
84
- const selectedMcps = mcps.length > 0
85
- ? await checkbox({
86
- message: 'Select MCPs to install',
87
- choices: mcps.map(m => ({ name: m, value: m }))
88
- })
89
- : [];
90
-
91
- // Summary
92
- console.log('');
93
- console.log(chalk.yellow('Summary'));
94
- console.log('');
95
- console.log(` Platform: ${chalk.white(platform)}`);
96
- console.log(` Agents: ${chalk.white(selectedAgents.length > 0 ? selectedAgents.join(', ') : 'none')}`);
97
- console.log(` Skills: ${chalk.white(selectedSkills.length > 0 ? selectedSkills.join(', ') : 'none')}`);
98
- console.log(` MCPs: ${chalk.white(selectedMcps.length > 0 ? selectedMcps.join(', ') : 'none')}`);
99
- console.log('');
74
+ // State
75
+ let step = 1;
76
+ let platform = null;
77
+ let selectedAgents = [];
78
+ let selectedSkills = [];
79
+ let selectedMcps = [];
100
80
 
101
- const proceed = await confirm({ message: 'Proceed with installation?' });
81
+ const home = homedir();
102
82
 
103
- if (!proceed) {
104
- console.log(chalk.yellow('Installation cancelled.'));
105
- return;
83
+ // Navigation loop
84
+ while (step <= 5) {
85
+
86
+ if (step === 1) {
87
+ // Step 1: Platform
88
+ console.log(chalk.cyan('Step 1/5: Platform'));
89
+ console.log('');
90
+
91
+ platform = await select({
92
+ message: 'Where do you want to install?',
93
+ choices: [
94
+ { name: 'Claude Code', value: 'claude' },
95
+ { name: 'OpenCode', value: 'opencode' },
96
+ { name: 'Both', value: 'both' },
97
+ { name: chalk.yellow('←─ Cancel'), value: BACK }
98
+ ]
99
+ });
100
+
101
+ if (platform === BACK) {
102
+ console.log(chalk.yellow('Installation cancelled.'));
103
+ return;
104
+ }
105
+
106
+ step = 2;
107
+ }
108
+
109
+ else if (step === 2) {
110
+ // Step 2: Agents
111
+ console.log('');
112
+ console.log(chalk.cyan('Step 2/5: Agents'));
113
+ console.log(chalk.dim('Select items with space, then choose Continue'));
114
+ console.log('');
115
+
116
+ if (agents.length > 0) {
117
+ // Check which agents already exist
118
+ const agentChoices = [];
119
+ for (const agent of agents) {
120
+ let alreadyInstalled = false;
121
+
122
+ if (platform === 'claude' || platform === 'both') {
123
+ if (await exists(join(home, '.claude', 'agents', `${agent}.md`))) {
124
+ alreadyInstalled = true;
125
+ }
126
+ }
127
+ if (platform === 'opencode' || platform === 'both') {
128
+ if (await exists(join(home, '.config', 'opencode', 'agent', `${agent}.md`))) {
129
+ alreadyInstalled = true;
130
+ }
131
+ }
132
+
133
+ agentChoices.push({
134
+ name: alreadyInstalled ? `${agent} ${chalk.yellow('(installed)')}` : agent,
135
+ value: agent,
136
+ });
137
+ }
138
+
139
+ agentChoices.push({ name: chalk.green('─→ Continue'), value: CONTINUE });
140
+ agentChoices.push({ name: chalk.yellow('←─ Back'), value: BACK });
141
+
142
+ const result = await checkbox({
143
+ message: 'Select agents:',
144
+ choices: agentChoices,
145
+ theme: {
146
+ icon: {
147
+ checked: chalk.green('[✓]'),
148
+ unchecked: '[ ]',
149
+ cursor: '→'
150
+ }
151
+ }
152
+ });
153
+
154
+ if (result.includes(BACK)) {
155
+ step = 1;
156
+ continue;
157
+ }
158
+
159
+ selectedAgents = result.filter(r => r !== CONTINUE && r !== BACK);
160
+
161
+ if (!result.includes(CONTINUE)) {
162
+ continue;
163
+ }
164
+ }
165
+
166
+ step = 3;
167
+ }
168
+
169
+ else if (step === 3) {
170
+ // Step 3: Skills
171
+ console.log('');
172
+ console.log(chalk.cyan('Step 3/5: Skills'));
173
+ console.log(chalk.dim('Select items with space, then choose Continue'));
174
+ console.log('');
175
+
176
+ if (skills.length > 0) {
177
+ // Check which skills already exist
178
+ const skillChoices = [];
179
+ for (const skill of skills) {
180
+ let alreadyInstalled = false;
181
+
182
+ if (platform === 'claude' || platform === 'both') {
183
+ if (await exists(join(home, '.claude', 'skills', skill))) {
184
+ alreadyInstalled = true;
185
+ }
186
+ }
187
+
188
+ skillChoices.push({
189
+ name: alreadyInstalled ? `${skill} ${chalk.yellow('(installed)')}` : skill,
190
+ value: skill,
191
+ });
192
+ }
193
+
194
+ skillChoices.push({ name: chalk.green('─→ Continue'), value: CONTINUE });
195
+ skillChoices.push({ name: chalk.yellow('←─ Back'), value: BACK });
196
+
197
+ const result = await checkbox({
198
+ message: 'Select skills:',
199
+ choices: skillChoices,
200
+ theme: {
201
+ icon: {
202
+ checked: chalk.green('[✓]'),
203
+ unchecked: '[ ]',
204
+ cursor: '→'
205
+ }
206
+ }
207
+ });
208
+
209
+ if (result.includes(BACK)) {
210
+ step = 2;
211
+ continue;
212
+ }
213
+
214
+ selectedSkills = result.filter(r => r !== CONTINUE && r !== BACK);
215
+
216
+ if (!result.includes(CONTINUE)) {
217
+ continue;
218
+ }
219
+ }
220
+
221
+ step = 4;
222
+ }
223
+
224
+ else if (step === 4) {
225
+ // Step 4: MCPs
226
+ console.log('');
227
+ console.log(chalk.cyan('Step 4/5: MCPs'));
228
+ console.log(chalk.dim('Select items with space, then choose Continue'));
229
+ console.log('');
230
+
231
+ if (mcps.length > 0) {
232
+ const result = await checkbox({
233
+ message: 'Select MCPs:',
234
+ choices: createChoices(mcps, true),
235
+ theme: {
236
+ icon: {
237
+ checked: chalk.green('[✓]'),
238
+ unchecked: '[ ]',
239
+ cursor: '→'
240
+ }
241
+ }
242
+ });
243
+
244
+ if (result.includes(BACK)) {
245
+ step = 3;
246
+ continue;
247
+ }
248
+
249
+ selectedMcps = result.filter(r => r !== CONTINUE && r !== BACK);
250
+
251
+ if (!result.includes(CONTINUE)) {
252
+ continue;
253
+ }
254
+ }
255
+
256
+ step = 5;
257
+ }
258
+
259
+ else if (step === 5) {
260
+ // Step 5: Summary & Confirm
261
+ console.log('');
262
+ console.log(chalk.yellow('Summary'));
263
+ console.log('');
264
+ console.log(` Platform: ${chalk.white(platform)}`);
265
+ console.log(` Agents: ${chalk.white(selectedAgents.length > 0 ? selectedAgents.join(', ') : 'none')}`);
266
+ console.log(` Skills: ${chalk.white(selectedSkills.length > 0 ? selectedSkills.join(', ') : 'none')}`);
267
+ console.log(` MCPs: ${chalk.white(selectedMcps.length > 0 ? selectedMcps.join(', ') : 'none')}`);
268
+ console.log('');
269
+
270
+ const action = await select({
271
+ message: 'What would you like to do?',
272
+ choices: [
273
+ { name: chalk.green('✓ Install'), value: 'install' },
274
+ { name: chalk.yellow('←─ Back'), value: BACK },
275
+ { name: chalk.red('✗ Cancel'), value: 'cancel' }
276
+ ]
277
+ });
278
+
279
+ if (action === BACK) {
280
+ step = 4;
281
+ continue;
282
+ }
283
+
284
+ if (action === 'cancel') {
285
+ console.log(chalk.yellow('Installation cancelled.'));
286
+ return;
287
+ }
288
+
289
+ // Proceed with installation
290
+ break;
291
+ }
106
292
  }
107
293
 
108
294
  // Installation
109
295
  console.log('');
110
296
  const spinner = ora('Installing...').start();
111
297
 
112
- const home = homedir();
113
-
114
298
  async function installClaude() {
115
299
  const claudeDir = join(home, '.claude');
116
300
  const agentsDir = join(claudeDir, 'agents');
@@ -128,7 +312,7 @@ export async function run() {
128
312
  for (const skill of selectedSkills) {
129
313
  const src = join(ROOT_DIR, 'skills', skill);
130
314
  const dest = join(skillsDir, skill);
131
- await cp(src, dest, { recursive: true });
315
+ await cp(src, dest, { recursive: true, force: true });
132
316
  }
133
317
  }
134
318
 
@@ -164,9 +348,9 @@ export async function run() {
164
348
 
165
349
  // Done
166
350
  console.log('');
167
- console.log(chalk.green('╔═══════════════════════════════════════════════════╗'));
168
- console.log(chalk.green('║') + chalk.white.bold(' ✓ Installation Complete! ') + chalk.green('║'));
169
- console.log(chalk.green('╚═══════════════════════════════════════════════════╝'));
351
+ console.log(chalk.green('╔══════════════════════════════════╗'));
352
+ console.log(chalk.green('║ ✓ Installation Complete! ║'));
353
+ console.log(chalk.green('╚══════════════════════════════════╝'));
170
354
 
171
355
  if (selectedMcps.length > 0) {
172
356
  console.log('');