@umbral/cli 0.0.1

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.
@@ -0,0 +1,596 @@
1
+ // ../mcp-server/src/index.ts
2
+ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
3
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
4
+ import {
5
+ CallToolRequestSchema,
6
+ ListToolsRequestSchema
7
+ } from "@modelcontextprotocol/sdk/types.js";
8
+
9
+ // ../persistence/src/db.ts
10
+ import Database from "better-sqlite3";
11
+ import * as sqliteVec from "sqlite-vec";
12
+ import { mkdirSync } from "fs";
13
+ import { homedir } from "os";
14
+ import { dirname } from "path";
15
+
16
+ // ../persistence/src/migrate.ts
17
+ var migrations = [
18
+ {
19
+ id: "001_edes",
20
+ up: `CREATE TABLE IF NOT EXISTS edes (
21
+ id TEXT PRIMARY KEY,
22
+ data TEXT NOT NULL,
23
+ title TEXT NOT NULL,
24
+ status TEXT NOT NULL,
25
+ cognitive_level TEXT NOT NULL,
26
+ created_at TEXT,
27
+ updated_at TEXT
28
+ )`
29
+ },
30
+ {
31
+ id: "002_grill_sessions",
32
+ up: `CREATE TABLE IF NOT EXISTS grill_sessions (
33
+ id TEXT PRIMARY KEY,
34
+ ede_id TEXT NOT NULL,
35
+ data TEXT NOT NULL,
36
+ status TEXT NOT NULL,
37
+ alignment_score REAL NOT NULL DEFAULT 0,
38
+ created_at TEXT NOT NULL,
39
+ updated_at TEXT NOT NULL
40
+ );
41
+ CREATE TABLE IF NOT EXISTS cognitive_debts (
42
+ id TEXT PRIMARY KEY,
43
+ session_id TEXT NOT NULL,
44
+ ede_id TEXT NOT NULL,
45
+ gap REAL NOT NULL,
46
+ reason TEXT NOT NULL,
47
+ created_at TEXT NOT NULL
48
+ )`
49
+ },
50
+ {
51
+ id: "003_semantic_embeddings",
52
+ up: `CREATE TABLE IF NOT EXISTS session_nodes (
53
+ id TEXT PRIMARY KEY,
54
+ session_id TEXT NOT NULL,
55
+ type TEXT NOT NULL,
56
+ content TEXT NOT NULL,
57
+ metadata TEXT,
58
+ source_doc_node TEXT NOT NULL,
59
+ created_at TEXT NOT NULL
60
+ );
61
+ CREATE VIRTUAL TABLE IF NOT EXISTS session_nodes_fts USING fts5(
62
+ node_id UNINDEXED, content, session_id UNINDEXED, type UNINDEXED
63
+ );
64
+ CREATE TABLE IF NOT EXISTS doc_chunks (
65
+ rowid INTEGER PRIMARY KEY AUTOINCREMENT,
66
+ id TEXT UNIQUE NOT NULL,
67
+ doc_id TEXT NOT NULL,
68
+ content TEXT NOT NULL,
69
+ source_doc_node TEXT NOT NULL,
70
+ created_at TEXT NOT NULL
71
+ );
72
+ CREATE VIRTUAL TABLE IF NOT EXISTS doc_chunks_vec USING vec0(
73
+ embedding float[384]
74
+ )`
75
+ },
76
+ {
77
+ id: "004_terminal_sessions",
78
+ up: `CREATE TABLE IF NOT EXISTS terminal_sessions (
79
+ id TEXT PRIMARY KEY,
80
+ ede_id TEXT,
81
+ pid INTEGER NOT NULL,
82
+ status TEXT NOT NULL DEFAULT 'active',
83
+ working_directory TEXT NOT NULL,
84
+ claude_md_path TEXT,
85
+ created_at TEXT NOT NULL,
86
+ last_activity_at TEXT NOT NULL,
87
+ terminated_at TEXT
88
+ )`
89
+ }
90
+ ];
91
+ function runMigrations(db) {
92
+ db.exec(
93
+ `CREATE TABLE IF NOT EXISTS _migrations (
94
+ id TEXT PRIMARY KEY,
95
+ applied_at TEXT NOT NULL
96
+ )`
97
+ );
98
+ const applied = new Set(
99
+ db.prepare("SELECT id FROM _migrations").pluck().all()
100
+ );
101
+ for (const m of migrations) {
102
+ if (!applied.has(m.id)) {
103
+ db.exec(m.up);
104
+ db.prepare("INSERT INTO _migrations (id, applied_at) VALUES (?, ?)").run(
105
+ m.id,
106
+ (/* @__PURE__ */ new Date()).toISOString()
107
+ );
108
+ }
109
+ }
110
+ }
111
+
112
+ // ../persistence/src/db.ts
113
+ var DEFAULT_DB_PATH = `${homedir()}/.umbral/umbral.db`;
114
+ function openDb(path = DEFAULT_DB_PATH) {
115
+ mkdirSync(dirname(path), { recursive: true });
116
+ const db = new Database(path);
117
+ try {
118
+ sqliteVec.load(db);
119
+ } catch (err) {
120
+ db.close();
121
+ throw new Error(
122
+ `[S13] sqlite-vec extension failed to load \u2014 the app will not start. Cause: ${err instanceof Error ? err.message : err}`
123
+ );
124
+ }
125
+ db.pragma("journal_mode = WAL");
126
+ const ok = db.pragma("integrity_check", { simple: true });
127
+ if (ok !== "ok") {
128
+ db.close();
129
+ throw new Error("[S13] DB integrity check failed \u2014 the app will not start.");
130
+ }
131
+ runMigrations(db);
132
+ return db;
133
+ }
134
+
135
+ // ../persistence/src/ede-store.ts
136
+ import { z } from "zod";
137
+ var edeSchema = z.object({
138
+ id: z.string(),
139
+ title: z.string(),
140
+ version: z.number(),
141
+ status: z.enum(["proposed", "accepted", "deprecated"]),
142
+ cognitiveLevel: z.enum(["explorer", "navigator", "anchor"]),
143
+ complexityTier: z.union([z.literal(1), z.literal(2), z.literal(3)]),
144
+ whatAndHow: z.object({ decision: z.string(), mechanism: z.string() }),
145
+ why: z.object({
146
+ rationale: z.string(),
147
+ alternativesConsidered: z.array(
148
+ z.object({ option: z.string(), rejectedBecause: z.string() })
149
+ ),
150
+ references: z.array(z.string())
151
+ }),
152
+ whatNotToDo: z.object({ antiPatterns: z.array(z.string()) }),
153
+ whatsNext: z.object({
154
+ continuations: z.array(z.string()),
155
+ openQuestions: z.array(z.string())
156
+ }),
157
+ contracts: z.object({
158
+ layerContracts: z.array(z.string()),
159
+ verifiedBy: z.array(z.string())
160
+ }),
161
+ tests: z.object({
162
+ unitTests: z.array(z.string()),
163
+ sadPaths: z.array(z.string()),
164
+ coverageTarget: z.number()
165
+ }),
166
+ provenance: z.object({
167
+ phase: z.string(),
168
+ slice: z.number().nullable(),
169
+ createdBy: z.string(),
170
+ createdAt: z.string().nullable(),
171
+ lastUpdated: z.string().nullable()
172
+ })
173
+ });
174
+ function createEdeStore(db) {
175
+ return {
176
+ save(ede) {
177
+ db.prepare(
178
+ `INSERT OR REPLACE INTO edes (id, data, title, status, cognitive_level, created_at, updated_at)
179
+ VALUES (?, ?, ?, ?, ?, ?, ?)`
180
+ ).run(
181
+ ede.id,
182
+ JSON.stringify(ede),
183
+ ede.title,
184
+ ede.status,
185
+ ede.cognitiveLevel,
186
+ ede.provenance.createdAt,
187
+ ede.provenance.lastUpdated
188
+ );
189
+ },
190
+ getById(id) {
191
+ const row = db.prepare("SELECT data FROM edes WHERE id = ?").get(id);
192
+ return row ? JSON.parse(row.data) : null;
193
+ },
194
+ getAll() {
195
+ const rows = db.prepare("SELECT data FROM edes ORDER BY id").all();
196
+ return rows.map((r) => JSON.parse(r.data));
197
+ }
198
+ };
199
+ }
200
+
201
+ // ../persistence/src/grill-store.ts
202
+ function createGrillStore(db) {
203
+ return {
204
+ saveSession(session) {
205
+ db.prepare(
206
+ `INSERT OR REPLACE INTO grill_sessions (id, ede_id, data, status, alignment_score, created_at, updated_at)
207
+ VALUES (?, ?, ?, ?, ?, ?, ?)`
208
+ ).run(
209
+ session.id,
210
+ session.edeId,
211
+ JSON.stringify(session),
212
+ session.status,
213
+ session.alignmentScore,
214
+ session.createdAt,
215
+ session.updatedAt
216
+ );
217
+ },
218
+ getSession(id) {
219
+ const row = db.prepare("SELECT data FROM grill_sessions WHERE id = ?").get(id);
220
+ return row ? JSON.parse(row.data) : null;
221
+ },
222
+ getActiveSession() {
223
+ const row = db.prepare(
224
+ "SELECT data FROM grill_sessions WHERE status = 'in_progress' ORDER BY updated_at DESC LIMIT 1"
225
+ ).get();
226
+ return row ? JSON.parse(row.data) : null;
227
+ },
228
+ saveDebt(debt) {
229
+ db.prepare(
230
+ `INSERT INTO cognitive_debts (id, session_id, ede_id, gap, reason, created_at)
231
+ VALUES (?, ?, ?, ?, ?, ?)`
232
+ ).run(debt.id, debt.sessionId, debt.edeId, debt.gap, debt.reason, debt.createdAt);
233
+ },
234
+ getDebts() {
235
+ return db.prepare("SELECT * FROM cognitive_debts ORDER BY created_at DESC").all();
236
+ }
237
+ };
238
+ }
239
+
240
+ // ../persistence/src/semantic-store.ts
241
+ function createSemanticStore(db) {
242
+ return {
243
+ index(node) {
244
+ db.prepare(
245
+ `INSERT OR REPLACE INTO session_nodes (id, session_id, type, content, metadata, source_doc_node, created_at)
246
+ VALUES (?, ?, ?, ?, ?, ?, ?)`
247
+ ).run(
248
+ node.id,
249
+ node.sessionId,
250
+ node.type,
251
+ node.content,
252
+ JSON.stringify(node.metadata),
253
+ node.sourceDocNode,
254
+ node.createdAt
255
+ );
256
+ db.prepare(
257
+ `INSERT OR REPLACE INTO session_nodes_fts (node_id, content, session_id, type)
258
+ VALUES (?, ?, ?, ?)`
259
+ ).run(node.id, node.content, node.sessionId, node.type);
260
+ },
261
+ search(query, filters) {
262
+ let ftsQuery = `SELECT node_id FROM session_nodes_fts WHERE content MATCH ?`;
263
+ const params = [query];
264
+ if (filters?.type) {
265
+ ftsQuery += ` AND type = ?`;
266
+ params.push(filters.type);
267
+ }
268
+ if (filters?.sessionId) {
269
+ ftsQuery += ` AND session_id = ?`;
270
+ params.push(filters.sessionId);
271
+ }
272
+ const ids = db.prepare(ftsQuery).pluck().all(...params);
273
+ if (ids.length === 0) return [];
274
+ const placeholders = ids.map(() => "?").join(",");
275
+ const rows = db.prepare(
276
+ `SELECT * FROM session_nodes WHERE id IN (${placeholders})`
277
+ ).all(...ids);
278
+ return rows.map((r) => ({
279
+ id: r.id,
280
+ sessionId: r.session_id,
281
+ type: r.type,
282
+ content: r.content,
283
+ metadata: JSON.parse(r.metadata || "{}"),
284
+ sourceDocNode: r.source_doc_node,
285
+ createdAt: r.created_at
286
+ }));
287
+ }
288
+ };
289
+ }
290
+
291
+ // ../orchestrator/src/gates/code-guard.ts
292
+ var codeGuard = {
293
+ id: "code-guard",
294
+ mode: "coercive",
295
+ evaluate: (context) => {
296
+ if (!context.edeId) {
297
+ return { pass: false, reason: "[S12] Modificaci\xF3n sin EDE asociada: bloqueada." };
298
+ }
299
+ if (context.edeStatus !== "accepted") {
300
+ return {
301
+ pass: false,
302
+ reason: `[S12] EDE ${context.edeId} no est\xE1 aprobada (status: ${context.edeStatus}): bloqueada.`
303
+ };
304
+ }
305
+ return { pass: true };
306
+ }
307
+ };
308
+
309
+ // ../orchestrator/src/gates/plan-guard.ts
310
+ var planGuard = {
311
+ id: "plan-guard",
312
+ mode: "normative",
313
+ evaluate: (context) => {
314
+ if (!context.plan) {
315
+ return { pass: false, reason: "[S12] Plan ausente: rechazado." };
316
+ }
317
+ if (context.plan.unitTests.length === 0) {
318
+ return { pass: false, reason: "[S12] Plan sin tests unitarios: rechazado." };
319
+ }
320
+ if (context.plan.sadPaths.length === 0) {
321
+ return { pass: false, reason: "[S12] Plan sin sad-paths: rechazado." };
322
+ }
323
+ return { pass: true };
324
+ }
325
+ };
326
+
327
+ // ../orchestrator/src/claude-context.ts
328
+ function assembleClaudeContext(edes) {
329
+ const accepted = edes.filter((e) => e.status === "accepted");
330
+ const sections = [];
331
+ sections.push("# Umbral \u2014 Contexto del Proyecto\n");
332
+ sections.push("> Auto-generado por Umbral. No editar manualmente.\n");
333
+ if (accepted.length === 0) {
334
+ sections.push("No hay EDEs activas.\n");
335
+ return sections.join("\n");
336
+ }
337
+ sections.push("## Decisiones Activas (EDEs)\n");
338
+ sections.push("Estas decisiones son vinculantes. Resp\xE9talas en todo c\xF3digo que generes.\n");
339
+ for (const ede of accepted) {
340
+ sections.push(`### ${ede.id}: ${ede.title}
341
+ `);
342
+ sections.push(`**Decisi\xF3n:** ${ede.whatAndHow.decision}
343
+ `);
344
+ sections.push(`**Mecanismo:** ${ede.whatAndHow.mechanism}
345
+ `);
346
+ sections.push(`**Rationale:** ${ede.why.rationale}
347
+ `);
348
+ if (ede.whatNotToDo.antiPatterns.length > 0) {
349
+ sections.push("**Anti-patterns (PROHIBIDO):**");
350
+ for (const ap of ede.whatNotToDo.antiPatterns) {
351
+ sections.push(`- ${ap}`);
352
+ }
353
+ sections.push("");
354
+ }
355
+ if (ede.contracts.layerContracts.length > 0) {
356
+ sections.push(
357
+ `**Contratos de capa:** ${ede.contracts.layerContracts.join(", ")}
358
+ `
359
+ );
360
+ }
361
+ }
362
+ sections.push("## Reglas Generales\n");
363
+ sections.push("- Fail-fast (S13): todo error de validaci\xF3n debe lanzar inmediatamente, nunca degradar en silencio.");
364
+ sections.push("- Fuente \xFAnica (S2): solo S2 detecta cambios. Otros subsistemas reaccionan al DocRegenEvent.");
365
+ sections.push("- Local-first: la base de datos vive en ~/.umbral/umbral.db (SQLite con FTS5 + sqlite-vec).");
366
+ sections.push("- No agregar dependencias externas sin justificaci\xF3n en una EDE.");
367
+ sections.push("");
368
+ return sections.join("\n");
369
+ }
370
+
371
+ // ../mcp-server/src/tools.ts
372
+ var TOOL_DEFINITIONS = [
373
+ {
374
+ name: "umbral_ede_list",
375
+ description: "List all EDEs (Explicit Decision Structures) in the project. Returns id, title, status, and cognitive level for each.",
376
+ inputSchema: {
377
+ type: "object",
378
+ properties: {
379
+ status: {
380
+ type: "string",
381
+ enum: ["accepted", "proposed", "deprecated"],
382
+ description: "Filter by status. Omit to list all."
383
+ }
384
+ }
385
+ }
386
+ },
387
+ {
388
+ name: "umbral_ede_get",
389
+ description: "Get full details of a specific EDE by ID. Includes decision, mechanism, rationale, anti-patterns, contracts, and tests.",
390
+ inputSchema: {
391
+ type: "object",
392
+ properties: {
393
+ id: { type: "string", description: "EDE ID (e.g. EDE-000-persistence)" }
394
+ },
395
+ required: ["id"]
396
+ }
397
+ },
398
+ {
399
+ name: "umbral_ede_search",
400
+ description: "Search EDEs by text query. Matches against title, decision, mechanism, rationale, and anti-patterns.",
401
+ inputSchema: {
402
+ type: "object",
403
+ properties: {
404
+ query: { type: "string", description: "Search query text" }
405
+ },
406
+ required: ["query"]
407
+ }
408
+ },
409
+ {
410
+ name: "umbral_context",
411
+ description: "Get the full assembled Umbral governance context. Contains all active decisions, anti-patterns, layer contracts, and rules.",
412
+ inputSchema: { type: "object", properties: {} }
413
+ },
414
+ {
415
+ name: "umbral_grill_status",
416
+ description: "Get the current Grill Me alignment session status. Shows active session, alignment score, and cognitive debts.",
417
+ inputSchema: { type: "object", properties: {} }
418
+ },
419
+ {
420
+ name: "umbral_semantic_search",
421
+ description: "Search indexed session content using FTS5 full-text search.",
422
+ inputSchema: {
423
+ type: "object",
424
+ properties: {
425
+ query: { type: "string", description: "FTS5 search query" },
426
+ type: { type: "string", description: "Filter by node type" }
427
+ },
428
+ required: ["query"]
429
+ }
430
+ },
431
+ {
432
+ name: "umbral_gate_check",
433
+ description: "Run a gate validation check. Validates EDE status and test plan against CodeGuard and PlanGuard.",
434
+ inputSchema: {
435
+ type: "object",
436
+ properties: {
437
+ edeId: { type: "string", description: "EDE ID to validate" },
438
+ edeStatus: { type: "string", description: "EDE status (accepted/proposed/deprecated)" },
439
+ unitTests: {
440
+ type: "array",
441
+ items: { type: "string" },
442
+ description: "List of unit test identifiers"
443
+ },
444
+ sadPaths: {
445
+ type: "array",
446
+ items: { type: "string" },
447
+ description: "List of sad path test identifiers"
448
+ }
449
+ },
450
+ required: ["edeId"]
451
+ }
452
+ }
453
+ ];
454
+ function searchEdes(edes, query) {
455
+ const q = query.toLowerCase();
456
+ return edes.filter((e) => {
457
+ const searchable = [
458
+ e.id,
459
+ e.title,
460
+ e.whatAndHow.decision,
461
+ e.whatAndHow.mechanism,
462
+ e.why.rationale,
463
+ ...e.whatNotToDo.antiPatterns
464
+ ].join(" ").toLowerCase();
465
+ return searchable.includes(q);
466
+ });
467
+ }
468
+ function handleToolCall(toolName, args, db) {
469
+ const edeStore = createEdeStore(db);
470
+ switch (toolName) {
471
+ case "umbral_ede_list": {
472
+ const all = edeStore.getAll();
473
+ const filtered = args.status ? all.filter((e) => e.status === args.status) : all;
474
+ return filtered.map((e) => ({
475
+ id: e.id,
476
+ title: e.title,
477
+ status: e.status,
478
+ cognitiveLevel: e.cognitiveLevel,
479
+ complexityTier: e.complexityTier,
480
+ decision: e.whatAndHow.decision
481
+ }));
482
+ }
483
+ case "umbral_ede_get": {
484
+ const ede = edeStore.getById(args.id);
485
+ if (!ede) return { error: `EDE ${args.id} not found` };
486
+ return ede;
487
+ }
488
+ case "umbral_ede_search": {
489
+ const all = edeStore.getAll();
490
+ const results = searchEdes(all, args.query);
491
+ return results.map((e) => ({
492
+ id: e.id,
493
+ title: e.title,
494
+ status: e.status,
495
+ decision: e.whatAndHow.decision,
496
+ mechanism: e.whatAndHow.mechanism
497
+ }));
498
+ }
499
+ case "umbral_context": {
500
+ const all = edeStore.getAll();
501
+ return { context: assembleClaudeContext(all) };
502
+ }
503
+ case "umbral_grill_status": {
504
+ const grillStore = createGrillStore(db);
505
+ const active = grillStore.getActiveSession();
506
+ const debts = grillStore.getDebts();
507
+ return {
508
+ activeSession: active ? {
509
+ id: active.id,
510
+ edeId: active.edeId,
511
+ status: active.status,
512
+ alignmentScore: active.alignmentScore,
513
+ roundCount: active.rounds.length
514
+ } : null,
515
+ cognitiveDebts: debts.length,
516
+ debts: debts.slice(0, 5)
517
+ };
518
+ }
519
+ case "umbral_semantic_search": {
520
+ const semanticStore = createSemanticStore(db);
521
+ const results = semanticStore.search(
522
+ args.query,
523
+ args.type ? { type: args.type } : void 0
524
+ );
525
+ return results.map((r) => ({
526
+ id: r.id,
527
+ type: r.type,
528
+ content: r.content.slice(0, 500),
529
+ sourceDocNode: r.sourceDocNode
530
+ }));
531
+ }
532
+ case "umbral_gate_check": {
533
+ const ctx = {
534
+ edeId: args.edeId,
535
+ edeStatus: args.edeStatus ?? "accepted",
536
+ plan: {
537
+ unitTests: args.unitTests ?? [],
538
+ sadPaths: args.sadPaths ?? [],
539
+ coverageTarget: 0.8
540
+ }
541
+ };
542
+ const codeResult = codeGuard.evaluate(ctx);
543
+ if (!codeResult.pass) {
544
+ return { pass: false, gate: "CodeGuard", reason: codeResult.reason };
545
+ }
546
+ const planResult = planGuard.evaluate(ctx);
547
+ if (!planResult.pass) {
548
+ return { pass: false, gate: "PlanGuard", reason: planResult.reason };
549
+ }
550
+ return { pass: true, gates: ["CodeGuard", "PlanGuard"] };
551
+ }
552
+ default:
553
+ return { error: `Unknown tool: ${toolName}` };
554
+ }
555
+ }
556
+
557
+ // ../mcp-server/src/index.ts
558
+ var server = new Server(
559
+ { name: "umbral-mcp", version: "0.0.1" },
560
+ { capabilities: { tools: {} } }
561
+ );
562
+ server.setRequestHandler(ListToolsRequestSchema, async () => ({
563
+ tools: TOOL_DEFINITIONS
564
+ }));
565
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
566
+ const { name, arguments: args } = request.params;
567
+ const db = openDb();
568
+ try {
569
+ const result = handleToolCall(name, args ?? {}, db);
570
+ return {
571
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
572
+ };
573
+ } catch (err) {
574
+ return {
575
+ content: [
576
+ {
577
+ type: "text",
578
+ text: JSON.stringify({
579
+ error: err instanceof Error ? err.message : String(err)
580
+ })
581
+ }
582
+ ],
583
+ isError: true
584
+ };
585
+ } finally {
586
+ db.close();
587
+ }
588
+ });
589
+ async function main() {
590
+ const transport = new StdioServerTransport();
591
+ await server.connect(transport);
592
+ }
593
+ main().catch((err) => {
594
+ console.error("Umbral MCP server failed to start:", err);
595
+ process.exit(1);
596
+ });
package/package.json ADDED
@@ -0,0 +1,39 @@
1
+ {
2
+ "name": "@umbral/cli",
3
+ "version": "0.0.1",
4
+ "private": false,
5
+ "type": "module",
6
+ "bin": {
7
+ "umbral": "./dist/index.js"
8
+ },
9
+ "main": "dist/index.js",
10
+ "types": "dist/index.d.ts",
11
+ "files": [
12
+ "dist"
13
+ ],
14
+ "scripts": {
15
+ "typecheck": "tsc --noEmit",
16
+ "build": "tsup",
17
+ "build:tsc": "tsc",
18
+ "test": "vitest run"
19
+ },
20
+ "dependencies": {
21
+ "@modelcontextprotocol/sdk": "^1",
22
+ "commander": "^12",
23
+ "better-sqlite3": "^11",
24
+ "sqlite-vec": "^0.1",
25
+ "zod": "^3"
26
+ },
27
+ "devDependencies": {
28
+ "@umbral/contracts": "workspace:*",
29
+ "@umbral/persistence": "workspace:*",
30
+ "@umbral/orchestrator": "workspace:*",
31
+ "@umbral/mcp-server": "workspace:*",
32
+ "@umbral/specialists": "workspace:*",
33
+ "@types/better-sqlite3": "^7",
34
+ "@types/node": "^22",
35
+ "tsup": "^8",
36
+ "typescript": "^5",
37
+ "vitest": "^3"
38
+ }
39
+ }