@lenne.tech/cli 1.2.0 → 1.3.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/build/commands/claude/install-plugin.js +339 -0
- package/package.json +1 -1
- package/build/commands/claude/install-commands.js +0 -337
- package/build/commands/claude/install-mcps.js +0 -258
- package/build/commands/claude/install-skills.js +0 -693
- package/build/lib/mcp-registry.js +0 -80
- package/build/templates/claude-commands/code-cleanup.md +0 -82
- package/build/templates/claude-commands/commit-message.md +0 -21
- package/build/templates/claude-commands/create-story.md +0 -435
- package/build/templates/claude-commands/mr-description-clipboard.md +0 -48
- package/build/templates/claude-commands/mr-description.md +0 -33
- package/build/templates/claude-commands/sec-review.md +0 -62
- package/build/templates/claude-commands/skill-optimize.md +0 -481
- package/build/templates/claude-commands/test-generate.md +0 -45
- package/build/templates/claude-skills/building-stories-with-tdd/SKILL.md +0 -265
- package/build/templates/claude-skills/building-stories-with-tdd/code-quality.md +0 -276
- package/build/templates/claude-skills/building-stories-with-tdd/database-indexes.md +0 -182
- package/build/templates/claude-skills/building-stories-with-tdd/examples.md +0 -1383
- package/build/templates/claude-skills/building-stories-with-tdd/handling-existing-tests.md +0 -197
- package/build/templates/claude-skills/building-stories-with-tdd/reference.md +0 -1427
- package/build/templates/claude-skills/building-stories-with-tdd/security-review.md +0 -307
- package/build/templates/claude-skills/building-stories-with-tdd/workflow.md +0 -1004
- package/build/templates/claude-skills/generating-nest-servers/SKILL.md +0 -303
- package/build/templates/claude-skills/generating-nest-servers/configuration.md +0 -285
- package/build/templates/claude-skills/generating-nest-servers/declare-keyword-warning.md +0 -133
- package/build/templates/claude-skills/generating-nest-servers/description-management.md +0 -226
- package/build/templates/claude-skills/generating-nest-servers/examples.md +0 -893
- package/build/templates/claude-skills/generating-nest-servers/framework-guide.md +0 -259
- package/build/templates/claude-skills/generating-nest-servers/quality-review.md +0 -864
- package/build/templates/claude-skills/generating-nest-servers/reference.md +0 -487
- package/build/templates/claude-skills/generating-nest-servers/security-rules.md +0 -371
- package/build/templates/claude-skills/generating-nest-servers/verification-checklist.md +0 -262
- package/build/templates/claude-skills/generating-nest-servers/workflow-process.md +0 -1061
- package/build/templates/claude-skills/using-lt-cli/SKILL.md +0 -284
- package/build/templates/claude-skills/using-lt-cli/examples.md +0 -546
- package/build/templates/claude-skills/using-lt-cli/reference.md +0 -513
|
@@ -1,693 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
-
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
-
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
-
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
-
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
-
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
-
});
|
|
10
|
-
};
|
|
11
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
-
const os_1 = require("os");
|
|
13
|
-
const path_1 = require("path");
|
|
14
|
-
/**
|
|
15
|
-
* Skill-specific permissions mapping
|
|
16
|
-
*/
|
|
17
|
-
const SKILL_PERMISSIONS = {
|
|
18
|
-
'building-stories-with-tdd': [
|
|
19
|
-
'Bash(npm test:*)',
|
|
20
|
-
'Bash(npm run test:*)',
|
|
21
|
-
],
|
|
22
|
-
'generating-nest-servers': [
|
|
23
|
-
'Bash(lt server:*)',
|
|
24
|
-
],
|
|
25
|
-
'using-lt-cli': [
|
|
26
|
-
'Bash(lt:*)',
|
|
27
|
-
],
|
|
28
|
-
};
|
|
29
|
-
/**
|
|
30
|
-
* Mapping of old skill names to new names (for cleanup of renamed skills)
|
|
31
|
-
*/
|
|
32
|
-
const LEGACY_SKILL_NAMES = {
|
|
33
|
-
'lt-cli': 'using-lt-cli',
|
|
34
|
-
'nest-server-generator': 'generating-nest-servers',
|
|
35
|
-
'story-tdd': 'building-stories-with-tdd',
|
|
36
|
-
};
|
|
37
|
-
/**
|
|
38
|
-
* Check for and optionally remove legacy skill directories
|
|
39
|
-
* Returns list of deleted legacy skills
|
|
40
|
-
*/
|
|
41
|
-
function cleanupLegacySkills(filesystem, info, prompt, skipInteractive) {
|
|
42
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
43
|
-
const skillsBaseDir = (0, path_1.join)((0, os_1.homedir)(), '.claude', 'skills');
|
|
44
|
-
const deletedSkills = [];
|
|
45
|
-
// Check if skills directory exists
|
|
46
|
-
if (!filesystem.exists(skillsBaseDir)) {
|
|
47
|
-
return deletedSkills;
|
|
48
|
-
}
|
|
49
|
-
// Find existing legacy skill directories
|
|
50
|
-
const existingLegacySkills = [];
|
|
51
|
-
for (const [oldName, newName] of Object.entries(LEGACY_SKILL_NAMES)) {
|
|
52
|
-
const legacyPath = (0, path_1.join)(skillsBaseDir, oldName);
|
|
53
|
-
if (filesystem.exists(legacyPath) && filesystem.isDirectory(legacyPath)) {
|
|
54
|
-
existingLegacySkills.push({ newName, oldName, path: legacyPath });
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
if (existingLegacySkills.length === 0) {
|
|
58
|
-
return deletedSkills;
|
|
59
|
-
}
|
|
60
|
-
// Show found legacy skills
|
|
61
|
-
info('');
|
|
62
|
-
info('Found legacy skill directories (renamed skills):');
|
|
63
|
-
existingLegacySkills.forEach(({ newName, oldName }) => {
|
|
64
|
-
info(` • ${oldName} → ${newName}`);
|
|
65
|
-
});
|
|
66
|
-
info('');
|
|
67
|
-
// Ask if user wants to delete them (default: yes)
|
|
68
|
-
const shouldDelete = skipInteractive ? true : yield prompt.confirm('Delete these old skill directories?', true);
|
|
69
|
-
if (shouldDelete) {
|
|
70
|
-
for (const { oldName, path } of existingLegacySkills) {
|
|
71
|
-
try {
|
|
72
|
-
filesystem.remove(path);
|
|
73
|
-
deletedSkills.push(oldName);
|
|
74
|
-
info(` ✓ Deleted ${oldName}`);
|
|
75
|
-
}
|
|
76
|
-
catch (err) {
|
|
77
|
-
info(` ✗ Could not delete ${oldName}: ${err.message}`);
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
if (deletedSkills.length > 0) {
|
|
81
|
-
info('');
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
return deletedSkills;
|
|
85
|
-
});
|
|
86
|
-
}
|
|
87
|
-
/**
|
|
88
|
-
* Get skill descriptions from SKILL.md frontmatter
|
|
89
|
-
*/
|
|
90
|
-
function getSkillDescription(skillDir, filesystem) {
|
|
91
|
-
const skillMdPath = (0, path_1.join)(skillDir, 'SKILL.md');
|
|
92
|
-
if (!filesystem.exists(skillMdPath)) {
|
|
93
|
-
return 'No description available';
|
|
94
|
-
}
|
|
95
|
-
const content = filesystem.read(skillMdPath);
|
|
96
|
-
const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---/);
|
|
97
|
-
if (!frontmatterMatch) {
|
|
98
|
-
return 'No description available';
|
|
99
|
-
}
|
|
100
|
-
const frontmatter = frontmatterMatch[1];
|
|
101
|
-
const descMatch = frontmatter.match(/description:\s*([^\n]+)/);
|
|
102
|
-
return descMatch ? descMatch[1].trim() : 'No description available';
|
|
103
|
-
}
|
|
104
|
-
/**
|
|
105
|
-
* Install a single skill
|
|
106
|
-
*/
|
|
107
|
-
function installSingleSkill(skillName, cliRoot, filesystem, info, error) {
|
|
108
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
109
|
-
const templatesDir = (0, path_1.join)(cliRoot, 'templates', 'claude-skills', skillName);
|
|
110
|
-
// Check if templates exist
|
|
111
|
-
if (!filesystem.exists(templatesDir)) {
|
|
112
|
-
error(`Skill '${skillName}' not found in CLI installation.`);
|
|
113
|
-
info(`Expected location: ${templatesDir}`);
|
|
114
|
-
return { copiedCount: 0, skippedCount: 0, skippedFiles: [], success: false, updatedCount: 0 };
|
|
115
|
-
}
|
|
116
|
-
// Create ~/.claude/skills/<skillName> directory
|
|
117
|
-
const skillsDir = (0, path_1.join)((0, os_1.homedir)(), '.claude', 'skills', skillName);
|
|
118
|
-
if (!filesystem.exists(skillsDir)) {
|
|
119
|
-
filesystem.dir(skillsDir);
|
|
120
|
-
}
|
|
121
|
-
// Copy all skill files with version checking
|
|
122
|
-
const skillFiles = ['SKILL.md', 'examples.md', 'reference.md'];
|
|
123
|
-
let copiedCount = 0;
|
|
124
|
-
let skippedCount = 0;
|
|
125
|
-
let updatedCount = 0;
|
|
126
|
-
const skippedFiles = [];
|
|
127
|
-
info(`\nInstalling skill: ${skillName}`);
|
|
128
|
-
for (const file of skillFiles) {
|
|
129
|
-
const sourcePath = (0, path_1.join)(templatesDir, file);
|
|
130
|
-
const targetPath = (0, path_1.join)(skillsDir, file);
|
|
131
|
-
if (!filesystem.exists(sourcePath)) {
|
|
132
|
-
info(` Warning: ${file} not found in templates, skipping...`);
|
|
133
|
-
continue;
|
|
134
|
-
}
|
|
135
|
-
const sourceContent = filesystem.read(sourcePath);
|
|
136
|
-
// Check if target file exists
|
|
137
|
-
if (filesystem.exists(targetPath)) {
|
|
138
|
-
const targetContent = filesystem.read(targetPath);
|
|
139
|
-
// Parse versions from both files
|
|
140
|
-
const sourceVersion = parseVersion(sourceContent);
|
|
141
|
-
const targetVersion = parseVersion(targetContent);
|
|
142
|
-
// If both have versions, compare them
|
|
143
|
-
if (sourceVersion && targetVersion) {
|
|
144
|
-
if (isVersionNewer(sourceVersion, targetVersion)) {
|
|
145
|
-
// Source is newer or equal, update
|
|
146
|
-
filesystem.write(targetPath, sourceContent);
|
|
147
|
-
updatedCount++;
|
|
148
|
-
copiedCount++;
|
|
149
|
-
info(` ✓ Updated ${file} (${targetVersion} → ${sourceVersion})`);
|
|
150
|
-
}
|
|
151
|
-
else {
|
|
152
|
-
// Target is newer, skip
|
|
153
|
-
skippedCount++;
|
|
154
|
-
const reason = `local version ${targetVersion} is newer than ${sourceVersion}`;
|
|
155
|
-
skippedFiles.push({ file: `${skillName}/${file}`, reason });
|
|
156
|
-
info(` ⊙ Skipped ${file} (${reason})`);
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
else if (sourceVersion && !targetVersion) {
|
|
160
|
-
// Source has version, target doesn't - update
|
|
161
|
-
filesystem.write(targetPath, sourceContent);
|
|
162
|
-
updatedCount++;
|
|
163
|
-
copiedCount++;
|
|
164
|
-
info(` ✓ Updated ${file} (no version → ${sourceVersion})`);
|
|
165
|
-
}
|
|
166
|
-
else {
|
|
167
|
-
// No version info, always update (backward compatibility)
|
|
168
|
-
filesystem.write(targetPath, sourceContent);
|
|
169
|
-
updatedCount++;
|
|
170
|
-
copiedCount++;
|
|
171
|
-
info(` ✓ Updated ${file} (no version control)`);
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
else {
|
|
175
|
-
// Target doesn't exist, create new
|
|
176
|
-
filesystem.write(targetPath, sourceContent);
|
|
177
|
-
copiedCount++;
|
|
178
|
-
const version = parseVersion(sourceContent);
|
|
179
|
-
info(` + Created ${file}${version ? ` (v${version})` : ''}`);
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
return { copiedCount, skippedCount, skippedFiles, success: true, updatedCount };
|
|
183
|
-
});
|
|
184
|
-
}
|
|
185
|
-
/**
|
|
186
|
-
* Compare semantic versions
|
|
187
|
-
* Returns true if sourceVersion >= targetVersion
|
|
188
|
-
*/
|
|
189
|
-
function isVersionNewer(sourceVersion, targetVersion) {
|
|
190
|
-
const parseSemver = (version) => {
|
|
191
|
-
return version.split('.').map(n => parseInt(n, 10) || 0);
|
|
192
|
-
};
|
|
193
|
-
const source = parseSemver(sourceVersion);
|
|
194
|
-
const target = parseSemver(targetVersion);
|
|
195
|
-
// Compare major, minor, patch
|
|
196
|
-
for (let i = 0; i < 3; i++) {
|
|
197
|
-
if (source[i] > target[i]) {
|
|
198
|
-
return true;
|
|
199
|
-
}
|
|
200
|
-
if (source[i] < target[i]) {
|
|
201
|
-
return false;
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
// Versions are equal
|
|
205
|
-
return true;
|
|
206
|
-
}
|
|
207
|
-
/**
|
|
208
|
-
* Parse frontmatter from markdown file and extract version
|
|
209
|
-
*/
|
|
210
|
-
function parseVersion(content) {
|
|
211
|
-
const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---/);
|
|
212
|
-
if (!frontmatterMatch) {
|
|
213
|
-
return null;
|
|
214
|
-
}
|
|
215
|
-
const frontmatter = frontmatterMatch[1];
|
|
216
|
-
const versionMatch = frontmatter.match(/version:\s*([^\n]+)/);
|
|
217
|
-
return versionMatch ? versionMatch[1].trim() : null;
|
|
218
|
-
}
|
|
219
|
-
/**
|
|
220
|
-
* Setup project detection hook for nest-server-generator
|
|
221
|
-
*/
|
|
222
|
-
function setupProjectDetectionHook(filesystem, info, error, promptConfirm, skipInteractive) {
|
|
223
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
224
|
-
const globalSettingsPath = (0, path_1.join)((0, os_1.homedir)(), '.claude', 'settings.json');
|
|
225
|
-
const cwd = filesystem.cwd();
|
|
226
|
-
// Detect if we're in a project with package.json
|
|
227
|
-
let packageJsonPath = null;
|
|
228
|
-
let searchDir = cwd;
|
|
229
|
-
// Search up to 3 levels for package.json
|
|
230
|
-
for (let i = 0; i < 3; i++) {
|
|
231
|
-
const testPath = (0, path_1.join)(searchDir, 'package.json');
|
|
232
|
-
if (filesystem.exists(testPath)) {
|
|
233
|
-
packageJsonPath = testPath;
|
|
234
|
-
break;
|
|
235
|
-
}
|
|
236
|
-
const parent = (0, path_1.join)(searchDir, '..');
|
|
237
|
-
if (parent === searchDir)
|
|
238
|
-
break; // Reached root
|
|
239
|
-
searchDir = parent;
|
|
240
|
-
}
|
|
241
|
-
// Determine installation scope
|
|
242
|
-
let scope = 'global';
|
|
243
|
-
let settingsPath = globalSettingsPath;
|
|
244
|
-
let projectRoot = null;
|
|
245
|
-
if (packageJsonPath) {
|
|
246
|
-
projectRoot = searchDir;
|
|
247
|
-
const projectSettingsPath = (0, path_1.join)(projectRoot, '.claude', 'settings.json');
|
|
248
|
-
// Ask where to install the hook
|
|
249
|
-
if (!skipInteractive) {
|
|
250
|
-
info('');
|
|
251
|
-
info(`Detected project at: ${projectRoot}`);
|
|
252
|
-
const choices = [
|
|
253
|
-
{ name: 'Global (~/.claude/settings.json) - Available for all projects', value: 'global' },
|
|
254
|
-
{ name: `Project (${projectRoot}/.claude/settings.json) - Only this project`, value: 'project' }
|
|
255
|
-
];
|
|
256
|
-
const answer = yield promptConfirm({
|
|
257
|
-
choices: choices.map(c => c.name),
|
|
258
|
-
initial: 1, // Default to project
|
|
259
|
-
message: 'Where should the project detection hook be installed?',
|
|
260
|
-
name: 'scope',
|
|
261
|
-
type: 'select',
|
|
262
|
-
});
|
|
263
|
-
// Map the choice back to the value
|
|
264
|
-
scope = choices[answer.scope === 0 ? 0 : 1].value;
|
|
265
|
-
}
|
|
266
|
-
else {
|
|
267
|
-
// In non-interactive mode, prefer project if we found one
|
|
268
|
-
scope = 'project';
|
|
269
|
-
}
|
|
270
|
-
if (scope === 'project') {
|
|
271
|
-
settingsPath = projectSettingsPath;
|
|
272
|
-
// Ensure .claude directory exists
|
|
273
|
-
filesystem.dir((0, path_1.join)(projectRoot, '.claude'));
|
|
274
|
-
}
|
|
275
|
-
}
|
|
276
|
-
try {
|
|
277
|
-
// Read existing settings
|
|
278
|
-
let settings = {};
|
|
279
|
-
if (filesystem.exists(settingsPath)) {
|
|
280
|
-
const content = filesystem.read(settingsPath);
|
|
281
|
-
settings = JSON.parse(content);
|
|
282
|
-
}
|
|
283
|
-
// Ensure hooks array exists
|
|
284
|
-
if (!settings.hooks) {
|
|
285
|
-
settings.hooks = [];
|
|
286
|
-
}
|
|
287
|
-
// Check if hook already exists (check both old and new names for backward compatibility)
|
|
288
|
-
const hookExists = settings.hooks.some((hook) => hook.event === 'user-prompt-submit' &&
|
|
289
|
-
(hook.name === 'nest-server-detector' || hook.name === 'generating-nest-servers-detector'));
|
|
290
|
-
if (hookExists) {
|
|
291
|
-
return {
|
|
292
|
-
added: false,
|
|
293
|
-
alreadyExists: true,
|
|
294
|
-
scope,
|
|
295
|
-
success: true,
|
|
296
|
-
};
|
|
297
|
-
}
|
|
298
|
-
// Create the hook configuration
|
|
299
|
-
const hook = {
|
|
300
|
-
command: `
|
|
301
|
-
# Detect @lenne.tech/nest-server in package.json and suggest using generating-nest-servers skill
|
|
302
|
-
# Supports both single projects and monorepos
|
|
303
|
-
|
|
304
|
-
# Check if the prompt mentions NestJS-related tasks first
|
|
305
|
-
if ! echo "$PROMPT" | grep -qiE "(module|service|controller|resolver|model|object|nestjs|nest-server|lt server)"; then
|
|
306
|
-
# Not a NestJS-related prompt, skip
|
|
307
|
-
echo '{}'
|
|
308
|
-
exit 0
|
|
309
|
-
fi
|
|
310
|
-
|
|
311
|
-
# Function to check if package.json contains @lenne.tech/nest-server
|
|
312
|
-
check_package_json() {
|
|
313
|
-
local pkg_json="$1"
|
|
314
|
-
if [ -f "$pkg_json" ] && grep -q "@lenne\\.tech/nest-server" "$pkg_json"; then
|
|
315
|
-
return 0
|
|
316
|
-
fi
|
|
317
|
-
return 1
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
# First, check package.json in project root
|
|
321
|
-
if check_package_json "$CLAUDE_PROJECT_DIR/package.json"; then
|
|
322
|
-
cat << 'EOF'
|
|
323
|
-
{
|
|
324
|
-
"contextToAppend": "\\n\\n📦 Detected @lenne.tech/nest-server in this project. Consider using the generating-nest-servers skill for this task."
|
|
325
|
-
}
|
|
326
|
-
EOF
|
|
327
|
-
exit 0
|
|
328
|
-
fi
|
|
329
|
-
|
|
330
|
-
# If not found in root, check common monorepo patterns
|
|
331
|
-
for pattern in "projects/*/package.json" "packages/*/package.json" "apps/*/package.json"; do
|
|
332
|
-
for pkg_json in $CLAUDE_PROJECT_DIR/$pattern; do
|
|
333
|
-
if check_package_json "$pkg_json"; then
|
|
334
|
-
cat << 'EOF'
|
|
335
|
-
{
|
|
336
|
-
"contextToAppend": "\\n\\n📦 Detected @lenne.tech/nest-server in this monorepo. Consider using the generating-nest-servers skill for this task."
|
|
337
|
-
}
|
|
338
|
-
EOF
|
|
339
|
-
exit 0
|
|
340
|
-
fi
|
|
341
|
-
done
|
|
342
|
-
done
|
|
343
|
-
|
|
344
|
-
# No @lenne.tech/nest-server found
|
|
345
|
-
echo '{}'
|
|
346
|
-
exit 0
|
|
347
|
-
`.trim(),
|
|
348
|
-
description: 'Detects projects using @lenne.tech/nest-server and suggests using generating-nest-servers skill',
|
|
349
|
-
event: 'user-prompt-submit',
|
|
350
|
-
name: 'generating-nest-servers-detector',
|
|
351
|
-
type: 'command',
|
|
352
|
-
};
|
|
353
|
-
// Add the hook
|
|
354
|
-
settings.hooks.push(hook);
|
|
355
|
-
// Write back settings
|
|
356
|
-
filesystem.write(settingsPath, JSON.stringify(settings, null, 2));
|
|
357
|
-
return {
|
|
358
|
-
added: true,
|
|
359
|
-
alreadyExists: false,
|
|
360
|
-
scope,
|
|
361
|
-
success: true,
|
|
362
|
-
};
|
|
363
|
-
}
|
|
364
|
-
catch (err) {
|
|
365
|
-
error(`Could not configure project detection hook: ${err.message}`);
|
|
366
|
-
return {
|
|
367
|
-
added: false,
|
|
368
|
-
alreadyExists: false,
|
|
369
|
-
error: true,
|
|
370
|
-
scope: 'none',
|
|
371
|
-
success: false,
|
|
372
|
-
};
|
|
373
|
-
}
|
|
374
|
-
});
|
|
375
|
-
}
|
|
376
|
-
/**
|
|
377
|
-
* Setup global permissions for installed skills
|
|
378
|
-
*/
|
|
379
|
-
function setupSkillPermissions(skills, filesystem, error) {
|
|
380
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
381
|
-
const settingsPath = (0, path_1.join)((0, os_1.homedir)(), '.claude', 'settings.json');
|
|
382
|
-
// Collect all requested permissions
|
|
383
|
-
const requestedPermissions = [];
|
|
384
|
-
skills.forEach(skill => {
|
|
385
|
-
const perms = SKILL_PERMISSIONS[skill];
|
|
386
|
-
if (perms) {
|
|
387
|
-
requestedPermissions.push(...perms);
|
|
388
|
-
}
|
|
389
|
-
});
|
|
390
|
-
if (requestedPermissions.length === 0) {
|
|
391
|
-
return { added: [], error: false, existing: [], requested: [], success: true };
|
|
392
|
-
}
|
|
393
|
-
try {
|
|
394
|
-
// Read existing settings
|
|
395
|
-
let settings = {};
|
|
396
|
-
if (filesystem.exists(settingsPath)) {
|
|
397
|
-
const content = filesystem.read(settingsPath);
|
|
398
|
-
settings = JSON.parse(content);
|
|
399
|
-
}
|
|
400
|
-
// Ensure permissions.allow exists
|
|
401
|
-
if (!settings.permissions) {
|
|
402
|
-
settings.permissions = {};
|
|
403
|
-
}
|
|
404
|
-
if (!settings.permissions.allow) {
|
|
405
|
-
settings.permissions.allow = [];
|
|
406
|
-
}
|
|
407
|
-
// Check which permissions are new vs existing
|
|
408
|
-
const existingPerms = new Set(settings.permissions.allow);
|
|
409
|
-
const added = [];
|
|
410
|
-
const existing = [];
|
|
411
|
-
requestedPermissions.forEach(perm => {
|
|
412
|
-
if (existingPerms.has(perm)) {
|
|
413
|
-
existing.push(perm);
|
|
414
|
-
}
|
|
415
|
-
else {
|
|
416
|
-
added.push(perm);
|
|
417
|
-
settings.permissions.allow.push(perm);
|
|
418
|
-
}
|
|
419
|
-
});
|
|
420
|
-
// Write back settings
|
|
421
|
-
filesystem.write(settingsPath, JSON.stringify(settings, null, 2));
|
|
422
|
-
return {
|
|
423
|
-
added,
|
|
424
|
-
existing,
|
|
425
|
-
requested: requestedPermissions,
|
|
426
|
-
success: true,
|
|
427
|
-
};
|
|
428
|
-
}
|
|
429
|
-
catch (err) {
|
|
430
|
-
error(`Could not configure permissions: ${err.message}`);
|
|
431
|
-
return {
|
|
432
|
-
added: [],
|
|
433
|
-
error: true,
|
|
434
|
-
existing: [],
|
|
435
|
-
requested: requestedPermissions,
|
|
436
|
-
success: false,
|
|
437
|
-
};
|
|
438
|
-
}
|
|
439
|
-
});
|
|
440
|
-
}
|
|
441
|
-
/**
|
|
442
|
-
* Install Claude Skills to ~/.claude/skills/
|
|
443
|
-
*/
|
|
444
|
-
const NewCommand = {
|
|
445
|
-
alias: ['skills', 'is'],
|
|
446
|
-
description: 'Installs Claude Skills to ~/.claude/skills/ for Claude Code integration. Interactively select which skills to install. Use -y to skip interactive selection and install all skills.',
|
|
447
|
-
hidden: false,
|
|
448
|
-
name: 'install-skills',
|
|
449
|
-
run: (toolbox) => __awaiter(void 0, void 0, void 0, function* () {
|
|
450
|
-
// Retrieve the tools we need
|
|
451
|
-
const { filesystem, parameters, print: { error, info, spin, success }, prompt, } = toolbox;
|
|
452
|
-
try {
|
|
453
|
-
// Get the CLI installation directory
|
|
454
|
-
const cliRoot = (0, path_1.join)(__dirname, '..', '..');
|
|
455
|
-
const claudeSkillsDir = (0, path_1.join)(cliRoot, 'templates', 'claude-skills');
|
|
456
|
-
// Check if claude-skills directory exists
|
|
457
|
-
if (!filesystem.exists(claudeSkillsDir)) {
|
|
458
|
-
error('Claude skills directory not found in CLI installation.');
|
|
459
|
-
info(`Expected location: ${claudeSkillsDir}`);
|
|
460
|
-
info('Please reinstall the CLI or report this issue.');
|
|
461
|
-
return;
|
|
462
|
-
}
|
|
463
|
-
// Get all available skills
|
|
464
|
-
const items = filesystem.list(claudeSkillsDir);
|
|
465
|
-
const availableSkills = items.filter(item => filesystem.isDirectory((0, path_1.join)(claudeSkillsDir, item)));
|
|
466
|
-
if (availableSkills.length === 0) {
|
|
467
|
-
error('No skills found in CLI installation.');
|
|
468
|
-
return;
|
|
469
|
-
}
|
|
470
|
-
const skipInteractive = parameters.options.y || parameters.options.yes || parameters.options['no-interactive'];
|
|
471
|
-
// Check for and cleanup legacy skill directories before installation
|
|
472
|
-
yield cleanupLegacySkills(filesystem, info, prompt, skipInteractive);
|
|
473
|
-
let skillsToInstall = [];
|
|
474
|
-
// Check if specific skills provided as parameters
|
|
475
|
-
if (parameters.first && parameters.first !== 'all') {
|
|
476
|
-
// Non-interactive mode: install specific skill(s)
|
|
477
|
-
const requestedSkills = parameters.array || [parameters.first];
|
|
478
|
-
// Validate requested skills
|
|
479
|
-
const invalidSkills = requestedSkills.filter(s => !availableSkills.includes(s));
|
|
480
|
-
if (invalidSkills.length > 0) {
|
|
481
|
-
error(`Invalid skill(s): ${invalidSkills.join(', ')}`);
|
|
482
|
-
info('');
|
|
483
|
-
info('Available skills:');
|
|
484
|
-
availableSkills.forEach(s => {
|
|
485
|
-
const desc = getSkillDescription((0, path_1.join)(claudeSkillsDir, s), filesystem);
|
|
486
|
-
info(` • ${s}`);
|
|
487
|
-
info(` ${desc}`);
|
|
488
|
-
});
|
|
489
|
-
return;
|
|
490
|
-
}
|
|
491
|
-
skillsToInstall = requestedSkills;
|
|
492
|
-
}
|
|
493
|
-
else if (parameters.first === 'all' || skipInteractive) {
|
|
494
|
-
// Install all skills without prompting
|
|
495
|
-
skillsToInstall = availableSkills;
|
|
496
|
-
if (skipInteractive) {
|
|
497
|
-
info('Installing all skills (non-interactive mode)...');
|
|
498
|
-
}
|
|
499
|
-
}
|
|
500
|
-
else {
|
|
501
|
-
// Interactive mode: ask for each skill individually
|
|
502
|
-
info('');
|
|
503
|
-
info('Available skills:');
|
|
504
|
-
info('');
|
|
505
|
-
// Show all available skills with descriptions
|
|
506
|
-
availableSkills.forEach(skill => {
|
|
507
|
-
const desc = getSkillDescription((0, path_1.join)(claudeSkillsDir, skill), filesystem);
|
|
508
|
-
info(` • ${skill}`);
|
|
509
|
-
info(` ${desc.substring(0, 100)}${desc.length > 100 ? '...' : ''}`);
|
|
510
|
-
info('');
|
|
511
|
-
});
|
|
512
|
-
// Ask if user wants to install all or select individually
|
|
513
|
-
const installAll = yield prompt.confirm('Install all skills?', true);
|
|
514
|
-
if (installAll) {
|
|
515
|
-
skillsToInstall = availableSkills;
|
|
516
|
-
}
|
|
517
|
-
else {
|
|
518
|
-
// Ask for each skill
|
|
519
|
-
info('');
|
|
520
|
-
info('Select which skills to install:');
|
|
521
|
-
info('');
|
|
522
|
-
for (const skill of availableSkills) {
|
|
523
|
-
const shouldInstall = yield prompt.confirm(`Install ${skill}?`, true);
|
|
524
|
-
if (shouldInstall) {
|
|
525
|
-
skillsToInstall.push(skill);
|
|
526
|
-
}
|
|
527
|
-
}
|
|
528
|
-
if (skillsToInstall.length === 0) {
|
|
529
|
-
info('No skills selected. Installation cancelled.');
|
|
530
|
-
return;
|
|
531
|
-
}
|
|
532
|
-
}
|
|
533
|
-
}
|
|
534
|
-
const installSpinner = spin(`Installing ${skillsToInstall.length} skill(s)...`);
|
|
535
|
-
let totalCopied = 0;
|
|
536
|
-
let totalSkipped = 0;
|
|
537
|
-
let totalUpdated = 0;
|
|
538
|
-
const allSkippedFiles = [];
|
|
539
|
-
// Install each skill
|
|
540
|
-
for (const skill of skillsToInstall) {
|
|
541
|
-
const result = yield installSingleSkill(skill, cliRoot, filesystem, info, error);
|
|
542
|
-
if (!result.success) {
|
|
543
|
-
continue;
|
|
544
|
-
}
|
|
545
|
-
totalCopied += result.copiedCount;
|
|
546
|
-
totalSkipped += result.skippedCount;
|
|
547
|
-
totalUpdated += result.updatedCount;
|
|
548
|
-
allSkippedFiles.push(...result.skippedFiles);
|
|
549
|
-
}
|
|
550
|
-
if (totalCopied === 0 && totalSkipped === 0) {
|
|
551
|
-
installSpinner.fail();
|
|
552
|
-
error('No skill files were found.');
|
|
553
|
-
return;
|
|
554
|
-
}
|
|
555
|
-
if (totalCopied === 0 && totalSkipped > 0) {
|
|
556
|
-
installSpinner.succeed('All selected skills are already up to date!');
|
|
557
|
-
info('');
|
|
558
|
-
info(`Skipped: ${totalSkipped} file(s) across ${skillsToInstall.length} skill(s)`);
|
|
559
|
-
info(`Location: ${(0, path_1.join)((0, os_1.homedir)(), '.claude', 'skills')}`);
|
|
560
|
-
return;
|
|
561
|
-
}
|
|
562
|
-
installSpinner.succeed(`Successfully installed ${skillsToInstall.length} skill(s) to ~/.claude/skills/`);
|
|
563
|
-
info('');
|
|
564
|
-
if (totalUpdated > 0 && totalSkipped > 0) {
|
|
565
|
-
success(`Updated ${totalUpdated} file(s), skipped ${totalSkipped} file(s)`);
|
|
566
|
-
}
|
|
567
|
-
else if (totalUpdated > 0) {
|
|
568
|
-
success(`Updated ${totalUpdated} file(s)`);
|
|
569
|
-
}
|
|
570
|
-
else {
|
|
571
|
-
success(`Created ${totalCopied} file(s)`);
|
|
572
|
-
}
|
|
573
|
-
info('');
|
|
574
|
-
info('Installed skills:');
|
|
575
|
-
skillsToInstall.forEach(s => {
|
|
576
|
-
const desc = getSkillDescription((0, path_1.join)(claudeSkillsDir, s), filesystem);
|
|
577
|
-
info(` • ${s}`);
|
|
578
|
-
info(` ${desc.substring(0, 100)}${desc.length > 100 ? '...' : ''}`);
|
|
579
|
-
});
|
|
580
|
-
info('');
|
|
581
|
-
info('These skills are now available in Claude Code!');
|
|
582
|
-
info('Claude will automatically use them when appropriate.');
|
|
583
|
-
info('');
|
|
584
|
-
info('Examples:');
|
|
585
|
-
if (skillsToInstall.includes('using-lt-cli')) {
|
|
586
|
-
info(' • "Checkout branch DEV-123"');
|
|
587
|
-
}
|
|
588
|
-
if (skillsToInstall.includes('generating-nest-servers')) {
|
|
589
|
-
info(' • "Create a User module with email and username"');
|
|
590
|
-
info(' • "Generate the complete server structure from this specification"');
|
|
591
|
-
}
|
|
592
|
-
info('');
|
|
593
|
-
info(`Location: ${(0, path_1.join)((0, os_1.homedir)(), '.claude', 'skills')}`);
|
|
594
|
-
if (totalSkipped > 0) {
|
|
595
|
-
info('');
|
|
596
|
-
info(`Note: ${totalSkipped} file(s) were skipped because your local versions are newer:`);
|
|
597
|
-
allSkippedFiles.forEach(({ file, reason }) => {
|
|
598
|
-
info(` • ${file} (${reason})`);
|
|
599
|
-
});
|
|
600
|
-
info('');
|
|
601
|
-
info('To force update, manually delete the files and run this command again.');
|
|
602
|
-
}
|
|
603
|
-
// Ask about setting up permissions
|
|
604
|
-
info('');
|
|
605
|
-
const setupPermissions = skipInteractive ? true : yield prompt.confirm('Set up global permissions for these skills? (Recommended - auto-approves skill-related commands)', true);
|
|
606
|
-
if (setupPermissions) {
|
|
607
|
-
const permissionsResult = yield setupSkillPermissions(skillsToInstall, filesystem, error);
|
|
608
|
-
if (permissionsResult.success) {
|
|
609
|
-
info('');
|
|
610
|
-
success('Permissions configured successfully!');
|
|
611
|
-
if (permissionsResult.added.length > 0) {
|
|
612
|
-
info('Added permissions:');
|
|
613
|
-
permissionsResult.added.forEach(perm => {
|
|
614
|
-
info(` • ${perm}`);
|
|
615
|
-
});
|
|
616
|
-
}
|
|
617
|
-
if (permissionsResult.existing.length > 0) {
|
|
618
|
-
info('Already configured:');
|
|
619
|
-
permissionsResult.existing.forEach(perm => {
|
|
620
|
-
info(` • ${perm}`);
|
|
621
|
-
});
|
|
622
|
-
}
|
|
623
|
-
info('');
|
|
624
|
-
info('Location: ~/.claude/settings.json');
|
|
625
|
-
}
|
|
626
|
-
else if (permissionsResult.error) {
|
|
627
|
-
info('');
|
|
628
|
-
info('⚠ Could not automatically configure permissions.');
|
|
629
|
-
info('You can manually add these to ~/.claude/settings.json:');
|
|
630
|
-
info('');
|
|
631
|
-
info(JSON.stringify({
|
|
632
|
-
permissions: {
|
|
633
|
-
allow: permissionsResult.requested
|
|
634
|
-
}
|
|
635
|
-
}, null, 2));
|
|
636
|
-
}
|
|
637
|
-
}
|
|
638
|
-
// Ask about setting up project detection hook for generating-nest-servers
|
|
639
|
-
if (skillsToInstall.includes('generating-nest-servers')) {
|
|
640
|
-
info('');
|
|
641
|
-
const setupHook = skipInteractive ? false : yield prompt.confirm('Set up automatic project detection for @lenne.tech/nest-server? (Recommended - suggests generating-nest-servers skill when detected)', false);
|
|
642
|
-
if (setupHook) {
|
|
643
|
-
const hookResult = yield setupProjectDetectionHook(filesystem, info, error, prompt.ask, skipInteractive);
|
|
644
|
-
if (hookResult.success) {
|
|
645
|
-
if (hookResult.added) {
|
|
646
|
-
info('');
|
|
647
|
-
success('Project detection hook configured successfully!');
|
|
648
|
-
info('');
|
|
649
|
-
info(`Scope: ${hookResult.scope === 'global' ? 'Global (all projects)' : 'Project-specific'}`);
|
|
650
|
-
info('');
|
|
651
|
-
info('How it works:');
|
|
652
|
-
info(' • Detects @lenne.tech/nest-server in package.json');
|
|
653
|
-
info(' • Supports monorepos: searches projects/*, packages/*, apps/* directories');
|
|
654
|
-
info(' • Suggests using generating-nest-servers skill for NestJS tasks');
|
|
655
|
-
info(' • Works from any directory in the project');
|
|
656
|
-
info('');
|
|
657
|
-
info(`Location: ${hookResult.scope === 'global' ? '~/.claude/settings.json' : '.claude/settings.json'}`);
|
|
658
|
-
}
|
|
659
|
-
else if (hookResult.alreadyExists) {
|
|
660
|
-
info('');
|
|
661
|
-
info('✓ Project detection hook already configured');
|
|
662
|
-
info(` Scope: ${hookResult.scope === 'global' ? 'Global' : 'Project-specific'}`);
|
|
663
|
-
}
|
|
664
|
-
}
|
|
665
|
-
else if (hookResult.error) {
|
|
666
|
-
info('');
|
|
667
|
-
info('⚠ Could not automatically configure project detection hook.');
|
|
668
|
-
info('You can manually add this hook to your settings.json');
|
|
669
|
-
}
|
|
670
|
-
}
|
|
671
|
-
}
|
|
672
|
-
}
|
|
673
|
-
catch (err) {
|
|
674
|
-
error(`Failed to install skill(s): ${err.message}`);
|
|
675
|
-
info('');
|
|
676
|
-
info('Troubleshooting:');
|
|
677
|
-
info(' • Ensure ~/.claude directory exists and is writable');
|
|
678
|
-
info(' • Check file permissions');
|
|
679
|
-
info(' • Try running with sudo if permission issues persist');
|
|
680
|
-
// NOTE: Using return instead of process.exit() here because error can occur
|
|
681
|
-
// before any prompts, and we want to let the process clean up naturally
|
|
682
|
-
return;
|
|
683
|
-
}
|
|
684
|
-
// NOTE: This command ends naturally without process.exit() because it has additional
|
|
685
|
-
// prompts at the end (setupPermissions and setupHook) that properly close the readline
|
|
686
|
-
// stream. If you create a new install command without such trailing prompts, you MUST
|
|
687
|
-
// call process.exit(0) explicitly, otherwise the process will hang indefinitely.
|
|
688
|
-
// See install-commands.ts for an example.
|
|
689
|
-
return `claude install-skills`;
|
|
690
|
-
}),
|
|
691
|
-
};
|
|
692
|
-
exports.default = NewCommand;
|
|
693
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5zdGFsbC1za2lsbHMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvY29tbWFuZHMvY2xhdWRlL2luc3RhbGwtc2tpbGxzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7Ozs7Ozs7O0FBQ0EsMkJBQTZCO0FBQzdCLCtCQUE0QjtBQUk1Qjs7R0FFRztBQUNILE1BQU0saUJBQWlCLEdBQTZCO0lBQ2xELDJCQUEyQixFQUFFO1FBQzNCLGtCQUFrQjtRQUNsQixzQkFBc0I7S0FDdkI7SUFDRCx5QkFBeUIsRUFBRTtRQUN6QixtQkFBbUI7S0FDcEI7SUFDRCxjQUFjLEVBQUU7UUFDZCxZQUFZO0tBQ2I7Q0FDRixDQUFDO0FBRUY7O0dBRUc7QUFDSCxNQUFNLGtCQUFrQixHQUEyQjtJQUNqRCxRQUFRLEVBQUUsY0FBYztJQUN4Qix1QkFBdUIsRUFBRSx5QkFBeUI7SUFDbEQsV0FBVyxFQUFFLDJCQUEyQjtDQUN6QyxDQUFDO0FBRUY7OztHQUdHO0FBQ0gsU0FBZSxtQkFBbUIsQ0FDaEMsVUFBZSxFQUNmLElBQVMsRUFDVCxNQUFXLEVBQ1gsZUFBd0I7O1FBRXhCLE1BQU0sYUFBYSxHQUFHLElBQUEsV0FBSSxFQUFDLElBQUEsWUFBTyxHQUFFLEVBQUUsU0FBUyxFQUFFLFFBQVEsQ0FBQyxDQUFDO1FBQzNELE1BQU0sYUFBYSxHQUFhLEVBQUUsQ0FBQztRQUVuQyxtQ0FBbUM7UUFDbkMsSUFBSSxDQUFDLFVBQVUsQ0FBQyxNQUFNLENBQUMsYUFBYSxDQUFDLEVBQUUsQ0FBQztZQUN0QyxPQUFPLGFBQWEsQ0FBQztRQUN2QixDQUFDO1FBRUQseUNBQXlDO1FBQ3pDLE1BQU0sb0JBQW9CLEdBQThELEVBQUUsQ0FBQztRQUUzRixLQUFLLE1BQU0sQ0FBQyxPQUFPLEVBQUUsT0FBTyxDQUFDLElBQUksTUFBTSxDQUFDLE9BQU8sQ0FBQyxrQkFBa0IsQ0FBQyxFQUFFLENBQUM7WUFDcEUsTUFBTSxVQUFVLEdBQUcsSUFBQSxXQUFJ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
|