@ngxtm/devkit 3.3.0 → 3.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.
- package/cli/index.js +59 -13
- package/cli/rules.js +248 -0
- package/package.json +1 -1
package/cli/index.js
CHANGED
|
@@ -17,6 +17,7 @@ const { initProject, uninstallProject } = require('./init');
|
|
|
17
17
|
const { updateProject, showStatus } = require('./update');
|
|
18
18
|
const { detectProjectType, getRulesForTypes, printDetectionResults } = require('./detect');
|
|
19
19
|
const { validatePath } = require('./utils');
|
|
20
|
+
const { listRules, addRules, removeRules } = require('./rules');
|
|
20
21
|
|
|
21
22
|
// Legacy imports for backwards compatibility (will be deprecated)
|
|
22
23
|
let legacyInstall = null;
|
|
@@ -38,6 +39,8 @@ function parseArgs(args) {
|
|
|
38
39
|
update: false,
|
|
39
40
|
clean: false,
|
|
40
41
|
help: false,
|
|
42
|
+
installed: false,
|
|
43
|
+
ruleArgs: [],
|
|
41
44
|
// Legacy options (deprecated)
|
|
42
45
|
tool: null,
|
|
43
46
|
minimal: false,
|
|
@@ -57,6 +60,8 @@ function parseArgs(args) {
|
|
|
57
60
|
options.update = true;
|
|
58
61
|
} else if (arg === '--clean' || arg === '-c') {
|
|
59
62
|
options.clean = true;
|
|
63
|
+
} else if (arg === '--installed' || arg === '-i') {
|
|
64
|
+
options.installed = true;
|
|
60
65
|
} else if (arg === '--status' || arg === '-s') {
|
|
61
66
|
options.command = 'status';
|
|
62
67
|
} else if (arg.startsWith('--path=')) {
|
|
@@ -65,21 +70,15 @@ function parseArgs(args) {
|
|
|
65
70
|
// Handle legacy options for backwards compatibility
|
|
66
71
|
if (arg === '--minimal' || arg === '-m') options.minimal = true;
|
|
67
72
|
if (arg === '--lite' || arg === '-l') options.lite = true;
|
|
68
|
-
if (arg === '--interactive' || arg === '-i') options.interactive = true;
|
|
69
|
-
if (arg === '--full') options.fullSkills = true;
|
|
70
73
|
if (arg.startsWith('--category=')) {
|
|
71
74
|
const cats = arg.split('=')[1].split(',').map(c => c.trim());
|
|
72
75
|
options.categories.push(...cats);
|
|
73
76
|
}
|
|
74
77
|
} else if (!options.command) {
|
|
75
78
|
options.command = arg;
|
|
76
|
-
} else
|
|
77
|
-
//
|
|
78
|
-
|
|
79
|
-
options.tool = arg;
|
|
80
|
-
} else {
|
|
81
|
-
options.path = arg;
|
|
82
|
-
}
|
|
79
|
+
} else {
|
|
80
|
+
// Additional args (for add/remove commands)
|
|
81
|
+
options.ruleArgs.push(arg);
|
|
83
82
|
}
|
|
84
83
|
}
|
|
85
84
|
|
|
@@ -104,6 +103,15 @@ COMMANDS:
|
|
|
104
103
|
detect Show detected technologies for current project
|
|
105
104
|
Useful to see what devkit will install.
|
|
106
105
|
|
|
106
|
+
rules List all available rules
|
|
107
|
+
Use --installed to show only installed rules.
|
|
108
|
+
|
|
109
|
+
add <rule> Add rules manually (space-separated)
|
|
110
|
+
Example: devkit add golang docker
|
|
111
|
+
|
|
112
|
+
remove <rule> Remove rules from project
|
|
113
|
+
Example: devkit remove flutter
|
|
114
|
+
|
|
107
115
|
status Show installation status
|
|
108
116
|
|
|
109
117
|
uninstall Remove devkit from current project
|
|
@@ -115,10 +123,11 @@ COMMANDS:
|
|
|
115
123
|
version Show version
|
|
116
124
|
|
|
117
125
|
OPTIONS:
|
|
118
|
-
--force, -f
|
|
119
|
-
--clean, -c
|
|
120
|
-
--
|
|
121
|
-
--
|
|
126
|
+
--force, -f Force overwrite existing installation
|
|
127
|
+
--clean, -c Remove rules for technologies no longer detected (with update)
|
|
128
|
+
--installed, -i Show only installed rules (with rules command)
|
|
129
|
+
--path=DIR Specify project directory (default: current directory)
|
|
130
|
+
--help, -h Show this help
|
|
122
131
|
|
|
123
132
|
EXAMPLES:
|
|
124
133
|
devkit init # Initialize in current project
|
|
@@ -126,6 +135,10 @@ EXAMPLES:
|
|
|
126
135
|
devkit update # Update and re-detect technologies
|
|
127
136
|
devkit update --clean # Update and remove old rules
|
|
128
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
|
|
129
142
|
devkit status # Show current installation
|
|
130
143
|
devkit uninstall # Remove from current project
|
|
131
144
|
|
|
@@ -211,6 +224,39 @@ const commands = {
|
|
|
211
224
|
});
|
|
212
225
|
},
|
|
213
226
|
|
|
227
|
+
// List available rules
|
|
228
|
+
rules: (options) => {
|
|
229
|
+
const projectPath = validatePath(options.path) || process.cwd();
|
|
230
|
+
return listRules({
|
|
231
|
+
path: projectPath,
|
|
232
|
+
installed: options.installed
|
|
233
|
+
});
|
|
234
|
+
},
|
|
235
|
+
|
|
236
|
+
// Add rules manually
|
|
237
|
+
add: (options) => {
|
|
238
|
+
const projectPath = validatePath(options.path) || process.cwd();
|
|
239
|
+
if (options.ruleArgs.length === 0) {
|
|
240
|
+
console.log('\n Usage: devkit add <rule> [rule2 ...]');
|
|
241
|
+
console.log(' Example: devkit add golang docker\n');
|
|
242
|
+
console.log(' Run "devkit rules" to see available rules.\n');
|
|
243
|
+
return { success: false, reason: 'no_rules' };
|
|
244
|
+
}
|
|
245
|
+
return addRules(options.ruleArgs, { path: projectPath });
|
|
246
|
+
},
|
|
247
|
+
|
|
248
|
+
// Remove rules
|
|
249
|
+
remove: (options) => {
|
|
250
|
+
const projectPath = validatePath(options.path) || process.cwd();
|
|
251
|
+
if (options.ruleArgs.length === 0) {
|
|
252
|
+
console.log('\n Usage: devkit remove <rule> [rule2 ...]');
|
|
253
|
+
console.log(' Example: devkit remove flutter\n');
|
|
254
|
+
console.log(' Run "devkit rules --installed" to see installed rules.\n');
|
|
255
|
+
return { success: false, reason: 'no_rules' };
|
|
256
|
+
}
|
|
257
|
+
return removeRules(options.ruleArgs, { path: projectPath });
|
|
258
|
+
},
|
|
259
|
+
|
|
214
260
|
// List skills (from skills-index.json)
|
|
215
261
|
list: () => {
|
|
216
262
|
const indexPath = path.join(__dirname, '..', 'skills-index.json');
|
package/cli/rules.js
ADDED
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rules Management
|
|
3
|
+
*
|
|
4
|
+
* Add/remove rules manually for projects where auto-detection
|
|
5
|
+
* doesn't cover all needs.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const fs = require('fs');
|
|
9
|
+
const path = require('path');
|
|
10
|
+
const { copyDir } = require('./utils');
|
|
11
|
+
|
|
12
|
+
const PACKAGE_ROOT = path.join(__dirname, '..');
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Get all available rules from the rules/ directory
|
|
16
|
+
*/
|
|
17
|
+
function getAvailableRules() {
|
|
18
|
+
const rulesDir = path.join(PACKAGE_ROOT, 'rules');
|
|
19
|
+
|
|
20
|
+
if (!fs.existsSync(rulesDir)) {
|
|
21
|
+
return [];
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return fs.readdirSync(rulesDir).filter(item => {
|
|
25
|
+
const itemPath = path.join(rulesDir, item);
|
|
26
|
+
return fs.statSync(itemPath).isDirectory() && !item.startsWith('.');
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Get installed rules from devkit.json
|
|
32
|
+
*/
|
|
33
|
+
function getInstalledRules(projectDir) {
|
|
34
|
+
const configPath = path.join(projectDir, '.claude', 'devkit.json');
|
|
35
|
+
|
|
36
|
+
if (!fs.existsSync(configPath)) {
|
|
37
|
+
return { rules: [], manualRules: [] };
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
try {
|
|
41
|
+
const config = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
|
|
42
|
+
return {
|
|
43
|
+
rules: config.installedRules || [],
|
|
44
|
+
manualRules: config.manualRules || [],
|
|
45
|
+
detectedTypes: config.detectedTypes || []
|
|
46
|
+
};
|
|
47
|
+
} catch (e) {
|
|
48
|
+
return { rules: [], manualRules: [] };
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Update devkit.json with new rules
|
|
54
|
+
*/
|
|
55
|
+
function updateDevkitConfig(projectDir, updates) {
|
|
56
|
+
const configPath = path.join(projectDir, '.claude', 'devkit.json');
|
|
57
|
+
|
|
58
|
+
if (!fs.existsSync(configPath)) {
|
|
59
|
+
console.log(' No devkit.json found. Run "devkit init" first.');
|
|
60
|
+
return false;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const config = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
|
|
64
|
+
Object.assign(config, updates, { updatedAt: new Date().toISOString() });
|
|
65
|
+
fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
|
|
66
|
+
return true;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* List all available rules
|
|
71
|
+
*/
|
|
72
|
+
function listRules(options = {}) {
|
|
73
|
+
const projectDir = options.path || process.cwd();
|
|
74
|
+
const available = getAvailableRules();
|
|
75
|
+
const { rules: installed, manualRules } = getInstalledRules(projectDir);
|
|
76
|
+
const allInstalled = [...new Set([...installed, ...manualRules])];
|
|
77
|
+
|
|
78
|
+
console.log('\n' + '='.repeat(50));
|
|
79
|
+
console.log(' AVAILABLE RULES');
|
|
80
|
+
console.log('='.repeat(50));
|
|
81
|
+
|
|
82
|
+
if (options.installed) {
|
|
83
|
+
// Show only installed rules
|
|
84
|
+
console.log('\nInstalled in this project:');
|
|
85
|
+
if (allInstalled.length === 0) {
|
|
86
|
+
console.log(' (none)');
|
|
87
|
+
} else {
|
|
88
|
+
allInstalled.forEach(rule => {
|
|
89
|
+
const isManual = manualRules.includes(rule);
|
|
90
|
+
console.log(` - ${rule}${isManual ? ' (manual)' : ' (auto-detected)'}`);
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
} else {
|
|
94
|
+
// Show all available rules
|
|
95
|
+
console.log(`\nTotal: ${available.length} rules\n`);
|
|
96
|
+
available.forEach(rule => {
|
|
97
|
+
const isInstalled = allInstalled.includes(rule);
|
|
98
|
+
const marker = isInstalled ? '[x]' : '[ ]';
|
|
99
|
+
console.log(` ${marker} ${rule}`);
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
console.log('\n [x] = installed in current project');
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
console.log('');
|
|
106
|
+
return { available, installed: allInstalled };
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Add rules to project
|
|
111
|
+
*/
|
|
112
|
+
function addRules(ruleNames, options = {}) {
|
|
113
|
+
const projectDir = options.path || process.cwd();
|
|
114
|
+
const claudeDir = path.join(projectDir, '.claude');
|
|
115
|
+
|
|
116
|
+
if (!fs.existsSync(claudeDir)) {
|
|
117
|
+
console.log('\n No .claude/ folder found. Run "devkit init" first.\n');
|
|
118
|
+
return { success: false, reason: 'not_initialized' };
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const available = getAvailableRules();
|
|
122
|
+
const { rules: autoRules, manualRules = [] } = getInstalledRules(projectDir);
|
|
123
|
+
const allInstalled = [...new Set([...autoRules, ...manualRules])];
|
|
124
|
+
|
|
125
|
+
console.log('\n' + '='.repeat(50));
|
|
126
|
+
console.log(' ADD RULES');
|
|
127
|
+
console.log('='.repeat(50));
|
|
128
|
+
|
|
129
|
+
const added = [];
|
|
130
|
+
const skipped = [];
|
|
131
|
+
const notFound = [];
|
|
132
|
+
|
|
133
|
+
for (const ruleName of ruleNames) {
|
|
134
|
+
const rule = ruleName.toLowerCase();
|
|
135
|
+
|
|
136
|
+
if (!available.includes(rule)) {
|
|
137
|
+
notFound.push(rule);
|
|
138
|
+
console.log(` [!] ${rule} - not found`);
|
|
139
|
+
continue;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
if (allInstalled.includes(rule)) {
|
|
143
|
+
skipped.push(rule);
|
|
144
|
+
console.log(` [-] ${rule} - already installed`);
|
|
145
|
+
continue;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Copy rule files
|
|
149
|
+
const srcDir = path.join(PACKAGE_ROOT, 'rules', rule);
|
|
150
|
+
const destDir = path.join(claudeDir, 'rules', rule);
|
|
151
|
+
|
|
152
|
+
if (fs.existsSync(srcDir)) {
|
|
153
|
+
const count = copyDir(srcDir, destDir);
|
|
154
|
+
added.push(rule);
|
|
155
|
+
console.log(` [+] ${rule} - added (${count} files)`);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// Update devkit.json
|
|
160
|
+
if (added.length > 0) {
|
|
161
|
+
const newManualRules = [...new Set([...manualRules, ...added])];
|
|
162
|
+
updateDevkitConfig(projectDir, { manualRules: newManualRules });
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// Summary
|
|
166
|
+
console.log('\n' + '-'.repeat(50));
|
|
167
|
+
if (added.length > 0) {
|
|
168
|
+
console.log(` Added: ${added.join(', ')}`);
|
|
169
|
+
}
|
|
170
|
+
if (skipped.length > 0) {
|
|
171
|
+
console.log(` Skipped (already installed): ${skipped.join(', ')}`);
|
|
172
|
+
}
|
|
173
|
+
if (notFound.length > 0) {
|
|
174
|
+
console.log(` Not found: ${notFound.join(', ')}`);
|
|
175
|
+
console.log(` Run "devkit rules" to see available rules.`);
|
|
176
|
+
}
|
|
177
|
+
console.log('');
|
|
178
|
+
|
|
179
|
+
return { success: true, added, skipped, notFound };
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Remove rules from project
|
|
184
|
+
*/
|
|
185
|
+
function removeRules(ruleNames, options = {}) {
|
|
186
|
+
const projectDir = options.path || process.cwd();
|
|
187
|
+
const claudeDir = path.join(projectDir, '.claude');
|
|
188
|
+
|
|
189
|
+
if (!fs.existsSync(claudeDir)) {
|
|
190
|
+
console.log('\n No .claude/ folder found.\n');
|
|
191
|
+
return { success: false, reason: 'not_initialized' };
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
const { rules: autoRules, manualRules = [] } = getInstalledRules(projectDir);
|
|
195
|
+
|
|
196
|
+
console.log('\n' + '='.repeat(50));
|
|
197
|
+
console.log(' REMOVE RULES');
|
|
198
|
+
console.log('='.repeat(50));
|
|
199
|
+
|
|
200
|
+
const removed = [];
|
|
201
|
+
const notInstalled = [];
|
|
202
|
+
|
|
203
|
+
for (const ruleName of ruleNames) {
|
|
204
|
+
const rule = ruleName.toLowerCase();
|
|
205
|
+
const ruleDir = path.join(claudeDir, 'rules', rule);
|
|
206
|
+
|
|
207
|
+
if (!fs.existsSync(ruleDir)) {
|
|
208
|
+
notInstalled.push(rule);
|
|
209
|
+
console.log(` [-] ${rule} - not installed`);
|
|
210
|
+
continue;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// Remove rule directory
|
|
214
|
+
fs.rmSync(ruleDir, { recursive: true, force: true });
|
|
215
|
+
removed.push(rule);
|
|
216
|
+
console.log(` [x] ${rule} - removed`);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// Update devkit.json
|
|
220
|
+
if (removed.length > 0) {
|
|
221
|
+
const newAutoRules = autoRules.filter(r => !removed.includes(r));
|
|
222
|
+
const newManualRules = manualRules.filter(r => !removed.includes(r));
|
|
223
|
+
updateDevkitConfig(projectDir, {
|
|
224
|
+
installedRules: newAutoRules,
|
|
225
|
+
manualRules: newManualRules
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// Summary
|
|
230
|
+
console.log('\n' + '-'.repeat(50));
|
|
231
|
+
if (removed.length > 0) {
|
|
232
|
+
console.log(` Removed: ${removed.join(', ')}`);
|
|
233
|
+
}
|
|
234
|
+
if (notInstalled.length > 0) {
|
|
235
|
+
console.log(` Not installed: ${notInstalled.join(', ')}`);
|
|
236
|
+
}
|
|
237
|
+
console.log('');
|
|
238
|
+
|
|
239
|
+
return { success: true, removed, notInstalled };
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
module.exports = {
|
|
243
|
+
getAvailableRules,
|
|
244
|
+
getInstalledRules,
|
|
245
|
+
listRules,
|
|
246
|
+
addRules,
|
|
247
|
+
removeRules
|
|
248
|
+
};
|