@happyvertical/smrt-dev-mcp 0.40.36 → 0.40.38
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 +89 -2
- package/README.md +70 -3
- package/dist/index.d.ts +61 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +190 -24
- package/dist/index.js.map +1 -1
- package/dist/knowledge/index.d.ts +78 -1
- package/dist/knowledge/index.d.ts.map +1 -1
- package/dist/{knowledge-BPbgCtVJ.js → knowledge-DUUhTM5u.js} +796 -61
- package/dist/knowledge-DUUhTM5u.js.map +1 -0
- package/dist/knowledge.js +2 -2
- package/dist/tools/introspect-project.d.ts +17 -0
- package/dist/tools/introspect-project.d.ts.map +1 -1
- package/package.json +4 -4
- package/dist/knowledge-BPbgCtVJ.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { a as checkKnowledgeFreshnessFromIndex,
|
|
2
|
+
import { a as checkKnowledgeFreshnessFromIndex, d as smrtReview, i as checkKnowledgeFreshness, n as buildKnowledgeIndex, r as buildReviewContext, s as packageDocPaths, t as buildArchitectureContext, u as smrtArchitecture } from "./knowledge-DUUhTM5u.js";
|
|
3
3
|
import { existsSync, readFileSync, realpathSync } from "node:fs";
|
|
4
4
|
import { dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
5
5
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
6
6
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
7
7
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
8
8
|
import { CallToolRequestSchema, ErrorCode, GetPromptRequestSchema, ListPromptsRequestSchema, ListResourcesRequestSchema, ListToolsRequestSchema, McpError, ReadResourceRequestSchema } from "@modelcontextprotocol/sdk/types.js";
|
|
9
|
+
import { ManifestAdapter, OxcScanner } from "@happyvertical/smrt-scanner";
|
|
9
10
|
import { access, readFile, readdir } from "node:fs/promises";
|
|
10
11
|
import { ManifestGenerator } from "@happyvertical/smrt-core/scanner";
|
|
11
|
-
import { ManifestAdapter, OxcScanner } from "@happyvertical/smrt-scanner";
|
|
12
12
|
//#region src/agent-skills.ts
|
|
13
13
|
var AGENT_SKILLS = [{
|
|
14
14
|
name: "smrt-code-review",
|
|
@@ -255,8 +255,20 @@ var RELATIONSHIP_TYPES = /* @__PURE__ */ new Set([
|
|
|
255
255
|
"oneToMany",
|
|
256
256
|
"manyToMany"
|
|
257
257
|
]);
|
|
258
|
+
/**
|
|
259
|
+
* Response budget in characters. Generous enough that a 42-object summary never
|
|
260
|
+
* truncates, while still capping a runaway payload instead of emitting one.
|
|
261
|
+
*/
|
|
262
|
+
var DEFAULT_MAX_CHARS = 5e4;
|
|
263
|
+
var DEFAULT_MCP_OPERATIONS = [
|
|
264
|
+
"list",
|
|
265
|
+
"get",
|
|
266
|
+
"create",
|
|
267
|
+
"update",
|
|
268
|
+
"delete"
|
|
269
|
+
];
|
|
258
270
|
async function introspectProject(args) {
|
|
259
|
-
const { directory = process.cwd(), includeFields = true, includeRelationships = true, includeMethods = true } = args;
|
|
271
|
+
const { directory = process.cwd(), includeFields = true, includeRelationships = true, includeMethods = true, detail = "summary", maxChars = DEFAULT_MAX_CHARS } = args;
|
|
260
272
|
const projectPath = resolve(directory);
|
|
261
273
|
if (!await pathExists$1(projectPath)) return JSON.stringify({
|
|
262
274
|
projectPath,
|
|
@@ -271,7 +283,13 @@ async function introspectProject(args) {
|
|
|
271
283
|
const packageMetadata = await readPackageMetadata(projectPath);
|
|
272
284
|
const tenantScopes = await scanTenantScopes(projectPath);
|
|
273
285
|
const manifestResult = await loadManifestArtifact(projectPath, args.manifestPath) ?? await scanSourceManifest(projectPath, packageMetadata);
|
|
274
|
-
const
|
|
286
|
+
const entries = Object.entries(manifestResult.manifest.objects ?? {});
|
|
287
|
+
const objects = detail === "summary" ? entries.map(([manifestKey, object]) => summarizeObject({
|
|
288
|
+
manifestKey,
|
|
289
|
+
object,
|
|
290
|
+
projectPath,
|
|
291
|
+
tenantScope: tenantScopes.get(object.className)
|
|
292
|
+
})) : entries.map(([manifestKey, object]) => formatObject({
|
|
275
293
|
manifestKey,
|
|
276
294
|
object,
|
|
277
295
|
projectPath,
|
|
@@ -279,21 +297,77 @@ async function introspectProject(args) {
|
|
|
279
297
|
includeRelationships,
|
|
280
298
|
includeMethods,
|
|
281
299
|
tenantScope: tenantScopes.get(object.className)
|
|
282
|
-
}))
|
|
300
|
+
}));
|
|
301
|
+
objects.sort((left, right) => left.className.localeCompare(right.className));
|
|
302
|
+
const { kept, omitted } = applyObjectBudget(objects, maxChars);
|
|
283
303
|
const output = {
|
|
284
304
|
projectPath,
|
|
285
305
|
manifestSource: manifestResult.source,
|
|
286
306
|
manifestPath: "path" in manifestResult ? relative(projectPath, manifestResult.path) : void 0,
|
|
287
307
|
packageName: manifestResult.manifest.packageName ?? packageMetadata.name ?? void 0,
|
|
288
308
|
packageVersion: manifestResult.manifest.packageVersion ?? packageMetadata.version ?? void 0,
|
|
309
|
+
detail,
|
|
289
310
|
objectCount: objects.length,
|
|
290
311
|
scannedFileCount: manifestResult.scannedFileCount,
|
|
291
312
|
parseTimeMs: manifestResult.parseTimeMs,
|
|
292
|
-
|
|
313
|
+
...omitted > 0 ? { truncated: {
|
|
314
|
+
returnedObjectCount: kept.length,
|
|
315
|
+
omittedObjectCount: omitted,
|
|
316
|
+
budgetChars: maxChars,
|
|
317
|
+
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."
|
|
318
|
+
} } : {},
|
|
319
|
+
objects: kept,
|
|
293
320
|
diagnostics: manifestResult.diagnostics
|
|
294
321
|
};
|
|
295
322
|
return JSON.stringify(output, null, 2);
|
|
296
323
|
}
|
|
324
|
+
/**
|
|
325
|
+
* Trims the object list to the character budget, always keeping at least one so
|
|
326
|
+
* a caller sees the shape rather than an empty list.
|
|
327
|
+
*/
|
|
328
|
+
function applyObjectBudget(objects, maxChars) {
|
|
329
|
+
const kept = [];
|
|
330
|
+
let used = 0;
|
|
331
|
+
for (const object of objects) {
|
|
332
|
+
const size = JSON.stringify(object, null, 2).length + 2;
|
|
333
|
+
if (used + size > maxChars && kept.length > 0) break;
|
|
334
|
+
used += size;
|
|
335
|
+
kept.push(object);
|
|
336
|
+
}
|
|
337
|
+
return {
|
|
338
|
+
kept,
|
|
339
|
+
omitted: objects.length - kept.length
|
|
340
|
+
};
|
|
341
|
+
}
|
|
342
|
+
function summarizeObject({ manifestKey, object, projectPath, tenantScope }) {
|
|
343
|
+
const fields = Object.entries(object.fields ?? {});
|
|
344
|
+
const decoratorConfig = object.decoratorConfig ?? {};
|
|
345
|
+
return compactObject({
|
|
346
|
+
manifestKey,
|
|
347
|
+
className: object.className,
|
|
348
|
+
qualifiedName: object.qualifiedName,
|
|
349
|
+
filePath: sanitizePath$1(projectPath, object.filePath),
|
|
350
|
+
extends: object.extends,
|
|
351
|
+
collection: object.collection,
|
|
352
|
+
tableName: object.schema?.tableName ?? stringFromConfig(decoratorConfig.tableName) ?? object.collection,
|
|
353
|
+
tenantScope: tenantScope ?? normalizeTenantScopedConfig(decoratorConfig.tenantScoped),
|
|
354
|
+
fieldCount: fields.length,
|
|
355
|
+
relationships: fields.filter(([, field]) => RELATIONSHIP_TYPES.has(field.type)).map(([name, field]) => `${name} -> ${field.related ?? ""} (${field.type})`).join(", ") || void 0,
|
|
356
|
+
mcpOperations: mcpOperationsFromConfig(decoratorConfig.mcp)
|
|
357
|
+
});
|
|
358
|
+
}
|
|
359
|
+
/**
|
|
360
|
+
* An omitted `mcp` config means full CRUD, not a closed surface — the same rule
|
|
361
|
+
* the knowledge index applies.
|
|
362
|
+
*/
|
|
363
|
+
function mcpOperationsFromConfig(config) {
|
|
364
|
+
if (config === false) return [];
|
|
365
|
+
if (typeof config !== "object" || config === null || Array.isArray(config)) return [...DEFAULT_MCP_OPERATIONS];
|
|
366
|
+
const record = config;
|
|
367
|
+
const include = Array.isArray(record.include) ? record.include.filter((item) => typeof item === "string") : DEFAULT_MCP_OPERATIONS;
|
|
368
|
+
const exclude = new Set(Array.isArray(record.exclude) ? record.exclude.filter((item) => typeof item === "string") : []);
|
|
369
|
+
return include.filter((operation) => !exclude.has(operation));
|
|
370
|
+
}
|
|
297
371
|
async function loadManifestArtifact(projectPath, manifestPath) {
|
|
298
372
|
const candidates = manifestPath ? [resolve(projectPath, manifestPath)] : DEFAULT_MANIFEST_PATHS.map((candidate) => join(projectPath, candidate));
|
|
299
373
|
const diagnostics = [];
|
|
@@ -1127,7 +1201,7 @@ var TOOLS = [
|
|
|
1127
1201
|
},
|
|
1128
1202
|
{
|
|
1129
1203
|
name: "introspect-project",
|
|
1130
|
-
description: "Scan
|
|
1204
|
+
description: "Scan a directory for SMRT objects. Returns a compact summary by default; pass detail: \"full\" for field, schema, and method details.",
|
|
1131
1205
|
inputSchema: {
|
|
1132
1206
|
type: "object",
|
|
1133
1207
|
properties: {
|
|
@@ -1139,17 +1213,27 @@ var TOOLS = [
|
|
|
1139
1213
|
type: "string",
|
|
1140
1214
|
description: "Optional manifest path. Defaults to .smrt/manifest.json, dist/manifest.json, then source scanning."
|
|
1141
1215
|
},
|
|
1216
|
+
detail: {
|
|
1217
|
+
type: "string",
|
|
1218
|
+
enum: ["summary", "full"],
|
|
1219
|
+
default: "summary",
|
|
1220
|
+
description: "summary: one compact record per object. full: complete field/schema/method detail (large)."
|
|
1221
|
+
},
|
|
1222
|
+
maxChars: {
|
|
1223
|
+
type: "number",
|
|
1224
|
+
description: "Character budget for the `objects` payload. Objects past the budget are omitted and reported under `truncated`. Project metadata and diagnostics are always returned in full, so the serialized response is somewhat larger than this budget."
|
|
1225
|
+
},
|
|
1142
1226
|
includeFields: {
|
|
1143
1227
|
type: "boolean",
|
|
1144
|
-
description: "Include field details"
|
|
1228
|
+
description: "Include field details (detail: \"full\" only)"
|
|
1145
1229
|
},
|
|
1146
1230
|
includeRelationships: {
|
|
1147
1231
|
type: "boolean",
|
|
1148
|
-
description: "Analyze relationships"
|
|
1232
|
+
description: "Analyze relationships (detail: \"full\" only)"
|
|
1149
1233
|
},
|
|
1150
1234
|
includeMethods: {
|
|
1151
1235
|
type: "boolean",
|
|
1152
|
-
description: "Include public method details"
|
|
1236
|
+
description: "Include public method details (detail: \"full\" only)"
|
|
1153
1237
|
}
|
|
1154
1238
|
}
|
|
1155
1239
|
}
|
|
@@ -1265,7 +1349,13 @@ var TOOLS = [
|
|
|
1265
1349
|
items: { type: "string" }
|
|
1266
1350
|
},
|
|
1267
1351
|
focus: { type: "string" },
|
|
1268
|
-
documentation: { type: "string" }
|
|
1352
|
+
documentation: { type: "string" },
|
|
1353
|
+
detail: {
|
|
1354
|
+
type: "string",
|
|
1355
|
+
enum: ["summary", "full"],
|
|
1356
|
+
default: "summary",
|
|
1357
|
+
description: "summary: authored package docs are listed by path to stay inside tool-result budgets. full: embed them."
|
|
1358
|
+
}
|
|
1269
1359
|
}
|
|
1270
1360
|
}
|
|
1271
1361
|
},
|
|
@@ -1292,7 +1382,13 @@ var TOOLS = [
|
|
|
1292
1382
|
],
|
|
1293
1383
|
default: "project"
|
|
1294
1384
|
},
|
|
1295
|
-
package: { type: "string" }
|
|
1385
|
+
package: { type: "string" },
|
|
1386
|
+
detail: {
|
|
1387
|
+
type: "string",
|
|
1388
|
+
enum: ["summary", "full"],
|
|
1389
|
+
default: "summary",
|
|
1390
|
+
description: "summary: authored package docs are listed by path to stay inside tool-result budgets. full: embed them."
|
|
1391
|
+
}
|
|
1296
1392
|
}
|
|
1297
1393
|
}
|
|
1298
1394
|
},
|
|
@@ -1317,6 +1413,12 @@ var TOOLS = [
|
|
|
1317
1413
|
"both"
|
|
1318
1414
|
],
|
|
1319
1415
|
default: "both"
|
|
1416
|
+
},
|
|
1417
|
+
detail: {
|
|
1418
|
+
type: "string",
|
|
1419
|
+
enum: ["summary", "full"],
|
|
1420
|
+
default: "summary",
|
|
1421
|
+
description: "summary: authored package docs are listed by path to stay inside tool-result budgets. full: embed them."
|
|
1320
1422
|
}
|
|
1321
1423
|
}
|
|
1322
1424
|
}
|
|
@@ -1330,7 +1432,13 @@ var TOOLS = [
|
|
|
1330
1432
|
rootDir: { type: "string" },
|
|
1331
1433
|
idea: { type: "string" },
|
|
1332
1434
|
documentation: { type: "string" },
|
|
1333
|
-
focus: { type: "string" }
|
|
1435
|
+
focus: { type: "string" },
|
|
1436
|
+
detail: {
|
|
1437
|
+
type: "string",
|
|
1438
|
+
enum: ["summary", "full"],
|
|
1439
|
+
default: "summary",
|
|
1440
|
+
description: "summary: authored package docs are listed by path to stay inside tool-result budgets. full: embed them."
|
|
1441
|
+
}
|
|
1334
1442
|
}
|
|
1335
1443
|
}
|
|
1336
1444
|
},
|
|
@@ -1354,7 +1462,13 @@ var TOOLS = [
|
|
|
1354
1462
|
],
|
|
1355
1463
|
default: "project"
|
|
1356
1464
|
},
|
|
1357
|
-
package: { type: "string" }
|
|
1465
|
+
package: { type: "string" },
|
|
1466
|
+
detail: {
|
|
1467
|
+
type: "string",
|
|
1468
|
+
enum: ["summary", "full"],
|
|
1469
|
+
default: "summary",
|
|
1470
|
+
description: "summary: authored package docs are listed by path to stay inside tool-result budgets. full: embed them."
|
|
1471
|
+
}
|
|
1358
1472
|
}
|
|
1359
1473
|
}
|
|
1360
1474
|
},
|
|
@@ -1367,7 +1481,13 @@ var TOOLS = [
|
|
|
1367
1481
|
rootDir: { type: "string" },
|
|
1368
1482
|
idea: { type: "string" },
|
|
1369
1483
|
documentation: { type: "string" },
|
|
1370
|
-
focus: { type: "string" }
|
|
1484
|
+
focus: { type: "string" },
|
|
1485
|
+
detail: {
|
|
1486
|
+
type: "string",
|
|
1487
|
+
enum: ["summary", "full"],
|
|
1488
|
+
default: "summary",
|
|
1489
|
+
description: "summary: authored package docs are listed by path to stay inside tool-result budgets. full: embed them."
|
|
1490
|
+
}
|
|
1371
1491
|
}
|
|
1372
1492
|
}
|
|
1373
1493
|
},
|
|
@@ -1611,6 +1731,8 @@ async function main() {
|
|
|
1611
1731
|
smrtPackageCount: index.smrtPackages.length,
|
|
1612
1732
|
sdkPackageCount: index.sdkPackages.length,
|
|
1613
1733
|
relationshipsV2: index.relationshipsV2,
|
|
1734
|
+
coverage: index.coverage,
|
|
1735
|
+
diagnostics: index.diagnostics,
|
|
1614
1736
|
freshness
|
|
1615
1737
|
}, null, 2);
|
|
1616
1738
|
break;
|
|
@@ -1626,6 +1748,8 @@ async function main() {
|
|
|
1626
1748
|
domainKnowledgePackageCount: index.packages.filter((pkg) => pkg.hasDomainKnowledge).length,
|
|
1627
1749
|
missingDomainKnowledgePackages: index.packages.filter((pkg) => pkg.exportKeys.includes("./smrt-knowledge.json") && !pkg.hasDomainKnowledge).map((pkg) => pkg.name),
|
|
1628
1750
|
relationshipsV2: index.relationshipsV2,
|
|
1751
|
+
coverage: index.coverage,
|
|
1752
|
+
diagnostics: index.diagnostics,
|
|
1629
1753
|
freshness
|
|
1630
1754
|
}, null, 2);
|
|
1631
1755
|
break;
|
|
@@ -1637,22 +1761,18 @@ async function main() {
|
|
|
1637
1761
|
result = JSON.stringify(await checkKnowledgeFreshness(args), null, 2);
|
|
1638
1762
|
break;
|
|
1639
1763
|
case "build-review-context":
|
|
1640
|
-
result = JSON.stringify(await buildReviewContext(args), null, 2);
|
|
1641
|
-
break;
|
|
1642
1764
|
case "build-domain-review-context":
|
|
1643
|
-
result = JSON.stringify(await buildReviewContext(args), null, 2);
|
|
1765
|
+
result = JSON.stringify(compactContextResult(await buildReviewContext(args), detailArg(args)), null, 2);
|
|
1644
1766
|
break;
|
|
1645
1767
|
case "smrt-review":
|
|
1646
|
-
result = JSON.stringify(await smrtReview(args), null, 2);
|
|
1768
|
+
result = JSON.stringify(compactContextResult(await smrtReview(args), detailArg(args)), null, 2);
|
|
1647
1769
|
break;
|
|
1648
1770
|
case "build-architecture-context":
|
|
1649
|
-
result = JSON.stringify(await buildArchitectureContext(args), null, 2);
|
|
1650
|
-
break;
|
|
1651
1771
|
case "build-domain-architecture-context":
|
|
1652
|
-
result = JSON.stringify(await buildArchitectureContext(args), null, 2);
|
|
1772
|
+
result = JSON.stringify(compactContextResult(await buildArchitectureContext(args), detailArg(args)), null, 2);
|
|
1653
1773
|
break;
|
|
1654
1774
|
case "smrt-architecture":
|
|
1655
|
-
result = JSON.stringify(await smrtArchitecture(args), null, 2);
|
|
1775
|
+
result = JSON.stringify(compactContextResult(await smrtArchitecture(args), detailArg(args)), null, 2);
|
|
1656
1776
|
break;
|
|
1657
1777
|
case "list-agent-skills":
|
|
1658
1778
|
result = JSON.stringify({ skills: listAgentSkills() }, null, 2);
|
|
@@ -1759,10 +1879,56 @@ function sanitizeKnowledgeIndex(index) {
|
|
|
1759
1879
|
sdkPackages: packages.filter((pkg) => pkg.kind === "sdk")
|
|
1760
1880
|
};
|
|
1761
1881
|
}
|
|
1882
|
+
/**
|
|
1883
|
+
* Compact projection of a context result for MCP callers (#2143).
|
|
1884
|
+
*
|
|
1885
|
+
* `selectedPackages` carries each package's whole authored `agentDoc`, its
|
|
1886
|
+
* module-doc contents, and its full domain manifest. A three-package downstream
|
|
1887
|
+
* product serialized to 329,003 characters that way, which is what made these
|
|
1888
|
+
* tools unusable as planning aids. `detail: "full"` returns the whole shape.
|
|
1889
|
+
*/
|
|
1890
|
+
function compactContextResult(result, detail) {
|
|
1891
|
+
const source = result;
|
|
1892
|
+
if (detail === "full") return source;
|
|
1893
|
+
const compact = {
|
|
1894
|
+
...source,
|
|
1895
|
+
detail: "summary"
|
|
1896
|
+
};
|
|
1897
|
+
for (const key of ["selectedPackages", "selectedSdkPackages"]) {
|
|
1898
|
+
const packages = source[key];
|
|
1899
|
+
if (!Array.isArray(packages)) continue;
|
|
1900
|
+
compact[key] = packages.map(compactKnowledgePackage);
|
|
1901
|
+
}
|
|
1902
|
+
return compact;
|
|
1903
|
+
}
|
|
1904
|
+
function compactKnowledgePackage(pkg) {
|
|
1905
|
+
return {
|
|
1906
|
+
name: pkg.name,
|
|
1907
|
+
version: pkg.version,
|
|
1908
|
+
kind: pkg.kind,
|
|
1909
|
+
directory: pkg.relativeDirectory,
|
|
1910
|
+
objectSource: pkg.objectSource,
|
|
1911
|
+
...pkg.objectSourceReason ? { objectSourceReason: pkg.objectSourceReason } : {},
|
|
1912
|
+
docs: packageDocPaths(pkg),
|
|
1913
|
+
domainKnowledgePath: pkg.domainKnowledgePath,
|
|
1914
|
+
relationshipFeatures: pkg.relationshipFeatures,
|
|
1915
|
+
smrtDependencies: pkg.smrtDependencies,
|
|
1916
|
+
sdkDependencies: pkg.sdkDependencies,
|
|
1917
|
+
exportKeys: pkg.exportKeys,
|
|
1918
|
+
objectCount: pkg.objects.length,
|
|
1919
|
+
objects: pkg.objects.map((object) => object.tableName ? `${object.qualifiedName ?? object.className} (${object.tableName})` : object.qualifiedName ?? object.className),
|
|
1920
|
+
mcpToolCount: pkg.mcpTools.length
|
|
1921
|
+
};
|
|
1922
|
+
}
|
|
1923
|
+
function detailArg(args) {
|
|
1924
|
+
const detail = args?.detail;
|
|
1925
|
+
return typeof detail === "string" ? detail : void 0;
|
|
1926
|
+
}
|
|
1762
1927
|
function sanitizeKnowledgePackage(pkg) {
|
|
1763
|
-
const { directory: _directory, objects, ...rest } = pkg;
|
|
1928
|
+
const { directory: _directory, objects, checkedObjectPaths, ...rest } = pkg;
|
|
1764
1929
|
return {
|
|
1765
1930
|
...rest,
|
|
1931
|
+
checkedObjectPaths: checkedObjectPaths.map((path) => sanitizePath(path) ?? path),
|
|
1766
1932
|
objects: objects.map((object) => ({
|
|
1767
1933
|
...object,
|
|
1768
1934
|
filePath: sanitizePath(object.filePath)
|