refactor(omo-codex): port ultrawork hook to typescript
This commit is contained in:
@@ -0,0 +1,66 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
import { isUltraworkPrompt, runUserPromptSubmitHook } from "../src/codex-hook.js";
|
||||
|
||||
describe("codex ultrawork hook", () => {
|
||||
it("#given ultrawork prompt #when hook runs #then emits directive", () => {
|
||||
// given
|
||||
const payload = {
|
||||
hook_event_name: "UserPromptSubmit",
|
||||
prompt: "please ulw this change",
|
||||
};
|
||||
|
||||
// when
|
||||
const output = runUserPromptSubmitHook(payload);
|
||||
|
||||
// then
|
||||
expect(output).toMatch(/^<ultrawork-mode>/);
|
||||
expect(output).toMatch(/First user-visible line this turn MUST be exactly:/);
|
||||
});
|
||||
|
||||
it("#given identifier-like ulw #when hook runs #then does not emit directive", () => {
|
||||
// given
|
||||
const payload = {
|
||||
hook_event_name: "UserPromptSubmit",
|
||||
prompt: "refactor ulw_helper.ts",
|
||||
};
|
||||
|
||||
// when
|
||||
const output = runUserPromptSubmitHook(payload);
|
||||
|
||||
// then
|
||||
expect(output).toBe("");
|
||||
expect(isUltraworkPrompt("ulw_helper.ts")).toBe(false);
|
||||
});
|
||||
|
||||
it("#given malformed or empty input #when hook runs #then exits with empty output", () => {
|
||||
// given
|
||||
const inputs = [undefined, {}, { hook_event_name: "UserPromptSubmit", prompt: "" }] as const;
|
||||
|
||||
// when
|
||||
const outputs = inputs.map((input) => runUserPromptSubmitHook(input));
|
||||
|
||||
// then
|
||||
expect(outputs).toEqual(["", "", ""]);
|
||||
});
|
||||
|
||||
it("#given directive #when inspected #then keeps manual QA and cleanup invariants", () => {
|
||||
// given
|
||||
const payload = {
|
||||
hook_event_name: "UserPromptSubmit",
|
||||
prompt: "please ultrawork",
|
||||
};
|
||||
|
||||
// when
|
||||
const output = runUserPromptSubmitHook(payload);
|
||||
|
||||
// then
|
||||
expect(output).toMatch(/# Manual-QA channels/);
|
||||
expect(output).toMatch(/TESTS ALONE NEVER PROVE DONE/);
|
||||
expect(output).toMatch(/1\. HTTP call/);
|
||||
expect(output).toMatch(/2\. tmux/);
|
||||
expect(output).toMatch(/3\. Browser use/);
|
||||
expect(output).toMatch(/4\. Computer use/);
|
||||
expect(output).toMatch(/CLEANUP \(PAIRED/);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,78 @@
|
||||
import { readFileSync } from "node:fs";
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
type PackageJson = {
|
||||
readonly type: string;
|
||||
readonly packageManager: string;
|
||||
readonly bin: Record<string, string>;
|
||||
readonly files: readonly string[];
|
||||
readonly scripts: Record<string, string>;
|
||||
};
|
||||
|
||||
describe("codex ultrawork package metadata", () => {
|
||||
it("#given package metadata #when inspected #then hook ships as built TypeScript", () => {
|
||||
// given
|
||||
const packageJson = readPackageJson("package.json");
|
||||
const hooksJson = readJson("hooks/hooks.json");
|
||||
const cliSource = readFileSync("src/cli.ts", "utf8");
|
||||
|
||||
// when
|
||||
const packageFiles = packageJson.files;
|
||||
const hookCommands = collectHookCommandsFromValue(hooksJson);
|
||||
const pluginRoot = ["$", "{PLUGIN_ROOT}"].join("");
|
||||
|
||||
// then
|
||||
expect(packageJson.type).toBe("module");
|
||||
expect(packageJson.packageManager).toBe("npm@11.12.1");
|
||||
expect(packageJson.bin["codex-ultrawork"]).toBe("./dist/cli.js");
|
||||
expect(packageJson.scripts["build"]).toBe("tsc -p tsconfig.build.json");
|
||||
expect(packageJson.scripts["test"]).toBe("vitest --run");
|
||||
expect(packageFiles).toContain("dist");
|
||||
expect(packageFiles).toContain("directive.md");
|
||||
expect(packageFiles).not.toContain("hooks/ultrawork-detector.py");
|
||||
expect(cliSource.startsWith("#!/usr/bin/env node")).toBe(true);
|
||||
expect(hookCommands).toContain(`node "${pluginRoot}/dist/cli.js" hook user-prompt-submit`);
|
||||
expect(hookCommands).not.toContainEqual(expect.stringMatching(/\bpython3?\b|ultrawork-detector\.py/));
|
||||
});
|
||||
});
|
||||
|
||||
function readJson(path: string): unknown {
|
||||
return JSON.parse(readFileSync(path, "utf8"));
|
||||
}
|
||||
|
||||
function readPackageJson(path: string): PackageJson {
|
||||
const parsed = readJson(path);
|
||||
if (!isPackageJson(parsed)) throw new TypeError(`Invalid package metadata: ${path}`);
|
||||
return parsed;
|
||||
}
|
||||
|
||||
function collectHookCommandsFromValue(value: unknown): readonly string[] {
|
||||
if (typeof value === "string") return [];
|
||||
if (Array.isArray(value)) return value.flatMap(collectHookCommandsFromValue);
|
||||
if (!isRecord(value)) return [];
|
||||
const ownCommand = typeof value["command"] === "string" ? [value["command"]] : [];
|
||||
return [...ownCommand, ...Object.values(value).flatMap(collectHookCommandsFromValue)];
|
||||
}
|
||||
|
||||
function isPackageJson(value: unknown): value is PackageJson {
|
||||
return (
|
||||
isRecord(value) &&
|
||||
value["type"] === "module" &&
|
||||
value["packageManager"] === "npm@11.12.1" &&
|
||||
isStringRecord(value["bin"]) &&
|
||||
isStringArray(value["files"]) &&
|
||||
isStringRecord(value["scripts"])
|
||||
);
|
||||
}
|
||||
|
||||
function isStringArray(value: unknown): value is readonly string[] {
|
||||
return Array.isArray(value) && value.every((item) => typeof item === "string");
|
||||
}
|
||||
|
||||
function isStringRecord(value: unknown): value is Record<string, string> {
|
||||
return isRecord(value) && Object.values(value).every((item) => typeof item === "string");
|
||||
}
|
||||
|
||||
function isRecord(value: unknown): value is Record<string, unknown> {
|
||||
return typeof value === "object" && value !== null && !Array.isArray(value);
|
||||
}
|
||||
Reference in New Issue
Block a user