fix(cursor): export missing symbols, eliminate subcommand sync risk, improve tests

- Export detectProvider and formatModelName from barrel index
- Export CURSOR_SUBCOMMANDS from cursor-command.ts, import in ccs.ts
- Use getCcsDir() in tests instead of manual path construction
- Add handleCursorCommand routing test for unknown subcommand
This commit is contained in:
Tam Nhu Tran committed 2026-02-12 08:21:39 +07:00
1 parent cda037e7e5
commit 934238740e
4 files changed
+50 -25

No files matched your search

+9 -7
View File
@@ -536,13 +536,15 @@ async function main(): Promise<void> {
// Only route to command handler for known subcommands, otherwise treat as profile // 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) // Note: Bare `ccs cursor` shows help (unlike copilot which falls through to profile)
// This is intentional — cursor has no profile-switching mode // This is intentional — cursor has no profile-switching mode
// Keep in sync with cursor-command.ts switch cases if (firstArg === 'cursor') {
const CURSOR_SUBCOMMANDS = ['auth', 'status', 'models', 'start', 'stop', 'help', '--help', '-h']; const { handleCursorCommand, CURSOR_SUBCOMMANDS } = await import('./commands/cursor-command');
if (firstArg === 'cursor' && (args.length === 1 || CURSOR_SUBCOMMANDS.includes(args[1]))) { if (
// `ccs cursor <subcommand>` - route to cursor command handler args.length === 1 ||
const { handleCursorCommand } = await import('./commands/cursor-command'); CURSOR_SUBCOMMANDS.includes(args[1] as (typeof CURSOR_SUBCOMMANDS)[number])
const exitCode = await handleCursorCommand(args.slice(1)); ) {
process.exit(exitCode); const exitCode = await handleCursorCommand(args.slice(1));
process.exit(exitCode);
}
} }
// Special case: copilot command (GitHub Copilot integration) // Special case: copilot command (GitHub Copilot integration)
+12
View File
@@ -23,6 +23,18 @@ const DEFAULT_CURSOR_CONFIG = {
model: DEFAULT_CURSOR_MODEL, model: DEFAULT_CURSOR_MODEL,
}; };
/** Valid cursor subcommands — imported by ccs.ts for routing */
export const CURSOR_SUBCOMMANDS = [
'auth',
'status',
'models',
'start',
'stop',
'help',
'--help',
'-h',
] as const;
/** /**
* Handle cursor subcommand. * Handle cursor subcommand.
*/ */
+2
View File
@@ -29,6 +29,8 @@ export {
fetchModelsFromDaemon, fetchModelsFromDaemon,
getAvailableModels, getAvailableModels,
getDefaultModel, getDefaultModel,
detectProvider,
formatModelName,
} from './cursor-models'; } from './cursor-models';
// Executor // Executor
+27 -18
View File
@@ -15,6 +15,8 @@ import {
stopDaemon, stopDaemon,
startDaemon, startDaemon,
} from '../../../src/cursor/cursor-daemon'; } from '../../../src/cursor/cursor-daemon';
import { getCcsDir } from '../../../src/utils/config-manager';
import { handleCursorCommand } from '../../../src/commands/cursor-command';
// Test isolation // Test isolation
let originalCcsHome: string | undefined; let originalCcsHome: string | undefined;
@@ -41,8 +43,8 @@ afterEach(() => {
} }
}); });
// CCS_HOME is set to tempDir; getCcsDir() appends '.ccs' to it // Use getCcsDir() for consistent path resolution with production code
const ccsDir = () => path.join(tempDir, '.ccs'); const getTestCursorDir = () => path.join(getCcsDir(), 'cursor');
describe('getPidFromFile', () => { describe('getPidFromFile', () => {
it('returns null when no PID file exists', () => { it('returns null when no PID file exists', () => {
@@ -50,25 +52,25 @@ describe('getPidFromFile', () => {
}); });
it('returns PID when valid PID file exists', () => { it('returns PID when valid PID file exists', () => {
const cursorDir = path.join(ccsDir(), 'cursor'); const dir = getTestCursorDir();
fs.mkdirSync(cursorDir, { recursive: true }); fs.mkdirSync(dir, { recursive: true });
fs.writeFileSync(path.join(cursorDir, 'daemon.pid'), '12345'); fs.writeFileSync(path.join(dir, 'daemon.pid'), '12345');
expect(getPidFromFile()).toBe(12345); expect(getPidFromFile()).toBe(12345);
}); });
it('returns null when PID file contains invalid content', () => { it('returns null when PID file contains invalid content', () => {
const cursorDir = path.join(ccsDir(), 'cursor'); const dir = getTestCursorDir();
fs.mkdirSync(cursorDir, { recursive: true }); fs.mkdirSync(dir, { recursive: true });
fs.writeFileSync(path.join(cursorDir, 'daemon.pid'), 'not-a-number'); fs.writeFileSync(path.join(dir, 'daemon.pid'), 'not-a-number');
expect(getPidFromFile()).toBeNull(); expect(getPidFromFile()).toBeNull();
}); });
it('trims whitespace from PID file content', () => { it('trims whitespace from PID file content', () => {
const cursorDir = path.join(ccsDir(), 'cursor'); const dir = getTestCursorDir();
fs.mkdirSync(cursorDir, { recursive: true }); fs.mkdirSync(dir, { recursive: true });
fs.writeFileSync(path.join(cursorDir, 'daemon.pid'), ' 42 \n'); fs.writeFileSync(path.join(dir, 'daemon.pid'), ' 42 \n');
expect(getPidFromFile()).toBe(42); expect(getPidFromFile()).toBe(42);
}); });
@@ -78,25 +80,25 @@ describe('writePidToFile', () => {
it('creates PID file with correct content', () => { it('creates PID file with correct content', () => {
writePidToFile(12345); writePidToFile(12345);
const pidFile = path.join(ccsDir(), 'cursor', 'daemon.pid'); const pidFile = path.join(getTestCursorDir(), 'daemon.pid');
expect(fs.existsSync(pidFile)).toBe(true); expect(fs.existsSync(pidFile)).toBe(true);
expect(fs.readFileSync(pidFile, 'utf8')).toBe('12345'); expect(fs.readFileSync(pidFile, 'utf8')).toBe('12345');
}); });
it('creates cursor directory if it does not exist', () => { it('creates cursor directory if it does not exist', () => {
const cursorDir = path.join(ccsDir(), 'cursor'); const dir = getTestCursorDir();
expect(fs.existsSync(cursorDir)).toBe(false); expect(fs.existsSync(dir)).toBe(false);
writePidToFile(999); writePidToFile(999);
expect(fs.existsSync(cursorDir)).toBe(true); expect(fs.existsSync(dir)).toBe(true);
}); });
it('overwrites existing PID file', () => { it('overwrites existing PID file', () => {
writePidToFile(111); writePidToFile(111);
writePidToFile(222); writePidToFile(222);
const pidFile = path.join(ccsDir(), 'cursor', 'daemon.pid'); const pidFile = path.join(getTestCursorDir(), 'daemon.pid');
expect(fs.readFileSync(pidFile, 'utf8')).toBe('222'); expect(fs.readFileSync(pidFile, 'utf8')).toBe('222');
}); });
}); });
@@ -104,7 +106,7 @@ describe('writePidToFile', () => {
describe('removePidFile', () => { describe('removePidFile', () => {
it('removes existing PID file', () => { it('removes existing PID file', () => {
writePidToFile(12345); writePidToFile(12345);
const pidFile = path.join(ccsDir(), 'cursor', 'daemon.pid'); const pidFile = path.join(getTestCursorDir(), 'daemon.pid');
expect(fs.existsSync(pidFile)).toBe(true); expect(fs.existsSync(pidFile)).toBe(true);
removePidFile(); removePidFile();
@@ -177,7 +179,14 @@ describe('stopDaemon', () => {
expect(result.error).toBeUndefined(); expect(result.error).toBeUndefined();
// PID file should be removed // PID file should be removed
const pidFile = path.join(ccsDir(), 'cursor', 'daemon.pid'); const pidFile = path.join(getTestCursorDir(), 'daemon.pid');
expect(fs.existsSync(pidFile)).toBe(false); expect(fs.existsSync(pidFile)).toBe(false);
}); });
}); });
describe('handleCursorCommand', () => {
it('returns exit code 1 for unknown subcommand', async () => {
const exitCode = await handleCursorCommand(['nonexistent']);
expect(exitCode).toBe(1);
});
});