Files
gemini-cli/packages/a2a-server/src/commands/init.ts
2026-02-10 00:10:15 +00:00

170 lines
4.4 KiB
TypeScript

/**
* @license
* Copyright 2025 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import * as fs from 'node:fs';
import * as path from 'node:path';
import { CoderAgentEvent, type AgentSettings } from '../types.js';
import { performInit } from '@google/gemini-cli-core';
import type {
Command,
CommandContext,
CommandExecutionResponse,
} from './types.js';
import type { CoderAgentExecutor } from '../agent/executor.js';
import type {
ExecutionEventBus,
RequestContext,
AgentExecutionEvent,
} from '@a2a-js/sdk/server';
import { v4 as uuidv4 } from 'uuid';
import { logger } from '../utils/logger.js';
export class InitCommand implements Command {
name = 'init';
description = 'Analyzes the project and creates a tailored GEMINI.md file';
requiresWorkspace = true;
streaming = true;
private handleMessageResult(
result: { content: string; messageType: 'info' | 'error' },
context: CommandContext,
eventBus: ExecutionEventBus,
taskId: string,
contextId: string,
): CommandExecutionResponse {
const statusState = result.messageType === 'error' ? 'failed' : 'completed';
const eventType =
result.messageType === 'error'
? CoderAgentEvent.StateChangeEvent
: CoderAgentEvent.TextContentEvent;
const event: AgentExecutionEvent = {
kind: 'status-update',
taskId,
contextId,
status: {
state: statusState,
message: {
kind: 'message',
role: 'agent',
parts: [{ kind: 'text', text: result.content }],
messageId: uuidv4(),
taskId,
contextId,
},
timestamp: new Date().toISOString(),
},
final: true,
metadata: {
coderAgent: { kind: eventType },
model: context.config.getModel(),
},
};
logger.info('[EventBus event]: ', event);
eventBus.publish(event);
return {
name: this.name,
data: result,
};
}
private async handleSubmitPromptResult(
result: { content: unknown },
context: CommandContext,
geminiMdPath: string,
eventBus: ExecutionEventBus,
taskId: string,
contextId: string,
): Promise<CommandExecutionResponse> {
fs.writeFileSync(geminiMdPath, '', 'utf8');
if (!context.agentExecutor) {
throw new Error('Agent executor not found in context.');
}
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
const agentExecutor = context.agentExecutor as CoderAgentExecutor;
const agentSettings: AgentSettings = {
kind: CoderAgentEvent.StateAgentSettingsEvent,
workspacePath: process.env['CODER_AGENT_WORKSPACE_PATH']!,
autoExecute: true,
};
if (typeof result.content !== 'string') {
throw new Error('Init command content must be a string.');
}
const promptText = result.content;
const requestContext: RequestContext = {
userMessage: {
kind: 'message',
role: 'user',
parts: [{ kind: 'text', text: promptText }],
messageId: uuidv4(),
taskId,
contextId,
metadata: {
coderAgent: agentSettings,
},
},
taskId,
contextId,
};
// The executor will handle the entire agentic loop, including
// creating the task, streaming responses, and handling tools.
await agentExecutor.execute(requestContext, eventBus);
return {
name: this.name,
data: geminiMdPath,
};
}
async execute(
context: CommandContext,
_args: string[] = [],
): Promise<CommandExecutionResponse> {
if (!context.eventBus) {
return {
name: this.name,
data: 'Use executeStream to get streaming results.',
};
}
const geminiMdPath = path.join(
process.env['CODER_AGENT_WORKSPACE_PATH']!,
'GEMINI.md',
);
const result = performInit(fs.existsSync(geminiMdPath));
const taskId = uuidv4();
const contextId = uuidv4();
switch (result.type) {
case 'message':
return this.handleMessageResult(
result,
context,
context.eventBus,
taskId,
contextId,
);
case 'submit_prompt':
return this.handleSubmitPromptResult(
result,
context,
geminiMdPath,
context.eventBus,
taskId,
contextId,
);
default:
throw new Error('Unknown result type from performInit');
}
}
}