@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 +52 -0
- package/README.md +10 -1
- package/dist/{commands-BAKZil1v.js → commands-DBnnqrFy.js} +233 -41
- package/dist/index.js +9 -9
- package/package.json +7 -7
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
|
|
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 (
|
|
5835
|
-
console.log("1.
|
|
5836
|
-
console.log(
|
|
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
|
|
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
|
-
|
|
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)
|
|
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
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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.
|
|
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.
|
|
36
|
-
"@happyvertical/smrt-
|
|
37
|
-
"@happyvertical/smrt-
|
|
38
|
-
"@happyvertical/smrt-
|
|
39
|
-
"@happyvertical/smrt-
|
|
40
|
-
"@happyvertical/smrt-
|
|
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",
|