` |
+
+## P2: Touch & Interaction (CRITICAL)
+
+| Rule | Standard | Avoid |
+|---|---|---|
+| `hover-states` | All interactive = visible hover feedback | Silent no-ops |
+| `cursor-pointer` | `cursor: pointer` on all clickable | Clickable divs without cursor |
+| `loading-feedback` | Spinner/skeleton ALL async > 300ms | Frozen UI |
+| `hover-vs-tap` | `@media (hover: hover)` for hover; click/tap for primary | Features only via hover |
+| `disabled-states` | `opacity: 0.5` + `pointer-events: none` + `aria-disabled` | Disabled still clickable |
+| `empty-states` | Headline + copy + CTA, every empty container | Blank screen |
+| `error-feedback` | Inline message + summary at top (identical wording) | "Something went wrong" |
+| `tap-feedback` | Visual feedback < 100ms of tap | No visual response |
+| `gesture-alternative` | Every gesture = visible button equivalent | Gesture-only actions |
+| `safe-area` | Primary targets away from notch, gesture bar, edges | Content under OS chrome |
+
+## P3: Layout & Responsive (HIGH)
+
+| Rule | Standard | Avoid |
+|---|---|---|
+| `mobile-first` | Mobile styles first; override with `min-width` | Desktop-first `max-width` |
+| `breakpoints` | 375 / 768 / 1024 / 1280 / 1440px | Arbitrary device breakpoints |
+| `content-max-width` | `max-width: 1280px` page; `65ch` prose | Full-width paragraphs on ultrawide |
+| `4px-grid` | ALL spacing = multiples of 4px | 11px, 17px, 23px |
+| `spacing-hierarchy` | Space within groups < space between groups | Same padding everywhere |
+| `z-index-scale` | dropdown(1000) sticky(1020) modal(1050) tooltip(1070) toast(1080) | `z-index: 9999` |
+| `aspect-ratio` | `aspect-ratio` or `width`+`height` on all images | Images without dimensions (CLS) |
+| `sticky-offset` | Fixed/sticky nav โ `padding-top: nav-height` on body | Nav covering first section |
+| `viewport-units` | `min-h-dvh` not `100vh` mobile | `100vh` (broken iOS) |
+| `12-col-grid` | 12 col, 24px gutters desktop, 16px mobile, 48-64px margins | No grid system |
+| `no-horizontal-scroll` | Content fits viewport width | Horizontal scroll mobile |
+
+## P4: Typography (HIGH)
+
+| Rule | Standard | Avoid |
+|---|---|---|
+| `type-scale` | Ratio: 1.25 compact / 1.333 standard / 1.414 dramatic | Random sizes (17px, 23px) |
+| `body-fixed` | Body 16px fixed; NEVER fluid `clamp()` | `clamp()` on body (causes reflow) |
+| `heading-fluid` | Headings use `clamp()` | Fixed heading sizes |
+| `line-height-invert` | Display 1.05-1.15; Heading 1.2-1.3; Body 1.5 app / 1.6-1.75 prose | Same lh everywhere |
+| `tracking` | Headings -0.01 to -0.03em; Body 0; Overlines +0.06 to +0.10em | Negative tracking on body |
+| `weight-contrast` | Heading 600-800, body 400 | `font-weight: 500` everywhere |
+| `max-2-families` | Sans + mono for apps; serif + sans for editorial | 3+ families |
+| `prose-width` | `max-width: 65ch` on body text | Full-width text |
+| `fallback-stack` | `ui-sans-serif, system-ui, sans-serif` always | Missing fallbacks |
+| `readable-mobile` | 16px min body mobile (avoids iOS auto-zoom) | 14px body mobile |
+
+## P5: Color System (HIGH)
+
+| Rule | Standard | Avoid |
+|---|---|---|
+| `oklch` | OKLCH for all design tokens | Hex/HSL for design systems |
+| `no-pure-bw` | Near-black `oklch(0.12-0.15 0.005 H)` + near-white `oklch(0.97-0.99 0.005 H)` | Pure #000 on #FFF |
+| `warm-cool-commit` | Commit to warm OR cool neutrals; never mix | Warm bg + cool borders |
+| `semantic-tokens` | 3-layer: primitive ramps โ semantic tokens โ Tailwind bridge | Raw hex in components |
+| `dark-mode` | Redesign, don't invert. Warm charcoal bg. Lighter surfaces per elevation. | Inverting light mode |
+| `status-colors` | Success/error/warning/info each with solid + soft variant; L ~0.55 for AA | Same color for both variants |
+| `no-ai-purple` | Custom brand hue, not #6366F1 | AI purple as unconscious default |
+| `chart-tokens` | Dedicated `--chart-*` tokens separate from `--primary` | Reusing brand for charts |
+| `shadow-tint` | Warm-tinted oklch shadows, never `rgba(0,0,0,...)` | Cold black shadows |
+| `dark-elevation` | Progressively lighter bg per level | Box shadows in dark mode |
+| `color-dark-mode-test` | Test contrast separately in dark mode | One-mode testing |
+
+## P6: Motion & Animation (MEDIUM)
+
+| Rule | Standard | Avoid |
+|---|---|---|
+| `duration-scale` | 100-150ms hover; 200ms default; 300ms panel; 400-500ms complex | > 500ms UI transitions |
+| `exit-faster` | Exit 50-100ms shorter than enter | Same duration enter/exit |
+| `easing` | ease-out (enter), ease-in (exit), ease-in-out (reposition) | Linear easing |
+| `gpu-only` | Animate only `transform` and `opacity` | width/height/top/left |
+| `no-decorative` | Animation must serve information | `animate-bounce` on static icons |
+| `max-2-animated` | Max 2 animated elements per viewport | 6 elements animating on load |
+| `spring-context` | Spring/bounce only for playful/consumer | Bounce in enterprise |
+| `interruptible` | User gesture cancels in-progress animation | Blocked input during animation |
+| `stagger-sequence` | Stagger list entrance 30-50ms per item | All items appear simultaneously |
+
+## P7: Components (MEDIUM)
+
+| Rule | Standard | Avoid |
+|---|---|---|
+| `all-states` | Every component: default, hover, active, focus, disabled, loading | Missing states |
+| `button-loading` | Spinner replaces text, same width (no CLS) | Width changing |
+| `input-labels` | Visible persistent label above input | Placeholder-as-label |
+| `input-validation` | Validate on blur; real-time only when correcting | Validating on focus |
+| `password-toggle` | Show/hide with text label; never disable paste | Paste disabled |
+| `empty-state` | Headline + copy + single CTA | "No data" |
+| `icon-system` | SVG icons (Lucide/Heroicons/Phosphor), never emojis | Emojis as icons |
+| `confirmation` | Title = action question; buttons = specific verbs | "Are you sure?" / "OK" |
+| `error-messages` | What happened + why + how to fix; never blame user | "Error 403" |
+| `nav-active` | Current page visually indicated | No active indicator |
+| `consistent-radius` | Same radius per component type | Mixing 4px and 24px |
+| `elevation-consistent` | Consistent shadow/elevation scale | Random shadow values |
+
+## P8: UX Writing (MEDIUM)
+
+| Rule | Standard | Avoid |
+|---|---|---|
+| `button-labels` | Verb + object: "Download report", "Add to cart" | "Submit", "Click here", "OK" |
+| `first-person-cta` | "Start my trial" > "Start your trial" (+90% ContentVerve) | Second-person CTAs |
+| `single-cta` | One primary CTA per view (+266% conversions) | Multiple competing CTAs |
+| `plain-language` | Grade 4-6, max 25 words/sentence | "Purchase", "Commence" |
+| `sentence-case` | Sentence case headings | Title Case Everywhere |
+| `specific-headlines` | "3 items in your cart" | "Almost there!" |
+| `loading-copy` | < 2s spinner; 2-10s "Saving..."; 10s+ progressive | "Loading..." for everything |
+| `no-blame` | "That password doesn't match" | "You entered wrong password" |
+| `no-clear-on-error` | Preserve all user input on error | Clearing form on error |
+| `inclusive-language` | "select" not "click"; "they/their"; "primary/subordinate" | "click", "master/slave" |
+
+## P9: Landing Page Conversion (MEDIUM)
+
+| Rule | Evidence | Standard |
+|---|---|---|
+| `hero-3sec` | NNG: 57% viewing time above fold | Value prop in 3 seconds |
+| `false-floor` | NNG: 102% more views above fold | Bleed 40-80px of next section |
+| `section-order` | Unbounce 41K pages | HeroโProofโProblemโFeaturesโTestimonialsโPricingโFAQโCTAโFooter |
+| `social-proof` | TrustRadius: 30-70% lift | Named metrics > logos > badges |
+| `pricing-3tier` | ProfitWell 12K SaaS | 3 tiers, highlight middle, expensive first |
+| `trust-signals` | 61% didn't buy without seals | Trust signal near every CTA |
+| `page-speed` | Google: 1sโ3s = +32% bounce | Sub-2s; AVIF > WebP > JPEG |
+| `cta-above-fold` | HubSpot 40K pages: +30% | CTA above fold, repeated after sections |
+| `grade-5-7` | Unbounce: +56% vs grade 8-9 | Copy at grade 5-7 |
+| `form-fields` | Baymard: -4-6% per field beyond 8 | Max 7-8 fields |
+| `mobile-cta` | 82.9% mobile traffic | Sticky CTA bar, thumb-zone placement |
+
+## P10: Charts & Data (LOW)
+
+| Rule | Standard | Avoid |
+|---|---|---|
+| `right-align-numbers` | Right-align quantitative data in monospace | Left-aligned numbers |
+| `table-row-hover` | Subtle bg highlight on row hover | No row tracking |
+| `table-pagination` | Pagination > infinite scroll | Infinite scroll on tables |
+| `fixed-headers` | Freeze row/col headers during scroll | Scrolling headers |
+| `no-rainbow` | Viridis/Okabe-Ito; max 8 categories | Rainbow colormap |
+| `data-ink` | Erase elements without info loss (Tufte) | Gridlines dominating data |
+| `chart-a11y` | Color + pattern/shape; provide table alternative | Color-only encoding |
---
-# PHASE 4: GENERATE DESIGN.md (Conditional)
+# PERSONALITY ATLAS
+
+> Source: 58 real company design systems. Design = communication before aesthetics.
+
+```
+Layer 1: Personality โ what does product say about itself?
+Layer 2: Language โ what visual vocabulary carries that?
+Layer 3: Rules โ what constraints enforce it consistently?
+```
+
+## 1. Premium Precision
+**Communicates:** "Engineered by people who care about every pixel."
+**Exemplars:** Linear (tight type -0.03em, 3-variable color, opacity hierarchy), Vercel (black+white, Geist -0.04em, 96-128px sections), Apple (SF Pro 44pt targets, Clarity/Deference/Depth), Stripe (weight 300/500, CIELAB 5-step = 4.5:1)
+
+| Property | Value | Why |
+|---|---|---|
+| Colors | Mono + single accent | Multiple colors compete. One directs. |
+| Typography | -0.02 to -0.03em headings, weight 300-600 | Tight = deliberate. Low weight = restraint. |
+| Radius | 0-8px | Sharp = engineered. |
+| Shadows | None or ultra-subtle | Precision demands lightness. |
+| Motion | 200-250ms, ease-out, no bounce | Motion serves info, not personality. |
+| Density | Normal | Balanced. |
+| Default | Light | Content visibility. |
+
+**Use:** SaaS, dev tools (light), docs, B2B. **Never:** Children's, gaming, playful.
+
+## 2. Technical Developer
+**Communicates:** "Built by developers, for developers."
+**Exemplars:** Supabase (dark emerald, code-first), Warp (IDE-like, terminal IS product), Cursor (sleek dark, keyboard-first), Raycast (command palette is entire UX, every action < 100ms)
+
+| Property | Value | Why |
+|---|---|---|
+| Colors | Dark bg (oklch 0.08-0.14), green/cyan accents | Dark reduces strain. Green/cyan = terminal convention. |
+| Typography | Monospace for data, sans for UI chrome | Monospace = data. Sans = chrome. That IS hierarchy. |
+| Radius | 4-8px | Functional middle. |
+| Shadows | Glow on accent (0 0 20px oklch(accent / 0.15)) | Box shadows wrong on dark. Glow feels native. |
+| Motion | 100-200ms, snappy | Devs notice 50ms lag. |
+| Density | Compact (14px body, 48px sections, 20px cards) | Info density = feature. |
+| Default | Dark | 8+ hours in dark IDEs. |
+
+**Critical:** Command palette (Cmd+K) NOT optional for 50+ feature apps. Keyboard shortcuts on every action. Code blocks: syntax highlighting + copy button.
+
+**Use:** Dev tools, CLIs, APIs, editors. **Never:** Consumer, marketing, healthcare, government.
+
+## 3. Warm Editorial
+**Communicates:** "Reading this should feel like opening a well-made book."
+**Exemplars:** Notion (serif headings, cream bg, editor disappears), Airbnb (warm coral #FF5A5F, photography-driven, rounded-xl), Medium (serif body, 1.75lh)
+
+| Property | Value | Why |
+|---|---|---|
+| Colors | Warm-tinted: oklch(0.98 0.012 78) not oklch(0.98 0 0) | 0.012 chroma at hue 78 = "premium notebook" not "cold SaaS". Highest-leverage single decision. |
+| Typography | Serif or humanist sans, 18px body, 1.6-1.75 lh | Serif = trust. Larger body + generous leading = comfort. |
+| Radius | 12-20px | Rounded = friendly (Gestalt). Cap 20px - beyond = childish. |
+| Shadows | Warm-tinted (oklch(0.22 0.006 56 / 0.06)) | Cold rgba shadows disconnected on warm surfaces. |
+| Motion | 200-300ms, ease-in-out, gentle | Spring/bounce = tonally wrong. |
+| Density | Comfortable (96px sections, 40px cards, 18px body) | Reading requires breathing room. |
+| Default | Light | Paper metaphor. |
+
+**Use:** Content, editorial, consumer, hospitality. **Never:** Dev tools, data-dense, fintech.
+
+## 4. Bold Energetic
+**Communicates:** "We're confident and not afraid to stand out."
+**Exemplars:** Figma (multi-color, each feature = own color), Framer (bold black + blue, motion-first), PostHog (playful dark, hedgehog branding)
+
+| Property | Value | Why |
+|---|---|---|
+| Colors | 4-6 vivid, complementary/triadic, C 0.15+ | Energy requires contrast. Mono kills it. Cap 6 = chaos limit. |
+| Typography | Display 700-900, 32px+, -0.02 to -0.03em | Large heavy type demands attention. That's the point. |
+| Radius | 12-16px | Confident. Not sharp (cold) or super-round (childish). |
+| Shadows | Colored (0 8px 24px oklch(brand / 0.10)) | Colored shadows reference brand. More expressive. |
+| Motion | 200-400ms, spring (cubic-bezier(0.34, 1.56, 0.64, 1)) | Bold = bold motion. Under 400ms for UI. |
+| Default | Light | Bold colors need light contrast. Dark mutes vibrancy. |
-Only run this phase when route classification says `requires_design_contract = true`.
+**Use:** Creative tools, startups, social, youth. **Never:** Banking, healthcare, government, B2B.
-Assemble 10 sections. Wait for user approval. NO CODE YET.
-1. Visual Theme (Emotional target, personality).
-2. Color Palette (OKLCH, semantic, dark mode).
-3. Typography (Scale, pairing).
-4. Spacing (Grid, density, content width).
-5. Component Specs (Variants + states).
-6. Motion (Durations, easing, reduced-motion).
-7. Elevation (Shadow tokens, z-index).
-8. Do's & Don'ts (Industry-traced).
-9. Responsive (375/768/1024/1280/1440px targets).
-10. Anti-Patterns (Slop checks passed).
+## 5. Cinematic Dark
+**Communicates:** "This is an experience, not a tool."
+**Exemplars:** ElevenLabs (audio waveform aesthetics define visual language), RunwayML (AI content IS hero, UI = invisible framing), SpaceX (full-bleed mission photography, futuristic through restraint)
+
+| Property | Value | Why |
+|---|---|---|
+| Colors | Near-black (oklch 0.05-0.10), single neon accent | True black = 60% OLED power savings at 100% brightness. Single accent = Von Restorff focal. |
+| Typography | Light weight 300-400 at large display, tight tracking | Light on dark = ethereal. Heavy on dark = oppressive. |
+| Radius | 0-6px | Sharp = futuristic. Rounded adds warmth that conflicts. |
+| Shadows | Glow (0 0 40px oklch(accent / 0.20)), no box-shadow | Shadows invisible on dark. Glow = native. |
+| Motion | 400-600ms cinematic, parallax, ease-in-out | Speed destroys cinematic feel. ONLY personality where 500ms+ OK for reveals. |
+| Density | Comfortable (120px sections) | Dense breaks immersive spell. Each section = "scene." |
+| Default | Dark | Personality IS darkness. Light mode may not be appropriate. |
+
+**Use:** Media, entertainment, gaming, music, AI creative. **Never:** Productivity, forms-heavy, government, healthcare.
+
+## 6. Enterprise Trust
+**Communicates:** "Your data is safe with us."
+**Exemplars:** IBM Carbon (every component ships WCAG 2.1 AA, 3:1 focus ring), Coinbase (institutional blue, says "bank" not "startup"), Salesforce (Lightning system, complexity through structure)
+
+| Property | Value | Why |
+|---|---|---|
+| Colors | Blue/navy (oklch 0.45-0.55 0.15-0.18 240-260), 1 brand + 1 accent | Blue = most cross-culturally consistent trust signal (Frontiers in Psychology, Goldstein 1942). |
+| Typography | Formal sans (IBM Plex, Inter), 400 body, 600 heading | No serif (editorial), no display weight (startup). Moderate = structured. |
+| Radius | 4-8px | Professional. 0px = brutalist. 12px+ = consumer. |
+| Motion | 150-200ms, ease-out | 8+ hour workday. Decorative motion = distraction. |
+| Default | Light | Institutional convention. |
+
+**Trust signals required:** SOC2/ISO badges, uptime guarantees, SSO/SAML, audit logs, RBAC visible.
+
+**Use:** B2B, fintech, healthcare management, gov tech. **Never:** Consumer, creative, gaming.
+
+## Choosing Personality
+
+Industry ร user type ร emotional target. Signals conflict โ **industry wins** (harder constraint).
+
+| Emotional Target | Maps To |
+|---|---|
+| Trustworthy | enterprise-trust |
+| Playful | bold-energetic |
+| Premium | premium-precision |
+| Energetic | bold-energetic |
+| Calm | warm-editorial |
+| Technical | technical-developer |
+| Bold | bold-energetic |
+| Editorial | warm-editorial |
+
+---
+
+# INDUSTRY RULES
+
+> Source: ui-ux-pro-max (161 rules), awesome-design-md (58 companies).
+
+| Industry | Style | Color | Must | Never | Notes |
+|---|---|---|---|---|---|
+| SaaS | Soft UI | Trust blue + accent | Hover transitions, empty states, onboarding checklist (3-5 items) | Excessive animation, AI purple, > 2 accents | |
+| Analytics/Dashboard | Minimalism | Neutral + Okabe-Ito/viridis | Data export, filtering, sortable tables, keyboard nav, monospace numbers, right-align quantities | Ornate design, rainbow colormaps | |
+| Healthcare | Soft UI | Calm blue + green, C < 0.12 | **WCAG AAA**, 48px+ targets, crisis resources | Neon, motion-heavy, < 14px text, gamification w/o clinical validation | HIPAA: no PHI in URLs, session timeouts |
+| Fintech | Minimalism | Navy + blue + gold | Security signals, trust badges near monetary actions, locale-aware number formatting, audit trail | Playful design, AI purple, unclear fees (FTC risk), animations on transactions | |
+| Creative Agency | Vibrant Block | Bold, high chroma | Case studies, full-bleed imagery, scroll animations, portfolio grid | Corporate minimalism, stock photos, template layouts | |
+| Developer Tool | Dark OLED | Dark + emerald/cyan | Code examples, keyboard shortcuts, Cmd+K, syntax highlighting, copy buttons | Heavy chrome, poor keyboard support, forced light default | Docs quality IS product quality |
+| AI/Chatbot | Minimalism | Neutral + one accent (NOT #6366F1) | Streaming text < 100ms first token, typing indicators, code blocks + copy | Heavy chrome, AI purple default, anthropomorphized persona | |
+| E-commerce (Luxury) | Minimalism | Black + gold | Product photography as hero, trust signals, size guides, guest checkout (26% abandon if forced account) | Block-based colorful, cheap discount badges | "complimentary shipping" not "FREE SHIPPING!!!" |
+| Government | Minimalism | High contrast, institutional | **WCAG AAA**, skip links, grade 4-6 language, breadcrumbs, multi-language, print-friendly | Ornate, motion, decorative images, jargon | |
+| Mental Health | Soft UI | Muted pastels, warm | Calm aesthetics, privacy-first, breathing room, crisis resources every screen | Neon, gamification, social pressure, dark default, aggressive notifications | |
+| Education | Soft UI / Clay | Friendly pastels | Progress indicators, achievement feedback, clear nav, full a11y | Complex layouts, dense tables, dark default, small text | |
+| Wellness | Soft UI | Earth tones, sage, coral | Calm, breathing space, warm photography, gentle CTAs | Dark default, aggressive motion, neon, dense layouts | |
+
+For full detail: `designer_get_industry_rules(industry)`
+
+---
+
+# COGNITIVE LAWS (11)
+
+> Source: Laws of UX, NNG, Smashing Magazine. Not opinions - empirically documented.
+
+## Fitts' Law
+`T = a + b * log2(2D/W)` - Halving distance > doubling size.
+- Touch targets โฅ 44px (WCAG) / 48px (Material). Screen edges = infinite mouse targets, hardest for touch.
+- Submit CTA at bottom of form (pointer already near last field). Destructive actions โฅ 8px from safe.
+- **Violations:** Icon-only buttons (16px visual โ 44px target), Delete beside Save < 8px gap.
+
+## Hick's Law
+`RT = a + b * log2(n + 1)` - 2โ4 choices costs more than 20โ22. First added choices most expensive.
+- Minimize choices at irreversible points. Wizard pattern for multi-step. Surface recommended option.
+- Applies to decisions, not recognition. 15-item nav with clear categories = fine. 15-option modal = not.
+- **Violations:** All features on first login, pricing with no highlighted plan, 50+ unsorted dropdowns.
+
+## Miller's Law
+7 ยฑ2 chunks (Cowan 2001: 4 ยฑ1 without rehearsal). Unit = chunk, not item.
+- "(919) 555-2743" = 3 chunks vs "9195552743" = 10 items. 73% lower cognitive load.
+- Auto-chunk numbers, max 7 nav items, group forms โค 5 fields/section.
+- **Violations:** Flat 12+ item nav, unbroken reference codes, 20+ ungrouped settings.
+
+## Gestalt Principles
+**Proximity > Similarity > Closure > Continuity > Common Fate**
+- **Proximity:** Label-input gap 4-8px < input-to-next-label 16-24px. Equal spacing = invisible structure.
+- **Similarity:** All links share treatment. Primary โ secondary button weight.
+- **Closure:** Partial card at viewport bottom = scroll hook. Skeleton screens exploit this.
+- **Continuity:** Vertical form alignment. Zigzag layouts break scanning (NNG confirmed).
+- **Common Fate:** Skeleton placeholders pulse at identical timing.
+
+## Von Restorff Effect
+ONE highlighted element per view. Multiple highlights cancel.
+- One CTA. One recommended pricing plan. Red dot badge = only red in monochromatic UI.
+- **Violations:** Multiple "highlighted" CTAs, 6 elements animating on load.
+
+## Serial Position Effect
+First (primacy) and last (recency) remembered most. Middle forgotten.
+- Nav: important at edges. Pricing: recommended first or last. Onboarding: aha moment early, friction last.
+- Form fields: easy first (name, email), hard middle (tax ID).
+
+## F-Pattern (NNG: 232 users, 1.5M fixations)
+Dense text โ horizontal top sweep โ shorter lower sweep โ vertical left scan. Right side = near-zero. 80% viewing time on left half.
+- Front-load sentences. "Billing settings" not "Settings for billing". H2/H3 every 200-300 words.
+
+## Z-Pattern
+Sparse visual pages (< 50 words). Logo top-left โ trust top-right โ diagonal โ **CTA bottom-right** (terminal point).
+
+## Jakob's Law
+Users prefer sites to work like other sites. Radio = single, checkbox = multi, toggle = binary. Blue underline = link (-10-15% clicks without it). Cart top-right. Redesign: preview + opt-in + revert.
+
+## Doherty Threshold (IBM 1982)
+< 100ms instantaneous. 100-400ms immediate. **> 400ms flow breaks.** > 1s abandonment. > 10s gone.
+- Optimistic UI. Skeleton > spinner. Autocomplete < 300ms. Keystroke-to-display < 50ms.
+
+## Peak-End Rule (Kahneman 1993)
+Remembered = avg(peak intensity + final moment). Duration neglect.
+- Order confirmation = peak AND end (Mailchimp "High Five"). Onboarding completion = peak. Error recovery = peak management (Stripe specific messages). No-friction cancellation > dark-pattern 5-step.
+
+**Cross-law interactions:**
+- Hick + Serial Position on nav: fewer items + important at edges
+- Fitts + Von Restorff on CTAs: large + visually isolated = compound positive
+- Doherty + Peak-End: design loading reveal as positive peak
+- Hick vs disclosure: < 20% usage = hide; 80%+ = visible
+
+---
+
+# VISUAL COMPOSITION
+
+> Source: NNG (1.5M fixations, 57,453 fold fixations), A List Apart, Smashing Magazine
+
+## Visual Hierarchy - 6 Levers (priority order)
+
+1. **Size** - Bigger = more important. Max 3 size variations. Never equal size for different priorities.
+2. **Contrast** - Squint test: blur 5-10px, primary action must be first visible.
+3. **Color** - Warm/saturated advances, cool/muted recedes. Red = destructive only.
+4. **Typography** - Weight is primary signal. Everything emphasized = nothing emphasized.
+5. **Spacing** - More surrounding space = more attention. Use before borders/fills.
+6. **Position** - Top-left = most attention. Optical center above mathematical center.
+
+## CRAP Principles
+- **Contrast:** If different, make VERY different. Same-same = no hierarchy.
+- **Repetition:** All links same treatment. Consistency = implicit rules.
+- **Alignment:** Left-align = simplest valid system. Never center multi-line paragraphs.
+- **Proximity:** Label-input 4-8px. Form groups 24-32px. Sections 48-96px. Same gap everywhere = invisible structure.
+
+## Whitespace
+**Micro** (tracking, leading, label-input): governs readability. **Macro** (sections, margins, hero): governs perceived value. Luxury = generous macro. Crowded = "affordable." Active whitespace = spotlight (Apple product isolation). Passive = structural.
+
+## The Fold (NNG: 57,453 fixations)
+- Above fold: **102% more views**. 57% viewing time above fold.
+- **Never fill exact viewport height** - bleed 40-80px of next section. False floor = users think page done.
+- First 100px must be relevant or users leave.
+
+## Reading Patterns
+- **F-Pattern:** Dense text. Right side = zero attention. Front-load sentences.
+- **Layer-Cake:** Most effective. Headings = summaries not labels.
+- **Z-Pattern:** Sparse visual < 50 words. CTA at terminal point (bottom-right).
+- **Spotted:** Specific targets (prices, dates). Distinct visual treatment.
+
+## Grids
+12-column: divides by 1/2/3/4/6/12. Desktop 24px gutters, mobile 16px, margins 48-64px / 20px.
+Baseline: 8px rhythm. Line heights = multiples (16, 24, 32, 40px).
+Full 12/12 (hero), wide 10/12, standard 8/12 (forms), narrow 6/12, sidebar 4/12.
+
+## Optical Alignment
+Mathematical โ optical. Icons in buttons: 1-2px up. Text in buttons: more bottom padding. Hero headlines: raise 5-8% above mathematical center. Circles must be physically larger than squares to appear equal.
+
+---
+
+# UX WRITING
+
+> Source: Mailchimp, NNG, GOV.UK, Copyhackers, Microsoft.
+
+## Button Labels
+- "Start **my** trial" > "Start **your** trial" (+90% ContentVerve). First-person = endowment effect.
+- Verb + object: "Download report", "Add to cart". Single CTA per screen (+266%).
+- Front-load verb. "No credit card required" near CTA (+34%).
+- **Never:** "Click here", "Submit", "OK", "Yes/No", "Learn More" as primary.
+
+## Voice & Tone (Mailchimp)
+Voice (constant): plainspoken, genuine, dry humor. Tone (variable): adjust to reader's state. "Clear > clever, always." Active voice.
+
+## Plain Language (GOV.UK)
+Reading age 9. Max 25 words/sentence. buy not purchase, help not assist, start not commence. 80% prefer clear English. Block caps 13-18% harder to read.
+
+## Error Messages (NNG + GOV.UK + Stripe)
+**What happened + Why + How to fix.** Never blame user. Never clear input. Inline + summary (identical wording). Validate on blur.
+- Good: "That email is already registered. Try signing in."
+- Stripe: "Your bank declined. No payment was made. Try a different card."
+
+## Placeholder Text (NNG)
+**Never as label** (disappears on focus, memory burden). Acceptable: format hints only ("MM/DD/YYYY").
+
+## Confirmation Dialogs
+Title = action question ("Delete this project?"). Body = consequences ("12,847 subscribers. Cannot be undone."). Buttons = specific verbs ("Delete project" / "Keep project"). Destructive: red button, focus on Cancel, max distance between buttons.
+
+## Empty States
+Every empty state = onboarding moment. Headline + copy + CTA. First-use: encouraging. Cleared: congratulatory. No results: helpful path. Never blank.
+
+## Loading States
+< 2s: spinner only. 2-10s: "Saving..." 10s+: "Uploading (3 of 12)..." Present participle.
+
+---
+
+# DESIGN MASTERS - 5 CONVERGENCE POINTS
+
+> 7 masters (Rams, Norman, Vignelli, Spiekermann, Ive, Tufte, Kare) converged independently.
+
+## Rams: 10 Principles
+Innovative (remove friction, not add spectacle), Useful (function + psychology + aesthetics), Aesthetic (integral to usefulness), Understandable (zero-onboarding), Unobtrusive (canvas disappears), Honest (no dark patterns), Long-lasting (avoids fashion), Thorough (404 pages, empty states = design surfaces), **As little design as possible.**
+
+## Norman: 6 Principles
+**Signifiers** (blue underline = link), **Mapping** (controls match results), **Feedback** (< 100ms, calibrated), **Constraints** (define what's impossible), **Conceptual Models** (user mental model must match design). Two Gulfs: Execution (can I do it?) and Evaluation (did it work?). Good design narrows both.
+
+## Vignelli: Grid + Type Discipline
+Max 2 type sizes per screen. 2x ratio minimum for hierarchy. "White space > black of type." Grid removes arbitrary placement.
+
+## Spiekermann: Type = Brand
+Custom typeface = purchased differentiation. Open apertures for screen legibility. Size and tracking are inverse.
+
+## Tufte: Data-Ink Ratio
+Maximize toward 1.0. "Can this be erased without info loss?" Lie Factor = effect shown / effect in data (1.0 = honest). Small multiples. Sparklines.
+
+## Ive/Apple: Simplicity = Purpose
+"Simplicity not absence of clutter - that's a consequence." Clarity (transparent carrier), Deference (UI steps back), Depth (spring physics = signifiers).
+
+## Kare: Icons as Universal Language
+Meaningful (real metaphors), Memorable (one-trial learning), Clear (traffic sign test).
+
+## The 5 Convergences
+
+| # | Principle | Implication |
+|---|---|---|
+| 1 | Reduction is hardest work | Every element earns place. Default = removal. |
+| 2 | Constraints enable, not limit | Type scale, spacing grid, palette = preconditions for quality. |
+| 3 | Timelessness over trend | Build for structure. Glassmorphism fades. Good typography permanent. |
+| 4 | Function precedes form, but form not optional | Poor hierarchy = harder to use, not just ugly. |
+| 5 | Design communicates trust | No blame, no manipulation, no lying with graphics. |
+
+---
+
+# AI SLOP FINGERPRINT
+
+ANY present โ go back to Phase 3:
+
+| # | Pattern | Fix |
+|---|---|---|
+| 1 | `#6366F1` gradient (AI purple) | Custom brand hue in OKLCH |
+| 2 | Grid of identical cards, identical padding | Vary sizes, bento/asymmetric |
+| 3 | `font-weight: 500` everywhere | 700+ heading, 400 body |
+| 4 | `#F9FAFB` background (cold grey) | oklch(0.98 0.008-0.015 60-80) |
+| 5 | Missing states | Design ALL states |
+| 6 | `animate-bounce`/`pulse` on static | Animation for loading/state changes only |
+| 7 | 3+ font families | Max 2 (sans + mono) |
+| 8 | Line-height 1.75 on app UI | 1.5 for app; 1.75 prose only |
+| 9 | Absolute/relative from Figma MCP | Solve via flex/grid |
+| 10 | `rgba(0,0,0,...)` shadows | oklch-tinted warm shadows |
+| 11 | Same color for UI AND charts | Separate `--chart-*` tokens |
+| 12 | No `prefers-reduced-motion` | Media query with `!important` |
+| 13 | Pure #000 on #FFF | Near-black on tinted white |
+| 14 | `outline: none` without replacement | 2px ring, 2px offset |
+| 15 | Touch targets < 44px | Pad to 44px minimum |
+
+---
+
+# PHASE 4: GENERATE DESIGN.md
+
+Assemble all decisions into 10-section DESIGN.md. See [template](references/design-md-template.md). See [examples](examples/) for worked outputs.
+
+```
+1. Visual Theme & Atmosphere - emotional target, personality, system inspiration, identity
+2. Color Palette - brand ramp (OKLCH 11 stops), semantic tokens, dark mode strategy
+3. Typography - scale table, font pairing + rationale, fluid vs fixed
+4. Spacing - semantic tokens, density, grid (12-col), content max-width
+5. Component Specs - button/input/card/nav with ALL variants + ALL states
+6. Motion - duration scale, easing rules, prefers-reduced-motion strategy
+7. Elevation - shadow system (light), bg-color elevation (dark), z-index scale
+8. Do's and Don'ts - 10 rules for THIS product, each traced to evidence
+9. Responsive - behavior at 375/768/1024/1280/1440px
+10. Anti-Patterns - industry violations, AI slop checks this design passes
+```
+
+**Present to user. Wait for approval. No code until approved.**
---
# PHASE 5: CODE GENERATION
-Post-approval implementation order:
-1. CSS custom properties (`:root`) โ `design_tokens_generate`.
-2. Base layout โ grid + spacing.
-3. Typography.
-4. Components (ALL states).
-5. Motion (After layout).
-6. Responsive.
-7. Accessibility (Focus, ARIA).
-8. Final audit.
+After DESIGN.md approved:
+
+1. CSS custom properties (`:root` tokens) โ call `design_tokens_generate`
+2. Base layout โ grid + spacing
+3. Typography โ apply scale
+4. Components โ ALL states (default, hover, active, focus, disabled, loading)
+5. Motion โ after layout correct
+6. Responsive โ mobile-first
+7. Accessibility โ focus, ARIA, touch targets
+8. Final audit โ anti-pattern checklist + pre-delivery checks
+
+---
+
+# QUALITY GATES (5)
+
+| Gate | Test | Fail = |
+|---|---|---|
+| Personality | Emotional target in one sentence? | AI slop |
+| Industry | Follows `designer_get_industry_rules`? | Contextual failure |
+| Anti-Pattern | Zero matches from fingerprint? | Fix first |
+| Accessibility | Passes all P1 rules? | Not shippable |
+| DESIGN.md | Coherent DESIGN.md writable from output? | Generated, not designed |
+
+---
+
+# PRE-DELIVERY CHECKLIST
+
+**Visual Quality**
+- [ ] No AI purple as default
+- [ ] Weight contrast (heading โ body)
+- [ ] Warm/cool neutrals committed
+- [ ] Near-black + near-white (not pure)
+- [ ] All spacing = 4px multiples
+- [ ] Consistent radius per component type
+
+**States**
+- [ ] Hover on all interactive
+- [ ] Focus ring (2px) on all interactive
+- [ ] Loading state for every async
+- [ ] Error state for every form input
+- [ ] Empty state for every data container
+- [ ] Disabled: opacity 0.5 + pointer-events none
+
+**Accessibility**
+- [ ] Body contrast โฅ 4.5:1 (both modes)
+- [ ] Touch targets โฅ 44px
+- [ ] prefers-reduced-motion implemented
+- [ ] No color-only indicators
+- [ ] All images have alt text
+- [ ] Skip links present
+- [ ] Semantic HTML (nav, main, button, a href)
+- [ ] Heading hierarchy sequential
+
+**Responsive**
+- [ ] Tested at 375, 768, 1024, 1440px
+- [ ] Content max-width constraint
+- [ ] No horizontal scroll mobile
+- [ ] Prose max-width: 65ch
+- [ ] dvh not vh for mobile full-height
+
+**Motion**
+- [ ] No > 500ms UI transitions
+- [ ] No linear easing
+- [ ] Only transform + opacity animated
+- [ ] No decorative bounce/pulse
+- [ ] Exits faster than entrances
+
+**Code**
+- [ ] No emojis as icons (SVG only)
+- [ ] cursor: pointer on clickable
+- [ ] No arbitrary z-index
+- [ ] Images have aspect-ratio or dimensions
+- [ ] No Figma absolute/relative dump
+- [ ] No form clearing on error
---
# INTEGRATION CONTRACTS
-**Upstream (`hyperstack:blueprint`):** New-surface or visual-semantic intent detected โ run pipeline โ return approved conditional `design_contract` / DESIGN.md path.
+## Upstream: How designer gets invoked
+
+**From `hyperstack:blueprint`:** visual/UX intent detected โ input raw request + codebase context โ return approved DESIGN.md path
+
+**From `hyperstack` root:** Phase 2 detects visual work โ routes here before engineering-discipline for any visual task
+
+**From user direct:** "design", "build me a", "landing page", "DESIGN.md", any visual phrase โ run full pipeline from Phase 1
+
+## Downstream: What designer hands off
+
+**To `hyperstack:forge-plan` (primary consumer):**
+After Phase 4 approved โ file at `docs/DESIGN.md` or `
/DESIGN.md`
+
+forge-plan reads 10 sections โ generates tasks:
+- S2 (Color) โ task: `design_tokens_generate`
+- S3 (Typography) โ task: font loading + type scale
+- S4 (Spacing) โ task: configure Tailwind spacing tokens
+- S5 (Components) โ tasks: one per component. If Q11b=shadcn: `shadcn_get_component` for each. If raw Tailwind: hand-write from DESIGN.md. If other library: use its docs.
+- S6 (Motion) โ task: `motion_generate_animation` with DESIGN.md motion spec
+- S7 (Elevation) โ task: define shadow tokens
+- S9 (Responsive) โ tasks: breakpoint-specific overrides
+- S8 (Do's/Don'ts) โ assertions embedded in every task's self-review
+
+Invocation: *"DESIGN.md approved and saved at ``. Invoking `hyperstack:forge-plan` with this as input spec."*
+
+**To `shadcn` MCP plugin - ONLY if Q11b=shadcn:**
+```
+shadcn_get_rules โ architectural constraints (ALWAYS first)
+shadcn_get_composition(page_type) โ which components compose for this page
+shadcn_list_components โ available catalog
+for each component in DESIGN.md S5:
+ shadcn_get_component(name) โ full spec: primitive, data-slots, variants
+ shadcn_get_snippet(name) โ canonical usage example
+```
+No shadcn component matching DESIGN.md spec โ escalate to `hyperstack:designer`, don't invent hybrid.
-**Downstream (`hyperstack:forge-plan`):**
-DESIGN.md saved โ `forge-plan` reads 10 sections โ generates tasks.
+**To raw Tailwind - ONLY if Q11b=raw Tailwind:**
+```
+design_tokens_get_category("component-sizing")
+ui_ux_get_component_pattern(name)
+```
+Hand-build from DESIGN.md S5 using Tailwind classes directly. Apply all P7 rules.
-**To `shadcn` Plugin (If selected):**
-Call `shadcn_get_rules` FIRST. `shadcn_get_composition`, `shadcn_list_components`. `shadcn_get_component` per DESIGN.md. No hallucinated components.
+**To `motion_generate_animation`:**
+```
+motion_generate_animation({
+ description: "",
+ durations: { fast: "150ms", normal: "200ms", slow: "300ms" },
+ easing: "ease-out",
+ prefersReducedMotion: true // always
+})
+```
+
+**To `design_tokens_generate`:**
+```
+design_tokens_generate({
+ description: "",
+ brand: ,
+ neutral: ,
+ typography: ,
+ spacing: ,
+})
+```
+
+**To `hyperstack:behaviour-analysis`:**
+After implementation complete, before ship-gate. DESIGN.md = "expected behaviour" ground truth:
+- S5 โ expected states per component
+- S6 โ expected timing/easing
+- S8 โ assertions to verify
+- S10 โ violations to search for
**To `hyperstack:ship-gate`:**
-When present, DESIGN.md acts as ground truth for testing expected behaviours, states, animations, and non-slop compliance. When absent, verification falls back to workspace patterns plus route-level proof requirements.
+Before completion claim. Verifies:
+- All S5 components have ALL required states implemented
+- S10 anti-patterns absent from code
+- OKLCH tokens from S2 present in CSS
+- `prefers-reduced-motion` implemented (S6)
+
+## Reverse Escalation
+
+| Discovery | Source | Action |
+|---|---|---|
+| Visual gap mid-plan | forge-plan โ designer | Append clarification to DESIGN.md, resume |
+| DESIGN.md expected behavior unclear/contradictory | behaviour-analysis โ designer | User resolves, DESIGN.md updated |
+| DESIGN.md compliance fail not fixable in code | ship-gate โ designer | Revise DESIGN.md framework constraints |
+
+## Announcement Protocol
-**Announce invocation:** *"Using hyperstack:designer - stepping into Creative Director persona to produce DESIGN.md."*
+When invoked: *"Using hyperstack:designer - producing DESIGN.md contract for [task type]."*
+When handing off: *"DESIGN.md complete at [path]. Invoking hyperstack:forge-plan with this as input spec."*
+When escalating back: *"[from-skill] escalating to designer - [reason]."*
\ No newline at end of file
diff --git a/skills/engineering-discipline/SKILL.md b/skills/engineering-discipline/SKILL.md
index 77fc4f3..384984d 100755
--- a/skills/engineering-discipline/SKILL.md
+++ b/skills/engineering-discipline/SKILL.md
@@ -51,6 +51,17 @@ references:
- **Quick Reference** โ direct lookup of patterns, principles, naming conventions
- **Process Mode** โ full 8-step workflow for complex/production features
+## The Iron Laws
+
+```
+1. NO REFACTOR WITHOUT TESTS FIRST
+2. NO PATTERN WITHOUT A NAMED FORCE
+3. NO SYNTAX BEFORE ARCHITECTURE
+4. NO ASSUMPTIONS WITHOUT DISCLOSURE
+5. NO "IT SHOULD WORK" - VERIFY IT DOES
+```
+
+Violating the letter = violating the spirit.
## Core Philosophy
@@ -135,6 +146,33 @@ Critical issue unaddressed โ HARD STOP.
| Architecture decisions | `references/architecture/architecture-reasoning.md` |
| Standard response format | `references/architecture/output-format.md` |
+## Red Flags - STOP
+
+| Thought | Reality |
+|---|---|
+| "Quick fix, don't need the full 8-step framework" | Quick fixes break invariants when you skip Step 3. Do the framework. |
+| "I'll skip Step 8 Negative Doubt, I'm confident" | Confidence = #1 predictor of shipped bugs. Do the negative doubt. |
+| "I already know the responsibilities" | Write them down anyway. Writing forces clarity you thought you had. |
+| "Tests for a refactor are overkill" | Refactor without tests = random code change. Not negotiable. |
+| "I'll add tests after the refactor" | Write tests first, watch them pass, then refactor. |
+| "The pattern is obviously the right one" | Obvious patterns without named forces = cargo-culting. Name the force. |
+| "Small code, skip architecture reasoning" | Small code with wrong architecture compounds fast. |
+| "I'll assume the API is stable" | Never. State the assumption explicitly. |
+| "The 5-failure-mode exercise is busywork" | Most effective bug catcher in the framework. Do all 5. |
+| "I'll write tests that match the implementation" | Tests define behavior. Write them against the spec. |
+| "Refactoring doesn't change behavior, so tests are unchanged" | Write a test first that locks behavior. Then refactor. Then run. |
+| "I understand the invariants intuitively" | Write them down. Intuition drifts in 48 hours. |
+
+## Critical Reminders
+
+1. โ No refactor without tests
+2. โ No pattern without named force
+3. โ No circular dependencies
+4. โ No assumptions without disclosure
+5. โ No global state without justification
+6. โ No proceeding with ambiguous requirements
+
+Something cannot be done safely โ say so and explain why.
## Lifecycle Integration
diff --git a/skills/forge-plan/SKILL.md b/skills/forge-plan/SKILL.md
index 99a5da2..a08511e 100644
--- a/skills/forge-plan/SKILL.md
+++ b/skills/forge-plan/SKILL.md
@@ -1,163 +1,241 @@
---
name: forge-plan
category: core
-description: Executed after blueprint/designer approval. Produces task-by-task implementation plan grounded in MCP-verified APIs. Zero placeholders.
+description: Use after blueprint design approval to produce a task-by-task implementation plan grounded in MCP-verified API calls. No placeholders, no assumed syntax.
---
# Forge Plan
## Input
-Requires approved design:
-1. **`hyperstack:blueprint`** โ Backend/infra/architecture. Input: architecture note.
-2. **`hyperstack:designer`** โ Visual/UX. Input: 10-section `DESIGN.md`.
+Requires a completed, user-approved design from one of:
-No design present:
-- Visual/UX task โ STOP. Invoke `designer`.
-- Backend/infra task โ STOP. Invoke `blueprint`.
+1. **`hyperstack:blueprint`** โ backend/infra/architecture work. Design = architecture note.
+2. **`hyperstack:designer`** โ visual/UX work. Design = structured `DESIGN.md` with 10 sections.
-## DESIGN.md Ingestion (Visual/UX)
+No approved design:
+- Visual/UX task โ stop, invoke `hyperstack:designer`
+- Backend/infra task โ stop, invoke `hyperstack:blueprint`
-Parse DESIGN.md into task categories:
+## DESIGN.md Ingestion (visual/UX work)
+
+Parse DESIGN.md and map sections to task categories:
| DESIGN.md Section | Task Category | MCP Calls |
|---|---|---|
-| 1. Visual Theme | Context | `designer_get_personality` |
-| 2. Color Palette | Token setup | `design_tokens_generate` (OKLCH) |
-| 3. Typography | Font loading + scale | `design_tokens_get_category("typography")` |
-| 4. Spacing | Tailwind config | `design_tokens_get_category("spacing")` |
-| 5. Component Specs | Component impl | shadcn: `shadcn_get_rules`, `get_composition`, `get_component`. Raw Tailwind: `ui_ux_get_component_pattern`. |
-| 6. Motion | Animation build | `motion_generate_animation` |
-| 7. Elevation | Shadow tokens | `design_tokens_get_category("shadows")` |
-| 8. Do's & Don'ts | Self-review | Embedded assertions in tasks |
-| 9. Responsive | Breakpoint config | `ui_ux_get_principle("responsive")` |
-| 10. Anti-Patterns| Self-review | Embedded validations |
+| 1. Visual Theme | (context only - used in all tasks) | `designer_get_personality` |
+| 2. Color Palette | Token setup tasks | `design_tokens_generate` with OKLCH values |
+| 3. Typography | Font loading + type scale tasks | `design_tokens_get_category("typography")` |
+| 4. Spacing | Tailwind spacing config | `design_tokens_get_category("spacing")` |
+| 5. Component Specs | One task per component | shadcn โ `shadcn_get_rules` + `shadcn_get_composition` + `shadcn_get_component`. Raw Tailwind โ `ui_ux_get_component_pattern` + hand-build. Other library โ use that library's own docs. |
+| 6. Motion | Animation tasks | `motion_generate_animation` with DESIGN.md motion spec |
+| 7. Elevation | Shadow token tasks | `design_tokens_get_category("shadows")` |
+| 8. Do's and Don'ts | Embedded as self-review assertions in every task |
+| 9. Responsive Breakpoints | Breakpoint-specific override tasks | `ui_ux_get_principle("responsive")` |
+| 10. Anti-Patterns | Embedded as self-review assertions - each task must verify none present |
+
+Every task's self-review step must cite the relevant DESIGN.md section for traceability.
## The Contract
-Every implementation step touching an MCP domain MUST reference tool output. Imagined syntax = rejected plan.
+Every implementation step touching a domain covered by Hyperstack MCP **must** reference actual MCP tool output. Plan built on imagined syntax = bug report scheduled for delivery.
## Process
-### Step 1: MCP Survey
+### Step 1: MCP Survey for Implementation
-Query tools BEFORE writing:
+Before writing a single task, call relevant MCP tools for every domain the implementation will touch:
| Domain | Call |
|---|---|
-| **DESIGN.md present** | **`designer_get_personality`, `get_page_template`, `get_anti_patterns`** |
-| React Flow | `reactflow_get_api` |
-| Motion | `motion_get_api`, `motion_generate_animation` |
-| Go / Echo | `golang_get_pattern`, `echo_get_recipe` |
-| Rust | `rust_get_practice` |
-| Design tokens | `design_tokens_get_procedure`, `design_tokens_generate` |
-| React / Next.js | `react_get_pattern`, `react_get_constraints` |
-| shadcn (if selected) | `shadcn_get_rules` (FIRST), `get_composition`, `get_component`, `get_snippet`. Invoke `shadcn-expert` for guidance. |
-| Raw Tailwind (if selected) | `ui_ux_get_component_pattern` |
-| Other library | Read library documentation |
-
-**Degraded Mode:** If MCP fails, inform user: "MCP unavailable for [domain]. Plan contains unverified API shapes." Mark tasks `[UNVERIFIED]`.
+| **DESIGN.md present** | **`designer_get_personality(cluster)`, `designer_get_page_template(type)`, `designer_get_anti_patterns(industry)` - DESIGN.md is ground truth for all visual decisions** |
+| React Flow | `reactflow_get_api` for each component/hook used |
+| Motion | `motion_get_api` for each animation primitive + `motion_generate_animation` if DESIGN.md specifies motion |
+| Go / Echo | `golang_get_pattern` + `echo_get_recipe` for each pattern |
+| Rust | `rust_get_practice` for each relevant practice |
+| Design tokens | `design_tokens_get_procedure` per token step + `design_tokens_generate` if DESIGN.md specifies OKLCH palette |
+| React / Next.js | `react_get_pattern` + `react_get_constraints` |
+| shadcn (only if Q11b chose shadcn) | `shadcn_get_rules` (first) + `shadcn_get_composition(page_type)` + `shadcn_get_component(name)` + `shadcn_get_snippet(name)`. Invoke `hyperstack:shadcn-expert` for architectural guidance. |
+| Raw Tailwind (only if Q11b chose raw Tailwind) | `ui_ux_get_component_pattern(name)` per component. Hand-build from DESIGN.md Section 5. No library wrapper. |
+| Other component library (MUI, Mantine, Chakra, Ant Design) | Use the library's own docs. Hyperstack has no plugin for these. Flag to user. |
+
+Record each tool output. Plan's code blocks must match what tools return.
+
+**MCP Degraded Mode:** Tools fail โ inform user: "MCP unavailable for [domain] - plan may contain unverified API shapes." Mark affected tasks `[UNVERIFIED]`. Don't silently proceed with assumed APIs.
### Step 2: Map Files
+Before defining tasks, map every file to be created or modified:
+
```
-Create: path/to/new-file.ts - one-line purpose
-Modify: path/to/existing.ts - what changes + why
-Test: path/to/file.test.ts - behaviour tested
+Create: exact/path/to/new-file.ts - one-line responsibility
+Modify: exact/path/to/existing.ts - what changes and why
+Test: exact/path/to/file.test.ts - what behaviour is tested
```
-Split by responsibility, not by layer. Files changing together live together.
+
+Each file has one clear responsibility. Files that change together live together. Split by responsibility, not by layer.
### Step 3: Write Tasks
-Task structure MUST be autonomous and testable:
+**Each task produces working, testable, committed software on its own.**
+
+Task structure:
````markdown
### Task N: [Name]
**Files:**
-- Create/Modify: `path/file.ts`
+- Create/Modify/Test: `exact/path/file.ts`
+
+**MCP references:** [tool calls from Step 1 relevant to this task]
-**MCP references:** [cite Step 1 outputs]
+- [ ] **Step 1: Write the failing test**
-- [ ] **Step 1: Failing test**
```ts
-describe('Component', () => { it('does X', () => { ... }); });
+describe('ComponentName', () => {
+ it('should do X when Y', () => {
+ // ...
+ });
+});
```
-Run: `npx vitest run path/file.test.ts` (Expected: FAIL)
+
+Run: `npx vitest run path/to/file.test.ts`
+Expected: FAIL - "X is not defined"
- [ ] **Step 2: Implement**
+
```ts
-// actual verified implementation
+// actual implementation matching MCP-verified API shapes
```
- [ ] **Step 3: Verify**
-Run: `npx vitest run path/file.test.ts` (Expected: PASS)
+
+Run: `npx vitest run path/to/file.test.ts`
+Expected: PASS - 1/1
- [ ] **Step 4: Commit**
+
```bash
-git add file.ts file.test.ts
-git commit -m "feat: [desc]"
+git add path/to/file.ts path/to/file.test.ts
+git commit -m "feat: [specific description]"
```
````
### Step 4: Self-Review
-1. **Spec coverage:** Cover every requirement?
-2. **Placeholder scan:** Hunt and destroy "TBD", "TODO", "implement later", "similar to Task N".
-3. **MCP verification:** All APIs trace back?
-4. **Type consistency:** Signatures match?
-5. **Atomicity:** Steps represent 2-5 minutes of work?
+After writing the complete plan, check inline:
+
+1. **Spec coverage** โ task for every requirement in the approved design? List gaps.
+2. **Placeholder scan** โ search for "TBD", "TODO", "add error handling", "similar to Task N", "implement later", steps without code blocks. Fix every instance.
+3. **MCP verification** โ every domain-specific code block traces back to a tool call in Step 1.
+4. **Type consistency** โ types, method names, prop names consistent across tasks.
+5. **Step atomicity** โ each step = 2-5 minutes of work. "Implement the entire service layer" is not a step.
### Step 5: Handoff
-Save to `docs/plans/YYYY-MM-DD-[feature].md`. Commit. Ask user:
+Save plan to `docs/plans/YYYY-MM-DD-[feature-name].md` and commit.
+
+Then offer:
-> "Plan saved. Execution options:
-> 1. **Autonomous** (`autonomous-mode`) - End-to-end auto.
-> 2. **Subagent-driven** (`subagent-ops`) - Strict agent handoff per task.
-> 3. **Manual checkpoints** (`engineering-discipline`) - Execute, pause for human review."
+> "Plan saved to `[path]`. Three execution options:
+>
+> 1. **Autonomous** - execute all tasks end-to-end without pausing. Tests, reviews, ship-gate run automatically. Only stops on failure.
+> 2. **Subagent-driven** (`hyperstack:subagent-ops`) - fresh agent per task, automated two-stage review between tasks.
+> 3. **Inline with checkpoints** (`hyperstack:engineering-discipline`) - execute tasks in this session, pause for human review at phase gates.
+>
+> Which approach?"
-## NO PLACEHOLDERS
+## No Placeholders - Ever
-Never write:
-- "TBD" / "fill in later"
-- "Add error handling" (write it)
-- "Write tests for above" (provide code)
-- "Similar to Task N" (copy it)
+Plan failures โ never write:
+- "TBD" / "TODO" / "fill in later"
+- "Add appropriate error handling"
+- "Write tests for the above" without actual test code
+- "Similar to Task N" โ repeat the code, tasks may be read out of order
+- Steps describing what to do without showing how
+- References to types or functions not defined in any task
-## IRON LAW
+## The Iron Law
```
NO PLANS WITHOUT FRESH MCP-VERIFIED DATA
```
-Prop names and hook signatures trace to THIS session's MCP output. Not memory. Not last session.
+Every API call, prop name, hook signature, or library pattern must trace to an MCP tool call made in THIS session. Not last session. Not from memory.
-## RED FLAGS - STOP
+## Red Flags - STOP
| Thought | Reality |
|---|---|
-| "I know the React Flow API." | Write wrong props. Call tool. |
-| "Tester writes assertions." | Placeholder detected. Write the test. |
-| "Too small for test." | All tasks require tests. |
-| "Run survey later." | Survey dictates plan. Run first. |
-| "Tool output obvious." | Cite it anyway. |
-| "TBD uncertain parts." | Resolve before writing. |
-| "Similar to Task N." | Plans read piecemeal. Repeat code. |
+| "I know how React Flow Handle works, no need to check" | Plan will have wrong prop names. Call the tool. |
+| "I'll write the test structure, developer can fill in assertions" | That's a placeholder. Write the real test. |
+| "This task is too small for a full test" | No task is too small for a failing test. |
+| "I'll reference the survey output later" | Do the survey before writing. Not after. |
+| "I already surveyed this library last week" | State drifts. MCP data updates. Call again. |
+| "The MCP tool output is obvious" | Cite the actual output. |
+| "I'll TBD the uncertain parts" | TBD = plan failure. Resolve uncertainty before writing the task. |
+| "Similar to Task N saves time" | Plans get read out of order. Repeat the code. |
+| "I don't need to run the tool for this common pattern" | Common patterns drift. Call the tool. |
+| "User is waiting, I'll skip the survey" | Plan without survey = bug report. Do the survey. |
+| "This is a minor refactor" | Minor refactors move responsibility. Plans for responsibility changes need MCP verification. |
+
+## Integration
+
+- **Requires (backend/infra):** `hyperstack:blueprint` approved design
+- **Requires (visual/UX):** `hyperstack:designer` approved DESIGN.md
+- **Executes via:** Autonomous mode, `hyperstack:subagent-ops`, or `hyperstack:engineering-discipline`
+- **Completes via:** `hyperstack:ship-gate` โ `hyperstack:deliver`
## Reverse Escalation
| Discovery | Escalate to | Action |
|---|---|---|
-| DESIGN.md ambiguous | `designer` | Append clarification. Resume. |
-| Missing component | `designer` | Invoke for gap. Update DESIGN.md. |
-| MCP tool conflict | `designer` | Acknowledge framework constraints. |
-| Architecture gap | `blueprint` | Formalize structural decision. |
+| DESIGN.md section ambiguous or contradictory | `hyperstack:designer` | Pause plan, resolve, append clarification to DESIGN.md, resume |
+| Component needed not in DESIGN.md Section 5 | `hyperstack:designer` | Invoke designer with specific gap, append to DESIGN.md |
+| MCP tool returns conflicting shapes with DESIGN.md | `hyperstack:designer` | DESIGN.md may need to acknowledge framework constraints |
+| Architecture gap (non-visual) | `hyperstack:blueprint` | Re-enter blueprint for architecture decision |
-## Lifecycle
+Don't silently invent what DESIGN.md doesn't specify. Escalate back to designer.
-**Frontend Agent:**
-`blueprint` โ `designer` โ `forge-plan` (THIS) โ `[execution]` โ `ship-gate` โ `deliver`
-**Backend Agent:**
-`blueprint` โ `forge-plan` (THIS) โ `[execution]` โ `ship-gate` โ `deliver`
+## Lifecycle Integration
+
+### Agent Workflow Chains
+
+**Website/Frontend Agent:**
+```
+blueprint โ designer โ forge-plan (THIS) โ [execution] โ ship-gate โ deliver
+ โ
+ DESIGN.md ingestion
+ โ
+ [shadcn-expert if Q11b=shadcn]
+```
+
+**Backend/Infra Agent:**
+```
+blueprint โ forge-plan (THIS) โ [execution] โ ship-gate โ deliver
+ โ
+ architecture note
+```
+
+**Execution handoff (user chooses):**
+- `autonomous-mode` โ full auto end-to-end
+- `subagent-ops` โ fresh agent per task
+- `engineering-discipline` โ manual with checkpoints
+
+### Upstream Dependencies
+- `blueprint` โ approved architecture note (backend/infra)
+- `designer` โ approved DESIGN.md (visual/UX)
+
+### Downstream Consumers
+- `autonomous-mode` | `subagent-ops` | `engineering-discipline` โ executes plan
+- `shadcn-expert` โ if Q11b=shadcn, per-component guidance
+- `worktree-isolation` โ clean workspace before execution
+
+### Reverse Escalation
+| Discovery | Escalate to | Action |
+|---|---|---|
+| DESIGN.md section ambiguous | `designer` | Pause, resolve, append to DESIGN.md |
+| Component not in DESIGN.md Section 5 | `designer` | Add to DESIGN.md |
+| MCP tool conflicts with DESIGN.md | `designer` | Reconcile framework constraints |
+| Architecture gap (non-visual) | `blueprint` | Re-enter for decision |
diff --git a/skills/hyperstack/SKILL.md b/skills/hyperstack/SKILL.md
deleted file mode 100644
index 966d97e..0000000
--- a/skills/hyperstack/SKILL.md
+++ /dev/null
@@ -1,176 +0,0 @@
----
-name: hyperstack
-category: meta
-description: Bootstrap definitions establishing MCP tools and skills prior to work. Auto-loaded at session start. Do not bypass or rationalize skipping this skill.
----
-
-
-If dispatched as a subagent, skip this skill.
-Context is provided by the orchestrating agent. Do not reload bootstrap.
-
-
-
-Hyperstack is active. This constitutes the mandatory operational framework for this repository.
-
-**Three-Layer Ecosystem:**
-1. **Layer 1: Ground Truth (MCP)** - Deterministic data for the stack.
-2. **Layer 2: Process (Skills)** - Disciplined engineering workflows and gates.
-3. **Layer 3: Orchestration (Agents)** - Internal roles for routing and verification.
-
-**The 1% Rule:** If a 1% probability exists that a Hyperstack skill, MCP tool, or internal agent role applies, YOU MUST invoke or route to it BEFORE acting. Not after checking code. Not after testing a hypothesis. BEFORE.
-
-You cannot rationalize exceptions to this rule.
-
-
----
-
-## The Iron Laws
-
-```
-1. NO CODE WITHOUT MCP GROUND-TRUTH DATA
- Call relevant Hyperstack plugins prior to implementation.
-
-2. NO VISUAL CODE WITHOUT AN APPROVED DESIGN.md
- The designer skill produces the contract when routing requires a design contract; existing-project frontend logic work stays workspace-first.
-
-3. NO COMPLETION CLAIMS WITHOUT SHIP-GATE EVIDENCE
- "Should work" is unacceptable. Execute the command and output results.
-
-4. NO SKIPPING SKILLS BECAUSE "THIS IS SIMPLE"
- Simple tasks hide unexamined assumptions that cause the most damage.
-
-5. NO SPECIALIST WORK WITHOUT PROPER ROLE ROUTING
- Route specialist domain tasks (e.g., website building) to the corresponding agent.
-```
-
-Violating the letter of these laws violates the spirit.
-
----
-
-## Instruction Priority
-
-1. **User's explicit instructions** (Project rules, direct requests) - Maximum priority.
-2. **Hyperstack skills** - Overrides system defaults.
-3. **Default system behavior** - Minimum priority.
-
----
-
-## Layer 1: MCP Tools (Ground-Truth Data)
-
-Call these BEFORE writing code. **Memory is unacceptable.**
-
-| Namespace | Stack | Must-call-first tools |
-|---|---|---|
-| `designer_*` | Design engine | `resolve_intent`, `get_personality`, `get_preset`, `get_page_template`, `get_font_pairing`, `get_anti_patterns` |
-| `design_tokens_*` | Token systems | `generate`, `get_category`, `get_gotchas` |
-| `ui_ux_*` | UI/UX principles | `get_principle`, `get_component_pattern`, `get_gotchas` |
-| `shadcn_*` (if selected) | Base UI edition | `get_rules` (FIRST), `get_composition`, `get_component`, `get_snippet`, `list_components` |
-| `reactflow_*` | React Flow v12 | `get_api`, `search_docs`, `get_pattern` |
-| `motion_*` | Motion for React v12 | `get_api`, `get_examples`, `get_transitions` |
-| `lenis_*` | Lenis smooth scroll | `get_api`, `generate_setup`, `get_pattern` |
-| `react_*` | React 19 / Next.js | `get_pattern`, `get_constraints`, `search_docs` |
-| `echo_*` | Echo (Go HTTP) | `get_recipe`, `get_middleware`, `decision_matrix` |
-| `golang_*` | Go best practices | `get_practice`, `get_pattern`, `get_antipatterns` |
-| `rust_*` | Rust practices | `get_practice`, `cheatsheet`, `search_docs` |
-
-### MCP Degraded Mode
-
-If MCP tools fail:
-1. Explicitly inform user: "Hyperstack MCP server is unavailable. Assertions are uncertain."
-2. Fall back to training data, but FLAG every answer as uncertain.
-3. NEVER assume ground-truth validity.
-4. DO NOT invent API shapes.
-
----
-
-## Layer 2: When to Invoke Skills
-
-Load via `Skill` tool **before acting**. Registered in: `skills/INDEX.md`.
-
-### Announcement Iron Law
-
-```
-BEFORE invoking any Hyperstack skill, announce:
-"Using hyperstack:[skill-name] - [one-line purpose]"
-```
-
-### Condition โ Skill Table
-
-| Situation | Load this skill |
-|---|---|
-| Starting any new feature, component, or behaviour change | `blueprint` |
-| Any task touching existing project code | `blueprint` builds `workspace_inventory` first |
-| Task creates a new surface, changes visual semantics, or has no reliable existing pattern match | `designer` โ produces conditional `design_contract` / DESIGN.md |
-| Have an approved design or plan to execute | `forge-plan` or `run-plan` |
-| Claiming anything is complete, fixed, or passing | `ship-gate` โ evidence required |
-| Hit any bug, test failure, or unexpected behaviour | `debug-discipline` โ root cause before any fix |
-| User says "autonomous", "just do it", "go ahead" | `autonomous-mode` |
-| Plan has 2+ independent tasks | `subagent-ops` or `parallel-dispatch` |
-| Implementing any feature or bug fix (before code) | `test-first` |
-| Reviewing completed implementation | `code-review` |
-| Security-sensitive API, auth, or infra code | `security-review` |
-| UI state machine, interaction audit | `behaviour-analysis` |
-| Selecting an abstraction or design pattern | `engineering-discipline` |
-| Writing or updating project documentation | `readme-writer` |
-| Starting implementation that needs workspace isolation | `worktree-isolation` |
-
-### Workflow Chain Reference
-
-```
-New work: blueprint โ workspace_inventory + change classification โ [designer only if required] โ forge-plan โ execution โ ship-gate โ deliver
-
-Existing: run-plan โโโค
- โโ autonomous-mode
- โโ subagent-ops
- โโ engineering-discipline
-
-Debugging: debug-discipline โ parallel-dispatch
-```
-
-**Planning rule:** `workspace_inventory` is universal. `design_contract` is conditional. `verification_report` is universal.
-
----
-
-## Layer 3: Agents (Orchestration & Routing)
-
-Internal roles โ auto-invoked, not user-facing.
-
-| Agent | Owns |
-|---|---|
-| `hyper` | Classification, routing, gate enforcement, final verification, delivery |
-| `frontend-builder` | Frontend-facing design/implementation, CTA hierarchy, page structure, and existing-project frontend logic work |
-
-### Disallowed Transitions
-
-- `user request -> frontend-builder`
-- `frontend-builder -> ship`
-- `frontend-builder -> deliver`
-
----
-
-## High-Signal Red Flags
-
-| Thought | Reality | Action |
-|---|---|---|
-| "I know the React Flow API from memory." | Memory drifts. v11 and v12 differ. | Call `reactflow_get_api` first. |
-| "This is a simple animation." | Animations require `prefers-reduced-motion`, easing, and GPU properties. | Call `motion_get_examples` first. |
-| "Go error handling is straightforward." | Straightforward code hosts anti-patterns. | Call `golang_get_practice` first. |
-| "I'll check docs after writing." | Shipping occurs before checking. | Consult docs BEFORE code. |
-| "I know the OKLCH token pattern." | OKLCH dictates specific alpha, chroma, and lightness rules. | Call `design_tokens_get_procedure` first. |
-| "This pattern is common; I'll adapt it." | Adaptation obscures structural drift. | Call the MCP tool and replicate ground truth. |
-| "The user is impatient; skip the gate." | User impatience does not grant permission to ship slop. | Enforcement gates are mandatory. |
-| "This isn't visual work." | If it changes look, motion, or interaction, it is visual. | Invoke designer skill. |
-| "Subagent reported success." | Subagents hallucinate success. | Manually review the diff and run tests. |
-| "Just this once." | "Once" establishes a pattern. | Zero exceptions. |
-
----
-
-## Final Response Check
-
-1. [ ] Did I evaluate Hyperstack skill applicability? (1% rule)
-2. [ ] Did I execute relevant MCP tools? (No memory rely)
-3. [ ] If visual, did I invoke `designer` BEFORE coding?
-4. [ ] If claiming completion, did I execute verification THIS message?
-5. [ ] Did I announce skill invocation with exact formatting?
-
-**Stop and fix if any answer is NO.**
diff --git a/skills/parallel-dispatch/SKILL.md b/skills/parallel-dispatch/SKILL.md
index b6204f4..69c5e5d 100644
--- a/skills/parallel-dispatch/SKILL.md
+++ b/skills/parallel-dispatch/SKILL.md
@@ -89,6 +89,14 @@ Return: Summary of root cause and changes made.
| Related failures split across agents | Investigate together first |
| Same files given to multiple agents | Guaranteed merge conflicts |
+## Red Flags - STOP
+
+| Thought | Reality |
+|---|---|
+| "Dispatch 5 agents for speed" | More agents โ faster if they conflict |
+| "I'll let agents figure out the scope" | Vague scope โ vague results |
+| "These might be related but I'll parallelize anyway" | Related bugs in parallel = wasted work. Investigate first. |
+| "Skip the integration check" | Agents don't know about each other. You must verify. |
## Integration
diff --git a/skills/readme-writer/SKILL.md b/skills/readme-writer/SKILL.md
index 1e6079f..8d119e7 100755
--- a/skills/readme-writer/SKILL.md
+++ b/skills/readme-writer/SKILL.md
@@ -15,7 +15,7 @@ Write README files that are specific, verifiable, and useful to the next develop
Main failure mode of LLM-written READMEs: false confidence - invented setup steps, inflated feature claims, vague value propositions, examples not grounded in the repository.
-**Style layer:**
+**Style layer (from CLAUDE.md rules):**
- Emoji on every section heading
- Centered hero block with project name, tagline, and badges in ``
- Library-specific badges with colors and logos (not generic)
diff --git a/skills/run-plan/SKILL.md b/skills/run-plan/SKILL.md
index e0f42a8..efebd72 100644
--- a/skills/run-plan/SKILL.md
+++ b/skills/run-plan/SKILL.md
@@ -14,6 +14,13 @@ description: Use when you have an existing plan, spec, or task list to execute.
Creating a new plan from scratch โ use `hyperstack:blueprint` โ `hyperstack:forge-plan` instead.
+## The Iron Law
+
+```
+NO EXECUTION WITHOUT PLAN VALIDATION FIRST.
+```
+
+Plan with wrong API shapes or missing steps โ wrong code. 2 minutes of validation saves hours of rework.
## Process
@@ -85,6 +92,15 @@ Blocker mid-task โ stop immediately, don't guess or work around it. Report to
All tasks marked complete โ invoke `hyperstack:deliver`.
+## Red Flags - STOP
+
+| Thought | Reality |
+|---|---|
+| "The plan looks fine, no need to check MCP" | One wrong prop name = broken code in every task that uses it |
+| "I'll fix the gap as I go" | Undocumented gaps โ undocumented decisions |
+| "The user wrote this plan, it must be correct" | Plans have bugs. That's why this step exists. |
+| "Step N is unclear but I can infer what they meant" | Stop and ask. Inferred intent โ surprising code. |
+| "I'll skip ship-gate on this task, it's small" | No exceptions. |
## Integration
diff --git a/skills/shadcn-expert/SKILL.md b/skills/shadcn-expert/SKILL.md
index 2314b9f..224811a 100644
--- a/skills/shadcn-expert/SKILL.md
+++ b/skills/shadcn-expert/SKILL.md
@@ -6,6 +6,15 @@ description: Advanced shadcn/ui architect specializing in Base UI, Tailwind v4,
# shadcn/ui Expert (Base UI Edition)
+## The Iron Law
+
+```
+NO COMPONENT WITHOUT shadcn_get_rules FIRST
+```
+
+Call `shadcn_get_rules` before proposing any new component or modification. Base UI edition differs from standard shadcn (Radix) in multiple ways. Memory is not acceptable.
+
+Violating the letter = violating the spirit.
## When This Skill Applies
@@ -22,13 +31,10 @@ description: Advanced shadcn/ui architect specializing in Base UI, Tailwind v4,
## Position in the Ecosystem
```
-hyperstack:blueprint โ workspace_inventory + change classification
+hyperstack:designer โ DESIGN.md
โ
- โผ (if shadcn chosen and design contract required)
-hyperstack:designer โ conditional DESIGN.md / design_contract
- โ
- โผ
-hyperstack:forge-plan reads workspace + routed requirements
+ โผ (if shadcn chosen in Q11)
+hyperstack:forge-plan reads DESIGN.md
โ
โผ
hyperstack:shadcn-expert (THIS skill)
@@ -39,7 +45,7 @@ hyperstack:shadcn-expert (THIS skill)
โโโถ shadcn_get_snippet(name)
โ
โผ
-Implementation tasks per routed component requirements
+Implementation tasks per DESIGN.md Section 5
```
## MCP Tools
@@ -97,12 +103,26 @@ Implementation tasks per routed component requirements
## Integration with Designer + Forge-Plan
-**Upstream:** `hyperstack:forge-plan` processes routed component requirements. If a design contract exists, it uses the relevant component sections. Existing-project frontend logic work may stay workspace-first without forcing a design contract.
+**Upstream:** `hyperstack:forge-plan` processes DESIGN.md Section 5 โ calls `shadcn_get_component(name)` per component โ references this skill for architectural guidance
-**Downstream:** Component code matching required variants + states, all P7 (Components) rules enforced, ready for `hyperstack:ship-gate`
+**Downstream:** Component code matching DESIGN.md Section 5 variants + states, all P7 (Components) rules enforced, ready for `hyperstack:ship-gate`
-**Reverse escalation:** If a routed design contract is incompatible with shadcn architecture, escalate to `hyperstack:designer` to reconcile. Don't silently adapt.
+**Reverse escalation:** DESIGN.md spec incompatible with shadcn architecture โ escalate to `hyperstack:designer` to reconcile. Don't silently adapt.
+## Red Flags - STOP
+
+| Thought | Reality |
+|---|---|
+| "User didn't say 'shadcn' explicitly, but I'll assume it" | Do NOT assume. Ask or check designer Q11. |
+| "I know the shadcn rules from training data" | Training data has standard shadcn (Radix). This is Base UI edition. Call `shadcn_get_rules`. |
+| "data-slot is just a naming convention" | It's the primary styling selector for parentโchild styling. Mandatory. |
+| "I'll use Radix because it's more common" | Project chose Base UI. Use `@base-ui/react`. |
+| "Hardcoding pixel positions is faster" | Use Base UI props. Hardcoded px breaks responsive behavior. |
+| "This Dialog has 5 slots, I'll combine into one component" | Split into sub-components. Monolithic Dialogs are anti-pattern. |
+| "I'll skip 'use client' since it seems stateless" | Does it use `data-open` or any state modifier? โ needs `'use client'`. Check before skipping. |
+| "The cn utility is optional" | Mandatory. All className merging goes through `cn`. |
+| "I'll pick variant names that match the brand" | Stick to `default/outline/secondary/ghost/destructive`. Custom variants break the system. |
+| "shadcn components work with any color system" | OKLCH-native. Hex values break the token system. |
## Lifecycle Integration
@@ -111,23 +131,23 @@ Implementation tasks per routed component requirements
**Website/Frontend Agent (if Q11b=shadcn):**
```
-blueprint โ workspace inventory / change classification โ [designer only if required] โ forge-plan โ shadcn-expert (THIS) โ [component implementation]
- โ
- [shadcn_* MCP tools]
+designer โ DESIGN.md โ forge-plan โ shadcn-expert (THIS) โ [component implementation]
+ โ
+ [shadcn_* MCP tools]
```
### Upstream Dependencies
-- Q11b chose shadcn/ui (Base UI edition)
-- `forge-plan` โ processes routed component requirements โ calls shadcn_get_component per component
+- `designer` โ Q11b chose shadcn/ui (Base UI edition)
+- `forge-plan` โ processes DESIGN.md Section 5 โ calls shadcn_get_component per component
### Downstream Consumers
-- Component code matching routed variants + states
+- Component code matching DESIGN.md Section 5 variants + states
- `ship-gate` โ P7 (Components) rules enforced
### Reverse Escalation
| Discovery | Escalate to | Action |
|---|---|---|
-| Design contract incompatible with shadcn architecture | `designer` | Reconcile design contract with Base UI constraints |
+| DESIGN.md spec incompatible with shadcn architecture | `designer` | Reconcile DESIGN.md with Base UI constraints |
### When NOT to Use
- User chose raw Tailwind (no component library)
diff --git a/skills/ship-gate/SKILL.md b/skills/ship-gate/SKILL.md
index b523829..bc3ac3c 100644
--- a/skills/ship-gate/SKILL.md
+++ b/skills/ship-gate/SKILL.md
@@ -1,57 +1,99 @@
---
name: ship-gate
category: core
-description: Execute before claiming work is complete, fixed, or passing. Run the verification command and show output. No evidence = no claim.
+description: Use before claiming any work is complete, fixed, or passing. Run the verification command and show output before making any success claim.
---
# Verification Before Completion
+## The Iron Law
+
+```
+NO COMPLETION CLAIMS WITHOUT FRESH VERIFICATION EVIDENCE.
+```
+
+Not run in this message โ cannot claim it passes. Claiming completion without evidence = dishonesty.
+
+Violating the letter = violating the spirit.
## The Gate
```
-1. IDENTIFY โ Which command proves this claim?
-2. RUN โ Execute it fresh. Not from memory.
-3. READ โ Full output. Exit code. Error count.
-4. VERIFY โ Output confirms claim?
- NO โ State status + provide evidence.
- YES โ State claim + provide evidence.
+1. IDENTIFY โ What command proves this claim?
+2. RUN โ Execute it fresh. Not from memory. Not from a prior run.
+3. READ โ Full output. Exit code. Error count. Every line.
+4. VERIFY โ Does output confirm the claim?
+ NO โ State actual status with evidence
+ YES โ State claim WITH evidence attached
5. CLAIM โ Only now.
```
-Skipping steps = lying.
-**Evidence MUST be visible.** Paste actual command output. "I ran the command" is unacceptable. No output = no claim.
+Skipping any step = lying, not verifying.
+
+**Evidence must be visible in this message.** Not "I ran the command." Not "I checked earlier." Actual command output pasted into this message. No output = no claim.
## Evidence Format
-Show output in message:
+### For test claims:
+```
+โ
Tests pass:
+
+$ npm test
+PASS src/components/__tests__/Button.test.tsx
+ Button
+ โ renders with label (5ms)
+ โ handles click event (3ms)
+
+Tests: 3 passed, 3 total
+```
+
+### For type checks:
+```
+โ
Types check:
+
+$ tsc --noEmit
+[no output = success]
+```
+
+### For build claims:
+```
+โ
Build succeeds:
+
+$ npm run build
+[build output showing exit 0]
+```
+
+### For MCP-verified patterns:
+```
+โ
Code matches MCP output:
+
+MCP tool: reactflow_get_api(Handle)
+Output: props = { children, position, type }
-**Tests:** `$ npm test` โ `Tests: 3 passed, 3 total`
-**Types:** `$ tsc --noEmit` โ `[no output = success]`
-**Build:** `$ npm run build` โ `[exit 0]`
-**MCP:** Code block must explicitly match specific MCP tool response.
+Code:
+```
-Cannot show evidence โ cannot make claim.
+Cannot show evidence โ cannot make the claim.
## Verification Map
| Claim | Required | Not sufficient |
|---|---|---|
-| Tests pass | Test command output showing 0 failures | "Should pass", previous run |
-| Build succeeds | Build command: exit 0 | Linter passing |
-| Bug is fixed | Reproduce original symptom: now passes | "Logically fixed" |
+| Tests pass | Test command output showing 0 failures | "Should pass", previous run, looking at the code |
+| Build succeeds | Build command: exit 0 | Linter passing, "no obvious errors" |
+| Bug is fixed | Reproduce original symptom: now passes | Code changed, "logically fixed" |
| Type checks | `tsc --noEmit`: 0 errors | "Looks typed correctly" |
-| MCP pattern | Code matches current session MCP output | "Following pattern" |
-| Requirements | Line-by-line checklist against spec | Tests passing |
-| Subagent done | VCS diff confirms actual changes | Subagent reports "done" |
-| Regression | Red-green cycle verified | "I wrote a test" |
-| **Design-contract compliance** | **Implementation matches all required sections when a design contract exists.** | **"Looks right"** |
+| MCP data applied correctly | Code matches MCP output shown in session | "I followed the pattern" |
+| Requirements met | Line-by-line checklist against the spec | Tests passing |
+| Subagent completed | VCS diff confirms actual changes | Subagent reports "done" |
+| Regression covered | Red-green cycle verified | "I wrote a test for it" |
+| **DESIGN.md compliance** | **Implementation matches all 10 DESIGN.md sections. All component states present. No anti-patterns from Section 10.** | **"Looks right", "follows the design"** |
-## DESIGN.md / Design Contract Compliance Gate (Visual/UX)
+## DESIGN.md Compliance Gate (visual/UX tasks only)
-If a task requires a design contract and DESIGN.md exists, completion requires this gate:
+If DESIGN.md exists in the repo, completion claim must pass this gate:
-### Step 1: Automated Checker
+### Step 1: Run Automated Compliance Checker
```bash
designer_verify_implementation(
@@ -63,37 +105,96 @@ designer_verify_implementation(
| DESIGN.md Section | What the tool checks |
|---|---|
| 2. Color Palette | All OKLCH tokens present. Contrast >= WCAG AA. |
-| 3. Typography | Fonts loaded. Scale defined. Tracking matches. |
-| 4. Spacing | 4px grid. No arbitrary pixels. |
-| 5. Components | ALL states (default/hover/focus/active/disabled/loading). |
-| 6. Motion | `prefers-reduced-motion`. No `linear`. Transitions < 500ms. |
-| 7. Elevation | Shadow tokens defined. Z-index uses scale. |
-| 8. Do's and Don'ts | Each rule checked against code. |
-| 9. Responsive | 375/768/1024/1440px tested. Prose <= 65ch. |
-| 10. Anti-Patterns | No `#6366F1`, no `500` weight everywhere, no `rgba` shadows. |
+| 3. Typography | Font family loaded. Type scale tokens defined. Tracking/line-height match DESIGN.md. |
+| 4. Spacing | Spacing tokens on 4px grid. No arbitrary pixel values. |
+| 5. Components | ALL required states present (default/hover/focus/active/disabled/loading). Semantic HTML used. |
+| 6. Motion | `prefers-reduced-motion` respected. No `linear` easing. No `> 500ms` UI transitions. |
+| 7. Elevation | Shadow tokens defined. Z-index uses named scale (no `9999`). |
+| 8. Do's and Don'ts | Each Do/Don't from DESIGN.md checked against code. None violated. |
+| 9. Responsive | Layout tested at 375/768/1024/1440px. No horizontal scroll. Prose max-width 65ch. |
+| 10. Anti-Patterns | No AI slop: no `#6366F1`, no `font-weight: 500` everywhere, no missing states, no `animate-bounce` on static, no 3+ font families, no `rgba(0,0,0)` shadows. |
-### Step 2: Show Output
+### Step 2: Show the Tool Output
-Paste the `designer_verify_implementation` output block representing PASS on all 10 sections.
+```
+โ
DESIGN.md Compliance Check:
+
+Section 1 (Theme): PASS
+Section 2 (Colors): PASS - all OKLCH tokens present
+Section 3 (Typography): PASS - fonts loaded, scale defined
+Section 4 (Spacing): PASS - 4px grid enforced
+Section 5 (Components): PASS - all states present
+Section 6 (Motion): PASS - prefers-reduced-motion respected
+Section 7 (Elevation): PASS - shadow/z-index tokens used
+Section 8 (Do's/Don'ts): PASS - no violations
+Section 9 (Responsive): PASS - tested at all breakpoints
+Section 10 (Anti-Patterns): PASS - no slop detected
+```
### Step 3: Handle Failures
-Any required section fails โ DO NOT claim completion.
-- Option A: Fix code.
-- Option B: Escalate to `designer` to revise the design contract.
+Any section fails โ do NOT claim completion.
+- Option A: Fix the code to pass the check
+- Option B: Escalate to `hyperstack:designer` to revise DESIGN.md if the design was wrong
-No design contract on a task that requires one? Stop. Invoke `designer`.
+DESIGN.md doesn't exist for a visual task โ process failure upstream. Stop and invoke `hyperstack:designer` before shipping anything.
+## Red Flags - STOP
-## Lifecycle
+| Thought | Reality |
+|---|---|
+| "Should work now" | "Should" โ evidence. Run the command. |
+| "I'm confident in this change" | Confidence โ evidence. Run the command. |
+| "Subagent said it's done" | Subagents lie. Check the VCS diff. Run the tests. |
+| "Minor change, no need to recheck" | Minor changes cause regressions. Run the command. |
+| "Tests were passing before my change" | Irrelevant. Run them again now. |
+| "MCP tool confirmed the pattern" | Confirms the pattern, not that your code is correct. Run the command. |
+| "I'll verify after I push" | After you push it's in CI. Verify BEFORE. |
+| "I already ran it earlier this conversation" | State drifts. Run it again. |
+| "The linter is passing" | Linter โ compiler โ runtime. Run the full verification. |
+| "Partial check is enough" | Partial verification = theater. Full check. |
+| "I ran the tests, they passed" (no output shown) | Evidence not shown = claim not made. Paste the full output. |
+| "The code looks correct" (claiming DESIGN.md compliance) | Looks โ verification. Run `designer_verify_implementation`. Show tool output. |
+| "I did the DESIGN.md checks manually" | Manual checks miss edge cases. Use the automated tool. |
+| "DESIGN.md doesn't exist yet, I'll implement first" | DESIGN.md is a blocker. Invoke `hyperstack:designer` before writing CSS/components. |
+| "Just this once" | No exceptions. |
+
+## Integration
+
+Run this skill before:
+- Any `git commit` or PR creation
+- Marking any task as complete
+- Reporting status to the user
+- Claiming a bug is fixed
+- Handing work off to a subagent or reviewer
+- Transitioning between phases in `hyperstack:engineering-discipline`
+
+
+## Lifecycle Integration
+
+### Agent Workflow Chains
**All execution paths converge here:**
-`[autonomous-mode | subagent-ops | engineering-discipline] โ ship-gate (THIS) โ deliver`
+```
+[autonomous-mode | subagent-ops | engineering-discipline] โ ship-gate (THIS) โ deliver
+```
+
+**DESIGN.md compliance (visual/UX only):**
+```
+ship-gate โ designer_verify_implementation โ [PASS โ deliver | FAIL โ fix or escalate]
+```
-**Design-contract flow:**
-`ship-gate โ designer_verify_implementation (when design contract required) โ [PASS โ deliver | FAIL โ fix/escalate]`
+### Upstream Dependencies
+- `autonomous-mode` โ final gate before delivery
+- `subagent-ops` โ final gate after all tasks
+- `engineering-discipline` โ per-task + final gate
-**Escalations:**
-- Design-contract check fails โ `designer`
-- Tests fail โ `debug-discipline`
-- Type/lint errors โ Fix โ re-run ship-gate
+### Downstream Consumers
+- `deliver` โ only proceeds if ship-gate passes
+
+### Escalation Paths
+| Failure | Escalate to | Action |
+|---|---|---|
+| DESIGN.md compliance fails | `designer` | Fix code or revise DESIGN.md |
+| Tests fail | `debug-discipline` โ fix โ re-run ship-gate |
+| Type/lint errors | Fix โ re-run ship-gate |
diff --git a/skills/subagent-ops/SKILL.md b/skills/subagent-ops/SKILL.md
index b7fda99..ca0eb17 100644
--- a/skills/subagent-ops/SKILL.md
+++ b/skills/subagent-ops/SKILL.md
@@ -103,6 +103,17 @@ Good subagent prompts are:
Bad: "Fix the tests" (too broad)
Good: "Fix the 3 failing tests in `src/flow/nodes.test.ts`. Root cause is [X]. Expected: all pass. Return: summary of changes."
+## Red Flags - STOP
+
+| Thought | Reality |
+|---|---|
+| "I'll dispatch multiple implementers in parallel" | They'll conflict on shared files. One at a time. |
+| "Skip spec review, the code looks fine" | Spec drift is invisible without review. |
+| "Start code quality before spec compliance" | Wrong order. Spec first, quality second. |
+| "Subagent said done, move on" | Verify with review. Trust but verify. |
+| "I'll fix it myself instead of re-dispatching" | Context pollution. Dispatch a fix subagent. |
+| "This task is too small for the full cycle" | Small tasks still get spec + quality review. |
+| "Let the subagent read the plan file" | Provide full text. File reads waste subagent context. |
## Integration
diff --git a/skills/test-first/SKILL.md b/skills/test-first/SKILL.md
index a59dc4f..71e0a1e 100644
--- a/skills/test-first/SKILL.md
+++ b/skills/test-first/SKILL.md
@@ -6,6 +6,15 @@ description: Use when implementing any feature, bug fix, or behaviour change - b
# Test-First Development
+## The Iron Law
+
+```
+NO PRODUCTION CODE WITHOUT A FAILING TEST FIRST.
+```
+
+Wrote code before the test? Delete it. Start over. Don't keep it as "reference." Don't "adapt" it while writing tests. Delete means delete.
+
+Violating the letter = violating the spirit.
## When to Use
@@ -96,10 +105,20 @@ Before marking work complete:
Can't check all boxes? โ You skipped TDD. Start over.
-## The Rule
-
-No production code without a failing test first. Delete code written before the test โ don't keep it as "reference".
+## Red Flags - STOP
+| Thought | Reality |
+|---|---|
+| "Too simple to test" | Simple code breaks. Test takes 30 seconds. |
+| "I'll test after" | Tests passing immediately prove nothing. |
+| "Tests after achieve the same goals" | Tests-after = "what does this do?" Tests-first = "what should this do?" |
+| "I already manually tested it" | Ad-hoc โ systematic. No record, can't re-run. |
+| "Deleting X hours of work is wasteful" | Sunk cost fallacy. Keeping unverified code = debt. |
+| "Keep as reference, write tests first" | You'll adapt it. That's testing after. Delete means delete. |
+| "Need to explore first" | Fine. Throw away exploration, then start with TDD. |
+| "Test is hard to write = skip it" | Hard to test = hard to use. Simplify the interface. |
+| "TDD will slow me down" | TDD is faster than debugging. Always. |
+| "This is different because..." | No it isn't. Write the test. |
## Integration
diff --git a/skills/testing-skills/SKILL.md b/skills/testing-skills/SKILL.md
index 8a0b83b..4e7d5a6 100644
--- a/skills/testing-skills/SKILL.md
+++ b/skills/testing-skills/SKILL.md
@@ -6,6 +6,15 @@ description: Use when creating or editing Hyperstack skills, before shipping the
# Testing Skills With Subagents
+## The Iron Law
+
+```
+NO SKILL SHIPS WITHOUT SUBAGENT PRESSURE TEST EVIDENCE
+```
+
+A skill that has not been tested against an adversarial subagent is a paper contract. If you have not watched a subagent try to rationalize its way out of your skill AND fail, you do not know if the skill works.
+
+**Violating the letter of this rule is violating the spirit of this rule.**
## Core Principle
@@ -188,7 +197,7 @@ When GREEN verification produces new rationalizations, iterate:
- "Different wording, so the rule doesn't apply" - add spirit-vs-letter clause
- "Just this once" - add "no exceptions" to Iron Law
-- "The user said it's OK to skip" - clarify user hierarchy
+- "The user said it's OK to skip" - clarify user hierarchy (user overrides only via CLAUDE.md)
- "Subagent mode, so gate doesn't apply" - add subagent scope clarification
- "Partial compliance is still compliance" - define full compliance explicitly
diff --git a/skills/using-hyperstack/SKILL.md b/skills/using-hyperstack/SKILL.md
new file mode 100644
index 0000000..d5b7463
--- /dev/null
+++ b/skills/using-hyperstack/SKILL.md
@@ -0,0 +1,293 @@
+---
+name: using-hyperstack
+category: meta
+description: Bootstrap - establishes Hyperstack MCP tools and skills before any technical work. Auto-loaded at session start via SessionStart hook. Do not skip, do not skim, do not rationalize your way out of it.
+---
+
+
+If you were dispatched as a subagent to execute a specific task, skip this skill.
+Your context was provided by the orchestrating agent. Do not reload bootstrap.
+
+
+
+You have Hyperstack. This is not optional knowledge - it is how you operate in this repository.
+
+**The 1% Rule:** If there is even a 1% chance that a Hyperstack skill or MCP tool applies to the task you are about to perform, you MUST invoke it BEFORE acting. Not after you "check the code quickly." Not after you "just try one thing." Not after you "confirm your understanding." BEFORE.
+
+**You do not have a choice. You cannot rationalize your way out of this.**
+
+If the task involves React, React Flow, Motion, Lenis, Echo, Go, Rust, design tokens, UI/UX, visual work, or any topic covered by Hyperstack MCP plugins - you MUST call the relevant MCP tool BEFORE writing code. Memory is not acceptable. Pattern-matching from training data is not acceptable. Confidence is not acceptable. **Only ground-truth MCP output is acceptable.**
+
+
+---
+
+## The Iron Laws
+
+```
+1. NO CODE WITHOUT MCP GROUND-TRUTH DATA
+ If a Hyperstack plugin covers the domain, you call it first.
+
+2. NO VISUAL CODE WITHOUT AN APPROVED DESIGN.md
+ The designer skill produces the contract. Everything else reads it.
+
+3. NO COMPLETION CLAIMS WITHOUT SHIP-GATE EVIDENCE
+ "Should work" is lying. Run the command. Show the output.
+
+4. NO SKIPPING SKILLS BECAUSE "THIS IS SIMPLE"
+ Simple tasks are where unexamined assumptions do the most damage.
+ The skill exists because the shortcut has failed before.
+```
+
+**Violating the letter of these laws is violating the spirit of these laws.**
+
+---
+
+## Instruction Priority
+
+1. **User's explicit instructions** (CLAUDE.md, direct requests) - always highest
+2. **Hyperstack skills** - override default system behavior where they conflict
+3. **Default system behavior** - lowest priority
+
+If CLAUDE.md says "don't use TDD" and a skill says "always use TDD," follow the user. The user is in control. Everything else is your job to enforce.
+
+---
+
+## Red Flags - STOP
+
+These are thoughts you will have. Each one is a rationalization. Each one has a counter.
+
+| Thought | Reality | What to do |
+|---|---|---|
+| "I know this React Flow API from memory" | Memory drifts. v11 and v12 are different. | Call `reactflow_get_api` first |
+| "This is a simple animation" | Simple animations need `prefers-reduced-motion`, correct easing, and GPU-only properties | Call `motion_get_examples` first |
+| "Go error handling is straightforward" | Straightforward code is where anti-patterns ship | Call `golang_get_practice` first |
+| "I'll check docs after I write it" | You will ship before you check. Every time. | Docs BEFORE code. Always. |
+| "I know the OKLCH token pattern" | OKLCH has specific rules about alpha, chroma peaks, dark mode lightness | Call `design_tokens_get_procedure` first |
+| "This pattern looks common, I'll adapt it" | Adaptation hides drift | Call the MCP tool. Copy from ground truth. |
+| "The user is impatient, I'll skip the gate" | User impatience is not permission to ship slop | Gates are not optional |
+| "This doesn't count as visual work" | If it looks, moves, or is interacted with โ visual | Invoke designer skill |
+| "I'll verify after I commit" | The verification step exists because "after" never comes | Verify BEFORE claim |
+| "Subagent said it's done" | Subagents lie | Check the diff. Run the tests. |
+| "Just this one time" | There is no "just this one time" | No exceptions |
+| "I'll write the test after" | No. Write it before. | `NO PRODUCTION CODE WITHOUT A FAILING TEST FIRST` |
+| "I already checked earlier in this conversation" | Check again. State drifts. | Fresh verification every time |
+| "The file is too small to need a design" | Small files with wrong decisions ship to production | Every decision gets a gate |
+
+---
+
+## Layer 1: MCP Tools (Ground-Truth Data)
+
+Call these BEFORE writing any code for these stacks. **Memory is not acceptable.**
+
+| Namespace | Stack | Must-call-first tools |
+|---|---|---|
+| `designer_*` | Design decision engine | `designer_resolve_intent`, `designer_get_personality`, `designer_get_preset`, `designer_get_page_template`, `designer_get_font_pairing`, `designer_get_anti_patterns` |
+| `design_tokens_*` | Design token systems | `design_tokens_generate`, `design_tokens_get_category`, `design_tokens_get_gotchas` |
+| `ui_ux_*` | UI/UX principles | `ui_ux_get_principle`, `ui_ux_get_component_pattern`, `ui_ux_get_gotchas` |
+| `shadcn_*` (**only if shadcn chosen**) | shadcn/ui Base UI edition | `shadcn_get_rules` (first), `shadcn_get_composition`, `shadcn_get_component`, `shadcn_get_snippet`, `shadcn_list_components` |
+| `reactflow_*` | React Flow v12 | `reactflow_get_api`, `reactflow_search_docs`, `reactflow_get_pattern` |
+| `motion_*` | Motion for React v12 | `motion_get_api`, `motion_get_examples`, `motion_get_transitions` |
+| `lenis_*` | Lenis smooth scroll | `lenis_get_api`, `lenis_generate_setup`, `lenis_get_pattern` |
+| `react_*` | React 19 / Next.js | `react_get_pattern`, `react_get_constraints`, `react_search_docs` |
+| `echo_*` | Echo (Go HTTP) | `echo_get_recipe`, `echo_get_middleware`, `echo_decision_matrix` |
+| `golang_*` | Go best practices | `golang_get_practice`, `golang_get_pattern`, `golang_get_antipatterns` |
+| `rust_*` | Rust practices | `rust_get_practice`, `rust_cheatsheet`, `rust_search_docs` |
+
+### MCP Degraded Mode
+
+If MCP tools fail or are unavailable:
+1. Tell the user explicitly: "Hyperstack MCP server is unavailable - my answers will be less precise and I am flagging them as uncertain."
+2. Fall back to training data but FLAG every answer as uncertain.
+3. Never silently answer as if ground-truth data was used.
+4. Do not invent API shapes.
+
+---
+
+## Layer 2: Skills (Engineering Process)
+
+Use the `Skill` tool to load these before the relevant task type.
+
+**Full skill index:** See `skills/INDEX.md` - all skills grouped by category (core / domain / meta). Regenerate with `npm run skills:index` after adding or editing any skill.
+
+### Announcement Iron Law
+
+```
+BEFORE invoking any Hyperstack skill, announce it:
+"Using hyperstack:[skill-name] - [one-line purpose]"
+```
+
+This is non-negotiable. Silent skill invocations are invisible to the user and cannot be audited. **If you invoke a skill silently, you are lying by omission.**
+
+### Workflow Skills (invoke in this order for feature work)
+
+| Skill | When to invoke | Gate type |
+|---|---|---|
+| `hyperstack:blueprint` | Before any feature build - MCP survey, design gate, negative doubt | **HARD GATE** |
+| `hyperstack:designer` | Before any visual/UX work - produces DESIGN.md contract | **HARD GATE** |
+| `hyperstack:forge-plan` | After design approval - MCP-verified implementation plan | Requires approved design |
+| `hyperstack:run-plan` | Have an existing plan - validate then execute | Requires plan |
+| `hyperstack:engineering-discipline` | During execution - Senior SDE phase gates | Phase gates |
+| `hyperstack:ship-gate` | Before any completion claim - evidence required | **HARD GATE** |
+| `hyperstack:deliver` | After all tasks complete - final verification and delivery | Gate |
+
+### Execution Skills (invoke during implementation)
+
+| Skill | When to invoke |
+|---|---|
+| `hyperstack:autonomous-mode` | Full autonomous execution - runs end-to-end, only stops on failure |
+| `hyperstack:subagent-ops` | Plans with independent tasks - fresh agent per task, two-stage review |
+| `hyperstack:test-first` | Before writing any implementation code - red-green-refactor |
+| `hyperstack:worktree-isolation` | Before feature work - clean workspace isolation |
+| `hyperstack:code-review` | After completing tasks - dispatch reviewer subagent |
+| `hyperstack:parallel-dispatch` | 2+ independent failures or tasks - concurrent agent dispatch |
+
+### Support Skills (invoke when the situation calls for it)
+
+| Skill | When to invoke |
+|---|---|
+| `hyperstack:designer` | Before any visual/UX work - produces DESIGN.md |
+| `hyperstack:debug-discipline` | Any bug or unexpected behaviour - root cause first |
+| `hyperstack:behaviour-analysis` | UI/UX audits, state machine correctness |
+| `hyperstack:design-patterns-skill` | Selecting the right abstraction or design pattern |
+| `hyperstack:security-review` | OWASP audits, API and infrastructure security |
+| `hyperstack:readme-writer` | Evidence-based documentation |
+
+### Workflow Chain
+
+```
+New work: blueprint โ [designer if visual] โ forge-plan โ choose execution mode โ ship-gate โ deliver
+ โ โ
+ โ โโโ produces DESIGN.md (input to forge-plan)
+ โ
+Existing: run-plan โโโค
+ โ
+ โโ autonomous-mode (full auto, stops only on failure)
+ โโ subagent-ops (fresh agent per task, two-stage review)
+ โโ engineering-discipline (manual, human checkpoints)
+
+Before execution: worktree-isolation (clean workspace)
+Debugging: debug-discipline โ parallel-dispatch (if independent failures)
+```
+
+**Visual work branch:** If the task changes how something looks, feels, moves, or is interacted with, `blueprint` routes through `designer` BEFORE `forge-plan`. The DESIGN.md produced by designer becomes the input spec for forge-plan.
+
+**Skip designer when:** pure backend, single CSS bug fix, performance optimization with no visual change, infra/DevOps, documentation-only.
+
+For non-trivial tasks, follow the chain in order. Do not skip steps.
+
+**Platform tool equivalences:** See `skills/using-hyperstack/references/` for tool name mappings per harness.
+
+---
+
+## Internal Agent Harness
+
+Hyperstack now has internal roles. These roles are **internal and auto-called**.
+Users do not invoke them directly. The bootstrap and harness choose the correct
+role based on the request and lifecycle state.
+
+V1 keeps the current skills and MCP plugins as the execution substrate. The role
+harness is layered on top of them; it does not replace them yet.
+
+## Role Registry
+
+- `hyper` - conductor, classifier, gatekeeper, verifier, and delivery owner
+- `website-builder` - first specialist for website-facing design and
+ implementation work
+
+## Routing Summary
+
+- Every request enters through `hyper`
+- `hyper` inspects the workspace first: package manifests, dependency signals,
+ and likely core files for the affected surface
+- `hyper -> website-builder` for website-facing work: landing pages, dashboards,
+ marketing pages, redesigns, page structure, CTA hierarchy, form friction,
+ trust signals, and website-experience-heavy UI work
+- `website-builder -> hyper` after specialist output is ready for review and
+ verification
+- If classification is ambiguous, stay in `hyper`
+
+## Allowed Transitions
+
+- `user request -> hyper`
+- `hyper -> website-builder`
+- `website-builder -> hyper`
+- `hyper -> existing Hyperstack skills/plugins`
+- `hyper -> verification and delivery gates`
+
+## Disallowed Transitions
+
+- `user request -> website-builder`
+- `website-builder -> ship`
+- `website-builder -> deliver`
+- `website-builder` claiming final completion directly
+
+---
+
+## The Rationalization Catalog (Read Before Every Session)
+
+These are the exact thoughts you will have when you want to skip a skill. Every one is a bug in your reasoning. Every one has been written down because someone (probably you in a past session) used it to ship bad code.
+
+### Skill-skipping rationalizations
+
+| Thought | Why it's wrong |
+|---|---|
+| "This is just a question, not a task" | Questions are tasks. They lead to answers that become code. Check for skills. |
+| "I need context first, then I'll use skills" | Skills tell you HOW to gather context. Check first. |
+| "Let me quickly look at the files" | "Quickly" is the word you use when you're skipping the gate. |
+| "I remember this pattern" | Remembering is not the same as using the current tool output. Memory drifts. |
+| "This doesn't need a formal skill, it's obvious" | If it's obvious, invoking the skill takes 10 seconds. Do it. |
+| "The skill is overkill for this" | You don't get to decide what's overkill. The skill exists because the shortcut failed. |
+| "I'll just do one thing first then invoke the skill" | No. Skill invocation comes BEFORE the one thing. |
+| "I already have a mental model of this" | Mental models drift faster than you think. Refresh it from MCP. |
+
+### Verification-skipping rationalizations
+
+| Thought | Why it's wrong |
+|---|---|
+| "Should work now" | "Should" is not evidence. Run the command. |
+| "I'm confident in this change" | Confidence is not evidence. Run the command. |
+| "Minor change, no need to recheck" | Minor changes cause regressions. Run the command. |
+| "Tests were passing before my change" | Irrelevant. Run them again now. |
+| "The subagent reported success" | Subagents lie. Check the diff yourself. |
+| "I followed the pattern correctly" | Following the pattern is not the same as the pattern working. Run the command. |
+| "I'll verify after I push" | After you push it's in CI. Verify BEFORE. |
+| "Just this once" | There is no "just this once." |
+| "I'm tired" | Exhaustion is not an excuse. Stop and rest if you must, don't ship unverified. |
+| "Partial check is enough" | Partial verification is theater. Do the full check. |
+
+### The "I'm different" rationalization
+
+| Thought | Why it's wrong |
+|---|---|
+| "These rules are for general AI, I understand them better" | No. They were written for past sessions of you that also thought they understood. |
+| "The spirit of the rule allows this" | The letter of the rule is the spirit of the rule. |
+| "This specific case isn't covered" | Every case is covered by "use the skill or don't proceed." |
+| "I can adapt the process to be faster" | Fast is for after discipline is internalized. You are not there yet. |
+
+---
+
+## The One Rule That Governs All Rules
+
+> **If there is even a 1% chance a Hyperstack skill or MCP tool applies to what you are about to do, you ABSOLUTELY MUST invoke it first.**
+
+Not after. Not alongside. FIRST.
+
+If the skill turns out not to apply once you've loaded it, you don't have to use it. But you MUST check.
+
+---
+
+## Final Check Before Any Response
+
+Before you respond to the user's message, confirm:
+
+1. [ ] Did I check whether any Hyperstack skill applies to this task? (1% rule)
+2. [ ] Did I call any relevant MCP tool for ground-truth data? (memory is not acceptable)
+3. [ ] If this involves visual work, did I invoke designer BEFORE writing any code?
+4. [ ] If I'm claiming something is done, did I run the verification command THIS message?
+5. [ ] Did I announce every skill invocation with the exact format?
+
+If any answer is no, **stop and fix it before responding.**
+
+---
+
+**You have Hyperstack. Use it.**
diff --git a/skills/hyperstack/references/agent-tools.md b/skills/using-hyperstack/references/claude-code-tools.md
similarity index 89%
rename from skills/hyperstack/references/agent-tools.md
rename to skills/using-hyperstack/references/claude-code-tools.md
index 00ffe1d..f65e1ac 100644
--- a/skills/hyperstack/references/agent-tools.md
+++ b/skills/using-hyperstack/references/claude-code-tools.md
@@ -1,6 +1,6 @@
-# Code Tool Reference
+# Claude Code Tool Reference
-When skills reference tools
+When skills reference tools, use these in Claude Code:
| Operation | Tool |
|---|---|
diff --git a/skills/hyperstack/references/codex-tools.md b/skills/using-hyperstack/references/codex-tools.md
similarity index 100%
rename from skills/hyperstack/references/codex-tools.md
rename to skills/using-hyperstack/references/codex-tools.md
diff --git a/skills/hyperstack/references/gemini-tools.md b/skills/using-hyperstack/references/gemini-tools.md
similarity index 100%
rename from skills/hyperstack/references/gemini-tools.md
rename to skills/using-hyperstack/references/gemini-tools.md
diff --git a/skills/worktree-isolation/SKILL.md b/skills/worktree-isolation/SKILL.md
index da855eb..555c9b5 100644
--- a/skills/worktree-isolation/SKILL.md
+++ b/skills/worktree-isolation/SKILL.md
@@ -24,10 +24,10 @@ ls -d .worktrees 2>/dev/null || ls -d worktrees 2>/dev/null
Found โ use it. Both exist โ `.worktrees` wins.
-### 2. Check Project Config / Agent Rules
+### 2. Check CLAUDE.md / Project Config
```bash
-grep -ri "worktree.*director" .cursorrules .roo/ .claude.json 2>/dev/null
+grep -i "worktree.*director" CLAUDE.md 2>/dev/null
```
Preference specified โ use it without asking.
@@ -92,7 +92,7 @@ echo "Worktree ready at . Tests: passing, 0 failures."
| `.worktrees/` exists | Use it (verify ignored) |
| `worktrees/` exists | Use it (verify ignored) |
| Both exist | Use `.worktrees/` |
-| Neither exists | Check project config, then ask user |
+| Neither exists | Check CLAUDE.md, then ask user |
| Directory not ignored | Add to `.gitignore` + commit |
| Baseline tests fail | Report failures, ask user |
| No package.json/Cargo.toml | Skip dependency install |
@@ -109,6 +109,14 @@ git worktree remove
# Leave worktree in place, report its location
```
+## Red Flags - STOP
+
+| Thought | Reality |
+|---|---|
+| "I'll just work on the main branch" | Dirty state โ mysterious failures. Isolate. |
+| "Worktree setup is overhead" | 30 seconds of setup prevents hours of state debugging. |
+| "I'll skip baseline tests" | Won't know if failures are yours or pre-existing. |
+| "The directory doesn't need to be ignored" | One `git add .` and the worktree is in your repo. |
## Integration
diff --git a/src/adapters/local-tools/index.ts b/src/adapters/local-tools/index.ts
deleted file mode 100644
index a960d0b..0000000
--- a/src/adapters/local-tools/index.ts
+++ /dev/null
@@ -1,24 +0,0 @@
-import { readFileSync } from "node:fs";
-import { dirname, resolve } from "node:path";
-import { fileURLToPath, pathToFileURL } from "node:url";
-import { invokeRegisteredTool } from "../../engine/tool-bridge.js";
-
-const adapterDir = dirname(fileURLToPath(import.meta.url));
-const repoRoot = resolve(adapterDir, "../../..");
-const registryPath = resolve(repoRoot, "generated", "tool-index", "local-tool-registry.json");
-
-function readRegistry(): Record {
- return JSON.parse(readFileSync(registryPath, "utf8")) as Record;
-}
-
-export async function invokeLocalTool(name: string, args: Record) {
- const registry = readRegistry();
- const importPath = registry[name];
- if (!importPath) {
- throw new Error(`Unknown local tool: ${name}`);
- }
-
- const resolvedImportPath = resolve(repoRoot, importPath);
- const module = await import(pathToFileURL(resolvedImportPath).href);
- return invokeRegisteredTool(module.register, args);
-}
diff --git a/src/cli.ts b/src/cli.ts
deleted file mode 100644
index f689e45..0000000
--- a/src/cli.ts
+++ /dev/null
@@ -1,99 +0,0 @@
-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 { positional, flags } = parseArgs(process.argv.slice(2));
- const [command, ...rest] = positional;
- const topology = loadTopology(process.cwd());
-
- if (command === "tool") {
- 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;
- const result = await invokeLocalTool(toolName, args);
- process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
- process.exit(0);
- }
-
- if (command === "route") {
- 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" && 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);
- }
-
- usage();
-}
-
-main().catch((error) => {
- process.stderr.write(`${error instanceof Error ? error.message : String(error)}\n`);
- process.exit(1);
-});
diff --git a/src/engine/artifact-loader.ts b/src/engine/artifact-loader.ts
deleted file mode 100644
index c137985..0000000
--- a/src/engine/artifact-loader.ts
+++ /dev/null
@@ -1,9 +0,0 @@
-import type { ArtifactContract, LoadedTopology } from "./contracts.js";
-
-export function getArtifactContract(topology: LoadedTopology, artifactId: string): ArtifactContract {
- const artifact = topology.artifacts.find((entry) => entry.id === artifactId);
- if (!artifact) {
- throw new Error(`Unknown artifact contract: ${artifactId}`);
- }
- return artifact;
-}
diff --git a/src/engine/artifact-validator.ts b/src/engine/artifact-validator.ts
deleted file mode 100644
index d8a43ee..0000000
--- a/src/engine/artifact-validator.ts
+++ /dev/null
@@ -1,18 +0,0 @@
-import type { LoadedTopology } from "./contracts.js";
-import { getArtifactContract } from "./artifact-loader.js";
-
-export function validateArtifactPayload(
- topology: LoadedTopology,
- artifactId: string,
- payload: Record,
-) {
- const contract = getArtifactContract(topology, artifactId);
- const missingFields = contract.requiredFields.filter((field) => !(field in payload));
-
- return {
- ok: missingFields.length === 0,
- artifactId,
- proofMode: contract.proofMode,
- missingFields,
- };
-}
diff --git a/src/engine/contracts.ts b/src/engine/contracts.ts
deleted file mode 100644
index 5e1ee59..0000000
--- a/src/engine/contracts.ts
+++ /dev/null
@@ -1,66 +0,0 @@
-export interface TopologyRoot {
- version: 1;
- defaultTransport: "local-tools";
- entryAgent: string;
- domains: string[];
- agents: string[];
- bundles: string[];
- artifacts: string[];
-}
-
-export interface DomainPolicy {
- id: string;
- writePolicy: "constrained" | "direct" | "policy_only";
- completionProof: string;
- truthBundles: string[];
- requiredGates: string[];
- optionalGates: string[];
- forbiddenBundles: string[];
-}
-
-export interface AgentPolicy {
- id: string;
- kind: "orchestrator" | "specialist" | "cross-domain";
- domains: string[];
- allowedSkills: string[];
- allowedBundles: string[];
- forbiddenBundles: string[];
- handoffIn: string;
- handoffOut: string;
- completionProof: string;
-}
-
-export interface BundlePolicy {
- id: string;
- domain: string;
- capabilities: string[];
- sources: string[];
- toolPrefixes: string[];
- outputContracts: string[];
-}
-
-export interface ArtifactContract {
- id: string;
- requiredFields: string[];
- proofMode: string;
-}
-
-export interface RouteDefaults {
- defaultAgent: string;
- requiresWorkspaceInventory: boolean;
- domainPreference: Record;
- crossDomainAgent: string;
- designContractRequiredWhen: string[];
- strictestProofOrder: string[];
-}
-
-export interface LoadedTopology {
- version: 1;
- defaultTransport: "local-tools";
- entryAgent: string;
- domains: DomainPolicy[];
- agents: AgentPolicy[];
- bundles: BundlePolicy[];
- artifacts: ArtifactContract[];
- routeDefaults: RouteDefaults;
-}
diff --git a/src/engine/policy.ts b/src/engine/policy.ts
deleted file mode 100644
index 51031ae..0000000
--- a/src/engine/policy.ts
+++ /dev/null
@@ -1,65 +0,0 @@
-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);
- if (!agent) {
- throw new Error(`Unknown agent: ${agentId}`);
- }
- return agent;
-}
-
-export function getDomain(topology: LoadedTopology, domainId: string): DomainPolicy {
- const domain = topology.domains.find((entry) => entry.id === domainId);
- if (!domain) {
- throw new Error(`Unknown domain: ${domainId}`);
- }
- 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) }))
- .filter((entry) => entry.index >= 0)
- .sort((left, right) => right.index - left.index);
-
- if (ranked.length === 0) {
- throw new Error(`No proof mode could be ranked from: ${proofModes.join(", ")}`);
- }
-
- return ranked[0].mode;
-}
diff --git a/src/engine/router.ts b/src/engine/router.ts
deleted file mode 100644
index 346ea95..0000000
--- a/src/engine/router.ts
+++ /dev/null
@@ -1,59 +0,0 @@
-import type { LoadedTopology } from "./contracts.js";
-import { getAgent, getDomain, getStrictestProofMode } from "./policy.js";
-
-function shouldRequireDesignContract(input: {
- changeClassification: string;
- workspaceInventory: { projectMode: "greenfield" | "existing"; existingPatterns: string[] };
-}): boolean {
- if (input.changeClassification !== "frontend_visual") {
- return false;
- }
-
- return (
- input.workspaceInventory.projectMode === "greenfield" ||
- input.workspaceInventory.existingPatterns.length === 0
- );
-}
-
-export function routeRequest(
- topology: LoadedTopology,
- input: {
- requestId: string;
- domainTargets: string[];
- capabilityTargets: string[];
- workspaceInventory: {
- projectMode: "greenfield" | "existing";
- existingPatterns: string[];
- };
- changeClassification: string;
- },
-) {
- const uniqueDomains = [...new Set(input.domainTargets)];
- const routeAgentId =
- uniqueDomains.length > 1
- ? topology.routeDefaults.crossDomainAgent
- : topology.routeDefaults.domainPreference[uniqueDomains[0] ?? "shared"] ?? topology.routeDefaults.defaultAgent;
-
- const agent = getAgent(topology, routeAgentId);
- const proofMode = getStrictestProofMode(
- topology.routeDefaults.strictestProofOrder,
- uniqueDomains.map((domainId) => getDomain(topology, domainId).completionProof),
- );
-
- const requiredArtifacts = ["workspace_inventory", "task_handoff"];
- const requiresDesignContract = shouldRequireDesignContract(input);
-
- if (requiresDesignContract) {
- requiredArtifacts.push("design_contract");
- }
-
- return {
- requestId: input.requestId,
- agent,
- proofMode,
- domains: uniqueDomains,
- capabilityTargets: input.capabilityTargets,
- requiredArtifacts,
- requiresDesignContract,
- };
-}
diff --git a/src/engine/tool-bridge.ts b/src/engine/tool-bridge.ts
deleted file mode 100644
index 15e13e1..0000000
--- a/src/engine/tool-bridge.ts
+++ /dev/null
@@ -1,27 +0,0 @@
-import assert from "node:assert/strict";
-import type { ToolServer } from "../shared/tool-types.js";
-
-type ToolResult = {
- content?: Array<{ type?: string; text?: string }>;
- isError?: boolean;
-};
-
-type ToolHandler = (args: Record) => Promise | ToolResult;
-
-export async function invokeRegisteredTool(
- register: (server: ToolServer) => void,
- args: Record,
-): Promise {
- let capturedHandler: ToolHandler | undefined;
-
- const server = {
- tool(_name: string, _description: string, _schema: unknown, handler: ToolHandler) {
- capturedHandler = handler;
- },
- } as unknown as ToolServer;
-
- register(server);
-
- assert.ok(capturedHandler, "tool registration did not capture a handler");
- return await capturedHandler(args);
-}
diff --git a/src/engine/topology-loader.ts b/src/engine/topology-loader.ts
deleted file mode 100644
index 7e5bc0c..0000000
--- a/src/engine/topology-loader.ts
+++ /dev/null
@@ -1,170 +0,0 @@
-import { readFileSync } from "node:fs";
-import { join } from "node:path";
-import YAML from "yaml";
-import type {
- AgentPolicy,
- ArtifactContract,
- BundlePolicy,
- DomainPolicy,
- LoadedTopology,
- RouteDefaults,
- TopologyRoot,
-} from "./contracts.js";
-
-interface RootDocument {
- version: 1;
- default_transport: "local-tools";
- entry_agent: string;
- domains: string[];
- agents: string[];
- bundles: string[];
- artifacts: string[];
-}
-
-interface DomainDocument {
- id: string;
- write_policy: DomainPolicy["writePolicy"];
- completion_proof: string;
- truth_bundles: string[];
- required_gates: string[];
- optional_gates: string[];
- forbidden_bundles: string[];
-}
-
-interface AgentDocument {
- id: string;
- kind: AgentPolicy["kind"];
- domains: string[];
- allowed_skills: string[];
- allowed_bundles: string[];
- forbidden_bundles: string[];
- handoff_in: string;
- handoff_out: string;
- completion_proof: string;
-}
-
-interface BundleDocument {
- id: string;
- domain: string;
- capabilities: string[];
- sources: string[];
- tool_prefixes: string[];
- output_contracts: string[];
-}
-
-interface ArtifactDocument {
- id: string;
- required_fields: string[];
- proof_mode: string;
-}
-
-interface RouteDefaultsDocument {
- default_agent: string;
- requires_workspace_inventory: boolean;
- domain_preference: Record;
- cross_domain_agent: string;
- design_contract_required_when: string[];
- strictest_proof_order: string[];
-}
-
-function readYaml(filePath: string): T {
- return YAML.parse(readFileSync(filePath, "utf8")) as T;
-}
-
-function mapRootDocument(root: RootDocument): TopologyRoot {
- return {
- version: root.version,
- defaultTransport: root.default_transport,
- entryAgent: root.entry_agent,
- domains: root.domains,
- agents: root.agents,
- bundles: root.bundles,
- artifacts: root.artifacts,
- };
-}
-
-function mapDomainDocument(domain: DomainDocument): DomainPolicy {
- return {
- id: domain.id,
- writePolicy: domain.write_policy,
- completionProof: domain.completion_proof,
- truthBundles: domain.truth_bundles,
- requiredGates: domain.required_gates,
- optionalGates: domain.optional_gates,
- forbiddenBundles: domain.forbidden_bundles,
- };
-}
-
-function mapAgentDocument(agent: AgentDocument): AgentPolicy {
- return {
- id: agent.id,
- kind: agent.kind,
- domains: agent.domains,
- allowedSkills: agent.allowed_skills,
- allowedBundles: agent.allowed_bundles,
- forbiddenBundles: agent.forbidden_bundles,
- handoffIn: agent.handoff_in,
- handoffOut: agent.handoff_out,
- completionProof: agent.completion_proof,
- };
-}
-
-function mapBundleDocument(bundle: BundleDocument): BundlePolicy {
- return {
- id: bundle.id,
- domain: bundle.domain,
- capabilities: bundle.capabilities,
- sources: bundle.sources,
- toolPrefixes: bundle.tool_prefixes,
- outputContracts: bundle.output_contracts,
- };
-}
-
-function mapArtifactDocument(doc: ArtifactDocument): ArtifactContract {
- return {
- id: doc.id,
- requiredFields: doc.required_fields,
- proofMode: doc.proof_mode,
- };
-}
-
-function mapRouteDefaultsDocument(doc: RouteDefaultsDocument): RouteDefaults {
- return {
- defaultAgent: doc.default_agent,
- requiresWorkspaceInventory: doc.requires_workspace_inventory,
- domainPreference: doc.domain_preference,
- crossDomainAgent: doc.cross_domain_agent,
- designContractRequiredWhen: doc.design_contract_required_when,
- strictestProofOrder: doc.strictest_proof_order,
- };
-}
-
-export function loadTopology(repoRoot: string): LoadedTopology {
- const root = mapRootDocument(readYaml(join(repoRoot, "topology", "manifest.yaml")));
- const domains = root.domains.map((id) =>
- mapDomainDocument(readYaml(join(repoRoot, "topology", "domains", `${id}.yaml`))),
- );
- const agents = root.agents.map((id) =>
- mapAgentDocument(readYaml(join(repoRoot, "topology", "agents", `${id}.yaml`))),
- );
- const bundles = root.bundles.map((id) =>
- mapBundleDocument(readYaml(join(repoRoot, "topology", "bundles", `${id}.yaml`))),
- );
- const artifacts = root.artifacts.map((id) =>
- mapArtifactDocument(readYaml(join(repoRoot, "topology", "artifacts", `${id}.yaml`))),
- );
- const routeDefaults = mapRouteDefaultsDocument(
- readYaml(join(repoRoot, "topology", "routes", "defaults.yaml")),
- );
-
- return {
- version: root.version,
- defaultTransport: root.defaultTransport,
- entryAgent: root.entryAgent,
- domains,
- agents,
- bundles,
- artifacts,
- routeDefaults,
- };
-}
diff --git a/src/index.ts b/src/index.ts
new file mode 100755
index 0000000..1b9b15a
--- /dev/null
+++ b/src/index.ts
@@ -0,0 +1,55 @@
+#!/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";
+
+const server = new McpServer({
+ name: "hyperstack",
+ version: "1.0.5",
+});
+
+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/compile-runtime-context.ts b/src/internal/compile-runtime-context.ts
index a8241e7..2c24897 100644
--- a/src/internal/compile-runtime-context.ts
+++ b/src/internal/compile-runtime-context.ts
@@ -10,7 +10,7 @@ const pluginRoot = join(scriptDir, "../..");
try {
const artifacts = compileContextArtifacts(pluginRoot);
- const source = readFileSync(join(pluginRoot, "skills", "hyperstack", "SKILL.md"), "utf8");
+ const source = readFileSync(join(pluginRoot, "skills", "using-hyperstack", "SKILL.md"), "utf8");
const { stats } = compileUsingHyperstackBootstrap(source);
process.stdout.write(
diff --git a/src/internal/context-compiler.ts b/src/internal/context-compiler.ts
index c27de98..b771a33 100644
--- a/src/internal/context-compiler.ts
+++ b/src/internal/context-compiler.ts
@@ -44,9 +44,9 @@ const REQUIRED_BOOTSTRAP_MARKERS = [
"MCP unavailable",
"announce it",
"hyper",
- "frontend-builder",
+ "website-builder",
"auto-called",
- "hyper -> frontend-builder",
+ "hyper -> website-builder",
];
function stripFrontmatter(source: string): string {
@@ -62,55 +62,17 @@ function extractTaggedBlock(source: string, tag: string): string {
return match[1].trim();
}
-function parseHeading(line: string): { level: number; text: string } | null {
- const match = line.trim().match(/^(#{1,6})\s+(.*)$/);
- if (!match) {
- return null;
- }
-
- return {
- level: match[1].length,
- text: match[2].trim(),
- };
-}
-
-function matchesHeadingText(text: string, heading: string): boolean {
- return (
- text === heading ||
- text.startsWith(`${heading} `) ||
- text.startsWith(`${heading}:`) ||
- text.startsWith(`${heading} -`)
- );
-}
-
-function extractSection(source: string, heading: string | string[]): string {
+function extractSection(source: string, heading: string): string {
const lines = source.split("\n");
- const headings = Array.isArray(heading) ? heading : [heading];
- let startIndex = -1;
- let startLevel = 0;
-
- for (let index = 0; index < lines.length; index += 1) {
- const headingInfo = parseHeading(lines[index] ?? "");
- if (!headingInfo) {
- continue;
- }
-
- if (headings.some((candidate) => matchesHeadingText(headingInfo.text, candidate))) {
- startIndex = index;
- startLevel = headingInfo.level;
- break;
- }
- }
-
+ const targetHeading = `## ${heading}`;
+ const startIndex = lines.findIndex((line) => line.trim() === targetHeading);
if (startIndex === -1) {
- throw new Error(`Could not extract section "${headings.join(", ")}" from source`);
+ throw new Error(`Could not extract section "${heading}" from source`);
}
-
const contentLines: string[] = [];
for (let index = startIndex + 1; index < lines.length; index += 1) {
const line = lines[index] ?? "";
- const headingInfo = parseHeading(line);
- if (headingInfo && headingInfo.level <= startLevel) {
+ if (line.startsWith("## ")) {
break;
}
contentLines.push(line);
@@ -153,7 +115,7 @@ function compactNamespaceTable(section: string): string[] {
}
function compactWorkflowTables(source: string): string[] {
- const workflowSection = extractSection(source, ["Layer 2: Skills (Engineering Process)", "Layer 2: When to Invoke Skills"]);
+ const workflowSection = extractSection(source, "Layer 2: Skills (Engineering Process)");
const rows = parseMarkdownTable(workflowSection).filter((row) => row.cells.length >= 2);
return rows
@@ -161,40 +123,8 @@ function compactWorkflowTables(source: string): string[] {
.map((row) => `- ${row.cells[0]}: ${row.cells[1]}`);
}
-function extractInternalAgents(source: string): string[] {
- const section = extractSection(source, [
- "Layer 3: Agents (Orchestration & Routing)",
- "Layer 3: Orchestration (Agents)",
- "Role Registry Details",
- "Role Registry",
- "Internal Agents",
- ]);
-
- const lines = section.split("\n").map((line) => line.trim()).filter(Boolean);
- const agents = parseMarkdownTable(section).filter((row) => row.cells.length >= 2);
- const agentNames = agents.map((row) => row.cells[0].replace(/^`|`$/g, ""));
- const output: string[] = [];
-
- if (lines.some((line) => /auto-(invoked|called)/i.test(line))) {
- output.push("- Internal roles are auto-called, not user-facing.");
- }
-
- if (agentNames.includes("hyper") && agentNames.includes("frontend-builder")) {
- output.push("- hyper -> frontend-builder");
- }
-
- output.push(
- ...agents.map((row) => {
- const name = row.cells[0].replace(/^`|`$/g, "");
- return `- \`${name}\`: ${row.cells[1]}`;
- }),
- );
-
- return output;
-}
-
function extractFinalCheck(source: string): string[] {
- const finalSection = extractSection(source, ["Final Check Before Any Response", "Final Response Check"]);
+ const finalSection = extractSection(source, "Final Check Before Any Response");
return finalSection
.split("\n")
.map((line) => line.trim())
@@ -203,7 +133,7 @@ function extractFinalCheck(source: string): string[] {
}
function extractRedFlags(source: string): string[] {
- const redFlagsSection = extractSection(source, ["Red Flags - STOP", "High-Signal Red Flags"]);
+ const redFlagsSection = extractSection(source, "Red Flags - STOP");
const rows = parseMarkdownTable(redFlagsSection).filter((row) => row.cells.length >= 2);
return rows.slice(0, 6).map((row) => `- ${row.cells[0]} -> ${row.cells[1]}`);
}
@@ -237,13 +167,15 @@ export function compileUsingHyperstackBootstrap(source: string): { content: stri
const namespaces = compactNamespaceTable(extractSection(body, "Layer 1: MCP Tools (Ground-Truth Data)"));
const workflowSkills = compactWorkflowTables(body);
const instructionPriority = extractInstructionPriority(body);
- const roleRegistry = extractInternalAgents(body);
+ const roleRegistry = extractSimpleBullets(extractSection(body, "Role Registry"));
+ const routingSummary = extractSimpleBullets(extractSection(body, "Routing Summary"));
+ const allowedTransitions = extractSimpleBullets(extractSection(body, "Allowed Transitions"));
const disallowedTransitions = extractSimpleBullets(extractSection(body, "Disallowed Transitions"));
const finalCheck = extractFinalCheck(body);
const redFlags = extractRedFlags(body);
const content = [
- "",
+ "",
"# Hyperstack Runtime Bootstrap",
"",
"## Critical",
@@ -263,10 +195,16 @@ export function compileUsingHyperstackBootstrap(source: string): { content: stri
"## Workflow Skills",
...workflowSkills,
"",
- "## Internal Agents",
+ "## Internal Roles",
"- Roles are internal and auto-called. Users do not invoke them directly.",
...roleRegistry,
"",
+ "## Routing Summary",
+ ...routingSummary,
+ "",
+ "## Allowed Transitions",
+ ...allowedTransitions,
+ "",
"## Disallowed Transitions",
...disallowedTransitions,
"",
@@ -298,8 +236,8 @@ export function validateUsingHyperstackBootstrap(content: string): string[] {
}
export function compileContextArtifacts(pluginRoot: string): ContextArtifact[] {
- const sourcePath = join(pluginRoot, "skills", "hyperstack", "SKILL.md");
- const outputPath = join(pluginRoot, "generated", "runtime-context", "hyperstack.bootstrap.md");
+ const sourcePath = join(pluginRoot, "skills", "using-hyperstack", "SKILL.md");
+ const outputPath = join(pluginRoot, "generated", "runtime-context", "using-hyperstack.bootstrap.md");
const source = readFileSync(sourcePath, "utf8");
const { content } = compileUsingHyperstackBootstrap(source);
diff --git a/src/internal/setup-hyperstack.ts b/src/internal/setup-hyperstack.ts
new file mode 100644
index 0000000..81aec3f
--- /dev/null
+++ b/src/internal/setup-hyperstack.ts
@@ -0,0 +1,244 @@
+import * as fs from "node:fs";
+import * as path from "node:path";
+import * as os from "node:os";
+
+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",
+ },
+ // 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.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" = "docker"
+): { 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,
+ },
+ },
+ };
+}
diff --git a/src/plugins/design-tokens/index.ts b/src/plugins/design-tokens/index.ts
new file mode 100644
index 0000000..b9f7c71
--- /dev/null
+++ b/src/plugins/design-tokens/index.ts
@@ -0,0 +1,24 @@
+import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.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: McpServer): 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/design-tokens/tools/generate.ts b/src/plugins/design-tokens/tools/generate.ts
index 3500050..58a86d9 100644
--- a/src/plugins/design-tokens/tools/generate.ts
+++ b/src/plugins/design-tokens/tools/generate.ts
@@ -1,4 +1,4 @@
-import type { ToolServer } from "../../../shared/tool-types.js";
+import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.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: ToolServer): void {
+export function register(server: McpServer): 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 9a227c5..6e07b58 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 { ToolServer } from "../../../shared/tool-types.js";
+import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";
import { TOKEN_CATEGORIES, getCategoryByName } from "../data.js";
-export function register(server: ToolServer): void {
+export function register(server: McpServer): 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 85b0643..9bdc709 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 { ToolServer } from "../../../shared/tool-types.js";
+import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";
import { COLOR_RAMPS, getRampByName } from "../data.js";
-export function register(server: ToolServer): void {
+export function register(server: McpServer): 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 d5dba5e..8257bfe 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 { ToolServer } from "../../../shared/tool-types.js";
+import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { getAllGotchas } from "../data.js";
-export function register(server: ToolServer): void {
+export function register(server: McpServer): 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 fdec648..e24b745 100644
--- a/src/plugins/design-tokens/tools/get-procedure.ts
+++ b/src/plugins/design-tokens/tools/get-procedure.ts
@@ -1,35 +1,8 @@
-import type { ToolServer } from "../../../shared/tool-types.js";
+import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";
import { TOKEN_PROCEDURES, getProcedureByStep } from "../data.js";
-function renderProcedure(proc: {
- step: number;
- title: string;
- description: string;
- code?: string;
- rules?: string[];
- gotchas?: string[];
-}): string {
- let text = `# Step ${proc.step}: ${proc.title}\n\n${proc.description}\n\n`;
-
- if (proc.code) {
- text += `## Code\n\`\`\`css\n${proc.code}\n\`\`\`\n\n`;
- }
-
- if (proc.rules && proc.rules.length > 0) {
- text += `## Rules\n`;
- for (const rule of proc.rules) text += `- ${rule}\n`;
- }
-
- if (proc.gotchas && proc.gotchas.length > 0) {
- text += `\n## Gotchas\n`;
- for (const gotcha of proc.gotchas) text += `- ${gotcha}\n`;
- }
-
- return text;
-}
-
-export function register(server: ToolServer): void {
+export function register(server: McpServer): void {
server.tool(
"design_tokens_get_procedure",
"Get the step-by-step token system build procedure. Steps 1-8 cover the full production workflow.",
@@ -37,7 +10,7 @@ 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 }: { step?: number }) => {
+ async ({ step }) => {
if (step !== undefined) {
const proc = getProcedureByStep(step);
if (!proc) {
@@ -46,7 +19,13 @@ export function register(server: ToolServer): void {
isError: true,
};
}
- return { content: [{ type: "text", text: renderProcedure(proc) }] };
+ let text = `# Step ${proc.step}: ${proc.title}\n\n${proc.description}\n\n`;
+ text += `## Code\n\`\`\`css\n${proc.code}\n\`\`\`\n\n`;
+ text += `## Rules\n`;
+ for (const rule of proc.rules) text += `- ${rule}\n`;
+ text += `\n## Gotchas\n`;
+ for (const gotcha of proc.gotchas) text += `- ${gotcha}\n`;
+ return { content: [{ type: "text", text }] };
}
let text = "# Design Token Build Procedure\n\n";
@@ -56,6 +35,6 @@ export function register(server: ToolServer): void {
}
text += "\nCall `design_tokens_get_procedure` with a step number for full code + rules.";
return { content: [{ type: "text", text }] };
- },
+ }
);
}
diff --git a/src/plugins/design-tokens/tools/list-categories.ts b/src/plugins/design-tokens/tools/list-categories.ts
index a818f93..324ebbb 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 { ToolServer } from "../../../shared/tool-types.js";
+import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";
import { TOKEN_CATEGORIES } from "../data.js";
-export function register(server: ToolServer): void {
+export function register(server: McpServer): 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 aee413f..3959e94 100644
--- a/src/plugins/design-tokens/tools/search.ts
+++ b/src/plugins/design-tokens/tools/search.ts
@@ -1,8 +1,8 @@
-import type { ToolServer } from "../../../shared/tool-types.js";
+import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";
import { searchTokens } from "../data.js";
-export function register(server: ToolServer): void {
+export function register(server: McpServer): 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
new file mode 100644
index 0000000..4c24d70
--- /dev/null
+++ b/src/plugins/designer/index.ts
@@ -0,0 +1,48 @@
+import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.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: McpServer): 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/designer/tools/generate-design-brief.ts b/src/plugins/designer/tools/generate-design-brief.ts
index 908711b..6aed85d 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 { ToolServer } from "../../../shared/tool-types.js";
+import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.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: ToolServer): void {
+export function register(server: McpServer): 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 8c37a92..e48cbd5 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 { ToolServer } from "../../../shared/tool-types.js";
+import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.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: ToolServer): void {
+export function register(server: McpServer): 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 2833124..91560fc 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 { ToolServer } from "../../../shared/tool-types.js";
+import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";
import { ANTI_PATTERN_CATEGORIES, INDUSTRY_CATEGORIES, getAntiPatterns } from "../data.js";
-export function register(server: ToolServer): void {
+export function register(server: McpServer): 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 fcf59ef..6a68d95 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 { ToolServer } from "../../../shared/tool-types.js";
+import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";
import { COGNITIVE_LAW_NAMES, getCognitiveLaw } from "../data.js";
-export function register(server: ToolServer): void {
+export function register(server: McpServer): 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 9fb13df..5335be5 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 { ToolServer } from "../../../shared/tool-types.js";
+import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";
import { COMPOSITION_TOPICS, getCompositionRule } from "../data.js";
-export function register(server: ToolServer): void {
+export function register(server: McpServer): 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 d54048e..57a49f9 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 { ToolServer } from "../../../shared/tool-types.js";
+import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";
import { DESIGN_SYSTEM_NAMES, getDesignSystem, CROSS_SYSTEM_CONVERGENCES } from "../data.js";
-export function register(server: ToolServer): void {
+export function register(server: McpServer): 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 2d3889f..3ca2fb8 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 { ToolServer } from "../../../shared/tool-types.js";
+import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";
import { FONT_MOODS, INDUSTRY_CATEGORIES, FONT_PAIRINGS, getFontPairings } from "../data.js";
-export function register(server: ToolServer): void {
+export function register(server: McpServer): 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 7c93b0b..3780dba 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 { ToolServer } from "../../../shared/tool-types.js";
+import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";
import { INDUSTRY_CATEGORIES, getIndustryRule } from "../data.js";
-export function register(server: ToolServer): void {
+export function register(server: McpServer): 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 2f9cc63..e8d8be9 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 { ToolServer } from "../../../shared/tool-types.js";
+import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";
import { INTERACTION_CATEGORIES, getInteractionPattern } from "../data.js";
-export function register(server: ToolServer): void {
+export function register(server: McpServer): 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 0955228..16ec2fb 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 { ToolServer } from "../../../shared/tool-types.js";
+import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";
import { LANDING_TOPICS, getLandingPattern } from "../data.js";
-export function register(server: ToolServer): void {
+export function register(server: McpServer): 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 d943ce0..d83ca9f 100644
--- a/src/plugins/designer/tools/get-page-template.ts
+++ b/src/plugins/designer/tools/get-page-template.ts
@@ -1,15 +1,15 @@
-import type { ToolServer } from "../../../shared/tool-types.js";
+import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";
import { PAGE_TYPES, getPageTemplate } from "../data.js";
-export function register(server: ToolServer): void {
+export function register(server: McpServer): 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.",
{
type: z.enum(PAGE_TYPES).describe("Page type to get template for"),
},
- async ({ type }: { type: (typeof PAGE_TYPES)[number] }) => {
+ async ({ type }) => {
const template = getPageTemplate(type);
if (!template) {
return {
@@ -23,27 +23,27 @@ export function register(server: ToolServer): void {
text += `**Layout:** ${template.layout}\n\n`;
text += `## Sections\n\n`;
- for (const section of template.sections ?? []) {
- text += `### ${section.name ?? "Untitled Section"}${section.required ? "" : " (optional)"}\n\n`;
+ for (const section of template.sections) {
+ text += `### ${section.name}${section.required ? "" : " (optional)"}\n\n`;
text += `**Components:**\n`;
- for (const comp of section.components ?? []) {
+ for (const comp of section.components) {
text += `- ${comp}\n`;
}
- text += `\n**Notes:** ${section.notes ?? ""}\n\n`;
+ text += `\n**Notes:** ${section.notes}\n\n`;
}
text += `## Apply These\n\n`;
- text += `**Cognitive Laws:** ${(template.cognitiveApply ?? []).join(", ")}\n`;
- text += `**Composition:** ${(template.compositionApply ?? []).join(", ")}\n`;
- text += `**Interaction Patterns:** ${(template.interactionApply ?? []).join(", ")}\n`;
- text += `**UX Writing:** ${(template.writingApply ?? []).join(", ")}\n\n`;
+ text += `**Cognitive Laws:** ${template.cognitiveApply.join(", ")}\n`;
+ text += `**Composition:** ${template.compositionApply.join(", ")}\n`;
+ text += `**Interaction Patterns:** ${template.interactionApply.join(", ")}\n`;
+ text += `**UX Writing:** ${template.writingApply.join(", ")}\n\n`;
text += `## Key Rules\n\n`;
- for (const rule of template.keyRules ?? []) {
+ for (const rule of template.keyRules) {
text += `- ${rule}\n`;
}
return { content: [{ type: "text" as const, text }] };
- },
+ }
);
}
diff --git a/src/plugins/designer/tools/get-personality.ts b/src/plugins/designer/tools/get-personality.ts
index 748a742..9ed3f04 100644
--- a/src/plugins/designer/tools/get-personality.ts
+++ b/src/plugins/designer/tools/get-personality.ts
@@ -1,8 +1,8 @@
-import type { ToolServer } from "../../../shared/tool-types.js";
+import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";
import { PERSONALITY_CLUSTERS, getPersonality } from "../data.js";
-export function register(server: ToolServer): void {
+export function register(server: McpServer): 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 14804f6..15a6d5e 100644
--- a/src/plugins/designer/tools/get-preset.ts
+++ b/src/plugins/designer/tools/get-preset.ts
@@ -1,8 +1,8 @@
-import type { ToolServer } from "../../../shared/tool-types.js";
+import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";
import { PRESET_NAMES, PRESETS, getPreset } from "../data.js";
-export function register(server: ToolServer): void {
+export function register(server: McpServer): 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 b550fa5..e17e586 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 { ToolServer } from "../../../shared/tool-types.js";
+import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";
import { WRITING_TOPICS, getWritingGuideline } from "../data.js";
-export function register(server: ToolServer): void {
+export function register(server: McpServer): 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 3bbf42e..8d4a1e1 100644
--- a/src/plugins/designer/tools/list-personalities.ts
+++ b/src/plugins/designer/tools/list-personalities.ts
@@ -1,7 +1,7 @@
-import type { ToolServer } from "../../../shared/tool-types.js";
+import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { PERSONALITIES } from "../data.js";
-export function register(server: ToolServer): void {
+export function register(server: McpServer): 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 5103be6..82f05df 100644
--- a/src/plugins/designer/tools/list-presets.ts
+++ b/src/plugins/designer/tools/list-presets.ts
@@ -1,7 +1,7 @@
-import type { ToolServer } from "../../../shared/tool-types.js";
+import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { PRESETS } from "../data.js";
-export function register(server: ToolServer): void {
+export function register(server: McpServer): 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 87eea71..a5f519d 100644
--- a/src/plugins/designer/tools/resolve-intent.ts
+++ b/src/plugins/designer/tools/resolve-intent.ts
@@ -1,11 +1,11 @@
-import type { ToolServer } from "../../../shared/tool-types.js";
+import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.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: ToolServer): void {
+export function register(server: McpServer): 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 484b3a1..9830494 100644
--- a/src/plugins/designer/tools/search.ts
+++ b/src/plugins/designer/tools/search.ts
@@ -1,8 +1,8 @@
-import type { ToolServer } from "../../../shared/tool-types.js";
+import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";
import { searchDesigner } from "../data.js";
-export function register(server: ToolServer): void {
+export function register(server: McpServer): 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 b3c5d29..b9349a8 100644
--- a/src/plugins/designer/tools/verify-implementation.ts
+++ b/src/plugins/designer/tools/verify-implementation.ts
@@ -1,4 +1,4 @@
-import type { ToolServer } from "../../../shared/tool-types.js";
+import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.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: ToolServer): void {
+export function register(server: McpServer): 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
new file mode 100644
index 0000000..bad41ae
--- /dev/null
+++ b/src/plugins/echo/index.ts
@@ -0,0 +1,19 @@
+import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.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: McpServer): 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/echo/tools/decision-matrix.ts b/src/plugins/echo/tools/decision-matrix.ts
index 142a7c5..0630aac 100644
--- a/src/plugins/echo/tools/decision-matrix.ts
+++ b/src/plugins/echo/tools/decision-matrix.ts
@@ -1,8 +1,8 @@
-import type { ToolServer } from "../../../shared/tool-types.js";
+import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";
import { DECISION_MATRIX } from "../data.js";
-export function register(server: ToolServer): void {
+export function register(server: McpServer): 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 ee517a9..590b04b 100644
--- a/src/plugins/echo/tools/get-middleware.ts
+++ b/src/plugins/echo/tools/get-middleware.ts
@@ -1,8 +1,8 @@
-import type { ToolServer } from "../../../shared/tool-types.js";
+import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";
import { MIDDLEWARE, getMiddlewareByName, searchMiddleware, formatMiddleware } from "../data.js";
-export function register(server: ToolServer): void {
+export function register(server: McpServer): 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 91cd370..fe0fadd 100644
--- a/src/plugins/echo/tools/get-recipe.ts
+++ b/src/plugins/echo/tools/get-recipe.ts
@@ -1,15 +1,15 @@
-import type { ToolServer } from "../../../shared/tool-types.js";
+import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";
import { RECIPES, getRecipeByName, searchRecipes, formatRecipe } from "../data.js";
-export function register(server: ToolServer): void {
+export function register(server: McpServer): void {
server.tool(
"echo_get_recipe",
"Get a specific Echo framework recipe with full working Go code, gotchas, and related recipes.",
{
name: z.string().describe("Recipe name (e.g., 'crud-api', 'websocket', 'sse', 'jwt-auth', 'graceful-shutdown')"),
},
- async ({ name }: { name: string }) => {
+ async ({ name }) => {
const recipe = getRecipeByName(name);
if (!recipe) {
const suggestions = searchRecipes(name).map((r) => r.name);
@@ -25,6 +25,6 @@ export function register(server: ToolServer): void {
};
}
return { content: [{ type: "text", text: formatRecipe(recipe) }] };
- },
+ }
);
}
diff --git a/src/plugins/echo/tools/list-middleware.ts b/src/plugins/echo/tools/list-middleware.ts
index eb98b9c..0c0c61d 100644
--- a/src/plugins/echo/tools/list-middleware.ts
+++ b/src/plugins/echo/tools/list-middleware.ts
@@ -1,7 +1,7 @@
-import type { ToolServer } from "../../../shared/tool-types.js";
+import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { MIDDLEWARE } from "../data.js";
-export function register(server: ToolServer): void {
+export function register(server: McpServer): 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 ba6f5c7..1682d58 100644
--- a/src/plugins/echo/tools/list-recipes.ts
+++ b/src/plugins/echo/tools/list-recipes.ts
@@ -1,8 +1,8 @@
-import type { ToolServer } from "../../../shared/tool-types.js";
+import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";
import { RECIPES, RECIPE_CATEGORIES } from "../data.js";
-export function register(server: ToolServer): void {
+export function register(server: McpServer): 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 b3c59fd..abce20d 100644
--- a/src/plugins/echo/tools/search-docs.ts
+++ b/src/plugins/echo/tools/search-docs.ts
@@ -1,8 +1,8 @@
-import type { ToolServer } from "../../../shared/tool-types.js";
+import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";
import { searchRecipes, searchMiddleware, RECIPE_CATEGORIES } from "../data.js";
-export function register(server: ToolServer): void {
+export function register(server: McpServer): 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
new file mode 100644
index 0000000..2dafd4e
--- /dev/null
+++ b/src/plugins/golang/index.ts
@@ -0,0 +1,22 @@
+import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.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: McpServer): 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/golang/tools/get-antipatterns.ts b/src/plugins/golang/tools/get-antipatterns.ts
index b616bfe..f8f8af6 100644
--- a/src/plugins/golang/tools/get-antipatterns.ts
+++ b/src/plugins/golang/tools/get-antipatterns.ts
@@ -1,7 +1,7 @@
-import type { ToolServer } from "../../../shared/tool-types.js";
+import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { ANTI_PATTERNS } from "../data.js";
-export function register(server: ToolServer): void {
+export function register(server: McpServer): 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 59f6bc4..e17dd28 100644
--- a/src/plugins/golang/tools/get-pattern.ts
+++ b/src/plugins/golang/tools/get-pattern.ts
@@ -1,8 +1,8 @@
-import type { ToolServer } from "../../../shared/tool-types.js";
+import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";
import { DESIGN_PATTERNS } from "../data.js";
-export function register(server: ToolServer): void {
+export function register(server: McpServer): 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 ca96d9f..47eb7f8 100644
--- a/src/plugins/golang/tools/get-practice.ts
+++ b/src/plugins/golang/tools/get-practice.ts
@@ -1,15 +1,15 @@
-import type { ToolServer } from "../../../shared/tool-types.js";
+import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";
import { BEST_PRACTICES } from "../data.js";
-export function register(server: ToolServer): void {
+export function register(server: McpServer): void {
server.tool(
"golang_get_practice",
"Get a Go best practice with good/bad code examples",
{
name: z.string().describe("Practice name (e.g. 'error-wrapping', 'goroutine-lifecycle', 'crypto-rand', 'table-driven-tests', 'thin-handlers')"),
},
- async ({ name }: { name: string }) => {
+ async ({ name }) => {
const practice = BEST_PRACTICES.find((p) => p.name.toLowerCase() === name.toLowerCase());
if (!practice) {
return {
@@ -24,6 +24,6 @@ export function register(server: ToolServer): void {
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`;
return { content: [{ type: "text", text }] };
- },
+ }
);
}
diff --git a/src/plugins/golang/tools/list-patterns.ts b/src/plugins/golang/tools/list-patterns.ts
index da038ec..d7b1b72 100644
--- a/src/plugins/golang/tools/list-patterns.ts
+++ b/src/plugins/golang/tools/list-patterns.ts
@@ -1,8 +1,8 @@
-import type { ToolServer } from "../../../shared/tool-types.js";
+import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";
import { DESIGN_PATTERNS, getPatternsByCategory } from "../data.js";
-export function register(server: ToolServer): void {
+export function register(server: McpServer): 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 c792142..8c74903 100644
--- a/src/plugins/golang/tools/list-practices.ts
+++ b/src/plugins/golang/tools/list-practices.ts
@@ -1,8 +1,8 @@
-import type { ToolServer } from "../../../shared/tool-types.js";
+import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";
import { BEST_PRACTICES, TOPICS, getPracticesByTopic } from "../data.js";
-export function register(server: ToolServer): void {
+export function register(server: McpServer): 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 95b4f7d..bf2f28b 100644
--- a/src/plugins/golang/tools/search-docs.ts
+++ b/src/plugins/golang/tools/search-docs.ts
@@ -1,8 +1,8 @@
-import type { ToolServer } from "../../../shared/tool-types.js";
+import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";
import { searchAll } from "../data.js";
-export function register(server: ToolServer): void {
+export function register(server: McpServer): 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
new file mode 100644
index 0000000..7904f0a
--- /dev/null
+++ b/src/plugins/hyperstack/index.ts
@@ -0,0 +1,10 @@
+import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
+import { Plugin } from "../../registry.js";
+import { registerSetupTool } from "./tools/setup.js";
+
+export const hyperstackPlugin: Plugin = {
+ name: "hyperstack",
+ register: (server: McpServer) => {
+ registerSetupTool(server);
+ },
+};
diff --git a/src/plugins/hyperstack/tools/setup.ts b/src/plugins/hyperstack/tools/setup.ts
new file mode 100644
index 0000000..7499bf0
--- /dev/null
+++ b/src/plugins/hyperstack/tools/setup.ts
@@ -0,0 +1,51 @@
+import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
+import { z } from "zod";
+import * as setup from "../../../internal/setup-hyperstack.js";
+import * as path from "node:path";
+
+export function registerSetupTool(server: McpServer) {
+ 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
new file mode 100644
index 0000000..e28b539
--- /dev/null
+++ b/src/plugins/lenis/index.ts
@@ -0,0 +1,22 @@
+import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.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: McpServer): 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/lenis/tools/cheatsheet.ts b/src/plugins/lenis/tools/cheatsheet.ts
index 218873a..967eaed 100644
--- a/src/plugins/lenis/tools/cheatsheet.ts
+++ b/src/plugins/lenis/tools/cheatsheet.ts
@@ -1,6 +1,6 @@
-import type { ToolServer } from "../../../shared/tool-types.js";
+import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
-export function register(server: ToolServer): void {
+export function register(server: McpServer): 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 6d93cad..3a84ce9 100644
--- a/src/plugins/lenis/tools/generate-setup.ts
+++ b/src/plugins/lenis/tools/generate-setup.ts
@@ -1,7 +1,7 @@
-import type { ToolServer } from "../../../shared/tool-types.js";
+import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";
-export function register(server: ToolServer): void {
+export function register(server: McpServer): 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 a02d908..42afecb 100644
--- a/src/plugins/lenis/tools/get-api.ts
+++ b/src/plugins/lenis/tools/get-api.ts
@@ -1,8 +1,8 @@
-import type { ToolServer } from "../../../shared/tool-types.js";
+import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";
import { ALL_APIS, searchApis, getApiByName, formatApiReference } from "../data.js";
-export function register(server: ToolServer): void {
+export function register(server: McpServer): 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 afbd1c5..4c706f5 100644
--- a/src/plugins/lenis/tools/get-pattern.ts
+++ b/src/plugins/lenis/tools/get-pattern.ts
@@ -1,10 +1,10 @@
-import type { ToolServer } from "../../../shared/tool-types.js";
+import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";
import { PATTERNS } from "../data.js";
const PATTERN_NAMES = Object.keys(PATTERNS) as [string, ...string[]];
-export function register(server: ToolServer): void {
+export function register(server: McpServer): 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.",
@@ -15,7 +15,7 @@ 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 }: { name: string }) => {
+ async ({ name }) => {
const pattern = PATTERNS[name];
if (!pattern) {
return {
@@ -39,6 +39,7 @@ export function register(server: ToolServer): void {
text += `## Key Notes\n\n`;
for (const tip of pattern.tips) text += `- ${tip}\n`;
}
+
return { content: [{ type: "text", text }] };
},
);
diff --git a/src/plugins/lenis/tools/list-apis.ts b/src/plugins/lenis/tools/list-apis.ts
index 6293a67..511afe6 100644
--- a/src/plugins/lenis/tools/list-apis.ts
+++ b/src/plugins/lenis/tools/list-apis.ts
@@ -1,8 +1,8 @@
-import type { ToolServer } from "../../../shared/tool-types.js";
+import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";
import { ALL_APIS, API_KINDS, capitalize } from "../data.js";
-export function register(server: ToolServer): void {
+export function register(server: McpServer): 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 50745c1..018e20f 100644
--- a/src/plugins/lenis/tools/search-docs.ts
+++ b/src/plugins/lenis/tools/search-docs.ts
@@ -1,8 +1,8 @@
-import type { ToolServer } from "../../../shared/tool-types.js";
+import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";
import { searchApis, PATTERNS } from "../data.js";
-export function register(server: ToolServer): void {
+export function register(server: McpServer): 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
new file mode 100644
index 0000000..a29a56f
--- /dev/null
+++ b/src/plugins/motion/index.ts
@@ -0,0 +1,24 @@
+import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.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: McpServer): 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/motion/tools/cheatsheet.ts b/src/plugins/motion/tools/cheatsheet.ts
index de868f0..8fcdde0 100644
--- a/src/plugins/motion/tools/cheatsheet.ts
+++ b/src/plugins/motion/tools/cheatsheet.ts
@@ -1,6 +1,6 @@
-import type { ToolServer } from "../../../shared/tool-types.js";
+import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
-export function register(server: ToolServer): void {
+export function register(server: McpServer): 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 c0043dd..79acdb5 100644
--- a/src/plugins/motion/tools/generate-animation.ts
+++ b/src/plugins/motion/tools/generate-animation.ts
@@ -1,7 +1,7 @@
-import type { ToolServer } from "../../../shared/tool-types.js";
+import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";
-export function register(server: ToolServer): void {
+export function register(server: McpServer): 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 c6d3e63..a806038 100644
--- a/src/plugins/motion/tools/get-api.ts
+++ b/src/plugins/motion/tools/get-api.ts
@@ -1,15 +1,15 @@
-import type { ToolServer } from "../../../shared/tool-types.js";
+import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";
import { ALL_APIS, searchApis, getApiByName, formatApiReference } from "../data.js";
-export function register(server: ToolServer): void {
+export function register(server: McpServer): 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.",
{
name: z.string().describe("API name (e.g., 'motion', 'AnimatePresence', 'useAnimate', 'useScroll', 'stagger', 'Reorder.Group')"),
},
- async ({ name }: { name: string }) => {
+ async ({ name }) => {
const api = getApiByName(name);
if (!api) {
const suggestions = searchApis(name).map((r) => r.api.name);
diff --git a/src/plugins/motion/tools/get-examples.ts b/src/plugins/motion/tools/get-examples.ts
index d307e80..3846138 100644
--- a/src/plugins/motion/tools/get-examples.ts
+++ b/src/plugins/motion/tools/get-examples.ts
@@ -1,13 +1,13 @@
-import type { ToolServer } from "../../../shared/tool-types.js";
+import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";
import { CATEGORIES, getExamplesByCategory, formatExample, capitalize } from "../data.js";
-export function register(server: ToolServer): void {
+export function register(server: McpServer): void {
server.tool(
"motion_get_examples",
"Get code examples for a specific animation category",
{ category: z.string().describe(`Category: ${CATEGORIES.join(", ")}`) },
- async ({ category }: { category: string }) => {
+ async ({ category }) => {
const examples = getExamplesByCategory(category);
if (examples.length === 0) {
return { content: [{ type: "text", text: `No examples for category "${category}". Available: ${CATEGORIES.join(", ")}` }] };
diff --git a/src/plugins/motion/tools/get-transitions.ts b/src/plugins/motion/tools/get-transitions.ts
index 8f7c1ae..93e7631 100644
--- a/src/plugins/motion/tools/get-transitions.ts
+++ b/src/plugins/motion/tools/get-transitions.ts
@@ -1,7 +1,7 @@
-import type { ToolServer } from "../../../shared/tool-types.js";
+import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { TRANSITIONS_REFERENCE } from "../data.js";
-export function register(server: ToolServer): void {
+export function register(server: McpServer): 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 30be9fd..3ec6d49 100644
--- a/src/plugins/motion/tools/list-apis.ts
+++ b/src/plugins/motion/tools/list-apis.ts
@@ -1,8 +1,8 @@
-import type { ToolServer } from "../../../shared/tool-types.js";
+import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";
import { ALL_APIS, API_KINDS, capitalize } from "../data.js";
-export function register(server: ToolServer): void {
+export function register(server: McpServer): 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 b5dd6b2..3a0d928 100644
--- a/src/plugins/motion/tools/search-docs.ts
+++ b/src/plugins/motion/tools/search-docs.ts
@@ -1,8 +1,8 @@
-import type { ToolServer } from "../../../shared/tool-types.js";
+import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";
import { CATEGORIES, searchApis, formatExample } from "../data.js";
-export function register(server: ToolServer): void {
+export function register(server: McpServer): 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
new file mode 100644
index 0000000..2c2e1d3
--- /dev/null
+++ b/src/plugins/react/index.ts
@@ -0,0 +1,18 @@
+import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.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 {
+ listPatterns(server);
+ getPattern(server);
+ getConstraints(server);
+ searchDocs(server);
+}
+
+export const reactPlugin: Plugin = {
+ name: "react",
+ register,
+};
diff --git a/src/plugins/react/tools/get-constraints.ts b/src/plugins/react/tools/get-constraints.ts
index 484b1f2..102683e 100644
--- a/src/plugins/react/tools/get-constraints.ts
+++ b/src/plugins/react/tools/get-constraints.ts
@@ -1,7 +1,7 @@
-import type { ToolServer } from "../../../shared/tool-types.js";
+import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { CONSTRAINTS } from "../data.js";
-export function register(server: ToolServer): void {
+export function register(server: McpServer): 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 d4d4bb2..8975cc9 100644
--- a/src/plugins/react/tools/get-pattern.ts
+++ b/src/plugins/react/tools/get-pattern.ts
@@ -1,15 +1,15 @@
-import type { ToolServer } from "../../../shared/tool-types.js";
+import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";
import { PATTERNS, getPatternByName } from "../data.js";
-export function register(server: ToolServer): void {
+export function register(server: McpServer): void {
server.tool(
"react_get_pattern",
"Get a React/Next.js pattern with full code example and anti-pattern",
{
name: z.string().describe("Pattern name (e.g. 'rsc-default', 'state-hierarchy', 'zustand-store', 'suspense-boundary', 'nextjs-metadata', 'composition-pattern', 'component-template')"),
},
- async ({ name }: { name: string }) => {
+ async ({ name }) => {
const pattern = getPatternByName(name);
if (!pattern) {
const available = PATTERNS.map((p) => p.name).join(", ");
@@ -32,7 +32,8 @@ export function register(server: ToolServer): void {
text += `## Tips\n`;
for (const tip of pattern.tips) text += `- ${tip}\n`;
}
+
return { content: [{ type: "text", text }] };
- },
+ }
);
}
diff --git a/src/plugins/react/tools/list-patterns.ts b/src/plugins/react/tools/list-patterns.ts
index c0a30d6..53abfca 100644
--- a/src/plugins/react/tools/list-patterns.ts
+++ b/src/plugins/react/tools/list-patterns.ts
@@ -1,8 +1,8 @@
-import type { ToolServer } from "../../../shared/tool-types.js";
+import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";
import { PATTERNS, PATTERN_CATEGORIES, getPatternsByCategory } from "../data.js";
-export function register(server: ToolServer): void {
+export function register(server: McpServer): 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 41c953f..e13ab78 100644
--- a/src/plugins/react/tools/search-docs.ts
+++ b/src/plugins/react/tools/search-docs.ts
@@ -1,8 +1,8 @@
-import type { ToolServer } from "../../../shared/tool-types.js";
+import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";
import { searchPatterns, CONSTRAINTS } from "../data.js";
-export function register(server: ToolServer): void {
+export function register(server: McpServer): 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
new file mode 100644
index 0000000..9586952
--- /dev/null
+++ b/src/plugins/reactflow/index.ts
@@ -0,0 +1,28 @@
+import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.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: McpServer): 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/reactflow/tools/cheatsheet.ts b/src/plugins/reactflow/tools/cheatsheet.ts
index 1466221..0838d02 100644
--- a/src/plugins/reactflow/tools/cheatsheet.ts
+++ b/src/plugins/reactflow/tools/cheatsheet.ts
@@ -1,6 +1,6 @@
-import type { ToolServer } from "../../../shared/tool-types.js";
+import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
-export function register(server: ToolServer): void {
+export function register(server: McpServer): 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 d730df8..4369071 100644
--- a/src/plugins/reactflow/tools/generate-flow.ts
+++ b/src/plugins/reactflow/tools/generate-flow.ts
@@ -1,7 +1,7 @@
-import type { ToolServer } from "../../../shared/tool-types.js";
+import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";
-export function register(server: ToolServer): void {
+export function register(server: McpServer): 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 fe89963..ff3d6bd 100644
--- a/src/plugins/reactflow/tools/get-api.ts
+++ b/src/plugins/reactflow/tools/get-api.ts
@@ -1,8 +1,8 @@
-import type { ToolServer } from "../../../shared/tool-types.js";
+import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";
import { ALL_APIS, searchApis, getApiByName, formatApiReference } from "../data/index.js";
-export function register(server: ToolServer): void {
+export function register(server: McpServer): 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.",
@@ -13,7 +13,7 @@ export function register(server: ToolServer): void {
"API name (e.g., 'ReactFlow', 'useReactFlow', 'Handle', 'addEdge', 'Node', 'Edge', 'NodeProps')",
),
},
- async ({ name }: { name: string }) => {
+ async ({ name }) => {
const api = getApiByName(name, ALL_APIS);
if (!api) {
const suggestions = searchApis(name, ALL_APIS)
diff --git a/src/plugins/reactflow/tools/get-examples.ts b/src/plugins/reactflow/tools/get-examples.ts
index cd8ceb3..b1dfcc6 100644
--- a/src/plugins/reactflow/tools/get-examples.ts
+++ b/src/plugins/reactflow/tools/get-examples.ts
@@ -1,8 +1,8 @@
-import type { ToolServer } from "../../../shared/tool-types.js";
+import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";
import { ALL_APIS, CATEGORIES, getExamplesByCategory, formatExample, capitalize } from "../data/index.js";
-export function register(server: ToolServer): void {
+export function register(server: McpServer): 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 39c9700..2ef077c 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 { ToolServer } from "../../../shared/tool-types.js";
+import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { V12_MIGRATION } from "../data/index.js";
-export function register(server: ToolServer): void {
+export function register(server: McpServer): 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 b7f09e6..45deb82 100644
--- a/src/plugins/reactflow/tools/get-pattern.ts
+++ b/src/plugins/reactflow/tools/get-pattern.ts
@@ -1,8 +1,8 @@
-import type { ToolServer } from "../../../shared/tool-types.js";
+import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";
import { PATTERNS, PATTERN_SECTIONS } from "../data/index.js";
-export function register(server: ToolServer): void {
+export function register(server: McpServer): 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 61eebe4..6190b31 100644
--- a/src/plugins/reactflow/tools/get-template.ts
+++ b/src/plugins/reactflow/tools/get-template.ts
@@ -1,8 +1,8 @@
-import type { ToolServer } from "../../../shared/tool-types.js";
+import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";
import { TEMPLATES, capitalize } from "../data/index.js";
-export function register(server: ToolServer): void {
+export function register(server: McpServer): 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: ToolServer): void {
.enum(["custom-node", "custom-edge", "zustand-store"] as const)
.describe("Template name"),
},
- async ({ template }: { template: "custom-node" | "custom-edge" | "zustand-store" }) => {
+ async ({ template }) => {
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 4a9181d..8cf3376 100644
--- a/src/plugins/reactflow/tools/list-apis.ts
+++ b/src/plugins/reactflow/tools/list-apis.ts
@@ -1,8 +1,8 @@
-import type { ToolServer } from "../../../shared/tool-types.js";
+import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";
import { ALL_APIS, capitalize, API_KINDS } from "../data/index.js";
-export function register(server: ToolServer): void {
+export function register(server: McpServer): 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 11eddc0..9827120 100644
--- a/src/plugins/reactflow/tools/search-docs.ts
+++ b/src/plugins/reactflow/tools/search-docs.ts
@@ -1,8 +1,8 @@
-import type { ToolServer } from "../../../shared/tool-types.js";
+import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";
import { ALL_APIS, CATEGORIES, PATTERN_SECTIONS, searchApis, formatExample } from "../data/index.js";
-export function register(server: ToolServer): void {
+export function register(server: McpServer): 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
new file mode 100644
index 0000000..1f521bc
--- /dev/null
+++ b/src/plugins/rust/index.ts
@@ -0,0 +1,18 @@
+import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.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 {
+ listPractices(server);
+ getPractice(server);
+ searchDocs(server);
+ cheatsheet(server);
+}
+
+export const rustPlugin: Plugin = {
+ name: "rust",
+ register,
+};
diff --git a/src/plugins/rust/tools/cheatsheet.ts b/src/plugins/rust/tools/cheatsheet.ts
index a871917..2cf0a0a 100644
--- a/src/plugins/rust/tools/cheatsheet.ts
+++ b/src/plugins/rust/tools/cheatsheet.ts
@@ -1,9 +1,9 @@
-import type { ToolServer } from "../../../shared/tool-types.js";
+import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { snippet } from "../loader.js";
const CHEATSHEET = snippet("cheatsheet.txt");
-export function register(server: ToolServer): void {
+export function register(server: McpServer): 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 0055670..5808cf3 100644
--- a/src/plugins/rust/tools/get-practice.ts
+++ b/src/plugins/rust/tools/get-practice.ts
@@ -1,15 +1,15 @@
-import type { ToolServer } from "../../../shared/tool-types.js";
+import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";
import { BEST_PRACTICES } from "../data.js";
-export function register(server: ToolServer): void {
+export function register(server: McpServer): void {
server.tool(
"rust_get_practice",
"Get a Rust best practice with code examples",
{
name: z.string().describe("Practice name (e.g. 'borrow-over-clone', 'result-not-panic', 'thiserror-vs-anyhow', 'type-state-pattern', 'clippy-command')"),
},
- async ({ name }: { name: string }) => {
+ async ({ name }) => {
const practice = BEST_PRACTICES.find((p) => p.name.toLowerCase() === name.toLowerCase());
if (!practice) {
return {
@@ -28,6 +28,6 @@ export function register(server: ToolServer): void {
for (const tip of practice.tips) text += `- ${tip}\n`;
}
return { content: [{ type: "text", text }] };
- },
+ }
);
}
diff --git a/src/plugins/rust/tools/list-practices.ts b/src/plugins/rust/tools/list-practices.ts
index 9075b3e..761ae6e 100644
--- a/src/plugins/rust/tools/list-practices.ts
+++ b/src/plugins/rust/tools/list-practices.ts
@@ -1,8 +1,8 @@
-import type { ToolServer } from "../../../shared/tool-types.js";
+import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";
import { BEST_PRACTICES, CHAPTERS, getPracticesByChapter } from "../data.js";
-export function register(server: ToolServer): void {
+export function register(server: McpServer): 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 307494e..3398154 100644
--- a/src/plugins/rust/tools/search-docs.ts
+++ b/src/plugins/rust/tools/search-docs.ts
@@ -1,8 +1,8 @@
-import type { ToolServer } from "../../../shared/tool-types.js";
+import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";
import { searchPractices } from "../data.js";
-export function register(server: ToolServer): void {
+export function register(server: McpServer): 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
new file mode 100644
index 0000000..fb2fb90
--- /dev/null
+++ b/src/plugins/shadcn/index.ts
@@ -0,0 +1,20 @@
+import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.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: McpServer): void {
+ listComponents(server);
+ getComponent(server);
+ getRules(server);
+ getSnippet(server);
+ getComposition(server);
+}
+
+export const shadcnPlugin: Plugin = {
+ name: "shadcn",
+ register,
+};
diff --git a/src/plugins/shadcn/tools/get-component.ts b/src/plugins/shadcn/tools/get-component.ts
index f0555a8..c68ce8a 100644
--- a/src/plugins/shadcn/tools/get-component.ts
+++ b/src/plugins/shadcn/tools/get-component.ts
@@ -1,15 +1,15 @@
-import type { ToolServer } from "../../../shared/tool-types.js";
+import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";
import { getComponentByName, SHADCN_COMPONENTS } from "../data.js";
-export function register(server: ToolServer): void {
+export function register(server: McpServer): 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.",
{
name: z.string().describe("Component name (e.g., 'Button', 'Dialog', 'Field', 'Select'). Case-insensitive."),
},
- async ({ name }: { name: string }) => {
+ async ({ name }) => {
const component = getComponentByName(name);
if (!component) {
const available = SHADCN_COMPONENTS.map((c) => c.name).join(", ");
@@ -59,6 +59,6 @@ export function register(server: ToolServer): void {
text += `- [ ] OKLCH color tokens from design system (not hardcoded hex)\n`;
return { content: [{ type: "text" as const, text }] };
- },
+ }
);
}
diff --git a/src/plugins/shadcn/tools/get-composition.ts b/src/plugins/shadcn/tools/get-composition.ts
index b191706..e8ad27b 100644
--- a/src/plugins/shadcn/tools/get-composition.ts
+++ b/src/plugins/shadcn/tools/get-composition.ts
@@ -1,4 +1,4 @@
-import type { ToolServer } from "../../../shared/tool-types.js";
+import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.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: ToolServer): void {
+export function register(server: McpServer): 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 765987d..cd42a15 100644
--- a/src/plugins/shadcn/tools/get-rules.ts
+++ b/src/plugins/shadcn/tools/get-rules.ts
@@ -1,7 +1,7 @@
-import type { ToolServer } from "../../../shared/tool-types.js";
+import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { SHADCN_RULES, ARCHITECTURAL_CHECKLIST } from "../data.js";
-export function register(server: ToolServer): void {
+export function register(server: McpServer): 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 29732bd..44791b0 100644
--- a/src/plugins/shadcn/tools/get-snippet.ts
+++ b/src/plugins/shadcn/tools/get-snippet.ts
@@ -1,8 +1,8 @@
-import type { ToolServer } from "../../../shared/tool-types.js";
+import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";
import { getComponentByName, SHADCN_COMPONENTS } from "../data.js";
-export function register(server: ToolServer): void {
+export function register(server: McpServer): 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 3a53b4e..9edd68b 100644
--- a/src/plugins/shadcn/tools/list-components.ts
+++ b/src/plugins/shadcn/tools/list-components.ts
@@ -1,8 +1,8 @@
-import type { ToolServer } from "../../../shared/tool-types.js";
+import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";
import { SHADCN_COMPONENTS, COMPONENT_CATEGORIES, getComponentsByCategory } from "../data.js";
-export function register(server: ToolServer): void {
+export function register(server: McpServer): 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
new file mode 100644
index 0000000..e7443a1
--- /dev/null
+++ b/src/plugins/ui-ux/index.ts
@@ -0,0 +1,22 @@
+import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.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: McpServer): 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/plugins/ui-ux/tools/get-checklist.ts b/src/plugins/ui-ux/tools/get-checklist.ts
index e3592e5..49aa310 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 { ToolServer } from "../../../shared/tool-types.js";
+import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";
import { DOMAINS, getChecklistByDomain } from "../data.js";
-export function register(server: ToolServer): void {
+export function register(server: McpServer): 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 473b8e5..189ea47 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 { ToolServer } from "../../../shared/tool-types.js";
+import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";
import { COMPONENT_PATTERNS, getComponentPatternByName } from "../data.js";
-export function register(server: ToolServer): void {
+export function register(server: McpServer): 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 c196307..0f07e2d 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 { ToolServer } from "../../../shared/tool-types.js";
+import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";
import { DOMAINS, getAllGotchas } from "../data.js";
-export function register(server: ToolServer): void {
+export function register(server: McpServer): 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 f5d8f36..45594ef 100644
--- a/src/plugins/ui-ux/tools/get-principle.ts
+++ b/src/plugins/ui-ux/tools/get-principle.ts
@@ -1,16 +1,18 @@
-import type { ToolServer } from "../../../shared/tool-types.js";
+import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";
import { PRINCIPLES } from "../data.js";
-export function register(server: ToolServer): void {
+export function register(server: McpServer): void {
server.tool(
"ui_ux_get_principle",
"Get full details for a UI/UX principle including examples, anti-patterns, and CSS examples",
{
name: z.string().describe("Principle name (e.g. 'type-scale', 'wcag-contrast', 'dark-mode-principles', 'touch-targets', 'easing-rules')"),
},
- async ({ name }: { name: string }) => {
- const principle = PRINCIPLES.find((p) => p.name.toLowerCase() === name.toLowerCase());
+ async ({ name }) => {
+ const principle = PRINCIPLES.find(
+ (p) => p.name.toLowerCase() === name.toLowerCase()
+ );
if (!principle) {
const available = PRINCIPLES.map((p) => p.name).join(", ");
@@ -38,7 +40,8 @@ export function register(server: ToolServer): void {
text += `## Anti-patterns (avoid)\n`;
for (const ap of principle.antiPatterns) text += `- โ ${ap}\n`;
}
+
return { content: [{ type: "text", text }] };
- },
+ }
);
}
diff --git a/src/plugins/ui-ux/tools/list-principles.ts b/src/plugins/ui-ux/tools/list-principles.ts
index bcf0af0..271620e 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 { ToolServer } from "../../../shared/tool-types.js";
+import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";
import { PRINCIPLES, DOMAINS, getPrinciplesByDomain } from "../data.js";
-export function register(server: ToolServer): void {
+export function register(server: McpServer): 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 876b271..2544f43 100644
--- a/src/plugins/ui-ux/tools/search.ts
+++ b/src/plugins/ui-ux/tools/search.ts
@@ -1,8 +1,8 @@
-import type { ToolServer } from "../../../shared/tool-types.js";
+import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";
import { searchPrinciples, COMPONENT_PATTERNS } from "../data.js";
-export function register(server: ToolServer): void {
+export function register(server: McpServer): 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
new file mode 100755
index 0000000..1ece8b6
--- /dev/null
+++ b/src/registry.ts
@@ -0,0 +1,12 @@
+import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
+
+export interface Plugin {
+ name: string;
+ register: (server: McpServer) => void;
+}
+
+export function loadPlugins(server: McpServer, 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
deleted file mode 100644
index 60908d9..0000000
--- a/src/shared/tool-types.ts
+++ /dev/null
@@ -1,29 +0,0 @@
-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;
-}
diff --git a/summary.md b/summary.md
index 4e1c93e..b6f3177 100644
--- a/summary.md
+++ b/summary.md
@@ -13,10 +13,7 @@ Redefine the relationship between a human developer and an AI coding assistant.
## ๐ ๏ธ What We Have Accomplished
### 1. Monorepo Consolidation
-Merged two fragmented repositories (`unified-mcp` for data, `unified-skill` for persona) into a single high-cohesion repository. Hyperstack is now a **Three-Layer Ecosystem**:
-- **Layer 1: Ground Truth (MCP)** - The "Brain": Deterministic data and tools
-- **Layer 2: Process (Skills)** - The "Body": Disciplined engineering workflows and gates
-- **Layer 3: Orchestration (Agents)** - The "Nervous System": Internal roles for routing and verification
+Merged two fragmented repositories (`unified-mcp` for data, `unified-skill` for persona) into a single high-cohesion repository. The "Brain" (MCP plugins) and the "Body" (skills) now live together and stay in sync.
### 2. Plugin Architecture (Encapsulation)
Every plugin is self-contained:
@@ -60,21 +57,16 @@ Every gate skill rewritten with:
- **1% Rule** - if there is even a 1% chance a skill applies, invoke it
- **Rationalization tables** listing the exact excuses the AI will use to skip the gate, with counters
- **Spirit of the rule equals letter of the rule** clause to close loophole-hunting
-- **SessionStart hook** (`hooks/session-start`) that force-injects the `hyperstack` skill into every session's context, so discipline reaches the agent without manual invocation
+- **SessionStart hook** (`hooks/session-start`) that force-injects the `using-hyperstack` skill into every session's context, so discipline reaches the agent without manual invocation
-### 8. Internal Agent Harness (Layer 3)
-Hyperstack uses internal roles to manage complexity. These roles are **internal and auto-invoked**, ensuring specialists are used when necessary without shifting the burden to the user:
-- **`hyper` (Core):** The conductor. Owns request classification, routing, gate enforcement, final verification, and delivery.
-- **`website-builder` (Specialist):** Owns website-facing design/implementation, CTA hierarchy, and page structure. Delegates back to `hyper` for verification.
-
-### 9. The Skill System (21 skills, 3 categories)
+### 8. The Skill System (21 skills, 3 categories)
Every skill has a `category:` frontmatter field. The index at `skills/INDEX.md` is auto-generated from frontmatter by `bash scripts/generate-skills-index.sh`.
- **Core (13):** blueprint, forge-plan, run-plan, engineering-discipline, ship-gate, deliver, test-first, debug-discipline, code-review, autonomous-mode, subagent-ops, parallel-dispatch, worktree-isolation
- **Domain (6):** designer, shadcn-expert, behaviour-analysis, security-review, design-patterns-skill, readme-writer
-- **Meta (2):** hyperstack, testing-skills
+- **Meta (2):** using-hyperstack, testing-skills
-### 10. Skill Testing Discipline (`testing-skills`)
+### 9. Skill Testing Discipline (`testing-skills`)
A TDD-for-skills methodology:
1. Write pressure scenarios
2. Dispatch fresh subagents WITHOUT the skill (RED phase)
@@ -86,7 +78,7 @@ Iron Law: NO SKILL SHIPS WITHOUT SUBAGENT PRESSURE TEST EVIDENCE.
Scenario files currently exist for ship-gate, designer, and blueprint. Remaining gate skills still need their own scenarios.
-### 11. Ecosystem Wiring
+### 10. Ecosystem Wiring
Every skill references its upstream and downstream edges explicitly.
- Designer hands DESIGN.md to forge-plan
- Forge-plan calls shadcn tools (if shadcn chosen) or hand-builds from DESIGN.md (if raw Tailwind)
@@ -113,7 +105,7 @@ Every skill references its upstream and downstream edges explicitly.
**Active development.** Main branch is stable and the Docker image publishes automatically on push.
-Eleven plugins, seventy-nine tools, twenty-one skills, and two internal agents (`hyper` and `website-builder`). The SessionStart hook is wired. The adversarial enforcement is in place. The designer pipeline works end-to-end (verified via test harness). shadcn is integrated as an optional choice.
+Eleven plugins, seventy-nine tools, twenty-one skills. The SessionStart hook is wired. The adversarial enforcement is in place. The designer pipeline works end-to-end (verified via test harness). shadcn is integrated as an optional choice.
### Remaining work
- More pressure-test scenarios for gate skills (forge-plan, engineering-discipline, behaviour-analysis, test-first)
diff --git a/tests/context-compiler-behaviour.test.ts b/tests/context-compiler-behaviour.test.ts
new file mode 100644
index 0000000..3bfbe83
--- /dev/null
+++ b/tests/context-compiler-behaviour.test.ts
@@ -0,0 +1,86 @@
+import { test, expect } from "bun:test";
+import { readFileSync, writeFileSync } from "node:fs";
+import { resolve } from "node:path";
+import {
+ compileUsingHyperstackBootstrap,
+ generateHyperstackBootstrap,
+} from "../src/internal/context-compiler.ts";
+
+function normalize(str: string): string {
+ return str.replace(/\r\n/g, "\n");
+}
+
+test("compileUsingHyperstackBootstrap keeps required invariants while shrinking the source", () => {
+ const source = `
+
+Not for subagents.
+
+
+
+This is extremely important.
+
+
+# Skill Name
+This is a skill.
+{INV: invariant-1}
+{INV: invariant-2}
+
+## The Iron Laws
+\`\`\`
+- Law 1
+{INV: invariant-1}
+{INV: invariant-2}
+\`\`\`
+
+## Instruction Priority
+- P1
+
+## Red Flags - STOP
+- Red 1
+${"x".repeat(2000)}
+
+## Layer 1: MCP Tools (Ground-Truth Data)
+- Tool 1
+
+## Layer 2: Skills (Engineering Process)
+- Skill 1
+
+## Role Registry
+- Role 1
+
+## Routing Summary
+- Route 1
+
+## Allowed Transitions
+- Transition 1
+
+## Disallowed Transitions
+- Transition 2
+
+## The Rationalization Catalog (Read Before Every Session)
+- Rational 1
+
+## The One Rule That Governs All Rules
+- Rule 1
+
+## Final Check Before Any Response
+- Check 1
+
+### Steps
+1. Step 1
+ `;
+ const { content } = compileUsingHyperstackBootstrap(source);
+
+ expect(content).toMatch(/invariant-1/);
+ expect(content).toMatch(/invariant-2/);
+ expect(content.length).toBeLessThan(source.length);
+});
+
+test("generated bootstrap artifact stays in sync with the compiler output", () => {
+ const skillSource = normalize(readFileSync(resolve("skills/using-hyperstack/SKILL.md"), "utf8"));
+ const currentBootstrap = normalize(readFileSync(resolve("generated/runtime-context/using-hyperstack.bootstrap.md"), "utf8"));
+
+ const nextBootstrap = generateHyperstackBootstrap(skillSource);
+
+ expect(normalize(nextBootstrap)).toBe(currentBootstrap);
+});
diff --git a/tests/generator-behaviour.test.ts b/tests/generator-behaviour.test.ts
new file mode 100644
index 0000000..db76d98
--- /dev/null
+++ b/tests/generator-behaviour.test.ts
@@ -0,0 +1,34 @@
+import { test, expect } from "bun:test";
+import { generateFlowCode } from "../src/plugins/reactflow/tools/generate-flow.ts";
+import { generateAnimationCode } from "../src/plugins/motion/tools/generate-animation.ts";
+import { generateSetupCode } from "../src/plugins/lenis/tools/generate-setup.ts";
+import { buildTasks, parseDesignMd } from "../src/plugins/designer/tools/generate-implementation-plan.ts";
+
+test("reactflow_generate_flow returns self-contained controlled-flow code", () => {
+ const result = generateFlowCode("simple flow", false);
+ expect(result).toMatch(/import { .*ReactFlow.* } from '@xyflow\/react'/);
+ expect(result).toMatch(/useNodesState/);
+ expect(result).toMatch(/useEdgesState/);
+});
+
+test("motion_generate_animation does not emit placeholder exit keys", () => {
+ const result = generateAnimationCode("fade in");
+ expect(result).not.toMatch(/exit=\{\}/);
+});
+
+test("motion_generate_animation declares list inputs for staggered list output", () => {
+ const result = generateAnimationCode("staggered list");
+ expect(result).toMatch(/items\.map/);
+});
+
+test("lenis_generate_setup uses a reactive reduced-motion check", () => {
+ const { code } = generateSetupCode("next.js setup with basic accessibility");
+ expect(code).toMatch(/useReducedMotion|matchMedia/);
+});
+
+test("buildTasks preserves multi-word component names from DESIGN.md", () => {
+ const designMd = `## 5. Components\n\n### Header Navigation\n- Build a header with navigation.`;
+ const sections = parseDesignMd(designMd);
+ const tasks = buildTasks(sections, "react");
+ expect(tasks.some((t) => t.name.includes("Header Navigation"))).toBe(true);
+});
diff --git a/tests/helpers.ts b/tests/helpers.ts
new file mode 100644
index 0000000..d6d4677
--- /dev/null
+++ b/tests/helpers.ts
@@ -0,0 +1,44 @@
+import assert from "node:assert/strict";
+import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
+
+type ToolHandler = (args: Record) => Promise | unknown;
+
+export function captureTool(register: (server: McpServer) => void) {
+ let capturedName = "";
+ let capturedHandler: ToolHandler | undefined;
+
+ const server = {
+ tool(name: string, _description: string, _schema: unknown, handler: ToolHandler) {
+ capturedName = name;
+ capturedHandler = handler;
+ },
+ } as unknown as McpServer;
+
+ register(server);
+
+ assert.ok(capturedName, "tool registration did not provide a name");
+ assert.ok(capturedHandler, "tool registration did not provide a handler");
+
+ return {
+ name: capturedName,
+ async invoke(args: Record) {
+ return capturedHandler!(args);
+ },
+ };
+}
+
+export function extractTextContent(result: unknown): string {
+ const payload = result as {
+ content?: Array<{ type?: string; text?: string }>;
+ };
+
+ const textBlock = payload.content?.find((item) => item.type === "text" && typeof item.text === "string");
+ assert.ok(textBlock?.text, "tool response did not include a text content block");
+ return textBlock.text;
+}
+
+export function extractTsxFence(markdown: string): string {
+ const match = markdown.match(/```tsx\n([\s\S]*?)\n```/);
+ assert.ok(match?.[1], "tool response did not include a tsx code fence");
+ return match[1];
+}
diff --git a/tests/plugin-registry-behaviour.test.ts b/tests/plugin-registry-behaviour.test.ts
new file mode 100644
index 0000000..14fb82f
--- /dev/null
+++ b/tests/plugin-registry-behaviour.test.ts
@@ -0,0 +1,60 @@
+import { test, expect } from "bun:test";
+import { allPlugins } from "../src/index.ts";
+
+function getRegisteredTools() {
+ const tools: Array<{ name: string; description: string }> = [];
+ const mockServer = {
+ tool: (name: string, description: string) => {
+ tools.push({ name, description });
+ },
+ resource: () => {},
+ prompt: () => {},
+ } as any;
+
+ for (const plugin of allPlugins) {
+ plugin.register(mockServer);
+ }
+ return tools;
+}
+
+test("all 11 plugins register at least one tool", () => {
+ const tools = getRegisteredTools();
+ const pluginPrefixes = new Set(tools.map((t) => t.name.split("_")[0]));
+ expect(pluginPrefixes.size).toBe(11);
+});
+
+test("every registered tool has a non-empty name and description", () => {
+ const tools = getRegisteredTools();
+ for (const tool of tools) {
+ expect(tool.name).not.toBe("");
+ expect(tool.description).not.toBe("");
+ }
+});
+
+test("tool names follow namespace convention (plugin_name_action)", () => {
+ const tools = getRegisteredTools();
+ for (const tool of tools) {
+ expect(tool.name).toMatch(/^[a-z]+_[a-z0-9_]+$/);
+ }
+});
+
+test("designer plugin registers the required MCP tools referenced in skills", () => {
+ const tools = getRegisteredTools();
+ const toolNames = new Set(tools.map((t) => t.name));
+ expect(toolNames.has("designer_resolve_intent")).toBe(true);
+ expect(toolNames.has("designer_generate_design_brief")).toBe(true);
+});
+
+test("shadcn plugin registers the required MCP tools referenced in skills", () => {
+ const tools = getRegisteredTools();
+ const toolNames = new Set(tools.map((t) => t.name));
+ expect(toolNames.has("shadcn_get_component")).toBe(true);
+ expect(toolNames.has("shadcn_get_composition")).toBe(true);
+});
+
+test("no two plugins register a tool with the same name", () => {
+ const tools = getRegisteredTools();
+ const names = tools.map((t) => t.name);
+ const uniqueNames = new Set(names);
+ expect(names.length).toBe(uniqueNames.size);
+});
diff --git a/tests/role-harness-behaviour.test.ts b/tests/role-harness-behaviour.test.ts
new file mode 100644
index 0000000..f223911
--- /dev/null
+++ b/tests/role-harness-behaviour.test.ts
@@ -0,0 +1,104 @@
+import { test, expect } from "bun:test";
+import { existsSync, readFileSync } from "node:fs";
+import { resolve } from "node:path";
+import {
+ compileUsingHyperstackBootstrap,
+ validateUsingHyperstackBootstrap,
+} from "../src/internal/context-compiler.ts";
+
+const REQUIRED_ROLE_FILES = [
+ "agents/hyper/PROFILE.md",
+ "agents/hyper/LIFECYCLE.md",
+ "agents/hyper/CONTEXT.md",
+ "agents/hyper/CHECKS.md",
+ "agents/website-builder/PROFILE.md",
+ "agents/website-builder/LIFECYCLE.md",
+ "agents/website-builder/CONTEXT.md",
+ "agents/website-builder/CHECKS.md",
+ "harness/router.md",
+ "harness/transitions.md",
+ "harness/context-policy.md",
+ "harness/observability.md",
+];
+
+const REQUIRED_PROFILE_KEYS = [
+ "name",
+ "kind",
+ "auto_invoke_when",
+ "owns",
+ "must_not_do",
+ "delegates_to",
+ "requires",
+];
+
+function normalize(str: string): string {
+ return str.replace(/\r\n/g, "\n");
+}
+
+test("role harness files exist for hyper and website-builder", () => {
+ for (const relativePath of REQUIRED_ROLE_FILES) {
+ expect(existsSync(resolve(relativePath))).toBe(true);
+ }
+});
+
+test("role profile frontmatter includes the required contract keys", () => {
+ for (const relativePath of [
+ "agents/hyper/PROFILE.md",
+ "agents/website-builder/PROFILE.md",
+ ]) {
+ const content = normalize(readFileSync(resolve(relativePath), "utf8"));
+ const frontmatter = content.match(/^---\n([\s\S]*?)\n---\n/);
+ expect(frontmatter?.[1]).toBeDefined();
+
+ if (frontmatter) {
+ for (const key of REQUIRED_PROFILE_KEYS) {
+ expect(frontmatter[1]).toMatch(new RegExp(`^${key}:`, "m"));
+ }
+ }
+ }
+});
+
+test("role lifecycle and checks documents expose required headings", () => {
+ const lifecycleContent = normalize(readFileSync(resolve("agents/hyper/LIFECYCLE.md"), "utf8"));
+ expect(lifecycleContent).toMatch(/^## Entry Criteria$/m);
+ expect(lifecycleContent).toMatch(/^## Steps$/m);
+ expect(lifecycleContent).toMatch(/^## Handoffs$/m);
+ expect(lifecycleContent).toMatch(/^## Exit Criteria$/m);
+ expect(lifecycleContent).toMatch(/^## Failure Escalation$/m);
+
+ const checksContent = normalize(readFileSync(resolve("agents/website-builder/CHECKS.md"), "utf8"));
+ expect(checksContent).toMatch(/^## Preconditions$/m);
+ expect(checksContent).toMatch(/^## Required Evidence$/m);
+ expect(checksContent).toMatch(/^## Done Criteria$/m);
+ expect(checksContent).toMatch(/^## Red Flags$/m);
+});
+
+test("using-hyperstack bootstrap compiler preserves role-routing markers", () => {
+ const source = normalize(readFileSync(resolve("skills/using-hyperstack/SKILL.md"), "utf8"));
+ const { content } = compileUsingHyperstackBootstrap(source);
+ const missing = validateUsingHyperstackBootstrap(content);
+
+ expect(missing.length).toBe(0);
+ expect(content).toMatch(/hyper/);
+ expect(content).toMatch(/website-builder/);
+ expect(content).toMatch(/auto-called/);
+ expect(content).toMatch(/hyper -> website-builder/);
+});
+
+test("website-builder lifecycle requires workspace discovery before website decisions", () => {
+ const lifecycleContent = normalize(readFileSync(resolve("agents/website-builder/LIFECYCLE.md"), "utf8"));
+ const contextContent = normalize(readFileSync(resolve("agents/website-builder/CONTEXT.md"), "utf8"));
+
+ expect(lifecycleContent).toMatch(/workspace/i);
+ expect(lifecycleContent).toMatch(/package\.json|manifests?|dependencies|packages/i);
+ expect(lifecycleContent).toMatch(/frontend core files|core frontend files|routes|components|tokens|styles/i);
+ expect(contextContent).toMatch(/package\.json|manifests?|dependencies|packages/i);
+});
+
+test("designer skill gives user preferences precedence over auto-resolved defaults", () => {
+ const designerContent = normalize(readFileSync(resolve("skills/designer/SKILL.md"), "utf8"));
+
+ expect(designerContent).toMatch(/user preferences?/i);
+ expect(designerContent).toMatch(/preferences?.*override|override.*preferences?/i);
+ expect(designerContent).toMatch(/auto-resolved defaults?|defaults?.*suggestions?/i);
+});
diff --git a/tests/runtime-behaviour.test.ts b/tests/runtime-behaviour.test.ts
new file mode 100644
index 0000000..efa66e9
--- /dev/null
+++ b/tests/runtime-behaviour.test.ts
@@ -0,0 +1,143 @@
+import { test, expect } from "bun:test";
+import { readFile } from "node:fs/promises";
+import { resolve } from "node:path";
+import { once } from "node:events";
+import { setTimeout as delay } from "node:timers/promises";
+import { spawn } from "node:child_process";
+
+function normalize(str: string): string {
+ return str.replace(/\r\n/g, "\n");
+}
+
+async function runSessionStartHook(envOverrides: Record) {
+ const child = spawn(process.execPath, [resolve("hooks/session-start.mjs")], {
+ cwd: process.cwd(),
+ env: { ...process.env, ...envOverrides },
+ stdio: ["ignore", "pipe", "pipe"],
+ });
+
+ let stdout = "";
+ let stderr = "";
+ child.stdout.on("data", (chunk: Buffer | string) => {
+ stdout += chunk.toString();
+ });
+ child.stderr.on("data", (chunk: Buffer | string) => {
+ stderr += chunk.toString();
+ });
+
+ const [exitCode] = (await once(child, "close")) as [number | null];
+ if (exitCode !== 0) {
+ throw new Error(`SessionStart hook failed.\nstdout:\n${stdout}\nstderr:\n${stderr}`);
+ }
+
+ return JSON.parse(stdout) as {
+ additional_context?: string;
+ additionalContext?: string;
+ hookSpecificOutput?: { additionalContext?: string };
+ };
+}
+
+test("Claude SessionStart hook command executes successfully on this platform", async () => {
+ const raw = await readFile(resolve("hooks/hooks.json"), "utf8");
+ const config = JSON.parse(raw) as {
+ hooks: {
+ SessionStart: Array<{
+ hooks: Array<{ command: string }>;
+ }>;
+ };
+ };
+
+ const command = config.hooks.SessionStart[0]?.hooks[0]?.command;
+ expect(command).toBeDefined();
+
+ const scriptPath = resolve("hooks/session-start.mjs");
+ const child = spawn(process.execPath, [scriptPath], {
+ cwd: process.cwd(),
+ env: { ...process.env, CLAUDE_PLUGIN_ROOT: process.cwd() },
+ stdio: ["ignore", "pipe", "pipe"],
+ });
+
+ let stdout = "";
+ let stderr = "";
+ child.stdout.on("data", (chunk: Buffer | string) => {
+ stdout += chunk.toString();
+ });
+ child.stderr.on("data", (chunk: Buffer | string) => {
+ stderr += chunk.toString();
+ });
+
+ const [exitCode] = (await once(child, "close")) as [number | null];
+ if (exitCode !== 0) {
+ throw new Error(`SessionStart hook failed.\nstdout:\n${stdout}\nstderr:\n${stderr}`);
+ }
+
+ const payload = JSON.parse(stdout) as {
+ additionalContext?: string;
+ hookSpecificOutput?: { additionalContext?: string };
+ };
+
+ expect(payload.additionalContext || payload.hookSpecificOutput?.additionalContext).toBeDefined();
+ expect(normalize(payload.additionalContext || payload.hookSpecificOutput?.additionalContext || "")).toMatch(/compiled runtime bootstrap/);
+});
+
+test("SessionStart hook emits Cursor-compatible output shape when CURSOR_PLUGIN_ROOT is set", async () => {
+ const payload = await runSessionStartHook({
+ CURSOR_PLUGIN_ROOT: process.cwd(),
+ CLAUDE_PLUGIN_ROOT: undefined,
+ COPILOT_CLI: undefined,
+ });
+
+ expect(payload.additional_context).toBeDefined();
+ expect(payload.additionalContext).toBeUndefined();
+ expect(payload.hookSpecificOutput).toBeUndefined();
+});
+
+test("SessionStart hook emits SDK-standard output shape when no platform-specific env is set", async () => {
+ const payload = await runSessionStartHook({
+ CURSOR_PLUGIN_ROOT: undefined,
+ CLAUDE_PLUGIN_ROOT: undefined,
+ COPILOT_CLI: undefined,
+ });
+
+ expect(payload.additionalContext).toBeDefined();
+ expect(payload.additional_context).toBeUndefined();
+ expect(payload.hookSpecificOutput).toBeUndefined();
+});
+
+test("SessionStart hook prefers Cursor output when both CURSOR_PLUGIN_ROOT and CLAUDE_PLUGIN_ROOT are set", async () => {
+ const payload = await runSessionStartHook({
+ CURSOR_PLUGIN_ROOT: process.cwd(),
+ CLAUDE_PLUGIN_ROOT: process.cwd(),
+ COPILOT_CLI: undefined,
+ });
+
+ expect(payload.additional_context).toBeDefined();
+ expect(payload.additionalContext).toBeUndefined();
+ expect(payload.hookSpecificOutput).toBeUndefined();
+});
+
+test("package bin entry starts without an immediate runtime crash", async () => {
+ const raw = await readFile(resolve("package.json"), "utf8");
+ const pkg = JSON.parse(raw) as {
+ bin?: { hyperstack?: string };
+ };
+
+ const binEntry = pkg.bin?.hyperstack;
+ expect(binEntry).toBeDefined();
+
+ const child = spawn(process.execPath, [resolve(binEntry!)], {
+ cwd: process.cwd(),
+ stdio: ["pipe", "pipe", "pipe"],
+ });
+
+ let stderr = "";
+ child.stderr.on("data", (chunk: Buffer | string) => {
+ stderr += chunk.toString();
+ });
+
+ await delay(200);
+ expect(child.exitCode).toBeNull();
+
+ child.kill("SIGTERM");
+ await once(child, "close");
+});
diff --git a/tests/skills-index-behaviour.test.ts b/tests/skills-index-behaviour.test.ts
new file mode 100644
index 0000000..3c4fdf7
--- /dev/null
+++ b/tests/skills-index-behaviour.test.ts
@@ -0,0 +1,69 @@
+import { test, expect } from "bun:test";
+import { readFileSync, readdirSync, existsSync } from "node:fs";
+import { resolve, join } from "node:path";
+import { generateIndex } from "../scripts/generate-skills-index.ts";
+
+const SKILLS_DIR = resolve("skills");
+const INDEX_PATH = resolve("skills/INDEX.md");
+
+function normalize(str: string): string {
+ return str.replace(/\r\n/g, "\n");
+}
+
+function getSkillDirs(): string[] {
+ return readdirSync(SKILLS_DIR, { withFileTypes: true })
+ .filter((d) => d.isDirectory())
+ .map((d) => d.name)
+ .filter((name) => existsSync(join(SKILLS_DIR, name, "SKILL.md")));
+}
+
+function getSkillCategory(skillName: string): string | null {
+ const skillFile = join(SKILLS_DIR, skillName, "SKILL.md");
+ const content = normalize(readFileSync(skillFile, "utf8"));
+ const match = content.match(/^category:\s*(.+)$/m);
+ return match ? match[1].trim() : null;
+}
+
+test("skills/INDEX.md stays in sync with actual skill directories", () => {
+ const currentIndex = normalize(readFileSync(INDEX_PATH, "utf8"));
+
+ generateIndex();
+
+ const regenerated = normalize(readFileSync(INDEX_PATH, "utf8"));
+ expect(currentIndex).toBe(regenerated);
+});
+
+test("every skill directory has a SKILL.md with required frontmatter fields", () => {
+ const skillDirs = getSkillDirs();
+ expect(skillDirs.length).toBeGreaterThan(0);
+
+ for (const skillName of skillDirs) {
+ const skillFile = join(SKILLS_DIR, skillName, "SKILL.md");
+ const content = normalize(readFileSync(skillFile, "utf8"));
+
+ expect(content).toMatch(/^---\n/m);
+ expect(content).toMatch(/^name:\s*.+$/m);
+ expect(content).toMatch(/^category:\s*.+$/m);
+ expect(content).toMatch(/^description:\s*.+/ms);
+ }
+});
+
+test("every skill has a valid category (core | domain | meta)", () => {
+ const VALID_CATEGORIES = new Set(["core", "domain", "meta"]);
+ const skillDirs = getSkillDirs();
+
+ for (const skillName of skillDirs) {
+ const category = getSkillCategory(skillName);
+ expect(category).toBeDefined();
+ expect(VALID_CATEGORIES.has(category!)).toBe(true);
+ }
+});
+
+test("skills/INDEX.md references every skill directory that has a SKILL.md", () => {
+ const indexContent = normalize(readFileSync(INDEX_PATH, "utf8"));
+ const skillDirs = getSkillDirs();
+
+ for (const skillName of skillDirs) {
+ expect(indexContent).toMatch(new RegExp(`\`${skillName}\``));
+ }
+});
diff --git a/tests/workflow-behaviour.test.ts b/tests/workflow-behaviour.test.ts
new file mode 100644
index 0000000..b064ae6
--- /dev/null
+++ b/tests/workflow-behaviour.test.ts
@@ -0,0 +1,15 @@
+import { test, expect } from "bun:test";
+import { readFile } from "node:fs/promises";
+import { resolve } from "node:path";
+
+test("publish workflow verifies the package across the supported OS and Bun matrix before publishing", async () => {
+ const workflow = (await readFile(resolve(".github/workflows/publish.yml"), "utf8")).replace(/\r\n/g, "\n");
+
+ expect(workflow).toMatch(/pull_request:/);
+ expect(workflow).toMatch(/strategy:\s*\n(?:\s+.*\n)*?\s+matrix:/);
+ expect(workflow).toMatch(/os:\s*\[ubuntu-latest,\s*macos-latest,\s*windows-latest\]/);
+ expect(workflow).toMatch(/bun-version:/);
+ expect(workflow).toMatch(/needs:\s*verify/);
+ expect(workflow).toMatch(/gh release create/);
+ expect(workflow).toMatch(/docker\/build-push-action/);
+});
diff --git a/topology/agents/backend-builder.yaml b/topology/agents/backend-builder.yaml
deleted file mode 100644
index 0e69225..0000000
--- a/topology/agents/backend-builder.yaml
+++ /dev/null
@@ -1,20 +0,0 @@
-id: backend-builder
-kind: specialist
-domains:
- - backend
-allowed_skills:
- - test-first
- - debug-discipline
- - engineering-discipline
- - worktree-isolation
- - security-review
-allowed_bundles:
- - backend.http
- - backend.lang.go
- - backend.lang.rust
-forbidden_bundles:
- - frontend.design
- - frontend.react
-handoff_in: task_handoff
-handoff_out: build_result
-completion_proof: executable
diff --git a/topology/agents/frontend-builder.yaml b/topology/agents/frontend-builder.yaml
deleted file mode 100644
index d878a4d..0000000
--- a/topology/agents/frontend-builder.yaml
+++ /dev/null
@@ -1,22 +0,0 @@
-id: frontend-builder
-kind: specialist
-domains:
- - frontend
-allowed_skills:
- - test-first
- - debug-discipline
- - engineering-discipline
- - worktree-isolation
- - designer
- - behaviour-analysis
- - shadcn-expert
-allowed_bundles:
- - frontend.design
- - frontend.react
-forbidden_bundles:
- - backend.http
- - backend.lang.go
- - backend.lang.rust
-handoff_in: task_handoff
-handoff_out: build_result
-completion_proof: visual_and_behavioral
diff --git a/topology/agents/fullstack-builder.yaml b/topology/agents/fullstack-builder.yaml
deleted file mode 100644
index eed25f8..0000000
--- a/topology/agents/fullstack-builder.yaml
+++ /dev/null
@@ -1,24 +0,0 @@
-id: fullstack-builder
-kind: cross-domain
-domains:
- - frontend
- - backend
-allowed_skills:
- - test-first
- - debug-discipline
- - engineering-discipline
- - worktree-isolation
- - designer
- - behaviour-analysis
- - shadcn-expert
- - security-review
-allowed_bundles:
- - frontend.design
- - frontend.react
- - backend.http
- - backend.lang.go
- - backend.lang.rust
-forbidden_bundles: []
-handoff_in: task_handoff
-handoff_out: build_result
-completion_proof: strictest_touched_domain
diff --git a/topology/agents/hyper.yaml b/topology/agents/hyper.yaml
deleted file mode 100644
index e38fd01..0000000
--- a/topology/agents/hyper.yaml
+++ /dev/null
@@ -1,20 +0,0 @@
-id: hyper
-kind: orchestrator
-domains:
- - shared
-allowed_skills:
- - blueprint
- - forge-plan
- - run-plan
- - parallel-dispatch
- - subagent-ops
- - autonomous-mode
- - ship-gate
- - code-review
- - deliver
-allowed_bundles:
- - shared.system
-forbidden_bundles: []
-handoff_in: user_request
-handoff_out: task_handoff
-completion_proof: routing_and_verification
diff --git a/topology/artifacts/build_result.yaml b/topology/artifacts/build_result.yaml
deleted file mode 100644
index fa068f1..0000000
--- a/topology/artifacts/build_result.yaml
+++ /dev/null
@@ -1,6 +0,0 @@
-id: build_result
-required_fields:
- - agent_id
- - changed_files
- - verification_commands
-proof_mode: executable
diff --git a/topology/artifacts/design_contract.yaml b/topology/artifacts/design_contract.yaml
deleted file mode 100644
index 0189b9e..0000000
--- a/topology/artifacts/design_contract.yaml
+++ /dev/null
@@ -1,9 +0,0 @@
-id: design_contract
-required_fields:
- - visual_theme
- - color_system
- - typography
- - spacing
- - component_states
- - motion_rules
-proof_mode: visual_contract_conditional
diff --git a/topology/artifacts/task_handoff.yaml b/topology/artifacts/task_handoff.yaml
deleted file mode 100644
index 11c4035..0000000
--- a/topology/artifacts/task_handoff.yaml
+++ /dev/null
@@ -1,10 +0,0 @@
-id: task_handoff
-required_fields:
- - request_id
- - domain_targets
- - capability_targets
- - success_criteria
- - change_classification
- - requires_design_contract
- - required_artifacts
-proof_mode: routing_only
diff --git a/topology/artifacts/verification_report.yaml b/topology/artifacts/verification_report.yaml
deleted file mode 100644
index 7dbb04e..0000000
--- a/topology/artifacts/verification_report.yaml
+++ /dev/null
@@ -1,6 +0,0 @@
-id: verification_report
-required_fields:
- - status
- - proof_mode
- - covered_paths
-proof_mode: routing_and_verification
diff --git a/topology/artifacts/workspace_inventory.yaml b/topology/artifacts/workspace_inventory.yaml
deleted file mode 100644
index 637a712..0000000
--- a/topology/artifacts/workspace_inventory.yaml
+++ /dev/null
@@ -1,9 +0,0 @@
-id: workspace_inventory
-required_fields:
- - repo_type
- - stack
- - touched_surfaces
- - existing_patterns
- - verification_commands
- - project_mode
-proof_mode: discovery_only
diff --git a/topology/bundles/backend.http.yaml b/topology/bundles/backend.http.yaml
deleted file mode 100644
index 502639c..0000000
--- a/topology/bundles/backend.http.yaml
+++ /dev/null
@@ -1,10 +0,0 @@
-id: backend.http
-domain: backend
-capabilities:
- - backend.http.patterns
-sources:
- - corpus/backend/echo
-tool_prefixes:
- - echo_
-output_contracts:
- - backend_recipe
diff --git a/topology/bundles/backend.lang.go.yaml b/topology/bundles/backend.lang.go.yaml
deleted file mode 100644
index 01933dc..0000000
--- a/topology/bundles/backend.lang.go.yaml
+++ /dev/null
@@ -1,10 +0,0 @@
-id: backend.lang.go
-domain: backend
-capabilities:
- - backend.go.patterns
-sources:
- - corpus/backend/golang
-tool_prefixes:
- - golang_
-output_contracts:
- - go_reference
diff --git a/topology/bundles/backend.lang.rust.yaml b/topology/bundles/backend.lang.rust.yaml
deleted file mode 100644
index 5a38fe4..0000000
--- a/topology/bundles/backend.lang.rust.yaml
+++ /dev/null
@@ -1,10 +0,0 @@
-id: backend.lang.rust
-domain: backend
-capabilities:
- - backend.rust.patterns
-sources:
- - corpus/backend/rust
-tool_prefixes:
- - rust_
-output_contracts:
- - rust_reference
diff --git a/topology/bundles/frontend.design.yaml b/topology/bundles/frontend.design.yaml
deleted file mode 100644
index b104568..0000000
--- a/topology/bundles/frontend.design.yaml
+++ /dev/null
@@ -1,18 +0,0 @@
-id: frontend.design
-domain: frontend
-capabilities:
- - design.intent
- - design.contract
- - design.tokens
-sources:
- - corpus/frontend/designer
- - corpus/frontend/design-tokens
- - corpus/frontend/ui-ux
-tool_prefixes:
- - designer_
- - design_tokens_
- - ui_ux_
-output_contracts:
- - intent_resolution
- - design_contract
- - token_spec
diff --git a/topology/bundles/frontend.react.yaml b/topology/bundles/frontend.react.yaml
deleted file mode 100644
index e06f064..0000000
--- a/topology/bundles/frontend.react.yaml
+++ /dev/null
@@ -1,21 +0,0 @@
-id: frontend.react
-domain: frontend
-capabilities:
- - frontend.patterns
- - frontend.motion
- - frontend.flow
-sources:
- - corpus/frontend/react
- - corpus/frontend/shadcn
- - corpus/frontend/motion
- - corpus/frontend/lenis
- - corpus/frontend/reactflow
-tool_prefixes:
- - react_
- - shadcn_
- - motion_
- - lenis_
- - reactflow_
-output_contracts:
- - frontend_reference
- - component_contract
diff --git a/topology/bundles/shared.system.yaml b/topology/bundles/shared.system.yaml
deleted file mode 100644
index 875c58d..0000000
--- a/topology/bundles/shared.system.yaml
+++ /dev/null
@@ -1,10 +0,0 @@
-id: shared.system
-domain: shared
-capabilities:
- - system.setup
-sources:
- - corpus/shared/system
-tool_prefixes:
- - hyperstack_
-output_contracts:
- - setup_patch
diff --git a/topology/domains/backend.yaml b/topology/domains/backend.yaml
deleted file mode 100644
index 152c866..0000000
--- a/topology/domains/backend.yaml
+++ /dev/null
@@ -1,13 +0,0 @@
-id: backend
-write_policy: direct
-completion_proof: executable
-truth_bundles:
- - backend.http
- - backend.lang.go
- - backend.lang.rust
-required_gates: []
-optional_gates:
- - security-review
-forbidden_bundles:
- - frontend.design
- - frontend.react
diff --git a/topology/domains/frontend.yaml b/topology/domains/frontend.yaml
deleted file mode 100644
index 375e8c9..0000000
--- a/topology/domains/frontend.yaml
+++ /dev/null
@@ -1,15 +0,0 @@
-id: frontend
-write_policy: constrained
-completion_proof: visual_and_behavioral
-truth_bundles:
- - frontend.design
- - frontend.react
-required_gates:
- - designer
- - behaviour-analysis
-optional_gates:
- - shadcn-expert
-forbidden_bundles:
- - backend.http
- - backend.lang.go
- - backend.lang.rust
diff --git a/topology/domains/shared.yaml b/topology/domains/shared.yaml
deleted file mode 100644
index 31e243a..0000000
--- a/topology/domains/shared.yaml
+++ /dev/null
@@ -1,8 +0,0 @@
-id: shared
-write_policy: policy_only
-completion_proof: routing_and_verification
-truth_bundles:
- - shared.system
-required_gates: []
-optional_gates: []
-forbidden_bundles: []
diff --git a/topology/manifest.yaml b/topology/manifest.yaml
deleted file mode 100644
index 3569f0a..0000000
--- a/topology/manifest.yaml
+++ /dev/null
@@ -1,25 +0,0 @@
-version: 1
-default_transport: local-tools
-entry_agent: hyper
-domains:
- - frontend
- - backend
- - shared
-agents:
- - hyper
- - frontend-builder
- - backend-builder
- - fullstack-builder
-bundles:
- - shared.system
- - frontend.design
- - frontend.react
- - backend.http
- - backend.lang.go
- - backend.lang.rust
-artifacts:
- - workspace_inventory
- - task_handoff
- - design_contract
- - build_result
- - verification_report
diff --git a/topology/routes/defaults.yaml b/topology/routes/defaults.yaml
deleted file mode 100644
index 1d8dc52..0000000
--- a/topology/routes/defaults.yaml
+++ /dev/null
@@ -1,15 +0,0 @@
-default_agent: hyper
-requires_workspace_inventory: true
-domain_preference:
- frontend: frontend-builder
- backend: backend-builder
- shared: hyper
-cross_domain_agent: fullstack-builder
-design_contract_required_when:
- - new_surface
- - visual_semantic_change
- - no_existing_pattern_match
-strictest_proof_order:
- - routing_and_verification
- - executable
- - visual_and_behavioral
diff --git a/topology/skills/groups.yaml b/topology/skills/groups.yaml
deleted file mode 100644
index dcc217e..0000000
--- a/topology/skills/groups.yaml
+++ /dev/null
@@ -1,23 +0,0 @@
-groups:
- orchestration:
- - hyperstack
- - blueprint
- - forge-plan
- - run-plan
- - parallel-dispatch
- - subagent-ops
- - autonomous-mode
- quality:
- - test-first
- - debug-discipline
- - engineering-discipline
- - ship-gate
- - code-review
- - deliver
- - worktree-isolation
- frontend-specialty:
- - designer
- - behaviour-analysis
- - shadcn-expert
- backend-specialty:
- - security-review
diff --git a/topology/skills/policies.yaml b/topology/skills/policies.yaml
deleted file mode 100644
index 66fb826..0000000
--- a/topology/skills/policies.yaml
+++ /dev/null
@@ -1,26 +0,0 @@
-skills:
- designer:
- phase: design
- domains:
- - frontend
- requires_artifacts:
- - task_handoff
- produces_artifacts:
- - design_contract
- behaviour-analysis:
- phase: verify
- domains:
- - frontend
- requires_artifacts:
- - build_result
- - design_contract
- produces_artifacts:
- - verification_report
- security-review:
- phase: verify
- domains:
- - backend
- requires_artifacts:
- - build_result
- produces_artifacts:
- - review_report