soulhubcli 1.0.14 → 1.0.16

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 (3) hide show
  1. package/README.md +31 -22
  2. package/dist/index.cjs +85 -51
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # SoulHub CLI
2
2
 
3
- SoulHub CLI — 用于安装和管理 OpenClaw AI Agent 人格模板的命令行工具。支持从 SoulHub Registry 或本地目录安装单 Agent 和多 Agent 团队。
3
+ SoulHub CLI — 用于发现、安装和管理 AI Agent 灵魂(Soul)的命令行工具。支持从 SoulHub Registry 或本地目录安装单 Agent 和多 Agent 团队,兼容 OpenClaw 和 LightClaw。
4
4
 
5
5
  ## 安装
6
6
 
@@ -34,14 +34,14 @@ npx soulhubcli <command>
34
34
 
35
35
  | 命令 | 说明 |
36
36
  |------|------|
37
- | `soulhub search <keyword>` | 按关键词搜索 Agent 模板 |
38
- | `soulhub info <name>` | 查看 Agent 详细信息 |
39
- | `soulhub install <name>` | 从 Registry 安装 Agent 或团队 |
37
+ | `soulhub search <keyword>` | 搜索 Agent |
38
+ | `soulhub info <name>` | 查看 Agent 详细信息(identity、soul、skills 等) |
39
+ | `soulhub install <name>` | 从 Registry 安装 Agent 或团队(默认为 worker,安装到所有检测到的 claw) |
40
+ | `soulhub install <name> --main` | 安装为主 Agent |
40
41
  | `soulhub install --from <source>` | 从本地目录、ZIP 文件或 URL 安装 |
41
42
  | `soulhub list` | 列出已安装的 Agent |
42
43
  | `soulhub update [name]` | 更新已安装的 Agent |
43
- | `soulhub uninstall <name>` | 卸载已安装的 Agent |
44
- | `soulhub publish` | 验证并发布你的 Agent |
44
+ | `soulhub rollback` | 回滚到上一次安装状态 |
45
45
 
46
46
  ## 使用方法
47
47
 
@@ -56,11 +56,16 @@ soulhub search "content writing"
56
56
 
57
57
  CLI 会自动识别目标是单 Agent 还是多 Agent 团队,无需手动区分。
58
58
 
59
+ **默认行为:安装为 Worker Agent,自动安装到所有检测到的 claw 目录。**
60
+
59
61
  **从 Registry 安装:**
60
62
 
61
63
  ```bash
62
- # 安装单 Agent(作为主 Agent,部署到 workspace 目录)
63
- soulhub install ops-assistant
64
+ # 安装单 Agent(默认为 worker,安装到所有检测到的 claw)
65
+ soulhub install writer-wechat
66
+
67
+ # 安装为主 Agent
68
+ soulhub install writer-wechat --main
64
69
 
65
70
  # 安装多 Agent 团队(调度 Agent + 工作 Agent)
66
71
  soulhub install dev-squad
@@ -82,11 +87,11 @@ soulhub install --from https://example.com/agent-team.zip
82
87
  **指定目标目录:**
83
88
 
84
89
  ```bash
85
- # 安装到自定义目录(不依赖 OpenClaw 环境)
86
- soulhub install ops-assistant --dir ./my-agents
90
+ # 安装到自定义目录(不依赖 OpenClaw/LightClaw 环境)
91
+ soulhub install writer-wechat --dir ./my-agents
87
92
 
