Files
oh-my-opencode/packages/omo-codex/plugin/components/ulw-loop/test/package-smoke.test.ts
T
2026-05-30 20:55:04 +09:00

165 lines
6.9 KiB
TypeScript

// biome-ignore-all format: smoke test pulls verbatim JSON for structural assertion.
import { readFile, stat } from "node:fs/promises";
import { dirname, join, resolve } from "node:path";
import { fileURLToPath } from "node:url";
import { describe, expect, it } from "vitest";
const repoRoot = resolve(dirname(fileURLToPath(import.meta.url)), "..");
async function readText(relative: string): Promise<string> {
return readFile(join(repoRoot, relative), "utf8");
}
async function readJson(relative: string): Promise<unknown> {
return JSON.parse(await readText(relative));
}
describe("package.json", () => {
it("declares ESM + npm + Node >=20", async () => {
const pkg = await readJson("package.json") as Record<string, unknown>;
expect(pkg["type"]).toBe("module");
expect(pkg["packageManager"]).toBe("npm@11.12.1");
expect((pkg["engines"] as Record<string, unknown>)["node"]).toBe(">=20.0.0");
});
it("exposes the omo binary pointing at dist/cli.js", async () => {
const pkg = await readJson("package.json") as Record<string, unknown>;
const bin = pkg["bin"] as Record<string, string>;
expect(bin["omo"]).toBe("./dist/cli.js");
});
it("ships the expected files for npm publish", async () => {
const pkg = await readJson("package.json") as Record<string, unknown>;
const files = pkg["files"] as readonly string[];
expect(files).toContain("dist");
expect(files).toContain("hooks");
expect(files).toContain("skills");
expect(files).not.toContain(".codex-plugin");
});
});
describe("component plugin identity", () => {
it("is owned by the aggregate OMO plugin root", async () => {
await expect(readText(".codex-plugin/plugin.json")).rejects.toMatchObject({ code: "ENOENT" });
});
});
describe("hooks/hooks.json", () => {
it("registers UserPromptSubmit with PLUGIN_ROOT interpolation", async () => {
const hooks = await readJson("hooks/hooks.json") as Record<string, unknown>;
const events = (hooks["hooks"] as Record<string, unknown>)["UserPromptSubmit"] as readonly Record<string, unknown>[];
expect(events.length).toBeGreaterThan(0);
const command = ((events[0]?.["hooks"] as readonly Record<string, unknown>[])[0]?.["command"]) as string;
expect(command).toContain(`$${"{PLUGIN_ROOT}"}`);
expect(command).toContain("dist/cli.js");
expect(command).toContain("hook user-prompt-submit");
});
it("#given ulw-loop component is enabled #when hooks are inspected #then create_goal PreToolUse guard is registered", async () => {
const text = await readText("hooks/hooks.json");
expect(text).toContain('"PreToolUse"');
expect(text).toContain('"matcher": "^create_goal$"');
expect(text).toContain("hook pre-tool-use");
});
});
describe("src/cli.ts", () => {
it("starts with #!/usr/bin/env node shebang", async () => {
const text = await readText("src/cli.ts");
expect(text.split("\n")[0]).toBe("#!/usr/bin/env node");
});
});
describe("skills/ulw-loop/SKILL.md", () => {
it("exists", async () => {
const info = await stat(join(repoRoot, "skills/ulw-loop/SKILL.md"));
expect(info.isFile()).toBe(true);
});
it("#given Codex skill hinting #when ulw-loop skill metadata is inspected #then ulw-loop is the primary mention name", async () => {
const text = await readText("skills/ulw-loop/SKILL.md");
expect(text).toMatch(/^---\nname: ulw-loop\n/m);
expect(text).toContain("Goal-like loop that uses ultrawork mode to decompose work into systematic, evidence-bound steps.");
expect(text).toContain("short-description: Goal-like ultrawork loop for systematic decomposition");
});
it("#given Codex dollar hinting #when querying ulw-loop #then ulw-loop surfaces the ulw-loop alias", async () => {
const text = await readText("skills/ulw-loop/agents/openai.yaml");
expect(text).toContain('display_name: "ulw loop"');
expect(text).not.toContain("ulw-loop / ulw-loop");
expect(text).toContain('short_description: "Goal-like ultrawork loop for systematic decomposition"');
expect(text).toContain("Use $ulw-loop");
});
it("#given Codex dollar hinting #when querying ulw-loop #then ulw-loop remains discoverable as an alias", async () => {
const text = await readText("skills/ulw-loop/agents/openai.yaml");
expect(text).toContain("search_terms:");
expect(text).toContain('- "ulw-loop"');
});
it("references the success criteria and record-evidence vocabulary", async () => {
const text = await readText("skills/ulw-loop/SKILL.md");
expect(text.toLowerCase()).toMatch(/success criteria|successcriteria/);
expect(text.toLowerCase()).toContain("record-evidence");
});
it("#given omo is absent from PATH #when bootstrap instructions are read #then local cached CLI fallback is documented", async () => {
const text = await readText("skills/ulw-loop/SKILL.md");
expect(text).toContain("If `omo` is absent from PATH");
expect(text).toContain("ULW_LOOP_CLI");
expect(text).toContain("components/ulw-loop/dist/cli.js");
});
it("#given empty PATH #when bootstrap instructions are read #then handles empty PATH without losing notepad bootstrap", async () => {
const text = await readText("skills/ulw-loop/SKILL.md");
expect(text).toContain("If PATH is empty");
expect(text).toContain("ULW_LOOP_NODE");
expect(text).toContain(".omo/ulw-loop/bootstrap-notepad.md");
expect(text).not.toContain("ls -1");
});
it("uses the .omo workspace path", async () => {
const text = await readText("skills/ulw-loop/SKILL.md");
expect(text).toContain(".omo/ulw-loop");
});
it("#given long Codex runs #when worker guidance is inspected #then avoids context-expensive agent polling", async () => {
const text = await readText("skills/ulw-loop/SKILL.md");
expect(text).toMatch(/list_agents/);
expect(text).toMatch(/polling or status tool/);
expect(text).toMatch(/replay large agent status and latest-message payloads/);
expect(text).toMatch(/Track spawned agent names locally/);
expect(text).toMatch(/wait_agent.*completion/);
expect(text).toMatch(/targeted followups only when needed/);
expect(text).toMatch(/close_agent.*after integrating each result/);
expect(text).toContain("Every worker message MUST carry");
expect(text).toContain("Each worker does strict TDD");
});
});
describe("source LOC budget", () => {
it("every source file stays at or under 250 pure LOC", async () => {
const files = [
"src/types.ts", "src/paths.ts", "src/plan-io.ts", "src/plan-crud.ts", "src/goal-status.ts",
"src/evidence.ts", "src/quality-gate.ts", "src/checkpoint.ts", "src/review-blockers.ts",
"src/steering.ts", "src/codex-goal-instruction.ts", "src/codex-goal-snapshot.ts", "src/codex-hook.ts",
"src/cli.ts", "src/cli-arg-parser.ts", "src/cli-output.ts", "src/cli-steering.ts", "src/cli-commands.ts",
];
for (const file of files) {
const text = await readText(file);
const pure = text.split("\n").filter((line) => {
const trimmed = line.trim();
return trimmed.length > 0 && !trimmed.startsWith("//");
}).length;
expect(pure, `${file} pure LOC`).toBeLessThanOrEqual(250);
}
});
});