voicci 1.0.9 → 1.0.10

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "voicci",
3
- "version": "1.0.9",
3
+ "version": "1.0.10",
4
4
  "description": "AI-Powered Audiobook Generator for Claude Code, OpenCode & AI Code Editors. Convert books and PDFs to audiobooks using natural language.",
5
5
  "type": "module",
6
6
  "main": "cli/index.js",
@@ -9,12 +9,13 @@ import { execFileSync } from 'child_process';
9
9
  const __filename = fileURLToPath(import.meta.url);
10
10
  const __dirname = path.dirname(__filename);
11
11
 
12
- // Two skill files: simple and detailed
13
- const SKILL_NAME_SIMPLE = 'voicci.md';
14
- const SKILL_NAME_DETAILED = 'voicci-audiobook.md';
15
-
16
- const SKILL_CONTENT_SIMPLE = `---
17
- description: "Voicci - AI audiobook generator"
12
+ // Skills use directory-based format: skills/<name>/SKILL.md
13
+ const SKILLS = [
14
+ {
15
+ dirName: 'voicci',
16
+ content: `---
17
+ name: voicci
18
+ description: "Voicci - AI audiobook generator. Use when the user wants to convert books, PDFs, or documents to audiobooks."
18
19
  argument-hint: "COMMAND_OR_FILE"
19
20
  ---
20
21
 
@@ -33,10 +34,13 @@ Convert books, PDFs, and documents to audiobooks using AI text-to-speech.
33
34
  #!/bin/bash
34
35
  voicci $ARGUMENTS