88
- # 指定 OpenClaw 安装目录
89
- soulhub install ops-assistant --claw-dir /opt/openclaw
93
+ # 指定 claw 安装目录(只安装到该 claw)
94
+ soulhub install writer-wechat --claw-dir ~/.lightclaw
90
95
  ```
91
96
 
92
97
  ### 列出已安装的 Agent
@@ -112,9 +117,11 @@ soulhub uninstall ops-assistant
112
117
 
113
118
  ### 单 Agent 安装
114
119
 
115
- - Agent 会作为**主 Agent** 安装到 OpenClaw 的 `workspace/` 目录
116
- - 如果已存在主 Agent,CLI 会**自动备份**(复制到 `agentbackup/workspace`),原目录保持不变
117
- - 仅覆盖 `IDENTITY.md` `SOUL.md` 等灵魂文件,不影响 workspace 中的其他运行时文件
120
+ - **默认安装为 Worker Agent**(子 agent),部署到 `workspace-<agentId>/` 目录
121
+ - 使用 `--main` 参数可安装为主 Agent,部署到 `workspace/` 目录
122
+ - **自动安装到所有检测到的 claw 目录**(OpenClaw / LightClaw),使用 `--claw-dir` 可指定单个 claw
123
+ - 如果目标目录已存在,CLI 会**自动备份**(复制到 `agentbackup/`)
124
+ - 仅覆盖 `IDENTITY.md`、`SOUL.md` 等灵魂文件,不影响 workspace 中的其他运行时文件
118
125
  - 安装完成后自动重启 OpenClaw Gateway;若重启失败会提示手动重启
119
126
 
120
127
  ### 多 Agent 团队安装
@@ -122,7 +129,7 @@ soulhub uninstall ops-assistant
122
129
  - **调度 Agent(Dispatcher)** 作为主 Agent 安装到 `workspace/` 目录
123
130
  - **工作 Agent(Worker)** 安装到各自的 `workspace-<agentId>/` 目录
124
131
  - 自动配置多 Agent 之间的通信
125
- - Worker Agent 通过 `openclaw agents add` 命令注册
132
+ - Worker Agent 自动注册到 claw 配置中
126
133
  - 安装完成后自动重启 OpenClaw Gateway
127
134
 
128
135
  ## 配置
@@ -137,18 +144,20 @@ CLI 将配置存储在 `~/.soulhub/config.json`。
137
144
  export SOULHUB_REGISTRY_URL=https://your-registry.example.com
138
145
  ```
139
146
 
140
- ### OpenClaw 目录
147
+ ### OpenClaw / LightClaw 目录
148
+
149
+ CLI 按以下优先级查找 claw 安装目录:
141
150
 
142
- CLI 按以下优先级查找 OpenClaw 安装目录:
151
+ 1. `--claw-dir` 命令行参数(指定时只安装到该 claw)
152
+ 2. `OPENCLAW_HOME` / `LIGHTCLAW_HOME` 环境变量
153
+ 3. 默认路径 `~/.openclaw`、`~/.lightclaw`
143
154
 
144
- 1. `--claw-dir` 命令行参数
145
- 2. `OPENCLAW_HOME` 环境变量
146
- 3. 默认路径 `~/.openclaw`
155
+ 未指定 `--claw-dir` 时,CLI 会检测所有可用的 claw 目录并全部安装。
147
156
 
148
157
  ## 环境要求
149
158
 
150
159
  - Node.js >= 18.0.0
151
- - OpenClaw(可选,使用 `--dir` 参数时不需要)
160
+ - OpenClaw 或 LightClaw(可选,使用 `--dir` 参数时不需要)
152
161
 
153
162
  ## License
154
163
 
package/dist/index.cjs CHANGED
@@ -19731,7 +19731,14 @@ async function resolveClawDir(clawDir) {
19731
19731
  }
19732
19732
  return promptSelectClawDir();
