nextclaw 0.4.4 → 0.4.6

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.
Files changed (2) hide show
  1. package/dist/cli/index.js +222 -99
  2. package/package.json +1 -1
package/dist/cli/index.js CHANGED
@@ -35,7 +35,7 @@ import { startUiServer } from "nextclaw-server";
35
35
  import {
36
36
  closeSync,
37
37
  cpSync,
38
- existsSync as existsSync3,
38
+ existsSync as existsSync4,
39
39
  mkdirSync as mkdirSync2,
40
40
  openSync,
41
41
  readdirSync,
@@ -43,19 +43,16 @@ import {
43
43
  rmSync as rmSync2,
44
44
  writeFileSync as writeFileSync2
45
45
  } from "fs";
46
- import { dirname, join as join3, resolve as resolve3 } from "path";
47
- import { spawn as spawn2, spawnSync as spawnSync2 } from "child_process";
46
+ import { dirname, join as join3, resolve as resolve4 } from "path";
47
+ import { spawn as spawn2, spawnSync as spawnSync3 } from "child_process";
48
48
  import { createInterface } from "readline";
49
49
  import { createRequire } from "module";
50
- import { fileURLToPath as fileURLToPath3 } from "url";
50
+ import { fileURLToPath as fileURLToPath2 } from "url";
51
51
  import chokidar from "chokidar";
52
52
 
53
53
  // src/cli/gateway/controller.ts
54
54
  import { createHash } from "crypto";
55
55
  import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
56
- import { spawnSync } from "child_process";
57
- import { fileURLToPath as fileURLToPath2 } from "url";
58
- import { join as join2, resolve as resolve2 } from "path";
59
56
  import {
60
57
  ConfigSchema
61
58
  } from "nextclaw-core";
@@ -98,12 +95,12 @@ async function isPortAvailable(port, host) {
98
95
  return await canBindPort(port, checkHost);
99
96
  }
100
97
  async function canBindPort(port, host) {
101
- return await new Promise((resolve4) => {
98
+ return await new Promise((resolve5) => {
102
99
  const server = createServer();
103
100
  server.unref();
104
- server.once("error", () => resolve4(false));
101
+ server.once("error", () => resolve5(false));
105
102
  server.listen({ port, host }, () => {
106
- server.close(() => resolve4(true));
103
+ server.close(() => resolve5(true));
107
104
  });
108
105
  });
109
106
  }
@@ -161,7 +158,7 @@ async function waitForExit(pid, timeoutMs) {
161
158
  if (!isProcessRunning(pid)) {
162
159
  return true;
163
160
  }
164
- await new Promise((resolve4) => setTimeout(resolve4, 200));
161
+ await new Promise((resolve5) => setTimeout(resolve5, 200));
165
162
  }
166
163
  return !isProcessRunning(pid);
167
164
  }
@@ -292,11 +289,55 @@ function printAgentResponse(response) {
292
289
  async function prompt(rl, question) {
293
290
  rl.setPrompt(question);
294
291
  rl.prompt();
295
- return new Promise((resolve4) => {
296
- rl.once("line", (line) => resolve4(line));
292
+ return new Promise((resolve5) => {
293
+ rl.once("line", (line) => resolve5(line));
297
294
  });
298
295
  }
299
296
 
297
+ // src/cli/update/runner.ts
298
+ import { spawnSync } from "child_process";
299
+ import { resolve as resolve2 } from "path";
300
+ var DEFAULT_TIMEOUT_MS = 20 * 6e4;
301
+ function runSelfUpdate(options = {}) {
302
+ const steps = [];
303
+ const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
304
+ const updateCommand = options.updateCommand ?? process.env.NEXTCLAW_UPDATE_COMMAND?.trim();
305
+ const packageName = options.packageName ?? "nextclaw";
306
+ const runStep = (cmd, args, cwd) => {
307
+ const result = spawnSync(cmd, args, {
308
+ cwd,
309
+ encoding: "utf-8",
310
+ timeout: timeoutMs,
311
+ stdio: "pipe"
312
+ });
313
+ steps.push({
314
+ cmd,
315
+ args,
316
+ cwd,
317
+ code: result.status,
318
+ stdout: (result.stdout ?? "").toString().slice(0, 4e3),
319
+ stderr: (result.stderr ?? "").toString().slice(0, 4e3)
320
+ });
321
+ return { ok: result.status === 0, code: result.status };
322
+ };
323
+ if (updateCommand) {
324
+ const cwd = options.cwd ? resolve2(options.cwd) : process.cwd();
325
+ const ok = runStep("sh", ["-c", updateCommand], cwd);
326
+ if (!ok.ok) {
327
+ return { ok: false, error: "update command failed", strategy: "command", steps };
328
+ }
329
+ return { ok: true, strategy: "command", steps };
330
+ }
331
+ if (which("npm")) {
332
+ const ok = runStep("npm", ["i", "-g", packageName], process.cwd());
333
+ if (!ok.ok) {
334
+ return { ok: false, error: `npm install -g ${packageName} failed`, strategy: "npm", steps };
335
+ }
336
+ return { ok: true, strategy: "npm", steps };
337
+ }
338
+ return { ok: false, error: "no update strategy available", strategy: "none", steps };
339
+ }
340
+
300
341
  // src/cli/gateway/controller.ts
301
342
  var hashRaw = (raw) => createHash("sha256").update(raw).digest("hex");
302
343
  var redactConfig = (value) => {
@@ -503,62 +544,9 @@ var GatewayControllerImpl = class {
503
544
  };
504
545
  }
505
546
  async updateRun(params) {
506
- const timeoutMs = params.timeoutMs ?? 20 * 6e4;
507
- const gatewayDir = resolve2(fileURLToPath2(new URL(".", import.meta.url)));
508
- const cliDir = resolve2(gatewayDir, "..");
509
- const pkgRoot = resolve2(cliDir, "..", "..");
510
- const repoRoot = existsSync2(join2(pkgRoot, ".git")) ? pkgRoot : resolve2(pkgRoot, "..", "..");
511
- const steps = [];
512
- const runStep = (cmd, args, cwd) => {
513
- const result = spawnSync(cmd, args, {
514
- cwd,
515
- encoding: "utf-8",
516
- timeout: timeoutMs,
517
- stdio: "pipe"
518
- });
519
- const step = {
520
- cmd,
521
- args,
522
- cwd,
523
- code: result.status,
524
- stdout: (result.stdout ?? "").toString().slice(0, 4e3),
525
- stderr: (result.stderr ?? "").toString().slice(0, 4e3)
526
- };
527
- steps.push(step);
528
- return { ok: result.status === 0, code: result.status };
529
- };
530
- const updateCommand = process.env.NEXTCLAW_UPDATE_COMMAND?.trim();
531
- if (updateCommand) {
532
- const ok = runStep("sh", ["-c", updateCommand], process.cwd());
533
- if (!ok.ok) {
534
- return { ok: false, error: "update command failed", steps };
535
- }
536
- } else if (existsSync2(join2(repoRoot, ".git"))) {
537
- if (!which("git")) {
538
- return { ok: false, error: "git not found for repo update", steps };
539
- }
540
- const ok = runStep("git", ["-C", repoRoot, "pull", "--rebase"], repoRoot);
541
- if (!ok.ok) {
542
- return { ok: false, error: "git pull failed", steps };
543
- }
544
- if (existsSync2(join2(repoRoot, "pnpm-lock.yaml")) && which("pnpm")) {
545
- const installOk = runStep("pnpm", ["install"], repoRoot);
546
- if (!installOk.ok) {
547
- return { ok: false, error: "pnpm install failed", steps };
548
- }
549
- } else if (existsSync2(join2(repoRoot, "package.json")) && which("npm")) {
550
- const installOk = runStep("npm", ["install"], repoRoot);
551
- if (!installOk.ok) {
552
- return { ok: false, error: "npm install failed", steps };
553
- }
554
- }
555
- } else if (which("npm")) {
556
- const ok = runStep("npm", ["i", "-g", "nextclaw"], process.cwd());
557
- if (!ok.ok) {
558
- return { ok: false, error: "npm install -g nextclaw failed", steps };
559
- }
560
- } else {
561
- return { ok: false, error: "no update strategy available", steps };
547
+ const result = runSelfUpdate({ timeoutMs: params.timeoutMs });
548
+ if (!result.ok) {
549
+ return { ok: false, error: result.error ?? "update failed", steps: result.steps };
562
550
  }
563
551
  const delayMs = params.restartDelayMs ?? 0;
564
552
  scheduleRestart(delayMs, "update.run");
@@ -566,11 +554,82 @@ var GatewayControllerImpl = class {
566
554
  ok: true,
567
555
  note: params.note ?? null,
568
556
  restart: { scheduled: true, delayMs },
569
- steps
557
+ strategy: result.strategy,
558
+ steps: result.steps
570
559
  };
571
560
  }
572
561
  };
573
562
 
563
+ // src/cli/skills/clawhub.ts
564
+ import { spawnSync as spawnSync2 } from "child_process";
565
+ import { existsSync as existsSync3 } from "fs";
566
+ import { isAbsolute, join as join2, resolve as resolve3 } from "path";
567
+ async function installClawHubSkill(options) {
568
+ const slug = options.slug.trim();
569
+ if (!slug) {
570
+ throw new Error("Skill slug is required.");
571
+ }
572
+ const workdir = resolve3(options.workdir);
573
+ if (!existsSync3(workdir)) {
574
+ throw new Error(`Workdir does not exist: ${workdir}`);
575
+ }
576
+ const dirName = options.dir?.trim() || "skills";
577
+ const destinationDir = isAbsolute(dirName) ? resolve3(dirName, slug) : resolve3(workdir, dirName, slug);
578
+ const skillFile = join2(destinationDir, "SKILL.md");
579
+ if (!options.force && existsSync3(destinationDir)) {
580
+ if (existsSync3(skillFile)) {
581
+ return {
582
+ slug,
583
+ version: options.version,
584
+ registry: options.registry,
585
+ destinationDir,
586
+ alreadyInstalled: true
587
+ };
588
+ }
589
+ throw new Error(`Skill directory already exists: ${destinationDir} (use --force)`);
590
+ }
591
+ const args = buildClawHubArgs(slug, options);
592
+ const result = spawnSync2("npx", args, {
593
+ cwd: workdir,
594
+ stdio: "pipe",
595
+ env: process.env
596
+ });
597
+ if (result.error) {
598
+ throw new Error(`Failed to run npx clawhub: ${String(result.error)}`);
599
+ }
600
+ if (result.status !== 0) {
601
+ const stdout = result.stdout ? String(result.stdout).trim() : "";
602
+ const stderr = result.stderr ? String(result.stderr).trim() : "";
603
+ const details = [stderr, stdout].filter(Boolean).join("\n");
604
+ throw new Error(details || `clawhub install failed with code ${result.status ?? 1}`);
605
+ }
606
+ return {
607
+ slug,
608
+ version: options.version,
609
+ registry: options.registry,
610
+ destinationDir
611
+ };
612
+ }
613
+ function buildClawHubArgs(slug, options) {
614
+ const args = ["--yes", "clawhub", "install", slug];
615
+ if (options.version) {
616
+ args.push("--version", options.version);
617
+ }
618
+ if (options.registry) {
619
+ args.push("--registry", options.registry);
620
+ }
621
+ if (options.workdir) {
622
+ args.push("--workdir", options.workdir);
623
+ }
624
+ if (options.dir) {
625
+ args.push("--dir", options.dir);
626
+ }
627
+ if (options.force) {
628
+ args.push("--force");
629
+ }
630
+ return args;
631
+ }
632
+
574
633
  // src/cli/runtime.ts
575
634
  var LOGO = "\u{1F916}";
576
635
  var EXIT_COMMANDS = /* @__PURE__ */ new Set(["exit", "quit", "/exit", "/quit", ":q"]);
@@ -699,7 +758,7 @@ var CliRuntime = class {
699
758
  const force = Boolean(options.force);
700
759
  const configPath = getConfigPath();
701
760
  let createdConfig = false;
702
- if (!existsSync3(configPath)) {
761
+ if (!existsSync4(configPath)) {
703
762
  const config2 = ConfigSchema2.parse({});
704
763
  saveConfig(config2);
705
764
  createdConfig = true;
@@ -707,7 +766,7 @@ var CliRuntime = class {
707
766
  const config = loadConfig();
708
767
  const workspaceSetting = config.agents.defaults.workspace;
709
768
  const workspacePath = !workspaceSetting || workspaceSetting === DEFAULT_WORKSPACE_PATH ? join3(getDataDir2(), DEFAULT_WORKSPACE_DIR) : expandHome(workspaceSetting);
710
- const workspaceExisted = existsSync3(workspacePath);
769
+ const workspaceExisted = existsSync4(workspacePath);
711
770
  mkdirSync2(workspacePath, { recursive: true });
712
771
  const templateResult = this.createWorkspaceTemplates(workspacePath, { force });
713
772
  if (createdConfig) {
@@ -872,9 +931,9 @@ ${this.logo} ${APP_NAME} is ready! (${source})`);
872
931
  console.log(`${this.logo} Interactive mode (type exit or Ctrl+C to quit)
873
932
  `);
874
933
  const historyFile = join3(getDataDir2(), "history", "cli_history");
875
- const historyDir = resolve3(historyFile, "..");
934
+ const historyDir = resolve4(historyFile, "..");
876
935
  mkdirSync2(historyDir, { recursive: true });
877
- const history = existsSync3(historyFile) ? readFileSync3(historyFile, "utf-8").split("\n").filter(Boolean) : [];
936
+ const history = existsSync4(historyFile) ? readFileSync3(historyFile, "utf-8").split("\n").filter(Boolean) : [];
878
937
  const rl = createInterface({ input: process.stdin, output: process.stdout });
879
938
  rl.on("close", () => {
880
939
  const merged = history.concat(rl.history ?? []);
@@ -900,6 +959,62 @@ ${this.logo} ${APP_NAME} is ready! (${source})`);
900
959
  printAgentResponse(response);
901
960
  }
902
961
  }
962
+ async update(opts) {
963
+ let timeoutMs;
964
+ if (opts.timeout !== void 0) {
965
+ const parsed = Number(opts.timeout);
966
+ if (!Number.isFinite(parsed) || parsed <= 0) {
967
+ console.error("Invalid --timeout value. Provide milliseconds (e.g. 1200000).");
968
+ process.exit(1);
969
+ }
970
+ timeoutMs = parsed;
971
+ }
972
+ const result = runSelfUpdate({ timeoutMs, cwd: process.cwd() });
973
+ const printSteps = () => {
974
+ for (const step of result.steps) {
975
+ console.log(`- ${step.cmd} ${step.args.join(" ")} (code ${step.code ?? "?"})`);
976
+ if (step.stderr) {
977
+ console.log(` stderr: ${step.stderr}`);
978
+ }
979
+ if (step.stdout) {
980
+ console.log(` stdout: ${step.stdout}`);
981
+ }
982
+ }
983
+ };
984
+ if (!result.ok) {
985
+ console.error(`Update failed: ${result.error ?? "unknown error"}`);
986
+ if (result.steps.length > 0) {
987
+ printSteps();
988
+ }
989
+ process.exit(1);
990
+ }
991
+ console.log(`\u2713 Update complete (${result.strategy})`);
992
+ const state = readServiceState();
993
+ if (state && isProcessRunning(state.pid)) {
994
+ console.log(`Tip: restart ${APP_NAME} to apply the update.`);
995
+ }
996
+ }
997
+ async skillsInstall(options) {
998
+ const workdir = options.workdir ? expandHome(options.workdir) : getWorkspacePath();
999
+ const result = await installClawHubSkill({
1000
+ slug: options.slug,
1001
+ version: options.version,
1002
+ registry: options.registry,
1003
+ workdir,
1004
+ dir: options.dir,
1005
+ force: options.force
1006
+ });
1007
+ const versionLabel = result.version ?? "latest";
1008
+ if (result.alreadyInstalled) {
1009
+ console.log(`\u2713 ${result.slug} is already installed`);
1010
+ } else {
1011
+ console.log(`\u2713 Installed ${result.slug}@${versionLabel}`);
1012
+ }
1013
+ if (result.registry) {
1014
+ console.log(` Registry: ${result.registry}`);
1015
+ }
1016
+ console.log(` Path: ${result.destinationDir}`);
1017
+ }
903
1018
  channelsStatus() {
904
1019
  const config = loadConfig();
905
1020
  console.log("Channel Status");
@@ -915,7 +1030,7 @@ ${this.logo} ${APP_NAME} is ready! (${source})`);
915
1030
  const bridgeDir = this.getBridgeDir();
916
1031
  console.log(`${this.logo} Starting bridge...`);
917
1032
  console.log("Scan the QR code to connect.\n");
918
- const result = spawnSync2("npm", ["start"], { cwd: bridgeDir, stdio: "inherit" });
1033
+ const result = spawnSync3("npm", ["start"], { cwd: bridgeDir, stdio: "inherit" });
919
1034
  if (result.status !== 0) {
920
1035
  console.error(`Bridge failed: ${result.status ?? 1}`);
921
1036
  }
@@ -996,8 +1111,8 @@ ${this.logo} ${APP_NAME} is ready! (${source})`);
996
1111
  const workspace = getWorkspacePath(config.agents.defaults.workspace);
997
1112
  console.log(`${this.logo} ${APP_NAME} Status
998
1113
  `);
999
- console.log(`Config: ${configPath} ${existsSync3(configPath) ? "\u2713" : "\u2717"}`);
1000
- console.log(`Workspace: ${workspace} ${existsSync3(workspace) ? "\u2713" : "\u2717"}`);
1114
+ console.log(`Config: ${configPath} ${existsSync4(configPath) ? "\u2713" : "\u2717"}`);
1115
+ console.log(`Workspace: ${workspace} ${existsSync4(workspace) ? "\u2713" : "\u2717"}`);
1001
1116
  console.log(`Model: ${config.agents.defaults.model}`);
1002
1117
  for (const spec of PROVIDERS) {
1003
1118
  const provider = config.providers[spec.name];
@@ -1182,7 +1297,7 @@ ${this.logo} ${APP_NAME} is ready! (${source})`);
1182
1297
  console.log("Warning: UI frontend not found. Use --frontend to start the dev server.");
1183
1298
  }
1184
1299
  const logPath = resolveServiceLogPath();
1185
- const logDir = resolve3(logPath, "..");
1300
+ const logDir = resolve4(logPath, "..");
1186
1301
  mkdirSync2(logDir, { recursive: true });
1187
1302
  const logFd = openSync(logPath, "a");
1188
1303
  const serveArgs = buildServeArgs({
@@ -1292,11 +1407,11 @@ ${this.logo} ${APP_NAME} is ready! (${source})`);
1292
1407
  ];
1293
1408
  for (const entry of templateFiles) {
1294
1409
  const filePath = join3(workspace, entry.target);
1295
- if (!force && existsSync3(filePath)) {
1410
+ if (!force && existsSync4(filePath)) {
1296
1411
  continue;
1297
1412
  }
1298
1413
  const templatePath = join3(templateDir, entry.source);
1299
- if (!existsSync3(templatePath)) {
1414
+ if (!existsSync4(templatePath)) {
1300
1415
  console.warn(`Warning: Template file missing: ${templatePath}`);
1301
1416
  continue;
1302
1417
  }
@@ -1307,12 +1422,12 @@ ${this.logo} ${APP_NAME} is ready! (${source})`);
1307
1422
  created.push(entry.target);
1308
1423
  }
1309
1424
  const memoryDir = join3(workspace, "memory");
1310
- if (!existsSync3(memoryDir)) {
1425
+ if (!existsSync4(memoryDir)) {
1311
1426
  mkdirSync2(memoryDir, { recursive: true });
1312
1427
  created.push(join3("memory", ""));
1313
1428
  }
1314
1429
  const skillsDir = join3(workspace, "skills");
1315
- if (!existsSync3(skillsDir)) {
1430
+ if (!existsSync4(skillsDir)) {
1316
1431
  mkdirSync2(skillsDir, { recursive: true });
1317
1432
  created.push(join3("skills", ""));
1318
1433
  }
@@ -1338,11 +1453,11 @@ ${this.logo} ${APP_NAME} is ready! (${source})`);
1338
1453
  continue;
1339
1454
  }
1340
1455
  const src = join3(sourceDir, entry.name);
1341
- if (!existsSync3(join3(src, "SKILL.md"))) {
1456
+ if (!existsSync4(join3(src, "SKILL.md"))) {
1342
1457
  continue;
1343
1458
  }
1344
1459
  const dest = join3(targetDir, entry.name);
1345
- if (!force && existsSync3(dest)) {
1460
+ if (!force && existsSync4(dest)) {
1346
1461
  continue;
1347
1462
  }
1348
1463
  cpSync(src, dest, { recursive: true, force: true });
@@ -1354,13 +1469,13 @@ ${this.logo} ${APP_NAME} is ready! (${source})`);
1354
1469
  try {
1355
1470
  const require2 = createRequire(import.meta.url);
1356
1471
  const entry = require2.resolve("nextclaw-core");
1357
- const pkgRoot = resolve3(dirname(entry), "..");
1472
+ const pkgRoot = resolve4(dirname(entry), "..");
1358
1473
  const distSkills = join3(pkgRoot, "dist", "skills");
1359
- if (existsSync3(distSkills)) {
1474
+ if (existsSync4(distSkills)) {
1360
1475
  return distSkills;
1361
1476
  }
1362
1477
  const srcSkills = join3(pkgRoot, "src", "agent", "skills");
1363
- if (existsSync3(srcSkills)) {
1478
+ if (existsSync4(srcSkills)) {
1364
1479
  return srcSkills;
1365
1480
  }
1366
1481
  return null;
@@ -1373,11 +1488,11 @@ ${this.logo} ${APP_NAME} is ready! (${source})`);
1373
1488
  if (override) {
1374
1489
  return override;
1375
1490
  }
1376
- const cliDir = resolve3(fileURLToPath3(new URL(".", import.meta.url)));
1377
- const pkgRoot = resolve3(cliDir, "..", "..");
1491
+ const cliDir = resolve4(fileURLToPath2(new URL(".", import.meta.url)));
1492
+ const pkgRoot = resolve4(cliDir, "..", "..");
1378
1493
  const candidates = [join3(pkgRoot, "templates")];
1379
1494
  for (const candidate of candidates) {
1380
- if (existsSync3(candidate)) {
1495
+ if (existsSync4(candidate)) {
1381
1496
  return candidate;
1382
1497
  }
1383
1498
  }
@@ -1385,21 +1500,21 @@ ${this.logo} ${APP_NAME} is ready! (${source})`);
1385
1500
  }
1386
1501
  getBridgeDir() {
1387
1502
  const userBridge = join3(getDataDir2(), "bridge");
1388
- if (existsSync3(join3(userBridge, "dist", "index.js"))) {
1503
+ if (existsSync4(join3(userBridge, "dist", "index.js"))) {
1389
1504
  return userBridge;
1390
1505
  }
1391
1506
  if (!which("npm")) {
1392
1507
  console.error("npm not found. Please install Node.js >= 18.");
1393
1508
  process.exit(1);
1394
1509
  }
1395
- const cliDir = resolve3(fileURLToPath3(new URL(".", import.meta.url)));
1396
- const pkgRoot = resolve3(cliDir, "..", "..");
1510
+ const cliDir = resolve4(fileURLToPath2(new URL(".", import.meta.url)));
1511
+ const pkgRoot = resolve4(cliDir, "..", "..");
1397
1512
  const pkgBridge = join3(pkgRoot, "bridge");
1398
1513
  const srcBridge = join3(pkgRoot, "..", "..", "bridge");
1399
1514
  let source = null;
1400
- if (existsSync3(join3(pkgBridge, "package.json"))) {
1515
+ if (existsSync4(join3(pkgBridge, "package.json"))) {
1401
1516
  source = pkgBridge;
1402
- } else if (existsSync3(join3(srcBridge, "package.json"))) {
1517
+ } else if (existsSync4(join3(srcBridge, "package.json"))) {
1403
1518
  source = srcBridge;
1404
1519
  }
1405
1520
  if (!source) {
@@ -1407,15 +1522,15 @@ ${this.logo} ${APP_NAME} is ready! (${source})`);
1407
1522
  process.exit(1);
1408
1523
  }
1409
1524
  console.log(`${this.logo} Setting up bridge...`);
1410
- mkdirSync2(resolve3(userBridge, ".."), { recursive: true });
1411
- if (existsSync3(userBridge)) {
1525
+ mkdirSync2(resolve4(userBridge, ".."), { recursive: true });
1526
+ if (existsSync4(userBridge)) {
1412
1527
  rmSync2(userBridge, { recursive: true, force: true });
1413
1528
  }
1414
1529
  cpSync(source, userBridge, {
1415
1530
  recursive: true,
1416
1531
  filter: (src) => !src.includes("node_modules") && !src.includes("dist")
1417
1532
  });
1418
- const install = spawnSync2("npm", ["install"], { cwd: userBridge, stdio: "pipe" });
1533
+ const install = spawnSync3("npm", ["install"], { cwd: userBridge, stdio: "pipe" });
1419
1534
  if (install.status !== 0) {
1420
1535
  console.error(`Bridge install failed: ${install.status ?? 1}`);
1421
1536
  if (install.stderr) {
@@ -1423,7 +1538,7 @@ ${this.logo} ${APP_NAME} is ready! (${source})`);
1423
1538
  }
1424
1539
  process.exit(1);
1425
1540
  }
1426
- const build = spawnSync2("npm", ["run", "build"], { cwd: userBridge, stdio: "pipe" });
1541
+ const build = spawnSync3("npm", ["run", "build"], { cwd: userBridge, stdio: "pipe" });
1427
1542
  if (build.status !== 0) {
1428
1543
  console.error(`Bridge build failed: ${build.status ?? 1}`);
1429
1544
  if (build.stderr) {
@@ -1448,6 +1563,14 @@ program.command("start").description(`Start the ${APP_NAME2} gateway + UI in the
1448
1563
  program.command("serve").description(`Run the ${APP_NAME2} gateway + UI in the foreground`).option("--ui-host <host>", "UI host").option("--ui-port <port>", "UI port").option("--frontend", "Start UI frontend dev server").option("--frontend-port <port>", "UI frontend dev server port").option("--open", "Open browser after start", false).action(async (opts) => runtime.serve(opts));
1449
1564
  program.command("stop").description(`Stop the ${APP_NAME2} background service`).action(async () => runtime.stop());
1450
1565
  program.command("agent").description("Interact with the agent directly").option("-m, --message <message>", "Message to send to the agent").option("-s, --session <session>", "Session ID", "cli:default").option("--no-markdown", "Disable Markdown rendering").action(async (opts) => runtime.agent(opts));
1566
+ program.command("update").description(`Update ${APP_NAME2}`).option("--timeout <ms>", "Update command timeout in milliseconds").action(async (opts) => runtime.update(opts));
1567
+ var registerClawHubInstall = (cmd) => {
1568
+ cmd.command("install <slug>").description("Install a skill from ClawHub").option("--version <version>", "Skill version (default: latest)").option("--registry <url>", "ClawHub registry base URL").option("--workdir <dir>", "Workspace directory to install into").option("--dir <dir>", "Skills directory name (default: skills)").option("-f, --force", "Overwrite existing skill files", false).action(async (slug, opts) => runtime.skillsInstall({ slug, ...opts }));
1569
+ };
1570
+ var skills = program.command("skills").description("Manage skills");
1571
+ registerClawHubInstall(skills);
1572
+ var clawhub = program.command("clawhub").description("Install skills from ClawHub");
1573
+ registerClawHubInstall(clawhub);
1451
1574
  var channels = program.command("channels").description("Manage channels");
1452
1575
  channels.command("status").description("Show channel status").action(() => runtime.channelsStatus());
1453
1576
  channels.command("login").description("Link device via QR code").action(() => runtime.channelsLogin());
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nextclaw",
3
- "version": "0.4.4",
3
+ "version": "0.4.6",
4
4
  "description": "Lightweight personal AI assistant with CLI, multi-provider routing, and channel integrations.",
5
5
  "private": false,
6
6
  "type": "module",