loor-cli 0.1.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 +307 -0
- package/dist/bin.d.ts +1 -0
- package/dist/bin.js +28 -0
- package/dist/chunk-AUVNDYJL.js +26 -0
- package/dist/chunk-E6WOLYO3.js +289 -0
- package/dist/cli-PUXM5F6I.js +1083 -0
- package/dist/server-R2ZGYYBT.js +954 -0
- package/package.json +37 -0
|
@@ -0,0 +1,954 @@
|
|
|
1
|
+
import {
|
|
2
|
+
PackageRegistry,
|
|
3
|
+
ensureCache,
|
|
4
|
+
getPackagesDir
|
|
5
|
+
} from "./chunk-E6WOLYO3.js";
|
|
6
|
+
import "./chunk-AUVNDYJL.js";
|
|
7
|
+
|
|
8
|
+
// src/mcp/server.ts
|
|
9
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
10
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
11
|
+
|
|
12
|
+
// src/mcp/tools/listPackages.ts
|
|
13
|
+
import { z } from "zod";
|
|
14
|
+
function registerListPackages(server, registry) {
|
|
15
|
+
server.tool(
|
|
16
|
+
"list_packages",
|
|
17
|
+
"List all available loor packages with descriptions, categories, and compatibility info",
|
|
18
|
+
{
|
|
19
|
+
category: z.enum(["core", "server", "client", "config"]).optional().describe("Filter by category"),
|
|
20
|
+
compatibility: z.enum(["api", "client"]).optional().describe("Filter by app compatibility")
|
|
21
|
+
},
|
|
22
|
+
async ({ category, compatibility }) => {
|
|
23
|
+
let packages = registry.getAll();
|
|
24
|
+
if (category) {
|
|
25
|
+
packages = packages.filter((p) => p.category === category);
|
|
26
|
+
}
|
|
27
|
+
if (compatibility) {
|
|
28
|
+
packages = packages.filter((p) => p.compatibility.includes(compatibility));
|
|
29
|
+
}
|
|
30
|
+
const result = packages.map((p) => ({
|
|
31
|
+
name: p.name,
|
|
32
|
+
description: p.description,
|
|
33
|
+
category: p.category,
|
|
34
|
+
compatibility: p.compatibility,
|
|
35
|
+
requires: p.requires
|
|
36
|
+
}));
|
|
37
|
+
return {
|
|
38
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// src/mcp/tools/getPackageGuide.ts
|
|
45
|
+
import path from "path";
|
|
46
|
+
import fs from "fs-extra";
|
|
47
|
+
import { z as z2 } from "zod";
|
|
48
|
+
function registerGetPackageGuide(server, registry) {
|
|
49
|
+
server.tool(
|
|
50
|
+
"get_package_guide",
|
|
51
|
+
"Get detailed guide for a specific loor package including patterns, donts, architecture docs, and infra info",
|
|
52
|
+
{
|
|
53
|
+
name: z2.string().describe('Package name (e.g. "auth", "mediaKit", "expressRouteKit")')
|
|
54
|
+
},
|
|
55
|
+
async ({ name }) => {
|
|
56
|
+
const manifest = registry.get(name);
|
|
57
|
+
if (!manifest) {
|
|
58
|
+
return {
|
|
59
|
+
content: [{ type: "text", text: `Package "${name}" not found. Use list_packages to see available packages.` }],
|
|
60
|
+
isError: true
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
const sections = [];
|
|
64
|
+
sections.push(`# ${manifest.name}`);
|
|
65
|
+
sections.push(`${manifest.description}`);
|
|
66
|
+
sections.push(`**Category:** ${manifest.category} | **Compatibility:** ${manifest.compatibility.join(", ")}`);
|
|
67
|
+
if (manifest.requires.length > 0) {
|
|
68
|
+
sections.push(`**Requires:** ${manifest.requires.join(", ")}`);
|
|
69
|
+
}
|
|
70
|
+
if (manifest.ai?.patterns?.length) {
|
|
71
|
+
sections.push("\n## DO");
|
|
72
|
+
for (const p of manifest.ai.patterns) {
|
|
73
|
+
sections.push(`- ${p}`);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
if (manifest.ai?.donts?.length) {
|
|
77
|
+
sections.push("\n## DON'T");
|
|
78
|
+
for (const d of manifest.ai.donts) {
|
|
79
|
+
sections.push(`- ${d}`);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
if (manifest.apiInfra?.length) {
|
|
83
|
+
sections.push("\n## API Infrastructure Files");
|
|
84
|
+
for (const infra of manifest.apiInfra) {
|
|
85
|
+
sections.push(`- \`${infra.path}\` \u2014 ${infra.description}`);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
if (manifest.clientSetup?.length) {
|
|
89
|
+
sections.push("\n## Client Setup Files");
|
|
90
|
+
for (const setup of manifest.clientSetup) {
|
|
91
|
+
sections.push(`- \`${setup.path}\` \u2014 ${setup.description}`);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
if (manifest.envVars?.length) {
|
|
95
|
+
sections.push("\n## Environment Variables");
|
|
96
|
+
for (const env of manifest.envVars) {
|
|
97
|
+
const req = env.required ? "(required)" : "(optional)";
|
|
98
|
+
sections.push(`- \`${env.key}\` ${req} \u2014 ${env.description}`);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
const pkgDir = path.join(registry.getPackagesDir(), name);
|
|
102
|
+
const archDocPath = manifest.ai?.guide ? path.join(pkgDir, manifest.ai.guide) : path.join(pkgDir, "docs", "architecture.md");
|
|
103
|
+
if (await fs.pathExists(archDocPath)) {
|
|
104
|
+
const archDoc = await fs.readFile(archDocPath, "utf-8");
|
|
105
|
+
sections.push("\n## Architecture Guide");
|
|
106
|
+
sections.push(archDoc);
|
|
107
|
+
}
|
|
108
|
+
const readmePath = path.join(pkgDir, "README.md");
|
|
109
|
+
if (await fs.pathExists(readmePath)) {
|
|
110
|
+
const readme = await fs.readFile(readmePath, "utf-8");
|
|
111
|
+
sections.push("\n## README");
|
|
112
|
+
sections.push(readme);
|
|
113
|
+
}
|
|
114
|
+
return {
|
|
115
|
+
content: [{ type: "text", text: sections.join("\n") }]
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// src/mcp/tools/getConventions.ts
|
|
122
|
+
import { z as z3 } from "zod";
|
|
123
|
+
|
|
124
|
+
// src/mcp/context/conventions.ts
|
|
125
|
+
import path2 from "path";
|
|
126
|
+
import fs2 from "fs-extra";
|
|
127
|
+
async function loadConventions(packagesDir) {
|
|
128
|
+
const docsDir = path2.join(packagesDir, "..", "docs");
|
|
129
|
+
const packageStandardPath = path2.join(docsDir, "PACKAGE_STANDARD.md");
|
|
130
|
+
const packageArchPath = path2.join(docsDir, "PACKAGE_ARCHITECTURE.md");
|
|
131
|
+
const packageStandard = await fs2.pathExists(packageStandardPath) ? await fs2.readFile(packageStandardPath, "utf-8") : "";
|
|
132
|
+
const packageArch = await fs2.pathExists(packageArchPath) ? await fs2.readFile(packageArchPath, "utf-8") : "";
|
|
133
|
+
return {
|
|
134
|
+
architecture: packageArch || getFallbackArchitecture(),
|
|
135
|
+
packageStructure: packageStandard || getFallbackPackageStructure(),
|
|
136
|
+
routePattern: getRoutePatternConventions(),
|
|
137
|
+
featureDev: getFeatureDevConventions()
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
function getFallbackArchitecture() {
|
|
141
|
+
return `# Architecture Conventions
|
|
142
|
+
|
|
143
|
+
## Hexagonal Architecture (Ports & Adapters)
|
|
144
|
+
|
|
145
|
+
- **Packages** define abstract ports (interfaces) in \`ports/\` folder
|
|
146
|
+
- **Apps** implement adapters in \`infra/\` folder
|
|
147
|
+
- Factory functions accept ports as dependencies for DI
|
|
148
|
+
|
|
149
|
+
## Package Exports
|
|
150
|
+
|
|
151
|
+
\`\`\`
|
|
152
|
+
packages/<name>/src/
|
|
153
|
+
\u251C\u2500\u2500 index.ts # Pure exports (browser, RN, Edge compatible)
|
|
154
|
+
\u251C\u2500\u2500 server/index.ts # Node.js only exports
|
|
155
|
+
\u2514\u2500\u2500 client/index.ts # Browser only exports (optional)
|
|
156
|
+
\`\`\`
|
|
157
|
+
|
|
158
|
+
- Use \`Uint8Array\` instead of \`Buffer\` for universal compatibility
|
|
159
|
+
- Keep \`index.ts\` free of Node.js-specific imports (fs, streams, etc.)`;
|
|
160
|
+
}
|
|
161
|
+
function getFallbackPackageStructure() {
|
|
162
|
+
return `# Package Structure Conventions
|
|
163
|
+
|
|
164
|
+
## Directory Layout
|
|
165
|
+
|
|
166
|
+
\`\`\`
|
|
167
|
+
packages/<name>/
|
|
168
|
+
\u251C\u2500\u2500 src/
|
|
169
|
+
\u2502 \u251C\u2500\u2500 index.ts # Pure exports
|
|
170
|
+
\u2502 \u251C\u2500\u2500 server/index.ts # Node.js adapters
|
|
171
|
+
\u2502 \u251C\u2500\u2500 client/index.ts # Browser adapters
|
|
172
|
+
\u2502 \u251C\u2500\u2500 core/ # Business logic
|
|
173
|
+
\u2502 \u251C\u2500\u2500 ports/ # Interface definitions
|
|
174
|
+
\u2502 \u2514\u2500\u2500 adapters/ # Adapter implementations
|
|
175
|
+
\u251C\u2500\u2500 docs/
|
|
176
|
+
\u2502 \u2514\u2500\u2500 architecture.md # Package design docs
|
|
177
|
+
\u251C\u2500\u2500 loor.json # Package manifest
|
|
178
|
+
\u251C\u2500\u2500 package.json
|
|
179
|
+
\u2514\u2500\u2500 tsconfig.json
|
|
180
|
+
\`\`\`
|
|
181
|
+
|
|
182
|
+
## Key Rules
|
|
183
|
+
|
|
184
|
+
1. Ports define interfaces, adapters implement them
|
|
185
|
+
2. Core logic depends only on ports, never on adapters
|
|
186
|
+
3. Apps wire adapters in \`src/infra/\` directory`;
|
|
187
|
+
}
|
|
188
|
+
function getRoutePatternConventions() {
|
|
189
|
+
return `# Route Pattern Conventions
|
|
190
|
+
|
|
191
|
+
## File-System Route Discovery
|
|
192
|
+
|
|
193
|
+
Routes are placed in \`src/routes/<domain>/<action>/index.ts\` and auto-discovered by routerLoader.
|
|
194
|
+
|
|
195
|
+
## RouteProvider Pattern
|
|
196
|
+
|
|
197
|
+
Each route extends \`RouteProvider\` and implements:
|
|
198
|
+
- \`method\`: HTTP method (GET, POST, PUT, DELETE)
|
|
199
|
+
- \`path\`: URL path with params (e.g. \`/admin/creators/:id\`)
|
|
200
|
+
- \`middleware\`: Array of Express middleware (auth, role check)
|
|
201
|
+
- \`validate(req)\`: Zod schema validation, returns typed data
|
|
202
|
+
- \`handle(req, res, data)\`: Business logic with validated data
|
|
203
|
+
|
|
204
|
+
## Response Helpers
|
|
205
|
+
|
|
206
|
+
Use \`sendSuccess(res, data)\` and \`sendError(res, error)\` from expressRouteKit.
|
|
207
|
+
|
|
208
|
+
## Example Structure
|
|
209
|
+
|
|
210
|
+
\`\`\`
|
|
211
|
+
src/routes/
|
|
212
|
+
\u251C\u2500\u2500 admin/
|
|
213
|
+
\u2502 \u251C\u2500\u2500 users/
|
|
214
|
+
\u2502 \u2502 \u251C\u2500\u2500 list/index.ts # GET /admin/users
|
|
215
|
+
\u2502 \u2502 \u251C\u2500\u2500 get/index.ts # GET /admin/users/:id
|
|
216
|
+
\u2502 \u2502 \u251C\u2500\u2500 create/index.ts # POST /admin/users
|
|
217
|
+
\u2502 \u2502 \u2514\u2500\u2500 delete/index.ts # DELETE /admin/users/:id
|
|
218
|
+
\u2502 \u2514\u2500\u2500 creators/
|
|
219
|
+
\u2502 \u251C\u2500\u2500 list/index.ts
|
|
220
|
+
\u2502 \u2514\u2500\u2500 approve/index.ts
|
|
221
|
+
\u251C\u2500\u2500 auth/
|
|
222
|
+
\u2502 \u2514\u2500\u2500 admin/
|
|
223
|
+
\u2502 \u251C\u2500\u2500 login/index.ts
|
|
224
|
+
\u2502 \u2514\u2500\u2500 refreshToken/index.ts
|
|
225
|
+
\u2514\u2500\u2500 media/
|
|
226
|
+
\u251C\u2500\u2500 image/upload/index.ts
|
|
227
|
+
\u2514\u2500\u2500 image/download/index.ts
|
|
228
|
+
\`\`\``;
|
|
229
|
+
}
|
|
230
|
+
function getFeatureDevConventions() {
|
|
231
|
+
return `# Feature Development Conventions (Client Apps)
|
|
232
|
+
|
|
233
|
+
## Feature Folder Structure
|
|
234
|
+
|
|
235
|
+
\`\`\`
|
|
236
|
+
src/features/<featureName>/
|
|
237
|
+
\u251C\u2500\u2500 index.tsx # Main page/component
|
|
238
|
+
\u251C\u2500\u2500 components/
|
|
239
|
+
\u2502 \u251C\u2500\u2500 columns.tsx # DataTable column definitions
|
|
240
|
+
\u2502 \u251C\u2500\u2500 dataTableToolbar.tsx # Search/filter toolbar
|
|
241
|
+
\u2502 \u2514\u2500\u2500 selectionActionBar.tsx # Bulk action bar
|
|
242
|
+
\u251C\u2500\u2500 hooks/
|
|
243
|
+
\u2502 \u251C\u2500\u2500 use<Feature>Form.ts # Form logic (react-hook-form + zod)
|
|
244
|
+
\u2502 \u2514\u2500\u2500 useDelete<Feature>.ts # Delete mutation hook
|
|
245
|
+
\u251C\u2500\u2500 types/
|
|
246
|
+
\u2502 \u2514\u2500\u2500 index.ts # Feature-specific types
|
|
247
|
+
\u251C\u2500\u2500 validations/
|
|
248
|
+
\u2502 \u2514\u2500\u2500 index.ts # Zod schemas
|
|
249
|
+
\u2514\u2500\u2500 locales/
|
|
250
|
+
\u251C\u2500\u2500 en.ts # English translations
|
|
251
|
+
\u2514\u2500\u2500 ar.ts # Arabic translations
|
|
252
|
+
\`\`\`
|
|
253
|
+
|
|
254
|
+
## Key Patterns
|
|
255
|
+
|
|
256
|
+
1. Each feature is self-contained with its own components, hooks, types, locales
|
|
257
|
+
2. DataTable pattern: columns.tsx + dataTableToolbar.tsx + selectionActionBar.tsx
|
|
258
|
+
3. Form pattern: useForm hook wrapping react-hook-form with zodResolver
|
|
259
|
+
4. API calls through RTK Query hooks from apiClient services
|
|
260
|
+
5. All user-facing strings via i18n translation keys`;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
// src/mcp/tools/getConventions.ts
|
|
264
|
+
function registerGetConventions(server, registry) {
|
|
265
|
+
server.tool(
|
|
266
|
+
"get_conventions",
|
|
267
|
+
"Get loor project conventions for architecture, package structure, route patterns, and feature development",
|
|
268
|
+
{
|
|
269
|
+
topic: z3.enum(["all", "architecture", "package_structure", "route_pattern", "feature_dev"]).optional().default("all").describe("Convention topic to retrieve")
|
|
270
|
+
},
|
|
271
|
+
async ({ topic }) => {
|
|
272
|
+
const conventions = await loadConventions(registry.getPackagesDir());
|
|
273
|
+
const sections = [];
|
|
274
|
+
if (topic === "all" || topic === "architecture") {
|
|
275
|
+
sections.push(conventions.architecture);
|
|
276
|
+
}
|
|
277
|
+
if (topic === "all" || topic === "package_structure") {
|
|
278
|
+
sections.push(conventions.packageStructure);
|
|
279
|
+
}
|
|
280
|
+
if (topic === "all" || topic === "route_pattern") {
|
|
281
|
+
sections.push(conventions.routePattern);
|
|
282
|
+
}
|
|
283
|
+
if (topic === "all" || topic === "feature_dev") {
|
|
284
|
+
sections.push(conventions.featureDev);
|
|
285
|
+
}
|
|
286
|
+
return {
|
|
287
|
+
content: [{ type: "text", text: sections.join("\n\n---\n\n") }]
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
);
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
// src/mcp/tools/howTo.ts
|
|
294
|
+
import { z as z4 } from "zod";
|
|
295
|
+
|
|
296
|
+
// src/mcp/context/howToGuides.ts
|
|
297
|
+
var guides = {
|
|
298
|
+
create_project: `# How To: Create a New Project
|
|
299
|
+
|
|
300
|
+
## Interactive Mode
|
|
301
|
+
|
|
302
|
+
\`\`\`bash
|
|
303
|
+
loor init
|
|
304
|
+
\`\`\`
|
|
305
|
+
|
|
306
|
+
Prompts for: project name, app types, app names, and optional packages.
|
|
307
|
+
|
|
308
|
+
## Non-Interactive Mode (CI / Scripted)
|
|
309
|
+
|
|
310
|
+
\`\`\`bash
|
|
311
|
+
loor init my-saas --apps api:backend,web-client:dashboard --packages mediaKit,notification
|
|
312
|
+
\`\`\`
|
|
313
|
+
|
|
314
|
+
## --apps Flag Format
|
|
315
|
+
|
|
316
|
+
\`type\` or \`type:name\`, comma-separated:
|
|
317
|
+
- \`--apps api\` \u2192 API app with default name "api"
|
|
318
|
+
- \`--apps api:backend\` \u2192 API app named "backend"
|
|
319
|
+
- \`--apps api,web-client\` \u2192 API "api" + web-client "admin" (defaults)
|
|
320
|
+
- \`--apps api:backend,web-client:dash,web-client:portal\` \u2192 multiple apps
|
|
321
|
+
|
|
322
|
+
Valid types: \`api\`, \`web-client\`
|
|
323
|
+
|
|
324
|
+
## --packages Flag Format
|
|
325
|
+
|
|
326
|
+
- \`--packages mediaKit,notification\` \u2192 include these optional packages
|
|
327
|
+
- \`--no-packages\` \u2192 skip optional packages (only required ones installed)
|
|
328
|
+
- *(omit flag)* \u2192 prompted interactively
|
|
329
|
+
|
|
330
|
+
## Partial Flag Usage
|
|
331
|
+
|
|
332
|
+
Each flag independently skips its prompt:
|
|
333
|
+
- Only \`--apps\` \u2192 packages still prompted
|
|
334
|
+
- Only \`--packages\` \u2192 apps still prompted
|
|
335
|
+
- Both \u2192 fully non-interactive
|
|
336
|
+
|
|
337
|
+
## After Creation
|
|
338
|
+
|
|
339
|
+
\`\`\`bash
|
|
340
|
+
cd my-saas
|
|
341
|
+
pnpm install
|
|
342
|
+
pnpm dev
|
|
343
|
+
\`\`\`
|
|
344
|
+
|
|
345
|
+
## Adding More Apps Later
|
|
346
|
+
|
|
347
|
+
\`\`\`bash
|
|
348
|
+
loor add app web-client portal
|
|
349
|
+
pnpm install
|
|
350
|
+
\`\`\`
|
|
351
|
+
|
|
352
|
+
## Adding More Packages Later
|
|
353
|
+
|
|
354
|
+
\`\`\`bash
|
|
355
|
+
loor add package scheduler
|
|
356
|
+
pnpm install
|
|
357
|
+
\`\`\``,
|
|
358
|
+
add_api_route: `# How To: Add an API Route
|
|
359
|
+
|
|
360
|
+
## Steps
|
|
361
|
+
|
|
362
|
+
### 1. Create the route file
|
|
363
|
+
Create \`src/routes/<domain>/<action>/index.ts\` in your API app.
|
|
364
|
+
|
|
365
|
+
### 2. Extend RouteProvider
|
|
366
|
+
\`\`\`typescript
|
|
367
|
+
import { RouteProvider, sendSuccess, sendError } from '@workspace/expressRouteKit';
|
|
368
|
+
import { z } from 'zod';
|
|
369
|
+
|
|
370
|
+
const schema = z.object({
|
|
371
|
+
// define request validation
|
|
372
|
+
});
|
|
373
|
+
|
|
374
|
+
export default class extends RouteProvider {
|
|
375
|
+
method = 'post' as const;
|
|
376
|
+
path = '/domain/action';
|
|
377
|
+
middleware = []; // add auth, role middleware as needed
|
|
378
|
+
|
|
379
|
+
validate(req: Request) {
|
|
380
|
+
return schema.parse(req.body);
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
async handle(req: Request, res: Response, data: z.infer<typeof schema>) {
|
|
384
|
+
// business logic
|
|
385
|
+
sendSuccess(res, { result });
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
\`\`\`
|
|
389
|
+
|
|
390
|
+
### 3. Add middleware (if protected)
|
|
391
|
+
\`\`\`typescript
|
|
392
|
+
import { bearerAuthMiddleware } from '@/infra/tokenService';
|
|
393
|
+
import { roleMiddleware } from '@workspace/auth/server';
|
|
394
|
+
|
|
395
|
+
middleware = [bearerAuthMiddleware, roleMiddleware(['admin'])];
|
|
396
|
+
\`\`\`
|
|
397
|
+
|
|
398
|
+
### 4. Test
|
|
399
|
+
The route is auto-discovered by routerLoader. Restart the server and test the endpoint.
|
|
400
|
+
|
|
401
|
+
## Key Points
|
|
402
|
+
- Routes are auto-discovered from the file system \u2014 no manual registration needed
|
|
403
|
+
- Always validate input with Zod in \`validate()\`
|
|
404
|
+
- Use \`sendSuccess()\` and \`sendError()\` for consistent response format
|
|
405
|
+
- Add rate limiting for public endpoints`,
|
|
406
|
+
add_feature: `# How To: Add a Client Feature
|
|
407
|
+
|
|
408
|
+
## Steps
|
|
409
|
+
|
|
410
|
+
### 1. Create feature directory
|
|
411
|
+
\`\`\`
|
|
412
|
+
src/features/<featureName>/
|
|
413
|
+
\u251C\u2500\u2500 index.tsx
|
|
414
|
+
\u251C\u2500\u2500 components/
|
|
415
|
+
\u251C\u2500\u2500 hooks/
|
|
416
|
+
\u251C\u2500\u2500 types/
|
|
417
|
+
\u251C\u2500\u2500 validations/
|
|
418
|
+
\u2514\u2500\u2500 locales/
|
|
419
|
+
\u251C\u2500\u2500 en.ts
|
|
420
|
+
\u2514\u2500\u2500 ar.ts
|
|
421
|
+
\`\`\`
|
|
422
|
+
|
|
423
|
+
### 2. Define types
|
|
424
|
+
\`\`\`typescript
|
|
425
|
+
// types/index.ts
|
|
426
|
+
export interface MyEntity {
|
|
427
|
+
id: string;
|
|
428
|
+
name: string;
|
|
429
|
+
// ...
|
|
430
|
+
}
|
|
431
|
+
\`\`\`
|
|
432
|
+
|
|
433
|
+
### 3. Create API service (if needed)
|
|
434
|
+
\`\`\`typescript
|
|
435
|
+
// In src/api/services/<domain>.ts
|
|
436
|
+
import { baseApi } from '../baseApi';
|
|
437
|
+
|
|
438
|
+
const api = baseApi.injectEndpoints({
|
|
439
|
+
endpoints: (build) => ({
|
|
440
|
+
listEntities: build.query({ query: () => '/entities' }),
|
|
441
|
+
createEntity: build.mutation({ query: (body) => ({ url: '/entities', method: 'POST', body }) }),
|
|
442
|
+
}),
|
|
443
|
+
});
|
|
444
|
+
|
|
445
|
+
export const { useListEntitiesQuery, useCreateEntityMutation } = api;
|
|
446
|
+
\`\`\`
|
|
447
|
+
|
|
448
|
+
### 4. Build the page component
|
|
449
|
+
\`\`\`typescript
|
|
450
|
+
// index.tsx
|
|
451
|
+
import { DataTable } from '@workspace/ui';
|
|
452
|
+
import { columns } from './components/columns';
|
|
453
|
+
|
|
454
|
+
export default function MyFeaturePage() {
|
|
455
|
+
const { data } = useListEntitiesQuery();
|
|
456
|
+
return <DataTable columns={columns} data={data ?? []} />;
|
|
457
|
+
}
|
|
458
|
+
\`\`\`
|
|
459
|
+
|
|
460
|
+
### 5. Add route
|
|
461
|
+
Register the page in your router config with appropriate auth guards.
|
|
462
|
+
|
|
463
|
+
### 6. Add translations
|
|
464
|
+
Add \`en.ts\` and \`ar.ts\` locale files and register them with i18n.
|
|
465
|
+
|
|
466
|
+
## Key Points
|
|
467
|
+
- Each feature is self-contained
|
|
468
|
+
- Use DataTable for list views with sorting, filtering, pagination
|
|
469
|
+
- Use react-hook-form + zod for forms
|
|
470
|
+
- All strings through i18n`,
|
|
471
|
+
add_package: `# How To: Add a Package to a Project
|
|
472
|
+
|
|
473
|
+
## Using CLI (Recommended)
|
|
474
|
+
|
|
475
|
+
\`\`\`bash
|
|
476
|
+
loor add package <name>
|
|
477
|
+
\`\`\`
|
|
478
|
+
|
|
479
|
+
This will:
|
|
480
|
+
1. Resolve transitive dependencies automatically
|
|
481
|
+
2. Copy package source to \`packages/<name>/\`
|
|
482
|
+
3. Generate infra adapters in API apps (\`src/infra/\`)
|
|
483
|
+
4. Generate client setup files in client apps
|
|
484
|
+
5. Add env vars to \`.env.example\`
|
|
485
|
+
6. Merge app dependencies into \`package.json\`
|
|
486
|
+
|
|
487
|
+
## Adding Packages During Project Init
|
|
488
|
+
|
|
489
|
+
You can also include packages at project creation time:
|
|
490
|
+
|
|
491
|
+
\`\`\`bash
|
|
492
|
+
# Interactive selection
|
|
493
|
+
loor init my-project
|
|
494
|
+
|
|
495
|
+
# Via CLI flag (non-interactive)
|
|
496
|
+
loor init my-project --apps api,web-client --packages mediaKit,notification
|
|
497
|
+
|
|
498
|
+
# Skip optional packages entirely
|
|
499
|
+
loor init my-project --apps api --no-packages
|
|
500
|
+
\`\`\`
|
|
501
|
+
|
|
502
|
+
## Manual Steps (if needed)
|
|
503
|
+
|
|
504
|
+
### 1. Copy package
|
|
505
|
+
Copy from the registry to \`packages/<name>/\`.
|
|
506
|
+
|
|
507
|
+
### 2. Wire infrastructure
|
|
508
|
+
For API packages, create adapter files in \`apps/<apiApp>/src/infra/\`:
|
|
509
|
+
\`\`\`typescript
|
|
510
|
+
// Example: src/infra/logger.ts
|
|
511
|
+
import { createPinoLogger } from '@workspace/logger/server';
|
|
512
|
+
import config from '../config';
|
|
513
|
+
|
|
514
|
+
export const logger = createPinoLogger({
|
|
515
|
+
level: config.log.level,
|
|
516
|
+
pretty: config.log.pretty,
|
|
517
|
+
});
|
|
518
|
+
\`\`\`
|
|
519
|
+
|
|
520
|
+
### 3. Add dependencies
|
|
521
|
+
Install the package's \`appDependencies\` in the relevant apps.
|
|
522
|
+
|
|
523
|
+
### 4. Configure env vars
|
|
524
|
+
Add required env vars from the package manifest to \`.env\`.
|
|
525
|
+
|
|
526
|
+
## Key Points
|
|
527
|
+
- Always check \`requires\` field \u2014 install dependencies first
|
|
528
|
+
- Use \`loor list\` to see available packages
|
|
529
|
+
- Run \`pnpm install\` after adding packages
|
|
530
|
+
- Use \`cli_usage\` MCP tool for full CLI reference`,
|
|
531
|
+
add_infra_adapter: `# How To: Add an Infrastructure Adapter
|
|
532
|
+
|
|
533
|
+
## Overview
|
|
534
|
+
Adapters implement package ports (interfaces) with concrete dependencies. They live in \`apps/<appName>/src/infra/\`.
|
|
535
|
+
|
|
536
|
+
## Steps
|
|
537
|
+
|
|
538
|
+
### 1. Identify the port
|
|
539
|
+
Find the port interface in the package's \`src/ports/\` directory:
|
|
540
|
+
\`\`\`typescript
|
|
541
|
+
// packages/logger/src/ports/errorLog.port.ts
|
|
542
|
+
export interface ErrorLogPort {
|
|
543
|
+
error(message: string, meta?: Record<string, unknown>): void;
|
|
544
|
+
warn(message: string, meta?: Record<string, unknown>): void;
|
|
545
|
+
}
|
|
546
|
+
\`\`\`
|
|
547
|
+
|
|
548
|
+
### 2. Create the adapter
|
|
549
|
+
\`\`\`typescript
|
|
550
|
+
// apps/myApi/src/infra/logger.ts
|
|
551
|
+
import { createPinoLogger } from '@workspace/logger/server';
|
|
552
|
+
import config from '../config';
|
|
553
|
+
|
|
554
|
+
export const logger = createPinoLogger({
|
|
555
|
+
level: config.log.level,
|
|
556
|
+
pretty: config.log.pretty,
|
|
557
|
+
});
|
|
558
|
+
\`\`\`
|
|
559
|
+
|
|
560
|
+
### 3. Inject into services
|
|
561
|
+
Pass the adapter as a dependency where needed:
|
|
562
|
+
\`\`\`typescript
|
|
563
|
+
import { logger } from '@/infra/logger';
|
|
564
|
+
import { createAuthService } from '@workspace/auth';
|
|
565
|
+
|
|
566
|
+
export const authService = createAuthService({ logger });
|
|
567
|
+
\`\`\`
|
|
568
|
+
|
|
569
|
+
### 4. Add config/env vars
|
|
570
|
+
If the adapter needs configuration:
|
|
571
|
+
- Add env vars to \`.env.example\`
|
|
572
|
+
- Add config keys to \`src/config/index.ts\`
|
|
573
|
+
|
|
574
|
+
## Naming Convention
|
|
575
|
+
- File: \`src/infra/<adapterName>.ts\`
|
|
576
|
+
- Export: named export matching the port purpose (e.g. \`logger\`, \`tokenService\`, \`mediaStorage\`)
|
|
577
|
+
|
|
578
|
+
## Key Points
|
|
579
|
+
- One adapter file per port implementation
|
|
580
|
+
- Import factory from \`@workspace/<package>/server\`
|
|
581
|
+
- Wire config values from \`src/config/\`
|
|
582
|
+
- Adapters are singletons \u2014 instantiate once, export for reuse`
|
|
583
|
+
};
|
|
584
|
+
function getHowToGuide(task) {
|
|
585
|
+
return guides[task];
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
// src/mcp/tools/howTo.ts
|
|
589
|
+
function registerHowTo(server) {
|
|
590
|
+
server.tool(
|
|
591
|
+
"how_to",
|
|
592
|
+
"Get step-by-step instructions for common loor development tasks",
|
|
593
|
+
{
|
|
594
|
+
task: z4.enum(["create_project", "add_api_route", "add_feature", "add_package", "add_infra_adapter"]).describe("The task you need step-by-step instructions for")
|
|
595
|
+
},
|
|
596
|
+
async ({ task }) => {
|
|
597
|
+
const guide = getHowToGuide(task);
|
|
598
|
+
return {
|
|
599
|
+
content: [{ type: "text", text: guide }]
|
|
600
|
+
};
|
|
601
|
+
}
|
|
602
|
+
);
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
// src/mcp/tools/cliUsage.ts
|
|
606
|
+
import { z as z5 } from "zod";
|
|
607
|
+
var commands = {
|
|
608
|
+
init: `# loor init [name]
|
|
609
|
+
|
|
610
|
+
Create a new loor monorepo project.
|
|
611
|
+
|
|
612
|
+
## Arguments
|
|
613
|
+
|
|
614
|
+
- \`name\` (optional) \u2014 Project name. If omitted, prompted interactively.
|
|
615
|
+
|
|
616
|
+
## Options
|
|
617
|
+
|
|
618
|
+
| Option | Description |
|
|
619
|
+
|--------|-------------|
|
|
620
|
+
| \`--apps <apps>\` | App types with optional names (comma-separated). Format: \`type\` or \`type:name\`. |
|
|
621
|
+
| \`--packages <packages>\` | Optional packages to include (comma-separated). |
|
|
622
|
+
| \`--no-packages\` | Skip optional packages \u2014 only required ones are installed. |
|
|
623
|
+
|
|
624
|
+
## App Types
|
|
625
|
+
|
|
626
|
+
| Type | Description | Default Name |
|
|
627
|
+
|------|-------------|--------------|
|
|
628
|
+
| \`api\` | Express.js + MongoDB REST API | \`api\` |
|
|
629
|
+
| \`web-client\` | React + Vite frontend | \`admin\` |
|
|
630
|
+
|
|
631
|
+
Multiple apps of the same type are allowed (e.g., two web-client apps with different names).
|
|
632
|
+
|
|
633
|
+
## Examples
|
|
634
|
+
|
|
635
|
+
### Fully interactive
|
|
636
|
+
\`\`\`bash
|
|
637
|
+
loor init
|
|
638
|
+
\`\`\`
|
|
639
|
+
|
|
640
|
+
### Fully non-interactive
|
|
641
|
+
\`\`\`bash
|
|
642
|
+
loor init my-saas --apps api:backend,web-client:dashboard --packages mediaKit,notification
|
|
643
|
+
\`\`\`
|
|
644
|
+
|
|
645
|
+
### Custom app names, default packages prompt
|
|
646
|
+
\`\`\`bash
|
|
647
|
+
loor init my-saas --apps api:my-api,web-client:admin
|
|
648
|
+
\`\`\`
|
|
649
|
+
|
|
650
|
+
### Default app names
|
|
651
|
+
\`\`\`bash
|
|
652
|
+
loor init my-saas --apps api,web-client
|
|
653
|
+
# api app \u2192 "api", web-client app \u2192 "admin"
|
|
654
|
+
\`\`\`
|
|
655
|
+
|
|
656
|
+
### Skip optional packages
|
|
657
|
+
\`\`\`bash
|
|
658
|
+
loor init my-saas --apps api,web-client --no-packages
|
|
659
|
+
\`\`\`
|
|
660
|
+
|
|
661
|
+
### Only specify packages (apps prompted interactively)
|
|
662
|
+
\`\`\`bash
|
|
663
|
+
loor init my-saas --packages mediaKit,notification
|
|
664
|
+
\`\`\`
|
|
665
|
+
|
|
666
|
+
## Required Packages (always installed)
|
|
667
|
+
|
|
668
|
+
**API apps:** auth, base, expressRouteKit, logger, eslintConfig, typescriptConfig
|
|
669
|
+
|
|
670
|
+
**Web Client apps:** auth, base, storage, ui, i18n, routerProvider, commandMenu, apiClient, eslintConfig, typescriptConfig
|
|
671
|
+
|
|
672
|
+
## What It Does
|
|
673
|
+
|
|
674
|
+
1. Creates project directory with root configs (turbo.json, pnpm-workspace.yaml, tsconfig.json, etc.)
|
|
675
|
+
2. Generates package.json programmatically
|
|
676
|
+
3. Copies selected app templates from reference apps
|
|
677
|
+
4. Copies all packages, then removes artifacts of unselected packages (subtractive approach)
|
|
678
|
+
5. Resolves transitive package dependencies automatically
|
|
679
|
+
6. Replaces \`@loor/\` scope with \`@<projectName>/\` across the project`,
|
|
680
|
+
add_app: `# loor add app <type> [name]
|
|
681
|
+
|
|
682
|
+
Add a new app to an existing project. Must be run from the project root.
|
|
683
|
+
|
|
684
|
+
## Arguments
|
|
685
|
+
|
|
686
|
+
| Argument | Required | Description |
|
|
687
|
+
|----------|----------|-------------|
|
|
688
|
+
| \`type\` | Yes | App type: \`api\` or \`web-client\` |
|
|
689
|
+
| \`name\` | No | App name. Defaults to \`api\` for API, \`admin\` for web-client. |
|
|
690
|
+
|
|
691
|
+
## Examples
|
|
692
|
+
|
|
693
|
+
\`\`\`bash
|
|
694
|
+
loor add app api
|
|
695
|
+
loor add app api backend
|
|
696
|
+
loor add app web-client dashboard
|
|
697
|
+
loor add app web-client portal
|
|
698
|
+
\`\`\`
|
|
699
|
+
|
|
700
|
+
## What It Does
|
|
701
|
+
|
|
702
|
+
1. Copies reference app template to \`apps/<name>/\`
|
|
703
|
+
2. Installs required packages for the app type (if missing from project)
|
|
704
|
+
3. Removes artifacts of non-installed packages from the new app
|
|
705
|
+
4. You need to run \`pnpm install\` after`,
|
|
706
|
+
add_package: `# loor add package <name>
|
|
707
|
+
|
|
708
|
+
Add a package to an existing project. Must be run from the project root.
|
|
709
|
+
|
|
710
|
+
## Arguments
|
|
711
|
+
|
|
712
|
+
| Argument | Required | Description |
|
|
713
|
+
|----------|----------|-------------|
|
|
714
|
+
| \`name\` | Yes | Package name from the registry |
|
|
715
|
+
|
|
716
|
+
## Examples
|
|
717
|
+
|
|
718
|
+
\`\`\`bash
|
|
719
|
+
loor add package mediaKit
|
|
720
|
+
loor add package notification
|
|
721
|
+
loor add package scheduler
|
|
722
|
+
\`\`\`
|
|
723
|
+
|
|
724
|
+
## What It Does
|
|
725
|
+
|
|
726
|
+
1. Resolves transitive dependencies automatically
|
|
727
|
+
2. Copies package source to \`packages/<name>/\`
|
|
728
|
+
3. Generates infra adapters in API apps (\`src/infra/\`)
|
|
729
|
+
4. Generates client setup files in client apps
|
|
730
|
+
5. Adds env vars to \`.env.example\`
|
|
731
|
+
6. Merges npm dependencies into app \`package.json\` files
|
|
732
|
+
7. Injects config annotation blocks
|
|
733
|
+
|
|
734
|
+
## Tips
|
|
735
|
+
|
|
736
|
+
- Use \`loor list\` to see all available packages
|
|
737
|
+
- Already-installed packages are skipped
|
|
738
|
+
- Run \`pnpm install\` after adding packages`,
|
|
739
|
+
list: `# loor list
|
|
740
|
+
|
|
741
|
+
List all available packages with descriptions, categories, and compatibility info.
|
|
742
|
+
|
|
743
|
+
\`\`\`bash
|
|
744
|
+
loor list
|
|
745
|
+
\`\`\`
|
|
746
|
+
|
|
747
|
+
Packages are organized by category: **core**, **server**, **client**, **config**.
|
|
748
|
+
|
|
749
|
+
Each entry shows: name, description, compatibility (api/client), and required dependencies.`,
|
|
750
|
+
update: `# loor update
|
|
751
|
+
|
|
752
|
+
Force-refresh the package registry from the remote source.
|
|
753
|
+
|
|
754
|
+
\`\`\`bash
|
|
755
|
+
loor update
|
|
756
|
+
\`\`\`
|
|
757
|
+
|
|
758
|
+
The registry is cached locally (\`~/.loor/registry/\`) with a 24-hour TTL. This command forces a re-download regardless of cache age.`,
|
|
759
|
+
config: `# loor config
|
|
760
|
+
|
|
761
|
+
Configure the CLI registry source \u2014 local development or remote production.
|
|
762
|
+
|
|
763
|
+
## Options
|
|
764
|
+
|
|
765
|
+
| Option | Description |
|
|
766
|
+
|--------|-------------|
|
|
767
|
+
| \`--local [path]\` | Use a local repo as scaffold source. Defaults to current directory. |
|
|
768
|
+
| \`--remote\` | Use the remote GitHub registry (production). |
|
|
769
|
+
| *(no flags)* | Show current configuration. |
|
|
770
|
+
|
|
771
|
+
## Examples
|
|
772
|
+
|
|
773
|
+
\`\`\`bash
|
|
774
|
+
# Show current config
|
|
775
|
+
loor config
|
|
776
|
+
|
|
777
|
+
# Use local repo (current directory)
|
|
778
|
+
loor config --local
|
|
779
|
+
|
|
780
|
+
# Use local repo (specific path)
|
|
781
|
+
loor config --local /path/to/loor
|
|
782
|
+
|
|
783
|
+
# Switch back to remote
|
|
784
|
+
loor config --remote
|
|
785
|
+
\`\`\`
|
|
786
|
+
|
|
787
|
+
## Environment Variable
|
|
788
|
+
|
|
789
|
+
The \`LOOR_LOCAL\` env var overrides any persisted config. When set, CLI always uses the path it points to.`,
|
|
790
|
+
ai_init: `# loor ai init
|
|
791
|
+
|
|
792
|
+
Generate AI context files for the project.
|
|
793
|
+
|
|
794
|
+
\`\`\`bash
|
|
795
|
+
loor ai init
|
|
796
|
+
\`\`\`
|
|
797
|
+
|
|
798
|
+
## What It Does
|
|
799
|
+
|
|
800
|
+
1. Scans the project for installed apps and packages
|
|
801
|
+
2. Generates \`CLAUDE.md\` with project-specific AI context:
|
|
802
|
+
- App list with types and paths
|
|
803
|
+
- Architecture overview
|
|
804
|
+
- Installed packages with DO/DON'T patterns
|
|
805
|
+
- Infra files and required env vars
|
|
806
|
+
- Conventions (hexagonal architecture, routes, features)
|
|
807
|
+
- App-specific context (routes, infra, features)
|
|
808
|
+
3. Generates \`.mcp.json\` to register the loor MCP server
|
|
809
|
+
|
|
810
|
+
## MCP Tools Available After Init
|
|
811
|
+
|
|
812
|
+
| Tool | Description |
|
|
813
|
+
|------|-------------|
|
|
814
|
+
| \`list_packages\` | List packages with optional category/compatibility filters |
|
|
815
|
+
| \`get_package_guide\` | Detailed package guide (patterns, infra, env vars, docs) |
|
|
816
|
+
| \`get_conventions\` | Project conventions (architecture, routes, features) |
|
|
817
|
+
| \`how_to\` | Step-by-step guides for common development tasks |
|
|
818
|
+
| \`cli_usage\` | Complete CLI command reference and usage examples |`,
|
|
819
|
+
overview: `# loor CLI \u2014 Complete Reference
|
|
820
|
+
|
|
821
|
+
## All Commands
|
|
822
|
+
|
|
823
|
+
| Command | Description |
|
|
824
|
+
|---------|-------------|
|
|
825
|
+
| \`loor init [name]\` | Create a new monorepo project |
|
|
826
|
+
| \`loor add app <type> [name]\` | Add an app to existing project |
|
|
827
|
+
| \`loor add package <name>\` | Add a package to existing project |
|
|
828
|
+
| \`loor list\` | List available packages |
|
|
829
|
+
| \`loor update\` | Refresh registry cache |
|
|
830
|
+
| \`loor config\` | Configure registry source |
|
|
831
|
+
| \`loor ai init\` | Generate AI context files |
|
|
832
|
+
|
|
833
|
+
## Global Options
|
|
834
|
+
|
|
835
|
+
| Option | Description |
|
|
836
|
+
|--------|-------------|
|
|
837
|
+
| \`--debug\` | Enable debug mode with detailed error output |
|
|
838
|
+
| \`--version\` | Show CLI version |
|
|
839
|
+
| \`--help\` | Show help for any command |
|
|
840
|
+
|
|
841
|
+
## Init Command \u2014 Quick Reference
|
|
842
|
+
|
|
843
|
+
\`\`\`bash
|
|
844
|
+
# Fully interactive
|
|
845
|
+
loor init
|
|
846
|
+
|
|
847
|
+
# Fully non-interactive
|
|
848
|
+
loor init my-saas --apps api:backend,web-client:dashboard --packages mediaKit
|
|
849
|
+
|
|
850
|
+
# Skip optional packages
|
|
851
|
+
loor init my-saas --apps api,web-client --no-packages
|
|
852
|
+
|
|
853
|
+
# Only specify apps
|
|
854
|
+
loor init my-saas --apps api:my-api,web-client:admin
|
|
855
|
+
|
|
856
|
+
# Only specify packages
|
|
857
|
+
loor init my-saas --packages mediaKit,notification
|
|
858
|
+
\`\`\`
|
|
859
|
+
|
|
860
|
+
## --apps Flag Format
|
|
861
|
+
|
|
862
|
+
\`type\` or \`type:name\`, comma-separated:
|
|
863
|
+
- \`--apps api\` \u2192 API app named "api"
|
|
864
|
+
- \`--apps api:backend\` \u2192 API app named "backend"
|
|
865
|
+
- \`--apps api,web-client\` \u2192 API "api" + web-client "admin"
|
|
866
|
+
- \`--apps api:backend,web-client:dash\` \u2192 API "backend" + web-client "dash"
|
|
867
|
+
|
|
868
|
+
Valid types: \`api\`, \`web-client\`
|
|
869
|
+
|
|
870
|
+
## --packages Flag Format
|
|
871
|
+
|
|
872
|
+
Comma-separated package names:
|
|
873
|
+
- \`--packages mediaKit\` \u2192 add mediaKit
|
|
874
|
+
- \`--packages mediaKit,notification,scheduler\` \u2192 add multiple
|
|
875
|
+
- \`--no-packages\` \u2192 no optional packages
|
|
876
|
+
|
|
877
|
+
## Environment Variables
|
|
878
|
+
|
|
879
|
+
| Variable | Description |
|
|
880
|
+
|----------|-------------|
|
|
881
|
+
| \`LOOR_LOCAL\` | Override config to use local repo path |
|
|
882
|
+
| \`LOOR_REPO\` | GitHub repo (default: ismailyagci/loor) |
|
|
883
|
+
| \`LOOR_BRANCH\` | Branch (default: master) |
|
|
884
|
+
|
|
885
|
+
## Typical Workflows
|
|
886
|
+
|
|
887
|
+
### New project (CI/scripted)
|
|
888
|
+
\`\`\`bash
|
|
889
|
+
loor init my-app --apps api,web-client:admin --packages mediaKit
|
|
890
|
+
cd my-app && pnpm install && pnpm dev
|
|
891
|
+
\`\`\`
|
|
892
|
+
|
|
893
|
+
### Add second frontend
|
|
894
|
+
\`\`\`bash
|
|
895
|
+
cd my-app
|
|
896
|
+
loor add app web-client portal
|
|
897
|
+
pnpm install
|
|
898
|
+
\`\`\`
|
|
899
|
+
|
|
900
|
+
### Add package
|
|
901
|
+
\`\`\`bash
|
|
902
|
+
cd my-app
|
|
903
|
+
loor add package notification
|
|
904
|
+
pnpm install
|
|
905
|
+
\`\`\`
|
|
906
|
+
|
|
907
|
+
### Setup AI context
|
|
908
|
+
\`\`\`bash
|
|
909
|
+
cd my-app
|
|
910
|
+
loor ai init
|
|
911
|
+
\`\`\``
|
|
912
|
+
};
|
|
913
|
+
function registerCliUsage(server) {
|
|
914
|
+
server.tool(
|
|
915
|
+
"cli_usage",
|
|
916
|
+
"Get detailed CLI command reference, usage examples, and available options for loor commands",
|
|
917
|
+
{
|
|
918
|
+
command: z5.enum(["overview", "init", "add_app", "add_package", "list", "update", "config", "ai_init"]).optional().default("overview").describe('CLI command to get usage info for. Use "overview" for complete reference.')
|
|
919
|
+
},
|
|
920
|
+
async ({ command }) => {
|
|
921
|
+
const content = commands[command];
|
|
922
|
+
return {
|
|
923
|
+
content: [{ type: "text", text: content }]
|
|
924
|
+
};
|
|
925
|
+
}
|
|
926
|
+
);
|
|
927
|
+
}
|
|
928
|
+
|
|
929
|
+
// src/mcp/server.ts
|
|
930
|
+
async function startMcpServer() {
|
|
931
|
+
const originalLog = console.log;
|
|
932
|
+
console.log = console.error;
|
|
933
|
+
try {
|
|
934
|
+
await ensureCache();
|
|
935
|
+
} finally {
|
|
936
|
+
console.log = originalLog;
|
|
937
|
+
}
|
|
938
|
+
const registry = new PackageRegistry(await getPackagesDir());
|
|
939
|
+
await registry.load();
|
|
940
|
+
const server = new McpServer({
|
|
941
|
+
name: "loor",
|
|
942
|
+
version: "0.1.0"
|
|
943
|
+
});
|
|
944
|
+
registerListPackages(server, registry);
|
|
945
|
+
registerGetPackageGuide(server, registry);
|
|
946
|
+
registerGetConventions(server, registry);
|
|
947
|
+
registerHowTo(server);
|
|
948
|
+
registerCliUsage(server);
|
|
949
|
+
const transport = new StdioServerTransport();
|
|
950
|
+
await server.connect(transport);
|
|
951
|
+
}
|
|
952
|
+
export {
|
|
953
|
+
startMcpServer
|
|
954
|
+
};
|