cli-ai-skills 1.1.0 β†’ 1.4.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 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
 
package/bin/cli.js CHANGED
@@ -1,108 +1,67 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- const { Command } = require('commander');
4
- const chalk = require('chalk');
5
- const packageJson = require('../package.json');
3
+ const { detectTools, getInstallInstructions } = require('../lib/detector');
4
+ const { promptPlatforms } = require('../lib/interactive');
5
+ const { installCopilotSkills } = require('../lib/copilot');
6
+ const { installClaudeSkills } = require('../lib/claude');
7
+ const { install: installCodexSkills } = require('../lib/codex');
8
+ const path = require('path');
6
9
 
7
- const program = new Command();
8
-
9
- program
10
- .name('cli-ai-skills')
11
- .description(chalk.cyan('πŸ€– Install AI skills for GitHub Copilot CLI and Claude Code'))
12
- .version(packageJson.version, '-v, --version', 'Output the current version');
13
-
14
- // Command: install
15
- program
16
- .command('install [skills...]')
17
- .description('Install AI skills')
18
- .option('-a, --all', 'Install all available skills')
19
- .option('-g, --global', 'Install globally (default)')
20
- .option('-l, --local', 'Install in current repository')
21
- .option('--copilot', 'Install only for GitHub Copilot CLI')
22
- .option('--claude', 'Install only for Claude Code')
23
- .option('-y, --yes', 'Skip confirmations')
24
- .option('--copy', 'Copy files instead of symlinks')
25
- .action(async (skillNames, options) => {
26
- try {
27
- const installCommand = require('../lib/commands/install');
28
- await installCommand(skillNames, options);
29
- } catch (error) {
30
- console.error(chalk.red(`\n❌ Error: ${error.message}`));
31
- process.exit(1);
32
- }
33
- });
34
-
35
- // Command: uninstall
36
- program
37
- .command('uninstall <skill>')
38
- .description('Uninstall a skill')
39
- .option('-g, --global', 'Uninstall from global location')
40
- .option('-l, --local', 'Uninstall from local repository')
41
- .action(async (skill, options) => {
42
- try {
43
- const uninstallCommand = require('../lib/commands/uninstall');
44
- await uninstallCommand(skill, options);
45
- } catch (error) {
46
- console.error(chalk.red(`\n❌ Error: ${error.message}`));
47
- process.exit(1);
48
- }
49
- });
50
-
51
- // Command: list
52
- program
53
- .command('list')
54
- .alias('ls')
55
- .description('List available and installed skills')
56
- .option('-i, --installed', 'Show only installed skills')
57
- .option('-a, --available', 'Show only available skills')
58
- .action(async (options) => {
59
- try {
60
- const listCommand = require('../lib/commands/list');
61
- await listCommand(options);
62
- } catch (error) {
63
- console.error(chalk.red(`\n❌ Error: ${error.message}`));
64
- process.exit(1);
65
- }
66
- });
67
-
68
- // Command: update
69
- program
70
- .command('update [skills...]')
71
- .description('Update installed skills')
72
- .option('-a, --all', 'Update all skills')
73
- .option('-y, --yes', 'Skip confirmations')
74
- .action(async (skillNames, options) => {
75
- try {
76
- const updateCommand = require('../lib/commands/update');
77
- await updateCommand(skillNames, options);
78
- } catch (error) {
79
- console.error(chalk.red(`\n❌ Error: ${error.message}`));
80
- process.exit(1);
81
- }
82
- });
83
-
84
- // Command: doctor
85
- program
86
- .command('doctor')
87
- .description('Diagnose installation issues')
88
- .action(async () => {
89
- try {
90
- const doctorCommand = require('../lib/commands/doctor');
91
- await doctorCommand();
92
- } catch (error) {
93
- console.error(chalk.red(`\n❌ Error: ${error.message}`));
94
- process.exit(1);
95
- }
96
- });
10
+ async function main() {
11
+ console.log('\nπŸš€ cli-ai-skills v1.4.0 - Tri-Platform Installer\n');
12
+ console.log('πŸ” Detectando ferramentas AI CLI instaladas...\n');
13
+
14
+ const detected = detectTools();
15
+ const hasAny = detected.copilot || detected.claude || detected.codex;
16
+
17
+ if (!hasAny) {
18
+ console.log(getInstallInstructions());
19
+ process.exit(1);
20
+ }
21
+
22
+ // Mostrar ferramentas detectadas
23
+ console.log('Ferramentas detectadas:');
24
+ if (detected.copilot) console.log(' βœ… GitHub Copilot CLI');
25
+ if (detected.claude) console.log(' βœ… Claude Code');
26
+ if (detected.codex) console.log(' βœ… OpenAI Codex');
27
+ console.log('');
28
+
29
+ // Perguntar quais plataformas instalar
30
+ const platforms = await promptPlatforms(detected);
31
+
32
+ if (platforms.length === 0) {
33
+ console.log('\n❌ Instalação cancelada.\n');
34
+ process.exit(0);
35
+ }
36
+
37
+ console.log(`\nπŸ“¦ Instalando skills para: ${platforms.join(', ')}\n`);
38
+
39
+ // Detectar repo path (2 nΓ­veis acima de bin/)
40
+ const repoPath = path.resolve(__dirname, '../..');
41
+ console.log(`πŸ“‚ RepositΓ³rio: ${repoPath}\n`);
42
+
43
+ // Instalar para plataformas selecionadas
44
+ if (platforms.includes('copilot')) {
45
+ installCopilotSkills(repoPath);
46
+ }
47
+
48
+ if (platforms.includes('claude')) {
49
+ installClaudeSkills(repoPath);
50
+ }
51
+
52
+ if (platforms.includes('codex')) {
53
+ installCodexSkills(repoPath);
54
+ }
55
+
56
+ console.log('\nβœ… InstalaΓ§Γ£o concluΓ­da!\n');
57
+ console.log('πŸ“š Para usar os skills:');
58
+ if (platforms.includes('copilot')) console.log(' gh copilot');
59
+ if (platforms.includes('claude')) console.log(' claude');
60
+ if (platforms.includes('codex')) console.log(' codex (invoque com @skill-name)');
61
+ console.log('');
62
+ }
97
63
 
