@magek/mcp-server 0.0.8

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 (52) hide show
  1. package/README.md +42 -0
  2. package/dist/index.d.ts +2 -0
  3. package/dist/index.js +25 -0
  4. package/dist/prompts/cqrs-flow.d.ts +15 -0
  5. package/dist/prompts/cqrs-flow.js +252 -0
  6. package/dist/prompts/troubleshooting.d.ts +15 -0
  7. package/dist/prompts/troubleshooting.js +239 -0
  8. package/dist/resources/cli-reference.d.ts +13 -0
  9. package/dist/resources/cli-reference.js +193 -0
  10. package/dist/resources/documentation.d.ts +18 -0
  11. package/dist/resources/documentation.js +62 -0
  12. package/dist/server.d.ts +5 -0
  13. package/dist/server.js +127 -0
  14. package/dist/utils/docs-loader.d.ts +19 -0
  15. package/dist/utils/docs-loader.js +111 -0
  16. package/docs/advanced/custom-templates.md +96 -0
  17. package/docs/advanced/data-migrations.md +181 -0
  18. package/docs/advanced/environment-configuration.md +74 -0
  19. package/docs/advanced/framework-packages.md +17 -0
  20. package/docs/advanced/health/sensor-health.md +389 -0
  21. package/docs/advanced/instrumentation.md +135 -0
  22. package/docs/advanced/register.md +119 -0
  23. package/docs/advanced/sensor.md +10 -0
  24. package/docs/advanced/testing.md +96 -0
  25. package/docs/advanced/touch-entities.md +45 -0
  26. package/docs/architecture/command.md +367 -0
  27. package/docs/architecture/entity.md +214 -0
  28. package/docs/architecture/event-driven.md +30 -0
  29. package/docs/architecture/event-handler.md +108 -0
  30. package/docs/architecture/event.md +145 -0
  31. package/docs/architecture/notifications.md +54 -0
  32. package/docs/architecture/queries.md +207 -0
  33. package/docs/architecture/read-model.md +507 -0
  34. package/docs/contributing.md +349 -0
  35. package/docs/docs-index.json +200 -0
  36. package/docs/features/error-handling.md +204 -0
  37. package/docs/features/event-stream.md +35 -0
  38. package/docs/features/logging.md +81 -0
  39. package/docs/features/schedule-actions.md +44 -0
  40. package/docs/getting-started/ai-coding-assistants.md +181 -0
  41. package/docs/getting-started/coding.md +543 -0
  42. package/docs/getting-started/installation.md +143 -0
  43. package/docs/graphql.md +1213 -0
  44. package/docs/index.md +62 -0
  45. package/docs/introduction.md +58 -0
  46. package/docs/magek-arch.png +0 -0
  47. package/docs/magek-cli.md +67 -0
  48. package/docs/magek-logo.svg +1 -0
  49. package/docs/security/authentication.md +189 -0
  50. package/docs/security/authorization.md +242 -0
  51. package/docs/security/security.md +16 -0
  52. package/package.json +46 -0
