soulhubcli 1.0.23 → 1.0.24

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 +7 -7
  2. package/dist/index.cjs +90 -306
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -42,7 +42,7 @@ npx soulhubcli <command>
42
42
  | `soulhub info <name> --identity` | 显示 IDENTITY.md 内容 |
43
43
  | `soulhub info <name> --soul` | 显示 SOUL.md 内容 |
44
44
  | `soulhub info <name> --json` | 以 JSON 格式输出 Agent 详情 |
45
- | `soulhub install <name>` | 从 Registry 安装 Agent 或团队(交互式:选择角色和目标 claw) |
45
+ | `soulhub install <name>` | 从 Registry 安装 Agent 或团队(交互式:选择角色和目标 Claw) |
46
46
  | `soulhub install <name> --role main` | 安装为主 Agent(跳过角色选择) |
47
47
  | `soulhub install <name> --role worker` | 安装为 Worker Agent(跳过角色选择) |
48
48
  | `soulhub install <name> --claw-type <type>` | 指定 claw 类型(跳过 claw 选择) |
@@ -92,7 +92,7 @@ soulhub search writer --json
92
92
 
93
93
  CLI 会自动识别目标是单 Agent 还是多 Agent 团队,无需手动区分。
94
94
 
95
- **默认行为:交互式安装。** CLI 会提示用户选择安装角色(主 Agent / Worker Agent)以及目标 claw 目录(支持多选)。通过命令行参数可跳过交互,实现完全非交互式安装。
95
+ **默认行为:交互式安装。** CLI 会提示用户选择安装角色(主 Agent / Worker Agent)以及目标 Claw 目录(单选)。通过命令行参数可跳过交互,实现完全非交互式安装。
96
96
 
97
97
  **从 Registry 安装:**
98
98
 
@@ -215,8 +215,8 @@ soulhub rollback --id <record-id>
215
215
  1. 展示 Agent 基本信息(名称、版本、描述、分类、标签)
216
216
  2. 提示选择安装角色:**Main Agent** 或 **Worker Agent**
217
217
  3. 安装为 Main Agent 时,警告将覆盖当前 workspace 内容(人格文件会被替换,记忆不受影响),需用户确认(或使用 `-y` 跳过)
218
- 4. 提示多选目标 claw 目录(OpenClaw / LightClaw),可同时安装到多个 claw
219
- 5. 执行安装、注册、重启
218
+ 4. 提示单选目标 Claw 目录(OpenClaw / LightClaw),一次只安装到一个 Claw
219
+ 5. 执行安装、注册,并提示用户重启
220
220
 
221
221
  ### 单 Agent 安装
222
222
 
@@ -224,7 +224,7 @@ soulhub rollback --id <record-id>
224
224
  - 安装为 **Main Agent** 时,部署到 `workspace/` 目录,会覆盖已有人格文件
225
225
  - 安装前自动备份已有内容到 `~/.soulhub/backups/<claw>/`(按 claw 类型分目录存储)
226
226
  - 仅覆盖 `IDENTITY.md`、`SOUL.md` 等灵魂文件,不影响 workspace 中的其他运行时文件
227
- - 安装完成后自动重启 OpenClaw/LightClaw Gateway;若重启失败会提示手动重启
227
+ - 安装完成后提示用户重启 OpenClaw/LightClaw Gateway
228
228
 
229
229
  ### 多 Agent 团队安装
230
230
 
@@ -233,7 +233,7 @@ soulhub rollback --id <record-id>
233
233
  - 安装前自动备份存量子 Agent(mv 方式移走已有 worker 目录)
234
234
  - 自动配置多 Agent 之间的通信
235
235
  - Worker Agent 自动注册到 claw 配置中
236
- - 安装完成后自动重启 OpenClaw/LightClaw Gateway
236
+ - 安装完成后提示用户重启 OpenClaw/LightClaw Gateway
237
237
 
238
238
  ### 备份与回滚
239
239
 
@@ -264,7 +264,7 @@ CLI 按以下优先级查找 claw 安装目录:
264
264
  3. `OPENCLAW_HOME` / `LIGHTCLAW_HOME` 环境变量
265
265
  4. 默认路径 `~/.openclaw`、`~/.lightclaw`
266
266
 
267
- 未指定 `--claw-type` 或 `--dir` 时,CLI 会检测所有可用的 claw 目录,多个时交互式多选。
267
+ 未指定 `--claw-type` 或 `--dir` 时,CLI 会检测所有可用的 Claw 目录,多个时交互式单选(一次只安装到一个 Claw)。
268
268
 
269
269
  ## 环境要求
270
270
 
