Files
devclaw-gitea/lib/onboarding.ts
Lauren ten Hoor b2fc94db9e feat: LLM-powered model auto-configuration and improved onboarding
Major changes:
- Add autoconfigure_models tool for intelligent model assignment
- Implement LLM-based model selection using openclaw agent
- Improve onboarding flow with better model access checks
- Update README with clearer installation and onboarding instructions

Technical improvements:
- Add model-fetcher utility to query authenticated models
- Add smart-model-selector for LLM-driven model assignment
- Use session context for LLM calls during onboarding
- Suppress logging from openclaw models list calls

Documentation:
- Add prerequisites section to README
- Add conversational onboarding example
- Improve quick start flow

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-12 20:37:15 +08:00

194 lines
7.2 KiB
TypeScript

/**
* onboarding.ts — Conversational onboarding context templates.
*
* Provides context templates for the onboard tool.
*/
import fs from "node:fs/promises";
import path from "node:path";
import { DEFAULT_MODELS } from "./tiers.js";
// ---------------------------------------------------------------------------
// Detection
// ---------------------------------------------------------------------------
export function isPluginConfigured(
pluginConfig?: Record<string, unknown>,
): boolean {
const models = (pluginConfig as { models?: Record<string, string> })?.models;
return !!models && Object.keys(models).length > 0;
}
export async function hasWorkspaceFiles(
workspaceDir?: string,
): Promise<boolean> {
if (!workspaceDir) return false;
try {
const content = await fs.readFile(
path.join(workspaceDir, "AGENTS.md"),
"utf-8",
);
return content.includes("DevClaw") && content.includes("work_start");
} catch {
return false;
}
}
// ---------------------------------------------------------------------------
// Context templates
// ---------------------------------------------------------------------------
function buildModelTable(pluginConfig?: Record<string, unknown>): string {
const cfg = (
pluginConfig as {
models?: { dev?: Record<string, string>; qa?: Record<string, string> };
}
)?.models;
const lines: string[] = [];
for (const [role, levels] of Object.entries(DEFAULT_MODELS)) {
for (const [level, defaultModel] of Object.entries(levels)) {
const model = cfg?.[role as "dev" | "qa"]?.[level] || defaultModel;
lines.push(
` - **${role} ${level}**: ${model} (default: ${defaultModel})`,
);
}
}
return lines.join("\n");
}
export function buildReconfigContext(
pluginConfig?: Record<string, unknown>,
): string {
const modelTable = buildModelTable(pluginConfig);
return `# DevClaw Reconfiguration
The user wants to reconfigure DevClaw. Current model configuration:
${modelTable}
## What can be changed
1. **Model levels** — call \`setup\` with a \`models\` object containing only the levels to change
2. **Workspace files** — \`setup\` re-writes AGENTS.md, HEARTBEAT.md (backs up existing files)
3. **Register new projects** — use \`project_register\`
Ask what they want to change, then call the appropriate tool.
\`setup\` is safe to re-run — it backs up existing files before overwriting.
`;
}
export function buildOnboardToolContext(): string {
// Build the model table dynamically from DEFAULT_MODELS
const rows: string[] = [];
const purposes: Record<string, string> = {
junior: "Typos, single-file fixes",
medior: "Features, bug fixes",
senior: "Architecture, refactoring",
reviewer: "Code review",
tester: "Testing",
};
for (const [role, levels] of Object.entries(DEFAULT_MODELS)) {
for (const [level, model] of Object.entries(levels)) {
rows.push(`| ${role} | ${level} | ${model} | ${purposes[level] ?? ""} |`);
}
}
const modelTable = rows.join("\n");
return `# DevClaw Onboarding
## What is DevClaw?
DevClaw turns each Telegram group into an autonomous development team:
- An **orchestrator** that manages backlogs and delegates work
- **DEV workers** (junior/medior/senior levels) that write code in isolated sessions
- **QA workers** that review code and run tests
- Atomic tools for label transitions, session dispatch, state management, and audit logging
## Setup Steps
**Step 1: Agent Selection**
Ask: "Do you want to configure DevClaw for the current agent, or create a new dedicated agent?"
- Current agent → no \`newAgentName\` needed
- New agent → ask for:
1. Agent name
2. **Channel binding**: "Which channel should this agent listen to? (telegram/whatsapp/none)"
- If telegram/whatsapp selected:
a) Check openclaw.json for existing channel bindings
b) If channel not configured/enabled → warn and recommend skipping binding for now
c) If channel-wide binding exists on another agent → ask: "Migrate binding from {agentName}?"
d) Collect migration decision
- If none selected, user can add bindings manually later via openclaw.json
**Step 2: Model Configuration**
1. **Call \`autoconfigure_models\`** to automatically discover and assign models:
- Discovers all authenticated models in OpenClaw
- Uses AI to intelligently assign them to DevClaw roles
- Returns a ready-to-use model configuration
2. **Handle the result**:
- If \`success: false\` and \`modelCount: 0\`:
- **BLOCK setup** - show the authentication instructions from the message
- **DO NOT proceed** - exit onboarding until user configures API keys
- If \`success: true\`:
- Present the model assignment table to the user
- Store the \`models\` object for Step 3
3. **Optional: Prefer specific provider**
- If user wants only models from one provider (e.g., "only use Anthropic"):
- Call \`autoconfigure_models({ preferProvider: "anthropic" })\`
4. **Confirm with user**
- Ask: "Does this look good, or would you like to customize any roles?"
- If approved → proceed to Step 3 with the \`models\` configuration
- If they want changes → ask which specific roles to modify
- If they want different provider → go back to step 3
**Step 3: Run Setup**
Call \`setup\` with the collected answers:
- Current agent: \`setup({})\` or \`setup({ models: { dev: { ... }, qa: { ... } } })\`
- New agent: \`setup({ newAgentName: "<name>", channelBinding: "telegram"|"whatsapp"|null, migrateFrom: "<agentId>"|null, models: { ... } })\`
- \`migrateFrom\`: Include if user wants to migrate an existing channel-wide binding
**Step 4: Telegram Group Setup (IMPORTANT)**
After setup completes, explain project isolation best practices:
📱 **Telegram Group Guidance:**
DevClaw uses **one Telegram group per project** for isolation and clean backlogs.
**Recommended Setup:**
1. **Create a new Telegram group** for each project
2. **Add your bot** to the group
3. **Use mentions** to interact: "@botname status", "@botname pick up #42"
4. Each group gets its own queue, workers, and audit log
**Why separate groups?**
- Clean issue backlogs per project
- Isolated worker state (no cross-project confusion)
- Clear audit trails
- Team-specific access control
**Single-project mode:**
If you REALLY want all projects in one group (not recommended):
- You can register multiple projects to the same group ID
- ⚠️ WARNING: Shared queues, workers will see all issues
- Only use this for personal/solo projects
Ask: "Do you understand the group-per-project model, or do you want single-project mode?"
- Most users should proceed with the recommended approach
- Only force single-project if they insist
**Step 5: Project Registration**
Ask: "Would you like to register a project now?"
If yes, collect: project name, repo path, Telegram group ID, group name, base branch.
Then call \`project_register\`.
💡 **Tip**: For the Telegram group ID:
- Add the bot to your group
- Send any message with the bot mentioned
- Bot can tell you the group ID
## Guidelines
- Be conversational and friendly. Ask one question at a time.
- Show defaults so the user can accept them quickly.
- After setup, summarize what was configured (including channel binding if applicable).
`;
}