diff --git a/src/ccs.ts b/src/ccs.ts index 2880dab9..c12e35b3 100644 --- a/src/ccs.ts +++ b/src/ccs.ts @@ -533,18 +533,11 @@ async function main(): Promise { } // Special case: cursor command (Cursor IDE integration) - // Only route to command handler for known subcommands, otherwise treat as profile - // Note: Bare `ccs cursor` shows help (unlike copilot which falls through to profile) - // This is intentional — cursor has no profile-switching mode + // All `ccs cursor *` routes to cursor command handler — cursor has no profile-switching mode if (firstArg === 'cursor') { - const { handleCursorCommand, CURSOR_SUBCOMMANDS } = await import('./commands/cursor-command'); - if ( - args.length === 1 || - CURSOR_SUBCOMMANDS.includes(args[1] as (typeof CURSOR_SUBCOMMANDS)[number]) - ) { - const exitCode = await handleCursorCommand(args.slice(1)); - process.exit(exitCode); - } + const { handleCursorCommand } = await import('./commands/cursor-command'); + const exitCode = await handleCursorCommand(args.slice(1)); + process.exit(exitCode); } // Special case: copilot command (GitHub Copilot integration) diff --git a/src/commands/cursor-command.ts b/src/commands/cursor-command.ts index c5547380..ad6ce59a 100644 --- a/src/commands/cursor-command.ts +++ b/src/commands/cursor-command.ts @@ -160,7 +160,7 @@ async function handleStatus(): Promise { console.log(`Authentication: ${authIcon} ${authText}`); if (authStatus.authenticated && authStatus.tokenAge !== undefined) { - console.log(` Token age: ${authStatus.tokenAge.toFixed(1)} hours`); + console.log(` Token age: ${authStatus.tokenAge} hours`); } // Daemon status diff --git a/src/cursor/cursor-daemon.ts b/src/cursor/cursor-daemon.ts index 19ad0034..99fef7b2 100644 --- a/src/cursor/cursor-daemon.ts +++ b/src/cursor/cursor-daemon.ts @@ -179,7 +179,8 @@ export async function startDaemon( `, ]; - proc = spawn(process.execPath, args, { + // Append --ccs-daemon marker for PID validation in stopDaemon + proc = spawn(process.execPath, [...args, '--ccs-daemon'], { stdio: 'ignore', detached: true, }); @@ -267,7 +268,7 @@ export async function stopDaemon(): Promise<{ success: boolean; error?: string } // Verify the PID belongs to our daemon before signaling try { const cmdline = fs.readFileSync(`/proc/${pid}/cmdline`, 'utf8'); - if (!cmdline.includes('cursor') && !cmdline.includes('/health')) { + if (!cmdline.includes('--ccs-daemon')) { // PID was reused by an unrelated process removePidFile(); return { success: true }; diff --git a/tests/unit/cursor/cursor-daemon.test.ts b/tests/unit/cursor/cursor-daemon.test.ts index 4865dbed..e6480b9d 100644 --- a/tests/unit/cursor/cursor-daemon.test.ts +++ b/tests/unit/cursor/cursor-daemon.test.ts @@ -141,7 +141,7 @@ describe('startDaemon', () => { it( 'starts and stops daemon successfully', async () => { - const port = 18765; + const port = 10000 + Math.floor(Math.random() * 50000); const result = await startDaemon({ port, model: 'test' }); expect(result.success).toBe(true); expect(result.pid).toBeDefined();