From 7feda7a44358e0ff75c2f4cebe0186889a768fa2 Mon Sep 17 00:00:00 2001 From: Kailas Mahavarkar <66670953+KailasMahavarkar@users.noreply.github.com> Date: Wed, 22 Apr 2026 11:01:38 +0530 Subject: [PATCH 1/5] refactor: replace @modelcontextprotocol/sdk type imports with local ToolServer shim Introduces src/shared/tool-types.ts with a minimal ToolServer/ToolHandler/ToolResult/Plugin type surface that matches the signatures the plugin tools use. Replaces every `import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"` across 95 plugin tool, plugin index, registry, and tool-bridge files with `import type { ToolServer } from "...shared/tool-types.js"`. Removes the two remaining value imports of McpServer in hyperstack/index.ts and hyperstack/tools/setup.ts. This severs the MCP SDK from the runtime code paths without changing any observable behavior. The shim keeps server.tool/server.resource/server.prompt signatures compatible with existing plugin registration, and tool-bridge continues to capture the handler through the same `as unknown as ToolServer` pattern. Prepares for the next commit which deletes the MCP server entrypoint, docker scripts, and the @modelcontextprotocol/sdk dependency entirely. Co-Authored-By: Claude Opus 4.7 (1M context) --- src/engine/tool-bridge.ts | 6 ++-- src/plugins/design-tokens/index.ts | 4 +-- src/plugins/design-tokens/tools/generate.ts | 4 +-- .../design-tokens/tools/get-category.ts | 4 +-- .../design-tokens/tools/get-color-ramp.ts | 4 +-- .../design-tokens/tools/get-gotchas.ts | 4 +-- .../design-tokens/tools/get-procedure.ts | 4 +-- .../design-tokens/tools/list-categories.ts | 4 +-- src/plugins/design-tokens/tools/search.ts | 4 +-- src/plugins/designer/index.ts | 4 +-- .../designer/tools/generate-design-brief.ts | 4 +-- .../tools/generate-implementation-plan.ts | 4 +-- .../designer/tools/get-anti-patterns.ts | 4 +-- .../designer/tools/get-cognitive-law.ts | 4 +-- .../designer/tools/get-composition-rules.ts | 4 +-- .../designer/tools/get-design-system.ts | 4 +-- .../designer/tools/get-font-pairing.ts | 4 +-- .../designer/tools/get-industry-rules.ts | 4 +-- .../designer/tools/get-interaction-pattern.ts | 4 +-- .../designer/tools/get-landing-pattern.ts | 4 +-- .../designer/tools/get-page-template.ts | 4 +-- src/plugins/designer/tools/get-personality.ts | 4 +-- src/plugins/designer/tools/get-preset.ts | 4 +-- src/plugins/designer/tools/get-ux-writing.ts | 4 +-- .../designer/tools/list-personalities.ts | 4 +-- src/plugins/designer/tools/list-presets.ts | 4 +-- src/plugins/designer/tools/resolve-intent.ts | 4 +-- src/plugins/designer/tools/search.ts | 4 +-- .../designer/tools/verify-implementation.ts | 4 +-- src/plugins/echo/index.ts | 4 +-- src/plugins/echo/tools/decision-matrix.ts | 4 +-- src/plugins/echo/tools/get-middleware.ts | 4 +-- src/plugins/echo/tools/get-recipe.ts | 4 +-- src/plugins/echo/tools/list-middleware.ts | 4 +-- src/plugins/echo/tools/list-recipes.ts | 4 +-- src/plugins/echo/tools/search-docs.ts | 4 +-- src/plugins/golang/index.ts | 4 +-- src/plugins/golang/tools/get-antipatterns.ts | 4 +-- src/plugins/golang/tools/get-pattern.ts | 4 +-- src/plugins/golang/tools/get-practice.ts | 4 +-- src/plugins/golang/tools/list-patterns.ts | 4 +-- src/plugins/golang/tools/list-practices.ts | 4 +-- src/plugins/golang/tools/search-docs.ts | 4 +-- src/plugins/hyperstack/index.ts | 5 ++-- src/plugins/hyperstack/tools/setup.ts | 4 +-- src/plugins/lenis/index.ts | 4 +-- src/plugins/lenis/tools/cheatsheet.ts | 4 +-- src/plugins/lenis/tools/generate-setup.ts | 4 +-- src/plugins/lenis/tools/get-api.ts | 4 +-- src/plugins/lenis/tools/get-pattern.ts | 4 +-- src/plugins/lenis/tools/list-apis.ts | 4 +-- src/plugins/lenis/tools/search-docs.ts | 4 +-- src/plugins/motion/index.ts | 4 +-- src/plugins/motion/tools/cheatsheet.ts | 4 +-- .../motion/tools/generate-animation.ts | 4 +-- src/plugins/motion/tools/get-api.ts | 4 +-- src/plugins/motion/tools/get-examples.ts | 4 +-- src/plugins/motion/tools/get-transitions.ts | 4 +-- src/plugins/motion/tools/list-apis.ts | 4 +-- src/plugins/motion/tools/search-docs.ts | 4 +-- src/plugins/react/index.ts | 4 +-- src/plugins/react/tools/get-constraints.ts | 4 +-- src/plugins/react/tools/get-pattern.ts | 4 +-- src/plugins/react/tools/list-patterns.ts | 4 +-- src/plugins/react/tools/search-docs.ts | 4 +-- src/plugins/reactflow/index.ts | 4 +-- src/plugins/reactflow/tools/cheatsheet.ts | 4 +-- src/plugins/reactflow/tools/generate-flow.ts | 4 +-- src/plugins/reactflow/tools/get-api.ts | 4 +-- src/plugins/reactflow/tools/get-examples.ts | 4 +-- .../reactflow/tools/get-migration-guide.ts | 4 +-- src/plugins/reactflow/tools/get-pattern.ts | 4 +-- src/plugins/reactflow/tools/get-template.ts | 6 ++-- src/plugins/reactflow/tools/list-apis.ts | 4 +-- src/plugins/reactflow/tools/search-docs.ts | 4 +-- src/plugins/rust/index.ts | 4 +-- src/plugins/rust/tools/cheatsheet.ts | 4 +-- src/plugins/rust/tools/get-practice.ts | 4 +-- src/plugins/rust/tools/list-practices.ts | 4 +-- src/plugins/rust/tools/search-docs.ts | 4 +-- src/plugins/shadcn/index.ts | 4 +-- src/plugins/shadcn/tools/get-component.ts | 4 +-- src/plugins/shadcn/tools/get-composition.ts | 4 +-- src/plugins/shadcn/tools/get-rules.ts | 4 +-- src/plugins/shadcn/tools/get-snippet.ts | 4 +-- src/plugins/shadcn/tools/list-components.ts | 4 +-- src/plugins/ui-ux/index.ts | 4 +-- src/plugins/ui-ux/tools/get-checklist.ts | 4 +-- .../ui-ux/tools/get-component-pattern.ts | 4 +-- src/plugins/ui-ux/tools/get-gotchas.ts | 4 +-- src/plugins/ui-ux/tools/get-principle.ts | 4 +-- src/plugins/ui-ux/tools/list-principles.ts | 4 +-- src/plugins/ui-ux/tools/search.ts | 4 +-- src/registry.ts | 6 ++-- src/shared/tool-types.ts | 29 +++++++++++++++++++ 95 files changed, 220 insertions(+), 192 deletions(-) create mode 100644 src/shared/tool-types.ts diff --git a/src/engine/tool-bridge.ts b/src/engine/tool-bridge.ts index cf5919f..15e13e1 100644 --- a/src/engine/tool-bridge.ts +++ b/src/engine/tool-bridge.ts @@ -1,5 +1,5 @@ import assert from "node:assert/strict"; -import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import type { ToolServer } from "../shared/tool-types.js"; type ToolResult = { content?: Array<{ type?: string; text?: string }>; @@ -9,7 +9,7 @@ type ToolResult = { type ToolHandler = (args: Record) => Promise | ToolResult; export async function invokeRegisteredTool( - register: (server: McpServer) => void, + register: (server: ToolServer) => void, args: Record, ): Promise { let capturedHandler: ToolHandler | undefined; @@ -18,7 +18,7 @@ export async function invokeRegisteredTool( tool(_name: string, _description: string, _schema: unknown, handler: ToolHandler) { capturedHandler = handler; }, - } as unknown as McpServer; + } as unknown as ToolServer; register(server); diff --git a/src/plugins/design-tokens/index.ts b/src/plugins/design-tokens/index.ts index b9f7c71..a9570fd 100644 --- a/src/plugins/design-tokens/index.ts +++ b/src/plugins/design-tokens/index.ts @@ -1,4 +1,4 @@ -import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import type { ToolServer } from "../../shared/tool-types.js"; import type { Plugin } from "../../registry.js"; import { register as listCategories } from "./tools/list-categories.js"; import { register as getCategory } from "./tools/get-category.js"; @@ -8,7 +8,7 @@ import { register as search } from "./tools/search.js"; import { register as getGotchas } from "./tools/get-gotchas.js"; import { register as generate } from "./tools/generate.js"; -function register(server: McpServer): void { +function register(server: ToolServer): void { listCategories(server); getCategory(server); getColorRamp(server); diff --git a/src/plugins/design-tokens/tools/generate.ts b/src/plugins/design-tokens/tools/generate.ts index 58a86d9..3500050 100644 --- a/src/plugins/design-tokens/tools/generate.ts +++ b/src/plugins/design-tokens/tools/generate.ts @@ -1,4 +1,4 @@ -import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import type { ToolServer } from "../../../shared/tool-types.js"; import { z } from "zod"; import { snippet } from "../loader.js"; @@ -7,7 +7,7 @@ const TEMPLATE_SPACING = snippet("templates/spacing.txt"); const TEMPLATE_TYPOGRAPHY = snippet("templates/typography.txt"); const TEMPLATE_MOTION = snippet("templates/motion.txt"); -export function register(server: McpServer): void { +export function register(server: ToolServer): void { server.tool( "design_tokens_generate", "Generate CSS token scaffolding from a description. Returns ready-to-use CSS custom properties.", diff --git a/src/plugins/design-tokens/tools/get-category.ts b/src/plugins/design-tokens/tools/get-category.ts index 6e07b58..9a227c5 100644 --- a/src/plugins/design-tokens/tools/get-category.ts +++ b/src/plugins/design-tokens/tools/get-category.ts @@ -1,8 +1,8 @@ -import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import type { ToolServer } from "../../../shared/tool-types.js"; import { z } from "zod"; import { TOKEN_CATEGORIES, getCategoryByName } from "../data.js"; -export function register(server: McpServer): void { +export function register(server: ToolServer): void { server.tool( "design_tokens_get_category", "Get full details for a design token category including CSS examples, rules, and gotchas", diff --git a/src/plugins/design-tokens/tools/get-color-ramp.ts b/src/plugins/design-tokens/tools/get-color-ramp.ts index 9bdc709..85b0643 100644 --- a/src/plugins/design-tokens/tools/get-color-ramp.ts +++ b/src/plugins/design-tokens/tools/get-color-ramp.ts @@ -1,8 +1,8 @@ -import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import type { ToolServer } from "../../../shared/tool-types.js"; import { z } from "zod"; import { COLOR_RAMPS, getRampByName } from "../data.js"; -export function register(server: McpServer): void { +export function register(server: ToolServer): void { server.tool( "design_tokens_get_color_ramp", "Get a color ramp (brand/neutral/pop) with all 11 OKLCH stops, semantic roles, and light/dark mode usage", diff --git a/src/plugins/design-tokens/tools/get-gotchas.ts b/src/plugins/design-tokens/tools/get-gotchas.ts index 8257bfe..d5dba5e 100644 --- a/src/plugins/design-tokens/tools/get-gotchas.ts +++ b/src/plugins/design-tokens/tools/get-gotchas.ts @@ -1,7 +1,7 @@ -import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import type { ToolServer } from "../../../shared/tool-types.js"; import { getAllGotchas } from "../data.js"; -export function register(server: McpServer): void { +export function register(server: ToolServer): void { server.tool( "design_tokens_get_gotchas", "List all common design token mistakes and fixes across all categories", diff --git a/src/plugins/design-tokens/tools/get-procedure.ts b/src/plugins/design-tokens/tools/get-procedure.ts index 69468eb..5f7e916 100644 --- a/src/plugins/design-tokens/tools/get-procedure.ts +++ b/src/plugins/design-tokens/tools/get-procedure.ts @@ -1,4 +1,4 @@ -import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import type { ToolServer } from "../../../shared/tool-types.js"; import { readFileSync } from "node:fs"; import { dirname, join } from "node:path"; import { fileURLToPath } from "node:url"; @@ -145,7 +145,7 @@ function renderProcedure(proc: { return text; } -export function register(server: McpServer): void { +export function register(server: ToolServer): void { server.tool( "design_tokens_get_procedure", "Get the step-by-step token system build procedure. Steps 1-8 cover the full production workflow.", diff --git a/src/plugins/design-tokens/tools/list-categories.ts b/src/plugins/design-tokens/tools/list-categories.ts index 324ebbb..a818f93 100644 --- a/src/plugins/design-tokens/tools/list-categories.ts +++ b/src/plugins/design-tokens/tools/list-categories.ts @@ -1,8 +1,8 @@ -import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import type { ToolServer } from "../../../shared/tool-types.js"; import { z } from "zod"; import { TOKEN_CATEGORIES } from "../data.js"; -export function register(server: McpServer): void { +export function register(server: ToolServer): void { server.tool( "design_tokens_list_categories", "List all 10 design token categories with descriptions and architecture layer", diff --git a/src/plugins/design-tokens/tools/search.ts b/src/plugins/design-tokens/tools/search.ts index 3959e94..aee413f 100644 --- a/src/plugins/design-tokens/tools/search.ts +++ b/src/plugins/design-tokens/tools/search.ts @@ -1,8 +1,8 @@ -import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import type { ToolServer } from "../../../shared/tool-types.js"; import { z } from "zod"; import { searchTokens } from "../data.js"; -export function register(server: McpServer): void { +export function register(server: ToolServer): void { server.tool( "design_tokens_search", "Search design token documentation by keyword across categories, ramps, and procedures", diff --git a/src/plugins/designer/index.ts b/src/plugins/designer/index.ts index 4c24d70..110381e 100644 --- a/src/plugins/designer/index.ts +++ b/src/plugins/designer/index.ts @@ -1,4 +1,4 @@ -import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import type { ToolServer } from "../../shared/tool-types.js"; import type { Plugin } from "../../registry.js"; import { register as listPersonalities } from "./tools/list-personalities.js"; import { register as getPersonality } from "./tools/get-personality.js"; @@ -20,7 +20,7 @@ import { register as getFontPairing } from "./tools/get-font-pairing.js"; import { register as generateImplementationPlan } from "./tools/generate-implementation-plan.js"; import { register as verifyImplementation } from "./tools/verify-implementation.js"; -function register(server: McpServer): void { +function register(server: ToolServer): void { listPersonalities(server); getPersonality(server); getIndustryRules(server); diff --git a/src/plugins/designer/tools/generate-design-brief.ts b/src/plugins/designer/tools/generate-design-brief.ts index 6aed85d..908711b 100644 --- a/src/plugins/designer/tools/generate-design-brief.ts +++ b/src/plugins/designer/tools/generate-design-brief.ts @@ -1,4 +1,4 @@ -import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import type { ToolServer } from "../../../shared/tool-types.js"; import { z } from "zod"; import { PERSONALITY_CLUSTERS, @@ -27,7 +27,7 @@ const PERSONALITY_TO_SYSTEM: Record = { "enterprise-trust": "ibm-carbon", }; -export function register(server: McpServer): void { +export function register(server: ToolServer): void { server.tool( "designer_generate_design_brief", "Generate a complete design brief for a product: resolves intent, then assembles visual theme, industry rules, style, cognitive laws, anti-patterns, design system inspiration, and composition rules", diff --git a/src/plugins/designer/tools/generate-implementation-plan.ts b/src/plugins/designer/tools/generate-implementation-plan.ts index e48cbd5..8c37a92 100644 --- a/src/plugins/designer/tools/generate-implementation-plan.ts +++ b/src/plugins/designer/tools/generate-implementation-plan.ts @@ -1,4 +1,4 @@ -import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import type { ToolServer } from "../../../shared/tool-types.js"; import { z } from "zod"; import { readFileSync, existsSync } from "fs"; import { isAbsolute } from "path"; @@ -8,7 +8,7 @@ import { isAbsolute } from "path"; * an implementation plan with exact MCP calls per section. This is the bridge * that forge-plan uses to turn a DESIGN.md contract into executable tasks. */ -export function register(server: McpServer): void { +export function register(server: ToolServer): void { server.tool( "designer_generate_implementation_plan", "Parse a DESIGN.md file into its 10 sections and generate a structured implementation plan. Each section becomes one or more tasks with exact MCP calls (shadcn_get_component, motion_generate_animation, design_tokens_generate, etc.) and self-review assertions. This is the bridge from designer to forge-plan.", diff --git a/src/plugins/designer/tools/get-anti-patterns.ts b/src/plugins/designer/tools/get-anti-patterns.ts index 91560fc..2833124 100644 --- a/src/plugins/designer/tools/get-anti-patterns.ts +++ b/src/plugins/designer/tools/get-anti-patterns.ts @@ -1,8 +1,8 @@ -import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import type { ToolServer } from "../../../shared/tool-types.js"; import { z } from "zod"; import { ANTI_PATTERN_CATEGORIES, INDUSTRY_CATEGORIES, getAntiPatterns } from "../data.js"; -export function register(server: McpServer): void { +export function register(server: ToolServer): void { server.tool( "designer_get_anti_patterns", "Get design anti-patterns filtered by category and/or industry", diff --git a/src/plugins/designer/tools/get-cognitive-law.ts b/src/plugins/designer/tools/get-cognitive-law.ts index 6a68d95..fcf59ef 100644 --- a/src/plugins/designer/tools/get-cognitive-law.ts +++ b/src/plugins/designer/tools/get-cognitive-law.ts @@ -1,8 +1,8 @@ -import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import type { ToolServer } from "../../../shared/tool-types.js"; import { z } from "zod"; import { COGNITIVE_LAW_NAMES, getCognitiveLaw } from "../data.js"; -export function register(server: McpServer): void { +export function register(server: ToolServer): void { server.tool( "designer_get_cognitive_law", "Get a cognitive law: formula, key insight, UI applications, common violations, and academic source", diff --git a/src/plugins/designer/tools/get-composition-rules.ts b/src/plugins/designer/tools/get-composition-rules.ts index 5335be5..9fb13df 100644 --- a/src/plugins/designer/tools/get-composition-rules.ts +++ b/src/plugins/designer/tools/get-composition-rules.ts @@ -1,8 +1,8 @@ -import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import type { ToolServer } from "../../../shared/tool-types.js"; import { z } from "zod"; import { COMPOSITION_TOPICS, getCompositionRule } from "../data.js"; -export function register(server: McpServer): void { +export function register(server: ToolServer): void { server.tool( "designer_get_composition_rules", "Get visual composition rules: key rule, detail, applications, and violations", diff --git a/src/plugins/designer/tools/get-design-system.ts b/src/plugins/designer/tools/get-design-system.ts index 57a49f9..d54048e 100644 --- a/src/plugins/designer/tools/get-design-system.ts +++ b/src/plugins/designer/tools/get-design-system.ts @@ -1,8 +1,8 @@ -import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import type { ToolServer } from "../../../shared/tool-types.js"; import { z } from "zod"; import { DESIGN_SYSTEM_NAMES, getDesignSystem, CROSS_SYSTEM_CONVERGENCES } from "../data.js"; -export function register(server: McpServer): void { +export function register(server: ToolServer): void { server.tool( "designer_get_design_system", "Get design system reference: signature, key insights, typography, color, spacing, reference code, and cross-system convergences", diff --git a/src/plugins/designer/tools/get-font-pairing.ts b/src/plugins/designer/tools/get-font-pairing.ts index 3ca2fb8..2d3889f 100644 --- a/src/plugins/designer/tools/get-font-pairing.ts +++ b/src/plugins/designer/tools/get-font-pairing.ts @@ -1,8 +1,8 @@ -import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import type { ToolServer } from "../../../shared/tool-types.js"; import { z } from "zod"; import { FONT_MOODS, INDUSTRY_CATEGORIES, FONT_PAIRINGS, getFontPairings } from "../data.js"; -export function register(server: McpServer): void { +export function register(server: ToolServer): void { server.tool( "designer_get_font_pairing", "Get curated font pairings filtered by mood (technical, elegant, friendly, editorial, bold, corporate, playful, luxury, startup, minimal) and/or industry. Returns heading + body + mono fonts with weights, tracking, line-height, Google Fonts import, and rationale. 21 pairings available.", diff --git a/src/plugins/designer/tools/get-industry-rules.ts b/src/plugins/designer/tools/get-industry-rules.ts index 3780dba..7c93b0b 100644 --- a/src/plugins/designer/tools/get-industry-rules.ts +++ b/src/plugins/designer/tools/get-industry-rules.ts @@ -1,8 +1,8 @@ -import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import type { ToolServer } from "../../../shared/tool-types.js"; import { z } from "zod"; import { INDUSTRY_CATEGORIES, getIndustryRule } from "../data.js"; -export function register(server: McpServer): void { +export function register(server: ToolServer): void { server.tool( "designer_get_industry_rules", "Get design rules for an industry: primary/secondary style, must-have features, never-use patterns, color mood, emotional target", diff --git a/src/plugins/designer/tools/get-interaction-pattern.ts b/src/plugins/designer/tools/get-interaction-pattern.ts index e8d8be9..2f9cc63 100644 --- a/src/plugins/designer/tools/get-interaction-pattern.ts +++ b/src/plugins/designer/tools/get-interaction-pattern.ts @@ -1,8 +1,8 @@ -import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import type { ToolServer } from "../../../shared/tool-types.js"; import { z } from "zod"; import { INTERACTION_CATEGORIES, getInteractionPattern } from "../data.js"; -export function register(server: McpServer): void { +export function register(server: ToolServer): void { server.tool( "designer_get_interaction_pattern", "Get interaction pattern: key rule, detail, best practices, and anti-patterns", diff --git a/src/plugins/designer/tools/get-landing-pattern.ts b/src/plugins/designer/tools/get-landing-pattern.ts index 16ec2fb..0955228 100644 --- a/src/plugins/designer/tools/get-landing-pattern.ts +++ b/src/plugins/designer/tools/get-landing-pattern.ts @@ -1,8 +1,8 @@ -import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import type { ToolServer } from "../../../shared/tool-types.js"; import { z } from "zod"; import { LANDING_TOPICS, getLandingPattern } from "../data.js"; -export function register(server: McpServer): void { +export function register(server: ToolServer): void { server.tool( "designer_get_landing_pattern", "Get landing page pattern: key stats, best practices, and anti-patterns", diff --git a/src/plugins/designer/tools/get-page-template.ts b/src/plugins/designer/tools/get-page-template.ts index bb6b9a0..3a6ce01 100644 --- a/src/plugins/designer/tools/get-page-template.ts +++ b/src/plugins/designer/tools/get-page-template.ts @@ -1,4 +1,4 @@ -import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import type { ToolServer } from "../../../shared/tool-types.js"; import { readFileSync } from "node:fs"; import { dirname, join } from "node:path"; import { fileURLToPath } from "node:url"; @@ -111,7 +111,7 @@ function getCorpusAwarePageTemplate(type: (typeof PAGE_TYPES)[number]) { return getPageTemplate(type); } -export function register(server: McpServer): void { +export function register(server: ToolServer): void { server.tool( "designer_get_page_template", "Get section anatomy, component inventory, and applicable cognitive laws for a page type. Covers landing, dashboard, auth, settings, checkout, blog, docs, admin, profile, error-page, ai-chat, pricing, and onboarding.", diff --git a/src/plugins/designer/tools/get-personality.ts b/src/plugins/designer/tools/get-personality.ts index 9ed3f04..748a742 100644 --- a/src/plugins/designer/tools/get-personality.ts +++ b/src/plugins/designer/tools/get-personality.ts @@ -1,8 +1,8 @@ -import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import type { ToolServer } from "../../../shared/tool-types.js"; import { z } from "zod"; import { PERSONALITY_CLUSTERS, getPersonality } from "../data.js"; -export function register(server: McpServer): void { +export function register(server: ToolServer): void { server.tool( "designer_get_personality", "Get full personality profile: description, exemplars, visual vocabulary, mode, and CSS example", diff --git a/src/plugins/designer/tools/get-preset.ts b/src/plugins/designer/tools/get-preset.ts index 15a6d5e..14804f6 100644 --- a/src/plugins/designer/tools/get-preset.ts +++ b/src/plugins/designer/tools/get-preset.ts @@ -1,8 +1,8 @@ -import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import type { ToolServer } from "../../../shared/tool-types.js"; import { z } from "zod"; import { PRESET_NAMES, PRESETS, getPreset } from "../data.js"; -export function register(server: McpServer): void { +export function register(server: ToolServer): void { server.tool( "designer_get_preset", "Get a complete, code-ready design token preset based on a real premium design system. Returns colors (OKLCH), typography (font, scale, weights, tracking), spacing, radius, shadows, motion, and CSS example. Available presets: linear, stripe, vercel, apple, carbon, shadcn, notion, supabase, figma.", diff --git a/src/plugins/designer/tools/get-ux-writing.ts b/src/plugins/designer/tools/get-ux-writing.ts index e17e586..b550fa5 100644 --- a/src/plugins/designer/tools/get-ux-writing.ts +++ b/src/plugins/designer/tools/get-ux-writing.ts @@ -1,8 +1,8 @@ -import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import type { ToolServer } from "../../../shared/tool-types.js"; import { z } from "zod"; import { WRITING_TOPICS, getWritingGuideline } from "../data.js"; -export function register(server: McpServer): void { +export function register(server: ToolServer): void { server.tool( "designer_get_ux_writing", "Get UX writing guideline: key rule, evidence, do/don't examples", diff --git a/src/plugins/designer/tools/list-personalities.ts b/src/plugins/designer/tools/list-personalities.ts index 8d4a1e1..3bbf42e 100644 --- a/src/plugins/designer/tools/list-personalities.ts +++ b/src/plugins/designer/tools/list-personalities.ts @@ -1,7 +1,7 @@ -import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import type { ToolServer } from "../../../shared/tool-types.js"; import { PERSONALITIES } from "../data.js"; -export function register(server: McpServer): void { +export function register(server: ToolServer): void { server.tool( "designer_list_personalities", "List all 6 designer personality clusters with descriptions and exemplar names", diff --git a/src/plugins/designer/tools/list-presets.ts b/src/plugins/designer/tools/list-presets.ts index 82f05df..5103be6 100644 --- a/src/plugins/designer/tools/list-presets.ts +++ b/src/plugins/designer/tools/list-presets.ts @@ -1,7 +1,7 @@ -import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import type { ToolServer } from "../../../shared/tool-types.js"; import { PRESETS } from "../data.js"; -export function register(server: McpServer): void { +export function register(server: ToolServer): void { server.tool( "designer_list_presets", "List all available design presets - complete, code-ready design token configurations based on real premium design systems (Linear, Stripe, Vercel, Apple, Carbon, shadcn, Notion, Supabase, Figma).", diff --git a/src/plugins/designer/tools/resolve-intent.ts b/src/plugins/designer/tools/resolve-intent.ts index a5f519d..87eea71 100644 --- a/src/plugins/designer/tools/resolve-intent.ts +++ b/src/plugins/designer/tools/resolve-intent.ts @@ -1,11 +1,11 @@ -import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import type { ToolServer } from "../../../shared/tool-types.js"; import { z } from "zod"; import { resolveFullIntent } from "../data.js"; const USER_TYPES = ["developer", "consumer", "enterprise", "child", "creative", "healthcare"] as const; const EMOTIONAL_TARGETS = ["trustworthy", "playful", "premium", "energetic", "calm", "technical", "bold", "editorial"] as const; -export function register(server: McpServer): void { +export function register(server: ToolServer): void { server.tool( "designer_resolve_intent", "Resolve a product description into a full design intent: industry, personality, style, mode, density, color mood, must-have/never-use lists", diff --git a/src/plugins/designer/tools/search.ts b/src/plugins/designer/tools/search.ts index 9830494..484b3a1 100644 --- a/src/plugins/designer/tools/search.ts +++ b/src/plugins/designer/tools/search.ts @@ -1,8 +1,8 @@ -import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import type { ToolServer } from "../../../shared/tool-types.js"; import { z } from "zod"; import { searchDesigner } from "../data.js"; -export function register(server: McpServer): void { +export function register(server: ToolServer): void { server.tool( "designer_search", "Search across all designer knowledge: personalities, styles, industries, cognitive laws, design systems, composition, interactions, writing, landing, anti-patterns, and master principles", diff --git a/src/plugins/designer/tools/verify-implementation.ts b/src/plugins/designer/tools/verify-implementation.ts index b9349a8..b3c5d29 100644 --- a/src/plugins/designer/tools/verify-implementation.ts +++ b/src/plugins/designer/tools/verify-implementation.ts @@ -1,4 +1,4 @@ -import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import type { ToolServer } from "../../../shared/tool-types.js"; import { z } from "zod"; import { readFileSync, existsSync, statSync } from "fs"; import { isAbsolute } from "path"; @@ -7,7 +7,7 @@ import { isAbsolute } from "path"; * Programmatically verifies an implementation against its DESIGN.md contract. * This runs during ship-gate as the hard compliance gate. */ -export function register(server: McpServer): void { +export function register(server: ToolServer): void { server.tool( "designer_verify_implementation", "Verify implementation code against its DESIGN.md contract. Runs pattern checks for anti-patterns (AI purple, font-weight 500 everywhere, cold shadows, etc.), verifies OKLCH tokens present, checks for prefers-reduced-motion, and reports per-section compliance. Use this before ship-gate. Returns a pass/fail report with specific violations.", diff --git a/src/plugins/echo/index.ts b/src/plugins/echo/index.ts index bad41ae..3d2083b 100644 --- a/src/plugins/echo/index.ts +++ b/src/plugins/echo/index.ts @@ -1,4 +1,4 @@ -import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import type { ToolServer } from "../../shared/tool-types.js"; import type { Plugin } from "../../registry.js"; import { register as listRecipes } from "./tools/list-recipes.js"; import { register as getRecipe } from "./tools/get-recipe.js"; @@ -7,7 +7,7 @@ import { register as getMiddleware } from "./tools/get-middleware.js"; import { register as searchDocs } from "./tools/search-docs.js"; import { register as decisionMatrix } from "./tools/decision-matrix.js"; -function register(server: McpServer): void { +function register(server: ToolServer): void { listRecipes(server); getRecipe(server); listMiddleware(server); diff --git a/src/plugins/echo/tools/decision-matrix.ts b/src/plugins/echo/tools/decision-matrix.ts index 0630aac..142a7c5 100644 --- a/src/plugins/echo/tools/decision-matrix.ts +++ b/src/plugins/echo/tools/decision-matrix.ts @@ -1,8 +1,8 @@ -import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import type { ToolServer } from "../../../shared/tool-types.js"; import { z } from "zod"; import { DECISION_MATRIX } from "../data.js"; -export function register(server: McpServer): void { +export function register(server: ToolServer): void { server.tool( "echo_decision_matrix", "Given a need or requirement, get the recommended Echo pattern and recipe to use.", diff --git a/src/plugins/echo/tools/get-middleware.ts b/src/plugins/echo/tools/get-middleware.ts index 590b04b..ee517a9 100644 --- a/src/plugins/echo/tools/get-middleware.ts +++ b/src/plugins/echo/tools/get-middleware.ts @@ -1,8 +1,8 @@ -import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import type { ToolServer } from "../../../shared/tool-types.js"; import { z } from "zod"; import { MIDDLEWARE, getMiddlewareByName, searchMiddleware, formatMiddleware } from "../data.js"; -export function register(server: McpServer): void { +export function register(server: ToolServer): void { server.tool( "echo_get_middleware", "Get detailed config and usage for a specific Echo middleware.", diff --git a/src/plugins/echo/tools/get-recipe.ts b/src/plugins/echo/tools/get-recipe.ts index 0ca4a83..2fc51fb 100644 --- a/src/plugins/echo/tools/get-recipe.ts +++ b/src/plugins/echo/tools/get-recipe.ts @@ -1,4 +1,4 @@ -import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import type { ToolServer } from "../../../shared/tool-types.js"; import { readFileSync } from "node:fs"; import { dirname, join } from "node:path"; import { fileURLToPath } from "node:url"; @@ -98,7 +98,7 @@ function loadCorpusRecipe(name: string): LoadedCorpusRecipe | null { } } -export function register(server: McpServer): void { +export function register(server: ToolServer): void { server.tool( "echo_get_recipe", "Get a specific Echo framework recipe with full working Go code, gotchas, and related recipes.", diff --git a/src/plugins/echo/tools/list-middleware.ts b/src/plugins/echo/tools/list-middleware.ts index 0c0c61d..eb98b9c 100644 --- a/src/plugins/echo/tools/list-middleware.ts +++ b/src/plugins/echo/tools/list-middleware.ts @@ -1,7 +1,7 @@ -import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import type { ToolServer } from "../../../shared/tool-types.js"; import { MIDDLEWARE } from "../data.js"; -export function register(server: McpServer): void { +export function register(server: ToolServer): void { server.tool( "echo_list_middleware", "List all available Echo framework middleware with their purpose and recommended chain order.", diff --git a/src/plugins/echo/tools/list-recipes.ts b/src/plugins/echo/tools/list-recipes.ts index 1682d58..ba6f5c7 100644 --- a/src/plugins/echo/tools/list-recipes.ts +++ b/src/plugins/echo/tools/list-recipes.ts @@ -1,8 +1,8 @@ -import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import type { ToolServer } from "../../../shared/tool-types.js"; import { z } from "zod"; import { RECIPES, RECIPE_CATEGORIES } from "../data.js"; -export function register(server: McpServer): void { +export function register(server: ToolServer): void { server.tool( "echo_list_recipes", "List all Go Echo framework recipes. Optionally filter by category.", diff --git a/src/plugins/echo/tools/search-docs.ts b/src/plugins/echo/tools/search-docs.ts index abce20d..b3c59fd 100644 --- a/src/plugins/echo/tools/search-docs.ts +++ b/src/plugins/echo/tools/search-docs.ts @@ -1,8 +1,8 @@ -import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import type { ToolServer } from "../../../shared/tool-types.js"; import { z } from "zod"; import { searchRecipes, searchMiddleware, RECIPE_CATEGORIES } from "../data.js"; -export function register(server: McpServer): void { +export function register(server: ToolServer): void { server.tool( "echo_search_docs", "Search Echo framework recipes and middleware by keyword.", diff --git a/src/plugins/golang/index.ts b/src/plugins/golang/index.ts index 2dafd4e..2458511 100644 --- a/src/plugins/golang/index.ts +++ b/src/plugins/golang/index.ts @@ -1,4 +1,4 @@ -import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import type { ToolServer } from "../../shared/tool-types.js"; import type { Plugin } from "../../registry.js"; import { register as listPractices } from "./tools/list-practices.js"; import { register as getPractice } from "./tools/get-practice.js"; @@ -7,7 +7,7 @@ import { register as getPattern } from "./tools/get-pattern.js"; import { register as getAntipatterns } from "./tools/get-antipatterns.js"; import { register as searchDocs } from "./tools/search-docs.js"; -function register(server: McpServer): void { +function register(server: ToolServer): void { listPractices(server); getPractice(server); listPatterns(server); diff --git a/src/plugins/golang/tools/get-antipatterns.ts b/src/plugins/golang/tools/get-antipatterns.ts index f8f8af6..b616bfe 100644 --- a/src/plugins/golang/tools/get-antipatterns.ts +++ b/src/plugins/golang/tools/get-antipatterns.ts @@ -1,7 +1,7 @@ -import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import type { ToolServer } from "../../../shared/tool-types.js"; import { ANTI_PATTERNS } from "../data.js"; -export function register(server: McpServer): void { +export function register(server: ToolServer): void { server.tool( "golang_get_antipatterns", "List all Go anti-patterns to avoid", diff --git a/src/plugins/golang/tools/get-pattern.ts b/src/plugins/golang/tools/get-pattern.ts index e17dd28..59f6bc4 100644 --- a/src/plugins/golang/tools/get-pattern.ts +++ b/src/plugins/golang/tools/get-pattern.ts @@ -1,8 +1,8 @@ -import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import type { ToolServer } from "../../../shared/tool-types.js"; import { z } from "zod"; import { DESIGN_PATTERNS } from "../data.js"; -export function register(server: McpServer): void { +export function register(server: ToolServer): void { server.tool( "golang_get_pattern", "Get a Go design pattern with idiomatic code", diff --git a/src/plugins/golang/tools/get-practice.ts b/src/plugins/golang/tools/get-practice.ts index 2c1b352..f5975d9 100644 --- a/src/plugins/golang/tools/get-practice.ts +++ b/src/plugins/golang/tools/get-practice.ts @@ -1,4 +1,4 @@ -import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import type { ToolServer } from "../../../shared/tool-types.js"; import { readFileSync } from "node:fs"; import { dirname, join } from "node:path"; import { fileURLToPath } from "node:url"; @@ -98,7 +98,7 @@ function loadCorpusPractice(name: string): LoadedCorpusPractice | null { } } -export function register(server: McpServer): void { +export function register(server: ToolServer): void { server.tool( "golang_get_practice", "Get a Go best practice with good/bad code examples", diff --git a/src/plugins/golang/tools/list-patterns.ts b/src/plugins/golang/tools/list-patterns.ts index d7b1b72..da038ec 100644 --- a/src/plugins/golang/tools/list-patterns.ts +++ b/src/plugins/golang/tools/list-patterns.ts @@ -1,8 +1,8 @@ -import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import type { ToolServer } from "../../../shared/tool-types.js"; import { z } from "zod"; import { DESIGN_PATTERNS, getPatternsByCategory } from "../data.js"; -export function register(server: McpServer): void { +export function register(server: ToolServer): void { server.tool( "golang_list_patterns", "List Go design patterns by category (creational, structural, behavioral, concurrency)", diff --git a/src/plugins/golang/tools/list-practices.ts b/src/plugins/golang/tools/list-practices.ts index 8c74903..c792142 100644 --- a/src/plugins/golang/tools/list-practices.ts +++ b/src/plugins/golang/tools/list-practices.ts @@ -1,8 +1,8 @@ -import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import type { ToolServer } from "../../../shared/tool-types.js"; import { z } from "zod"; import { BEST_PRACTICES, TOPICS, getPracticesByTopic } from "../data.js"; -export function register(server: McpServer): void { +export function register(server: ToolServer): void { server.tool( "golang_list_practices", "List Go best practices by topic and priority (P0=critical, P1=standard)", diff --git a/src/plugins/golang/tools/search-docs.ts b/src/plugins/golang/tools/search-docs.ts index bf2f28b..95b4f7d 100644 --- a/src/plugins/golang/tools/search-docs.ts +++ b/src/plugins/golang/tools/search-docs.ts @@ -1,8 +1,8 @@ -import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import type { ToolServer } from "../../../shared/tool-types.js"; import { z } from "zod"; import { searchAll } from "../data.js"; -export function register(server: McpServer): void { +export function register(server: ToolServer): void { server.tool( "golang_search_docs", "Search Go best practices and design patterns by keyword", diff --git a/src/plugins/hyperstack/index.ts b/src/plugins/hyperstack/index.ts index 7904f0a..cc20052 100644 --- a/src/plugins/hyperstack/index.ts +++ b/src/plugins/hyperstack/index.ts @@ -1,10 +1,9 @@ -import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; -import { Plugin } from "../../registry.js"; +import type { Plugin, ToolServer } from "../../shared/tool-types.js"; import { registerSetupTool } from "./tools/setup.js"; export const hyperstackPlugin: Plugin = { name: "hyperstack", - register: (server: McpServer) => { + register: (server: ToolServer) => { registerSetupTool(server); }, }; diff --git a/src/plugins/hyperstack/tools/setup.ts b/src/plugins/hyperstack/tools/setup.ts index 7499bf0..682e2fa 100644 --- a/src/plugins/hyperstack/tools/setup.ts +++ b/src/plugins/hyperstack/tools/setup.ts @@ -1,9 +1,9 @@ -import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import type { ToolServer } from "../../../shared/tool-types.js"; import { z } from "zod"; import * as setup from "../../../internal/setup-hyperstack.js"; import * as path from "node:path"; -export function registerSetupTool(server: McpServer) { +export function registerSetupTool(server: ToolServer) { server.tool( "hyperstack_setup", "Identify current IDE/CLI environment and generate a tailored MCP configuration patch for Hyperstack.", diff --git a/src/plugins/lenis/index.ts b/src/plugins/lenis/index.ts index e28b539..5597370 100644 --- a/src/plugins/lenis/index.ts +++ b/src/plugins/lenis/index.ts @@ -1,4 +1,4 @@ -import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import type { ToolServer } from "../../shared/tool-types.js"; import type { Plugin } from "../../registry.js"; import { register as listApis } from "./tools/list-apis.js"; import { register as getApi } from "./tools/get-api.js"; @@ -7,7 +7,7 @@ import { register as getPattern } from "./tools/get-pattern.js"; import { register as generateSetup } from "./tools/generate-setup.js"; import { register as cheatsheet } from "./tools/cheatsheet.js"; -function register(server: McpServer): void { +function register(server: ToolServer): void { listApis(server); getApi(server); searchDocs(server); diff --git a/src/plugins/lenis/tools/cheatsheet.ts b/src/plugins/lenis/tools/cheatsheet.ts index 967eaed..218873a 100644 --- a/src/plugins/lenis/tools/cheatsheet.ts +++ b/src/plugins/lenis/tools/cheatsheet.ts @@ -1,6 +1,6 @@ -import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import type { ToolServer } from "../../../shared/tool-types.js"; -export function register(server: McpServer): void { +export function register(server: ToolServer): void { server.resource( "lenis-cheatsheet", "lenis://react/cheatsheet", diff --git a/src/plugins/lenis/tools/generate-setup.ts b/src/plugins/lenis/tools/generate-setup.ts index 3a84ce9..6d93cad 100644 --- a/src/plugins/lenis/tools/generate-setup.ts +++ b/src/plugins/lenis/tools/generate-setup.ts @@ -1,7 +1,7 @@ -import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import type { ToolServer } from "../../../shared/tool-types.js"; import { z } from "zod"; -export function register(server: McpServer): void { +export function register(server: ToolServer): void { server.tool( "lenis_generate_setup", "Generate complete Lenis setup code from a natural-language description. Handles Next.js, GSAP, Framer Motion, basic React, and custom container scenarios.", diff --git a/src/plugins/lenis/tools/get-api.ts b/src/plugins/lenis/tools/get-api.ts index 42afecb..a02d908 100644 --- a/src/plugins/lenis/tools/get-api.ts +++ b/src/plugins/lenis/tools/get-api.ts @@ -1,8 +1,8 @@ -import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import type { ToolServer } from "../../../shared/tool-types.js"; import { z } from "zod"; import { ALL_APIS, searchApis, getApiByName, formatApiReference } from "../data.js"; -export function register(server: McpServer): void { +export function register(server: ToolServer): void { server.tool( "lenis_get_api", "Get detailed API reference for a specific Lenis API - props, options, usage examples, and tips.", diff --git a/src/plugins/lenis/tools/get-pattern.ts b/src/plugins/lenis/tools/get-pattern.ts index e01dd34..fbee5c4 100644 --- a/src/plugins/lenis/tools/get-pattern.ts +++ b/src/plugins/lenis/tools/get-pattern.ts @@ -1,4 +1,4 @@ -import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import type { ToolServer } from "../../../shared/tool-types.js"; import { readFileSync } from "node:fs"; import { dirname, join } from "node:path"; import { fileURLToPath } from "node:url"; @@ -71,7 +71,7 @@ function loadCorpusPattern(name: string): { name: string; pattern: CorpusPattern } } -export function register(server: McpServer): void { +export function register(server: ToolServer): void { server.tool( "lenis_get_pattern", "Get a complete Lenis integration pattern with full production-ready code. Covers Next.js setup, GSAP integration, Framer Motion sync, custom containers, accessibility, and navigation.", diff --git a/src/plugins/lenis/tools/list-apis.ts b/src/plugins/lenis/tools/list-apis.ts index 511afe6..6293a67 100644 --- a/src/plugins/lenis/tools/list-apis.ts +++ b/src/plugins/lenis/tools/list-apis.ts @@ -1,8 +1,8 @@ -import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import type { ToolServer } from "../../../shared/tool-types.js"; import { z } from "zod"; import { ALL_APIS, API_KINDS, capitalize } from "../data.js"; -export function register(server: McpServer): void { +export function register(server: ToolServer): void { server.tool( "lenis_list_apis", "List all Lenis smooth scroll APIs - ReactLenis component, useLenis hook, LenisRef and LenisOptions types.", diff --git a/src/plugins/lenis/tools/search-docs.ts b/src/plugins/lenis/tools/search-docs.ts index 018e20f..50745c1 100644 --- a/src/plugins/lenis/tools/search-docs.ts +++ b/src/plugins/lenis/tools/search-docs.ts @@ -1,8 +1,8 @@ -import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import type { ToolServer } from "../../../shared/tool-types.js"; import { z } from "zod"; import { searchApis, PATTERNS } from "../data.js"; -export function register(server: McpServer): void { +export function register(server: ToolServer): void { server.tool( "lenis_search_docs", "Search Lenis documentation by keyword. Searches API names, descriptions, code examples, and integration patterns.", diff --git a/src/plugins/motion/index.ts b/src/plugins/motion/index.ts index a29a56f..9df40d1 100644 --- a/src/plugins/motion/index.ts +++ b/src/plugins/motion/index.ts @@ -1,4 +1,4 @@ -import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import type { ToolServer } from "../../shared/tool-types.js"; import type { Plugin } from "../../registry.js"; import { register as listApis } from "./tools/list-apis.js"; import { register as getApi } from "./tools/get-api.js"; @@ -8,7 +8,7 @@ import { register as getTransitions } from "./tools/get-transitions.js"; import { register as generateAnimation } from "./tools/generate-animation.js"; import { register as cheatsheet } from "./tools/cheatsheet.js"; -function register(server: McpServer): void { +function register(server: ToolServer): void { listApis(server); getApi(server); searchDocs(server); diff --git a/src/plugins/motion/tools/cheatsheet.ts b/src/plugins/motion/tools/cheatsheet.ts index 8fcdde0..de868f0 100644 --- a/src/plugins/motion/tools/cheatsheet.ts +++ b/src/plugins/motion/tools/cheatsheet.ts @@ -1,6 +1,6 @@ -import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import type { ToolServer } from "../../../shared/tool-types.js"; -export function register(server: McpServer): void { +export function register(server: ToolServer): void { server.resource( "cheatsheet", "motion://react/cheatsheet", diff --git a/src/plugins/motion/tools/generate-animation.ts b/src/plugins/motion/tools/generate-animation.ts index 79acdb5..c0043dd 100644 --- a/src/plugins/motion/tools/generate-animation.ts +++ b/src/plugins/motion/tools/generate-animation.ts @@ -1,7 +1,7 @@ -import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import type { ToolServer } from "../../../shared/tool-types.js"; import { z } from "zod"; -export function register(server: McpServer): void { +export function register(server: ToolServer): void { server.tool( "motion_generate_animation", "Generate a Motion for React animation snippet from a natural-language description. Returns ready-to-use JSX.", diff --git a/src/plugins/motion/tools/get-api.ts b/src/plugins/motion/tools/get-api.ts index 3999369..5944693 100644 --- a/src/plugins/motion/tools/get-api.ts +++ b/src/plugins/motion/tools/get-api.ts @@ -1,4 +1,4 @@ -import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import type { ToolServer } from "../../../shared/tool-types.js"; import { readFileSync } from "node:fs"; import { dirname, join } from "node:path"; import { fileURLToPath } from "node:url"; @@ -90,7 +90,7 @@ function loadCorpusApi(name: string): LoadedCorpusApi | null { } } -export function register(server: McpServer): void { +export function register(server: ToolServer): void { server.tool( "motion_get_api", "Get detailed API reference for a specific Motion for React component, hook, or utility. Includes props, usage, examples, and tips.", diff --git a/src/plugins/motion/tools/get-examples.ts b/src/plugins/motion/tools/get-examples.ts index 34fe8c5..145da3a 100644 --- a/src/plugins/motion/tools/get-examples.ts +++ b/src/plugins/motion/tools/get-examples.ts @@ -1,4 +1,4 @@ -import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import type { ToolServer } from "../../../shared/tool-types.js"; import { readFileSync } from "node:fs"; import { dirname, join } from "node:path"; import { fileURLToPath } from "node:url"; @@ -93,7 +93,7 @@ function loadCorpusExamples(category: string): { examples: Example[]; source: st } } -export function register(server: McpServer): void { +export function register(server: ToolServer): void { server.tool( "motion_get_examples", "Get code examples for a specific animation category", diff --git a/src/plugins/motion/tools/get-transitions.ts b/src/plugins/motion/tools/get-transitions.ts index 93e7631..8f7c1ae 100644 --- a/src/plugins/motion/tools/get-transitions.ts +++ b/src/plugins/motion/tools/get-transitions.ts @@ -1,7 +1,7 @@ -import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import type { ToolServer } from "../../../shared/tool-types.js"; import { TRANSITIONS_REFERENCE } from "../data.js"; -export function register(server: McpServer): void { +export function register(server: ToolServer): void { server.tool( "motion_get_transitions", "Get the complete transition types reference (tween, spring, inertia, orchestration, per-value config)", diff --git a/src/plugins/motion/tools/list-apis.ts b/src/plugins/motion/tools/list-apis.ts index 3ec6d49..30be9fd 100644 --- a/src/plugins/motion/tools/list-apis.ts +++ b/src/plugins/motion/tools/list-apis.ts @@ -1,8 +1,8 @@ -import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import type { ToolServer } from "../../../shared/tool-types.js"; import { z } from "zod"; import { ALL_APIS, API_KINDS, capitalize } from "../data.js"; -export function register(server: McpServer): void { +export function register(server: ToolServer): void { server.tool( "motion_list_apis", "List all available Motion for React APIs (components, hooks, utilities)", diff --git a/src/plugins/motion/tools/search-docs.ts b/src/plugins/motion/tools/search-docs.ts index 3a0d928..b5dd6b2 100644 --- a/src/plugins/motion/tools/search-docs.ts +++ b/src/plugins/motion/tools/search-docs.ts @@ -1,8 +1,8 @@ -import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import type { ToolServer } from "../../../shared/tool-types.js"; import { z } from "zod"; import { CATEGORIES, searchApis, formatExample } from "../data.js"; -export function register(server: McpServer): void { +export function register(server: ToolServer): void { server.tool( "motion_search_docs", "Search Motion for React documentation by keyword. Searches API names, descriptions, and code examples.", diff --git a/src/plugins/react/index.ts b/src/plugins/react/index.ts index 2c2e1d3..51718a8 100644 --- a/src/plugins/react/index.ts +++ b/src/plugins/react/index.ts @@ -1,11 +1,11 @@ -import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import type { ToolServer } from "../../shared/tool-types.js"; import type { Plugin } from "../../registry.js"; import { register as listPatterns } from "./tools/list-patterns.js"; import { register as getPattern } from "./tools/get-pattern.js"; import { register as getConstraints } from "./tools/get-constraints.js"; import { register as searchDocs } from "./tools/search-docs.js"; -function register(server: McpServer): void { +function register(server: ToolServer): void { listPatterns(server); getPattern(server); getConstraints(server); diff --git a/src/plugins/react/tools/get-constraints.ts b/src/plugins/react/tools/get-constraints.ts index 102683e..484b1f2 100644 --- a/src/plugins/react/tools/get-constraints.ts +++ b/src/plugins/react/tools/get-constraints.ts @@ -1,7 +1,7 @@ -import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import type { ToolServer } from "../../../shared/tool-types.js"; import { CONSTRAINTS } from "../data.js"; -export function register(server: McpServer): void { +export function register(server: ToolServer): void { server.tool( "react_get_constraints", "List all forbidden React/Next.js patterns and their reasons", diff --git a/src/plugins/react/tools/get-pattern.ts b/src/plugins/react/tools/get-pattern.ts index a953bad..d9b331b 100644 --- a/src/plugins/react/tools/get-pattern.ts +++ b/src/plugins/react/tools/get-pattern.ts @@ -1,4 +1,4 @@ -import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import type { ToolServer } from "../../../shared/tool-types.js"; import { readFileSync } from "node:fs"; import { dirname, join } from "node:path"; import { fileURLToPath } from "node:url"; @@ -98,7 +98,7 @@ function loadCorpusPattern(name: string): LoadedCorpusPattern | null { } } -export function register(server: McpServer): void { +export function register(server: ToolServer): void { server.tool( "react_get_pattern", "Get a React/Next.js pattern with full code example and anti-pattern", diff --git a/src/plugins/react/tools/list-patterns.ts b/src/plugins/react/tools/list-patterns.ts index 53abfca..c0a30d6 100644 --- a/src/plugins/react/tools/list-patterns.ts +++ b/src/plugins/react/tools/list-patterns.ts @@ -1,8 +1,8 @@ -import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import type { ToolServer } from "../../../shared/tool-types.js"; import { z } from "zod"; import { PATTERNS, PATTERN_CATEGORIES, getPatternsByCategory } from "../data.js"; -export function register(server: McpServer): void { +export function register(server: ToolServer): void { server.tool( "react_list_patterns", "List all React/Next.js patterns by category", diff --git a/src/plugins/react/tools/search-docs.ts b/src/plugins/react/tools/search-docs.ts index e13ab78..41c953f 100644 --- a/src/plugins/react/tools/search-docs.ts +++ b/src/plugins/react/tools/search-docs.ts @@ -1,8 +1,8 @@ -import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import type { ToolServer } from "../../../shared/tool-types.js"; import { z } from "zod"; import { searchPatterns, CONSTRAINTS } from "../data.js"; -export function register(server: McpServer): void { +export function register(server: ToolServer): void { server.tool( "react_search_docs", "Search React/Next.js patterns and constraints by keyword", diff --git a/src/plugins/reactflow/index.ts b/src/plugins/reactflow/index.ts index 9586952..05b3c23 100644 --- a/src/plugins/reactflow/index.ts +++ b/src/plugins/reactflow/index.ts @@ -1,4 +1,4 @@ -import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import type { ToolServer } from "../../shared/tool-types.js"; import type { Plugin } from "../../registry.js"; import { register as listApis } from "./tools/list-apis.js"; import { register as getApi } from "./tools/get-api.js"; @@ -10,7 +10,7 @@ import { register as getMigrationGuide } from "./tools/get-migration-guide.js"; import { register as generateFlow } from "./tools/generate-flow.js"; import { register as cheatsheet } from "./tools/cheatsheet.js"; -function register(server: McpServer): void { +function register(server: ToolServer): void { listApis(server); getApi(server); searchDocs(server); diff --git a/src/plugins/reactflow/tools/cheatsheet.ts b/src/plugins/reactflow/tools/cheatsheet.ts index 0838d02..1466221 100644 --- a/src/plugins/reactflow/tools/cheatsheet.ts +++ b/src/plugins/reactflow/tools/cheatsheet.ts @@ -1,6 +1,6 @@ -import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import type { ToolServer } from "../../../shared/tool-types.js"; -export function register(server: McpServer): void { +export function register(server: ToolServer): void { server.resource( "cheatsheet", "reactflow://cheatsheet", diff --git a/src/plugins/reactflow/tools/generate-flow.ts b/src/plugins/reactflow/tools/generate-flow.ts index 4369071..d730df8 100644 --- a/src/plugins/reactflow/tools/generate-flow.ts +++ b/src/plugins/reactflow/tools/generate-flow.ts @@ -1,7 +1,7 @@ -import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import type { ToolServer } from "../../../shared/tool-types.js"; import { z } from "zod"; -export function register(server: McpServer): void { +export function register(server: ToolServer): void { server.tool( "reactflow_generate_flow", "Generate a React Flow component from a natural-language description. Returns ready-to-use TSX with proper imports.", diff --git a/src/plugins/reactflow/tools/get-api.ts b/src/plugins/reactflow/tools/get-api.ts index 8c99119..783493b 100644 --- a/src/plugins/reactflow/tools/get-api.ts +++ b/src/plugins/reactflow/tools/get-api.ts @@ -1,4 +1,4 @@ -import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import type { ToolServer } from "../../../shared/tool-types.js"; import { readFileSync } from "node:fs"; import { dirname, join } from "node:path"; import { fileURLToPath } from "node:url"; @@ -118,7 +118,7 @@ function loadCorpusApi(name: string): LoadedCorpusApi | null { } } -export function register(server: McpServer): void { +export function register(server: ToolServer): void { server.tool( "reactflow_get_api", "Get detailed API reference for a specific React Flow component, hook, utility, or type. Includes props, usage, examples, and tips.", diff --git a/src/plugins/reactflow/tools/get-examples.ts b/src/plugins/reactflow/tools/get-examples.ts index b1dfcc6..cd8ceb3 100644 --- a/src/plugins/reactflow/tools/get-examples.ts +++ b/src/plugins/reactflow/tools/get-examples.ts @@ -1,8 +1,8 @@ -import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import type { ToolServer } from "../../../shared/tool-types.js"; import { z } from "zod"; import { ALL_APIS, CATEGORIES, getExamplesByCategory, formatExample, capitalize } from "../data/index.js"; -export function register(server: McpServer): void { +export function register(server: ToolServer): void { server.tool( "reactflow_get_examples", "Get code examples for a specific React Flow category", diff --git a/src/plugins/reactflow/tools/get-migration-guide.ts b/src/plugins/reactflow/tools/get-migration-guide.ts index 2ef077c..39c9700 100644 --- a/src/plugins/reactflow/tools/get-migration-guide.ts +++ b/src/plugins/reactflow/tools/get-migration-guide.ts @@ -1,7 +1,7 @@ -import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import type { ToolServer } from "../../../shared/tool-types.js"; import { V12_MIGRATION } from "../data/index.js"; -export function register(server: McpServer): void { +export function register(server: ToolServer): void { server.tool( "reactflow_get_migration_guide", "Get the React Flow v11 to v12 migration guide with all breaking changes, import changes, and type changes", diff --git a/src/plugins/reactflow/tools/get-pattern.ts b/src/plugins/reactflow/tools/get-pattern.ts index 45deb82..b7f09e6 100644 --- a/src/plugins/reactflow/tools/get-pattern.ts +++ b/src/plugins/reactflow/tools/get-pattern.ts @@ -1,8 +1,8 @@ -import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import type { ToolServer } from "../../../shared/tool-types.js"; import { z } from "zod"; import { PATTERNS, PATTERN_SECTIONS } from "../data/index.js"; -export function register(server: McpServer): void { +export function register(server: ToolServer): void { server.tool( "reactflow_get_pattern", "Get an enterprise React Flow pattern with full implementation code. Patterns include store architecture, undo/redo, drag-and-drop, auto-layout, context menus, copy/paste, save/restore, DAG validation, keyboard shortcuts, performance, dark mode, SSR, subflows, edge reconnection, and more.", diff --git a/src/plugins/reactflow/tools/get-template.ts b/src/plugins/reactflow/tools/get-template.ts index 6190b31..61eebe4 100644 --- a/src/plugins/reactflow/tools/get-template.ts +++ b/src/plugins/reactflow/tools/get-template.ts @@ -1,8 +1,8 @@ -import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import type { ToolServer } from "../../../shared/tool-types.js"; import { z } from "zod"; import { TEMPLATES, capitalize } from "../data/index.js"; -export function register(server: McpServer): void { +export function register(server: ToolServer): void { server.tool( "reactflow_get_template", "Get a production-ready code template: custom-node (Tailwind + toolbar + handles + status), custom-edge (delete button + BaseEdge), or zustand-store (full store with selectors)", @@ -11,7 +11,7 @@ export function register(server: McpServer): void { .enum(["custom-node", "custom-edge", "zustand-store"] as const) .describe("Template name"), }, - async ({ template }) => { + async ({ template }: { template: "custom-node" | "custom-edge" | "zustand-store" }) => { const code = TEMPLATES[template]; return { content: [ diff --git a/src/plugins/reactflow/tools/list-apis.ts b/src/plugins/reactflow/tools/list-apis.ts index 8cf3376..4a9181d 100644 --- a/src/plugins/reactflow/tools/list-apis.ts +++ b/src/plugins/reactflow/tools/list-apis.ts @@ -1,8 +1,8 @@ -import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import type { ToolServer } from "../../../shared/tool-types.js"; import { z } from "zod"; import { ALL_APIS, capitalize, API_KINDS } from "../data/index.js"; -export function register(server: McpServer): void { +export function register(server: ToolServer): void { server.tool( "reactflow_list_apis", "List all React Flow v12 APIs - components, hooks, utilities, and types", diff --git a/src/plugins/reactflow/tools/search-docs.ts b/src/plugins/reactflow/tools/search-docs.ts index 9827120..11eddc0 100644 --- a/src/plugins/reactflow/tools/search-docs.ts +++ b/src/plugins/reactflow/tools/search-docs.ts @@ -1,8 +1,8 @@ -import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import type { ToolServer } from "../../../shared/tool-types.js"; import { z } from "zod"; import { ALL_APIS, CATEGORIES, PATTERN_SECTIONS, searchApis, formatExample } from "../data/index.js"; -export function register(server: McpServer): void { +export function register(server: ToolServer): void { server.tool( "reactflow_search_docs", "Search React Flow documentation by keyword. Searches API names, descriptions, code examples, and tips.", diff --git a/src/plugins/rust/index.ts b/src/plugins/rust/index.ts index 1f521bc..bde2299 100644 --- a/src/plugins/rust/index.ts +++ b/src/plugins/rust/index.ts @@ -1,11 +1,11 @@ -import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import type { ToolServer } from "../../shared/tool-types.js"; import type { Plugin } from "../../registry.js"; import { register as listPractices } from "./tools/list-practices.js"; import { register as getPractice } from "./tools/get-practice.js"; import { register as searchDocs } from "./tools/search-docs.js"; import { register as cheatsheet } from "./tools/cheatsheet.js"; -function register(server: McpServer): void { +function register(server: ToolServer): void { listPractices(server); getPractice(server); searchDocs(server); diff --git a/src/plugins/rust/tools/cheatsheet.ts b/src/plugins/rust/tools/cheatsheet.ts index 2cf0a0a..a871917 100644 --- a/src/plugins/rust/tools/cheatsheet.ts +++ b/src/plugins/rust/tools/cheatsheet.ts @@ -1,9 +1,9 @@ -import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import type { ToolServer } from "../../../shared/tool-types.js"; import { snippet } from "../loader.js"; const CHEATSHEET = snippet("cheatsheet.txt"); -export function register(server: McpServer): void { +export function register(server: ToolServer): void { server.tool( "rust_cheatsheet", "Quick Rust reference: ownership rules, error handling, clippy commands, and key patterns", diff --git a/src/plugins/rust/tools/get-practice.ts b/src/plugins/rust/tools/get-practice.ts index 25cf811..9750dc9 100644 --- a/src/plugins/rust/tools/get-practice.ts +++ b/src/plugins/rust/tools/get-practice.ts @@ -1,4 +1,4 @@ -import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import type { ToolServer } from "../../../shared/tool-types.js"; import { readFileSync } from "node:fs"; import { dirname, join } from "node:path"; import { fileURLToPath } from "node:url"; @@ -97,7 +97,7 @@ function loadCorpusPractice(name: string): LoadedCorpusPractice | null { } } -export function register(server: McpServer): void { +export function register(server: ToolServer): void { server.tool( "rust_get_practice", "Get a Rust best practice with code examples", diff --git a/src/plugins/rust/tools/list-practices.ts b/src/plugins/rust/tools/list-practices.ts index 761ae6e..9075b3e 100644 --- a/src/plugins/rust/tools/list-practices.ts +++ b/src/plugins/rust/tools/list-practices.ts @@ -1,8 +1,8 @@ -import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import type { ToolServer } from "../../../shared/tool-types.js"; import { z } from "zod"; import { BEST_PRACTICES, CHAPTERS, getPracticesByChapter } from "../data.js"; -export function register(server: McpServer): void { +export function register(server: ToolServer): void { server.tool( "rust_list_practices", "List Rust best practices by chapter", diff --git a/src/plugins/rust/tools/search-docs.ts b/src/plugins/rust/tools/search-docs.ts index 3398154..307494e 100644 --- a/src/plugins/rust/tools/search-docs.ts +++ b/src/plugins/rust/tools/search-docs.ts @@ -1,8 +1,8 @@ -import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import type { ToolServer } from "../../../shared/tool-types.js"; import { z } from "zod"; import { searchPractices } from "../data.js"; -export function register(server: McpServer): void { +export function register(server: ToolServer): void { server.tool( "rust_search_docs", "Search Rust best practices by keyword", diff --git a/src/plugins/shadcn/index.ts b/src/plugins/shadcn/index.ts index fb2fb90..d052266 100644 --- a/src/plugins/shadcn/index.ts +++ b/src/plugins/shadcn/index.ts @@ -1,4 +1,4 @@ -import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import type { ToolServer } from "../../shared/tool-types.js"; import type { Plugin } from "../../registry.js"; import { register as listComponents } from "./tools/list-components.js"; import { register as getComponent } from "./tools/get-component.js"; @@ -6,7 +6,7 @@ import { register as getRules } from "./tools/get-rules.js"; import { register as getSnippet } from "./tools/get-snippet.js"; import { register as getComposition } from "./tools/get-composition.js"; -function register(server: McpServer): void { +function register(server: ToolServer): void { listComponents(server); getComponent(server); getRules(server); diff --git a/src/plugins/shadcn/tools/get-component.ts b/src/plugins/shadcn/tools/get-component.ts index e21761f..f05328c 100644 --- a/src/plugins/shadcn/tools/get-component.ts +++ b/src/plugins/shadcn/tools/get-component.ts @@ -1,4 +1,4 @@ -import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import type { ToolServer } from "../../../shared/tool-types.js"; import { readFileSync } from "node:fs"; import { dirname, join } from "node:path"; import { fileURLToPath } from "node:url"; @@ -107,7 +107,7 @@ function loadCorpusComponent(name: string): LoadedCorpusComponent | null { } } -export function register(server: McpServer): void { +export function register(server: ToolServer): void { server.tool( "shadcn_get_component", "Get full details for a shadcn/ui component: base primitive, data-slots, variants, sizes, usage example, and which other components it pairs with. Uses curated reference data.", diff --git a/src/plugins/shadcn/tools/get-composition.ts b/src/plugins/shadcn/tools/get-composition.ts index e8ad27b..b191706 100644 --- a/src/plugins/shadcn/tools/get-composition.ts +++ b/src/plugins/shadcn/tools/get-composition.ts @@ -1,4 +1,4 @@ -import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import type { ToolServer } from "../../../shared/tool-types.js"; import { z } from "zod"; import { PAGE_TYPE_NAMES, getPageComposition } from "../data.js"; @@ -7,7 +7,7 @@ import { PAGE_TYPE_NAMES, getPageComposition } from "../data.js"; * When forge-plan processes a DESIGN.md with a specific page type, it calls this tool * to get concrete shadcn component recommendations per section. */ -export function register(server: McpServer): void { +export function register(server: ToolServer): void { server.tool( "shadcn_get_composition", "Get shadcn/ui component composition for a specific page type. Returns which components to combine per section with rationale. This is the bridge from designer's page templates to concrete implementation. Use after designer_get_page_template.", diff --git a/src/plugins/shadcn/tools/get-rules.ts b/src/plugins/shadcn/tools/get-rules.ts index cd42a15..765987d 100644 --- a/src/plugins/shadcn/tools/get-rules.ts +++ b/src/plugins/shadcn/tools/get-rules.ts @@ -1,7 +1,7 @@ -import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import type { ToolServer } from "../../../shared/tool-types.js"; import { SHADCN_RULES, ARCHITECTURAL_CHECKLIST } from "../data.js"; -export function register(server: McpServer): void { +export function register(server: ToolServer): void { server.tool( "shadcn_get_rules", "Get the architectural rules and mandatory checklist for shadcn/ui (Base UI edition). Call this before proposing any new component or modification.", diff --git a/src/plugins/shadcn/tools/get-snippet.ts b/src/plugins/shadcn/tools/get-snippet.ts index 44791b0..29732bd 100644 --- a/src/plugins/shadcn/tools/get-snippet.ts +++ b/src/plugins/shadcn/tools/get-snippet.ts @@ -1,8 +1,8 @@ -import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import type { ToolServer } from "../../../shared/tool-types.js"; import { z } from "zod"; import { getComponentByName, SHADCN_COMPONENTS } from "../data.js"; -export function register(server: McpServer): void { +export function register(server: ToolServer): void { server.tool( "shadcn_get_snippet", "Get a usage code snippet for a shadcn/ui component. Returns the canonical example showing variants, sizes, and composition patterns.", diff --git a/src/plugins/shadcn/tools/list-components.ts b/src/plugins/shadcn/tools/list-components.ts index 9edd68b..3a53b4e 100644 --- a/src/plugins/shadcn/tools/list-components.ts +++ b/src/plugins/shadcn/tools/list-components.ts @@ -1,8 +1,8 @@ -import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import type { ToolServer } from "../../../shared/tool-types.js"; import { z } from "zod"; import { SHADCN_COMPONENTS, COMPONENT_CATEGORIES, getComponentsByCategory } from "../data.js"; -export function register(server: McpServer): void { +export function register(server: ToolServer): void { server.tool( "shadcn_list_components", "List all curated shadcn/ui components (Base UI edition). Optionally filter by category: button, input, card, dialog, dropdown, tabs, table, form, navigation, feedback, overlay, data-display.", diff --git a/src/plugins/ui-ux/index.ts b/src/plugins/ui-ux/index.ts index e7443a1..170ef8c 100644 --- a/src/plugins/ui-ux/index.ts +++ b/src/plugins/ui-ux/index.ts @@ -1,4 +1,4 @@ -import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import type { ToolServer } from "../../shared/tool-types.js"; import type { Plugin } from "../../registry.js"; import { register as listPrinciples } from "./tools/list-principles.js"; import { register as getPrinciple } from "./tools/get-principle.js"; @@ -7,7 +7,7 @@ import { register as getChecklist } from "./tools/get-checklist.js"; import { register as search } from "./tools/search.js"; import { register as getGotchas } from "./tools/get-gotchas.js"; -function register(server: McpServer): void { +function register(server: ToolServer): void { listPrinciples(server); getPrinciple(server); getComponentPattern(server); diff --git a/src/plugins/ui-ux/tools/get-checklist.ts b/src/plugins/ui-ux/tools/get-checklist.ts index 49aa310..e3592e5 100644 --- a/src/plugins/ui-ux/tools/get-checklist.ts +++ b/src/plugins/ui-ux/tools/get-checklist.ts @@ -1,8 +1,8 @@ -import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import type { ToolServer } from "../../../shared/tool-types.js"; import { z } from "zod"; import { DOMAINS, getChecklistByDomain } from "../data.js"; -export function register(server: McpServer): void { +export function register(server: ToolServer): void { server.tool( "ui_ux_get_checklist", "Get quality checklist for a UI/UX domain before shipping", diff --git a/src/plugins/ui-ux/tools/get-component-pattern.ts b/src/plugins/ui-ux/tools/get-component-pattern.ts index 189ea47..473b8e5 100644 --- a/src/plugins/ui-ux/tools/get-component-pattern.ts +++ b/src/plugins/ui-ux/tools/get-component-pattern.ts @@ -1,8 +1,8 @@ -import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import type { ToolServer } from "../../../shared/tool-types.js"; import { z } from "zod"; import { COMPONENT_PATTERNS, getComponentPatternByName } from "../data.js"; -export function register(server: McpServer): void { +export function register(server: ToolServer): void { server.tool( "ui_ux_get_component_pattern", "Get component pattern spec including variants, states, and sizing rules", diff --git a/src/plugins/ui-ux/tools/get-gotchas.ts b/src/plugins/ui-ux/tools/get-gotchas.ts index 0f07e2d..c196307 100644 --- a/src/plugins/ui-ux/tools/get-gotchas.ts +++ b/src/plugins/ui-ux/tools/get-gotchas.ts @@ -1,8 +1,8 @@ -import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import type { ToolServer } from "../../../shared/tool-types.js"; import { z } from "zod"; import { DOMAINS, getAllGotchas } from "../data.js"; -export function register(server: McpServer): void { +export function register(server: ToolServer): void { server.tool( "ui_ux_get_gotchas", "List all common UI/UX mistakes and fixes, optionally filtered by domain", diff --git a/src/plugins/ui-ux/tools/get-principle.ts b/src/plugins/ui-ux/tools/get-principle.ts index d1309be..2853089 100644 --- a/src/plugins/ui-ux/tools/get-principle.ts +++ b/src/plugins/ui-ux/tools/get-principle.ts @@ -1,4 +1,4 @@ -import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import type { ToolServer } from "../../../shared/tool-types.js"; import { readFileSync } from "node:fs"; import { dirname, join } from "node:path"; import { fileURLToPath } from "node:url"; @@ -97,7 +97,7 @@ function loadCorpusPrinciple(name: string): LoadedCorpusPrinciple | null { } } -export function register(server: McpServer): void { +export function register(server: ToolServer): void { server.tool( "ui_ux_get_principle", "Get full details for a UI/UX principle including examples, anti-patterns, and CSS examples", diff --git a/src/plugins/ui-ux/tools/list-principles.ts b/src/plugins/ui-ux/tools/list-principles.ts index 271620e..bcf0af0 100644 --- a/src/plugins/ui-ux/tools/list-principles.ts +++ b/src/plugins/ui-ux/tools/list-principles.ts @@ -1,8 +1,8 @@ -import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import type { ToolServer } from "../../../shared/tool-types.js"; import { z } from "zod"; import { PRINCIPLES, DOMAINS, getPrinciplesByDomain } from "../data.js"; -export function register(server: McpServer): void { +export function register(server: ToolServer): void { server.tool( "ui_ux_list_principles", "List all UI/UX principles by domain (typography, color, spacing, elevation, motion, accessibility, responsive, components)", diff --git a/src/plugins/ui-ux/tools/search.ts b/src/plugins/ui-ux/tools/search.ts index 2544f43..876b271 100644 --- a/src/plugins/ui-ux/tools/search.ts +++ b/src/plugins/ui-ux/tools/search.ts @@ -1,8 +1,8 @@ -import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import type { ToolServer } from "../../../shared/tool-types.js"; import { z } from "zod"; import { searchPrinciples, COMPONENT_PATTERNS } from "../data.js"; -export function register(server: McpServer): void { +export function register(server: ToolServer): void { server.tool( "ui_ux_search", "Search UI/UX principles and component patterns by keyword", diff --git a/src/registry.ts b/src/registry.ts index 1ece8b6..a7d95b6 100755 --- a/src/registry.ts +++ b/src/registry.ts @@ -1,11 +1,11 @@ -import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import type { ToolServer } from "./shared/tool-types.js"; export interface Plugin { name: string; - register: (server: McpServer) => void; + register: (server: ToolServer) => void; } -export function loadPlugins(server: McpServer, plugins: Plugin[]): void { +export function loadPlugins(server: ToolServer, plugins: Plugin[]): void { for (const plugin of plugins) { plugin.register(server); } diff --git a/src/shared/tool-types.ts b/src/shared/tool-types.ts new file mode 100644 index 0000000..60908d9 --- /dev/null +++ b/src/shared/tool-types.ts @@ -0,0 +1,29 @@ +export type ToolContent = { type: "text"; text: string }; + +export type ToolResult = { + content: ToolContent[]; + isError?: boolean; +}; + +export type ToolHandler = (args: TArgs) => Promise | ToolResult; + +export interface ToolServer { + tool( + name: string, + description: string, + schema: unknown, + handler: ToolHandler, + ): void; + resource( + name: string, + uriTemplate: string, + metadata: unknown, + handler: (...args: any[]) => Promise | unknown, + ): void; + prompt(name: string, description: string, schema: unknown, handler: (...args: any[]) => unknown): void; +} + +export interface Plugin { + name: string; + register: (server: ToolServer) => void; +} From fa4f0b67b0383584816de927fa49cddb46e0be51 Mon Sep 17 00:00:00 2001 From: Kailas Mahavarkar <66670953+KailasMahavarkar@users.noreply.github.com> Date: Wed, 22 Apr 2026 11:03:25 +0530 Subject: [PATCH 2/5] chore: delete MCP runtime, drop @modelcontextprotocol/sdk dependency, retire docker path Removes every file that existed solely to run Hyperstack as an MCP server and replaces the installation flow with the plain CLI entrypoint. Deleted: - src/index.ts - MCP stdio server boot - src/registry.ts - plugin aggregator used only by the MCP boot - src/plugins//index.ts for all 11 plugins - the aggregator wrappers around individual tool files. The stable tool names keep working because scripts/generate-local-tool-registry.ts scans src/plugins//tools/*.ts directly for `server.tool("name", ...)` matches. - src/plugins/hyperstack/ entirely - only hosted the hyperstack_setup tool which generated MCP config patches - src/internal/setup-hyperstack.ts - MCP config detection + generation helper - scripts/setup.ts - interactive MCP installer - scripts/start-mcp.ts - MCP stdio server launcher - scripts/ensure-singleton.ts - docker singleton guard - Dockerfile and .dockerignore package.json: - removes @modelcontextprotocol/sdk dependency - drops start, dev, docker:run, mcp:start, setup scripts - bumps version to 2.0.0 and retitles description to reflect the topology-driven CLI surface - bun install confirmed the SDK is no longer referenced anywhere Verified: - bun run build - typecheck exit 0 - bun run generate:local-tools - regenerates registry (76 tools; hyperstack_setup removed) - bun run generate:topology - topology bootstrap + allow/deny markdown regenerated clean - hyperstack tool designer_resolve_intent --json '{...}' returns the expected result - hyperstack route --json '{...}' returns a routed agent with required artifacts Topology CLI is now the sole runtime. Plugin tools stay stable through the generated local-tool-registry; the MCP transport is retired. Co-Authored-By: Claude Opus 4.7 (1M context) --- .dockerignore | 8 - Dockerfile | 7 - bun.lock | 181 ---------- generated/tool-index/local-tool-registry.json | 1 - generated/tool-index/local-tool-registry.ts | 1 - package.json | 12 +- scripts/ensure-singleton.ts | 44 --- scripts/setup.ts | 54 --- scripts/start-mcp.ts | 74 ---- src/index.ts | 63 ---- src/internal/setup-hyperstack.ts | 338 ------------------ src/plugins/design-tokens/index.ts | 24 -- src/plugins/designer/index.ts | 48 --- src/plugins/echo/index.ts | 19 - src/plugins/golang/index.ts | 22 -- src/plugins/hyperstack/index.ts | 9 - src/plugins/hyperstack/tools/setup.ts | 51 --- src/plugins/lenis/index.ts | 22 -- src/plugins/motion/index.ts | 24 -- src/plugins/react/index.ts | 18 - src/plugins/reactflow/index.ts | 28 -- src/plugins/rust/index.ts | 18 - src/plugins/shadcn/index.ts | 20 -- src/plugins/ui-ux/index.ts | 22 -- src/registry.ts | 12 - 25 files changed, 3 insertions(+), 1117 deletions(-) delete mode 100644 .dockerignore delete mode 100644 Dockerfile delete mode 100644 scripts/ensure-singleton.ts delete mode 100644 scripts/setup.ts delete mode 100644 scripts/start-mcp.ts delete mode 100755 src/index.ts delete mode 100644 src/internal/setup-hyperstack.ts delete mode 100644 src/plugins/design-tokens/index.ts delete mode 100644 src/plugins/designer/index.ts delete mode 100644 src/plugins/echo/index.ts delete mode 100644 src/plugins/golang/index.ts delete mode 100644 src/plugins/hyperstack/index.ts delete mode 100644 src/plugins/hyperstack/tools/setup.ts delete mode 100644 src/plugins/lenis/index.ts delete mode 100644 src/plugins/motion/index.ts delete mode 100644 src/plugins/react/index.ts delete mode 100644 src/plugins/reactflow/index.ts delete mode 100644 src/plugins/rust/index.ts delete mode 100644 src/plugins/shadcn/index.ts delete mode 100644 src/plugins/ui-ux/index.ts delete mode 100755 src/registry.ts diff --git a/.dockerignore b/.dockerignore deleted file mode 100644 index 8a9ea42..0000000 --- a/.dockerignore +++ /dev/null @@ -1,8 +0,0 @@ -node_modules/ -tests/ -docs/ -.git/ -.github/ -*.md -tsconfig.json -scripts/ diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index 12dc5af..0000000 --- a/Dockerfile +++ /dev/null @@ -1,7 +0,0 @@ -FROM oven/bun:alpine -WORKDIR /app -COPY package.json bun.lock ./ -RUN bun install --frozen-lockfile -COPY src/ src/ -USER bun -ENTRYPOINT ["bun", "src/index.ts"] diff --git a/bun.lock b/bun.lock index d00d454..d0c7ed1 100644 --- a/bun.lock +++ b/bun.lock @@ -5,7 +5,6 @@ "": { "name": "@orkait-ai/hyperstack", "dependencies": { - "@modelcontextprotocol/sdk": "^1.17.0", "tsx": "^4.21.0", "yaml": "^2.5.1", "zod": "^3.23.0", @@ -69,204 +68,24 @@ "@esbuild/win32-x64": ["@esbuild/win32-x64@0.27.7", "", { "os": "win32", "cpu": "x64" }, "sha512-56hiAJPhwQ1R4i+21FVF7V8kSD5zZTdHcVuRFMW0hn753vVfQN8xlx4uOPT4xoGH0Z/oVATuR82AiqSTDIpaHg=="], - "@hono/node-server": ["@hono/node-server@1.19.11", "", { "peerDependencies": { "hono": "^4" } }, "sha512-dr8/3zEaB+p0D2n/IUrlPF1HZm586qgJNXK1a9fhg/PzdtkK7Ksd5l312tJX2yBuALqDYBlG20QEbayqPyxn+g=="], - - "@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.27.1", "", { "dependencies": { "@hono/node-server": "^1.19.9", "ajv": "^8.17.1", "ajv-formats": "^3.0.1", "content-type": "^1.0.5", "cors": "^2.8.5", "cross-spawn": "^7.0.5", "eventsource": "^3.0.2", "eventsource-parser": "^3.0.0", "express": "^5.2.1", "express-rate-limit": "^8.2.1", "hono": "^4.11.4", "jose": "^6.1.3", "json-schema-typed": "^8.0.2", "pkce-challenge": "^5.0.0", "raw-body": "^3.0.0", "zod": "^3.25 || ^4.0", "zod-to-json-schema": "^3.25.1" }, "peerDependencies": { "@cfworker/json-schema": "^4.1.1", "zod": "^3.25 || ^4.0" }, "optionalPeers": ["@cfworker/json-schema"] }, "sha512-sr6GbP+4edBwFndLbM60gf07z0FQ79gaExpnsjMGePXqFcSSb7t6iscpjk9DhFhwd+mTEQrzNafGP8/iGGFYaA=="], - "@types/node": ["@types/node@20.19.37", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-8kzdPJ3FsNsVIurqBs7oodNnCEVbni9yUEkaHbgptDACOPW04jimGagZ51E6+lXUwJjgnBw+hyko/lkFWCldqw=="], - "accepts": ["accepts@2.0.0", "", { "dependencies": { "mime-types": "^3.0.0", "negotiator": "^1.0.0" } }, "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng=="], - - "ajv": ["ajv@8.18.0", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A=="], - - "ajv-formats": ["ajv-formats@3.0.1", "", { "dependencies": { "ajv": "^8.0.0" }, "peerDependencies": { "ajv": "^8.0.0" } }, "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ=="], - - "body-parser": ["body-parser@2.2.2", "", { "dependencies": { "bytes": "^3.1.2", "content-type": "^1.0.5", "debug": "^4.4.3", "http-errors": "^2.0.0", "iconv-lite": "^0.7.0", "on-finished": "^2.4.1", "qs": "^6.14.1", "raw-body": "^3.0.1", "type-is": "^2.0.1" } }, "sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA=="], - - "bytes": ["bytes@3.1.2", "", {}, "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg=="], - - "call-bind-apply-helpers": ["call-bind-apply-helpers@1.0.2", "", { "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" } }, "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ=="], - - "call-bound": ["call-bound@1.0.4", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "get-intrinsic": "^1.3.0" } }, "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg=="], - - "content-disposition": ["content-disposition@1.0.1", "", {}, "sha512-oIXISMynqSqm241k6kcQ5UwttDILMK4BiurCfGEREw6+X9jkkpEe5T9FZaApyLGGOnFuyMWZpdolTXMtvEJ08Q=="], - - "content-type": ["content-type@1.0.5", "", {}, "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA=="], - - "cookie": ["cookie@0.7.2", "", {}, "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w=="], - - "cookie-signature": ["cookie-signature@1.2.2", "", {}, "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg=="], - - "cors": ["cors@2.8.6", "", { "dependencies": { "object-assign": "^4", "vary": "^1" } }, "sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw=="], - - "cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="], - - "debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="], - - "depd": ["depd@2.0.0", "", {}, "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw=="], - - "dunder-proto": ["dunder-proto@1.0.1", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" } }, "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A=="], - - "ee-first": ["ee-first@1.1.1", "", {}, "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="], - - "encodeurl": ["encodeurl@2.0.0", "", {}, "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg=="], - - "es-define-property": ["es-define-property@1.0.1", "", {}, "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g=="], - - "es-errors": ["es-errors@1.3.0", "", {}, "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw=="], - - "es-object-atoms": ["es-object-atoms@1.1.1", "", { "dependencies": { "es-errors": "^1.3.0" } }, "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA=="], - "esbuild": ["esbuild@0.27.7", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.27.7", "@esbuild/android-arm": "0.27.7", "@esbuild/android-arm64": "0.27.7", "@esbuild/android-x64": "0.27.7", "@esbuild/darwin-arm64": "0.27.7", "@esbuild/darwin-x64": "0.27.7", "@esbuild/freebsd-arm64": "0.27.7", "@esbuild/freebsd-x64": "0.27.7", "@esbuild/linux-arm": "0.27.7", "@esbuild/linux-arm64": "0.27.7", "@esbuild/linux-ia32": "0.27.7", "@esbuild/linux-loong64": "0.27.7", "@esbuild/linux-mips64el": "0.27.7", "@esbuild/linux-ppc64": "0.27.7", "@esbuild/linux-riscv64": "0.27.7", "@esbuild/linux-s390x": "0.27.7", "@esbuild/linux-x64": "0.27.7", "@esbuild/netbsd-arm64": "0.27.7", "@esbuild/netbsd-x64": "0.27.7", "@esbuild/openbsd-arm64": "0.27.7", "@esbuild/openbsd-x64": "0.27.7", "@esbuild/openharmony-arm64": "0.27.7", "@esbuild/sunos-x64": "0.27.7", "@esbuild/win32-arm64": "0.27.7", "@esbuild/win32-ia32": "0.27.7", "@esbuild/win32-x64": "0.27.7" }, "bin": "bin/esbuild" }, "sha512-IxpibTjyVnmrIQo5aqNpCgoACA/dTKLTlhMHihVHhdkxKyPO1uBBthumT0rdHmcsk9uMonIWS0m4FljWzILh3w=="], - "escape-html": ["escape-html@1.0.3", "", {}, "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow=="], - - "etag": ["etag@1.8.1", "", {}, "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg=="], - - "eventsource": ["eventsource@3.0.7", "", { "dependencies": { "eventsource-parser": "^3.0.1" } }, "sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA=="], - - "eventsource-parser": ["eventsource-parser@3.0.6", "", {}, "sha512-Vo1ab+QXPzZ4tCa8SwIHJFaSzy4R6SHf7BY79rFBDf0idraZWAkYrDjDj8uWaSm3S2TK+hJ7/t1CEmZ7jXw+pg=="], - - "express": ["express@5.2.1", "", { "dependencies": { "accepts": "^2.0.0", "body-parser": "^2.2.1", "content-disposition": "^1.0.0", "content-type": "^1.0.5", "cookie": "^0.7.1", "cookie-signature": "^1.2.1", "debug": "^4.4.0", "depd": "^2.0.0", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "etag": "^1.8.1", "finalhandler": "^2.1.0", "fresh": "^2.0.0", "http-errors": "^2.0.0", "merge-descriptors": "^2.0.0", "mime-types": "^3.0.0", "on-finished": "^2.4.1", "once": "^1.4.0", "parseurl": "^1.3.3", "proxy-addr": "^2.0.7", "qs": "^6.14.0", "range-parser": "^1.2.1", "router": "^2.2.0", "send": "^1.1.0", "serve-static": "^2.2.0", "statuses": "^2.0.1", "type-is": "^2.0.1", "vary": "^1.1.2" } }, "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw=="], - - "express-rate-limit": ["express-rate-limit@8.3.1", "", { "dependencies": { "ip-address": "10.1.0" }, "peerDependencies": { "express": ">= 4.11" } }, "sha512-D1dKN+cmyPWuvB+G2SREQDzPY1agpBIcTa9sJxOPMCNeH3gwzhqJRDWCXW3gg0y//+LQ/8j52JbMROWyrKdMdw=="], - - "fast-deep-equal": ["fast-deep-equal@3.1.3", "", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="], - - "fast-uri": ["fast-uri@3.1.0", "", {}, "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA=="], - - "finalhandler": ["finalhandler@2.1.1", "", { "dependencies": { "debug": "^4.4.0", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "on-finished": "^2.4.1", "parseurl": "^1.3.3", "statuses": "^2.0.1" } }, "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA=="], - - "forwarded": ["forwarded@0.2.0", "", {}, "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow=="], - - "fresh": ["fresh@2.0.0", "", {}, "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A=="], - "fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="], - "function-bind": ["function-bind@1.1.2", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="], - - "get-intrinsic": ["get-intrinsic@1.3.0", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", "math-intrinsics": "^1.1.0" } }, "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ=="], - - "get-proto": ["get-proto@1.0.1", "", { "dependencies": { "dunder-proto": "^1.0.1", "es-object-atoms": "^1.0.0" } }, "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g=="], - "get-tsconfig": ["get-tsconfig@4.13.7", "", { "dependencies": { "resolve-pkg-maps": "^1.0.0" } }, "sha512-7tN6rFgBlMgpBML5j8typ92BKFi2sFQvIdpAqLA2beia5avZDrMs0FLZiM5etShWq5irVyGcGMEA1jcDaK7A/Q=="], - "gopd": ["gopd@1.2.0", "", {}, "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg=="], - - "has-symbols": ["has-symbols@1.1.0", "", {}, "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ=="], - - "hasown": ["hasown@2.0.2", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ=="], - - "hono": ["hono@4.12.8", "", {}, "sha512-VJCEvtrezO1IAR+kqEYnxUOoStaQPGrCmX3j4wDTNOcD1uRPFpGlwQUIW8niPuvHXaTUxeOUl5MMDGrl+tmO9A=="], - - "http-errors": ["http-errors@2.0.1", "", { "dependencies": { "depd": "~2.0.0", "inherits": "~2.0.4", "setprototypeof": "~1.2.0", "statuses": "~2.0.2", "toidentifier": "~1.0.1" } }, "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ=="], - - "iconv-lite": ["iconv-lite@0.7.2", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw=="], - - "inherits": ["inherits@2.0.4", "", {}, "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="], - - "ip-address": ["ip-address@10.1.0", "", {}, "sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q=="], - - "ipaddr.js": ["ipaddr.js@1.9.1", "", {}, "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g=="], - - "is-promise": ["is-promise@4.0.0", "", {}, "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ=="], - - "isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="], - - "jose": ["jose@6.2.2", "", {}, "sha512-d7kPDd34KO/YnzaDOlikGpOurfF0ByC2sEV4cANCtdqLlTfBlw2p14O/5d/zv40gJPbIQxfES3nSx1/oYNyuZQ=="], - - "json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="], - - "json-schema-typed": ["json-schema-typed@8.0.2", "", {}, "sha512-fQhoXdcvc3V28x7C7BMs4P5+kNlgUURe2jmUT1T//oBRMDrqy1QPelJimwZGo7Hg9VPV3EQV5Bnq4hbFy2vetA=="], - - "math-intrinsics": ["math-intrinsics@1.1.0", "", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="], - - "media-typer": ["media-typer@1.1.0", "", {}, "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw=="], - - "merge-descriptors": ["merge-descriptors@2.0.0", "", {}, "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g=="], - - "mime-db": ["mime-db@1.54.0", "", {}, "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ=="], - - "mime-types": ["mime-types@3.0.2", "", { "dependencies": { "mime-db": "^1.54.0" } }, "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A=="], - - "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], - - "negotiator": ["negotiator@1.0.0", "", {}, "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg=="], - - "object-assign": ["object-assign@4.1.1", "", {}, "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="], - - "object-inspect": ["object-inspect@1.13.4", "", {}, "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew=="], - - "on-finished": ["on-finished@2.4.1", "", { "dependencies": { "ee-first": "1.1.1" } }, "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg=="], - - "once": ["once@1.4.0", "", { "dependencies": { "wrappy": "1" } }, "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w=="], - - "parseurl": ["parseurl@1.3.3", "", {}, "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ=="], - - "path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="], - - "path-to-regexp": ["path-to-regexp@8.3.0", "", {}, "sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA=="], - - "pkce-challenge": ["pkce-challenge@5.0.1", "", {}, "sha512-wQ0b/W4Fr01qtpHlqSqspcj3EhBvimsdh0KlHhH8HRZnMsEa0ea2fTULOXOS9ccQr3om+GcGRk4e+isrZWV8qQ=="], - - "proxy-addr": ["proxy-addr@2.0.7", "", { "dependencies": { "forwarded": "0.2.0", "ipaddr.js": "1.9.1" } }, "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg=="], - - "qs": ["qs@6.15.0", "", { "dependencies": { "side-channel": "^1.1.0" } }, "sha512-mAZTtNCeetKMH+pSjrb76NAM8V9a05I9aBZOHztWy/UqcJdQYNsf59vrRKWnojAT9Y+GbIvoTBC++CPHqpDBhQ=="], - - "range-parser": ["range-parser@1.2.1", "", {}, "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg=="], - - "raw-body": ["raw-body@3.0.2", "", { "dependencies": { "bytes": "~3.1.2", "http-errors": "~2.0.1", "iconv-lite": "~0.7.0", "unpipe": "~1.0.0" } }, "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA=="], - - "require-from-string": ["require-from-string@2.0.2", "", {}, "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw=="], - "resolve-pkg-maps": ["resolve-pkg-maps@1.0.0", "", {}, "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw=="], - "router": ["router@2.2.0", "", { "dependencies": { "debug": "^4.4.0", "depd": "^2.0.0", "is-promise": "^4.0.0", "parseurl": "^1.3.3", "path-to-regexp": "^8.0.0" } }, "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ=="], - - "safer-buffer": ["safer-buffer@2.1.2", "", {}, "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="], - - "send": ["send@1.2.1", "", { "dependencies": { "debug": "^4.4.3", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "etag": "^1.8.1", "fresh": "^2.0.0", "http-errors": "^2.0.1", "mime-types": "^3.0.2", "ms": "^2.1.3", "on-finished": "^2.4.1", "range-parser": "^1.2.1", "statuses": "^2.0.2" } }, "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ=="], - - "serve-static": ["serve-static@2.2.1", "", { "dependencies": { "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "parseurl": "^1.3.3", "send": "^1.2.0" } }, "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw=="], - - "setprototypeof": ["setprototypeof@1.2.0", "", {}, "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="], - - "shebang-command": ["shebang-command@2.0.0", "", { "dependencies": { "shebang-regex": "^3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="], - - "shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="], - - "side-channel": ["side-channel@1.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3", "side-channel-list": "^1.0.0", "side-channel-map": "^1.0.1", "side-channel-weakmap": "^1.0.2" } }, "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw=="], - - "side-channel-list": ["side-channel-list@1.0.0", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3" } }, "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA=="], - - "side-channel-map": ["side-channel-map@1.0.1", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3" } }, "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA=="], - - "side-channel-weakmap": ["side-channel-weakmap@1.0.2", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3", "side-channel-map": "^1.0.1" } }, "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A=="], - - "statuses": ["statuses@2.0.2", "", {}, "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw=="], - - "toidentifier": ["toidentifier@1.0.1", "", {}, "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA=="], - "tsx": ["tsx@4.21.0", "", { "dependencies": { "esbuild": "~0.27.0", "get-tsconfig": "^4.7.5" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "bin": "dist/cli.mjs" }, "sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw=="], - "type-is": ["type-is@2.0.1", "", { "dependencies": { "content-type": "^1.0.5", "media-typer": "^1.1.0", "mime-types": "^3.0.0" } }, "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw=="], - "typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="], "undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], - "unpipe": ["unpipe@1.0.0", "", {}, "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ=="], - - "vary": ["vary@1.1.2", "", {}, "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg=="], - - "which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="], - - "wrappy": ["wrappy@1.0.2", "", {}, "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="], - "yaml": ["yaml@2.8.3", "", { "bin": { "yaml": "bin.mjs" } }, "sha512-AvbaCLOO2Otw/lW5bmh9d/WEdcDFdQp2Z2ZUH3pX9U2ihyUY0nvLv7J6TrWowklRGPYbB/IuIMfYgxaCPg5Bpg=="], "zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="], - - "zod-to-json-schema": ["zod-to-json-schema@3.25.1", "", { "peerDependencies": { "zod": "^3.25 || ^4" } }, "sha512-pM/SU9d3YAggzi6MtR4h7ruuQlqKtad8e9S0fmxcMi+ueAK5Korys/aWcV9LIIHTVbj01NdzxcnXSN+O74ZIVA=="], } } diff --git a/generated/tool-index/local-tool-registry.json b/generated/tool-index/local-tool-registry.json index c5bf7c9..88e7a76 100644 --- a/generated/tool-index/local-tool-registry.json +++ b/generated/tool-index/local-tool-registry.json @@ -37,7 +37,6 @@ "golang_list_patterns": "src/plugins/golang/tools/list-patterns.js", "golang_list_practices": "src/plugins/golang/tools/list-practices.js", "golang_search_docs": "src/plugins/golang/tools/search-docs.js", - "hyperstack_setup": "src/plugins/hyperstack/tools/setup.js", "lenis_generate_setup": "src/plugins/lenis/tools/generate-setup.js", "lenis_get_api": "src/plugins/lenis/tools/get-api.js", "lenis_get_pattern": "src/plugins/lenis/tools/get-pattern.js", diff --git a/generated/tool-index/local-tool-registry.ts b/generated/tool-index/local-tool-registry.ts index d961c04..7258201 100644 --- a/generated/tool-index/local-tool-registry.ts +++ b/generated/tool-index/local-tool-registry.ts @@ -37,7 +37,6 @@ export const LOCAL_TOOL_REGISTRY = { "golang_list_patterns": () => import("src/plugins/golang/tools/list-patterns.js"), "golang_list_practices": () => import("src/plugins/golang/tools/list-practices.js"), "golang_search_docs": () => import("src/plugins/golang/tools/search-docs.js"), - "hyperstack_setup": () => import("src/plugins/hyperstack/tools/setup.js"), "lenis_generate_setup": () => import("src/plugins/lenis/tools/generate-setup.js"), "lenis_get_api": () => import("src/plugins/lenis/tools/get-api.js"), "lenis_get_pattern": () => import("src/plugins/lenis/tools/get-pattern.js"), diff --git a/package.json b/package.json index 20539da..767047a 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@orkait-ai/hyperstack", - "version": "1.1.3", - "description": "Disciplined MCP server + skill system. 12 plugins, 80 tools, 21 skills with adversarial enforcement. Designer/DESIGN.md pipeline, shadcn/ui, React Flow, Motion, Lenis, React 19, Echo, Go, Rust, design tokens, UI/UX.", + "version": "2.0.0", + "description": "Topology-driven CLI for 11 plugin tool families covering designer, design tokens, UI/UX, React, shadcn, Motion, Lenis, React Flow, Echo, Go, and Rust.", "bin": { "hyperstack": "bin/hyperstack.mjs" }, @@ -11,12 +11,7 @@ "compile:context": "tsx src/internal/compile-runtime-context.ts", "generate:local-tools": "tsx scripts/generate-local-tool-registry.ts", "generate:topology": "tsx scripts/generate-topology-artifacts.ts", - "start": "bun src/index.ts", - "dev": "bun --watch src/index.ts", - "docker:run": "bun scripts/ensure-singleton.ts", - "skills:index": "tsx scripts/generate-skills-index.ts", - "mcp:start": "bun scripts/start-mcp.ts", - "setup": "tsx scripts/setup.ts" + "skills:index": "tsx scripts/generate-skills-index.ts" }, "author": "Orkait", "license": "MIT", @@ -24,7 +19,6 @@ "node": ">=18" }, "dependencies": { - "@modelcontextprotocol/sdk": "^1.17.0", "tsx": "^4.21.0", "yaml": "^2.5.1", "zod": "^3.23.0" diff --git a/scripts/ensure-singleton.ts b/scripts/ensure-singleton.ts deleted file mode 100644 index 635f519..0000000 --- a/scripts/ensure-singleton.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { execSync } from "child_process"; - -const IMAGE = "ghcr.io/orkait/hyperstack:main"; -const CONTAINER_NAME = "hyperstack-mcp"; - -function run() { - console.log("Checking for existing Hyperstack containers (cross-platform)..."); - - try { - // 1. Find containers by image and name - const byImage = execSync(`docker ps -aq --filter "ancestor=${IMAGE}"`).toString().trim().split(/\s+/).filter(Boolean); - const byName = execSync(`docker ps -aq --filter "name=${CONTAINER_NAME}"`).toString().trim().split(/\s+/).filter(Boolean); - - // 2. Combine and uniq - const allStale = Array.from(new Set([...byImage, ...byName])); - - if (allStale.length > 0) { - console.log(`Removing stale Hyperstack containers: ${allStale.join(", ")}`); - // We use a loop or join with space since 'docker rm -f' accepts multiple IDs - execSync(`docker rm -f ${allStale.join(" ")}`, { stdio: "inherit" }); - } else { - console.log("No stale containers found."); - } - - // 3. Start fresh container - console.log(`Starting fresh Hyperstack container: ${CONTAINER_NAME}`); - execSync( - `docker run -d --name ${CONTAINER_NAME} --restart unless-stopped \ - --memory=512m --cpus=1 \ - --entrypoint sleep \ - ${IMAGE} infinity`, - { stdio: "inherit" } - ); - - console.log("\nVerification:"); - execSync(`docker ps --filter name=${CONTAINER_NAME}`, { stdio: "inherit" }); - - } catch (error: any) { - console.error("Error ensuring singleton container:", error.message); - process.exit(1); - } -} - -run(); diff --git a/scripts/setup.ts b/scripts/setup.ts deleted file mode 100644 index 1edd852..0000000 --- a/scripts/setup.ts +++ /dev/null @@ -1,54 +0,0 @@ -import * as setup from "../src/internal/setup-hyperstack.js"; -import * as fs from "node:fs"; -import * as path from "node:path"; - -async function main() { - console.log("\n🚀 Hyperstack Autonomous Setup (CLI)"); - console.log("=====================================\n"); - - const hintedPlatform = setup.detectEnvironment(); - console.log(`📡 Hinted platform: ${hintedPlatform}`); - - const configPath = setup.findConfigFile(hintedPlatform); - - if (!configPath) { - console.warn("⚠️ Could not find an MCP configuration file in any known location."); - console.log("Tried: .claude.json, .cursor/mcp.json, .codeium/windsurf/mcp_config.json, .roo/mcp.json, .gemini/settings.json, .kiro/settings/mcp.json, .qwen/settings.json"); - console.log("\n💡 OpenAI Codex CLI? Run: codex mcp add hyperstack -- bun ~/.hyperstack/bin/hyperstack.mjs"); - console.log(" For any unknown IDE, use the Agentic Autopilot instead."); - process.exit(1); - } - - // Resolve the actual platform from the found config path - const platform = setup.detectPlatformFromConfigPath(configPath); - console.log(`✅ Found config: ${configPath} (${platform})`); - - const skillPath = setup.findSkillPath(platform); - if (skillPath) { - const hyperstackSkills = path.join(process.cwd(), "skills"); - const skillTarget = path.join(skillPath, "hyperstack"); - console.log(`\n📚 Skill target: ${skillTarget}`); - console.log(`Run this to activate adversarial gates:`); - console.log(` ln -s "${hyperstackSkills}" "${skillTarget}"`); - } - - const pluginRoot = process.cwd(); - - const patch = setup.generateMcpPatch(configPath, pluginRoot, platform, "local"); - - // Proactively apply the patch - setup.applyMcpPatch(configPath, patch); - - console.log("\n📋 Configuration Summary:"); - console.log("---------------------------------"); - console.log(`✅ Environment: ${platform}`); - console.log(`✅ Config Path: ${configPath}`); - if (skillPath) { - console.log(`✅ Skill Target: ${path.join(skillPath, "hyperstack")}`); - } - console.log("---------------------------------\n"); - - console.log("🚀 Setup Complete! You must restart your AI client to pick up the new tools."); -} - -main().catch(console.error); diff --git a/scripts/start-mcp.ts b/scripts/start-mcp.ts deleted file mode 100644 index ffb7de1..0000000 --- a/scripts/start-mcp.ts +++ /dev/null @@ -1,74 +0,0 @@ -import { execSync, spawn } from "child_process"; -import * as fs from "fs"; -import * as path from "path"; -import * as os from "os"; - -const IMAGE = "ghcr.io/orkait/hyperstack:main"; -const CONTAINER_NAME = "hyperstack-mcp"; -const LOCK_FILE = path.join(os.tmpdir(), "hyperstack-mcp-startup.lock"); - -/** - * Standardized Cross-Platform MCP Startup Logic - * - Ensures the persistent container is running. - * - Bridges the local I/O to the containerized MCP process via 'docker exec'. - */ -async function startMcp() { - let lockFd: number | null = null; - - try { - // 1. Concurrency control via file lock - try { - lockFd = fs.openSync(LOCK_FILE, 'wx'); - - // Check if the container is already running - const running = execSync(`docker ps -q --filter "name=^${CONTAINER_NAME}$"`, { stdio: "pipe" }).toString().trim(); - - if (!running) { - // If not running, ensure it's removed and start fresh - // Suppress errors if container doesn't exist - try { execSync(`docker rm -f ${CONTAINER_NAME}`, { stdio: "ignore" }); } catch {} - - execSync( - `docker run -d --name ${CONTAINER_NAME} --restart unless-stopped \ - --memory=512m --cpus=1 \ - --entrypoint sleep \ - ${IMAGE} infinity`, - { stdio: "inherit" } - ); - - // Brief pause to allow Docker to initialize the process - await new Promise(r => setTimeout(r, 300)); - } - } catch (e: any) { - if (e.code === 'EEXIST') { - // Another process is handling startup, wait briefly - await new Promise(r => setTimeout(r, 500)); - } else { - throw e; - } - } finally { - if (lockFd !== null) { - fs.closeSync(lockFd); - try { fs.unlinkSync(LOCK_FILE); } catch {} - } - } - - // 2. Connect to the MCP server inside the container - // This allows the local agent/IDE to communicate with the containerized Bun process. - const proc = spawn("docker", ["exec", "-i", CONTAINER_NAME, "bun", "/app/src/index.ts"], { - stdio: "inherit" - }); - - proc.on("exit", (code) => process.exit(code || 0)); - proc.on("error", (err) => { - console.error("Failed to bridge to containerized MCP:", err.message); - process.exit(1); - }); - - } catch (error: any) { - console.error("Critical error starting Hyperstack MCP:", error.message); - process.exit(1); - } -} - -startMcp(); diff --git a/src/index.ts b/src/index.ts deleted file mode 100755 index 8082695..0000000 --- a/src/index.ts +++ /dev/null @@ -1,63 +0,0 @@ -#!/usr/bin/env node - -import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; -import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; -import { loadPlugins } from "./registry.js"; -import { reactflowPlugin } from "./plugins/reactflow/index.js"; -import { motionPlugin } from "./plugins/motion/index.js"; -import { lenisPlugin } from "./plugins/lenis/index.js"; -import { reactPlugin } from "./plugins/react/index.js"; -import { echoPlugin } from "./plugins/echo/index.js"; -import { golangPlugin } from "./plugins/golang/index.js"; -import { rustPlugin } from "./plugins/rust/index.js"; -import { designTokensPlugin } from "./plugins/design-tokens/index.js"; -import { uiUxPlugin } from "./plugins/ui-ux/index.js"; -import { designerPlugin } from "./plugins/designer/index.js"; -import { shadcnPlugin } from "./plugins/shadcn/index.js"; -import { hyperstackPlugin } from "./plugins/hyperstack/index.js"; - -import { readFileSync } from "node:fs"; -import { dirname, join } from "node:path"; -import { fileURLToPath } from "node:url"; - -const __dirname = dirname(fileURLToPath(import.meta.url)); -const pkgPath = join(__dirname, "..", "package.json"); -const pkg = JSON.parse(readFileSync(pkgPath, "utf-8")); - -const server = new McpServer({ - name: "hyperstack", - version: pkg.version, -}); - -export const allPlugins = [ - reactflowPlugin, - motionPlugin, - lenisPlugin, - reactPlugin, - echoPlugin, - golangPlugin, - rustPlugin, - designTokensPlugin, - uiUxPlugin, - designerPlugin, - shadcnPlugin, - hyperstackPlugin, -]; - -loadPlugins(server, allPlugins); - -async function main() { - const transport = new StdioServerTransport(); - await server.connect(transport); - - const shutdown = () => process.exit(0); - process.stdin.on("close", shutdown); - process.stdin.on("end", shutdown); - process.on("SIGTERM", shutdown); - process.on("SIGINT", shutdown); -} - -main().catch((err) => { - console.error("Failed to start MCP server:", err); - process.exit(1); -}); diff --git a/src/internal/setup-hyperstack.ts b/src/internal/setup-hyperstack.ts deleted file mode 100644 index 8a28290..0000000 --- a/src/internal/setup-hyperstack.ts +++ /dev/null @@ -1,338 +0,0 @@ -import * as fs from "node:fs"; -import * as path from "node:path"; -import * as os from "node:os"; -import { execSync } from "node:child_process"; - -export interface SetupResult { - detectedPlatform: string; - configPath: string | null; - skillPath: string | null; - status: "success" | "pending_research" | "error"; - proposedPatch?: any; - message: string; -} - -export type PlatformFormat = "json-mcpServers" | "json-contextServers" | "toml-mcp_servers" | "json-mcpServers-nested"; - -const KNOWN_PLATFORMS: Record = { - // ─── AI CLIs ──────────────────────────────────────────────── - - // Claude Code (CLI) - global user config - // Source: docs.anthropic.com, inventivehq.com (April 2025, multiple) - "claude-code": { - env: ["CLAUDE_PLUGIN_ROOT"], - configFiles: [".claude.json"], - skillPath: ".claude/skills", - format: "json-mcpServers", - }, - // Gemini CLI - global user config - // Source: geminicli.com, augmentcode.com (April 2025, multiple) - "gemini-cli": { - configFiles: [".gemini/settings.json"], - format: "json-mcpServers", - notes: "Run '/mcp' inside Gemini CLI to verify connection", - }, - // Antigravity (Google DeepMind) - local user config - "antigravity": { - configFiles: [".gemini/antigravity/mcp_config.json"], - skillPath: ".gemini/antigravity/skills", - format: "json-mcpServers", - }, - // Qwen Code (Alibaba) - global user config - // Source: github.io/qwen-code official docs (April 2025) - "qwen-code": { - configFiles: [".qwen/settings.json"], - skillPath: ".qwen/skills", - format: "json-mcpServers", - notes: "CLI alternative: qwen mcp add ", - }, - // OpenAI Codex CLI - global user config (TOML format) - // Source: openai.com official docs (April 2025) - "codex": { - configFiles: [".codex/config.toml"], - format: "toml-mcp_servers", - notes: "CLI alternative: codex mcp add hyperstack -- bun ~/.hyperstack/bin/hyperstack.mjs", - }, - - // ─── AI IDEs ──────────────────────────────────────────────── - - // Cursor IDE - global user config - // Source: cursor.com docs, confirmed via 10+ tutorials (April 2025) - "cursor": { - env: ["CURSOR_PLUGIN_ROOT"], - configFiles: [".cursor/mcp.json"], - skillPath: ".cursor/rules", - format: "json-mcpServers", - notes: "Project-level: .cursor/mcp.json in project root", - }, - // Windsurf IDE (Codeium) - global user config - // Source: windsurf.com official docs, bito.ai, zapier (April 2025) - "windsurf": { - configFiles: [".codeium/windsurf/mcp_config.json"], - format: "json-mcpServers", - notes: "Click 'Refresh' in Cascade panel after editing", - }, - // Kiro (Amazon AI IDE) - global user config - // Source: kiro.dev official docs, aws.com (April 2025) - "kiro": { - configFiles: [".kiro/settings/mcp.json"], - format: "json-mcpServers", - notes: "Workspace-level: .kiro/settings/mcp.json in project root", - }, - // Zed editor - global user config (uses 'context_servers' key, not 'mcpServers') - // Source: zed.dev official docs, skeet.build (April 2025) - "zed": { - configFiles: [".config/zed/settings.json"], - format: "json-contextServers", - notes: "Uses 'context_servers' key instead of 'mcpServers'", - }, - - // ─── VS Code Extensions ───────────────────────────────────── - - // VS Code + GitHub Copilot - platform-specific global config - // Source: code.visualstudio.com official docs (April 2025) - "vscode": { - env: ["VSCODE_PID"], - configFiles: [ - ".config/Code/User/mcp.json", // Linux - "Library/Application Support/Code/User/mcp.json", // macOS - ], - format: "json-mcpServers", - notes: "Project-level: .vscode/mcp.json in project root", - }, - // Roo Code (VS Code extension) - // Global config has no fixed home path (stored in VS Code extension storage). - // Source: roocode.com official docs (April 2025) - "roo-code": { - configFiles: [".roo/mcp.json"], - skillPath: ".roo/rules", - format: "json-mcpServers", - notes: "Global config: open via Roo Code UI > Edit Global MCP", - }, - // Cline (VS Code extension) - Linux global path - // Source: cline.bot official docs, reddit (April 2025) - "cline": { - configFiles: [ - ".config/Code/User/globalStorage/saoudrizwan.claude-dev/settings/cline_mcp_settings.json", // Linux - ".cline/data/settings/cline_mcp_settings.json", // Cline CLI fallback - ], - format: "json-mcpServers", - notes: "Access via Cline panel > MCP Servers > Configure", - }, - // Continue.dev (VS Code extension) - project-level only - // Source: continue.dev official docs (April 2025) - "continue-dev": { - configFiles: [".continue/mcpServers/mcp.json"], - format: "json-mcpServers", - notes: "Requires Agent Mode. Place config in .continue/mcpServers/ directory", - }, -}; - - -export function detectEnvironment(): string { - // Env-var hints (fast path, but unreliable in nested terminals) - if (process.env.ANTIGRAVITY_AGENT) return "antigravity"; - if (process.env.CLAUDE_PLUGIN_ROOT) return "claude-code"; - if (process.env.CURSOR_PLUGIN_ROOT) return "cursor"; - // VSCODE_PID fires inside any VS Code-hosted terminal (Gemini, Roo Code, etc.) - // Do NOT short-circuit here - fall through to config-file probing below. - return "unknown"; -} - -export function findConfigFile(platform: string): string | null { - const home = os.homedir(); - - // Always probe all known config files - this is the authoritative source of truth. - // Even if `platform` was resolved from env vars, the config file must exist. - const candidates: Array<{ platform: string; file: string }> = []; - - if (platform !== "unknown" && KNOWN_PLATFORMS[platform]) { - // Try hinted platform first - for (const file of KNOWN_PLATFORMS[platform].configFiles) { - candidates.push({ platform, file }); - } - } - - // Always append all other platforms as fallback probes - for (const [p, info] of Object.entries(KNOWN_PLATFORMS)) { - if (p !== platform) { - for (const file of info.configFiles) { - candidates.push({ platform: p, file }); - } - } - } - - for (const { file } of candidates) { - const fullPath = path.join(home, file); - if (fs.existsSync(fullPath)) return fullPath; - } - - return null; -} - -export function detectPlatformFromConfigPath(configPath: string): string { - for (const [p, info] of Object.entries(KNOWN_PLATFORMS)) { - for (const file of info.configFiles) { - if (configPath.endsWith(file)) return p; - } - } - return "unknown"; -} - -export function findSkillPath(platform: string): string | null { - const info = KNOWN_PLATFORMS[platform]; - if (!info || !info.skillPath) return null; - - return path.join(os.homedir(), info.skillPath); -} - - -export function getPlatformFormat(platform: string): PlatformFormat { - return KNOWN_PLATFORMS[platform]?.format ?? "json-mcpServers"; -} - -export function generateMcpPatch( - configPath: string, - pluginRoot: string, - platform: string, - method: "docker" | "local" = "local" -): { format: PlatformFormat; content: string | object } { - const binaryPath = path.join(pluginRoot, "bin", "hyperstack.mjs"); - const localServerConfig = { - command: "node", - args: [binaryPath], - env: { HYPERSTACK_ROOT: pluginRoot }, - }; - const dockerServerConfig = { - command: "docker", - args: ["exec", "-i", "hyperstack-mcp", "bun", "/app/src/index.ts"], - env: { HYPERSTACK_ROOT: pluginRoot }, - }; - const serverConfig = method === "docker" ? dockerServerConfig : localServerConfig; - const format = getPlatformFormat(platform); - - // Zed uses 'context_servers' key instead of 'mcpServers' - // Source: zed.dev official docs (April 2025) - if (format === "json-contextServers") { - return { - format, - content: { - context_servers: { - hyperstack: serverConfig, - }, - }, - }; - } - - // Codex CLI uses TOML format: [mcp_servers.] - // Source: openai.com official docs (April 2025) - if (format === "toml-mcp_servers") { - const tomlBlock = method === "docker" - ? `[mcp_servers.hyperstack]\ncommand = "docker"\nargs = ["exec", "-i", "hyperstack-mcp", "bun", "/app/src/index.ts"]\n[mcp_servers.hyperstack.env]\nHYPERSTACK_ROOT = "${pluginRoot}"` - : `[mcp_servers.hyperstack]\ncommand = "node"\nargs = ["${binaryPath}"]\n[mcp_servers.hyperstack.env]\nHYPERSTACK_ROOT = "${pluginRoot}"`; - return { format, content: tomlBlock }; - } - - // Default: standard mcpServers JSON schema - // Covers: Claude Code, Cursor, Windsurf, Roo Code, VS Code, Kiro, Qwen, Gemini, Cline, Continue.dev - return { - format, - content: { - mcpServers: { - hyperstack: serverConfig, - }, - }, - }; -} - -/** - * Persistently applies the generated patch to the user's config file - * without destroying existing settings. - */ -export function applyMcpPatch(configPath: string, patch: { format: PlatformFormat; content: any }) { - if (!fs.existsSync(configPath)) { - // If it doesn't exist, create it with the patch - if (typeof patch.content === "string") { - fs.writeFileSync(configPath, patch.content); - } else { - fs.writeFileSync(configPath, JSON.stringify(patch.content, null, 2)); - } - return; - } - - if (patch.format === "toml-mcp_servers") { - let content = fs.readFileSync(configPath, "utf8"); - if (!content.includes("[mcp_servers.hyperstack]")) { - fs.appendFileSync(configPath, "\n" + patch.content); - console.log(`✅ Appended Hyperstack block to ${configPath}`); - } else { - console.log(`ℹ️ Hyperstack block already exists in ${configPath}`); - } - return; - } - - // JSON deep-merge - try { - const existing = JSON.parse(fs.readFileSync(configPath, "utf8")); - const patchObj = patch.content as any; - - if (patch.format === "json-contextServers") { - existing.context_servers = { - ...(existing.context_servers || {}), - ...patchObj.context_servers, - }; - } else { - existing.mcpServers = { - ...(existing.mcpServers || {}), - ...patchObj.mcpServers, - }; - } - - fs.writeFileSync(configPath, JSON.stringify(existing, null, 2)); - console.log(`✅ Deep-merged Hyperstack config into ${configPath}`); - } catch (err) { - console.error(`❌ Failed to merge config: ${err}`); - } -} - -export function selfHealDocker() { - try { - // Check if Docker is available - execSync("docker --version", { stdio: "ignore" }); - - console.log("\n🛡️ Running Docker Self-Healing Protocol..."); - - // Check for existing containers - try { - // Find containers with our image or the exact name - const cmd = 'docker ps -aq --filter "ancestor=ghcr.io/orkait/hyperstack:main"'; - const existing = execSync(cmd).toString().trim().split(/\r?\n/).filter(Boolean); - - const namedCmd = 'docker ps -aq --filter "name=hyperstack-mcp"'; - const named = execSync(namedCmd).toString().trim().split(/\r?\n/).filter(Boolean); - - const allToPurge = [...new Set([...existing, ...named])]; - - if (allToPurge.length > 0) { - console.log(`🧹 Found ${allToPurge.length} old container(s) and fragments. Purging immediately...`); - execSync(`docker rm -f ${allToPurge.join(' ')}`, { stdio: "ignore" }); - } - } catch(e) {} - - console.log("📥 Pulling the absolute latest ghcr.io/orkait/hyperstack:main..."); - execSync("docker pull ghcr.io/orkait/hyperstack:main", { stdio: "inherit" }); - - console.log("🏥 Booting clean persistent container (hyperstack-mcp)..."); - execSync("docker run -d --name hyperstack-mcp --restart unless-stopped --memory=512m --cpus=1 --entrypoint sleep ghcr.io/orkait/hyperstack:main infinity", { stdio: "ignore" }); - - console.log("✅ Registry & Engine synchronized successfully."); - } catch (err) { - console.log("\\n⚠️ Docker skipped: Docker engine not responsive or not installed on this host."); - } -} diff --git a/src/plugins/design-tokens/index.ts b/src/plugins/design-tokens/index.ts deleted file mode 100644 index a9570fd..0000000 --- a/src/plugins/design-tokens/index.ts +++ /dev/null @@ -1,24 +0,0 @@ -import type { ToolServer } from "../../shared/tool-types.js"; -import type { Plugin } from "../../registry.js"; -import { register as listCategories } from "./tools/list-categories.js"; -import { register as getCategory } from "./tools/get-category.js"; -import { register as getColorRamp } from "./tools/get-color-ramp.js"; -import { register as getProcedure } from "./tools/get-procedure.js"; -import { register as search } from "./tools/search.js"; -import { register as getGotchas } from "./tools/get-gotchas.js"; -import { register as generate } from "./tools/generate.js"; - -function register(server: ToolServer): void { - listCategories(server); - getCategory(server); - getColorRamp(server); - getProcedure(server); - search(server); - getGotchas(server); - generate(server); -} - -export const designTokensPlugin: Plugin = { - name: "design-tokens", - register, -}; diff --git a/src/plugins/designer/index.ts b/src/plugins/designer/index.ts deleted file mode 100644 index 110381e..0000000 --- a/src/plugins/designer/index.ts +++ /dev/null @@ -1,48 +0,0 @@ -import type { ToolServer } from "../../shared/tool-types.js"; -import type { Plugin } from "../../registry.js"; -import { register as listPersonalities } from "./tools/list-personalities.js"; -import { register as getPersonality } from "./tools/get-personality.js"; -import { register as getIndustryRules } from "./tools/get-industry-rules.js"; -import { register as getCognitiveLaw } from "./tools/get-cognitive-law.js"; -import { register as getDesignSystem } from "./tools/get-design-system.js"; -import { register as getCompositionRules } from "./tools/get-composition-rules.js"; -import { register as getInteractionPattern } from "./tools/get-interaction-pattern.js"; -import { register as getUxWriting } from "./tools/get-ux-writing.js"; -import { register as getLandingPattern } from "./tools/get-landing-pattern.js"; -import { register as getAntiPatterns } from "./tools/get-anti-patterns.js"; -import { register as resolveIntent } from "./tools/resolve-intent.js"; -import { register as search } from "./tools/search.js"; -import { register as generateDesignBrief } from "./tools/generate-design-brief.js"; -import { register as getPageTemplate } from "./tools/get-page-template.js"; -import { register as getPreset } from "./tools/get-preset.js"; -import { register as listPresets } from "./tools/list-presets.js"; -import { register as getFontPairing } from "./tools/get-font-pairing.js"; -import { register as generateImplementationPlan } from "./tools/generate-implementation-plan.js"; -import { register as verifyImplementation } from "./tools/verify-implementation.js"; - -function register(server: ToolServer): void { - listPersonalities(server); - getPersonality(server); - getIndustryRules(server); - getCognitiveLaw(server); - getDesignSystem(server); - getCompositionRules(server); - getInteractionPattern(server); - getUxWriting(server); - getLandingPattern(server); - getAntiPatterns(server); - resolveIntent(server); - search(server); - generateDesignBrief(server); - getPageTemplate(server); - getPreset(server); - listPresets(server); - getFontPairing(server); - generateImplementationPlan(server); - verifyImplementation(server); -} - -export const designerPlugin: Plugin = { - name: "designer", - register, -}; diff --git a/src/plugins/echo/index.ts b/src/plugins/echo/index.ts deleted file mode 100644 index 3d2083b..0000000 --- a/src/plugins/echo/index.ts +++ /dev/null @@ -1,19 +0,0 @@ -import type { ToolServer } from "../../shared/tool-types.js"; -import type { Plugin } from "../../registry.js"; -import { register as listRecipes } from "./tools/list-recipes.js"; -import { register as getRecipe } from "./tools/get-recipe.js"; -import { register as listMiddleware } from "./tools/list-middleware.js"; -import { register as getMiddleware } from "./tools/get-middleware.js"; -import { register as searchDocs } from "./tools/search-docs.js"; -import { register as decisionMatrix } from "./tools/decision-matrix.js"; - -function register(server: ToolServer): void { - listRecipes(server); - getRecipe(server); - listMiddleware(server); - getMiddleware(server); - searchDocs(server); - decisionMatrix(server); -} - -export const echoPlugin: Plugin = { name: "echo", register }; diff --git a/src/plugins/golang/index.ts b/src/plugins/golang/index.ts deleted file mode 100644 index 2458511..0000000 --- a/src/plugins/golang/index.ts +++ /dev/null @@ -1,22 +0,0 @@ -import type { ToolServer } from "../../shared/tool-types.js"; -import type { Plugin } from "../../registry.js"; -import { register as listPractices } from "./tools/list-practices.js"; -import { register as getPractice } from "./tools/get-practice.js"; -import { register as listPatterns } from "./tools/list-patterns.js"; -import { register as getPattern } from "./tools/get-pattern.js"; -import { register as getAntipatterns } from "./tools/get-antipatterns.js"; -import { register as searchDocs } from "./tools/search-docs.js"; - -function register(server: ToolServer): void { - listPractices(server); - getPractice(server); - listPatterns(server); - getPattern(server); - getAntipatterns(server); - searchDocs(server); -} - -export const golangPlugin: Plugin = { - name: "golang", - register, -}; diff --git a/src/plugins/hyperstack/index.ts b/src/plugins/hyperstack/index.ts deleted file mode 100644 index cc20052..0000000 --- a/src/plugins/hyperstack/index.ts +++ /dev/null @@ -1,9 +0,0 @@ -import type { Plugin, ToolServer } from "../../shared/tool-types.js"; -import { registerSetupTool } from "./tools/setup.js"; - -export const hyperstackPlugin: Plugin = { - name: "hyperstack", - register: (server: ToolServer) => { - registerSetupTool(server); - }, -}; diff --git a/src/plugins/hyperstack/tools/setup.ts b/src/plugins/hyperstack/tools/setup.ts deleted file mode 100644 index 682e2fa..0000000 --- a/src/plugins/hyperstack/tools/setup.ts +++ /dev/null @@ -1,51 +0,0 @@ -import type { ToolServer } from "../../../shared/tool-types.js"; -import { z } from "zod"; -import * as setup from "../../../internal/setup-hyperstack.js"; -import * as path from "node:path"; - -export function registerSetupTool(server: ToolServer) { - server.tool( - "hyperstack_setup", - "Identify current IDE/CLI environment and generate a tailored MCP configuration patch for Hyperstack.", - { - researchResult: z.string().optional().describe("If the environment was unknown, provide the researched config path or schema details here."), - method: z.enum(["docker", "local"]).default("docker").describe("Preferred installation method. Use 'docker' (default) for stable persistent environments, 'local' for fallback."), - }, - async ({ researchResult, method }) => { - const platform = setup.detectEnvironment(); - const configPath = setup.findConfigFile(platform); - - const pluginRoot = process.env.HYPERSTACK_ROOT || process.cwd(); - - if (!configPath && !researchResult) { - return { - content: [{ - type: "text", - text: `Detection failed for environment: ${platform}.\n\nPlease use 'web_search' to find where ${platform} stores its MCP configuration (e.g. 'Cursor MCP config path' or 'Windsurf MCP config location').\n\nOnce found, provide the path as 'researchResult'.` - }] - }; - } - - const activeConfigPath = configPath || (researchResult ? path.resolve(researchResult) : null); - - if (!activeConfigPath) { - return { - isError: true, - content: [{ - type: "text", - text: "Could not resolve a valid configuration path." - }] - }; - } - - const patch = setup.generateMcpPatch(activeConfigPath, pluginRoot, method); - - return { - content: [{ - type: "text", - text: `Environment Identified: ${platform}\nTarget Config: ${activeConfigPath}\n\nProposed Patch (JSON):\n${JSON.stringify(patch, null, 2)}\n\nTo apply this, the agent should read the file, merge the 'hyperstack' server entry, and write it back.` - }] - }; - } - ); -} diff --git a/src/plugins/lenis/index.ts b/src/plugins/lenis/index.ts deleted file mode 100644 index 5597370..0000000 --- a/src/plugins/lenis/index.ts +++ /dev/null @@ -1,22 +0,0 @@ -import type { ToolServer } from "../../shared/tool-types.js"; -import type { Plugin } from "../../registry.js"; -import { register as listApis } from "./tools/list-apis.js"; -import { register as getApi } from "./tools/get-api.js"; -import { register as searchDocs } from "./tools/search-docs.js"; -import { register as getPattern } from "./tools/get-pattern.js"; -import { register as generateSetup } from "./tools/generate-setup.js"; -import { register as cheatsheet } from "./tools/cheatsheet.js"; - -function register(server: ToolServer): void { - listApis(server); - getApi(server); - searchDocs(server); - getPattern(server); - generateSetup(server); - cheatsheet(server); -} - -export const lenisPlugin: Plugin = { - name: "lenis", - register, -}; diff --git a/src/plugins/motion/index.ts b/src/plugins/motion/index.ts deleted file mode 100644 index 9df40d1..0000000 --- a/src/plugins/motion/index.ts +++ /dev/null @@ -1,24 +0,0 @@ -import type { ToolServer } from "../../shared/tool-types.js"; -import type { Plugin } from "../../registry.js"; -import { register as listApis } from "./tools/list-apis.js"; -import { register as getApi } from "./tools/get-api.js"; -import { register as searchDocs } from "./tools/search-docs.js"; -import { register as getExamples } from "./tools/get-examples.js"; -import { register as getTransitions } from "./tools/get-transitions.js"; -import { register as generateAnimation } from "./tools/generate-animation.js"; -import { register as cheatsheet } from "./tools/cheatsheet.js"; - -function register(server: ToolServer): void { - listApis(server); - getApi(server); - searchDocs(server); - getExamples(server); - getTransitions(server); - generateAnimation(server); - cheatsheet(server); -} - -export const motionPlugin: Plugin = { - name: "motion", - register, -}; diff --git a/src/plugins/react/index.ts b/src/plugins/react/index.ts deleted file mode 100644 index 51718a8..0000000 --- a/src/plugins/react/index.ts +++ /dev/null @@ -1,18 +0,0 @@ -import type { ToolServer } from "../../shared/tool-types.js"; -import type { Plugin } from "../../registry.js"; -import { register as listPatterns } from "./tools/list-patterns.js"; -import { register as getPattern } from "./tools/get-pattern.js"; -import { register as getConstraints } from "./tools/get-constraints.js"; -import { register as searchDocs } from "./tools/search-docs.js"; - -function register(server: ToolServer): void { - listPatterns(server); - getPattern(server); - getConstraints(server); - searchDocs(server); -} - -export const reactPlugin: Plugin = { - name: "react", - register, -}; diff --git a/src/plugins/reactflow/index.ts b/src/plugins/reactflow/index.ts deleted file mode 100644 index 05b3c23..0000000 --- a/src/plugins/reactflow/index.ts +++ /dev/null @@ -1,28 +0,0 @@ -import type { ToolServer } from "../../shared/tool-types.js"; -import type { Plugin } from "../../registry.js"; -import { register as listApis } from "./tools/list-apis.js"; -import { register as getApi } from "./tools/get-api.js"; -import { register as searchDocs } from "./tools/search-docs.js"; -import { register as getExamples } from "./tools/get-examples.js"; -import { register as getPattern } from "./tools/get-pattern.js"; -import { register as getTemplate } from "./tools/get-template.js"; -import { register as getMigrationGuide } from "./tools/get-migration-guide.js"; -import { register as generateFlow } from "./tools/generate-flow.js"; -import { register as cheatsheet } from "./tools/cheatsheet.js"; - -function register(server: ToolServer): void { - listApis(server); - getApi(server); - searchDocs(server); - getExamples(server); - getPattern(server); - getTemplate(server); - getMigrationGuide(server); - generateFlow(server); - cheatsheet(server); -} - -export const reactflowPlugin: Plugin = { - name: "reactflow", - register, -}; diff --git a/src/plugins/rust/index.ts b/src/plugins/rust/index.ts deleted file mode 100644 index bde2299..0000000 --- a/src/plugins/rust/index.ts +++ /dev/null @@ -1,18 +0,0 @@ -import type { ToolServer } from "../../shared/tool-types.js"; -import type { Plugin } from "../../registry.js"; -import { register as listPractices } from "./tools/list-practices.js"; -import { register as getPractice } from "./tools/get-practice.js"; -import { register as searchDocs } from "./tools/search-docs.js"; -import { register as cheatsheet } from "./tools/cheatsheet.js"; - -function register(server: ToolServer): void { - listPractices(server); - getPractice(server); - searchDocs(server); - cheatsheet(server); -} - -export const rustPlugin: Plugin = { - name: "rust", - register, -}; diff --git a/src/plugins/shadcn/index.ts b/src/plugins/shadcn/index.ts deleted file mode 100644 index d052266..0000000 --- a/src/plugins/shadcn/index.ts +++ /dev/null @@ -1,20 +0,0 @@ -import type { ToolServer } from "../../shared/tool-types.js"; -import type { Plugin } from "../../registry.js"; -import { register as listComponents } from "./tools/list-components.js"; -import { register as getComponent } from "./tools/get-component.js"; -import { register as getRules } from "./tools/get-rules.js"; -import { register as getSnippet } from "./tools/get-snippet.js"; -import { register as getComposition } from "./tools/get-composition.js"; - -function register(server: ToolServer): void { - listComponents(server); - getComponent(server); - getRules(server); - getSnippet(server); - getComposition(server); -} - -export const shadcnPlugin: Plugin = { - name: "shadcn", - register, -}; diff --git a/src/plugins/ui-ux/index.ts b/src/plugins/ui-ux/index.ts deleted file mode 100644 index 170ef8c..0000000 --- a/src/plugins/ui-ux/index.ts +++ /dev/null @@ -1,22 +0,0 @@ -import type { ToolServer } from "../../shared/tool-types.js"; -import type { Plugin } from "../../registry.js"; -import { register as listPrinciples } from "./tools/list-principles.js"; -import { register as getPrinciple } from "./tools/get-principle.js"; -import { register as getComponentPattern } from "./tools/get-component-pattern.js"; -import { register as getChecklist } from "./tools/get-checklist.js"; -import { register as search } from "./tools/search.js"; -import { register as getGotchas } from "./tools/get-gotchas.js"; - -function register(server: ToolServer): void { - listPrinciples(server); - getPrinciple(server); - getComponentPattern(server); - getChecklist(server); - search(server); - getGotchas(server); -} - -export const uiUxPlugin: Plugin = { - name: "ui-ux", - register, -}; diff --git a/src/registry.ts b/src/registry.ts deleted file mode 100755 index a7d95b6..0000000 --- a/src/registry.ts +++ /dev/null @@ -1,12 +0,0 @@ -import type { ToolServer } from "./shared/tool-types.js"; - -export interface Plugin { - name: string; - register: (server: ToolServer) => void; -} - -export function loadPlugins(server: ToolServer, plugins: Plugin[]): void { - for (const plugin of plugins) { - plugin.register(server); - } -} From f5c14a68afaff0398cba1aab61f8b443c4906ee1 Mon Sep 17 00:00:00 2001 From: Kailas Mahavarkar <66670953+KailasMahavarkar@users.noreply.github.com> Date: Wed, 22 Apr 2026 11:03:54 +0530 Subject: [PATCH 3/5] chore: delete unreachable engine modules (navigation, injector, resolver, skill-enforcer) Audit showed these modules are not imported by cli.ts, the router, the artifact validator, the tool bridge, or any script. They remained from the earlier exploration phase. Deleted: - src/engine/navigation.ts (getBundle, listAgentRouting - zero consumers) - src/engine/injector.ts (buildInjectionSlice - zero consumers) - src/engine/resolver.ts (resolveCapabilityContext - zero consumers outside itself) - src/engine/skill-enforcer.ts (assertSkillAllowedForAgent - zero consumers) Trimmed: - src/engine/policy.ts no longer exports getBundleByCapability; BundlePolicy import removed along with it. Only getAgent, getDomain, and getStrictestProofMode remain, all of which are consumed by the router. Typecheck still exits 0. Router, artifact validator, and CLI tool path unchanged. Co-Authored-By: Claude Opus 4.7 (1M context) --- src/engine/injector.ts | 10 ---------- src/engine/navigation.ts | 17 ----------------- src/engine/policy.ts | 10 +--------- src/engine/resolver.ts | 21 --------------------- src/engine/skill-enforcer.ts | 7 ------- 5 files changed, 1 insertion(+), 64 deletions(-) delete mode 100644 src/engine/injector.ts delete mode 100644 src/engine/navigation.ts delete mode 100644 src/engine/resolver.ts delete mode 100644 src/engine/skill-enforcer.ts diff --git a/src/engine/injector.ts b/src/engine/injector.ts deleted file mode 100644 index cda1c20..0000000 --- a/src/engine/injector.ts +++ /dev/null @@ -1,10 +0,0 @@ -import type { BundlePolicy } from "./contracts.js"; - -export function buildInjectionSlice(bundle: BundlePolicy, capability: string) { - return { - bundle: bundle.id, - capability, - sources: bundle.sources, - toolPrefixes: bundle.toolPrefixes, - }; -} diff --git a/src/engine/navigation.ts b/src/engine/navigation.ts deleted file mode 100644 index 231f8ee..0000000 --- a/src/engine/navigation.ts +++ /dev/null @@ -1,17 +0,0 @@ -import type { BundlePolicy, LoadedTopology } from "./contracts.js"; - -export function getBundle(topology: LoadedTopology, bundleId: string): BundlePolicy { - const bundle = topology.bundles.find((entry) => entry.id === bundleId); - if (!bundle) { - throw new Error(`Unknown bundle: ${bundleId}`); - } - return bundle; -} - -export function listAgentRouting(topology: LoadedTopology) { - return topology.agents.map((agent) => ({ - id: agent.id, - allowedBundles: agent.allowedBundles, - forbiddenBundles: agent.forbiddenBundles, - })); -} diff --git a/src/engine/policy.ts b/src/engine/policy.ts index a3f8ee5..9240827 100644 --- a/src/engine/policy.ts +++ b/src/engine/policy.ts @@ -1,4 +1,4 @@ -import type { AgentPolicy, BundlePolicy, DomainPolicy, LoadedTopology } from "./contracts.js"; +import type { AgentPolicy, DomainPolicy, LoadedTopology } from "./contracts.js"; export function getAgent(topology: LoadedTopology, agentId: string): AgentPolicy { const agent = topology.agents.find((entry) => entry.id === agentId); @@ -8,14 +8,6 @@ export function getAgent(topology: LoadedTopology, agentId: string): AgentPolicy return agent; } -export function getBundleByCapability(topology: LoadedTopology, capability: string): BundlePolicy { - const bundle = topology.bundles.find((entry) => entry.capabilities.includes(capability)); - if (!bundle) { - throw new Error(`No bundle found for capability: ${capability}`); - } - return bundle; -} - export function getDomain(topology: LoadedTopology, domainId: string): DomainPolicy { const domain = topology.domains.find((entry) => entry.id === domainId); if (!domain) { diff --git a/src/engine/resolver.ts b/src/engine/resolver.ts deleted file mode 100644 index 6d0789a..0000000 --- a/src/engine/resolver.ts +++ /dev/null @@ -1,21 +0,0 @@ -import type { LoadedTopology } from "./contracts.js"; -import { getAgent, getBundleByCapability, getDomain } from "./policy.js"; - -export function resolveCapabilityContext( - topology: LoadedTopology, - input: { agentId: string; capability: string }, -) { - const agent = getAgent(topology, input.agentId); - const bundle = getBundleByCapability(topology, input.capability); - const domain = getDomain(topology, bundle.domain); - - if (!agent.allowedBundles.includes(bundle.id)) { - throw new Error(`Agent ${agent.id} attempted forbidden bundle ${bundle.id}`); - } - - if (agent.forbiddenBundles.includes(bundle.id) || domain.forbiddenBundles.includes(bundle.id)) { - throw new Error(`Agent ${agent.id} attempted forbidden bundle ${bundle.id}`); - } - - return { agent, bundle, domain }; -} diff --git a/src/engine/skill-enforcer.ts b/src/engine/skill-enforcer.ts deleted file mode 100644 index 876fc2b..0000000 --- a/src/engine/skill-enforcer.ts +++ /dev/null @@ -1,7 +0,0 @@ -import type { AgentPolicy } from "./contracts.js"; - -export function assertSkillAllowedForAgent(agent: AgentPolicy, skillName: string): void { - if (!agent.allowedSkills.includes(skillName)) { - throw new Error(`Skill ${skillName} is not allowed for agent ${agent.id}`); - } -} From d0f0e7df96444b357cfec7475442dba9527bb6fc Mon Sep 17 00:00:00 2001 From: Kailas Mahavarkar <66670953+KailasMahavarkar@users.noreply.github.com> Date: Wed, 22 Apr 2026 11:07:25 +0530 Subject: [PATCH 4/5] chore: strip corpus-reader dead code from 12 plugin tools Reverts the 12 corpus-aware tool handlers back to in-file-only dispatch after PR #91 deleted the corpus/ directory. Each file had a 40-80 line loadCorpusX loader that read YAML files from a corpus tree that no longer exists, caught the ENOENT, and fell through to the plugin data. Net effect was dead code that re-read and parsed the corpus tree on every cold call path. Files simplified: - src/plugins/echo/tools/get-recipe.ts - src/plugins/rust/tools/get-practice.ts - src/plugins/golang/tools/get-practice.ts - src/plugins/react/tools/get-pattern.ts - src/plugins/ui-ux/tools/get-principle.ts - src/plugins/shadcn/tools/get-component.ts - src/plugins/motion/tools/get-api.ts - src/plugins/motion/tools/get-examples.ts - src/plugins/reactflow/tools/get-api.ts - src/plugins/designer/tools/get-page-template.ts - src/plugins/design-tokens/tools/get-procedure.ts - src/plugins/lenis/tools/get-pattern.ts Each tool now: zod-typed args -> in-file data lookup -> render. No YAML, no fs reads, no cache, no corpus namespace. Verified: - bun run build - typecheck exit 0 - bin/hyperstack.mjs tool designer_resolve_intent --json '{"product":"test"}' returns the expected output - bin/hyperstack.mjs tool design_tokens_get_procedure --json '{"step":2}' returns the step 2 procedure (confirming plugin data path still works for a tool that was most corpus-backed under the old layout) Co-Authored-By: Claude Opus 4.7 (1M context) --- .../design-tokens/tools/get-procedure.ts | 128 +---------------- .../designer/tools/get-page-template.ts | 118 +--------------- src/plugins/echo/tools/get-recipe.ts | 115 +-------------- src/plugins/golang/tools/get-practice.ts | 112 +-------------- src/plugins/lenis/tools/get-pattern.ts | 77 +--------- src/plugins/motion/tools/get-api.ts | 103 +------------- src/plugins/motion/tools/get-examples.ts | 99 +------------ src/plugins/react/tools/get-pattern.ts | 110 +-------------- src/plugins/reactflow/tools/get-api.ts | 131 +----------------- src/plugins/rust/tools/get-practice.ts | 111 +-------------- src/plugins/shadcn/tools/get-component.ts | 118 +--------------- src/plugins/ui-ux/tools/get-principle.ts | 111 +-------------- 12 files changed, 41 insertions(+), 1292 deletions(-) diff --git a/src/plugins/design-tokens/tools/get-procedure.ts b/src/plugins/design-tokens/tools/get-procedure.ts index 5f7e916..fdec648 100644 --- a/src/plugins/design-tokens/tools/get-procedure.ts +++ b/src/plugins/design-tokens/tools/get-procedure.ts @@ -1,118 +1,7 @@ import type { ToolServer } from "../../../shared/tool-types.js"; -import { readFileSync } from "node:fs"; -import { dirname, join } from "node:path"; -import { fileURLToPath } from "node:url"; -import YAML from "yaml"; import { z } from "zod"; import { TOKEN_PROCEDURES, getProcedureByStep } from "../data.js"; -type CorpusIndex = { - namespaces?: { - "frontend.design-tokens"?: { - index?: string; - }; - }; -}; - -type CorpusNamespaceIndex = { - namespace?: string; - procedures?: Record; -}; - -type CorpusProcedureEntry = { - step?: number; - title?: string; - description?: string; - code?: string; - rules?: string[]; - gotchas?: string[]; -}; - -type LoadedCorpusProcedure = { - step: number; - title: string; - description: string; - code: string; - rules: string[]; - gotchas: string[]; - source: string; -}; - -const moduleDir = dirname(fileURLToPath(import.meta.url)); -const corpusRoot = join(moduleDir, "../../../../corpus"); -const corpusNamespace = "frontend.design-tokens"; - -let cachedCorpusProcedures: Map | null | undefined; - -function loadCorpusProcedures(): Map | null { - if (cachedCorpusProcedures !== undefined) { - return cachedCorpusProcedures; - } - - try { - const indexRaw = readFileSync(join(corpusRoot, "index.yaml"), "utf8"); - const index = YAML.parse(indexRaw) as CorpusIndex | null; - const namespaceIndexPath = index?.namespaces?.[corpusNamespace]?.index; - if (!namespaceIndexPath) { - cachedCorpusProcedures = null; - return cachedCorpusProcedures; - } - - const namespaceRaw = readFileSync(join(corpusRoot, namespaceIndexPath), "utf8"); - const namespaceIndex = YAML.parse(namespaceRaw) as CorpusNamespaceIndex | null; - const procedureEntries = namespaceIndex?.procedures ?? {}; - const loaded = new Map(); - - for (const [key, meta] of Object.entries(procedureEntries)) { - const match = key.match(/^step-(\d+)$/i); - const step = match ? Number(match[1]) : Number.NaN; - const file = meta?.file; - if (!Number.isInteger(step) || !file) { - continue; - } - - const raw = readFileSync(join(corpusRoot, "frontend/design-tokens", file), "utf8"); - const entry = YAML.parse(raw) as CorpusProcedureEntry | null; - if ( - !entry || - entry.step !== step || - !entry.title || - !entry.description || - !entry.code || - !Array.isArray(entry.rules) || - !Array.isArray(entry.gotchas) - ) { - continue; - } - - loaded.set(step, { - step, - title: entry.title, - description: entry.description, - code: entry.code, - rules: entry.rules, - gotchas: entry.gotchas, - source: corpusNamespace, - }); - } - - cachedCorpusProcedures = loaded.size > 0 ? loaded : null; - return cachedCorpusProcedures; - } catch { - cachedCorpusProcedures = null; - return cachedCorpusProcedures; - } -} - -function getProcedure(step: number) { - const corpusEntry = loadCorpusProcedures()?.get(step); - if (corpusEntry) { - return corpusEntry; - } - - return getProcedureByStep(step); -} - function renderProcedure(proc: { step: number; title: string; @@ -120,7 +9,6 @@ function renderProcedure(proc: { code?: string; rules?: string[]; gotchas?: string[]; - source?: string; }): string { let text = `# Step ${proc.step}: ${proc.title}\n\n${proc.description}\n\n`; @@ -138,10 +26,6 @@ function renderProcedure(proc: { for (const gotcha of proc.gotchas) text += `- ${gotcha}\n`; } - if (proc.source) { - text += `\n**Corpus Source:** ${proc.source}`; - } - return text; } @@ -153,9 +37,9 @@ export function register(server: ToolServer): void { step: z.number().min(1).max(8).optional() .describe("Step number (1-8). Omit to get all steps."), }, - async ({ step }) => { + async ({ step }: { step?: number }) => { if (step !== undefined) { - const proc = getProcedure(step); + const proc = getProcedureByStep(step); if (!proc) { return { content: [{ type: "text", text: `Step ${step} not found. Valid steps: 1-${TOKEN_PROCEDURES.length}` }], @@ -168,14 +52,10 @@ export function register(server: ToolServer): void { let text = "# Design Token Build Procedure\n\n"; text += "A complete token system = 10 categories. Follow in order.\n\n"; for (const proc of TOKEN_PROCEDURES) { - const corpusProc = loadCorpusProcedures()?.get(proc.step); - text += `## Step ${proc.step}: ${corpusProc?.title ?? proc.title}\n${corpusProc?.description ?? proc.description}\n\n`; - } - if (loadCorpusProcedures()) { - text += `**Corpus Source:** ${corpusNamespace}`; + text += `## Step ${proc.step}: ${proc.title}\n${proc.description}\n\n`; } text += "\nCall `design_tokens_get_procedure` with a step number for full code + rules."; return { content: [{ type: "text", text }] }; - } + }, ); } diff --git a/src/plugins/designer/tools/get-page-template.ts b/src/plugins/designer/tools/get-page-template.ts index 3a6ce01..d943ce0 100644 --- a/src/plugins/designer/tools/get-page-template.ts +++ b/src/plugins/designer/tools/get-page-template.ts @@ -1,116 +1,7 @@ import type { ToolServer } from "../../../shared/tool-types.js"; -import { readFileSync } from "node:fs"; -import { dirname, join } from "node:path"; -import { fileURLToPath } from "node:url"; -import YAML from "yaml"; import { z } from "zod"; import { PAGE_TYPES, getPageTemplate } from "../data.js"; -type CorpusIndex = { - namespaces?: { - "frontend.designer"?: { - index?: string; - }; - }; -}; - -type CorpusNamespaceIndex = { - namespace?: string; - pageTemplates?: { - dashboard?: { - file?: string; - }; - }; -}; - -type CorpusPageSection = { - name?: string; - required?: boolean; - components?: string[]; - notes?: string; -}; - -type CorpusPageTemplate = { - type?: string; - description?: string; - layout?: string; - sections?: CorpusPageSection[]; - cognitiveApply?: string[]; - compositionApply?: string[]; - interactionApply?: string[]; - writingApply?: string[]; - keyRules?: string[]; -}; - -const moduleDir = dirname(fileURLToPath(import.meta.url)); -const corpusRoot = join(moduleDir, "../../../../corpus"); -const corpusNamespace = "frontend.designer"; - -let cachedCorpusDashboard: { template: CorpusPageTemplate; source: string } | null | undefined; - -function loadCorpusDashboardTemplate(): { template: CorpusPageTemplate; source: string } | null { - if (cachedCorpusDashboard !== undefined) { - return cachedCorpusDashboard; - } - - try { - const indexRaw = readFileSync(join(corpusRoot, "index.yaml"), "utf8"); - const index = YAML.parse(indexRaw) as CorpusIndex | null; - const namespaceIndexPath = index?.namespaces?.[corpusNamespace]?.index; - if (!namespaceIndexPath) { - cachedCorpusDashboard = null; - return null; - } - - const namespaceRaw = readFileSync(join(corpusRoot, namespaceIndexPath), "utf8"); - const namespaceIndex = YAML.parse(namespaceRaw) as CorpusNamespaceIndex | null; - const pageTemplatePath = namespaceIndex?.pageTemplates?.dashboard?.file; - if (!pageTemplatePath) { - cachedCorpusDashboard = null; - return null; - } - - const templateRaw = readFileSync(join(corpusRoot, "frontend/designer", pageTemplatePath), "utf8"); - const template = YAML.parse(templateRaw) as CorpusPageTemplate | null; - const sections = template?.sections; - const cognitiveApply = template?.cognitiveApply; - const compositionApply = template?.compositionApply; - const interactionApply = template?.interactionApply; - const writingApply = template?.writingApply; - const keyRules = template?.keyRules; - if ( - !template || - template.type?.toLowerCase() !== "dashboard" || - !template.description || - !template.layout || - !Array.isArray(sections) || - sections.length === 0 || - !Array.isArray(cognitiveApply) || - !Array.isArray(compositionApply) || - !Array.isArray(interactionApply) || - !Array.isArray(writingApply) || - !Array.isArray(keyRules) - ) { - cachedCorpusDashboard = null; - return null; - } - - cachedCorpusDashboard = { template, source: corpusNamespace }; - return cachedCorpusDashboard; - } catch { - cachedCorpusDashboard = null; - return null; - } -} - -function getCorpusAwarePageTemplate(type: (typeof PAGE_TYPES)[number]) { - if (type === "dashboard") { - return loadCorpusDashboardTemplate()?.template ?? getPageTemplate(type); - } - - return getPageTemplate(type); -} - export function register(server: ToolServer): void { server.tool( "designer_get_page_template", @@ -118,9 +9,8 @@ export function register(server: ToolServer): void { { type: z.enum(PAGE_TYPES).describe("Page type to get template for"), }, - async ({ type }) => { - const corpusEntry = type === "dashboard" ? loadCorpusDashboardTemplate() : null; - const template = getCorpusAwarePageTemplate(type); + async ({ type }: { type: (typeof PAGE_TYPES)[number] }) => { + const template = getPageTemplate(type); if (!template) { return { content: [{ type: "text" as const, text: `Page type "${type}" not found. Available: ${PAGE_TYPES.join(", ")}` }], @@ -153,10 +43,6 @@ export function register(server: ToolServer): void { text += `- ${rule}\n`; } - if (corpusEntry) { - text += `\n**Corpus Source:** ${corpusEntry.source}`; - } - return { content: [{ type: "text" as const, text }] }; }, ); diff --git a/src/plugins/echo/tools/get-recipe.ts b/src/plugins/echo/tools/get-recipe.ts index 2fc51fb..91cd370 100644 --- a/src/plugins/echo/tools/get-recipe.ts +++ b/src/plugins/echo/tools/get-recipe.ts @@ -1,102 +1,6 @@ import type { ToolServer } from "../../../shared/tool-types.js"; -import { readFileSync } from "node:fs"; -import { dirname, join } from "node:path"; -import { fileURLToPath } from "node:url"; -import YAML from "yaml"; import { z } from "zod"; -import { RECIPES, getRecipeByName, searchRecipes, formatRecipe, type Recipe } from "../data.js"; - -type CorpusIndex = { - namespaces?: { - "backend.echo"?: { - index?: string; - }; - }; -}; - -type CorpusNamespaceIndex = { - namespace?: string; - recipes?: Record; -}; - -type CorpusRecipeEntry = { - name?: string; - category?: string; - description?: string; - when?: string; - code?: string; - gotchas?: string[]; - relatedRecipes?: string[]; -}; - -type LoadedCorpusRecipe = { recipe: Recipe; source: string }; - -const moduleDir = dirname(fileURLToPath(import.meta.url)); -const corpusRoot = join(moduleDir, "../../../../corpus"); -const corpusNamespace = "backend.echo"; - -const cachedCorpusRecipes = new Map(); - -function normalize(name: string): string { - return name.toLowerCase().trim(); -} - -function loadCorpusRecipe(name: string): LoadedCorpusRecipe | null { - const key = normalize(name); - if (cachedCorpusRecipes.has(key)) { - return cachedCorpusRecipes.get(key) ?? null; - } - - try { - const indexRaw = readFileSync(join(corpusRoot, "index.yaml"), "utf8"); - const index = YAML.parse(indexRaw) as CorpusIndex | null; - const namespaceIndexPath = index?.namespaces?.[corpusNamespace]?.index; - if (!namespaceIndexPath) { - cachedCorpusRecipes.set(key, null); - return null; - } - - const namespaceRaw = readFileSync(join(corpusRoot, namespaceIndexPath), "utf8"); - const namespaceIndex = YAML.parse(namespaceRaw) as CorpusNamespaceIndex | null; - const recipePath = namespaceIndex?.recipes?.[key]?.file; - if (!recipePath) { - cachedCorpusRecipes.set(key, null); - return null; - } - - const raw = readFileSync(join(corpusRoot, "backend/echo", recipePath), "utf8"); - const entry = YAML.parse(raw) as CorpusRecipeEntry | null; - if ( - !entry || - normalize(entry.name ?? "") !== key || - !entry.category || - !entry.description || - !entry.when || - !entry.code - ) { - cachedCorpusRecipes.set(key, null); - return null; - } - - const loaded: LoadedCorpusRecipe = { - recipe: { - name: entry.name ?? name, - category: entry.category as Recipe["category"], - description: entry.description, - when: entry.when, - code: entry.code, - gotchas: entry.gotchas, - relatedRecipes: entry.relatedRecipes, - }, - source: corpusNamespace, - }; - cachedCorpusRecipes.set(key, loaded); - return loaded; - } catch { - cachedCorpusRecipes.set(key, null); - return null; - } -} +import { RECIPES, getRecipeByName, searchRecipes, formatRecipe } from "../data.js"; export function register(server: ToolServer): void { server.tool( @@ -105,9 +9,8 @@ export function register(server: ToolServer): void { { name: z.string().describe("Recipe name (e.g., 'crud-api', 'websocket', 'sse', 'jwt-auth', 'graceful-shutdown')"), }, - async ({ name }) => { - const corpusEntry = loadCorpusRecipe(name); - const recipe = corpusEntry?.recipe ?? getRecipeByName(name); + async ({ name }: { name: string }) => { + const recipe = getRecipeByName(name); if (!recipe) { const suggestions = searchRecipes(name).map((r) => r.name); const allNames = RECIPES.map((r) => r.name).join(", "); @@ -121,15 +24,7 @@ export function register(server: ToolServer): void { isError: true, }; } - const formatted = formatRecipe(recipe); - return { - content: [ - { - type: "text", - text: corpusEntry ? `${formatted}\n**Corpus Source:** ${corpusEntry.source}` : formatted, - }, - ], - }; - } + return { content: [{ type: "text", text: formatRecipe(recipe) }] }; + }, ); } diff --git a/src/plugins/golang/tools/get-practice.ts b/src/plugins/golang/tools/get-practice.ts index f5975d9..ca96d9f 100644 --- a/src/plugins/golang/tools/get-practice.ts +++ b/src/plugins/golang/tools/get-practice.ts @@ -1,102 +1,6 @@ import type { ToolServer } from "../../../shared/tool-types.js"; -import { readFileSync } from "node:fs"; -import { dirname, join } from "node:path"; -import { fileURLToPath } from "node:url"; -import YAML from "yaml"; import { z } from "zod"; -import { BEST_PRACTICES, type BestPractice } from "../data.js"; - -type CorpusIndex = { - namespaces?: { - "backend.golang"?: { - index?: string; - }; - }; -}; - -type CorpusNamespaceIndex = { - namespace?: string; - practices?: Record; -}; - -type CorpusPracticeEntry = { - name?: string; - topic?: string; - priority?: string; - rule?: string; - reason?: string; - good?: string; - bad?: string; -}; - -type LoadedCorpusPractice = { practice: BestPractice; source: string }; - -const moduleDir = dirname(fileURLToPath(import.meta.url)); -const corpusRoot = join(moduleDir, "../../../../corpus"); -const corpusNamespace = "backend.golang"; - -const cachedCorpusPractices = new Map(); - -function normalize(name: string): string { - return name.toLowerCase().trim(); -} - -function loadCorpusPractice(name: string): LoadedCorpusPractice | null { - const key = normalize(name); - if (cachedCorpusPractices.has(key)) { - return cachedCorpusPractices.get(key) ?? null; - } - - try { - const indexRaw = readFileSync(join(corpusRoot, "index.yaml"), "utf8"); - const index = YAML.parse(indexRaw) as CorpusIndex | null; - const namespaceIndexPath = index?.namespaces?.[corpusNamespace]?.index; - if (!namespaceIndexPath) { - cachedCorpusPractices.set(key, null); - return null; - } - - const namespaceRaw = readFileSync(join(corpusRoot, namespaceIndexPath), "utf8"); - const namespaceIndex = YAML.parse(namespaceRaw) as CorpusNamespaceIndex | null; - const practicePath = namespaceIndex?.practices?.[key]?.file; - if (!practicePath) { - cachedCorpusPractices.set(key, null); - return null; - } - - const raw = readFileSync(join(corpusRoot, "backend/golang", practicePath), "utf8"); - const entry = YAML.parse(raw) as CorpusPracticeEntry | null; - if ( - !entry || - normalize(entry.name ?? "") !== key || - !entry.topic || - (entry.priority !== "P0" && entry.priority !== "P1") || - !entry.rule || - !entry.reason - ) { - cachedCorpusPractices.set(key, null); - return null; - } - - const loaded: LoadedCorpusPractice = { - practice: { - name: entry.name ?? name, - topic: entry.topic as BestPractice["topic"], - priority: entry.priority, - rule: entry.rule, - reason: entry.reason, - good: entry.good, - bad: entry.bad, - }, - source: corpusNamespace, - }; - cachedCorpusPractices.set(key, loaded); - return loaded; - } catch { - cachedCorpusPractices.set(key, null); - return null; - } -} +import { BEST_PRACTICES } from "../data.js"; export function register(server: ToolServer): void { server.tool( @@ -105,11 +9,8 @@ export function register(server: ToolServer): void { { name: z.string().describe("Practice name (e.g. 'error-wrapping', 'goroutine-lifecycle', 'crypto-rand', 'table-driven-tests', 'thin-handlers')"), }, - async ({ name }) => { - const corpusEntry = loadCorpusPractice(name); - const practice = - corpusEntry?.practice ?? - BEST_PRACTICES.find((p) => p.name.toLowerCase() === name.toLowerCase()); + async ({ name }: { name: string }) => { + const practice = BEST_PRACTICES.find((p) => p.name.toLowerCase() === name.toLowerCase()); if (!practice) { return { content: [{ type: "text", text: `Practice "${name}" not found.\n\nAvailable: ${BEST_PRACTICES.map((p) => p.name).join(", ")}` }], @@ -122,12 +23,7 @@ export function register(server: ToolServer): void { text += `**Why:** ${practice.reason}\n\n`; if (practice.good) text += `## ✅ Good\n\`\`\`go\n${practice.good}\n\`\`\`\n\n`; if (practice.bad) text += `## ❌ Bad\n\`\`\`go\n${practice.bad}\n\`\`\`\n`; - - if (corpusEntry) { - text += `\n**Corpus Source:** ${corpusEntry.source}`; - } - return { content: [{ type: "text", text }] }; - } + }, ); } diff --git a/src/plugins/lenis/tools/get-pattern.ts b/src/plugins/lenis/tools/get-pattern.ts index fbee5c4..afbd1c5 100644 --- a/src/plugins/lenis/tools/get-pattern.ts +++ b/src/plugins/lenis/tools/get-pattern.ts @@ -1,76 +1,9 @@ import type { ToolServer } from "../../../shared/tool-types.js"; -import { readFileSync } from "node:fs"; -import { dirname, join } from "node:path"; -import { fileURLToPath } from "node:url"; -import YAML from "yaml"; import { z } from "zod"; import { PATTERNS } from "../data.js"; const PATTERN_NAMES = Object.keys(PATTERNS) as [string, ...string[]]; -type CorpusIndex = { - namespaces?: { - "frontend.lenis"?: { - index?: string; - }; - }; -}; - -type CorpusNamespaceIndex = { - namespace?: string; - patterns?: { - [name: string]: { - file?: string; - }; - }; -}; - -type CorpusPatternEntry = { - name?: string; - description?: string; - code?: string; - tips?: string[]; -}; - -const moduleDir = dirname(fileURLToPath(import.meta.url)); -const corpusRoot = join(moduleDir, "../../../../corpus"); -const corpusNamespace = "frontend.lenis"; - -let cachedCorpusPattern: { name: string; pattern: CorpusPatternEntry; source: string } | undefined; - -function loadCorpusPattern(name: string): { name: string; pattern: CorpusPatternEntry; source: string } | null { - if (cachedCorpusPattern?.name === name) { - return cachedCorpusPattern; - } - - try { - const indexRaw = readFileSync(join(corpusRoot, "index.yaml"), "utf8"); - const index = YAML.parse(indexRaw) as CorpusIndex | null; - const namespaceIndexPath = index?.namespaces?.[corpusNamespace]?.index; - if (!namespaceIndexPath) { - return null; - } - - const namespaceRaw = readFileSync(join(corpusRoot, namespaceIndexPath), "utf8"); - const namespaceIndex = YAML.parse(namespaceRaw) as CorpusNamespaceIndex | null; - const patternPath = namespaceIndex?.patterns?.[name]?.file; - if (!patternPath) { - return null; - } - - const patternRaw = readFileSync(join(corpusRoot, "frontend/lenis", patternPath), "utf8"); - const pattern = YAML.parse(patternRaw) as CorpusPatternEntry | null; - if (!pattern || pattern.name?.toLowerCase() !== name.toLowerCase() || !pattern.description || !pattern.code) { - return null; - } - - cachedCorpusPattern = { name, pattern, source: corpusNamespace }; - return cachedCorpusPattern; - } catch { - return null; - } -} - export function register(server: ToolServer): void { server.tool( "lenis_get_pattern", @@ -82,9 +15,8 @@ export function register(server: ToolServer): void { "Pattern name: full-page | next-js | gsap-integration | framer-motion-integration | custom-container | accessibility | scroll-to-nav", ), }, - async ({ name }) => { - const corpusEntry = loadCorpusPattern(name); - const pattern = corpusEntry?.pattern ?? PATTERNS[name]; + async ({ name }: { name: string }) => { + const pattern = PATTERNS[name]; if (!pattern) { return { content: [ @@ -107,11 +39,6 @@ export function register(server: ToolServer): void { text += `## Key Notes\n\n`; for (const tip of pattern.tips) text += `- ${tip}\n`; } - - if (corpusEntry) { - text += `\n**Corpus Source:** ${corpusEntry.source}`; - } - return { content: [{ type: "text", text }] }; }, ); diff --git a/src/plugins/motion/tools/get-api.ts b/src/plugins/motion/tools/get-api.ts index 5944693..c6d3e63 100644 --- a/src/plugins/motion/tools/get-api.ts +++ b/src/plugins/motion/tools/get-api.ts @@ -1,95 +1,7 @@ import type { ToolServer } from "../../../shared/tool-types.js"; -import { readFileSync } from "node:fs"; -import { dirname, join } from "node:path"; -import { fileURLToPath } from "node:url"; -import YAML from "yaml"; import { z } from "zod"; import { ALL_APIS, searchApis, getApiByName, formatApiReference } from "../data.js"; -type CorpusIndex = { - namespaces?: { - "frontend.motion"?: { - index?: string; - }; - }; -}; - -type CorpusNamespaceIndex = { - namespace?: string; - apis?: Record; -}; - -type CorpusApiEntry = { - name?: string; - kind?: string; - description?: string; - importPath?: string; - props?: Array<{ name: string; type: string; description: string; default?: string }>; - returns?: string; - usage?: string; - examples?: Array<{ title: string; code: string; description?: string; category: string }>; - tips?: string[]; - relatedApis?: string[]; -}; - -type LoadedCorpusApi = { api: CorpusApiEntry; source: string }; - -const moduleDir = dirname(fileURLToPath(import.meta.url)); -const corpusRoot = join(moduleDir, "../../../../corpus"); -const corpusNamespace = "frontend.motion"; - -const cachedCorpusApis = new Map(); - -function normalizeApiName(name: string): string { - return name.toLowerCase().replace(/[<>/()]/g, "").trim(); -} - -function loadCorpusApi(name: string): LoadedCorpusApi | null { - const key = normalizeApiName(name); - if (cachedCorpusApis.has(key)) { - return cachedCorpusApis.get(key) ?? null; - } - - try { - const indexRaw = readFileSync(join(corpusRoot, "index.yaml"), "utf8"); - const index = YAML.parse(indexRaw) as CorpusIndex | null; - const namespaceIndexPath = index?.namespaces?.[corpusNamespace]?.index; - if (!namespaceIndexPath) { - cachedCorpusApis.set(key, null); - return null; - } - - const namespaceRaw = readFileSync(join(corpusRoot, namespaceIndexPath), "utf8"); - const namespaceIndex = YAML.parse(namespaceRaw) as CorpusNamespaceIndex | null; - const apiPath = namespaceIndex?.apis?.[key]?.file; - if (!apiPath) { - cachedCorpusApis.set(key, null); - return null; - } - - const apiRaw = readFileSync(join(corpusRoot, "frontend/motion", apiPath), "utf8"); - const api = YAML.parse(apiRaw) as CorpusApiEntry | null; - if ( - !api || - normalizeApiName(api.name ?? "") !== key || - !api.usage || - !api.importPath || - !api.description || - !api.kind - ) { - cachedCorpusApis.set(key, null); - return null; - } - - const loaded: LoadedCorpusApi = { api, source: corpusNamespace }; - cachedCorpusApis.set(key, loaded); - return loaded; - } catch { - cachedCorpusApis.set(key, null); - return null; - } -} - export function register(server: ToolServer): void { server.tool( "motion_get_api", @@ -97,9 +9,8 @@ export function register(server: ToolServer): void { { name: z.string().describe("API name (e.g., 'motion', 'AnimatePresence', 'useAnimate', 'useScroll', 'stagger', 'Reorder.Group')"), }, - async ({ name }) => { - const corpusEntry = loadCorpusApi(name); - const api = corpusEntry?.api ?? getApiByName(name); + async ({ name }: { name: string }) => { + const api = getApiByName(name); if (!api) { const suggestions = searchApis(name).map((r) => r.api.name); return { @@ -107,15 +18,7 @@ export function register(server: ToolServer): void { isError: true, }; } - const rendered = formatApiReference(api as Parameters[0]); - return { - content: [ - { - type: "text", - text: corpusEntry ? `${rendered}\n**Corpus Source:** ${corpusEntry.source}` : rendered, - }, - ], - }; + return { content: [{ type: "text", text: formatApiReference(api) }] }; }, ); } diff --git a/src/plugins/motion/tools/get-examples.ts b/src/plugins/motion/tools/get-examples.ts index 145da3a..d307e80 100644 --- a/src/plugins/motion/tools/get-examples.ts +++ b/src/plugins/motion/tools/get-examples.ts @@ -1,114 +1,19 @@ import type { ToolServer } from "../../../shared/tool-types.js"; -import { readFileSync } from "node:fs"; -import { dirname, join } from "node:path"; -import { fileURLToPath } from "node:url"; -import YAML from "yaml"; import { z } from "zod"; import { CATEGORIES, getExamplesByCategory, formatExample, capitalize } from "../data.js"; -import type { Example } from "../data.js"; - -type CorpusIndex = { - namespaces?: { - "frontend.motion"?: { - index?: string; - }; - }; -}; - -type CorpusNamespaceIndex = { - namespace?: string; - examples?: Record; -}; - -type CorpusExampleFile = { - category?: string; - examples?: Array<{ - title?: string; - description?: string; - code?: string; - category?: string; - }>; -}; - -type ParsedCorpusExample = { - title: string; - description?: string; - code: string; - category?: string; -}; - -const moduleDir = dirname(fileURLToPath(import.meta.url)); -const corpusRoot = join(moduleDir, "../../../../corpus"); -const corpusNamespace = "frontend.motion"; - -let cachedCorpusExamples: Map | undefined; - -function loadCorpusExamples(category: string): { examples: Example[]; source: string } | null { - const key = category.toLowerCase().trim(); - cachedCorpusExamples ??= new Map(); - const cached = cachedCorpusExamples.get(key); - if (cached !== undefined) { - return cached; - } - - try { - const indexRaw = readFileSync(join(corpusRoot, "index.yaml"), "utf8"); - const index = YAML.parse(indexRaw) as CorpusIndex | null; - const namespaceIndexPath = index?.namespaces?.[corpusNamespace]?.index; - if (!namespaceIndexPath) { - cachedCorpusExamples.set(key, null); - return null; - } - - const namespaceRaw = readFileSync(join(corpusRoot, namespaceIndexPath), "utf8"); - const namespaceIndex = YAML.parse(namespaceRaw) as CorpusNamespaceIndex | null; - const examplePath = namespaceIndex?.examples?.[key]?.file; - if (!examplePath) { - cachedCorpusExamples.set(key, null); - return null; - } - - const fileRaw = readFileSync(join(corpusRoot, "frontend/motion", examplePath), "utf8"); - const file = YAML.parse(fileRaw) as CorpusExampleFile | null; - const examples = (file?.examples ?? []) - .filter((ex): ex is ParsedCorpusExample => Boolean(ex?.title && ex?.code)) - .map((ex) => ({ - title: ex.title, - code: ex.code, - description: ex.description, - category: (ex.category ?? key) as Example["category"], - })); - - if (examples.length === 0) { - cachedCorpusExamples.set(key, null); - return null; - } - - const result = { examples, source: corpusNamespace }; - cachedCorpusExamples.set(key, result); - return result; - } catch { - cachedCorpusExamples.set(key, null); - return null; - } -} export function register(server: ToolServer): void { server.tool( "motion_get_examples", "Get code examples for a specific animation category", { category: z.string().describe(`Category: ${CATEGORIES.join(", ")}`) }, - async ({ category }) => { - const corpusEntry = loadCorpusExamples(category); - const examples = corpusEntry?.examples ?? getExamplesByCategory(category); + async ({ category }: { category: string }) => { + const examples = getExamplesByCategory(category); if (examples.length === 0) { return { content: [{ type: "text", text: `No examples for category "${category}". Available: ${CATEGORIES.join(", ")}` }] }; } let text = `# ${capitalize(category)} Examples\n\n`; for (const ex of examples) text += formatExample(ex, 2); - if (corpusEntry) { - text += `**Corpus Source:** ${corpusEntry.source}\n`; - } return { content: [{ type: "text", text }] }; }, ); diff --git a/src/plugins/react/tools/get-pattern.ts b/src/plugins/react/tools/get-pattern.ts index d9b331b..d4d4bb2 100644 --- a/src/plugins/react/tools/get-pattern.ts +++ b/src/plugins/react/tools/get-pattern.ts @@ -1,102 +1,6 @@ import type { ToolServer } from "../../../shared/tool-types.js"; -import { readFileSync } from "node:fs"; -import { dirname, join } from "node:path"; -import { fileURLToPath } from "node:url"; -import YAML from "yaml"; import { z } from "zod"; -import { PATTERNS, getPatternByName, type Pattern } from "../data.js"; - -type CorpusIndex = { - namespaces?: { - "frontend.react"?: { - index?: string; - }; - }; -}; - -type CorpusNamespaceIndex = { - namespace?: string; - patterns?: Record; -}; - -type CorpusPatternEntry = { - name?: string; - category?: string; - description?: string; - when?: string; - code?: string; - antiPattern?: string; - tips?: string[]; -}; - -type LoadedCorpusPattern = { pattern: Pattern; source: string }; - -const moduleDir = dirname(fileURLToPath(import.meta.url)); -const corpusRoot = join(moduleDir, "../../../../corpus"); -const corpusNamespace = "frontend.react"; - -const cachedCorpusPatterns = new Map(); - -function normalize(name: string): string { - return name.toLowerCase().trim(); -} - -function loadCorpusPattern(name: string): LoadedCorpusPattern | null { - const key = normalize(name); - if (cachedCorpusPatterns.has(key)) { - return cachedCorpusPatterns.get(key) ?? null; - } - - try { - const indexRaw = readFileSync(join(corpusRoot, "index.yaml"), "utf8"); - const index = YAML.parse(indexRaw) as CorpusIndex | null; - const namespaceIndexPath = index?.namespaces?.[corpusNamespace]?.index; - if (!namespaceIndexPath) { - cachedCorpusPatterns.set(key, null); - return null; - } - - const namespaceRaw = readFileSync(join(corpusRoot, namespaceIndexPath), "utf8"); - const namespaceIndex = YAML.parse(namespaceRaw) as CorpusNamespaceIndex | null; - const patternPath = namespaceIndex?.patterns?.[key]?.file; - if (!patternPath) { - cachedCorpusPatterns.set(key, null); - return null; - } - - const raw = readFileSync(join(corpusRoot, "frontend/react", patternPath), "utf8"); - const entry = YAML.parse(raw) as CorpusPatternEntry | null; - if ( - !entry || - normalize(entry.name ?? "") !== key || - !entry.category || - !entry.description || - !entry.when || - !entry.code - ) { - cachedCorpusPatterns.set(key, null); - return null; - } - - const loaded: LoadedCorpusPattern = { - pattern: { - name: entry.name ?? name, - category: entry.category as Pattern["category"], - description: entry.description, - when: entry.when, - code: entry.code, - antiPattern: entry.antiPattern, - tips: entry.tips, - }, - source: corpusNamespace, - }; - cachedCorpusPatterns.set(key, loaded); - return loaded; - } catch { - cachedCorpusPatterns.set(key, null); - return null; - } -} +import { PATTERNS, getPatternByName } from "../data.js"; export function register(server: ToolServer): void { server.tool( @@ -105,9 +9,8 @@ export function register(server: ToolServer): void { { name: z.string().describe("Pattern name (e.g. 'rsc-default', 'state-hierarchy', 'zustand-store', 'suspense-boundary', 'nextjs-metadata', 'composition-pattern', 'component-template')"), }, - async ({ name }) => { - const corpusEntry = loadCorpusPattern(name); - const pattern = corpusEntry?.pattern ?? getPatternByName(name); + async ({ name }: { name: string }) => { + const pattern = getPatternByName(name); if (!pattern) { const available = PATTERNS.map((p) => p.name).join(", "); return { @@ -129,12 +32,7 @@ export function register(server: ToolServer): void { text += `## Tips\n`; for (const tip of pattern.tips) text += `- ${tip}\n`; } - - if (corpusEntry) { - text += `\n**Corpus Source:** ${corpusEntry.source}`; - } - return { content: [{ type: "text", text }] }; - } + }, ); } diff --git a/src/plugins/reactflow/tools/get-api.ts b/src/plugins/reactflow/tools/get-api.ts index 783493b..fe89963 100644 --- a/src/plugins/reactflow/tools/get-api.ts +++ b/src/plugins/reactflow/tools/get-api.ts @@ -1,123 +1,7 @@ import type { ToolServer } from "../../../shared/tool-types.js"; -import { readFileSync } from "node:fs"; -import { dirname, join } from "node:path"; -import { fileURLToPath } from "node:url"; -import YAML from "yaml"; import { z } from "zod"; import { ALL_APIS, searchApis, getApiByName, formatApiReference } from "../data/index.js"; -type CorpusIndex = { - namespaces?: { - "frontend.reactflow"?: { - index?: string; - }; - }; -}; - -type CorpusNamespaceIndex = { - namespace?: string; - apis?: Record; -}; - -type CorpusApiEntry = { - name?: string; - kind?: string; - description?: string; - importPath?: string; - props?: Array<{ name: string; type: string; description: string; default?: string }>; - returns?: string; - usage?: string; - examples?: Array<{ title?: string; code?: string; description?: string; category?: string }>; - tips?: string[]; - relatedApis?: string[]; -}; - -type LoadedCorpusApi = { - api: Parameters[0]; - source: string; -}; - -type ReactFlowExample = Parameters[0]["examples"][number]; - -const moduleDir = dirname(fileURLToPath(import.meta.url)); -const corpusRoot = join(moduleDir, "../../../../corpus"); -const corpusNamespace = "frontend.reactflow"; - -const cachedCorpusApis = new Map(); - -function normalizeApiName(name: string): string { - return name.toLowerCase().replace(/[<>/()]/g, "").trim(); -} - -function loadCorpusApi(name: string): LoadedCorpusApi | null { - const key = normalizeApiName(name); - if (cachedCorpusApis.has(key)) { - return cachedCorpusApis.get(key) ?? null; - } - - try { - const indexRaw = readFileSync(join(corpusRoot, "index.yaml"), "utf8"); - const index = YAML.parse(indexRaw) as CorpusIndex | null; - const namespaceIndexPath = index?.namespaces?.[corpusNamespace]?.index; - if (!namespaceIndexPath) { - cachedCorpusApis.set(key, null); - return null; - } - - const namespaceRaw = readFileSync(join(corpusRoot, namespaceIndexPath), "utf8"); - const namespaceIndex = YAML.parse(namespaceRaw) as CorpusNamespaceIndex | null; - const apiPath = namespaceIndex?.apis?.[key]?.file; - if (!apiPath) { - cachedCorpusApis.set(key, null); - return null; - } - - const apiRaw = readFileSync(join(corpusRoot, "frontend/reactflow", apiPath), "utf8"); - const api = YAML.parse(apiRaw) as CorpusApiEntry | null; - if ( - !api || - normalizeApiName(api.name ?? "") !== key || - !api.usage || - !api.importPath || - !api.description || - !api.kind - ) { - cachedCorpusApis.set(key, null); - return null; - } - - const loaded: LoadedCorpusApi = { - api: { - name: api.name ?? name, - kind: api.kind as Parameters[0]["kind"], - description: api.description, - importPath: api.importPath, - props: api.props, - returns: api.returns, - usage: api.usage, - examples: (api.examples ?? []) - .filter((ex): ex is { title: string; code: string; description?: string; category?: ReactFlowExample["category"] } => - Boolean(ex?.title && ex?.code), - ) - .map((ex) => ({ - title: ex.title, - code: ex.code, - description: ex.description, - category: ex.category ?? "quickstart", - })), - tips: api.tips ?? [], - relatedApis: api.relatedApis ?? [], - }, - source: corpusNamespace, - }; - cachedCorpusApis.set(key, loaded); - return loaded; - } catch { - cachedCorpusApis.set(key, null); - return null; - } -} - export function register(server: ToolServer): void { server.tool( "reactflow_get_api", @@ -129,9 +13,8 @@ export function register(server: ToolServer): void { "API name (e.g., 'ReactFlow', 'useReactFlow', 'Handle', 'addEdge', 'Node', 'Edge', 'NodeProps')", ), }, - async ({ name }) => { - const corpusEntry = loadCorpusApi(name); - const api = corpusEntry?.api ?? getApiByName(name, ALL_APIS); + async ({ name }: { name: string }) => { + const api = getApiByName(name, ALL_APIS); if (!api) { const suggestions = searchApis(name, ALL_APIS) .slice(0, 5) @@ -146,15 +29,7 @@ export function register(server: ToolServer): void { isError: true, }; } - const rendered = formatApiReference(api); - return { - content: [ - { - type: "text", - text: corpusEntry ? `${rendered}\n**Corpus Source:** ${corpusEntry.source}` : rendered, - }, - ], - }; + return { content: [{ type: "text", text: formatApiReference(api) }] }; }, ); } diff --git a/src/plugins/rust/tools/get-practice.ts b/src/plugins/rust/tools/get-practice.ts index 9750dc9..0055670 100644 --- a/src/plugins/rust/tools/get-practice.ts +++ b/src/plugins/rust/tools/get-practice.ts @@ -1,101 +1,6 @@ import type { ToolServer } from "../../../shared/tool-types.js"; -import { readFileSync } from "node:fs"; -import { dirname, join } from "node:path"; -import { fileURLToPath } from "node:url"; -import YAML from "yaml"; import { z } from "zod"; -import { BEST_PRACTICES, type BestPractice } from "../data.js"; - -type CorpusIndex = { - namespaces?: { - "backend.rust"?: { - index?: string; - }; - }; -}; - -type CorpusNamespaceIndex = { - namespace?: string; - practices?: Record; -}; - -type CorpusPracticeEntry = { - name?: string; - chapter?: string; - rule?: string; - reason?: string; - good?: string; - bad?: string; - tips?: string[]; -}; - -type LoadedCorpusPractice = { practice: BestPractice; source: string }; - -const moduleDir = dirname(fileURLToPath(import.meta.url)); -const corpusRoot = join(moduleDir, "../../../../corpus"); -const corpusNamespace = "backend.rust"; - -const cachedCorpusPractices = new Map(); - -function normalize(name: string): string { - return name.toLowerCase().trim(); -} - -function loadCorpusPractice(name: string): LoadedCorpusPractice | null { - const key = normalize(name); - if (cachedCorpusPractices.has(key)) { - return cachedCorpusPractices.get(key) ?? null; - } - - try { - const indexRaw = readFileSync(join(corpusRoot, "index.yaml"), "utf8"); - const index = YAML.parse(indexRaw) as CorpusIndex | null; - const namespaceIndexPath = index?.namespaces?.[corpusNamespace]?.index; - if (!namespaceIndexPath) { - cachedCorpusPractices.set(key, null); - return null; - } - - const namespaceRaw = readFileSync(join(corpusRoot, namespaceIndexPath), "utf8"); - const namespaceIndex = YAML.parse(namespaceRaw) as CorpusNamespaceIndex | null; - const practicePath = namespaceIndex?.practices?.[key]?.file; - if (!practicePath) { - cachedCorpusPractices.set(key, null); - return null; - } - - const raw = readFileSync(join(corpusRoot, "backend/rust", practicePath), "utf8"); - const entry = YAML.parse(raw) as CorpusPracticeEntry | null; - if ( - !entry || - normalize(entry.name ?? "") !== key || - !entry.chapter || - !entry.rule || - !entry.reason - ) { - cachedCorpusPractices.set(key, null); - return null; - } - - const loaded: LoadedCorpusPractice = { - practice: { - name: entry.name ?? name, - chapter: entry.chapter as BestPractice["chapter"], - rule: entry.rule, - reason: entry.reason, - good: entry.good, - bad: entry.bad, - tips: entry.tips, - }, - source: corpusNamespace, - }; - cachedCorpusPractices.set(key, loaded); - return loaded; - } catch { - cachedCorpusPractices.set(key, null); - return null; - } -} +import { BEST_PRACTICES } from "../data.js"; export function register(server: ToolServer): void { server.tool( @@ -104,11 +9,8 @@ export function register(server: ToolServer): void { { name: z.string().describe("Practice name (e.g. 'borrow-over-clone', 'result-not-panic', 'thiserror-vs-anyhow', 'type-state-pattern', 'clippy-command')"), }, - async ({ name }) => { - const corpusEntry = loadCorpusPractice(name); - const practice = - corpusEntry?.practice ?? - BEST_PRACTICES.find((p) => p.name.toLowerCase() === name.toLowerCase()); + async ({ name }: { name: string }) => { + const practice = BEST_PRACTICES.find((p) => p.name.toLowerCase() === name.toLowerCase()); if (!practice) { return { content: [{ type: "text", text: `Practice "${name}" not found.\n\nAvailable: ${BEST_PRACTICES.map((p) => p.name).join(", ")}` }], @@ -125,12 +27,7 @@ export function register(server: ToolServer): void { text += `## Tips\n`; for (const tip of practice.tips) text += `- ${tip}\n`; } - - if (corpusEntry) { - text += `\n**Corpus Source:** ${corpusEntry.source}`; - } - return { content: [{ type: "text", text }] }; - } + }, ); } diff --git a/src/plugins/shadcn/tools/get-component.ts b/src/plugins/shadcn/tools/get-component.ts index f05328c..f0555a8 100644 --- a/src/plugins/shadcn/tools/get-component.ts +++ b/src/plugins/shadcn/tools/get-component.ts @@ -1,111 +1,6 @@ import type { ToolServer } from "../../../shared/tool-types.js"; -import { readFileSync } from "node:fs"; -import { dirname, join } from "node:path"; -import { fileURLToPath } from "node:url"; -import YAML from "yaml"; import { z } from "zod"; -import { getComponentByName, SHADCN_COMPONENTS, type ShadcnComponent } from "../data.js"; - -type CorpusIndex = { - namespaces?: { - "frontend.shadcn"?: { - index?: string; - }; - }; -}; - -type CorpusNamespaceIndex = { - namespace?: string; - components?: Record; -}; - -type CorpusComponentEntry = { - name?: string; - category?: string; - description?: string; - basePrimitive?: string; - dataSlots?: string[]; - variants?: string[]; - sizes?: string[]; - requiresUseClient?: boolean; - usageSnippet?: string; - pairsWith?: string[]; -}; - -type LoadedCorpusComponent = { component: ShadcnComponent; source: string }; - -const moduleDir = dirname(fileURLToPath(import.meta.url)); -const corpusRoot = join(moduleDir, "../../../../corpus"); -const corpusNamespace = "frontend.shadcn"; - -const cachedCorpusComponents = new Map(); - -function normalize(name: string): string { - return name.toLowerCase().trim(); -} - -function loadCorpusComponent(name: string): LoadedCorpusComponent | null { - const key = normalize(name); - if (cachedCorpusComponents.has(key)) { - return cachedCorpusComponents.get(key) ?? null; - } - - try { - const indexRaw = readFileSync(join(corpusRoot, "index.yaml"), "utf8"); - const index = YAML.parse(indexRaw) as CorpusIndex | null; - const namespaceIndexPath = index?.namespaces?.[corpusNamespace]?.index; - if (!namespaceIndexPath) { - cachedCorpusComponents.set(key, null); - return null; - } - - const namespaceRaw = readFileSync(join(corpusRoot, namespaceIndexPath), "utf8"); - const namespaceIndex = YAML.parse(namespaceRaw) as CorpusNamespaceIndex | null; - const componentPath = namespaceIndex?.components?.[key]?.file; - if (!componentPath) { - cachedCorpusComponents.set(key, null); - return null; - } - - const raw = readFileSync(join(corpusRoot, "frontend/shadcn", componentPath), "utf8"); - const entry = YAML.parse(raw) as CorpusComponentEntry | null; - if ( - !entry || - normalize(entry.name ?? "") !== key || - !entry.category || - !entry.description || - !entry.basePrimitive || - !Array.isArray(entry.dataSlots) || - !entry.usageSnippet || - typeof entry.requiresUseClient !== "boolean" || - !Array.isArray(entry.pairsWith) - ) { - cachedCorpusComponents.set(key, null); - return null; - } - - const loaded: LoadedCorpusComponent = { - component: { - name: entry.name ?? name, - category: entry.category as ShadcnComponent["category"], - description: entry.description, - basePrimitive: entry.basePrimitive, - dataSlots: entry.dataSlots, - variants: entry.variants, - sizes: entry.sizes, - requiresUseClient: entry.requiresUseClient, - usageSnippet: entry.usageSnippet, - pairsWith: entry.pairsWith, - }, - source: corpusNamespace, - }; - cachedCorpusComponents.set(key, loaded); - return loaded; - } catch { - cachedCorpusComponents.set(key, null); - return null; - } -} +import { getComponentByName, SHADCN_COMPONENTS } from "../data.js"; export function register(server: ToolServer): void { server.tool( @@ -114,9 +9,8 @@ export function register(server: ToolServer): void { { name: z.string().describe("Component name (e.g., 'Button', 'Dialog', 'Field', 'Select'). Case-insensitive."), }, - async ({ name }) => { - const corpusEntry = loadCorpusComponent(name); - const component = corpusEntry?.component ?? getComponentByName(name); + async ({ name }: { name: string }) => { + const component = getComponentByName(name); if (!component) { const available = SHADCN_COMPONENTS.map((c) => c.name).join(", "); return { @@ -164,11 +58,7 @@ export function register(server: ToolServer): void { text += `- [ ] Props spread (...props) to underlying primitive\n`; text += `- [ ] OKLCH color tokens from design system (not hardcoded hex)\n`; - if (corpusEntry) { - text += `\n**Corpus Source:** ${corpusEntry.source}`; - } - return { content: [{ type: "text" as const, text }] }; - } + }, ); } diff --git a/src/plugins/ui-ux/tools/get-principle.ts b/src/plugins/ui-ux/tools/get-principle.ts index 2853089..f5d8f36 100644 --- a/src/plugins/ui-ux/tools/get-principle.ts +++ b/src/plugins/ui-ux/tools/get-principle.ts @@ -1,101 +1,6 @@ import type { ToolServer } from "../../../shared/tool-types.js"; -import { readFileSync } from "node:fs"; -import { dirname, join } from "node:path"; -import { fileURLToPath } from "node:url"; -import YAML from "yaml"; import { z } from "zod"; -import { PRINCIPLES, type Principle } from "../data.js"; - -type CorpusIndex = { - namespaces?: { - "frontend.ui-ux"?: { - index?: string; - }; - }; -}; - -type CorpusNamespaceIndex = { - namespace?: string; - principles?: Record; -}; - -type CorpusPrincipleEntry = { - name?: string; - domain?: string; - rule?: string; - detail?: string; - examples?: string[]; - antiPatterns?: string[]; - cssExample?: string; -}; - -type LoadedCorpusPrinciple = { principle: Principle; source: string }; - -const moduleDir = dirname(fileURLToPath(import.meta.url)); -const corpusRoot = join(moduleDir, "../../../../corpus"); -const corpusNamespace = "frontend.ui-ux"; - -const cachedCorpusPrinciples = new Map(); - -function normalize(name: string): string { - return name.toLowerCase().trim(); -} - -function loadCorpusPrinciple(name: string): LoadedCorpusPrinciple | null { - const key = normalize(name); - if (cachedCorpusPrinciples.has(key)) { - return cachedCorpusPrinciples.get(key) ?? null; - } - - try { - const indexRaw = readFileSync(join(corpusRoot, "index.yaml"), "utf8"); - const index = YAML.parse(indexRaw) as CorpusIndex | null; - const namespaceIndexPath = index?.namespaces?.[corpusNamespace]?.index; - if (!namespaceIndexPath) { - cachedCorpusPrinciples.set(key, null); - return null; - } - - const namespaceRaw = readFileSync(join(corpusRoot, namespaceIndexPath), "utf8"); - const namespaceIndex = YAML.parse(namespaceRaw) as CorpusNamespaceIndex | null; - const principlePath = namespaceIndex?.principles?.[key]?.file; - if (!principlePath) { - cachedCorpusPrinciples.set(key, null); - return null; - } - - const raw = readFileSync(join(corpusRoot, "frontend/ui-ux", principlePath), "utf8"); - const entry = YAML.parse(raw) as CorpusPrincipleEntry | null; - if ( - !entry || - normalize(entry.name ?? "") !== key || - !entry.domain || - !entry.rule || - !entry.detail - ) { - cachedCorpusPrinciples.set(key, null); - return null; - } - - const loaded: LoadedCorpusPrinciple = { - principle: { - name: entry.name ?? name, - domain: entry.domain as Principle["domain"], - rule: entry.rule, - detail: entry.detail, - examples: entry.examples, - antiPatterns: entry.antiPatterns, - cssExample: entry.cssExample, - }, - source: corpusNamespace, - }; - cachedCorpusPrinciples.set(key, loaded); - return loaded; - } catch { - cachedCorpusPrinciples.set(key, null); - return null; - } -} +import { PRINCIPLES } from "../data.js"; export function register(server: ToolServer): void { server.tool( @@ -104,11 +9,8 @@ export function register(server: ToolServer): void { { name: z.string().describe("Principle name (e.g. 'type-scale', 'wcag-contrast', 'dark-mode-principles', 'touch-targets', 'easing-rules')"), }, - async ({ name }) => { - const corpusEntry = loadCorpusPrinciple(name); - const principle = - corpusEntry?.principle ?? - PRINCIPLES.find((p) => p.name.toLowerCase() === name.toLowerCase()); + async ({ name }: { name: string }) => { + const principle = PRINCIPLES.find((p) => p.name.toLowerCase() === name.toLowerCase()); if (!principle) { const available = PRINCIPLES.map((p) => p.name).join(", "); @@ -136,12 +38,7 @@ export function register(server: ToolServer): void { text += `## Anti-patterns (avoid)\n`; for (const ap of principle.antiPatterns) text += `- ❌ ${ap}\n`; } - - if (corpusEntry) { - text += `\n**Corpus Source:** ${corpusEntry.source}`; - } - return { content: [{ type: "text", text }] }; - } + }, ); } From 1c42b70117b88da70acfb459f743db4c42165f66 Mon Sep 17 00:00:00 2001 From: Kailas Mahavarkar <66670953+KailasMahavarkar@users.noreply.github.com> Date: Wed, 22 Apr 2026 11:09:15 +0530 Subject: [PATCH 5/5] feat: enforce topology on CLI tool invocations via --agent / HYPERSTACK_AGENT Wires the bundle allow/deny policy that topology already declares into the actual tool dispatch path. Tools can now be rejected before any handler runs when the caller is acting as an agent that is not permitted to touch their bundle. Engine: - src/engine/policy.ts adds getBundleForTool which resolves a tool name to a bundle by longest matching tool_prefixes entry and assertToolAllowedForAgent which rejects with a precise error when the bundle is forbidden, not allowed, or the tool is not mapped at all. CLI (src/cli.ts): - rewrites arg parsing with a small flag parser so `--agent ` and `--json '{...}'` can appear in any order after the positional command/tool - accepts HYPERSTACK_AGENT env as fallback for the agent identity - when an agent is supplied, the tool command runs getAgent + assertToolAllowedForAgent before invoking the tool; emits a single [topology] breadcrumb to stderr on success - usage text documents the new enforcement behavior - route and artifact validate paths unchanged Behavior verified: - `hyperstack tool designer_resolve_intent --json '{}'` (no agent) runs permissively (back-compat) - `--agent frontend-builder` on a designer_ tool allows it and logs bundle=frontend.design - `--agent frontend-builder` on a golang_ tool rejects with "forbidden from bundle backend.lang.go" - `--agent backend-builder` on a golang_ tool allows it (bundle=backend.lang.go) - `--agent ghost` errors with "Unknown agent: ghost" - unmapped tool name errors with "not mapped to any bundle" Co-Authored-By: Claude Opus 4.7 (1M context) --- src/cli.ts | 89 ++++++++++++++++++++++++++++++-------------- src/engine/policy.ts | 37 +++++++++++++++++- 2 files changed, 97 insertions(+), 29 deletions(-) diff --git a/src/cli.ts b/src/cli.ts index 6bd102a..f689e45 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -1,16 +1,64 @@ import { validateArtifactPayload } from "./engine/artifact-validator.js"; import { routeRequest } from "./engine/router.js"; import { loadTopology } from "./engine/topology-loader.js"; +import { assertToolAllowedForAgent, getAgent } from "./engine/policy.js"; import { invokeLocalTool } from "./adapters/local-tools/index.js"; +type ParsedArgs = { + positional: string[]; + flags: Record; +}; + +function parseArgs(argv: string[]): ParsedArgs { + const positional: string[] = []; + const flags: Record = {}; + for (let i = 0; i < argv.length; i += 1) { + const token = argv[i]; + if (token.startsWith("--")) { + const name = token.slice(2); + const next = argv[i + 1]; + if (next === undefined || next.startsWith("--")) { + flags[name] = "true"; + } else { + flags[name] = next; + i += 1; + } + } else { + positional.push(token); + } + } + return { positional, flags }; +} + +function usage(): never { + process.stderr.write( + "Usage:\n" + + " hyperstack tool --json '{...}' [--agent ]\n" + + " hyperstack route --json '{...}'\n" + + " hyperstack artifact validate --json '{...}'\n" + + "\n" + + "Topology enforcement:\n" + + " Pass --agent or set HYPERSTACK_AGENT to enforce bundle allow/deny\n" + + " on `tool` calls. When unset, tools run without topology check.\n", + ); + process.exit(1); +} + async function main() { - const [command, toolName, flag, json] = process.argv.slice(2); + const { positional, flags } = parseArgs(process.argv.slice(2)); + const [command, ...rest] = positional; const topology = loadTopology(process.cwd()); if (command === "tool") { - if (!toolName || flag !== "--json" || !json) { - process.stderr.write('Usage: hyperstack tool --json \'{"key":"value"}\'\n'); - process.exit(1); + const toolName = rest[0]; + const json = flags.json; + if (!toolName || !json) usage(); + + const agentId = flags.agent ?? process.env.HYPERSTACK_AGENT; + if (agentId) { + const agent = getAgent(topology, agentId); + const bundle = assertToolAllowedForAgent(topology, agent, toolName); + process.stderr.write(`[topology] agent=${agent.id} bundle=${bundle.id} tool=${toolName}\n`); } const args = JSON.parse(json) as Record; @@ -20,44 +68,29 @@ async function main() { } if (command === "route") { - if (toolName !== "--json" || !flag) { - process.stderr.write("Usage: hyperstack route --json '{...}'\n"); - process.exit(1); - } - - const input = JSON.parse(flag) as { + const json = flags.json; + if (!json) usage(); + const input = JSON.parse(json) as { requestId: string; domainTargets: string[]; capabilityTargets: string[]; workspaceInventory: { projectMode: "greenfield" | "existing"; existingPatterns: string[] }; changeClassification: string; }; - process.stdout.write(`${JSON.stringify(routeRequest(topology, input), null, 2)}\n`); process.exit(0); } - if (command === "artifact" && toolName === "validate") { - const artifactId = flag; - const jsonFlag = process.argv[5]; - const jsonPayload = process.argv[6]; - - if (!artifactId || jsonFlag !== "--json" || !jsonPayload) { - process.stderr.write("Usage: hyperstack artifact validate --json '{...}'\n"); - process.exit(1); - } - - const payload = JSON.parse(jsonPayload) as Record; + if (command === "artifact" && rest[0] === "validate") { + const artifactId = rest[1]; + const json = flags.json; + if (!artifactId || !json) usage(); + const payload = JSON.parse(json) as Record; process.stdout.write(`${JSON.stringify(validateArtifactPayload(topology, artifactId, payload), null, 2)}\n`); process.exit(0); } - process.stderr.write( - "Usage: hyperstack tool --json '{...}'\n" + - " or: hyperstack route --json '{...}'\n" + - " or: hyperstack artifact validate --json '{...}'\n", - ); - process.exit(1); + usage(); } main().catch((error) => { diff --git a/src/engine/policy.ts b/src/engine/policy.ts index 9240827..51031ae 100644 --- a/src/engine/policy.ts +++ b/src/engine/policy.ts @@ -1,4 +1,4 @@ -import type { AgentPolicy, DomainPolicy, LoadedTopology } from "./contracts.js"; +import type { AgentPolicy, BundlePolicy, DomainPolicy, LoadedTopology } from "./contracts.js"; export function getAgent(topology: LoadedTopology, agentId: string): AgentPolicy { const agent = topology.agents.find((entry) => entry.id === agentId); @@ -16,6 +16,41 @@ export function getDomain(topology: LoadedTopology, domainId: string): DomainPol return domain; } +export function getBundleForTool(topology: LoadedTopology, toolName: string): BundlePolicy | null { + const matches = topology.bundles + .filter((bundle) => bundle.toolPrefixes.some((prefix) => toolName.startsWith(prefix))) + .sort((left, right) => { + const leftLen = Math.max(...left.toolPrefixes.filter((p) => toolName.startsWith(p)).map((p) => p.length)); + const rightLen = Math.max(...right.toolPrefixes.filter((p) => toolName.startsWith(p)).map((p) => p.length)); + return rightLen - leftLen; + }); + return matches[0] ?? null; +} + +export function assertToolAllowedForAgent( + topology: LoadedTopology, + agent: AgentPolicy, + toolName: string, +): BundlePolicy { + const bundle = getBundleForTool(topology, toolName); + if (!bundle) { + throw new Error( + `Tool ${toolName} is not mapped to any bundle (no matching tool_prefixes in topology/bundles/*).`, + ); + } + if (agent.forbiddenBundles.includes(bundle.id)) { + throw new Error( + `Agent ${agent.id} is forbidden from bundle ${bundle.id} (tool ${toolName}).`, + ); + } + if (!agent.allowedBundles.includes(bundle.id)) { + throw new Error( + `Agent ${agent.id} is not allowed to call tools in bundle ${bundle.id} (tool ${toolName}). Allowed bundles: ${agent.allowedBundles.join(", ") || "(none)"}.`, + ); + } + return bundle; +} + export function getStrictestProofMode(order: string[], proofModes: string[]): string { const ranked = proofModes .map((mode) => ({ mode, index: order.indexOf(mode) }))