From f7e54acec1ba1e8a3907697e837736ce4c3f1b7b Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Thu, 4 Jun 2026 11:03:24 -0400 Subject: [PATCH] docs: document plugin program cli helpers --- src/cli/plugins-install-command.ts | 2 ++ src/cli/plugins-install-persist.ts | 2 ++ src/cli/plugins-list-format.ts | 1 + src/cli/plugins-uninstall-command.ts | 2 ++ src/cli/ports.ts | 2 ++ src/cli/profile-utils.ts | 1 + src/cli/profile.ts | 2 ++ src/cli/program/build-program.ts | 1 + src/cli/program/command-registry-core.ts | 2 ++ src/cli/program/config-guard.ts | 2 ++ 10 files changed, 17 insertions(+) diff --git a/src/cli/plugins-install-command.ts b/src/cli/plugins-install-command.ts index fa1c3b225e38..e7819a9b2f95 100644 --- a/src/cli/plugins-install-command.ts +++ b/src/cli/plugins-install-command.ts @@ -1,3 +1,4 @@ +// Plugin install command implementation for bundled, npm, path, git, ClawHub, and hook packs. import fs from "node:fs"; import { isRecord } from "@openclaw/normalization-core/record-coerce"; import { uniqueStrings } from "@openclaw/normalization-core/string-normalization"; @@ -152,6 +153,7 @@ async function installBundledPluginSource(params: { invalidateRuntimeCache?: boolean; runtime?: RuntimeEnv; }) { + // Bundled plugins with required config are recorded but not enabled until config validates. const existingEntry = params.snapshot.config.plugins?.entries?.[params.bundledSource.pluginId]; const shouldEnable = hasValidBundledPluginConfig({ bundledSource: params.bundledSource, diff --git a/src/cli/plugins-install-persist.ts b/src/cli/plugins-install-persist.ts index 7da70f2365ff..2a7b8026bb1b 100644 --- a/src/cli/plugins-install-persist.ts +++ b/src/cli/plugins-install-persist.ts @@ -1,3 +1,4 @@ +// Persistence helpers for plugin and hook-pack installs plus related config mutation. import { theme } from "../../packages/terminal-core/src/theme.js"; import { replaceConfigFile } from "../config/config.js"; import type { OpenClawConfig } from "../config/types.openclaw.js"; @@ -83,6 +84,7 @@ function logShadowedNpmInstallWarning(params: { install: Omit; runtime: RuntimeEnv; }): void { + // Warn when a newly installed npm plugin is shadowed by an explicit config source. if (params.install.source !== "npm") { return; } diff --git a/src/cli/plugins-list-format.ts b/src/cli/plugins-list-format.ts index 82bb26f1f9d9..f2ee6cb84395 100644 --- a/src/cli/plugins-list-format.ts +++ b/src/cli/plugins-list-format.ts @@ -1,3 +1,4 @@ +// Text formatter for plugin list rows and verbose plugin details. import { sanitizeTerminalText } from "../../packages/terminal-core/src/safe-text.js"; import { theme } from "../../packages/terminal-core/src/theme.js"; import type { PluginRecord } from "../plugins/registry.js"; diff --git a/src/cli/plugins-uninstall-command.ts b/src/cli/plugins-uninstall-command.ts index 3759ad40caa2..cc514beb5e49 100644 --- a/src/cli/plugins-uninstall-command.ts +++ b/src/cli/plugins-uninstall-command.ts @@ -1,3 +1,4 @@ +// Plugin uninstall command implementation and confirmation-driven removal plan execution. import os from "node:os"; import path from "node:path"; import { theme } from "../../packages/terminal-core/src/theme.js"; @@ -32,6 +33,7 @@ export async function runPluginUninstallCommand( opts: PluginUninstallOptions = {}, runtime: RuntimeEnv = defaultRuntime, ): Promise { + // Uninstall mutates config/install records and optionally managed files, so guard write mode first. assertConfigWriteAllowedInCurrentMode(); const { diff --git a/src/cli/ports.ts b/src/cli/ports.ts index 2c9491dd1696..80f597a6d8c8 100644 --- a/src/cli/ports.ts +++ b/src/cli/ports.ts @@ -1,3 +1,4 @@ +// Port inspection and force-free helpers used by gateway run/install flows. import { execFileSync } from "node:child_process"; import { createServer } from "node:net"; import { formatErrorMessage } from "../infra/errors.js"; @@ -62,6 +63,7 @@ function getErrnoCode(err: unknown): string | undefined { } function isRecoverableLsofError(err: unknown): boolean { + // Permission or missing-binary failures can fall back to fuser on Linux. const code = getErrnoCode(err); if (code === "ENOENT" || code === "EACCES" || code === "EPERM") { return true; diff --git a/src/cli/profile-utils.ts b/src/cli/profile-utils.ts index 578f73aabfb2..0cb15fe3f672 100644 --- a/src/cli/profile-utils.ts +++ b/src/cli/profile-utils.ts @@ -1,3 +1,4 @@ +// Profile name validation and normalization helpers for root CLI profile routing. import { normalizeLowercaseStringOrEmpty } from "@openclaw/normalization-core/string-coerce"; const PROFILE_NAME_RE = /^[a-z0-9][a-z0-9_-]{0,63}$/i; diff --git a/src/cli/profile.ts b/src/cli/profile.ts index 38e7cd5a41a3..63c0a11d188c 100644 --- a/src/cli/profile.ts +++ b/src/cli/profile.ts @@ -1,3 +1,4 @@ +// Root --profile/--dev parsing and environment projection for profile-specific state. import os from "node:os"; import path from "node:path"; import { @@ -21,6 +22,7 @@ function isCommandLocalProfileOption(out: string[]): boolean { } export function parseCliProfileArgs(argv: string[]): CliProfileParseResult { + // Root profile flags are stripped before Commander sees argv, except command-local cases. let profile: string | null = null; let sawDev = false; diff --git a/src/cli/program/build-program.ts b/src/cli/program/build-program.ts index df30edc2a21b..b93c263d2762 100644 --- a/src/cli/program/build-program.ts +++ b/src/cli/program/build-program.ts @@ -1,3 +1,4 @@ +// Builds the root Commander program, context, help, hooks, and command registry. import process from "node:process"; import { Command } from "commander"; import { registerProgramCommands } from "./command-registry.js"; diff --git a/src/cli/program/command-registry-core.ts b/src/cli/program/command-registry-core.ts index da7d9189fb57..6696cee34113 100644 --- a/src/cli/program/command-registry-core.ts +++ b/src/cli/program/command-registry-core.ts @@ -1,3 +1,4 @@ +// Core command registry that lazily imports command groups based on parsed argv. import type { Command } from "commander"; import { resolveCliArgvInvocation } from "../argv-invocation.js"; import { shouldRegisterPrimaryCommandOnly } from "../command-registration-policy.js"; @@ -142,6 +143,7 @@ const coreEntrySpecs: readonly CommandGroupDescriptorSpec< ]; function resolveCoreCommandGroups(ctx: ProgramContext, argv: string[]): CommandGroupEntry[] { + // Descriptor metadata and import specs stay separate so help can stay cheap. return buildCommandGroupEntries( getCoreCliCommandDescriptors(), coreEntrySpecs, diff --git a/src/cli/program/config-guard.ts b/src/cli/program/config-guard.ts index b164929d962c..356118b0a22f 100644 --- a/src/cli/program/config-guard.ts +++ b/src/cli/program/config-guard.ts @@ -1,3 +1,4 @@ +// CLI config readiness guard, legacy-state migration routing, and invalid-config allowances. import fs from "node:fs"; import os from "node:os"; import path from "node:path"; @@ -97,6 +98,7 @@ function hasBundledChannelLegacyStateMigrationInputs(stateDir: string, oauthDir: } function hasLegacyStateMigrationInputs(): boolean { + // Only run migration prompts when old state actually exists in known legacy locations. const stateDir = resolveStateDir(process.env, os.homedir); const oauthDir = resolveOAuthDir(process.env, stateDir); if (