@decantr/mcp-server 1.0.5 → 2.0.0

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Decantr AI
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -144,7 +144,7 @@ For the broader product surface and support policy, see the root Decantr docs an
144
144
 
145
145
  ## Compatibility
146
146
 
147
- `@decantr/mcp-server` is stable in the `1.x` line for the documented MCP tool surface.
147
+ `@decantr/mcp-server` is stable in the `2.x` line for the documented MCP tool surface.
148
148
 
149
149
  - new tools may be added in compatible releases
150
150
  - existing documented tool names and envelopes should not break without a major version
package/dist/bin.js CHANGED
@@ -1,2 +1,2 @@
1
1
  #!/usr/bin/env node
2
- import "./chunk-A4ZCCVQR.js";
2
+ import "./chunk-MUNBODQK.js";
@@ -7,14 +7,14 @@ import { CallToolRequestSchema, ListToolsRequestSchema } from "@modelcontextprot
7
7
  import { existsSync, readdirSync, readFileSync } from "fs";
8
8
  import { readFile as readFile2 } from "fs/promises";
9
9
  import { basename as basename2, dirname as dirname2, join as join2, relative as relative2 } from "path";
10
- import { evaluateGuard, isV3 as isV32, validateEssence } from "@decantr/essence-spec";
10
+ import { evaluateGuard, isV4 as isV42, validateEssence } from "@decantr/essence-spec";
11
11
  import { isContentIntelligenceSource, resolvePatternPreset } from "@decantr/registry";
12
12
 
13
13
  // src/helpers.ts
14
14
  import { realpathSync } from "fs";
15
15
  import { mkdir, readFile, writeFile } from "fs/promises";
16
16
  import { basename, dirname, isAbsolute, join, relative, resolve } from "path";
17
- import { isV3, migrateV2ToV3 } from "@decantr/essence-spec";
17
+ import { isV4 } from "@decantr/essence-spec";
18
18
  import { RegistryAPIClient } from "@decantr/registry";
19
19
  var MAX_INPUT_LENGTH = 1e3;
