@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/README.md +148 -223
- package/dist/esm-LUPOYJFX.js +6055 -0
- package/dist/index.js +4008 -62
- package/package.json +60 -6
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
|
|
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: "
|
|
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:
|
|
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: "
|
|
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:
|
|
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
|
|
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: "
|
|
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
|
|
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: "
|
|
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: {
|
|
1762
|
-
|
|
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: {
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
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: {
|
|
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: {
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
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
|
|
3221
|
-
const
|
|
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:
|
|
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 || [
|
|
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:
|
|
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: "
|
|
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
|
|
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/
|
|
5203
|
-
function
|
|
5204
|
-
|
|
5205
|
-
|
|
5206
|
-
|
|
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
|
-
|
|
5209
|
-
|
|
5210
|
-
|
|
5211
|
-
|
|
5212
|
-
|
|
5213
|
-
|
|
5214
|
-
|
|
5215
|
-
|
|
5216
|
-
|
|
5217
|
-
|
|
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
|
-
|
|
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
|
-
|
|
5222
|
-
|
|
5223
|
-
|
|
5224
|
-
|
|
5225
|
-
|
|
5226
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
});
|