openclaw-agent-builder 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,13 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>OpenClaw Agent Builder</title>
7
+ <script type="module" crossorigin src="/assets/index-yzWCTaaY.js"></script>
8
+ <link rel="stylesheet" crossorigin href="/assets/index-J39606WI.css">
9
+ </head>
10
+ <body>
11
+ <div id="root"></div>
12
+ </body>
13
+ </html>
package/package.json ADDED
@@ -0,0 +1,46 @@
1
+ {
2
+ "name": "openclaw-agent-builder",
3
+ "version": "0.0.1",
4
+ "description": "Web-based wizard to create and deploy OpenClaw agents and multi-agent teams",
5
+ "type": "module",
6
+ "bin": {
7
+ "openclaw-agent-builder": "bin/cli.js"
8
+ },
9
+ "scripts": {
10
+ "dev": "NODE_ENV=development node bin/cli.js --no-open",
11
+ "build": "cd client && vite build",
12
+ "start": "NODE_ENV=production node bin/cli.js",
13
+ "preview": "npm run build && npm start"
14
+ },
15
+ "files": [
16
+ "bin/",
17
+ "server/",
18
+ "dist/",
19
+ "README.md"
20
+ ],
21
+ "engines": {
22
+ "node": ">=18.0.0"
23
+ },
24
+ "keywords": [
25
+ "openclaw",
26
+ "agent",
27
+ "wizard",
28
+ "ai",
29
+ "multi-agent"
30
+ ],
31
+ "license": "MIT",
32
+ "dependencies": {
33
+ "express": "^4.18.2",
34
+ "open": "^9.1.0"
35
+ },
36
+ "devDependencies": {
37
+ "@vitejs/plugin-react": "^4.2.1",
38
+ "autoprefixer": "^10.4.17",
39
+ "postcss": "^8.4.35",
40
+ "react": "^18.2.0",
41
+ "react-dom": "^18.2.0",
42
+ "tailwindcss": "^3.4.1",
43
+ "vite": "^5.0.12",
44
+ "zustand": "^4.5.0"
45
+ }
46
+ }
@@ -0,0 +1,87 @@
1
+ /**
2
+ * Generate AGENTS.md for an agent.
3
+ * Pure function — never invents content not in the TeamSpec.
4
+ */
5
+ export function generateAgents(agent, teamSpec) {
6
+ const neverRules = agent.never && agent.never.length > 0
7
+ ? agent.never.map(r => `- **NEVER** ${r}`).join('\n')
8
+ : '- // TODO: define red lines';
9
+
10
+ const isRouter = teamSpec.orchestration &&
11
+ teamSpec.orchestration.router_agent === agent.id;
12
+
13
+ const otherAgents = teamSpec.agents.filter(a => a.id !== agent.id);
14
+
15
+ let orchestrationSection = '';
16
+ if (isRouter && otherAgents.length > 0) {
17
+ const rows = otherAgents.map(a =>
18
+ `| ${a.id} | ${a.name} | ${a.mission || '// TODO'} |`
19
+ ).join('\n');
20
+ orchestrationSection = `
21
+ ## Orchestration Rules
22
+
23
+ You are the router agent. Route tasks to the appropriate specialist:
24
+
25
+ | Agent ID | Name | Mission |
26
+ |----------|------|---------|
27
+ ${rows}
28
+
29
+ Orchestration mode: \`${teamSpec.orchestration.mode}\`
30
+ `;
31
+ } else if (isRouter) {
32
+ orchestrationSection = `
33
+ ## Orchestration Rules
34
+
35
+ You are the router agent. Orchestration mode: \`${teamSpec.orchestration?.mode || 'hub_and_spoke'}\`
36
+ `;
37
+ } else if (teamSpec.orchestration && teamSpec.orchestration.router_agent) {
38
+ orchestrationSection = `
39
+ ## Orchestration Rules
40
+
41
+ Escalate to **${teamSpec.orchestration.router_agent}** for coordination decisions.
42
+ `;
43
+ }
44
+
45
+ const agentHandoffs = (teamSpec.handoffs || []).filter(h => h.from === agent.id);
46
+ let handoffSection = '';
47
+ if (agentHandoffs.length > 0) {
48
+ const rows = agentHandoffs.map(h =>
49
+ `| ${h.to} | ${h.when} | ${h.payload_contract} |`
50
+ ).join('\n');
51
+ handoffSection = `
52
+ ## Cross-Agent Handoffs
53
+
54
+ | To Agent | When | Payload |
55
+ |----------|------|---------|
56
+ ${rows}
57
+ `;
58
+ } else {
59
+ handoffSection = `
60
+ ## Cross-Agent Handoffs
61
+
62
+ // TODO: define handoff conditions
63
+ `;
64
+ }
65
+
66
+ return `# AGENTS.md - Operating Rules
67
+
68
+ ## Session Startup
69
+
70
+ 1. Read \`SOUL.md\` — who you are
71
+ 2. Read \`USER.md\` — who you're helping
72
+ 3. Read \`MEMORY.md\` — your long-term memory
73
+ 4. Read \`memory/YYYY-MM-DD.md\` (today + yesterday) for recent context
74
+
75
+ ## Red Lines
76
+
77
+ ${neverRules}
78
+
79
+ ## Escalation Rules
80
+
81
+ ${agent.escalation || '// TODO: define escalation rules'}
82
+
83
+ ## Failure Behavior
84
+
85
+ ${agent.failure || '// TODO: define failure behavior'}
86
+ ${orchestrationSection}${handoffSection}`;
87
+ }
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Generate BOOTSTRAP.md for an agent.
3
+ * Pure function.
4
+ */
5
+ export function generateBootstrap(agent, _teamSpec) {
6
+ const inputsSection = agent.inputs
7
+ ? agent.inputs
8
+ .split(/[\n;]+/)
9
+ .map(s => s.trim())
10
+ .filter(Boolean)
11
+ .map(s => `- ${s}`)
12
+ .join('\n')
13
+ : '- // TODO: inputs not specified';
14
+
15
+ return `# BOOTSTRAP.md - Session Startup
16
+
17
+ ## Who You Are
18
+
19
+ **${agent.name}** — ${agent.mission || '// TODO: mission not specified'}
20
+
21
+ ## What Triggers You
22
+
23
+ ${inputsSection}
24
+
25
+ ## Startup Checklist
26
+
27
+ 1. Read \`SOUL.md\` — your identity and values
28
+ 2. Read \`USER.md\` — who you're helping
29
+ 3. Read \`MEMORY.md\` — long-term memory
30
+ 4. Read \`memory/YYYY-MM-DD.md\` — recent context
31
+ 5. Check your task queue
32
+
33
+ ## First Run
34
+
35
+ If this is your first session, introduce yourself and confirm your mission.
36
+ Update \`IDENTITY.md\` and \`USER.md\` with what you learn.
37
+
38
+ ---
39
+
40
+ _Delete this file once you're up and running. You won't need it again._
41
+ `;
42
+ }
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Generate IDENTITY.md for an agent.
3
+ * Pure function.
4
+ */
5
+ export function generateIdentity(agent, _teamSpec) {
6
+ const roleShort = agent.mission
7
+ ? agent.mission.split('.')[0].slice(0, 80)
8
+ : '// TODO';
9
+
10
+ return `# IDENTITY.md - Who Am I?
11
+
12
+ - **Name:** ${agent.name}
13
+ - **Role:** ${roleShort}
14
+ - **Vibe:** // TODO
15
+ - **Emoji:** // TODO
16
+ - **Avatar:** // TODO
17
+ `;
18
+ }
@@ -0,0 +1,81 @@
1
+ import { generateSoul } from './soul.js';
2
+ import { generateAgents } from './agents.js';
3
+ import { generateIdentity } from './identity.js';
4
+ import { generateUser } from './user.js';
5
+ import { generateTools } from './tools.js';
6
+ import { generateBootstrap } from './bootstrap.js';
7
+ import { generateMemory, generateMemoryReadme } from './memory.js';
8
+ import { generateTeam } from './team.js';
9
+ import { generateRouting } from './routing.js';
10
+
11
+ function generateSkillsReadme(agent, teamSpec) {
12
+ const recommended = teamSpec.capabilities?.recommendedSkills || [];
13
+ const agentSkills = recommended.filter(s => s.source === 'clawhub' || s.source === 'workspace');
14
+
15
+ const installLines = agentSkills.length > 0
16
+ ? agentSkills.map(s => `# ${s.name}: ${s.reason}\nclawhub install ${s.id}`).join('\n\n')
17
+ : '# Add workspace-local skills here\n# clawhub install <skill-id>';
18
+
19
+ return `# Workspace Skills — ${agent.name || agent.id}
20
+
21
+ Skills placed in this folder are **local to this agent only** and override any global skill with the same name.
22
+
23
+ ## How to install a skill into this workspace
24
+
25
+ \`\`\`bash
26
+ # From inside this workspace directory:
27
+ clawhub install <skill-id>
28
+
29
+ # Or specify the workspace explicitly:
30
+ clawhub install <skill-id> --workdir ~/.openclaw/workspace-${agent.id}
31
+ \`\`\`
32
+
33
+ ## Recommended skills for this agent
34
+
35
+ ${installLines}
36
+
37
+ ## Notes
38
+
39
+ - Skills here take precedence over bundled and managed (~/.openclaw/skills) skills of the same name.
40
+ - After installing, restart the OpenClaw gateway (or start a new session) for the skill to take effect.
41
+ - Browse all available skills at https://clawhub.ai
42
+ `;
43
+ }
44
+
45
+ /**
46
+ * Generate all workspace files for a TeamSpec.
47
+ * Returns [{ path, content }] in the canonical order.
48
+ */
49
+ export function generateFiles(teamSpec) {
50
+ const files = [];
51
+
52
+ // 1. team/TEAM.md
53
+ files.push({
54
+ path: 'team/TEAM.md',
55
+ content: generateTeam(teamSpec),
56
+ });
57
+
58
+ // 2. gateway/openclaw.routing.json5
59
+ files.push({
60
+ path: 'gateway/openclaw.routing.json5',
61
+ content: generateRouting(teamSpec),
62
+ });
63
+
64
+ // 3. Per-agent files, alphabetically by agentId
65
+ const sortedAgents = [...teamSpec.agents].sort((a, b) => a.id.localeCompare(b.id));
66
+
67
+ for (const agent of sortedAgents) {
68
+ const prefix = `workspace-${agent.id}`;
69
+ files.push({ path: `${prefix}/SOUL.md`, content: generateSoul(agent, teamSpec) });
70
+ files.push({ path: `${prefix}/AGENTS.md`, content: generateAgents(agent, teamSpec) });
71
+ files.push({ path: `${prefix}/IDENTITY.md`, content: generateIdentity(agent, teamSpec) });
72
+ files.push({ path: `${prefix}/USER.md`, content: generateUser(agent, teamSpec) });
73
+ files.push({ path: `${prefix}/TOOLS.md`, content: generateTools(agent, teamSpec) });
74
+ files.push({ path: `${prefix}/BOOTSTRAP.md`, content: generateBootstrap(agent, teamSpec) });
75
+ files.push({ path: `${prefix}/MEMORY.md`, content: generateMemory(agent, teamSpec) });
76
+ files.push({ path: `${prefix}/memory/README.md`, content: generateMemoryReadme(agent, teamSpec) });
77
+ files.push({ path: `${prefix}/skills/README.md`, content: generateSkillsReadme(agent, teamSpec) });
78
+ }
79
+
80
+ return files;
81
+ }
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Generate MEMORY.md and memory/README.md for an agent.
3
+ * Pure functions.
4
+ */
5
+ export function generateMemory(agent, _teamSpec) {
6
+ return `# MEMORY.md - Long-Term Memory
7
+
8
+ ## Role
9
+
10
+ **${agent.name}** — ${agent.mission || '// TODO'}
11
+
12
+ ## Goal
13
+
14
+ // TODO: Add long-term goals and current priorities
15
+
16
+ ## Active Context
17
+
18
+ // TODO: What are you currently working on?
19
+
20
+ ## Key Decisions
21
+
22
+ // TODO: Important decisions made so far
23
+
24
+ ## Lessons Learned
25
+
26
+ // TODO: What have you learned that future-you should know?
27
+
28
+ ---
29
+
30
+ _Update this file during sessions. This is your curated long-term memory._
31
+ `;
32
+ }
33
+
34
+ export function generateMemoryReadme(_agent, _teamSpec) {
35
+ return `# memory/ - Session Logs
36
+
37
+ ## Naming Convention
38
+
39
+ \`YYYY-MM-DD.md\` — one file per day.
40
+
41
+ ## What to Log
42
+
43
+ - Tasks completed
44
+ - Decisions made and why
45
+ - Context that will be useful next session
46
+ - Errors encountered and how they were resolved
47
+ - Links, IDs, and references for ongoing work
48
+
49
+ ## What NOT to Log
50
+
51
+ - Sensitive credentials (use environment variables)
52
+ - Redundant info already in SOUL.md or AGENTS.md
53
+
54
+ ---
55
+
56
+ _These are raw daily notes. Distill important learnings into MEMORY.md periodically._
57
+ `;
58
+ }
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Generate gateway/openclaw.routing.json5.
3
+ * Pure function — outputs JSON5 string with // TODO comments for missing data.
4
+ */
5
+ export function generateRouting(teamSpec) {
6
+ const { agents, routing, orchestration } = teamSpec;
7
+ const defaultAgentId = routing?.defaultAgentId || orchestration?.router_agent || agents[0]?.id;
8
+
9
+ const agentsList = agents.map(a => {
10
+ const isDefault = a.id === defaultAgentId;
11
+ const workspace = `~/.openclaw/workspace-${a.id}`;
12
+ if (isDefault) {
13
+ return ` { id: "${a.id}", default: true, workspace: "${workspace}" }`;
14
+ }
15
+ return ` { id: "${a.id}", workspace: "${workspace}" }`;
16
+ }).join(',\n');
17
+
18
+ const bindingsList = (routing?.bindings || []).map(b => {
19
+ const matchParts = Object.entries(b.match)
20
+ .map(([k, v]) => {
21
+ if (typeof v === 'object') {
22
+ return `${k}: ${JSON.stringify(v)}`;
23
+ }
24
+ return `${k}: "${v}"`;
25
+ })
26
+ .join(', ');
27
+ return ` { agentId: "${b.agentId}", match: { ${matchParts} } }`;
28
+ });
29
+
30
+ const bindingsSection = bindingsList.length === 0
31
+ ? ` // TODO: Add channel bindings\n // Example: { agentId: "${defaultAgentId}", match: { channel: "discord", accountId: "${defaultAgentId}" } }`
32
+ : bindingsList.join(',\n');
33
+
34
+ return `{
35
+ // OpenClaw Gateway Routing Config
36
+ // Generated by openclaw-agent-builder
37
+ // Edit as needed — this is JSON5, so comments are supported.
38
+
39
+ agents: {
40
+ list: [
41
+ ${agentsList}
42
+ ]
43
+ },
44
+
45
+ // Bindings — sorted by specificity: peer > guildId > teamId > accountId-exact > accountId:* > default
46
+ bindings: [
47
+ ${bindingsSection}
48
+ ]
49
+ }
50
+ `;
51
+ }
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Generate SOUL.md for an agent.
3
+ * Pure function — never invents content not in the TeamSpec.
4
+ */
5
+ export function generateSoul(agent, teamSpec) {
6
+ const otherAgents = teamSpec.agents.filter(a => a.id !== agent.id);
7
+
8
+ const whatIDo = agent.outputs
9
+ ? agent.outputs
10
+ .split(/[\n;]+/)
11
+ .map(s => s.trim())
12
+ .filter(Boolean)
13
+ .map(s => `- ${s}`)
14
+ .join('\n')
15
+ : '- // TODO: outputs not specified';
16
+
17
+ const teamSection = otherAgents.length > 0
18
+ ? otherAgents.map(a => `- **${a.name}** — ${a.mission || '// TODO: mission'}`).join('\n')
19
+ : teamSpec.team && teamSpec.team.name
20
+ ? `- Working solo within **${teamSpec.team.name}**`
21
+ : '- // TODO: team context';
22
+
23
+ const neverRules = agent.never && agent.never.length > 0
24
+ ? agent.never.map(r => `- **NEVER** ${r}`).join('\n')
25
+ : '- // TODO: define never rules';
26
+
27
+ return `# SOUL.md - Who I Am
28
+
29
+ I'm **${agent.name}** — ${agent.mission || '// TODO: mission not specified'}
30
+
31
+ ## My Purpose
32
+
33
+ ${agent.mission || '// TODO: describe agent purpose in detail'}
34
+
35
+ ## What I Do
36
+
37
+ ${whatIDo}
38
+
39
+ ## How I Operate
40
+
41
+ ${agent.failure ? `**Failure behavior:** ${agent.failure}` : '// TODO: failure behavior'}
42
+
43
+ ${agent.escalation ? `**Escalation:** ${agent.escalation}` : '// TODO: escalation rules'}
44
+
45
+ ## My Team
46
+
47
+ ${teamSection}
48
+
49
+ ## Hard Rules
50
+
51
+ ${neverRules}
52
+ `;
53
+ }
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Generate team/TEAM.md.
3
+ * Pure function.
4
+ */
5
+ export function generateTeam(teamSpec) {
6
+ const agentsTable = teamSpec.agents.map(a =>
7
+ `| ${a.id} | ${a.name} | ${a.mission || '// TODO'} |`
8
+ ).join('\n');
9
+
10
+ const handoffGraph = (teamSpec.handoffs || []).length > 0
11
+ ? teamSpec.handoffs.map(h =>
12
+ `- **${h.from}** → **${h.to}**: ${h.when} (${h.payload_contract})`
13
+ ).join('\n')
14
+ : '// TODO: No handoffs defined';
15
+
16
+ const sharedNever = (teamSpec.shared_constraints?.never || []).length > 0
17
+ ? teamSpec.shared_constraints.never.map(r => `- **NEVER** ${r}`).join('\n')
18
+ : '// TODO: Define shared constraints';
19
+
20
+ const memoryMode = teamSpec.shared_memory?.mode || 'separate';
21
+ const memoryLocation = teamSpec.shared_memory?.location || '// TODO';
22
+
23
+ const teamName = teamSpec.team?.name ||
24
+ (teamSpec.agents.length === 1 ? `${teamSpec.agents[0].name} Workspace` : '// TODO');
25
+
26
+ return `# TEAM.md - Team Charter
27
+
28
+ ## Team
29
+
30
+ **${teamName}**
31
+
32
+ ${teamSpec.team?.mission || '// TODO: Define team mission'}
33
+
34
+ ## Agents
35
+
36
+ | ID | Name | Mission |
37
+ |----|------|---------|
38
+ ${agentsTable}
39
+
40
+ ## Orchestration
41
+
42
+ - **Mode:** ${teamSpec.orchestration?.mode || '// TODO'}
43
+ - **Router Agent:** ${teamSpec.orchestration?.router_agent || '// TODO'}
44
+
45
+ ## Handoff Graph
46
+
47
+ ${handoffGraph}
48
+
49
+ ## Shared Constraints
50
+
51
+ ${sharedNever}
52
+
53
+ ## Memory Model
54
+
55
+ - **Mode:** ${memoryMode}
56
+ ${memoryMode === 'shared_summary' ? `- **Location:** ${memoryLocation}` : '- Each agent maintains their own MEMORY.md'}
57
+ `;
58
+ }
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Generate TOOLS.md for an agent.
3
+ * Pure function.
4
+ */
5
+ export function generateTools(agent, _teamSpec) {
6
+ const inputsSection = agent.inputs
7
+ ? agent.inputs
8
+ .split(/[\n;]+/)
9
+ .map(s => s.trim())
10
+ .filter(Boolean)
11
+ .map(s => `- ${s}`)
12
+ .join('\n')
13
+ : '- // TODO: inputs not specified';
14
+
15
+ const outputsSection = agent.outputs
16
+ ? agent.outputs
17
+ .split(/[\n;]+/)
18
+ .map(s => s.trim())
19
+ .filter(Boolean)
20
+ .map(s => `- ${s}`)
21
+ .join('\n')
22
+ : '- // TODO: outputs not specified';
23
+
24
+ return `# TOOLS.md - Local Notes
25
+
26
+ ## Inputs / Triggers
27
+
28
+ ${inputsSection}
29
+
30
+ ## Outputs
31
+
32
+ ${outputsSection}
33
+
34
+ ## Environment
35
+
36
+ // TODO: Add environment-specific details (API keys locations, SSH hosts, device names, etc.)
37
+
38
+ ## Skills
39
+
40
+ // TODO: List installed skills and their configurations
41
+ `;
42
+ }
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Generate USER.md for an agent.
3
+ * Pure function.
4
+ */
5
+ export function generateUser(agent, teamSpec) {
6
+ const teamName = teamSpec.team?.name || '// TODO';
7
+ const teamMission = teamSpec.team?.mission || '// TODO';
8
+
9
+ return `# USER.md - About Your Human
10
+
11
+ - **Name:** // TODO
12
+ - **What to call them:** // TODO
13
+ - **Pronouns:** // TODO
14
+ - **Timezone:** // TODO
15
+ - **Notes:** // TODO
16
+
17
+ ## Context
18
+
19
+ // TODO: Learn about the person you're helping. Update this as you go.
20
+
21
+ ## Team Context
22
+
23
+ - **Team:** ${teamName}
24
+ - **Team Mission:** ${teamMission}
25
+ - **Your Role:** ${agent.mission || '// TODO'}
26
+ `;
27
+ }
@@ -0,0 +1,56 @@
1
+ import express from 'express';
2
+ import { fileURLToPath } from 'url';
3
+ import path from 'path';
4
+ import { existsSync } from 'fs';
5
+ import generateRoute from './routes/generate.js';
6
+ import filesRoute from './routes/files.js';
7
+ import configRoute from './routes/config.js';
8
+ import validateRoute from './routes/validate.js';
9
+ import chatRoute from './routes/chat.js';
10
+ import channelRoute from './routes/channel.js';
11
+ import preflightRoute from './routes/preflight.js';
12
+ import capabilitiesRoute from './routes/capabilities.js';
13
+ import agentChatRoute from './routes/agentChat.js';
14
+
15
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
16
+
17
+ export async function createServer() {
18
+ const app = express();
19
+ app.use(express.json({ limit: '10mb' }));
20
+
21
+ // API routes
22
+ app.use('/api/generate', generateRoute);
23
+ app.use('/api', filesRoute);
24
+ app.use('/api', configRoute);
25
+ app.use('/api', validateRoute);
26
+ app.use('/api', chatRoute);
27
+ app.use('/api', channelRoute);
28
+ app.use('/api', preflightRoute);
29
+ app.use('/api', capabilitiesRoute);
30
+ app.use('/api', agentChatRoute);
31
+
32
+ if (process.env.NODE_ENV === 'development') {
33
+ // Vite dev server proxy
34
+ const { createServer: createViteServer } = await import('vite');
35
+ const vite = await createViteServer({
36
+ root: path.join(__dirname, '../client'),
37
+ server: { middlewareMode: true },
38
+ appType: 'spa',
39
+ });
40
+ app.use(vite.middlewares);
41
+ } else {
42
+ // Serve built client
43
+ const distPath = path.join(__dirname, '../dist');
44
+ if (!existsSync(path.join(distPath, 'index.html'))) {
45
+ console.error('\n ERROR: No built client found at dist/index.html');
46
+ console.error(' Run "npm run build" first, then start the server.\n');
47
+ process.exit(1);
48
+ }
49
+ app.use(express.static(distPath));
50
+ app.get('*', (_req, res) => {
51
+ res.sendFile(path.join(distPath, 'index.html'));
52
+ });
53
+ }
54
+
55
+ return app;
56
+ }