package/dist/index.cjs CHANGED
@@ -13464,7 +13464,6 @@ var isDownKey = (key, keybindings = []) => (
13464
13464
  keybindings.includes("vim") && key.name === "j" || // Emacs keybinding: Ctrl+N means "next" in Emacs navigation conventions
13465
13465
  keybindings.includes("emacs") && key.ctrl && key.name === "n"
13466
13466
  );
13467
- var isSpaceKey = (key) => key.name === "space";
13468
13467
  var isBackspaceKey = (key) => key.name === "backspace";
13469
13468
  var isTabKey = (key) => key.name === "tab";
13470
13469
  var isNumberKey = (key) => "1234567890".includes(key.name);
@@ -14941,196 +14940,6 @@ var Separator = class {
14941
14940
  }
14942
14941
  };
14943
14942
 
14944
- // node_modules/@inquirer/checkbox/dist/index.js
14945
- var import_node_util4 = require("util");
14946
- var checkboxTheme = {
14947
- icon: {
14948
- checked: (0, import_node_util4.styleText)("green", dist_default.circleFilled),
14949
- unchecked: dist_default.circle,
14950
- cursor: dist_default.pointer,
14951
- disabledChecked: (0, import_node_util4.styleText)("green", dist_default.circleDouble),
14952
- disabledUnchecked: "-"
14953
- },
14954
- style: {
14955
- disabled: (text) => (0, import_node_util4.styleText)("dim", text),
14956
- renderSelectedChoices: (selectedChoices) => selectedChoices.map((choice) => choice.short).join(", "),
14957
- description: (text) => (0, import_node_util4.styleText)("cyan", text),
14958
- keysHelpTip: (keys) => keys.map(([key, action]) => `${(0, import_node_util4.styleText)("bold", key)} ${(0, import_node_util4.styleText)("dim", action)}`).join((0, import_node_util4.styleText)("dim", " \u2022 "))
14959
- },
14960
- i18n: { disabledError: "This option is disabled and cannot be toggled." },
14961
- keybindings: []
14962
- };
14963
- function isSelectable(item) {
14964
- return !Separator.isSeparator(item) && !item.disabled;
14965
- }
14966
- function isNavigable(item) {
14967
- return !Separator.isSeparator(item);
14968
- }
14969
- function isChecked(item) {
14970
- return !Separator.isSeparator(item) && item.checked;
14971
- }
14972
- function toggle(item) {
14973
- return isSelectable(item) ? { ...item, checked: !item.checked } : item;
14974
- }
14975
- function check(checked) {
14976
- return function(item) {
14977
- return isSelectable(item) ? { ...item, checked } : item;
14978
- };
14979
- }
14980
- function normalizeChoices(choices) {
14981
- return choices.map((choice) => {
14982
- if (Separator.isSeparator(choice))
14983
- return choice;
14984
- if (typeof choice === "string") {
14985
- return {
14986
- value: choice,
14987
- name: choice,
14988
- short: choice,
14989
- checkedName: choice,
14990
- disabled: false,
14991
- checked: false
14992
- };
14993
- }
14994
- const name = choice.name ?? String(choice.value);
14995
- const normalizedChoice = {
14996
- value: choice.value,
14997
- name,
14998
- short: choice.short ?? name,
14999
- checkedName: choice.checkedName ?? name,
15000
- disabled: choice.disabled ?? false,
15001
- checked: choice.checked ?? false
15002
- };
15003
- if (choice.description) {
15004
- normalizedChoice.description = choice.description;
15005
- }
15006
- return normalizedChoice;
15007
- });
15008
- }
15009
- var dist_default4 = createPrompt((config, done) => {
15010
- const { pageSize = 7, loop = true, required, validate = () => true } = config;
15011
- const shortcuts = { all: "a", invert: "i", ...config.shortcuts };
15012
- const theme = makeTheme(checkboxTheme, config.theme);
15013
- const { keybindings } = theme;
15014
- const [status, setStatus] = useState("idle");
15015
- const prefix = usePrefix({ status, theme });
15016
- const [items, setItems] = useState(normalizeChoices(config.choices));
15017
- const bounds = useMemo(() => {
15018
- const first = items.findIndex(isNavigable);
15019
- const last = items.findLastIndex(isNavigable);
15020
- if (first === -1) {
15021
- throw new ValidationError("[checkbox prompt] No selectable choices. All choices are disabled.");
15022
- }
15023
- return { first, last };
15024
- }, [items]);
15025
- const [active, setActive] = useState(bounds.first);
15026
- const [errorMsg, setError] = useState();
15027
- useKeypress(async (key) => {
15028
- if (isEnterKey(key)) {
15029
- const selection = items.filter(isChecked);
15030
- const isValid = await validate([...selection]);
15031
- if (required && !selection.length) {
15032
- setError("At least one choice must be selected");
15033
- } else if (isValid === true) {
15034
- setStatus("done");
15035
- done(selection.map((choice) => choice.value));
15036
- } else {
15037
- setError(isValid || "You must select a valid value");
15038
- }
15039
- } else if (isUpKey(key, keybindings) || isDownKey(key, keybindings)) {
15040
- if (errorMsg) {
15041
- setError(void 0);
15042
- }
15043
- if (loop || isUpKey(key, keybindings) && active !== bounds.first || isDownKey(key, keybindings) && active !== bounds.last) {
15044
- const offset = isUpKey(key, keybindings) ? -1 : 1;
15045
- let next = active;
15046
- do {
15047
- next = (next + offset + items.length) % items.length;
15048
- } while (!isNavigable(items[next]));
15049
- setActive(next);
15050
- }
15051
- } else if (isSpaceKey(key)) {
15052
- const activeItem = items[active];
15053
- if (activeItem && !Separator.isSeparator(activeItem)) {
15054
- if (activeItem.disabled) {
15055
- setError(theme.i18n.disabledError);
15056
- } else {
15057
- setError(void 0);
15058
- setItems(items.map((choice, i) => i === active ? toggle(choice) : choice));
15059
- }
15060
- }
15061
- } else if (key.name === shortcuts.all) {
15062
- const selectAll = items.some((choice) => isSelectable(choice) && !choice.checked);
15063
- setItems(items.map(check(selectAll)));
15064
- } else if (key.name === shortcuts.invert) {
15065
- setItems(items.map(toggle));
15066
- } else if (isNumberKey(key)) {
15067
- const selectedIndex = Number(key.name) - 1;
15068
- let selectableIndex = -1;
15069
- const position = items.findIndex((item) => {
15070
- if (Separator.isSeparator(item))
15071
- return false;
15072
- selectableIndex++;
15073
- return selectableIndex === selectedIndex;
15074
- });
15075
- const selectedItem = items[position];
15076
- if (selectedItem && isSelectable(selectedItem)) {
15077
- setActive(position);
15078
- setItems(items.map((choice, i) => i === position ? toggle(choice) : choice));
15079
- }
15080
- }
15081
- });
15082
- const message = theme.style.message(config.message, status);
15083
- let description;
15084
- const page = usePagination({
15085
- items,
15086
- active,
15087
- renderItem({ item, isActive }) {
15088
- if (Separator.isSeparator(item)) {
15089
- return ` ${item.separator}`;
15090
- }
15091
- const cursor = isActive ? theme.icon.cursor : " ";
15092
- if (item.disabled) {
15093
- const disabledLabel = typeof item.disabled === "string" ? item.disabled : "(disabled)";
15094
- const checkbox2 = item.checked ? theme.icon.disabledChecked : theme.icon.disabledUnchecked;
15095
- return theme.style.disabled(`${cursor}${checkbox2} ${item.name} ${disabledLabel}`);
15096
- }
15097
- if (isActive) {
15098
- description = item.description;
15099
- }
15100
- const checkbox = item.checked ? theme.icon.checked : theme.icon.unchecked;
15101
- const name = item.checked ? item.checkedName : item.name;
15102
- const color = isActive ? theme.style.highlight : (x) => x;
15103
- return color(`${cursor}${checkbox} ${name}`);
15104
- },
15105
- pageSize,
15106
- loop
15107
- });
15108
- if (status === "done") {
15109
- const selection = items.filter(isChecked);
15110
- const answer = theme.style.answer(theme.style.renderSelectedChoices(selection, items));
15111
- return [prefix, message, answer].filter(Boolean).join(" ");
15112
- }
15113
- const keys = [
15114
- ["\u2191\u2193", "navigate"],
15115
- ["space", "select"]
15116
- ];
15117
- if (shortcuts.all)
15118
- keys.push([shortcuts.all, "all"]);
15119
- if (shortcuts.invert)
15120
- keys.push([shortcuts.invert, "invert"]);
15121
- keys.push(["\u23CE", "submit"]);
15122
- const helpLine = theme.style.keysHelpTip(keys);
15123
- const lines = [
15124
- [prefix, message].filter(Boolean).join(" "),
15125
- page,
15126
- " ",
15127
- description ? theme.style.description(description) : "",
15128
- errorMsg ? theme.style.error(errorMsg) : "",
15129
- helpLine
15130
- ].filter(Boolean).join("\n").trimEnd();
15131
- return `${lines}${cursorHide}`;
15132
- });
15133
-
15134
14943
  // node_modules/@inquirer/confirm/dist/index.js
