carouselbot 0.3.1 → 0.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -1
- package/package.json +1 -1
- package/src/setup.mjs +39 -10
package/README.md
CHANGED
|
@@ -8,7 +8,9 @@ npx carouselbot@latest setup
|
|
|
8
8
|
|
|
9
9
|
Setup pins that exact package version in the generated MCP configuration, refreshes
|
|
10
10
|
the agent skill, and automatically upgrades an older shared daemon. Rerun the
|
|
11
|
-
command when you intentionally want to update.
|
|
11
|
+
command when you intentionally want to update. When setup runs inside a named
|
|
12
|
+
Hermes profile, it also installs the skill into that profile's active
|
|
13
|
+
HERMES_HOME and non-interactively enables the discovered CarouselBot tools.
|
|
12
14
|
|
|
13
15
|
For non-interactive agent setup, select the current client explicitly:
|
|
14
16
|
|
package/package.json
CHANGED
package/src/setup.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { spawnSync } from "node:child_process";
|
|
2
2
|
import { cp, mkdir } from "node:fs/promises";
|
|
3
3
|
import { homedir } from "node:os";
|
|
4
|
-
import { join } from "node:path";
|
|
4
|
+
import { join, resolve } from "node:path";
|
|
5
5
|
import { createInterface } from "node:readline/promises";
|
|
6
6
|
import { EDITOR_URL, PACKAGE_NAME, PACKAGE_ROOT, PACKAGE_VERSION } from "./config.mjs";
|
|
7
7
|
import { companionUpgrade } from "./companion.mjs";
|
|
@@ -44,13 +44,35 @@ function openCodeSnippet(specifier, version = commandVersion("opencode")) {
|
|
|
44
44
|
: { mcp: { [serverName]: { ...server, enabled: true } } }, null, 2);
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
-
|
|
48
|
-
const source = join(PACKAGE_ROOT, "skill", "carouselbot");
|
|
47
|
+
export function setupSkillTargets(homeDirectory = homedir(), hermesHome = process.env.HERMES_HOME) {
|
|
49
48
|
const targets = [
|
|
50
|
-
join(
|
|
51
|
-
join(
|
|
52
|
-
join(
|
|
49
|
+
join(homeDirectory, ".agents", "skills", "carouselbot"),
|
|
50
|
+
join(homeDirectory, ".claude", "skills", "carouselbot"),
|
|
51
|
+
join(homeDirectory, ".hermes", "skills", "carouselbot"),
|
|
53
52
|
];
|
|
53
|
+
if (hermesHome?.trim()) targets.push(join(resolve(hermesHome.trim()), "skills", "carouselbot"));
|
|
54
|
+
return [...new Set(targets)];
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export function clientSpawnOptions(client) {
|
|
58
|
+
if (client === "hermes") {
|
|
59
|
+
// Hermes tool discovery prompts even after the caller approved CarouselBot
|
|
60
|
+
// setup, and an existing entry adds an overwrite prompt first. Feed both
|
|
61
|
+
// approvals explicitly; EOF otherwise looks like a successful cancel.
|
|
62
|
+
return { input: "y\ny\n", stdio: ["pipe", "inherit", "inherit"] };
|
|
63
|
+
}
|
|
64
|
+
return { stdio: "inherit" };
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function clientConfigurationPresent(client) {
|
|
68
|
+
if (client !== "hermes") return true;
|
|
69
|
+
const result = spawnSync("hermes", ["mcp", "list"], { encoding: "utf8" });
|
|
70
|
+
return result.status === 0 && /(?:^|\s)carouselbot(?:\s|$)/m.test(String(result.stdout || "") + String(result.stderr || ""));
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
async function installSkill() {
|
|
74
|
+
const source = join(PACKAGE_ROOT, "skill", "carouselbot");
|
|
75
|
+
const targets = setupSkillTargets();
|
|
54
76
|
for (const target of targets) {
|
|
55
77
|
await mkdir(target, { recursive: true });
|
|
56
78
|
await cp(source, target, { recursive: true, force: true });
|
|
@@ -93,10 +115,17 @@ export async function runSetup(arguments_) {
|
|
|
93
115
|
for (const client of clients) {
|
|
94
116
|
const command = shellCommand(client, specifier);
|
|
95
117
|
if (!command) continue;
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
118
|
+
if (client !== "hermes") {
|
|
119
|
+
for (const remove of removeCommands(client)) spawnSync(remove[0], remove.slice(1), { stdio: "ignore" });
|
|
120
|
+
}
|
|
121
|
+
const result = spawnSync(command[0], command.slice(1), clientSpawnOptions(client));
|
|
122
|
+
if (result.status === 0 && clientConfigurationPresent(client)) {
|
|
123
|
+
configured.push(client);
|
|
124
|
+
if (client === "hermes") {
|
|
125
|
+
const legacyRemove = removeCommands(client).find((remove) => remove.at(-1) === legacyServerName);
|
|
126
|
+
if (legacyRemove) spawnSync(legacyRemove[0], legacyRemove.slice(1), { input: "y\n", stdio: ["pipe", "ignore", "ignore"] });
|
|
127
|
+
}
|
|
128
|
+
} else process.stderr.write(`Could not verify ${client}'s CarouselBot config; the existing config was preserved and its command is printed above for manual setup.\n`);
|
|
100
129
|
}
|
|
101
130
|
const skillTargets = await installSkill();
|
|
102
131
|
const companion = await companionUpgrade();
|