@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.
Files changed (2) hide show
  1. package/dist/index.js +174 -3
  2. 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 generateProposals(detections) {
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
- const proposals = generateProposals(analysis.detections);
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.3");
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);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@umbral/cli",
3
- "version": "0.0.3",
3
+ "version": "0.0.4",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "bin": {