From f15188f4261644d5a8bd291c48c077a5598035e2 Mon Sep 17 00:00:00 2001 From: Shashank Sharma Date: Tue, 13 Jan 2026 13:37:06 -0800 Subject: [PATCH] chore: code formatting and style cleanup - Apply consistent formatting across base-action tests - Format README.md bullet points consistently - Minor whitespace and formatting fixes in various files Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com> --- README.md | 42 +++++++------ base-action/src/run-droid.ts | 43 +++++++++---- base-action/test/parse-shell-args.test.ts | 19 +++--- base-action/test/run-droid-mcp.test.ts | 63 +++++++++++-------- base-action/test/run-droid.test.ts | 6 +- base-action/test/setup-droid-settings.test.ts | 4 +- base-action/test/validate-env.test.ts | 4 +- src/entrypoints/prepare.ts | 1 - src/github/token.ts | 38 +++++++---- src/mcp/github-inline-comment-server.ts | 6 +- src/mcp/install-mcp-server.ts | 5 +- src/tag/commands/fill.ts | 2 +- src/utils/parse-tools.ts | 27 ++++---- test/comment-logic.test.ts | 4 +- test/github/token.test.ts | 13 ++-- test/integration/fill-flow.test.ts | 36 +++++------ test/modes/tag/fill-command.test.ts | 9 +-- test/prepare-context.test.ts | 8 ++- test/trigger-validation.test.ts | 1 - test/utils/retry.test.ts | 20 +++--- 20 files changed, 191 insertions(+), 160 deletions(-) diff --git a/README.md b/README.md index b3cfb4d..be17208 100644 --- a/README.md +++ b/README.md @@ -2,8 +2,8 @@ This GitHub Action powers the Factory **Droid** app. It watches your pull requests for the two supported commands and runs a full Droid Exec session to help you ship faster: -* `@droid fill` — turns a bare pull request into a polished description that matches your template or our opinionated fallback. -* `@droid review` — performs an automated code review, surfaces potential bugs, and leaves inline comments directly on the diff. +- `@droid fill` — turns a bare pull request into a polished description that matches your template or our opinionated fallback. +- `@droid review` — performs an automated code review, surfaces potential bugs, and leaves inline comments directly on the diff. Everything runs inside GitHub Actions using your Factory API key, so the bot never leaves your repository and operates with the permissions you grant. @@ -18,11 +18,11 @@ Everything runs inside GitHub Actions using your Factory API key, so the bot nev ## Installation 1. **Install the Droid GitHub App** - * Install from the Factory dashboard and grant it access to the repositories where you want Droid to operate. + - Install from the Factory dashboard and grant it access to the repositories where you want Droid to operate. 2. **Create a Factory API Key** - * Generate a token at [https://app.factory.ai/settings/api-keys](https://app.factory.ai/settings/api-keys) and save it as `FACTORY_API_KEY` in your repository or organization secrets. + - Generate a token at [https://app.factory.ai/settings/api-keys](https://app.factory.ai/settings/api-keys) and save it as `FACTORY_API_KEY` in your repository or organization secrets. 3. **Add the Action Workflows** - * Create two workflow files under `.github/workflows/` to separate on-demand tagging from automatic PR reviews. + - Create two workflow files under `.github/workflows/` to separate on-demand tagging from automatic PR reviews. `droid.yml` (responds to explicit `@droid` mentions): @@ -105,26 +105,28 @@ Once committed, tagging `@droid fill` or `@droid review` on an open PR will trig ## Using the Commands ### `@droid fill` -* Place the command in the PR description or in a top-level comment. -* Droid searches for common PR template locations (`.github/pull_request_template.md`, etc.). When a template exists, it fills the sections; otherwise it writes a structured summary (overview, changes, testing, rollout). -* The original request is replaced with the generated description so reviewers can merge immediately. + +- Place the command in the PR description or in a top-level comment. +- Droid searches for common PR template locations (`.github/pull_request_template.md`, etc.). When a template exists, it fills the sections; otherwise it writes a structured summary (overview, changes, testing, rollout). +- The original request is replaced with the generated description so reviewers can merge immediately. ### `@droid review` -* Mention `@droid review` in a PR comment. -* Droid inspects the diff, prioritizes potential bugs or high-impact issues, and leaves inline comments directly on the changed lines. -* A short summary comment is posted in the original thread highlighting the findings and linking to any inline feedback. + +- Mention `@droid review` in a PR comment. +- Droid inspects the diff, prioritizes potential bugs or high-impact issues, and leaves inline comments directly on the changed lines. +- A short summary comment is posted in the original thread highlighting the findings and linking to any inline feedback. ## Configuration Essentials -| Input | Purpose | -| --- | --- | -| `factory_api_key` | **Required.** Grants Droid Exec permission to run via Factory. | -| `github_token` | Optional override if you prefer a custom GitHub App/token. By default the installed app token is used. | -| `review_model` | Optional. Override the model used for code review (e.g., `claude-sonnet-4-5-20250929`, `gpt-5.1-codex`). Only applies to review flows. | -| `fill_model` | Optional. Override the model used for PR description fill (e.g., `claude-sonnet-4-5-20250929`, `gpt-5.1-codex`). Only applies to fill flows. | +| Input | Purpose | +| ----------------- | -------------------------------------------------------------------------------------------------------------------------------------------- | +| `factory_api_key` | **Required.** Grants Droid Exec permission to run via Factory. | +| `github_token` | Optional override if you prefer a custom GitHub App/token. By default the installed app token is used. | +| `review_model` | Optional. Override the model used for code review (e.g., `claude-sonnet-4-5-20250929`, `gpt-5.1-codex`). Only applies to review flows. | +| `fill_model` | Optional. Override the model used for PR description fill (e.g., `claude-sonnet-4-5-20250929`, `gpt-5.1-codex`). Only applies to fill flows. | ## Troubleshooting & Support -* Check the workflow run linked from the Droid tracking comment for execution logs. -* Verify that the workflow file and repository allow the GitHub App to run (branch protections can block bots). -* Need more detail? Start with the [Setup Guide](./docs/setup.md) or [FAQ](./docs/faq.md). +- Check the workflow run linked from the Droid tracking comment for execution logs. +- Verify that the workflow file and repository allow the GitHub App to run (branch protections can block bots). +- Need more detail? Start with the [Setup Guide](./docs/setup.md) or [FAQ](./docs/faq.md). diff --git a/base-action/src/run-droid.ts b/base-action/src/run-droid.ts index d7cb999..015dc98 100644 --- a/base-action/src/run-droid.ts +++ b/base-action/src/run-droid.ts @@ -68,6 +68,7 @@ function sanitizeJsonOutput( export type DroidOptions = { droidArgs?: string; + reasoningEffort?: string; pathToDroidExecutable?: string; allowedTools?: string; disallowedTools?: string; @@ -90,6 +91,11 @@ export function prepareRunConfig( ): PreparedConfig { const droidArgs = [...BASE_ARGS]; + // Add reasoning effort only when explicitly requested + if (options.reasoningEffort?.trim()) { + droidArgs.push("--reasoning-effort", options.reasoningEffort.trim()); + } + // Parse and add user's custom Droid arguments if (options.droidArgs?.trim()) { const parsed = parseShellArgs(options.droidArgs); @@ -121,10 +127,12 @@ export async function runDroid(promptPath: string, options: DroidOptions) { const cfg = JSON.parse(options.mcpTools); const servers = cfg?.mcpServers || {}; const serverNames = Object.keys(servers); - + if (serverNames.length > 0) { - console.log(`Registering ${serverNames.length} MCP servers: ${serverNames.join(", ")}`); - + console.log( + `Registering ${serverNames.length} MCP servers: ${serverNames.join(", ")}`, + ); + for (const [name, def] of Object.entries(servers)) { const cmd = [def.command, ...(def.args || [])] .filter(Boolean) @@ -143,12 +151,15 @@ export async function runDroid(promptPath: string, options: DroidOptions) { .join(" "); const addCmd = `droid mcp add ${name} "${cmd}" ${envFlags}`.trim(); - + try { await execAsync(addCmd, { env: { ...process.env } }); console.log(` ✓ Registered MCP server: ${name}`); } catch (e: any) { - console.error(` ✗ Failed to register MCP server ${name}:`, e.message); + console.error( + ` ✗ Failed to register MCP server ${name}:`, + e.message, + ); throw e; } } @@ -184,15 +195,19 @@ export async function runDroid(promptPath: string, options: DroidOptions) { // Log custom arguments if any if (options.droidArgs && options.droidArgs.trim() !== "") { console.log(`Custom Droid arguments: ${options.droidArgs}`); - + // Check for deprecated MCP tool naming - const enabledToolsMatch = options.droidArgs.match(/--enabled-tools\s+["\']?([^"\']+)["\']?/); + const enabledToolsMatch = options.droidArgs.match( + /--enabled-tools\s+["\']?([^"\']+)["\']?/, + ); if (enabledToolsMatch && enabledToolsMatch[1]) { - const tools = enabledToolsMatch[1].split(",").map(t => t.trim()); - const oldStyleTools = tools.filter(t => t.startsWith("mcp__")); - + const tools = enabledToolsMatch[1].split(",").map((t) => t.trim()); + const oldStyleTools = tools.filter((t) => t.startsWith("mcp__")); + if (oldStyleTools.length > 0) { - console.warn(`Warning: Found ${oldStyleTools.length} tools with deprecated mcp__ prefix. Update to new pattern (e.g., github_comment___update_droid_comment)`); + console.warn( + `Warning: Found ${oldStyleTools.length} tools with deprecated mcp__ prefix. Update to new pattern (e.g., github_comment___update_droid_comment)`, + ); } } } @@ -247,7 +262,10 @@ export async function runDroid(promptPath: string, options: DroidOptions) { const parsed = JSON.parse(line); if (!sessionId && typeof parsed === "object" && parsed !== null) { const detectedSessionId = parsed.session_id; - if (typeof detectedSessionId === "string" && detectedSessionId.trim()) { + if ( + typeof detectedSessionId === "string" && + detectedSessionId.trim() + ) { sessionId = detectedSessionId; console.log(`Detected Droid session: ${sessionId}`); } @@ -272,7 +290,6 @@ export async function runDroid(promptPath: string, options: DroidOptions) { // In non-full-output mode, suppress non-JSON output } }); - }); // Handle stdout errors diff --git a/base-action/test/parse-shell-args.test.ts b/base-action/test/parse-shell-args.test.ts index b6f0dc4..297d5e3 100644 --- a/base-action/test/parse-shell-args.test.ts +++ b/base-action/test/parse-shell-args.test.ts @@ -8,14 +8,8 @@ describe("shell-quote parseShellArgs", () => { }); test("should parse simple arguments", () => { - expect(parseShellArgs("--auto medium")).toEqual([ - "--auto", - "medium", - ]); - expect(parseShellArgs("-s session-123")).toEqual([ - "-s", - "session-123", - ]); + expect(parseShellArgs("--auto medium")).toEqual(["--auto", "medium"]); + expect(parseShellArgs("-s session-123")).toEqual(["-s", "session-123"]); }); test("should handle double quotes", () => { @@ -27,10 +21,11 @@ describe("shell-quote parseShellArgs", () => { }); test("should handle single quotes", () => { - expect(parseShellArgs("--file '/tmp/prompt.md'")) - .toEqual(["--file", "/tmp/prompt.md"]); - expect(parseShellArgs("'arg with spaces'")) - .toEqual(["arg with spaces"]); + expect(parseShellArgs("--file '/tmp/prompt.md'")).toEqual([ + "--file", + "/tmp/prompt.md", + ]); + expect(parseShellArgs("'arg with spaces'")).toEqual(["arg with spaces"]); }); test("should handle escaped characters", () => { expect(parseShellArgs("arg\\ with\\ spaces")).toEqual(["arg with spaces"]); diff --git a/base-action/test/run-droid-mcp.test.ts b/base-action/test/run-droid-mcp.test.ts index ea652a2..98cde42 100644 --- a/base-action/test/run-droid-mcp.test.ts +++ b/base-action/test/run-droid-mcp.test.ts @@ -74,11 +74,15 @@ const mockSpawn = mock( mock.module("child_process", () => ({ exec: ( command: string, - options?: Record | ((err: Error | null, result?: any) => void), + options?: + | Record + | ((err: Error | null, result?: any) => void), maybeCallback?: (err: Error | null, result?: any) => void, ) => { const callback = - typeof options === "function" ? options : maybeCallback ?? (() => undefined); + typeof options === "function" + ? options + : (maybeCallback ?? (() => undefined)); setImmediate(async () => { try { @@ -98,7 +102,7 @@ let runDroid: RunDroidModule["runDroid"]; beforeAll(async () => { const module = (await import( - `../src/run-droid?mcp-test=${Math.random().toString(36).slice(2)}`, + `../src/run-droid?mcp-test=${Math.random().toString(36).slice(2)}` )) as RunDroidModule; prepareRunConfig = module.prepareRunConfig; runDroid = module.runDroid; @@ -139,23 +143,23 @@ describe("MCP Server Registration", () => { env: { GITHUB_TOKEN: "test-token", REPO_OWNER: "owner", - REPO_NAME: "repo" - } + REPO_NAME: "repo", + }, }, github_ci: { command: "bun", args: ["run", "/path/to/github-actions-server.ts"], env: { GITHUB_TOKEN: "test-token", - PR_NUMBER: "123" - } - } - } + PR_NUMBER: "123", + }, + }, + }, }); const options: DroidOptions = { mcpTools, - pathToDroidExecutable: "droid" + pathToDroidExecutable: "droid", }; const promptPath = await createPromptFile(); const tempDir = process.env.RUNNER_TEMP!; @@ -180,14 +184,14 @@ describe("MCP Server Registration", () => { mcpTools: "", }; const prepared = prepareRunConfig("/tmp/test-prompt.txt", options); - + expect(prepared.droidArgs).not.toContain("--mcp-config"); }); test("should handle invalid JSON in MCP config", () => { const options: DroidOptions = { mcpTools: "{ invalid json }", - pathToDroidExecutable: "droid" + pathToDroidExecutable: "droid", }; // prepareRunConfig doesn't parse MCP config, so it won't throw @@ -205,7 +209,8 @@ describe("MCP Server Registration", () => { console.warn = warnSpy as unknown as typeof console.warn; const options: DroidOptions = { - droidArgs: '--enabled-tools "mcp__github_comment__update_droid_comment,Execute"' + droidArgs: + '--enabled-tools "mcp__github_comment__update_droid_comment,Execute"', }; const promptPath = await createPromptFile(); @@ -216,8 +221,10 @@ describe("MCP Server Registration", () => { const warningMessages = warnSpy.mock.calls.map((args) => args[0]); expect( - warningMessages.some((msg) => - typeof msg === "string" && msg.includes("deprecated mcp__ prefix"), + warningMessages.some( + (msg) => + typeof msg === "string" && + msg.includes("deprecated mcp__ prefix"), ), ).toBe(true); } finally { @@ -232,7 +239,8 @@ describe("MCP Server Registration", () => { console.warn = warnSpy as unknown as typeof console.warn; const options: DroidOptions = { - droidArgs: '--enabled-tools "github_comment___update_droid_comment,Execute"' + droidArgs: + '--enabled-tools "github_comment___update_droid_comment,Execute"', }; const promptPath = await createPromptFile(); @@ -249,14 +257,17 @@ describe("MCP Server Registration", () => { test("should detect MCP tools with triple underscore pattern", () => { const options: DroidOptions = { - droidArgs: '--enabled-tools "github_ci___get_ci_status,github_comment___update_droid_comment"' + droidArgs: + '--enabled-tools "github_ci___get_ci_status,github_comment___update_droid_comment"', }; const prepared = prepareRunConfig("/tmp/test-prompt.txt", options); - + // The args should be passed through correctly expect(prepared.droidArgs).toContain("--enabled-tools"); - expect(prepared.droidArgs).toContain("github_ci___get_ci_status,github_comment___update_droid_comment"); + expect(prepared.droidArgs).toContain( + "github_ci___get_ci_status,github_comment___update_droid_comment", + ); }); }); @@ -267,14 +278,14 @@ describe("MCP Server Registration", () => { failing_server: { command: "nonexistent", args: ["command"], - env: {} - } - } + env: {}, + }, + }, }); const options: DroidOptions = { mcpTools, - pathToDroidExecutable: "droid" + pathToDroidExecutable: "droid", }; const promptPath = await createPromptFile(); const tempDir = process.env.RUNNER_TEMP!; @@ -299,18 +310,18 @@ describe("MCP Server Registration", () => { describe("Environment Variables", () => { test("should include GITHUB_ACTION_INPUTS when present", () => { process.env.INPUT_ACTION_INPUTS_PRESENT = "true"; - + const options: DroidOptions = {}; const prepared = prepareRunConfig("/tmp/test-prompt.txt", options); expect(prepared.env.GITHUB_ACTION_INPUTS).toBe("true"); - + delete process.env.INPUT_ACTION_INPUTS_PRESENT; }); test("should not include GITHUB_ACTION_INPUTS when not present", () => { delete process.env.INPUT_ACTION_INPUTS_PRESENT; - + const options: DroidOptions = {}; const prepared = prepareRunConfig("/tmp/test-prompt.txt", options); diff --git a/base-action/test/run-droid.test.ts b/base-action/test/run-droid.test.ts index 4bb95d2..36f7cb6 100644 --- a/base-action/test/run-droid.test.ts +++ b/base-action/test/run-droid.test.ts @@ -43,7 +43,7 @@ describe("prepareRunConfig", () => { "exec", "--output-format", "stream-json", -"--skip-permissions-unsafe", + "--skip-permissions-unsafe", "--max-turns", "10", "--model", @@ -63,7 +63,7 @@ describe("prepareRunConfig", () => { "exec", "--output-format", "stream-json", - "--skip-permissions-unsafe", + "--skip-permissions-unsafe", "-f", "/tmp/test-prompt.txt", ]); @@ -79,7 +79,7 @@ describe("prepareRunConfig", () => { "exec", "--output-format", "stream-json", - "--skip-permissions-unsafe", + "--skip-permissions-unsafe", "--system-prompt", "You are a helpful assistant", "-f", diff --git a/base-action/test/setup-droid-settings.test.ts b/base-action/test/setup-droid-settings.test.ts index 00dc661..0ae22a4 100644 --- a/base-action/test/setup-droid-settings.test.ts +++ b/base-action/test/setup-droid-settings.test.ts @@ -99,7 +99,9 @@ describe("setupDroidSettings", () => { }); test("should throw error for non-existent file path", async () => { - expect(() => setupDroidSettings("/non/existent/file.json", testHomeDir)).toThrow(); + expect(() => + setupDroidSettings("/non/existent/file.json", testHomeDir), + ).toThrow(); }); test("should handle empty string input", async () => { diff --git a/base-action/test/validate-env.test.ts b/base-action/test/validate-env.test.ts index 67b5a28..0989d29 100644 --- a/base-action/test/validate-env.test.ts +++ b/base-action/test/validate-env.test.ts @@ -16,13 +16,13 @@ describe("validateEnvironmentVariables", () => { }); test("passes when FACTORY_API_KEY is set", () => { - process.env.FACTORY_API_KEY = 'test-factory-key'; + process.env.FACTORY_API_KEY = "test-factory-key"; expect(() => validateEnvironmentVariables()).not.toThrow(); }); test("throws when FACTORY_API_KEY is missing", () => { expect(() => validateEnvironmentVariables()).toThrow( - 'FACTORY_API_KEY is required to run Droid Exec. Please provide the factory_api_key input.' + "FACTORY_API_KEY is required to run Droid Exec. Please provide the factory_api_key input.", ); }); }); diff --git a/src/entrypoints/prepare.ts b/src/entrypoints/prepare.ts index cb09303..5763f35 100644 --- a/src/entrypoints/prepare.ts +++ b/src/entrypoints/prepare.ts @@ -73,7 +73,6 @@ async function run() { if (result?.mcpTools) { core.setOutput("mcp_tools", result.mcpTools); } - } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); core.setFailed(`Prepare step failed with error: ${errorMessage}`); diff --git a/src/github/token.ts b/src/github/token.ts index 986f0b6..471f8ba 100644 --- a/src/github/token.ts +++ b/src/github/token.ts @@ -41,45 +41,57 @@ async function exchangeForAppToken(oidcToken: string): Promise { // Handle the simplified flat error response format const errorCode = responseJson.error || `http_${response.status}`; - const errorMessage = responseJson.message || responseJson.detail || responseJson.error || "Unknown error"; + const errorMessage = + responseJson.message || + responseJson.detail || + responseJson.error || + "Unknown error"; const specificErrorCode = responseJson.error_code; const repository = responseJson.repository; // Check for specific error codes that should skip the action - if (errorCode === "workflow_validation_failed" || - specificErrorCode === "workflow_not_found_on_default_branch") { - core.warning(`Skipping action due to workflow validation: ${errorMessage}`); + if ( + errorCode === "workflow_validation_failed" || + specificErrorCode === "workflow_not_found_on_default_branch" + ) { + core.warning( + `Skipping action due to workflow validation: ${errorMessage}`, + ); console.log( "Action skipped due to workflow validation error. This is expected when adding Droid workflows to new repositories or on PRs with workflow changes. If you're seeing this, your workflow will begin working once you merge your PR.", ); core.setOutput("skipped_due_to_workflow_validation_mismatch", "true"); process.exit(0); } - + // Handle GitHub App not installed error with helpful message if (errorCode === "app_not_installed") { const repo = repository || "this repository"; console.error( `The Factory GitHub App is not installed for ${repo}. ` + - `Please install it at: https://github.com/apps/factory-ai` + `Please install it at: https://github.com/apps/factory-ai`, ); throw new Error(errorMessage); } - + // Handle rate limiting with retry suggestion if (errorCode === "rate_limited") { console.error( - `GitHub API rate limit exceeded. Please wait a few minutes and try again.` + `GitHub API rate limit exceeded. Please wait a few minutes and try again.`, ); throw new Error(errorMessage); } - + // Handle OIDC verification errors if (errorCode === "oidc_verification_failed") { if (specificErrorCode === "token_expired") { - console.error("OIDC token has expired. The workflow may be taking too long."); + console.error( + "OIDC token has expired. The workflow may be taking too long.", + ); } else if (specificErrorCode === "audience_mismatch") { - console.error("OIDC token audience mismatch. This is likely a configuration issue."); + console.error( + "OIDC token audience mismatch. This is likely a configuration issue.", + ); } else if (specificErrorCode === "invalid_signature") { console.error("OIDC token signature verification failed."); } @@ -89,7 +101,7 @@ async function exchangeForAppToken(oidcToken: string): Promise { console.error( `App token exchange failed: ${response.status} ${response.statusText} - ${errorMessage}`, errorCode !== errorMessage ? `(Code: ${errorCode})` : "", - specificErrorCode ? `(Specific: ${specificErrorCode})` : "" + specificErrorCode ? `(Specific: ${specificErrorCode})` : "", ); throw new Error(errorMessage); } @@ -99,7 +111,7 @@ async function exchangeForAppToken(oidcToken: string): Promise { token?: string; expires_at?: string; }; - + if (!appTokenData.token) { throw new Error("App token not found in response"); } diff --git a/src/mcp/github-inline-comment-server.ts b/src/mcp/github-inline-comment-server.ts index 3b9ae31..2a51185 100644 --- a/src/mcp/github-inline-comment-server.ts +++ b/src/mcp/github-inline-comment-server.ts @@ -59,9 +59,9 @@ server.tool( .optional() .default("RIGHT") .describe( - "Side of the diff to comment on: LEFT (old code) or RIGHT (new code). " + - "IMPORTANT: Use RIGHT for comments on new/modified code. " + - "Only use LEFT when specifically referencing code being removed." + "Side of the diff to comment on: LEFT (old code) or RIGHT (new code). " + + "IMPORTANT: Use RIGHT for comments on new/modified code. " + + "Only use LEFT when specifically referencing code being removed.", ), commit_id: z .string() diff --git a/src/mcp/install-mcp-server.ts b/src/mcp/install-mcp-server.ts index 44b5c46..0714523 100644 --- a/src/mcp/install-mcp-server.ts +++ b/src/mcp/install-mcp-server.ts @@ -93,7 +93,6 @@ export async function prepareMcpTools( }, }; - // Include inline comment server for PRs when requested via allowed tools if ( isEntityContext(context) && @@ -118,9 +117,7 @@ export async function prepareMcpTools( const hasWorkflowToken = !!process.env.DEFAULT_WORKFLOW_TOKEN; const shouldIncludeCIServer = - isEntityContext(context) && - context.isPR && - hasWorkflowToken; + isEntityContext(context) && context.isPR && hasWorkflowToken; if (shouldIncludeCIServer) { // Verify the token actually has actions:read permission diff --git a/src/tag/commands/fill.ts b/src/tag/commands/fill.ts index 67a5e19..1cc7e31 100644 --- a/src/tag/commands/fill.ts +++ b/src/tag/commands/fill.ts @@ -33,7 +33,7 @@ export async function prepareFillMode({ const commentId = trackingCommentId ?? (await createInitialComment(octokit.rest, context)).id; - + const prData = await fetchPRBranchData({ octokits: octokit, repository: context.repository, diff --git a/src/utils/parse-tools.ts b/src/utils/parse-tools.ts index 9f6e065..9020401 100644 --- a/src/utils/parse-tools.ts +++ b/src/utils/parse-tools.ts @@ -22,7 +22,10 @@ export function parseAllowedTools(args: string): string[] { return []; } - return value.split(",").map((tool) => tool.trim()).filter(Boolean); + return value + .split(",") + .map((tool) => tool.trim()) + .filter(Boolean); } export function normalizeDroidArgs(args: string): string { @@ -30,14 +33,16 @@ export function normalizeDroidArgs(args: string): string { return ""; } - return args - .replace(/--allowedTools/g, "--enabled-tools") - .replace(/--allowed-tools/g, "--enabled-tools") - .replace(/--enabledTools/g, "--enabled-tools") - .replace(/--disallowedTools/g, "--disabled-tools") - .replace(/--disabled-tools/g, "--disabled-tools") - .replace(/--disallowed-tools/g, "--disabled-tools") - // Strip unsupported MCP inline config flags to avoid CLI errors - .replace(/--mcp-config\s+(?:"[^"]*"|'[^']*'|[^\s]+)/g, "") - .trim(); + return ( + args + .replace(/--allowedTools/g, "--enabled-tools") + .replace(/--allowed-tools/g, "--enabled-tools") + .replace(/--enabledTools/g, "--enabled-tools") + .replace(/--disallowedTools/g, "--disabled-tools") + .replace(/--disabled-tools/g, "--disabled-tools") + .replace(/--disallowed-tools/g, "--disabled-tools") + // Strip unsupported MCP inline config flags to avoid CLI errors + .replace(/--mcp-config\s+(?:"[^"]*"|'[^']*'|[^\s]+)/g, "") + .trim() + ); } diff --git a/test/comment-logic.test.ts b/test/comment-logic.test.ts index 33bb2e6..b068949 100644 --- a/test/comment-logic.test.ts +++ b/test/comment-logic.test.ts @@ -279,9 +279,7 @@ describe("updateCommentBody", () => { }; const result = updateCommentBody(input); - expect(result).toContain( - "**Droid finished @testuser's task in 1m 15s**", - ); + expect(result).toContain("**Droid finished @testuser's task in 1m 15s**"); }); it("includes duration in error header", () => { diff --git a/test/github/token.test.ts b/test/github/token.test.ts index d5983a6..a4d8a5a 100644 --- a/test/github/token.test.ts +++ b/test/github/token.test.ts @@ -34,15 +34,14 @@ describe("setupGitHubToken", () => { process.env.OVERRIDE_GITHUB_TOKEN = "override-token"; const setOutputSpy = spyOn(core, "setOutput").mockImplementation(() => {}); - const getIdTokenSpy = spyOn(core, "getIDToken").mockResolvedValue("oidc-token"); + const getIdTokenSpy = spyOn(core, "getIDToken").mockResolvedValue( + "oidc-token", + ); const result = await setupGitHubToken(); expect(result).toBe("override-token"); - expect(setOutputSpy).toHaveBeenCalledWith( - "GITHUB_TOKEN", - "override-token", - ); + expect(setOutputSpy).toHaveBeenCalledWith("GITHUB_TOKEN", "override-token"); expect(getIdTokenSpy).not.toHaveBeenCalled(); setOutputSpy.mockRestore(); @@ -59,7 +58,9 @@ describe("setupGitHubToken", () => { global.fetch = fetchMock as unknown as typeof fetch; const setOutputSpy = spyOn(core, "setOutput").mockImplementation(() => {}); - const getIdTokenSpy = spyOn(core, "getIDToken").mockResolvedValue("oidc-token"); + const getIdTokenSpy = spyOn(core, "getIDToken").mockResolvedValue( + "oidc-token", + ); const retrySpy = spyOn(retryModule, "retryWithBackoff").mockImplementation( (operation: () => Promise) => operation(), ); diff --git a/test/integration/fill-flow.test.ts b/test/integration/fill-flow.test.ts index 21ea50c..ed41bc5 100644 --- a/test/integration/fill-flow.test.ts +++ b/test/integration/fill-flow.test.ts @@ -1,11 +1,4 @@ -import { - afterEach, - beforeEach, - describe, - expect, - it, - spyOn, -} from "bun:test"; +import { afterEach, beforeEach, describe, expect, it, spyOn } from "bun:test"; import path from "node:path"; import os from "node:os"; import { mkdtemp, readFile, rm } from "node:fs/promises"; @@ -93,17 +86,18 @@ describe("fill command integration", () => { } as any, }); - const octokit = { - rest: {}, - graphql: () => Promise.resolve({ - repository: { - pullRequest: { - baseRefName: "main", - headRefName: "feature/fill", - headRefOid: "abc123", - } - } - }) + const octokit = { + rest: {}, + graphql: () => + Promise.resolve({ + repository: { + pullRequest: { + baseRefName: "main", + headRefName: "feature/fill", + headRefOid: "abc123", + }, + }, + }), } as any; graphqlSpy = spyOn(octokit, "graphql").mockResolvedValue({ @@ -112,8 +106,8 @@ describe("fill command integration", () => { baseRefName: "main", headRefName: "feature/fill", headRefOid: "abc123", - } - } + }, + }, }); const result = await prepareTagExecution({ diff --git a/test/modes/tag/fill-command.test.ts b/test/modes/tag/fill-command.test.ts index 46b6cdf..ad3f061 100644 --- a/test/modes/tag/fill-command.test.ts +++ b/test/modes/tag/fill-command.test.ts @@ -1,11 +1,4 @@ -import { - afterEach, - beforeEach, - describe, - expect, - it, - spyOn, -} from "bun:test"; +import { afterEach, beforeEach, describe, expect, it, spyOn } from "bun:test"; import * as core from "@actions/core"; import { prepareFillMode } from "../../../src/tag/commands/fill"; import { createMockContext } from "../../mockContext"; diff --git a/test/prepare-context.test.ts b/test/prepare-context.test.ts index e228822..d391956 100644 --- a/test/prepare-context.test.ts +++ b/test/prepare-context.test.ts @@ -72,7 +72,9 @@ describe("parseEnvVarsWithContext", () => { test("should throw error when DROID_BRANCH is missing", () => { expect(() => prepareContext(mockIssueCommentContext, "12345", "main"), - ).toThrow("issue_comment on issues requires droidBranch and baseBranch"); + ).toThrow( + "issue_comment on issues requires droidBranch and baseBranch", + ); }); test("should throw error when BASE_BRANCH is missing", () => { @@ -83,7 +85,9 @@ describe("parseEnvVarsWithContext", () => { undefined, "droid/issue-67890-20240101-1200", ), - ).toThrow("issue_comment on issues requires droidBranch and baseBranch"); + ).toThrow( + "issue_comment on issues requires droidBranch and baseBranch", + ); }); }); diff --git a/test/trigger-validation.test.ts b/test/trigger-validation.test.ts index 3ca63f0..46bdf8f 100644 --- a/test/trigger-validation.test.ts +++ b/test/trigger-validation.test.ts @@ -21,7 +21,6 @@ import type { import type { ParsedGitHubContext } from "../src/github/context"; describe("checkContainsTrigger", () => { - // TODO: Assignee and label triggers are disabled until instruct mode is implemented. // These tests verify the triggers are no-ops for now. describe("assignee trigger (disabled)", () => { diff --git a/test/utils/retry.test.ts b/test/utils/retry.test.ts index 36a58f0..84a584d 100644 --- a/test/utils/retry.test.ts +++ b/test/utils/retry.test.ts @@ -5,14 +5,14 @@ describe("retryWithBackoff", () => { let timeoutSpy: ReturnType; beforeEach(() => { - timeoutSpy = spyOn(globalThis, "setTimeout").mockImplementation( - ((handler: Parameters[0]) => { - if (typeof handler === "function") { - handler(); - } - return 0 as unknown as ReturnType; - }) as unknown as typeof setTimeout, - ); + timeoutSpy = spyOn(globalThis, "setTimeout").mockImplementation((( + handler: Parameters[0], + ) => { + if (typeof handler === "function") { + handler(); + } + return 0 as unknown as ReturnType; + }) as unknown as typeof setTimeout); }); afterEach(() => { @@ -64,7 +64,9 @@ describe("retryWithBackoff", () => { expect(attempts).toBe(2); expect(timeoutSpy).toHaveBeenCalledTimes(1); - const firstCall = timeoutSpy.mock.calls[0] as Parameters | undefined; + const firstCall = timeoutSpy.mock.calls[0] as + | Parameters + | undefined; expect(firstCall?.[1]).toBe(5); }); });