@codihaus/claude-skills 1.0.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/README.md +167 -0
- package/bin/cli.js +58 -0
- package/package.json +46 -0
- package/skills/_quality-attributes.md +392 -0
- package/skills/_registry.md +189 -0
- package/skills/debrief/SKILL.md +647 -0
- package/skills/debrief/references/change-request-template.md +124 -0
- package/skills/debrief/references/file-patterns.md +173 -0
- package/skills/debrief/references/group-codes.md +72 -0
- package/skills/debrief/references/research-queries.md +106 -0
- package/skills/debrief/references/use-case-template.md +141 -0
- package/skills/debrief/scripts/generate_questionnaire.py +195 -0
- package/skills/dev-arch/SKILL.md +747 -0
- package/skills/dev-changelog/SKILL.md +378 -0
- package/skills/dev-coding/SKILL.md +470 -0
- package/skills/dev-coding-backend/SKILL.md +361 -0
- package/skills/dev-coding-frontend/SKILL.md +534 -0
- package/skills/dev-coding-frontend/references/nextjs.md +477 -0
- package/skills/dev-review/SKILL.md +548 -0
- package/skills/dev-scout/SKILL.md +723 -0
- package/skills/dev-scout/references/feature-patterns.md +210 -0
- package/skills/dev-scout/references/file-patterns.md +252 -0
- package/skills/dev-scout/references/tech-detection.md +211 -0
- package/skills/dev-scout/scripts/scout-analyze.sh +280 -0
- package/skills/dev-specs/SKILL.md +577 -0
- package/skills/dev-specs/references/checklist.md +176 -0
- package/skills/dev-specs/references/spec-templates.md +460 -0
- package/skills/dev-test/SKILL.md +364 -0
- package/skills/utils/diagram/SKILL.md +205 -0
- package/skills/utils/diagram/references/common-errors.md +305 -0
- package/skills/utils/diagram/references/diagram-types.md +636 -0
- package/skills/utils/docs-graph/SKILL.md +204 -0
- package/skills/utils/gemini/SKILL.md +292 -0
- package/skills/utils/gemini/scripts/gemini-scan.py +340 -0
- package/skills/utils/gemini/scripts/setup.sh +169 -0
- package/src/commands/add.js +64 -0
- package/src/commands/doctor.js +179 -0
- package/src/commands/init.js +251 -0
- package/src/commands/list.js +88 -0
- package/src/commands/remove.js +60 -0
- package/src/commands/update.js +72 -0
- package/src/index.js +26 -0
- package/src/utils/config.js +272 -0
- package/src/utils/deps.js +599 -0
- package/src/utils/skills.js +253 -0
- package/templates/CLAUDE.md.template +58 -0
|
@@ -0,0 +1,599 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dependency Checker
|
|
3
|
+
*
|
|
4
|
+
* Checks for required global tools and project dependencies.
|
|
5
|
+
* Handles macOS and Linux differences.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { execSync } from 'child_process';
|
|
9
|
+
import fs from 'fs-extra';
|
|
10
|
+
import path from 'path';
|
|
11
|
+
import chalk from 'chalk';
|
|
12
|
+
import os from 'os';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Detect OS for install hints
|
|
16
|
+
*/
|
|
17
|
+
function getOS() {
|
|
18
|
+
const platform = os.platform();
|
|
19
|
+
if (platform === 'darwin') return 'macos';
|
|
20
|
+
if (platform === 'linux') {
|
|
21
|
+
// Try to detect distro
|
|
22
|
+
try {
|
|
23
|
+
const release = fs.readFileSync('/etc/os-release', 'utf-8');
|
|
24
|
+
if (release.includes('Ubuntu') || release.includes('Debian')) return 'debian';
|
|
25
|
+
if (release.includes('Fedora') || release.includes('Red Hat') || release.includes('CentOS')) return 'redhat';
|
|
26
|
+
if (release.includes('Arch')) return 'arch';
|
|
27
|
+
} catch (e) {
|
|
28
|
+
// Fallback
|
|
29
|
+
}
|
|
30
|
+
return 'linux';
|
|
31
|
+
}
|
|
32
|
+
return 'unknown';
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Get install command based on OS
|
|
37
|
+
*/
|
|
38
|
+
function getInstallHint(tool, osType) {
|
|
39
|
+
const hints = {
|
|
40
|
+
node: {
|
|
41
|
+
macos: 'brew install node OR https://nodejs.org/',
|
|
42
|
+
debian: 'sudo apt install nodejs npm OR https://nodejs.org/',
|
|
43
|
+
redhat: 'sudo dnf install nodejs npm OR https://nodejs.org/',
|
|
44
|
+
arch: 'sudo pacman -S nodejs npm',
|
|
45
|
+
linux: 'https://nodejs.org/ (use official installer)',
|
|
46
|
+
unknown: 'https://nodejs.org/'
|
|
47
|
+
},
|
|
48
|
+
git: {
|
|
49
|
+
macos: 'xcode-select --install OR brew install git',
|
|
50
|
+
debian: 'sudo apt install git',
|
|
51
|
+
redhat: 'sudo dnf install git',
|
|
52
|
+
arch: 'sudo pacman -S git',
|
|
53
|
+
linux: 'sudo apt install git OR sudo dnf install git',
|
|
54
|
+
unknown: 'https://git-scm.com/'
|
|
55
|
+
},
|
|
56
|
+
python3: {
|
|
57
|
+
macos: 'brew install python3 OR https://python.org/',
|
|
58
|
+
debian: 'sudo apt install python3 python3-pip',
|
|
59
|
+
redhat: 'sudo dnf install python3 python3-pip',
|
|
60
|
+
arch: 'sudo pacman -S python python-pip',
|
|
61
|
+
linux: 'sudo apt install python3 python3-pip',
|
|
62
|
+
unknown: 'https://python.org/'
|
|
63
|
+
},
|
|
64
|
+
pip3: {
|
|
65
|
+
macos: 'python3 -m ensurepip OR brew install python3',
|
|
66
|
+
debian: 'sudo apt install python3-pip',
|
|
67
|
+
redhat: 'sudo dnf install python3-pip',
|
|
68
|
+
arch: 'sudo pacman -S python-pip',
|
|
69
|
+
linux: 'sudo apt install python3-pip',
|
|
70
|
+
unknown: 'python3 -m ensurepip'
|
|
71
|
+
},
|
|
72
|
+
jq: {
|
|
73
|
+
macos: 'brew install jq',
|
|
74
|
+
debian: 'sudo apt install jq',
|
|
75
|
+
redhat: 'sudo dnf install jq',
|
|
76
|
+
arch: 'sudo pacman -S jq',
|
|
77
|
+
linux: 'sudo apt install jq OR sudo dnf install jq',
|
|
78
|
+
unknown: 'https://stedolan.github.io/jq/download/'
|
|
79
|
+
},
|
|
80
|
+
gh: {
|
|
81
|
+
macos: 'brew install gh',
|
|
82
|
+
debian: 'sudo apt install gh OR https://cli.github.com/',
|
|
83
|
+
redhat: 'sudo dnf install gh',
|
|
84
|
+
arch: 'sudo pacman -S github-cli',
|
|
85
|
+
linux: 'https://cli.github.com/',
|
|
86
|
+
unknown: 'https://cli.github.com/'
|
|
87
|
+
},
|
|
88
|
+
mmdc: {
|
|
89
|
+
macos: 'npm install -g @mermaid-js/mermaid-cli',
|
|
90
|
+
debian: 'npm install -g @mermaid-js/mermaid-cli',
|
|
91
|
+
redhat: 'npm install -g @mermaid-js/mermaid-cli',
|
|
92
|
+
arch: 'npm install -g @mermaid-js/mermaid-cli',
|
|
93
|
+
linux: 'npm install -g @mermaid-js/mermaid-cli',
|
|
94
|
+
unknown: 'npm install -g @mermaid-js/mermaid-cli'
|
|
95
|
+
},
|
|
96
|
+
tree: {
|
|
97
|
+
macos: 'brew install tree',
|
|
98
|
+
debian: 'sudo apt install tree',
|
|
99
|
+
redhat: 'sudo dnf install tree',
|
|
100
|
+
arch: 'sudo pacman -S tree',
|
|
101
|
+
linux: 'sudo apt install tree',
|
|
102
|
+
unknown: 'https://mama.indstate.edu/users/ice/tree/'
|
|
103
|
+
},
|
|
104
|
+
scc: {
|
|
105
|
+
macos: 'brew install scc OR go install github.com/boyter/scc/v3@latest',
|
|
106
|
+
debian: 'go install github.com/boyter/scc/v3@latest',
|
|
107
|
+
redhat: 'go install github.com/boyter/scc/v3@latest',
|
|
108
|
+
arch: 'yay -S scc OR go install github.com/boyter/scc/v3@latest',
|
|
109
|
+
linux: 'go install github.com/boyter/scc/v3@latest',
|
|
110
|
+
unknown: 'https://github.com/boyter/scc'
|
|
111
|
+
},
|
|
112
|
+
rg: {
|
|
113
|
+
macos: 'brew install ripgrep',
|
|
114
|
+
debian: 'sudo apt install ripgrep',
|
|
115
|
+
redhat: 'sudo dnf install ripgrep',
|
|
116
|
+
arch: 'sudo pacman -S ripgrep',
|
|
117
|
+
linux: 'sudo apt install ripgrep OR cargo install ripgrep',
|
|
118
|
+
unknown: 'https://github.com/BurntSushi/ripgrep'
|
|
119
|
+
}
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
return hints[tool]?.[osType] || hints[tool]?.unknown || 'See documentation';
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const osType = getOS();
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Global dependencies required for skills to work
|
|
129
|
+
*/
|
|
130
|
+
export const GLOBAL_DEPS = {
|
|
131
|
+
required: [
|
|
132
|
+
{
|
|
133
|
+
name: 'node',
|
|
134
|
+
command: 'node --version',
|
|
135
|
+
minVersion: '18.0.0',
|
|
136
|
+
get installHint() { return getInstallHint('node', osType); },
|
|
137
|
+
purpose: 'Runtime for CLI and scripts',
|
|
138
|
+
usedBy: ['CLI', 'npm packages']
|
|
139
|
+
},
|
|
140
|
+
{
|
|
141
|
+
name: 'git',
|
|
142
|
+
command: 'git --version',
|
|
143
|
+
get installHint() { return getInstallHint('git', osType); },
|
|
144
|
+
purpose: 'Version control, git operations',
|
|
145
|
+
usedBy: ['/dev-review', '/dev-changelog']
|
|
146
|
+
}
|
|
147
|
+
],
|
|
148
|
+
recommended: [
|
|
149
|
+
{
|
|
150
|
+
name: 'python3',
|
|
151
|
+
command: 'python3 --version',
|
|
152
|
+
minVersion: '3.8.0',
|
|
153
|
+
get installHint() { return getInstallHint('python3', osType); },
|
|
154
|
+
purpose: 'Required for Python scripts',
|
|
155
|
+
usedBy: ['/utils/gemini', '/debrief (questionnaire)']
|
|
156
|
+
},
|
|
157
|
+
{
|
|
158
|
+
name: 'pip3',
|
|
159
|
+
command: 'pip3 --version',
|
|
160
|
+
alternateCommand: 'python3 -m pip --version',
|
|
161
|
+
get installHint() { return getInstallHint('pip3', osType); },
|
|
162
|
+
purpose: 'Install Python packages',
|
|
163
|
+
usedBy: ['/utils/gemini', '/debrief']
|
|
164
|
+
},
|
|
165
|
+
{
|
|
166
|
+
name: 'jq',
|
|
167
|
+
command: 'jq --version',
|
|
168
|
+
get installHint() { return getInstallHint('jq', osType); },
|
|
169
|
+
purpose: 'JSON processing for docs-graph',
|
|
170
|
+
usedBy: ['/utils/docs-graph', '/dev-scout']
|
|
171
|
+
}
|
|
172
|
+
],
|
|
173
|
+
optional: [
|
|
174
|
+
{
|
|
175
|
+
name: 'gh',
|
|
176
|
+
command: 'gh --version',
|
|
177
|
+
get installHint() { return getInstallHint('gh', osType); },
|
|
178
|
+
purpose: 'GitHub CLI for PR creation',
|
|
179
|
+
usedBy: ['PR creation', 'GitHub operations']
|
|
180
|
+
},
|
|
181
|
+
{
|
|
182
|
+
name: 'mmdc',
|
|
183
|
+
command: 'mmdc --version',
|
|
184
|
+
get installHint() { return getInstallHint('mmdc', osType); },
|
|
185
|
+
purpose: 'Mermaid CLI for diagram rendering',
|
|
186
|
+
usedBy: ['/utils/diagram']
|
|
187
|
+
},
|
|
188
|
+
{
|
|
189
|
+
name: 'tree',
|
|
190
|
+
command: 'tree --version',
|
|
191
|
+
get installHint() { return getInstallHint('tree', osType); },
|
|
192
|
+
purpose: 'Directory visualization (has fallback)',
|
|
193
|
+
usedBy: ['/dev-scout']
|
|
194
|
+
},
|
|
195
|
+
{
|
|
196
|
+
name: 'scc',
|
|
197
|
+
command: 'scc --version',
|
|
198
|
+
get installHint() { return getInstallHint('scc', osType); },
|
|
199
|
+
purpose: 'Fast code statistics - lines, languages (has fallback)',
|
|
200
|
+
usedBy: ['/dev-scout']
|
|
201
|
+
},
|
|
202
|
+
{
|
|
203
|
+
name: 'rg',
|
|
204
|
+
command: 'rg --version',
|
|
205
|
+
get installHint() { return getInstallHint('rg', osType); },
|
|
206
|
+
purpose: 'Ripgrep for faster searching (has fallback)',
|
|
207
|
+
usedBy: ['/dev-scout']
|
|
208
|
+
}
|
|
209
|
+
]
|
|
210
|
+
};
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Python packages needed
|
|
214
|
+
*/
|
|
215
|
+
export const PYTHON_DEPS = [
|
|
216
|
+
{
|
|
217
|
+
name: 'openpyxl',
|
|
218
|
+
importName: 'openpyxl',
|
|
219
|
+
installCmd: 'pip3 install openpyxl',
|
|
220
|
+
purpose: 'Excel questionnaire generation',
|
|
221
|
+
usedBy: ['/debrief']
|
|
222
|
+
},
|
|
223
|
+
{
|
|
224
|
+
name: 'google-generativeai',
|
|
225
|
+
importName: 'google.generativeai',
|
|
226
|
+
installCmd: 'pip3 install google-generativeai',
|
|
227
|
+
purpose: 'Gemini API for large codebase scanning',
|
|
228
|
+
usedBy: ['/utils/gemini']
|
|
229
|
+
}
|
|
230
|
+
];
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* Project npm dependencies
|
|
234
|
+
*/
|
|
235
|
+
export const PROJECT_DEPS = {
|
|
236
|
+
forTesting: [
|
|
237
|
+
{
|
|
238
|
+
name: '@playwright/test',
|
|
239
|
+
purpose: 'UI testing with /dev-test',
|
|
240
|
+
usedBy: ['/dev-test', '/dev-coding-frontend']
|
|
241
|
+
}
|
|
242
|
+
],
|
|
243
|
+
optional: [
|
|
244
|
+
{
|
|
245
|
+
name: '@mermaid-js/mermaid-cli',
|
|
246
|
+
purpose: 'Diagram rendering (optional)',
|
|
247
|
+
usedBy: ['/utils/diagram'],
|
|
248
|
+
global: true
|
|
249
|
+
}
|
|
250
|
+
]
|
|
251
|
+
};
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* Check if a command exists and optionally verify version
|
|
255
|
+
*/
|
|
256
|
+
export function checkCommand(cmd, alternateCmd = null, minVersion = null) {
|
|
257
|
+
const commands = alternateCmd ? [cmd, alternateCmd] : [cmd];
|
|
258
|
+
|
|
259
|
+
for (const command of commands) {
|
|
260
|
+
try {
|
|
261
|
+
const output = execSync(command, {
|
|
262
|
+
encoding: 'utf-8',
|
|
263
|
+
stdio: ['pipe', 'pipe', 'pipe']
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
if (minVersion) {
|
|
267
|
+
const versionMatch = output.match(/(\d+\.\d+\.\d+)/);
|
|
268
|
+
if (versionMatch) {
|
|
269
|
+
const version = versionMatch[1];
|
|
270
|
+
if (compareVersions(version, minVersion) < 0) {
|
|
271
|
+
return { found: true, version, outdated: true, minVersion };
|
|
272
|
+
}
|
|
273
|
+
return { found: true, version, outdated: false };
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
return { found: true, version: output.trim().split('\n')[0] };
|
|
278
|
+
} catch (e) {
|
|
279
|
+
// Try next command
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
return { found: false };
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
/**
|
|
287
|
+
* Check if a Python package is installed
|
|
288
|
+
*/
|
|
289
|
+
export function checkPythonPackage(importName) {
|
|
290
|
+
try {
|
|
291
|
+
execSync(`python3 -c "import ${importName}"`, {
|
|
292
|
+
encoding: 'utf-8',
|
|
293
|
+
stdio: ['pipe', 'pipe', 'pipe']
|
|
294
|
+
});
|
|
295
|
+
return { installed: true };
|
|
296
|
+
} catch (e) {
|
|
297
|
+
return { installed: false };
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
/**
|
|
302
|
+
* Compare semantic versions
|
|
303
|
+
*/
|
|
304
|
+
function compareVersions(a, b) {
|
|
305
|
+
const pa = a.split('.').map(Number);
|
|
306
|
+
const pb = b.split('.').map(Number);
|
|
307
|
+
|
|
308
|
+
for (let i = 0; i < 3; i++) {
|
|
309
|
+
if ((pa[i] || 0) > (pb[i] || 0)) return 1;
|
|
310
|
+
if ((pa[i] || 0) < (pb[i] || 0)) return -1;
|
|
311
|
+
}
|
|
312
|
+
return 0;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
/**
|
|
316
|
+
* Check all global dependencies
|
|
317
|
+
*/
|
|
318
|
+
export async function checkGlobalDeps(options = {}) {
|
|
319
|
+
const results = {
|
|
320
|
+
os: osType,
|
|
321
|
+
required: [],
|
|
322
|
+
recommended: [],
|
|
323
|
+
optional: [],
|
|
324
|
+
python: [],
|
|
325
|
+
hasErrors: false,
|
|
326
|
+
hasWarnings: false
|
|
327
|
+
};
|
|
328
|
+
|
|
329
|
+
// Check required
|
|
330
|
+
for (const dep of GLOBAL_DEPS.required) {
|
|
331
|
+
const result = checkCommand(dep.command, dep.alternateCommand, dep.minVersion);
|
|
332
|
+
results.required.push({
|
|
333
|
+
...dep,
|
|
334
|
+
...result,
|
|
335
|
+
status: result.found && !result.outdated ? 'ok' : 'error'
|
|
336
|
+
});
|
|
337
|
+
if (!result.found || result.outdated) {
|
|
338
|
+
results.hasErrors = true;
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
// Check recommended
|
|
343
|
+
for (const dep of GLOBAL_DEPS.recommended) {
|
|
344
|
+
const result = checkCommand(dep.command, dep.alternateCommand, dep.minVersion);
|
|
345
|
+
results.recommended.push({
|
|
346
|
+
...dep,
|
|
347
|
+
...result,
|
|
348
|
+
status: result.found && !result.outdated ? 'ok' : 'warning'
|
|
349
|
+
});
|
|
350
|
+
if (!result.found || result.outdated) {
|
|
351
|
+
results.hasWarnings = true;
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
// Check optional
|
|
356
|
+
for (const dep of GLOBAL_DEPS.optional) {
|
|
357
|
+
const result = checkCommand(dep.command, dep.alternateCommand);
|
|
358
|
+
results.optional.push({
|
|
359
|
+
...dep,
|
|
360
|
+
...result,
|
|
361
|
+
status: result.found ? 'ok' : 'info'
|
|
362
|
+
});
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
// Check Python packages (only if python3 is available)
|
|
366
|
+
const hasPython = results.required.concat(results.recommended)
|
|
367
|
+
.find(d => d.name === 'python3')?.found;
|
|
368
|
+
|
|
369
|
+
if (hasPython) {
|
|
370
|
+
for (const pkg of PYTHON_DEPS) {
|
|
371
|
+
const result = checkPythonPackage(pkg.importName);
|
|
372
|
+
results.python.push({
|
|
373
|
+
...pkg,
|
|
374
|
+
...result,
|
|
375
|
+
status: result.installed ? 'ok' : 'warning'
|
|
376
|
+
});
|
|
377
|
+
if (!result.installed) {
|
|
378
|
+
results.hasWarnings = true;
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
return results;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
/**
|
|
387
|
+
* Check project dependencies
|
|
388
|
+
*/
|
|
389
|
+
export async function checkProjectDeps(projectPath) {
|
|
390
|
+
const results = {
|
|
391
|
+
npm: [],
|
|
392
|
+
missing: [],
|
|
393
|
+
hasErrors: false
|
|
394
|
+
};
|
|
395
|
+
|
|
396
|
+
const packageJsonPath = path.join(projectPath, 'package.json');
|
|
397
|
+
|
|
398
|
+
// Check if package.json exists
|
|
399
|
+
if (!await fs.pathExists(packageJsonPath)) {
|
|
400
|
+
results.noPackageJson = true;
|
|
401
|
+
return results;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
const packageJson = await fs.readJson(packageJsonPath);
|
|
405
|
+
const allDeps = {
|
|
406
|
+
...packageJson.dependencies,
|
|
407
|
+
...packageJson.devDependencies
|
|
408
|
+
};
|
|
409
|
+
|
|
410
|
+
// Check for required npm deps
|
|
411
|
+
for (const dep of PROJECT_DEPS.forTesting) {
|
|
412
|
+
const installed = !!allDeps[dep.name];
|
|
413
|
+
results.npm.push({
|
|
414
|
+
...dep,
|
|
415
|
+
installed,
|
|
416
|
+
status: installed ? 'ok' : 'missing'
|
|
417
|
+
});
|
|
418
|
+
if (!installed) {
|
|
419
|
+
results.missing.push(dep);
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
results.hasErrors = results.missing.length > 0;
|
|
424
|
+
return results;
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
/**
|
|
428
|
+
* Print dependency check results
|
|
429
|
+
*/
|
|
430
|
+
export function printDepsReport(globalResults, projectResults = null) {
|
|
431
|
+
console.log(chalk.bold(`\nSystem: ${globalResults.os}`));
|
|
432
|
+
console.log('─'.repeat(50));
|
|
433
|
+
|
|
434
|
+
// Required
|
|
435
|
+
console.log(chalk.cyan('\nRequired (must have):'));
|
|
436
|
+
for (const dep of globalResults.required) {
|
|
437
|
+
const icon = dep.status === 'ok' ? chalk.green('✓') : chalk.red('✗');
|
|
438
|
+
const version = dep.version ? chalk.gray(` (${dep.version.substring(0, 30)})`) : '';
|
|
439
|
+
console.log(` ${icon} ${dep.name}${version}`);
|
|
440
|
+
if (dep.status !== 'ok') {
|
|
441
|
+
console.log(chalk.yellow(` → ${dep.installHint}`));
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
// Recommended
|
|
446
|
+
console.log(chalk.cyan('\nRecommended (for full functionality):'));
|
|
447
|
+
for (const dep of globalResults.recommended) {
|
|
448
|
+
const icon = dep.status === 'ok' ? chalk.green('✓') : chalk.yellow('○');
|
|
449
|
+
const version = dep.version ? chalk.gray(` (${dep.version.substring(0, 30)})`) : '';
|
|
450
|
+
const usedBy = chalk.gray(` - ${dep.usedBy.join(', ')}`);
|
|
451
|
+
console.log(` ${icon} ${dep.name}${version}${usedBy}`);
|
|
452
|
+
if (dep.status !== 'ok') {
|
|
453
|
+
console.log(chalk.gray(` → ${dep.installHint}`));
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
// Python packages
|
|
458
|
+
if (globalResults.python.length > 0) {
|
|
459
|
+
console.log(chalk.cyan('\nPython Packages:'));
|
|
460
|
+
for (const pkg of globalResults.python) {
|
|
461
|
+
const icon = pkg.status === 'ok' ? chalk.green('✓') : chalk.yellow('○');
|
|
462
|
+
const usedBy = chalk.gray(` - ${pkg.usedBy.join(', ')}`);
|
|
463
|
+
console.log(` ${icon} ${pkg.name}${usedBy}`);
|
|
464
|
+
if (!pkg.installed) {
|
|
465
|
+
console.log(chalk.gray(` → ${pkg.installCmd}`));
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
// Optional
|
|
471
|
+
console.log(chalk.cyan('\nOptional:'));
|
|
472
|
+
for (const dep of globalResults.optional) {
|
|
473
|
+
const icon = dep.status === 'ok' ? chalk.green('✓') : chalk.gray('○');
|
|
474
|
+
const version = dep.version ? chalk.gray(` (${dep.version.substring(0, 30)})`) : '';
|
|
475
|
+
const usedBy = chalk.gray(` - ${dep.usedBy.join(', ')}`);
|
|
476
|
+
console.log(` ${icon} ${dep.name}${version}${usedBy}`);
|
|
477
|
+
if (dep.status !== 'ok') {
|
|
478
|
+
console.log(chalk.gray(` → ${dep.installHint}`));
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
// Project deps
|
|
483
|
+
if (projectResults) {
|
|
484
|
+
console.log('\n' + chalk.bold('Project Dependencies:'));
|
|
485
|
+
console.log('─'.repeat(50));
|
|
486
|
+
|
|
487
|
+
if (projectResults.noPackageJson) {
|
|
488
|
+
console.log(chalk.gray(' No package.json found (not a Node.js project)'));
|
|
489
|
+
} else {
|
|
490
|
+
for (const dep of projectResults.npm) {
|
|
491
|
+
const icon = dep.status === 'ok' ? chalk.green('✓') : chalk.yellow('○');
|
|
492
|
+
const usedBy = chalk.gray(` - ${dep.usedBy.join(', ')}`);
|
|
493
|
+
console.log(` ${icon} ${dep.name}${usedBy}`);
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
if (projectResults.missing.length > 0) {
|
|
497
|
+
console.log(chalk.yellow('\n To install missing dependencies:'));
|
|
498
|
+
const names = projectResults.missing.map(d => d.name).join(' ');
|
|
499
|
+
console.log(chalk.cyan(` npm install -D ${names}`));
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
console.log('');
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
/**
|
|
508
|
+
* Install missing project dependencies
|
|
509
|
+
*/
|
|
510
|
+
export async function installProjectDeps(projectPath, deps, options = {}) {
|
|
511
|
+
const npmDeps = deps.filter(d => !d.global);
|
|
512
|
+
|
|
513
|
+
if (npmDeps.length > 0) {
|
|
514
|
+
console.log(chalk.cyan('Installing npm dependencies...'));
|
|
515
|
+
const names = npmDeps.map(d => d.name).join(' ');
|
|
516
|
+
try {
|
|
517
|
+
execSync(`npm install -D ${names}`, {
|
|
518
|
+
cwd: projectPath,
|
|
519
|
+
stdio: 'inherit'
|
|
520
|
+
});
|
|
521
|
+
return true;
|
|
522
|
+
} catch (e) {
|
|
523
|
+
console.log(chalk.red('Failed to install npm dependencies'));
|
|
524
|
+
return false;
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
return true;
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
/**
|
|
532
|
+
* Install missing Python packages
|
|
533
|
+
*/
|
|
534
|
+
export async function installPythonDeps(packages) {
|
|
535
|
+
if (packages.length === 0) return true;
|
|
536
|
+
|
|
537
|
+
console.log(chalk.cyan('Installing Python packages...'));
|
|
538
|
+
|
|
539
|
+
for (const pkg of packages) {
|
|
540
|
+
try {
|
|
541
|
+
console.log(` Installing ${pkg.name}...`);
|
|
542
|
+
execSync(pkg.installCmd, { stdio: 'inherit' });
|
|
543
|
+
} catch (e) {
|
|
544
|
+
console.log(chalk.red(` Failed to install ${pkg.name}`));
|
|
545
|
+
return false;
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
return true;
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
/**
|
|
553
|
+
* Get summary of what's missing
|
|
554
|
+
*/
|
|
555
|
+
export function getMissingSummary(globalResults, projectResults) {
|
|
556
|
+
const missing = {
|
|
557
|
+
critical: [],
|
|
558
|
+
recommended: [],
|
|
559
|
+
optional: []
|
|
560
|
+
};
|
|
561
|
+
|
|
562
|
+
for (const dep of globalResults.required) {
|
|
563
|
+
if (dep.status !== 'ok') {
|
|
564
|
+
missing.critical.push({
|
|
565
|
+
name: dep.name,
|
|
566
|
+
hint: dep.installHint
|
|
567
|
+
});
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
for (const dep of globalResults.recommended) {
|
|
572
|
+
if (dep.status !== 'ok') {
|
|
573
|
+
missing.recommended.push({
|
|
574
|
+
name: dep.name,
|
|
575
|
+
hint: dep.installHint
|
|
576
|
+
});
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
for (const pkg of globalResults.python || []) {
|
|
581
|
+
if (!pkg.installed) {
|
|
582
|
+
missing.recommended.push({
|
|
583
|
+
name: pkg.name,
|
|
584
|
+
hint: pkg.installCmd
|
|
585
|
+
});
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
if (projectResults && projectResults.missing) {
|
|
590
|
+
for (const dep of projectResults.missing) {
|
|
591
|
+
missing.optional.push({
|
|
592
|
+
name: dep.name,
|
|
593
|
+
hint: `npm install -D ${dep.name}`
|
|
594
|
+
});
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
return missing;
|
|
599
|
+
}
|