






















@@ -3,9 +3,11 @@ import os from "node:os";
33import path from "node:path";
44import { pathToFileURL } from "node:url";
55import { afterEach, describe, expect, it } from "vitest";
6+import { isHeartbeatContentEffectivelyEmpty } from "../auto-reply/heartbeat.js";
67import {
78resetWorkspaceTemplateDirCache,
89resolveWorkspaceTemplateDir,
10+resolveWorkspaceTemplateSearchDirs,
911} from "./workspace-templates.js";
10121113const tempDirs: string[] = [];
@@ -28,9 +30,9 @@ describe("resolveWorkspaceTemplateDir", () => {
2830const root = await makeTempRoot();
2931await fs.writeFile(path.join(root, "package.json"), JSON.stringify({ name: "openclaw" }));
303231-const templatesDir = path.join(root, "docs", "reference", "templates");
33+const templatesDir = path.join(root, "src", "agents", "templates");
3234await fs.mkdir(templatesDir, { recursive: true });
33-await fs.writeFile(path.join(templatesDir, "AGENTS.md"), "# ok\n");
35+await fs.writeFile(path.join(templatesDir, "HEARTBEAT.md"), "# ok\n");
34363537const distDir = path.join(root, "dist");
3638await fs.mkdir(distDir, { recursive: true });
@@ -40,7 +42,7 @@ describe("resolveWorkspaceTemplateDir", () => {
4042expect(resolved).toBe(templatesDir);
4143});
424443-it("falls back to package-root docs path when templates directory is missing", async () => {
45+it("falls back to package-root runtime path when templates directory is missing", async () => {
4446const root = await makeTempRoot();
4547await fs.writeFile(path.join(root, "package.json"), JSON.stringify({ name: "openclaw" }));
4648@@ -49,6 +51,42 @@ describe("resolveWorkspaceTemplateDir", () => {
4951const moduleUrl = pathToFileURL(path.join(distDir, "model-selection.mjs")).toString();
50525153const resolved = await resolveWorkspaceTemplateDir({ cwd: distDir, moduleUrl });
52-expect(path.normalize(resolved)).toBe(path.resolve("docs", "reference", "templates"));
54+expect(path.normalize(resolved)).toBe(path.resolve("src", "agents", "templates"));
55+});
56+57+it("includes docs templates as secondary search roots", async () => {
58+const root = await makeTempRoot();
59+await fs.writeFile(path.join(root, "package.json"), JSON.stringify({ name: "openclaw" }));
60+61+const runtimeTemplatesDir = path.join(root, "src", "agents", "templates");
62+const docsTemplatesDir = path.join(root, "docs", "reference", "templates");
63+await fs.mkdir(runtimeTemplatesDir, { recursive: true });
64+await fs.mkdir(docsTemplatesDir, { recursive: true });
65+66+const distDir = path.join(root, "dist");
67+await fs.mkdir(distDir, { recursive: true });
68+const moduleUrl = pathToFileURL(path.join(distDir, "model-selection.mjs")).toString();
69+70+const resolved = await resolveWorkspaceTemplateSearchDirs({ cwd: distDir, moduleUrl });
71+expect(resolved.slice(0, 2)).toEqual([runtimeTemplatesDir, docsTemplatesDir]);
72+});
73+74+it("keeps runtime templates free of docs frontmatter", async () => {
75+const runtimeTemplatesDir = path.resolve("src", "agents", "templates");
76+const entries = await fs.readdir(runtimeTemplatesDir);
77+const markdownFiles = entries.filter((entry) => entry.endsWith(".md"));
78+79+expect(markdownFiles).toContain("HEARTBEAT.md");
80+for (const fileName of markdownFiles) {
81+const content = await fs.readFile(path.join(runtimeTemplatesDir, fileName), "utf-8");
82+expect(content.startsWith("---")).toBe(false);
83+}
84+});
85+86+it("keeps the runtime HEARTBEAT.md template effectively empty", async () => {
87+const runtimeTemplatesDir = path.resolve("src", "agents", "templates");
88+const content = await fs.readFile(path.join(runtimeTemplatesDir, "HEARTBEAT.md"), "utf-8");
89+90+expect(isHeartbeatContentEffectivelyEmpty(content)).toBe(true);
5391});
5492});
此內容由慣性聚合(RSS閱讀器)自動聚合整理,僅供閱讀參考。 原文來自 — 版權歸原作者所有。