@dozimple/abap-adt 1.0.0-rc.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.
Files changed (75) hide show
  1. package/CHANGELOG.md +75 -0
  2. package/LICENSE +202 -0
  3. package/NOTICE +19 -0
  4. package/README.es.md +356 -0
  5. package/README.md +356 -0
  6. package/SECURITY.md +68 -0
  7. package/THIRD_PARTY_NOTICES.md +134 -0
  8. package/config/systems.example.json +39 -0
  9. package/dist/core/activation.js +18 -0
  10. package/dist/core/atc.js +94 -0
  11. package/dist/core/audit.js +93 -0
  12. package/dist/core/catalog.en.js +92 -0
  13. package/dist/core/catalog.js +139 -0
  14. package/dist/core/checks.js +19 -0
  15. package/dist/core/config.js +136 -0
  16. package/dist/core/confirm.js +43 -0
  17. package/dist/core/connection.js +159 -0
  18. package/dist/core/credentials.js +44 -0
  19. package/dist/core/datapolicy.js +175 -0
  20. package/dist/core/diff.js +115 -0
  21. package/dist/core/edits.js +31 -0
  22. package/dist/core/errors.js +68 -0
  23. package/dist/core/feeds.js +60 -0
  24. package/dist/core/notes.js +18 -0
  25. package/dist/core/objects.js +143 -0
  26. package/dist/core/output.js +27 -0
  27. package/dist/core/policy.js +126 -0
  28. package/dist/core/prompts.js +89 -0
  29. package/dist/core/registry.js +316 -0
  30. package/dist/core/revisions.js +97 -0
  31. package/dist/core/sidecar.js +85 -0
  32. package/dist/core/sql.js +54 -0
  33. package/dist/core/telemetry.js +42 -0
  34. package/dist/core/tool.js +5 -0
  35. package/dist/core/transport.js +109 -0
  36. package/dist/index.js +55 -0
  37. package/dist/scripts/audit-verify.js +19 -0
  38. package/dist/scripts/smoke.js +87 -0
  39. package/dist/tools/core/activate.js +27 -0
  40. package/dist/tools/core/api_release_state.js +58 -0
  41. package/dist/tools/core/atc_quickfix.js +159 -0
  42. package/dist/tools/core/co_change.js +73 -0
  43. package/dist/tools/core/create_transport.js +44 -0
  44. package/dist/tools/core/ddic_type_info.js +81 -0
  45. package/dist/tools/core/dumps.js +55 -0
  46. package/dist/tools/core/edit_preflight.js +112 -0
  47. package/dist/tools/core/function_modules.js +75 -0
  48. package/dist/tools/core/gateway_errors.js +45 -0
  49. package/dist/tools/core/get_source.js +70 -0
  50. package/dist/tools/core/inactive_objects.js +29 -0
  51. package/dist/tools/core/object_versions.js +35 -0
  52. package/dist/tools/core/package_contents.js +59 -0
  53. package/dist/tools/core/run_atc.js +117 -0
  54. package/dist/tools/core/run_unit_tests.js +60 -0
  55. package/dist/tools/core/search_objects.js +26 -0
  56. package/dist/tools/core/sql_query.js +25 -0
  57. package/dist/tools/core/support.js +95 -0
  58. package/dist/tools/core/syntax_check.js +35 -0
  59. package/dist/tools/core/table_contents.js +57 -0
  60. package/dist/tools/core/text_elements.js +108 -0
  61. package/dist/tools/core/transaction_info.js +43 -0
  62. package/dist/tools/core/transport_contents.js +49 -0
  63. package/dist/tools/core/transport_diff.js +111 -0
  64. package/dist/tools/core/where_used.js +37 -0
  65. package/dist/tools/core/write_source.js +95 -0
  66. package/dist/tools/docs/docs.js +159 -0
  67. package/dist/tools/local/growth.js +104 -0
  68. package/dist/tools/local/sap_systems.js +65 -0
  69. package/dist/tools/transport-risk/_service.js +63 -0
  70. package/dist/tools/transport-risk/risk_extras.js +90 -0
  71. package/dist/tools/transport-risk/transport_risk.js +42 -0
  72. package/docs/THREAT_MODEL.md +70 -0
  73. package/docs/TOOLS.md +980 -0
  74. package/package.json +68 -0
  75. package/scripts/set-password.sh +31 -0
