diff --git a/README.md b/README.md index 55142332..5880e465 100644 --- a/README.md +++ b/README.md @@ -132,7 +132,7 @@ Run `adhd-ai setup` to answer plain-language prompts and generate local setup fi At runtime, config resolution uses this precedence for env-like values: process environment -> SQLite env store -> built-in defaults. -Run `adhd-ai setup --check` to validate that config loads, the execution path exists, `gh` is authenticated, Codex is available, and configured secrets are not present in tracked config files. +Run `adhd-ai setup --check` to validate that config loads, the execution path exists, `gh` is authenticated, `rtk` is available, Codex is available, and configured secrets are not present in tracked config files. ## Cron Jobs diff --git a/docs/product-specs/new-user-onboarding.md b/docs/product-specs/new-user-onboarding.md index ab871dc0..baba7672 100644 --- a/docs/product-specs/new-user-onboarding.md +++ b/docs/product-specs/new-user-onboarding.md @@ -7,14 +7,14 @@ Get a new operator from zero setup to running a scoped project workflow. ## Preconditions 1. Repository is cloned. -2. `bun` and `gh` are installed. +2. `bun`, `gh`, and `rtk` are installed. 3. Linear and GitHub credentials are available. ## Setup 1. Run `adhd-ai setup` and answer the guided prompts. 2. Confirm the wizard writes `.env` for secrets and `adhd-ai.local.config.ts` for local project settings. -3. Run `adhd-ai setup --check` to verify config, GitHub auth, Codex availability, and secret placement. +3. Run `adhd-ai setup --check` to verify config, GitHub auth, RTK availability, Codex availability, and secret placement. ## First Run diff --git a/src/core/setup.ts b/src/core/setup.ts index 9450835a..c340dce6 100644 --- a/src/core/setup.ts +++ b/src/core/setup.ts @@ -12,6 +12,7 @@ const ENV_FILE = ".env"; const LOCAL_CONFIG_FILE = "adhd-ai.local.config.ts"; const DEFAULT_PROJECT_NAME = "Default Project"; const DEFAULT_BASE_BRANCH = "main"; +const RTK_INSTALL_URL = "https://github.com/rtk-ai/rtk"; export interface SetupDraft { projectId: string; @@ -340,6 +341,21 @@ export async function collectSetupChecks( }, ); + const rtk = await safeRun(commandRunner, "rtk", ["--version"], commandCwd); + checks.push( + rtk.code === 0 + ? { + name: "RTK binary", + status: "pass", + message: "rtk is available", + } + : { + name: "RTK binary", + status: "fail", + message: formatMissingRtkMessage(), + }, + ); + const codexBackends = config.projects.filter( (project) => !project.agent?.backend || project.agent.backend === "codex", ); @@ -421,6 +437,11 @@ export async function runSetupWizard(cwd: string): Promise { }); try { + const rtk = await safeRun(runCommand, "rtk", ["--version"], cwd); + if (rtk.code !== 0) { + process.stdout.write(renderSetupRtkInstallPrompt()); + } + const projectName = await ask(io, "Project name", DEFAULT_PROJECT_NAME); const projectId = await ask( io, @@ -680,6 +701,14 @@ function commandFailureMessage(result: CommandResult): string { return output || `command exited with ${result.code}`; } +function formatMissingRtkMessage(): string { + return `rtk binary not found. Install from: ${RTK_INSTALL_URL}`; +} + +export function renderSetupRtkInstallPrompt(): string { + return `RTK is required for ADHD.ai agent workflow commands.\nInstall RTK before running workflows: ${RTK_INSTALL_URL}\n`; +} + function renderEnvEntries(entries: Record): string { return Object.entries(entries) .map(([key, value]) => renderEnvEntry(key, value ?? "")) diff --git a/tests/setup.test.ts b/tests/setup.test.ts index c01d8af5..45e43264 100644 --- a/tests/setup.test.ts +++ b/tests/setup.test.ts @@ -13,6 +13,7 @@ import { normalizeProjectId, renderEnvFile, renderLocalConfig, + renderSetupRtkInstallPrompt, writeSetupFiles, } from "../src/core/setup"; import type { CommandResult } from "../src/utils/shell"; @@ -204,6 +205,35 @@ describe("setup helpers", () => { message: "adhd-ai.config.ts contains a configured secret", }); }); + + it("reports missing rtk binary", async () => { + const checks = await collectSetupChecks("/tmp/demo", { + loadConfig: async () => loadedConfig({ linearApiKey: "lin_secret_123" }), + access: async () => {}, + readFile: async () => "", + runCommand: async (command) => + command === "rtk" + ? { + code: 1, + stdout: "", + stderr: "command not found: rtk", + } + : okCommand(), + }); + + expect(checks).toContainEqual({ + name: "RTK binary", + status: "fail", + message: + "rtk binary not found. Install from: https://github.com/rtk-ai/rtk", + }); + }); + + it("renders setup rtk install prompt", () => { + expect(renderSetupRtkInstallPrompt()).toContain( + "Install RTK before running workflows: https://github.com/rtk-ai/rtk", + ); + }); }); function loadedConfig({