komplian 0.4.6 → 0.4.7

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.
@@ -0,0 +1,238 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Komplian MCP — escribe `.cursor/mcp.json` con servidores MCP por stdio (prefijo KOMPLIAN-*)
4
+ * y genera `.cursor/KOMPLIAN_MCP_SETUP.md` (plugins de Cursor que no van en JSON).
5
+ *
6
+ * No incluye secretos: tokens vacíos; rellena en Cursor o vía KOMPLIAN_MCP_SECRETS.env (local, gitignored).
7
+ *
8
+ * Uso: npx komplian mcp-tools --yes
9
+ */
10
+
11
+ import { mkdirSync, readFileSync, existsSync, writeFileSync } from "node:fs";
12
+ import { dirname, join, resolve } from "node:path";
13
+ import { homedir } from "node:os";
14
+ import { createInterface } from "node:readline/promises";
15
+ import { stdin as input, stdout as output } from "node:process";
16
+
17
+ const c = {
18
+ reset: "\x1b[0m",
19
+ dim: "\x1b[2m",
20
+ bold: "\x1b[1m",
21
+ cyan: "\x1b[36m",
22
+ green: "\x1b[32m",
23
+ red: "\x1b[31m",
24
+ yellow: "\x1b[33m",
25
+ };
26
+
27
+ function log(s = "") {
28
+ console.log(s);
29
+ }
30
+
31
+ /** Preset Komplian: solo claves KOMPLIAN-* para no pisar servidores del dev. */
32
+ const KOMPLIAN_MCP_PRESET = {
33
+ mcpServers: {
34
+ "KOMPLIAN-github": {
35
+ command: "npx",
36
+ args: ["-y", "@modelcontextprotocol/server-github"],
37
+ env: {
38
+ GITHUB_PERSONAL_ACCESS_TOKEN: "",
39
+ },
40
+ },
41
+ "KOMPLIAN-sentry": {
42
+ command: "npx",
43
+ args: ["-y", "@sentry/mcp-server@latest"],
44
+ env: {},
45
+ },
46
+ "KOMPLIAN-stripe": {
47
+ command: "npx",
48
+ args: ["-y", "@stripe/mcp"],
49
+ env: {
50
+ STRIPE_SECRET_KEY: "",
51
+ },
52
+ },
53
+ },
54
+ };
55
+
56
+ const SETUP_MD = `# KOMPLIAN — MCP en Cursor
57
+
58
+ Este archivo lo genera \`npx komplian mcp-tools\`. **No commitees** tokens; \`.cursor/mcp.json\` puede llevar env vacíos que rellenas solo en local.
59
+
60
+ ## 1. Servidores en \`.cursor/mcp.json\` (stdio / npx)
61
+
62
+ | ID | Qué es | Rellena |
63
+ |----|--------|---------|
64
+ | **KOMPLIAN-github** | GitHub API (issues, PRs) | \`GITHUB_PERSONAL_ACCESS_TOKEN\` en el bloque \`env\` del servidor (PAT con scopes repo/workflow según necesidad). El paquete npm está deprecado a favor del servidor oficial; si falla, sustituye por la config que indique [github/github-mcp-server](https://github.com/github/github-mcp-server). |
65
+ | **KOMPLIAN-sentry** | Sentry (issues, trazas) | Primera vez: login por navegador (device code). **Komplian:** \`organizationSlug\` = \`komplian\`, \`regionUrl\` = \`https://de.sentry.io\`. Proyectos: \`komplian-api\`, \`komplian-app\`. |
66
+ | **KOMPLIAN-stripe** | Stripe API | \`STRIPE_SECRET_KEY\` en \`env\` (mejor **Restricted key** / test en dev). Modo test vs live según [docs Stripe MCP](https://docs.stripe.com/mcp). |
67
+
68
+ ## 2. Solo en Cursor (plugins / integraciones)
69
+
70
+ Estos **no** se instalan con npx en este JSON; actívalos en **Cursor → Settings → MCP** (o integraciones) e inicia sesión:
71
+
72
+ | Integración | Uso en Komplian |
73
+ |-------------|-----------------|
74
+ | **Atlassian** (Jira + Confluence) | Site \`komplian.atlassian.net\`, proyecto Jira **KAPP**. Plugin \`plugin-atlassian-atlassian\` / \`user-mcp-atlassian\`. |
75
+ | **Chrome DevTools** | Depuración del navegador (\`user-chrome-devtools\`). |
76
+
77
+ ## 3. Reinicio
78
+
79
+ Tras editar \`mcp.json\`, **reinicia Cursor** (o recarga ventana) para cargar MCP.
80
+
81
+ ## 4. Referencia interna
82
+
83
+ Ver \`.cursor/rules/mcp-integrations.mdc\` en el monorepo (org Sentry, Stripe, reglas KAPP).
84
+ `;
85
+
86
+ function findWorkspaceRoot(start) {
87
+ let dir = resolve(start);
88
+ for (let i = 0; i < 8; i++) {
89
+ if (
90
+ existsSync(join(dir, "api", "package.json")) &&
91
+ existsSync(join(dir, "app", "package.json"))
92
+ ) {
93
+ return dir;
94
+ }
95
+ const parent = dirname(dir);
96
+ if (parent === dir) break;
97
+ dir = parent;
98
+ }
99
+ return resolve(start);
100
+ }
101
+
102
+ function mergeKomplianPreset(existing, preset, force) {
103
+ const base =
104
+ existing && typeof existing === "object" ? structuredClone(existing) : {};
105
+ if (!base.mcpServers || typeof base.mcpServers !== "object") {
106
+ base.mcpServers = {};
107
+ }
108
+ for (const [name, cfg] of Object.entries(preset.mcpServers)) {
109
+ if (!name.startsWith("KOMPLIAN-")) continue;
110
+ if (!force && base.mcpServers[name]) continue;
111
+ base.mcpServers[name] = structuredClone(cfg);
112
+ }
113
+ return base;
114
+ }
115
+
116
+ function parseMcpArgs(argv) {
117
+ const opts = {
118
+ yes: false,
119
+ force: false,
120
+ dryRun: false,
121
+ global: false,
122
+ help: false,
123
+ workspace: "",
124
+ };
125
+ const rest = [];
126
+ for (let i = 0; i < argv.length; i++) {
127
+ const a = argv[i];
128
+ if (a === "--yes" || a === "-y") opts.yes = true;
129
+ else if (a === "--force") opts.force = true;
130
+ else if (a === "--dry-run") opts.dryRun = true;
131
+ else if (a === "--global") opts.global = true;
132
+ else if (a === "-h" || a === "--help") opts.help = true;
133
+ else if (a === "--workspace" || a === "-w") opts.workspace = argv[++i] || "";
134
+ else if (a.startsWith("-")) {
135
+ log(`${c.red}✗${c.reset} Opción desconocida: ${a}`);
136
+ process.exit(1);
137
+ } else rest.push(a);
138
+ }
139
+ if (rest[0]) opts.workspace = rest[0];
140
+ return opts;
141
+ }
142
+
143
+ function usageMcpTools() {
144
+ log(`Uso: npx komplian mcp-tools [opciones] [carpeta-monorepo]`);
145
+ log(``);
146
+ log(` Añade servidores MCP KOMPLIAN-* en .cursor/mcp.json (GitHub, Sentry, Stripe)`);
147
+ log(` y genera .cursor/KOMPLIAN_MCP_SETUP.md (Atlassian + Chrome DevTools = solo Cursor).`);
148
+ log(``);
149
+ log(` ${c.dim}Secretos: vacíos en JSON; no se publican en el paquete npm komplian.${c.reset}`);
150
+ log(``);
151
+ log(` -y, --yes Sin confirmación`);
152
+ log(` --force Sobreescribe entradas KOMPLIAN-* existentes`);
153
+ log(` --dry-run Imprime JSON sin escribir`);
154
+ log(` --global Fusiona en ~/.cursor/mcp.json (toda la máquina)`);
155
+ log(` -w, --workspace Raíz del monorepo (por defecto: cwd)`);
156
+ log(` -h, --help`);
157
+ }
158
+
159
+ async function confirmForceOverwrite(yes) {
160
+ if (yes) return true;
161
+ const rl = createInterface({ input, output });
162
+ const ans = await rl.question(
163
+ `\n${c.bold}¿Sobrescribir entradas KOMPLIAN-* existentes? [y/N]${c.reset} `
164
+ );
165
+ rl.close();
166
+ return /^y(es)?$/i.test((ans || "").trim());
167
+ }
168
+
169
+ export async function runMcpTools(argv) {
170
+ const opts = parseMcpArgs(argv);
171
+ if (opts.help) {
172
+ usageMcpTools();
173
+ return;
174
+ }
175
+
176
+ const workspaceRoot = opts.workspace.trim()
177
+ ? resolve(opts.workspace.replace(/^~(?=$|[/\\])/, homedir()))
178
+ : findWorkspaceRoot(process.cwd());
179
+
180
+ const cursorDir = opts.global
181
+ ? join(homedir(), ".cursor")
182
+ : join(workspaceRoot, ".cursor");
183
+ const mcpPath = join(cursorDir, "mcp.json");
184
+ const setupPath = join(cursorDir, "KOMPLIAN_MCP_SETUP.md");
185
+
186
+ let existing = {};
187
+ if (existsSync(mcpPath)) {
188
+ try {
189
+ existing = JSON.parse(readFileSync(mcpPath, "utf8"));
190
+ } catch {
191
+ log(`${c.red}✗${c.reset} JSON inválido: ${mcpPath}`);
192
+ process.exit(1);
193
+ }
194
+ }
195
+
196
+ const hadKomplian = Object.keys(existing.mcpServers || {}).some((k) =>
197
+ k.startsWith("KOMPLIAN-")
198
+ );
199
+ if (opts.force && hadKomplian && !opts.yes) {
200
+ const ok = await confirmForceOverwrite(opts.yes);
201
+ if (!ok) {
202
+ log(`${c.yellow}○${c.reset} Cancelado.`);
203
+ return;
204
+ }
205
+ }
206
+
207
+ const merged = mergeKomplianPreset(existing, KOMPLIAN_MCP_PRESET, opts.force);
208
+ const json = JSON.stringify(merged, null, 2);
209
+
210
+ if (opts.dryRun) {
211
+ log(`${c.cyan}━━ dry-run ━━${c.reset} ${mcpPath}`);
212
+ log(json);
213
+ log("");
214
+ log(SETUP_MD);
215
+ return;
216
+ }
217
+
218
+ mkdirSync(cursorDir, { recursive: true });
219
+ const prevStr = existsSync(mcpPath) ? readFileSync(mcpPath, "utf8") : "";
220
+ const changed = prevStr.trim() !== json.trim();
221
+ if (changed) {
222
+ writeFileSync(mcpPath, json + "\n", "utf8");
223
+ }
224
+ writeFileSync(setupPath, SETUP_MD, "utf8");
225
+
226
+ log("");
227
+ const mcpMark = changed ? `${c.green}✓${c.reset}` : `${c.dim}○${c.reset}`;
228
+ const mcpNote = changed ? "" : ` ${c.dim}(sin cambios)${c.reset}`;
229
+ log(`${mcpMark} ${c.bold}mcp.json${c.reset} ${c.dim}${mcpPath}${c.reset}${mcpNote}`);
230
+ log(`${c.green}✓${c.reset} ${c.bold}KOMPLIAN_MCP_SETUP.md${c.reset} ${c.dim}${setupPath}${c.reset}`);
231
+ log("");
232
+ log(`${c.bold}Siguiente${c.reset}`);
233
+ log(` 1. Rellena tokens vacíos en Cursor (Settings → MCP) o edita ${c.bold}mcp.json${c.reset} en local.`);
234
+ log(` 2. Activa en Cursor: ${c.dim}Atlassian + Chrome DevTools${c.reset} (ver guía arriba).`);
235
+ log(` 3. Reinicia Cursor.`);
236
+ log("");
237
+ log(`${c.dim}Paquete npm komplian solo incluye *.mjs — nunca sube tu mcp.json.${c.reset}`);
238
+ }
@@ -394,11 +394,12 @@ function npmInstallEach(workspace) {
394
394
  }
