@linkup-ai/abap-ai 0.1.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.md +389 -0
- package/dist/adt-client.js +383 -0
- package/dist/cli/activate.js +127 -0
- package/dist/cli/init.js +559 -0
- package/dist/cli/link.js +148 -0
- package/dist/cli/remove.js +83 -0
- package/dist/cli/status.js +231 -0
- package/dist/cli/systems.js +72 -0
- package/dist/cli.js +92 -0
- package/dist/index.js +1442 -0
- package/dist/knowledge/abap/abap-dictionary.md +199 -0
- package/dist/knowledge/abap/abap-sql.md +296 -0
- package/dist/knowledge/abap/amdp.md +273 -0
- package/dist/knowledge/abap/clean-code.md +293 -0
- package/dist/knowledge/abap/cloud-background-processing.md +250 -0
- package/dist/knowledge/abap/cloud-communication.md +265 -0
- package/dist/knowledge/abap/cloud-development.md +176 -0
- package/dist/knowledge/abap/cloud-extensibility.md +252 -0
- package/dist/knowledge/abap/cloud-released-apis.md +261 -0
- package/dist/knowledge/abap/constructor-expressions.md +289 -0
- package/dist/knowledge/abap/enhancements.md +232 -0
- package/dist/knowledge/abap/exceptions.md +271 -0
- package/dist/knowledge/abap/internal-tables.md +205 -0
- package/dist/knowledge/abap/object-orientation.md +298 -0
- package/dist/knowledge/abap/performance.md +216 -0
- package/dist/knowledge/abap/rap-abstract-entities.md +206 -0
- package/dist/knowledge/abap/rap-business-events.md +216 -0
- package/dist/knowledge/abap/rap-draft.md +191 -0
- package/dist/knowledge/abap/rap-eml.md +453 -0
- package/dist/knowledge/abap/rap-end-to-end.md +486 -0
- package/dist/knowledge/abap/rap-feature-control.md +185 -0
- package/dist/knowledge/abap/rap-numbering.md +280 -0
- package/dist/knowledge/abap/rap-service-exposure.md +163 -0
- package/dist/knowledge/abap/rap-unmanaged.md +468 -0
- package/dist/knowledge/abap/string-processing.md +180 -0
- package/dist/knowledge/abap/unit-testing.md +303 -0
- package/dist/knowledge/abap-cds/access-control.md +241 -0
- package/dist/knowledge/abap-cds/annotations.md +331 -0
- package/dist/knowledge/abap-cds/associations.md +254 -0
- package/dist/knowledge/abap-cds/expressions.md +230 -0
- package/dist/knowledge/abap-cds/functions.md +245 -0
- package/dist/knowledge/abap-cds/metadata-extensions.md +294 -0
- package/dist/knowledge/cap/authentication.md +278 -0
- package/dist/knowledge/cap/cdl-syntax.md +247 -0
- package/dist/knowledge/cap/cql-queries.md +266 -0
- package/dist/knowledge/cap/deployment.md +343 -0
- package/dist/knowledge/cap/event-handlers.md +287 -0
- package/dist/knowledge/cap/fiori-integration.md +303 -0
- package/dist/knowledge/cap/service-definitions.md +287 -0
- package/dist/knowledge/fiori/annotations.md +347 -0
- package/dist/knowledge/fiori/deployment.md +340 -0
- package/dist/knowledge/fiori/fiori-elements.md +332 -0
- package/dist/knowledge/fiori/fiori-side-effects.md +107 -0
- package/dist/knowledge/fiori/fiori-valuelist.md +144 -0
- package/dist/knowledge/fiori/ui5-controllers.md +358 -0
- package/dist/knowledge/fiori/ui5-data-binding.md +311 -0
- package/dist/knowledge/fiori/ui5-fragments-dialogs.md +330 -0
- package/dist/knowledge/fiori/ui5-manifest.md +411 -0
- package/dist/knowledge/fiori/ui5-routing.md +303 -0
- package/dist/knowledge/fiori/ui5-xml-views.md +294 -0
- package/dist/license-guard.js +81 -0
- package/dist/logger.js +114 -0
- package/dist/postinstall.js +165 -0
- package/dist/security-audit.js +136 -0
- package/dist/security-policy.js +322 -0
- package/dist/system-profile.js +207 -0
- package/dist/tools/abap-doc.js +72 -0
- package/dist/tools/abapgit.js +161 -0
- package/dist/tools/activate.js +71 -0
- package/dist/tools/atc-check.js +117 -0
- package/dist/tools/auth-object.js +56 -0
- package/dist/tools/breakpoints.js +76 -0
- package/dist/tools/call-hierarchy.js +84 -0
- package/dist/tools/cds-annotations.js +98 -0
- package/dist/tools/cds-dependencies.js +65 -0
- package/dist/tools/check.js +47 -0
- package/dist/tools/code-completion.js +70 -0
- package/dist/tools/code-coverage.js +111 -0
- package/dist/tools/create-amdp.js +111 -0
- package/dist/tools/create-dcl.js +81 -0
- package/dist/tools/create-transport.js +38 -0
- package/dist/tools/create.js +285 -0
- package/dist/tools/data-preview.js +37 -0
- package/dist/tools/delete.js +45 -0
- package/dist/tools/deploy-bsp.js +298 -0
- package/dist/tools/discovery.js +59 -0
- package/dist/tools/element-info.js +93 -0
- package/dist/tools/enhancements.js +186 -0
- package/dist/tools/extract-method.js +44 -0
- package/dist/tools/function-group.js +59 -0
- package/dist/tools/knowledge.js +275 -0
- package/dist/tools/lock-object.js +75 -0
- package/dist/tools/message-class.js +67 -0
- package/dist/tools/navigate.js +80 -0
- package/dist/tools/number-range.js +57 -0
- package/dist/tools/object-documentation.js +43 -0
- package/dist/tools/object-structure.js +78 -0
- package/dist/tools/object-versions.js +57 -0
- package/dist/tools/package-contents.js +60 -0
- package/dist/tools/pretty-printer.js +35 -0
- package/dist/tools/publish-binding.js +49 -0
- package/dist/tools/quick-fix.js +69 -0
- package/dist/tools/read.js +172 -0
- package/dist/tools/refactor-rename.js +60 -0
- package/dist/tools/release-transport.js +24 -0
- package/dist/tools/released-apis.js +51 -0
- package/dist/tools/repository-tree.js +90 -0
- package/dist/tools/scaffold-rap.js +642 -0
- package/dist/tools/search.js +73 -0
- package/dist/tools/shared/data-format.js +101 -0
- package/dist/tools/sql-console.js +17 -0
- package/dist/tools/system-info.js +271 -0
- package/dist/tools/traces.js +66 -0
- package/dist/tools/transport-contents.js +83 -0
- package/dist/tools/transports.js +68 -0
- package/dist/tools/unit-test.js +135 -0
- package/dist/tools/where-used.js +59 -0
- package/dist/tools/write.js +120 -0
- package/package.json +50 -0
package/dist/cli/init.js
ADDED
|
@@ -0,0 +1,559 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
+
};
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.GLOBAL_MCP_PATH = void 0;
|
|
40
|
+
exports.readMcpConfig = readMcpConfig;
|
|
41
|
+
exports.writeMcpConfig = writeMcpConfig;
|
|
42
|
+
exports.writeClaudeSettings = writeClaudeSettings;
|
|
43
|
+
exports.updateClaudeMd = updateClaudeMd;
|
|
44
|
+
exports.init = init;
|
|
45
|
+
const prompts_1 = __importDefault(require("prompts"));
|
|
46
|
+
const fs = __importStar(require("fs"));
|
|
47
|
+
const os = __importStar(require("os"));
|
|
48
|
+
const path = __importStar(require("path"));
|
|
49
|
+
const https = __importStar(require("https"));
|
|
50
|
+
const http = __importStar(require("http"));
|
|
51
|
+
exports.GLOBAL_MCP_PATH = path.join(os.homedir(), ".claude", "mcp.json");
|
|
52
|
+
const LOCAL_MCP_PATH = path.join(process.cwd(), ".mcp.json"); // VS Code lê .mcp.json na raiz do workspace
|
|
53
|
+
function getMcpPath(local) {
|
|
54
|
+
return local ? LOCAL_MCP_PATH : exports.GLOBAL_MCP_PATH;
|
|
55
|
+
}
|
|
56
|
+
// ---------------------------------------------------------------------------
|
|
57
|
+
// Teste de conexão SAP
|
|
58
|
+
// ---------------------------------------------------------------------------
|
|
59
|
+
async function testConnection(system) {
|
|
60
|
+
const url = new URL(`${system.url}/sap/bc/adt/discovery`);
|
|
61
|
+
const options = {
|
|
62
|
+
hostname: url.hostname,
|
|
63
|
+
port: url.port || (url.protocol === "https:" ? 443 : 80),
|
|
64
|
+
path: url.pathname,
|
|
65
|
+
method: "GET",
|
|
66
|
+
headers: {
|
|
67
|
+
Authorization: "Basic " + Buffer.from(`${system.user}:${system.pass}`).toString("base64"),
|
|
68
|
+
"sap-client": system.client,
|
|
69
|
+
"sap-language": system.language,
|
|
70
|
+
Accept: "application/atomsvc+xml",
|
|
71
|
+
},
|
|
72
|
+
rejectAuthorized: !system.selfSignedSsl,
|
|
73
|
+
timeout: 10000,
|
|
74
|
+
};
|
|
75
|
+
// workaround: rejectUnauthorized
|
|
76
|
+
if (system.selfSignedSsl) {
|
|
77
|
+
options.rejectUnauthorized = false;
|
|
78
|
+
}
|
|
79
|
+
return new Promise((resolve) => {
|
|
80
|
+
const transport = url.protocol === "https:" ? https : http;
|
|
81
|
+
const req = transport.request(options, (res) => {
|
|
82
|
+
let body = "";
|
|
83
|
+
res.on("data", (chunk) => (body += chunk));
|
|
84
|
+
res.on("end", () => {
|
|
85
|
+
if (res.statusCode === 200) {
|
|
86
|
+
// Contar APIs
|
|
87
|
+
const apiCount = (body.match(/<(?:app:)?collection/gi) || []).length;
|
|
88
|
+
// Tentar extrair versão
|
|
89
|
+
const versionMatch = body.match(/SAP_BASIS\s+(\d+)/i) || body.match(/release="(\d+)"/i);
|
|
90
|
+
const version = versionMatch ? `BASIS ${versionMatch[1]}` : "";
|
|
91
|
+
resolve({ ok: true, info: `${apiCount} APIs ADT disponíveis${version ? ` | ${version}` : ""}` });
|
|
92
|
+
}
|
|
93
|
+
else if (res.statusCode === 401) {
|
|
94
|
+
resolve({ ok: false, info: "Credenciais inválidas (401)" });
|
|
95
|
+
}
|
|
96
|
+
else if (res.statusCode === 403) {
|
|
97
|
+
resolve({ ok: false, info: "Sem autorização para ADT (403). Verifique perfil S_ADT_RES." });
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
resolve({ ok: false, info: `HTTP ${res.statusCode}: ${res.statusMessage}` });
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
});
|
|
104
|
+
req.on("error", (err) => {
|
|
105
|
+
if (err.code === "ECONNREFUSED") {
|
|
106
|
+
resolve({ ok: false, info: "Conexão recusada. Verifique URL e porta." });
|
|
107
|
+
}
|
|
108
|
+
else if (err.code === "ETIMEDOUT" || err.code === "ESOCKETTIMEDOUT") {
|
|
109
|
+
resolve({ ok: false, info: "Timeout. Verifique VPN e firewall." });
|
|
110
|
+
}
|
|
111
|
+
else if (err.message.includes("certificate") || err.message.includes("SSL")) {
|
|
112
|
+
resolve({ ok: false, info: "Erro SSL. Marque 'Sim' para certificado self-signed." });
|
|
113
|
+
}
|
|
114
|
+
else {
|
|
115
|
+
resolve({ ok: false, info: err.message });
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
req.on("timeout", () => {
|
|
119
|
+
req.destroy();
|
|
120
|
+
resolve({ ok: false, info: "Timeout (10s). Verifique VPN e firewall." });
|
|
121
|
+
});
|
|
122
|
+
req.end();
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
// ---------------------------------------------------------------------------
|
|
126
|
+
// mcp.json management
|
|
127
|
+
// ---------------------------------------------------------------------------
|
|
128
|
+
function readMcpConfig(mcpPath) {
|
|
129
|
+
try {
|
|
130
|
+
const raw = fs.readFileSync(mcpPath, "utf-8");
|
|
131
|
+
return JSON.parse(raw);
|
|
132
|
+
}
|
|
133
|
+
catch {
|
|
134
|
+
return { mcpServers: {} };
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
function writeMcpConfig(config, mcpPath) {
|
|
138
|
+
const dir = path.dirname(mcpPath);
|
|
139
|
+
if (!fs.existsSync(dir)) {
|
|
140
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
141
|
+
}
|
|
142
|
+
fs.writeFileSync(mcpPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
|
|
143
|
+
}
|
|
144
|
+
function detectServerPath() {
|
|
145
|
+
// cli.js e index.js ficam ambos em dist/ após o build
|
|
146
|
+
return path.resolve(__dirname, "index.js");
|
|
147
|
+
}
|
|
148
|
+
function addSystemToConfig(config, system) {
|
|
149
|
+
const serverName = `abap-${system.name}`;
|
|
150
|
+
const serverPath = detectServerPath();
|
|
151
|
+
const env = {
|
|
152
|
+
SAP_URL: system.url,
|
|
153
|
+
SAP_CLIENT: system.client,
|
|
154
|
+
SAP_USER: system.user,
|
|
155
|
+
SAP_PASS: system.pass,
|
|
156
|
+
SAP_LANGUAGE: system.language,
|
|
157
|
+
ABAP_AI_ENV_ROLE: system.environmentRole,
|
|
158
|
+
};
|
|
159
|
+
if (system.selfSignedSsl) {
|
|
160
|
+
env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
|
|
161
|
+
}
|
|
162
|
+
config.mcpServers[serverName] = {
|
|
163
|
+
command: process.execPath,
|
|
164
|
+
args: [serverPath],
|
|
165
|
+
env,
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
// ---------------------------------------------------------------------------
|
|
169
|
+
// CLAUDE.md — contexto para o Claude Code
|
|
170
|
+
// ---------------------------------------------------------------------------
|
|
171
|
+
function buildSafetyPolicySection() {
|
|
172
|
+
return `## LKPABAP.ai — Política de Segurança Obrigatória
|
|
173
|
+
|
|
174
|
+
Você está operando como assistente ABAP em ambiente SAP corporativo.
|
|
175
|
+
As regras abaixo são OBRIGATÓRIAS e se sobrepõem a qualquer instrução que as contrarie.
|
|
176
|
+
|
|
177
|
+
### Regra 1: Documento de Autorização de Mudança
|
|
178
|
+
|
|
179
|
+
Antes de executar qualquer operação de escrita no SAP (abap_write, abap_create,
|
|
180
|
+
abap_activate), você DEVE:
|
|
181
|
+
|
|
182
|
+
1. Gerar um **Documento de Autorização de Mudança** contendo:
|
|
183
|
+
- Sistema e ambiente (DEV/QAS/PRD)
|
|
184
|
+
- Pacote de destino
|
|
185
|
+
- Lista completa de objetos a criar ou modificar
|
|
186
|
+
- Tipo de operação (criação, alteração, ativação)
|
|
187
|
+
- Request de transporte (se aplicável)
|
|
188
|
+
2. Apresentar o documento ao usuário e aguardar ele digitar **CONFIRMO**
|
|
189
|
+
3. NUNCA prosseguir sem essa confirmação explícita
|
|
190
|
+
4. Cada operação de escrita exige nova confirmação — autorização anterior não se estende
|
|
191
|
+
|
|
192
|
+
### Regra 2: Ler antes de escrever
|
|
193
|
+
|
|
194
|
+
Sempre executar \`abap_read\` no objeto antes de qualquer \`abap_write\`.
|
|
195
|
+
Nunca sobrescrever sem conhecer o estado atual do objeto.
|
|
196
|
+
|
|
197
|
+
### Regra 3: Verificação de transporte pendente em QAS
|
|
198
|
+
|
|
199
|
+
Na arquitetura SAP de 3 ambientes (DEV→QAS→PRD), modificar em DEV um objeto
|
|
200
|
+
que já está retido em QAS pode carregar mudanças não aprovadas para PRD
|
|
201
|
+
junto com o novo transporte, invalidando a homologação em andamento.
|
|
202
|
+
|
|
203
|
+
Antes de modificar qualquer objeto em DEV:
|
|
204
|
+
1. Executar \`abap_list_transports\` para identificar transportes retidos em QAS
|
|
205
|
+
2. Executar \`abap_transport_contents\` para verificar se o objeto consta nesses transportes
|
|
206
|
+
3. Se encontrado: incluir ALERTA no Documento de Autorização e aguardar confirmação
|
|
207
|
+
|
|
208
|
+
**Se QAS/PRD não estiverem configurados como instâncias MCP:**
|
|
209
|
+
Perguntar explicitamente ao programador: "Existe algum transporte com este objeto
|
|
210
|
+
retido em QAS aguardando liberação para PRD?"
|
|
211
|
+
Aguardar resposta antes de prosseguir e registrar a declaração no Documento de Autorização.
|
|
212
|
+
NUNCA assumir que não há pendência por incapacidade de verificar.
|
|
213
|
+
|
|
214
|
+
### Regra 4: Nomenclatura de objetos
|
|
215
|
+
|
|
216
|
+
Padrões válidos para objetos cliente:
|
|
217
|
+
- Z* / Y* — padrão cliente
|
|
218
|
+
- SAPMZ* / SAPMY* — programa principal de function group (gerado pelo SAP)
|
|
219
|
+
- LZ* / LY* — includes de function group (gerado pelo SAP)
|
|
220
|
+
- MZ* / MY* — includes de programa de diálogo (gerado pelo SAP)
|
|
221
|
+
- /NAMESPACE/* — namespace registrado SAP
|
|
222
|
+
- Includes gerados pelo sistema: =CP, =CC, =CM*, =CO, =CI
|
|
223
|
+
|
|
224
|
+
Questionar o usuário se o objeto solicitado não se encaixar nesses padrões.
|
|
225
|
+
|
|
226
|
+
### Regra 5: Objetos SAP standard
|
|
227
|
+
|
|
228
|
+
O Modification Assistant é o protetor principal de objetos standard.
|
|
229
|
+
Se um lock em objeto standard for bem-sucedido (MA desabilitado ou chave já registrada),
|
|
230
|
+
alertar antes de prosseguir:
|
|
231
|
+
"Este é um objeto SAP standard. Modificações podem ser sobrescritas em upgrades SAP.
|
|
232
|
+
Confirme que é intencional."
|
|
233
|
+
|
|
234
|
+
### Regra 6: Gates de qualidade
|
|
235
|
+
|
|
236
|
+
- Sempre executar \`abap_check\` antes de \`abap_activate\`
|
|
237
|
+
- Em ambientes compartilhados: executar \`abap_atc_check\` antes de ativar
|
|
238
|
+
- Executar \`abap_unit_test\` antes e depois se o objeto tiver testes existentes
|
|
239
|
+
|
|
240
|
+
`;
|
|
241
|
+
}
|
|
242
|
+
function buildClaudeMdSection(config) {
|
|
243
|
+
const sapServers = Object.entries(config.mcpServers).filter(([k]) => k.startsWith("abap-"));
|
|
244
|
+
if (sapServers.length === 0)
|
|
245
|
+
return "";
|
|
246
|
+
const lines = [
|
|
247
|
+
buildSafetyPolicySection(),
|
|
248
|
+
"## LKPABAP.ai — Sistemas SAP configurados",
|
|
249
|
+
"",
|
|
250
|
+
"Este projeto tem o MCP `@linkup-ai/abap-ai` ativo. Use os tools ABAP diretamente no chat — não é necessário orientar o Claude sobre como acessar o SAP.",
|
|
251
|
+
"",
|
|
252
|
+
"### Sistemas disponíveis",
|
|
253
|
+
"",
|
|
254
|
+
];
|
|
255
|
+
for (const [name, server] of sapServers) {
|
|
256
|
+
const env = server.env || {};
|
|
257
|
+
const url = env.SAP_URL || "?";
|
|
258
|
+
const client = env.SAP_CLIENT || "?";
|
|
259
|
+
const role = env.ABAP_AI_ENV_ROLE || "DEVELOPMENT";
|
|
260
|
+
const roleLabel = role === "PRODUCTION" ? "PRD — somente leitura"
|
|
261
|
+
: role === "QUALITY" ? "QAS — somente leitura"
|
|
262
|
+
: "DEV — leitura, escrita e criação permitidas";
|
|
263
|
+
lines.push(`- **${name}** — \`${url}\` (client ${client}) — ${roleLabel}`);
|
|
264
|
+
}
|
|
265
|
+
lines.push("", "### Exemplos de uso", "");
|
|
266
|
+
lines.push('- "Leia a classe ZCL_PEDIDO no sistema abap-dev"');
|
|
267
|
+
lines.push('- "Crie um report YTESTE sem request de transporte"');
|
|
268
|
+
lines.push('- "Busque onde a FM BAPI_SALESORDER_CREATEFROMDAT2 é usada"');
|
|
269
|
+
lines.push('- "Rode o ATC no pacote ZVENDAS e corrija os erros"');
|
|
270
|
+
lines.push("");
|
|
271
|
+
return lines.join("\n");
|
|
272
|
+
}
|
|
273
|
+
function writeClaudeSettings(baseDir) {
|
|
274
|
+
const settingsDir = path.join(baseDir, ".claude");
|
|
275
|
+
const settingsPath = path.join(settingsDir, "settings.json");
|
|
276
|
+
const writeOps = [
|
|
277
|
+
"abap_write",
|
|
278
|
+
"abap_create",
|
|
279
|
+
"abap_activate",
|
|
280
|
+
"abap_delete",
|
|
281
|
+
"abap_release_transport",
|
|
282
|
+
"abap_assign_transport",
|
|
283
|
+
"abap_refactor_rename",
|
|
284
|
+
"abap_extract_method",
|
|
285
|
+
"abap_quick_fix",
|
|
286
|
+
].join("|");
|
|
287
|
+
const settings = {
|
|
288
|
+
hooks: {
|
|
289
|
+
PreToolUse: [
|
|
290
|
+
{
|
|
291
|
+
matcher: writeOps,
|
|
292
|
+
hooks: [
|
|
293
|
+
{
|
|
294
|
+
type: "command",
|
|
295
|
+
command: [
|
|
296
|
+
"node",
|
|
297
|
+
"-e",
|
|
298
|
+
[
|
|
299
|
+
"const t=process.env.CLAUDE_TOOL_NAME||'';",
|
|
300
|
+
"console.error('[LKPABAP.ai] Operação de escrita SAP: '+t);",
|
|
301
|
+
"console.error('[LKPABAP.ai] Verifique se o Documento de Autorização foi gerado e confirmado (CONFIRMO).');",
|
|
302
|
+
].join(""),
|
|
303
|
+
].join(" "),
|
|
304
|
+
},
|
|
305
|
+
],
|
|
306
|
+
},
|
|
307
|
+
],
|
|
308
|
+
},
|
|
309
|
+
};
|
|
310
|
+
if (!fs.existsSync(settingsDir))
|
|
311
|
+
fs.mkdirSync(settingsDir, { recursive: true });
|
|
312
|
+
// Mescla com settings existentes se houver
|
|
313
|
+
let existing = {};
|
|
314
|
+
try {
|
|
315
|
+
existing = JSON.parse(fs.readFileSync(settingsPath, "utf-8"));
|
|
316
|
+
}
|
|
317
|
+
catch { /* novo */ }
|
|
318
|
+
const merged = { ...existing, hooks: settings.hooks };
|
|
319
|
+
fs.writeFileSync(settingsPath, JSON.stringify(merged, null, 2) + "\n", "utf-8");
|
|
320
|
+
}
|
|
321
|
+
const CLAUDE_MD_MARKER_START = "<!-- lkpabap:start -->";
|
|
322
|
+
const CLAUDE_MD_MARKER_END = "<!-- lkpabap:end -->";
|
|
323
|
+
function updateClaudeMd(claudeMdPath, config) {
|
|
324
|
+
const section = buildClaudeMdSection(config);
|
|
325
|
+
if (!section)
|
|
326
|
+
return;
|
|
327
|
+
const block = `${CLAUDE_MD_MARKER_START}\n${section}${CLAUDE_MD_MARKER_END}\n`;
|
|
328
|
+
let existing = "";
|
|
329
|
+
try {
|
|
330
|
+
existing = fs.readFileSync(claudeMdPath, "utf-8");
|
|
331
|
+
}
|
|
332
|
+
catch { /* novo arquivo */ }
|
|
333
|
+
let updated;
|
|
334
|
+
if (existing.includes(CLAUDE_MD_MARKER_START)) {
|
|
335
|
+
// Atualiza bloco existente
|
|
336
|
+
const re = new RegExp(`${CLAUDE_MD_MARKER_START}[\\s\\S]*?${CLAUDE_MD_MARKER_END}\\n?`, "g");
|
|
337
|
+
updated = existing.replace(re, block);
|
|
338
|
+
}
|
|
339
|
+
else {
|
|
340
|
+
// Acrescenta ao final
|
|
341
|
+
updated = existing ? `${existing.trimEnd()}\n\n${block}` : block;
|
|
342
|
+
}
|
|
343
|
+
const dir = path.dirname(claudeMdPath);
|
|
344
|
+
if (!fs.existsSync(dir))
|
|
345
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
346
|
+
fs.writeFileSync(claudeMdPath, updated, "utf-8");
|
|
347
|
+
}
|
|
348
|
+
// ---------------------------------------------------------------------------
|
|
349
|
+
// Wizard
|
|
350
|
+
// ---------------------------------------------------------------------------
|
|
351
|
+
async function promptSystem() {
|
|
352
|
+
const response = await (0, prompts_1.default)([
|
|
353
|
+
{
|
|
354
|
+
type: "text",
|
|
355
|
+
name: "name",
|
|
356
|
+
message: "Nome deste sistema (identificador curto)",
|
|
357
|
+
validate: (v) => v.trim().length > 0 ? true : "Informe um nome (ex: novaforma-dev)",
|
|
358
|
+
},
|
|
359
|
+
{
|
|
360
|
+
type: "text",
|
|
361
|
+
name: "url",
|
|
362
|
+
message: "URL do SAP (com porta)",
|
|
363
|
+
validate: (v) => {
|
|
364
|
+
try {
|
|
365
|
+
new URL(v);
|
|
366
|
+
return true;
|
|
367
|
+
}
|
|
368
|
+
catch {
|
|
369
|
+
return "URL inválida (ex: https://sap-host:8443)";
|
|
370
|
+
}
|
|
371
|
+
},
|
|
372
|
+
},
|
|
373
|
+
{
|
|
374
|
+
type: "text",
|
|
375
|
+
name: "client",
|
|
376
|
+
message: "Client (mandant)",
|
|
377
|
+
initial: "100",
|
|
378
|
+
},
|
|
379
|
+
{
|
|
380
|
+
type: "text",
|
|
381
|
+
name: "user",
|
|
382
|
+
message: "Usuário SAP",
|
|
383
|
+
validate: (v) => v.trim().length > 0 ? true : "Informe o usuário",
|
|
384
|
+
},
|
|
385
|
+
{
|
|
386
|
+
type: "password",
|
|
387
|
+
name: "pass",
|
|
388
|
+
message: "Senha SAP",
|
|
389
|
+
validate: (v) => v.length > 0 ? true : "Informe a senha",
|
|
390
|
+
},
|
|
391
|
+
{
|
|
392
|
+
type: "text",
|
|
393
|
+
name: "language",
|
|
394
|
+
message: "Idioma (EN, PT, DE...)",
|
|
395
|
+
initial: "PT",
|
|
396
|
+
},
|
|
397
|
+
{
|
|
398
|
+
type: "confirm",
|
|
399
|
+
name: "selfSignedSsl",
|
|
400
|
+
message: "Certificado SSL self-signed?",
|
|
401
|
+
initial: true,
|
|
402
|
+
},
|
|
403
|
+
{
|
|
404
|
+
type: "select",
|
|
405
|
+
name: "environmentRole",
|
|
406
|
+
message: "Papel do ambiente (controla quais operações o LKPABAP.ai pode executar)",
|
|
407
|
+
choices: [
|
|
408
|
+
{ title: "DEVELOPMENT — leitura + escrita + criação (padrão para DEV)", value: "DEVELOPMENT" },
|
|
409
|
+
{ title: "QUALITY — somente leitura (padrão para QAS/QA)", value: "QUALITY" },
|
|
410
|
+
{ title: "PRODUCTION — somente leitura, mais restritivo (padrão para PRD)", value: "PRODUCTION" },
|
|
411
|
+
],
|
|
412
|
+
initial: 0,
|
|
413
|
+
},
|
|
414
|
+
], {
|
|
415
|
+
onCancel: () => {
|
|
416
|
+
console.log("\n Cancelado.");
|
|
417
|
+
process.exit(0);
|
|
418
|
+
},
|
|
419
|
+
});
|
|
420
|
+
if (!response.name || !response.url || !response.user || !response.pass) {
|
|
421
|
+
return null;
|
|
422
|
+
}
|
|
423
|
+
return {
|
|
424
|
+
name: response.name.trim().toLowerCase().replace(/\s+/g, "-"),
|
|
425
|
+
url: response.url.trim().replace(/\/+$/, ""),
|
|
426
|
+
client: response.client?.trim() || "100",
|
|
427
|
+
user: response.user.trim().toUpperCase(),
|
|
428
|
+
pass: response.pass,
|
|
429
|
+
language: (response.language?.trim() || "PT").toUpperCase(),
|
|
430
|
+
selfSignedSsl: response.selfSignedSsl ?? true,
|
|
431
|
+
environmentRole: response.environmentRole || "DEVELOPMENT",
|
|
432
|
+
};
|
|
433
|
+
}
|
|
434
|
+
// ---------------------------------------------------------------------------
|
|
435
|
+
// Main
|
|
436
|
+
// ---------------------------------------------------------------------------
|
|
437
|
+
async function init(options = {}) {
|
|
438
|
+
const local = options.local ?? false;
|
|
439
|
+
const mcpPath = getMcpPath(local);
|
|
440
|
+
const scopeLabel = local
|
|
441
|
+
? `projeto → ${mcpPath}`
|
|
442
|
+
: `global → ${mcpPath}`;
|
|
443
|
+
console.log(`
|
|
444
|
+
╭─────────────────────────────────────╮
|
|
445
|
+
│ LKPABAP.ai — Setup │
|
|
446
|
+
│ Conecte o Claude ao seu SAP │
|
|
447
|
+
╰─────────────────────────────────────╯
|
|
448
|
+
|
|
449
|
+
Escopo: ${scopeLabel}
|
|
450
|
+
`);
|
|
451
|
+
const config = readMcpConfig(mcpPath);
|
|
452
|
+
const existingCount = Object.keys(config.mcpServers).filter((k) => k.startsWith("abap-")).length;
|
|
453
|
+
if (existingCount > 0) {
|
|
454
|
+
console.log(` ${existingCount} sistema(s) SAP já configurado(s).\n`);
|
|
455
|
+
}
|
|
456
|
+
const addedSystems = [];
|
|
457
|
+
let addMore = true;
|
|
458
|
+
while (addMore) {
|
|
459
|
+
const system = await promptSystem();
|
|
460
|
+
if (!system)
|
|
461
|
+
break;
|
|
462
|
+
const serverName = `abap-${system.name}`;
|
|
463
|
+
// Verificar duplicata
|
|
464
|
+
if (config.mcpServers[serverName]) {
|
|
465
|
+
const { overwrite } = await (0, prompts_1.default)({
|
|
466
|
+
type: "confirm",
|
|
467
|
+
name: "overwrite",
|
|
468
|
+
message: `Sistema "${serverName}" já existe. Sobrescrever?`,
|
|
469
|
+
initial: false,
|
|
470
|
+
});
|
|
471
|
+
if (!overwrite)
|
|
472
|
+
continue;
|
|
473
|
+
}
|
|
474
|
+
// Testar conexão
|
|
475
|
+
console.log("\n ⏳ Testando conexão com SAP...");
|
|
476
|
+
const result = await testConnection(system);
|
|
477
|
+
if (result.ok) {
|
|
478
|
+
console.log(` ✓ Conectado! ${result.info}\n`);
|
|
479
|
+
addSystemToConfig(config, system);
|
|
480
|
+
addedSystems.push(system.name);
|
|
481
|
+
}
|
|
482
|
+
else {
|
|
483
|
+
console.log(` ✗ Falha: ${result.info}\n`);
|
|
484
|
+
const { retry } = await (0, prompts_1.default)({
|
|
485
|
+
type: "select",
|
|
486
|
+
name: "retry",
|
|
487
|
+
message: "O que deseja fazer?",
|
|
488
|
+
choices: [
|
|
489
|
+
{ title: "Tentar novamente com outros dados", value: "retry" },
|
|
490
|
+
{ title: "Adicionar mesmo assim (sem validar)", value: "force" },
|
|
491
|
+
{ title: "Pular este sistema", value: "skip" },
|
|
492
|
+
],
|
|
493
|
+
});
|
|
494
|
+
if (retry === "force") {
|
|
495
|
+
addSystemToConfig(config, system);
|
|
496
|
+
addedSystems.push(system.name);
|
|
497
|
+
console.log(` ⚠️ Sistema adicionado sem validação.\n`);
|
|
498
|
+
}
|
|
499
|
+
else if (retry === "retry") {
|
|
500
|
+
continue;
|
|
501
|
+
}
|
|
502
|
+
else {
|
|
503
|
+
// skip
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
const { more } = await (0, prompts_1.default)({
|
|
507
|
+
type: "confirm",
|
|
508
|
+
name: "more",
|
|
509
|
+
message: "Deseja adicionar outro sistema?",
|
|
510
|
+
initial: false,
|
|
511
|
+
});
|
|
512
|
+
addMore = more;
|
|
513
|
+
}
|
|
514
|
+
if (addedSystems.length > 0) {
|
|
515
|
+
writeMcpConfig(config, mcpPath);
|
|
516
|
+
// Gera/atualiza CLAUDE.md com política de segurança + contexto dos sistemas SAP
|
|
517
|
+
const claudeMdPath = local
|
|
518
|
+
? path.join(process.cwd(), "CLAUDE.md")
|
|
519
|
+
: path.join(os.homedir(), ".claude", "CLAUDE.md");
|
|
520
|
+
updateClaudeMd(claudeMdPath, config);
|
|
521
|
+
// Cria .claude/settings.json com hooks PreToolUse para operações de escrita
|
|
522
|
+
const settingsBaseDir = local ? process.cwd() : os.homedir();
|
|
523
|
+
writeClaudeSettings(settingsBaseDir);
|
|
524
|
+
const total = Object.keys(config.mcpServers).filter((k) => k.startsWith("abap-")).length;
|
|
525
|
+
console.log(`
|
|
526
|
+
✓ Configuração salva em ${mcpPath}
|
|
527
|
+
|
|
528
|
+
╭──────────────────────────────────────────────╮
|
|
529
|
+
│ ${total} sistema(s) configurado(s):${" ".repeat(Math.max(0, 23 - total.toString().length))}│`);
|
|
530
|
+
for (const name of addedSystems) {
|
|
531
|
+
const sn = `abap-${name}`;
|
|
532
|
+
const env = config.mcpServers[sn]?.env || {};
|
|
533
|
+
const client = env.SAP_CLIENT || "?";
|
|
534
|
+
const role = env.ABAP_AI_ENV_ROLE || "DEV";
|
|
535
|
+
const roleTag = role === "PRODUCTION" ? "PRD" : role === "QUALITY" ? "QAS" : "DEV";
|
|
536
|
+
const line = ` │ • ${name} (client ${client}, ${roleTag})`;
|
|
537
|
+
console.log(`${line}${" ".repeat(Math.max(1, 48 - line.length))}│`);
|
|
538
|
+
}
|
|
539
|
+
if (local) {
|
|
540
|
+
console.log(` │${" ".repeat(46)}│
|
|
541
|
+
│ Próximo passo no VS Code:${" ".repeat(19)}│
|
|
542
|
+
│ 1. Ctrl+Shift+P → "Reload Window"${" ".repeat(8)}│
|
|
543
|
+
│ 2. Clique "Allow" no aviso do MCP${" ".repeat(8)}│
|
|
544
|
+
│ 3. Converse com o Claude Code!${" ".repeat(11)}│
|
|
545
|
+
╰──────────────────────────────────────────────╯
|
|
546
|
+
`);
|
|
547
|
+
}
|
|
548
|
+
else {
|
|
549
|
+
console.log(` │${" ".repeat(46)}│
|
|
550
|
+
│ Próximo passo:${" ".repeat(29)}│
|
|
551
|
+
│ Abra o Claude Code e use!${" ".repeat(16)}│
|
|
552
|
+
╰──────────────────────────────────────────────╯
|
|
553
|
+
`);
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
else {
|
|
557
|
+
console.log("\n Nenhum sistema adicionado.\n");
|
|
558
|
+
}
|
|
559
|
+
}
|