feat: add claude and project CLI commands
Claude command manages .mcp.json files (generate from project, show, add, remove entries). Project command provides CRUD for projects with profile assignment management. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
111
src/cli/tests/commands/project.test.ts
Normal file
111
src/cli/tests/commands/project.test.ts
Normal file
@@ -0,0 +1,111 @@
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||
import { createProjectCommand } from '../../src/commands/project.js';
|
||||
import type { ApiClient } from '../../src/api-client.js';
|
||||
|
||||
function mockClient(): ApiClient {
|
||||
return {
|
||||
get: vi.fn(async () => []),
|
||||
post: vi.fn(async () => ({ id: 'proj-1', name: 'my-project' })),
|
||||
put: vi.fn(async () => ({})),
|
||||
delete: vi.fn(async () => {}),
|
||||
} as unknown as ApiClient;
|
||||
}
|
||||
|
||||
describe('project command', () => {
|
||||
let client: ReturnType<typeof mockClient>;
|
||||
let output: string[];
|
||||
const log = (...args: unknown[]) => output.push(args.map(String).join(' '));
|
||||
|
||||
beforeEach(() => {
|
||||
client = mockClient();
|
||||
output = [];
|
||||
});
|
||||
|
||||
describe('list', () => {
|
||||
it('shows no projects message when empty', async () => {
|
||||
const cmd = createProjectCommand({ client, log });
|
||||
await cmd.parseAsync(['list'], { from: 'user' });
|
||||
expect(output.join('\n')).toContain('No projects found');
|
||||
});
|
||||
|
||||
it('shows project table', async () => {
|
||||
vi.mocked(client.get).mockResolvedValue([
|
||||
{ id: 'proj-1', name: 'dev', description: 'Dev project', ownerId: 'user-1', createdAt: '2025-01-01' },
|
||||
]);
|
||||
const cmd = createProjectCommand({ client, log });
|
||||
await cmd.parseAsync(['list'], { from: 'user' });
|
||||
expect(output.join('\n')).toContain('proj-1');
|
||||
expect(output.join('\n')).toContain('dev');
|
||||
});
|
||||
|
||||
it('outputs json', async () => {
|
||||
vi.mocked(client.get).mockResolvedValue([{ id: 'proj-1', name: 'dev' }]);
|
||||
const cmd = createProjectCommand({ client, log });
|
||||
await cmd.parseAsync(['list', '-o', 'json'], { from: 'user' });
|
||||
expect(output[0]).toContain('"id"');
|
||||
});
|
||||
});
|
||||
|
||||
describe('create', () => {
|
||||
it('creates a project', async () => {
|
||||
const cmd = createProjectCommand({ client, log });
|
||||
await cmd.parseAsync(['create', 'my-project', '-d', 'A test project'], { from: 'user' });
|
||||
expect(client.post).toHaveBeenCalledWith('/api/v1/projects', {
|
||||
name: 'my-project',
|
||||
description: 'A test project',
|
||||
});
|
||||
expect(output.join('\n')).toContain("Project 'my-project' created");
|
||||
});
|
||||
});
|
||||
|
||||
describe('delete', () => {
|
||||
it('deletes a project', async () => {
|
||||
const cmd = createProjectCommand({ client, log });
|
||||
await cmd.parseAsync(['delete', 'proj-1'], { from: 'user' });
|
||||
expect(client.delete).toHaveBeenCalledWith('/api/v1/projects/proj-1');
|
||||
expect(output.join('\n')).toContain('deleted');
|
||||
});
|
||||
});
|
||||
|
||||
describe('show', () => {
|
||||
it('shows project details', async () => {
|
||||
vi.mocked(client.get).mockImplementation(async (url: string) => {
|
||||
if (url.endsWith('/profiles')) return [];
|
||||
return { id: 'proj-1', name: 'dev', description: 'Dev project', ownerId: 'user-1', createdAt: '2025-01-01' };
|
||||
});
|
||||
const cmd = createProjectCommand({ client, log });
|
||||
await cmd.parseAsync(['show', 'proj-1'], { from: 'user' });
|
||||
expect(output.join('\n')).toContain('Name: dev');
|
||||
expect(output.join('\n')).toContain('ID: proj-1');
|
||||
});
|
||||
});
|
||||
|
||||
describe('profiles', () => {
|
||||
it('lists profiles for a project', async () => {
|
||||
vi.mocked(client.get).mockResolvedValue([
|
||||
{ id: 'prof-1', name: 'default', serverId: 'srv-1' },
|
||||
]);
|
||||
const cmd = createProjectCommand({ client, log });
|
||||
await cmd.parseAsync(['profiles', 'proj-1'], { from: 'user' });
|
||||
expect(client.get).toHaveBeenCalledWith('/api/v1/projects/proj-1/profiles');
|
||||
expect(output.join('\n')).toContain('default');
|
||||
});
|
||||
|
||||
it('shows empty message when no profiles', async () => {
|
||||
const cmd = createProjectCommand({ client, log });
|
||||
await cmd.parseAsync(['profiles', 'proj-1'], { from: 'user' });
|
||||
expect(output.join('\n')).toContain('No profiles assigned');
|
||||
});
|
||||
});
|
||||
|
||||
describe('set-profiles', () => {
|
||||
it('sets profiles for a project', async () => {
|
||||
const cmd = createProjectCommand({ client, log });
|
||||
await cmd.parseAsync(['set-profiles', 'proj-1', 'prof-1', 'prof-2'], { from: 'user' });
|
||||
expect(client.put).toHaveBeenCalledWith('/api/v1/projects/proj-1/profiles', {
|
||||
profileIds: ['prof-1', 'prof-2'],
|
||||
});
|
||||
expect(output.join('\n')).toContain('2 profile(s)');
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user