@happyvertical/smrt-dev-mcp 0.47.1 → 0.48.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.
Files changed (43) hide show
  1. package/AGENTS.md +10 -9
  2. package/README.md +151 -66
  3. package/dist/dev-plane.d.ts +48 -0
  4. package/dist/dev-plane.d.ts.map +1 -0
  5. package/dist/dev-plane.js +211 -0
  6. package/dist/dev-plane.js.map +1 -0
  7. package/dist/http-TeoaK9Xr.js +168 -0
  8. package/dist/http-TeoaK9Xr.js.map +1 -0
  9. package/dist/http.d.ts +96 -0
  10. package/dist/http.d.ts.map +1 -0
  11. package/dist/index.d.ts +11 -1
  12. package/dist/index.d.ts.map +1 -1
  13. package/dist/index.js +86 -428
  14. package/dist/index.js.map +1 -1
  15. package/dist/knowledge/index.d.ts +27 -1
  16. package/dist/knowledge/index.d.ts.map +1 -1
  17. package/dist/{knowledge-DmZ-7GO9.js → knowledge-BOsSAdPo.js} +82 -655
  18. package/dist/knowledge-BOsSAdPo.js.map +1 -0
  19. package/dist/knowledge.js +2 -2
  20. package/dist/observation-VJgHaPps.js +802 -0
  21. package/dist/observation-VJgHaPps.js.map +1 -0
  22. package/dist/runtime.d.ts +21 -0
  23. package/dist/runtime.d.ts.map +1 -0
  24. package/dist/runtime.js +18 -0
  25. package/dist/runtime.js.map +1 -0
  26. package/dist/server-info.d.ts +3 -0
  27. package/dist/server-info.d.ts.map +1 -0
  28. package/dist/tool-catalog-Zl-bcimC.js +643 -0
  29. package/dist/tool-catalog-Zl-bcimC.js.map +1 -0
  30. package/dist/tool-catalog.d.ts.map +1 -1
  31. package/dist/tools/introspect-project.d.ts +8 -0
  32. package/dist/tools/introspect-project.d.ts.map +1 -1
  33. package/dist/tools/runtime/boot.d.ts +70 -0
  34. package/dist/tools/runtime/boot.d.ts.map +1 -0
  35. package/dist/tools/runtime/connection.d.ts +19 -0
  36. package/dist/tools/runtime/connection.d.ts.map +1 -1
  37. package/dist/tools/runtime/observation.d.ts +43 -0
  38. package/dist/tools/runtime/observation.d.ts.map +1 -0
  39. package/dist/tools/runtime/tools.d.ts +19 -0
  40. package/dist/tools/runtime/tools.d.ts.map +1 -1
  41. package/package.json +14 -6
  42. package/skills/smrt-code-review/SKILL.md +1 -1
  43. package/dist/knowledge-DmZ-7GO9.js.map +0 -1
package/dist/index.js CHANGED
@@ -1,17 +1,16 @@
1
1
  #!/usr/bin/env node
2
- import { a as checkKnowledgeFreshness, f as smrtArchitecture, h as TOOLS, i as buildReviewContext, m as REVIEW_SKILL_NAME, n as buildKnowledgeIndex, o as checkKnowledgeFreshnessFromIndex, p as smrtReview, r as buildPackageSpecialistContext, s as compactContextResult, t as buildArchitectureContext } from "./knowledge-DmZ-7GO9.js";
2
+ import { i as SERVER_VERSION, n as startRuntimeHttpHost, r as SERVER_NAME } from "./http-TeoaK9Xr.js";
3
+ import { n as TOOLS, t as REVIEW_SKILL_NAME } from "./tool-catalog-Zl-bcimC.js";
4
+ import { c as runtimeMigrationStatus, d as runtimeScheduleHealth, f as redactConnectionString, l as runtimeRecentChanges, n as runtimeRegistry, o as runtimeDispatchHealth, r as runtimeSchemaDiff, s as runtimeJobHealth, t as runtimeObject, u as runtimeRegistryDrift } from "./observation-VJgHaPps.js";
5
+ import { a as buildReviewContext, c as compactContextResult, i as buildPackageSpecialistContext, n as buildContext, o as checkKnowledgeFreshness, r as buildKnowledgeIndex, s as checkKnowledgeFreshnessFromIndex, t as buildArchitectureContext } from "./knowledge-BOsSAdPo.js";
3
6
  import { existsSync, readFileSync, realpathSync } from "node:fs";
