cli-ai-skills 1.0.0 → 1.3.1

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 CHANGED
@@ -14,6 +14,7 @@ npx cli-ai-skills install
14
14
  - šŸŒ **Global or Local** - Install globally or per-repository
15
15
  - šŸ”— **Symlink Support** - Auto-updates with repository changes
16
16
  - šŸ“Š **Progress Gauge** - Visual progress tracking
17
+ - šŸ **Python Requirements** - Auto-installs Python dependencies for skills that need them
17
18
  - šŸ„ **Doctor Command** - Diagnose installation issues
18
19
 
19
20
  ## šŸš€ Quick Start
@@ -46,6 +47,9 @@ You'll be prompted to select:
46
47
  - **prompt-engineer** - Transform prompts using 11 established frameworks
47
48
  - **skill-creator** - Create new skills interactively
48
49
  - **youtube-summarizer** - Extract and summarize YouTube videos
50
+ - **audio-transcriber** šŸ - Transcribe audio to text with meeting minutes and summaries
51
+
52
+ > šŸ = Requires Python dependencies (auto-installed during setup)
49
53
 
50
54
  ## šŸ“– Commands
51
55
 
@@ -126,6 +130,53 @@ Checks:
126
130
  - āœ… Platform installations (Copilot/Claude)
127
131
  - āœ… Directory permissions
128
132
  - āœ… Network connectivity
133
+ - āœ… Python environment (for audio-transcriber skill)
134
+ - āœ… Whisper and ffmpeg installation
135
+
136
+ ## šŸ Python Requirements
137
+
138
+ Some skills (like **audio-transcriber**) require Python dependencies. The installer handles this automatically:
139
+
140
+ ### Automatic Installation
141
+
142
+ ```bash
143
+ $ npx cli-ai-skills install audio-transcriber
144
+
145
+ šŸ“¦ Downloading audio-transcriber v1.0.0...
146
+ āœ… Installed successfully
147
+
148
+ šŸ“¦ This skill requires Python dependencies
149
+ āœ… Python detected: 3.11.7
150
+ ? Install Python requirements now? (Y/n) Y
151
+
152
+ šŸ”§ Running install-requirements.sh...
153
+ āœ… pkg-config installed
154
+ āœ… ffmpeg installed
155
+ āœ… openai-whisper installed
156
+
157
+ šŸŽ‰ audio-transcriber ready to use!
158
+ ```
159
+
160
+ ### Manual Installation
161
+
162
+ If you skip auto-install, you can run it later:
163
+
164
+ ```bash
165
+ # Using the skill's install script
166
+ bash ~/.copilot/skills/audio-transcriber/scripts/install-requirements.sh
167
+
168
+ # Or manually with pip
169
+ pip install --user openai-whisper
170
+ brew install ffmpeg # macOS
171
+ ```
172
+
173
+ ### Checking Python Status
174
+
175
+ ```bash
176
+ npx cli-ai-skills doctor
177
+ ```
178
+
179
+ Shows Python version, Whisper, and ffmpeg status.
129
180
 
130
181
  ## šŸŽØ Example Usage
131
182
 
@@ -1,5 +1,6 @@
1
1
  const chalk = require('chalk');
2
2
  const PlatformDetector = require('../core/detector');
3
+ const RequirementsInstaller = require('../core/requirements-installer');
3
4
  const { execSync } = require('child_process');
4
5
 
