komplian 0.7.0 → 0.7.2
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 +1 -1
- package/komplian-db-all-dev.mjs +32 -29
- package/komplian-localhost.mjs +73 -48
- package/komplian-mcp-tools.mjs +46 -41
- package/komplian-onboard.mjs +237 -116
- package/komplian-postman.mjs +35 -27
- package/komplian-setup.mjs +359 -168
- package/package.json +2 -2
package/komplian-onboard.mjs
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* npx komplian onboard --yes
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
-
import { spawnSync } from "node:child_process";
|
|
11
|
+
import { spawnSync, spawn } from "node:child_process";
|
|
12
12
|
import { existsSync, readFileSync, mkdirSync, cpSync, rmSync, readdirSync, statSync } from "node:fs";
|
|
13
13
|
import { dirname, join, resolve, normalize } from "node:path";
|
|
14
14
|
import { fileURLToPath } from "node:url";
|
|
@@ -37,11 +37,17 @@ const c = {
|
|
|
37
37
|
};
|
|
38
38
|
|
|
39
39
|
function log(s = "") {
|
|
40
|
+
if (process.env.KOMPLIAN_CLI_QUIET === "1") return;
|
|
41
|
+
console.log(s);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** Branding and repo progress: always visible (even when KOMPLIAN_CLI_QUIET=1). */
|
|
45
|
+
function ux(s = "") {
|
|
40
46
|
console.log(s);
|
|
41
47
|
}
|
|
42
48
|
|
|
43
49
|
function banner() {
|
|
44
|
-
|
|
50
|
+
ux(
|
|
45
51
|
[
|
|
46
52
|
`${c.cyan}${c.bold}`,
|
|
47
53
|
"██╗ ██╗ ██████╗ ███╗ ███╗ ██████╗ ██╗ ██╗ █████╗ ███╗ ██╗",
|
|
@@ -51,12 +57,52 @@ function banner() {
|
|
|
51
57
|
"██║ ██╗ ╚██████╔╝ ██║ ╚═╝ ██║ ██║ ███████╗ ██║ ██║ ██║ ██║ ╚████║",
|
|
52
58
|
"╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝ ╚═╝ ╚══════╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═══╝",
|
|
53
59
|
`${c.reset}`,
|
|
54
|
-
`${c.dim} Secure setup · GitHub CLI · git clone
|
|
60
|
+
`${c.dim} Secure setup · GitHub CLI · git clone${c.reset}`,
|
|
55
61
|
"",
|
|
56
62
|
].join("\n")
|
|
57
63
|
);
|
|
58
64
|
}
|
|
59
65
|
|
|
66
|
+
function startRepoSpinner(repoName) {
|
|
67
|
+
const frames = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
|
|
68
|
+
const cloud = `${c.blue}☁${c.reset}`;
|
|
69
|
+
let i = 0;
|
|
70
|
+
const id = setInterval(() => {
|
|
71
|
+
const fr = frames[i++ % frames.length];
|
|
72
|
+
process.stdout.write(
|
|
73
|
+
`\r ${cloud} ${c.cyan}${fr}${c.reset} ${c.bold}${repoName}${c.reset} ${c.dim}…${c.reset} `
|
|
74
|
+
);
|
|
75
|
+
}, 90);
|
|
76
|
+
return {
|
|
77
|
+
stop() {
|
|
78
|
+
clearInterval(id);
|
|
79
|
+
process.stdout.write("\r\x1b[K");
|
|
80
|
+
},
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function runSpawn(cmd, args, cwd) {
|
|
85
|
+
return new Promise((resolve) => {
|
|
86
|
+
const child = spawn(cmd, args, {
|
|
87
|
+
...spawnWin({
|
|
88
|
+
cwd,
|
|
89
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
90
|
+
}),
|
|
91
|
+
});
|
|
92
|
+
let stderr = "";
|
|
93
|
+
child.stderr?.setEncoding?.("utf8");
|
|
94
|
+
child.stderr?.on("data", (d) => {
|
|
95
|
+
stderr += String(d);
|
|
96
|
+
});
|
|
97
|
+
child.on("close", (code) => {
|
|
98
|
+
resolve({ status: code === 0 ? 0 : code ?? 1, stderr });
|
|
99
|
+
});
|
|
100
|
+
child.on("error", (err) => {
|
|
101
|
+
resolve({ status: 1, stderr: String(err?.message || err) });
|
|
102
|
+
});
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
|
|
60
106
|
function ghOk() {
|
|
61
107
|
const r = spawnSync(
|
|
62
108
|
"gh",
|
|
@@ -70,8 +116,8 @@ function ghOk() {
|
|
|
70
116
|
}
|
|
71
117
|
|
|
72
118
|
function runGhAuth() {
|
|
73
|
-
|
|
74
|
-
`${c.yellow}→${c.reset}
|
|
119
|
+
ux(
|
|
120
|
+
`${c.yellow}→${c.reset} Opening ${c.bold}GitHub${c.reset} in your browser (OAuth). Your password is not shown here.`
|
|
75
121
|
);
|
|
76
122
|
const r = spawnSync(
|
|
77
123
|
"gh",
|
|
@@ -101,32 +147,32 @@ function verifyOrgMembership(org) {
|
|
|
101
147
|
try {
|
|
102
148
|
const j = JSON.parse(mem.stdout);
|
|
103
149
|
if (j.state === "active") return;
|
|
104
|
-
|
|
105
|
-
`${c.red}✗${c.reset}
|
|
150
|
+
console.error(
|
|
151
|
+
`${c.red}✗${c.reset} Org ${c.bold}${org}${c.reset} membership not active (state: ${j.state || "?"}).`
|
|
106
152
|
);
|
|
107
153
|
process.exit(1);
|
|
108
154
|
} catch {
|
|
109
|
-
|
|
155
|
+
console.error(`${c.red}✗${c.reset} Invalid response while verifying org membership.`);
|
|
110
156
|
process.exit(1);
|
|
111
157
|
}
|
|
112
158
|
}
|
|
113
159
|
const hint = (mem.stderr + mem.stdout).toLowerCase();
|
|
114
160
|
if (hint.includes("404") || hint.includes("not found")) {
|
|
115
|
-
|
|
116
|
-
`${c.red}✗${c.reset}
|
|
161
|
+
console.error(
|
|
162
|
+
`${c.red}✗${c.reset} This account is ${c.bold}not a member${c.reset} of org ${c.bold}${org}${c.reset}.`
|
|
117
163
|
);
|
|
118
|
-
|
|
119
|
-
`${c.dim}
|
|
164
|
+
console.error(
|
|
165
|
+
`${c.dim} Wrong account? gh auth logout && gh auth login -h github.com -s repo -s read:org -w${c.reset}`
|
|
120
166
|
);
|
|
121
167
|
process.exit(1);
|
|
122
168
|
}
|
|
123
169
|
if (hint.includes("403") || hint.includes("read:org") || hint.includes("scope")) {
|
|
124
|
-
|
|
125
|
-
|
|
170
|
+
console.error(`${c.red}✗${c.reset} GitHub CLI needs ${c.bold}read:org${c.reset} scope.`);
|
|
171
|
+
console.error(`${c.dim} gh auth refresh -h github.com -s repo -s read:org${c.reset}`);
|
|
126
172
|
process.exit(1);
|
|
127
173
|
}
|
|
128
|
-
|
|
129
|
-
`${c.red}✗${c.reset}
|
|
174
|
+
console.error(
|
|
175
|
+
`${c.red}✗${c.reset} Could not verify org (exit ${mem.status}):\n${c.dim}${(mem.stderr || mem.stdout).trim()}${c.reset}`
|
|
130
176
|
);
|
|
131
177
|
process.exit(1);
|
|
132
178
|
}
|
|
@@ -158,10 +204,12 @@ function needCmd(name, hint = "") {
|
|
|
158
204
|
? canRun("git", ["--version"])
|
|
159
205
|
: canRun(name, ["--version"]);
|
|
160
206
|
if (!ok) {
|
|
161
|
-
|
|
207
|
+
console.error(
|
|
208
|
+
`${c.red}✗${c.reset} Cannot run ${c.bold}${name}${c.reset} (PATH or install?).${hint ? ` ${hint}` : ""}`
|
|
209
|
+
);
|
|
162
210
|
if (IS_WIN) {
|
|
163
|
-
|
|
164
|
-
`${c.dim} Windows:
|
|
211
|
+
console.error(
|
|
212
|
+
`${c.dim} Windows: restart the terminal after installing gh/git, or reboot to refresh PATH.${c.reset}`
|
|
165
213
|
);
|
|
166
214
|
}
|
|
167
215
|
process.exit(1);
|
|
@@ -179,7 +227,7 @@ function ghRepoList(org) {
|
|
|
179
227
|
spawnWin({ encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] })
|
|
180
228
|
);
|
|
181
229
|
if (r.status !== 0) {
|
|
182
|
-
|
|
230
|
+
console.error(`${c.red}✗${c.reset} gh repo list failed:\n${r.stderr || r.stdout}`);
|
|
183
231
|
process.exit(1);
|
|
184
232
|
}
|
|
185
233
|
const rows = JSON.parse(r.stdout || "[]");
|
|
@@ -208,8 +256,8 @@ function resolveRepos(cfg, org, teamArg, allRepos) {
|
|
|
208
256
|
|
|
209
257
|
const teams = cfg.teams || {};
|
|
210
258
|
if (!teams[team]) {
|
|
211
|
-
|
|
212
|
-
`${c.red}✗${c.reset}
|
|
259
|
+
console.error(
|
|
260
|
+
`${c.red}✗${c.reset} Unknown team ${c.bold}${team}${c.reset}. Valid: ${Object.keys(teams).sort().join(", ")}`
|
|
213
261
|
);
|
|
214
262
|
process.exit(2);
|
|
215
263
|
}
|
|
@@ -235,42 +283,51 @@ function isSafeTargetDir(abs) {
|
|
|
235
283
|
function cloudLine(org, name, status) {
|
|
236
284
|
const cloud = `${c.blue}☁${c.reset}`;
|
|
237
285
|
if (status === "ok") {
|
|
238
|
-
return ` ${cloud} ${c.green}✓${c.reset} ${c.bold}${org}/${name}${c.reset} ${c.dim}
|
|
286
|
+
return ` ${cloud} ${c.green}✓${c.reset} ${c.bold}${org}/${name}${c.reset} ${c.dim}cloned${c.reset}`;
|
|
239
287
|
}
|
|
240
288
|
if (status === "skip") {
|
|
241
|
-
return ` ${cloud} ${c.yellow}○${c.reset} ${org}/${name} ${c.dim}(
|
|
289
|
+
return ` ${cloud} ${c.yellow}○${c.reset} ${org}/${name} ${c.dim}(already present)${c.reset}`;
|
|
242
290
|
}
|
|
243
291
|
if (status === "fail") {
|
|
244
|
-
return ` ${cloud} ${c.red}✗${c.reset} ${org}/${name} ${c.dim}
|
|
292
|
+
return ` ${cloud} ${c.red}✗${c.reset} ${org}/${name} ${c.dim}failed${c.reset}`;
|
|
245
293
|
}
|
|
246
|
-
return ` ${cloud} ${c.cyan}…${c.reset} ${org}/${name} ${c.dim}
|
|
294
|
+
return ` ${cloud} ${c.cyan}…${c.reset} ${org}/${name} ${c.dim}cloning…${c.reset}`;
|
|
247
295
|
}
|
|
248
296
|
|
|
249
|
-
function
|
|
297
|
+
async function cloneOneAsync(org, name, workspace, useSsh) {
|
|
298
|
+
const q = process.env.KOMPLIAN_CLI_QUIET === "1";
|
|
250
299
|
const gitDir = join(workspace, name, ".git");
|
|
251
300
|
if (existsSync(gitDir)) {
|
|
252
|
-
|
|
301
|
+
ux(cloudLine(org, name, "skip"));
|
|
253
302
|
return true;
|
|
254
303
|
}
|
|
255
|
-
|
|
304
|
+
|
|
256
305
|
const args = useSsh
|
|
257
306
|
? ["clone", `git@github.com:${org}/${name}.git`, name]
|
|
258
307
|
: ["repo", "clone", `${org}/${name}`, name];
|
|
259
308
|
const cmd = useSsh ? "git" : "gh";
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
309
|
+
|
|
310
|
+
const tty = process.stdout.isTTY;
|
|
311
|
+
const spin = tty ? startRepoSpinner(name) : null;
|
|
312
|
+
if (!tty) {
|
|
313
|
+
ux(
|
|
314
|
+
`${c.blue}☁${c.reset} ${c.cyan}…${c.reset} ${c.bold}${name}${c.reset} ${c.dim}cloning…${c.reset}`
|
|
315
|
+
);
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
const r = await runSpawn(cmd, args, workspace);
|
|
319
|
+
if (spin) spin.stop();
|
|
320
|
+
|
|
268
321
|
if (r.status === 0) {
|
|
269
|
-
|
|
322
|
+
ux(cloudLine(org, name, "ok"));
|
|
270
323
|
return true;
|
|
271
324
|
}
|
|
272
|
-
|
|
273
|
-
|
|
325
|
+
ux(cloudLine(org, name, "fail"));
|
|
326
|
+
const err = (r.stderr || "").trim();
|
|
327
|
+
if (err) {
|
|
328
|
+
if (q) console.error(`${c.dim}${err.slice(0, 500)}${c.reset}`);
|
|
329
|
+
else log(`${c.dim}${err}${c.reset}`);
|
|
330
|
+
}
|
|
274
331
|
return false;
|
|
275
332
|
}
|
|
276
333
|
|
|
@@ -279,7 +336,7 @@ function copyCursorPack(workspace, cursorRepoUrl) {
|
|
|
279
336
|
if (cursorRepoUrl && String(cursorRepoUrl).trim()) {
|
|
280
337
|
const tmp = join(workspace, ".cursor-bootstrap-tmp");
|
|
281
338
|
rmSync(tmp, { recursive: true, force: true });
|
|
282
|
-
|
|
339
|
+
ux(`${c.dim}→${c.reset} Cursor rules pack…`);
|
|
283
340
|
const r = spawnSync(
|
|
284
341
|
"git",
|
|
285
342
|
["clone", "--depth", "1", String(cursorRepoUrl).trim(), tmp],
|
|
@@ -293,15 +350,18 @@ function copyCursorPack(workspace, cursorRepoUrl) {
|
|
|
293
350
|
rmSync(rootCursor, { recursive: true, force: true });
|
|
294
351
|
cpSync(join(tmp, ".cursor"), rootCursor, { recursive: true });
|
|
295
352
|
rmSync(tmp, { recursive: true, force: true });
|
|
296
|
-
|
|
353
|
+
ux(`${c.green}✓${c.reset} ${c.bold}.cursor${c.reset} ${c.dim}(KOMPLIAN_CURSOR_REPO)${c.reset}`);
|
|
297
354
|
return;
|
|
298
355
|
}
|
|
299
356
|
rmSync(tmp, { recursive: true, force: true });
|
|
300
|
-
|
|
357
|
+
ux(
|
|
358
|
+
`${c.yellow}○${c.reset} KOMPLIAN_CURSOR_REPO: clone failed or no .cursor/ at repo root`
|
|
359
|
+
);
|
|
360
|
+
} else if (!process.env.KOMPLIAN_CLI_QUIET) {
|
|
361
|
+
log(
|
|
362
|
+
`${c.dim}○ No .cursor/ (optional: KOMPLIAN_CURSOR_REPO=<git url>).${c.reset}`
|
|
363
|
+
);
|
|
301
364
|
}
|
|
302
|
-
log(
|
|
303
|
-
`${c.dim}○ Sin .cursor/ en el workspace (opcional: KOMPLIAN_CURSOR_REPO=<url git>).${c.reset}`
|
|
304
|
-
);
|
|
305
365
|
}
|
|
306
366
|
|
|
307
367
|
/** Sin esto, `npm install` crea o retoca package-lock.json y git muestra cambios sin querer. */
|
|
@@ -315,6 +375,9 @@ function npmInstallOneRepo(dir, name) {
|
|
|
315
375
|
const pkg = join(dir, "package.json");
|
|
316
376
|
if (!existsSync(pkg)) return { ok: true, skipped: true };
|
|
317
377
|
|
|
378
|
+
const q = process.env.KOMPLIAN_CLI_QUIET === "1";
|
|
379
|
+
const stdio = q ? "ignore" : "inherit";
|
|
380
|
+
|
|
318
381
|
const yarnLock = join(dir, "yarn.lock");
|
|
319
382
|
const pnpmLock = join(dir, "pnpm-lock.yaml");
|
|
320
383
|
const npmLock = join(dir, "package-lock.json");
|
|
@@ -322,15 +385,21 @@ function npmInstallOneRepo(dir, name) {
|
|
|
322
385
|
if (existsSync(yarnLock)) {
|
|
323
386
|
if (!canRun("yarn", ["--version"])) {
|
|
324
387
|
log(
|
|
325
|
-
`${c.yellow}○${c.reset} ${name} ${c.dim}(yarn.lock;
|
|
388
|
+
`${c.yellow}○${c.reset} ${name} ${c.dim}(yarn.lock; install yarn or run yarn install manually)${c.reset}`
|
|
326
389
|
);
|
|
327
390
|
return { ok: true, skipped: true };
|
|
328
391
|
}
|
|
329
|
-
|
|
392
|
+
if (q) {
|
|
393
|
+
ux(
|
|
394
|
+
` ${c.blue}☁${c.reset} ${c.cyan}…${c.reset} ${c.bold}${name}${c.reset} ${c.dim}yarn install…${c.reset}`
|
|
395
|
+
);
|
|
396
|
+
} else {
|
|
397
|
+
log(`${c.dim}→${c.reset} ${name} ${c.dim}(yarn)${c.reset}`);
|
|
398
|
+
}
|
|
330
399
|
const r = spawnSync(
|
|
331
400
|
"yarn",
|
|
332
401
|
["install", "--frozen-lockfile"],
|
|
333
|
-
spawnWin({ cwd: dir, stdio
|
|
402
|
+
spawnWin({ cwd: dir, stdio })
|
|
334
403
|
);
|
|
335
404
|
return { ok: r.status === 0, skipped: false };
|
|
336
405
|
}
|
|
@@ -338,54 +407,85 @@ function npmInstallOneRepo(dir, name) {
|
|
|
338
407
|
if (existsSync(pnpmLock)) {
|
|
339
408
|
if (!canRun("pnpm", ["--version"])) {
|
|
340
409
|
log(
|
|
341
|
-
`${c.yellow}○${c.reset} ${name} ${c.dim}(pnpm-lock;
|
|
410
|
+
`${c.yellow}○${c.reset} ${name} ${c.dim}(pnpm-lock; install pnpm or run pnpm install manually)${c.reset}`
|
|
342
411
|
);
|
|
343
412
|
return { ok: true, skipped: true };
|
|
344
413
|
}
|
|
345
|
-
|
|
414
|
+
if (q) {
|
|
415
|
+
ux(
|
|
416
|
+
` ${c.blue}☁${c.reset} ${c.cyan}…${c.reset} ${c.bold}${name}${c.reset} ${c.dim}pnpm install…${c.reset}`
|
|
417
|
+
);
|
|
418
|
+
} else {
|
|
419
|
+
log(`${c.dim}→${c.reset} ${name} ${c.dim}(pnpm)${c.reset}`);
|
|
420
|
+
}
|
|
346
421
|
const r = spawnSync(
|
|
347
422
|
"pnpm",
|
|
348
423
|
["install", "--frozen-lockfile"],
|
|
349
|
-
spawnWin({ cwd: dir, stdio
|
|
424
|
+
spawnWin({ cwd: dir, stdio })
|
|
350
425
|
);
|
|
351
426
|
return { ok: r.status === 0, skipped: false };
|
|
352
427
|
}
|
|
353
428
|
|
|
354
429
|
if (!canRun("npm", ["--version"])) {
|
|
355
|
-
log(`${c.yellow}○${c.reset} npm
|
|
430
|
+
log(`${c.yellow}○${c.reset} npm not in PATH — skipping ${name}`);
|
|
356
431
|
return { ok: true, skipped: true };
|
|
357
432
|
}
|
|
358
433
|
|
|
359
434
|
const quiet = npmQuietFlags();
|
|
360
435
|
|
|
361
436
|
if (existsSync(npmLock)) {
|
|
362
|
-
|
|
363
|
-
|
|
437
|
+
if (q) {
|
|
438
|
+
ux(
|
|
439
|
+
` ${c.blue}☁${c.reset} ${c.cyan}…${c.reset} ${c.bold}${name}${c.reset} ${c.dim}npm ci…${c.reset}`
|
|
440
|
+
);
|
|
441
|
+
} else {
|
|
442
|
+
log(`${c.dim}→${c.reset} ${name} ${c.dim}(npm ci)${c.reset}`);
|
|
443
|
+
}
|
|
444
|
+
const r = spawnSync("npm", ["ci", ...quiet], spawnWin({ cwd: dir, stdio }));
|
|
364
445
|
if (r.status === 0) return { ok: true, skipped: false };
|
|
365
446
|
log(
|
|
366
|
-
`${c.yellow}○${c.reset} ${name}: npm ci
|
|
447
|
+
`${c.yellow}○${c.reset} ${name}: npm ci failed (lock out of sync?). ${c.dim}Try npm install in that repo.${c.reset}`
|
|
367
448
|
);
|
|
368
449
|
return { ok: false, skipped: false };
|
|
369
450
|
}
|
|
370
451
|
|
|
371
|
-
|
|
452
|
+
if (q) {
|
|
453
|
+
ux(
|
|
454
|
+
` ${c.blue}☁${c.reset} ${c.cyan}…${c.reset} ${c.bold}${name}${c.reset} ${c.dim}npm install…${c.reset}`
|
|
455
|
+
);
|
|
456
|
+
} else {
|
|
457
|
+
log(`${c.dim}→${c.reset} ${name} ${c.dim}(npm install — no new lockfile)${c.reset}`);
|
|
458
|
+
}
|
|
372
459
|
const r = spawnSync(
|
|
373
460
|
"npm",
|
|
374
461
|
["install", ...quiet, "--no-package-lock"],
|
|
375
|
-
spawnWin({ cwd: dir, stdio
|
|
462
|
+
spawnWin({ cwd: dir, stdio })
|
|
376
463
|
);
|
|
377
464
|
return { ok: r.status === 0, skipped: false };
|
|
378
465
|
}
|
|
379
466
|
|
|
380
467
|
function npmInstallEach(workspace) {
|
|
381
|
-
|
|
382
|
-
|
|
468
|
+
const q = process.env.KOMPLIAN_CLI_QUIET === "1";
|
|
469
|
+
if (!q) {
|
|
470
|
+
log("");
|
|
471
|
+
log(`${c.cyan}━━ dependencies ━━${c.reset}`);
|
|
472
|
+
} else {
|
|
473
|
+
ux("");
|
|
474
|
+
ux(`${c.cyan}Dependencies${c.reset}`);
|
|
475
|
+
}
|
|
383
476
|
for (const ent of readdirSync(workspace)) {
|
|
384
477
|
const d = join(workspace, ent);
|
|
385
478
|
if (!statSync(d).isDirectory()) continue;
|
|
386
479
|
const { ok, skipped } = npmInstallOneRepo(d, ent);
|
|
387
480
|
if (skipped) continue;
|
|
388
|
-
if (
|
|
481
|
+
if (q) {
|
|
482
|
+
const cloud = `${c.blue}☁${c.reset}`;
|
|
483
|
+
if (ok) {
|
|
484
|
+
ux(` ${cloud} ${c.green}✓${c.reset} ${c.bold}${ent}${c.reset} ${c.dim}deps${c.reset}`);
|
|
485
|
+
} else {
|
|
486
|
+
ux(` ${cloud} ${c.yellow}○${c.reset} ${c.bold}${ent}${c.reset} ${c.dim}deps${c.reset}`);
|
|
487
|
+
}
|
|
488
|
+
} else if (ok) {
|
|
389
489
|
log(`${c.green}✓${c.reset} ${ent}`);
|
|
390
490
|
} else {
|
|
391
491
|
log(`${c.yellow}○${c.reset} ${ent}`);
|
|
@@ -394,30 +494,34 @@ function npmInstallEach(workspace) {
|
|
|
394
494
|
}
|
|
395
495
|
|
|
396
496
|
function usage() {
|
|
397
|
-
log(`
|
|
398
|
-
log(
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
log(`
|
|
402
|
-
log(`
|
|
403
|
-
log(`
|
|
404
|
-
log(`
|
|
405
|
-
log(
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
log(`
|
|
409
|
-
log(
|
|
410
|
-
log(
|
|
411
|
-
log(
|
|
412
|
-
log(`
|
|
413
|
-
log(`
|
|
414
|
-
log(
|
|
415
|
-
log(`
|
|
416
|
-
log(`
|
|
417
|
-
log(` --
|
|
418
|
-
log(` --
|
|
419
|
-
log(`
|
|
420
|
-
log(` -
|
|
497
|
+
console.log(`Usage: komplian setup | onboard | postman | mcp-tools | db:all:dev | localhost | db …`);
|
|
498
|
+
console.log(
|
|
499
|
+
` ${c.bold}All-in-one:${c.reset} ${c.cyan}npx komplian setup${c.reset} ${c.dim}(onboard+postman+mcp+db+localhost; browser form if needed)${c.reset}`
|
|
500
|
+
);
|
|
501
|
+
console.log(` ${c.bold}Step by step:${c.reset}`);
|
|
502
|
+
console.log(` 1. npx komplian onboard --yes`);
|
|
503
|
+
console.log(` 2. npx komplian postman --yes ${c.dim}(@komplian.com)${c.reset}`);
|
|
504
|
+
console.log(` 3. npx komplian mcp-tools --yes`);
|
|
505
|
+
console.log(
|
|
506
|
+
` 4. npx komplian db:all:dev ${c.dim}(Postman + 3 dev URLs → ~/.komplian + .env.local)${c.reset}`
|
|
507
|
+
);
|
|
508
|
+
console.log(` 5. npx komplian localhost --yes`);
|
|
509
|
+
console.log(``);
|
|
510
|
+
console.log(` npx komplian db:app:development ${c.dim}(psql one DB)${c.reset}`);
|
|
511
|
+
console.log(``);
|
|
512
|
+
console.log(` Once: gh auth login -h github.com -s repo -s read:org -w`);
|
|
513
|
+
console.log(` Requires: Node 18+, git, GitHub CLI (gh)`);
|
|
514
|
+
console.log(``);
|
|
515
|
+
console.log(` onboard implies --install unless --no-install`);
|
|
516
|
+
console.log(` [folder] Target (default: cwd, not ~/komplian)`);
|
|
517
|
+
console.log(` -y, --yes Non-interactive (default team from JSON)`);
|
|
518
|
+
console.log(` -t, --team <slug> Team in komplian-team-repos.json`);
|
|
519
|
+
console.log(` -i, --install npm install in each repo with package.json`);
|
|
520
|
+
console.log(` --no-install Skip npm install`);
|
|
521
|
+
console.log(` --all-repos All visible repos (minus exclude_from_all)`);
|
|
522
|
+
console.log(` --ssh Clone with git@github.com:…`);
|
|
523
|
+
console.log(` --list-teams Print team slugs (JSON only) and exit`);
|
|
524
|
+
console.log(` -h, --help`);
|
|
421
525
|
}
|
|
422
526
|
|
|
423
527
|
function parseArgs(argv) {
|
|
@@ -444,8 +548,8 @@ function parseArgs(argv) {
|
|
|
444
548
|
else if (a === "-h" || a === "--help") out.help = true;
|
|
445
549
|
else if (a === "-t" || a === "--team") {
|
|
446
550
|
out.team = argv[++i] || "";
|
|
447
|
-
}
|
|
448
|
-
|
|
551
|
+
} else if (a.startsWith("-")) {
|
|
552
|
+
console.error(`${c.red}✗${c.reset} Unknown option: ${a}`);
|
|
449
553
|
usage();
|
|
450
554
|
process.exit(1);
|
|
451
555
|
} else rest.push(a);
|
|
@@ -545,20 +649,22 @@ async function main() {
|
|
|
545
649
|
}
|
|
546
650
|
|
|
547
651
|
if (!existsSync(configPath)) {
|
|
548
|
-
|
|
652
|
+
console.error(`${c.red}✗${c.reset} Missing config: ${configPath}`);
|
|
549
653
|
process.exit(1);
|
|
550
654
|
}
|
|
551
655
|
|
|
552
656
|
const cfg = loadConfig(configPath);
|
|
553
657
|
|
|
554
658
|
if (args.listTeams) {
|
|
555
|
-
log(Object.keys(cfg.teams || {}).sort().join("\n"));
|
|
659
|
+
console.log(Object.keys(cfg.teams || {}).sort().join("\n"));
|
|
556
660
|
return;
|
|
557
661
|
}
|
|
558
662
|
|
|
559
663
|
const nodeMajor = Number(process.versions.node.split(".")[0], 10);
|
|
560
664
|
if (nodeMajor < 18) {
|
|
561
|
-
|
|
665
|
+
console.error(
|
|
666
|
+
`${c.red}✗${c.reset} Node 18+ required. You have ${process.versions.node}.`
|
|
667
|
+
);
|
|
562
668
|
process.exit(1);
|
|
563
669
|
}
|
|
564
670
|
|
|
@@ -572,22 +678,22 @@ async function main() {
|
|
|
572
678
|
);
|
|
573
679
|
|
|
574
680
|
if (!ghOk()) {
|
|
575
|
-
|
|
681
|
+
console.error(`${c.red}✗${c.reset} GitHub CLI not logged in.`);
|
|
576
682
|
if (!runGhAuth()) {
|
|
577
|
-
|
|
683
|
+
console.error(`${c.red}✗${c.reset} GitHub auth cancelled.`);
|
|
578
684
|
process.exit(1);
|
|
579
685
|
}
|
|
580
686
|
}
|
|
581
|
-
|
|
687
|
+
|
|
688
|
+
banner();
|
|
689
|
+
ux(`${c.green}✓${c.reset} GitHub CLI`);
|
|
582
690
|
|
|
583
691
|
const org = process.env.KOMPLIAN_ORG || cfg.org || "Komplian";
|
|
584
692
|
verifyOrgMembership(org);
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
);
|
|
589
|
-
|
|
590
|
-
banner();
|
|
693
|
+
if (!process.env.KOMPLIAN_CLI_QUIET) {
|
|
694
|
+
logGhIdentity();
|
|
695
|
+
}
|
|
696
|
+
ux(`${c.green}✓${c.reset} Org ${c.bold}${org}${c.reset}`);
|
|
591
697
|
|
|
592
698
|
let team = args.team;
|
|
593
699
|
if (!team && !args.yes && !args.allRepos) {
|
|
@@ -596,7 +702,9 @@ async function main() {
|
|
|
596
702
|
|
|
597
703
|
const repos = resolveRepos(cfg, org, team, args.allRepos);
|
|
598
704
|
if (repos.length === 0) {
|
|
599
|
-
|
|
705
|
+
console.error(
|
|
706
|
+
`${c.red}✗${c.reset} No repositories to clone (permissions / team / org).`
|
|
707
|
+
);
|
|
600
708
|
process.exit(1);
|
|
601
709
|
}
|
|
602
710
|
|
|
@@ -606,21 +714,28 @@ async function main() {
|
|
|
606
714
|
}
|
|
607
715
|
const abs = resolve(workspace.replace(/^~(?=$|[/\\])/, homedir()));
|
|
608
716
|
if (!isSafeTargetDir(abs)) {
|
|
609
|
-
|
|
717
|
+
console.error(`${c.red}✗${c.reset} Unsafe destination folder: ${abs}`);
|
|
610
718
|
process.exit(1);
|
|
611
719
|
}
|
|
612
720
|
|
|
613
721
|
mkdirSync(abs, { recursive: true });
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
722
|
+
const q = process.env.KOMPLIAN_CLI_QUIET === "1";
|
|
723
|
+
ux("");
|
|
724
|
+
if (!q) {
|
|
725
|
+
log(`${c.cyan}━━ Workspace ━━${c.reset} ${c.bold}${abs}${c.reset}`);
|
|
726
|
+
log(`${c.cyan}━━ Org ━━${c.reset} ${c.bold}${org}${c.reset}`);
|
|
727
|
+
if (team) log(`${c.cyan}━━ Team ━━${c.reset} ${c.bold}${team}${c.reset}`);
|
|
728
|
+
log(`${c.cyan}━━ Repos (${repos.length}) ━━${c.reset}`);
|
|
729
|
+
} else {
|
|
730
|
+
ux(`${c.dim}Workspace:${c.reset} ${abs}`);
|
|
731
|
+
if (team) ux(`${c.dim}Team:${c.reset} ${team}`);
|
|
732
|
+
ux(`${c.cyan}Repositories${c.reset} ${c.dim}(${repos.length})${c.reset}`);
|
|
733
|
+
}
|
|
734
|
+
ux("");
|
|
620
735
|
|
|
621
736
|
let failed = 0;
|
|
622
737
|
for (const name of repos) {
|
|
623
|
-
const ok =
|
|
738
|
+
const ok = await cloneOneAsync(org, name, abs, args.ssh);
|
|
624
739
|
if (!ok) failed += 1;
|
|
625
740
|
}
|
|
626
741
|
|
|
@@ -630,16 +745,22 @@ async function main() {
|
|
|
630
745
|
npmInstallEach(abs);
|
|
631
746
|
}
|
|
632
747
|
|
|
633
|
-
|
|
634
|
-
|
|
748
|
+
ux("");
|
|
749
|
+
ux(`${c.cyan}━━ Done ━━${c.reset}`);
|
|
635
750
|
if (failed > 0) {
|
|
636
|
-
|
|
751
|
+
ux(
|
|
752
|
+
`${c.yellow}○${c.reset} ${failed} repo(s) failed — check access and retry.`
|
|
753
|
+
);
|
|
754
|
+
}
|
|
755
|
+
ux(`${c.green}✓${c.reset} Open in Cursor: ${c.bold}File → Open Folder → ${abs}${c.reset}`);
|
|
756
|
+
if (!q) {
|
|
757
|
+
log(
|
|
758
|
+
`${c.dim} package-lock: npm ci. No lock: npm install --no-package-lock. yarn/pnpm: frozen lockfile. KOMPLIAN_NPM_AUDIT=1 enables npm audit.${c.reset}`
|
|
759
|
+
);
|
|
760
|
+
log(
|
|
761
|
+
`${c.dim} Copy .env.example → .env per project; secrets in 1Password — never commit.${c.reset}`
|
|
762
|
+
);
|
|
637
763
|
}
|
|
638
|
-
log(`${c.green}✓${c.reset} Cursor: ${c.bold}File → Open Folder → ${abs}${c.reset}`);
|
|
639
|
-
log(
|
|
640
|
-
`${c.dim} Con package-lock.json: npm ci (no retoca el lock). Sin lock: npm install --no-package-lock. yarn/pnpm: lock congelado. KOMPLIAN_NPM_AUDIT=1 activa auditoría en npm.${c.reset}`
|
|
641
|
-
);
|
|
642
|
-
log(`${c.dim} .env.example → .env por proyecto; secretos en 1Password — nunca commit.${c.reset}`);
|
|
643
764
|
}
|
|
644
765
|
|
|
645
766
|
main().catch((e) => {
|