@hyperfixi/mcp-server 2.0.0 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -5,6 +5,8 @@ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
5
5
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
6
6
  import {
7
7
  CallToolRequestSchema,
8
+ GetPromptRequestSchema,
9
+ ListPromptsRequestSchema,
8
10
  ListResourcesRequestSchema,
9
11
  ListToolsRequestSchema,
10
12
  ReadResourceRequestSchema
@@ -14,7 +16,7 @@ import {
14
16
  var analysisTools = [
15
17
  {
16
18
  name: "analyze_complexity",
17
- description: "Calculate code complexity metrics (cyclomatic, cognitive, Halstead) for hyperscript",
19
+ description: "Calculate numeric complexity scores for hyperscript: cyclomatic, cognitive, and Halstead metrics. Use for code review when deciding if code is too complex.",
18
20
  inputSchema: {
19
21
  type: "object",
20
22
  properties: {
@@ -28,7 +30,7 @@ var analysisTools = [
28
30
  },
29
31
  {
30
32
  name: "analyze_metrics",
31
- description: "Perform comprehensive code analysis including patterns, code smells, and quality metrics",
33
+ description: "Detect code smells and quality issues in hyperscript. Returns patterns, problems (too many commands, missing error handling), and quality scores. More detailed than analyze_complexity.",
32
34
  inputSchema: {
33
35
  type: "object",
34
36
  properties: {
@@ -42,7 +44,7 @@ var analysisTools = [
42
44
  },
43
45
  {
44
46
  name: "explain_code",
45
- description: "Generate natural language explanation of hyperscript code",
47
+ description: 'Generate a plain English explanation of hyperscript code for humans. Use when a user asks "what does this code do?" Supports beginner/intermediate/expert audience levels.',
46
48
  inputSchema: {
47
49
  type: "object",
48
50
  properties: {
@@ -66,7 +68,7 @@ var analysisTools = [
66
68
  },
67
69
  {
68
70
  name: "recognize_intent",
69
- description: "Analyze hyperscript code to understand its purpose and classify common patterns",
71
+ description: "Classify what hyperscript code does: returns intent labels (dom-manipulation, form-handling, data-fetching, animation, etc.) with confidence scores. Use to categorize code purpose, not measure quality.",
70
72
  inputSchema: {
71
73
  type: "object",
72
74
  properties: {
@@ -377,7 +379,7 @@ function simpleIntentRecognition(code) {
377
379
  var patternTools = [
378
380
  {
379
381
  name: "get_examples",
380
- description: "Get hyperscript examples relevant to a task for few-shot learning",
382
+ description: 'Get hyperscript code examples matching a natural language task description (e.g., "toggle a class on click"). Returns working code with quality scores. Best for generating new code by example.',
381
383
  inputSchema: {
382
384
  type: "object",
383
385
  properties: {
@@ -401,7 +403,7 @@ var patternTools = [
401
403
  },
402
404
  {
403
405
  name: "search_patterns",
404
- description: "Search the pattern database for hyperscript examples",
406
+ description: "Search the pattern database by keyword or category filter (class-manipulation, visibility, async, validation, etc.). Unlike get_examples which takes task descriptions, this searches pattern names and content.",
405
407
  inputSchema: {
406
408
  type: "object",
407
409
  properties: {
@@ -424,7 +426,7 @@ var patternTools = [
424
426
  },
425
427
  {
426
428
  name: "translate_hyperscript",
427
- description: "Translate hyperscript between languages",
429
+ description: "Translate hyperscript keywords between any of 24 languages via pattern substitution. Preserves selectors and values. For higher-fidelity translation with grammar transformation, use translate_code instead.",
428
430
  inputSchema: {
429
431
  type: "object",
430
432
  properties: {
@@ -1169,7 +1171,7 @@ function getSemanticAnalyzer() {
1169
1171
  var validationTools = [
1170
1172
  {
1171
1173
  name: "validate_hyperscript",
1172
- description: "Validate hyperscript syntax and return any errors",
1174
+ description: "Check hyperscript code for syntax errors and role warnings. Use as the FIRST validation step before returning code to users. For detailed line/column positions, use get_diagnostics instead.",
1173
1175
  inputSchema: {
1174
1176
  type: "object",
1175
1177
  properties: {
@@ -1188,7 +1190,7 @@ var validationTools = [
1188
1190
  },
1189
1191
  {
1190
1192
  name: "validate_schema",
1191
- description: "Validate command schemas for design issues. Returns structured validation items with machine-readable codes, severity levels (error/warning/note), and suggested fixes. Useful for catching schema issues before they cause runtime problems.",
1193
+ description: "Validate internal command SCHEMA definitions for design issues \u2014 NOT for checking user-written hyperscript. Returns structured items with error codes and severity. Use only when developing or debugging LokaScript command schemas.",
1192
1194
  inputSchema: {
1193
1195
  type: "object",
1194
1196
  properties: {
@@ -1282,7 +1284,7 @@ var validationTools = [
1282
1284
  },
1283
1285
  {
1284
1286
  name: "translate_to_english",
1285
- description: "Translate hyperscript from any supported language to English. Essential for LLMs to understand non-English code.",
1287
+ description: "Normalize non-English hyperscript to English. Returns English code plus explicit bracket syntax. Set getAllLanguages=true to get all 24 translations at once. Use when you receive code in Japanese, Korean, Arabic, etc.",
1286
1288
  inputSchema: {
1287
1289
  type: "object",
1288
1290
  properties: {
@@ -1741,59 +1743,133 @@ var COMMAND_SUGGESTIONS = {
1741
1743
  toggle: {
1742
1744
  command: "toggle",
1743
1745
  example: "toggle .active",
1746
+ explicitSyntax: "[toggle patient:.active destination:#element]",
1744
1747
  description: "Toggle a class, attribute, or visibility"
1745
1748
  },
1746
1749
  add: {
1747
1750
  command: "add",
1748
1751
  example: "add .highlight to me",
1752
+ explicitSyntax: "[add patient:.highlight destination:#element]",
1749
1753
  description: "Add a class, attribute, or style"
1750
1754
  },
1751
1755
  remove: {
1752
1756
  command: "remove",
1753
1757
  example: "remove .error from #form",
1758
+ explicitSyntax: "[remove patient:.error source:#form]",
1754
1759
  description: "Remove a class, attribute, or element"
1755
1760
  },
1756
1761
  show: {
1757
1762
  command: "show",
1758
1763
  example: "show #modal with *opacity",
1764
+ explicitSyntax: "[show patient:#modal style:opacity]",
1759
1765
  description: "Show a hidden element"
1760
1766
  },
1761
- hide: { command: "hide", example: "hide me with *opacity", description: "Hide an element" },
1762
- set: { command: "set", example: "set :count to 0", description: "Set a variable or property" },
1767
+ hide: {
1768
+ command: "hide",
1769
+ example: "hide me with *opacity",
1770
+ explicitSyntax: "[hide patient:me style:opacity]",
1771
+ description: "Hide an element"
1772
+ },
1773
+ set: {
1774
+ command: "set",
1775
+ example: "set :count to 0",
1776
+ explicitSyntax: "[set destination::count patient:0]",
1777
+ description: "Set a variable or property"
1778
+ },
1763
1779
  put: {
1764
1780
  command: "put",
1765
1781
  example: 'put "Hello" into #greeting',
1782
+ explicitSyntax: '[put patient:"Hello" destination:#greeting]',
1766
1783
  description: "Set element content"
1767
1784
  },
1768
- fetch: { command: "fetch", example: "fetch /api as json", description: "Make HTTP request" },
1769
- wait: { command: "wait", example: "wait 500ms", description: "Pause execution" },
1770
- send: { command: "send", example: "send refresh to #list", description: "Dispatch custom event" },
1771
- go: { command: "go", example: "go to /page", description: "Navigate to URL" },
1785
+ fetch: {
1786
+ command: "fetch",
1787
+ example: "fetch /api as json",
1788
+ explicitSyntax: "[fetch source:/api responseType:json]",
1789
+ description: "Make HTTP request"
1790
+ },
1791
+ wait: {
1792
+ command: "wait",
1793
+ example: "wait 500ms",
1794
+ explicitSyntax: "[wait patient:500ms]",
1795
+ description: "Pause execution"
1796
+ },
1797
+ send: {
1798
+ command: "send",
1799
+ example: "send refresh to #list",
1800
+ explicitSyntax: "[send event:refresh destination:#list]",
1801
+ description: "Dispatch custom event"
1802
+ },
1803
+ go: {
1804
+ command: "go",
1805
+ example: "go to /page",
1806
+ explicitSyntax: "[go destination:/page]",
1807
+ description: "Navigate to URL"
1808
+ },
1772
1809
  increment: {
1773
1810
  command: "increment",
1774
1811
  example: "increment :count",
1812
+ explicitSyntax: "[increment patient::count]",
1775
1813
  description: "Add 1 to a number"
1776
1814
  },
1777
1815
  decrement: {
1778
1816
  command: "decrement",
1779
1817
  example: "decrement :count",
1818
+ explicitSyntax: "[decrement patient::count]",
1780
1819
  description: "Subtract 1 from a number"
1781
1820
  },
1782
- focus: { command: "focus", example: "focus #input", description: "Focus an element" },
1821
+ focus: {
1822
+ command: "focus",
1823
+ example: "focus #input",
1824
+ explicitSyntax: "[focus patient:#input]",
1825
+ description: "Focus an element"
1826
+ },
1783
1827
  call: {
1784
1828
  command: "call",
1785
1829
  example: "call myFunction()",
1830
+ explicitSyntax: "[call patient:myFunction()]",
1786
1831
  description: "Call a JavaScript function"
1787
1832
  },
1788
- log: { command: "log", example: "log me", description: "Log to console" },
1789
- beep: { command: "beep", example: "beep me", description: "Debug output with type info" },
1790
- break: { command: "break", example: "break", description: "Exit current loop" },
1791
- copy: { command: "copy", example: "copy #input.value", description: "Copy to clipboard" },
1792
- exit: { command: "exit", example: "exit", description: "Exit event handler" },
1793
- pick: { command: "pick", example: "pick .items", description: "Pick random item" },
1833
+ log: {
1834
+ command: "log",
1835
+ example: "log me",
1836
+ explicitSyntax: "[log patient:me]",
1837
+ description: "Log to console"
1838
+ },
1839
+ beep: {
1840
+ command: "beep",
1841
+ example: "beep me",
1842
+ explicitSyntax: "[beep patient:me]",
1843
+ description: "Debug output with type info"
1844
+ },
1845
+ break: {
1846
+ command: "break",
1847
+ example: "break",
1848
+ explicitSyntax: "[break]",
1849
+ description: "Exit current loop"
1850
+ },
1851
+ copy: {
1852
+ command: "copy",
1853
+ example: "copy #input.value",
1854
+ explicitSyntax: "[copy patient:#input]",
1855
+ description: "Copy to clipboard"
1856
+ },
1857
+ exit: {
1858
+ command: "exit",
1859
+ example: "exit",
1860
+ explicitSyntax: "[exit]",
1861
+ description: "Exit event handler"
1862
+ },
1863
+ pick: {
1864
+ command: "pick",
1865
+ example: "pick .items",
1866
+ explicitSyntax: "[pick source:.items]",
1867
+ description: "Pick random item"
1868
+ },
1794
1869
  render: {
1795
1870
  command: "render",
1796
1871
  example: 'render "<p>${name}</p>" with {name: "World"}',
1872
+ explicitSyntax: '[render patient:"<p>${name}</p>"]',
1797
1873
  description: "Render a template"
1798
1874
  }
1799
1875
  };
@@ -1833,6 +1909,7 @@ function translateSuggestion(suggestion, language) {
1833
1909
  return {
1834
1910
  command: trans.primary,
1835
1911
  example: suggestion.example.replace(suggestion.command, trans.primary),
1912
+ explicitSyntax: suggestion.explicitSyntax,
1836
1913
  description: suggestion.description,
1837
1914
  englishCommand: suggestion.command
1838
1915
  };
@@ -2586,18 +2663,37 @@ var astToolkit = null;
2586
2663
  try {
2587
2664
  astToolkit = await import("@hyperfixi/core/ast-utils");
2588
2665
  } catch {
2666
+ console.error(
2667
+ "[mcp:lsp-bridge] @hyperfixi/core/ast-utils not available \u2014 AST-based diagnostics disabled"
2668
+ );
2589
2669
  }
2590
2670
  var parseFunction = null;
2591
2671
  try {
2592
2672
  const core = await import("@hyperfixi/core");
2593
2673
  parseFunction = core.parse;
2594
2674
  } catch {
2675
+ console.error("[mcp:lsp-bridge] @hyperfixi/core not available \u2014 parse-based analysis disabled");
2595
2676
  }
2596
2677
  var semanticPackage2 = null;
2597
2678
  try {
2598
2679
  semanticPackage2 = await import("@lokascript/semantic");
2599
2680
  } catch {
2681
+ console.error(
2682
+ "[mcp:lsp-bridge] @lokascript/semantic not available \u2014 multilingual support disabled"
2683
+ );
2684
+ }
2685
+ var frameworkIR = null;
2686
+ try {
2687
+ const fw = await import("@lokascript/framework");
2688
+ frameworkIR = { fromInterchangeNode: fw.fromInterchangeNode, renderExplicit: fw.renderExplicit };
2689
+ } catch {
2690
+ console.error(
2691
+ "[mcp:lsp-bridge] @lokascript/framework not available \u2014 LSE bracket notation in hover disabled"
2692
+ );
2600
2693
  }
2694
+ console.error(
2695
+ `[mcp:lsp-bridge] capabilities: ast=${!!astToolkit}, parse=${!!parseFunction}, semantic=${!!semanticPackage2}, framework=${!!frameworkIR}`
2696
+ );
2601
2697
  var cachedAnalyzer2 = null;
2602
2698
  function getSemanticAnalyzer2() {
2603
2699
  if (!semanticPackage2) return null;
@@ -3214,11 +3310,17 @@ function inferContext(beforeCursor) {
3214
3310
  return "default";
3215
3311
  }
3216
3312
  async function getHoverInfo(code, line, character, language) {
3217
- if (astToolkit && parseFunction) {
3313
+ if (astToolkit && parseFunction && astToolkit.fromCoreAST && astToolkit.interchangeToLSPHover) {
3218
3314
  try {
3219
3315
  const ast = parseFunction(code);
3220
- if (ast && astToolkit.astToLSPHover) {
3221
- const hover2 = astToolkit.astToLSPHover(ast, { line, character });
3316
+ if (ast) {
3317
+ const interchange = astToolkit.fromCoreAST(ast);
3318
+ const nodes = Array.isArray(interchange) ? interchange : [interchange];
3319
+ const hoverOptions = {};
3320
+ if (frameworkIR) {
3321
+ hoverOptions.renderLSE = (node) => frameworkIR.renderExplicit(frameworkIR.fromInterchangeNode(node));
3322
+ }
3323
+ const hover2 = astToolkit.interchangeToLSPHover(nodes, { line, character }, hoverOptions);
3222
3324
  if (hover2) {
3223
3325
  return {
3224
3326
  content: [{ type: "text", text: JSON.stringify({ ...hover2, language }, null, 2) }]
@@ -3507,7 +3609,7 @@ var languageDocsTools = [
3507
3609
  },
3508
3610
  {
3509
3611
  name: "search_language_elements",
3510
- description: "Search across all hyperscript language elements (commands, expressions, keywords, features, special symbols). Useful for discovering available features.",
3612
+ description: 'Search across all hyperscript language elements (commands, expressions, keywords, features, special symbols, semantic roles). Useful for discovering available features. Search for role names like "patient", "destination", "source" to understand semantic roles used in explicit syntax.',
3511
3613
  inputSchema: {
3512
3614
  type: "object",
3513
3615
  properties: {
@@ -3519,7 +3621,7 @@ var languageDocsTools = [
3519
3621
  type: "array",
3520
3622
  items: {
3521
3623
  type: "string",
3522
- enum: ["command", "expression", "keyword", "feature", "special_symbol"]
3624
+ enum: ["command", "expression", "keyword", "feature", "special_symbol", "role"]
3523
3625
  },
3524
3626
  description: "Filter by element types. If not specified, searches all types."
3525
3627
  },
@@ -3575,6 +3677,49 @@ function generateSyntaxString(schema) {
3575
3677
  }
3576
3678
  return parts.join(" ");
3577
3679
  }
3680
+ function generateExplicitExample(schema) {
3681
+ const action = schema.action;
3682
+ if (!schema.roles || schema.roles.length === 0) {
3683
+ return `[${action}]`;
3684
+ }
3685
+ const parts = [action];
3686
+ for (const role of schema.roles) {
3687
+ const exampleValue = getExampleValue(role);
3688
+ parts.push(`${role.role}:${exampleValue}`);
3689
+ }
3690
+ return `[${parts.join(" ")}]`;
3691
+ }
3692
+ function generateJsonExample(schema) {
3693
+ const roles = {};
3694
+ for (const role of schema.roles || []) {
3695
+ const primaryType = role.expectedTypes?.[0] ?? "literal";
3696
+ roles[role.role] = {
3697
+ type: primaryType === "expression" ? "literal" : primaryType,
3698
+ value: getExampleValue(role)
3699
+ };
3700
+ }
3701
+ return { action: schema.action, roles };
3702
+ }
3703
+ function getExampleValue(role) {
3704
+ if (role.default) {
3705
+ return role.default.value ?? "me";
3706
+ }
3707
+ const primaryType = role.expectedTypes?.[0];
3708
+ switch (primaryType) {
3709
+ case "selector":
3710
+ return role.description?.toLowerCase().includes("class") ? ".active" : "#element";
3711
+ case "reference":
3712
+ return "me";
3713
+ case "number":
3714
+ return 5;
3715
+ case "boolean":
3716
+ return true;
3717
+ case "duration":
3718
+ return "500ms";
3719
+ default:
3720
+ return '"value"';
3721
+ }
3722
+ }
3578
3723
  var EXPRESSION_CATALOG = {
3579
3724
  // === CORE: references ===
3580
3725
  me: {
@@ -4025,6 +4170,165 @@ var SPECIAL_SYMBOLS_CATALOG = [
4025
4170
  description: "Block terminator for if/repeat/for/while blocks"
4026
4171
  }
4027
4172
  ];
4173
+ var ROLE_CATALOG = {
4174
+ patient: {
4175
+ name: "patient",
4176
+ description: "The entity undergoing the action \u2014 what is being acted upon. The most common role, used by 25+ commands.",
4177
+ origin: "Linguistic thematic role: the entity that undergoes a change of state.",
4178
+ usage: "The class being toggled, the content being put, the element being shown, the variable being incremented.",
4179
+ commands: [
4180
+ "toggle",
4181
+ "add",
4182
+ "remove",
4183
+ "put",
4184
+ "set",
4185
+ "show",
4186
+ "hide",
4187
+ "wait",
4188
+ "log",
4189
+ "increment",
4190
+ "decrement",
4191
+ "append",
4192
+ "prepend",
4193
+ "copy",
4194
+ "throw",
4195
+ "call",
4196
+ "return",
4197
+ "focus",
4198
+ "blur",
4199
+ "beep",
4200
+ "pick",
4201
+ "render",
4202
+ "morph",
4203
+ "swap"
4204
+ ],
4205
+ explicitExample: "[toggle patient:.active]"
4206
+ },
4207
+ destination: {
4208
+ name: "destination",
4209
+ description: "Where the action targets or where something ends up. Intentionally broad: DOM element, URL, or variable. Disambiguated by value type per command.",
4210
+ origin: "Linguistic thematic role: the endpoint or goal location of a motion/transfer.",
4211
+ usage: "The element receiving a class (toggle/add), the container for content (put), the URL to navigate to (go), the variable to store into (set).",
4212
+ commands: [
4213
+ "toggle",
4214
+ "add",
4215
+ "put",
4216
+ "remove",
4217
+ "append",
4218
+ "prepend",
4219
+ "trigger",
4220
+ "clone",
4221
+ "show",
4222
+ "hide",
4223
+ "transition",
4224
+ "go",
4225
+ "tell",
4226
+ "set",
4227
+ "get",
4228
+ "fetch",
4229
+ "send",
4230
+ "morph",
4231
+ "swap"
4232
+ ],
4233
+ explicitExample: '[put patient:"hello" destination:#output]'
4234
+ },
4235
+ source: {
4236
+ name: "source",
4237
+ description: "Where something originates from \u2014 the origin of data or the element to act on.",
4238
+ origin: "Linguistic thematic role: the starting point of a motion/transfer.",
4239
+ usage: "The element to remove from, the URL to fetch from, the event source element.",
4240
+ commands: ["remove", "on", "get", "take", "pick", "repeat", "for", "measure", "fetch"],
4241
+ explicitExample: "[fetch source:/api/data responseType:json]"
4242
+ },
4243
+ event: {
4244
+ name: "event",
4245
+ description: "The trigger event for an event handler.",
4246
+ origin: "DOM event model.",
4247
+ usage: "The event name that starts execution: click, input, keydown, mouseover, custom events.",
4248
+ commands: ["on", "trigger", "send", "repeat"],
4249
+ explicitExample: "[on event:click body:[toggle patient:.active]]"
4250
+ },
4251
+ condition: {
4252
+ name: "condition",
4253
+ description: "A boolean expression that controls whether something executes.",
4254
+ origin: "Control flow semantics.",
4255
+ usage: "The test in an if/unless/while block.",
4256
+ commands: ["if", "unless", "while"],
4257
+ explicitExample: '[if condition:"x > 5"]'
4258
+ },
4259
+ quantity: {
4260
+ name: "quantity",
4261
+ description: "A numeric amount for the action.",
4262
+ origin: "Linguistic quantifier role.",
4263
+ usage: "How much to increment/decrement by, how many times to repeat.",
4264
+ commands: ["increment", "decrement", "repeat", "for"],
4265
+ explicitExample: "[increment patient:#count quantity:5]"
4266
+ },
4267
+ duration: {
4268
+ name: "duration",
4269
+ description: "A time span for the action.",
4270
+ origin: "Temporal adverbial role.",
4271
+ usage: "How long a transition takes.",
4272
+ commands: ["transition"],
4273
+ explicitExample: "[transition patient:opacity duration:500ms]"
4274
+ },
4275
+ goal: {
4276
+ name: "goal",
4277
+ description: "The target value or state to reach.",
4278
+ origin: "Linguistic thematic role: the desired end-state.",
4279
+ usage: "The target value in a transition.",
4280
+ commands: ["transition"],
4281
+ explicitExample: "[transition patient:opacity goal:0]"
4282
+ },
4283
+ style: {
4284
+ name: "style",
4285
+ description: "The visual or behavioral manner of the action.",
4286
+ origin: "Adverbial modifier for manner/instrument.",
4287
+ usage: "Animation style for show/hide (fade, slide, opacity).",
4288
+ commands: ["show", "hide", "render", "transition"],
4289
+ explicitExample: "[show patient:#modal style:opacity]"
4290
+ },
4291
+ method: {
4292
+ name: "method",
4293
+ description: "The technique or HTTP method used.",
4294
+ origin: "Instrument/manner role, specialized for web.",
4295
+ usage: "HTTP method for fetch (GET, POST), swap strategy (innerHTML, outerHTML).",
4296
+ commands: ["swap", "fetch"],
4297
+ explicitExample: "[fetch source:/api method:POST responseType:json]"
4298
+ },
4299
+ responseType: {
4300
+ name: "responseType",
4301
+ description: "The expected response format.",
4302
+ origin: "HTTP content negotiation.",
4303
+ usage: "How to parse a fetch response: json, text, html, blob.",
4304
+ commands: ["fetch"],
4305
+ explicitExample: "[fetch source:/api responseType:json]"
4306
+ },
4307
+ manner: {
4308
+ name: "manner",
4309
+ description: 'Reserved role for insertion position. Not currently used in command schemas \u2014 swap uses "method", put handles position via destination markers.',
4310
+ origin: "Linguistic adverbial role for how an action is performed.",
4311
+ usage: "Reserved for future use.",
4312
+ commands: [],
4313
+ explicitExample: "(not currently used)"
4314
+ },
4315
+ agent: {
4316
+ name: "agent",
4317
+ description: "Who or what performs the action. Reserved for future use (AI agents, server-side execution).",
4318
+ origin: "Linguistic thematic role: the entity that initiates the action.",
4319
+ usage: "Reserved for future use.",
4320
+ commands: [],
4321
+ explicitExample: "(not currently used)"
4322
+ },
4323
+ loopType: {
4324
+ name: "loopType",
4325
+ description: "The loop variant for repeat commands.",
4326
+ origin: "Control flow semantics.",
4327
+ usage: "Loop style: forever, times, while, until, until-event.",
4328
+ commands: ["repeat"],
4329
+ explicitExample: "[repeat loopType:forever]"
4330
+ }
4331
+ };
4028
4332
  var BEST_PRACTICES = [
4029
4333
  {
4030
4334
  id: "prefer-toggle",
@@ -4274,6 +4578,8 @@ function handleGetCommandDocs(args) {
4274
4578
  command: schema.action,
4275
4579
  description: schema.description,
4276
4580
  syntax: generateSyntaxString(schema),
4581
+ explicitSyntax: generateExplicitExample(schema),
4582
+ jsonSyntax: generateJsonExample(schema),
4277
4583
  category: schema.category,
4278
4584
  primaryRole: schema.primaryRole,
4279
4585
  hasBody: schema.hasBody || false,
@@ -4413,7 +4719,14 @@ function handleSearchLanguageElements(args) {
4413
4719
  const query = args.query.toLowerCase();
4414
4720
  const types = args.types;
4415
4721
  const limit = args.limit || 10;
4416
- const searchTypes = types || ["command", "expression", "keyword", "feature", "special_symbol"];
4722
+ const searchTypes = types || [
4723
+ "command",
4724
+ "expression",
4725
+ "keyword",
4726
+ "feature",
4727
+ "special_symbol",
4728
+ "role"
4729
+ ];
4417
4730
  const results = [];
4418
4731
  if (searchTypes.includes("command")) {
4419
4732
  if (semanticPackage3?.commandSchemas) {
@@ -4499,6 +4812,21 @@ function handleSearchLanguageElements(args) {
4499
4812
  }
4500
4813
  }
4501
4814
  }
4815
+ if (searchTypes.includes("role")) {
4816
+ for (const [name, role] of Object.entries(ROLE_CATALOG)) {
4817
+ if (name.includes(query) || role.description.toLowerCase().includes(query) || role.origin.toLowerCase().includes(query) || role.usage.toLowerCase().includes(query) || role.commands.some((c) => c.includes(query))) {
4818
+ results.push({
4819
+ type: "role",
4820
+ name: role.name,
4821
+ description: role.description,
4822
+ origin: role.origin,
4823
+ usage: role.usage,
4824
+ commands: role.commands,
4825
+ explicitExample: role.explicitExample
4826
+ });
4827
+ }
4828
+ }
4829
+ }
4502
4830
  const limited = results.slice(0, limit);
4503
4831
  return {
4504
4832
  content: [
@@ -4972,7 +5300,7 @@ async function getService() {
4972
5300
  var compilationTools = [
4973
5301
  {
4974
5302
  name: "compile_hyperscript",
4975
- description: "Compile hyperscript to optimized JavaScript. Accepts natural language (24 languages), explicit syntax ([command role:value]), or LLM JSON ({ action, roles, trigger }).",
5303
+ description: 'Compile hyperscript to optimized JavaScript. Accepts natural language (code + language), explicit syntax (explicit), or LLM JSON (semantic). Common roles: patient (what to act on), destination (where to), source (where from). Use get_command_docs for per-command roles. Examples: explicit="[toggle patient:.active destination:#btn]", semantic={ action: "toggle", roles: { patient: { type: "selector", value: ".active" } } }',
4976
5304
  inputSchema: {
4977
5305
  type: "object",
4978
5306
  properties: {
@@ -5006,7 +5334,7 @@ var compilationTools = [
5006
5334
  },
5007
5335
  {
5008
5336
  name: "validate_and_compile",
5009
- description: "Validate hyperscript without compiling. Returns semantic representation and diagnostics. Same input format as compile_hyperscript.",
5337
+ description: "Parse hyperscript into semantic IR with diagnostics, without generating JavaScript. Returns action, roles, and trigger structure. Accepts natural language, explicit bracket syntax, or LLM JSON \u2014 same input formats as compile_hyperscript.",
5010
5338
  inputSchema: {
5011
5339
  type: "object",
5012
5340
  properties: {
@@ -5020,7 +5348,7 @@ var compilationTools = [
5020
5348
  },
5021
5349
  {
5022
5350
  name: "translate_code",
5023
- description: "Translate hyperscript between any of 24 supported languages.",
5351
+ description: "Translate hyperscript between any of 24 languages via full semantic parsing. Higher fidelity than translate_hyperscript \u2014 handles SVO/SOV/VSO grammar transformation. Preferred for production translations.",
5024
5352
  inputSchema: {
5025
5353
  type: "object",
5026
5354
  properties: {
@@ -5046,13 +5374,17 @@ var compilationTools = [
5046
5374
  type: "string",
5047
5375
  enum: ["runtime", "compiled"],
5048
5376
  description: "How to load hyperscript in test (default runtime)"
5377
+ },
5378
+ framework: {
5379
+ type: "string",
5380
+ description: 'Test framework to target (default "playwright")'
5049
5381
  }
5050
5382
  }
5051
5383
  }
5052
5384
  },
5053
5385
  {
5054
5386
  name: "generate_component",
5055
- description: "Generate a React component from hyperscript. Maps semantic operations to React hooks and JSX.",
5387
+ description: "Generate a React component from hyperscript. Maps semantic operations to React hooks and JSX, Vue 3 Composition API and templates, or Svelte 5 runes. Supports react (default), vue, and svelte frameworks.",
5056
5388
  inputSchema: {
5057
5389
  type: "object",
5058
5390
  properties: {
@@ -5064,6 +5396,10 @@ var compilationTools = [
5064
5396
  typescript: {
5065
5397
  type: "boolean",
5066
5398
  description: "TypeScript output (default true)"
5399
+ },
5400
+ framework: {
5401
+ type: "string",
5402
+ description: 'Component framework to target: "react" (default), "vue", or "svelte"'
5067
5403
  }
5068
5404
  }
5069
5405
  }
@@ -5148,7 +5484,8 @@ async function handleCompilationTool(name, args) {
5148
5484
  semantic: args.semantic,
5149
5485
  language: args.language,
5150
5486
  testName: args.testName,
5151
- executionMode: args.executionMode
5487
+ executionMode: args.executionMode,
5488
+ framework: args.framework
5152
5489
  });
5153
5490
  return {
5154
5491
  content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
@@ -5162,7 +5499,8 @@ async function handleCompilationTool(name, args) {
5162
5499
  semantic: args.semantic,
5163
5500
  language: args.language,
5164
5501
  componentName: args.componentName,
5165
- typescript: args.typescript
5502
+ typescript: args.typescript,
5503
+ framework: args.framework
5166
5504
  });
5167
5505
  return {
5168
5506
  content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
@@ -5199,36 +5537,3402 @@ async function handleCompilationTool(name, args) {
5199
5537
  }
5200
5538
  }
5201
5539
 
5202
- // src/resources/content.ts
5203
- function getCommandsReference() {
5204
- return `# Hyperscript Commands Reference
5205
-
5206
- ## DOM Manipulation
5540
+ // src/tools/utils.ts
5541
+ function validateRequired(args, required) {
5542
+ for (const param of required) {
5543
+ if (args[param] === void 0 || args[param] === null) {
5544
+ return {
5545
+ isError: true,
5546
+ content: [
5547
+ {
5548
+ type: "text",
5549
+ text: JSON.stringify(
5550
+ {
5551
+ error: `Missing required parameter: ${param}`,
5552
+ required,
5553
+ received: Object.keys(args)
5554
+ },
5555
+ null,
5556
+ 2
5557
+ )
5558
+ }
5559
+ ]
5560
+ };
5561
+ }
5562
+ }
5563
+ return null;
5564
+ }
5565
+ function getString(args, name, defaultValue = "") {
5566
+ const value = args[name];
5567
+ return typeof value === "string" ? value : defaultValue;
5568
+ }
5569
+ function getBoolean(args, name, defaultValue = false) {
5570
+ const value = args[name];
5571
+ return typeof value === "boolean" ? value : defaultValue;
5572
+ }
5573
+ function getNumber(args, name, defaultValue = 0) {
5574
+ const value = args[name];
5575
+ return typeof value === "number" ? value : defaultValue;
5576
+ }
5577
+ function jsonResponse(data) {
5578
+ return {
5579
+ content: [
5580
+ {
5581
+ type: "text",
5582
+ text: JSON.stringify(data, null, 2)
5583
+ }
5584
+ ]
5585
+ };
5586
+ }
5587
+ function errorResponse(error, details) {
5588
+ return {
5589
+ isError: true,
5590
+ content: [
5591
+ {
5592
+ type: "text",
5593
+ text: JSON.stringify(
5594
+ {
5595
+ error,
5596
+ ...details
5597
+ },
5598
+ null,
5599
+ 2
5600
+ )
5601
+ }
5602
+ ]
5603
+ };
5604
+ }
5207
5605
 
5208
- | Command | Usage | Example |
5209
- |---------|-------|---------|
5210
- | \`toggle\` | Toggle class/attribute | \`toggle .active on #menu\` |
5211
- | \`add\` | Add class/attribute/style | \`add .highlight to me\` |
5212
- | \`remove\` | Remove class/attribute/element | \`remove .error from #form\` |
5213
- | \`show\` | Show element | \`show #modal with *opacity\` |
5214
- | \`hide\` | Hide element | \`hide me with *opacity\` |
5215
- | \`put\` | Set element content | \`put "Hello" into #greeting\` |
5216
- | \`append\` | Add to end | \`append "<li/>" to #list\` |
5217
- | \`swap\` | Replace content | \`swap #target innerHTML\` |
5606
+ // src/tools/routes.ts
5607
+ var scanRoutesImpl = null;
5608
+ var generators = {};
5609
+ async function getScanRoutes() {
5610
+ if (scanRoutesImpl) return scanRoutesImpl;
5611
+ try {
5612
+ const mod = await import("@hyperfixi/server-bridge");
5613
+ scanRoutesImpl = mod.scanRoutes;
5614
+ generators = {
5615
+ express: mod.ExpressGenerator,
5616
+ hono: mod.HonoGenerator,
5617
+ openapi: mod.OpenAPIGenerator,
5618
+ django: mod.DjangoGenerator,
5619
+ fastapi: mod.FastAPIGenerator
5620
+ };
5621
+ return scanRoutesImpl;
5622
+ } catch {
5623
+ throw new Error(
5624
+ "@hyperfixi/server-bridge not available. Install it to use route extraction tools."
5625
+ );
5626
+ }
5627
+ }
5628
+ var routeTools = [
5629
+ {
5630
+ name: "extract_routes",
5631
+ description: "Extract server route contracts from HTML content containing hyperscript fetch commands, htmx attributes (hx-get, hx-post, etc.), and fixi attributes (fx-action). Returns RouteDescriptor[] with path, method, responseFormat, pathParams, and requestBody.",
5632
+ inputSchema: {
5633
+ type: "object",
5634
+ properties: {
5635
+ html: {
5636
+ type: "string",
5637
+ description: "HTML content to scan for route declarations"
5638
+ },
5639
+ filename: {
5640
+ type: "string",
5641
+ description: 'Optional filename for source attribution (default: "input.html")'
5642
+ }
5643
+ },
5644
+ required: ["html"]
5645
+ }
5646
+ },
5647
+ {
5648
+ name: "generate_server_routes",
5649
+ description: "Generate server-side route scaffolding from HTML content. Scans for routes and produces framework-specific code. Supports Express, Hono, OpenAPI, Django, and FastAPI output.",
5650
+ inputSchema: {
5651
+ type: "object",
5652
+ properties: {
5653
+ html: {
5654
+ type: "string",
5655
+ description: "HTML content to scan for route declarations"
5656
+ },
5657
+ framework: {
5658
+ type: "string",
5659
+ description: 'Target framework: "express", "hono", "openapi", "django", or "fastapi" (default: "express")',
5660
+ enum: ["express", "hono", "openapi", "django", "fastapi"]
5661
+ },
5662
+ typescript: {
5663
+ type: "boolean",
5664
+ description: "Generate TypeScript output (default: true)"
5665
+ },
5666
+ filename: {
5667
+ type: "string",
5668
+ description: 'Optional filename for source attribution (default: "input.html")'
5669
+ }
5670
+ },
5671
+ required: ["html"]
5672
+ }
5673
+ }
5674
+ ];
5675
+ async function handleRouteTool(name, args) {
5676
+ try {
5677
+ switch (name) {
5678
+ case "extract_routes":
5679
+ return await extractRoutes(args);
5680
+ case "generate_server_routes":
5681
+ return await generateServerRoutes(args);
5682
+ default:
5683
+ return errorResponse(`Unknown route tool: ${name}`);
5684
+ }
5685
+ } catch (error) {
5686
+ const message = error instanceof Error ? error.message : String(error);
5687
+ return errorResponse(`Route tool error: ${message}`);
5688
+ }
5689
+ }
5690
+ async function extractRoutes(args) {
5691
+ const error = validateRequired(args, ["html"]);
5692
+ if (error) return error;
5693
+ const html = getString(args, "html");
5694
+ const filename = getString(args, "filename", "input.html");
5695
+ const scanRoutes = await getScanRoutes();
5696
+ const result = scanRoutes(html, filename);
5697
+ return jsonResponse({
5698
+ routes: result.routes,
5699
+ count: result.routes.length,
5700
+ errors: result.errors
5701
+ });
5702
+ }
5703
+ async function generateServerRoutes(args) {
5704
+ const error = validateRequired(args, ["html"]);
5705
+ if (error) return error;
5706
+ const html = getString(args, "html");
5707
+ const framework = getString(args, "framework", "express");
5708
+ const typescript = getBoolean(args, "typescript", true);
5709
+ const filename = getString(args, "filename", "input.html");
5710
+ const scanRoutes = await getScanRoutes();
5711
+ const result = scanRoutes(html, filename);
5712
+ if (result.routes.length === 0) {
5713
+ return jsonResponse({
5714
+ files: [],
5715
+ routes: 0,
5716
+ message: "No routes found in the provided HTML.",
5717
+ errors: result.errors
5718
+ });
5719
+ }
5720
+ const GeneratorClass = generators[framework];
5721
+ if (!GeneratorClass) {
5722
+ return errorResponse(
5723
+ `Unknown framework: ${framework}. Supported: express, hono, openapi, django, fastapi`
5724
+ );
5725
+ }
5726
+ const generator = new GeneratorClass();
5727
+ const generated = generator.generate(result.routes, {
5728
+ outputDir: "./server/routes",
5729
+ typescript
5730
+ });
5731
+ return jsonResponse({
5732
+ framework: generator.framework,
5733
+ routes: result.routes.length,
5734
+ files: generated.files.map((f) => ({
5735
+ path: f.path,
5736
+ content: f.content
5737
+ })),
5738
+ warnings: generated.warnings,
5739
+ scanErrors: result.errors
5740
+ });
5741
+ }
5218
5742
 
5219
- ## Data Commands
5743
+ // src/tools/domain-registry-setup.ts
5744
+ import { DomainRegistry } from "@lokascript/framework";
5745
+ function createDomainRegistry() {
5746
+ const registry2 = new DomainRegistry();
5747
+ registry2.register({
5748
+ name: "sql",
5749
+ description: "natural language SQL",
5750
+ languages: ["en", "es", "ja", "ar", "ko", "zh", "tr", "fr"],
5751
+ inputLabel: "query",
5752
+ inputDescription: 'SQL query in natural language (e.g., "select name from users")',
5753
+ outputDescription: "a SQL query string",
5754
+ getDSL: () => import("@lokascript/domain-sql").then((m) => m.createSQLDSL()),
5755
+ getRenderer: () => import("@lokascript/domain-sql").then((m) => m.renderSQL),
5756
+ scanConfig: {
5757
+ attributes: ["data-sql", "_sql"],
5758
+ scriptTypes: ["text/sql-dsl"],
5759
+ defaultLanguage: "en"
5760
+ }
5761
+ });
5762
+ registry2.register({
5763
+ name: "bdd",
5764
+ description: "BDD scenario",
5765
+ languages: ["en", "es", "ja", "ar"],
5766
+ inputLabel: "scenario",
5767
+ inputDescription: 'BDD scenario text (e.g., "given #button is exists")',
5768
+ outputDescription: "Playwright test code with Given/When/Then assertions",
5769
+ getDSL: () => import("@lokascript/domain-bdd").then((m) => m.createBDDDSL()),
5770
+ getRenderer: () => import("@lokascript/domain-bdd").then((m) => m.renderBDD),
5771
+ scanConfig: {
5772
+ attributes: ["data-bdd", "_bdd"],
5773
+ scriptTypes: ["text/bdd"],
5774
+ defaultLanguage: "en"
5775
+ }
5776
+ });
5777
+ registry2.register({
5778
+ name: "jsx",
5779
+ description: "natural language JSX/React",
5780
+ languages: ["en", "es", "ja", "ar", "ko", "zh", "tr", "fr"],
5781
+ inputLabel: "code",
5782
+ inputDescription: 'JSX description in natural language (e.g., "element div with className app")',
5783
+ outputDescription: "JSX/React component markup",
5784
+ getDSL: () => import("@lokascript/domain-jsx").then((m) => m.createJSXDSL()),
5785
+ getRenderer: () => import("@lokascript/domain-jsx").then((m) => m.renderJSX),
5786
+ scanConfig: {
5787
+ attributes: ["data-jsx", "_jsx"],
5788
+ scriptTypes: ["text/jsx-dsl"],
5789
+ defaultLanguage: "en"
5790
+ }
5791
+ });
5792
+ registry2.register({
5793
+ name: "todo",
5794
+ description: "natural language todo management",
5795
+ languages: ["en", "es", "ja", "ar", "ko", "zh", "tr", "fr"],
5796
+ inputLabel: "command",
5797
+ inputDescription: 'Todo command in natural language (e.g., "add milk to groceries")',
5798
+ outputDescription: "a structured todo operation object",
5799
+ getDSL: () => import("@lokascript/domain-todo").then((m) => m.createTodoDSL()),
5800
+ getRenderer: () => import("@lokascript/domain-todo").then((m) => m.renderTodo),
5801
+ scanConfig: {
5802
+ attributes: ["data-todo", "_todo"],
5803
+ scriptTypes: ["text/todo"],
5804
+ defaultLanguage: "en"
5805
+ }
5806
+ });
5807
+ registry2.register({
5808
+ name: "behaviorspec",
5809
+ description: "interaction testing specification",
5810
+ languages: ["en", "es", "ja", "ar", "ko", "zh", "tr", "fr"],
5811
+ inputLabel: "scenario",
5812
+ inputDescription: 'BehaviorSpec scenario text (e.g., "given page /home")',
5813
+ outputDescription: "a Playwright interaction test with page assertions",
5814
+ getDSL: () => import("@lokascript/domain-behaviorspec").then((m) => m.createBehaviorSpecDSL()),
5815
+ getRenderer: () => import("@lokascript/domain-behaviorspec").then((m) => m.renderBehaviorSpec),
5816
+ scanConfig: {
5817
+ attributes: ["data-spec", "_spec"],
5818
+ scriptTypes: ["text/behaviorspec"],
5819
+ defaultLanguage: "en"
5820
+ }
5821
+ });
5822
+ registry2.register({
5823
+ name: "llm",
5824
+ description: "natural language LLM prompts (ask, summarize, analyze, translate)",
5825
+ languages: ["en", "es", "ja", "ar", "ko", "zh", "tr", "fr"],
5826
+ inputLabel: "command",
5827
+ inputDescription: 'LLM command in natural language (e.g., "ask claude to summarize #article as bullets")',
5828
+ outputDescription: "an LLMPromptSpec JSON (action, messages, model preferences)",
5829
+ getDSL: () => import("@lokascript/domain-llm").then((m) => m.createLLMDSL()),
5830
+ getRenderer: () => import("@lokascript/domain-llm").then((m) => m.renderLLM),
5831
+ scanConfig: {
5832
+ attributes: ["data-llm", "_llm"],
5833
+ scriptTypes: ["text/llm"],
5834
+ defaultLanguage: "en"
5835
+ }
5836
+ });
5837
+ registry2.register({
5838
+ name: "flow",
5839
+ description: "declarative reactive data flow pipelines (fetch, poll, stream, submit)",
5840
+ languages: ["en", "es", "ja", "ar", "ko", "zh", "tr", "fr"],
5841
+ inputLabel: "pipeline",
5842
+ inputDescription: 'FlowScript pipeline (e.g., "fetch /api/users as json into #list")',
5843
+ outputDescription: "reactive data flow JavaScript (fetch/poll/stream handlers)",
5844
+ getDSL: () => import("@lokascript/domain-flow").then((m) => m.createFlowDSL()),
5845
+ getRenderer: () => import("@lokascript/domain-flow").then((m) => m.renderFlow),
5846
+ scanConfig: {
5847
+ attributes: ["data-flow", "_flow"],
5848
+ scriptTypes: ["text/flowscript"],
5849
+ defaultLanguage: "en"
5850
+ }
5851
+ });
5852
+ registry2.register({
5853
+ name: "voice",
5854
+ description: "voice/accessibility commands for speech-controlled web interaction",
5855
+ languages: ["en", "es", "ja", "ar", "ko", "zh", "tr", "fr"],
5856
+ inputLabel: "command",
5857
+ inputDescription: 'Voice command in natural language (e.g., "click the submit button")',
5858
+ outputDescription: "a DOM interaction command (click, scroll, type, navigate)",
5859
+ getDSL: () => import("@lokascript/domain-voice").then((m) => m.createVoiceDSL()),
5860
+ getRenderer: () => import("@lokascript/domain-voice").then((m) => m.renderVoice),
5861
+ scanConfig: {
5862
+ attributes: ["data-voice", "_voice"],
5863
+ scriptTypes: ["text/voice"],
5864
+ defaultLanguage: "en"
5865
+ }
5866
+ });
5867
+ loadAllSchemas(registry2);
5868
+ return registry2;
5869
+ }
5870
+ async function loadAllSchemas(registry2) {
5871
+ const loaders = [
5872
+ { domain: "sql", load: () => import("@lokascript/domain-sql").then((m) => [...m.allSchemas]) },
5873
+ { domain: "bdd", load: () => import("@lokascript/domain-bdd").then((m) => [...m.allSchemas]) },
5874
+ { domain: "jsx", load: () => import("@lokascript/domain-jsx").then((m) => [...m.allSchemas]) },
5875
+ { domain: "todo", load: () => import("@lokascript/domain-todo").then((m) => [...m.allSchemas]) },
5876
+ {
5877
+ domain: "behaviorspec",
5878
+ load: () => import("@lokascript/domain-behaviorspec").then((m) => [...m.allSchemas])
5879
+ },
5880
+ { domain: "llm", load: () => import("@lokascript/domain-llm").then((m) => [...m.allSchemas]) },
5881
+ { domain: "flow", load: () => import("@lokascript/domain-flow").then((m) => [...m.allSchemas]) },
5882
+ {
5883
+ domain: "voice",
5884
+ load: () => import("@lokascript/domain-voice").then((m) => [...m.allSchemas])
5885
+ }
5886
+ ];
5887
+ await Promise.allSettled(
5888
+ loaders.map(async ({ domain, load }) => {
5889
+ try {
5890
+ const schemas = await load();
5891
+ registry2.setSchemas(domain, schemas);
5892
+ } catch {
5893
+ }
5894
+ })
5895
+ );
5896
+ }
5220
5897
 
5221
- | Command | Usage | Example |
5222
- |---------|-------|---------|
5223
- | \`set\` | Set variable/property | \`set :count to 0\` |
5224
- | \`get\` | Get value | \`get #input.value\` |
5225
- | \`increment\` | Add 1 | \`increment :count\` |
5226
- | \`decrement\` | Subtract 1 | \`decrement :count\` |
5898
+ // src/tools/bdd-extras.ts
5899
+ var bddScenarioParser = null;
5900
+ var bddGenerator = null;
5901
+ var bddRenderer = null;
5902
+ async function getBDDExtras() {
5903
+ if (bddScenarioParser)
5904
+ return { parseScenario: bddScenarioParser, generator: bddGenerator, renderer: bddRenderer };
5905
+ try {
5906
+ const mod = await import("@lokascript/domain-bdd");
5907
+ bddScenarioParser = mod.parseBDDScenario;
5908
+ bddGenerator = mod.bddCodeGenerator;
5909
+ bddRenderer = mod.renderBDD ?? null;
5910
+ return { parseScenario: bddScenarioParser, generator: bddGenerator, renderer: bddRenderer };
5911
+ } catch {
5912
+ throw new Error("@lokascript/domain-bdd not available for multi-step parsing.");
5913
+ }
5914
+ }
5915
+ function serializeRoles(roles) {
5916
+ const result = {};
5917
+ for (const [key, value] of roles) {
5918
+ result[key] = value;
5919
+ }
5920
+ return result;
5921
+ }
5922
+ function isMultiStepBDD(args) {
5923
+ const scenario = getString(args, "scenario");
5924
+ return /[,\n،、。]/.test(scenario);
5925
+ }
5926
+ async function handleBDDMultiStep(name, args) {
5927
+ const scenario = getString(args, "scenario");
5928
+ if (!scenario) return errorResponse("Missing required parameter: scenario");
5929
+ try {
5930
+ const { parseScenario, generator, renderer } = await getBDDExtras();
5931
+ const operation = name.replace("_bdd", "");
5932
+ if (operation === "parse") {
5933
+ const language = getString(args, "language", "en");
5934
+ const result = parseScenario(scenario, language);
5935
+ return jsonResponse({
5936
+ type: "scenario",
5937
+ stepCount: result.steps.length,
5938
+ steps: result.steps.map((step) => ({
5939
+ action: step.action,
5940
+ roles: serializeRoles(step.roles)
5941
+ })),
5942
+ errors: result.errors,
5943
+ language
5944
+ });
5945
+ }
5946
+ if (operation === "compile") {
5947
+ const language = getString(args, "language", "en");
5948
+ const result = parseScenario(scenario, language);
5949
+ const code = generator.generate(result.scenario);
5950
+ return jsonResponse({
5951
+ ok: result.errors.length === 0,
5952
+ code,
5953
+ stepCount: result.steps.length,
5954
+ ...result.name ? { scenarioName: result.name } : {},
5955
+ errors: result.errors.length > 0 ? result.errors : void 0,
5956
+ language
5957
+ });
5958
+ }
5959
+ if (operation === "validate") {
5960
+ const language = getString(args, "language", "en");
5961
+ const result = parseScenario(scenario, language);
5962
+ return jsonResponse({
5963
+ valid: result.errors.length === 0,
5964
+ stepCount: result.steps.length,
5965
+ errorCount: result.errors.length,
5966
+ errors: result.errors,
5967
+ language
5968
+ });
5969
+ }
5970
+ if (operation === "translate") {
5971
+ const from = getString(args, "from");
5972
+ const to = getString(args, "to");
5973
+ if (!from || !to) return errorResponse("Missing required parameter: from, to");
5974
+ const result = parseScenario(scenario, from);
5975
+ let rendered = null;
5976
+ if (renderer) {
5977
+ const renderedSteps = result.steps.map((step) => renderer(step, to));
5978
+ rendered = renderedSteps.join("\n");
5979
+ }
5980
+ return jsonResponse({
5981
+ input: { scenario, language: from },
5982
+ translated: rendered,
5983
+ playwright: generator.generate(result.scenario),
5984
+ steps: result.steps.map((step) => ({
5985
+ action: step.action,
5986
+ roles: serializeRoles(step.roles)
5987
+ })),
5988
+ errors: result.errors
5989
+ });
5990
+ }
5991
+ return errorResponse(`Unknown BDD operation: ${operation}`);
5992
+ } catch (error) {
5993
+ const message = error instanceof Error ? error.message : String(error);
5994
+ return errorResponse(`BDD multi-step error: ${message}`);
5995
+ }
5996
+ }
5227
5997
 
5228
- ## Events
5998
+ // src/tools/behaviorspec-extras.ts
5999
+ var behaviorSpecParser = null;
6000
+ var behaviorCompiler = null;
6001
+ var behaviorRenderer = null;
6002
+ async function getBehaviorSpecExtras() {
6003
+ if (behaviorSpecParser)
6004
+ return { parseSpec: behaviorSpecParser, compile: behaviorCompiler, render: behaviorRenderer };
6005
+ try {
6006
+ const mod = await import("@lokascript/domain-behaviorspec");
6007
+ behaviorSpecParser = mod.parseBehaviorSpec;
6008
+ behaviorCompiler = mod.compileBehaviorSpec;
6009
+ behaviorRenderer = mod.renderBehaviorSpec;
6010
+ return { parseSpec: behaviorSpecParser, compile: behaviorCompiler, render: behaviorRenderer };
6011
+ } catch {
6012
+ throw new Error("@lokascript/domain-behaviorspec not available for multi-line parsing.");
6013
+ }
6014
+ }
6015
+ function serializeRoles2(roles) {
6016
+ const result = {};
6017
+ for (const [key, value] of roles) {
6018
+ result[key] = value;
6019
+ }
6020
+ return result;
6021
+ }
6022
+ function isMultiLineBehaviorSpec(args) {
6023
+ const scenario = getString(args, "scenario");
6024
+ return scenario.includes("\n");
6025
+ }
6026
+ async function handleBehaviorSpecMultiLine(name, args) {
6027
+ const scenario = getString(args, "scenario");
6028
+ if (!scenario) return errorResponse("Missing required parameter: scenario");
6029
+ try {
6030
+ const { parseSpec, compile, render } = await getBehaviorSpecExtras();
6031
+ const operation = name.replace("_behaviorspec", "");
6032
+ if (operation === "parse") {
6033
+ const language = getString(args, "language", "en");
6034
+ const result = parseSpec(scenario, language);
6035
+ return jsonResponse({
6036
+ type: "spec",
6037
+ testCount: result.tests.length,
6038
+ tests: result.tests.map((test) => ({
6039
+ name: test.name,
6040
+ givenCount: test.givens.length,
6041
+ interactionCount: test.interactions.length,
6042
+ givens: test.givens.map((g) => ({
6043
+ action: g.action,
6044
+ roles: serializeRoles2(g.roles)
6045
+ })),
6046
+ interactions: test.interactions.map((i) => ({
6047
+ when: { action: i.when.action, roles: serializeRoles2(i.when.roles) },
6048
+ expectationCount: i.expectations.length
6049
+ }))
6050
+ })),
6051
+ errors: result.errors,
6052
+ language
6053
+ });
6054
+ }
6055
+ if (operation === "compile") {
6056
+ const language = getString(args, "language", "en");
6057
+ const code = compile(scenario, language);
6058
+ return jsonResponse({ ok: true, code, language });
6059
+ }
6060
+ if (operation === "validate") {
6061
+ const language = getString(args, "language", "en");
6062
+ const result = parseSpec(scenario, language);
6063
+ return jsonResponse({
6064
+ valid: result.errors.length === 0,
6065
+ testCount: result.tests.length,
6066
+ errorCount: result.errors.length,
6067
+ errors: result.errors,
6068
+ language
6069
+ });
6070
+ }
6071
+ if (operation === "translate") {
6072
+ const from = getString(args, "from");
6073
+ const to = getString(args, "to");
6074
+ if (!from || !to) return errorResponse("Missing required parameter: from, to");
6075
+ const result = parseSpec(scenario, from);
6076
+ const renderedTests = [];
6077
+ for (const test of result.tests) {
6078
+ const lines = [];
6079
+ lines.push(`test "${test.name}"`);
6080
+ for (const given of test.givens) {
6081
+ lines.push(` ${render(given, to)}`);
6082
+ }
6083
+ for (const interaction of test.interactions) {
6084
+ lines.push(` ${render(interaction.when, to)}`);
6085
+ for (const exp of interaction.expectations) {
6086
+ lines.push(` ${render(exp.node, to)}`);
6087
+ }
6088
+ }
6089
+ renderedTests.push(lines.join("\n"));
6090
+ }
6091
+ return jsonResponse({
6092
+ input: { scenario, language: from },
6093
+ translated: renderedTests.join("\n\n"),
6094
+ playwright: compile(scenario, from),
6095
+ errors: result.errors
6096
+ });
6097
+ }
6098
+ return errorResponse(`Unknown BehaviorSpec operation: ${operation}`);
6099
+ } catch (error) {
6100
+ const message = error instanceof Error ? error.message : String(error);
6101
+ return errorResponse(`BehaviorSpec multi-line error: ${message}`);
6102
+ }
6103
+ }
5229
6104
 
5230
- | Command | Usage | Example |
5231
- |---------|-------|---------|
6105
+ // src/prompts/index.ts
6106
+ var LLM_PROMPTS = [
6107
+ {
6108
+ name: "llm_ask",
6109
+ description: "Ask an LLM a question with optional context and response style",
6110
+ arguments: [
6111
+ { name: "question", description: "The question or prompt to ask", required: true },
6112
+ {
6113
+ name: "context",
6114
+ description: "DOM selector, URL, or text to use as context",
6115
+ required: false
6116
+ },
6117
+ {
6118
+ name: "style",
6119
+ description: "Response style: bullets, json, paragraph, brief",
6120
+ required: false
6121
+ },
6122
+ { name: "model", description: "Model hint: claude, gpt, etc.", required: false }
6123
+ ]
6124
+ },
6125
+ {
6126
+ name: "llm_summarize",
6127
+ description: "Summarize content using an LLM",
6128
+ arguments: [
6129
+ { name: "content", description: "Text or selector of content to summarize", required: true },
6130
+ {
6131
+ name: "length",
6132
+ description: 'Target length: "3 sentences", "100 words", "2 paragraphs"',
6133
+ required: false
6134
+ },
6135
+ {
6136
+ name: "format",
6137
+ description: "Output format: bullets, paragraph, tldr, markdown",
6138
+ required: false
6139
+ }
6140
+ ]
6141
+ },
6142
+ {
6143
+ name: "llm_analyze",
6144
+ description: "Analyze content for a specific quality (sentiment, entities, themes, etc.)",
6145
+ arguments: [
6146
+ { name: "content", description: "Text or selector of content to analyze", required: true },
6147
+ {
6148
+ name: "analysis_type",
6149
+ description: "Analysis type: sentiment, entities, themes, tone, keywords",
6150
+ required: true
6151
+ }
6152
+ ]
6153
+ },
6154
+ {
6155
+ name: "llm_translate",
6156
+ description: "Translate content between natural languages using an LLM",
6157
+ arguments: [
6158
+ { name: "content", description: "Text or selector of content to translate", required: true },
6159
+ {
6160
+ name: "from_language",
6161
+ description: "Source language (english, spanish, japanese, etc.)",
6162
+ required: true
6163
+ },
6164
+ {
6165
+ name: "to_language",
6166
+ description: "Target language (english, spanish, japanese, etc.)",
6167
+ required: true
6168
+ }
6169
+ ]
6170
+ }
6171
+ ];
6172
+ function getLLMPromptDefinitions() {
6173
+ return LLM_PROMPTS;
6174
+ }
6175
+ function renderLLMPrompt(name, args) {
6176
+ switch (name) {
6177
+ case "llm_ask":
6178
+ return renderAskPrompt(args);
6179
+ case "llm_summarize":
6180
+ return renderSummarizePrompt(args);
6181
+ case "llm_analyze":
6182
+ return renderAnalyzePrompt(args);
6183
+ case "llm_translate":
6184
+ return renderTranslatePrompt(args);
6185
+ default:
6186
+ throw new Error(`Unknown LLM prompt: ${name}`);
6187
+ }
6188
+ }
6189
+ function renderAskPrompt(args) {
6190
+ const { question, context, style, model } = args;
6191
+ let content = question || "(no question provided)";
6192
+ if (context) content = `Context: ${context}
6193
+
6194
+ ${content}`;
6195
+ if (style) content += `
6196
+
6197
+ Please respond as: ${style}`;
6198
+ const modelNote = model ? ` (using ${model})` : "";
6199
+ return {
6200
+ description: `Ask${modelNote}: ${question}`,
6201
+ messages: [{ role: "user", content: { type: "text", text: content } }]
6202
+ };
6203
+ }
6204
+ function renderSummarizePrompt(args) {
6205
+ const { content, length, format } = args;
6206
+ let text = `Please summarize the following content`;
6207
+ if (length) text += ` in ${length}`;
6208
+ if (format) text += ` as ${format}`;
6209
+ text += `:
6210
+
6211
+ ${content || "(no content provided)"}`;
6212
+ return {
6213
+ description: `Summarize${length ? ` in ${length}` : ""}`,
6214
+ messages: [{ role: "user", content: { type: "text", text } }]
6215
+ };
6216
+ }
6217
+ function renderAnalyzePrompt(args) {
6218
+ const { content, analysis_type } = args;
6219
+ const text = `Please analyze the following content for ${analysis_type || "general insights"}:
6220
+
6221
+ ${content || "(no content provided)"}`;
6222
+ return {
6223
+ description: `Analyze as ${analysis_type}`,
6224
+ messages: [{ role: "user", content: { type: "text", text } }]
6225
+ };
6226
+ }
6227
+ function renderTranslatePrompt(args) {
6228
+ const { content, from_language, to_language } = args;
6229
+ const text = `Please translate the following from ${from_language || "the source language"} to ${to_language || "the target language"}:
6230
+
6231
+ ${content || "(no content provided)"}`;
6232
+ return {
6233
+ description: `Translate from ${from_language} to ${to_language}`,
6234
+ messages: [{ role: "user", content: { type: "text", text } }]
6235
+ };
6236
+ }
6237
+
6238
+ // src/prompts/debug-prompts.ts
6239
+ var DEBUG_PROMPTS = [
6240
+ {
6241
+ name: "debug_explain_state",
6242
+ description: "Explain the current debug state when paused in the HyperFixi debugger. Provide a snapshot of the execution state and get a human-friendly explanation.",
6243
+ arguments: [
6244
+ {
6245
+ name: "snapshot",
6246
+ description: "JSON-serialized DebugSnapshot: { commandName, element, variables, depth, index }",
6247
+ required: true
6248
+ },
6249
+ {
6250
+ name: "handler_code",
6251
+ description: "The full hyperscript handler code for additional context",
6252
+ required: false
6253
+ }
6254
+ ]
6255
+ },
6256
+ {
6257
+ name: "debug_why_wrong",
6258
+ description: "Diagnose why a variable has an unexpected value during debugging. Provide the execution history and expected vs actual values.",
6259
+ arguments: [
6260
+ {
6261
+ name: "variable",
6262
+ description: 'The variable name (e.g., "it", ":count", "me")',
6263
+ required: true
6264
+ },
6265
+ {
6266
+ name: "expected",
6267
+ description: "What the developer expected the value to be",
6268
+ required: true
6269
+ },
6270
+ {
6271
+ name: "actual",
6272
+ description: "The actual value observed",
6273
+ required: true
6274
+ },
6275
+ {
6276
+ name: "history",
6277
+ description: "JSON-serialized array of DebugSnapshot objects showing execution history",
6278
+ required: false
6279
+ }
6280
+ ]
6281
+ },
6282
+ {
6283
+ name: "debug_suggest_breakpoints",
6284
+ description: "Given hyperscript code and a description of the issue, suggest where to place breakpoints for effective debugging.",
6285
+ arguments: [
6286
+ {
6287
+ name: "code",
6288
+ description: "The hyperscript event handler code",
6289
+ required: true
6290
+ },
6291
+ {
6292
+ name: "issue",
6293
+ description: 'Description of the problem to debug (e.g., "the counter does not increment")',
6294
+ required: true
6295
+ }
6296
+ ]
6297
+ }
6298
+ ];
6299
+ function getDebugPromptDefinitions() {
6300
+ return DEBUG_PROMPTS;
6301
+ }
6302
+ function renderDebugPrompt(name, args) {
6303
+ switch (name) {
6304
+ case "debug_explain_state":
6305
+ return renderExplainState(args);
6306
+ case "debug_why_wrong":
6307
+ return renderWhyWrong(args);
6308
+ case "debug_suggest_breakpoints":
6309
+ return renderSuggestBreakpoints(args);
6310
+ default:
6311
+ throw new Error(`Unknown debug prompt: ${name}`);
6312
+ }
6313
+ }
6314
+ function isDebugPrompt(name) {
6315
+ return DEBUG_PROMPTS.some((p) => p.name === name);
6316
+ }
6317
+ function renderExplainState(args) {
6318
+ const { snapshot, handler_code } = args;
6319
+ let text = `I'm paused in the HyperFixi interactive debugger. Here is my current execution state:
6320
+
6321
+ `;
6322
+ text += `**Debug Snapshot:**
6323
+ \`\`\`json
6324
+ ${snapshot}
6325
+ \`\`\`
6326
+
6327
+ `;
6328
+ if (handler_code) {
6329
+ text += `**Full handler code:**
6330
+ \`\`\`hyperscript
6331
+ ${handler_code}
6332
+ \`\`\`
6333
+
6334
+ `;
6335
+ }
6336
+ text += `Please explain:
6337
+ `;
6338
+ text += `1. What command is about to execute and what it will do
6339
+ `;
6340
+ text += `2. What the current variable state tells us
6341
+ `;
6342
+ text += `3. What will likely happen next
6343
+ `;
6344
+ text += `4. Any potential issues you see in the state`;
6345
+ return {
6346
+ description: "Explain current debug state",
6347
+ messages: [{ role: "user", content: { type: "text", text } }]
6348
+ };
6349
+ }
6350
+ function renderWhyWrong(args) {
6351
+ const { variable, expected, actual, history } = args;
6352
+ let text = `I'm debugging a HyperFixi hyperscript handler and a variable has an unexpected value.
6353
+
6354
+ `;
6355
+ text += `**Variable:** \`${variable}\`
6356
+ `;
6357
+ text += `**Expected:** ${expected}
6358
+ `;
6359
+ text += `**Actual:** ${actual}
6360
+
6361
+ `;
6362
+ if (history) {
6363
+ text += `**Execution history (DebugSnapshots):**
6364
+ \`\`\`json
6365
+ ${history}
6366
+ \`\`\`
6367
+
6368
+ `;
6369
+ }
6370
+ text += `Please analyze:
6371
+ `;
6372
+ text += `1. What likely caused \`${variable}\` to be \`${actual}\` instead of \`${expected}\`
6373
+ `;
6374
+ text += `2. Which command in the history is the most likely source of the problem
6375
+ `;
6376
+ text += `3. How to fix it`;
6377
+ return {
6378
+ description: `Diagnose unexpected value of ${variable}`,
6379
+ messages: [{ role: "user", content: { type: "text", text } }]
6380
+ };
6381
+ }
6382
+ function renderSuggestBreakpoints(args) {
6383
+ const { code, issue } = args;
6384
+ let text = `I need to debug this HyperFixi hyperscript code:
6385
+
6386
+ `;
6387
+ text += `\`\`\`hyperscript
6388
+ ${code}
6389
+ \`\`\`
6390
+
6391
+ `;
6392
+ text += `**Issue:** ${issue}
6393
+
6394
+ `;
6395
+ text += `Please suggest:
6396
+ `;
6397
+ text += `1. Where to place breakpoints (by command name or element selector)
6398
+ `;
6399
+ text += `2. What variables to watch at each breakpoint
6400
+ `;
6401
+ text += `3. A step-by-step debugging strategy to isolate the issue`;
6402
+ return {
6403
+ description: `Debug strategy for: ${issue}`,
6404
+ messages: [{ role: "user", content: { type: "text", text } }]
6405
+ };
6406
+ }
6407
+
6408
+ // src/prompts/lse-prompts.ts
6409
+ var LSE_PROMPTS = [
6410
+ {
6411
+ name: "lse_generate",
6412
+ description: "Generate LokaScript Explicit Syntax (LSE) from a task description. Includes schema-aware system prompt context.",
6413
+ arguments: [
6414
+ {
6415
+ name: "task",
6416
+ description: 'Natural language description of what to do (e.g., "fetch users from API and show in a list")',
6417
+ required: true
6418
+ },
6419
+ {
6420
+ name: "domain",
6421
+ description: "Domain to generate for (flow, sql, bdd, llm, jsx, todo, behaviorspec, voice). Default: flow",
6422
+ required: false
6423
+ },
6424
+ {
6425
+ name: "format",
6426
+ description: "Output format: explicit (bracket syntax), json (LLM-simplified), or both. Default: explicit",
6427
+ required: false
6428
+ }
6429
+ ]
6430
+ },
6431
+ {
6432
+ name: "lse_fix",
6433
+ description: "Fix invalid LSE given the original input and diagnostic errors. Returns corrected LSE with explanation.",
6434
+ arguments: [
6435
+ {
6436
+ name: "input",
6437
+ description: "The invalid LSE bracket syntax or JSON that failed validation",
6438
+ required: true
6439
+ },
6440
+ {
6441
+ name: "errors",
6442
+ description: "Error messages or diagnostic output from the validator",
6443
+ required: true
6444
+ },
6445
+ {
6446
+ name: "domain",
6447
+ description: "Domain context (flow, sql, bdd, etc.). Helps narrow valid commands.",
6448
+ required: false
6449
+ }
6450
+ ]
6451
+ }
6452
+ ];
6453
+ function getLSEPromptDefinitions() {
6454
+ return LSE_PROMPTS;
6455
+ }
6456
+ function isLSEPrompt(name) {
6457
+ return name === "lse_generate" || name === "lse_fix";
6458
+ }
6459
+ function renderLSEPrompt(name, args) {
6460
+ switch (name) {
6461
+ case "lse_generate":
6462
+ return renderGeneratePrompt(args);
6463
+ case "lse_fix":
6464
+ return renderFixPrompt(args);
6465
+ default:
6466
+ throw new Error(`Unknown LSE prompt: ${name}`);
6467
+ }
6468
+ }
6469
+ function renderGeneratePrompt(args) {
6470
+ const { task, domain, format } = args;
6471
+ const domainName = domain || "flow";
6472
+ const outputFormat = format || "explicit";
6473
+ const systemPrompt = buildLSESystemPrompt(domainName, outputFormat);
6474
+ const userPrompt = task || "(no task provided)";
6475
+ return {
6476
+ description: `Generate LSE for: ${task}`,
6477
+ messages: [
6478
+ {
6479
+ role: "user",
6480
+ content: {
6481
+ type: "text",
6482
+ text: `${systemPrompt}
6483
+
6484
+ ---
6485
+
6486
+ **Task:** ${userPrompt}
6487
+
6488
+ Generate the LSE output for this task.`
6489
+ }
6490
+ }
6491
+ ]
6492
+ };
6493
+ }
6494
+ function renderFixPrompt(args) {
6495
+ const { input, errors, domain } = args;
6496
+ const domainNote = domain ? ` in the ${domain} domain` : "";
6497
+ const text = `You are fixing invalid LokaScript Explicit Syntax (LSE)${domainNote}.
6498
+
6499
+ **Original input:**
6500
+ \`\`\`
6501
+ ${input || "(no input provided)"}
6502
+ \`\`\`
6503
+
6504
+ **Errors:**
6505
+ \`\`\`
6506
+ ${errors || "(no errors provided)"}
6507
+ \`\`\`
6508
+
6509
+ **Rules for fixing:**
6510
+ 1. Preserve the original intent \u2014 only fix what's broken
6511
+ 2. Use valid bracket syntax: \`[command role:value ...]\`
6512
+ 3. All required roles must be present
6513
+ 4. Value types must match the role's expected type
6514
+ 5. Command names must be lowercase
6515
+
6516
+ **Output:** Provide the corrected LSE and briefly explain what was wrong.`;
6517
+ return {
6518
+ description: `Fix LSE: ${(input || "").slice(0, 50)}...`,
6519
+ messages: [{ role: "user", content: { type: "text", text } }]
6520
+ };
6521
+ }
6522
+ function buildLSESystemPrompt(domain, format) {
6523
+ const formatInstructions = format === "json" ? 'Output valid JSON in the full-fidelity format: { "kind": "command", "action": "...", "roles": { "roleName": { "type": "...", "value": "..." } } }' : format === "both" ? 'Output EITHER bracket syntax [command role:value ...] OR full-fidelity JSON { "kind": "command", "action": "...", "roles": { ... } }.' : "Output valid LSE bracket syntax: [command role:value ...]";
6524
+ return `You are generating LokaScript Explicit Syntax (LSE) for the "${domain}" domain.
6525
+
6526
+ **LSE Bracket Syntax:**
6527
+ - Commands: \`[action role1:value1 role2:value2 +flag]\`
6528
+ - No spaces around the colon in role:value pairs
6529
+ - Selectors: \`#id\`, \`.class\`, \`[attr]\`, \`@aria\`, \`*wild\`
6530
+ - Strings with spaces: \`"hello world"\`
6531
+ - References: \`me\`, \`you\`, \`it\`, \`result\`, \`event\`, \`target\`, \`body\`
6532
+ - Flags: \`+enabled\`, \`~disabled\`
6533
+ - Durations: \`500ms\`, \`2s\`, \`1m\`, \`1h\`
6534
+ - Compound: \`[add patient:.loading] then [fetch source:/api]\` (chain operators: then, and, async, sequential)
6535
+
6536
+ **Event Handlers:**
6537
+ \`[on event:click body:[toggle patient:.active]]\`
6538
+
6539
+ **Conditional (v1.1):**
6540
+ \`[if condition:"x > 0" then:[toggle patient:.active] else:[remove patient:.active]]\`
6541
+
6542
+ **Loop (v1.1):**
6543
+ - \`[repeat loopVariant:forever body:[wait delay:1s]]\`
6544
+ - \`[repeat quantity:5 loopVariant:times body:[increment patient:#count]]\`
6545
+ - \`[repeat source:#items loopVariant:for loopVariable:item body:[add patient:.active]]\`
6546
+ - \`[repeat condition:"x > 0" loopVariant:while body:[decrement patient:#count]]\`
6547
+
6548
+ **Pipe operator (v1.2):**
6549
+ \`[fetch source:/api/users] |> [put destination:#list]\` (threads output of left into right)
6550
+ - In JSON: compound node with \`chainType: "pipe"\`
6551
+
6552
+ **Match / pattern dispatch (v1.2):**
6553
+ \`[match patient:status arms:[{pattern:"active" body:[show patient:#panel]},{pattern:"idle" body:[hide patient:#panel]}] defaultArm:[log patient:"unknown"]]\`
6554
+ - Arms are ordered; first match wins; \`defaultArm\` is the catch-all
6555
+
6556
+ **Try / catch / finally (v1.2):**
6557
+ \`[try body:[fetch source:/api] catchBranch:[show patient:#error] finallyBranch:[remove patient:.loading]]\`
6558
+ - \`catchBranch\` and \`finallyBranch\` are both optional
6559
+
6560
+ **Async coordination (v1.2):**
6561
+ - \`[all asyncBody:[fetch source:/api/users, fetch source:/api/posts]]\` \u2014 wait for all, fail fast
6562
+ - \`[race asyncBody:[fetch source:/cache, fetch source:/api]]\` \u2014 first to resolve wins
6563
+
6564
+ **Annotations (v1.2):**
6565
+ \`@timeout(5s) @retry(3) [fetch source:/api/users]\`
6566
+ - In JSON: \`annotations: [{name:"timeout",value:"5s"},{name:"retry",value:"3"}]\` on the node
6567
+ - Annotations are metadata; they do not change command semantics
6568
+
6569
+ **Value types:** selector, literal (string/number/boolean/duration), reference, expression, property-path, flag.
6570
+
6571
+ **Node kinds (JSON):** command, event-handler, compound.
6572
+ - Conditionals are \`command\` nodes with \`thenBranch\`/\`elseBranch\` arrays.
6573
+ - Loops are \`command\` nodes with \`loopVariant\`, \`loopBody\`, \`loopVariable\`?, \`indexVariable\`?.
6574
+ - Try/catch is a \`command\` node with \`body\`, \`catchBranch\`?, \`finallyBranch\`? arrays.
6575
+ - Async is a \`command\` node with \`asyncVariant: "all"|"race"\` and \`asyncBody\` array.
6576
+ - Match is a \`command\` node with \`arms\` array and optional \`defaultArm\` array.
6577
+
6578
+ **${formatInstructions}**
6579
+
6580
+ Every value must have a role name. Do not invent commands or roles not in the schema. When unsure, use the "expression" value type.`;
6581
+ }
6582
+
6583
+ // src/tools/llm-sampling.ts
6584
+ var samplingTools = [
6585
+ {
6586
+ name: "ask_claude",
6587
+ description: "Ask Claude a question with optional context. Uses MCP sampling \u2014 the client handles model selection and user approval.",
6588
+ inputSchema: {
6589
+ type: "object",
6590
+ properties: {
6591
+ question: {
6592
+ type: "string",
6593
+ description: "The question or prompt to ask"
6594
+ },
6595
+ context: {
6596
+ type: "string",
6597
+ description: "Optional context text to include with the question"
6598
+ },
6599
+ style: {
6600
+ type: "string",
6601
+ description: "Response style: bullets, json, paragraph, brief",
6602
+ enum: ["bullets", "json", "paragraph", "brief"]
6603
+ },
6604
+ maxTokens: {
6605
+ type: "number",
6606
+ description: "Maximum tokens for the response (default: 1024)"
6607
+ }
6608
+ },
6609
+ required: ["question"]
6610
+ }
6611
+ },
6612
+ {
6613
+ name: "summarize_content",
6614
+ description: "Summarize text content using Claude. Uses MCP sampling \u2014 the client handles model selection and user approval.",
6615
+ inputSchema: {
6616
+ type: "object",
6617
+ properties: {
6618
+ content: {
6619
+ type: "string",
6620
+ description: "Text content to summarize"
6621
+ },
6622
+ length: {
6623
+ type: "string",
6624
+ description: 'Target length: "3 sentences", "100 words", "2 paragraphs"'
6625
+ },
6626
+ format: {
6627
+ type: "string",
6628
+ description: "Output format: bullets, paragraph, tldr, markdown",
6629
+ enum: ["bullets", "paragraph", "tldr", "markdown"]
6630
+ },
6631
+ maxTokens: {
6632
+ type: "number",
6633
+ description: "Maximum tokens for the response (default: 512)"
6634
+ }
6635
+ },
6636
+ required: ["content"]
6637
+ }
6638
+ },
6639
+ {
6640
+ name: "analyze_content",
6641
+ description: "Analyze text content for sentiment, entities, themes, or other qualities using Claude. Uses MCP sampling.",
6642
+ inputSchema: {
6643
+ type: "object",
6644
+ properties: {
6645
+ content: {
6646
+ type: "string",
6647
+ description: "Text content to analyze"
6648
+ },
6649
+ analysis_type: {
6650
+ type: "string",
6651
+ description: "Analysis type: sentiment, entities, themes, tone, keywords",
6652
+ enum: ["sentiment", "entities", "themes", "tone", "keywords"]
6653
+ },
6654
+ maxTokens: {
6655
+ type: "number",
6656
+ description: "Maximum tokens for the response (default: 512)"
6657
+ }
6658
+ },
6659
+ required: ["content", "analysis_type"]
6660
+ }
6661
+ },
6662
+ {
6663
+ name: "translate_content",
6664
+ description: "Translate text between natural languages using Claude. Uses MCP sampling \u2014 the client handles model selection and user approval.",
6665
+ inputSchema: {
6666
+ type: "object",
6667
+ properties: {
6668
+ content: {
6669
+ type: "string",
6670
+ description: "Text content to translate"
6671
+ },
6672
+ from_language: {
6673
+ type: "string",
6674
+ description: "Source language (english, spanish, japanese, etc.)"
6675
+ },
6676
+ to_language: {
6677
+ type: "string",
6678
+ description: "Target language (english, spanish, japanese, etc.)"
6679
+ },
6680
+ maxTokens: {
6681
+ type: "number",
6682
+ description: "Maximum tokens for the response (default: 1024)"
6683
+ }
6684
+ },
6685
+ required: ["content", "from_language", "to_language"]
6686
+ }
6687
+ },
6688
+ {
6689
+ name: "execute_llm",
6690
+ description: 'Execute an LLM command written in natural language (8 languages supported). Compiles via domain-llm then invokes Claude via MCP sampling. Examples: "ask what is 2+2", "summarize #article as bullets", "\uC694\uC57D \uC774 \uBB38\uC11C\uB97C \uAE00\uBA38\uB9AC\uB85C" (Korean).',
6691
+ inputSchema: {
6692
+ type: "object",
6693
+ properties: {
6694
+ command: {
6695
+ type: "string",
6696
+ description: 'LLM command in natural language (e.g., "ask what is the capital of France")'
6697
+ },
6698
+ language: {
6699
+ type: "string",
6700
+ description: "Language code for the command (en, es, ja, ar, ko, zh, tr, fr). Default: en",
6701
+ default: "en"
6702
+ }
6703
+ },
6704
+ required: ["command"]
6705
+ }
6706
+ }
6707
+ ];
6708
+ function specToSamplingParams(spec) {
6709
+ const systemMessages = spec.messages.filter((m) => m.role === "system");
6710
+ const chatMessages = spec.messages.filter((m) => m.role !== "system");
6711
+ return {
6712
+ systemPrompt: systemMessages.length ? systemMessages.map((m) => m.content).join("\n") : void 0,
6713
+ messages: chatMessages.map((m) => ({
6714
+ role: m.role,
6715
+ content: { type: "text", text: m.content }
6716
+ })),
6717
+ maxTokens: spec.maxTokens ?? 1024,
6718
+ ...spec.modelPreferences && { modelPreferences: spec.modelPreferences }
6719
+ };
6720
+ }
6721
+ function argsToSpec(name, args) {
6722
+ switch (name) {
6723
+ case "ask_claude":
6724
+ return buildAskSpec(args);
6725
+ case "summarize_content":
6726
+ return buildSummarizeSpec(args);
6727
+ case "analyze_content":
6728
+ return buildAnalyzeSpec(args);
6729
+ case "translate_content":
6730
+ return buildTranslateSpec(args);
6731
+ default:
6732
+ throw new Error(`Unknown sampling tool: ${name}`);
6733
+ }
6734
+ }
6735
+ function buildAskSpec(args) {
6736
+ const question = String(args.question || "");
6737
+ const context = args.context ? String(args.context) : void 0;
6738
+ const style = args.style ? String(args.style) : void 0;
6739
+ const styleInstruction = style ? `Respond in ${style} format.` : "Respond clearly and concisely.";
6740
+ const messages = [{ role: "system", content: styleInstruction }];
6741
+ if (context) {
6742
+ messages.push({ role: "user", content: `Context:
6743
+ ${context}` });
6744
+ }
6745
+ messages.push({ role: "user", content: question });
6746
+ return {
6747
+ action: "ask",
6748
+ messages,
6749
+ maxTokens: typeof args.maxTokens === "number" ? args.maxTokens : 1024,
6750
+ metadata: {
6751
+ sourceLanguage: "en",
6752
+ roles: { patient: question, source: context, manner: style }
6753
+ }
6754
+ };
6755
+ }
6756
+ function buildSummarizeSpec(args) {
6757
+ const content = String(args.content || "");
6758
+ const length = args.length ? String(args.length) : void 0;
6759
+ const format = args.format ? String(args.format) : void 0;
6760
+ const lengthHint = length ? ` in ${length}` : "";
6761
+ const formatHint = format ? ` as ${format}` : "";
6762
+ return {
6763
+ action: "summarize",
6764
+ messages: [
6765
+ {
6766
+ role: "system",
6767
+ content: `You are a summarization assistant. Provide concise${formatHint} summaries${lengthHint}.`
6768
+ },
6769
+ { role: "user", content: `Summarize the following:
6770
+
6771
+ ${content}` }
6772
+ ],
6773
+ maxTokens: typeof args.maxTokens === "number" ? args.maxTokens : 512,
6774
+ metadata: {
6775
+ sourceLanguage: "en",
6776
+ roles: { patient: content, quantity: length, manner: format }
6777
+ }
6778
+ };
6779
+ }
6780
+ function buildAnalyzeSpec(args) {
6781
+ const content = String(args.content || "");
6782
+ const analysisType = String(args.analysis_type || "general quality");
6783
+ return {
6784
+ action: "analyze",
6785
+ messages: [
6786
+ {
6787
+ role: "system",
6788
+ content: `You are an analysis assistant. Analyze content for ${analysisType}. Be specific and structured in your response.`
6789
+ },
6790
+ {
6791
+ role: "user",
6792
+ content: `Analyze the following for ${analysisType}:
6793
+
6794
+ ${content}`
6795
+ }
6796
+ ],
6797
+ maxTokens: typeof args.maxTokens === "number" ? args.maxTokens : 512,
6798
+ metadata: {
6799
+ sourceLanguage: "en",
6800
+ roles: { patient: content, manner: analysisType }
6801
+ }
6802
+ };
6803
+ }
6804
+ function buildTranslateSpec(args) {
6805
+ const content = String(args.content || "");
6806
+ const fromLang = String(args.from_language || "the source language");
6807
+ const toLang = String(args.to_language || "English");
6808
+ return {
6809
+ action: "translate",
6810
+ messages: [
6811
+ {
6812
+ role: "system",
6813
+ content: `You are a professional translator. Translate accurately from ${fromLang} to ${toLang}. Preserve tone and meaning. Return only the translation without explanation.`
6814
+ },
6815
+ {
6816
+ role: "user",
6817
+ content: `Translate from ${fromLang} to ${toLang}:
6818
+
6819
+ ${content}`
6820
+ }
6821
+ ],
6822
+ maxTokens: typeof args.maxTokens === "number" ? args.maxTokens : 2048,
6823
+ metadata: {
6824
+ sourceLanguage: "en",
6825
+ roles: { patient: content, source: fromLang, destination: toLang }
6826
+ }
6827
+ };
6828
+ }
6829
+ async function handleSamplingTool(name, args, server2, registry2) {
6830
+ try {
6831
+ if (name === "execute_llm") {
6832
+ return await handleExecuteLLM(args, server2, registry2);
6833
+ }
6834
+ const spec = argsToSpec(name, args);
6835
+ return await executeSpec(spec, server2);
6836
+ } catch (error) {
6837
+ const message = error instanceof Error ? error.message : String(error);
6838
+ return {
6839
+ content: [{ type: "text", text: `Sampling error: ${message}` }],
6840
+ isError: true
6841
+ };
6842
+ }
6843
+ }
6844
+ async function executeSpec(spec, server2) {
6845
+ const params = specToSamplingParams(spec);
6846
+ const response = await server2.createMessage(params);
6847
+ const text = response.content.type === "text" ? response.content.text : `Received non-text response of type: ${response.content.type}`;
6848
+ return { content: [{ type: "text", text }] };
6849
+ }
6850
+ async function handleExecuteLLM(args, server2, registry2) {
6851
+ if (!registry2) {
6852
+ return {
6853
+ content: [{ type: "text", text: "execute_llm requires domain registry (internal error)" }],
6854
+ isError: true
6855
+ };
6856
+ }
6857
+ const command = String(args.command || "");
6858
+ const language = String(args.language || "en");
6859
+ if (!command.trim()) {
6860
+ return {
6861
+ content: [{ type: "text", text: "execute_llm requires a non-empty command" }],
6862
+ isError: true
6863
+ };
6864
+ }
6865
+ const dsl = await registry2.getDSLForDomain("llm");
6866
+ if (!dsl) {
6867
+ return {
6868
+ content: [{ type: "text", text: "LLM domain not registered" }],
6869
+ isError: true
6870
+ };
6871
+ }
6872
+ const result = dsl.compile(command, language);
6873
+ if (!result.ok || !result.code) {
6874
+ const errors = result.errors?.join(", ") || "Unknown compilation error";
6875
+ return {
6876
+ content: [{ type: "text", text: `Compilation failed: ${errors}` }],
6877
+ isError: true
6878
+ };
6879
+ }
6880
+ let spec;
6881
+ try {
6882
+ spec = JSON.parse(result.code);
6883
+ } catch {
6884
+ return {
6885
+ content: [{ type: "text", text: `Invalid LLMPromptSpec JSON: ${result.code}` }],
6886
+ isError: true
6887
+ };
6888
+ }
6889
+ return await executeSpec(spec, server2);
6890
+ }
6891
+
6892
+ // src/tools/dispatcher.ts
6893
+ import { CrossDomainDispatcher } from "@lokascript/framework";
6894
+ var dispatcher = null;
6895
+ function getDispatcher(registry2) {
6896
+ if (!dispatcher) {
6897
+ dispatcher = new CrossDomainDispatcher(registry2, {
6898
+ minConfidence: 0.5,
6899
+ // Domains with more specific schemas should win over generic catch-all domains
6900
+ // when confidence scores are close. voice/flow have broad patterns that match
6901
+ // inputs intended for sql/bdd/todo/jsx.
6902
+ priority: ["sql", "bdd", "behaviorspec", "jsx", "todo", "llm", "flow", "voice"]
6903
+ });
6904
+ }
6905
+ return dispatcher;
6906
+ }
6907
+ function serializeRoles3(roles) {
6908
+ const result = {};
6909
+ for (const [key, value] of roles) {
6910
+ result[key] = value;
6911
+ }
6912
+ return result;
6913
+ }
6914
+ var dispatcherTools = [
6915
+ {
6916
+ name: "detect_domain",
6917
+ description: "Auto-detect which registered domain handles the input. Tries all domains (sql, bdd, jsx, todo, behaviorspec, llm, flow, voice) and returns the best match by confidence score.",
6918
+ inputSchema: {
6919
+ type: "object",
6920
+ properties: {
6921
+ input: {
6922
+ type: "string",
6923
+ description: "The natural-language input to classify"
6924
+ },
6925
+ language: {
6926
+ type: "string",
6927
+ description: "Language code (en, es, ja, ar, ko, zh, tr, fr). Default: en",
6928
+ default: "en"
6929
+ }
6930
+ },
6931
+ required: ["input"]
6932
+ }
6933
+ },
6934
+ {
6935
+ name: "parse_composite",
6936
+ description: "Parse multi-line input where each line may belong to a different domain. Skips blank and comment lines (//, --, #). Returns per-line results with domain, action, roles, and confidence.",
6937
+ inputSchema: {
6938
+ type: "object",
6939
+ properties: {
6940
+ input: {
6941
+ type: "string",
6942
+ description: "Multi-line input text. Each non-blank line is matched to the best domain."
6943
+ },
6944
+ language: {
6945
+ type: "string",
6946
+ description: "Language code (en, es, ja, ar, ko, zh, tr, fr). Default: en",
6947
+ default: "en"
6948
+ }
6949
+ },
6950
+ required: ["input"]
6951
+ }
6952
+ },
6953
+ {
6954
+ name: "compile_auto",
6955
+ description: "Auto-detect domain and compile in one shot. Returns compiled output along with the detected domain name.",
6956
+ inputSchema: {
6957
+ type: "object",
6958
+ properties: {
6959
+ input: {
6960
+ type: "string",
6961
+ description: "The natural-language input to compile"
6962
+ },
6963
+ language: {
6964
+ type: "string",
6965
+ description: "Language code (en, es, ja, ar, ko, zh, tr, fr). Default: en",
6966
+ default: "en"
6967
+ }
6968
+ },
6969
+ required: ["input"]
6970
+ }
6971
+ },
6972
+ {
6973
+ name: "compile_composite",
6974
+ description: "Compile multi-line input where each line may belong to a different domain. Lines are auto-detected and compiled via their domain. Supports natural language and explicit bracket syntax. Skips blank and comment lines (//, --, #).",
6975
+ inputSchema: {
6976
+ type: "object",
6977
+ properties: {
6978
+ input: {
6979
+ type: "string",
6980
+ description: "Multi-line input text. Each non-blank line is detected and compiled."
6981
+ },
6982
+ language: {
6983
+ type: "string",
6984
+ description: "Language code (en, es, ja, ar, ko, zh, tr, fr). Default: en",
6985
+ default: "en"
6986
+ }
6987
+ },
6988
+ required: ["input"]
6989
+ }
6990
+ }
6991
+ ];
6992
+ async function handleDispatcherTool(name, args, registry2) {
6993
+ const input = getString(args, "input");
6994
+ if (!input) return errorResponse("Missing required parameter: input");
6995
+ const language = getString(args, "language", "en");
6996
+ const dispatch = getDispatcher(registry2);
6997
+ try {
6998
+ switch (name) {
6999
+ case "detect_domain":
7000
+ return handleDetect(dispatch, input, language);
7001
+ case "parse_composite":
7002
+ return handleParseComposite(dispatch, input, language);
7003
+ case "compile_auto":
7004
+ return handleCompileAuto(dispatch, input, language);
7005
+ case "compile_composite":
7006
+ return handleCompileComposite(dispatch, input, language);
7007
+ default:
7008
+ return errorResponse(`Unknown dispatcher tool: ${name}`);
7009
+ }
7010
+ } catch (error) {
7011
+ const message = error instanceof Error ? error.message : String(error);
7012
+ return errorResponse(`Dispatcher error: ${message}`);
7013
+ }
7014
+ }
7015
+ async function handleDetect(dispatch, input, language) {
7016
+ const result = await dispatch.detect(input, language);
7017
+ if (!result) {
7018
+ return jsonResponse({
7019
+ domain: null,
7020
+ reason: "No domain matched this input above the confidence threshold",
7021
+ input,
7022
+ language
7023
+ });
7024
+ }
7025
+ return jsonResponse({
7026
+ domain: result.domain,
7027
+ action: result.node.action,
7028
+ roles: serializeRoles3(result.node.roles),
7029
+ confidence: result.confidence,
7030
+ language
7031
+ });
7032
+ }
7033
+ async function handleParseComposite(dispatch, input, language) {
7034
+ const result = await dispatch.parseComposite(input, language);
7035
+ return jsonResponse({
7036
+ statements: result.statements.map((s) => ({
7037
+ line: s.line,
7038
+ input: s.input,
7039
+ domain: s.domain,
7040
+ action: s.node.action,
7041
+ roles: serializeRoles3(s.node.roles),
7042
+ confidence: s.confidence
7043
+ })),
7044
+ errors: result.errors,
7045
+ language,
7046
+ summary: {
7047
+ matched: result.statements.length,
7048
+ unmatched: result.errors.length,
7049
+ domains: [...new Set(result.statements.map((s) => s.domain))]
7050
+ }
7051
+ });
7052
+ }
7053
+ async function handleCompileComposite(dispatch, input, language) {
7054
+ const result = await dispatch.compileComposite(input, language);
7055
+ return jsonResponse({
7056
+ statements: result.statements.map((s) => ({
7057
+ line: s.line,
7058
+ input: s.input,
7059
+ domain: s.domain,
7060
+ ok: s.ok,
7061
+ code: s.code ?? null,
7062
+ errors: s.errors ?? []
7063
+ })),
7064
+ errors: result.errors,
7065
+ language,
7066
+ summary: {
7067
+ compiled: result.statements.length,
7068
+ succeeded: result.statements.filter((s) => s.ok).length,
7069
+ failed: result.statements.filter((s) => !s.ok).length,
7070
+ unmatched: result.errors.length,
7071
+ domains: [...new Set(result.statements.map((s) => s.domain))]
7072
+ }
7073
+ });
7074
+ }
7075
+ async function handleCompileAuto(dispatch, input, language) {
7076
+ const result = await dispatch.compile(input, language);
7077
+ if (!result) {
7078
+ return jsonResponse({
7079
+ domain: null,
7080
+ ok: false,
7081
+ errors: ["No domain matched this input"],
7082
+ input,
7083
+ language
7084
+ });
7085
+ }
7086
+ return jsonResponse({
7087
+ domain: result.domain,
7088
+ ok: result.ok,
7089
+ code: result.code ?? null,
7090
+ errors: result.errors ?? [],
7091
+ language
7092
+ });
7093
+ }
7094
+
7095
+ // src/tools/ir-tools.ts
7096
+ import {
7097
+ parseExplicit,
7098
+ renderExplicit,
7099
+ fromProtocolJSON,
7100
+ toProtocolJSON,
7101
+ validateProtocolJSON,
7102
+ toEnvelopeJSON,
7103
+ fromEnvelopeJSON,
7104
+ isEnvelope,
7105
+ fromInterchangeNode
7106
+ } from "@lokascript/framework";
7107
+ var irTools = [
7108
+ {
7109
+ name: "convert_format",
7110
+ description: "Convert between explicit bracket syntax, LLM JSON, and InterchangeNode AST without compilation. Provide `explicit` (bracket syntax), `semantic` (JSON), or `interchange` (InterchangeNode AST). Returns both explicit and protocol JSON formats for bidirectional use. Tip: use lse_from_hyperscript to parse natural hyperscript into LSE first, then pipe its output here.",
7111
+ inputSchema: {
7112
+ type: "object",
7113
+ properties: {
7114
+ explicit: {
7115
+ type: "string",
7116
+ description: "Explicit syntax to convert: [command role:value ...]"
7117
+ },
7118
+ interchange: {
7119
+ type: "object",
7120
+ description: 'InterchangeNode AST object (from core or semantic parser). Must have a `type` discriminant field (e.g., "command", "event", "if", "repeat", etc.).',
7121
+ properties: {
7122
+ type: { type: "string", description: "Node type discriminant" }
7123
+ }
7124
+ },
7125
+ semantic: {
7126
+ type: "object",
7127
+ description: 'Protocol JSON to convert. `kind` is optional (defaults to "command"). `trigger` is convenience sugar that wraps a command in an event handler. Full form: { kind?, action, roles, trigger?, body?, statements?, chainType?, thenBranch?, elseBranch?, loopVariant?, loopBody?, loopVariable?, indexVariable?, catchBranch?, finallyBranch?, asyncVariant?, asyncBody?, arms?, defaultArm?, annotations?, diagnostics? }. Envelope: { lseVersion, nodes[], features? }.',
7128
+ properties: {
7129
+ kind: {
7130
+ type: "string",
7131
+ enum: ["command", "event-handler", "compound"],
7132
+ description: 'Node kind (optional, defaults to "command")'
7133
+ },
7134
+ action: { type: "string" },
7135
+ roles: { type: "object" },
7136
+ trigger: {
7137
+ type: "object",
7138
+ description: "Convenience sugar: wraps command in event handler. { event: string, modifiers?: object }"
7139
+ },
7140
+ body: {
7141
+ type: "array",
7142
+ description: "Event handler body commands (event-handler nodes)"
7143
+ },
7144
+ statements: {
7145
+ type: "array",
7146
+ description: "Chained commands (compound nodes)"
7147
+ },
7148
+ chainType: {
7149
+ type: "string",
7150
+ enum: ["then", "and", "async", "sequential", "pipe"],
7151
+ description: "Chain operator (compound nodes)"
7152
+ },
7153
+ thenBranch: {
7154
+ type: "array",
7155
+ description: "Then-branch commands (v1.1 conditional)"
7156
+ },
7157
+ elseBranch: {
7158
+ type: "array",
7159
+ description: "Else-branch commands (v1.1 conditional)"
7160
+ },
7161
+ loopVariant: {
7162
+ type: "string",
7163
+ enum: ["forever", "times", "for", "while", "until"],
7164
+ description: "Loop variant (v1.1 loop)"
7165
+ },
7166
+ loopBody: {
7167
+ type: "array",
7168
+ description: "Loop body commands (v1.1 loop)"
7169
+ },
7170
+ loopVariable: {
7171
+ type: "string",
7172
+ description: "Loop iteration variable name (v1.1 loop)"
7173
+ },
7174
+ indexVariable: {
7175
+ type: "string",
7176
+ description: "Optional loop index variable name (v1.1 loop)"
7177
+ },
7178
+ catchBranch: {
7179
+ type: "array",
7180
+ description: "Catch branch commands (v1.2 try/catch)"
7181
+ },
7182
+ finallyBranch: {
7183
+ type: "array",
7184
+ description: "Finally branch commands (v1.2 try/finally)"
7185
+ },
7186
+ asyncVariant: {
7187
+ type: "string",
7188
+ enum: ["all", "race"],
7189
+ description: "Async coordination variant (v1.2)"
7190
+ },
7191
+ asyncBody: {
7192
+ type: "array",
7193
+ description: "Async body commands (v1.2)"
7194
+ },
7195
+ arms: {
7196
+ type: "array",
7197
+ description: "Match arms \u2014 array of { pattern, body } (v1.2)"
7198
+ },
7199
+ defaultArm: {
7200
+ type: "array",
7201
+ description: "Default match arm commands (v1.2)"
7202
+ },
7203
+ annotations: {
7204
+ type: "array",
7205
+ description: "Metadata annotations \u2014 array of { name, value? } (v1.2)"
7206
+ },
7207
+ diagnostics: {
7208
+ type: "array",
7209
+ description: "Type constraint diagnostics \u2014 array of { level, role, message, code } (v1.2)"
7210
+ }
7211
+ }
7212
+ }
7213
+ }
7214
+ }
7215
+ },
7216
+ {
7217
+ name: "validate_explicit",
7218
+ description: "Validate explicit bracket syntax like `[toggle patient:.active]` without full compilation. Faster than validate_and_compile. Use ONLY for bracket syntax input, not natural language hyperscript.",
7219
+ inputSchema: {
7220
+ type: "object",
7221
+ properties: {
7222
+ explicit: {
7223
+ type: "string",
7224
+ description: "Explicit syntax to validate: [command role:value ...]"
7225
+ }
7226
+ },
7227
+ required: ["explicit"]
7228
+ }
7229
+ },
7230
+ {
7231
+ name: "validate_protocol",
7232
+ description: "Validate a protocol JSON node (v1.1/v1.2). Returns diagnostics for any structural issues.",
7233
+ inputSchema: {
7234
+ type: "object",
7235
+ properties: {
7236
+ json: {
7237
+ type: "object",
7238
+ description: "Protocol JSON node to validate (must have `kind` field)"
7239
+ }
7240
+ },
7241
+ required: ["json"]
7242
+ }
7243
+ },
7244
+ {
7245
+ name: "to_envelope",
7246
+ description: "Wrap one or more protocol JSON nodes into an LSE v1.2 envelope with version metadata and feature flags.",
7247
+ inputSchema: {
7248
+ type: "object",
7249
+ properties: {
7250
+ nodes: {
7251
+ type: "array",
7252
+ description: "Array of protocol JSON nodes (or explicit bracket strings to parse first)"
7253
+ },
7254
+ version: {
7255
+ type: "string",
7256
+ description: 'LSE version string (default: "1.2")'
7257
+ }
7258
+ },
7259
+ required: ["nodes"]
7260
+ }
7261
+ },
7262
+ {
7263
+ name: "from_envelope",
7264
+ description: "Unwrap an LSE v1.2 envelope into its constituent semantic nodes and metadata.",
7265
+ inputSchema: {
7266
+ type: "object",
7267
+ properties: {
7268
+ envelope: {
7269
+ type: "object",
7270
+ description: "LSE envelope JSON: { lseVersion, nodes[], features? }"
7271
+ }
7272
+ },
7273
+ required: ["envelope"]
7274
+ }
7275
+ }
7276
+ ];
7277
+ async function handleIRTool(name, args) {
7278
+ try {
7279
+ switch (name) {
7280
+ case "convert_format":
7281
+ return handleConvertFormat(args);
7282
+ case "validate_explicit":
7283
+ return handleValidateExplicit(args);
7284
+ case "validate_protocol":
7285
+ return handleValidateProtocol(args);
7286
+ case "to_envelope":
7287
+ return handleToEnvelope(args);
7288
+ case "from_envelope":
7289
+ return handleFromEnvelope(args);
7290
+ default:
7291
+ return errorResponse(`Unknown IR tool: ${name}`);
7292
+ }
7293
+ } catch (error) {
7294
+ const message = error instanceof Error ? error.message : String(error);
7295
+ return errorResponse(`IR tool error: ${message}`);
7296
+ }
7297
+ }
7298
+ function handleConvertFormat(args) {
7299
+ const explicitInput = typeof args.explicit === "string" ? args.explicit : void 0;
7300
+ const interchangeInput = typeof args.interchange === "object" && args.interchange ? args.interchange : void 0;
7301
+ const semanticInput = typeof args.semantic === "object" && args.semantic ? args.semantic : void 0;
7302
+ if (!explicitInput && !interchangeInput && !semanticInput) {
7303
+ return errorResponse(
7304
+ "Provide `explicit` (bracket syntax), `interchange` (InterchangeNode AST), or `semantic` (JSON)."
7305
+ );
7306
+ }
7307
+ if (explicitInput) {
7308
+ const node2 = parseExplicit(explicitInput);
7309
+ return jsonResponse({
7310
+ ok: true,
7311
+ explicit: renderExplicit(node2),
7312
+ semantic: toProtocolJSON(node2)
7313
+ });
7314
+ }
7315
+ if (interchangeInput) {
7316
+ const input2 = interchangeInput;
7317
+ if (!input2.type || typeof input2.type !== "string") {
7318
+ return errorResponse("InterchangeNode must have a `type` string field.");
7319
+ }
7320
+ const node2 = fromInterchangeNode(input2);
7321
+ return jsonResponse({
7322
+ ok: true,
7323
+ explicit: renderExplicit(node2),
7324
+ semantic: toProtocolJSON(node2)
7325
+ });
7326
+ }
7327
+ const input = semanticInput;
7328
+ if (isEnvelope(input)) {
7329
+ const envelope = fromEnvelopeJSON(input);
7330
+ const results = envelope.nodes.map((node2) => ({
7331
+ explicit: renderExplicit(node2),
7332
+ semantic: toProtocolJSON(node2)
7333
+ }));
7334
+ return jsonResponse({
7335
+ ok: true,
7336
+ envelope: { lseVersion: envelope.lseVersion, features: envelope.features },
7337
+ nodes: results
7338
+ });
7339
+ }
7340
+ const diagnostics = validateProtocolJSON(input);
7341
+ const errors = diagnostics.filter((d) => d.severity === "error");
7342
+ if (errors.length > 0) {
7343
+ return jsonResponse({ ok: false, diagnostics: errors });
7344
+ }
7345
+ const node = fromProtocolJSON(input);
7346
+ return jsonResponse({
7347
+ ok: true,
7348
+ explicit: renderExplicit(node),
7349
+ semantic: toProtocolJSON(node)
7350
+ });
7351
+ }
7352
+ function handleValidateExplicit(args) {
7353
+ const explicit = typeof args.explicit === "string" ? args.explicit : void 0;
7354
+ if (!explicit) {
7355
+ return errorResponse("Missing required parameter: explicit");
7356
+ }
7357
+ try {
7358
+ const node = parseExplicit(explicit);
7359
+ const json = toProtocolJSON(node);
7360
+ return jsonResponse({
7361
+ ok: true,
7362
+ semantic: json,
7363
+ diagnostics: []
7364
+ });
7365
+ } catch (error) {
7366
+ const message = error instanceof Error ? error.message : String(error);
7367
+ return jsonResponse({
7368
+ ok: false,
7369
+ diagnostics: [
7370
+ {
7371
+ severity: "error",
7372
+ code: "EXPLICIT_PARSE_ERROR",
7373
+ message
7374
+ }
7375
+ ]
7376
+ });
7377
+ }
7378
+ }
7379
+ function handleValidateProtocol(args) {
7380
+ const json = typeof args.json === "object" && args.json ? args.json : void 0;
7381
+ if (!json) {
7382
+ return errorResponse("Missing required parameter: json");
7383
+ }
7384
+ const diagnostics = validateProtocolJSON(json);
7385
+ const errors = diagnostics.filter((d) => d.severity === "error");
7386
+ const warnings = diagnostics.filter((d) => d.severity === "warning");
7387
+ return jsonResponse({
7388
+ ok: errors.length === 0,
7389
+ errors,
7390
+ warnings
7391
+ });
7392
+ }
7393
+ function handleToEnvelope(args) {
7394
+ const nodes = Array.isArray(args.nodes) ? args.nodes : void 0;
7395
+ if (!nodes || nodes.length === 0) {
7396
+ return errorResponse("Missing required parameter: nodes (non-empty array)");
7397
+ }
7398
+ const semanticNodes = nodes.map((n, i) => {
7399
+ if (typeof n === "string") {
7400
+ return parseExplicit(n);
7401
+ }
7402
+ if (typeof n === "object" && n) {
7403
+ const diags = validateProtocolJSON(n);
7404
+ const errs = diags.filter((d) => d.severity === "error");
7405
+ if (errs.length > 0) {
7406
+ throw new Error(`Node ${i}: ${errs.map((e) => e.message).join("; ")}`);
7407
+ }
7408
+ return fromProtocolJSON(n);
7409
+ }
7410
+ throw new Error(`Node ${i}: must be a string (explicit) or object (protocol JSON)`);
7411
+ });
7412
+ const version = typeof args.version === "string" ? args.version : "1.2";
7413
+ const envelope = toEnvelopeJSON({ lseVersion: version, nodes: semanticNodes });
7414
+ return jsonResponse({
7415
+ ok: true,
7416
+ envelope
7417
+ });
7418
+ }
7419
+ function handleFromEnvelope(args) {
7420
+ const envelope = typeof args.envelope === "object" && args.envelope ? args.envelope : void 0;
7421
+ if (!envelope) {
7422
+ return errorResponse("Missing required parameter: envelope");
7423
+ }
7424
+ if (!isEnvelope(envelope)) {
7425
+ return errorResponse("Invalid envelope: must have `lseVersion` and `nodes` fields");
7426
+ }
7427
+ const result = fromEnvelopeJSON(envelope);
7428
+ const nodes = result.nodes.map((node) => ({
7429
+ explicit: renderExplicit(node),
7430
+ semantic: toProtocolJSON(node)
7431
+ }));
7432
+ return jsonResponse({
7433
+ ok: true,
7434
+ lseVersion: result.lseVersion,
7435
+ features: result.features,
7436
+ nodes
7437
+ });
7438
+ }
7439
+
7440
+ // src/tools/debug-tools.ts
7441
+ var debugTools = [
7442
+ {
7443
+ name: "debug_analyze_snapshot",
7444
+ description: "Analyze a HyperFixi debug snapshot. Given the current command, element, variables, and execution depth, explains what the command does, what state is available, and predicts what will happen next. Use when a developer is paused in the debugger and wants to understand the current state.",
7445
+ inputSchema: {
7446
+ type: "object",
7447
+ properties: {
7448
+ snapshot: {
7449
+ type: "object",
7450
+ description: "A DebugSnapshot object: { commandName, element, variables, timestamp, depth, index }",
7451
+ properties: {
7452
+ commandName: {
7453
+ type: "string",
7454
+ description: "The hyperscript command about to execute"
7455
+ },
7456
+ element: {
7457
+ type: "string",
7458
+ description: 'Description of the target element, e.g. "<button#submit.primary>"'
7459
+ },
7460
+ variables: {
7461
+ type: "object",
7462
+ description: "Map of variable names to values (it, result, me, :locals, event.type, etc.)"
7463
+ },
7464
+ depth: { type: "number", description: "Nesting depth (0 = top-level)" },
7465
+ index: { type: "number", description: "Sequential command index" }
7466
+ },
7467
+ required: ["commandName"]
7468
+ }
7469
+ },
7470
+ required: ["snapshot"]
7471
+ }
7472
+ },
7473
+ {
7474
+ name: "debug_explain_handler",
7475
+ description: "Explain a hyperscript event handler step-by-step. Given the full handler code, breaks down what each command does, identifies potential issues (null references, missing elements, type mismatches), and suggests where to set breakpoints for debugging.",
7476
+ inputSchema: {
7477
+ type: "object",
7478
+ properties: {
7479
+ code: {
7480
+ type: "string",
7481
+ description: 'The full hyperscript event handler code (e.g., "on click toggle .active on me then add .clicked")'
7482
+ },
7483
+ issue: {
7484
+ type: "string",
7485
+ description: 'Optional description of the problem being debugged (e.g., "the class is not toggling")'
7486
+ }
7487
+ },
7488
+ required: ["code"]
7489
+ }
7490
+ },
7491
+ {
7492
+ name: "debug_suggest_fix",
7493
+ description: "Given a debug snapshot where something went wrong (unexpected variable value, wrong element, error), analyze the state and suggest likely causes and fixes. Combines debug state analysis with hyperscript diagnostics.",
7494
+ inputSchema: {
7495
+ type: "object",
7496
+ properties: {
7497
+ snapshot: {
7498
+ type: "object",
7499
+ description: "The DebugSnapshot at the point of failure",
7500
+ properties: {
7501
+ commandName: { type: "string" },
7502
+ element: { type: "string" },
7503
+ variables: { type: "object" },
7504
+ depth: { type: "number" },
7505
+ index: { type: "number" }
7506
+ },
7507
+ required: ["commandName"]
7508
+ },
7509
+ expected: {
7510
+ type: "string",
7511
+ description: 'What the developer expected to happen (e.g., "it should be 5 but it is null")'
7512
+ },
7513
+ error: {
7514
+ type: "string",
7515
+ description: "Error message if an exception occurred"
7516
+ }
7517
+ },
7518
+ required: ["snapshot"]
7519
+ }
7520
+ },
7521
+ {
7522
+ name: "debug_trace_variable",
7523
+ description: "Given an array of debug snapshots (execution history) and a variable name, trace how that variable changed over time. Shows each step where the variable was modified, its old and new values, and which command caused the change.",
7524
+ inputSchema: {
7525
+ type: "object",
7526
+ properties: {
7527
+ history: {
7528
+ type: "array",
7529
+ description: "Array of DebugSnapshot objects from the execution history",
7530
+ items: {
7531
+ type: "object",
7532
+ properties: {
7533
+ commandName: { type: "string" },
7534
+ variables: { type: "object" },
7535
+ index: { type: "number" }
7536
+ }
7537
+ }
7538
+ },
7539
+ variable: {
7540
+ type: "string",
7541
+ description: 'The variable name to trace (e.g., "it", ":count", "me")'
7542
+ }
7543
+ },
7544
+ required: ["history", "variable"]
7545
+ }
7546
+ }
7547
+ ];
7548
+ async function handleDebugTool(name, args) {
7549
+ try {
7550
+ switch (name) {
7551
+ case "debug_analyze_snapshot":
7552
+ return analyzeSnapshot(args.snapshot);
7553
+ case "debug_explain_handler":
7554
+ return explainHandler(args.code, args.issue);
7555
+ case "debug_suggest_fix":
7556
+ return suggestFix(
7557
+ args.snapshot,
7558
+ args.expected,
7559
+ args.error
7560
+ );
7561
+ case "debug_trace_variable":
7562
+ return traceVariable(
7563
+ args.history,
7564
+ args.variable
7565
+ );
7566
+ default:
7567
+ return {
7568
+ content: [{ type: "text", text: `Unknown debug tool: ${name}` }],
7569
+ isError: true
7570
+ };
7571
+ }
7572
+ } catch (err) {
7573
+ return {
7574
+ content: [
7575
+ {
7576
+ type: "text",
7577
+ text: `Debug tool error: ${err instanceof Error ? err.message : String(err)}`
7578
+ }
7579
+ ],
7580
+ isError: true
7581
+ };
7582
+ }
7583
+ }
7584
+ function analyzeSnapshot(snapshot) {
7585
+ const cmd = snapshot.commandName;
7586
+ const element = snapshot.element;
7587
+ const variables = snapshot.variables || {};
7588
+ const depth = snapshot.depth || 0;
7589
+ const index = snapshot.index || 0;
7590
+ const analysis = [];
7591
+ analysis.push(`## Debug Snapshot Analysis (Step ${index})`);
7592
+ analysis.push("");
7593
+ analysis.push(`**Current Command:** \`${cmd}\``);
7594
+ analysis.push(
7595
+ `**Nesting Depth:** ${depth} ${depth === 0 ? "(top-level)" : `(${depth} level${depth > 1 ? "s" : ""} deep)`}`
7596
+ );
7597
+ if (element) {
7598
+ analysis.push(`**Target Element:** \`${element}\``);
7599
+ }
7600
+ const insight = getCommandInsight(cmd);
7601
+ if (insight) {
7602
+ analysis.push("");
7603
+ analysis.push(`**What this command does:** ${insight}`);
7604
+ }
7605
+ const varEntries = Object.entries(variables);
7606
+ if (varEntries.length > 0) {
7607
+ analysis.push("");
7608
+ analysis.push("**Available State:**");
7609
+ for (const [name, value] of varEntries) {
7610
+ analysis.push(`- \`${name}\` = \`${formatVal(value)}\``);
7611
+ }
7612
+ }
7613
+ const warnings = detectIssues(cmd, element, variables);
7614
+ if (warnings.length > 0) {
7615
+ analysis.push("");
7616
+ analysis.push("**Potential Issues:**");
7617
+ for (const w of warnings) {
7618
+ analysis.push(`- ${w}`);
7619
+ }
7620
+ }
7621
+ return { content: [{ type: "text", text: analysis.join("\n") }] };
7622
+ }
7623
+ function explainHandler(code, issue) {
7624
+ if (!code) {
7625
+ return {
7626
+ content: [{ type: "text", text: "Error: code parameter is required" }],
7627
+ isError: true
7628
+ };
7629
+ }
7630
+ const lines = [];
7631
+ lines.push("## Event Handler Analysis");
7632
+ lines.push("");
7633
+ lines.push("```hyperscript");
7634
+ lines.push(code);
7635
+ lines.push("```");
7636
+ lines.push("");
7637
+ const commands = code.replace(/^on\s+\w+\s*/, "").split(/\s+then\s+/).map((c) => c.trim()).filter(Boolean);
7638
+ lines.push("**Step-by-step breakdown:**");
7639
+ commands.forEach((cmd, i) => {
7640
+ lines.push(`${i + 1}. \`${cmd}\``);
7641
+ const insight = getCommandInsightFromCode(cmd);
7642
+ if (insight) lines.push(` ${insight}`);
7643
+ });
7644
+ lines.push("");
7645
+ lines.push("**Suggested breakpoints:**");
7646
+ const bpSuggestions = suggestBreakpoints(commands, issue);
7647
+ for (const bp of bpSuggestions) {
7648
+ lines.push(`- ${bp}`);
7649
+ }
7650
+ if (issue) {
7651
+ lines.push("");
7652
+ lines.push(`**Regarding "${issue}":**`);
7653
+ lines.push(diagnoseIssue(commands, issue));
7654
+ }
7655
+ return { content: [{ type: "text", text: lines.join("\n") }] };
7656
+ }
7657
+ function suggestFix(snapshot, expected, error) {
7658
+ const cmd = snapshot.commandName;
7659
+ const element = snapshot.element;
7660
+ const variables = snapshot.variables || {};
7661
+ const lines = [];
7662
+ lines.push("## Debug Fix Suggestions");
7663
+ lines.push("");
7664
+ lines.push(`**Failed at:** \`${cmd}\``);
7665
+ if (error) {
7666
+ lines.push(`**Error:** ${error}`);
7667
+ lines.push("");
7668
+ lines.push("**Likely causes:**");
7669
+ const causes = diagnosError(cmd, error, variables);
7670
+ for (const c of causes) {
7671
+ lines.push(`- ${c}`);
7672
+ }
7673
+ }
7674
+ if (expected) {
7675
+ lines.push("");
7676
+ lines.push(`**Expected:** ${expected}`);
7677
+ lines.push("");
7678
+ lines.push("**Analysis:**");
7679
+ if (variables["it"] === null || variables["it"] === void 0) {
7680
+ lines.push("- `it` is null/undefined. The previous command may not have returned a value.");
7681
+ lines.push(" Check if a `get` or `fetch` command ran successfully before this step.");
7682
+ }
7683
+ if (!element) {
7684
+ lines.push("- No target element. The CSS selector may not match any element on the page.");
7685
+ }
7686
+ }
7687
+ lines.push("");
7688
+ lines.push("**Suggested fixes:**");
7689
+ const fixes = generateFixes(cmd, element, variables, error);
7690
+ for (const f of fixes) {
7691
+ lines.push(`- ${f}`);
7692
+ }
7693
+ return { content: [{ type: "text", text: lines.join("\n") }] };
7694
+ }
7695
+ function traceVariable(history, variable) {
7696
+ if (!history || !Array.isArray(history) || !variable) {
7697
+ return {
7698
+ content: [{ type: "text", text: "Error: history array and variable name are required" }],
7699
+ isError: true
7700
+ };
7701
+ }
7702
+ const lines = [];
7703
+ lines.push(`## Variable Trace: \`${variable}\``);
7704
+ lines.push("");
7705
+ let prevValue = void 0;
7706
+ const changes = [];
7707
+ for (const snap of history) {
7708
+ const vars = snap.variables || {};
7709
+ const currentValue = vars[variable];
7710
+ const index = snap.index;
7711
+ const cmd = snap.commandName;
7712
+ if (currentValue !== prevValue) {
7713
+ changes.push({
7714
+ index,
7715
+ cmd,
7716
+ oldVal: prevValue,
7717
+ newVal: currentValue
7718
+ });
7719
+ prevValue = currentValue;
7720
+ }
7721
+ }
7722
+ if (changes.length === 0) {
7723
+ lines.push(`Variable \`${variable}\` was not found in any snapshot, or never changed.`);
7724
+ } else {
7725
+ lines.push(`| Step | Command | Old Value | New Value |`);
7726
+ lines.push(`|------|---------|-----------|-----------|`);
7727
+ for (const c of changes) {
7728
+ lines.push(
7729
+ `| ${c.index} | \`${c.cmd}\` | \`${formatVal(c.oldVal)}\` | \`${formatVal(c.newVal)}\` |`
7730
+ );
7731
+ }
7732
+ lines.push("");
7733
+ lines.push(`**Total changes:** ${changes.length}`);
7734
+ lines.push(`**Final value:** \`${formatVal(prevValue)}\``);
7735
+ }
7736
+ return { content: [{ type: "text", text: lines.join("\n") }] };
7737
+ }
7738
+ function formatVal(v) {
7739
+ if (v === void 0) return "undefined";
7740
+ if (v === null) return "null";
7741
+ if (typeof v === "string") return v.length > 40 ? `"${v.slice(0, 37)}..."` : `"${v}"`;
7742
+ return String(v);
7743
+ }
7744
+ var COMMAND_INSIGHTS = {
7745
+ toggle: "Toggles a CSS class on the target element. If the class is present, it removes it; if absent, it adds it.",
7746
+ add: "Adds a CSS class or attribute to the target element.",
7747
+ remove: "Removes a CSS class, attribute, or the element itself from the DOM.",
7748
+ set: "Sets a variable or property to a value.",
7749
+ get: "Evaluates an expression and stores the result in `it`.",
7750
+ put: "Places content into an element (similar to setting innerHTML or textContent).",
7751
+ show: "Makes a hidden element visible (removes display:none or similar).",
7752
+ hide: "Hides an element (sets display:none or similar).",
7753
+ fetch: "Makes an HTTP request and stores the response in `it`.",
7754
+ wait: "Pauses execution for a specified duration.",
7755
+ trigger: "Dispatches a custom event on an element.",
7756
+ send: "Sends a custom event to a target element.",
7757
+ log: "Logs a value to the browser console.",
7758
+ call: "Calls a JavaScript function.",
7759
+ if: "Conditional branch \u2014 executes commands only if the condition is true.",
7760
+ repeat: "Loops over commands a specified number of times or for each item.",
7761
+ increment: "Increases a numeric value by 1 (or a specified amount).",
7762
+ decrement: "Decreases a numeric value by 1 (or a specified amount).",
7763
+ append: "Appends content to an element (adds to end, does not replace).",
7764
+ go: "Navigates to a URL."
7765
+ };
7766
+ function getCommandInsight(cmd) {
7767
+ return COMMAND_INSIGHTS[cmd] || null;
7768
+ }
7769
+ function getCommandInsightFromCode(code) {
7770
+ const firstWord = code.trim().split(/\s+/)[0].toLowerCase();
7771
+ return COMMAND_INSIGHTS[firstWord] || null;
7772
+ }
7773
+ function detectIssues(cmd, element, variables) {
7774
+ const issues = [];
7775
+ if (["toggle", "add", "remove", "show", "hide"].includes(cmd) && !element) {
7776
+ issues.push("No target element \u2014 DOM commands need an element to act on.");
7777
+ }
7778
+ if (["put", "set"].includes(cmd) && variables["it"] === null) {
7779
+ issues.push(
7780
+ "`it` is null \u2014 if using `it` as the value to set/put, this may produce unexpected results."
7781
+ );
7782
+ }
7783
+ if (cmd === "fetch" && !element) {
7784
+ issues.push("fetch command detected \u2014 ensure the URL is correct and the server is running.");
7785
+ }
7786
+ return issues;
7787
+ }
7788
+ function suggestBreakpoints(commands, issue) {
7789
+ const suggestions = [];
7790
+ if (commands.length > 1) {
7791
+ suggestions.push(
7792
+ `Set breakpoint on command \`${commands[0].split(/\s+/)[0]}\` to verify the first step executes correctly.`
7793
+ );
7794
+ }
7795
+ const fetchIdx = commands.findIndex((c) => c.trim().startsWith("fetch"));
7796
+ if (fetchIdx >= 0) {
7797
+ suggestions.push(
7798
+ `Set breakpoint after \`fetch\` (step ${fetchIdx + 1}) to inspect the response in \`it\`.`
7799
+ );
7800
+ }
7801
+ const ifIdx = commands.findIndex((c) => c.trim().startsWith("if"));
7802
+ if (ifIdx >= 0) {
7803
+ suggestions.push(
7804
+ `Set breakpoint on \`if\` (step ${ifIdx + 1}) to inspect the condition value.`
7805
+ );
7806
+ }
7807
+ if (issue && suggestions.length === 0) {
7808
+ suggestions.push(
7809
+ "Set breakpoint on the first command and step through to find where behavior diverges from expectations."
7810
+ );
7811
+ }
7812
+ return suggestions.length > 0 ? suggestions : ["Step through from the beginning with F10 (Step Over)."];
7813
+ }
7814
+ function diagnoseIssue(commands, issue) {
7815
+ const lowerIssue = issue.toLowerCase();
7816
+ if (lowerIssue.includes("not toggling") || lowerIssue.includes("class")) {
7817
+ return "Check that the CSS selector in `toggle` matches the intended element. Use the element highlighter in the debug overlay to verify which element is being targeted.";
7818
+ }
7819
+ if (lowerIssue.includes("null") || lowerIssue.includes("undefined")) {
7820
+ return "A variable is unexpectedly null. Step through each command and check `it` after each step \u2014 a command may be failing silently.";
7821
+ }
7822
+ if (lowerIssue.includes("fetch") || lowerIssue.includes("network") || lowerIssue.includes("api")) {
7823
+ return "The fetch command may be failing. Set a breakpoint after `fetch` and check `it` for the response. Also check the browser Network tab for HTTP errors.";
7824
+ }
7825
+ return "Set a breakpoint on the first command and use Step Over (F10) to execute one command at a time, checking variables after each step.";
7826
+ }
7827
+ function diagnosError(cmd, error, variables) {
7828
+ const causes = [];
7829
+ const lowerError = error.toLowerCase();
7830
+ if (lowerError.includes("null") || lowerError.includes("undefined")) {
7831
+ causes.push(
7832
+ "A variable or element reference is null. Check that selectors match existing elements."
7833
+ );
7834
+ if (variables["me"] === null || variables["me"] === void 0) {
7835
+ causes.push("`me` is null \u2014 the command may not have a valid context element.");
7836
+ }
7837
+ }
7838
+ if (lowerError.includes("not a function")) {
7839
+ causes.push(
7840
+ "Attempting to call something that is not a function. Check that the method or function name is spelled correctly."
7841
+ );
7842
+ }
7843
+ if (lowerError.includes("network") || lowerError.includes("fetch")) {
7844
+ causes.push(
7845
+ "Network request failed. Check the URL, CORS settings, and that the server is running."
7846
+ );
7847
+ }
7848
+ if (causes.length === 0) {
7849
+ causes.push(
7850
+ `The \`${cmd}\` command threw an error. Check the command syntax and that all referenced elements/variables exist.`
7851
+ );
7852
+ }
7853
+ return causes;
7854
+ }
7855
+ function generateFixes(cmd, element, variables, error) {
7856
+ const fixes = [];
7857
+ if (!element && ["toggle", "add", "remove"].includes(cmd)) {
7858
+ fixes.push(
7859
+ "Add a target element: e.g., `toggle .active on me` or `toggle .active on #myElement`"
7860
+ );
7861
+ }
7862
+ if (variables["it"] === null && ["put", "set"].includes(cmd)) {
7863
+ fixes.push(
7864
+ "Ensure a previous command sets `it` before using it. Try adding `get ...` or `set ... to ...` before this command."
7865
+ );
7866
+ }
7867
+ if (error && error.includes("not found")) {
7868
+ fixes.push(
7869
+ "Verify the CSS selector matches an element in the DOM. Use browser DevTools Elements panel to check."
7870
+ );
7871
+ }
7872
+ if (fixes.length === 0) {
7873
+ fixes.push(
7874
+ "Use the debug overlay to step through execution and identify the exact point where behavior diverges."
7875
+ );
7876
+ fixes.push("Check the browser console for additional error details.");
7877
+ }
7878
+ return fixes;
7879
+ }
7880
+
7881
+ // src/tools/inventory.ts
7882
+ var extractSnippetsFromProjectImpl = null;
7883
+ async function getExtractor() {
7884
+ if (extractSnippetsFromProjectImpl) return extractSnippetsFromProjectImpl;
7885
+ try {
7886
+ const mod = await import("@hyperfixi/developer-tools");
7887
+ if (mod?.extractSnippetsFromProject) {
7888
+ extractSnippetsFromProjectImpl = mod.extractSnippetsFromProject;
7889
+ } else {
7890
+ extractSnippetsFromProjectImpl = createInlineExtractor();
7891
+ }
7892
+ return extractSnippetsFromProjectImpl;
7893
+ } catch {
7894
+ extractSnippetsFromProjectImpl = createInlineExtractor();
7895
+ return extractSnippetsFromProjectImpl;
7896
+ }
7897
+ }
7898
+ var cachedSnippets = [];
7899
+ var cachedProjectDir = "";
7900
+ var inventoryTools = [
7901
+ {
7902
+ name: "scan_inventory",
7903
+ description: "Scan a project directory for all hyperscript (_=), htmx (hx-*), and fixi (fx-*) attributes across HTML templates. Returns a summary with snippet counts by category, command, event, and element type, plus the full snippet list. Use this to understand how a project uses hyperscript and htmx.",
7904
+ inputSchema: {
7905
+ type: "object",
7906
+ properties: {
7907
+ directory: {
7908
+ type: "string",
7909
+ description: 'Absolute path to the project directory to scan (e.g., "/path/to/project/templates")'
7910
+ },
7911
+ category: {
7912
+ type: "string",
7913
+ description: 'Filter by category: "hyperscript", "htmx-request", "htmx-swap", "htmx-target", "htmx-trigger", "htmx-other", "fixi". Omit for all.'
7914
+ },
7915
+ limit: {
7916
+ type: "number",
7917
+ description: "Maximum number of snippets to return (default: 50). Use 0 for summary only."
7918
+ }
7919
+ },
7920
+ required: ["directory"]
7921
+ }
7922
+ },
7923
+ {
7924
+ name: "query_inventory",
7925
+ description: "Search and filter within previously scanned inventory results. Must call scan_inventory first. Supports filtering by text search, category, command, event, file path, and element type.",
7926
+ inputSchema: {
7927
+ type: "object",
7928
+ properties: {
7929
+ search: {
7930
+ type: "string",
7931
+ description: "Text search across snippet values, file paths, attribute names, commands, and events"
7932
+ },
7933
+ category: {
7934
+ type: "string",
7935
+ description: "Filter by snippet category"
7936
+ },
7937
+ command: {
7938
+ type: "string",
7939
+ description: 'Filter by hyperscript command (e.g., "toggle", "add", "fetch")'
7940
+ },
7941
+ event: {
7942
+ type: "string",
7943
+ description: 'Filter by event name (e.g., "click", "load", "change")'
7944
+ },
7945
+ file: {
7946
+ type: "string",
7947
+ description: "Filter by file path (substring match)"
7948
+ },
7949
+ element: {
7950
+ type: "string",
7951
+ description: 'Filter by HTML element type (e.g., "button", "form", "div")'
7952
+ },
7953
+ limit: {
7954
+ type: "number",
7955
+ description: "Maximum number of snippets to return (default: 30)"
7956
+ }
7957
+ }
7958
+ }
7959
+ }
7960
+ ];
7961
+ async function handleInventoryTool(name, args) {
7962
+ try {
7963
+ switch (name) {
7964
+ case "scan_inventory":
7965
+ return await scanInventory(args);
7966
+ case "query_inventory":
7967
+ return queryInventory(args);
7968
+ default:
7969
+ return errorResponse(`Unknown inventory tool: ${name}`);
7970
+ }
7971
+ } catch (error) {
7972
+ const message = error instanceof Error ? error.message : String(error);
7973
+ return errorResponse(`Inventory tool error: ${message}`);
7974
+ }
7975
+ }
7976
+ async function scanInventory(args) {
7977
+ const directory = getString(args, "directory");
7978
+ if (!directory) {
7979
+ return errorResponse("Missing required parameter: directory");
7980
+ }
7981
+ const category = getString(args, "category") || void 0;
7982
+ const limit = getNumber(args, "limit", 50);
7983
+ const extract = await getExtractor();
7984
+ const allSnippets = await extract(directory);
7985
+ cachedSnippets = allSnippets;
7986
+ cachedProjectDir = directory;
7987
+ let snippets = allSnippets;
7988
+ if (category) {
7989
+ snippets = snippets.filter((s) => s.category === category);
7990
+ }
7991
+ const summary = computeSummary(snippets);
7992
+ const returnSnippets = limit === 0 ? [] : snippets.slice(0, limit).map(formatSnippet);
7993
+ return jsonResponse({
7994
+ projectDir: directory,
7995
+ total: snippets.length,
7996
+ summary,
7997
+ snippets: returnSnippets,
7998
+ ...snippets.length > limit && limit > 0 ? {
7999
+ note: `Showing ${limit} of ${snippets.length}. Use query_inventory to filter or increase limit.`
8000
+ } : {}
8001
+ });
8002
+ }
8003
+ function queryInventory(args) {
8004
+ if (cachedSnippets.length === 0) {
8005
+ return errorResponse(
8006
+ "No inventory data. Call scan_inventory first to scan a project directory."
8007
+ );
8008
+ }
8009
+ const search = getString(args, "search") || void 0;
8010
+ const category = getString(args, "category") || void 0;
8011
+ const command = getString(args, "command") || void 0;
8012
+ const event = getString(args, "event") || void 0;
8013
+ const file = getString(args, "file") || void 0;
8014
+ const element = getString(args, "element") || void 0;
8015
+ const limit = getNumber(args, "limit", 30);
8016
+ let snippets = cachedSnippets;
8017
+ if (category) snippets = snippets.filter((s) => s.category === category);
8018
+ if (command) snippets = snippets.filter((s) => s.commands?.includes(command));
8019
+ if (event) snippets = snippets.filter((s) => s.events?.includes(event));
8020
+ if (file) snippets = snippets.filter((s) => s.relativePath?.includes(file));
8021
+ if (element) snippets = snippets.filter((s) => s.elementType === element);
8022
+ if (search) {
8023
+ const q = search.toLowerCase();
8024
+ snippets = snippets.filter(
8025
+ (s) => s.value?.toLowerCase().includes(q) || s.relativePath?.toLowerCase().includes(q) || s.attributeName?.toLowerCase().includes(q) || s.commands?.some((c) => c.includes(q)) || s.events?.some((e) => e.includes(q))
8026
+ );
8027
+ }
8028
+ const summary = computeSummary(snippets);
8029
+ const returnSnippets = snippets.slice(0, limit).map(formatSnippet);
8030
+ return jsonResponse({
8031
+ projectDir: cachedProjectDir,
8032
+ matched: snippets.length,
8033
+ summary,
8034
+ snippets: returnSnippets,
8035
+ ...snippets.length > limit ? { note: `Showing ${limit} of ${snippets.length}. Narrow your filters or increase limit.` } : {}
8036
+ });
8037
+ }
8038
+ function computeSummary(snippets) {
8039
+ const files = new Set(snippets.map((s) => s.filePath || s.relativePath));
8040
+ const byCategory = {};
8041
+ const byCommand = {};
8042
+ const byEvent = {};
8043
+ const byElementType = {};
8044
+ for (const s of snippets) {
8045
+ byCategory[s.category] = (byCategory[s.category] ?? 0) + 1;
8046
+ if (s.elementType) byElementType[s.elementType] = (byElementType[s.elementType] ?? 0) + 1;
8047
+ for (const cmd of s.commands ?? []) {
8048
+ byCommand[cmd] = (byCommand[cmd] ?? 0) + 1;
8049
+ }
8050
+ for (const evt of s.events ?? []) {
8051
+ byEvent[evt] = (byEvent[evt] ?? 0) + 1;
8052
+ }
8053
+ }
8054
+ return {
8055
+ totalFiles: files.size,
8056
+ totalSnippets: snippets.length,
8057
+ byCategory,
8058
+ ...Object.keys(byCommand).length > 0 ? { byCommand } : {},
8059
+ ...Object.keys(byEvent).length > 0 ? { byEvent } : {},
8060
+ ...Object.keys(byElementType).length > 0 ? { byElementType } : {}
8061
+ };
8062
+ }
8063
+ function formatSnippet(s) {
8064
+ return {
8065
+ file: s.relativePath,
8066
+ line: s.line,
8067
+ attribute: s.attributeName,
8068
+ value: s.value,
8069
+ category: s.category,
8070
+ element: s.elementType,
8071
+ ...s.commands?.length ? { commands: s.commands } : {},
8072
+ ...s.events?.length ? { events: s.events } : {}
8073
+ };
8074
+ }
8075
+ function createInlineExtractor() {
8076
+ return async function extractSnippets(dir, _options) {
8077
+ const fs = await import("fs");
8078
+ const path = await import("path");
8079
+ const crypto = await import("crypto");
8080
+ const { glob } = await import("./esm-LUPOYJFX.js");
8081
+ const globs = [
8082
+ "**/*.html",
8083
+ "**/*.htm",
8084
+ "**/*.djhtml",
8085
+ "**/*.jinja",
8086
+ "**/*.j2",
8087
+ "**/*.erb",
8088
+ "**/*.php",
8089
+ "**/*.vue",
8090
+ "**/*.svelte",
8091
+ "**/*.jsx",
8092
+ "**/*.tsx",
8093
+ "**/*.hbs",
8094
+ "**/*.ejs",
8095
+ "**/*.astro"
8096
+ ];
8097
+ const ignore = ["**/node_modules/**", "**/dist/**", "**/.git/**", "**/venv/**", "**/.venv/**"];
8098
+ const files = await glob(globs, { cwd: dir, ignore, absolute: true });
8099
+ const snippets = [];
8100
+ const patterns = [
8101
+ [/(_)\s*=\s*"((?:[^"\\]|\\.)*)"/gi, "hyperscript"],
8102
+ [/(_)\s*=\s*'((?:[^'\\]|\\.)*)'/gi, "hyperscript"],
8103
+ [/(hx-on:\w+)\s*=\s*"((?:[^"\\]|\\.)*)"/gi, "hyperscript"],
8104
+ [/(hx-(?:get|post|put|patch|delete))\s*=\s*"((?:[^"\\]|\\.)*)"/gi, "htmx-request"],
8105
+ [/(hx-(?:get|post|put|patch|delete))\s*=\s*'((?:[^'\\]|\\.)*)'/gi, "htmx-request"],
8106
+ [/((?:hx|fx)-swap)\s*=\s*"((?:[^"\\]|\\.)*)"/gi, "htmx-swap"],
8107
+ [/((?:hx|fx)-target)\s*=\s*"((?:[^"\\]|\\.)*)"/gi, "htmx-target"],
8108
+ [/((?:hx|fx)-trigger)\s*=\s*"((?:[^"\\]|\\.)*)"/gi, "htmx-trigger"],
8109
+ [/(fx-action)\s*=\s*"((?:[^"\\]|\\.)*)"/gi, "fixi"],
8110
+ [
8111
+ /(hx-(?:confirm|push-url|replace-url|indicator|select|vals|headers|include|boost|ext|encoding|params))\s*=\s*"((?:[^"\\]|\\.)*)"/gi,
8112
+ "htmx-other"
8113
+ ],
8114
+ [
8115
+ /(hx-(?:confirm|push-url|replace-url|indicator|select|vals|headers|include|boost|ext|encoding|params))\s*=\s*'((?:[^'\\]|\\.)*)'/gi,
8116
+ "htmx-other"
8117
+ ]
8118
+ ];
8119
+ const cmdRe = /\b(toggle|add|remove|show|hide|set|get|put|append|take|increment|decrement|log|send|trigger|wait|transition|go|call|focus|blur|return|fetch|tell|install)\b/g;
8120
+ const evtRe = /\bon\s+(\w+)/g;
8121
+ for (const file of files) {
8122
+ try {
8123
+ const content = fs.readFileSync(file, "utf-8");
8124
+ const stat = fs.statSync(file);
8125
+ const relPath = path.relative(dir, file);
8126
+ const seen = /* @__PURE__ */ new Set();
8127
+ for (const [pattern, category] of patterns) {
8128
+ const re = new RegExp(pattern.source, pattern.flags);
8129
+ let m;
8130
+ while (m = re.exec(content)) {
8131
+ const attrName = m[1];
8132
+ const value = m[2];
8133
+ const offset = m.index + m[0].indexOf(m[2]);
8134
+ if (seen.has(offset)) continue;
8135
+ seen.add(offset);
8136
+ let line = 1;
8137
+ for (let i = 0; i < m.index && i < content.length; i++) {
8138
+ if (content[i] === "\n") line++;
8139
+ }
8140
+ let start = m.index;
8141
+ while (start > 0 && content[start] !== "<") start--;
8142
+ const tagMatch = content.slice(start).match(/^<\s*(\w[\w-]*)/);
8143
+ const elementType = tagMatch ? tagMatch[1].toLowerCase() : "unknown";
8144
+ const isHS = category === "hyperscript";
8145
+ const commands = [];
8146
+ const events = [];
8147
+ if (isHS) {
8148
+ let cm;
8149
+ const cr = new RegExp(cmdRe.source, "g");
8150
+ while (cm = cr.exec(value)) commands.push(cm[1]);
8151
+ const er = new RegExp(evtRe.source, "g");
8152
+ while (cm = er.exec(value)) events.push(cm[1]);
8153
+ }
8154
+ const id = crypto.createHash("md5").update(`${file}:${offset}`).digest("hex").slice(0, 12);
8155
+ snippets.push({
8156
+ id,
8157
+ filePath: file,
8158
+ relativePath: relPath,
8159
+ attributeName: attrName,
8160
+ value,
8161
+ isMultiline: value.includes("\n"),
8162
+ line,
8163
+ column: 1,
8164
+ elementType,
8165
+ category,
8166
+ commands: [...new Set(commands)],
8167
+ events: [...new Set(events)],
8168
+ fileMtime: stat.mtimeMs
8169
+ });
8170
+ }
8171
+ }
8172
+ } catch {
8173
+ }
8174
+ }
8175
+ snippets.sort(
8176
+ (a, b) => a.relativePath.localeCompare(b.relativePath) || a.line - b.line
8177
+ );
8178
+ return snippets;
8179
+ };
8180
+ }
8181
+
8182
+ // src/tools/training-data.ts
8183
+ var trainingDataTools = [
8184
+ {
8185
+ name: "generate_training_data",
8186
+ description: "Synthesize (natural_language, LSE) training pairs from domain command schemas. Returns JSONL-formatted data for LLM fine-tuning.",
8187
+ inputSchema: {
8188
+ type: "object",
8189
+ properties: {
8190
+ domain: {
8191
+ type: "string",
8192
+ description: "Domain to generate training data for (flow, sql, bdd, llm, jsx, todo, behaviorspec, voice)"
8193
+ },
8194
+ max_per_command: {
8195
+ type: "number",
8196
+ description: "Maximum pairs per command. Default: 5"
8197
+ },
8198
+ languages: {
8199
+ type: "string",
8200
+ description: "Comma-separated language codes. Default: en"
8201
+ },
8202
+ min_quality: {
8203
+ type: "number",
8204
+ description: "Minimum quality threshold (0-1). Default: 0.5"
8205
+ },
8206
+ format: {
8207
+ type: "string",
8208
+ description: "Output format: jsonl (default) or json"
8209
+ }
8210
+ },
8211
+ required: ["domain"]
8212
+ }
8213
+ }
8214
+ ];
8215
+ async function handleTrainingDataTool(name, args) {
8216
+ if (name !== "generate_training_data") {
8217
+ return {
8218
+ content: [{ type: "text", text: `Unknown training data tool: ${name}` }],
8219
+ isError: true
8220
+ };
8221
+ }
8222
+ try {
8223
+ const { synthesizeFromSchemas, toJSONL } = await import("@lokascript/framework");
8224
+ const domain = args.domain;
8225
+ if (!domain) {
8226
+ return {
8227
+ content: [{ type: "text", text: "Missing required parameter: domain" }],
8228
+ isError: true
8229
+ };
8230
+ }
8231
+ const schemas = await loadDomainSchemas(domain);
8232
+ if (!schemas || schemas.length === 0) {
8233
+ return {
8234
+ content: [
8235
+ {
8236
+ type: "text",
8237
+ text: `No schemas found for domain: ${domain}. Available: flow, sql, bdd, llm, jsx, todo, behaviorspec, voice`
8238
+ }
8239
+ ],
8240
+ isError: true
8241
+ };
8242
+ }
8243
+ const languages = typeof args.languages === "string" ? args.languages.split(",").map((l) => l.trim()) : ["en"];
8244
+ const result = synthesizeFromSchemas(schemas, {
8245
+ domain,
8246
+ maxPairsPerCommand: typeof args.max_per_command === "number" ? args.max_per_command : 5,
8247
+ minQuality: typeof args.min_quality === "number" ? args.min_quality : 0.5,
8248
+ languages
8249
+ });
8250
+ const format = args.format === "json" ? "json" : "jsonl";
8251
+ const output = format === "json" ? JSON.stringify(result, null, 2) : toJSONL(result.pairs);
8252
+ return {
8253
+ content: [
8254
+ {
8255
+ type: "text",
8256
+ text: `Generated ${result.metadata.pairCount} training pairs for domain "${domain}" (${result.metadata.commandCount} commands, languages: ${languages.join(", ")})
8257
+
8258
+ ${output}`
8259
+ }
8260
+ ]
8261
+ };
8262
+ } catch (error) {
8263
+ return {
8264
+ content: [
8265
+ {
8266
+ type: "text",
8267
+ text: `Training data generation error: ${error instanceof Error ? error.message : String(error)}`
8268
+ }
8269
+ ],
8270
+ isError: true
8271
+ };
8272
+ }
8273
+ }
8274
+ async function loadDomainSchemas(domain) {
8275
+ try {
8276
+ switch (domain) {
8277
+ case "flow":
8278
+ return (await import("@lokascript/domain-flow")).allSchemas;
8279
+ case "sql":
8280
+ return (await import("@lokascript/domain-sql")).allSchemas;
8281
+ case "bdd":
8282
+ return (await import("@lokascript/domain-bdd")).allSchemas;
8283
+ case "llm":
8284
+ return (await import("@lokascript/domain-llm")).allSchemas;
8285
+ case "jsx":
8286
+ return (await import("@lokascript/domain-jsx")).allSchemas;
8287
+ case "todo":
8288
+ return (await import("@lokascript/domain-todo")).allSchemas;
8289
+ case "behaviorspec":
8290
+ return (await import("@lokascript/domain-behaviorspec")).allSchemas;
8291
+ case "voice":
8292
+ return (await import("@lokascript/domain-voice")).allSchemas;
8293
+ default:
8294
+ return null;
8295
+ }
8296
+ } catch {
8297
+ return null;
8298
+ }
8299
+ }
8300
+
8301
+ // src/tools/feedback-tools.ts
8302
+ var feedbackTools = [
8303
+ {
8304
+ name: "lse_validate_and_feedback",
8305
+ description: "Validate LSE input and return structured feedback with machine-actionable hints, corrected examples, and schema context for LLM self-correction.",
8306
+ inputSchema: {
8307
+ type: "object",
8308
+ properties: {
8309
+ input: {
8310
+ type: "string",
8311
+ description: "The LSE input to validate (bracket syntax, JSON, or natural language)"
8312
+ },
8313
+ format: {
8314
+ type: "string",
8315
+ description: "Input format: explicit (default), json, protocol (full-fidelity v1.1/v1.2), envelope (v1.2 multi-node), or natural"
8316
+ },
8317
+ domain: {
8318
+ type: "string",
8319
+ description: "Domain to validate against (flow, sql, bdd, llm, jsx, todo, behaviorspec, voice)"
8320
+ }
8321
+ },
8322
+ required: ["input"]
8323
+ }
8324
+ },
8325
+ {
8326
+ name: "lse_pattern_stats",
8327
+ description: "Return pattern hit-rate statistics: success rates by command and language, top diagnostic failures, and outcome distribution.",
8328
+ inputSchema: {
8329
+ type: "object",
8330
+ properties: {
8331
+ domain: {
8332
+ type: "string",
8333
+ description: "Filter stats by domain (optional)"
8334
+ }
8335
+ }
8336
+ }
8337
+ }
8338
+ ];
8339
+ async function handleFeedbackTool(name, args) {
8340
+ switch (name) {
8341
+ case "lse_validate_and_feedback":
8342
+ return handleValidateAndFeedback(args);
8343
+ case "lse_pattern_stats":
8344
+ return handlePatternStats();
8345
+ default:
8346
+ return {
8347
+ content: [{ type: "text", text: `Unknown feedback tool: ${name}` }],
8348
+ isError: true
8349
+ };
8350
+ }
8351
+ }
8352
+ async function handleValidateAndFeedback(args) {
8353
+ try {
8354
+ const { buildFeedback, parseExplicit: parseExplicit2 } = await import("@lokascript/framework");
8355
+ const input = args.input;
8356
+ if (!input) {
8357
+ return {
8358
+ content: [{ type: "text", text: "Missing required parameter: input" }],
8359
+ isError: true
8360
+ };
8361
+ }
8362
+ const format = args.format || "explicit";
8363
+ const domain = args.domain;
8364
+ let action;
8365
+ let schemaLookup;
8366
+ const diagnostics = [];
8367
+ if (domain) {
8368
+ const schemas = await loadDomainSchemas2(domain);
8369
+ if (schemas) {
8370
+ schemaLookup = createSchemaLookup(schemas);
8371
+ }
8372
+ }
8373
+ if (format === "explicit") {
8374
+ try {
8375
+ const result = parseExplicit2(input, { schemaLookup });
8376
+ if (result) {
8377
+ action = result.action;
8378
+ }
8379
+ } catch (e) {
8380
+ diagnostics.push({
8381
+ severity: "error",
8382
+ message: e instanceof Error ? e.message : "Parse error: invalid bracket syntax",
8383
+ code: "parse-error"
8384
+ });
8385
+ }
8386
+ } else if (format === "json") {
8387
+ try {
8388
+ const parsed = JSON.parse(input);
8389
+ action = parsed.action;
8390
+ if (!action) {
8391
+ diagnostics.push({
8392
+ severity: "error",
8393
+ message: 'JSON missing required "action" field',
8394
+ code: "missing-role"
8395
+ });
8396
+ }
8397
+ } catch {
8398
+ diagnostics.push({
8399
+ severity: "error",
8400
+ message: "Invalid JSON syntax",
8401
+ code: "parse-error"
8402
+ });
8403
+ }
8404
+ } else if (format === "protocol") {
8405
+ try {
8406
+ const { validateProtocolJSON: validateProtocolJSON2, fromProtocolJSON: fromProtocolJSON2 } = await import("@lokascript/framework");
8407
+ const parsed = JSON.parse(input);
8408
+ const protoDiags = validateProtocolJSON2(parsed);
8409
+ for (const d of protoDiags) {
8410
+ diagnostics.push({ severity: d.severity, message: d.message, code: d.code });
8411
+ }
8412
+ if (protoDiags.filter((d) => d.severity === "error").length === 0) {
8413
+ const node = fromProtocolJSON2(parsed);
8414
+ action = node.action;
8415
+ }
8416
+ } catch (e) {
8417
+ diagnostics.push({
8418
+ severity: "error",
8419
+ message: e instanceof Error ? e.message : "Invalid protocol JSON",
8420
+ code: "parse-error"
8421
+ });
8422
+ }
8423
+ } else if (format === "envelope") {
8424
+ try {
8425
+ const { isEnvelope: isEnvelope2, fromEnvelopeJSON: fromEnvelopeJSON2, validateProtocolJSON: validateProtocolJSON2 } = await import("@lokascript/framework");
8426
+ const parsed = JSON.parse(input);
8427
+ if (!isEnvelope2(parsed)) {
8428
+ diagnostics.push({
8429
+ severity: "error",
8430
+ message: "Not a valid LSE envelope: missing lseVersion or nodes",
8431
+ code: "invalid-envelope"
8432
+ });
8433
+ } else {
8434
+ for (let i = 0; i < parsed.nodes.length; i++) {
8435
+ const nodeDiags = validateProtocolJSON2(parsed.nodes[i]);
8436
+ for (const d of nodeDiags) {
8437
+ diagnostics.push({
8438
+ severity: d.severity,
8439
+ message: `Node ${i}: ${d.message}`,
8440
+ code: d.code
8441
+ });
8442
+ }
8443
+ }
8444
+ if (diagnostics.filter((d) => d.severity === "error").length === 0) {
8445
+ const envelope = fromEnvelopeJSON2(parsed);
8446
+ action = envelope.nodes[0]?.action;
8447
+ }
8448
+ }
8449
+ } catch (e) {
8450
+ diagnostics.push({
8451
+ severity: "error",
8452
+ message: e instanceof Error ? e.message : "Invalid envelope JSON",
8453
+ code: "parse-error"
8454
+ });
8455
+ }
8456
+ }
8457
+ const feedbackFormat = format === "protocol" || format === "envelope" ? "json" : format;
8458
+ const feedback = buildFeedback(input, feedbackFormat, diagnostics, schemaLookup, action);
8459
+ return {
8460
+ content: [{ type: "text", text: JSON.stringify(feedback, null, 2) }]
8461
+ };
8462
+ } catch (error) {
8463
+ return {
8464
+ content: [
8465
+ {
8466
+ type: "text",
8467
+ text: `Feedback tool error: ${error instanceof Error ? error.message : String(error)}`
8468
+ }
8469
+ ],
8470
+ isError: true
8471
+ };
8472
+ }
8473
+ }
8474
+ var _tracker = null;
8475
+ async function getTracker() {
8476
+ if (!_tracker) {
8477
+ const { PatternTracker } = await import("@lokascript/framework");
8478
+ _tracker = new PatternTracker(1e3);
8479
+ }
8480
+ return _tracker;
8481
+ }
8482
+ async function handlePatternStats() {
8483
+ try {
8484
+ const tracker = await getTracker();
8485
+ const summary = tracker.getSummary();
8486
+ const text = summary.totalEvents === 0 ? "No pattern events recorded yet. Events are recorded as LSE inputs are validated." : JSON.stringify(summary, null, 2);
8487
+ return {
8488
+ content: [{ type: "text", text }]
8489
+ };
8490
+ } catch (error) {
8491
+ return {
8492
+ content: [
8493
+ {
8494
+ type: "text",
8495
+ text: `Pattern stats error: ${error instanceof Error ? error.message : String(error)}`
8496
+ }
8497
+ ],
8498
+ isError: true
8499
+ };
8500
+ }
8501
+ }
8502
+ async function loadDomainSchemas2(domain) {
8503
+ try {
8504
+ switch (domain) {
8505
+ case "flow":
8506
+ return (await import("@lokascript/domain-flow")).allSchemas;
8507
+ case "sql":
8508
+ return (await import("@lokascript/domain-sql")).allSchemas;
8509
+ case "bdd":
8510
+ return (await import("@lokascript/domain-bdd")).allSchemas;
8511
+ case "llm":
8512
+ return (await import("@lokascript/domain-llm")).allSchemas;
8513
+ case "jsx":
8514
+ return (await import("@lokascript/domain-jsx")).allSchemas;
8515
+ case "todo":
8516
+ return (await import("@lokascript/domain-todo")).allSchemas;
8517
+ case "behaviorspec":
8518
+ return (await import("@lokascript/domain-behaviorspec")).allSchemas;
8519
+ case "voice":
8520
+ return (await import("@lokascript/domain-voice")).allSchemas;
8521
+ default:
8522
+ return null;
8523
+ }
8524
+ } catch {
8525
+ return null;
8526
+ }
8527
+ }
8528
+ function createSchemaLookup(schemas) {
8529
+ const map = /* @__PURE__ */ new Map();
8530
+ for (const s of schemas) {
8531
+ map.set(s.action, s);
8532
+ }
8533
+ return {
8534
+ getSchema(action) {
8535
+ return map.get(action);
8536
+ }
8537
+ };
8538
+ }
8539
+
8540
+ // src/tools/lse-pipeline.ts
8541
+ var lsePipelineTools = [
8542
+ // ── Phase 7.5: LLM-Native LSE tools ──────────────────────────────────────
8543
+ {
8544
+ name: "execute_lse",
8545
+ description: "Execute LSE bracket syntax directly. Parses the LSE, compiles to JavaScript, and returns the execution result. Use this when an LLM has generated LSE and you want to run it.",
8546
+ inputSchema: {
8547
+ type: "object",
8548
+ properties: {
8549
+ lse: {
8550
+ type: "string",
8551
+ description: "LSE bracket syntax: [command role:value ...]"
8552
+ }
8553
+ },
8554
+ required: ["lse"]
8555
+ }
8556
+ },
8557
+ {
8558
+ name: "validate_lse",
8559
+ description: "Validate LSE bracket syntax or protocol JSON without executing. Returns diagnostics (errors, warnings) and the normalized LSE. Use for pre-flight checks before execution.",
8560
+ inputSchema: {
8561
+ type: "object",
8562
+ properties: {
8563
+ lse: {
8564
+ type: "string",
8565
+ description: "LSE bracket syntax to validate"
8566
+ },
8567
+ protocol: {
8568
+ type: "object",
8569
+ description: "Protocol JSON to validate (alternative to lse)"
8570
+ }
8571
+ }
8572
+ }
8573
+ },
8574
+ {
8575
+ name: "translate_lse",
8576
+ description: "Translate LSE bracket syntax to natural language in a specified target language. Returns the natural-language hyperscript equivalent. Supports all 24 languages.",
8577
+ inputSchema: {
8578
+ type: "object",
8579
+ properties: {
8580
+ lse: {
8581
+ type: "string",
8582
+ description: "LSE bracket syntax: [command role:value ...]"
8583
+ },
8584
+ language: {
8585
+ type: "string",
8586
+ description: "Target language code (e.g., en, ja, es, ko, ar, zh)"
8587
+ }
8588
+ },
8589
+ required: ["lse", "language"]
8590
+ }
8591
+ },
8592
+ // ── Existing pipeline tools ───────────────────────────────────────────────
8593
+ {
8594
+ name: "lse_from_hyperscript",
8595
+ description: "Parse hyperscript code (any of 24 languages) to LSE bracket syntax and protocol JSON. Use this to get a structured representation for LLM modification or cross-language translation. Returns both bracket syntax (human-readable) and protocol JSON (machine-readable).",
8596
+ inputSchema: {
8597
+ type: "object",
8598
+ properties: {
8599
+ code: {
8600
+ type: "string",
8601
+ description: "Hyperscript code in any supported language"
8602
+ },
8603
+ language: {
8604
+ type: "string",
8605
+ description: "ISO 639-1 language code (default: en)"
8606
+ },
8607
+ confidence: {
8608
+ type: "number",
8609
+ description: "Minimum confidence threshold for semantic parsing (default: 0.5)"
8610
+ }
8611
+ },
8612
+ required: ["code"]
8613
+ }
8614
+ },
8615
+ {
8616
+ name: "lse_to_hyperscript",
8617
+ description: "Validate and optionally compile LSE bracket syntax or protocol JSON returned by an LLM. Returns validation diagnostics, the normalized LSE, and optionally compiled JavaScript. Use lse_validate_and_feedback for richer error feedback with correction hints.",
8618
+ inputSchema: {
8619
+ type: "object",
8620
+ properties: {
8621
+ lse: {
8622
+ type: "string",
8623
+ description: "LSE bracket syntax: [command role:value ...]"
8624
+ },
8625
+ protocol: {
8626
+ type: "object",
8627
+ description: "Protocol JSON (alternative to lse). Must have `kind` field."
8628
+ },
8629
+ compile: {
8630
+ type: "boolean",
8631
+ description: "Also compile to JavaScript (default: true)"
8632
+ }
8633
+ }
8634
+ }
8635
+ }
8636
+ ];
8637
+ async function handleLsePipelineTool(name, args) {
8638
+ try {
8639
+ switch (name) {
8640
+ case "execute_lse":
8641
+ return await handleExecuteLse(args);
8642
+ case "validate_lse":
8643
+ return await handleValidateLse(args);
8644
+ case "translate_lse":
8645
+ return await handleTranslateLse(args);
8646
+ case "lse_from_hyperscript":
8647
+ return await handleFromHyperscript(args);
8648
+ case "lse_to_hyperscript":
8649
+ return await handleToHyperscript(args);
8650
+ default:
8651
+ return errorResponse(`Unknown LSE pipeline tool: ${name}`);
8652
+ }
8653
+ } catch (error) {
8654
+ const message = error instanceof Error ? error.message : String(error);
8655
+ return errorResponse(`LSE pipeline error: ${message}`);
8656
+ }
8657
+ }
8658
+ async function handleFromHyperscript(args) {
8659
+ const code = args.code;
8660
+ if (!code || typeof code !== "string") {
8661
+ return errorResponse("Missing required parameter: code");
8662
+ }
8663
+ const language = args.language || "en";
8664
+ const minConfidence = typeof args.confidence === "number" ? args.confidence : 0.5;
8665
+ const { parseSemantic } = await import("@lokascript/semantic");
8666
+ const parseResult = parseSemantic(code, language);
8667
+ if (!parseResult.node) {
8668
+ return jsonResponse({
8669
+ ok: false,
8670
+ error: "Semantic parsing failed \u2014 no node produced",
8671
+ confidence: parseResult.confidence,
8672
+ language
8673
+ });
8674
+ }
8675
+ if (parseResult.confidence < minConfidence) {
8676
+ return jsonResponse({
8677
+ ok: false,
8678
+ error: `Confidence ${parseResult.confidence.toFixed(2)} below threshold ${minConfidence}`,
8679
+ confidence: parseResult.confidence,
8680
+ language
8681
+ });
8682
+ }
8683
+ const { fromSemanticAST } = await import("@lokascript/semantic");
8684
+ const { fromInterchangeNode: fromInterchangeNode2, renderExplicit: renderExplicit2, toProtocolJSON: toProtocolJSON2 } = await import("@lokascript/framework");
8685
+ const interchangeNode = fromSemanticAST(parseResult.node);
8686
+ const semanticNode = fromInterchangeNode2(interchangeNode);
8687
+ const explicit = renderExplicit2(semanticNode);
8688
+ const protocol = toProtocolJSON2(semanticNode);
8689
+ return jsonResponse({
8690
+ ok: true,
8691
+ lse: explicit,
8692
+ protocol,
8693
+ confidence: parseResult.confidence,
8694
+ language
8695
+ });
8696
+ }
8697
+ async function handleToHyperscript(args) {
8698
+ const lseInput = typeof args.lse === "string" ? args.lse : void 0;
8699
+ const protocolInput = typeof args.protocol === "object" && args.protocol ? args.protocol : void 0;
8700
+ const shouldCompile = args.compile !== false;
8701
+ if (!lseInput && !protocolInput) {
8702
+ return errorResponse("Provide `lse` (bracket syntax) or `protocol` (JSON).");
8703
+ }
8704
+ const { parseExplicit: parseExplicit2, renderExplicit: renderExplicit2, toProtocolJSON: toProtocolJSON2, fromProtocolJSON: fromProtocolJSON2, validateProtocolJSON: validateProtocolJSON2 } = await import("@lokascript/framework");
8705
+ let node;
8706
+ const diagnostics = [];
8707
+ if (lseInput) {
8708
+ try {
8709
+ node = parseExplicit2(lseInput);
8710
+ } catch (e) {
8711
+ const errorMessage = e instanceof Error ? e.message : "Invalid bracket syntax";
8712
+ try {
8713
+ const { buildFeedback } = await import("@lokascript/framework");
8714
+ const actionMatch = lseInput.match(/^\[\s*(\w+)/);
8715
+ const feedback = buildFeedback(
8716
+ lseInput,
8717
+ "explicit",
8718
+ [{ severity: "error", message: errorMessage, code: "EXPLICIT_PARSE_ERROR" }],
8719
+ void 0,
8720
+ actionMatch?.[1]
8721
+ );
8722
+ return jsonResponse({ ok: false, feedback });
8723
+ } catch {
8724
+ diagnostics.push({
8725
+ severity: "error",
8726
+ code: "EXPLICIT_PARSE_ERROR",
8727
+ message: errorMessage
8728
+ });
8729
+ }
8730
+ }
8731
+ } else if (protocolInput) {
8732
+ const protoDiags = validateProtocolJSON2(protocolInput);
8733
+ const hasErrors = protoDiags.some((d) => d.severity === "error");
8734
+ if (hasErrors) {
8735
+ try {
8736
+ const { buildFeedback } = await import("@lokascript/framework");
8737
+ const feedback = buildFeedback(
8738
+ JSON.stringify(protocolInput, null, 2),
8739
+ "json",
8740
+ protoDiags.map((d) => ({
8741
+ severity: d.severity,
8742
+ message: d.message,
8743
+ code: d.code
8744
+ })),
8745
+ void 0,
8746
+ protocolInput.action
8747
+ );
8748
+ return jsonResponse({ ok: false, feedback });
8749
+ } catch {
8750
+ for (const d of protoDiags) {
8751
+ diagnostics.push({ severity: d.severity, code: d.code, message: d.message });
8752
+ }
8753
+ }
8754
+ } else {
8755
+ for (const d of protoDiags) {
8756
+ diagnostics.push({ severity: d.severity, code: d.code, message: d.message });
8757
+ }
8758
+ node = fromProtocolJSON2(protocolInput);
8759
+ }
8760
+ }
8761
+ if (!node) {
8762
+ return jsonResponse({ ok: false, diagnostics });
8763
+ }
8764
+ const normalizedLse = renderExplicit2(node);
8765
+ const normalizedProtocol = toProtocolJSON2(node);
8766
+ const result = {
8767
+ ok: true,
8768
+ lse: normalizedLse,
8769
+ protocol: normalizedProtocol,
8770
+ diagnostics
8771
+ };
8772
+ if (shouldCompile) {
8773
+ try {
8774
+ const { CompilationService } = await import("@lokascript/compilation-service");
8775
+ const service = await CompilationService.create();
8776
+ const compileResult = service.compile({ explicit: normalizedLse });
8777
+ result.js = compileResult.ok ? compileResult.js : void 0;
8778
+ if (!compileResult.ok && compileResult.diagnostics) {
8779
+ for (const d of compileResult.diagnostics) {
8780
+ if (d.severity === "error") {
8781
+ diagnostics.push({
8782
+ severity: "error",
8783
+ code: "COMPILE_ERROR",
8784
+ message: d.message
8785
+ });
8786
+ }
8787
+ }
8788
+ }
8789
+ } catch (e) {
8790
+ result.js = void 0;
8791
+ result.compileNote = "Compilation service unavailable";
8792
+ }
8793
+ }
8794
+ return jsonResponse(result);
8795
+ }
8796
+ async function handleExecuteLse(args) {
8797
+ const lseInput = args.lse;
8798
+ if (!lseInput || typeof lseInput !== "string") {
8799
+ return errorResponse("Missing required parameter: lse");
8800
+ }
8801
+ const { parseExplicit: parseExplicit2, renderExplicit: renderExplicit2, toProtocolJSON: toProtocolJSON2 } = await import("@lokascript/framework");
8802
+ let node;
8803
+ try {
8804
+ node = parseExplicit2(lseInput);
8805
+ } catch (e) {
8806
+ return errorResponse(`LSE parse error: ${e instanceof Error ? e.message : String(e)}`);
8807
+ }
8808
+ const normalizedLse = renderExplicit2(node);
8809
+ const protocol = toProtocolJSON2(node);
8810
+ let js;
8811
+ try {
8812
+ const { CompilationService } = await import("@lokascript/compilation-service");
8813
+ const service = await CompilationService.create();
8814
+ const compileResult = service.compile({ explicit: normalizedLse });
8815
+ js = compileResult.ok ? compileResult.js : void 0;
8816
+ } catch {
8817
+ }
8818
+ return jsonResponse({
8819
+ ok: true,
8820
+ lse: normalizedLse,
8821
+ protocol,
8822
+ js,
8823
+ note: js ? "Compiled successfully" : "Compilation service unavailable \u2014 parsed and validated only"
8824
+ });
8825
+ }
8826
+ async function handleValidateLse(args) {
8827
+ const lseInput = typeof args.lse === "string" ? args.lse : void 0;
8828
+ const protocolInput = typeof args.protocol === "object" && args.protocol ? args.protocol : void 0;
8829
+ if (!lseInput && !protocolInput) {
8830
+ return errorResponse("Provide `lse` (bracket syntax) or `protocol` (JSON).");
8831
+ }
8832
+ const { parseExplicit: parseExplicit2, renderExplicit: renderExplicit2, validateProtocolJSON: validateProtocolJSON2 } = await import("@lokascript/framework");
8833
+ const diagnostics = [];
8834
+ if (lseInput) {
8835
+ try {
8836
+ const node = parseExplicit2(lseInput);
8837
+ const normalized = renderExplicit2(node);
8838
+ return jsonResponse({
8839
+ valid: true,
8840
+ normalized,
8841
+ diagnostics: []
8842
+ });
8843
+ } catch (e) {
8844
+ diagnostics.push({
8845
+ severity: "error",
8846
+ code: "PARSE_ERROR",
8847
+ message: e instanceof Error ? e.message : String(e)
8848
+ });
8849
+ return jsonResponse({ valid: false, diagnostics });
8850
+ }
8851
+ }
8852
+ if (protocolInput) {
8853
+ const protoDiags = validateProtocolJSON2(protocolInput);
8854
+ const hasErrors = protoDiags.some((d) => d.severity === "error");
8855
+ return jsonResponse({
8856
+ valid: !hasErrors,
8857
+ diagnostics: protoDiags
8858
+ });
8859
+ }
8860
+ return jsonResponse({ valid: false, diagnostics });
8861
+ }
8862
+ async function handleTranslateLse(args) {
8863
+ const lseInput = args.lse;
8864
+ const language = args.language;
8865
+ if (!lseInput || typeof lseInput !== "string") {
8866
+ return errorResponse("Missing required parameter: lse");
8867
+ }
8868
+ if (!language || typeof language !== "string") {
8869
+ return errorResponse("Missing required parameter: language");
8870
+ }
8871
+ try {
8872
+ const { parseExplicit: parseExplicit2, renderExplicit: renderExplicit2 } = await import("@lokascript/framework");
8873
+ const { translate } = await import("@lokascript/semantic");
8874
+ const node = parseExplicit2(lseInput);
8875
+ const normalized = renderExplicit2(node);
8876
+ const action = node.action;
8877
+ const roles = node.roles;
8878
+ const parts = [action];
8879
+ const roleEntries = roles instanceof Map ? [...roles.entries()] : Object.entries(roles || {});
8880
+ for (const [role, value] of roleEntries) {
8881
+ const val = typeof value === "object" && value !== null ? value.value ?? value : value;
8882
+ if (role === "patient") {
8883
+ parts.push(String(val));
8884
+ } else if (role === "destination") {
8885
+ parts.push("on", String(val));
8886
+ } else if (role === "source") {
8887
+ parts.push("from", String(val));
8888
+ } else {
8889
+ parts.push(String(val));
8890
+ }
8891
+ }
8892
+ const english = parts.join(" ");
8893
+ const translated = translate(english, "en", language);
8894
+ return jsonResponse({
8895
+ ok: true,
8896
+ language,
8897
+ translated,
8898
+ lse: normalized,
8899
+ english
8900
+ });
8901
+ } catch (e) {
8902
+ return errorResponse(`Translation error: ${e instanceof Error ? e.message : String(e)}`);
8903
+ }
8904
+ }
8905
+
8906
+ // src/resources/content.ts
8907
+ function getCommandsReference() {
8908
+ return `# Hyperscript Commands Reference
8909
+
8910
+ ## DOM Manipulation
8911
+
8912
+ | Command | Usage | Example |
8913
+ |---------|-------|---------|
8914
+ | \`toggle\` | Toggle class/attribute | \`toggle .active on #menu\` |
8915
+ | \`add\` | Add class/attribute/style | \`add .highlight to me\` |
8916
+ | \`remove\` | Remove class/attribute/element | \`remove .error from #form\` |
8917
+ | \`show\` | Show element | \`show #modal with *opacity\` |
8918
+ | \`hide\` | Hide element | \`hide me with *opacity\` |
8919
+ | \`put\` | Set element content | \`put "Hello" into #greeting\` |
8920
+ | \`append\` | Add to end | \`append "<li/>" to #list\` |
8921
+ | \`swap\` | Replace content | \`swap #target innerHTML\` |
8922
+
8923
+ ## Data Commands
8924
+
8925
+ | Command | Usage | Example |
8926
+ |---------|-------|---------|
8927
+ | \`set\` | Set variable/property | \`set :count to 0\` |
8928
+ | \`get\` | Get value | \`get #input.value\` |
8929
+ | \`increment\` | Add 1 | \`increment :count\` |
8930
+ | \`decrement\` | Subtract 1 | \`decrement :count\` |
8931
+
8932
+ ## Events
8933
+
8934
+ | Command | Usage | Example |
8935
+ |---------|-------|---------|
5232
8936
  | \`send\` | Dispatch event | \`send refresh to #list\` |
5233
8937
  | \`trigger\` | Trigger event | \`trigger submit on #form\` |
5234
8938
 
@@ -5923,6 +9627,170 @@ function getSetupGuide() {
5923
9627
  ]
5924
9628
  };
5925
9629
  }
9630
+ function getLSEProtocolSpec() {
9631
+ return `# LokaScript Explicit Syntax (LSE) Quick Reference
9632
+
9633
+ ## Syntax
9634
+
9635
+ \`\`\`
9636
+ [command role1:value1 role2:value2 +flag1 ~flag2]
9637
+ \`\`\`
9638
+
9639
+ | Element | Syntax | Example |
9640
+ |---------|--------|---------|
9641
+ | Command | first token (lowercased) | \`toggle\`, \`fetch\`, \`add\` |
9642
+ | Role pair | \`name:value\` | \`patient:.active\` |
9643
+ | Enabled flag | \`+name\` | \`+primary-key\` |
9644
+ | Disabled flag | \`~name\` | \`~nullable\` |
9645
+ | String literal | \`"..."\` or \`'...'\` | \`patient:"hello world"\` |
9646
+ | Selector | \`#id\` \`.class\` \`[attr]\` \`@aria\` \`*wild\` | \`destination:#button\` |
9647
+ | Boolean | \`true\` / \`false\` | \`goal:true\` |
9648
+ | Number | digits with optional decimal | \`quantity:5\` |
9649
+ | Duration | number + suffix | \`delay:500ms\`, \`timeout:2s\` |
9650
+ | Reference | built-in name | \`destination:me\`, \`source:it\` |
9651
+ | Nested body | bracket command in value | \`body:[toggle patient:.active]\` |
9652
+ | Compound | chain operator between commands | \`[add ...] then [fetch ...]\` |
9653
+
9654
+ ## Value Classification Priority (first match wins)
9655
+
9656
+ 1. **Selector** \u2014 starts with \`#\`, \`.\`, \`[\`, \`@\`, \`*\`
9657
+ 2. **String** \u2014 starts with \`"\` or \`'\`
9658
+ 3. **Boolean** \u2014 exact: \`true\` or \`false\`
9659
+ 4. **Reference** \u2014 \`me\`, \`you\`, \`it\`, \`result\`, \`event\`, \`target\`, \`body\`
9660
+ 5. **Duration** \u2014 number + \`ms\`/\`s\`/\`m\`/\`h\`
9661
+ 6. **Number** \u2014 integer or decimal
9662
+ 7. **Plain** \u2014 fallback (any non-whitespace)
9663
+
9664
+ ## Node Kinds
9665
+
9666
+ Every node has a \`kind\` field:
9667
+
9668
+ | Kind | Required Fields | Description |
9669
+ |------|----------------|-------------|
9670
+ | \`command\` | \`kind\`, \`action\`, \`roles\` | Single command (also encodes conditionals and loops via v1.1 fields) |
9671
+ | \`event-handler\` | \`kind\`, \`action\`, \`roles\`, \`body\` | Event handler with body commands |
9672
+ | \`compound\` | \`kind\`, \`action\`, \`statements\` | Chained commands with \`chainType\` (\`then\`/\`and\`/\`async\`/\`sequential\`) |
9673
+
9674
+ ## Value Types
9675
+
9676
+ | Type | JSON Shape | Notes |
9677
+ |------|-----------|-------|
9678
+ | \`selector\` | \`{ "type": "selector", "value": ".active", "selectorKind?": "class" }\` | \`selectorKind\`: id, class, attribute, element, complex (optional) |
9679
+ | \`literal\` | \`{ "type": "literal", "value": "hello", "dataType": "string" }\` | \`dataType\`: string, number, boolean, duration |
9680
+ | \`reference\` | \`{ "type": "reference", "value": "me" }\` | Built-in: me, you, it, result, event, target, body |
9681
+ | \`expression\` | \`{ "type": "expression", "raw": "x > 0" }\` | Nested syntax or raw expressions |
9682
+ | \`property-path\` | \`{ "type": "property-path", "value": "event.detail" }\` | Dotted property access |
9683
+ | \`flag\` | \`{ "type": "flag", "name": "primary-key", "enabled": true }\` | \`+flag\` = enabled, \`~flag\` = disabled |
9684
+
9685
+ ## JSON Wire Format \u2014 Full-Fidelity
9686
+
9687
+ Lossless round-trip format with \`kind\` field. Used for tool-to-tool interchange.
9688
+
9689
+ ### Command
9690
+ \`\`\`json
9691
+ {
9692
+ "kind": "command",
9693
+ "action": "toggle",
9694
+ "roles": {
9695
+ "patient": { "type": "selector", "value": ".active" },
9696
+ "destination": { "type": "selector", "value": "#button" }
9697
+ }
9698
+ }
9699
+ \`\`\`
9700
+
9701
+ ### Event Handler
9702
+ \`\`\`json
9703
+ {
9704
+ "kind": "event-handler",
9705
+ "action": "on",
9706
+ "roles": { "event": { "type": "literal", "value": "click", "dataType": "string" } },
9707
+ "body": [
9708
+ { "kind": "command", "action": "toggle", "roles": { "patient": { "type": "selector", "value": ".active" } } }
9709
+ ]
9710
+ }
9711
+ \`\`\`
9712
+
9713
+ ### Compound
9714
+ \`\`\`json
9715
+ {
9716
+ "kind": "compound",
9717
+ "action": "compound",
9718
+ "chainType": "then",
9719
+ "statements": [
9720
+ { "kind": "command", "action": "add", "roles": { "patient": { "type": "selector", "value": ".loading" } } },
9721
+ { "kind": "command", "action": "fetch", "roles": { "source": { "type": "literal", "value": "/api/data", "dataType": "string" } } }
9722
+ ]
9723
+ }
9724
+ \`\`\`
9725
+
9726
+ ## Control Flow (v1.1)
9727
+
9728
+ ### Conditional
9729
+ A command node with \`thenBranch\` and optional \`elseBranch\` arrays:
9730
+ \`\`\`
9731
+ [if condition:"x > 0" then:[toggle patient:.active] else:[remove patient:.active]]
9732
+ \`\`\`
9733
+ \`\`\`json
9734
+ {
9735
+ "kind": "command",
9736
+ "action": "if",
9737
+ "roles": { "condition": { "type": "expression", "raw": "x > 0" } },
9738
+ "thenBranch": [
9739
+ { "kind": "command", "action": "toggle", "roles": { "patient": { "type": "selector", "value": ".active" } } }
9740
+ ],
9741
+ "elseBranch": [
9742
+ { "kind": "command", "action": "remove", "roles": { "patient": { "type": "selector", "value": ".active" } } }
9743
+ ]
9744
+ }
9745
+ \`\`\`
9746
+
9747
+ ### Loop
9748
+ A command node with \`loopVariant\`, \`loopBody\`, and optional \`loopVariable\`/\`indexVariable\`:
9749
+ \`\`\`
9750
+ [repeat source:#items loopVariant:for loopVariable:item body:[add patient:.active]]
9751
+ \`\`\`
9752
+ \`\`\`json
9753
+ {
9754
+ "kind": "command",
9755
+ "action": "repeat",
9756
+ "roles": { "source": { "type": "selector", "value": "#items" } },
9757
+ "loopVariant": "for",
9758
+ "loopBody": [
9759
+ { "kind": "command", "action": "add", "roles": { "patient": { "type": "selector", "value": ".active" } } }
9760
+ ],
9761
+ "loopVariable": "item"
9762
+ }
9763
+ \`\`\`
9764
+
9765
+ Loop variants: \`forever\`, \`times\`, \`for\`, \`while\`, \`until\`.
9766
+
9767
+ ## JSON Wire Format \u2014 LLM-Simplified
9768
+
9769
+ Compact format optimized for LLM generation. Omits \`kind\`, uses flat \`trigger\` for event handlers.
9770
+
9771
+ \`\`\`json
9772
+ {
9773
+ "action": "toggle",
9774
+ "roles": { "patient": { "type": "selector", "value": ".active" } },
9775
+ "trigger": { "event": "click" }
9776
+ }
9777
+ \`\`\`
9778
+
9779
+ | Difference | Full-Fidelity | LLM-Simplified |
9780
+ |-----------|--------------|----------------|
9781
+ | \`kind\` field | Required | Absent |
9782
+ | Event handlers | \`event-handler\` kind with \`body\` | \`trigger\` field on command |
9783
+ | Compound | \`statements\` array | Not supported |
9784
+ | Expression values | \`{ "raw": "..." }\` | \`{ "value": "..." }\` |
9785
+ | Flag values | \`{ "name": "x", "enabled": true }\` | \`{ "value": true }\` |
9786
+
9787
+ ## Event Handlers (Bracket Syntax)
9788
+
9789
+ \`\`\`
9790
+ [on event:click body:[toggle patient:.active]]
9791
+ \`\`\`
9792
+ `;
9793
+ }
5926
9794
 
5927
9795
  // src/resources/index.ts
5928
9796
  function listResources() {
@@ -5974,6 +9842,12 @@ function listResources() {
5974
9842
  name: "Setup Decision Guide",
5975
9843
  description: "Structured decision tree for choosing between LokaScript and original _hyperscript adapter, and selecting the right bundle.",
5976
9844
  mimeType: "application/json"
9845
+ },
9846
+ {
9847
+ uri: "lse://protocol/spec",
9848
+ name: "LSE Protocol Specification",
9849
+ description: "Quick reference for LokaScript Explicit Syntax (LSE) \u2014 bracket syntax, value types, JSON wire format.",
9850
+ mimeType: "text/markdown"
5977
9851
  }
5978
9852
  ];
5979
9853
  }
@@ -6015,12 +9889,15 @@ function readResource(uri) {
6015
9889
  }
6016
9890
  ]
6017
9891
  };
9892
+ case "lse://protocol/spec":
9893
+ return { contents: [{ uri, mimeType: "text/markdown", text: getLSEProtocolSpec() }] };
6018
9894
  default:
6019
9895
  throw new Error(`Unknown resource: ${uri}`);
6020
9896
  }
6021
9897
  }
6022
9898
 
6023
9899
  // src/index.ts
9900
+ var registry = createDomainRegistry();
6024
9901
  var server = new Server(
6025
9902
  {
6026
9903
  name: "@hyperfixi/mcp-server",
@@ -6029,7 +9906,8 @@ var server = new Server(
6029
9906
  {
6030
9907
  capabilities: {
6031
9908
  tools: {},
6032
- resources: {}
9909
+ resources: {},
9910
+ prompts: {}
6033
9911
  }
6034
9912
  }
6035
9913
  );
@@ -6042,7 +9920,17 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
6042
9920
  ...lspBridgeTools,
6043
9921
  ...languageDocsTools,
6044
9922
  ...profileTools,
6045
- ...compilationTools
9923
+ ...compilationTools,
9924
+ ...routeTools,
9925
+ ...registry.getToolDefinitions(),
9926
+ ...samplingTools,
9927
+ ...dispatcherTools,
9928
+ ...irTools,
9929
+ ...debugTools,
9930
+ ...inventoryTools,
9931
+ ...trainingDataTools,
9932
+ ...feedbackTools,
9933
+ ...lsePipelineTools
6046
9934
  ]
6047
9935
  };
6048
9936
  });
@@ -6069,6 +9957,44 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
6069
9957
  if (name === "compile_hyperscript" || name === "validate_and_compile" || name === "translate_code" || name === "generate_tests" || name === "generate_component" || name === "diff_behaviors") {
6070
9958
  return handleCompilationTool(name, args);
6071
9959
  }
9960
+ if (name === "detect_domain" || name === "parse_composite" || name === "compile_auto" || name === "compile_composite") {
9961
+ return handleDispatcherTool(name, args, registry);
9962
+ }
9963
+ if (name === "convert_format" || name === "validate_explicit" || name === "validate_protocol" || name === "to_envelope" || name === "from_envelope") {
9964
+ return handleIRTool(name, args);
9965
+ }
9966
+ if (registry.canHandle(name)) {
9967
+ const typedArgs = args;
9968
+ if (name.endsWith("_bdd") && isMultiStepBDD(typedArgs)) {
9969
+ return handleBDDMultiStep(name, typedArgs);
9970
+ }
9971
+ if (name.endsWith("_behaviorspec") && isMultiLineBehaviorSpec(typedArgs)) {
9972
+ return handleBehaviorSpecMultiLine(name, typedArgs);
9973
+ }
9974
+ const result = await registry.handleToolCall(name, typedArgs);
9975
+ if (result) return result;
9976
+ }
9977
+ if (name === "extract_routes" || name === "generate_server_routes") {
9978
+ return handleRouteTool(name, args);
9979
+ }
9980
+ if (name === "ask_claude" || name === "summarize_content" || name === "analyze_content" || name === "translate_content" || name === "execute_llm") {
9981
+ return handleSamplingTool(name, args, server, registry);
9982
+ }
9983
+ if (name.startsWith("debug_")) {
9984
+ return handleDebugTool(name, args);
9985
+ }
9986
+ if (name === "scan_inventory" || name === "query_inventory") {
9987
+ return handleInventoryTool(name, args);
9988
+ }
9989
+ if (name === "generate_training_data") {
9990
+ return handleTrainingDataTool(name, args);
9991
+ }
9992
+ if (name === "lse_validate_and_feedback" || name === "lse_pattern_stats") {
9993
+ return handleFeedbackTool(name, args);
9994
+ }
9995
+ if (name === "lse_from_hyperscript" || name === "lse_to_hyperscript") {
9996
+ return handleLsePipelineTool(name, args);
9997
+ }
6072
9998
  if (name.startsWith("get_")) {
6073
9999
  return handlePatternTool(name, args);
6074
10000
  }
@@ -6077,6 +10003,26 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
6077
10003
  isError: true
6078
10004
  };
6079
10005
  });
10006
+ server.setRequestHandler(ListPromptsRequestSchema, async () => {
10007
+ return {
10008
+ prompts: [
10009
+ ...getLLMPromptDefinitions(),
10010
+ ...getDebugPromptDefinitions(),
10011
+ ...getLSEPromptDefinitions()
10012
+ ]
10013
+ };
10014
+ });
10015
+ server.setRequestHandler(GetPromptRequestSchema, async (request) => {
10016
+ const { name, arguments: promptArgs } = request.params;
10017
+ const typedArgs = promptArgs ?? {};
10018
+ if (isDebugPrompt(name)) {
10019
+ return renderDebugPrompt(name, typedArgs);
10020
+ }
10021
+ if (isLSEPrompt(name)) {
10022
+ return renderLSEPrompt(name, typedArgs);
10023
+ }
10024
+ return renderLLMPrompt(name, typedArgs);
10025
+ });
6080
10026
  server.setRequestHandler(ListResourcesRequestSchema, async () => {
6081
10027
  return { resources: listResources() };
6082
10028
  });