@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.
- package/dist/index.js +89 -61
- 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
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
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)
|
|
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(
|
|
998
|
-
|
|
999
|
-
|
|
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
|
-
|
|
1017
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
1123
|
-
if (
|
|
1150
|
+
const result = await generateWithClaude(detections);
|
|
1151
|
+
if (result.edes && result.edes.length > 0) {
|
|
1124
1152
|
spinner.succeed(
|
|
1125
|
-
`${
|
|
1153
|
+
`${result.edes.length} EDEs generadas con Claude Code (IA)`
|
|
1126
1154
|
);
|
|
1127
|
-
return { proposals:
|
|
1155
|
+
return { proposals: result.edes, source: "claude" };
|
|
1128
1156
|
}
|
|
1129
|
-
spinner.fail(
|
|
1130
|
-
|
|
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
|
|
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 =
|
|
1422
|
+
const claudeDir = join13(projectPath, ".claude");
|
|
1395
1423
|
mkdirSync2(claudeDir, { recursive: true });
|
|
1396
|
-
const settingsPath =
|
|
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
|
-
|
|
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
|
|
1447
|
-
import { join as
|
|
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 =
|
|
1578
|
+
const claudeDir = join14(projectPath, ".claude");
|
|
1551
1579
|
mkdirSync3(claudeDir, { recursive: true });
|
|
1552
|
-
|
|
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
|
|
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 =
|
|
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
|
|
1707
|
-
import { join as
|
|
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 =
|
|
1711
|
-
var COMPOSE_PATH =
|
|
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
|
-
|
|
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
|
-
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
1870
|
+
import { execSync as execSync3 } from "child_process";
|
|
1843
1871
|
import { existsSync as existsSync12 } from "fs";
|
|
1844
|
-
import { join as
|
|
1872
|
+
import { join as join17 } from "path";
|
|
1845
1873
|
import { homedir as homedir4 } from "os";
|
|
1846
|
-
var COMPOSE_PATH2 =
|
|
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
|
-
|
|
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.
|
|
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);
|