Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
4 changes: 2 additions & 2 deletions docs/product-specs/new-user-onboarding.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
29 changes: 29 additions & 0 deletions src/core/setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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",
);
Expand Down Expand Up @@ -421,6 +437,11 @@ export async function runSetupWizard(cwd: string): Promise<void> {
});

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,
Expand Down Expand Up @@ -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, string | undefined>): string {
return Object.entries(entries)
.map(([key, value]) => renderEnvEntry(key, value ?? ""))
Expand Down
30 changes: 30 additions & 0 deletions tests/setup.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
normalizeProjectId,
renderEnvFile,
renderLocalConfig,
renderSetupRtkInstallPrompt,
writeSetupFiles,
} from "../src/core/setup";
import type { CommandResult } from "../src/utils/shell";
Expand Down Expand Up @@ -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({
Expand Down
Loading