@happyvertical/smrt-cli 0.43.10 → 0.44.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/AGENTS.md CHANGED
@@ -82,6 +82,58 @@ after that commit. This is the mode for large index rollouts.
82
82
  - Without the flag, a batch containing explicit `CONCURRENTLY` DDL is rejected
83
83
  before the transaction opens — PostgreSQL cannot run it there.
84
84
 
85
+ ## `doctor`'s agent-surface section (#2591)
86
+
87
+ `smrt doctor` reports the application's complete agent-addressable surface —
88
+ generated model tools, declared view intents, registered playbooks — **from
89
+ build artifacts alone**. No route is mounted, no server starts, and no
90
+ application module is imported. That is the whole point: before this, the only
91
+ way to answer "what can an agent do in this app" was to mount every route and
92
+ enumerate, which is not an answer a build, a review, or a CI gate can use.
93
+
94
+ `readAgentSurfaceReport(cwd)` reads the first of `.smrt/smrt-knowledge.json`
95
+ then `dist/smrt-knowledge.json`. Model tools come from the artifact's `mcp`
96
+ surfaces (REST/CLI entries are not agent-addressable tools and are excluded);
97
+ intents and playbooks come from its `agentSurface`.
98
+
99
+ **Known under-report, tracked as #2619:** the artifact's `mcp` surfaces come
100
+ from `configuredSurfaces()` in core, which returns nothing when an object omits
101
+ `mcp` config — while `MCPGenerator` treats an omitted config as full CRUD and
102
+ also emits eligible public custom-method tools. Doctor therefore lists fewer
103
+ model tools than the runtime exposes for such an object. The error is
104
+ conservative (it never advertises a tool that does not exist), and the fix
105
+ belongs to the shared surface projection rather than this report.
106
+ `renderAgentSurfaceReport(report)` returns the printed lines; both are exported
107
+ so the shape is testable without running the command.
108
+
109
+ Sample output:
110
+
111
+ ```text
112
+ 🤖 Agent Surface
113
+
114
+ Source: .smrt/smrt-knowledge.json
115
+ Generated model tools: 2
116
+ - orders_create (Order.create)
117
+ - orders_list (Order.list)
118
+ Declared view intents: 1
119
+ - orders.next_page (read · browser · src/lib/orders.intents.ts)
120
+ Registered playbooks: 1
121
+ - commerce.checkout (2 step(s) · browser · src/lib/checkout.playbooks.ts)
122
+ ✅ 4 agent-addressable operation(s) reported
123
+ ```
124
+
125
+ A declaration the scanner recognized but could not read is listed under
126
+ `⚠️ Not statically emittable` with the message naming `useWebMcpTool`, and
127
+ raises a doctor **warning**, not an issue — a computed tool set is a legitimate
128
+ choice, but it must never vanish unremarked.
129
+
130
+ Reading an artifact validates its shape, not just its JSON: `{}` parses cleanly
131
+ and would otherwise render as a healthy zero-operation surface while suppressing
132
+ the fallback to a good `dist/` artifact, and `{ "surfaces": {} }` would throw
133
+ mid-report and abort the whole doctor run. A malformed candidate is stepped over
134
+ so the next one is still tried; with no readable artifact at all the section says
135
+ to run a build.
136
+
85
137
  ## Live-schema parity (#2368)
86
138
 
87
139
  `doctor --db` and `db:status --parity` share `src/commands/db-parity.ts`, which
