komplian 0.1.0 → 0.3.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,23 +1,21 @@
1
1
  # komplian (npm CLI)
2
2
 
3
- **Developers:** from any machine with Node 18+ and GitHub CLI (`gh`):
3
+ **Developers**
4
4
 
5
- ```bash
6
- npx komplian onboard --yes
7
- ```
5
+ 1. Install [GitHub CLI](https://cli.github.com) and **git** (one-time per machine).
6
+ 2. Browser login: `gh auth login -h github.com -s repo -s read:org -w`
7
+ 3. `npx komplian onboard --yes`
8
8
 
9
- This downloads the CLI from npm (no manual git clone), opens GitHub login if needed, and clones the repos your account can access (filtered by team in the bundled `komplian-team-repos.json`). Default workspace: `~/komplian` (Windows: `%USERPROFILE%\komplian`).
9
+ No OAuth App registration `gh` uses GitHub’s built-in flow. Default workspace: `~/komplian`.
10
10
 
11
- **Maintainers:** publish **from this folder** (the one that contains `package.json` not the monorepo root):
11
+ **Maintainers:** publish from **`scripts/`** (folder with `package.json`), not the monorepo root:
12
12
 
13
13
  ```bash
14
- cd path/to/monorepo/scripts # e.g. cd ~/Desktop/temp/scripts
15
- npm login # once per machine
14
+ cd path/to/monorepo/scripts
15
+ npm login
16
16
  npm publish --access public
17
17
  ```
18
18
 
19
- Running `npm publish` from the repo root causes `ENOENT package.json` because the npm package lives only under `scripts/`.
20
-
21
- Bump `version` in `package.json` before each publish.
19
+ Bump `version` before each publish.
22
20
 
23
- **If publish fails with `403` / “Two-factor authentication … required”:** enable **2FA** on your npm account (*npmjs.com → Account → Security*), then run `npm publish` again (npm may prompt for an OTP). For CI, create a **granular access token** (*Access Tokens → Granular token*) with **Publish** on the `komplian` package and “**Bypass two-factor authentication**” if your org allows it; set `NPM_TOKEN` in GitHub Actions.
21
+ **If publish fails with `403` / 2FA:** enable **2FA** on npm (*Account → Security*) or use a **granular token** with publish rights; set `NPM_TOKEN` in CI (see `.github/workflows/publish-komplian-npm.yml`).
@@ -1,12 +1,11 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
- * Komplian onboard — one-command dev setup (GitHub CLI auth + clone + optional npm install).
4
- * Zero npm deps: Node 18+, git, gh, and komplian-team-repos.json beside this file.
3
+ * Komplian onboard — GitHub CLI (browser login) + clone. No OAuth App to register.
4
+ * Needs: Node 18+, git, gh (https://cli.github.com)
5
5
  *
6
6
  * Usage:
7
- * npx komplian onboard --yes # npm ships this package; no manual monorepo clone for developers
8
- *
9
- * Security: `gh` OAuth; active org membership verified via GitHub API before clones; repo ACLs enforced by GitHub. See ONBOARDING.md.
7
+ * gh auth login -h github.com -s repo -s read:org -w # once
8
+ * npx komplian onboard --yes
10
9
  */
11
10
 
12
11
  import { spawnSync } from "node:child_process";
@@ -35,7 +34,6 @@ function log(s = "") {
35
34
  }
36
35
 
37
36
  function banner() {
38
- // Block Unicode (cfonts "block"). Fourth letter is P: row 4 uses ██╔═══╝; R would use ██╔══██╗.
39
37
  log(
40
38
  [
41
39
  `${c.cyan}${c.bold}`,
@@ -46,7 +44,7 @@ function banner() {
46
44
  "██║ ██╗ ╚██████╔╝ ██║ ╚═╝ ██║ ██║ ███████╗ ██║ ██║ ██║ ██║ ╚████║",
47
45
  "╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝ ╚═╝ ╚══════╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═══╝",
48
46
  `${c.reset}`,
49
- `${c.dim} Secure setup · GitHub CLI · HTTPS clones · No secrets stored by this CLI${c.reset}`,
47
+ `${c.dim} Secure setup · GitHub CLI · git clone · Zero org OAuth setup${c.reset}`,
50
48
  "",
51
49
  ].join("\n")
52
50
  );
@@ -60,10 +58,9 @@ function ghOk() {
60
58
  return r.status === 0;
61
59
  }
62
60
 
63
- /** Scopes: repo (clone), read:org (verify org membership — required for private orgs). */
64
61
  function runGhAuth() {
65
62
  log(
66
- `${c.yellow}→${c.reset} Opening ${c.bold}GitHub${c.reset} login in the browser (OAuth). We never see your password.`
63
+ `${c.yellow}→${c.reset} Abre ${c.bold}GitHub${c.reset} en el navegador (OAuth). No vemos tu contraseña.`
67
64
  );
68
65
  const r = spawnSync(
69
66
  "gh",
@@ -81,47 +78,39 @@ function ghApiJson(path) {
81
78
  return { status: r.status, stdout: r.stdout || "", stderr: r.stderr || "" };
82
79
  }
83
80
 
84
- /**
85
- * Ensures the authenticated user is an active member of the GitHub org (not just any GitHub account).
86
- * Relies on GitHub as source of truth; optional SAML/SSO is enforced by the org on github.com.
87
- */
88
81
  function verifyOrgMembership(org) {
89
82
  const enc = encodeURIComponent(org);
90
83
  const mem = ghApiJson(`user/memberships/orgs/${enc}`);
91
84
  if (mem.status === 200) {
92
85
  try {
93
86
  const j = JSON.parse(mem.stdout);
94
- if (j.state === "active") {
95
- return;
96
- }
87
+ if (j.state === "active") return;
97
88
  log(
98
- `${c.red}✗${c.reset} Organization ${c.bold}${org}${c.reset} membership is not active (state: ${j.state || "?"}).`
89
+ `${c.red}✗${c.reset} Membresía en ${c.bold}${org}${c.reset} no activa (state: ${j.state || "?"}).`
99
90
  );
100
91
  process.exit(1);
101
92
  } catch {
102
- log(`${c.red}✗${c.reset} Invalid response verifying org membership.`);
93
+ log(`${c.red}✗${c.reset} Respuesta inválida al verificar la org.`);
103
94
  process.exit(1);
104
95
  }
105
96
  }
106
97
  if (mem.status === 404) {
107
98
  log(
108
- `${c.red}✗${c.reset} This GitHub account is ${c.bold}not a member${c.reset} of org ${c.bold}${org}${c.reset}.`
99
+ `${c.red}✗${c.reset} Esta cuenta ${c.bold}no es miembro${c.reset} de la org ${c.bold}${org}${c.reset}.`
109
100
  );
110
101
  log(
111
- `${c.dim} Only invited org members can run this tool. Wrong account? Run: gh auth logout && gh auth login${c.reset}`
102
+ `${c.dim} ¿Otra cuenta? gh auth logout && gh auth login -h github.com -s repo -s read:org -w${c.reset}`
112
103
  );
113
104
  process.exit(1);
114
105
  }
115
106
  const hint = (mem.stderr + mem.stdout).toLowerCase();
116
107
  if (mem.status === 403 || hint.includes("read:org") || hint.includes("scope")) {
117
- log(`${c.red}✗${c.reset} Token cannot verify org membership (needs ${c.bold}read:org${c.reset} scope).`);
118
- log(
119
- `${c.dim} Run: gh auth refresh -h github.com -s repo -s read:org${c.reset}`
120
- );
108
+ log(`${c.red}✗${c.reset} Falta scope ${c.bold}read:org${c.reset} en gh.`);
109
+ log(`${c.dim} gh auth refresh -h github.com -s repo -s read:org${c.reset}`);
121
110
  process.exit(1);
122
111
  }
123
112
  log(
124
- `${c.red}✗${c.reset} Could not verify org membership (${mem.status}):\n${c.dim}${(mem.stderr || mem.stdout).trim()}${c.reset}`
113
+ `${c.red}✗${c.reset} No se pudo verificar la org (${mem.status}):\n${c.dim}${(mem.stderr || mem.stdout).trim()}${c.reset}`
125
114
  );
126
115
  process.exit(1);
127
116
  }
@@ -132,17 +121,25 @@ function logGhIdentity() {
132
121
  try {
133
122
  const j = JSON.parse(u.stdout);
134
123
  if (j.login) {
135
- log(`${c.dim} Signed in as ${c.bold}@${j.login}${c.reset}${c.dim} (github.com)${c.reset}`);
124
+ log(`${c.dim} Sesión: ${c.bold}@${j.login}${c.reset}${c.dim} (github.com)${c.reset}`);
136
125
  }
137
126
  } catch {
138
127
  /* ignore */
139
128
  }
140
129
  }
141
130
 
142
- function needCmd(name) {
143
- const r = spawnSync("command", ["-v", name], { shell: true, stdio: "ignore" });
144
- if (r.status !== 0) {
145
- log(`${c.red}✗${c.reset} Missing ${c.bold}${name}${c.reset}. Install it and retry.`);
131
+ const isWin = process.platform === "win32";
132
+
133
+ function cmdExists(name) {
134
+ const r = isWin
135
+ ? spawnSync("where", [name], { stdio: "ignore" })
136
+ : spawnSync("command", ["-v", name], { shell: true, stdio: "ignore" });
137
+ return r.status === 0;
138
+ }
139
+
140
+ function needCmd(name, hint = "") {
141
+ if (!cmdExists(name)) {
142
+ log(`${c.red}✗${c.reset} Falta ${c.bold}${name}${c.reset} en el PATH.${hint ? ` ${hint}` : ""}`);
146
143
  process.exit(1);
147
144
  }
148
145
  }
@@ -158,7 +155,7 @@ function ghRepoList(org) {
158
155
  { encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] }
159
156
  );
160
157
  if (r.status !== 0) {
161
- log(`${c.red}✗${c.reset} gh repo list failed:\n${r.stderr || r.stdout}`);
158
+ log(`${c.red}✗${c.reset} gh repo list falló:\n${r.stderr || r.stdout}`);
162
159
  process.exit(1);
163
160
  }
164
161
  const rows = JSON.parse(r.stdout || "[]");
@@ -188,7 +185,7 @@ function resolveRepos(cfg, org, teamArg, allRepos) {
188
185
  const teams = cfg.teams || {};
189
186
  if (!teams[team]) {
190
187
  log(
191
- `${c.red}✗${c.reset} Unknown team ${c.bold}${team}${c.reset}. Valid: ${Object.keys(teams).sort().join(", ")}`
188
+ `${c.red}✗${c.reset} Equipo desconocido ${c.bold}${team}${c.reset}. Válidos: ${Object.keys(teams).sort().join(", ")}`
192
189
  );
193
190
  process.exit(2);
194
191
  }
@@ -197,7 +194,7 @@ function resolveRepos(cfg, org, teamArg, allRepos) {
197
194
  const out = [];
198
195
  for (const name of [...allowed].sort()) {
199
196
  if (!visible.has(name)) {
200
- log(`${c.dim}○ skip ${org}/${name} (no access or not in org)${c.reset}`);
197
+ log(`${c.dim}○ omito ${org}/${name} (sin acceso o no está en la org)${c.reset}`);
201
198
  continue;
202
199
  }
203
200
  out.push(name);
@@ -214,15 +211,15 @@ function isSafeTargetDir(abs) {
214
211
  function cloudLine(org, name, status) {
215
212
  const cloud = `${c.blue}☁${c.reset}`;
216
213
  if (status === "ok") {
217
- return ` ${cloud} ${c.green}✓${c.reset} ${c.bold}${org}/${name}${c.reset} ${c.dim}cloned${c.reset}`;
214
+ return ` ${cloud} ${c.green}✓${c.reset} ${c.bold}${org}/${name}${c.reset} ${c.dim}clonado${c.reset}`;
218
215
  }
219
216
  if (status === "skip") {
220
- return ` ${cloud} ${c.yellow}○${c.reset} ${org}/${name} ${c.dim}(already there)${c.reset}`;
217
+ return ` ${cloud} ${c.yellow}○${c.reset} ${org}/${name} ${c.dim}(ya existe)${c.reset}`;
221
218
  }
222
219
  if (status === "fail") {
223
- return ` ${cloud} ${c.red}✗${c.reset} ${org}/${name} ${c.dim}failed${c.reset}`;
220
+ return ` ${cloud} ${c.red}✗${c.reset} ${org}/${name} ${c.dim}falló${c.reset}`;
224
221
  }
225
- return ` ${cloud} ${c.cyan}…${c.reset} ${org}/${name} ${c.dim}cloning…${c.reset}`;
222
+ return ` ${cloud} ${c.cyan}…${c.reset} ${org}/${name} ${c.dim}clonando…${c.reset}`;
226
223
  }
227
224
 
228
225
  function cloneOne(org, name, workspace, useSsh) {
@@ -256,7 +253,7 @@ function copyCursorPack(workspace, cursorRepoUrl) {
256
253
  if (cursorRepoUrl && String(cursorRepoUrl).trim()) {
257
254
  const tmp = join(workspace, ".cursor-bootstrap-tmp");
258
255
  rmSync(tmp, { recursive: true, force: true });
259
- log(`${c.dim}→${c.reset} Shallow clone Cursor pack…`);
256
+ log(`${c.dim}→${c.reset} Cursor pack (clone superficial)…`);
260
257
  const r = spawnSync("git", ["clone", "--depth", "1", String(cursorRepoUrl).trim(), tmp], {
261
258
  cwd: workspace,
262
259
  stdio: ["ignore", "pipe", "pipe"],
@@ -270,33 +267,29 @@ function copyCursorPack(workspace, cursorRepoUrl) {
270
267
  return;
271
268
  }
272
269
  rmSync(tmp, { recursive: true, force: true });
273
- log(`${c.yellow}○${c.reset} KOMPLIAN_CURSOR_REPO clone failed or missing .cursor/ at repo root`);
274
- }
275
-
276
- const aiToolsCursor = join(workspace, "ai-tools", ".cursor");
277
- if (existsSync(aiToolsCursor)) {
278
- rmSync(rootCursor, { recursive: true, force: true });
279
- cpSync(aiToolsCursor, rootCursor, { recursive: true });
280
- log(`${c.green}✓${c.reset} ${c.bold}.cursor${c.reset} ${c.dim}← ai-tools${c.reset}`);
281
- return;
270
+ log(`${c.yellow}○${c.reset} KOMPLIAN_CURSOR_REPO: falló el clone o no hay .cursor/ en la raíz`);
282
271
  }
283
272
  log(
284
- `${c.dim}○ No workspace .cursor/ (add ai-tools to your team or set KOMPLIAN_CURSOR_REPO).${c.reset}`
273
+ `${c.dim}○ Sin .cursor/ en el workspace (opcional: KOMPLIAN_CURSOR_REPO=<url git>).${c.reset}`
285
274
  );
286
275
  }
287
276
 
288
277
  function npmInstallEach(workspace) {
289
278
  log("");
290
- log(`${c.cyan}━━ npm install per repo ━━${c.reset}`);
279
+ log(`${c.cyan}━━ npm install por repo ━━${c.reset}`);
280
+ if (!cmdExists("npm")) {
281
+ log(`${c.yellow}○${c.reset} npm no está en PATH — omito installs`);
282
+ return;
283
+ }
291
284
  for (const ent of readdirSync(workspace)) {
292
285
  const d = join(workspace, ent);
293
286
  if (!statSync(d).isDirectory()) continue;
294
287
  const pkg = join(d, "package.json");
295
288
  if (!existsSync(pkg)) continue;
296
289
  log(`${c.dim}→${c.reset} ${ent}`);
297
- const r = spawnSync("npm", ["install"], { cwd: d, stdio: "inherit" });
298
- if (r.status !== 0) {
299
- log(`${c.yellow}○${c.reset} npm install had issues in ${ent}`);
290
+ const ir = spawnSync("npm", ["install"], { cwd: d, stdio: "inherit" });
291
+ if (ir.status !== 0) {
292
+ log(`${c.yellow}○${c.reset} npm install con avisos en ${ent}`);
300
293
  } else {
301
294
  log(`${c.green}✓${c.reset} ${ent}`);
302
295
  }
@@ -304,18 +297,21 @@ function npmInstallEach(workspace) {
304
297
  }
305
298
 
306
299
  function usage() {
307
- log(`Usage: komplian onboard [options] [dir]`);
308
- log(` npx komplian onboard --yes clones + npm install (default dir: ~/komplian)`);
300
+ log(`Uso: komplian onboard [opciones] [carpeta]`);
301
+ log(` npx komplian onboard --yes`);
309
302
  log(``);
310
- log(` Subcommand onboard implies --install; add --no-install to skip npm install.`);
311
- log(` -y, --yes Non-interactive (default team from JSON)`);
312
- log(` -t, --team <slug> Team from komplian-team-repos.json`);
313
- log(` -i, --install npm install (on by default with onboard; optional without subcommand)`);
314
- log(` --no-install Skip npm install (only after onboard)`);
315
- log(` --all-repos Clone all visible org repos (minus exclude list)`);
316
- log(` --ssh Use SSH URLs (git@github.com:...)`);
317
- log(` --list-teams Print team slugs and exit`);
318
- log(` -h, --help This help`);
303
+ log(` Antes (una vez): gh auth login -h github.com -s repo -s read:org -w`);
304
+ log(` Requisitos: Node 18+, git, GitHub CLI (gh)`);
305
+ log(``);
306
+ log(` onboard implica --install salvo --no-install`);
307
+ log(` -y, --yes Sin menú interactivo (equipo por defecto del JSON)`);
308
+ log(` -t, --team <slug> Equipo en komplian-team-repos.json`);
309
+ log(` -i, --install npm install en cada repo con package.json`);
310
+ log(` --no-install No ejecutar npm install`);
311
+ log(` --all-repos Todos los repos visibles (menos exclude_from_all)`);
312
+ log(` --ssh Clonar con git@github.com:…`);
313
+ log(` --list-teams Lista slugs de equipo (solo JSON) y sale`);
314
+ log(` -h, --help`);
319
315
  }
320
316
 
321
317
  function parseArgs(argv) {
@@ -343,7 +339,7 @@ function parseArgs(argv) {
343
339
  else if (a === "-t" || a === "--team") {
344
340
  out.team = argv[++i] || "";
345
341
  } else if (a.startsWith("-")) {
346
- log(`${c.red}✗${c.reset} Unknown option: ${a}`);
342
+ log(`${c.red}✗${c.reset} Opción desconocida: ${a}`);
347
343
  usage();
348
344
  process.exit(1);
349
345
  } else rest.push(a);
@@ -355,14 +351,14 @@ function parseArgs(argv) {
355
351
  async function pickTeam(cfg) {
356
352
  const teams = Object.keys(cfg.teams || {}).sort();
357
353
  const def = (cfg.default_team || "").trim();
358
- log(`${c.cyan}Teams:${c.reset}`);
354
+ log(`${c.cyan}Equipos:${c.reset}`);
359
355
  teams.forEach((t, i) => {
360
- const mark = t === def ? ` ${c.dim}(default)${c.reset}` : "";
356
+ const mark = t === def ? ` ${c.dim}(por defecto)${c.reset}` : "";
361
357
  log(` ${i + 1}. ${t}${mark}`);
362
358
  });
363
359
  const rl = createInterface({ input, output });
364
360
  const ans = await rl.question(
365
- `\n${c.bold}Choose number or name${c.reset} [${def}]: `
361
+ `\n${c.bold}Número o nombre${c.reset} [${def}]: `
366
362
  );
367
363
  rl.close();
368
364
  const trimmed = (ans || "").trim();
@@ -396,52 +392,47 @@ async function main() {
396
392
  return;
397
393
  }
398
394
 
399
- if (args.listTeams) {
400
- if (!existsSync(configPath)) {
401
- log(`${c.red}✗${c.reset} Missing ${configPath}`);
402
- process.exit(1);
403
- }
404
- needCmd("gh");
405
- if (!ghOk()) {
406
- log(`${c.red}✗${c.reset} Not logged in to GitHub. Run: ${c.bold}gh auth login -h github.com -s repo -s read:org -w${c.reset}`);
407
- process.exit(1);
408
- }
409
- const cfg = loadConfig(configPath);
410
- const orgList = process.env.KOMPLIAN_ORG || cfg.org || "Komplian";
411
- verifyOrgMembership(orgList);
412
- log(Object.keys(cfg.teams || {}).sort().join("\n"));
413
- return;
414
- }
415
-
416
395
  if (!existsSync(configPath)) {
417
- log(`${c.red}✗${c.reset} Missing config: ${configPath}`);
396
+ log(`${c.red}✗${c.reset} Falta la config: ${configPath}`);
418
397
  process.exit(1);
419
398
  }
420
399
 
421
- needCmd("git");
422
- needCmd("gh");
400
+ const cfg = loadConfig(configPath);
401
+
402
+ if (args.listTeams) {
403
+ log(Object.keys(cfg.teams || {}).sort().join("\n"));
404
+ return;
405
+ }
423
406
 
424
407
  const nodeMajor = Number(process.versions.node.split(".")[0], 10);
425
408
  if (nodeMajor < 18) {
426
- log(`${c.red}✗${c.reset} Need Node 18+. You have ${process.versions.node}.`);
409
+ log(`${c.red}✗${c.reset} Hace falta Node 18+. Tienes ${process.versions.node}.`);
427
410
  process.exit(1);
428
411
  }
429
412
 
413
+ needCmd(
414
+ "git",
415
+ `${c.dim}https://git-scm.com${c.reset}`
416
+ );
417
+ needCmd(
418
+ "gh",
419
+ `${c.dim}https://cli.github.com${c.reset}`
420
+ );
421
+
430
422
  if (!ghOk()) {
423
+ log(`${c.red}✗${c.reset} No hay sesión en GitHub CLI.`);
431
424
  if (!runGhAuth()) {
432
- log(`${c.red}✗${c.reset} GitHub authentication did not complete.`);
425
+ log(`${c.red}✗${c.reset} Autenticación cancelada.`);
433
426
  process.exit(1);
434
427
  }
435
428
  }
436
- log(`${c.green}✓${c.reset} GitHub CLI session OK`);
429
+ log(`${c.green}✓${c.reset} GitHub CLI OK`);
437
430
 
438
- const cfg = loadConfig(configPath);
439
431
  const org = process.env.KOMPLIAN_ORG || cfg.org || "Komplian";
440
-
441
432
  verifyOrgMembership(org);
442
433
  logGhIdentity();
443
434
  log(
444
- `${c.green}✓${c.reset} Org ${c.bold}${org}${c.reset}: ${c.dim}active membership verified (GitHub API)${c.reset}`
435
+ `${c.green}✓${c.reset} Org ${c.bold}${org}${c.reset}: ${c.dim}membresía activa${c.reset}`
445
436
  );
446
437
 
447
438
  banner();
@@ -453,7 +444,7 @@ async function main() {
453
444
 
454
445
  const repos = resolveRepos(cfg, org, team, args.allRepos);
455
446
  if (repos.length === 0) {
456
- log(`${c.red}✗${c.reset} No repositories to clone (permissions / team / org).`);
447
+ log(`${c.red}✗${c.reset} No hay repos que clonar (permisos / equipo / org).`);
457
448
  process.exit(1);
458
449
  }
459
450
 
@@ -463,7 +454,7 @@ async function main() {
463
454
  }
464
455
  const abs = resolve(workspace.replace(/^~(?=$|[/\\])/, homedir()));
465
456
  if (!isSafeTargetDir(abs)) {
466
- log(`${c.red}✗${c.reset} Refusing unsafe target directory: ${abs}`);
457
+ log(`${c.red}✗${c.reset} Carpeta destino no segura: ${abs}`);
467
458
  process.exit(1);
468
459
  }
469
460
 
@@ -471,7 +462,7 @@ async function main() {
471
462
  log("");
472
463
  log(`${c.cyan}━━ Workspace ━━${c.reset} ${c.bold}${abs}${c.reset}`);
473
464
  log(`${c.cyan}━━ Org ━━${c.reset} ${c.bold}${org}${c.reset}`);
474
- if (team) log(`${c.cyan}━━ Team ━━${c.reset} ${c.bold}${team}${c.reset}`);
465
+ if (team) log(`${c.cyan}━━ Equipo ━━${c.reset} ${c.bold}${team}${c.reset}`);
475
466
  log(`${c.cyan}━━ Repos (${repos.length}) ━━${c.reset}`);
476
467
  log("");
477
468
 
@@ -484,17 +475,16 @@ async function main() {
484
475
  copyCursorPack(abs, process.env.KOMPLIAN_CURSOR_REPO);
485
476
 
486
477
  if (args.install) {
487
- needCmd("npm");
488
478
  npmInstallEach(abs);
489
479
  }
490
480
 
491
481
  log("");
492
- log(`${c.cyan}━━ Done ━━${c.reset}`);
482
+ log(`${c.cyan}━━ Listo ━━${c.reset}`);
493
483
  if (failed > 0) {
494
- log(`${c.yellow}○${c.reset} ${failed} repo(s) failedcheck access and retry.`);
484
+ log(`${c.yellow}○${c.reset} ${failed} repo(s) fallaronrevisa acceso y reintenta.`);
495
485
  }
496
- log(`${c.green}✓${c.reset} Open in Cursor: ${c.bold}File → Open Folder → ${abs}${c.reset}`);
497
- log(`${c.dim} Copy .env.example → .env per project; secrets via 1Password — never commit.${c.reset}`);
486
+ log(`${c.green}✓${c.reset} Cursor: ${c.bold}File → Open Folder → ${abs}${c.reset}`);
487
+ log(`${c.dim} .env.example → .env por proyecto; secretos en 1Password — nunca commit.${c.reset}`);
498
488
  }
499
489
 
500
490
  main().catch((e) => {
@@ -1,14 +1,14 @@
1
1
  {
2
- "_comment": "Map team slug (-t) repo names in org. Align slugs with GitHub Team names or internal labels. Adjust when teams/repos change.",
2
+ "_comment": "Teams = solo repos de producto (app, api, web, admin, docs). exclude_from_all evita ai-tools, scripts, infra, odoo-module en --all-repos.",
3
3
  "org": "Komplian",
4
4
  "default_team": "platform",
5
- "exclude_from_all": [],
5
+ "exclude_from_all": ["ai-tools", "scripts", "infra", "odoo-module"],
6
6
  "teams": {
7
- "platform": ["app", "api", "web", "admin", "docs", "ai-tools"],
8
- "backend": ["api", "ai-tools"],
9
- "frontend": ["app", "web", "docs", "ai-tools"],
10
- "admin": ["admin", "api", "ai-tools"],
11
- "docs": ["docs", "web", "ai-tools"],
12
- "growth": ["web", "docs", "ai-tools"]
7
+ "platform": ["app", "api", "web", "admin", "docs"],
8
+ "backend": ["api"],
9
+ "frontend": ["app", "web", "docs"],
10
+ "admin": ["admin", "api"],
11
+ "docs": ["docs", "web"],
12
+ "growth": ["web", "docs"]
13
13
  }
14
14
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "komplian",
3
- "version": "0.1.0",
4
- "description": "Komplian developer workspace setup: GitHub CLI auth + clone repos by team (zero manual repo clone for developers use npx).",
3
+ "version": "0.3.1",
4
+ "description": "Komplian developer workspace setup: GitHub CLI (browser login) + git clone by team. Node 18+, git, gh no OAuth App to register.",
5
5
  "type": "module",
6
6
  "engines": {
7
7
  "node": ">=18"