@@ -0,0 +1,143 @@
1
+ import { ADTClient } from "abap-adt-api";
2
+ import { ToolError } from "./errors.js";
3
+ import { addNote } from "./notes.js";
4
+ /**
5
+ * Tipos cortos que entiende la tool → tipo ADT (TADIR/WB). También se acepta
6
+ * el tipo ADT tal cual (p. ej. "PROG/P").
7
+ */
8
+ export const TYPE_MAP = {
9
+ PROG: "PROG/P",
10
+ INCL: "PROG/I",
11
+ CLAS: "CLAS/OC",
12
+ INTF: "INTF/OI",
13
+ FUGR: "FUGR/F",
14
+ FUNC: "FUGR/FF",
15
+ DDLS: "DDLS/DF",
16
+ DDLX: "DDLX/EX",
17
+ DCLS: "DCLS/DL",
18
+ TABL: "TABL/DT",
19
+ STRU: "TABL/DS",
20
+ VIEW: "VIEW/DV",
21
+ DTEL: "DTEL/DE",
22
+ DOMA: "DOMA/DD",
23
+ TTYP: "TTYP/DA",
24
+ MSAG: "MSAG/N",
25
+ XSLT: "XSLT/VT",
26
+ BDEF: "BDEF/BDO",
27
+ SRVD: "SRVD/SRV",
28
+ SRVB: "SRVB/SVB",
29
+ /** Implementación de ampliación (enhancement): BAdI (ENHO/XHB), plugin de código (ENHO/XHH)… cualquier subtipo. */
30
+ ENHO: "ENHO",
31
+ };
32
+ export const TYPE_HELP = "Tipo corto: " + Object.keys(TYPE_MAP).join(", ") + ". También vale el tipo ADT (p. ej. PROG/P).";
33
+ export function adtType(t) {
34
+ if (!t)
35
+ return undefined;
36
+ const up = t.trim().toUpperCase();
37
+ if (up.includes("/"))
38
+ return up;
39
+ const mapped = TYPE_MAP[up];
40
+ if (!mapped)
41
+ throw new ToolError("INPUT", `Tipo desconocido: ${t}. ${TYPE_HELP}`);
42
+ return mapped;
43
+ }
44
+ const TYPE_LABEL = {
45
+ "PROG/P": "programa",
46
+ "PROG/I": "include",
47
+ "TABL/DT": "tabla",
48
+ "TABL/DS": "estructura",
49
+ "FUGR/F": "grupo de funciones",
50
+ "FUGR/FF": "módulo de función",
51
+ "CLAS/OC": "clase",
52
+ "INTF/OI": "interfaz",
53
+ };
54
+ /**
55
+ * Mismo objeto con otro subtipo: misma familia (4 primeras letras). Los grupos de funciones nunca: un grupo y un
56
+ * módulo son objetos distintos, y confundirlos cambia qué se lee o se escribe.
57
+ */
58
+ export function compatibleType(requested, actual) {
59
+ const fam = (t) => t.slice(0, 4);
60
+ if (fam(requested) !== fam(actual))
61
+ return false;
62
+ if (fam(requested) === "FUGR")
63
+ return requested === actual;
64
+ return true;
65
+ }
66
+ /**
67
+ * Localiza un objeto por nombre exacto (y tipo si se da) vía quickSearch.
68
+ * No adivina: si hay varios tipos con el mismo nombre y no se indicó tipo,
69
+ * devuelve el listado para que se elija.
70
+ */
71
+ export async function resolveObject(c, name, type) {
72
+ const wanted = name.trim().toUpperCase();
73
+ const at = adtType(type);
74
+ let hits = await c.searchObject(wanted, at, 50);
75
+ // Algunos releases no filtran bien por tipo en quickSearch: se reintenta sin filtro.
76
+ if (at && !hits.some((h) => h["adtcore:name"].toUpperCase() === wanted)) {
77
+ hits = await c.searchObject(wanted, undefined, 100);
78
+ }
79
+ const sameName = hits.filter((h) => h["adtcore:name"].toUpperCase() === wanted);
80
+ let exact = at ? sameName.filter((h) => h["adtcore:type"].toUpperCase().startsWith(at)) : sameName;
81
+ if (exact.length === 0 && at && sameName.length) {
82
+ // El nombre existe con otro subtipo de la misma familia (PROG → include PROG/I, TABL → estructura TABL/DS):
83
+ // con UNA sola candidata se resuelve y se deja constancia; con varias, se sigue pidiendo el tipo.
84
+ const compatible = sameName.filter((h) => compatibleType(at, h["adtcore:type"].toUpperCase()));
85
+ if (compatible.length === 1) {
86
+ const t = compatible[0]["adtcore:type"].toUpperCase();
87
+ addNote(`se pidió ${type.toUpperCase()} ${wanted}; en el sistema es ${t}${TYPE_LABEL[t] ? ` (${TYPE_LABEL[t]})` : ""} y se usó ese.`);
88
+ exact = compatible;
89
+ }
90
+ else if (at === "FUGR/FF" && sameName.some((h) => h["adtcore:type"].toUpperCase() === "FUGR/F")) {
91
+ throw new ToolError("NOT_FOUND", `${wanted} es un grupo de funciones (FUGR/F), no un módulo de función.`, `Sus módulos: function_modules(group="${wanted}"). El nombre del módulo suele diferir del del grupo.`);
92
+ }
93
+ }
94
+ if (exact.length === 0) {
95
+ const near = hits.slice(0, 8).map((h) => `${h["adtcore:name"]} (${h["adtcore:type"]})`);
96
+ throw new ToolError("NOT_FOUND", `No existe ${type ? type.toUpperCase() + " " : ""}${wanted}.` + (near.length ? ` Parecidos: ${near.join(", ")}` : ""));
97
+ }
98
+ if (exact.length > 1 && !at) {
99
+ throw new ToolError("INPUT", `${wanted} es ambiguo; indica object_type: ` + exact.map((h) => h["adtcore:type"]).join(", "));
100
+ }
101
+ const h = exact[0];
102
+ return {
103
+ name: h["adtcore:name"],
104
+ type: h["adtcore:type"],
105
+ uri: h["adtcore:uri"],
106
+ packageName: h["adtcore:packageName"],
107
+ description: h["adtcore:description"],
108
+ };
109
+ }
110
+ export const CLASS_INCLUDES = ["main", "definitions", "implementations", "macros", "testclasses"];
111
+ /** URL de la fuente del objeto (o del include de clase pedido). */
112
+ export async function sourceUrl(c, obj, include = "main") {
113
+ if (obj.type.startsWith("CLAS")) {
114
+ const st = (await c.objectStructure(obj.uri));
115
+ const url = ADTClient.classIncludes(st).get(include);
116
+ if (!url) {
117
+ const has = [...ADTClient.classIncludes(st).keys()].join(", ");
118
+ throw new ToolError("NOT_FOUND", `La clase ${obj.name} no tiene include "${include}". Tiene: ${has}`);
119
+ }
120
+ return url;
121
+ }
122
+ if (include !== "main")
123
+ throw new ToolError("INPUT", `"include" solo aplica a clases.`);
124
+ if (ADTClient.isMainInclude(obj.uri))
125
+ return obj.uri;
126
+ try {
127
+ return ADTClient.mainInclude(await c.objectStructure(obj.uri));
128
+ }
129
+ catch {
130
+ return `${obj.uri}/source/main`;
131
+ }
132
+ }
133
+ /** Localiza por nombre exacto aceptando varios prefijos de tipo (p. ej. PROG/ = programa o include). */
134
+ export async function resolveByTypePrefix(c, name, prefixes) {
135
+ const hits = await c.searchObject(name, undefined, 50);
136
+ const h = hits.find((x) => x["adtcore:name"].toUpperCase() === name.toUpperCase() && prefixes.some((t) => x["adtcore:type"].startsWith(t)));
137
+ return h ? { uri: h["adtcore:uri"], type: h["adtcore:type"] } : undefined;
138
+ }
139
+ /** Escapa un literal para ABAP SQL. */
140
+ export function sqlLiteral(v) {
141
+ return `'${v.replace(/'/g, "''")}'`;
142
+ }
143
+ //# sourceMappingURL=objects.js.map
@@ -0,0 +1,27 @@
1
+ /** Tope de caracteres de una respuesta: lo que pase se recorta avisando. */
2
+ export const MAX_CHARS = 80_000;
3
+ /**
4
+ * Recorta por líneas y dice cuánto quedó fuera y cómo pedirlo. Nunca recorta
5
+ * en silencio: un recorte sin aviso se lee como «el objeto termina aquí».
6
+ */
7
+ export function budget(text, max = MAX_CHARS, howToGetMore = "") {
8
+ if (text.length <= max)
9
+ return text;
10
+ const lines = text.split("\n");
11
+ let used = 0;
12
+ let n = 0;
13
+ while (n < lines.length && used + lines[n].length + 1 <= max)
14
+ used += lines[n++].length + 1;
15
+ return (lines.slice(0, n).join("\n") +
16
+ `\n\n[… recortado: se muestran ${n} de ${lines.length} líneas.${howToGetMore ? " " + howToGetMore : ""}]`);
17
+ }
18
+ /** Tabla separada por tabuladores: compacta y fácil de leer para el modelo. */
19
+ export function tsv(columns, rows) {
20
+ const cell = (v) => v === null || v === undefined ? "" : String(v).replace(/[\t\n\r]+/g, " ").trimEnd();
21
+ return [columns.join("\t"), ...rows.map((r) => r.map(cell).join("\t"))].join("\n");
22
+ }
23
+ export function numbered(lines, first = 1) {
24
+ const width = String(first + lines.length - 1).length;
25
+ return lines.map((l, i) => `${String(first + i).padStart(width)}| ${l}`).join("\n");
26
+ }
27
+ //# sourceMappingURL=output.js.map
@@ -0,0 +1,126 @@
1
+ import { canWrite } from "./config.js";
2
+ import { ToolError } from "./errors.js";
3
+ /** Lanza POLICY si la tool no puede correr en ese sistema. */
4
+ export function assertAccess(access, s) {
5
+ if (access === "write" && !canWrite(s)) {
6
+ throw new ToolError("POLICY", s.role !== "DEV"
7
+ ? `${s.id} es ${s.role}: este servidor nunca escribe fuera de desarrollo.`
8
+ : `${s.id} no tiene la escritura habilitada.`, s.role === "DEV" ? `Para habilitarla, pon "allowWrite": true en su entrada de systems.json.` : undefined);
9
+ }
10
+ if (access === "exec" && s.role !== "DEV") {
11
+ throw new ToolError("POLICY", `${s.id} es ${s.role}: solo se ejecuta código en sistemas de desarrollo.`);
12
+ }
13
+ }
14
+ /**
15
+ * La vista previa de datos de ADT solo admite lecturas, pero se valida
16
+ * igualmente para dar un error claro antes de ir a SAP.
17
+ */
18
+ export function assertSelectOnly(sql) {
19
+ const q = sql.trim().replace(/\.$/, "");
20
+ if (!/^(select|with)\b/i.test(q)) {
21
+ throw new ToolError("INPUT", "Solo se admiten consultas SELECT (o WITH).");
22
+ }
23
+ if (q.includes(";")) {
24
+ throw new ToolError("INPUT", "Una sola sentencia, sin «;».");
25
+ }
26
+ return q;
27
+ }
28
+ /**
29
+ * Material de credenciales que el servidor no lee nunca, aunque el usuario SAP
30
+ * tenga autorización: hashes de contraseñas, almacén seguro (donde viven las
31
+ * claves de los destinos RFC), PSE con claves privadas, secretos OAuth, ACL de
32
+ * usuarios y dumps (SNAP guarda valores de variables en memoria). Un agente no
33
+ * lo necesita, y una respuesta de tool puede acabar en un log, un ticket o un
34
+ * proveedor de LLM. La configuración sin secretos (RFCDES, RFCSYSACL, AGR_*)
35
+ * sí se puede leer: la usa el análisis de Basis.
36
+ */
37
+ export const SENSITIVE_TABLES = [
38
+ "USR02", "USH02", "USRPWDHISTORY", "USH02_ARC_TMP",
39
+ "RSECTAB", "RSECACTB",
40
+ "SSF_PSE_D", "SSF_PSE_H", "SSF_PSE_L",
41
+ "USRACL", "SNAP",
42
+ ];
43
+ export const SENSITIVE_PREFIXES = ["OA2C_"];
44
+ export const SENSITIVE_COLUMNS = ["PWDSALTEDHASH", "BCODE", "PASSCODE"];
45
+ /** Datos de personal (infotipos, clusters de nómina): categoría especial, nunca por este servidor. */
46
+ export const HR_TABLE_RE = /^(PA\d{4}|PB\d{4}|PCL[1-5]|HRPY_[A-Z0-9_]+)$/;
47
+ /** Qué palabras de una consulta (o de una definición de vista) son material vetado. */
48
+ export function sensitiveHits(words) {
49
+ const secrets = new Set();
50
+ const hr = new Set();
51
+ for (const raw of words) {
52
+ const w = raw.toUpperCase();
53
+ if (SENSITIVE_TABLES.includes(w) || SENSITIVE_COLUMNS.includes(w) || SENSITIVE_PREFIXES.some((p) => w.startsWith(p)))
54
+ secrets.add(w);
55
+ else if (HR_TABLE_RE.test(w))
56
+ hr.add(w);
57
+ }
58
+ return { secrets: [...secrets], hr: [...hr] };
59
+ }
60
+ export function sqlWords(sql) {
61
+ return sql.replace(/'(?:[^']|'')*'/g, " ").toUpperCase().match(/[A-Z0-9_/]+/g) ?? [];
62
+ }
63
+ export function rejectSensitive(hits, via) {
64
+ const how = via ? ` a través de ${via}` : "";
65
+ if (hits.secrets.length) {
66
+ throw new ToolError("POLICY", `Consulta bloqueada: toca datos de seguridad${how} (${hits.secrets.join(", ")}). Este servidor no lee credenciales, ` +
67
+ `hashes, almacén seguro, claves privadas ni dumps.`, "Si necesitas el estado de un usuario (bloqueo, validez), usa SU01 o pide la columna concreta a Basis; para dumps, la tool dumps.");
68
+ }
69
+ if (hits.hr.length) {
70
+ throw new ToolError("POLICY", `Consulta bloqueada: datos de personal${how} (${hits.hr.join(", ")}). Este servidor no lee infotipos ni nómina.`, "Para la estructura, ddic_type_info o get_source sobre la tabla (definición, no datos).");
71
+ }
72
+ }
73
+ export function assertNotSensitive(sql) {
74
+ rejectSensitive(sensitiveHits(sqlWords(sql)));
75
+ }
76
+ const GENERIC_LABELS = new Set(["com", "net", "org", "sap", "corp", "local", "cloud", "hana", "ondemand", "intra", "internal", "prod", "dev"]);
77
+ /** Términos que identifican a clientes o sistemas, deducidos de la configuración. */
78
+ export function sensitiveTerms(cfg, extra = []) {
79
+ const terms = new Set(extra.map((t) => t.toLowerCase()).filter((t) => t.length >= 3));
80
+ for (const s of cfg.systems) {
81
+ terms.add(s.id.toLowerCase());
82
+ if (s.sid)
83
+ terms.add(s.sid.toLowerCase());
84
+ terms.add(s.user.toLowerCase());
85
+ try {
86
+ const labels = new URL(s.url).hostname.split(".").slice(1, -1);
87
+ for (const l of labels)
88
+ if (l.length >= 4 && !GENERIC_LABELS.has(l.toLowerCase()))
89
+ terms.add(l.toLowerCase());
90
+ }
91
+ catch {
92
+ /* URL ya validada por el esquema */
93
+ }
94
+ }
95
+ return [...terms];
96
+ }
97
+ /** Tokens que parecen objetos de cliente, namespaces u órdenes de transporte. */
98
+ export function customTokens(query) {
99
+ return (query.match(/[A-Za-z0-9_/]+/g) ?? []).filter((t) => /^[zy][a-z0-9]*_[a-z0-9_]*$/i.test(t) || // ZDEMO_REPORT_01, zcl_x
100
+ /^[zy][a-z]{1,4}\d{2,}[a-z0-9_]*$/i.test(t) || // ZFI001, ZMM012F01
101
+ /^\/[a-z0-9]{2,}\/\w+/i.test(t) || // /NSP/ALGO
102
+ /^[a-z0-9]{3}k\d{6}$/i.test(t));
103
+ }
104
+ /**
105
+ * Última barrera antes de que una consulta salga a internet (búsqueda online
106
+ * de documentación): sin objetos de cliente, sistemas, usuarios ni nombres de
107
+ * cliente. No sustituye el criterio del usuario: lo respalda.
108
+ */
109
+ export function assertPublicQuery(query, cfg, blockTerms = []) {
110
+ const lower = query.toLowerCase();
111
+ const hits = [
112
+ ...customTokens(query),
113
+ ...sensitiveTerms(cfg, blockTerms).filter((t) => new RegExp(`(^|[^a-z0-9])${t.replace(/[.*+?^${}()|[\]\\/]/g, "\\$&")}($|[^a-z0-9])`).test(lower)),
114
+ ];
115
+ if (hits.length) {
116
+ throw new ToolError("POLICY", `La consulta saldría a internet con datos del cliente o del sistema (${[...new Set(hits)].join(", ")}).`, "Reformúlala con el concepto técnico (p. ej. «MATNR length extension BAPI»), sin nombres Z, órdenes, sistemas ni clientes; o búscala sin online.");
117
+ }
118
+ }
119
+ export const TRKORR_RE = /^[A-Z0-9]{3}K\d{6}$/;
120
+ export function assertTrkorr(t, label = "transport") {
121
+ const up = t.trim().toUpperCase();
122
+ if (!TRKORR_RE.test(up))
123
+ throw new ToolError("INPUT", `${label}: "${t}" no es un número de orden (p. ej. DEVK900123).`);
124
+ return up;
125
+ }
126
+ //# sourceMappingURL=policy.js.map
@@ -0,0 +1,89 @@
1
+ import { z } from "zod";
2
+ /** Metadatos de los flujos: los usan el registro y la documentación generada. */
3
+ export const PROMPT_META = {
4
+ revisar_pase: {
5
+ title: "Revisar una orden antes del pase",
6
+ description: "Revisión completa de una orden: código, compañeros ausentes, bloqueos y, con el módulo de riesgo, su análisis.",
7
+ args: ["system", "transport"],
8
+ chain: "transport_contents → transport_diff → inactive_objects → co_change + edit_preflight → analyze_transport_risk (si hay módulo) → informe en tres capas: negocio, consultor, Basis",
9
+ },
10
+ remediar_atc: {
11
+ title: "Remediar hallazgos ATC de un objeto",
12
+ description: "ATC → documentación y nota SAP → sucesor liberado → corrección → sintaxis → guardado con orden.",
13
+ args: ["system", "object_name", "object_type?", "transport?"],
14
+ chain: "edit_preflight → run_atc (BEFORE) → explain + api_release_state + where_used/object_versions → clasificación CAMBIAR/INVESTIGAR/NO_CAMBIAR → atc_quickfix → syntax_check → write_source con la orden (con OK humano) → run_atc (AFTER) y reducción de P1",
15
+ },
16
+ diagnosticar_ticket: {
17
+ title: "Diagnosticar un incidente",
18
+ description: "Dumps, jobs, log de aplicación y errores de Gateway alrededor de un incidente.",
19
+ args: ["system", "hint", "date?"],
20
+ chain: "dumps → jobs → application_log → gateway_errors → transaction_info / get_source / object_versions / transport_contents → causa probable con evidencia y lo que no se pudo comprobar",
21
+ },
22
+ };
23
+ /**
24
+ * Flujos guiados (prompts MCP): aparecen como comandos en el cliente
25
+ * (/mcp__abap-adt-doZimple__revisar_pase en Claude Code). Encadenan tools con
26
+ * las reglas de trabajo ya aprendidas; no ejecutan nada por sí mismos.
27
+ */
28
+ export function registerPrompts(server, config) {
29
+ const ids = config.systems.map((s) => s.id).join(", ");
30
+ const sys = z.string().describe(`Sistema: ${ids}`);
31
+ const msg = (text) => ({ messages: [{ role: "user", content: { type: "text", text } }] });
32
+ server.registerPrompt("revisar_pase", {
33
+ title: PROMPT_META.revisar_pase.title,
34
+ description: PROMPT_META.revisar_pase.description,
35
+ argsSchema: { system: sys, transport: z.string().describe("Orden, p. ej. DEVK900123") },
36
+ }, ({ system, transport }) => msg(`Revisa la orden ${transport} en ${system} antes de su pase. Solo lectura: no liberes ni modifiques nada.\n\n` +
37
+ `1. transport_contents: cabecera, tareas abiertas y dueños.\n` +
38
+ `2. transport_diff (summary_only primero; luego el diff de los objetos con más cambios): qué cambia de verdad. ` +
39
+ `Señala código comentado de más, lógica de negocio tocada fuera del alcance, y cambios SIN ACTIVAR.\n` +
40
+ `3. inactive_objects: lo que quede sin activar no viaja.\n` +
41
+ `4. Para los 3 objetos principales: co_change con transport=${transport} (compañeros habituales ausentes) y ` +
42
+ `edit_preflight (bloqueos TLOCK ajenos, reparaciones).\n` +
43
+ `5. Si el sistema tiene el módulo dz-transport-risk: analyze_transport_risk (pregunta el destino si no está claro) ` +
44
+ `y sigue el steering analisis-riesgo-transporte para redactar.\n\n` +
45
+ `Informe en español, en tres capas: veredicto de negocio (sin ABAP), qué revisar (objeto, motivo, línea) y ` +
46
+ `acciones de pase (tareas por liberar, órdenes a coordinar). No afirmes nada que no hayas visto en una tool.`));
47
+ server.registerPrompt("remediar_atc", {
48
+ title: PROMPT_META.remediar_atc.title,
49
+ description: PROMPT_META.remediar_atc.description,
50
+ argsSchema: {
51
+ system: sys,
52
+ object_name: z.string(),
53
+ object_type: z.string().optional(),
54
+ transport: z.string().optional().describe("Orden del ticket"),
55
+ },
56
+ }, ({ system, object_name, object_type, transport }) => msg(`Remedia los hallazgos ATC de ${object_name}${object_type ? ` (${object_type})` : ""} en ${system}` +
57
+ `${transport ? `, en la orden ${transport}` : ""}.\n\n` +
58
+ `Reglas: cambio mínimo, nunca lógica de negocio; primer criterio la nota SAP, ` +
59
+ `luego los precedentes del proyecto; supresión solo como último recurso y justificada.\n\n` +
60
+ `1. edit_preflight: si hay bloqueo ajeno o es reparación, PARA y avísame antes de nada.\n` +
61
+ `2. run_atc (priorities [1,2] primero). Guarda el conteo P1/P2/P3 como BEFORE.\n` +
62
+ `3. Por cada hallazgo: run_atc(explain=N) para la documentación y la nota; si menciona un objeto obsoleto, ` +
63
+ `api_release_state para su sucesor; where_used y object_versions para precedentes.\n` +
64
+ `4. Clasifica CAMBIAR / INVESTIGAR / NO_CAMBIAR y enséñame la tabla antes de tocar código.\n` +
65
+ `5. Para los CAMBIAR: atc_quickfix(finding=N) si SAP ofrece corrección; si no, prepara el cambio a mano. ` +
66
+ `Siempre syntax_check con la fuente completa propuesta.\n` +
67
+ `6. Guardar solo con mi OK: write_source con transport explícito (o abap-fs si la escritura no está habilitada). ` +
68
+ `Si una corrección crea símbolos de texto, write_text_elements.\n` +
69
+ `7. run_atc de nuevo como AFTER y calcula la reducción de P1.`));
70
+ server.registerPrompt("diagnosticar_ticket", {
71
+ title: PROMPT_META.diagnosticar_ticket.title,
72
+ description: PROMPT_META.diagnosticar_ticket.description,
73
+ argsSchema: {
74
+ system: sys,
75
+ hint: z.string().describe("Qué falló: programa, transacción, job, usuario, servicio OData o nº de documento"),
76
+ date: z.string().optional().describe("AAAA-MM-DD del incidente"),
77
+ },
78
+ }, ({ system, hint, date }) => msg(`Diagnostica el incidente «${hint}» en ${system}${date ? ` del ${date}` : ""}. Solo lectura.\n\n` +
79
+ `1. dumps (filtra por usuario o por el programa/error con contains) y dumps(detail=N) del más relevante.\n` +
80
+ `2. Si es un proceso de fondo: jobs (nombre con *, from_date) — estado y pasos.\n` +
81
+ `3. application_log (objeto o nº externo) para errores registrados.\n` +
82
+ `4. Si es una app Fiori/OData: gateway_errors y su detalle.\n` +
83
+ `5. Si hay transacción: transaction_info → get_source del punto que falla; object_versions para ver si cambió ` +
84
+ `hace poco y transport_contents de esa orden.\n\n` +
85
+ `Cierra con: causa probable (con la evidencia de cada tool), qué no se pudo comprobar, y siguiente paso. ` +
86
+ `Si algo solo se ve en SAP GUI (log de job, textos de SLG1), dilo y llama a report_gap.`));
87
+ return ["revisar_pase", "remediar_atc", "diagnosticar_ticket"];
88
+ }
89
+ //# sourceMappingURL=prompts.js.map