19733
19733
  }
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(
19734
+ function resolveAllClawDirs(clawDir) {
19735
+ if (clawDir) {
19736
+ const resolved = findOpenClawDir(clawDir);
19737
+ return resolved ? [resolved] : [];
19738
+ }
19739
+ return findAllClawDirs();
19740
+ }
19741
+ var installCommand = new Command("install").description("Install an agent or team from the SoulHub registry (default: as worker, installs to all detected claws)").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").option(
19735
19742
  "--dir <path>",
19736
19743
  "Target directory (defaults to OpenClaw/LightClaw workspace)"
19737
19744
  ).option(
@@ -19747,10 +19754,11 @@ var installCommand = new Command("install").description("Install an agent or tea
19747
19754
  } else {
19748
19755
  console.error(source_default.red("Please specify an agent or team name, or use --from to install from a local source."));
19749
19756
  console.log(source_default.dim(" Examples:"));
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/)"));
19752
- console.log(source_default.dim(" soulhub install dev-squad # \u4ECE registry \u5B89\u88C5\u56E2\u961F"));
19753
- console.log(source_default.dim(" soulhub install --from ./agent-team/ # \u4ECE\u672C\u5730\u76EE\u5F55\u5B89\u88C5"));
19757
+ console.log(source_default.dim(" soulhub install writer-wechat # Install as worker to all detected claws"));
19758
+ console.log(source_default.dim(" soulhub install writer-wechat --main # Install as main agent"));
19759
+ console.log(source_default.dim(" soulhub install dev-squad # Install a team from registry"));
19760
+ console.log(source_default.dim(" soulhub install --from ./agent-team/ # Install from local directory"));
19761
+ console.log(source_default.dim(" soulhub install writer-wechat --claw-dir ~/.lightclaw # Install to specific claw"));
19754
19762
  process.exit(1);
19755
19763
  }
19756
19764
  } catch (error) {
@@ -19762,7 +19770,7 @@ var installCommand = new Command("install").description("Install an agent or tea
19762
19770
  process.exit(1);
19763
19771
  }
19764
19772
  });
