@umbral/cli 0.0.3 → 0.0.4
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 +174 -3
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -853,8 +853,156 @@ function getTemplate(detection) {
|
|
|
853
853
|
return TEMPLATES.find((t) => t.slugs.includes(detection.slug)) ?? null;
|
|
854
854
|
}
|
|
855
855
|
|
|
856
|
+
// src/claude-generate.ts
|
|
857
|
+
import { spawnSync } from "child_process";
|
|
858
|
+
function isClaudeAvailable() {
|
|
859
|
+
try {
|
|
860
|
+
const r = spawnSync("claude", ["--version"], {
|
|
861
|
+
timeout: 5e3,
|
|
862
|
+
shell: true,
|
|
863
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
864
|
+
});
|
|
865
|
+
return r.status === 0;
|
|
866
|
+
} catch {
|
|
867
|
+
return false;
|
|
868
|
+
}
|
|
869
|
+
}
|
|
870
|
+
function buildPrompt(detections) {
|
|
871
|
+
const detectionsJson = JSON.stringify(
|
|
872
|
+
detections.map((d) => ({
|
|
873
|
+
name: d.name,
|
|
874
|
+
slug: d.slug,
|
|
875
|
+
category: d.category,
|
|
876
|
+
confidence: d.confidence,
|
|
877
|
+
evidence: d.evidence,
|
|
878
|
+
metadata: d.metadata,
|
|
879
|
+
subdir: d.subdir
|
|
880
|
+
})),
|
|
881
|
+
null,
|
|
882
|
+
2
|
|
883
|
+
);
|
|
884
|
+
return `Eres Umbral, un framework de gobernanza para proyectos de software. Bas\xE1ndote en las tecnolog\xEDas detectadas abajo, genera Estructuras de Decisi\xF3n Expl\xEDcitas (EDEs) \u2014 decisiones arquitect\xF3nicas formalizadas y espec\xEDficas para ESTE proyecto.
|
|
885
|
+
|
|
886
|
+
## Tecnolog\xEDas Detectadas
|
|
887
|
+
${detectionsJson}
|
|
888
|
+
|
|
889
|
+
## Esquema JSON de una EDE
|
|
890
|
+
Cada EDE debe seguir EXACTAMENTE esta estructura:
|
|
891
|
+
{
|
|
892
|
+
"id": "EDE-001-slug",
|
|
893
|
+
"title": "string",
|
|
894
|
+
"version": 1,
|
|
895
|
+
"status": "proposed",
|
|
896
|
+
"cognitiveLevel": "explorer" | "navigator" | "anchor",
|
|
897
|
+
"complexityTier": 1 | 2 | 3,
|
|
898
|
+
"whatAndHow": { "decision": "string", "mechanism": "string" },
|
|
899
|
+
"why": {
|
|
900
|
+
"rationale": "string",
|
|
901
|
+
"alternativesConsidered": [{ "option": "string", "rejectedBecause": "string" }],
|
|
902
|
+
"references": []
|
|
903
|
+
},
|
|
904
|
+
"whatNotToDo": { "antiPatterns": ["string"] },
|
|
905
|
+
"whatsNext": { "continuations": [], "openQuestions": [] },
|
|
906
|
+
"contracts": { "layerContracts": [], "verifiedBy": [] },
|
|
907
|
+
"tests": { "unitTests": [], "sadPaths": [], "coverageTarget": 0.8 },
|
|
908
|
+
"provenance": { "phase": "onboarding", "slice": null, "createdBy": "claude", "createdAt": null, "lastUpdated": null }
|
|
909
|
+
}
|
|
910
|
+
|
|
911
|
+
## Instrucciones
|
|
912
|
+
- Genera una EDE por cada decisi\xF3n arquitect\xF3nica significativa
|
|
913
|
+
- S\xE9 ESPEC\xCDFICO para este proyecto: usa la evidencia detectada para personalizar cada decisi\xF3n
|
|
914
|
+
- Incluye anti-patrones relevantes a la COMBINACI\xD3N de tecnolog\xEDas (no gen\xE9ricos)
|
|
915
|
+
- IDs: EDE-001-slug, EDE-002-slug, etc.
|
|
916
|
+
- cognitiveLevel: "explorer" para simples, "navigator" para intermedias, "anchor" para invariantes
|
|
917
|
+
- complexityTier: 1 simple, 2 medio, 3 complejo
|
|
918
|
+
- Todo el contenido en espa\xF1ol
|
|
919
|
+
- Genera entre 3 y 10 EDEs seg\xFAn la complejidad del proyecto
|
|
920
|
+
|
|
921
|
+
Retorna \xDANICAMENTE un array JSON v\xE1lido. Sin markdown, sin backticks, sin texto adicional.`;
|
|
922
|
+
}
|
|
923
|
+
function extractJson(text) {
|
|
924
|
+
const codeBlock = text.match(/```(?:json)?\s*\n?([\s\S]*?)\n?```/);
|
|
925
|
+
if (codeBlock) return codeBlock[1].trim();
|
|
926
|
+
const arrayMatch = text.match(/\[[\s\S]*\]/);
|
|
927
|
+
if (arrayMatch) return arrayMatch[0];
|
|
928
|
+
return text.trim();
|
|
929
|
+
}
|
|
930
|
+
function normalizeEde(raw, index) {
|
|
931
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
932
|
+
return {
|
|
933
|
+
id: raw.id ?? `EDE-${String(index + 1).padStart(3, "0")}-generated`,
|
|
934
|
+
title: raw.title ?? "EDE generada por Claude",
|
|
935
|
+
version: 1,
|
|
936
|
+
status: "proposed",
|
|
937
|
+
cognitiveLevel: raw.cognitiveLevel ?? "explorer",
|
|
938
|
+
complexityTier: raw.complexityTier ?? 1,
|
|
939
|
+
whatAndHow: {
|
|
940
|
+
decision: raw.whatAndHow?.decision ?? "",
|
|
941
|
+
mechanism: raw.whatAndHow?.mechanism ?? ""
|
|
942
|
+
},
|
|
943
|
+
why: {
|
|
944
|
+
rationale: raw.why?.rationale ?? "",
|
|
945
|
+
alternativesConsidered: Array.isArray(raw.why?.alternativesConsidered) ? raw.why.alternativesConsidered : [],
|
|
946
|
+
references: []
|
|
947
|
+
},
|
|
948
|
+
whatNotToDo: {
|
|
949
|
+
antiPatterns: Array.isArray(raw.whatNotToDo?.antiPatterns) ? raw.whatNotToDo.antiPatterns : []
|
|
950
|
+
},
|
|
951
|
+
whatsNext: {
|
|
952
|
+
continuations: [],
|
|
953
|
+
openQuestions: Array.isArray(raw.whatsNext?.openQuestions) ? raw.whatsNext.openQuestions : []
|
|
954
|
+
},
|
|
955
|
+
contracts: {
|
|
956
|
+
layerContracts: Array.isArray(raw.contracts?.layerContracts) ? raw.contracts.layerContracts : [],
|
|
957
|
+
verifiedBy: []
|
|
958
|
+
},
|
|
959
|
+
tests: {
|
|
960
|
+
unitTests: [],
|
|
961
|
+
sadPaths: [],
|
|
962
|
+
coverageTarget: raw.tests?.coverageTarget ?? 0.8
|
|
963
|
+
},
|
|
964
|
+
provenance: {
|
|
965
|
+
phase: "onboarding",
|
|
966
|
+
slice: null,
|
|
967
|
+
createdBy: "claude",
|
|
968
|
+
createdAt: now,
|
|
969
|
+
lastUpdated: now
|
|
970
|
+
}
|
|
971
|
+
};
|
|
972
|
+
}
|
|
973
|
+
function generateWithClaude(detections) {
|
|
974
|
+
if (detections.length === 0) return null;
|
|
975
|
+
const prompt = buildPrompt(detections);
|
|
976
|
+
try {
|
|
977
|
+
const result = spawnSync("claude", ["--print"], {
|
|
978
|
+
input: prompt,
|
|
979
|
+
encoding: "utf-8",
|
|
980
|
+
timeout: 12e4,
|
|
981
|
+
shell: true,
|
|
982
|
+
maxBuffer: 5 * 1024 * 1024
|
|
983
|
+
});
|
|
984
|
+
if (result.status !== 0 || !result.stdout) return null;
|
|
985
|
+
const jsonStr = extractJson(result.stdout);
|
|
986
|
+
const parsed = JSON.parse(jsonStr);
|
|
987
|
+
if (!Array.isArray(parsed) || parsed.length === 0) return null;
|
|
988
|
+
const edes = [];
|
|
989
|
+
for (let i = 0; i < parsed.length; i++) {
|
|
990
|
+
try {
|
|
991
|
+
const ede = normalizeEde(parsed[i], i);
|
|
992
|
+
if (ede.whatAndHow.decision && ede.why.rationale) {
|
|
993
|
+
edes.push(ede);
|
|
994
|
+
}
|
|
995
|
+
} catch {
|
|
996
|
+
}
|
|
997
|
+
}
|
|
998
|
+
return edes.length > 0 ? edes : null;
|
|
999
|
+
} catch {
|
|
1000
|
+
return null;
|
|
1001
|
+
}
|
|
1002
|
+
}
|
|
1003
|
+
|
|
856
1004
|
// src/generate.ts
|
|
857
|
-
function
|
|
1005
|
+
function generateFromTemplates(detections) {
|
|
858
1006
|
const proposals = [];
|
|
859
1007
|
let counter = 1;
|
|
860
1008
|
for (const detection of detections) {
|
|
@@ -906,6 +1054,17 @@ function generateProposals(detections) {
|
|
|
906
1054
|
}
|
|
907
1055
|
return proposals;
|
|
908
1056
|
}
|
|
1057
|
+
function generateProposals(detections, onStatus) {
|
|
1058
|
+
if (isClaudeAvailable()) {
|
|
1059
|
+
onStatus?.("Generando EDEs con Claude Code...");
|
|
1060
|
+
const claudeEdes = generateWithClaude(detections);
|
|
1061
|
+
if (claudeEdes && claudeEdes.length > 0) {
|
|
1062
|
+
return { proposals: claudeEdes, source: "claude" };
|
|
1063
|
+
}
|
|
1064
|
+
onStatus?.("Claude no pudo generar EDEs, usando templates...");
|
|
1065
|
+
}
|
|
1066
|
+
return { proposals: generateFromTemplates(detections), source: "templates" };
|
|
1067
|
+
}
|
|
909
1068
|
|
|
910
1069
|
// src/ui.ts
|
|
911
1070
|
import { createInterface } from "readline/promises";
|
|
@@ -1340,7 +1499,19 @@ async function initCommand(options) {
|
|
|
1340
1499
|
`);
|
|
1341
1500
|
}
|
|
1342
1501
|
}
|
|
1343
|
-
|
|
1502
|
+
w("\n");
|
|
1503
|
+
const { proposals, source } = generateProposals(
|
|
1504
|
+
analysis.detections,
|
|
1505
|
+
(msg) => w(` ${msg}
|
|
1506
|
+
`)
|
|
1507
|
+
);
|
|
1508
|
+
if (source === "claude") {
|
|
1509
|
+
w(` \u2713 ${proposals.length} EDEs generadas con Claude Code (IA)
|
|
1510
|
+
`);
|
|
1511
|
+
} else {
|
|
1512
|
+
w(` \u21B3 EDEs generadas desde templates locales
|
|
1513
|
+
`);
|
|
1514
|
+
}
|
|
1344
1515
|
if (proposals.length === 0) {
|
|
1345
1516
|
w("\n No hay propuestas de EDEs para generar.\n");
|
|
1346
1517
|
w(" Configurando infraestructura base...\n\n");
|
|
@@ -1636,7 +1807,7 @@ function stopCommand() {
|
|
|
1636
1807
|
|
|
1637
1808
|
// src/index.ts
|
|
1638
1809
|
var program = new Command();
|
|
1639
|
-
program.name("umbral").description("Umbral \u2014 Framework de gobernanza para proyectos con Claude Code").version("0.0.
|
|
1810
|
+
program.name("umbral").description("Umbral \u2014 Framework de gobernanza para proyectos con Claude Code").version("0.0.4");
|
|
1640
1811
|
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);
|
|
1641
1812
|
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);
|
|
1642
1813
|
program.command("stop").description("Detener la plataforma Umbral").action(stopCommand);
|