Skip to content

fix(cli): clarify error when unknown subcommand is actually an agent tool name (#77214)#77221

Open
100yenadmin wants to merge 1 commit intoopenclaw:mainfrom
electricsheephq:fix/77214-cli-tool-name-suggestion
Open

fix(cli): clarify error when unknown subcommand is actually an agent tool name (#77214)#77221
100yenadmin wants to merge 1 commit intoopenclaw:mainfrom
electricsheephq:fix/77214-cli-tool-name-suggestion

Conversation

@100yenadmin
Copy link
Copy Markdown
Contributor

Summary

  • When openclaw <name> does not match a CLI subcommand or a plugin id, the unknown-subcommand handler now first looks up <name> against the runtime plugin tool registry. If <name> is an agent tool registered by a loaded plugin (e.g. lcm_recent from lossless-claw), emit a clear "this is an agent tool, not a CLI subcommand" error pointing at model tool-use instead of the misleading plugins.allow suggestion.
  • The previous error told the user to add the tool name to plugins.allow, but plugins.allow accepts plugin ids (not tool names), and config patches against it are rejected by the protected-config-paths guard. In the wild this sent an agent down 3 restart cycles trying to "fix" config that was never the problem.
  • Fall-throughs are preserved: if <name> is not a registered tool, the existing plugins.allow and plugins.entries.<id>.enabled suggestions are emitted unchanged. The new branch only fires when the tool-registry lookup matches.

Reproduction

Before:

[openclaw] Failed to start CLI: Error: The `openclaw lcm_recent` command is unavailable
because `plugins.allow` excludes "lcm_recent". Add "lcm_recent" to `plugins.allow`
if you want that bundled plugin CLI surface.

After:

"lcm_recent" is an agent tool registered by the "lossless-claw" plugin, not a CLI
subcommand. Use it from an agent turn (model tool-use), not the CLI. Run
`openclaw --help` to see available CLI subcommands.

Implementation

  • New resolveManifestToolOwnerInRegistry in src/plugins/manifest-command-aliases.ts walks plugins[].contracts.tools (the same field the agent dispatch uses) and returns {toolName, pluginId} for the owning plugin.
  • PluginManifestCommandAliasRegistry is extended with an optional contracts?: { tools?: readonly string[] } per plugin entry. The runtime PluginManifestRegistry already populates this, so production wiring is structural.
  • resolveMissingPluginCommandMessage (in src/cli/run-main-policy.ts) gets a parallel resolveToolOwner callback hook (mirroring resolveCommandAliasOwner) and consults the tool registry just before the plugins.allow excludes branch.
  • src/cli/run-main.ts passes the new resolveManifestToolOwner runtime resolver into the policy call.

Validation

  • pnpm exec vitest run src/cli/run-main.test.ts src/plugins/manifest-command-aliases.test.ts -- 33 passed (3 new in run-main.test.ts, 1 new in manifest-command-aliases.test.ts).
  • pnpm exec vitest run src/cli/run-main.exit.test.ts src/cli/run-main.profile-env.test.ts -- 79 passed (sanity check on adjacent test files).
  • pnpm exec oxlint --type-aware src/cli/run-main-policy.ts src/cli/run-main.ts src/cli/run-main.test.ts src/plugins/manifest-command-aliases.ts src/plugins/manifest-command-aliases.runtime.ts src/plugins/manifest-command-aliases.test.ts -- 0 warnings, 0 errors.
  • pnpm tsgo:core -- clean.
  • pnpm check:changelog-attributions -- clean.

Closes #77214.

…tool name

When `openclaw <name>` does not match a CLI subcommand or a plugin id,
check the runtime plugin tool registry first. If `<name>` is actually
an agent tool registered by a loaded plugin (e.g. `lcm_recent` from
`lossless-claw`), emit a clear error explaining that the name is an
agent tool and is invoked via model tool-use, not the CLI -- instead of
the previous misleading suggestion to add the tool name to
`plugins.allow` (which has no effect, since `plugins.allow` accepts
plugin ids, not tool names, and editing it is rejected by the
protected-config-paths guard anyway).

Fixes openclaw#77214.
Copilot AI review requested due to automatic review settings May 4, 2026 09:08
@chatgpt-codex-connector
Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, add credits to your account and enable them for code reviews in your settings.

@clawsweeper
Copy link
Copy Markdown
Contributor

clawsweeper Bot commented May 4, 2026

Codex review: needs changes before merge.

Summary
The PR adds a CLI diagnostic branch that maps unknown command names to plugin contracts.tools owners and emits an agent-tool-not-CLI message, with tests and a changelog entry.

Reproducibility: yes. from source inspection. Current main’s unknown-subcommand policy emits the plugins.allow suggestion for a tool-like name when that name is not an allowed plugin id, and the PR’s proposed failure mode is also source-reproducible because it consults manifest ownership rather than active availability.

Next step before merge
The PR has a narrow, actionable correctness blocker: replace the raw manifest-owner check with an active/available tool check while preserving the existing CLI diagnostic fallthroughs.

Security
Cleared: The diff is limited to CLI diagnostic logic, plugin metadata lookup helpers, tests, and changelog; no concrete security or supply-chain regression was found.

Review findings

  • [P2] Check active tool availability before claiming registration — src/plugins/manifest-command-aliases.runtime.ts:38-42
Review details

Best possible solution:

Land the diagnostic only after it uses the same active/available plugin-tool semantics as agent tool discovery, preserving the existing enablement messages for excluded, disabled, optional, denied, or auth-unavailable tools.

Do we have a high-confidence way to reproduce the issue?

Yes from source inspection. Current main’s unknown-subcommand policy emits the plugins.allow suggestion for a tool-like name when that name is not an allowed plugin id, and the PR’s proposed failure mode is also source-reproducible because it consults manifest ownership rather than active availability.

Is this the best way to solve the issue?

No. The direction is right, but contracts.tools is only an ownership contract; the maintainable fix should query active/effective tool availability or apply the same enablement, optional, denylist, and toolMetadata filters before claiming a tool is registered.

Full review comments:

  • [P2] Check active tool availability before claiming registration — src/plugins/manifest-command-aliases.runtime.ts:38-42
    resolveManifestToolOwner loads the manifest metadata registry and the new policy branch treats any contracts.tools match as a registered agent tool. That registry includes disabled plugins, and manifest ownership also covers optional or auth/config-gated tools that runtime discovery would skip, so openclaw <tool> can now tell users to use model tool-use when the real fix is enabling/configuring the plugin or when the tool is not available at all. Please base this branch on the active/effective tool registry, or apply the same plugin enablement and tool availability filters used by plugin tool discovery.
    Confidence: 0.9

Overall correctness: patch is incorrect
Overall confidence: 0.88

Acceptance criteria:

  • pnpm test src/cli/run-main.test.ts src/plugins/manifest-command-aliases.test.ts src/plugins/tools.optional.test.ts
  • pnpm tsgo:core
  • pnpm check:changed

What I checked:

  • Current main emits the misleading fallback: After command-alias checks, current main falls through to the plugins.allow excludes message for any unknown primary command not present in plugins.allow, which matches the reported openclaw lcm_recent behavior. (src/cli/run-main-policy.ts:171, 04aa4a3fe6a8)
  • PR uses manifest metadata for the new tool owner branch: The proposed runtime resolver loads loadManifestMetadataRegistry(...).manifestRegistry and then resolves by manifest tool ownership before the existing allow/disabled branches. (src/plugins/manifest-command-aliases.runtime.ts:38, 2852a981a1ef)
  • Metadata registry includes disabled plugins: loadPluginMetadataSnapshotImpl builds the manifest registry from the installed index with includeDisabled: true, so a raw manifest lookup can see plugins excluded by config. (src/plugins/plugin-metadata-snapshot.ts:220, 04aa4a3fe6a8)
  • Runtime tool discovery applies availability filters: Plugin tool runtime selection skips disabled/denied plugins, optional tools not allowed by policy, and tools whose toolMetadata availability signals do not pass. (src/plugins/tools.ts:443, 04aa4a3fe6a8)
  • Manifest docs separate ownership from availability: The public manifest docs state that contracts.tools declares ownership, while toolMetadata declares cheap availability evidence used to avoid loading runtimes unnecessarily. Public docs: docs/plugins/manifest.md. (docs/plugins/manifest.md:287, 04aa4a3fe6a8)
  • Review discussion flags the same blocker: The PR review comments point out that the branch can fire for disabled plugins and for manifest-declared tools that are not actually available in the current process. (2852a981a1ef)

Likely related people:

  • Peter Steinberger: Recent history on the central CLI/plugin metadata files includes plugin command alias diagnostics, manifest command alias helper extraction, and broad manifest control-plane refactors. (role: introduced behavior and recent maintainer; confidence: high; commits: beaff3c553d9, 777c6f758091, ba7804df50d4; files: src/cli/run-main-policy.ts, src/plugins/manifest-command-aliases.ts, src/plugins/manifest.ts)
  • Vincent Koc: Recent history on the same plugin metadata surface includes manifest activation/setup descriptors and leaf type extraction around plugin contracts. (role: adjacent plugin metadata maintainer; confidence: medium; commits: 79c3dbecd128, 74e7b8d47b18, 982383338373; files: src/plugins/manifest.ts, src/plugins/tools.ts)
  • Tak Hoffman: Recent history includes the /tools runtime availability view and plugin tool auto-enable/reload fixes, which are close to the active-tool availability semantics this PR needs. (role: adjacent runtime tool availability owner; confidence: medium; commits: 9c7823350bf6, cfd1e94e6101, 36ac9224cc92; files: src/plugins/tools.ts, src/gateway/server-methods/tools-effective.ts)

Remaining risk / open question:

  • The repair should preserve CLI startup laziness; loading broad plugin runtimes only to improve this diagnostic would regress the control-plane boundary.

Codex review notes: model gpt-5.5, reasoning high; reviewed against 04aa4a3fe6a8.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR tries to improve CLI diagnostics when openclaw <name> is not a real CLI subcommand by distinguishing agent-tool names from plugin command names. It extends plugin manifest lookup so the unknown-command path can point users toward model tool-use instead of the existing plugins.allow suggestion.

Changes:

  • Added manifest-level tool-owner lookup alongside existing command-alias lookup.
  • Wired the new tool-owner resolver into CLI unknown-subcommand handling.
  • Added focused tests for tool-name detection and updated the changelog.

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
src/plugins/manifest-command-aliases.ts Adds manifest-based tool-owner lookup and extends the registry shape with contracts.tools.
src/plugins/manifest-command-aliases.test.ts Adds unit coverage for manifest-based tool-owner resolution.
src/plugins/manifest-command-aliases.runtime.ts Exposes a runtime helper that loads manifest metadata and resolves tool owners.
src/cli/run-main.ts Passes the new runtime tool-owner resolver into unknown-command policy handling.
src/cli/run-main.test.ts Adds tests for the new agent-tool error messaging behavior.
src/cli/run-main-policy.ts Introduces the new “agent tool, not CLI subcommand” error branch.
CHANGELOG.md Documents the CLI diagnostic change.

Comment on lines +178 to +193
const toolOwner = options?.registry
? resolveManifestToolOwnerInRegistry({
toolName: normalizedPluginId,
registry: options.registry,
})
: options?.resolveToolOwner?.({
toolName: normalizedPluginId,
config,
...(options?.registry ? { registry: options.registry } : {}),
});
if (toolOwner) {
return (
`"${normalizedPluginId}" is an agent tool registered by the "${toolOwner.pluginId}" plugin, ` +
`not a CLI subcommand. Use it from an agent turn (model tool-use), not the CLI. ` +
"Run `openclaw --help` to see available CLI subcommands."
);
Comment on lines +79 to +90
for (const plugin of params.registry.plugins) {
const tools = plugin.contracts?.tools;
if (!tools || tools.length === 0) {
continue;
}
const match = tools.find(
(entry) => normalizeOptionalLowercaseString(entry) === normalizedToolName,
);
if (match) {
return { toolName: match, pluginId: plugin.id };
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

cli CLI command changes size: S

Projects

None yet

Development

Successfully merging this pull request may close these issues.

CLI suggests plugins.allow for unknown subcommands when input is actually an agent tool name

2 participants