5
6
  async function doctorCommand() {
@@ -80,6 +81,57 @@ async function doctorCommand() {
80
81
  console.log(chalk.dim(` Install: https://claude.ai/code`));
81
82
  }
82
83
 
84
+ console.log(chalk.cyan('\n' + '━'.repeat(60)));
85
+ console.log(chalk.bold('\nPython Environment (for audio-transcriber):\n'));
86
+
87
+ const requirementsInstaller = new RequirementsInstaller();
88
+
89
+ // Check Python
90
+ console.log(chalk.bold('Python:'));
91
+ const pythonCheck = await requirementsInstaller.verifyPython();
92
+ if (pythonCheck.available) {
93
+ console.log(chalk.green(` āœ… ${pythonCheck.version}`));
94
+ } else {
95
+ console.log(chalk.yellow(' āš ļø Python 3 not found'));
96
+ console.log(chalk.dim(' Required for audio-transcriber skill'));
97
+ console.log(chalk.dim(' Install: https://www.python.org/downloads/'));
98
+ warnings++;
99
+ }
100
+
101
+ // Check Whisper (if Python available)
102
+ if (pythonCheck.available) {
103
+ console.log(chalk.bold('\nWhisper (Audio Transcription):'));
104
+ const fasterWhisperInstalled = await requirementsInstaller.isPackageInstalled('faster_whisper');
105
+ const whisperInstalled = await requirementsInstaller.isPackageInstalled('whisper');
106
+
107
+ if (fasterWhisperInstalled) {
108
+ console.log(chalk.green(' āœ… Faster-Whisper installed (optimized)'));
109
+ } else if (whisperInstalled) {
110
+ console.log(chalk.green(' āœ… OpenAI Whisper installed'));
111
+ } else {
112
+ console.log(chalk.yellow(' āš ļø Whisper not installed'));
113
+ console.log(chalk.dim(' Required for audio-transcriber skill'));
114
+ console.log(chalk.dim(' Install: pip install --user openai-whisper'));
115
+ warnings++;
116
+ }
117
+
118
+ // Check ffmpeg
119
+ console.log(chalk.bold('\nffmpeg (Audio Processing):'));
120
+ try {
121
+ execSync('which ffmpeg', { stdio: 'pipe' });
122
+ const ffmpegVersion = execSync('ffmpeg -version 2>&1 | head -1', {
123
+ encoding: 'utf-8',
124
+ stdio: 'pipe'
125
+ }).trim();
126
+ console.log(chalk.green(` āœ… Installed`));
127
+ console.log(chalk.dim(` ${ffmpegVersion.split('\n')[0]}`));
128
+ } catch {
129
+ console.log(chalk.yellow(' āš ļø Not installed (optional)'));
130
+ console.log(chalk.dim(' Recommended for audio format conversion'));
131
+ console.log(chalk.dim(' Install: brew install ffmpeg (macOS)'));
132
+ }
133
+ }
134
+
83
135
  console.log(chalk.cyan('\n' + '━'.repeat(60)));
84
136
  console.log(chalk.bold('\nNetwork Connectivity:\n'));
85
137
 
@@ -4,6 +4,7 @@ const PlatformDetector = require('../core/detector');
4
4
  const SkillDownloader = require('../core/downloader');
5
5
  const VersionChecker = require('../core/version-checker');
6
6
  const SkillInstaller = require('../core/installer');
7
+ const RequirementsInstaller = require('../core/requirements-installer');
7
8
  const InstallationPrompts = require('../ui/prompts');
8
9
  const ProgressGauge = require('../ui/progress-gauge');
9
10
  const path = require('path');
@@ -195,6 +196,69 @@ async function installCommand(skillNames, options) {
195
196
  results.installed.push(skillName);
196
197
  console.log(chalk.green(` āœ… Installed successfully`));
197
198
  }
199
+
200
+ // Check for Python requirements
201
+ const requirementsInstaller = new RequirementsInstaller();
202
+ const firstPlatform = selectedPlatforms[0];
203
+ const skillPath = path.join(installPaths[firstPlatform], skillName);
204
+
205
+ const requirements = await requirementsInstaller.detectRequirements(skillPath);
206
+
207
+ if (requirements.hasRequirements) {
208
+ console.log(chalk.blue('\n šŸ“¦ This skill requires Python dependencies'));
209
+
210
+ // Check Python availability
211
+ const pythonCheck = await requirementsInstaller.verifyPython();
212
+
213
+ if (!pythonCheck.available) {
214
+ console.log(chalk.yellow(' āš ļø Python 3 not found'));
215
+ console.log(chalk.dim(' Please install Python 3.8+ to use this skill'));
216
+ console.log(chalk.dim(' Download: https://www.python.org/downloads/'));
217
+ continue;
218
+ }
219
+
220
+ console.log(chalk.green(` āœ… Python detected: ${pythonCheck.version}`));
221
+
222
+ // Ask user if they want to install requirements
223
+ let installRequirements = options.yes;
224
+
225
+ if (!options.yes) {
226
+ const inquirer = require('inquirer');
227
+ const answer = await inquirer.prompt([
228
+ {
229
+ type: 'confirm',
230
+ name: 'install',
231
+ message: 'Install Python requirements now?',
232
+ default: true
233
+ }
234
+ ]);
235
+ installRequirements = answer.install;
236
+ }
237
+
238
+ if (installRequirements) {
239
+ const installResult = await requirementsInstaller.installRequirements(requirements, {
240
+ verbose: options.verbose
241
+ });
242
+
243
+ if (!installResult.success) {
244
+ console.log(chalk.yellow('\n šŸ’” You can install requirements later:'));
245
+ if (requirements.type === 'bash') {
246
+ console.log(chalk.dim(` bash ${requirements.scriptPath}`));
247
+ } else if (requirements.type === 'pip') {
248
+ console.log(chalk.dim(` pip install --user ${requirements.packages.join(' ')}`));
249
+ }
250
+ }
251
+ } else {
252
+ console.log(chalk.blue('\n ā„¹ļø Skipped requirements installation'));
253
+ console.log(chalk.dim(' You can install later:'));
254
+ if (requirements.type === 'bash') {
255
+ console.log(chalk.dim(` bash ${path.relative(process.cwd(), requirements.scriptPath)}`));
256
+ } else if (requirements.type === 'pip') {
257
+ console.log(chalk.dim(` pip install --user ${requirements.packages.join(' ')}`));
258
+ }
259
+ }
260
+ }
261
+
198
262
  } else {
199
263
  results.failed.push(skillName);
200
264
  console.log(chalk.red(` āŒ Failed: ${result.errors.join(', ')}`));
@@ -2,6 +2,7 @@ const chalk = require('chalk');
2
2
  const PlatformDetector = require('../core/detector');
3
3
  const SkillDownloader = require('../core/downloader');
4
4
  const VersionChecker = require('../core/version-checker');
5
+ const RequirementsInstaller = require('../core/requirements-installer');
5
6
  const path = require('path');
6
7
 
7
8
  async function listCommand(options) {
@@ -10,6 +11,7 @@ async function listCommand(options) {
10
11
  const detector = new PlatformDetector();
11
12
  const downloader = new SkillDownloader();
12
13
  const versionChecker = new VersionChecker();
14
+ const requirementsInstaller = new RequirementsInstaller();
13
15
 
14
16
  try {
15
17
  // Get available skills
@@ -25,6 +27,7 @@ async function listCommand(options) {
25
27
  let installedStatus = '⬜';
26
28
  let versionInfo = '';
27
29
  let platformList = [];
30
+ let requirementsStatus = '';
28
31
 
29
32
  // Check if installed in each platform
30
33
  for (const [platform, dirPath] of Object.entries(installPaths)) {
@@ -34,6 +37,20 @@ async function listCommand(options) {
34
37
  if (updateStatus.installed) {
35
38
  platformList.push(platform);
36
39
 
40
+ // Check requirements status only once
41
+ if (!requirementsStatus) {
42
+ const reqStatus = await requirementsInstaller.checkRequirementsStatus(skillPath);
43
+ if (reqStatus.hasRequirements) {
44
+ if (reqStatus.status === 'installed') {
45
+ requirementsStatus = chalk.green(` šŸ (${reqStatus.details.join(', ')})`);
46
+ } else if (reqStatus.status === 'not-installed') {
47
+ requirementsStatus = chalk.yellow(` šŸ (requirements not installed)`);
48
+ } else {
49
+ requirementsStatus = chalk.dim(` šŸ`);
50
+ }
51
+ }
52
+ }
53
+
37
54
  if (updateStatus.needsUpdate) {
38
55
  installedStatus = 'āš ļø ';
39
56
  versionInfo = chalk.yellow(` (v${updateStatus.currentVersion} → v${skill.version} available)`);
@@ -44,7 +61,7 @@ async function listCommand(options) {
44
61
  }
45
62
  }
46
63
 
47
- console.log(`${installedStatus} ${chalk.bold(skill.name)} v${skill.version}${versionInfo}`);
64
+ console.log(`${installedStatus} ${chalk.bold(skill.name)} v${skill.version}${versionInfo}${requirementsStatus}`);
48
65
  console.log(chalk.dim(` ${skill.description}`));
49
66
 
50
67
  if (platformList.length > 0) {
@@ -0,0 +1,202 @@
1
+ const fs = require('fs-extra');
2
+ const path = require('path');
3
+ const { execSync } = require('child_process');
4
+ const chalk = require('chalk');
5
+ const ora = require('ora');
6
+
7
+ /**
8
+ * Handles installation of Python requirements for skills
9
+ */
10
+ class RequirementsInstaller {
11
+ constructor() {
12
+ this.pythonCmd = 'python3';
13
+ }
14
+
15
+ /**
16
+ * Detect if skill has requirements to install
17
+ * @param {string} skillPath - Path to skill directory
18
+ * @returns {Promise<Object>} Requirements info
19
+ */
20
+ async detectRequirements(skillPath) {
21
+ // Check for install script (preferred method)
22
+ const installScript = path.join(skillPath, 'scripts', 'install-requirements.sh');
23
+
24
+ if (await fs.pathExists(installScript)) {
25
+ return {
26
+ hasRequirements: true,
27
+ scriptPath: installScript,
28
+ type: 'bash',
29
+ method: 'script'
30
+ };
31
+ }
32
+
33
+ // Check for requirements.txt (fallback)
34
+ const requirementsTxt = path.join(skillPath, 'requirements.txt');
35
+
36
+ if (await fs.pathExists(requirementsTxt)) {
37
+ const packages = await fs.readFile(requirementsTxt, 'utf-8');
38
+ const parsedPackages = packages
39
+ .split('\n')
40
+ .filter(p => p.trim() && !p.trim().startsWith('#'));
41
+ if (parsedPackages.length === 0) {
42
+ return { hasRequirements: false };
43
+ }
44
+ return {
45
+ hasRequirements: true,
46
+ file: requirementsTxt,
47
+ packages: parsedPackages,
48
+ type: 'pip',
49
+ method: 'requirements.txt'
50
+ };
51
+ }
52
+
53
+ return { hasRequirements: false };
54
+ }
55
+
56
+ /**
57
+ * Verify Python is available
58
+ * @returns {Promise<Object>} Python availability info
59
+ */
60
+ async verifyPython() {
61
+ try {
62
+ const version = execSync(`${this.pythonCmd} --version`, {
63
+ encoding: 'utf-8',
64
+ stdio: ['pipe', 'pipe', 'pipe']
65
+ }).trim();
66
+
67
+ return {
68
+ available: true,
69
+ version: version,
70
+ command: this.pythonCmd
71
+ };
72
+ } catch (error) {
73
+ return { available: false };
74
+ }
75
+ }
76
+
77
+ /**
78
+ * Install requirements for a skill
79
+ * @param {Object} requirements - Requirements object from detectRequirements()
80
+ * @param {Object} options - Installation options
81
+ * @returns {Promise<Object>} Installation result
82
+ */
83
+ async installRequirements(requirements, options = {}) {
84
+ if (!requirements.hasRequirements) {
85
+ return { success: true, skipped: true };
86
+ }
87
+
88
+ const spinner = ora('Installing skill requirements...').start();
89
+
90
+ try {
91
+ if (requirements.type === 'bash') {
92
+ // Execute installation script
93
+ spinner.text = `Running ${path.basename(requirements.scriptPath)}...`;
94
+
95
+ execSync(`bash "${requirements.scriptPath}"`, {
96
+ stdio: options.verbose ? 'inherit' : 'pipe',
97
+ cwd: path.dirname(requirements.scriptPath),
98
+ env: { ...process.env, TERM: 'dumb' } // Prevent interactive prompts
99
+ });
100
+
101
+ } else if (requirements.type === 'pip') {
102
+ // Install via pip
103
+ spinner.text = `Installing Python packages: ${requirements.packages.join(', ')}...`;
104
+
105
+ const packagesStr = requirements.packages.join(' ');
106
+ execSync(`${this.pythonCmd} -m pip install --user --break-system-packages ${packagesStr}`, {
107
+ stdio: options.verbose ? 'inherit' : 'pipe'
108
+ });
109
+ }
110
+
111
+ spinner.succeed(chalk.green('Requirements installed successfully'));
112
+ return { success: true };
113
+
114
+ } catch (error) {
115
+ spinner.fail(chalk.red('Requirements installation failed'));
116
+
117
+ console.error(chalk.yellow('\nāš ļø Automatic installation failed'));
118
+ console.error(chalk.gray(`Error: ${error.message}`));
119
+ console.error(chalk.yellow('\nšŸ’” You can install requirements manually:'));
120
+
121
+ if (requirements.type === 'bash') {
122
+ console.error(chalk.gray(` bash ${requirements.scriptPath}`));
123
+ } else if (requirements.type === 'pip') {
124
+ console.error(chalk.gray(` pip install --user ${requirements.packages.join(' ')}`));
125
+ }
126
+
127
+ return { success: false, error: error.message };
128
+ }
129
+ }
130
+
131
+ /**
132
+ * Verify if Python package is installed
133
+ * @param {string} packageName - Package name to check
134
+ * @returns {Promise<boolean>} True if installed
135
+ */
136
+ async isPackageInstalled(packageName) {
137
+ try {
138
+ execSync(`${this.pythonCmd} -c "import ${packageName}"`, {
139
+ stdio: 'pipe'
140
+ });
141
+ return true;
142
+ } catch {
143
+ return false;
144
+ }
145
+ }
146
+
147
+ /**
148
+ * Check status of requirements for installed skills
149
+ * @param {string} skillPath - Path to skill directory
150
+ * @returns {Promise<Object>} Status info
151
+ */
152
+ async checkRequirementsStatus(skillPath) {
153
+ const requirements = await this.detectRequirements(skillPath);
154
+
155
+ if (!requirements.hasRequirements) {
156
+ return { hasRequirements: false, status: 'n/a' };
157
+ }
158
+
159
+ // For audio-transcriber, check if Whisper is installed
160
+ const skillName = path.basename(skillPath);
161
+
162
+ if (skillName === 'audio-transcriber') {
163
+ const whisperInstalled = await this.isPackageInstalled('whisper');
164
+ const fasterWhisperInstalled = await this.isPackageInstalled('faster_whisper');
165
+
166
+ let status = 'not-installed';
167
+ let details = [];
168
+
169
+ if (fasterWhisperInstalled) {
170
+ status = 'installed';
171
+ details.push('faster-whisper');
172
+ } else if (whisperInstalled) {
173
+ status = 'installed';
174
+ details.push('openai-whisper');
175
+ }
176
+
177
+ // Check ffmpeg
178
+ try {
179
+ execSync('which ffmpeg', { stdio: 'pipe' });
180
+ details.push('ffmpeg');
181
+ } catch {
182
+ // ffmpeg not installed (optional)
183
+ }
184
+
185
+ return {
186
+ hasRequirements: true,
187
+ status,
188
+ details,
189
+ packages: fasterWhisperInstalled || whisperInstalled ? details : []
190
+ };
191
+ }
192
+
193
+ // For other Python skills in the future
194
+ return {
195
+ hasRequirements: true,
196
+ status: 'unknown',
197
+ details: []
198
+ };
199
+ }
200
+ }
201
+
202
+ module.exports = RequirementsInstaller;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cli-ai-skills",
3
- "version": "1.0.0",
3
+ "version": "1.3.1",
4
4
  "description": "Install AI skills for GitHub Copilot CLI and Claude Code",
5
5
  "main": "lib/index.js",
6
6
  "bin": {