@@ -0,0 +1,193 @@
1
+ export const CLI_REFERENCE_URI = 'magek://cli/reference';
2
+ export const CLI_REFERENCE_CONTENT = `# Magek CLI Commands
3
+
4
+ Comprehensive reference for all Magek CLI scaffolding commands. These commands generate TypeScript files following Magek conventions.
5
+
6
+ ## Scaffolding Commands
7
+
8
+ ### Create a Command
9
+
10
+ Commands represent user intent that triggers events. They are the entry point for all write operations.
11
+
12
+ \`\`\`bash
13
+ npx magek new:command <Name> --fields field1:type1 field2:type2
14
+ \`\`\`
15
+
16
+ **Example:**
17
+ \`\`\`bash
18
+ npx magek new:command CreateProduct --fields sku:string price:number description:string
19
+ \`\`\`
20
+
21
+ **Available field types:** string, number, boolean, UUID, Date
22
+
23
+ ---
24
+
25
+ ### Create an Event
26
+
27
+ Events are immutable facts that happened in the system. They are the source of truth.
28
+
29
+ \`\`\`bash
30
+ npx magek new:event <Name> --fields field1:type1 field2:type2
31
+ \`\`\`
32
+
33
+ **Example:**
34
+ \`\`\`bash
35
+ npx magek new:event ProductCreated --fields productId:UUID sku:string price:number
36
+ \`\`\`
37
+
38
+ ---
39
+
40
+ ### Create an Entity
41
+
42
+ Entities (aggregate roots) maintain state from events. Use --reduces to specify which events the entity handles.
43
+
44
+ \`\`\`bash
45
+ npx magek new:entity <Name> --fields field1:type1 field2:type2 --reduces EventName
46
+ \`\`\`
47
+
48
+ **Example:**
49
+ \`\`\`bash
50
+ npx magek new:entity Product --fields sku:string price:number inStock:boolean --reduces ProductCreated
51
+ \`\`\`
52
+
53
+ ---
54
+
55
+ ### Create a Read Model
56
+
57
+ Read models are query-optimized projections. Use --projects to specify which entity to project.
58
+
59
+ \`\`\`bash
60
+ npx magek new:read-model <Name> --fields field1:type1 field2:type2 --projects Entity:joinKey
61
+ \`\`\`
62
+
63
+ **Example:**
64
+ \`\`\`bash
65
+ npx magek new:read-model ProductReadModel --fields sku:string price:number name:string --projects Product:id
66
+ \`\`\`
67
+
68
+ ---
69
+
70
+ ### Create an Event Handler
71
+
72
+ Event handlers react to events and perform side effects (send emails, call external APIs, etc.).
73
+
74
+ \`\`\`bash
75
+ npx magek new:event-handler <Name> --event EventName
76
+ \`\`\`
77
+
78
+ **Example:**
79
+ \`\`\`bash
80
+ npx magek new:event-handler SendWelcomeEmail --event UserRegistered
81
+ \`\`\`
82
+
83
+ ---
84
+
85
+ ### Create a Scheduled Command
86
+
87
+ Scheduled commands run on a cron schedule for recurring tasks.
88
+
89
+ \`\`\`bash
90
+ npx magek new:scheduled-command <Name>
91
+ \`\`\`
92
+
93
+ **Example:**
94
+ \`\`\`bash
95
+ npx magek new:scheduled-command CleanupExpiredSessions
96
+ \`\`\`
97
+
98
+ ---
99
+
100
+ ### Create a Query
101
+
102
+ Queries retrieve data from read models without side effects.
103
+
104
+ \`\`\`bash
105
+ npx magek new:query <Name> --fields field1:type1
106
+ \`\`\`
107
+
108
+ **Example:**
109
+ \`\`\`bash
110
+ npx magek new:query GetProductBySku --fields sku:string
111
+ \`\`\`
112
+
113
+ ---
114
+
115
+ ### Create a Type
116
+
117
+ Custom types for domain modeling.
118
+
119
+ \`\`\`bash
120
+ npx magek new:type <Name> --fields field1:type1 field2:type2
121
+ \`\`\`
122
+
123
+ **Example:**
124
+ \`\`\`bash
125
+ npx magek new:type Money --fields amount:number currency:string
126
+ \`\`\`
127
+
128
+ ---
129
+
130
+ ## Project Initialization
131
+
132
+ ### Create a New Magek Project
133
+
134
+ \`\`\`bash
135
+ npx create-magek my-app
136
+ \`\`\`
137
+
138
+ Or with pnpm:
139
+ \`\`\`bash
140
+ pnpm create magek my-app
141
+ \`\`\`
142
+
143
+ ---
144
+
145
+ ## Development Server
146
+
147
+ ### Start the Development Server
148
+
149
+ \`\`\`bash
150
+ npx magek start
151
+ \`\`\`
152
+
153
+ This starts:
154
+ - GraphQL API at http://localhost:3000/graphql
155
+ - GraphQL Playground for testing queries
156
+
157
+ ---
158
+
159
+ ## Field Type Reference
160
+
161
+ | Type | Description | Example |
162
+ |------|-------------|---------|
163
+ | \`string\` | Text values | \`name:string\` |
164
+ | \`number\` | Numeric values | \`price:number\` |
165
+ | \`boolean\` | True/false values | \`isActive:boolean\` |
166
+ | \`UUID\` | Unique identifiers | \`productId:UUID\` |
167
+ | \`Date\` | Date/time values | \`createdAt:Date\` |
168
+
169
+ ## Tips for Claude Code
170
+
171
+ When helping users scaffold Magek components:
172
+
173
+ 1. **Start with the Command** - This defines the user action
174
+ 2. **Create the Event** - This records what happened
175
+ 3. **Create the Entity** - This maintains the state
176
+ 4. **Create the Read Model** - This provides query access
177
+
178
+ Always use descriptive names that reflect the domain (e.g., \`CreateOrder\` not \`Create\`).
179
+ `;
180
+ export function getCliReferenceResource() {
181
+ return {
182
+ uri: CLI_REFERENCE_URI,
183
+ name: 'Magek CLI Reference',
184
+ description: 'Complete reference for Magek CLI scaffolding commands',
185
+ mimeType: 'text/markdown',
186
+ };
187
+ }
188
+ export function readCliReference() {
189
+ return {
190
+ content: CLI_REFERENCE_CONTENT,
191
+ mimeType: 'text/markdown',
192
+ };
193
+ }
@@ -0,0 +1,18 @@
1
+ import type { DocsLoader } from '../utils/docs-loader.js';
2
+ export interface DocumentationResource {
3
+ uri: string;
4
+ name: string;
5
+ description: string;
6
+ mimeType: string;
7
+ }
8
+ export declare class DocumentationResources {
9
+ private readonly docsLoader;
10
+ constructor(docsLoader: DocsLoader);
11
+ listResources(): Promise<DocumentationResource[]>;
12
+ readResource(uri: string): Promise<{
13
+ content: string;
14
+ mimeType: string;
15
+ }>;
16
+ private getIndex;
17
+ private organizeByCategory;
18
+ }
@@ -0,0 +1,62 @@
1
+ export class DocumentationResources {
2
+ docsLoader;
3
+ constructor(docsLoader) {
4
+ this.docsLoader = docsLoader;
5
+ }
6
+ async listResources() {
7
+ const documents = await this.docsLoader.loadIndex();
8
+ const resources = documents.map((doc) => ({
9
+ uri: doc.uri,
10
+ name: doc.title,
11
+ description: doc.description || `Magek documentation: ${doc.path}`,
12
+ mimeType: 'text/markdown',
13
+ }));
14
+ // Add the index resource
15
+ resources.unshift({
16
+ uri: 'magek://docs/index',
17
+ name: 'Documentation Index',
18
+ description: 'Index of all Magek documentation topics',
19
+ mimeType: 'application/json',
20
+ });
21
+ return resources;
22
+ }
23
+ async readResource(uri) {
24
+ if (uri === 'magek://docs/index') {
25
+ return this.getIndex();
26
+ }
27
+ const path = this.docsLoader.resolveUriToPath(uri);
28
+ if (!path) {
29
+ throw new Error(`Invalid resource URI: ${uri}`);
30
+ }
31
+ const content = await this.docsLoader.loadDocument(path);
32
+ return { content, mimeType: 'text/markdown' };
33
+ }
34
+ async getIndex() {
35
+ const documents = await this.docsLoader.loadIndex();
36
+ const index = {
37
+ description: 'Magek Documentation Index',
38
+ topics: this.organizeByCategory(documents),
39
+ };
40
+ return {
41
+ content: JSON.stringify(index, null, 2),
42
+ mimeType: 'application/json',
43
+ };
44
+ }
45
+ organizeByCategory(documents) {
46
+ const categories = {};
47
+ for (const doc of documents) {
48
+ // Extract category from path (e.g., "getting-started/installation.md" -> "getting-started")
49
+ const pathParts = doc.path.split('/');
50
+ const category = pathParts.length > 1 ? pathParts[0] : 'general';
51
+ if (!categories[category]) {
52
+ categories[category] = [];
53
+ }
54
+ categories[category].push({
55
+ title: doc.title,
56
+ uri: doc.uri,
57
+ description: doc.description,
58
+ });
59
+ }
60
+ return categories;
61
+ }
62
+ }
@@ -0,0 +1,5 @@
1
+ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
2
+ export interface MagekServerOptions {
3
+ docsPath: string;
4
+ }
5
+ export declare function createMagekServer(options: MagekServerOptions): Server;
package/dist/server.js ADDED
@@ -0,0 +1,127 @@
1
+ import { createRequire } from 'node:module';
2
+ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
3
+ import { CallToolRequestSchema, GetPromptRequestSchema, ListPromptsRequestSchema, ListResourcesRequestSchema, ListToolsRequestSchema, ReadResourceRequestSchema, } from '@modelcontextprotocol/sdk/types.js';
4
+ import { DocsLoader } from './utils/docs-loader.js';
5
+ const require = createRequire(import.meta.url);
6
+ const packageJson = require('../package.json');
7
+ import { DocumentationResources } from './resources/documentation.js';
8
+ import { CLI_REFERENCE_URI, getCliReferenceResource, readCliReference, } from './resources/cli-reference.js';
9
+ import { CQRS_FLOW_PROMPT_NAME, getCqrsFlowPrompt, getCqrsFlowPromptDefinition, } from './prompts/cqrs-flow.js';
10
+ import { TROUBLESHOOTING_PROMPT_NAME, getTroubleshootingPrompt, getTroubleshootingPromptDefinition, } from './prompts/troubleshooting.js';
11
+ export function createMagekServer(options) {
12
+ const { docsPath } = options;
13
+ const docsLoader = new DocsLoader(docsPath);
14
+ const docResources = new DocumentationResources(docsLoader);
15
+ const server = new Server({
16
+ name: 'magek',
17
+ version: packageJson.version,
18
+ }, {
19
+ capabilities: {
20
+ resources: {},
21
+ prompts: {},
22
+ tools: {},
23
+ },
24
+ });
25
+ // List resources handler
26
+ server.setRequestHandler(ListResourcesRequestSchema, async () => {
27
+ const resources = await docResources.listResources();
28
+ // Add CLI reference resource
29
+ resources.push(getCliReferenceResource());
30
+ return {
31
+ resources: resources.map((r) => ({
32
+ uri: r.uri,
33
+ name: r.name,
34
+ description: r.description,
35
+ mimeType: r.mimeType,
36
+ })),
37
+ };
38
+ });
39
+ // Read resource handler
40
+ server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
41
+ const { uri } = request.params;
42
+ // Handle CLI reference
43
+ if (uri === CLI_REFERENCE_URI) {
44
+ const { content, mimeType } = readCliReference();
45
+ return {
46
+ contents: [
47
+ {
48
+ uri,
49
+ mimeType,
50
+ text: content,
51
+ },
52
+ ],
53
+ };
54
+ }
55
+ // Handle documentation resources
56
+ if (uri.startsWith('magek://docs/')) {
57
+ const { content, mimeType } = await docResources.readResource(uri);
58
+ return {
59
+ contents: [
60
+ {
61
+ uri,
62
+ mimeType,
63
+ text: content,
64
+ },
65
+ ],
66
+ };
67
+ }
68
+ throw new Error(`Unknown resource: ${uri}`);
69
+ });
70
+ // List prompts handler
71
+ server.setRequestHandler(ListPromptsRequestSchema, async () => {
72
+ return {
73
+ prompts: [getCqrsFlowPromptDefinition(), getTroubleshootingPromptDefinition()],
74
+ };
75
+ });
76
+ // Get prompt handler
77
+ server.setRequestHandler(GetPromptRequestSchema, async (request) => {
78
+ const { name, arguments: args } = request.params;
79
+ if (name === CQRS_FLOW_PROMPT_NAME) {
80
+ const feature = args?.feature;
81
+ if (!feature || typeof feature !== 'string') {
82
+ throw new Error('Required argument "feature" is missing');
83
+ }
84
+ return {
85
+ description: `CQRS implementation guide for: ${feature}`,
86
+ messages: [
87
+ {
88
+ role: 'user',
89
+ content: {
90
+ type: 'text',
91
+ text: getCqrsFlowPrompt({ feature }),
92
+ },
93
+ },
94
+ ],
95
+ };
96
+ }
97
+ if (name === TROUBLESHOOTING_PROMPT_NAME) {
98
+ const issue = args?.issue;
99
+ return {
100
+ description: issue
101
+ ? `Troubleshooting guide for: ${issue}`
102
+ : 'General Magek troubleshooting guide',
103
+ messages: [
104
+ {
105
+ role: 'user',
106
+ content: {
107
+ type: 'text',
108
+ text: getTroubleshootingPrompt({
109
+ issue: typeof issue === 'string' ? issue : undefined,
110
+ }),
111
+ },
112
+ },
113
+ ],
114
+ };
115
+ }
116
+ throw new Error(`Unknown prompt: ${name}`);
117
+ });
118
+ // List tools handler - no tools for now, just resources and prompts
119
+ server.setRequestHandler(ListToolsRequestSchema, async () => {
120
+ return { tools: [] };
121
+ });
122
+ // Call tool handler - no tools for now
123
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
124
+ throw new Error(`Unknown tool: ${request.params.name}`);
125
+ });
126
+ return server;
127
+ }
@@ -0,0 +1,19 @@
1
+ export interface DocumentInfo {
2
+ uri: string;
3
+ path: string;
4
+ title: string;
5
+ description: string | null;
6
+ }
7
+ export interface DocsIndex {
8
+ documents: DocumentInfo[];
9
+ }
10
+ export declare class DocsLoader {
11
+ private readonly docsPath;
12
+ constructor(docsPath: string);
13
+ loadDocument(relativePath: string): Promise<string>;
14
+ loadIndex(): Promise<DocumentInfo[]>;
15
+ private scanDocuments;
16
+ private extractTitle;
17
+ private extractDescription;
18
+ resolveUriToPath(uri: string): string | null;
19
+ }
@@ -0,0 +1,111 @@
1
+ import { readFile, readdir, stat } from 'node:fs/promises';
2
+ import { existsSync } from 'node:fs';
3
+ import { join, normalize, relative, resolve } from 'node:path';
4
+ export class DocsLoader {
5
+ docsPath;
6
+ constructor(docsPath) {
7
+ this.docsPath = docsPath;
8
+ }
9
+ async loadDocument(relativePath) {
10
+ const fullPath = resolve(this.docsPath, relativePath);
11
+ const normalizedDocsPath = resolve(this.docsPath);
12
+ // Security: ensure the resolved path is within docsPath
13
+ if (!fullPath.startsWith(normalizedDocsPath + '/') && fullPath !== normalizedDocsPath) {
14
+ throw new Error(`Invalid path: ${relativePath}`);
15
+ }
16
+ if (!existsSync(fullPath)) {
17
+ throw new Error(`Document not found: ${relativePath}`);
18
+ }
19
+ return readFile(fullPath, 'utf-8');
20
+ }
21
+ async loadIndex() {
22
+ const indexPath = join(this.docsPath, 'docs-index.json');
23
+ if (existsSync(indexPath)) {
24
+ const content = await readFile(indexPath, 'utf-8');
25
+ return JSON.parse(content);
26
+ }
27
+ // Fallback: scan directory
28
+ return this.scanDocuments();
29
+ }
30
+ async scanDocuments(dir = this.docsPath, documents = []) {
31
+ if (!existsSync(dir)) {
32
+ return documents;
33
+ }
34
+ const entries = await readdir(dir);
35
+ for (const entry of entries) {
36
+ const fullPath = join(dir, entry);
37
+ const stats = await stat(fullPath);
38
+ if (stats.isDirectory()) {
39
+ await this.scanDocuments(fullPath, documents);
40
+ }
41
+ else if (entry.endsWith('.md')) {
42
+ const relativePath = relative(this.docsPath, fullPath);
43
+ const uri = relativePath.replace(/\.md$/, '').replace(/\\/g, '/');
44
+ const content = await readFile(fullPath, 'utf-8');
45
+ documents.push({
46
+ uri: `magek://docs/${uri}`,
47
+ path: relativePath.replace(/\\/g, '/'),
48
+ title: this.extractTitle(content) || uri.split('/').pop() || uri,
49
+ description: this.extractDescription(content),
50
+ });
51
+ }
52
+ }
53
+ return documents;
54
+ }
55
+ extractTitle(content) {
56
+ // Try to extract title from first H1 heading
57
+ const h1Match = content.match(/^#\s+(.+)$/m);
58
+ if (h1Match) {
59
+ return h1Match[1].trim();
60
+ }
61
+ // Try to extract from frontmatter title
62
+ const frontmatterMatch = content.match(/^---[\s\S]*?title:\s*['"]?([^'"\n]+)['"]?[\s\S]*?---/m);
63
+ if (frontmatterMatch) {
64
+ return frontmatterMatch[1].trim();
65
+ }
66
+ return null;
67
+ }
68
+ extractDescription(content) {
69
+ // Remove frontmatter
70
+ const contentWithoutFrontmatter = content.replace(/^---[\s\S]*?---\n*/m, '');
71
+ // Find first paragraph after H1
72
+ const lines = contentWithoutFrontmatter.split('\n');
73
+ let foundH1 = false;
74
+ let description = '';
75
+ for (const line of lines) {
76
+ if (line.startsWith('# ')) {
77
+ foundH1 = true;
78
+ continue;
79
+ }
80
+ if (foundH1 && line.trim() && !line.startsWith('#')) {
81
+ description = line.trim();
82
+ break;
83
+ }
84
+ }
85
+ // Truncate to reasonable length
86
+ if (description.length > 200) {
87
+ description = description.substring(0, 197) + '...';
88
+ }
89
+ return description || null;
90
+ }
91
+ resolveUriToPath(uri) {
92
+ // Convert magek://docs/getting-started/installation -> getting-started/installation.md
93
+ const match = uri.match(/^magek:\/\/docs\/(.+)$/);
94
+ if (!match) {
95
+ return null;
96
+ }
97
+ const pathSegment = match[1];
98
+ // Security: reject path traversal attempts and absolute paths
99
+ if (pathSegment.includes('..') ||
100
+ pathSegment.startsWith('/') ||
101
+ pathSegment.includes('\\')) {
102
+ return null;
103
+ }
104
+ // Normalize and verify it's a clean relative path
105
+ const normalized = normalize(pathSegment);
106
+ if (normalized.startsWith('..') || normalized.startsWith('/')) {
107
+ return null;
108
+ }
109
+ return `${normalized}.md`;
110
+ }
111
+ }
@@ -0,0 +1,96 @@
1
+ ---
2
+ title: "Custom Templates"
3
+ group: "Advanced"
4
+ ---
5
+
6
+ # Customizing CLI resource templates
7
+
8
+ You can change what the newly created Magek resources will contain by customizing the resource template files.
9
+
10
+ To do this, you first need to publish the resource templates by running the `npx magek stub:publish` command. This will create a folder `stubs` in the root directory of the project, and it will contain all the resources that you can customize:
11
+
12
+ ```
13
+ stubs/
14
+ ├─ command.stub
15
+ ├─ entity.stub
16
+ ├─ event.stub
17
+ ├─ event-handler.stub
18
+ ├─ read-model.stub
19
+ ├─ scheduled-command.stub
20
+ └─ type.stub
21
+ ```
22
+
23
+ After that, Magek CLI will start using your local templates instead of the default ones.
24
+ Let's try this by adding a simple comment to the `type.stub` file.
25
+
26
+ ```
27
+ // Look I am a comment that will now appear in every new type file 🐙
28
+ export class {{{ name }}} {
29
+ public constructor(
30
+ {{#fields}}
31
+ public {{{name}}}: {{{type}}},
32
+ {{/fields}}
33
+ ) {}
34
+ }
35
+ ```
36
+
37
+ Now if you run `npx magek new:type CartItem --fields sku:string` command, you will get `common/cart-item.ts` file with following content:
38
+ ```typescript
39
+ // Look I am a comment that will now appear in every new type file 🐙
40
+ export class CartItem {
41
+ public constructor(
42
+ public sku: string,
43
+ ) {}
44
+ }
45
+ ```
46
+
47
+ You did it, we just updated our resource template file! Now when you run `npx magek new:type`, it will contain the comment you added earlier 🚀
48
+ Of course, this is a simple example, and you may want to add new methods, import something, you name it!
49
+
50
+ Here are some answers to questions you may have:
51
+
52
+ #### QA
53
+
54
+ **Can I have only one stub for a certain resource?**
55
+
56
+ Yes! The resource generator will check if you have a custom template or it will use the default template
57
+
58
+ **How can I keep up with new template updates?**
59
+
60
+ 1. Run `npx magek stub:publish --force` command
61
+ 2. Review changes
62
+ 3. Done!
63
+
64
+ **Can I adjust the command template and leave the other resources as they are?**
65
+
66
+ Yes. You can only have the `command.stub` file in the `/stubs` folder and customize it.
67
+ The generator will use the default templates for the other resources.
68
+
69
+ **How can I use the default templates again!?**
70
+
71
+ Simply delete the `/stubs` folder or a specific resource file.
72
+
73
+ **What are these strange name, #fields, etc. things????**
74
+
75
+ These are the variables and sections used by the mustache.js templating engine.
76
+ They allow us to dynamically generate new resources.
77
+
78
+ **How do I use custom project templates?**
79
+
80
+ Project creation now uses the modern `npm create` pattern instead of the CLI. You can use custom project templates in two ways:
81
+
82
+ **From GitHub repositories:**
83
+ ```bash
84
+ npm create magek@latest my-app -- --template user/custom-template
85
+ ```
86
+
87
+ **From local filesystem:**
88
+ ```bash
89
+ npm create magek@latest my-app -- --template ./path/to/local/template
90
+ ```
91
+
92
+ The template should contain a Magek project structure with mustache placeholders like `{{PROJECT_NAME}}`, `{{description}}`, etc.
93
+
94
+ **I have another question!**
95
+
96
+ You can ask questions on our Discord channel or create discussion on Github.