@happyvertical/smrt-cli 0.43.9 → 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
package/README.md CHANGED
@@ -146,7 +146,16 @@ layout that means declaring them, not relying on the CLI's own dependencies.
146
146
  |---------|------------|
147
147
  | `smrt config:export` | Export agent config for SSG |
148
148
  | `smrt export` | Export data in various formats |
149
- | `smrt init` | Initialize a new s-m-r-t project |
149
+ | `smrt init` | Initialize s-m-r-t in an existing SvelteKit project |
150
+
151
+ `smrt init` updates an existing SvelteKit application's `package.json` with the
152
+ direct dependencies its generated source and default MCP server require:
153
+ `@happyvertical/smrt-core`, `@happyvertical/smrt-config`, and
154
+ `@modelcontextprotocol/server`. It also adds `@happyvertical/smrt-cli` as a
155
+ development dependency so the generated project can run `smrt generate-mcp`.
156
+ Run the project's package-manager install command after initialization. Existing
157
+ versions are preserved; s-m-r-t workspace projects receive `workspace:*` ranges and
158
+ consumer projects receive the CLI's published release line.
150
159
 
151
160
  ### Dispatch
152
161
 
@@ -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
  }
@@ -5564,6 +5578,74 @@ var gnodeCommands = {
5564
5578
  *
5565
5579
  * Initialize SMRT in an existing SvelteKit project
5566
5580
  */
5581
+ var RUNTIME_DEPENDENCIES = [
5582
+ "@happyvertical/smrt-core",
5583
+ "@happyvertical/smrt-config",
5584
+ "@modelcontextprotocol/server"
5585
+ ];
5586
+ var CLI_DEPENDENCY = "@happyvertical/smrt-cli";
5587
+ var MCP_SERVER_VERSION = "2.0.0";
5588
+ /**
5589
+ * Find the SMRT monorepo root when init runs against one of its workspace
5590
+ * projects. Consumer projects must use the published CLI release line instead.
5591
+ */
5592
+ function findSmrtWorkspaceRoot(startDir) {
5593
+ let current = resolve(startDir);
5594
+ while (true) {
5595
+ if (existsSync(join(current, "pnpm-workspace.yaml")) && existsSync(join(current, "packages", "smrt-playground", "host", "package.json"))) return current;
5596
+ const parent = dirname(current);
5597
+ if (parent === current) return null;
5598
+ current = parent;
5599
+ }
5600
+ }
5601
+ function getSmrtDependencyVersion(projectRoot) {
5602
+ if (findSmrtWorkspaceRoot(projectRoot)) return "workspace:*";
5603
+ let current = dirname(fileURLToPath(import.meta.url));
5604
+ while (true) {
5605
+ const packageJsonPath = join(current, "package.json");
5606
+ if (existsSync(packageJsonPath)) {
5607
+ const cliPackageJson = JSON.parse(readFileSync(packageJsonPath, "utf-8"));
5608
+ if (cliPackageJson.name === CLI_DEPENDENCY && cliPackageJson.version) return `^${cliPackageJson.version}`;
5609
+ }
5610
+ const parent = dirname(current);
5611
+ if (parent === current) break;
5612
+ current = parent;
5613
+ }
5614
+ throw new Error("Could not determine the installed SMRT CLI version");
5615
+ }
5616
+ /**
5617
+ * Declare the packages emitted by init and the default MCP generator. Generated
5618
+ * MCP servers resolve from the host application under pnpm, so the CLI's own
5619
+ * transitive dependencies are not sufficient.
5620
+ */
5621
+ function ensureInitDependencies(packageJsonPath, packageJson, projectRoot) {
5622
+ const smrtVersion = getSmrtDependencyVersion(projectRoot);
5623
+ const added = [];
5624
+ let changed = false;
5625
+ packageJson.dependencies ??= {};
5626
+ packageJson.devDependencies ??= {};
5627
+ for (const dependency of RUNTIME_DEPENDENCIES) {
5628
+ if (packageJson.dependencies[dependency]) {
5629
+ if (dependency in packageJson.devDependencies) {
5630
+ delete packageJson.devDependencies[dependency];
5631
+ changed = true;
5632
+ }
5633
+ continue;
5634
+ }
5635
+ const version = packageJson.devDependencies[dependency] ?? (dependency === "@modelcontextprotocol/server" ? MCP_SERVER_VERSION : smrtVersion);
5636
+ packageJson.dependencies[dependency] = version;
5637
+ delete packageJson.devDependencies[dependency];
5638
+ added.push(`${dependency}@${version}`);
5639
+ changed = true;
5640
+ }
5641
+ if (!packageJson.dependencies[CLI_DEPENDENCY] && !packageJson.devDependencies[CLI_DEPENDENCY]) {
5642
+ packageJson.devDependencies[CLI_DEPENDENCY] = smrtVersion;
5643
+ added.push(`${CLI_DEPENDENCY}@${smrtVersion}`);
5644
+ changed = true;
5645
+ }
5646
+ if (changed) writeFileSync(packageJsonPath, `${JSON.stringify(packageJson, null, 2)}\n`);
5647
+ return added;
5648
+ }
5567
5649
  /**
5568
5650
  * Template for smrt.config.ts
5569
5651
  */
@@ -5789,6 +5871,7 @@ var initCommands = { init: {
5789
5871
  console.warn("⚠️ This does not appear to be a SvelteKit project.");
5790
5872
  console.warn(" SMRT init is designed for SvelteKit. Continuing anyway...\n");
5791
5873
  }
5874
+ const dependenciesAdded = ensureInitDependencies(packageJsonPath, packageJson, cwd);
5792
5875
  const created = [];
5793
5876
  const skipped = [];
5794
5877
  const writeFile = (path, content, description) => {
@@ -5823,7 +5906,6 @@ var initCommands = { init: {
5823
5906
  created.push("data/ - Database directory");
5824
5907
  }
5825
5908
  }
5826
- const hasSmrt = packageJson.dependencies?.["@happyvertical/smrt-core"] || packageJson.devDependencies?.["@happyvertical/smrt-core"];
5827
5909
  console.log("✅ Created files:");
5828
5910
  for (const file of created) console.log(` • ${file}`);
5829
5911
  if (skipped.length > 0) {
@@ -5831,9 +5913,10 @@ var initCommands = { init: {
5831
5913
  for (const file of skipped) console.log(` • ${file}`);
5832
5914
  }
5833
5915
  console.log("\n📋 Next steps:\n");
5834
- if (!hasSmrt) {
5835
- console.log("1. Install SMRT:");
5836
- console.log(" npm install @happyvertical/smrt-core\n");
5916
+ if (dependenciesAdded.length > 0) {
5917
+ console.log("1. Added SMRT dependencies to package.json:");
5918
+ for (const dependency of dependenciesAdded) console.log(` • ${dependency}`);
5919
+ console.log(" Run your package manager install command.\n");
5837
5920
  }
5838
5921
  console.log("2. Update your vite.config.ts to add smrtPlugin:");
5839
5922
  console.log(`
@@ -7559,6 +7642,99 @@ function getErrorContext(error) {
7559
7642
  }
7560
7643
  }
7561
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
+ /**
7562
7738
  * Utility commands for CLI
7563
7739
  */
7564
7740
  var utilityCommands = {
@@ -8328,6 +8504,8 @@ export default testManifest;
8328
8504
  migrations.push(...partitionedChanges.migrations);
8329
8505
  manualInterventions.push(...partitionedChanges.manualInterventions);
8330
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");
8331
8509
  assertForceMigrationTargetsExist(forceSelection.forceMigrations, [
8332
8510
  ...diff.added_tables.map((schema) => `create_table_${schema.tableName}`),
8333
8511
  ...migrations.flatMap((migration) => {
@@ -8418,6 +8596,10 @@ export default testManifest;
8418
8596
  console.log();
8419
8597
  }
8420
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
+ }
8421
8603
  for (const schema of tablePlan.schemas) {
8422
8604
  const ddl = plannedTableDDL.get(schema.tableName);
8423
8605
  if (!ddl) continue;
@@ -8431,7 +8613,7 @@ export default testManifest;
8431
8613
  const terminator = migration.sql.trimEnd().endsWith(";") ? "" : ";";
8432
8614
  console.log(` ${migration.sql}${terminator}`);
8433
8615
  }
8434
- for (const m of migrations) {
8616
+ for (const m of remainingMigrations) {
8435
8617
  const sqlStatements = m.sqlStatements ?? (m.sql ? [m.sql] : []);
8436
8618
  for (const sql of sqlStatements) console.log(` ${sql};`);
8437
8619
  }
@@ -8452,41 +8634,9 @@ export default testManifest;
8452
8634
  if (applySchemaMigrations && schemaChangeCount > 0) {
8453
8635
  const migrationDefs = [];
8454
8636
  const migrationLogs = /* @__PURE__ */ new Map();
8455
- for (const schema of tablePlan.schemas) {
8456
- const ddl = plannedTableDDL.get(schema.tableName);
8457
- if (!ddl) throw new Error(`Cannot create table ${schema.tableName}: planned DDL is unavailable.`);
8458
- const createTableSql = ddl.createTable || schema.ddl;
8459
- if (!createTableSql?.trim()) throw new Error(`Cannot create table ${schema.tableName}: schema definition has no generated DDL.`);
8460
- const migrationName = `create_table_${schema.tableName}`;
8461
- const fields = Object.keys(schema.columns).length;
8462
- migrationDefs.push({
8463
- id: migrationName,
8464
- description: `Create table ${schema.tableName}`,
8465
- version: "1.0.0",
8466
- up: [
8467
- createTableSql,
8468
- ...ddl.indexes,
8469
- ...ddl.triggers
8470
- ],
8471
- down: [`DROP TABLE IF EXISTS "${schema.tableName}"`]
8472
- });
8473
- migrationLogs.set(migrationName, { successMessage: `Created table ${schema.tableName} (${fields} columns)` });
8474
- }
8475
- for (const migration of deferredForeignKeyMigrations) {
8637
+ const pushMigrationDef = (migration) => {
8476
8638
  const migrationName = getSyntheticMigrationNameForAction(migration);
8477
- if (!migrationName) continue;
8478
- migrationDefs.push({
8479
- id: migrationName,
8480
- description: `Add deferred foreign-key constraint ${migration.className} on ${migration.tableName}`,
8481
- version: "1.0.0",
8482
- up: migration.sql ? [migration.sql] : [],
8483
- down: []
8484
- });
8485
- migrationLogs.set(migrationName, { successMessage: `Added deferred foreign-key constraint ${migration.className} on ${migration.tableName}` });
8486
- }
8487
- for (const migration of migrations) {
8488
- const migrationName = getSyntheticMigrationNameForAction(migration);
8489
- if (!migrationName) continue;
8639
+ if (!migrationName) return;
8490
8640
  let migrationSql;
8491
8641
  let actionDesc;
8492
8642
  if (migration.type === "add_column" && migration.column) {
@@ -8513,7 +8663,7 @@ export default testManifest;
8513
8663
  } else if (migration.type === "drop_foreign_key") {
8514
8664
  migrationSql = migration.sql || "";
8515
8665
  actionDesc = `Dropped foreign-key constraint on ${migration.tableName}`;
8516
- } else continue;
8666
+ } else return;
8517
8667
  const migrationSqlStatements = migration.sqlStatements ?? (migrationSql ? [migrationSql] : []);
8518
8668
  migrationDefs.push({
8519
8669
  id: migrationName,
@@ -8526,7 +8676,41 @@ export default testManifest;
8526
8676
  successMessage: actionDesc,
8527
8677
  skippedMessage: `${migrationName} already applied`
8528
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)` });
8529
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);
8530
8714
  const deferredIndexMigrations = concurrentIndexMode && engine === "postgres" ? buildConcurrentIndexPlan(migrationDefs, true).size : 0;
8531
8715
  const batchHasIndexDDL = migrations.some((migration) => migration.type === "add_index" || migration.type === "drop_index");
8532
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`);
@@ -8870,6 +9054,14 @@ export default testManifest;
8870
9054
  check("Project manifest readable", false, void 0, "Could not read .smrt/manifest.json to validate external registrations");
8871
9055
  }
8872
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();
8873
9065
  console.log("🏃 Runtime\n");
8874
9066
  const runtimeErrors = runtimeCheck.findings.filter((finding) => finding.severity === "error");
8875
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-BAKZil1v.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-BAKZil1v.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-BAKZil1v.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-BAKZil1v.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-BAKZil1v.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-BAKZil1v.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-BAKZil1v.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-BAKZil1v.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-BAKZil1v.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.9",
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-agents": "0.43.9",
36
- "@happyvertical/smrt-config": "0.43.9",
37
- "@happyvertical/smrt-core": "0.43.9",
38
- "@happyvertical/smrt-dev-mcp": "0.43.9",
39
- "@happyvertical/smrt-playground": "0.43.9",
40
- "@happyvertical/smrt-types": "0.43.9"
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",