feat: enhance workspace scaffolding with new identity and soul templates, and support for default user file

This commit is contained in:
Lauren ten Hoor
2026-02-16 14:57:17 +08:00
parent 25ce06e14f
commit d0d1bdda60
3 changed files with 99 additions and 3 deletions

View File

@@ -66,7 +66,8 @@ export async function runSetup(opts: SetupOpts): Promise<SetupResult> {
await writePluginConfig(opts.api, agentId, opts.projectExecution); await writePluginConfig(opts.api, agentId, opts.projectExecution);
const filesWritten = await scaffoldWorkspace(workspacePath); const defaultWorkspacePath = getDefaultWorkspacePath(opts.api);
const filesWritten = await scaffoldWorkspace(workspacePath, defaultWorkspacePath);
const models = buildModelConfig(opts.models); const models = buildModelConfig(opts.models);
await writeModelsToWorkflow(workspacePath, models); await writeModelsToWorkflow(workspacePath, models);
@@ -140,6 +141,15 @@ function buildModelConfig(overrides?: SetupOpts["models"]): ModelConfig {
return result; return result;
} }
function getDefaultWorkspacePath(api: OpenClawPluginApi): string | undefined {
try {
const config = api.runtime.config.loadConfig();
return (config as any).agents?.defaults?.workspace ?? undefined;
} catch {
return undefined;
}
}
/** /**
* Write model configuration to workflow.yaml (single source of truth). * Write model configuration to workflow.yaml (single source of truth).
* Reads the existing workflow.yaml, merges model overrides into the roles section, and writes back. * Reads the existing workflow.yaml, merges model overrides into the roles section, and writes back.

View File

@@ -8,6 +8,8 @@ import path from "node:path";
import { import {
AGENTS_MD_TEMPLATE, AGENTS_MD_TEMPLATE,
HEARTBEAT_MD_TEMPLATE, HEARTBEAT_MD_TEMPLATE,
IDENTITY_MD_TEMPLATE,
SOUL_MD_TEMPLATE,
WORKFLOW_YAML_TEMPLATE, WORKFLOW_YAML_TEMPLATE,
DEFAULT_ROLE_INSTRUCTIONS, DEFAULT_ROLE_INSTRUCTIONS,
} from "../templates.js"; } from "../templates.js";
@@ -57,21 +59,51 @@ export async function ensureDefaultFiles(workspacePath: string): Promise<void> {
/** /**
* Write all workspace files for a DevClaw agent. * Write all workspace files for a DevClaw agent.
* Returns the list of files that were written (skips files that already exist). * Returns the list of files that were written (skips files that already exist).
*
* @param defaultWorkspacePath — If provided, USER.md is copied from here (only if not already present).
*/ */
export async function scaffoldWorkspace(workspacePath: string): Promise<string[]> { export async function scaffoldWorkspace(workspacePath: string, defaultWorkspacePath?: string): Promise<string[]> {
// Migrate old layout if detected // Migrate old layout if detected
await migrateWorkspaceLayout(workspacePath); await migrateWorkspaceLayout(workspacePath);
const written: string[] = [];
// AGENTS.md (backup existing — stays at workspace root) // AGENTS.md (backup existing — stays at workspace root)
await backupAndWrite(path.join(workspacePath, "AGENTS.md"), AGENTS_MD_TEMPLATE); await backupAndWrite(path.join(workspacePath, "AGENTS.md"), AGENTS_MD_TEMPLATE);
written.push("AGENTS.md");
// HEARTBEAT.md (stays at workspace root) // HEARTBEAT.md (stays at workspace root)
await backupAndWrite(path.join(workspacePath, "HEARTBEAT.md"), HEARTBEAT_MD_TEMPLATE); await backupAndWrite(path.join(workspacePath, "HEARTBEAT.md"), HEARTBEAT_MD_TEMPLATE);
written.push("HEARTBEAT.md");
// IDENTITY.md (create-only — never overwrite user customizations)
const identityPath = path.join(workspacePath, "IDENTITY.md");
if (!await fileExists(identityPath)) {
await fs.writeFile(identityPath, IDENTITY_MD_TEMPLATE, "utf-8");
written.push("IDENTITY.md");
}
// SOUL.md (create-only — never overwrite user customizations)
const soulPath = path.join(workspacePath, "SOUL.md");
if (!await fileExists(soulPath)) {
await fs.writeFile(soulPath, SOUL_MD_TEMPLATE, "utf-8");
written.push("SOUL.md");
}
// USER.md — copy from default workspace if available (create-only)
const userPath = path.join(workspacePath, "USER.md");
if (!await fileExists(userPath) && defaultWorkspacePath) {
const sourceUser = path.join(defaultWorkspacePath, "USER.md");
if (await fileExists(sourceUser)) {
await fs.copyFile(sourceUser, userPath);
written.push("USER.md");
}
}
// Ensure all data-dir defaults (workflow.yaml, prompts, etc.) // Ensure all data-dir defaults (workflow.yaml, prompts, etc.)
await ensureDefaultFiles(workspacePath); await ensureDefaultFiles(workspacePath);
return ["AGENTS.md", "HEARTBEAT.md"]; return written;
} }
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------

