From f60de129c4b03ff2bf67579ca266c064986760cb Mon Sep 17 00:00:00 2001 From: Mateus Zitelli <201472+MateusZitelli@users.noreply.github.com> Date: Fri, 7 Aug 2026 15:09:38 -0300 Subject: [PATCH] Default run create AI task ID from environment --- .changeset/track-ai-created-runs.md | 5 ++ src/commands/publicApi/index.aiTask.test.ts | 67 +++++++++++++++++++++ src/commands/publicApi/index.ts | 12 +++- 3 files changed, 81 insertions(+), 3 deletions(-) create mode 100644 .changeset/track-ai-created-runs.md create mode 100644 src/commands/publicApi/index.aiTask.test.ts diff --git a/.changeset/track-ai-created-runs.md b/.changeset/track-ai-created-runs.md new file mode 100644 index 000000000..1c395acb5 --- /dev/null +++ b/.changeset/track-ai-created-runs.md @@ -0,0 +1,5 @@ +--- +"@qawolf/cli": patch +--- + +Default the generated `run create` AI task option from `QAWOLF_AI_TASK_ID`. diff --git a/src/commands/publicApi/index.aiTask.test.ts b/src/commands/publicApi/index.aiTask.test.ts new file mode 100644 index 000000000..681793173 --- /dev/null +++ b/src/commands/publicApi/index.aiTask.test.ts @@ -0,0 +1,67 @@ +import { afterEach, expect, it } from "bun:test"; +import { Command } from "commander"; +import { z } from "zod"; + +import { + makeCallPublicApiMock, + makeMockPlatformClient, +} from "~/shell/platform/createPlatformClient.testUtils.js"; +import { createSignalRegistry } from "~/shell/signals/createSignalRegistry.js"; + +import { registerPublicApiCommands } from "./index.js"; + +const contract = { + description: "Create a run.", + input: z.object({ + aiTaskId: z.string().optional(), + environmentId: z.string(), + }), + kind: "write", + name: "run.create", + output: z.object({ runId: z.string() }), +} as const; + +afterEach(() => { + delete process.env["QAWOLF_AI_TASK_ID"]; +}); + +async function runCreate({ aiTaskId }: { aiTaskId?: string } = {}) { + const callPublicApi = makeCallPublicApiMock().mockResolvedValue({ + ok: true, + value: { runId: "run-id" }, + }); + const program = new Command().name("qawolf").exitOverride(); + registerPublicApiCommands(program, createSignalRegistry(), { + authDeps: { + requireApiKey: async () => ({ key: "qawolf_key", source: "env" }), + createPlatform: () => makeMockPlatformClient({ callPublicApi }), + }, + contracts: { run: { create: contract } }, + }); + const args = ["run", "create", "--environment-id", "environment-id"]; + if (aiTaskId) args.push("--ai-task-id", aiTaskId); + await program.parseAsync(args, { from: "user" }); + return callPublicApi; +} + +it("defaults the generated run.create AI task option from the environment", async () => { + process.env["QAWOLF_AI_TASK_ID"] = "task-from-env"; + + const callPublicApi = await runCreate(); + + expect(callPublicApi).toHaveBeenCalledWith(contract, { + aiTaskId: "task-from-env", + environmentId: "environment-id", + }); +}); + +it("prefers an explicit AI task option over the environment", async () => { + process.env["QAWOLF_AI_TASK_ID"] = "task-from-env"; + + const callPublicApi = await runCreate({ aiTaskId: "task-from-flag" }); + + expect(callPublicApi).toHaveBeenCalledWith(contract, { + aiTaskId: "task-from-flag", + environmentId: "environment-id", + }); +}); diff --git a/src/commands/publicApi/index.ts b/src/commands/publicApi/index.ts index 723706a72..5f49803c4 100644 --- a/src/commands/publicApi/index.ts +++ b/src/commands/publicApi/index.ts @@ -1,4 +1,4 @@ -import type { Command } from "commander"; +import { Option, type Command } from "commander"; import { publicContractsV1 } from "@qawolf/api-contracts/v1"; import { withAuthContext } from "~/commands/context.js"; @@ -38,6 +38,8 @@ const skippedContractNames: ReadonlySet = new Set([ "issue.update", ]); +const runCreateTrpcPath = "public.run.create"; + function resolveGroup(parent: Command, segment: string): Command { const existing = parent.commands.find( (command) => command.name() === segment, @@ -78,13 +80,17 @@ function registerSpec( spec.kind, ).description(spec.description); for (const flag of spec.flags) { - if (flag.required) { + if (spec.trpcPath === runCreateTrpcPath && flag.field === "aiTaskId") { + const option = new Option(flag.flag, flag.description).env( + "QAWOLF_AI_TASK_ID", + ); + command.addOption(flag.required ? option.makeOptionMandatory() : option); + } else if (flag.required) { command.requiredOption(flag.flag, flag.description); } else { command.option(flag.flag, flag.description); } } - command.action((options: Record, leaf: Command) => withAuthContext( signals,