@maxisoft/instantcms-mcp 1.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (111) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +308 -0
  3. package/dist/__tests__/artifact-tool.test.js +196 -0
  4. package/dist/__tests__/db-tool.test.js +152 -0
  5. package/dist/__tests__/define-tool.test.js +106 -0
  6. package/dist/__tests__/find-tool.test.js +185 -0
  7. package/dist/__tests__/get-component-api.test.js +144 -0
  8. package/dist/__tests__/hardening.test.js +73 -0
  9. package/dist/__tests__/hooks-tool.test.js +173 -0
  10. package/dist/__tests__/mcp-integration-extra.test.js +166 -0
  11. package/dist/__tests__/mcp-integration.test.js +53 -0
  12. package/dist/__tests__/pagination.test.js +114 -0
  13. package/dist/__tests__/performance-baseline.test.js +56 -0
  14. package/dist/__tests__/project-patch.test.js +144 -0
  15. package/dist/__tests__/project-source.test.js +44 -0
  16. package/dist/__tests__/project-workflows-extra.test.js +165 -0
  17. package/dist/__tests__/project-workflows.test.js +51 -0
  18. package/dist/__tests__/scaffold-addon-roundtrip.test.js +287 -0
  19. package/dist/__tests__/serialization.test.js +170 -0
  20. package/dist/__tests__/source-knowledge-parsers.test.js +42 -0
  21. package/dist/__tests__/template-development.test.js +78 -0
  22. package/dist/__tests__/template-productivity.test.js +87 -0
  23. package/dist/__tests__/tools.test.js +2143 -0
  24. package/dist/data/components.js +2818 -0
  25. package/dist/data/controllers-map.js +7571 -0
  26. package/dist/data/core-api.js +9356 -0
  27. package/dist/data/database-schema.js +4555 -0
  28. package/dist/data/events-map.js +1339 -0
  29. package/dist/data/fields-map.js +3692 -0
  30. package/dist/data/hooks.js +2257 -0
  31. package/dist/data/js-api.js +123 -0
  32. package/dist/data/libs-api.js +264 -0
  33. package/dist/data/routes-map.js +203 -0
  34. package/dist/data/schemas-validation.js +255 -0
  35. package/dist/data/schemas.js +1404 -0
  36. package/dist/data/traits-map.js +1004 -0
  37. package/dist/data/version-profiles.js +41 -0
  38. package/dist/data/widgets-map.js +131 -0
  39. package/dist/data/wysiwyg-map.js +486 -0
  40. package/dist/generated/components-source.js +14748 -0
  41. package/dist/generated/hooks-source.js +3399 -0
  42. package/dist/generated/knowledge-meta.js +101 -0
  43. package/dist/index.js +13 -0
  44. package/dist/registry/database-tools.js +112 -0
  45. package/dist/registry/extension-tools.js +473 -0
  46. package/dist/registry/generator-tools.js +506 -0
  47. package/dist/registry/knowledge-tools.js +441 -0
  48. package/dist/registry/language-tools.js +99 -0
  49. package/dist/registry/meta-tools.js +152 -0
  50. package/dist/registry/project-tools.js +48 -0
  51. package/dist/registry/resources.js +98 -0
  52. package/dist/registry/source-tools.js +211 -0
  53. package/dist/registry/template-development-tools.js +79 -0
  54. package/dist/server.js +32 -0
  55. package/dist/tools/addon-tool.js +1437 -0
  56. package/dist/tools/admin-partial-tool.js +498 -0
  57. package/dist/tools/api-tool.js +294 -0
  58. package/dist/tools/artifact-tool.js +95 -0
  59. package/dist/tools/cache-tool.js +360 -0
  60. package/dist/tools/component-tool.js +415 -0
  61. package/dist/tools/controllers-tool.js +125 -0
  62. package/dist/tools/cron-tool.js +197 -0
  63. package/dist/tools/crud-tool.js +659 -0
  64. package/dist/tools/db-tool.js +198 -0
  65. package/dist/tools/email-tool.js +222 -0
  66. package/dist/tools/external-api-tool.js +596 -0
  67. package/dist/tools/filter-tool.js +341 -0
  68. package/dist/tools/form-tool.js +275 -0
  69. package/dist/tools/grid-tool.js +189 -0
  70. package/dist/tools/hooks-tool.js +104 -0
  71. package/dist/tools/import-export-tool.js +548 -0
  72. package/dist/tools/lang-tool.js +251 -0
  73. package/dist/tools/layout-override-tool.js +125 -0
  74. package/dist/tools/layout-tool.js +548 -0
  75. package/dist/tools/maria-tool.js +127 -0
  76. package/dist/tools/mariadb.js +201 -0
  77. package/dist/tools/migration-tool.js +329 -0
  78. package/dist/tools/oauth-tool.js +520 -0
  79. package/dist/tools/parser/components-parser.js +76 -0
  80. package/dist/tools/parser/controllers-parser.js +313 -0
  81. package/dist/tools/parser/core-parser.js +294 -0
  82. package/dist/tools/parser/coverage-generator.js +424 -0
  83. package/dist/tools/parser/events-parser.js +127 -0
  84. package/dist/tools/parser/fields-parser.js +382 -0
  85. package/dist/tools/parser/hooks-parser.js +106 -0
  86. package/dist/tools/parser/sql-parser.js +197 -0
  87. package/dist/tools/parser/traits-parser.js +161 -0
  88. package/dist/tools/parser/widgets-parser.js +150 -0
  89. package/dist/tools/permission-tool.js +348 -0
  90. package/dist/tools/project-patch-tool.js +73 -0
  91. package/dist/tools/project-source-tool.js +188 -0
  92. package/dist/tools/project-workflow-tool.js +186 -0
  93. package/dist/tools/requirement-tool.js +212 -0
  94. package/dist/tools/scaffold-tool.js +815 -0
  95. package/dist/tools/seo-tool.js +412 -0
  96. package/dist/tools/source-tool.js +155 -0
  97. package/dist/tools/template-development-tool.js +271 -0
  98. package/dist/tools/template-overrides-tool.js +294 -0
  99. package/dist/tools/template-productivity-tool.js +319 -0
  100. package/dist/tools/template-tool.js +665 -0
  101. package/dist/tools/test-tool.js +183 -0
  102. package/dist/tools/webhook-tool.js +427 -0
  103. package/dist/tools/widget-tool.js +331 -0
  104. package/dist/tools/wysiwyg-tool.js +137 -0
  105. package/dist/types/scaffold.js +68 -0
  106. package/dist/utils/define-tool.js +37 -0
  107. package/dist/utils/find-tool.js +97 -0
  108. package/dist/utils/mcp-result.js +18 -0
  109. package/dist/utils/pagination.js +27 -0
  110. package/dist/utils/serialization.js +27 -0
  111. package/package.json +94 -0
