mirror of
https://github.com/tiennm99/ccs.git
synced 2026-10-11 18:12:36 +00:00
fix(dispatch): route unknown bar/copilot/cursor subcommands and survive corrupt config
Pre-dispatch only routed known subcommand tokens, so unknown bar/copilot/cursor subcommands never reached their handlers (no error, no non-zero exit). Route them through. Also wrap autoMigrate() so a corrupt config.yaml no longer crashes startup, and print only the first line of a best-effort recovery failure.
This commit is contained in:
1 parent
9258aed1ff
commit
2900743767
2 files changed
+29
-14
No files matched your search
@@ -66,6 +66,7 @@ export async function handleBarCommand(args: string[]): Promise<void> {
|
||||
if (!handler) {
|
||||
console.error(`[X] Unknown bar subcommand: ${subcommand}`);
|
||||
console.error('[i] Usage: ccs bar [launch|serve|stop|status|install|uninstall|version|--help]');
|
||||
process.exitCode = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -68,10 +68,19 @@ export async function runPreDispatchHandlers(ctx: PreDispatchContext): Promise<b
|
||||
}
|
||||
|
||||
// Auto-migrate to unified config format (silent if already migrated)
|
||||
// Skip if user is explicitly running migrate command
|
||||
// Skip if user is explicitly running migrate command.
|
||||
// Wrapped in try-catch so a corrupt config.yaml does not crash pre-dispatch
|
||||
// before doctor/setup can run and report the problem to the user.
|
||||
if (firstArg !== 'migrate') {
|
||||
const { autoMigrate } = await import('../config/migration-manager');
|
||||
await autoMigrate();
|
||||
try {
|
||||
const { autoMigrate } = await import('../config/migration-manager');
|
||||
await autoMigrate();
|
||||
} catch (err) {
|
||||
cliLogger.warn('migration.failed', 'Auto-migration failed (config may be corrupt)', {
|
||||
message: (err as Error).message,
|
||||
});
|
||||
// Do not print the error again — loadUnifiedConfig already printed the YAML details.
|
||||
}
|
||||
}
|
||||
|
||||
// Auto-recovery for missing configuration (BEFORE any early-exit commands)
|
||||
@@ -90,8 +99,10 @@ export async function runPreDispatchHandlers(ctx: PreDispatchContext): Promise<b
|
||||
cliLogger.warn('recovery.failed', 'Auto-recovery failed during CLI startup', {
|
||||
message: (err as Error).message,
|
||||
});
|
||||
// Recovery is best-effort - don't block basic CLI functionality
|
||||
console.warn('[!] Recovery failed:', (err as Error).message);
|
||||
// Recovery is best-effort - don't block basic CLI functionality. Use only the
|
||||
// first line: a YAML parse error embeds a multi-line snippet the loader already
|
||||
// printed, so re-emitting it here just duplicates the noise.
|
||||
console.warn('[!] Recovery failed:', (err as Error).message.split('\n')[0].trim());
|
||||
}
|
||||
|
||||
// Root command router (handles --help, --version, config, doctor, etc.)
|
||||
@@ -112,12 +123,18 @@ export async function runPreDispatchHandlers(ctx: PreDispatchContext): Promise<b
|
||||
}
|
||||
|
||||
// Special case: copilot command (GitHub Copilot integration)
|
||||
// Route known subcommands to command handler, keep all other args as profile passthrough.
|
||||
// Route known subcommands AND unknown tokens to the command handler so the
|
||||
// default case can print "[X] Unknown subcommand: <token>" and exit 1.
|
||||
// Unknown tokens that are NOT subcommand-like are kept as profile passthrough.
|
||||
if (firstArg === 'copilot' && args.length > 1) {
|
||||
const copilotToken = args[1];
|
||||
const shouldRouteToCopilotCommand = isCopilotSubcommandToken(copilotToken);
|
||||
const isKnownSubcommand = isCopilotSubcommandToken(copilotToken);
|
||||
// Route any non-flag, non-profile-looking second token to the handler so
|
||||
// the default branch fires with the correct error message.
|
||||
const looksLikeCopilotSubcmd =
|
||||
isKnownSubcommand || (typeof copilotToken === 'string' && !copilotToken.startsWith('-'));
|
||||
|
||||
if (shouldRouteToCopilotCommand) {
|
||||
if (looksLikeCopilotSubcmd) {
|
||||
const { handleCopilotCommand } = await import('../commands/copilot-command');
|
||||
const exitCode = await handleCopilotCommand(args.slice(1));
|
||||
process.exit(exitCode);
|
||||
@@ -125,14 +142,11 @@ export async function runPreDispatchHandlers(ctx: PreDispatchContext): Promise<b
|
||||
}
|
||||
|
||||
// Special case: explicit legacy Cursor bridge namespace.
|
||||
// Route ALL tokens (known and unknown) so the default case can report the error.
|
||||
if (firstArg === LEGACY_CURSOR_PROFILE_NAME && args.length > 1) {
|
||||
const { handleCursorCommand } = await import('../commands/cursor-command');
|
||||
const cursorToken = args[1];
|
||||
|
||||
if (isCursorSubcommandToken(cursorToken)) {
|
||||
const exitCode = await handleCursorCommand(args.slice(1));
|
||||
process.exit(exitCode);
|
||||
}
|
||||
const exitCode = await handleCursorCommand(args.slice(1));
|
||||
process.exit(exitCode);
|
||||
}
|
||||
|
||||
// Compatibility shim: old `ccs cursor <subcommand>` still forwards to the legacy bridge
|
||||
|
||||
Reference in new issue
Block a user