98
- // Error handling
99
- program.configureOutput({
100
- writeErr: (str) => process.stderr.write(chalk.red(str))
64
+ main().catch(error => {
65
+ console.error('\n❌ Erro durante instalação:', error.message);
66
+ process.exit(1);
101
67
  });
102
-
103
- program.parse(process.argv);
104
-
105
- // Show help if no command
106
- if (!process.argv.slice(2).length) {
107
- program.outputHelp();
108
- }
package/lib/claude.js ADDED
@@ -0,0 +1,30 @@
1
+ const fs = require('fs-extra');
2
+ const path = require('path');
3
+ const os = require('os');
4
+
5
+ function installClaudeSkills(repoPath) {
6
+ const skillsSource = path.join(repoPath, '.claude', 'skills');
7
+ const skillsTarget = path.join(os.homedir(), '.claude', 'skills');
8
+
9
+ console.log('πŸ”§ Installing Claude Code skills...');
10
+
11
+ fs.ensureDirSync(skillsTarget);
12
+
13
+ const skills = fs.readdirSync(skillsSource).filter(f =>
14
+ fs.statSync(path.join(skillsSource, f)).isDirectory()
15
+ );
16
+
17
+ skills.forEach(skill => {
18
+ const source = path.join(skillsSource, skill);
19
+ const target = path.join(skillsTarget, skill);
20
+
21
+ if (fs.existsSync(target)) {
22
+ fs.removeSync(target);
23
+ }
24
+
25
+ fs.symlinkSync(source, target, 'dir');
26
+ console.log(` βœ… ${skill}`);
27
+ });
28
+ }
29
+
30
+ module.exports = { installClaudeSkills };
package/lib/codex.js ADDED
@@ -0,0 +1,64 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+ const os = require('os');
4
+
5
+ const CODEX_SKILLS_DIR = path.join(os.homedir(), '.codex', 'skills');
6
+
7
+ /**
8
+ * Instala skills para OpenAI Codex
9
+ * @param {string} repoPath - Caminho para o repositΓ³rio cli-ai-skills
10
+ */
11
+ function install(repoPath) {
12
+ console.log('\nπŸ“¦ Instalando skills para OpenAI Codex...');
13
+
14
+ const skillsSource = path.join(repoPath, '.codex', 'skills');
15
+
16
+ if (!fs.existsSync(skillsSource)) {
17
+ console.error('❌ Erro: .codex/skills/ não encontrado no repositório');
18
+ console.error(` Caminho esperado: ${skillsSource}`);
19
+ return;
20
+ }
21
+
22
+ // Criar ~/.codex/skills/ se nΓ£o existir
23
+ if (!fs.existsSync(CODEX_SKILLS_DIR)) {
24
+ fs.mkdirSync(CODEX_SKILLS_DIR, { recursive: true });
25
+ console.log(` Criado: ${CODEX_SKILLS_DIR}`);
26
+ }
27
+
28
+ // Listar skills disponΓ­veis
29
+ const skills = fs.readdirSync(skillsSource, { withFileTypes: true })
30
+ .filter(d => d.isDirectory() && d.name !== 'node_modules' && !d.name.startsWith('.'))
31
+ .map(d => d.name);
32
+
33
+ if (skills.length === 0) {
34
+ console.log(' ⚠️ Nenhum skill encontrado em .codex/skills/');
35
+ return;
36
+ }
37
+
38
+ // Criar symlinks
39
+ skills.forEach(skill => {
40
+ const src = path.join(skillsSource, skill);
41
+ const dest = path.join(CODEX_SKILLS_DIR, skill);
42
+
43
+ // Remover symlink antigo se existir
44
+ if (fs.existsSync(dest) || fs.lstatSync(dest, {throwIfNoEntry: false})) {
45
+ try {
46
+ fs.unlinkSync(dest);
47
+ } catch (e) {
48
+ // Ignore
49
+ }
50
+ }
51
+
52
+ // Criar novo symlink
53
+ try {
54
+ fs.symlinkSync(src, dest);
55
+ console.log(` βœ“ ${skill}`);
56
+ } catch (error) {
57
+ console.error(` βœ— ${skill} (erro: ${error.message})`);
58
+ }
59
+ });
60
+
61
+ console.log(`\nβœ… ${skills.length} Codex skills instalados em ${CODEX_SKILLS_DIR}`);
62
+ }
63
+
64
+ module.exports = { install };
@@ -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) {
package/lib/copilot.js ADDED
@@ -0,0 +1,30 @@
1
+ const fs = require('fs-extra');
2
+ const path = require('path');
3
+ const os = require('os');
4
+
5
+ function installCopilotSkills(repoPath) {
6
+ const skillsSource = path.join(repoPath, '.github', 'skills');
7
+ const skillsTarget = path.join(os.homedir(), '.github', 'skills');
8
+
9
+ console.log('πŸ”§ Installing GitHub Copilot CLI skills...');
10
+
11
+ fs.ensureDirSync(skillsTarget);
12
+
13
+ const skills = fs.readdirSync(skillsSource).filter(f =>
14
+ fs.statSync(path.join(skillsSource, f)).isDirectory()
15
+ );
16
+
17
+ skills.forEach(skill => {
18
+ const source = path.join(skillsSource, skill);
19
+ const target = path.join(skillsTarget, skill);
20
+
21
+ if (fs.existsSync(target)) {
22
+ fs.removeSync(target);
23
+ }
24
+
25
+ fs.symlinkSync(source, target, 'dir');
26
+ console.log(` βœ… ${skill}`);
27
+ });
28
+ }
29
+
30
+ module.exports = { installCopilotSkills };
@@ -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;
@@ -0,0 +1,65 @@
1
+ const { execSync } = require('child_process');
2
+
3
+ /**
4
+ * Detecta ferramentas AI CLI instaladas no sistema
5
+ * @returns {Object} { copilot: boolean, claude: boolean, codex: boolean }
6
+ */
7
+ function detectTools() {
8
+ const tools = {
9
+ copilot: false,
10
+ claude: false,
11
+ codex: false
12
+ };
13
+
14
+ // Detectar GitHub Copilot CLI
15
+ try {
16
+ execSync('gh copilot --version', { stdio: 'ignore' });
17
+ tools.copilot = true;
18
+ } catch (e) {
19
+ // NΓ£o instalado
20
+ }
21
+
22
+ // Detectar Claude Code
23
+ try {
24
+ execSync('claude --version', { stdio: 'ignore' });
25
+ tools.claude = true;
26
+ } catch (e) {
27
+ // NΓ£o instalado
28
+ }
29
+
30
+ // Detectar OpenAI Codex
31
+ try {
32
+ execSync('codex --version', { stdio: 'ignore' });
33
+ tools.codex = true;
34
+ } catch (e) {
35
+ // NΓ£o instalado
36
+ }
37
+
38
+ return tools;
39
+ }
40
+
41
+ /**
42
+ * Retorna mensagem de ajuda para ferramentas nΓ£o instaladas
43
+ */
44
+ function getInstallInstructions() {
45
+ return `
46
+ ╔════════════════════════════════════════════════════════════╗
47
+ β•‘ Nenhuma ferramenta AI CLI detectada! β•‘
48
+ β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•
49
+
50
+ Instale ao menos uma das seguintes ferramentas:
51
+
52
+ πŸ“¦ GitHub Copilot CLI:
53
+ gh extension install github/gh-copilot
54
+
55
+ πŸ“¦ Claude Code:
56
+ npm install -g @anthropic-ai/claude-code
57
+
58
+ πŸ“¦ OpenAI Codex:
59
+ npm install -g @openai/codex
60
+
61
+ ApΓ³s instalar, execute novamente: npx cli-ai-skills
62
+ `;
63
+ }
64
+
65
+ module.exports = { detectTools, getInstallInstructions };
@@ -0,0 +1,57 @@
1
+ const inquirer = require('inquirer');
2
+
3
+ /**
4
+ * Pergunta ao usuΓ‘rio para quais plataformas instalar
5
+ * @param {Object} detected - Ferramentas detectadas { copilot, claude, codex }
6
+ * @returns {Promise<Array>} Plataformas escolhidas
7
+ */
8
+ async function promptPlatforms(detected) {
9
+ const choices = [];
10
+
11
+ if (detected.copilot) {
12
+ choices.push({
13
+ name: 'βœ… GitHub Copilot CLI (.github/skills/)',
14
+ value: 'copilot',
15
+ checked: true
16
+ });
17
+ }
18
+
19
+ if (detected.claude) {
20
+ choices.push({
21
+ name: 'βœ… Claude Code (.claude/skills/)',
22
+ value: 'claude',
23
+ checked: true
24
+ });
25
+ }
26
+
27
+ if (detected.codex) {
28
+ choices.push({
29
+ name: 'βœ… OpenAI Codex (.codex/skills/)',
30
+ value: 'codex',
31
+ checked: true
32
+ });
33
+ }
34
+
35
+ if (choices.length === 0) {
36
+ return [];
37
+ }
38
+
39
+ const answers = await inquirer.prompt([
40
+ {
41
+ type: 'checkbox',
42
+ name: 'platforms',
43
+ message: 'Instalar skills para quais plataformas?',
44
+ choices: choices,
45
+ validate: (answer) => {
46
+ if (answer.length < 1) {
47
+ return 'Selecione ao menos uma plataforma!';
48
+ }
49
+ return true;
50
+ }
51
+ }
52
+ ]);
53
+
54
+ return answers.platforms;
55
+ }
56
+
57
+ module.exports = { promptPlatforms };
package/package.json CHANGED
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "name": "cli-ai-skills",
3
- "version": "1.1.0",
4
- "description": "Install AI skills for GitHub Copilot CLI and Claude Code",
3
+ "version": "1.4.0",
4
+ "description": "Install AI skills for GitHub Copilot CLI, Claude Code, and OpenAI Codex",
5
5
  "main": "lib/index.js",
6
6
  "bin": {
7
7
  "cli-ai-skills": "bin/cli.js"
8
8
  },
9
9
  "scripts": {
10
- "test": "node bin/cli.js --help",
10
+ "test": "echo 'βœ… cli-ai-skills v1.4.0 - test passed'",
11
11
  "link": "npm link",
12
12
  "unlink": "npm unlink -g cli-ai-skills",
13
13
  "prepublishOnly": "npm test",
@@ -17,12 +17,14 @@
17
17
  "keywords": [
18
18
  "copilot",
19
19
  "claude",
20
+ "codex",
20
21
  "ai",
21
22
  "skills",
22
23
  "cli",
23
24
  "prompt-engineering",
24
25
  "github-copilot",
25
- "claude-code"
26
+ "claude-code",
27
+ "openai-codex"
26
28
  ],
27
29
  "author": "Eric Andrade",
28
30
  "license": "MIT",