35
36
  \`\`\`
36
- `;
37
-
38
- const SKILL_CONTENT_DETAILED = `---
39
- description: "Voicci - AI audiobook generator CLI"
37
+ `
38
+ },
39
+ {
40
+ dirName: 'voicci-audiobook',
41
+ content: `---
42
+ name: voicci-audiobook
43
+ description: "Voicci - AI audiobook generator with full documentation. Use when the user needs detailed help with audiobook generation."
40
44
  argument-hint: "COMMAND_OR_FILE"
41
45
  ---
42
46
 
@@ -101,7 +105,6 @@ voicci memory
101
105
 
102
106
  \`\`\`!
103
107
  #!/bin/bash
104
- # Execute voicci with user-provided arguments
105
108
  voicci $ARGUMENTS
106
109
  \`\`\`
107
110
 
@@ -112,14 +115,9 @@ voicci $ARGUMENTS
112
115
  - macOS: \`~/Library/Application Support/voicci/audiobooks/\`
113
116
  - Linux: \`~/.local/share/voicci/audiobooks/\`
114
117
  - Copyright warning will appear before first book search
115
-
116
- ## Usage Examples
117
- - \`/voicci-audiobook "Lord of the Rings"\` - Search and convert book
118
- - \`/voicci-audiobook ~/Documents/book.pdf\` - Convert local file
119
- - \`/voicci-audiobook -s\` - Check all job statuses
120
- - \`/voicci-audiobook summary book.pdf\` - Generate summary only
121
- - \`/voicci-audiobook --help\` - Show all options
122
- `;
118
+ `
119
+ }
120
+ ];
123
121
 
124
122
  // Editor detection configurations
125
123
  const EDITORS = {
@@ -127,7 +125,6 @@ const EDITORS = {
127
125
  name: 'Claude Code',
128
126
  detect: () => {
129
127
  const homeDir = os.homedir();
130
- // Check for claude command or .claude directory
131
128
  if (fs.existsSync(path.join(homeDir, '.claude'))) return true;
132
129
  try {
133
130
  execFileSync('which', ['claude'], { stdio: 'ignore' });
@@ -136,11 +133,7 @@ const EDITORS = {
136
133
  return false;
137
134
  }
138
135
  },
139
- // Install to both skills/ and commands/ so slash commands appear in autocomplete
140
- skillsDirs: () => [
141
- path.join(os.homedir(), '.claude', 'commands'),
142
- path.join(os.homedir(), '.claude', 'skills')
143
- ]
136
+ skillsDir: () => path.join(os.homedir(), '.claude', 'skills')
144
137
  },
145
138
  'OpenCode': {
146
139
  name: 'OpenCode',
@@ -154,7 +147,7 @@ const EDITORS = {
154
147
  return false;
155
148
  }
156
149
  },
157
- skillsDirs: () => [path.join(os.homedir(), '.opencode', 'skills')]
150
+ skillsDir: () => path.join(os.homedir(), '.opencode', 'skills')
158
151
  },
159
152
  'Cursor': {
160
153
  name: 'Cursor',
@@ -169,17 +162,16 @@ const EDITORS = {
169
162
  return false;
170
163
  }
171
164
  },
172
- skillsDirs: () => {
165
+ skillsDir: () => {
173
166
  const homeDir = os.homedir();
174
167
  const locations = [
175
168
  path.join(homeDir, '.cursor', 'skills'),
176
169
  path.join(homeDir, 'Library', 'Application Support', 'Cursor', 'skills')
177
170
  ];
178
171
  for (const loc of locations) {
179
- const parent = path.dirname(loc);
180
- if (fs.existsSync(parent)) return [loc];
172
+ if (fs.existsSync(path.dirname(loc))) return loc;
181
173
  }
182
- return [locations[0]];
174
+ return locations[0];
183
175
  }
184
176
  },
185
177
  'Windsurf': {
@@ -194,64 +186,65 @@ const EDITORS = {
194
186
  return false;
195
187
  }
196
188
  },
197
- skillsDirs: () => [path.join(os.homedir(), '.windsurf', 'skills')]
189
+ skillsDir: () => path.join(os.homedir(), '.windsurf', 'skills')
198
190
  }
199
191
  };
200
192
 
201
- function installSkillFile(skillsDir, skillName, content) {
202
- const skillFile = path.join(skillsDir, skillName);
203
-
193
+ function installSkill(skillsDir, skill) {
194
+ const skillDir = path.join(skillsDir, skill.dirName);
195
+ const skillFile = path.join(skillDir, 'SKILL.md');
196
+
204
197
  // Check if already installed
205
198
  if (fs.existsSync(skillFile)) {
206
199
  const existingContent = fs.readFileSync(skillFile, 'utf8');
207
- if (existingContent.includes('voicci $ARGUMENTS')) {
200
+ if (existingContent.includes('voicci')) {
208
201
  return 'exists';
209
202
  }
210
203
  }
211
-
212
- // Write the skill file
213
- fs.writeFileSync(skillFile, content, 'utf8');
204
+
205
+ // Create skill directory and write SKILL.md
206
+ fs.mkdirSync(skillDir, { recursive: true });
207
+ fs.writeFileSync(skillFile, skill.content, 'utf8');
208
+
209
+ // Clean up legacy flat file if it exists
210
+ const legacyFile = path.join(skillsDir, `${skill.dirName}.md`);
211
+ if (fs.existsSync(legacyFile)) {
212
+ fs.unlinkSync(legacyFile);
213
+ }
214
+
214
215
  return 'new';
215
216
  }
216
217
 
217
218
  function detectAndInstall() {
218
- const homeDir = os.homedir();
219
219
  const installedEditors = [];
220
220
  const failedEditors = [];
221
221
 
222
- console.log('šŸ” Detecting AI code editors...\n');
222
+ console.log('Detecting AI code editors...\n');
223
223
 
224
- // Detect and install for each editor
225
224
  for (const [key, editor] of Object.entries(EDITORS)) {
226
225
  try {
227
226
  if (editor.detect()) {
228
- console.log(`āœ“ Found ${editor.name}`);
227
+ console.log(` Found ${editor.name}`);
229
228
 
230
- const dirs = editor.skillsDirs();
231
-
232
- let anyNew = false;
233
- let allExist = true;
229
+ const skillsDir = editor.skillsDir();
234
230
 
235
- for (const skillsDir of dirs) {
236
- // Create directory if it doesn't exist
237
- if (!fs.existsSync(skillsDir)) {
238
- fs.mkdirSync(skillsDir, { recursive: true });
239
- }
231
+ // Create skills directory if needed
232
+ if (!fs.existsSync(skillsDir)) {
233
+ fs.mkdirSync(skillsDir, { recursive: true });
234
+ }
240
235
 
241
- // Install both skills
242
- const simpleStatus = installSkillFile(skillsDir, SKILL_NAME_SIMPLE, SKILL_CONTENT_SIMPLE);
243
- const detailedStatus = installSkillFile(skillsDir, SKILL_NAME_DETAILED, SKILL_CONTENT_DETAILED);
236
+ let anyNew = false;
244
237
 
245
- if (simpleStatus === 'new' || detailedStatus === 'new') {
238
+ for (const skill of SKILLS) {
239
+ const status = installSkill(skillsDir, skill);
240
+ if (status === 'new') {
246
241
  anyNew = true;
247
- allExist = false;
248
- if (simpleStatus === 'new') console.log(` ↳ Installed: ${path.join(skillsDir, SKILL_NAME_SIMPLE)}`);
249
- if (detailedStatus === 'new') console.log(` ↳ Installed: ${path.join(skillsDir, SKILL_NAME_DETAILED)}`);
242
+ console.log(` Installed: ${path.join(skillsDir, skill.dirName, 'SKILL.md')}`);
250
243
  }
251
244
  }
252
245
 
253
246
  if (!anyNew) {
254
- console.log(` ↳ Skills already installed`);
247
+ console.log(` Skills already installed`);
255
248
  installedEditors.push({ name: editor.name, status: 'exists' });
256
249
  } else {
257
250
  installedEditors.push({ name: editor.name, status: 'new' });
@@ -263,37 +256,36 @@ function detectAndInstall() {
263
256
  }
264
257
 
265
258
  // Summary
266
- console.log('\n' + '═'.repeat(60));
259
+ console.log('\n' + '='.repeat(60));
267
260
 
268
261
  if (installedEditors.length > 0) {
269
- console.log('\nāœ… Voicci CLI installed successfully!');
270
- console.log('\nšŸ“¦ Command-line tool: voicci');
271
- console.log('šŸ”§ Skill commands: /voicci OR /voicci-audiobook');
272
- console.log('\nšŸ“ Installed in:');
262
+ console.log('\nVoicci CLI installed successfully!');
263
+ console.log('\n Command-line tool: voicci');
264
+ console.log(' Skill commands: /voicci OR /voicci-audiobook');
265
+ console.log('\n Installed in:');
273
266
  installedEditors.forEach(editor => {
274
267
  const status = editor.status === 'new' ? '(new)' : '(already installed)';
275
- console.log(` • ${editor.name} ${status}`);
268
+ console.log(` ${editor.name} ${status}`);
276
269
  });
277
270
 
278
- console.log('\nšŸ’” Usage:');
279
- console.log(' 1. Restart your AI code editor');
280
- console.log(' 2. Use: /voicci "search query" (simple)');
281
- console.log(' 3. Or: /voicci-audiobook "search query" (detailed docs)');
282
- console.log(' 4. Or CLI: voicci "your search query"');
271
+ console.log('\n Usage:');
272
+ console.log(' 1. Restart your AI code editor');
273
+ console.log(' 2. Type: /voicci "search query"');
274
+ console.log(' 3. Or CLI: voicci "your search query"');
283
275
  } else {
284
- console.log('\nāš ļø No supported AI code editors detected');
285
- console.log('\nSupported editors: Claude Code, OpenCode, Cursor, Windsurf');
286
- console.log('\nāœ“ You can still use the CLI: voicci <command>');
276
+ console.log('\n No supported AI code editors detected');
277
+ console.log('\n Supported editors: Claude Code, OpenCode, Cursor, Windsurf');
278
+ console.log('\n You can still use the CLI: voicci <command>');
287
279
  }
288
280
 
289
281
  if (failedEditors.length > 0) {
290
- console.log('\nāš ļø Failed to install for:');
282
+ console.log('\n Failed to install for:');
291
283
  failedEditors.forEach(({ name, error }) => {
292
- console.log(` • ${name}: ${error}`);
284
+ console.log(` ${name}: ${error}`);
293
285
  });
294
286
  }
295
287
 
296
- console.log('\n' + '═'.repeat(60));
288
+ console.log('\n' + '='.repeat(60));
297
289
  console.log();
298
290
  }
299
291