@dami_deleon/rikudo 1.1.0 → 2.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,107 +1,182 @@
1
1
  # 🌀 Rikudo: El Sabio de los Commits
2
2
 
3
- **Rikudo** es una herramienta de terminal (CLI) potenciada por Inteligencia Artificial diseñada para automatizar y estandarizar el flujo de trabajo de Git y la gestión de tareas en Redmine.
3
+ **Rikudo** es una herramienta CLI potenciada por Inteligencia Artificial, agnóstica del proveedor de IA, diseñada para automatizar y estandarizar el flujo de trabajo de Git y la gestión de tareas en Redmine.
4
4
 
5
- Inspirado en el Sabio de los Seis Caminos, Rikudo utiliza seis módulos especializados para observar tu código, entender el contexto de tus tareas y mantener la armonía en el repositorio siguiendo estrictamente las convenciones del equipo.
5
+ Inspirado en el Sabio de los Seis Caminos, Rikudo utiliza módulos especializados para observar tu código, entender el contexto de tus tareas y mantener la armonía en el repositorio siguiendo las convenciones del equipo.
6
6
 
7
7
  ---
8
8
 
9
9
  ## 🚀 Los 6 Caminos de Rikudo
10
10
 
11
11
  1. **Camino Deva (Git):** Control total sobre el stage y los commits.
12
- 2. **Camino Humano (Gemini AI):** Inteligencia profunda para redactar mensajes técnicos precisos.
12
+ 2. **Camino Humano (AI):** Inteligencia profunda para redactar mensajes técnicos precisos.
13
13
  3. **Camino Animal (Redmine):** Invocación y sincronización con el Project Manager.
14
14
  4. **Camino Preta (Summarizer):** Absorción y síntesis de archivos de gran tamaño.
15
15
  5. **Camino Naraka (Cache):** Memoria persistente basada en hashes de Git para optimizar tokens.
16
16
  6. **Camino Asura (Rate Limiter):** Gestión de poder para respetar los límites de las APIs.
17
17
 
18
+ ---
18
19
 
19
- ## ✨ Características Principales
20
+ ## ✨ Dos Modos de Uso
21
+
22
+ Rikudo puede usarse de dos maneras:
23
+
24
+ ### Modo CLI
25
+ Interactúa directamente desde la terminal con un menú interactivo o comandos directos.
26
+
27
+ ### Modo MCP (Model Context Protocol)
28
+ Actúa como servidor MCP, exponiendo herramientas y prompts para que agentes de IA externos (Claude Code, Gemini CLI, Cursor, etc.) puedan invocar sus funcionalidades.
29
+
30
+ ---
20
31
 
21
- * **🧠 Multi-Provider AI:** Tú eliges el cerebro. Soporte nativo para:
22
- * **Google Gemini** (Rápido y eficiente)
23
- * **OpenAI GPT-4o / GPT-3.5** (El estándar)
24
- * **Anthropic Claude** (Gran capacidad de contexto)
25
- * **Ollama** (Privacidad total con modelos locales como Llama 3)
32
+ ## Características Principales
26
33
 
34
+ * **🧠 Agnóstico de IA:** Tú eliges el cerebro. Soporte nativo para:
35
+ * **Google Gemini** (Rápido y eficiente)
36
+ * **OpenAI GPT-4o / GPT-3.5** (El estándar)
37
+ * **Anthropic Claude** (Gran capacidad de contexto)
38
+ * **Ollama** (Privacidad total con modelos locales como Llama 3)
27
39
 
28
40
  * **📝 Smart Commits:** Analiza tus `staged files` y genera mensajes siguiendo **Conventional Commits** basados en tus propias reglas.
29
41
  * **🚀 PR Generator:** Selecciona múltiples commits y genera una descripción de Pull Request completa siguiendo tu plantilla.
30
42
  * **⚙️ Configuración Flexible:** Define tus preferencias globales o por proyecto.
31
- * **🔍 Diagnóstico Integrado:** Herramienta `test` para verificar tus API Keys y conexión con servicios externos (Redmine, IA, etc.).
43
+ * **🔍 Diagnóstico Integrado:** Herramienta `test` para verificar tus API Keys y conexión con servicios externos.
44
+ * **🔌 Servidor MCP:** Actúa como skill library para agentes de IA externos mediante el protocolo MCP.
32
45
 