395
395
 
396
396
  function usage() {
397
- log(`Uso: komplian onboard [opciones] [carpeta] | komplian postman [opciones] | komplian localhost [opciones]`);
397
+ log(`Uso: komplian onboard | postman | localhost | mcp-tools [opciones]`);
398
398
  log(` npx komplian onboard --yes`);
399
399
  log(` npx komplian postman login ${c.dim}(una vez · guarda API key)${c.reset}`);
400
400
  log(` npx komplian postman --yes ${c.dim}(email @komplian.com)${c.reset}`);
401
401
  log(` npx komplian localhost --yes ${c.dim}(env local + api app web admin docs)${c.reset}`);
402
+ log(` npx komplian mcp-tools --yes ${c.dim}(.cursor/mcp.json + guía MCP Komplian)${c.reset}`);
402
403
  log(``);
403
404
  log(` Antes (una vez): gh auth login -h github.com -s repo -s read:org -w`);
404
405
  log(` Requisitos: Node 18+, git, GitHub CLI (gh)`);
@@ -492,6 +493,11 @@ async function main() {
492
493
  await runLocalhost(rawArgv.slice(1));
493
494
  return;
494
495
  }
496
+ if (rawArgv[0] === "mcp-tools") {
497
+ const { runMcpTools } = await import("./komplian-mcp-tools.mjs");
498
+ await runMcpTools(rawArgv.slice(1));
499
+ return;
500
+ }
495
501
 
496
502
  const configPath = join(__dirname, "komplian-team-repos.json");
497
503
  const { argv, fromOnboardSubcommand } = normalizeArgv(rawArgv);
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "komplian",
3
- "version": "0.4.6",
4
- "description": "Komplian CLI: clone repos (onboard), Postman sync, localhost (.env.local + dev servers). Node 18+. No env files in the published package.",
3
+ "version": "0.4.7",
4
+ "description": "Komplian CLI: onboard, Postman, localhost, mcp-tools (Cursor MCP). Node 18+. Published tarball has no .env / secrets.",
5
5
  "type": "module",
6
6
  "engines": {
7
7
  "node": ">=18"
@@ -13,6 +13,7 @@
13
13
  "komplian-onboard.mjs",
14
14
  "komplian-postman.mjs",
15
15
  "komplian-localhost.mjs",
16
+ "komplian-mcp-tools.mjs",
16
17
  "komplian-team-repos.json",
17
18
  "README.md"
18
19
  ],
@@ -20,7 +21,7 @@
20
21
  "access": "public"
21
22
  },
22
23
  "scripts": {
23
- "prepublishOnly": "node --check komplian-onboard.mjs && node --check komplian-postman.mjs && node --check komplian-localhost.mjs"
24
+ "prepublishOnly": "node --check komplian-onboard.mjs && node --check komplian-postman.mjs && node --check komplian-localhost.mjs && node --check komplian-mcp-tools.mjs"
24
25
  },
25
26
  "keywords": [
26
27
  "komplian",