20
20
  function validateStringArg(args, field) {
@@ -95,8 +95,13 @@ async function writeEssenceFile(essencePath, essence) {
95
95
  }
96
96
  async function mutateEssenceFile(essencePath, mutate) {
97
97
  const { essence, path } = await readEssenceFile(essencePath);
98
- const v3 = isV3(essence) ? structuredClone(essence) : migrateV2ToV3(essence);
99
- const updated = mutate(v3);
98
+ if (!isV4(essence)) {
99
+ throw new Error(
100
+ "Active Decantr V2 workflows require Essence v4.0.0. Run `decantr migrate --to v4` for older essence files."
101
+ );
102
+ }
103
+ const v4 = structuredClone(essence);
104
+ const updated = mutate(v4);
100
105
  await writeEssenceFile(path, updated);
101
106
  return { essence: updated, path };
102
107
  }
@@ -469,7 +474,7 @@ var TOOLS = [
469
474
  {
470
475
  name: "decantr_read_essence",
471
476
  title: "Read Essence",
472
- description: "Read and return the current decantr.essence.json file from the working directory. For v3 files, optionally filter by layer (dna, blueprint, or full).",
477
+ description: "Read and return the current Essence v4 decantr.essence.json file from the working directory. Optionally filter by layer (dna, blueprint, or full).",
473
478
  inputSchema: {
474
479
  type: "object",
475
480
  properties: {
@@ -480,7 +485,7 @@ var TOOLS = [
480
485
  layer: {
481
486
  type: "string",
482
487
  enum: ["dna", "blueprint", "full"],
483
- description: "For v3 essences: return only the specified layer. Defaults to full."
488
+ description: "For Essence v4 files: return only the specified layer. Defaults to full."
484
489
  }
485
490
  }
486
491
  },
@@ -490,7 +495,7 @@ var TOOLS = [
490
495
  {
491
496
  name: "decantr_validate",
492
497
  title: "Validate Essence",
493
- description: "Validate a decantr.essence.json file against the schema and guard rules. For v3, reports DNA vs Blueprint violations separately.",
498
+ description: "Validate an Essence v4 decantr.essence.json file against the schema and guard rules, reporting DNA vs Blueprint violations separately.",
494
499
  inputSchema: {
495
500
  type: "object",
496
501
  properties: {
@@ -593,7 +598,7 @@ var TOOLS = [
593
598
  {
594
599
  name: "decantr_check_drift",
595
600
  title: "Check Drift",
596
- description: "Check if code changes violate the design intent captured in the Essence spec. For v3, returns separate dna_violations and blueprint_drift with autoFixable flags.",
601
+ description: "Check if code changes violate the design intent captured in the Essence v4 spec. Returns separate dna_violations and blueprint_drift with autoFixable flags.",
597
602
  inputSchema: {
598
603
  type: "object",
599
604
  properties: {
@@ -619,7 +624,7 @@ var TOOLS = [
619
624
  {
620
625
  name: "decantr_create_essence",
621
626
  title: "Create Essence",
622
- description: "Generate a valid v3 Essence spec skeleton from a project description. Returns a structured essence.json template based on the closest matching archetype and blueprint.",
627
+ description: "Generate a valid Essence v4 skeleton from a project description. Returns a sectioned decantr.essence.json template based on the closest matching archetype and blueprint.",
623
628
  inputSchema: {
624
629
  type: "object",
625
630
  properties: {
@@ -680,7 +685,7 @@ var TOOLS = [
680
685
  {
681
686
  name: "decantr_update_essence",
682
687
  title: "Update Essence",
683
- description: "Mutate the essence file: add/remove/update pages, update DNA or blueprint fields, add/remove features. Operates on v3 format (auto-migrates v2).",
688
+ description: "Mutate an Essence v4 file: add/remove/update pages, update DNA or blueprint fields, add/remove features. Older projects must run `decantr migrate --to v4` first.",
684
689
  inputSchema: {
685
690
  type: "object",
686
691
  properties: {
@@ -946,7 +951,12 @@ async function handleTool(name, args) {
946
951
  const raw = await readFile2(essencePath, "utf-8");
947
952
  const essence = JSON.parse(raw);
948
953
  const layer = args.layer;
949
- if (layer && isV32(essence)) {
954
+ if (!isV42(essence)) {
955
+ return {
956
+ error: "Active Decantr V2 workflows require Essence v4.0.0. Run `decantr migrate --to v4` for older essence files."
957
+ };
958
+ }
959
+ if (layer) {
950
960
  if (layer === "dna") return essence.dna;
951
961
  if (layer === "blueprint") return essence.blueprint;
952
962
  }
@@ -975,13 +985,13 @@ async function handleTool(name, args) {
975
985
  } catch {
976
986
  }
977
987
  }
978
- if (result.valid && typeof essence === "object" && essence !== null && isV32(essence)) {
988
+ if (result.valid && typeof essence === "object" && essence !== null && isV42(essence)) {
979
989
  const dnaViolations = guardViolations.filter((v) => v.layer === "dna");
980
990
  const blueprintViolations = guardViolations.filter((v) => v.layer === "blueprint");
981
991
  const otherViolations = guardViolations.filter((v) => !v.layer);
982
992
  return {
983
993
  ...result,
984
- format: "v3",
994
+ format: "v4",
985
995
  dna_violations: dnaViolations,
986
996
  blueprint_violations: blueprintViolations,
987
997
  guardViolations: otherViolations
@@ -1189,51 +1199,43 @@ async function handleTool(name, args) {
1189
1199
  if (!validation.valid) {
1190
1200
  return { drifted: true, reason: "invalid_essence", errors: validation.errors };
1191
1201
  }
1202
+ if (!isV42(essence)) {
1203
+ return {
1204
+ drifted: true,
1205
+ reason: "legacy_essence",
1206
+ errors: [
1207
+ "Active Decantr V2 workflows require Essence v4.0.0. Run `decantr migrate --to v4` for older essence files."
1208
+ ]
1209
+ };
1210
+ }
1192
1211
  const violations = [];
1193
1212
  if (args.theme_used && typeof args.theme_used === "string") {
1194
- let expectedThemeId;
1195
- if (isV32(essence)) {
1196
- expectedThemeId = essence.dna.theme.id;
1197
- } else {
1198
- const expectedTheme = essence.theme;
1199
- expectedThemeId = expectedTheme?.id ?? expectedTheme?.style;
1200
- }
1213
+ const expectedThemeId = essence.dna.theme.id;
1201
1214
  if (expectedThemeId && args.theme_used !== expectedThemeId) {
1202
1215
  violations.push({
1203
1216
  rule: "theme-match",
1204
1217
  severity: "critical",
1205
1218
  message: `Theme drift: code uses "${args.theme_used}" but Essence specifies "${expectedThemeId}". Do not switch themes.`,
1206
- ...isV32(essence) ? { layer: "dna", autoFixable: false } : {}
1219
+ layer: "dna",
1220
+ autoFixable: false
1207
1221
  });
1208
1222
  }
1209
1223
  }
1210
1224
  if (args.page_id && typeof args.page_id === "string") {
1211
- let pages;
1212
- if (isV32(essence)) {
1213
- pages = essence.blueprint.pages;
1214
- } else {
1215
- pages = essence.structure || [];
1216
- }
1225
+ const pages = listEssencePages(essence);
1217
1226
  if (!pages.find((p) => p.id === args.page_id)) {
1218
1227
  violations.push({
1219
1228
  rule: "page-exists",
1220
1229
  severity: "critical",
1221
1230
  message: `Page "${args.page_id}" not found in Essence structure. Add it to the Essence before generating code for it.`,
1222
- ...isV32(essence) ? {
1223
- layer: "blueprint",
1224
- autoFixable: true,
1225
- autoFix: { type: "add_page", patch: { id: args.page_id } }
1226
- } : {}
1231
+ layer: "blueprint",
1232
+ autoFixable: true,
1233
+ autoFix: { type: "add_page", patch: { id: args.page_id } }
1227
1234
  });
1228
1235
  }
1229
1236
  }
1230
1237
  if (args.components_used && Array.isArray(args.components_used) && args.page_id && typeof args.page_id === "string") {
1231
- let pages;
1232
- if (isV32(essence)) {
1233
- pages = essence.blueprint.pages;
1234
- } else {
1235
- pages = essence.structure || [];
1236
- }
1238
+ const pages = listEssencePages(essence);
1237
1239
  const page = pages.find((p) => p.id === args.page_id);
1238
1240
  if (page && page.layout) {
1239
1241
  const expectedPatterns = /* @__PURE__ */ new Set();
@@ -1265,7 +1267,8 @@ async function handleTool(name, args) {
1265
1267
  rule: "component-pattern-match",
1266
1268
  severity: "warning",
1267
1269
  message: `Components [${unmatchedComponents.join(", ")}] do not match any pattern in page "${args.page_id}" layout. Expected patterns: [${[...expectedPatterns].join(", ")}].`,
1268
- ...isV32(essence) ? { layer: "blueprint", autoFixable: false } : {}
1270
+ layer: "blueprint",
1271
+ autoFixable: false
1269
1272
  });
1270
1273
  }
1271
1274
  }
@@ -1286,21 +1289,11 @@ async function handleTool(name, args) {
1286
1289
  }
1287
1290
  } catch {
1288
1291
  }
1289
- if (isV32(essence)) {
1290
- const dnaViolations = violations.filter((v) => v.layer === "dna");
1291
- const blueprintDrift = violations.filter((v) => v.layer === "blueprint");
1292
- const other = violations.filter((v) => !v.layer);
1293
- return {
1294
- drifted: violations.length > 0,
1295
- dna_violations: dnaViolations,
1296
- blueprint_drift: blueprintDrift,
1297
- other_violations: other,
1298
- checkedAgainst: essencePath
1299
- };
1300
- }
1301
1292
  return {
1302
1293
  drifted: violations.length > 0,
1303
- violations,
1294
+ dna_violations: violations.filter((v) => v.layer === "dna"),
1295
+ blueprint_drift: violations.filter((v) => v.layer === "blueprint"),
1296
+ other_violations: violations.filter((v) => !v.layer),
1304
1297
  checkedAgainst: essencePath
1305
1298
  };
1306
1299
  }
@@ -1348,8 +1341,17 @@ async function handleTool(name, args) {
1348
1341
  }
1349
1342
  const rawPages = pages || [{ id: "home", shell: "full-bleed", default_layout: ["hero"] }];
1350
1343
  const defaultShell = rawPages[0]?.shell || "sidebar-main";
1344
+ const sectionPages = rawPages.map((p, index) => ({
1345
+ id: p.id,
1346
+ route: p.id === "home" || index === 0 ? "/" : `/${p.id}`,
1347
+ ...p.shell !== defaultShell ? { shell_override: p.shell } : {},
1348
+ layout: p.default_layout || []
1349
+ }));
1350
+ const routes = Object.fromEntries(
1351
+ sectionPages.map((page) => [page.route, { section: bestMatch, page: page.id }])
1352
+ );
1351
1353
  const essence = {
1352
- version: "3.0.0",
1354
+ version: "4.0.0",
1353
1355
  dna: {
1354
1356
  theme: {
1355
1357
  id: "auradecantism",
@@ -1394,12 +1396,18 @@ async function handleTool(name, args) {
1394
1396
  },
1395
1397
  blueprint: {
1396
1398
  shell: defaultShell,
1397
- pages: rawPages.map((p) => ({
1398
- id: p.id,
1399
- ...p.shell !== defaultShell ? { shell_override: p.shell } : {},
1400
- layout: p.default_layout || []
1401
- })),
1402
- features
1399
+ sections: [
1400
+ {
1401
+ id: bestMatch,
1402
+ role: "primary",
1403
+ shell: defaultShell,
1404
+ features,
1405
+ description: `${bestMatch} primary section`,
1406
+ pages: sectionPages
1407
+ }
1408
+ ],
1409
+ features,
1410
+ routes
1403
1411
  },
1404
1412
  meta: {
1405
1413
  archetype: bestMatch,
@@ -1411,7 +1419,7 @@ async function handleTool(name, args) {
1411
1419
  return {
1412
1420
  essence,
1413
1421
  archetype: bestMatch,
1414
- format: "v3",
1422
+ format: "v4",
1415
1423
  instructions: `Save this as decantr.essence.json in your project root. Review the dna (design tokens), blueprint (pages/features), and meta (project config) sections and adjust to match your needs. The guard rules will validate your code against this spec.`,
1416
1424
  _generated: {
1417
1425
  matched_archetype: bestMatch,
@@ -1477,11 +1485,11 @@ async function handleTool(name, args) {
1477
1485
  };
1478
1486
  }
1479
1487
  try {
1480
- const { essence, path } = await mutateEssenceFile(args.path, (v3) => {
1488
+ const { essence, path } = await mutateEssenceFile(args.path, (v4) => {
1481
1489
  for (const v of violations) {
1482
- applyDriftAcceptance(v3, v, resolution, args.scope);
1490
+ applyDriftAcceptance(v4, v, resolution, args.scope);
1483
1491
  }
1484
- return v3;
1492
+ return v4;
1485
1493
  });
1486
1494
  return {
1487
1495
  status: resolution === "accept_scoped" ? "accepted_scoped" : "accepted",
@@ -1517,8 +1525,8 @@ async function handleTool(name, args) {
1517
1525
  };
1518
1526
  }
1519
1527
  try {
1520
- const { essence, path } = await mutateEssenceFile(args.path, (v3) => {
1521
- return applyEssenceUpdate(v3, operation, payload);
1528
+ const { essence, path } = await mutateEssenceFile(args.path, (v4) => {
1529
+ return applyEssenceUpdate(v4, operation, payload);
1522
1530
  });
1523
1531
  return {
1524
1532
  status: "updated",
@@ -1647,8 +1655,10 @@ async function handleTool(name, args) {
1647
1655
  } catch {
1648
1656
  return { error: "No valid essence file found. Run decantr init first." };
1649
1657
  }
1650
- if (!isV32(essence)) {
1651
- return { error: "Section context requires a v3 essence file. Run decantr migrate first." };
1658
+ if (!isV42(essence)) {
1659
+ return {
1660
+ error: "Section context requires Essence v4.0.0. Run `decantr migrate --to v4` first."
1661
+ };
1652
1662
  }
1653
1663
  const sections = essence.blueprint.sections || [];
1654
1664
  const section = sections.find((s) => s.id === sectionId);
@@ -2096,6 +2106,28 @@ async function handleTool(name, args) {
2096
2106
  return { error: `Unknown tool: ${name}` };
2097
2107
  }
2098
2108
  }
2109
+ function listEssencePages(essence) {
2110
+ return essence.blueprint.sections.flatMap(
2111
+ (section) => section.pages.map((page) => ({ ...page, sectionId: section.id }))
2112
+ );
2113
+ }
2114
+ function getMutablePage(essence, id, sectionId) {
2115
+ for (const section of essence.blueprint.sections) {
2116
+ if (sectionId && section.id !== sectionId) continue;
2117
+ const index = section.pages.findIndex((page) => page.id === id);
2118
+ if (index !== -1) {
2119
+ return { page: section.pages[index], section, index };
2120
+ }
2121
+ }
2122
+ return null;
2123
+ }
2124
+ function getDefaultSection(essence) {
2125
+ const section = essence.blueprint.sections.find((s) => s.role === "primary") ?? essence.blueprint.sections[0];
2126
+ if (!section) {
2127
+ throw new Error("Essence v4 requires at least one blueprint section.");
2128
+ }
2129
+ return section;
2130
+ }
2099
2131
  function applyDriftAcceptance(essence, violation, resolution, scope) {
2100
2132
  switch (violation.rule) {
2101
2133
  case "theme-match":
@@ -2109,9 +2141,10 @@ function applyDriftAcceptance(essence, violation, resolution, scope) {
2109
2141
  case "page-exists":
2110
2142
  case "structure": {
2111
2143
  if (violation.page_id) {
2112
- const existing = essence.blueprint.pages.find((p) => p.id === violation.page_id);
2144
+ const section = getDefaultSection(essence);
2145
+ const existing = getMutablePage(essence, violation.page_id);
2113
2146
  if (!existing) {
2114
- essence.blueprint.pages.push({
2147
+ section.pages.push({
2115
2148
  id: violation.page_id,
2116
2149
  layout: []
2117
2150
  });
@@ -2134,11 +2167,15 @@ function applyEssenceUpdate(essence, operation, payload) {
2134
2167
  case "add_page": {
2135
2168
  const id = payload.id;
2136
2169
  if (!id) throw new Error('Payload must include "id" for add_page.');
2137
- const existing = essence.blueprint.pages.find((p) => p.id === id);
2170
+ const sectionId = payload.section_id;
2171
+ const section = sectionId ? essence.blueprint.sections.find((candidate) => candidate.id === sectionId) : getDefaultSection(essence);
2172
+ if (!section) throw new Error(`Section "${sectionId}" not found.`);
2173
+ const existing = getMutablePage(essence, id, section.id);
2138
2174
  if (existing) throw new Error(`Page "${id}" already exists.`);
2139
- essence.blueprint.pages.push({
2175
+ section.pages.push({
2140
2176
  id,
2141
2177
  layout: payload.layout || [],
2178
+ ...payload.route ? { route: payload.route } : {},
2142
2179
  ...payload.shell_override ? { shell_override: payload.shell_override } : {},
2143
2180
  ...payload.surface ? { surface: payload.surface } : {}
2144
2181
  });
@@ -2147,9 +2184,9 @@ function applyEssenceUpdate(essence, operation, payload) {
2147
2184
  case "remove_page": {
2148
2185
  const id = payload.id;
2149
2186
  if (!id) throw new Error('Payload must include "id" for remove_page.');
2150
- const idx = essence.blueprint.pages.findIndex((p) => p.id === id);
2151
- if (idx === -1) throw new Error(`Page "${id}" not found.`);
2152
- essence.blueprint.pages.splice(idx, 1);
2187
+ const match = getMutablePage(essence, id, payload.section_id);
2188
+ if (!match) throw new Error(`Page "${id}" not found.`);
2189
+ match.section.pages.splice(match.index, 1);
2153
2190
  break;
2154
2191
  }
2155
2192
  case "update_page_layout": {
@@ -2158,9 +2195,9 @@ function applyEssenceUpdate(essence, operation, payload) {
2158
2195
  if (!id) throw new Error('Payload must include "id" for update_page_layout.');
2159
2196
  if (!layout || !Array.isArray(layout))
2160
2197
  throw new Error('Payload must include "layout" array for update_page_layout.');
2161
- const page = essence.blueprint.pages.find((p) => p.id === id);
2162
- if (!page) throw new Error(`Page "${id}" not found.`);
2163
- page.layout = layout;
2198
+ const match = getMutablePage(essence, id, payload.section_id);
2199
+ if (!match) throw new Error(`Page "${id}" not found.`);
2200
+ match.page.layout = layout;
2164
2201
  break;
2165
2202
  }
2166
2203
  case "update_dna": {
@@ -2178,7 +2215,7 @@ function applyEssenceUpdate(essence, operation, payload) {
2178
2215
  }
2179
2216
  case "update_blueprint": {
2180
2217
  for (const [key, value] of Object.entries(payload)) {
2181
- if (key === "pages") continue;
2218
+ if (key === "pages" || key === "sections") continue;
2182
2219
  essence.blueprint[key] = value;
2183
2220
  }
2184
2221
  break;
@@ -2224,7 +2261,7 @@ function describeUpdate(operation, payload) {
2224
2261
  }
2225
2262
 
2226
2263
  // src/index.ts
2227
- var VERSION = "0.2.0";
2264
+ var VERSION = "2.0.0";
2228
2265
  var server = new Server({ name: "decantr", version: VERSION }, { capabilities: { tools: {} } });
2229
2266
  server.setRequestHandler(ListToolsRequestSchema, async () => {
2230
2267
  return { tools: TOOLS };
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- import "./chunk-A4ZCCVQR.js";
1
+ import "./chunk-MUNBODQK.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@decantr/mcp-server",
3
- "version": "1.0.5",
3
+ "version": "2.0.0",
4
4
  "mcpName": "io.github.decantr-ai/mcp-server",
5
5
  "description": "MCP server for Decantr — exposes design intelligence, packs, and verification to AI coding assistants",
6
6
  "keywords": [
@@ -44,17 +44,16 @@
44
44
  "publishConfig": {
45
45
  "access": "public"
46
46
  },
47
+ "dependencies": {
48
+ "@modelcontextprotocol/sdk": "^1.29.0",
49
+ "@decantr/registry": "2.0.0",
50
+ "@decantr/verifier": "2.0.0",
51
+ "@decantr/essence-spec": "2.0.1"
52
+ },
47
53
  "scripts": {
48
54
  "build": "tsup",
49
55
  "test": "vitest run",
50
56
  "test:watch": "vitest",
51
- "prepublishOnly": "pnpm build",
52
57
  "preversion": "pnpm build && pnpm test"
53
- },
54
- "dependencies": {
55
- "@decantr/essence-spec": "workspace:*",
56
- "@decantr/registry": "workspace:*",
57
- "@decantr/verifier": "workspace:*",
58
- "@modelcontextprotocol/sdk": "^1.29.0"
59
58
  }
60
- }
59
+ }