4
- import { dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
5
7
  import { fileURLToPath, pathToFileURL } from "node:url";
6
8
  import { ProtocolError, ProtocolErrorCode, Server } from "@modelcontextprotocol/server";
7
9
  import { serveStdio } from "@modelcontextprotocol/server/stdio";
8
- import { createHash } from "node:crypto";
10
+ import { dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
9
11
  import { ManifestAdapter, OxcScanner } from "@happyvertical/smrt-scanner";
10
12
  import { access, readFile, readdir } from "node:fs/promises";
11
13
  import { ManifestGenerator } from "@happyvertical/smrt-core/scanner";
12
- import { readDispatchHealth, readJobHealth, readMigrationStatus, readRecentChanges, readRegistryDrift, readScheduleHealth } from "@happyvertical/smrt-core";
13
- import { getPackageConfig, loadConfig } from "@happyvertical/smrt-config";
14
- import { getDatabase } from "@happyvertical/sql";
15
14
  //#region src/agent-skills.ts
16
15
  var AGENT_SKILLS = [{
17
16
  name: "smrt-code-review",
@@ -271,7 +270,7 @@ var DEFAULT_MCP_OPERATIONS = [
271
270
  "delete"
272
271
  ];
273
272
  async function introspectProject(args) {
274
- const { directory = process.cwd(), includeFields = true, includeRelationships = true, includeMethods = true, detail = "summary", maxChars = DEFAULT_MAX_CHARS } = args;
273
+ const { directory = process.cwd(), includeFields = true, includeRelationships = true, includeMethods = true, detail = "summary", maxChars = DEFAULT_MAX_CHARS, cursor, limit } = args;
275
274
  const projectPath = resolve(directory);
276
275
  if (!await pathExists$1(projectPath)) return JSON.stringify({
277
276
  projectPath,
@@ -302,7 +301,11 @@ async function introspectProject(args) {
302
301
  tenantScope: tenantScopes.get(object.className)
303
302
  }));
304
303
  objects.sort((left, right) => left.className.localeCompare(right.className));
305
- const { kept, omitted } = applyObjectBudget(objects, maxChars);
304
+ const afterCursor = typeof cursor === "string" && cursor.length > 0 ? objects.filter((object) => object.className.localeCompare(cursor) > 0) : objects;
305
+ const pageLimit = typeof limit === "number" && Number.isFinite(limit) && limit >= 1 ? Math.floor(limit) : void 0;
306
+ const { kept, omitted: budgetOmitted } = applyObjectBudget(pageLimit !== void 0 ? afterCursor.slice(0, pageLimit) : afterCursor, maxChars);
307
+ const omitted = afterCursor.length - kept.length;
308
+ const nextCursor = omitted > 0 && kept.length > 0 ? kept[kept.length - 1].className : void 0;
306
309
  const output = {
307
310
  projectPath,
308
311
  manifestSource: manifestResult.source,
@@ -313,11 +316,14 @@ async function introspectProject(args) {
313
316
  objectCount: objects.length,
314
317
  scannedFileCount: manifestResult.scannedFileCount,
315
318
  parseTimeMs: manifestResult.parseTimeMs,
319
+ ...cursor ? { cursor } : {},
320
+ ...nextCursor ? { nextCursor } : {},
316
321
  ...omitted > 0 ? { truncated: {
317
322
  returnedObjectCount: kept.length,
318
323
  omittedObjectCount: omitted,
319
324
  budgetChars: maxChars,
320
- guidance: "The object list hit its character budget; metadata and diagnostics are still complete. Narrow the scan with `directory` (a single package), keep `detail: \"summary\"`, or raise `maxChars` deliberately. Object names are sorted alphabetically, so omitted objects are the alphabetical tail."
325
+ ...pageLimit !== void 0 ? { limit: pageLimit } : {},
326
+ guidance: budgetOmitted > 0 ? "The object list hit its character budget; metadata and diagnostics are still complete. Pass `cursor: nextCursor` to read the next page, narrow the scan with `directory`, keep `detail: \"summary\"`, or raise `maxChars` deliberately. Objects are sorted alphabetically." : "More objects follow this page. Pass `cursor: nextCursor` to continue."
321
327
  } } : {},
322
328
  objects: kept,
323
329
  diagnostics: manifestResult.diagnostics
@@ -1056,403 +1062,12 @@ function pathExistsSyncHint(path) {
1056
1062
  return existsSync(path);
1057
1063
  }
1058
1064
  //#endregion
1059
- //#region src/tools/runtime/connection.ts
1060
- /**
1061
- * Optional read-only dev-database connection resolution for runtime
1062
- * diagnostics tools (#1824).
1063
- *
1064
- * Resolution order per call:
1065
- * 1. explicit `dbUrl`/`dbType` tool arguments
1066
- * 2. `SMRT_DEV_DB_URL` environment variable
1067
- * 3. the project's cosmiconfig CLI section (`getPackageConfig('cli', ...)`
1068
- * from `@happyvertical/smrt-config`) → `database.{type,url}`
1069
- *
1070
- * No configured connection → `db: null` with `source: 'none'`; callers return
1071
- * a successful static-only envelope. A connection is always opened lazily per
1072
- * call and closed in the caller's `finally` — nothing is cached across calls
1073
- * and the server never holds a database handle.
1074
- *
1075
- * Sensitive handling: connection strings are never logged or echoed. Every
1076
- * surfaced URL passes through {@link redactConnectionString}; driver errors
1077
- * are surfaced only through {@link safeErrorMessage}, which strips anything
1078
- * that looks like a credential-bearing URL.
1079
- */
1080
- var RUNTIME_DATABASE_TYPES = [
1081
- "sqlite",
1082
- "postgres",
1083
- "duckdb"
1084
- ];
1085
- function isRuntimeDatabaseType(value) {
1086
- return RUNTIME_DATABASE_TYPES.includes(value);
1087
- }
1088
- /**
1089
- * Sensitive query-parameter names. Matching normalizes the key (lowercase,
1090
- * `_`/`-` stripped), so camelCase (`authToken`, `accessToken`) and hyphen
1091
- * variants (`api-key`) are masked exactly like their snake_case forms.
1092
- */
1093
- var SENSITIVE_QUERY_PARAMS = [
1094
- "access_token",
1095
- "apikey",
1096
- "api_key",
1097
- "auth",
1098
- "auth_token",
1099
- "connectionstring",
1100
- "connection_string",
1101
- "password",
1102
- "token"
1103
- ];
1104
- function normalizeQueryParamName(key) {
1105
- return key.toLowerCase().replace(/[_-]/g, "");
1106
- }
1107
- var SENSITIVE_QUERY_PARAM_NAMES = new Set(SENSITIVE_QUERY_PARAMS.map(normalizeQueryParamName));
1108
- var DEFAULT_CLI_DATABASE = { database: {
1109
- type: "sqlite",
1110
- url: ":memory:"
1111
- } };
1112
- /**
1113
- * Redact a connection string so it can be shown to an agent without leaking
1114
- * credentials. Mirrors the CLI's `redactConnectionString` (which is CLI
1115
- * private); patterned identically so dev-mcp never depends on the CLI.
1116
- *
1117
- * Query-parameter masking normalizes each key (lowercase, `_`/`-` stripped),
1118
- * so camelCase forms such as Turso/libsql's `?authToken=` mask exactly like
1119
- * their snake_case forms. A final regex pass also masks `key=value` pairs
1120
- * embedded in free text (driver error messages often quote the URL); it treats
1121
- * the start of the string, `?`, `&`, `,`, `(`, and whitespace as the
1122
- * preceding boundary.
1123
- */
1124
- function redactConnectionString(value) {
1125
- let redacted = value;
1126
- try {
1127
- const url = new URL(value);
1128
- if (url.password) url.password = "***";
1129
- for (const key of [...url.searchParams.keys()]) if (SENSITIVE_QUERY_PARAM_NAMES.has(normalizeQueryParamName(key))) url.searchParams.set(key, "***");
1130
- redacted = url.toString();
1131
- } catch {
1132
- redacted = value.replace(/([a-z][a-z0-9+.-]*:\/\/[^:\s/@]+:)(?:[^@\s]|@(?=[^@\s]*@))+(@)/gi, "$1***$2");
1133
- }
1134
- return redacted.replace(/((?:^|[?&,(\s])([a-z][a-z0-9_-]{0,30})=)([^&,\s)]+)/gi, (match, prefix, key) => SENSITIVE_QUERY_PARAM_NAMES.has(normalizeQueryParamName(key)) ? `${prefix}***` : match);
1135
- }
1136
- /**
1137
- * Build a safe, redacted error message for a database failure. Connection
1138
- * strings and raw driver error objects are never surfaced verbatim.
1139
- */
1140
- function safeErrorMessage(error) {
1141
- return redactConnectionString(error instanceof Error ? error.message : String(error ?? "unknown error"));
1142
- }
1143
- /**
1144
- * Normalize a type hint into an engine `getDatabase` accepts. Unknown values
1145
- * throw a safe error (no URL is included) so the caller can surface a
1146
- * diagnostic instead of silently opening the wrong adapter.
1147
- */
1148
- function toRuntimeDatabaseType(value) {
1149
- const normalized = value.trim().toLowerCase();
1150
- if (isRuntimeDatabaseType(normalized)) return normalized;
1151
- throw new Error(`Unsupported runtime database type "${normalized}"; expected sqlite, postgres, or duckdb`);
1152
- }
1153
- /** Infer an engine hint from a URL scheme when no explicit type is given. */
1154
- function inferDatabaseType(url, hint) {
1155
- if (hint && hint.trim().length > 0) return hint;
1156
- if (/^postgres(ql)?:/i.test(url)) return "postgres";
1157
- if (/^duckdb:/i.test(url)) return "duckdb";
1158
- return "sqlite";
1159
- }
1160
- /**
1161
- * Resolve the dev-database connection for one tool call.
1162
- *
1163
- * Returns `db: null` (never throws) when no connection is configured; callers
1164
- * must treat that as "no runtime database" and return a static-only envelope.
1165
- * A thrown connect error is propagated to the caller, which converts it into
1166
- * a diagnostic envelope — it must never reach the MCP transport.
1167
- */
1168
- async function resolveRuntimeConnection(args = {}) {
1169
- const argUrl = args.dbUrl?.trim();
1170
- if (argUrl && argUrl !== ":memory:") {
1171
- const databaseType = toRuntimeDatabaseType(inferDatabaseType(argUrl, args.dbType));
1172
- return {
1173
- db: await getDatabaseInstance({
1174
- type: databaseType,
1175
- url: argUrl
1176
- }),
1177
- source: "argument",
1178
- displayUrl: redactConnectionString(argUrl),
1179
- databaseType
1180
- };
1181
- }
1182
- const envUrl = process.env.SMRT_DEV_DB_URL?.trim();
1183
- if (envUrl && envUrl !== ":memory:") {
1184
- const databaseType = toRuntimeDatabaseType(inferDatabaseType(envUrl, args.dbType));
1185
- return {
1186
- db: await getDatabaseInstance({
1187
- type: databaseType,
1188
- url: envUrl
1189
- }),
1190
- source: "environment",
1191
- displayUrl: redactConnectionString(envUrl),
1192
- databaseType
1193
- };
1194
- }
1195
- const config = await loadCliDatabaseConfig();
1196
- const configUrl = config?.database?.url?.trim();
1197
- if (configUrl && configUrl !== ":memory:") {
1198
- const databaseType = toRuntimeDatabaseType(config.database?.type || inferDatabaseType(configUrl, args.dbType));
1199
- return {
1200
- db: await getDatabaseInstance({
1201
- type: databaseType,
1202
- url: configUrl
1203
- }),
1204
- source: "config",
1205
- displayUrl: redactConnectionString(configUrl),
1206
- databaseType
1207
- };
1208
- }
1209
- return {
1210
- db: null,
1211
- source: "none",
1212
- displayUrl: "",
1213
- databaseType: null
1214
- };
1215
- }
1216
- async function loadCliDatabaseConfig() {
1217
- try {
1218
- await loadConfig();
1219
- const database = getPackageConfig("cli", DEFAULT_CLI_DATABASE).database;
1220
- if (database && typeof database.url === "string") return { database };
1221
- return {};
1222
- } catch {
1223
- return {};
1224
- }
1225
- }
1226
- async function getDatabaseInstance(options) {
1227
- return getDatabase(options);
1228
- }
1229
- /**
1230
- * Best-effort close of a resolved connection. Never throws; diagnostics must
1231
- * not fail because cleanup hiccuped.
1232
- */
1233
- async function closeRuntimeConnection(db) {
1234
- if (!db || typeof db !== "object") return;
1235
- const closeable = db;
1236
- const close = closeable.close ?? closeable.client?.end ?? closeable.client?.close;
1237
- if (typeof close !== "function") return;
1238
- try {
1239
- await close.call(closeable.close ? closeable : closeable.client);
1240
- } catch {}
1241
- }
1242
- //#endregion
1243
- //#region src/tools/runtime/tools.ts
1244
- /**
1245
- * Runtime diagnostics tools (#1824): read-only views over a project's dev
1246
- * database `_smrt_*` system tables, powered by the shared SELECT-only
1247
- * system-diagnostics reader in `@happyvertical/smrt-core`.
1248
- *
1249
- * Contract:
1250
- * - **Optional connection.** No configured connection returns a successful
1251
- * static-only envelope — the server always starts and static tools are
1252
- * unaffected. A live connection is opened lazily per call and closed in
1253
- * `finally`; nothing is cached across calls.
1254
- * - **Read-only.** Every underlying statement is a bounded SELECT; the reader
1255
- * never selects sensitive columns (job payloads/results, schedule
1256
- * `agentConfig`/`methodArgs`, dispatch `payload`/`metadata`).
1257
- * - **Provenance-labeled.** Live results carry `provenance: 'runtime (live DB)'`;
1258
- * static-only results carry `provenance: 'static'` — agents must never
1259
- * conflate runtime facts with declared/manifest facts.
1260
- * - **Fail-safe.** A connect/read error becomes a diagnostic envelope; it must
1261
- * never reach the MCP transport and never includes raw driver text or URLs.
1262
- */
1263
- /** Provenance labels separating runtime facts from static/declared facts. */
1264
- var RUNTIME_PROVENANCE = "runtime (live DB)";
1265
- var STATIC_PROVENANCE = "static";
1266
- /**
1267
- * Serialize resolve → read → close per connection target so overlapping tool
1268
- * calls never close a shared cached handle out from under each other.
1269
- *
1270
- * `@happyvertical/sql`'s `getDatabase` returns a cached handle per URL (no
1271
- * opt-out in its public API). `closeRuntimeConnection` only calls the handle's
1272
- * own `close`/`end`, but the SDK wraps those so a close also evicts the handle
1273
- * from its connection cache. Two concurrent diagnostics calls resolving the
1274
- * same URL would therefore share one handle, with the first finisher closing
1275
- * it mid-read for the second. A per-key promise chain keeps each call's
1276
- * lifecycle private: every call resolves its own view of the connection,
1277
- * performs its read, and only then closes — the next queued call re-resolves
1278
- * a fresh handle.
1279
- *
1280
- * The key is a digest of the resolved target, never the raw URL, so a
1281
- * credential-bearing connection string is not retained in this map.
1282
- */
1283
- var runtimeReadQueues = /* @__PURE__ */ new Map();
1284
- function connectionQueueKey(args) {
1285
- const target = args.dbUrl?.trim() || process.env.SMRT_DEV_DB_URL?.trim() || "cli.config";
1286
- return createHash("sha256").update(target).digest("hex");
1287
- }
1288
- async function enqueueRuntimeRead(key, operation) {
1289
- const run = (runtimeReadQueues.get(key) ?? Promise.resolve()).then(operation, operation);
1290
- const tail = run.catch(() => void 0);
1291
- runtimeReadQueues.set(key, tail);
1292
- tail.then(() => {
1293
- if (runtimeReadQueues.get(key) === tail) runtimeReadQueues.delete(key);
1294
- });
1295
- return run;
1296
- }
1297
- /**
1298
- * Run one read against the optional runtime connection, mapping every outcome
1299
- * to a successful MCP envelope:
1300
- *
1301
- * - no connection configured → static-only envelope (`connected: false`)
1302
- * - connect failure → static envelope with a safe diagnostic
1303
- * - read failure → connected envelope with a safe diagnostic
1304
- * - success → live result under `provenance: 'runtime (live DB)'`; a
1305
- * category-unavailable reader result keeps its `available: false` data and
1306
- * surfaces its message as a diagnostic
1307
- */
1308
- async function withRuntimeConnection(args, read, staticHint) {
1309
- return enqueueRuntimeRead(connectionQueueKey(args), () => runWithRuntimeConnection(args, read, staticHint));
1310
- }
1311
- async function runWithRuntimeConnection(args, read, staticHint) {
1312
- let resolved;
1313
- try {
1314
- resolved = await resolveRuntimeConnection(args);
1315
- } catch (error) {
1316
- return {
1317
- ok: true,
1318
- coverage: null,
1319
- diagnostics: [{
1320
- severity: "warning",
1321
- code: "runtime_connection_error",
1322
- message: safeErrorMessage(error)
1323
- }],
1324
- data: {
1325
- provenance: STATIC_PROVENANCE,
1326
- connected: false
1327
- }
1328
- };
1329
- }
1330
- if (!resolved.db) return {
1331
- ok: true,
1332
- coverage: null,
1333
- diagnostics: [{
1334
- severity: "info",
1335
- code: "runtime_connection_unavailable",
1336
- message: `No runtime dev database configured (set SMRT_DEV_DB_URL or cli.database); returning static-only result: ${staticHint}. Static tools are unaffected.`
1337
- }],
1338
- data: {
1339
- provenance: STATIC_PROVENANCE,
1340
- connected: false
1341
- }
1342
- };
1343
- const { db, source, displayUrl, databaseType } = resolved;
1344
- try {
1345
- const { data, diagnostics } = await read(db);
1346
- return {
1347
- ok: true,
1348
- coverage: null,
1349
- diagnostics,
1350
- data: {
1351
- provenance: RUNTIME_PROVENANCE,
1352
- connected: true,
1353
- connectionSource: source,
1354
- databaseType,
1355
- displayUrl,
1356
- ...data
1357
- }
1358
- };
1359
- } catch (error) {
1360
- return {
1361
- ok: true,
1362
- coverage: null,
1363
- diagnostics: [{
1364
- severity: "warning",
1365
- code: "runtime_read_error",
1366
- message: safeErrorMessage(error)
1367
- }],
1368
- data: {
1369
- provenance: RUNTIME_PROVENANCE,
1370
- connected: true,
1371
- connectionSource: source,
1372
- databaseType,
1373
- displayUrl
1374
- }
1375
- };
1376
- } finally {
1377
- await closeRuntimeConnection(db);
1378
- }
1379
- }
1380
- /**
1381
- * Stored error columns (`error_message`, `last_error`) are free text written
1382
- * at failure time and routinely quote connection URLs or credentials. Every
1383
- * string in a live result passes through {@link redactConnectionString}
1384
- * before it reaches an MCP client; structure and non-string values are kept.
1385
- */
1386
- function redactStrings(value) {
1387
- if (typeof value === "string") return redactConnectionString(value);
1388
- if (Array.isArray(value)) return value.map((item) => redactStrings(item));
1389
- if (value !== null && typeof value === "object") {
1390
- const out = {};
1391
- for (const [key, item] of Object.entries(value)) out[key] = redactStrings(item);
1392
- return out;
1393
- }
1394
- return value;
1395
- }
1396
- /** Convert a reader result into envelope data + diagnostics. */
1397
- function toEnvelopeParts(rawResult) {
1398
- const result = redactStrings(rawResult);
1399
- if (result !== null && typeof result === "object" && "available" in result && result.available === false) {
1400
- const unavailable = result;
1401
- const { message, ...rest } = unavailable;
1402
- return {
1403
- data: rest,
1404
- diagnostics: [{
1405
- severity: unavailable.reason === "retired" ? "info" : "warning",
1406
- code: `category_unavailable_${String(unavailable.reason).replace(/-/g, "_")}`,
1407
- message: String(message)
1408
- }]
1409
- };
1410
- }
1411
- return {
1412
- data: result,
1413
- diagnostics: []
1414
- };
1415
- }
1416
- function readToParts(read) {
1417
- return read.then((result) => toEnvelopeParts(result));
1418
- }
1419
- async function runtimeMigrationStatus(args = {}) {
1420
- const { limit, ...connectionArgs } = args;
1421
- return withRuntimeConnection(connectionArgs, (db) => readToParts(readMigrationStatus(db, { limit })), "no migration status — the manifest still reports the declared schema");
1422
- }
1423
- async function runtimeJobHealth(args = {}) {
1424
- const { limit, ...connectionArgs } = args;
1425
- return withRuntimeConnection(connectionArgs, (db) => readToParts(readJobHealth(db, { limit })), "no job health snapshot — the manifest still reports declared job queues");
1426
- }
1427
- async function runtimeScheduleHealth(args = {}) {
1428
- const { limit, ...connectionArgs } = args;
1429
- return withRuntimeConnection(connectionArgs, (db) => readToParts(readScheduleHealth(db, { limit })), "no schedule health snapshot — the manifest still reports declared schedules");
1430
- }
1431
- async function runtimeDispatchHealth(args = {}) {
1432
- const { limit, ...connectionArgs } = args;
1433
- return withRuntimeConnection(connectionArgs, (db) => readToParts(readDispatchHealth(db, { limit })), "no dispatch health snapshot — the manifest still reports declared dispatch topology");
1434
- }
1435
- async function runtimeRecentChanges(args = {}) {
1436
- const { since, tables, tenantId, limit, ...connectionArgs } = args;
1437
- return withRuntimeConnection(connectionArgs, (db) => readToParts(readRecentChanges(db, {
1438
- since,
1439
- tables,
1440
- tenantId,
1441
- limit
1442
- })), "no recent changes — static knowledge artifacts are unchanged");
1443
- }
1444
- async function runtimeRegistryDrift(args = {}) {
1445
- return withRuntimeConnection(args, (db) => readToParts(readRegistryDrift(db)), "no registry drift report — _smrt_registry is retired; declared objects come from the manifest");
1446
- }
1447
- //#endregion
1448
1065
  //#region src/index.ts
1449
1066
  /**
1450
1067
  * SMRT Development MCP Server
1451
1068
  * Provides code generation, project introspection, knowledge context,
1452
1069
  * review/architecture prompt bundles, and portable agent skills.
1453
1070
  */
1454
- var SERVER_NAME = "smrt-dev-mcp";
1455
- var SERVER_VERSION = readPackageVersion();
1456
1071
  var DEBUG = process.env.DEBUG === "true";
1457
1072
  var REVIEW_SKILL_URI = `smrt-dev-mcp://agent-skills/${REVIEW_SKILL_NAME}`;
1458
1073
  var DOMAIN_CODE_REVIEW_PROMPT = "domain-code-review";
@@ -1771,26 +1386,27 @@ function createServer() {
1771
1386
  case "check-knowledge-freshness":
1772
1387
  result = JSON.stringify(await checkKnowledgeFreshness(args), null, 2);
1773
1388
  break;
1774
- case "check-domain-knowledge":
1775
- result = JSON.stringify(await checkKnowledgeFreshness(args), null, 2);
1776
- break;
1777
- case "build-review-context":
1778
- case "build-domain-review-context":
1779
- result = JSON.stringify(compactContextResult(await buildReviewContext(args), detailArg(args)), null, 2);
1389
+ case "build-context":
1390
+ result = JSON.stringify(compactContextResult(await buildContext(args), detailArg(args)), null, 2);
1780
1391
  break;
1781
1392
  case "smrt-review":
1782
- result = JSON.stringify(compactContextResult(await smrtReview(args), detailArg(args)), null, 2);
1393
+ case "smrt-architecture": {
1394
+ const task = name === "smrt-review" ? "review" : "architecture";
1395
+ const context = compactContextResult(await buildContext({
1396
+ ...args,
1397
+ task
1398
+ }), detailArg(args));
1399
+ context.diagnostics = [...Array.isArray(context.diagnostics) ? context.diagnostics : [], {
1400
+ severity: "info",
1401
+ code: "deprecated_tool_name",
1402
+ message: `${name} is a compatibility name; call build-context with task: '${task}'. It will be removed in the next minor release.`
1403
+ }];
1404
+ result = JSON.stringify(context, null, 2);
1783
1405
  break;
1406
+ }
1784
1407
  case "build-package-specialist-context":
1785
1408
  result = JSON.stringify(sanitizePackageSpecialist(await buildPackageSpecialistContext(args)), null, 2);
1786
1409
  break;
1787
- case "build-architecture-context":
1788
- case "build-domain-architecture-context":
1789
- result = JSON.stringify(compactContextResult(await buildArchitectureContext(args), detailArg(args)), null, 2);
1790
- break;
1791
- case "smrt-architecture":
1792
- result = JSON.stringify(compactContextResult(await smrtArchitecture(args), detailArg(args)), null, 2);
1793
- break;
1794
1410
  case "list-agent-skills":
1795
1411
  result = JSON.stringify({ skills: listAgentSkills() }, null, 2);
1796
1412
  break;
@@ -1815,6 +1431,15 @@ function createServer() {
1815
1431
  case "registry-drift":
1816
1432
  result = JSON.stringify(await runtimeRegistryDrift(args), null, 2);
1817
1433
  break;
1434
+ case "runtime-registry":
1435
+ result = JSON.stringify(await runtimeRegistry(args), null, 2);
1436
+ break;
1437
+ case "runtime-object":
1438
+ result = JSON.stringify(await runtimeObject(args), null, 2);
1439
+ break;
1440
+ case "runtime-schema-diff":
1441
+ result = JSON.stringify(await runtimeSchemaDiff(args), null, 2);
1442
+ break;
1818
1443
  default: throw new Error(`Unknown tool: ${name}`);
1819
1444
  }
1820
1445
  return {
@@ -1870,15 +1495,6 @@ function isEntrypoint() {
1870
1495
  return import.meta.url === pathToFileURL(entry).href;
1871
1496
  }
1872
1497
  }
1873
- function readPackageVersion() {
1874
- try {
1875
- const packageJsonPath = join(dirname(fileURLToPath(import.meta.url)), "..", "package.json");
1876
- const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf-8"));
1877
- return typeof packageJson.version === "string" ? packageJson.version : "0.0.0";
1878
- } catch {
1879
- return "0.0.0";
1880
- }
1881
- }
1882
1498
  function renderAgentSkillMarkdown(name) {
1883
1499
  const skill = getAgentSkill({
1884
1500
  name,
@@ -1995,11 +1611,53 @@ function sanitizePath(path) {
1995
1611
  if (path.startsWith("/") || /^[A-Za-z]:[\\/]/.test(path)) return "<absolute-path>";
1996
1612
  return path;
1997
1613
  }
1998
- if (isEntrypoint()) main().catch((error) => {
1999
- console.error(`[${SERVER_NAME}] Fatal error:`, error);
2000
- process.exit(1);
2001
- });
1614
+ /**
1615
+ * `smrt-dev-mcp --http [--port N] [--project DIR]` starts the Level 2 runtime
1616
+ * dev-plane host (#1831) instead of the stdio server. Loopback-only, bearer
1617
+ * protected, positive read-only catalog; see `http.ts`.
1618
+ */
1619
+ function parseHttpCliArgs(argv) {
1620
+ const result = { http: false };
1621
+ for (let index = 0; index < argv.length; index += 1) {
1622
+ const arg = argv[index];
1623
+ if (arg === "--http") result.http = true;
1624
+ else if (arg === "--port") {
1625
+ const value = Number.parseInt(argv[index + 1] ?? "", 10);
1626
+ if (!Number.isInteger(value) || value < 0 || value > 65535) throw new Error("--port requires an integer between 0 and 65535");
1627
+ result.port = value;
1628
+ index += 1;
1629
+ } else if (arg === "--project") {
1630
+ const value = argv[index + 1];
1631
+ if (!value) throw new Error("--project requires a directory");
1632
+ result.projectRoot = value;
1633
+ index += 1;
1634
+ }
1635
+ }
1636
+ return result;
1637
+ }
1638
+ async function mainHttp(cli) {
1639
+ const host = await startRuntimeHttpHost({
1640
+ port: cli.port,
1641
+ projectRoot: cli.projectRoot
1642
+ });
1643
+ const minted = !process.env.SMRT_DEV_MCP_TOKEN?.trim();
1644
+ console.error(`[${SERVER_NAME}] runtime dev-plane listening at ${host.url} (${host.boot.objectCount} booted objects, ${host.boot.manifests.length} manifests)`);
1645
+ if (minted) console.error(`[${SERVER_NAME}] bearer token: ${host.token}`);
1646
+ const shutdown = async () => {
1647
+ await host.close();
1648
+ process.exit(0);
1649
+ };
1650
+ process.on("SIGINT", shutdown);
1651
+ process.on("SIGTERM", shutdown);
1652
+ }
1653
+ if (isEntrypoint()) {
1654
+ const cli = parseHttpCliArgs(process.argv.slice(2));
1655
+ (cli.http ? mainHttp(cli) : main()).catch((error) => {
1656
+ console.error(`[${SERVER_NAME}] Fatal error:`, error);
1657
+ process.exit(1);
1658
+ });
1659
+ }
2002
1660
  //#endregion
2003
- export { SERVER_VERSION, TOOLS, createServer };
1661
+ export { SERVER_VERSION, TOOLS, createServer, parseHttpCliArgs };
2004
1662
 
2005
1663
  //# sourceMappingURL=index.js.map