spb/khaelor Public
KHAELOR — a terminal-native autonomous engineering agent powered by Anthropic.
TypeScript 82.9%
HTML 14.9%
CSS 1.1%
JavaScript 0.7%
1/**2 * KHAELOR3 * File: tests/context/system-prompt.test.ts4 * Description: System-prompt tests — instruction discovery precedence (temp dirs) and byte-stable tier construction.5 *6 * Author: Simon-Pierre Boucher7 * Contact: contact@spboucher.ai8 */910import { mkdtemp, mkdir, rm, writeFile } from "node:fs/promises";11import { tmpdir } from "node:os";12import { join } from "node:path";13import { afterEach, beforeEach, describe, expect, it } from "vitest";14import { buildSystemPrompt, discoverProjectInstructions } from "../../src/context/index.js";15import { LocalWorkspace } from "../../src/workspace/index.js";1617let root: string;1819beforeEach(async () => {20 root = await mkdtemp(join(tmpdir(), "khaelor-ctx-"));21});2223afterEach(async () => {24 await rm(root, { recursive: true, force: true });25});2627describe("discoverProjectInstructions", () => {28 it("orders global → project root → nested dirs toward cwd (CLAUDE.md §12 precedence)", async () => {29 const userDir = join(root, "userdir");30 const projectRoot = join(root, "proj");31 const nested = join(projectRoot, "packages");32 const deep = join(nested, "core");33 await mkdir(userDir, { recursive: true });34 await mkdir(deep, { recursive: true });35 await writeFile(join(userDir, "KHAELOR.md"), "global rules");36 await writeFile(join(projectRoot, "KHAELOR.md"), "project rules");37 await writeFile(join(nested, "CLAUDE.md"), "packages rules");38 await writeFile(join(deep, "AGENTS.md"), "core rules");3940 const reader = new LocalWorkspace(deep);41 const found = await discoverProjectInstructions(reader, { userDir, projectRoot });4243 expect(found.map((f) => ({ path: f.path, scope: f.scope, content: f.content }))).toEqual([44 { path: join(userDir, "KHAELOR.md"), scope: "user", content: "global rules" },45 { path: join(projectRoot, "KHAELOR.md"), scope: "project", content: "project rules" },46 { path: join(nested, "CLAUDE.md"), scope: "nested", content: "packages rules" },47 { path: join(deep, "AGENTS.md"), scope: "nested", content: "core rules" },48 ]);49 });5051 it("prefers KHAELOR.md over CLAUDE.md over AGENTS.md within one directory", async () => {52 const projectRoot = join(root, "proj");53 await mkdir(projectRoot, { recursive: true });54 await writeFile(join(projectRoot, "KHAELOR.md"), "khaelor wins");55 await writeFile(join(projectRoot, "CLAUDE.md"), "claude loses");56 await writeFile(join(projectRoot, "AGENTS.md"), "agents loses");5758 const reader = new LocalWorkspace(projectRoot);59 const found = await discoverProjectInstructions(reader, { projectRoot });60 expect(found).toHaveLength(1);61 expect(found[0]?.content).toBe("khaelor wins");6263 await rm(join(projectRoot, "KHAELOR.md"));64 const fallback = await discoverProjectInstructions(reader, { projectRoot });65 expect(fallback[0]?.content).toBe("claude loses");66 });6768 it("returns empty when nothing is discoverable and skips the user tier when userDir is omitted", async () => {69 const projectRoot = join(root, "empty");70 await mkdir(projectRoot, { recursive: true });71 const reader = new LocalWorkspace(projectRoot);72 expect(await discoverProjectInstructions(reader)).toEqual([]);73 });74});7576describe("buildSystemPrompt", () => {77 const options = {78 workingDirectory: "/home/u/proj",79 toolNames: ["read", "write", "edit", "grep", "glob", "bash", "process"],80 instructions: [81 { path: "/home/u/.khaelor/KHAELOR.md", scope: "user" as const, content: "be brief" },82 { path: "/home/u/proj/KHAELOR.md", scope: "project" as const, content: "use tabs" },83 ],84 };8586 it("builds three stable tiers in order: identity → tools → project instructions", () => {87 const tiers = buildSystemPrompt(options);88 expect(tiers.map((t) => t.name)).toEqual(["identity", "tools", "project-instructions"]);89 expect(tiers[0]?.text).toContain("KHAELOR");90 expect(tiers[0]?.text).toContain("/home/u/proj");91 expect(tiers[1]?.text).toContain("read, write, edit, grep, glob, bash, process");92 expect(tiers[2]?.text).toContain("be brief");93 expect(tiers[2]?.text).toContain("use tabs");94 expect(tiers[2]?.text.indexOf("be brief")).toBeLessThan(tiers[2]?.text.indexOf("use tabs") ?? -1);95 });9697 it("is byte-stable: same inputs produce byte-identical tiers", () => {98 expect(JSON.stringify(buildSystemPrompt(options))).toBe(JSON.stringify(buildSystemPrompt(options)));99 });100101 it("omits the project-instructions tier when no instructions were discovered", () => {102 const tiers = buildSystemPrompt({ ...options, instructions: [] });103 expect(tiers.map((t) => t.name)).toEqual(["identity", "tools"]);104 });105});106