@robbiesrobotics/alice-agents 1.3.3 → 1.4.1

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/lib/installer.mjs CHANGED
@@ -2,6 +2,7 @@ import { readFileSync, existsSync } from 'node:fs';
2
2
  import { join, dirname } from 'node:path';
3
3
  import { fileURLToPath } from 'node:url';
4
4
  import { execSync } from 'node:child_process';
5
+ import { homedir } from 'node:os';
5
6
  import { configExists, mergeConfig, removeAliceAgents, detectAvailableModels } from './config-merger.mjs';
6
7
  import { scaffoldAll } from './workspace-scaffolder.mjs';
7
8
  import { readManifest, writeManifest } from './manifest.mjs';
@@ -19,16 +20,25 @@ import {
19
20
  detectUserName,
20
21
  detectTimezone,
21
22
  } from './prompter.mjs';
23
+ import { c, bold, dim, green, greenBold, red, yellow, cyan, gray,
24
+ icons, separator, printSection, printSeparator, printBox,
25
+ printStepDone, printStepFail, printStepSkip } from './colors.mjs';
26
+ import { runSkillsWizardStep } from './skills.mjs';
22
27
 
23
- function isOpenClawInstalled() {
28
+ function commandExists(cmd) {
29
+ const probe = process.platform === 'win32' ? 'where' : 'which';
24
30
  try {
25
- execSync('which openclaw', { stdio: 'pipe' });
31
+ execSync(`${probe} ${cmd}`, { stdio: 'pipe' });
26
32
  return true;
27
33
  } catch {
28
34
  return false;
29
35
  }
30
36
  }
31
37
 
38
+ function isOpenClawInstalled() {
39
+ return commandExists('openclaw');
40
+ }
41
+
32
42
  /**
33
43
  * On Linux, Docker requires the user to be in the docker group.
34
44
  * Detect this early and warn before OpenClaw's own preflight fails cryptically.
@@ -36,9 +46,7 @@ function isOpenClawInstalled() {
36
46
  function checkLinuxDockerPermissions() {
37
47
  if (process.platform !== 'linux') return;
38
48
 
39
- try {
40
- execSync('which docker', { stdio: 'pipe' });
41
- } catch {
49
+ if (!commandExists('docker')) {
42
50
  return; // Docker not installed — not our problem
43
51
  }
44
52
 
@@ -54,7 +62,7 @@ function checkLinuxDockerPermissions() {
54
62
  msg.includes('connect: permission denied');
55
63
 
56
64
  if (isPermissionIssue) {
57
- console.log(' ⚠️ Docker permission issue detected.\n');
65
+ console.log(` ${icons.warn} ${yellow('Docker permission issue detected.')}\n`);
58
66
  console.log(' Your user is not in the docker group. This will cause');
59
67
  console.log(' OpenClaw to fail when it tries to access Docker.\n');
60
68
  console.log(' Fix this now (recommended):');
@@ -75,7 +83,7 @@ async function detectRuntime() {
75
83
  } catch {}
76
84
 
77
85
  // Check for NemoClaw directory
78
- const nemoclawDir = join(process.env.HOME || '', '.nemoclaw');
86
+ const nemoclawDir = join(homedir(), '.nemoclaw');
79
87
  if (existsSync(nemoclawDir)) return 'nemoclaw';
80
88
 
81
89
  // Fall back to OpenClaw
@@ -105,16 +113,17 @@ async function checkForOpenClawUpdate(auto) {
105
113
  const current = getOpenClawVersion();
106
114
  if (!current) return;
107
115
 
108
- console.log(` OpenClaw version: ${current}`);
116
+ console.log(` ${dim('OpenClaw version:')} ${green(current)}`);
109
117
 
110
118
  const latest = getLatestNpmVersion();
111
119
  if (!latest) {
112
- console.log(' ⚠️ Could not check for updates (npm registry unreachable)\n');
120
+ console.log(` ${icons.warn} ${yellow('Could not check for updates (npm registry unreachable)')}\n`);
113
121
  return;
114
122
  }
115
123
 
116
124
  if (current === latest) {
117
- console.log(` ✓ OpenClaw is up to date (${latest})\n`);
125
+ printStepDone('OpenClaw is up to date', latest);
126
+ console.log('');
118
127
  return;
119
128
  }
120
129
 
@@ -126,13 +135,15 @@ async function checkForOpenClawUpdate(auto) {
126
135
  }
127
136
 
128
137
  if (shouldUpdate) {
129
- console.log(' 📦 Updating OpenClaw...\n');
138
+ console.log(` ${icons.pkg} ${bold('Updating OpenClaw...')}\n`);
130
139
  try {
131
140
  execSync('npm install -g openclaw@latest', { stdio: 'inherit' });
132
141
  const updated = getOpenClawVersion();
133
- console.log(`\n ✓ OpenClaw updated to ${updated || latest}\n`);
142
+ console.log('');
143
+ printStepDone('OpenClaw updated', updated || latest);
144
+ console.log('');
134
145
  } catch {
135
- console.log('\n ⚠️ Update failed — continuing with current version\n');
146
+ console.log(`\n ${icons.warn} ${yellow('Update failed — continuing with current version')}\n`);
136
147
  }
137
148
  } else {
138
149
  console.log();
@@ -140,51 +151,267 @@ async function checkForOpenClawUpdate(auto) {
140
151
  }
141
152
 
142
153
  async function installRuntime(auto) {
143
- console.log(' ⚠️ No agent runtime detected.\n');
144
- console.log(' A.L.I.C.E. requires a compatible runtime. We recommend NemoClaw —');
145
- console.log(" NVIDIA's secure, open-source distribution that includes OpenClaw");
146
- console.log(' plus enterprise-grade security (OpenShell sandbox, local AI models).\n');
154
+ const plat = process.platform;
155
+ const isWSL = !!process.env.WSL_DISTRO_NAME || !!process.env.WSLENV;
156
+
157
+ console.log(` ${icons.warn} ${yellow('No agent runtime detected.')}\n`);
158
+
159
+ // Windows (native, not WSL)
160
+ if (plat === "win32") {
161
+ console.log(" A.L.I.C.E. requires OpenClaw or NemoClaw to run.\n");
162
+ console.log(" NemoClaw (the recommended runtime with enterprise sandbox) requires");
163
+ console.log(" Linux — but you can get it on Windows via WSL2 (Windows Subsystem for Linux).\n");
164
+
165
+ const wslAvailable = commandExists("wsl");
166
+
167
+ if (wslAvailable) {
168
+ let wslDistros = "";
169
+ try {
170
+ // WSL --list output on Windows can be UTF-16LE encoded — decode from Buffer
171
+ const raw = execSync("wsl --list --quiet", { stdio: "pipe" });
172
+ wslDistros = raw.toString("utf16le").replace(/\0/g, "").trim();
173
+ } catch {}
174
+
175
+ const hasDistro = wslDistros.length > 0 &&
176
+ !wslDistros.toLowerCase().includes("no installed") &&
177
+ !wslDistros.toLowerCase().includes("has no");
178
+
179
+ if (hasDistro) {
180
+ console.log(" ✔ WSL2 detected with an installed distro.\n");
181
+ console.log(" Recommended: Install NemoClaw inside WSL2 for full sandbox support.\n");
182
+
183
+ let choice;
184
+ if (auto) {
185
+ choice = "wsl-nemoclaw";
186
+ } else {
187
+ choice = await choose(" How would you like to proceed?", [
188
+ { label: "Install NemoClaw inside WSL2 (Recommended)", value: "wsl-nemoclaw" },
189
+ { label: "Install OpenClaw for Windows (no sandbox)", value: "openclaw-win" },
190
+ ]);
191
+ }
192
+
193
+ if (choice === "wsl-nemoclaw") {
194
+ console.log("\n 📦 Installing NemoClaw inside WSL2...\n");
195
+ console.log(" This will run the NemoClaw installer in your default WSL2 distro.\n");
196
+ try {
197
+ execSync(
198
+ "wsl bash -c \"curl -fsSL https://nvidia.com/nemoclaw.sh | bash\"",
199
+ { stdio: "inherit" }
200
+ );
201
+ console.log("\n ✔ NemoClaw installed in WSL2.\n");
202
+ console.log(" ℹ️ Note: On Ubuntu 24.04 / WSL2, Docker may need a cgroup fix.");
203
+ console.log(" If onboard fails, run inside WSL2: nemoclaw setup-spark\n");
204
+ } catch {
205
+ console.error("\n ❌ WSL2 NemoClaw install failed. Try manually inside WSL2:");
206
+ console.error(" wsl bash -c \"curl -fsSL https://nvidia.com/nemoclaw.sh | bash\"\n");
207
+ process.exit(1);
208
+ }
209
+ } else {
210
+ console.log("\n 📦 Installing OpenClaw for Windows...\n");
211
+ try {
212
+ execSync("npm install -g openclaw", { stdio: "inherit" });
213
+ console.log("\n ✔ OpenClaw installed.\n");
214
+ console.log(" 💡 Tip: Set up WSL2 + NemoClaw later for sandbox mode.\n");
215
+ } catch {
216
+ console.error("\n ❌ Failed to install OpenClaw:");
217
+ console.error(" npm install -g openclaw\n");
218
+ process.exit(1);
219
+ }
220
+ }
221
+ } else {
222
+ console.log(" ℹ️ WSL is installed but no Linux distro is set up yet.\n");
223
+
224
+ let shouldSetup;
225
+ if (auto) {
226
+ shouldSetup = true;
227
+ } else {
228
+ shouldSetup = await confirm(
229
+ " Set up Ubuntu in WSL2 now and install NemoClaw? (Recommended)"
230
+ );
231
+ }
232
+
233
+ if (shouldSetup) {
234
+ console.log("\n 📦 Setting up Ubuntu in WSL2...\n");
235
+ console.log(" This will install Ubuntu and then install NemoClaw inside it.");
236
+ console.log(" You may be prompted to create a Linux username and password.\n");
237
+ try {
238
+ // Modern syntax (Win11/Win10 modern): wsl --install Ubuntu
239
+ // On older Win10 builds, -d flag is required: wsl --install --distribution Ubuntu
240
+ execSync("wsl --install Ubuntu", { stdio: "inherit" });
241
+ execSync("wsl --set-default-version 2", { stdio: "inherit" });
242
+ console.log("\n ✔ Ubuntu installed in WSL2.\n");
243
+ console.log(" 📦 Installing NemoClaw inside WSL2...\n");
244
+ execSync(
245
+ "wsl bash -c \"curl -fsSL https://nvidia.com/nemoclaw.sh | bash\"",
246
+ { stdio: "inherit" }
247
+ );
248
+ console.log("\n ✔ NemoClaw installed in WSL2.\n");
249
+ console.log(" ℹ️ If onboard fails on Ubuntu 24.04: run inside WSL2: nemoclaw setup-spark\n");
250
+ } catch {
251
+ console.error("\n ❌ WSL2 setup failed. Try manually:");
252
+ console.error(" 1. wsl --install Ubuntu (or: wsl --install --distribution Ubuntu on older Win10)");
253
+ console.error(" 2. wsl bash -c \"curl -fsSL https://nvidia.com/nemoclaw.sh | bash\"\n");
254
+ console.error(" Falling back to OpenClaw for Windows...\n");
255
+ try {
256
+ execSync("npm install -g openclaw", { stdio: "inherit" });
257
+ console.log(" ✔ OpenClaw installed as fallback.\n");
258
+ } catch {
259
+ process.exit(1);
260
+ }
261
+ }
262
+ } else {
263
+ console.log("\n 📦 Installing OpenClaw for Windows...\n");
264
+ try {
265
+ execSync("npm install -g openclaw", { stdio: "inherit" });
266
+ console.log("\n ✔ OpenClaw installed.\n");
267
+ console.log(" 💡 Tip: Run \"wsl --install Ubuntu\" later, then re-run this installer");
268
+ console.log(" to get NemoClaw with full sandbox support.\n");
269
+ } catch {
270
+ console.error("\n ❌ Failed to install OpenClaw:");
271
+ console.error(" npm install -g openclaw\n");
272
+ process.exit(1);
273
+ }
274
+ }
275
+ }
276
+ } else {
277
+ console.log(" WSL2 is not installed on this machine.\n");
278
+
279
+ let shouldInstallWSL;
280
+ if (auto) {
281
+ shouldInstallWSL = false;
282
+ } else {
283
+ shouldInstallWSL = await confirm(
284
+ " Install WSL2 + Ubuntu now for full NemoClaw support? (Recommended)"
285
+ );
286
+ }
287
+
288
+ if (shouldInstallWSL) {
289
+ console.log("\n 📦 Installing WSL2 and Ubuntu...\n");
290
+ console.log(" ⚠️ This requires a system restart after WSL2 installs.");
291
+ console.log(" After restarting, re-run: npx @robbiesrobotics/alice-agents\n");
292
+ try {
293
+ execSync("wsl --install", { stdio: "inherit" });
294
+ try { execSync("wsl --set-default-version 2", { stdio: "pipe" }); } catch {}
295
+ console.log("\n ✔ WSL2 installation initiated.");
296
+ console.log(" 🔁 Please restart your computer, then re-run the installer.\n");
297
+ process.exit(0);
298
+ } catch {
299
+ console.error("\n ❌ WSL2 install failed. Enable it manually:");
300
+ console.error(" 1. Open PowerShell as Administrator");
301
+ console.error(" 2. Run: wsl --install");
302
+ console.error(" 3. Restart your computer");
303
+ console.error(" 4. Re-run: npx @robbiesrobotics/alice-agents\n");
304
+ console.error(" Falling back to OpenClaw for Windows...\n");
305
+ try {
306
+ execSync("npm install -g openclaw", { stdio: "inherit" });
307
+ console.log(" ✔ OpenClaw installed as fallback.\n");
308
+ } catch {
309
+ process.exit(1);
310
+ }
311
+ }
312
+ } else {
313
+ console.log("\n 📦 Installing OpenClaw for Windows...\n");
314
+ console.log(" A.L.I.C.E. will run without the NemoClaw sandbox.\n");
315
+ try {
316
+ execSync("npm install -g openclaw", { stdio: "inherit" });
317
+ console.log("\n ✔ OpenClaw installed.\n");
318
+ console.log(" 💡 To enable NemoClaw later:");
319
+ console.log(" 1. Open PowerShell as Administrator: wsl --install");
320
+ console.log(" 2. Restart, then re-run: npx @robbiesrobotics/alice-agents\n");
321
+ } catch {
322
+ console.error("\n ❌ Failed to install OpenClaw:");
323
+ console.error(" npm install -g openclaw\n");
324
+ process.exit(1);
325
+ }
326
+ }
327
+ }
328
+
329
+ try {
330
+ execSync("openclaw configure", { stdio: "inherit" });
331
+ } catch {
332
+ console.error("\n ⚠️ Configuration incomplete. Run manually: openclaw configure\n");
333
+ process.exit(1);
334
+ }
335
+ return;
336
+ }
337
+
338
+ // macOS
339
+ if (plat === "darwin") {
340
+ console.log(" A.L.I.C.E. requires a compatible runtime. NemoClaw requires Linux");
341
+ console.log(" (OpenShell uses Linux kernel primitives: Landlock, seccomp, netns).\n");
342
+ console.log(" On macOS, OpenClaw will be installed. For NemoClaw sandbox mode,");
343
+ console.log(" use a Linux VM or deploy remotely with: nemoclaw deploy <brev-instance>\n");
344
+
345
+ console.log(" 📦 Installing OpenClaw...\n");
346
+ try {
347
+ execSync("npm install -g openclaw", { stdio: "inherit" });
348
+ console.log("\n ✔ OpenClaw installed.\n");
349
+ } catch {
350
+ console.error("\n ❌ Failed to install OpenClaw:");
351
+ console.error(" npm install -g openclaw\n");
352
+ process.exit(1);
353
+ }
354
+
355
+ try {
356
+ execSync("openclaw configure", { stdio: "inherit" });
357
+ console.log("\n ✓ OpenClaw configured\n");
358
+ } catch {
359
+ console.error("\n ⚠️ Configuration incomplete. Run manually: openclaw configure\n");
360
+ process.exit(1);
361
+ }
362
+ return;
363
+ }
364
+
365
+ // Linux / WSL2
366
+ console.log(" A.L.I.C.E. requires a compatible runtime. We recommend NemoClaw —");
367
+ console.log(" NVIDIA\u2019s secure, open-source distribution that includes OpenClaw");
368
+ console.log(" plus enterprise-grade security (OpenShell sandbox, local AI models).\n");
369
+
370
+ if (isWSL) {
371
+ console.log(" ℹ️ WSL2 environment detected.");
372
+ console.log(" If NemoClaw onboard fails, run: nemoclaw setup-spark");
373
+ console.log(" (applies the required cgroup v2 Docker fix for WSL2)\n");
374
+ }
147
375
 
148
376
  let choice;
149
377
  if (auto) {
150
- choice = 'nemoclaw';
378
+ choice = "nemoclaw";
151
379
  } else {
152
- choice = await choose(' Which would you like to install?', [
153
- { label: 'NemoClaw (Recommended)', value: 'nemoclaw' },
154
- { label: 'OpenClaw only', value: 'openclaw' },
380
+ choice = await choose(" Which would you like to install?", [
381
+ { label: "NemoClaw (Recommended)", value: "nemoclaw" },
382
+ { label: "OpenClaw only", value: "openclaw" },
155
383
  ]);
156
384
  }
157
385
 
158
- if (choice === 'nemoclaw') {
159
- console.log(' 📦 Installing NemoClaw...\n');
386
+ if (choice === "nemoclaw") {
387
+ console.log(" 📦 Installing NemoClaw...\n");
160
388
  try {
161
- execSync('curl -fsSL https://nvidia.com/nemoclaw.sh | bash', { stdio: 'inherit' });
162
- console.log('\n ✔ NemoClaw installed — agents will run in OpenShell sandbox\n');
163
- } catch (err) {
164
- console.error('\n ❌ Failed to install NemoClaw. Try manually:');
165
- console.error(' curl -fsSL https://nvidia.com/nemoclaw.sh | bash\n');
389
+ execSync("curl -fsSL https://nvidia.com/nemoclaw.sh | bash", { stdio: "inherit" });
390
+ console.log("\n ✔ NemoClaw installed — agents will run in OpenShell sandbox\n");
391
+ } catch {
392
+ console.error("\n ❌ Failed to install NemoClaw. Try manually:");
393
+ console.error(" curl -fsSL https://nvidia.com/nemoclaw.sh | bash\n");
166
394
  process.exit(1);
167
395
  }
168
396
  } else {
169
- console.log(' 📦 Installing OpenClaw...\n');
397
+ console.log(" 📦 Installing OpenClaw...\n");
170
398
  try {
171
- execSync('npm install -g openclaw', { stdio: 'inherit' });
172
- console.log('\n ✔ OpenClaw installed\n');
173
- } catch (err) {
174
- console.error('\n ❌ Failed to install OpenClaw. Try manually:');
175
- console.error(' npm install -g openclaw\n');
399
+ execSync("npm install -g openclaw", { stdio: "inherit" });
400
+ console.log("\n ✔ OpenClaw installed\n");
401
+ } catch {
402
+ console.error("\n ❌ Failed to install OpenClaw:");
403
+ console.error(" npm install -g openclaw\n");
176
404
  process.exit(1);
177
405
  }
178
406
  }
179
407
 
180
- // Run openclaw configure
181
408
  try {
182
- execSync('openclaw configure', { stdio: 'inherit' });
183
- console.log('\n ✓ OpenClaw configured\n');
409
+ execSync("openclaw configure", { stdio: "inherit" });
410
+ console.log("\n ✓ OpenClaw configured\n");
184
411
  } catch {
185
- console.error('\n ⚠️ Configuration incomplete. Run manually:');
186
- console.error(' openclaw configure');
187
- console.error(` Then: npx @robbiesrobotics/alice-agents\n`);
412
+ console.error("\n ⚠️ Configuration incomplete. Run manually:");
413
+ console.error(" openclaw configure");
414
+ console.error(" Then: npx @robbiesrobotics/alice-agents\n");
188
415
  process.exit(1);
189
416
  }
190
417
  }
@@ -197,39 +424,43 @@ function loadAgentRegistry() {
197
424
  }
198
425
 
199
426
  function printBanner() {
200
- console.log();
201
- console.log(' ╔═══════════════════════════════════════════════════╗');
202
- console.log(' ║ ║');
203
- console.log(' ║ 🧠 A.L.I.C.E. Agent Framework Installer ║');
204
- console.log(' ║ ║');
205
- console.log(' ║ Adaptive Learning & Intelligent Coordination ║');
206
- console.log(' ║ Engine 10 starter agents, one conversation. ║');
207
- console.log(' ║ ║');
208
- console.log(' ╚═══════════════════════════════════════════════════╝');
209
- console.log();
427
+ const pkg = JSON.parse(readFileSync(new URL('../package.json', import.meta.url)));
428
+ const version = pkg.version || '';
429
+ console.log('');
430
+ console.log(` ${dim('╭──────────────────────────────────────────────╮')}`);
431
+ console.log(` ${dim('│')} ${dim('│')}`);
432
+ console.log(` ${dim('│')} ${greenBold('A . L . I . C . E .')} ${dim('│')}`);
433
+ console.log(` ${dim('│')} ${dim('Adaptive Learning & Intelligent Coordination')} ${dim('│')}`);
434
+ console.log(` ${dim('│')} ${dim('Engine — Multi-Agent Orchestration')} ${dim('│')}`);
435
+ console.log(` ${dim('│')} ${dim('│')}`);
436
+ console.log(` ${dim('│')} ${dim('v' + version)} ${green('●')} ${dim('10 starter agents, one team')} ${dim('│')}`);
437
+ console.log(` ${dim('│')} ${dim('│')}`);
438
+ console.log(` ${dim('╰──────────────────────────────────────────────╯')}`);
439
+ console.log('');
210
440
  }
211
441
 
212
442
  function printSummary(mode, tier, agents, preset, userInfo, detectedModels) {
213
443
  const modelLabel =
214
444
  preset === 'detected'
215
- ? `${detectedModels?.primary || 'your configured model'} (detected)`
216
- : preset === 'custom'
217
- ? `custom`
218
- : preset;
219
-
220
- console.log('\n ── Install Summary ──────────────────────────────');
221
- console.log(` Mode: ${mode}`);
222
- console.log(` Tier: ${tier} (${agents.length} agents)`);
223
- console.log(` Model: ${modelLabel}`);
224
- console.log(` User: ${userInfo.name}`);
225
- console.log(` Timezone: ${userInfo.timezone}`);
226
- console.log(' ─────────────────────────────────────────────────');
227
- console.log();
228
- console.log(' Agents:');
229
- for (const a of agents) {
230
- console.log(` ${a.emoji} ${a.name.padEnd(10)} — ${a.domain}`);
231
- }
232
- console.log();
445
+ ? `${detectedModels?.primary || 'your configured model'} ${dim('(detected)')}`
446
+ : preset === 'custom' ? 'custom' : preset;
447
+
448
+ printSection('Install Summary');
449
+ console.log('');
450
+
451
+ const lines = [
452
+ `${dim('Mode:')} ${green(mode)}`,
453
+ `${dim('Tier:')} ${green(tier)} ${dim('(' + agents.length + ' agents)')}`,
454
+ `${dim('Model:')} ${green(modelLabel)}`,
455
+ `${dim('User:')} ${green(userInfo.name)}`,
456
+ `${dim('Timezone:')} ${green(userInfo.timezone)}`,
457
+ '',
458
+ `${dim('Agents:')}`,
459
+ ...agents.map(a => ` ${icons.bullet} ${green(a.emoji)} ${bold(a.name.padEnd(10))} ${dim('─')} ${a.domain}`),
460
+ ];
461
+
462
+ printBox(lines, { title: 'Ready to install', padding: 2 });
463
+ console.log('');
233
464
  }
234
465
 
235
466
  export async function runInstall(options = {}) {
@@ -237,7 +468,7 @@ export async function runInstall(options = {}) {
237
468
 
238
469
  // Check health flag first (before banner)
239
470
  if (process.argv.includes('--health')) {
240
- const healthPath = join(process.env.HOME, '.openclaw', '.alice-health-alert.json');
471
+ const healthPath = join(homedir(), '.openclaw', '.alice-health-alert.json');
241
472
  if (existsSync(healthPath)) {
242
473
  const alerts = JSON.parse(readFileSync(healthPath, 'utf8'));
243
474
  console.log('\n⚠️ A.L.I.C.E. Health Report\n');
@@ -263,7 +494,8 @@ export async function runInstall(options = {}) {
263
494
 
264
495
  // Final check
265
496
  if (!configExists()) {
266
- console.error('OpenClaw config not found. Run: openclaw configure\n');
497
+ printStepFail('OpenClaw config not found', 'Run: openclaw configure');
498
+ console.log('');
267
499
  process.exit(1);
268
500
  }
269
501
 
@@ -272,23 +504,27 @@ export async function runInstall(options = {}) {
272
504
 
273
505
  const runtime = await detectRuntime();
274
506
  if (runtime === 'nemoclaw') {
275
- console.log('NemoClaw detected agents will run in OpenShell sandbox (secure mode)\n');
507
+ console.log(` ${icons.ok} ${greenBold('NemoClaw detected')} ${dim('─')} agents run in OpenShell sandbox\n`);
276
508
  } else {
277
- console.log('OpenClaw detected\n');
278
- console.log(' 💡 Tip: Upgrade to NemoClaw for enterprise security: https://nvidia.com/nemoclaw\n');
509
+ console.log(` ${icons.ok} ${green('OpenClaw detected')}\n`);
510
+ if (process.platform === "linux" || process.env.WSL_DISTRO_NAME || process.env.WSLENV) {
511
+ console.log(` ${icons.info} ${dim('Tip:')} Upgrade to NemoClaw for enterprise security: https://nvidia.com/nemoclaw\n`);
512
+ } else if (process.platform === "darwin") {
513
+ console.log(` ${icons.info} ${dim('Tip:')} NemoClaw requires Linux. For sandbox mode, use a Linux VM or Brev remote deploy.\n`);
514
+ }
279
515
  }
280
516
 
281
517
  // Detect what models the user already has configured
282
518
  const detectedModels = detectAvailableModels();
283
519
  if (detectedModels?.hasModel) {
284
- console.log(` Detected configured model: ${detectedModels.primary}`);
520
+ console.log(` ${icons.ok} ${green('Detected configured model:')} ${detectedModels.primary}`);
285
521
  if (detectedModels.providers.length > 0) {
286
522
  console.log(` Providers: ${detectedModels.providers.join(', ')}\n`);
287
523
  } else {
288
524
  console.log();
289
525
  }
290
526
  } else {
291
- console.log('No model configured yet — you\'ll be prompted to choose one.\n');
527
+ console.log(` ${icons.info} ${dim('No model configured yet — you\'ll be prompted to choose one.')}\n`);
292
528
  }
293
529
 
294
530
  const allAgents = loadAgentRegistry();
@@ -307,7 +543,7 @@ export async function runInstall(options = {}) {
307
543
  if (mode === 'upgrade') {
308
544
  const manifest = readManifest();
309
545
  if (!manifest) {
310
- console.log('No previous install found. Switching to fresh install.\n');
546
+ console.log(` ${icons.warn} ${yellow('No previous install found. Switching to fresh install.')}\n`);
311
547
  mode = 'fresh';
312
548
  }
313
549
  }
@@ -326,7 +562,8 @@ export async function runInstall(options = {}) {
326
562
  if (auto) {
327
563
  userInfo = { name: detectUserName(), timezone: detectTimezone(), notes: '' };
328
564
  } else {
329
- console.log('\n ── About You ───────────────────────────────────\n');
565
+ printSection('About You');
566
+ console.log('');
330
567
  userInfo = await promptUserInfo();
331
568
  }
332
569
 
@@ -358,14 +595,14 @@ export async function runInstall(options = {}) {
358
595
  const existing = await checkProLicense();
359
596
 
360
597
  if (existing.licensed) {
361
- console.log(`Pro license found (${existing.key.slice(0, 12)}...)`);
598
+ printStepDone(`Pro license found (${existing.key.slice(0, 12)}...)`);
362
599
  } else if (auto) {
363
600
  // --yes flag: skip interactive prompt, fallback to Starter if no stored license
364
601
  console.log('');
365
- console.log(' ℹ️ Pro tier requires a license key.');
366
- console.log(' Run without --yes to enter your license key.');
367
- console.log(' Falling back to Starter tier.');
368
- console.log(' Purchase a license at: https://getalice.av3.ai/pricing');
602
+ console.log(` ${icons.info} ${dim('Pro tier requires a license key.')}`);
603
+ console.log(` ${dim('Run without --yes to enter your license key.')}`);
604
+ console.log(` ${dim('Falling back to Starter tier.')}`);
605
+ console.log(` ${dim('Purchase a license at:')} ${cyan('https://getalice.av3.ai/pricing')}`);
369
606
  tier = 'starter';
370
607
  } else {
371
608
  // No stored license — prompt for key
@@ -376,7 +613,7 @@ export async function runInstall(options = {}) {
376
613
  key = await promptLicenseKey();
377
614
 
378
615
  if (!isValidFormat(key)) {
379
- console.log('Invalid format. Key must be ALICE-XXXX-XXXX-XXXX');
616
+ printStepFail('Invalid format. Key must be ALICE-XXXX-XXXX-XXXX');
380
617
  attempts++;
381
618
  continue;
382
619
  }
@@ -387,13 +624,13 @@ export async function runInstall(options = {}) {
387
624
  if (result.valid) {
388
625
  storeLicense(key);
389
626
  if (result.message === 'offline') {
390
- console.log('Key stored (offline — will validate on next run)');
627
+ printStepDone('Key stored', 'offline — will validate on next run');
391
628
  } else {
392
- console.log('License verified! Welcome to A.L.I.C.E. Pro.');
629
+ printStepDone('License verified! Welcome to A.L.I.C.E. Pro.');
393
630
  }
394
631
  break;
395
632
  } else {
396
- console.log(`Invalid key: ${result.message ?? 'Not recognized'}`);
633
+ printStepFail(`Invalid key: ${result.message ?? 'Not recognized'}`);
397
634
  attempts++;
398
635
 
399
636
  if (attempts >= 3) {
@@ -424,16 +661,20 @@ export async function runInstall(options = {}) {
424
661
  closePrompt();
425
662
 
426
663
  // Execute
427
- console.log('\n Installing...\n');
664
+ printSection('Installing');
665
+ console.log('');
428
666
 
429
667
  // Merge config
430
- const { backupPath, agentCount } = mergeConfig({
668
+ const { backupPath, agentCount, effectivePreset, warning } = mergeConfig({
431
669
  agents,
432
670
  mode,
433
671
  preset,
434
672
  customModels,
435
673
  });
436
- console.log(` ✓ Config updated (backup: ${backupPath})`);
674
+ printStepDone('Config updated', `backup: ${backupPath}`);
675
+ if (warning) {
676
+ console.log(` ${icons.warn} ${yellow(warning)}`);
677
+ }
437
678
 
438
679
  // Scaffold workspaces
439
680
  const results = scaffoldAll(agents, userInfo);
@@ -446,7 +687,15 @@ export async function runInstall(options = {}) {
446
687
  updatedWorkspaces++;
447
688
  }
448
689
  }
449
- console.log(` ✓ Workspaces: ${newWorkspaces} created, ${updatedWorkspaces} updated`);
690
+ printStepDone('Workspaces', `${newWorkspaces} created, ${updatedWorkspaces} updated`);
691
+
692
+ // Skills installation step
693
+ const finalRuntimeForSkills = await detectRuntime();
694
+ const skillsInstalled = await runSkillsWizardStep({
695
+ auto,
696
+ nemoclaw: finalRuntimeForSkills === 'nemoclaw',
697
+ sandboxName: 'my-assistant',
698
+ });
450
699
 
451
700
  // Write manifest
452
701
  const existing = readManifest();
@@ -456,16 +705,28 @@ export async function runInstall(options = {}) {
456
705
  agents: agents.map((a) => a.id),
457
706
  userName: userInfo.name,
458
707
  userTimezone: userInfo.timezone,
459
- modelPreset: preset,
708
+ modelPreset: effectivePreset,
460
709
  });
461
- console.log('Manifest written');
710
+ printStepDone('Manifest written');
462
711
 
463
- console.log(`\n 🎉 A.L.I.C.E. installed! ${agentCount} agents ready.`);
712
+ console.log('');
713
+ printSeparator();
714
+ console.log('');
715
+ console.log(` ${icons.ok} ${greenBold('A.L.I.C.E. installed!')} ${dim(agentCount + ' agents ready.')}`);
716
+ console.log('');
464
717
  const finalRuntime = await detectRuntime();
465
718
  if (finalRuntime === 'nemoclaw') {
466
- console.log(' 🔒 Running in secure mode OpenShell sandbox active\n');
719
+ console.log(` ${icons.ok} ${green('Secure mode')} ${dim('─')} OpenShell sandbox active`);
720
+ } else {
721
+ console.log(` ${icons.info} ${dim('Runtime: OpenClaw')}`);
467
722
  }
468
- console.log(' Restart OpenClaw to activate: openclaw gateway restart\n');
723
+ console.log('');
724
+ console.log(` ${dim('Manage skills:')} ${cyan('npx @robbiesrobotics/alice-agents --skills')}`);
725
+ console.log(` ${dim('Health check:')} ${cyan('npx @robbiesrobotics/alice-agents --doctor')}`);
726
+ console.log(` ${dim('Restart runtime:')} ${cyan('openclaw gateway restart')}`);
727
+ console.log('');
728
+ printSeparator();
729
+ console.log('');
469
730
  }
470
731
 
471
732
  export async function runUninstall(options = {}) {