From 942a85eaf4e257bd73c86308dadad778ebb49bb0 Mon Sep 17 00:00:00 2001 From: Stephen Dolan Date: Tue, 29 Sep 2026 11:24:27 -0400 Subject: [PATCH] fix: migrate biome.json to Biome 2 and gate formatting in CI The installed Biome 2 rejected the Biome 1 `files.include`/`ignore` keys, so `format:check` aborted before checking anything. Translate them to `files.includes` with `!` negations (via `biome migrate`), apply the formatter's pending line-wrapping to three files, and run format:check in CI so the config and the source cannot drift unnoticed again. Co-Authored-By: Claude Opus 5.5 --- .github/workflows/ci.yml | 1 + CLAUDE.md | 3 +++ biome.json | 5 ++--- src/lib/__tests__/errors.test.ts | 7 +++++- src/lib/__tests__/pipe-truncation.test.ts | 4 +--- src/mcp/server.ts | 27 +++++++++++++++++------ 6 files changed, 33 insertions(+), 14 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6a0b490..9f9797e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -19,5 +19,6 @@ jobs: - run: bun install - run: bun run typecheck - run: bun run lint + - run: bun run format:check - run: bun run build - run: bun test diff --git a/CLAUDE.md b/CLAUDE.md index 1e46ce4..f6e12d8 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -14,8 +14,11 @@ bun install # Install dependencies bun run build # Build TypeScript to dist/ bun run dev # Watch mode for development bun link # Link binary for local testing (creates `of` command) +bun run format # Format src/ with Biome (format:check runs in CI) ``` +Biome is v2: `biome.json` uses `files.includes` with `!` negations. Biome 1 keys (`include`, `ignore`) make it abort before formatting. After a Biome major bump, run `bunx biome migrate --write`. + ### Testing the CLI After `bun link`, use `of` command globally: ```bash diff --git a/biome.json b/biome.json index 84ec880..2cb99b9 100644 --- a/biome.json +++ b/biome.json @@ -1,13 +1,12 @@ { - "$schema": "https://biomejs.dev/schemas/1.9.4/schema.json", + "$schema": "https://biomejs.dev/schemas/2.3.11/schema.json", "vcs": { "enabled": true, "clientKind": "git", "useIgnoreFile": true }, "files": { - "include": ["src/**/*.ts"], - "ignore": ["dist", "node_modules"] + "includes": ["**/src/**/*.ts", "!**/dist", "!**/node_modules"] }, "formatter": { "enabled": true, diff --git a/src/lib/__tests__/errors.test.ts b/src/lib/__tests__/errors.test.ts index eace399..00bfd70 100644 --- a/src/lib/__tests__/errors.test.ts +++ b/src/lib/__tests__/errors.test.ts @@ -24,7 +24,12 @@ interface HandleErrorResult { } async function runHandleError( - scenario: 'omnifocus_cli_error' | 'error_not_found' | 'error_multiple' | 'error_plain' | 'non_error' + scenario: + | 'omnifocus_cli_error' + | 'error_not_found' + | 'error_multiple' + | 'error_plain' + | 'non_error' ): Promise { const tmp = await mkdtemp(join(tmpdir(), 'of-errors-test-')); const script = join(tmp, 'run.mjs'); diff --git a/src/lib/__tests__/pipe-truncation.test.ts b/src/lib/__tests__/pipe-truncation.test.ts index 9a7029e..cda8719 100644 --- a/src/lib/__tests__/pipe-truncation.test.ts +++ b/src/lib/__tests__/pipe-truncation.test.ts @@ -39,9 +39,7 @@ async function pipedStdoutLength(args: string[]): Promise { describe('pipe truncation regression (issue #20)', () => { beforeAll(() => { if (!existsSync(distCli)) { - throw new Error( - `dist/cli.js not found — run \`bun run build\` before this test suite.` - ); + throw new Error(`dist/cli.js not found — run \`bun run build\` before this test suite.`); } }); diff --git a/src/mcp/server.ts b/src/mcp/server.ts index 56f6720..afe6367 100644 --- a/src/mcp/server.ts +++ b/src/mcp/server.ts @@ -113,9 +113,13 @@ server.tool( async ({ query }) => jsonResponse(await of.searchTasks(query)) ); -server.tool('get_task_stats', 'Get task statistics', {}, async () => jsonResponse(await of.getTaskStats())); +server.tool('get_task_stats', 'Get task statistics', {}, async () => + jsonResponse(await of.getTaskStats()) +); -server.tool('list_inbox', 'List all inbox tasks', {}, async () => jsonResponse(await of.listInboxTasks())); +server.tool('list_inbox', 'List all inbox tasks', {}, async () => + jsonResponse(await of.listInboxTasks()) +); server.tool('get_inbox_count', 'Get the number of inbox tasks', {}, async () => jsonResponse({ count: await of.getInboxCount() }) @@ -178,7 +182,9 @@ server.tool( } ); -server.tool('get_project_stats', 'Get project statistics', {}, async () => jsonResponse(await of.getProjectStats())); +server.tool('get_project_stats', 'Get project statistics', {}, async () => + jsonResponse(await of.getProjectStats()) +); server.tool('list_perspectives', 'List all available perspectives', {}, async () => jsonResponse(await of.listPerspectives()) @@ -241,7 +247,9 @@ server.tool( } ); -server.tool('get_tag_stats', 'Get tag statistics', {}, async () => jsonResponse(await of.getTagStats())); +server.tool('get_tag_stats', 'Get tag statistics', {}, async () => + jsonResponse(await of.getTagStats()) +); server.tool( 'list_folders', @@ -257,19 +265,24 @@ server.tool( idOrName: z.string().describe('Folder ID or name'), includeDropped: z.boolean().optional().describe('Include dropped children'), }, - async ({ idOrName, includeDropped }) => jsonResponse(await of.getFolder(idOrName, { includeDropped })) + async ({ idOrName, includeDropped }) => + jsonResponse(await of.getFolder(idOrName, { includeDropped })) ); server.tool( 'search_tools', 'Search for available tools by name or description using regex. Returns matching tool names.', { - query: z.string().describe('Regex pattern to match against tool names and descriptions (case-insensitive)'), + query: z + .string() + .describe('Regex pattern to match against tool names and descriptions (case-insensitive)'), }, async ({ query }) => { try { const pattern = new RegExp(query, 'i'); - const matches = toolRegistry.filter((t) => pattern.test(t.name) || pattern.test(t.description)); + const matches = toolRegistry.filter( + (t) => pattern.test(t.name) || pattern.test(t.description) + ); return jsonResponse({ tools: matches }); } catch { return jsonResponse({ error: 'Invalid regex pattern' });