@ngxtm/devkit 3.6.0 → 3.7.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/SKILLS_INDEX.md +2419 -634
- package/cli/index.js +6 -4
- package/cli/update.js +145 -86
- package/package.json +1 -1
- package/scripts/generate-index.js +3 -1
- package/skills-index.json +2248 -463
package/cli/index.js
CHANGED
|
@@ -63,6 +63,8 @@ function parseArgs(args) {
|
|
|
63
63
|
options.update = true;
|
|
64
64
|
} else if (arg === '--clean' || arg === '-c') {
|
|
65
65
|
options.clean = true;
|
|
66
|
+
} else if (arg === '--no-clean') {
|
|
67
|
+
options.clean = false;
|
|
66
68
|
} else if (arg === '--installed' || arg === '-i') {
|
|
67
69
|
options.installed = true;
|
|
68
70
|
} else if (arg === '--all' || arg === '-a') {
|
|
@@ -105,7 +107,7 @@ COMMANDS:
|
|
|
105
107
|
Auto-detects tech stack and installs relevant rules.
|
|
106
108
|
|
|
107
109
|
update Update existing installation
|
|
108
|
-
Re-detects project
|
|
110
|
+
Re-detects project, updates commands, and cleans old rules.
|
|
109
111
|
|
|
110
112
|
detect Show detected technologies for current project
|
|
111
113
|
Useful to see what devkit will install.
|
|
@@ -134,7 +136,7 @@ OPTIONS:
|
|
|
134
136
|
--all, -a Install for all supported tools (skip menu)
|
|
135
137
|
--tools=LIST Install for specific tools (comma-separated)
|
|
136
138
|
Example: --tools=claude,cursor
|
|
137
|
-
--clean
|
|
139
|
+
--no-clean Skip removing rules for technologies no longer detected
|
|
138
140
|
--installed, -i Show only installed rules (with rules command)
|
|
139
141
|
--path=DIR Specify project directory (default: current directory)
|
|
140
142
|
--help, -h Show this help
|
|
@@ -150,8 +152,8 @@ EXAMPLES:
|
|
|
150
152
|
devkit init --all # Install for all tools
|
|
151
153
|
devkit init --tools=claude,cursor # Install for specific tools
|
|
152
154
|
devkit init --force # Overwrite existing installation
|
|
153
|
-
devkit update #
|
|
154
|
-
devkit update --clean
|
|
155
|
+
devkit update # Full update (commands + clean old rules)
|
|
156
|
+
devkit update --no-clean # Update without removing old rules
|
|
155
157
|
devkit detect # Show what would be detected
|
|
156
158
|
devkit rules # List all available rules
|
|
157
159
|
devkit rules --installed # Show installed rules
|
package/cli/update.js
CHANGED
|
@@ -13,7 +13,7 @@ const { execSync } = require('child_process');
|
|
|
13
13
|
|
|
14
14
|
const { detectProjectType, getRulesForTypes } = require('./detect');
|
|
15
15
|
const { initProject } = require('./init');
|
|
16
|
-
const { copyDir, getDirSize, parseJsonFile } = require('./utils');
|
|
16
|
+
const { copyDir, getDirSize, parseJsonFile, TOOLS } = require('./utils');
|
|
17
17
|
|
|
18
18
|
const VERSION = require('../package.json').version;
|
|
19
19
|
const PACKAGE_ROOT = path.join(__dirname, '..');
|
|
@@ -34,33 +34,134 @@ function checkForUpdates() {
|
|
|
34
34
|
}
|
|
35
35
|
|
|
36
36
|
/**
|
|
37
|
-
* Update
|
|
37
|
+
* Update a single tool installation
|
|
38
|
+
* @param {string} toolId - Tool identifier
|
|
39
|
+
* @param {Object} tool - Tool configuration
|
|
40
|
+
* @param {string} projectDir - Project directory
|
|
41
|
+
* @param {Object} options - Update options
|
|
42
|
+
* @returns {Object} - Update result
|
|
43
|
+
*/
|
|
44
|
+
function updateToolInstallation(toolId, tool, projectDir, options = {}) {
|
|
45
|
+
const targetDir = path.join(projectDir, tool.projectPath);
|
|
46
|
+
const configPath = path.join(targetDir, 'devkit.json');
|
|
47
|
+
|
|
48
|
+
if (!fs.existsSync(configPath)) {
|
|
49
|
+
return { success: false, reason: 'not_installed' };
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const config = parseJsonFile(configPath);
|
|
53
|
+
if (!config) {
|
|
54
|
+
return { success: false, reason: 'corrupted_config' };
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
let updatedCount = 0;
|
|
58
|
+
|
|
59
|
+
// 1. Update commands (if tool supports it)
|
|
60
|
+
if (tool.commandsPath) {
|
|
61
|
+
const mergedCommandsDir = path.join(PACKAGE_ROOT, 'merged-commands');
|
|
62
|
+
const commandsDir = path.join(targetDir, tool.commandsPath);
|
|
63
|
+
if (fs.existsSync(mergedCommandsDir)) {
|
|
64
|
+
updatedCount += copyDir(mergedCommandsDir, commandsDir);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// 2. Update rules
|
|
69
|
+
if (tool.rulesPath && options.rules && options.rules.length > 0) {
|
|
70
|
+
const rulesDir = path.join(targetDir, tool.rulesPath);
|
|
71
|
+
for (const ruleType of options.rules) {
|
|
72
|
+
const srcRulesDir = path.join(PACKAGE_ROOT, 'templates', ruleType, 'rules');
|
|
73
|
+
if (fs.existsSync(srcRulesDir)) {
|
|
74
|
+
const destRulesDir = path.join(rulesDir, ruleType);
|
|
75
|
+
updatedCount += copyDir(srcRulesDir, destRulesDir);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// 3. Remove old rules (if clean enabled)
|
|
81
|
+
if (tool.rulesPath && options.removedRules && options.removedRules.length > 0 && options.clean !== false) {
|
|
82
|
+
const rulesDir = path.join(targetDir, tool.rulesPath);
|
|
83
|
+
for (const ruleType of options.removedRules) {
|
|
84
|
+
const ruleDir = path.join(rulesDir, ruleType);
|
|
85
|
+
if (fs.existsSync(ruleDir)) {
|
|
86
|
+
fs.rmSync(ruleDir, { recursive: true, force: true });
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// 4. Update hooks (if tool supports it)
|
|
92
|
+
if (tool.supportsHooks && tool.hooksPath) {
|
|
93
|
+
const srcHooksDir = path.join(PACKAGE_ROOT, 'hooks');
|
|
94
|
+
const hooksDir = path.join(targetDir, tool.hooksPath);
|
|
95
|
+
if (fs.existsSync(srcHooksDir)) {
|
|
96
|
+
updatedCount += copyDir(srcHooksDir, hooksDir);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// 5. Update skills index
|
|
101
|
+
const skillsIndexSrc = path.join(PACKAGE_ROOT, 'skills-index.json');
|
|
102
|
+
if (fs.existsSync(skillsIndexSrc)) {
|
|
103
|
+
fs.copyFileSync(skillsIndexSrc, path.join(targetDir, 'skills-index.json'));
|
|
104
|
+
updatedCount++;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// 6. Update devkit.json
|
|
108
|
+
const newConfig = {
|
|
109
|
+
...config,
|
|
110
|
+
version: VERSION,
|
|
111
|
+
detectedTypes: options.detectedTypes || config.detectedTypes,
|
|
112
|
+
installedRules: options.allRules || config.installedRules,
|
|
113
|
+
updatedAt: new Date().toISOString(),
|
|
114
|
+
stats: {
|
|
115
|
+
totalFiles: config.stats?.totalFiles || 0,
|
|
116
|
+
sizeKB: Math.round(getDirSize(targetDir) / 1024)
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
fs.writeFileSync(configPath, JSON.stringify(newConfig, null, 2));
|
|
120
|
+
|
|
121
|
+
return {
|
|
122
|
+
success: true,
|
|
123
|
+
tool: toolId,
|
|
124
|
+
toolName: tool.name,
|
|
125
|
+
path: targetDir,
|
|
126
|
+
updatedFiles: updatedCount,
|
|
127
|
+
sizeKB: newConfig.stats.sizeKB
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Update devkit installation for all installed tools
|
|
38
133
|
*/
|
|
39
134
|
function updateProject(options = {}) {
|
|
40
135
|
const projectDir = options.path || process.cwd();
|
|
41
|
-
const claudeDir = path.join(projectDir, '.claude');
|
|
42
|
-
const configPath = path.join(claudeDir, 'devkit.json');
|
|
43
136
|
|
|
44
137
|
console.log('\n' + '='.repeat(60));
|
|
45
138
|
console.log(' DEVKIT - UPDATE');
|
|
46
139
|
console.log('='.repeat(60));
|
|
47
140
|
|
|
48
|
-
//
|
|
49
|
-
|
|
141
|
+
// Find all installed tools
|
|
142
|
+
const installedTools = [];
|
|
143
|
+
let primaryConfig = null;
|
|
144
|
+
|
|
145
|
+
for (const [toolId, tool] of Object.entries(TOOLS)) {
|
|
146
|
+
const targetDir = path.join(projectDir, tool.projectPath);
|
|
147
|
+
const configPath = path.join(targetDir, 'devkit.json');
|
|
148
|
+
if (fs.existsSync(configPath)) {
|
|
149
|
+
const config = parseJsonFile(configPath);
|
|
150
|
+
if (config) {
|
|
151
|
+
installedTools.push({ toolId, tool, config });
|
|
152
|
+
if (!primaryConfig) primaryConfig = config;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
if (installedTools.length === 0) {
|
|
50
158
|
console.log('\n No devkit installation found.');
|
|
51
159
|
console.log(' Run: devkit init\n');
|
|
52
160
|
return { success: false, reason: 'not_installed' };
|
|
53
161
|
}
|
|
54
162
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
if (!config) {
|
|
58
|
-
console.log('\n Error: devkit.json is corrupted.');
|
|
59
|
-
console.log(' Try reinitializing with: devkit init --force\n');
|
|
60
|
-
return { success: false, reason: 'corrupted_config' };
|
|
61
|
-
}
|
|
62
|
-
console.log(`\n Current version: ${config.version}`);
|
|
63
|
-
console.log(` Installed: ${new Date(config.installedAt).toLocaleDateString()}`);
|
|
163
|
+
console.log(`\n Found ${installedTools.length} tool(s): ${installedTools.map(t => t.tool.name).join(', ')}`);
|
|
164
|
+
console.log(` Current version: ${primaryConfig.version}`);
|
|
64
165
|
|
|
65
166
|
// Check for package updates
|
|
66
167
|
console.log('\n Checking for updates...');
|
|
@@ -79,12 +180,11 @@ function updateProject(options = {}) {
|
|
|
79
180
|
// Re-detect project type
|
|
80
181
|
console.log('\n Re-detecting project type...');
|
|
81
182
|
const newDetected = detectProjectType(projectDir);
|
|
82
|
-
const oldDetected =
|
|
183
|
+
const oldDetected = primaryConfig.detectedTypes || [];
|
|
83
184
|
|
|
84
185
|
// Find differences
|
|
85
186
|
const added = newDetected.filter(t => !oldDetected.includes(t));
|
|
86
187
|
const removed = oldDetected.filter(t => !newDetected.includes(t));
|
|
87
|
-
const unchanged = newDetected.filter(t => oldDetected.includes(t));
|
|
88
188
|
|
|
89
189
|
if (added.length > 0) {
|
|
90
190
|
console.log(` New tech detected: ${added.join(', ')}`);
|
|
@@ -92,91 +192,50 @@ function updateProject(options = {}) {
|
|
|
92
192
|
if (removed.length > 0) {
|
|
93
193
|
console.log(` Removed: ${removed.join(', ')}`);
|
|
94
194
|
}
|
|
95
|
-
if (unchanged.length > 0) {
|
|
96
|
-
console.log(` Unchanged: ${unchanged.join(', ')}`);
|
|
97
|
-
}
|
|
98
195
|
if (added.length === 0 && removed.length === 0) {
|
|
99
196
|
console.log(' No changes in detected technologies.');
|
|
100
197
|
}
|
|
101
198
|
|
|
102
|
-
//
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
// Remove old rules if needed and user agrees
|
|
120
|
-
if (removed.length > 0 && options.clean) {
|
|
121
|
-
const rulesDir = path.join(claudeDir, 'rules');
|
|
122
|
-
|
|
123
|
-
console.log('\n Removing old rules...');
|
|
199
|
+
// Get all rules to install
|
|
200
|
+
const allRules = getRulesForTypes(newDetected);
|
|
201
|
+
const newRules = getRulesForTypes(added);
|
|
202
|
+
|
|
203
|
+
// Update each tool
|
|
204
|
+
console.log('\n Updating installations...');
|
|
205
|
+
const results = [];
|
|
206
|
+
|
|
207
|
+
for (const { toolId, tool } of installedTools) {
|
|
208
|
+
const result = updateToolInstallation(toolId, tool, projectDir, {
|
|
209
|
+
...options,
|
|
210
|
+
rules: newRules,
|
|
211
|
+
removedRules: removed,
|
|
212
|
+
allRules,
|
|
213
|
+
detectedTypes: newDetected
|
|
214
|
+
});
|
|
124
215
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
console.log(` Removed: ${ruleType}`);
|
|
130
|
-
}
|
|
216
|
+
if (result.success) {
|
|
217
|
+
console.log(` [+] ${tool.name}: ${result.updatedFiles} files (${result.sizeKB} KB)`);
|
|
218
|
+
} else {
|
|
219
|
+
console.log(` [-] ${tool.name}: ${result.reason}`);
|
|
131
220
|
}
|
|
132
|
-
}
|
|
133
221
|
|
|
134
|
-
|
|
135
|
-
if (options.force) {
|
|
136
|
-
console.log('\n Updating commands...');
|
|
137
|
-
const mergedCommandsDir = path.join(PACKAGE_ROOT, 'merged-commands');
|
|
138
|
-
const commandsDir = path.join(claudeDir, 'commands');
|
|
139
|
-
|
|
140
|
-
if (fs.existsSync(mergedCommandsDir)) {
|
|
141
|
-
// Backup existing
|
|
142
|
-
const backupDir = path.join(claudeDir, 'commands.backup');
|
|
143
|
-
if (fs.existsSync(commandsDir)) {
|
|
144
|
-
fs.renameSync(commandsDir, backupDir);
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
const count = copyDir(mergedCommandsDir, commandsDir);
|
|
148
|
-
console.log(` Commands: ${count} files`);
|
|
149
|
-
|
|
150
|
-
// Remove backup
|
|
151
|
-
if (fs.existsSync(backupDir)) {
|
|
152
|
-
fs.rmSync(backupDir, { recursive: true, force: true });
|
|
153
|
-
}
|
|
154
|
-
}
|
|
222
|
+
results.push(result);
|
|
155
223
|
}
|
|
156
224
|
|
|
157
|
-
//
|
|
158
|
-
const
|
|
159
|
-
|
|
160
|
-
version: VERSION,
|
|
161
|
-
detectedTypes: newDetected,
|
|
162
|
-
installedRules: getRulesForTypes(newDetected),
|
|
163
|
-
updatedAt: new Date().toISOString(),
|
|
164
|
-
stats: {
|
|
165
|
-
totalFiles: config.stats?.totalFiles || 0,
|
|
166
|
-
sizeKB: Math.round(getDirSize(claudeDir) / 1024)
|
|
167
|
-
}
|
|
168
|
-
};
|
|
169
|
-
|
|
170
|
-
fs.writeFileSync(configPath, JSON.stringify(newConfig, null, 2));
|
|
225
|
+
// Summary
|
|
226
|
+
const successCount = results.filter(r => r.success).length;
|
|
227
|
+
const totalSize = results.reduce((sum, r) => sum + (r.sizeKB || 0), 0);
|
|
171
228
|
|
|
172
229
|
console.log('\n' + '='.repeat(60));
|
|
173
230
|
console.log(' UPDATE COMPLETE');
|
|
174
231
|
console.log('='.repeat(60));
|
|
175
|
-
console.log(`\n
|
|
232
|
+
console.log(`\n Tools updated: ${successCount}/${installedTools.length}`);
|
|
233
|
+
console.log(` Total size: ${totalSize} KB`);
|
|
176
234
|
console.log(` Detected: ${newDetected.join(', ') || 'generic'}\n`);
|
|
177
235
|
|
|
178
236
|
return {
|
|
179
|
-
success:
|
|
237
|
+
success: successCount > 0,
|
|
238
|
+
tools: results,
|
|
180
239
|
added,
|
|
181
240
|
removed,
|
|
182
241
|
detected: newDetected
|
package/package.json
CHANGED
|
@@ -20,7 +20,9 @@ const OUTPUT_FILE = path.join(__dirname, '..', 'SKILLS_INDEX.md');
|
|
|
20
20
|
* Parse YAML frontmatter from markdown file
|
|
21
21
|
*/
|
|
22
22
|
function parseFrontmatter(content) {
|
|
23
|
-
|
|
23
|
+
// Normalize line endings to LF
|
|
24
|
+
const normalized = content.replace(/\r\n/g, '\n').replace(/\r/g, '\n');
|
|
25
|
+
const match = normalized.match(/^---\n([\s\S]*?)\n---/);
|
|
24
26
|
if (!match) return {};
|
|
25
27
|
|
|
26
28
|
const yaml = match[1];
|