View File

@@ -226,6 +226,10 @@ All orchestration goes through these tools. You do NOT manually manage sessions,
| \`work_finish\` | End-to-end: label transition, state update, issue close/reopen | | \`work_finish\` | End-to-end: label transition, state update, issue close/reopen |
| \`design_task\` | Spawn an architect for design investigation. Creates To Design issue and dispatches architect | | \`design_task\` | Spawn an architect for design investigation. Creates To Design issue and dispatches architect |
### First Thing on Session Start
**Always call \`status\` first** when you start a new session. This tells you which projects you manage, what's in the queue, and which workers are active. Don't guess — check.
### Pipeline Flow ### Pipeline Flow
\`\`\` \`\`\`
@@ -295,6 +299,56 @@ export const HEARTBEAT_MD_TEMPLATE = `# HEARTBEAT.md
Do nothing. An internal token-free heartbeat service handles health checks and queue dispatch automatically. Do nothing. An internal token-free heartbeat service handles health checks and queue dispatch automatically.
`; `;
export const IDENTITY_MD_TEMPLATE = `# IDENTITY.md - Who Am I?
- **Name:** DevClaw
- **Creature:** Development orchestrator — plans, dispatches, never codes
- **Vibe:** Direct, decisive, transparent. No fluff.
- **Emoji:** 🦞
`;
export const SOUL_MD_TEMPLATE = `# SOUL.md - DevClaw Orchestrator Identity
You are a **development orchestrator** — you plan, prioritize, and dispatch. You never write code yourself.
## Core Principles
**Be direct.** Skip pleasantries, get to the point. Say what you're doing and why.
**Be decisive.** Evaluate task complexity, pick the right level, dispatch. Don't deliberate when the answer is obvious.
**Be transparent.** Always include issue URLs. Always explain what happened and what's next. No black boxes.
**Be resourceful.** Check status before asking. Read the issue before dispatching. Understand the codebase before planning. Come back with answers, not questions.
## How You Work
- You receive requests via chat (Telegram, WhatsApp, or web)
- You break work into issues, assign complexity levels, and dispatch workers
- Workers (developer, tester, architect) do the actual work in isolated sessions
- You track progress, handle failures, and keep the human informed
- The heartbeat runs automatically — you don't manage it
## Communication Style
- Concise status updates with issue links
- Use the announcement format from tool responses
- Flag blockers and failures immediately
- Don't over-explain routine operations
## Boundaries
- **Never write code** — dispatch a developer worker
- **Never skip testing** — every code change goes through QA
- **Never close issues** without a tester pass
- **Ask before** architectural decisions affecting multiple projects
## Continuity
Each session starts fresh. AGENTS.md defines your operational procedures. This file defines who you are. USER.md tells you about the humans you work with. Update these files as you learn.
`;
/** /**
* Generate WORKFLOW_YAML_TEMPLATE from the runtime objects (single source of truth). * Generate WORKFLOW_YAML_TEMPLATE from the runtime objects (single source of truth).
*/ */