diff --git a/.gitignore b/.gitignore index 5ef6a52..e3a7542 100644 --- a/.gitignore +++ b/.gitignore @@ -39,3 +39,4 @@ yarn-error.log* # typescript *.tsbuildinfo next-env.d.ts +.env*.local diff --git a/.oxfmtrc.json b/.oxfmtrc.json new file mode 100644 index 0000000..5106a3f --- /dev/null +++ b/.oxfmtrc.json @@ -0,0 +1,5 @@ +{ + "$schema": "./node_modules/oxfmt/configuration_schema.json", + "printWidth": 120, + "proseWrap": "preserve" +} diff --git a/.oxlintrc.json b/.oxlintrc.json new file mode 100644 index 0000000..e943e8f --- /dev/null +++ b/.oxlintrc.json @@ -0,0 +1,10 @@ +{ + "$schema": "./node_modules/oxlint/configuration_schema.json", + "plugins": ["react", "jsx-a11y", "nextjs"], + "env": { + "browser": true, + "node": true, + "es2024": true + }, + "ignorePatterns": ["node_modules/**", ".next/**", "out/**", "build/**", "next-env.d.ts"] +} diff --git a/ORCA_REFERENCE.md b/ORCA_REFERENCE.md index a953c36..f8ccec0 100644 --- a/ORCA_REFERENCE.md +++ b/ORCA_REFERENCE.md @@ -2,137 +2,651 @@ Coordinated agent run harness. +> Generated by `node scripts/generate-docs-reference.mjs` from `orcastrator@0.2.25`. Do not edit this file by hand. + ## Install -```bash +```shell npm install -g orcastrator ``` -## Run Management +## Command Reference + +This section is generated from the Commander command tree used by the built Orca CLI. + +### orca + +```text +Usage: orca [options] [command] + +Orca CLI: coordinated agent run harness + +Options: + -V, --version output the version number + -h, --help display help for command + +Commands: + run [options] [goal] Run pre-planning and execution + answer [options] [run-id] [answer] Submit an answer for a run waiting for + input + plan [options] Run pre-planning and output validated task + graph + status [options] Show run status or list all runs + list [options] List all runs in run store + skills [options] List available skills + resume [options] Resume an incomplete run + cancel [options] Cancel an active run + pr Pull request workflow commands + pr-finalize [options] Finalize a prepared pull request + setup [options] Run first-time setup and environment + checks + help [command] display help for command +``` + +### orca run + +```text +Usage: orca run [options] [goal] + +Run pre-planning and execution + +Options: + --spec Path to spec markdown file + --plan Alias for --spec — path to a plan/spec file + --task Inline task text (alternative to --spec) + -p, --prompt Inline task text (alias for --task) + --config Path to orca config file + --codex-only Force Codex executor for this run (overrides config) + --codex-effort Codex thinking level override for this run + --on-milestone Shell hook command for onMilestone + --on-question Shell hook command for onQuestion + --on-task-complete Shell hook command for onTaskComplete + --on-task-fail Shell hook command for onTaskFail + --on-invalid-plan Shell hook command for onInvalidPlan + --on-findings Shell hook command for onFindings + --on-complete Shell hook command for onComplete + --on-error Shell hook command for onError + -h, --help display help for command +``` + +### orca plan + +```text +Usage: orca plan [options] + +Run pre-planning and output validated task graph + +Options: + --spec Path to spec markdown file + --config Path to orca config file + --on-milestone Shell hook command for onMilestone + --on-error Shell hook command for onError + -h, --help display help for command +``` + +### orca status + +```text +Usage: orca status [options] + +Show run status or list all runs + +Options: + --run Run ID to inspect + --last Use the most recent run + --config Path to orca config file + -h, --help display help for command +``` + +### orca list + +```text +Usage: orca list [options] + +List all runs in run store + +Options: + --config Path to orca config file + -h, --help display help for command +``` + +### orca skills + +```text +Usage: orca skills [options] + +List available skills + +Options: + --config Path to orca config file + -h, --help display help for command +``` + +### orca resume + +```text +Usage: orca resume [options] -Orca starts each run with a lightweight planning-necessity decision (`needsPlan?`). Multi-step work goes through full planning; simple focused work can execute directly as one task. +Resume an incomplete run -```bash -orca status # list all runs (default) -orca status --last # most recent run details -orca status --run # specific run details +Options: + --run Run ID to resume + --last Use the most recent run + --config Path to orca config file + --codex-only Force Codex executor for this resumed run (overrides + config) + --codex-effort Codex thinking level override for this resumed run + -h, --help display help for command +``` -orca resume --last -orca resume --run -orca resume --run --codex-only --codex-effort high +### orca cancel -orca cancel --last -orca cancel --run +```text +Usage: orca cancel [options] -orca answer "yes, use migration A" +Cancel an active run + +Options: + --run Run ID to cancel + --last Use the most recent run + --config Path to orca config file + -h, --help display help for command ``` -## PR Workflow +### orca answer + +```text +Usage: orca answer [options] [run-id] [answer] -Canonical public flow: +Submit an answer for a run waiting for input -```bash -orca pr draft --run -orca pr create --run -orca pr publish --run -orca pr status --run +Options: + --run Run ID waiting for answer + -h, --help display help for command ``` -`orca pr publish` run selection behavior: -- TTY: if `--run`/`--last` omitted, interactive picker is shown. -- non-TTY: pass `--run` or `--last`. +### orca pr -## Config Discovery / Precedence +```text +Usage: orca pr [options] [command] -Load order (later overrides earlier): -1. global config: `~/.orca/config.ts` then `~/.orca/config.js` (`.ts` takes precedence when both exist) -2. project config: `./orca.config.ts` then `./orca.config.js` (`.ts` takes precedence when both exist) -3. `--config ` +Pull request workflow commands -## CLI Flags Reference +Options: + -h, --help display help for command -`orca` / `orca run`: -- `[task]`, `--task `, `-p, --prompt ` -- `--spec `, `--plan `, `--config ` -- `--codex-only` -- `--codex-effort ` -- `--on-milestone ` -- `--on-task-complete ` -- `--on-task-fail ` -- `--on-invalid-plan ` -- `--on-findings ` -- `--on-complete ` -- `--on-error ` +Commands: + draft [options] Create a draft pull request for a run + create [options] Create a pull request for a run + publish [options] Publish a draft PR (mark ready for review) + status [options] Show pull request status for a run + finalize [options] Deprecated: use `orca pr publish` +``` -`orca plan`: -- `--spec ` -- `--config ` -- `--on-milestone ` -- `--on-error ` +### orca pr draft -`orca status`: `--run `, `--last`, `--config ` +```text +Usage: orca pr draft [options] -`orca resume`: `--run `, `--last`, `--config `, `--codex-only`, `--codex-effort ` +Create a draft pull request for a run -`orca cancel`: `--run `, `--last`, `--config ` +Options: + --run Run ID to create draft PR for + --last Use the most recent run + --config Path to orca config file + -h, --help display help for command +``` -`orca answer`: `[run-id] [answer]`, `--run ` +### orca pr create -`orca list`: `--config ` +```text +Usage: orca pr create [options] -`orca pr draft|create|publish|status`: `--run `, `--last`, `--config ` (accepted for compatibility; currently unused by PR command run resolution) +Create a pull request for a run -`orca setup`: -- `--openai-key ` -- `--global` -- `--project` -- `--project-config-template` -- `--skip-project-config` +Options: + --run Run ID to create PR for + --last Use the most recent run + --config Path to orca config file + -h, --help display help for command +``` -`orca skills`: `--config ` +### orca pr publish -`orca help`: `[command]` +```text +Usage: orca pr publish [options] -## Hooks + Types +Publish a draft PR (mark ready for review) -Hook names: -- `onMilestone` -- `onTaskComplete` -- `onTaskFail` -- `onInvalidPlan` -- `onFindings` -- `onComplete` -- `onError` +Options: + --run Run ID to publish PR for + --last Use the most recent run + --config Path to orca config file + -h, --help display help for command +``` + +### orca pr status + +```text +Usage: orca pr status [options] + +Show pull request status for a run + +Options: + --run Run ID to inspect PR for + --last Use the most recent run + --config Path to orca config file + -h, --help display help for command +``` -Hook contract: -- Function hooks (`hooks`) receive `(event, context)`. -- `context` is `{ cwd, pid, invokedAt }`. -- Command hooks (`hookCommands` and CLI `--on-*`) receive JSON via stdin. -- No `ORCA_*` hook payload env-var framing. -- `onError` fires for run errors and hook-dispatch/command-hook failures. +### orca setup -## OrcaConfig Reference (complete) +```text +Usage: orca setup [options] -Top-level: `executor`, `openaiApiKey`, `runsDir`, `sessionLogs`, `skills`, `maxRetries`, `codex`, `hooks`, `hookCommands`, `pr`, `review` +Run first-time setup and environment checks -`maxRetries` is an accepted OrcaConfig field; current planner-generated task retry limits are still fixed by task graph contracts. +Options: + --openai-key Provide OpenAI API key directly + --global Save config to ~/.orca/config.js (or ~/.orca/config.ts + with --ts) + --project Save config to ./orca.config.js (or ./orca.config.ts with + --ts) + --ts Write TypeScript config (.ts) with OrcaConfig satisfies + syntax + --executor Set executor in written config (codex) + -h, --help display help for command +``` +### orca help -`codex.*`: `enabled`, `model`, `effort`, `thinkingLevel.decision|planning|execution`, `command`, `timeoutMs`, `multiAgent`, `perCwdExtraUserRoots` +```text +Usage: orca help [options] [command] -`pr.*`: `enabled`, `requireConfirmation` +display help for command -`review.plan.*`: `enabled`, `onInvalid` +Options: + -h, --help display help for command +``` -`review.execution.*`: `enabled`, `maxCycles`, `onFindings`, `validator.auto`, `validator.commands`, `prompt` +### orca pr-finalize -Thinking-level controls use `codex.thinkingLevel.decision|planning|execution` with canonical values `low|medium|high|xhigh`. +```text +Usage: orca pr-finalize [options] -Codex app-server integration: at session startup Orca calls `skills/list` with `cwds: [cwd]`, `forceReload: true`, and optional `codex.perCwdExtraUserRoots` mappings. +Finalize a prepared pull request -Deprecated compatibility aliases: -- `review.enabled` -- `review.onInvalid` +Options: + --run Run ID to finalize PR for + --config Path to orca config file + -h, --help display help for command +``` -Validator caveat: -- `ORCA_SKIP_VALIDATORS=1` forces `review.execution.validator.auto` off. +## Config Schema And Public Type Reference + +Codex effort values: `low|medium|high|xhigh`. + +This section is generated from the Zod-backed config schema and public types in `src/types/index.ts`, which are re-exported by the package entry point. + +```ts +export type PlannerAgent = "codex" | "claude"; +export type PlannerAgentSelection = PlannerAgent | "auto"; +export type ClaudeEffort = CodexEffort | "max"; + +declare const CUSTOM_MODEL_ID: unique symbol; +export type CustomModelId = string & { readonly [CUSTOM_MODEL_ID]: true }; + +export type OpenAIModelId = + | "gpt-5.5" + | "gpt-5.2" + | "gpt-5.2-pro" + | "gpt-5.2-codex" + | "gpt-5.1" + | "gpt-5.1-codex" + | "gpt-5.1-codex-max" + | "gpt-5.1-codex-mini" + | "gpt-5" + | "gpt-5-codex" + | "gpt-5.3-codex" + | "gpt-5.3-codex-spark" + | CustomModelId; + +export type ClaudeModelId = + | "claude-opus-4-7" + | "claude-sonnet-4-6" + | "claude-opus-4-1" + | "claude-opus-4-1-20250805" + | "claude-opus-4-0" + | "claude-opus-4-20250514" + | "claude-sonnet-4-0" + | "claude-sonnet-4-20250514" + | "claude-3-7-sonnet-latest" + | "claude-3-7-sonnet-20250219" + | "claude-3-5-sonnet-latest" + | "claude-3-5-sonnet-20241022" + | "claude-3-5-haiku-latest" + | "claude-3-5-haiku-20241022" + | "opus" + | "sonnet" + | "haiku" + | "opusplan" + | "default" + | CustomModelId; + +export interface PlannerRoutingDecision { + planner: PlannerAgent; + reason: string; +} + +export interface PlannerRouterConfig { + /** + * Codex model used only for planner.agent="auto" routing. + */ + model?: OpenAIModelId; +} + +export type PlannerConfig = + | { + /** + * Ask a Codex router to choose Claude or Codex for task graph generation. + * This is also the default when planner is omitted. + */ + agent?: "auto"; + router?: PlannerRouterConfig; + } + | { + /** + * Always use Claude for non-skipped task graph generation. + */ + agent: "claude"; + router?: never; + } + | { + /** + * Always use Codex for non-skipped task graph generation. + */ + agent: "codex"; + router?: never; + }; + +export interface OrcaConfig { + openaiApiKey?: string; + runsDir?: string; + sessionLogs?: string; + skills?: string[]; + maxRetries?: number; + executor?: "codex"; + planner?: PlannerConfig; + claude?: { + command?: string; + model?: ClaudeModelId; + effort?: ClaudeEffort; + timeoutMs?: number; + }; + codex?: { + enabled?: boolean; + model?: OpenAIModelId; + effort?: CodexEffort; + thinkingLevel?: { + decision?: CodexEffort; + planning?: CodexEffort; + review?: CodexEffort; + execution?: CodexEffort; + }; + command?: string; + timeoutMs?: number; + multiAgent?: boolean; + /** + * Optional extra skill roots to use for specific working directories when + * querying Codex app-server skills/list. + */ + perCwdExtraUserRoots?: Array<{ + cwd: string; + extraUserRoots: string[]; + }>; + }; + hooks?: { [K in HookName]?: HookHandler }; + hookCommands?: Partial>; + pr?: { + enabled?: boolean; + requireConfirmation?: boolean; + }; + review?: { + /** @deprecated Use review.plan.enabled */ + enabled?: boolean; + /** @deprecated Use review.plan.onInvalid */ + onInvalid?: "fail" | "warn_skip"; + plan?: { + enabled?: boolean; + onInvalid?: "fail" | "warn_skip"; + }; + execution?: { + enabled?: boolean; + maxCycles?: number; + onFindings?: "auto_fix" | "report_only" | "fail"; + validator?: { + auto?: boolean; + commands?: string[]; + }; + prompt?: string; + }; + }; +} + +const scalarStringSchema = z.custom((value) => typeof value === "string", { + message: "must be a string", +}); +const scalarBooleanSchema = z.custom((value) => typeof value === "boolean", { + message: "must be a boolean", +}); +const positiveIntegerSchema = z.custom( + (value) => typeof value === "number" && Number.isInteger(value) && value >= 1, + { message: "must be an integer >= 1" }, +); +const nonnegativeIntegerSchema = z.custom( + (value) => typeof value === "number" && Number.isInteger(value) && value >= 0, + { message: "must be a nonnegative integer" }, +); + +const openAIModelSchema = scalarStringSchema as z.ZodType; +const claudeModelSchema = scalarStringSchema as z.ZodType; + +const codexEffortSchema = z + .custom((value) => typeof value === "string", { + message: "must be a string", + }) + .transform((value, context): CodexEffort => { + try { + return parseCodexEffort(value); + } catch (error) { + context.addIssue({ + code: "custom", + message: error instanceof Error ? error.message : String(error), + }); + + return z.NEVER; + } + }); + +const claudeEffortSchema = z.custom( + (value) => typeof value === "string" && ([...CODEX_EFFORT_VALUES, "max"] as string[]).includes(value), + { message: "must be one of low, medium, high, xhigh, max" }, +); + +const hookNameValues = [ + "onMilestone", + "onQuestion", + "onTaskComplete", + "onTaskFail", + "onInvalidPlan", + "onFindings", + "onComplete", + "onError", +] as const satisfies readonly HookName[]; + +const hookNameSet = new Set(hookNameValues); + +function addUnknownHookIssue(context: z.RefinementCtx, configPath: "hooks" | "hookCommands", hookName: string) { + context.addIssue({ + code: "custom", + path: [hookName], + message: `Unknown hook key in Config.${configPath}: ${hookName}. Allowed hooks: ${hookNameValues.join(", ")}`, + }); +} + +const hookHandlerSchema = z.custom((value) => typeof value === "function", { + message: "must be a function", +}); + +const hooksSchema = z.record(z.string(), hookHandlerSchema).superRefine((hooks, context) => { + for (const hookName of Object.keys(hooks)) { + if (!hookNameSet.has(hookName)) { + addUnknownHookIssue(context, "hooks", hookName); + } + } +}) as z.ZodType; + +const hookCommandsSchema = z.record(z.string(), scalarStringSchema).superRefine((hookCommands, context) => { + for (const hookName of Object.keys(hookCommands)) { + if (!hookNameSet.has(hookName)) { + addUnknownHookIssue(context, "hookCommands", hookName); + } + } +}) as z.ZodType; + +export const PlannerRouterConfigSchema = z + .object({ + model: openAIModelSchema.optional(), + }) + .passthrough() as z.ZodType; + +export const PlannerConfigSchema = z + .object({ + agent: z + .enum(["auto", "codex", "claude"], { + message: "must be 'auto', 'codex', or 'claude'", + }) + .optional(), + router: PlannerRouterConfigSchema.optional(), + }) + .passthrough() + .superRefine((planner, context) => { + if ((planner.agent === "claude" || planner.agent === "codex") && planner.router !== undefined) { + context.addIssue({ + code: "custom", + path: ["router"], + message: `Config.planner.router is only used when Config.planner.agent is 'auto'. Remove router for forced '${planner.agent}' planning.`, + }); + } + }) as z.ZodType; + +const codexThinkingLevelSchema = z + .object({ + decision: codexEffortSchema.optional(), + planning: codexEffortSchema.optional(), + review: codexEffortSchema.optional(), + execution: codexEffortSchema.optional(), + }) + .passthrough(); + +export const CodexConfigSchema = z + .object({ + enabled: scalarBooleanSchema.optional(), + model: openAIModelSchema.optional(), + effort: codexEffortSchema.optional(), + thinkingLevel: codexThinkingLevelSchema.optional(), + command: scalarStringSchema.optional(), + timeoutMs: positiveIntegerSchema.optional(), + multiAgent: scalarBooleanSchema.optional(), + perCwdExtraUserRoots: z + .array( + z + .object({ + cwd: scalarStringSchema, + extraUserRoots: z.array(scalarStringSchema), + }) + .passthrough(), + ) + .optional(), + }) + .passthrough() + .superRefine((codex, context) => { + if ("thinking" in codex && codex.thinking !== undefined) { + context.addIssue({ + code: "custom", + path: ["thinking"], + message: "Config.codex.thinking is no longer supported. Use Config.codex.thinkingLevel instead.", + }); + } + }) as z.ZodType; + +export const ClaudeConfigSchema = z + .object({ + command: scalarStringSchema.optional(), + model: claudeModelSchema.optional(), + effort: claudeEffortSchema.optional(), + timeoutMs: positiveIntegerSchema.optional(), + }) + .passthrough() as z.ZodType; + +const reviewOnInvalidSchema = z.enum(["fail", "warn_skip"], { + message: "must be 'fail' or 'warn_skip'", +}); +const reviewOnFindingsSchema = z.enum(["auto_fix", "report_only", "fail"], { + message: "must be 'auto_fix', 'report_only', or 'fail'", +}); + +export const ReviewConfigSchema = z + .object({ + enabled: scalarBooleanSchema.optional(), + onInvalid: reviewOnInvalidSchema.optional(), + plan: z + .object({ + enabled: scalarBooleanSchema.optional(), + onInvalid: reviewOnInvalidSchema.optional(), + }) + .passthrough() + .optional(), + execution: z + .object({ + enabled: scalarBooleanSchema.optional(), + maxCycles: positiveIntegerSchema.optional(), + onFindings: reviewOnFindingsSchema.optional(), + validator: z + .object({ + auto: scalarBooleanSchema.optional(), + commands: z.array(scalarStringSchema).optional(), + }) + .passthrough() + .optional(), + prompt: scalarStringSchema.optional(), + }) + .passthrough() + .optional(), + }) + .passthrough() as z.ZodType; + +export const OrcaConfigSchema = z + .object({ + openaiApiKey: scalarStringSchema.optional(), + runsDir: scalarStringSchema.optional(), + sessionLogs: scalarStringSchema.optional(), + skills: z.array(scalarStringSchema).optional(), + maxRetries: nonnegativeIntegerSchema.optional(), + executor: z.literal("codex").optional(), + planner: PlannerConfigSchema.optional(), + claude: ClaudeConfigSchema.optional(), + codex: CodexConfigSchema.optional(), + hooks: hooksSchema.optional(), + hookCommands: hookCommandsSchema.optional(), + pr: z + .object({ + enabled: scalarBooleanSchema.optional(), + requireConfirmation: scalarBooleanSchema.optional(), + }) + .passthrough() + .optional(), + review: ReviewConfigSchema.optional(), + }) + .passthrough(); +``` diff --git a/README.md b/README.md index 380a43f..b747053 100644 --- a/README.md +++ b/README.md @@ -24,8 +24,9 @@ From `package.json`: - `bun dev` — start local dev server - `bun run build` — production build - `bun start` — run production server -- `bun run lint` — run Biome checks -- `bun run format` — format with Biome +- `bun run lint` — run Oxlint checks +- `bun run format` — format with Oxfmt +- `bun run format:check` — check formatting with Oxfmt ## Project links diff --git a/app/components/AgentSkillCard.tsx b/app/components/AgentSkillCard.tsx index 34a17d4..4269e15 100644 --- a/app/components/AgentSkillCard.tsx +++ b/app/components/AgentSkillCard.tsx @@ -20,7 +20,7 @@ orca "your task here" ## Key Commands orca Start a new run orca status [--last] Check run status -orca answer Answer a question the agent raised +orca answer Answer a question the agent raised orca resume [--last] Resume a paused run orca cancel [--last] Cancel a run orca pr create [--last] Open a PR for the run's branch @@ -30,11 +30,15 @@ export default { executor: "codex", // "codex" (default) sessionLogs: "./session-logs", hooks: { + onQuestion: async (event) => { + console.log(event.questions[0]?.question); + }, onComplete: async (event, context) => { console.log(event.message, context.cwd); }, }, hookCommands: { + onQuestion: "node ./scripts/on-question.mjs", onComplete: "node ./scripts/on-complete.mjs", }, codex: { multiAgent: false }, @@ -44,7 +48,7 @@ export default { - Codex executor requires ~/.codex/auth.json - Must be run inside a git repo - Run ID format: -- -- Use orca answer to unblock a waiting run`; +- Use orca answer to respond when status is waiting_for_answer (run resumes automatically)`; export function AgentSkillCard({ compact = false }: { compact?: boolean }) { return ( diff --git a/app/components/CodeBlock.tsx b/app/components/CodeBlock.tsx index b3aee03..899b9a7 100644 --- a/app/components/CodeBlock.tsx +++ b/app/components/CodeBlock.tsx @@ -39,9 +39,7 @@ function highlightInline(line: string, lang?: string): React.ReactNode[] { return [tokenSpan(line, `shell-${line.length}`, "#e2e8f0")]; } - const tokens = line - .split(/(\s+|"[^"]*"|'[^']*'|`[^`]*`|\/\/.*$|[{}()[\];,.:=<>])/g) - .filter(Boolean); + const tokens = line.split(/(\s+|"[^"]*"|'[^']*'|`[^`]*`|\/\/.*$|[{}()[\];,.:=<>])/g).filter(Boolean); const nodes: React.ReactNode[] = []; let cursor = 0; diff --git a/app/components/PageCopyButton.tsx b/app/components/PageCopyButton.tsx index 661ffb1..ae20b34 100644 --- a/app/components/PageCopyButton.tsx +++ b/app/components/PageCopyButton.tsx @@ -1,14 +1,20 @@ "use client"; import { useState } from "react"; -import { PAGE_MARKDOWN } from "../lib/pageMarkdown"; export function PageCopyButton() { const [copied, setCopied] = useState(false); const handleCopy = async () => { try { - await navigator.clipboard.writeText(PAGE_MARKDOWN); + const response = await fetch("/md"); + if (!response.ok) { + throw new Error( + `Failed to fetch Markdown reference: ${response.status}`, + ); + } + + await navigator.clipboard.writeText(await response.text()); setCopied(true); setTimeout(() => setCopied(false), 1500); } catch { diff --git a/app/globals.css b/app/globals.css index b7328a7..2f79240 100644 --- a/app/globals.css +++ b/app/globals.css @@ -39,7 +39,11 @@ html { body { background-color: var(--bg); color: var(--text); - font-family: ui-sans-serif, system-ui, -apple-system, sans-serif; + font-family: + ui-sans-serif, + system-ui, + -apple-system, + sans-serif; font-size: 15px; line-height: 1.6; margin: 0; @@ -48,9 +52,7 @@ body { code, kbd, pre { - font-family: - ui-monospace, "Cascadia Code", "Fira Code", Menlo, Monaco, "Courier New", - monospace; + font-family: ui-monospace, "Cascadia Code", "Fira Code", Menlo, Monaco, "Courier New", monospace; } ::-webkit-scrollbar { diff --git a/app/lib/pageMarkdown.test.ts b/app/lib/pageMarkdown.test.ts index bd2dd50..82bf1da 100644 --- a/app/lib/pageMarkdown.test.ts +++ b/app/lib/pageMarkdown.test.ts @@ -7,29 +7,26 @@ const ROOT = join(import.meta.dir, "..", ".."); const ORCA_REFERENCE_PATH = join(ROOT, "ORCA_REFERENCE.md"); test("uses canonical codex effort values and thinkingLevel naming", () => { - expect(PAGE_MARKDOWN).toContain("--codex-effort "); expect(PAGE_MARKDOWN).toContain( - "codex.thinkingLevel.decision|planning|execution", + "Codex effort values: `low|medium|high|xhigh`", ); + expect(PAGE_MARKDOWN).toContain("thinkingLevel?:"); expect(PAGE_MARKDOWN).not.toContain("extra-high"); expect(PAGE_MARKDOWN).not.toContain("codex.thinking:"); }); -test("documents planning-necessity gate and skills/list cwd behavior", () => { - expect(PAGE_MARKDOWN).toContain("planning-necessity decision (`needsPlan?`)"); - expect(PAGE_MARKDOWN).toContain("skills/list"); - expect(PAGE_MARKDOWN).toContain("cwds: [cwd]"); - expect(PAGE_MARKDOWN).toContain("forceReload: true"); +test("is generated from CLI and public type references", () => { + expect(PAGE_MARKDOWN).toContain( + "Generated by `node scripts/generate-docs-reference.mjs`", + ); + expect(PAGE_MARKDOWN).toContain( + "This section is generated from the Commander command tree", + ); + expect(PAGE_MARKDOWN).toContain("Usage: orca run [options] [goal]"); + expect(PAGE_MARKDOWN).toContain("export interface OrcaConfig"); }); -test("ORCA_REFERENCE.md stays aligned with PAGE_MARKDOWN semantics", () => { +test("PAGE_MARKDOWN is loaded from ORCA_REFERENCE.md", () => { const reference = readFileSync(ORCA_REFERENCE_PATH, "utf8"); - expect(reference).toContain("--codex-effort "); - expect(reference).toContain( - "codex.thinkingLevel.decision|planning|execution", - ); - expect(reference).toContain("planning-necessity decision (`needsPlan?`)"); - expect(reference).toContain("skills/list"); - expect(reference).toContain("cwds: [cwd]"); - expect(reference).not.toContain("extra-high"); + expect(PAGE_MARKDOWN).toBe(reference); }); diff --git a/app/lib/pageMarkdown.ts b/app/lib/pageMarkdown.ts index 91e8b73..125e4cd 100644 --- a/app/lib/pageMarkdown.ts +++ b/app/lib/pageMarkdown.ts @@ -1,137 +1,7 @@ -export const PAGE_MARKDOWN = `# orca +import { readFileSync } from "node:fs"; +import { join } from "node:path"; -Coordinated agent run harness. - -## Install - -\`\`\`shell -npm install -g orcastrator -\`\`\` - -## Run Management - -Orca starts each run with a lightweight planning-necessity decision (\`needsPlan?\`). Multi-step work goes through full planning; simple focused work can execute directly as one task. - -\`\`\`shell -orca status # list all runs (default) -orca status --last # most recent run details -orca status --run # specific run details - -orca resume --last -orca resume --run -orca resume --run --codex-only --codex-effort high - -orca cancel --last -orca cancel --run - -orca answer "yes, use migration A" -\`\`\` - -## PR Workflow - -Canonical public flow: - -\`\`\`shell -orca pr draft --run -orca pr create --run -orca pr publish --run -orca pr status --run -\`\`\` - -\`orca pr publish\` run selection behavior: -- TTY: if \`--run\`/\`--last\` omitted, interactive picker is shown. -- non-TTY: pass \`--run\` or \`--last\`. - -## Config Discovery / Precedence - -Load order (later overrides earlier): -1. global config: \`~/.orca/config.ts\` then \`~/.orca/config.js\` (\`.ts\` takes precedence when both exist) -2. project config: \`./orca.config.ts\` then \`./orca.config.js\` (\`.ts\` takes precedence when both exist) -3. \`--config \` - -## CLI Flags Reference - -\`orca\` / \`orca run\`: -- \`[task]\`, \`--task \`, \`-p, --prompt \` -- \`--spec \`, \`--plan \`, \`--config \` -- \`--codex-effort \` -- \`--on-milestone \` -- \`--on-task-complete \` -- \`--on-task-fail \` -- \`--on-invalid-plan \` -- \`--on-findings \` -- \`--on-complete \` -- \`--on-error \` - -\`orca plan\`: -- \`--spec \` -- \`--config \` -- \`--on-milestone \` -- \`--on-error \` - -\`orca status\`: \`--run \`, \`--last\`, \`--config \` - - -\`orca cancel\`: \`--run \`, \`--last\`, \`--config \` - -\`orca answer\`: \`[run-id] [answer]\`, \`--run \` - -\`orca list\`: \`--config \` - -\`orca pr draft|create|publish|status\`: \`--run \`, \`--last\`, \`--config \` (accepted for compatibility; currently unused by PR command run resolution) - -\`orca setup\`: -- \`--openai-key \` -- \`--ts\` (write \`.ts\` config output) -- \`--global\` -- \`--project\` -- \`--project-config-template\` -- \`--skip-project-config\` - -\`orca skills\`: \`--config \` - -\`orca help\`: \`[command]\` - -## Hooks + Types - -Hook names: -- \`onMilestone\` -- \`onTaskComplete\` -- \`onTaskFail\` -- \`onInvalidPlan\` -- \`onFindings\` -- \`onComplete\` -- \`onError\` - -Hook contract: -- Function hooks (\`hooks\`) receive \`(event, context)\`. -- \`context\` is \`{ cwd, pid, invokedAt }\`. -- Command hooks (\`hookCommands\` and CLI \`--on-*\`) receive JSON via stdin. -- No \`ORCA_*\` hook payload env-var framing. -- \`onError\` fires for run errors and hook-dispatch/command-hook failures. - -## OrcaConfig Reference (complete) - - -\`maxRetries\` is an accepted OrcaConfig field; current planner-generated task retry limits are still fixed by task graph contracts. - - -\`codex.*\`: \`enabled\`, \`model\`, \`effort\`, \`thinkingLevel.decision|planning|execution\`, \`command\`, \`timeoutMs\`, \`multiAgent\`, \`perCwdExtraUserRoots\` - -\`pr.*\`: \`enabled\`, \`requireConfirmation\` - -\`review.plan.*\`: \`enabled\`, \`onInvalid\` - -\`review.execution.*\`: \`enabled\`, \`maxCycles\`, \`onFindings\`, \`validator.auto\`, \`validator.commands\`, \`prompt\` - -Thinking-level controls use \`codex.thinkingLevel.decision|planning|execution\` with canonical values \`low|medium|high|xhigh\`. - -Codex app-server integration: at session startup Orca calls \`skills/list\` with \`cwds: [cwd]\`, \`forceReload: true\`, and optional \`codex.perCwdExtraUserRoots\` mappings. - -Deprecated compatibility aliases: -- \`review.enabled\` -- \`review.onInvalid\` - -Validator caveat: -- \`ORCA_SKIP_VALIDATORS=1\` forces \`review.execution.validator.auto\` off. -`; +export const PAGE_MARKDOWN = readFileSync( + join(process.cwd(), "ORCA_REFERENCE.md"), + "utf8", +); diff --git a/app/page.tsx b/app/page.tsx index 4c67b8f..7e87aac 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -320,16 +320,10 @@ export default function Home() { marginLeft: "8px", }} > - + commands - + config @@ -400,8 +394,8 @@ export default function Home() { maxWidth: "520px", }} > - Coordinated agent run harness. Breaks down a task into a graph of - tasks, then executes it end-to-end via a persistent{" "} + Coordinated agent run harness. Breaks down a task into a graph of tasks, then executes it end-to-end via a + persistent{" "} - - - - - - - - - - + + + + + + + + + + + - + @@ -519,29 +481,14 @@ orca --plan ./specs/feature.md`} usage="orca plan [flags]" desc="Plan tasks without executing them. Useful for reviewing the task breakdown before committing to a run." > - +

