Skip to content

Commit d763b83

Browse files
authored
fix: explain disabled plugin command aliases
Preserve enabled-by-default metadata for manifest command aliases so missing CLI command diagnostics can point users at the parent bundled plugin and the `openclaw plugins enable <plugin>` repair path. Also carries the current-main deadcode allowlist entry for the command-analysis barrel that blocked CI. Verified: - pnpm test src/cli/run-main.test.ts src/plugins/manifest-command-aliases.test.ts - pnpm deadcode:unused-files - pnpm exec oxfmt --check --threads=1 scripts/deadcode-unused-files.allowlist.mjs CHANGELOG.md src/cli/run-main-policy.ts src/cli/run-main.test.ts src/plugins/manifest-command-aliases.ts src/plugins/manifest-command-aliases.test.ts - git diff --check - PR CI on 6076ff2 green, ignoring cancelled auto-response per landing matrix
1 parent 7857dfa commit d763b83

6 files changed

Lines changed: 71 additions & 1 deletion

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ Docs: https://docs.openclaw.ai
3030

3131
- Agents/tools: stop treating `tools.deny: ["write"]` as an implicit `apply_patch` deny; operators who want to block patch writes should deny `apply_patch` or `group:fs` explicitly. Fixes #76749. (#76795) Thanks @Nek-12 and @hclsys.
3232
- Gateway/update: recover an installed-but-unloaded macOS LaunchAgent after package updates, rerun Gateway health/version/channel readiness checks, and print restart, reinstall, and rollback guidance before reporting update failure. (#76790) Thanks @jonathanlindsay.
33+
- CLI/plugins: explain when a missing plugin command alias belongs to a bundled plugin that is disabled by default, including the `openclaw plugins enable <plugin>` repair command. (#76835)
3334
- Google Meet: route stateful CLI session commands through the gateway-owned runtime so joined realtime sessions survive after the starting CLI process exits. Fixes #76344. Thanks @coltonharris-wq.
3435
- Memory/status: split builtin sqlite-vec store readiness from embedding-provider readiness in `memory status --deep` and `openclaw status`, so local vector-store failures no longer look like provider failures and provider failures no longer hide a healthy local vector store.
3536
- CLI/doctor: trust a ready gateway memory probe when CLI-side active memory backend resolution is unavailable, preventing false "No active memory plugin is registered" warnings for healthy runtime setups. Fixes #76792. Thanks @som-686.

scripts/deadcode-unused-files.allowlist.mjs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ export const KNIP_UNUSED_FILE_ALLOWLIST = [
1616
"src/gateway/gateway-cli-backend.live-probe-helpers.ts",
1717
"src/gateway/gateway-codex-harness.live-helpers.ts",
1818
"src/infra/changelog-unreleased.ts",
19+
"src/infra/command-analysis/index.ts",
1920
"src/mcp/openclaw-tools-serve.ts",
2021
"src/mcp/plugin-tools-handlers.ts",
2122
"src/mcp/plugin-tools-serve.ts",

src/cli/run-main-policy.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,17 @@ export function resolveMissingPluginCommandMessage(
141141
"the bundled plugin command surface."
142142
);
143143
}
144+
if (
145+
commandAlias.kind !== "runtime-slash" &&
146+
commandAlias.enabledByDefault !== true &&
147+
config?.plugins?.entries?.[parentPluginId]?.enabled !== true
148+
) {
149+
return (
150+
`The \`openclaw ${normalizedPluginId}\` command is provided by the ` +
151+
`"${parentPluginId}" plugin, but that bundled plugin is disabled by default. Run ` +
152+
`\`openclaw plugins enable ${parentPluginId}\` to enable that CLI surface.`
153+
);
154+
}
144155
if (commandAlias.kind === "runtime-slash") {
145156
const cliHint = commandAlias.cliCommand
146157
? `Use \`openclaw ${commandAlias.cliCommand}\` for related CLI operations, or `

src/cli/run-main.test.ts

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ const memoryWikiCommandAliasRegistry: PluginManifestCommandAliasRegistry = {
1616
plugins: [
1717
{
1818
id: "memory-wiki",
19+
enabledByDefault: true,
1920
commandAliases: [{ name: "wiki" }],
2021
},
2122
],
@@ -271,6 +272,54 @@ describe("resolveMissingPluginCommandMessage", () => {
271272
expect(message).toContain("plugins.allow");
272273
});
273274

275+
it("explains disabled-by-default parent plugins for CLI command aliases", () => {
276+
const message = resolveMissingPluginCommandMessage(
277+
"voicecall",
278+
{},
279+
{
280+
registry: {
281+
plugins: [
282+
{
283+
id: "voice-call",
284+
commandAliases: [{ name: "voicecall" }],
285+
},
286+
],
287+
},
288+
},
289+
);
290+
291+
expect(message).toContain('"voice-call" plugin');
292+
expect(message).toContain("disabled by default");
293+
expect(message).toContain("openclaw plugins enable voice-call");
294+
});
295+
296+
it("returns null for CLI command aliases when disabled-by-default parent plugins are enabled", () => {
297+
const message = resolveMissingPluginCommandMessage(
298+
"voicecall",
299+
{
300+
plugins: {
301+
entries: {
302+
"voice-call": {
303+
enabled: true,
304+
},
305+
},
306+
},
307+
},
308+
{
309+
registry: {
310+
plugins: [
311+
{
312+
id: "voice-call",
313+
commandAliases: [{ name: "voicecall" }],
314+
},
315+
],
316+
},
317+
},
318+
);
319+
320+
expect(message).toBeNull();
321+
});
322+
274323
it("explains parent plugin disablement for runtime command aliases", () => {
275324
const message = resolveMissingPluginCommandMessage(
276325
"dreaming",

src/plugins/manifest-command-aliases.test.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ describe("manifest command aliases", () => {
2929
},
3030
{
3131
id: "memory",
32+
enabledByDefault: true,
3233
commandAliases: [{ name: "legacy-memory" }],
3334
},
3435
],
@@ -41,6 +42,7 @@ describe("manifest command aliases", () => {
4142
resolveManifestCommandAliasOwnerInRegistry({ command: "legacy-memory", registry }),
4243
).toMatchObject({
4344
pluginId: "memory",
45+
enabledByDefault: true,
4446
name: "legacy-memory",
4547
});
4648
});

src/plugins/manifest-command-aliases.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,13 @@ export type PluginManifestCommandAlias = {
1717

1818
export type PluginManifestCommandAliasRecord = PluginManifestCommandAlias & {
1919
pluginId: string;
20+
enabledByDefault?: boolean;
2021
};
2122

2223
export type PluginManifestCommandAliasRegistry = {
2324
plugins: readonly {
2425
id: string;
26+
enabledByDefault?: boolean;
2527
commandAliases?: readonly PluginManifestCommandAlias[];
2628
}[];
2729
};
@@ -81,7 +83,11 @@ export function resolveManifestCommandAliasOwnerInRegistry(params: {
8183
(entry) => normalizeOptionalLowercaseString(entry.name) === normalizedCommand,
8284
);
8385
if (alias) {
84-
return { ...alias, pluginId: plugin.id };
86+
return {
87+
...alias,
88+
pluginId: plugin.id,
89+
...(plugin.enabledByDefault === true ? { enabledByDefault: true } : {}),
90+
};
8591
}
8692
}
8793
return undefined;

0 commit comments

Comments
 (0)