From ea0a763ed0d3cafcfeaa6a5213c597a16fc664ea Mon Sep 17 00:00:00 2001 From: NekoPunch Date: Thu, 10 Sep 2026 22:36:31 -0700 Subject: [PATCH] refactor(cli): give each command its own help screen MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `maka --help` printed 110 lines, 62 of them the Runtime Host service, access, plugin and profile surfaces that only an operator installing a service needs. A newcomer asking what the tool does met a wall of administration syntax, and the six option sections meant the root screen carried titles like "Managed Runtime Host direct-peer options". Help is now layered the way `run` and `activate` already were: the root lists the top-level commands and the TUI options in 25 lines, and `runtime-host --help` and `runtime-host --help` carry what belongs to them. `update --help` gains a screen it never had — it used to answer "Unexpected argument". Column widths are measured rather than fixed, so a development launcher ("npm run cli:dev --") aligns like the packaged `maka` name. Generated-by: Claude Code --- packages/cli/src/__tests__/cli.test.ts | 120 +++++++++-- packages/cli/src/cli-core.ts | 139 +++---------- packages/cli/src/runtime-host-cli.ts | 33 +++- packages/cli/src/runtime-host-help.ts | 262 +++++++++++++++++++++++++ 4 files changed, 418 insertions(+), 136 deletions(-) create mode 100644 packages/cli/src/runtime-host-help.ts diff --git a/packages/cli/src/__tests__/cli.test.ts b/packages/cli/src/__tests__/cli.test.ts index 09ed0228b9..ae6a7ceb74 100644 --- a/packages/cli/src/__tests__/cli.test.ts +++ b/packages/cli/src/__tests__/cli.test.ts @@ -46,17 +46,17 @@ describe('Maka CLI args', () => { const help = parseMakaCliArgs(['--help'], '0.1.0'); assert.equal(help.kind, 'help'); if (help.kind !== 'help') return; - assert.match(help.text, /^ maka Start the TUI$/m); + assert.match(help.text, /^ {2}maka {2,}Start the TUI$/m); assert.doesNotMatch(help.text, /maka-agent/); - assert.match(help.text, /^ maka run /m); - assert.match(help.text, /^ maka activate /m); - assert.match(help.text, /^ maka eval /m); - assert.match(help.text, /^ maka update --target /m); - assert.match( - help.text, - /^ maka --acp Serve ACP v1 over stdio \(initialize, session\/new, session\/list\)$/m, - ); - assert.match(help.text, /^ maka runtime-host serve /m); + assert.match(help.text, /^ {2}maka run /m); + assert.match(help.text, /^ {2}maka activate /m); + assert.match(help.text, /^ {2}maka eval /m); + assert.match(help.text, /^ {2}maka update /m); + assert.match(help.text, /^ {2}maka --acp {2,}Serve ACP v1 over stdio /m); + // Runtime Host owns its own help; the root lists it once and points there. + assert.match(help.text, /^ {2}maka runtime-host \.\.\. {2,}Serve and manage a Runtime Host$/m); + assert.doesNotMatch(help.text, /^ {2}maka runtime-host (?:serve|service|access) /m); + assert.ok(help.text.split('\n').length < 30, 'the root help stays a single screen'); assert.doesNotMatch(help.text, /cli:dev/); }); @@ -118,18 +118,27 @@ describe('Maka CLI args', () => { assert.equal(help.kind, 'help'); if (help.kind === 'help') { assert.match(help.text, /^Usage: npm run cli:dev --$/m); + assert.match(help.text, /^ {2}npm run cli:dev -- runtime-host \.\.\. /m); + assert.doesNotMatch(help.text, /^ {2}maka runtime-host /m); + assert.doesNotMatch(help.text, /maka-agent/); + } + // The launcher name has to survive every layer, not just the root. + const hostHelp = parseMakaCliArgs(['runtime-host', '--help'], '0.1.0', 'npm run cli:dev --'); + assert.equal(hostHelp.kind, 'help'); + if (hostHelp.kind === 'help') { + assert.match(hostHelp.text, /^Usage: npm run cli:dev -- runtime-host /m); + } + const accessHelp = parseMakaCliArgs( + ['runtime-host', 'access', '--help'], + '0.1.0', + 'npm run cli:dev --', + ); + assert.equal(accessHelp.kind, 'help'); + if (accessHelp.kind === 'help') { assert.match( - help.text, - /^ npm run cli:dev -- runtime-host access issue --principal --preset /m, - ); - assert.match(help.text, /^ npm run cli:dev -- runtime-host project list /m); - assert.match( - help.text, - /^ npm run cli:dev -- runtime-host profile set .*--ssh-destination /m, + accessHelp.text, + /^ {2}npm run cli:dev -- runtime-host access issue --principal --preset /m, ); - assert.match(help.text, /^ npm run cli:dev -- runtime-host profile set .*--plaintext-url /m); - assert.doesNotMatch(help.text, /^ maka runtime-host /m); - assert.doesNotMatch(help.text, /maka-agent/); } await assert.rejects( runMakaCli(['--version'], { @@ -366,3 +375,74 @@ async function readClientInstanceId(path: string): Promise { } return document.clientInstanceId; } + +describe('layered help coverage', () => { + const screens = (launcher = 'maka') => { + const texts: string[] = []; + const grab = (argv: string[]) => { + const command = parseMakaCliArgs(argv, '0.1.0', launcher); + if (command.kind === 'help') texts.push(command.text); + }; + grab(['--help']); + grab(['update', '--help']); + grab(['session-export', '--help']); + grab(['session-import', '--help']); + grab(['runtime-host', '--help']); + for (const topic of [ + 'activate', + 'serve', + 'setup', + 'service', + 'access', + 'project', + 'plugin', + 'profile', + 'capability-provider', + ]) { + grab(['runtime-host', topic, '--help']); + } + return texts.join('\n'); + }; + + test('every command the parser accepts has a help screen', () => { + const all = screens(); + for (const command of [ + 'runtime-host activate', + 'runtime-host serve', + 'runtime-host setup', + 'runtime-host service', + 'runtime-host access', + 'runtime-host project', + 'runtime-host plugin', + 'runtime-host profile', + 'runtime-host capability-provider', + 'session-export', + 'session-import', + 'update', + ]) { + assert.ok(all.includes(`maka ${command}`), `${command} is unreachable from any help screen`); + } + }); + + test('the credential environment variable stays documented', () => { + // Its only other home was the root screen, which the layering shrank. + assert.match(screens(), /MAKA_RUNTIME_HOST_ACCESS_CREDENTIAL/); + }); + + test('every summary in the root screen shares one column', () => { + const help = parseMakaCliArgs(['--help'], '0.1.0'); + assert.equal(help.kind, 'help'); + if (help.kind !== 'help') return; + const summaryColumns = help.text + .split('\n') + .filter((line) => /^ {2}maka\b/.test(line)) + .map((line) => line.search(/(?<= {2})\S(?!.*\s{2}\S)/u)) + .filter((column) => column > 0); + assert.ok(summaryColumns.length > 5, 'the root screen should list several commands'); + assert.equal( + new Set(summaryColumns).size, + 1, + `summaries start at ${[...new Set(summaryColumns)].join(', ')}`, + ); + }); +}); diff --git a/packages/cli/src/cli-core.ts b/packages/cli/src/cli-core.ts index 41cbcea1d9..d1e0365651 100644 --- a/packages/cli/src/cli-core.ts +++ b/packages/cli/src/cli-core.ts @@ -30,6 +30,7 @@ import { parseRuntimeHostInstalledUpdateCommand, type RuntimeHostCliCommand, } from './runtime-host-cli.js'; +import { sessionBundleHelpText } from './runtime-host-help.js'; import { resolveCliUiLocale } from './cli-ui-locale.js'; export type MakaCliCommand = @@ -85,11 +86,14 @@ export function parseMakaCliArgs( if (first?.startsWith('--')) return parseTuiArgs(argv); if (first === 'run' || first === '-p') return { kind: 'run', args: argv.slice(1) }; if (first === 'activate') return { kind: 'activate', args: argv.slice(1) }; - if (first === 'session-export') return { kind: 'session-export', args: argv.slice(1) }; - if (first === 'session-import') return { kind: 'session-import', args: argv.slice(1) }; + if (first === 'session-export' || first === 'session-import') { + return argv[1] === '--help' || argv[1] === '-h' + ? { kind: 'help', text: sessionBundleHelpText(cliCommand, first) } + : { kind: first, args: argv.slice(1) }; + } if (first === 'eval') return { kind: 'eval', args: argv.slice(1) }; - if (first === 'update') return parseRuntimeHostInstalledUpdateCommand(argv.slice(1)); - if (first === 'runtime-host') return parseRuntimeHostCommand(argv.slice(1)); + if (first === 'update') return parseRuntimeHostInstalledUpdateCommand(argv.slice(1), cliCommand); + if (first === 'runtime-host') return parseRuntimeHostCommand(argv.slice(1), cliCommand); return { kind: 'error', message: `Unexpected argument: ${first ?? ''}`, @@ -136,48 +140,27 @@ function helpText(cliCommand: string): string { 'Launches the Maka terminal UI in the current working directory.', '', 'Commands:', - ` ${cliCommand} Start the TUI`, - ` ${cliCommand} --acp Serve ACP v1 over stdio (initialize, session/new, session/list)`, - ` ${cliCommand} run ... Run one non-interactive model turn`, - ` ${cliCommand} activate ... Run one Cloud Session activation and emit JSONL`, - ` ${cliCommand} session-export --workspace-root --session --out `, - ` ${cliCommand} session-import --workspace-root --bundle `, - ` ${cliCommand} -p ... Alias for ${cliCommand} run`, - ` ${cliCommand} eval ... Run one declarative multi-arm experiment`, - ` ${cliCommand} update --target Update this npm-global CLI and its local Runtime Host`, - ` ${cliCommand} runtime-host serve [options] Run a Runtime Host service`, - ` ${cliCommand} runtime-host activate --framed --root-id `, - ` ${cliCommand} runtime-host setup --principal --preset [options]`, - ` ${cliCommand} runtime-host service install [options]`, - ` ${cliCommand} runtime-host service configure (--project-root