15135
14944
  function getBooleanValue(value, defaultValue) {
15136
14945
  let answer = defaultValue !== false;
@@ -15143,7 +14952,7 @@ function getBooleanValue(value, defaultValue) {
15143
14952
  function boolToString(value) {
15144
14953
  return value ? "Yes" : "No";
15145
14954
  }
15146
- var dist_default5 = createPrompt((config, done) => {
14955
+ var dist_default4 = createPrompt((config, done) => {
15147
14956
  const { transformer = boolToString } = config;
15148
14957
  const [status, setStatus] = useState("idle");
15149
14958
  const [value, setValue] = useState("");
@@ -15178,25 +14987,25 @@ var dist_default5 = createPrompt((config, done) => {
15178
14987
  });
15179
14988
 
15180
14989
  // node_modules/@inquirer/select/dist/index.js
15181
- var import_node_util5 = require("util");
14990
+ var import_node_util4 = require("util");
15182
14991
  var selectTheme = {
15183
14992
  icon: { cursor: dist_default.pointer },
15184
14993
  style: {
15185
- disabled: (text) => (0, import_node_util5.styleText)("dim", text),
15186
- description: (text) => (0, import_node_util5.styleText)("cyan", text),
15187
- keysHelpTip: (keys) => keys.map(([key, action]) => `${(0, import_node_util5.styleText)("bold", key)} ${(0, import_node_util5.styleText)("dim", action)}`).join((0, import_node_util5.styleText)("dim", " \u2022 "))
14994
+ disabled: (text) => (0, import_node_util4.styleText)("dim", text),
14995
+ description: (text) => (0, import_node_util4.styleText)("cyan", text),
14996
+ keysHelpTip: (keys) => keys.map(([key, action]) => `${(0, import_node_util4.styleText)("bold", key)} ${(0, import_node_util4.styleText)("dim", action)}`).join((0, import_node_util4.styleText)("dim", " \u2022 "))
15188
14997
  },
15189
14998
  i18n: { disabledError: "This option is disabled and cannot be selected." },
15190
14999
  indexMode: "hidden",
15191
15000
  keybindings: []
15192
15001
  };
15193
- function isSelectable2(item) {
15002
+ function isSelectable(item) {
15194
15003
  return !Separator.isSeparator(item) && !item.disabled;
15195
15004
  }
15196
- function isNavigable2(item) {
15005
+ function isNavigable(item) {
15197
15006
  return !Separator.isSeparator(item);
15198
15007
  }
15199
- function normalizeChoices2(choices) {
15008
+ function normalizeChoices(choices) {
15200
15009
  return choices.map((choice) => {
15201
15010
  if (Separator.isSeparator(choice))
15202
15011
  return choice;
@@ -15222,7 +15031,7 @@ function normalizeChoices2(choices) {
15222
15031
  return normalizedChoice;
15223
15032
  });
15224
15033
  }
15225
- var dist_default6 = createPrompt((config, done) => {
15034
+ var dist_default5 = createPrompt((config, done) => {
15226
15035
  const { loop = true, pageSize = 7 } = config;
15227
15036
  const theme = makeTheme(selectTheme, config.theme);
15228
15037
  const { keybindings } = theme;
@@ -15230,10 +15039,10 @@ var dist_default6 = createPrompt((config, done) => {
15230
15039
  const prefix = usePrefix({ status, theme });
15231
15040
  const searchTimeoutRef = useRef();
15232
15041
  const searchEnabled = !keybindings.includes("vim");
15233
- const items = useMemo(() => normalizeChoices2(config.choices), [config.choices]);
15042
+ const items = useMemo(() => normalizeChoices(config.choices), [config.choices]);
15234
15043
  const bounds = useMemo(() => {
15235
- const first = items.findIndex(isNavigable2);
15236
- const last = items.findLastIndex(isNavigable2);
15044
+ const first = items.findIndex(isNavigable);
15045
+ const last = items.findLastIndex(isNavigable);
15237
15046
  if (first === -1) {
15238
15047
  throw new ValidationError("[select prompt] No selectable choices. All choices are disabled.");
15239
15048
  }
@@ -15242,7 +15051,7 @@ var dist_default6 = createPrompt((config, done) => {
15242
15051
  const defaultItemIndex = useMemo(() => {
15243
15052
  if (!("default" in config))
15244
15053
  return -1;
15245
- return items.findIndex((item) => isSelectable2(item) && item.value === config.default);
15054
+ return items.findIndex((item) => isSelectable(item) && item.value === config.default);
15246
15055
  }, [config.default, items]);
15247
15056
  const [active, setActive] = useState(defaultItemIndex === -1 ? bounds.first : defaultItemIndex);
15248
15057
  const selectedChoice = items[active];
@@ -15266,7 +15075,7 @@ var dist_default6 = createPrompt((config, done) => {
15266
15075
  let next = active;
15267
15076
  do {
15268
15077
  next = (next + offset + items.length) % items.length;
15269
- } while (!isNavigable2(items[next]));
15078
+ } while (!isNavigable(items[next]));
15270
15079
  setActive(next);
15271
15080
  }
15272
15081
  } else if (isNumberKey(key) && !Number.isNaN(Number(rl.line))) {
@@ -15279,7 +15088,7 @@ var dist_default6 = createPrompt((config, done) => {
15279
15088
  return selectableIndex === selectedIndex;
15280
15089
  });
15281
15090
  const item = items[position];
15282
- if (item != null && isSelectable2(item)) {
15091
+ if (item != null && isSelectable(item)) {
15283
15092
  setActive(position);
15284
15093
  }
15285
15094
  searchTimeoutRef.current = setTimeout(() => {
@@ -15290,7 +15099,7 @@ var dist_default6 = createPrompt((config, done) => {
15290
15099
  } else if (searchEnabled) {
15291
15100
  const searchTerm = rl.line.toLowerCase();
15292
15101
  const matchIndex = items.findIndex((item) => {
15293
- if (Separator.isSeparator(item) || !isSelectable2(item))
15102
+ if (Separator.isSeparator(item) || !isSelectable(item))
15294
15103
  return false;
15295
15104
  return item.name.toLowerCase().startsWith(searchTerm);
15296
15105
  });
@@ -21157,7 +20966,7 @@ async function promptSelectClawDir(customDir) {
21157
20966
  return dirs[0];
21158
20967
  }
21159
20968
  try {
21160
- const selected = await dist_default6({
20969
+ const selected = await dist_default5({
21161
20970
  message: "Select target Claw installation:",
21162
20971
  choices: dirs.map((dir) => {
21163
20972
  const brand = detectClawBrand(dir);
@@ -21487,7 +21296,7 @@ function commitBackupRecord(record) {
21487
21296
  }
21488
21297
  async function promptSelectRole() {
21489
21298
  try {
21490
- const selected = await dist_default6({
21299
+ const selected = await dist_default5({
21491
21300
  message: "Install as:",
21492
21301
  choices: [
21493
21302
  { name: "\u{1F477} Worker agent (\u5B50Agent\uFF0C\u5B89\u88C5\u5230 workspace-<name>/ \u76EE\u5F55)", value: "worker" },
@@ -21512,24 +21321,21 @@ async function promptMultiSelectClawDirs() {
21512
21321
  return dirs;
21513
21322
  }
21514
21323
  try {
21515
- const selected = await dist_default4({
21516
- message: "Select target Claw installations (space to toggle, enter to confirm):",
21324
+ const selected = await dist_default5({
21325
+ message: "Select target Claw installation:",
21517
21326
  choices: dirs.map((dir) => {
21518
21327
  const brand = detectClawBrand(dir);
21519
- return { name: `${brand} ${dir}`, value: dir, checked: true };
21328
+ return { name: `${brand} ${dir}`, value: dir };
21520
21329
  })
21521
21330
  });
21522
- if (selected.length === 0) {
21523
- console.log(" No claw selected, operation cancelled.");
21524
- }
21525
- return selected;
21331
+ return [selected];
21526
21332
  } catch {
21527
21333
  return [];
21528
21334
  }
21529
21335
  }
21530
21336
  async function promptConfirm(message, defaultYes = true) {
21531
21337
  try {
21532
- return await dist_default5({
21338
+ return await dist_default4({
21533
21339
  message,
21534
21340
  default: defaultYes
21535
21341
  });
@@ -21619,28 +21425,6 @@ function detectClawCommand(clawDir) {
21619
21425
  }
21620
21426
  return "openclaw";
21621
21427
  }
21622
- function restartOpenClawGateway(clawDir) {
21623
- const clawCmd = detectClawCommand(clawDir);
21624
- logger.debug(`Restarting ${clawCmd} Gateway`);
21625
- try {
21626
- (0, import_node_child_process.execSync)(`${clawCmd} gateway restart`, {
21627
- stdio: "pipe",
21628
- timeout: 3e4
21629
- // 30 秒超时
21630
- });
21631
- return {
21632
- success: true,
21633
- message: `${clawCmd} Gateway restarted successfully.`
21634
- };
21635
- } catch (error) {
21636
- const stderr = error && typeof error === "object" && "stderr" in error ? String(error.stderr).trim() : "";
21637
- const errMsg = stderr || (error instanceof Error ? error.message : String(error));
21638
- return {
21639
- success: false,
21640
- message: errMsg
21641
- };
21642
- }
21643
- }
21644
21428
 
21645
21429
  // src/commands/search.ts
21646
21430
  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("-n, --limit <number>", "Max results to show", "20").option("--json", "Output results in JSON format").action(async (query, options) => {
@@ -21913,7 +21697,8 @@ function resolveAllClawDirs(clawDir) {
21913
21697
  const resolved = findOpenClawDir(clawDir);
21914
21698
  return resolved ? [resolved] : [];
21915
21699
  }
21916
- return findAllClawDirs();
21700
+ const all = findAllClawDirs();
21701
+ return all.length > 0 ? [all[0]] : [];
21917
21702
  }
21918
21703
  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("-r, --role <role>", "Install role: main or worker (skip role selection prompt)").option(
21919
21704
  "--dir <path>",
@@ -22058,14 +21843,13 @@ async function installSingleAgent(name, targetDir, clawDir, asMain = false, preR
22058
21843
  spinner.text = `Downloading ${source_default.cyan(agent.displayName)} package...`;
22059
21844
  const pkgDir = await downloadAgentPackage(name, agent.version);
22060
21845
  spinner.succeed(`Package ${source_default.cyan(agent.displayName)} downloaded.`);
22061
- const showClawHeader = allClawDirs.length > 1;
22062
- for (const selectedClawDir of allClawDirs) {
22063
- if (showClawHeader) {
22064
- const brand = detectClawBrand(selectedClawDir);
22065
- console.log();
22066
- console.log(source_default.bold(`\u2500\u2500 ${brand} (${selectedClawDir}) \u2500\u2500`));
22067
- }
21846
+ const selectedClawDir = allClawDirs[0];
21847
+ try {
22068
21848
  await installSingleAgentToClaw(name, selectedClawDir, void 0, asMain, pkgDir, agent);
21849
+ } catch (err) {
21850
+ const errMsg = err instanceof Error ? err.message : String(err);
21851
+ logger.error(`Failed to install to ${selectedClawDir}`, { error: errMsg });
21852
+ console.error(source_default.red(` \u2717 Installation failed for ${selectedClawDir}: ${errMsg}`));
22069
21853
  }
22070
21854
  import_node_fs9.default.rmSync(pkgDir, { recursive: true, force: true });
22071
21855
  }
@@ -22234,23 +22018,29 @@ async function installRecipeFromRegistry(name, recipe, targetDir, clawDir) {
22234
22018
  spinner.text = `Installing worker ${source_default.cyan(worker.name)}...`;
22235
22019
  const agentName = worker.dir || worker.name;
22236
22020
  const agentId = worker.name;
22237
- const workerDir = targetDir ? import_node_path12.default.join(resolvedClawDir, `workspace-${agentId}`) : getWorkspaceDir(resolvedClawDir, agentId);
22238
- if (!targetDir) {
22239
- const regResult = registerAgentToOpenClaw(agentId, workerDir, resolvedClawDir);
22240
- if (!regResult.success) {
22241
- console.log(source_default.yellow(` \u26A0 Failed to register ${agentId}: ${regResult.message}`));
22242
- continue;
22243
- }
22244
- } else {
22245
- import_node_fs9.default.mkdirSync(workerDir, { recursive: true });
22021
+ try {
22022
+ const workerDir = targetDir ? import_node_path12.default.join(resolvedClawDir, `workspace-${agentId}`) : getWorkspaceDir(resolvedClawDir, agentId);
22023
+ if (!targetDir) {
22024
+ const regResult = registerAgentToOpenClaw(agentId, workerDir, resolvedClawDir);
22025
+ if (!regResult.success) {
22026
+ console.log(source_default.yellow(` \u26A0 Failed to register ${agentId}: ${regResult.message}`));
22027
+ continue;
22028
+ }
22029
+ } else {
22030
+ import_node_fs9.default.mkdirSync(workerDir, { recursive: true });
22031
+ }
22032
+ const agentInfo = index.agents.find((a) => a.name === agentName);
22033
+ const agentVersion = agentInfo?.version || "latest";
22034
+ const pkgDir = await downloadAgentPackage(agentName, agentVersion);
22035
+ copyAgentFilesFromPackage(pkgDir, workerDir);
22036
+ import_node_fs9.default.rmSync(pkgDir, { recursive: true, force: true });
22037
+ recordInstall(agentId, recipe.version || "1.0.0", workerDir);
22038
+ workerIds.push(agentId);
22039
+ } catch (err) {
22040
+ const errMsg = err instanceof Error ? err.message : String(err);
22041
+ logger.error(`Failed to install worker ${agentId}`, { error: errMsg });
22042
+ console.log(source_default.red(` \u2717 Failed to install worker ${agentId}: ${errMsg}`));
22246
22043
  }
22247
- const agentInfo = index.agents.find((a) => a.name === agentName);
22248
- const agentVersion = agentInfo?.version || "latest";
22249
- const pkgDir = await downloadAgentPackage(agentName, agentVersion);
22250
- copyAgentFilesFromPackage(pkgDir, workerDir);
22251
- import_node_fs9.default.rmSync(pkgDir, { recursive: true, force: true });
22252
- recordInstall(agentId, recipe.version || "1.0.0", workerDir);
22253
- workerIds.push(agentId);
22254
22044
  }
22255
22045
  if (!targetDir) {
22256
22046
  spinner.text = "Configuring multi-agent communication...";
@@ -22367,14 +22157,13 @@ async function installSingleAgentFromDir(packageDir, targetDir, clawDir, asMain
22367
22157
  printOpenClawInstallHelp();
22368
22158
  return;
22369
22159
  }
22370
- const showClawHeader = allClawDirs.length > 1;
22371
- for (const selectedClawDir of allClawDirs) {
22372
- if (showClawHeader) {
22373
- const brand = detectClawBrand(selectedClawDir);
22374
- console.log();
22375
- console.log(source_default.bold(`\u2500\u2500 ${brand} (${selectedClawDir}) \u2500\u2500`));
22376
- }
22160
+ const selectedClawDir = allClawDirs[0];
22161
+ try {
22377
22162
  await installSingleAgentFromDirToClaw(packageDir, agentName, pkg, selectedClawDir, void 0, asMain);
22163
+ } catch (err) {
22164
+ const errMsg = err instanceof Error ? err.message : String(err);
22165
+ logger.error(`Failed to install to ${selectedClawDir}`, { error: errMsg });
22166
+ console.error(source_default.red(` \u2717 Installation failed for ${selectedClawDir}: ${errMsg}`));
22378
22167
  }
22379
22168
  }
22380
22169
  async function installSingleAgentFromDirToClaw(packageDir, agentName, pkg, selectedClawDir, targetDir, asMain = false) {
@@ -22542,22 +22331,28 @@ async function installTeamFromDir(packageDir, targetDir, clawDir) {
22542
22331
  const agentId = worker.name;
22543
22332
  const agentDir = worker.dir || worker.name;
22544
22333
  spinner.text = `Installing worker ${source_default.cyan(agentId)}...`;
22545
- const workerWorkspace = targetDir ? import_node_path12.default.join(resolvedClawDir, `workspace-${agentId}`) : getWorkspaceDir(resolvedClawDir, agentId);
22546
- if (!targetDir) {
22547
- const regResult = registerAgentToOpenClaw(agentId, workerWorkspace, resolvedClawDir);
22548
- if (!regResult.success) {
22549
- console.log(source_default.yellow(` \u26A0 Failed to register ${agentId}: ${regResult.message}`));
22550
- continue;
22334
+ try {
22335
+ const workerWorkspace = targetDir ? import_node_path12.default.join(resolvedClawDir, `workspace-${agentId}`) : getWorkspaceDir(resolvedClawDir, agentId);
22336
+ if (!targetDir) {
22337
+ const regResult = registerAgentToOpenClaw(agentId, workerWorkspace, resolvedClawDir);
22338
+ if (!regResult.success) {
22339
+ console.log(source_default.yellow(` \u26A0 Failed to register ${agentId}: ${regResult.message}`));
22340
+ continue;
22341
+ }
22342
+ } else {
22343
+ import_node_fs9.default.mkdirSync(workerWorkspace, { recursive: true });
22551
22344
  }
22552
- } else {
22553
- import_node_fs9.default.mkdirSync(workerWorkspace, { recursive: true });
22554
- }
22555
- const workerSourceDir = import_node_path12.default.join(packageDir, agentDir);
22556
- if (import_node_fs9.default.existsSync(workerSourceDir)) {
22557
- copyAgentFilesFromDir(workerSourceDir, workerWorkspace);
22345
+ const workerSourceDir = import_node_path12.default.join(packageDir, agentDir);
22346
+ if (import_node_fs9.default.existsSync(workerSourceDir)) {
22347
+ copyAgentFilesFromDir(workerSourceDir, workerWorkspace);
22348
+ }
22349
+ recordInstall(agentId, pkg.version || "local", workerWorkspace);
22350
+ workerIds.push(agentId);
22351
+ } catch (err) {
22352
+ const errMsg = err instanceof Error ? err.message : String(err);
22353
+ logger.error(`Failed to install worker ${agentId}`, { error: errMsg });
22354
+ console.log(source_default.red(` \u2717 Failed to install worker ${agentId}: ${errMsg}`));
22558
22355
  }
22559
- recordInstall(agentId, pkg.version || "local", workerWorkspace);
22560
- workerIds.push(agentId);
22561
22356
  }
22562
22357
  if (!targetDir && workerIds.length > 0) {
22563
22358
  spinner.text = "Configuring multi-agent communication...";
@@ -22665,13 +22460,9 @@ function printTeamSummary(pkg, workerIds) {
22665
22460
  async function tryRestartGateway(clawDir) {
22666
22461
  const clawCmd = detectClawCommand(clawDir);
22667
22462
  const brandName = clawCmd === "lightclaw" ? "LightClaw" : "OpenClaw";
22668
- const restartSpinner = createSpinner(`Restarting ${brandName} Gateway...`).start();
22669
- const result = restartOpenClawGateway(clawDir);
22670
- if (result.success) {
22671
- restartSpinner.succeed(`${brandName} Gateway restarted successfully.`);
22672
- } else {
22673
- restartSpinner.warn(`Failed to restart ${brandName} Gateway. Please restart it manually.`);
22674
- }
22463
+ console.log();
22464
+ console.log(source_default.yellow(` \u26A0 Please restart ${brandName} Gateway to apply changes:`));
22465
+ console.log(source_default.cyan(` ${clawCmd} gateway restart`));
22675
22466
  }
22676
22467
 
22677
22468
  // src/commands/list.ts
@@ -22956,7 +22747,7 @@ async function interactiveRollback(clawDir) {
22956
22747
  console.log();
22957
22748
  let selected;
22958
22749
  try {
22959
- selected = await dist_default6({
22750
+ selected = await dist_default5({
22960
22751
  message: "Select a record to rollback:",
22961
22752
  choices: manifest.records.map((record, index) => {
22962
22753
  const date = new Date(record.createdAt).toLocaleString();
@@ -23003,7 +22794,7 @@ async function performRollbackByIndex(n, clawDir, skipConfirm = false) {
23003
22794
  if (!skipConfirm) {
23004
22795
  let confirmed;
23005
22796
  try {
23006
- confirmed = await dist_default5({
22797
+ confirmed = await dist_default4({
23007
22798
  message: `${source_default.yellow("\u26A0")} Proceed with rollback?`,
23008
22799
  default: true
23009
22800
  });
@@ -23073,7 +22864,7 @@ function printRollbackDetails(record) {
23073
22864
  }
23074
22865
  async function promptConfirmRollback() {
23075
22866
  try {
23076
- return await dist_default5({
22867
+ return await dist_default4({
23077
22868
  message: `${source_default.yellow("\u26A0")} Proceed with rollback?`,
23078
22869
  default: true
23079
22870
  });
@@ -23170,16 +22961,9 @@ async function executeRollback(record, clawDir) {
23170
22961
  );
23171
22962
  const clawCmd = detectClawCommand(resolvedClawDir);
23172
22963
  const brandName = clawCmd === "lightclaw" ? "LightClaw" : "OpenClaw";
23173
- const restartSpinner = createSpinner(`Restarting ${brandName} Gateway...`).start();
23174
- const result = restartOpenClawGateway(resolvedClawDir);
23175
- if (result.success) {
23176
- restartSpinner.succeed(`${brandName} Gateway restarted successfully.`);
23177
- } else {
23178
- restartSpinner.warn(`Failed to restart ${brandName} Gateway.`);
23179
- console.log(source_default.yellow(` Reason: ${result.message}`));
23180
- console.log(source_default.dim(" Please restart manually:"));
23181
- console.log(source_default.dim(` ${clawCmd} gateway restart`));
23182
- }
22964
+ console.log();
22965
+ console.log(source_default.yellow(` \u26A0 Please restart ${brandName} Gateway to apply changes:`));
22966
+ console.log(source_default.cyan(` ${clawCmd} gateway restart`));
23183
22967
  console.log();
23184
22968
  }
23185
22969
  function formatInstallType(type2) {
@@ -23206,13 +22990,13 @@ function detectClawBrandFromDir(clawDir) {
23206
22990
 
23207
22991
  // src/index.ts
23208
22992
  var program2 = new Command();
23209
- program2.name("soulhub").description("SoulHub CLI - Discover, install and manage AI agent souls").version("1.0.23").option("--verbose", "Enable verbose debug logging").hook("preAction", () => {
22993
+ program2.name("soulhub").description("SoulHub CLI - Discover, install and manage AI agent souls").version("1.0.24").option("--verbose", "Enable verbose debug logging").hook("preAction", () => {
23210
22994
  const opts = program2.opts();
23211
22995
  const verbose = opts.verbose || process.env.SOULHUB_DEBUG === "1";
23212
22996
  logger.init(verbose);
23213
22997
  logger.info("CLI started", {
23214
22998
  args: process.argv.slice(2),
23215
- version: "1.0.23",
22999
+ version: "1.0.24",
23216
23000
  node: process.version
23217
23001
  });
23218
23002
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "soulhubcli",
3
- "version": "1.0.23",
3
+ "version": "1.0.24",
4
4
  "description": "SoulHub CLI - Install and manage AI agent persona templates for OpenClaw",
5
5
  "type": "module",
6
6
  "bin": {