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 +10 -12
- package/komplian-onboard.mjs +91 -101
- 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,25 @@ 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
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
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
|
|
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}
|
|
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}○
|
|
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}
|
|
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}(
|
|
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}
|
|
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}
|
|
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}
|
|
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
|
|
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}○
|
|
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
|
|
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
|
|
298
|
-
if (
|
|
299
|
-
log(`${c.yellow}○${c.reset} npm install
|
|
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(`
|
|
308
|
-
log(` npx komplian onboard --yes
|
|
300
|
+
log(`Uso: komplian onboard [opciones] [carpeta]`);
|
|
301
|
+
log(` npx komplian onboard --yes`);
|
|
309
302
|
log(``);
|
|
310
|
-
log(`
|
|
311
|
-
log(`
|
|
312
|
-
log(
|
|
313
|
-
log(`
|
|
314
|
-
log(`
|
|
315
|
-
log(`
|
|
316
|
-
log(` --
|
|
317
|
-
log(` --
|
|
318
|
-
log(` -
|
|
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}
|
|
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}
|
|
354
|
+
log(`${c.cyan}Equipos:${c.reset}`);
|
|
359
355
|
teams.forEach((t, i) => {
|
|
360
|
-
const mark = t === def ? ` ${c.dim}(
|
|
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}
|
|
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}
|
|
396
|
+
log(`${c.red}✗${c.reset} Falta la config: ${configPath}`);
|
|
418
397
|
process.exit(1);
|
|
419
398
|
}
|
|
420
399
|
|
|
421
|
-
|
|
422
|
-
|
|
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}
|
|
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}
|
|
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
|
|
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}
|
|
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
|
|
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}
|
|
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}━━
|
|
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}━━
|
|
482
|
+
log(`${c.cyan}━━ Listo ━━${c.reset}`);
|
|
493
483
|
if (failed > 0) {
|
|
494
|
-
log(`${c.yellow}○${c.reset} ${failed} repo(s)
|
|
484
|
+
log(`${c.yellow}○${c.reset} ${failed} repo(s) fallaron — revisa acceso y reintenta.`);
|
|
495
485
|
}
|
|
496
|
-
log(`${c.green}✓${c.reset}
|
|
497
|
-
log(`${c.dim}
|
|
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) => {
|
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.1
|
|
4
|
-
"description": "Komplian developer workspace setup: GitHub CLI
|
|
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"
|