komplian 0.1.0 → 0.3.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 +10 -12
- package/komplian-onboard.mjs +82 -99
- package/komplian-team-repos.json +8 -8
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,23 +1,21 @@
|
|
|
1
1
|
# komplian (npm CLI)
|
|
2
2
|
|
|
3
|
-
**Developers
|
|
3
|
+
**Developers**
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
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
|
-
|
|
9
|
+
No OAuth App registration — `gh` uses GitHub’s built-in flow. Default workspace: `~/komplian`.
|
|
10
10
|
|
|
11
|
-
**Maintainers:** publish
|
|
11
|
+
**Maintainers:** publish from **`scripts/`** (folder with `package.json`), not the monorepo root:
|
|
12
12
|
|
|
13
13
|
```bash
|
|
14
|
-
cd path/to/monorepo/scripts
|
|
15
|
-
npm login
|
|
14
|
+
cd path/to/monorepo/scripts
|
|
15
|
+
npm login
|
|
16
16
|
npm publish --access public
|
|
17
17
|
```
|
|
18
18
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
Bump `version` in `package.json` before each publish.
|
|
19
|
+
Bump `version` before each publish.
|
|
22
20
|
|
|
23
|
-
**If publish fails with `403` /
|
|
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`).
|
package/komplian-onboard.mjs
CHANGED
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/**
|
|
3
|
-
* Komplian onboard —
|
|
4
|
-
*
|
|
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
|
-
*
|
|
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 ·
|
|
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}
|
|
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}
|
|
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}
|
|
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}
|
|
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}
|
|
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}
|
|
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}
|
|
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,17 @@ function logGhIdentity() {
|
|
|
132
121
|
try {
|
|
133
122
|
const j = JSON.parse(u.stdout);
|
|
134
123
|
if (j.login) {
|
|
135
|
-
log(`${c.dim}
|
|
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) {
|
|
131
|
+
function needCmd(name, hint = "") {
|
|
143
132
|
const r = spawnSync("command", ["-v", name], { shell: true, stdio: "ignore" });
|
|
144
133
|
if (r.status !== 0) {
|
|
145
|
-
log(`${c.red}✗${c.reset}
|
|
134
|
+
log(`${c.red}✗${c.reset} Falta ${c.bold}${name}${c.reset} en el PATH.${hint ? ` ${hint}` : ""}`);
|
|
146
135
|
process.exit(1);
|
|
147
136
|
}
|
|
148
137
|
}
|
|
@@ -158,7 +147,7 @@ function ghRepoList(org) {
|
|
|
158
147
|
{ encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] }
|
|
159
148
|
);
|
|
160
149
|
if (r.status !== 0) {
|
|
161
|
-
log(`${c.red}✗${c.reset} gh repo list
|
|
150
|
+
log(`${c.red}✗${c.reset} gh repo list falló:\n${r.stderr || r.stdout}`);
|
|
162
151
|
process.exit(1);
|
|
163
152
|
}
|
|
164
153
|
const rows = JSON.parse(r.stdout || "[]");
|
|
@@ -188,7 +177,7 @@ function resolveRepos(cfg, org, teamArg, allRepos) {
|
|
|
188
177
|
const teams = cfg.teams || {};
|
|
189
178
|
if (!teams[team]) {
|
|
190
179
|
log(
|
|
191
|
-
`${c.red}✗${c.reset}
|
|
180
|
+
`${c.red}✗${c.reset} Equipo desconocido ${c.bold}${team}${c.reset}. Válidos: ${Object.keys(teams).sort().join(", ")}`
|
|
192
181
|
);
|
|
193
182
|
process.exit(2);
|
|
194
183
|
}
|
|
@@ -197,7 +186,7 @@ function resolveRepos(cfg, org, teamArg, allRepos) {
|
|
|
197
186
|
const out = [];
|
|
198
187
|
for (const name of [...allowed].sort()) {
|
|
199
188
|
if (!visible.has(name)) {
|
|
200
|
-
log(`${c.dim}○
|
|
189
|
+
log(`${c.dim}○ omito ${org}/${name} (sin acceso o no está en la org)${c.reset}`);
|
|
201
190
|
continue;
|
|
202
191
|
}
|
|
203
192
|
out.push(name);
|
|
@@ -214,15 +203,15 @@ function isSafeTargetDir(abs) {
|
|
|
214
203
|
function cloudLine(org, name, status) {
|
|
215
204
|
const cloud = `${c.blue}☁${c.reset}`;
|
|
216
205
|
if (status === "ok") {
|
|
217
|
-
return ` ${cloud} ${c.green}✓${c.reset} ${c.bold}${org}/${name}${c.reset} ${c.dim}
|
|
206
|
+
return ` ${cloud} ${c.green}✓${c.reset} ${c.bold}${org}/${name}${c.reset} ${c.dim}clonado${c.reset}`;
|
|
218
207
|
}
|
|
219
208
|
if (status === "skip") {
|
|
220
|
-
return ` ${cloud} ${c.yellow}○${c.reset} ${org}/${name} ${c.dim}(
|
|
209
|
+
return ` ${cloud} ${c.yellow}○${c.reset} ${org}/${name} ${c.dim}(ya existe)${c.reset}`;
|
|
221
210
|
}
|
|
222
211
|
if (status === "fail") {
|
|
223
|
-
return ` ${cloud} ${c.red}✗${c.reset} ${org}/${name} ${c.dim}
|
|
212
|
+
return ` ${cloud} ${c.red}✗${c.reset} ${org}/${name} ${c.dim}falló${c.reset}`;
|
|
224
213
|
}
|
|
225
|
-
return ` ${cloud} ${c.cyan}…${c.reset} ${org}/${name} ${c.dim}
|
|
214
|
+
return ` ${cloud} ${c.cyan}…${c.reset} ${org}/${name} ${c.dim}clonando…${c.reset}`;
|
|
226
215
|
}
|
|
227
216
|
|
|
228
217
|
function cloneOne(org, name, workspace, useSsh) {
|
|
@@ -256,7 +245,7 @@ function copyCursorPack(workspace, cursorRepoUrl) {
|
|
|
256
245
|
if (cursorRepoUrl && String(cursorRepoUrl).trim()) {
|
|
257
246
|
const tmp = join(workspace, ".cursor-bootstrap-tmp");
|
|
258
247
|
rmSync(tmp, { recursive: true, force: true });
|
|
259
|
-
log(`${c.dim}→${c.reset}
|
|
248
|
+
log(`${c.dim}→${c.reset} Cursor pack (clone superficial)…`);
|
|
260
249
|
const r = spawnSync("git", ["clone", "--depth", "1", String(cursorRepoUrl).trim(), tmp], {
|
|
261
250
|
cwd: workspace,
|
|
262
251
|
stdio: ["ignore", "pipe", "pipe"],
|
|
@@ -270,33 +259,30 @@ function copyCursorPack(workspace, cursorRepoUrl) {
|
|
|
270
259
|
return;
|
|
271
260
|
}
|
|
272
261
|
rmSync(tmp, { recursive: true, force: true });
|
|
273
|
-
log(`${c.yellow}○${c.reset} KOMPLIAN_CURSOR_REPO clone
|
|
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;
|
|
262
|
+
log(`${c.yellow}○${c.reset} KOMPLIAN_CURSOR_REPO: falló el clone o no hay .cursor/ en la raíz`);
|
|
282
263
|
}
|
|
283
264
|
log(
|
|
284
|
-
`${c.dim}○
|
|
265
|
+
`${c.dim}○ Sin .cursor/ en el workspace (opcional: KOMPLIAN_CURSOR_REPO=<url git>).${c.reset}`
|
|
285
266
|
);
|
|
286
267
|
}
|
|
287
268
|
|
|
288
269
|
function npmInstallEach(workspace) {
|
|
289
270
|
log("");
|
|
290
|
-
log(`${c.cyan}━━ npm install
|
|
271
|
+
log(`${c.cyan}━━ npm install por repo ━━${c.reset}`);
|
|
272
|
+
const r = spawnSync("command", ["-v", "npm"], { shell: true, stdio: "ignore" });
|
|
273
|
+
if (r.status !== 0) {
|
|
274
|
+
log(`${c.yellow}○${c.reset} npm no está en PATH — omito installs`);
|
|
275
|
+
return;
|
|
276
|
+
}
|
|
291
277
|
for (const ent of readdirSync(workspace)) {
|
|
292
278
|
const d = join(workspace, ent);
|
|
293
279
|
if (!statSync(d).isDirectory()) continue;
|
|
294
280
|
const pkg = join(d, "package.json");
|
|
295
281
|
if (!existsSync(pkg)) continue;
|
|
296
282
|
log(`${c.dim}→${c.reset} ${ent}`);
|
|
297
|
-
const
|
|
298
|
-
if (
|
|
299
|
-
log(`${c.yellow}○${c.reset} npm install
|
|
283
|
+
const ir = spawnSync("npm", ["install"], { cwd: d, stdio: "inherit" });
|
|
284
|
+
if (ir.status !== 0) {
|
|
285
|
+
log(`${c.yellow}○${c.reset} npm install con avisos en ${ent}`);
|
|
300
286
|
} else {
|
|
301
287
|
log(`${c.green}✓${c.reset} ${ent}`);
|
|
302
288
|
}
|
|
@@ -304,18 +290,21 @@ function npmInstallEach(workspace) {
|
|
|
304
290
|
}
|
|
305
291
|
|
|
306
292
|
function usage() {
|
|
307
|
-
log(`
|
|
308
|
-
log(` npx komplian onboard --yes
|
|
293
|
+
log(`Uso: komplian onboard [opciones] [carpeta]`);
|
|
294
|
+
log(` npx komplian onboard --yes`);
|
|
309
295
|
log(``);
|
|
310
|
-
log(`
|
|
311
|
-
log(`
|
|
312
|
-
log(
|
|
313
|
-
log(`
|
|
314
|
-
log(`
|
|
315
|
-
log(`
|
|
316
|
-
log(` --
|
|
317
|
-
log(` --
|
|
318
|
-
log(` -
|
|
296
|
+
log(` Antes (una vez): gh auth login -h github.com -s repo -s read:org -w`);
|
|
297
|
+
log(` Requisitos: Node 18+, git, GitHub CLI (gh)`);
|
|
298
|
+
log(``);
|
|
299
|
+
log(` onboard implica --install salvo --no-install`);
|
|
300
|
+
log(` -y, --yes Sin menú interactivo (equipo por defecto del JSON)`);
|
|
301
|
+
log(` -t, --team <slug> Equipo en komplian-team-repos.json`);
|
|
302
|
+
log(` -i, --install npm install en cada repo con package.json`);
|
|
303
|
+
log(` --no-install No ejecutar npm install`);
|
|
304
|
+
log(` --all-repos Todos los repos visibles (menos exclude_from_all)`);
|
|
305
|
+
log(` --ssh Clonar con git@github.com:…`);
|
|
306
|
+
log(` --list-teams Lista slugs de equipo (solo JSON) y sale`);
|
|
307
|
+
log(` -h, --help`);
|
|
319
308
|
}
|
|
320
309
|
|
|
321
310
|
function parseArgs(argv) {
|
|
@@ -343,7 +332,7 @@ function parseArgs(argv) {
|
|
|
343
332
|
else if (a === "-t" || a === "--team") {
|
|
344
333
|
out.team = argv[++i] || "";
|
|
345
334
|
} else if (a.startsWith("-")) {
|
|
346
|
-
log(`${c.red}✗${c.reset}
|
|
335
|
+
log(`${c.red}✗${c.reset} Opción desconocida: ${a}`);
|
|
347
336
|
usage();
|
|
348
337
|
process.exit(1);
|
|
349
338
|
} else rest.push(a);
|
|
@@ -355,14 +344,14 @@ function parseArgs(argv) {
|
|
|
355
344
|
async function pickTeam(cfg) {
|
|
356
345
|
const teams = Object.keys(cfg.teams || {}).sort();
|
|
357
346
|
const def = (cfg.default_team || "").trim();
|
|
358
|
-
log(`${c.cyan}
|
|
347
|
+
log(`${c.cyan}Equipos:${c.reset}`);
|
|
359
348
|
teams.forEach((t, i) => {
|
|
360
|
-
const mark = t === def ? ` ${c.dim}(
|
|
349
|
+
const mark = t === def ? ` ${c.dim}(por defecto)${c.reset}` : "";
|
|
361
350
|
log(` ${i + 1}. ${t}${mark}`);
|
|
362
351
|
});
|
|
363
352
|
const rl = createInterface({ input, output });
|
|
364
353
|
const ans = await rl.question(
|
|
365
|
-
`\n${c.bold}
|
|
354
|
+
`\n${c.bold}Número o nombre${c.reset} [${def}]: `
|
|
366
355
|
);
|
|
367
356
|
rl.close();
|
|
368
357
|
const trimmed = (ans || "").trim();
|
|
@@ -396,52 +385,47 @@ async function main() {
|
|
|
396
385
|
return;
|
|
397
386
|
}
|
|
398
387
|
|
|
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
388
|
if (!existsSync(configPath)) {
|
|
417
|
-
log(`${c.red}✗${c.reset}
|
|
389
|
+
log(`${c.red}✗${c.reset} Falta la config: ${configPath}`);
|
|
418
390
|
process.exit(1);
|
|
419
391
|
}
|
|
420
392
|
|
|
421
|
-
|
|
422
|
-
|
|
393
|
+
const cfg = loadConfig(configPath);
|
|
394
|
+
|
|
395
|
+
if (args.listTeams) {
|
|
396
|
+
log(Object.keys(cfg.teams || {}).sort().join("\n"));
|
|
397
|
+
return;
|
|
398
|
+
}
|
|
423
399
|
|
|
424
400
|
const nodeMajor = Number(process.versions.node.split(".")[0], 10);
|
|
425
401
|
if (nodeMajor < 18) {
|
|
426
|
-
log(`${c.red}✗${c.reset}
|
|
402
|
+
log(`${c.red}✗${c.reset} Hace falta Node 18+. Tienes ${process.versions.node}.`);
|
|
427
403
|
process.exit(1);
|
|
428
404
|
}
|
|
429
405
|
|
|
406
|
+
needCmd(
|
|
407
|
+
"git",
|
|
408
|
+
`${c.dim}https://git-scm.com${c.reset}`
|
|
409
|
+
);
|
|
410
|
+
needCmd(
|
|
411
|
+
"gh",
|
|
412
|
+
`${c.dim}https://cli.github.com${c.reset}`
|
|
413
|
+
);
|
|
414
|
+
|
|
430
415
|
if (!ghOk()) {
|
|
416
|
+
log(`${c.red}✗${c.reset} No hay sesión en GitHub CLI.`);
|
|
431
417
|
if (!runGhAuth()) {
|
|
432
|
-
log(`${c.red}✗${c.reset}
|
|
418
|
+
log(`${c.red}✗${c.reset} Autenticación cancelada.`);
|
|
433
419
|
process.exit(1);
|
|
434
420
|
}
|
|
435
421
|
}
|
|
436
|
-
log(`${c.green}✓${c.reset} GitHub CLI
|
|
422
|
+
log(`${c.green}✓${c.reset} GitHub CLI OK`);
|
|
437
423
|
|
|
438
|
-
const cfg = loadConfig(configPath);
|
|
439
424
|
const org = process.env.KOMPLIAN_ORG || cfg.org || "Komplian";
|
|
440
|
-
|
|
441
425
|
verifyOrgMembership(org);
|
|
442
426
|
logGhIdentity();
|
|
443
427
|
log(
|
|
444
|
-
`${c.green}✓${c.reset} Org ${c.bold}${org}${c.reset}: ${c.dim}
|
|
428
|
+
`${c.green}✓${c.reset} Org ${c.bold}${org}${c.reset}: ${c.dim}membresía activa${c.reset}`
|
|
445
429
|
);
|
|
446
430
|
|
|
447
431
|
banner();
|
|
@@ -453,7 +437,7 @@ async function main() {
|
|
|
453
437
|
|
|
454
438
|
const repos = resolveRepos(cfg, org, team, args.allRepos);
|
|
455
439
|
if (repos.length === 0) {
|
|
456
|
-
log(`${c.red}✗${c.reset} No
|
|
440
|
+
log(`${c.red}✗${c.reset} No hay repos que clonar (permisos / equipo / org).`);
|
|
457
441
|
process.exit(1);
|
|
458
442
|
}
|
|
459
443
|
|
|
@@ -463,7 +447,7 @@ async function main() {
|
|
|
463
447
|
}
|
|
464
448
|
const abs = resolve(workspace.replace(/^~(?=$|[/\\])/, homedir()));
|
|
465
449
|
if (!isSafeTargetDir(abs)) {
|
|
466
|
-
log(`${c.red}✗${c.reset}
|
|
450
|
+
log(`${c.red}✗${c.reset} Carpeta destino no segura: ${abs}`);
|
|
467
451
|
process.exit(1);
|
|
468
452
|
}
|
|
469
453
|
|
|
@@ -471,7 +455,7 @@ async function main() {
|
|
|
471
455
|
log("");
|
|
472
456
|
log(`${c.cyan}━━ Workspace ━━${c.reset} ${c.bold}${abs}${c.reset}`);
|
|
473
457
|
log(`${c.cyan}━━ Org ━━${c.reset} ${c.bold}${org}${c.reset}`);
|
|
474
|
-
if (team) log(`${c.cyan}━━
|
|
458
|
+
if (team) log(`${c.cyan}━━ Equipo ━━${c.reset} ${c.bold}${team}${c.reset}`);
|
|
475
459
|
log(`${c.cyan}━━ Repos (${repos.length}) ━━${c.reset}`);
|
|
476
460
|
log("");
|
|
477
461
|
|
|
@@ -484,17 +468,16 @@ async function main() {
|
|
|
484
468
|
copyCursorPack(abs, process.env.KOMPLIAN_CURSOR_REPO);
|
|
485
469
|
|
|
486
470
|
if (args.install) {
|
|
487
|
-
needCmd("npm");
|
|
488
471
|
npmInstallEach(abs);
|
|
489
472
|
}
|
|
490
473
|
|
|
491
474
|
log("");
|
|
492
|
-
log(`${c.cyan}━━
|
|
475
|
+
log(`${c.cyan}━━ Listo ━━${c.reset}`);
|
|
493
476
|
if (failed > 0) {
|
|
494
|
-
log(`${c.yellow}○${c.reset} ${failed} repo(s)
|
|
477
|
+
log(`${c.yellow}○${c.reset} ${failed} repo(s) fallaron — revisa acceso y reintenta.`);
|
|
495
478
|
}
|
|
496
|
-
log(`${c.green}✓${c.reset}
|
|
497
|
-
log(`${c.dim}
|
|
479
|
+
log(`${c.green}✓${c.reset} Cursor: ${c.bold}File → Open Folder → ${abs}${c.reset}`);
|
|
480
|
+
log(`${c.dim} .env.example → .env por proyecto; secretos en 1Password — nunca commit.${c.reset}`);
|
|
498
481
|
}
|
|
499
482
|
|
|
500
483
|
main().catch((e) => {
|
package/komplian-team-repos.json
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
{
|
|
2
|
-
"_comment": "
|
|
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"
|
|
8
|
-
"backend": ["api"
|
|
9
|
-
"frontend": ["app", "web", "docs"
|
|
10
|
-
"admin": ["admin", "api"
|
|
11
|
-
"docs": ["docs", "web"
|
|
12
|
-
"growth": ["web", "docs"
|
|
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.
|
|
4
|
-
"description": "Komplian developer workspace setup: GitHub CLI
|
|
3
|
+
"version": "0.3.0",
|
|
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"
|