@mandujs/mcp 0.13.0 → 0.17.0
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/README.md +102 -7
- package/package.json +3 -2
- package/src/adapters/index.ts +20 -20
- package/src/adapters/monitor-adapter.ts +100 -100
- package/src/adapters/tool-adapter.ts +88 -88
- package/src/executor/error-handler.ts +250 -250
- package/src/executor/index.ts +22 -22
- package/src/executor/tool-executor.ts +148 -148
- package/src/hooks/config-watcher.ts +174 -174
- package/src/hooks/index.ts +23 -23
- package/src/hooks/mcp-hooks.ts +227 -227
- package/src/logging/index.ts +15 -15
- package/src/logging/mcp-transport.ts +134 -134
- package/src/registry/index.ts +13 -13
- package/src/registry/mcp-tool-registry.ts +298 -298
- package/src/resources/skills/guides.ts +1136 -1136
- package/src/resources/skills/index.ts +12 -12
- package/src/resources/skills/loader.ts +218 -218
- package/src/resources/skills/mandu-composition/SKILL.md +91 -91
- package/src/resources/skills/mandu-composition/metadata.json +13 -13
- package/src/resources/skills/mandu-composition/rules/_sections.md +26 -26
- package/src/resources/skills/mandu-composition/rules/_template.md +77 -77
- package/src/resources/skills/mandu-composition/rules/comp-arch-avoid-boolean-props.md +146 -146
- package/src/resources/skills/mandu-composition/rules/comp-arch-compound-components.md +164 -164
- package/src/resources/skills/mandu-composition/rules/comp-island-event.md +161 -161
- package/src/resources/skills/mandu-composition/rules/comp-island-slot-split.md +167 -167
- package/src/resources/skills/mandu-composition/rules/comp-pattern-children.md +149 -149
- package/src/resources/skills/mandu-composition/rules/comp-state-context-interface.md +148 -148
- package/src/resources/skills/mandu-composition/rules/comp-state-lift-state.md +150 -150
- package/src/resources/skills/mandu-deployment/SKILL.md +92 -92
- package/src/resources/skills/mandu-deployment/_sections.md +41 -41
- package/src/resources/skills/mandu-deployment/_template.md +38 -38
- package/src/resources/skills/mandu-deployment/metadata.json +13 -13
- package/src/resources/skills/mandu-deployment/rules/deploy-build-bun.md +109 -109
- package/src/resources/skills/mandu-deployment/rules/deploy-build-output.md +115 -115
- package/src/resources/skills/mandu-deployment/rules/deploy-cicd-github.md +219 -219
- package/src/resources/skills/mandu-deployment/rules/deploy-docker-bun.md +150 -150
- package/src/resources/skills/mandu-deployment/rules/deploy-docker-compose.md +223 -223
- package/src/resources/skills/mandu-deployment/rules/deploy-platform-fly.md +152 -152
- package/src/resources/skills/mandu-deployment/rules/deploy-platform-render.md +179 -179
- package/src/resources/skills/mandu-deployment/rules/deploy-platform-supabase.md +323 -323
- package/src/resources/skills/mandu-deployment/rules/deploy-platform-vercel.md +140 -140
- package/src/resources/skills/mandu-fs-routes/SKILL.md +82 -82
- package/src/resources/skills/mandu-fs-routes/metadata.json +12 -12
- package/src/resources/skills/mandu-fs-routes/rules/_sections.md +36 -36
- package/src/resources/skills/mandu-fs-routes/rules/_template.md +69 -69
- package/src/resources/skills/mandu-fs-routes/rules/routes-api-methods.md +65 -65
- package/src/resources/skills/mandu-fs-routes/rules/routes-dynamic-param.md +93 -93
- package/src/resources/skills/mandu-fs-routes/rules/routes-naming-page.md +55 -55
- package/src/resources/skills/mandu-guard/SKILL.md +129 -129
- package/src/resources/skills/mandu-guard/metadata.json +12 -12
- package/src/resources/skills/mandu-guard/rules/_sections.md +36 -36
- package/src/resources/skills/mandu-guard/rules/_template.md +82 -82
- package/src/resources/skills/mandu-guard/rules/guard-config-rules.md +100 -100
- package/src/resources/skills/mandu-guard/rules/guard-layer-direction.md +76 -76
- package/src/resources/skills/mandu-guard/rules/guard-preset-mandu.md +81 -81
- package/src/resources/skills/mandu-guard/rules/guard-validate-import.md +80 -80
- package/src/resources/skills/mandu-hydration/SKILL.md +91 -91
- package/src/resources/skills/mandu-hydration/metadata.json +12 -12
- package/src/resources/skills/mandu-hydration/rules/_sections.md +31 -31
- package/src/resources/skills/mandu-hydration/rules/_template.md +72 -72
- package/src/resources/skills/mandu-hydration/rules/hydration-data-event.md +109 -109
- package/src/resources/skills/mandu-hydration/rules/hydration-directive-use-client.md +55 -55
- package/src/resources/skills/mandu-hydration/rules/hydration-island-setup.md +113 -113
- package/src/resources/skills/mandu-hydration/rules/hydration-priority-visible.md +68 -68
- package/src/resources/skills/mandu-performance/SKILL.md +85 -85
- package/src/resources/skills/mandu-performance/metadata.json +14 -14
- package/src/resources/skills/mandu-performance/rules/_sections.md +31 -31
- package/src/resources/skills/mandu-performance/rules/_template.md +64 -64
- package/src/resources/skills/mandu-performance/rules/perf-async-defer-await.md +103 -103
- package/src/resources/skills/mandu-performance/rules/perf-async-parallel.md +95 -95
- package/src/resources/skills/mandu-performance/rules/perf-bun-file.md +124 -124
- package/src/resources/skills/mandu-performance/rules/perf-bun-serve.md +125 -125
- package/src/resources/skills/mandu-performance/rules/perf-bundle-imports.md +80 -80
- package/src/resources/skills/mandu-performance/rules/perf-bundle-island-lazy.md +145 -145
- package/src/resources/skills/mandu-performance/rules/perf-cache-react.md +98 -98
- package/src/resources/skills/mandu-performance/rules/perf-render-transitions.md +154 -154
- package/src/resources/skills/mandu-security/SKILL.md +87 -87
- package/src/resources/skills/mandu-security/metadata.json +13 -13
- package/src/resources/skills/mandu-security/rules/_sections.md +31 -31
- package/src/resources/skills/mandu-security/rules/_template.md +74 -74
- package/src/resources/skills/mandu-security/rules/sec-auth-guard.md +127 -127
- package/src/resources/skills/mandu-security/rules/sec-env-management.md +133 -133
- package/src/resources/skills/mandu-security/rules/sec-input-validate.md +148 -148
- package/src/resources/skills/mandu-security/rules/sec-protect-csrf.md +146 -146
- package/src/resources/skills/mandu-security/rules/sec-protect-headers.md +138 -138
- package/src/resources/skills/mandu-slot/SKILL.md +85 -85
- package/src/resources/skills/mandu-slot/metadata.json +12 -12
- package/src/resources/skills/mandu-slot/rules/_sections.md +36 -36
- package/src/resources/skills/mandu-slot/rules/_template.md +63 -63
- package/src/resources/skills/mandu-slot/rules/slot-basic-structure.md +38 -38
- package/src/resources/skills/mandu-slot/rules/slot-ctx-response.md +56 -56
- package/src/resources/skills/mandu-slot/rules/slot-guard-auth.md +59 -59
- package/src/resources/skills/mandu-slot/rules/slot-http-methods.md +64 -64
- package/src/resources/skills/mandu-styling/SKILL.md +154 -154
- package/src/resources/skills/mandu-styling/_sections.md +43 -43
- package/src/resources/skills/mandu-styling/_template.md +32 -32
- package/src/resources/skills/mandu-styling/metadata.json +15 -15
- package/src/resources/skills/mandu-styling/rules/style-component-compound.md +235 -235
- package/src/resources/skills/mandu-styling/rules/style-component-slots.md +255 -255
- package/src/resources/skills/mandu-styling/rules/style-component-tokens.md +205 -205
- package/src/resources/skills/mandu-styling/rules/style-island-animations.md +272 -272
- package/src/resources/skills/mandu-styling/rules/style-island-scoping.md +167 -167
- package/src/resources/skills/mandu-styling/rules/style-island-variants.md +221 -221
- package/src/resources/skills/mandu-styling/rules/style-perf-critical.md +209 -209
- package/src/resources/skills/mandu-styling/rules/style-perf-purge.md +192 -192
- package/src/resources/skills/mandu-styling/rules/style-setup-modules.md +162 -162
- package/src/resources/skills/mandu-styling/rules/style-setup-panda.md +164 -164
- package/src/resources/skills/mandu-styling/rules/style-setup-tailwind.md +170 -170
- package/src/resources/skills/mandu-styling/rules/style-tailwind-v4-gotchas.md +179 -179
- package/src/resources/skills/mandu-styling/rules/style-theme-darkmode.md +229 -229
- package/src/resources/skills/mandu-testing/SKILL.md +99 -99
- package/src/resources/skills/mandu-testing/metadata.json +13 -13
- package/src/resources/skills/mandu-testing/rules/_sections.md +26 -26
- package/src/resources/skills/mandu-testing/rules/_template.md +65 -65
- package/src/resources/skills/mandu-testing/rules/test-component-island.md +195 -195
- package/src/resources/skills/mandu-testing/rules/test-e2e-playwright.md +196 -196
- package/src/resources/skills/mandu-testing/rules/test-mock-fetch.md +219 -219
- package/src/resources/skills/mandu-testing/rules/test-slot-unit.md +192 -192
- package/src/resources/skills/mandu-ui/SKILL.md +117 -117
- package/src/resources/skills/mandu-ui/_sections.md +23 -23
- package/src/resources/skills/mandu-ui/_template.md +32 -32
- package/src/resources/skills/mandu-ui/metadata.json +13 -13
- package/src/resources/skills/mandu-ui/rules/ui-accessibility-aria.md +232 -232
- package/src/resources/skills/mandu-ui/rules/ui-accessibility-focus.md +238 -238
- package/src/resources/skills/mandu-ui/rules/ui-composition-patterns.md +259 -259
- package/src/resources/skills/mandu-ui/rules/ui-island-integration.md +258 -258
- package/src/resources/skills/mandu-ui/rules/ui-radix-patterns.md +213 -213
- package/src/resources/skills/mandu-ui/rules/ui-shadcn-setup.md +209 -209
- package/src/resources/skills/recipes.ts +932 -932
- package/src/tools/ate.ts +219 -0
- package/src/tools/index.ts +4 -1
- package/src/tools/project.ts +334 -334
- package/src/tools/runtime.ts +497 -497
- package/src/tools/seo.ts +417 -417
- package/src/utils/withWarnings.ts +83 -83
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
export { GUIDES, getGuide, listGuides, type GuideId } from "./guides.js";
|
|
2
|
-
export { RECIPES, getRecipe, listRecipes, type RecipeId } from "./recipes.js";
|
|
3
|
-
|
|
4
|
-
// Agent Skills pattern
|
|
5
|
-
export {
|
|
6
|
-
listSkills,
|
|
7
|
-
getSkill,
|
|
8
|
-
listSkillRules,
|
|
9
|
-
getSkillRule,
|
|
10
|
-
type SkillMeta,
|
|
11
|
-
type RuleMeta,
|
|
12
|
-
} from "./loader.js";
|
|
1
|
+
export { GUIDES, getGuide, listGuides, type GuideId } from "./guides.js";
|
|
2
|
+
export { RECIPES, getRecipe, listRecipes, type RecipeId } from "./recipes.js";
|
|
3
|
+
|
|
4
|
+
// Agent Skills pattern
|
|
5
|
+
export {
|
|
6
|
+
listSkills,
|
|
7
|
+
getSkill,
|
|
8
|
+
listSkillRules,
|
|
9
|
+
getSkillRule,
|
|
10
|
+
type SkillMeta,
|
|
11
|
+
type RuleMeta,
|
|
12
|
+
} from "./loader.js";
|
|
@@ -1,218 +1,218 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Mandu MCP Skills - File-based Skill Loader
|
|
3
|
-
* Agent Skills 패턴으로 구성된 스킬을 파일 시스템에서 로드
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import { readdir, readFile } from "fs/promises";
|
|
7
|
-
import { join, dirname } from "path";
|
|
8
|
-
import { fileURLToPath } from "url";
|
|
9
|
-
|
|
10
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
11
|
-
const __dirname = dirname(__filename);
|
|
12
|
-
|
|
13
|
-
export interface SkillMeta {
|
|
14
|
-
id: string;
|
|
15
|
-
name: string;
|
|
16
|
-
description: string;
|
|
17
|
-
version: string;
|
|
18
|
-
author: string;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export interface RuleMeta {
|
|
22
|
-
id: string;
|
|
23
|
-
title: string;
|
|
24
|
-
impact: "CRITICAL" | "HIGH" | "MEDIUM" | "LOW";
|
|
25
|
-
impactDescription: string;
|
|
26
|
-
tags: string[];
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
// Available skills
|
|
30
|
-
const SKILL_IDS = [
|
|
31
|
-
"mandu-slot",
|
|
32
|
-
"mandu-fs-routes",
|
|
33
|
-
"mandu-hydration",
|
|
34
|
-
"mandu-guard",
|
|
35
|
-
"mandu-performance",
|
|
36
|
-
"mandu-composition",
|
|
37
|
-
"mandu-security",
|
|
38
|
-
"mandu-testing",
|
|
39
|
-
"mandu-deployment",
|
|
40
|
-
"mandu-styling",
|
|
41
|
-
"mandu-ui",
|
|
42
|
-
];
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* Parse YAML frontmatter from markdown content
|
|
46
|
-
*/
|
|
47
|
-
function parseFrontmatter(content: string): { frontmatter: Record<string, unknown>; body: string } {
|
|
48
|
-
const match = content.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n([\s\S]*)$/);
|
|
49
|
-
if (!match) {
|
|
50
|
-
return { frontmatter: {}, body: content };
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
const [, yamlStr, body] = match;
|
|
54
|
-
const frontmatter: Record<string, unknown> = {};
|
|
55
|
-
|
|
56
|
-
// Simple YAML parsing (key: value pairs)
|
|
57
|
-
for (const line of yamlStr.split("\n")) {
|
|
58
|
-
const colonIndex = line.indexOf(":");
|
|
59
|
-
if (colonIndex > 0) {
|
|
60
|
-
const key = line.slice(0, colonIndex).trim();
|
|
61
|
-
let value: unknown = line.slice(colonIndex + 1).trim();
|
|
62
|
-
|
|
63
|
-
// Handle multiline values (description with |)
|
|
64
|
-
if (value === "|") {
|
|
65
|
-
continue; // Will be captured in subsequent lines
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
// Parse arrays (tags)
|
|
69
|
-
if (typeof value === "string" && value.includes(",")) {
|
|
70
|
-
value = value.split(",").map((s) => s.trim());
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
frontmatter[key] = value;
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
return { frontmatter, body };
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
/**
|
|
81
|
-
* List all available skills
|
|
82
|
-
*/
|
|
83
|
-
export function listSkills(): SkillMeta[] {
|
|
84
|
-
return SKILL_IDS.map((id) => {
|
|
85
|
-
const name = id.replace("mandu-", "");
|
|
86
|
-
return {
|
|
87
|
-
id,
|
|
88
|
-
name,
|
|
89
|
-
description: getSkillDescription(id),
|
|
90
|
-
version: "1.0.0",
|
|
91
|
-
author: "mandu",
|
|
92
|
-
};
|
|
93
|
-
});
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
function getSkillDescription(id: string): string {
|
|
97
|
-
const descriptions: Record<string, string> = {
|
|
98
|
-
"mandu-slot": "Business logic with Mandu.filling() API",
|
|
99
|
-
"mandu-fs-routes": "File-system based routing patterns",
|
|
100
|
-
"mandu-hydration": "Island hydration and client components",
|
|
101
|
-
"mandu-guard": "Architecture enforcement and layer dependencies",
|
|
102
|
-
"mandu-performance": "Performance optimization patterns for Mandu apps",
|
|
103
|
-
"mandu-composition": "React composition patterns for Islands and state",
|
|
104
|
-
"mandu-security": "Security best practices for authentication and protection",
|
|
105
|
-
"mandu-testing": "Testing patterns with Bun test and Playwright",
|
|
106
|
-
"mandu-deployment": "Production deployment with Render, Supabase, Docker, and CI/CD",
|
|
107
|
-
"mandu-styling": "CSS framework integration with Tailwind, Panda CSS, and theming",
|
|
108
|
-
"mandu-ui": "UI component library integration with shadcn/ui and accessibility",
|
|
109
|
-
};
|
|
110
|
-
return descriptions[id] || "";
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
/**
|
|
114
|
-
* Get a skill's SKILL.md content
|
|
115
|
-
*/
|
|
116
|
-
export async function getSkill(skillId: string): Promise<{ meta: SkillMeta; content: string } | null> {
|
|
117
|
-
if (!SKILL_IDS.includes(skillId)) {
|
|
118
|
-
return null;
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
const skillPath = join(__dirname, skillId, "SKILL.md");
|
|
122
|
-
|
|
123
|
-
try {
|
|
124
|
-
const content = await readFile(skillPath, "utf-8");
|
|
125
|
-
const { frontmatter, body } = parseFrontmatter(content);
|
|
126
|
-
|
|
127
|
-
return {
|
|
128
|
-
meta: {
|
|
129
|
-
id: skillId,
|
|
130
|
-
name: (frontmatter.name as string) || skillId,
|
|
131
|
-
description: (frontmatter.description as string) || "",
|
|
132
|
-
version: ((frontmatter.metadata as Record<string, string>)?.version as string) || "1.0.0",
|
|
133
|
-
author: ((frontmatter.metadata as Record<string, string>)?.author as string) || "mandu",
|
|
134
|
-
},
|
|
135
|
-
content: body,
|
|
136
|
-
};
|
|
137
|
-
} catch {
|
|
138
|
-
return null;
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
/**
|
|
143
|
-
* List rules for a skill
|
|
144
|
-
*/
|
|
145
|
-
export async function listSkillRules(skillId: string): Promise<RuleMeta[]> {
|
|
146
|
-
if (!SKILL_IDS.includes(skillId)) {
|
|
147
|
-
return [];
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
const rulesPath = join(__dirname, skillId, "rules");
|
|
151
|
-
|
|
152
|
-
try {
|
|
153
|
-
const files = await readdir(rulesPath);
|
|
154
|
-
const rules: RuleMeta[] = [];
|
|
155
|
-
|
|
156
|
-
for (const file of files) {
|
|
157
|
-
if (!file.endsWith(".md")) continue;
|
|
158
|
-
|
|
159
|
-
const ruleId = file.replace(".md", "");
|
|
160
|
-
const content = await readFile(join(rulesPath, file), "utf-8");
|
|
161
|
-
const { frontmatter } = parseFrontmatter(content);
|
|
162
|
-
|
|
163
|
-
rules.push({
|
|
164
|
-
id: ruleId,
|
|
165
|
-
title: (frontmatter.title as string) || ruleId,
|
|
166
|
-
impact: (frontmatter.impact as RuleMeta["impact"]) || "MEDIUM",
|
|
167
|
-
impactDescription: (frontmatter.impactDescription as string) || "",
|
|
168
|
-
tags: Array.isArray(frontmatter.tags)
|
|
169
|
-
? (frontmatter.tags as string[])
|
|
170
|
-
: typeof frontmatter.tags === "string"
|
|
171
|
-
? frontmatter.tags.split(",").map((s: string) => s.trim())
|
|
172
|
-
: [],
|
|
173
|
-
});
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
// Sort by impact priority
|
|
177
|
-
const impactOrder = { CRITICAL: 0, HIGH: 1, MEDIUM: 2, LOW: 3 };
|
|
178
|
-
return rules.sort((a, b) => impactOrder[a.impact] - impactOrder[b.impact]);
|
|
179
|
-
} catch {
|
|
180
|
-
return [];
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
/**
|
|
185
|
-
* Get a specific rule's content
|
|
186
|
-
*/
|
|
187
|
-
export async function getSkillRule(
|
|
188
|
-
skillId: string,
|
|
189
|
-
ruleId: string
|
|
190
|
-
): Promise<{ meta: RuleMeta; content: string } | null> {
|
|
191
|
-
if (!SKILL_IDS.includes(skillId)) {
|
|
192
|
-
return null;
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
const rulePath = join(__dirname, skillId, "rules", `${ruleId}.md`);
|
|
196
|
-
|
|
197
|
-
try {
|
|
198
|
-
const content = await readFile(rulePath, "utf-8");
|
|
199
|
-
const { frontmatter, body } = parseFrontmatter(content);
|
|
200
|
-
|
|
201
|
-
return {
|
|
202
|
-
meta: {
|
|
203
|
-
id: ruleId,
|
|
204
|
-
title: (frontmatter.title as string) || ruleId,
|
|
205
|
-
impact: (frontmatter.impact as RuleMeta["impact"]) || "MEDIUM",
|
|
206
|
-
impactDescription: (frontmatter.impactDescription as string) || "",
|
|
207
|
-
tags: Array.isArray(frontmatter.tags)
|
|
208
|
-
? (frontmatter.tags as string[])
|
|
209
|
-
: typeof frontmatter.tags === "string"
|
|
210
|
-
? frontmatter.tags.split(",").map((s: string) => s.trim())
|
|
211
|
-
: [],
|
|
212
|
-
},
|
|
213
|
-
content: body,
|
|
214
|
-
};
|
|
215
|
-
} catch {
|
|
216
|
-
return null;
|
|
217
|
-
}
|
|
218
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Mandu MCP Skills - File-based Skill Loader
|
|
3
|
+
* Agent Skills 패턴으로 구성된 스킬을 파일 시스템에서 로드
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { readdir, readFile } from "fs/promises";
|
|
7
|
+
import { join, dirname } from "path";
|
|
8
|
+
import { fileURLToPath } from "url";
|
|
9
|
+
|
|
10
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
11
|
+
const __dirname = dirname(__filename);
|
|
12
|
+
|
|
13
|
+
export interface SkillMeta {
|
|
14
|
+
id: string;
|
|
15
|
+
name: string;
|
|
16
|
+
description: string;
|
|
17
|
+
version: string;
|
|
18
|
+
author: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface RuleMeta {
|
|
22
|
+
id: string;
|
|
23
|
+
title: string;
|
|
24
|
+
impact: "CRITICAL" | "HIGH" | "MEDIUM" | "LOW";
|
|
25
|
+
impactDescription: string;
|
|
26
|
+
tags: string[];
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Available skills
|
|
30
|
+
const SKILL_IDS = [
|
|
31
|
+
"mandu-slot",
|
|
32
|
+
"mandu-fs-routes",
|
|
33
|
+
"mandu-hydration",
|
|
34
|
+
"mandu-guard",
|
|
35
|
+
"mandu-performance",
|
|
36
|
+
"mandu-composition",
|
|
37
|
+
"mandu-security",
|
|
38
|
+
"mandu-testing",
|
|
39
|
+
"mandu-deployment",
|
|
40
|
+
"mandu-styling",
|
|
41
|
+
"mandu-ui",
|
|
42
|
+
];
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Parse YAML frontmatter from markdown content
|
|
46
|
+
*/
|
|
47
|
+
function parseFrontmatter(content: string): { frontmatter: Record<string, unknown>; body: string } {
|
|
48
|
+
const match = content.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n([\s\S]*)$/);
|
|
49
|
+
if (!match) {
|
|
50
|
+
return { frontmatter: {}, body: content };
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const [, yamlStr, body] = match;
|
|
54
|
+
const frontmatter: Record<string, unknown> = {};
|
|
55
|
+
|
|
56
|
+
// Simple YAML parsing (key: value pairs)
|
|
57
|
+
for (const line of yamlStr.split("\n")) {
|
|
58
|
+
const colonIndex = line.indexOf(":");
|
|
59
|
+
if (colonIndex > 0) {
|
|
60
|
+
const key = line.slice(0, colonIndex).trim();
|
|
61
|
+
let value: unknown = line.slice(colonIndex + 1).trim();
|
|
62
|
+
|
|
63
|
+
// Handle multiline values (description with |)
|
|
64
|
+
if (value === "|") {
|
|
65
|
+
continue; // Will be captured in subsequent lines
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Parse arrays (tags)
|
|
69
|
+
if (typeof value === "string" && value.includes(",")) {
|
|
70
|
+
value = value.split(",").map((s) => s.trim());
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
frontmatter[key] = value;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return { frontmatter, body };
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* List all available skills
|
|
82
|
+
*/
|
|
83
|
+
export function listSkills(): SkillMeta[] {
|
|
84
|
+
return SKILL_IDS.map((id) => {
|
|
85
|
+
const name = id.replace("mandu-", "");
|
|
86
|
+
return {
|
|
87
|
+
id,
|
|
88
|
+
name,
|
|
89
|
+
description: getSkillDescription(id),
|
|
90
|
+
version: "1.0.0",
|
|
91
|
+
author: "mandu",
|
|
92
|
+
};
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function getSkillDescription(id: string): string {
|
|
97
|
+
const descriptions: Record<string, string> = {
|
|
98
|
+
"mandu-slot": "Business logic with Mandu.filling() API",
|
|
99
|
+
"mandu-fs-routes": "File-system based routing patterns",
|
|
100
|
+
"mandu-hydration": "Island hydration and client components",
|
|
101
|
+
"mandu-guard": "Architecture enforcement and layer dependencies",
|
|
102
|
+
"mandu-performance": "Performance optimization patterns for Mandu apps",
|
|
103
|
+
"mandu-composition": "React composition patterns for Islands and state",
|
|
104
|
+
"mandu-security": "Security best practices for authentication and protection",
|
|
105
|
+
"mandu-testing": "Testing patterns with Bun test and Playwright",
|
|
106
|
+
"mandu-deployment": "Production deployment with Render, Supabase, Docker, and CI/CD",
|
|
107
|
+
"mandu-styling": "CSS framework integration with Tailwind, Panda CSS, and theming",
|
|
108
|
+
"mandu-ui": "UI component library integration with shadcn/ui and accessibility",
|
|
109
|
+
};
|
|
110
|
+
return descriptions[id] || "";
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Get a skill's SKILL.md content
|
|
115
|
+
*/
|
|
116
|
+
export async function getSkill(skillId: string): Promise<{ meta: SkillMeta; content: string } | null> {
|
|
117
|
+
if (!SKILL_IDS.includes(skillId)) {
|
|
118
|
+
return null;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const skillPath = join(__dirname, skillId, "SKILL.md");
|
|
122
|
+
|
|
123
|
+
try {
|
|
124
|
+
const content = await readFile(skillPath, "utf-8");
|
|
125
|
+
const { frontmatter, body } = parseFrontmatter(content);
|
|
126
|
+
|
|
127
|
+
return {
|
|
128
|
+
meta: {
|
|
129
|
+
id: skillId,
|
|
130
|
+
name: (frontmatter.name as string) || skillId,
|
|
131
|
+
description: (frontmatter.description as string) || "",
|
|
132
|
+
version: ((frontmatter.metadata as Record<string, string>)?.version as string) || "1.0.0",
|
|
133
|
+
author: ((frontmatter.metadata as Record<string, string>)?.author as string) || "mandu",
|
|
134
|
+
},
|
|
135
|
+
content: body,
|
|
136
|
+
};
|
|
137
|
+
} catch {
|
|
138
|
+
return null;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* List rules for a skill
|
|
144
|
+
*/
|
|
145
|
+
export async function listSkillRules(skillId: string): Promise<RuleMeta[]> {
|
|
146
|
+
if (!SKILL_IDS.includes(skillId)) {
|
|
147
|
+
return [];
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
const rulesPath = join(__dirname, skillId, "rules");
|
|
151
|
+
|
|
152
|
+
try {
|
|
153
|
+
const files = await readdir(rulesPath);
|
|
154
|
+
const rules: RuleMeta[] = [];
|
|
155
|
+
|
|
156
|
+
for (const file of files) {
|
|
157
|
+
if (!file.endsWith(".md")) continue;
|
|
158
|
+
|
|
159
|
+
const ruleId = file.replace(".md", "");
|
|
160
|
+
const content = await readFile(join(rulesPath, file), "utf-8");
|
|
161
|
+
const { frontmatter } = parseFrontmatter(content);
|
|
162
|
+
|
|
163
|
+
rules.push({
|
|
164
|
+
id: ruleId,
|
|
165
|
+
title: (frontmatter.title as string) || ruleId,
|
|
166
|
+
impact: (frontmatter.impact as RuleMeta["impact"]) || "MEDIUM",
|
|
167
|
+
impactDescription: (frontmatter.impactDescription as string) || "",
|
|
168
|
+
tags: Array.isArray(frontmatter.tags)
|
|
169
|
+
? (frontmatter.tags as string[])
|
|
170
|
+
: typeof frontmatter.tags === "string"
|
|
171
|
+
? frontmatter.tags.split(",").map((s: string) => s.trim())
|
|
172
|
+
: [],
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// Sort by impact priority
|
|
177
|
+
const impactOrder = { CRITICAL: 0, HIGH: 1, MEDIUM: 2, LOW: 3 };
|
|
178
|
+
return rules.sort((a, b) => impactOrder[a.impact] - impactOrder[b.impact]);
|
|
179
|
+
} catch {
|
|
180
|
+
return [];
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Get a specific rule's content
|
|
186
|
+
*/
|
|
187
|
+
export async function getSkillRule(
|
|
188
|
+
skillId: string,
|
|
189
|
+
ruleId: string
|
|
190
|
+
): Promise<{ meta: RuleMeta; content: string } | null> {
|
|
191
|
+
if (!SKILL_IDS.includes(skillId)) {
|
|
192
|
+
return null;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
const rulePath = join(__dirname, skillId, "rules", `${ruleId}.md`);
|
|
196
|
+
|
|
197
|
+
try {
|
|
198
|
+
const content = await readFile(rulePath, "utf-8");
|
|
199
|
+
const { frontmatter, body } = parseFrontmatter(content);
|
|
200
|
+
|
|
201
|
+
return {
|
|
202
|
+
meta: {
|
|
203
|
+
id: ruleId,
|
|
204
|
+
title: (frontmatter.title as string) || ruleId,
|
|
205
|
+
impact: (frontmatter.impact as RuleMeta["impact"]) || "MEDIUM",
|
|
206
|
+
impactDescription: (frontmatter.impactDescription as string) || "",
|
|
207
|
+
tags: Array.isArray(frontmatter.tags)
|
|
208
|
+
? (frontmatter.tags as string[])
|
|
209
|
+
: typeof frontmatter.tags === "string"
|
|
210
|
+
? frontmatter.tags.split(",").map((s: string) => s.trim())
|
|
211
|
+
: [],
|
|
212
|
+
},
|
|
213
|
+
content: body,
|
|
214
|
+
};
|
|
215
|
+
} catch {
|
|
216
|
+
return null;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
@@ -1,91 +1,91 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: mandu-composition
|
|
3
|
-
description: |
|
|
4
|
-
React composition patterns for Mandu applications. Use when designing
|
|
5
|
-
Island components, managing shared state, or building reusable component
|
|
6
|
-
APIs. Triggers on compound components, context providers, boolean props,
|
|
7
|
-
or component architecture tasks.
|
|
8
|
-
license: MIT
|
|
9
|
-
metadata:
|
|
10
|
-
author: mandu
|
|
11
|
-
version: "1.0.0"
|
|
12
|
-
---
|
|
13
|
-
|
|
14
|
-
# Mandu Composition
|
|
15
|
-
|
|
16
|
-
Mandu 애플리케이션을 위한 React 컴포지션 패턴 가이드. Island 컴파운드 컴포넌트, 상태 관리 인터페이스, Provider 패턴, slot-client 분리를 다룹니다. Vercel의 Composition Patterns를 Mandu 컨텍스트로 변환하여 적용합니다.
|
|
17
|
-
|
|
18
|
-
## When to Apply
|
|
19
|
-
|
|
20
|
-
Reference these guidelines when:
|
|
21
|
-
- Designing Island component architecture
|
|
22
|
-
- Managing shared state between Islands
|
|
23
|
-
- Building reusable component APIs
|
|
24
|
-
- Refactoring components with boolean prop proliferation
|
|
25
|
-
- Working with compound components or context providers
|
|
26
|
-
|
|
27
|
-
## Rule Categories by Priority
|
|
28
|
-
|
|
29
|
-
| Priority | Category | Impact | Prefix |
|
|
30
|
-
|----------|----------|--------|--------|
|
|
31
|
-
| 1 | Component Architecture | HIGH | `comp-arch-` |
|
|
32
|
-
| 2 | State Management | HIGH | `comp-state-` |
|
|
33
|
-
| 3 | Island Patterns | MEDIUM | `comp-island-` |
|
|
34
|
-
| 4 | Implementation Patterns | MEDIUM | `comp-pattern-` |
|
|
35
|
-
|
|
36
|
-
## Quick Reference
|
|
37
|
-
|
|
38
|
-
### 1. Component Architecture (HIGH)
|
|
39
|
-
|
|
40
|
-
- `comp-arch-avoid-boolean-props` - Use composition instead of boolean customization
|
|
41
|
-
- `comp-arch-compound-components` - Structure Islands as compound components
|
|
42
|
-
- `comp-arch-explicit-variants` - Create explicit variant components
|
|
43
|
-
|
|
44
|
-
### 2. State Management (HIGH)
|
|
45
|
-
|
|
46
|
-
- `comp-state-context-interface` - Define generic state/actions/meta interface
|
|
47
|
-
- `comp-state-lift-state` - Move state into provider for sibling access
|
|
48
|
-
- `comp-state-decouple-impl` - Provider is the only place knowing implementation
|
|
49
|
-
|
|
50
|
-
### 3. Island Patterns (MEDIUM)
|
|
51
|
-
|
|
52
|
-
- `comp-island-compound` - Compose Islands with shared context
|
|
53
|
-
- `comp-island-event` - Communicate between Islands with useIslandEvent
|
|
54
|
-
- `comp-island-slot-split` - Separate server logic (slot) from client (Island)
|
|
55
|
-
|
|
56
|
-
### 4. Implementation Patterns (MEDIUM)
|
|
57
|
-
|
|
58
|
-
- `comp-pattern-children` - Use children for composition over render props
|
|
59
|
-
- `comp-pattern-provider-boundary` - Understand provider boundary vs visual nesting
|
|
60
|
-
|
|
61
|
-
## Core Principle
|
|
62
|
-
|
|
63
|
-
**Lift state, compose internals, make state dependency-injectable.**
|
|
64
|
-
|
|
65
|
-
```
|
|
66
|
-
┌─────────────────────────────────────────┐
|
|
67
|
-
│ Provider (state + actions + meta) │
|
|
68
|
-
│ ┌───────────────────────────────────┐ │
|
|
69
|
-
│ │ Composer.Frame │ │
|
|
70
|
-
│ │ ┌─────────┐ ┌─────────────────┐ │ │
|
|
71
|
-
│ │ │ Input │ │ Footer │ │ │
|
|
72
|
-
│ │ └─────────┘ │ ┌─────┐ ┌─────┐ │ │ │
|
|
73
|
-
│ │ │ │Emoji│ │Send │ │ │ │
|
|
74
|
-
│ │ │ └─────┘ └─────┘ │ │ │
|
|
75
|
-
│ │ └─────────────────┘ │ │
|
|
76
|
-
│ └───────────────────────────────────┘ │
|
|
77
|
-
│ ┌───────────────┐ ← Outside Frame │
|
|
78
|
-
│ │ Preview │ but inside │
|
|
79
|
-
│ └───────────────┘ Provider! │
|
|
80
|
-
└─────────────────────────────────────────┘
|
|
81
|
-
```
|
|
82
|
-
|
|
83
|
-
## How to Use
|
|
84
|
-
|
|
85
|
-
Read individual rule files for detailed explanations:
|
|
86
|
-
|
|
87
|
-
```
|
|
88
|
-
rules/comp-arch-compound-components.md
|
|
89
|
-
rules/comp-state-context-interface.md
|
|
90
|
-
rules/comp-island-event.md
|
|
91
|
-
```
|
|
1
|
+
---
|
|
2
|
+
name: mandu-composition
|
|
3
|
+
description: |
|
|
4
|
+
React composition patterns for Mandu applications. Use when designing
|
|
5
|
+
Island components, managing shared state, or building reusable component
|
|
6
|
+
APIs. Triggers on compound components, context providers, boolean props,
|
|
7
|
+
or component architecture tasks.
|
|
8
|
+
license: MIT
|
|
9
|
+
metadata:
|
|
10
|
+
author: mandu
|
|
11
|
+
version: "1.0.0"
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
# Mandu Composition
|
|
15
|
+
|
|
16
|
+
Mandu 애플리케이션을 위한 React 컴포지션 패턴 가이드. Island 컴파운드 컴포넌트, 상태 관리 인터페이스, Provider 패턴, slot-client 분리를 다룹니다. Vercel의 Composition Patterns를 Mandu 컨텍스트로 변환하여 적용합니다.
|
|
17
|
+
|
|
18
|
+
## When to Apply
|
|
19
|
+
|
|
20
|
+
Reference these guidelines when:
|
|
21
|
+
- Designing Island component architecture
|
|
22
|
+
- Managing shared state between Islands
|
|
23
|
+
- Building reusable component APIs
|
|
24
|
+
- Refactoring components with boolean prop proliferation
|
|
25
|
+
- Working with compound components or context providers
|
|
26
|
+
|
|
27
|
+
## Rule Categories by Priority
|
|
28
|
+
|
|
29
|
+
| Priority | Category | Impact | Prefix |
|
|
30
|
+
|----------|----------|--------|--------|
|
|
31
|
+
| 1 | Component Architecture | HIGH | `comp-arch-` |
|
|
32
|
+
| 2 | State Management | HIGH | `comp-state-` |
|
|
33
|
+
| 3 | Island Patterns | MEDIUM | `comp-island-` |
|
|
34
|
+
| 4 | Implementation Patterns | MEDIUM | `comp-pattern-` |
|
|
35
|
+
|
|
36
|
+
## Quick Reference
|
|
37
|
+
|
|
38
|
+
### 1. Component Architecture (HIGH)
|
|
39
|
+
|
|
40
|
+
- `comp-arch-avoid-boolean-props` - Use composition instead of boolean customization
|
|
41
|
+
- `comp-arch-compound-components` - Structure Islands as compound components
|
|
42
|
+
- `comp-arch-explicit-variants` - Create explicit variant components
|
|
43
|
+
|
|
44
|
+
### 2. State Management (HIGH)
|
|
45
|
+
|
|
46
|
+
- `comp-state-context-interface` - Define generic state/actions/meta interface
|
|
47
|
+
- `comp-state-lift-state` - Move state into provider for sibling access
|
|
48
|
+
- `comp-state-decouple-impl` - Provider is the only place knowing implementation
|
|
49
|
+
|
|
50
|
+
### 3. Island Patterns (MEDIUM)
|
|
51
|
+
|
|
52
|
+
- `comp-island-compound` - Compose Islands with shared context
|
|
53
|
+
- `comp-island-event` - Communicate between Islands with useIslandEvent
|
|
54
|
+
- `comp-island-slot-split` - Separate server logic (slot) from client (Island)
|
|
55
|
+
|
|
56
|
+
### 4. Implementation Patterns (MEDIUM)
|
|
57
|
+
|
|
58
|
+
- `comp-pattern-children` - Use children for composition over render props
|
|
59
|
+
- `comp-pattern-provider-boundary` - Understand provider boundary vs visual nesting
|
|
60
|
+
|
|
61
|
+
## Core Principle
|
|
62
|
+
|
|
63
|
+
**Lift state, compose internals, make state dependency-injectable.**
|
|
64
|
+
|
|
65
|
+
```
|
|
66
|
+
┌─────────────────────────────────────────┐
|
|
67
|
+
│ Provider (state + actions + meta) │
|
|
68
|
+
│ ┌───────────────────────────────────┐ │
|
|
69
|
+
│ │ Composer.Frame │ │
|
|
70
|
+
│ │ ┌─────────┐ ┌─────────────────┐ │ │
|
|
71
|
+
│ │ │ Input │ │ Footer │ │ │
|
|
72
|
+
│ │ └─────────┘ │ ┌─────┐ ┌─────┐ │ │ │
|
|
73
|
+
│ │ │ │Emoji│ │Send │ │ │ │
|
|
74
|
+
│ │ │ └─────┘ └─────┘ │ │ │
|
|
75
|
+
│ │ └─────────────────┘ │ │
|
|
76
|
+
│ └───────────────────────────────────┘ │
|
|
77
|
+
│ ┌───────────────┐ ← Outside Frame │
|
|
78
|
+
│ │ Preview │ but inside │
|
|
79
|
+
│ └───────────────┘ Provider! │
|
|
80
|
+
└─────────────────────────────────────────┘
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## How to Use
|
|
84
|
+
|
|
85
|
+
Read individual rule files for detailed explanations:
|
|
86
|
+
|
|
87
|
+
```
|
|
88
|
+
rules/comp-arch-compound-components.md
|
|
89
|
+
rules/comp-state-context-interface.md
|
|
90
|
+
rules/comp-island-event.md
|
|
91
|
+
```
|