@umbral/cli 0.0.5 → 0.0.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.
Files changed (2) hide show
  1. package/dist/index.js +89 -61
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -855,6 +855,9 @@ function getTemplate(detection) {
855
855
 
856
856
  // src/claude-generate.ts
857
857
  import { spawn, spawnSync } from "child_process";
858
+ import { writeFileSync, unlinkSync } from "fs";
859
+ import { join as join12 } from "path";
860
+ import { tmpdir } from "os";
858
861
  function isClaudeAvailable() {
859
862
  try {
860
863
  const r = spawnSync("claude", ["--version"], {
@@ -971,33 +974,34 @@ function normalizeEde(raw, index) {
971
974
  };
972
975
  }
973
976
  function parseEdes(stdout2) {
974
- try {
975
- const jsonStr = extractJson(stdout2);
976
- const parsed = JSON.parse(jsonStr);
977
- if (!Array.isArray(parsed) || parsed.length === 0) return null;
978
- const edes = [];
979
- for (let i = 0; i < parsed.length; i++) {
980
- try {
981
- const ede = normalizeEde(parsed[i], i);
982
- if (ede.whatAndHow.decision && ede.why.rationale) {
983
- edes.push(ede);
984
- }
985
- } catch {
977
+ const jsonStr = extractJson(stdout2);
978
+ const parsed = JSON.parse(jsonStr);
979
+ if (!Array.isArray(parsed) || parsed.length === 0) return null;
980
+ const edes = [];
981
+ for (let i = 0; i < parsed.length; i++) {
982
+ try {
983
+ const ede = normalizeEde(parsed[i], i);
984
+ if (ede.whatAndHow.decision && ede.why.rationale) {
985
+ edes.push(ede);
986
986
  }
987
+ } catch {
987
988
  }
988
- return edes.length > 0 ? edes : null;
989
- } catch {
990
- return null;
991
989
  }
990
+ return edes.length > 0 ? edes : null;
992
991
  }
993
992
  function generateWithClaude(detections) {
994
- if (detections.length === 0) return Promise.resolve(null);
993
+ if (detections.length === 0)
994
+ return Promise.resolve({ edes: null, error: "Sin detecciones" });
995
995
  const prompt = buildPrompt(detections);
996
+ const tmpFile = join12(tmpdir(), `umbral-prompt-${Date.now()}.txt`);
997
+ writeFileSync(tmpFile, prompt, "utf-8");
998
+ const readCmd = process.platform === "win32" ? `type "${tmpFile}"` : `cat "${tmpFile}"`;
996
999
  return new Promise((resolve) => {
997
- const child = spawn("claude", ["--print"], {
998
- shell: true,
999
- stdio: ["pipe", "pipe", "pipe"]
1000
- });
1000
+ const child = spawn(
1001
+ process.platform === "win32" ? "cmd.exe" : "/bin/sh",
1002
+ process.platform === "win32" ? ["/c", `${readCmd} | claude --print`] : ["-c", `${readCmd} | claude --print`],
1003
+ { stdio: ["ignore", "pipe", "pipe"] }
1004
+ );
1001
1005
  let stdout2 = "";
1002
1006
  let stderr = "";
1003
1007
  let timedOut = false;
@@ -1013,18 +1017,41 @@ function generateWithClaude(detections) {
1013
1017
  });
1014
1018
  child.on("close", (code) => {
1015
1019
  clearTimeout(timeout);
1016
- if (timedOut || code !== 0 || !stdout2) {
1017
- resolve(null);
1020
+ try {
1021
+ unlinkSync(tmpFile);
1022
+ } catch {
1023
+ }
1024
+ if (timedOut) {
1025
+ resolve({ edes: null, error: "Timeout (>2min)" });
1026
+ return;
1027
+ }
1028
+ if (code !== 0 || !stdout2.trim()) {
1029
+ const reason = stderr.trim().split("\n")[0] || `exit code ${code}`;
1030
+ resolve({ edes: null, error: reason });
1018
1031
  return;
1019
1032
  }
1020
- resolve(parseEdes(stdout2));
1033
+ try {
1034
+ const edes = parseEdes(stdout2);
1035
+ if (!edes) {
1036
+ resolve({
1037
+ edes: null,
1038
+ error: "Claude respondio pero el JSON no es valido"
1039
+ });
1040
+ return;
1041
+ }
1042
+ resolve({ edes });
1043
+ } catch (e) {
1044
+ resolve({ edes: null, error: `Error parseando JSON: ${e.message}` });
1045
+ }
1021
1046
  });
1022
- child.on("error", () => {
1047
+ child.on("error", (err) => {
1023
1048
  clearTimeout(timeout);
1024
- resolve(null);
1049
+ try {
1050
+ unlinkSync(tmpFile);
1051
+ } catch {
1052
+ }
1053
+ resolve({ edes: null, error: err.message });
1025
1054
  });
1026
- child.stdin.write(prompt);
1027
- child.stdin.end();
1028
1055
  });
1029
1056
  }
1030
1057
 
@@ -1117,20 +1144,21 @@ function generateFromTemplates(detections) {
1117
1144
  return proposals;
1118
1145
  }
1119
1146
  async function generateProposals(detections) {
1147
+ const w = (s) => process.stdout.write(s);
1120
1148
  if (isClaudeAvailable()) {
1121
1149
  const spinner = createSpinner("Generando EDEs con Claude Code...");
1122
- const claudeEdes = await generateWithClaude(detections);
1123
- if (claudeEdes && claudeEdes.length > 0) {
1150
+ const result = await generateWithClaude(detections);
1151
+ if (result.edes && result.edes.length > 0) {
1124
1152
  spinner.succeed(
1125
- `${claudeEdes.length} EDEs generadas con Claude Code (IA)`
1153
+ `${result.edes.length} EDEs generadas con Claude Code (IA)`
1126
1154
  );
1127
- return { proposals: claudeEdes, source: "claude" };
1155
+ return { proposals: result.edes, source: "claude" };
1128
1156
  }
1129
- spinner.fail("Claude no pudo generar EDEs \u2014 usando templates");
1130
- } else {
1131
- process.stdout.write(
1132
- " \u21B3 Claude Code no disponible \u2014 usando templates locales\n"
1157
+ spinner.fail(
1158
+ `Claude fallo: ${result.error ?? "respuesta vacia"} \u2014 usando templates`
1133
1159
  );
1160
+ } else {
1161
+ w(" \u21B3 Claude Code no disponible \u2014 usando templates locales\n");
1134
1162
  }
1135
1163
  return { proposals: generateFromTemplates(detections), source: "templates" };
1136
1164
  }
@@ -1388,12 +1416,12 @@ function setupDatabase(edes) {
1388
1416
  }
1389
1417
 
1390
1418
  // src/setup/hooks.ts
1391
- import { existsSync as existsSync10, readFileSync as readFileSync10, writeFileSync, mkdirSync as mkdirSync2 } from "fs";
1392
- import { join as join12 } from "path";
1419
+ import { existsSync as existsSync10, readFileSync as readFileSync10, writeFileSync as writeFileSync2, mkdirSync as mkdirSync2 } from "fs";
1420
+ import { join as join13 } from "path";
1393
1421
  function setupHooks(projectPath) {
1394
- const claudeDir = join12(projectPath, ".claude");
1422
+ const claudeDir = join13(projectPath, ".claude");
1395
1423
  mkdirSync2(claudeDir, { recursive: true });
1396
- const settingsPath = join12(claudeDir, "settings.json");
1424
+ const settingsPath = join13(claudeDir, "settings.json");
1397
1425
  let existing = {};
1398
1426
  if (existsSync10(settingsPath)) {
1399
1427
  try {
@@ -1439,12 +1467,12 @@ function setupHooks(projectPath) {
1439
1467
  }
1440
1468
  }
1441
1469
  };
1442
- writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n", "utf-8");
1470
+ writeFileSync2(settingsPath, JSON.stringify(settings, null, 2) + "\n", "utf-8");
1443
1471
  }
1444
1472
 
1445
1473
  // src/setup/context.ts
1446
- import { writeFileSync as writeFileSync2, mkdirSync as mkdirSync3 } from "fs";
1447
- import { join as join13 } from "path";
1474
+ import { writeFileSync as writeFileSync3, mkdirSync as mkdirSync3 } from "fs";
1475
+ import { join as join14 } from "path";
1448
1476
 
1449
1477
  // ../orchestrator/src/claude-context.ts
1450
1478
  function assembleClaudeContext(edes) {
@@ -1547,9 +1575,9 @@ function setupContext(projectPath) {
1547
1575
  const edes = createEdeStore(db).getAll();
1548
1576
  db.close();
1549
1577
  const content = assembleClaudeContext(edes);
1550
- const claudeDir = join13(projectPath, ".claude");
1578
+ const claudeDir = join14(projectPath, ".claude");
1551
1579
  mkdirSync3(claudeDir, { recursive: true });
1552
- writeFileSync2(join13(claudeDir, "CLAUDE.md"), content, "utf-8");
1580
+ writeFileSync3(join14(claudeDir, "CLAUDE.md"), content, "utf-8");
1553
1581
  }
1554
1582
 
1555
1583
  // src/commands/init.ts
@@ -1691,28 +1719,28 @@ async function hookCommand(_event) {
1691
1719
 
1692
1720
  // src/commands/mcp.ts
1693
1721
  import { execFileSync } from "child_process";
1694
- import { join as join14, dirname as dirname2 } from "path";
1722
+ import { join as join15, dirname as dirname2 } from "path";
1695
1723
  import { fileURLToPath } from "url";
1696
1724
  async function mcpCommand() {
1697
1725
  const __dirname = dirname2(fileURLToPath(import.meta.url));
1698
- const mcpEntry = join14(__dirname, "mcp-entry.js");
1726
+ const mcpEntry = join15(__dirname, "mcp-entry.js");
1699
1727
  execFileSync(process.execPath, [mcpEntry], {
1700
1728
  stdio: "inherit"
1701
1729
  });
1702
1730
  }
1703
1731
 
1704
1732
  // src/commands/start.ts
1705
- import { execSync, spawn as spawn2 } from "child_process";
1706
- import { writeFileSync as writeFileSync3, mkdirSync as mkdirSync4 } from "fs";
1707
- import { join as join15 } from "path";
1733
+ import { execSync as execSync2, spawn as spawn2 } from "child_process";
1734
+ import { writeFileSync as writeFileSync4, mkdirSync as mkdirSync4 } from "fs";
1735
+ import { join as join16 } from "path";
1708
1736
  import { homedir as homedir3 } from "os";
1709
1737
  import { createServer } from "net";
1710
- var UMBRAL_DIR = join15(homedir3(), ".umbral");
1711
- var COMPOSE_PATH = join15(UMBRAL_DIR, "docker-compose.yml");
1738
+ var UMBRAL_DIR = join16(homedir3(), ".umbral");
1739
+ var COMPOSE_PATH = join16(UMBRAL_DIR, "docker-compose.yml");
1712
1740
  var DOCKER_IMAGE = process.env.UMBRAL_IMAGE ?? "ghcr.io/josephrobles23/umbral-web:latest";
1713
1741
  function dockerInstalled() {
1714
1742
  try {
1715
- execSync("docker --version", { stdio: "ignore" });
1743
+ execSync2("docker --version", { stdio: "ignore" });
1716
1744
  return true;
1717
1745
  } catch {
1718
1746
  return false;
@@ -1720,7 +1748,7 @@ function dockerInstalled() {
1720
1748
  }
1721
1749
  function composeInstalled() {
1722
1750
  try {
1723
- execSync("docker compose version", { stdio: "ignore" });
1751
+ execSync2("docker compose version", { stdio: "ignore" });
1724
1752
  return true;
1725
1753
  } catch {
1726
1754
  return false;
@@ -1741,7 +1769,7 @@ function findFreePort(start) {
1741
1769
  });
1742
1770
  }
1743
1771
  function generateCompose(webPort, wsPort) {
1744
- const dbPath = join15(UMBRAL_DIR, "umbral.db").replace(/\\/g, "/");
1772
+ const dbPath = join16(UMBRAL_DIR, "umbral.db").replace(/\\/g, "/");
1745
1773
  const dbDir = UMBRAL_DIR.replace(/\\/g, "/");
1746
1774
  return `# Auto-generated by Umbral CLI \u2014 do not edit manually
1747
1775
  name: umbral
@@ -1801,14 +1829,14 @@ async function startCommand(options) {
1801
1829
  const wsPort = await findFreePort(webPort + 99);
1802
1830
  mkdirSync4(UMBRAL_DIR, { recursive: true });
1803
1831
  const compose = generateCompose(webPort, wsPort);
1804
- writeFileSync3(COMPOSE_PATH, compose, "utf-8");
1832
+ writeFileSync4(COMPOSE_PATH, compose, "utf-8");
1805
1833
  w(` \u2713 docker-compose.yml generado
1806
1834
  `);
1807
1835
  w(" \u27F3 Descargando imagenes y levantando servicios...\n\n");
1808
1836
  const detach = options.detach !== false;
1809
1837
  try {
1810
1838
  if (detach) {
1811
- execSync(`docker compose -f "${COMPOSE_PATH}" up -d`, {
1839
+ execSync2(`docker compose -f "${COMPOSE_PATH}" up -d`, {
1812
1840
  stdio: "inherit"
1813
1841
  });
1814
1842
  } else {
@@ -1839,11 +1867,11 @@ async function startCommand(options) {
1839
1867
  }
1840
1868
 
1841
1869
  // src/commands/stop.ts
1842
- import { execSync as execSync2 } from "child_process";
1870
+ import { execSync as execSync3 } from "child_process";
1843
1871
  import { existsSync as existsSync12 } from "fs";
1844
- import { join as join16 } from "path";
1872
+ import { join as join17 } from "path";
1845
1873
  import { homedir as homedir4 } from "os";
1846
- var COMPOSE_PATH2 = join16(homedir4(), ".umbral", "docker-compose.yml");
1874
+ var COMPOSE_PATH2 = join17(homedir4(), ".umbral", "docker-compose.yml");
1847
1875
  function stopCommand() {
1848
1876
  const w = (s) => process.stdout.write(s);
1849
1877
  w("\n Umbral \u2014 Deteniendo plataforma\n\n");
@@ -1853,7 +1881,7 @@ function stopCommand() {
1853
1881
  process.exit(1);
1854
1882
  }
1855
1883
  try {
1856
- execSync2(`docker compose -f "${COMPOSE_PATH2}" down`, {
1884
+ execSync3(`docker compose -f "${COMPOSE_PATH2}" down`, {
1857
1885
  stdio: "inherit"
1858
1886
  });
1859
1887
  w("\n \u2713 Servicios detenidos.\n\n");
@@ -1865,7 +1893,7 @@ function stopCommand() {
1865
1893
 
1866
1894
  // src/index.ts
1867
1895
  var program = new Command();
1868
- program.name("umbral").description("Umbral \u2014 Framework de gobernanza para proyectos con Claude Code").version("0.0.5");
1896
+ program.name("umbral").description("Umbral \u2014 Framework de gobernanza para proyectos con Claude Code").version("0.0.6");
1869
1897
  program.command("init").description("Inicializar Umbral en el proyecto actual").option("--yes", "Aceptar todas las propuestas sin preguntar").option("--path <path>", "Ruta al proyecto (default: directorio actual)").action(initCommand);
1870
1898
  program.command("start").description("Levantar la plataforma Umbral (Neo4j + Dashboard web)").option("--port <port>", "Puerto para el dashboard (default: auto)").option("--no-detach", "Correr en primer plano (sin -d)").action(startCommand);
1871
1899
  program.command("stop").description("Detener la plataforma Umbral").action(stopCommand);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@umbral/cli",
3
- "version": "0.0.5",
3
+ "version": "0.0.6",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "bin": {