33
46
  ## 📦 Instalación
34
47
 
35
- Rikudo está disponible en npm (actualmente en Alpha).
36
-
37
- ### Uso global (Recomendado)
38
-
39
48
  ```bash
40
- npm install -g @dami_deleon/rikudo@alpha
41
-
49
+ npm install -g @dami_deleon/rikudo
42
50
  ```
43
51
 
44
52
  ### Ejecución única (sin instalar)
45
53
 
46
54
  ```bash
47
- npx @dami_deleon/rikudo@alpha
48
-
55
+ npx @dami_deleon/rikudo
49
56
  ```
50
57
 
58
+ ---
59
+
51
60
  ## 🚀 Uso Rápido
52
61
 
53
- Simplemente ejecuta el comando en tu terminal dentro de cualquier repositorio git:
62
+ ### Modo CLI
63
+
64
+ Ejecuta el comando en tu terminal dentro de cualquier repositorio git:
54
65
 
55
66
  ```bash
56
67
  rikudo
57
-
58
68
  ```
59
69
 
60
70
  Esto abrirá el **Menú Interactivo** donde podrás elegir entre crear commits, PRs o configurar la herramienta.
61
71
 
62
- ### Comandos Directos
63
-
64
- Si eres un power-user, puedes usar atajos:
72
+ #### Comandos Directos
65
73
 
66
74
  * **Crear Commit:**
67
75
  ```bash
68
76
  rikudo commit
69
77
  # O con ticket asociado:
70
78
  rikudo commit -t 1234
71
-
72
79
  ```
73
80
 
74
-
75
81
  * **Crear Pull Request:**
76
82
  ```bash
77
83
  rikudo pr
78
84
  # O con ticket asociado:
79
85
  rikudo pr -t 1234
80
-
81
86
  ```
82
87
 
83
-
84
88
  * **Diagnóstico de Conexión:**
85
89
  ```bash
86
90
  rikudo test
87
-
88
91
  ```
89
92
 
90
-
91
93
  * **Configuración:**
92
94
  ```bash
93
95
  rikudo config
96
+ ```
97
+
98
+ ---
99
+
100
+ ### Modo MCP
101
+
102
+ Para usar Rikudo como servidor MCP con cualquier agente de IA compatible:
103
+
104
+ ```bash
105
+ rikudo mcp
106
+ ```
107
+
108
+ El servidor se comunica mediante **stdio**, permitiendo integrarse con cualquier cliente MCP.
109
+
110
+ #### Herramientas Disponibles
111
+
112
+ | Herramienta | Descripción |
113
+ |------------|-------------|
114
+ | `validate_configuration` | Verifica que las variables de entorno necesarias (Redmine, Gitea) estén configuradas correctamente. |
115
+ | `get_ticket_context` | Obtiene el contexto completo de un ticket de Redmine (título, descripción, estado, prioridad). |
116
+ | `get_git_diff` | Retorna el diff de los archivos que están en staging (`git add`). |
117
+ | `shift_left_start` | Flujo shift-left completo: checkout a rama objetivo, crea nueva rama, commit vacío y abre Draft PR en Gitea. Soporta parámetro `isWip` para usar prefijo "WIP:" en el título. |
118
+ | `update_pm_redmine` | Agrega un comentario a un ticket de Redmine para notificar al Project Manager. |
119
+
120
+ #### Prompts Disponibles
121
+
122
+ | Prompt | Descripción |
123
+ |--------|-------------|
124
+ | `start_feature` | Inicia el flujo de trabajo para una nueva feature. Obtiene contexto del ticket, genera nombre de rama y prepara el entorno en Gitea con PR en modo WIP. |
125
+ | `review_and_comment` | Analiza los cambios staged, genera un comentario técnico y lo publica en Redmine. |
126
+
127
+ #### Integración con Agentes IA
128
+
129
+ Rikudo es compatible con cualquier herramienta de IA que soporte el protocolo MCP, como:
130
+ - **Claude Code / Claude Desktop**
131
+ - **Gemini CLI**
132
+ - **Cursor**
133
+ - **Windsurf**
134
+ - **Continue**
135
+ - Y cualquier otro cliente MCP
136
+
137
+ Agrega esto en la configuración de tu cliente MCP:
138
+
139
+ ```json
140
+ {
141
+ "mcpServers": {
142
+ "rikudo": {
143
+ "command": "rikudo",
144
+ "args": ["mcp"]
145
+ }
146
+ }
147
+ }
148
+ ```
149
+
150
+ #### Variables de Entorno para MCP
151
+
152
+ ```env
153
+ # Redmine
154
+ RIKUDO_REDMINE_URL=https://redmine.tuempresa.com
155
+ RIKUDO_REDMINE_API_KEY=tu_redmine_key
156
+
157
+ # Gitea
158
+ RIKUDO_GITEA_URL=https://gitea.tuempresa.com
159
+ RIKUDO_GITEA_TOKEN=tu_gitea_token
160
+ RIKUDO_GITEA_OWNER=tu_usuario_o_organizacion
161
+ RIKUDO_GITEA_REPO=nombre_del_repositorio
162
+ ```
163
+
164
+ #### Formato de Ramas
165
+
166
+ Al usar `start_feature` o `shift_left_start`, el nombre de rama sigue el formato:
94
167
 
