@happyvertical/smrt-dev-mcp 0.51.32 → 0.51.34
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 +40 -3
- package/README.md +35 -3
- package/dist/dev-plane.d.ts.map +1 -1
- package/dist/dev-plane.js +6 -4
- package/dist/dev-plane.js.map +1 -1
- package/dist/{http-TeoaK9Xr.js → http-C302xXnO.js} +5 -4
- package/dist/{http-TeoaK9Xr.js.map → http-C302xXnO.js.map} +1 -1
- package/dist/http.d.ts +18 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +31 -454
- package/dist/index.js.map +1 -1
- package/dist/introspect-project-Peu2LC2T.js +490 -0
- package/dist/introspect-project-Peu2LC2T.js.map +1 -0
- package/dist/knowledge/index.d.ts +3 -1
- package/dist/knowledge/index.d.ts.map +1 -1
- package/dist/knowledge/mcp-docs.d.ts +1 -1
- package/dist/{knowledge-DYe5lCfq.js → knowledge-CvNlDTLz.js} +9 -6
- package/dist/knowledge-CvNlDTLz.js.map +1 -0
- package/dist/knowledge.js +2 -2
- package/dist/observation-CqE0tBgS.js +386 -0
- package/dist/observation-CqE0tBgS.js.map +1 -0
- package/dist/runtime-test-fixture.d.ts +3 -0
- package/dist/runtime-test-fixture.d.ts.map +1 -0
- package/dist/runtime.d.ts +1 -1
- package/dist/runtime.js +2 -1
- package/dist/runtime.js.map +1 -1
- package/dist/{tool-catalog-Zl-bcimC.js → tool-catalog-RHq7nyCm.js} +26 -2
- package/dist/tool-catalog-RHq7nyCm.js.map +1 -0
- package/dist/tool-catalog.d.ts +1 -1
- package/dist/tool-catalog.d.ts.map +1 -1
- package/dist/tools/introspect-project.d.ts.map +1 -1
- package/dist/tools/runtime/boot.d.ts.map +1 -1
- package/dist/tools/runtime/connection.d.ts +23 -3
- package/dist/tools/runtime/connection.d.ts.map +1 -1
- package/dist/tools/runtime/observation.d.ts +17 -12
- package/dist/tools/runtime/observation.d.ts.map +1 -1
- package/dist/tools/runtime/project-runtime.d.ts +16 -0
- package/dist/tools/runtime/project-runtime.d.ts.map +1 -0
- package/dist/tools/runtime/tools.d.ts +23 -3
- package/dist/tools/runtime/tools.d.ts.map +1 -1
- package/dist/tools-ma2JFPVY.js +568 -0
- package/dist/tools-ma2JFPVY.js.map +1 -0
- package/package.json +11 -9
- package/dist/knowledge-DYe5lCfq.js.map +0 -1
- package/dist/observation-VJgHaPps.js +0 -802
- package/dist/observation-VJgHaPps.js.map +0 -1
- package/dist/tool-catalog-Zl-bcimC.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,16 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
import { n as
|
|
4
|
-
import {
|
|
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-
|
|
2
|
+
import { f as redactConnectionString, m as ProjectRuntimeResolutionError } from "./tools-ma2JFPVY.js";
|
|
3
|
+
import { i as SERVER_VERSION, n as startRuntimeHttpHost, r as SERVER_NAME } from "./http-C302xXnO.js";
|
|
4
|
+
import { n as TOOLS, t as REVIEW_SKILL_NAME } from "./tool-catalog-RHq7nyCm.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-CvNlDTLz.js";
|
|
6
6
|
import { existsSync, readFileSync, realpathSync } from "node:fs";
|
|
7
|
+
import { dirname, join, relative, resolve, sep } from "node:path";
|
|
7
8
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
8
9
|
import { ProtocolError, ProtocolErrorCode, Server } from "@modelcontextprotocol/server";
|
|
9
10
|
import { serveStdio } from "@modelcontextprotocol/server/stdio";
|
|
10
|
-
import { dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
11
|
-
import { ManifestAdapter, OxcScanner } from "@happyvertical/smrt-scanner";
|
|
12
11
|
import { access, readFile, readdir } from "node:fs/promises";
|
|
13
|
-
import { ManifestGenerator } from "@happyvertical/smrt-core/scanner";
|
|
14
12
|
//#region src/agent-skills.ts
|
|
15
13
|
var AGENT_SKILLS = [{
|
|
16
14
|
name: "smrt-code-review",
|
|
@@ -187,7 +185,7 @@ function renderProperty(prop) {
|
|
|
187
185
|
const nullable = prop.nullable === true;
|
|
188
186
|
const tsType = nullable ? `${mapping.tsType} | null` : mapping.tsType;
|
|
189
187
|
const defaultValue = prop.defaultValue !== void 0 ? renderLiteral(prop.defaultValue) : nullable ? "null" : mapping.defaultValue;
|
|
190
|
-
const fieldOptions = compactObject
|
|
188
|
+
const fieldOptions = compactObject({
|
|
191
189
|
required: prop.required,
|
|
192
190
|
nullable: prop.nullable,
|
|
193
191
|
description: prop.description
|
|
@@ -195,7 +193,7 @@ function renderProperty(prop) {
|
|
|
195
193
|
return `${prop.description ? ` /** ${prop.description} */\n` : ""}${Object.keys(fieldOptions).length > 0 ? ` @field(${JSON.stringify(fieldOptions)})\n` : ""} ${prop.name}: ${tsType} = ${defaultValue};`;
|
|
196
194
|
}
|
|
197
195
|
function renderRelationship(relationship) {
|
|
198
|
-
const options = compactObject
|
|
196
|
+
const options = compactObject({
|
|
199
197
|
required: relationship.required,
|
|
200
198
|
nullable: relationship.nullable,
|
|
201
199
|
description: relationship.description,
|
|
@@ -222,7 +220,7 @@ function renderCompanionSnippets(className, usesTenantScoped) {
|
|
|
222
220
|
*/`;
|
|
223
221
|
}
|
|
224
222
|
function renderObjectLiteral(value) {
|
|
225
|
-
return JSON.stringify(compactObject
|
|
223
|
+
return JSON.stringify(compactObject(value), null, 2);
|
|
226
224
|
}
|
|
227
225
|
function renderLiteral(value) {
|
|
228
226
|
if (typeof value === "string") return JSON.stringify(value);
|
|
@@ -230,434 +228,9 @@ function renderLiteral(value) {
|
|
|
230
228
|
if (value === null) return "null";
|
|
231
229
|
return JSON.stringify(value, null, 2);
|
|
232
230
|
}
|
|
233
|
-
function compactObject$1(value) {
|
|
234
|
-
return Object.fromEntries(Object.entries(value).filter(([, entry]) => entry !== void 0));
|
|
235
|
-
}
|
|
236
|
-
//#endregion
|
|
237
|
-
//#region src/tools/introspect-project.ts
|
|
238
|
-
var DEFAULT_MANIFEST_PATHS = [
|
|
239
|
-
".smrt/manifest.json",
|
|
240
|
-
"dist/manifest.json",
|
|
241
|
-
"src/manifest/manifest.json"
|
|
242
|
-
];
|
|
243
|
-
var SCAN_EXCLUDE = [
|
|
244
|
-
"**/node_modules/**",
|
|
245
|
-
"**/dist/**",
|
|
246
|
-
"**/build/**",
|
|
247
|
-
"**/.git/**",
|
|
248
|
-
"**/.smrt/**",
|
|
249
|
-
"**/*.d.ts",
|
|
250
|
-
"**/*.test.ts",
|
|
251
|
-
"**/*.spec.ts",
|
|
252
|
-
"**/__tests__/**"
|
|
253
|
-
];
|
|
254
|
-
var RELATIONSHIP_TYPES = /* @__PURE__ */ new Set([
|
|
255
|
-
"foreignKey",
|
|
256
|
-
"crossPackageRef",
|
|
257
|
-
"oneToMany",
|
|
258
|
-
"manyToMany"
|
|
259
|
-
]);
|
|
260
|
-
/**
|
|
261
|
-
* Response budget in characters. Generous enough that a 42-object summary never
|
|
262
|
-
* truncates, while still capping a runaway payload instead of emitting one.
|
|
263
|
-
*/
|
|
264
|
-
var DEFAULT_MAX_CHARS = 5e4;
|
|
265
|
-
var DEFAULT_MCP_OPERATIONS = [
|
|
266
|
-
"list",
|
|
267
|
-
"get",
|
|
268
|
-
"create",
|
|
269
|
-
"update",
|
|
270
|
-
"delete"
|
|
271
|
-
];
|
|
272
|
-
async function introspectProject(args) {
|
|
273
|
-
const { directory = process.cwd(), includeFields = true, includeRelationships = true, includeMethods = true, detail = "summary", maxChars = DEFAULT_MAX_CHARS, cursor, limit } = args;
|
|
274
|
-
const projectPath = resolve(directory);
|
|
275
|
-
if (!await pathExists$1(projectPath)) return JSON.stringify({
|
|
276
|
-
projectPath,
|
|
277
|
-
manifestSource: "none",
|
|
278
|
-
objectCount: 0,
|
|
279
|
-
objects: [],
|
|
280
|
-
diagnostics: [{
|
|
281
|
-
severity: "warning",
|
|
282
|
-
message: `Project directory does not exist: ${projectPath}`
|
|
283
|
-
}]
|
|
284
|
-
}, null, 2);
|
|
285
|
-
const packageMetadata = await readPackageMetadata(projectPath);
|
|
286
|
-
const tenantScopes = await scanTenantScopes(projectPath);
|
|
287
|
-
const manifestResult = await loadManifestArtifact(projectPath, args.manifestPath) ?? await scanSourceManifest(projectPath, packageMetadata);
|
|
288
|
-
const entries = Object.entries(manifestResult.manifest.objects ?? {});
|
|
289
|
-
const objects = detail === "summary" ? entries.map(([manifestKey, object]) => summarizeObject({
|
|
290
|
-
manifestKey,
|
|
291
|
-
object,
|
|
292
|
-
projectPath,
|
|
293
|
-
tenantScope: tenantScopes.get(object.className)
|
|
294
|
-
})) : entries.map(([manifestKey, object]) => formatObject({
|
|
295
|
-
manifestKey,
|
|
296
|
-
object,
|
|
297
|
-
projectPath,
|
|
298
|
-
includeFields,
|
|
299
|
-
includeRelationships,
|
|
300
|
-
includeMethods,
|
|
301
|
-
tenantScope: tenantScopes.get(object.className)
|
|
302
|
-
}));
|
|
303
|
-
objects.sort((left, right) => left.className.localeCompare(right.className));
|
|
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;
|
|
309
|
-
const output = {
|
|
310
|
-
projectPath,
|
|
311
|
-
manifestSource: manifestResult.source,
|
|
312
|
-
manifestPath: "path" in manifestResult ? relative(projectPath, manifestResult.path) : void 0,
|
|
313
|
-
packageName: manifestResult.manifest.packageName ?? packageMetadata.name ?? void 0,
|
|
314
|
-
packageVersion: manifestResult.manifest.packageVersion ?? packageMetadata.version ?? void 0,
|
|
315
|
-
detail,
|
|
316
|
-
objectCount: objects.length,
|
|
317
|
-
scannedFileCount: manifestResult.scannedFileCount,
|
|
318
|
-
parseTimeMs: manifestResult.parseTimeMs,
|
|
319
|
-
...cursor ? { cursor } : {},
|
|
320
|
-
...nextCursor ? { nextCursor } : {},
|
|
321
|
-
...omitted > 0 ? { truncated: {
|
|
322
|
-
returnedObjectCount: kept.length,
|
|
323
|
-
omittedObjectCount: omitted,
|
|
324
|
-
budgetChars: maxChars,
|
|
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."
|
|
327
|
-
} } : {},
|
|
328
|
-
objects: kept,
|
|
329
|
-
diagnostics: manifestResult.diagnostics
|
|
330
|
-
};
|
|
331
|
-
return JSON.stringify(output, null, 2);
|
|
332
|
-
}
|
|
333
|
-
/**
|
|
334
|
-
* Trims the object list to the character budget, always keeping at least one so
|
|
335
|
-
* a caller sees the shape rather than an empty list.
|
|
336
|
-
*/
|
|
337
|
-
function applyObjectBudget(objects, maxChars) {
|
|
338
|
-
const kept = [];
|
|
339
|
-
let used = 0;
|
|
340
|
-
for (const object of objects) {
|
|
341
|
-
const size = JSON.stringify(object, null, 2).length + 2;
|
|
342
|
-
if (used + size > maxChars && kept.length > 0) break;
|
|
343
|
-
used += size;
|
|
344
|
-
kept.push(object);
|
|
345
|
-
}
|
|
346
|
-
return {
|
|
347
|
-
kept,
|
|
348
|
-
omitted: objects.length - kept.length
|
|
349
|
-
};
|
|
350
|
-
}
|
|
351
|
-
function summarizeObject({ manifestKey, object, projectPath, tenantScope }) {
|
|
352
|
-
const fields = Object.entries(object.fields ?? {});
|
|
353
|
-
const decoratorConfig = object.decoratorConfig ?? {};
|
|
354
|
-
return compactObject({
|
|
355
|
-
manifestKey,
|
|
356
|
-
className: object.className,
|
|
357
|
-
qualifiedName: object.qualifiedName,
|
|
358
|
-
filePath: sanitizePath$1(projectPath, object.filePath),
|
|
359
|
-
extends: object.extends,
|
|
360
|
-
collection: object.collection,
|
|
361
|
-
tableName: object.schema?.tableName ?? stringFromConfig(decoratorConfig.tableName) ?? object.collection,
|
|
362
|
-
tenantScope: tenantScope ?? normalizeTenantScopedConfig(decoratorConfig.tenantScoped),
|
|
363
|
-
fieldCount: fields.length,
|
|
364
|
-
relationships: fields.filter(([, field]) => RELATIONSHIP_TYPES.has(field.type)).map(([name, field]) => `${name} -> ${field.related ?? ""} (${field.type})`).join(", ") || void 0,
|
|
365
|
-
mcpOperations: mcpOperationsFromConfig(decoratorConfig.mcp)
|
|
366
|
-
});
|
|
367
|
-
}
|
|
368
|
-
/**
|
|
369
|
-
* An omitted `mcp` config means full CRUD, not a closed surface — the same rule
|
|
370
|
-
* the knowledge index applies.
|
|
371
|
-
*/
|
|
372
|
-
function mcpOperationsFromConfig(config) {
|
|
373
|
-
if (config === false) return [];
|
|
374
|
-
if (typeof config !== "object" || config === null || Array.isArray(config)) return [...DEFAULT_MCP_OPERATIONS];
|
|
375
|
-
const record = config;
|
|
376
|
-
const include = Array.isArray(record.include) ? record.include.filter((item) => typeof item === "string") : DEFAULT_MCP_OPERATIONS;
|
|
377
|
-
const exclude = new Set(Array.isArray(record.exclude) ? record.exclude.filter((item) => typeof item === "string") : []);
|
|
378
|
-
return include.filter((operation) => !exclude.has(operation));
|
|
379
|
-
}
|
|
380
|
-
async function loadManifestArtifact(projectPath, manifestPath) {
|
|
381
|
-
const candidates = manifestPath ? [resolve(projectPath, manifestPath)] : DEFAULT_MANIFEST_PATHS.map((candidate) => join(projectPath, candidate));
|
|
382
|
-
const diagnostics = [];
|
|
383
|
-
for (const candidate of candidates) {
|
|
384
|
-
if (!await pathExists$1(candidate)) continue;
|
|
385
|
-
try {
|
|
386
|
-
const parsed = JSON.parse(await readFile(candidate, "utf-8"));
|
|
387
|
-
if (isManifestLike(parsed)) return {
|
|
388
|
-
source: "manifest",
|
|
389
|
-
path: candidate,
|
|
390
|
-
manifest: parsed,
|
|
391
|
-
diagnostics
|
|
392
|
-
};
|
|
393
|
-
diagnostics.push({
|
|
394
|
-
severity: "warning",
|
|
395
|
-
filePath: candidate,
|
|
396
|
-
message: "Manifest artifact is present but does not contain objects."
|
|
397
|
-
});
|
|
398
|
-
} catch (error) {
|
|
399
|
-
diagnostics.push({
|
|
400
|
-
severity: "error",
|
|
401
|
-
filePath: candidate,
|
|
402
|
-
message: `Unable to parse manifest artifact: ${messageFromError(error)}`
|
|
403
|
-
});
|
|
404
|
-
}
|
|
405
|
-
}
|
|
406
|
-
return manifestPath ? {
|
|
407
|
-
source: "manifest",
|
|
408
|
-
path: resolve(projectPath, manifestPath),
|
|
409
|
-
manifest: { objects: {} },
|
|
410
|
-
diagnostics: [...diagnostics, {
|
|
411
|
-
severity: "warning",
|
|
412
|
-
filePath: resolve(projectPath, manifestPath),
|
|
413
|
-
message: "Requested manifest artifact was not found."
|
|
414
|
-
}]
|
|
415
|
-
} : void 0;
|
|
416
|
-
}
|
|
417
|
-
async function scanSourceManifest(projectPath, packageMetadata) {
|
|
418
|
-
const { results, resolved } = await new OxcScanner({
|
|
419
|
-
cwd: projectPath,
|
|
420
|
-
include: [
|
|
421
|
-
"**/*.ts",
|
|
422
|
-
"**/*.tsx",
|
|
423
|
-
"**/*.js",
|
|
424
|
-
"**/*.jsx"
|
|
425
|
-
],
|
|
426
|
-
exclude: SCAN_EXCLUDE
|
|
427
|
-
}).scanAndResolve();
|
|
428
|
-
const manifest = new ManifestAdapter().toManifest(resolved.filter((classDef) => classDef.hasSmartDecorator), {
|
|
429
|
-
packageName: packageMetadata.name,
|
|
430
|
-
packageVersion: packageMetadata.version,
|
|
431
|
-
typeAliases: results.typeAliases
|
|
432
|
-
});
|
|
433
|
-
finalizeScannerManifest(manifest, packageMetadata);
|
|
434
|
-
return {
|
|
435
|
-
source: "scanner",
|
|
436
|
-
manifest,
|
|
437
|
-
diagnostics: results.errors.map(scanErrorToDiagnostic),
|
|
438
|
-
scannedFileCount: results.fileCount,
|
|
439
|
-
parseTimeMs: Math.round(results.totalParseTimeMs)
|
|
440
|
-
};
|
|
441
|
-
}
|
|
442
|
-
function finalizeScannerManifest(manifest, packageMetadata) {
|
|
443
|
-
const manifestGen = new ManifestGenerator();
|
|
444
|
-
const fullManifest = manifest;
|
|
445
|
-
withSuppressedConsoleLog(() => {
|
|
446
|
-
manifestGen.injectTenantScopedFields(fullManifest);
|
|
447
|
-
manifestGen.mergeInheritedFields(fullManifest);
|
|
448
|
-
manifestGen.generateValidationRules(fullManifest);
|
|
449
|
-
manifestGen.generateSchemas(fullManifest);
|
|
450
|
-
manifestGen.assertTenantScopedSchemaContract(fullManifest);
|
|
451
|
-
manifestGen.generateAgentManifests(fullManifest, packageMetadata.name, packageMetadata.json);
|
|
452
|
-
});
|
|
453
|
-
}
|
|
454
|
-
function withSuppressedConsoleLog(callback) {
|
|
455
|
-
const originalLog = console.log;
|
|
456
|
-
console.log = () => void 0;
|
|
457
|
-
try {
|
|
458
|
-
return callback();
|
|
459
|
-
} finally {
|
|
460
|
-
console.log = originalLog;
|
|
461
|
-
}
|
|
462
|
-
}
|
|
463
|
-
function formatObject({ manifestKey, object, projectPath, includeFields, includeRelationships, includeMethods, tenantScope }) {
|
|
464
|
-
const fieldDetails = Object.entries(object.fields ?? {}).map(([name, field]) => ({
|
|
465
|
-
name,
|
|
466
|
-
type: field.type,
|
|
467
|
-
...field.required !== void 0 ? { required: field.required } : {},
|
|
468
|
-
...field.default !== void 0 ? { default: field.default } : {},
|
|
469
|
-
...field.related ? { related: field.related } : {},
|
|
470
|
-
...field.description ? { description: field.description } : {},
|
|
471
|
-
...field._meta ? { meta: field._meta } : {},
|
|
472
|
-
...field.transient !== void 0 ? { transient: field.transient } : {}
|
|
473
|
-
}));
|
|
474
|
-
const relationshipDetails = fieldDetails.filter((field) => RELATIONSHIP_TYPES.has(field.type)).map((field) => ({
|
|
475
|
-
field: field.name,
|
|
476
|
-
relatedClass: field.related ?? "",
|
|
477
|
-
type: field.type,
|
|
478
|
-
...field.meta ? { meta: field.meta } : {}
|
|
479
|
-
}));
|
|
480
|
-
const methodDetails = Object.entries(object.methods ?? {}).map(([name, method]) => ({
|
|
481
|
-
name: method.name ?? name,
|
|
482
|
-
isAsync: method.async === true,
|
|
483
|
-
isStatic: method.isStatic === true,
|
|
484
|
-
isPublic: method.isPublic !== false,
|
|
485
|
-
parameters: method.parameters ?? [],
|
|
486
|
-
returnType: method.returnType ?? "unknown",
|
|
487
|
-
...method.description ? { description: method.description } : {}
|
|
488
|
-
}));
|
|
489
|
-
const decoratorConfig = object.decoratorConfig ?? {};
|
|
490
|
-
const effectiveTenantScope = tenantScope ?? normalizeTenantScopedConfig(decoratorConfig.tenantScoped);
|
|
491
|
-
const schema = object.schema;
|
|
492
|
-
return compactObject({
|
|
493
|
-
manifestKey,
|
|
494
|
-
name: object.name,
|
|
495
|
-
className: object.className,
|
|
496
|
-
qualifiedName: object.qualifiedName,
|
|
497
|
-
filePath: sanitizePath$1(projectPath, object.filePath),
|
|
498
|
-
packageName: object.packageName,
|
|
499
|
-
packageVersion: object.packageVersion,
|
|
500
|
-
importPath: object.importPath,
|
|
501
|
-
modulePath: object.modulePath,
|
|
502
|
-
exportName: object.exportName,
|
|
503
|
-
collectionExportName: object.collectionExportName,
|
|
504
|
-
collection: object.collection,
|
|
505
|
-
extends: object.extends,
|
|
506
|
-
extendsTypeArg: object.extendsTypeArg,
|
|
507
|
-
tableName: schema?.tableName ?? stringFromConfig(decoratorConfig.tableName) ?? object.collection,
|
|
508
|
-
tableStrategy: stringFromConfig(decoratorConfig.tableStrategy) ?? "cti",
|
|
509
|
-
conflictColumns: arrayFromConfig(decoratorConfig.conflictColumns),
|
|
510
|
-
tenantScope: effectiveTenantScope,
|
|
511
|
-
decoratorConfig,
|
|
512
|
-
schema: schema ? {
|
|
513
|
-
tableName: schema.tableName,
|
|
514
|
-
columns: schema.columns,
|
|
515
|
-
indexes: schema.indexes ?? [],
|
|
516
|
-
version: schema.version
|
|
517
|
-
} : void 0,
|
|
518
|
-
indexes: schema?.indexes ?? [],
|
|
519
|
-
staticProperties: object.staticProperties,
|
|
520
|
-
validationRules: object.validationRules,
|
|
521
|
-
...includeFields && {
|
|
522
|
-
fields: fieldDetails.map((field) => `${field.name}: ${field.type}`).join(", "),
|
|
523
|
-
fieldDetails
|
|
524
|
-
},
|
|
525
|
-
...includeRelationships && relationshipDetails.length > 0 && {
|
|
526
|
-
relationships: relationshipDetails.map((relationship) => `${relationship.field} -> ${relationship.relatedClass} (${relationship.type})`).join(", "),
|
|
527
|
-
relationshipDetails
|
|
528
|
-
},
|
|
529
|
-
...includeMethods && methodDetails.length > 0 && {
|
|
530
|
-
methods: methodDetails.map((method) => `${method.isAsync ? "async " : ""}${method.name}()`).join(", "),
|
|
531
|
-
methodDetails
|
|
532
|
-
}
|
|
533
|
-
});
|
|
534
|
-
}
|
|
535
|
-
async function scanTenantScopes(projectPath) {
|
|
536
|
-
const files = await listSourceFiles$1(projectPath);
|
|
537
|
-
const scopes = /* @__PURE__ */ new Map();
|
|
538
|
-
await Promise.all(files.map(async (filePath) => {
|
|
539
|
-
const content = await readFile(filePath, "utf-8");
|
|
540
|
-
const classMatches = content.matchAll(/class\s+([A-Za-z_]\w*)\b/g);
|
|
541
|
-
for (const match of classMatches) {
|
|
542
|
-
const className = match[1];
|
|
543
|
-
if (!className || match.index === void 0) continue;
|
|
544
|
-
const prefix = content.slice(Math.max(0, match.index - 800), match.index);
|
|
545
|
-
const tenantMatch = Array.from(prefix.matchAll(/@TenantScoped\s*\(([\s\S]*?)\)/g)).at(-1);
|
|
546
|
-
if (!tenantMatch) continue;
|
|
547
|
-
scopes.set(className, {
|
|
548
|
-
source: "TenantScoped",
|
|
549
|
-
...parseTenantScopedOptions(tenantMatch[1])
|
|
550
|
-
});
|
|
551
|
-
}
|
|
552
|
-
}));
|
|
553
|
-
return scopes;
|
|
554
|
-
}
|
|
555
|
-
async function listSourceFiles$1(projectPath) {
|
|
556
|
-
const files = [];
|
|
557
|
-
async function visit(dir) {
|
|
558
|
-
let entries;
|
|
559
|
-
try {
|
|
560
|
-
entries = await readdir(dir, { withFileTypes: true });
|
|
561
|
-
} catch {
|
|
562
|
-
return;
|
|
563
|
-
}
|
|
564
|
-
for (const entry of entries) {
|
|
565
|
-
const fullPath = join(dir, entry.name);
|
|
566
|
-
if (entry.isDirectory()) {
|
|
567
|
-
if ([
|
|
568
|
-
"node_modules",
|
|
569
|
-
"dist",
|
|
570
|
-
"build",
|
|
571
|
-
".git",
|
|
572
|
-
".smrt",
|
|
573
|
-
"__tests__"
|
|
574
|
-
].includes(entry.name) || entry.name.startsWith(".")) continue;
|
|
575
|
-
await visit(fullPath);
|
|
576
|
-
continue;
|
|
577
|
-
}
|
|
578
|
-
if (entry.isFile() && /\.(tsx?|jsx?)$/.test(entry.name) && !entry.name.endsWith(".d.ts") && !entry.name.endsWith(".test.ts") && !entry.name.endsWith(".spec.ts")) files.push(fullPath);
|
|
579
|
-
}
|
|
580
|
-
}
|
|
581
|
-
await visit(projectPath);
|
|
582
|
-
return files;
|
|
583
|
-
}
|
|
584
|
-
function parseTenantScopedOptions(raw) {
|
|
585
|
-
const mode = raw.match(/mode\s*:\s*['"`](required|optional)['"`]/)?.[1];
|
|
586
|
-
const field = raw.match(/field\s*:\s*['"`]([A-Za-z_]\w*)['"`]/)?.[1];
|
|
587
|
-
const allowSuperAdminBypass = raw.match(/allowSuperAdminBypass\s*:\s*(true|false)/)?.[1];
|
|
588
|
-
const autoFilter = raw.match(/autoFilter\s*:\s*(true|false)/)?.[1];
|
|
589
|
-
const autoPopulate = raw.match(/autoPopulate\s*:\s*(true|false)/)?.[1];
|
|
590
|
-
return compactObject({
|
|
591
|
-
mode: mode ?? "required",
|
|
592
|
-
field: field ?? "tenantId",
|
|
593
|
-
autoFilter: autoFilter === void 0 ? void 0 : autoFilter === "true",
|
|
594
|
-
autoPopulate: autoPopulate === void 0 ? void 0 : autoPopulate === "true",
|
|
595
|
-
allowSuperAdminBypass: allowSuperAdminBypass === void 0 ? void 0 : allowSuperAdminBypass === "true"
|
|
596
|
-
});
|
|
597
|
-
}
|
|
598
|
-
function normalizeTenantScopedConfig(tenantScoped) {
|
|
599
|
-
if (!tenantScoped) return void 0;
|
|
600
|
-
const options = typeof tenantScoped === "object" && !Array.isArray(tenantScoped) ? tenantScoped : {};
|
|
601
|
-
return {
|
|
602
|
-
source: "smrt",
|
|
603
|
-
mode: options.mode ?? "required",
|
|
604
|
-
field: options.field ?? "tenantId",
|
|
605
|
-
autoFilter: options.autoFilter ?? true,
|
|
606
|
-
autoPopulate: options.autoPopulate ?? true,
|
|
607
|
-
allowSuperAdminBypass: options.allowSuperAdminBypass ?? false
|
|
608
|
-
};
|
|
609
|
-
}
|
|
610
|
-
async function readPackageMetadata(projectPath) {
|
|
611
|
-
const packageJsonPath = join(projectPath, "package.json");
|
|
612
|
-
if (!await pathExists$1(packageJsonPath)) return {};
|
|
613
|
-
try {
|
|
614
|
-
const json = JSON.parse(await readFile(packageJsonPath, "utf-8"));
|
|
615
|
-
return {
|
|
616
|
-
name: typeof json.name === "string" ? json.name : void 0,
|
|
617
|
-
version: typeof json.version === "string" ? json.version : void 0,
|
|
618
|
-
json
|
|
619
|
-
};
|
|
620
|
-
} catch {
|
|
621
|
-
return {};
|
|
622
|
-
}
|
|
623
|
-
}
|
|
624
|
-
async function pathExists$1(path) {
|
|
625
|
-
try {
|
|
626
|
-
await access(path);
|
|
627
|
-
return true;
|
|
628
|
-
} catch {
|
|
629
|
-
return false;
|
|
630
|
-
}
|
|
631
|
-
}
|
|
632
|
-
function isManifestLike(value) {
|
|
633
|
-
return !!value && typeof value === "object" && !!value.objects && typeof value.objects === "object";
|
|
634
|
-
}
|
|
635
|
-
function scanErrorToDiagnostic(error) {
|
|
636
|
-
return {
|
|
637
|
-
severity: error.severity,
|
|
638
|
-
message: error.message,
|
|
639
|
-
filePath: error.filePath,
|
|
640
|
-
line: error.line,
|
|
641
|
-
column: error.column
|
|
642
|
-
};
|
|
643
|
-
}
|
|
644
|
-
function sanitizePath$1(projectPath, filePath) {
|
|
645
|
-
const relativePath = relative(projectPath, isAbsolute(filePath) ? filePath : resolve(projectPath, filePath));
|
|
646
|
-
if (!relativePath.startsWith("..")) return relativePath || filePath;
|
|
647
|
-
return filePath;
|
|
648
|
-
}
|
|
649
|
-
function stringFromConfig(value) {
|
|
650
|
-
return typeof value === "string" ? value : void 0;
|
|
651
|
-
}
|
|
652
|
-
function arrayFromConfig(value) {
|
|
653
|
-
return Array.isArray(value) ? value.filter((item) => typeof item === "string") : void 0;
|
|
654
|
-
}
|
|
655
231
|
function compactObject(value) {
|
|
656
232
|
return Object.fromEntries(Object.entries(value).filter(([, entry]) => entry !== void 0));
|
|
657
233
|
}
|
|
658
|
-
function messageFromError(error) {
|
|
659
|
-
return error instanceof Error ? error.message : String(error);
|
|
660
|
-
}
|
|
661
234
|
//#endregion
|
|
662
235
|
//#region src/tools/review-smrt-project.ts
|
|
663
236
|
/**
|
|
@@ -1346,7 +919,7 @@ function createServer() {
|
|
|
1346
919
|
result = await generateSmrtClass(args);
|
|
1347
920
|
break;
|
|
1348
921
|
case "introspect-project":
|
|
1349
|
-
result = await introspectProject(args);
|
|
922
|
+
result = await (await import("./introspect-project-Peu2LC2T.js")).introspectProject(args);
|
|
1350
923
|
break;
|
|
1351
924
|
case "review-smrt-project":
|
|
1352
925
|
result = await reviewSmrtProject(args);
|
|
@@ -1414,32 +987,35 @@ function createServer() {
|
|
|
1414
987
|
result = JSON.stringify(await getAgentSkill(args), null, 2);
|
|
1415
988
|
break;
|
|
1416
989
|
case "migration-status":
|
|
1417
|
-
result = JSON.stringify(await runtimeMigrationStatus(args), null, 2);
|
|
1418
|
-
break;
|
|
1419
990
|
case "job-health":
|
|
1420
|
-
result = JSON.stringify(await runtimeJobHealth(args), null, 2);
|
|
1421
|
-
break;
|
|
1422
991
|
case "schedule-health":
|
|
1423
|
-
result = JSON.stringify(await runtimeScheduleHealth(args), null, 2);
|
|
1424
|
-
break;
|
|
1425
992
|
case "dispatch-health":
|
|
1426
|
-
result = JSON.stringify(await runtimeDispatchHealth(args), null, 2);
|
|
1427
|
-
break;
|
|
1428
993
|
case "recent-changes":
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
994
|
+
case "registry-drift": {
|
|
995
|
+
const runtime = await import("./tools-ma2JFPVY.js").then((n) => n.l);
|
|
996
|
+
const handlers = {
|
|
997
|
+
"migration-status": runtime.runtimeMigrationStatus,
|
|
998
|
+
"job-health": runtime.runtimeJobHealth,
|
|
999
|
+
"schedule-health": runtime.runtimeScheduleHealth,
|
|
1000
|
+
"dispatch-health": runtime.runtimeDispatchHealth,
|
|
1001
|
+
"recent-changes": runtime.runtimeRecentChanges,
|
|
1002
|
+
"registry-drift": runtime.runtimeRegistryDrift
|
|
1003
|
+
};
|
|
1004
|
+
result = JSON.stringify(await handlers[name](args), null, 2);
|
|
1433
1005
|
break;
|
|
1006
|
+
}
|
|
1434
1007
|
case "runtime-registry":
|
|
1435
|
-
result = JSON.stringify(await runtimeRegistry(args), null, 2);
|
|
1436
|
-
break;
|
|
1437
1008
|
case "runtime-object":
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1009
|
+
case "runtime-schema-diff": {
|
|
1010
|
+
const observation = await import("./observation-CqE0tBgS.js").then((n) => n.t);
|
|
1011
|
+
const handlers = {
|
|
1012
|
+
"runtime-registry": observation.runtimeRegistry,
|
|
1013
|
+
"runtime-object": observation.runtimeObject,
|
|
1014
|
+
"runtime-schema-diff": observation.runtimeSchemaDiff
|
|
1015
|
+
};
|
|
1016
|
+
result = JSON.stringify(await handlers[name](args), null, 2);
|
|
1442
1017
|
break;
|
|
1018
|
+
}
|
|
1443
1019
|
default: throw new Error(`Unknown tool: ${name}`);
|
|
1444
1020
|
}
|
|
1445
1021
|
return {
|
|
@@ -1463,6 +1039,7 @@ function createServer() {
|
|
|
1463
1039
|
coverage: null,
|
|
1464
1040
|
diagnostics: [{
|
|
1465
1041
|
severity: "error",
|
|
1042
|
+
...error instanceof ProjectRuntimeResolutionError ? { code: error.code } : {},
|
|
1466
1043
|
message: errorMessage
|
|
1467
1044
|
}],
|
|
1468
1045
|
data: null
|