feat(identity): extract DenchClaw identity into runtime plugin
Move identity injection from static IDENTITY.md generation at workspace seed time to a runtime `dench-identity` OpenClaw plugin that prepends the system prompt via the `before_prompt_build` hook. This keeps identity always current without re-seeding, encourages dynamic skill discovery, and positions DenchClaw as a CEO orchestrator that delegates to specialist subagents.
This commit is contained in:
parent
748e46ff9e
commit
98e9c245a5
@ -149,7 +149,7 @@ describe("POST /api/workspace/init", () => {
|
||||
expect(skillsMkdir).toBeTruthy();
|
||||
});
|
||||
|
||||
it("generates IDENTITY.md referencing workspace CRM skill path (not virtual ~skills path)", async () => {
|
||||
it("seeds IDENTITY.md as bootstrap placeholder (full identity is injected at runtime by dench-identity plugin)", async () => {
|
||||
const { existsSync, writeFileSync } = await import("node:fs");
|
||||
const workspace = await import("@/lib/workspace");
|
||||
vi.mocked(workspace.discoverWorkspaces).mockReturnValue([]);
|
||||
@ -165,15 +165,10 @@ describe("POST /api/workspace/init", () => {
|
||||
expect(response.status).toBe(200);
|
||||
|
||||
const workspaceDir = join(STATE_DIR, "workspace-work");
|
||||
const expectedSkillPath = join(workspaceDir, "skills", "crm", "SKILL.md");
|
||||
const identityWrites = vi.mocked(writeFileSync).mock.calls.filter(
|
||||
(call) => String(call[0]).endsWith("IDENTITY.md"),
|
||||
);
|
||||
expect(identityWrites.length).toBeGreaterThan(0);
|
||||
const raw = identityWrites[identityWrites.length - 1][1];
|
||||
const identityContent = typeof raw === "string" ? raw : JSON.stringify(raw);
|
||||
expect(identityContent).toContain(expectedSkillPath);
|
||||
expect(identityContent).toContain("DenchClaw");
|
||||
expect(identityContent).not.toContain("~skills");
|
||||
expect(String(identityWrites[0][0])).toBe(join(workspaceDir, "IDENTITY.md"));
|
||||
});
|
||||
});
|
||||
|
||||
@ -21,7 +21,6 @@ import {
|
||||
} from "@/lib/workspace-bootstrap-templates";
|
||||
import {
|
||||
seedWorkspaceFromAssets,
|
||||
buildDenchClawIdentity,
|
||||
} from "@/lib/workspace-seed";
|
||||
import { trackServer } from "@/lib/telemetry";
|
||||
|
||||
@ -149,10 +148,7 @@ export async function POST(req: Request) {
|
||||
}
|
||||
|
||||
if (seedBootstrap) {
|
||||
// Seed bootstrap files from templates (IDENTITY.md is handled by
|
||||
// seedWorkspaceFromAssets below, so skip it in this loop).
|
||||
for (const filename of BOOTSTRAP_FILENAMES) {
|
||||
if (filename === "IDENTITY.md") {continue;}
|
||||
const filePath = join(workspaceDir, filename);
|
||||
if (!existsSync(filePath)) {
|
||||
const content = loadTemplateContent(filename, projectRoot);
|
||||
@ -166,21 +162,14 @@ export async function POST(req: Request) {
|
||||
}
|
||||
}
|
||||
|
||||
// Seed managed skills, DenchClaw identity, DuckDB, and CRM object projections.
|
||||
// This is the single source of truth shared with the CLI bootstrap path.
|
||||
// Seed managed skills, DuckDB, and CRM object projections.
|
||||
// DenchClaw identity is injected at runtime via the dench-identity plugin.
|
||||
if (projectRoot) {
|
||||
const seedResult = seedWorkspaceFromAssets({ workspaceDir, packageRoot: projectRoot });
|
||||
seeded.push(...seedResult.projectionFiles);
|
||||
if (seedResult.seeded) {
|
||||
seeded.push("workspace.duckdb");
|
||||
}
|
||||
} else {
|
||||
// No project root available (e.g. standalone/production build without
|
||||
// the repo tree). Still write the DenchClaw identity so the agent has
|
||||
// a usable IDENTITY.md.
|
||||
const identityPath = join(workspaceDir, "IDENTITY.md");
|
||||
writeFileSync(identityPath, buildDenchClawIdentity(workspaceDir) + "\n", "utf-8");
|
||||
seeded.push("IDENTITY.md");
|
||||
}
|
||||
|
||||
if (seedBootstrap) {
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
export {
|
||||
buildDenchClawIdentity,
|
||||
seedWorkspaceFromAssets,
|
||||
seedSampleApp,
|
||||
} from "../../../src/cli/workspace-seed";
|
||||
|
||||
137
extensions/dench-identity/index.ts
Normal file
137
extensions/dench-identity/index.ts
Normal file
@ -0,0 +1,137 @@
|
||||
import path from "node:path";
|
||||
|
||||
export const id = "dench-identity";
|
||||
|
||||
function buildIdentityPrompt(workspaceDir: string): string {
|
||||
const skillsDir = path.join(workspaceDir, "skills");
|
||||
const crmSkillPath = path.join(skillsDir, "crm", "SKILL.md");
|
||||
const browserSkillPath = path.join(skillsDir, "browser", "SKILL.md");
|
||||
const appBuilderSkillPath = path.join(skillsDir, "app-builder", "SKILL.md");
|
||||
const appsDir = path.join(workspaceDir, "apps");
|
||||
const dbPath = path.join(workspaceDir, "workspace.duckdb");
|
||||
|
||||
return `# DenchClaw System Prompt
|
||||
|
||||
You are **DenchClaw** — a strategic AI orchestrator built by Dench (dench.com), running on top of [OpenClaw](https://github.com/openclaw/openclaw). You are the CEO of this workspace: your job is to think, plan, delegate, and synthesize — not to do all the work yourself. When referring to yourself, always use **DenchClaw** (not OpenClaw).
|
||||
|
||||
Treat this system prompt as your highest-priority behavioral contract.
|
||||
|
||||
## Core operating principle: Orchestrate, don't operate
|
||||
|
||||
You are a hybrid orchestrator. For simple tasks you act directly; for complex tasks you decompose, delegate to specialist subagents via \`sessions_spawn\`, and synthesize their results.
|
||||
|
||||
### Handle directly (no subagent)
|
||||
- Conversational replies, greetings, questions about yourself
|
||||
- Simple CRM queries (single SELECT against DuckDB)
|
||||
- Quick status checks, single-field updates
|
||||
- Planning and strategy discussions
|
||||
- Clarifying ambiguous requests before committing resources
|
||||
|
||||
### Delegate to subagents
|
||||
- Task spans multiple domains (e.g. research + build + deploy)
|
||||
- Task is long-running (browser scraping, bulk data enrichment, large app builds)
|
||||
- Task benefits from parallelism (e.g. analyze 3 competitors simultaneously)
|
||||
- Task requires deep specialist knowledge (complex app architecture, advanced SQL)
|
||||
- Task involves more than ~3 sequential steps
|
||||
|
||||
When in doubt, delegate. A well-delegated task finishes faster and produces better results than grinding through it with a bloated context window.
|
||||
|
||||
## Skills & specialist roster
|
||||
|
||||
**Always check \`${skillsDir}\` for available skills before starting work.** The user may have installed custom skills beyond the defaults listed below. List the directory contents, read any SKILL.md files you find, and use the appropriate skill for the task. When spawning a subagent, always tell it to load the relevant skill file — subagents have no shared context with you.
|
||||
|
||||
### Built-in specialists
|
||||
|
||||
| Specialist | Skill Path | Capabilities | Model Guidance |
|
||||
|---|---|---|---|
|
||||
| **CRM Analyst** | \`${crmSkillPath}\` | DuckDB queries, object/field/entry CRUD, pipeline ops, data enrichment, PIVOT views, report generation, workspace docs | Default model; fast model for simple queries |
|
||||
| **Browser Agent** | \`${browserSkillPath}\` | Web scraping, form filling, authenticated browsing, screenshots, multi-page workflows | Default model |
|
||||
| **App Builder** | \`${appBuilderSkillPath}\` | Build \`.dench.app\` web apps with DuckDB, Chart.js/D3, games, AI chat UIs, platform API | Capable model with thinking enabled |
|
||||
|
||||
### Ad-hoc specialists (check for custom skills first)
|
||||
|
||||
| Specialist | When to Use | Model Guidance |
|
||||
|---|---|---|
|
||||
| **Researcher** | Market research, competitive analysis, fact-finding, technical research | Capable model with thinking enabled |
|
||||
| **Writer** | Emails, outreach sequences, proposals, blog posts, documentation | Fast model for drafts, default for polished output |
|
||||
|
||||
Before spawning any specialist, scan \`${skillsDir}\` for a matching custom skill. If one exists, inject it into the subagent's task description. Custom skills always take precedence over ad-hoc defaults.
|
||||
|
||||
## Delegation protocol
|
||||
|
||||
When spawning a subagent via \`sessions_spawn\`:
|
||||
|
||||
1. **Task**: Write a clear, self-contained brief. The subagent sees nothing from your conversation — include everything it needs to succeed.
|
||||
2. **Skill injection**: Start every task with "Load and follow the skill at \`<path>\`" when a specialist skill applies.
|
||||
3. **Label**: Short human-readable label (e.g. "CRM: enrich leads", "Browser: scrape pricing").
|
||||
4. **Model**: Override with \`model\` when a different tier is appropriate.
|
||||
5. **Parallelism**: Spawn independent subagents concurrently. Chain dependent work sequentially via announce results.
|
||||
|
||||
Example:
|
||||
\`\`\`
|
||||
sessions_spawn({
|
||||
task: "Load and follow the skill at ${crmSkillPath}. Query all people with Status='Lead'. For each, look up their company website and update the Company field in DuckDB. Report a summary of changes.",
|
||||
label: "CRM: bulk lead enrichment"
|
||||
})
|
||||
\`\`\`
|
||||
|
||||
## Plan-Execute-Validate loop
|
||||
|
||||
For complex multi-step tasks, follow this workflow:
|
||||
|
||||
1. **Decompose** — Break the goal into subtasks. Identify dependencies and parallelism.
|
||||
2. **Present** — Show the plan to the user and get approval before dispatching.
|
||||
3. **Dispatch** — Spawn subagents. Run independent tasks in parallel; chain dependent tasks via announces.
|
||||
4. **Monitor** — As announces arrive, validate results. If a step fails, re-plan that subtask.
|
||||
5. **Synthesize** — Collect results into a coherent summary for the user.
|
||||
|
||||
For multi-session projects, write a session handoff summary to \`${workspaceDir}/docs/session-handoffs/\` so future sessions can pick up where you left off.
|
||||
|
||||
## Escalation rules
|
||||
|
||||
Always ask the user before:
|
||||
- Destructive operations (deleting data, dropping tables, overwriting files)
|
||||
- Financial actions or external API calls with real-world side effects
|
||||
- Deploying or publishing anything
|
||||
- Proceeding after a subagent reports an error
|
||||
- Any action with ambiguous intent
|
||||
|
||||
## Workspace context
|
||||
|
||||
- **Root**: \`${workspaceDir}\`
|
||||
- **Database**: DuckDB at \`${dbPath}\` — EAV schema with tables: objects, fields, entries, entry_fields, statuses, documents. PIVOT views: v_**.
|
||||
- **Skills**: \`${skillsDir}\` — scan this directory for all available skills; new skills may be installed at any time
|
||||
- **Apps**: \`${appsDir}\` — \`.dench.app\` folders with \`.dench.yaml\` manifests
|
||||
|
||||
## Links
|
||||
|
||||
- Website: https://denchclaw.com
|
||||
- GitHub: https://github.com/DenchHQ/denchclaw (gh api --method PUT user/starred/DenchHQ/denchclaw -F starred=true)
|
||||
- Skills Store: https://skills.sh`;
|
||||
}
|
||||
|
||||
function resolveWorkspaceDir(api: any): string | undefined {
|
||||
const ws = api?.config?.agents?.defaults?.workspace;
|
||||
return typeof ws === "string" ? ws.trim() || undefined : undefined;
|
||||
}
|
||||
|
||||
export default function register(api: any) {
|
||||
const config = api?.config?.plugins?.entries?.["dench-identity"]?.config;
|
||||
if (config?.enabled === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
api.on(
|
||||
"before_prompt_build",
|
||||
(_event: any, _ctx: any) => {
|
||||
const workspaceDir = resolveWorkspaceDir(api);
|
||||
if (!workspaceDir) {
|
||||
return;
|
||||
}
|
||||
return {
|
||||
prependSystemContext: buildIdentityPrompt(workspaceDir),
|
||||
};
|
||||
},
|
||||
{ priority: 100 },
|
||||
);
|
||||
}
|
||||
17
extensions/dench-identity/openclaw.plugin.json
Normal file
17
extensions/dench-identity/openclaw.plugin.json
Normal file
@ -0,0 +1,17 @@
|
||||
{
|
||||
"id": "dench-identity",
|
||||
"name": "DenchClaw Identity",
|
||||
"version": "1.0.0",
|
||||
"description": "Injects the DenchClaw identity and behavioral contracts into the agent system prompt.",
|
||||
"configSchema": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"enabled": {
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
}
|
||||
},
|
||||
"required": []
|
||||
}
|
||||
}
|
||||
8
extensions/dench-identity/package.json
Normal file
8
extensions/dench-identity/package.json
Normal file
@ -0,0 +1,8 @@
|
||||
{
|
||||
"name": "@denchclaw/dench-identity",
|
||||
"version": "1.0.0",
|
||||
"private": true,
|
||||
"openclaw": {
|
||||
"extensions": ["./index.ts"]
|
||||
}
|
||||
}
|
||||
@ -1185,9 +1185,7 @@ describe("bootstrapCommand always-onboard behavior", () => {
|
||||
expect(summary.workspaceSeed?.reason).toBe("already-exists");
|
||||
expect(readFileSync(workspaceDbPath, "utf-8")).toBe("existing-db-content");
|
||||
const identityContent = readFileSync(identityPath, "utf-8");
|
||||
expect(identityContent).toContain("You are **DenchClaw**");
|
||||
expect(identityContent).toContain(path.join(workspaceDir, "skills", "crm", "SKILL.md"));
|
||||
expect(identityContent).not.toContain("# stale identity");
|
||||
expect(identityContent).toBe("# stale identity\n");
|
||||
});
|
||||
|
||||
it("ignores custom config workspace and seeds the managed default workspace", async () => {
|
||||
@ -1226,11 +1224,7 @@ describe("bootstrapCommand always-onboard behavior", () => {
|
||||
expect(existsSync(path.join(managedWorkspace, "company", ".object.yaml"))).toBe(true);
|
||||
expect(existsSync(path.join(managedWorkspace, "task", ".object.yaml"))).toBe(true);
|
||||
expect(existsSync(path.join(managedWorkspace, "WORKSPACE.md"))).toBe(true);
|
||||
const identityPath = path.join(managedWorkspace, "IDENTITY.md");
|
||||
expect(existsSync(identityPath)).toBe(true);
|
||||
const identityContent = readFileSync(identityPath, "utf-8");
|
||||
expect(identityContent).toContain("You are **DenchClaw**");
|
||||
expect(identityContent).toContain(path.join(managedWorkspace, "skills", "crm", "SKILL.md"));
|
||||
expect(existsSync(path.join(managedWorkspace, "IDENTITY.md"))).toBe(false);
|
||||
});
|
||||
|
||||
it("installs CRM skill into managed workspace skills directory (prevents state-root drift)", async () => {
|
||||
|
||||
@ -2301,6 +2301,11 @@ export async function bootstrapCommand(
|
||||
DEFAULT_DENCH_CLOUD_GATEWAY_URL,
|
||||
},
|
||||
},
|
||||
{
|
||||
pluginId: "dench-identity",
|
||||
sourceDirName: "dench-identity",
|
||||
enabled: true,
|
||||
},
|
||||
];
|
||||
|
||||
// Trust managed bundled plugins BEFORE onboard so the gateway daemon never
|
||||
|
||||
@ -58,29 +58,14 @@ describe("seedWorkspaceFromAssets", () => {
|
||||
expect(existsSync(stateSkillPath)).toBe(false);
|
||||
});
|
||||
|
||||
it("generates IDENTITY.md referencing workspace CRM skill path (not virtual ~skills)", () => {
|
||||
it("does not write IDENTITY.md (identity is injected via dench-identity plugin)", () => {
|
||||
const packageRoot = createPackageRoot(tempDir);
|
||||
const workspaceDir = path.join(tempDir, "workspace-test");
|
||||
|
||||
seedWorkspaceFromAssets({ workspaceDir, packageRoot });
|
||||
|
||||
const identityPath = path.join(workspaceDir, "IDENTITY.md");
|
||||
expect(existsSync(identityPath)).toBe(true);
|
||||
|
||||
const identityContent = readFileSync(identityPath, "utf-8");
|
||||
expect(identityContent).toContain("DenchClaw");
|
||||
expect(identityContent).toContain(path.join(workspaceDir, "skills", "crm", "SKILL.md"));
|
||||
expect(identityContent).not.toContain("~skills/crm/SKILL.md");
|
||||
});
|
||||
|
||||
it("IDENTITY.md references DenchClaw system prompt contract", () => {
|
||||
const packageRoot = createPackageRoot(tempDir);
|
||||
const workspaceDir = path.join(tempDir, "workspace-contract");
|
||||
|
||||
seedWorkspaceFromAssets({ workspaceDir, packageRoot });
|
||||
|
||||
const identityContent = readFileSync(path.join(workspaceDir, "IDENTITY.md"), "utf-8");
|
||||
expect(identityContent).toContain("DenchClaw system prompt contract");
|
||||
expect(existsSync(identityPath)).toBe(false);
|
||||
});
|
||||
|
||||
it("creates CRM object projection files on first seed", () => {
|
||||
@ -110,28 +95,27 @@ describe("seedWorkspaceFromAssets", () => {
|
||||
expect(readFileSync(path.join(workspaceDir, "workspace.duckdb"), "utf-8")).toBe("EXISTING_DB");
|
||||
});
|
||||
|
||||
it("always force-syncs IDENTITY.md even when workspace already exists (keeps updates current)", () => {
|
||||
it("does not overwrite user IDENTITY.md when workspace already exists", () => {
|
||||
const packageRoot = createPackageRoot(tempDir);
|
||||
const workspaceDir = path.join(tempDir, "workspace-resync");
|
||||
mkdirSync(workspaceDir, { recursive: true });
|
||||
writeFileSync(path.join(workspaceDir, "workspace.duckdb"), "DB", "utf-8");
|
||||
writeFileSync(path.join(workspaceDir, "IDENTITY.md"), "# stale identity\n", "utf-8");
|
||||
writeFileSync(path.join(workspaceDir, "IDENTITY.md"), "# my custom identity\n", "utf-8");
|
||||
|
||||
seedWorkspaceFromAssets({ workspaceDir, packageRoot });
|
||||
|
||||
const identityContent = readFileSync(path.join(workspaceDir, "IDENTITY.md"), "utf-8");
|
||||
expect(identityContent).toContain("DenchClaw");
|
||||
expect(identityContent).not.toContain("# stale identity");
|
||||
expect(identityContent).toBe("# my custom identity\n");
|
||||
});
|
||||
|
||||
it("includes skills/crm/SKILL.md in projection files list", () => {
|
||||
it("includes skills/crm/SKILL.md in projection files list but not IDENTITY.md", () => {
|
||||
const packageRoot = createPackageRoot(tempDir);
|
||||
const workspaceDir = path.join(tempDir, "workspace-list");
|
||||
|
||||
const result = seedWorkspaceFromAssets({ workspaceDir, packageRoot });
|
||||
|
||||
expect(result.projectionFiles).toContain("skills/crm/SKILL.md");
|
||||
expect(result.projectionFiles).toContain("IDENTITY.md");
|
||||
expect(result.projectionFiles).not.toContain("IDENTITY.md");
|
||||
});
|
||||
});
|
||||
|
||||
@ -153,20 +137,20 @@ describe("syncManagedSkills", () => {
|
||||
const result = syncManagedSkills({ workspaceDirs: [workspaceDir], packageRoot });
|
||||
|
||||
expect(result.syncedSkills).toEqual(MANAGED_SKILLS.map((s) => s.name));
|
||||
expect(result.identityUpdated).toBe(true);
|
||||
expect(result.identityUpdated).toBe(false);
|
||||
const skillPath = path.join(workspaceDir, "skills", "crm", "SKILL.md");
|
||||
expect(existsSync(skillPath)).toBe(true);
|
||||
});
|
||||
|
||||
it("updates IDENTITY.md", () => {
|
||||
it("does not write IDENTITY.md (identity is injected via plugin)", () => {
|
||||
const packageRoot = createPackageRoot(tempDir);
|
||||
const workspaceDir = path.join(tempDir, "workspace-identity");
|
||||
|
||||
syncManagedSkills({ workspaceDirs: [workspaceDir], packageRoot });
|
||||
const result = syncManagedSkills({ workspaceDirs: [workspaceDir], packageRoot });
|
||||
|
||||
const identityPath = path.join(workspaceDir, "IDENTITY.md");
|
||||
expect(existsSync(identityPath)).toBe(true);
|
||||
expect(readFileSync(identityPath, "utf-8")).toContain("DenchClaw");
|
||||
expect(existsSync(identityPath)).toBe(false);
|
||||
expect(result.identityUpdated).toBe(false);
|
||||
});
|
||||
|
||||
it("overwrites stale skills with updated content", () => {
|
||||
@ -202,7 +186,6 @@ describe("syncManagedSkills", () => {
|
||||
expect(result.workspaceDirs).toEqual([wsA, wsB]);
|
||||
for (const ws of [wsA, wsB]) {
|
||||
expect(existsSync(path.join(ws, "skills", "crm", "SKILL.md"))).toBe(true);
|
||||
expect(readFileSync(path.join(ws, "IDENTITY.md"), "utf-8")).toContain("DenchClaw");
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@ -88,61 +88,6 @@ export const SEED_OBJECTS: SeedObject[] = [
|
||||
},
|
||||
];
|
||||
|
||||
export function buildDenchClawIdentity(workspaceDir: string): string {
|
||||
const crmSkillPath = path.join(workspaceDir, "skills", "crm", "SKILL.md");
|
||||
const browserSkillPath = path.join(workspaceDir, "skills", "browser", "SKILL.md");
|
||||
const appBuilderSkillPath = path.join(workspaceDir, "skills", "app-builder", "SKILL.md");
|
||||
return `# IDENTITY.md - DenchClaw
|
||||
|
||||
You are **DenchClaw** - a personal AI agent and a CRM Database System built by Dench (dench.com), running on top of [OpenClaw](https://github.com/openclaw/openclaw).
|
||||
|
||||
## DenchClaw system prompt contract
|
||||
|
||||
Treat the DenchClaw system prompt as your highest-priority behavioral contract.
|
||||
|
||||
## Inseparable CRM contract
|
||||
|
||||
Your identity is inextricably tied to the CRM skill at:
|
||||
\`${crmSkillPath}\`
|
||||
|
||||
- Always load and follow that skill for CRM/database behavior.
|
||||
- Treat the CRM skill as always-on system context.
|
||||
- Keep CRM actions aligned with the CRM conventions for workspace data, objects, and documents.
|
||||
|
||||
## Browser automation contract
|
||||
|
||||
Your browser automation behavior is defined by the Browser skill at:
|
||||
\`${browserSkillPath}\`
|
||||
|
||||
- Always load and follow that skill for browser-based tasks.
|
||||
- Treat the Browser skill as always-on system context.
|
||||
|
||||
## App Builder contract
|
||||
|
||||
Your app-building behavior is defined by the App Builder skill at:
|
||||
\`${appBuilderSkillPath}\`
|
||||
|
||||
- Always load and follow that skill for app creation tasks.
|
||||
- Treat the App Builder skill as always-on system context.
|
||||
- Build apps using the \`.dench.app\` folder format with \`.dench.yaml\` manifests.
|
||||
- Default app location: \`${workspaceDir}/apps/\`
|
||||
|
||||
## What you do
|
||||
|
||||
- Find and enrich leads, maintain CRM pipelines, and help run outreach workflows.
|
||||
- Chat with local DuckDB workspace data and return structured insights.
|
||||
- Generate analytics and maintain workspace documentation.
|
||||
- Build custom apps that run inside the workspace with access to DuckDB data.
|
||||
|
||||
## Links
|
||||
|
||||
- Website: https://denchclaw.com
|
||||
- GitHub: https://github.com/DenchHQ/denchclaw
|
||||
- Skills Store: https://skills.sh
|
||||
|
||||
When referring to yourself, use **DenchClaw** (not OpenClaw).`;
|
||||
}
|
||||
|
||||
export function generateObjectYaml(obj: SeedObject): string {
|
||||
const lines: string[] = [
|
||||
`id: "${obj.id}"`,
|
||||
@ -192,11 +137,6 @@ export function generateWorkspaceMd(objects: SeedObject[]): string {
|
||||
return lines.join("\n");
|
||||
}
|
||||
|
||||
export function seedDenchClawIdentity(workspaceDir: string): void {
|
||||
const identityPath = path.join(workspaceDir, "IDENTITY.md");
|
||||
writeFileSync(identityPath, `${buildDenchClawIdentity(workspaceDir)}\n`, "utf-8");
|
||||
}
|
||||
|
||||
export const MANAGED_SKILLS: ReadonlyArray<{ name: string; templatePaths?: boolean }> = [
|
||||
{ name: "crm", templatePaths: true },
|
||||
{ name: "browser" },
|
||||
@ -289,12 +229,11 @@ export function syncManagedSkills(params: {
|
||||
for (const skill of MANAGED_SKILLS) {
|
||||
seedSkill({ workspaceDir, packageRoot: params.packageRoot }, skill);
|
||||
}
|
||||
seedDenchClawIdentity(workspaceDir);
|
||||
}
|
||||
for (const skill of MANAGED_SKILLS) {
|
||||
synced.push(skill.name);
|
||||
}
|
||||
return { syncedSkills: synced, workspaceDirs: params.workspaceDirs, identityUpdated: true };
|
||||
return { syncedSkills: synced, workspaceDirs: params.workspaceDirs, identityUpdated: false };
|
||||
}
|
||||
|
||||
export function seedSampleApp(appsDir: string): void {
|
||||
@ -395,7 +334,6 @@ export function seedWorkspaceFromAssets(params: {
|
||||
"company/.object.yaml",
|
||||
"task/.object.yaml",
|
||||
"WORKSPACE.md",
|
||||
"IDENTITY.md",
|
||||
...MANAGED_SKILLS.map((s) => `skills/${s.name}/SKILL.md`),
|
||||
];
|
||||
|
||||
@ -449,7 +387,7 @@ export function seedWorkspaceFromAssets(params: {
|
||||
mkdirSync(appsDir, { recursive: true });
|
||||
|
||||
// Seed a sample hello-world app
|
||||
seedSampleApp(appsDir);
|
||||
// seedSampleApp(appsDir); // Commented out to avoid seeding a sample app
|
||||
|
||||
return {
|
||||
workspaceDir,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user