Flags

- - - - + + + +
@@ -562,18 +509,9 @@ orca status --run # status of a specific run`}

Flags

- - - + + +
@@ -589,10 +527,7 @@ orca status --run # status of a specific run`}

Flags

- +
@@ -612,23 +547,11 @@ orca resume --run feature-auth-1766228123456-1a2b`}

Flags

- + - - - + + +
@@ -648,15 +571,9 @@ orca cancel --run feature-auth-1766228123456-1a2b`}

Flags

- + - +
@@ -668,19 +585,13 @@ orca cancel --run feature-auth-1766228123456-1a2b`} usage="orca answer " desc="Provide an answer to a run that is blocked waiting for input." > - +

Flags

- +
@@ -703,10 +614,7 @@ orca pr status --run # check PR status`}

Flags

- + Flags
- + - - - - - - + + + + + +
@@ -811,21 +698,13 @@ orca skills --config ./orca.config.js`}

Flags

- +
{/* orca help */} - +

Config Discovery

-

- Orca auto-discovers config in this order — later entries override - earlier ones: -

+

Orca auto-discovers config in this order — later entries override earlier ones:

- Run state is stored - at{" "} + Run state is stored at{" "}

Project Instruction Files

-

- During planning, Orca automatically injects project instruction - files when present: -

+

During planning, Orca automatically injects project instruction files when present:

    + + @@ -1080,12 +968,41 @@ export default defineOrcaConfig({ flag="review.execution.*" desc="enabled, maxCycles, onFindings, validator.auto, validator.commands, prompt" /> - + +

    + Planner routing has three JSON-readable shapes: +

    + +

    + Forced Claude or Codex planning bypasses the router, so those configs should not include{" "} + + router + + . Claude planning shells out to{" "} + + claude -p + + ; Codex still executes tasks and runs reviews. +

    Thinking-level controls are explicit: use - {" codex.thinkingLevel.decision|planning|execution "} + {" codex.thinkingLevel.decision|planning|review|execution "} {" "} with canonical values .

    +

    + Model IDs are strongly typed for documented provider models, with{" "} + + customModel("...") + {" "} + for private, unreleased, or provider-specific IDs. +

    +

    + When updating model lists, check{" "} + + OpenAI models + + {", "} + + Anthropic Claude models + + {", and "} + + Claude Code model config + + . +

    On Codex session startup, Orca calls app-server - - + + + - - - - + + + +

    @@ -1216,9 +1149,7 @@ export default defineOrcaConfig({ .

    -

    - Command hooks receive structured event payload JSON on stdin. -

    +

    Command hooks receive structured event payload JSON on stdin.

    Interactive hook flow

    If a run needs operator input, Orca moves the run into @@ -1237,10 +1168,7 @@ export default defineOrcaConfig({ orca status --run # 2) Submit the answer -orca answer "yes, use migration A" - -# 3) Continue execution -orca resume --run `} +orca answer "yes, use migration A"`} lang="shell" />

    `} }} > See{" "} - + Types ↓ {" "} for full hook event and context contracts. @@ -1292,9 +1217,8 @@ export default defineOrcaConfig({

    Multi-agent Mode

    - Codex supports experimental multi-agent workflows where it can - spawn parallel sub-agents for complex tasks. Off by default - because enabling it modifies your global{" "} + Codex supports experimental multi-agent workflows where it can spawn parallel sub-agents for complex + tasks. Off by default because enabling it modifies your global{" "}

    ⚠️ Enabling multi-agent mode writes{" "} - - multi_agent = true - {" "} - to your global Codex config. If you already have it enabled there, - orca picks it up automatically. + multi_agent = true to your global Codex + config. If you already have it enabled there, orca picks it up automatically.

    Agent Skill

    - Drop this SKILL.md into your agent's skills directory to give - it native Orca support. + Drop this SKILL.md into your agent's skills directory to give it native Orca support.

    @@ -1364,8 +1284,7 @@ export default {

    Types

    - Full TypeScript contracts for hook events and context. Import - directly from{" "} + Full TypeScript contracts for hook events and context. Import directly from{" "}

    HookHandlerContext

    -

    - Passed as the second argument to every function hook. -

    +

    Passed as the second argument to every function hook.

    BaseHookEvent

    -

    - Base shape shared by all hook events. Hook-specific events extend - this. -

    +

    Base shape shared by all hook events. Hook-specific events extend this.

    | null; + }>; + }; onTaskComplete: BaseHookEvent & { hook: "onTaskComplete"; taskId: string; taskName: string }; onTaskFail: BaseHookEvent & { hook: "onTaskFail"; taskId: string; taskName: string; error: string }; onInvalidPlan: BaseHookEvent & { hook: "onInvalidPlan"; error: string }; @@ -1423,12 +1352,8 @@ export default { };`} lang="ts" /> -

    - Example payload (JSON) -

    -

    - Shape of the JSON written to stdin for command hooks. -

    +

    Example payload (JSON)

    +

    Shape of the JSON written to stdin for command hooks.

    =0.18.0" }, "optionalPeers": ["oxlint-tsgolint"], "bin": { "oxlint": "bin/oxlint" } }, "sha512-1uFkg6HakjsGIpW9wNdeW4/2LOHW9MEkoWjZUTUfQtIHyLIZPYt00w3Sg+H3lH+206FgBPHBbW5dVE5l2ExECQ=="], + "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="], "postcss": ["postcss@8.5.6", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg=="], @@ -230,6 +293,8 @@ "tapable": ["tapable@2.3.0", "", {}, "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg=="], + "tinypool": ["tinypool@2.1.0", "", {}, "sha512-Pugqs6M0m7Lv1I7FtxN4aoyToKg1C4tu+/381vH35y8oENM/Ai7f7C4StcoK4/+BSw9ebcS8jRiVrORFKCALLw=="], + "tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], "typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="], diff --git a/next.config.ts b/next.config.ts index e9ffa30..fb626ac 100644 --- a/next.config.ts +++ b/next.config.ts @@ -1,7 +1,9 @@ import type { NextConfig } from "next"; const nextConfig: NextConfig = { - /* config options here */ + outputFileTracingIncludes: { + "/*": ["./ORCA_REFERENCE.md"], + }, }; export default nextConfig; diff --git a/package.json b/package.json index 970aaf9..d8f283d 100644 --- a/package.json +++ b/package.json @@ -6,8 +6,9 @@ "dev": "next dev", "build": "next build", "start": "next start", - "lint": "biome check", - "format": "biome format --write" + "lint": "oxlint .", + "format": "oxfmt .", + "format:check": "oxfmt --check ." }, "dependencies": { "next": "16.1.6", @@ -15,11 +16,12 @@ "react-dom": "19.2.3" }, "devDependencies": { - "@biomejs/biome": "2.2.0", "@tailwindcss/postcss": "^4", "@types/node": "^20", "@types/react": "^19", "@types/react-dom": "^19", + "oxfmt": "0.47.0", + "oxlint": "1.62.0", "tailwindcss": "^4", "typescript": "^5" }, diff --git a/tsconfig.json b/tsconfig.json index 3a13f90..cc9ed39 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -22,13 +22,6 @@ "@/*": ["./*"] } }, - "include": [ - "next-env.d.ts", - "**/*.ts", - "**/*.tsx", - ".next/types/**/*.ts", - ".next/dev/types/**/*.ts", - "**/*.mts" - ], + "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts", ".next/dev/types/**/*.ts", "**/*.mts"], "exclude": ["node_modules"] }