168
+ ```
169
+ [numero-tarea]-[titulo-tarea-ingles-corto]
95
170
  ```
96
171
 
172
+ Ejemplo: `1234-add-user-authentication`
97
173
 
174
+ ---
98
175
 
99
176
  ## ⚙️ Configuración
100
177
 
101
178
  Rikudo busca un archivo `.env` en la raíz de tu proyecto para las credenciales sensibles.
102
179
 
103
- Crea un archivo `.env` y añade las variables según el proveedor que quieras usar:
104
-
105
180
  ```env
106
181
  # --- Configuración General ---
107
182
  RIKUDO_AI_PROVIDER=gemini # Opciones: gemini, openai, claude, ollama
@@ -115,41 +190,28 @@ RIKUDO_CLAUDE_API_KEY=tu_api_key_aqui
115
190
  RIKUDO_OLLAMA_URL=http://localhost:11434/api/generate
116
191
  RIKUDO_OLLAMA_MODEL=llama3
117
192
 
118
- # --- Integraciones (Opcional) ---
193
+ # --- Integraciones ---
119
194
  RIKUDO_REDMINE_URL=https://redmine.tuempresa.com
120
195
  RIKUDO_REDMINE_API_KEY=tu_redmine_key
121
196
 
197
+ # --- Gitea (para servidor MCP) ---
198
+ RIKUDO_GITEA_URL=https://gitea.tuempresa.com
199
+ RIKUDO_GITEA_TOKEN=tu_gitea_token
200
+ RIKUDO_GITEA_OWNER=tu_usuario_o_organizacion
201
+ RIKUDO_GITEA_REPO=nombre_del_repositorio
122
202
  ```
123
203
 
124
- ### Plantillas Personalizadas (Templates)
125
-
126
- Puedes personalizar cómo Rikudo escribe. Coloca estos archivos en la raíz de tu proyecto:
204
+ ### Plantillas Personalizadas
127
205
 
128
206
  1. **`conventions.txt`**: Reglas para tus commits.
129
- * *Ejemplo:* "Usa emojis al inicio. Formato: <emoji> <tipo>: <mensaje>."
130
-
131
-
132
207
  2. **`pull-request-template.txt`**: Estructura para tus PRs.
133
- * *Ejemplo:*
134
- ```markdown
135
- # Cambios
136
- - ...
137
- # Tickets Relacionados
138
- - ...
139
-
140
- ```
141
-
142
-
143
-
144
208
 
209
+ ---
145
210
 
146
211
  ## 🛠️ Solución de Problemas
147
212
 
148
- Si tienes problemas de conexión o configuración, ejecuta el diagnóstico:
149
-
150
213
  ```bash
151
214
  rikudo test
152
-
153
215
  ```
154
216
 
155
217
  Esto validará:
@@ -160,4 +222,4 @@ Esto validará:
160
222
 
161
223
  ---
162
224
 
