From 66fef9538f050f0bcd740dc07aacb9e9b657ffd3 Mon Sep 17 00:00:00 2001 From: Evan Sosenko Date: Wed, 5 Aug 2026 22:13:43 -0700 Subject: [PATCH] Fix cycle linter error --- src/bin/cli.ts | 3 ++- src/lib/commands/local/completion.ts | 33 ++++++++++++++-------------- src/lib/commands/registry.ts | 6 ++++- 3 files changed, 24 insertions(+), 18 deletions(-) diff --git a/src/bin/cli.ts b/src/bin/cli.ts index dffced76..d1aebc35 100644 --- a/src/bin/cli.ts +++ b/src/bin/cli.ts @@ -114,7 +114,8 @@ async function cli(args: ParsedArgs, argv: string[]) { isLocal: true, }) - await printCompletion(shell, { update }) + const cachedBlueprint = await getApiBlueprint({ update }) + printCompletion(shell, buildRegistry(cachedBlueprint).spec) return } diff --git a/src/lib/commands/local/completion.ts b/src/lib/commands/local/completion.ts index 729f3a28..1ca51042 100644 --- a/src/lib/commands/local/completion.ts +++ b/src/lib/commands/local/completion.ts @@ -1,5 +1,6 @@ -import { getApiBlueprint } from 'lib/blueprint/index.js' +import type { ApiBlueprint } from 'lib/blueprint/index.js' import type { Command } from 'lib/commands/registry.js' +import type { CommandSpec } from 'lib/commands/spec.js' import { getOutput } from 'lib/output/get-output.js' import { type CompletionShell, @@ -16,19 +17,19 @@ import { * Called by the entry before any auth or blueprint context exists, and by * the registered command's executor — one implementation for both. */ -export const printCompletion = async ( +export const printCompletion = ( shell: CompletionShell, - { update = false }: { update?: boolean } = {}, -): Promise => { - // Deferred import: the registry lists this module's commands, so a static - // import back into it would be a cycle. - const { buildRegistry } = await import('lib/commands/registry.js') - const blueprint = await getApiBlueprint({ update }) - const { spec } = buildRegistry(blueprint) + spec: CommandSpec, +): void => { getOutput().text(renderCompletion(shell, spec)) } -const completionCommand = (shell: CompletionShell): Command => ({ +type BuildSpec = (blueprint: ApiBlueprint) => CommandSpec + +const completionCommand = ( + shell: CompletionShell, + buildSpec: BuildSpec, +): Command => ({ definition: { path: ['completion', shell], kind: 'cli', @@ -37,14 +38,14 @@ const completionCommand = (shell: CompletionShell): Command => ({ flags: [], }, requiresAuth: false, - execute: async ({ args }) => { - await printCompletion(shell, { update: args['update'] === true }) + execute: async (_invocation, ctx) => { + printCompletion(shell, buildSpec(ctx.blueprint)) return { kind: 'done' } }, }) -export const completionCommands: Command[] = [ - completionCommand('bash'), - completionCommand('fish'), - completionCommand('zsh'), +export const createCompletionCommands = (buildSpec: BuildSpec): Command[] => [ + completionCommand('bash', buildSpec), + completionCommand('fish', buildSpec), + completionCommand('zsh', buildSpec), ] diff --git a/src/lib/commands/registry.ts b/src/lib/commands/registry.ts index 19e77a6a..f40175cb 100644 --- a/src/lib/commands/registry.ts +++ b/src/lib/commands/registry.ts @@ -5,7 +5,7 @@ import type { ApiBlueprint } from 'lib/blueprint/index.js' import type { CliContext } from 'lib/context.js' import { executeApiCommand } from './api-command.js' -import { completionCommands } from './local/completion.js' +import { createCompletionCommands } from './local/completion.js' import { configRevealLocationCommand } from './local/config-reveal-location.js' import { configSetFakeServerCommand } from './local/config-set-fake-server.js' import { configUseRemoteApiDefsCommand } from './local/config-use-remote-api-defs.js' @@ -65,6 +65,10 @@ export interface CommandRegistry { * blueprint. The single source of truth: the spec, the picker, and the * dispatcher all consume this list. */ +const completionCommands = createCompletionCommands( + (blueprint) => buildRegistry(blueprint).spec, +) + export const localCommands: Command[] = [ ...completionCommands, configRevealLocationCommand,