@happyvertical/smrt-dev-mcp 0.37.2 → 0.37.4

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/dist/index.js CHANGED
@@ -1,115 +1,101 @@
1
1
  #!/usr/bin/env node
2
- import { readFileSync, existsSync, realpathSync } from 'node:fs';
3
- import { join, dirname, resolve, relative, isAbsolute, sep } from 'node:path';
4
- import { fileURLToPath, pathToFileURL } from 'node:url';
5
- import { Server } from '@modelcontextprotocol/sdk/server/index.js';
6
- import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
7
- import { ListToolsRequestSchema, ListPromptsRequestSchema, GetPromptRequestSchema, McpError, ErrorCode, ListResourcesRequestSchema, ReadResourceRequestSchema, CallToolRequestSchema } from '@modelcontextprotocol/sdk/types.js';
8
- import { c as buildReviewContext, b as buildArchitectureContext, a as buildKnowledgeIndex, s as smrtArchitecture, h as smrtReview, d as checkKnowledgeFreshness, e as checkKnowledgeFreshnessFromIndex } from './index-K9gNIcVt.js';
9
- import { access, readFile, readdir } from 'node:fs/promises';
10
- import { ManifestGenerator } from '@happyvertical/smrt-core/scanner';
11
- import { OxcScanner, ManifestAdapter } from '@happyvertical/smrt-scanner';
12
-
13
- const AGENT_SKILLS = [
14
- {
15
- name: "smrt-code-review",
16
- description: "Harness-agnostic downstream SMRT code review workflow using smrt-dev-mcp deterministic context and prompt bundles.",
17
- path: "agent-skills/smrt-code-review/SKILL.md",
18
- skillFile: "agent-skills/smrt-code-review/SKILL.md",
19
- references: ["agent-skills/smrt-code-review/references/review-output.md"]
20
- }
21
- ];
2
+ import { a as checkKnowledgeFreshnessFromIndex, i as checkKnowledgeFreshness, l as smrtArchitecture, n as buildKnowledgeIndex, r as buildReviewContext, t as buildArchitectureContext, u as smrtReview } from "./knowledge-DDunC2zg.js";
3
+ import { existsSync, readFileSync, realpathSync } from "node:fs";
4
+ import { dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
5
+ import { fileURLToPath, pathToFileURL } from "node:url";
6
+ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
7
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
8
+ import { CallToolRequestSchema, ErrorCode, GetPromptRequestSchema, ListPromptsRequestSchema, ListResourcesRequestSchema, ListToolsRequestSchema, McpError, ReadResourceRequestSchema } from "@modelcontextprotocol/sdk/types.js";
9
+ import { access, readFile, readdir } from "node:fs/promises";
10
+ import { ManifestGenerator } from "@happyvertical/smrt-core/scanner";
11
+ import { ManifestAdapter, OxcScanner } from "@happyvertical/smrt-scanner";
12
+ //#region src/agent-skills.ts
13
+ var AGENT_SKILLS = [{
14
+ name: "smrt-code-review",
15
+ description: "Harness-agnostic downstream SMRT code review workflow using smrt-dev-mcp deterministic context and prompt bundles.",
16
+ path: "agent-skills/smrt-code-review/SKILL.md",
17
+ skillFile: "agent-skills/smrt-code-review/SKILL.md",
18
+ references: ["agent-skills/smrt-code-review/references/review-output.md"]
19
+ }];
22
20
  function listAgentSkills() {
23
- return AGENT_SKILLS.map(({ skillFile: _skillFile, ...skill }) => skill);
21
+ return AGENT_SKILLS.map(({ skillFile: _skillFile, ...skill }) => skill);
24
22
  }
25
23
  function getAgentSkill(options) {
26
- const skill = AGENT_SKILLS.find((item) => item.name === options.name);
27
- if (!skill) {
28
- throw new Error(`Unknown agent skill: ${options.name}`);
29
- }
30
- return {
31
- name: skill.name,
32
- description: skill.description,
33
- path: skill.path,
34
- references: skill.references,
35
- skillMarkdown: readPackageFile(skill.skillFile),
36
- referenceFiles: options.includeReferences === false ? [] : skill.references.map((path) => ({
37
- path,
38
- content: readPackageFile(path)
39
- }))
40
- };
24
+ const skill = AGENT_SKILLS.find((item) => item.name === options.name);
25
+ if (!skill) throw new Error(`Unknown agent skill: ${options.name}`);
26
+ return {
27
+ name: skill.name,
28
+ description: skill.description,
29
+ path: skill.path,
30
+ references: skill.references,
31
+ skillMarkdown: readPackageFile(skill.skillFile),
32
+ referenceFiles: options.includeReferences === false ? [] : skill.references.map((path) => ({
33
+ path,
34
+ content: readPackageFile(path)
35
+ }))
36
+ };
41
37
  }
42
38
  function readPackageFile(relativePath) {
43
- const packageRoot = resolvePackageRoot();
44
- return readFileSync(join(packageRoot, relativePath), "utf8");
39
+ return readFileSync(join(resolvePackageRoot(), relativePath), "utf8");
45
40
  }
46
41
  function resolvePackageRoot() {
47
- const packageRoot = dirname(dirname(fileURLToPath(import.meta.url)));
48
- if (!existsSync(join(packageRoot, "package.json"))) {
49
- throw new Error(
50
- `Unable to resolve smrt-dev-mcp package root from ${import.meta.url}`
51
- );
52
- }
53
- return packageRoot;
54
- }
55
-
56
- const TYPE_MAPPING = {
57
- text: { tsType: "string", defaultValue: "''" },
58
- integer: { tsType: "number", defaultValue: "0" },
59
- decimal: { tsType: "number", defaultValue: "0.0" },
60
- boolean: { tsType: "boolean", defaultValue: "false" },
61
- datetime: { tsType: "Date", defaultValue: "new Date()" },
62
- json: { tsType: "any", defaultValue: "{}" }
42
+ const packageRoot = dirname(dirname(fileURLToPath(import.meta.url)));
43
+ if (!existsSync(join(packageRoot, "package.json"))) throw new Error(`Unable to resolve smrt-dev-mcp package root from ${import.meta.url}`);
44
+ return packageRoot;
45
+ }
46
+ //#endregion
47
+ //#region src/tools/generate-smrt-class.ts
48
+ var TYPE_MAPPING = {
49
+ text: {
50
+ tsType: "string",
51
+ defaultValue: "''"
52
+ },
53
+ integer: {
54
+ tsType: "number",
55
+ defaultValue: "0"
56
+ },
57
+ decimal: {
58
+ tsType: "number",
59
+ defaultValue: "0.0"
60
+ },
61
+ boolean: {
62
+ tsType: "boolean",
63
+ defaultValue: "false"
64
+ },
65
+ datetime: {
66
+ tsType: "Date",
67
+ defaultValue: "new Date()"
68
+ },
69
+ json: {
70
+ tsType: "any",
71
+ defaultValue: "{}"
72
+ }
63
73
  };
64
74
  async function generateSmrtClass(args) {
65
- const normalized = normalizeArgs(args);
66
- const {
67
- className,
68
- properties,
69
- relationships,
70
- baseClass,
71
- tableName,
72
- conflictColumns,
73
- tenantScoped,
74
- includeTenantIdField,
75
- includeApiConfig,
76
- includeMcpConfig,
77
- includeCliConfig,
78
- includeCompanionSnippets
79
- } = normalized;
80
- const coreImports = /* @__PURE__ */ new Set([baseClass, "smrt"]);
81
- if (needsFieldDecorator(properties)) coreImports.add("field");
82
- for (const relationship of relationships) {
83
- coreImports.add(relationship.type);
84
- }
85
- const imports = [
86
- `import { ${Array.from(coreImports).join(", ")} } from '@happyvertical/smrt-core';`
87
- ];
88
- if (tenantScoped) {
89
- const tenancyImports = ["TenantScoped"];
90
- if (includeTenantIdField) tenancyImports.push("tenantId");
91
- imports.push(
92
- `import { ${tenancyImports.join(", ")} } from '@happyvertical/smrt-tenancy';`
93
- );
94
- }
95
- const decoratorLines = [
96
- ...tenantScoped ? [`@TenantScoped(${renderObjectLiteral({ ...tenantScoped })})`] : [],
97
- renderSmrtDecorator({
98
- includeApiConfig,
99
- includeMcpConfig,
100
- includeCliConfig,
101
- tableName,
102
- conflictColumns
103
- })
104
- ];
105
- const classMembers = [
106
- ...includeTenantIdField && tenantScoped ? [renderTenantIdField(tenantScoped)] : [],
107
- ...properties.map(renderProperty),
108
- ...relationships.map(renderRelationship)
109
- ].filter(Boolean);
110
- const companionSnippets = includeCompanionSnippets ? `
111
- ${renderCompanionSnippets(className, Boolean(tenantScoped))}` : "";
112
- return `${imports.join("\n")}
75
+ const { className, properties, relationships, baseClass, tableName, conflictColumns, tenantScoped, includeTenantIdField, includeApiConfig, includeMcpConfig, includeCliConfig, includeCompanionSnippets } = normalizeArgs(args);
76
+ const coreImports = /* @__PURE__ */ new Set([baseClass, "smrt"]);
77
+ if (needsFieldDecorator(properties)) coreImports.add("field");
78
+ for (const relationship of relationships) coreImports.add(relationship.type);
79
+ const imports = [`import { ${Array.from(coreImports).join(", ")} } from '@happyvertical/smrt-core';`];
80
+ if (tenantScoped) {
81
+ const tenancyImports = ["TenantScoped"];
82
+ if (includeTenantIdField) tenancyImports.push("tenantId");
83
+ imports.push(`import { ${tenancyImports.join(", ")} } from '@happyvertical/smrt-tenancy';`);
84
+ }
85
+ const decoratorLines = [...tenantScoped ? [`@TenantScoped(${renderObjectLiteral({ ...tenantScoped })})`] : [], renderSmrtDecorator({
86
+ includeApiConfig,
87
+ includeMcpConfig,
88
+ includeCliConfig,
89
+ tableName,
90
+ conflictColumns
91
+ })];
92
+ const classMembers = [
93
+ ...includeTenantIdField && tenantScoped ? [renderTenantIdField(tenantScoped)] : [],
94
+ ...properties.map(renderProperty),
95
+ ...relationships.map(renderRelationship)
96
+ ].filter(Boolean);
97
+ const companionSnippets = includeCompanionSnippets ? `\n${renderCompanionSnippets(className, Boolean(tenantScoped))}` : "";
98
+ return `${imports.join("\n")}
113
99
 
114
100
  ${decoratorLines.join("\n")}
115
101
  export class ${className} extends ${baseClass} {
@@ -123,1957 +109,1676 @@ ${classMembers.join("\n\n")}
123
109
  ${companionSnippets}`;
124
110
  }
125
111
  function normalizeArgs(args) {
126
- const template = args.template ?? "basic";
127
- const templateDefaults = defaultsForTemplate(template);
128
- const tenantScoped = args.tenantScoped === true ? templateDefaults.tenantScoped ?? { mode: "required" } : args.tenantScoped === false ? void 0 : args.tenantScoped ?? templateDefaults.tenantScoped;
129
- return {
130
- className: args.className,
131
- properties: args.properties,
132
- baseClass: args.baseClass ?? "SmrtObject",
133
- template,
134
- tableName: args.tableName ?? templateDefaults.tableName,
135
- conflictColumns: args.conflictColumns ?? templateDefaults.conflictColumns ?? [],
136
- tenantScoped: normalizeTenantScoped(tenantScoped),
137
- includeTenantIdField: args.includeTenantIdField ?? templateDefaults.includeTenantIdField ?? Boolean(tenantScoped),
138
- relationships: args.relationships ?? [],
139
- includeApiConfig: args.includeApiConfig ?? true,
140
- includeMcpConfig: args.includeMcpConfig ?? true,
141
- includeCliConfig: args.includeCliConfig ?? true,
142
- includeCompanionSnippets: args.includeCompanionSnippets ?? false
143
- };
112
+ const template = args.template ?? "basic";
113
+ const templateDefaults = defaultsForTemplate(template);
114
+ const tenantScoped = args.tenantScoped === true ? templateDefaults.tenantScoped ?? { mode: "required" } : args.tenantScoped === false ? void 0 : args.tenantScoped ?? templateDefaults.tenantScoped;
115
+ return {
116
+ className: args.className,
117
+ properties: args.properties,
118
+ baseClass: args.baseClass ?? "SmrtObject",
119
+ template,
120
+ tableName: args.tableName ?? templateDefaults.tableName,
121
+ conflictColumns: args.conflictColumns ?? templateDefaults.conflictColumns ?? [],
122
+ tenantScoped: normalizeTenantScoped(tenantScoped),
123
+ includeTenantIdField: args.includeTenantIdField ?? templateDefaults.includeTenantIdField ?? Boolean(tenantScoped),
124
+ relationships: args.relationships ?? [],
125
+ includeApiConfig: args.includeApiConfig ?? true,
126
+ includeMcpConfig: args.includeMcpConfig ?? true,
127
+ includeCliConfig: args.includeCliConfig ?? true,
128
+ includeCompanionSnippets: args.includeCompanionSnippets ?? false
129
+ };
144
130
  }
145
131
  function defaultsForTemplate(template) {
146
- switch (template) {
147
- case "optional-catalog":
148
- return {
149
- tenantScoped: { mode: "optional" },
150
- includeTenantIdField: true,
151
- conflictColumns: ["tenant_id", "slug"]
152
- };
153
- case "tenant-project-object":
154
- return {
155
- tenantScoped: { mode: "required" },
156
- includeTenantIdField: true
157
- };
158
- case "tenant-event-log-object":
159
- return {
160
- tenantScoped: { mode: "optional" },
161
- includeTenantIdField: true
162
- };
163
- case "global-catalog":
164
- return { conflictColumns: ["slug"] };
165
- case "cross-package-reference":
166
- case "basic":
167
- return {};
168
- }
132
+ switch (template) {
133
+ case "optional-catalog": return {
134
+ tenantScoped: { mode: "optional" },
135
+ includeTenantIdField: true,
136
+ conflictColumns: ["tenant_id", "slug"]
137
+ };
138
+ case "tenant-project-object": return {
139
+ tenantScoped: { mode: "required" },
140
+ includeTenantIdField: true
141
+ };
142
+ case "tenant-event-log-object": return {
143
+ tenantScoped: { mode: "optional" },
144
+ includeTenantIdField: true
145
+ };
146
+ case "global-catalog": return { conflictColumns: ["slug"] };
147
+ case "cross-package-reference":
148
+ case "basic": return {};
149
+ }
169
150
  }
170
151
  function normalizeTenantScoped(value) {
171
- if (!value) return void 0;
172
- return {
173
- mode: typeof value === "object" ? value.mode ?? "required" : "required",
174
- field: typeof value === "object" ? value.field ?? "tenantId" : "tenantId",
175
- autoFilter: typeof value === "object" ? value.autoFilter : void 0,
176
- autoPopulate: typeof value === "object" ? value.autoPopulate : void 0,
177
- allowSuperAdminBypass: typeof value === "object" ? value.allowSuperAdminBypass : void 0
178
- };
152
+ if (!value) return void 0;
153
+ return {
154
+ mode: typeof value === "object" ? value.mode ?? "required" : "required",
155
+ field: typeof value === "object" ? value.field ?? "tenantId" : "tenantId",
156
+ autoFilter: typeof value === "object" ? value.autoFilter : void 0,
157
+ autoPopulate: typeof value === "object" ? value.autoPopulate : void 0,
158
+ allowSuperAdminBypass: typeof value === "object" ? value.allowSuperAdminBypass : void 0
159
+ };
179
160
  }
180
161
  function renderSmrtDecorator(options) {
181
- const decoratorConfig = {};
182
- if (options.tableName) {
183
- decoratorConfig.tableName = options.tableName;
184
- }
185
- if (options.conflictColumns.length > 0) {
186
- decoratorConfig.conflictColumns = options.conflictColumns;
187
- }
188
- if (options.includeApiConfig) {
189
- decoratorConfig.api = {
190
- include: ["list", "get", "create", "update"],
191
- exclude: ["delete"]
192
- };
193
- }
194
- if (options.includeMcpConfig) {
195
- decoratorConfig.mcp = {
196
- include: ["list", "get"]
197
- };
198
- }
199
- if (options.includeCliConfig) {
200
- decoratorConfig.cli = true;
201
- }
202
- return Object.keys(decoratorConfig).length > 0 ? `@smrt(${JSON.stringify(decoratorConfig, null, 2)})` : "@smrt()";
162
+ const decoratorConfig = {};
163
+ if (options.tableName) decoratorConfig.tableName = options.tableName;
164
+ if (options.conflictColumns.length > 0) decoratorConfig.conflictColumns = options.conflictColumns;
165
+ if (options.includeApiConfig) decoratorConfig.api = {
166
+ include: [
167
+ "list",
168
+ "get",
169
+ "create",
170
+ "update"
171
+ ],
172
+ exclude: ["delete"]
173
+ };
174
+ if (options.includeMcpConfig) decoratorConfig.mcp = { include: ["list", "get"] };
175
+ if (options.includeCliConfig) decoratorConfig.cli = true;
176
+ return Object.keys(decoratorConfig).length > 0 ? `@smrt(${JSON.stringify(decoratorConfig, null, 2)})` : "@smrt()";
203
177
  }
204
178
  function renderTenantIdField(tenantScoped) {
205
- const nullable = tenantScoped.mode === "optional";
206
- const field = tenantScoped.field ?? "tenantId";
207
- return nullable ? ` @tenantId({ nullable: true })
208
- ${field}: string | null = null;` : ` @tenantId()
209
- ${field}: string = '';`;
179
+ const nullable = tenantScoped.mode === "optional";
180
+ const field = tenantScoped.field ?? "tenantId";
181
+ return nullable ? ` @tenantId({ nullable: true })\n ${field}: string | null = null;` : ` @tenantId()\n ${field}: string = '';`;
210
182
  }
211
183
  function renderProperty(prop) {
212
- const mapping = TYPE_MAPPING[prop.type];
213
- const nullable = prop.nullable === true;
214
- const tsType = nullable ? `${mapping.tsType} | null` : mapping.tsType;
215
- const defaultValue = prop.defaultValue !== void 0 ? renderLiteral(prop.defaultValue) : nullable ? "null" : mapping.defaultValue;
216
- const fieldOptions = compactObject$1({
217
- required: prop.required,
218
- nullable: prop.nullable,
219
- description: prop.description
220
- });
221
- const jsdoc = prop.description ? ` /** ${prop.description} */
222
- ` : "";
223
- const decorator = Object.keys(fieldOptions).length > 0 ? ` @field(${JSON.stringify(fieldOptions)})
224
- ` : "";
225
- return `${jsdoc}${decorator} ${prop.name}: ${tsType} = ${defaultValue};`;
184
+ const mapping = TYPE_MAPPING[prop.type];
185
+ const nullable = prop.nullable === true;
186
+ const tsType = nullable ? `${mapping.tsType} | null` : mapping.tsType;
187
+ const defaultValue = prop.defaultValue !== void 0 ? renderLiteral(prop.defaultValue) : nullable ? "null" : mapping.defaultValue;
188
+ const fieldOptions = compactObject$1({
189
+ required: prop.required,
190
+ nullable: prop.nullable,
191
+ description: prop.description
192
+ });
193
+ return `${prop.description ? ` /** ${prop.description} */\n` : ""}${Object.keys(fieldOptions).length > 0 ? ` @field(${JSON.stringify(fieldOptions)})\n` : ""} ${prop.name}: ${tsType} = ${defaultValue};`;
226
194
  }
227
195
  function renderRelationship(relationship) {
228
- const options = compactObject$1({
229
- required: relationship.required,
230
- nullable: relationship.nullable,
231
- description: relationship.description,
232
- validate: relationship.validate,
233
- foreignKey: relationship.foreignKey,
234
- through: relationship.through,
235
- sourceKey: relationship.sourceKey,
236
- targetKey: relationship.targetKey
237
- });
238
- const args = [
239
- renderLiteral(relationship.related),
240
- ...Object.keys(options).length > 0 ? [JSON.stringify(options)] : []
241
- ];
242
- const decorator = `@${relationship.type}(${args.join(", ")})`;
243
- const fieldType = relationship.type === "oneToMany" || relationship.type === "manyToMany" ? "unknown[]" : relationship.nullable ? "string | null" : "string";
244
- const defaultValue = relationship.type === "oneToMany" || relationship.type === "manyToMany" ? "[]" : relationship.nullable ? "null" : "''";
245
- return ` ${decorator}
246
- ${relationship.name}: ${fieldType} = ${defaultValue};`;
196
+ const options = compactObject$1({
197
+ required: relationship.required,
198
+ nullable: relationship.nullable,
199
+ description: relationship.description,
200
+ validate: relationship.validate,
201
+ foreignKey: relationship.foreignKey,
202
+ through: relationship.through,
203
+ sourceKey: relationship.sourceKey,
204
+ targetKey: relationship.targetKey
205
+ });
206
+ const args = [renderLiteral(relationship.related), ...Object.keys(options).length > 0 ? [JSON.stringify(options)] : []];
207
+ const decorator = `@${relationship.type}(${args.join(", ")})`;
208
+ const fieldType = relationship.type === "oneToMany" || relationship.type === "manyToMany" ? "unknown[]" : relationship.nullable ? "string | null" : "string";
209
+ const defaultValue = relationship.type === "oneToMany" || relationship.type === "manyToMany" ? "[]" : relationship.nullable ? "null" : "''";
210
+ return ` ${decorator}\n ${relationship.name}: ${fieldType} = ${defaultValue};`;
247
211
  }
248
212
  function needsFieldDecorator(properties) {
249
- return properties.some(
250
- (property) => property.required !== void 0 || property.nullable !== void 0 || property.description !== void 0
251
- );
213
+ return properties.some((property) => property.required !== void 0 || property.nullable !== void 0 || property.description !== void 0);
252
214
  }
253
215
  function renderCompanionSnippets(className, usesTenantScoped) {
254
- const dependencyNote = usesTenantScoped ? `
255
- * - Ensure package.json declares "@happyvertical/smrt-tenancy".` : "";
256
- return `/*
216
+ return `/*
257
217
  * Package wiring:
258
218
  * - Export ${className} from the package entrypoint used by consumers.
259
- * - Import this module from any package registration file that eagerly loads objects.${dependencyNote}
219
+ * - Import this module from any package registration file that eagerly loads objects.${usesTenantScoped ? `\n * - Ensure package.json declares "@happyvertical/smrt-tenancy".` : ""}
260
220
  */`;
261
221
  }
262
222
  function renderObjectLiteral(value) {
263
- return JSON.stringify(compactObject$1(value), null, 2);
223
+ return JSON.stringify(compactObject$1(value), null, 2);
264
224
  }
265
225
  function renderLiteral(value) {
266
- if (typeof value === "string") return JSON.stringify(value);
267
- if (typeof value === "number" || typeof value === "boolean") {
268
- return String(value);
269
- }
270
- if (value === null) return "null";
271
- return JSON.stringify(value, null, 2);
226
+ if (typeof value === "string") return JSON.stringify(value);
227
+ if (typeof value === "number" || typeof value === "boolean") return String(value);
228
+ if (value === null) return "null";
229
+ return JSON.stringify(value, null, 2);
272
230
  }
273
231
  function compactObject$1(value) {
274
- return Object.fromEntries(
275
- Object.entries(value).filter(([, entry]) => entry !== void 0)
276
- );
277
- }
278
-
279
- const DEFAULT_MANIFEST_PATHS = [
280
- ".smrt/manifest.json",
281
- "dist/manifest.json",
282
- "src/manifest/manifest.json"
232
+ return Object.fromEntries(Object.entries(value).filter(([, entry]) => entry !== void 0));
233
+ }
234
+ //#endregion
235
+ //#region src/tools/introspect-project.ts
236
+ var DEFAULT_MANIFEST_PATHS = [
237
+ ".smrt/manifest.json",
238
+ "dist/manifest.json",
239
+ "src/manifest/manifest.json"
283
240
  ];
284
- const SCAN_EXCLUDE = [
285
- "**/node_modules/**",
286
- "**/dist/**",
287
- "**/build/**",
288
- "**/.git/**",
289
- "**/.smrt/**",
290
- "**/*.d.ts",
291
- "**/*.test.ts",
292
- "**/*.spec.ts",
293
- "**/__tests__/**"
241
+ var SCAN_EXCLUDE = [
242
+ "**/node_modules/**",
243
+ "**/dist/**",
244
+ "**/build/**",
245
+ "**/.git/**",
246
+ "**/.smrt/**",
247
+ "**/*.d.ts",
248
+ "**/*.test.ts",
249
+ "**/*.spec.ts",
250
+ "**/__tests__/**"
294
251
  ];
295
- const RELATIONSHIP_TYPES = /* @__PURE__ */ new Set([
296
- "foreignKey",
297
- "crossPackageRef",
298
- "oneToMany",
299
- "manyToMany"
252
+ var RELATIONSHIP_TYPES = /* @__PURE__ */ new Set([
253
+ "foreignKey",
254
+ "crossPackageRef",
255
+ "oneToMany",
256
+ "manyToMany"
300
257
  ]);
301
258
  async function introspectProject(args) {
302
- const {
303
- directory = process.cwd(),
304
- includeFields = true,
305
- includeRelationships = true,
306
- includeMethods = true
307
- } = args;
308
- const projectPath = resolve(directory);
309
- const exists = await pathExists$1(projectPath);
310
- if (!exists) {
311
- return JSON.stringify(
312
- {
313
- projectPath,
314
- manifestSource: "none",
315
- objectCount: 0,
316
- objects: [],
317
- diagnostics: [
318
- {
319
- severity: "warning",
320
- message: `Project directory does not exist: ${projectPath}`
321
- }
322
- ]
323
- },
324
- null,
325
- 2
326
- );
327
- }
328
- const packageMetadata = await readPackageMetadata(projectPath);
329
- const tenantScopes = await scanTenantScopes(projectPath);
330
- const manifestResult = await loadManifestArtifact(projectPath, args.manifestPath) ?? await scanSourceManifest(projectPath, packageMetadata);
331
- const objects = Object.entries(manifestResult.manifest.objects ?? {}).map(
332
- ([manifestKey, object]) => formatObject({
333
- manifestKey,
334
- object,
335
- projectPath,
336
- includeFields,
337
- includeRelationships,
338
- includeMethods,
339
- tenantScope: tenantScopes.get(object.className)
340
- })
341
- ).sort((left, right) => left.className.localeCompare(right.className));
342
- const output = {
343
- projectPath,
344
- manifestSource: manifestResult.source,
345
- manifestPath: "path" in manifestResult ? relative(projectPath, manifestResult.path) : void 0,
346
- packageName: manifestResult.manifest.packageName ?? packageMetadata.name ?? void 0,
347
- packageVersion: manifestResult.manifest.packageVersion ?? packageMetadata.version ?? void 0,
348
- objectCount: objects.length,
349
- scannedFileCount: manifestResult.scannedFileCount,
350
- parseTimeMs: manifestResult.parseTimeMs,
351
- objects,
352
- diagnostics: manifestResult.diagnostics
353
- };
354
- return JSON.stringify(output, null, 2);
259
+ const { directory = process.cwd(), includeFields = true, includeRelationships = true, includeMethods = true } = args;
260
+ const projectPath = resolve(directory);
261
+ if (!await pathExists$1(projectPath)) return JSON.stringify({
262
+ projectPath,
263
+ manifestSource: "none",
264
+ objectCount: 0,
265
+ objects: [],
266
+ diagnostics: [{
267
+ severity: "warning",
268
+ message: `Project directory does not exist: ${projectPath}`
269
+ }]
270
+ }, null, 2);
271
+ const packageMetadata = await readPackageMetadata(projectPath);
272
+ const tenantScopes = await scanTenantScopes(projectPath);
273
+ const manifestResult = await loadManifestArtifact(projectPath, args.manifestPath) ?? await scanSourceManifest(projectPath, packageMetadata);
274
+ const objects = Object.entries(manifestResult.manifest.objects ?? {}).map(([manifestKey, object]) => formatObject({
275
+ manifestKey,
276
+ object,
277
+ projectPath,
278
+ includeFields,
279
+ includeRelationships,
280
+ includeMethods,
281
+ tenantScope: tenantScopes.get(object.className)
282
+ })).sort((left, right) => left.className.localeCompare(right.className));
283
+ const output = {
284
+ projectPath,
285
+ manifestSource: manifestResult.source,
286
+ manifestPath: "path" in manifestResult ? relative(projectPath, manifestResult.path) : void 0,
287
+ packageName: manifestResult.manifest.packageName ?? packageMetadata.name ?? void 0,
288
+ packageVersion: manifestResult.manifest.packageVersion ?? packageMetadata.version ?? void 0,
289
+ objectCount: objects.length,
290
+ scannedFileCount: manifestResult.scannedFileCount,
291
+ parseTimeMs: manifestResult.parseTimeMs,
292
+ objects,
293
+ diagnostics: manifestResult.diagnostics
294
+ };
295
+ return JSON.stringify(output, null, 2);
355
296
  }
356
297
  async function loadManifestArtifact(projectPath, manifestPath) {
357
- const candidates = manifestPath ? [resolve(projectPath, manifestPath)] : DEFAULT_MANIFEST_PATHS.map((candidate) => join(projectPath, candidate));
358
- const diagnostics = [];
359
- for (const candidate of candidates) {
360
- if (!await pathExists$1(candidate)) {
361
- continue;
362
- }
363
- try {
364
- const parsed = JSON.parse(await readFile(candidate, "utf-8"));
365
- if (isManifestLike(parsed)) {
366
- return {
367
- source: "manifest",
368
- path: candidate,
369
- manifest: parsed,
370
- diagnostics
371
- };
372
- }
373
- diagnostics.push({
374
- severity: "warning",
375
- filePath: candidate,
376
- message: "Manifest artifact is present but does not contain objects."
377
- });
378
- } catch (error) {
379
- diagnostics.push({
380
- severity: "error",
381
- filePath: candidate,
382
- message: `Unable to parse manifest artifact: ${messageFromError(error)}`
383
- });
384
- }
385
- }
386
- return manifestPath ? {
387
- source: "manifest",
388
- path: resolve(projectPath, manifestPath),
389
- manifest: { objects: {} },
390
- diagnostics: [
391
- ...diagnostics,
392
- {
393
- severity: "warning",
394
- filePath: resolve(projectPath, manifestPath),
395
- message: "Requested manifest artifact was not found."
396
- }
397
- ]
398
- } : void 0;
298
+ const candidates = manifestPath ? [resolve(projectPath, manifestPath)] : DEFAULT_MANIFEST_PATHS.map((candidate) => join(projectPath, candidate));
299
+ const diagnostics = [];
300
+ for (const candidate of candidates) {
301
+ if (!await pathExists$1(candidate)) continue;
302
+ try {
303
+ const parsed = JSON.parse(await readFile(candidate, "utf-8"));
304
+ if (isManifestLike(parsed)) return {
305
+ source: "manifest",
306
+ path: candidate,
307
+ manifest: parsed,
308
+ diagnostics
309
+ };
310
+ diagnostics.push({
311
+ severity: "warning",
312
+ filePath: candidate,
313
+ message: "Manifest artifact is present but does not contain objects."
314
+ });
315
+ } catch (error) {
316
+ diagnostics.push({
317
+ severity: "error",
318
+ filePath: candidate,
319
+ message: `Unable to parse manifest artifact: ${messageFromError(error)}`
320
+ });
321
+ }
322
+ }
323
+ return manifestPath ? {
324
+ source: "manifest",
325
+ path: resolve(projectPath, manifestPath),
326
+ manifest: { objects: {} },
327
+ diagnostics: [...diagnostics, {
328
+ severity: "warning",
329
+ filePath: resolve(projectPath, manifestPath),
330
+ message: "Requested manifest artifact was not found."
331
+ }]
332
+ } : void 0;
399
333
  }
400
334
  async function scanSourceManifest(projectPath, packageMetadata) {
401
- const scanner = new OxcScanner({
402
- cwd: projectPath,
403
- include: ["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx"],
404
- exclude: SCAN_EXCLUDE
405
- });
406
- const { results, resolved } = await scanner.scanAndResolve();
407
- const adapter = new ManifestAdapter();
408
- const manifest = adapter.toManifest(
409
- resolved.filter((classDef) => classDef.hasSmartDecorator),
410
- {
411
- packageName: packageMetadata.name,
412
- packageVersion: packageMetadata.version,
413
- typeAliases: results.typeAliases
414
- }
415
- );
416
- finalizeScannerManifest(manifest, packageMetadata);
417
- return {
418
- source: "scanner",
419
- manifest,
420
- diagnostics: results.errors.map(scanErrorToDiagnostic),
421
- scannedFileCount: results.fileCount,
422
- parseTimeMs: Math.round(results.totalParseTimeMs)
423
- };
335
+ const { results, resolved } = await new OxcScanner({
336
+ cwd: projectPath,
337
+ include: [
338
+ "**/*.ts",
339
+ "**/*.tsx",
340
+ "**/*.js",
341
+ "**/*.jsx"
342
+ ],
343
+ exclude: SCAN_EXCLUDE
344
+ }).scanAndResolve();
345
+ const manifest = new ManifestAdapter().toManifest(resolved.filter((classDef) => classDef.hasSmartDecorator), {
346
+ packageName: packageMetadata.name,
347
+ packageVersion: packageMetadata.version,
348
+ typeAliases: results.typeAliases
349
+ });
350
+ finalizeScannerManifest(manifest, packageMetadata);
351
+ return {
352
+ source: "scanner",
353
+ manifest,
354
+ diagnostics: results.errors.map(scanErrorToDiagnostic),
355
+ scannedFileCount: results.fileCount,
356
+ parseTimeMs: Math.round(results.totalParseTimeMs)
357
+ };
424
358
  }
425
359
  function finalizeScannerManifest(manifest, packageMetadata) {
426
- const manifestGen = new ManifestGenerator();
427
- const fullManifest = manifest;
428
- withSuppressedConsoleLog(() => {
429
- manifestGen.injectTenantScopedFields(fullManifest);
430
- manifestGen.mergeInheritedFields(fullManifest);
431
- manifestGen.generateValidationRules(fullManifest);
432
- manifestGen.generateSchemas(fullManifest);
433
- manifestGen.assertTenantScopedSchemaContract(fullManifest);
434
- manifestGen.generateAgentManifests(
435
- fullManifest,
436
- packageMetadata.name,
437
- packageMetadata.json
438
- );
439
- });
360
+ const manifestGen = new ManifestGenerator();
361
+ const fullManifest = manifest;
362
+ withSuppressedConsoleLog(() => {
363
+ manifestGen.injectTenantScopedFields(fullManifest);
364
+ manifestGen.mergeInheritedFields(fullManifest);
365
+ manifestGen.generateValidationRules(fullManifest);
366
+ manifestGen.generateSchemas(fullManifest);
367
+ manifestGen.assertTenantScopedSchemaContract(fullManifest);
368
+ manifestGen.generateAgentManifests(fullManifest, packageMetadata.name, packageMetadata.json);
369
+ });
440
370
  }
441
371
  function withSuppressedConsoleLog(callback) {
442
- const originalLog = console.log;
443
- console.log = () => void 0;
444
- try {
445
- return callback();
446
- } finally {
447
- console.log = originalLog;
448
- }
449
- }
450
- function formatObject({
451
- manifestKey,
452
- object,
453
- projectPath,
454
- includeFields,
455
- includeRelationships,
456
- includeMethods,
457
- tenantScope
458
- }) {
459
- const fieldDetails = Object.entries(object.fields ?? {}).map(
460
- ([name, field]) => ({
461
- name,
462
- type: field.type,
463
- ...field.required !== void 0 ? { required: field.required } : {},
464
- ...field.default !== void 0 ? { default: field.default } : {},
465
- ...field.related ? { related: field.related } : {},
466
- ...field.description ? { description: field.description } : {},
467
- ...field._meta ? { meta: field._meta } : {},
468
- ...field.transient !== void 0 ? { transient: field.transient } : {}
469
- })
470
- );
471
- const relationshipDetails = fieldDetails.filter((field) => RELATIONSHIP_TYPES.has(field.type)).map((field) => ({
472
- field: field.name,
473
- relatedClass: field.related ?? "",
474
- type: field.type,
475
- ...field.meta ? { meta: field.meta } : {}
476
- }));
477
- const methodDetails = Object.entries(object.methods ?? {}).map(
478
- ([name, method]) => ({
479
- name: method.name ?? name,
480
- isAsync: method.async === true,
481
- isStatic: method.isStatic === true,
482
- isPublic: method.isPublic !== false,
483
- parameters: method.parameters ?? [],
484
- returnType: method.returnType ?? "unknown",
485
- ...method.description ? { description: method.description } : {}
486
- })
487
- );
488
- const decoratorConfig = object.decoratorConfig ?? {};
489
- const effectiveTenantScope = tenantScope ?? normalizeTenantScopedConfig(decoratorConfig.tenantScoped);
490
- const schema = object.schema;
491
- return compactObject({
492
- manifestKey,
493
- name: object.name,
494
- className: object.className,
495
- qualifiedName: object.qualifiedName,
496
- filePath: sanitizePath$1(projectPath, object.filePath),
497
- packageName: object.packageName,
498
- packageVersion: object.packageVersion,
499
- importPath: object.importPath,
500
- modulePath: object.modulePath,
501
- exportName: object.exportName,
502
- collectionExportName: object.collectionExportName,
503
- collection: object.collection,
504
- extends: object.extends,
505
- extendsTypeArg: object.extendsTypeArg,
506
- tableName: schema?.tableName ?? stringFromConfig(decoratorConfig.tableName) ?? object.collection,
507
- tableStrategy: stringFromConfig(decoratorConfig.tableStrategy) ?? "cti",
508
- conflictColumns: arrayFromConfig(decoratorConfig.conflictColumns),
509
- tenantScope: effectiveTenantScope,
510
- decoratorConfig,
511
- schema: schema ? {
512
- tableName: schema.tableName,
513
- columns: schema.columns,
514
- indexes: schema.indexes ?? [],
515
- version: schema.version
516
- } : void 0,
517
- indexes: schema?.indexes ?? [],
518
- staticProperties: object.staticProperties,
519
- validationRules: object.validationRules,
520
- ...includeFields && {
521
- fields: fieldDetails.map((field) => `${field.name}: ${field.type}`).join(", "),
522
- fieldDetails
523
- },
524
- ...includeRelationships && relationshipDetails.length > 0 && {
525
- relationships: relationshipDetails.map(
526
- (relationship) => `${relationship.field} -> ${relationship.relatedClass} (${relationship.type})`
527
- ).join(", "),
528
- relationshipDetails
529
- },
530
- ...includeMethods && methodDetails.length > 0 && {
531
- methods: methodDetails.map((method) => `${method.isAsync ? "async " : ""}${method.name}()`).join(", "),
532
- methodDetails
533
- }
534
- });
372
+ const originalLog = console.log;
373
+ console.log = () => void 0;
374
+ try {
375
+ return callback();
376
+ } finally {
377
+ console.log = originalLog;
378
+ }
379
+ }
380
+ function formatObject({ manifestKey, object, projectPath, includeFields, includeRelationships, includeMethods, tenantScope }) {
381
+ const fieldDetails = Object.entries(object.fields ?? {}).map(([name, field]) => ({
382
+ name,
383
+ type: field.type,
384
+ ...field.required !== void 0 ? { required: field.required } : {},
385
+ ...field.default !== void 0 ? { default: field.default } : {},
386
+ ...field.related ? { related: field.related } : {},
387
+ ...field.description ? { description: field.description } : {},
388
+ ...field._meta ? { meta: field._meta } : {},
389
+ ...field.transient !== void 0 ? { transient: field.transient } : {}
390
+ }));
391
+ const relationshipDetails = fieldDetails.filter((field) => RELATIONSHIP_TYPES.has(field.type)).map((field) => ({
392
+ field: field.name,
393
+ relatedClass: field.related ?? "",
394
+ type: field.type,
395
+ ...field.meta ? { meta: field.meta } : {}
396
+ }));
397
+ const methodDetails = Object.entries(object.methods ?? {}).map(([name, method]) => ({
398
+ name: method.name ?? name,
399
+ isAsync: method.async === true,
400
+ isStatic: method.isStatic === true,
401
+ isPublic: method.isPublic !== false,
402
+ parameters: method.parameters ?? [],
403
+ returnType: method.returnType ?? "unknown",
404
+ ...method.description ? { description: method.description } : {}
405
+ }));
406
+ const decoratorConfig = object.decoratorConfig ?? {};
407
+ const effectiveTenantScope = tenantScope ?? normalizeTenantScopedConfig(decoratorConfig.tenantScoped);
408
+ const schema = object.schema;
409
+ return compactObject({
410
+ manifestKey,
411
+ name: object.name,
412
+ className: object.className,
413
+ qualifiedName: object.qualifiedName,
414
+ filePath: sanitizePath$1(projectPath, object.filePath),
415
+ packageName: object.packageName,
416
+ packageVersion: object.packageVersion,
417
+ importPath: object.importPath,
418
+ modulePath: object.modulePath,
419
+ exportName: object.exportName,
420
+ collectionExportName: object.collectionExportName,
421
+ collection: object.collection,
422
+ extends: object.extends,
423
+ extendsTypeArg: object.extendsTypeArg,
424
+ tableName: schema?.tableName ?? stringFromConfig(decoratorConfig.tableName) ?? object.collection,
425
+ tableStrategy: stringFromConfig(decoratorConfig.tableStrategy) ?? "cti",
426
+ conflictColumns: arrayFromConfig(decoratorConfig.conflictColumns),
427
+ tenantScope: effectiveTenantScope,
428
+ decoratorConfig,
429
+ schema: schema ? {
430
+ tableName: schema.tableName,
431
+ columns: schema.columns,
432
+ indexes: schema.indexes ?? [],
433
+ version: schema.version
434
+ } : void 0,
435
+ indexes: schema?.indexes ?? [],
436
+ staticProperties: object.staticProperties,
437
+ validationRules: object.validationRules,
438
+ ...includeFields && {
439
+ fields: fieldDetails.map((field) => `${field.name}: ${field.type}`).join(", "),
440
+ fieldDetails
441
+ },
442
+ ...includeRelationships && relationshipDetails.length > 0 && {
443
+ relationships: relationshipDetails.map((relationship) => `${relationship.field} -> ${relationship.relatedClass} (${relationship.type})`).join(", "),
444
+ relationshipDetails
445
+ },
446
+ ...includeMethods && methodDetails.length > 0 && {
447
+ methods: methodDetails.map((method) => `${method.isAsync ? "async " : ""}${method.name}()`).join(", "),
448
+ methodDetails
449
+ }
450
+ });
535
451
  }
536
452
  async function scanTenantScopes(projectPath) {
537
- const files = await listSourceFiles$1(projectPath);
538
- const scopes = /* @__PURE__ */ new Map();
539
- await Promise.all(
540
- files.map(async (filePath) => {
541
- const content = await readFile(filePath, "utf-8");
542
- const classMatches = content.matchAll(/class\s+([A-Za-z_]\w*)\b/g);
543
- for (const match of classMatches) {
544
- const className = match[1];
545
- if (!className || match.index === void 0) continue;
546
- const prefix = content.slice(
547
- Math.max(0, match.index - 800),
548
- match.index
549
- );
550
- const tenantMatches = Array.from(
551
- prefix.matchAll(/@TenantScoped\s*\(([\s\S]*?)\)/g)
552
- );
553
- const tenantMatch = tenantMatches.at(-1);
554
- if (!tenantMatch) continue;
555
- scopes.set(className, {
556
- source: "TenantScoped",
557
- ...parseTenantScopedOptions(tenantMatch[1])
558
- });
559
- }
560
- })
561
- );
562
- return scopes;
453
+ const files = await listSourceFiles$1(projectPath);
454
+ const scopes = /* @__PURE__ */ new Map();
455
+ await Promise.all(files.map(async (filePath) => {
456
+ const content = await readFile(filePath, "utf-8");
457
+ const classMatches = content.matchAll(/class\s+([A-Za-z_]\w*)\b/g);
458
+ for (const match of classMatches) {
459
+ const className = match[1];
460
+ if (!className || match.index === void 0) continue;
461
+ const prefix = content.slice(Math.max(0, match.index - 800), match.index);
462
+ const tenantMatch = Array.from(prefix.matchAll(/@TenantScoped\s*\(([\s\S]*?)\)/g)).at(-1);
463
+ if (!tenantMatch) continue;
464
+ scopes.set(className, {
465
+ source: "TenantScoped",
466
+ ...parseTenantScopedOptions(tenantMatch[1])
467
+ });
468
+ }
469
+ }));
470
+ return scopes;
563
471
  }
564
472
  async function listSourceFiles$1(projectPath) {
565
- const files = [];
566
- async function visit(dir) {
567
- let entries;
568
- try {
569
- entries = await readdir(dir, { withFileTypes: true });
570
- } catch {
571
- return;
572
- }
573
- for (const entry of entries) {
574
- const fullPath = join(dir, entry.name);
575
- if (entry.isDirectory()) {
576
- if ([
577
- "node_modules",
578
- "dist",
579
- "build",
580
- ".git",
581
- ".smrt",
582
- "__tests__"
583
- ].includes(entry.name) || entry.name.startsWith(".")) {
584
- continue;
585
- }
586
- await visit(fullPath);
587
- continue;
588
- }
589
- if (entry.isFile() && /\.(tsx?|jsx?)$/.test(entry.name) && !entry.name.endsWith(".d.ts") && !entry.name.endsWith(".test.ts") && !entry.name.endsWith(".spec.ts")) {
590
- files.push(fullPath);
591
- }
592
- }
593
- }
594
- await visit(projectPath);
595
- return files;
473
+ const files = [];
474
+ async function visit(dir) {
475
+ let entries;
476
+ try {
477
+ entries = await readdir(dir, { withFileTypes: true });
478
+ } catch {
479
+ return;
480
+ }
481
+ for (const entry of entries) {
482
+ const fullPath = join(dir, entry.name);
483
+ if (entry.isDirectory()) {
484
+ if ([
485
+ "node_modules",
486
+ "dist",
487
+ "build",
488
+ ".git",
489
+ ".smrt",
490
+ "__tests__"
491
+ ].includes(entry.name) || entry.name.startsWith(".")) continue;
492
+ await visit(fullPath);
493
+ continue;
494
+ }
495
+ 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);
496
+ }
497
+ }
498
+ await visit(projectPath);
499
+ return files;
596
500
  }
597
501
  function parseTenantScopedOptions(raw) {
598
- const mode = raw.match(/mode\s*:\s*['"`](required|optional)['"`]/)?.[1];
599
- const field = raw.match(/field\s*:\s*['"`]([A-Za-z_]\w*)['"`]/)?.[1];
600
- const allowSuperAdminBypass = raw.match(
601
- /allowSuperAdminBypass\s*:\s*(true|false)/
602
- )?.[1];
603
- const autoFilter = raw.match(/autoFilter\s*:\s*(true|false)/)?.[1];
604
- const autoPopulate = raw.match(/autoPopulate\s*:\s*(true|false)/)?.[1];
605
- return compactObject({
606
- mode: mode ?? "required",
607
- field: field ?? "tenantId",
608
- autoFilter: autoFilter === void 0 ? void 0 : autoFilter === "true",
609
- autoPopulate: autoPopulate === void 0 ? void 0 : autoPopulate === "true",
610
- allowSuperAdminBypass: allowSuperAdminBypass === void 0 ? void 0 : allowSuperAdminBypass === "true"
611
- });
502
+ const mode = raw.match(/mode\s*:\s*['"`](required|optional)['"`]/)?.[1];
503
+ const field = raw.match(/field\s*:\s*['"`]([A-Za-z_]\w*)['"`]/)?.[1];
504
+ const allowSuperAdminBypass = raw.match(/allowSuperAdminBypass\s*:\s*(true|false)/)?.[1];
505
+ const autoFilter = raw.match(/autoFilter\s*:\s*(true|false)/)?.[1];
506
+ const autoPopulate = raw.match(/autoPopulate\s*:\s*(true|false)/)?.[1];
507
+ return compactObject({
508
+ mode: mode ?? "required",
509
+ field: field ?? "tenantId",
510
+ autoFilter: autoFilter === void 0 ? void 0 : autoFilter === "true",
511
+ autoPopulate: autoPopulate === void 0 ? void 0 : autoPopulate === "true",
512
+ allowSuperAdminBypass: allowSuperAdminBypass === void 0 ? void 0 : allowSuperAdminBypass === "true"
513
+ });
612
514
  }
613
515
  function normalizeTenantScopedConfig(tenantScoped) {
614
- if (!tenantScoped) return void 0;
615
- const options = typeof tenantScoped === "object" && !Array.isArray(tenantScoped) ? tenantScoped : {};
616
- return {
617
- source: "smrt",
618
- mode: options.mode ?? "required",
619
- field: options.field ?? "tenantId",
620
- autoFilter: options.autoFilter ?? true,
621
- autoPopulate: options.autoPopulate ?? true,
622
- allowSuperAdminBypass: options.allowSuperAdminBypass ?? false
623
- };
516
+ if (!tenantScoped) return void 0;
517
+ const options = typeof tenantScoped === "object" && !Array.isArray(tenantScoped) ? tenantScoped : {};
518
+ return {
519
+ source: "smrt",
520
+ mode: options.mode ?? "required",
521
+ field: options.field ?? "tenantId",
522
+ autoFilter: options.autoFilter ?? true,
523
+ autoPopulate: options.autoPopulate ?? true,
524
+ allowSuperAdminBypass: options.allowSuperAdminBypass ?? false
525
+ };
624
526
  }
625
527
  async function readPackageMetadata(projectPath) {
626
- const packageJsonPath = join(projectPath, "package.json");
627
- if (!await pathExists$1(packageJsonPath)) return {};
628
- try {
629
- const json = JSON.parse(await readFile(packageJsonPath, "utf-8"));
630
- return {
631
- name: typeof json.name === "string" ? json.name : void 0,
632
- version: typeof json.version === "string" ? json.version : void 0,
633
- json
634
- };
635
- } catch {
636
- return {};
637
- }
528
+ const packageJsonPath = join(projectPath, "package.json");
529
+ if (!await pathExists$1(packageJsonPath)) return {};
530
+ try {
531
+ const json = JSON.parse(await readFile(packageJsonPath, "utf-8"));
532
+ return {
533
+ name: typeof json.name === "string" ? json.name : void 0,
534
+ version: typeof json.version === "string" ? json.version : void 0,
535
+ json
536
+ };
537
+ } catch {
538
+ return {};
539
+ }
638
540
  }
639
541
  async function pathExists$1(path) {
640
- try {
641
- await access(path);
642
- return true;
643
- } catch {
644
- return false;
645
- }
542
+ try {
543
+ await access(path);
544
+ return true;
545
+ } catch {
546
+ return false;
547
+ }
646
548
  }
647
549
  function isManifestLike(value) {
648
- return !!value && typeof value === "object" && !!value.objects && typeof value.objects === "object";
550
+ return !!value && typeof value === "object" && !!value.objects && typeof value.objects === "object";
649
551
  }
650
552
  function scanErrorToDiagnostic(error) {
651
- return {
652
- severity: error.severity,
653
- message: error.message,
654
- filePath: error.filePath,
655
- line: error.line,
656
- column: error.column
657
- };
553
+ return {
554
+ severity: error.severity,
555
+ message: error.message,
556
+ filePath: error.filePath,
557
+ line: error.line,
558
+ column: error.column
559
+ };
658
560
  }
659
561
  function sanitizePath$1(projectPath, filePath) {
660
- const absolute = isAbsolute(filePath) ? filePath : resolve(projectPath, filePath);
661
- const relativePath = relative(projectPath, absolute);
662
- if (!relativePath.startsWith("..")) {
663
- return relativePath || filePath;
664
- }
665
- return filePath;
562
+ const relativePath = relative(projectPath, isAbsolute(filePath) ? filePath : resolve(projectPath, filePath));
563
+ if (!relativePath.startsWith("..")) return relativePath || filePath;
564
+ return filePath;
666
565
  }
667
566
  function stringFromConfig(value) {
668
- return typeof value === "string" ? value : void 0;
567
+ return typeof value === "string" ? value : void 0;
669
568
  }
670
569
  function arrayFromConfig(value) {
671
- return Array.isArray(value) ? value.filter((item) => typeof item === "string") : void 0;
570
+ return Array.isArray(value) ? value.filter((item) => typeof item === "string") : void 0;
672
571
  }
673
572
  function compactObject(value) {
674
- return Object.fromEntries(
675
- Object.entries(value).filter(([, entry]) => entry !== void 0)
676
- );
573
+ return Object.fromEntries(Object.entries(value).filter(([, entry]) => entry !== void 0));
677
574
  }
678
575
  function messageFromError(error) {
679
- return error instanceof Error ? error.message : String(error);
680
- }
681
-
682
- const SOURCE_EXTENSIONS = /\.(tsx?|jsx?|svelte)$/;
683
- const SKIP_DIRS = /* @__PURE__ */ new Set([
684
- "node_modules",
685
- "dist",
686
- "build",
687
- ".git",
688
- ".smrt",
689
- ".svelte-kit",
690
- "coverage"
576
+ return error instanceof Error ? error.message : String(error);
577
+ }
578
+ //#endregion
579
+ //#region src/tools/review-smrt-project.ts
580
+ /**
581
+ * Ecosystem-aware downstream project review.
582
+ * Advisory only: scans manifests and source files, then reports alignment risks.
583
+ */
584
+ var SOURCE_EXTENSIONS = /\.(tsx?|jsx?|svelte)$/;
585
+ var SKIP_DIRS = /* @__PURE__ */ new Set([
586
+ "node_modules",
587
+ "dist",
588
+ "build",
589
+ ".git",
590
+ ".smrt",
591
+ ".svelte-kit",
592
+ "coverage"
691
593
  ]);
692
- const DEPENDENCY_SECTIONS = [
693
- "dependencies",
694
- "devDependencies",
695
- "peerDependencies",
696
- "optionalDependencies"
594
+ var DEPENDENCY_SECTIONS = [
595
+ "dependencies",
596
+ "devDependencies",
597
+ "peerDependencies",
598
+ "optionalDependencies"
697
599
  ];
698
- const SMRT_PACKAGE_PREFIX = "@happyvertical/smrt-";
699
- const HAPPYVERTICAL_PACKAGE_PREFIX = "@happyvertical/";
600
+ var SMRT_PACKAGE_PREFIX = "@happyvertical/smrt-";
601
+ var HAPPYVERTICAL_PACKAGE_PREFIX = "@happyvertical/";
700
602
  async function reviewSmrtProject(args) {
701
- const projectPath = resolve(args.directory ?? args.rootDir ?? process.cwd());
702
- if (!await pathExists(projectPath)) {
703
- return JSON.stringify(
704
- {
705
- projectPath,
706
- packageCount: 0,
707
- packages: [],
708
- findings: [],
709
- summary: { high: 0, medium: 0, low: 0 },
710
- diagnostics: [
711
- {
712
- severity: "warning",
713
- message: `Project directory does not exist: ${projectPath}`
714
- }
715
- ]
716
- },
717
- null,
718
- 2
719
- );
720
- }
721
- const packageContexts = await buildPackageContexts(projectPath);
722
- const sourceFiles = await listSourceFiles(projectPath, packageContexts);
723
- await attachSourceFiles(sourceFiles, packageContexts, projectPath);
724
- const findings = limitFindings(
725
- [
726
- ...findMissingHappyVerticalDependencies(packageContexts),
727
- ...findCustomManifestGeneration(sourceFiles, projectPath),
728
- ...findDirectStorageBypasses(sourceFiles, packageContexts, projectPath),
729
- ...findCustomHttpShells(sourceFiles, packageContexts, projectPath),
730
- ...findLocalAuthTenancy(sourceFiles, packageContexts, projectPath),
731
- ...findUiShellDrift(packageContexts, projectPath),
732
- ...findMissingManifestArtifacts(
733
- sourceFiles,
734
- packageContexts,
735
- projectPath
736
- )
737
- ],
738
- args.maxFindings
739
- );
740
- const packages = packageContexts.map(packageInventory).sort((left, right) => left.path.localeCompare(right.path));
741
- const summary = summarizeFindings(findings);
742
- return JSON.stringify(
743
- {
744
- projectPath,
745
- packageCount: packages.length,
746
- packages,
747
- findings: args.includeSourceEvidence === false ? findings.map(({ evidence: _evidence, ...finding }) => finding) : findings,
748
- summary,
749
- referenceChecks: [
750
- "Prefer SMRT scanner/runtime manifests over custom manifest builders.",
751
- "Prefer SvelteKit plus @happyvertical/smrt-svelte for app shells.",
752
- "Prefer @happyvertical/sql and @happyvertical/files/assets/content over direct durable node:fs storage.",
753
- "Prefer smrt-users, smrt-tenancy, and profile/audit packages over local static auth seams."
754
- ],
755
- suggestedFollowUpIssues: findings.map((finding) => ({
756
- title: finding.suggestedIssueTitle,
757
- severity: finding.severity,
758
- area: finding.area
759
- }))
760
- },
761
- null,
762
- 2
763
- );
603
+ const projectPath = resolve(args.directory ?? args.rootDir ?? process.cwd());
604
+ if (!await pathExists(projectPath)) return JSON.stringify({
605
+ projectPath,
606
+ packageCount: 0,
607
+ packages: [],
608
+ findings: [],
609
+ summary: {
610
+ high: 0,
611
+ medium: 0,
612
+ low: 0
613
+ },
614
+ diagnostics: [{
615
+ severity: "warning",
616
+ message: `Project directory does not exist: ${projectPath}`
617
+ }]
618
+ }, null, 2);
619
+ const packageContexts = await buildPackageContexts(projectPath);
620
+ const sourceFiles = await listSourceFiles(projectPath, packageContexts);
621
+ await attachSourceFiles(sourceFiles, packageContexts, projectPath);
622
+ const findings = limitFindings([
623
+ ...findMissingHappyVerticalDependencies(packageContexts),
624
+ ...findCustomManifestGeneration(sourceFiles, projectPath),
625
+ ...findDirectStorageBypasses(sourceFiles, packageContexts, projectPath),
626
+ ...findCustomHttpShells(sourceFiles, packageContexts, projectPath),
627
+ ...findLocalAuthTenancy(sourceFiles, packageContexts, projectPath),
628
+ ...findUiShellDrift(packageContexts, projectPath),
629
+ ...findMissingManifestArtifacts(sourceFiles, packageContexts, projectPath)
630
+ ], args.maxFindings);
631
+ const packages = packageContexts.map(packageInventory).sort((left, right) => left.path.localeCompare(right.path));
632
+ const summary = summarizeFindings(findings);
633
+ return JSON.stringify({
634
+ projectPath,
635
+ packageCount: packages.length,
636
+ packages,
637
+ findings: args.includeSourceEvidence === false ? findings.map(({ evidence: _evidence, ...finding }) => finding) : findings,
638
+ summary,
639
+ referenceChecks: [
640
+ "Prefer SMRT scanner/runtime manifests over custom manifest builders.",
641
+ "Prefer SvelteKit plus @happyvertical/smrt-svelte for app shells.",
642
+ "Prefer @happyvertical/sql and @happyvertical/files/assets/content over direct durable node:fs storage.",
643
+ "Prefer smrt-users, smrt-tenancy, and profile/audit packages over local static auth seams."
644
+ ],
645
+ suggestedFollowUpIssues: findings.map((finding) => ({
646
+ title: finding.suggestedIssueTitle,
647
+ severity: finding.severity,
648
+ area: finding.area
649
+ }))
650
+ }, null, 2);
764
651
  }
765
652
  async function buildPackageContexts(projectPath) {
766
- const packageJsonPaths = await listPackageJsonFiles(projectPath);
767
- const contexts = await Promise.all(
768
- packageJsonPaths.map(async (packageJsonPath) => {
769
- const json = JSON.parse(
770
- await readFile(packageJsonPath, "utf-8")
771
- );
772
- const directory = dirname(packageJsonPath);
773
- return {
774
- directory,
775
- relativePath: relative(projectPath, directory) || ".",
776
- packageJsonPath,
777
- json,
778
- dependencies: collectDependencies(json),
779
- scripts: isRecord(json.scripts) ? json.scripts : {},
780
- imports: /* @__PURE__ */ new Map(),
781
- sourceFiles: []
782
- };
783
- })
784
- );
785
- if (contexts.length === 0) {
786
- contexts.push({
787
- directory: projectPath,
788
- relativePath: ".",
789
- packageJsonPath: join(projectPath, "package.json"),
790
- json: {},
791
- dependencies: /* @__PURE__ */ new Set(),
792
- scripts: {},
793
- imports: /* @__PURE__ */ new Map(),
794
- sourceFiles: []
795
- });
796
- }
797
- return contexts.sort(
798
- (left, right) => left.directory.length - right.directory.length
799
- );
653
+ const packageJsonPaths = await listPackageJsonFiles(projectPath);
654
+ const contexts = await Promise.all(packageJsonPaths.map(async (packageJsonPath) => {
655
+ const json = JSON.parse(await readFile(packageJsonPath, "utf-8"));
656
+ const directory = dirname(packageJsonPath);
657
+ return {
658
+ directory,
659
+ relativePath: relative(projectPath, directory) || ".",
660
+ packageJsonPath,
661
+ json,
662
+ dependencies: collectDependencies(json),
663
+ scripts: isRecord(json.scripts) ? json.scripts : {},
664
+ imports: /* @__PURE__ */ new Map(),
665
+ sourceFiles: []
666
+ };
667
+ }));
668
+ if (contexts.length === 0) contexts.push({
669
+ directory: projectPath,
670
+ relativePath: ".",
671
+ packageJsonPath: join(projectPath, "package.json"),
672
+ json: {},
673
+ dependencies: /* @__PURE__ */ new Set(),
674
+ scripts: {},
675
+ imports: /* @__PURE__ */ new Map(),
676
+ sourceFiles: []
677
+ });
678
+ return contexts.sort((left, right) => left.directory.length - right.directory.length);
800
679
  }
801
680
  async function listPackageJsonFiles(projectPath) {
802
- const files = [];
803
- await visit(projectPath, async (filePath, entryName) => {
804
- if (entryName === "package.json") files.push(filePath);
805
- });
806
- return files;
681
+ const files = [];
682
+ await visit(projectPath, async (filePath, entryName) => {
683
+ if (entryName === "package.json") files.push(filePath);
684
+ });
685
+ return files;
807
686
  }
808
687
  async function listSourceFiles(projectPath, packages) {
809
- const files = [];
810
- await visit(projectPath, async (filePath, entryName) => {
811
- if (!SOURCE_EXTENSIONS.test(entryName)) return;
812
- if (entryName.endsWith(".d.ts") || entryName.endsWith(".test.ts") || entryName.endsWith(".spec.ts")) {
813
- return;
814
- }
815
- const packageDir = findOwningPackage(filePath, packages).directory;
816
- const content = await readFile(filePath, "utf-8");
817
- files.push({ path: filePath, packageDir, content });
818
- });
819
- return files;
688
+ const files = [];
689
+ await visit(projectPath, async (filePath, entryName) => {
690
+ if (!SOURCE_EXTENSIONS.test(entryName)) return;
691
+ if (entryName.endsWith(".d.ts") || entryName.endsWith(".test.ts") || entryName.endsWith(".spec.ts")) return;
692
+ const packageDir = findOwningPackage(filePath, packages).directory;
693
+ const content = await readFile(filePath, "utf-8");
694
+ files.push({
695
+ path: filePath,
696
+ packageDir,
697
+ content
698
+ });
699
+ });
700
+ return files;
820
701
  }
821
702
  async function attachSourceFiles(sourceFiles, packages, projectPath) {
822
- for (const sourceFile of sourceFiles) {
823
- const owner = packages.find(
824
- (pkg) => pkg.directory === sourceFile.packageDir
825
- );
826
- if (!owner) continue;
827
- owner.sourceFiles.push(sourceFile);
828
- for (const importedPackage of extractImports(sourceFile.content)) {
829
- if (!importedPackage.startsWith(HAPPYVERTICAL_PACKAGE_PREFIX)) continue;
830
- const evidence = {
831
- filePath: relative(projectPath, sourceFile.path),
832
- line: lineNumber(sourceFile.content, importedPackage),
833
- detail: `Imports ${importedPackage}`
834
- };
835
- const existing = owner.imports.get(importedPackage) ?? [];
836
- existing.push(evidence);
837
- owner.imports.set(importedPackage, existing);
838
- }
839
- }
703
+ for (const sourceFile of sourceFiles) {
704
+ const owner = packages.find((pkg) => pkg.directory === sourceFile.packageDir);
705
+ if (!owner) continue;
706
+ owner.sourceFiles.push(sourceFile);
707
+ for (const importedPackage of extractImports(sourceFile.content)) {
708
+ if (!importedPackage.startsWith(HAPPYVERTICAL_PACKAGE_PREFIX)) continue;
709
+ const evidence = {
710
+ filePath: relative(projectPath, sourceFile.path),
711
+ line: lineNumber(sourceFile.content, importedPackage),
712
+ detail: `Imports ${importedPackage}`
713
+ };
714
+ const existing = owner.imports.get(importedPackage) ?? [];
715
+ existing.push(evidence);
716
+ owner.imports.set(importedPackage, existing);
717
+ }
718
+ }
840
719
  }
841
720
  function findMissingHappyVerticalDependencies(packages) {
842
- return packages.flatMap((pkg) => {
843
- const ownName = typeof pkg.json.name === "string" ? pkg.json.name : void 0;
844
- const missing = Array.from(pkg.imports.keys()).filter(
845
- (importedPackage) => importedPackage !== ownName && !pkg.dependencies.has(importedPackage)
846
- );
847
- if (missing.length === 0) return [];
848
- const hasSmrtMissing = missing.some(
849
- (name) => name.startsWith(SMRT_PACKAGE_PREFIX)
850
- );
851
- return [
852
- {
853
- severity: hasSmrtMissing ? "high" : "medium",
854
- area: "dependencies",
855
- code: "missing-happyvertical-dependencies",
856
- title: `${packageLabel(pkg)} imports HappyVertical packages that package.json does not declare`,
857
- evidence: missing.flatMap((name) => pkg.imports.get(name) ?? []),
858
- recommendation: "Declare every imported @happyvertical package in the owning package manifest so downstream installs and generated knowledge stay reproducible.",
859
- suggestedIssueTitle: `Declare missing HappyVertical dependencies in ${packageLabel(pkg)}`
860
- }
861
- ];
862
- });
721
+ return packages.flatMap((pkg) => {
722
+ const ownName = typeof pkg.json.name === "string" ? pkg.json.name : void 0;
723
+ const missing = Array.from(pkg.imports.keys()).filter((importedPackage) => importedPackage !== ownName && !pkg.dependencies.has(importedPackage));
724
+ if (missing.length === 0) return [];
725
+ return [{
726
+ severity: missing.some((name) => name.startsWith(SMRT_PACKAGE_PREFIX)) ? "high" : "medium",
727
+ area: "dependencies",
728
+ code: "missing-happyvertical-dependencies",
729
+ title: `${packageLabel(pkg)} imports HappyVertical packages that package.json does not declare`,
730
+ evidence: missing.flatMap((name) => pkg.imports.get(name) ?? []),
731
+ recommendation: "Declare every imported @happyvertical package in the owning package manifest so downstream installs and generated knowledge stay reproducible.",
732
+ suggestedIssueTitle: `Declare missing HappyVertical dependencies in ${packageLabel(pkg)}`
733
+ }];
734
+ });
863
735
  }
864
736
  function findCustomManifestGeneration(sourceFiles, projectPath) {
865
- return sourceFiles.filter(
866
- (file) => /manifest\.json/.test(file.content) && /objects\s*:/.test(file.content) && /\b(writeFile|writeFileSync)\b/.test(file.content) && !/@happyvertical\/smrt-scanner/.test(file.content)
867
- ).map((file) => ({
868
- severity: "high",
869
- area: "manifest",
870
- code: "custom-object-manifest-generation",
871
- title: "Custom SMRT object manifest generation detected",
872
- evidence: [
873
- {
874
- filePath: relative(projectPath, file.path),
875
- line: lineNumber(file.content, "manifest.json"),
876
- detail: "Writes a manifest.json object inventory outside the SMRT scanner/runtime path."
877
- }
878
- ],
879
- recommendation: "Use the SMRT scanner/runtime manifest path so defaults, relationships, schemas, tenant fields, and cross-package references match framework behavior.",
880
- suggestedIssueTitle: "Replace custom object manifest generation with SMRT scanner/runtime manifest generation"
881
- }));
737
+ return sourceFiles.filter((file) => /manifest\.json/.test(file.content) && /objects\s*:/.test(file.content) && /\b(writeFile|writeFileSync)\b/.test(file.content) && !/@happyvertical\/smrt-scanner/.test(file.content)).map((file) => ({
738
+ severity: "high",
739
+ area: "manifest",
740
+ code: "custom-object-manifest-generation",
741
+ title: "Custom SMRT object manifest generation detected",
742
+ evidence: [{
743
+ filePath: relative(projectPath, file.path),
744
+ line: lineNumber(file.content, "manifest.json"),
745
+ detail: "Writes a manifest.json object inventory outside the SMRT scanner/runtime path."
746
+ }],
747
+ recommendation: "Use the SMRT scanner/runtime manifest path so defaults, relationships, schemas, tenant fields, and cross-package references match framework behavior.",
748
+ suggestedIssueTitle: "Replace custom object manifest generation with SMRT scanner/runtime manifest generation"
749
+ }));
882
750
  }
883
751
  function findDirectStorageBypasses(sourceFiles, packages, projectPath) {
884
- return sourceFiles.flatMap((file) => {
885
- const owner = findOwningPackage(file.path, packages);
886
- const usesFs = /from\s+['"](?:node:fs|node:fs\/promises|fs|fs\/promises)['"]|require\(['"](?:node:fs|node:fs\/promises|fs|fs\/promises)['"]\)/.test(
887
- file.content
888
- );
889
- const writesDurableData = /\b(writeFile|appendFile|mkdir|rm|rename)\b/.test(file.content) && /(\.json|data|storage|persist|cache|db)/i.test(file.content);
890
- const usesDirectSql = /from\s+['"](?:better-sqlite3|sqlite3|pg|mysql2?|knex|drizzle-orm)['"]/.test(
891
- file.content
892
- );
893
- if ((!usesFs || !writesDurableData) && !usesDirectSql) return [];
894
- const hasApprovedStorage = owner.dependencies.has("@happyvertical/sql") || owner.dependencies.has("@happyvertical/files") || owner.dependencies.has("@happyvertical/smrt-assets") || owner.dependencies.has("@happyvertical/smrt-content");
895
- if (hasApprovedStorage) return [];
896
- return [
897
- {
898
- severity: "medium",
899
- area: "storage",
900
- code: "direct-storage-bypass",
901
- title: `${packageLabel(owner)} appears to bypass HappyVertical storage packages`,
902
- evidence: [
903
- {
904
- filePath: relative(projectPath, file.path),
905
- line: lineNumber(
906
- file.content,
907
- usesDirectSql ? "sqlite" : "writeFile"
908
- ),
909
- detail: usesDirectSql ? "Imports a direct SQL/storage library without @happyvertical/sql." : "Uses node:fs-style durable writes without @happyvertical/files/assets/content."
910
- }
911
- ],
912
- recommendation: "Route durable data through @happyvertical/sql, @happyvertical/files, SMRT assets, or SMRT content unless this is explicitly build-only tooling.",
913
- suggestedIssueTitle: `Review direct storage usage in ${packageLabel(owner)}`
914
- }
915
- ];
916
- });
752
+ return sourceFiles.flatMap((file) => {
753
+ const owner = findOwningPackage(file.path, packages);
754
+ const usesFs = /from\s+['"](?:node:fs|node:fs\/promises|fs|fs\/promises)['"]|require\(['"](?:node:fs|node:fs\/promises|fs|fs\/promises)['"]\)/.test(file.content);
755
+ const writesDurableData = /\b(writeFile|appendFile|mkdir|rm|rename)\b/.test(file.content) && /(\.json|data|storage|persist|cache|db)/i.test(file.content);
756
+ const usesDirectSql = /from\s+['"](?:better-sqlite3|sqlite3|pg|mysql2?|knex|drizzle-orm)['"]/.test(file.content);
757
+ if ((!usesFs || !writesDurableData) && !usesDirectSql) return [];
758
+ if (owner.dependencies.has("@happyvertical/sql") || owner.dependencies.has("@happyvertical/files") || owner.dependencies.has("@happyvertical/smrt-assets") || owner.dependencies.has("@happyvertical/smrt-content")) return [];
759
+ return [{
760
+ severity: "medium",
761
+ area: "storage",
762
+ code: "direct-storage-bypass",
763
+ title: `${packageLabel(owner)} appears to bypass HappyVertical storage packages`,
764
+ evidence: [{
765
+ filePath: relative(projectPath, file.path),
766
+ line: lineNumber(file.content, usesDirectSql ? "sqlite" : "writeFile"),
767
+ detail: usesDirectSql ? "Imports a direct SQL/storage library without @happyvertical/sql." : "Uses node:fs-style durable writes without @happyvertical/files/assets/content."
768
+ }],
769
+ recommendation: "Route durable data through @happyvertical/sql, @happyvertical/files, SMRT assets, or SMRT content unless this is explicitly build-only tooling.",
770
+ suggestedIssueTitle: `Review direct storage usage in ${packageLabel(owner)}`
771
+ }];
772
+ });
917
773
  }
918
774
  function findCustomHttpShells(sourceFiles, packages, projectPath) {
919
- const evidenceByPackage = /* @__PURE__ */ new Map();
920
- for (const pkg of packages) {
921
- if (pkg.dependencies.has("@sveltejs/kit")) continue;
922
- const routerDependencies = ["express", "fastify", "hono", "koa"].filter(
923
- (dep) => pkg.dependencies.has(dep)
924
- );
925
- if (routerDependencies.length === 0) continue;
926
- appendEvidence(evidenceByPackage, pkg, {
927
- filePath: relative(projectPath, pkg.packageJsonPath),
928
- detail: `Declares custom router package(s): ${routerDependencies.join(", ")}.`
929
- });
930
- }
931
- for (const file of sourceFiles) {
932
- const owner = findOwningPackage(file.path, packages);
933
- if (owner.dependencies.has("@sveltejs/kit")) continue;
934
- const usesNodeHttp = /from\s+['"](?:node:http|http|node:https|https)['"]|createServer\s*\(/.test(
935
- file.content
936
- );
937
- if (!usesNodeHttp) continue;
938
- appendEvidence(evidenceByPackage, owner, {
939
- filePath: relative(projectPath, file.path),
940
- line: lineNumber(file.content, "createServer"),
941
- detail: "Custom HTTP routing found without the SvelteKit/SMRT app-shell dependency pattern."
942
- });
943
- }
944
- return Array.from(evidenceByPackage.entries()).map(([owner, evidence]) => ({
945
- severity: "medium",
946
- area: "api-shell",
947
- code: "custom-http-shell",
948
- title: `${packageLabel(owner)} uses a custom HTTP shell`,
949
- evidence,
950
- recommendation: "Compare the app shell against the Anytown/Ergot SvelteKit + SMRT shell pattern before adding custom HTTP infrastructure.",
951
- suggestedIssueTitle: `Align ${packageLabel(owner)} app shell with SMRT/SvelteKit conventions`
952
- }));
775
+ const evidenceByPackage = /* @__PURE__ */ new Map();
776
+ for (const pkg of packages) {
777
+ if (pkg.dependencies.has("@sveltejs/kit")) continue;
778
+ const routerDependencies = [
779
+ "express",
780
+ "fastify",
781
+ "hono",
782
+ "koa"
783
+ ].filter((dep) => pkg.dependencies.has(dep));
784
+ if (routerDependencies.length === 0) continue;
785
+ appendEvidence(evidenceByPackage, pkg, {
786
+ filePath: relative(projectPath, pkg.packageJsonPath),
787
+ detail: `Declares custom router package(s): ${routerDependencies.join(", ")}.`
788
+ });
789
+ }
790
+ for (const file of sourceFiles) {
791
+ const owner = findOwningPackage(file.path, packages);
792
+ if (owner.dependencies.has("@sveltejs/kit")) continue;
793
+ if (!/from\s+['"](?:node:http|http|node:https|https)['"]|createServer\s*\(/.test(file.content)) continue;
794
+ appendEvidence(evidenceByPackage, owner, {
795
+ filePath: relative(projectPath, file.path),
796
+ line: lineNumber(file.content, "createServer"),
797
+ detail: "Custom HTTP routing found without the SvelteKit/SMRT app-shell dependency pattern."
798
+ });
799
+ }
800
+ return Array.from(evidenceByPackage.entries()).map(([owner, evidence]) => ({
801
+ severity: "medium",
802
+ area: "api-shell",
803
+ code: "custom-http-shell",
804
+ title: `${packageLabel(owner)} uses a custom HTTP shell`,
805
+ evidence,
806
+ recommendation: "Compare the app shell against the Anytown/Ergot SvelteKit + SMRT shell pattern before adding custom HTTP infrastructure.",
807
+ suggestedIssueTitle: `Align ${packageLabel(owner)} app shell with SMRT/SvelteKit conventions`
808
+ }));
953
809
  }
954
810
  function appendEvidence(evidenceByPackage, pkg, evidence) {
955
- const existing = evidenceByPackage.get(pkg);
956
- if (existing) {
957
- existing.push(evidence);
958
- return;
959
- }
960
- evidenceByPackage.set(pkg, [evidence]);
811
+ const existing = evidenceByPackage.get(pkg);
812
+ if (existing) {
813
+ existing.push(evidence);
814
+ return;
815
+ }
816
+ evidenceByPackage.set(pkg, [evidence]);
961
817
  }
962
818
  function findLocalAuthTenancy(sourceFiles, packages, projectPath) {
963
- return sourceFiles.flatMap((file) => {
964
- const owner = findOwningPackage(file.path, packages);
965
- const pathSignal = /(auth|tenant|audit|session|rbac|user)/i.test(file.path);
966
- const codeSignal = /\b(tenantId|tenant|role|permission|session|auditLog|apiKey)\b/.test(
967
- file.content
968
- );
969
- if (!pathSignal || !codeSignal) return [];
970
- const hasApprovedPackages = owner.dependencies.has("@happyvertical/smrt-users") || owner.dependencies.has("@happyvertical/smrt-tenancy") || owner.dependencies.has("@happyvertical/smrt-profiles");
971
- if (hasApprovedPackages) return [];
972
- return [
973
- {
974
- severity: "medium",
975
- area: "auth-tenancy",
976
- code: "local-auth-tenancy",
977
- title: `${packageLabel(owner)} contains local auth/tenancy/audit logic`,
978
- evidence: [
979
- {
980
- filePath: relative(projectPath, file.path),
981
- line: lineNumber(file.content, "tenant"),
982
- detail: "Auth, tenancy, session, role, or audit terminology appears without smrt-users/smrt-tenancy/smrt-profiles dependencies."
983
- }
984
- ],
985
- recommendation: "Add explicit adapters to smrt-users, smrt-tenancy, and profile/audit models or document why this local implementation is intentionally isolated.",
986
- suggestedIssueTitle: `Review auth and tenancy adapters in ${packageLabel(owner)}`
987
- }
988
- ];
989
- });
819
+ return sourceFiles.flatMap((file) => {
820
+ const owner = findOwningPackage(file.path, packages);
821
+ const pathSignal = /(auth|tenant|audit|session|rbac|user)/i.test(file.path);
822
+ const codeSignal = /\b(tenantId|tenant|role|permission|session|auditLog|apiKey)\b/.test(file.content);
823
+ if (!pathSignal || !codeSignal) return [];
824
+ if (owner.dependencies.has("@happyvertical/smrt-users") || owner.dependencies.has("@happyvertical/smrt-tenancy") || owner.dependencies.has("@happyvertical/smrt-profiles")) return [];
825
+ return [{
826
+ severity: "medium",
827
+ area: "auth-tenancy",
828
+ code: "local-auth-tenancy",
829
+ title: `${packageLabel(owner)} contains local auth/tenancy/audit logic`,
830
+ evidence: [{
831
+ filePath: relative(projectPath, file.path),
832
+ line: lineNumber(file.content, "tenant"),
833
+ detail: "Auth, tenancy, session, role, or audit terminology appears without smrt-users/smrt-tenancy/smrt-profiles dependencies."
834
+ }],
835
+ recommendation: "Add explicit adapters to smrt-users, smrt-tenancy, and profile/audit models or document why this local implementation is intentionally isolated.",
836
+ suggestedIssueTitle: `Review auth and tenancy adapters in ${packageLabel(owner)}`
837
+ }];
838
+ });
990
839
  }
991
840
  function findUiShellDrift(packages, projectPath) {
992
- return packages.flatMap((pkg) => {
993
- const hasUiSource = pkg.sourceFiles.some(
994
- (file) => file.path.endsWith(".svelte")
995
- );
996
- const likelyUiPackage = hasUiSource || /(?:web|ui|app|site|frontend)/i.test(packageLabel(pkg)) || pkg.dependencies.has("svelte") || pkg.dependencies.has("vite");
997
- if (!likelyUiPackage) return [];
998
- if (pkg.dependencies.has("@happyvertical/smrt-svelte")) return [];
999
- return [
1000
- {
1001
- severity: "low",
1002
- area: "ui-shell",
1003
- code: "missing-smrt-svelte-shell",
1004
- title: `${packageLabel(pkg)} looks like UI work without @happyvertical/smrt-svelte`,
1005
- evidence: [
1006
- {
1007
- filePath: relative(projectPath, pkg.packageJsonPath),
1008
- detail: "UI-facing package does not declare @happyvertical/smrt-svelte."
1009
- }
1010
- ],
1011
- recommendation: "Use @happyvertical/smrt-svelte and the SvelteKit shell pattern for downstream app UI unless this package is intentionally framework-agnostic.",
1012
- suggestedIssueTitle: `Check SMRT Svelte shell alignment for ${packageLabel(pkg)}`
1013
- }
1014
- ];
1015
- });
841
+ return packages.flatMap((pkg) => {
842
+ if (!(pkg.sourceFiles.some((file) => file.path.endsWith(".svelte")) || /(?:web|ui|app|site|frontend)/i.test(packageLabel(pkg)) || pkg.dependencies.has("svelte") || pkg.dependencies.has("vite"))) return [];
843
+ if (pkg.dependencies.has("@happyvertical/smrt-svelte")) return [];
844
+ return [{
845
+ severity: "low",
846
+ area: "ui-shell",
847
+ code: "missing-smrt-svelte-shell",
848
+ title: `${packageLabel(pkg)} looks like UI work without @happyvertical/smrt-svelte`,
849
+ evidence: [{
850
+ filePath: relative(projectPath, pkg.packageJsonPath),
851
+ detail: "UI-facing package does not declare @happyvertical/smrt-svelte."
852
+ }],
853
+ recommendation: "Use @happyvertical/smrt-svelte and the SvelteKit shell pattern for downstream app UI unless this package is intentionally framework-agnostic.",
854
+ suggestedIssueTitle: `Check SMRT Svelte shell alignment for ${packageLabel(pkg)}`
855
+ }];
856
+ });
1016
857
  }
1017
858
  function findMissingManifestArtifacts(sourceFiles, packages, projectPath) {
1018
- return packages.flatMap((pkg) => {
1019
- const hasSmrtSource = pkg.sourceFiles.some(
1020
- (file) => /@smrt\s*\(/.test(file.content)
1021
- );
1022
- if (!hasSmrtSource) return [];
1023
- const hasManifest = sourceFiles.some(
1024
- (file) => file.packageDir === pkg.directory && /@happyvertical\/smrt-scanner/.test(file.content)
1025
- ) || pathExistsSyncHint(join(pkg.directory, ".smrt", "manifest.json")) || pathExistsSyncHint(join(pkg.directory, "dist", "manifest.json"));
1026
- if (hasManifest) return [];
1027
- return [
1028
- {
1029
- severity: "low",
1030
- area: "manifest",
1031
- code: "missing-generated-manifest-artifact",
1032
- title: `${packageLabel(pkg)} has @smrt objects but no generated manifest artifact was found`,
1033
- evidence: [
1034
- {
1035
- filePath: relative(projectPath, pkg.packageJsonPath),
1036
- detail: "No .smrt/manifest.json or dist/manifest.json was visible during review."
1037
- }
1038
- ],
1039
- recommendation: "Run the package build/test manifest generation path and verify it uses the SMRT scanner/runtime manifest pipeline.",
1040
- suggestedIssueTitle: `Verify SMRT manifest generation for ${packageLabel(pkg)}`
1041
- }
1042
- ];
1043
- });
859
+ return packages.flatMap((pkg) => {
860
+ if (!pkg.sourceFiles.some((file) => /@smrt\s*\(/.test(file.content))) return [];
861
+ if (sourceFiles.some((file) => file.packageDir === pkg.directory && /@happyvertical\/smrt-scanner/.test(file.content)) || pathExistsSyncHint(join(pkg.directory, ".smrt", "manifest.json")) || pathExistsSyncHint(join(pkg.directory, "dist", "manifest.json"))) return [];
862
+ return [{
863
+ severity: "low",
864
+ area: "manifest",
865
+ code: "missing-generated-manifest-artifact",
866
+ title: `${packageLabel(pkg)} has @smrt objects but no generated manifest artifact was found`,
867
+ evidence: [{
868
+ filePath: relative(projectPath, pkg.packageJsonPath),
869
+ detail: "No .smrt/manifest.json or dist/manifest.json was visible during review."
870
+ }],
871
+ recommendation: "Run the package build/test manifest generation path and verify it uses the SMRT scanner/runtime manifest pipeline.",
872
+ suggestedIssueTitle: `Verify SMRT manifest generation for ${packageLabel(pkg)}`
873
+ }];
874
+ });
1044
875
  }
1045
876
  async function visit(dir, onFile) {
1046
- let entries;
1047
- try {
1048
- entries = await readdir(dir, { withFileTypes: true });
1049
- } catch {
1050
- return;
1051
- }
1052
- for (const entry of entries) {
1053
- const fullPath = join(dir, entry.name);
1054
- if (entry.isDirectory()) {
1055
- if (SKIP_DIRS.has(entry.name)) continue;
1056
- await visit(fullPath, onFile);
1057
- continue;
1058
- }
1059
- if (entry.isFile()) await onFile(fullPath, entry.name);
1060
- }
877
+ let entries;
878
+ try {
879
+ entries = await readdir(dir, { withFileTypes: true });
880
+ } catch {
881
+ return;
882
+ }
883
+ for (const entry of entries) {
884
+ const fullPath = join(dir, entry.name);
885
+ if (entry.isDirectory()) {
886
+ if (SKIP_DIRS.has(entry.name)) continue;
887
+ await visit(fullPath, onFile);
888
+ continue;
889
+ }
890
+ if (entry.isFile()) await onFile(fullPath, entry.name);
891
+ }
1061
892
  }
1062
893
  function collectDependencies(packageJson) {
1063
- const dependencies = /* @__PURE__ */ new Set();
1064
- for (const sectionName of DEPENDENCY_SECTIONS) {
1065
- const section = packageJson[sectionName];
1066
- if (!isRecord(section)) continue;
1067
- for (const dependencyName of Object.keys(section)) {
1068
- dependencies.add(dependencyName);
1069
- }
1070
- }
1071
- return dependencies;
894
+ const dependencies = /* @__PURE__ */ new Set();
895
+ for (const sectionName of DEPENDENCY_SECTIONS) {
896
+ const section = packageJson[sectionName];
897
+ if (!isRecord(section)) continue;
898
+ for (const dependencyName of Object.keys(section)) dependencies.add(dependencyName);
899
+ }
900
+ return dependencies;
1072
901
  }
1073
902
  function extractImports(content) {
1074
- const imports = /* @__PURE__ */ new Set();
1075
- const patterns = [
1076
- /(?:import|export)\s+(?:type\s+)?(?:[^'"]+\s+from\s+)?['"]([^'"]+)['"]/g,
1077
- /\bimport\s*\(\s*['"]([^'"]+)['"]\s*\)/g,
1078
- /\brequire\s*\(\s*['"]([^'"]+)['"]\s*\)/g
1079
- ];
1080
- for (const pattern of patterns) {
1081
- for (const match of content.matchAll(pattern)) {
1082
- const imported = normalizePackageImport(match[1]);
1083
- if (imported) imports.add(imported);
1084
- }
1085
- }
1086
- return Array.from(imports);
903
+ const imports = /* @__PURE__ */ new Set();
904
+ for (const pattern of [
905
+ /(?:import|export)\s+(?:type\s+)?(?:[^'"]+\s+from\s+)?['"]([^'"]+)['"]/g,
906
+ /\bimport\s*\(\s*['"]([^'"]+)['"]\s*\)/g,
907
+ /\brequire\s*\(\s*['"]([^'"]+)['"]\s*\)/g
908
+ ]) for (const match of content.matchAll(pattern)) {
909
+ const imported = normalizePackageImport(match[1]);
910
+ if (imported) imports.add(imported);
911
+ }
912
+ return Array.from(imports);
1087
913
  }
1088
914
  function normalizePackageImport(value) {
1089
- if (!value || value.startsWith(".") || value.startsWith("/"))
1090
- return void 0;
1091
- if (value.startsWith("@")) {
1092
- const [scope, name] = value.split("/");
1093
- return scope && name ? `${scope}/${name}` : value;
1094
- }
1095
- return value.split("/")[0];
915
+ if (!value || value.startsWith(".") || value.startsWith("/")) return void 0;
916
+ if (value.startsWith("@")) {
917
+ const [scope, name] = value.split("/");
918
+ return scope && name ? `${scope}/${name}` : value;
919
+ }
920
+ return value.split("/")[0];
1096
921
  }
1097
922
  function findOwningPackage(filePath, packages) {
1098
- const normalized = resolve(filePath);
1099
- const matches = packages.filter(
1100
- (pkg) => normalized === pkg.directory || normalized.startsWith(`${pkg.directory}${sep}`)
1101
- ).sort((left, right) => right.directory.length - left.directory.length);
1102
- return matches[0] ?? packages[0];
923
+ const normalized = resolve(filePath);
924
+ return packages.filter((pkg) => normalized === pkg.directory || normalized.startsWith(`${pkg.directory}${sep}`)).sort((left, right) => right.directory.length - left.directory.length)[0] ?? packages[0];
1103
925
  }
1104
926
  function packageInventory(pkg) {
1105
- const declaredHappyVerticalDependencies = Array.from(pkg.dependencies).filter((dependency) => dependency.startsWith(HAPPYVERTICAL_PACKAGE_PREFIX)).sort();
1106
- const importedHappyVerticalPackages = Array.from(pkg.imports.keys()).sort();
1107
- const ownName = typeof pkg.json.name === "string" ? pkg.json.name : void 0;
1108
- return {
1109
- name: packageLabel(pkg),
1110
- path: pkg.relativePath,
1111
- private: typeof pkg.json.private === "boolean" ? pkg.json.private : void 0,
1112
- scripts: Object.keys(pkg.scripts).sort(),
1113
- declaredHappyVerticalDependencies,
1114
- importedHappyVerticalPackages,
1115
- missingHappyVerticalDependencies: importedHappyVerticalPackages.filter(
1116
- (name) => name !== ownName && !pkg.dependencies.has(name)
1117
- ),
1118
- hasSvelteKit: pkg.dependencies.has("@sveltejs/kit"),
1119
- hasSmrtSvelte: pkg.dependencies.has("@happyvertical/smrt-svelte")
1120
- };
927
+ const declaredHappyVerticalDependencies = Array.from(pkg.dependencies).filter((dependency) => dependency.startsWith(HAPPYVERTICAL_PACKAGE_PREFIX)).sort();
928
+ const importedHappyVerticalPackages = Array.from(pkg.imports.keys()).sort();
929
+ const ownName = typeof pkg.json.name === "string" ? pkg.json.name : void 0;
930
+ return {
931
+ name: packageLabel(pkg),
932
+ path: pkg.relativePath,
933
+ private: typeof pkg.json.private === "boolean" ? pkg.json.private : void 0,
934
+ scripts: Object.keys(pkg.scripts).sort(),
935
+ declaredHappyVerticalDependencies,
936
+ importedHappyVerticalPackages,
937
+ missingHappyVerticalDependencies: importedHappyVerticalPackages.filter((name) => name !== ownName && !pkg.dependencies.has(name)),
938
+ hasSvelteKit: pkg.dependencies.has("@sveltejs/kit"),
939
+ hasSmrtSvelte: pkg.dependencies.has("@happyvertical/smrt-svelte")
940
+ };
1121
941
  }
1122
942
  function summarizeFindings(findings) {
1123
- return findings.reduce(
1124
- (summary, finding) => {
1125
- summary[finding.severity]++;
1126
- return summary;
1127
- },
1128
- { high: 0, medium: 0, low: 0 }
1129
- );
943
+ return findings.reduce((summary, finding) => {
944
+ summary[finding.severity]++;
945
+ return summary;
946
+ }, {
947
+ high: 0,
948
+ medium: 0,
949
+ low: 0
950
+ });
1130
951
  }
1131
952
  function limitFindings(findings, maxFindings) {
1132
- const sorted = findings.sort(
1133
- (left, right) => severityRank(left.severity) - severityRank(right.severity) || left.area.localeCompare(right.area) || left.title.localeCompare(right.title)
1134
- );
1135
- return maxFindings && maxFindings > 0 ? sorted.slice(0, maxFindings) : sorted;
953
+ const sorted = findings.sort((left, right) => severityRank(left.severity) - severityRank(right.severity) || left.area.localeCompare(right.area) || left.title.localeCompare(right.title));
954
+ return maxFindings && maxFindings > 0 ? sorted.slice(0, maxFindings) : sorted;
1136
955
  }
1137
956
  function severityRank(severity) {
1138
- return severity === "high" ? 0 : severity === "medium" ? 1 : 2;
957
+ return severity === "high" ? 0 : severity === "medium" ? 1 : 2;
1139
958
  }
1140
959
  function packageLabel(pkg) {
1141
- return typeof pkg.json.name === "string" ? pkg.json.name : pkg.relativePath;
960
+ return typeof pkg.json.name === "string" ? pkg.json.name : pkg.relativePath;
1142
961
  }
1143
962
  function lineNumber(content, needle) {
1144
- const index = content.indexOf(needle);
1145
- if (index < 0) return void 0;
1146
- return content.slice(0, index).split("\n").length;
963
+ const index = content.indexOf(needle);
964
+ if (index < 0) return void 0;
965
+ return content.slice(0, index).split("\n").length;
1147
966
  }
1148
967
  function isRecord(value) {
1149
- return !!value && typeof value === "object" && !Array.isArray(value);
968
+ return !!value && typeof value === "object" && !Array.isArray(value);
1150
969
  }
1151
970
  async function pathExists(path) {
1152
- try {
1153
- await access(path);
1154
- return true;
1155
- } catch {
1156
- return false;
1157
- }
971
+ try {
972
+ await access(path);
973
+ return true;
974
+ } catch {
975
+ return false;
976
+ }
1158
977
  }
1159
978
  function pathExistsSyncHint(path) {
1160
- return existsSync(path);
1161
- }
1162
-
1163
- const SERVER_NAME = "smrt-dev-mcp";
1164
- const SERVER_VERSION = readPackageVersion();
1165
- const DEBUG = process.env.DEBUG === "true";
1166
- const REVIEW_SKILL_NAME = "smrt-code-review";
1167
- const REVIEW_SKILL_URI = `smrt-dev-mcp://agent-skills/${REVIEW_SKILL_NAME}`;
1168
- const DOMAIN_CODE_REVIEW_PROMPT = "domain-code-review";
1169
- const DOMAIN_ARCHITECTURE_PROMPT = "domain-architecture";
1170
- const KNOWLEDGE_PROJECT_URI = "smrt://knowledge/project";
1171
- const KNOWLEDGE_PACKAGE_PREFIX = "smrt://knowledge/package/";
1172
- const TOOLS = [
1173
- // Code Generation Tools
1174
- {
1175
- name: "generate-smrt-class",
1176
- description: "Generate a complete SMRT class with @smrt() decorator",
1177
- inputSchema: {
1178
- type: "object",
1179
- properties: {
1180
- className: {
1181
- type: "string",
1182
- description: "Name of the class (PascalCase)"
1183
- },
1184
- properties: {
1185
- type: "array",
1186
- description: "Array of property definitions",
1187
- items: {
1188
- type: "object",
1189
- properties: {
1190
- name: { type: "string" },
1191
- type: {
1192
- type: "string",
1193
- enum: [
1194
- "text",
1195
- "integer",
1196
- "decimal",
1197
- "boolean",
1198
- "datetime",
1199
- "json"
1200
- ]
1201
- },
1202
- required: { type: "boolean" },
1203
- nullable: { type: "boolean" },
1204
- description: { type: "string" },
1205
- defaultValue: {
1206
- oneOf: [
1207
- { type: "string" },
1208
- { type: "number" },
1209
- { type: "boolean" },
1210
- { type: "object" },
1211
- { type: "null" }
1212
- ]
1213
- }
1214
- },
1215
- required: ["name", "type"]
1216
- }
1217
- },
1218
- baseClass: {
1219
- type: "string",
1220
- enum: ["SmrtObject", "SmrtCollection"],
1221
- default: "SmrtObject"
1222
- },
1223
- template: {
1224
- type: "string",
1225
- enum: [
1226
- "basic",
1227
- "global-catalog",
1228
- "optional-catalog",
1229
- "tenant-project-object",
1230
- "tenant-event-log-object",
1231
- "cross-package-reference"
1232
- ],
1233
- default: "basic"
1234
- },
1235
- tableName: { type: "string" },
1236
- conflictColumns: {
1237
- type: "array",
1238
- items: { type: "string" }
1239
- },
1240
- tenantScoped: {
1241
- oneOf: [
1242
- { type: "boolean" },
1243
- {
1244
- type: "object",
1245
- properties: {
1246
- mode: { type: "string", enum: ["required", "optional"] },
1247
- field: { type: "string" },
1248
- autoFilter: { type: "boolean" },
1249
- autoPopulate: { type: "boolean" },
1250
- allowSuperAdminBypass: { type: "boolean" }
1251
- }
1252
- }
1253
- ]
1254
- },
1255
- includeTenantIdField: { type: "boolean" },
1256
- relationships: {
1257
- type: "array",
1258
- items: {
1259
- type: "object",
1260
- properties: {
1261
- name: { type: "string" },
1262
- type: {
1263
- type: "string",
1264
- enum: [
1265
- "foreignKey",
1266
- "crossPackageRef",
1267
- "oneToMany",
1268
- "manyToMany"
1269
- ]
1270
- },
1271
- related: { type: "string" },
1272
- required: { type: "boolean" },
1273
- nullable: { type: "boolean" },
1274
- description: { type: "string" },
1275
- validate: { type: "boolean" },
1276
- foreignKey: { type: "string" },
1277
- through: { type: "string" },
1278
- sourceKey: { type: "string" },
1279
- targetKey: { type: "string" }
1280
- },
1281
- required: ["name", "type", "related"]
1282
- }
1283
- },
1284
- includeCompanionSnippets: { type: "boolean", default: false },
1285
- includeApiConfig: { type: "boolean", default: true },
1286
- includeMcpConfig: { type: "boolean", default: true },
1287
- includeCliConfig: { type: "boolean", default: true }
1288
- },
1289
- required: ["className", "properties"]
1290
- }
1291
- },
1292
- // Project Introspection Tools
1293
- {
1294
- name: "introspect-project",
1295
- description: "Scan current directory for SMRT objects",
1296
- inputSchema: {
1297
- type: "object",
1298
- properties: {
1299
- directory: {
1300
- type: "string",
1301
- description: "Project directory (default: cwd)"
1302
- },
1303
- manifestPath: {
1304
- type: "string",
1305
- description: "Optional manifest path. Defaults to .smrt/manifest.json, dist/manifest.json, then source scanning."
1306
- },
1307
- includeFields: {
1308
- type: "boolean",
1309
- description: "Include field details"
1310
- },
1311
- includeRelationships: {
1312
- type: "boolean",
1313
- description: "Analyze relationships"
1314
- },
1315
- includeMethods: {
1316
- type: "boolean",
1317
- description: "Include public method details"
1318
- }
1319
- }
1320
- }
1321
- },
1322
- {
1323
- name: "review-smrt-project",
1324
- description: "Advisory ecosystem alignment review for downstream SMRT projects",
1325
- inputSchema: {
1326
- type: "object",
1327
- properties: {
1328
- directory: {
1329
- type: "string",
1330
- description: "Project directory (default: cwd)"
1331
- },
1332
- rootDir: {
1333
- type: "string",
1334
- description: "Compatibility alias for directory"
1335
- },
1336
- includeSourceEvidence: {
1337
- type: "boolean",
1338
- description: "Include file and line evidence in findings",
1339
- default: true
1340
- },
1341
- maxFindings: {
1342
- type: "number",
1343
- description: "Optional maximum number of findings to return"
1344
- }
1345
- }
1346
- }
1347
- },
1348
- {
1349
- name: "reflect-knowledge",
1350
- description: "Report deterministic SMRT + HappyVertical SDK knowledge coverage and freshness",
1351
- inputSchema: {
1352
- type: "object",
1353
- properties: {
1354
- rootDir: {
1355
- type: "string",
1356
- description: "Project root directory (default: cwd)"
1357
- }
1358
- }
1359
- }
1360
- },
1361
- {
1362
- name: "reflect-domain-knowledge",
1363
- description: "Report domain-scoped SMRT knowledge artifacts, SDK packages, and freshness",
1364
- inputSchema: {
1365
- type: "object",
1366
- properties: {
1367
- rootDir: { type: "string" },
1368
- scope: {
1369
- type: "string",
1370
- enum: ["project", "local", "package", "sdk"],
1371
- default: "project"
1372
- },
1373
- package: { type: "string" }
1374
- }
1375
- }
1376
- },
1377
- {
1378
- name: "check-knowledge-freshness",
1379
- description: "Run deterministic freshness checks for SMRT agent knowledge",
1380
- inputSchema: {
1381
- type: "object",
1382
- properties: {
1383
- rootDir: { type: "string" },
1384
- changed: {
1385
- type: "boolean",
1386
- description: "Limit stale-pattern checks to changed files"
1387
- },
1388
- strict: {
1389
- type: "boolean",
1390
- description: "Treat stale-pattern findings as errors"
1391
- }
1392
- }
1393
- }
1394
- },
1395
- {
1396
- name: "check-domain-knowledge",
1397
- description: "Run deterministic freshness checks for domain knowledge artifacts",
1398
- inputSchema: {
1399
- type: "object",
1400
- properties: {
1401
- rootDir: { type: "string" },
1402
- changed: { type: "boolean" },
1403
- strict: { type: "boolean" },
1404
- scope: {
1405
- type: "string",
1406
- enum: ["project", "local", "package", "sdk"],
1407
- default: "project"
1408
- },
1409
- package: { type: "string" }
1410
- }
1411
- }
1412
- },
1413
- {
1414
- name: "build-review-context",
1415
- description: "Build model-ready SMRT review context from changed files and optional focus text",
1416
- inputSchema: {
1417
- type: "object",
1418
- properties: {
1419
- rootDir: { type: "string" },
1420
- changedFiles: { type: "array", items: { type: "string" } },
1421
- focus: { type: "string" },
1422
- documentation: { type: "string" }
1423
- }
1424
- }
1425
- },
1426
- {
1427
- name: "build-domain-review-context",
1428
- description: "Build domain-scoped model-ready SMRT review context and prompt bundle",
1429
- inputSchema: {
1430
- type: "object",
1431
- properties: {
1432
- rootDir: { type: "string" },
1433
- changedFiles: { type: "array", items: { type: "string" } },
1434
- focus: { type: "string" },
1435
- documentation: { type: "string" },
1436
- scope: {
1437
- type: "string",
1438
- enum: ["project", "local", "package", "sdk"],
1439
- default: "project"
1440
- },
1441
- package: { type: "string" }
1442
- }
1443
- }
1444
- },
1445
- {
1446
- name: "smrt-review",
1447
- description: 'Return deterministic review findings and/or a reusable model prompt bundle. For a formal downstream review, first call get-agent-skill with { "name": "smrt-code-review" } or load the smrt-code-review MCP prompt/resource.',
1448
- inputSchema: {
1449
- type: "object",
1450
- properties: {
1451
- rootDir: { type: "string" },
1452
- changedFiles: { type: "array", items: { type: "string" } },
1453
- focus: { type: "string" },
1454
- documentation: { type: "string" },
1455
- mode: {
1456
- type: "string",
1457
- enum: ["findings", "prompt-bundle", "both"],
1458
- default: "both"
1459
- }
1460
- }
1461
- }
1462
- },
1463
- {
1464
- name: "build-architecture-context",
1465
- description: "Build model-ready SMRT architecture context from an idea or documentation",
1466
- inputSchema: {
1467
- type: "object",
1468
- properties: {
1469
- rootDir: { type: "string" },
1470
- idea: { type: "string" },
1471
- documentation: { type: "string" },
1472
- focus: { type: "string" }
1473
- }
1474
- }
1475
- },
1476
- {
1477
- name: "build-domain-architecture-context",
1478
- description: "Build domain-scoped model-ready SMRT architecture context and prompt bundle",
1479
- inputSchema: {
1480
- type: "object",
1481
- properties: {
1482
- rootDir: { type: "string" },
1483
- idea: { type: "string" },
1484
- documentation: { type: "string" },
1485
- focus: { type: "string" },
1486
- scope: {
1487
- type: "string",
1488
- enum: ["project", "local", "package", "sdk"],
1489
- default: "project"
1490
- },
1491
- package: { type: "string" }
1492
- }
1493
- }
1494
- },
1495
- {
1496
- name: "smrt-architecture",
1497
- description: "Suggest SMRT and HappyVertical SDK packages and return an architecture prompt bundle",
1498
- inputSchema: {
1499
- type: "object",
1500
- properties: {
1501
- rootDir: { type: "string" },
1502
- idea: { type: "string" },
1503
- documentation: { type: "string" },
1504
- focus: { type: "string" }
1505
- }
1506
- }
1507
- },
1508
- {
1509
- name: "list-agent-skills",
1510
- description: "List bundled harness-agnostic agent skills shipped with smrt-dev-mcp",
1511
- inputSchema: {
1512
- type: "object",
1513
- properties: {}
1514
- }
1515
- },
1516
- {
1517
- name: "get-agent-skill",
1518
- description: "Return a bundled harness-agnostic agent skill as Markdown plus optional references",
1519
- inputSchema: {
1520
- type: "object",
1521
- properties: {
1522
- name: {
1523
- type: "string",
1524
- enum: [REVIEW_SKILL_NAME],
1525
- description: "Bundled agent skill name"
1526
- },
1527
- includeReferences: {
1528
- type: "boolean",
1529
- default: true,
1530
- description: "Include referenced files with the skill bundle"
1531
- }
1532
- },
1533
- required: ["name"]
1534
- }
1535
- }
979
+ return existsSync(path);
980
+ }
981
+ //#endregion
982
+ //#region src/index.ts
983
+ /**
984
+ * SMRT Development MCP Server
985
+ * Provides code generation, project introspection, knowledge context,
986
+ * review/architecture prompt bundles, and portable agent skills.
987
+ */
988
+ var SERVER_NAME = "smrt-dev-mcp";
989
+ var SERVER_VERSION = readPackageVersion();
990
+ var DEBUG = process.env.DEBUG === "true";
991
+ var REVIEW_SKILL_NAME = "smrt-code-review";
992
+ var REVIEW_SKILL_URI = `smrt-dev-mcp://agent-skills/${REVIEW_SKILL_NAME}`;
993
+ var DOMAIN_CODE_REVIEW_PROMPT = "domain-code-review";
994
+ var DOMAIN_ARCHITECTURE_PROMPT = "domain-architecture";
995
+ var KNOWLEDGE_PROJECT_URI = "smrt://knowledge/project";
996
+ var KNOWLEDGE_PACKAGE_PREFIX = "smrt://knowledge/package/";
997
+ var TOOLS = [
998
+ {
999
+ name: "generate-smrt-class",
1000
+ description: "Generate a complete SMRT class with @smrt() decorator",
1001
+ inputSchema: {
1002
+ type: "object",
1003
+ properties: {
1004
+ className: {
1005
+ type: "string",
1006
+ description: "Name of the class (PascalCase)"
1007
+ },
1008
+ properties: {
1009
+ type: "array",
1010
+ description: "Array of property definitions",
1011
+ items: {
1012
+ type: "object",
1013
+ properties: {
1014
+ name: { type: "string" },
1015
+ type: {
1016
+ type: "string",
1017
+ enum: [
1018
+ "text",
1019
+ "integer",
1020
+ "decimal",
1021
+ "boolean",
1022
+ "datetime",
1023
+ "json"
1024
+ ]
1025
+ },
1026
+ required: { type: "boolean" },
1027
+ nullable: { type: "boolean" },
1028
+ description: { type: "string" },
1029
+ defaultValue: { oneOf: [
1030
+ { type: "string" },
1031
+ { type: "number" },
1032
+ { type: "boolean" },
1033
+ { type: "object" },
1034
+ { type: "null" }
1035
+ ] }
1036
+ },
1037
+ required: ["name", "type"]
1038
+ }
1039
+ },
1040
+ baseClass: {
1041
+ type: "string",
1042
+ enum: ["SmrtObject", "SmrtCollection"],
1043
+ default: "SmrtObject"
1044
+ },
1045
+ template: {
1046
+ type: "string",
1047
+ enum: [
1048
+ "basic",
1049
+ "global-catalog",
1050
+ "optional-catalog",
1051
+ "tenant-project-object",
1052
+ "tenant-event-log-object",
1053
+ "cross-package-reference"
1054
+ ],
1055
+ default: "basic"
1056
+ },
1057
+ tableName: { type: "string" },
1058
+ conflictColumns: {
1059
+ type: "array",
1060
+ items: { type: "string" }
1061
+ },
1062
+ tenantScoped: { oneOf: [{ type: "boolean" }, {
1063
+ type: "object",
1064
+ properties: {
1065
+ mode: {
1066
+ type: "string",
1067
+ enum: ["required", "optional"]
1068
+ },
1069
+ field: { type: "string" },
1070
+ autoFilter: { type: "boolean" },
1071
+ autoPopulate: { type: "boolean" },
1072
+ allowSuperAdminBypass: { type: "boolean" }
1073
+ }
1074
+ }] },
1075
+ includeTenantIdField: { type: "boolean" },
1076
+ relationships: {
1077
+ type: "array",
1078
+ items: {
1079
+ type: "object",
1080
+ properties: {
1081
+ name: { type: "string" },
1082
+ type: {
1083
+ type: "string",
1084
+ enum: [
1085
+ "foreignKey",
1086
+ "crossPackageRef",
1087
+ "oneToMany",
1088
+ "manyToMany"
1089
+ ]
1090
+ },
1091
+ related: { type: "string" },
1092
+ required: { type: "boolean" },
1093
+ nullable: { type: "boolean" },
1094
+ description: { type: "string" },
1095
+ validate: { type: "boolean" },
1096
+ foreignKey: { type: "string" },
1097
+ through: { type: "string" },
1098
+ sourceKey: { type: "string" },
1099
+ targetKey: { type: "string" }
1100
+ },
1101
+ required: [
1102
+ "name",
1103
+ "type",
1104
+ "related"
1105
+ ]
1106
+ }
1107
+ },
1108
+ includeCompanionSnippets: {
1109
+ type: "boolean",
1110
+ default: false
1111
+ },
1112
+ includeApiConfig: {
1113
+ type: "boolean",
1114
+ default: true
1115
+ },
1116
+ includeMcpConfig: {
1117
+ type: "boolean",
1118
+ default: true
1119
+ },
1120
+ includeCliConfig: {
1121
+ type: "boolean",
1122
+ default: true
1123
+ }
1124
+ },
1125
+ required: ["className", "properties"]
1126
+ }
1127
+ },
1128
+ {
1129
+ name: "introspect-project",
1130
+ description: "Scan current directory for SMRT objects",
1131
+ inputSchema: {
1132
+ type: "object",
1133
+ properties: {
1134
+ directory: {
1135
+ type: "string",
1136
+ description: "Project directory (default: cwd)"
1137
+ },
1138
+ manifestPath: {
1139
+ type: "string",
1140
+ description: "Optional manifest path. Defaults to .smrt/manifest.json, dist/manifest.json, then source scanning."
1141
+ },
1142
+ includeFields: {
1143
+ type: "boolean",
1144
+ description: "Include field details"
1145
+ },
1146
+ includeRelationships: {
1147
+ type: "boolean",
1148
+ description: "Analyze relationships"
1149
+ },
1150
+ includeMethods: {
1151
+ type: "boolean",
1152
+ description: "Include public method details"
1153
+ }
1154
+ }
1155
+ }
1156
+ },
1157
+ {
1158
+ name: "review-smrt-project",
1159
+ description: "Advisory ecosystem alignment review for downstream SMRT projects",
1160
+ inputSchema: {
1161
+ type: "object",
1162
+ properties: {
1163
+ directory: {
1164
+ type: "string",
1165
+ description: "Project directory (default: cwd)"
1166
+ },
1167
+ rootDir: {
1168
+ type: "string",
1169
+ description: "Compatibility alias for directory"
1170
+ },
1171
+ includeSourceEvidence: {
1172
+ type: "boolean",
1173
+ description: "Include file and line evidence in findings",
1174
+ default: true
1175
+ },
1176
+ maxFindings: {
1177
+ type: "number",
1178
+ description: "Optional maximum number of findings to return"
1179
+ }
1180
+ }
1181
+ }
1182
+ },
1183
+ {
1184
+ name: "reflect-knowledge",
1185
+ description: "Report deterministic SMRT + HappyVertical SDK knowledge coverage and freshness",
1186
+ inputSchema: {
1187
+ type: "object",
1188
+ properties: { rootDir: {
1189
+ type: "string",
1190
+ description: "Project root directory (default: cwd)"
1191
+ } }
1192
+ }
1193
+ },
1194
+ {
1195
+ name: "reflect-domain-knowledge",
1196
+ description: "Report domain-scoped SMRT knowledge artifacts, SDK packages, and freshness",
1197
+ inputSchema: {
1198
+ type: "object",
1199
+ properties: {
1200
+ rootDir: { type: "string" },
1201
+ scope: {
1202
+ type: "string",
1203
+ enum: [
1204
+ "project",
1205
+ "local",
1206
+ "package",
1207
+ "sdk"
1208
+ ],
1209
+ default: "project"
1210
+ },
1211
+ package: { type: "string" }
1212
+ }
1213
+ }
1214
+ },
1215
+ {
1216
+ name: "check-knowledge-freshness",
1217
+ description: "Run deterministic freshness checks for SMRT agent knowledge",
1218
+ inputSchema: {
1219
+ type: "object",
1220
+ properties: {
1221
+ rootDir: { type: "string" },
1222
+ changed: {
1223
+ type: "boolean",
1224
+ description: "Limit stale-pattern checks to changed files"
1225
+ },
1226
+ strict: {
1227
+ type: "boolean",
1228
+ description: "Treat stale-pattern findings as errors"
1229
+ }
1230
+ }
1231
+ }
1232
+ },
1233
+ {
1234
+ name: "check-domain-knowledge",
1235
+ description: "Run deterministic freshness checks for domain knowledge artifacts",
1236
+ inputSchema: {
1237
+ type: "object",
1238
+ properties: {
1239
+ rootDir: { type: "string" },
1240
+ changed: { type: "boolean" },
1241
+ strict: { type: "boolean" },
1242
+ scope: {
1243
+ type: "string",
1244
+ enum: [
1245
+ "project",
1246
+ "local",
1247
+ "package",
1248
+ "sdk"
1249
+ ],
1250
+ default: "project"
1251
+ },
1252
+ package: { type: "string" }
1253
+ }
1254
+ }
1255
+ },
1256
+ {
1257
+ name: "build-review-context",
1258
+ description: "Build model-ready SMRT review context from changed files and optional focus text",
1259
+ inputSchema: {
1260
+ type: "object",
1261
+ properties: {
1262
+ rootDir: { type: "string" },
1263
+ changedFiles: {
1264
+ type: "array",
1265
+ items: { type: "string" }
1266
+ },
1267
+ focus: { type: "string" },
1268
+ documentation: { type: "string" }
1269
+ }
1270
+ }
1271
+ },
1272
+ {
1273
+ name: "build-domain-review-context",
1274
+ description: "Build domain-scoped model-ready SMRT review context and prompt bundle",
1275
+ inputSchema: {
1276
+ type: "object",
1277
+ properties: {
1278
+ rootDir: { type: "string" },
1279
+ changedFiles: {
1280
+ type: "array",
1281
+ items: { type: "string" }
1282
+ },
1283
+ focus: { type: "string" },
1284
+ documentation: { type: "string" },
1285
+ scope: {
1286
+ type: "string",
1287
+ enum: [
1288
+ "project",
1289
+ "local",
1290
+ "package",
1291
+ "sdk"
1292
+ ],
1293
+ default: "project"
1294
+ },
1295
+ package: { type: "string" }
1296
+ }
1297
+ }
1298
+ },
1299
+ {
1300
+ name: "smrt-review",
1301
+ description: "Return deterministic review findings and/or a reusable model prompt bundle. For a formal downstream review, first call get-agent-skill with { \"name\": \"smrt-code-review\" } or load the smrt-code-review MCP prompt/resource.",
1302
+ inputSchema: {
1303
+ type: "object",
1304
+ properties: {
1305
+ rootDir: { type: "string" },
1306
+ changedFiles: {
1307
+ type: "array",
1308
+ items: { type: "string" }
1309
+ },
1310
+ focus: { type: "string" },
1311
+ documentation: { type: "string" },
1312
+ mode: {
1313
+ type: "string",
1314
+ enum: [
1315
+ "findings",
1316
+ "prompt-bundle",
1317
+ "both"
1318
+ ],
1319
+ default: "both"
1320
+ }
1321
+ }
1322
+ }
1323
+ },
1324
+ {
1325
+ name: "build-architecture-context",
1326
+ description: "Build model-ready SMRT architecture context from an idea or documentation",
1327
+ inputSchema: {
1328
+ type: "object",
1329
+ properties: {
1330
+ rootDir: { type: "string" },
1331
+ idea: { type: "string" },
1332
+ documentation: { type: "string" },
1333
+ focus: { type: "string" }
1334
+ }
1335
+ }
1336
+ },
1337
+ {
1338
+ name: "build-domain-architecture-context",
1339
+ description: "Build domain-scoped model-ready SMRT architecture context and prompt bundle",
1340
+ inputSchema: {
1341
+ type: "object",
1342
+ properties: {
1343
+ rootDir: { type: "string" },
1344
+ idea: { type: "string" },
1345
+ documentation: { type: "string" },
1346
+ focus: { type: "string" },
1347
+ scope: {
1348
+ type: "string",
1349
+ enum: [
1350
+ "project",
1351
+ "local",
1352
+ "package",
1353
+ "sdk"
1354
+ ],
1355
+ default: "project"
1356
+ },
1357
+ package: { type: "string" }
1358
+ }
1359
+ }
1360
+ },
1361
+ {
1362
+ name: "smrt-architecture",
1363
+ description: "Suggest SMRT and HappyVertical SDK packages and return an architecture prompt bundle",
1364
+ inputSchema: {
1365
+ type: "object",
1366
+ properties: {
1367
+ rootDir: { type: "string" },
1368
+ idea: { type: "string" },
1369
+ documentation: { type: "string" },
1370
+ focus: { type: "string" }
1371
+ }
1372
+ }
1373
+ },
1374
+ {
1375
+ name: "list-agent-skills",
1376
+ description: "List bundled harness-agnostic agent skills shipped with smrt-dev-mcp",
1377
+ inputSchema: {
1378
+ type: "object",
1379
+ properties: {}
1380
+ }
1381
+ },
1382
+ {
1383
+ name: "get-agent-skill",
1384
+ description: "Return a bundled harness-agnostic agent skill as Markdown plus optional references",
1385
+ inputSchema: {
1386
+ type: "object",
1387
+ properties: {
1388
+ name: {
1389
+ type: "string",
1390
+ enum: [REVIEW_SKILL_NAME],
1391
+ description: "Bundled agent skill name"
1392
+ },
1393
+ includeReferences: {
1394
+ type: "boolean",
1395
+ default: true,
1396
+ description: "Include referenced files with the skill bundle"
1397
+ }
1398
+ },
1399
+ required: ["name"]
1400
+ }
1401
+ }
1536
1402
  ];
1537
1403
  async function main() {
1538
- if (DEBUG) {
1539
- console.error(`[${SERVER_NAME}] Starting server v${SERVER_VERSION}`);
1540
- }
1541
- const server = new Server(
1542
- {
1543
- name: SERVER_NAME,
1544
- version: SERVER_VERSION
1545
- },
1546
- {
1547
- capabilities: {
1548
- prompts: {},
1549
- resources: {},
1550
- tools: {}
1551
- }
1552
- }
1553
- );
1554
- server.setRequestHandler(ListToolsRequestSchema, async () => {
1555
- if (DEBUG) {
1556
- console.error(`[${SERVER_NAME}] ListTools request`);
1557
- }
1558
- return { tools: TOOLS };
1559
- });
1560
- server.setRequestHandler(ListPromptsRequestSchema, async () => {
1561
- return {
1562
- prompts: [
1563
- {
1564
- name: REVIEW_SKILL_NAME,
1565
- title: "SMRT Code Review",
1566
- description: "Harness-agnostic downstream SMRT review procedure that uses smrt-dev-mcp deterministic context and prompt bundles."
1567
- },
1568
- {
1569
- name: DOMAIN_CODE_REVIEW_PROMPT,
1570
- title: "Domain Code Review",
1571
- description: "Model-ready domain-scoped SMRT code review prompt bundle.",
1572
- arguments: [
1573
- {
1574
- name: "rootDir",
1575
- description: "Project root directory. Defaults to server cwd.",
1576
- required: false
1577
- },
1578
- {
1579
- name: "changedFiles",
1580
- description: "Changed file paths as newline-separated, comma-separated, or JSON array text.",
1581
- required: false
1582
- },
1583
- {
1584
- name: "focus",
1585
- description: "Review focus text.",
1586
- required: false
1587
- },
1588
- {
1589
- name: "documentation",
1590
- description: "Additional documentation or notes.",
1591
- required: false
1592
- },
1593
- {
1594
- name: "scope",
1595
- description: "Knowledge scope: project, local, package, or sdk.",
1596
- required: false
1597
- },
1598
- {
1599
- name: "package",
1600
- description: "Package name or short package selector.",
1601
- required: false
1602
- }
1603
- ]
1604
- },
1605
- {
1606
- name: DOMAIN_ARCHITECTURE_PROMPT,
1607
- title: "Domain Architecture",
1608
- description: "Model-ready domain-scoped SMRT architecture planning prompt bundle.",
1609
- arguments: [
1610
- {
1611
- name: "rootDir",
1612
- description: "Project root directory. Defaults to server cwd.",
1613
- required: false
1614
- },
1615
- {
1616
- name: "idea",
1617
- description: "Architecture idea or product concept.",
1618
- required: false
1619
- },
1620
- {
1621
- name: "documentation",
1622
- description: "Additional documentation or notes.",
1623
- required: false
1624
- },
1625
- {
1626
- name: "focus",
1627
- description: "Planning focus text.",
1628
- required: false
1629
- },
1630
- {
1631
- name: "scope",
1632
- description: "Knowledge scope: project, local, package, or sdk.",
1633
- required: false
1634
- },
1635
- {
1636
- name: "package",
1637
- description: "Package name or short package selector.",
1638
- required: false
1639
- }
1640
- ]
1641
- }
1642
- ]
1643
- };
1644
- });
1645
- server.setRequestHandler(GetPromptRequestSchema, async (request) => {
1646
- const { name } = request.params;
1647
- if (name === REVIEW_SKILL_NAME) {
1648
- return {
1649
- description: "Use this procedure when reviewing downstream SMRT projects.",
1650
- messages: [
1651
- {
1652
- role: "user",
1653
- content: {
1654
- type: "text",
1655
- text: renderAgentSkillMarkdown(REVIEW_SKILL_NAME)
1656
- }
1657
- }
1658
- ]
1659
- };
1660
- }
1661
- if (name === DOMAIN_CODE_REVIEW_PROMPT) {
1662
- const context = await buildReviewContext(
1663
- reviewPromptArguments(request.params.arguments)
1664
- );
1665
- return {
1666
- description: "Review downstream SMRT code with domain knowledge.",
1667
- messages: [
1668
- {
1669
- role: "user",
1670
- content: {
1671
- type: "text",
1672
- text: context.promptBundle.contextMarkdown
1673
- }
1674
- }
1675
- ]
1676
- };
1677
- }
1678
- if (name === DOMAIN_ARCHITECTURE_PROMPT) {
1679
- const context = await buildArchitectureContext(
1680
- architecturePromptArguments(request.params.arguments)
1681
- );
1682
- return {
1683
- description: "Plan a downstream SMRT project with domain knowledge.",
1684
- messages: [
1685
- {
1686
- role: "user",
1687
- content: {
1688
- type: "text",
1689
- text: context.promptBundle.contextMarkdown
1690
- }
1691
- }
1692
- ]
1693
- };
1694
- }
1695
- throw new McpError(ErrorCode.InvalidParams, `Unknown prompt: ${name}`);
1696
- });
1697
- server.setRequestHandler(ListResourcesRequestSchema, async () => {
1698
- const index = await buildKnowledgeIndex();
1699
- return {
1700
- resources: [
1701
- {
1702
- uri: REVIEW_SKILL_URI,
1703
- name: REVIEW_SKILL_NAME,
1704
- title: "SMRT Code Review Skill",
1705
- description: "Bundled Markdown skill for downstream SMRT code reviews.",
1706
- mimeType: "text/markdown"
1707
- },
1708
- {
1709
- uri: KNOWLEDGE_PROJECT_URI,
1710
- name: "smrt-domain-knowledge-project",
1711
- title: "SMRT Domain Knowledge Project Index",
1712
- description: "Composed SMRT, downstream domain, and HappyVertical SDK knowledge index.",
1713
- mimeType: "application/json"
1714
- },
1715
- ...index.packages.map((pkg) => ({
1716
- uri: `${KNOWLEDGE_PACKAGE_PREFIX}${encodeURIComponent(pkg.name)}`,
1717
- name: `smrt-domain-knowledge-${pkg.name}`,
1718
- title: `SMRT Domain Knowledge: ${pkg.name}`,
1719
- description: "Package-scoped SMRT domain knowledge, generated surfaces, and authored context.",
1720
- mimeType: "application/json"
1721
- }))
1722
- ]
1723
- };
1724
- });
1725
- server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
1726
- const { uri } = request.params;
1727
- if (uri === REVIEW_SKILL_URI) {
1728
- return {
1729
- contents: [
1730
- {
1731
- uri,
1732
- mimeType: "text/markdown",
1733
- text: renderAgentSkillMarkdown(REVIEW_SKILL_NAME)
1734
- }
1735
- ]
1736
- };
1737
- }
1738
- if (uri === KNOWLEDGE_PROJECT_URI) {
1739
- const index = await buildKnowledgeIndex();
1740
- return {
1741
- contents: [
1742
- {
1743
- uri,
1744
- mimeType: "application/json",
1745
- text: JSON.stringify(sanitizeKnowledgeIndex(index), null, 2)
1746
- }
1747
- ]
1748
- };
1749
- }
1750
- if (uri.startsWith(KNOWLEDGE_PACKAGE_PREFIX)) {
1751
- const packageName = decodeURIComponent(
1752
- uri.slice(KNOWLEDGE_PACKAGE_PREFIX.length)
1753
- );
1754
- const index = await buildKnowledgeIndex();
1755
- const pkg = index.packages.find((item) => item.name === packageName);
1756
- if (!pkg) {
1757
- throw new McpError(
1758
- ErrorCode.InvalidParams,
1759
- `Unknown knowledge package: ${packageName}`
1760
- );
1761
- }
1762
- return {
1763
- contents: [
1764
- {
1765
- uri,
1766
- mimeType: "application/json",
1767
- text: JSON.stringify(sanitizeKnowledgePackage(pkg), null, 2)
1768
- }
1769
- ]
1770
- };
1771
- }
1772
- throw new McpError(ErrorCode.InvalidParams, `Unknown resource: ${uri}`);
1773
- });
1774
- server.setRequestHandler(CallToolRequestSchema, async (request) => {
1775
- const { name, arguments: args } = request.params;
1776
- if (DEBUG) {
1777
- console.error(`[${SERVER_NAME}] CallTool: ${name}`);
1778
- console.error(
1779
- `[${SERVER_NAME}] Arguments:`,
1780
- JSON.stringify(args, null, 2)
1781
- );
1782
- }
1783
- try {
1784
- let result;
1785
- switch (name) {
1786
- case "generate-smrt-class":
1787
- result = await generateSmrtClass(
1788
- args
1789
- );
1790
- break;
1791
- case "introspect-project":
1792
- result = await introspectProject(
1793
- args
1794
- );
1795
- break;
1796
- case "review-smrt-project":
1797
- result = await reviewSmrtProject(
1798
- args
1799
- );
1800
- break;
1801
- case "reflect-knowledge": {
1802
- const index = await buildKnowledgeIndex(
1803
- args
1804
- );
1805
- const freshness = await checkKnowledgeFreshnessFromIndex(
1806
- index,
1807
- args
1808
- );
1809
- result = JSON.stringify(
1810
- {
1811
- rootDir: index.rootDir,
1812
- packageCount: index.packages.length,
1813
- smrtPackageCount: index.smrtPackages.length,
1814
- sdkPackageCount: index.sdkPackages.length,
1815
- relationshipsV2: index.relationshipsV2,
1816
- freshness
1817
- },
1818
- null,
1819
- 2
1820
- );
1821
- break;
1822
- }
1823
- case "reflect-domain-knowledge": {
1824
- const index = await buildKnowledgeIndex(
1825
- args
1826
- );
1827
- const freshness = await checkKnowledgeFreshnessFromIndex(
1828
- index,
1829
- args
1830
- );
1831
- result = JSON.stringify(
1832
- {
1833
- rootDir: index.rootDir,
1834
- packageCount: index.packages.length,
1835
- smrtPackageCount: index.smrtPackages.length,
1836
- sdkPackageCount: index.sdkPackages.length,
1837
- domainKnowledgePackageCount: index.packages.filter(
1838
- (pkg) => pkg.hasDomainKnowledge
1839
- ).length,
1840
- missingDomainKnowledgePackages: index.packages.filter(
1841
- (pkg) => pkg.exportKeys.includes("./smrt-knowledge.json") && !pkg.hasDomainKnowledge
1842
- ).map((pkg) => pkg.name),
1843
- relationshipsV2: index.relationshipsV2,
1844
- freshness
1845
- },
1846
- null,
1847
- 2
1848
- );
1849
- break;
1850
- }
1851
- case "check-knowledge-freshness":
1852
- result = JSON.stringify(
1853
- await checkKnowledgeFreshness(
1854
- args
1855
- ),
1856
- null,
1857
- 2
1858
- );
1859
- break;
1860
- case "check-domain-knowledge":
1861
- result = JSON.stringify(
1862
- await checkKnowledgeFreshness(
1863
- args
1864
- ),
1865
- null,
1866
- 2
1867
- );
1868
- break;
1869
- case "build-review-context":
1870
- result = JSON.stringify(
1871
- await buildReviewContext(
1872
- args
1873
- ),
1874
- null,
1875
- 2
1876
- );
1877
- break;
1878
- case "build-domain-review-context":
1879
- result = JSON.stringify(
1880
- await buildReviewContext(
1881
- args
1882
- ),
1883
- null,
1884
- 2
1885
- );
1886
- break;
1887
- case "smrt-review":
1888
- result = JSON.stringify(
1889
- await smrtReview(
1890
- args
1891
- ),
1892
- null,
1893
- 2
1894
- );
1895
- break;
1896
- case "build-architecture-context":
1897
- result = JSON.stringify(
1898
- await buildArchitectureContext(
1899
- args
1900
- ),
1901
- null,
1902
- 2
1903
- );
1904
- break;
1905
- case "build-domain-architecture-context":
1906
- result = JSON.stringify(
1907
- await buildArchitectureContext(
1908
- args
1909
- ),
1910
- null,
1911
- 2
1912
- );
1913
- break;
1914
- case "smrt-architecture":
1915
- result = JSON.stringify(
1916
- await smrtArchitecture(
1917
- args
1918
- ),
1919
- null,
1920
- 2
1921
- );
1922
- break;
1923
- case "list-agent-skills":
1924
- result = JSON.stringify({ skills: listAgentSkills() }, null, 2);
1925
- break;
1926
- case "get-agent-skill":
1927
- result = JSON.stringify(
1928
- await getAgentSkill(
1929
- args
1930
- ),
1931
- null,
1932
- 2
1933
- );
1934
- break;
1935
- default:
1936
- throw new Error(`Unknown tool: ${name}`);
1937
- }
1938
- return {
1939
- content: [
1940
- {
1941
- type: "text",
1942
- text: result
1943
- }
1944
- ]
1945
- };
1946
- } catch (error) {
1947
- const errorMessage = error instanceof Error ? error.message : "Unknown error";
1948
- console.error(`[${SERVER_NAME}] Error:`, error);
1949
- return {
1950
- content: [
1951
- {
1952
- type: "text",
1953
- text: `Error executing tool ${name}: ${errorMessage}`
1954
- }
1955
- ],
1956
- isError: true
1957
- };
1958
- }
1959
- });
1960
- const transport = new StdioServerTransport();
1961
- await server.connect(transport);
1962
- if (DEBUG) {
1963
- console.error(`[${SERVER_NAME}] Server connected via stdio`);
1964
- }
1965
- process.on("SIGINT", async () => {
1966
- if (DEBUG) {
1967
- console.error(`[${SERVER_NAME}] Shutting down...`);
1968
- }
1969
- await server.close();
1970
- process.exit(0);
1971
- });
1972
- process.on("SIGTERM", async () => {
1973
- if (DEBUG) {
1974
- console.error(`[${SERVER_NAME}] Shutting down...`);
1975
- }
1976
- await server.close();
1977
- process.exit(0);
1978
- });
1404
+ if (DEBUG) console.error(`[${SERVER_NAME}] Starting server v${SERVER_VERSION}`);
1405
+ const server = new Server({
1406
+ name: SERVER_NAME,
1407
+ version: SERVER_VERSION
1408
+ }, { capabilities: {
1409
+ prompts: {},
1410
+ resources: {},
1411
+ tools: {}
1412
+ } });
1413
+ server.setRequestHandler(ListToolsRequestSchema, async () => {
1414
+ if (DEBUG) console.error(`[${SERVER_NAME}] ListTools request`);
1415
+ return { tools: TOOLS };
1416
+ });
1417
+ server.setRequestHandler(ListPromptsRequestSchema, async () => {
1418
+ return { prompts: [
1419
+ {
1420
+ name: REVIEW_SKILL_NAME,
1421
+ title: "SMRT Code Review",
1422
+ description: "Harness-agnostic downstream SMRT review procedure that uses smrt-dev-mcp deterministic context and prompt bundles."
1423
+ },
1424
+ {
1425
+ name: DOMAIN_CODE_REVIEW_PROMPT,
1426
+ title: "Domain Code Review",
1427
+ description: "Model-ready domain-scoped SMRT code review prompt bundle.",
1428
+ arguments: [
1429
+ {
1430
+ name: "rootDir",
1431
+ description: "Project root directory. Defaults to server cwd.",
1432
+ required: false
1433
+ },
1434
+ {
1435
+ name: "changedFiles",
1436
+ description: "Changed file paths as newline-separated, comma-separated, or JSON array text.",
1437
+ required: false
1438
+ },
1439
+ {
1440
+ name: "focus",
1441
+ description: "Review focus text.",
1442
+ required: false
1443
+ },
1444
+ {
1445
+ name: "documentation",
1446
+ description: "Additional documentation or notes.",
1447
+ required: false
1448
+ },
1449
+ {
1450
+ name: "scope",
1451
+ description: "Knowledge scope: project, local, package, or sdk.",
1452
+ required: false
1453
+ },
1454
+ {
1455
+ name: "package",
1456
+ description: "Package name or short package selector.",
1457
+ required: false
1458
+ }
1459
+ ]
1460
+ },
1461
+ {
1462
+ name: DOMAIN_ARCHITECTURE_PROMPT,
1463
+ title: "Domain Architecture",
1464
+ description: "Model-ready domain-scoped SMRT architecture planning prompt bundle.",
1465
+ arguments: [
1466
+ {
1467
+ name: "rootDir",
1468
+ description: "Project root directory. Defaults to server cwd.",
1469
+ required: false
1470
+ },
1471
+ {
1472
+ name: "idea",
1473
+ description: "Architecture idea or product concept.",
1474
+ required: false
1475
+ },
1476
+ {
1477
+ name: "documentation",
1478
+ description: "Additional documentation or notes.",
1479
+ required: false
1480
+ },
1481
+ {
1482
+ name: "focus",
1483
+ description: "Planning focus text.",
1484
+ required: false
1485
+ },
1486
+ {
1487
+ name: "scope",
1488
+ description: "Knowledge scope: project, local, package, or sdk.",
1489
+ required: false
1490
+ },
1491
+ {
1492
+ name: "package",
1493
+ description: "Package name or short package selector.",
1494
+ required: false
1495
+ }
1496
+ ]
1497
+ }
1498
+ ] };
1499
+ });
1500
+ server.setRequestHandler(GetPromptRequestSchema, async (request) => {
1501
+ const { name } = request.params;
1502
+ if (name === REVIEW_SKILL_NAME) return {
1503
+ description: "Use this procedure when reviewing downstream SMRT projects.",
1504
+ messages: [{
1505
+ role: "user",
1506
+ content: {
1507
+ type: "text",
1508
+ text: renderAgentSkillMarkdown(REVIEW_SKILL_NAME)
1509
+ }
1510
+ }]
1511
+ };
1512
+ if (name === DOMAIN_CODE_REVIEW_PROMPT) return {
1513
+ description: "Review downstream SMRT code with domain knowledge.",
1514
+ messages: [{
1515
+ role: "user",
1516
+ content: {
1517
+ type: "text",
1518
+ text: (await buildReviewContext(reviewPromptArguments(request.params.arguments))).promptBundle.contextMarkdown
1519
+ }
1520
+ }]
1521
+ };
1522
+ if (name === DOMAIN_ARCHITECTURE_PROMPT) return {
1523
+ description: "Plan a downstream SMRT project with domain knowledge.",
1524
+ messages: [{
1525
+ role: "user",
1526
+ content: {
1527
+ type: "text",
1528
+ text: (await buildArchitectureContext(architecturePromptArguments(request.params.arguments))).promptBundle.contextMarkdown
1529
+ }
1530
+ }]
1531
+ };
1532
+ throw new McpError(ErrorCode.InvalidParams, `Unknown prompt: ${name}`);
1533
+ });
1534
+ server.setRequestHandler(ListResourcesRequestSchema, async () => {
1535
+ const index = await buildKnowledgeIndex();
1536
+ return { resources: [
1537
+ {
1538
+ uri: REVIEW_SKILL_URI,
1539
+ name: REVIEW_SKILL_NAME,
1540
+ title: "SMRT Code Review Skill",
1541
+ description: "Bundled Markdown skill for downstream SMRT code reviews.",
1542
+ mimeType: "text/markdown"
1543
+ },
1544
+ {
1545
+ uri: KNOWLEDGE_PROJECT_URI,
1546
+ name: "smrt-domain-knowledge-project",
1547
+ title: "SMRT Domain Knowledge Project Index",
1548
+ description: "Composed SMRT, downstream domain, and HappyVertical SDK knowledge index.",
1549
+ mimeType: "application/json"
1550
+ },
1551
+ ...index.packages.map((pkg) => ({
1552
+ uri: `${KNOWLEDGE_PACKAGE_PREFIX}${encodeURIComponent(pkg.name)}`,
1553
+ name: `smrt-domain-knowledge-${pkg.name}`,
1554
+ title: `SMRT Domain Knowledge: ${pkg.name}`,
1555
+ description: "Package-scoped SMRT domain knowledge, generated surfaces, and authored context.",
1556
+ mimeType: "application/json"
1557
+ }))
1558
+ ] };
1559
+ });
1560
+ server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
1561
+ const { uri } = request.params;
1562
+ if (uri === REVIEW_SKILL_URI) return { contents: [{
1563
+ uri,
1564
+ mimeType: "text/markdown",
1565
+ text: renderAgentSkillMarkdown(REVIEW_SKILL_NAME)
1566
+ }] };
1567
+ if (uri === KNOWLEDGE_PROJECT_URI) {
1568
+ const index = await buildKnowledgeIndex();
1569
+ return { contents: [{
1570
+ uri,
1571
+ mimeType: "application/json",
1572
+ text: JSON.stringify(sanitizeKnowledgeIndex(index), null, 2)
1573
+ }] };
1574
+ }
1575
+ if (uri.startsWith(KNOWLEDGE_PACKAGE_PREFIX)) {
1576
+ const packageName = decodeURIComponent(uri.slice(25));
1577
+ const pkg = (await buildKnowledgeIndex()).packages.find((item) => item.name === packageName);
1578
+ if (!pkg) throw new McpError(ErrorCode.InvalidParams, `Unknown knowledge package: ${packageName}`);
1579
+ return { contents: [{
1580
+ uri,
1581
+ mimeType: "application/json",
1582
+ text: JSON.stringify(sanitizeKnowledgePackage(pkg), null, 2)
1583
+ }] };
1584
+ }
1585
+ throw new McpError(ErrorCode.InvalidParams, `Unknown resource: ${uri}`);
1586
+ });
1587
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
1588
+ const { name, arguments: args } = request.params;
1589
+ if (DEBUG) {
1590
+ console.error(`[${SERVER_NAME}] CallTool: ${name}`);
1591
+ console.error(`[${SERVER_NAME}] Arguments:`, JSON.stringify(args, null, 2));
1592
+ }
1593
+ try {
1594
+ let result;
1595
+ switch (name) {
1596
+ case "generate-smrt-class":
1597
+ result = await generateSmrtClass(args);
1598
+ break;
1599
+ case "introspect-project":
1600
+ result = await introspectProject(args);
1601
+ break;
1602
+ case "review-smrt-project":
1603
+ result = await reviewSmrtProject(args);
1604
+ break;
1605
+ case "reflect-knowledge": {
1606
+ const index = await buildKnowledgeIndex(args);
1607
+ const freshness = await checkKnowledgeFreshnessFromIndex(index, args);
1608
+ result = JSON.stringify({
1609
+ rootDir: index.rootDir,
1610
+ packageCount: index.packages.length,
1611
+ smrtPackageCount: index.smrtPackages.length,
1612
+ sdkPackageCount: index.sdkPackages.length,
1613
+ relationshipsV2: index.relationshipsV2,
1614
+ freshness
1615
+ }, null, 2);
1616
+ break;
1617
+ }
1618
+ case "reflect-domain-knowledge": {
1619
+ const index = await buildKnowledgeIndex(args);
1620
+ const freshness = await checkKnowledgeFreshnessFromIndex(index, args);
1621
+ result = JSON.stringify({
1622
+ rootDir: index.rootDir,
1623
+ packageCount: index.packages.length,
1624
+ smrtPackageCount: index.smrtPackages.length,
1625
+ sdkPackageCount: index.sdkPackages.length,
1626
+ domainKnowledgePackageCount: index.packages.filter((pkg) => pkg.hasDomainKnowledge).length,
1627
+ missingDomainKnowledgePackages: index.packages.filter((pkg) => pkg.exportKeys.includes("./smrt-knowledge.json") && !pkg.hasDomainKnowledge).map((pkg) => pkg.name),
1628
+ relationshipsV2: index.relationshipsV2,
1629
+ freshness
1630
+ }, null, 2);
1631
+ break;
1632
+ }
1633
+ case "check-knowledge-freshness":
1634
+ result = JSON.stringify(await checkKnowledgeFreshness(args), null, 2);
1635
+ break;
1636
+ case "check-domain-knowledge":
1637
+ result = JSON.stringify(await checkKnowledgeFreshness(args), null, 2);
1638
+ break;
1639
+ case "build-review-context":
1640
+ result = JSON.stringify(await buildReviewContext(args), null, 2);
1641
+ break;
1642
+ case "build-domain-review-context":
1643
+ result = JSON.stringify(await buildReviewContext(args), null, 2);
1644
+ break;
1645
+ case "smrt-review":
1646
+ result = JSON.stringify(await smrtReview(args), null, 2);
1647
+ break;
1648
+ case "build-architecture-context":
1649
+ result = JSON.stringify(await buildArchitectureContext(args), null, 2);
1650
+ break;
1651
+ case "build-domain-architecture-context":
1652
+ result = JSON.stringify(await buildArchitectureContext(args), null, 2);
1653
+ break;
1654
+ case "smrt-architecture":
1655
+ result = JSON.stringify(await smrtArchitecture(args), null, 2);
1656
+ break;
1657
+ case "list-agent-skills":
1658
+ result = JSON.stringify({ skills: listAgentSkills() }, null, 2);
1659
+ break;
1660
+ case "get-agent-skill":
1661
+ result = JSON.stringify(await getAgentSkill(args), null, 2);
1662
+ break;
1663
+ default: throw new Error(`Unknown tool: ${name}`);
1664
+ }
1665
+ return { content: [{
1666
+ type: "text",
1667
+ text: result
1668
+ }] };
1669
+ } catch (error) {
1670
+ const errorMessage = error instanceof Error ? error.message : "Unknown error";
1671
+ console.error(`[${SERVER_NAME}] Error:`, error);
1672
+ return {
1673
+ content: [{
1674
+ type: "text",
1675
+ text: `Error executing tool ${name}: ${errorMessage}`
1676
+ }],
1677
+ isError: true
1678
+ };
1679
+ }
1680
+ });
1681
+ const transport = new StdioServerTransport();
1682
+ await server.connect(transport);
1683
+ if (DEBUG) console.error(`[${SERVER_NAME}] Server connected via stdio`);
1684
+ process.on("SIGINT", async () => {
1685
+ if (DEBUG) console.error(`[${SERVER_NAME}] Shutting down...`);
1686
+ await server.close();
1687
+ process.exit(0);
1688
+ });
1689
+ process.on("SIGTERM", async () => {
1690
+ if (DEBUG) console.error(`[${SERVER_NAME}] Shutting down...`);
1691
+ await server.close();
1692
+ process.exit(0);
1693
+ });
1979
1694
  }
1980
1695
  function isEntrypoint() {
1981
- const entry = process.argv[1];
1982
- if (!entry) return false;
1983
- try {
1984
- return realpathSync(fileURLToPath(import.meta.url)) === realpathSync(entry);
1985
- } catch {
1986
- return import.meta.url === pathToFileURL(entry).href;
1987
- }
1696
+ const entry = process.argv[1];
1697
+ if (!entry) return false;
1698
+ try {
1699
+ return realpathSync(fileURLToPath(import.meta.url)) === realpathSync(entry);
1700
+ } catch {
1701
+ return import.meta.url === pathToFileURL(entry).href;
1702
+ }
1988
1703
  }
1989
1704
  function readPackageVersion() {
1990
- try {
1991
- const packageRoot = dirname(fileURLToPath(import.meta.url));
1992
- const packageJsonPath = join(packageRoot, "..", "package.json");
1993
- const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf-8"));
1994
- return typeof packageJson.version === "string" ? packageJson.version : "0.0.0";
1995
- } catch {
1996
- return "0.0.0";
1997
- }
1705
+ try {
1706
+ const packageJsonPath = join(dirname(fileURLToPath(import.meta.url)), "..", "package.json");
1707
+ const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf-8"));
1708
+ return typeof packageJson.version === "string" ? packageJson.version : "0.0.0";
1709
+ } catch {
1710
+ return "0.0.0";
1711
+ }
1998
1712
  }
1999
1713
  function renderAgentSkillMarkdown(name) {
2000
- const skill = getAgentSkill({ name, includeReferences: true });
2001
- const references = skill.referenceFiles.map(
2002
- (file) => `## Reference: ${file.path}
2003
-
2004
- ${file.content.trim()}`
2005
- );
2006
- return [skill.skillMarkdown.trim(), ...references].join("\n\n");
1714
+ const skill = getAgentSkill({
1715
+ name,
1716
+ includeReferences: true
1717
+ });
1718
+ const references = skill.referenceFiles.map((file) => `## Reference: ${file.path}\n\n${file.content.trim()}`);
1719
+ return [skill.skillMarkdown.trim(), ...references].join("\n\n");
2007
1720
  }
2008
1721
  function reviewPromptArguments(args) {
2009
- return compactRecord({
2010
- rootDir: args?.rootDir,
2011
- changedFiles: parseStringList(args?.changedFiles),
2012
- focus: args?.focus,
2013
- documentation: args?.documentation,
2014
- scope: args?.scope,
2015
- package: args?.package
2016
- });
1722
+ return compactRecord({
1723
+ rootDir: args?.rootDir,
1724
+ changedFiles: parseStringList(args?.changedFiles),
1725
+ focus: args?.focus,
1726
+ documentation: args?.documentation,
1727
+ scope: args?.scope,
1728
+ package: args?.package
1729
+ });
2017
1730
  }
2018
1731
  function architecturePromptArguments(args) {
2019
- return compactRecord({
2020
- rootDir: args?.rootDir,
2021
- idea: args?.idea,
2022
- documentation: args?.documentation,
2023
- focus: args?.focus,
2024
- scope: args?.scope,
2025
- package: args?.package
2026
- });
1732
+ return compactRecord({
1733
+ rootDir: args?.rootDir,
1734
+ idea: args?.idea,
1735
+ documentation: args?.documentation,
1736
+ focus: args?.focus,
1737
+ scope: args?.scope,
1738
+ package: args?.package
1739
+ });
2027
1740
  }
2028
1741
  function parseStringList(value) {
2029
- if (!value?.trim()) return void 0;
2030
- try {
2031
- const parsed = JSON.parse(value);
2032
- if (Array.isArray(parsed)) {
2033
- return parsed.filter((item) => typeof item === "string");
2034
- }
2035
- } catch {
2036
- }
2037
- return value.split(/[\n,]/).map((item) => item.trim()).filter(Boolean);
1742
+ if (!value?.trim()) return void 0;
1743
+ try {
1744
+ const parsed = JSON.parse(value);
1745
+ if (Array.isArray(parsed)) return parsed.filter((item) => typeof item === "string");
1746
+ } catch {}
1747
+ return value.split(/[\n,]/).map((item) => item.trim()).filter(Boolean);
2038
1748
  }
2039
1749
  function compactRecord(value) {
2040
- return Object.fromEntries(
2041
- Object.entries(value).filter(([, entry]) => entry !== void 0)
2042
- );
1750
+ return Object.fromEntries(Object.entries(value).filter(([, entry]) => entry !== void 0));
2043
1751
  }
2044
1752
  function sanitizeKnowledgeIndex(index) {
2045
- const packages = index.packages.map((pkg) => sanitizeKnowledgePackage(pkg));
2046
- return {
2047
- ...index,
2048
- rootDir: ".",
2049
- packages,
2050
- smrtPackages: packages.filter((pkg) => pkg.kind === "smrt"),
2051
- sdkPackages: packages.filter((pkg) => pkg.kind === "sdk")
2052
- };
1753
+ const packages = index.packages.map((pkg) => sanitizeKnowledgePackage(pkg));
1754
+ return {
1755
+ ...index,
1756
+ rootDir: ".",
1757
+ packages,
1758
+ smrtPackages: packages.filter((pkg) => pkg.kind === "smrt"),
1759
+ sdkPackages: packages.filter((pkg) => pkg.kind === "sdk")
1760
+ };
2053
1761
  }
2054
1762
  function sanitizeKnowledgePackage(pkg) {
2055
- const { directory: _directory, objects, ...rest } = pkg;
2056
- return {
2057
- ...rest,
2058
- objects: objects.map((object) => ({
2059
- ...object,
2060
- filePath: sanitizePath(object.filePath)
2061
- }))
2062
- };
1763
+ const { directory: _directory, objects, ...rest } = pkg;
1764
+ return {
1765
+ ...rest,
1766
+ objects: objects.map((object) => ({
1767
+ ...object,
1768
+ filePath: sanitizePath(object.filePath)
1769
+ }))
1770
+ };
2063
1771
  }
2064
1772
  function sanitizePath(path) {
2065
- if (!path) return path;
2066
- if (path.startsWith("/") || /^[A-Za-z]:[\\/]/.test(path)) {
2067
- return "<absolute-path>";
2068
- }
2069
- return path;
2070
- }
2071
- if (isEntrypoint()) {
2072
- main().catch((error) => {
2073
- console.error(`[${SERVER_NAME}] Fatal error:`, error);
2074
- process.exit(1);
2075
- });
2076
- }
2077
-
1773
+ if (!path) return path;
1774
+ if (path.startsWith("/") || /^[A-Za-z]:[\\/]/.test(path)) return "<absolute-path>";
1775
+ return path;
1776
+ }
1777
+ if (isEntrypoint()) main().catch((error) => {
1778
+ console.error(`[${SERVER_NAME}] Fatal error:`, error);
1779
+ process.exit(1);
1780
+ });
1781
+ //#endregion
2078
1782
  export { SERVER_VERSION, TOOLS };
2079
- //# sourceMappingURL=index.js.map
1783
+
1784
+ //# sourceMappingURL=index.js.map