import fs from "node:fs/promises"; import path from "node:path"; import { describe, expect, it } from "vitest"; import type { OpenClawConfig } from "../../../config/config.js"; import { makeTempWorkspace, writeWorkspaceFile } from "../../../test-helpers/workspace.js"; import type { AgentBootstrapHookContext } from "../../hooks.js"; import { createHookEvent } from "../../hooks.js"; import handler from "./handler.js"; function createBootstrapExtraConfig(paths: string[]): OpenClawConfig { return { hooks: { internal: { entries: { "bootstrap-extra-files": { enabled: true, paths, }, }, }, }, }; } async function createBootstrapContext(params: { workspaceDir: string; cfg: OpenClawConfig; sessionKey: string; rootFiles: Array<{ name: string; content: string }>; }): Promise { const bootstrapFiles = await Promise.all( params.rootFiles.map(async (file) => ({ name: file.name, path: await writeWorkspaceFile({ dir: params.workspaceDir, name: file.name, content: file.content, }), content: file.content, missing: false, })), ); return { workspaceDir: params.workspaceDir, bootstrapFiles, cfg: params.cfg, sessionKey: params.sessionKey, }; } describe("bootstrap-extra-files hook", () => { it("appends extra bootstrap files from configured patterns", async () => { const tempDir = await makeTempWorkspace("openclaw-bootstrap-extra-"); const extraDir = path.join(tempDir, "packages", "core"); await fs.mkdir(extraDir, { recursive: true }); await fs.writeFile(path.join(extraDir, "AGENTS.md"), "extra agents", "utf-8"); const cfg = createBootstrapExtraConfig(["packages/*/AGENTS.md"]); const context = await createBootstrapContext({ workspaceDir: tempDir, cfg, sessionKey: "agent:main:main", rootFiles: [{ name: "AGENTS.md", content: "root agents" }], }); const event = createHookEvent("agent", "bootstrap", "agent:main:main", context); await handler(event); const injected = context.bootstrapFiles.filter((f) => f.name === "AGENTS.md"); expect(injected).toHaveLength(2); expect(injected.some((f) => f.path.endsWith(path.join("packages", "core", "AGENTS.md")))).toBe( true, ); }); it("re-applies subagent bootstrap allowlist after extras are added", async () => { const tempDir = await makeTempWorkspace("openclaw-bootstrap-extra-subagent-"); const extraDir = path.join(tempDir, "packages", "persona"); await fs.mkdir(extraDir, { recursive: true }); await fs.writeFile(path.join(extraDir, "SOUL.md"), "evil", "utf-8"); const cfg = createBootstrapExtraConfig(["packages/*/SOUL.md"]); const context = await createBootstrapContext({ workspaceDir: tempDir, cfg, sessionKey: "agent:main:subagent:abc", rootFiles: [ { name: "AGENTS.md", content: "root agents" }, { name: "TOOLS.md", content: "root tools" }, ], }); const event = createHookEvent("agent", "bootstrap", "agent:main:subagent:abc", context); await handler(event); expect(context.bootstrapFiles.map((f) => f.name).toSorted()).toEqual(["AGENTS.md", "TOOLS.md"]); }); });