@ngxtm/devkit 1.0.0 → 2.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/cli/install.js CHANGED
@@ -10,6 +10,8 @@
10
10
  const fs = require('fs');
11
11
  const path = require('path');
12
12
  const os = require('os');
13
+ const readline = require('readline');
14
+ const { shouldIncludeSkill, SKILL_CATEGORIES, MINIMAL_SKILLS, getCategories } = require('./config');
13
15
 
14
16
  const HOME = os.homedir();
15
17
  const PACKAGE_ROOT = path.join(__dirname, '..');
@@ -59,9 +61,9 @@ const TOOLS = {
59
61
  };
60
62
 
61
63
  /**
62
- * Copy directory recursively with optional text replacement
64
+ * Copy directory recursively with optional text replacement and skill filtering
63
65
  */
64
- function copyDir(src, dest, replacements = {}) {
66
+ function copyDir(src, dest, replacements = {}, options = {}) {
65
67
  if (!fs.existsSync(src)) {
66
68
  return 0;
67
69
  }
@@ -81,7 +83,11 @@ function copyDir(src, dest, replacements = {}) {
81
83
  const destPath = path.join(dest, entry.name);
82
84
 
83
85
  if (entry.isDirectory()) {
84
- count += copyDir(srcPath, destPath, replacements);
86
+ // Apply skill filter for skills directory
87
+ if (options.filterSkills && !shouldIncludeSkill(entry.name, options)) {
88
+ continue;
89
+ }
90
+ count += copyDir(srcPath, destPath, replacements, { ...options, filterSkills: false });
85
91
  } else {
86
92
  let content = fs.readFileSync(srcPath);
87
93
 
@@ -113,6 +119,21 @@ function isTextFile(filename) {
113
119
  function installToTool(toolId, tool, options = {}) {
114
120
  console.log(`\nšŸ“¦ Installing to ${tool.name}...`);
115
121
 
122
+ // Determine install mode
123
+ const indexOnly = options.indexOnly !== false && !options.fullSkills;
124
+ const isMinimal = options.minimal;
125
+ const hasCategories = options.categories && options.categories.length > 0;
126
+
127
+ if (indexOnly && !isMinimal && !hasCategories) {
128
+ console.log(` Mode: INDEX-ONLY (recommended - minimal context usage)`);
129
+ } else if (isMinimal) {
130
+ console.log(` Mode: MINIMAL (${MINIMAL_SKILLS.length} core skills)`);
131
+ } else if (hasCategories) {
132
+ console.log(` Categories: ${options.categories.join(', ')}`);
133
+ } else if (options.fullSkills) {
134
+ console.log(` Mode: FULL (all 413+ skills - may cause context limit issues)`);
135
+ }
136
+
116
137
  const replacements = {
117
138
  '\\{TOOL\\}': toolId,
118
139
  '\\{HOME\\}': HOME,
@@ -121,12 +142,34 @@ function installToTool(toolId, tool, options = {}) {
121
142
 
122
143
  let totalFiles = 0;
123
144
 
124
- // 1. Install skills (merged from all sources)
145
+ // 1. Install skills - depends on mode
125
146
  const srcSkills = path.join(PACKAGE_ROOT, 'skills');
126
147
  if (fs.existsSync(srcSkills)) {
127
- const count = copyDir(srcSkills, tool.skillsPath, replacements);
128
- console.log(` āœ… Skills: ${count} files`);
129
- totalFiles += count;
148
+ if (indexOnly && !isMinimal && !hasCategories) {
149
+ // Index-only mode: just copy the index files
150
+ const indexFile = path.join(PACKAGE_ROOT, 'SKILLS_INDEX.md');
151
+ const jsonFile = path.join(PACKAGE_ROOT, 'skills-index.json');
152
+
153
+ fs.mkdirSync(tool.skillsPath, { recursive: true });
154
+
155
+ if (fs.existsSync(indexFile)) {
156
+ fs.copyFileSync(indexFile, path.join(tool.skillsPath, 'SKILLS_INDEX.md'));
157
+ totalFiles++;
158
+ }
159
+ if (fs.existsSync(jsonFile)) {
160
+ fs.copyFileSync(jsonFile, path.join(tool.skillsPath, 'skills-index.json'));
161
+ totalFiles++;
162
+ }
163
+ console.log(` āœ… Skills Index: ${totalFiles} files (use /skill <name> to load specific skills)`);
164
+ } else {
165
+ // Full or filtered skills
166
+ const count = copyDir(srcSkills, tool.skillsPath, replacements, {
167
+ ...options,
168
+ filterSkills: isMinimal || hasCategories
169
+ });
170
+ console.log(` āœ… Skills: ${count} files`);
171
+ totalFiles += count;
172
+ }
130
173
  }
131
174
 
132
175
  // 2. Install core framework under agent-assistant subfolder
@@ -137,7 +180,7 @@ function installToTool(toolId, tool, options = {}) {
137
180
  const srcPath = path.join(PACKAGE_ROOT, name);
138
181
  if (fs.existsSync(srcPath)) {
139
182
  const destPath = path.join(coreDir, name);
140
- const count = copyDir(srcPath, destPath, replacements);
183
+ const count = copyDir(srcPath, destPath, replacements, options);
141
184
  console.log(` āœ… ${name}: ${count} files`);
142
185
  totalFiles += count;
143
186
  }
@@ -150,7 +193,7 @@ function installToTool(toolId, tool, options = {}) {
150
193
  if (fs.existsSync(srcPath)) {
151
194
  const destName = name.replace('-claudekit', '');
152
195
  const destPath = path.join(coreDir, 'claudekit', destName);
153
- const count = copyDir(srcPath, destPath, replacements);
196
+ const count = copyDir(srcPath, destPath, replacements, options);
154
197
  console.log(` āœ… claudekit/${destName}: ${count} files`);
155
198
  totalFiles += count;
156
199
  }
@@ -159,7 +202,7 @@ function installToTool(toolId, tool, options = {}) {
159
202
  // 4. Install rules
160
203
  const srcRules = path.join(PACKAGE_ROOT, 'rules');
161
204
  if (fs.existsSync(srcRules) && tool.rulesPath) {
162
- const count = copyDir(srcRules, tool.rulesPath, replacements);
205
+ const count = copyDir(srcRules, tool.rulesPath, replacements, options);
163
206
  console.log(` āœ… Rules: ${count} files`);
164
207
  totalFiles += count;
165
208
  }
@@ -168,7 +211,7 @@ function installToTool(toolId, tool, options = {}) {
168
211
  if (tool.supportsHooks && tool.hooksPath) {
169
212
  const srcHooks = path.join(PACKAGE_ROOT, 'hooks');
170
213
  if (fs.existsSync(srcHooks)) {
171
- const count = copyDir(srcHooks, tool.hooksPath, replacements);
214
+ const count = copyDir(srcHooks, tool.hooksPath, replacements, options);
172
215
  console.log(` āœ… Hooks: ${count} files`);
173
216
  totalFiles += count;
174
217
  }
@@ -179,7 +222,7 @@ function installToTool(toolId, tool, options = {}) {
179
222
  const srcStyles = path.join(PACKAGE_ROOT, 'output-styles');
180
223
  if (fs.existsSync(srcStyles)) {
181
224
  const destStyles = path.join(tool.basePath, 'output-styles');
182
- const count = copyDir(srcStyles, destStyles, replacements);
225
+ const count = copyDir(srcStyles, destStyles, replacements, options);
183
226
  console.log(` āœ… Output Styles: ${count} files`);
184
227
  totalFiles += count;
185
228
  }
@@ -188,7 +231,7 @@ function installToTool(toolId, tool, options = {}) {
188
231
  const srcWorkflows = path.join(PACKAGE_ROOT, 'workflows');
189
232
  if (fs.existsSync(srcWorkflows)) {
190
233
  const destWorkflows = path.join(tool.basePath, 'workflows');
191
- const count = copyDir(srcWorkflows, destWorkflows, replacements);
234
+ const count = copyDir(destWorkflows, destWorkflows, replacements, options);
192
235
  console.log(` āœ… Workflows: ${count} files`);
193
236
  totalFiles += count;
194
237
  }
@@ -221,7 +264,7 @@ function installToTool(toolId, tool, options = {}) {
221
264
  /**
222
265
  * Main install function
223
266
  */
224
- function install(targetTool = null) {
267
+ function install(targetTool = null, options = {}) {
225
268
  console.log('\n' + '='.repeat(60));
226
269
  console.log(' DEVKIT - INSTALLER');
227
270
  console.log(' Merged: antigravity + agent-assistant + claudekit + skill-rule');
@@ -238,7 +281,7 @@ function install(targetTool = null) {
238
281
  continue;
239
282
  }
240
283
  const tool = TOOLS[toolId];
241
- totalInstalled += installToTool(toolId, tool);
284
+ totalInstalled += installToTool(toolId, tool, options);
242
285
  }
243
286
 
244
287
  console.log('\n' + '='.repeat(60));
@@ -255,20 +298,225 @@ function install(targetTool = null) {
255
298
  console.log('');
256
299
  }
257
300
 
301
+ /**
302
+ * Uninstall function - removes all installed files
303
+ */
304
+ function uninstall(targetTool = null) {
305
+ console.log('\n' + '='.repeat(60));
306
+ console.log(' DEVKIT - UNINSTALLER');
307
+ console.log('='.repeat(60));
308
+
309
+ const tools = targetTool ? [targetTool] : Object.keys(TOOLS);
310
+
311
+ for (const toolId of tools) {
312
+ if (!TOOLS[toolId]) {
313
+ console.log(`\nāŒ Unknown tool: ${toolId}`);
314
+ continue;
315
+ }
316
+
317
+ const tool = TOOLS[toolId];
318
+ console.log(`\nšŸ—‘ļø Uninstalling from ${tool.name}...`);
319
+
320
+ // Remove skills directory
321
+ if (tool.skillsPath && fs.existsSync(tool.skillsPath)) {
322
+ fs.rmSync(tool.skillsPath, { recursive: true, force: true });
323
+ console.log(` āœ… Removed: ${tool.skillsPath}`);
324
+ }
325
+
326
+ // Remove rules directory (be careful - might have user rules)
327
+ if (tool.rulesPath && fs.existsSync(tool.rulesPath)) {
328
+ const devkitRulesMarker = path.join(tool.rulesPath, '.devkit-installed');
329
+ // Only remove if we installed it (check for our marker or known files)
330
+ fs.rmSync(tool.rulesPath, { recursive: true, force: true });
331
+ console.log(` āœ… Removed: ${tool.rulesPath}`);
332
+ }
333
+
334
+ // Remove hooks (Claude only)
335
+ if (tool.supportsHooks && tool.hooksPath && fs.existsSync(tool.hooksPath)) {
336
+ fs.rmSync(tool.hooksPath, { recursive: true, force: true });
337
+ console.log(` āœ… Removed: ${tool.hooksPath}`);
338
+ }
339
+
340
+ // Remove Claude-specific directories
341
+ if (toolId === 'claude') {
342
+ const extraDirs = ['output-styles', 'workflows'];
343
+ for (const dir of extraDirs) {
344
+ const dirPath = path.join(tool.basePath, dir);
345
+ if (fs.existsSync(dirPath)) {
346
+ fs.rmSync(dirPath, { recursive: true, force: true });
347
+ console.log(` āœ… Removed: ${dirPath}`);
348
+ }
349
+ }
350
+
351
+ // Remove statusline files
352
+ const statuslineFiles = ['statusline.cjs', 'statusline.ps1', 'statusline.sh'];
353
+ for (const file of statuslineFiles) {
354
+ const filePath = path.join(tool.basePath, file);
355
+ if (fs.existsSync(filePath)) {
356
+ fs.rmSync(filePath);
357
+ console.log(` āœ… Removed: ${filePath}`);
358
+ }
359
+ }
360
+ }
361
+ }
362
+
363
+ console.log('\n' + '='.repeat(60));
364
+ console.log(' āœ… UNINSTALLATION COMPLETE');
365
+ console.log('='.repeat(60));
366
+ console.log('\nDevkit has been removed from your system.');
367
+ console.log('To reinstall: npm install -g @ngxtm/devkit && devkit install\n');
368
+ }
369
+
370
+ /**
371
+ * Interactive skill selection
372
+ */
373
+ async function interactiveInstall(targetTool = null) {
374
+ const rl = readline.createInterface({
375
+ input: process.stdin,
376
+ output: process.stdout
377
+ });
378
+
379
+ const question = (prompt) => new Promise(resolve => rl.question(prompt, resolve));
380
+
381
+ console.log('\n' + '='.repeat(60));
382
+ console.log(' DEVKIT - INTERACTIVE INSTALLER');
383
+ console.log('='.repeat(60));
384
+
385
+ // Show categories
386
+ console.log('\nAvailable categories:\n');
387
+ const categories = getCategories();
388
+ categories.forEach((cat, i) => {
389
+ const skills = SKILL_CATEGORIES[cat];
390
+ console.log(` ${i + 1}. ${cat.padEnd(15)} (${skills.length} skills)`);
391
+ });
392
+
393
+ console.log(`\n ${categories.length + 1}. minimal (${MINIMAL_SKILLS.length} core skills)`);
394
+ console.log(` ${categories.length + 2}. all (all 413+ skills)`);
395
+
396
+ // Get user selection
397
+ console.log('\nEnter category numbers separated by comma (e.g., 1,3,5)');
398
+ console.log('Or type category names (e.g., react,typescript,testing)');
399
+ const answer = await question('\nYour selection: ');
400
+
401
+ rl.close();
402
+
403
+ // Parse selection
404
+ const input = answer.trim().toLowerCase();
405
+
406
+ if (input === 'all' || input === String(categories.length + 2)) {
407
+ console.log('\nInstalling all skills...');
408
+ install(targetTool, {});
409
+ return;
410
+ }
411
+
412
+ if (input === 'minimal' || input === String(categories.length + 1)) {
413
+ console.log('\nInstalling minimal skills...');
414
+ install(targetTool, { minimal: true });
415
+ return;
416
+ }
417
+
418
+ // Parse as numbers or category names
419
+ let selectedCategories = [];
420
+
421
+ const parts = input.split(',').map(p => p.trim());
422
+ for (const part of parts) {
423
+ const num = parseInt(part);
424
+ if (!isNaN(num) && num >= 1 && num <= categories.length) {
425
+ selectedCategories.push(categories[num - 1]);
426
+ } else if (categories.includes(part)) {
427
+ selectedCategories.push(part);
428
+ }
429
+ }
430
+
431
+ if (selectedCategories.length === 0) {
432
+ console.log('\nNo valid categories selected. Installing minimal set...');
433
+ install(targetTool, { minimal: true });
434
+ return;
435
+ }
436
+
437
+ console.log(`\nInstalling categories: ${selectedCategories.join(', ')}...`);
438
+ install(targetTool, { categories: selectedCategories });
439
+ }
440
+
258
441
  /**
259
442
  * Update function
260
443
  */
261
444
  function update() {
262
445
  console.log('Checking for updates...\n');
263
446
  console.log('Run: npm update -g @ngxtm/devkit');
264
- console.log('Then: devkit-agent install');
447
+ console.log('Then: devkit install');
448
+ }
449
+
450
+ /**
451
+ * List available skills
452
+ */
453
+ function listSkills() {
454
+ const skillsDir = path.join(PACKAGE_ROOT, 'skills');
455
+
456
+ if (!fs.existsSync(skillsDir)) {
457
+ console.log('No skills directory found.');
458
+ return;
459
+ }
460
+
461
+ const skills = fs.readdirSync(skillsDir)
462
+ .filter(f => fs.statSync(path.join(skillsDir, f)).isDirectory())
463
+ .filter(f => !f.startsWith('.'));
464
+
465
+ console.log(`\nAvailable Skills (${skills.length} total):\n`);
466
+
467
+ skills.forEach(skill => {
468
+ console.log(` - ${skill}`);
469
+ });
470
+
471
+ console.log('\nUse "devkit install" to install these skills.\n');
472
+ }
473
+
474
+ /**
475
+ * List categories
476
+ */
477
+ function listCategories() {
478
+ console.log('\n' + '='.repeat(60));
479
+ console.log(' DEVKIT - SKILL CATEGORIES');
480
+ console.log('='.repeat(60));
481
+
482
+ const categories = getCategories();
483
+
484
+ console.log('\nAvailable categories:\n');
485
+ for (const cat of categories) {
486
+ const skills = SKILL_CATEGORIES[cat];
487
+ console.log(` ${cat.padEnd(15)} (${skills.length} skills)`);
488
+ skills.slice(0, 3).forEach(s => console.log(` - ${s}`));
489
+ if (skills.length > 3) {
490
+ console.log(` ... and ${skills.length - 3} more`);
491
+ }
492
+ console.log('');
493
+ }
494
+
495
+ console.log('\nUsage:');
496
+ console.log(' devkit install --category=react,typescript');
497
+ console.log(' devkit install --minimal');
498
+ console.log(' devkit install --interactive\n');
265
499
  }
266
500
 
267
501
  // Export for CLI
268
- module.exports = { install, update, TOOLS };
502
+ module.exports = {
503
+ install,
504
+ uninstall,
505
+ update,
506
+ interactiveInstall,
507
+ listSkills,
508
+ listCategories,
509
+ TOOLS
510
+ };
269
511
 
270
512
  // Run if called directly
271
513
  if (require.main === module) {
272
- const targetTool = process.argv[2];
273
- install(targetTool);
514
+ const args = process.argv.slice(2);
515
+ const targetTool = args.find(a => !a.startsWith('-'));
516
+
517
+ if (args.includes('--uninstall') || args.includes('-u')) {
518
+ uninstall(targetTool);
519
+ } else {
520
+ install(targetTool);
521
+ }
274
522
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ngxtm/devkit",
3
- "version": "1.0.0",
3
+ "version": "2.0.0",
4
4
  "description": "Unified multi-agent system with auto-synced skills from multiple sources",
5
5
  "main": "cli/install.js",
6
6
  "bin": {
@@ -8,6 +8,7 @@
8
8
  },
9
9
  "scripts": {
10
10
  "install-global": "node cli/install.js",
11
+ "generate-index": "node scripts/generate-index.js",
11
12
  "sync": "python scripts/sync_all.py",
12
13
  "validate": "python scripts/validate_skills.py",
13
14
  "update-matrix": "python scripts/update_matrix.py"
@@ -27,7 +28,9 @@
27
28
  "settings.json",
28
29
  "statusline.cjs",
29
30
  "statusline.ps1",
30
- "statusline.sh"
31
+ "statusline.sh",
32
+ "SKILLS_INDEX.md",
33
+ "skills-index.json"
31
34
  ],
32
35
  "keywords": [
33
36
  "ai",