voicci 1.0.6 ā 1.0.7
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 +164 -33
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "voicci",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.7",
|
|
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,20 @@ 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
|
|
|
12
|
+
// Use unique name to avoid conflicts with SSH shorthand skills
|
|
13
|
+
const SKILL_NAME = 'voicci-audiobook.md';
|
|
14
|
+
|
|
11
15
|
const SKILL_CONTENT = `---
|
|
12
|
-
description: "Voicci
|
|
16
|
+
description: "Voicci - AI audiobook generator CLI"
|
|
13
17
|
argument-hint: "COMMAND_OR_FILE"
|
|
14
18
|
---
|
|
15
19
|
|
|
16
|
-
# Voicci
|
|
20
|
+
# Voicci - AI Audiobook Generator
|
|
17
21
|
|
|
18
22
|
Convert books, PDFs, and documents to audiobooks using AI-powered text-to-speech (XTTS v2).
|
|
19
23
|
|
|
@@ -75,7 +79,7 @@ voicci memory
|
|
|
75
79
|
\`\`\`!
|
|
76
80
|
#!/bin/bash
|
|
77
81
|
# Execute voicci with user-provided arguments
|
|
78
|
-
voicci
|
|
82
|
+
voicci $ARGUMENTS
|
|
79
83
|
\`\`\`
|
|
80
84
|
|
|
81
85
|
## Important Notes
|
|
@@ -87,41 +91,168 @@ voicci \$ARGUMENTS
|
|
|
87
91
|
- Copyright warning will appear before first book search
|
|
88
92
|
|
|
89
93
|
## Usage Examples
|
|
90
|
-
- \`/voicci-
|
|
91
|
-
- \`/voicci-
|
|
92
|
-
- \`/voicci-
|
|
93
|
-
- \`/voicci-
|
|
94
|
-
- \`/voicci-
|
|
94
|
+
- \`/voicci-audiobook "Lord of the Rings"\` - Search and convert book
|
|
95
|
+
- \`/voicci-audiobook ~/Documents/book.pdf\` - Convert local file
|
|
96
|
+
- \`/voicci-audiobook -s\` - Check all job statuses
|
|
97
|
+
- \`/voicci-audiobook summary book.pdf\` - Generate summary only
|
|
98
|
+
- \`/voicci-audiobook --help\` - Show all options
|
|
95
99
|
`;
|
|
96
100
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
101
|
+
// Editor detection configurations
|
|
102
|
+
const EDITORS = {
|
|
103
|
+
'Claude Code': {
|
|
104
|
+
name: 'Claude Code',
|
|
105
|
+
detect: () => {
|
|
106
|
+
const homeDir = os.homedir();
|
|
107
|
+
// Check for claude command or .claude directory
|
|
108
|
+
if (fs.existsSync(path.join(homeDir, '.claude'))) return true;
|
|
109
|
+
try {
|
|
110
|
+
execFileSync('which', ['claude'], { stdio: 'ignore' });
|
|
111
|
+
return true;
|
|
112
|
+
} catch {
|
|
113
|
+
return false;
|
|
114
|
+
}
|
|
115
|
+
},
|
|
116
|
+
skillsDir: () => path.join(os.homedir(), '.claude', 'skills'),
|
|
117
|
+
skillName: SKILL_NAME
|
|
118
|
+
},
|
|
119
|
+
'OpenCode': {
|
|
120
|
+
name: 'OpenCode',
|
|
121
|
+
detect: () => {
|
|
122
|
+
const homeDir = os.homedir();
|
|
123
|
+
// Check for opencode command or config directory
|
|
124
|
+
if (fs.existsSync(path.join(homeDir, '.opencode'))) return true;
|
|
125
|
+
try {
|
|
126
|
+
execFileSync('which', ['opencode'], { stdio: 'ignore' });
|
|
127
|
+
return true;
|
|
128
|
+
} catch {
|
|
129
|
+
return false;
|
|
130
|
+
}
|
|
131
|
+
},
|
|
132
|
+
skillsDir: () => path.join(os.homedir(), '.opencode', 'skills'),
|
|
133
|
+
skillName: SKILL_NAME
|
|
134
|
+
},
|
|
135
|
+
'Cursor': {
|
|
136
|
+
name: 'Cursor',
|
|
137
|
+
detect: () => {
|
|
138
|
+
const homeDir = os.homedir();
|
|
139
|
+
// Check for cursor command or config directory
|
|
140
|
+
if (fs.existsSync(path.join(homeDir, '.cursor'))) return true;
|
|
141
|
+
if (fs.existsSync(path.join(homeDir, 'Library', 'Application Support', 'Cursor'))) return true;
|
|
142
|
+
try {
|
|
143
|
+
execFileSync('which', ['cursor'], { stdio: 'ignore' });
|
|
144
|
+
return true;
|
|
145
|
+
} catch {
|
|
146
|
+
return false;
|
|
147
|
+
}
|
|
148
|
+
},
|
|
149
|
+
skillsDir: () => {
|
|
150
|
+
const homeDir = os.homedir();
|
|
151
|
+
// Try common locations
|
|
152
|
+
const locations = [
|
|
153
|
+
path.join(homeDir, '.cursor', 'skills'),
|
|
154
|
+
path.join(homeDir, 'Library', 'Application Support', 'Cursor', 'skills')
|
|
155
|
+
];
|
|
156
|
+
for (const loc of locations) {
|
|
157
|
+
const parent = path.dirname(loc);
|
|
158
|
+
if (fs.existsSync(parent)) return loc;
|
|
159
|
+
}
|
|
160
|
+
return locations[0]; // Default to first
|
|
161
|
+
},
|
|
162
|
+
skillName: SKILL_NAME
|
|
163
|
+
},
|
|
164
|
+
'Windsurf': {
|
|
165
|
+
name: 'Windsurf',
|
|
166
|
+
detect: () => {
|
|
167
|
+
const homeDir = os.homedir();
|
|
168
|
+
// Check for windsurf command or config directory
|
|
169
|
+
if (fs.existsSync(path.join(homeDir, '.windsurf'))) return true;
|
|
170
|
+
try {
|
|
171
|
+
execFileSync('which', ['windsurf'], { stdio: 'ignore' });
|
|
172
|
+
return true;
|
|
173
|
+
} catch {
|
|
174
|
+
return false;
|
|
175
|
+
}
|
|
176
|
+
},
|
|
177
|
+
skillsDir: () => path.join(os.homedir(), '.windsurf', 'skills'),
|
|
178
|
+
skillName: SKILL_NAME
|
|
179
|
+
}
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
function detectAndInstall() {
|
|
183
|
+
const homeDir = os.homedir();
|
|
184
|
+
const installedEditors = [];
|
|
185
|
+
const failedEditors = [];
|
|
186
|
+
|
|
187
|
+
console.log('š Detecting AI code editors...\n');
|
|
188
|
+
|
|
189
|
+
// Detect and install for each editor
|
|
190
|
+
for (const [key, editor] of Object.entries(EDITORS)) {
|
|
191
|
+
try {
|
|
192
|
+
if (editor.detect()) {
|
|
193
|
+
console.log(`ā Found ${editor.name}`);
|
|
194
|
+
|
|
195
|
+
const skillsDir = editor.skillsDir();
|
|
196
|
+
const skillFile = path.join(skillsDir, editor.skillName);
|
|
197
|
+
|
|
198
|
+
// Create skills directory if it doesn't exist
|
|
199
|
+
if (!fs.existsSync(skillsDir)) {
|
|
200
|
+
fs.mkdirSync(skillsDir, { recursive: true });
|
|
201
|
+
}
|
|
102
202
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
203
|
+
// Check for conflicts with existing files
|
|
204
|
+
if (fs.existsSync(skillFile)) {
|
|
205
|
+
const existingContent = fs.readFileSync(skillFile, 'utf8');
|
|
206
|
+
// Only skip if it's already our skill
|
|
207
|
+
if (existingContent.includes('voicci $ARGUMENTS')) {
|
|
208
|
+
console.log(` ā³ Skill already installed at: ${skillFile}`);
|
|
209
|
+
installedEditors.push({ name: editor.name, path: skillFile, status: 'exists' });
|
|
210
|
+
continue;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// Write the skill file
|
|
215
|
+
fs.writeFileSync(skillFile, SKILL_CONTENT, 'utf8');
|
|
216
|
+
console.log(` ā³ Installed skill: ${skillFile}`);
|
|
217
|
+
installedEditors.push({ name: editor.name, path: skillFile, status: 'new' });
|
|
218
|
+
}
|
|
219
|
+
} catch (error) {
|
|
220
|
+
failedEditors.push({ name: editor.name, error: error.message });
|
|
106
221
|
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// Summary
|
|
225
|
+
console.log('\n' + 'ā'.repeat(60));
|
|
226
|
+
|
|
227
|
+
if (installedEditors.length > 0) {
|
|
228
|
+
console.log('\nā
Voicci CLI installed successfully!');
|
|
229
|
+
console.log('\nš¦ Command-line tool: voicci');
|
|
230
|
+
console.log('š§ Skill command: /voicci-audiobook');
|
|
231
|
+
console.log('\nš Installed in:');
|
|
232
|
+
installedEditors.forEach(editor => {
|
|
233
|
+
const status = editor.status === 'new' ? '(new)' : '(already installed)';
|
|
234
|
+
console.log(` ⢠${editor.name} ${status}`);
|
|
235
|
+
});
|
|
107
236
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
console.log('
|
|
112
|
-
|
|
113
|
-
console.log('
|
|
114
|
-
console.log('
|
|
115
|
-
console.log('');
|
|
116
|
-
console.log('To use in Claude Code:');
|
|
117
|
-
console.log(' 1. Restart your Claude Code session');
|
|
118
|
-
console.log(' 2. Use: /voicci-cli "search query" or /voicci-cli mybook.pdf');
|
|
119
|
-
console.log('');
|
|
120
|
-
console.log('Or use directly: voicci "your search query"');
|
|
121
|
-
} catch (error) {
|
|
122
|
-
console.error('ā ļø Failed to install Claude Code skill:', error.message);
|
|
123
|
-
console.log('You can still use: voicci <command>');
|
|
237
|
+
console.log('\nš” Usage:');
|
|
238
|
+
console.log(' 1. Restart your AI code editor');
|
|
239
|
+
console.log(' 2. Use: /voicci-audiobook "search query"');
|
|
240
|
+
console.log(' 3. Or use CLI directly: voicci "your search query"');
|
|
241
|
+
} else {
|
|
242
|
+
console.log('\nā ļø No supported AI code editors detected');
|
|
243
|
+
console.log('\nSupported editors: Claude Code, OpenCode, Cursor, Windsurf');
|
|
244
|
+
console.log('\nā You can still use the CLI: voicci <command>');
|
|
124
245
|
}
|
|
246
|
+
|
|
247
|
+
if (failedEditors.length > 0) {
|
|
248
|
+
console.log('\nā ļø Failed to install for:');
|
|
249
|
+
failedEditors.forEach(({ name, error }) => {
|
|
250
|
+
console.log(` ⢠${name}: ${error}`);
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
console.log('\n' + 'ā'.repeat(60));
|
|
255
|
+
console.log();
|
|
125
256
|
}
|
|
126
257
|
|
|
127
|
-
|
|
258
|
+
detectAndInstall();
|