@ngxtm/devkit 3.6.1 → 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/update.js +145 -77
- package/package.json +1 -1
- package/scripts/generate-index.js +3 -1
- package/skills-index.json +2248 -463
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,82 +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 (enabled by default, use --no-clean to skip)
|
|
120
|
-
if (removed.length > 0 && options.clean !== false) {
|
|
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 !== false) {
|
|
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
|
-
// Copy directly - copyDir will overwrite existing files
|
|
142
|
-
// No need for backup/rename which fails on Windows when files are open
|
|
143
|
-
const count = copyDir(mergedCommandsDir, commandsDir);
|
|
144
|
-
console.log(` Commands: ${count} files`);
|
|
145
|
-
}
|
|
222
|
+
results.push(result);
|
|
146
223
|
}
|
|
147
224
|
|
|
148
|
-
//
|
|
149
|
-
const
|
|
150
|
-
|
|
151
|
-
version: VERSION,
|
|
152
|
-
detectedTypes: newDetected,
|
|
153
|
-
installedRules: getRulesForTypes(newDetected),
|
|
154
|
-
updatedAt: new Date().toISOString(),
|
|
155
|
-
stats: {
|
|
156
|
-
totalFiles: config.stats?.totalFiles || 0,
|
|
157
|
-
sizeKB: Math.round(getDirSize(claudeDir) / 1024)
|
|
158
|
-
}
|
|
159
|
-
};
|
|
160
|
-
|
|
161
|
-
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);
|
|
162
228
|
|
|
163
229
|
console.log('\n' + '='.repeat(60));
|
|
164
230
|
console.log(' UPDATE COMPLETE');
|
|
165
231
|
console.log('='.repeat(60));
|
|
166
|
-
console.log(`\n
|
|
232
|
+
console.log(`\n Tools updated: ${successCount}/${installedTools.length}`);
|
|
233
|
+
console.log(` Total size: ${totalSize} KB`);
|
|
167
234
|
console.log(` Detected: ${newDetected.join(', ') || 'generic'}\n`);
|
|
168
235
|
|
|
169
236
|
return {
|
|
170
|
-
success:
|
|
237
|
+
success: successCount > 0,
|
|
238
|
+
tools: results,
|
|
171
239
|
added,
|
|
172
240
|
removed,
|
|
173
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];
|