mcp-phase-gate 2.1.0 → 2.2.0
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.es.md +125 -57
- package/README.md +109 -50
- package/build/cli.d.ts +14 -0
- package/build/cli.js +98 -0
- package/build/config.d.ts +27 -2
- package/build/config.js +151 -22
- package/build/confirmacion.d.ts +15 -0
- package/build/confirmacion.js +100 -0
- package/build/diagnostico.d.ts +83 -0
- package/build/diagnostico.js +394 -0
- package/build/index.js +28 -388
- package/build/log.d.ts +6 -0
- package/build/log.js +27 -0
- package/build/paths.js +3 -3
- package/build/phases.d.ts +10 -1
- package/build/phases.js +48 -17
- package/build/prompts-mcp.d.ts +33 -0
- package/build/prompts-mcp.js +194 -0
- package/build/recursos.d.ts +8 -0
- package/build/recursos.js +306 -0
- package/build/schemas.d.ts +105 -51
- package/build/schemas.js +151 -78
- package/build/servidor.d.ts +3 -0
- package/build/servidor.js +372 -0
- package/build/tasks.d.ts +2 -0
- package/build/tasks.js +114 -2
- package/package.json +1 -2
- package/prompts/comandos/f1.md +14 -0
- package/prompts/comandos/f2.md +12 -0
- package/prompts/comandos/f3.md +9 -0
- package/prompts/comandos/f4.md +10 -0
- package/prompts/comandos/f5.md +12 -0
package/build/schemas.d.ts
CHANGED
|
@@ -27,68 +27,122 @@ export declare const readCentralDocShape: {
|
|
|
27
27
|
file_path: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
28
28
|
};
|
|
29
29
|
export declare const writeCentralDocShape: {
|
|
30
|
+
file_path: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
30
31
|
content: z.ZodString;
|
|
31
32
|
force: z.ZodOptional<z.ZodNullable<z.ZodBoolean>>;
|
|
32
33
|
project: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
33
34
|
task_name: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
34
35
|
file_name: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
35
|
-
file_path: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
36
36
|
};
|
|
37
37
|
export declare const readCrossRepoShape: {
|
|
38
38
|
repo_name: z.ZodString;
|
|
39
39
|
file_path: z.ZodString;
|
|
40
40
|
};
|
|
41
|
-
declare const
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
41
|
+
export declare const getOrchestratorStatusShape: {};
|
|
42
|
+
export declare const diagnosticoShape: {
|
|
43
|
+
paquete: z.ZodObject<{
|
|
44
|
+
version: z.ZodString;
|
|
45
|
+
ruta: z.ZodString;
|
|
46
|
+
via: z.ZodEnum<{
|
|
47
|
+
npx: "npx";
|
|
48
|
+
global: "global";
|
|
49
|
+
dependencia: "dependencia";
|
|
50
|
+
clon: "clon";
|
|
51
|
+
}>;
|
|
52
|
+
}, z.core.$strip>;
|
|
53
|
+
node: z.ZodObject<{
|
|
54
|
+
version: z.ZodString;
|
|
55
|
+
plataforma: z.ZodString;
|
|
56
|
+
}, z.core.$strip>;
|
|
57
|
+
variables: z.ZodArray<z.ZodObject<{
|
|
58
|
+
logica: z.ZodEnum<{
|
|
59
|
+
DOCS_PATH: "DOCS_PATH";
|
|
60
|
+
REPOS_PATH: "REPOS_PATH";
|
|
61
|
+
TASKS_SUBDIR: "TASKS_SUBDIR";
|
|
62
|
+
PROMPTS_PATH: "PROMPTS_PATH";
|
|
63
|
+
STATE_PATH: "STATE_PATH";
|
|
64
|
+
ELICITATION: "ELICITATION";
|
|
65
|
+
}>;
|
|
66
|
+
valor: z.ZodOptional<z.ZodString>;
|
|
67
|
+
usada: z.ZodNullable<z.ZodString>;
|
|
68
|
+
fuente: z.ZodEnum<{
|
|
69
|
+
".env": ".env";
|
|
70
|
+
entorno: "entorno";
|
|
71
|
+
"sin definir": "sin definir";
|
|
72
|
+
}>;
|
|
73
|
+
discrepancia: z.ZodNullable<z.ZodString>;
|
|
74
|
+
}, z.core.$strip>>;
|
|
75
|
+
rutas: z.ZodObject<{
|
|
76
|
+
docs: z.ZodObject<{
|
|
77
|
+
ruta: z.ZodOptional<z.ZodString>;
|
|
78
|
+
existe: z.ZodBoolean;
|
|
79
|
+
error: z.ZodOptional<z.ZodString>;
|
|
80
|
+
}, z.core.$strip>;
|
|
81
|
+
repos: z.ZodObject<{
|
|
82
|
+
ruta: z.ZodOptional<z.ZodString>;
|
|
83
|
+
existe: z.ZodBoolean;
|
|
84
|
+
error: z.ZodOptional<z.ZodString>;
|
|
85
|
+
}, z.core.$strip>;
|
|
86
|
+
}, z.core.$strip>;
|
|
87
|
+
estado: z.ZodObject<{
|
|
88
|
+
archivo: z.ZodString;
|
|
89
|
+
existe: z.ZodBoolean;
|
|
90
|
+
punteros: z.ZodNumber;
|
|
91
|
+
}, z.core.$strip>;
|
|
92
|
+
capas: z.ZodArray<z.ZodObject<{
|
|
93
|
+
ruta: z.ZodString;
|
|
94
|
+
etiqueta: z.ZodEnum<{
|
|
95
|
+
fábrica: "fábrica";
|
|
96
|
+
propia: "propia";
|
|
97
|
+
}>;
|
|
98
|
+
existe: z.ZodBoolean;
|
|
99
|
+
}, z.core.$strip>>;
|
|
100
|
+
reglasGlobales: z.ZodObject<{
|
|
101
|
+
archivo: z.ZodNullable<z.ZodString>;
|
|
102
|
+
capa: z.ZodNullable<z.ZodString>;
|
|
103
|
+
faltan: z.ZodArray<z.ZodString>;
|
|
104
|
+
}, z.core.$strip>;
|
|
105
|
+
fases: z.ZodArray<z.ZodObject<{
|
|
106
|
+
numero: z.ZodNumber;
|
|
107
|
+
titulo: z.ZodString;
|
|
108
|
+
archivo: z.ZodString;
|
|
109
|
+
capa: z.ZodString;
|
|
110
|
+
documento: z.ZodOptional<z.ZodString>;
|
|
111
|
+
recibos: z.ZodOptional<z.ZodString>;
|
|
112
|
+
comando: z.ZodNullable<z.ZodString>;
|
|
113
|
+
}, z.core.$strip>>;
|
|
114
|
+
proyectos: z.ZodArray<z.ZodObject<{
|
|
115
|
+
nombre: z.ZodString;
|
|
116
|
+
tareas: z.ZodNumber;
|
|
117
|
+
activa: z.ZodNullable<z.ZodObject<{
|
|
118
|
+
tarea: z.ZodString;
|
|
119
|
+
origen: z.ZodEnum<{
|
|
120
|
+
puntero: "puntero";
|
|
121
|
+
deducida: "deducida";
|
|
122
|
+
}>;
|
|
123
|
+
fase: z.ZodString;
|
|
124
|
+
}, z.core.$strip>>;
|
|
125
|
+
}, z.core.$strip>>;
|
|
126
|
+
cliente: z.ZodOptional<z.ZodObject<{
|
|
127
|
+
nombre: z.ZodString;
|
|
128
|
+
version: z.ZodString;
|
|
129
|
+
capacidades: z.ZodObject<{
|
|
130
|
+
elicitation: z.ZodBoolean;
|
|
131
|
+
roots: z.ZodBoolean;
|
|
132
|
+
}, z.core.$strip>;
|
|
133
|
+
}, z.core.$strip>>;
|
|
134
|
+
hallazgos: z.ZodArray<z.ZodObject<{
|
|
135
|
+
severidad: z.ZodEnum<{
|
|
136
|
+
error: "error";
|
|
137
|
+
info: "info";
|
|
138
|
+
aviso: "aviso";
|
|
139
|
+
}>;
|
|
140
|
+
codigo: z.ZodString;
|
|
141
|
+
texto: z.ZodString;
|
|
142
|
+
}, z.core.$strip>>;
|
|
81
143
|
};
|
|
82
|
-
type
|
|
83
|
-
type SinNulos<T> = {
|
|
144
|
+
export type SinNulos<T> = {
|
|
84
145
|
[K in keyof T]: Exclude<T[K], null>;
|
|
85
146
|
};
|
|
86
|
-
export
|
|
87
|
-
[K in NombreTool]: {
|
|
88
|
-
tool: K;
|
|
89
|
-
args: SinNulos<z.output<z.ZodObject<(typeof SHAPES)[K]>>>;
|
|
90
|
-
};
|
|
91
|
-
}[NombreTool];
|
|
92
|
-
export declare function validarLlamada(tool: string, args: unknown): LlamadaValidada;
|
|
93
|
-
export {};
|
|
147
|
+
export declare function sinNulos<T extends Record<string, unknown>>(args: T): SinNulos<T>;
|
|
94
148
|
//# sourceMappingURL=schemas.d.ts.map
|
package/build/schemas.js
CHANGED
|
@@ -1,108 +1,181 @@
|
|
|
1
1
|
// Los argumentos de cada tool, declarados una sola vez con zod.
|
|
2
2
|
//
|
|
3
3
|
// Antes cada tool leía sus argumentos con String(args?.x), que no valida nada: sin task_name,
|
|
4
|
-
// String(undefined) creaba la tarea "undefined/" y la dejaba activa.
|
|
5
|
-
// antes de usarse, y el error nombra el campo.
|
|
4
|
+
// String(undefined) creaba la tarea "undefined/" y la dejaba activa. Desde la Etapa 0 todo pasa
|
|
5
|
+
// por aquí antes de usarse, y el error nombra el campo.
|
|
6
6
|
//
|
|
7
|
-
// Se exportan shapes (los campos sueltos) y no z.object(...): es lo que recibe registerTool
|
|
8
|
-
//
|
|
7
|
+
// Se exportan shapes (los campos sueltos) y no z.object(...): es lo que recibe registerTool del
|
|
8
|
+
// SDK. El SDK genera con ellos el JSON Schema que ve el cliente —por eso cada campo lleva aquí su
|
|
9
|
+
// descripción— y valida cada llamada antes del callback.
|
|
10
|
+
//
|
|
11
|
+
// Los mensajes de error van en cada campo, en español, porque el SDK los entrega tal cual detrás
|
|
12
|
+
// de su prefijo en inglés ("Input validation error: Invalid arguments for tool …"). Sin ellos
|
|
13
|
+
// llegaría el texto genérico de zod ("expected string, received undefined"), que no le dice al
|
|
14
|
+
// modelo cómo corregir la llamada.
|
|
9
15
|
//
|
|
10
16
|
// Los opcionales son .nullish() y null cuenta como ausente: algunos clientes con modelos en
|
|
11
17
|
// modo estricto mandan null en todo campo opcional. Un número en texto (phase: "3") no se
|
|
12
18
|
// convierte: se rechaza con un mensaje que dice qué se espera. Es raro, el modelo lo corrige
|
|
13
|
-
// al primer intento, y convertirlo complicaría el JSON Schema que se
|
|
19
|
+
// al primer intento, y convertirlo complicaría el JSON Schema que se genera desde aquí.
|
|
14
20
|
import { z } from "zod";
|
|
21
|
+
// Lo que se espera de cada tipo, dicho como se le diría al modelo para que corrija la llamada.
|
|
22
|
+
// El único número es `phase`, de ahí el ejemplo.
|
|
23
|
+
const TIPOS = {
|
|
24
|
+
string: "un texto",
|
|
25
|
+
number: "un número, por ejemplo 3",
|
|
26
|
+
int: "un número entero, por ejemplo 3",
|
|
27
|
+
boolean: "true o false",
|
|
28
|
+
object: "un objeto",
|
|
29
|
+
};
|
|
30
|
+
// El mensaje de un campo. null también es "falta": en un opcional se tolera antes de llegar aquí,
|
|
31
|
+
// en un obligatorio es lo mismo que no venir.
|
|
32
|
+
function mensaje(campo) {
|
|
33
|
+
const nombre = `\`${campo}\``;
|
|
34
|
+
return (issue) => {
|
|
35
|
+
if (issue.input === undefined || issue.input === null)
|
|
36
|
+
return `Falta ${nombre}.`;
|
|
37
|
+
if (issue.code === "invalid_type")
|
|
38
|
+
return `${nombre} debe ser ${TIPOS[issue.expected] ?? issue.expected}.`;
|
|
39
|
+
// El único máximo que puede saltar es el del entero seguro de `phase`.
|
|
40
|
+
if (issue.code === "too_big")
|
|
41
|
+
return `${nombre} es demasiado grande.`;
|
|
42
|
+
return `${nombre} no es válido.`;
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
function texto(campo) {
|
|
46
|
+
return z.string({ error: mensaje(campo) });
|
|
47
|
+
}
|
|
48
|
+
// `recortar` quita los espacios ANTES de exigir el mínimo: " " cuenta como que falta.
|
|
49
|
+
function obligatorio(campo, opciones = {}) {
|
|
50
|
+
const base = opciones.recortar ? texto(campo).trim() : texto(campo);
|
|
51
|
+
return base.min(1, `Falta \`${campo}\`.`);
|
|
52
|
+
}
|
|
53
|
+
function booleano(campo) {
|
|
54
|
+
return z.boolean({ error: mensaje(campo) });
|
|
55
|
+
}
|
|
56
|
+
// ─── Descripciones ──────────────────────────────────────────────────────────
|
|
57
|
+
// Son estáticas: no nombran proyectos ni fases concretas. Antes se armaban al arrancar con la
|
|
58
|
+
// lista de proyectos y de fases, y se quedaban viejas en cuanto alguien creaba una carpeta con el
|
|
59
|
+
// cliente abierto, porque la lista de tools no se vuelve a pedir. Lo que cambia vive ahora en los
|
|
60
|
+
// recursos phase-gate://estado y phase-gate://fases, que sí avisan cuando cambian.
|
|
61
|
+
const PROYECTO = "Proyecto: el nombre de una carpeta de proyecto de la Documentación Central. Si no existe, la " +
|
|
62
|
+
"tool te devuelve los que hay. También los listan `list_tasks`, `get_orchestrator_status` y el recurso " +
|
|
63
|
+
"`phase-gate://estado`.";
|
|
64
|
+
const FASE = "Número de fase. Con los prompts de fábrica son 1 a 5; `get_orchestrator_status` y el recurso " +
|
|
65
|
+
"`phase-gate://fases` listan las disponibles con su título y documento.";
|
|
66
|
+
const TAREA = "Nombre de la carpeta de la tarea.";
|
|
67
|
+
const ARCHIVO = "Nombre (o ruta relativa) del archivo dentro de la carpeta de la tarea (ej. '01 - Análisis Técnico.md').";
|
|
68
|
+
// ─── Shapes ─────────────────────────────────────────────────────────────────
|
|
15
69
|
export const startTaskShape = {
|
|
16
|
-
project:
|
|
17
|
-
task_name:
|
|
18
|
-
initial_context:
|
|
19
|
-
crear_proyecto:
|
|
70
|
+
project: obligatorio("project", { recortar: true }).describe(PROYECTO),
|
|
71
|
+
task_name: obligatorio("task_name").describe("Nombre de la tarea (creará una carpeta con este nombre)."),
|
|
72
|
+
initial_context: obligatorio("initial_context").describe("El contexto, correo o requerimiento inicial completo."),
|
|
73
|
+
crear_proyecto: booleano("crear_proyecto")
|
|
74
|
+
.nullish()
|
|
75
|
+
.describe("Solo para ESTRENAR un proyecto que todavía no existe. Déjalo sin poner salvo que el usuario te haya " +
|
|
76
|
+
"dicho explícitamente que quiere crear un proyecto nuevo: sirve para que un nombre mal escrito no cree " +
|
|
77
|
+
"una carpeta suelta en la Documentación."),
|
|
20
78
|
};
|
|
21
79
|
export const getPhasePromptShape = {
|
|
22
|
-
phase: z.number().int(),
|
|
23
|
-
project:
|
|
24
|
-
|
|
80
|
+
phase: z.number({ error: mensaje("phase") }).int({ error: mensaje("phase") }).describe(FASE),
|
|
81
|
+
project: texto("project")
|
|
82
|
+
.nullish()
|
|
83
|
+
.describe(`${PROYECTO} Pásalo siempre que lo sepas —lo sabes por el repositorio que tienes abierto— para que se ` +
|
|
84
|
+
"pueda comprobar que no te saltas una fase. La tarea se toma de la activa de ese proyecto."),
|
|
85
|
+
task_name: texto("task_name")
|
|
86
|
+
.nullish()
|
|
87
|
+
.describe("Solo si quieres comprobar contra una tarea distinta de la activa del proyecto. Normalmente se omite."),
|
|
25
88
|
};
|
|
26
89
|
export const getActiveTaskShape = {
|
|
27
|
-
project:
|
|
90
|
+
project: texto("project")
|
|
91
|
+
.nullish()
|
|
92
|
+
.describe(`${PROYECTO} Si lo omites, se devuelve la tarea activa de cada proyecto.`),
|
|
28
93
|
};
|
|
29
94
|
export const listTasksShape = {
|
|
30
|
-
project:
|
|
95
|
+
project: texto("project").nullish().describe(`${PROYECTO} Si lo omites, se listan todos.`),
|
|
31
96
|
};
|
|
32
97
|
export const switchTaskShape = {
|
|
33
|
-
project:
|
|
34
|
-
task_name:
|
|
98
|
+
project: obligatorio("project", { recortar: true }).describe(PROYECTO),
|
|
99
|
+
task_name: obligatorio("task_name").describe("Nombre de la carpeta de la tarea a activar. Si no existe, la tool te devuelve las que sí."),
|
|
35
100
|
};
|
|
36
101
|
// Los cuatro son opcionales por separado: qué combinación vale lo decide resolveDocPath.
|
|
37
102
|
export const readCentralDocShape = {
|
|
38
|
-
project:
|
|
39
|
-
task_name:
|
|
40
|
-
file_name:
|
|
41
|
-
file_path:
|
|
103
|
+
project: texto("project").nullish().describe(`${PROYECTO} Úsalo junto con task_name y file_name.`),
|
|
104
|
+
task_name: texto("task_name").nullish().describe(TAREA),
|
|
105
|
+
file_name: texto("file_name").nullish().describe(ARCHIVO),
|
|
106
|
+
file_path: texto("file_path")
|
|
107
|
+
.nullish()
|
|
108
|
+
.describe("ALTERNATIVA: ruta relativa dentro de la Documentación Central (ej. " +
|
|
109
|
+
"'PROYECTO-A/Proyectos/tarea/01 - Análisis Técnico.md'). Solo si no usas project+task_name."),
|
|
42
110
|
};
|
|
43
111
|
export const writeCentralDocShape = {
|
|
44
112
|
...readCentralDocShape,
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
113
|
+
file_path: texto("file_path")
|
|
114
|
+
.nullish()
|
|
115
|
+
.describe("ALTERNATIVA: ruta relativa dentro de la Documentación Central, que tiene que caer dentro de la carpeta " +
|
|
116
|
+
"de una tarea que ya existe (ej. 'PROYECTO-A/Proyectos/tarea/01 - Análisis Técnico.md'). Los archivos " +
|
|
117
|
+
"del proyecto no se escriben con esta tool. Solo si no usas project+task_name."),
|
|
118
|
+
content: texto("content").describe("Contenido a escribir."),
|
|
119
|
+
force: booleano("force")
|
|
120
|
+
.nullish()
|
|
121
|
+
.describe("Permite reescribir un documento congelado o el contexto inicial. Solo se usa si tu cliente no puede " +
|
|
122
|
+
"pedirle la confirmación al usuario directamente. Nunca lo pongas sin que el usuario lo haya confirmado en el chat."),
|
|
49
123
|
};
|
|
50
124
|
export const readCrossRepoShape = {
|
|
51
|
-
repo_name:
|
|
52
|
-
file_path:
|
|
125
|
+
repo_name: obligatorio("repo_name").describe("Nombre del repositorio (ej. 'repo-a', 'repo-b')."),
|
|
126
|
+
file_path: obligatorio("file_path").describe("Ruta relativa del archivo dentro del repositorio (ej. 'src/main.ts')."),
|
|
53
127
|
};
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
128
|
+
// get_orchestrator_status no recibe nada.
|
|
129
|
+
export const getOrchestratorStatusShape = {};
|
|
130
|
+
// ─── Salida de get_orchestrator_status ──────────────────────────────────────
|
|
131
|
+
// El objeto Diagnostico de diagnostico.ts, campo por campo. Con un outputSchema el cliente recibe
|
|
132
|
+
// además `structuredContent`, y el SDK comprueba antes de enviarlo que cuadra con este shape:
|
|
133
|
+
// si diagnostico.ts cambia y esto no, la tool falla en los tests en vez de mentir al cliente.
|
|
134
|
+
const estadoRuta = z.object({ ruta: z.string().optional(), existe: z.boolean(), error: z.string().optional() });
|
|
135
|
+
export const diagnosticoShape = {
|
|
136
|
+
paquete: z.object({
|
|
137
|
+
version: z.string(),
|
|
138
|
+
ruta: z.string(),
|
|
139
|
+
via: z.enum(["npx", "global", "dependencia", "clon"]),
|
|
140
|
+
}),
|
|
141
|
+
node: z.object({ version: z.string(), plataforma: z.string() }),
|
|
142
|
+
variables: z.array(z.object({
|
|
143
|
+
logica: z.enum(["DOCS_PATH", "REPOS_PATH", "TASKS_SUBDIR", "PROMPTS_PATH", "STATE_PATH", "ELICITATION"]),
|
|
144
|
+
valor: z.string().optional(),
|
|
145
|
+
usada: z.string().nullable(),
|
|
146
|
+
fuente: z.enum(["entorno", ".env", "sin definir"]),
|
|
147
|
+
discrepancia: z.string().nullable(),
|
|
148
|
+
})),
|
|
149
|
+
rutas: z.object({ docs: estadoRuta, repos: estadoRuta }),
|
|
150
|
+
estado: z.object({ archivo: z.string(), existe: z.boolean(), punteros: z.number().int() }),
|
|
151
|
+
capas: z.array(z.object({ ruta: z.string(), etiqueta: z.enum(["fábrica", "propia"]), existe: z.boolean() })),
|
|
152
|
+
reglasGlobales: z.object({ archivo: z.string().nullable(), capa: z.string().nullable(), faltan: z.array(z.string()) }),
|
|
153
|
+
fases: z.array(z.object({
|
|
154
|
+
numero: z.number().int(),
|
|
155
|
+
titulo: z.string(),
|
|
156
|
+
archivo: z.string(),
|
|
157
|
+
capa: z.string(),
|
|
158
|
+
documento: z.string().optional(),
|
|
159
|
+
recibos: z.string().optional(),
|
|
160
|
+
comando: z.string().nullable(),
|
|
161
|
+
})),
|
|
162
|
+
proyectos: z.array(z.object({
|
|
163
|
+
nombre: z.string(),
|
|
164
|
+
tareas: z.number().int(),
|
|
165
|
+
activa: z
|
|
166
|
+
.object({ tarea: z.string(), origen: z.enum(["puntero", "deducida"]), fase: z.string() })
|
|
167
|
+
.nullable(),
|
|
168
|
+
})),
|
|
169
|
+
cliente: z
|
|
170
|
+
.object({
|
|
171
|
+
nombre: z.string(),
|
|
172
|
+
version: z.string(),
|
|
173
|
+
capacidades: z.object({ elicitation: z.boolean(), roots: z.boolean() }),
|
|
174
|
+
})
|
|
175
|
+
.optional(),
|
|
176
|
+
hallazgos: z.array(z.object({ severidad: z.enum(["error", "aviso", "info"]), codigo: z.string(), texto: z.string() })),
|
|
63
177
|
};
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
const TIPOS = {
|
|
67
|
-
string: "un texto",
|
|
68
|
-
number: "un número, por ejemplo 3",
|
|
69
|
-
int: "un número entero, por ejemplo 3",
|
|
70
|
-
boolean: "true o false",
|
|
71
|
-
object: "un objeto",
|
|
72
|
-
};
|
|
73
|
-
function describirProblema(issue, recibidos) {
|
|
74
|
-
const campo = issue.path.map(String).join(".");
|
|
75
|
-
if (!campo)
|
|
76
|
-
return "los argumentos tienen que ser un objeto.";
|
|
77
|
-
const nombre = `\`${campo}\``;
|
|
78
|
-
// null también es "falta": en un opcional se tolera, en un obligatorio es lo mismo que no venir.
|
|
79
|
-
if (issue.code === "invalid_type") {
|
|
80
|
-
return recibidos[campo] == null
|
|
81
|
-
? `Falta ${nombre}.`
|
|
82
|
-
: `${nombre} debe ser ${TIPOS[issue.expected] ?? issue.expected}.`;
|
|
83
|
-
}
|
|
84
|
-
// Los mínimos llevan su mensaje en español en el propio shape ("Falta `task_name`.").
|
|
85
|
-
if (issue.code === "too_small")
|
|
86
|
-
return issue.message;
|
|
87
|
-
// Los máximos no: el único que puede saltar es el del entero seguro de `phase`.
|
|
88
|
-
if (issue.code === "too_big")
|
|
89
|
-
return `${nombre} es demasiado grande.`;
|
|
90
|
-
return `${nombre}: ${issue.message}.`;
|
|
91
|
-
}
|
|
92
|
-
export function validarLlamada(tool, args) {
|
|
93
|
-
if (!Object.hasOwn(SHAPES, tool)) {
|
|
94
|
-
throw new Error(`Unknown tool: ${tool}`);
|
|
95
|
-
}
|
|
96
|
-
const resultado = z.object(SHAPES[tool]).safeParse(args ?? {});
|
|
97
|
-
if (!resultado.success) {
|
|
98
|
-
const recibidos = (args && typeof args === "object" ? args : {});
|
|
99
|
-
const problemas = resultado.error.issues.map((issue) => describirProblema(issue, recibidos));
|
|
100
|
-
throw new Error(`Argumentos no válidos. ${problemas.join(" ")}`);
|
|
101
|
-
}
|
|
102
|
-
// null cuenta como ausente: se quita aquí, una vez, para que ninguna tool tenga que
|
|
103
|
-
// distinguir entre un campo que no vino y uno que vino vacío.
|
|
104
|
-
const sinNulos = Object.fromEntries(Object.entries(resultado.data).filter(([, valor]) => valor !== null));
|
|
105
|
-
// El cast es seguro: los datos los acaba de producir el shape de esta misma tool.
|
|
106
|
-
return { tool, args: sinNulos };
|
|
178
|
+
export function sinNulos(args) {
|
|
179
|
+
return Object.fromEntries(Object.entries(args).filter(([, valor]) => valor !== null));
|
|
107
180
|
}
|
|
108
181
|
//# sourceMappingURL=schemas.js.map
|