@tsrx/mcp 0.0.33 → 0.0.37

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -88,7 +88,7 @@ Add the generic config above to your Codex MCP configuration.
88
88
  target-neutral authoring advice with linked docs resources.
89
89
  - `review-tsrx-accessibility` - review TSRX source for common accessibility issues
90
90
  before browser-based Axe validation, including missing button names, unlabeled
91
- form controls, and visible text written in a non-rendering shape.
91
+ form controls, and visible text accidentally wrapped in quote characters.
92
92
  - `review-tsrx-styles` - review function-local style usage for malformed style
93
93
  blocks, broad selectors, root styling, and contrast risks.
94
94
  - `review-tsrx-components` - review component structure and suggest extraction
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "description": "MCP server for TSRX documentation and project context",
4
4
  "license": "MIT",
5
5
  "author": "Dominic Gannaway",
6
- "version": "0.0.33",
6
+ "version": "0.0.37",
7
7
  "type": "module",
8
8
  "repository": {
9
9
  "type": "git",
@@ -37,8 +37,8 @@
37
37
  "@modelcontextprotocol/sdk": "^1.29.0",
38
38
  "prettier": "^3.8.3",
39
39
  "zod": "^4.3.6",
40
- "@tsrx/core": "0.1.20",
41
- "@tsrx/prettier-plugin": "0.3.72"
40
+ "@tsrx/core": "0.1.24",
41
+ "@tsrx/prettier-plugin": "0.3.76"
42
42
  },
