soulhubcli 1.0.13 → 1.0.14

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/index.cjs +110 -36
  2. package/package.json +1 -1
package/dist/index.cjs CHANGED
@@ -19078,7 +19078,14 @@ function findAllClawDirs(customDir) {
19078
19078
  import_node_path11.default.join(process.cwd(), ".openclaw"),
19079
19079
  import_node_path11.default.join(process.cwd(), ".lightclaw")
19080
19080
  ];
19081
- return candidates.filter((c) => import_node_fs8.default.existsSync(c));
19081
+ const existing = candidates.filter((c) => import_node_fs8.default.existsSync(c));
19082
+ const seen = /* @__PURE__ */ new Set();
19083
+ return existing.filter((c) => {
19084
+ const real = import_node_fs8.default.realpathSync(c);
19085
+ if (seen.has(real)) return false;
19086
+ seen.add(real);
19087
+ return true;
19088
+ });
19082
19089
  }
19083
19090
  async function promptSelectClawDir(customDir) {
19084
19091
  const dirs = findAllClawDirs(customDir);
@@ -19491,7 +19498,7 @@ function restartOpenClawGateway() {
19491
19498
  }
19492
19499
 
19493
19500
  // src/commands/search.ts
19494
- var searchCommand = new Command("search").description("Search for agent templates in the SoulHub registry").argument("[query]", "Search query (matches name, description, tags)").option("-c, --category <category>", "Filter by category").option("-l, --limit <number>", "Max results to show", "20").action(async (query, options) => {
19501
+ var searchCommand = new Command("search").description("Search for agents in the SoulHub registry").argument("[query]", "Search query (matches name, description, tags)").option("-c, --category <category>", "Filter by category").option("-l, --limit <number>", "Max results to show", "20").action(async (query, options) => {
19495
19502
  try {
19496
19503
  const index = await fetchIndex();
19497
19504
  let agents = index.agents;
@@ -19557,7 +19564,7 @@ var searchCommand = new Command("search").description("Search for agent template
19557
19564
  });
19558
19565
 
19559
19566
  // src/commands/info.ts
19560
- var infoCommand = new Command("info").description("View detailed information about an agent template").argument("<name>", "Agent name").option("--identity", "Show IDENTITY.md content").option("--soul", "Show SOUL.md content").action(async (name, options) => {
19567
+ var infoCommand = new Command("info").description("Show details of an agent (identity, soul, skills, etc.)").argument("<name>", "Agent name").option("--identity", "Show IDENTITY.md content").option("--soul", "Show SOUL.md content").action(async (name, options) => {
19561
19568
  try {
19562
19569
  const index = await fetchIndex();
19563
19570
  const agent = index.agents.find((a) => a.name === name);
@@ -19724,7 +19731,7 @@ async function resolveClawDir(clawDir) {
19724
19731
  }
19725
19732
  return promptSelectClawDir();
19726
19733
  }
19727
- var installCommand = new Command("install").description("Install an agent or team from the SoulHub registry").argument("[name]", "Agent or team name to install").option("--from <source>", "Install from a local directory, ZIP file, or URL").option(
19734
+ var installCommand = new Command("install").description("Install an agent or team from the SoulHub registry (default: as worker)").argument("[name]", "Agent or team name to install").option("--from <source>", "Install from a local directory, ZIP file, or URL").option("--main", "Install as main agent (default is worker/sub-agent)").option(
19728
19735
  "--dir <path>",
19729
19736
  "Target directory (defaults to OpenClaw/LightClaw workspace)"
19730
19737
  ).option(
@@ -19732,14 +19739,16 @@ var installCommand = new Command("install").description("Install an agent or tea
19732
19739
  "OpenClaw/LightClaw installation directory (overrides OPENCLAW_HOME/LIGHTCLAW_HOME env var, defaults to ~/.openclaw or ~/.lightclaw)"
19733
19740
  ).action(async (name, options) => {
19734
19741
  try {
19742
+ const asMain = !!options.main;
19735
19743
  if (options.from) {
19736
- await installFromSource(options.from, options.dir, options.clawDir);
19744
+ await installFromSource(options.from, options.dir, options.clawDir, asMain);
19737
19745
  } else if (name) {
19738
- await installFromRegistry(name, options.dir, options.clawDir);
19746
+ await installFromRegistry(name, options.dir, options.clawDir, asMain);
19739
19747
  } else {
19740
19748
  console.error(source_default.red("Please specify an agent or team name, or use --from to install from a local source."));
19741
19749
  console.log(source_default.dim(" Examples:"));
19742
- console.log(source_default.dim(" soulhub install writer-wechat # \u4ECE registry \u5B89\u88C5\u5355 agent"));
19750
+ console.log(source_default.dim(" soulhub install writer-wechat # \u5B89\u88C5\u4E3A\u5B50 agent (workspace-writer-wechat/)"));
19751
+ console.log(source_default.dim(" soulhub install writer-wechat --main # \u5B89\u88C5\u4E3A\u4E3B agent (workspace/)"));
19743
19752
  console.log(source_default.dim(" soulhub install dev-squad # \u4ECE registry \u5B89\u88C5\u56E2\u961F"));
19744
19753
  console.log(source_default.dim(" soulhub install --from ./agent-team/ # \u4ECE\u672C\u5730\u76EE\u5F55\u5B89\u88C5"));
19745
19754
  process.exit(1);
@@ -19753,15 +19762,15 @@ var installCommand = new Command("install").description("Install an agent or tea
19753
19762
  process.exit(1);
19754
19763
  }
19755
19764
  });
19756
- async function installFromRegistry(name, targetDir, clawDir) {
19765
+ async function installFromRegistry(name, targetDir, clawDir, asMain = false) {
19757
19766
  const spinner = createSpinner(`Checking registry for ${source_default.cyan(name)}...`).start();
19758
19767
  const index = await fetchIndex();
19759
19768
  const agent = index.agents.find((a) => a.name === name);
19760
19769
  const recipe = index.recipes.find((r) => r.name === name);
19761
19770
  if (agent && !recipe) {
19762
19771
  spinner.stop();
19763
- logger.info(`Installing single agent from registry: ${name}`);
19764
- await installSingleAgent(name, targetDir, clawDir);
19772
+ logger.info(`Installing single agent from registry: ${name}, asMain=${asMain}`);
19773
+ await installSingleAgent(name, targetDir, clawDir, asMain);
19765
19774
  } else if (recipe) {
19766
19775
  spinner.stop();
19767
19776
  logger.info(`Installing team recipe from registry: ${name}`);
@@ -19772,7 +19781,7 @@ async function installFromRegistry(name, targetDir, clawDir) {
19772
19781
  console.log(source_default.dim(" Use 'soulhub search' to find available agents and teams."));
19773
19782
  }
19774
19783
  }
19775
- async function installSingleAgent(name, targetDir, clawDir) {
19784
+ async function installSingleAgent(name, targetDir, clawDir, asMain = false) {
19776
19785
  const spinner = createSpinner(`Checking environment...`).start();
19777
19786
  let selectedClawDir = null;
19778
19787
  if (!targetDir) {
@@ -19796,13 +19805,16 @@ async function installSingleAgent(name, targetDir, clawDir) {
19796
19805
  return;
19797
19806
  }
19798
19807
  let workspaceDir;
19808
+ const agentId = name.toLowerCase().replace(/[\s_]+/g, "-");
19799
19809
  if (targetDir) {
19800
19810
  workspaceDir = import_node_path12.default.resolve(targetDir);
19801
- } else {
19811
+ } else if (asMain) {
19802
19812
  workspaceDir = getMainWorkspaceDir(selectedClawDir);
19813
+ } else {
19814
+ workspaceDir = getWorkspaceDir(selectedClawDir, agentId);
19803
19815
  }
19804
19816
  const backupRecord = !targetDir ? createBackupRecord("single-agent", name, selectedClawDir) : null;
19805
- if (!targetDir) {
19817
+ if (!targetDir && asMain) {
19806
19818
  const mainCheck = checkMainAgentExists(selectedClawDir);
19807
19819
  if (mainCheck.hasContent) {
19808
19820
  spinner.warn(
@@ -19820,7 +19832,21 @@ async function installSingleAgent(name, targetDir, clawDir) {
19820
19832
  });
19821
19833
  }
19822
19834
  }
19823
- } else {
19835
+ } else if (!targetDir && !asMain) {
19836
+ if (import_node_fs9.default.existsSync(workspaceDir)) {
19837
+ const backupDir = backupAgentWorkspace(workspaceDir);
19838
+ if (backupDir) {
19839
+ console.log(source_default.yellow(` \u26A0 Existing worker ${agentId} backed up to: ${backupDir}`));
19840
+ addBackupItem(backupRecord, {
19841
+ originalPath: workspaceDir,
19842
+ backupPath: backupDir,
19843
+ method: "cp",
19844
+ role: "worker",
19845
+ agentId
19846
+ });
19847
+ }
19848
+ }
19849
+ } else if (targetDir) {
19824
19850
  const backupDir = backupAgentWorkspace(workspaceDir);
19825
19851
  if (backupDir) {
19826
19852
  console.log(source_default.yellow(` \u26A0 Existing agent backed up to: ${backupDir}`));
@@ -19830,9 +19856,19 @@ async function installSingleAgent(name, targetDir, clawDir) {
19830
19856
  import_node_fs9.default.mkdirSync(workspaceDir, { recursive: true });
19831
19857
  }
19832
19858
  if (!targetDir) {
19833
- spinner.text = `Registering ${source_default.cyan(agent.displayName)} as main agent...`;
19834
- addAgentToOpenClawConfig(selectedClawDir, "main", name, true);
19835
- spinner.text = source_default.dim(`Main agent registered in config`);
19859
+ if (asMain) {
19860
+ spinner.text = `Registering ${source_default.cyan(agent.displayName)} as main agent...`;
19861
+ addAgentToOpenClawConfig(selectedClawDir, "main", name, true);
19862
+ spinner.text = source_default.dim(`Main agent registered in config`);
19863
+ } else {
19864
+ spinner.text = `Registering ${source_default.cyan(agent.displayName)} as worker agent...`;
19865
+ const regResult = registerAgentToOpenClaw(agentId, workspaceDir, clawDir);
19866
+ if (!regResult.success) {
19867
+ spinner.fail(`Failed to register ${agentId}: ${regResult.message}`);
19868
+ return;
19869
+ }
19870
+ spinner.text = source_default.dim(`Worker agent ${agentId} registered in config`);
19871
+ }
19836
19872
  }
19837
19873
  spinner.text = `Downloading ${source_default.cyan(agent.displayName)} package...`;
19838
19874
  const pkgDir = await downloadAgentPackage(name, agent.version);
@@ -19840,17 +19876,23 @@ async function installSingleAgent(name, targetDir, clawDir) {
19840
19876
  import_node_fs9.default.rmSync(pkgDir, { recursive: true, force: true });
19841
19877
  recordInstall(name, agent.version, workspaceDir);
19842
19878
  if (backupRecord) {
19843
- backupRecord.installedMainAgent = name;
19879
+ if (asMain) {
19880
+ backupRecord.installedMainAgent = name;
19881
+ } else {
19882
+ backupRecord.installedWorkerIds = [agentId];
19883
+ }
19844
19884
  commitBackupRecord(backupRecord);
19845
19885
  }
19846
- logger.info(`Single agent installed: ${name}`, { version: agent.version, workspace: workspaceDir });
19886
+ const roleLabel = asMain ? "main agent" : "worker agent";
19887
+ const typeLabel = asMain ? source_default.blue("Single Agent (Main)") : source_default.green("Single Agent (Worker)");
19888
+ logger.info(`Single agent installed as ${roleLabel}: ${name}`, { version: agent.version, workspace: workspaceDir });
19847
19889
  spinner.succeed(
19848
- `${source_default.cyan.bold(agent.displayName)} installed as main agent!`
19890
+ `${source_default.cyan.bold(agent.displayName)} installed as ${roleLabel}!`
19849
19891
  );
19850
19892
  console.log();
19851
19893
  console.log(` ${source_default.dim("Location:")} ${workspaceDir}`);
19852
19894
  console.log(` ${source_default.dim("Version:")} ${agent.version}`);
19853
- console.log(` ${source_default.dim("Type:")} ${source_default.blue("Single Agent (Main)")}`);
19895
+ console.log(` ${source_default.dim("Type:")} ${typeLabel}`);
19854
19896
  if (!targetDir) {
19855
19897
  await tryRestartGateway();
19856
19898
  }
@@ -19948,7 +19990,7 @@ async function installRecipeFromRegistry(name, recipe, targetDir, clawDir) {
19948
19990
  await tryRestartGateway();
19949
19991
  }
19950
19992
  }
19951
- async function installFromSource(source, targetDir, clawDir) {
19993
+ async function installFromSource(source, targetDir, clawDir, asMain = false) {
19952
19994
  const spinner = createSpinner("Analyzing package...").start();
19953
19995
  let packageDir;
19954
19996
  let tempDir = null;
@@ -20002,7 +20044,7 @@ async function installFromSource(source, targetDir, clawDir) {
20002
20044
  spinner.text = `Detected package type: ${source_default.blue(kind)}`;
20003
20045
  if (kind === "agent") {
20004
20046
  spinner.stop();
20005
- await installSingleAgentFromDir(packageDir, targetDir, clawDir);
20047
+ await installSingleAgentFromDir(packageDir, targetDir, clawDir, asMain);
20006
20048
  } else if (kind === "team") {
20007
20049
  spinner.stop();
20008
20050
  await installTeamFromDir(packageDir, targetDir, clawDir);
@@ -20014,7 +20056,7 @@ async function installFromSource(source, targetDir, clawDir) {
20014
20056
  import_node_fs9.default.rmSync(tempDir, { recursive: true, force: true });
20015
20057
  }
20016
20058
  }
20017
- async function installSingleAgentFromDir(packageDir, targetDir, clawDir) {
20059
+ async function installSingleAgentFromDir(packageDir, targetDir, clawDir, asMain = false) {
20018
20060
  const spinner = createSpinner("Installing single agent...").start();
20019
20061
  const pkg = readSoulHubPackage(packageDir);
20020
20062
  const agentName = pkg?.name || import_node_path12.default.basename(packageDir);
@@ -20030,13 +20072,16 @@ async function installSingleAgentFromDir(packageDir, targetDir, clawDir) {
20030
20072
  spinner.start();
20031
20073
  }
20032
20074
  let workspaceDir;
20075
+ const agentId = agentName.toLowerCase().replace(/[\s_]+/g, "-");
20033
20076
  if (targetDir) {
20034
20077
  workspaceDir = import_node_path12.default.resolve(targetDir);
20035
- } else {
20078
+ } else if (asMain) {
20036
20079
  workspaceDir = getMainWorkspaceDir(selectedClawDir);
20080
+ } else {
20081
+ workspaceDir = getWorkspaceDir(selectedClawDir, agentId);
20037
20082
  }
20038
20083
  const localBackupRecord = !targetDir ? createBackupRecord("single-agent-local", agentName, selectedClawDir) : null;
20039
- if (!targetDir) {
20084
+ if (!targetDir && asMain) {
20040
20085
  const mainCheck = checkMainAgentExists(selectedClawDir);
20041
20086
  if (mainCheck.hasContent) {
20042
20087
  spinner.warn("Existing main agent detected. Backing up...");
@@ -20052,27 +20097,56 @@ async function installSingleAgentFromDir(packageDir, targetDir, clawDir) {
20052
20097
  });
20053
20098
  }
20054
20099
  }
20100
+ } else if (!targetDir && !asMain) {
20101
+ if (import_node_fs9.default.existsSync(workspaceDir)) {
20102
+ const backupDir = backupAgentWorkspace(workspaceDir);
20103
+ if (backupDir) {
20104
+ console.log(source_default.yellow(` \u26A0 Existing worker ${agentId} backed up to: ${backupDir}`));
20105
+ addBackupItem(localBackupRecord, {
20106
+ originalPath: workspaceDir,
20107
+ backupPath: backupDir,
20108
+ method: "cp",
20109
+ role: "worker",
20110
+ agentId
20111
+ });
20112
+ }
20113
+ }
20055
20114
  }
20056
20115
  if (!import_node_fs9.default.existsSync(workspaceDir)) {
20057
20116
  import_node_fs9.default.mkdirSync(workspaceDir, { recursive: true });
20058
20117
  }
20059
20118
  if (!targetDir) {
20060
- spinner.text = `Registering ${source_default.cyan(agentName)} as main agent...`;
20061
- addAgentToOpenClawConfig(selectedClawDir, "main", agentName, true);
20119
+ if (asMain) {
20120
+ spinner.text = `Registering ${source_default.cyan(agentName)} as main agent...`;
20121
+ addAgentToOpenClawConfig(selectedClawDir, "main", agentName, true);
20122
+ } else {
20123
+ spinner.text = `Registering ${source_default.cyan(agentName)} as worker agent...`;
20124
+ const regResult = registerAgentToOpenClaw(agentId, workspaceDir, clawDir);
20125
+ if (!regResult.success) {
20126
+ spinner.fail(`Failed to register ${agentId}: ${regResult.message}`);
20127
+ return;
20128
+ }
20129
+ }
20062
20130
  }
20063
20131
  spinner.text = `Copying soul files...`;
20064
20132
  copyAgentFilesFromDir(packageDir, workspaceDir);
20065
20133
  recordInstall(agentName, pkg?.version || "local", workspaceDir);
20066
20134
  if (localBackupRecord) {
20067
- localBackupRecord.installedMainAgent = agentName;
20135
+ if (asMain) {
20136
+ localBackupRecord.installedMainAgent = agentName;
20137
+ } else {
20138
+ localBackupRecord.installedWorkerIds = [agentId];
20139
+ }
20068
20140
  commitBackupRecord(localBackupRecord);
20069
20141
  }
20070
- logger.info(`Single agent installed from dir: ${agentName}`, { source: packageDir, workspace: workspaceDir });
20071
- spinner.succeed(`${source_default.cyan.bold(agentName)} installed as main agent!`);
20142
+ const roleLabel = asMain ? "main agent" : "worker agent";
20143
+ const typeLabel = asMain ? source_default.blue("Single Agent (Main)") : source_default.green("Single Agent (Worker)");
20144
+ logger.info(`Single agent installed from dir as ${roleLabel}: ${agentName}`, { source: packageDir, workspace: workspaceDir });
20145
+ spinner.succeed(`${source_default.cyan.bold(agentName)} installed as ${roleLabel}!`);
20072
20146
  console.log();
20073
20147
  console.log(` ${source_default.dim("Location:")} ${workspaceDir}`);
20074
20148
  console.log(` ${source_default.dim("Source:")} ${packageDir}`);
20075
- console.log(` ${source_default.dim("Type:")} ${source_default.blue("Single Agent (Main)")}`);
20149
+ console.log(` ${source_default.dim("Type:")} ${typeLabel}`);
20076
20150
  if (!targetDir) {
20077
20151
  await tryRestartGateway();
20078
20152
  }
@@ -20294,7 +20368,7 @@ async function tryRestartGateway() {
20294
20368
  }
20295
20369
 
20296
20370
  // src/commands/list.ts
20297
- var listCommand = new Command("list").description("List installed agent templates").alias("ls").action(async () => {
20371
+ var listCommand = new Command("list").description("List installed agents").alias("ls").action(async () => {
20298
20372
  try {
20299
20373
  const config = loadConfig();
20300
20374
  if (config.installed.length === 0) {
@@ -20334,7 +20408,7 @@ var listCommand = new Command("list").description("List installed agent template
20334
20408
 
20335
20409
  // src/commands/update.ts
20336
20410
  var import_node_fs10 = __toESM(require("fs"), 1);
20337
- var updateCommand = new Command("update").description("Update installed agent templates to latest versions").argument("[name]", "Agent name to update (updates all if omitted)").action(async (name) => {
20411
+ var updateCommand = new Command("update").description("Update installed agents to latest versions").argument("[name]", "Agent name to update (updates all if omitted)").action(async (name) => {
20338
20412
  try {
20339
20413
  const config = loadConfig();
20340
20414
  if (config.installed.length === 0) {
@@ -20575,13 +20649,13 @@ function formatInstallType(type2) {
20575
20649
 
20576
20650
  // src/index.ts
20577
20651
  var program2 = new Command();
20578
- program2.name("soulhub").description("SoulHub CLI - Install and manage AI agent persona templates").version("1.0.13").option("--verbose", "Enable verbose debug logging").hook("preAction", () => {
20652
+ program2.name("soulhub").description("SoulHub CLI - Discover, install and manage AI agent souls").version("1.0.14").option("--verbose", "Enable verbose debug logging").hook("preAction", () => {
20579
20653
  const opts = program2.opts();
20580
20654
  const verbose = opts.verbose || process.env.SOULHUB_DEBUG === "1";
20581
20655
  logger.init(verbose);
20582
20656
  logger.info("CLI started", {
20583
20657
  args: process.argv.slice(2),
20584
- version: "1.0.13",
20658
+ version: "1.0.14",
20585
20659
  node: process.version
20586
20660
  });
20587
20661
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "soulhubcli",
3
- "version": "1.0.13",
3
+ "version": "1.0.14",
4
4
  "description": "SoulHub CLI - Install and manage AI agent persona templates for OpenClaw",
5
5
  "type": "module",
6
6
  "bin": {