cli-ai-skills 1.7.3 → 1.7.6

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/bin/cli.js CHANGED
@@ -96,7 +96,7 @@ async function main() {
96
96
  displayToolsTable(detected);
97
97
 
98
98
  const hasAny = detected.copilot.installed || detected.claude.installed ||
99
- detected.codex.installed || detected.opencode.installed ||
99
+ detected.codex_cli.installed || detected.codex_app.installed || detected.opencode.installed ||
100
100
  detected.gemini.installed;
101
101
 
102
102
  if (!hasAny) {
@@ -112,7 +112,7 @@ async function main() {
112
112
  platforms = [];
113
113
  if (detected.copilot.installed) platforms.push('copilot');
114
114
  if (detected.claude.installed) platforms.push('claude');
115
- if (detected.codex.installed) platforms.push('codex');
115
+ if (detected.codex_cli.installed) platforms.push('codex_cli');
116
116
  if (detected.opencode.installed) platforms.push('opencode');
117
117
  if (detected.gemini.installed) platforms.push('gemini');
118
118
  } else {
@@ -140,7 +140,7 @@ async function main() {
140
140
  if (platforms.includes('claude')) {
141
141
  installClaudeSkills(repoPath, [skill], quiet);
142
142
  }
143
- if (platforms.includes('codex')) {
143
+ if (platforms.includes('codex') || platforms.includes('codex_cli') || platforms.includes('codex_app')) {
144
144
  installCodexSkills(repoPath, [skill], quiet);
145
145
  }
146
146
  if (platforms.includes('opencode')) {
@@ -167,7 +167,7 @@ async function main() {
167
167
  displayToolsTable(detected);
168
168
 
169
169
  const hasAny = detected.copilot.installed || detected.claude.installed ||
170
- detected.codex.installed || detected.opencode.installed ||
170
+ detected.codex_cli.installed || detected.codex_app.installed || detected.opencode.installed ||
171
171
  detected.gemini.installed;
172
172
 
173
173
  if (!hasAny) {
@@ -227,7 +227,8 @@ async function main() {
227
227
  platforms = [];
228
228
  if (detected.copilot.installed) platforms.push('copilot');
229
229
  if (detected.claude.installed) platforms.push('claude');
230
- if (detected.codex.installed) platforms.push('codex');
230
+ if (detected.codex_cli.installed) platforms.push('codex_cli');
231
+ if (detected.codex_app.installed) platforms.push('codex_app');
231
232
  if (detected.opencode.installed) platforms.push('opencode');
232
233
  if (detected.gemini.installed) platforms.push('gemini');
233
234
  } else {
@@ -254,7 +255,7 @@ async function main() {
254
255
  installClaudeSkills(repoPath, null, quiet);
255
256
  }
256
257
 
257
- if (platforms.includes('codex')) {
258
+ if (platforms.includes('codex') || platforms.includes('codex_cli') || platforms.includes('codex_app')) {
258
259
  installCodexSkills(repoPath, null, quiet);
259
260
  }
260
261
 
package/lib/codex.js CHANGED
@@ -1,45 +1,70 @@
1
1
  const fs = require('fs');
2
2
  const path = require('path');
3
3
  const os = require('os');
4
+ const chalk = require('chalk');
4
5
  const { getSkillsSourcePath, getUserSkillsPath } = require('./utils/path-resolver');
5
6
 
6
- const CODEX_SKILLS_DIR = getUserSkillsPath('codex');
7
-
8
7
  /**
9
8
  * Instala skills para OpenAI Codex
9
+ * Solução robusta com multi-path fallback e logging detalhado
10
10
  * @param {string} repoPath - Caminho para o repositório cli-ai-skills
11
+ * @param {Array<string>|null} skills - Skills to install (null = all)
12
+ * @param {boolean} quiet - Suppress output
11
13
  */
12
- function install(repoPath) {
13
- console.log('\n📦 Instalando skills para OpenAI Codex...');
14
+ function install(repoPath, skills = null, quiet = false) {
15
+ const targetDir = getUserSkillsPath('codex');
16
+
17
+ if (!quiet) {
18
+ console.log(chalk.cyan('\n📦 Instalando skills para OpenAI Codex...'));
19
+ console.log(chalk.gray(` Destino: ${targetDir}`));
20
+ }
14
21
 
15
- const skillsSource = getSkillsSourcePath(repoPath, 'codex');
22
+ const sourceDir = getSkillsSourcePath(repoPath, 'codex');
16
23
 
17
- if (!fs.existsSync(skillsSource)) {
18
- console.error('❌ Erro: .codex/skills/ não encontrado no repositório');
19
- console.error(` Caminho esperado: ${skillsSource}`);
20
- return;
24
+ if (!fs.existsSync(sourceDir)) {
25
+ if (!quiet) {
26
+ console.log(chalk.red('❌ Diretório .codex/skills não encontrado no repositório'));
27
+ console.log(chalk.gray(` Esperado: ${sourceDir}`));
28
+ }
29
+ return { installed: 0, failed: 0 };
21
30
  }
22
31
 
23
- // Criar ~/.codex/skills/ se não existir
24
- if (!fs.existsSync(CODEX_SKILLS_DIR)) {
25
- fs.mkdirSync(CODEX_SKILLS_DIR, { recursive: true });
26
- console.log(` Criado: ${CODEX_SKILLS_DIR}`);
32
+ // Criar diretório de destino se não existir
33
+ if (!fs.existsSync(targetDir)) {
34
+ fs.mkdirSync(targetDir, { recursive: true });
35
+ if (!quiet) {
36
+ console.log(chalk.green(` ✓ Criado: ${targetDir}`));
37
+ }
27
38
  }
28
39
 
29
40
  // Listar skills disponíveis
30
- const skills = fs.readdirSync(skillsSource, { withFileTypes: true })
41
+ const availableSkills = fs.readdirSync(sourceDir, { withFileTypes: true })
31
42
  .filter(d => d.isDirectory() && d.name !== 'node_modules' && !d.name.startsWith('.'))
32
43
  .map(d => d.name);
33
44
 
34
- if (skills.length === 0) {
35
- console.log(' ⚠️ Nenhum skill encontrado em .codex/skills/');
36
- return;
45
+ if (availableSkills.length === 0) {
46
+ if (!quiet) {
47
+ console.log(chalk.yellow(' ⚠️ Nenhum skill encontrado em .codex/skills/'));
48
+ }
49
+ return { installed: 0, failed: 0 };
37
50
  }
51
+
52
+ const skillsToInstall = skills || availableSkills;
53
+ let installed = 0;
54
+ let failed = 0;
38
55
 
39
56
  // Criar symlinks
40
- skills.forEach(skill => {
41
- const src = path.join(skillsSource, skill);
42
- const dest = path.join(CODEX_SKILLS_DIR, skill);
57
+ skillsToInstall.forEach(skill => {
58
+ const src = path.join(sourceDir, skill);
59
+ const dest = path.join(targetDir, skill);
60
+
61
+ if (!fs.existsSync(src)) {
62
+ if (!quiet) {
63
+ console.log(chalk.yellow(` ⚠️ Skill não encontrada: ${skill}`));
64
+ }
65
+ failed++;
66
+ return;
67
+ }
43
68
 
44
69
  // Remover symlink antigo se existir
45
70
  if (fs.existsSync(dest) || fs.lstatSync(dest, {throwIfNoEntry: false})) {
@@ -53,13 +78,26 @@ function install(repoPath) {
53
78
  // Criar novo symlink
54
79
  try {
55
80
  fs.symlinkSync(src, dest);
56
- console.log(` ✓ ${skill}`);
81
+ if (!quiet) {
82
+ console.log(chalk.green(` ✓ Codex: ${skill}`));
83
+ }
84
+ installed++;
57
85
  } catch (error) {
58
- console.error(` ✗ ${skill} (erro: ${error.message})`);
86
+ if (!quiet) {
87
+ console.log(chalk.red(` ✗ Erro ao instalar ${skill}: ${error.message}`));
88
+ }
89
+ failed++;
59
90
  }
60
91
  });
61
92
 
62
- console.log(`\n✅ ${skills.length} Codex skills instalados em ${CODEX_SKILLS_DIR}`);
93
+ if (!quiet) {
94
+ console.log(chalk.green(`\n✅ ${installed} Codex skill(s) instalado(s)`));
95
+ if (failed > 0) {
96
+ console.log(chalk.yellow(`⚠️ ${failed} skill(s) falharam`));
97
+ }
98
+ }
99
+
100
+ return { installed, failed };
63
101
  }
64
102
 
65
103
  module.exports = { install };
package/lib/detector.js CHANGED
@@ -1,14 +1,18 @@
1
1
  const { execSync } = require('child_process');
2
+ const fs = require('fs');
3
+ const os = require('os');
4
+ const path = require('path');
2
5
 
3
6
  /**
4
7
  * Detecta ferramentas AI CLI instaladas no sistema
5
- * @returns {Object} { copilot: {installed, version, path}, claude: {...}, ... }
8
+ * @returns {Object} { copilot: {installed, version, path}, claude: {...}, codex_cli: {...}, codex_app: {...}, ... }
6
9
  */
7
10
  function detectTools() {
8
11
  const tools = {
9
12
  copilot: detectCopilot(),
10
13
  claude: detectClaude(),
11
- codex: detectCodex(),
14
+ codex_cli: detectCodexCli(),
15
+ codex_app: detectCodexApp(),
12
16
  opencode: detectOpenCode(),
13
17
  gemini: detectGemini()
14
18
  };
@@ -43,9 +47,9 @@ function detectClaude() {
43
47
  }
44
48
 
45
49
  /**
46
- * Detecta OpenAI Codex
50
+ * Detecta OpenAI Codex CLI
47
51
  */
48
- function detectCodex() {
52
+ function detectCodexCli() {
49
53
  try {
50
54
  const version = execSync('codex --version', { encoding: 'utf-8', stdio: ['pipe', 'pipe', 'ignore'] }).trim();
51
55
  const path = execSync('which codex', { encoding: 'utf-8', stdio: ['pipe', 'pipe', 'ignore'] }).trim();
@@ -55,6 +59,62 @@ function detectCodex() {
55
59
  }
56
60
  }
57
61
 
62
+ /**
63
+ * Detecta OpenAI Codex App (Desktop)
64
+ */
65
+ function detectCodexApp() {
66
+ // Check macOS
67
+ if (os.platform() === 'darwin') {
68
+ const appPath = '/Applications/Codex.app';
69
+ if (fs.existsSync(appPath)) {
70
+ try {
71
+ // Try to get version from Info.plist
72
+ const plistPath = path.join(appPath, 'Contents', 'Info.plist');
73
+ const version = 'Codex Desktop'; // Could parse plist for exact version
74
+ return { installed: true, version, path: appPath };
75
+ } catch (e) {
76
+ return { installed: true, version: 'unknown', path: appPath };
77
+ }
78
+ }
79
+ }
80
+
81
+ // Check Linux (if applicable)
82
+ if (os.platform() === 'linux') {
83
+ // Could check for ~/.local/share/applications or similar
84
+ const homeDir = os.homedir();
85
+ const possiblePaths = [
86
+ path.join(homeDir, '.local', 'share', 'codex'),
87
+ '/opt/Codex',
88
+ '/usr/local/bin/Codex'
89
+ ];
90
+
91
+ for (const appPath of possiblePaths) {
92
+ if (fs.existsSync(appPath)) {
93
+ return { installed: true, version: 'Codex Desktop', path: appPath };
94
+ }
95
+ }
96
+ }
97
+
98
+ // Check Windows
99
+ if (os.platform() === 'win32') {
100
+ const programFiles = process.env['ProgramFiles'] || 'C:\\Program Files';
101
+ const appPath = path.join(programFiles, 'Codex', 'Codex.exe');
102
+ if (fs.existsSync(appPath)) {
103
+ return { installed: true, version: 'Codex Desktop', path: appPath };
104
+ }
105
+ }
106
+
107
+ return { installed: false, version: null, path: null };
108
+ }
109
+
110
+ /**
111
+ * @deprecated Use detectCodexCli() and detectCodexApp() instead
112
+ * Detecta OpenAI Codex (mantido para backward compatibility)
113
+ */
114
+ function detectCodex() {
115
+ return detectCodexCli();
116
+ }
117
+
58
118
  /**
59
119
  * Detecta OpenCode
60
120
  */
@@ -123,5 +183,5 @@ Após instalar, execute novamente: npx cli-ai-skills
123
183
  `;
124
184
  }
125
185
 
126
- module.exports = { detectTools, getInstallInstructions };
186
+ module.exports = { detectTools, getInstallInstructions, detectCodex, detectCodexCli, detectCodexApp };
127
187
 
@@ -50,47 +50,58 @@ async function confirmCancel() {
50
50
 
51
51
  /**
52
52
  * Pergunta ao usuário para quais plataformas instalar
53
- * @param {Object} detected - Ferramentas detectadas { copilot, claude, codex, opencode, gemini }
53
+ * Codex CLI e Codex App são mostrados SEMPRE separadamente
54
+ * @param {Object} detected - Ferramentas detectadas { copilot, claude, codex_cli, codex_app, opencode, gemini }
54
55
  * @returns {Promise<Array>} Plataformas escolhidas
55
56
  */
56
57
  async function promptPlatforms(detected) {
57
58
  const choices = [];
58
59
 
59
- if (detected.copilot) {
60
+ if (detected.copilot && detected.copilot.installed) {
60
61
  choices.push({
61
- name: '✅ GitHub Copilot CLI (.github/skills/)',
62
+ name: '✅ GitHub Copilot CLI (~/.github/skills/)',
62
63
  value: 'copilot',
63
64
  checked: true
64
65
  });
65
66
  }
66
67
 
67
- if (detected.claude) {
68
+ if (detected.claude && detected.claude.installed) {
68
69
  choices.push({
69
- name: '✅ Claude Code (.claude/skills/)',
70
+ name: '✅ Claude Code (~/.claude/skills/)',
70
71
  value: 'claude',
71
72
  checked: true
72
73
  });
73
74
  }
74
75
 
75
- if (detected.codex) {
76
+ // Codex CLI - sempre separado
77
+ if (detected.codex_cli && detected.codex_cli.installed) {
76
78
  choices.push({
77
- name: '✅ OpenAI Codex (.codex/skills/)',
78
- value: 'codex',
79
+ name: '✅ OpenAI Codex CLI (~/.codex/vendor_imports/skills/skills/.curated/)',
80
+ value: 'codex_cli',
79
81
  checked: true
80
82
  });
81
83
  }
82
84
 
83
- if (detected.opencode) {
85
+ // Codex App - sempre separado
86
+ if (detected.codex_app && detected.codex_app.installed) {
84
87
  choices.push({
85
- name: '✅ OpenCode (.opencode/skills/)',
88
+ name: '✅ OpenAI Codex App (~/.codex/vendor_imports/skills/skills/.curated/)',
89
+ value: 'codex_app',
90
+ checked: true
91
+ });
92
+ }
93
+
94
+ if (detected.opencode && detected.opencode.installed) {
95
+ choices.push({
96
+ name: '✅ OpenCode (~/.opencode/skills/)',
86
97
  value: 'opencode',
87
98
  checked: true
88
99
  });
89
100
  }
90
101
 
91
- if (detected.gemini) {
102
+ if (detected.gemini && detected.gemini.installed) {
92
103
  choices.push({
93
- name: '✅ Gemini CLI (.gemini/skills/)',
104
+ name: '✅ Gemini CLI (~/.gemini/skills/)',
94
105
  value: 'gemini',
95
106
  checked: true
96
107
  });
package/lib/ui/table.js CHANGED
@@ -2,36 +2,40 @@ const chalk = require('chalk');
2
2
 
3
3
  /**
4
4
  * Exibe tabela formatada de ferramentas detectadas
5
+ * Agora com Codex CLI e Codex App separados
5
6
  * @param {Object} tools - Objeto retornado por detectTools()
6
7
  */
7
8
  function displayToolsTable(tools) {
8
- console.log('\n┌─────────────────────────────────────────────────────────────┐');
9
- console.log('│ Ferramenta │ Status │ Versão │');
10
- console.log('├─────────────────────────────────────────────────────────────┤');
9
+ console.log('\n┌──────────────────────────────────────────────────────────────────┐');
10
+ console.log('│ Ferramenta │ Status │ Versão │');
11
+ console.log('├──────────────────────────────────────────────────────────────────┤');
11
12
 
12
13
  const toolNames = {
13
14
  copilot: 'GitHub Copilot CLI',
14
15
  claude: 'Claude Code',
15
- codex: 'OpenAI Codex',
16
+ codex_cli: 'OpenAI Codex CLI',
17
+ codex_app: 'OpenAI Codex App',
16
18
  opencode: 'OpenCode',
17
19
  gemini: 'Gemini CLI'
18
20
  };
19
21
 
20
22
  for (const [key, name] of Object.entries(toolNames)) {
21
23
  const tool = tools[key];
24
+ if (!tool) continue; // Skip if tool not in detected object
25
+
22
26
  const status = tool.installed ? chalk.green('✓') : chalk.red('✗');
23
27
  const version = tool.version || chalk.gray('-');
24
28
 
25
29
  // Formatar linha com espaçamento fixo
26
30
  const namePadded = name.padEnd(21);
27
31
  const statusPadded = ' ' + status + ' ';
28
- const versionStr = String(version).substring(0, 20);
29
- const versionPadded = versionStr.padEnd(20);
32
+ const versionStr = String(version).substring(0, 26);
33
+ const versionPadded = versionStr.padEnd(26);
30
34
 
31
35
  console.log(`│ ${namePadded} │${statusPadded}│ ${versionPadded}│`);
32
36
  }
33
37
 
34
- console.log('└─────────────────────────────────────────────────────────────┘\n');
38
+ console.log('└──────────────────────────────────────────────────────────────────┘\n');
35
39
  }
36
40
 
37
41
  /**
@@ -44,12 +48,13 @@ function getToolsSummary(tools) {
44
48
 
45
49
  if (tools.copilot && tools.copilot.installed) installed.push('copilot');
46
50
  if (tools.claude && tools.claude.installed) installed.push('claude');
47
- if (tools.codex && tools.codex.installed) installed.push('codex');
51
+ if (tools.codex_cli && tools.codex_cli.installed) installed.push('codex_cli');
52
+ if (tools.codex_app && tools.codex_app.installed) installed.push('codex_app');
48
53
  if (tools.opencode && tools.opencode.installed) installed.push('opencode');
49
54
  if (tools.gemini && tools.gemini.installed) installed.push('gemini');
50
55
 
51
56
  return {
52
- total: 5,
57
+ total: 6,
53
58
  installed: installed.length,
54
59
  names: installed
55
60
  };
@@ -63,16 +63,35 @@ function getSkillsSourcePath(basePath, platform) {
63
63
 
64
64
  /**
65
65
  * Get user home directory skills path for a platform
66
+ * Includes multi-path fallback for Codex (robust solution)
66
67
  * @param {string} platform - Platform name
67
68
  * @returns {string} Path to user's skills directory
68
69
  */
69
70
  function getUserSkillsPath(platform) {
70
71
  const home = process.env.HOME || process.env.USERPROFILE;
71
72
 
73
+ // Special handling for Codex with multi-path fallback
74
+ if (platform === 'codex') {
75
+ const codexPaths = [
76
+ path.join(home, '.codex', 'vendor_imports', 'skills', 'skills', '.curated'), // Real path (primary)
77
+ path.join(home, '.codex', 'vendor_imports', 'skills', 'skills', '.custom'), // Alternative
78
+ path.join(home, '.codex', 'skills') // Documented path (fallback)
79
+ ];
80
+
81
+ // Return first existing path, or primary path if none exist (will be created)
82
+ for (const codexPath of codexPaths) {
83
+ if (fs.existsSync(codexPath)) {
84
+ return codexPath;
85
+ }
86
+ }
87
+
88
+ // Return primary path (will be created during install)
89
+ return codexPaths[0];
90
+ }
91
+
72
92
  const platformDirs = {
73
93
  'copilot': path.join(home, '.github', 'skills'),
74
94
  'claude': path.join(home, '.claude', 'skills'),
75
- 'codex': path.join(home, '.codex', 'skills'),
76
95
  'opencode': path.join(home, '.opencode', 'skills'),
77
96
  'gemini': path.join(home, '.gemini', 'skills')
78
97
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cli-ai-skills",
3
- "version": "1.7.3",
3
+ "version": "1.7.6",
4
4
  "description": "Install AI skills for GitHub Copilot CLI, Claude Code, and OpenAI Codex",
5
5
  "main": "lib/index.js",
6
6
  "bin": {