43
43
  "devDependencies": {
44
44
  "@types/node": "^24.3.0",
package/src/analyze.js CHANGED
@@ -3,7 +3,11 @@ import {
3
3
  TSRX_DO_WHILE_STATEMENT_ERROR,
4
4
  TSRX_FOR_IN_STATEMENT_ERROR,
5
5
  TSRX_FOR_STATEMENT_ERROR,
6
+ TSRX_IF_BREAK_ERROR,
7
+ TSRX_IF_CONTINUE_ERROR,
8
+ TSRX_IF_RETURN_ERROR,
6
9
  TSRX_LOOP_BREAK_ERROR,
10
+ TSRX_LOOP_CONTINUE_ERROR,
7
11
  TSRX_LOOP_RETURN_ERROR,
8
12
  TSRX_RETURN_STATEMENT_ERROR,
9
13
  TSRX_WHILE_STATEMENT_ERROR,
@@ -76,20 +80,50 @@ function create_advice(input) {
76
80
  advice.push({
77
81
  kind: 'jsx-expression-value',
78
82
  severity: 'info',
79
- title: 'Wrap expression-position JSX',
83
+ title: 'Use JSX-shaped expression values',
80
84
  message:
81
- 'When JSX is needed as a value, wrap native TSRX in a fragment `<>...</>` or use a host compatibility island such as `<tsx:react>...</tsx:react>`.',
82
- documentation: ['tsrx://docs/tsx-expression-values.md'],
85
+ 'TSRX expression values use JSX-shaped nodes. Use a JSXElement directly for one child, a JSXFragment when the value needs multiple children, or a JSX statement container when setup must produce one final output.',
86
+ documentation: ['tsrx://docs/expression-values.md'],
83
87
  });
84
88
  }
85
89
 
86
- if (error_messages.has(TSRX_LOOP_RETURN_ERROR) || error_messages.has(TSRX_LOOP_BREAK_ERROR)) {
90
+ if (error_codes.has(DIAGNOSTIC_CODES.FORGOTTEN_STATEMENT_CONTAINER)) {
91
+ advice.push({
92
+ kind: 'forgotten-statement-container',
93
+ severity: 'error',
94
+ title: 'Add @ before statement-container braces',
95
+ message:
96
+ 'A function body with TypeScript setup followed by bare TSRX output must use a JSX statement container. Change the opening `{` to `@{`, or use an ordinary `return` when the function only returns JSX.',
97
+ documentation: ['tsrx://docs/components.md', 'tsrx://docs/expression-values.md'],
98
+ });
99
+ }
100
+
101
+ if (
102
+ error_messages.has(TSRX_LOOP_RETURN_ERROR) ||
103
+ error_messages.has(TSRX_LOOP_BREAK_ERROR) ||
104
+ error_messages.has(TSRX_LOOP_CONTINUE_ERROR)
105
+ ) {
87
106
  advice.push({
88
107
  kind: 'tsrx-loop-control-flow',
89
108
  severity: 'error',
90
- title: 'Use continue inside TSRX for...of loops',
109
+ title: 'Filter before TSRX for...of loops',
110
+ message:
111
+ 'Direct continue, break, and return statements are not valid inside TSRX @for loops. Filter the iterable before rendering, use @empty { ... } for the no-items fallback, and keep ordinary JavaScript control flow inside nested functions.',
112
+ documentation: ['tsrx://docs/control-flow.md'],
113
+ });
114
+ }
115
+
116
+ if (
117
+ error_messages.has(TSRX_IF_RETURN_ERROR) ||
118
+ error_messages.has(TSRX_IF_BREAK_ERROR) ||
119
+ error_messages.has(TSRX_IF_CONTINUE_ERROR)
120
+ ) {
121
+ advice.push({
122
+ kind: 'tsrx-if-control-flow',
123
+ severity: 'error',
124
+ title: 'Keep @if branches render-only',
91
125
  message:
92
- 'Return statements are not valid inside TSRX templates, and break statements are not valid inside TSRX for...of loops. Use continue to skip the current rendered item. Nested functions inside the loop keep ordinary JavaScript control flow.',
126
+ 'Direct return, continue, and break statements are not valid inside TSRX @if branches. Use an ordinary JavaScript if statement before template output for guard returns, or render the branch output conditionally.',
93
127
  documentation: ['tsrx://docs/control-flow.md'],
94
128
  });
95
129
  }
@@ -98,9 +132,9 @@ function create_advice(input) {
98
132
  advice.push({
99
133
  kind: 'tsrx-template-return',
100
134
  severity: 'error',
101
- title: 'Move returns outside TSRX templates',
135
+ title: 'Put returns in TypeScript setup',
102
136
  message:
103
- 'Return statements are ordinary JavaScript control flow for functions, not template control flow. Use guard clauses before returning TSRX, or render conditionally inside the template.',
137
+ 'Return statements are ordinary JavaScript control flow for functions, not template output. Use guard clauses before a JSX statement container or return value, or render conditionally inside the template.',
104
138
  documentation: ['tsrx://docs/control-flow.md'],
105
139
  });
106
140
  }
package/src/authoring.js CHANGED
@@ -129,11 +129,11 @@ function collect_direct_quoted_text_issues(code) {
129
129
  issues.push({
130
130
  kind: 'direct-quoted-text',
131
131
  severity: match[1] === 'button' ? 'error' : 'warning',
132
- title: 'Use expression text for visible TSRX text',
132
+ title: 'Write visible TSRX text without extra quotes',
133
133
  message:
134
- 'Direct quoted text inside TSRX elements can be treated as a JavaScript string statement instead of rendered text in some target output.',
134
+ 'Quoted text inside TSRX elements renders the quote characters as part of JSX text. Use plain JSX text unless you intentionally want visible quotes.',
135
135
  snippet: line_snippet(match[0]),
136
- recommendation: `Replace it with expression text, for example <${match[1]}>{'${match[3].replace(/'/g, "\\'")}'}</${match[1]}>.`,
136
+ recommendation: `Replace it with plain JSX text, for example <${match[1]}>${match[3]}</${match[1]}>.`,
137
137
  documentation: ['tsrx://docs/text-and-template-expressions.md'],
138
138
  });
139
139
  }
@@ -160,6 +160,7 @@ export function review_tsrx_accessibility(input) {
160
160
  has_attribute(attrs, 'aria-label') ||
161
161
  has_attribute(attrs, 'aria-labelledby') ||
162
162
  has_attribute(attrs, 'title') ||
163
+ has_visible_label_text(inner) ||
163
164
  (has_expression_text(inner) && !/^\s*\{\s*['"`]\s*['"`]\s*\}\s*$/.test(inner));
164
165
 
165
166
  if (!has_name || has_direct_quoted_text(inner)) {
@@ -171,7 +172,7 @@ export function review_tsrx_accessibility(input) {
171
172
  'Buttons must expose a visible label or an aria-label. This is especially important for disabled submit buttons and icon-only controls.',
172
173
  snippet: line_snippet(match[0]),
173
174
  recommendation:
174
- "Use visible expression text such as {'Add task'} or add aria-label for icon-only buttons.",
175
+ 'Use visible JSX text such as Add task or add aria-label for icon-only buttons.',
175
176
  documentation: ['tsrx://docs/text-and-template-expressions.md'],
176
177
  });
177
178
  }
@@ -227,7 +228,7 @@ export function review_tsrx_accessibility(input) {
227
228
  ? ['Run browser-based Axe or the benchmark validation loop.']
228
229
  : [
229
230
  'Fix error-severity issues before returning code.',
230
- 'Prefer expression text for visible labels.',
231
+ 'Prefer plain JSX text for visible labels.',
231
232
  'Run review-tsrx-accessibility again, then compile-tsrx and browser-based Axe.',
232
233
  ],
233
234
  };
package/src/format.js CHANGED
@@ -104,7 +104,7 @@ export async function format_tsrx(input) {
104
104
  ...(project_config ?? {}),
105
105
  ...user_overrides,
106
106
  filepath: resolved_filename,
107
- parser: 'ripple',
107
+ parser: 'tsrx',
108
108
  plugins: [tsrx_prettier_plugin],
109
109
  };
110
110
 
@@ -11,7 +11,7 @@ export const documentation_sections = [
11
11
  use_cases:
12
12
  'always, introduction, explain tsrx, compare jsx, language model context, runtime targets',
13
13
  content:
14
- '# TSRX Overview\n\nTSRX is a TypeScript language extension for authoring declarative UI in .tsrx files. It adds a small set of syntax forms on top of TypeScript, while letting each target compiler define the runtime semantics.\n\nCore ideas:\n- Components are ordinary TypeScript functions that return TSRX.\n- TSRX opens in expression position, then template children use a template statement list inside the fragment.\n- control-flow statements can contain template output.\n- native TSRX can be returned directly as `<div />` or `<>...</>`.\n- lazy destructuring uses &[] and &{} for by-reference bindings.\n\nThe core language docs should stay target-neutral. After identifying the active runtime target, use target-specific docs, prompts, or skills for runtime imports, bundler setup, and semantics that are not defined by TSRX itself.\n\nSource: website-tsrx/src/pages/specification.tsrx',
14
+ '# TSRX Overview\n\nTSRX is a TypeScript language extension for authoring declarative UI in .tsrx files. It adds a small set of syntax forms on top of TypeScript, while letting each target compiler define the runtime semantics.\n\nCore ideas:\n- Components are ordinary TypeScript functions. Use a JSX statement container, `@{ ... }`, when the function body is mostly TSRX setup plus one rendered output.\n- JSXElement, JSXFragment, JSXText, JSXExpressionContainer, attributes, and spreads use the standard JSX node family.\n- A mixed setup/template scope must finish with exactly one output node: a JSXElement, JSXFragment, or JSX control-flow expression. Wrap plain text, expression containers, or multiple siblings in a fragment.\n- Template control flow uses directive expressions: `@if`, `@for`, `@switch`, and `@try`; every directive body uses a `{...}` template block.\n- lazy destructuring uses &[] and &{} for by-reference bindings.\n\nThe core language docs should stay target-neutral. After identifying the active runtime target, use target-specific docs, prompts, or skills for runtime imports, bundler setup, and semantics that are not defined by TSRX itself.\n\nSource: website-tsrx/src/pages/specification.tsrx',
15
15
  },
16
16
  {
17
17
  slug: 'components',
@@ -19,14 +19,15 @@ export const documentation_sections = [
19
19
  use_cases:
20
20
  'components, functions, props, authoring .tsrx files, jsx return syntax',
21
21
  content:
22
- '# Function Components\n\nAuthor UI as ordinary TypeScript functions that return TSRX.\n\n```tsx\nfunction Button(props: { label: string }) {\n return <button>{props.label}</button>;\n}\n```\n\nInside the returned TSRX fragment, template elements and control flow share the same template statement list.\n\nSource: website-tsrx/src/pages/specification.tsrx#components',
22
+ '# Function Components\n\nAuthor UI as ordinary TypeScript functions. Components can return JSX directly, or use a JSX statement container when setup and output should live together.\n\n```tsx\nexport function Button({ label }: { label: string }) @{\n <button>{label}</button>\n}\n```\n\nInside `@{ ... }`, put any setup statements first and end with one rendered output node. No JavaScript setup can appear after that output.\n\nIf a normal function body contains setup statements followed by bare TSRX output, add the missing `@` before the opening brace. Plain `{ ... }` is JavaScript; `@{ ... }` is the statement-container form.\n\nSource: website-tsrx/src/pages/specification.tsrx#components',
23
23
  },
24
24
  {
25
25
  slug: 'text-and-template-expressions',
26
26
  title: 'Text and Template Expressions',
27
- use_cases: 'text children, quoted text, raw text errors, string literals',
27
+ use_cases:
28
+ 'text children, jsx text, comments, string literals, expression containers',
28
29
  content:
29
- '# Text and Template Expressions\n\nRaw unquoted text children are not valid TSRX. Static text should be written as a direct double-quoted child, and dynamic values should be wrapped in braces.\n\n```tsx\nfunction Greeting({ name }: { name: string }) {\n return <>\n <h1>"Hello"</h1>\n <p>{name}</p>\n </>;\n}\n```\n\nSingle-quoted strings and template literals remain JavaScript expressions, so they must be inside braces. When you need explicit string coercion, write it in JavaScript with `String(value)`, `value + \'\'`, or a typed string value.\n\nSpecification grammar:\n\n```text\nDoubleQuotedTextChild :\n " JSXStringCharactersopt "\n\nTemplateExpression :\n { AssignmentExpression }\n```\n\nSource: website-tsrx/src/pages/specification.tsrx#templates',
30
+ '# Text and Template Expressions\n\nStatic text is JSXText and can be written directly between tags. Dynamic values use normal JSX expression containers.\n\n```tsx\nfunction Greeting({ name }: { name: string }) @{\n <>\n <h1>Hello</h1>\n <p>{name}</p>\n </>\n}\n```\n\nJavaScript comments are also allowed between template children and are not rendered. Use braces for JavaScript expressions, including string literals that should be evaluated as JavaScript.\n\nSpecification grammar:\n\n```text\nJSXTextChild :\n JSXTextCharacters\n\nJSXExpressionContainer :\n { AssignmentExpression }\n\nJSXCodeBlock :\n @{ StatementListItemListopt TemplateOutput }\n\nTemplateBlock :\n { TemplateChildrenopt }\n { StatementListItemList TemplateOutput }\n\nJSXIfExpression :\n @if ( Expression ) TemplateBlock\n @if ( Expression ) TemplateBlock @else TemplateBlock\n @if ( Expression ) TemplateBlock @else JSXIfExpression\n\nJSXForExpression :\n @for ( ForHeader TemplateForOptionsopt ) TemplateBlock\n @for ( ForHeader TemplateForOptionsopt ) TemplateBlock @empty TemplateBlock\n\nJSXSwitchExpression :\n @switch ( Expression ) { JSXSwitchCaseListopt }\n\nJSXSwitchCase :\n @case Expression : TemplateBlock\n @default : TemplateBlock\n\nJSXTryExpression :\n @try TemplateBlock @pending TemplateBlock\n @try TemplateBlock @catch ( CatchParameteropt ) TemplateBlock\n```\n\nSource: website-tsrx/src/pages/specification.tsrx#templates',
30
31
  },
31
32
  {
32
33
  slug: 'expression-values',
@@ -34,7 +35,7 @@ export const documentation_sections = [
34
35
  use_cases:
35
36
  'fragments, pass template as prop, return template from helper, render props, expression position jsx',
36
37
  content:
37
- '# Expression Values\n\nReturned TSRX opens in expression position. Inside the TSRX fragment, template elements are statements and control flow can emit UI.\n\n```tsx\nfunction App() {\n const title = <>"Settings"</>;\n\n return <Card title={title} />;\n}\n```\n\nNative TSRX expression fragments can contain setup statements and template control flow:\n\n```tsx\nfunction badge(label: string) {\n return <>\n const normalized = label.trim();\n <span class="badge">{normalized}</span>\n </>;\n}\n```\n\nUse fragments for assigning UI to variables, returning UI from helper functions, or passing UI as props.\n\nSpecification grammar:\n\n```text\nTsrxExpression :\n Element\n <> TemplateChildrenopt </>\n```\n\nSource: website-tsrx/src/pages/specification.tsrx#expression-values',
38
+ '# Expression Values\n\nTSRX uses JSX-shaped expression values. A single JSXElement can be assigned, passed, or returned directly. Use a JSXFragment when the value needs multiple children, or use a JSX statement container when setup needs to produce one final output.\n\n```tsx\nfunction App() @{\n const title = <span class="title">Settings</span>;\n const badge = (label: string) => @{\n const normalized = label.trim();\n\n <span class="badge">{normalized}</span>\n };\n\n <Card title={title}>{badge(\'New\')}</Card>\n}\n```\n\n`@{ ... }` is a JSX statement container. A normal JSX fragment, element body, or control-flow branch can also contain setup before output, but that scope must still end with one output node. Use a fragment when the output is text, an expression container, or multiple siblings.\n\nWhen generating code, prefer `function Component(props) @{ ... }` for component bodies that need hooks, setup, guard returns, and final template output together. Do not silently drop the `@`; the compiler treats plain braces as a normal JavaScript function body.\n\nSpecification grammar:\n\n```text\nPrimaryExpression :\n JSXElement\n JSXFragment\n JSXStyleElement\n JSXCodeBlock\n JSXIfExpression\n JSXForExpression\n JSXSwitchExpression\n JSXTryExpression\n```\n\nSource: website-tsrx/src/pages/specification.tsrx#expression-values',
38
39
  },
39
40
  {
40
41
  slug: 'control-flow',
@@ -42,7 +43,7 @@ export const documentation_sections = [
42
43
  use_cases:
43
44
  'if else, for loops, switch, try catch, conditional rendering, lists, guard returns',
44
45
  content:
45
- '# Control Flow\n\nStandard JavaScript control flow can contain template statements inside returned TSRX fragments and nested element children.\n\n```tsx\nfunction List({ items }: { items: string[] }) {\n return <>\n if (items.length === 0) {\n <p>"No items"</p>\n } else {\n <ul>\n for (const item of items; index i; key item) {\n if (!item) continue;\n <li>{item}</li>\n }\n </ul>\n }\n </>;\n}\n```\n\nUse normal function returns for guard exits before TSRX opens. Inside a nested TSRX loop body, `continue` skips the current rendered iteration.\n\n`return` statements are invalid anywhere inside returned TSRX element or fragment bodies. Inside a TSRX `for...of` loop, `continue` skips the current rendered iteration and is the only supported top-level loop control-flow statement. `break` is invalid inside TSRX `for...of` loops; use `continue` for item skips and `break` only for `switch` cases.\n\nTSRX rendering supports `for...of` list loops. Regular `for`, `for...in`, `while`, and `do...while` loops are not supported in TSRX template scope. Move imperative loops into a nested function, event handler, effect, or helper where normal JavaScript control flow rules apply.\n\nSource: website-tsrx/src/pages/features.tsrx#for',
46
+ '# Control Flow\n\nTemplate control flow uses directive-prefixed expressions inside JSX children.\n\n```tsx\nfunction List({ items }: { items: string[] }) @{\n <>\n @if (items.length === 0) {\n <p>No items</p>\n } @else {\n <ul>\n @for (const item of items.filter(Boolean); index i; key item) {\n <li>{item}</li>\n } @empty {\n <li>No items</li>\n }\n </ul>\n }\n </>\n}\n```\n\nUse normal function returns for guard exits before entering template output. Filter a collection before passing it to `@for` when some items should not render, and use `@empty { ... }` for the no-items fallback.\n\n`return` statements are not template output. Put guard returns before the JSX statement container or return value, or render conditionally with `@if`. Inside TSRX `@if` branches and `@for ... of` loops, direct `continue`, `break`, and `return` statements are invalid. Inside a TSRX `@switch` case body, both `break` and `return` are invalid because cases are isolated template blocks.\n\nTSRX rendering supports `@for ... of` list loops. Regular `for`, `for...in`, `while`, and `do...while` loops are not rendering constructs. Move imperative loops into setup code, a nested function, event handler, effect, or helper where normal JavaScript control-flow rules apply.\n\nSource: website-tsrx/src/pages/features.tsrx#for',
46
47
  },
47
48
  {
48
49
  slug: 'lazy-destructuring',
@@ -57,7 +58,15 @@ export const documentation_sections = [
57
58
  use_cases:
58
59
  'style expressions, scoped css, module server, submodule imports, compile-time identifiers',
59
60
  content:
60
- '# Style and Server Extensions\n\nAssign a `<style>` expression to expose scoped CSS class names declared in the current module.\n\n```tsx\nconst styles = <style>\n .card { padding: 1rem; }\n</style>;\n\nreturn <Child class={styles.card} />;\n```\n\n`module server { ... }` declares a server-oriented submodule in the Ripple host profile. Import exported functions with `import { load } from server` before use.\n\nSpecification grammar:\n\n```text\nStyleElement :\n <style JSXAttributesopt> CSSSource </style>\n\nSubmoduleDeclaration :\n module Identifier { ModuleItemListopt }\n\nSubmoduleImportDeclaration :\n import ImportClause from Identifier ;\n\n```\n\nSource: website-tsrx/src/pages/specification.tsrx#style',
61
+ '# Style and Server Extensions\n\nAssign a `<style>` expression to expose scoped CSS class names declared in the current module.\n\n```tsx\nconst styles = <style>\n .card { padding: 1rem; }\n</style>;\n\nexport function ChildCard() @{\n <>\n <Child class={styles.card} />\n </>\n}\n```\n\n`module server { ... }` declares a server-oriented submodule in the Ripple host profile. Import exported functions with `import { load } from server` before use.\n\nSpecification grammar:\n\n```text\nJSXStyleElement :\n <style JSXAttributesopt> CSSSource </style>\n\nSubmoduleDeclaration :\n module Identifier { ModuleItemListopt }\n\nSubmoduleImportDeclaration :\n import ImportClause from Identifier ;\n\n```\n\nSource: website-tsrx/src/pages/specification.tsrx#style',
62
+ },
63
+ {
64
+ slug: 'dynamic-elements-and-components',
65
+ title: 'Dynamic Elements and Components',
66
+ use_cases:
67
+ 'dynamic elements, dynamic components, Dynamic component, is prop, runtime tag, runtime component, removed <@tag syntax',
68
+ content:
69
+ "# Dynamic Elements and Components\n\nUse the target runtime `Dynamic` component when the element tag or component constructor is chosen at runtime. The source form is ordinary JSX: `<Dynamic is={value} />`.\n\nImport `Dynamic` from the active target runtime module:\n\n| Target | Import |\n| --- | --- |\n| React | `import { Dynamic } from '@tsrx/react/dynamic';` |\n| Preact | `import { Dynamic } from '@tsrx/preact/dynamic';` |\n| Solid | `import { Dynamic } from '@tsrx/solid/dynamic';` |\n| Vue | `import { Dynamic } from '@tsrx/vue/dynamic';` |\n| Ripple | `import { Dynamic } from 'ripple';` |\n\n```tsx\nimport { Dynamic } from '@tsrx/react/dynamic';\n\ntype Tag = 'section' | 'article';\n\nexport function Panel({ as = 'section', title }: { as?: Tag; title: string }) @{\n <Dynamic is={as} className=\"panel\">\n <h2>{title}</h2>\n </Dynamic>\n}\n```\n\nThe `is` prop can be a string tag name or a component value:\n\n```tsx\nconst Body = expanded ? ExpandedBody : CompactBody;\n\n<Dynamic is={Body} item={item} />\n```\n\nFor React host classes, use `className`. For Preact, Solid, Vue, and Ripple host classes, use `class`.\n\nDo not use removed dynamic tag syntax such as `<@tag />` or `<@Component />`. Use `<Dynamic is={...} />` instead.\n\nSource: website-tsrx/src/pages/features.tsrx#dynamic",
61
70
  },
62
71
  {
63
72
  slug: 'target-integration',
package/src/server.js CHANGED
@@ -305,9 +305,11 @@ function create_tsrx_task_prompt(options) {
305
305
  1. Identify whether the task is about target-neutral TSRX syntax, target runtime behavior, or both.
306
306
  ${project_context_step}
307
307
  3. For syntax uncertainty, use \`list-sections\`, \`get-documentation\`, or read \`tsrx://docs/{slug}.md\`.
308
- 4. Keep core TSRX advice target-neutral: component functions, statement templates, control flow, TSX expression values, lazy destructuring, style identifiers, and submodule declarations.
308
+ 4. Keep core TSRX advice target-neutral: component functions, JSX expression values, JSX statement containers \`@{ ... }\`, JSX text, directive control flow, lazy destructuring, style identifiers, and submodule declarations.
309
309
  5. Use \`tsrx://targets/{target}.md\` as the handoff point for target-specific responsibilities.
310
- 5a. In component template scope, render lists with \`for...of\`; use \`continue\` to skip an item; do not use \`return\` anywhere inside TSRX element or fragment bodies, do not use \`break\` inside \`for...of\` template loops, and do not use regular \`for\`, \`for...in\`, \`while\`, or \`do...while\` loops there.
310
+ 5a. In template output, render lists with \`@for (... of ...)\`; filter the iterable before rendering when items should be skipped, and use \`@empty { ... }\` for the no-items fallback; use \`@if\`, \`@switch\`, and \`@try\` for rendering control flow. Every directive body uses a \`{...}\` template block. When a scope mixes setup with output, setup comes first and the scope ends with exactly one JSX element, JSX fragment, or JSX control-flow expression.
311
+ 5b. \`return\` is JavaScript function control flow, not template output. Use guard returns before a JSX statement container or return value, or render conditionally with \`@if\`. Do not use \`continue\`, \`break\`, or \`return\` inside \`@if\` template branches or \`@for\` template loops. \`@switch\` cases are isolated blocks and do not use \`break\` or \`return\`.
312
+ 5c. If a component body has setup statements followed by bare JSX/template output, use \`@{ ... }\`, not plain \`{ ... }\`. Plain braces are an ordinary JavaScript function body; the missing \`@\` is the common fix.
311
313
  ${file_validation_step}
312
314
  ${compile_step}
313
315
  ${authoring_step}
@@ -600,7 +602,7 @@ export function createTSRXMcpServer(options = {}) {
600
602
  {
601
603
  title: 'Review TSRX Accessibility',
602
604
  description:
603
- 'Reviews TSRX source for common accessibility issues before browser-based Axe validation, including missing button names, unlabeled form controls, and direct quoted text that may not render as accessible text.',
605
+ 'Reviews TSRX source for common accessibility issues before browser-based Axe validation, including missing button names, unlabeled form controls, and visible text accidentally wrapped in quote characters.',
604
606
  inputSchema: {
605
607
  code: z.string(),
606
608
  filename: z.string().optional(),
package/src/target.js CHANGED
@@ -5,7 +5,7 @@ export const TARGET_CANDIDATES = [
5
5
  {
6
6
  target: 'ripple',
7
7
  compilerPackage: '@tsrx/ripple',
8
- signals: ['@tsrx/ripple', 'ripple', '@ripple-ts/vite-plugin', '@ripple-ts/compat-react'],
8
+ signals: ['@tsrx/ripple', 'ripple', '@ripple-ts/vite-plugin'],
9
9
  },
10
10
  {
11
11
  target: 'react',