19765
- async function installFromRegistry(name, targetDir, clawDir, asMain = false) {
19773
+ async function installFromRegistry(name, targetDir, clawDir, asMain) {
19766
19774
  const spinner = createSpinner(`Checking registry for ${source_default.cyan(name)}...`).start();
19767
19775
  const index = await fetchIndex();
19768
19776
  const agent = index.agents.find((a) => a.name === name);
@@ -19782,21 +19790,17 @@ async function installFromRegistry(name, targetDir, clawDir, asMain = false) {
19782
19790
  }
19783
19791
  }
19784
19792
  async function installSingleAgent(name, targetDir, clawDir, asMain = false) {
19785
- const spinner = createSpinner(`Checking environment...`).start();
19786
- let selectedClawDir = null;
19787
- if (!targetDir) {
19788
- spinner.stop();
19789
- selectedClawDir = await resolveClawDir(clawDir);
19790
- if (!selectedClawDir) {
19791
- console.log(source_default.red("OpenClaw/LightClaw workspace directory not found."));
19792
- printOpenClawInstallHelp();
19793
- return;
19794
- }
19795
- spinner.start();
19796
- const brand = detectClawBrand(selectedClawDir);
19797
- spinner.text = source_default.dim(`${brand} detected: ${selectedClawDir}`);
19793
+ if (targetDir) {
19794
+ await installSingleAgentToClaw(name, null, targetDir, asMain);
19795
+ return;
19798
19796
  }
19799
- spinner.text = `Fetching agent ${source_default.cyan(name)}...`;
19797
+ const allClawDirs = resolveAllClawDirs(clawDir);
19798
+ if (allClawDirs.length === 0) {
19799
+ console.log(source_default.red("OpenClaw/LightClaw workspace directory not found."));
19800
+ printOpenClawInstallHelp();
19801
+ return;
19802
+ }
19803
+ const spinner = createSpinner(`Fetching agent ${source_default.cyan(name)}...`).start();
19800
19804
  const index = await fetchIndex();
19801
19805
  const agent = index.agents.find((a) => a.name === name);
19802
19806
  if (!agent) {
@@ -19804,8 +19808,33 @@ async function installSingleAgent(name, targetDir, clawDir, asMain = false) {
19804
19808
  console.log(source_default.dim(" Use 'soulhub search' to find available agents."));
19805
19809
  return;
19806
19810
  }
19807
- let workspaceDir;
19811
+ spinner.text = `Downloading ${source_default.cyan(agent.displayName)} package...`;
19812
+ const pkgDir = await downloadAgentPackage(name, agent.version);
19813
+ spinner.succeed(`Package ${source_default.cyan(agent.displayName)} downloaded.`);
19814
+ for (const selectedClawDir of allClawDirs) {
19815
+ await installSingleAgentToClaw(name, selectedClawDir, void 0, asMain, pkgDir, agent);
19816
+ }
19817
+ import_node_fs9.default.rmSync(pkgDir, { recursive: true, force: true });
19818
+ }
19819
+ async function installSingleAgentToClaw(name, selectedClawDir, targetDir, asMain = false, preDownloadedPkgDir, preLoadedAgent) {
19820
+ const brand = selectedClawDir ? detectClawBrand(selectedClawDir) : null;
19821
+ const clawLabel = brand ? `${brand} (${selectedClawDir})` : targetDir;
19822
+ const spinner = createSpinner(`Installing to ${source_default.dim(clawLabel)}...`).start();
19808
19823
  const agentId = name.toLowerCase().replace(/[\s_]+/g, "-");
19824
+ logger.info(`Install role resolved: asMain=${asMain}, claw=${selectedClawDir || targetDir}`);
19825
+ let agent = preLoadedAgent;
19826
+ if (!agent) {
19827
+ spinner.text = `Fetching agent ${source_default.cyan(name)}...`;
19828
+ const index = await fetchIndex();
19829
+ const found = index.agents.find((a) => a.name === name);
19830
+ if (!found) {
19831
+ spinner.fail(`Agent "${name}" not found in registry.`);
19832
+ console.log(source_default.dim(" Use 'soulhub search' to find available agents."));
19833
+ return;
19834
+ }
19835
+ agent = found;
19836
+ }
19837
+ let workspaceDir;
19809
19838
  if (targetDir) {
19810
19839
  workspaceDir = import_node_path12.default.resolve(targetDir);
19811
19840
  } else if (asMain) {
@@ -19817,9 +19846,7 @@ async function installSingleAgent(name, targetDir, clawDir, asMain = false) {
19817
19846
  if (!targetDir && asMain) {
19818
19847
  const mainCheck = checkMainAgentExists(selectedClawDir);
19819
19848
  if (mainCheck.hasContent) {
19820
- spinner.warn(
19821
- `Existing main agent detected. Backing up workspace...`
19822
- );
19849
+ spinner.warn(`Existing main agent detected. Backing up workspace...`);
19823
19850
  const backupDir = backupAgentWorkspace(workspaceDir);
19824
19851
  if (backupDir) {
19825
19852
  console.log(source_default.yellow(` \u26A0 Existing main agent backed up to: ${backupDir}`));
@@ -19859,21 +19886,23 @@ async function installSingleAgent(name, targetDir, clawDir, asMain = false) {
19859
19886
  if (asMain) {
19860
19887
  spinner.text = `Registering ${source_default.cyan(agent.displayName)} as main agent...`;
19861
19888
  addAgentToOpenClawConfig(selectedClawDir, "main", name, true);
19862
- spinner.text = source_default.dim(`Main agent registered in config`);
19863
19889
  } else {
19864
19890
  spinner.text = `Registering ${source_default.cyan(agent.displayName)} as worker agent...`;
19865
- const regResult = registerAgentToOpenClaw(agentId, workspaceDir, clawDir);
19891
+ const regResult = registerAgentToOpenClaw(agentId, workspaceDir);
19866
19892
  if (!regResult.success) {
19867
19893
  spinner.fail(`Failed to register ${agentId}: ${regResult.message}`);
19868
19894
  return;
19869
19895
  }
19870
- spinner.text = source_default.dim(`Worker agent ${agentId} registered in config`);
19871
19896
  }
19872
19897
  }
19873
- spinner.text = `Downloading ${source_default.cyan(agent.displayName)} package...`;
19874
- const pkgDir = await downloadAgentPackage(name, agent.version);
19875
- copyAgentFilesFromPackage(pkgDir, workspaceDir);
19876
- import_node_fs9.default.rmSync(pkgDir, { recursive: true, force: true });
19898
+ if (preDownloadedPkgDir) {
19899
+ copyAgentFilesFromPackage(preDownloadedPkgDir, workspaceDir);
19900
+ } else {
19901
+ spinner.text = `Downloading ${source_default.cyan(agent.displayName)} package...`;
19902
+ const pkgDir = await downloadAgentPackage(name, agent.version);
19903
+ copyAgentFilesFromPackage(pkgDir, workspaceDir);
19904
+ import_node_fs9.default.rmSync(pkgDir, { recursive: true, force: true });
19905
+ }
19877
19906
  recordInstall(name, agent.version, workspaceDir);
19878
19907
  if (backupRecord) {
19879
19908
  if (asMain) {
@@ -19887,9 +19916,8 @@ async function installSingleAgent(name, targetDir, clawDir, asMain = false) {
19887
19916
  const typeLabel = asMain ? source_default.blue("Single Agent (Main)") : source_default.green("Single Agent (Worker)");
19888
19917
  logger.info(`Single agent installed as ${roleLabel}: ${name}`, { version: agent.version, workspace: workspaceDir });
19889
19918
  spinner.succeed(
19890
- `${source_default.cyan.bold(agent.displayName)} installed as ${roleLabel}!`
19919
+ `${source_default.cyan.bold(agent.displayName)} installed as ${roleLabel} \u2192 ${source_default.dim(clawLabel)}`
19891
19920
  );
19892
- console.log();
19893
19921
  console.log(` ${source_default.dim("Location:")} ${workspaceDir}`);
19894
19922
  console.log(` ${source_default.dim("Version:")} ${agent.version}`);
19895
19923
  console.log(` ${source_default.dim("Type:")} ${typeLabel}`);
@@ -19990,7 +20018,7 @@ async function installRecipeFromRegistry(name, recipe, targetDir, clawDir) {
19990
20018
  await tryRestartGateway();
19991
20019
  }
19992
20020
  }
19993
- async function installFromSource(source, targetDir, clawDir, asMain = false) {
20021
+ async function installFromSource(source, targetDir, clawDir, asMain) {
19994
20022
  const spinner = createSpinner("Analyzing package...").start();
19995
20023
  let packageDir;
19996
20024
  let tempDir = null;
@@ -20057,22 +20085,29 @@ async function installFromSource(source, targetDir, clawDir, asMain = false) {
20057
20085
  }
20058
20086
  }
20059
20087
  async function installSingleAgentFromDir(packageDir, targetDir, clawDir, asMain = false) {
20060
- const spinner = createSpinner("Installing single agent...").start();
20061
20088
  const pkg = readSoulHubPackage(packageDir);
20062
20089
  const agentName = pkg?.name || import_node_path12.default.basename(packageDir);
20063
- let selectedClawDir = null;
20064
- if (!targetDir) {
20065
- spinner.stop();
20066
- selectedClawDir = await resolveClawDir(clawDir);
20067
- if (!selectedClawDir) {
20068
- console.log(source_default.red("OpenClaw/LightClaw workspace directory not found."));
20069
- printOpenClawInstallHelp();
20070
- return;
20071
- }
20072
- spinner.start();
20090
+ if (targetDir) {
20091
+ await installSingleAgentFromDirToClaw(packageDir, agentName, pkg, null, targetDir, asMain);
20092
+ return;
20073
20093
  }
20074
- let workspaceDir;
20094
+ const allClawDirs = resolveAllClawDirs(clawDir);
20095
+ if (allClawDirs.length === 0) {
20096
+ console.log(source_default.red("OpenClaw/LightClaw workspace directory not found."));
20097
+ printOpenClawInstallHelp();
20098
+ return;
20099
+ }
20100
+ for (const selectedClawDir of allClawDirs) {
20101
+ await installSingleAgentFromDirToClaw(packageDir, agentName, pkg, selectedClawDir, void 0, asMain);
20102
+ }
20103
+ }
20104
+ async function installSingleAgentFromDirToClaw(packageDir, agentName, pkg, selectedClawDir, targetDir, asMain = false) {
20105
+ const brand = selectedClawDir ? detectClawBrand(selectedClawDir) : null;
20106
+ const clawLabel = brand ? `${brand} (${selectedClawDir})` : targetDir;
20107
+ const spinner = createSpinner(`Installing to ${source_default.dim(clawLabel)}...`).start();
20075
20108
  const agentId = agentName.toLowerCase().replace(/[\s_]+/g, "-");
20109
+ logger.info(`Install role resolved: asMain=${asMain}, claw=${selectedClawDir || targetDir}`);
20110
+ let workspaceDir;
20076
20111
  if (targetDir) {
20077
20112
  workspaceDir = import_node_path12.default.resolve(targetDir);
20078
20113
  } else if (asMain) {
@@ -20121,7 +20156,7 @@ async function installSingleAgentFromDir(packageDir, targetDir, clawDir, asMain
20121
20156
  addAgentToOpenClawConfig(selectedClawDir, "main", agentName, true);
20122
20157
  } else {
20123
20158
  spinner.text = `Registering ${source_default.cyan(agentName)} as worker agent...`;
20124
- const regResult = registerAgentToOpenClaw(agentId, workspaceDir, clawDir);
20159
+ const regResult = registerAgentToOpenClaw(agentId, workspaceDir);
20125
20160
  if (!regResult.success) {
20126
20161
  spinner.fail(`Failed to register ${agentId}: ${regResult.message}`);
20127
20162
  return;
@@ -20142,8 +20177,7 @@ async function installSingleAgentFromDir(packageDir, targetDir, clawDir, asMain
20142
20177
  const roleLabel = asMain ? "main agent" : "worker agent";
20143
20178
  const typeLabel = asMain ? source_default.blue("Single Agent (Main)") : source_default.green("Single Agent (Worker)");
20144
20179
  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}!`);
20146
- console.log();
20180
+ spinner.succeed(`${source_default.cyan.bold(agentName)} installed as ${roleLabel} \u2192 ${source_default.dim(clawLabel)}`);
20147
20181
  console.log(` ${source_default.dim("Location:")} ${workspaceDir}`);
20148
20182
  console.log(` ${source_default.dim("Source:")} ${packageDir}`);
20149
20183
  console.log(` ${source_default.dim("Type:")} ${typeLabel}`);
@@ -20649,13 +20683,13 @@ function formatInstallType(type2) {
20649
20683
 
20650
20684
  // src/index.ts
20651
20685
  var program2 = new Command();
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", () => {
20686
+ program2.name("soulhub").description("SoulHub CLI - Discover, install and manage AI agent souls").version("1.0.16").option("--verbose", "Enable verbose debug logging").hook("preAction", () => {
20653
20687
  const opts = program2.opts();
20654
20688
  const verbose = opts.verbose || process.env.SOULHUB_DEBUG === "1";
20655
20689
  logger.init(verbose);
20656
20690
  logger.info("CLI started", {
20657
20691
  args: process.argv.slice(2),
20658
- version: "1.0.14",
20692
+ version: "1.0.16",
20659
20693
  node: process.version
20660
20694
  });
20661
20695
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "soulhubcli",
3
- "version": "1.0.14",
3
+ "version": "1.0.16",
4
4
  "description": "SoulHub CLI - Install and manage AI agent persona templates for OpenClaw",
5
5
  "type": "module",
6
6
  "bin": {