163
- Hecho con ❤️ y TypeScript.
225
+ Hecho con ❤️ y TypeScript.
@@ -36,7 +36,6 @@ Registro del Último Commit (Log Técnico): ${commitMessage}
36
36
 
37
37
  SALIDA:
38
38
  Solo devuelve el comentario, sin títulos o subtítulos o lineas de saludos.
39
-
40
39
  `;
41
40
  }
42
41
  export function generatePullRequestMessagePrompt(commits, ticketInfo, template) {
@@ -65,6 +64,6 @@ INSTRUCCIONES:
65
64
 
66
65
  SALIDA:
67
66
  Solo el contenido de la plantilla rellena.
68
- `;
67
+ `;
69
68
  return prompt;
70
69
  }
package/dist/index.js CHANGED
@@ -16,6 +16,7 @@ import { generateCommitMessagePrompt, generateProjectManagerCommentPrompt, gener
16
16
  import { getConventions, getPRTemplate } from "./utils/config.js";
17
17
  import { showIssueMenu } from "./menus/issueMenu.js";
18
18
  import { runDiagnostics } from "./test/connection.js";
19
+ import { runMCPServer } from "./mcp/server.js";
19
20
  import { COMMIT_CONVENTIONS_PATH, ENV_PATH, PULL_REQUEST_TEMPLATE_PATH } from "./const/paths.js";
20
21
  // --- Setup ---
21
22
  const __filename = fileURLToPath(import.meta.url);
@@ -62,6 +63,14 @@ program
62
63
  .command("config")
63
64
  .description("Abrir menú de configuración")
64
65
  .action(() => showConfigMenu());
66
+ // MCP Server
67
+ program
68
+ .command("mcp")
69
+ .description("Iniciar servidor MCP (Model Context Protocol) para agentes de IA")
70
+ .action(async (options) => {
71
+ loadEnv(options.env);
72
+ await runMCPServer();
73
+ });
65
74
  // --- Modo Interactivo (Default) ---
66
75
  program
67
76
  .description("Modo interactivo del Sabio Rikudo")
@@ -0,0 +1,226 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
3
+ import { z } from "zod";
4
+ import { simpleGit } from "simple-git";
5
+ import { getIssueDetails, updateIssueComment } from "../services/redmine.js";
6
+ import { getStagedDiff } from "../services/git.js";
7
+ import { createPullRequest } from "../services/gitea.js";
8
+ import { loadEnv } from "../utils/env.js";
9
+ import { getConfig } from "../utils/config.js";
10
+ import fs from "node:fs";
11
+ import path from "node:path";
12
+ import { fileURLToPath } from "node:url";
13
+ const __filename = fileURLToPath(import.meta.url);
14
+ const __dirname = path.dirname(__filename);
15
+ const packagePath = path.resolve(__dirname, "../../package.json");
16
+ const packageJson = JSON.parse(fs.readFileSync(packagePath, "utf-8"));
17
+ const git = simpleGit();
18
+ const server = new McpServer({
19
+ name: "rikudo-mcp-server",
20
+ version: packageJson.version || "1.0.0",
21
+ });
22
+ server.registerTool("get_ticket_context", {
23
+ title: "Get Ticket Context",
24
+ description: "Obtiene el contexto completo de un ticket de Redmine (título, descripción, estado, prioridad).",
25
+ inputSchema: z.object({
26
+ ticketId: z.string().describe("ID del ticket de Redmine (ej: '1234')"),
27
+ }),
28
+ }, async ({ ticketId }) => {
29
+ try {
30
+ const ticket = await getIssueDetails(ticketId);
31
+ if (!ticket) {
32
+ return {
33
+ content: [{ type: "text", text: `No se pudo obtener el ticket #${ticketId}` }],
34
+ isError: true,
35
+ };
36
+ }
37
+ return {
38
+ content: [
39
+ {
40
+ type: "text",
41
+ text: JSON.stringify({
42
+ id: ticket.id,
43
+ subject: ticket.subject,
44
+ description: ticket.description,
45
+ status: ticket.status,
46
+ priority: ticket.priority,
47
+ author: ticket.author,
48
+ url: ticket.url,
49
+ }, null, 2),
50
+ },
51
+ ],
52
+ };
53
+ }
54
+ catch (error) {
55
+ const errorMessage = error instanceof Error ? error.message : "Error desconocido";
56
+ return { content: [{ type: "text", text: `Error: ${errorMessage}` }], isError: true };
57
+ }
58
+ });
59
+ server.registerTool("get_git_diff", {
60
+ title: "Get Git Diff",
61
+ description: "Obtiene el diff de los cambios que están en staging (git add). Retorna el diff crudo.",
62
+ inputSchema: z.object({}),
63
+ }, async () => {
64
+ try {
65
+ const diff = await getStagedDiff();
66
+ if (!diff) {
67
+ return {
68
+ content: [{ type: "text", text: "No hay cambios en staging. Ejecuta 'git add' primero." }],
69
+ isError: true,
70
+ };
71
+ }
72
+ return { content: [{ type: "text", text: diff }] };
73
+ }
74
+ catch (error) {
75
+ const errorMessage = error instanceof Error ? error.message : "Error desconocido";
76
+ return { content: [{ type: "text", text: `Error: ${errorMessage}` }], isError: true };
77
+ }
78
+ });
79
+ server.registerTool("validate_configuration", {
80
+ title: "Validate Configuration",
81
+ description: "Verifica si todas las variables de entorno necesarias para Rikudo (Redmine, Gitea y Proveedor de IA) están configuradas correctamente en el sistema del usuario. Llama a esta herramienta antes de intentar interactuar con tickets o repositorios.",
82
+ inputSchema: z.object({}),
83
+ }, async () => {
84
+ const config = getConfig();
85
+ const missingVars = [];
86
+ if (!config.REDMINE_URL)
87
+ missingVars.push("RIKUDO_REDMINE_URL");
88
+ if (!config.REDMINE_API_KEY)
89
+ missingVars.push("RIKUDO_REDMINE_API_KEY");
90
+ if (!config.GITEA_URL)
91
+ missingVars.push("RIKUDO_GITEA_URL");
92
+ if (!config.GITEA_TOKEN)
93
+ missingVars.push("RIKUDO_GITEA_TOKEN");
94
+ if (missingVars.length === 0) {
95
+ return {
96
+ content: [{ type: "text", text: "✅ Configuración de entorno válida. Todos los sistemas listos." }],
97
+ };
98
+ }
99
+ return {
100
+ content: [
101
+ {
102
+ type: "text",
103
+ text: `❌ Faltan las siguientes variables de entorno: ${missingVars.join(", ")}. Por favor, pide al usuario que las configure en su archivo .env antes de continuar.`,
104
+ },
105
+ ],
106
+ isError: true,
107
+ };
108
+ });
109
+ server.registerTool("shift_left_start", {
110
+ title: "Shift Left Start",
111
+ description: "Inicia un flujo shift-left: hace checkout a la rama objetivo, crea una nueva rama, hace commit vacío y abre un Draft PR en Gitea.",
112
+ inputSchema: z.object({
113
+ ticketId: z.string().describe("ID del ticket de Redmine (ej: '1234')"),
114
+ branchName: z.string().describe("Nombre de la nueva rama a crear (ej: '1234-add-user-auth')"),
115
+ targetBranch: z.string().default("alpha").describe("Rama base para hacer checkout (default: 'alpha')"),
116
+ isWip: z.boolean().default(false).describe("Si es true, el PR título lleva prefijo 'WIP:' (Work In Progress)"),
117
+ }),
118
+ }, async ({ ticketId, branchName, targetBranch, isWip }) => {
119
+ try {
120
+ await git.checkout(targetBranch);
121
+ await git.pull("origin", targetBranch);
122
+ await git.checkoutLocalBranch(branchName);
123
+ await git.commit("chore: init #" + ticketId, ["--allow-empty"]);
124
+ await git.push(["-u", "origin", branchName]);
125
+ const ticket = await getIssueDetails(ticketId);
126
+ const prTitle = isWip
127
+ ? `WIP: ${ticket?.subject || `Ticket #${ticketId}`}`
128
+ : `Draft: ${ticket?.subject || `Ticket #${ticketId}`}`;
129
+ const prBody = `## Descripción\n\nTrabajo iniciado para el ticket #${ticketId}${ticket ? `\n\n**${ticket.subject}**` : ""}`;
130
+ const prResult = await createPullRequest({
131
+ title: prTitle,
132
+ head: branchName,
133
+ base: targetBranch,
134
+ body: prBody,
135
+ draft: true,
136
+ });
137
+ if (!prResult.success) {
138
+ return {
139
+ content: [{ type: "text", text: `Rama '${branchName}' creada y empujada a origin. Error al crear PR: ${prResult.error}` }],
140
+ isError: true,
141
+ };
142
+ }
143
+ return {
144
+ content: [
145
+ {
146
+ type: "text",
147
+ text: JSON.stringify({
148
+ success: true,
149
+ branch: branchName,
150
+ targetBranch,
151
+ prUrl: prResult.url,
152
+ prNumber: prResult.number,
153
+ message: `Entorno preparado en rama '${branchName}'. Draft PR creado: ${prResult.url}`,
154
+ }, null, 2),
155
+ },
156
+ ],
157
+ };
158
+ }
159
+ catch (error) {
160
+ const errorMessage = error instanceof Error ? error.message : "Error desconocido";
161
+ return { content: [{ type: "text", text: `Error en shift_left_start: ${errorMessage}` }], isError: true };
162
+ }
163
+ });
164
+ server.registerTool("update_pm_redmine", {
165
+ title: "Update PM Redmine",
166
+ description: "Agrega un comentario a un ticket de Redmine. Ideal para notificar al Project Manager.",
167
+ inputSchema: z.object({
168
+ ticketId: z.string().describe("ID del ticket de Redmine (ej: '1234')"),
169
+ commentMarkdown: z.string().describe("Comentario en formato Markdown/Textile para Redmine"),
170
+ }),
171
+ }, async ({ ticketId, commentMarkdown }) => {
172
+ try {
173
+ await updateIssueComment(ticketId, commentMarkdown);
174
+ return {
175
+ content: [{ type: "text", text: `Comentario agregado exitosamente al ticket #${ticketId}` }],
176
+ };
177
+ }
178
+ catch (error) {
179
+ const errorMessage = error instanceof Error ? error.message : "Error desconocido";
180
+ return { content: [{ type: "text", text: `Error actualizando Redmine: ${errorMessage}` }], isError: true };
181
+ }
182
+ });
183
+ server.registerPrompt("start_feature", {
184
+ title: "Start Feature",
185
+ description: "Inicia el flujo de trabajo para una nueva feature. Obtiene contexto del ticket y prepara el entorno en Gitea.",
186
+ argsSchema: {
187
+ ticket_id: z.string().describe("ID del ticket de Redmine"),
188
+ },
189
+ }, async ({ ticket_id }) => {
190
+ return {
191
+ messages: [
192
+ {
193
+ role: "user",
194
+ content: {
195
+ type: "text",
196
+ text: `Eres un asistente de desarrollo. Llama a la herramienta 'get_ticket_context' para el ticket ${ticket_id}. Lee su título y descripción. Genera un nombre de rama en inglés, cortas y en minúsculas, con el formato '[numero-tarea]-[titulo-tarea-ingles-corto]' (ej: '1234-add-user-authentication'). Luego, llama obligatoriamente a la herramienta 'shift_left_start' con esos datos para preparar mi entorno de trabajo en Gitea.`,
197
+ },
198
+ },
199
+ ],
200
+ };
201
+ });
202
+ server.registerPrompt("review_and_comment", {
203
+ title: "Review and Comment",
204
+ description: "Analiza los cambios staged, genera un comentario técnico para el Project Manager y lo publica en Redmine.",
205
+ argsSchema: {
206
+ ticket_id: z.string().describe("ID del ticket de Redmine"),
207
+ },
208
+ }, async ({ ticket_id }) => {
209
+ return {
210
+ messages: [
211
+ {
212
+ role: "user",
213
+ content: {
214
+ type: "text",
215
+ text: `Llama a la herramienta 'get_git_diff'. Analiza profundamente los cambios de código. Redacta un comentario técnico formal para el Project Manager explicando qué se hizo. Finalmente, llama a la herramienta 'update_pm_redmine' con el ${ticket_id} y el comentario generado para notificar al equipo.`,
216
+ },
217
+ },
218
+ ],
219
+ };
220
+ });
221
+ export async function runMCPServer() {
222
+ loadEnv();
223
+ const transport = new StdioServerTransport();
224
+ await server.connect(transport);
225
+ console.error("Rikudo MCP Server started");
226
+ }
@@ -0,0 +1,59 @@
1
+ import axios, { AxiosError } from "axios";
2
+ const getGiteaConfig = () => {
3
+ return {
4
+ url: process.env.RIKUDO_GITEA_URL || "",
5
+ token: process.env.RIKUDO_GITEA_TOKEN || "",
6
+ owner: process.env.RIKUDO_GITEA_OWNER || "",
7
+ repo: process.env.RIKUDO_GITEA_REPO || "",
8
+ };
9
+ };
10
+ const getGiteaClient = () => {
11
+ const config = getGiteaConfig();
12
+ return axios.create({
13
+ baseURL: config.url,
14
+ headers: {
15
+ Authorization: `token ${config.token}`,
16
+ "Content-Type": "application/json",
17
+ },
18
+ });
19
+ };
20
+ export const createPullRequest = async (payload) => {
21
+ const config = getGiteaConfig();
22
+ if (!config.url || !config.token || !config.owner || !config.repo) {
23
+ return {
24
+ success: false,
25
+ error: "Faltan variables de entorno de Gitea (RIKUDO_GITEA_URL, RIKUDO_GITEA_TOKEN, RIKUDO_GITEA_OWNER, RIKUDO_GITEA_REPO)",
26
+ };
27
+ }
28
+ try {
29
+ const client = getGiteaClient();
30
+ const endpoint = `/api/v1/repos/${config.owner}/${config.repo}/pulls`;
31
+ const response = await client.post(endpoint, {
32
+ title: payload.title,
33
+ head: payload.head,
34
+ base: payload.base,
35
+ body: payload.body,
36
+ draft: payload.draft ?? false,
37
+ });
38
+ return {
39
+ success: true,
40
+ url: response.data.html_url,
41
+ number: response.data.number,
42
+ };
43
+ }
44
+ catch (error) {
45
+ if (error instanceof AxiosError) {
46
+ return {
47
+ success: false,
48
+ error: `Error de Gitea: ${error.message} - ${error.response?.data?.message || ""}`,
49
+ };
50
+ }
51
+ return {
52
+ success: false,
53
+ error: error instanceof Error ? error.message : "Error desconocido al crear PR",
54
+ };
55
+ }
56
+ };
57
+ export const Gitea = {
58
+ createPullRequest,
59
+ };
@@ -17,6 +17,10 @@ export function getConfig() {
17
17
  REDMINE_URL: process.env.RIKUDO_REDMINE_URL || "",
18
18
  REDMINE_API_KEY: process.env.RIKUDO_REDMINE_API_KEY || "",
19
19
  PROJECT_ID: process.env.RIKUDO_REDMINE_PROJECT_ID || "",
20
+ GITEA_URL: process.env.RIKUDO_GITEA_URL || "",
21
+ GITEA_TOKEN: process.env.RIKUDO_GITEA_TOKEN || "",
22
+ GITEA_OWNER: process.env.RIKUDO_GITEA_OWNER || "",
23
+ GITEA_REPO: process.env.RIKUDO_GITEA_REPO || "",
20
24
  };
21
25
  }
22
26
  export function getLimits() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dami_deleon/rikudo",
3
- "version": "1.1.0",
3
+ "version": "2.0.1",
4
4
  "description": "El Sabio de los Commits: AI Assistant con integración a Redmine",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",
@@ -35,6 +35,7 @@
35
35
  "@anthropic-ai/sdk": "0.74.0",
36
36
  "@google/generative-ai": "0.24.1",
37
37
  "@inquirer/prompts": "8.2.0",
38
+ "@modelcontextprotocol/sdk": "^1.27.1",
38
39
  "@types/figlet": "1.7.0",
39
40
  "axios": "1.13.2",
40
41
  "chalk": "5.6.2",