voicci 1.0.6 ā 1.0.8
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 +1 -1
- package/scripts/postinstall.js +200 -34
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "voicci",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.8",
|
|
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",
|
package/scripts/postinstall.js
CHANGED
|
@@ -4,16 +4,43 @@ import fs from 'fs';
|
|
|
4
4
|
import path from 'path';
|
|
5
5
|
import os from 'os';
|
|
6
6
|
import { fileURLToPath } from 'url';
|
|
7
|
+
import { execFileSync } from 'child_process';
|
|
7
8
|
|
|
8
9
|
const __filename = fileURLToPath(import.meta.url);
|
|
9
10
|
const __dirname = path.dirname(__filename);
|
|
10
11
|
|
|
11
|
-
|
|
12
|
-
|
|
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"
|
|
18
|
+
argument-hint: "COMMAND_OR_FILE"
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
# Voicci - AI Audiobook Generator
|
|
22
|
+
|
|
23
|
+
Convert books, PDFs, and documents to audiobooks using AI text-to-speech.
|
|
24
|
+
|
|
25
|
+
## Quick Examples
|
|
26
|
+
- \`/voicci "Lord of the Rings"\` - Search and convert book
|
|
27
|
+
- \`/voicci mybook.pdf\` - Convert local file
|
|
28
|
+
- \`/voicci -s\` - Check job status
|
|
29
|
+
- \`/voicci -l\` - List audiobooks
|
|
30
|
+
- \`/voicci --help\` - Show all options
|
|
31
|
+
|
|
32
|
+
\`\`\`!
|
|
33
|
+
#!/bin/bash
|
|
34
|
+
voicci $ARGUMENTS
|
|
35
|
+
\`\`\`
|
|
36
|
+
`;
|
|
37
|
+
|
|
38
|
+
const SKILL_CONTENT_DETAILED = `---
|
|
39
|
+
description: "Voicci - AI audiobook generator CLI"
|
|
13
40
|
argument-hint: "COMMAND_OR_FILE"
|
|
14
41
|
---
|
|
15
42
|
|
|
16
|
-
# Voicci
|
|
43
|
+
# Voicci - AI Audiobook Generator
|
|
17
44
|
|
|
18
45
|
Convert books, PDFs, and documents to audiobooks using AI-powered text-to-speech (XTTS v2).
|
|
19
46
|
|
|
@@ -75,7 +102,7 @@ voicci memory
|
|
|
75
102
|
\`\`\`!
|
|
76
103
|
#!/bin/bash
|
|
77
104
|
# Execute voicci with user-provided arguments
|
|
78
|
-
voicci
|
|
105
|
+
voicci $ARGUMENTS
|
|
79
106
|
\`\`\`
|
|
80
107
|
|
|
81
108
|
## Important Notes
|
|
@@ -87,41 +114,180 @@ voicci \$ARGUMENTS
|
|
|
87
114
|
- Copyright warning will appear before first book search
|
|
88
115
|
|
|
89
116
|
## Usage Examples
|
|
90
|
-
- \`/voicci-
|
|
91
|
-
- \`/voicci-
|
|
92
|
-
- \`/voicci-
|
|
93
|
-
- \`/voicci-
|
|
94
|
-
- \`/voicci-
|
|
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
|
|
95
122
|
`;
|
|
96
123
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
124
|
+
// Editor detection configurations
|
|
125
|
+
const EDITORS = {
|
|
126
|
+
'Claude Code': {
|
|
127
|
+
name: 'Claude Code',
|
|
128
|
+
detect: () => {
|
|
129
|
+
const homeDir = os.homedir();
|
|
130
|
+
// Check for claude command or .claude directory
|
|
131
|
+
if (fs.existsSync(path.join(homeDir, '.claude'))) return true;
|
|
132
|
+
try {
|
|
133
|
+
execFileSync('which', ['claude'], { stdio: 'ignore' });
|
|
134
|
+
return true;
|
|
135
|
+
} catch {
|
|
136
|
+
return false;
|
|
137
|
+
}
|
|
138
|
+
},
|
|
139
|
+
skillsDir: () => path.join(os.homedir(), '.claude', 'skills')
|
|
140
|
+
},
|
|
141
|
+
'OpenCode': {
|
|
142
|
+
name: 'OpenCode',
|
|
143
|
+
detect: () => {
|
|
144
|
+
const homeDir = os.homedir();
|
|
145
|
+
// Check for opencode command or config directory
|
|
146
|
+
if (fs.existsSync(path.join(homeDir, '.opencode'))) return true;
|
|
147
|
+
try {
|
|
148
|
+
execFileSync('which', ['opencode'], { stdio: 'ignore' });
|
|
149
|
+
return true;
|
|
150
|
+
} catch {
|
|
151
|
+
return false;
|
|
152
|
+
}
|
|
153
|
+
},
|
|
154
|
+
skillsDir: () => path.join(os.homedir(), '.opencode', 'skills')
|
|
155
|
+
},
|
|
156
|
+
'Cursor': {
|
|
157
|
+
name: 'Cursor',
|
|
158
|
+
detect: () => {
|
|
159
|
+
const homeDir = os.homedir();
|
|
160
|
+
// Check for cursor command or config directory
|
|
161
|
+
if (fs.existsSync(path.join(homeDir, '.cursor'))) return true;
|
|
162
|
+
if (fs.existsSync(path.join(homeDir, 'Library', 'Application Support', 'Cursor'))) return true;
|
|
163
|
+
try {
|
|
164
|
+
execFileSync('which', ['cursor'], { stdio: 'ignore' });
|
|
165
|
+
return true;
|
|
166
|
+
} catch {
|
|
167
|
+
return false;
|
|
168
|
+
}
|
|
169
|
+
},
|
|
170
|
+
skillsDir: () => {
|
|
171
|
+
const homeDir = os.homedir();
|
|
172
|
+
// Try common locations
|
|
173
|
+
const locations = [
|
|
174
|
+
path.join(homeDir, '.cursor', 'skills'),
|
|
175
|
+
path.join(homeDir, 'Library', 'Application Support', 'Cursor', 'skills')
|
|
176
|
+
];
|
|
177
|
+
for (const loc of locations) {
|
|
178
|
+
const parent = path.dirname(loc);
|
|
179
|
+
if (fs.existsSync(parent)) return loc;
|
|
180
|
+
}
|
|
181
|
+
return locations[0]; // Default to first
|
|
182
|
+
}
|
|
183
|
+
},
|
|
184
|
+
'Windsurf': {
|
|
185
|
+
name: 'Windsurf',
|
|
186
|
+
detect: () => {
|
|
187
|
+
const homeDir = os.homedir();
|
|
188
|
+
// Check for windsurf command or config directory
|
|
189
|
+
if (fs.existsSync(path.join(homeDir, '.windsurf'))) return true;
|
|
190
|
+
try {
|
|
191
|
+
execFileSync('which', ['windsurf'], { stdio: 'ignore' });
|
|
192
|
+
return true;
|
|
193
|
+
} catch {
|
|
194
|
+
return false;
|
|
195
|
+
}
|
|
196
|
+
},
|
|
197
|
+
skillsDir: () => path.join(os.homedir(), '.windsurf', 'skills')
|
|
198
|
+
}
|
|
199
|
+
};
|
|
102
200
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
201
|
+
function installSkillFile(skillsDir, skillName, content) {
|
|
202
|
+
const skillFile = path.join(skillsDir, skillName);
|
|
203
|
+
|
|
204
|
+
// Check if already installed
|
|
205
|
+
if (fs.existsSync(skillFile)) {
|
|
206
|
+
const existingContent = fs.readFileSync(skillFile, 'utf8');
|
|
207
|
+
if (existingContent.includes('voicci $ARGUMENTS')) {
|
|
208
|
+
return 'exists';
|
|
106
209
|
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// Write the skill file
|
|
213
|
+
fs.writeFileSync(skillFile, content, 'utf8');
|
|
214
|
+
return 'new';
|
|
215
|
+
}
|
|
107
216
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
217
|
+
function detectAndInstall() {
|
|
218
|
+
const homeDir = os.homedir();
|
|
219
|
+
const installedEditors = [];
|
|
220
|
+
const failedEditors = [];
|
|
221
|
+
|
|
222
|
+
console.log('š Detecting AI code editors...\n');
|
|
223
|
+
|
|
224
|
+
// Detect and install for each editor
|
|
225
|
+
for (const [key, editor] of Object.entries(EDITORS)) {
|
|
226
|
+
try {
|
|
227
|
+
if (editor.detect()) {
|
|
228
|
+
console.log(`ā Found ${editor.name}`);
|
|
229
|
+
|
|
230
|
+
const skillsDir = editor.skillsDir();
|
|
231
|
+
|
|
232
|
+
// Create skills directory if it doesn't exist
|
|
233
|
+
if (!fs.existsSync(skillsDir)) {
|
|
234
|
+
fs.mkdirSync(skillsDir, { recursive: true });
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// Install both skills
|
|
238
|
+
const simpleStatus = installSkillFile(skillsDir, SKILL_NAME_SIMPLE, SKILL_CONTENT_SIMPLE);
|
|
239
|
+
const detailedStatus = installSkillFile(skillsDir, SKILL_NAME_DETAILED, SKILL_CONTENT_DETAILED);
|
|
240
|
+
|
|
241
|
+
const simpleFile = path.join(skillsDir, SKILL_NAME_SIMPLE);
|
|
242
|
+
const detailedFile = path.join(skillsDir, SKILL_NAME_DETAILED);
|
|
243
|
+
|
|
244
|
+
if (simpleStatus === 'exists' && detailedStatus === 'exists') {
|
|
245
|
+
console.log(` ā³ Skills already installed`);
|
|
246
|
+
installedEditors.push({ name: editor.name, status: 'exists' });
|
|
247
|
+
} else {
|
|
248
|
+
if (simpleStatus === 'new') console.log(` ā³ Installed: ${simpleFile}`);
|
|
249
|
+
if (detailedStatus === 'new') console.log(` ā³ Installed: ${detailedFile}`);
|
|
250
|
+
installedEditors.push({ name: editor.name, status: 'new' });
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
} catch (error) {
|
|
254
|
+
failedEditors.push({ name: editor.name, error: error.message });
|
|
255
|
+
}
|
|
124
256
|
}
|
|
257
|
+
|
|
258
|
+
// Summary
|
|
259
|
+
console.log('\n' + 'ā'.repeat(60));
|
|
260
|
+
|
|
261
|
+
if (installedEditors.length > 0) {
|
|
262
|
+
console.log('\nā
Voicci 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:');
|
|
266
|
+
installedEditors.forEach(editor => {
|
|
267
|
+
const status = editor.status === 'new' ? '(new)' : '(already installed)';
|
|
268
|
+
console.log(` ⢠${editor.name} ${status}`);
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
console.log('\nš” Usage:');
|
|
272
|
+
console.log(' 1. Restart your AI code editor');
|
|
273
|
+
console.log(' 2. Use: /voicci "search query" (simple)');
|
|
274
|
+
console.log(' 3. Or: /voicci-audiobook "search query" (detailed docs)');
|
|
275
|
+
console.log(' 4. Or CLI: voicci "your search query"');
|
|
276
|
+
} else {
|
|
277
|
+
console.log('\nā ļø No supported AI code editors detected');
|
|
278
|
+
console.log('\nSupported editors: Claude Code, OpenCode, Cursor, Windsurf');
|
|
279
|
+
console.log('\nā You can still use the CLI: voicci <command>');
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
if (failedEditors.length > 0) {
|
|
283
|
+
console.log('\nā ļø Failed to install for:');
|
|
284
|
+
failedEditors.forEach(({ name, error }) => {
|
|
285
|
+
console.log(` ⢠${name}: ${error}`);
|
|
286
|
+
});
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
console.log('\n' + 'ā'.repeat(60));
|
|
290
|
+
console.log();
|
|
125
291
|
}
|
|
126
292
|
|
|
127
|
-
|
|
293
|
+
detectAndInstall();
|