@@ -0,0 +1,412 @@
1
+ "use strict";
2
+ /**
3
+ * @fileoverview SEO scaffolding tool for InstantCMS
4
+ * Generates SEO meta tags, sitemap, Open Graph, and Schema.org markup
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.seoToolSchema = exports.SeoFieldTypeEnum = void 0;
8
+ exports.scaffoldSeo = scaffoldSeo;
9
+ const zod_1 = require("zod");
10
+ const scaffold_1 = require("../types/scaffold");
11
+ /**
12
+ * Available SEO field types
13
+ */
14
+ exports.SeoFieldTypeEnum = zod_1.z.enum([
15
+ 'title',
16
+ 'description',
17
+ 'keywords',
18
+ 'og_image',
19
+ 'canonical',
20
+ 'robots',
21
+ ]);
22
+ /**
23
+ * Generates SEO configuration class
24
+ */
25
+ function generateSeoConfig(name, Name, fields, options) {
26
+ const titleField = fields.find(f => f.type === 'title')?.field || 'title';
27
+ const descField = fields.find(f => f.type === 'description')?.field || 'description';
28
+ const keywordsField = fields.find(f => f.type === 'keywords')?.field || 'keywords';
29
+ return `<?php
30
+ // InstantCMS 2. ${name}/seo.php
31
+
32
+ class ${Name}Seo {
33
+ private static $instance = null;
34
+ private $config = [];
35
+
36
+ private function __construct() {
37
+ $this->config = include __DIR__ . '/seo_config.php';
38
+ }
39
+
40
+ public static function getInstance() {
41
+ if (self::$instance === null) {
42
+ self::$instance = new self();
43
+ }
44
+ return self::$instance;
45
+ }
46
+
47
+ public function getConfig() {
48
+ return $this->config;
49
+ }
50
+
51
+ public function getTitle($item = null) {
52
+ $template = $this->config['${titleField}']['value'] ?? '{item.title}';
53
+ return $this->replaceVariables($template, $item);
54
+ }
55
+
56
+ public function getDescription($item = null) {
57
+ $template = $this->config['${descField}']['value'] ?? '';
58
+ return $this->replaceVariables($template, $item);
59
+ }
60
+
61
+ public function getKeywords($item = null) {
62
+ $template = $this->config['${keywordsField}']['value'] ?? '';
63
+ return $this->replaceVariables($template, $item);
64
+ }
65
+
66
+ private function replaceVariables($template, $item) {
67
+ if (!$item) {
68
+ return $template;
69
+ }
70
+
71
+ $item = (array)$item;
72
+ $result = str_replace('{site.name}', cmsConfig::get('sitename'), $template);
73
+ $result = str_replace('{site.url}', cmsConfig::get('root_url'), $result);
74
+
75
+ foreach ($item as $key => $value) {
76
+ if (is_string($value) || is_numeric($value)) {
77
+ $result = str_replace('{item.' . $key . '}', $value, $result);
78
+ }
79
+ }
80
+
81
+ return $result;
82
+ }
83
+
84
+ public function getMetaTags($item = null) {
85
+ $tags = [];
86
+
87
+ if (${options.auto_generation}) {
88
+ $title = $this->getTitle($item);
89
+ if ($title) {
90
+ $tags['title'] = $title;
91
+ }
92
+
93
+ $description = $this->getDescription($item);
94
+ if ($description) {
95
+ $tags['description'] = $description;
96
+ }
97
+
98
+ $keywords = $this->getKeywords($item);
99
+ if ($keywords) {
100
+ $tags['keywords'] = $keywords;
101
+ }
102
+ }
103
+
104
+ return $tags;
105
+ }
106
+
107
+ public function getOgTags($item = null) {
108
+ if (!${options.use_og_tags}) {
109
+ return [];
110
+ }
111
+
112
+ $tags = [
113
+ 'og:title' => $this->getTitle($item),
114
+ 'og:description' => $this->getDescription($item),
115
+ 'og:type' => 'article',
116
+ 'og:url' => cmsConfig::get('root_url') . '/' . ($item['slug'] ?? ''),
117
+ ];
118
+
119
+ if (!empty($item['og_image'])) {
120
+ $tags['og:image'] = $item['og_image'];
121
+ } elseif (!empty($item['image'])) {
122
+ $tags['og:image'] = $item['image'];
123
+ }
124
+
125
+ return array_filter($tags);
126
+ }
127
+
128
+ public function getRobots($item = null) {
129
+ if (!empty($this->config['robots']['value'])) {
130
+ return $this->config['robots']['value'];
131
+ }
132
+
133
+ if (!empty($item['is_draft']) || !empty($item['is_private'])) {
134
+ return 'noindex, nofollow';
135
+ }
136
+
137
+ return 'index, follow';
138
+ }
139
+ }`;
140
+ }
141
+ /**
142
+ * Generates SEO hook handlers
143
+ */
144
+ function generateSeoHooks(name, Name, _fields, options) {
145
+ return `<?php
146
+ // InstantCMS 2. system/hooks/${name}/seo.hooks.php
147
+
148
+ class on${Name}SeoHook {
149
+ public function onBeforeRender($controller, $item = null) {
150
+ if (!${options.auto_generation}) {
151
+ return;
152
+ }
153
+
154
+ $seo = ${Name}Seo::getInstance();
155
+ $meta = $seo->getMetaTags($item);
156
+
157
+ if (!empty($meta['title'])) {
158
+ $controller->setPageTitle($meta['title']);
159
+ }
160
+
161
+ if (!empty($meta['description'])) {
162
+ $controller->setPageDescription($meta['description']);
163
+ }
164
+
165
+ if (!empty($meta['keywords'])) {
166
+ $controller->setPageKeywords($meta['keywords']);
167
+ }
168
+
169
+ if (${options.use_og_tags}) {
170
+ $og = $seo->getOgTags($item);
171
+ foreach ($og as $property => $content) {
172
+ $controller->addHeadJsVar('icms[og][' . $property . ']', $content);
173
+ }
174
+ }
175
+
176
+ $robots = $seo->getRobots($item);
177
+ $controller->setPageRobots($robots);
178
+ }
179
+
180
+ public function onAfterSave($item) {
181
+ if (!empty($item['slug'])) {
182
+ $this->updateSitemap($item);
183
+ }
184
+ }
185
+
186
+ public function onAfterDelete($item) {
187
+ $this->removeFromSitemap($item);
188
+ }
189
+
190
+ private function updateSitemap($item) {
191
+ if (!${options.use_sitemap}) {
192
+ return;
193
+ }
194
+
195
+ $model = cmsModel::getInstance();
196
+ $model->delete('sitemap', ['entity' => '${name}', 'entity_id' => $item['id']], true);
197
+
198
+ $model->insert('sitemap', [
199
+ 'entity' => '${name}',
200
+ 'entity_id' => $item['id'],
201
+ 'slug' => $item['slug'],
202
+ 'last_modified' => date('Y-m-d H:i:s'),
203
+ 'change_freq' => 'weekly',
204
+ 'priority' => '0.7',
205
+ ]);
206
+ }
207
+
208
+ private function removeFromSitemap($item) {
209
+ if (!${options.use_sitemap}) {
210
+ return;
211
+ }
212
+
213
+ $model = cmsModel::getInstance();
214
+ $model->delete('sitemap', ['entity' => '${name}', 'entity_id' => $item['id']], true);
215
+ }
216
+ }`;
217
+ }
218
+ /**
219
+ * Generates sitemap class
220
+ */
221
+ function generateSitemap(name, Name, _fields) {
222
+ return `<?php
223
+ // InstantCMS 2. ${name}/sitemap.php
224
+
225
+ class ${Name}Sitemap {
226
+ public function getItems($page = 1, $per_page = 100) {
227
+ $model = cmsModel::getInstance();
228
+
229
+ return $model->get('${name}', function ($item) {
230
+ return [
231
+ 'loc' => cmsConfig::get('root_url') . '/' . $item['slug'],
232
+ 'lastmod' => !empty($item['date_pub']) ? date('Y-m-d', strtotime($item['date_pub'])) : date('Y-m-d'),
233
+ 'changefreq' => !empty($item['changefreq']) ? $item['changefreq'] : 'weekly',
234
+ 'priority' => !empty($item['priority']) ? $item['priority'] : '0.7',
235
+ ];
236
+ }, [
237
+ 'is_deleted' => false,
238
+ ], 'id DESC', $page, $per_page);
239
+ }
240
+
241
+ public function getTotal() {
242
+ $model = cmsModel::getInstance();
243
+ return $model->getCount('${name}', function ($model) {
244
+ $model->filterIsNull('is_deleted');
245
+ });
246
+ }
247
+
248
+ public function generateXml($items) {
249
+ $xml = '<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">';
250
+
251
+ foreach ($items as $item) {
252
+ $xml .= '<url>';
253
+ $xml .= '<loc>' . htmlspecialchars($item['loc']) . '</loc>';
254
+ $xml .= '<lastmod>' . $item['lastmod'] . '</lastmod>';
255
+ $xml .= '<changefreq>' . $item['changefreq'] . '</changefreq>';
256
+ $xml .= '<priority>' . $item['priority'] . '</priority>';
257
+ $xml .= '</url>';
258
+ }
259
+
260
+ $xml .= '</urlset>';
261
+
262
+ return $xml;
263
+ }
264
+ }`;
265
+ }
266
+ /**
267
+ * Generates Schema.org JSON-LD class
268
+ */
269
+ function generateSchemaOrg(name, Name, _fields) {
270
+ return `<?php
271
+ // InstantCMS 2. ${name}/schema.php
272
+
273
+ class ${Name}SchemaOrg {
274
+ public static function getArticleSchema($item) {
275
+ $schema = [
276
+ '@context' => 'https://schema.org',
277
+ '@type' => 'Article',
278
+ 'headline' => $item['title'] ?? '',
279
+ 'description' => $item['description'] ?? '',
280
+ 'image' => $item['image'] ?? '',
281
+ 'datePublished' => $item['date_pub'] ?? '',
282
+ 'dateModified' => $item['updated_at'] ?? $item['date_pub'] ?? '',
283
+ 'author' => [
284
+ '@type' => 'Person',
285
+ 'name' => $item['user_nickname'] ?? $item['author_name'] ?? '',
286
+ ],
287
+ ];
288
+
289
+ if (!empty($item['slug'])) {
290
+ $schema['url'] = cmsConfig::get('root_url') . '/' . $item['slug'];
291
+ }
292
+
293
+ return $schema;
294
+ }
295
+
296
+ public static function toJsonLd($schema) {
297
+ return '<script type="application/ld+json">' . json_encode($schema) . '</script>';
298
+ }
299
+ }`;
300
+ }
301
+ /**
302
+ * Generates SEO configuration file
303
+ */
304
+ function generateSeoConfigFile(fields) {
305
+ const fieldEntries = fields
306
+ .map(f => {
307
+ const value = f.value ? `'${f.value}'` : 'null';
308
+ return ` '${f.field}' => ['type' => '${f.type}', 'value' => ${value}]`;
309
+ })
310
+ .join(',\n');
311
+ return `<?php
312
+ // InstantCMS 2. SEO configuration
313
+
314
+ return [
315
+ ${fieldEntries}
316
+ ];`;
317
+ }
318
+ /**
319
+ * Generates a complete SEO system for an InstantCMS addon
320
+ *
321
+ * @param opts - Configuration options for the SEO system
322
+ * @returns Object containing generated files and metadata
323
+ *
324
+ * @example
325
+ * ```typescript
326
+ * const result = scaffoldSeo({
327
+ * addon_name: 'blog',
328
+ * fields: [
329
+ * { field: 'title', type: 'title', value: '{item.title} | {site.name}' },
330
+ * { field: 'description', type: 'description', value: '{item.description}' }
331
+ * ],
332
+ * options: { use_sitemap: true, use_og_tags: true, use_schema_org: true }
333
+ * });
334
+ * ```
335
+ */
336
+ function scaffoldSeo(opts) {
337
+ const { lowercase, UpperCamelCase } = (0, scaffold_1.normalizeAddonName)(opts.addon_name);
338
+ const files = {};
339
+ const fields = opts.fields || [
340
+ { field: 'title', type: 'title', value: '{item.title} | {site.name}' },
341
+ { field: 'description', type: 'description', value: '{item.description}' },
342
+ { field: 'keywords', type: 'keywords' },
343
+ ];
344
+ const options = {
345
+ auto_generation: opts.options?.auto_generation ?? true,
346
+ use_sitemap: opts.options?.use_sitemap ?? true,
347
+ use_og_tags: opts.options?.use_og_tags ?? true,
348
+ use_schema_org: opts.options?.use_schema_org ?? false,
349
+ };
350
+ files[`${lowercase}/seo.php`] = generateSeoConfig(lowercase, UpperCamelCase, fields, options);
351
+ files[`${lowercase}/seo_config.php`] = generateSeoConfigFile(fields);
352
+ files[`system/hooks/${lowercase}/seo.hooks.php`] = generateSeoHooks(lowercase, UpperCamelCase, fields, options);
353
+ if (options.use_sitemap) {
354
+ files[`${lowercase}/sitemap.php`] = generateSitemap(lowercase, UpperCamelCase, fields);
355
+ }
356
+ if (options.use_schema_org) {
357
+ files[`${lowercase}/schema.php`] = generateSchemaOrg(lowercase, UpperCamelCase, fields);
358
+ }
359
+ return {
360
+ addon_name: lowercase,
361
+ files,
362
+ fields_count: fields.length,
363
+ options,
364
+ seo_fields: fields.map(f => ({
365
+ field: f.field,
366
+ type: f.type,
367
+ value: f.value,
368
+ })),
369
+ };
370
+ }
371
+ exports.seoToolSchema = {
372
+ name: 'scaffold_seo',
373
+ description: 'Генерация SEO мета-тегов, sitemap и Open Graph разметки для InstantCMS',
374
+ inputSchema: {
375
+ type: 'object',
376
+ properties: {
377
+ addon_name: { type: 'string', description: 'Имя дополнения' },
378
+ fields: {
379
+ type: 'array',
380
+ items: {
381
+ type: 'object',
382
+ properties: {
383
+ field: { type: 'string', description: 'Имя поля' },
384
+ type: {
385
+ type: 'string',
386
+ enum: ['title', 'description', 'keywords', 'og_image', 'canonical', 'robots'],
387
+ description: 'Тип поля',
388
+ },
389
+ value: { type: 'string', description: 'Шаблон значения' },
390
+ },
391
+ },
392
+ description: 'Поля SEO',
393
+ },
394
+ options: {
395
+ type: 'object',
396
+ properties: {
397
+ auto_generation: { type: 'boolean', description: 'Автогенерация мета-тегов' },
398
+ use_sitemap: { type: 'boolean', description: 'Использовать sitemap' },
399
+ use_og_tags: { type: 'boolean', description: 'Open Graph теги' },
400
+ use_schema_org: { type: 'boolean', description: 'Schema.org разметка' },
401
+ },
402
+ },
403
+ },
404
+ required: ['addon_name'],
405
+ },
406
+ inputExamples: [
407
+ {
408
+ addon_name: 'blog',
409
+ options: { auto_generation: true, use_sitemap: true, use_og_tags: true },
410
+ },
411
+ ],
412
+ };
@@ -0,0 +1,155 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.listWidgets = listWidgets;
4
+ exports.getWidgetInfo = getWidgetInfo;
5
+ exports.listTraits = listTraits;
6
+ exports.getTraitInfo = getTraitInfo;
7
+ exports.listFields = listFields;
8
+ exports.getFieldInfo = getFieldInfo;
9
+ exports.listRoutes = listRoutes;
10
+ const widgets_map_js_1 = require("../data/widgets-map.js");
11
+ const traits_map_js_1 = require("../data/traits-map.js");
12
+ const fields_map_js_1 = require("../data/fields-map.js");
13
+ const routes_map_js_1 = require("../data/routes-map.js");
14
+ function listWidgets(controller) {
15
+ let widgets = widgets_map_js_1.widgetsMap.widgets;
16
+ if (controller) {
17
+ widgets = (0, widgets_map_js_1.getWidgetsByController)(controller);
18
+ }
19
+ return {
20
+ total: widgets.length,
21
+ widgets: widgets.map(w => ({
22
+ name: w.name,
23
+ className: w.className,
24
+ controller: w.controller,
25
+ hasOptionsForm: w.hasOptionsForm,
26
+ description: w.description,
27
+ })),
28
+ };
29
+ }
30
+ function getWidgetInfo(name) {
31
+ const widget = (0, widgets_map_js_1.getWidget)(name);
32
+ if (!widget) {
33
+ return {
34
+ error: `Widget "${name}" not found`,
35
+ available: widgets_map_js_1.widgetsMap.widgets.map(w => w.name),
36
+ };
37
+ }
38
+ return {
39
+ name: widget.name,
40
+ className: widget.className,
41
+ controller: widget.controller,
42
+ filePath: widget.filePath,
43
+ hasOptionsForm: widget.hasOptionsForm,
44
+ optionsFormPath: widget.optionsFormPath,
45
+ description: widget.description,
46
+ };
47
+ }
48
+ function listTraits(namespace) {
49
+ let traits = traits_map_js_1.traitsMap.traits;
50
+ if (namespace) {
51
+ traits = (0, traits_map_js_1.getTraitsByNamespace)(namespace);
52
+ }
53
+ const byNamespace = {};
54
+ for (const trait of traits) {
55
+ if (!byNamespace[trait.namespace]) {
56
+ byNamespace[trait.namespace] = [];
57
+ }
58
+ byNamespace[trait.namespace].push({
59
+ name: trait.name,
60
+ methodsCount: trait.methods.length,
61
+ description: trait.description,
62
+ });
63
+ }
64
+ return {
65
+ total: traits.length,
66
+ methodCount: traits.reduce((sum, t) => sum + t.methods.length, 0),
67
+ namespaces: Object.keys(byNamespace).length,
68
+ byNamespace,
69
+ traits: traits.map(t => ({
70
+ name: t.name,
71
+ namespace: t.namespace,
72
+ methodsCount: t.methods.length,
73
+ description: t.description,
74
+ })),
75
+ };
76
+ }
77
+ function getTraitInfo(name) {
78
+ const trait = (0, traits_map_js_1.getTrait)(name);
79
+ if (!trait) {
80
+ return {
81
+ error: `Trait "${name}" not found`,
82
+ available: traits_map_js_1.traitsMap.traits.map(t => t.name),
83
+ };
84
+ }
85
+ return {
86
+ name: trait.name,
87
+ namespace: trait.namespace,
88
+ filePath: trait.filePath,
89
+ description: trait.description,
90
+ methods: trait.methods.map(m => ({
91
+ name: m.name,
92
+ visibility: m.visibility,
93
+ params: m.params,
94
+ paramCount: m.params.length,
95
+ })),
96
+ };
97
+ }
98
+ function listFields() {
99
+ return {
100
+ total: fields_map_js_1.fieldsMap.fieldCount,
101
+ systemFieldsCount: fields_map_js_1.fieldsMap.systemFields.length,
102
+ fields: fields_map_js_1.fieldsMap.fields.map(f => ({
103
+ name: f.name,
104
+ className: f.className,
105
+ isSystem: f.isSystem,
106
+ hasOptions: f.hasOptions,
107
+ description: f.description,
108
+ })),
109
+ };
110
+ }
111
+ function getFieldInfo(name) {
112
+ const field = (0, fields_map_js_1.getField)(name);
113
+ if (!field) {
114
+ return {
115
+ error: `Field "${name}" not found`,
116
+ available: fields_map_js_1.fieldsMap.fields.map(f => f.name),
117
+ };
118
+ }
119
+ return {
120
+ name: field.name,
121
+ className: field.className,
122
+ filePath: field.filePath,
123
+ isSystem: field.isSystem,
124
+ hasOptions: field.hasOptions,
125
+ options: field.options,
126
+ description: field.description,
127
+ };
128
+ }
129
+ function listRoutes(controller) {
130
+ if (controller) {
131
+ const routes = (0, routes_map_js_1.getRoutesByController)(controller);
132
+ if (!routes) {
133
+ return {
134
+ error: `Controller "${controller}" has no routes`,
135
+ available: routes_map_js_1.routesMap.controllers.map(c => c.name),
136
+ };
137
+ }
138
+ return {
139
+ total: routes.routes.length,
140
+ controller: routes.name,
141
+ functionName: routes.functionName,
142
+ filePath: routes.filePath,
143
+ routes: routes.routes,
144
+ };
145
+ }
146
+ return {
147
+ total: routes_map_js_1.routesMap.routeCount,
148
+ controllersCount: routes_map_js_1.routesMap.controllers.length,
149
+ controllers: routes_map_js_1.routesMap.controllers.map(c => ({
150
+ name: c.name,
151
+ functionName: c.functionName,
152
+ routeCount: c.routes.length,
153
+ })),
154
+ };
155
+ }