@pyreon/mcp 0.5.0 → 0.5.1

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.
@@ -1,9 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import { ZodOptional, z } from "zod";
3
3
  import process$1 from "node:process";
4
- import { detectReactPatterns, diagnoseError, migrateReactCode } from "@pyreon/compiler";
5
- import * as fs from "node:fs";
6
- import * as path from "node:path";
4
+ import { detectReactPatterns, diagnoseError, generateContext, migrateReactCode } from "@pyreon/compiler";
7
5
 
8
6
  //#region \0rolldown/runtime.js
9
7
 
@@ -304,10 +302,7 @@ function assignProp(target, prop, value) {
304
302
  }
305
303
  function mergeDefs(...defs) {
306
304
  const mergedDescriptors = {};
307
- for (const def of defs) {
308
- const descriptors = Object.getOwnPropertyDescriptors(def);
309
- Object.assign(mergedDescriptors, descriptors);
310
- }
305
+ for (const def of defs) Object.assign(mergedDescriptors, Object.getOwnPropertyDescriptors(def));
311
306
  return Object.defineProperties({}, mergedDescriptors);
312
307
  }
313
308
  function esc(str) {
@@ -3208,168 +3203,6 @@ function serializeMessage(message) {
3208
3203
  * This transport is only available in Node.js environments.
3209
3204
  */
3210
3205
 
3211
- //#endregion
3212
- //#region src/project-scanner.ts
3213
- /**
3214
- * Project scanner — extracts route, component, and island information from source files.
3215
- */
3216
- function generateContext(cwd) {
3217
- const files = collectSourceFiles(cwd);
3218
- return {
3219
- framework: "pyreon",
3220
- version: readVersion(cwd),
3221
- generatedAt: (/* @__PURE__ */new Date()).toISOString(),
3222
- routes: extractRoutes(files, cwd),
3223
- components: extractComponents(files, cwd),
3224
- islands: extractIslands(files, cwd)
3225
- };
3226
- }
3227
- function collectSourceFiles(cwd) {
3228
- const results = [];
3229
- const extensions = new Set([".tsx", ".jsx", ".ts", ".js"]);
3230
- const ignoreDirs = new Set(["node_modules", "dist", "lib", ".pyreon", ".git", "build"]);
3231
- function walk(dir) {
3232
- let entries;
3233
- try {
3234
- entries = fs.readdirSync(dir, {
3235
- withFileTypes: true
3236
- });
3237
- } catch {
3238
- return;
3239
- }
3240
- for (const entry of entries) {
3241
- if (entry.name.startsWith(".") && entry.isDirectory()) continue;
3242
- if (ignoreDirs.has(entry.name) && entry.isDirectory()) continue;
3243
- const fullPath = path.join(dir, entry.name);
3244
- if (entry.isDirectory()) walk(fullPath);else if (entry.isFile() && extensions.has(path.extname(entry.name))) results.push(fullPath);
3245
- }
3246
- }
3247
- walk(cwd);
3248
- return results;
3249
- }
3250
- function extractRoutes(files, _cwd) {
3251
- const routes = [];
3252
- for (const file of files) {
3253
- let code;
3254
- try {
3255
- code = fs.readFileSync(file, "utf-8");
3256
- } catch {
3257
- continue;
3258
- }
3259
- const routeArrayRe = /(?:createRouter\s*\(\s*\[|(?:const|let)\s+routes\s*(?::\s*RouteRecord\[\])?\s*=\s*\[)([\s\S]*?)\]/g;
3260
- let match;
3261
- for (match = routeArrayRe.exec(code); match; match = routeArrayRe.exec(code)) {
3262
- const block = match[1] ?? "";
3263
- const routeObjRe = /path\s*:\s*["']([^"']+)["']/g;
3264
- let routeMatch;
3265
- for (routeMatch = routeObjRe.exec(block); routeMatch; routeMatch = routeObjRe.exec(block)) {
3266
- const routePath = routeMatch[1] ?? "";
3267
- const surroundingStart = Math.max(0, routeMatch.index - 50);
3268
- const surroundingEnd = Math.min(block.length, routeMatch.index + 200);
3269
- const surrounding = block.slice(surroundingStart, surroundingEnd);
3270
- routes.push({
3271
- path: routePath,
3272
- name: surrounding.match(/name\s*:\s*["']([^"']+)["']/)?.[1],
3273
- hasLoader: /loader\s*:/.test(surrounding),
3274
- hasGuard: /beforeEnter\s*:|beforeLeave\s*:/.test(surrounding),
3275
- params: extractParams(routePath)
3276
- });
3277
- }
3278
- }
3279
- }
3280
- return routes;
3281
- }
3282
- function extractComponents(files, cwd) {
3283
- const components = [];
3284
- for (const file of files) {
3285
- let code;
3286
- try {
3287
- code = fs.readFileSync(file, "utf-8");
3288
- } catch {
3289
- continue;
3290
- }
3291
- const componentRe = /(?:export\s+)?(?:const|function)\s+([A-Z]\w*)\s*(?::\s*ComponentFn<[^>]+>\s*)?=?\s*\(?(?:\s*\{?\s*([^)]*?)\s*\}?\s*)?\)?\s*(?:=>|{)/g;
3292
- let match;
3293
- for (match = componentRe.exec(code); match; match = componentRe.exec(code)) {
3294
- const name = match[1] ?? "Unknown";
3295
- const props = (match[2] ?? "").split(/[,;]/).map(p => p.trim().replace(/[{}]/g, "").trim().split(":")[0]?.split("=")[0]?.trim() ?? "").filter(p => p && p !== "props");
3296
- const bodyStart = match.index + match[0].length;
3297
- const body = code.slice(bodyStart, Math.min(code.length, bodyStart + 2e3));
3298
- const signalNames = [];
3299
- const signalRe = /(?:const|let)\s+(\w+)\s*=\s*signal\s*[<(]/g;
3300
- let sigMatch;
3301
- for (sigMatch = signalRe.exec(body); sigMatch; sigMatch = signalRe.exec(body)) if (sigMatch[1]) signalNames.push(sigMatch[1]);
3302
- components.push({
3303
- name,
3304
- file: path.relative(cwd, file),
3305
- hasSignals: signalNames.length > 0,
3306
- signalNames,
3307
- props
3308
- });
3309
- }
3310
- }
3311
- return components;
3312
- }
3313
- function extractIslands(files, cwd) {
3314
- const islands = [];
3315
- for (const file of files) {
3316
- let code;
3317
- try {
3318
- code = fs.readFileSync(file, "utf-8");
3319
- } catch {
3320
- continue;
3321
- }
3322
- const islandRe = /island\s*\(\s*\(\)\s*=>\s*import\(.+?\)\s*,\s*\{[^}]*name\s*:\s*["']([^"']+)["'][^}]*?(?:hydrate\s*:\s*["']([^"']+)["'])?[^}]*\}/g;
3323
- let match;
3324
- for (match = islandRe.exec(code); match; match = islandRe.exec(code)) if (match[1]) islands.push({
3325
- name: match[1],
3326
- file: path.relative(cwd, file),
3327
- hydrate: match[2] ?? "load"
3328
- });
3329
- }
3330
- return islands;
3331
- }
3332
- function extractParams(routePath) {
3333
- const params = [];
3334
- const paramRe = /:(\w+)\??/g;
3335
- let match;
3336
- for (match = paramRe.exec(routePath); match; match = paramRe.exec(routePath)) if (match[1]) params.push(match[1]);
3337
- return params;
3338
- }
3339
- function readVersion(cwd) {
3340
- try {
3341
- const pkg = JSON.parse(fs.readFileSync(path.join(cwd, "package.json"), "utf-8"));
3342
- const deps = {
3343
- ...pkg.dependencies,
3344
- ...pkg.devDependencies
3345
- };
3346
- for (const [name, ver] of Object.entries(deps)) if (name.startsWith("@pyreon/") && typeof ver === "string") return ver.replace(/^[\^~]/, "");
3347
- return pkg.version || "unknown";
3348
- } catch {
3349
- return "unknown";
3350
- }
3351
- }
3352
-
3353
- //#endregion
3354
- //#region src/index.ts
3355
- /**
3356
- * @pyreon/mcp — Model Context Protocol server for Pyreon
3357
- *
3358
- * Exposes tools that AI coding assistants (Claude Code, Cursor, etc.) can use
3359
- * to generate, validate, and migrate Pyreon code.
3360
- *
3361
- * Tools:
3362
- * get_api — Look up any Pyreon API: signature, usage, common mistakes
3363
- * validate — Check a code snippet for Pyreon anti-patterns
3364
- * migrate_react — Convert React code to idiomatic Pyreon
3365
- * diagnose — Parse an error message into structured fix information
3366
- * get_routes — List all routes in the current project
3367
- * get_components — List all components with their props and signals
3368
- *
3369
- * Usage:
3370
- * bunx @pyreon/mcp # stdio transport (for IDE integration)
3371
- */
3372
-
3373
3206
  function getContext() {
3374
3207
  if (!cachedContext || contextCwd !== process.cwd()) {
3375
3208
  contextCwd = process.cwd();