import { spawn } from "node:child_process"; import { readFile } from "node:fs/promises"; import { fileURLToPath } from "node:url"; const checks: string[] = []; const failures: string[] = []; const root = new URL("..", import.meta.url); const rootPath = fileURLToPath(root); const textFiles = new Map(); async function read(relativePath: string): Promise { const cached = textFiles.get(relativePath); if (cached !== undefined) { return cached; } const text = await readFile(new URL(relativePath, root), "utf8"); textFiles.set(relativePath, text); return text; } async function expectIncludes(file: string, needle: string, label?: string): Promise { const text = await read(file); checks.push(`${file}: ${label ?? needle}`); if (!text.includes(needle)) { failures.push(`${file} is missing ${JSON.stringify(needle)}`); } } async function expectExcludes(file: string, needle: string, label?: string): Promise { const text = await read(file); checks.push(`${file}: excludes ${label ?? needle}`); if (text.includes(needle)) { failures.push(`${file} should not contain ${JSON.stringify(needle)}`); } } async function main(): Promise { const helpProc = spawn(process.execPath, ["--import", "tsx", "packages/codex-client/src/cli/index.ts", "--help"], { cwd: rootPath, }); const [help, helpError, helpExit] = await Promise.all([ collectText(helpProc.stdout), collectText(helpProc.stderr), exitCodeFor(helpProc), ]); if (helpExit !== 0) { process.stderr.write(helpError); process.stderr.write(help); process.exit(helpExit); } const cliDoc = await read("docs/pages/reference/cli.md"); const requiredCliLines = [ "codex-flows fetch [--json] [--no-color]", "codex-flows app [params-json]", "codex-flows workspace doctor [--mode auto|local|actions] [--json]", "codex-flows workspace tick [--mode auto|local|actions]", "codex-flows workspace run [--mode auto|local|actions]", "codex-flows memories transplant global-to-workspace [--apply]", "codex-flows memories transplant workspace-to-global [--apply]", "codex-flows threads locate [--codex-home ]", "codex-flows threads inspect [--codex-home ]", "codex-flows threads install-rollout [--codex-home ] [--replace]", "codex-flows threads transplant --from-codex-home --to-codex-home [--replace]", "codex-flows pack inspect [--json]", "codex-flows pack add [--apply] [--include ] [--exclude ]", "codex-flows pack doctor [--json]", "codex-flows pack list [--json]", "codex-flows flow dispatch --event ", "--merge codex", ]; for (const line of requiredCliLines) { checks.push(`CLI help includes ${line}`); if (!help.includes(line)) { failures.push(`CLI help is missing ${JSON.stringify(line)}`); } checks.push(`CLI docs include ${line}`); if (!cliDoc.includes(line)) { failures.push(`docs/pages/reference/cli.md is missing ${JSON.stringify(line)}`); } } await expectExcludes("README.md", "Codex Bare"); await expectExcludes("README.md", "DEVELOP.md"); await expectExcludes("package.json", "codex-bare"); await expectExcludes("package.json", "Thin web UI"); await expectExcludes("SECURITY.md", "codex-bare"); await expectIncludes("SECURITY.md", "Memory transplant"); await expectIncludes("README.md", "docs/pages/guides/workspace-autonomy.md"); await expectIncludes("README.md", "docs/pages/guides/memory-transplant.md"); await expectIncludes("README.md", "docs/pages/guides/thread-transplant.md"); await expectIncludes("README.md", "docs/pages/guides/install-pack-repos.md"); await expectIncludes("docs/tome.config.js", "\"guides/workspace-autonomy\""); await expectIncludes("docs/tome.config.js", "\"guides/memory-transplant\""); await expectIncludes("docs/tome.config.js", "\"guides/thread-transplant\""); await expectIncludes("docs/tome.config.js", "\"guides/install-pack-repos\""); await expectIncludes("docs/tome.config.js", "RELEASE.md"); await expectIncludes("docs/index.html", "codex-flows"); await expectIncludes("docs/pages/index.md", "Workspace autonomy"); await expectIncludes("docs/pages/index.md", "Memory transplant"); await expectIncludes("docs/pages/index.md", "Thread Transplant"); await expectIncludes("docs/pages/index.md", "Pack Install"); await expectIncludes("docs/pages/index.md", "@peezy.tech/codex-flows"); await expectIncludes("docs/pages/reference/packages.md", "workspace autonomy"); await expectIncludes("docs/pages/reference/packages.md", "memory transplant"); await expectIncludes("docs/pages/reference/packages.md", "@peezy.tech/codex-flows/threads"); await expectIncludes("docs/pages/guides/workspace-autonomy.md", "[workspace]"); await expectIncludes("docs/pages/guides/workspace-autonomy.md", ".codex/workspace/local"); await expectIncludes("docs/pages/guides/workspace-autonomy.md", ".codex/workspace/actions"); await expectIncludes("docs/pages/guides/workspace-autonomy.md", "CODEX_WORKSPACE_MODE=actions"); await expectIncludes("docs/pages/guides/memory-transplant.md", "MEMORY.md"); await expectIncludes("docs/pages/guides/memory-transplant.md", "memory_summary.md"); await expectIncludes("docs/pages/guides/memory-transplant.md", "raw_memories.md"); await expectIncludes("docs/pages/guides/memory-transplant.md", "rollout_summaries/*.md"); await expectIncludes("docs/pages/guides/memory-transplant.md", "sqlite"); await expectIncludes("docs/pages/guides/memory-transplant.md", "skills"); await expectIncludes("docs/pages/guides/thread-transplant.md", "install-rollout"); await expectIncludes("docs/pages/guides/thread-transplant.md", "sessions///
/.jsonl"); await expectIncludes("docs/pages/guides/thread-transplant.md", "--replace"); await expectIncludes("docs/pages/guides/thread-transplant.md", "not app-server-native import"); await expectIncludes("docs/pages/guides/install-pack-repos.md", "pack repo"); await expectIncludes("docs/pages/guides/install-pack-repos.md", ".codex/pack-lock.json"); await expectIncludes("docs/pages/guides/install-pack-repos.md", ".agents/plugins/marketplace.json"); await expectIncludes("docs/pages/guides/install-pack-repos.md", "[features].plugin_hooks = true"); await expectIncludes("packages/codex-client/README.md", "codex-flows workspace doctor"); await expectIncludes("packages/codex-client/README.md", "codex-flows memories transplant global-to-workspace"); await expectIncludes("packages/codex-client/README.md", "codex-flows threads transplant "); await expectIncludes("packages/codex-client/README.md", "codex-flows pack inspect owner/repo"); if (failures.length > 0) { for (const failure of failures) { console.error(`docs check failed: ${failure}`); } console.error(`docs check inspected ${checks.length} conditions`); process.exit(1); } console.log(`docs check passed (${checks.length} conditions)`); } function collectText(stream: NodeJS.ReadableStream | null): Promise { return new Promise((resolve, reject) => { let output = ""; if (!stream) { resolve(output); return; } stream.setEncoding("utf8"); stream.on("data", (chunk: string) => { output += chunk; }); stream.once("error", reject); stream.once("end", () => resolve(output)); }); } function exitCodeFor(child: ReturnType): Promise { return new Promise((resolve, reject) => { child.once("error", reject); child.once("exit", (code) => resolve(code)); }); } void main().catch((error) => { console.error(error instanceof Error ? error.message : String(error)); process.exit(1); });