voicecc 1.0.7 → 1.0.9

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
@@ -39,10 +39,3 @@ The voice loop runs locally with zero external API calls except to Claude:
39
39
  6. **Narration**: Claude's response stripped of markdown and split into sentences
40
40
  7. **Text-to-speech**: Kokoro-82M via mlx-audio on Apple Silicon GPU (~8x realtime)
41
41
  8. **Speaker playback**: Audio output through VPIO at 24kHz with echo cancellation
42
-
43
- ## Troubleshooting
44
-
45
- - **"sox not found"**: Install sox with `brew install sox`
46
- - **"espeak not installed"**: Install espeak-ng with `brew install espeak-ng`
47
- - **tts-server.py not ready**: Ensure the Python venv is set up correctly
48
- - **Mic permission denied**: Grant microphone permissions to your terminal or IDE
package/bin/voicecc.js CHANGED
@@ -3,31 +3,28 @@
3
3
  /**
4
4
  * CLI entry point for the voicecc command.
5
5
  *
6
- * Resolves the package install directory and spawns `tsx run.ts`
7
- * with inherited stdio so the dashboard server runs in the foreground.
8
- *
9
- * Responsibilities:
10
- * - Resolve the package root from this script's location
11
- * - Spawn tsx with run.ts in the correct working directory
12
- * - Forward signals (SIGINT, SIGTERM) so Ctrl+C stops the server cleanly
6
+ * Checks if first-run setup is needed (compile mic-vpio, Python venv, etc.)
7
+ * and runs it with visible output. Then spawns `tsx run.ts` for the dashboard.
13
8
  */
14
9
 
15
10
  import { spawn } from "node:child_process";
16
11
  import { dirname, join } from "node:path";
17
12
  import { fileURLToPath } from "node:url";
18
13
 
19
- // ============================================================================
20
- // CONSTANTS
21
- // ============================================================================
22
-
23
14
  const __dirname = dirname(fileURLToPath(import.meta.url));
24
15
  const PKG_ROOT = join(__dirname, "..");
25
16
  const TSX_BIN = join(PKG_ROOT, "node_modules", ".bin", "tsx");
26
17
 
27
- // ============================================================================
28
- // MAIN ENTRYPOINT
29
- // ============================================================================
18
+ // Run setup if needed (first run or incomplete install)
19
+ process.chdir(PKG_ROOT);
20
+ const { needsSetup, runSetup } = await import("../scripts/postinstall.js");
21
+
22
+ if (needsSetup()) {
23
+ console.log("[voicecc] Running first-time setup...\n");
24
+ runSetup();
25
+ }
30
26
 
