feat(cli): add init command with agent instruction generation

This commit is contained in:
chahinebrini 2026-06-24 23:39:19 +02:00
parent 830e7af0c7
commit cd6add215d
3 changed files with 89 additions and 0 deletions

41
src/cli/commands/init.ts Normal file
View File

@ -0,0 +1,41 @@
import { mkdirSync, writeFileSync } from 'fs';
import { join } from 'path';
import { getAgentHubDir } from '../../core/paths.js';
import { saveConfig, defaultConfig } from '../../core/config.js';
import { agentsMd, claudeMd, codexMd, kimiMd } from '../../core/templates.js';
import { askProjectName, askAgents, askDelegationMode } from '../prompts.js';
export async function init(cwd: string, options: { projectName?: string; yes?: boolean }): Promise<void> {
const projectName = options.projectName ?? (options.yes ? getDefaultProjectName(cwd) : await askProjectName(getDefaultProjectName(cwd)));
const { claude, codex, kimi } = options.yes
? { claude: true, codex: true, kimi: true }
: await askAgents();
const mode = options.yes ? 'suggest' : await askDelegationMode();
const config = defaultConfig(projectName);
config.delegationMode = mode;
const dir = getAgentHubDir(cwd);
mkdirSync(join(dir, 'tasks'), { recursive: true });
mkdirSync(join(dir, 'handoffs'), { recursive: true });
mkdirSync(join(dir, 'decisions'), { recursive: true });
mkdirSync(join(dir, 'memory'), { recursive: true });
mkdirSync(join(dir, 'status'), { recursive: true });
saveConfig(cwd, config);
writeFileSync(join(cwd, 'AGENTS.md'), agentsMd(projectName), 'utf-8');
if (claude) writeFileSync(join(cwd, 'CLAUDE.md'), claudeMd(), 'utf-8');
if (codex) writeFileSync(join(cwd, 'CODEX.md'), codexMd(), 'utf-8');
if (kimi) writeFileSync(join(cwd, 'KIMI.md'), kimiMd(), 'utf-8');
console.log(`AgentHub initialized for "${projectName}".`);
console.log('Run `agenthub status` to see the current project state.');
}
function getDefaultProjectName(cwd: string): string {
const parts = cwd.split(/[/\\]/);
return parts[parts.length - 1] || 'project';
}

24
src/cli/prompts.ts Normal file
View File

@ -0,0 +1,24 @@
import { input, confirm, select } from '@inquirer/prompts';
export async function askProjectName(defaultName: string): Promise<string> {
return input({ message: 'Project name:', default: defaultName });
}
export async function askAgents(): Promise<{ claude: boolean; codex: boolean; kimi: boolean }> {
const claude = await confirm({ message: 'Support Claude Code?', default: true });
const codex = await confirm({ message: 'Support Codex CLI?', default: true });
const kimi = await confirm({ message: 'Support Kimi Code CLI?', default: true });
return { claude, codex, kimi };
}
export async function askDelegationMode(): Promise<'manual' | 'suggest' | 'auto'> {
return select({
message: 'Default delegation mode:',
choices: [
{ name: 'suggest', value: 'suggest' },
{ name: 'manual', value: 'manual' },
{ name: 'auto', value: 'auto' },
],
default: 'suggest',
});
}

24
tests/cli-init.test.ts Normal file
View File

@ -0,0 +1,24 @@
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
import { mkdtempSync, rmSync, existsSync } from 'fs';
import { tmpdir } from 'os';
import { join } from 'path';
import { init } from '../src/cli/commands/init.js';
describe('cli init', () => {
let cwd: string;
beforeEach(() => {
cwd = mkdtempSync(join(tmpdir(), 'agenthub-'));
});
afterEach(() => {
rmSync(cwd, { recursive: true, force: true });
});
it('initializes project with --yes', async () => {
await init(cwd, { yes: true, projectName: 'test-proj' });
expect(existsSync(join(cwd, '.agenthub', 'agenthub.config.json'))).toBe(true);
expect(existsSync(join(cwd, 'AGENTS.md'))).toBe(true);
expect(existsSync(join(cwd, 'CLAUDE.md'))).toBe(true);
});
});