Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 68 additions & 0 deletions packages/cli/src/commands/adapter-cmd.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
import { makeCategoryCmd } from './adapter-cmd.js';
import type { AdapterCategory } from '../adapter-registry.js';

const testCategory: AdapterCategory = {
id: 'bots',
pkgPrefix: '@profullstack/sh1pt-bot',
description: 'Chat bots — Discord, Telegram, Slack',
adapters: ['discord', 'telegram', 'slack'],
};

describe('adapter category list --json', () => {
let stdout: string[];
let cmd: ReturnType<typeof makeCategoryCmd>;

beforeEach(() => {
stdout = [];
vi.spyOn(console, 'log').mockImplementation((...args: unknown[]) => {
stdout.push(args.map(String).join(' '));
});
cmd = makeCategoryCmd(testCategory);
});

afterEach(() => {
vi.restoreAllMocks();
});

it('outputs human-readable list by default', () => {
const listCmd = cmd.commands.find((c) => c.name() === 'list')!;
listCmd.parse([], { from: 'user' });
const output = stdout.join('\n');
expect(output).toContain('discord');
expect(output).toContain('telegram');
expect(output).toContain('slack');
});

it('outputs valid JSON when --json is passed', () => {
const listCmd = cmd.commands.find((c) => c.name() === 'list')!;
listCmd.parse(['--json'], { from: 'user' });
const output = stdout.join('\n');
const parsed = JSON.parse(output);
expect(Array.isArray(parsed)).toBe(true);
expect(parsed).toHaveLength(3);
expect(parsed[0]).toHaveProperty('name', 'discord');
expect(parsed[0]).toHaveProperty('package');
expect(parsed[0]).toHaveProperty('setupCommand');
});

it('JSON output includes correct package names', () => {
const listCmd = cmd.commands.find((c) => c.name() === 'list')!;
listCmd.parse(['--json'], { from: 'user' });
const output = stdout.join('\n');
const parsed = JSON.parse(output);
expect(parsed[0].package).toBe('@profullstack/sh1pt-bot-discord');
expect(parsed[1].package).toBe('@profullstack/sh1pt-bot-telegram');
expect(parsed[2].package).toBe('@profullstack/sh1pt-bot-slack');
});

it('JSON output includes correct setup commands', () => {
const listCmd = cmd.commands.find((c) => c.name() === 'list')!;
listCmd.parse(['--json'], { from: 'user' });
const output = stdout.join('\n');
const parsed = JSON.parse(output);
expect(parsed[0].setupCommand).toBe('sh1pt bots discord setup');
expect(parsed[1].setupCommand).toBe('sh1pt bots telegram setup');
expect(parsed[2].setupCommand).toBe('sh1pt bots slack setup');
});
});
12 changes: 11 additions & 1 deletion packages/cli/src/commands/adapter-cmd.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,17 @@ export function makeCategoryCmd(category: AdapterCategory): Command {
cmd
.command('list')
.description(`List all ${category.id} adapters`)
.action(() => {
.option('--json', 'output as JSON')
.action((opts: { json?: boolean }) => {
if (opts.json) {
const output = category.adapters.map((name) => ({
name,
package: packageFor(category, name),
setupCommand: `sh1pt ${category.id} ${name} setup`,
}));
console.log(JSON.stringify(output, null, 2));
return;
}
for (const name of category.adapters) {
console.log(` ${kleur.cyan(name)} ${kleur.dim(packageFor(category, name))}`);
}
Expand Down
49 changes: 49 additions & 0 deletions packages/cli/src/commands/agents.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
import { agentsCmd } from './agents.js';

describe('agents list --json', () => {
let stdout: string[];

beforeEach(() => {
stdout = [];
vi.spyOn(console, 'log').mockImplementation((...args: unknown[]) => {
stdout.push(args.map(String).join(' '));
});
});

afterEach(() => {
vi.restoreAllMocks();
});

it('outputs human-readable list by default', () => {
agentsCmd.commands.find((c) => c.name() === 'list')!.parse([], { from: 'user' });
const output = stdout.join('\n');
expect(output).toContain('claude');
expect(output).toContain('codex');
expect(output).toContain('qwen');
});

it('outputs valid JSON when --json is passed', () => {
agentsCmd.commands.find((c) => c.name() === 'list')!.parse(['--json'], { from: 'user' });
const output = stdout.join('\n');
const parsed = JSON.parse(output);
expect(Array.isArray(parsed)).toBe(true);
expect(parsed).toHaveLength(3);
expect(parsed[0]).toHaveProperty('id', 'claude');
expect(parsed[0]).toHaveProperty('package');
expect(parsed[0]).toHaveProperty('setupCommand');
expect(parsed[1]).toHaveProperty('id', 'codex');
expect(parsed[2]).toHaveProperty('id', 'qwen');
});

it('JSON output includes package and setupCommand fields', () => {
agentsCmd.commands.find((c) => c.name() === 'list')!.parse(['--json'], { from: 'user' });
const output = stdout.join('\n');
const parsed = JSON.parse(output);
for (const agent of parsed) {
expect(typeof agent.id).toBe('string');
expect(typeof agent.package).toBe('string');
expect(typeof agent.setupCommand).toBe('string');
}
});
});
14 changes: 12 additions & 2 deletions packages/cli/src/commands/agents.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,21 @@ export const agentsCmd = new Command('agents')
agentsCmd
.command('list')
.description('Which agent CLIs are installed on this machine')
.action(() => {
.option('--json', 'output as JSON')
.action((opts: { json?: boolean }) => {
const agents = ['claude', 'codex', 'qwen'];
if (opts.json) {
const output = agents.map((id) => ({
id,
package: `@profullstack/sh1pt-agent-${id}`,
setupCommand: `sh1pt agents setup --agent ${id}`,
}));
console.log(JSON.stringify(output, null, 2));
return;
}
for (const a of agents) {
// TODO: resolve adapter, call check(), render real status
console.log(` ${kleur.gray('○')} ${kleur.bold(a)} ${kleur.dim('run `sh1pt agents setup --agent ' + a + '`')}`);
console.log(` ${kleur.gray('○')} ${kleur.bold(a)} ${kleur.dim('run \`sh1pt agents setup --agent ' + a + '\`')}`);
}
});

Expand Down
51 changes: 51 additions & 0 deletions packages/cli/src/commands/skills.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
import { skillsCmd } from './skills.js';

describe('skills marketplaces --json', () => {
let stdout: string[];

beforeEach(() => {
stdout = [];
vi.spyOn(console, 'log').mockImplementation((...args: unknown[]) => {
stdout.push(args.map(String).join(' '));
});
});

afterEach(() => {
vi.restoreAllMocks();
});

it('outputs human-readable list by default', () => {
const mktCmd = skillsCmd.commands.find((c) => c.name() === 'marketplaces')!;
mktCmd.parse([], { from: 'user' });
const output = stdout.join('\n');
expect(output).toContain('ugig');
expect(output).toContain('clawhub');
});

it('outputs valid JSON when --json is passed', () => {
const mktCmd = skillsCmd.commands.find((c) => c.name() === 'marketplaces')!;
mktCmd.parse(['--json'], { from: 'user' });
const output = stdout.join('\n');
const parsed = JSON.parse(output);
expect(Array.isArray(parsed)).toBe(true);
expect(parsed.length).toBeGreaterThan(0);
expect(parsed[0]).toHaveProperty('id');
expect(parsed[0]).toHaveProperty('name');
expect(parsed[0]).toHaveProperty('method');
expect(parsed[0]).toHaveProperty('readiness');
});

it('JSON output has correct structure for each marketplace', () => {
const mktCmd = skillsCmd.commands.find((c) => c.name() === 'marketplaces')!;
mktCmd.parse(['--json'], { from: 'user' });
const output = stdout.join('\n');
const parsed = JSON.parse(output);
for (const mp of parsed) {
expect(typeof mp.id).toBe('string');
expect(typeof mp.name).toBe('string');
expect(typeof mp.method).toBe('string');
expect(typeof mp.readiness).toBe('string');
}
});
});
13 changes: 12 additions & 1 deletion packages/cli/src/commands/skills.ts
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,17 @@ skillsCmd
skillsCmd
.command('marketplaces')
.description('List known skill marketplaces')
.action(() => {
.option('--json', 'output as JSON')
.action((opts: { json?: boolean }) => {
if (opts.json) {
const output = MARKETPLACES.map((mp) => ({
id: mp.id,
name: mp.name,
method: mp.method,
readiness: mp.readiness,
}));
console.log(JSON.stringify(output, null, 2));
return;
}
for (const mp of MARKETPLACES) console.log(`${mp.id}\t${mp.name}\t${mp.method}\t${mp.readiness}`);
});
Loading