diff --git a/src/cli.ts b/src/cli.ts index c7a0ed4..2e5f17e 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -256,7 +256,7 @@ function booleanFlag(flags: Flags, key: string, usage: string): boolean { export async function handleCliCommand(argv: string[]): Promise { const rawCommand = argv[0]; - if (!rawCommand || rawCommand === '--help') { + if (!rawCommand || rawCommand === '--help' || rawCommand === 'help') { printUsage(); return; } diff --git a/src/index.ts b/src/index.ts index e7c829e..3e7a989 100644 --- a/src/index.ts +++ b/src/index.ts @@ -93,7 +93,8 @@ const CLI_SUBCOMMANDS = [ 'refs', 'cycles', 'init', - 'map' + 'map', + 'help' ]; // Check if this module is the entry point. @@ -108,6 +109,10 @@ const isDirectCliSubcommand = // Resolve optional bootstrap root with validation handled later in main(). function resolveRootPath(): string | undefined { + if (isDirectRun && directSubcommand === 'help') { + return undefined; + } + const envPath = process.env.CODEBASE_ROOT; // Walk argv starting at position 2, skip known flags and their values. diff --git a/tests/cli-entrypoint-runtime.test.ts b/tests/cli-entrypoint-runtime.test.ts index 7bdb291..1bef25a 100644 --- a/tests/cli-entrypoint-runtime.test.ts +++ b/tests/cli-entrypoint-runtime.test.ts @@ -1,5 +1,7 @@ import { spawnSync } from 'node:child_process'; -import { resolve } from 'node:path'; +import { mkdir, mkdtemp, readdir, rm } from 'node:fs/promises'; +import { tmpdir } from 'node:os'; +import { join, resolve } from 'node:path'; import { describe, expect, it } from 'vitest'; const root = resolve(import.meta.dirname, '..'); @@ -54,4 +56,47 @@ describe('CLI entrypoint runtime', () => { expect(parsed.rootPath).toBe(root); expect(parsed.hint).toContain('refresh_index'); }, 120_000); + + it('prints usage for help from a project containing a directory named help', async () => { + const projectRoot = await mkdtemp(join(tmpdir(), 'cbc-cli-help-')); + const helpDirectory = join(projectRoot, 'help'); + + const env = { ...process.env }; + delete env.CODEBASE_ROOT; + + try { + await mkdir(helpDirectory); + + const result = spawnSync( + process.execPath, + ['--import', import.meta.resolve('tsx'), entrypoint, 'help'], + { + cwd: projectRoot, + env, + encoding: 'utf8', + timeout: 10_000 + } + ); + + expect({ + errorCode: result.error?.code, + exitCode: result.status, + helpText: result.stdout.includes('codebase-context [options]'), + commandsSection: result.stdout.includes('Commands:'), + stderr: result.stderr, + projectEntries: await readdir(projectRoot), + helpEntries: await readdir(helpDirectory) + }).toEqual({ + errorCode: undefined, + exitCode: 0, + helpText: true, + commandsSection: true, + stderr: '', + projectEntries: ['help'], + helpEntries: [] + }); + } finally { + await rm(projectRoot, { recursive: true, force: true }); + } + }, 70_000); });