@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.
Files changed (46) hide show
  1. package/README.md +167 -0
  2. package/bin/cli.js +58 -0
  3. package/package.json +46 -0
  4. package/skills/_quality-attributes.md +392 -0
  5. package/skills/_registry.md +189 -0
  6. package/skills/debrief/SKILL.md +647 -0
  7. package/skills/debrief/references/change-request-template.md +124 -0
  8. package/skills/debrief/references/file-patterns.md +173 -0
  9. package/skills/debrief/references/group-codes.md +72 -0
  10. package/skills/debrief/references/research-queries.md +106 -0
  11. package/skills/debrief/references/use-case-template.md +141 -0
  12. package/skills/debrief/scripts/generate_questionnaire.py +195 -0
  13. package/skills/dev-arch/SKILL.md +747 -0
  14. package/skills/dev-changelog/SKILL.md +378 -0
  15. package/skills/dev-coding/SKILL.md +470 -0
  16. package/skills/dev-coding-backend/SKILL.md +361 -0
  17. package/skills/dev-coding-frontend/SKILL.md +534 -0
  18. package/skills/dev-coding-frontend/references/nextjs.md +477 -0
  19. package/skills/dev-review/SKILL.md +548 -0
  20. package/skills/dev-scout/SKILL.md +723 -0
  21. package/skills/dev-scout/references/feature-patterns.md +210 -0
  22. package/skills/dev-scout/references/file-patterns.md +252 -0
  23. package/skills/dev-scout/references/tech-detection.md +211 -0
  24. package/skills/dev-scout/scripts/scout-analyze.sh +280 -0
  25. package/skills/dev-specs/SKILL.md +577 -0
  26. package/skills/dev-specs/references/checklist.md +176 -0
  27. package/skills/dev-specs/references/spec-templates.md +460 -0
  28. package/skills/dev-test/SKILL.md +364 -0
  29. package/skills/utils/diagram/SKILL.md +205 -0
  30. package/skills/utils/diagram/references/common-errors.md +305 -0
  31. package/skills/utils/diagram/references/diagram-types.md +636 -0
  32. package/skills/utils/docs-graph/SKILL.md +204 -0
  33. package/skills/utils/gemini/SKILL.md +292 -0
  34. package/skills/utils/gemini/scripts/gemini-scan.py +340 -0
  35. package/skills/utils/gemini/scripts/setup.sh +169 -0
  36. package/src/commands/add.js +64 -0
  37. package/src/commands/doctor.js +179 -0
  38. package/src/commands/init.js +251 -0
  39. package/src/commands/list.js +88 -0
  40. package/src/commands/remove.js +60 -0
  41. package/src/commands/update.js +72 -0
  42. package/src/index.js +26 -0
  43. package/src/utils/config.js +272 -0
  44. package/src/utils/deps.js +599 -0
  45. package/src/utils/skills.js +253 -0
  46. 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
+ }