@@ -581,7 +581,7 @@ function getUnresolvedGeneratedMigrationNames(changes) {
581
581
  for (const change of changes) {
582
582
  if (change.type !== "add_column" && change.type !== "drop_column" && change.type !== "alter_column" && change.type !== "add_foreign_key" && change.type !== "drop_foreign_key" && change.type !== "add_index" && change.type !== "drop_index" && change.type !== "type_upgrade") continue;
583
583
  if (change.type === "type_upgrade" && classifyTypeUpgradeSql(change.sql) === "noop") continue;
584
- if (change.type === "alter_column" && isAdvisoryOnlyChangeLike(change)) continue;
584
+ if ((change.type === "alter_column" || change.type === "type_upgrade") && isAdvisoryOnlyChangeLike(change)) continue;
585
585
  for (const migrationName of getSyntheticMigrationNamesForChange(change)) names.add(migrationName);
586
586
  }
587
587
  return names;
@@ -765,6 +765,18 @@ function partitionSchemaChanges(changes, getClassForTable) {
765
765
  case "type_upgrade": {
766
766
  const mm = change.mismatch;
767
767
  const col = change.column;
768
+ if (isAdvisoryOnlyChangeLike(change) && change.advisory) {
769
+ if (!change.name) continue;
770
+ advisories.push({
771
+ type: "type_upgrade",
772
+ tableName: change.table,
773
+ className,
774
+ name: change.name,
775
+ actual: mm?.actual,
776
+ advisory: change.advisory
777
+ });
778
+ break;
779
+ }
768
780
  if (!change.name || !mm || !col) continue;
769
781
  const action = {
770
782
  type: "type_upgrade",
@@ -777,6 +789,7 @@ function partitionSchemaChanges(changes, getClassForTable) {
777
789
  defaultValue: col.defaultValue,
778
790
  unique: col.unique
779
791
  },
792
+ ...change.phase ? { phase: change.phase } : {},
780
793
  mismatch: {
781
794
  column: change.name,
782
795
  expected: mm.expected,
@@ -840,6 +853,7 @@ function describeAdvisory(item) {
840
853
  switch (item.type) {
841
854
  case "orphan_column": return `${item.tableName}.${item.name}: orphan column${item.actual ? ` (${item.actual})` : ""}`;
842
855
  case "orphan_index": return `${item.tableName}.${item.name}: unique constraint not in manifest${item.actual ? ` (${item.actual})` : ""}`;
856
+ case "type_upgrade": return `${item.tableName}.${item.name}: type upgrade blocked${item.actual ? ` (live: ${item.actual})` : ""}`;
843
857
  default: return `${item.tableName}.${item.name}: ${item.alteration ?? "alter_column"}${item.actual ? ` (live: ${item.actual})` : ""}`;
844
858
  }
845
859
  }
@@ -7628,6 +7642,99 @@ function getErrorContext(error) {
7628
7642
  }
7629
7643
  }
7630
7644
  /**
7645
+ * Artifacts the surface is read from, in preference order.
7646
+ *
7647
+ * A dev/build working copy first, then a library build's output. Both are
7648
+ * BUILD artifacts — the whole point of the report is that "what can an agent do
7649
+ * in this app" is answerable without mounting a route or starting a server
7650
+ * (#2591). Nothing here loads application code.
7651
+ */
7652
+ var AGENT_SURFACE_ARTIFACTS = [".smrt/smrt-knowledge.json", "dist/smrt-knowledge.json"];
7653
+ /**
7654
+ * Whether a parsed artifact has the shape this report reads.
7655
+ *
7656
+ * Deliberately checks the exact fields the report touches — the schema marker,
7657
+ * `surfaces`, and, when present, each `agentSurface` list — rather than trusting
7658
+ * the type assertion. Everything here comes off disk and may predate the
7659
+ * current schema or have been written by something else entirely.
7660
+ */
7661
+ function isReadableKnowledgeArtifact(value) {
7662
+ if (!value || typeof value !== "object" || Array.isArray(value)) return false;
7663
+ const record = value;
7664
+ if (record.schemaVersion !== 1) return false;
7665
+ if (!Array.isArray(record.surfaces)) return false;
7666
+ const surface = record.agentSurface;
7667
+ if (surface === void 0) return true;
7668
+ if (!surface || typeof surface !== "object" || Array.isArray(surface)) return false;
7669
+ const agentSurface = surface;
7670
+ return Array.isArray(agentSurface.intents) && Array.isArray(agentSurface.playbooks) && Array.isArray(agentSurface.diagnostics);
7671
+ }
7672
+ /**
7673
+ * Read the complete agent-addressable surface from build artifacts alone.
7674
+ *
7675
+ * Model tools come from the artifact's emitted `mcp` surfaces, which is where
7676
+ * core has always recorded them; intents and playbooks come from the emitted
7677
+ * agent surface. They are reported together because an agent does not
7678
+ * experience them as three mechanisms — it experiences one list of things it
7679
+ * can do.
7680
+ *
7681
+ * @param cwd - Project root to read artifacts from.
7682
+ * @returns The report, or `undefined` when no artifact exists yet.
7683
+ */
7684
+ async function readAgentSurfaceReport(cwd) {
7685
+ const { existsSync, readFileSync } = await import("node:fs");
7686
+ const { resolve } = await import("node:path");
7687
+ for (const candidate of AGENT_SURFACE_ARTIFACTS) {
7688
+ const artifactPath = resolve(cwd, candidate);
7689
+ if (!existsSync(artifactPath)) continue;
7690
+ try {
7691
+ const parsed = JSON.parse(readFileSync(artifactPath, "utf-8"));
7692
+ if (!isReadableKnowledgeArtifact(parsed)) continue;
7693
+ const artifact = parsed;
7694
+ const surface = artifact.agentSurface;
7695
+ return {
7696
+ source: candidate,
7697
+ modelTools: (artifact.surfaces ?? []).filter((entry) => entry.kind === "mcp").map((entry) => ({
7698
+ kind: "model tool",
7699
+ name: String(entry.name),
7700
+ detail: `${entry.objectName ?? "?"}.${entry.operation}`
7701
+ })).sort((a, b) => a.name < b.name ? -1 : a.name > b.name ? 1 : 0),
7702
+ intents: (surface?.intents ?? []).map((intent) => ({
7703
+ kind: "view intent",
7704
+ name: String(intent.id),
7705
+ detail: `${intent.capability.effect} · ${intent.planes.join("+")} · ${intent.sourceFile}`
7706
+ })),
7707
+ playbooks: (surface?.playbooks ?? []).map((playbook) => ({
7708
+ kind: "playbook",
7709
+ name: String(playbook.key),
7710
+ detail: `${playbook.steps.length} step(s) · ${playbook.planes.join("+")} · ${playbook.sourceFile}`
7711
+ })),
7712
+ notStatic: (surface?.diagnostics ?? []).map((diagnostic) => ({
7713
+ sourceFile: diagnostic.line ? `${diagnostic.sourceFile}:${diagnostic.line}` : String(diagnostic.sourceFile),
7714
+ message: String(diagnostic.message)
7715
+ }))
7716
+ };
7717
+ } catch {}
7718
+ }
7719
+ }
7720
+ /** Render the surface report as the lines `smrt doctor` prints. */
7721
+ function renderAgentSurfaceReport(report) {
7722
+ if (!report) return [" ⚠️ No knowledge artifact found — run a build to emit the agent surface"];
7723
+ const lines = [` Source: ${report.source}`];
7724
+ const section = (label, entries) => {
7725
+ lines.push(` ${label}: ${entries.length}`);
7726
+ for (const entry of entries) lines.push(` - ${entry.name} (${entry.detail})`);
7727
+ };
7728
+ section("Generated model tools", report.modelTools);
7729
+ section("Declared view intents", report.intents);
7730
+ section("Registered playbooks", report.playbooks);
7731
+ if (report.notStatic.length > 0) {
7732
+ lines.push(` ⚠️ Not statically emittable: ${report.notStatic.length}`);
7733
+ for (const entry of report.notStatic) lines.push(` - ${entry.sourceFile}: ${entry.message}`);
7734
+ }
7735
+ return lines;
7736
+ }
7737
+ /**
7631
7738
  * Utility commands for CLI
7632
7739
  */
7633
7740
  var utilityCommands = {
@@ -8397,6 +8504,8 @@ export default testManifest;
8397
8504
  migrations.push(...partitionedChanges.migrations);
8398
8505
  manualInterventions.push(...partitionedChanges.manualInterventions);
8399
8506
  const advisories = partitionedChanges.advisories;
8507
+ const preForeignKeyMigrations = migrations.filter((migration) => migration.phase === "pre_foreign_key");
8508
+ const remainingMigrations = migrations.filter((migration) => migration.phase !== "pre_foreign_key");
8400
8509
  assertForceMigrationTargetsExist(forceSelection.forceMigrations, [
8401
8510
  ...diff.added_tables.map((schema) => `create_table_${schema.tableName}`),
8402
8511
  ...migrations.flatMap((migration) => {
@@ -8487,6 +8596,10 @@ export default testManifest;
8487
8596
  console.log();
8488
8597
  }
8489
8598
  console.log(" SQL Statements:\n");
8599
+ for (const m of preForeignKeyMigrations) {
8600
+ const sqlStatements = m.sqlStatements ?? (m.sql ? [m.sql] : []);
8601
+ for (const sql of sqlStatements) console.log(` ${sql};`);
8602
+ }
8490
8603
  for (const schema of tablePlan.schemas) {
8491
8604
  const ddl = plannedTableDDL.get(schema.tableName);
8492
8605
  if (!ddl) continue;
@@ -8500,7 +8613,7 @@ export default testManifest;
8500
8613
  const terminator = migration.sql.trimEnd().endsWith(";") ? "" : ";";
8501
8614
  console.log(` ${migration.sql}${terminator}`);
8502
8615
  }
8503
- for (const m of migrations) {
8616
+ for (const m of remainingMigrations) {
8504
8617
  const sqlStatements = m.sqlStatements ?? (m.sql ? [m.sql] : []);
8505
8618
  for (const sql of sqlStatements) console.log(` ${sql};`);
8506
8619
  }
@@ -8521,41 +8634,9 @@ export default testManifest;
8521
8634
  if (applySchemaMigrations && schemaChangeCount > 0) {
8522
8635
  const migrationDefs = [];
8523
8636
  const migrationLogs = /* @__PURE__ */ new Map();
8524
- for (const schema of tablePlan.schemas) {
8525
- const ddl = plannedTableDDL.get(schema.tableName);
8526
- if (!ddl) throw new Error(`Cannot create table ${schema.tableName}: planned DDL is unavailable.`);
8527
- const createTableSql = ddl.createTable || schema.ddl;
8528
- if (!createTableSql?.trim()) throw new Error(`Cannot create table ${schema.tableName}: schema definition has no generated DDL.`);
8529
- const migrationName = `create_table_${schema.tableName}`;
8530
- const fields = Object.keys(schema.columns).length;
8531
- migrationDefs.push({
8532
- id: migrationName,
8533
- description: `Create table ${schema.tableName}`,
8534
- version: "1.0.0",
8535
- up: [
8536
- createTableSql,
8537
- ...ddl.indexes,
8538
- ...ddl.triggers
8539
- ],
8540
- down: [`DROP TABLE IF EXISTS "${schema.tableName}"`]
8541
- });
8542
- migrationLogs.set(migrationName, { successMessage: `Created table ${schema.tableName} (${fields} columns)` });
8543
- }
8544
- for (const migration of deferredForeignKeyMigrations) {
8637
+ const pushMigrationDef = (migration) => {
8545
8638
  const migrationName = getSyntheticMigrationNameForAction(migration);
8546
- if (!migrationName) continue;
8547
- migrationDefs.push({
8548
- id: migrationName,
8549
- description: `Add deferred foreign-key constraint ${migration.className} on ${migration.tableName}`,
8550
- version: "1.0.0",
8551
- up: migration.sql ? [migration.sql] : [],
8552
- down: []
8553
- });
8554
- migrationLogs.set(migrationName, { successMessage: `Added deferred foreign-key constraint ${migration.className} on ${migration.tableName}` });
8555
- }
8556
- for (const migration of migrations) {
8557
- const migrationName = getSyntheticMigrationNameForAction(migration);
8558
- if (!migrationName) continue;
8639
+ if (!migrationName) return;
8559
8640
  let migrationSql;
8560
8641
  let actionDesc;
8561
8642
  if (migration.type === "add_column" && migration.column) {
@@ -8582,7 +8663,7 @@ export default testManifest;
8582
8663
  } else if (migration.type === "drop_foreign_key") {
8583
8664
  migrationSql = migration.sql || "";
8584
8665
  actionDesc = `Dropped foreign-key constraint on ${migration.tableName}`;
8585
- } else continue;
8666
+ } else return;
8586
8667
  const migrationSqlStatements = migration.sqlStatements ?? (migrationSql ? [migrationSql] : []);
8587
8668
  migrationDefs.push({
8588
8669
  id: migrationName,
@@ -8595,7 +8676,41 @@ export default testManifest;
8595
8676
  successMessage: actionDesc,
8596
8677
  skippedMessage: `${migrationName} already applied`
8597
8678
  });
8679
+ };
8680
+ for (const migration of preForeignKeyMigrations) pushMigrationDef(migration);
8681
+ for (const schema of tablePlan.schemas) {
8682
+ const ddl = plannedTableDDL.get(schema.tableName);
8683
+ if (!ddl) throw new Error(`Cannot create table ${schema.tableName}: planned DDL is unavailable.`);
8684
+ const createTableSql = ddl.createTable || schema.ddl;
8685
+ if (!createTableSql?.trim()) throw new Error(`Cannot create table ${schema.tableName}: schema definition has no generated DDL.`);
8686
+ const migrationName = `create_table_${schema.tableName}`;
8687
+ const fields = Object.keys(schema.columns).length;
8688
+ migrationDefs.push({
8689
+ id: migrationName,
8690
+ description: `Create table ${schema.tableName}`,
8691
+ version: "1.0.0",
8692
+ up: [
8693
+ createTableSql,
8694
+ ...ddl.indexes,
8695
+ ...ddl.triggers
8696
+ ],
8697
+ down: [`DROP TABLE IF EXISTS "${schema.tableName}"`]
8698
+ });
8699
+ migrationLogs.set(migrationName, { successMessage: `Created table ${schema.tableName} (${fields} columns)` });
8598
8700
  }
8701
+ for (const migration of deferredForeignKeyMigrations) {
8702
+ const migrationName = getSyntheticMigrationNameForAction(migration);
8703
+ if (!migrationName) continue;
8704
+ migrationDefs.push({
8705
+ id: migrationName,
8706
+ description: `Add deferred foreign-key constraint ${migration.className} on ${migration.tableName}`,
8707
+ version: "1.0.0",
8708
+ up: migration.sql ? [migration.sql] : [],
8709
+ down: []
8710
+ });
8711
+ migrationLogs.set(migrationName, { successMessage: `Added deferred foreign-key constraint ${migration.className} on ${migration.tableName}` });
8712
+ }
8713
+ for (const migration of remainingMigrations) pushMigrationDef(migration);
8599
8714
  const deferredIndexMigrations = concurrentIndexMode && engine === "postgres" ? buildConcurrentIndexPlan(migrationDefs, true).size : 0;
8600
8715
  const batchHasIndexDDL = migrations.some((migration) => migration.type === "add_index" || migration.type === "drop_index");
8601
8716
  console.log(deferredIndexMigrations > 0 ? `🔨 Applying ${schemaChangeCount} schema change(s) (non-index DDL atomically, ${deferredIndexMigrations} index migration(s) CONCURRENTLY)...\n` : `🔨 Applying ${schemaChangeCount} schema change(s) atomically...\n`);
@@ -8939,6 +9054,14 @@ export default testManifest;
8939
9054
  check("Project manifest readable", false, void 0, "Could not read .smrt/manifest.json to validate external registrations");
8940
9055
  }
8941
9056
  console.log();
9057
+ console.log("🤖 Agent Surface\n");
9058
+ const agentSurfaceReport = await readAgentSurfaceReport(cwd);
9059
+ for (const line of renderAgentSurfaceReport(agentSurfaceReport)) console.log(line);
9060
+ if (agentSurfaceReport) {
9061
+ check(`${agentSurfaceReport.modelTools.length + agentSurfaceReport.intents.length + agentSurfaceReport.playbooks.length} agent-addressable operation(s) reported`, true);
9062
+ if (agentSurfaceReport.notStatic.length > 0) check("Agent-surface declarations are all static", false, void 0, `${agentSurfaceReport.notStatic.length} declaration(s) could not be read statically and were excluded — use useWebMcpTool for a computed tool set`);
9063
+ } else check("Agent surface reported", false, void 0, "No knowledge artifact — run a build first");
9064
+ console.log();
8942
9065
  console.log("🏃 Runtime\n");
8943
9066
  const runtimeErrors = runtimeCheck.findings.filter((finding) => finding.severity === "error");
8944
9067
  const runtimeWarnings = runtimeCheck.findings.filter((finding) => finding.severity === "warning");
package/dist/index.js CHANGED
@@ -49,63 +49,63 @@ var _playgroundCommands = null;
49
49
  var _workbenchCommands = null;
50
50
  async function getGnodeCommands() {
51
51
  if (!_gnodeCommands) {
52
- const { gnodeCommands } = await import("./commands-BoyqL_79.js");
52
+ const { gnodeCommands } = await import("./commands-DBnnqrFy.js");
53
53
  _gnodeCommands = gnodeCommands;
54
54
  }
55
55
  return _gnodeCommands;
56
56
  }
57
57
  async function getGitCommands() {
58
58
  if (!_gitCommands) {
59
- const { gitCommands } = await import("./commands-BoyqL_79.js");
59
+ const { gitCommands } = await import("./commands-DBnnqrFy.js");
60
60
  _gitCommands = gitCommands;
61
61
  }
62
62
  return _gitCommands;
63
63
  }
64
64
  async function getGenerateCommands() {
65
65
  if (!_generateCommands) {
66
- const { generateCommands } = await import("./commands-BoyqL_79.js");
66
+ const { generateCommands } = await import("./commands-DBnnqrFy.js");
67
67
  _generateCommands = generateCommands;
68
68
  }
69
69
  return _generateCommands;
70
70
  }
71
71
  async function getInitCommands() {
72
72
  if (!_initCommands) {
73
- const { initCommands } = await import("./commands-BoyqL_79.js");
73
+ const { initCommands } = await import("./commands-DBnnqrFy.js");
74
74
  _initCommands = initCommands;
75
75
  }
76
76
  return _initCommands;
77
77
  }
78
78
  async function getUtilityCommands() {
79
79
  if (!_utilityCommands) {
80
- const { utilityCommands } = await import("./commands-BoyqL_79.js");
80
+ const { utilityCommands } = await import("./commands-DBnnqrFy.js");
81
81
  _utilityCommands = utilityCommands;
82
82
  }
83
83
  return _utilityCommands;
84
84
  }
85
85
  async function getDispatchCommands() {
86
86
  if (!_dispatchCommands) {
87
- const { dispatchCommands } = await import("./commands-BoyqL_79.js");
87
+ const { dispatchCommands } = await import("./commands-DBnnqrFy.js");
88
88
  _dispatchCommands = dispatchCommands;
89
89
  }
90
90
  return _dispatchCommands;
91
91
  }
92
92
  async function getDocsCommands() {
93
93
  if (!_docsCommands) {
94
- const { docsCommands } = await import("./commands-BoyqL_79.js");
94
+ const { docsCommands } = await import("./commands-DBnnqrFy.js");
95
95
  _docsCommands = docsCommands;
96
96
  }
97
97
  return _docsCommands;
98
98
  }
99
99
  async function getPlaygroundCommands() {
100
100
  if (!_playgroundCommands) {
101
- const { playgroundCommands } = await import("./commands-BoyqL_79.js");
101
+ const { playgroundCommands } = await import("./commands-DBnnqrFy.js");
102
102
  _playgroundCommands = playgroundCommands;
103
103
  }
104
104
  return _playgroundCommands;
105
105
  }
106
106
  async function getWorkbenchCommands() {
107
107
  if (!_workbenchCommands) {
108
- const { workbenchCommands } = await import("./commands-BoyqL_79.js");
108
+ const { workbenchCommands } = await import("./commands-DBnnqrFy.js");
109
109
  _workbenchCommands = workbenchCommands;
110
110
  }
111
111
  return _workbenchCommands;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@happyvertical/smrt-cli",
3
- "version": "0.43.10",
3
+ "version": "0.44.0",
4
4
  "description": "Developer CLI for SMRT framework - introspection, testing, and project management",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -32,12 +32,12 @@
32
32
  "acorn": "^8.17.0",
33
33
  "fast-glob": "3.3.3",
34
34
  "tar": "^7.5.19",
35
- "@happyvertical/smrt-core": "0.43.10",
36
- "@happyvertical/smrt-dev-mcp": "0.43.10",
37
- "@happyvertical/smrt-playground": "0.43.10",
38
- "@happyvertical/smrt-config": "0.43.10",
39
- "@happyvertical/smrt-types": "0.43.10",
40
- "@happyvertical/smrt-agents": "0.43.10"
35
+ "@happyvertical/smrt-agents": "0.44.0",
36
+ "@happyvertical/smrt-core": "0.44.0",
37
+ "@happyvertical/smrt-dev-mcp": "0.44.0",
38
+ "@happyvertical/smrt-config": "0.44.0",
39
+ "@happyvertical/smrt-types": "0.44.0",
40
+ "@happyvertical/smrt-playground": "0.44.0"
41
41
  },
42
42
  "devDependencies": {
43
43
  "@types/node": "24.13.2",