fix: simplify Crestodian startup greeting

This commit is contained in:
Peter Steinberger
2026-04-25 11:20:48 +01:00
parent a0c70c4f5a
commit 6a71c19839
5 changed files with 84 additions and 33 deletions

View File

@@ -7,6 +7,7 @@ Docs: https://docs.openclaw.ai
### Changes
- CLI/Crestodian: open interactive Crestodian in the full OpenClaw TUI shell instead of a basic readline prompt.
- CLI/Crestodian: shorten the startup greeting to the active planner/model, config state, Gateway probe result, and next debug action instead of dumping every discovered backend.
- Diagnostics/OTEL: add bounded outbound message delivery lifecycle diagnostics and export them as low-cardinality delivery spans/metrics without message body, recipient, room, or media-path data. (#71471) Thanks @vincentkoc and @jlapenna.
- Diagnostics/OTEL: emit bounded exec-process diagnostics and export them as `openclaw.exec` spans without exposing command text, working directories, or container identifiers. (#71451) Thanks @vincentkoc and @jlapenna.
- Diagnostics/OTEL: support `OPENCLAW_OTEL_PRELOADED=1` so the plugin can reuse an already-registered OpenTelemetry SDK while keeping OpenClaw diagnostic listeners wired. (#71450) Thanks @vincentkoc and @jlapenna.

View File

@@ -18,24 +18,22 @@ Running `openclaw crestodian` starts the same helper explicitly.
## What Crestodian shows
On startup, interactive Crestodian opens the same TUI shell used by
`openclaw tui`, with a Crestodian chat backend. The chat log starts with a
compact system overview:
`openclaw tui`, with a Crestodian chat backend. The chat log starts with a short
greeting:
- config path and validity
- configured agents and the default agent
- default model
- local Codex and Claude Code CLI availability
- OpenAI and Anthropic API-key presence
- planner mode (`deterministic` or model-assisted through the configured model)
- local docs path or the public docs URL
- local source path for Git checkouts, otherwise the OpenClaw GitHub source URL
- gateway reachability
- the immediate recommended next step
- when to start Crestodian
- the model or deterministic planner path Crestodian is actually using
- config validity and the default agent
- Gateway reachability from the first startup probe
- the next debug action Crestodian can take
It does not dump secrets or load plugin CLI commands just to start. The TUI
still provides the normal header, chat log, status line, footer, autocomplete,
and editor controls.
Use `status` for the detailed inventory with config path, docs/source paths,
local CLI probes, API-key presence, agents, model, and Gateway details.
Crestodian uses the same OpenClaw reference discovery as regular agents. In a Git checkout,
it points itself at local `docs/` and the local source tree. In an npm package install, it
uses the bundled package docs and links to

View File

@@ -53,7 +53,8 @@ describe("loadCrestodianOverview", () => {
)}\n`,
);
const { formatCrestodianOverview, loadCrestodianOverview } = await import("./overview.js");
const { formatCrestodianOverview, formatCrestodianStartupMessage, loadCrestodianOverview } =
await import("./overview.js");
const overview = await loadCrestodianOverview();
expect(overview.config).toMatchObject({
@@ -72,7 +73,15 @@ describe("loadCrestodianOverview", () => {
expect(overview.references.docsPath).toMatch(/docs$/);
expect(overview.references.sourceUrl).toBe("https://github.com/openclaw/openclaw");
expect(formatCrestodianOverview(overview)).toContain(
'Next: say "gateway status" or "restart gateway"',
'Next: run "gateway status" or "restart gateway"',
);
const startup = formatCrestodianStartupMessage(overview);
expect(startup).toContain("## Hi, I'm Crestodian.");
expect(startup).toContain("Using: openai/gpt-5.2");
expect(startup).toContain("Gateway: not reachable");
expect(startup).toContain("I can start debugging with `gateway status`");
expect(startup).not.toContain("Codex:");
expect(startup).not.toContain("Claude Code:");
expect(startup).not.toContain("API keys:");
});
});

View File

@@ -237,16 +237,70 @@ export function formatCrestodianOverview(overview: CrestodianOverview): string {
export function recommendCrestodianNextStep(overview: CrestodianOverview): string {
if (!overview.config.exists) {
return 'say "setup" to create a starter config';
return 'run "setup" to create a starter config';
}
if (!overview.config.valid) {
return 'say "validate config" or "doctor" to inspect the config';
return 'run "validate config" or "doctor" to inspect the config';
}
if (!overview.defaultModel) {
return 'say "setup" or "set default model <provider/model>"';
return 'run "setup" or "set default model <provider/model>"';
}
if (!overview.gateway.reachable) {
return 'say "gateway status" or "restart gateway"';
return 'run "gateway status" or "restart gateway"';
}
return 'say "talk to agent" to enter your default agent';
return 'run "talk to agent" to enter your default agent';
}
function formatStartupConfigStatus(overview: CrestodianOverview): string {
if (!overview.config.exists) {
return "missing";
}
return overview.config.valid ? "valid" : "invalid";
}
function formatStartupUse(overview: CrestodianOverview): string {
if (overview.defaultModel) {
return `Using: ${overview.defaultModel} for fuzzy local planning.`;
}
return "Using: deterministic typed commands until we configure a model.";
}
function formatStartupGatewayStatus(overview: CrestodianOverview): string {
if (overview.gateway.reachable) {
return `Gateway: reachable at ${overview.gateway.url}.`;
}
return `Gateway: not reachable at ${overview.gateway.url}; I already did the first probe.`;
}
function formatStartupAction(overview: CrestodianOverview): string {
if (!overview.config.exists) {
return "I can start by creating a starter config with `setup`.";
}
if (!overview.config.valid) {
return "I can start debugging with `validate config` or `doctor`.";
}
if (!overview.defaultModel) {
return "I can start by choosing a model with `setup`.";
}
if (!overview.gateway.reachable) {
return "I can start debugging with `gateway status`, or queue `restart gateway` for approval.";
}
return "Everything basic is reachable. Use `talk to agent` when you want the normal agent.";
}
export function formatCrestodianStartupMessage(overview: CrestodianOverview): string {
const agent = overview.agents.find((entry) => entry.id === overview.defaultAgentId);
const agentLabel = agent?.name
? `${overview.defaultAgentId} (${agent.name})`
: overview.defaultAgentId;
return [
"## Hi, I'm Crestodian.",
"",
"- Start me when setup, config, Gateway, model choice, or agent routing feels off.",
`- ${formatStartupUse(overview)}`,
`- Config: ${formatStartupConfigStatus(overview)}. Default agent: ${agentLabel}.`,
`- ${formatStartupGatewayStatus(overview)}`,
"",
formatStartupAction(overview),
].join("\n");
}

View File

@@ -19,7 +19,7 @@ import {
type CrestodianCommandDeps,
type CrestodianOperation,
} from "./operations.js";
import { formatCrestodianOverview, loadCrestodianOverview } from "./overview.js";
import { formatCrestodianStartupMessage, loadCrestodianOverview } from "./overview.js";
export type CrestodianTuiOptions = {
yes?: boolean;
@@ -75,14 +75,6 @@ function splitModelRef(ref: string | undefined): { provider?: string; model?: st
};
}
function crestodianWelcome(overviewText: string): string {
return [
overviewText,
"",
"Say: status, doctor, health, gateway status, restart gateway, agents, models, set default model <provider/model>, talk to agent, audit, or quit.",
].join("\n");
}
class CrestodianTuiBackend implements TuiBackend {
readonly connection = { url: "crestodian local" };
@@ -212,7 +204,7 @@ class CrestodianTuiBackend implements TuiBackend {
this.messages.splice(
0,
this.messages.length,
message("assistant", crestodianWelcome(formatCrestodianOverview(overview))),
message("assistant", formatCrestodianStartupMessage(overview)),
);
return { ok: true };
}
@@ -325,10 +317,7 @@ export async function runCrestodianTui(
let nextInput: string | undefined;
for (;;) {
const overview = await loadCrestodianOverview();
const backend = new CrestodianTuiBackend(
opts,
crestodianWelcome(formatCrestodianOverview(overview)),
);
const backend = new CrestodianTuiBackend(opts, formatCrestodianStartupMessage(overview));
await runTui({
local: true,
session: CRESTODIAN_SESSION_KEY,