SPB Git

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%
4.6 KB · 106 lines typescript
Raw Blame History
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