@ngxtm/devkit 3.4.1 → 3.6.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/index.js CHANGED
@@ -41,6 +41,9 @@ function parseArgs(args) {
41
41
  help: false,
42
42
  installed: false,
43
43
  ruleArgs: [],
44
+ // New v3.6 options
45
+ all: false,
46
+ tools: [],
44
47
  // Legacy options (deprecated)
45
48
  tool: null,
46
49
  minimal: false,
@@ -62,10 +65,14 @@ function parseArgs(args) {
62
65
  options.clean = true;
63
66
  } else if (arg === '--installed' || arg === '-i') {
64
67
  options.installed = true;
68
+ } else if (arg === '--all' || arg === '-a') {
69
+ options.all = true;
65
70
  } else if (arg === '--status' || arg === '-s') {
66
71
  options.command = 'status';
67
72
  } else if (arg.startsWith('--path=')) {
68
73
  options.path = arg.split('=')[1];
74
+ } else if (arg.startsWith('--tools=')) {
75
+ options.tools = arg.split('=')[1].split(',').map(t => t.trim());
69
76
  } else if (arg.startsWith('--')) {
70
77
  // Handle legacy options for backwards compatibility
71
78
  if (arg === '--minimal' || arg === '-m') options.minimal = true;
@@ -93,9 +100,9 @@ USAGE:
93
100
  devkit <command> [options]
94
101
 
95
102
  COMMANDS:
96
- init Initialize devkit in current project (.claude/ folder)
97
- Auto-detects tech stack and installs relevant rules only.
98
- This is the PRIMARY command - use this for new projects.
103
+ init Initialize devkit in current project
104
+ Shows interactive tool selection menu.
105
+ Auto-detects tech stack and installs relevant rules.
99
106
 
100
107
  update Update existing installation
101
108
  Re-detects project type and updates rules accordingly.
@@ -124,23 +131,34 @@ COMMANDS:
124
131
 
125
132
  OPTIONS:
126
133
  --force, -f Force overwrite existing installation
134
+ --all, -a Install for all supported tools (skip menu)
135
+ --tools=LIST Install for specific tools (comma-separated)
136
+ Example: --tools=claude,cursor
127
137
  --clean, -c Remove rules for technologies no longer detected (with update)
128
138
  --installed, -i Show only installed rules (with rules command)
129
139
  --path=DIR Specify project directory (default: current directory)
130
140
  --help, -h Show this help
131
141
 
142
+ SUPPORTED TOOLS:
143
+ claude Claude Code
144
+ cursor Cursor
145
+ copilot GitHub Copilot
146
+ gemini Gemini CLI
147
+
132
148
  EXAMPLES:
133
- devkit init # Initialize in current project
134
- devkit init --force # Overwrite existing installation
135
- devkit update # Update and re-detect technologies
136
- devkit update --clean # Update and remove old rules
137
- devkit detect # Show what would be detected
138
- devkit rules # List all available rules
139
- devkit rules --installed # Show installed rules
140
- devkit add golang docker # Add golang and docker rules
141
- devkit remove flutter # Remove flutter rule
142
- devkit status # Show current installation
143
- devkit uninstall # Remove from current project
149
+ devkit init # Interactive tool selection
150
+ devkit init --all # Install for all tools
151
+ devkit init --tools=claude,cursor # Install for specific tools
152
+ devkit init --force # Overwrite existing installation
153
+ devkit update # Update and re-detect technologies
154
+ devkit update --clean # Update and remove old rules
155
+ devkit detect # Show what would be detected
156
+ devkit rules # List all available rules
157
+ devkit rules --installed # Show installed rules
158
+ devkit add golang docker # Add golang and docker rules
159
+ devkit remove flutter # Remove flutter rule
160
+ devkit status # Show current installation
161
+ devkit uninstall # Remove from current project
144
162
 
145
163
  HOW IT WORKS:
146
164
  1. devkit init analyzes your project files:
@@ -181,13 +199,15 @@ This auto-detects your tech stack and installs only relevant rules.
181
199
 
182
200
  // Command handlers
183
201
  const commands = {
184
- // Primary command - per-project init
185
- init: (options) => {
202
+ // Primary command - per-project init (now async with tool selection)
203
+ init: async (options) => {
186
204
  const projectPath = validatePath(options.path) || process.cwd();
187
205
  return initProject({
188
206
  path: projectPath,
189
207
  force: options.force,
190
- update: options.update
208
+ update: options.update,
209
+ all: options.all,
210
+ tools: options.tools
191
211
  });
192
212
  },
193
213
 
package/cli/init.js CHANGED
@@ -3,167 +3,280 @@
3
3
  /**
4
4
  * Devkit Init - Per-Project Installation
5
5
  *
6
- * Installs devkit to the current project's .claude/ directory with:
7
- * - Merged commands (from both agent-assistant and claudekit)
6
+ * Installs devkit to the current project with support for multiple AI tools:
7
+ * - Claude Code, Cursor, GitHub Copilot, Gemini CLI
8
+ * - Interactive tool selection with auto-detection
8
9
  * - Tech-specific rules (based on project detection)
9
- * - Skills index (for on-demand loading)
10
- * - Essential hooks
10
+ * - Merged commands and essential hooks
11
11
  */
12
12
 
13
13
  const fs = require('fs');
14
14
  const path = require('path');
15
15
 
16
- const { detectProjectType, getRulesForTypes, printDetectionResults } = require('./detect');
17
- const { copyDir, getDirSize, validatePath } = require('./utils');
16
+ const { detectProjectType, getRulesForTypes } = require('./detect');
17
+ const { copyDir, getDirSize, detectInstalledTools, TOOLS } = require('./utils');
18
18
 
19
19
  const VERSION = require('../package.json').version;
20
20
  const PACKAGE_ROOT = path.join(__dirname, '..');
21
21
 
22
22
  /**
23
- * Initialize devkit in a project directory
23
+ * Show interactive tool selection menu
24
+ * @param {Object} detectedTools - Results from detectInstalledTools()
25
+ * @returns {Promise<string[]>} - Array of selected tool ids
24
26
  */
25
- function initProject(options = {}) {
26
- const projectDir = options.path || process.cwd();
27
- const claudeDir = path.join(projectDir, '.claude');
28
- const isUpdate = options.update || false;
29
-
30
- console.log('\n' + '='.repeat(60));
31
- console.log(' DEVKIT v' + VERSION + (isUpdate ? ' - UPDATE' : ' - INIT'));
32
- console.log('='.repeat(60));
27
+ async function showToolSelectionMenu(detectedTools) {
28
+ // Dynamic import for inquirer (ES module)
29
+ const inquirer = (await import('inquirer')).default;
30
+
31
+ console.log('\n Detecting installed AI tools...\n');
32
+
33
+ const choices = Object.entries(TOOLS).map(([id, tool]) => {
34
+ const detected = detectedTools[id]?.detected;
35
+ const status = detected ? '(detected)' : '(not detected)';
36
+ return {
37
+ name: `${tool.name} ${status}`,
38
+ value: id,
39
+ checked: detected // Pre-select detected tools
40
+ };
41
+ });
42
+
43
+ const { selectedTools } = await inquirer.prompt([
44
+ {
45
+ type: 'checkbox',
46
+ name: 'selectedTools',
47
+ message: 'Select AI tools to install devkit for:',
48
+ choices,
49
+ validate: (answer) => {
50
+ if (answer.length === 0) {
51
+ return 'Please select at least one tool.';
52
+ }
53
+ return true;
54
+ }
55
+ }
56
+ ]);
33
57
 
34
- // Check if .claude already exists
35
- if (fs.existsSync(claudeDir) && !isUpdate && !options.force) {
36
- console.log(`\n .claude/ folder already exists.`);
37
- console.log(' Use --force to overwrite or --update to update.\n');
38
- return { success: false, reason: 'exists' };
39
- }
58
+ return selectedTools;
59
+ }
40
60
 
41
- // 1. Detect project type
42
- console.log('\n Detecting project type...');
43
- const detectedTypes = detectProjectType(projectDir);
44
- const rulesToInstall = getRulesForTypes(detectedTypes);
61
+ /**
62
+ * Install devkit for a single tool
63
+ * @param {string} toolId - Tool identifier
64
+ * @param {Object} tool - Tool configuration
65
+ * @param {string} projectDir - Project directory
66
+ * @param {Object} options - Install options
67
+ * @returns {Object} - Installation result
68
+ */
69
+ function installForTool(toolId, tool, projectDir, options = {}) {
70
+ const targetDir = path.join(projectDir, tool.projectPath);
71
+ const isUpdate = options.update || false;
45
72
 
46
- if (detectedTypes.length > 0) {
47
- console.log(` Detected: ${detectedTypes.join(', ')}`);
48
- console.log(` Rules: ${rulesToInstall.join(', ')}`);
49
- } else {
50
- console.log(' No specific technology detected.');
51
- console.log(' Installing base commands only.');
73
+ // Check if already exists
74
+ if (fs.existsSync(targetDir) && !isUpdate && !options.force) {
75
+ return {
76
+ success: false,
77
+ reason: 'exists',
78
+ message: `${tool.projectPath}/ already exists`
79
+ };
52
80
  }
53
81
 
54
- // Create .claude directory
55
- fs.mkdirSync(claudeDir, { recursive: true });
82
+ // Create target directory
83
+ fs.mkdirSync(targetDir, { recursive: true });
56
84
 
57
85
  let totalFiles = 0;
58
86
  const stats = {};
59
87
 
60
- // 2. Install merged commands
61
- console.log('\n Installing components...');
62
- const mergedCommandsDir = path.join(PACKAGE_ROOT, 'merged-commands');
63
- const commandsDir = path.join(claudeDir, 'commands');
64
-
65
- if (fs.existsSync(mergedCommandsDir)) {
66
- const count = copyDir(mergedCommandsDir, commandsDir);
67
- stats.commands = count;
68
- totalFiles += count;
69
- console.log(` Commands: ${count} files`);
70
- } else {
71
- // Fallback to commands-claudekit if merged not available
72
- const fallbackDir = path.join(PACKAGE_ROOT, 'commands-claudekit');
73
- if (fs.existsSync(fallbackDir)) {
74
- const count = copyDir(fallbackDir, commandsDir);
88
+ // 1. Install commands (if tool supports it)
89
+ if (tool.commandsPath) {
90
+ const mergedCommandsDir = path.join(PACKAGE_ROOT, 'merged-commands');
91
+ const commandsDir = path.join(targetDir, tool.commandsPath);
92
+
93
+ if (fs.existsSync(mergedCommandsDir)) {
94
+ const count = copyDir(mergedCommandsDir, commandsDir);
75
95
  stats.commands = count;
76
96
  totalFiles += count;
77
- console.log(` Commands (claudekit): ${count} files`);
78
97
  }
79
98
  }
80
99
 
81
- // 3. Install tech-specific rules
82
- const rulesDir = path.join(claudeDir, 'rules');
83
- let rulesCount = 0;
84
-
85
- for (const ruleType of rulesToInstall) {
86
- const srcRulesDir = path.join(PACKAGE_ROOT, 'templates', ruleType, 'rules');
87
- if (fs.existsSync(srcRulesDir)) {
88
- const destRulesDir = path.join(rulesDir, ruleType);
89
- const count = copyDir(srcRulesDir, destRulesDir);
90
- rulesCount += count;
100
+ // 2. Install rules
101
+ if (tool.rulesPath && options.rules && options.rules.length > 0) {
102
+ const rulesDir = path.join(targetDir, tool.rulesPath);
103
+ let rulesCount = 0;
104
+
105
+ for (const ruleType of options.rules) {
106
+ const srcRulesDir = path.join(PACKAGE_ROOT, 'templates', ruleType, 'rules');
107
+ if (fs.existsSync(srcRulesDir)) {
108
+ const destRulesDir = path.join(rulesDir, ruleType);
109
+ const count = copyDir(srcRulesDir, destRulesDir);
110
+ rulesCount += count;
111
+ }
91
112
  }
92
- }
93
113
 
94
- if (rulesCount > 0) {
95
- stats.rules = rulesCount;
96
- totalFiles += rulesCount;
97
- console.log(` Rules: ${rulesCount} files (${rulesToInstall.join(', ')})`);
114
+ if (rulesCount > 0) {
115
+ stats.rules = rulesCount;
116
+ totalFiles += rulesCount;
117
+ }
98
118
  }
99
119
 
100
- // 4. Install essential hooks
101
- const srcHooksDir = path.join(PACKAGE_ROOT, 'templates', 'base', 'hooks');
102
- const hooksDir = path.join(claudeDir, 'hooks');
120
+ // 3. Install hooks (if tool supports it)
121
+ if (tool.supportsHooks && tool.hooksPath) {
122
+ const srcHooksDir = path.join(PACKAGE_ROOT, 'templates', 'base', 'hooks');
123
+ const hooksDir = path.join(targetDir, tool.hooksPath);
103
124
 
104
- if (fs.existsSync(srcHooksDir)) {
105
- const count = copyDir(srcHooksDir, hooksDir);
106
- stats.hooks = count;
107
- totalFiles += count;
108
- console.log(` Hooks: ${count} files`);
109
- } else {
110
- // Fallback to main hooks directory (essential only)
111
- const fallbackHooks = path.join(PACKAGE_ROOT, 'hooks');
112
- if (fs.existsSync(fallbackHooks)) {
113
- const count = copyDir(fallbackHooks, hooksDir);
125
+ if (fs.existsSync(srcHooksDir)) {
126
+ const count = copyDir(srcHooksDir, hooksDir);
114
127
  stats.hooks = count;
115
128
  totalFiles += count;
116
- console.log(` Hooks: ${count} files`);
129
+ } else {
130
+ const fallbackHooks = path.join(PACKAGE_ROOT, 'hooks');
131
+ if (fs.existsSync(fallbackHooks)) {
132
+ const count = copyDir(fallbackHooks, hooksDir);
133
+ stats.hooks = count;
134
+ totalFiles += count;
135
+ }
117
136
  }
118
137
  }
119
138
 
120
- // 5. Install skills index
139
+ // 4. Install skills index
121
140
  const skillsIndexSrc = path.join(PACKAGE_ROOT, 'skills-index.json');
122
- const skillsIndexDest = path.join(claudeDir, 'skills-index.json');
123
-
124
141
  if (fs.existsSync(skillsIndexSrc)) {
125
- fs.copyFileSync(skillsIndexSrc, skillsIndexDest);
142
+ fs.copyFileSync(skillsIndexSrc, path.join(targetDir, 'skills-index.json'));
126
143
  totalFiles++;
127
- console.log(` Skills Index: 1 file`);
128
144
  }
129
145
 
130
- // 6. Create devkit.json tracking file
146
+ // 5. Create devkit.json tracking file
131
147
  const devkitConfig = {
132
148
  version: VERSION,
133
- detectedTypes: detectedTypes,
134
- installedRules: rulesToInstall,
149
+ tool: toolId,
150
+ toolName: tool.name,
151
+ detectedTypes: options.detectedTypes || [],
152
+ installedRules: options.rules || [],
135
153
  installedAt: new Date().toISOString(),
136
154
  updatedAt: isUpdate ? new Date().toISOString() : null,
137
155
  stats: {
138
156
  totalFiles: totalFiles,
139
- sizeKB: Math.round(getDirSize(claudeDir) / 1024)
157
+ sizeKB: Math.round(getDirSize(targetDir) / 1024)
140
158
  }
141
159
  };
142
160
 
143
161
  fs.writeFileSync(
144
- path.join(claudeDir, 'devkit.json'),
162
+ path.join(targetDir, 'devkit.json'),
145
163
  JSON.stringify(devkitConfig, null, 2)
146
164
  );
147
165
  totalFiles++;
148
166
 
149
- // 7. Create settings.json if not exists
150
- const settingsPath = path.join(claudeDir, 'settings.json');
151
- if (!fs.existsSync(settingsPath)) {
152
- const settings = {
153
- includeCoAuthoredBy: false
154
- };
155
- fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2));
156
- totalFiles++;
167
+ // 6. Create settings.json if not exists (for tools that use it)
168
+ if (toolId === 'claude') {
169
+ const settingsPath = path.join(targetDir, 'settings.json');
170
+ if (!fs.existsSync(settingsPath)) {
171
+ const settings = { includeCoAuthoredBy: false };
172
+ fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2));
173
+ totalFiles++;
174
+ }
157
175
  }
158
176
 
159
- // Calculate total size
160
- const totalSizeKB = Math.round(getDirSize(claudeDir) / 1024);
177
+ return {
178
+ success: true,
179
+ tool: toolId,
180
+ toolName: tool.name,
181
+ path: targetDir,
182
+ stats: {
183
+ files: totalFiles,
184
+ sizeKB: Math.round(getDirSize(targetDir) / 1024),
185
+ ...stats
186
+ }
187
+ };
188
+ }
189
+
190
+ /**
191
+ * Initialize devkit in a project directory
192
+ * @param {Object} options - Installation options
193
+ */
194
+ async function initProject(options = {}) {
195
+ const projectDir = options.path || process.cwd();
196
+ const isUpdate = options.update || false;
197
+
198
+ console.log('\n' + '='.repeat(60));
199
+ console.log(' DEVKIT v' + VERSION + (isUpdate ? ' - UPDATE' : ' - INIT'));
200
+ console.log('='.repeat(60));
201
+
202
+ // Determine which tools to install
203
+ let selectedTools = [];
204
+
205
+ if (options.all) {
206
+ // --all flag: install for all tools
207
+ selectedTools = Object.keys(TOOLS);
208
+ console.log('\n Installing for all tools...');
209
+ } else if (options.tools && options.tools.length > 0) {
210
+ // Specific tools via --tools flag
211
+ selectedTools = options.tools;
212
+ } else {
213
+ // Interactive mode: show selection menu
214
+ const detectedTools = detectInstalledTools();
215
+ selectedTools = await showToolSelectionMenu(detectedTools);
216
+ }
217
+
218
+ if (selectedTools.length === 0) {
219
+ console.log('\n No tools selected. Exiting.\n');
220
+ return { success: false, reason: 'no_selection' };
221
+ }
222
+
223
+ // Detect project type
224
+ console.log('\n Detecting project type...');
225
+ const detectedTypes = detectProjectType(projectDir);
226
+ const rulesToInstall = getRulesForTypes(detectedTypes);
227
+
228
+ if (detectedTypes.length > 0) {
229
+ console.log(` Detected: ${detectedTypes.join(', ')}`);
230
+ console.log(` Rules: ${rulesToInstall.join(', ')}`);
231
+ } else {
232
+ console.log(' No specific technology detected.');
233
+ console.log(' Installing base commands only.');
234
+ }
235
+
236
+ // Install for each selected tool
237
+ console.log('\n Installing components...');
238
+ const results = [];
239
+
240
+ for (const toolId of selectedTools) {
241
+ const tool = TOOLS[toolId];
242
+ if (!tool) {
243
+ console.log(` [!] Unknown tool: ${toolId}`);
244
+ continue;
245
+ }
246
+
247
+ const result = installForTool(toolId, tool, projectDir, {
248
+ ...options,
249
+ detectedTypes,
250
+ rules: rulesToInstall
251
+ });
252
+
253
+ if (result.success) {
254
+ console.log(` [+] ${tool.name}: ${result.stats.files} files (${result.stats.sizeKB} KB)`);
255
+ } else {
256
+ console.log(` [-] ${tool.name}: ${result.message || result.reason}`);
257
+ }
258
+
259
+ results.push(result);
260
+ }
261
+
262
+ // Summary
263
+ const successCount = results.filter(r => r.success).length;
264
+ const totalFiles = results.reduce((sum, r) => sum + (r.stats?.files || 0), 0);
265
+ const totalSize = results.reduce((sum, r) => sum + (r.stats?.sizeKB || 0), 0);
161
266
 
162
267
  console.log('\n' + '='.repeat(60));
163
268
  console.log(' INSTALLATION COMPLETE');
164
269
  console.log('='.repeat(60));
165
- console.log(`\n Total: ${totalFiles} files (${totalSizeKB} KB)`);
166
- console.log(` Location: ${claudeDir}`);
270
+ console.log(`\n Tools: ${successCount}/${selectedTools.length} installed`);
271
+ console.log(` Total: ${totalFiles} files (${totalSize} KB)`);
272
+
273
+ // Show installed locations
274
+ console.log('\n Installed to:');
275
+ for (const result of results) {
276
+ if (result.success) {
277
+ console.log(` - ${result.path}`);
278
+ }
279
+ }
167
280
 
168
281
  console.log('\n Available commands:');
169
282
  console.log(' /plan - Plan implementation');
@@ -176,15 +289,16 @@ function initProject(options = {}) {
176
289
  console.log(`\n Tech-specific rules loaded for: ${detectedTypes.join(', ')}`);
177
290
  }
178
291
 
179
- console.log('\n Restart Claude Code to use the new skills.\n');
292
+ console.log('\n Restart your AI tool to use the new skills.\n');
180
293
 
181
294
  return {
182
- success: true,
295
+ success: successCount > 0,
296
+ tools: results,
183
297
  detected: detectedTypes,
184
298
  rules: rulesToInstall,
185
299
  stats: {
186
300
  files: totalFiles,
187
- sizeKB: totalSizeKB
301
+ sizeKB: totalSize
188
302
  }
189
303
  };
190
304
  }
@@ -194,52 +308,62 @@ function initProject(options = {}) {
194
308
  */
195
309
  function uninstallProject(options = {}) {
196
310
  const projectDir = options.path || process.cwd();
197
- const claudeDir = path.join(projectDir, '.claude');
198
311
 
199
312
  console.log('\n' + '='.repeat(60));
200
313
  console.log(' DEVKIT - UNINSTALL');
201
314
  console.log('='.repeat(60));
202
315
 
203
- if (!fs.existsSync(claudeDir)) {
204
- console.log('\n No .claude/ folder found.\n');
205
- return { success: false, reason: 'not_found' };
206
- }
316
+ let removedCount = 0;
317
+
318
+ for (const [toolId, tool] of Object.entries(TOOLS)) {
319
+ const targetDir = path.join(projectDir, tool.projectPath);
320
+ const devkitConfig = path.join(targetDir, 'devkit.json');
207
321
 
208
- // Check if it's a devkit installation
209
- const devkitConfig = path.join(claudeDir, 'devkit.json');
210
- if (!fs.existsSync(devkitConfig)) {
211
- console.log('\n .claude/ exists but is not a devkit installation.');
212
- console.log(' Remove manually if needed.\n');
213
- return { success: false, reason: 'not_devkit' };
322
+ if (fs.existsSync(devkitConfig)) {
323
+ fs.rmSync(targetDir, { recursive: true, force: true });
324
+ console.log(` Removed: ${targetDir}`);
325
+ removedCount++;
326
+ }
214
327
  }
215
328
 
216
- // Remove the directory
217
- fs.rmSync(claudeDir, { recursive: true, force: true });
218
- console.log(`\n Removed: ${claudeDir}`);
219
- console.log(' Devkit uninstalled successfully.\n');
329
+ if (removedCount === 0) {
330
+ console.log('\n No devkit installations found.\n');
331
+ return { success: false, reason: 'not_found' };
332
+ }
220
333
 
221
- return { success: true };
334
+ console.log(`\n Uninstalled ${removedCount} tool(s) successfully.\n`);
335
+ return { success: true, removed: removedCount };
222
336
  }
223
337
 
224
338
  module.exports = {
225
339
  initProject,
226
340
  uninstallProject,
227
- copyDir,
228
- getDirSize
341
+ installForTool,
342
+ showToolSelectionMenu
229
343
  };
230
344
 
231
345
  // Run if called directly
232
346
  if (require.main === module) {
233
347
  const args = process.argv.slice(2);
348
+
349
+ // Parse --tools=claude,cursor format
350
+ const toolsArg = args.find(a => a.startsWith('--tools='));
351
+ const tools = toolsArg ? toolsArg.split('=')[1].split(',') : [];
352
+
234
353
  const options = {
235
354
  force: args.includes('--force') || args.includes('-f'),
236
355
  update: args.includes('--update') || args.includes('-u'),
237
- path: args.find(a => !a.startsWith('-')) || process.cwd()
356
+ all: args.includes('--all') || args.includes('-a'),
357
+ tools: tools,
358
+ path: args.find(a => !a.startsWith('-') && !a.includes('=')) || process.cwd()
238
359
  };
239
360
 
240
361
  if (args.includes('--uninstall')) {
241
362
  uninstallProject(options);
242
363
  } else {
243
- initProject(options);
364
+ initProject(options).catch(err => {
365
+ console.error('Error:', err.message);
366
+ process.exit(1);
367
+ });
244
368
  }
245
369
  }
package/cli/utils.js CHANGED
@@ -6,6 +6,112 @@
6
6
 
7
7
  const fs = require('fs');
8
8
  const path = require('path');
9
+ const os = require('os');
10
+ const { execSync } = require('child_process');
11
+
12
+ const HOME = os.homedir();
13
+
14
+ /**
15
+ * Supported AI tools configuration
16
+ */
17
+ const TOOLS = {
18
+ 'claude': {
19
+ id: 'claude',
20
+ name: 'Claude Code',
21
+ basePath: path.join(HOME, '.claude'),
22
+ projectPath: '.claude',
23
+ skillsPath: 'skills',
24
+ rulesPath: 'rules',
25
+ hooksPath: 'hooks',
26
+ commandsPath: 'commands',
27
+ supportsHooks: true,
28
+ configFile: 'CLAUDE.md',
29
+ detectCmd: 'claude --version',
30
+ detectFolder: path.join(HOME, '.claude')
31
+ },
32
+ 'cursor': {
33
+ id: 'cursor',
34
+ name: 'Cursor',
35
+ basePath: path.join(HOME, '.cursor'),
36
+ projectPath: '.cursor',
37
+ skillsPath: 'skills',
38
+ rulesPath: 'rules',
39
+ hooksPath: 'hooks',
40
+ commandsPath: 'commands',
41
+ supportsHooks: false,
42
+ configFile: 'CURSOR.md',
43
+ detectCmd: null,
44
+ detectFolder: path.join(HOME, '.cursor')
45
+ },
46
+ 'copilot': {
47
+ id: 'copilot',
48
+ name: 'GitHub Copilot',
49
+ basePath: path.join(HOME, '.copilot'),
50
+ projectPath: '.github',
51
+ skillsPath: 'skills',
52
+ rulesPath: 'rules',
53
+ hooksPath: null,
54
+ commandsPath: null,
55
+ supportsHooks: false,
56
+ configFile: null,
57
+ detectCmd: 'gh copilot --version',
58
+ detectFolder: path.join(HOME, '.copilot')
59
+ },
60
+ 'gemini': {
61
+ id: 'gemini',
62
+ name: 'Gemini CLI',
63
+ basePath: path.join(HOME, '.gemini'),
64
+ projectPath: '.gemini',
65
+ skillsPath: 'skills',
66
+ rulesPath: 'rules',
67
+ hooksPath: null,
68
+ commandsPath: null,
69
+ supportsHooks: false,
70
+ configFile: 'GEMINI.md',
71
+ detectCmd: 'gemini --version',
72
+ detectFolder: path.join(HOME, '.gemini')
73
+ }
74
+ };
75
+
76
+ /**
77
+ * Detect which AI tools are installed on the system
78
+ * @returns {Object} - Object with tool ids as keys and detection status
79
+ */
80
+ function detectInstalledTools() {
81
+ const results = {};
82
+
83
+ for (const [toolId, tool] of Object.entries(TOOLS)) {
84
+ let detected = false;
85
+ let method = null;
86
+
87
+ // Try command detection first
88
+ if (tool.detectCmd) {
89
+ try {
90
+ execSync(tool.detectCmd, { stdio: 'pipe' });
91
+ detected = true;
92
+ method = 'cli';
93
+ } catch (e) {
94
+ // Command not found
95
+ }
96
+ }
97
+
98
+ // Fallback to folder detection
99
+ if (!detected && tool.detectFolder) {
100
+ if (fs.existsSync(tool.detectFolder)) {
101
+ detected = true;
102
+ method = 'folder';
103
+ }
104
+ }
105
+
106
+ results[toolId] = {
107
+ ...tool,
108
+ detected,
109
+ method
110
+ };
111
+ }
112
+
113
+ return results;
114
+ }
9
115
 
10
116
  /**
11
117
  * Copy directory recursively
@@ -191,5 +297,8 @@ module.exports = {
191
297
  getDirSize,
192
298
  parseJsonFile,
193
299
  validatePath,
194
- getAllEntries
300
+ getAllEntries,
301
+ detectInstalledTools,
302
+ TOOLS,
303
+ HOME
195
304
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ngxtm/devkit",
3
- "version": "3.4.1",
3
+ "version": "3.6.0",
4
4
  "description": "Per-project AI skills with smart tech detection - lightweight and context-optimized",
5
5
  "main": "cli/index.js",
6
6
  "bin": {
@@ -47,6 +47,9 @@
47
47
  "type": "git",
48
48
  "url": "git+https://github.com/ngxtm/devkit.git"
49
49
  },
50
+ "dependencies": {
51
+ "inquirer": "^9.2.12"
52
+ },
50
53
  "devDependencies": {
51
54
  "@semantic-release/changelog": "^6.0.3",
52
55
  "@semantic-release/commit-analyzer": "^11.1.0",
@@ -0,0 +1,476 @@
1
+ ---
2
+ name: learn
3
+ description: Interactive step-by-step learning mode. Teaches concepts from basics to advanced while solving real problems. Auto-detects language, verifies code at each step, creates markdown tutorials. Triggers on "/learn [topic]". Features: concept explanation, incremental coding with auto-verify, user checkpoints, optional quiz, saves tutorial to .claude/learn/.
4
+ ---
5
+
6
+ # Learn Mode - Interactive Step-by-Step Learning
7
+
8
+ > **Version 1.0.0** | Learn by Doing | Verified at Every Step
9
+
10
+ ---
11
+
12
+ ## Overview
13
+
14
+ Learn Mode helps you understand concepts deeply while solving real problems. Instead of just giving you code, it:
15
+
16
+ 1. Explains concepts from basics to advanced
17
+ 2. Guides you through implementation step-by-step
18
+ 3. Verifies code actually works at each step
19
+ 4. Saves everything to a markdown tutorial for future reference
20
+
21
+ ---
22
+
23
+ ## Activation
24
+
25
+ User invokes with: `/learn "topic or problem to solve"`
26
+
27
+ Examples:
28
+ - `/learn "implement debounce function in TypeScript"`
29
+ - `/learn "add JWT authentication to Express API"`
30
+ - `/learn "create custom React hook for form validation"`
31
+
32
+ ---
33
+
34
+ ## Execution Flow
35
+
36
+ ### Phase 0: INIT - Setup & Context Gathering
37
+
38
+ **Actions:**
39
+ 1. Create output directory if not exists: `.claude/learn/`
40
+ 2. Generate filename: `YYYY-MM-DD-{topic-slug}.md`
41
+ 3. Scan project for language detection:
42
+ - Check for config files: `tsconfig.json`, `package.json`, `go.mod`, `Cargo.toml`, `requirements.txt`, `pyproject.toml`, `pom.xml`, `composer.json`, `Gemfile`, etc.
43
+ - Identify primary language(s)
44
+ 4. Set verify strategy based on detected language
45
+ 5. Read relevant existing code for context
46
+
47
+ **Language Detection & Verify Strategy:**
48
+
49
+ | Language | Config Files | Verify Command |
50
+ |----------|--------------|----------------|
51
+ | TypeScript | `tsconfig.json`, `*.ts`, `*.tsx` | `npx tsc --noEmit` |
52
+ | JavaScript | `package.json`, `*.js`, `*.mjs` | `node --check <file>` |
53
+ | Python | `requirements.txt`, `pyproject.toml`, `*.py` | `python -m py_compile <file>` |
54
+ | Go | `go.mod`, `*.go` | `go build ./...` |
55
+ | Rust | `Cargo.toml`, `*.rs` | `cargo check` |
56
+ | Java | `pom.xml`, `build.gradle`, `*.java` | `javac <file>` or `./gradlew compileJava` |
57
+ | C# | `*.csproj`, `*.cs` | `dotnet build --no-restore` |
58
+ | PHP | `composer.json`, `*.php` | `php -l <file>` |
59
+ | Ruby | `Gemfile`, `*.rb` | `ruby -c <file>` |
60
+ | Shell | `*.sh`, `*.bash` | `bash -n <file>` |
61
+ | C/C++ | `Makefile`, `CMakeLists.txt`, `*.c`, `*.cpp` | `make` or `cmake --build .` |
62
+
63
+ **If multiple languages detected:** Ask user which one to use for this session.
64
+ **If no language detected:** Ask user to specify.
65
+
66
+ **Markdown Header (write to file):**
67
+ ```markdown
68
+ # Learn: {Topic}
69
+
70
+ > Generated: {YYYY-MM-DD HH:MM}
71
+ > Language: {detected_language}
72
+ > Project: {project_name}
73
+
74
+ ---
75
+ ```
76
+
77
+ ---
78
+
79
+ ### Phase 1: CONCEPT - Knowledge Foundation
80
+
81
+ **Goal:** Explain the underlying concepts before writing any code.
82
+
83
+ **Actions:**
84
+ 1. Break down the topic into fundamental concepts
85
+ 2. Explain each concept clearly:
86
+ - What is it?
87
+ - Why does it exist? What problem does it solve?
88
+ - How does it work (high-level)?
89
+ - Real-world analogies if helpful
90
+ 3. Compare with related concepts (if applicable)
91
+ - e.g., debounce vs throttle
92
+ - e.g., JWT vs session-based auth
93
+ 4. Show simple diagrams using ASCII if helpful
94
+
95
+ **Write to markdown:**
96
+ ```markdown
97
+ ## 1. Concepts
98
+
99
+ ### What is {topic}?
100
+ {explanation}
101
+
102
+ ### Why use {topic}?
103
+ {use cases and benefits}
104
+
105
+ ### How it works
106
+ {mechanism explanation}
107
+
108
+ ### Related Concepts
109
+ | Concept A | Concept B |
110
+ |-----------|-----------|
111
+ | ... | ... |
112
+
113
+ ---
114
+ ```
115
+
116
+ **User Checkpoint:**
117
+ ```
118
+ Phase 1/5: CONCEPT complete.
119
+
120
+ Do you understand these concepts?
121
+ [ ] Yes, continue to planning
122
+ [ ] Need more explanation (specify what)
123
+ ```
124
+
125
+ **STOP and wait for user response before proceeding.**
126
+
127
+ ---
128
+
129
+ ### Phase 2: PLAN - Implementation Strategy
130
+
131
+ **Goal:** Create a clear, step-by-step implementation plan.
132
+
133
+ **Actions:**
134
+ 1. Break implementation into small, verifiable steps (3-7 steps typically)
135
+ 2. Each step should:
136
+ - Have a clear goal
137
+ - Be independently verifiable
138
+ - Build on previous steps
139
+ 3. Identify files to create/modify
140
+ 4. Note any dependencies needed
141
+
142
+ **Write to markdown:**
143
+ ```markdown
144
+ ## 2. Implementation Plan
145
+
146
+ ### Files
147
+ - `{path/to/file1}` - {purpose}
148
+ - `{path/to/file2}` - {purpose}
149
+
150
+ ### Steps
151
+ 1. **{Step 1 title}** - {brief description}
152
+ 2. **{Step 2 title}** - {brief description}
153
+ 3. **{Step 3 title}** - {brief description}
154
+ ...
155
+
156
+ ### Dependencies
157
+ - {dependency 1} - {why needed}
158
+ - {dependency 2} - {why needed}
159
+
160
+ ---
161
+ ```
162
+
163
+ **User Checkpoint:**
164
+ ```
165
+ Phase 2/5: PLAN complete.
166
+
167
+ Ready to start coding?
168
+ [ ] Yes, let's code
169
+ [ ] Modify plan (specify changes)
170
+ ```
171
+
172
+ **STOP and wait for user response before proceeding.**
173
+
174
+ ---
175
+
176
+ ### Phase 3: CODE + VERIFY - Incremental Implementation
177
+
178
+ **Goal:** Implement each step, verify it works, ensure user understands.
179
+
180
+ **For each step in the plan:**
181
+
182
+ #### 3.1 Explain Before Coding
183
+ - What we're about to do
184
+ - Why we're doing it this way
185
+ - Key things to understand
186
+
187
+ #### 3.2 Write the Code
188
+ - Write complete, working code (no placeholders)
189
+ - Include all necessary imports
190
+ - Add inline comments explaining non-obvious parts
191
+ - Use Edit tool to modify existing files, Write for new files
192
+
193
+ #### 3.3 Auto-Verify
194
+ Run the appropriate verify command:
195
+ ```bash
196
+ # Execute verify command based on language
197
+ {verify_command}
198
+ ```
199
+
200
+ **If verify FAILS:**
201
+ 1. Analyze the error
202
+ 2. Explain what went wrong (teaching moment)
203
+ 3. Fix the code
204
+ 4. Re-verify
205
+ 5. Repeat until pass
206
+
207
+ **If verify PASSES:** Continue to user checkpoint.
208
+
209
+ #### 3.4 Write to Markdown
210
+ ```markdown
211
+ ### Step {N}: {Title}
212
+
213
+ **Goal:** {what this step accomplishes}
214
+
215
+ **Why:** {explanation of approach}
216
+
217
+ **Code:**
218
+ ```{language}
219
+ {code with comments}
220
+ ```
221
+
222
+ **Key Points:**
223
+ - {important thing 1}
224
+ - {important thing 2}
225
+
226
+ **Verify:** {verify_command} - PASSED
227
+
228
+ ---
229
+ ```
230
+
231
+ #### 3.5 User Checkpoint
232
+ ```
233
+ Step {N}/{total} complete and verified.
234
+
235
+ [ ] Understood, next step
236
+ [ ] Need more explanation
237
+ [ ] Code doesn't work on my machine (paste error)
238
+ ```
239
+
240
+ **If user reports error:**
241
+ 1. Ask for the error message
242
+ 2. Debug and fix
243
+ 3. Re-verify locally
244
+ 4. Update the markdown with the fix
245
+
246
+ **STOP and wait for user response before next step.**
247
+
248
+ ---
249
+
250
+ ### Phase 4: SUMMARY - Knowledge Consolidation
251
+
252
+ **Goal:** Reinforce learning with summary and best practices.
253
+
254
+ **Actions:**
255
+ 1. Summarize what was built
256
+ 2. List key takeaways
257
+ 3. Document common mistakes to avoid
258
+ 4. Suggest next steps for deeper learning
259
+
260
+ **Write to markdown:**
261
+ ```markdown
262
+ ## 4. Summary
263
+
264
+ ### What We Built
265
+ {summary of implementation}
266
+
267
+ ### Key Takeaways
268
+ 1. {takeaway 1}
269
+ 2. {takeaway 2}
270
+ 3. {takeaway 3}
271
+
272
+ ### Common Mistakes to Avoid
273
+ - {mistake 1} - {why it's bad}
274
+ - {mistake 2} - {why it's bad}
275
+
276
+ ### Next Steps
277
+ - {suggestion for further learning 1}
278
+ - {suggestion for further learning 2}
279
+
280
+ ---
281
+ ```
282
+
283
+ **User Checkpoint:**
284
+ ```
285
+ Phase 4/5: SUMMARY complete.
286
+
287
+ Would you like to take a quiz to reinforce learning?
288
+ [ ] Yes, quiz me
289
+ [ ] No, finish up
290
+ ```
291
+
292
+ ---
293
+
294
+ ### Phase 5: QUIZ (Optional)
295
+
296
+ **Goal:** Test understanding with practical questions.
297
+
298
+ **Only if user opted in.**
299
+
300
+ **Question Types:**
301
+ 1. **Conceptual:** Test understanding of the "why"
302
+ 2. **Code Reading:** Given code, predict behavior
303
+ 3. **Code Writing:** Small exercise to implement variation
304
+ 4. **Debugging:** Find the bug in given code
305
+
306
+ **Format:**
307
+ ```
308
+ QUIZ MODE
309
+
310
+ Q1 (Conceptual): {question}
311
+
312
+ Your answer: [wait for user]
313
+
314
+ ---
315
+
316
+ Correct answer: {answer}
317
+ Explanation: {why}
318
+
319
+ Score: {X}/4
320
+ ```
321
+
322
+ **Write to markdown:**
323
+ ```markdown
324
+ ## 5. Quiz
325
+
326
+ <details>
327
+ <summary>Q1: {question}</summary>
328
+
329
+ **Answer:** {answer}
330
+
331
+ **Explanation:** {explanation}
332
+ </details>
333
+
334
+ <details>
335
+ <summary>Q2: {question}</summary>
336
+ ...
337
+ </details>
338
+
339
+ ---
340
+ ```
341
+
342
+ ---
343
+
344
+ ### Phase 6: COMPLETE
345
+
346
+ **Actions:**
347
+ 1. Finalize markdown file
348
+ 2. Display completion message
349
+
350
+ **Add to markdown:**
351
+ ```markdown
352
+ ---
353
+
354
+ > Tutorial completed: {timestamp}
355
+ > Total steps: {N}
356
+ > All code verified and working
357
+
358
+ Happy coding!
359
+ ```
360
+
361
+ **Display to user:**
362
+ ```
363
+ LEARN MODE COMPLETE
364
+
365
+ Tutorial saved: .claude/learn/{filename}.md
366
+ You can review this file anytime to refresh your knowledge.
367
+
368
+ What you learned:
369
+ - {concept 1}
370
+ - {concept 2}
371
+ - {concept 3}
372
+
373
+ Great job!
374
+ ```
375
+
376
+ ---
377
+
378
+ ## Error Handling
379
+
380
+ ### Verify Command Not Available
381
+ If the verify command fails because tool is not installed:
382
+ 1. Inform user: "Verify tool not available: {command}"
383
+ 2. Ask: "Install it now, or proceed with manual verification?"
384
+ 3. If install: run appropriate install command
385
+ 4. If manual: ask user to confirm code works after each step
386
+
387
+ ### User Reports Code Doesn't Work
388
+ 1. Ask for exact error message
389
+ 2. Ask for their environment (OS, versions)
390
+ 3. Debug systematically
391
+ 4. Update tutorial with fix
392
+ 5. Add to "Common Issues" section in markdown
393
+
394
+ ### Language Not Detected
395
+ 1. List common languages
396
+ 2. Ask user to specify
397
+ 3. Set verify strategy accordingly
398
+
399
+ ---
400
+
401
+ ## Principles
402
+
403
+ 1. **No code without understanding** - Always explain before implementing
404
+ 2. **Verify everything** - Never assume code works, always test
405
+ 3. **Fix before proceed** - Don't move on until current step works
406
+ 4. **User controls pace** - Always checkpoint before next phase
407
+ 5. **Document for future** - Create reusable tutorial
408
+ 6. **Basics to advanced** - Start simple, build up complexity
409
+ 7. **Real working code** - No pseudocode, no placeholders
410
+
411
+ ---
412
+
413
+ ## Example Session
414
+
415
+ ```
416
+ User: /learn "implement debounce in TypeScript"
417
+
418
+ [INIT]
419
+ Detected: TypeScript (tsconfig.json found)
420
+ Verify command: npx tsc --noEmit
421
+ Creating: .claude/learn/2024-02-06-debounce-typescript.md
422
+
423
+ [CONCEPT]
424
+ Debounce is a technique that delays executing a function until
425
+ after a specified time has passed since the last call...
426
+ [detailed explanation]
427
+
428
+ Ready to continue? [Yes/Explain more]
429
+
430
+ User: Yes
431
+
432
+ [PLAN]
433
+ Step 1: Create utils/debounce.ts with basic structure
434
+ Step 2: Implement core debounce logic
435
+ Step 3: Add TypeScript generics for type safety
436
+ Step 4: Write unit tests
437
+
438
+ Ready to code? [Yes/Modify plan]
439
+
440
+ User: Yes
441
+
442
+ [CODE Step 1/4]
443
+ Creating utils/debounce.ts...
444
+ [code with explanation]
445
+
446
+ Verifying: npx tsc --noEmit
447
+ PASSED
448
+
449
+ Understood? [Yes/Explain more/Error on my machine]
450
+
451
+ User: Yes
452
+
453
+ [CODE Step 2/4]
454
+ ...
455
+
456
+ [After all steps]
457
+
458
+ [SUMMARY]
459
+ Key takeaways:
460
+ 1. Debounce delays execution until activity stops
461
+ 2. clearTimeout prevents stale callbacks
462
+ 3. Generics preserve function type signatures
463
+
464
+ Quiz? [Yes/No]
465
+
466
+ User: No
467
+
468
+ COMPLETE
469
+ Tutorial saved: .claude/learn/2024-02-06-debounce-typescript.md
470
+ ```
471
+
472
+ ---
473
+
474
+ ## Version History
475
+
476
+ - **1.0.0** - Initial release with full interactive learning flow