komplian 0.3.4 → 0.3.6

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.
@@ -18,6 +18,13 @@ import { homedir } from "node:os";
18
18
 
19
19
  const __dirname = dirname(fileURLToPath(import.meta.url));
20
20
 
21
+ /** Windows: sin esto, CreateProcess no resuelve gh.cmd / git.exe / npm.cmd como la consola. */
22
+ const IS_WIN = process.platform === "win32";
23
+
24
+ function spawnWin(extra = {}) {
25
+ return IS_WIN ? { ...extra, shell: true } : extra;
26
+ }
27
+
21
28
  const c = {
22
29
  reset: "\x1b[0m",
23
30
  dim: "\x1b[2m",
@@ -51,10 +58,14 @@ function banner() {
51
58
  }
52
59
 
53
60
  function ghOk() {
54
- const r = spawnSync("gh", ["auth", "status", "-h", "github.com"], {
55
- encoding: "utf8",
56
- stdio: ["ignore", "pipe", "pipe"],
57
- });
61
+ const r = spawnSync(
62
+ "gh",
63
+ ["auth", "status", "-h", "github.com"],
64
+ spawnWin({
65
+ encoding: "utf8",
66
+ stdio: ["ignore", "pipe", "pipe"],
67
+ })
68
+ );
58
69
  return r.status === 0;
59
70
  }
60
71
 
@@ -65,23 +76,28 @@ function runGhAuth() {
65
76
  const r = spawnSync(
66
77
  "gh",
67
78
  ["auth", "login", "-h", "github.com", "-s", "repo", "-s", "read:org", "-w"],
68
- { stdio: "inherit" }
79
+ spawnWin({ stdio: "inherit" })
69
80
  );
70
81
  return r.status === 0;
71
82
  }
72
83
 
73
84
  function ghApiJson(path) {
74
- const r = spawnSync("gh", ["api", path], {
75
- encoding: "utf8",
76
- stdio: ["ignore", "pipe", "pipe"],
77
- });
85
+ const r = spawnSync(
86
+ "gh",
87
+ ["api", path],
88
+ spawnWin({
89
+ encoding: "utf8",
90
+ stdio: ["ignore", "pipe", "pipe"],
91
+ })
92
+ );
78
93
  return { status: r.status, stdout: r.stdout || "", stderr: r.stderr || "" };
79
94
  }
80
95
 
81
96
  function verifyOrgMembership(org) {
82
97
  const enc = encodeURIComponent(org);
83
98
  const mem = ghApiJson(`user/memberships/orgs/${enc}`);
84
- if (mem.status === 200) {
99
+ /** spawnSync().status = código de salida de `gh` (0 = éxito), no código HTTP. */
100
+ if (mem.status === 0) {
85
101
  try {
86
102
  const j = JSON.parse(mem.stdout);
87
103
  if (j.state === "active") return;
@@ -94,7 +110,8 @@ function verifyOrgMembership(org) {
94
110
  process.exit(1);
95
111
  }
96
112
  }
97
- if (mem.status === 404) {
113
+ const hint = (mem.stderr + mem.stdout).toLowerCase();
114
+ if (hint.includes("404") || hint.includes("not found")) {
98
115
  log(
99
116
  `${c.red}✗${c.reset} Esta cuenta ${c.bold}no es miembro${c.reset} de la org ${c.bold}${org}${c.reset}.`
100
117
  );
@@ -103,21 +120,20 @@ function verifyOrgMembership(org) {
103
120
  );
104
121
  process.exit(1);
105
122
  }
106
- const hint = (mem.stderr + mem.stdout).toLowerCase();
107
- if (mem.status === 403 || hint.includes("read:org") || hint.includes("scope")) {
123
+ if (hint.includes("403") || hint.includes("read:org") || hint.includes("scope")) {
108
124
  log(`${c.red}✗${c.reset} Falta scope ${c.bold}read:org${c.reset} en gh.`);
109
125
  log(`${c.dim} gh auth refresh -h github.com -s repo -s read:org${c.reset}`);
110
126
  process.exit(1);
111
127
  }
112
128
  log(
113
- `${c.red}✗${c.reset} No se pudo verificar la org (${mem.status}):\n${c.dim}${(mem.stderr || mem.stdout).trim()}${c.reset}`
129
+ `${c.red}✗${c.reset} No se pudo verificar la org (código ${mem.status}):\n${c.dim}${(mem.stderr || mem.stdout).trim()}${c.reset}`
114
130
  );
115
131
  process.exit(1);
116
132
  }
117
133
 
118
134
  function logGhIdentity() {
119
135
  const u = ghApiJson("user");
120
- if (u.status !== 200) return;
136
+ if (u.status !== 0) return;
121
137
  try {
122
138
  const j = JSON.parse(u.stdout);
123
139
  if (j.login) {
@@ -128,30 +144,26 @@ function logGhIdentity() {
128
144
  }
129
145
  }
130
146
 
131
- function cmdExists(name) {
132
- const pathEnv = process.env.PATH || process.env.Path || "";
133
- const isWin = process.platform === "win32";
134
- const sep = isWin ? ";" : ":";
135
- const dirs = pathEnv.split(sep).filter(Boolean);
136
- const exts = isWin
137
- ? ["", ...(process.env.PATHEXT || ".COM;.EXE;.BAT;.CMD").split(";")]
138
- : [""];
139
- for (const dir of dirs) {
140
- for (const ext of exts) {
141
- try {
142
- const full = join(dir, name + ext);
143
- if (existsSync(full) && statSync(full).isFile()) return true;
144
- } catch {
145
- continue;
146
- }
147
- }
148
- }
149
- return false;
147
+ /** Misma resolución que los clones: en Windows hace falta shell para gh.cmd / git / npm. */
148
+ function canRun(cmd, args) {
149
+ const r = spawnSync(cmd, args, spawnWin({ stdio: "ignore" }));
150
+ return r.status === 0 && !r.error;
150
151
  }
151
152
 
152
153
  function needCmd(name, hint = "") {
153
- if (!cmdExists(name)) {
154
- log(`${c.red}✗${c.reset} Falta ${c.bold}${name}${c.reset} en el PATH.${hint ? ` ${hint}` : ""}`);
154
+ const ok =
155
+ name === "gh"
156
+ ? canRun("gh", ["version"])
157
+ : name === "git"
158
+ ? canRun("git", ["--version"])
159
+ : canRun(name, ["--version"]);
160
+ if (!ok) {
161
+ log(`${c.red}✗${c.reset} No se puede ejecutar ${c.bold}${name}${c.reset} (¿PATH o instalación?).${hint ? ` ${hint}` : ""}`);
162
+ if (IS_WIN) {
163
+ log(
164
+ `${c.dim} Windows: cierra y abre la terminal tras instalar gh/git, o reinicia para refrescar PATH.${c.reset}`
165
+ );
166
+ }
155
167
  process.exit(1);
156
168
  }
157
169
  }
@@ -164,7 +176,7 @@ function ghRepoList(org) {
164
176
  const r = spawnSync(
165
177
  "gh",
166
178
  ["repo", "list", org, "--limit", "1000", "--json", "name,isArchived,isFork"],
167
- { encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] }
179
+ spawnWin({ encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] })
168
180
  );
169
181
  if (r.status !== 0) {
170
182
  log(`${c.red}✗${c.reset} gh repo list falló:\n${r.stderr || r.stdout}`);
@@ -246,9 +258,11 @@ function cloneOne(org, name, workspace, useSsh) {
246
258
  : ["repo", "clone", `${org}/${name}`, name];
247
259
  const cmd = useSsh ? "git" : "gh";
248
260
  const r = spawnSync(cmd, args, {
249
- cwd: workspace,
250
- encoding: "utf8",
251
- stdio: ["ignore", "pipe", "pipe"],
261
+ ...spawnWin({
262
+ cwd: workspace,
263
+ encoding: "utf8",
264
+ stdio: ["ignore", "pipe", "pipe"],
265
+ }),
252
266
  });
253
267
  process.stdout.write("\r\x1b[K");
254
268
  if (r.status === 0) {
@@ -266,11 +280,15 @@ function copyCursorPack(workspace, cursorRepoUrl) {
266
280
  const tmp = join(workspace, ".cursor-bootstrap-tmp");
267
281
  rmSync(tmp, { recursive: true, force: true });
268
282
  log(`${c.dim}→${c.reset} Cursor pack (clone superficial)…`);
269
- const r = spawnSync("git", ["clone", "--depth", "1", String(cursorRepoUrl).trim(), tmp], {
270
- cwd: workspace,
271
- stdio: ["ignore", "pipe", "pipe"],
272
- encoding: "utf8",
273
- });
283
+ const r = spawnSync(
284
+ "git",
285
+ ["clone", "--depth", "1", String(cursorRepoUrl).trim(), tmp],
286
+ spawnWin({
287
+ cwd: workspace,
288
+ stdio: ["ignore", "pipe", "pipe"],
289
+ encoding: "utf8",
290
+ })
291
+ );
274
292
  if (r.status === 0 && existsSync(join(tmp, ".cursor"))) {
275
293
  rmSync(rootCursor, { recursive: true, force: true });
276
294
  cpSync(join(tmp, ".cursor"), rootCursor, { recursive: true });
@@ -289,7 +307,7 @@ function copyCursorPack(workspace, cursorRepoUrl) {
289
307
  function npmInstallEach(workspace) {
290
308
  log("");
291
309
  log(`${c.cyan}━━ npm install por repo ━━${c.reset}`);
292
- if (!cmdExists("npm")) {
310
+ if (!canRun("npm", ["--version"])) {
293
311
  log(`${c.yellow}○${c.reset} npm no está en PATH — omito installs`);
294
312
  return;
295
313
  }
@@ -299,7 +317,7 @@ function npmInstallEach(workspace) {
299
317
  const pkg = join(d, "package.json");
300
318
  if (!existsSync(pkg)) continue;
301
319
  log(`${c.dim}→${c.reset} ${ent}`);
302
- const ir = spawnSync("npm", ["install"], { cwd: d, stdio: "inherit" });
320
+ const ir = spawnSync("npm", ["install"], spawnWin({ cwd: d, stdio: "inherit" }));
303
321
  if (ir.status !== 0) {
304
322
  log(`${c.yellow}○${c.reset} npm install con avisos en ${ent}`);
305
323
  } else {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "komplian",
3
- "version": "0.3.4",
3
+ "version": "0.3.6",
4
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": {