27
+ // Start the dashboard
31
28
  const child = spawn(TSX_BIN, ["run.ts"], {
32
29
  cwd: PKG_ROOT,
33
30
  stdio: "inherit",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "voicecc",
3
- "version": "1.0.7",
3
+ "version": "1.0.9",
4
4
  "description": "Voice mode plugin for Claude Code -- hands-free interaction via local STT/TTS/VAD",
5
5
  "type": "module",
6
6
  "bin": {
@@ -10,8 +10,7 @@
10
10
  "start": "tsx run.ts",
11
11
  "dev:dashboard": "cd dashboard && npx vite",
12
12
  "build:dashboard": "cd dashboard && npx vite build",
13
- "prepublishOnly": "npm run build:dashboard",
14
- "postinstall": "node scripts/postinstall.js"
13
+ "prepublishOnly": "npm run build:dashboard"
15
14
  },
16
15
  "files": [
17
16
  "bin/",
@@ -1,16 +1,11 @@
1
1
  /**
2
- * Postinstall script that runs after `npm install`.
2
+ * Setup script for voicecc.
3
3
  *
4
4
  * Compiles the mic-vpio Swift binary (macOS VPIO echo cancellation),
5
5
  * checks for required system dependencies (espeak-ng), then sets up
6
6
  * the Python virtual environment and installs TTS dependencies.
7
7
  *
8
- * Responsibilities:
9
- * - Compile the mic-vpio Swift binary for echo-cancelled audio I/O
10
- * - Verify espeak-ng is installed
11
- * - Create Python venv in sidecar/.venv (if not already present)
12
- * - Install Python TTS packages (mlx-audio, misaki, etc.)
13
- * - Download spaCy English model
8
+ * Called from bin/voicecc.js on first run (or when setup is incomplete).
14
9
  */
15
10
 
16
11
  import { execSync } from "child_process";
@@ -24,6 +19,7 @@ import { join } from "path";
24
19
  const VENV_DIR = join("sidecar", ".venv");
25
20
  const PIP = join(VENV_DIR, "bin", "pip");
26
21
  const PYTHON = join(VENV_DIR, "bin", "python3");
22
+ const MIC_VPIO = join("sidecar", "mic-vpio");
27
23
 
28
24
  const PYTHON_PACKAGES = [
29
25
  "mlx-audio",
@@ -34,10 +30,24 @@ const PYTHON_PACKAGES = [
34
30
  ];
35
31
 
36
32
  // ============================================================================
37
- // MAIN ENTRYPOINT
33
+ // PUBLIC API
38
34
  // ============================================================================
39
35
 
40
- function main() {
36
+ /**
37
+ * Returns true if any setup step is incomplete.
38
+ */
39
+ export function needsSetup() {
40
+ return (
41
+ !existsSync(MIC_VPIO) ||
42
+ !existsSync(PYTHON) ||
43
+ !existsSync(join("dashboard", "dist", "index.html"))
44
+ );
45
+ }
46
+
47
+ /**
48
+ * Run all setup steps. Shows progress to stdout.
49
+ */
50
+ export function runSetup() {
41
51
  installClaudeMd();
42
52
  buildDashboard();
43
53
  compileMicVpio();
@@ -48,21 +58,20 @@ function main() {
48
58
 
49
59
  console.log("");
50
60
  console.log("========================================");
51
- console.log(" VOICECC INSTALLED ");
61
+ console.log(" VOICECC SETUP COMPLETE ");
52
62
  console.log("========================================");
53
63
  console.log("");
54
- console.log(" Run 'voicecc' in terminal to start the server!");
55
- console.log("");
56
64
  }
57
65
 
58
66
  // ============================================================================
59
67
  // HELPER FUNCTIONS
60
68
  // ============================================================================
61
69
 
62
- /**
63
- * Build the dashboard frontend via Vite.
64
- */
65
70
  function buildDashboard() {
71
+ if (existsSync(join("dashboard", "dist", "index.html"))) {
72
+ console.log("Dashboard already built, skipping.");
73
+ return;
74
+ }
66
75
  console.log("Building dashboard...");
67
76
  try {
68
77
  run("cd dashboard && npx vite build");
@@ -74,16 +83,11 @@ function buildDashboard() {
74
83
  console.log("Dashboard built successfully");
75
84
  }
76
85
 
77
- /**
78
- * Copy the project CLAUDE.md from init/ to the project root.
79
- * Overwrites any existing CLAUDE.md to keep it in sync with the repo.
80
- */
81
86
  function installClaudeMd() {
82
87
  const src = join("init", "CLAUDE.md");
83
88
  const dest = "CLAUDE.md";
84
89
 
85
90
  if (!existsSync(src)) {
86
- console.warn("init/CLAUDE.md not found, skipping.");
87
91
  return;
88
92
  }
89
93
 
@@ -91,13 +95,13 @@ function installClaudeMd() {
91
95
  console.log("Installed CLAUDE.md to project root.");
92
96
  }
93
97
 
94
- /**
95
- * Compile the mic-vpio Swift binary for macOS VPIO echo cancellation.
96
- * Skips compilation if the binary already exists and is newer than the source.
97
- */
98
98
  function compileMicVpio() {
99
+ if (existsSync(MIC_VPIO)) {
100
+ console.log("mic-vpio already compiled, skipping.");
101
+ return;
102
+ }
103
+
99
104
  const source = join("sidecar", "mic-vpio.swift");
100
- const binary = join("sidecar", "mic-vpio");
101
105
 
102
106
  if (process.platform !== "darwin") {
103
107
  console.error("\n[voicecc] ERROR: macOS is required.");
@@ -114,7 +118,7 @@ function compileMicVpio() {
114
118
 
115
119
  console.log("Compiling mic-vpio (VPIO echo cancellation)...");
116
120
  try {
117
- run(`swiftc -O -o ${binary} ${source} -framework AudioToolbox -framework CoreAudio`);
121
+ run(`swiftc -O -o ${MIC_VPIO} ${source} -framework AudioToolbox -framework CoreAudio`);
118
122
  } catch (err) {
119
123
  console.error("\n[voicecc] ERROR: Failed to compile mic-vpio.swift.");
120
124
  console.error(" Make sure Xcode Command Line Tools are installed: xcode-select --install\n");
@@ -123,10 +127,6 @@ function compileMicVpio() {
123
127
  console.log("mic-vpio compiled successfully");
124
128
  }
125
129
 
126
- /**
127
- * Check that required system binaries are available.
128
- * Exits with a clear error message if any are missing.
129
- */
130
130
  function checkSystemDeps() {
131
131
  const missing = [];
132
132
 
@@ -135,16 +135,13 @@ function checkSystemDeps() {
135
135
  if (missing.length > 0) {
136
136
  console.error(`\n[voicecc] ERROR: Missing system dependencies: ${missing.join(", ")}`);
137
137
  console.error(` Install with: brew install ${missing.join(" ")}`);
138
- console.error(` Then re-run: npm install\n`);
138
+ console.error(` Then re-run: voicecc\n`);
139
139
  process.exit(1);
140
140
  }
141
141
 
142
142
  console.log("System dependencies OK (espeak-ng)");
143
143
  }
144
144
 
145
- /**
146
- * Create the Python virtual environment if it doesn't exist.
147
- */
148
145
  function setupPythonVenv() {
149
146
  if (existsSync(PIP)) {
150
147
  console.log(`Python venv already exists at ${VENV_DIR}`);
@@ -167,9 +164,6 @@ function setupPythonVenv() {
167
164
  }
168
165
  }
169
166
 
170
- /**
171
- * Install Python TTS packages into the venv.
172
- */
173
167
  function installPythonPackages() {
174
168
  console.log("Installing Python TTS packages...");
175
169
  try {
@@ -178,14 +172,11 @@ function installPythonPackages() {
178
172
  console.error("\n[voicecc] ERROR: Failed to install Python TTS packages.");
179
173
  console.error(" This may be due to missing build tools or incompatible Python version.");
180
174
  console.error(" Required packages: " + PYTHON_PACKAGES.join(", "));
181
- console.error(" Try deleting sidecar/.venv and re-running: npm install\n");
175
+ console.error(" Try deleting sidecar/.venv and re-running: voicecc\n");
182
176
  process.exit(1);
183
177
  }
184
178
  }
185
179
 
186
- /**
187
- * Download the spaCy English language model.
188
- */
189
180
  function downloadSpacyModel() {
190
181
  console.log("Downloading spaCy English model...");
191
182
  try {
@@ -197,12 +188,6 @@ function downloadSpacyModel() {
197
188
  }
198
189
  }
199
190
 
200
- /**
201
- * Check if a command exists on the system PATH.
202
- *
203
- * @param {string} cmd - Command name to check
204
- * @returns {boolean} True if the command exists
205
- */
206
191
  function commandExists(cmd) {
207
192
  try {
208
193
  execSync(`which ${cmd}`, { stdio: "ignore" });
@@ -212,17 +197,6 @@ function commandExists(cmd) {
212
197
  }
213
198
  }
214
199
 
215
- /**
216
- * Run a shell command with inherited stdio (output visible to user).
217
- *
218
- * @param {string} cmd - Shell command to execute
219
- */
220
200
  function run(cmd) {
221
201
  execSync(cmd, { stdio: "inherit" });
222
202
  }
223
-
224
- // ============================================================================
225
- // ENTRY POINT
226
- // ============================================================================
227
-
228
- main();